@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,47 @@
1
+ import type { CompiledHookRule } from './schema.js';
2
+ /**
3
+ * Hook runtime evaluator (ADR-104 § Decisions 1, 2 + § Convergence).
4
+ *
5
+ * Takes a single hook rule plus a tool-call payload (tool name + tool args
6
+ * as a single string) and returns a structured allow/reject decision. The
7
+ * runtime is deterministic Node.js — no LLM calls in this path (Tenet 15
8
+ * corollary, ADR-103 § 8).
9
+ *
10
+ * V1 matcher class is regex-only per execution plan § 4. ast-grep and
11
+ * other matcher classes are deferred to V2; the schema permits future
12
+ * `verification_shadow` blocks but the V1 runtime ignores them.
13
+ */
14
+ export interface ToolCallPayload {
15
+ /** The tool the agent is attempting to invoke (e.g. "bash"). */
16
+ tool: string;
17
+ /** Serialized tool arguments. For bash this is the command string;
18
+ * for structured tools, callers serialize to a stable string form. */
19
+ args: string;
20
+ }
21
+ export type AllowDecision = {
22
+ decision: 'allow';
23
+ };
24
+ export type RejectDecision = {
25
+ decision: 'reject';
26
+ message: string;
27
+ packId: string;
28
+ ruleId: string;
29
+ recoveryHint?: string;
30
+ };
31
+ export type HookDecision = AllowDecision | RejectDecision;
32
+ /**
33
+ * Build the structured rejection message per ADR-104 § Decision 1:
34
+ *
35
+ * [totem:hook-block] <packId>/<ruleId>: <message>
36
+ * → <recoveryHint>
37
+ *
38
+ * The `→ <recoveryHint>` line is omitted when no recoveryHint is provided.
39
+ * Agents and operators grep for the `[totem:hook-block]` prefix; the
40
+ * `<packId>/<ruleId>` carries provenance.
41
+ *
42
+ * Parameter is narrowed to `RejectDecision` so the type system prevents
43
+ * passing an allow decision — no runtime guard needed.
44
+ */
45
+ export declare function formatRejection(decision: RejectDecision): string;
46
+ export declare function evaluateHook(rule: CompiledHookRule, payload: ToolCallPayload): HookDecision;
47
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/hook/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb;2EACuE;IACvE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAMhE;AA4CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,GAAG,YAAY,CA4B3F"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Build the structured rejection message per ADR-104 § Decision 1:
3
+ *
4
+ * [totem:hook-block] <packId>/<ruleId>: <message>
5
+ * → <recoveryHint>
6
+ *
7
+ * The `→ <recoveryHint>` line is omitted when no recoveryHint is provided.
8
+ * Agents and operators grep for the `[totem:hook-block]` prefix; the
9
+ * `<packId>/<ruleId>` carries provenance.
10
+ *
11
+ * Parameter is narrowed to `RejectDecision` so the type system prevents
12
+ * passing an allow decision — no runtime guard needed.
13
+ */
14
+ export function formatRejection(decision) {
15
+ const header = `[totem:hook-block] ${decision.packId}/${decision.ruleId}: ${decision.message}`;
16
+ if (decision.recoveryHint) {
17
+ return `${header}\n → ${decision.recoveryHint}`;
18
+ }
19
+ return header;
20
+ }
21
+ /**
22
+ * Evaluate a single compiled hook rule against a tool-call payload.
23
+ *
24
+ * Two-stage gate:
25
+ * 1. Trigger gate: does this rule apply to this tool + args?
26
+ * Rule applies when `rule.trigger.tool` equals `payload.tool` AND
27
+ * `rule.trigger.pattern` matches `payload.args`.
28
+ * 2. Check gate: when the trigger matches, apply `rule.check.pattern` to
29
+ * args. `reject-if-match` rejects on match; `reject-if-no-match`
30
+ * rejects on non-match.
31
+ *
32
+ * Returns `{ decision: 'allow' }` when either gate passes the call through.
33
+ *
34
+ * V1 invariant: regex matching only. Future matcher classes (ast-grep,
35
+ * Rego-shadow) ship in V2 follow-on ADRs.
36
+ *
37
+ * Per ADR-104 § Convergence, any `verification_shadow` block on the rule
38
+ * is silently ignored at the runtime layer (V1 hooks are Interpretive
39
+ * Rule class — no formal-verification obligation). Warn-and-ignore of
40
+ * verification_shadow happens at the load layer when compiling pack
41
+ * hooks.yaml, not on every hook-run invocation.
42
+ */
43
+ /**
44
+ * Memoizes compiled RegExp instances keyed by pattern string. Pack rule
45
+ * patterns are stable for the lifetime of a CLI process (the manifest is
46
+ * loaded once at startup), so a string-keyed cache is sufficient — there
47
+ * is no unbounded-growth concern in the single-shot CLI use case.
48
+ *
49
+ * Safe to memoize because the patterns are compiled with no flags, so the
50
+ * returned RegExp has no stateful `lastIndex` carry-over between `.test()`
51
+ * calls.
52
+ */
53
+ const regexCache = new Map();
54
+ function getCompiledRegex(pattern) {
55
+ const cached = regexCache.get(pattern);
56
+ if (cached)
57
+ return cached;
58
+ const compiled = new RegExp(pattern);
59
+ regexCache.set(pattern, compiled);
60
+ return compiled;
61
+ }
62
+ export function evaluateHook(rule, payload) {
63
+ if (rule.trigger.tool !== payload.tool) {
64
+ return { decision: 'allow' };
65
+ }
66
+ const triggerRegex = getCompiledRegex(rule.trigger.pattern);
67
+ if (!triggerRegex.test(payload.args)) {
68
+ return { decision: 'allow' };
69
+ }
70
+ const checkRegex = getCompiledRegex(rule.check.pattern);
71
+ const matched = checkRegex.test(payload.args);
72
+ const shouldReject = (rule.check.type === 'reject-if-match' && matched) ||
73
+ (rule.check.type === 'reject-if-no-match' && !matched);
74
+ if (!shouldReject) {
75
+ return { decision: 'allow' };
76
+ }
77
+ return {
78
+ decision: 'reject',
79
+ message: rule.message,
80
+ packId: rule.packId,
81
+ ruleId: rule.id,
82
+ recoveryHint: rule.recoveryHint,
83
+ };
84
+ }
85
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/hook/runtime.ts"],"names":[],"mappings":"AAmCA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,QAAwB;IACtD,MAAM,MAAM,GAAG,sBAAsB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC/F,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC1B,OAAO,GAAG,MAAM,SAAS,QAAQ,CAAC,YAAY,EAAE,CAAC;IACnD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE7C,SAAS,gBAAgB,CAAC,OAAe;IACvC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IACrC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAsB,EAAE,OAAwB;IAC3E,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,MAAM,YAAY,GAChB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,OAAO,CAAC;QAClD,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,oBAAoB,IAAI,CAAC,OAAO,CAAC,CAAC;IAEzD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,YAAY,EAAE,IAAI,CAAC,YAAY;KAChC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.test.d.ts","sourceRoot":"","sources":["../../src/hook/runtime.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,135 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { evaluateHook, formatRejection } from './runtime.js';
3
+ function rule(overrides = {}) {
4
+ return {
5
+ id: 'gca-tag-xor-command',
6
+ packId: '@mmnto/pack-bot-gemini-code-assist',
7
+ trigger: { tool: 'bash', pattern: 'gh\\s+(pr|issue)\\s+comment' },
8
+ check: {
9
+ pattern: '(?=.*@gemini-code-assist)(?=.*\\/gemini review)',
10
+ type: 'reject-if-match',
11
+ },
12
+ message: 'GCA tag XOR command — never both; doubling wastes GCA quota.',
13
+ recoveryHint: 'Choose one: @-mention to comment, /gemini review for fresh review.',
14
+ ...overrides,
15
+ };
16
+ }
17
+ describe('evaluateHook', () => {
18
+ describe('trigger gate', () => {
19
+ it('allows when payload.tool does not match rule.trigger.tool', () => {
20
+ const result = evaluateHook(rule(), { tool: 'write', args: 'whatever' });
21
+ expect(result.decision).toBe('allow');
22
+ });
23
+ it('allows when payload.tool matches but trigger.pattern does not', () => {
24
+ // Trigger requires `gh pr comment` or `gh issue comment`; this is `gh pr list`.
25
+ const result = evaluateHook(rule(), { tool: 'bash', args: 'gh pr list' });
26
+ expect(result.decision).toBe('allow');
27
+ });
28
+ });
29
+ describe('check gate — reject-if-match', () => {
30
+ it('rejects when both trigger and check patterns match', () => {
31
+ const args = 'gh pr comment 123 --body "@gemini-code-assist /gemini review please"';
32
+ const result = evaluateHook(rule(), { tool: 'bash', args });
33
+ expect(result.decision).toBe('reject');
34
+ if (result.decision === 'reject') {
35
+ expect(result.message).toMatch(/GCA tag XOR/);
36
+ expect(result.packId).toBe('@mmnto/pack-bot-gemini-code-assist');
37
+ expect(result.ruleId).toBe('gca-tag-xor-command');
38
+ }
39
+ });
40
+ it('allows when trigger matches but check does not (only one of the two tags present)', () => {
41
+ // Trigger matches `gh pr comment` but the check requires BOTH the
42
+ // @mention AND the slash command — this comment has only the mention.
43
+ const args = 'gh pr comment 123 --body "@gemini-code-assist take a look"';
44
+ const result = evaluateHook(rule(), { tool: 'bash', args });
45
+ expect(result.decision).toBe('allow');
46
+ });
47
+ });
48
+ describe('check gate — reject-if-no-match', () => {
49
+ it('rejects when trigger matches but the required check pattern is absent', () => {
50
+ const r = rule({
51
+ id: 'requires-fixes-block',
52
+ check: { pattern: '## Fixes', type: 'reject-if-no-match' },
53
+ trigger: { tool: 'bash', pattern: 'git\\s+commit' },
54
+ message: 'Commit message must include a `## Fixes` section.',
55
+ });
56
+ const result = evaluateHook(r, { tool: 'bash', args: 'git commit -m "wip"' });
57
+ expect(result.decision).toBe('reject');
58
+ });
59
+ it('allows when the required check pattern is present', () => {
60
+ const r = rule({
61
+ check: { pattern: '## Fixes', type: 'reject-if-no-match' },
62
+ trigger: { tool: 'bash', pattern: 'git\\s+commit' },
63
+ });
64
+ const result = evaluateHook(r, {
65
+ tool: 'bash',
66
+ args: 'git commit -m "feat: x\\n\\n## Fixes\\n- y"',
67
+ });
68
+ expect(result.decision).toBe('allow');
69
+ });
70
+ });
71
+ describe('reject decision payload', () => {
72
+ it('carries packId and ruleId for provenance', () => {
73
+ const args = 'gh pr comment 1 --body "@gemini-code-assist /gemini review"';
74
+ const result = evaluateHook(rule(), { tool: 'bash', args });
75
+ expect(result).toMatchObject({
76
+ decision: 'reject',
77
+ packId: '@mmnto/pack-bot-gemini-code-assist',
78
+ ruleId: 'gca-tag-xor-command',
79
+ });
80
+ });
81
+ it('preserves recoveryHint when present', () => {
82
+ const args = 'gh pr comment 1 --body "@gemini-code-assist /gemini review"';
83
+ const result = evaluateHook(rule(), { tool: 'bash', args });
84
+ if (result.decision === 'reject') {
85
+ expect(result.recoveryHint).toMatch(/Choose one/);
86
+ }
87
+ });
88
+ it('returns undefined recoveryHint when the rule omits it', () => {
89
+ const r = rule({ recoveryHint: undefined });
90
+ const args = 'gh pr comment 1 --body "@gemini-code-assist /gemini review"';
91
+ const result = evaluateHook(r, { tool: 'bash', args });
92
+ if (result.decision === 'reject') {
93
+ expect(result.recoveryHint).toBeUndefined();
94
+ }
95
+ });
96
+ });
97
+ describe('verification_shadow ignored at runtime', () => {
98
+ it('evaluates as Interpretive Rule even when verification_shadow is present', () => {
99
+ // Per ADR-104 § Convergence: hooks fall into Interpretive Rule class
100
+ // in V1. verification_shadow on a hook rule is reserved schema field
101
+ // for future Spine-Rule promotion; the V1 runtime must not change
102
+ // its decision based on its presence or absence.
103
+ const args = 'gh pr comment 1 --body "@gemini-code-assist /gemini review"';
104
+ const withShadow = rule({ verification_shadow: { rego: 'package x' } });
105
+ const withoutShadow = rule();
106
+ expect(evaluateHook(withShadow, { tool: 'bash', args })).toEqual(evaluateHook(withoutShadow, { tool: 'bash', args }));
107
+ });
108
+ });
109
+ });
110
+ describe('formatRejection', () => {
111
+ it('formats the structured prefix with packId/ruleId when recoveryHint is absent', () => {
112
+ const decision = {
113
+ decision: 'reject',
114
+ packId: '@mmnto/pack-bot-coderabbit',
115
+ ruleId: 'r1',
116
+ message: 'do not commit secrets',
117
+ };
118
+ expect(formatRejection(decision)).toBe('[totem:hook-block] @mmnto/pack-bot-coderabbit/r1: do not commit secrets');
119
+ });
120
+ it('includes the recovery-hint line when present', () => {
121
+ const decision = {
122
+ decision: 'reject',
123
+ packId: '@mmnto/pack-bot-coderabbit',
124
+ ruleId: 'r1',
125
+ message: 'do not commit secrets',
126
+ recoveryHint: 'use git-crypt or vault',
127
+ };
128
+ expect(formatRejection(decision)).toBe('[totem:hook-block] @mmnto/pack-bot-coderabbit/r1: do not commit secrets\n' +
129
+ ' → use git-crypt or vault');
130
+ });
131
+ // The previous "throws when given an allow decision" runtime guard is gone —
132
+ // formatRejection's parameter is now narrowed to RejectDecision so the type
133
+ // system prevents the caller bug at compile time. TS-only enforcement.
134
+ });
135
+ //# sourceMappingURL=runtime.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.test.js","sourceRoot":"","sources":["../../src/hook/runtime.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,385 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Hook rule schemas for the bot-pack wiring engine (ADR-104).
4
+ *
5
+ * Two surfaces:
6
+ * - `HooksYamlSchema` describes a pack's `hooks.yaml` (authoring surface).
7
+ * - `CompiledHooksManifestSchema` describes `.totem/compiled-hooks.json`
8
+ * (runtime surface produced by `totem sync` from installed packs).
9
+ *
10
+ * The compiled manifest carries the staleness metadata required by
11
+ * ADR-104 § Decision 3: schemaVersion + compiledAt + sourcePackVersions.
12
+ */
13
+ declare const HookCheckTypeSchema: z.ZodEnum<["reject-if-match", "reject-if-no-match"]>;
14
+ export type HookCheckType = z.infer<typeof HookCheckTypeSchema>;
15
+ /**
16
+ * Authoring-surface schema for a single hook rule in a pack's `hooks.yaml`.
17
+ *
18
+ * The optional `recoveryHint` field (ADR-104 § Decision 1) gives agents the
19
+ * WHAT-INSTEAD on a block — recommended but not required in V1. Adoption
20
+ * tracked toward a V2 upgrade-to-required trigger (>80% of published rules
21
+ * carry it, OR an empirically-observed retry-loop incident).
22
+ *
23
+ * The `verification_shadow` field is reserved for the Spine Rule
24
+ * classification path (ADR-104 § Convergence + Q1 binding). In V1 the engine
25
+ * MUST warn-and-ignore any verification_shadow on a hook rule (hooks are
26
+ * Interpretive Rule class — no formal verification obligation). Schema
27
+ * accepts it permissively so future Spine-Rule promotion does not require
28
+ * a schema break.
29
+ */
30
+ export declare const HookRuleSchema: z.ZodObject<{
31
+ id: z.ZodString;
32
+ trigger: z.ZodObject<{
33
+ tool: z.ZodString;
34
+ pattern: z.ZodEffects<z.ZodString, string, string>;
35
+ }, "strip", z.ZodTypeAny, {
36
+ tool: string;
37
+ pattern: string;
38
+ }, {
39
+ tool: string;
40
+ pattern: string;
41
+ }>;
42
+ check: z.ZodObject<{
43
+ pattern: z.ZodEffects<z.ZodString, string, string>;
44
+ type: z.ZodEnum<["reject-if-match", "reject-if-no-match"]>;
45
+ }, "strip", z.ZodTypeAny, {
46
+ type: "reject-if-match" | "reject-if-no-match";
47
+ pattern: string;
48
+ }, {
49
+ type: "reject-if-match" | "reject-if-no-match";
50
+ pattern: string;
51
+ }>;
52
+ message: z.ZodString;
53
+ recoveryHint: z.ZodOptional<z.ZodString>;
54
+ verification_shadow: z.ZodOptional<z.ZodUnknown>;
55
+ }, "strip", z.ZodTypeAny, {
56
+ check: {
57
+ type: "reject-if-match" | "reject-if-no-match";
58
+ pattern: string;
59
+ };
60
+ message: string;
61
+ id: string;
62
+ trigger: {
63
+ tool: string;
64
+ pattern: string;
65
+ };
66
+ recoveryHint?: string | undefined;
67
+ verification_shadow?: unknown;
68
+ }, {
69
+ check: {
70
+ type: "reject-if-match" | "reject-if-no-match";
71
+ pattern: string;
72
+ };
73
+ message: string;
74
+ id: string;
75
+ trigger: {
76
+ tool: string;
77
+ pattern: string;
78
+ };
79
+ recoveryHint?: string | undefined;
80
+ verification_shadow?: unknown;
81
+ }>;
82
+ export type HookRule = z.infer<typeof HookRuleSchema>;
83
+ export declare const HOOKS_YAML_SCHEMA_VERSION: 1;
84
+ /**
85
+ * Per-pack `hooks.yaml` file shape. The `version` field is the contract
86
+ * for forward-compat: when `totem sync` parses an unknown version (higher
87
+ * than the runner supports), it warns-and-skips that pack entirely
88
+ * (ADR-104 § Decision 4).
89
+ */
90
+ export declare const HooksYamlSchema: z.ZodEffects<z.ZodObject<{
91
+ version: z.ZodNumber;
92
+ hooks: z.ZodArray<z.ZodObject<{
93
+ id: z.ZodString;
94
+ trigger: z.ZodObject<{
95
+ tool: z.ZodString;
96
+ pattern: z.ZodEffects<z.ZodString, string, string>;
97
+ }, "strip", z.ZodTypeAny, {
98
+ tool: string;
99
+ pattern: string;
100
+ }, {
101
+ tool: string;
102
+ pattern: string;
103
+ }>;
104
+ check: z.ZodObject<{
105
+ pattern: z.ZodEffects<z.ZodString, string, string>;
106
+ type: z.ZodEnum<["reject-if-match", "reject-if-no-match"]>;
107
+ }, "strip", z.ZodTypeAny, {
108
+ type: "reject-if-match" | "reject-if-no-match";
109
+ pattern: string;
110
+ }, {
111
+ type: "reject-if-match" | "reject-if-no-match";
112
+ pattern: string;
113
+ }>;
114
+ message: z.ZodString;
115
+ recoveryHint: z.ZodOptional<z.ZodString>;
116
+ verification_shadow: z.ZodOptional<z.ZodUnknown>;
117
+ }, "strip", z.ZodTypeAny, {
118
+ check: {
119
+ type: "reject-if-match" | "reject-if-no-match";
120
+ pattern: string;
121
+ };
122
+ message: string;
123
+ id: string;
124
+ trigger: {
125
+ tool: string;
126
+ pattern: string;
127
+ };
128
+ recoveryHint?: string | undefined;
129
+ verification_shadow?: unknown;
130
+ }, {
131
+ check: {
132
+ type: "reject-if-match" | "reject-if-no-match";
133
+ pattern: string;
134
+ };
135
+ message: string;
136
+ id: string;
137
+ trigger: {
138
+ tool: string;
139
+ pattern: string;
140
+ };
141
+ recoveryHint?: string | undefined;
142
+ verification_shadow?: unknown;
143
+ }>, "many">;
144
+ }, "strip", z.ZodTypeAny, {
145
+ hooks: {
146
+ check: {
147
+ type: "reject-if-match" | "reject-if-no-match";
148
+ pattern: string;
149
+ };
150
+ message: string;
151
+ id: string;
152
+ trigger: {
153
+ tool: string;
154
+ pattern: string;
155
+ };
156
+ recoveryHint?: string | undefined;
157
+ verification_shadow?: unknown;
158
+ }[];
159
+ version: number;
160
+ }, {
161
+ hooks: {
162
+ check: {
163
+ type: "reject-if-match" | "reject-if-no-match";
164
+ pattern: string;
165
+ };
166
+ message: string;
167
+ id: string;
168
+ trigger: {
169
+ tool: string;
170
+ pattern: string;
171
+ };
172
+ recoveryHint?: string | undefined;
173
+ verification_shadow?: unknown;
174
+ }[];
175
+ version: number;
176
+ }>, {
177
+ hooks: {
178
+ check: {
179
+ type: "reject-if-match" | "reject-if-no-match";
180
+ pattern: string;
181
+ };
182
+ message: string;
183
+ id: string;
184
+ trigger: {
185
+ tool: string;
186
+ pattern: string;
187
+ };
188
+ recoveryHint?: string | undefined;
189
+ verification_shadow?: unknown;
190
+ }[];
191
+ version: number;
192
+ }, {
193
+ hooks: {
194
+ check: {
195
+ type: "reject-if-match" | "reject-if-no-match";
196
+ pattern: string;
197
+ };
198
+ message: string;
199
+ id: string;
200
+ trigger: {
201
+ tool: string;
202
+ pattern: string;
203
+ };
204
+ recoveryHint?: string | undefined;
205
+ verification_shadow?: unknown;
206
+ }[];
207
+ version: number;
208
+ }>;
209
+ export type HooksYaml = z.infer<typeof HooksYamlSchema>;
210
+ export declare const COMPILED_HOOKS_SCHEMA_VERSION: 1;
211
+ /**
212
+ * A compiled hook rule carries provenance (`packId`) so rejection messages
213
+ * can name `<packId>/<ruleId>` (ADR-104 § Decision 1) and so staleness
214
+ * checks can scope per-pack.
215
+ */
216
+ export declare const CompiledHookRuleSchema: z.ZodObject<{
217
+ id: z.ZodString;
218
+ trigger: z.ZodObject<{
219
+ tool: z.ZodString;
220
+ pattern: z.ZodEffects<z.ZodString, string, string>;
221
+ }, "strip", z.ZodTypeAny, {
222
+ tool: string;
223
+ pattern: string;
224
+ }, {
225
+ tool: string;
226
+ pattern: string;
227
+ }>;
228
+ check: z.ZodObject<{
229
+ pattern: z.ZodEffects<z.ZodString, string, string>;
230
+ type: z.ZodEnum<["reject-if-match", "reject-if-no-match"]>;
231
+ }, "strip", z.ZodTypeAny, {
232
+ type: "reject-if-match" | "reject-if-no-match";
233
+ pattern: string;
234
+ }, {
235
+ type: "reject-if-match" | "reject-if-no-match";
236
+ pattern: string;
237
+ }>;
238
+ message: z.ZodString;
239
+ recoveryHint: z.ZodOptional<z.ZodString>;
240
+ verification_shadow: z.ZodOptional<z.ZodUnknown>;
241
+ } & {
242
+ packId: z.ZodString;
243
+ }, "strip", z.ZodTypeAny, {
244
+ check: {
245
+ type: "reject-if-match" | "reject-if-no-match";
246
+ pattern: string;
247
+ };
248
+ message: string;
249
+ id: string;
250
+ trigger: {
251
+ tool: string;
252
+ pattern: string;
253
+ };
254
+ packId: string;
255
+ recoveryHint?: string | undefined;
256
+ verification_shadow?: unknown;
257
+ }, {
258
+ check: {
259
+ type: "reject-if-match" | "reject-if-no-match";
260
+ pattern: string;
261
+ };
262
+ message: string;
263
+ id: string;
264
+ trigger: {
265
+ tool: string;
266
+ pattern: string;
267
+ };
268
+ packId: string;
269
+ recoveryHint?: string | undefined;
270
+ verification_shadow?: unknown;
271
+ }>;
272
+ export type CompiledHookRule = z.infer<typeof CompiledHookRuleSchema>;
273
+ /**
274
+ * Runtime-surface manifest produced by `totem sync` and read on every
275
+ * `totem hook run` invocation. The metadata fields are load-bearing for
276
+ * ADR-104 § Decision 3 (staleness detection):
277
+ *
278
+ * - `schemaVersion`: bumps on breaking structural change to this manifest
279
+ * - `compiledAt`: ISO 8601 timestamp of last compile
280
+ * - `sourcePackVersions`: pack name → version at compile time; compared
281
+ * against package.json resolutions to emit `[totem:hook-stale]` warnings
282
+ * when packs have updated since last compile
283
+ */
284
+ export declare const CompiledHooksManifestSchema: z.ZodObject<{
285
+ schemaVersion: z.ZodLiteral<1>;
286
+ compiledAt: z.ZodString;
287
+ sourcePackVersions: z.ZodRecord<z.ZodString, z.ZodString>;
288
+ hooks: z.ZodArray<z.ZodObject<{
289
+ id: z.ZodString;
290
+ trigger: z.ZodObject<{
291
+ tool: z.ZodString;
292
+ pattern: z.ZodEffects<z.ZodString, string, string>;
293
+ }, "strip", z.ZodTypeAny, {
294
+ tool: string;
295
+ pattern: string;
296
+ }, {
297
+ tool: string;
298
+ pattern: string;
299
+ }>;
300
+ check: z.ZodObject<{
301
+ pattern: z.ZodEffects<z.ZodString, string, string>;
302
+ type: z.ZodEnum<["reject-if-match", "reject-if-no-match"]>;
303
+ }, "strip", z.ZodTypeAny, {
304
+ type: "reject-if-match" | "reject-if-no-match";
305
+ pattern: string;
306
+ }, {
307
+ type: "reject-if-match" | "reject-if-no-match";
308
+ pattern: string;
309
+ }>;
310
+ message: z.ZodString;
311
+ recoveryHint: z.ZodOptional<z.ZodString>;
312
+ verification_shadow: z.ZodOptional<z.ZodUnknown>;
313
+ } & {
314
+ packId: z.ZodString;
315
+ }, "strip", z.ZodTypeAny, {
316
+ check: {
317
+ type: "reject-if-match" | "reject-if-no-match";
318
+ pattern: string;
319
+ };
320
+ message: string;
321
+ id: string;
322
+ trigger: {
323
+ tool: string;
324
+ pattern: string;
325
+ };
326
+ packId: string;
327
+ recoveryHint?: string | undefined;
328
+ verification_shadow?: unknown;
329
+ }, {
330
+ check: {
331
+ type: "reject-if-match" | "reject-if-no-match";
332
+ pattern: string;
333
+ };
334
+ message: string;
335
+ id: string;
336
+ trigger: {
337
+ tool: string;
338
+ pattern: string;
339
+ };
340
+ packId: string;
341
+ recoveryHint?: string | undefined;
342
+ verification_shadow?: unknown;
343
+ }>, "many">;
344
+ }, "strip", z.ZodTypeAny, {
345
+ hooks: {
346
+ check: {
347
+ type: "reject-if-match" | "reject-if-no-match";
348
+ pattern: string;
349
+ };
350
+ message: string;
351
+ id: string;
352
+ trigger: {
353
+ tool: string;
354
+ pattern: string;
355
+ };
356
+ packId: string;
357
+ recoveryHint?: string | undefined;
358
+ verification_shadow?: unknown;
359
+ }[];
360
+ schemaVersion: 1;
361
+ compiledAt: string;
362
+ sourcePackVersions: Record<string, string>;
363
+ }, {
364
+ hooks: {
365
+ check: {
366
+ type: "reject-if-match" | "reject-if-no-match";
367
+ pattern: string;
368
+ };
369
+ message: string;
370
+ id: string;
371
+ trigger: {
372
+ tool: string;
373
+ pattern: string;
374
+ };
375
+ packId: string;
376
+ recoveryHint?: string | undefined;
377
+ verification_shadow?: unknown;
378
+ }[];
379
+ schemaVersion: 1;
380
+ compiledAt: string;
381
+ sourcePackVersions: Record<string, string>;
382
+ }>;
383
+ export type CompiledHooksManifest = z.infer<typeof CompiledHooksManifestSchema>;
384
+ export {};
385
+ //# sourceMappingURL=schema.d.ts.map