Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
81f5391
chore: configure codex graphify profile
Jul 9, 2026
c8ab1b3
fix: make graphify hook portable
Jul 9, 2026
ee03cff
fix: bound graphify hook runtime
Jul 9, 2026
176586a
no-mistakes(test): Captain, harden bootstrap and Pi tests
Jul 9, 2026
a7d3ed7
no-mistakes(document): Sync Codex Graphify docs
Jul 9, 2026
ebeb3fc
no-mistakes(lint): Fix Graphify markdown style
Jul 9, 2026
0c3aa42
chore: configure codex graphify profile
Jul 9, 2026
7b9df55
fix: make graphify hook portable
Jul 9, 2026
bdcde38
fix: bound graphify hook runtime
Jul 9, 2026
85d750d
no-mistakes(test): Captain, harden bootstrap and Pi tests
Jul 9, 2026
5f0fd76
no-mistakes(document): Sync Codex Graphify docs
Jul 9, 2026
9f75710
no-mistakes(lint): Fix Graphify markdown style
Jul 9, 2026
ff7e5d4
no-mistakes(review): Merge Graphify PreToolUse hook
Jul 18, 2026
3e05f3b
no-mistakes(test): Captain, fix firstmate test regressions
Jul 18, 2026
5e71fc6
no-mistakes(document): Sync Codex Graphify docs
Jul 18, 2026
a47bd22
no-mistakes(lint): Captain, fix unused AFK loop counter
Jul 18, 2026
27d4616
Merge daemon's validated pipeline commits
Jul 18, 2026
9f7ad29
no-mistakes(review): Captain, fix duplicate Codex PreToolUse hook
Jul 18, 2026
a6d7506
no-mistakes(review): Captain, activate Codex spawn profile
Jul 18, 2026
9155ecb
no-mistakes(document): Sync Codex Graphify documentation
Jul 18, 2026
6c30d11
no-mistakes(lint): Captain, brace Codex config key expansion
Jul 18, 2026
ba22446
no-mistakes(lint): Captain: silence unused afk launch counters
Jul 18, 2026
776cf13
no-mistakes(lint): Captain, follow repo-local shell sources
Jul 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .codex/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Codex FirstMate background profile.
# Not YOLO: workspace sandbox stays active; blocked boundary work escalates on request.
sandbox_mode = "workspace-write"
approval_policy = "on-request"
approvals_reviewer = "auto_review"
5 changes: 5 additions & 0 deletions .codex/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"type": "command",
"command": "bash -lc 'payload=$(cat 2>/dev/null || true); [ -n \"$payload\" ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; root=$(pwd -P) || exit 0; [ -x \"$root/bin/fm-cd-pretool-check.sh\" ] || exit 0; [ -f \"$root/AGENTS.md\" ] || exit 0; [ -f \"$root/.codex/hooks.json\" ] || exit 0; jq -e \"any(.hooks.PreToolUse[]?.hooks[]?.command?; type == \\\"string\\\" and contains(\\\"fm-cd-pretool-check.sh\\\"))\" \"$root/.codex/hooks.json\" >/dev/null 2>&1 || exit 0; printf \"%s\" \"$payload\" | \"$root/bin/fm-cd-pretool-check.sh\"'",
"timeout": 10
},
{
"type": "command",
"command": "bash -lc 'command -v graphify >/dev/null 2>&1 || exit 0; graphify hook-check'",
"timeout": 10
}
]
}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ config/backend
config/x-mode.env
config/cmux-socket-password
config/wedge-alarm
graphify-out/
22 changes: 20 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ Hard rules, in priority order:
If work failed, say so plainly with the evidence.

You may maintain this repo's private operational state directly.
Shared tracked material is `AGENTS.md`, `README.md`, `CONTRIBUTING.md`, `.tasks.toml`, `.github/workflows/`, `bin/`, `.agents/skills/`, and public `skills/`.
Shared tracked material is `AGENTS.md`, `README.md`, `CONTRIBUTING.md`, `.tasks.toml`, `.codex/`, `.github/workflows/`, `bin/`, `.agents/skills/`, and public `skills/`.
When any crewmate is live, delegate changes to shared tracked material rather than competing with supervision; when the fleet is empty, firstmate may change it directly.
This repo is a shared template, while `.env`, `data/`, `state/`, `config/`, `projects/`, and `.no-mistakes/` are captain-private and gitignored.
This repo is a shared template, while `.env`, `data/`, `state/`, `config/`, `projects/`, `.no-mistakes/`, and `graphify-out/` are captain-private and gitignored.
Ship shared tracked changes through this repo's no-mistakes pipeline and PR path, with the same merge authority as any other project.
Never add an agent name as a commit co-author.

