Skip to content

Scripts

Echo’s behavior lives in deterministic scripts; the slash commands and worker prompts are thin front-ends over them. This page maps every script under skills/echo-core/scripts/ and skills/echo-backlog/scripts/, grouped by the role it plays. Summaries are derived from each script’s own header comment.

Everything reads configuration and paths through two keystone helpers, so nothing hardcodes a board slug, status name, or directory:

ScriptWhat it does
echo-configThe keystone config resolver. Deep-merges defaults.json ← registry entry ← per-project config.json and prints the merged object or a dotted-path scalar. Every script reads config through this.
echo-pathsDerives a registered project’s runtime paths (state dir, lock/flock files, worker-pid glob, worktree base, agent-runs dir, outbox/notifications dirs) from config + project name.
locate_claude.sh(Sourced) Resolves the claude CLI to a real executable path — spawns need a real binary, not a shell alias.

Run by /echo:setup, one phase each. All live under scripts/setup/.

ScriptWhat it does / when it runs
setup/inspect.shRead-only repo inspection for the interview phase — cold-detects repo slug, owner, and proposals (--json).
setup/preflight.shProvisioning-readiness checks; prints an error list (not a first-failure throw) that must clear before provisioning.
setup/provision_board.shCreate-or-adopt the GitHub Projects v2 board + its single-select fields; --dry-run previews first.
setup/write_config.shPersists the assembled per-project config.json, backing up any existing file with a timestamp first (safe reconfigure).
setup/register_project.shAdds/updates the project’s entry in the machine registry — replaces, never appends, an existing entry for the same root.
setup/bootstrap_claudemd.shDrafts (and, with --apply, writes) a subsystem-map CLAUDE.md; refuses to overwrite a substantial existing file.

The /echo:tick sweep and the per-project pipeline it hands off to.

ScriptWhat it does / when it runs
tick.shOne hub sweep over every enabled registered project: global gates once, then per-project fast-paths; one status line per project. Never exits non-zero for a per-project condition.
dispatch_one.shThe per-project pipeline tick.sh hands a dispatchable project to: lock → select → pre-validate → flip Ready→Planning → spawn worker → comment → release.
lock.shAcquires the per-project dispatcher lock + a free worker slot (bounded-N concurrency). Honors ECHO_DISPATCHER_DISABLED and the ECHO_WORKER_SLOTS=0 soft kill-switch.
select_next_item.shPicks the next eligible Ready item (status == Ready, issue not closed), sorted by the project’s Priority rank then board position. Emits its metadata as JSON.
prevalidate_item.shThe one LLM-judgment dispatch step: a small-model classify of the item as proceed / needs_human / defer, handling the side effects of the latter two.
chain_next.shChain-triggers the next sweep the moment a worker frees a slot — so freed capacity or Ready work elsewhere is picked up without waiting for the next cron tick.

Spawned per item; the worker drives brainstorm → plan → implement → PR → review.

ScriptWhat it does / when it runs
spawn_worker.shLaunches a detached claude -p worker for a chosen item: renders the cacheable prompt prefix, cuts a worktree off the base branch, records the PID.
check_test_coverage.shMechanical PR test-coverage gate — a large non-docs diff must touch tests or carry a ## Test justification; tags needs-tests (blocking only when it also touches auth/isolation paths, exit 20).
sensitive_path_gate.shThe single source of truth for “does this changed-file set touch auth/tenant-isolation code?” — a match makes the worker hold auto-merge and route to human review.
file_review_findings.shFiles one tracked backlog item per confirmed-and-unfixed adversarial-review finding, so surfaced debt leaves a trail instead of just a PR comment.
flip_to_needs_human.shCentralized wrapper to flip an item to Needs Human — status mutation + issue comment + queued Discord outbox notification, as one unit.
flip_to_ready.shRe-queues an item to Ready with operator guidance as an issue comment — the inbound mirror of flip_to_needs_human.sh.

Trusted dispatcher-side merge and the post-merge routing sweep.

ScriptWhat it does / when it runs
gated_merge.shTrusted dispatcher-side merge: selects AI-reviewing items whose open PR carries echo:merge-ready, re-runs the deterministic gates, then merges or holds for a human.
reconcile.shThe universal post-merge trigger: for any in-flight item whose worker branch has a merged PR, routes it onward — straight to Done (verify.type: none) or to Building & Deploying + a deploy-watch.

Post-merge verification that a change actually reached production.

ScriptWhat it does / when it runs
spawn_watcher.shDetached, read-only deploy-watch launcher (per-item locked, idempotent). Refuses to launch on a non-read-only verify command; escalates instead.
watcher-run.shParent shell of the detached deploy-watch process — forces a terminal board flip if the watcher ever exits without finalizing (the death backstop).
finalize_watcher.shOwns the terminal board transition for a deploy-watch (done / needs-human / guard); idempotent, read-back-confirmed, never a false Done.
checkout_supporting_repo.shLazily creates a worktree of a declared supportingRepos repo for cross-repo edits (e.g. an image-tag bump); prefers a local clone, falls back to cloning.
resolve_supporting_repo.shLooks up one declared supportingRepos entry by name and emits it as compact JSON.

Escalation delivery and inbound reply routing (the posting itself is the hub’s job via the Discord MCP tool; these scripts prepare and record it).

ScriptWhat it does / when it runs
drain_outbox.shClaims queued Discord escalation files for one project (the atomic mv is the per-file lock, fixing double-posts) and emits their payloads for the hub to post.
record_delivery.shStamps the posted escalation’s Discord message id into its record so inbound quote-replies can be matched back to the item.
route_replies.shInbound reply router: for each operator quote-reply matching a Needs-Human escalation, re-queues the item to Ready with the reply text as guidance. Idempotent.

Best-effort Obsidian-native run notes — never load-bearing.

ScriptWhat it does / when it runs
vault_note.shWrite-side echo-vault notes (Dataview frontmatter + wikilinks): project / run / needs-human / archive / lessons subcommands. Unset vault → no-op.
distill_run.shBest-effort LLM enrichment of a run note with a Decisions/Patterns/Solutions/Blockers taxonomy distilled from the worker transcript. Any failure → silent no-op.

The board read/write layer — used by the sweep, /echo:status, and feature filing.

ScriptWhat it does / when it runs
fields.sh(Sourced) Shared board identity + live GraphQL resolution of project/field/option ids for a registered project. Every other backlog script sources this.
list_backlog.shLists board items with optional --status / --category / --priority / --search filters; --json for raw output. Backs /echo:status’s single board read.
view_item.shShows full detail of one item by issue number or project-item id, merging in the issue body (which the projection doesn’t include).
create_feature.shFiles a feature: opens a GitHub issue, adds it to the board, and sets Status/Category/Priority/Size (defaults from createFeature.*).