@hiro-c/agent-gate 1.0.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 (154) hide show
  1. package/AGENTS.md +76 -0
  2. package/LICENSE +21 -0
  3. package/README.md +205 -0
  4. package/dist/adapters/Adapter.d.ts +32 -0
  5. package/dist/adapters/Adapter.d.ts.map +1 -0
  6. package/dist/adapters/Adapter.js +2 -0
  7. package/dist/adapters/claude-code/adapter.d.ts +3 -0
  8. package/dist/adapters/claude-code/adapter.d.ts.map +1 -0
  9. package/dist/adapters/claude-code/adapter.js +45 -0
  10. package/dist/adapters/claude-code/transcript.d.ts +16 -0
  11. package/dist/adapters/claude-code/transcript.d.ts.map +1 -0
  12. package/dist/adapters/claude-code/transcript.js +104 -0
  13. package/dist/adapters/cursor/adapter.d.ts +3 -0
  14. package/dist/adapters/cursor/adapter.d.ts.map +1 -0
  15. package/dist/adapters/cursor/adapter.js +89 -0
  16. package/dist/adapters/index.d.ts +8 -0
  17. package/dist/adapters/index.d.ts.map +1 -0
  18. package/dist/adapters/index.js +20 -0
  19. package/dist/cli/agent-gate.d.ts +13 -0
  20. package/dist/cli/agent-gate.d.ts.map +1 -0
  21. package/dist/cli/agent-gate.js +226 -0
  22. package/dist/cli/installer.d.ts +21 -0
  23. package/dist/cli/installer.d.ts.map +1 -0
  24. package/dist/cli/installer.js +71 -0
  25. package/dist/collector/collectClaudeMd.d.ts +3 -0
  26. package/dist/collector/collectClaudeMd.d.ts.map +1 -0
  27. package/dist/collector/collectClaudeMd.js +87 -0
  28. package/dist/collector/collectRuleSources.d.ts +3 -0
  29. package/dist/collector/collectRuleSources.d.ts.map +1 -0
  30. package/dist/collector/collectRuleSources.js +151 -0
  31. package/dist/config/AgentGateConfig.d.ts +18 -0
  32. package/dist/config/AgentGateConfig.d.ts.map +1 -0
  33. package/dist/config/AgentGateConfig.js +34 -0
  34. package/dist/config/Config.d.ts +26 -0
  35. package/dist/config/Config.d.ts.map +1 -0
  36. package/dist/config/Config.js +25 -0
  37. package/dist/config/PluginConfigLoader.d.ts +3 -0
  38. package/dist/config/PluginConfigLoader.d.ts.map +1 -0
  39. package/dist/config/PluginConfigLoader.js +85 -0
  40. package/dist/config/defineConfig.d.ts +23 -0
  41. package/dist/config/defineConfig.d.ts.map +1 -0
  42. package/dist/config/defineConfig.js +10 -0
  43. package/dist/contracts/schemas/hookDataSchema.d.ts +7 -0
  44. package/dist/contracts/schemas/hookDataSchema.d.ts.map +1 -0
  45. package/dist/contracts/schemas/hookDataSchema.js +9 -0
  46. package/dist/contracts/types/Action.d.ts +23 -0
  47. package/dist/contracts/types/Action.d.ts.map +1 -0
  48. package/dist/contracts/types/Action.js +2 -0
  49. package/dist/contracts/types/ClaudeMdFile.d.ts +5 -0
  50. package/dist/contracts/types/ClaudeMdFile.d.ts.map +1 -0
  51. package/dist/contracts/types/ClaudeMdFile.js +2 -0
  52. package/dist/contracts/types/HookData.d.ts +6 -0
  53. package/dist/contracts/types/HookData.d.ts.map +1 -0
  54. package/dist/contracts/types/HookData.js +2 -0
  55. package/dist/contracts/types/ModelClient.d.ts +4 -0
  56. package/dist/contracts/types/ModelClient.d.ts.map +1 -0
  57. package/dist/contracts/types/ModelClient.js +2 -0
  58. package/dist/contracts/types/RuleSource.d.ts +7 -0
  59. package/dist/contracts/types/RuleSource.d.ts.map +1 -0
  60. package/dist/contracts/types/RuleSource.js +2 -0
  61. package/dist/contracts/types/SessionContext.d.ts +25 -0
  62. package/dist/contracts/types/SessionContext.d.ts.map +1 -0
  63. package/dist/contracts/types/SessionContext.js +2 -0
  64. package/dist/contracts/types/ValidationResult.d.ts +5 -0
  65. package/dist/contracts/types/ValidationResult.d.ts.map +1 -0
  66. package/dist/contracts/types/ValidationResult.js +2 -0
  67. package/dist/daemon/client.d.ts +17 -0
  68. package/dist/daemon/client.d.ts.map +1 -0
  69. package/dist/daemon/client.js +59 -0
  70. package/dist/daemon/protocol.d.ts +17 -0
  71. package/dist/daemon/protocol.d.ts.map +1 -0
  72. package/dist/daemon/protocol.js +8 -0
  73. package/dist/daemon/server.d.ts +27 -0
  74. package/dist/daemon/server.d.ts.map +1 -0
  75. package/dist/daemon/server.js +100 -0
  76. package/dist/deterministic/defaultRules.d.ts +11 -0
  77. package/dist/deterministic/defaultRules.d.ts.map +1 -0
  78. package/dist/deterministic/defaultRules.js +33 -0
  79. package/dist/deterministic/engine.d.ts +11 -0
  80. package/dist/deterministic/engine.d.ts.map +1 -0
  81. package/dist/deterministic/engine.js +12 -0
  82. package/dist/deterministic/factories.d.ts +20 -0
  83. package/dist/deterministic/factories.d.ts.map +1 -0
  84. package/dist/deterministic/factories.js +56 -0
  85. package/dist/deterministic/rules/preventBashSecretWrite.d.ts +3 -0
  86. package/dist/deterministic/rules/preventBashSecretWrite.d.ts.map +1 -0
  87. package/dist/deterministic/rules/preventBashSecretWrite.js +75 -0
  88. package/dist/deterministic/rules/preventForcePushMain.d.ts +7 -0
  89. package/dist/deterministic/rules/preventForcePushMain.d.ts.map +1 -0
  90. package/dist/deterministic/rules/preventForcePushMain.js +85 -0
  91. package/dist/deterministic/rules/preventRmRfRoot.d.ts +3 -0
  92. package/dist/deterministic/rules/preventRmRfRoot.d.ts.map +1 -0
  93. package/dist/deterministic/rules/preventRmRfRoot.js +68 -0
  94. package/dist/deterministic/rules/preventSecretFileWrite.d.ts +7 -0
  95. package/dist/deterministic/rules/preventSecretFileWrite.d.ts.map +1 -0
  96. package/dist/deterministic/rules/preventSecretFileWrite.js +55 -0
  97. package/dist/deterministic/rules/preventSystemPathWrite.d.ts +3 -0
  98. package/dist/deterministic/rules/preventSystemPathWrite.d.ts.map +1 -0
  99. package/dist/deterministic/rules/preventSystemPathWrite.js +38 -0
  100. package/dist/deterministic/types.d.ts +20 -0
  101. package/dist/deterministic/types.d.ts.map +1 -0
  102. package/dist/deterministic/types.js +2 -0
  103. package/dist/doctor/findings.d.ts +15 -0
  104. package/dist/doctor/findings.d.ts.map +1 -0
  105. package/dist/doctor/findings.js +2 -0
  106. package/dist/doctor/formatFindings.d.ts +3 -0
  107. package/dist/doctor/formatFindings.d.ts.map +1 -0
  108. package/dist/doctor/formatFindings.js +37 -0
  109. package/dist/doctor/lintRuleSources.d.ts +4 -0
  110. package/dist/doctor/lintRuleSources.d.ts.map +1 -0
  111. package/dist/doctor/lintRuleSources.js +87 -0
  112. package/dist/hooks/processHookData.d.ts +37 -0
  113. package/dist/hooks/processHookData.d.ts.map +1 -0
  114. package/dist/hooks/processHookData.js +181 -0
  115. package/dist/index.d.ts +38 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +54 -0
  118. package/dist/observability/decisionLogger.d.ts +15 -0
  119. package/dist/observability/decisionLogger.d.ts.map +1 -0
  120. package/dist/observability/decisionLogger.js +20 -0
  121. package/dist/observability/eventBus.d.ts +15 -0
  122. package/dist/observability/eventBus.d.ts.map +1 -0
  123. package/dist/observability/eventBus.js +33 -0
  124. package/dist/observability/sinks/JsonlFileSink.d.ts +13 -0
  125. package/dist/observability/sinks/JsonlFileSink.d.ts.map +1 -0
  126. package/dist/observability/sinks/JsonlFileSink.js +36 -0
  127. package/dist/observability/sinks/Sink.d.ts +46 -0
  128. package/dist/observability/sinks/Sink.d.ts.map +1 -0
  129. package/dist/observability/sinks/Sink.js +2 -0
  130. package/dist/observability/stats.d.ts +14 -0
  131. package/dist/observability/stats.d.ts.map +1 -0
  132. package/dist/observability/stats.js +78 -0
  133. package/dist/validation/models/AnthropicApi.d.ts +9 -0
  134. package/dist/validation/models/AnthropicApi.d.ts.map +1 -0
  135. package/dist/validation/models/AnthropicApi.js +44 -0
  136. package/dist/validation/models/ClaudeCli.d.ts +10 -0
  137. package/dist/validation/models/ClaudeCli.d.ts.map +1 -0
  138. package/dist/validation/models/ClaudeCli.js +64 -0
  139. package/dist/validation/models/CompositeModelClient.d.ts +20 -0
  140. package/dist/validation/models/CompositeModelClient.d.ts.map +1 -0
  141. package/dist/validation/models/CompositeModelClient.js +53 -0
  142. package/dist/validation/prompts/context.d.ts +3 -0
  143. package/dist/validation/prompts/context.d.ts.map +1 -0
  144. package/dist/validation/prompts/context.js +29 -0
  145. package/dist/validation/prompts/response.d.ts +2 -0
  146. package/dist/validation/prompts/response.d.ts.map +1 -0
  147. package/dist/validation/prompts/response.js +20 -0
  148. package/dist/validation/prompts/system-prompt.d.ts +7 -0
  149. package/dist/validation/prompts/system-prompt.d.ts.map +1 -0
  150. package/dist/validation/prompts/system-prompt.js +69 -0
  151. package/dist/validation/validator.d.ts +5 -0
  152. package/dist/validation/validator.d.ts.map +1 -0
  153. package/dist/validation/validator.js +98 -0
  154. package/package.json +67 -0
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { ValidationResult } from '../contracts/types/ValidationResult';
3
+ import { Adapter } from '../adapters/Adapter';
4
+ export declare function run(input: string, adapter?: Adapter): Promise<ValidationResult>;
5
+ interface ParsedArgs {
6
+ positional: string[];
7
+ agentId: string;
8
+ showHelp: boolean;
9
+ showVersion: boolean;
10
+ }
11
+ export declare function parseArgs(args: string[]): ParsedArgs;
12
+ export {};
13
+ //# sourceMappingURL=agent-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-gate.d.ts","sourceRoot":"","sources":["../../src/cli/agent-gate.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAYtE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAsC7C,wBAAsB,GAAG,CACvB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAsGD,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAgCpD"}
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.run = run;
5
+ exports.parseArgs = parseArgs;
6
+ const fs_1 = require("fs");
7
+ const processHookData_1 = require("../hooks/processHookData");
8
+ const installer_1 = require("./installer");
9
+ const adapters_1 = require("../adapters");
10
+ const stats_1 = require("../observability/stats");
11
+ const decisionLogger_1 = require("../observability/decisionLogger");
12
+ const collectRuleSources_1 = require("../collector/collectRuleSources");
13
+ const lintRuleSources_1 = require("../doctor/lintRuleSources");
14
+ const formatFindings_1 = require("../doctor/formatFindings");
15
+ const server_1 = require("../daemon/server");
16
+ const client_1 = require("../daemon/client");
17
+ const protocol_1 = require("../daemon/protocol");
18
+ const HELP_TEXT = `agent-gate — runtime enforcer for AI coding agent rules
19
+
20
+ Usage:
21
+ agent-gate Run as a hook (reads JSON from stdin)
22
+ agent-gate --agent <id> Use the named adapter (default: claude-code)
23
+ agent-gate install Register the hook in ~/.claude/settings.json
24
+ agent-gate uninstall Remove the hook from ~/.claude/settings.json
25
+ agent-gate stats Summarize decisions from the log file
26
+ agent-gate lint Audit CLAUDE.md / AGENTS.md / etc. for AI-friendliness
27
+ agent-gate daemon Start the long-lived daemon (Unix socket)
28
+ agent-gate --help Show this help
29
+ agent-gate --version Show version
30
+
31
+ Adapters (use with --agent):
32
+ ${(0, adapters_1.availableAdapterIds)().join(', ')}
33
+
34
+ Environment:
35
+ AGENT_GATE_MODEL Validation model (default: claude-sonnet-4-6)
36
+ AGENT_GATE_API_KEY Use Anthropic API directly when set
37
+ AGENT_GATE_COOLDOWN Cooldown in seconds between AI validations
38
+ AGENT_GATE_DISABLED Set to "true" to disable the whole tool
39
+ AGENT_GATE_DISABLED_RULES Comma-separated rule ids to disable
40
+ AGENT_GATE_LOG Set to "1" to write decisions to ~/.agent-gate/log.jsonl
41
+ AGENT_GATE_DAEMON Set to "1" to route hook calls through the daemon
42
+ AGENT_GATE_SOCKET_PATH Daemon socket path (default: $TMPDIR/agent-gate.sock)
43
+ USE_SYSTEM_CLAUDE Set to "true" to force PATH claude binary
44
+ `;
45
+ async function run(input, adapter) {
46
+ return (0, processHookData_1.processHookData)(input, adapter ? { adapter } : undefined);
47
+ }
48
+ function runHookMode(adapter) {
49
+ let inputData = '';
50
+ process.stdin.setEncoding('utf8');
51
+ process.stdin.on('data', (chunk) => {
52
+ inputData += chunk;
53
+ });
54
+ process.stdin.on('end', async () => {
55
+ try {
56
+ if (process.env.AGENT_GATE_DAEMON === '1') {
57
+ const socketPath = process.env.AGENT_GATE_SOCKET_PATH ?? (0, protocol_1.defaultSocketPath)();
58
+ const resp = await (0, client_1.sendToDaemon)({ adapter: adapter.id, payload: inputData, cwd: process.cwd() }, { socketPath, timeoutMs: 2000 });
59
+ if (resp !== null) {
60
+ console.log(resp.output);
61
+ process.exit(0);
62
+ }
63
+ // Daemon unreachable: fall through to direct mode.
64
+ }
65
+ const result = await run(inputData, adapter);
66
+ console.log(adapter.formatResponse(result));
67
+ }
68
+ catch (error) {
69
+ console.error('agent-gate error:', error);
70
+ }
71
+ finally {
72
+ process.exit(0);
73
+ }
74
+ });
75
+ }
76
+ async function runDaemon() {
77
+ const socketPath = process.env.AGENT_GATE_SOCKET_PATH ?? (0, protocol_1.defaultSocketPath)();
78
+ const server = new server_1.DaemonServer({
79
+ socketPath,
80
+ handler: async (req) => {
81
+ const adapter = (0, adapters_1.getAdapter)(req.adapter);
82
+ if (!adapter) {
83
+ return {
84
+ output: JSON.stringify({
85
+ error: `unknown adapter: ${req.adapter}`,
86
+ }),
87
+ };
88
+ }
89
+ const result = await (0, processHookData_1.processHookData)(req.payload, {
90
+ adapter,
91
+ cwd: req.cwd,
92
+ });
93
+ return { output: adapter.formatResponse(result) };
94
+ },
95
+ });
96
+ await server.start();
97
+ console.log(`agent-gate daemon listening on ${socketPath}`);
98
+ const shutdown = async () => {
99
+ await server.stop();
100
+ process.exit(0);
101
+ };
102
+ process.on('SIGINT', () => void shutdown());
103
+ process.on('SIGTERM', () => void shutdown());
104
+ }
105
+ function runInstall() {
106
+ const rawScriptPath = process.argv[1] ?? '';
107
+ let effectivePath = '';
108
+ try {
109
+ if (rawScriptPath) {
110
+ effectivePath = (0, fs_1.realpathSync)(rawScriptPath);
111
+ }
112
+ }
113
+ catch {
114
+ effectivePath = '';
115
+ }
116
+ const hookCommand = (0, installer_1.resolveHookCommand)(effectivePath);
117
+ const settingsFile = (0, installer_1.defaultSettingsPath)();
118
+ (0, installer_1.installHook)(hookCommand, settingsFile);
119
+ console.log(`agent-gate installed.`);
120
+ console.log(` settings: ${settingsFile}`);
121
+ console.log(` command: ${hookCommand}`);
122
+ console.log(`Restart Claude Code to activate.`);
123
+ }
124
+ function runUninstall() {
125
+ const settingsFile = (0, installer_1.defaultSettingsPath)();
126
+ (0, installer_1.uninstallHook)(settingsFile);
127
+ console.log(`agent-gate uninstalled.`);
128
+ console.log(` settings: ${settingsFile}`);
129
+ console.log(`Restart Claude Code to deactivate.`);
130
+ }
131
+ function printVersion() {
132
+ const pkg = require('../../package.json');
133
+ console.log(pkg.version ?? 'unknown');
134
+ }
135
+ function parseArgs(args) {
136
+ let agentId = adapters_1.DEFAULT_ADAPTER_ID;
137
+ let showHelp = false;
138
+ let showVersion = false;
139
+ const positional = [];
140
+ for (let i = 0; i < args.length; i++) {
141
+ const a = args[i];
142
+ if (a === '--agent') {
143
+ const next = args[i + 1];
144
+ if (next) {
145
+ agentId = next;
146
+ i++;
147
+ }
148
+ continue;
149
+ }
150
+ if (a.startsWith('--agent=')) {
151
+ agentId = a.slice('--agent='.length);
152
+ continue;
153
+ }
154
+ if (a === '--help' || a === '-h' || a === 'help') {
155
+ showHelp = true;
156
+ continue;
157
+ }
158
+ if (a === '--version' || a === '-v') {
159
+ showVersion = true;
160
+ continue;
161
+ }
162
+ positional.push(a);
163
+ }
164
+ return { positional, agentId, showHelp, showVersion };
165
+ }
166
+ function main() {
167
+ const parsedArgs = parseArgs(process.argv.slice(2));
168
+ if (parsedArgs.showHelp) {
169
+ console.log(HELP_TEXT);
170
+ return;
171
+ }
172
+ if (parsedArgs.showVersion) {
173
+ printVersion();
174
+ return;
175
+ }
176
+ const adapter = (0, adapters_1.getAdapter)(parsedArgs.agentId);
177
+ if (!adapter) {
178
+ console.error(`Unknown adapter: ${parsedArgs.agentId}. Available: ${(0, adapters_1.availableAdapterIds)().join(', ')}`);
179
+ process.exit(1);
180
+ }
181
+ const subcommand = parsedArgs.positional[0];
182
+ switch (subcommand) {
183
+ case undefined:
184
+ runHookMode(adapter);
185
+ return;
186
+ case 'install':
187
+ runInstall();
188
+ return;
189
+ case 'uninstall':
190
+ runUninstall();
191
+ return;
192
+ case 'stats':
193
+ runStats();
194
+ return;
195
+ case 'lint':
196
+ runLint();
197
+ return;
198
+ case 'daemon':
199
+ void runDaemon();
200
+ return;
201
+ default:
202
+ console.error(`Unknown subcommand: ${subcommand}`);
203
+ console.error(HELP_TEXT);
204
+ process.exit(1);
205
+ }
206
+ }
207
+ function runStats() {
208
+ const stats = (0, stats_1.readStats)((0, decisionLogger_1.defaultLogPath)());
209
+ console.log((0, stats_1.formatStats)(stats));
210
+ }
211
+ function runLint() {
212
+ const sources = (0, collectRuleSources_1.collectRuleSources)(process.cwd());
213
+ if (sources.length === 0) {
214
+ console.log('No instruction files found (looked for CLAUDE.md, AGENTS.md, .cursorrules, .cursor/rules/*.mdc, .clinerules/*.md, .windsurf/rules/*.md, .github/copilot-instructions.md, CONVENTIONS.md).');
215
+ return;
216
+ }
217
+ const findings = (0, lintRuleSources_1.lintRuleSources)(sources);
218
+ console.log((0, formatFindings_1.formatFindings)(findings));
219
+ const hasError = findings.some((f) => f.severity === 'error');
220
+ if (hasError) {
221
+ process.exitCode = 1;
222
+ }
223
+ }
224
+ if (require.main === module) {
225
+ main();
226
+ }
@@ -0,0 +1,21 @@
1
+ export declare const DEFAULT_HOOK_MATCHER = "Edit|Write|Bash";
2
+ export interface HookCommandEntry {
3
+ type: string;
4
+ command: string;
5
+ }
6
+ export interface HookMatcherEntry {
7
+ matcher?: string;
8
+ hooks?: HookCommandEntry[];
9
+ }
10
+ export interface ClaudeSettings {
11
+ hooks?: {
12
+ PreToolUse?: HookMatcherEntry[];
13
+ [key: string]: HookMatcherEntry[] | undefined;
14
+ };
15
+ [key: string]: unknown;
16
+ }
17
+ export declare function defaultSettingsPath(): string;
18
+ export declare function resolveHookCommand(resolvedScriptPath: string): string;
19
+ export declare function installHook(hookCommand: string, settingsFile?: string, matcher?: string): ClaudeSettings;
20
+ export declare function uninstallHook(settingsFile?: string): ClaudeSettings;
21
+ //# sourceMappingURL=installer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/cli/installer.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,oBAAoB,CAAA;AAErD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAA;QAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,SAAS,CAAA;KAC9C,CAAA;IACD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,kBAAkB,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAKrE;AAwBD,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAA8B,EAC5C,OAAO,GAAE,MAA6B,GACrC,cAAc,CAiBhB;AAED,wBAAgB,aAAa,CAC3B,YAAY,GAAE,MAA8B,GAC3C,cAAc,CAyBhB"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_HOOK_MATCHER = void 0;
4
+ exports.defaultSettingsPath = defaultSettingsPath;
5
+ exports.resolveHookCommand = resolveHookCommand;
6
+ exports.installHook = installHook;
7
+ exports.uninstallHook = uninstallHook;
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const os_1 = require("os");
11
+ exports.DEFAULT_HOOK_MATCHER = 'Edit|Write|Bash';
12
+ function defaultSettingsPath() {
13
+ return (0, path_1.join)((0, os_1.homedir)(), '.claude', 'settings.json');
14
+ }
15
+ function resolveHookCommand(resolvedScriptPath) {
16
+ if (!resolvedScriptPath) {
17
+ return 'agent-gate';
18
+ }
19
+ return `node ${resolvedScriptPath}`;
20
+ }
21
+ function readSettings(settingsFile) {
22
+ if (!(0, fs_1.existsSync)(settingsFile)) {
23
+ return {};
24
+ }
25
+ const content = (0, fs_1.readFileSync)(settingsFile, 'utf-8');
26
+ if (content.trim() === '') {
27
+ return {};
28
+ }
29
+ return JSON.parse(content);
30
+ }
31
+ function writeSettings(settingsFile, settings) {
32
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(settingsFile), { recursive: true });
33
+ (0, fs_1.writeFileSync)(settingsFile, JSON.stringify(settings, null, 2) + '\n');
34
+ }
35
+ function isAgentGateEntry(entry) {
36
+ return (entry.hooks ?? []).some((h) => typeof h.command === 'string' && h.command.includes('agent-gate'));
37
+ }
38
+ function installHook(hookCommand, settingsFile = defaultSettingsPath(), matcher = exports.DEFAULT_HOOK_MATCHER) {
39
+ const settings = readSettings(settingsFile);
40
+ if (!settings.hooks)
41
+ settings.hooks = {};
42
+ const preToolUse = settings.hooks.PreToolUse ?? [];
43
+ const filtered = preToolUse.filter((entry) => !isAgentGateEntry(entry));
44
+ filtered.push({
45
+ matcher,
46
+ hooks: [{ type: 'command', command: hookCommand }],
47
+ });
48
+ settings.hooks.PreToolUse = filtered;
49
+ writeSettings(settingsFile, settings);
50
+ return settings;
51
+ }
52
+ function uninstallHook(settingsFile = defaultSettingsPath()) {
53
+ if (!(0, fs_1.existsSync)(settingsFile)) {
54
+ return {};
55
+ }
56
+ const settings = readSettings(settingsFile);
57
+ if (settings.hooks?.PreToolUse) {
58
+ const cleaned = settings.hooks.PreToolUse.filter((entry) => !isAgentGateEntry(entry));
59
+ if (cleaned.length === 0) {
60
+ delete settings.hooks.PreToolUse;
61
+ }
62
+ else {
63
+ settings.hooks.PreToolUse = cleaned;
64
+ }
65
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) {
66
+ delete settings.hooks;
67
+ }
68
+ }
69
+ writeSettings(settingsFile, settings);
70
+ return settings;
71
+ }
@@ -0,0 +1,3 @@
1
+ import { ClaudeMdFile } from '../contracts/types/ClaudeMdFile';
2
+ export declare function collectClaudeMd(cwd: string): ClaudeMdFile[];
3
+ //# sourceMappingURL=collectClaudeMd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectClaudeMd.d.ts","sourceRoot":"","sources":["../../src/collector/collectClaudeMd.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAA;AAe9D,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAY3D"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectClaudeMd = collectClaudeMd;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const EXCLUDED_DIRS = new Set([
7
+ 'node_modules',
8
+ '.git',
9
+ 'target',
10
+ '.venv',
11
+ 'vendor',
12
+ '__pycache__',
13
+ 'dist',
14
+ 'build',
15
+ ]);
16
+ const MAX_DEPTH = 3;
17
+ function collectClaudeMd(cwd) {
18
+ const upward = collectUpward(cwd);
19
+ const downward = collectDownward(cwd);
20
+ // Deduplicate: upward already includes cwd/CLAUDE.md
21
+ const upwardPaths = new Set(upward.map((f) => f.path));
22
+ const combined = [
23
+ ...upward,
24
+ ...downward.filter((f) => !upwardPaths.has(f.path)),
25
+ ];
26
+ return combined;
27
+ }
28
+ function collectUpward(cwd) {
29
+ const files = [];
30
+ let dir = cwd;
31
+ while (true) {
32
+ const claudeMdPath = (0, path_1.join)(dir, 'CLAUDE.md');
33
+ const content = readFileSafe(claudeMdPath);
34
+ if (content !== null) {
35
+ files.push({ path: claudeMdPath, content });
36
+ }
37
+ const parent = (0, path_1.dirname)(dir);
38
+ if (parent === dir)
39
+ break;
40
+ dir = parent;
41
+ }
42
+ return files;
43
+ }
44
+ function collectDownward(cwd) {
45
+ const files = [];
46
+ walkDir(cwd, 0, files);
47
+ return files;
48
+ }
49
+ function walkDir(dir, depth, files) {
50
+ if (depth > MAX_DEPTH)
51
+ return;
52
+ let entries;
53
+ try {
54
+ entries = (0, fs_1.readdirSync)(dir);
55
+ }
56
+ catch {
57
+ return;
58
+ }
59
+ for (const entry of entries) {
60
+ if (EXCLUDED_DIRS.has(entry))
61
+ continue;
62
+ const fullPath = (0, path_1.join)(dir, entry);
63
+ if (entry === 'CLAUDE.md') {
64
+ const content = readFileSafe(fullPath);
65
+ if (content !== null) {
66
+ files.push({ path: fullPath, content });
67
+ }
68
+ continue;
69
+ }
70
+ try {
71
+ if ((0, fs_1.statSync)(fullPath).isDirectory()) {
72
+ walkDir(fullPath, depth + 1, files);
73
+ }
74
+ }
75
+ catch {
76
+ // Skip inaccessible entries
77
+ }
78
+ }
79
+ }
80
+ function readFileSafe(filePath) {
81
+ try {
82
+ return (0, fs_1.readFileSync)(filePath, 'utf-8');
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
@@ -0,0 +1,3 @@
1
+ import { RuleSource } from '../contracts/types/RuleSource';
2
+ export declare function collectRuleSources(cwd: string): RuleSource[];
3
+ //# sourceMappingURL=collectRuleSources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectRuleSources.d.ts","sourceRoot":"","sources":["../../src/collector/collectRuleSources.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAkB,MAAM,+BAA+B,CAAA;AA4D1E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAM5D"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectRuleSources = collectRuleSources;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const EXCLUDED_DIRS = new Set([
7
+ 'node_modules',
8
+ '.git',
9
+ 'target',
10
+ '.venv',
11
+ 'vendor',
12
+ '__pycache__',
13
+ 'dist',
14
+ 'build',
15
+ ]);
16
+ const MAX_DEPTH = 3;
17
+ // Files looked up directly inside a candidate directory
18
+ const DIRECT_FILES = [
19
+ { filename: 'CLAUDE.md', kind: 'claude-md' },
20
+ { filename: 'AGENTS.md', kind: 'agents-md' },
21
+ { filename: '.cursorrules', kind: 'cursorrules' },
22
+ { filename: 'CONVENTIONS.md', kind: 'aider-conventions' },
23
+ ];
24
+ // Files looked up inside a known subdirectory of a candidate directory.
25
+ // e.g. `<dir>/.cursor/rules/*.mdc` ; the parentDir here is `.cursor/rules`
26
+ // or `.github` for copilot-instructions.md.
27
+ const NESTED_FILES = [
28
+ {
29
+ parentDir: '.cursor/rules',
30
+ fileMatcher: (name) => name.endsWith('.mdc'),
31
+ kind: 'cursor-mdc',
32
+ },
33
+ {
34
+ parentDir: '.clinerules',
35
+ fileMatcher: (name) => name.endsWith('.md'),
36
+ kind: 'clinerules',
37
+ },
38
+ {
39
+ parentDir: '.windsurf/rules',
40
+ fileMatcher: (name) => name.endsWith('.md'),
41
+ kind: 'windsurf-rule',
42
+ },
43
+ {
44
+ parentDir: '.github',
45
+ fileMatcher: (name) => name === 'copilot-instructions.md',
46
+ kind: 'copilot-instructions',
47
+ },
48
+ ];
49
+ function collectRuleSources(cwd) {
50
+ const upward = collectUpward(cwd);
51
+ const downward = collectDownward(cwd);
52
+ const upwardPaths = new Set(upward.map((f) => f.path));
53
+ return [...upward, ...downward.filter((f) => !upwardPaths.has(f.path))];
54
+ }
55
+ function collectUpward(cwd) {
56
+ const files = [];
57
+ let dir = cwd;
58
+ while (true) {
59
+ collectDirectFiles(dir, files);
60
+ collectNestedFiles(dir, files);
61
+ const parent = (0, path_1.dirname)(dir);
62
+ if (parent === dir)
63
+ break;
64
+ dir = parent;
65
+ }
66
+ return files;
67
+ }
68
+ function collectDownward(cwd) {
69
+ const files = [];
70
+ walkDir(cwd, 0, files);
71
+ return files;
72
+ }
73
+ function walkDir(dir, depth, files) {
74
+ if (depth > MAX_DEPTH)
75
+ return;
76
+ let entries;
77
+ try {
78
+ entries = (0, fs_1.readdirSync)(dir);
79
+ }
80
+ catch {
81
+ return;
82
+ }
83
+ for (const entry of entries) {
84
+ if (EXCLUDED_DIRS.has(entry))
85
+ continue;
86
+ const fullPath = (0, path_1.join)(dir, entry);
87
+ const directSpec = DIRECT_FILES.find((s) => s.filename === entry);
88
+ if (directSpec) {
89
+ const content = readFileSafe(fullPath);
90
+ if (content !== null) {
91
+ files.push({ path: fullPath, content, kind: directSpec.kind });
92
+ }
93
+ continue;
94
+ }
95
+ let isDir = false;
96
+ try {
97
+ isDir = (0, fs_1.statSync)(fullPath).isDirectory();
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ if (isDir) {
103
+ const nestedSpec = NESTED_FILES.find((s) => s.parentDir === entry);
104
+ if (nestedSpec) {
105
+ collectFromNestedDir(fullPath, nestedSpec, files);
106
+ }
107
+ walkDir(fullPath, depth + 1, files);
108
+ }
109
+ }
110
+ }
111
+ function collectDirectFiles(dir, files) {
112
+ for (const spec of DIRECT_FILES) {
113
+ const filePath = (0, path_1.join)(dir, spec.filename);
114
+ const content = readFileSafe(filePath);
115
+ if (content !== null) {
116
+ files.push({ path: filePath, content, kind: spec.kind });
117
+ }
118
+ }
119
+ }
120
+ function collectNestedFiles(dir, files) {
121
+ for (const spec of NESTED_FILES) {
122
+ const nestedDir = (0, path_1.join)(dir, spec.parentDir);
123
+ collectFromNestedDir(nestedDir, spec, files);
124
+ }
125
+ }
126
+ function collectFromNestedDir(nestedDir, spec, files) {
127
+ let entries;
128
+ try {
129
+ entries = (0, fs_1.readdirSync)(nestedDir);
130
+ }
131
+ catch {
132
+ return;
133
+ }
134
+ for (const entry of entries) {
135
+ if (!spec.fileMatcher(entry))
136
+ continue;
137
+ const fullPath = (0, path_1.join)(nestedDir, entry);
138
+ const content = readFileSafe(fullPath);
139
+ if (content !== null) {
140
+ files.push({ path: fullPath, content, kind: spec.kind });
141
+ }
142
+ }
143
+ }
144
+ function readFileSafe(filePath) {
145
+ try {
146
+ return (0, fs_1.readFileSync)(filePath, 'utf-8');
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
@@ -0,0 +1,18 @@
1
+ import { AgentGatePluginConfig } from './defineConfig';
2
+ /**
3
+ * The resolved configuration that the hook pipeline consumes.
4
+ *
5
+ * This is the same shape as AgentGatePluginConfig; both names exist
6
+ * so callers can pick the more descriptive one for their context.
7
+ */
8
+ export type AgentGateConfig = AgentGatePluginConfig;
9
+ /**
10
+ * Resolves the effective AgentGateConfig for the given cwd.
11
+ *
12
+ * Reads `.agent-gate.config.{ts,mts,mjs,cjs,js}` (preferred) or the
13
+ * legacy `.agent-gate.json`, walking upward from cwd. Merges the
14
+ * AGENT_GATE_DISABLED_RULES env var into disabledRules so command-line
15
+ * users can disable rules without touching the config file.
16
+ */
17
+ export declare function loadAgentGateConfig(cwd: string): AgentGateConfig;
18
+ //# sourceMappingURL=AgentGateConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentGateConfig.d.ts","sourceRoot":"","sources":["../../src/config/AgentGateConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAGtD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAA;AAanD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAWhE"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadAgentGateConfig = loadAgentGateConfig;
4
+ const PluginConfigLoader_1 = require("./PluginConfigLoader");
5
+ const ENV_DISABLED_RULES = 'AGENT_GATE_DISABLED_RULES';
6
+ function disabledFromEnv() {
7
+ const raw = process.env[ENV_DISABLED_RULES];
8
+ if (!raw)
9
+ return [];
10
+ return raw
11
+ .split(',')
12
+ .map((s) => s.trim())
13
+ .filter((s) => s.length > 0);
14
+ }
15
+ /**
16
+ * Resolves the effective AgentGateConfig for the given cwd.
17
+ *
18
+ * Reads `.agent-gate.config.{ts,mts,mjs,cjs,js}` (preferred) or the
19
+ * legacy `.agent-gate.json`, walking upward from cwd. Merges the
20
+ * AGENT_GATE_DISABLED_RULES env var into disabledRules so command-line
21
+ * users can disable rules without touching the config file.
22
+ */
23
+ function loadAgentGateConfig(cwd) {
24
+ const fromFile = (0, PluginConfigLoader_1.loadPluginConfig)(cwd);
25
+ const fromEnv = disabledFromEnv();
26
+ const combinedDisabled = new Set([
27
+ ...(fromFile.disabledRules ?? []),
28
+ ...fromEnv,
29
+ ]);
30
+ return {
31
+ ...fromFile,
32
+ disabledRules: Array.from(combinedDisabled),
33
+ };
34
+ }
@@ -0,0 +1,26 @@
1
+ export declare const DEFAULT_MODEL = "claude-sonnet-4-6";
2
+ export type ConfigOptions = {
3
+ model?: string;
4
+ apiKey?: string;
5
+ cooldown?: number;
6
+ disabled?: boolean;
7
+ useSystemClaude?: boolean;
8
+ /**
9
+ * Language for the AI validator's "reason" field.
10
+ * - "auto" (or undefined): match the dominant language of the instruction
11
+ * files, falling back to English when ambiguous.
12
+ * - "en", "ja", "zh", "ko", etc.: write reasons in that language.
13
+ */
14
+ reasonLang?: string;
15
+ };
16
+ export declare class Config {
17
+ readonly model: string;
18
+ readonly apiKey: string | undefined;
19
+ readonly cooldown: number;
20
+ readonly disabled: boolean;
21
+ readonly useSystemClaude: boolean;
22
+ readonly reasonLang: string | undefined;
23
+ constructor(options?: ConfigOptions);
24
+ get useApi(): boolean;
25
+ }
26
+ //# sourceMappingURL=Config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/config/Config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,sBAAsB,CAAA;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,qBAAa,MAAM;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;gBAE3B,OAAO,CAAC,EAAE,aAAa;IAUnC,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF"}