@webpieces/hook-runtime 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/package.json +37 -0
  2. package/src/adapters/agent-adapters.d.ts +13 -0
  3. package/src/adapters/agent-adapters.js +23 -0
  4. package/src/adapters/agent-adapters.js.map +1 -0
  5. package/src/adapters/agent-payload.d.ts +48 -0
  6. package/src/adapters/agent-payload.js +30 -0
  7. package/src/adapters/agent-payload.js.map +1 -0
  8. package/src/adapters/claude-code-adapter.d.ts +26 -0
  9. package/src/adapters/claude-code-adapter.js +69 -0
  10. package/src/adapters/claude-code-adapter.js.map +1 -0
  11. package/src/adapters/codex-adapter.d.ts +19 -0
  12. package/src/adapters/codex-adapter.js +48 -0
  13. package/src/adapters/codex-adapter.js.map +1 -0
  14. package/src/adapters/detect-ai.d.ts +70 -0
  15. package/src/adapters/detect-ai.js +82 -0
  16. package/src/adapters/detect-ai.js.map +1 -0
  17. package/src/core/apply-patch-parse.d.ts +36 -0
  18. package/src/core/apply-patch-parse.js +155 -0
  19. package/src/core/apply-patch-parse.js.map +1 -0
  20. package/src/core/build-context.d.ts +11 -0
  21. package/src/core/build-context.js +69 -0
  22. package/src/core/build-context.js.map +1 -0
  23. package/src/core/command-scan.d.ts +122 -0
  24. package/src/core/command-scan.js +282 -0
  25. package/src/core/command-scan.js.map +1 -0
  26. package/src/core/custom-rule-adapter.d.ts +23 -0
  27. package/src/core/custom-rule-adapter.js +50 -0
  28. package/src/core/custom-rule-adapter.js.map +1 -0
  29. package/src/core/delete-scoped-rules.d.ts +9 -0
  30. package/src/core/delete-scoped-rules.js +31 -0
  31. package/src/core/delete-scoped-rules.js.map +1 -0
  32. package/src/core/disable-directives.d.ts +8 -0
  33. package/src/core/disable-directives.js +86 -0
  34. package/src/core/disable-directives.js.map +1 -0
  35. package/src/core/effective-tree.d.ts +197 -0
  36. package/src/core/effective-tree.js +269 -0
  37. package/src/core/effective-tree.js.map +1 -0
  38. package/src/core/excluded-paths.d.ts +23 -0
  39. package/src/core/excluded-paths.js +54 -0
  40. package/src/core/excluded-paths.js.map +1 -0
  41. package/src/core/file-evaluation.d.ts +11 -0
  42. package/src/core/file-evaluation.js +45 -0
  43. package/src/core/file-evaluation.js.map +1 -0
  44. package/src/core/fix-hint.d.ts +40 -0
  45. package/src/core/fix-hint.js +53 -0
  46. package/src/core/fix-hint.js.map +1 -0
  47. package/src/core/glob.d.ts +1 -0
  48. package/src/core/glob.js +42 -0
  49. package/src/core/glob.js.map +1 -0
  50. package/src/core/report.d.ts +21 -0
  51. package/src/core/report.js +74 -0
  52. package/src/core/report.js.map +1 -0
  53. package/src/core/root-manifest.d.ts +1 -0
  54. package/src/core/root-manifest.js +21 -0
  55. package/src/core/root-manifest.js.map +1 -0
  56. package/src/core/rule-base.d.ts +40 -0
  57. package/src/core/rule-base.js +37 -0
  58. package/src/core/rule-base.js.map +1 -0
  59. package/src/core/rule-evaluation.d.ts +18 -0
  60. package/src/core/rule-evaluation.js +179 -0
  61. package/src/core/rule-evaluation.js.map +1 -0
  62. package/src/core/rules/shell-segment-scan.d.ts +64 -0
  63. package/src/core/rules/shell-segment-scan.js +88 -0
  64. package/src/core/rules/shell-segment-scan.js.map +1 -0
  65. package/src/core/shell-read-parity.d.ts +34 -0
  66. package/src/core/shell-read-parity.js +157 -0
  67. package/src/core/shell-read-parity.js.map +1 -0
  68. package/src/core/strip-ts-noise.d.ts +1 -0
  69. package/src/core/strip-ts-noise.js +191 -0
  70. package/src/core/strip-ts-noise.js.map +1 -0
  71. package/src/core/target-tree.d.ts +84 -0
  72. package/src/core/target-tree.js +148 -0
  73. package/src/core/target-tree.js.map +1 -0
  74. package/src/core/types.d.ts +149 -0
  75. package/src/core/types.js +190 -0
  76. package/src/core/types.js.map +1 -0
  77. package/src/harness-vocabulary.d.ts +2 -0
  78. package/src/harness-vocabulary.js +10 -0
  79. package/src/harness-vocabulary.js.map +1 -0
  80. package/src/hook-app.d.ts +15 -0
  81. package/src/hook-app.js +58 -0
  82. package/src/hook-app.js.map +1 -0
  83. package/src/hook-evaluator.d.ts +5 -0
  84. package/src/hook-evaluator.js +13 -0
  85. package/src/hook-evaluator.js.map +1 -0
  86. package/src/hook-ports.d.ts +9 -0
  87. package/src/hook-ports.js +40 -0
  88. package/src/hook-ports.js.map +1 -0
  89. package/src/index.d.ts +31 -0
  90. package/src/index.js +59 -0
  91. package/src/index.js.map +1 -0
  92. package/src/outcome.d.ts +14 -0
  93. package/src/outcome.js +20 -0
  94. package/src/outcome.js.map +1 -0
  95. package/src/protocol.d.ts +40 -0
  96. package/src/protocol.js +55 -0
  97. package/src/protocol.js.map +1 -0
  98. package/src/response.d.ts +2 -0
  99. package/src/response.js +21 -0
  100. package/src/response.js.map +1 -0
  101. package/src/to-error.d.ts +1 -0
  102. package/src/to-error.js +9 -0
  103. package/src/to-error.js.map +1 -0
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlockedResult = exports.RuleGroup = exports.FileContext = exports.BashContext = exports.EditContext = exports.Violation = void 0;
4
+ const rules_config_1 = require("@webpieces/rules-config");
5
+ class Violation {
6
+ line;
7
+ snippet;
8
+ // Optional per-occurrence override for the `→` line. When omitted, the report falls back
9
+ // to the rule's `FixHint.violation`. Dynamic rules (param name, line count, marker path,
10
+ // branch/PR) pass a specific message here; static rules omit it.
11
+ message;
12
+ editIndex;
13
+ editCount;
14
+ constructor(line, snippet, message) {
15
+ this.line = line;
16
+ this.snippet = snippet;
17
+ this.message = message;
18
+ this.editIndex = undefined;
19
+ this.editCount = undefined;
20
+ }
21
+ }
22
+ exports.Violation = Violation;
23
+ class EditContext {
24
+ tool;
25
+ editIndex;
26
+ editCount;
27
+ filePath;
28
+ relativePath;
29
+ workspaceRoot;
30
+ addedContent;
31
+ strippedContent;
32
+ lines;
33
+ strippedLines;
34
+ removedContent;
35
+ isLineDisabled;
36
+ options;
37
+ constructor(tool, editIndex, editCount, filePath, relativePath, workspaceRoot, addedContent, strippedContent, lines, strippedLines, removedContent, isLineDisabled) {
38
+ this.tool = tool;
39
+ this.editIndex = editIndex;
40
+ this.editCount = editCount;
41
+ this.filePath = filePath;
42
+ this.relativePath = relativePath;
43
+ this.workspaceRoot = workspaceRoot;
44
+ this.addedContent = addedContent;
45
+ this.strippedContent = strippedContent;
46
+ this.lines = lines;
47
+ this.strippedLines = strippedLines;
48
+ this.removedContent = removedContent;
49
+ this.isLineDisabled = isLineDisabled;
50
+ this.options = {};
51
+ }
52
+ }
53
+ exports.EditContext = EditContext;
54
+ // A heredoc and its body: `<<EOF` / `<<'EOF'` / `<<-EOF` through the terminator line.
55
+ const HEREDOC_BODY = /<<-?\s*(['"]?)(\w+)\1[\s\S]*?^\t*\2\s*$/gm;
56
+ // A single- or double-quoted span.
57
+ const QUOTED_SPAN = /'([^']*)'|"([^"]*)"/g;
58
+ // Shell syntax that a quoted span is explicitly NOT: separators, pipes, subshells, redirection,
59
+ // expansion. Content carrying any of these is data (a jq filter, a regex, a glob), and unquoting it
60
+ // would hand the segment scanner syntax the shell never sees. See stripProse.
61
+ const SHELL_METACHARACTER = /[|;&()<>$`]/;
62
+ class BashContext {
63
+ tool;
64
+ command;
65
+ /**
66
+ * `command` with heredoc bodies and prose-in-quotes removed — what a guard should MATCH on, while
67
+ * `command` stays raw for the violation message (a human needs to see what they actually typed).
68
+ *
69
+ * A commit message that merely MENTIONS a blocked command is not that command. This repo's whole
70
+ * subject matter is the git workflow, so its commit messages, docs and the guards' own fix-hint
71
+ * text are full of `git push` / `gh pr …` strings; matching raw text blocks writing about the
72
+ * tooling. A guard that cries wolf is a guard someone eventually turns OFF, which costs far more
73
+ * than the theoretical bypass this opens (`sh -c '<blocked command>'` stops matching). That trade
74
+ * is deliberate: these guards exist to catch a FORGETFUL agent, not an adversarial one.
75
+ *
76
+ * ONLY for blocklist-shaped guards ("if the command matches X, block"), where stripping can only
77
+ * ever block LESS. Never use it in an allowlist-shaped guard such as merged-branch-bash-guard,
78
+ * where stripping could turn a permitted recovery command into a blocked one and deadlock the
79
+ * session — there, match on the raw `command`.
80
+ */
81
+ commandCode;
82
+ /**
83
+ * The tree this command is JUDGED against — the root of the worktree it actually acts on, which
84
+ * is NOT the shell's cwd whenever the command carries a leading `cd <path> &&` (see
85
+ * EffectiveTreeResolver). Guards read git state, the main-sync cache and the decision log from
86
+ * here, so a command aimed at a linked worktree is judged on that worktree's branch, never the
87
+ * primary clone's.
88
+ */
89
+ workspaceRoot;
90
+ /** The directory the command really runs in (post-`cd`). Relative paths resolve against THIS. */
91
+ effectiveCwd;
92
+ /** The root owning webpieces.config.json. Equals workspaceRoot except in a linked worktree. */
93
+ governedRoot;
94
+ options;
95
+ constructor(command, workspaceRoot, effectiveCwd, governedRoot) {
96
+ this.tool = 'Bash';
97
+ this.command = command;
98
+ this.commandCode = this.stripProse(command);
99
+ this.workspaceRoot = workspaceRoot;
100
+ this.effectiveCwd = effectiveCwd ?? workspaceRoot;
101
+ this.governedRoot = governedRoot ?? workspaceRoot;
102
+ this.options = {};
103
+ }
104
+ /**
105
+ * A quoted span WITHOUT whitespace is kept: `git checkout "main"` is a real command with a quoted
106
+ * argument, not prose. One containing whitespace is a sentence, and becomes a single space.
107
+ *
108
+ * ONE exception, and it is a false positive that actually fired: a whitespace-free span carrying
109
+ * SHELL METACHARACTERS is dropped too. Keeping the content strips only the quotes, so a jq filter —
110
+ * `--jq '[.[]|select(.title|test("x"))]|length'` — was handed to the guards as bare shell syntax.
111
+ * Every guard then split it on its `|`, `(` and `;` and saw a segment reading exactly `test`, which
112
+ * whole-repo-build-guard classified as the workspace-wide test script and BLOCKED. There was no
113
+ * build anywhere in that command; it polled npm and GitHub.
114
+ *
115
+ * Quoting is precisely how a shell says "this is DATA, not syntax", so honouring the quotes is the
116
+ * correct reading. And because commandCode only ever feeds blocklist-shaped guards, dropping more
117
+ * can only ever block LESS — never turn an allowed command into a refused one.
118
+ */
119
+ stripProse(command) {
120
+ const withoutHeredocs = command.replace(HEREDOC_BODY, ' ');
121
+ return withoutHeredocs.replace(QUOTED_SPAN, (match, single, double) => {
122
+ const content = single ?? double ?? '';
123
+ if (/\s/.test(content))
124
+ return ' ';
125
+ return SHELL_METACHARACTER.test(content) ? ' ' : content;
126
+ });
127
+ }
128
+ }
129
+ exports.BashContext = BashContext;
130
+ class FileContext {
131
+ tool;
132
+ filePath;
133
+ relativePath;
134
+ workspaceRoot;
135
+ /** The git tree that owns filePath. workspaceRoot remains the root that owns policy/config. */
136
+ targetRoot;
137
+ /** filePath relative to targetRoot, independent of its governed-root spelling. */
138
+ targetRelativePath;
139
+ currentFileLines;
140
+ linesAdded;
141
+ linesRemoved;
142
+ projectedFileLines;
143
+ options;
144
+ constructor(tool, filePath, relativePath, workspaceRoot, currentFileLines, linesAdded, linesRemoved, projectedFileLines, targetRoot = workspaceRoot, targetRelativePath = relativePath) {
145
+ this.tool = tool;
146
+ this.filePath = filePath;
147
+ this.relativePath = relativePath;
148
+ this.workspaceRoot = workspaceRoot;
149
+ this.targetRoot = targetRoot;
150
+ this.targetRelativePath = targetRelativePath;
151
+ this.currentFileLines = currentFileLines;
152
+ this.linesAdded = linesAdded;
153
+ this.linesRemoved = linesRemoved;
154
+ this.projectedFileLines = projectedFileLines;
155
+ this.options = {};
156
+ }
157
+ }
158
+ exports.FileContext = FileContext;
159
+ class RuleGroup {
160
+ ruleName;
161
+ ruleDescription;
162
+ fixHint;
163
+ violations;
164
+ constructor(ruleName, ruleDescription, fixHint, violations) {
165
+ this.ruleName = ruleName;
166
+ this.ruleDescription = ruleDescription;
167
+ this.fixHint = fixHint;
168
+ this.violations = violations;
169
+ }
170
+ }
171
+ exports.RuleGroup = RuleGroup;
172
+ class BlockedResult {
173
+ report;
174
+ /**
175
+ * The L0 fault this block IS, in the codebook's own letter (see core/l0-fault-codes.ts), or `-` for
176
+ * an ordinary rule block.
177
+ *
178
+ * It rides on the result rather than being re-derived from the report text because the block is
179
+ * decided deep in the runner (fault C in configMissingBlock, fault Y in checkConfigSync) and
180
+ * STAMPED by the adapter at the terminal boundary, several frames up. Scraping the report for a
181
+ * fault would be a second answer to a question the producer already knows.
182
+ */
183
+ fault;
184
+ constructor(report, fault = rules_config_1.L0_FAULT_NONE) {
185
+ this.report = report;
186
+ this.fault = fault;
187
+ }
188
+ }
189
+ exports.BlockedResult = BlockedResult;
190
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../packages/tooling/hook-runtime/src/core/types.ts"],"names":[],"mappings":"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","sourcesContent":["// ResolvedConfig / ResolvedRuleConfig / RuleOptions now live in @webpieces/rules-config\n// so ai-hooks and the Nx validate-code executor share one loader and one config file.\nimport { RuleOptions } from '@webpieces/rules-config';\nimport { HookMode, NormalizedBashInput, NormalizedEdit, NormalizedToolInput, ToolKind } from '../protocol';\nimport { FixHint } from './fix-hint';\nimport { L0_FAULT_NONE } from '@webpieces/rules-config';\n\n// 'Read' is a first-class member because read-stale-guard is a file-scoped guard that runs on the\n// Read fast path. It is deliberately NOT in HANDLED_FILE_TOOLS (adapters/claude-code-adapter.ts), so\n// ClaudeCodeAdapter.kindOf() maps it to the 'Read' routing kind and Read never enters the edit/file\n// rule pipeline — only the one guard that asks for it. Nothing switches exhaustively on this union;\n// it is carried for logging.\n//\n// 'Delete' arrives ONLY from a Codex `apply_patch` carrying a `*** Delete File:` directive — no Claude\n// Code tool produces it. Every existing rule DEFAULTS TO NOT FIRING on it (see DeleteScopedRules in\n// ./delete-scoped-rules.ts): a rule written to judge the bytes an edit ADDS has nothing to say about a file going\n// away, and inventing a verdict for it would be guessing. Rules for which a delete IS meaningful — a\n// barrel export disappearing, a doc that documents a deleted file — opt in there, deliberately.\nexport type RuleScope = 'edit' | 'file' | 'bash';\n// Which category of built-in rules a hook invocation runs: code-style 'rules', git/PR/branch\n// 'guards' (the hookGuards section), or 'all' (both categories — used by the openclaw plugin adapter,\n// which is a single before_tool_call hook rather than two split PreToolUse hooks).\nexport type IsLineDisabled = (lineNum: number, ruleName: string) => boolean;\n\nexport class Violation {\n readonly line: number;\n readonly snippet: string;\n // Optional per-occurrence override for the `→` line. When omitted, the report falls back\n // to the rule's `FixHint.violation`. Dynamic rules (param name, line count, marker path,\n // branch/PR) pass a specific message here; static rules omit it.\n readonly message: string | undefined;\n editIndex: number | undefined;\n editCount: number | undefined;\n\n constructor(line: number, snippet: string, message?: string) {\n this.line = line;\n this.snippet = snippet;\n this.message = message;\n this.editIndex = undefined;\n this.editCount = undefined;\n }\n}\n\nexport class EditContext {\n readonly tool: ToolKind;\n readonly editIndex: number;\n readonly editCount: number;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: string;\n readonly addedContent: string;\n readonly strippedContent: string;\n readonly lines: readonly string[];\n readonly strippedLines: readonly string[];\n readonly removedContent: string;\n readonly isLineDisabled: IsLineDisabled;\n options: RuleOptions;\n\n constructor(\n tool: ToolKind,\n editIndex: number,\n editCount: number,\n filePath: string,\n relativePath: string,\n workspaceRoot: string,\n addedContent: string,\n strippedContent: string,\n lines: readonly string[],\n strippedLines: readonly string[],\n removedContent: string,\n isLineDisabled: IsLineDisabled,\n ) {\n this.tool = tool;\n this.editIndex = editIndex;\n this.editCount = editCount;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.addedContent = addedContent;\n this.strippedContent = strippedContent;\n this.lines = lines;\n this.strippedLines = strippedLines;\n this.removedContent = removedContent;\n this.isLineDisabled = isLineDisabled;\n this.options = {};\n }\n}\n\n// A heredoc and its body: `<<EOF` / `<<'EOF'` / `<<-EOF` through the terminator line.\nconst HEREDOC_BODY = /<<-?\\s*(['\"]?)(\\w+)\\1[\\s\\S]*?^\\t*\\2\\s*$/gm;\n\n// A single- or double-quoted span.\nconst QUOTED_SPAN = /'([^']*)'|\"([^\"]*)\"/g;\n\n// Shell syntax that a quoted span is explicitly NOT: separators, pipes, subshells, redirection,\n// expansion. Content carrying any of these is data (a jq filter, a regex, a glob), and unquoting it\n// would hand the segment scanner syntax the shell never sees. See stripProse.\nconst SHELL_METACHARACTER = /[|;&()<>$`]/;\n\nexport class BashContext {\n readonly tool: 'Bash';\n readonly command: string;\n /**\n * `command` with heredoc bodies and prose-in-quotes removed — what a guard should MATCH on, while\n * `command` stays raw for the violation message (a human needs to see what they actually typed).\n *\n * A commit message that merely MENTIONS a blocked command is not that command. This repo's whole\n * subject matter is the git workflow, so its commit messages, docs and the guards' own fix-hint\n * text are full of `git push` / `gh pr …` strings; matching raw text blocks writing about the\n * tooling. A guard that cries wolf is a guard someone eventually turns OFF, which costs far more\n * than the theoretical bypass this opens (`sh -c '<blocked command>'` stops matching). That trade\n * is deliberate: these guards exist to catch a FORGETFUL agent, not an adversarial one.\n *\n * ONLY for blocklist-shaped guards (\"if the command matches X, block\"), where stripping can only\n * ever block LESS. Never use it in an allowlist-shaped guard such as merged-branch-bash-guard,\n * where stripping could turn a permitted recovery command into a blocked one and deadlock the\n * session — there, match on the raw `command`.\n */\n readonly commandCode: string;\n /**\n * The tree this command is JUDGED against — the root of the worktree it actually acts on, which\n * is NOT the shell's cwd whenever the command carries a leading `cd <path> &&` (see\n * EffectiveTreeResolver). Guards read git state, the main-sync cache and the decision log from\n * here, so a command aimed at a linked worktree is judged on that worktree's branch, never the\n * primary clone's.\n */\n readonly workspaceRoot: string;\n /** The directory the command really runs in (post-`cd`). Relative paths resolve against THIS. */\n readonly effectiveCwd: string;\n /** The root owning webpieces.config.json. Equals workspaceRoot except in a linked worktree. */\n readonly governedRoot: string;\n options: RuleOptions;\n\n constructor(command: string, workspaceRoot: string, effectiveCwd?: string, governedRoot?: string) {\n this.tool = 'Bash';\n this.command = command;\n this.commandCode = this.stripProse(command);\n this.workspaceRoot = workspaceRoot;\n this.effectiveCwd = effectiveCwd ?? workspaceRoot;\n this.governedRoot = governedRoot ?? workspaceRoot;\n this.options = {};\n }\n\n /**\n * A quoted span WITHOUT whitespace is kept: `git checkout \"main\"` is a real command with a quoted\n * argument, not prose. One containing whitespace is a sentence, and becomes a single space.\n *\n * ONE exception, and it is a false positive that actually fired: a whitespace-free span carrying\n * SHELL METACHARACTERS is dropped too. Keeping the content strips only the quotes, so a jq filter —\n * `--jq '[.[]|select(.title|test(\"x\"))]|length'` — was handed to the guards as bare shell syntax.\n * Every guard then split it on its `|`, `(` and `;` and saw a segment reading exactly `test`, which\n * whole-repo-build-guard classified as the workspace-wide test script and BLOCKED. There was no\n * build anywhere in that command; it polled npm and GitHub.\n *\n * Quoting is precisely how a shell says \"this is DATA, not syntax\", so honouring the quotes is the\n * correct reading. And because commandCode only ever feeds blocklist-shaped guards, dropping more\n * can only ever block LESS — never turn an allowed command into a refused one.\n */\n private stripProse(command: string): string {\n const withoutHeredocs = command.replace(HEREDOC_BODY, ' ');\n return withoutHeredocs.replace(QUOTED_SPAN, (match: string, single?: string, double?: string): string => {\n const content = single ?? double ?? '';\n if (/\\s/.test(content)) return ' ';\n return SHELL_METACHARACTER.test(content) ? ' ' : content;\n });\n }\n}\n\nexport class FileContext {\n readonly tool: ToolKind;\n readonly filePath: string;\n readonly relativePath: string;\n readonly workspaceRoot: string;\n /** The git tree that owns filePath. workspaceRoot remains the root that owns policy/config. */\n readonly targetRoot: string;\n /** filePath relative to targetRoot, independent of its governed-root spelling. */\n readonly targetRelativePath: string;\n readonly currentFileLines: number;\n readonly linesAdded: number;\n readonly linesRemoved: number;\n readonly projectedFileLines: number;\n options: RuleOptions;\n\n constructor(\n tool: ToolKind,\n filePath: string,\n relativePath: string,\n workspaceRoot: string,\n currentFileLines: number,\n linesAdded: number,\n linesRemoved: number,\n projectedFileLines: number,\n targetRoot: string = workspaceRoot,\n targetRelativePath: string = relativePath,\n ) {\n this.tool = tool;\n this.filePath = filePath;\n this.relativePath = relativePath;\n this.workspaceRoot = workspaceRoot;\n this.targetRoot = targetRoot;\n this.targetRelativePath = targetRelativePath;\n this.currentFileLines = currentFileLines;\n this.linesAdded = linesAdded;\n this.linesRemoved = linesRemoved;\n this.projectedFileLines = projectedFileLines;\n this.options = {};\n }\n}\n\n/**\n * The shape of a custom rule loaded from a `rulesDir` (a plain object returned by require()).\n * It carries only metadata + a `check` method — no on/off logic of its own.\n */\nexport interface PlainRule {\n readonly name: string;\n readonly description: string;\n readonly scope: RuleScope;\n readonly files: readonly string[];\n readonly defaultOptions: RuleOptions;\n readonly fixHint: FixHint;\n check(ctx: EditContext | FileContext | BashContext): readonly Violation[];\n}\n\n/**\n * The runtime contract the runner iterates. Both the built-in rule classes (which extend\n * EditRuleBase/FileRuleBase/BashRuleBase) and the custom-rule adapter satisfy this: they add\n * `shouldRun()` (mode + escape-hatch decision) on top of the PlainRule metadata.\n */\nexport interface Rule extends PlainRule {\n /**\n * The webpieces.config.json key whose entry configures this rule — NOT necessarily `name`.\n *\n * `name` is the operator identity (what a decision-log line and a deny report carry); `configKey`\n * is the switch. They differ wherever several classes implement one POLICY: all four branch-state\n * guards read `branch-state-guard`, all four PR-lifecycle guards read `pr-lifecycle-guard`. Every\n * consumer that asks \"which section does this live in?\" (filterByMode) or \"does this have an\n * entry?\" (the fault-Y config-sync check) must read THIS field, because asking for `name` there\n * demands entries under keys the validator rejects as retired.\n */\n readonly configKey: string;\n shouldRun(): boolean;\n}\n\nexport class RuleGroup {\n readonly ruleName: string;\n readonly ruleDescription: string;\n readonly fixHint: FixHint;\n readonly violations: readonly Violation[];\n\n constructor(\n ruleName: string,\n ruleDescription: string,\n fixHint: FixHint,\n violations: readonly Violation[],\n ) {\n this.ruleName = ruleName;\n this.ruleDescription = ruleDescription;\n this.fixHint = fixHint;\n this.violations = violations;\n }\n}\n\nexport class BlockedResult {\n readonly report: string;\n /**\n * The L0 fault this block IS, in the codebook's own letter (see core/l0-fault-codes.ts), or `-` for\n * an ordinary rule block.\n *\n * It rides on the result rather than being re-derived from the report text because the block is\n * decided deep in the runner (fault C in configMissingBlock, fault Y in checkConfigSync) and\n * STAMPED by the adapter at the terminal boundary, several frames up. Scraping the report for a\n * fault would be a second answer to a question the producer already knows.\n */\n readonly fault: string;\n\n constructor(report: string, fault: string = L0_FAULT_NONE) {\n this.report = report;\n this.fault = fault;\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export { AI_TYPE_SH } from './adapters/detect-ai';
2
+ export { READ_COMMANDS, SED_RANGE_BODY } from './core/shell-read-parity';
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SED_RANGE_BODY = exports.READ_COMMANDS = exports.AI_TYPE_SH = void 0;
4
+ // Narrow L0 imports keep shim repair independent of the runtime application and rule evaluator.
5
+ var detect_ai_1 = require("./adapters/detect-ai");
6
+ Object.defineProperty(exports, "AI_TYPE_SH", { enumerable: true, get: function () { return detect_ai_1.AI_TYPE_SH; } });
7
+ var shell_read_parity_1 = require("./core/shell-read-parity");
8
+ Object.defineProperty(exports, "READ_COMMANDS", { enumerable: true, get: function () { return shell_read_parity_1.READ_COMMANDS; } });
9
+ Object.defineProperty(exports, "SED_RANGE_BODY", { enumerable: true, get: function () { return shell_read_parity_1.SED_RANGE_BODY; } });
10
+ //# sourceMappingURL=harness-vocabulary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-vocabulary.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/harness-vocabulary.ts"],"names":[],"mappings":";;;AAAA,gGAAgG;AAChG,kDAAkD;AAAzC,uGAAA,UAAU,OAAA;AACnB,8DAAyE;AAAhE,kHAAA,aAAa,OAAA;AAAE,mHAAA,cAAc,OAAA","sourcesContent":["// Narrow L0 imports keep shim repair independent of the runtime application and rule evaluator.\nexport { AI_TYPE_SH } from './adapters/detect-ai';\nexport { READ_COMMANDS, SED_RANGE_BODY } from './core/shell-read-parity';\n"]}
@@ -0,0 +1,15 @@
1
+ import { HookEvaluator } from './hook-evaluator';
2
+ import { HookArgs } from './outcome';
3
+ import { HookProcessExit, HookStdinSource, HookStdoutSink } from './hook-ports';
4
+ export declare class HookApp {
5
+ private readonly stdin;
6
+ private readonly stdout;
7
+ private readonly processExit;
8
+ private readonly evaluator;
9
+ constructor(stdin: HookStdinSource, stdout: HookStdoutSink, processExit: HookProcessExit, evaluator: HookEvaluator);
10
+ run(args: HookArgs): Promise<void>;
11
+ private decide;
12
+ }
13
+ export declare class HookBootFailure {
14
+ report(err: unknown): void;
15
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookBootFailure = exports.HookApp = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const inversify_1 = require("inversify");
6
+ const hook_evaluator_1 = require("./hook-evaluator");
7
+ const outcome_1 = require("./outcome");
8
+ const hook_ports_1 = require("./hook-ports");
9
+ const response_1 = require("./response");
10
+ const to_error_1 = require("./to-error");
11
+ const CRASH_PREFIX = '[ai-hooks] hook crashed unexpectedly — failing closed: ';
12
+ let HookApp = class HookApp {
13
+ stdin;
14
+ stdout;
15
+ processExit;
16
+ evaluator;
17
+ constructor(stdin, stdout, processExit, evaluator) {
18
+ this.stdin = stdin;
19
+ this.stdout = stdout;
20
+ this.processExit = processExit;
21
+ this.evaluator = evaluator;
22
+ }
23
+ async run(args) {
24
+ const outcome = await this.decide(args);
25
+ if (outcome.stdout !== '')
26
+ this.stdout.write(outcome.stdout);
27
+ this.processExit.exit(outcome.exitCode);
28
+ }
29
+ async decide(args) {
30
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- fail-closed boundary converts port/evaluator faults into an explicit protocol deny.
31
+ try {
32
+ return await this.evaluator.evaluate(await this.stdin.read(), args);
33
+ }
34
+ catch (err) {
35
+ const error = (0, to_error_1.toError)(err);
36
+ return new outcome_1.HookOutcome(`${(0, response_1.denyJson)(null, `${CRASH_PREFIX}${error.message}`)}\n`, 0);
37
+ }
38
+ }
39
+ };
40
+ exports.HookApp = HookApp;
41
+ exports.HookApp = HookApp = tslib_1.__decorate([
42
+ (0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton),
43
+ tslib_1.__metadata("design:paramtypes", [hook_ports_1.HookStdinSource,
44
+ hook_ports_1.HookStdoutSink,
45
+ hook_ports_1.HookProcessExit,
46
+ hook_evaluator_1.HookEvaluator])
47
+ ], HookApp);
48
+ class HookBootFailure {
49
+ // webpieces-disable no-any-unknown -- a process rejection is unknown by construction and is immediately narrowed through toError().
50
+ report(err) {
51
+ const error = (0, to_error_1.toError)(err);
52
+ process.stdout.write(`${(0, response_1.denyJson)(null, `${CRASH_PREFIX}${error.message}`)}\n`);
53
+ // webpieces-disable no-process-exit-outside-main -- last-resort protocol boundary before DI exists.
54
+ process.exit(0);
55
+ }
56
+ }
57
+ exports.HookBootFailure = HookBootFailure;
58
+ //# sourceMappingURL=hook-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-app.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/hook-app.ts"],"names":[],"mappings":";;;;AAAA,yCAA2D;AAE3D,qDAAiD;AACjD,uCAAkD;AAClD,6CAAgF;AAChF,yCAAsC;AACtC,yCAAqC;AAErC,MAAM,YAAY,GAAG,yDAAyD,CAAC;AAGxE,IAAM,OAAO,GAAb,MAAM,OAAO;IAEK;IACA;IACA;IACA;IAJrB,YACqB,KAAsB,EACtB,MAAsB,EACtB,WAA4B,EAC5B,SAAwB;QAHxB,UAAK,GAAL,KAAK,CAAiB;QACtB,WAAM,GAAN,MAAM,CAAgB;QACtB,gBAAW,GAAX,WAAW,CAAiB;QAC5B,cAAS,GAAT,SAAS,CAAe;IAC1C,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,IAAc;QACpB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,IAAc;QAC/B,qJAAqJ;QACrJ,IAAI,CAAC;YACD,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,IAAI,qBAAW,CAAC,GAAG,IAAA,mBAAQ,EAAC,IAAI,EAAE,GAAG,YAAY,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACxF,CAAC;IACL,CAAC;CACJ,CAAA;AAvBY,0BAAO;kBAAP,OAAO;IADnB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;6CAGT,4BAAe;QACd,2BAAc;QACT,4BAAe;QACjB,8BAAa;GALpC,OAAO,CAuBnB;AAED,MAAa,eAAe;IACxB,oIAAoI;IACpI,MAAM,CAAC,GAAY;QACf,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAA,mBAAQ,EAAC,IAAI,EAAE,GAAG,YAAY,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;QAC/E,oGAAoG;QACpG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;CACJ;AARD,0CAQC","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\n\nimport { HookEvaluator } from './hook-evaluator';\nimport { HookArgs, HookOutcome } from './outcome';\nimport { HookProcessExit, HookStdinSource, HookStdoutSink } from './hook-ports';\nimport { denyJson } from './response';\nimport { toError } from './to-error';\n\nconst CRASH_PREFIX = '[ai-hooks] hook crashed unexpectedly — failing closed: ';\n\n@injectable(bindingScopeValues.Singleton)\nexport class HookApp {\n constructor(\n private readonly stdin: HookStdinSource,\n private readonly stdout: HookStdoutSink,\n private readonly processExit: HookProcessExit,\n private readonly evaluator: HookEvaluator,\n ) {}\n\n async run(args: HookArgs): Promise<void> {\n const outcome = await this.decide(args);\n if (outcome.stdout !== '') this.stdout.write(outcome.stdout);\n this.processExit.exit(outcome.exitCode);\n }\n\n private async decide(args: HookArgs): Promise<HookOutcome> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- fail-closed boundary converts port/evaluator faults into an explicit protocol deny.\n try {\n return await this.evaluator.evaluate(await this.stdin.read(), args);\n } catch (err: unknown) {\n const error = toError(err);\n return new HookOutcome(`${denyJson(null, `${CRASH_PREFIX}${error.message}`)}\\n`, 0);\n }\n }\n}\n\nexport class HookBootFailure {\n // webpieces-disable no-any-unknown -- a process rejection is unknown by construction and is immediately narrowed through toError().\n report(err: unknown): void {\n const error = toError(err);\n process.stdout.write(`${denyJson(null, `${CRASH_PREFIX}${error.message}`)}\\n`);\n // webpieces-disable no-process-exit-outside-main -- last-resort protocol boundary before DI exists.\n process.exit(0);\n }\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { HookArgs, HookOutcome } from './outcome';
2
+ /** Injected product boundary: the runtime never imports a concrete rule registry or evaluator. */
3
+ export declare abstract class HookEvaluator {
4
+ abstract evaluate(raw: string, args: HookArgs): HookOutcome | Promise<HookOutcome>;
5
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookEvaluator = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const inversify_1 = require("inversify");
6
+ /** Injected product boundary: the runtime never imports a concrete rule registry or evaluator. */
7
+ let HookEvaluator = class HookEvaluator {
8
+ };
9
+ exports.HookEvaluator = HookEvaluator;
10
+ exports.HookEvaluator = HookEvaluator = tslib_1.__decorate([
11
+ (0, inversify_1.injectable)()
12
+ ], HookEvaluator);
13
+ //# sourceMappingURL=hook-evaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-evaluator.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/hook-evaluator.ts"],"names":[],"mappings":";;;;AAAA,yCAAuC;AAGvC,kGAAkG;AAE3F,IAAe,aAAa,GAA5B,MAAe,aAAa;CAElC,CAAA;AAFqB,sCAAa;wBAAb,aAAa;IADlC,IAAA,sBAAU,GAAE;GACS,aAAa,CAElC","sourcesContent":["import { injectable } from 'inversify';\nimport { HookArgs, HookOutcome } from './outcome';\n\n/** Injected product boundary: the runtime never imports a concrete rule registry or evaluator. */\n@injectable()\nexport abstract class HookEvaluator {\n abstract evaluate(raw: string, args: HookArgs): HookOutcome | Promise<HookOutcome>;\n}\n"]}
@@ -0,0 +1,9 @@
1
+ export declare class HookStdinSource {
2
+ read(): Promise<string>;
3
+ }
4
+ export declare class HookStdoutSink {
5
+ write(bytes: string): void;
6
+ }
7
+ export declare class HookProcessExit {
8
+ exit(code: number): void;
9
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookProcessExit = exports.HookStdoutSink = exports.HookStdinSource = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const inversify_1 = require("inversify");
6
+ let HookStdinSource = class HookStdinSource {
7
+ read() {
8
+ return new Promise((resolve) => {
9
+ let data = '';
10
+ process.stdin.setEncoding('utf8');
11
+ process.stdin.on('data', (chunk) => { data += chunk; });
12
+ process.stdin.on('end', () => { resolve(data); });
13
+ process.stdin.on('error', () => { resolve(''); });
14
+ if (process.stdin.isTTY)
15
+ resolve('');
16
+ });
17
+ }
18
+ };
19
+ exports.HookStdinSource = HookStdinSource;
20
+ exports.HookStdinSource = HookStdinSource = tslib_1.__decorate([
21
+ (0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
22
+ ], HookStdinSource);
23
+ let HookStdoutSink = class HookStdoutSink {
24
+ write(bytes) { process.stdout.write(bytes); }
25
+ };
26
+ exports.HookStdoutSink = HookStdoutSink;
27
+ exports.HookStdoutSink = HookStdoutSink = tslib_1.__decorate([
28
+ (0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
29
+ ], HookStdoutSink);
30
+ let HookProcessExit = class HookProcessExit {
31
+ exit(code) {
32
+ // webpieces-disable no-process-exit-outside-main -- this port is the hook protocol's terminal boundary.
33
+ process.exit(code);
34
+ }
35
+ };
36
+ exports.HookProcessExit = HookProcessExit;
37
+ exports.HookProcessExit = HookProcessExit = tslib_1.__decorate([
38
+ (0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
39
+ ], HookProcessExit);
40
+ //# sourceMappingURL=hook-ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-ports.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/hook-ports.ts"],"names":[],"mappings":";;;;AAAA,yCAA2D;AAGpD,IAAM,eAAe,GAArB,MAAM,eAAe;IACxB,IAAI;QACA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAgC,EAAE,EAAE;YACpD,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK;gBAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAXY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,eAAe,CAW3B;AAGM,IAAM,cAAc,GAApB,MAAM,cAAc;IACvB,KAAK,CAAC,KAAa,IAAU,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAC9D,CAAA;AAFY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,cAAc,CAE1B;AAGM,IAAM,eAAe,GAArB,MAAM,eAAe;IACxB,IAAI,CAAC,IAAY;QACb,wGAAwG;QACxG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;CACJ,CAAA;AALY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,eAAe,CAK3B","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\n\n@injectable(bindingScopeValues.Singleton)\nexport class HookStdinSource {\n read(): Promise<string> {\n return new Promise((resolve: (value: string) => void) => {\n let data = '';\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (chunk: string) => { data += chunk; });\n process.stdin.on('end', () => { resolve(data); });\n process.stdin.on('error', () => { resolve(''); });\n if (process.stdin.isTTY) resolve('');\n });\n }\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class HookStdoutSink {\n write(bytes: string): void { process.stdout.write(bytes); }\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class HookProcessExit {\n exit(code: number): void {\n // webpieces-disable no-process-exit-outside-main -- this port is the hook protocol's terminal boundary.\n process.exit(code);\n }\n}\n"]}
package/src/index.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ export { AiType, AI_TYPES, AI_TYPE_UNKNOWN, HookMode, ToolKind, AgentEventKind, NormalizedEdit, NormalizedToolInput, NormalizedBashInput, FileOperation, AgentHookEvent, } from './protocol';
2
+ export { HookOutcome, HookArgs, HookTerminated } from './outcome';
3
+ export { denyJson } from './response';
4
+ export { HookStdinSource, HookStdoutSink, HookProcessExit } from './hook-ports';
5
+ export { HookEvaluator } from './hook-evaluator';
6
+ export { HookApp, HookBootFailure } from './hook-app';
7
+ export { RuleScope, IsLineDisabled, Violation, EditContext, FileContext, BashContext, Rule, PlainRule, RuleGroup, BlockedResult } from './core/types';
8
+ export * from './core/fix-hint';
9
+ export * from './core/rule-base';
10
+ export * from './core/custom-rule-adapter';
11
+ export * from './core/strip-ts-noise';
12
+ export * from './core/disable-directives';
13
+ export * from './core/build-context';
14
+ export * from './core/report';
15
+ export * from './core/effective-tree';
16
+ export * from './core/command-scan';
17
+ export * from './core/target-tree';
18
+ export * from './core/excluded-paths';
19
+ export * from './core/delete-scoped-rules';
20
+ export * from './core/apply-patch-parse';
21
+ export * from './core/shell-read-parity';
22
+ export * from './core/glob';
23
+ export * from './adapters/agent-payload';
24
+ export * from './adapters/detect-ai';
25
+ export * from './adapters/agent-adapters';
26
+ export * from './adapters/claude-code-adapter';
27
+ export * from './adapters/codex-adapter';
28
+ export * from './core/root-manifest';
29
+ export * from './core/rule-evaluation';
30
+ export * from './core/rules/shell-segment-scan';
31
+ export * from './core/file-evaluation';
package/src/index.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlockedResult = exports.RuleGroup = exports.BashContext = exports.FileContext = exports.EditContext = exports.Violation = exports.HookBootFailure = exports.HookApp = exports.HookEvaluator = exports.HookProcessExit = exports.HookStdoutSink = exports.HookStdinSource = exports.denyJson = exports.HookTerminated = exports.HookArgs = exports.HookOutcome = exports.AgentHookEvent = exports.FileOperation = exports.NormalizedBashInput = exports.NormalizedToolInput = exports.NormalizedEdit = exports.AI_TYPE_UNKNOWN = exports.AI_TYPES = void 0;
4
+ const tslib_1 = require("tslib");
5
+ var protocol_1 = require("./protocol");
6
+ Object.defineProperty(exports, "AI_TYPES", { enumerable: true, get: function () { return protocol_1.AI_TYPES; } });
7
+ Object.defineProperty(exports, "AI_TYPE_UNKNOWN", { enumerable: true, get: function () { return protocol_1.AI_TYPE_UNKNOWN; } });
8
+ Object.defineProperty(exports, "NormalizedEdit", { enumerable: true, get: function () { return protocol_1.NormalizedEdit; } });
9
+ Object.defineProperty(exports, "NormalizedToolInput", { enumerable: true, get: function () { return protocol_1.NormalizedToolInput; } });
10
+ Object.defineProperty(exports, "NormalizedBashInput", { enumerable: true, get: function () { return protocol_1.NormalizedBashInput; } });
11
+ Object.defineProperty(exports, "FileOperation", { enumerable: true, get: function () { return protocol_1.FileOperation; } });
12
+ Object.defineProperty(exports, "AgentHookEvent", { enumerable: true, get: function () { return protocol_1.AgentHookEvent; } });
13
+ var outcome_1 = require("./outcome");
14
+ Object.defineProperty(exports, "HookOutcome", { enumerable: true, get: function () { return outcome_1.HookOutcome; } });
15
+ Object.defineProperty(exports, "HookArgs", { enumerable: true, get: function () { return outcome_1.HookArgs; } });
16
+ Object.defineProperty(exports, "HookTerminated", { enumerable: true, get: function () { return outcome_1.HookTerminated; } });
17
+ var response_1 = require("./response");
18
+ Object.defineProperty(exports, "denyJson", { enumerable: true, get: function () { return response_1.denyJson; } });
19
+ var hook_ports_1 = require("./hook-ports");
20
+ Object.defineProperty(exports, "HookStdinSource", { enumerable: true, get: function () { return hook_ports_1.HookStdinSource; } });
21
+ Object.defineProperty(exports, "HookStdoutSink", { enumerable: true, get: function () { return hook_ports_1.HookStdoutSink; } });
22
+ Object.defineProperty(exports, "HookProcessExit", { enumerable: true, get: function () { return hook_ports_1.HookProcessExit; } });
23
+ var hook_evaluator_1 = require("./hook-evaluator");
24
+ Object.defineProperty(exports, "HookEvaluator", { enumerable: true, get: function () { return hook_evaluator_1.HookEvaluator; } });
25
+ var hook_app_1 = require("./hook-app");
26
+ Object.defineProperty(exports, "HookApp", { enumerable: true, get: function () { return hook_app_1.HookApp; } });
27
+ Object.defineProperty(exports, "HookBootFailure", { enumerable: true, get: function () { return hook_app_1.HookBootFailure; } });
28
+ var types_1 = require("./core/types");
29
+ Object.defineProperty(exports, "Violation", { enumerable: true, get: function () { return types_1.Violation; } });
30
+ Object.defineProperty(exports, "EditContext", { enumerable: true, get: function () { return types_1.EditContext; } });
31
+ Object.defineProperty(exports, "FileContext", { enumerable: true, get: function () { return types_1.FileContext; } });
32
+ Object.defineProperty(exports, "BashContext", { enumerable: true, get: function () { return types_1.BashContext; } });
33
+ Object.defineProperty(exports, "RuleGroup", { enumerable: true, get: function () { return types_1.RuleGroup; } });
34
+ Object.defineProperty(exports, "BlockedResult", { enumerable: true, get: function () { return types_1.BlockedResult; } });
35
+ tslib_1.__exportStar(require("./core/fix-hint"), exports);
36
+ tslib_1.__exportStar(require("./core/rule-base"), exports);
37
+ tslib_1.__exportStar(require("./core/custom-rule-adapter"), exports);
38
+ tslib_1.__exportStar(require("./core/strip-ts-noise"), exports);
39
+ tslib_1.__exportStar(require("./core/disable-directives"), exports);
40
+ tslib_1.__exportStar(require("./core/build-context"), exports);
41
+ tslib_1.__exportStar(require("./core/report"), exports);
42
+ tslib_1.__exportStar(require("./core/effective-tree"), exports);
43
+ tslib_1.__exportStar(require("./core/command-scan"), exports);
44
+ tslib_1.__exportStar(require("./core/target-tree"), exports);
45
+ tslib_1.__exportStar(require("./core/excluded-paths"), exports);
46
+ tslib_1.__exportStar(require("./core/delete-scoped-rules"), exports);
47
+ tslib_1.__exportStar(require("./core/apply-patch-parse"), exports);
48
+ tslib_1.__exportStar(require("./core/shell-read-parity"), exports);
49
+ tslib_1.__exportStar(require("./core/glob"), exports);
50
+ tslib_1.__exportStar(require("./adapters/agent-payload"), exports);
51
+ tslib_1.__exportStar(require("./adapters/detect-ai"), exports);
52
+ tslib_1.__exportStar(require("./adapters/agent-adapters"), exports);
53
+ tslib_1.__exportStar(require("./adapters/claude-code-adapter"), exports);
54
+ tslib_1.__exportStar(require("./adapters/codex-adapter"), exports);
55
+ tslib_1.__exportStar(require("./core/root-manifest"), exports);
56
+ tslib_1.__exportStar(require("./core/rule-evaluation"), exports);
57
+ tslib_1.__exportStar(require("./core/rules/shell-segment-scan"), exports);
58
+ tslib_1.__exportStar(require("./core/file-evaluation"), exports);
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/index.ts"],"names":[],"mappings":";;;;AAAA,uCAGoB;AAFR,oGAAA,QAAQ,OAAA;AAAE,2GAAA,eAAe,OAAA;AACjC,0GAAA,cAAc,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,yGAAA,aAAa,OAAA;AAAE,0GAAA,cAAc,OAAA;AAE3F,qCAAkE;AAAzD,sGAAA,WAAW,OAAA;AAAE,mGAAA,QAAQ,OAAA;AAAE,yGAAA,cAAc,OAAA;AAC9C,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,2CAAgF;AAAvE,6GAAA,eAAe,OAAA;AAAE,4GAAA,cAAc,OAAA;AAAE,6GAAA,eAAe,OAAA;AACzD,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AACtB,uCAAsD;AAA7C,mGAAA,OAAO,OAAA;AAAE,2GAAA,eAAe,OAAA;AAEjC,sCAAsJ;AAAlH,kGAAA,SAAS,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAmB,kGAAA,SAAS,OAAA;AAAE,sGAAA,aAAa,OAAA;AAC/H,0DAAgC;AAChC,2DAAiC;AACjC,qEAA2C;AAC3C,gEAAsC;AACtC,oEAA0C;AAC1C,+DAAqC;AACrC,wDAA8B;AAC9B,gEAAsC;AACtC,8DAAoC;AACpC,6DAAmC;AACnC,gEAAsC;AACtC,qEAA2C;AAC3C,mEAAyC;AACzC,mEAAyC;AACzC,sDAA4B;AAC5B,mEAAyC;AACzC,+DAAqC;AACrC,oEAA0C;AAC1C,yEAA+C;AAC/C,mEAAyC;AACzC,+DAAqC;AACrC,iEAAuC;AACvC,0EAAgD;AAChD,iEAAuC","sourcesContent":["export {\n AiType, AI_TYPES, AI_TYPE_UNKNOWN, HookMode, ToolKind, AgentEventKind,\n NormalizedEdit, NormalizedToolInput, NormalizedBashInput, FileOperation, AgentHookEvent,\n} from './protocol';\nexport { HookOutcome, HookArgs, HookTerminated } from './outcome';\nexport { denyJson } from './response';\nexport { HookStdinSource, HookStdoutSink, HookProcessExit } from './hook-ports';\nexport { HookEvaluator } from './hook-evaluator';\nexport { HookApp, HookBootFailure } from './hook-app';\n\nexport { RuleScope, IsLineDisabled, Violation, EditContext, FileContext, BashContext, Rule, PlainRule, RuleGroup, BlockedResult } from './core/types';\nexport * from './core/fix-hint';\nexport * from './core/rule-base';\nexport * from './core/custom-rule-adapter';\nexport * from './core/strip-ts-noise';\nexport * from './core/disable-directives';\nexport * from './core/build-context';\nexport * from './core/report';\nexport * from './core/effective-tree';\nexport * from './core/command-scan';\nexport * from './core/target-tree';\nexport * from './core/excluded-paths';\nexport * from './core/delete-scoped-rules';\nexport * from './core/apply-patch-parse';\nexport * from './core/shell-read-parity';\nexport * from './core/glob';\nexport * from './adapters/agent-payload';\nexport * from './adapters/detect-ai';\nexport * from './adapters/agent-adapters';\nexport * from './adapters/claude-code-adapter';\nexport * from './adapters/codex-adapter';\nexport * from './core/root-manifest';\nexport * from './core/rule-evaluation';\nexport * from './core/rules/shell-segment-scan';\nexport * from './core/file-evaluation';\n"]}
@@ -0,0 +1,14 @@
1
+ import { HookMode } from './protocol';
2
+ export declare class HookOutcome {
3
+ readonly stdout: string;
4
+ readonly exitCode: number;
5
+ constructor(stdout: string, exitCode: number);
6
+ }
7
+ export declare class HookArgs {
8
+ readonly mode: HookMode;
9
+ constructor(mode: HookMode);
10
+ }
11
+ export declare class HookTerminated extends Error {
12
+ readonly outcome: HookOutcome;
13
+ constructor(outcome: HookOutcome);
14
+ }
package/src/outcome.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HookTerminated = exports.HookArgs = exports.HookOutcome = void 0;
4
+ class HookOutcome {
5
+ stdout;
6
+ exitCode;
7
+ constructor(stdout, exitCode) { this.stdout = stdout; this.exitCode = exitCode; }
8
+ }
9
+ exports.HookOutcome = HookOutcome;
10
+ class HookArgs {
11
+ mode;
12
+ constructor(mode) { this.mode = mode; }
13
+ }
14
+ exports.HookArgs = HookArgs;
15
+ class HookTerminated extends Error {
16
+ outcome;
17
+ constructor(outcome) { super('hook invocation terminated'); this.outcome = outcome; }
18
+ }
19
+ exports.HookTerminated = HookTerminated;
20
+ //# sourceMappingURL=outcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../../../../packages/tooling/hook-runtime/src/outcome.ts"],"names":[],"mappings":";;;AAEA,MAAa,WAAW;IACX,MAAM,CAAS;IACf,QAAQ,CAAS;IAC1B,YAAY,MAAc,EAAE,QAAgB,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC;CACpG;AAJD,kCAIC;AAED,MAAa,QAAQ;IACR,IAAI,CAAW;IACxB,YAAY,IAAc,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC;CACpD;AAHD,4BAGC;AAED,MAAa,cAAe,SAAQ,KAAK;IAC5B,OAAO,CAAc;IAC9B,YAAY,OAAoB,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CACrG;AAHD,wCAGC","sourcesContent":["import { HookMode } from './protocol';\n\nexport class HookOutcome {\n readonly stdout: string;\n readonly exitCode: number;\n constructor(stdout: string, exitCode: number) { this.stdout = stdout; this.exitCode = exitCode; }\n}\n\nexport class HookArgs {\n readonly mode: HookMode;\n constructor(mode: HookMode) { this.mode = mode; }\n}\n\nexport class HookTerminated extends Error {\n readonly outcome: HookOutcome;\n constructor(outcome: HookOutcome) { super('hook invocation terminated'); this.outcome = outcome; }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ /** Coding harness that produced an agent-hook call. */
2
+ export type AiType = 'claude-code' | 'codex';
3
+ export declare const AI_TYPES: readonly AiType[];
4
+ export declare const AI_TYPE_UNKNOWN = "unknown";
5
+ export type HookMode = 'rules' | 'guards' | 'all';
6
+ export type ToolKind = 'Write' | 'Edit' | 'MultiEdit' | 'Read' | 'Delete';
7
+ export type AgentEventKind = 'File' | 'Bash' | 'Read' | 'Ignored';
8
+ export declare class NormalizedEdit {
9
+ readonly oldString: string;
10
+ readonly newString: string;
11
+ constructor(oldString: string, newString: string);
12
+ }
13
+ export declare class NormalizedToolInput {
14
+ readonly filePath: string;
15
+ readonly edits: readonly NormalizedEdit[];
16
+ constructor(filePath: string, edits: readonly NormalizedEdit[]);
17
+ }
18
+ export declare class NormalizedBashInput {
19
+ readonly command: string;
20
+ constructor(command: string);
21
+ }
22
+ export declare class FileOperation {
23
+ readonly toolKind: ToolKind;
24
+ readonly input: NormalizedToolInput;
25
+ constructor(toolKind: ToolKind, input: NormalizedToolInput);
26
+ }
27
+ /** One normalized hook event, independent of the source harness and rule product. */
28
+ export declare class AgentHookEvent {
29
+ readonly aiType: AiType;
30
+ readonly kind: AgentEventKind;
31
+ readonly rawToolName: string;
32
+ readonly cwd: string;
33
+ readonly sessionId: string;
34
+ readonly agentId: string;
35
+ readonly agentType: string;
36
+ readonly files: readonly FileOperation[];
37
+ readonly bash: NormalizedBashInput | null;
38
+ readonly reads: readonly string[];
39
+ constructor(aiType: AiType, kind: AgentEventKind, rawToolName: string, cwd: string, sessionId: string, agentId: string, agentType: string, files: readonly FileOperation[], bash: NormalizedBashInput | null, reads: readonly string[]);
40
+ }