runlist 0.99.2 → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/GETTING_STARTED.md +249 -0
  2. package/README.md +414 -46
  3. package/bin/runlist.mjs +402 -51
  4. package/package.json +3 -1
  5. package/plugins/runlist-codex/.codex-plugin/plugin.json +1 -1
  6. package/plugins/runlist-codex/bin/consume-prompt.mjs +45 -0
  7. package/plugins/runlist-codex/bin/runlist-hook +29 -11
  8. package/plugins/runlist-codex/bin/session-start.mjs +16 -0
  9. package/plugins/runlist-codex/bin/subagent-start.mjs +16 -0
  10. package/plugins/runlist-codex/hooks/hooks.json +11 -0
  11. package/plugins/runlist-codex/skills/runlist/SKILL.md +27 -11
  12. package/plugins/runlist-codex/skills/runlist-setup/SKILL.md +109 -0
  13. package/runlist.config.example.mjs +6 -0
  14. package/src/agent-context.mjs +8 -2
  15. package/src/agent-hosts.mjs +305 -0
  16. package/src/app-create.mjs +11 -1
  17. package/src/atomic-mutation.mjs +140 -35
  18. package/src/baton.mjs +127 -33
  19. package/src/codex-integration.mjs +57 -7
  20. package/src/commands.mjs +92 -38
  21. package/src/config-command.mjs +115 -0
  22. package/src/config-setting.mjs +163 -0
  23. package/src/config.mjs +57 -12
  24. package/src/doctor-hosts.mjs +192 -0
  25. package/src/doctor.mjs +16 -6
  26. package/src/export.mjs +11 -0
  27. package/src/git.mjs +58 -2
  28. package/src/guard.mjs +41 -13
  29. package/src/handoff-record.mjs +290 -30
  30. package/src/handoffs-help.mjs +143 -0
  31. package/src/handoffs-mode.mjs +486 -0
  32. package/src/handoffs-scope.mjs +98 -0
  33. package/src/help.mjs +72 -0
  34. package/src/host-integration.mjs +7 -1
  35. package/src/hud.mjs +174 -29
  36. package/src/index.mjs +11 -4
  37. package/src/init.mjs +255 -160
  38. package/src/install.mjs +35 -18
  39. package/src/installation.mjs +110 -27
  40. package/src/journal.mjs +2 -2
  41. package/src/lifecycle.mjs +22 -11
  42. package/src/managed-path.mjs +68 -4
  43. package/src/migrate.mjs +7 -0
  44. package/src/naming.mjs +26 -0
  45. package/src/new.mjs +202 -91
  46. package/src/pickup-card.mjs +1 -1
  47. package/src/plan-changes.mjs +49 -0
  48. package/src/prompt-handoff.mjs +16 -0
  49. package/src/prompts.mjs +101 -27
  50. package/src/rename.mjs +13 -0
  51. package/src/setup-carry.mjs +291 -0
  52. package/src/setup-waiting.mjs +20 -0
  53. package/src/setup-worker.mjs +257 -0
  54. package/src/setup.mjs +404 -0
  55. package/src/statuses.mjs +5 -2
  56. package/src/update.mjs +239 -79
  57. package/src/use.mjs +4 -2
  58. package/src/validate.mjs +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runlist",
3
- "version": "0.99.2",
3
+ "version": "0.100.0",
4
4
  "description": "CLI for managing markdown documents with YAML frontmatter — index, query, validate, graph, export, lifecycle, and AI summaries.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,6 +17,7 @@
17
17
  "src/",
18
18
  "assets/",
19
19
  "plugins/runlist-codex/",
20
+ "GETTING_STARTED.md",
20
21
  "scripts/postinstall.mjs",
21
22
  "runlist.config.example.mjs"
22
23
  ],
@@ -42,6 +43,7 @@
42
43
  },
43
44
  "homepage": "https://github.com/reowens/runlist#readme",
