@mmnto/cli 1.53.6 → 1.53.8
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/dist/commands/doctor-parity.d.ts +18 -0
- package/dist/commands/doctor-parity.d.ts.map +1 -1
- package/dist/commands/doctor-parity.js +18 -8
- package/dist/commands/doctor-parity.js.map +1 -1
- package/dist/commands/doctor-parity.test.js +85 -1
- package/dist/commands/doctor-parity.test.js.map +1 -1
- package/dist/commands/init-doctrine.d.ts +82 -0
- package/dist/commands/init-doctrine.d.ts.map +1 -0
- package/dist/commands/init-doctrine.js +266 -0
- package/dist/commands/init-doctrine.js.map +1 -0
- package/dist/commands/init-doctrine.test.d.ts +2 -0
- package/dist/commands/init-doctrine.test.d.ts.map +1 -0
- package/dist/commands/init-doctrine.test.js +201 -0
- package/dist/commands/init-doctrine.test.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +35 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/exemptions/exemption-schema.d.ts +6 -6
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `totem init --doctrine` — wire the cohort parity manifest into a consumer.
|
|
3
|
+
*
|
|
4
|
+
* Proposal 292 §10 (mmnto-ai/totem-strategy#548), build-slice S1. Under
|
|
5
|
+
* emitter-B the strategy-canonical `parity-manifest.yaml` is distributed as a
|
|
6
|
+
* private package (`@mmnto/strategy-doctrine`, npmjs-private); totem-core's
|
|
7
|
+
* whole job is to point `orient.parityManifest` at the installed pin. The
|
|
8
|
+
* consumer adds the pin dependency explicitly (the opt-in); this command writes
|
|
9
|
+
* ONLY the config pointer — never `package.json`, never the manifest, never the
|
|
10
|
+
* reader (the parity reader / honest-absent SKIP / `configured` self-gate
|
|
11
|
+
* already ship, mmnto-ai/totem#2085). No pin installed ⇒ honest-absent: guide,
|
|
12
|
+
* write nothing, exit 0.
|
|
13
|
+
*
|
|
14
|
+
* **Detection is parse-based, insertion is conservative** (bot review on #2089):
|
|
15
|
+
* "already set" and "does an `orient` block exist" are read from the *parsed*
|
|
16
|
+
* config (`loadConfig`) — ground truth, so comments, strings, quoted keys, and
|
|
17
|
+
* nested objects can never produce a false positive. The only auto-edit is
|
|
18
|
+
* adding a brand-new top-level `orient` field to a canonical `export default {`
|
|
19
|
+
* config; any existing `orient` block or non-canonical shape bails to a
|
|
20
|
+
* format-aware manual snippet rather than risk a wrong edit (Tenet 4).
|
|
21
|
+
*/
|
|
22
|
+
/** The private package carrying the strategy-canonical parity manifest snapshot. */
|
|
23
|
+
export const DOCTRINE_PIN_PACKAGE = '@mmnto/strategy-doctrine';
|
|
24
|
+
/**
|
|
25
|
+
* Repo-root-relative path the config pointer is set to. Resolves through the
|
|
26
|
+
* node_modules pin (pnpm's direct-dep symlink included) the same way the parity
|
|
27
|
+
* reader resolves `orient.parityManifest` — zero-network, riding the install
|
|
28
|
+
* that already ran (Tenet 6).
|
|
29
|
+
*/
|
|
30
|
+
export const DOCTRINE_MANIFEST_RELPATH = `node_modules/${DOCTRINE_PIN_PACKAGE}/parity-manifest.yaml`;
|
|
31
|
+
/**
|
|
32
|
+
* Offset just past the `{` of the SOLE top-level `export default {`, scanning
|
|
33
|
+
* only CODE regions — line/block comments and single/double/backtick strings
|
|
34
|
+
* are skipped so a decoy inside any of them can never be mistaken for the real
|
|
35
|
+
* declaration (GCA + CodeRabbit review on #2089: a regex over raw text leaks
|
|
36
|
+
* comment and template-literal cases). Returns null unless exactly one real
|
|
37
|
+
* declaration exists — zero (a `defineConfig(...)` wrapper) or many bails. Any
|
|
38
|
+
* mis-scan degrades to a bail (never an edit), so it can never corrupt.
|
|
39
|
+
*/
|
|
40
|
+
function findSoleExportDefaultBrace(src) {
|
|
41
|
+
const EXPORT_DEFAULT_RE = /^export\s+default\s*\{/;
|
|
42
|
+
const isWordChar = (ch) => ch !== undefined && /\w/.test(ch);
|
|
43
|
+
const hits = [];
|
|
44
|
+
let i = 0;
|
|
45
|
+
while (i < src.length) {
|
|
46
|
+
const c = src[i];
|
|
47
|
+
const next = src[i + 1];
|
|
48
|
+
if (c === '/' && next === '/') {
|
|
49
|
+
i += 2;
|
|
50
|
+
while (i < src.length && src[i] !== '\n')
|
|
51
|
+
i++;
|
|
52
|
+
}
|
|
53
|
+
else if (c === '/' && next === '*') {
|
|
54
|
+
i += 2;
|
|
55
|
+
while (i < src.length && !(src[i] === '*' && src[i + 1] === '/'))
|
|
56
|
+
i++;
|
|
57
|
+
i += 2;
|
|
58
|
+
}
|
|
59
|
+
else if (c === '"' || c === "'" || c === '`') {
|
|
60
|
+
const quote = c;
|
|
61
|
+
i++;
|
|
62
|
+
while (i < src.length && src[i] !== quote) {
|
|
63
|
+
if (src[i] === '\\')
|
|
64
|
+
i++;
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
i++;
|
|
68
|
+
}
|
|
69
|
+
else if (c === 'e' && !isWordChar(src[i - 1])) {
|
|
70
|
+
const match = EXPORT_DEFAULT_RE.exec(src.slice(i));
|
|
71
|
+
if (match) {
|
|
72
|
+
hits.push(i + match[0].length);
|
|
73
|
+
i += match[0].length;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
i++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return hits.length === 1 ? hits[0] : null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Insert a NEW top-level `orient: { parityManifest }` field just after the sole
|
|
87
|
+
* `export default {` of a TS/JS config. Called ONLY when the parsed config has
|
|
88
|
+
* no `orient` key (detection is parse-based upstream), so there is no merge to
|
|
89
|
+
* perform and no risk of a duplicate key. Conservative by construction: a
|
|
90
|
+
* config without exactly one real (non-string, non-comment) `export default {`
|
|
91
|
+
* declaration bails to `unspliceable` so the caller falls back to the manual
|
|
92
|
+
* snippet. Never a wrong edit (Tenet 4).
|
|
93
|
+
*/
|
|
94
|
+
export function insertTopLevelOrient(configContent, manifestPath) {
|
|
95
|
+
const insertAt = findSoleExportDefaultBrace(configContent);
|
|
96
|
+
if (insertAt === null) {
|
|
97
|
+
return { kind: 'unspliceable' };
|
|
98
|
+
}
|
|
99
|
+
const content = configContent.slice(0, insertAt) +
|
|
100
|
+
`\n orient: { parityManifest: '${manifestPath}' },` +
|
|
101
|
+
configContent.slice(insertAt);
|
|
102
|
+
return { kind: 'written', content };
|
|
103
|
+
}
|
|
104
|
+
/** The single field line to add inside an existing `orient` block, by config format. */
|
|
105
|
+
export function doctrineFieldSnippet(manifestPath, ext) {
|
|
106
|
+
switch (ext) {
|
|
107
|
+
case '.yaml':
|
|
108
|
+
case '.yml':
|
|
109
|
+
return ` parityManifest: ${manifestPath}`;
|
|
110
|
+
case '.toml':
|
|
111
|
+
return `parityManifest = "${manifestPath}"`;
|
|
112
|
+
default:
|
|
113
|
+
return ` parityManifest: '${manifestPath}',`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** The full `orient` block to add at the config's top level, by config format. */
|
|
117
|
+
export function doctrineBlockSnippet(manifestPath, ext) {
|
|
118
|
+
switch (ext) {
|
|
119
|
+
case '.yaml':
|
|
120
|
+
case '.yml':
|
|
121
|
+
return `orient:\n parityManifest: ${manifestPath}`;
|
|
122
|
+
case '.toml':
|
|
123
|
+
return `[orient]\nparityManifest = "${manifestPath}"`;
|
|
124
|
+
default:
|
|
125
|
+
return ` orient: { parityManifest: '${manifestPath}' },`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Detect the doctrine pin and wire (or honest-absent-skip) the config pointer.
|
|
130
|
+
* Takes `cwd` (and an optional `homeDir`) explicitly so it is testable without
|
|
131
|
+
* `process.chdir`, mirroring the parity reader's cwd-in signature.
|
|
132
|
+
*/
|
|
133
|
+
export async function wireDoctrineManifest(cwd, homeDir) {
|
|
134
|
+
const fs = await import('node:fs');
|
|
135
|
+
const path = await import('node:path');
|
|
136
|
+
// Honest-absent: no pin installed ⇒ nothing to wire. `existsSync` follows the
|
|
137
|
+
// pnpm direct-dep symlink, so this is true once the consumer adds the dep.
|
|
138
|
+
const manifestAbs = path.join(cwd, DOCTRINE_MANIFEST_RELPATH);
|
|
139
|
+
if (!fs.existsSync(manifestAbs)) {
|
|
140
|
+
return { kind: 'pin-absent', manifestPath: DOCTRINE_MANIFEST_RELPATH };
|
|
141
|
+
}
|
|
142
|
+
const { resolveConfigPath, isGlobalConfigPath, loadConfig } = await import('../utils.js');
|
|
143
|
+
const { TotemConfigError, TotemError } = await import('@mmnto/totem');
|
|
144
|
+
let configPath;
|
|
145
|
+
try {
|
|
146
|
+
configPath = resolveConfigPath(cwd, homeDir);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
// resolveConfigPath throws TotemConfigError only when no config exists
|
|
150
|
+
// anywhere — that is this command's "run totem init first". Anything else
|
|
151
|
+
// is unexpected and must propagate (Tenet 4 — never swallow it).
|
|
152
|
+
if (err instanceof TotemConfigError) {
|
|
153
|
+
return { kind: 'no-config' };
|
|
154
|
+
}
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
// Per-repo only: the reader never reads a global `parityManifest`
|
|
158
|
+
// (doctor-parity's repo-scoped guard), so a global-only profile has no repo
|
|
159
|
+
// config to wire into.
|
|
160
|
+
if (isGlobalConfigPath(configPath, homeDir)) {
|
|
161
|
+
return { kind: 'global-only' };
|
|
162
|
+
}
|
|
163
|
+
// Ground-truth detection: parse the config so comments, strings, quoted keys,
|
|
164
|
+
// and nested objects can never produce a false `already-set` or a phantom
|
|
165
|
+
// `orient` (a no-AST text scan cannot see semantics — bot review on #2089).
|
|
166
|
+
let parsed;
|
|
167
|
+
try {
|
|
168
|
+
parsed = await loadConfig(configPath);
|
|
169
|
+
}
|
|
170
|
+
catch (cause) {
|
|
171
|
+
throw new TotemError('CONFIG_INVALID', `Could not parse the Totem config at ${configPath}.`, 'Fix the config syntax, then re-run `totem init --doctrine`.', cause);
|
|
172
|
+
}
|
|
173
|
+
// Trim: a blank / whitespace-only placeholder is effectively unset
|
|
174
|
+
// (CodeRabbit review on #2089) — don't silently no-op on `parityManifest: ''`.
|
|
175
|
+
if (parsed.orient?.parityManifest?.trim()) {
|
|
176
|
+
return { kind: 'already-set', configPath };
|
|
177
|
+
}
|
|
178
|
+
const ext = path.extname(configPath).toLowerCase();
|
|
179
|
+
// An existing `orient` block: do NOT risk an in-place merge of arbitrary
|
|
180
|
+
// config text (the fragile case the bots flagged) — surface the one line to
|
|
181
|
+
// add. The common consumer (no `orient` yet) takes the auto path below.
|
|
182
|
+
if (parsed.orient !== undefined) {
|
|
183
|
+
return {
|
|
184
|
+
kind: 'manual',
|
|
185
|
+
configPath,
|
|
186
|
+
reason: 'orient-exists',
|
|
187
|
+
snippet: doctrineFieldSnippet(DOCTRINE_MANIFEST_RELPATH, ext),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// The auto-insert understands TS/JS object syntax only. Non-JS configs
|
|
191
|
+
// (YAML/TOML) go straight to the format-aware manual snippet — we never run
|
|
192
|
+
// the JS scanner over them.
|
|
193
|
+
const JS_CONFIG_EXTS = new Set(['.ts', '.js', '.mts', '.cts', '.mjs', '.cjs']);
|
|
194
|
+
if (!JS_CONFIG_EXTS.has(ext)) {
|
|
195
|
+
return {
|
|
196
|
+
kind: 'manual',
|
|
197
|
+
configPath,
|
|
198
|
+
reason: 'no-splice-point',
|
|
199
|
+
snippet: doctrineBlockSnippet(DOCTRINE_MANIFEST_RELPATH, ext),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
let content;
|
|
203
|
+
try {
|
|
204
|
+
content = await fs.promises.readFile(configPath, 'utf-8');
|
|
205
|
+
}
|
|
206
|
+
catch (cause) {
|
|
207
|
+
throw new TotemError('CONFIG_INVALID', `Failed to read the Totem config at ${configPath}.`, 'Ensure the file exists and is readable.', cause);
|
|
208
|
+
}
|
|
209
|
+
const result = insertTopLevelOrient(content, DOCTRINE_MANIFEST_RELPATH);
|
|
210
|
+
const candidate = result.kind === 'written' ? result.content : null;
|
|
211
|
+
// Validate-by-reparse: the scanner is only a heuristic for WHERE to splice;
|
|
212
|
+
// the real parser is the safety net. Confirm the candidate re-parses with the
|
|
213
|
+
// intended value before writing, so ANY scanner edge case (a regex literal, a
|
|
214
|
+
// division operator, etc.) degrades to a manual bail instead of a corrupt
|
|
215
|
+
// write. Corruption is impossible by construction (GCA review on #2089).
|
|
216
|
+
if (candidate === null || !(await reparseConfirms(candidate, configPath))) {
|
|
217
|
+
return {
|
|
218
|
+
kind: 'manual',
|
|
219
|
+
configPath,
|
|
220
|
+
reason: 'no-splice-point',
|
|
221
|
+
snippet: doctrineBlockSnippet(DOCTRINE_MANIFEST_RELPATH, ext),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
await fs.promises.writeFile(configPath, candidate, 'utf-8');
|
|
226
|
+
}
|
|
227
|
+
catch (cause) {
|
|
228
|
+
throw new TotemError('CONFIG_INVALID', `Failed to write the updated Totem config at ${configPath}.`, 'Ensure you have write permission for this file.', cause);
|
|
229
|
+
}
|
|
230
|
+
return { kind: 'written', configPath, manifestPath: DOCTRINE_MANIFEST_RELPATH };
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Write the candidate config to a throwaway sibling temp file, parse it with the
|
|
234
|
+
* real loader, and confirm `orient.parityManifest` landed as intended. A sibling
|
|
235
|
+
* (same dir + extension) preserves the consumer's relative imports / module
|
|
236
|
+
* resolution so the parse is faithful. Any parse failure or value mismatch ⇒
|
|
237
|
+
* `false` (the caller bails to the manual snippet) — so a scanner mis-splice can
|
|
238
|
+
* never reach disk. Best-effort temp cleanup either way.
|
|
239
|
+
*/
|
|
240
|
+
async function reparseConfirms(candidate, configPath) {
|
|
241
|
+
const fs = await import('node:fs');
|
|
242
|
+
const path = await import('node:path');
|
|
243
|
+
const { loadConfig } = await import('../utils.js');
|
|
244
|
+
const dir = path.dirname(configPath);
|
|
245
|
+
const ext = path.extname(configPath);
|
|
246
|
+
const tmp = path.join(dir, `.totem-doctrine-candidate-${process.pid}-${Date.now()}-probe${ext}`);
|
|
247
|
+
let ok = false;
|
|
248
|
+
try {
|
|
249
|
+
await fs.promises.writeFile(tmp, candidate, 'utf-8');
|
|
250
|
+
const reparsed = await loadConfig(tmp);
|
|
251
|
+
ok = reparsed.orient?.parityManifest === DOCTRINE_MANIFEST_RELPATH;
|
|
252
|
+
// totem-context: a candidate that throws on re-parse is an unverified edit (bail to the manual snippet), not an error to surface
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
ok = false;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
await fs.promises.unlink(tmp);
|
|
259
|
+
// totem-context: best-effort cleanup of the throwaway candidate temp file
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// a leftover dotfile temp is harmless
|
|
263
|
+
}
|
|
264
|
+
return ok;
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=init-doctrine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-doctrine.js","sourceRoot":"","sources":["../../src/commands/init-doctrine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-doctrine.test.d.ts","sourceRoot":"","sources":["../../src/commands/init-doctrine.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { cleanTmpDir } from '../test-utils.js';
|
|
6
|
+
import { DOCTRINE_MANIFEST_RELPATH, DOCTRINE_PIN_PACKAGE, doctrineBlockSnippet, doctrineFieldSnippet, insertTopLevelOrient, wireDoctrineManifest, } from './init-doctrine.js';
|
|
7
|
+
const PATH = DOCTRINE_MANIFEST_RELPATH;
|
|
8
|
+
/** A minimal, loadConfig-valid config body (targets is the only required field). */
|
|
9
|
+
function configBody(extra = '') {
|
|
10
|
+
return `export default {\n targets: [{ glob: '.totem/lessons/*.md', type: 'lesson', strategy: 'markdown-heading' }],${extra}\n};\n`;
|
|
11
|
+
}
|
|
12
|
+
function bracesBalanced(s) {
|
|
13
|
+
let depth = 0;
|
|
14
|
+
for (const ch of s) {
|
|
15
|
+
if (ch === '{')
|
|
16
|
+
depth++;
|
|
17
|
+
else if (ch === '}')
|
|
18
|
+
depth--;
|
|
19
|
+
if (depth < 0)
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return depth === 0;
|
|
23
|
+
}
|
|
24
|
+
describe('insertTopLevelOrient (pure)', () => {
|
|
25
|
+
it('inserts a top-level orient field after the sole export default {', () => {
|
|
26
|
+
const result = insertTopLevelOrient(configBody(), PATH);
|
|
27
|
+
expect(result.kind).toBe('written');
|
|
28
|
+
if (result.kind !== 'written')
|
|
29
|
+
return;
|
|
30
|
+
expect(result.content).toContain(`orient: { parityManifest: '${PATH}' }`);
|
|
31
|
+
expect(result.content).toContain('.totem/lessons/*.md'); // untouched
|
|
32
|
+
expect(bracesBalanced(result.content)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('bails (unspliceable) on a non-canonical export (defineConfig wrapper)', () => {
|
|
35
|
+
const cfg = `import { defineConfig } from '@mmnto/totem';\nexport default defineConfig({ targets: [] });\n`;
|
|
36
|
+
expect(insertTopLevelOrient(cfg, PATH).kind).toBe('unspliceable');
|
|
37
|
+
});
|
|
38
|
+
it('ignores a decoy export default { in a comment and inserts via the real export', () => {
|
|
39
|
+
const cfg = `// export default { fake: true }\n${configBody()}`;
|
|
40
|
+
const result = insertTopLevelOrient(cfg, PATH);
|
|
41
|
+
expect(result.kind).toBe('written');
|
|
42
|
+
if (result.kind !== 'written')
|
|
43
|
+
return;
|
|
44
|
+
expect(result.content).toContain(`orient: { parityManifest: '${PATH}' }`);
|
|
45
|
+
expect(result.content).toContain('// export default { fake: true }'); // comment preserved
|
|
46
|
+
});
|
|
47
|
+
it('never inserts into a comment when the only export default { is commented out', () => {
|
|
48
|
+
// Real export is a non-canonical defineConfig form; the sole literal
|
|
49
|
+
// "export default {" lives in a comment — must bail, not corrupt it (GCA).
|
|
50
|
+
const cfg = `// export default { legacy: true }\nexport default defineConfig({ targets: [] });\n`;
|
|
51
|
+
expect(insertTopLevelOrient(cfg, PATH).kind).toBe('unspliceable');
|
|
52
|
+
});
|
|
53
|
+
it('never splices into a multiline template-literal decoy', () => {
|
|
54
|
+
// The only literal "export default {" lives inside a template string; the
|
|
55
|
+
// real export is a defineConfig form — must bail, not corrupt the string.
|
|
56
|
+
const cfg = 'const decoy = `\nexport default {\n`;\nexport default defineConfig({ targets: [] });\n';
|
|
57
|
+
expect(insertTopLevelOrient(cfg, PATH).kind).toBe('unspliceable');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('doctrine snippets (format-aware)', () => {
|
|
61
|
+
it('field snippet renders per format', () => {
|
|
62
|
+
expect(doctrineFieldSnippet(PATH, '.ts')).toBe(` parityManifest: '${PATH}',`);
|
|
63
|
+
expect(doctrineFieldSnippet(PATH, '.yaml')).toBe(` parityManifest: ${PATH}`);
|
|
64
|
+
expect(doctrineFieldSnippet(PATH, '.toml')).toBe(`parityManifest = "${PATH}"`);
|
|
65
|
+
});
|
|
66
|
+
it('block snippet renders per format', () => {
|
|
67
|
+
expect(doctrineBlockSnippet(PATH, '.ts')).toBe(` orient: { parityManifest: '${PATH}' },`);
|
|
68
|
+
expect(doctrineBlockSnippet(PATH, '.yml')).toBe(`orient:\n parityManifest: ${PATH}`);
|
|
69
|
+
expect(doctrineBlockSnippet(PATH, '.toml')).toBe(`[orient]\nparityManifest = "${PATH}"`);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('wireDoctrineManifest (real loadConfig)', () => {
|
|
73
|
+
let tmpDir;
|
|
74
|
+
let homeDir;
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-doctrine-'));
|
|
77
|
+
homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-doctrine-home-'));
|
|
78
|
+
});
|
|
79
|
+
afterEach(() => {
|
|
80
|
+
cleanTmpDir(tmpDir);
|
|
81
|
+
cleanTmpDir(homeDir);
|
|
82
|
+
});
|
|
83
|
+
function installPin(root) {
|
|
84
|
+
const pinDir = path.join(root, 'node_modules', ...DOCTRINE_PIN_PACKAGE.split('/'));
|
|
85
|
+
fs.mkdirSync(pinDir, { recursive: true });
|
|
86
|
+
fs.writeFileSync(path.join(pinDir, 'parity-manifest.yaml'), 'schema-version: 1\n', 'utf-8');
|
|
87
|
+
}
|
|
88
|
+
function writeConfig(body) {
|
|
89
|
+
const p = path.join(tmpDir, 'totem.config.ts');
|
|
90
|
+
fs.writeFileSync(p, body, 'utf-8');
|
|
91
|
+
return p;
|
|
92
|
+
}
|
|
93
|
+
it('honest-absent: pin not installed → pin-absent, config byte-unchanged', async () => {
|
|
94
|
+
const p = writeConfig(configBody());
|
|
95
|
+
const before = fs.readFileSync(p, 'utf-8');
|
|
96
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
97
|
+
expect(outcome.kind).toBe('pin-absent');
|
|
98
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(before);
|
|
99
|
+
});
|
|
100
|
+
it('writes a top-level orient pointer when none exists', async () => {
|
|
101
|
+
installPin(tmpDir);
|
|
102
|
+
const p = writeConfig(configBody());
|
|
103
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
104
|
+
expect(outcome.kind).toBe('written');
|
|
105
|
+
const written = fs.readFileSync(p, 'utf-8');
|
|
106
|
+
expect(written).toContain(`parityManifest: '${PATH}'`);
|
|
107
|
+
expect(written).toContain('.totem/lessons/*.md'); // untouched
|
|
108
|
+
});
|
|
109
|
+
it('is already-set (parse-based) when orient.parityManifest is configured', async () => {
|
|
110
|
+
installPin(tmpDir);
|
|
111
|
+
const p = writeConfig(configBody(`\n orient: { parityManifest: '${PATH}' },`));
|
|
112
|
+
const before = fs.readFileSync(p, 'utf-8');
|
|
113
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
114
|
+
expect(outcome.kind).toBe('already-set');
|
|
115
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(before); // no rewrite
|
|
116
|
+
});
|
|
117
|
+
it('bails to a manual field snippet (no clobber) when an orient block already exists', async () => {
|
|
118
|
+
installPin(tmpDir);
|
|
119
|
+
const p = writeConfig(configBody('\n orient: { projectNumber: 7 },'));
|
|
120
|
+
const before = fs.readFileSync(p, 'utf-8');
|
|
121
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
122
|
+
expect(outcome.kind).toBe('manual');
|
|
123
|
+
if (outcome.kind === 'manual')
|
|
124
|
+
expect(outcome.reason).toBe('orient-exists');
|
|
125
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(before); // never edits an existing orient
|
|
126
|
+
});
|
|
127
|
+
it('errors honestly when no config exists (no-config)', async () => {
|
|
128
|
+
installPin(tmpDir);
|
|
129
|
+
expect((await wireDoctrineManifest(tmpDir, homeDir)).kind).toBe('no-config');
|
|
130
|
+
});
|
|
131
|
+
it('refuses to wire a per-repo setting into a global-only profile', async () => {
|
|
132
|
+
installPin(tmpDir);
|
|
133
|
+
const globalTotem = path.join(homeDir, '.totem');
|
|
134
|
+
fs.mkdirSync(globalTotem, { recursive: true });
|
|
135
|
+
fs.writeFileSync(path.join(globalTotem, 'totem.config.ts'), configBody(), 'utf-8');
|
|
136
|
+
expect((await wireDoctrineManifest(tmpDir, homeDir)).kind).toBe('global-only');
|
|
137
|
+
});
|
|
138
|
+
// ── bot-fix proofs (#2089 review): parse-based detection sees through text ──
|
|
139
|
+
it('ignores a commented-out orient block — writes the REAL orient (Greptile P1)', async () => {
|
|
140
|
+
installPin(tmpDir);
|
|
141
|
+
const p = writeConfig(`${configBody()}// orient: { projectNumber: 7 }\n`);
|
|
142
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
143
|
+
expect(outcome.kind).toBe('written');
|
|
144
|
+
const written = fs.readFileSync(p, 'utf-8');
|
|
145
|
+
expect(written).toContain(`orient: { parityManifest: '${PATH}' }`); // real orient inserted
|
|
146
|
+
expect(written).toContain('// orient: { projectNumber: 7 }'); // comment preserved, never parsed
|
|
147
|
+
});
|
|
148
|
+
it('does not false-positive already-set on a parityManifest mention in a comment (Greptile P2)', async () => {
|
|
149
|
+
installPin(tmpDir);
|
|
150
|
+
const p = writeConfig(`${configBody()}// TODO: set parityManifest once doctrine ships\n`);
|
|
151
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
152
|
+
expect(outcome.kind).toBe('written');
|
|
153
|
+
expect(fs.readFileSync(p, 'utf-8')).toContain(`parityManifest: '${PATH}'`);
|
|
154
|
+
});
|
|
155
|
+
it('treats a quoted orient key as an existing block — no duplicate key (GCA high)', async () => {
|
|
156
|
+
installPin(tmpDir);
|
|
157
|
+
const body = configBody(`\n 'orient': { projectNumber: 7 },`);
|
|
158
|
+
const p = writeConfig(body);
|
|
159
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
160
|
+
expect(outcome.kind).toBe('manual'); // not a second top-level orient
|
|
161
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(body);
|
|
162
|
+
});
|
|
163
|
+
it('throws a TotemError (with cause) when the config cannot be parsed', async () => {
|
|
164
|
+
installPin(tmpDir);
|
|
165
|
+
writeConfig('export default { targets: [\n'); // syntax error
|
|
166
|
+
await expect(wireDoctrineManifest(tmpDir, homeDir)).rejects.toThrow(/parse/i);
|
|
167
|
+
});
|
|
168
|
+
it('bails to a format-aware block for a non-JS (YAML) config — no auto-edit', async () => {
|
|
169
|
+
installPin(tmpDir);
|
|
170
|
+
const yamlBody = 'targets:\n - glob: ".totem/lessons/*.md"\n type: lesson\n strategy: markdown-heading\n';
|
|
171
|
+
const p = path.join(tmpDir, 'totem.yaml');
|
|
172
|
+
fs.writeFileSync(p, yamlBody, 'utf-8');
|
|
173
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
174
|
+
expect(outcome.kind).toBe('manual');
|
|
175
|
+
if (outcome.kind === 'manual') {
|
|
176
|
+
expect(outcome.reason).toBe('no-splice-point');
|
|
177
|
+
expect(outcome.snippet).toBe(`orient:\n parityManifest: ${PATH}`); // YAML form
|
|
178
|
+
}
|
|
179
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(yamlBody); // never edited
|
|
180
|
+
});
|
|
181
|
+
it('treats a blank parityManifest as unset, not already-set (CR)', async () => {
|
|
182
|
+
installPin(tmpDir);
|
|
183
|
+
const body = configBody(`\n orient: { parityManifest: ' ' },`);
|
|
184
|
+
const p = writeConfig(body);
|
|
185
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
186
|
+
expect(outcome.kind).not.toBe('already-set'); // blank ≠ configured → no silent no-op
|
|
187
|
+
expect(outcome.kind).toBe('manual'); // orient block exists → guided, not auto-merged
|
|
188
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(body);
|
|
189
|
+
});
|
|
190
|
+
it('reparse safety net: bails (no corrupt write) when the scanner would splice into a regex literal', async () => {
|
|
191
|
+
installPin(tmpDir);
|
|
192
|
+
const body = 'const defineConfig = (c) => c;\n' +
|
|
193
|
+
'const re = /export default {/;\n' +
|
|
194
|
+
"export default defineConfig({ targets: [{ glob: '.totem/lessons/*.md', type: 'lesson', strategy: 'markdown-heading' }] });\n";
|
|
195
|
+
const p = writeConfig(body);
|
|
196
|
+
const outcome = await wireDoctrineManifest(tmpDir, homeDir);
|
|
197
|
+
expect(outcome.kind).toBe('manual'); // scanner false-matched the regex; reparse caught it
|
|
198
|
+
expect(fs.readFileSync(p, 'utf-8')).toBe(body); // config never corrupted
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
//# sourceMappingURL=init-doctrine.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-doctrine.test.js","sourceRoot":"","sources":["../../src/commands/init-doctrine.test.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -111,6 +111,10 @@ export declare function initCommand(options?: {
|
|
|
111
111
|
* the literal `all`. Routes through the SAME `installGates` path the
|
|
112
112
|
* `gate install` verb uses — no second copy of the merge logic. */
|
|
113
113
|
gates?: string;
|
|
114
|
+
/** Wire `orient.parityManifest` to the installed doctrine pin and exit
|
|
115
|
+
* (mmnto-ai/totem#2088, Proposal 292 S1). Non-interactive; honest-absent
|
|
116
|
+
* when the `@mmnto/strategy-doctrine` pin is not installed. */
|
|
117
|
+
doctrine?: boolean;
|
|
114
118
|
/** Override home directory for testing. */
|
|
115
119
|
_homeDir?: string;
|
|
116
120
|
}): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,SAAS,EAGf,MAAM,kBAAkB,CAAC;AAuB1B,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAQ7B,eAAO,MAAM,6BAA6B,2BAA2B,CAAC;AAItE,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAsBD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAA0B,GACjC;IAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAqB5D;AAsED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAIrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAI3E;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAI5E;AA0GD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,EACxB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B;IACD,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;IAC5D;;+CAE2C;IAC3C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAwDA;AAUD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC;IAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAoD5D;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,OAAO,wBAAwB,EAAE,SAAS,EAC9C,UAAU,CAAC,EAAE,SAAS,EAAE,GACvB,OAAO,CAAC,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC,CAmE7C;AAID,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAE9D,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAchE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CA+BpF;AAgCD,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;8EAG0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;wEAGoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/init.js
CHANGED
|
@@ -487,6 +487,41 @@ function applyReflexUpgrade(filePath) {
|
|
|
487
487
|
return clean;
|
|
488
488
|
}
|
|
489
489
|
export async function initCommand(options) {
|
|
490
|
+
// ─── Doctrine pin wiring (mmnto-ai/totem#2088, Proposal 292 S1) ───
|
|
491
|
+
// Self-contained non-interactive path: point orient.parityManifest at the
|
|
492
|
+
// installed @mmnto/strategy-doctrine pin so `totem doctor --parity` stops
|
|
493
|
+
// honest-absent-SKIPping. Mirrors the --global early-return shape.
|
|
494
|
+
if (options?.doctrine) {
|
|
495
|
+
const { log } = await import('../ui.js');
|
|
496
|
+
const { TotemConfigError } = await import('@mmnto/totem');
|
|
497
|
+
const { DOCTRINE_PIN_PACKAGE, wireDoctrineManifest } = await import('./init-doctrine.js');
|
|
498
|
+
const outcome = await wireDoctrineManifest(process.cwd(), options._homeDir);
|
|
499
|
+
switch (outcome.kind) {
|
|
500
|
+
case 'pin-absent':
|
|
501
|
+
log.warn('Totem', `Doctrine pin ${DOCTRINE_PIN_PACKAGE} is not installed (looked for ${outcome.manifestPath}).`);
|
|
502
|
+
log.info('Totem', 'Add it as a dependency, then re-run `totem init --doctrine`. Until then `totem doctor --parity` stays an honest skip.');
|
|
503
|
+
return;
|
|
504
|
+
case 'no-config':
|
|
505
|
+
throw new TotemConfigError('No Totem configuration found in this repo.', 'Run `totem init` first, then `totem init --doctrine`.', 'CONFIG_MISSING');
|
|
506
|
+
case 'global-only':
|
|
507
|
+
throw new TotemConfigError('Only a global ~/.totem profile was found — the parity manifest is a per-repo setting.', 'Run `totem init` in this repo first, then `totem init --doctrine`.', 'CONFIG_MISSING');
|
|
508
|
+
case 'already-set':
|
|
509
|
+
log.info('Totem', `orient.parityManifest already configured in ${outcome.configPath}. Nothing to do.`);
|
|
510
|
+
return;
|
|
511
|
+
case 'manual': {
|
|
512
|
+
const where = outcome.reason === 'orient-exists'
|
|
513
|
+
? 'You already have an `orient` block — add this line inside it:'
|
|
514
|
+
: 'Could not safely auto-edit this config — add this manually:';
|
|
515
|
+
log.warn('Totem', `Could not auto-wire orient.parityManifest in ${outcome.configPath}.`);
|
|
516
|
+
log.info('Totem', `${where}\n${outcome.snippet}`);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
case 'written':
|
|
520
|
+
log.success('Totem', `Wired orient.parityManifest → ${outcome.manifestPath} in ${outcome.configPath}.`);
|
|
521
|
+
log.dim('Totem', 'Run `totem doctor --parity` to sense cohort drift.');
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
490
525
|
// ─── Global profile shortcut ───────────────────────
|
|
491
526
|
// totem-context: fs and path are static imports at top of file (lines 1-2)
|
|
492
527
|
if (options?.global) {
|