dotmd-cli 0.15.0 → 0.16.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 +62 -0
- package/bin/dotmd.mjs +71 -4
- package/dotmd.config.example.mjs +14 -7
- package/package.json +1 -1
- package/src/config-edit.mjs +603 -0
- package/src/doctor.mjs +148 -0
- package/src/migrate.mjs +32 -3
- package/src/statuses.mjs +736 -0
package/README.md
CHANGED
|
@@ -244,8 +244,30 @@ Shows: status counts, staleness, errors/warnings, freshness (today/week/month),
|
|
|
244
244
|
```bash
|
|
245
245
|
dotmd doctor # fix refs → lint → sync git dates → regen index
|
|
246
246
|
dotmd doctor --dry-run # preview all changes
|
|
247
|
+
dotmd doctor --statuses # detect overloaded status buckets (read-only)
|
|
248
|
+
dotmd doctor --statuses --json # machine-readable suggestions
|
|
247
249
|
```
|
|
248
250
|
|
|
251
|
+
`--statuses` is a read-only diagnostic. It scans each status with at least
|
|
252
|
+
10 plans and groups their `current_state` / `next_step` text against cue
|
|
253
|
+
keywords for `partial`, `paused`, `awaiting`, `queued-after`, and `blocked`.
|
|
254
|
+
When a single bucket lands plans in two or more cue groups (each above 15%
|
|
255
|
+
of the bucket), it prints a split suggestion:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
47 plan/backlog plans cluster across 4 patterns — consider splitting:
|
|
259
|
+
~22 → partial (cues: "shipped", "landed", "tail", "deferred")
|
|
260
|
+
~15 → paused (cues: "paused", "on hold", "set aside")
|
|
261
|
+
~ 6 → queued-after (cues: "after", "once", "depends on", "waiting on <plan>")
|
|
262
|
+
~ 4 → (kept in backlog — no clear pattern match)
|
|
263
|
+
|
|
264
|
+
Heuristic — verify before migrating.
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The heuristic is intentionally conservative: small buckets are skipped, plans
|
|
268
|
+
that match no cues stay in the original bucket, and the output is always a
|
|
269
|
+
suggestion — never a verdict.
|
|
270
|
+
|
|
249
271
|
### Graph
|
|
250
272
|
|
|
251
273
|
```bash
|
|
@@ -401,6 +423,34 @@ dotmd lint # report issues
|
|
|
401
423
|
dotmd lint --fix # fix all issues
|
|
402
424
|
```
|
|
403
425
|
|
|
426
|
+
### Manage Statuses
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
dotmd statuses # table view, all types
|
|
430
|
+
dotmd statuses --type plan # one type
|
|
431
|
+
dotmd statuses --json # machine-readable
|
|
432
|
+
|
|
433
|
+
dotmd statuses add paused --type plan --like blocked --quiet # clone blocked, then quiet
|
|
434
|
+
dotmd statuses set archived --type plan --no-quiet # tweak a flag
|
|
435
|
+
dotmd statuses remove obsolete --type plan # refuses if any docs use it
|
|
436
|
+
|
|
437
|
+
dotmd statuses migrate plan # array-form → rich-form
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
`--like <existing>` is the affordance for "kinda like X but…" — clones every
|
|
441
|
+
flag from another status, then user flags override. Write commands print a flag
|
|
442
|
+
diff and prompt for confirmation; pass `--yes` to skip the prompt or
|
|
443
|
+
`--dry-run` to preview without writing. Edits are atomic: the rewrite lands in
|
|
444
|
+
a sibling temp file, is validated by re-importing it and running
|
|
445
|
+
`resolveConfig`, then renamed into place — a syntax error or new warning
|
|
446
|
+
leaves the original untouched.
|
|
447
|
+
|
|
448
|
+
**Lifecycle-override gotcha.** If your config has both rich-form `types` and an
|
|
449
|
+
explicit `export const lifecycle = {...}`, the explicit lifecycle silently
|
|
450
|
+
overrides per-status flags at runtime. `dotmd statuses` write commands refuse
|
|
451
|
+
to write into that state and recommend deleting the explicit `lifecycle` block;
|
|
452
|
+
pass `--ignore-lifecycle-override` to write anyway.
|
|
453
|
+
|
|
404
454
|
### Rename
|
|
405
455
|
|
|
406
456
|
```bash
|
|
@@ -412,8 +462,20 @@ dotmd rename old-name.md new-name # renames + updates refs
|
|
|
412
462
|
```bash
|
|
413
463
|
dotmd migrate status research scoping # rename a status (e.g. for the 0.15 default-vocab change)
|
|
414
464
|
dotmd migrate module auth identity # rename a module
|
|
465
|
+
|
|
466
|
+
# Per-file form: split one overloaded status into several distinct ones.
|
|
467
|
+
# Only the listed files are rewritten; every other doc with the old value is left alone.
|
|
468
|
+
dotmd migrate status backlog paused docs/plans/foo.md docs/plans/bar.md
|
|
469
|
+
dotmd migrate status backlog partial docs/plans/payments-future.md # one at a time also works
|
|
415
470
|
```
|
|
416
471
|
|
|
472
|
+
With no file args, `migrate` rewrites every doc whose field matches
|
|
473
|
+
`<old-value>` (whole-bucket rename). Pass file args to scope the
|
|
474
|
+
rewrite — useful when one status has been doing several jobs and you
|
|
475
|
+
want to split it across the new vocabulary. File args match the same
|
|
476
|
+
way as `bulk archive`: exact path first, then substring fallback
|
|
477
|
+
against full path or basename.
|
|
478
|
+
|
|
417
479
|
### Preset Aliases
|
|
418
480
|
|
|
419
481
|
Built-in presets: `plans`, `stale`, `actionable`. Add your own in config:
|
package/bin/dotmd.mjs
CHANGED
|
@@ -49,7 +49,7 @@ Lifecycle:
|
|
|
49
49
|
touch <file> Bump updated date
|
|
50
50
|
touch --git Bulk-sync dates from git history
|
|
51
51
|
rename <old> <new> Rename doc and update all references
|
|
52
|
-
migrate <field> <old> <new>
|
|
52
|
+
migrate <field> <old> <new> [f...]Batch update a frontmatter field value (optional file filter)
|
|
53
53
|
|
|
54
54
|
Create & Export:
|
|
55
55
|
new <name> [--template <t>] Create doc from template (plan, adr, rfc, audit, design)
|
|
@@ -59,6 +59,7 @@ Create & Export:
|
|
|
59
59
|
|
|
60
60
|
Setup:
|
|
61
61
|
init Create starter config + docs directory
|
|
62
|
+
statuses [list|add|set|remove|migrate] Manage per-project status taxonomy
|
|
62
63
|
watch [command] Re-run a command on file changes
|
|
63
64
|
completions <shell> Shell completion script (bash, zsh)
|
|
64
65
|
|
|
@@ -236,6 +237,16 @@ Options:
|
|
|
236
237
|
Runs in sequence: fix broken references, lint --fix, sync dates from
|
|
237
238
|
git, regenerate index, then show remaining issues.
|
|
238
239
|
|
|
240
|
+
Modes:
|
|
241
|
+
(default) Auto-fix pass (writes by default; honors --dry-run)
|
|
242
|
+
--statuses Read-only diagnostic: detect overloaded status
|
|
243
|
+
buckets where one status holds plans pursuing
|
|
244
|
+
multiple distinct unstuck-actions. Suggests how
|
|
245
|
+
a bucket might split (e.g. backlog → partial /
|
|
246
|
+
paused / queued-after). Heuristic only — verify
|
|
247
|
+
before migrating.
|
|
248
|
+
--statuses --json Machine-readable suggestion shape for tooling.
|
|
249
|
+
|
|
239
250
|
Use --dry-run (-n) to preview all changes without writing anything.`,
|
|
240
251
|
|
|
241
252
|
'fix-refs': `dotmd fix-refs — auto-fix broken reference paths
|
|
@@ -359,14 +370,22 @@ in other docs that point to the old filename.
|
|
|
359
370
|
Body markdown links are warned about but not auto-fixed.
|
|
360
371
|
Use --dry-run (-n) to preview changes without writing anything.`,
|
|
361
372
|
|
|
362
|
-
migrate: `dotmd migrate <field> <old-value> <new-value> — batch update a frontmatter field
|
|
373
|
+
migrate: `dotmd migrate <field> <old-value> <new-value> [files...] — batch update a frontmatter field
|
|
363
374
|
|
|
364
375
|
Finds all docs where the given field equals old-value and updates it
|
|
365
|
-
to new-value.
|
|
376
|
+
to new-value. With no file args, every matching doc in the project is
|
|
377
|
+
rewritten (whole-bucket rename).
|
|
378
|
+
|
|
379
|
+
Pass one or more file args to scope the rewrite — only those files
|
|
380
|
+
are considered. This is how you split one overloaded status into
|
|
381
|
+
several distinct ones (e.g. moving some \`backlog\` plans to
|
|
382
|
+
\`paused\` and others to \`partial\`). File args use the same matching
|
|
383
|
+
as \`bulk archive\`: exact path, then substring fallback.
|
|
366
384
|
|
|
367
385
|
Examples:
|
|
368
386
|
dotmd migrate status research scoping
|
|
369
387
|
dotmd migrate module auth identity
|
|
388
|
+
dotmd migrate status backlog paused docs/plans/foo.md docs/plans/bar.md
|
|
370
389
|
|
|
371
390
|
Use --dry-run (-n) to preview changes without writing anything.`,
|
|
372
391
|
|
|
@@ -431,6 +450,53 @@ Options:
|
|
|
431
450
|
--list List all glossary terms
|
|
432
451
|
--json Output as JSON`,
|
|
433
452
|
|
|
453
|
+
statuses: `dotmd statuses — manage per-project status taxonomy
|
|
454
|
+
|
|
455
|
+
Subcommands:
|
|
456
|
+
list [--type <t>] [--json] Default. Table view of every status × type with all flags.
|
|
457
|
+
--type accepts comma-separated types.
|
|
458
|
+
add <name> --type <t> [--like <e>] [flags...]
|
|
459
|
+
Add a new status. --like <existing> clones every flag from
|
|
460
|
+
another status; user flags override. Inserts before the
|
|
461
|
+
first terminal/archive status. Refuses if name already
|
|
462
|
+
exists or is invalid.
|
|
463
|
+
set <name> --type <t> <flags...> Edit flags on an existing status. Refuses if status doesn't
|
|
464
|
+
exist. Flags overwrite individually.
|
|
465
|
+
remove <name> --type <t> Delete a status entry. Refuses if any docs use the status
|
|
466
|
+
(lists offenders, suggests \`dotmd migrate\`). Warns if an
|
|
467
|
+
explicit lifecycle export references the name.
|
|
468
|
+
migrate <type> One-shot conversion of array-form types.<t>.statuses to
|
|
469
|
+
rich form, pulling in peer staleDays/context and per-status
|
|
470
|
+
requiresModule from taxonomy.moduleRequiredFor.
|
|
471
|
+
|
|
472
|
+
Flags accepted by add/set:
|
|
473
|
+
--context <expanded|listed|counted> Briefing layout bucket
|
|
474
|
+
--staleDays <n|null> Stale threshold; null = never stale
|
|
475
|
+
--requiresModule / --no-requiresModule
|
|
476
|
+
--terminal / --no-terminal Closure state — excluded from active-work scope
|
|
477
|
+
--archive / --no-archive Auto-move to archive dir on transition
|
|
478
|
+
--skipStale / --no-skipStale
|
|
479
|
+
--skipWarnings / --no-skipWarnings
|
|
480
|
+
--quiet / --no-quiet Sugar for skipStale + skipWarnings (explicit overrides win)
|
|
481
|
+
|
|
482
|
+
Workflow flags:
|
|
483
|
+
--yes Skip the confirmation prompt
|
|
484
|
+
--dry-run, -n Show the diff without writing
|
|
485
|
+
--ignore-lifecycle-override Write even when an explicit \`lifecycle\` export
|
|
486
|
+
would silently mask the per-status flags
|
|
487
|
+
|
|
488
|
+
Examples:
|
|
489
|
+
dotmd statuses # list everything
|
|
490
|
+
dotmd statuses add paused --type plan --like blocked --quiet
|
|
491
|
+
dotmd statuses set archived --type plan --no-quiet
|
|
492
|
+
dotmd statuses remove obsolete --type plan
|
|
493
|
+
dotmd statuses migrate plan # array → rich
|
|
494
|
+
|
|
495
|
+
Lifecycle-override gotcha: if your config has both rich-form types and an explicit
|
|
496
|
+
\`export const lifecycle\`, the runtime ignores per-status flags. The CLI refuses
|
|
497
|
+
to write in that case unless you pass --ignore-lifecycle-override; the recommended
|
|
498
|
+
fix is to delete the explicit \`lifecycle\` block so flags take effect.`,
|
|
499
|
+
|
|
434
500
|
bulk: `dotmd bulk archive <f1> <f2> ... — archive multiple files at once
|
|
435
501
|
|
|
436
502
|
Archives each file: sets status to archived, moves to archive
|
|
@@ -551,6 +617,7 @@ async function main() {
|
|
|
551
617
|
if (command === 'migrate') { const { runMigrate } = await import('../src/migrate.mjs'); runMigrate(restArgs, config, { dryRun }); return; }
|
|
552
618
|
if (command === 'fix-refs') { const { runFixRefs } = await import('../src/fix-refs.mjs'); runFixRefs(restArgs, config, { dryRun }); return; }
|
|
553
619
|
if (command === 'doctor') { const { runDoctor } = await import('../src/doctor.mjs'); runDoctor(restArgs, config, { dryRun }); return; }
|
|
620
|
+
if (command === 'statuses') { const { runStatuses } = await import('../src/statuses.mjs'); await runStatuses(restArgs, config, { dryRun, type: typeArg }); return; }
|
|
554
621
|
|
|
555
622
|
// All remaining commands need the index + render modules
|
|
556
623
|
const { buildIndex } = await import('../src/index.mjs');
|
|
@@ -801,7 +868,7 @@ async function main() {
|
|
|
801
868
|
'focus', 'query', 'plans', 'stale', 'actionable', 'index', 'pickup', 'finish', 'status', 'archive', 'bulk', 'touch', 'doctor',
|
|
802
869
|
'unblocks', 'health', 'glossary',
|
|
803
870
|
'fix-refs', 'lint', 'rename', 'migrate', 'notion', 'export', 'summary',
|
|
804
|
-
'watch', 'diff', 'new', 'init', 'completions',
|
|
871
|
+
'watch', 'diff', 'new', 'init', 'completions', 'statuses',
|
|
805
872
|
];
|
|
806
873
|
const matches = allCommands
|
|
807
874
|
.map(c => ({ cmd: c, dist: levenshtein(command, c) }))
|
package/dotmd.config.example.mjs
CHANGED
|
@@ -103,13 +103,20 @@ export const statuses = {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
// Lifecycle behavior — which statuses trigger special handling.
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
//
|
|
107
|
+
// IMPORTANT: only enable this block if you're using ARRAY-form types (above) or
|
|
108
|
+
// no `types` definition. With rich-form types, the runtime derives these from
|
|
109
|
+
// per-status `terminal` / `archive` / `skipStale` / `skipWarnings` / `quiet`
|
|
110
|
+
// flags. An explicit `lifecycle` export sitting alongside rich-form types will
|
|
111
|
+
// SILENTLY OVERRIDE the per-status flags — `dotmd statuses` will warn you
|
|
112
|
+
// before writing into a config in that state.
|
|
113
|
+
//
|
|
114
|
+
// export const lifecycle = {
|
|
115
|
+
// archiveStatuses: ['archived'], // auto-move to archiveDir on transition
|
|
116
|
+
// skipStaleFor: ['archived'], // skip staleness checks
|
|
117
|
+
// skipWarningsFor: ['archived'], // skip validation warnings (summary, etc.)
|
|
118
|
+
// terminalStatuses: ['archived', 'deprecated', 'reference'], // skip current_state/next_step warnings, exclude from stats scope
|
|
119
|
+
// };
|
|
113
120
|
|
|
114
121
|
// Taxonomy validation — set fields to null to skip validation.
|
|
115
122
|
// moduleRequiredFor is derived from requiresModule when using rich status definitions.
|
package/package.json
CHANGED