@vohongtho.infotech/code-intel 1.0.2 → 1.0.3

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.1-blue)](https://www.npmjs.com/package/@vohongtho.infotech/code-intel)
3
+ [![npm version](https://img.shields.io/badge/npm-v1.0.3-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,7 @@ 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`, `.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
34
+ - **AI Context Files** — auto-generates `AGENTS.md` and `CLAUDE.md` after every analysis (universal, always written); agent-specific files (`.github/copilot-instructions.md`, `.cursor/rules/code-intel.mdc`, `.windsurfrules`) are written **only when the corresponding binary is detected on PATH** — never pollutes projects with files for tools the user doesn't have; `code-intel setup` writes all files for user-initiated one-time setup — supporting Amp, Claude Code, Codex, Copilot, Cursor, Aider, Gemini, Kiro, OpenCode, Trae, Hermes, Factory, Pi, Antigravity, OpenClaw, Cline, Windsurf, Kilo Code, and more
35
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
36
36
  - **Skill Files** — generates `.claude/skills/code-intel/` with per-cluster SKILL.md files (hot symbols, entry points, impact guidance) for AI assistants
37
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
@@ -55,6 +55,15 @@ A static code analysis platform that builds a **Knowledge Graph** from your sour
55
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
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
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
58
+ - **Inspect Disambiguation** _(v1.0.3)_ — `inspect` now detects when a symbol name exists in multiple files; CLI shows a multi-match warning listing all candidates with source previews and suggests `--file`; MCP returns a `disambiguation` JSON object instead of silently resolving the wrong class
59
+ - **`--file` flag for CLI** _(v1.0.3)_ — `inspect <symbol> --file <pattern>` and `impact <symbol> --file <pattern>` select the correct implementation when the same name exists across multiple modules (e.g. `login` in API vs CMS, `requestAccessToken` in JWT vs Token)
60
+ - **`code-intel read <file>`** _(v1.0.3)_ — new CLI command reads raw source lines from any indexed file using partial path matching; supports `--start`/`--end` line range (max 300 lines per call); essential for reading config files with no indexable symbols
61
+ - **MCP `get_source` tool** _(v1.0.3)_ — MCP equivalent of `read`; reads raw numbered source lines from any indexed file by partial path; accepts `start_line`/`end_line`; returns `hasMore` flag for pagination
62
+ - **BM25 Class-Name Boosting** _(v1.0.3)_ — file basename (class name) now weighted strongly in BM25 documents; queries like `"Token requestAccessToken"` rank the Token class above JWT for same-named methods; content window expanded 1 000 → 1 500 chars
63
+ - **Claude Code Plugin — PostToolUse & Augment** _(v1.0.3)_ — `code-intel hook claude` now handles both `PreToolUse` (command rewrite + graph context injection) and `PostToolUse` (stale-index notification after git commit/merge/pull via HEAD vs `.code-intel/meta.json` comparison); new `code-intel augment -- <pattern>` command injects compact symbol context (`in:N out:N` call counts, snippet) into the agent before grep/rg/cat calls; `code-intel hook <agent>` now supports `claude | cursor | gemini | copilot`
64
+ - **`analyze` no longer creates unwanted agent files** _(v1.0.3-patch)_ — strict binary-only gate: no binary = no file, period; pre-existing files from old runs are NOT updated unless the binary is present; `AGENTS.md` and `CLAUDE.md` always written; Kiro, Cline, Kilo Code, Antigravity files are setup-only and never touched by `analyze`
65
+ - **Cross-OS Agent Detection & MCP Registration** _(v1.0.3)_ — `init` and `setup` agent detection and MCP config writing now work correctly on Ubuntu, macOS, and Windows; `commandExists()` uses `execFileSync('which'/'where')` instead of a broken shell-composed string; OS-aware path helpers resolve the right config directories; atomic rename retries up to 5× on Windows transient file-lock errors; `npx`-based MCP entry used for all agents
66
+ - **Safe MCP config merging — zero data loss** _(v1.0.3-patch)_ — `mergeJsonFile()` creates a `.bak` backup before every write; Amp uses root-level flat-key merge so no `"amp.*"` settings are destroyed; all agents check idempotency before writing; Kiro IDE and OpenCode added as first-class `setup` agents with correct config formats
58
67
 
59
68
  ---
60
69
 
package/dist/cli/hook.js CHANGED
@@ -1,6 +1,86 @@
1
1
  #!/usr/bin/env node
2
2
  import process2 from 'process';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { spawnSync } from 'child_process';
3
6
 
7
+ function findCodeIntelDir(startDir) {
8
+ let dir = startDir;
9
+ for (let i = 0; i < 8; i++) {
10
+ const candidate = path.join(dir, ".code-intel");
11
+ try {
12
+ if (fs.statSync(candidate).isDirectory()) return candidate;
13
+ } catch {
14
+ }
15
+ const parent = path.dirname(dir);
16
+ if (parent === dir) break;
17
+ dir = parent;
18
+ }
19
+ return null;
20
+ }
21
+ function loadIndexedCommit(codeIntelDir) {
22
+ try {
23
+ const meta = JSON.parse(fs.readFileSync(path.join(codeIntelDir, "meta.json"), "utf-8"));
24
+ return meta.commitHash ?? "";
25
+ } catch {
26
+ return "";
27
+ }
28
+ }
29
+ function getHeadCommit(cwd) {
30
+ try {
31
+ const r = spawnSync("git", ["rev-parse", "HEAD"], {
32
+ encoding: "utf-8",
33
+ timeout: 3e3,
34
+ cwd,
35
+ stdio: ["pipe", "pipe", "pipe"]
36
+ });
37
+ return (r.stdout ?? "").trim();
38
+ } catch {
39
+ return "";
40
+ }
41
+ }
42
+ function runCodeIntelAugment(pattern, cwd) {
43
+ const isWin = process2.platform === "win32";
44
+ const bin = isWin ? "code-intel.cmd" : "code-intel";
45
+ const npxBin = isWin ? "npx.cmd" : "npx";
46
+ let r = spawnSync(bin, ["augment", "--", pattern], {
47
+ encoding: "utf-8",
48
+ timeout: 6e3,
49
+ cwd,
50
+ stdio: ["pipe", "pipe", "pipe"]
51
+ });
52
+ if (r.error || r.status !== 0) {
53
+ r = spawnSync(npxBin, ["code-intel", "augment", "--", pattern], {
54
+ encoding: "utf-8",
55
+ timeout: 1e4,
56
+ cwd,
57
+ stdio: ["pipe", "pipe", "pipe"]
58
+ });
59
+ }
60
+ return !r.error && r.status === 0 ? (r.stdout ?? "").trim() : "";
61
+ }
62
+ function handlePostToolUse(input) {
63
+ const toolName = input.tool_name ?? "";
64
+ if (toolName !== "Bash") return;
65
+ const command = input.tool_input?.command ?? "";
66
+ if (!/\bgit\s+(commit|merge|rebase|cherry-pick|pull)(\s|$)/.test(command)) return;
67
+ const exitCode = input.tool_output?.exit_code;
68
+ if (exitCode !== void 0 && exitCode !== 0) return;
69
+ const cwd = input.cwd ?? process2.cwd();
70
+ if (!path.isAbsolute(cwd)) return;
71
+ const codeIntelDir = findCodeIntelDir(cwd);
72
+ if (!codeIntelDir) return;
73
+ const head = getHeadCommit(cwd);
74
+ if (!head) return;
75
+ const indexed = loadIndexedCommit(codeIntelDir);
76
+ if (head && head === indexed) return;
77
+ const since = indexed ? indexed.slice(0, 7) : "never";
78
+ const current = head.slice(0, 7);
79
+ const msg = `code-intel index is stale (last indexed: ${since}, current HEAD: ${current}). Run \`code-intel analyze\` to update the knowledge graph.`;
80
+ process2.stdout.write(
81
+ JSON.stringify({ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: msg } })
82
+ );
83
+ }
4
84
  var SOURCE_EXT = /* @__PURE__ */ new Set([
5
85
  "ts",
6
86
  "tsx",
@@ -289,26 +369,49 @@ function runClaudeHook() {
289
369
  process2.exit(0);
290
370
  }
291
371
  const parsed = JSON.parse(input);
292
- const cmd = parsed?.tool_input?.command;
293
- if (typeof cmd !== "string" || !cmd.trim()) {
372
+ const hookEvent = parsed.hook_event_name ?? "PreToolUse";
373
+ if (hookEvent === "PostToolUse") {
374
+ handlePostToolUse(parsed);
294
375
  process2.exit(0);
295
376
  }
296
- const rewritten = rewriteCommand(cmd);
297
- if (rewritten === null || rewritten === cmd) {
377
+ const toolInput = parsed.tool_input;
378
+ const cmd = toolInput?.command;
379
+ if (typeof cmd !== "string" || !cmd.trim()) {
298
380
  process2.exit(0);
299
381
  }
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
382
+ const rewritten = rewriteCommand(cmd);
383
+ const cwd = parsed.cwd ?? process2.cwd();
384
+ let additionalContext;
385
+ try {
386
+ if (path.isAbsolute(cwd) && findCodeIntelDir(cwd)) {
387
+ const pattern = extractGrepSymbol(cmd) ?? (rewritten ? void 0 : void 0);
388
+ if (pattern) {
389
+ const ctx = runCodeIntelAugment(pattern, cwd);
390
+ if (ctx) additionalContext = ctx;
308
391
  }
309
392
  }
310
- };
311
- process2.stdout.write(JSON.stringify(response));
393
+ } catch {
394
+ }
395
+ if (rewritten !== null && rewritten !== cmd) {
396
+ const response = {
397
+ hookSpecificOutput: {
398
+ hookEventName: "PreToolUse",
399
+ permissionDecision: "allow",
400
+ permissionDecisionReason: "code-intel: semantic search replaces grep/cat",
401
+ updatedInput: { ...toolInput, command: rewritten },
402
+ ...additionalContext ? { additionalContext } : {}
403
+ }
404
+ };
405
+ process2.stdout.write(JSON.stringify(response));
406
+ } else if (additionalContext) {
407
+ const response = {
408
+ hookSpecificOutput: {
409
+ hookEventName: "PreToolUse",
410
+ additionalContext
411
+ }
412
+ };
413
+ process2.stdout.write(JSON.stringify(response));
414
+ }
312
415
  process2.exit(0);
313
416
  } catch {
314
417
  process2.exit(0);
@@ -1 +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"]}
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 and agent hook handlers.\n *\n * Called by:\n * - `code-intel rewrite <cmd>` → exit 0 (match) | exit 1 (no match)\n * - `code-intel hook claude` → reads stdin PreToolUse/PostToolUse JSON, writes response JSON\n * - `code-intel hook cursor` → same, Cursor format\n * - `code-intel hook gemini` → same, Gemini format\n * - `code-intel hook copilot` → same, Copilot format\n *\n * ZERO heavy imports — this module must load in <50ms.\n * Filesystem access and spawnSync are allowed only in hook handlers (not in rewriteCommand).\n *\n * Hook behaviour (inspired by GitNexus plugin pattern):\n * PreToolUse — rewrites grep/rg/cat to code-intel equivalents; optionally injects\n * graph context as `additionalContext` via `code-intel augment`.\n * PostToolUse — detects stale knowledge graph after git mutations (commit/merge/rebase/\n * cherry-pick/pull) by comparing `git rev-parse HEAD` against the\n * commitHash stored in `.code-intel/meta.json`. Notifies the agent to\n * re-run `code-intel analyze` when the index is behind.\n */\n\nimport process from 'node:process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { spawnSync } from 'node:child_process';\n\n// ─── Stale-index detection helpers ───────────────────────────────────────────\n\n/**\n * Walk up the directory tree from startDir looking for a `.code-intel/` directory.\n * Returns the path to `.code-intel/` or null if not found (up to 8 levels).\n */\nfunction findCodeIntelDir(startDir: string): string | null {\n let dir = startDir;\n for (let i = 0; i < 8; i++) {\n const candidate = path.join(dir, '.code-intel');\n try { if (fs.statSync(candidate).isDirectory()) return candidate; } catch { /* continue */ }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/** Read the `commitHash` stored in `.code-intel/meta.json` (empty string if absent). */\nfunction loadIndexedCommit(codeIntelDir: string): string {\n try {\n const meta = JSON.parse(fs.readFileSync(path.join(codeIntelDir, 'meta.json'), 'utf-8')) as Record<string, unknown>;\n return (meta.commitHash as string) ?? '';\n } catch { return ''; }\n}\n\n/** Run `git rev-parse HEAD` synchronously (empty string on error). */\nfunction getHeadCommit(cwd: string): string {\n try {\n const r = spawnSync('git', ['rev-parse', 'HEAD'], {\n encoding: 'utf-8', timeout: 3000, cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return (r.stdout ?? '').trim();\n } catch { return ''; }\n}\n\n// ─── Augment helper ───────────────────────────────────────────────────────────\n\n/**\n * Spawn `code-intel augment -- <pattern>` synchronously and return its stdout.\n * Falls back to npx if the binary is not on PATH.\n * Returns empty string on any error — this must never block the agent.\n *\n * SECURITY: args are passed as array elements, never through shell interpolation.\n */\nfunction runCodeIntelAugment(pattern: string, cwd: string): string {\n const isWin = process.platform === 'win32';\n const bin = isWin ? 'code-intel.cmd' : 'code-intel';\n const npxBin = isWin ? 'npx.cmd' : 'npx';\n\n let r = spawnSync(bin, ['augment', '--', pattern], {\n encoding: 'utf-8', timeout: 6000, cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n if (r.error || r.status !== 0) {\n r = spawnSync(npxBin, ['code-intel', 'augment', '--', pattern], {\n encoding: 'utf-8', timeout: 10000, cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n }\n return (!r.error && r.status === 0) ? (r.stdout ?? '').trim() : '';\n}\n\n// ─── PostToolUse handler ──────────────────────────────────────────────────────\n\n/**\n * PostToolUse: after git mutations, check whether the knowledge graph is stale.\n *\n * Instead of running a full `code-intel analyze` synchronously (which would block\n * the agent for up to 120s), we do a lightweight check: compare `git rev-parse HEAD`\n * against the `commitHash` stored in `.code-intel/meta.json`. When they differ, we\n * notify the agent via `additionalContext` so it can decide when to reindex.\n *\n * Writes the response JSON to stdout (empty string → no output / pass through).\n */\nfunction handlePostToolUse(input: Record<string, unknown>): void {\n const toolName = (input.tool_name as string | undefined) ?? '';\n if (toolName !== 'Bash') return;\n\n const command = ((input.tool_input as Record<string, unknown> | undefined)?.command as string | undefined) ?? '';\n // Only care about git operations that advance HEAD\n if (!/\\bgit\\s+(commit|merge|rebase|cherry-pick|pull)(\\s|$)/.test(command)) return;\n\n // Ignore failed commands\n const exitCode = (input.tool_output as Record<string, unknown> | undefined)?.exit_code;\n if (exitCode !== undefined && exitCode !== 0) return;\n\n const cwd = (input.cwd as string | undefined) ?? process.cwd();\n if (!path.isAbsolute(cwd)) return;\n\n const codeIntelDir = findCodeIntelDir(cwd);\n if (!codeIntelDir) return; // no index for this repo → nothing to check\n\n const head = getHeadCommit(cwd);\n if (!head) return;\n\n const indexed = loadIndexedCommit(codeIntelDir);\n if (head && head === indexed) return; // index is current → nothing to notify\n\n const since = indexed ? indexed.slice(0, 7) : 'never';\n const current = head.slice(0, 7);\n const msg =\n `code-intel index is stale (last indexed: ${since}, current HEAD: ${current}). ` +\n `Run \\`code-intel analyze\\` to update the knowledge graph.`;\n\n process.stdout.write(\n JSON.stringify({ hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: msg } }),\n );\n}\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()) { process.exit(0); }\n\n const parsed = JSON.parse(input) as Record<string, unknown>;\n const hookEvent = (parsed.hook_event_name as string | undefined) ?? 'PreToolUse';\n\n // ── PostToolUse: stale-index notification after git mutations ──────────\n if (hookEvent === 'PostToolUse') {\n handlePostToolUse(parsed);\n process.exit(0);\n }\n\n // ── PreToolUse: command rewrite + optional graph context augment ───────\n const toolInput = parsed.tool_input as Record<string, unknown> | undefined;\n const cmd = toolInput?.command;\n if (typeof cmd !== 'string' || !cmd.trim()) { process.exit(0); }\n\n const rewritten = rewriteCommand(cmd);\n const cwd = (parsed.cwd as string | undefined) ?? process.cwd();\n\n // Try to augment with graph context (non-blocking — any error → skip)\n let additionalContext: string | undefined;\n try {\n if (path.isAbsolute(cwd) && findCodeIntelDir(cwd)) {\n const pattern = extractGrepSymbol(cmd) ?? (rewritten ? undefined : undefined);\n if (pattern) {\n const ctx = runCodeIntelAugment(pattern, cwd);\n if (ctx) additionalContext = ctx;\n }\n }\n } catch { /* ignore augment errors */ }\n\n if (rewritten !== null && rewritten !== cmd) {\n // Rewrite found — update input command and optionally add context\n const response: Record<string, unknown> = {\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: 'code-intel: semantic search replaces grep/cat',\n updatedInput: { ...toolInput, command: rewritten },\n ...(additionalContext ? { additionalContext } : {}),\n },\n };\n process.stdout.write(JSON.stringify(response));\n } else if (additionalContext) {\n // No rewrite but we have graph context to inject\n const response = {\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n additionalContext,\n },\n };\n process.stdout.write(JSON.stringify(response));\n }\n // else: no rewrite, no context → empty stdout = pass through\n\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', () => { process.exit(0); });\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"]}