@danielblomma/cortex-mcp 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cortex.mjs +219 -4
- package/package.json +2 -2
- package/scaffold/mcp/package-lock.json +834 -671
- package/scaffold/mcp/package.json +1 -1
- package/scaffold/mcp/src/embed.ts +1 -1
- package/scaffold/mcp/src/embeddings.ts +1 -1
- package/scaffold/scripts/dashboard.mjs +38 -11
- package/scaffold/scripts/ingest.mjs +27 -0
- package/scaffold/scripts/parsers/csharp.mjs +27 -0
- package/scaffold/scripts/status.sh +7 -0
|
@@ -2,7 +2,7 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { env, pipeline } from "@
|
|
5
|
+
import { env, pipeline } from "@huggingface/transformers";
|
|
6
6
|
import { readJsonl, asString, asNumber, asBoolean } from "./jsonl.js";
|
|
7
7
|
import type { JsonObject, JsonValue } from "./types.js";
|
|
8
8
|
|
|
@@ -24,11 +24,12 @@ const SUPPORTED_TEXT_EXTENSIONS = new Set([
|
|
|
24
24
|
// Same skip dirs as ingest.mjs
|
|
25
25
|
const SKIP_DIRECTORIES = new Set([
|
|
26
26
|
".git", ".idea", ".vscode", "node_modules",
|
|
27
|
-
"dist", "build", "coverage", ".next", ".cache", ".context"
|
|
27
|
+
"bin", "obj", "dist", "build", "coverage", ".next", ".cache", ".context"
|
|
28
28
|
]);
|
|
29
29
|
|
|
30
30
|
const MAX_FILE_BYTES = 1024 * 1024;
|
|
31
31
|
const VERSION_CHECK_TTL_MS = 10 * 60 * 1000;
|
|
32
|
+
const VERSION_LOOKUP_TIMEOUT_MS = 8000;
|
|
32
33
|
const VERSION_INSTALL_HINT = "npm i -g github:DanielBlomma/cortex";
|
|
33
34
|
|
|
34
35
|
// ── ANSI helpers ──────────────────────────────────────────────
|
|
@@ -93,7 +94,11 @@ function walkDirectory(dirPath, files) {
|
|
|
93
94
|
if (entry.isDirectory()) {
|
|
94
95
|
walkDirectory(abs, files);
|
|
95
96
|
} else if (entry.isFile()) {
|
|
96
|
-
files.
|
|
97
|
+
if (typeof files.add === "function") {
|
|
98
|
+
files.add(abs);
|
|
99
|
+
} else {
|
|
100
|
+
files.push(abs);
|
|
101
|
+
}
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
@@ -111,20 +116,20 @@ function hasSourcePrefix(relPath, sourcePaths) {
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
// ── Data: baseline scan ──────────────────────────────────────
|
|
114
|
-
function scanBaseline() {
|
|
115
|
-
if (!fs.existsSync(
|
|
119
|
+
function scanBaseline(repoRoot = REPO_ROOT, configPath = CONFIG_PATH) {
|
|
120
|
+
if (!fs.existsSync(configPath)) return { files: 0, lines: 0, chars: 0, tokens: 0 };
|
|
116
121
|
|
|
117
|
-
const configText = fs.readFileSync(
|
|
118
|
-
const sourcePaths = parseSourcePaths(configText);
|
|
122
|
+
const configText = fs.readFileSync(configPath, "utf8");
|
|
123
|
+
const sourcePaths = [...new Set(parseSourcePaths(configText))];
|
|
119
124
|
if (sourcePaths.length === 0) return { files: 0, lines: 0, chars: 0, tokens: 0 };
|
|
120
125
|
|
|
121
|
-
const allFiles =
|
|
126
|
+
const allFiles = new Set();
|
|
122
127
|
for (const sp of sourcePaths) {
|
|
123
|
-
const abs = path.resolve(
|
|
128
|
+
const abs = path.resolve(repoRoot, sp);
|
|
124
129
|
if (!fs.existsSync(abs)) continue;
|
|
125
130
|
const stat = fs.statSync(abs);
|
|
126
131
|
if (stat.isFile()) {
|
|
127
|
-
allFiles.
|
|
132
|
+
allFiles.add(abs);
|
|
128
133
|
} else if (stat.isDirectory()) {
|
|
129
134
|
walkDirectory(abs, allFiles);
|
|
130
135
|
}
|
|
@@ -265,6 +270,9 @@ function shorten(text, max = 40) {
|
|
|
265
270
|
}
|
|
266
271
|
|
|
267
272
|
function summarizeError(error) {
|
|
273
|
+
if (error && typeof error === "object" && error.code === "ETIMEDOUT") {
|
|
274
|
+
return `version check timed out after ${Math.round(VERSION_LOOKUP_TIMEOUT_MS / 1000)}s`;
|
|
275
|
+
}
|
|
268
276
|
const raw = error instanceof Error ? error.message : String(error);
|
|
269
277
|
return shorten(raw.split(/\r?\n/)[0].trim());
|
|
270
278
|
}
|
|
@@ -325,7 +333,7 @@ function getVersionStatus() {
|
|
|
325
333
|
cwd: REPO_ROOT,
|
|
326
334
|
stdio: ["ignore", "pipe", "pipe"],
|
|
327
335
|
encoding: "utf8",
|
|
328
|
-
timeout:
|
|
336
|
+
timeout: VERSION_LOOKUP_TIMEOUT_MS,
|
|
329
337
|
env: { ...process.env, NPM_CONFIG_CACHE: npmCache },
|
|
330
338
|
shell: true,
|
|
331
339
|
}).trim();
|
|
@@ -491,6 +499,7 @@ function gatherData(baselineCache) {
|
|
|
491
499
|
},
|
|
492
500
|
tokens: tokenEstimate,
|
|
493
501
|
embeddings: embedModel ? { model: embedModel, count: embedCount, dimensions: embedDim } : null,
|
|
502
|
+
parserHealth: manifests.ingest?.parser_health || {},
|
|
494
503
|
freshness,
|
|
495
504
|
version,
|
|
496
505
|
topConnected,
|
|
@@ -696,6 +705,14 @@ function render(data, isTTY) {
|
|
|
696
705
|
} else {
|
|
697
706
|
lines.push(sideBorder(`Embeddings: ${col("not generated", C.yellow)} ${dim("Run: cortex embed")}`, w));
|
|
698
707
|
}
|
|
708
|
+
if (data.parserHealth.csharp && Number(data.parserHealth.csharp.files || 0) > 0) {
|
|
709
|
+
const csharp = data.parserHealth.csharp;
|
|
710
|
+
if (!csharp.available) {
|
|
711
|
+
lines.push(sideBorder(`Parser warning (C#): ${col("unavailable", C.red)} ${dim(csharp.reason || "install .NET SDK")}`, w));
|
|
712
|
+
} else if (csharp.chunks === 0) {
|
|
713
|
+
lines.push(sideBorder(`Parser warning (C#): ${col("0 chunks", C.yellow)} ${dim(`${csharp.files} files indexed`)}`, w));
|
|
714
|
+
}
|
|
715
|
+
}
|
|
699
716
|
lines.push(emptyLine(w));
|
|
700
717
|
|
|
701
718
|
// ── TOP CONNECTED ──
|
|
@@ -791,4 +808,14 @@ function main() {
|
|
|
791
808
|
});
|
|
792
809
|
}
|
|
793
810
|
|
|
794
|
-
|
|
811
|
+
const isMainModule = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(__filename);
|
|
812
|
+
if (isMainModule) {
|
|
813
|
+
main();
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
export {
|
|
817
|
+
parseSourcePaths,
|
|
818
|
+
render,
|
|
819
|
+
scanBaseline,
|
|
820
|
+
walkDirectory
|
|
821
|
+
};
|
|
@@ -24,6 +24,7 @@ let parseVb6Code = null;
|
|
|
24
24
|
let isVbNetParserAvailable = () => false;
|
|
25
25
|
let isCSharpParserAvailable = () => false;
|
|
26
26
|
let isCppParserAvailable = () => false;
|
|
27
|
+
let getCSharpParserRuntime = () => ({ available: false, reason: "parser module not loaded" });
|
|
27
28
|
|
|
28
29
|
async function loadOptionalParsers() {
|
|
29
30
|
const loaders = [
|
|
@@ -37,6 +38,10 @@ async function loadOptionalParsers() {
|
|
|
37
38
|
import("./parsers/csharp.mjs").then((module) => {
|
|
38
39
|
parseCSharpCode = module.parseCode;
|
|
39
40
|
parseCSharpProject = module.parseProject ?? null;
|
|
41
|
+
getCSharpParserRuntime =
|
|
42
|
+
typeof module.getCSharpParserRuntime === "function"
|
|
43
|
+
? module.getCSharpParserRuntime
|
|
44
|
+
: () => ({ available: typeof module.parseCode === "function", reason: "runtime details unavailable" });
|
|
40
45
|
isCSharpParserAvailable =
|
|
41
46
|
typeof module.isCSharpParserAvailable === "function"
|
|
42
47
|
? module.isCSharpParserAvailable
|
|
@@ -472,6 +477,8 @@ const SKIP_DIRECTORIES = new Set([
|
|
|
472
477
|
".idea",
|
|
473
478
|
".vscode",
|
|
474
479
|
"node_modules",
|
|
480
|
+
"bin",
|
|
481
|
+
"obj",
|
|
475
482
|
"dist",
|
|
476
483
|
"build",
|
|
477
484
|
"coverage",
|
|
@@ -2675,6 +2682,8 @@ async function main() {
|
|
|
2675
2682
|
|
|
2676
2683
|
const fileRecords = [...fileRecordMap.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
2677
2684
|
const adrRecords = [...adrRecordMap.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
2685
|
+
const csharpFileCount = fileRecords.filter((record) => path.extname(record.path).toLowerCase() === ".cs").length;
|
|
2686
|
+
const csharpRuntime = csharpFileCount > 0 ? getCSharpParserRuntime() : null;
|
|
2678
2687
|
const indexedFileIds = new Set(fileRecords.map((record) => record.id));
|
|
2679
2688
|
const changedFileIds = new Set(
|
|
2680
2689
|
[...candidates].map((absolutePath) => `file:${toPosixPath(path.relative(REPO_ROOT, absolutePath))}`)
|
|
@@ -3075,6 +3084,23 @@ async function main() {
|
|
|
3075
3084
|
}
|
|
3076
3085
|
}
|
|
3077
3086
|
|
|
3087
|
+
const csharpChunkCount = chunkRecords.filter((record) => record.language === "csharp").length;
|
|
3088
|
+
const parserHealth = {};
|
|
3089
|
+
if (csharpFileCount > 0) {
|
|
3090
|
+
parserHealth.csharp = {
|
|
3091
|
+
files: csharpFileCount,
|
|
3092
|
+
available: Boolean(csharpRuntime?.available),
|
|
3093
|
+
reason: csharpRuntime?.available ? null : (csharpRuntime?.reason ?? "C# parser unavailable"),
|
|
3094
|
+
chunks: csharpChunkCount,
|
|
3095
|
+
};
|
|
3096
|
+
|
|
3097
|
+
if (!csharpRuntime?.available) {
|
|
3098
|
+
console.log(`[ingest] warning csharp parser unavailable: ${parserHealth.csharp.reason}`);
|
|
3099
|
+
} else if (csharpChunkCount === 0) {
|
|
3100
|
+
console.log("[ingest] warning csharp parser produced 0 chunks across C# files");
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3078
3104
|
// Generate Module entities and relations
|
|
3079
3105
|
const moduleResult = generateModules(fileRecords, chunkRecords);
|
|
3080
3106
|
const moduleRecords = moduleResult.modules;
|
|
@@ -3512,6 +3538,7 @@ async function main() {
|
|
|
3512
3538
|
relations_transforms_config: configTransformRelations.length
|
|
3513
3539
|
},
|
|
3514
3540
|
skipped,
|
|
3541
|
+
parser_health: parserHealth,
|
|
3515
3542
|
incremental_mode: incrementalMode,
|
|
3516
3543
|
changed_candidates: candidates.size,
|
|
3517
3544
|
deleted_paths: deletedRelPaths.length
|
|
@@ -25,6 +25,20 @@ const DEFAULT_TARGET_FRAMEWORK = "net8.0";
|
|
|
25
25
|
let runtimeCache = null;
|
|
26
26
|
let publishCache = null;
|
|
27
27
|
|
|
28
|
+
function hasGitCheckout(startDir) {
|
|
29
|
+
let current = startDir;
|
|
30
|
+
while (true) {
|
|
31
|
+
if (fs.existsSync(path.join(current, ".git"))) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const parent = path.dirname(current);
|
|
35
|
+
if (parent === current) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
current = parent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
28
42
|
function getDotnetCommand() {
|
|
29
43
|
const override = process.env.CORTEX_DOTNET_CMD;
|
|
30
44
|
return override && override.trim().length > 0 ? override.trim() : DEFAULT_DOTNET_COMMAND;
|
|
@@ -74,6 +88,19 @@ function needsPublish() {
|
|
|
74
88
|
} catch {
|
|
75
89
|
return true;
|
|
76
90
|
}
|
|
91
|
+
|
|
92
|
+
if (process.env.CORTEX_CSHARP_FORCE_PUBLISH === "1") {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// In packaged installs there is no writable git checkout, but the
|
|
97
|
+
// published DLL is already bundled. Trust it instead of forcing an
|
|
98
|
+
// unnecessary `dotnet publish`, which can fail offline and leave C#
|
|
99
|
+
// repos with 0 chunks.
|
|
100
|
+
if (!hasGitCheckout(__dirname)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
77
104
|
return getMaxSourceMtime() > dllMtime;
|
|
78
105
|
}
|
|
79
106
|
|
|
@@ -24,6 +24,13 @@ console.log(`[status] files=${c.files ?? 0} adrs=${c.adrs ?? 0} rules=${c.rules
|
|
|
24
24
|
console.log(`[status] rels constrains=${c.relations_constrains ?? 0} implements=${c.relations_implements ?? 0} supersedes=${c.relations_supersedes ?? 0}`);
|
|
25
25
|
const s = data.skipped || {};
|
|
26
26
|
console.log(`[status] skipped unsupported=${s.unsupported ?? 0} too_large=${s.too_large ?? s.tooLarge ?? 0} binary=${s.binary ?? 0}`);
|
|
27
|
+
const parserHealth = data.parser_health || {};
|
|
28
|
+
if (parserHealth.csharp) {
|
|
29
|
+
console.log(`[status] csharp_parser available=${parserHealth.csharp.available} files=${parserHealth.csharp.files ?? 0} chunks=${parserHealth.csharp.chunks ?? 0}`);
|
|
30
|
+
if (parserHealth.csharp.reason) {
|
|
31
|
+
console.log(`[status] csharp_parser_reason=${parserHealth.csharp.reason}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
27
34
|
if (typeof data.incremental_mode === "boolean") {
|
|
28
35
|
console.log(`[status] incremental_mode=${data.incremental_mode} changed_candidates=${data.changed_candidates ?? 0} deleted_paths=${data.deleted_paths ?? 0}`);
|
|
29
36
|
}
|