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 +12 -1
- package/dist/claude/installer.d.ts +8 -0
- package/dist/claude/installer.js +93 -1
- package/dist/claude/installer.js.map +1 -1
- package/dist/claude/mcp.d.ts +3 -1
- package/dist/claude/mcp.js +298 -14
- package/dist/claude/mcp.js.map +1 -1
- package/dist/db/schema.d.ts +2 -0
- package/dist/db/schema.js +63 -24
- package/dist/db/schema.js.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/indexer/collector.js +1 -1
- package/dist/indexer/collector.js.map +1 -1
- package/dist/indexer/index.d.ts +4 -1
- package/dist/indexer/index.js +42 -10
- package/dist/indexer/index.js.map +1 -1
- package/dist/query/engine.d.ts +67 -0
- package/dist/query/engine.js +303 -20
- package/dist/query/engine.js.map +1 -1
- package/dist/watcher/daemon.js +69 -18
- package/dist/watcher/daemon.js.map +1 -1
- package/package.json +1 -1
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
|
|
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 {};
|
package/dist/claude/installer.js
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/claude/installer.ts"],"names":[],"mappings":"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"}
|
package/dist/claude/mcp.d.ts
CHANGED
package/dist/claude/mcp.js
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|