dotmd-cli 0.60.0 → 0.62.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 +102 -13
- package/bin/dotmd.mjs +109 -8
- package/dotmd.config.example.mjs +21 -4
- package/package.json +1 -1
- package/src/baton.mjs +231 -0
- package/src/commands.mjs +2 -2
- package/src/completions.mjs +1 -1
- package/src/doctor.mjs +38 -0
- package/src/guard.mjs +84 -32
- package/src/health.mjs +55 -9
- package/src/hud.mjs +40 -18
- package/src/index.mjs +8 -1
- package/src/lifecycle.mjs +4 -1
- package/src/new.mjs +7 -1
- package/src/prompts.mjs +25 -1
- package/src/query.mjs +305 -46
- package/src/render.mjs +22 -3
- package/src/runlist.mjs +118 -0
- package/src/validate.mjs +29 -2
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ dotmd new my-feature # scaffold a new doc with frontmatter
|
|
|
33
33
|
dotmd list # index all docs grouped by status
|
|
34
34
|
dotmd check # validate frontmatter and references
|
|
35
35
|
dotmd context # compact briefing (great for LLM context)
|
|
36
|
-
dotmd doctor #
|
|
36
|
+
dotmd doctor # preview fixes for everything (--apply to write)
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
### Shell Completion
|
|
@@ -83,7 +83,9 @@ Explicit frontmatter always wins. Body extraction is a cushion for partially-tag
|
|
|
83
83
|
## What It Does
|
|
84
84
|
|
|
85
85
|
- **Index** — group docs by status, with auto-detected progress bars (from `- [ ]` checklists) and next steps
|
|
86
|
-
- **Query** — filter by status, keyword, module, surface, owner, staleness
|
|
86
|
+
- **Query** — filter by status, keyword, module, surface, owner, staleness; `dotmd grep` searches document bodies too
|
|
87
|
+
- **Resume handoff** — `dotmd baton` saves a resume prompt for the next session and releases the in-session plan in one verb
|
|
88
|
+
- **Runlists** — group plans into an ordered sequence on a hub plan; `dotmd runlist next` picks up the next one
|
|
87
89
|
- **Validate** — check for missing fields, broken references, broken body links, stale dates
|
|
88
90
|
- **Stats** — health dashboard with staleness, completeness, audit coverage
|
|
89
91
|
- **Graph** — visualize document relationships as text, Graphviz DOT, or JSON
|
|
@@ -129,7 +131,7 @@ Design doc content here...
|
|
|
129
131
|
- [ ] Add tests
|
|
130
132
|
```
|
|
131
133
|
|
|
132
|
-
The only required field is `status`. Everything else is optional but unlocks more features. The `type` field (`plan`, `doc`, or `
|
|
134
|
+
The only required field is `status`. Everything else is optional but unlocks more features. The `type` field (`plan`, `doc`, or `prompt`) enables type-specific statuses and smarter context briefings.
|
|
133
135
|
|
|
134
136
|
> **Note:** `module:` and `surface:` (singular) are deprecated as of 0.36.3 — use the plural array forms (`modules:`, `surfaces:`). Run `dotmd lint --fix` to migrate existing docs.
|
|
135
137
|
|
|
@@ -177,6 +179,34 @@ Each *quiet* status (`partial`, `queued-after`, `archived`) is exempt from stale
|
|
|
177
179
|
|
|
178
180
|
> **Heads-up:** versions before 0.15 included a `done` plan status in the defaults. It saw effectively zero real-world use (plans went `in-session`/`active` → `archived` directly), so it was dropped from the built-in vocabulary. To finish a plan, run `dotmd archive <plan-file>` — or, if you preferred the previous behavior, add `done` back via the `types.plan.statuses` key in your config.
|
|
179
181
|
|
|
182
|
+
### Runlists: ordered groups of plans
|
|
183
|
+
|
|
184
|
+
When several plans must ship in a known order (an "auth revamp" sprint with extract → rewrite → cleanup phases), declare a `runlist:` array on a hub plan instead of chaining `queued-after` per pair or keeping the order in prose:
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
---
|
|
188
|
+
type: plan
|
|
189
|
+
status: active
|
|
190
|
+
title: Auth Revamp
|
|
191
|
+
runlist:
|
|
192
|
+
- auth-revamp-01-extract.md
|
|
193
|
+
- auth-revamp-02-rewrite.md
|
|
194
|
+
- auth-revamp-03-cleanup.md
|
|
195
|
+
---
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
dotmd runlist <hub> # children + statuses in order; first non-archived child marked →
|
|
200
|
+
dotmd runlist next <hub> # pick up the next child (marks in-session + prints it)
|
|
201
|
+
dotmd runlists # dashboard of coordination-hub runlists (--json, --limit N)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`runlist next` stops with a runlist-aware error if the next child isn't in a workable status (`active` / `planned` / `in-session`), so you resolve the blocker before continuing. Each child should set `parent_plan:` pointing back at the hub — `dotmd check` warns when it doesn't. There's no separate doc type: a runlist hub is just a plan with the array.
|
|
205
|
+
|
|
206
|
+
In `dotmd plans`, hubs are tagged `[RUNLIST]` (not `[ACTIVE]`) with their children folded underneath — the hub row shows `done/total` progress and the next-pickup `→`, so a multi-plan sprint reads as one runlist instead of cluttering the triage list. A child whose hub is filtered out of the view (e.g. `--status active` when the hub is `planned`) still renders on its own.
|
|
207
|
+
|
|
208
|
+
**Coordination runlists.** A `runlist:` array fits a small ordered *sprint*. For a large, prose-first *coordination map* (a domain hub pointing at many plans, with gating/sequence rationale, sometimes unordered), set `execution_mode: coordination` instead — or just name it `*-runlist`. These aren't folded: `dotmd plans` lifts them into a pinned `Runlists` section and out of the active count, and `dotmd runlists` shows that dashboard standalone. `dotmd briefing` and `dotmd health` do the same — coordination hubs are pulled out of the live/active counts into a `runlists` bucket (briefing) and a held-out `Runlists:` tally (health), so they don't inflate the actionable-plan or aging numbers. The per-hub "N related" count comes from `related_plans:`. `dotmd check` nudges a `*-runlist` hub missing `execution_mode: coordination`.
|
|
209
|
+
|
|
180
210
|
## Commands
|
|
181
211
|
|
|
182
212
|
```
|
|
@@ -191,27 +221,36 @@ dotmd unblocks <file> Show what depends on this doc
|
|
|
191
221
|
dotmd health [--json] Plan velocity, aging, and pipeline
|
|
192
222
|
dotmd briefing Compact summary for session start
|
|
193
223
|
dotmd context [--summarize] Full briefing (LLM-oriented)
|
|
224
|
+
dotmd agent-context Compact bounded JSON context for agents
|
|
194
225
|
dotmd focus [status] Detailed view for one status group
|
|
195
|
-
dotmd query [filters] Filtered search
|
|
196
|
-
dotmd
|
|
226
|
+
dotmd query [filters] Filtered search (--body scans document bodies)
|
|
227
|
+
dotmd grep <term> Keyword search incl. bodies — "which doc discussed X?"
|
|
228
|
+
dotmd plans List live plans (excludes archived)
|
|
197
229
|
dotmd modules Module dashboard (plans grouped by module)
|
|
198
230
|
dotmd module <name> Plans for one module, grouped by status
|
|
231
|
+
dotmd surfaces List configured surface taxonomy
|
|
199
232
|
dotmd stale List stale docs
|
|
200
233
|
dotmd actionable List docs with next steps
|
|
201
234
|
dotmd index [--print] Generate/update docs.md index block
|
|
202
235
|
dotmd hud Actionable triage (silent when clean — ideal SessionStart hook)
|
|
203
|
-
dotmd use <file>
|
|
204
|
-
|
|
236
|
+
dotmd use [<file-or-slug>] Open by type: prompt → consume, plan → start, doc → read
|
|
237
|
+
(no arg: consume the oldest pending prompt)
|
|
238
|
+
dotmd set <status> <file> Change a document's status (--note appends why to Version History)
|
|
239
|
+
dotmd baton [<plan>|<slug>] <@draft|-> Save a resume prompt; releases the in-session plan
|
|
240
|
+
dotmd runlist <hub> [next] Show or walk an ordered group of plans
|
|
205
241
|
dotmd status <file> <status> Transition document status (deprecated; prefer set)
|
|
206
242
|
dotmd archive <file> Archive (status + move + update refs)
|
|
207
243
|
dotmd bulk archive <files> Archive multiple files at once
|
|
244
|
+
dotmd bulk-tag [files] Tag pre-existing untagged .md files
|
|
208
245
|
dotmd touch <file> Bump updated date
|
|
209
246
|
dotmd touch --git Bulk-sync dates from git history
|
|
210
|
-
dotmd doctor
|
|
247
|
+
dotmd doctor [--apply] Fix refs, lint, dates, index (previews by default)
|
|
248
|
+
dotmd self-check Project/version skew diagnostic
|
|
211
249
|
dotmd fix-refs Auto-fix broken reference paths
|
|
212
250
|
dotmd lint [--fix] Check and auto-fix frontmatter issues
|
|
213
251
|
dotmd rename <old> <new> Rename doc and update references
|
|
214
252
|
dotmd migrate <f> <old> <new> Batch update a frontmatter field
|
|
253
|
+
dotmd ship [patch|minor|major] Regen + commit + bump in one step
|
|
215
254
|
dotmd notion <sub> [db-id] Notion import/export/sync
|
|
216
255
|
dotmd export [file] Export docs as md, html, or json
|
|
217
256
|
dotmd summary <file> AI summary of a document
|
|
@@ -219,19 +258,22 @@ dotmd glossary <term> Look up domain terms + related docs
|
|
|
219
258
|
dotmd watch [command] Re-run a command on file changes
|
|
220
259
|
dotmd diff [file] Show changes since last updated date
|
|
221
260
|
dotmd new <type> <name> Create a new doc (type: doc, plan, or prompt)
|
|
222
|
-
dotmd prompts [sub] Manage saved prompts (list,
|
|
261
|
+
dotmd prompts [sub] Manage saved prompts (list, show, hold, archive, new)
|
|
262
|
+
dotmd statuses [sub] Manage per-project status taxonomy
|
|
223
263
|
dotmd journal [flags] View opt-in command-usage journal (DOTMD_JOURNAL=1)
|
|
224
264
|
dotmd init Create starter config + docs directory
|
|
225
265
|
dotmd completions <shell> Output shell completion script (bash, zsh)
|
|
226
266
|
```
|
|
227
267
|
|
|
268
|
+
Run `dotmd help all` for the always-current version of this list, `dotmd help statuses` for the status vocabulary, and `dotmd <cmd> --help` for per-command details.
|
|
269
|
+
|
|
228
270
|
### Global Flags
|
|
229
271
|
|
|
230
272
|
```
|
|
231
273
|
--config <path> Explicit config file path
|
|
232
274
|
--dry-run, -n Preview changes without writing anything
|
|
233
275
|
--root <name> Filter to a specific docs root
|
|
234
|
-
--type <t1,t2> Filter by document type (plan, doc,
|
|
276
|
+
--type <t1,t2> Filter by document type (plan, doc, prompt, or custom)
|
|
235
277
|
--verbose Show resolved config details
|
|
236
278
|
--help, -h Show help (per-command with: dotmd <cmd> --help)
|
|
237
279
|
--version, -v Show version
|
|
@@ -246,9 +288,12 @@ dotmd query --stale --sort updated --all
|
|
|
246
288
|
dotmd query --surface backend --checklist-open
|
|
247
289
|
dotmd query --status active --summarize # AI summaries
|
|
248
290
|
dotmd query --status active --summarize --summarize-limit 3
|
|
291
|
+
dotmd query --keyword "retries" --body # scan document bodies too
|
|
249
292
|
```
|
|
250
293
|
|
|
251
|
-
Flags: `--type`, `--status`, `--keyword`, `--module`, `--surface`, `--domain`, `--owner`, `--updated-since`, `--stale`, `--has-next-step`, `--has-blockers`, `--checklist-open`, `--sort`, `--limit`, `--all`, `--git`, `--json`, `--summarize`, `--summarize-limit`, `--model`.
|
|
294
|
+
Flags: `--type`, `--status`, `--keyword`, `--body`, `--module`, `--surface`, `--domain`, `--owner`, `--updated-since`, `--stale`, `--has-next-step`, `--has-blockers`, `--checklist-open`, `--sort`, `--limit`, `--all`, `--git`, `--json`, `--summarize`, `--summarize-limit`, `--model`.
|
|
295
|
+
|
|
296
|
+
`dotmd grep <term>` is the "which doc discussed X?" shorthand — an alias for `dotmd query --keyword <term> --body --all` that prints doc cards plus line-numbered excerpts per body hit. Bodies are read lazily (frontmatter filters run first), and it composes with the usual query flags.
|
|
252
297
|
|
|
253
298
|
### Create Documents
|
|
254
299
|
|
|
@@ -311,9 +356,19 @@ dotmd new prompt from-file @/tmp/draft.md
|
|
|
311
356
|
|
|
312
357
|
Manage them with the `prompts` command family:
|
|
313
358
|
|
|
359
|
+
Consume one with `dotmd use` — it atomically prints the body and archives the prompt so it can't be double-consumed:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
dotmd use # consume the oldest pending prompt
|
|
363
|
+
dotmd use <file-or-slug> # consume a specific prompt
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Admin verbs live under the `prompts` namespace:
|
|
367
|
+
|
|
314
368
|
```bash
|
|
315
369
|
dotmd prompts # list pending prompts (default)
|
|
316
370
|
dotmd prompts list --all # all statuses
|
|
371
|
+
dotmd prompts show <file> # read-only peek: print the body WITHOUT consuming
|
|
317
372
|
dotmd prompts next # print body of oldest pending + auto-archive (one-shot)
|
|
318
373
|
dotmd prompts use <file> # print body of a specific prompt + auto-archive
|
|
319
374
|
dotmd prompts hold <file> # park a prompt (status → held) under prompts/held/:
|
|
@@ -328,6 +383,31 @@ dotmd prompts new <name> [body] # alias for `dotmd new prompt`
|
|
|
328
383
|
|
|
329
384
|
Statuses: `pending` (drafted, awaiting a session), `held` (saved but parked under `prompts/held/` — visible in `prompts list`, hidden from `hud`/`briefing`, skipped by `prompts next`), `archived` (consumed or filed away). `shelved` is a legacy spelling accepted for older files; `claimed` is reserved for a future "in-flight" state but is currently a synonym for archived in practice.
|
|
330
385
|
|
|
386
|
+
### Baton: resume prompts & session handoff
|
|
387
|
+
|
|
388
|
+
`dotmd baton` is the "save a resume prompt" verb — the way one session hands work to the next without pasting resume text into chat. Write a short draft (the next concrete decision plus any gotchas, not a recap), then:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
dotmd baton @/tmp/draft.md # plan mode: a plan is in-session
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
In plan mode, baton does the whole closeout in one call:
|
|
395
|
+
|
|
396
|
+
1. Saves a resume prompt named `resume-<plan-slug>` (collision-safe: `-2`, `-3`, …) under `docs/prompts/` with `status: pending`.
|
|
397
|
+
2. Releases the plan: one status flip, `in-session` → `active` by default (`--status paused|awaiting|partial|blocked` to override, `--note "why"` to record the reason in `## Version History`).
|
|
398
|
+
3. Prints the exact `git commit` command for the plan's frontmatter change — the prompt stays out of the pathspec, because saved prompts are session-local.
|
|
399
|
+
|
|
400
|
+
Baton resolves *your* plan via the command journal (or takes it explicitly: `dotmd baton <plan-file> @draft`), falling back to the only in-session plan.
|
|
401
|
+
|
|
402
|
+
No plan involved? Slug mode saves the prompt and touches nothing else:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
dotmd baton checkout-fixes @/tmp/draft.md # saves resume-checkout-fixes; no status changes
|
|
406
|
+
cat /tmp/draft.md | dotmd baton # body from stdin
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Either way, the next session's `dotmd hud` surfaces the pending prompt, and `dotmd use` consumes it.
|
|
410
|
+
|
|
331
411
|
### Command Journal (opt-in)
|
|
332
412
|
|
|
333
413
|
dotmd's primary user is an agent. Every CLI invocation can be journaled
|
|
@@ -403,8 +483,8 @@ Shows: status counts, staleness, errors/warnings, freshness (today/week/month),
|
|
|
403
483
|
### Doctor
|
|
404
484
|
|
|
405
485
|
```bash
|
|
406
|
-
dotmd doctor # fix refs → lint → sync git dates → regen index
|
|
407
|
-
dotmd doctor --
|
|
486
|
+
dotmd doctor # preview: fix refs → lint → sync git dates → regen index
|
|
487
|
+
dotmd doctor --apply # actually write the fixes (previews by default since 0.37.0)
|
|
408
488
|
dotmd doctor --statuses # detect overloaded status buckets (read-only)
|
|
409
489
|
dotmd doctor --statuses --json # machine-readable suggestions
|
|
410
490
|
```
|
|
@@ -607,6 +687,15 @@ dotmd archive docs/plans/my-plan.md --closeout-template # also inject ## Close
|
|
|
607
687
|
`in-session` is a status like any other — `dotmd set <status> <file>` writes it
|
|
608
688
|
to the file's frontmatter and does nothing else.
|
|
609
689
|
|
|
690
|
+
Add `--note "why"` to any `set` or `archive` to append the reason to the doc's
|
|
691
|
+
`## Version History` section in the same call (creates the section if missing) —
|
|
692
|
+
it saves the status-change + worklog-edit round-trip. `set partial` without a
|
|
693
|
+
note or successor link prints a reminder.
|
|
694
|
+
|
|
695
|
+
To stop mid-work and hand off to a future session, use `dotmd baton` (see
|
|
696
|
+
[Baton](#baton-resume-prompts--session-handoff)) — it saves the resume prompt
|
|
697
|
+
and releases the plan in one verb.
|
|
698
|
+
|
|
610
699
|
**Recommended Claude Code hook** — add to `~/.claude/settings.json`
|
|
611
700
|
(or your project's `.claude/settings.json`):
|
|
612
701
|
|
package/bin/dotmd.mjs
CHANGED
|
@@ -36,16 +36,19 @@ const FLAG_SPECS = {
|
|
|
36
36
|
context: { flags: new Set(['--json', '--compact', '--summarize', '--model']), values: new Set(['--model']) },
|
|
37
37
|
'agent-context': { flags: new Set(['--json']), values: new Set() },
|
|
38
38
|
hud: { flags: new Set(['--json', '--subagent']), values: new Set() },
|
|
39
|
+
// '-' is the stdin marker (a positional, not a flag) — listed so validation lets it through.
|
|
40
|
+
baton: { flags: new Set(['--status', '--note', '--body', '--message', '--dry-run', '-n', '-']), values: new Set(['--status', '--note', '--body', '--message']) },
|
|
39
41
|
guard: { flags: new Set(), values: new Set() },
|
|
40
42
|
misuse: { flags: new Set(['--json', '--tail', '--by-rule', '--repo']), values: new Set(['--tail', '--repo']) },
|
|
41
43
|
update: { flags: new Set(['--check', '--cli-only', '--plugin-only']), values: new Set() },
|
|
42
44
|
check: { flags: new Set(['--fix', '--errors-only', '--no-collapse', '--json', '--verbose']), values: new Set() },
|
|
43
45
|
doctor: { flags: new Set(['--apply', '--yes', '--dry-run', '-n', '--statuses', '--migrate-template', '--migrate-prompts', '--frontmatter-fix', '--project', '--json', '--include-archived']), values: new Set() },
|
|
44
46
|
runlist: { flags: new Set(['--json', '--full', '--no-index', '--show-files']), values: new Set(), subcommands: new Set(['next']) },
|
|
47
|
+
runlists: { flags: new Set(['--json', '--limit', '--sort']), values: new Set(['--limit', '--sort']) },
|
|
45
48
|
prompts: {
|
|
46
49
|
flags: new Set(['--json', '--status', '--include-archived', '--sort', '--limit', '--all', '--no-index', '--show-files', '--body', '--message', '--title']),
|
|
47
50
|
values: new Set(['--status', '--sort', '--limit', '--body', '--message', '--title']),
|
|
48
|
-
subcommands: new Set(['list', 'next', 'use', 'resume', 'archive', 'new', 'hold', 'unhold', 'shelve', 'unshelve', 'status']),
|
|
51
|
+
subcommands: new Set(['list', 'next', 'use', 'resume', 'show', 'peek', 'archive', 'new', 'hold', 'unhold', 'shelve', 'unshelve', 'status']),
|
|
49
52
|
},
|
|
50
53
|
};
|
|
51
54
|
|
|
@@ -133,6 +136,7 @@ Common commands:
|
|
|
133
136
|
set <status> [file] Transition status (start work, finish, archive — all via target status)
|
|
134
137
|
new <type> <name> Create plan/doc/prompt (pipe stdin or @path for body)
|
|
135
138
|
use [<file-or-slug>] Open a doc by type: prompt → consume, plan → start, doc → read
|
|
139
|
+
baton [<plan>|<slug>] <@draft|-> Save a resume prompt (+ release the plan, if one is in-session)
|
|
136
140
|
(no file: consume oldest pending prompt)
|
|
137
141
|
archive <file> Close out a plan (status → archived, move, update refs)
|
|
138
142
|
|
|
@@ -203,7 +207,8 @@ View & Query:
|
|
|
203
207
|
grep <term> Keyword search incl. document bodies (query --keyword --body --all)
|
|
204
208
|
plans Live plans (excludes archived; --include-archived for all)
|
|
205
209
|
use [<file-or-slug>] Open a doc by type: prompt → consume, plan → start, doc → read
|
|
206
|
-
|
|
210
|
+
baton [<plan>|<slug>] <@draft|-> Save a resume prompt; releases the plan + prints the commit when one is in-session
|
|
211
|
+
prompts [list|show|archive|new|hold] Prompt admin (list / peek / archive / save / hold). Use \`dotmd use\` to consume.
|
|
207
212
|
stale Stale docs (preset)
|
|
208
213
|
actionable Docs with next steps (preset)
|
|
209
214
|
|
|
@@ -231,6 +236,7 @@ Lifecycle:
|
|
|
231
236
|
use <file> Open a plan (mark in-session + print it) or consume a prompt
|
|
232
237
|
set <status> <file> Change a document's status (frontmatter write; archive also moves the file)
|
|
233
238
|
runlist <hub> [next] Show or walk an ordered group of plans (see \`dotmd help runlist\`)
|
|
239
|
+
runlists List coordination-hub runlists (the Runlists dashboard)
|
|
234
240
|
status <file> <status> Transition document status (deprecated; prefer \`set\`)
|
|
235
241
|
archive <file> Archive (status + move + update refs)
|
|
236
242
|
bulk archive <f1> <f2> ... Archive multiple files at once
|
|
@@ -259,7 +265,7 @@ Setup:
|
|
|
259
265
|
Global Options:
|
|
260
266
|
--config <path> Explicit config file path
|
|
261
267
|
--root <name> Filter to a specific docs root
|
|
262
|
-
--type <t1,t2> Filter by document type (plan, doc,
|
|
268
|
+
--type <t1,t2> Filter by document type (plan, doc, prompt)
|
|
263
269
|
--dry-run, -n Preview changes without writing anything
|
|
264
270
|
--verbose Show config details and doc count
|
|
265
271
|
--help, -h Show help (per-command: dotmd <cmd> --help)
|
|
@@ -406,7 +412,7 @@ Examples:
|
|
|
406
412
|
query: `dotmd query — filtered document search
|
|
407
413
|
|
|
408
414
|
Filters:
|
|
409
|
-
--type <t1,t2> Filter by type (plan, doc,
|
|
415
|
+
--type <t1,t2> Filter by type (plan, doc, prompt)
|
|
410
416
|
--status <s1,s2> Filter by status (comma-separated)
|
|
411
417
|
--keyword <term> Search title, summary, state, path
|
|
412
418
|
--body Extend --keyword into document bodies (lazy scan, shows matching-line excerpts)
|
|
@@ -603,7 +609,8 @@ Recommended SessionStart hook (in ~/.claude/settings.json):
|
|
|
603
609
|
"SessionStart": [{ "hooks": [{ "type": "command", "command": "dotmd hud", "timeout": 5 }] }]
|
|
604
610
|
|
|
605
611
|
Options:
|
|
606
|
-
--json Output as JSON ({ owned,
|
|
612
|
+
--json Output as JSON ({ owned, prompts, errors, previousSelf,
|
|
613
|
+
fleet, recentRejections, misuseRecap, drift })`,
|
|
607
614
|
|
|
608
615
|
briefing: `dotmd briefing — compact summary for session start
|
|
609
616
|
|
|
@@ -882,7 +889,7 @@ Options:
|
|
|
882
889
|
--format <md|html|json> Output format (default: md)
|
|
883
890
|
--output <path> Write to file/directory (default: stdout for md/json)
|
|
884
891
|
--status <s1,s2> Filter by status
|
|
885
|
-
--type <t1,t2> Filter by type (plan, doc,
|
|
892
|
+
--type <t1,t2> Filter by type (plan, doc, prompt)
|
|
886
893
|
--module <name> Filter by module
|
|
887
894
|
--root <name> Filter by root
|
|
888
895
|
--dry-run, -n Preview without writing`,
|
|
@@ -993,6 +1000,8 @@ Subcommands:
|
|
|
993
1000
|
targets the named prompt instead of picking oldest)
|
|
994
1001
|
resume <file-or-slug> Alias for \`use\` — same behavior, easier name
|
|
995
1002
|
when continuing a session
|
|
1003
|
+
show <file-or-slug> Read-only peek: print the body WITHOUT consuming
|
|
1004
|
+
(triage). \`peek\` is an alias.
|
|
996
1005
|
archive <file-or-slug> Archive a prompt without printing its body
|
|
997
1006
|
hold <file-or-slug> Park a prompt (status → held) under prompts/held/:
|
|
998
1007
|
kept in list, hidden from hud/briefing pending
|
|
@@ -1023,10 +1032,50 @@ Examples:
|
|
|
1023
1032
|
claude "$(dotmd prompts resume resume-foo)" # \`resume\` is an alias for \`use\`
|
|
1024
1033
|
dotmd prompt list # singular alias for \`dotmd prompts list\`
|
|
1025
1034
|
|
|
1035
|
+
dotmd prompts show resume-foo # peek without consuming (triage)
|
|
1026
1036
|
dotmd prompts next --dry-run # preview without consuming
|
|
1027
1037
|
dotmd prompts archive old-thing
|
|
1028
1038
|
dotmd prompts new my-prompt "Body text here"`,
|
|
1029
1039
|
|
|
1040
|
+
baton: `dotmd baton — save a resume prompt for whatever you're doing (and release the plan, if there is one)
|
|
1041
|
+
|
|
1042
|
+
The "save a resume prompt" verb. Works mid-anything:
|
|
1043
|
+
|
|
1044
|
+
Plan mode (a plan is in-session, or you pass one):
|
|
1045
|
+
1. Saves a resume prompt named resume-<plan-slug> (collision-safe: -2, -3, …).
|
|
1046
|
+
The prompt is session-local — the next session's hud surfaces it; never
|
|
1047
|
+
paste resume text into chat.
|
|
1048
|
+
2. Releases the plan: one status flip, in-session → active by default
|
|
1049
|
+
(--status to override, --note to record why in ## Version History).
|
|
1050
|
+
3. Prints the exact \`git commit\` for the plan's frontmatter change — the
|
|
1051
|
+
prompt stays OUT of the pathspec (it's session-local, often gitignored).
|
|
1052
|
+
Which plan? Pass it explicitly, or baton resolves the one THIS session marked
|
|
1053
|
+
in-session (via the journal), falling back to the only in-session plan.
|
|
1054
|
+
|
|
1055
|
+
Slug mode (no plan involved — "save a resume prompt for this"):
|
|
1056
|
+
dotmd baton <slug> @/tmp/draft.md → saves resume-<slug>, touches NOTHING
|
|
1057
|
+
else: no status changes, no commit, no plan required. Reference any relevant
|
|
1058
|
+
plans/docs inside the draft body.
|
|
1059
|
+
|
|
1060
|
+
Usage:
|
|
1061
|
+
dotmd baton [<plan-file> | <slug>] [@draft.md | - | --message "..."]
|
|
1062
|
+
|
|
1063
|
+
Options:
|
|
1064
|
+
--status <s> Target status for the plan (default: active; plan mode only)
|
|
1065
|
+
--note "why" Append the reason to ## Version History (plan mode only)
|
|
1066
|
+
--message / --body Inline body (one-liners; prefer @path or stdin)
|
|
1067
|
+
--dry-run, -n Preview without writing
|
|
1068
|
+
|
|
1069
|
+
Examples:
|
|
1070
|
+
dotmd baton @/tmp/draft.md # owned plan, body from file
|
|
1071
|
+
dotmd baton checkout-fixes @/tmp/draft.md # no plan: just save resume-checkout-fixes
|
|
1072
|
+
cat /tmp/draft.md | dotmd baton # body from stdin
|
|
1073
|
+
dotmd baton docs/plans/auth.md @/tmp/draft.md # explicit plan
|
|
1074
|
+
dotmd baton --status paused --note "blocked on review" @/tmp/d.md
|
|
1075
|
+
|
|
1076
|
+
Write the draft FIRST (10–20 lines): the next concrete decision plus any
|
|
1077
|
+
gotchas — not a recap of the plan body.`,
|
|
1078
|
+
|
|
1030
1079
|
stale: `dotmd stale — list stale documents
|
|
1031
1080
|
|
|
1032
1081
|
Shows docs that haven't been updated within their staleness threshold.
|
|
@@ -1165,7 +1214,33 @@ Common shape:
|
|
|
1165
1214
|
---
|
|
1166
1215
|
|
|
1167
1216
|
Child plans should set \`parent_plan:\` back at the hub — \`dotmd check\` warns
|
|
1168
|
-
when they don't
|
|
1217
|
+
when they don't.
|
|
1218
|
+
|
|
1219
|
+
In \`dotmd plans\`, a hub is tagged \`[RUNLIST]\` (not \`[ACTIVE]\`) and its
|
|
1220
|
+
children fold underneath it — progress (\`done/total\`) and the next pickup
|
|
1221
|
+
\`→\` show on the hub row, so a sprint reads as one runlist instead of N loose
|
|
1222
|
+
plans. Children whose hub is filtered out of the view (e.g. \`--status active\`
|
|
1223
|
+
when the hub is \`planned\`) still render on their own.
|
|
1224
|
+
|
|
1225
|
+
Larger, prose-first "coordination" runlists (a domain map pointing at many
|
|
1226
|
+
plans, marked \`execution_mode: coordination\` or named \`*-runlist\`) aren't
|
|
1227
|
+
folded — they're lifted into a separate \`Runlists\` section in \`dotmd plans\`
|
|
1228
|
+
and out of the active count. \`dotmd runlists\` shows that dashboard on its own.`,
|
|
1229
|
+
|
|
1230
|
+
runlists: `dotmd runlists — the coordination-hub dashboard
|
|
1231
|
+
|
|
1232
|
+
Lists every *coordination runlist*: a prose-first plan that sits above a
|
|
1233
|
+
cluster of others (a domain map), detected by \`execution_mode: coordination\`
|
|
1234
|
+
or a \`*-runlist\` / \`runlist\` slug. Each row shows the hub, its age, the rough
|
|
1235
|
+
size of its \`related_plans:\` cluster, and a one-line descriptor.
|
|
1236
|
+
|
|
1237
|
+
This is the standalone form of the \`Runlists\` section that \`dotmd plans\`
|
|
1238
|
+
pins beneath the leaf-plan triage list.
|
|
1239
|
+
|
|
1240
|
+
dotmd runlists All runlists (a small bounded set), most stale first.
|
|
1241
|
+
dotmd runlists --sort recent Order by recency instead (age|recent|related|title|status).
|
|
1242
|
+
dotmd runlists --limit N Cap the list at N.
|
|
1243
|
+
dotmd runlists --json Structured rows (path, status, childCount, …).`,
|
|
1169
1244
|
|
|
1170
1245
|
'bulk-tag': `dotmd bulk-tag [files...] — fill in type/status frontmatter on pre-existing markdown
|
|
1171
1246
|
|
|
@@ -1325,6 +1400,16 @@ async function main() {
|
|
|
1325
1400
|
runQuery(index, [...defaults, ...extras], config, { preset: 'plans' });
|
|
1326
1401
|
return;
|
|
1327
1402
|
}
|
|
1403
|
+
// `dotmd runlists` (plural) — the coordination-hub dashboard (the `Runlists`
|
|
1404
|
+
// section of `dotmd plans`, standalone). Distinct from `dotmd runlist <hub>`
|
|
1405
|
+
// (singular), which walks one hub's children.
|
|
1406
|
+
if (command === 'runlists') {
|
|
1407
|
+
const { buildIndex } = await import('../src/index.mjs');
|
|
1408
|
+
const { runRunlists } = await import('../src/query.mjs');
|
|
1409
|
+
const index = buildIndex(config);
|
|
1410
|
+
runRunlists(index, restArgs, config);
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1328
1413
|
if (command === 'prompts') {
|
|
1329
1414
|
const { runPrompts } = await import('../src/prompts.mjs');
|
|
1330
1415
|
await runPrompts(restArgs, config, { dryRun, verbose });
|
|
@@ -1339,6 +1424,14 @@ async function main() {
|
|
|
1339
1424
|
await runUse(restArgs, config, { dryRun });
|
|
1340
1425
|
return;
|
|
1341
1426
|
}
|
|
1427
|
+
// `dotmd baton [plan] <@draft|->` — the one-command handoff: save the resume
|
|
1428
|
+
// prompt, release the plan (one status flip), print the exact commit. See
|
|
1429
|
+
// src/baton.mjs for why this is a single verb and not a skill choreography.
|
|
1430
|
+
if (command === 'baton') {
|
|
1431
|
+
const { runBaton } = await import('../src/baton.mjs');
|
|
1432
|
+
await runBaton(restArgs, config, { dryRun });
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1342
1435
|
// `dotmd next` is a top-level alias for `dotmd use` with no arg — consume
|
|
1343
1436
|
// the oldest pending prompt. Wired separately so agents who reach for the
|
|
1344
1437
|
// literal verb "next" don't bounce off an Unknown-command. Any positional
|
|
@@ -1667,8 +1760,16 @@ async function main() {
|
|
|
1667
1760
|
const docs = index.docs.filter(d => d.type === 'doc');
|
|
1668
1761
|
const research = index.docs.filter(d => d.type === 'research');
|
|
1669
1762
|
const stale = index.docs.filter(d => d.isStale && !config.lifecycle.skipStaleFor.has(d.status)).length;
|
|
1763
|
+
// Coordination hubs are runlists, not actionable plans — split them out of
|
|
1764
|
+
// inSession/active into their own `runlists` array so the JSON mirrors the
|
|
1765
|
+
// rendered briefing. Empty on repos with no coordination hubs.
|
|
1766
|
+
const { buildCoordinationIndex } = await import('../src/runlist.mjs');
|
|
1767
|
+
const coordination = buildCoordinationIndex(index, config);
|
|
1768
|
+
const isHub = (d) => coordination.has(d.path);
|
|
1769
|
+
const closedStatuses = new Set([...config.lifecycle.archiveStatuses, ...config.lifecycle.terminalStatuses]);
|
|
1770
|
+
const isLiveHub = (d) => isHub(d) && !closedStatuses.has(d.status) && !isArchivedPath(d.path, config);
|
|
1670
1771
|
process.stdout.write(JSON.stringify({
|
|
1671
|
-
plans: { total: plans.length, inSession: plans.filter(d => d.status === 'in-session').map(d => ({ path: d.path, title: d.title, nextStep: d.nextStep })), active: plans.filter(d => d.status === 'active').map(d => ({ path: d.path, title: d.title, nextStep: d.nextStep })) },
|
|
1772
|
+
plans: { total: plans.length, inSession: plans.filter(d => d.status === 'in-session' && !isHub(d)).map(d => ({ path: d.path, title: d.title, nextStep: d.nextStep })), active: plans.filter(d => d.status === 'active' && !isHub(d)).map(d => ({ path: d.path, title: d.title, nextStep: d.nextStep })), runlists: plans.filter(isLiveHub).map(d => ({ path: d.path, title: d.title, status: d.status, childCount: coordination.get(d.path)?.childCount ?? 0 })) },
|
|
1672
1773
|
docs: { total: docs.length, active: docs.filter(d => !config.lifecycle.terminalStatuses.has(d.status)).length },
|
|
1673
1774
|
research: { total: research.length, active: research.filter(d => d.status === 'active').length },
|
|
1674
1775
|
stale, errorCount: index.errors.length, warningCount: index.warnings.length,
|
package/dotmd.config.example.mjs
CHANGED
|
@@ -193,7 +193,11 @@ export const presets = {
|
|
|
193
193
|
// Properties:
|
|
194
194
|
// description: string — shown in `dotmd new --list-types`
|
|
195
195
|
// defaultStatus: string — initial status if `--status` not passed
|
|
196
|
-
//
|
|
196
|
+
// acceptsBody: boolean — allow body input (inline / --body / @file / piped stdin).
|
|
197
|
+
// REQUIRED if you want `cat draft.md | dotmd new <type> <slug>` (or @path,
|
|
198
|
+
// --body, heredoc) to work. Your `body` fn must also interpolate the input,
|
|
199
|
+
// e.g. `${ctx?.bodyInput?.trim() ?? ''}`. See the body-acceptance guard below.
|
|
200
|
+
// requiresBody: boolean — error if no body input (implies acceptsBody; see `prompt` builtin)
|
|
197
201
|
// targetRoot: string — name (basename or suffix) of the root this type lives in.
|
|
198
202
|
// In flat-array `root` configs (e.g. ['docs/plans', 'docs/prompts']),
|
|
199
203
|
// the new doc lands in the matching root. Falls back to `config.docsRoot`
|
|
@@ -203,7 +207,17 @@ export const presets = {
|
|
|
203
207
|
// under `docsRoot='docs'`, `dir` puts files in `docs/plans/` and `docs/prompts/`;
|
|
204
208
|
// under flat-array roots, `targetRoot` routes directly to the type-specific root.
|
|
205
209
|
// frontmatter: (status, isoTime, ctx) => string
|
|
206
|
-
// body: (title, ctx) => string
|
|
210
|
+
// body: (title, ctx) => string — slot user-supplied body via `ctx.bodyInput`
|
|
211
|
+
//
|
|
212
|
+
// Body-acceptance guard (the #1 custom-template gotcha):
|
|
213
|
+
// When you override a builtin and supply your OWN `body` fn that NEVER references
|
|
214
|
+
// `bodyInput`, dotmd assumes the fn would silently discard piped input — so it strips
|
|
215
|
+
// the inherited `acceptsBody`/`requiresBody` and rejects body input with a fail-fast
|
|
216
|
+
// error. Two ways to keep piped/@path/heredoc bodies working in a custom template:
|
|
217
|
+
// 1. interpolate `${ctx?.bodyInput?.trim() ?? ''}` somewhere in your `body` fn, OR
|
|
218
|
+
// 2. set `acceptsBody: true` explicitly (do BOTH if you want the input to actually land).
|
|
219
|
+
// A `body: (t) =>` that ignores `ctx` is the classic trap — it scaffolds fine but
|
|
220
|
+
// `dotmd new <type> <slug> < draft.md` errors until you wire in `bodyInput`.
|
|
207
221
|
//
|
|
208
222
|
// Custom type example — adds a `spike` type that lives in the `spikes` root (or
|
|
209
223
|
// under `docs/spikes/` in single-root layouts):
|
|
@@ -218,8 +232,11 @@ export const presets = {
|
|
|
218
232
|
// },
|
|
219
233
|
//
|
|
220
234
|
// // Override a builtin (e.g. project-specific prompt frontmatter shape).
|
|
221
|
-
// //
|
|
222
|
-
// // `defaultStatus`, `requiresBody`,
|
|
235
|
+
// // Overrides shallow-merge OVER the builtin: any property you omit is inherited
|
|
236
|
+
// // (`targetRoot`, `dir`, `defaultStatus`, `requiresBody`, …), and anything you declare
|
|
237
|
+
// // wins. EXCEPTION: if you supply your own `body` fn that doesn't reference `bodyInput`,
|
|
238
|
+
// // the inherited `acceptsBody`/`requiresBody` are dropped (see the guard above) — so
|
|
239
|
+
// // re-declare `acceptsBody: true` and wire in `ctx.bodyInput` if you want piped bodies.
|
|
223
240
|
// // prompt: {
|
|
224
241
|
// // description: 'Project resume prompt',
|
|
225
242
|
// // defaultStatus: 'pending',
|
package/package.json
CHANGED