@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
package/bin/cortex.mjs
CHANGED
|
@@ -149,6 +149,80 @@ function ensureScaffoldExists() {
|
|
|
149
149
|
// Files that should never be overwritten if they already exist in the target.
|
|
150
150
|
// These contain user-specific configuration that would be lost on re-init.
|
|
151
151
|
const PRESERVE_FILES = new Set(["config.yaml", "enterprise.yml", "enterprise.yaml", "CLAUDE.md"]);
|
|
152
|
+
const DEFAULT_SOURCE_PATHS = [
|
|
153
|
+
"src",
|
|
154
|
+
"docs",
|
|
155
|
+
"design",
|
|
156
|
+
".context/notes",
|
|
157
|
+
".context/decisions",
|
|
158
|
+
"README.md"
|
|
159
|
+
];
|
|
160
|
+
const INIT_SKIP_DIRECTORIES = new Set([
|
|
161
|
+
".git",
|
|
162
|
+
".idea",
|
|
163
|
+
".vscode",
|
|
164
|
+
"node_modules",
|
|
165
|
+
"dist",
|
|
166
|
+
"build",
|
|
167
|
+
"coverage",
|
|
168
|
+
".next",
|
|
169
|
+
".cache",
|
|
170
|
+
".context",
|
|
171
|
+
"scripts",
|
|
172
|
+
"mcp",
|
|
173
|
+
".githooks",
|
|
174
|
+
"bin",
|
|
175
|
+
"obj"
|
|
176
|
+
]);
|
|
177
|
+
const INIT_SOURCE_EXTENSIONS = new Set([
|
|
178
|
+
".md",
|
|
179
|
+
".mdx",
|
|
180
|
+
".txt",
|
|
181
|
+
".adoc",
|
|
182
|
+
".rst",
|
|
183
|
+
".yaml",
|
|
184
|
+
".yml",
|
|
185
|
+
".json",
|
|
186
|
+
".toml",
|
|
187
|
+
".csv",
|
|
188
|
+
".ts",
|
|
189
|
+
".tsx",
|
|
190
|
+
".js",
|
|
191
|
+
".jsx",
|
|
192
|
+
".mjs",
|
|
193
|
+
".cjs",
|
|
194
|
+
".py",
|
|
195
|
+
".go",
|
|
196
|
+
".java",
|
|
197
|
+
".cs",
|
|
198
|
+
".vb",
|
|
199
|
+
".sln",
|
|
200
|
+
".vbproj",
|
|
201
|
+
".csproj",
|
|
202
|
+
".fsproj",
|
|
203
|
+
".props",
|
|
204
|
+
".targets",
|
|
205
|
+
".config",
|
|
206
|
+
".resx",
|
|
207
|
+
".settings",
|
|
208
|
+
".rb",
|
|
209
|
+
".rs",
|
|
210
|
+
".php",
|
|
211
|
+
".swift",
|
|
212
|
+
".kt",
|
|
213
|
+
".sql",
|
|
214
|
+
".sh",
|
|
215
|
+
".bash",
|
|
216
|
+
".zsh",
|
|
217
|
+
".ps1",
|
|
218
|
+
".c",
|
|
219
|
+
".h",
|
|
220
|
+
".cpp",
|
|
221
|
+
".hpp",
|
|
222
|
+
".cc",
|
|
223
|
+
".hh"
|
|
224
|
+
]);
|
|
225
|
+
const ROOT_DOC_PATHS = new Set(["docs", "design"]);
|
|
152
226
|
|
|
153
227
|
function copyDirectory(sourceDir, targetDir) {
|
|
154
228
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
@@ -175,6 +249,147 @@ function copyDirectory(sourceDir, targetDir) {
|
|
|
175
249
|
}
|
|
176
250
|
}
|
|
177
251
|
|
|
252
|
+
function toPosixPath(value) {
|
|
253
|
+
return value.split(path.sep).join("/");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function yamlScalar(value) {
|
|
257
|
+
return /^[A-Za-z0-9._/-]+$/.test(value) ? value : JSON.stringify(value);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function slugifyRepoId(value) {
|
|
261
|
+
const dashed = String(value || "")
|
|
262
|
+
.trim()
|
|
263
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
|
|
264
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
265
|
+
.replace(/[^A-Za-z0-9]+/g, "-")
|
|
266
|
+
.replace(/^-+|-+$/g, "")
|
|
267
|
+
.toLowerCase();
|
|
268
|
+
return dashed || "cortex";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function isInterestingSourceFile(fileName) {
|
|
272
|
+
const base = fileName.toLowerCase();
|
|
273
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
274
|
+
return INIT_SOURCE_EXTENSIONS.has(ext) || base === "readme" || base.startsWith("readme.");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function directoryContainsInterestingFiles(directoryPath) {
|
|
278
|
+
const stack = [directoryPath];
|
|
279
|
+
while (stack.length > 0) {
|
|
280
|
+
const current = stack.pop();
|
|
281
|
+
let entries = [];
|
|
282
|
+
try {
|
|
283
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
284
|
+
} catch {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
for (const entry of entries) {
|
|
289
|
+
const absolutePath = path.join(current, entry.name);
|
|
290
|
+
if (entry.isDirectory()) {
|
|
291
|
+
if (INIT_SKIP_DIRECTORIES.has(entry.name)) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
stack.push(absolutePath);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (entry.isFile() && isInterestingSourceFile(entry.name)) {
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function detectInitialSourcePaths(targetDir) {
|
|
308
|
+
if (!fs.existsSync(targetDir)) {
|
|
309
|
+
return [...DEFAULT_SOURCE_PATHS];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let entries = [];
|
|
313
|
+
try {
|
|
314
|
+
entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
315
|
+
} catch {
|
|
316
|
+
return [...DEFAULT_SOURCE_PATHS];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const codeDirs = [];
|
|
320
|
+
const docDirs = [];
|
|
321
|
+
const rootFiles = [];
|
|
322
|
+
|
|
323
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
324
|
+
const absolutePath = path.join(targetDir, entry.name);
|
|
325
|
+
|
|
326
|
+
if (entry.isDirectory()) {
|
|
327
|
+
if (INIT_SKIP_DIRECTORIES.has(entry.name)) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!directoryContainsInterestingFiles(absolutePath)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const bucket = ROOT_DOC_PATHS.has(entry.name) ? docDirs : codeDirs;
|
|
335
|
+
bucket.push(toPosixPath(entry.name));
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (entry.isFile() && isInterestingSourceFile(entry.name)) {
|
|
340
|
+
rootFiles.push(toPosixPath(entry.name));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const readmeFiles = rootFiles.filter((filePath) => /^readme(\.|$)/i.test(path.basename(filePath)));
|
|
345
|
+
const nonReadmeRootFiles = rootFiles.filter((filePath) => !readmeFiles.includes(filePath));
|
|
346
|
+
const detected = [
|
|
347
|
+
...codeDirs,
|
|
348
|
+
...nonReadmeRootFiles,
|
|
349
|
+
...docDirs,
|
|
350
|
+
".context/notes",
|
|
351
|
+
".context/decisions",
|
|
352
|
+
...readmeFiles
|
|
353
|
+
];
|
|
354
|
+
const uniqueDetected = [...new Set(detected)];
|
|
355
|
+
const hasConcreteRepoContent = uniqueDetected.some((value) => !value.startsWith(".context/"));
|
|
356
|
+
return hasConcreteRepoContent ? uniqueDetected : [...DEFAULT_SOURCE_PATHS];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function buildInitialConfig(targetDir) {
|
|
360
|
+
const repoId = slugifyRepoId(path.basename(path.resolve(targetDir)));
|
|
361
|
+
const sourcePaths = detectInitialSourcePaths(targetDir);
|
|
362
|
+
return [
|
|
363
|
+
`repo_id: ${yamlScalar(repoId)}`,
|
|
364
|
+
"source_paths:",
|
|
365
|
+
...sourcePaths.map((sourcePath) => ` - ${yamlScalar(sourcePath)}`),
|
|
366
|
+
"truth_order:",
|
|
367
|
+
" - ADR",
|
|
368
|
+
" - RULE",
|
|
369
|
+
" - CODE",
|
|
370
|
+
" - WIKI",
|
|
371
|
+
"ranking:",
|
|
372
|
+
" semantic: 0.40",
|
|
373
|
+
" graph: 0.25",
|
|
374
|
+
" trust: 0.20",
|
|
375
|
+
" recency: 0.15",
|
|
376
|
+
"runtime:",
|
|
377
|
+
" top_k: 5",
|
|
378
|
+
" include_uncertainties: true",
|
|
379
|
+
""
|
|
380
|
+
].join("\n");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function initializeScaffold(targetDir, force) {
|
|
384
|
+
const configPath = path.join(targetDir, ".context", "config.yaml");
|
|
385
|
+
const hasExistingConfig = fs.existsSync(configPath);
|
|
386
|
+
const generatedConfig = hasExistingConfig ? null : buildInitialConfig(targetDir);
|
|
387
|
+
installScaffold(targetDir, force);
|
|
388
|
+
if (!hasExistingConfig && generatedConfig) {
|
|
389
|
+
writeTextFile(configPath, generatedConfig);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
178
393
|
function ensurePathWritable(targetPath, force) {
|
|
179
394
|
if (!fs.existsSync(targetPath)) {
|
|
180
395
|
return;
|
|
@@ -555,7 +770,7 @@ async function maybeMigrateScaffold(targetDir, command) {
|
|
|
555
770
|
|
|
556
771
|
console.error(`[cortex] migrating scaffold in ${targetDir}`);
|
|
557
772
|
ensureScaffoldExists();
|
|
558
|
-
|
|
773
|
+
initializeScaffold(targetDir, true);
|
|
559
774
|
installAssistantHelpers(targetDir);
|
|
560
775
|
await maybeInstallGitHooks(targetDir);
|
|
561
776
|
await runContextCommand(targetDir, ["bootstrap"]);
|
|
@@ -590,7 +805,7 @@ async function ensureProjectInitializedForMcp(targetDir) {
|
|
|
590
805
|
}
|
|
591
806
|
ensureScaffoldExists();
|
|
592
807
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
593
|
-
|
|
808
|
+
initializeScaffold(targetDir, false);
|
|
594
809
|
installAssistantHelpers(targetDir);
|
|
595
810
|
await maybeInstallGitHooks(targetDir);
|
|
596
811
|
console.log(`[cortex] auto-init completed in ${targetDir}`);
|
|
@@ -632,7 +847,7 @@ async function run() {
|
|
|
632
847
|
const { target, force, bootstrap, connect, watch } = parseInitArgs(rest);
|
|
633
848
|
printBanner("Cortex initializes repo-scoped context for AI coding agents.");
|
|
634
849
|
fs.mkdirSync(target, { recursive: true });
|
|
635
|
-
|
|
850
|
+
initializeScaffold(target, force);
|
|
636
851
|
const helpers = installAssistantHelpers(target);
|
|
637
852
|
await maybeInstallGitHooks(target);
|
|
638
853
|
|
|
@@ -753,4 +968,4 @@ if (invokedAsScript) {
|
|
|
753
968
|
});
|
|
754
969
|
}
|
|
755
970
|
|
|
756
|
-
export { isScaffoldOutOfDate };
|
|
971
|
+
export { buildInitialConfig, detectInitialSourcePaths, isScaffoldOutOfDate, slugifyRepoId };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danielblomma/cortex-mcp",
|
|
3
3
|
"mcpName": "io.github.DanielBlomma/cortex",
|
|
4
|
-
"version": "1.7.
|
|
4
|
+
"version": "1.7.2",
|
|
5
5
|
"description": "Local, repo-scoped context platform for coding assistants. Semantic search, graph relationships, and architectural rule context.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Daniel Blomma",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"docs/MCP_MARKETPLACE.md"
|
|
47
47
|
],
|
|
48
48
|
"scripts": {
|
|
49
|
-
"test": "node tests/context-regressions.test.mjs && node --test tests/ingest-units.test.mjs tests/javascript-parser.test.mjs tests/sql-parser.test.mjs tests/config-parser.test.mjs tests/resources-parser.test.mjs tests/vbnet-parser.test.mjs tests/cpp-parser.test.mjs tests/multi-level.test.mjs tests/no-legacy-paths.test.mjs tests/tree-sitter-error-reporting.test.mjs tests/tree-sitter-body-cap.test.mjs tests/tree-sitter-exported.test.mjs tests/tree-sitter-robustness.test.mjs",
|
|
49
|
+
"test": "node tests/context-regressions.test.mjs && node --test tests/ingest-units.test.mjs tests/javascript-parser.test.mjs tests/sql-parser.test.mjs tests/config-parser.test.mjs tests/resources-parser.test.mjs tests/vbnet-parser.test.mjs tests/cpp-parser.test.mjs tests/dashboard.test.mjs tests/init-config.test.mjs tests/multi-level.test.mjs tests/no-legacy-paths.test.mjs tests/tree-sitter-error-reporting.test.mjs tests/tree-sitter-body-cap.test.mjs tests/tree-sitter-exported.test.mjs tests/tree-sitter-robustness.test.mjs",
|
|
50
50
|
"release:sync-version": "node scripts/sync-release-version.mjs",
|
|
51
51
|
"release:check-version-sync": "node scripts/sync-release-version.mjs --check",
|
|
52
52
|
"prepublishOnly": "echo 'Ready to publish to npm'"
|