dotmd-cli 0.49.5 → 0.50.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 +6 -0
- package/dotmd.config.example.mjs +1 -0
- package/package.json +1 -1
- package/src/config.mjs +5 -0
- package/src/hud.mjs +17 -69
- package/src/index-file.mjs +13 -3
- package/src/init.mjs +1 -0
package/README.md
CHANGED
|
@@ -879,9 +879,15 @@ export const index = {
|
|
|
879
879
|
path: 'docs/docs.md',
|
|
880
880
|
startMarker: '<!-- GENERATED:dotmd:start -->',
|
|
881
881
|
endMarker: '<!-- GENERATED:dotmd:end -->',
|
|
882
|
+
snapshot: 'status', // default; use 'state' to include live current_state text
|
|
882
883
|
};
|
|
883
884
|
```
|
|
884
885
|
|
|
886
|
+
Generated indexes default to status-only rows for live sections so README files
|
|
887
|
+
do not become stale mirrors of volatile `current_state` text. Set
|
|
888
|
+
`snapshot: 'state'` if you want the older `Status Snapshot` table for live
|
|
889
|
+
sections too. Archived highlights still include their historical snapshots.
|
|
890
|
+
|
|
885
891
|
All exports are optional. Additional options: `context`, `display`, `presets`, `templates`, `excludeDirs`, `notion`. See [`dotmd.config.example.mjs`](dotmd.config.example.mjs) for the full reference.
|
|
886
892
|
|
|
887
893
|
Config discovery walks up from cwd looking for `dotmd.config.mjs` or `.dotmd.config.mjs`.
|
package/dotmd.config.example.mjs
CHANGED
|
@@ -144,6 +144,7 @@ export const index = {
|
|
|
144
144
|
path: 'docs/docs.md',
|
|
145
145
|
startMarker: '<!-- GENERATED:dotmd:start -->',
|
|
146
146
|
endMarker: '<!-- GENERATED:dotmd:end -->',
|
|
147
|
+
snapshot: 'status', // default; use 'state' to include live current_state text
|
|
147
148
|
archivedLimit: 8,
|
|
148
149
|
};
|
|
149
150
|
|
package/package.json
CHANGED
package/src/config.mjs
CHANGED
|
@@ -316,6 +316,10 @@ function validateConfig(userConfig, config, validStatuses, indexPath) {
|
|
|
316
316
|
warnings.push(`Config: index path does not exist: ${indexPath}`);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
if (config.index?.snapshot !== undefined && !['status', 'state'].includes(config.index.snapshot)) {
|
|
320
|
+
warnings.push("Config: index.snapshot must be 'status' or 'state'.");
|
|
321
|
+
}
|
|
322
|
+
|
|
319
323
|
// Unknown top-level user config keys
|
|
320
324
|
for (const key of Object.keys(userConfig)) {
|
|
321
325
|
if (!VALID_CONFIG_KEYS.has(key)) {
|
|
@@ -512,6 +516,7 @@ export async function resolveConfig(cwd, explicitConfigPath) {
|
|
|
512
516
|
indexPath,
|
|
513
517
|
indexStartMarker: config.index?.startMarker ?? '<!-- GENERATED:dotmd:start -->',
|
|
514
518
|
indexEndMarker: config.index?.endMarker ?? '<!-- GENERATED:dotmd:end -->',
|
|
519
|
+
indexSnapshot: config.index?.snapshot ?? 'status',
|
|
515
520
|
archivedHighlightLimit: config.index?.archivedLimit ?? 8,
|
|
516
521
|
|
|
517
522
|
context: config.context,
|
package/src/hud.mjs
CHANGED
|
@@ -4,21 +4,11 @@ import { readLeases, findStaleLeases, currentSessionId, isLeaseStale, STALE_LEAS
|
|
|
4
4
|
import { scrubStaleSilently } from './lease-scrub.mjs';
|
|
5
5
|
import { extractFrontmatter, parseSimpleFrontmatter } from './frontmatter.mjs';
|
|
6
6
|
import { asString, toRepoPath } from './util.mjs';
|
|
7
|
-
import { dim
|
|
7
|
+
import { dim } from './color.mjs';
|
|
8
8
|
import { buildIndex } from './index.mjs';
|
|
9
9
|
import { refreshStaleSlashCommands } from './claude-commands.mjs';
|
|
10
10
|
import { readJournalEntries, journalFilePath } from './journal.mjs';
|
|
11
11
|
|
|
12
|
-
const MAX_PREVIEW = 5;
|
|
13
|
-
|
|
14
|
-
function slug(repoPath) { return path.basename(repoPath, '.md'); }
|
|
15
|
-
|
|
16
|
-
function previewList(items, max = MAX_PREVIEW) {
|
|
17
|
-
const slugs = items.slice(0, max).map(slug);
|
|
18
|
-
const more = items.length > max ? `, +${items.length - max} more` : '';
|
|
19
|
-
return slugs.join(', ') + more;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
12
|
// Statuses that count as "actionable" for a prompt are derived from config:
|
|
23
13
|
// types.prompt.context.expanded (the statuses the user wants prominently shown).
|
|
24
14
|
// Falls back to ['pending'] when no prompt type is configured (defensive default
|
|
@@ -223,11 +213,12 @@ export function runHud(argv, config) {
|
|
|
223
213
|
const hud = buildHud(config);
|
|
224
214
|
|
|
225
215
|
// Self-heal stale slash-command files. Wrapped: a broken scaffolder must
|
|
226
|
-
// never kill the SessionStart hook (would block every session).
|
|
227
|
-
//
|
|
228
|
-
|
|
216
|
+
// never kill the SessionStart hook (would block every session). Runs for its
|
|
217
|
+
// side effect only — the refresh is no longer announced in stdout (see the
|
|
218
|
+
// primer-only contract below). Skipped in --json mode to keep the structured
|
|
219
|
+
// shape stable for programmatic callers.
|
|
229
220
|
if (!json) {
|
|
230
|
-
try {
|
|
221
|
+
try { refreshStaleSlashCommands(config); }
|
|
231
222
|
catch { /* swallow — see comment above */ }
|
|
232
223
|
}
|
|
233
224
|
|
|
@@ -236,58 +227,15 @@ export function runHud(argv, config) {
|
|
|
236
227
|
return;
|
|
237
228
|
}
|
|
238
229
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (hud.owned?.length) state.push(`held: ${hud.owned.length} (${previewList(hud.owned)})`);
|
|
251
|
-
if (hud.prompts?.length) state.push(`prompts: ${hud.prompts.length} (${previewList(hud.prompts)})`);
|
|
252
|
-
if (hud.stale?.length) state.push(`stuck: ${hud.stale.length} (${previewList(hud.stale)})`);
|
|
253
|
-
if (hud.errors > 0) state.push(`errors: ${hud.errors} (run dotmd check)`);
|
|
254
|
-
if (state.length) lines.push(yellow(state.join(' · ')));
|
|
255
|
-
|
|
256
|
-
if (refreshed.length > 0) {
|
|
257
|
-
const from = refreshed[0].from;
|
|
258
|
-
const to = refreshed[0].to;
|
|
259
|
-
const names = refreshed.map(r => r.name).join(', ');
|
|
260
|
-
lines.push(dim(`↻ slash commands refreshed (v${from} → v${to}): ${names}`));
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// F17b: three journal-aware sections. Silent-when-clean: each block emits
|
|
264
|
-
// only when it has entries.
|
|
265
|
-
if (hud.previousSelf?.length) {
|
|
266
|
-
lines.push(dim('— previous self —'));
|
|
267
|
-
for (const e of hud.previousSelf) {
|
|
268
|
-
const cmd = (e.argv ?? []).join(' ');
|
|
269
|
-
const exitTag = e.exit === 0 ? '' : `, exit ${e.exit}`;
|
|
270
|
-
lines.push(dim(` ${cmd} (${e.ago}${exitTag})`));
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (hud.fleet?.length) {
|
|
275
|
-
lines.push(dim('— fleet (last 24h) —'));
|
|
276
|
-
for (const f of hud.fleet) {
|
|
277
|
-
const heldTag = f.holding?.length
|
|
278
|
-
? ` · holding ${f.holding.map(p => path.basename(p, '.md')).join(', ')}`
|
|
279
|
-
: '';
|
|
280
|
-
const staleTag = f.stale ? yellow(' [stale]') : '';
|
|
281
|
-
lines.push(dim(` session ${f.sid} · ${f.cmds} cmds · last ${f.lastAgo}${heldTag}`) + staleTag);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (hud.recentRejections?.length) {
|
|
286
|
-
lines.push(dim('— recent rejections (last 1h) —'));
|
|
287
|
-
for (const r of hud.recentRejections) {
|
|
288
|
-
lines.push(dim(` ${r.count}× "${r.cls}" on \`${r.cmd}\``));
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
process.stdout.write(lines.join('\n') + '\n');
|
|
230
|
+
// SessionStart contract: emit ONLY the command primer — the verb cheat-sheet
|
|
231
|
+
// that tells the agent which dotmd verbs exist. Everything else hud used to
|
|
232
|
+
// print (held/prompts/stuck/errors state, slash-command refresh notices, and
|
|
233
|
+
// the journal-aware previous-self / fleet / recent-rejections sections) is
|
|
234
|
+
// deliberately suppressed here: those signals nudged agents into phantom
|
|
235
|
+
// follow-up work — e.g. "errors: 1 (run dotmd check)" prompting a check run
|
|
236
|
+
// for state that belongs inside its own command. Each of those signals lives
|
|
237
|
+
// in its proper command (`plans`, `prompts`, `check`) and stays available via
|
|
238
|
+
// `dotmd hud --json` for programmatic callers. The hook's job is purely to
|
|
239
|
+
// teach the verbs, never to report status.
|
|
240
|
+
process.stdout.write(dim('dotmd: plans|briefing set <status> [<file>] new <type> <slug> use [<file>] archive <file> (use [no-arg] → oldest pending prompt)') + '\n');
|
|
293
241
|
}
|
package/src/index-file.mjs
CHANGED
|
@@ -21,6 +21,7 @@ export function renderIndexFile(index, config) {
|
|
|
21
21
|
function renderGeneratedBlock(index, config) {
|
|
22
22
|
const lines = [];
|
|
23
23
|
const indexDir = config.indexPath ? path.dirname(path.relative(config.repoRoot, config.indexPath)).split(path.sep).join('/') : '';
|
|
24
|
+
const snapshotMode = config.indexSnapshot ?? 'status';
|
|
24
25
|
|
|
25
26
|
for (const status of config.statusOrder) {
|
|
26
27
|
const docs = index.docs.filter(doc => doc.status === status);
|
|
@@ -34,10 +35,9 @@ function renderGeneratedBlock(index, config) {
|
|
|
34
35
|
|
|
35
36
|
lines.push(`## ${capitalize(status)}`);
|
|
36
37
|
lines.push('');
|
|
37
|
-
lines.push(
|
|
38
|
-
lines.push('|-----|-----------------|');
|
|
38
|
+
lines.push(...snapshotHeader(snapshotMode));
|
|
39
39
|
for (const doc of docs) {
|
|
40
|
-
const snapshot =
|
|
40
|
+
const snapshot = renderIndexSnapshot(doc, config, snapshotMode);
|
|
41
41
|
const linkPath = indexDir ? path.relative(indexDir, doc.path).split(path.sep).join('/') : doc.path;
|
|
42
42
|
lines.push(`| [${escapeTable(doc.title)}](${linkPath}) | ${escapeTable(snapshot)} |`);
|
|
43
43
|
}
|
|
@@ -76,6 +76,16 @@ function renderArchivedSection(docs, config, status) {
|
|
|
76
76
|
return lines;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function snapshotHeader(snapshotMode) {
|
|
80
|
+
if (snapshotMode === 'state') return ['| Doc | Status Snapshot |', '|-----|-----------------|'];
|
|
81
|
+
return ['| Doc | Status |', '|-----|--------|'];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function renderIndexSnapshot(doc, config, snapshotMode) {
|
|
85
|
+
if (snapshotMode === 'state') return formatSnapshot(doc, config);
|
|
86
|
+
return capitalize(doc.status ?? 'unknown');
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
export function writeIndex(content, config) {
|
|
80
90
|
writeFileSync(config.indexPath, content, 'utf8');
|
|
81
91
|
}
|
package/src/init.mjs
CHANGED
|
@@ -189,6 +189,7 @@ function generateDetectedConfig(scan, rootPath) {
|
|
|
189
189
|
lines.push(` path: '${rootPath}/docs.md',`);
|
|
190
190
|
lines.push(` startMarker: '<!-- GENERATED:dotmd:start -->',`);
|
|
191
191
|
lines.push(` endMarker: '<!-- GENERATED:dotmd:end -->',`);
|
|
192
|
+
lines.push(` snapshot: 'status',`);
|
|
192
193
|
lines.push('};');
|
|
193
194
|
lines.push('');
|
|
194
195
|
|