claude-ex 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,14 @@ claude-ex init
15
15
 
16
16
  That's it. Open Claude Code — the MCP server starts automatically and gives Claude structural awareness of your entire codebase.
17
17
 
18
+ For Codex, register it during init:
19
+
20
+ ```bash
21
+ claude-ex init --codex
22
+ ```
23
+
24
+ That also writes a marker-safe `AGENTS.md` snippet and runs `codex mcp add claude-ex -- claude-ex mcp --no-watch /path/to/project`.
25
+
18
26
  ## How It Works
19
27
 
20
28
  1. **Indexes** your codebase using tree-sitter (functions, classes, methods, imports, call graphs)
@@ -195,6 +203,8 @@ In Claude Code, type:
195
203
  | `get_dependencies` | What a symbol depends on | <3ms |
196
204
  | `get_file_map` | Every file and its exports | <5ms |
197
205
  | `get_file_symbols` | All symbols in a file | <3ms |
206
+ | `get_file_context` | Best context around files: symbols, imports, importers, callers, related files | <30ms |
207
+ | `get_task_context` | One-shot AI context pack from task/query to symbols, files, and related context | <50ms |
198
208
  | `find_files` | Find files by glob pattern | <3ms |
199
209
  | `find_by_kind` | All classes, interfaces, enums, etc. | <5ms |
200
210
  | `get_type_hierarchy` | Subclasses/implementors | <3ms |
@@ -209,6 +219,7 @@ In Claude Code, type:
209
219
 
210
220
  ```
211
221
  claude-ex init [path] Index + install config + generate docs
222
+ claude-ex init [path] --codex Also register the MCP server with Codex + write AGENTS.md
212
223
  claude-ex transparent-review [target] Zero-black-box review (before/after code, English, blast radius)
213
224
  claude-ex review [target] Graph-aware diff review (structured JSON)
214
225
  claude-ex search <query> Search symbols
@@ -223,7 +234,7 @@ claude-ex brief Project summary (SessionStart hook)
223
234
  claude-ex pre-edit <file> Pre-edit context (PreToolUse hook)
224
235
  claude-ex post-edit <file> Post-edit reindex (PostToolUse hook)
225
236
  claude-ex generate-docs Regenerate CLAUDE.md
226
- claude-ex mcp Run as MCP server
237
+ claude-ex mcp [path] [--no-watch] Run as MCP server
227
238
  claude-ex uninstall Remove all config
228
239
  ```
229
240
 
@@ -1,3 +1,11 @@
1
+ import { type SpawnSyncReturns } from 'child_process';
2
+ type CommandRunner = (command: string, args: string[], options: {
3
+ cwd: string;
4
+ encoding: BufferEncoding;
5
+ }) => SpawnSyncReturns<string>;
1
6
  export declare function install(rootDir: string, options?: {
2
7
  work?: boolean;
8
+ codex?: boolean;
9
+ commandRunner?: CommandRunner;
3
10
  }): void;
11
+ export {};
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.install = install;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
39
40
  const SKILL_CONTENT = `---
40
41
  name: claude-ex
41
42
  description: >
@@ -192,8 +193,44 @@ For each changed file with symbols:
192
193
  - Overall assessment: approve, request changes, or needs discussion
193
194
  - Prioritized list of action items if any
194
195
  `;
