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.
- package/GETTING_STARTED.md +249 -0
- package/README.md +414 -46
- package/bin/runlist.mjs +402 -51
- package/package.json +3 -1
- package/plugins/runlist-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/runlist-codex/bin/consume-prompt.mjs +45 -0
- package/plugins/runlist-codex/bin/runlist-hook +29 -11
- package/plugins/runlist-codex/bin/session-start.mjs +16 -0
- package/plugins/runlist-codex/bin/subagent-start.mjs +16 -0
- package/plugins/runlist-codex/hooks/hooks.json +11 -0
- package/plugins/runlist-codex/skills/runlist/SKILL.md +27 -11
- package/plugins/runlist-codex/skills/runlist-setup/SKILL.md +109 -0
- package/runlist.config.example.mjs +6 -0
- package/src/agent-context.mjs +8 -2
- package/src/agent-hosts.mjs +305 -0
- package/src/app-create.mjs +11 -1
- package/src/atomic-mutation.mjs +140 -35
- package/src/baton.mjs +127 -33
- package/src/codex-integration.mjs +57 -7
- package/src/commands.mjs +92 -38
- package/src/config-command.mjs +115 -0
- package/src/config-setting.mjs +163 -0
- package/src/config.mjs +57 -12
- package/src/doctor-hosts.mjs +192 -0
- package/src/doctor.mjs +16 -6
- package/src/export.mjs +11 -0
- package/src/git.mjs +58 -2
- package/src/guard.mjs +41 -13
- package/src/handoff-record.mjs +290 -30
- package/src/handoffs-help.mjs +143 -0
- package/src/handoffs-mode.mjs +486 -0
- package/src/handoffs-scope.mjs +98 -0
- package/src/help.mjs +72 -0
- package/src/host-integration.mjs +7 -1
- package/src/hud.mjs +174 -29
- package/src/index.mjs +11 -4
- package/src/init.mjs +255 -160
- package/src/install.mjs +35 -18
- package/src/installation.mjs +110 -27
- package/src/journal.mjs +2 -2
- package/src/lifecycle.mjs +22 -11
- package/src/managed-path.mjs +68 -4
- package/src/migrate.mjs +7 -0
- package/src/naming.mjs +26 -0
- package/src/new.mjs +202 -91
- package/src/pickup-card.mjs +1 -1
- package/src/plan-changes.mjs +49 -0
- package/src/prompt-handoff.mjs +16 -0
- package/src/prompts.mjs +101 -27
- package/src/rename.mjs +13 -0
- package/src/setup-carry.mjs +291 -0
- package/src/setup-waiting.mjs +20 -0
- package/src/setup-worker.mjs +257 -0
- package/src/setup.mjs +404 -0
- package/src/statuses.mjs +5 -2
- package/src/update.mjs +239 -79
- package/src/use.mjs +4 -2
- package/src/validate.mjs +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runlist",
|
|
3
|
-
"version": "0.
|
|
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",
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
72
|
-
#
|
|
73
|
-
#
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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.
|
package/src/agent-context.mjs
CHANGED
|
@@ -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
|
-
?
|
|
145
|
-
:
|
|
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,
|