@mmnto/cli 1.17.0 → 1.18.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.
Files changed (51) hide show
  1. package/dist/commands/adr.d.ts.map +1 -1
  2. package/dist/commands/adr.js +3 -1
  3. package/dist/commands/adr.js.map +1 -1
  4. package/dist/commands/adr.test.js +2 -0
  5. package/dist/commands/adr.test.js.map +1 -1
  6. package/dist/commands/doctor.d.ts +19 -0
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/doctor.js +63 -4
  9. package/dist/commands/doctor.js.map +1 -1
  10. package/dist/commands/doctor.test.js +62 -2
  11. package/dist/commands/doctor.test.js.map +1 -1
  12. package/dist/commands/proposal.d.ts.map +1 -1
  13. package/dist/commands/proposal.js +3 -1
  14. package/dist/commands/proposal.js.map +1 -1
  15. package/dist/commands/proposal.test.js +1 -0
  16. package/dist/commands/proposal.test.js.map +1 -1
  17. package/dist/commands/retrospect.d.ts.map +1 -1
  18. package/dist/commands/retrospect.js +10 -21
  19. package/dist/commands/retrospect.js.map +1 -1
  20. package/dist/commands/retrospect.test.js +12 -5
  21. package/dist/commands/retrospect.test.js.map +1 -1
  22. package/dist/commands/shield-estimate.d.ts +8 -0
  23. package/dist/commands/shield-estimate.d.ts.map +1 -1
  24. package/dist/commands/shield-estimate.js +224 -0
  25. package/dist/commands/shield-estimate.js.map +1 -1
  26. package/dist/commands/shield-estimate.test.js +570 -0
  27. package/dist/commands/shield-estimate.test.js.map +1 -1
  28. package/dist/commands/shield.d.ts +8 -0
  29. package/dist/commands/shield.d.ts.map +1 -1
  30. package/dist/commands/shield.js.map +1 -1
  31. package/dist/index.js +9 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/terminal-sanitize.d.ts +28 -0
  34. package/dist/terminal-sanitize.d.ts.map +1 -0
  35. package/dist/terminal-sanitize.js +36 -0
  36. package/dist/terminal-sanitize.js.map +1 -0
  37. package/dist/terminal-sanitize.test.d.ts +2 -0
  38. package/dist/terminal-sanitize.test.d.ts.map +1 -0
  39. package/dist/terminal-sanitize.test.js +46 -0
  40. package/dist/terminal-sanitize.test.js.map +1 -0
  41. package/dist/utils/governance.d.ts +38 -9
  42. package/dist/utils/governance.d.ts.map +1 -1
  43. package/dist/utils/governance.js +75 -23
  44. package/dist/utils/governance.js.map +1 -1
  45. package/dist/utils/governance.test.js +124 -1
  46. package/dist/utils/governance.test.js.map +1 -1
  47. package/dist/utils.d.ts +1 -0
  48. package/dist/utils.d.ts.map +1 -1
  49. package/dist/utils.js +6 -0
  50. package/dist/utils.js.map +1 -1
  51. package/package.json +2 -2
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { sanitizeForTerminal } from './terminal-sanitize.js';
3
+ describe('sanitizeForTerminal', () => {
4
+ it('strips CSI escape sequences (ESC [ … final byte)', () => {
5
+ expect(sanitizeForTerminal('\x1b[31mred\x1b[0m')).toBe('red');
6
+ expect(sanitizeForTerminal('a\x1b[1;33mb\x1b[mc')).toBe('abc');
7
+ });
8
+ it('replaces C0 control bytes with a space, preserving \\n and \\t', () => {
9
+ // \x07 BEL, \x08 BS, \x0b VT, \x0c FF, \x0e SO, \x1f US — all should become spaces.
10
+ expect(sanitizeForTerminal('a\x07b\x08c\x0bd\x0ce\x0ef\x1fg')).toBe('a b c d e f g');
11
+ // \n and \t survive (caller may collapse later).
12
+ expect(sanitizeForTerminal('line\n\twith\twhitespace')).toBe('line\n\twith\twhitespace');
13
+ // \x7f DEL also strips.
14
+ expect(sanitizeForTerminal('del\x7fhere')).toBe('del here');
15
+ });
16
+ it('strips \\r (CR) — a bare CR rewinds the terminal cursor', () => {
17
+ // CR mmnto-ai/totem#1739 R3 Critical: \r alone overwrites the current
18
+ // terminal line; preserving it would defeat the sanitizer's whole
19
+ // purpose. \n still survives.
20
+ expect(sanitizeForTerminal('safe\rrewound')).toBe('safe rewound');
21
+ expect(sanitizeForTerminal('safe\r\nwindows')).toBe('safe \nwindows');
22
+ // No CR survives anywhere in the output.
23
+ expect(sanitizeForTerminal('a\rb\rc\rd')).not.toContain('\r');
24
+ });
25
+ it('replaces C1 control bytes (\\x80-\\x9f) with a space — closes the original \\x7f gap', () => {
26
+ // \x9b is the 8-bit CSI introducer — directly equivalent to ESC [ on
27
+ // 8-bit-clean terminals. CR mmnto-ai/totem#1739 R2 caught the original
28
+ // regex stopped at \x7f leaving the C1 range open.
29
+ expect(sanitizeForTerminal('csi-8bit\x9bsequence')).toBe('csi-8bit sequence');
30
+ // Sweep the whole C1 range.
31
+ for (let cp = 0x80; cp <= 0x9f; cp += 1) {
32
+ const ctrl = String.fromCharCode(cp);
33
+ const out = sanitizeForTerminal(`a${ctrl}b`);
34
+ expect(out).toBe('a b');
35
+ }
36
+ });
37
+ it('passes printable text through unchanged', () => {
38
+ expect(sanitizeForTerminal('plain text 123 !@#$%^&*()')).toBe('plain text 123 !@#$%^&*()');
39
+ // Unicode above C1 is preserved.
40
+ expect(sanitizeForTerminal('café — naïve')).toBe('café — naïve');
41
+ });
42
+ it('returns the empty string unchanged', () => {
43
+ expect(sanitizeForTerminal('')).toBe('');
44
+ });
45
+ });
46
+ //# sourceMappingURL=terminal-sanitize.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-sanitize.test.js","sourceRoot":"","sources":["../src/terminal-sanitize.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,mBAAmB,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9D,MAAM,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,oFAAoF;QACpF,MAAM,CAAC,mBAAmB,CAAC,iCAAiC,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACrF,iDAAiD;QACjD,MAAM,CAAC,mBAAmB,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACzF,wBAAwB;QACxB,MAAM,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,sEAAsE;QACtE,kEAAkE;QAClE,8BAA8B;QAC9B,MAAM,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClE,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACtE,yCAAyC;QACzC,MAAM,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,qEAAqE;QACrE,uEAAuE;QACvE,mDAAmD;QACnD,MAAM,CAAC,mBAAmB,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC9E,4BAA4B;QAC5B,KAAK,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;YAC7C,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,mBAAmB,CAAC,2BAA2B,CAAC,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC3F,iCAAiC;QACjC,MAAM,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -6,11 +6,18 @@
6
6
  * its context via arguments so the tests can exercise both the submodule