44
45
  "scripts": {
46
+ "check:linux": "node scripts/check-linux-docker.mjs",
45
47
  "check:public": "node scripts/check-public.mjs",
46
48
  "prepack": "node scripts/check-public.mjs",
47
49
  "test": "node --import ./test/setup-env.mjs --test --test-concurrency=4 test/*.test.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runlist-codex",
3
- "version": "0.99.2",
3
+ "version": "0.100.0",
4
4
  "description": "Runlist workflow skill, session orientation, and tool guard for Codex",
5
5
  "author": {
6
6
  "name": "Reo Owens"
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { spawnSync } from 'node:child_process';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ function block(reason) {
7
+ process.stdout.write(JSON.stringify({ decision: 'block', reason }) + '\n');
8
+ }
9
+
10
+ let request;
11
+ try { request = JSON.parse(readFileSync(0, 'utf8')); }
12
+ catch { process.exit(0); }
13
+
14
+ if (request.hook_event_name !== 'UserPromptSubmit' || typeof request.prompt !== 'string') process.exit(0);
15
+ // T3 adds this role header to its delegated task's user message.
16
+ const prompt = request.prompt.replace(/^Act as the (?:implementation|research|review|design|test|general) sub-agent for this task\.\r?\n\r?\n/, '');
17
+ const match = /^\s*consume[ \t]+([A-Za-z0-9_.\/\\][A-Za-z0-9_.\/\\:-]*)[ \t]*$/i.exec(prompt);
18
+ if (!match) process.exit(0);
19
+
20
+ if (typeof request.session_id !== 'string' || !request.session_id.trim()
21
+ || typeof request.cwd !== 'string' || !path.isAbsolute(request.cwd)) {
22
+ block('Runlist needs the native session_id and absolute cwd to consume the requested handoff.');
23
+ process.exit(0);
24
+ }
25
+
26
+ const wrapper = fileURLToPath(new URL('./runlist-hook', import.meta.url));
27
+ const result = spawnSync('sh', [wrapper, 'prompts', 'use', match[1]], {
28
+ cwd: request.cwd,
29
+ env: { ...process.env, RUNLIST_SESSION_ID: request.session_id },
30
+ encoding: 'utf8', timeout: 12_000, maxBuffer: 4 * 1024 * 1024,
31
+ });
32
+
33
+ if (result.error || result.status !== 0 || !result.stdout?.trim()) {
34
+ const detail = result.error?.message || result.stderr?.trim() || 'No consumed handoff was returned; check that runlist is on PATH.';
35
+ block(`Runlist could not consume ${match[1]}: ${detail.slice(0, 2000)}`);
36
+ process.exit(0);
37
+ }
38
+
39
+ process.stdout.write(JSON.stringify({
40
+ hookSpecificOutput: {
41
+ hookEventName: 'UserPromptSubmit',
42
+ additionalContext: 'Runlist has already consumed the saved prompt requested by the user for this native session. Continue from the handoff below without consuming it again.\n\n'
43
+ + result.stdout + (result.stderr ? '\nRunlist receipt:\n' + result.stderr : ''),
44
+ },
45
+ }) + '\n');
@@ -1,23 +1,31 @@
1
1
  #!/bin/sh
2
- # runlist Codex plugin hook wrapper.
2
+ # runlist compatibility plugin hook wrapper.
3
3
  #
4
4
  # The plugin's hooks drive the whole workflow through the PATH `runlist` binary
5
5
  # (`runlist hud`, `runlist guard`), falling back to the legacy `runlist` alias. If a user enables the plugin but hasn't run
6
- # `npm i -g runlist`, the binary is missing, the hook errors `command not
6
+ # `npm install -g runlist`, the binary is missing, the hook errors `command not
7
7
  # found`, and the session is primed with *silent nothing* — no signal that the
8
8
  # whole workflow is one install away. runlist can't warn about its own absence
9
9
  # (it's the thing that's missing), so the check lives here, outside the binary.
10
10
  #
11
11
  # Usage: runlist-hook [--hint] <runlist-subcommand> [args...]
12
- # --hint emit a one-line install hint to stdout when no CLI is on PATH.
12
+ # --hint emit a short install hint to stdout when no CLI is on PATH.
13
13
  # Only SessionStart passes this, so the hint surfaces once per hook
14
14
  # invocation (stdout becomes session context), never on a tool call.
15
15
  #
16
16
  # When the CLI is missing we always exit 0 with no further output: hooks must
17
17
  # stay non-blocking, and the PreToolUse guard in particular must never deny a
18
18
  # tool just because the optional CLI isn't installed.
19
+ # Keep the published hook command stable for hosts caching its definition.
20
+ # Without node on PATH the JSON adapter cannot start; fall through to the
21
+ # plain-text hint below rather than exiting 127.
22
+ if [ "${1:-}" = "--hint" ] && [ "${2:-}" = "hud" ] && command -v node >/dev/null 2>&1; then
23
+ exec node "$(dirname "$0")/session-start.mjs"
24
+ fi
25
+
19
26
  hint=0
20
- if [ "$1" = "--hint" ]; then
27
+ # --hint-text is the JSON adapter's internal call back into this wrapper.
28
+ if [ "$1" = "--hint" ] || [ "$1" = "--hint-text" ]; then
21
29
  hint=1
22
30
  shift
23
31
  fi
@@ -28,13 +36,18 @@ elif command -v dotmd >/dev/null 2>&1; then
28
36
  CLI=dotmd
29
37
  else
30
38
  if [ "$hint" = "1" ]; then
31
- echo "runlist plugin: neither \`runlist\` nor \`dotmd\` is on PATH, so its hooks are no-ops. Enable the workflow with: npm i -g runlist"
39
+ # Pure shell on purpose (this is the no-CLI path). Say which PATH was searched so the
40
+ # reader can compare it with their terminal's. printf, not echo: some shells' echo
41
+ # interprets backslash sequences in $PATH.
42
+ searched=$(printf '%.200s' "$PATH")
43
+ [ "${#PATH}" -gt 200 ] && searched="$searched..."
44
+ printf '%s\n' "runlist plugin: neither runlist nor dotmd was found on the PATH this app sees, so its hooks are doing nothing here. PATH searched: $searched"
45
+ printf '%s\n' 'Not installed yet? Run `npm install -g runlist`, then fully quit and reopen the app. Installed already? In a terminal compare `command -v runlist` and `echo $PATH` with the PATH above. If the folder holding runlist is missing here, add it, then fully quit and reopen the app: on macOS/Linux `npm prefix -g` plus /bin, in your shell profile; on Windows the `npm prefix -g` folder itself, in your user environment variables.'
32
46
  fi
33
47
  exit 0
34
48
  fi
35
49
 
36
- # PreToolUse fast path: the guard fires on matched Bash, apply_patch, and MCP
37
- # read calls, and
50
+ # PreToolUse fast path: the guard fires on matched tool calls, and
38
51
  # a full Node boot (~100ms: CLI startup + config discovery) is pure overhead for
39
52
  # the vast majority of payloads no guard rule can possibly match. Every rule
40
53
  # needs a `.md` path AND either a prompts/ path (commit/cat/read-prompt) or the
@@ -68,14 +81,19 @@ if [ "$1" = "guard" ]; then
68
81
  exit 0
69
82
  fi
70
83
 
71
- # UserPromptSubmit fast path is retained for compatibility with the Claude
72
- # wrapper; Codex does not register that hook here. It would fire on every
73
- # message that asks for a handoff gets an answer. The glob is strictly weaker
84
+ # UserPromptSubmit hints: Claude Code runs this on every user message;
85
+ # Codex handles explicit consumption in consume-prompt.mjs. Only a handoff or
86
+ # pickup request gets an answer. The glob is strictly weaker
74
87
  # than the Node-side match, so a skipped payload could never have matched.
75
88
  if [ "$1" = "hud" ] && [ "$2" = "--prompt-submit" ]; then
89
+ # Time/session markers gate the new forms; save/continue alone stay cheap.
90
+ # Keep next/last time and context-tight forms too, not just session/tomorrow.
91
+ # Never match bare session: every payload carries session_id. The other
92
+ # markers must not match cwd/transcript_path or ordinary words such as
93
+ # somewhere, holiday, collateral, and timeout.
76
94
  payload=$(cat)
77
95
  case "$payload" in
78
- *[Bb][Aa][Tt][Oo][Nn]*|*[Hh][Aa][Nn][Dd]*[Oo][Ff][Ff]*|*[Rr][Ee][Ss][Uu][Mm][Ee]*|*[Pp][Ii][Cc][Kk]*[Uu][Pp]*)
96
+ *[Bb][Aa][Tt][Oo][Nn]*|*[Hh][Aa][Nn][Dd]*[Oo][Ff][Ff]*|*[Rr][Ee][Ss][Uu][Mm][Ee]*|*[Pp][Ii][Cc][Kk]*[Uu][Pp]*|*\ [Ll][Aa][Tt][Ee][Rr][!a-zA-Z0-9_]*|*[Tt][Oo][Mm][Oo][Rr][Rr][Oo][Ww]*|*[Tt][Oo][Dd][Aa][Yy]*|*[Yy][Ee][Ss][Tt][Ee][Rr][Dd][Aa][Yy]*|*[Ff][Oo][Rr]*[Nn][Oo][Ww]*|*[Nn][Ee][Xx][Tt]*[Ss][Ee][Ss][Ss][Ii][Oo][Nn]*|*[Nn][Ee][Ww]*[Ss][Ee][Ss][Ss][Ii][Oo][Nn]*|*[Ll][Aa][Ss][Tt]*[Ss][Ee][Ss][Ss][Ii][Oo][Nn]*|*[Nn][Ee][Xx][Tt]\ [Tt][Ii][Mm][Ee][!a-zA-Z0-9_]*|*[Ll][Aa][Ss][Tt]*[Tt][Ii][Mm][Ee]*|*[Cc][Oo][Nn][Tt][Ee][Xx][Tt]*[Tt][Ii][Gg][Hh][Tt]*|*[Ww][Hh][Ee][Rr][Ee]\ [WwIiYy]*|*\ [Dd][Aa][Yy]*|*[Tt][Oo][Nn][Ii][Gg][Hh][Tt]*)
79
97
  printf '%s' "$payload" | "$CLI" "$@"
