@mmnto/cli 1.35.0 → 1.36.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 (55) hide show
  1. package/dist/commands/hook-run.d.ts +91 -0
  2. package/dist/commands/hook-run.d.ts.map +1 -0
  3. package/dist/commands/hook-run.js +149 -0
  4. package/dist/commands/hook-run.js.map +1 -0
  5. package/dist/commands/hook-run.test.d.ts +2 -0
  6. package/dist/commands/hook-run.test.d.ts.map +1 -0
  7. package/dist/commands/hook-run.test.js +264 -0
  8. package/dist/commands/hook-run.test.js.map +1 -0
  9. package/dist/commands/hook-test.d.ts +29 -0
  10. package/dist/commands/hook-test.d.ts.map +1 -0
  11. package/dist/commands/hook-test.js +132 -0
  12. package/dist/commands/hook-test.js.map +1 -0
  13. package/dist/hook/classification.d.ts +45 -0
  14. package/dist/hook/classification.d.ts.map +1 -0
  15. package/dist/hook/classification.js +24 -0
  16. package/dist/hook/classification.js.map +1 -0
  17. package/dist/hook/classification.test.d.ts +2 -0
  18. package/dist/hook/classification.test.d.ts.map +1 -0
  19. package/dist/hook/classification.test.js +40 -0
  20. package/dist/hook/classification.test.js.map +1 -0
  21. package/dist/hook/loader.d.ts +47 -0
  22. package/dist/hook/loader.d.ts.map +1 -0
  23. package/dist/hook/loader.js +66 -0
  24. package/dist/hook/loader.js.map +1 -0
  25. package/dist/hook/loader.test.d.ts +2 -0
  26. package/dist/hook/loader.test.d.ts.map +1 -0
  27. package/dist/hook/loader.test.js +205 -0
  28. package/dist/hook/loader.test.js.map +1 -0
  29. package/dist/hook/runtime.d.ts +47 -0
  30. package/dist/hook/runtime.d.ts.map +1 -0
  31. package/dist/hook/runtime.js +85 -0
  32. package/dist/hook/runtime.js.map +1 -0
  33. package/dist/hook/runtime.test.d.ts +2 -0
  34. package/dist/hook/runtime.test.d.ts.map +1 -0
  35. package/dist/hook/runtime.test.js +135 -0
  36. package/dist/hook/runtime.test.js.map +1 -0
  37. package/dist/hook/schema.d.ts +385 -0
  38. package/dist/hook/schema.d.ts.map +1 -0
  39. package/dist/hook/schema.js +164 -0
  40. package/dist/hook/schema.js.map +1 -0
  41. package/dist/hook/schema.test.d.ts +2 -0
  42. package/dist/hook/schema.test.d.ts.map +1 -0
  43. package/dist/hook/schema.test.js +233 -0
  44. package/dist/hook/schema.test.js.map +1 -0
  45. package/dist/hook/test-runner.d.ts +64 -0
  46. package/dist/hook/test-runner.d.ts.map +1 -0
  47. package/dist/hook/test-runner.js +57 -0
  48. package/dist/hook/test-runner.js.map +1 -0
  49. package/dist/hook/test-runner.test.d.ts +2 -0
  50. package/dist/hook/test-runner.test.d.ts.map +1 -0
  51. package/dist/hook/test-runner.test.js +237 -0
  52. package/dist/hook/test-runner.test.js.map +1 -0
  53. package/dist/index.js +57 -4
  54. package/dist/index.js.map +1 -1
  55. package/package.json +2 -2
