@mmnto/cli 1.15.0 → 1.15.1

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.
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Governance-artifact scaffolding utilities (mmnto/totem#1288).
3
+ *
4
+ * Shared helpers for the `totem proposal new` and `totem adr new` commands.
5
+ * Nothing in this module carries module-level state — every helper takes
6
+ * its context via arguments so the tests can exercise both the submodule
7
+ * (`<totem>/.strategy/`) and the standalone (strategy-repo root) cases.
8
+ */
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import { resolveGitRoot, safeExec, TotemError } from '@mmnto/totem';
12
+ import { log } from '../ui.js';
13
+ const STRATEGY_SUBDIR = '.strategy';
14
+ function targetSubpath(type) {
15
+ return type === 'proposal' ? path.join('proposals', 'active') : 'adr';
16
+ }
17
+ function templateFilename(type) {
18
+ return type === 'proposal' ? 'proposal.md' : 'adr.md';
19
+ }
20
+ /**
21
+ * Resolve governance paths for the current invocation.
22
+ *
23
+ * Two supported contexts:
24
+ * 1. **Submodule case** — `<gitRoot>/.strategy/` exists. Used when Totem is
25
+ * the parent repo and `.strategy/` is a submodule/worktree.
26
+ * 2. **Standalone case** — `<gitRoot>` itself is the strategy repo
27
+ * (`proposals/active/` sits at the repo root). Used when the CLI is run
28
+ * from inside the strategy repo directly.
29
+ *
30
+ * Throws `TotemError` when cwd is not inside a git repo, or when neither
31
+ * layout can be detected.
32
+ */
33
+ export function resolveGovernancePaths(cwd, type) {
34
+ const gitRoot = resolveGitRoot(cwd);
35
+ if (gitRoot === null) {
36
+ throw new TotemError('CONFIG_MISSING', `Not inside a git repository: ${cwd}`, 'Run this command from inside a Totem or Totem-strategy repository checkout.');
37
+ }
38
+ const submoduleRoot = path.join(gitRoot, STRATEGY_SUBDIR);
39
+ const submoduleHasProposals = fs.existsSync(path.join(submoduleRoot, 'proposals'));
40
+ const submoduleHasAdr = fs.existsSync(path.join(submoduleRoot, 'adr'));
41
+ const standaloneHasProposals = fs.existsSync(path.join(gitRoot, 'proposals'));
42
+ const standaloneHasAdr = fs.existsSync(path.join(gitRoot, 'adr'));
43
+ let rootDir;
44
+ if (submoduleHasProposals || submoduleHasAdr) {
45
+ rootDir = submoduleRoot;
46
+ }
47
+ else if (standaloneHasProposals || standaloneHasAdr) {
48
+ rootDir = gitRoot;
49
+ }
50
+ else {
51
+ throw new TotemError('CONFIG_MISSING', `No Totem-strategy layout found under ${gitRoot}.`, 'Expected either a `.strategy/` submodule or top-level `proposals/` and `adr/` directories. Clone or link the strategy repo first.');
52
+ }
53
+ const targetDir = path.join(rootDir, targetSubpath(type));
54
+ const templatePath = path.join(rootDir, 'templates', templateFilename(type));
55
+ const dashboardFile = path.join(rootDir, 'README.md');
56
+ return {
57
+ rootDir: path.normalize(rootDir),
58
+ targetDir: path.normalize(targetDir),
59
+ templatePath: path.normalize(templatePath),
60
+ dashboardFile: path.normalize(dashboardFile),
61
+ };
62
+ }
63
+ // ─── Auto-increment + filename sanitization ─────────────
64
+ const ARTIFACT_FILENAME_RE = /^(\d{3})-(.+)\.md$/;
65
+ const MAX_ARTIFACT_ID = 999;
66
+ /**
67
+ * Scan `targetDir` for `NNN-slug.md` files, parse the prefix to an int,
68
+ * and return `(max + 1)` zero-padded to three digits.
69
+ *
70
+ * Returns `'001'` when the directory is missing or contains no matching
71
+ * files. Files that do not match `^(\d{3})-(.+)\.md$` are ignored so
72
+ * README.md or non-padded prefixes (e.g. `42-x.md`) do not pollute the
73
+ * count. Throws `TotemError` when the next id would exceed 999.
74
+ */
75
+ export function getNextArtifactId(targetDir) {
76
+ if (!fs.existsSync(targetDir)) {
77
+ return '001';
78
+ }
79
+ let highest = 0;
80
+ const entries = fs.readdirSync(targetDir);
81
+ for (const entry of entries) {
82
+ const match = ARTIFACT_FILENAME_RE.exec(entry);
83
+ if (!match)
84
+ continue;
85
+ const parsed = parseInt(match[1], 10);
86
+ if (Number.isFinite(parsed) && parsed > highest) {
87
+ highest = parsed;
88
+ }
89
+ }
90
+ const next = highest + 1;
91
+ if (next > MAX_ARTIFACT_ID) {
92
+ throw new TotemError('CONFIG_INVALID', `NNN-prefix format saturated at ${targetDir} (highest id is ${highest}).`, 'Archive older artifacts or extend the numbering scheme before adding more.');
93
+ }
94
+ return String(next).padStart(3, '0');
95
+ }
96
+ /**
97
+ * Build the final artifact filename from a numeric id and a raw title.
98
+ *
99
+ * Sanitization: lowercase, any non-alphanumeric run becomes a single hyphen,
100
+ * leading/trailing hyphens are stripped. Throws `TotemError` when the
101
+ * sanitized slug is empty — the error fires BEFORE any filesystem write so
102
+ * the caller never strands a half-written artifact.
103
+ */
104
+ export function formatArtifactFilename(id, title) {
105
+ const slug = title
106
+ .toLowerCase()
107
+ .replace(/[^a-z0-9]+/g, '-')
108
+ .replace(/^-+|-+$/g, '');
109
+ if (slug.length === 0) {
110
+ throw new TotemError('CONFIG_INVALID', `Title "${title}" produces an empty slug.`, 'Titles must contain at least one alphanumeric character.');
111
+ }
112
+ return `${id}-${slug}.md`;
113
+ }
114
+ /**
115
+ * Sanitize a raw title for safe inclusion in the scaffolded markdown body.
116
+ *
117
+ * Strips C0 control characters and `DEL` (0x00-0x1F, 0x7F) — most notably
118
+ * newlines, carriage returns, and tabs — then collapses any remaining run
119
+ * of whitespace to a single space and trims the edges. Without this pass,
120
+ * a title like `Fix\n## Fake Heading` would inject a fresh markdown block
121
+ * into the scaffolded artifact, shifting document structure out of the
122
+ * scaffolder's control. The `#` character itself is allowed through on
123
+ * purpose: a single-line heading that contains `#` characters stays a
124
+ * single h1 heading in markdown.
125
+ */
126
+ export function sanitizeArtifactTitle(title) {
127
+ return (title
128
+ // eslint-disable-next-line no-control-regex -- intentional: strip ASCII C0 controls + DEL
129
+ .replace(/[\x00-\x1F\x7F]+/g, ' ')
130
+ .replace(/\s+/g, ' ')
131
+ .trim());
132
+ }
133
+ // ─── Template engine ────────────────────────────────────
134
+ /**
135
+ * Default proposal template. Exported so tests and callers can inspect the
136
+ * baseline shape without re-deriving it. Uses ADR-091's exact heading form
137
+ * (`# Proposal NNN: Title` with a SPACE separator, not a hyphen). Keep in
138
+ * sync with `DEFAULT_ADR_TEMPLATE` below.
139
+ */
140
+ export const DEFAULT_PROPOSAL_TEMPLATE = `# Proposal {{ID}}: {{TITLE}}
141
+
142
+ **Status:** Draft
143
+ **Date:** {{DATE}}
144
+
145
+ ## Problem Statement
146
+
147
+ _Describe the problem this proposal addresses._
148
+
149
+ ## Proposal
150
+
151
+ _Describe the proposed change._
152
+
153
+ ## Alternatives Considered
154
+
155
+ _List alternatives and why they were rejected._
156
+
157
+ ## Impact
158
+
159
+ _Who / what does this affect?_
160
+ `;
161
+ /**
162
+ * Default ADR template. Mirrors `DEFAULT_PROPOSAL_TEMPLATE` but with the
163
+ * `# ADR NNN: Title` heading form required by ADR-091.
164
+ */
165
+ export const DEFAULT_ADR_TEMPLATE = `# ADR {{ID}}: {{TITLE}}
166
+
167
+ **Status:** Draft
168
+ **Date:** {{DATE}}
169
+
170
+ ## Context
171
+
172
+ _Describe the architectural context and forces at play._
173
+
174
+ ## Decision
175
+
176
+ _State the decision._
177
+
178
+ ## Consequences
179
+
180
+ _List the consequences. Note what improves and what regresses._
181
+ `;
182
+ /**
183
+ * Render the artifact template with variable substitution.
184
+ *
185
+ * If `templatePath` exists on disk, its contents are used; otherwise the
186
+ * hardcoded `DEFAULT_PROPOSAL_TEMPLATE` / `DEFAULT_ADR_TEMPLATE` string is
187
+ * used. Substitutes `{{TITLE}}`, `{{DATE}}`, and `{{ID}}` globally.
188
+ *
189
+ * MVP variable set per spec #1288: only `{{TITLE}}` and `{{DATE}}` are
190
+ * user-facing; `{{ID}}` is internal to the default templates so the NNN
191
+ * number lands in the heading without the caller having to splice it.
192
+ */
193
+ export function renderArtifactTemplate(opts) {
194
+ const { type, id, title, templatePath, date } = opts;
195
+ let template;
196
+ if (fs.existsSync(templatePath)) {
197
+ template = fs.readFileSync(templatePath, 'utf-8');
198
+ }
199
+ else {
200
+ template = type === 'proposal' ? DEFAULT_PROPOSAL_TEMPLATE : DEFAULT_ADR_TEMPLATE;
201
+ }
202
+ // Single-pass regex + keyed replacer function. Two reasons:
203
+ // 1. Sequential `.replace()` calls allow template injection (a title of
204
+ // `{{DATE}}` would be substituted into the template and then picked
205
+ // up by the next pass as an actual DATE token). One pass eliminates
206
+ // that class of bug.
207
+ // 2. The replacer-function form avoids `$&` / `$1` back-reference
208
+ // interpretation in the replacement string (see PR #1429 review
209
+ // cycle). A title like `Fix $foo bug` mis-renders otherwise.
210
+ const replacements = { TITLE: title, DATE: date, ID: id };
211
+ return template.replace(/\{\{(TITLE|DATE|ID)\}\}/g, (_match, key) => replacements[key]);
212
+ }
213
+ const defaultExec = (cmd, args, cwd) => {
214
+ safeExec(cmd, args, { cwd });
215
+ };
216
+ /**
217
+ * Run the two post-scaffold side-effects in sequence:
218
+ *
219
+ * 1. `pnpm run docs:inject` (refresh the dashboard index). On non-zero exit
220
+ * or missing script, warn to stderr and continue — the scaffolded file
221
+ * already exists on disk, so a dashboard refresh failure should not
222
+ * strand the artifact.
223
+ * 2. `git add <newFilePath> <dashboardFile>`. Stages ONLY those two paths;
224
+ * never `-A` or `.` (per lesson-8067935e / lesson-4a01b498). On failure,
225
+ * warn and return `staged: false` so the caller can surface the stage
226
+ * state in its user-facing summary.
227
+ *
228
+ * Neither step throws; both failures degrade gracefully so the user always
229
+ * walks away with the new artifact on disk.
230
+ */
231
+ export function runPostScaffoldHooks(opts) {
232
+ const { rootDir, newFilePath, dashboardFile, exec = defaultExec } = opts;
233
+ let dashboardRefreshed = false;
234
+ try {
235
+ exec('pnpm', ['run', 'docs:inject'], rootDir);
236
+ dashboardRefreshed = true; // totem-context: intentional warn-and-continue per spec 1288 — dashboard refresh failure must not strand the scaffolded artifact on disk.
237
+ }
238
+ catch (err) {
239
+ const msg = err instanceof Error ? err.message : String(err);
240
+ log.warn('Totem', `docs:inject did not run cleanly (${msg}). Dashboard not refreshed; run 'pnpm run docs:inject' manually.`);
241
+ }
242
+ let staged = false;
243
+ try {
244
+ exec('git', ['add', newFilePath, dashboardFile], rootDir);
245
+ staged = true; // totem-context: intentional warn-and-continue per spec 1288 — git add failure must not strand the scaffolded artifact on disk.
246
+ }
247
+ catch (err) {
248
+ const msg = err instanceof Error ? err.message : String(err);
249
+ log.warn('Totem', `git add failed (${msg}). File created but not staged; run 'git add' manually.`);
250
+ }
251
+ return { dashboardRefreshed, staged };
252
+ }
253
+ function todayIso() {
254
+ const now = new Date();
255
+ const y = now.getUTCFullYear();
256
+ const m = String(now.getUTCMonth() + 1).padStart(2, '0');
257
+ const d = String(now.getUTCDate()).padStart(2, '0');
258
+ return `${y}-${m}-${d}`;
259
+ }
260
+ /**
261
+ * Full scaffolding pipeline, invoked by both `totem proposal new` and
262
+ * `totem adr new`:
263
+ *
264
+ * resolve paths → compute id → sanitize filename → render template →
265
+ * collision guard → write file → run docs:inject + git add
266
+ *
267
+ * Pre-disk validation (path resolution, id computation, slug sanitization,
268
+ * collision check) happens BEFORE the filesystem is touched so a bad input
269
+ * never strands a half-written artifact.
270
+ */
271
+ export function scaffoldGovernanceArtifact(options, internals = {}) {
272
+ const paths = resolveGovernancePaths(options.cwd, options.type);
273
+ // Sanitize once at the orchestrator entry. Both the filename slug and the
274
+ // template body render against the cleaned form so a title carrying
275
+ // newlines, tabs, or control characters cannot inject markdown blocks
276
+ // into the scaffolded artifact.
277
+ const cleanTitle = sanitizeArtifactTitle(options.title);
278
+ const id = internals.forceId ?? getNextArtifactId(paths.targetDir);
279
+ const filename = formatArtifactFilename(id, cleanTitle);
280
+ const filePath = path.join(paths.targetDir, filename);
281
+ // Collision pre-check gives a clean user-facing error on the common path.
282
+ // The authoritative collision guard is the `flag: 'wx'` on `writeFileSync`
283
+ // below, which closes the TOCTOU race between the pre-check and the write
284
+ // (a concurrent scaffolder could slip in between the two). Keeping both
285
+ // gives a nice error when the file already exists AND safety against the
286
+ // narrow race window.
287
+ if (fs.existsSync(filePath)) {
288
+ throw new TotemError('CONFIG_INVALID', `Artifact already exists at ${filePath}.`, 'Choose a different title, or remove the existing file before re-scaffolding.');
289
+ }
290
+ const date = internals.date ?? todayIso();
291
+ const rendered = renderArtifactTemplate({
292
+ type: options.type,
293
+ id,
294
+ title: cleanTitle,
295
+ templatePath: paths.templatePath,
296
+ date,
297
+ });
298
+ // Ensure parent dir exists (it should, from resolveGovernancePaths, but
299
+ // cheap to defend against a manually-pruned target).
300
+ fs.mkdirSync(paths.targetDir, { recursive: true });
301
+ try {
302
+ // `flag: 'wx'` = write exclusive. Atomic fail with EEXIST if the file
303
+ // was created between the pre-check and now. Closes the TOCTOU race.
304
+ fs.writeFileSync(filePath, rendered, { encoding: 'utf-8', flag: 'wx' });
305
+ }
306
+ catch (err) {
307
+ if (err.code === 'EEXIST') {
308
+ throw new TotemError('CONFIG_INVALID', `Artifact already exists at ${filePath}.`, 'A concurrent scaffolder created this file between the pre-check and the write. Re-run the command to pick up a fresh NNN.');
309
+ }
310
+ throw err;
311
+ }
312
+ const hookResult = runPostScaffoldHooks({
313
+ rootDir: paths.rootDir,
314
+ newFilePath: filePath,
315
+ dashboardFile: paths.dashboardFile,
316
+ exec: internals.exec,
317
+ });
318
+ return {
319
+ id,
320
+ filename,
321
+ filePath,
322
+ dashboardFile: paths.dashboardFile,
323
+ dashboardRefreshed: hookResult.dashboardRefreshed,
324
+ staged: hookResult.staged,
325
+ };
326
+ }
327
+ //# sourceMappingURL=governance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance.js","sourceRoot":"","sources":["../../src/utils/governance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=governance.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"governance.test.d.ts","sourceRoot":"","sources":["../../src/utils/governance.test.ts"],"names":[],"mappings":""}