80
98
  exit 0
81
99
  ;;
@@ -0,0 +1,16 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ const wrapper = fileURLToPath(new URL('./runlist-hook', import.meta.url));
5
+ const result = spawnSync('sh', [wrapper, '--hint-text', 'hud'], {
6
+ encoding: 'utf8', timeout: 4_000, maxBuffer: 1024 * 1024,
7
+ });
8
+ const context = result.error || result.status !== 0
9
+ ? `Runlist session orientation failed: ${result.error?.message || result.stderr?.trim() || `exit ${result.status}`}`
10
+ : result.stdout.trim();
11
+
12
+ if (context) {
13
+ process.stdout.write(JSON.stringify({
14
+ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: context },
15
+ }) + '\n');
16
+ }
@@ -0,0 +1,16 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ const wrapper = fileURLToPath(new URL('./runlist-hook', import.meta.url));
5
+ const result = spawnSync('sh', [wrapper, 'hud', '--subagent'], {
6
+ encoding: 'utf8', timeout: 4_000, maxBuffer: 1024 * 1024,
7
+ });
8
+ const context = result.error || result.status !== 0
9
+ ? `Runlist child orientation failed: ${result.error?.message || result.stderr?.trim() || `exit ${result.status}`}`
10
+ : result.stdout.trim();
11
+
12
+ if (context) {
13
+ process.stdout.write(JSON.stringify({
14
+ hookSpecificOutput: { hookEventName: 'SubagentStart', additionalContext: context },
15
+ }) + '\n');
16
+ }
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "hooks": [{ "type": "command", "command": "node \"${PLUGIN_ROOT}/bin/consume-prompt.mjs\"", "timeout": 15 }]
6
+ }
7
+ ],
3
8
  "SessionStart": [
4
9
  {
5
10
  "matcher": "startup|resume|clear|compact",
6
11
  "hooks": [{ "type": "command", "command": "sh \"${PLUGIN_ROOT}/bin/runlist-hook\" --hint hud", "timeout": 5 }]
7
12
  }
8
13
  ],
