dotmd-cli 0.12.0 → 0.13.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 CHANGED
@@ -113,7 +113,8 @@ dotmd coverage [--json] Metadata coverage report
113
113
  dotmd stats [--json] Doc health dashboard
114
114
  dotmd graph [--dot|--json] Visualize document relationships
115
115
  dotmd deps [file] Dependency tree or overview
116
- dotmd context [--summarize] Compact briefing (LLM-oriented)
116
+ dotmd briefing Compact summary for session start
117
+ dotmd context [--summarize] Full briefing (LLM-oriented)
117
118
  dotmd focus [status] Detailed view for one status group
118
119
  dotmd query [filters] Filtered search
119
120
  dotmd plans List all plans
package/bin/dotmd.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
  import path from 'node:path';
6
6
  import { resolveConfig } from '../src/config.mjs';
7
7
  import { buildIndex } from '../src/index.mjs';
8
- import { renderCompactList, renderVerboseList, renderContext, renderCheck, renderCoverage, buildCoverage } from '../src/render.mjs';
8
+ import { renderCompactList, renderVerboseList, renderContext, renderBriefing, renderCheck, renderCoverage, buildCoverage } from '../src/render.mjs';
9
9
  import { renderIndexFile, writeIndex } from '../src/index-file.mjs';
10
10
  import { runFocus, runQuery } from '../src/query.mjs';
11
11
  import { runStatus, runArchive, runTouch, runBulkArchive, runPickup, runFinish } from '../src/lifecycle.mjs';