@@ -0,0 +1,132 @@
1
+ import path from 'node:path';
2
+ import { sanitize, TotemError } from '@mmnto/totem';
3
+ // Static `TotemError` import is intentional: `applyFilter` below is a pure
4
+ // synchronous helper that throws TEST_FAILED on a typoed `--filter`, and an
5
+ // async wrapper would defeat the testability win of extracting it. The
6
+ // per-codebase guideline against top-level heavy internal value imports is
7
+ // applied to `runHookTests` and `resolveInstalledPackVersions` (lazy-loaded
8
+ // inside the command handler below) instead.
9
+ const TAG = 'HookTest';
10
+ /**
11
+ * Apply `--filter` to a hook-test summary, failing loud when the filter
12
+ * matches nothing despite fixtures being present. Pure helper extracted
13
+ * from `hookTestCommand` so the filter contract is unit-testable without
14
+ * driving the command end-to-end through `process.cwd()` + `loadConfig`.
15
+ *
16
+ * Returns the filtered result slice when the filter matches (or is absent).
17
+ * Throws TEST_FAILED when `--filter` is set, `summary.total > 0`, and the
18
+ * filter matches no fixtures — a typoed filter must never look like a
19
+ * successful zero-test run.
20
+ */
21
+ export function applyFilter(summary, filter) {
22
+ if (!filter)
23
+ return summary.results;
24
+ const term = filter.toLowerCase();
25
+ const filtered = summary.results.filter((r) => r.hookId.toLowerCase().includes(term));
26
+ if (filtered.length === 0 && summary.total > 0) {
27
+ throw new TotemError('TEST_FAILED', `No hook tests matched --filter "${sanitize(filter)}".`, 'Use an existing hook id substring or omit --filter to run all hook tests.');
28
+ }
29
+ return filtered;
30
+ }
31
+ export async function hookTestCommand(opts) {
32
+ const { log, bold, errorColor, success: successColor } = await import('../ui.js');
33
+ const { loadConfig, loadEnv, resolveConfigPath } = await import('../utils.js');
34
+ const { runHookTests } = await import('../hook/test-runner.js');
35
+ const { resolveInstalledPackVersions } = await import('./hook-run.js');
36
+ const cwd = process.cwd();
37
+ loadEnv(cwd);
38
+ const configPath = resolveConfigPath(cwd);
39
+ const config = await loadConfig(configPath);
40
+ const configRoot = path.dirname(configPath);
41
+ const manifestPath = path.join(configRoot, config.totemDir, 'compiled-hooks.json');
42
+ const testsDir = path.join(configRoot, config.totemDir, 'tests');
43
+ const installedPackVersions = resolveInstalledPackVersions(configRoot);
44
+ log.info(TAG, 'Running hook tests...');
45
+ const summary = runHookTests({ manifestPath, testsDir, installedPackVersions });
46
+ // Sanitize every value that flows in from pack-supplied data (manifest
47
+ // contents, fixture paths) before it reaches the terminal — third-party
48
+ // packs are an untrusted boundary and identifiers like `hookId`/`packId`
49
+ // could carry ANSI control sequences that would mangle the operator's
50
+ // shell. The loader-emitted warnings/errors carry our own message text
51
+ // but may interpolate user-controlled paths, so they get sanitized too.
52
+ for (const w of summary.loadWarnings) {
53
+ log.warn(TAG, sanitize(w));
54
+ }
55
+ for (const e of summary.loadErrors) {
56
+ log.error('Totem Error', `${sanitize(e.code)}: ${sanitize(e.message)}`);
57
+ }
58
+ for (const unknown of summary.unknownHooks) {
59
+ const safeFixture = sanitize(path.basename(unknown.fixturePath));
60
+ const safeHookId = sanitize(unknown.hookId);
61
+ log.warn(TAG, `Fixture ${safeFixture} references hook id "${safeHookId}" not present in compiled manifest`);
62
+ }
63
+ const results = applyFilter(summary, opts.filter);
64
+ // "No fixtures" only fires when no fixtures exist at all — neither
65
+ // evaluatable results nor orphans referencing an absent hook. A directory
66
+ // containing only orphan fixtures must fail loud below, not show the
67
+ // "create a fixture" placeholder.
68
+ if (results.length === 0 &&
69
+ summary.total === 0 &&
70
+ summary.unknownHooks.length === 0 &&
71
+ summary.loadErrors.length === 0) {
72
+ log.dim(TAG, `No hook fixtures (surface: hooks) found in ${sanitize(config.totemDir)}/tests/`);
73
+ log.dim(TAG, 'Create a fixture with:');
74
+ log.dim(TAG, '');
75
+ log.dim(TAG, ' ---');
76
+ log.dim(TAG, ' rule: <hook-id>');
77
+ log.dim(TAG, ' surface: hooks');
78
+ log.dim(TAG, ' corpus: fail');
79
+ log.dim(TAG, ' ---');
80
+ log.dim(TAG, '');
81
+ log.dim(TAG, ' ## Should fail');
82
+ log.dim(TAG, ' ```text');
83
+ log.dim(TAG, ' <args payload that should be rejected>');
84
+ log.dim(TAG, ' ```');
85
+ return;
86
+ }
87
+ for (const result of results) {
88
+ const label = `${sanitize(result.packId)}/${sanitize(result.hookId)}`;
89
+ if (result.passed) {
90
+ log.success(TAG, `${label} — PASS`);
91
+ continue;
92
+ }
93
+ log.error('Totem Error', `${label} — FAIL`);
94
+ for (const failure of result.failures) {
95
+ const direction = failure.expected === 'reject' ? 'missed reject' : 'false positive';
96
+ console.error(` [${direction}] expected ${failure.expected}, got ${failure.actual}`);
97
+ console.error(` payload: ${sanitize(failure.line.trim())}`);
98
+ }
99
+ }
100
+ const passedCount = results.filter((r) => r.passed).length;
101
+ const failedCount = results.length - passedCount;
102
+ console.error('');
103
+ // Tenet 4: load errors or orphan fixtures must fail loud — they are
104
+ // never "success" states. A corrupt manifest or a fixture pointing at a
105
+ // typoed hook id silently passing would mask broken pack wiring.
106
+ const hasOrphans = summary.unknownHooks.length > 0;
107
+ const hasLoadErrors = summary.loadErrors.length > 0;
108
+ if (failedCount === 0 && !hasOrphans && !hasLoadErrors) {
109
+ const label = successColor(bold('PASS'));
110
+ log.info(TAG, `${label} — ${passedCount} hook test(s) passed`);
111
+ return;
112
+ }
113
+ const label = errorColor(bold('FAIL'));
114
+ const parts = [];
115
+ if (failedCount > 0)
116
+ parts.push(`${failedCount} failed`);
117
+ if (hasOrphans)
118
+ parts.push(`${summary.unknownHooks.length} unknown-hook reference(s)`);
119
+ if (hasLoadErrors)
120
+ parts.push(`${summary.loadErrors.length} manifest load error(s)`);
121
+ parts.push(`${passedCount} passed`);
122
+ log.info(TAG, `${label} — ${parts.join(', ')}`);
123
+ const reasons = [];
124
+ if (failedCount > 0)
125
+ reasons.push(`${failedCount} hook test(s) failed`);
126
+ if (hasOrphans)
127
+ reasons.push(`${summary.unknownHooks.length} fixture(s) reference unknown hook id`);
128
+ if (hasLoadErrors)
129
+ reasons.push(`${summary.loadErrors.length} compiled-hooks manifest load error(s)`);
130
+ throw new TotemError('TEST_FAILED', reasons.join('; ') + '.', 'Fix failing patterns, orphan fixtures, or manifest issues, then re-run `totem hook test`.');
131
+ }
132
+ //# sourceMappingURL=hook-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-test.js","sourceRoot":"","sources":["../../src/commands/hook-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { HookRule } from './schema.js';
2
+ /**
3
+ * Rule classification per ADR-104 § Convergence + § Target-aware dispatch
4
+ * (strategy-Gemini Q1 binding synthesis, T1930Z 2026-05-11).
5
+ *
6
+ * Two classes:
7
+ * - `spine`: has Rego-shadow representation; formally verified by SMT;
8
+ * eligible for the top ~30 invariant set. Ships in ADR-103's lint-rule
9
+ * pipeline. Not produced by this V1 hook engine.
10
+ * - `interpretive`: ast-grep / regex only; no formal verification obligation;
11
+ * ships outside the core invariant set. All bot-pack hooks in V1 fall here.
12
+ *
13
+ * The hook runtime treats every rule as `interpretive`. The class is named
14
+ * explicitly so the loader's warn-and-ignore signal on a future Spine-Rule
15
+ * promotion attempt (`verification_shadow:` on a hook rule) carries the
16
+ * dispatch contract in its error message rather than relying on prose docs.
17
+ *
18
+ * V2 may promote bot-pack hooks to `spine` via a follow-on ADR; this seam
19
+ * is where that promotion lands.
20
+ */
21
+ export type RuleClassification = 'spine' | 'interpretive';
22
+ export interface ClassificationResult {
23
+ classification: RuleClassification;
24
+ /**
25
+ * When set, the loader SHOULD emit this string to stderr but continue
26
+ * loading the rule. Empty when no warn-and-ignore signal applies.
27
+ */
28
+ warning?: string;
29
+ }
30
+ /**
31
+ * Classify a hook rule for V1 dispatch.
32
+ *
33
+ * Always returns `interpretive` per ADR-104 § Target-aware dispatch (hooks
34
+ * are Interpretive Rule class — no formal-verification obligation; PreToolUse
35
+ * payloads are not source code, so the Rego/OPA value proposition is weaker
36
+ * than for lint rules).
37
+ *
38
+ * If the rule carries a `verification_shadow:` block (forward-compat schema
39
+ * permits this for future Spine promotion), the returned result includes a
40
+ * structured warning. Per ADR-104 § Convergence: "the engine MUST
41
+ * warn-and-ignore the block (the rule itself still executes as
42
+ * Interpretive)."
43
+ */
44
+ export declare function classifyHookRule(rule: HookRule): ClassificationResult;
45
+ //# sourceMappingURL=classification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classification.d.ts","sourceRoot":"","sources":["../../src/hook/classification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,cAAc,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,kBAAkB,CAAC;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,oBAAoB,CAQrE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Classify a hook rule for V1 dispatch.
3
+ *
4
+ * Always returns `interpretive` per ADR-104 § Target-aware dispatch (hooks
5
+ * are Interpretive Rule class — no formal-verification obligation; PreToolUse
6
+ * payloads are not source code, so the Rego/OPA value proposition is weaker
7
+ * than for lint rules).
8
+ *
9
+ * If the rule carries a `verification_shadow:` block (forward-compat schema
10
+ * permits this for future Spine promotion), the returned result includes a
11
+ * structured warning. Per ADR-104 § Convergence: "the engine MUST
12
+ * warn-and-ignore the block (the rule itself still executes as
13
+ * Interpretive)."
14
+ */
15
+ export function classifyHookRule(rule) {
16
+ if (rule.verification_shadow !== undefined) {
17
+ return {
18
+ classification: 'interpretive',
19
+ warning: `[totem:hook-shadow-ignored] ${rule.id}: verification_shadow block ignored in V1 (hooks are Interpretive Rule class); rule still executes`,
20
+ };
21
+ }
22
+ return { classification: 'interpretive' };
23
+ }
24
+ //# sourceMappingURL=classification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classification.js","sourceRoot":"","sources":["../../src/hook/classification.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;QAC3C,OAAO;YACL,cAAc,EAAE,cAAc;YAC9B,OAAO,EAAE,+BAA+B,IAAI,CAAC,EAAE,oGAAoG;SACpJ,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=classification.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classification.test.d.ts","sourceRoot":"","sources":["../../src/hook/classification.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { classifyHookRule } from './classification.js';
3
+ const baseRule = {
4
+ id: 'r1',
5
+ trigger: { tool: 'bash', pattern: '.*' },
6
+ check: { pattern: 'x', type: 'reject-if-match' },
7
+ message: 'm',
8
+ };
9
+ describe('classifyHookRule', () => {
10
+ it('returns interpretive with no warning for a plain hook rule', () => {
11
+ const result = classifyHookRule(baseRule);
12
+ expect(result.classification).toBe('interpretive');
13
+ expect(result.warning).toBeUndefined();
14
+ });
15
+ it('returns interpretive but emits a warn-and-ignore signal when verification_shadow is present', () => {
16
+ const withShadow = {
17
+ ...baseRule,
18
+ verification_shadow: { rego: 'package x' },
19
+ };
20
+ const result = classifyHookRule(withShadow);
21
+ expect(result.classification).toBe('interpretive');
22
+ expect(result.warning).toBeDefined();
23
+ expect(result.warning).toContain('[totem:hook-shadow-ignored]');
24
+ expect(result.warning).toContain('r1');
25
+ });
26
+ it('treats verification_shadow: null as present (warns and continues)', () => {
27
+ // null is a JS-truthy distinct from undefined; if the schema admits it the
28
+ // classification helper should still emit the warn-and-ignore signal so
29
+ // the dispatch contract holds for any non-undefined value.
30
+ const withNullShadow = {
31
+ ...baseRule,
32
+ id: 'r-null',
33
+ verification_shadow: null,
34
+ };
35
+ const result = classifyHookRule(withNullShadow);
36
+ expect(result.classification).toBe('interpretive');
37
+ expect(result.warning).toBeDefined();
38
+ });
39
+ });
40
+ //# sourceMappingURL=classification.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classification.test.js","sourceRoot":"","sources":["../../src/hook/classification.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,QAAQ,GAAa;IACzB,EAAE,EAAE,IAAI;IACR,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;IACxC,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,iBAAiB,EAAE;IAChD,OAAO,EAAE,GAAG;CACb,CAAC;AAEF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6FAA6F,EAAE,GAAG,EAAE;QACrG,MAAM,UAAU,GAAa;YAC3B,GAAG,QAAQ;YACX,mBAAmB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;SAC3C,CAAC;QACF,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,2EAA2E;QAC3E,wEAAwE;QACxE,2DAA2D;QAC3D,MAAM,cAAc,GAAa;YAC/B,GAAG,QAAQ;YACX,EAAE,EAAE,QAAQ;YACZ,mBAAmB,EAAE,IAAI;SAC1B,CAAC;QACF,MAAM,MAAM,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { TotemError } from '@mmnto/totem';
2
+ import { type CompiledHookRule } from './schema.js';
3
+ /**
4
+ * Compiled-hooks manifest loader (ADR-104 § Decision 3 staleness check + a
5
+ * forward-compat warn-and-skip path for an evolving manifest schema).
6
+ *
7
+ * The loader is a pure function over `(manifestPath, installedPackVersions)`.
8
+ * It does NOT read `package.json` itself — callers (typically a
9
+ * bootstrap helper) resolve installed pack versions once and pass them in.
10
+ * Keeps the loader testable in isolation; mirrors the substrate-wiring
11
+ * pattern from `bootstrapEngine` (1.25.0 wiring lesson).
12
+ *
13
+ * Three failure modes are surfaced via the `warnings` array, not by
14
+ * throwing:
15
+ *
16
+ * 1. Manifest file missing — empty result, no warnings (a fresh repo without
17
+ * installed pack hooks is a valid state).
18
+ * 2. Manifest schemaVersion is not the runner's expected version — warn and
19
+ * skip the entire manifest (no hooks loaded). Composes with ADR-104
20
+ * § Decision 4's forward-compat ethos.
21
+ * 3. Pack version drift — for each pack whose installed version differs from
22
+ * the compiled-against version, emit a `[totem:hook-stale]` warning per
23
+ * the format in ADR-104 § Decision 3. Hooks still load (Tenet 4 carve-out:
24
+ * hooks are best-effort; staleness is signal, not a fail-closed condition).
25
+ *
26
+ * Structural errors (corrupt JSON, schema-validation failure on a manifest
27
+ * claiming the supported schemaVersion) populate `errors` and yield an empty
28
+ * hooks array — distinct from a missing manifest.
29
+ */
30
+ export interface LoadCompiledHooksOptions {
31
+ manifestPath: string;
32
+ installedPackVersions: Record<string, string>;
33
+ }
34
+ export interface LoadCompiledHooksResult {
35
+ hooks: CompiledHookRule[];
36
+ warnings: string[];
37
+ /**
38
+ * Errors carry the original cause via `Error.cause` so debug consumers
39
+ * can traverse the chain (per the codebase styleguide rule against
40
+ * concatenating `err.message` into new strings — destroys the stack).
41
+ * Callers that just need to log can use `err.message`; debug tooling
42
+ * walks `err.cause` recursively.
43
+ */
44
+ errors: TotemError[];
45
+ }
46
+ export declare function loadCompiledHooks(options: LoadCompiledHooksOptions): LoadCompiledHooksResult;
47
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/hook/loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;OAMG;IACH,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,uBAAuB,CA+F5F"}
@@ -0,0 +1,66 @@
1
+ import fs from 'node:fs';
2
+ import { TotemError } from '@mmnto/totem';
3
+ import { COMPILED_HOOKS_SCHEMA_VERSION, CompiledHooksManifestSchema, } from './schema.js';
4
+ export function loadCompiledHooks(options) {
5
+ const warnings = [];
6
+ const errors = [];
7
+ // No `fs.existsSync` pre-check: that returns false for any filesystem
8
+ // error (permission denied, symlink loops, EBUSY, etc.), not just ENOENT.
9
+ // Treating those as "missing manifest" would silently swallow real
10
+ // diagnostics. Catch ENOENT explicitly inside the read; everything else
11
+ // surfaces as a HOOKS_LOAD_FAILED entry.
12
+ let raw;
13
+ try {
14
+ raw = fs.readFileSync(options.manifestPath, 'utf8');
15
+ // totem-context: intentional — error captured into diagnostics array (the loader's contract is diagnostics-not-throws per Tenet 4 carve-out for hooks being best-effort)
16
+ }
17
+ catch (err) {
18
+ if (err.code === 'ENOENT') {
19
+ // Fresh repo without installed pack hooks is a valid state, not a fault.
20
+ return { hooks: [], warnings, errors };
21
+ }
22
+ errors.push(new TotemError('HOOKS_LOAD_FAILED', `failed to read compiled-hooks manifest at ${options.manifestPath}`, 'verify the file is readable and re-run `totem sync` to regenerate', err));
23
+ return { hooks: [], warnings, errors };
24
+ }
25
+ let parsed;
26
+ try {
27
+ parsed = JSON.parse(raw);
28
+ // totem-context: intentional — error captured into diagnostics array (the loader's contract is diagnostics-not-throws per Tenet 4 carve-out for hooks being best-effort)
29
+ }
30
+ catch (err) {
31
+ errors.push(new TotemError('HOOKS_LOAD_FAILED', `compiled-hooks manifest at ${options.manifestPath} is not valid JSON`, 're-run `totem sync` to regenerate the manifest', err));
32
+ return { hooks: [], warnings, errors };
33
+ }
34
+ // Forward-compat: peek at schemaVersion BEFORE invoking the strict z.literal
35
+ // schema validator, so an unknown version surfaces as a warn-and-skip
36
+ // rather than a thrown ZodError. Mirrors the per-pack warn-and-skip pattern
37
+ // for `hooks.yaml :: version` from ADR-104 § Decision 4.
38
+ const peekedVersion = typeof parsed === 'object' && parsed !== null
39
+ ? parsed.schemaVersion
40
+ : undefined;
41
+ if (peekedVersion !== COMPILED_HOOKS_SCHEMA_VERSION) {
42
+ warnings.push(`[totem:hook-schema] compiled-hooks manifest schemaVersion ${JSON.stringify(peekedVersion)} unsupported by this runner (expected ${COMPILED_HOOKS_SCHEMA_VERSION})\n → upgrade totem CLI or re-run \`totem sync\` to regenerate`);
43
+ return { hooks: [], warnings, errors };
44
+ }
45
+ const validation = CompiledHooksManifestSchema.safeParse(parsed);
46
+ if (!validation.success) {
47
+ const summary = validation.error.issues
48
+ .map((i) => `${i.path.join('.')}: ${i.message}`)
49
+ .join('; ');
50
+ errors.push(new TotemError('HOOKS_LOAD_FAILED', `compiled-hooks manifest at ${options.manifestPath} failed schema validation: ${summary}`, 're-run `totem sync` to regenerate the manifest, or upgrade totem CLI if the manifest was authored by a newer version', validation.error));
51
+ return { hooks: [], warnings, errors };
52
+ }
53
+ const manifest = validation.data;
54
+ for (const [packId, compiledVersion] of Object.entries(manifest.sourcePackVersions)) {
55
+ const installedVersion = options.installedPackVersions[packId];
56
+ if (installedVersion === undefined) {
57
+ warnings.push(`[totem:hook-stale] ${packId}: compiled against ${compiledVersion}, not currently installed\n → run \`totem sync\` to refresh .totem/compiled-hooks.json`);
58
+ continue;
59
+ }
60
+ if (installedVersion !== compiledVersion) {
61
+ warnings.push(`[totem:hook-stale] ${packId}: compiled against ${compiledVersion}, installed ${installedVersion}\n → run \`totem sync\` to refresh .totem/compiled-hooks.json`);
62
+ }
63
+ }
64
+ return { hooks: manifest.hooks, warnings, errors };
65
+ }
66
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/hook/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.d.ts","sourceRoot":"","sources":["../../src/hook/loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,205 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { loadCompiledHooks } from './loader.js';
6
+ let workDir;
7
+ beforeEach(() => {
8
+ workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-hook-loader-'));
9
+ });
10
+ afterEach(() => {
11
+ fs.rmSync(workDir, { recursive: true, force: true });
12
+ });
13
+ function writeManifest(content) {
14
+ const manifestPath = path.join(workDir, 'compiled-hooks.json');
15
+ fs.writeFileSync(manifestPath, JSON.stringify(content), 'utf8');
16
+ return manifestPath;
17
+ }
18
+ const validRule = {
19
+ id: 'r1',
20
+ packId: '@mmnto/pack-bot-coderabbit',
21
+ trigger: { tool: 'bash', pattern: '.*' },
22
+ check: { pattern: 'x', type: 'reject-if-match' },
23
+ message: 'm',
24
+ };
25
+ describe('loadCompiledHooks', () => {
26
+ it('returns an empty result when the manifest file does not exist (fresh repo, ENOENT)', () => {
27
+ const result = loadCompiledHooks({
28
+ manifestPath: path.join(workDir, 'missing.json'),
29
+ installedPackVersions: {},
30
+ });
31
+ expect(result.hooks).toEqual([]);
32
+ expect(result.warnings).toEqual([]);
33
+ expect(result.errors).toEqual([]);
34
+ });
35
+ it('surfaces non-ENOENT read errors as HOOKS_LOAD_FAILED (does not pretend the file is missing)', () => {
36
+ // Reading a directory as a file produces EISDIR on POSIX / EBADF or
37
+ // ENOTSUP-flavoured failures on Windows. Whatever the platform-specific
38
+ // errno, it is NOT ENOENT — so the loader must surface it, not silently
39
+ // return the "manifest absent" result that the prior existsSync pre-check
40
+ // would have masked.
41
+ const dirAsManifest = path.join(workDir, 'a-directory');
42
+ fs.mkdirSync(dirAsManifest);
43
+ const result = loadCompiledHooks({
44
+ manifestPath: dirAsManifest,
45
+ installedPackVersions: {},
46
+ });
47
+ expect(result.hooks).toEqual([]);
48
+ expect(result.errors.length).toBe(1);
49
+ const err = result.errors[0];
50
+ expect(err.code).toBe('HOOKS_LOAD_FAILED');
51
+ expect(err.message).toContain('failed to read compiled-hooks manifest');
52
+ expect(err.cause).toBeDefined();
53
+ });
54
+ it('records a structural error on invalid JSON and preserves the original SyntaxError via cause', () => {
55
+ const manifestPath = path.join(workDir, 'compiled-hooks.json');
56
+ fs.writeFileSync(manifestPath, '{ not valid json', 'utf8');
57
+ const result = loadCompiledHooks({
58
+ manifestPath,
59
+ installedPackVersions: {},
60
+ });
61
+ expect(result.hooks).toEqual([]);
62
+ expect(result.errors.length).toBe(1);
63
+ const err = result.errors[0];
64
+ expect(err.message).toContain('not valid JSON');
65
+ expect(err.code).toBe('HOOKS_LOAD_FAILED');
66
+ // Original parse error preserved on `.cause` so debug consumers can
67
+ // walk the chain without the stack being collapsed into a string.
68
+ expect(err.cause).toBeInstanceOf(SyntaxError);
69
+ });
70
+ it('warns and skips when schemaVersion is higher than the runner supports', () => {
71
+ const manifestPath = writeManifest({
72
+ schemaVersion: 2,
73
+ compiledAt: '2026-05-11T18:43:00Z',
74
+ sourcePackVersions: {},
75
+ hooks: [],
76
+ });
77
+ const result = loadCompiledHooks({
78
+ manifestPath,
79
+ installedPackVersions: {},
80
+ });
81
+ expect(result.hooks).toEqual([]);
82
+ expect(result.errors).toEqual([]);
83
+ expect(result.warnings.length).toBe(1);
84
+ expect(result.warnings[0]).toContain('[totem:hook-schema]');
85
+ expect(result.warnings[0]).toContain('schemaVersion 2');
86
+ });
87
+ it('warns and skips when schemaVersion is missing', () => {
88
+ const manifestPath = writeManifest({
89
+ compiledAt: '2026-05-11T18:43:00Z',
90
+ sourcePackVersions: {},
91
+ hooks: [],
92
+ });
93
+ const result = loadCompiledHooks({
94
+ manifestPath,
95
+ installedPackVersions: {},
96
+ });
97
+ expect(result.hooks).toEqual([]);
98
+ expect(result.warnings.length).toBe(1);
99
+ expect(result.warnings[0]).toContain('[totem:hook-schema]');
100
+ });
101
+ it('records a structural error when the supported-version manifest fails schema validation', () => {
102
+ const manifestPath = writeManifest({
103
+ schemaVersion: 1,
104
+ compiledAt: 'not-a-real-date',
105
+ sourcePackVersions: {},
106
+ hooks: [],
107
+ });
108
+ const result = loadCompiledHooks({
109
+ manifestPath,
110
+ installedPackVersions: {},
111
+ });
112
+ expect(result.hooks).toEqual([]);
113
+ expect(result.errors.length).toBe(1);
114
+ const err = result.errors[0];
115
+ expect(err.message).toContain('schema validation');
116
+ expect(err.code).toBe('HOOKS_LOAD_FAILED');
117
+ // Zod's ZodError preserved as the cause for debug-mode chain traversal.
118
+ expect(err.cause).toBeDefined();
119
+ });
120
+ it('returns hooks with no warnings when installed pack versions match compiled versions', () => {
121
+ const manifestPath = writeManifest({
122
+ schemaVersion: 1,
123
+ compiledAt: '2026-05-11T18:43:00Z',
124
+ sourcePackVersions: { '@mmnto/pack-bot-coderabbit': '1.0.0' },
125
+ hooks: [validRule],
126
+ });
127
+ const result = loadCompiledHooks({
128
+ manifestPath,
129
+ installedPackVersions: { '@mmnto/pack-bot-coderabbit': '1.0.0' },
130
+ });
131
+ expect(result.hooks).toHaveLength(1);
132
+ expect(result.warnings).toEqual([]);
133
+ expect(result.errors).toEqual([]);
134
+ });
135
+ it('emits a staleness warning when the installed pack version differs from compiled', () => {
136
+ const manifestPath = writeManifest({
137
+ schemaVersion: 1,
138
+ compiledAt: '2026-05-11T18:43:00Z',
139
+ sourcePackVersions: { '@mmnto/pack-bot-coderabbit': '1.0.0' },
140
+ hooks: [validRule],
141
+ });
142
+ const result = loadCompiledHooks({
143
+ manifestPath,
144
+ installedPackVersions: { '@mmnto/pack-bot-coderabbit': '1.1.0' },
145
+ });
146
+ expect(result.hooks).toHaveLength(1);
147
+ expect(result.warnings.length).toBe(1);
148
+ expect(result.warnings[0]).toContain('[totem:hook-stale]');
149
+ expect(result.warnings[0]).toContain('@mmnto/pack-bot-coderabbit');
150
+ expect(result.warnings[0]).toContain('compiled against 1.0.0, installed 1.1.0');
151
+ });
152
+ it('emits a staleness warning when a compiled-against pack is not installed at all', () => {
153
+ const manifestPath = writeManifest({
154
+ schemaVersion: 1,
155
+ compiledAt: '2026-05-11T18:43:00Z',
156
+ sourcePackVersions: { '@mmnto/pack-bot-coderabbit': '1.0.0' },
157
+ hooks: [validRule],
158
+ });
159
+ const result = loadCompiledHooks({
160
+ manifestPath,
161
+ installedPackVersions: {},
162
+ });
163
+ expect(result.hooks).toHaveLength(1);
164
+ expect(result.warnings.length).toBe(1);
165
+ expect(result.warnings[0]).toContain('not currently installed');
166
+ });
167
+ it('ignores extra installed packs not in sourcePackVersions (no warning)', () => {
168
+ // A pack installed after the last `totem sync` is benign — its hooks
169
+ // are not yet active, but that is not staleness in the compiled set.
170
+ const manifestPath = writeManifest({
171
+ schemaVersion: 1,
172
+ compiledAt: '2026-05-11T18:43:00Z',
173
+ sourcePackVersions: { '@mmnto/pack-bot-coderabbit': '1.0.0' },
174
+ hooks: [validRule],
175
+ });
176
+ const result = loadCompiledHooks({
177
+ manifestPath,
178
+ installedPackVersions: {
179
+ '@mmnto/pack-bot-coderabbit': '1.0.0',
180
+ '@mmnto/pack-bot-gemini-code-assist': '1.0.0',
181
+ },
182
+ });
183
+ expect(result.warnings).toEqual([]);
184
+ });
185
+ it('emits one staleness warning per drifting pack', () => {
186
+ const manifestPath = writeManifest({
187
+ schemaVersion: 1,
188
+ compiledAt: '2026-05-11T18:43:00Z',
189
+ sourcePackVersions: {
190
+ '@mmnto/pack-bot-coderabbit': '1.0.0',
191
+ '@mmnto/pack-bot-gemini-code-assist': '2.0.0',
192
+ },
193
+ hooks: [validRule],
194
+ });
195
+ const result = loadCompiledHooks({
196
+ manifestPath,
197
+ installedPackVersions: {
198
+ '@mmnto/pack-bot-coderabbit': '1.1.0',
199
+ '@mmnto/pack-bot-gemini-code-assist': '2.0.1',
200
+ },
201
+ });
202
+ expect(result.warnings.length).toBe(2);
203
+ });
204
+ });
205
+ //# sourceMappingURL=loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../src/hook/loader.test.ts"],"names":[],"mappings":"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"}