Skip to content

Latest commit

 

History

History
98 lines (73 loc) · 12 KB

scripts.md

File metadata and controls

98 lines (73 loc) · 12 KB

Scripts

Every script lives in scripts/ at the plugin root and is invoked as ${CLAUDE_PLUGIN_ROOT}/scripts/<name>. Two skills carry their own scripts instead: skills/init/scripts/init.sh and skills/setup/scripts/setup-checks.sh. -h/--help prints full usage on every script.

Conventions

  • Input: almost every script takes a bead id as an argument. tasks.py's epic, parents-to-close and worker subcommands take the task graph as JSON piped on stdin (bd list --all --limit 0 --json | tasks.py epic <id>) instead of as an argument, because Linux caps a single command-line argument at 128 KiB, which this project's bead history can exceed.
  • Exit codes: 0 success. 2 invalid arguments, or a bead/state the script refuses to act on (a usage problem, not a runtime failure). 1 a runtime failure the caller can retry or fix (a failed bd call, nothing to record, a verify or metrics check that failed). finish-task.py alone uses 3 for a problem only a person can resolve; see messages.
  • Shared helpers: lib.sh is sourced, never run directly. It defines show, get, config, children and worktree_of, used by most of the bash scripts.
  • Shared task-graph logic: tasks.py is both a CLI (see below) and a Python module imported by finish-task.py, supervise.py, workers.py, stats.py and next-tasks.py, so dispatch order, epic lookup and "is this worker running" share one definition.

Log and state files

All under <git-common-dir>/sdlc/, so worktrees on the same repository share them:

Path Written by Holds
logs/<task>-<session>.jsonl start-worker.sh (worker stdout), appended by the running claude -p the worker's stream-json transcript, plus a {"type":"dispatch_run",...} marker line at the start of each run (a resume adds another)
logs/<task>-<session>.jsonl.err start-worker.sh the worker process's stderr
logs/<task>-<session>.jsonl.wt run-task.sh stderr of the wt switch that created the worktree (present for every start, whether or not it failed); its mtime is the task's start time, used by workers.py for "started ... ago"
logs/<task>-<session>.jsonl.record start-worker.sh's wrapper the output of the record-task.sh call made once the worker process ends
logs/<task>-<session>.jsonl.remove record-task.sh wt remove's stderr, kept only when removing a merged task's worktree fails
logs/<task>-<session>.review-<N>.json, .review-<N>.err finish-task.py (agent_review) round <N>'s reviewer session output and stderr, for review level agent
wake created by supervise.py (a FIFO) wakes the sleeping supervisor; written by the beads on_create/on_update/on_close hooks and by start-worker.sh's wrapper once a worker ends
supervise.lock supervise.py flock, holding <pid> <generation> <argv>; a newer generation can take over a running supervisor, an older or equal one is refused
supervise.log supervise.py one line per event handled, actions taken, and anything the previous sweep's snapshot missed

Separately, a worker's own Claude Code transcript is ~/.claude/projects/<escaped-worktree-path>/<session>.jsonl, found by workers.py's find_transcript and read by claude --resume <session> --fork-session.

Dispatching a task

Script Does Called by Input Writes
create-task.sh validates and creates one bead (task or epic) with its metadata /sdlc:split, /sdlc:create-task, analyse-branch.sh flags: --title, --description, --type, --parent, --acceptance, --scope (optional), --domains (optional), --verify, --complexity, --design, --after (repeatable), --priority, --agent, --model, --effort, --review, --role, --set KEY=VALUE (repeatable) the bead; blocks edges from --after; a dep add onto the parent epic's integration task, if one exists
run-task.sh claims an open task, creates its worktree, starts its worker, returns supervise.py (start action) task id claims the task (dispatch_session, dispatch_host, dispatch_base, dispatch_branch, dispatch_started); on an epic's first dispatch, the epic branch and its integration task; the worktree (wt switch --create); hands off to start-worker.sh
start-worker.sh builds and starts the claude -p worker command, detached run-task.sh, resume-task.sh task id, --resume, --prompt TEXT (required) the log's dispatch_run marker; the detached worker process (setsid) with BASH_MAX_TIMEOUT_MS and BASH_DEFAULT_TIMEOUT_MS set to 5400000; once it ends, calls record-task.sh and writes to the wake pipe; the branch's worktrunk marker (running)
finish-task.py reviews, rebases, verifies, merges and closes; see task lifecycle the worker itself, via its one allowed Bash(finish-task.py *) tool task id sdlc/finish-<task>.lock (holds the pid; a second run for the task exits 1, a stale lock is taken over), dispatch_merge_commit (set then cleared around the merge), dispatch_review*, dispatch_state (awaiting-review, pr-opened, merged), dispatch_pr; the merge itself, the closed bead(s), a gh:pr or human gate
record-task.sh records a worker attempt once its process has ended start-worker.sh's wrapper (normal end); supervise.py (record/fail actions, for a crashed worker) task id, or --failed REASON dispatch_state, dispatch_cost, dispatch_model, dispatch_agents; a closed event bead; removes the worktree of a merged or pr-opened task (unless untracked files remain); the branch's worktrunk marker

Resuming, stopping and resetting

