dotmd-cli 0.64.0 → 0.64.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 CHANGED
@@ -9,6 +9,7 @@ Index, query, validate, and lifecycle-manage any collection of `.md` files — p
9
9
  ```bash
10
10
  npm install -g dotmd-cli # global — use `dotmd` anywhere
11
11
  npm install -D dotmd-cli # project devDep — use via npm scripts
12
+ npx dotmd-cli init # try it without installing — scaffold a repo first
12
13
  # requires Node.js >= 20
13
14
  ```
14
15
 
@@ -27,6 +28,19 @@ The plugin bundles the hooks (`SessionStart`/`SubagentStart` priming, a `PreTool
27
28
 
28
29
  > **Upgrading to 0.57.0+:** per-repo `.claude/commands/{plans,docs,baton}.md` scaffolding is retired — that guidance now ships via the plugin's workflow skill and `/plans`, `/docs`, `/prompts`, `/baton` commands. On the next `dotmd hud` (SessionStart), dotmd removes those generated files (only banner-stamped `<!-- dotmd-generated -->` ones — your hand-authored command files are never touched). If you'd committed them, you'll see deletions to commit — that's expected. Run `claude plugin update dotmd@dotmd` to pick up `/baton`.
29
30
 
31
+ ### Updating
32
+
33
+ The CLI and the Claude Code plugin are versioned in lockstep but ship as separate artifacts, so upgrading one can leave the other behind. `dotmd update` keeps them aligned:
34
+
35
+ ```bash
36
+ dotmd update # update both: npm CLI + the plugin
37
+ dotmd update --check # report CLI vs plugin versions, change nothing (no network)
38
+ dotmd update --cli-only # just the npm CLI
39
+ dotmd update --plugin-only # just the plugin (what to run after a plain npm upgrade)
40
+ ```
41
+
42
+ `--plugin-only` is the usual fixup: after `npm i -g dotmd-cli@latest` the CLI is fresh but the plugin is stale, so run `dotmd update --plugin-only`, then restart the session (or `/reload-plugins`).
43
+
30
44
  ## Quick Start
31
45
 
32
46
  ```bash
@@ -338,6 +352,18 @@ Each built-in type has a template baked in:
338
352
  | `doc` | `docs/<slug>.md` | Overview → Version History → Related (build-up shape lite) |
339
353
  | `prompt` | `docs/prompts/<slug>.md` | Body is required (see [Saved Prompts](#saved-prompts)) |
340
354
 
355
+ **Plan body variants (plans only).** The default `plan` template is the full build-up shape. For a smaller plan, or the recurring audit shape, pass one body-variant flag:
356
+
357
+ - `--lite` / `--minimal` — Problem → Phases → Version History (drops Goals / Non-Goals / What Exists Today / Constraints / Decisions / Deferred / Closeout).
358
+ - `--audit` / `--findings` — Problem → Findings (ranked) → Suggested order → Open Questions, for "I investigated X, here's what I found" plans.
359
+
360
+ ```bash
361
+ dotmd new plan quick-fix --lite
362
+ dotmd new plan perf-audit --audit
363
+ ```
364
+
365
+ To scaffold an ordered sprint or a coordination map instead, see [Runlists](#runlists-ordered-groups-of-plans) (`--runlist a,b,c` / `--coordination`). The body variants and the hub flags are all mutually exclusive — a plan has exactly one body shape.
366
+
341
367
  Add custom types via `templates` in your config:
342
368
 
343
369
  ```js
package/bin/dotmd.mjs CHANGED
@@ -255,6 +255,7 @@ Create & Export:
255
255
 
256
256
  Setup:
257
257
  init Create starter config + docs directory
258
+ update [--check|--cli-only|--plugin-only] Update the CLI + Claude Code plugin (--check reports skew, no network)
258
259
  statuses [list|add|set|remove|migrate] Manage per-project status taxonomy
259
260
  help statuses Full status vocabulary + unstuck-actions + transitions
260
261
  watch [command] Re-run a command on file changes
@@ -857,6 +858,20 @@ Scaffolding runlists (plans only):
857
858
  dotmd new plan auth-revamp --runlist extract,rewrite,cleanup
858
859
  dotmd new plan platform --coordination
859
860
 
861
+ Plan body variants (plans only — pick one body shape):
862
+ --lite / --minimal Trimmed plan: Problem → Phases → Version History. Drops
863
+ the full build-up scaffold (Goals / Non-Goals / What
864
+ Exists Today / Constraints / Decisions / Deferred /
865
+ Closeout) for a quick plan that doesn't need it.
866
+ --audit / --findings Audit plan: Problem → Findings (ranked) → Suggested order
867
+ → Open Questions. The "investigated X, here's what I
868
+ found" shape, instead of build-up phases.
869
+ (The body variants and \`--runlist\`/\`--coordination\` are all mutually
870
+ exclusive — a plan has exactly one body shape.)
871
+
872
+ dotmd new plan quick-fix --lite
873
+ dotmd new plan perf-audit --audit
874
+
860
875
  Other options:
861
876
  --status <s> Set initial status (defaults to first valid status for the type)
862
877
  --title <t> Override the auto-derived title
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotmd-cli",
3
- "version": "0.64.0",
3
+ "version": "0.64.2",
4
4
  "description": "CLI for managing markdown documents with YAML frontmatter — index, query, validate, graph, export, Notion sync, AI summaries.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/init.mjs CHANGED
@@ -190,9 +190,15 @@ function generateDetectedConfig(scan, rootPath) {
190
190
  lines.push('');
191
191
  }
192
192
 
193
- if (scan.surfaces.size > 0) {
193
+ if (scan.surfaces.size > 0 || scan.modules.size > 0) {
194
194
  lines.push('export const taxonomy = {');
195
- lines.push(` surfaces: [${[...scan.surfaces].sort().map(s => `'${s}'`).join(', ')}],`);
195
+ // Emit the full detected set so every existing doc passes — taxonomy
196
+ // enforcement only flags values outside the list, and the scan collected
197
+ // all of them. New surfaces/modules added later warn until appended here.
198
+ if (scan.surfaces.size > 0)
199
+ lines.push(` surfaces: [${[...scan.surfaces].sort().map(s => `'${s}'`).join(', ')}],`);
200
+ if (scan.modules.size > 0)
201
+ lines.push(` modules: [${[...scan.modules].sort().map(m => `'${m}'`).join(', ')}],`);
196
202
  lines.push('};');
197
203
  lines.push('');
198
204
  }
package/src/new.mjs CHANGED
@@ -350,6 +350,73 @@ graph pick it up. -->
350
350
  `;
351
351
  }
352
352
 
353
+ // Body for a `--lite` plan: the full build-up scaffold (Goals / Non-Goals /
354
+ // What Exists Today / Constraints / Decisions / Open Questions / Deferred /
355
+ // Closeout) is stripped down to the essentials — Problem → Phases → Version
356
+ // History — for a quick plan that doesn't need the full ceremony.
357
+ function litePlanBody(title, bodyInput, today) {
358
+ return `
359
+ # ${title}
360
+
361
+ > One-paragraph problem statement: what this plan is for, why now.
362
+
363
+ ## Problem
364
+
365
+ ${bodyInput?.trim() ?? ''}
366
+
367
+ ## Phases
368
+
369
+ <!-- Status markers in heading text: ⬜ not started · 🟡 in progress (pickup
370
+ targets this) · ✅ shipped · ⏭ skipped · 🚧 blocked. -->
371
+
372
+ ### Phase 1 — <title> ⬜
373
+
374
+
375
+
376
+ ## Version History
377
+
378
+ - **${today}** Created.
379
+ `;
380
+ }
381
+
382
+ // Body for an `--audit` plan: the recurring "investigated X, here's what I
383
+ // found" shape — ranked findings with impact tags, a suggested order to act on
384
+ // them, and open questions. The build-up phases scaffold doesn't fit audits
385
+ // (e.g. the onboarding audit), which is why this shape recurs by hand.
386
+ function auditPlanBody(title, bodyInput, today) {
387
+ return `
388
+ # ${title}
389
+
390
+ > One-paragraph: what was audited, and the headline finding.
391
+
392
+ ## Problem
393
+
394
+ ${bodyInput?.trim() ?? ''}
395
+
396
+ ## Findings (ranked)
397
+
398
+ ### 1. <finding> [impact]
399
+
400
+
401
+
402
+ ### 2. <finding> [impact]
403
+
404
+
405
+
406
+ ## Suggested order
407
+
408
+ 1. <which finding to act on first, and why>
409
+
410
+ ## Open Questions
411
+
412
+
413
+
414
+ ## Version History
415
+
416
+ - **${today}** Created (audit).
417
+ `;
418
+ }
419
+
353
420
  // Minimal child plan stub for a scaffolded runlist child. parent_plan points
354
421
  // back at the hub (same dir) so \`dotmd doctor\` is satisfied and the reverse
355
422
  // link/graph work; status starts `planned` (queued behind the hub).
@@ -402,11 +469,15 @@ export async function runNew(argv, config, opts = {}) {
402
469
  let showFiles = opts.showFiles ?? false;
403
470
  let runlistArg = null; // --runlist a,b,c → sprint hub + child stubs
404
471
  let coordination = false; // --coordination → coordination hub skeleton
472
+ let lite = false; // --lite/--minimal → trimmed plan body
473
+ let audit = false; // --audit/--findings → ranked-findings plan body
405
474
  for (let i = 0; i < argv.length; i++) {
406
475
  if (argv[i] === '--status' && argv[i + 1]) { status = argv[++i]; continue; }
407
476
  if (argv[i] === '--title' && argv[i + 1]) { title = argv[++i]; continue; }
408
477
  if (argv[i] === '--runlist' && argv[i + 1]) { runlistArg = argv[++i]; continue; }
409
478
  if (argv[i] === '--coordination') { coordination = true; continue; }
479
+ if (argv[i] === '--lite' || argv[i] === '--minimal') { lite = true; continue; }
480
+ if (argv[i] === '--audit' || argv[i] === '--findings') { audit = true; continue; }
410
481
  // --body is the canonical flag; --message is a back-compat alias.
411
482
  if ((argv[i] === '--body' || argv[i] === '--message') && argv[i + 1]) {
412
483
  bodyFlagName = argv[i];
@@ -474,16 +545,28 @@ export async function runNew(argv, config, opts = {}) {
474
545
  die(`Invalid status \`${status}\` for type \`${typeName}\`\nValid: ${[...effective].join(', ')}`);
475
546
  }
476
547
 
477
- // Runlist/coordination hubs are a plan shape, not a separate type. Guard the
478
- // flags to type plan and reject the contradictory combination (a sprint
479
- // `runlist:` array vs a prose-first coordination map are different shapes).
548
+ // Runlist/coordination hubs and the --lite/--audit variants are all plan
549
+ // *body shapes*, not separate types. Guard them to type plan and reject any
550
+ // combination — a plan has exactly one body shape (a sprint `runlist:` array,
551
+ // a prose-first coordination map, a trimmed lite plan, or a ranked-findings
552
+ // audit are mutually exclusive).
480
553
  const isRunlistHub = runlistArg !== null;
481
554
  const isCoordinationHub = coordination;
482
- if ((isRunlistHub || isCoordinationHub) && typeName !== 'plan') {
483
- die(`--${isRunlistHub ? 'runlist' : 'coordination'} only applies to plans. Use: dotmd new plan <name> --${isRunlistHub ? 'runlist a,b,c' : 'coordination'}`);
484
- }
485
- if (isRunlistHub && isCoordinationHub) {
486
- die('--runlist and --coordination are mutually exclusive: a sprint runlist hub carries an ordered `runlist:` array; a coordination hub is a prose-first map (`execution_mode: coordination`). Pick one.');
555
+ const isLite = lite;
556
+ const isAudit = audit;
557
+ const planShapes = [
558
+ ['--runlist', isRunlistHub],
559
+ ['--coordination', isCoordinationHub],
560
+ ['--lite', isLite],
561
+ ['--audit', isAudit],
562
+ ].filter(([, on]) => on);
563
+ if (planShapes.length > 0 && typeName !== 'plan') {
564
+ const flag = planShapes[0][0];
565
+ const usage = flag === '--runlist' ? '--runlist a,b,c' : flag;
566
+ die(`${flag} only applies to plans. Use: dotmd new plan <name> ${usage}`);
567
+ }
568
+ if (planShapes.length > 1) {
569
+ die(`${planShapes.map(([f]) => f).join(' and ')} are mutually exclusive — a plan has one body shape. Pick one.`);
487
570
  }
488
571
  const runlistTokens = isRunlistHub
489
572
  ? runlistArg.split(',').map(s => s.trim()).filter(Boolean)
@@ -652,6 +735,8 @@ export async function runNew(argv, config, opts = {}) {
652
735
  let body;
653
736
  if (isRunlistHub) body = runlistHubBody(docTitle, slug, runlistChildren, bodyInput, today);
654
737
  else if (isCoordinationHub) body = coordinationHubBody(docTitle, bodyInput, today);
738
+ else if (isLite) body = litePlanBody(docTitle, bodyInput, today);
739
+ else if (isAudit) body = auditPlanBody(docTitle, bodyInput, today);
655
740
  else body = template.body(docTitle, tmplCtx);
656
741
  content = `---\n${fm}\n---\n${body}`;
657
742
  }
@@ -669,7 +754,11 @@ export async function runNew(argv, config, opts = {}) {
669
754
  rootHint = `Root: ${chosenLabel} (others: ${others.join(', ')} — pass --root <name> to change)\n`;
670
755
  }
671
756
 
672
- const hubKind = isRunlistHub ? ' (runlist hub)' : isCoordinationHub ? ' (coordination hub)' : '';
757
+ const hubKind = isRunlistHub ? ' (runlist hub)'
758
+ : isCoordinationHub ? ' (coordination hub)'
759
+ : isLite ? ' (lite plan)'
760
+ : isAudit ? ' (audit plan)'
761
+ : '';
673
762
 
674
763
  if (dryRun) {
675
764
  process.stdout.write(`${dim('[dry-run]')} Would create: ${repoPath}\n`);