Expand All @@ -58,6 +58,7 @@ CONTRIBUTING.md contributor workflow and repo conventions
README.md public overview and development notes
.github/workflows/ shared CI and PR enforcement, committed
.tasks.toml tracked tasks-axi markdown backend config for the default backlog backend (section 10)
.codex/ tracked Codex primary profile plus SessionStart, PreToolUse, and Stop hooks (docs/configuration.md "Codex profile and Graphify hooks")
.agents/skills/ firstmate-loaded internal skills, committed; each carries metadata.internal=true for installers
.claude/skills symlink to .agents/skills for claude compatibility
skills/ standalone public installer-facing skills, committed; not loaded by firstmate
Expand All @@ -81,6 +82,7 @@ data/ personal fleet records; LOCAL, gitignored as a whole
<id>/brief.md per-task crewmate brief, or per-secondmate charter brief when kind=secondmate
<id>/report.md scout task deliverable, written by the crewmate; survives teardown
projects/ cloned repos; gitignored; READ-ONLY for you
graphify-out/ local Graphify knowledge graph artifacts; gitignored, expected to drift after hook or incremental updates
state/ volatile runtime signals; gitignored
<id>.status appended by crewmates: "<state>: <note>" wake-event lines, not current-state truth
<id>.turn-ended touched by turn-end hooks
Expand Down Expand Up @@ -476,3 +478,19 @@ Keep this file for knowledge useful to almost every future agent session in this
Do not repeat what the codebase already shows; point to the authoritative file, skill, command, or doc.
Prefer rewriting or pruning existing entries over appending new ones.
When updating this file, preserve every safety boundary and keep the always-loaded contract concise.

## Graphify

This project may have a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.

When the user types `/graphify`, use the installed graphify skill or instructions before doing anything else.

