home / skills / tkersey / dotfiles / st
This skill helps you persist and review durable task plans in your repo with JSONL, ensuring continuity across sessions.
npx playbooks add skill tkersey/dotfiles --skill stReview the files below or copy the command above to add this skill to your agents.
---
name: st
description: Manage persistent task plans in repo-committed JSONL (`.step/st-plan.jsonl`) so state survives turns/sessions and stays reviewable in git. Use when users ask to "use $st", "resume the plan", "export/import plan state", "checkpoint milestones", "track dependencies/blocked work", "show ready next tasks", "keep shared TODO status on disk", "compare and contrast $st vs plan files", or when work has 3+ dependent steps and transient `update_plan` is not durable enough.
---
# st
## Overview
Maintain a durable plan file in the repo (default: `.step/st-plan.jsonl`) using in-place JSONL v3 persistence with dual lanes:
- `event` lane for mutations
- `checkpoint` lane for periodic full-state snapshots
Plan items use typed dependency edges (`deps: [{id,type}]`) plus `notes` and `comments`, and render deterministically through `show`/read views.
## Workflow
1. Resolve repository root and plan path.
2. If the run has 3+ dependent steps, likely spans turns, or already uses `update_plan`, adopt `$st` as the durable source of truth before editing.
3. Initialize plan storage with `scripts/st_plan.py init` if missing.
4. Rehydrate current state with `scripts/st_plan.py show` (or focused views via `ready` / `blocked`).
5. Apply plan mutations through script subcommands (`add`, `set-status`, `set-deps`, `set-notes`, `add-comment`, `remove`, `import-plan`); do not hand-edit existing JSONL lines.
6. After each mutation command, consume the emitted `update_plan: {...}` payload and publish `update_plan` in the same turn.
7. Use `emit-update-plan` to regenerate the payload from durable state when needed.
8. Export/import snapshots when cross-session handoff is needed.
## Commands
Run commands from the target repository root.
```bash
CODEX_SKILLS_HOME="${CODEX_HOME:-$HOME/.codex}"
CLAUDE_SKILLS_HOME="${CLAUDE_HOME:-$HOME/.claude}"
ST_PLAN="$CODEX_SKILLS_HOME/skills/st/scripts/st_plan.py"
[ -f "$ST_PLAN" ] || ST_PLAN="$CLAUDE_SKILLS_HOME/skills/st/scripts/st_plan.py"
ST_QUERY="$CODEX_SKILLS_HOME/skills/st/scripts/st_query_fast.sh"
[ -f "$ST_QUERY" ] || ST_QUERY="$CLAUDE_SKILLS_HOME/skills/st/scripts/st_query_fast.sh"
uv run "$ST_PLAN" init --file .step/st-plan.jsonl
uv run "$ST_PLAN" add --file .step/st-plan.jsonl --id st-001 --step "Reproduce failing test" --deps ""
uv run "$ST_PLAN" add --file .step/st-plan.jsonl --id st-002 --step "Patch core logic" --deps "st-001"
uv run "$ST_PLAN" set-status --file .step/st-plan.jsonl --id st-001 --status in_progress
uv run "$ST_PLAN" set-deps --file .step/st-plan.jsonl --id st-002 --deps "st-001:blocks,st-003:blocks"
uv run "$ST_PLAN" set-notes --file .step/st-plan.jsonl --id st-002 --notes "Need benchmark evidence"
uv run "$ST_PLAN" add-comment --file .step/st-plan.jsonl --id st-002 --text "Pausing until CI clears" --author tk
uv run "$ST_PLAN" ready --file .step/st-plan.jsonl --format markdown
uv run "$ST_PLAN" blocked --file .step/st-plan.jsonl --format json
uv run "$ST_PLAN" show --file .step/st-plan.jsonl --format markdown
uv run "$ST_PLAN" emit-update-plan --file .step/st-plan.jsonl
uv run "$ST_PLAN" export --file .step/st-plan.jsonl --output .step/st-plan.snapshot.json
uv run "$ST_PLAN" import-plan --file .step/st-plan.jsonl --input .step/st-plan.snapshot.json --replace
```
## Operating Rules
- Keep exactly one `in_progress` item unless `--allow-multiple-in-progress` is explicitly used.
- Track prerequisites in each item's typed `deps` array; dependencies are part of the canonical JSONL schema.
- Parse CLI deps as comma-separated `id` or `id:type` tokens; missing type normalizes to `blocks`, and type must be kebab-case.
- Require dependency integrity:
- dependency IDs must exist in the current plan,
- no self-dependencies,
- no dependency cycles.
- Allow `in_progress` and `completed` only when all dependencies are `completed`.
- Normalize user status terms before writing:
- `open`, `queued` -> `pending`
- `active`, `doing` -> `in_progress`
- `done`, `closed` -> `completed`
- Mutation commands (`add`, `set-status`, `set-deps`, `set-notes`, `add-comment`, `remove`, `import-plan`) automatically print an `update_plan:` payload line after durable write.
- Lock sidecar policy: mutating commands require the lock file (`<plan-file>.lock`, for example `.step/st-plan.jsonl.lock`) to be gitignored when inside a git repo; add it to `.gitignore` before first mutation.
- Mutations rewrite the JSONL file atomically (`temp` + `fsync` + `os.replace`) and compact to a canonical `replace` event plus checkpoint snapshot at the current seq watermark.
- `import-plan --replace` atomically resets state in the same in-place write model.
- Prefer concise, stable item IDs (`st-001`, `st-002`, ...).
- Prefer `show --format markdown` for execution: it groups steps into `Ready`, `Waiting on Dependencies`, `In Progress`, and terminal/manual buckets.
## Fast Query Helper (`jq` + `rg`)
- Use `scripts/st_query_fast.sh` for low-latency read-only queries against large logs.
- Requires both `jq` and `rg` on `PATH`.
- Examples:
- `"$ST_QUERY" --file .step/st-plan.jsonl ready`
- `"$ST_QUERY" --file .step/st-plan.jsonl blocked`
- `"$ST_QUERY" --file .step/st-plan.jsonl show`
- `"$ST_QUERY" --file .step/st-plan.jsonl show st-002`
## Sync Checklist (`$st` -> `update_plan`)
- After each `$st` mutation (`add`, `set-status`, `set-deps`, `set-notes`, `add-comment`, `remove`, `import-plan`), parse the emitted `update_plan: {...}` line and publish `update_plan` in the same turn.
- If no emitted payload is available (for example after `init` or shell piping), run:
- `uv run "$ST_PLAN" emit-update-plan --file .step/st-plan.jsonl`
- Preserve item ordering from `$st` in `update_plan`.
- Map statuses:
- `in_progress` -> `in_progress`
- `completed` -> `completed`
- `pending`, `blocked`, `deferred`, `canceled` -> `pending`
- Keep dependency edges only in `$st` (`deps`); do not encode dependencies in `update_plan`.
- If an item has `dep_state=waiting_on_deps`, never publish that step as `in_progress` in `update_plan`.
- Before final response on turns that mutate `$st`, re-check no drift by comparing:
- `uv run "$ST_PLAN" show --file .step/st-plan.jsonl --format json`
- the latest emitted `update_plan` payload.
## Validation
- Run lightweight CLI sanity checks:
- `uv run "$ST_PLAN" --help`
- `uv run "$ST_PLAN" emit-update-plan --file .step/st-plan.jsonl`
- `uv run "$ST_PLAN" show --file .step/st-plan.jsonl --format json`
## References
- Read `references/jsonl-format.md` for event schema, status/dependency state vocabulary, and snapshot import/export shapes.
This skill manages a durable, repo-committed task plan using an in-place JSONL file (default: .step/st-plan.jsonl) so state survives turns, sessions, and is reviewable in git. It provides mutation commands, deterministic read views, and a lightweight sync protocol to publish update_plan payloads after each change. The model uses an event lane plus periodic checkpoint snapshots for efficient and auditable state.
The tool initializes and rehydrates plan state from a JSONL file that contains typed events and full-state checkpoints. Mutations (add, set-status, set-deps, set-notes, add-comment, remove, import-plan) write atomic JSONL entries and emit a single update_plan payload line that callers must parse and publish. Read-only helpers show ready/blocked/in-progress views and fast query scripts use jq + ripgrep for low-latency inspections.
How do I publish changes so downstream systems see them?
After any mutation command the script prints an update_plan: {...} payload line; parse and publish that payload in the same turn. If missing, run emit-update-plan to regenerate it.
Can I hand-edit the JSONL file to fix a typo?
No. Do not hand-edit existing JSONL lines. Use mutation commands or import-plan --replace to produce canonical, compacted writes.
What happens if a dependency cycle is introduced?
The CLI enforces dependency integrity and rejects cycles, self-deps, and missing IDs at mutation time.