dotmd-cli 0.18.0 → 0.19.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/lifecycle.mjs +8 -8
- package/src/new.mjs +79 -6
- package/src/util.mjs +4 -0
package/package.json
CHANGED
package/src/lifecycle.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { extractFrontmatter, parseSimpleFrontmatter, replaceFrontmatter } from './frontmatter.mjs';
|
|
4
|
-
import { asString, toRepoPath, die, warn, resolveDocPath, escapeRegex } from './util.mjs';
|
|
4
|
+
import { asString, toRepoPath, die, warn, resolveDocPath, escapeRegex, nowIso } from './util.mjs';
|
|
5
5
|
import { gitMv, getGitLastModified, getGitLastModifiedBatch } from './git.mjs';
|
|
6
6
|
import { buildIndex, collectDocFiles } from './index.mjs';
|
|
7
7
|
import { renderIndexFile, writeIndex } from './index-file.mjs';
|
|
@@ -71,7 +71,7 @@ export async function runStatus(argv, config, opts = {}) {
|
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const today =
|
|
74
|
+
const today = nowIso();
|
|
75
75
|
const archiveDir = path.join(fileRoot, config.archiveDir);
|
|
76
76
|
const relFromRoot = path.relative(fileRoot, filePath);
|
|
77
77
|
const inArchive = relFromRoot.startsWith(config.archiveDir + '/') || relFromRoot.startsWith(config.archiveDir + path.sep);
|
|
@@ -185,7 +185,7 @@ export async function runPickup(argv, config, opts = {}) {
|
|
|
185
185
|
const pickupable = new Set(['active', 'planned', 'in-session']);
|
|
186
186
|
if (oldStatus && !pickupable.has(oldStatus) && !handoffQueued) die(`Cannot pick up a plan with status '${oldStatus}'. Must be active or planned.\n ${repoPath}`);
|
|
187
187
|
|
|
188
|
-
const today =
|
|
188
|
+
const today = nowIso();
|
|
189
189
|
const leaseOldStatus = oldStatus === 'in-session' ? 'active' : (oldStatus ?? 'active');
|
|
190
190
|
let leaseOutcome = 'acquired';
|
|
191
191
|
|
|
@@ -302,7 +302,7 @@ export async function runUnpickup(argv, config, opts = {}) {
|
|
|
302
302
|
const parsedFm = parseSimpleFrontmatter(fmRaw);
|
|
303
303
|
const cur = asString(parsedFm.status);
|
|
304
304
|
if (cur === 'in-session') {
|
|
305
|
-
const today =
|
|
305
|
+
const today = nowIso();
|
|
306
306
|
updateFrontmatter(filePath, { status: newStatus, updated: today });
|
|
307
307
|
}
|
|
308
308
|
// If frontmatter is no longer in-session (manual flip), leave it alone.
|
|
@@ -412,7 +412,7 @@ export async function runFinish(argv, config, opts = {}) {
|
|
|
412
412
|
|
|
413
413
|
if (oldStatus !== 'in-session') die(`Plan is not in-session (current: ${oldStatus}).\n ${repoPath}`);
|
|
414
414
|
|
|
415
|
-
const today =
|
|
415
|
+
const today = nowIso();
|
|
416
416
|
|
|
417
417
|
if (dryRun) {
|
|
418
418
|
process.stderr.write(`${dim('[dry-run]')} Would update: status: in-session → ${targetStatus}, updated: ${today}\n`);
|
|
@@ -451,7 +451,7 @@ export function runArchive(argv, config, opts = {}) {
|
|
|
451
451
|
const parsed = parseSimpleFrontmatter(frontmatter);
|
|
452
452
|
const oldStatus = asString(parsed.status) ?? 'unknown';
|
|
453
453
|
|
|
454
|
-
const today =
|
|
454
|
+
const today = nowIso();
|
|
455
455
|
const targetDir = path.join(archiveFileRoot, config.archiveDir);
|
|
456
456
|
const targetPath = path.join(targetDir, path.basename(filePath));
|
|
457
457
|
const oldRepoPath = toRepoPath(filePath, config.repoRoot);
|
|
@@ -608,7 +608,7 @@ export function runTouch(argv, config, opts = {}) {
|
|
|
608
608
|
const filePath = resolveDocPath(input, config);
|
|
609
609
|
if (!filePath) { die(`File not found: ${input}\nSearched: ${toRepoPath(config.repoRoot, config.repoRoot) || '.'}, ${toRepoPath(config.docsRoot, config.repoRoot)}`); }
|
|
610
610
|
|
|
611
|
-
const today =
|
|
611
|
+
const today = nowIso();
|
|
612
612
|
|
|
613
613
|
if (dryRun) {
|
|
614
614
|
process.stdout.write(`${dim('[dry-run]')} Would touch: ${toRepoPath(filePath, config.repoRoot)} (updated → ${today})\n`);
|
|
@@ -790,7 +790,7 @@ export async function runHandoff(argv, config, opts = {}) {
|
|
|
790
790
|
die(`Not held by this session: ${repoPath}\n Run \`dotmd pickup ${repoPath}\` first.`);
|
|
791
791
|
}
|
|
792
792
|
|
|
793
|
-
const today =
|
|
793
|
+
const today = nowIso();
|
|
794
794
|
const targetStatus = lease.oldStatus || 'active';
|
|
795
795
|
|
|
796
796
|
if (dryRun) {
|
package/src/new.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { toRepoPath, die, warn } from './util.mjs';
|
|
3
|
+
import { toRepoPath, die, warn, nowIso } from './util.mjs';
|
|
4
4
|
import { green, dim, bold } from './color.mjs';
|
|
5
5
|
import { isInteractive, promptText } from './prompt.mjs';
|
|
6
6
|
|
|
@@ -11,9 +11,82 @@ const BUILTIN_TEMPLATES = {
|
|
|
11
11
|
body: (t) => `\n# ${t}\n`,
|
|
12
12
|
},
|
|
13
13
|
plan: {
|
|
14
|
-
description: 'Execution plan with
|
|
15
|
-
frontmatter: (s, d) =>
|
|
16
|
-
|
|
14
|
+
description: 'Execution plan — build-up shape (Problem → Phases → Closeout) with phase status markers and Version History',
|
|
15
|
+
frontmatter: (s, d) => [
|
|
16
|
+
'type: plan',
|
|
17
|
+
`status: ${s}`,
|
|
18
|
+
`created: ${d}`,
|
|
19
|
+
`updated: ${d}`,
|
|
20
|
+
'surfaces: []',
|
|
21
|
+
'modules: []',
|
|
22
|
+
'domain:',
|
|
23
|
+
'audience: internal',
|
|
24
|
+
'parent_plan:',
|
|
25
|
+
'related_plans: []',
|
|
26
|
+
'related_docs: []',
|
|
27
|
+
'current_state:',
|
|
28
|
+
'next_step:',
|
|
29
|
+
].join('\n'),
|
|
30
|
+
body: (t, ctx) => `
|
|
31
|
+
# ${t}
|
|
32
|
+
|
|
33
|
+
> One-paragraph problem statement: what this plan is for, why now.
|
|
34
|
+
|
|
35
|
+
## Problem
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## Goals
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Non-Goals
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## What Exists Today
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Constraints
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Decisions
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Open Questions
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Phases
|
|
64
|
+
|
|
65
|
+
<!--
|
|
66
|
+
Status markers (put in heading text):
|
|
67
|
+
⬜ not started
|
|
68
|
+
🟡 in progress (pickup targets this)
|
|
69
|
+
✅ shipped (history; pickup skips)
|
|
70
|
+
⏭ skipped (with reason in body)
|
|
71
|
+
🚧 blocked (link to blocker)
|
|
72
|
+
-->
|
|
73
|
+
|
|
74
|
+
### Phase 1 — <title> ⬜
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## Deferred
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Version History
|
|
83
|
+
|
|
84
|
+
- **${ctx?.today ?? ''}** Created.
|
|
85
|
+
|
|
86
|
+
## Closeout
|
|
87
|
+
|
|
88
|
+
<!-- Filled on archive: what shipped, key commits, deferrals dispositioned. -->
|
|
89
|
+
`,
|
|
17
90
|
},
|
|
18
91
|
adr: {
|
|
19
92
|
description: 'Architecture Decision Record',
|
|
@@ -115,7 +188,7 @@ export async function runNew(argv, config, opts = {}) {
|
|
|
115
188
|
die(`File already exists: ${repoPath}`);
|
|
116
189
|
}
|
|
117
190
|
|
|
118
|
-
const today =
|
|
191
|
+
const today = nowIso();
|
|
119
192
|
|
|
120
193
|
// Generate content
|
|
121
194
|
let content;
|
|
@@ -123,7 +196,7 @@ export async function runNew(argv, config, opts = {}) {
|
|
|
123
196
|
content = template(name, { status, title: docTitle, today });
|
|
124
197
|
} else {
|
|
125
198
|
const fm = template.frontmatter(status, today);
|
|
126
|
-
const body = template.body(docTitle);
|
|
199
|
+
const body = template.body(docTitle, { today, status });
|
|
127
200
|
content = `---\n${fm}\n---\n${body}`;
|
|
128
201
|
}
|
|
129
202
|
|
package/src/util.mjs
CHANGED
|
@@ -55,6 +55,10 @@ export function toRepoPath(absolutePath, repoRoot) {
|
|
|
55
55
|
return path.relative(repoRoot, absolutePath).split(path.sep).join('/');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export function nowIso() {
|
|
59
|
+
return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
60
|
+
}
|
|
61
|
+
|
|
58
62
|
export function warn(message) {
|
|
59
63
|
process.stderr.write(`${dim(message)}\n`);
|
|
60
64
|
}
|