dotmd-cli 0.32.1 → 0.33.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/package.json +1 -1
- package/src/claude-commands.mjs +30 -1
- package/src/hud.mjs +16 -0
package/package.json
CHANGED
package/src/claude-commands.mjs
CHANGED
|
@@ -46,6 +46,22 @@ function generatePlansCommand(config) {
|
|
|
46
46
|
return lines.join('\n');
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function generateBatonCommand() {
|
|
50
|
+
const lines = [VERSION_MARKER, ''];
|
|
51
|
+
lines.push('You are wrapping this session. Hand the baton cleanly to the next one.');
|
|
52
|
+
lines.push('');
|
|
53
|
+
lines.push('1. **Update the in-flight plan.** Find it via `dotmd plans --status in-session`. Edit its `current_state:` / `next_step:` frontmatter so they reflect where things actually stand. If status should change (shipped → archive, stuck on a human decision → awaiting, etc.), transition with `dotmd status <file> <status>` — or `dotmd archive <file>` if work is done.');
|
|
54
|
+
lines.push('');
|
|
55
|
+
lines.push('2. **Save ONE lean handoff prompt.** Run `dotmd new prompt resume-<plan-slug>` with a body of ~10-20 lines: point at the plan file, name the next concrete decision, flag any gotchas. Do NOT recap the plan body (the plan is for that). Do NOT print the handoff into chat for the user to copy-paste — the saved prompt is the handoff.');
|
|
56
|
+
lines.push('');
|
|
57
|
+
lines.push('3. **Release the lease.** `dotmd release` (skip if `dotmd archive` already closed out — archive auto-releases).');
|
|
58
|
+
lines.push('');
|
|
59
|
+
lines.push('The next session\'s `dotmd hud` (SessionStart hook) surfaces the pending prompt automatically.');
|
|
60
|
+
lines.push('');
|
|
61
|
+
|
|
62
|
+
return lines.join('\n');
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
function generateDocsCommand(config) {
|
|
50
66
|
const roots = Array.isArray(config.raw?.root) ? config.raw.root : [config.raw?.root ?? 'docs'];
|
|
51
67
|
const rootCount = roots.length;
|
|
@@ -118,6 +134,7 @@ export function scaffoldClaudeCommands(cwd, config, opts = {}) {
|
|
|
118
134
|
const files = [
|
|
119
135
|
{ name: 'plans.md', generate: () => generatePlansCommand(config) },
|
|
120
136
|
{ name: 'docs.md', generate: () => generateDocsCommand(config) },
|
|
137
|
+
{ name: 'baton.md', generate: () => generateBatonCommand() },
|
|
121
138
|
];
|
|
122
139
|
|
|
123
140
|
for (const { name, generate } of files) {
|
|
@@ -149,12 +166,24 @@ export function scaffoldClaudeCommands(cwd, config, opts = {}) {
|
|
|
149
166
|
return results;
|
|
150
167
|
}
|
|
151
168
|
|
|
169
|
+
// Self-heal: regen any slash-command file whose banner is older than pkg.version.
|
|
170
|
+
// Designed for runHud to call at SessionStart — closes the gap between "user
|
|
171
|
+
// upgraded dotmd" and "slash-command body reflects the new version" without
|
|
172
|
+
// requiring a manual `dotmd doctor`. Returns only the entries that actually
|
|
173
|
+
// changed so the caller can surface a one-line note; an empty array means the
|
|
174
|
+
// hud silent-clean contract is preserved. `skipped` (user-managed, no banner)
|
|
175
|
+
// and `current` entries are filtered out — callers don't care about them.
|
|
176
|
+
export function refreshStaleSlashCommands(config) {
|
|
177
|
+
const results = scaffoldClaudeCommands(config.repoRoot, config);
|
|
178
|
+
return results.filter(r => r.action === 'updated');
|
|
179
|
+
}
|
|
180
|
+
|
|
152
181
|
export function checkClaudeCommands(cwd) {
|
|
153
182
|
const commandsDir = path.join(cwd, '.claude', 'commands');
|
|
154
183
|
if (!existsSync(commandsDir)) return [];
|
|
155
184
|
|
|
156
185
|
const warnings = [];
|
|
157
|
-
for (const name of ['plans.md', 'docs.md']) {
|
|
186
|
+
for (const name of ['plans.md', 'docs.md', 'baton.md']) {
|
|
158
187
|
const filePath = path.join(commandsDir, name);
|
|
159
188
|
const installedVersion = getInstalledVersion(filePath);
|
|
160
189
|
if (installedVersion && installedVersion !== pkg.version) {
|
package/src/hud.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { extractFrontmatter, parseSimpleFrontmatter } from './frontmatter.mjs';
|
|
|
5
5
|
import { asString, toRepoPath } from './util.mjs';
|
|
6
6
|
import { green, yellow, red, dim } from './color.mjs';
|
|
7
7
|
import { buildIndex } from './index.mjs';
|
|
8
|
+
import { refreshStaleSlashCommands } from './claude-commands.mjs';
|
|
8
9
|
|
|
9
10
|
const MAX_PREVIEW = 5;
|
|
10
11
|
|
|
@@ -89,6 +90,15 @@ export function runHud(argv, config) {
|
|
|
89
90
|
const json = argv.includes('--json');
|
|
90
91
|
const hud = buildHud(config);
|
|
91
92
|
|
|
93
|
+
// Self-heal stale slash-command files. Wrapped: a broken scaffolder must
|
|
94
|
+
// never kill the SessionStart hook (would block every session). Skipped in
|
|
95
|
+
// --json mode to keep the structured shape stable for programmatic callers.
|
|
96
|
+
let refreshed = [];
|
|
97
|
+
if (!json) {
|
|
98
|
+
try { refreshed = refreshStaleSlashCommands(config); }
|
|
99
|
+
catch { /* swallow — see comment above */ }
|
|
100
|
+
}
|
|
101
|
+
|
|
92
102
|
if (json) {
|
|
93
103
|
process.stdout.write(JSON.stringify(hud, null, 2) + '\n');
|
|
94
104
|
return;
|
|
@@ -107,6 +117,12 @@ export function runHud(argv, config) {
|
|
|
107
117
|
if (hud.errors > 0) {
|
|
108
118
|
lines.push(red(`✗ ${hud.errors} validation error${hud.errors === 1 ? '' : 's'} ${dim('(run: dotmd check)')}`));
|
|
109
119
|
}
|
|
120
|
+
if (refreshed.length > 0) {
|
|
121
|
+
const from = refreshed[0].from;
|
|
122
|
+
const to = refreshed[0].to;
|
|
123
|
+
const names = refreshed.map(r => r.name).join(', ');
|
|
124
|
+
lines.push(dim(`↻ slash commands refreshed (v${from} → v${to}): ${names}`));
|
|
125
|
+
}
|
|
110
126
|
|
|
111
127
|
if (lines.length === 0) return; // silent when clean
|
|
112
128
|
process.stdout.write(lines.join('\n') + '\n');
|