7
7
  * (`<totem>/.strategy/`) and the standalone (strategy-repo root) cases.
8
8
  */
9
+ import { type StrategyResolverConfig } from '@mmnto/totem';
9
10
  export type GovernanceType = 'proposal' | 'adr';
10
11
  export interface ScaffoldOptions {
11
12
  type: GovernanceType;
12
13
  title: string;
13
14
  cwd: string;
15
+ /**
16
+ * Loaded `TotemConfig` (or any object with a `strategyRoot?: string` field).
17
+ * Forwarded to the strategy-root resolver so `TotemConfig.strategyRoot`
18
+ * wins precedence-2 over the sibling / submodule layers (mmnto-ai/totem#1710).
19
+ */
20
+ config?: StrategyResolverConfig;
14
21
  }
15
22
  export interface GovernancePaths {
16
23
  /** Directory that anchors the governance layout (strategy repo root or submodule root). */
@@ -22,20 +29,42 @@ export interface GovernancePaths {
22
29
  /** Dashboard README refreshed by `docs:inject`. */
23
30
  dashboardFile: string;
24
31
  }
32
+ /**
33
+ * Best-effort `TotemConfig` load for the governance commands. Returns
34
+ * `undefined` when the config is missing, unparseable, OR resolves to the
35
+ * global `~/.totem/` profile — both missing and unparseable are legitimate
36
+ * states for a freshly-cloned consumer repo, and a global-profile config
37
+ * is intentionally NOT used as a repo-local `strategyRoot` source (that
38
+ * would let one user's personal pointer leak across every repo on disk).
39
+ * The strategy-root resolver still has env / sibling / submodule layers
40
+ * to fall back on (mmnto-ai/totem#1710 R3 — CR R3 global-config-leak fix).
41
+ *
42
+ * Shared by `proposalNewCommand` and `adrNewCommand` so the load idiom +
43
+ * its `// totem-context: intentional best-effort` annotation live in one
44
+ * place (mmnto-ai/totem#1710 R2).
45
+ */
46
+ export declare function loadGovernanceConfig(cwd: string): Promise<StrategyResolverConfig | undefined>;
25
47
  /**
26
48
  * Resolve governance paths for the current invocation.
27
49
  *
28
- * Two supported contexts:
29
- * 1. **Submodule case** — `<gitRoot>/.strategy/` exists. Used when Totem is
30
- * the parent repo and `.strategy/` is a submodule/worktree.
31
- * 2. **Standalone case** `<gitRoot>` itself is the strategy repo
32
- * (`proposals/active/` sits at the repo root). Used when the CLI is run
33
- * from inside the strategy repo directly.
50
+ * Two layout shapes supported:
51
+ *
52
+ * 1. **Standalone (cwd IS the strategy repo)** `<gitRoot>` itself contains
53
+ * `proposals/` or `adr/` at the top level. Used when the CLI runs from
54
+ * inside the strategy repo directly. Detected here in the helper before
55
+ * delegating to the resolver because the strategy-root resolver answers
56
+ * "where IS the strategy repo from somewhere else", not "are we INSIDE
57
+ * one already."
58
+ *
59
+ * 2. **Resolved (cwd is in a consuming repo)** — defer to
60
+ * `resolveStrategyRoot` (mmnto-ai/totem#1710). The resolver walks env →
61
+ * config → sibling → submodule precedence and returns an absolute path.
62
+ * Throws an actionable `TotemError` (ADR-088) when none of the layers
63
+ * resolve.
34
64
  *
35
- * Throws `TotemError` when cwd is not inside a git repo, or when neither
36
- * layout can be detected.
65
+ * Also throws `TotemError` when cwd is not inside a git repo.
37
66
  */
38
- export declare function resolveGovernancePaths(cwd: string, type: GovernanceType): GovernancePaths;
67
+ export declare function resolveGovernancePaths(cwd: string, type: GovernanceType, config?: StrategyResolverConfig): GovernancePaths;
39
68
  /**
40
69
  * Scan `targetDir` for `NNN-slug.md` files, parse the prefix to an int,
41
70
  * and return `(max + 1)` zero-padded to three digits.
@@ -1 +1 @@
1
- {"version":3,"file":"governance.d.ts","sourceRoot":"","sources":["../../src/utils/governance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,KAAK,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;CACvB;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,eAAe,CAwCzF;AAOD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA0B3D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAexE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAID;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,yUAoBrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,+QAgBhC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAoB1E;AAID;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzE,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,2FAA2F;IAC3F,kBAAkB,EAAE,OAAO,CAAC;IAC5B,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,uBAAuB,GAAG,sBAAsB,CA4B1F;AAID,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,kBAAkB,EAAE,OAAO,CAAC;IAC5B,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,EACxB,SAAS,GAAE,yBAA8B,GACxC,sBAAsB,CAqExB"}
1
+ {"version":3,"file":"governance.d.ts","sourceRoot":"","sources":["../../src/utils/governance.ts"],"names":[],"mappings":"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"}
@@ -8,9 +8,37 @@
8
8
  */
9
9
  import * as fs from 'node:fs';
10
10
  import * as path from 'node:path';
11
- import { resolveGitRoot, safeExec, TotemError } from '@mmnto/totem';
11
+ import { resolveGitRoot, resolveStrategyRoot, safeExec, TotemError, } from '@mmnto/totem';
12
+ import { sanitizeForTerminal } from '../terminal-sanitize.js';
12
13
  import { log } from '../ui.js';
13
- const STRATEGY_SUBDIR = '.strategy';
14
+ /**
15
+ * Best-effort `TotemConfig` load for the governance commands. Returns
16
+ * `undefined` when the config is missing, unparseable, OR resolves to the
17
+ * global `~/.totem/` profile — both missing and unparseable are legitimate
18
+ * states for a freshly-cloned consumer repo, and a global-profile config
19
+ * is intentionally NOT used as a repo-local `strategyRoot` source (that
20
+ * would let one user's personal pointer leak across every repo on disk).
21
+ * The strategy-root resolver still has env / sibling / submodule layers
22
+ * to fall back on (mmnto-ai/totem#1710 R3 — CR R3 global-config-leak fix).
23
+ *
24
+ * Shared by `proposalNewCommand` and `adrNewCommand` so the load idiom +
25
+ * its `// totem-context: intentional best-effort` annotation live in one
26
+ * place (mmnto-ai/totem#1710 R2).
27
+ */
28
+ export async function loadGovernanceConfig(cwd) {
29
+ try {
30
+ const { loadConfig, resolveConfigPath, isGlobalConfigPath } = await import('../utils.js');
31
+ const configPath = resolveConfigPath(cwd);
32
+ if (isGlobalConfigPath(configPath))
33
+ return undefined;
34
+ const config = (await loadConfig(configPath));
35
+ return config;
36
+ // totem-context: intentional best-effort load — missing/unparseable config is a legitimate state for a freshly-cloned consumer repo; resolver's other layers (env / sibling / submodule) still apply.
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
14
42
  function targetSubpath(type) {
15
43
  return type === 'proposal' ? path.join('proposals', 'active') : 'adr';
16
44
  }
@@ -20,35 +48,59 @@ function templateFilename(type) {
20
48
  /**
21
49
  * Resolve governance paths for the current invocation.
22
50
  *
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.
51
+ * Two layout shapes supported:
52
+ *
53
+ * 1. **Standalone (cwd IS the strategy repo)** `<gitRoot>` itself contains
54
+ * `proposals/` or `adr/` at the top level. Used when the CLI runs from
55
+ * inside the strategy repo directly. Detected here in the helper before
56
+ * delegating to the resolver because the strategy-root resolver answers
57
+ * "where IS the strategy repo from somewhere else", not "are we INSIDE
58
+ * one already."
59
+ *
60
+ * 2. **Resolved (cwd is in a consuming repo)** — defer to
61
+ * `resolveStrategyRoot` (mmnto-ai/totem#1710). The resolver walks env →
62
+ * config → sibling → submodule precedence and returns an absolute path.
63
+ * Throws an actionable `TotemError` (ADR-088) when none of the layers
64
+ * resolve.
29
65
  *
30
- * Throws `TotemError` when cwd is not inside a git repo, or when neither
31
- * layout can be detected.
66
+ * Also throws `TotemError` when cwd is not inside a git repo.
32
67
  */
33
- export function resolveGovernancePaths(cwd, type) {
68
+ export function resolveGovernancePaths(cwd, type, config) {
34
69
  const gitRoot = resolveGitRoot(cwd);
35
70
  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.');
71
+ // Sanitize `cwd` here too the sister branch already does this for
72
+ // the resolved-but-no-layout error (mmnto-ai/totem#1710 R5). A working
73
+ // directory name with embedded ANSI/CR bytes would otherwise hit the
74
+ // CLI raw on the not-in-git-repo path (mmnto-ai/totem#1710 R6 / CR R6
75
+ // outside-diff Major).
76
+ throw new TotemError('CONFIG_MISSING', `Not inside a git repository: ${sanitizeForTerminal(cwd)}`, 'Run this command from inside a Totem or Totem-strategy repository checkout.');
37
77
  }
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'));
78
+ // Standalone detection requires BOTH the type-specific target subdir AND
79
+ // a `templates/` folder at the repo root. The conjunction catches the
80
+ // canonical strategy-repo layout (proposals/active/ + adr/ + templates/)
81
+ // while avoiding false positives on consumer repos that happen to ship a
82
+ // top-level `adr/` docs folder without a strategy substrate.
83
+ const standaloneHasTarget = fs.existsSync(path.join(gitRoot, targetSubpath(type)));
84
+ const standaloneHasTemplates = fs.existsSync(path.join(gitRoot, 'templates'));
43
85
  let rootDir;
44
- if (submoduleHasProposals || submoduleHasAdr) {
45
- rootDir = submoduleRoot;
46
- }
47
- else if (standaloneHasProposals || standaloneHasAdr) {
86
+ if (standaloneHasTarget && standaloneHasTemplates) {
48
87
  rootDir = gitRoot;
49
88
  }
50
89
  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.');
90
+ const status = resolveStrategyRoot(cwd, { gitRoot, config });
91
+ if (!status.resolved) {
92
+ // Sanitize filesystem- and env-derived strings before they flow
93
+ // into the CLI-rendered TotemError. `cwd` and `gitRoot` are
94
+ // process inputs; `status.reason` carries env values
95
+ // (`TOTEM_STRATEGY_ROOT`, `STRATEGY_ROOT`) and config values
96
+ // verbatim. ANSI/CR bytes there would rewind the cursor when the
97
+ // error is rendered (mmnto-ai/totem#1710 R5 / CR R5 Major).
98
+ const safeCwd = sanitizeForTerminal(cwd);
99
+ const safeClonePath = sanitizeForTerminal(path.join(path.dirname(gitRoot), 'totem-strategy'));
100
+ const safeReason = sanitizeForTerminal(status.reason);
101
+ throw new TotemError('CONFIG_MISSING', `No Totem-strategy layout found from ${safeCwd}.`, `Clone the strategy repo as a sibling (e.g., \`git clone <strategy-url> ${safeClonePath}\`), set the TOTEM_STRATEGY_ROOT env var, or initialize the legacy \`.strategy/\` submodule. Resolver detail: ${safeReason}`);
102
+ }
103
+ rootDir = status.path;
52
104
  }
53
105
  const targetDir = path.join(rootDir, targetSubpath(type));
54
106
  const templatePath = path.join(rootDir, 'templates', templateFilename(type));
@@ -269,7 +321,7 @@ function todayIso() {
269
321
  * never strands a half-written artifact.
270
322
  */
271
323
  export function scaffoldGovernanceArtifact(options, internals = {}) {
272
- const paths = resolveGovernancePaths(options.cwd, options.type);
324
+ const paths = resolveGovernancePaths(options.cwd, options.type, options.config);
273
325
  // Sanitize once at the orchestrator entry. Both the filename slug and the
274
326
  // template body render against the cleaned form so a title carrying
275
327
  // newlines, tabs, or control characters cannot inject markdown blocks
@@ -1 +1 @@
1
- {"version":3,"file":"governance.js","sourceRoot":"","sources":["../../src/utils/governance.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"governance.js","sourceRoot":"","sources":["../../src/utils/governance.ts"],"names":[],"mappings":"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"}
@@ -45,9 +45,12 @@ describe('resolveGovernancePaths', () => {
45
45
  });
46
46
  it('resolves standalone strategy path when submodule prefix is missing', async () => {
47
47
  initGit(tmpDir);
48
- // Standalone strategy repo — `proposals/active/` lives at the git root, no `.strategy/` prefix.
48
+ // Standalone strategy repo — `adr/` lives at the git root, no `.strategy/` prefix.
49
+ // `templates/` sentinel at the root distinguishes a real strategy repo from a
50
+ // consumer repo that happens to ship a top-level `adr/` docs folder.
49
51
  fs.mkdirSync(path.join(tmpDir, 'proposals', 'active'), { recursive: true });
50
52
  fs.mkdirSync(path.join(tmpDir, 'adr'), { recursive: true });
53
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
51
54
  const { resolveGovernancePaths } = await import('./governance.js');
52
55
  const paths = resolveGovernancePaths(tmpDir, 'adr');
53
56
  expect(paths.rootDir).toBe(path.normalize(tmpDir));
@@ -55,6 +58,15 @@ describe('resolveGovernancePaths', () => {
55
58
  expect(paths.dashboardFile).toBe(path.normalize(path.join(tmpDir, 'README.md')));
56
59
  expect(paths.templatePath).toBe(path.normalize(path.join(tmpDir, 'templates', 'adr.md')));
57
60
  });
61
+ it('rejects standalone false-positive when a consumer repo has only adr/ without templates/', async () => {
62
+ initGit(tmpDir);
63
+ // Consumer repo with a top-level `adr/` docs folder but NO `templates/`
64
+ // and NO sibling/submodule strategy. Pre-tightening, this would have
65
+ // false-positively scaffolded into the consumer's adr/ folder.
66
+ fs.mkdirSync(path.join(tmpDir, 'adr'), { recursive: true });
67
+ const { resolveGovernancePaths } = await import('./governance.js');
68
+ expect(() => resolveGovernancePaths(tmpDir, 'adr')).toThrow(/strategy/i);
69
+ });
58
70
  it('throws TotemError when cwd is not inside a git repository', async () => {
59
71
  // tmpDir has no `.git/`; resolveGitRoot returns null.
60
72
  const { resolveGovernancePaths } = await import('./governance.js');
@@ -66,6 +78,53 @@ describe('resolveGovernancePaths', () => {
66
78
  const { resolveGovernancePaths } = await import('./governance.js');
67
79
  expect(() => resolveGovernancePaths(tmpDir, 'proposal')).toThrow(/strategy/i);
68
80
  });
81
+ it('error message names the canonical sibling clone path and env var (mmnto-ai/totem#1710)', async () => {
82
+ initGit(tmpDir);
83
+ const { resolveGovernancePaths } = await import('./governance.js');
84
+ let caught;
85
+ try {
86
+ resolveGovernancePaths(tmpDir, 'proposal');
87
+ }
88
+ catch (err) {
89
+ caught = err;
90
+ }
91
+ const totemErr = caught;
92
+ const combined = `${totemErr.message ?? ''}\n${totemErr.recoveryHint ?? ''}`;
93
+ expect(combined).toMatch(/totem-strategy/i);
94
+ expect(combined).toMatch(/TOTEM_STRATEGY_ROOT/);
95
+ });
96
+ it('resolves sibling strategy path via the resolver when ../totem-strategy exists (mmnto-ai/totem#1710)', async () => {
97
+ // Layout:
98
+ // <parent>/repo/.git/ ← gitRoot (created via initGit)
99
+ // <parent>/totem-strategy/proposals/active/
100
+ const repo = path.join(tmpDir, 'repo');
101
+ fs.mkdirSync(repo, { recursive: true });
102
+ initGit(repo);
103
+ const sibling = path.join(tmpDir, 'totem-strategy');
104
+ fs.mkdirSync(path.join(sibling, 'proposals', 'active'), { recursive: true });
105
+ const { resolveGovernancePaths } = await import('./governance.js');
106
+ const paths = resolveGovernancePaths(repo, 'proposal');
107
+ expect(paths.rootDir).toBe(path.normalize(sibling));
108
+ expect(paths.targetDir).toBe(path.normalize(path.join(sibling, 'proposals', 'active')));
109
+ });
110
+ it('resolves env-driven strategy path when TOTEM_STRATEGY_ROOT is set (mmnto-ai/totem#1710)', async () => {
111
+ initGit(tmpDir);
112
+ const elsewhere = path.join(tmpDir, 'elsewhere-strategy');
113
+ fs.mkdirSync(path.join(elsewhere, 'proposals', 'active'), { recursive: true });
114
+ const prev = process.env.TOTEM_STRATEGY_ROOT;
115
+ process.env.TOTEM_STRATEGY_ROOT = elsewhere;
116
+ try {
117
+ const { resolveGovernancePaths } = await import('./governance.js');
118
+ const paths = resolveGovernancePaths(tmpDir, 'proposal');
119
+ expect(paths.rootDir).toBe(path.normalize(elsewhere));
120
+ }
121
+ finally {
122
+ if (prev === undefined)
123
+ delete process.env.TOTEM_STRATEGY_ROOT;
124
+ else
125
+ process.env.TOTEM_STRATEGY_ROOT = prev;
126
+ }
127
+ });
69
128
  });
70
129
  describe('getNextArtifactId', () => {
71
130
  let tmpDir;
@@ -383,6 +442,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
383
442
  initGit(tmpDir);
384
443
  fs.mkdirSync(path.join(tmpDir, 'proposals', 'active'), { recursive: true });
385
444
  fs.mkdirSync(path.join(tmpDir, 'adr'), { recursive: true });
445
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
386
446
  const calls = [];
387
447
  const exec = (cmd, args) => {
388
448
  calls.push({ cmd, args });
@@ -409,6 +469,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
409
469
  initGit(tmpDir);
410
470
  fs.mkdirSync(path.join(tmpDir, 'proposals', 'active'), { recursive: true });
411
471
  fs.mkdirSync(path.join(tmpDir, 'adr'), { recursive: true });
472
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
412
473
  const exec = () => { };
413
474
  const { scaffoldGovernanceArtifact } = await import('./governance.js');
414
475
  const result = scaffoldGovernanceArtifact({ type: 'adr', title: 'Database Sharding', cwd: tmpDir }, { exec, date: '2026-04-21' });
@@ -425,6 +486,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
425
486
  // template body see the cleaned form.
426
487
  initGit(tmpDir);
427
488
  fs.mkdirSync(path.join(tmpDir, 'proposals', 'active'), { recursive: true });
489
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
428
490
  const exec = () => { };
429
491
  const { scaffoldGovernanceArtifact } = await import('./governance.js');
430
492
  const result = scaffoldGovernanceArtifact({
@@ -445,6 +507,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
445
507
  it('creates file on disk even when docs:inject is missing', async () => {
446
508
  initGit(tmpDir);
447
509
  fs.mkdirSync(path.join(tmpDir, 'proposals', 'active'), { recursive: true });
510
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
448
511
  const origWarn = console.error;
449
512
  console.error = () => { };
450
513
  try {
@@ -467,6 +530,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
467
530
  initGit(tmpDir);
468
531
  const target = path.join(tmpDir, 'proposals', 'active');
469
532
  fs.mkdirSync(target, { recursive: true });
533
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
470
534
  fs.writeFileSync(path.join(target, '001-alpha.md'), '# a\n', 'utf-8');
471
535
  fs.writeFileSync(path.join(target, '003-beta.md'), '# b\n', 'utf-8');
472
536
  const { scaffoldGovernanceArtifact } = await import('./governance.js');
@@ -478,6 +542,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
478
542
  initGit(tmpDir);
479
543
  const target = path.join(tmpDir, 'proposals', 'active');
480
544
  fs.mkdirSync(target, { recursive: true });
545
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
481
546
  // Seed with the exact filename the scaffolder would generate for this title:
482
547
  // id=001 + slug='collide' ⇒ '001-collide.md'. We force this by ALSO seeding a
483
548
  // higher-numbered file so getNextArtifactId returns 002, then we seed 002-collide.md
@@ -509,6 +574,7 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
509
574
  it('throws TotemError BEFORE touching disk when title sanitizes to empty', async () => {
510
575
  initGit(tmpDir);
511
576
  fs.mkdirSync(path.join(tmpDir, 'proposals', 'active'), { recursive: true });
577
+ fs.mkdirSync(path.join(tmpDir, 'templates'), { recursive: true });
512
578
  const execCalls = [];
513
579
  const exec = (cmd) => {
514
580
  execCalls.push(cmd);
@@ -522,4 +588,61 @@ describe('scaffoldGovernanceArtifact (orchestrator)', () => {
522
588
  expect(entries).toEqual([]);
523
589
  });
524
590
  });
591
+ describe('loadGovernanceConfig (mmnto-ai/totem#1710 R3 — global config leak)', () => {
592
+ let tmpDir;
593
+ let homeDir;
594
+ let originalCwd;
595
+ let originalHomeEnv;
596
+ let originalUserprofile;
597
+ beforeEach(() => {
598
+ tmpDir = makeTmpDir();
599
+ homeDir = makeTmpDir('totem-home-');
600
+ originalCwd = process.cwd();
601
+ // os.homedir() reads HOME on POSIX and USERPROFILE on Windows; redirect
602
+ // both so the test runs cross-platform without leaking a real
603
+ // ~/.totem/ profile into the assertion.
604
+ originalHomeEnv = process.env['HOME'];
605
+ originalUserprofile = process.env['USERPROFILE'];
606
+ process.env['HOME'] = homeDir;
607
+ process.env['USERPROFILE'] = homeDir;
608
+ });
609
+ afterEach(() => {
610
+ process.chdir(originalCwd);
611
+ if (originalHomeEnv === undefined) {
612
+ delete process.env['HOME'];
613
+ }
614
+ else {
615
+ process.env['HOME'] = originalHomeEnv;
616
+ }
617
+ if (originalUserprofile === undefined) {
618
+ delete process.env['USERPROFILE'];
619
+ }
620
+ else {
621
+ process.env['USERPROFILE'] = originalUserprofile;
622
+ }
623
+ cleanTmpDir(tmpDir);
624
+ cleanTmpDir(homeDir);
625
+ });
626
+ it('returns undefined when the resolved config lives in the global ~/.totem/ profile', async () => {
627
+ // No local config in tmpDir — so resolveConfigPath walks up to the
628
+ // global ~/.totem/ profile. A user's personal global config
629
+ // typically encodes tier/embedder choices, NOT a strategy pointer
630
+ // for one specific repo. Loading it for governance scaffolding
631
+ // would let one user's strategyRoot leak across every repo on disk.
632
+ initGit(tmpDir);
633
+ const globalTotemDir = path.join(homeDir, '.totem');
634
+ fs.mkdirSync(globalTotemDir, { recursive: true });
635
+ fs.writeFileSync(path.join(globalTotemDir, 'totem.config.ts'), "export default { targets: [{ glob: '**/*.ts', type: 'code', strategy: 'typescript-ast' }], strategyRoot: '/some/personal/strategy' };\n", 'utf-8');
636
+ const { loadGovernanceConfig } = await import('./governance.js');
637
+ const config = await loadGovernanceConfig(tmpDir);
638
+ expect(config).toBeUndefined();
639
+ });
640
+ it('returns the local config when one exists in cwd', async () => {
641
+ initGit(tmpDir);
642
+ fs.writeFileSync(path.join(tmpDir, 'totem.config.ts'), "export default { targets: [{ glob: '**/*.ts', type: 'code', strategy: 'typescript-ast' }], strategyRoot: '../totem-strategy' };\n", 'utf-8');
643
+ const { loadGovernanceConfig } = await import('./governance.js');
644
+ const config = await loadGovernanceConfig(tmpDir);
645
+ expect(config?.strategyRoot).toBe('../totem-strategy');
646
+ });
647
+ });
525
648
  //# sourceMappingURL=governance.test.js.map