14
+ "SubagentStart": [
15
+ {
16
+ "matcher": "*",
17
+ "hooks": [{ "type": "command", "command": "node \"${PLUGIN_ROOT}/bin/subagent-start.mjs\"", "timeout": 5 }]
18
+ }
19
+ ],
9
20
  "PreToolUse": [
10
21
  {
11
22
  "matcher": "Bash|apply_patch|mcp__.*__(read_file|read_text_file|read_media_file|read_multiple_files)",
@@ -1,29 +1,45 @@
1
1
  ---
2
2
  name: runlist
3
- description: Manage a repository's plans, docs, decisions, and saved prompts with the runlist CLI. Use when starting or closing plan work, checking the plan queue, or creating or consuming a handoff.
3
+ description: Save and resume session handoffs, and manage a repository's plans, docs, decisions, and saved prompts with the runlist CLI. Use for explicit consume requests for saved prompts, creating or consuming a handoff, starting or closing plan work, or checking the plan queue.
4
4
  ---
5
5
 
6
6
  # Runlist workflow
7
7
 
8
- Use this skill in repositories with `runlist.config.mjs` or `dotmd.config.mjs`. The CLI is `runlist` (from the `runlist` package; `dotmd` remains an alias). The session hook prints a short live orientation. `CODEX_THREAD_ID` is the session identity used for plan claims; do not replace it with a process ID.
8
+ For an explicit `consume <prompt>` request, run `runlist use <prompt>` before file discovery or other task commands. If the native request hook already supplied the consumed handoff, continue from that output without consuming it again. Report a consumption failure before starting the handoff's work.
9
9
 
10
- **Document model:** a hub organizes related plans and supporting docs. Older docs and filenames call hubs *runlists*; this is the same document, not another level below a hub. A hub can be a coordination map or hold ordered plans in its `runlist:` array. A roadmap is an optional hub collecting other hubs and rolling up progress, with direct plan children also supported. Hubs currently use `type: plan` with hub metadata. Stage describes when a plan should ship, while status describes progress; neither adds a document level. Existing `runlist` commands, fields and output labels remain unchanged. See the repository README's “Hubs, Ordered Plans, And Roadmaps” section for examples.
10
+ For installing Runlist or turning on planning, use the **runlist-setup** skill.
11
+ Handoffs need no setup. This skill covers ongoing work.
11
12
 
12
- **Optional filing policy:** with `export const filing = true` (or `{ categoryDepth: 3 }`), `check` warns on missing/multiple/uncategorized homes, while `coverage` lists direct/inherited homes and hub:line evidence. A home is a status-bearing table row whose first cell links the plan; mentions and order lists are references. A child without its own row can inherit a home through explicit `parent_plan:` ancestors. Categories come from headings or the containing hub's filing category. Scoping retains full evidence; incomplete scans suppress absence findings. Neither command assigns homes or repairs rows. Enable the policy or retire an older custom check only with repository authorization. Available in CLI 0.93.0.
13
+ The CLI is `runlist` (from the `runlist` package; `dotmd` remains an alias). The session hook prints a short live orientation. `CODEX_THREAD_ID` is the session identity used for plan claims; do not replace it with a process ID.
14
+
15
+ ## Handoffs (every project)
16
+
17
+ - Save where the session stands: write the resume to a temp file (next concrete move, decisions, gotchas, paths; not a recap). If this session owns an in-session plan, run `runlist baton @/tmp/draft.md` with no slug: it hands off that plan, saving its prompt and releasing the claim. Otherwise run `runlist baton <slug> @/tmp/draft.md`, picking a short slug that names the work yourself; it saves `resume-<slug>`. Report the name baton prints last. In a Git repository with no runlist config, this first baton also writes a handoffs-only `runlist.config.mjs` and keeps prompts in `docs/prompts/`, first moving any an older runlist saved in a root `prompts/`.
18
+ - Resume: `runlist prompts` lists pending handoffs and `runlist use <name>` consumes one. With several pending, ask which; never consume the oldest by default.
19
+ - Consume a saved prompt with `runlist use <prompt-file>` (or bare `runlist use` for the oldest). This archives the prompt (and, where planning is on, claims its linked plan) before printing the body. Only an agent session consumes; a plain terminal gets the body plus the `runlist prompts use <path>` line, and the prompt stays pending. A prompt that ends with that line is still pending: run the command first. For read-only triage, survey names with `runlist prompts`, then peek selected bodies with `runlist prompts show a b c`; use `show --all` only with `--limit N`. Never open pending prompts with a file reader or `cat`.
20
+ - Before a second handoff for the same work, inspect the pending one with `runlist prompts show <file>`; keep it if current or pass `--replace` with the new draft. Replacement archives the old text and refreshes exactly one pending prompt.
21
+ - Inspect `runlist config get promptTracking` before prompt Git operations: `untracked` recommends leaving pending prompts out of commits to reduce churn, `tracked` allows commits, `local` denies them (legacy default). Untracked is not ignored; `git add -A` can include prompts. `runlist config set promptTracking <value>` records an explicit choice without changing ignore files or Git state.
22
+ - **Subagent handoffs:** return completed results to the parent. For unfinished work or an explicit saved handoff, use `runlist baton --subagent <task-slug> @/tmp/draft.md` to save `subagent-<task-slug>`. Under a parent-owned plan, choose a distinct task slug that does not resolve to a plan; do not release the parent's claim. A child owning a dedicated plan can use `runlist baton --subagent <plan-file> @/tmp/draft.md`. Add `--parent resume-<session-name>` to link the child beneath its parent in `rl prompts`; the parent may be saved later. Parent-session batons keep the default `resume-*` naming. Matching prefixes are kept once; conflicting prefixes are refused. Existing prompts keep their names, and `--subagent --replace` refuses a parent-prefixed prompt.
23
+ - **Draft outlines:** `runlist baton --template` prints the resume outline. The Runlist desktop app's Templates editor (not Claude Desktop) saves resume/subagent outlines in `runlist.templates.json`; repository baton instructions still apply.
24
+ - **Child draft:** `runlist baton --subagent --template --parent resume-<name>` prints the saved child outline (built-in: Task, State, Next, Evidence and Boundaries). Fill the draft; its `parent_prompt` travels on save. A launcher can pass `RUNLIST_PARENT_PROMPT` into the child environment instead. Keep full records at durable repo-relative paths with commits and checked results; temporary scratch paths are supplementary. Completed work is a result for the parent to record, not another restart. Repair old links without replacing text: `runlist prompts link <child...> --parent resume-<name>`. Showing or consuming a parent surfaces its pending children without consuming them.
25
+ - If `runlist plans` says planning is off, the project uses runlist for handoffs only: skip the planning bullets below, create no plans or docs, and suggest `runlist setup` only when the user asks for planning.
26
+
27
+ ## Planning projects
28
+
29
+ These apply where `runlist plans` shows a dashboard (a `runlist.config.mjs` or `dotmd.config.mjs` with planning on).
13
30
 
14
31
  - Orient with `runlist plans`. Use `runlist agent-context` for structured state; `runlist briefing` is the comprehensive view and can be large.
32
+ - Create from a draft with `runlist new plan <slug> @/tmp/draft.md` or `runlist new doc <slug> @/tmp/draft.md`. Draft headings become the body; opening frontmatter supplies fields such as `current_state` and `next_step`, while the command fixes `type`.
15
33
  - Read a plan or long doc with `runlist show <file>` (card, outline and open boxes with line numbers, the active phase), then `runlist show <file> --section "<heading>"` one section at a time; never read a plan whole.
16
34
  - Start plan work with `runlist use <plan-file>`. It claims the plan, marks it `in-session`, and prints the card.
17
35
  - Change status with `runlist set <status> <file> [--note "why"]`; this runs validation, lifecycle hooks, ref repairs, and index updates. Never hand-edit a `status:` field.
18
36
  - Close a shipped plan with `runlist archive <file>`; use `partial`, `active`, `awaiting`, or `blocked` when those match the actual state. Valid statuses are repository and type specific: `runlist statuses list --type plan`.
19
- - For a handoff, write a concrete resume draft, then run `runlist baton [<plan-or-slug>] @<draft-file>`. An owned plan is released in the same operation. If a handoff for that work is pending, inspect it with `runlist prompts show <file>`; keep it if current or pass `--replace` with the new draft. Replacement archives the old text and refreshes exactly one pending prompt.
20
- - **Subagent handoffs:** use `runlist baton --subagent <task-slug> @/tmp/draft.md` to save `subagent-<task-slug>`. Under a parent-owned plan, choose a distinct task slug that does not resolve to a plan; do not release the parent's claim. A child owning a dedicated plan can use `runlist baton --subagent <plan-file> @/tmp/draft.md`. Add `--parent resume-<session-name>` to link the child beneath its parent in `rl prompts`; the parent may be saved later. Parent-session batons keep the default `resume-*` naming. Matching prefixes are kept once; conflicting prefixes are refused. Existing prompts keep their names, and `--subagent --replace` refuses a parent-prefixed prompt.
21
- - **Draft outlines:** `runlist baton --template` prints the resume outline. The desktop Templates editor saves resume/subagent outlines in `runlist.templates.json`; repository baton instructions still apply.
22
- - **Child draft:** `runlist baton --subagent --template --parent resume-<name>` prints the saved child outline (built-in: Task, State, Next, Evidence and Boundaries). Fill the draft; its `parent_prompt` travels on save. A launcher can pass `RUNLIST_PARENT_PROMPT` into the child environment instead. Keep full records at durable repo-relative paths with commits and checked results; temporary scratch paths are supplementary. Completed work is a result for the parent to record, not another restart. Repair old links without replacing text: `runlist prompts link <child...> --parent resume-<name>`. Showing or consuming a parent surfaces its pending children without consuming them.
23
- - Consume a saved prompt with `runlist use <prompt-file>` (or bare `runlist use` for the oldest). This archives and claims before printing the body. Only an agent session consumes; a plain terminal gets the body plus the `runlist prompts use <path>` line, and the prompt stays pending. A prompt that ends with that line is still pending: run the command first. For read-only triage, survey names with `runlist prompts`, then peek selected bodies with `runlist prompts show a b c`; use `show --all` only with `--limit N`. Never open pending prompts with a file reader or `cat`.
24
- - Pending prompts are session-local. Commit the tracked plan or source files named by baton, not `docs/prompts/*.md`.
25
- - Create a document with `runlist new <type> <name> @<draft-file>`. A draft starting with frontmatter can supply scaffold fields; `type` is fixed by the command.
37
+ - For a plan handoff, `runlist baton @<draft-file>` releases the plan this session owns, and `runlist baton <plan-file> @<draft-file>` hands off a named one. If baton says `Handoff not done yet`, update the plan body, then run `runlist baton --done --next-step "<next move>"`. Baton prints a commit hint for the plan/source files only.
26
38
  - Record a decision with `runlist new decision <plan> --question "…" @<record-file>`; configured registers may also require `--answers`.
27
39
  - If a lifecycle mutation reports an abandoned transaction, inspect `runlist doctor --transactions` before retrying. Use `--apply` only for cases the doctor identifies as recoverable.
28
40
 
29
41
  The `PreToolUse` hook warns on direct pending-prompt reads and denies hand-edited status transitions or Git operations that would include a live prompt. A repo can opt into blocking direct reads of existing pending prompts with `guard: { promptReads: 'deny' }`. Archived prompts and `runlist prompts show` remain readable. Codex requires explicit trust of a plugin's hooks before they run.
42
+
43
+ **Document model:** a hub organizes related plans and supporting docs. Older docs and filenames call hubs *runlists*; this is the same document, not another level below a hub. A hub can be a coordination map or hold ordered plans in its `runlist:` array. A roadmap is an optional hub collecting other hubs and rolling up progress, with direct plan children also supported. Hubs currently use `type: plan` with hub metadata. Stage describes when a plan should ship, while status describes progress; neither adds a document level. Existing `runlist` commands, fields and output labels remain unchanged. See the repository README's “Hubs, Ordered Plans, And Roadmaps” section for examples.
44
+
45
+ **Optional filing policy:** with `export const filing = true` (or `{ categoryDepth: 3 }`), `check` warns on missing/multiple/uncategorized homes, while `coverage` lists direct/inherited homes and hub:line evidence. A home is a status-bearing table row whose first cell links the plan; mentions and order lists are references. A child without its own row can inherit a home through explicit `parent_plan:` ancestors. Categories come from headings or the containing hub's filing category. Scoping retains full evidence; incomplete scans suppress absence findings. Neither command assigns homes or repairs rows. Enable the policy or retire an older custom check only with repository authorization. Available in CLI 0.93.0.
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: runlist-setup
3
+ description: Set up Runlist planning in a project, including before any Runlist config exists or after handoffs-only use. Use when asked to install, initialize, onboard, turn on planning, or adopt existing project docs into Runlist. Use the runlist skill for ordinary plan, doc, and prompt work after setup.
4
+ ---
5
+
6
+ # Runlist project setup
7
+
8
+ Setup is for planning. Session handoffs need none: if the user only wants to
9
+ save where they are and pick it up later, do not run setup; the first
10
+ `runlist baton <slug> @<draft-file>` in a Git repository writes a handoffs-only
11
+ `runlist.config.mjs` and keeps prompts in `docs/prompts/`. In a handoffs-only
12
+ project (`runlist plans` says planning is off), setup turns planning on: it
13
+ rewrites that config's `mode`, widens its `root` from `docs/prompts` to `docs`,
14
+ adds plans and an index, and keeps the saved prompts where they are.
15
+
16
+ Turn the user's requested choices into a reviewed `runlist setup` preview and
17
+ apply the authorized changes. Ask only for missing preferences that affect the
18
+ requested outcome; honor explicit choices and authorization already given.
19
+ For "show me first" or preview-only requests, return the preview without applying.
20
+
21
+ Inspect the actual shell, working directory, checkout, config, and executable
22
+ paths first. In Bash/Zsh, `type -a runlist rl node npm claude codex opencode`
23
+ reveals competing installations; use `Get-Command ... -All` in PowerShell.
24
+ Resolve aliases, Node/npm scope, and the intended Claude binary when several
25
+ copies exist. Do not install or update an agent host to resolve a path mismatch.
26
+ For the Runlist CLI, use the executable selected by the current shell unless
27
+ the user chose another; report its path. Several installed copies alone do not
28
+ require a new user decision.
29
+
30
+ With no suitable shell or checkout access, supply concrete terminal instructions
31
+ and leave execution outstanding. Bootstrap the CLI with `npm install -g runlist`
32
+ or the user's chosen project scope (`npm install -D runlist`, then
33
+ `npx runlist setup`); `npx runlist setup` is also an ephemeral bootstrap. Host
34
+ hooks still need a persistent CLI on their `PATH`. Execute installations only
35
+ within the authorized scope. A plugin skill is unavailable before that
36
+ plugin is installed and loaded; do not claim an unavailable skill was invoked.
37
+
38
+ Claude Desktop Chat, Cowork, and Claude Code are different execution surfaces.
39
+ Inspect the actual tools: Cowork may use a cloud sandbox or a local VM; Claude
40
+ Code and Codex may run locally or remotely. Local-folder access alone does not
41
+ prove the shell runs on the user's computer. A remote npm success affects that
42
+ environment, not a local CLI, host, or GUI. This plugin provides no Desktop Chat
43
+ execution bridge. If local setup is needed, give commands for a local terminal.
44
+
45
+ Use `runlist setup` rather than rebuilding onboarding from unrelated verbs:
46
+
47
+ - Preview with `runlist setup <explicit-options> --dry-run --json`; inspect the
48
+ proposed paths, policy, adoption, host install, and findings. With no `--yes`,
49
+ noninteractive setup previews without writing or waiting. An interactive
50
+ terminal collects choices and confirms the concrete preview. `--dry-run`
51
+ previews even when `--yes` is present.
52
+ - Apply the same reviewed options with `--yes` when covered by the user's
53
+ authorization. Ask only about choices or side effects not already authorized;
54
+ do not impose another blanket confirmation. `--yes` applies explicit flags and
55
+ defaults; it does not license additional installations or repairs.
56
+ - Fresh defaults: relative `docs/` root, `--prompt-tracking untracked`, no ignore
57
+ edits, `--host none`, no first plan, no adoption. `untracked` is not ignored:
58
+ `git add -A` can include prompts. Alternatives are `tracked` and `local`.
59
+ Preserve existing policy absent an explicit selection. `--gitignore` is opt-in.
60
+ Requests to leave prompts out of routine commits or reduce commit churn select
61
+ the `untracked` recommendation; use `local` for an explicit request to deny
62
+ prompt Git operations through agent guards. Neither policy makes files ignored
63
+ or prevents manual Git operations outside those guards.
64
+ - A fresh project may choose `--docs-root <relative-path>`; a handoffs-only
65
+ project's docs root is always `docs`, so setup refuses another. Preserve existing
66
+ configured roots, JavaScript, and hooks. If a different existing root is
67
+ requested, explain setup's refusal and the manual config/path review needed;
68
+ do not replace the config or move docs to force success.
69
+ - Adopt only user-selected files with repeatable `--adopt <path>`, previewing
70
+ their frontmatter changes before apply. Never blanket-tag existing docs.
71
+ - Fresh setup, and setup turning planning on in a handoffs-only project,
72
+ carries runlist prompts and plans saved before configuration, including
73
+ archives, into the new folders by default. This finishes the
74
+ scaffold rather than tagging existing documents: only valid runlist
75
+ type/status files in the pre-config output folders qualify. Review each move;
76
+ references and local handoff/ownership state move with it. Foreign files and
77
+ destination collisions stay put and are reported. `--no-carry-prompts` opts
78
+ out for both prompts and plans. Other existing configured projects never carry over.
79
+ - Create a first plan only when requested: `--first-plan <slug>` requires
80
+ meaningful, user-supplied `--current-state <text>` (cap 1,500 characters) and
81
+ `--next-step <text>` (cap 800). Ask for missing facts; never invent progress,
82
+ use placeholders, or disable validation. Keep longer detail in the plan body.
83
+ Derive the slug from the requested plan title. The user's stated current state
84
+ and next action are sufficient to preview that plan: save them as metadata.
85
+ Creating a plan does not authorize executing its next action or inspecting a
86
+ different checkout to prove the user's description. Include all requested
87
+ plan fields in the preview and in any later apply command.
88
+ For example, "first plan: text export; today only JSON works; next review the
89
+ exporter" becomes `--first-plan text-export --current-state 'Only JSON export
90
+ works' --next-step 'Review the exporter' --dry-run --json`. Those facts are
91
+ already supplied; do not ask for them again or start the export work.
92
+ - Only explicit `--host claude|codex|opencode` plus apply authorizes the existing
93
+ integration installer. Host `none` is the default. Setup never downloads the
94
+ Runlist GUI. If requested separately, the supported Mac path is
95
+ `runlist desktop install`, then `runlist desktop`; respect the execution location.
96
+
97
+ Report **completed**, **outstanding**, and **failed** separately, with paths and
98
+ concrete next actions. Setup's **notes** are information, not remaining work;
99
+ mention them without counting them as outstanding. Keep pre-existing untagged docs, unavailable tools, and
100
+ host activation/trust/reload steps visible. Use `runlist check` and read-only
101
+ `runlist doctor` findings as follow-up evidence; do not silently run broad
102
+ repairs. CLI/plugin files present do not prove hooks or skills loaded in a
103
+ running or future host session. Once configured, route ongoing work to the
104
+ ordinary `runlist` workflow skill.
105
+
106
+ Setup adoption also previews any missing project scaffold directories. A setup
107
+ preview validates proposed changes; it does not run the whole-project check that
108
+ apply runs. Existing document errors can therefore produce an `attention` result
109
+ and exit 1 after the selected changes apply successfully.
@@ -7,6 +7,12 @@
7
7
  // Directory containing your markdown docs (relative to this config file)
8
8
  export const root = 'docs';
9
9
 
10
+ // Pending prompt Git policy: untracked (recommended, guard warns), tracked
11
+ // (guard allows), or local (guard denies). Untracked is not ignored: git add -A
12
+ // can include prompts. Existing configs that omit this export preserve local.
13
+ // No ignore files or Git state are changed by selecting a policy.
14
+ export const promptTracking = 'untracked';
15
+
10
16
  // Optional owner-written product goal. Read its Markdown body beside a plan's
11
17
  // delivers: field with `runlist yardstick show <plan>` or on pickup cards.
12
18
  // Explicit mark/close/fold commands record owner choices; no fit is inferred.
@@ -140,9 +140,12 @@ export function buildAgentContext(index, config, options = {}) {
140
140
  .sort(comparePromptDocs);
141
141
 
142
142
  const vocabulary = {};
143
+ // A handoffs-only project manages prompts alone; plan and doc statuses would
144
+ // only teach an agent verbs that refuse there.
145
+ const knownTypes = config.planning === false ? metadata.typeOrder.filter(type => type === 'prompt') : metadata.typeOrder;
143
146
  const vocabularyTypes = options.types?.length
144
- ? metadata.typeOrder.filter(type => options.types.includes(type))
145
- : metadata.typeOrder;
147
+ ? knownTypes.filter(type => options.types.includes(type))
148
+ : knownTypes;
146
149
  for (const type of vocabularyTypes) {
147
150
  vocabulary[type] = boundedCollection(
148
151
  (metadata.byType[type] ?? []).map(item => ({
@@ -170,6 +173,9 @@ export function buildAgentContext(index, config, options = {}) {
170
173
  types: options.types?.length ? [...options.types] : null,
171
174
  ...(options.sections ? { sections: [...sections] } : {}),
172
175
  },
176
+ // Handoffs-only projects have no plans to orient on; say so instead of
177
+ // leaving an agent to infer it from an empty plan vocabulary.
178
+ planning: config.planning !== false,
173
179
  statusVocabulary: vocabulary,
174
180
  counts: {
175
181
  documents: index.docs.length,