@vohongtho.infotech/code-intel 1.0.0 → 1.0.2

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Code Intelligence Platform
2
2
 
3
- [![npm version](https://img.shields.io/badge/npm-v1.0.0-blue)](https://www.npmjs.com/package/@vohongtho.infotech/code-intel)
3
+ [![npm version](https://img.shields.io/badge/npm-v1.0.1-blue)](https://www.npmjs.com/package/@vohongtho.infotech/code-intel)
4
4
 
5
5
  A static code analysis platform that builds a **Knowledge Graph** from your source code and makes it explorable through a Web UI, HTTP API, CLI, and MCP server.
6
6
 
@@ -31,7 +31,8 @@ A static code analysis platform that builds a **Knowledge Graph** from your sour
31
31
  - **Multi-language** — TypeScript, JavaScript, Python, Java, Go, C, C++, C#, Rust, PHP, Ruby, Swift, Kotlin, Dart (14 languages via tree-sitter AST)
32
32
  - **Incremental Analysis** — `--incremental` flag re-parses only git-changed/mtime-changed files; 10k-file repo with 3 changes: 288ms
33
33
  - **Parallel Analysis** — `--parallel` flag runs parse + resolve phases on worker threads for large repos
34
- - **AI Context Files** — auto-generates `AGENTS.md`, `CLAUDE.md`, `.github/copilot-instructions.md`, `.cursor/rules/code-intel.mdc`, and `.kiro/steering/code-intel.md` after every analysis with live stats, CLI reference, development workflows, and skill links — supporting Amp, Claude Code, Codex, Copilot, Cursor, Aider, Gemini, Kiro, Trae, Hermes, Factory, OpenCode, Pi, Antigravity, OpenClaw, and more
34
+ - **AI Context Files** — auto-generates `AGENTS.md`, `CLAUDE.md`, `.github/copilot-instructions.md`, `.cursor/rules/code-intel.mdc`, `.kiro/steering/code-intel.md`, `.clinerules`, `.windsurfrules`, `.kilocode/rules/code-intel-rules.md`, and `.agents/rules/code-intel-rules.md` after every analysis — supporting Amp, Claude Code, Codex, Copilot, Cursor, Aider, Gemini, Kiro, Trae, Hermes, Factory, OpenCode, Pi, Antigravity, OpenClaw, Cline, Windsurf, Kilo Code, and more
35
+ - **Agent Hook System** _(v1.0.2)_ — `code-intel setup` installs PreToolUse hooks for all major AI agents; when an agent runs `grep MyClass src/`, the `code-intel-hook` binary (~10KB, ~50ms startup) silently rewrites it to `code-intel search "MyClass"` — saving ~3,000 tokens per lookup; supports Claude Code, Cursor, Gemini CLI, GitHub Copilot (VS Code + CLI), OpenCode, OpenClaw; rules files for Cline/Roo Code, Windsurf, Kilo Code, Antigravity, Codex CLI
35
36
  - **Skill Files** — generates `.claude/skills/code-intel/` with per-cluster SKILL.md files (hot symbols, entry points, impact guidance) for AI assistants
36
37
  - **Repository Groups** — multi-repo / monorepo service tracking with workspace auto-discovery (npm, pnpm, Nx, Turborepo), contract extraction (OpenAPI, GraphQL, Protobuf), type-aware similarity scoring, and cross-repo dependency detection
37
38
  - **`.codeintelignore`** — exclude directories from analysis (like `.gitignore` but for code-intel)
@@ -51,6 +52,9 @@ A static code analysis platform that builds a **Knowledge Graph** from your sour
51
52
  - **Pipeline Profiling** _(v1.0)_ — `analyze --profile` writes `.code-intel/profile.json`; per-phase heap memory captured; bottleneck warning if any phase >50% of total; verbose timing table
52
53
  - **Load & Soak Tests** _(v1.0)_ — nightly CI load tests (1k/10k fixture repos), weekly soak tests (memory stability, watcher throughput), regression gate: >20% regression fails CI; `tests/perf/baseline.json` committed to repo
53
54
  - **Graceful Degradation** _(v1.0)_ — `X-Stale`/`X-Stale-Since` headers on DB outage; LLM-unavailable summarize skip; MCP tool timeout → `{ truncated: true }`; watcher crash recovery; worker crash retry
55
+ - **Token-Efficient MCP** _(v1.0.1)_ — compact JSON responses (null/undefined stripped); MCP tool defaults tuned for LLM sessions: `search`/`file_symbols`/`list_exports` default 10 results (was 50), `blast_radius`/`pr_impact` default 2 hops (was 5); `suggested_next_tools` opt-in via `CODE_INTEL_SUGGEST_NEXT_TOOLS=true`; ~63% fewer tokens per typical 5-tool session
56
+ - **Context Builder** _(v1.0.1)_ — `src/context/builder.ts` builds structured `[SUMMARY]` / `[LOGIC]` / `[RELATION]` / `[FOCUS CODE]` documents from seed symbols in ≤50% of v1.0.0 token cost; query-intent presets (`code`, `callers`, `architecture`, `auto`); adaptive snippets; cross-block dedup; `code-intel context <symbols...> --show-context`
57
+ - **Enforced Tool Policy in AI Context Files** _(v1.0.1)_ — `AGENTS.md`/`CLAUDE.md`/`copilot-instructions.md`/`.cursor/rules`/`.kiro/steering` now include a `TOOL POLICY: ENFORCED` block forbidding raw `grep`/`find`/`cat` in favour of `code-intel search` → `inspect` → `impact`; saves ~3,000 tokens per cold-file lookup
54
58
 
55
59
  ---
56
60
 
@@ -232,13 +236,15 @@ fixtures
232
236
 
233
237
  ## 🤖 MCP Setup (one-time)
234
238
 
235
- Run the one-time setup command to configure the MCP server for your AI editor (Claude Desktop / Claude Code):
239
+ Run the one-time setup command to configure the MCP server and install agent hooks:
236
240
 
237
241
  ```bash
238
242
  code-intel setup
239
243
  ```
240
244
 
241
- This writes the MCP server configuration to `~/.config/claude/claude_desktop_config.json`:
245
+ This does two things:
246
+
247
+ **1. MCP server** — writes `~/.config/claude/claude_desktop_config.json` so your editor can start the MCP server automatically:
242
248
 
243
249
  ```json
244
250
  {
@@ -251,6 +257,24 @@ This writes the MCP server configuration to `~/.config/claude/claude_desktop_con
251
257
  }
252
258
  ```
253
259
 
260
+ **2. Agent hooks** — installs PreToolUse hooks for every supported AI agent (idempotent, always safe to re-run):
261
+
262
+ | Agent | Hook type | What it does |
263
+ |-------|-----------|--------------|
264
+ | **Claude Code** | `~/.claude/settings.json` PreToolUse | Auto-rewrites grep/cat → code-intel search/inspect |
265
+ | **Cursor** | `~/.cursor/hooks.json` preToolUse | Auto-rewrites grep/cat → code-intel search/inspect |
266
+ | **Gemini CLI** | `~/.gemini/settings.json` BeforeTool | Auto-rewrites grep/cat → code-intel search/inspect |
267
+ | **GitHub Copilot** | `.github/hooks/code-intel-rewrite.json` | VS Code Chat: transparent rewrite; CLI: deny + suggestion |
268
+ | **OpenCode** | `~/.config/opencode/plugins/code-intel.ts` | Plugin: intercepts before tool execution |
269
+ | **OpenClaw** | `~/.openclaw/extensions/code-intel/` | Plugin: `before_tool_call` intercept |
270
+ | **Cline / Roo Code** | `.clinerules` | Prompt-level policy (also written by `analyze`) |
271
+ | **Windsurf** | `.windsurfrules` | Prompt-level policy (also written by `analyze`) |
272
+ | **Kilo Code** | `.kilocode/rules/code-intel-rules.md` | Prompt-level policy (also written by `analyze`) |
273
+ | **Antigravity** | `.agents/rules/code-intel-rules.md` | Prompt-level policy (also written by `analyze`) |
274
+ | **Codex CLI** | `AGENTS.md` (appended) | Prompt-level policy (also written by `analyze`) |
275
+
276
+ > **How hooks work:** The `code-intel-hook` binary (~10KB, ~50ms startup) intercepts every Bash tool call. When the agent tries to run `grep MyClass src/`, the hook silently rewrites it to `code-intel search "MyClass"` — saving ~3,000 tokens per lookup and returning structured graph results instead of raw text.
277
+
254
278
  After setup, the MCP server starts automatically when your AI editor launches, giving it direct access to all code-intel tools.
255
279
 
256
280
  ---
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+ import process2 from 'process';
3
+
4
+ var SOURCE_EXT = /* @__PURE__ */ new Set([
5
+ "ts",
6
+ "tsx",
7
+ "js",
8
+ "jsx",
9
+ "mjs",
10
+ "cjs",
11
+ "py",
12
+ "pyi",
13
+ "rs",
14
+ "go",
15
+ "java",
16
+ "kt",
17
+ "kts",
18
+ "rb",
19
+ "cs",
20
+ "cpp",
21
+ "cc",
22
+ "cxx",
23
+ "c",
24
+ "h",
25
+ "hpp",
26
+ "swift",
27
+ "scala",
28
+ "php"
29
+ ]);
30
+ var REGEX_META_RE = /[.*+?^${}()|[\]\\]/;
31
+ var SYMBOL_ID_RE = /^[A-Za-z_$][A-Za-z0-9_$.-]*$/;
32
+ function isSymbolLike(term) {
33
+ return SYMBOL_ID_RE.test(term) && !REGEX_META_RE.test(term);
34
+ }
35
+ function isSourceFile(filePath) {
36
+ const dot = filePath.lastIndexOf(".");
37
+ if (dot === -1) return false;
38
+ return SOURCE_EXT.has(filePath.slice(dot + 1).toLowerCase());
39
+ }
40
+ function fileStem(filePath) {
41
+ const base = filePath.includes("/") ? filePath.slice(filePath.lastIndexOf("/") + 1) : filePath;
42
+ const dot = base.lastIndexOf(".");
43
+ return dot === -1 ? base : base.slice(0, dot);
44
+ }
45
+ function extractGrepSymbol(cmd) {
46
+ if (/(?:^|\s)-[a-zA-Z]*[cvlLoZ]/.test(cmd)) return null;
47
+ const quoted = cmd.match(/(?:^|\s)["']([^"']+)["'](?:\s|$)/);
48
+ if (quoted) {
49
+ const term = quoted[1];
50
+ return isSymbolLike(term) ? term : null;
51
+ }
52
+ const tokens = cmd.split(/\s+/).slice(1);
53
+ for (const tok of tokens) {
54
+ if (tok.startsWith("-")) continue;
55
+ if (tok.startsWith("/")) continue;
56
+ if (tok.startsWith("./") || tok.startsWith("../")) continue;
57
+ if (tok.includes("/")) continue;
58
+ if (tok === "." || tok === "..") continue;
59
+ return isSymbolLike(tok) ? tok : null;
60
+ }
61
+ return null;
62
+ }
63
+ function rewriteCommand(cmd) {
64
+ try {
65
+ const trimmed = cmd.trim();
66
+ if (!trimmed) return null;
67
+ if (trimmed.startsWith("code-intel ") || trimmed === "code-intel") return null;
68
+ if (/(?:&&|\|\||;|\|)/.test(trimmed)) return null;
69
+ if (/^grep\s/.test(trimmed)) {
70
+ const sym = extractGrepSymbol(trimmed);
71
+ if (sym) return `code-intel search "${sym}"`;
72
+ return null;
73
+ }
74
+ if (/^rg\s/.test(trimmed)) {
75
+ if (/\s--files(?:\s|$)/.test(trimmed)) return null;
76
+ if (/\s--files-with-matches(?:\s|$)/.test(trimmed)) return null;
77
+ if (/\s--type-not\b/.test(trimmed)) return null;
78
+ const sym = extractGrepSymbol(trimmed);
79
+ if (sym) return `code-intel search "${sym}"`;
80
+ return null;
81
+ }
82
+ if (/^cat\s/.test(trimmed)) {
83
+ const m = trimmed.match(/^cat\s+(\S+)$/);
84
+ if (!m) return null;
85
+ const filePath = m[1];
86
+ if (filePath === "-") return null;
87
+ if (filePath.startsWith(">")) return null;
88
+ if (!isSourceFile(filePath)) return null;
89
+ return `code-intel inspect ${fileStem(filePath)}`;
90
+ }
91
+ if (/^(?:head|tail)\s/.test(trimmed)) {
92
+ if (/\s-f\b/.test(trimmed)) return null;
93
+ const m = trimmed.match(
94
+ /^(?:head|tail)\s+(?:-\d+\s+|-n\s+\d+\s+|--lines=\d+\s+)?(\S+)$/
95
+ );
96
+ if (!m) return null;
97
+ const filePath = m[1];
98
+ if (!isSourceFile(filePath)) return null;
99
+ return `code-intel inspect ${fileStem(filePath)}`;
100
+ }
101
+ return null;
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+ function runCopilotHook() {
107
+ process2.on("uncaughtException", () => process2.exit(0));
108
+ process2.on("unhandledRejection", () => process2.exit(0));
109
+ let input = "";
110
+ process2.stdin.setEncoding("utf-8");
111
+ process2.stdin.on("data", (chunk) => {
112
+ input += chunk;
113
+ });
114
+ process2.stdin.on("end", () => {
115
+ try {
116
+ if (!input.trim()) {
117
+ process2.exit(0);
118
+ }
119
+ const parsed = JSON.parse(input);
120
+ if (parsed.tool_name && !parsed.toolName) {
121
+ const toolName = parsed.tool_name;
122
+ if (!["runTerminalCommand", "Bash", "bash"].includes(toolName)) {
123
+ process2.exit(0);
124
+ }
125
+ const toolInput = parsed.tool_input;
126
+ const cmd = toolInput?.command;
127
+ if (!cmd || !cmd.trim()) {
128
+ process2.exit(0);
129
+ }
130
+ const rewritten = rewriteCommand(cmd);
131
+ if (rewritten === null || rewritten === cmd) {
132
+ process2.exit(0);
133
+ }
134
+ const response = {
135
+ hookSpecificOutput: {
136
+ hookEventName: "PreToolUse",
137
+ permissionDecision: "allow",
138
+ permissionDecisionReason: "code-intel: semantic search replaces grep/cat",
139
+ updatedInput: { ...toolInput, command: rewritten }
140
+ }
141
+ };
142
+ process2.stdout.write(JSON.stringify(response));
143
+ process2.exit(0);
144
+ }
145
+ if (parsed.toolName) {
146
+ if (parsed.toolName.toLowerCase() !== "bash") {
147
+ process2.exit(0);
148
+ }
149
+ let cmd = "";
150
+ try {
151
+ const toolArgs = JSON.parse(parsed.toolArgs);
152
+ cmd = toolArgs.command ?? "";
153
+ } catch {
154
+ process2.exit(0);
155
+ }
156
+ if (!cmd.trim()) {
157
+ process2.exit(0);
158
+ }
159
+ const rewritten = rewriteCommand(cmd);
160
+ if (rewritten === null || rewritten === cmd) {
161
+ process2.exit(0);
162
+ }
163
+ const response = {
164
+ permissionDecision: "deny",
165
+ permissionDecisionReason: `Use code-intel: ${rewritten}`
166
+ };
167
+ process2.stdout.write(JSON.stringify(response));
168
+ process2.exit(0);
169
+ }
170
+ process2.exit(0);
171
+ } catch {
172
+ process2.exit(0);
173
+ }
174
+ });
175
+ process2.stdin.on("error", () => process2.exit(0));
176
+ }
177
+ function runCursorHook() {
178
+ process2.on("uncaughtException", () => {
179
+ process2.stdout.write("{}");
180
+ process2.exit(0);
181
+ });
182
+ process2.on("unhandledRejection", () => {
183
+ process2.stdout.write("{}");
184
+ process2.exit(0);
185
+ });
186
+ let input = "";
187
+ process2.stdin.setEncoding("utf-8");
188
+ process2.stdin.on("data", (chunk) => {
189
+ input += chunk;
190
+ });
191
+ process2.stdin.on("end", () => {
192
+ try {
193
+ if (!input.trim()) {
194
+ process2.stdout.write("{}");
195
+ process2.exit(0);
196
+ }
197
+ const parsed = JSON.parse(input);
198
+ const cmd = parsed?.tool_input?.command;
199
+ if (typeof cmd !== "string" || !cmd.trim()) {
200
+ process2.stdout.write("{}");
201
+ process2.exit(0);
202
+ }
203
+ const rewritten = rewriteCommand(cmd);
204
+ if (rewritten === null || rewritten === cmd) {
205
+ process2.stdout.write("{}");
206
+ process2.exit(0);
207
+ }
208
+ const response = {
209
+ permission: "allow",
210
+ updated_input: { command: rewritten }
211
+ };
212
+ process2.stdout.write(JSON.stringify(response));
213
+ process2.exit(0);
214
+ } catch {
215
+ process2.stdout.write("{}");
216
+ process2.exit(0);
217
+ }
218
+ });
219
+ process2.stdin.on("error", () => {
220
+ process2.stdout.write("{}");
221
+ process2.exit(0);
222
+ });
223
+ }
224
+ function runGeminiHook() {
225
+ process2.on("uncaughtException", () => {
226
+ process2.stdout.write('{"decision":"allow"}');
227
+ process2.exit(0);
228
+ });
229
+ process2.on("unhandledRejection", () => {
230
+ process2.stdout.write('{"decision":"allow"}');
231
+ process2.exit(0);
232
+ });
233
+ let input = "";
234
+ process2.stdin.setEncoding("utf-8");
235
+ process2.stdin.on("data", (chunk) => {
236
+ input += chunk;
237
+ });
238
+ process2.stdin.on("end", () => {
239
+ try {
240
+ if (!input.trim()) {
241
+ process2.stdout.write('{"decision":"allow"}');
242
+ process2.exit(0);
243
+ }
244
+ const parsed = JSON.parse(input);
245
+ const toolName = parsed?.tool_name;
246
+ if (toolName && toolName !== "run_shell_command") {
247
+ process2.stdout.write('{"decision":"allow"}');
248
+ process2.exit(0);
249
+ }
250
+ const cmd = parsed?.tool_input?.command;
251
+ if (typeof cmd !== "string" || !cmd.trim()) {
252
+ process2.stdout.write('{"decision":"allow"}');
253
+ process2.exit(0);
254
+ }
255
+ const rewritten = rewriteCommand(cmd);
256
+ if (rewritten === null || rewritten === cmd) {
257
+ process2.stdout.write('{"decision":"allow"}');
258
+ process2.exit(0);
259
+ }
260
+ const response = {
261
+ decision: "allow",
262
+ hookSpecificOutput: {
263
+ tool_input: { command: rewritten }
264
+ }
265
+ };
266
+ process2.stdout.write(JSON.stringify(response));
267
+ process2.exit(0);
268
+ } catch {
269
+ process2.stdout.write('{"decision":"allow"}');
270
+ process2.exit(0);
271
+ }
272
+ });
273
+ process2.stdin.on("error", () => {
274
+ process2.stdout.write('{"decision":"allow"}');
275
+ process2.exit(0);
276
+ });
277
+ }
278
+ function runClaudeHook() {
279
+ process2.on("uncaughtException", () => process2.exit(0));
280
+ process2.on("unhandledRejection", () => process2.exit(0));
281
+ let input = "";
282
+ process2.stdin.setEncoding("utf-8");
283
+ process2.stdin.on("data", (chunk) => {
284
+ input += chunk;
285
+ });
286
+ process2.stdin.on("end", () => {
287
+ try {
288
+ if (!input.trim()) {
289
+ process2.exit(0);
290
+ }
291
+ const parsed = JSON.parse(input);
292
+ const cmd = parsed?.tool_input?.command;
293
+ if (typeof cmd !== "string" || !cmd.trim()) {
294
+ process2.exit(0);
295
+ }
296
+ const rewritten = rewriteCommand(cmd);
297
+ if (rewritten === null || rewritten === cmd) {
298
+ process2.exit(0);
299
+ }
300
+ const response = {
301
+ hookSpecificOutput: {
302
+ hookEventName: "PreToolUse",
303
+ permissionDecision: "allow",
304
+ permissionDecisionReason: "code-intel: semantic search replaces grep/cat",
305
+ updatedInput: {
306
+ ...parsed.tool_input,
307
+ command: rewritten
308
+ }
309
+ }
310
+ };
311
+ process2.stdout.write(JSON.stringify(response));
312
+ process2.exit(0);
313
+ } catch {
314
+ process2.exit(0);
315
+ }
316
+ });
317
+ process2.stdin.on("error", () => {
318
+ process2.exit(0);
319
+ });
320
+ }
321
+
322
+ // src/cli/hook.ts
323
+ var agent = process.argv[2];
324
+ if (!agent) {
325
+ process.stderr.write("[code-intel-hook] Usage: code-intel-hook <agent>\n");
326
+ process.stderr.write(" Agents: claude, copilot, cursor, gemini\n");
327
+ process.exit(0);
328
+ }
329
+ switch (agent) {
330
+ case "claude":
331
+ runClaudeHook();
332
+ break;
333
+ case "copilot":
334
+ runCopilotHook();
335
+ break;
336
+ case "cursor":
337
+ runCursorHook();
338
+ break;
339
+ case "gemini":
340
+ runGeminiHook();
341
+ break;
342
+ default:
343
+ process.stderr.write(`[code-intel-hook] Unknown agent: ${agent}
344
+ `);
345
+ process.exit(0);
346
+ }
347
+ //# sourceMappingURL=hook.js.map
348
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/hook-rewriter.ts","../../src/cli/hook.ts"],"names":["process"],"mappings":"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","file":"hook.js","sourcesContent":["/**\n * hook-rewriter.ts\n *\n * Single source of truth for all command rewrite rules.\n * Equivalent to RTK's src/discover/rules.rs + src/discover/registry.rs.\n *\n * Called by:\n * - `code-intel rewrite <cmd>` → exit 0 (match) | exit 1 (no match)\n * - `code-intel hook claude` → reads stdin PreToolUse JSON, writes response JSON\n *\n * ZERO heavy imports — this module must load in <50ms.\n * No DB, no graph, no pipeline, no config, no filesystem access.\n */\n\nimport process from 'node:process';\n\n// ─── Source file extensions ───────────────────────────────────────────────────\n// Only these are worth inspecting via code-intel inspect.\n// All other file types (config, docs, logs, data) pass through unchanged.\n\nconst SOURCE_EXT = new Set([\n 'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs',\n 'py', 'pyi',\n 'rs',\n 'go',\n 'java', 'kt', 'kts',\n 'rb',\n 'cs',\n 'cpp', 'cc', 'cxx', 'c', 'h', 'hpp',\n 'swift',\n 'scala',\n 'php',\n]);\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Regex metacharacters that indicate a true regex pattern, not a symbol name.\n * If a grep/rg search term contains any of these, we don't rewrite.\n */\nconst REGEX_META_RE = /[.*+?^${}()|[\\]\\\\]/;\n\n/**\n * A \"symbol-like\" identifier: starts with letter/$/_,\n * followed by alphanumeric/$/_/./- characters.\n * CamelCase, snake_case, dot.notation all qualify.\n */\nconst SYMBOL_ID_RE = /^[A-Za-z_$][A-Za-z0-9_$.-]*$/;\n\nfunction isSymbolLike(term: string): boolean {\n return SYMBOL_ID_RE.test(term) && !REGEX_META_RE.test(term);\n}\n\nfunction isSourceFile(filePath: string): boolean {\n const dot = filePath.lastIndexOf('.');\n if (dot === -1) return false;\n return SOURCE_EXT.has(filePath.slice(dot + 1).toLowerCase());\n}\n\n/** Extract the filename stem (basename without extension). */\nfunction fileStem(filePath: string): string {\n const base = filePath.includes('/')\n ? filePath.slice(filePath.lastIndexOf('/') + 1)\n : filePath;\n const dot = base.lastIndexOf('.');\n return dot === -1 ? base : base.slice(0, dot);\n}\n\n/**\n * Extract a symbol-like search term from a grep or rg command.\n * Returns null if:\n * - The term looks like a regex (contains metacharacters)\n * - The flags indicate non-symbol-discovery use (-c, -v, -l, -L, -o, -Z)\n * - No clear search term can be identified\n *\n * Flags that trigger passthrough (not symbol search):\n * -c count matches per file\n * -v invert match (select non-matching lines)\n * -l list files that have matches\n * -L list files that have NO matches\n * -o print only the matching part\n * -Z use NUL byte as line separator\n */\nfunction extractGrepSymbol(cmd: string): string | null {\n // Reject if any flag group contains a non-symbol-discovery flag character.\n // This regex looks for a dash followed by any combo of letters containing c/v/l/L/o/Z.\n // We use a word-boundary aware pattern: -rnic is ok (r,n,i,I are fine),\n // -rnc is not (c = count). The flag can appear anywhere after the command name.\n if (/(?:^|\\s)-[a-zA-Z]*[cvlLoZ]/.test(cmd)) return null;\n\n // Try double-quoted or single-quoted term first: grep \"Symbol\" or grep 'Symbol'\n const quoted = cmd.match(/(?:^|\\s)[\"']([^\"']+)[\"'](?:\\s|$)/);\n if (quoted) {\n const term = quoted[1];\n return isSymbolLike(term) ? term : null;\n }\n\n // Try unquoted term: skip command name and flags to find first bare identifier\n const tokens = cmd.split(/\\s+/).slice(1); // skip 'grep' or 'rg'\n for (const tok of tokens) {\n if (tok.startsWith('-')) continue; // skip flags\n if (tok.startsWith('/')) continue; // skip absolute paths\n if (tok.startsWith('./') || tok.startsWith('../')) continue; // skip relative paths\n if (tok.includes('/')) continue; // skip paths with slashes\n if (tok === '.' || tok === '..') continue; // skip dir shortcuts\n // First non-flag, non-path token is the search pattern\n return isSymbolLike(tok) ? tok : null;\n }\n\n return null;\n}\n\n// ─── Core rewrite function ────────────────────────────────────────────────────\n\n/**\n * Attempt to rewrite `cmd` to its code-intel equivalent.\n *\n * Returns the rewritten command string, or null if no rule matched.\n * NEVER throws — all rule errors silently degrade to null (passthrough).\n */\nexport function rewriteCommand(cmd: string): string | null {\n try {\n const trimmed = cmd.trim();\n if (!trimmed) return null;\n\n // ── Guard 1: Already a code-intel command (idempotency + anti-loop) ──────\n // This prevents: code-intel search \"X\" → being rewritten again.\n if (trimmed.startsWith('code-intel ') || trimmed === 'code-intel') return null;\n\n // ── Guard 2: Compound commands — pass through in v1 ──────────────────────\n // We don't attempt to split && || ; | in v1.\n // A simple character scan is safe here since we check before any rule matching.\n if (/(?:&&|\\|\\||;|\\|)/.test(trimmed)) return null;\n\n // ── Rule 1: grep → code-intel search ─────────────────────────────────────\n if (/^grep\\s/.test(trimmed)) {\n const sym = extractGrepSymbol(trimmed);\n if (sym) return `code-intel search \"${sym}\"`;\n return null;\n }\n\n // ── Rule 2: rg → code-intel search ───────────────────────────────────────\n if (/^rg\\s/.test(trimmed)) {\n // Reject rg-specific structural flags (not symbol search)\n if (/\\s--files(?:\\s|$)/.test(trimmed)) return null;\n if (/\\s--files-with-matches(?:\\s|$)/.test(trimmed)) return null;\n if (/\\s--type-not\\b/.test(trimmed)) return null;\n const sym = extractGrepSymbol(trimmed);\n if (sym) return `code-intel search \"${sym}\"`;\n return null;\n }\n\n // ── Rule 3: cat <single-source-file> → code-intel inspect <stem> ─────────\n if (/^cat\\s/.test(trimmed)) {\n // Must be exactly: cat <one-token> — no multi-file, no options\n const m = trimmed.match(/^cat\\s+(\\S+)$/);\n if (!m) return null; // multi-file or flags present\n const filePath = m[1];\n if (filePath === '-') return null; // stdin passthrough\n if (filePath.startsWith('>')) return null; // write redirect\n if (!isSourceFile(filePath)) return null; // not a source file\n return `code-intel inspect ${fileStem(filePath)}`;\n }\n\n // ── Rule 4: head/tail <single-source-file> → code-intel inspect <stem> ───\n if (/^(?:head|tail)\\s/.test(trimmed)) {\n if (/\\s-f\\b/.test(trimmed)) return null; // tail -f = live follow, not inspection\n // Accept common forms:\n // head <file>\n // head -N <file>\n // head -n N <file>\n // head --lines=N <file>\n const m = trimmed.match(\n /^(?:head|tail)\\s+(?:-\\d+\\s+|-n\\s+\\d+\\s+|--lines=\\d+\\s+)?(\\S+)$/,\n );\n if (!m) return null;\n const filePath = m[1];\n if (!isSourceFile(filePath)) return null;\n return `code-intel inspect ${fileStem(filePath)}`;\n }\n\n return null;\n } catch {\n // Any unexpected error → silently pass through.\n // A rule must NEVER cause the hook to crash.\n return null;\n }\n}\n\n// ─── CLI: `code-intel rewrite <cmd>` ─────────────────────────────────────────\n\n/**\n * Entry point for `code-intel rewrite <cmd>`.\n *\n * Exit codes (mirrors RTK exit code protocol):\n * 0 + stdout → rewrite found; hook may auto-allow the rewritten command\n * 1 → no match; hook passes through unchanged\n */\nexport function runRewrite(cmd: string): never {\n const rewritten = rewriteCommand(cmd.trim());\n if (rewritten === null) {\n process.exit(1);\n }\n process.stdout.write(rewritten);\n process.exit(0);\n}\n\n// ─── CLI: `code-intel hook claude` ───────────────────────────────────────────\n\n/**\n * Entry point for `code-intel hook claude`.\n *\n * Reads a Claude Code PreToolUse JSON payload from stdin.\n * If the command matches a rewrite rule, writes a JSON response to stdout\n * that tells Claude Code to use the rewritten command instead.\n * If no rule matches, exits 0 with empty stdout (pass through unchanged).\n *\n * NON-BLOCKING GUARANTEE: This function ALWAYS exits 0.\n * A non-zero exit would cause Claude Code to block the agent's command.\n * We must never block, even on errors.\n *\n * Claude Code PreToolUse input format (stdin):\n * { \"tool_name\": \"Bash\", \"tool_input\": { \"command\": \"grep ...\" } }\n *\n * Response format when rewriting (stdout):\n * {\n * \"hookSpecificOutput\": {\n * \"hookEventName\": \"PreToolUse\",\n * \"permissionDecision\": \"allow\",\n * \"permissionDecisionReason\": \"...\",\n * \"updatedInput\": { \"command\": \"code-intel search ...\" }\n * }\n * }\n */\n/**\n * `code-intel-hook cursor` — reads Cursor preToolUse stdin JSON, writes response JSON.\n *\n * Cursor format differs from Claude Code:\n * Input: { \"tool_input\": { \"command\": \"grep ...\" } }\n * Output (rewrite): { \"permission\": \"allow\", \"updated_input\": { \"command\": \"...\" } }\n * Output (no match): {} (Cursor requires JSON on all paths, even pass-through)\n *\n * ALWAYS exits 0 — non-blocking guarantee.\n */\n/**\n * `code-intel-hook copilot` — handles both VS Code Copilot Chat and Copilot CLI.\n *\n * Auto-detects input format:\n * VS Code Chat: snake_case { tool_name, tool_input: { command } }\n * → updatedInput (transparent rewrite)\n * Copilot CLI: camelCase { toolName, toolArgs: '{\"command\":\"...\"}' }\n * → deny-with-suggestion (CLI doesn't support updatedInput)\n *\n * ALWAYS exits 0 — non-blocking guarantee.\n */\nexport function runCopilotHook(): void {\n process.on('uncaughtException', () => process.exit(0));\n process.on('unhandledRejection', () => process.exit(0));\n\n let input = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.on('data', (chunk: string) => { input += chunk; });\n process.stdin.on('end', () => {\n try {\n if (!input.trim()) { process.exit(0); }\n\n const parsed = JSON.parse(input) as Record<string, unknown>;\n\n // ── VS Code Copilot Chat format (snake_case) ─────────────────────────\n // tool_name: \"runTerminalCommand\" | \"Bash\"\n // tool_input: { command: \"...\" }\n if (parsed.tool_name && !parsed.toolName) {\n const toolName = parsed.tool_name as string;\n if (!['runTerminalCommand', 'Bash', 'bash'].includes(toolName)) {\n process.exit(0);\n }\n const toolInput = parsed.tool_input as Record<string, unknown> | undefined;\n const cmd = toolInput?.command as string | undefined;\n if (!cmd || !cmd.trim()) { process.exit(0); }\n\n const rewritten = rewriteCommand(cmd);\n if (rewritten === null || rewritten === cmd) { process.exit(0); }\n\n // VS Code supports updatedInput — transparent rewrite\n const response = {\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: 'code-intel: semantic search replaces grep/cat',\n updatedInput: { ...toolInput, command: rewritten },\n },\n };\n process.stdout.write(JSON.stringify(response));\n process.exit(0);\n }\n\n // ── Copilot CLI format (camelCase, toolArgs JSON-stringified) ─────────\n // toolName: \"bash\"\n // toolArgs: '{\"command\":\"...\"}'\n if (parsed.toolName) {\n if ((parsed.toolName as string).toLowerCase() !== 'bash') { process.exit(0); }\n let cmd = '';\n try {\n const toolArgs = JSON.parse(parsed.toolArgs as string) as Record<string, unknown>;\n cmd = (toolArgs.command as string) ?? '';\n } catch { process.exit(0); }\n\n if (!cmd.trim()) { process.exit(0); }\n const rewritten = rewriteCommand(cmd);\n if (rewritten === null || rewritten === cmd) { process.exit(0); }\n\n // CLI doesn't support updatedInput — use deny-with-suggestion instead\n const response = {\n permissionDecision: 'deny',\n permissionDecisionReason: `Use code-intel: ${rewritten}`,\n };\n process.stdout.write(JSON.stringify(response));\n process.exit(0);\n }\n\n process.exit(0);\n } catch {\n process.exit(0);\n }\n });\n process.stdin.on('error', () => process.exit(0));\n}\n\nexport function runCursorHook(): void {\n process.on('uncaughtException', () => { process.stdout.write('{}'); process.exit(0); });\n process.on('unhandledRejection', () => { process.stdout.write('{}'); process.exit(0); });\n\n let input = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.on('data', (chunk: string) => { input += chunk; });\n process.stdin.on('end', () => {\n try {\n if (!input.trim()) { process.stdout.write('{}'); process.exit(0); }\n\n const parsed = JSON.parse(input) as {\n tool_input?: { command?: string; [key: string]: unknown };\n [key: string]: unknown;\n };\n\n const cmd = parsed?.tool_input?.command;\n if (typeof cmd !== 'string' || !cmd.trim()) { process.stdout.write('{}'); process.exit(0); }\n\n const rewritten = rewriteCommand(cmd);\n if (rewritten === null || rewritten === cmd) { process.stdout.write('{}'); process.exit(0); }\n\n // Cursor response format: permission + updated_input (no hookSpecificOutput)\n const response = {\n permission: 'allow',\n updated_input: { command: rewritten },\n };\n process.stdout.write(JSON.stringify(response));\n process.exit(0);\n } catch {\n process.stdout.write('{}');\n process.exit(0);\n }\n });\n process.stdin.on('error', () => { process.stdout.write('{}'); process.exit(0); });\n}\n\n/**\n * `code-intel-hook gemini` — reads Gemini CLI BeforeTool stdin JSON, writes response JSON.\n *\n * Gemini format differs from Claude Code and Cursor:\n * Input: { \"tool_name\": \"run_shell_command\", \"tool_input\": { \"command\": \"grep ...\" } }\n * Output (rewrite): { \"decision\": \"allow\", \"hookSpecificOutput\": { \"tool_input\": { \"command\": \"...\" } } }\n * Output (no match): { \"decision\": \"allow\" }\n *\n * ALWAYS exits 0 — non-blocking guarantee.\n */\nexport function runGeminiHook(): void {\n process.on('uncaughtException', () => { process.stdout.write('{\"decision\":\"allow\"}'); process.exit(0); });\n process.on('unhandledRejection', () => { process.stdout.write('{\"decision\":\"allow\"}'); process.exit(0); });\n\n let input = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.on('data', (chunk: string) => { input += chunk; });\n process.stdin.on('end', () => {\n try {\n if (!input.trim()) { process.stdout.write('{\"decision\":\"allow\"}'); process.exit(0); }\n\n const parsed = JSON.parse(input) as {\n tool_name?: string;\n tool_input?: { command?: string; [key: string]: unknown };\n [key: string]: unknown;\n };\n\n // Gemini uses tool_name = \"run_shell_command\"\n const toolName = parsed?.tool_name;\n if (toolName && toolName !== 'run_shell_command') {\n process.stdout.write('{\"decision\":\"allow\"}');\n process.exit(0);\n }\n\n const cmd = parsed?.tool_input?.command;\n if (typeof cmd !== 'string' || !cmd.trim()) {\n process.stdout.write('{\"decision\":\"allow\"}');\n process.exit(0);\n }\n\n const rewritten = rewriteCommand(cmd);\n if (rewritten === null || rewritten === cmd) {\n process.stdout.write('{\"decision\":\"allow\"}');\n process.exit(0);\n }\n\n // Gemini response format: decision + hookSpecificOutput.tool_input\n const response = {\n decision: 'allow',\n hookSpecificOutput: {\n tool_input: { command: rewritten },\n },\n };\n process.stdout.write(JSON.stringify(response));\n process.exit(0);\n } catch {\n process.stdout.write('{\"decision\":\"allow\"}');\n process.exit(0);\n }\n });\n process.stdin.on('error', () => { process.stdout.write('{\"decision\":\"allow\"}'); process.exit(0); });\n}\n\nexport function runClaudeHook(): void {\n // Override any previously registered exit(1) handlers.\n // These run before the global handlers in main.ts if anything throws\n // before we reach the try/catch inside stdin.on('end').\n process.on('uncaughtException', () => process.exit(0));\n process.on('unhandledRejection', () => process.exit(0));\n\n let input = '';\n process.stdin.setEncoding('utf-8');\n\n process.stdin.on('data', (chunk: string) => {\n input += chunk;\n });\n\n process.stdin.on('end', () => {\n try {\n if (!input.trim()) {\n process.exit(0);\n }\n\n // Parse the PreToolUse JSON payload\n const parsed = JSON.parse(input) as {\n tool_input?: { command?: string; [key: string]: unknown };\n [key: string]: unknown;\n };\n\n const cmd = parsed?.tool_input?.command;\n if (typeof cmd !== 'string' || !cmd.trim()) {\n process.exit(0);\n }\n\n const rewritten = rewriteCommand(cmd);\n\n // No match or identical → pass through (empty stdout + exit 0)\n if (rewritten === null || rewritten === cmd) {\n process.exit(0);\n }\n\n // Build the Claude Code PreToolUse response\n const response = {\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: 'code-intel: semantic search replaces grep/cat',\n updatedInput: {\n ...parsed.tool_input,\n command: rewritten,\n },\n },\n };\n\n process.stdout.write(JSON.stringify(response));\n process.exit(0);\n } catch {\n // JSON parse error, unexpected payload shape, or anything else.\n // ALWAYS exit 0 — never block the agent's command execution.\n process.exit(0);\n }\n });\n\n process.stdin.on('error', () => {\n process.exit(0); // stdin read error → pass through\n });\n}\n","#!/usr/bin/env node\n/**\n * hook.ts — Tiny standalone hook entry point.\n *\n * This is compiled to dist/cli/hook.js and published as the `code-intel-hook`\n * binary. It imports ONLY hook-rewriter.ts — nothing else.\n *\n * Why separate from main.ts:\n * main.ts bundles 714KB of OTel, DB, graph, pipeline code.\n * Even with IS_HOOK_MODE guards, Node.js still parses the entire bundle\n * (~850ms startup). This hook binary is ~5KB and starts in ~50ms.\n *\n * This binary is installed into ~/.claude/settings.json by `code-intel setup`:\n * { \"command\": \"code-intel-hook claude\" }\n */\n\nimport { runClaudeHook, runCopilotHook, runCursorHook, runGeminiHook } from './hook-rewriter.js';\n\nconst agent = process.argv[2];\n\nif (!agent) {\n process.stderr.write('[code-intel-hook] Usage: code-intel-hook <agent>\\n');\n process.stderr.write(' Agents: claude, copilot, cursor, gemini\\n');\n process.exit(0);\n}\n\nswitch (agent) {\n case 'claude':\n runClaudeHook();\n break;\n case 'copilot':\n runCopilotHook();\n break;\n case 'cursor':\n runCursorHook();\n break;\n case 'gemini':\n runGeminiHook();\n break;\n default:\n process.stderr.write(`[code-intel-hook] Unknown agent: ${agent}\\n`);\n process.exit(0);\n}\n"]}