Rules:
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists.
Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts.
These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- Dirty graphify-out/ files are expected after hooks or incremental updates; dirty graph files are not a reason to skip graphify.
Only skip graphify if the task is about stale or incorrect graph output, or the user explicitly says not to use it.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ See the [no-mistakes quick start](https://kunchenguid.github.io/no-mistakes/star

- This repo is a template for running a firstmate orchestrator agent.
`AGENTS.md` is the agent's main job description and names when to load bundled firstmate skills; `CLAUDE.md` is a symlink to it, and `.claude/skills` is a symlink to `.agents/skills`.
- Only shared material is tracked: `AGENTS.md`, `README.md`, `CONTRIBUTING.md`, `.tasks.toml`, `.github/workflows/`, `bin/`, `.agents/skills/`, and `skills/`.
- Only shared material is tracked: `AGENTS.md`, `README.md`, `CONTRIBUTING.md`, `.tasks.toml`, `.codex/`, `.github/workflows/`, `bin/`, `.agents/skills/`, and `skills/`.
`.agents/skills/` holds agent-loaded skills that assume a live firstmate home and carry `metadata.internal: true` so installers such as [skills.sh](https://skills.sh) hide them from discovery; `skills/` holds standalone, installer-facing public skills with no firstmate dependency (see the README's "Two-tier skill layout").
Everything personal to one captain's fleet (`.env`, `data/`, `state/`, `config/`, `projects/`, `.no-mistakes/`) is gitignored; never commit it.
Everything personal to one captain's fleet (`.env`, `data/`, `state/`, `config/`, `projects/`, `.no-mistakes/`, `graphify-out/`) is gitignored; never commit it.
The root `.tasks.toml` is tracked `tasks-axi` config for `data/backlog.md`; compatible `tasks-axi` is the default backend for routine backlog mutations, with the compatibility definition owned by [`docs/configuration.md`](docs/configuration.md) ("Backlog backend").
A local `config/backlog-backend=manual` opt-out forces firstmate's routine backlog updates to hand-editing and stays gitignored; validated secondmate handoffs still delegate through `tasks-axi mv`.
A local `config/backend` file explicitly overrides runtime auto-detection for new task endpoints and stays gitignored; spawn-supported values are `tmux` plus experimental `herdr`, `zellij`, `orca`, and `cmux`, while `codex-app` is documented only in `docs/codex-app-backend.md`.
It does not make `data/` tracked.
The tracked `.codex/` files are firstmate's Codex primary profile and project hooks, not personal Codex settings.
- Helper scripts in `bin/` are plain bash.
Each starts with a usage header comment; keep it accurate when you change behavior.
Test scripts and helpers in `tests/` are plain bash too.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ For larger fleets, you can opt in to persistent secondmates: second mates that a
firstmate is not a model, not a harness, not a skill, not an MCP server, and not a CLI.
firstmate is an agent distro for running a crew of agents.
An agent distro is a portable directory of instructions, skills, tooling, policies, and state conventions that turns a general-purpose agent into a specialized one.
There is no app to install: the cloned repo is the distro - `AGENTS.md`, bundled firstmate skills, and helper scripts that any terminal coding agent can follow.
There is no app to install: the cloned repo is the distro - `AGENTS.md`, bundled firstmate skills, tracked harness hooks and profiles, and helper scripts that any terminal coding agent can follow.
Launching a supported harness inside it instantiates your first mate - and makes you the captain.

## Features
Expand Down Expand Up @@ -183,7 +183,7 @@ Firstmate's skills live in two separate places with different audiences:
## Documentation

- [docs/architecture.md](docs/architecture.md) - how the crew, supervision, worktrees, secondmates, and project modes work.
- [docs/configuration.md](docs/configuration.md) - environment variables, `FM_HOME`, runtime backend selection, optional X mode, the files you set, and harness support.
- [docs/configuration.md](docs/configuration.md) - environment variables, `FM_HOME`, runtime backend selection, optional X mode, Codex profile and Graphify hook configuration, the files you set, and harness support.
- [docs/wedge-alarm.md](docs/wedge-alarm.md) - configure the active alert for an away-mode escalation delivery that gets stuck.
- [docs/tmux-backend.md](docs/tmux-backend.md) - setup guide for the tmux reference backend: prerequisites, attaching, and watching crew windows.
- [docs/herdr-backend.md](docs/herdr-backend.md) - setup guide for the experimental herdr backend, plus its verification notes and known gaps.
Expand Down
14 changes: 7 additions & 7 deletions bin/fm-afk-launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ fm_afk_launch_lock_owned() {
}

fm_afk_launch_lock_acquire() {
local i incomplete=0 identity
local _ incomplete=0 identity
mkdir -p "$FM_AFK_LAUNCH_STATE" || return 1
for i in $(seq 1 200); do
for _ in $(seq 1 200); do
if mkdir "$FM_AFK_LAUNCH_LOCK" 2>/dev/null; then
if ! printf '%s' "$$" > "$FM_AFK_LAUNCH_LOCK/pid"; then
rm -rf "$FM_AFK_LAUNCH_LOCK"
Expand Down Expand Up @@ -257,12 +257,12 @@ fm_afk_launch_terminal_alive() { # <backend> <target>
}

fm_afk_launch_wait_ready() { # <backend> <target>
local backend=$1 target=$2 i
local backend=$1 target=$2 _
if [ -n "${FM_AFK_LAUNCH_ENTRY:-}" ]; then
fm_afk_launch_terminal_alive "$backend" "$target"
return
fi
for i in $(seq 1 100); do
for _ in $(seq 1 100); do
daemon_lock_held_by_live_daemon && return 0
fm_afk_launch_terminal_alive "$backend" "$target" || return 1
sleep 0.05
Expand All @@ -287,8 +287,8 @@ fm_afk_launch_commit_terminal() { # <backend> <target> <extra> [already-recorde
}

fm_afk_launch_herdr_recover_created() { # <session> <label>
local session=$1 label=$2 workspaces ws_count wsid panes pane_count pane i
for i in $(seq 1 20); do
local session=$1 label=$2 workspaces ws_count wsid panes pane_count pane _
for _ in $(seq 1 20); do
workspaces=$(fm_backend_herdr_cli "$session" workspace list 2>/dev/null) || { sleep 0.05; continue; }
ws_count=$(printf '%s' "$workspaces" | jq --arg want "$label" \
'[.result.workspaces[]? | select(.label == $want)] | length' 2>/dev/null) || { sleep 0.05; continue; }
Expand Down Expand Up @@ -603,10 +603,10 @@ fm_afk_launch_stop() {

fm_afk_launch_main() {
local result
fm_afk_launch_lock_acquire || return 1
trap fm_afk_launch_lock_release EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
fm_afk_launch_lock_acquire || return 1
case "${1:-start}" in
start) fm_afk_launch_start ;;
start-native) fm_afk_launch_start_native ;;
Expand Down
2 changes: 1 addition & 1 deletion bin/fm-brief.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ else
PROJECT_CLONES_NOTE="The projects above are local clones for work you supervise; they are not an exclusive ownership claim."
fi
cat > "$BRIEF" <<EOF
You are a persistent second mate managed by the main firstmate. Work on your own; do not wait for a human.
You are a persistent second mate managed by the main firstmate: a persistent domain supervisor. Work on your own; do not wait for a human.

# Charter
$SECONDMATE_CHARTER
Expand Down
16 changes: 13 additions & 3 deletions bin/fm-lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# fm-lint.sh - the single owner of firstmate's shell-lint definition.
#
# Runs ShellCheck over firstmate's tracked shell scripts at ShellCheck's default
# severity (which reports info, warning, and error - the levels CI fails on).
# severity (which reports info, warning, and error - the levels CI fails on),
# following repo-local sourced helpers so info-level SC1091 checks stay useful.
# The lint command, the file set, the config, AND the pinned ShellCheck version
# live here and ONLY here, so the gates cannot drift apart: both invoke this
# script with no arguments.
Expand Down Expand Up @@ -67,10 +68,19 @@ if [ "$resolved" != "$REQUIRED_SHELLCHECK" ]; then
exit 1
fi

run_shellcheck() {
shellcheck --norc -x -P "$ROOT" "$@"
}

if [ "$#" -gt 0 ]; then
exec shellcheck --norc "$@"
run_shellcheck "$@"
exit $?
fi

# Canonical file set: the ONE authoritative definition. Callers reference this
# script; they never re-spell these globs.
exec shellcheck --norc bin/*.sh bin/backends/*.sh tests/*.sh
status=0
for script in bin/*.sh bin/backends/*.sh tests/*.sh; do
run_shellcheck "$script" || status=$?
done
exit "$status"
34 changes: 30 additions & 4 deletions bin/fm-spawn.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# axes chosen by firstmate at intake. They are only threaded into harnesses whose
# installed CLIs were verified to support that axis; unsupported axes are omitted
# from that harness's launch rather than guessed.
# Codex launches also require the tracked .codex/config.toml profile and pass its
# sandbox_mode, approval_policy, and approvals_reviewer values as CLI overrides.
# --backend <name> is the explicit runtime session-provider backend for this
# spawn. Without it, the script resolves FM_BACKEND, then config/backend, then
# runtime auto-detection (the runtime firstmate itself is executing inside -
Expand Down Expand Up @@ -69,6 +71,7 @@
# __BRIEF__ absolute path to data/<task-id>/brief.md
# __TURNEND__ absolute path to state/<task-id>.turn-ended (for harnesses whose
# turn-end signal rides the launch command, e.g. codex -c notify=[...])
# __CODEXCONFIG__ Codex profile overrides parsed from .codex/config.toml
# __PIEXT__ absolute path to state/<task-id>.pi-ext.ts (pi turn-end extension,
# written by this script; outside the worktree to avoid pi's trust gate)
# __PITURNEND__ absolute path to .pi/extensions/fm-primary-turnend-guard.ts in a pi secondmate home
Expand Down Expand Up @@ -327,9 +330,9 @@ launch_template() {
claude) printf '%s' 'CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false claude --dangerously-skip-permissions __MODELFLAG____EFFORTFLAG__"$(cat __BRIEF__)"' ;;
codex)
if [ "$kind" = secondmate ]; then
printf '%s' 'codex __MODELFLAG____EFFORTFLAG__--dangerously-bypass-approvals-and-sandbox "$(cat __BRIEF__)"'
printf '%s' 'codex __MODELFLAG____EFFORTFLAG____CODEXCONFIG__"$(cat __BRIEF__)"'
else
printf '%s' 'codex __MODELFLAG____EFFORTFLAG__--dangerously-bypass-approvals-and-sandbox -c "notify=[\"bash\",\"-c\",\"touch __TURNEND__\"]" "$(cat __BRIEF__)"'
printf '%s' 'codex __MODELFLAG____EFFORTFLAG____CODEXCONFIG__-c "notify=[\"bash\",\"-c\",\"touch __TURNEND__\"]" "$(cat __BRIEF__)"'
fi
;;
opencode) printf '%s' 'OPENCODE_CONFIG_CONTENT='\''{"permission":{"*":"allow"}}'\'' opencode __MODELFLAG__--prompt "$(cat __BRIEF__)"' ;;
Expand Down Expand Up @@ -480,6 +483,27 @@ effort_flag_for_harness() {
esac
}

codex_config_value() {
local key=$1 file=$FM_ROOT/.codex/config.toml values count
[ -f "$file" ] || { echo "error: missing Codex profile at $file" >&2; return 1; }
values=$(sed -n -E "s/^[[:space:]]*${key}[[:space:]]*=[[:space:]]*\"([^\"]*)\"[[:space:]]*(#.*)?$/\1/p" "$file")
count=$(printf '%s\n' "$values" | sed '/^$/d' | wc -l | tr -d ' ')
[ "$count" = 1 ] || { echo "error: Codex profile must define exactly one quoted $key in $file" >&2; return 1; }
printf '%s\n' "$values"
}

codex_config_flags_for_harness() {
local harness=$1 key value
case "$harness" in
codex*) ;;
*) return 0 ;;
esac
for key in sandbox_mode approval_policy approvals_reviewer; do
value=$(codex_config_value "$key") || return 1
printf -- '-c %s ' "$(shell_quote "$key=\"$value\"")"
done
}

json_escape() {
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}
Expand Down Expand Up @@ -986,6 +1010,9 @@ $("$FM_ROOT/bin/fm-project-mode.sh" "$PROJ_NAME")
EOF
fi

MODELFLAG=$(model_flag_for_harness "$HARNESS" "$MODEL")
EFFORTFLAG=$(effort_flag_for_harness "$HARNESS" "$EFFORT")
CODEXCONFIG=$(codex_config_flags_for_harness "$HARNESS")
META_WINDOW=$T
[ "$BACKEND" = orca ] && META_WINDOW=$W
{
Expand Down Expand Up @@ -1034,10 +1061,9 @@ sq_turnend=$(shell_quote "$TURNEND")
sq_piext=$(shell_quote "$STATE/$ID.pi-ext.ts")
sq_piturnend=$(shell_quote "$PROJ_ABS/.pi/extensions/fm-primary-turnend-guard.ts")
sq_piwatch=$(shell_quote "$PROJ_ABS/.pi/extensions/fm-primary-pi-watch.ts")
MODELFLAG=$(model_flag_for_harness "$HARNESS" "$MODEL")
EFFORTFLAG=$(effort_flag_for_harness "$HARNESS" "$EFFORT")
LAUNCH=${LAUNCH//__MODELFLAG__/$MODELFLAG}
LAUNCH=${LAUNCH//__EFFORTFLAG__/$EFFORTFLAG}
LAUNCH=${LAUNCH//__CODEXCONFIG__/$CODEXCONFIG}
LAUNCH=${LAUNCH//__BRIEF__/$sq_brief}
LAUNCH=${LAUNCH//__TURNEND__/$sq_turnend}
LAUNCH=${LAUNCH//__PIEXT__/$sq_piext}
Expand Down
1 change: 1 addition & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ When the file exists, `fm-spawn.sh` refuses crewmate and scout launches without
Secondmate launches are exempt because they resolve the secondmate harness and any optional secondmate model or effort tokens instead.
Unsupported effort values are still recorded in task meta when passed to `fm-spawn.sh`, but the launch template omits any effort flag that the selected harness does not accept.
That keeps spawn launch compatible across claude, codex, grok, pi, and opencode while preserving the requested profile for later audit.
Codex launches additionally thread the tracked `.codex/config.toml` profile as CLI `-c` overrides, so Codex crewmates and secondmates use the repository's sandbox, approval, and approval-reviewer posture instead of the previous bypass launch.

## Optional secondmates

Expand Down
Loading