@danielblomma/cortex-mcp 2.2.4 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -7
- package/bin/cortex.mjs +31 -167
- package/package.json +2 -2
- package/scaffold/mcp/package-lock.json +24 -21
- package/scaffold/mcp/src/cli/query.ts +36 -2
- package/scaffold/mcp/src/embed.ts +234 -21
- package/scaffold/mcp/src/embedScheduler.ts +69 -1
- package/scaffold/mcp/src/enterprise/index.ts +1 -1
- package/scaffold/mcp/src/enterprise/reviews/changed-files.ts +34 -0
- package/scaffold/mcp/src/enterprise/reviews/pattern-context.ts +231 -0
- package/scaffold/mcp/src/enterprise/tools/enterprise.ts +49 -36
- package/scaffold/mcp/src/paths.ts +3 -5
- package/scaffold/mcp/src/patternEvidence.ts +347 -0
- package/scaffold/mcp/src/search.ts +35 -9
- package/scaffold/mcp/src/searchCore.ts +254 -12
- package/scaffold/mcp/src/searchResults.ts +96 -9
- package/scaffold/mcp/src/types.ts +7 -0
- package/scaffold/mcp/tests/changed-files.test.mjs +41 -0
- package/scaffold/mcp/tests/embed-entities.test.mjs +109 -1
- package/scaffold/mcp/tests/embed-scheduler.test.mjs +52 -0
- package/scaffold/mcp/tests/enterprise-pattern-context.test.mjs +322 -0
- package/scaffold/mcp/tests/paths.test.mjs +11 -3
- package/scaffold/mcp/tests/pattern-evidence.test.mjs +321 -0
- package/scaffold/mcp/tests/query-cli.test.mjs +73 -1
- package/scaffold/mcp/tests/search-graph-score.test.mjs +167 -0
- package/scaffold/scripts/dashboard.mjs +29 -18
- package/scaffold/scripts/doctor.sh +26 -8
- package/scaffold/scripts/ingest.mjs +37 -4
- package/scaffold/scripts/status.sh +13 -6
|
@@ -180,15 +180,33 @@ fi
|
|
|
180
180
|
|
|
181
181
|
# Quick runtime import check
|
|
182
182
|
if [[ -f "$MCP_DIR/dist/server.js" ]] && [[ -d "$MCP_DIR/node_modules" ]]; then
|
|
183
|
-
MCP_CHECK=$(cd "$REPO_ROOT" &&
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
MCP_CHECK=$(cd "$REPO_ROOT" && node -e '
|
|
184
|
+
const { spawnSync } = require("node:child_process");
|
|
185
|
+
const timeoutMs = Number(process.env.CORTEX_DOCTOR_GRAPH_TIMEOUT_MS || 30000);
|
|
186
|
+
const probe = [
|
|
187
|
+
"const start = Date.now();",
|
|
188
|
+
"try {",
|
|
189
|
+
" require(\"./.context/mcp/dist/graph.js\");",
|
|
190
|
+
" console.log(\"ok \" + (Date.now() - start));",
|
|
191
|
+
"} catch (e) {",
|
|
192
|
+
" console.log(\"fail \" + (e && e.message ? e.message : String(e)));",
|
|
193
|
+
" process.exitCode = 1;",
|
|
194
|
+
"}"
|
|
195
|
+
].join("\n");
|
|
196
|
+
const result = spawnSync(process.execPath, ["-e", probe], {
|
|
197
|
+
encoding: "utf8",
|
|
198
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
199
|
+
timeout: timeoutMs
|
|
200
|
+
});
|
|
201
|
+
if (result.error && result.error.code === "ETIMEDOUT") {
|
|
202
|
+
console.log("fail timeout after " + timeoutMs + "ms");
|
|
203
|
+
} else if (result.error) {
|
|
204
|
+
console.log("fail " + result.error.message);
|
|
205
|
+
} else {
|
|
206
|
+
const lines = String(result.stdout || "").trim().split(/\r?\n/).filter(Boolean);
|
|
207
|
+
console.log(lines[lines.length - 1] || ("fail exit " + (result.status ?? "unknown")));
|
|
190
208
|
}
|
|
191
|
-
' 2>/dev/null || echo "fail
|
|
209
|
+
' 2>/dev/null || echo "fail graph check crashed")
|
|
192
210
|
if [[ "$MCP_CHECK" == ok* ]]; then
|
|
193
211
|
MS="${MCP_CHECK#ok }"
|
|
194
212
|
pass "Graph module loads (${MS}ms)"
|
|
@@ -457,11 +457,11 @@ function parseRules(rulesText) {
|
|
|
457
457
|
function walkDirectory(directoryPath, files) {
|
|
458
458
|
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
459
459
|
for (const entry of entries) {
|
|
460
|
-
|
|
460
|
+
const absolutePath = path.join(directoryPath, entry.name);
|
|
461
|
+
if (entry.isDirectory() && shouldSkipDirectory(absolutePath, entry.name)) {
|
|
461
462
|
continue;
|
|
462
463
|
}
|
|
463
464
|
|
|
464
|
-
const absolutePath = path.join(directoryPath, entry.name);
|
|
465
465
|
if (entry.isDirectory()) {
|
|
466
466
|
walkDirectory(absolutePath, files);
|
|
467
467
|
continue;
|
|
@@ -473,10 +473,40 @@ function walkDirectory(directoryPath, files) {
|
|
|
473
473
|
}
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
+
function shouldSkipDirectory(absolutePath, entryName) {
|
|
477
|
+
if (entryName === "bin" && path.resolve(path.dirname(absolutePath)) === REPO_ROOT) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
return SKIP_DIRECTORIES.has(entryName);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function normalizeSourcePrefix(sourcePath) {
|
|
484
|
+
const source = toPosixPath(sourcePath).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
485
|
+
return source === "." ? "" : source;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function normalizeRelativePath(relPath) {
|
|
489
|
+
return toPosixPath(relPath).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function hasSkippedDirectorySegment(relPath) {
|
|
493
|
+
const parts = normalizeRelativePath(relPath).split("/").filter(Boolean);
|
|
494
|
+
return parts.some((part, index) => {
|
|
495
|
+
if (part === "bin" && index === 0) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
return SKIP_DIRECTORIES.has(part);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
476
502
|
function hasSourcePrefix(relPath, sourcePaths) {
|
|
503
|
+
const normalizedRelPath = normalizeRelativePath(relPath);
|
|
504
|
+
if (!normalizedRelPath || hasSkippedDirectorySegment(normalizedRelPath)) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
477
507
|
return sourcePaths.some((sourcePath) => {
|
|
478
|
-
const source =
|
|
479
|
-
return
|
|
508
|
+
const source = normalizeSourcePrefix(sourcePath);
|
|
509
|
+
return source === "" || normalizedRelPath === source || normalizedRelPath.startsWith(`${source}/`);
|
|
480
510
|
});
|
|
481
511
|
}
|
|
482
512
|
|
|
@@ -627,6 +657,9 @@ function collectCandidateFiles(sourcePaths, mode) {
|
|
|
627
657
|
}
|
|
628
658
|
|
|
629
659
|
for (const sourcePath of sourcePaths) {
|
|
660
|
+
if (hasSkippedDirectorySegment(sourcePath)) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
630
663
|
const absoluteSourcePath = path.resolve(REPO_ROOT, sourcePath);
|
|
631
664
|
if (!fs.existsSync(absoluteSourcePath)) {
|
|
632
665
|
continue;
|
|
@@ -198,11 +198,12 @@ try {
|
|
|
198
198
|
|
|
199
199
|
node -e '
|
|
200
200
|
const path = require("node:path");
|
|
201
|
-
const { execSync } = require("node:child_process");
|
|
201
|
+
const { execFileSync, execSync } = require("node:child_process");
|
|
202
202
|
|
|
203
203
|
const repoRoot = process.argv[1];
|
|
204
204
|
const cacheDir = process.argv[2];
|
|
205
205
|
const localVersionEnv = process.argv[3] || "";
|
|
206
|
+
const VERSION_LOOKUP_TIMEOUT_MS = Number(process.env.CORTEX_VERSION_LOOKUP_TIMEOUT_MS || 8000);
|
|
206
207
|
|
|
207
208
|
function parseVersion(value) {
|
|
208
209
|
const match = String(value || "").trim().match(/^v?(\d+)\.(\d+)\.(\d+)$/);
|
|
@@ -225,7 +226,7 @@ function getLocalVersion() {
|
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
try {
|
|
228
|
-
const output =
|
|
229
|
+
const output = execFileSync("cortex", ["--version"], {
|
|
229
230
|
cwd: repoRoot,
|
|
230
231
|
stdio: ["ignore", "pipe", "ignore"],
|
|
231
232
|
encoding: "utf8",
|
|
@@ -282,11 +283,11 @@ try {
|
|
|
282
283
|
const npmCache = path.join(cacheDir, "npm-cache");
|
|
283
284
|
let latestRaw = "";
|
|
284
285
|
try {
|
|
285
|
-
latestRaw =
|
|
286
|
+
latestRaw = execFileSync("npm", ["view", "github:DanielBlomma/cortex", "version", "--json"], {
|
|
286
287
|
cwd: repoRoot,
|
|
287
288
|
stdio: ["ignore", "pipe", "pipe"],
|
|
288
289
|
encoding: "utf8",
|
|
289
|
-
timeout:
|
|
290
|
+
timeout: VERSION_LOOKUP_TIMEOUT_MS,
|
|
290
291
|
env: { ...process.env, NPM_CONFIG_CACHE: npmCache }
|
|
291
292
|
}).trim();
|
|
292
293
|
} catch (error) {
|
|
@@ -317,13 +318,19 @@ try {
|
|
|
317
318
|
process.exit(0);
|
|
318
319
|
}
|
|
319
320
|
|
|
320
|
-
const
|
|
321
|
+
const comparison = compareVersions(latestParsed, localParsed);
|
|
321
322
|
console.log(`[status] cortex_latest_version=${latestVersion}`);
|
|
322
323
|
|
|
323
|
-
if (
|
|
324
|
+
if (comparison > 0) {
|
|
325
|
+
console.log("[status] cortex_version_state=update-available");
|
|
324
326
|
console.log("[status] cortex_update_available=yes");
|
|
325
327
|
console.log("[status] run: npm i -g github:DanielBlomma/cortex");
|
|
328
|
+
} else if (comparison < 0) {
|
|
329
|
+
console.log("[status] cortex_version_state=local-newer");
|
|
330
|
+
console.log("[status] cortex_update_available=no");
|
|
331
|
+
console.log("[status] note: local Cortex is newer than the published version");
|
|
326
332
|
} else {
|
|
333
|
+
console.log("[status] cortex_version_state=current");
|
|
327
334
|
console.log("[status] cortex_update_available=no");
|
|
328
335
|
}
|
|
329
336
|
} catch (error) {
|