196
+ const AGENTS_MARKER_START = '<!-- claude-ex:agents:start -->';
197
+ const AGENTS_MARKER_END = '<!-- claude-ex:agents:end -->';
198
+ const AGENTS_CONTENT = `${AGENTS_MARKER_START}
199
+ # claude-ex
200
+
201
+ Use the \`claude-ex\` MCP server for structural codebase tasks before falling back to broad text search.
202
+
203
+ Prefer MCP for:
204
+ - symbol search and code discovery
205
+ - callers, dependents, dependencies, and type hierarchy
206
+ - architecture, file maps, and package usage
207
+ - graph-aware diff review
208
+
209
+ Use the following tools when they match the task:
210
+ - \`search_code\`
211
+ - \`get_symbol\`
212
+ - \`get_callers\`
213
+ - \`get_dependents\`
214
+ - \`get_dependencies\`
215
+ - \`get_architecture\`
216
+ - \`get_file_map\`
217
+ - \`find_files\`
218
+ - \`get_file_symbols\`
219
+ - \`get_file_context\`
220
+ - \`get_task_context\`
221
+ - \`find_by_kind\`
222
+ - \`get_type_hierarchy\`
223
+ - \`find_dead_exports\`
224
+ - \`get_pkg_usages\`
225
+ - \`review_diff\`
226
+ - \`transparent_review\`
227
+
228
+ Use grep/ripgrep for plain text and regex-only searches like TODOs, exact literals, or log lines.
229
+ ${AGENTS_MARKER_END}
230
+ `;
195
231
  function install(rootDir, options) {
196
232
  const work = options?.work ?? false;
233
+ const codex = options?.codex ?? false;
197
234
  if (work) {
198
235
  // Work mode: data goes in .local/.codex/, config stays at root but is gitignored
199
236
  const codexDir = path.join(rootDir, '.local', '.codex');
@@ -203,6 +240,8 @@ function install(rootDir, options) {
203
240
  addToGitignore(rootDir, '.local/');
204
241
  addToGitignore(rootDir, '.claude/');
205
242
  addToGitignore(rootDir, '.mcp.json');
243
+ if (codex)
244
+ addToGitignore(rootDir, 'AGENTS.md');
206
245
  }
207
246
  else {
208
247
  // Normal mode: .codex/ at root
@@ -219,6 +258,10 @@ function install(rootDir, options) {
219
258
  // 4. Create skill files
220
259
  installSkill(rootDir);
221
260
  installReviewSkill(rootDir);
261
+ if (codex) {
262
+ installAgentsMd(rootDir);
263
+ installCodexMcp(rootDir, options?.commandRunner ?? child_process_1.spawnSync);
264
+ }
222
265
  }
223
266
  function addToGitignore(rootDir, entry) {
224
267
  const gitignorePath = path.join(rootDir, '.gitignore');
@@ -247,7 +290,7 @@ function installMcpConfig(rootDir) {
247
290
  config.mcpServers.codex = {
248
291
  type: 'stdio',
249
292
  command: 'claude-ex',
250
- args: ['mcp'],
293
+ args: ['mcp', rootDir],
251
294
  };
252
295
  fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\n');
253
296
  }
@@ -334,4 +377,53 @@ function installReviewSkill(rootDir) {
334
377
  fs.mkdirSync(skillDir, { recursive: true });
335
378
  fs.writeFileSync(path.join(skillDir, 'SKILL.md'), REVIEW_SKILL_CONTENT);
336
379
  }
380
+ function installAgentsMd(rootDir) {
381
+ const agentsPath = path.join(rootDir, 'AGENTS.md');
382
+ let updated = AGENTS_CONTENT;
383
+ if (fs.existsSync(agentsPath)) {
384
+ const existing = fs.readFileSync(agentsPath, 'utf-8');
385
+ const start = existing.indexOf(AGENTS_MARKER_START);
386
+ const end = existing.indexOf(AGENTS_MARKER_END);
387
+ if (start !== -1 && end !== -1 && end > start) {
388
+ updated = existing.slice(0, start) + AGENTS_CONTENT + existing.slice(end + AGENTS_MARKER_END.length);
389
+ }
390
+ else if (!existing.includes(AGENTS_MARKER_START)) {
391
+ updated = existing.trimEnd() + '\n\n' + AGENTS_CONTENT + '\n';
392
+ }
393
+ else {
394
+ updated = existing;
395
+ }
396
+ }
397
+ if (!fs.existsSync(agentsPath) || fs.readFileSync(agentsPath, 'utf-8') !== updated) {
398
+ fs.writeFileSync(agentsPath, updated);
399
+ }
400
+ }
401
+ function installCodexMcp(rootDir, commandRunner) {
402
+ const addArgs = ['mcp', 'add', 'claude-ex', '--', 'claude-ex', 'mcp', '--no-watch', rootDir];
403
+ const existing = commandRunner('codex', ['mcp', 'get', 'claude-ex'], {
404
+ cwd: rootDir,
405
+ encoding: 'utf-8',
406
+ });
407
+ if (existing.status === 0) {
408
+ const current = `${existing.stdout || ''}\n${existing.stderr || ''}`;
409
+ if (current.includes(rootDir))
410
+ return;
411
+ const remove = commandRunner('codex', ['mcp', 'remove', 'claude-ex'], {
412
+ cwd: rootDir,
413
+ encoding: 'utf-8',
414
+ });
415
+ if (remove.status !== 0) {
416
+ const detail = (remove.stderr || remove.stdout || remove.error?.message || 'unknown error').trim();
417
+ throw new Error(`Failed to update existing Codex MCP server: ${detail}`);
418
+ }
419
+ }
420
+ const result = commandRunner('codex', addArgs, {
421
+ cwd: rootDir,
422
+ encoding: 'utf-8',
423
+ });
424
+ if (result.status !== 0) {
425
+ const detail = (result.stderr || result.stdout || result.error?.message || 'unknown error').trim();
426
+ throw new Error(`Failed to register Codex MCP server: ${detail}`);
427
+ }
428
+ }
337
429
  //# sourceMappingURL=installer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/claude/installer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiKA,0BA8BC;AA/LD,uCAAyB;AACzB,2CAA6B;AAE7B,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GrB,CAAC;AAEF,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgD5B,CAAC;AAEF,SAAgB,OAAO,CAAC,OAAe,EAAE,OAA4B;IACjE,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,KAAK,CAAC;IAEpC,IAAI,IAAI,EAAE,CAAC;QACP,iFAAiF;QACjF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACxD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACnC,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACpC,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACJ,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,4BAA4B;IAC5B,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE1B,wCAAwC;IACxC,YAAY,CAAC,OAAO,CAAC,CAAC;IAEtB,wBAAwB;IACxB,YAAY,CAAC,OAAO,CAAC,CAAC;IACtB,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,cAAc,CAAC,OAAe,EAAE,KAAa;IAClD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACvD,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;QAClD,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChD,IAAI,MAAM,GAAQ,EAAE,CAAC;IAErB,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,GAAG,EAAE,CAAC;QAChB,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU;QAAE,MAAM,CAAC,UAAU,GAAG,EAAE,CAAC;IAE/C,MAAM,CAAC,UAAU,CAAC,KAAK,GAAG;QACtB,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,WAAW;QACpB,IAAI,EAAE,CAAC,KAAK,CAAC;KAChB,CAAC;IAEF,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAC3D,IAAI,MAAM,GAAQ,EAAE,CAAC;IAErB,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,GAAG,EAAE,CAAC;QAChB,CAAC;IACL,CAAC;IAED,yDAAyD;IACzD,IAAI,CAAC,MAAM,CAAC,WAAW;QAAE,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK;QAAE,MAAM,CAAC,WAAW,CAAC,KAAK,GAAG,EAAE,CAAC;IAC7D,MAAM,UAAU,GAAG,eAAe,CAAC;IACnC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;IAErC,oEAAoE;IACpE,MAAM,WAAW,GAAG,CAAC,OAAc,EAAE,EAAE,CACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAE1F,eAAe;IACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY;QAAE,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC;IAC/D,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC;YAC3B,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,CAAC;oBACJ,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,iBAAiB;oBAC1B,OAAO,EAAE,IAAI;iBAChB,CAAC;SACL,CAAC,CAAC;IACP,CAAC;IAED,4CAA4C;IAC5C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU;QAAE,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;IAC3D,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC;YACxD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;gBACzB,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,CAAC;wBACJ,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,yDAAyD;wBAClE,OAAO,EAAE,IAAI;qBAChB,CAAC;aACL,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,uCAAuC;IACvC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW;QAAE,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC;IAC7D,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC;gBAC1B,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,CAAC;wBACJ,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,0DAA0D;wBACnE,OAAO,EAAE,IAAI;qBAChB,CAAC;aACL,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAe;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,oBAAoB,CAAC,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/claude/installer.ts"],"names":[],"mappings":"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"}
@@ -1 +1,3 @@
1
- export declare function runMcpServer(): Promise<void>;
1
+ export declare function runMcpServer(pathArg?: string, options?: {
2
+ watch?: boolean;
3
+ }): Promise<void>;
@@ -1,16 +1,155 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.runMcpServer = runMcpServer;
4
37
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
38
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
39
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
7
42
  const schema_1 = require("../db/schema");
8
43
  const utils_1 = require("../utils");
9
44
  const daemon_1 = require("../watcher/daemon");
10
45
  const engine_1 = require("../query/engine");
11
46
  const indexer_1 = require("../indexer");
12
- async function runMcpServer() {
13
- const rootDir = (0, utils_1.findProjectRoot)() || process.env.CODEX_ROOT || process.cwd();
47
+ const collector_1 = require("../indexer/collector");
48
+ const parser_1 = require("../indexer/parser");
49
+ function resolveMcpRoot(pathArg) {
50
+ const explicitRoot = pathArg || process.env.CLAUDE_EX_ROOT || process.env.CODEX_ROOT;
51
+ if (explicitRoot) {
52
+ const resolved = path.resolve(explicitRoot);
53
+ return (0, utils_1.findProjectRoot)(resolved) || resolved;
54
+ }
55
+ return (0, utils_1.findProjectRoot)() || process.cwd();
56
+ }
57
+ function normalizeFileArg(rootDir, fileArg) {
58
+ if (typeof fileArg !== 'string')
59
+ return '';
60
+ let filePath = fileArg.trim();
61
+ if (filePath.startsWith('file://')) {
62
+ try {
63
+ filePath = new URL(filePath).pathname;
64
+ }
65
+ catch {
66
+ // Keep the original value and let normal path handling deal with it.
67
+ }
68
+ }
69
+ filePath = filePath.replace(/\\/g, '/');
70
+ const relPath = path.isAbsolute(filePath)
71
+ ? path.relative(rootDir, filePath)
72
+ : filePath;
73
+ return path.normalize(relPath).replace(/\\/g, '/').replace(/^\.\//, '');
74
+ }
75
+ function normalizeFileList(rootDir, fileArgs) {
76
+ const values = Array.isArray(fileArgs) ? fileArgs : [fileArgs];
77
+ return [...new Set(values
78
+ .map(file => normalizeFileArg(rootDir, file))
79
+ .filter(Boolean))];
80
+ }
81
+ function resolveInsideRoot(rootDir, relPath) {
82
+ if (!relPath || relPath === '.')
83
+ return null;
84
+ const fullPath = path.resolve(rootDir, relPath);
85
+ const relative = path.relative(rootDir, fullPath);
86
+ if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative))
87
+ return null;
88
+ return fullPath;
89
+ }
90
+ function fileExists(rootDir, relPath) {
91
+ const fullPath = resolveInsideRoot(rootDir, relPath);
92
+ return !!fullPath && fs.existsSync(fullPath);
93
+ }
94
+ function readLiveFileSymbols(rootDir, relPath) {
95
+ const fullPath = resolveInsideRoot(rootDir, relPath);
96
+ if (!fullPath)
97
+ return [];
98
+ try {
99
+ const content = fs.readFileSync(fullPath, 'utf-8');
100
+ return (0, parser_1.parseFile)(relPath, content).symbols.map(sym => ({
101
+ name: sym.name,
102
+ qualifiedName: sym.qualifiedName ?? null,
103
+ kind: sym.kind,
104
+ lineStart: sym.lineStart,
105
+ lineEnd: sym.lineEnd,
106
+ signature: sym.signature ?? null,
107
+ exported: !!sym.exported,
108
+ parameters: sym.parameters ? JSON.stringify(sym.parameters) : null,
109
+ }));
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ }
115
+ function hasIndexableCode(rootDir) {
116
+ return (0, collector_1.collectFiles)(rootDir).some(file => {
117
+ const language = (0, parser_1.getLanguage)(file);
118
+ return !!language && !['json', 'css', 'html'].includes(language);
119
+ });
120
+ }
121
+ function needsIndexRecovery(rootDir, stats) {
122
+ if (stats.files === 0)
123
+ return (0, collector_1.collectFiles)(rootDir).length > 0;
124
+ if (stats.symbols === 0)
125
+ return hasIndexableCode(rootDir);
126
+ return false;
127
+ }
128
+ function latestSourceMtime(rootDir, files) {
129
+ let latest = 0;
130
+ for (const file of files) {
131
+ try {
132
+ const mtime = fs.statSync(path.join(rootDir, file)).mtimeMs;
133
+ if (mtime > latest)
134
+ latest = mtime;
135
+ }
136
+ catch {
137
+ // Ignore files that disappeared between collection and stat.
138
+ }
139
+ }
140
+ return latest;
141
+ }
142
+ function indexLooksStale(rootDir, db, stats) {
143
+ const files = (0, collector_1.collectFiles)(rootDir);
144
+ if (files.length !== stats.files)
145
+ return true;
146
+ if (files.length === 0)
147
+ return false;
148
+ const indexed = db.prepare('SELECT COALESCE(MAX(last_modified), 0) as latest FROM files').get();
149
+ return latestSourceMtime(rootDir, files) > indexed.latest + 1;
150
+ }
151
+ async function runMcpServer(pathArg, options) {
152
+ const rootDir = resolveMcpRoot(pathArg);
14
153
  const startTime = performance.now();
15
154
  // Open database (stays open for lifetime)
16
155
  let db;
@@ -22,22 +161,62 @@ async function runMcpServer() {
22
161
  process.stderr.write(`[codex-mcp] Run 'claude-ex init' first.\n`);
23
162
  process.exit(1);
24
163
  }
25
- // Start file watcher inside MCP server process
26
164
  let watcher;
27
- try {
28
- watcher = await (0, daemon_1.startWatcher)(rootDir, db, (file) => {
29
- process.stderr.write(`[codex-mcp] reindexed: ${file}\n`);
30
- });
31
- }
32
- catch (err) {
33
- process.stderr.write(`[codex-mcp] Watcher failed to start: ${err}\n`);
34
- }
165
+ let shuttingDown = false;
166
+ let recoveryPromise = null;
167
+ let lastFreshnessCheck = 0;
35
168
  // Pre-warm statement cache + SQLite page cache
36
169
  try {
37
170
  (0, engine_1.getStats)(db);
38
171
  (0, engine_1.search)(db, 'a', 1);
39
172
  }
40
173
  catch { /* warm-up, ignore errors */ }
174
+ function scheduleIndexRecovery(reason) {
175
+ if (!recoveryPromise) {
176
+ recoveryPromise = Promise.resolve().then(() => {
177
+ const start = performance.now();
178
+ process.stderr.write(`[codex-mcp] rebuilding index (${reason})...\n`);
179
+ const stats = (0, indexer_1.indexProject)(rootDir);
180
+ const elapsed = (performance.now() - start).toFixed(0);
181
+ process.stderr.write(`[codex-mcp] rebuilt index in ${elapsed}ms (${stats.totalFiles} files, ${stats.symbols} symbols)\n`);
182
+ }).finally(() => {
183
+ recoveryPromise = null;
184
+ });
185
+ }
186
+ return recoveryPromise;
187
+ }
188
+ async function ensureIndexReady() {
189
+ if (recoveryPromise) {
190
+ await recoveryPromise;
191
+ return;
192
+ }
193
+ try {
194
+ const stats = (0, engine_1.getStats)(db);
195
+ if (needsIndexRecovery(rootDir, stats)) {
196
+ await scheduleIndexRecovery('empty index');
197
+ return;
198
+ }
199
+ const now = performance.now();
200
+ if (now - lastFreshnessCheck > 5000) {
201
+ lastFreshnessCheck = now;
202
+ if (indexLooksStale(rootDir, db, stats)) {
203
+ await scheduleIndexRecovery('stale index');
204
+ }
205
+ }
206
+ }
207
+ catch (err) {
208
+ await scheduleIndexRecovery(`stats failed: ${err.message}`);
209
+ }
210
+ }
211
+ try {
212
+ const stats = (0, engine_1.getStats)(db);
213
+ if (needsIndexRecovery(rootDir, stats)) {
214
+ void scheduleIndexRecovery('startup empty index');
215
+ }
216
+ }
217
+ catch {
218
+ void scheduleIndexRecovery('startup stats failed');
219
+ }
41
220
  const server = new index_js_1.Server({ name: 'claude-ex', version: '1.0.0' }, { capabilities: { tools: {} } });
42
221
  // Memoized tool list (allocated once, not per request)
43
222
  const TOOL_LIST = [
@@ -139,6 +318,44 @@ async function runMcpServer() {
139
318
  required: ['file'],
140
319
  },
141
320
  },
321
+ {
322
+ name: 'get_file_context',
323
+ description: 'Build the best structural context around one or more files: key symbols, exports, imports, importers, packages, callers, transitive dependents, and ranked related files. Use before editing or reviewing files.',
324
+ inputSchema: {
325
+ type: 'object',
326
+ properties: {
327
+ files: {
328
+ type: 'array',
329
+ items: { type: 'string' },
330
+ description: 'File paths relative to project root, or absolute paths',
331
+ },
332
+ maxSymbols: { type: 'number', description: 'Max symbols per file (default 30)' },
333
+ maxRelated: { type: 'number', description: 'Max related files to return (default 20)' },
334
+ includeCode: { type: 'boolean', description: 'Include stored symbol code snippets for top symbols (default false)' },
335
+ },
336
+ required: ['files'],
337
+ },
338
+ },
339
+ {
340
+ name: 'get_task_context',
341
+ description: 'One-shot AI context builder. Given a task or symbol query plus optional files, returns ranked symbols, matched files, selected file context, dependency/importer/caller context, and related files in one MCP call.',
342
+ inputSchema: {
343
+ type: 'object',
344
+ properties: {
345
+ query: { type: 'string', description: 'Natural-language task, symbol name, or file-ish query' },
346
+ files: {
347
+ type: 'array',
348
+ items: { type: 'string' },
349
+ description: 'Optional files to pin into the context first',
350
+ },
351
+ maxSymbols: { type: 'number', description: 'Max symbol matches and per-file symbols (default 12)' },
352
+ maxFiles: { type: 'number', description: 'Max selected files (default 8)' },
353
+ maxRelated: { type: 'number', description: 'Max related files (default 12)' },
354
+ includeCode: { type: 'boolean', description: 'Include stored symbol code snippets (default false)' },
355
+ },
356
+ required: ['query'],
357
+ },
358
+ },
142
359
  {
143
360
  name: 'find_by_kind',
144
361
  description: 'Find all symbols of a specific kind (class, function, interface, type, enum, method, variable). Ranked by structural importance.',
@@ -222,6 +439,7 @@ async function runMcpServer() {
222
439
  const callStart = performance.now();
223
440
  try {
224
441
  let result;
442
+ await ensureIndexReady();
225
443
  switch (name) {
226
444
  case 'search_code':
227
445
  result = (0, engine_1.search)(db, args.query, args.limit);
@@ -236,7 +454,7 @@ async function runMcpServer() {
236
454
  result = (0, engine_1.getCallers)(db, args.name);
237
455
  break;
238
456
  case 'get_dependents':
239
- result = (0, engine_1.getImpact)(db, args.file, args.maxDepth);
457
+ result = (0, engine_1.getImpact)(db, normalizeFileArg(rootDir, args.file), args.maxDepth);
240
458
  break;
241
459
  case 'get_dependencies':
242
460
  result = (0, engine_1.getDeps)(db, args.name);
@@ -255,7 +473,48 @@ async function runMcpServer() {
255
473
  result = (0, engine_1.findFiles)(db, args.pattern, args.limit);
256
474
  break;
257
475
  case 'get_file_symbols':
258
- result = (0, engine_1.getFileSymbols)(db, args.file);
476
+ {
477
+ const file = normalizeFileArg(rootDir, args.file);
478
+ if (fileExists(rootDir, file)) {
479
+ (0, indexer_1.reindexFile)(rootDir, file, db);
480
+ }
481
+ result = (0, engine_1.getFileSymbols)(db, file);
482
+ if (result.length === 0 && fileExists(rootDir, file)) {
483
+ result = readLiveFileSymbols(rootDir, file);
484
+ }
485
+ }
486
+ break;
487
+ case 'get_file_context':
488
+ {
489
+ const files = normalizeFileList(rootDir, args.files);
490
+ for (const file of files) {
491
+ if (fileExists(rootDir, file)) {
492
+ (0, indexer_1.reindexFile)(rootDir, file, db);
493
+ }
494
+ }
495
+ result = (0, engine_1.getFileContext)(db, files, {
496
+ maxSymbols: args.maxSymbols,
497
+ maxRelated: args.maxRelated,
498
+ includeCode: !!args.includeCode,
499
+ });
500
+ }
501
+ break;
502
+ case 'get_task_context':
503
+ {
504
+ const files = normalizeFileList(rootDir, args.files ?? []);
505
+ for (const file of files) {
506
+ if (fileExists(rootDir, file)) {
507
+ (0, indexer_1.reindexFile)(rootDir, file, db);
508
+ }
509
+ }
510
+ result = (0, engine_1.getTaskContext)(db, args.query, {
511
+ files,
512
+ maxSymbols: args.maxSymbols,
513
+ maxFiles: args.maxFiles,
514
+ maxRelated: args.maxRelated,
515
+ includeCode: !!args.includeCode,
516
+ });
517
+ }
259
518
  break;
260
519
  case 'find_by_kind':
261
520
  result = (0, engine_1.findByKind)(db, args.kind, args.limit);
@@ -271,7 +530,7 @@ async function runMcpServer() {
271
530
  break;
272
531
  case 'reindex_file': {
273
532
  const fileStart = performance.now();
274
- (0, indexer_1.reindexFile)(rootDir, args.file, db);
533
+ (0, indexer_1.reindexFile)(rootDir, normalizeFileArg(rootDir, args.file), db, { force: true });
275
534
  result = { success: true, timeMs: +(performance.now() - fileStart).toFixed(1) };
276
535
  break;
277
536
  }
@@ -313,8 +572,25 @@ async function runMcpServer() {
313
572
  // Connect stdio transport
314
573
  const transport = new stdio_js_1.StdioServerTransport();
315
574
  await server.connect(transport);
575
+ const watchEnabled = options?.watch ?? process.env.CLAUDE_EX_WATCH !== '0';
576
+ if (watchEnabled) {
577
+ (0, daemon_1.startWatcher)(rootDir, db, (file) => {
578
+ process.stderr.write(`[codex-mcp] reindexed: ${file}\n`);
579
+ }).then((startedWatcher) => {
580
+ if (shuttingDown) {
581
+ startedWatcher.close();
582
+ return;
583
+ }
584
+ watcher = startedWatcher;
585
+ }).catch((err) => {
586
+ process.stderr.write(`[codex-mcp] Watcher failed to start: ${err}\n`);
587
+ });
588
+ }
316
589
  // Graceful shutdown
317
590
  const shutdown = () => {
591
+ if (shuttingDown)
592
+ return;
593
+ shuttingDown = true;
318
594
  process.stderr.write('[codex-mcp] Shutting down...\n');
319
595
  if (watcher)
320
596
  watcher.close();
@@ -323,5 +599,13 @@ async function runMcpServer() {
323
599
  };
324
600
  process.on('SIGINT', shutdown);
325
601
  process.on('SIGTERM', shutdown);
602
+ // A stdio MCP server is owned by its client (e.g. Claude Code). If the
603
+ // client goes away without sending a signal, the OS reparents us to launchd
604
+ // and we keep running forever — each orphan holding a chokidar watcher that
605
+ // slowly leaks file descriptors until the system file table is exhausted.
606
+ // Exit as soon as the transport or stdin closes so orphans can't accumulate.
607
+ transport.onclose = shutdown;
608
+ process.stdin.on('end', shutdown);
609
+ process.stdin.on('close', shutdown);
326
610
  }
327
611
  //# sourceMappingURL=mcp.js.map