@@ -39,7 +39,8 @@ const HELP = {
39
39
  View & Query:
40
40
  list [--verbose] [--json] List docs grouped by status (default command)
41
41
  json Full index as JSON
42
- context [--summarize] [--json] Compact briefing (LLM-oriented)
42
+ briefing [--json] Compact summary for session start (5-10 lines)
43
+ context [--summarize] [--json] Full briefing (LLM-oriented)
43
44
  focus [status] [--json] Detailed view for one status group
44
45
  query [filters] [--json] Filtered search (--status, --keyword, --stale, etc.)
45
46
  coverage [--json] Metadata coverage report
@@ -191,7 +192,15 @@ Shows detailed info for all docs matching the given status (default: active).
191
192
  Options:
192
193
  --json Output as JSON`,
193
194
 
194
- context: `dotmd context — compact briefing (LLM-oriented)
195
+ briefing: `dotmd briefing — compact summary for session start
196
+
197
+ Shows plan statuses with next steps, doc/research counts, and health
198
+ in 5-10 lines. Designed for LLM context injection.
199
+
200
+ Options:
201
+ --json Output as JSON`,
202
+
203
+ context: `dotmd context — full briefing (LLM-oriented)
195
204
 
196
205
  Generates a compact status briefing designed for AI/LLM consumption.
197
206
 
@@ -648,6 +657,24 @@ async function main() {
648
657
 
649
658
  if (command === 'focus') { runFocus(index, restArgs, config); return; }
650
659
  if (command === 'query') { runQuery(index, restArgs, config); return; }
660
+ if (command === 'briefing') {
661
+ if (args.includes('--json')) {
662
+ const plans = index.docs.filter(d => d.type === 'plan');
663
+ const docs = index.docs.filter(d => d.type === 'doc');
664
+ const research = index.docs.filter(d => d.type === 'research');
665
+ const stale = index.docs.filter(d => d.isStale && !config.lifecycle.skipStaleFor.has(d.status)).length;
666
+ process.stdout.write(JSON.stringify({
667
+ 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 })) },
668
+ docs: { total: docs.length, active: docs.filter(d => !config.lifecycle.terminalStatuses.has(d.status)).length },
669
+ research: { total: research.length, active: research.filter(d => d.status === 'active').length },
670
+ stale, errorCount: index.errors.length, warningCount: index.warnings.length,
671
+ }, null, 2) + '\n');
672
+ } else {
673
+ process.stdout.write(renderBriefing(index, config));
674
+ }
675
+ return;
676
+ }
677
+
651
678
  if (command === 'context') {
652
679
  const summarize = args.includes('--summarize');
653
680
  const modelIdx = args.indexOf('--model');
@@ -723,7 +750,7 @@ async function main() {
723
750
 
724
751
  // Unknown command — suggest closest match
725
752
  const allCommands = [
726
- 'list', 'json', 'check', 'coverage', 'stats', 'graph', 'deps', 'context',
753
+ 'list', 'json', 'check', 'coverage', 'stats', 'graph', 'deps', 'briefing', 'context',
727
754
  'focus', 'query', 'plans', 'stale', 'actionable', 'index', 'pickup', 'finish', 'status', 'archive', 'touch', 'doctor',
728
755
  'fix-refs', 'lint', 'rename', 'migrate', 'notion', 'export', 'summary',
729
756
  'watch', 'diff', 'new', 'init', 'completions',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotmd-cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
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",
@@ -1,7 +1,7 @@
1
1
  import { die } from './util.mjs';
2
2
 
3
3
  const COMMANDS = [
4
- 'list', 'json', 'check', 'coverage', 'stats', 'graph', 'deps', 'unblocks', 'health', 'glossary', 'context', 'focus', 'query',
4
+ 'list', 'json', 'check', 'coverage', 'stats', 'graph', 'deps', 'unblocks', 'health', 'glossary', 'briefing', 'context', 'focus', 'query',
5
5
  'plans', 'stale', 'actionable', 'index', 'pickup', 'finish', 'status', 'archive', 'bulk', 'touch', 'doctor', 'lint', 'rename', 'migrate',
6
6
  'fix-refs', 'notion', 'export', 'summary', 'watch', 'diff', 'init', 'new', 'completions',
7
7
  ];
@@ -32,6 +32,7 @@ const COMMAND_FLAGS = {
32
32
  plans: ['--status', '--json', '--sort', '--limit', '--all', '--stale', '--has-next-step'],
33
33
  stale: ['--json', '--sort', '--limit', '--all'],
34
34
  actionable: ['--json', '--sort', '--limit', '--all'],
35
+ briefing: ['--json'],
35
36
  pickup: ['--json'],
36
37
  finish: ['--json'],
37
38
  status: [],
package/src/render.mjs CHANGED
@@ -258,6 +258,44 @@ function _renderContext(index, config, opts = {}) {
258
258
  return `${lines.join('\n').trimEnd()}\n`;
259
259
  }
260
260
 
261
+ export function renderBriefing(index, config) {
262
+ const lines = [];
263
+ const plans = index.docs.filter(d => d.type === 'plan');
264
+ const docs = index.docs.filter(d => d.type === 'doc');
265
+ const research = index.docs.filter(d => d.type === 'research');
266
+ const untyped = index.docs.filter(d => !d.type);
267
+
268
+ if (plans.length) {
269
+ const bySt = {};
270
+ for (const p of plans) { bySt[p.status] = (bySt[p.status] ?? 0) + 1; }
271
+ const counts = Object.entries(bySt).map(([s, n]) => `${n} ${s}`).join(', ');
272
+ lines.push(`${plans.length} plans: ${counts}`);
273
+ const show = plans.filter(p => p.status === 'in-session' || p.status === 'active');
274
+ for (const p of show) {
275
+ const next = p.nextStep ? `next: ${p.nextStep}` : '(no next step)';
276
+ lines.push(` > ${path.basename(p.path, '.md')} (${p.status}) ${next}`);
277
+ }
278
+ }
279
+
280
+ const parts = [];
281
+ if (docs.length) {
282
+ const active = docs.filter(d => !config.lifecycle.terminalStatuses.has(d.status)).length;
283
+ const rest = docs.length - active;
284
+ parts.push(`${active} docs active` + (rest ? `, ${rest} other` : ''));
285
+ }
286
+ if (research.length) {
287
+ const active = research.filter(d => d.status === 'active').length;
288
+ parts.push(`${active} research active`);
289
+ }
290
+ if (untyped.length) parts.push(`${untyped.length} untyped`);
291
+ if (parts.length) lines.push(parts.join(' | '));
292
+
293
+ const stale = index.docs.filter(d => d.isStale && !config.lifecycle.skipStaleFor.has(d.status)).length;
294
+ lines.push(`Stale: ${stale} | Errors: ${index.errors.length} | Warnings: ${index.warnings.length}`);
295
+
296
+ return lines.join('\n') + '\n';
297
+ }
298
+
261
299
  export function renderCheck(index, config, opts = {}) {
262
300
  const defaultRenderer = (idx) => _renderCheck(idx, opts);
263
301
  if (config.hooks.renderCheck) {