dotmd-cli 0.8.4 → 0.8.5

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/bin/dotmd.mjs CHANGED
@@ -35,7 +35,7 @@ const HELP = {
35
35
  _main: `dotmd v${pkg.version} — frontmatter markdown document manager
36
36
 
37
37
  Commands:
38
- list [--verbose] List docs grouped by status (default)
38
+ list [--verbose|--json] List docs grouped by status (default)
39
39
  json Full index as JSON
40
40
  check [flags] Validate frontmatter and references
41
41
  coverage [--json] Metadata coverage report
@@ -70,6 +70,22 @@ Options:
70
70
  --help, -h Show help
71
71
  --version, -v Show version`,
72
72
 
73
+ list: `dotmd list — list docs grouped by status
74
+
75
+ Options:
76
+ --verbose Show full details per doc
77
+ --json Output full index as JSON (same as dotmd json)`,
78
+
79
+ json: `dotmd json — full index as JSON
80
+
81
+ Outputs the complete document index as JSON to stdout.`,
82
+
83
+ completions: `dotmd completions <bash|zsh> — output shell completion script
84
+
85
+ Add to your shell config:
86
+ bash: eval "$(dotmd completions bash)"
87
+ zsh: eval "$(dotmd completions zsh)"`,
88
+
73
89
  query: `dotmd query — filtered document search
74
90
 
75
91
  Filters:
@@ -105,7 +121,9 @@ Use --dry-run (-n) to preview changes without writing anything.`,
105
121
 
106
122
  Options:
107
123
  --errors-only Show only errors, suppress warnings
108
- --fix Auto-fix broken references and regenerate index`,
124
+ --fix Auto-fix broken refs, lint issues, and regenerate index
125
+ --json Output errors and warnings as JSON
126
+ --dry-run, -n Preview fixes without writing (with --fix)`,
109
127
 
110
128
  archive: `dotmd archive <file> — archive a document
111
129
 
@@ -123,13 +141,17 @@ Options:
123
141
 
124
142
  focus: `dotmd focus [status] — detailed view for one status group
125
143
 
126
- Shows detailed info for all docs matching the given status (default: active).`,
144
+ Shows detailed info for all docs matching the given status (default: active).
145
+
146
+ Options:
147
+ --json Output as JSON`,
127
148
 
128
149
  context: `dotmd context — compact briefing (LLM-oriented)
129
150
 
130
151
  Generates a compact status briefing designed for AI/LLM consumption.
131
152
 
132
153
  Options:
154
+ --json Output as JSON
133
155
  --summarize Add AI summaries for expanded docs
134
156
  --model <name> MLX model for AI summaries`,
135
157
 
@@ -242,7 +264,8 @@ Options:
242
264
  --output <path> Write to file/directory (default: stdout for md/json)
243
265
  --status <s1,s2> Filter by status
244
266
  --module <name> Filter by module
245
- --root <name> Filter by root`,
267
+ --root <name> Filter by root
268
+ --dry-run, -n Preview without writing`,
246
269
 
247
270
  summary: `dotmd summary <file> — AI summary of a document
248
271
 
@@ -439,19 +462,32 @@ async function main() {
439
462
 
440
463
  if (fix) {
441
464
  // Auto-fix: broken refs, then lint, then rebuild index
442
- const refResult = fixBrokenRefs(config, { dryRun, quiet: false });
443
- if (!dryRun) {
444
- runLint(['--fix'], config, { dryRun });
445
- }
446
- if (!dryRun && config.indexPath) {
447
- const { renderIndexFile: rif, writeIndex: wi } = await import('../src/index-file.mjs');
448
- const freshIndex = buildIndex(config);
449
- wi(rif(freshIndex, config), config);
450
- process.stdout.write('Index regenerated.\n');
465
+ fixBrokenRefs(config, { dryRun, quiet: false });
466
+ runLint(['--fix'], config, { dryRun });
467
+ if (config.indexPath) {
468
+ if (!dryRun) {
469
+ const { renderIndexFile: rif, writeIndex: wi } = await import('../src/index-file.mjs');
470
+ const freshIndex = buildIndex(config);
471
+ wi(rif(freshIndex, config), config);
472
+ process.stdout.write('Index regenerated.\n');
473
+ } else {
474
+ process.stdout.write('[dry-run] Would regenerate index.\n');
475
+ }
451
476
  }
452
477
  // Show remaining issues
453
478
  const freshIndex = buildIndex(config);
454
- process.stdout.write('\n' + renderCheck(freshIndex, config, { errorsOnly }));
479
+ if (args.includes('--json')) {
480
+ process.stdout.write(JSON.stringify({
481
+ docsScanned: freshIndex.docs.length,
482
+ errors: freshIndex.errors,
483
+ warnings: errorsOnly ? [] : freshIndex.warnings,
484
+ errorCount: freshIndex.errors.length,
485
+ warningCount: freshIndex.warnings.length,
486
+ passed: freshIndex.errors.length === 0,
487
+ }, null, 2) + '\n');
488
+ } else {
489
+ process.stdout.write('\n' + renderCheck(freshIndex, config, { errorsOnly }));
490
+ }
455
491
  if (freshIndex.errors.length > 0) process.exitCode = 1;
456
492
  return;
457
493
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotmd-cli",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
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/lint.mjs CHANGED
@@ -78,9 +78,13 @@ export function runLint(argv, config, opts = {}) {
78
78
  }
79
79
  }
80
80
 
81
- // Also get non-fixable issues from index
81
+ // Also get non-fixable issues from index, excluding issues we can already fix
82
82
  const index = buildIndex(config);
83
- const nonFixable = [...index.errors, ...index.warnings];
83
+ const fixablePaths = new Set(fixable.map(f => f.repoPath));
84
+ const nonFixable = [...index.errors, ...index.warnings].filter(issue => {
85
+ if (issue.message.includes('Missing frontmatter `status`') && fixablePaths.has(issue.path)) return false;
86
+ return true;
87
+ });
84
88
 
85
89
  if (!fix) {
86
90
  // Report mode
package/src/render.mjs CHANGED
@@ -41,6 +41,32 @@ function _renderCompactList(index, config) {
41
41
  lines.push('');
42
42
  }
43
43
 
44
+ // Render docs with statuses not in statusOrder
45
+ const knownStatuses = new Set(config.statusOrder);
46
+ const otherStatuses = [...new Set(index.docs.filter(d => d.status && !knownStatuses.has(d.status)).map(d => d.status))].sort();
47
+ for (const status of otherStatuses) {
48
+ const docs = index.docs.filter(d => d.status === status);
49
+ if (!docs.length) continue;
50
+
51
+ lines.push(bold(`${capitalize(status)} (${docs.length})`));
52
+ const maxTitle = Math.min(config.display.truncateTitle || 30, Math.max(...docs.map(d => d.title.length)));
53
+
54
+ for (const doc of docs) {
55
+ const title = doc.title.length > maxTitle
56
+ ? doc.title.slice(0, maxTitle - 3) + '...'
57
+ : doc.title.padEnd(maxTitle);
58
+ const days = doc.daysSinceUpdate != null ? `${doc.daysSinceUpdate}d` : '';
59
+ const progress = renderProgressBar(doc.checklist);
60
+ const next = doc.nextStep ? `next: ${doc.nextStep}` : '';
61
+ const parts = [` ${title} ${days.padStart(4)}`];
62
+ if (progress) parts.push(progress);
63
+ if (next) parts.push(next);
64
+ const line = parts.join(' ');
65
+ lines.push(line.length > maxWidth ? line.slice(0, maxWidth - 3) + '...' : line);
66
+ }
67
+ lines.push('');
68
+ }
69
+
44
70
  return `${lines.join('\n').trimEnd()}\n`;
45
71
  }
46
72
 
@@ -62,6 +88,24 @@ export function renderVerboseList(index, config) {
62
88
  lines.push('');
63
89
  }
64
90
 
91
+ // Render docs with statuses not in statusOrder
92
+ const knownStatuses = new Set(config.statusOrder);
93
+ const otherStatuses = [...new Set(index.docs.filter(d => d.status && !knownStatuses.has(d.status)).map(d => d.status))].sort();
94
+ for (const status of otherStatuses) {
95
+ const docs = index.docs.filter(doc => doc.status === status);
96
+ if (docs.length === 0) continue;
97
+
98
+ lines.push(`${capitalize(status)} (${docs.length})`);
99
+ for (const doc of docs) {
100
+ const parts = [`- ${doc.title}`, `${capitalize(status)}: ${doc.currentState}`, `(${doc.path})`];
101
+ if (doc.nextStep) {
102
+ parts.push(`next: ${doc.nextStep}`);
103
+ }
104
+ lines.push(parts.join(' — '));
105
+ }
106
+ lines.push('');
107
+ }
108
+
65
109
  return `${lines.join('\n').trimEnd()}\n`;
66
110
  }
67
111