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 +50 -14
- package/package.json +1 -1
- package/src/lint.mjs +6 -2
- package/src/render.mjs +44 -0
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]
|
|
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
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
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
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
|
|
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
|
|