dotmd-cli 0.77.0 → 0.77.2
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 +7 -1
- package/bin/dotmd.mjs +18 -22
- package/package.json +2 -1
- package/src/completions.mjs +2 -2
- package/src/config.mjs +15 -3
- package/src/prompts.mjs +14 -19
- package/src/util.mjs +9 -0
package/README.md
CHANGED
|
@@ -20,6 +20,10 @@ npm install -D dotmd-cli # project scripts via node_modules/.bin
|
|
|
20
20
|
npx dotmd-cli init # try it without installing
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
`runlist` is the canonical executable, `rl` is its short convenience alias, and
|
|
24
|
+
`dotmd` remains supported during the compatibility window. All three invoke the
|
|
25
|
+
same CLI; examples below retain `dotmd` while the public package identity does.
|
|
26
|
+
|
|
23
27
|
Maintainer release automation is POSIX-only because it uses Bash and POSIX
|
|
24
28
|
command-line tools. The published Node.js CLI remains cross-platform.
|
|
25
29
|
|
|
@@ -36,6 +40,8 @@ dotmd doctor --session # what identity dotmd sees here, and from where
|
|
|
36
40
|
```
|
|
37
41
|
|
|
38
42
|
Both are one-time and global; `dotmd update` keeps them in step with the CLI.
|
|
43
|
+
Codex needs no install for identity: it exports `CODEX_THREAD_ID` to every tool
|
|
44
|
+
shell, and dotmd reads it as a per-session identity automatically.
|
|
39
45
|
|
|
40
46
|
### Claude Code Plugin
|
|
41
47
|
|
|
@@ -159,7 +165,7 @@ readable for compatibility and can be migrated with `dotmd lint --fix`.
|
|
|
159
165
|
|---|---|---|
|
|
160
166
|
| `plan` | Executable work | `in-session`, `active`, `planned`, `blocked`, `partial`, `paused`, `awaiting`, `queued-after`, `archived` |
|
|
161
167
|
| `doc` | Specs, ADRs, audits, and reference material | `draft`, `active`, `review`, `reference`, `deprecated`, `archived` |
|
|
162
|
-
| `prompt` | Saved future-session instructions | `pending`, `
|
|
168
|
+
| `prompt` | Saved future-session instructions | `pending`, `archived` |
|
|
163
169
|
|
|
164
170
|
Status definitions can be customized per type. Rich status objects co-locate
|
|
165
171
|
display, staleness, validation, terminal, and archive behavior in one place.
|
package/bin/dotmd.mjs
CHANGED
|
@@ -103,7 +103,7 @@ const HELP = {
|
|
|
103
103
|
|
|
104
104
|
Common commands:
|
|
105
105
|
plans Live plans (excludes archived)
|
|
106
|
-
prompts Prompt queue/admin (list, next, archive, new
|
|
106
|
+
prompts Prompt queue/admin (list, next, archive, new)
|
|
107
107
|
briefing Full briefing with plan counts + next steps
|
|
108
108
|
agent-context Compact bounded JSON context for agents
|
|
109
109
|
set <status> [file] Transition status (start work, finish, archive — all via target status)
|
|
@@ -219,7 +219,7 @@ View & Query:
|
|
|
219
219
|
plans Live plans (excludes archived; --include-archived for all)
|
|
220
220
|
use [<file-or-slug>] Open a doc by type: prompt → consume, plan → start, doc → read
|
|
221
221
|
baton [<plan>|<slug>] <@<file>|-> Save a resume prompt; releases the plan + prints the commit when one is in-session
|
|
222
|
-
prompts [list|show|archive|new
|
|
222
|
+
prompts [list|show|archive|new] Prompt admin (list / peek / archive / save). Use \`dotmd use\` to consume.
|
|
223
223
|
stale Stale docs (preset)
|
|
224
224
|
actionable Docs with next steps (preset)
|
|
225
225
|
|
|
@@ -359,21 +359,21 @@ doc statuses
|
|
|
359
359
|
────────────────────────────────────────────────────────────────────
|
|
360
360
|
prompt statuses
|
|
361
361
|
|
|
362
|
+
A prompt has two states and no third.
|
|
363
|
+
|
|
362
364
|
pending Ready for the next session to consume.
|
|
363
365
|
\`dotmd prompts use <file>\` prints body + archives atomically.
|
|
364
366
|
\`dotmd prompts next\` does the same for the oldest pending.
|
|
365
367
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
Still listed by \`dotmd prompts list\`.
|
|
369
|
-
\`dotmd prompts unhold <file>\` → pending.
|
|
370
|
-
|
|
371
|
-
shelved Legacy spelling for held prompts. \`dotmd prompts shelve\`
|
|
372
|
-
now writes \`status: held\`.
|
|
373
|
-
|
|
374
|
-
claimed Legacy intermediate state (atomic use → archived now).
|
|
368
|
+
archived Consumed prompt; body preserved in the archive directory,
|
|
369
|
+
which is the only directory a prompt ever moves into.
|
|
375
370
|
|
|
376
|
-
|
|
371
|
+
\`held\`, \`shelved\` and \`claimed\` were removed 2026-08-30, along with
|
|
372
|
+
\`prompts hold\` / \`unhold\` / \`shelve\` / \`unshelve\` and the
|
|
373
|
+
prompts/held/ bucket. A prompt directs work, so parking one instead of
|
|
374
|
+
archiving it left work state outside the plan that owns it, fighting with
|
|
375
|
+
that plan's own status. If a prompt should not be consumed, lift its content
|
|
376
|
+
into the plan and archive the prompt.
|
|
377
377
|
|
|
378
378
|
────────────────────────────────────────────────────────────────────
|
|
379
379
|
Related commands:
|
|
@@ -504,9 +504,10 @@ another session's work.
|
|
|
504
504
|
a unique bare slug / basename across the doc roots (\`set paused auth-revamp\`).
|
|
505
505
|
Ambiguous slugs error with the candidate list instead of guessing.
|
|
506
506
|
When the path is omitted, exactly one plan must be owned by this session.
|
|
507
|
-
Claude Code session IDs are recognized automatically
|
|
508
|
-
|
|
509
|
-
|
|
507
|
+
Claude Code and Codex session IDs are recognized automatically (Codex exports
|
|
508
|
+
CODEX_THREAD_ID to every tool shell), as is OpenCode (via OPENCODE_PID — per
|
|
509
|
+
OpenCode process, not per session). Other hosts must set RUNLIST_SESSION_ID;
|
|
510
|
+
anonymous ownership mutations fail closed.
|
|
510
511
|
Pickup hooks use at-least-once delivery with a stable operationId; hook side
|
|
511
512
|
effects must deduplicate that ID.
|
|
512
513
|
|
|
@@ -995,7 +996,7 @@ Other options:
|
|
|
995
996
|
|
|
996
997
|
For plans, the default status vocabulary is: in-session, active, planned,
|
|
997
998
|
blocked, partial, paused, awaiting, queued-after, archived.
|
|
998
|
-
For prompts: pending (default),
|
|
999
|
+
For prompts: pending (default), archived.
|
|
999
1000
|
|
|
1000
1001
|
Use --dry-run (-n) to preview without creating the file.`,
|
|
1001
1002
|
|
|
@@ -1132,11 +1133,6 @@ Subcommands:
|
|
|
1132
1133
|
show <file-or-slug> Read-only peek: print the body WITHOUT consuming
|
|
1133
1134
|
(triage). \`peek\` is an alias.
|
|
1134
1135
|
archive <file-or-slug> Archive a prompt without printing its body
|
|
1135
|
-
hold <file-or-slug> Park a prompt (status → held) under prompts/held/:
|
|
1136
|
-
kept in list, hidden from hud/briefing pending
|
|
1137
|
-
surfaces, skipped by \`prompts next\`.
|
|
1138
|
-
unhold <file-or-slug> Move a held prompt back to pending.
|
|
1139
|
-
shelve / unshelve Legacy aliases for hold / unhold.
|
|
1140
1136
|
new <slug> [body] Create a new prompt (alias for
|
|
1141
1137
|
\`dotmd new prompt <slug> [body]\`)
|
|
1142
1138
|
|
|
@@ -1144,7 +1140,7 @@ Subcommands:
|
|
|
1144
1140
|
slug matching a prompt basename, or a unique substring of a prompt
|
|
1145
1141
|
path. Ambiguous substrings error with the candidate list.
|
|
1146
1142
|
|
|
1147
|
-
Default prompt statuses: pending,
|
|
1143
|
+
Default prompt statuses: pending, archived.
|
|
1148
1144
|
|
|
1149
1145
|
Examples:
|
|
1150
1146
|
dotmd prompts # pending prompts (default)
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dotmd-cli",
|
|
3
|
-
"version": "0.77.
|
|
3
|
+
"version": "0.77.2",
|
|
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",
|
|
7
7
|
"bin": {
|
|
8
8
|
"runlist": "bin/runlist.mjs",
|
|
9
|
+
"rl": "bin/runlist.mjs",
|
|
9
10
|
"dotmd": "bin/dotmd.mjs"
|
|
10
11
|
},
|
|
11
12
|
"exports": {
|
package/src/completions.mjs
CHANGED
|
@@ -39,7 +39,7 @@ ${Object.entries(COMMAND_WORDS).map(([command, words]) =>
|
|
|
39
39
|
*) COMPREPLY=( $(compgen -W "${GLOBAL_FLAGS.join(' ')}" -- "$cur") ) ;;
|
|
40
40
|
esac
|
|
41
41
|
}
|
|
42
|
-
complete -F _runlist runlist dotmd`;
|
|
42
|
+
complete -F _runlist runlist rl dotmd`;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function zshCompletion() {
|
|
@@ -81,7 +81,7 @@ ${Object.entries(COMMAND_WORDS).map(([command, words]) =>
|
|
|
81
81
|
|
|
82
82
|
_describe 'flag' global_flags
|
|
83
83
|
}
|
|
84
|
-
compdef _runlist runlist dotmd`;
|
|
84
|
+
compdef _runlist runlist rl dotmd`;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function runCompletions(argv) {
|
package/src/config.mjs
CHANGED
|
@@ -37,9 +37,17 @@ const DEFAULTS = {
|
|
|
37
37
|
context: { expanded: ['active'], listed: ['draft', 'review'], counted: ['reference', 'deprecated', 'archived'] },
|
|
38
38
|
staleDays: { draft: 30, active: 14, review: 14 },
|
|
39
39
|
},
|
|
40
|
+
// A prompt has two states and no third. It is pending — the next session
|
|
41
|
+
// should consume it — or it is archived, consumed, body kept under the
|
|
42
|
+
// archive directory. `held`, `shelved` and `claimed` were removed 2026-08-30:
|
|
43
|
+
// a prompt directs work, so parking one instead of archiving it leaves work
|
|
44
|
+
// state sitting outside the plan that owns it, where it fights with the
|
|
45
|
+
// plan's own status and neither one is the truth. If a prompt should not be
|
|
46
|
+
// consumed, its content belongs in the plan and the prompt should be
|
|
47
|
+
// archived. See lifecycle.filedStatuses — prompts file nowhere.
|
|
40
48
|
prompt: {
|
|
41
|
-
statuses: ['pending', '
|
|
42
|
-
context: { expanded: ['pending'],
|
|
49
|
+
statuses: ['pending', 'archived'],
|
|
50
|
+
context: { expanded: ['pending'], counted: ['archived'] },
|
|
43
51
|
staleDays: { pending: 30 },
|
|
44
52
|
},
|
|
45
53
|
},
|
|
@@ -64,7 +72,11 @@ const DEFAULTS = {
|
|
|
64
72
|
// F15: per-status filing buckets (status → dirName). Built-in held/paused
|
|
65
73
|
// statuses file under the owning type folder; archive remains a separate
|
|
66
74
|
// primitive untouched.
|
|
67
|
-
|
|
75
|
+
// Plans only. A paused plan files under <plansDir>/held/ so the live plan
|
|
76
|
+
// list stays readable. Prompts were removed from this map 2026-08-30 along
|
|
77
|
+
// with their held/shelved statuses — `archived` is the only directory a
|
|
78
|
+
// prompt ever moves into.
|
|
79
|
+
filedStatuses: { paused: 'held' },
|
|
68
80
|
// Types whose archive nests under their own type dir (<typeDir>/<archiveDir>,
|
|
69
81
|
// e.g. docs/prompts/archived/) instead of the shared <root>/<archiveDir>.
|
|
70
82
|
// Prompts are session-local churn — keeping their archive out of the shared
|
package/src/prompts.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { extractFrontmatter, parseSimpleFrontmatter } from './frontmatter.mjs';
|
|
|
4
4
|
import { 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
|
-
import { completePlanClaim, regenIndex, renderLifecycleMutation, runArchive
|
|
7
|
+
import { completePlanClaim, regenIndex, renderLifecycleMutation, runArchive } from './lifecycle.mjs';
|
|
8
8
|
import { runNew } from './new.mjs';
|
|
9
9
|
import { green, dim, yellow } from './color.mjs';
|
|
10
10
|
import { authorizeManagedSource } from './managed-path.mjs';
|
|
@@ -21,11 +21,23 @@ import { LEGACY_STATE_DIR, STATE_DIR } from './naming.mjs';
|
|
|
21
21
|
// `resume` is an alias for `use` — agents reach for "resume" when continuing a
|
|
22
22
|
// session; `use` reads as internal mechanics. Both names stay valid; the
|
|
23
23
|
// canonical output ("Consumed: …") is unchanged.
|
|
24
|
-
const SUBCOMMANDS = new Set(['list', 'next', 'use', 'resume', 'show', 'peek', 'archive', 'new'
|
|
24
|
+
const SUBCOMMANDS = new Set(['list', 'next', 'use', 'resume', 'show', 'peek', 'archive', 'new']);
|
|
25
|
+
|
|
26
|
+
// Removed 2026-08-30 with the `held`/`shelved` statuses. Named rather than left
|
|
27
|
+
// to fall through to the list filter below, which would silently answer a
|
|
28
|
+
// removed command with an empty list — the caller needs to be told the state is
|
|
29
|
+
// gone and what replaced it.
|
|
30
|
+
const REMOVED_SUBCOMMANDS = new Set(['hold', 'unhold', 'shelve', 'unshelve']);
|
|
25
31
|
|
|
26
32
|
export async function runPrompts(argv, config, opts = {}) {
|
|
27
33
|
const sub = argv[0];
|
|
28
34
|
|
|
35
|
+
if (sub && REMOVED_SUBCOMMANDS.has(sub)) {
|
|
36
|
+
die(`\`dotmd prompts ${sub}\` was removed — a prompt is pending or archived, and nothing else.\n`
|
|
37
|
+
+ 'A prompt directs work, so parking one instead of archiving it leaves work state outside the\n'
|
|
38
|
+
+ 'plan that owns it. Lift its content into that plan, then `dotmd prompts archive <file>`.');
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
if (!sub || !SUBCOMMANDS.has(sub)) {
|
|
30
42
|
return runPromptsList(argv, config, opts);
|
|
31
43
|
}
|
|
@@ -40,10 +52,6 @@ export async function runPrompts(argv, config, opts = {}) {
|
|
|
40
52
|
case 'peek': return runPromptsShow(rest, config);
|
|
41
53
|
case 'archive': return runPromptsArchive(rest, config, opts);
|
|
42
54
|
case 'new': return runPromptsNew(rest, config, opts);
|
|
43
|
-
case 'hold': return runPromptsHold(rest, config, opts);
|
|
44
|
-
case 'unhold': return runPromptsUnhold(rest, config, opts);
|
|
45
|
-
case 'shelve': return runPromptsHold(rest, config, opts);
|
|
46
|
-
case 'unshelve': return runPromptsUnhold(rest, config, opts);
|
|
47
55
|
}
|
|
48
56
|
}
|
|
49
57
|
|
|
@@ -562,16 +570,3 @@ async function runPromptsNew(argv, config, opts = {}) {
|
|
|
562
570
|
return runNew(['prompt', ...argv], config, opts);
|
|
563
571
|
}
|
|
564
572
|
|
|
565
|
-
async function runPromptsHold(argv, config, opts = {}) {
|
|
566
|
-
const input = argv.find(a => !a.startsWith('-'));
|
|
567
|
-
if (!input) die('Usage: dotmd prompts hold <file-or-slug>');
|
|
568
|
-
const filePath = resolvePromptInput(input, config);
|
|
569
|
-
return runStatus([filePath, 'held'], config, opts);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
async function runPromptsUnhold(argv, config, opts = {}) {
|
|
573
|
-
const input = argv.find(a => !a.startsWith('-'));
|
|
574
|
-
if (!input) die('Usage: dotmd prompts unhold <file-or-slug>');
|
|
575
|
-
const filePath = resolvePromptInput(input, config);
|
|
576
|
-
return runStatus([filePath, 'pending'], config, opts);
|
|
577
|
-
}
|
package/src/util.mjs
CHANGED
|
@@ -26,6 +26,15 @@ const SESSION_ID_SOURCES = [
|
|
|
26
26
|
{ variable: 'DOTMD_SESSION_ID', prefix: null, scope: 'session', host: 'explicit override' },
|
|
27
27
|
{ variable: 'CLAUDE_CODE_SESSION_ID', prefix: null, scope: 'session', host: 'Claude Code' },
|
|
28
28
|
{ variable: 'CLAUDE_SESSION_ID', prefix: null, scope: 'session', host: 'Claude Code' },
|
|
29
|
+
// Verified against codex-cli 0.153.4 (2026-09-07): `codex exec` ran
|
|
30
|
+
// `env | grep ^CODEX` in a tool shell and printed both variables, each equal
|
|
31
|
+
// to the "session id:" line Codex prints in its own header, so they name one
|
|
32
|
+
// Codex thread (session), not the Codex process. THREAD_ID is what the exec
|
|
33
|
+
// path (core/src/exec.rs) sets first; SESSION_ID is its older twin. No
|
|
34
|
+
// CODEX_PID exists, so a Codex claim records no owning process and reads as
|
|
35
|
+
// 'unverifiable' — a takeover stays an explicit --force.
|
|
36
|
+
{ variable: 'CODEX_THREAD_ID', prefix: null, scope: 'session', host: 'Codex' },
|
|
37
|
+
{ variable: 'CODEX_SESSION_ID', prefix: null, scope: 'session', host: 'Codex' },
|
|
29
38
|
{ variable: 'OPENCODE_SESSION_ID', prefix: null, scope: 'session', host: 'OpenCode' },
|
|
30
39
|
{ variable: 'OPENCODE_SESSION', prefix: null, scope: 'session', host: 'OpenCode' },
|
|
31
40
|
{ variable: 'OPENCODE_PID', prefix: 'opencode', scope: 'process', host: 'OpenCode' },
|