Script Does Called by Input Writes
resume-task.sh resumes a crashed worker's Claude session, detached, in its existing worktree supervise.py (resume action), /sdlc:recover task id, --prompt TEXT, --ended releases the merge queue; hands off to start-worker.sh --resume, or to record-task.sh directly if the log already ended with a result
resume-reviewed.sh resumes a task once its human review gate has closed supervise.py (resume_reviewed action) task id clears dispatch_state, then as resume-task.sh --ended; restores dispatch_state=awaiting-review and the marker if the resume itself fails
stop-task.sh ends a task's (or every task under an epic's) running worker /sdlc:stop task or epic id dispatch_state=stopped (or merged, if the worker finished first); TERM then KILL to the whole process tree; releases the merge queue; a comment; the branch's worktrunk marker
reset-task.sh undoes a dispatch entirely, back to a fresh open task /sdlc:dispatch (reopening a task it can't start), /sdlc:split (a stopped/failed bead before splitting), /sdlc:recover task id releases the merge queue; wt remove -D the worktree and branch; clears the marker; reopens the bead with every dispatch_* key unset
close-prs.sh closes an epic-pr integration task, and its epic, once the pull request is merged supervise.py (close_pr action) task id closes the task and, once all its epic's tasks are closed, the epic; or dispatch_state=stopped with a comment, if the pull request closed unmerged

Merging and verifying

Script Does Called by Input Writes
merge-queue.sh one queue bead per branch, so merges into it serialize across every machine finish-task.py (acquire/release around the merge), run-task.sh/verify.sh (ensure), reset-task.sh/stop-task.sh/clean.sh (release) ensure |acquire|release <branch> [holder] [--timeout SECONDS] the queue bead; bd kv dispatch.queue.<branch>
verify.sh runs the checks that finishing a task, parent task or epic would run finish-task.py, /sdlc:verify [--no-pre-merge] <task|parent|epic-id> nothing (read-only); a failing check's stderr carries a reproduce command and the project's debug hint
mark-branch.sh sets or clears a task branch's worktrunk state marker (an emoji wt shows in wt list) finish-task.py, record-task.sh, reset-task.sh, stop-task.sh, clean.sh, start-worker.sh <branch> <state> the marker, via wt config state marker; never fails the caller

Observing

Script Does Called by Input Writes
workers.py lists dispatched, non-closed tasks with where their worker runs, and the crash evidence for a crashed one /sdlc:status, /sdlc:stop, /sdlc:dispatch (display) --under ID (repeatable), --alive-count nothing (read-only)
next-tasks.py lists ready tasks in dispatch order /sdlc:status, /sdlc:dispatch (display) --under ID (repeatable), --ids nothing (read-only)
logs.py renders a task's worker logs as readable lines, oldest first /sdlc:logs, /sdlc:recover task id, --follow (needs a terminal), --raw, --last N nothing (read-only)
stats.py reports cost, duration, models and agents per task and per epic, and cost per complexity and model, from event beads /sdlc:stats [id] [--under ID] nothing (read-only)
tasks.py shared task-graph queries (see Conventions); also a CLI create-task.sh, stop-task.sh (piped bd list JSON) subcommands epic, parents-to-close, worker, under, log-totals (reads a log path directly, not stdin) nothing (read-only)

The supervisor

Script Does Called by Input Writes
supervise.py the one process that starts, resumes, records and wakes a person; see dispatch /sdlc:dispatch, run in the background --under ID (repeatable), --parallel N, --sweep SECONDS, --generation N, --running the lock, the log, the wake pipe; drives every other script in this page as needed

Housekeeping

Script Does Called by Input Writes
clean.sh lists (and, with --apply, removes) worktrees, branches and merge queues left behind /sdlc:clean [--apply] with --apply: wt remove -D, git branch -D, merge-queue.sh release, cleared markers

Code quality

Script Does Called by Input Writes
metrics.sh wraps lizard to report or check per-function complexity and length analyse-branch.sh, the sdlc:analyst agent, /sdlc:review (--changed) [--ccn N] [--length N] <path>..., or --changed <base>, or --check <path>... nothing (read-only)
analyse-branch.sh checks a branch head against the thresholds, files a refactor epic and analysis task when it crosses one supervise.py (analyse action, when custom.dispatch.analysis=auto), /sdlc:analyse branch name bd kv dispatch.analysed.<branch>; a refactor epic and its analysis task (via create-task.sh)

Settings

Script Does Called by Input Writes
settings.sh prints the effective dispatch settings, or the configured external doc sources most skills (display), lib.sh's config, supervise.py, metrics.sh, analyse-branch.sh [key | sources] nothing (read-only); see settings

Skill scripts

Script Does Called by Input Writes Exit codes
skills/init/scripts/init.sh prepares a repository for sdlc: beads init, beads config, .claude/sdlc.local.md/sdlc.md, .gitignore, the beads wake hooks /sdlc:init [--integration MODE] [--target BRANCH] [--parallel N] [--workflow build|none] the beads database, custom.dispatch.* config, .claude/sdlc.local.md, .claude/sdlc.md, .gitignore entries, .beads/hooks/{on_create,on_update,on_close} 0 done, 1 a step failed, 2 invalid arguments, not a git repository, detached HEAD, or no commits yet
skills/setup/scripts/setup-checks.sh checks this machine for the tools sdlc needs, one line per item /sdlc:setup none nothing (read-only) always 0