runlist 0.98.2 → 0.99.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/README.md +8 -2
- package/bin/runlist.mjs +14 -5
- package/package.json +1 -1
- package/plugins/runlist-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/runlist-codex/skills/runlist/SKILL.md +1 -1
- package/src/git-commit-support.mjs +2 -1
- package/src/prompts.mjs +28 -3
- package/src/util.mjs +11 -0
package/README.md
CHANGED
|
@@ -71,8 +71,8 @@ controls are experimental; live retrieval depends on a compatible external
|
|
|
71
71
|
provider. Local chat and push are deferred.
|
|
72
72
|
|
|
73
73
|
<!-- runlist:release:start -->
|
|
74
|
-
CLI and desktop release version: **0.
|
|
75
|
-
[Mac installer and release notes](https://github.com/reowens/runlist/releases/tag/desktop-v0.
|
|
74
|
+
CLI and desktop release version: **0.99.0**.
|
|
75
|
+
[Mac installer and release notes](https://github.com/reowens/runlist/releases/tag/desktop-v0.99.0).
|
|
76
76
|
Normal product releases publish both components; an installed Mac app is updated with rollback after it has quit.
|
|
77
77
|
<!-- runlist:release:end -->
|
|
78
78
|
|
|
@@ -803,6 +803,12 @@ excludes session prompts from `paths`.
|
|
|
803
803
|
Saved prompts are local session state. Consume them with `runlist use`; inspect
|
|
804
804
|
without consuming via `runlist prompts show`. Consuming a baton prompt also claims
|
|
805
805
|
its plan; `runlist use --no-claim` reads and archives it without starting the plan.
|
|
806
|
+
Only an agent session consumes: Claude Code, Codex, OpenCode, or a shell with
|
|
807
|
+
`RUNLIST_SESSION_ID` set. In a plain terminal, `runlist use` prints the body and a
|
|
808
|
+
closing line naming the `runlist prompts use <path>` the session runs, and leaves
|
|
809
|
+
the prompt pending and its plan unclaimed. So `claude "$(runlist use)"` starts a
|
|
810
|
+
session that consumes the prompt itself, and a session that never starts takes
|
|
811
|
+
nothing.
|
|
806
812
|
|
|
807
813
|
New plans are created `planned`; `runlist use` starts one, and
|
|
808
814
|
`runlist new plan <name> --status <status>` sets a different starting status.
|
package/bin/runlist.mjs
CHANGED
|
@@ -119,7 +119,8 @@ Common commands:
|
|
|
119
119
|
set <status> [file] Transition status (start work, finish, archive — all via target status)
|
|
120
120
|
new <type> <name> Create plan/doc/prompt (pipe stdin or @path for body)
|
|
121
121
|
use [<file-or-slug>] Open a doc by type: prompt → consume, plan → start, doc → read
|
|
122
|
-
(no file: consume oldest pending prompt
|
|
122
|
+
(no file: consume oldest pending prompt; outside an
|
|
123
|
+
agent session a prompt is printed, not consumed)
|
|
123
124
|
baton [<plan>|<slug>] @<draft-file>
|
|
124
125
|
Save a resume prompt (+ release the plan, if one is in-session)
|
|
125
126
|
archive <file> Close out a plan (status → archived, move, update refs)
|
|
@@ -422,6 +423,8 @@ prompt statuses
|
|
|
422
423
|
pending Ready for the next session to consume.
|
|
423
424
|
\`runlist prompts use <file>\` prints body + archives atomically.
|
|
424
425
|
\`runlist prompts next\` does the same for the oldest pending.
|
|
426
|
+
Outside an agent session both only print, and the
|
|
427
|
+
prompt stays pending.
|
|
425
428
|
|
|
426
429
|
archived Consumed prompt; body preserved in the archive directory,
|
|
427
430
|
which is the only directory a prompt ever moves into.
|
|
@@ -1284,6 +1287,11 @@ Prompts are documents with \`type: prompt\`, typically saved under
|
|
|
1284
1287
|
docs/prompts/. They seed future Claude sessions; consuming a prompt
|
|
1285
1288
|
prints its body to stdout and atomically archives it (one-shot).
|
|
1286
1289
|
|
|
1290
|
+
Only an agent session consumes (Claude Code, Codex, OpenCode, or a shell
|
|
1291
|
+
with RUNLIST_SESSION_ID set). In a plain terminal \`use\`, \`resume\` and
|
|
1292
|
+
\`next\` print the body and a closing line naming the command the session
|
|
1293
|
+
runs to consume it; the prompt stays pending and its plan unclaimed.
|
|
1294
|
+
|
|
1287
1295
|
\`runlist prompt\` (singular) is an alias for \`runlist prompts\` — every
|
|
1288
1296
|
subcommand below works under either spelling.
|
|
1289
1297
|
|
|
@@ -1330,10 +1338,11 @@ Examples:
|
|
|
1330
1338
|
runlist prompts list --status claimed # already-consumed prompts
|
|
1331
1339
|
runlist prompts --json # JSON output
|
|
1332
1340
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
claude "$(runlist prompts
|
|
1341
|
+
runlist prompts use resume-foo # in a session: consume by slug
|
|
1342
|
+
runlist prompts use docs/prompts/foo.md # by path
|
|
1343
|
+
runlist prompts resume resume-foo # \`resume\` is an alias for \`use\`
|
|
1344
|
+
claude "$(runlist prompts next)" # from a terminal: start claude on the oldest
|
|
1345
|
+
# pending; the session consumes it itself
|
|
1337
1346
|
runlist prompt list # singular alias for \`runlist prompts list\`
|
|
1338
1347
|
|
|
1339
1348
|
runlist prompts show resume-foo # peek without consuming (triage)
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ Use this skill in repositories with `runlist.config.mjs` or `dotmd.config.mjs`.
|
|
|
20
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
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
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. For read-only triage, use `runlist prompts show <file>` or `runlist prompts show --all`; never open pending prompts with a file reader or `cat`.
|
|
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, use `runlist prompts show <file>` or `runlist prompts show --all`; never open pending prompts with a file reader or `cat`.
|
|
24
24
|
- Pending prompts are session-local. Commit the tracked plan or source files named by baton, not `docs/prompts/*.md`.
|
|
25
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.
|
|
26
26
|
- Record a decision with `runlist new decision <plan> --question "…" @<record-file>`; configured registers may also require `--answers`.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Keep the service and worker on the same terminal-qualified matrix.
|
|
2
|
+
// macOS evidence: Homebrew Git 2.56.0 passed the guarded-commit suite on ARM64.
|
|
2
3
|
// Linux evidence: Debian 13 ARM64 / Git 2.47.3, pinned Node 24.21.0.
|
|
3
4
|
export function commitPlatformQualified(platform=process.platform,arch=process.arch){
|
|
4
5
|
return arch==='arm64'&&(platform==='darwin'||platform==='linux');
|
|
5
6
|
}
|
|
6
7
|
export function commitVersionQualified(version,platform=process.platform,arch=process.arch){
|
|
7
8
|
if(!commitPlatformQualified(platform,arch))return false;
|
|
8
|
-
return platform==='darwin'?/^git version 2\.(54|55)\.0(?: |$)/.test(version):version==='git version 2.47.3';
|
|
9
|
+
return platform==='darwin'?/^git version 2\.(54|55|56)\.0(?: |$)/.test(version):version==='git version 2.47.3';
|
|
9
10
|
}
|
|
10
11
|
export function localCommitSupport(version,platform=process.platform,arch=process.arch){
|
|
11
12
|
const available=commitVersionQualified(version,platform,arch);
|
package/src/prompts.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { extractFrontmatter, parseSimpleFrontmatter } from './frontmatter.mjs';
|
|
4
|
-
import { asString, toRepoPath, die, resolveDocPath, resolveRefPath, isArchivedPath } from './util.mjs';
|
|
4
|
+
import { agentSessionSource, asString, toRepoPath, die, resolveDocPath, resolveRefPath, isArchivedPath } from './util.mjs';
|
|
5
5
|
import { buildIndex, resolveDocArg } from './index.mjs';
|
|
6
6
|
import { runQuery } from './query.mjs';
|
|
7
7
|
import { completePlanClaim, regenIndex, renderLifecycleMutation, runArchive } from './lifecycle.mjs';
|
|
@@ -372,6 +372,13 @@ export async function consumePrompt(filePath, config, opts) {
|
|
|
372
372
|
}
|
|
373
373
|
const family = childHandoffs(repoPath, config);
|
|
374
374
|
|
|
375
|
+
// Only an agent session consumes. A plain terminal (`claude "$(runlist use)"`,
|
|
376
|
+
// or someone looking) gets the body and the command the session runs, so a
|
|
377
|
+
// session that never starts takes nothing and claims no plan.
|
|
378
|
+
if (!dryRun && !agentSessionSource(opts.env ?? process.env)) {
|
|
379
|
+
return printUnconsumedPrompt(body + family.notice, repoPath, status);
|
|
380
|
+
}
|
|
381
|
+
|
|
375
382
|
const planRef = asString(parsed.plan);
|
|
376
383
|
let linkedClaim = null;
|
|
377
384
|
let claimSkipReason = null;
|
|
@@ -401,8 +408,8 @@ export async function consumePrompt(filePath, config, opts) {
|
|
|
401
408
|
|
|
402
409
|
// Archive BEFORE emitting the body. If runArchive throws (git mv failure,
|
|
403
410
|
// hook crash, anything), the body must not have already gone to stdout —
|
|
404
|
-
// otherwise
|
|
405
|
-
//
|
|
411
|
+
// otherwise the session reads a prompt that was never archived, and the next
|
|
412
|
+
// session sees the same prompt as pending.
|
|
406
413
|
const archiveResult = runArchive([filePath], config, {
|
|
407
414
|
noIndex,
|
|
408
415
|
showFiles,
|
|
@@ -476,6 +483,24 @@ export async function consumePrompt(filePath, config, opts) {
|
|
|
476
483
|
};
|
|
477
484
|
}
|
|
478
485
|
|
|
486
|
+
export function consumeInstruction(repoPath) {
|
|
487
|
+
return `This prompt is still pending: runlist consumes a prompt only inside an agent session. The session that works on it runs \`runlist prompts use ${repoPath}\` first.`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async function printUnconsumedPrompt(body, repoPath, status) {
|
|
491
|
+
const content = `${body}${body.endsWith('\n') ? '' : '\n'}\n${consumeInstruction(repoPath)}\n`;
|
|
492
|
+
await new Promise((resolve, reject) => process.stdout.write(content, err => err ? reject(err) : resolve()));
|
|
493
|
+
return {
|
|
494
|
+
operation: 'print',
|
|
495
|
+
status: { from: status ?? null, to: status ?? null, changed: false },
|
|
496
|
+
repositoryFiles: [],
|
|
497
|
+
sessionFiles: [],
|
|
498
|
+
generatedFiles: [],
|
|
499
|
+
deferredGeneratedFiles: [],
|
|
500
|
+
claim: null,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
479
504
|
function linkedPlanRepoPath(planRef, promptPath, config) {
|
|
480
505
|
const abs = resolveRefPath(planRef, path.dirname(promptPath), config.repoRoot);
|
|
481
506
|
return abs && existsSync(abs) ? toRepoPath(abs, config.repoRoot) : null;
|
package/src/util.mjs
CHANGED
|
@@ -52,6 +52,17 @@ export function hostSessionSource(env = process.env) {
|
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// The agent session runlist runs inside, or null in a plain terminal. A
|
|
56
|
+
// terminal id names a window, not an agent, so it does not count: consuming a
|
|
57
|
+
// prompt there would archive it with no session to read it.
|
|
58
|
+
export function agentSessionSource(env = process.env) {
|
|
59
|
+
const source = hostSessionSource(env);
|
|
60
|
+
return source && source.scope !== 'terminal' ? source : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Every variable that can name a session, for a caller that must clear them all.
|
|
64
|
+
export const SESSION_ID_VARIABLES = SESSION_ID_SOURCES.map(source => source.variable);
|
|
65
|
+
|
|
55
66
|
// The session id the environment names, or null when it names none.
|
|
56
67
|
export function hostSessionId(env = process.env) {
|
|
57
68
|
return hostSessionSource(env)?.id ?? null;
|