@arcbridge/core 0.4.1 → 0.5.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/dist/index.d.ts +24 -10
- package/dist/index.js +1383 -237
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/wasm/tree-sitter-go.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ var ArcBridgeConfigSchema = z2.object({
|
|
|
57
57
|
"angular-app"
|
|
58
58
|
]).default("nextjs-app-router"),
|
|
59
59
|
services: z2.array(ServiceSchema).default([]),
|
|
60
|
-
platforms: z2.array(z2.enum(["claude", "copilot", "gemini", "codex"])).default(["claude"]),
|
|
60
|
+
platforms: z2.array(z2.enum(["claude", "copilot", "gemini", "codex", "opencode"])).default(["claude"]),
|
|
61
61
|
quality_priorities: z2.array(QualityCategorySchema).default(["security", "performance", "accessibility"]).describe(QUALITY_PRIORITIES_DESCRIPTION),
|
|
62
62
|
indexing: z2.object({
|
|
63
63
|
include: z2.array(z2.string()).default(["src/**/*", "app/**/*"]),
|
|
@@ -195,7 +195,8 @@ var AgentRoleSchema = z6.object({
|
|
|
195
195
|
platform_overrides: z6.object({
|
|
196
196
|
claude: z6.record(z6.string(), z6.unknown()).optional(),
|
|
197
197
|
copilot: z6.record(z6.string(), z6.unknown()).optional(),
|
|
198
|
-
codex: z6.record(z6.string(), z6.unknown()).optional()
|
|
198
|
+
codex: z6.record(z6.string(), z6.unknown()).optional(),
|
|
199
|
+
opencode: z6.record(z6.string(), z6.unknown()).optional()
|
|
199
200
|
}).default({}),
|
|
200
201
|
// The markdown body (system prompt) is stored separately
|
|
201
202
|
system_prompt: z6.string().min(1)
|
|
@@ -4963,8 +4964,9 @@ function ensureGitignore(targetDir) {
|
|
|
4963
4964
|
}
|
|
4964
4965
|
|
|
4965
4966
|
// src/indexer/index.ts
|
|
4966
|
-
import
|
|
4967
|
-
import {
|
|
4967
|
+
import ts6 from "typescript";
|
|
4968
|
+
import { relative as relative5, join as join16 } from "path";
|
|
4969
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
|
|
4968
4970
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
4969
4971
|
import YAML from "yaml";
|
|
4970
4972
|
|
|
@@ -5332,7 +5334,7 @@ function buildSymbolLookup(symbols) {
|
|
|
5332
5334
|
function extractDependencies(sourceFile, checker, relativePath, projectRoot, lookup) {
|
|
5333
5335
|
const deps = [];
|
|
5334
5336
|
const seen = /* @__PURE__ */ new Set();
|
|
5335
|
-
function
|
|
5337
|
+
function addDep4(sourceId, targetId, kind) {
|
|
5336
5338
|
const key = `${sourceId}|${targetId}|${kind}`;
|
|
5337
5339
|
if (seen.has(key)) return;
|
|
5338
5340
|
if (!lookup.allIds.has(sourceId) || !lookup.allIds.has(targetId)) return;
|
|
@@ -5368,7 +5370,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5368
5370
|
if (!targetId) continue;
|
|
5369
5371
|
const fileSymbols = getFileTopLevelSymbols(relativePath, lookup);
|
|
5370
5372
|
for (const sourceId of fileSymbols) {
|
|
5371
|
-
|
|
5373
|
+
addDep4(sourceId, targetId, "imports");
|
|
5372
5374
|
}
|
|
5373
5375
|
}
|
|
5374
5376
|
}
|
|
@@ -5380,7 +5382,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5380
5382
|
if (targetId) {
|
|
5381
5383
|
const fileSymbols = getFileTopLevelSymbols(relativePath, lookup);
|
|
5382
5384
|
for (const sourceId of fileSymbols) {
|
|
5383
|
-
|
|
5385
|
+
addDep4(sourceId, targetId, "imports");
|
|
5384
5386
|
}
|
|
5385
5387
|
}
|
|
5386
5388
|
}
|
|
@@ -5402,7 +5404,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5402
5404
|
const resolved = exprSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(exprSymbol) : exprSymbol;
|
|
5403
5405
|
const targetId = resolveSymbolId(resolved);
|
|
5404
5406
|
if (targetId) {
|
|
5405
|
-
|
|
5407
|
+
addDep4(classId, targetId, kind);
|
|
5406
5408
|
}
|
|
5407
5409
|
}
|
|
5408
5410
|
}
|
|
@@ -5425,7 +5427,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5425
5427
|
const resolved = calledSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(calledSymbol) : calledSymbol;
|
|
5426
5428
|
const targetId = resolveSymbolId(resolved);
|
|
5427
5429
|
if (targetId) {
|
|
5428
|
-
|
|
5430
|
+
addDep4(ownerId, targetId, "calls");
|
|
5429
5431
|
}
|
|
5430
5432
|
}
|
|
5431
5433
|
}
|
|
@@ -5455,7 +5457,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5455
5457
|
const resolved = calledSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(calledSymbol) : calledSymbol;
|
|
5456
5458
|
const targetId = resolveSymbolId(resolved);
|
|
5457
5459
|
if (targetId) {
|
|
5458
|
-
|
|
5460
|
+
addDep4(callOwnerId, targetId, "calls");
|
|
5459
5461
|
}
|
|
5460
5462
|
}
|
|
5461
5463
|
}
|
|
@@ -5487,7 +5489,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5487
5489
|
const resolved = typeSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(typeSymbol) : typeSymbol;
|
|
5488
5490
|
const targetId = resolveSymbolId(resolved);
|
|
5489
5491
|
if (targetId) {
|
|
5490
|
-
|
|
5492
|
+
addDep4(ownerId, targetId, "uses_type");
|
|
5491
5493
|
}
|
|
5492
5494
|
}
|
|
5493
5495
|
}
|
|
@@ -5511,7 +5513,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5511
5513
|
const resolved = jsxSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(jsxSymbol) : jsxSymbol;
|
|
5512
5514
|
const targetId = resolveSymbolId(resolved);
|
|
5513
5515
|
if (targetId) {
|
|
5514
|
-
|
|
5516
|
+
addDep4(ownerId, targetId, "renders");
|
|
5515
5517
|
}
|
|
5516
5518
|
}
|
|
5517
5519
|
if (ts4.isPropertyAccessExpression(tagName) && ts4.isIdentifier(tagName.name) && tagName.name.text === "Provider") {
|
|
@@ -5520,7 +5522,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5520
5522
|
const resolved = ctxSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(ctxSymbol) : ctxSymbol;
|
|
5521
5523
|
const targetId = resolveSymbolId(resolved);
|
|
5522
5524
|
if (targetId) {
|
|
5523
|
-
|
|
5525
|
+
addDep4(ownerId, targetId, "provides_context");
|
|
5524
5526
|
}
|
|
5525
5527
|
}
|
|
5526
5528
|
}
|
|
@@ -5567,7 +5569,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5567
5569
|
const resolved = ctxSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(ctxSymbol) : ctxSymbol;
|
|
5568
5570
|
const targetId = resolveSymbolId(resolved);
|
|
5569
5571
|
if (targetId) {
|
|
5570
|
-
|
|
5572
|
+
addDep4(ownerId, targetId, "consumes_context");
|
|
5571
5573
|
}
|
|
5572
5574
|
}
|
|
5573
5575
|
}
|
|
@@ -6014,36 +6016,37 @@ function hashContent(content) {
|
|
|
6014
6016
|
}
|
|
6015
6017
|
|
|
6016
6018
|
// src/indexer/db-writer.ts
|
|
6017
|
-
function getExistingHashes(db, service) {
|
|
6018
|
-
const
|
|
6019
|
-
|
|
6020
|
-
).all(
|
|
6019
|
+
function getExistingHashes(db, service, language) {
|
|
6020
|
+
const query = language ? "SELECT DISTINCT file_path, content_hash FROM symbols WHERE service = ? AND language = ?" : "SELECT DISTINCT file_path, content_hash FROM symbols WHERE service = ?";
|
|
6021
|
+
const params = language ? [service, language] : [service];
|
|
6022
|
+
const rows = db.prepare(query).all(...params);
|
|
6021
6023
|
const map = /* @__PURE__ */ new Map();
|
|
6022
6024
|
for (const row of rows) {
|
|
6023
6025
|
map.set(row.file_path, row.content_hash);
|
|
6024
6026
|
}
|
|
6025
6027
|
return map;
|
|
6026
6028
|
}
|
|
6027
|
-
function
|
|
6029
|
+
function removeScopedSymbolsForFiles(db, filePaths, service, language) {
|
|
6028
6030
|
if (filePaths.length === 0) return;
|
|
6029
|
-
const
|
|
6030
|
-
"DELETE FROM symbols WHERE file_path = ?"
|
|
6031
|
-
);
|
|
6031
|
+
const scope = "file_path = ? AND service = ? AND language = ?";
|
|
6032
6032
|
const deleteDepsSource = db.prepare(
|
|
6033
|
-
|
|
6033
|
+
`DELETE FROM dependencies WHERE source_symbol IN (SELECT id FROM symbols WHERE ${scope})`
|
|
6034
6034
|
);
|
|
6035
6035
|
const deleteDepsTarget = db.prepare(
|
|
6036
|
-
|
|
6036
|
+
`DELETE FROM dependencies WHERE target_symbol IN (SELECT id FROM symbols WHERE ${scope})`
|
|
6037
6037
|
);
|
|
6038
6038
|
const deleteComponents = db.prepare(
|
|
6039
|
-
|
|
6039
|
+
`DELETE FROM components WHERE symbol_id IN (SELECT id FROM symbols WHERE ${scope})`
|
|
6040
|
+
);
|
|
6041
|
+
const deleteSymbols = db.prepare(
|
|
6042
|
+
`DELETE FROM symbols WHERE ${scope}`
|
|
6040
6043
|
);
|
|
6041
6044
|
transaction(db, () => {
|
|
6042
6045
|
for (const fp of filePaths) {
|
|
6043
|
-
deleteDepsSource.run(fp);
|
|
6044
|
-
deleteDepsTarget.run(fp);
|
|
6045
|
-
deleteComponents.run(fp);
|
|
6046
|
-
deleteSymbols.run(fp);
|
|
6046
|
+
deleteDepsSource.run(fp, service, language);
|
|
6047
|
+
deleteDepsTarget.run(fp, service, language);
|
|
6048
|
+
deleteComponents.run(fp, service, language);
|
|
6049
|
+
deleteSymbols.run(fp, service, language);
|
|
6047
6050
|
}
|
|
6048
6051
|
});
|
|
6049
6052
|
}
|
|
@@ -6176,11 +6179,11 @@ function hasGlobalTool() {
|
|
|
6176
6179
|
return false;
|
|
6177
6180
|
}
|
|
6178
6181
|
function resolveIndexerProject() {
|
|
6179
|
-
const
|
|
6182
|
+
const currentDir4 = dirname2(fileURLToPath(import.meta.url));
|
|
6180
6183
|
const candidates = [
|
|
6181
|
-
resolve(
|
|
6182
|
-
resolve(
|
|
6183
|
-
resolve(
|
|
6184
|
+
resolve(currentDir4, "../../../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj"),
|
|
6185
|
+
resolve(currentDir4, "../../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj"),
|
|
6186
|
+
resolve(currentDir4, "../../dotnet-indexer/ArcBridge.DotnetIndexer.csproj")
|
|
6184
6187
|
];
|
|
6185
6188
|
for (const candidate of candidates) {
|
|
6186
6189
|
if (existsSync5(candidate)) return candidate;
|
|
@@ -6246,7 +6249,7 @@ function indexDotnetProjectRoslyn(db, options) {
|
|
|
6246
6249
|
"No .sln or .csproj file found in project root. The .NET indexer requires a project or solution file."
|
|
6247
6250
|
);
|
|
6248
6251
|
}
|
|
6249
|
-
const existingHashes = getExistingHashes(db, service);
|
|
6252
|
+
const existingHashes = getExistingHashes(db, service, "csharp");
|
|
6250
6253
|
const hashesJson = JSON.stringify(Object.fromEntries(existingHashes));
|
|
6251
6254
|
const stdout = runDotnetIndexer(dotnetProject, hashesJson, projectRoot);
|
|
6252
6255
|
const lines = stdout.trim().split("\n");
|
|
@@ -6263,7 +6266,7 @@ function indexDotnetProjectRoslyn(db, options) {
|
|
|
6263
6266
|
);
|
|
6264
6267
|
}
|
|
6265
6268
|
const filesToClean = [...output.changedFiles, ...output.removedFiles];
|
|
6266
|
-
|
|
6269
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "csharp");
|
|
6267
6270
|
const symbols = output.symbols.map((s) => ({
|
|
6268
6271
|
id: s.id,
|
|
6269
6272
|
name: s.name,
|
|
@@ -7183,7 +7186,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7183
7186
|
ignore: ignorePatterns,
|
|
7184
7187
|
absolute: false
|
|
7185
7188
|
});
|
|
7186
|
-
const existingHashes = getExistingHashes(db, service);
|
|
7189
|
+
const existingHashes = getExistingHashes(db, service, "csharp");
|
|
7187
7190
|
const currentPaths = /* @__PURE__ */ new Set();
|
|
7188
7191
|
const fileCache = /* @__PURE__ */ new Map();
|
|
7189
7192
|
const changedFiles = [];
|
|
@@ -7210,7 +7213,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7210
7213
|
}
|
|
7211
7214
|
}
|
|
7212
7215
|
const filesToClean = [...removed, ...changedFiles];
|
|
7213
|
-
|
|
7216
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "csharp");
|
|
7214
7217
|
const allNewSymbols = [];
|
|
7215
7218
|
for (const relPath of changedFiles) {
|
|
7216
7219
|
const cached = fileCache.get(relPath);
|
|
@@ -7219,7 +7222,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7219
7222
|
allNewSymbols.push(...symbols);
|
|
7220
7223
|
}
|
|
7221
7224
|
writeSymbols(db, allNewSymbols, service, "csharp");
|
|
7222
|
-
const allDbSymbols = db.prepare("SELECT id, file_path as filePath, name, kind, start_line as startLine, end_line as endLine FROM symbols WHERE service = ?").all(service);
|
|
7225
|
+
const allDbSymbols = db.prepare("SELECT id, file_path as filePath, name, kind, start_line as startLine, end_line as endLine FROM symbols WHERE service = ? AND language = 'csharp'").all(service);
|
|
7223
7226
|
const symbolLookup = buildCSharpSymbolLookup(allDbSymbols);
|
|
7224
7227
|
const allDeps = [];
|
|
7225
7228
|
for (const [relPath, cached] of fileCache) {
|
|
@@ -7266,215 +7269,1358 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7266
7269
|
};
|
|
7267
7270
|
}
|
|
7268
7271
|
|
|
7269
|
-
// src/indexer/
|
|
7272
|
+
// src/indexer/python/indexer.ts
|
|
7273
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
7270
7274
|
import { join as join12 } from "path";
|
|
7271
|
-
import {
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7275
|
+
import { globbySync as globbySync2 } from "globby";
|
|
7276
|
+
|
|
7277
|
+
// src/indexer/python/parser.ts
|
|
7278
|
+
import { accessSync as accessSync3, constants as constants3 } from "fs";
|
|
7279
|
+
import { dirname as dirname4, resolve as resolve3 } from "path";
|
|
7280
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7281
|
+
import "web-tree-sitter";
|
|
7282
|
+
var currentDir2 = dirname4(fileURLToPath3(import.meta.url));
|
|
7283
|
+
var cachedParser2 = null;
|
|
7284
|
+
var initPromise2 = null;
|
|
7285
|
+
function resolveGrammarPath2() {
|
|
7286
|
+
const candidates = [
|
|
7287
|
+
resolve3(currentDir2, "../../../wasm/tree-sitter-python.wasm"),
|
|
7288
|
+
// from src/indexer/python/
|
|
7289
|
+
resolve3(currentDir2, "../wasm/tree-sitter-python.wasm")
|
|
7290
|
+
// from dist/ (tsup flat bundle)
|
|
7291
|
+
];
|
|
7292
|
+
for (const candidate of candidates) {
|
|
7293
|
+
try {
|
|
7294
|
+
accessSync3(candidate, constants3.R_OK);
|
|
7295
|
+
return candidate;
|
|
7296
|
+
} catch {
|
|
7297
|
+
continue;
|
|
7298
|
+
}
|
|
7281
7299
|
}
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
const insert = db.prepare(
|
|
7285
|
-
"INSERT OR IGNORE INTO package_dependencies (name, version, source, service) VALUES (?, ?, ?, ?)"
|
|
7300
|
+
throw new Error(
|
|
7301
|
+
"Could not find tree-sitter-python.wasm. Ensure the wasm/ directory is present in the @arcbridge/core package."
|
|
7286
7302
|
);
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7303
|
+
}
|
|
7304
|
+
async function ensurePythonParser() {
|
|
7305
|
+
if (cachedParser2) return cachedParser2;
|
|
7306
|
+
if (initPromise2) return initPromise2;
|
|
7307
|
+
initPromise2 = (async () => {
|
|
7308
|
+
try {
|
|
7309
|
+
const TreeSitter = await import("web-tree-sitter");
|
|
7310
|
+
await TreeSitter.Parser.init();
|
|
7311
|
+
const grammarPath = resolveGrammarPath2();
|
|
7312
|
+
const Python = await TreeSitter.Language.load(grammarPath);
|
|
7313
|
+
const parser = new TreeSitter.Parser();
|
|
7314
|
+
parser.setLanguage(Python);
|
|
7315
|
+
cachedParser2 = parser;
|
|
7316
|
+
return parser;
|
|
7317
|
+
} catch (err) {
|
|
7318
|
+
initPromise2 = null;
|
|
7319
|
+
throw err;
|
|
7290
7320
|
}
|
|
7291
|
-
});
|
|
7292
|
-
return
|
|
7321
|
+
})();
|
|
7322
|
+
return initPromise2;
|
|
7293
7323
|
}
|
|
7294
|
-
function
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7324
|
+
function parsePython(content) {
|
|
7325
|
+
if (!cachedParser2) {
|
|
7326
|
+
throw new Error(
|
|
7327
|
+
"Python parser not initialized. Call await ensurePythonParser() first."
|
|
7328
|
+
);
|
|
7329
|
+
}
|
|
7330
|
+
const tree = cachedParser2.parse(content);
|
|
7331
|
+
if (!tree) {
|
|
7332
|
+
throw new Error("Failed to parse Python content");
|
|
7333
|
+
}
|
|
7334
|
+
return tree;
|
|
7335
|
+
}
|
|
7336
|
+
|
|
7337
|
+
// src/indexer/python/symbol-extractor.ts
|
|
7338
|
+
function extractPythonSymbols(tree, relativePath, contentHash) {
|
|
7339
|
+
const symbols = [];
|
|
7340
|
+
const root = tree.rootNode;
|
|
7341
|
+
for (const child of root.namedChildren) {
|
|
7342
|
+
walkNode2(child, null, relativePath, contentHash, symbols);
|
|
7343
|
+
}
|
|
7344
|
+
return symbols;
|
|
7345
|
+
}
|
|
7346
|
+
function walkNode2(node, currentClassName, relativePath, contentHash, symbols) {
|
|
7347
|
+
switch (node.type) {
|
|
7348
|
+
case "decorated_definition": {
|
|
7349
|
+
const decorators = extractDecorators(node);
|
|
7350
|
+
const definition = findChild4(node, "function_definition") ?? findChild4(node, "class_definition");
|
|
7351
|
+
if (definition) {
|
|
7352
|
+
walkDefinitionNode(definition, currentClassName, relativePath, contentHash, symbols, decorators);
|
|
7302
7353
|
}
|
|
7354
|
+
return;
|
|
7303
7355
|
}
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7356
|
+
case "function_definition":
|
|
7357
|
+
case "class_definition":
|
|
7358
|
+
walkDefinitionNode(node, currentClassName, relativePath, contentHash, symbols, []);
|
|
7359
|
+
return;
|
|
7360
|
+
case "expression_statement": {
|
|
7361
|
+
if (!currentClassName) {
|
|
7362
|
+
extractAssignment(node, relativePath, contentHash, symbols);
|
|
7307
7363
|
}
|
|
7364
|
+
return;
|
|
7308
7365
|
}
|
|
7309
|
-
return deps;
|
|
7310
|
-
} catch {
|
|
7311
|
-
return [];
|
|
7312
7366
|
}
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7367
|
+
for (const child of node.namedChildren) {
|
|
7368
|
+
walkNode2(child, currentClassName, relativePath, contentHash, symbols);
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
function walkDefinitionNode(node, currentClassName, relativePath, contentHash, symbols, decorators) {
|
|
7372
|
+
if (node.type === "function_definition") {
|
|
7373
|
+
const name = getIdentifierName2(node);
|
|
7374
|
+
if (!name) return;
|
|
7375
|
+
const isMethod = currentClassName !== null;
|
|
7376
|
+
const qualifiedName = isMethod ? `${currentClassName}.${name}` : name;
|
|
7377
|
+
const isAsync = hasAsyncKeyword(node);
|
|
7378
|
+
const signature = extractFunctionSignature(node);
|
|
7379
|
+
const returnType = extractReturnType2(node);
|
|
7380
|
+
const docComment = extractDocstring(node);
|
|
7381
|
+
const decoratorText = decorators.length > 0 ? decorators.join("\n") : null;
|
|
7382
|
+
const fullDocComment = decoratorText ? docComment ? `${decoratorText}
|
|
7383
|
+
${docComment}` : decoratorText : docComment;
|
|
7384
|
+
symbols.push(makeSymbol2({
|
|
7385
|
+
name,
|
|
7386
|
+
qualifiedName,
|
|
7387
|
+
kind: "function",
|
|
7388
|
+
node,
|
|
7389
|
+
relativePath,
|
|
7390
|
+
contentHash,
|
|
7391
|
+
isExported: !name.startsWith("_"),
|
|
7392
|
+
isAsync,
|
|
7393
|
+
signature,
|
|
7394
|
+
returnType,
|
|
7395
|
+
docComment: fullDocComment
|
|
7396
|
+
}));
|
|
7397
|
+
return;
|
|
7330
7398
|
}
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7399
|
+
if (node.type === "class_definition") {
|
|
7400
|
+
const name = getIdentifierName2(node);
|
|
7401
|
+
if (!name) return;
|
|
7402
|
+
const qualifiedName = currentClassName ? `${currentClassName}.${name}` : name;
|
|
7403
|
+
const docComment = extractDocstring(node);
|
|
7404
|
+
const decoratorText = decorators.length > 0 ? decorators.join("\n") : null;
|
|
7405
|
+
const fullDocComment = decoratorText ? docComment ? `${decoratorText}
|
|
7406
|
+
${docComment}` : decoratorText : docComment;
|
|
7407
|
+
symbols.push(makeSymbol2({
|
|
7408
|
+
name,
|
|
7409
|
+
qualifiedName,
|
|
7410
|
+
kind: "class",
|
|
7411
|
+
node,
|
|
7412
|
+
relativePath,
|
|
7413
|
+
contentHash,
|
|
7414
|
+
isExported: !name.startsWith("_"),
|
|
7415
|
+
docComment: fullDocComment
|
|
7416
|
+
}));
|
|
7417
|
+
const body = findChild4(node, "block");
|
|
7418
|
+
if (body) {
|
|
7419
|
+
for (const child of body.namedChildren) {
|
|
7420
|
+
walkNode2(child, qualifiedName, relativePath, contentHash, symbols);
|
|
7346
7421
|
}
|
|
7347
|
-
} catch {
|
|
7348
7422
|
}
|
|
7423
|
+
return;
|
|
7349
7424
|
}
|
|
7350
|
-
walk(dir, 0);
|
|
7351
|
-
return results;
|
|
7352
7425
|
}
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7426
|
+
function extractAssignment(node, relativePath, contentHash, symbols) {
|
|
7427
|
+
const assignment = findChild4(node, "assignment");
|
|
7428
|
+
if (!assignment) return;
|
|
7429
|
+
const left = assignment.childForFieldName("left") ?? assignment.namedChildren[0];
|
|
7430
|
+
if (!left || left.type !== "identifier") return;
|
|
7431
|
+
const name = left.text;
|
|
7432
|
+
if (name.startsWith("__") && name.endsWith("__")) return;
|
|
7433
|
+
const isAllCaps = /^[A-Z][A-Z0-9_]*$/.test(name);
|
|
7434
|
+
const kind = isAllCaps ? "constant" : "variable";
|
|
7435
|
+
const typeNode = assignment.childForFieldName("type");
|
|
7436
|
+
const returnType = typeNode ? typeNode.text : null;
|
|
7437
|
+
symbols.push(makeSymbol2({
|
|
7438
|
+
name,
|
|
7439
|
+
qualifiedName: name,
|
|
7440
|
+
kind,
|
|
7441
|
+
node: assignment,
|
|
7442
|
+
relativePath,
|
|
7443
|
+
contentHash,
|
|
7444
|
+
isExported: !name.startsWith("_"),
|
|
7445
|
+
returnType
|
|
7446
|
+
}));
|
|
7447
|
+
}
|
|
7448
|
+
function makeSymbol2(args) {
|
|
7449
|
+
const { name, qualifiedName, kind, node, relativePath, contentHash } = args;
|
|
7450
|
+
return {
|
|
7451
|
+
id: `${relativePath}::${qualifiedName}#${kind}`,
|
|
7452
|
+
name,
|
|
7453
|
+
qualifiedName,
|
|
7454
|
+
kind,
|
|
7455
|
+
filePath: relativePath,
|
|
7456
|
+
startLine: node.startPosition.row + 1,
|
|
7457
|
+
endLine: node.endPosition.row + 1,
|
|
7458
|
+
startCol: node.startPosition.column + 1,
|
|
7459
|
+
endCol: node.endPosition.column + 1,
|
|
7460
|
+
signature: args.signature ?? null,
|
|
7461
|
+
returnType: args.returnType ?? null,
|
|
7462
|
+
docComment: args.docComment ?? null,
|
|
7463
|
+
isExported: args.isExported ?? true,
|
|
7464
|
+
isAsync: args.isAsync ?? false,
|
|
7465
|
+
contentHash
|
|
7466
|
+
};
|
|
7467
|
+
}
|
|
7468
|
+
function getIdentifierName2(node) {
|
|
7469
|
+
const nameNode = node.childForFieldName("name");
|
|
7470
|
+
if (nameNode) return nameNode.text;
|
|
7471
|
+
for (const child of node.namedChildren) {
|
|
7472
|
+
if (child.type === "identifier") return child.text;
|
|
7373
7473
|
}
|
|
7474
|
+
return null;
|
|
7374
7475
|
}
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
if (existsSync7(join14(projectRoot, "ProjectSettings")) && existsSync7(join14(projectRoot, "Assets"))) {
|
|
7379
|
-
return "csharp";
|
|
7476
|
+
function findChild4(node, type) {
|
|
7477
|
+
for (const child of node.namedChildren) {
|
|
7478
|
+
if (child.type === type) return child;
|
|
7380
7479
|
}
|
|
7381
|
-
|
|
7382
|
-
if (existsSync7(join14(projectRoot, "package.json"))) return "typescript";
|
|
7383
|
-
if (findDotnetProject(projectRoot)) return "csharp";
|
|
7384
|
-
return "typescript";
|
|
7480
|
+
return null;
|
|
7385
7481
|
}
|
|
7386
|
-
|
|
7387
|
-
const
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7482
|
+
function hasAsyncKeyword(node) {
|
|
7483
|
+
for (const child of node.children) {
|
|
7484
|
+
if (child.type === "async") return true;
|
|
7485
|
+
if (child.type === "def") break;
|
|
7486
|
+
}
|
|
7487
|
+
return false;
|
|
7488
|
+
}
|
|
7489
|
+
function extractFunctionSignature(node) {
|
|
7490
|
+
const params = node.childForFieldName("parameters") ?? findChild4(node, "parameters");
|
|
7491
|
+
if (!params) return null;
|
|
7492
|
+
const name = getIdentifierName2(node) ?? "func";
|
|
7493
|
+
return `${name}${params.text}`;
|
|
7494
|
+
}
|
|
7495
|
+
function extractReturnType2(node) {
|
|
7496
|
+
const returnType = node.childForFieldName("return_type");
|
|
7497
|
+
if (returnType) return returnType.text;
|
|
7498
|
+
return null;
|
|
7499
|
+
}
|
|
7500
|
+
function extractDocstring(node) {
|
|
7501
|
+
const body = findChild4(node, "block");
|
|
7502
|
+
if (!body || body.namedChildren.length === 0) return null;
|
|
7503
|
+
const firstStmt = body.namedChildren[0];
|
|
7504
|
+
if (firstStmt.type !== "expression_statement") return null;
|
|
7505
|
+
const expr = firstStmt.namedChildren[0];
|
|
7506
|
+
if (!expr || expr.type !== "string") return null;
|
|
7507
|
+
let text = expr.text;
|
|
7508
|
+
if (text.startsWith('"""') && text.endsWith('"""')) {
|
|
7509
|
+
text = text.slice(3, -3);
|
|
7510
|
+
} else if (text.startsWith("'''") && text.endsWith("'''")) {
|
|
7511
|
+
text = text.slice(3, -3);
|
|
7512
|
+
} else if (text.startsWith('"') && text.endsWith('"')) {
|
|
7513
|
+
text = text.slice(1, -1);
|
|
7514
|
+
} else if (text.startsWith("'") && text.endsWith("'")) {
|
|
7515
|
+
text = text.slice(1, -1);
|
|
7516
|
+
}
|
|
7517
|
+
return text.trim() || null;
|
|
7518
|
+
}
|
|
7519
|
+
function extractDecorators(node) {
|
|
7520
|
+
const decorators = [];
|
|
7521
|
+
for (const child of node.namedChildren) {
|
|
7522
|
+
if (child.type === "decorator") {
|
|
7523
|
+
decorators.push(child.text);
|
|
7397
7524
|
}
|
|
7398
|
-
return await indexCSharpTreeSitter(db, {
|
|
7399
|
-
projectRoot: options.projectRoot,
|
|
7400
|
-
service: options.service
|
|
7401
|
-
});
|
|
7402
7525
|
}
|
|
7403
|
-
return
|
|
7526
|
+
return decorators;
|
|
7404
7527
|
}
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
}
|
|
7416
|
-
} catch {
|
|
7528
|
+
|
|
7529
|
+
// src/indexer/python/dependency-extractor.ts
|
|
7530
|
+
function buildPythonSymbolLookup(symbols) {
|
|
7531
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
7532
|
+
for (const sym of symbols) {
|
|
7533
|
+
const existing = lookup.get(sym.name);
|
|
7534
|
+
if (existing) {
|
|
7535
|
+
existing.push(sym.id);
|
|
7536
|
+
} else {
|
|
7537
|
+
lookup.set(sym.name, [sym.id]);
|
|
7417
7538
|
}
|
|
7418
7539
|
}
|
|
7419
|
-
|
|
7420
|
-
|
|
7540
|
+
return lookup;
|
|
7541
|
+
}
|
|
7542
|
+
function extractPythonDependencies(tree, relativePath, allSymbols, symbolLookup) {
|
|
7543
|
+
const deps = [];
|
|
7544
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7545
|
+
const fileSymbols = allSymbols.filter((s) => s.filePath === relativePath);
|
|
7546
|
+
walkForDependencies2(tree.rootNode, fileSymbols, symbolLookup, deps, seen);
|
|
7547
|
+
return deps;
|
|
7548
|
+
}
|
|
7549
|
+
function walkForDependencies2(node, fileSymbols, lookup, deps, seen) {
|
|
7550
|
+
switch (node.type) {
|
|
7551
|
+
case "class_definition":
|
|
7552
|
+
extractInheritanceDeps2(node, fileSymbols, lookup, deps, seen);
|
|
7553
|
+
break;
|
|
7554
|
+
case "call":
|
|
7555
|
+
extractCallDeps2(node, fileSymbols, lookup, deps, seen);
|
|
7556
|
+
break;
|
|
7421
7557
|
}
|
|
7422
|
-
if (
|
|
7423
|
-
|
|
7558
|
+
if (node.type === "typed_parameter" || node.type === "typed_default_parameter") {
|
|
7559
|
+
extractTypeAnnotationDeps(node, fileSymbols, lookup, deps, seen);
|
|
7424
7560
|
}
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
execFileSync2("dotnet", ["--version"], {
|
|
7428
|
-
encoding: "utf-8",
|
|
7429
|
-
timeout: 5e3
|
|
7430
|
-
});
|
|
7431
|
-
return "roslyn";
|
|
7432
|
-
} catch {
|
|
7433
|
-
}
|
|
7561
|
+
for (const child of node.namedChildren) {
|
|
7562
|
+
walkForDependencies2(child, fileSymbols, lookup, deps, seen);
|
|
7434
7563
|
}
|
|
7435
|
-
return "tree-sitter";
|
|
7436
7564
|
}
|
|
7437
|
-
function
|
|
7438
|
-
const
|
|
7439
|
-
|
|
7440
|
-
const
|
|
7441
|
-
|
|
7442
|
-
const
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
const
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
}
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
const
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7565
|
+
function extractInheritanceDeps2(node, fileSymbols, lookup, deps, seen) {
|
|
7566
|
+
const className = getNodeIdentifier2(node);
|
|
7567
|
+
if (!className) return;
|
|
7568
|
+
const sourceSymbol = findEnclosingSymbol2(className, fileSymbols, "class");
|
|
7569
|
+
if (!sourceSymbol) return;
|
|
7570
|
+
const argList = node.childForFieldName("superclasses") ?? findChild5(node, "argument_list");
|
|
7571
|
+
if (!argList) return;
|
|
7572
|
+
for (const child of argList.namedChildren) {
|
|
7573
|
+
const baseName = extractSimpleTypeName2(child);
|
|
7574
|
+
if (!baseName || baseName === className) continue;
|
|
7575
|
+
const targetIds = lookup.get(baseName);
|
|
7576
|
+
if (!targetIds) continue;
|
|
7577
|
+
for (const targetId of targetIds) {
|
|
7578
|
+
if (targetId === sourceSymbol.id) continue;
|
|
7579
|
+
addDep2(deps, seen, sourceSymbol.id, targetId, "extends");
|
|
7580
|
+
}
|
|
7581
|
+
}
|
|
7582
|
+
}
|
|
7583
|
+
function extractCallDeps2(node, fileSymbols, lookup, deps, seen) {
|
|
7584
|
+
const calledName = extractCalledFunctionName(node);
|
|
7585
|
+
if (!calledName) return;
|
|
7586
|
+
const enclosing = findEnclosingFunctionForNode(node, fileSymbols);
|
|
7587
|
+
if (!enclosing) return;
|
|
7588
|
+
const targetIds = lookup.get(calledName);
|
|
7589
|
+
if (!targetIds) return;
|
|
7590
|
+
for (const targetId of targetIds) {
|
|
7591
|
+
if (targetId === enclosing.id) continue;
|
|
7592
|
+
addDep2(deps, seen, enclosing.id, targetId, "calls");
|
|
7593
|
+
}
|
|
7594
|
+
}
|
|
7595
|
+
function extractTypeAnnotationDeps(node, fileSymbols, lookup, deps, seen) {
|
|
7596
|
+
const typeNames = collectTypeIdentifiers2(node);
|
|
7597
|
+
if (typeNames.length === 0) return;
|
|
7598
|
+
const enclosing = findEnclosingSymbolForNode2(node, fileSymbols);
|
|
7599
|
+
if (!enclosing) return;
|
|
7600
|
+
for (const typeName of typeNames) {
|
|
7601
|
+
const targetIds = lookup.get(typeName);
|
|
7602
|
+
if (!targetIds) continue;
|
|
7603
|
+
for (const targetId of targetIds) {
|
|
7604
|
+
if (targetId === enclosing.id) continue;
|
|
7605
|
+
addDep2(deps, seen, enclosing.id, targetId, "uses_type");
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
function addDep2(deps, seen, source, target, kind) {
|
|
7610
|
+
const key = `${source}|${target}|${kind}`;
|
|
7611
|
+
if (seen.has(key)) return;
|
|
7612
|
+
seen.add(key);
|
|
7613
|
+
deps.push({ sourceSymbolId: source, targetSymbolId: target, kind });
|
|
7614
|
+
}
|
|
7615
|
+
function findChild5(node, type) {
|
|
7616
|
+
for (const child of node.namedChildren) {
|
|
7617
|
+
if (child.type === type) return child;
|
|
7618
|
+
}
|
|
7619
|
+
return null;
|
|
7620
|
+
}
|
|
7621
|
+
function getNodeIdentifier2(node) {
|
|
7622
|
+
const nameNode = node.childForFieldName("name");
|
|
7623
|
+
if (nameNode) return nameNode.text;
|
|
7624
|
+
for (const child of node.namedChildren) {
|
|
7625
|
+
if (child.type === "identifier") return child.text;
|
|
7626
|
+
}
|
|
7627
|
+
return null;
|
|
7628
|
+
}
|
|
7629
|
+
function extractSimpleTypeName2(node) {
|
|
7630
|
+
if (node.type === "identifier") return node.text;
|
|
7631
|
+
if (node.type === "attribute") {
|
|
7632
|
+
const attrNode = node.childForFieldName("attribute");
|
|
7633
|
+
return attrNode?.text ?? null;
|
|
7634
|
+
}
|
|
7635
|
+
if (node.type === "subscript") {
|
|
7636
|
+
const value = node.childForFieldName("value") ?? node.namedChildren[0];
|
|
7637
|
+
if (value) return extractSimpleTypeName2(value);
|
|
7638
|
+
}
|
|
7639
|
+
if (node.type === "keyword_argument") {
|
|
7640
|
+
const value = node.childForFieldName("value") ?? node.namedChildren[1];
|
|
7641
|
+
if (value) return extractSimpleTypeName2(value);
|
|
7642
|
+
}
|
|
7643
|
+
for (const child of node.namedChildren) {
|
|
7644
|
+
if (child.type === "identifier") return child.text;
|
|
7645
|
+
}
|
|
7646
|
+
return null;
|
|
7647
|
+
}
|
|
7648
|
+
function extractCalledFunctionName(node) {
|
|
7649
|
+
const funcNode = node.childForFieldName("function") ?? node.namedChildren[0];
|
|
7650
|
+
if (!funcNode) return null;
|
|
7651
|
+
if (funcNode.type === "identifier") return funcNode.text;
|
|
7652
|
+
if (funcNode.type === "attribute") {
|
|
7653
|
+
const attrNode = funcNode.childForFieldName("attribute");
|
|
7654
|
+
return attrNode?.text ?? null;
|
|
7655
|
+
}
|
|
7656
|
+
return null;
|
|
7657
|
+
}
|
|
7658
|
+
function findEnclosingSymbol2(name, fileSymbols, ...kinds) {
|
|
7659
|
+
return fileSymbols.find(
|
|
7660
|
+
(s) => s.name === name && kinds.includes(s.kind)
|
|
7661
|
+
);
|
|
7662
|
+
}
|
|
7663
|
+
function findEnclosingFunctionForNode(node, fileSymbols) {
|
|
7664
|
+
let current = node.parent;
|
|
7665
|
+
while (current) {
|
|
7666
|
+
if (current.type === "function_definition") {
|
|
7667
|
+
const name = getNodeIdentifier2(current);
|
|
7668
|
+
if (name) {
|
|
7669
|
+
return fileSymbols.find(
|
|
7670
|
+
(s) => s.kind === "function" && s.name === name && s.startLine <= current.startPosition.row + 1 && s.endLine >= current.endPosition.row + 1
|
|
7671
|
+
);
|
|
7672
|
+
}
|
|
7673
|
+
}
|
|
7674
|
+
current = current.parent;
|
|
7675
|
+
}
|
|
7676
|
+
return void 0;
|
|
7677
|
+
}
|
|
7678
|
+
function findEnclosingSymbolForNode2(node, fileSymbols) {
|
|
7679
|
+
let current = node.parent;
|
|
7680
|
+
while (current) {
|
|
7681
|
+
if (current.type === "function_definition") {
|
|
7682
|
+
const name = getNodeIdentifier2(current);
|
|
7683
|
+
if (name) {
|
|
7684
|
+
const found = fileSymbols.find(
|
|
7685
|
+
(s) => s.kind === "function" && s.name === name && s.startLine <= current.startPosition.row + 1 && s.endLine >= current.endPosition.row + 1
|
|
7686
|
+
);
|
|
7687
|
+
if (found) return found;
|
|
7688
|
+
}
|
|
7689
|
+
}
|
|
7690
|
+
if (current.type === "class_definition") {
|
|
7691
|
+
const name = getNodeIdentifier2(current);
|
|
7692
|
+
if (name) {
|
|
7693
|
+
return fileSymbols.find(
|
|
7694
|
+
(s) => s.name === name && s.kind === "class"
|
|
7695
|
+
);
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
current = current.parent;
|
|
7699
|
+
}
|
|
7700
|
+
return void 0;
|
|
7701
|
+
}
|
|
7702
|
+
function collectTypeIdentifiers2(node) {
|
|
7703
|
+
const names = [];
|
|
7704
|
+
collectTypeIdentifiersRecursive2(node, names);
|
|
7705
|
+
return [...new Set(names)];
|
|
7706
|
+
}
|
|
7707
|
+
function collectTypeIdentifiersRecursive2(node, names) {
|
|
7708
|
+
if (node.type === "attribute") {
|
|
7709
|
+
const attrNode = node.childForFieldName("attribute");
|
|
7710
|
+
if (attrNode && attrNode.type === "identifier") {
|
|
7711
|
+
names.push(attrNode.text);
|
|
7712
|
+
}
|
|
7713
|
+
return;
|
|
7714
|
+
}
|
|
7715
|
+
if (node.type === "identifier" && isTypeContext2(node)) {
|
|
7716
|
+
names.push(node.text);
|
|
7717
|
+
}
|
|
7718
|
+
for (const child of node.namedChildren) {
|
|
7719
|
+
collectTypeIdentifiersRecursive2(child, names);
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
7722
|
+
function isTypeContext2(node) {
|
|
7723
|
+
const parent = node.parent;
|
|
7724
|
+
if (!parent) return false;
|
|
7725
|
+
return parent.type === "type" || parent.type === "subscript" || // In typed_parameter, the type annotation is the second named child
|
|
7726
|
+
parent.type === "typed_parameter" && node !== parent.childForFieldName("name") || parent.type === "typed_default_parameter" && node !== parent.childForFieldName("name");
|
|
7727
|
+
}
|
|
7728
|
+
|
|
7729
|
+
// src/indexer/python/indexer.ts
|
|
7730
|
+
async function indexPythonTreeSitter(db, options) {
|
|
7731
|
+
const start = Date.now();
|
|
7732
|
+
await ensurePythonParser();
|
|
7733
|
+
const service = options.service ?? "main";
|
|
7734
|
+
const projectRoot = options.projectRoot;
|
|
7735
|
+
const ignorePatterns = [
|
|
7736
|
+
"**/__pycache__/**",
|
|
7737
|
+
"**/.venv/**",
|
|
7738
|
+
"**/venv/**",
|
|
7739
|
+
"**/.tox/**",
|
|
7740
|
+
"**/node_modules/**",
|
|
7741
|
+
"**/.git/**",
|
|
7742
|
+
"**/dist/**",
|
|
7743
|
+
"**/build/**",
|
|
7744
|
+
"**/.eggs/**",
|
|
7745
|
+
"**/*.egg-info/**"
|
|
7746
|
+
];
|
|
7747
|
+
const pyFiles = globbySync2("**/*.py", {
|
|
7748
|
+
cwd: projectRoot,
|
|
7749
|
+
ignore: ignorePatterns,
|
|
7750
|
+
absolute: false
|
|
7751
|
+
});
|
|
7752
|
+
const existingHashes = getExistingHashes(db, service, "python");
|
|
7753
|
+
const currentPaths = /* @__PURE__ */ new Set();
|
|
7754
|
+
const fileCache = /* @__PURE__ */ new Map();
|
|
7755
|
+
const changedFiles = [];
|
|
7756
|
+
let filesSkipped = 0;
|
|
7757
|
+
for (const filePath of pyFiles) {
|
|
7758
|
+
const relPath = filePath.replace(/\\/g, "/");
|
|
7759
|
+
currentPaths.add(relPath);
|
|
7760
|
+
const fullPath = join12(projectRoot, relPath);
|
|
7761
|
+
const content = readFileSync5(fullPath, "utf-8");
|
|
7762
|
+
const hash = hashContent(content);
|
|
7763
|
+
const tree = parsePython(content);
|
|
7764
|
+
fileCache.set(relPath, { content, tree, hash });
|
|
7765
|
+
const existingHash = existingHashes.get(relPath);
|
|
7766
|
+
if (existingHash === hash) {
|
|
7767
|
+
filesSkipped++;
|
|
7768
|
+
} else {
|
|
7769
|
+
changedFiles.push(relPath);
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
const removed = [];
|
|
7773
|
+
for (const existingPath of existingHashes.keys()) {
|
|
7774
|
+
if (!currentPaths.has(existingPath)) {
|
|
7775
|
+
removed.push(existingPath);
|
|
7776
|
+
}
|
|
7777
|
+
}
|
|
7778
|
+
const filesToClean = [...removed, ...changedFiles];
|
|
7779
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "python");
|
|
7780
|
+
const allNewSymbols = [];
|
|
7781
|
+
for (const relPath of changedFiles) {
|
|
7782
|
+
const cached = fileCache.get(relPath);
|
|
7783
|
+
if (!cached) continue;
|
|
7784
|
+
const symbols = extractPythonSymbols(cached.tree, relPath, cached.hash);
|
|
7785
|
+
allNewSymbols.push(...symbols);
|
|
7786
|
+
}
|
|
7787
|
+
writeSymbols(db, allNewSymbols, service, "python");
|
|
7788
|
+
const allDbSymbols = db.prepare("SELECT id, file_path as filePath, name, kind, start_line as startLine, end_line as endLine FROM symbols WHERE service = ? AND language = 'python'").all(service);
|
|
7789
|
+
const symbolLookup = buildPythonSymbolLookup(allDbSymbols);
|
|
7790
|
+
const allDeps = [];
|
|
7791
|
+
for (const [relPath, cached] of fileCache) {
|
|
7792
|
+
const fileDeps = extractPythonDependencies(cached.tree, relPath, allDbSymbols, symbolLookup);
|
|
7793
|
+
allDeps.push(...fileDeps);
|
|
7794
|
+
}
|
|
7795
|
+
db.prepare(
|
|
7796
|
+
"DELETE FROM dependencies WHERE source_symbol IN (SELECT id FROM symbols WHERE service = ? AND language = 'python')"
|
|
7797
|
+
).run(service);
|
|
7798
|
+
writeDependencies(db, allDeps);
|
|
7799
|
+
return {
|
|
7800
|
+
symbolsIndexed: allNewSymbols.length,
|
|
7801
|
+
dependenciesIndexed: allDeps.length,
|
|
7802
|
+
componentsAnalyzed: 0,
|
|
7803
|
+
routesAnalyzed: 0,
|
|
7804
|
+
filesProcessed: changedFiles.length,
|
|
7805
|
+
filesSkipped,
|
|
7806
|
+
filesRemoved: removed.length,
|
|
7807
|
+
durationMs: Date.now() - start
|
|
7808
|
+
};
|
|
7809
|
+
}
|
|
7810
|
+
|
|
7811
|
+
// src/indexer/go/indexer.ts
|
|
7812
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
7813
|
+
import { join as join13 } from "path";
|
|
7814
|
+
import { globbySync as globbySync3 } from "globby";
|
|
7815
|
+
|
|
7816
|
+
// src/indexer/go/parser.ts
|
|
7817
|
+
import { accessSync as accessSync4, constants as constants4 } from "fs";
|
|
7818
|
+
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
7819
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
7820
|
+
import "web-tree-sitter";
|
|
7821
|
+
var currentDir3 = dirname5(fileURLToPath4(import.meta.url));
|
|
7822
|
+
var cachedParser3 = null;
|
|
7823
|
+
var initPromise3 = null;
|
|
7824
|
+
function resolveGrammarPath3() {
|
|
7825
|
+
const candidates = [
|
|
7826
|
+
resolve4(currentDir3, "../../../wasm/tree-sitter-go.wasm"),
|
|
7827
|
+
// from src/indexer/go/
|
|
7828
|
+
resolve4(currentDir3, "../wasm/tree-sitter-go.wasm")
|
|
7829
|
+
// from dist/ (tsup flat bundle)
|
|
7830
|
+
];
|
|
7831
|
+
for (const candidate of candidates) {
|
|
7832
|
+
try {
|
|
7833
|
+
accessSync4(candidate, constants4.R_OK);
|
|
7834
|
+
return candidate;
|
|
7835
|
+
} catch {
|
|
7836
|
+
continue;
|
|
7837
|
+
}
|
|
7838
|
+
}
|
|
7839
|
+
throw new Error(
|
|
7840
|
+
"Could not find tree-sitter-go.wasm. Ensure the wasm/ directory is present in the @arcbridge/core package."
|
|
7841
|
+
);
|
|
7842
|
+
}
|
|
7843
|
+
async function ensureGoParser() {
|
|
7844
|
+
if (cachedParser3) return cachedParser3;
|
|
7845
|
+
if (initPromise3) return initPromise3;
|
|
7846
|
+
initPromise3 = (async () => {
|
|
7847
|
+
try {
|
|
7848
|
+
const TreeSitter = await import("web-tree-sitter");
|
|
7849
|
+
await TreeSitter.Parser.init();
|
|
7850
|
+
const grammarPath = resolveGrammarPath3();
|
|
7851
|
+
const Go = await TreeSitter.Language.load(grammarPath);
|
|
7852
|
+
const parser = new TreeSitter.Parser();
|
|
7853
|
+
parser.setLanguage(Go);
|
|
7854
|
+
cachedParser3 = parser;
|
|
7855
|
+
return parser;
|
|
7856
|
+
} catch (err) {
|
|
7857
|
+
initPromise3 = null;
|
|
7858
|
+
throw err;
|
|
7859
|
+
}
|
|
7860
|
+
})();
|
|
7861
|
+
return initPromise3;
|
|
7862
|
+
}
|
|
7863
|
+
function parseGo(content) {
|
|
7864
|
+
if (!cachedParser3) {
|
|
7865
|
+
throw new Error(
|
|
7866
|
+
"Go parser not initialized. Call await ensureGoParser() first."
|
|
7867
|
+
);
|
|
7868
|
+
}
|
|
7869
|
+
const tree = cachedParser3.parse(content);
|
|
7870
|
+
if (!tree) {
|
|
7871
|
+
throw new Error("Failed to parse Go content");
|
|
7872
|
+
}
|
|
7873
|
+
return tree;
|
|
7874
|
+
}
|
|
7875
|
+
|
|
7876
|
+
// src/indexer/go/symbol-extractor.ts
|
|
7877
|
+
function extractGoSymbols(tree, relativePath, contentHash) {
|
|
7878
|
+
const symbols = [];
|
|
7879
|
+
const root = tree.rootNode;
|
|
7880
|
+
for (const child of root.namedChildren) {
|
|
7881
|
+
walkNode3(child, relativePath, contentHash, symbols);
|
|
7882
|
+
}
|
|
7883
|
+
return symbols;
|
|
7884
|
+
}
|
|
7885
|
+
function walkNode3(node, relativePath, contentHash, symbols) {
|
|
7886
|
+
switch (node.type) {
|
|
7887
|
+
case "function_declaration": {
|
|
7888
|
+
const name = getFieldName(node);
|
|
7889
|
+
if (!name) break;
|
|
7890
|
+
const signature = extractFunctionSignature2(node, name);
|
|
7891
|
+
const returnType = extractReturnType3(node);
|
|
7892
|
+
symbols.push(makeSymbol3({
|
|
7893
|
+
name,
|
|
7894
|
+
qualifiedName: name,
|
|
7895
|
+
kind: "function",
|
|
7896
|
+
node,
|
|
7897
|
+
relativePath,
|
|
7898
|
+
contentHash,
|
|
7899
|
+
docComment: extractDocComment2(node),
|
|
7900
|
+
signature,
|
|
7901
|
+
returnType
|
|
7902
|
+
}));
|
|
7903
|
+
return;
|
|
7904
|
+
}
|
|
7905
|
+
case "method_declaration": {
|
|
7906
|
+
const name = getFieldName(node);
|
|
7907
|
+
if (!name) break;
|
|
7908
|
+
const receiverType = extractReceiverType(node);
|
|
7909
|
+
const qualifiedName = receiverType ? `${receiverType}.${name}` : name;
|
|
7910
|
+
const signature = extractFunctionSignature2(node, name);
|
|
7911
|
+
const returnType = extractReturnType3(node);
|
|
7912
|
+
symbols.push(makeSymbol3({
|
|
7913
|
+
name,
|
|
7914
|
+
qualifiedName,
|
|
7915
|
+
kind: "function",
|
|
7916
|
+
node,
|
|
7917
|
+
relativePath,
|
|
7918
|
+
contentHash,
|
|
7919
|
+
docComment: extractDocComment2(node),
|
|
7920
|
+
signature,
|
|
7921
|
+
returnType
|
|
7922
|
+
}));
|
|
7923
|
+
return;
|
|
7924
|
+
}
|
|
7925
|
+
case "type_declaration": {
|
|
7926
|
+
for (const child of node.namedChildren) {
|
|
7927
|
+
if (child.type === "type_spec") {
|
|
7928
|
+
extractTypeSpec(child, node, relativePath, contentHash, symbols);
|
|
7929
|
+
}
|
|
7930
|
+
}
|
|
7931
|
+
return;
|
|
7932
|
+
}
|
|
7933
|
+
case "const_declaration": {
|
|
7934
|
+
for (const child of node.namedChildren) {
|
|
7935
|
+
if (child.type === "const_spec") {
|
|
7936
|
+
const names = getSpecNames(child);
|
|
7937
|
+
const doc = extractDocComment2(child) ?? extractDocComment2(node);
|
|
7938
|
+
for (const name of names) {
|
|
7939
|
+
symbols.push(makeSymbol3({
|
|
7940
|
+
name,
|
|
7941
|
+
qualifiedName: name,
|
|
7942
|
+
kind: "constant",
|
|
7943
|
+
node: child,
|
|
7944
|
+
relativePath,
|
|
7945
|
+
contentHash,
|
|
7946
|
+
docComment: doc
|
|
7947
|
+
}));
|
|
7948
|
+
}
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
return;
|
|
7952
|
+
}
|
|
7953
|
+
case "var_declaration": {
|
|
7954
|
+
for (const child of node.namedChildren) {
|
|
7955
|
+
if (child.type === "var_spec") {
|
|
7956
|
+
const names = getSpecNames(child);
|
|
7957
|
+
const doc = extractDocComment2(child) ?? extractDocComment2(node);
|
|
7958
|
+
for (const name of names) {
|
|
7959
|
+
symbols.push(makeSymbol3({
|
|
7960
|
+
name,
|
|
7961
|
+
qualifiedName: name,
|
|
7962
|
+
kind: "variable",
|
|
7963
|
+
node: child,
|
|
7964
|
+
relativePath,
|
|
7965
|
+
contentHash,
|
|
7966
|
+
docComment: doc
|
|
7967
|
+
}));
|
|
7968
|
+
}
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7971
|
+
return;
|
|
7972
|
+
}
|
|
7973
|
+
}
|
|
7974
|
+
for (const child of node.namedChildren) {
|
|
7975
|
+
walkNode3(child, relativePath, contentHash, symbols);
|
|
7976
|
+
}
|
|
7977
|
+
}
|
|
7978
|
+
function extractTypeSpec(spec, parentDecl, relativePath, contentHash, symbols) {
|
|
7979
|
+
const name = getFieldName(spec);
|
|
7980
|
+
if (!name) return;
|
|
7981
|
+
const typeNode = spec.childForFieldName("type");
|
|
7982
|
+
let kind = "type";
|
|
7983
|
+
if (typeNode) {
|
|
7984
|
+
if (typeNode.type === "struct_type") {
|
|
7985
|
+
kind = "class";
|
|
7986
|
+
} else if (typeNode.type === "interface_type") {
|
|
7987
|
+
kind = "interface";
|
|
7988
|
+
}
|
|
7989
|
+
}
|
|
7990
|
+
symbols.push(makeSymbol3({
|
|
7991
|
+
name,
|
|
7992
|
+
qualifiedName: name,
|
|
7993
|
+
kind,
|
|
7994
|
+
node: spec,
|
|
7995
|
+
relativePath,
|
|
7996
|
+
contentHash,
|
|
7997
|
+
docComment: extractDocComment2(spec) ?? extractDocComment2(parentDecl)
|
|
7998
|
+
}));
|
|
7999
|
+
}
|
|
8000
|
+
function makeSymbol3(args) {
|
|
8001
|
+
const { name, qualifiedName, kind, node, relativePath, contentHash, docComment } = args;
|
|
8002
|
+
return {
|
|
8003
|
+
id: `${relativePath}::${qualifiedName}#${kind}`,
|
|
8004
|
+
name,
|
|
8005
|
+
qualifiedName,
|
|
8006
|
+
kind,
|
|
8007
|
+
filePath: relativePath,
|
|
8008
|
+
startLine: node.startPosition.row + 1,
|
|
8009
|
+
endLine: node.endPosition.row + 1,
|
|
8010
|
+
startCol: node.startPosition.column + 1,
|
|
8011
|
+
endCol: node.endPosition.column + 1,
|
|
8012
|
+
signature: args.signature ?? null,
|
|
8013
|
+
returnType: args.returnType ?? null,
|
|
8014
|
+
docComment,
|
|
8015
|
+
isExported: /^[A-Z]/.test(name),
|
|
8016
|
+
isAsync: false,
|
|
8017
|
+
contentHash
|
|
8018
|
+
};
|
|
8019
|
+
}
|
|
8020
|
+
function getFieldName(node) {
|
|
8021
|
+
const nameNode = node.childForFieldName("name");
|
|
8022
|
+
if (nameNode) return nameNode.text;
|
|
8023
|
+
for (const child of node.namedChildren) {
|
|
8024
|
+
if (child.type === "identifier") return child.text;
|
|
8025
|
+
}
|
|
8026
|
+
return null;
|
|
8027
|
+
}
|
|
8028
|
+
function getSpecNames(node) {
|
|
8029
|
+
const names = [];
|
|
8030
|
+
for (const child of node.namedChildren) {
|
|
8031
|
+
if (child.type === "identifier") {
|
|
8032
|
+
names.push(child.text);
|
|
8033
|
+
} else {
|
|
8034
|
+
break;
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
return names;
|
|
8038
|
+
}
|
|
8039
|
+
function extractReceiverType(node) {
|
|
8040
|
+
const receiver = node.childForFieldName("receiver");
|
|
8041
|
+
if (!receiver) return null;
|
|
8042
|
+
for (const child of receiver.namedChildren) {
|
|
8043
|
+
if (child.type === "parameter_declaration") {
|
|
8044
|
+
const typeNode = child.childForFieldName("type");
|
|
8045
|
+
if (typeNode) {
|
|
8046
|
+
if (typeNode.type === "pointer_type") {
|
|
8047
|
+
const inner = typeNode.namedChildren[0];
|
|
8048
|
+
return inner ? inner.text : typeNode.text.replace(/^\*/, "");
|
|
8049
|
+
}
|
|
8050
|
+
return typeNode.text;
|
|
8051
|
+
}
|
|
8052
|
+
}
|
|
8053
|
+
}
|
|
8054
|
+
return null;
|
|
8055
|
+
}
|
|
8056
|
+
function extractDocComment2(node) {
|
|
8057
|
+
const comments = [];
|
|
8058
|
+
let sibling = node.previousSibling;
|
|
8059
|
+
while (sibling) {
|
|
8060
|
+
if (sibling.type === "comment" && sibling.text.startsWith("//")) {
|
|
8061
|
+
comments.unshift(sibling.text);
|
|
8062
|
+
} else {
|
|
8063
|
+
break;
|
|
8064
|
+
}
|
|
8065
|
+
sibling = sibling.previousSibling;
|
|
8066
|
+
}
|
|
8067
|
+
if (comments.length === 0) return null;
|
|
8068
|
+
return comments.map((c) => c.replace(/^\s*\/\/\s?/, "").trim()).filter(Boolean).join(" ");
|
|
8069
|
+
}
|
|
8070
|
+
function extractFunctionSignature2(node, name) {
|
|
8071
|
+
const params = node.childForFieldName("parameters");
|
|
8072
|
+
if (!params) return null;
|
|
8073
|
+
return `${name}${params.text}`;
|
|
8074
|
+
}
|
|
8075
|
+
function extractReturnType3(node) {
|
|
8076
|
+
const result = node.childForFieldName("result");
|
|
8077
|
+
if (!result) return null;
|
|
8078
|
+
return result.text;
|
|
8079
|
+
}
|
|
8080
|
+
|
|
8081
|
+
// src/indexer/go/dependency-extractor.ts
|
|
8082
|
+
function buildGoSymbolLookup(symbols) {
|
|
8083
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
8084
|
+
for (const sym of symbols) {
|
|
8085
|
+
const existing = lookup.get(sym.name);
|
|
8086
|
+
if (existing) {
|
|
8087
|
+
existing.push(sym.id);
|
|
8088
|
+
} else {
|
|
8089
|
+
lookup.set(sym.name, [sym.id]);
|
|
8090
|
+
}
|
|
8091
|
+
}
|
|
8092
|
+
return lookup;
|
|
8093
|
+
}
|
|
8094
|
+
function extractGoDependencies(tree, relativePath, allSymbols, symbolLookup) {
|
|
8095
|
+
const deps = [];
|
|
8096
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8097
|
+
const fileSymbols = allSymbols.filter((s) => s.filePath === relativePath);
|
|
8098
|
+
walkForDependencies3(tree.rootNode, fileSymbols, symbolLookup, deps, seen);
|
|
8099
|
+
return deps;
|
|
8100
|
+
}
|
|
8101
|
+
function walkForDependencies3(node, fileSymbols, lookup, deps, seen) {
|
|
8102
|
+
switch (node.type) {
|
|
8103
|
+
case "type_declaration":
|
|
8104
|
+
for (const child of node.namedChildren) {
|
|
8105
|
+
if (child.type === "type_spec") {
|
|
8106
|
+
extractEmbeddingDeps(child, fileSymbols, lookup, deps, seen);
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
8109
|
+
break;
|
|
8110
|
+
case "call_expression":
|
|
8111
|
+
extractCallDeps3(node, fileSymbols, lookup, deps, seen);
|
|
8112
|
+
break;
|
|
8113
|
+
}
|
|
8114
|
+
if (node.type === "parameter_declaration" || node.type === "field_declaration" || node.type === "var_spec") {
|
|
8115
|
+
extractTypeRefDeps2(node, fileSymbols, lookup, deps, seen);
|
|
8116
|
+
}
|
|
8117
|
+
for (const child of node.namedChildren) {
|
|
8118
|
+
walkForDependencies3(child, fileSymbols, lookup, deps, seen);
|
|
8119
|
+
}
|
|
8120
|
+
}
|
|
8121
|
+
function extractEmbeddingDeps(typeSpec, fileSymbols, lookup, deps, seen) {
|
|
8122
|
+
const typeName = getNodeIdentifier3(typeSpec);
|
|
8123
|
+
if (!typeName) return;
|
|
8124
|
+
const sourceSymbol = findEnclosingSymbol3(typeName, fileSymbols, "class", "interface");
|
|
8125
|
+
if (!sourceSymbol) return;
|
|
8126
|
+
const typeNode = typeSpec.childForFieldName("type");
|
|
8127
|
+
if (!typeNode) return;
|
|
8128
|
+
if (typeNode.type === "struct_type" || typeNode.type === "interface_type") {
|
|
8129
|
+
const fieldList = findChild6(typeNode, "field_declaration_list") ?? typeNode;
|
|
8130
|
+
for (const field of fieldList.namedChildren) {
|
|
8131
|
+
if (field.type === "field_declaration") {
|
|
8132
|
+
const nameNode = field.childForFieldName("name");
|
|
8133
|
+
if (nameNode) continue;
|
|
8134
|
+
const embeddedTypeName = extractSimpleTypeName3(field);
|
|
8135
|
+
if (!embeddedTypeName || embeddedTypeName === typeName) continue;
|
|
8136
|
+
const targetIds = lookup.get(embeddedTypeName);
|
|
8137
|
+
if (!targetIds) continue;
|
|
8138
|
+
for (const targetId of targetIds) {
|
|
8139
|
+
if (targetId === sourceSymbol.id) continue;
|
|
8140
|
+
addDep3(deps, seen, sourceSymbol.id, targetId, "extends");
|
|
8141
|
+
}
|
|
8142
|
+
}
|
|
8143
|
+
if (field.type === "type_name" || field.type === "qualified_type" || field.type === "type_identifier") {
|
|
8144
|
+
const embeddedName = extractSimpleTypeNameFromNode(field);
|
|
8145
|
+
if (!embeddedName || embeddedName === typeName) continue;
|
|
8146
|
+
const targetIds = lookup.get(embeddedName);
|
|
8147
|
+
if (!targetIds) continue;
|
|
8148
|
+
for (const targetId of targetIds) {
|
|
8149
|
+
if (targetId === sourceSymbol.id) continue;
|
|
8150
|
+
addDep3(deps, seen, sourceSymbol.id, targetId, "extends");
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
}
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
function extractCallDeps3(node, fileSymbols, lookup, deps, seen) {
|
|
8157
|
+
const calledName = extractCalledFunctionName2(node);
|
|
8158
|
+
if (!calledName) return;
|
|
8159
|
+
const enclosing = findEnclosingFunctionForNode2(node, fileSymbols);
|
|
8160
|
+
if (!enclosing) return;
|
|
8161
|
+
const targetIds = lookup.get(calledName);
|
|
8162
|
+
if (!targetIds) return;
|
|
8163
|
+
for (const targetId of targetIds) {
|
|
8164
|
+
if (targetId === enclosing.id) continue;
|
|
8165
|
+
addDep3(deps, seen, enclosing.id, targetId, "calls");
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
function extractTypeRefDeps2(node, fileSymbols, lookup, deps, seen) {
|
|
8169
|
+
const typeNames = collectTypeIdentifiers3(node);
|
|
8170
|
+
if (typeNames.length === 0) return;
|
|
8171
|
+
const enclosing = findEnclosingSymbolForNode3(node, fileSymbols);
|
|
8172
|
+
if (!enclosing) return;
|
|
8173
|
+
for (const typeName of typeNames) {
|
|
8174
|
+
const targetIds = lookup.get(typeName);
|
|
8175
|
+
if (!targetIds) continue;
|
|
8176
|
+
for (const targetId of targetIds) {
|
|
8177
|
+
if (targetId === enclosing.id) continue;
|
|
8178
|
+
addDep3(deps, seen, enclosing.id, targetId, "uses_type");
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
8181
|
+
}
|
|
8182
|
+
function addDep3(deps, seen, source, target, kind) {
|
|
8183
|
+
const key = `${source}|${target}|${kind}`;
|
|
8184
|
+
if (seen.has(key)) return;
|
|
8185
|
+
seen.add(key);
|
|
8186
|
+
deps.push({ sourceSymbolId: source, targetSymbolId: target, kind });
|
|
8187
|
+
}
|
|
8188
|
+
function findChild6(node, type) {
|
|
8189
|
+
for (const child of node.namedChildren) {
|
|
8190
|
+
if (child.type === type) return child;
|
|
8191
|
+
}
|
|
8192
|
+
return null;
|
|
8193
|
+
}
|
|
8194
|
+
function getNodeIdentifier3(node) {
|
|
8195
|
+
const nameNode = node.childForFieldName("name");
|
|
8196
|
+
if (nameNode) return nameNode.text;
|
|
8197
|
+
for (const child of node.namedChildren) {
|
|
8198
|
+
if (child.type === "identifier" || child.type === "type_identifier") return child.text;
|
|
8199
|
+
}
|
|
8200
|
+
return null;
|
|
8201
|
+
}
|
|
8202
|
+
function extractSimpleTypeName3(node) {
|
|
8203
|
+
for (const child of node.namedChildren) {
|
|
8204
|
+
if (child.type === "type_identifier" || child.type === "identifier") return child.text;
|
|
8205
|
+
if (child.type === "pointer_type") {
|
|
8206
|
+
const inner = child.namedChildren[0];
|
|
8207
|
+
return inner ? extractSimpleTypeNameFromNode(inner) : null;
|
|
8208
|
+
}
|
|
8209
|
+
if (child.type === "qualified_type") {
|
|
8210
|
+
const parts = child.text.split(".");
|
|
8211
|
+
return parts[parts.length - 1];
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
return null;
|
|
8215
|
+
}
|
|
8216
|
+
function extractSimpleTypeNameFromNode(node) {
|
|
8217
|
+
if (node.type === "type_identifier" || node.type === "identifier") return node.text;
|
|
8218
|
+
if (node.type === "qualified_type") {
|
|
8219
|
+
const parts = node.text.split(".");
|
|
8220
|
+
return parts[parts.length - 1];
|
|
8221
|
+
}
|
|
8222
|
+
return null;
|
|
8223
|
+
}
|
|
8224
|
+
function extractCalledFunctionName2(node) {
|
|
8225
|
+
const funcNode = node.childForFieldName("function");
|
|
8226
|
+
if (!funcNode) return node.namedChildren[0]?.text ?? null;
|
|
8227
|
+
if (funcNode.type === "selector_expression") {
|
|
8228
|
+
const fieldNode = funcNode.childForFieldName("field");
|
|
8229
|
+
return fieldNode?.text ?? null;
|
|
8230
|
+
}
|
|
8231
|
+
if (funcNode.type === "identifier") {
|
|
8232
|
+
return funcNode.text;
|
|
8233
|
+
}
|
|
8234
|
+
return null;
|
|
8235
|
+
}
|
|
8236
|
+
function findEnclosingSymbol3(name, fileSymbols, ...kinds) {
|
|
8237
|
+
return fileSymbols.find(
|
|
8238
|
+
(s) => s.name === name && kinds.includes(s.kind)
|
|
8239
|
+
);
|
|
8240
|
+
}
|
|
8241
|
+
function findEnclosingFunctionForNode2(node, fileSymbols) {
|
|
8242
|
+
let current = node.parent;
|
|
8243
|
+
while (current) {
|
|
8244
|
+
if (current.type === "function_declaration" || current.type === "method_declaration") {
|
|
8245
|
+
const name = getNodeIdentifier3(current);
|
|
8246
|
+
if (name) {
|
|
8247
|
+
return fileSymbols.find(
|
|
8248
|
+
(s) => s.kind === "function" && s.name === name && s.startLine <= current.startPosition.row + 1 && s.endLine >= current.endPosition.row + 1
|
|
8249
|
+
);
|
|
8250
|
+
}
|
|
8251
|
+
}
|
|
8252
|
+
current = current.parent;
|
|
8253
|
+
}
|
|
8254
|
+
return void 0;
|
|
8255
|
+
}
|
|
8256
|
+
function findEnclosingSymbolForNode3(node, fileSymbols) {
|
|
8257
|
+
let current = node.parent;
|
|
8258
|
+
while (current) {
|
|
8259
|
+
if (current.type === "function_declaration" || current.type === "method_declaration") {
|
|
8260
|
+
const name = getNodeIdentifier3(current);
|
|
8261
|
+
if (name) {
|
|
8262
|
+
const found = fileSymbols.find(
|
|
8263
|
+
(s) => s.kind === "function" && s.name === name && s.startLine <= current.startPosition.row + 1 && s.endLine >= current.endPosition.row + 1
|
|
8264
|
+
);
|
|
8265
|
+
if (found) return found;
|
|
8266
|
+
}
|
|
8267
|
+
}
|
|
8268
|
+
if (current.type === "type_spec") {
|
|
8269
|
+
const name = getNodeIdentifier3(current);
|
|
8270
|
+
if (name) {
|
|
8271
|
+
return fileSymbols.find(
|
|
8272
|
+
(s) => s.name === name && (s.kind === "class" || s.kind === "interface")
|
|
8273
|
+
);
|
|
8274
|
+
}
|
|
8275
|
+
}
|
|
8276
|
+
current = current.parent;
|
|
8277
|
+
}
|
|
8278
|
+
return void 0;
|
|
8279
|
+
}
|
|
8280
|
+
function collectTypeIdentifiers3(node) {
|
|
8281
|
+
const names = [];
|
|
8282
|
+
collectTypeIdentifiersRecursive3(node, names);
|
|
8283
|
+
return [...new Set(names)];
|
|
8284
|
+
}
|
|
8285
|
+
function collectTypeIdentifiersRecursive3(node, names) {
|
|
8286
|
+
if (node.type === "type_identifier") {
|
|
8287
|
+
names.push(node.text);
|
|
8288
|
+
} else if (node.type === "qualified_type") {
|
|
8289
|
+
const parts = node.text.split(".");
|
|
8290
|
+
names.push(parts[parts.length - 1]);
|
|
8291
|
+
} else if (node.type === "pointer_type") {
|
|
8292
|
+
for (const child of node.namedChildren) {
|
|
8293
|
+
collectTypeIdentifiersRecursive3(child, names);
|
|
8294
|
+
}
|
|
8295
|
+
return;
|
|
8296
|
+
}
|
|
8297
|
+
for (const child of node.namedChildren) {
|
|
8298
|
+
collectTypeIdentifiersRecursive3(child, names);
|
|
8299
|
+
}
|
|
8300
|
+
}
|
|
8301
|
+
|
|
8302
|
+
// src/indexer/go/indexer.ts
|
|
8303
|
+
async function indexGoTreeSitter(db, options) {
|
|
8304
|
+
const start = Date.now();
|
|
8305
|
+
await ensureGoParser();
|
|
8306
|
+
const service = options.service ?? "main";
|
|
8307
|
+
const projectRoot = options.projectRoot;
|
|
8308
|
+
const ignorePatterns = [
|
|
8309
|
+
"**/vendor/**",
|
|
8310
|
+
"**/node_modules/**",
|
|
8311
|
+
"**/.git/**",
|
|
8312
|
+
"**/testdata/**",
|
|
8313
|
+
"**/*_test.go",
|
|
8314
|
+
"**/*_gen.go",
|
|
8315
|
+
"**/*_generated.go",
|
|
8316
|
+
"**/*.pb.go"
|
|
8317
|
+
];
|
|
8318
|
+
const goFiles = globbySync3("**/*.go", {
|
|
8319
|
+
cwd: projectRoot,
|
|
8320
|
+
ignore: ignorePatterns,
|
|
8321
|
+
absolute: false
|
|
8322
|
+
});
|
|
8323
|
+
const existingHashes = getExistingHashes(db, service, "go");
|
|
8324
|
+
const currentPaths = /* @__PURE__ */ new Set();
|
|
8325
|
+
const fileCache = /* @__PURE__ */ new Map();
|
|
8326
|
+
const changedFiles = [];
|
|
8327
|
+
let filesSkipped = 0;
|
|
8328
|
+
for (const filePath of goFiles) {
|
|
8329
|
+
const relPath = filePath.replace(/\\/g, "/");
|
|
8330
|
+
currentPaths.add(relPath);
|
|
8331
|
+
const fullPath = join13(projectRoot, relPath);
|
|
8332
|
+
const content = readFileSync6(fullPath, "utf-8");
|
|
8333
|
+
const hash = hashContent(content);
|
|
8334
|
+
const tree = parseGo(content);
|
|
8335
|
+
fileCache.set(relPath, { content, tree, hash });
|
|
8336
|
+
const existingHash = existingHashes.get(relPath);
|
|
8337
|
+
if (existingHash === hash) {
|
|
8338
|
+
filesSkipped++;
|
|
8339
|
+
} else {
|
|
8340
|
+
changedFiles.push(relPath);
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8343
|
+
const removed = [];
|
|
8344
|
+
for (const existingPath of existingHashes.keys()) {
|
|
8345
|
+
if (!currentPaths.has(existingPath)) {
|
|
8346
|
+
removed.push(existingPath);
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
const filesToClean = [...removed, ...changedFiles];
|
|
8350
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "go");
|
|
8351
|
+
const allNewSymbols = [];
|
|
8352
|
+
for (const relPath of changedFiles) {
|
|
8353
|
+
const cached = fileCache.get(relPath);
|
|
8354
|
+
if (!cached) continue;
|
|
8355
|
+
const symbols = extractGoSymbols(cached.tree, relPath, cached.hash);
|
|
8356
|
+
allNewSymbols.push(...symbols);
|
|
8357
|
+
}
|
|
8358
|
+
writeSymbols(db, allNewSymbols, service, "go");
|
|
8359
|
+
const allDbSymbols = db.prepare("SELECT id, file_path as filePath, name, kind, start_line as startLine, end_line as endLine FROM symbols WHERE service = ? AND language = 'go'").all(service);
|
|
8360
|
+
const symbolLookup = buildGoSymbolLookup(allDbSymbols);
|
|
8361
|
+
const allDeps = [];
|
|
8362
|
+
for (const [relPath, cached] of fileCache) {
|
|
8363
|
+
const fileDeps = extractGoDependencies(cached.tree, relPath, allDbSymbols, symbolLookup);
|
|
8364
|
+
allDeps.push(...fileDeps);
|
|
8365
|
+
}
|
|
8366
|
+
db.prepare(
|
|
8367
|
+
"DELETE FROM dependencies WHERE source_symbol IN (SELECT id FROM symbols WHERE service = ? AND language = 'go')"
|
|
8368
|
+
).run(service);
|
|
8369
|
+
writeDependencies(db, allDeps);
|
|
8370
|
+
return {
|
|
8371
|
+
symbolsIndexed: allNewSymbols.length,
|
|
8372
|
+
dependenciesIndexed: allDeps.length,
|
|
8373
|
+
componentsAnalyzed: 0,
|
|
8374
|
+
routesAnalyzed: 0,
|
|
8375
|
+
filesProcessed: changedFiles.length,
|
|
8376
|
+
filesSkipped,
|
|
8377
|
+
filesRemoved: removed.length,
|
|
8378
|
+
durationMs: Date.now() - start
|
|
8379
|
+
};
|
|
8380
|
+
}
|
|
8381
|
+
|
|
8382
|
+
// src/indexer/package-deps.ts
|
|
8383
|
+
import { join as join14 } from "path";
|
|
8384
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync5 } from "fs";
|
|
8385
|
+
function indexPackageDependencies(db, projectRoot, service = "main") {
|
|
8386
|
+
const deps = [];
|
|
8387
|
+
const pkgJsonPath = join14(projectRoot, "package.json");
|
|
8388
|
+
if (existsSync6(pkgJsonPath)) {
|
|
8389
|
+
deps.push(...parsePackageJson(pkgJsonPath));
|
|
8390
|
+
}
|
|
8391
|
+
const csprojFiles = findCsprojFiles(projectRoot);
|
|
8392
|
+
for (const csproj of csprojFiles) {
|
|
8393
|
+
deps.push(...parseCsproj(csproj));
|
|
8394
|
+
}
|
|
8395
|
+
if (deps.length === 0) return 0;
|
|
8396
|
+
db.prepare("DELETE FROM package_dependencies WHERE service = ?").run(service);
|
|
8397
|
+
const insert = db.prepare(
|
|
8398
|
+
"INSERT OR IGNORE INTO package_dependencies (name, version, source, service) VALUES (?, ?, ?, ?)"
|
|
8399
|
+
);
|
|
8400
|
+
transaction(db, () => {
|
|
8401
|
+
for (const dep of deps) {
|
|
8402
|
+
insert.run(dep.name, dep.version, dep.source, service);
|
|
8403
|
+
}
|
|
8404
|
+
});
|
|
8405
|
+
return deps.length;
|
|
8406
|
+
}
|
|
8407
|
+
function parsePackageJson(filePath) {
|
|
8408
|
+
try {
|
|
8409
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
8410
|
+
const pkg = JSON.parse(content);
|
|
8411
|
+
const deps = [];
|
|
8412
|
+
if (pkg.dependencies) {
|
|
8413
|
+
for (const [name, version] of Object.entries(pkg.dependencies)) {
|
|
8414
|
+
deps.push({ name, version, source: "npm" });
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8417
|
+
if (pkg.devDependencies) {
|
|
8418
|
+
for (const [name, version] of Object.entries(pkg.devDependencies)) {
|
|
8419
|
+
deps.push({ name, version, source: "npm-dev" });
|
|
8420
|
+
}
|
|
8421
|
+
}
|
|
8422
|
+
return deps;
|
|
8423
|
+
} catch {
|
|
8424
|
+
return [];
|
|
8425
|
+
}
|
|
8426
|
+
}
|
|
8427
|
+
function parseCsproj(filePath) {
|
|
8428
|
+
try {
|
|
8429
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
8430
|
+
const deps = [];
|
|
8431
|
+
const pattern = /<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]*)")?[^>]*\/?>/gi;
|
|
8432
|
+
let match;
|
|
8433
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
8434
|
+
deps.push({
|
|
8435
|
+
name: match[1],
|
|
8436
|
+
version: match[2] ?? null,
|
|
8437
|
+
source: "nuget"
|
|
8438
|
+
});
|
|
8439
|
+
}
|
|
8440
|
+
return deps;
|
|
8441
|
+
} catch {
|
|
8442
|
+
return [];
|
|
8443
|
+
}
|
|
8444
|
+
}
|
|
8445
|
+
function findCsprojFiles(dir, maxDepth = 4) {
|
|
8446
|
+
const results = [];
|
|
8447
|
+
function walk(currentDir4, depth) {
|
|
8448
|
+
if (depth > maxDepth) return;
|
|
8449
|
+
try {
|
|
8450
|
+
const entries = readdirSync5(currentDir4, { withFileTypes: true });
|
|
8451
|
+
for (const entry of entries) {
|
|
8452
|
+
if (entry.name === "bin" || entry.name === "obj" || entry.name === "node_modules" || entry.name === ".git") continue;
|
|
8453
|
+
const fullPath = join14(currentDir4, entry.name);
|
|
8454
|
+
if (entry.isFile() && entry.name.endsWith(".csproj")) {
|
|
8455
|
+
results.push(fullPath);
|
|
8456
|
+
} else if (entry.isDirectory()) {
|
|
8457
|
+
walk(fullPath, depth + 1);
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
} catch {
|
|
8461
|
+
}
|
|
8462
|
+
}
|
|
8463
|
+
walk(dir, 0);
|
|
8464
|
+
return results;
|
|
8465
|
+
}
|
|
8466
|
+
|
|
8467
|
+
// src/config/loader.ts
|
|
8468
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
8469
|
+
import { join as join15 } from "path";
|
|
8470
|
+
import yaml from "yaml";
|
|
8471
|
+
function loadConfig(projectRoot) {
|
|
8472
|
+
const configPath = join15(projectRoot, ".arcbridge", "config.yaml");
|
|
8473
|
+
try {
|
|
8474
|
+
const raw = readFileSync8(configPath, "utf-8");
|
|
8475
|
+
const parsed = ArcBridgeConfigSchema.safeParse(yaml.parse(raw));
|
|
8476
|
+
if (parsed.success) {
|
|
8477
|
+
return { config: parsed.data, error: null };
|
|
8478
|
+
}
|
|
8479
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
8480
|
+
return { config: null, error: `Config validation failed: ${issues}` };
|
|
8481
|
+
} catch (err) {
|
|
8482
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
8483
|
+
return { config: null, error: null };
|
|
8484
|
+
}
|
|
8485
|
+
return { config: null, error: `Config load error: ${err instanceof Error ? err.message : String(err)}` };
|
|
8486
|
+
}
|
|
8487
|
+
}
|
|
8488
|
+
|
|
8489
|
+
// src/indexer/index.ts
|
|
8490
|
+
function detectProjectLanguage(projectRoot) {
|
|
8491
|
+
if (existsSync7(join16(projectRoot, "ProjectSettings")) && existsSync7(join16(projectRoot, "Assets"))) {
|
|
8492
|
+
return "csharp";
|
|
8493
|
+
}
|
|
8494
|
+
if (existsSync7(join16(projectRoot, "tsconfig.json"))) return "typescript";
|
|
8495
|
+
if (findDotnetProject(projectRoot)) return "csharp";
|
|
8496
|
+
if (existsSync7(join16(projectRoot, "go.mod"))) return "go";
|
|
8497
|
+
if (existsSync7(join16(projectRoot, "pyproject.toml")) || existsSync7(join16(projectRoot, "requirements.txt")) || existsSync7(join16(projectRoot, "setup.py"))) {
|
|
8498
|
+
return "python";
|
|
8499
|
+
}
|
|
8500
|
+
if (existsSync7(join16(projectRoot, "package.json"))) return "typescript";
|
|
8501
|
+
return "typescript";
|
|
8502
|
+
}
|
|
8503
|
+
async function indexProject(db, options) {
|
|
8504
|
+
const language = options.language ?? "auto";
|
|
8505
|
+
const resolvedLanguage = language === "auto" ? detectProjectLanguage(options.projectRoot) : language;
|
|
8506
|
+
indexPackageDependencies(db, options.projectRoot, options.service ?? "main");
|
|
8507
|
+
if (resolvedLanguage === "csharp") {
|
|
8508
|
+
const backend = resolveCSharpBackend(options.projectRoot);
|
|
8509
|
+
if (backend === "roslyn") {
|
|
8510
|
+
return indexDotnetProjectRoslyn(db, {
|
|
8511
|
+
projectRoot: options.projectRoot,
|
|
8512
|
+
service: options.service
|
|
8513
|
+
});
|
|
8514
|
+
}
|
|
8515
|
+
return await indexCSharpTreeSitter(db, {
|
|
8516
|
+
projectRoot: options.projectRoot,
|
|
8517
|
+
service: options.service
|
|
8518
|
+
});
|
|
8519
|
+
}
|
|
8520
|
+
if (resolvedLanguage === "python") {
|
|
8521
|
+
return await indexPythonTreeSitter(db, {
|
|
8522
|
+
projectRoot: options.projectRoot,
|
|
8523
|
+
service: options.service
|
|
8524
|
+
});
|
|
8525
|
+
}
|
|
8526
|
+
if (resolvedLanguage === "go") {
|
|
8527
|
+
return await indexGoTreeSitter(db, {
|
|
8528
|
+
projectRoot: options.projectRoot,
|
|
8529
|
+
service: options.service
|
|
8530
|
+
});
|
|
8531
|
+
}
|
|
8532
|
+
const resolvedTsconfigPath = options.tsconfigPath ?? ts6.findConfigFile(options.projectRoot, ts6.sys.fileExists, "tsconfig.json");
|
|
8533
|
+
if (!resolvedTsconfigPath) {
|
|
8534
|
+
return {
|
|
8535
|
+
symbolsIndexed: 0,
|
|
8536
|
+
dependenciesIndexed: 0,
|
|
8537
|
+
componentsAnalyzed: 0,
|
|
8538
|
+
routesAnalyzed: 0,
|
|
8539
|
+
filesProcessed: 0,
|
|
8540
|
+
filesSkipped: 0,
|
|
8541
|
+
filesRemoved: 0,
|
|
8542
|
+
durationMs: 0,
|
|
8543
|
+
skippedReason: "no tsconfig.json found"
|
|
8544
|
+
};
|
|
8545
|
+
}
|
|
8546
|
+
return indexTypeScriptProject(db, {
|
|
8547
|
+
...options,
|
|
8548
|
+
tsconfigPath: resolvedTsconfigPath
|
|
8549
|
+
});
|
|
8550
|
+
}
|
|
8551
|
+
function resolveCSharpBackend(projectRoot) {
|
|
8552
|
+
const { config, error } = loadConfig(projectRoot);
|
|
8553
|
+
let setting = config?.indexing?.csharp_indexer;
|
|
8554
|
+
if (!setting && error) {
|
|
8555
|
+
try {
|
|
8556
|
+
const raw = readFileSync9(join16(projectRoot, ".arcbridge", "config.yaml"), "utf-8");
|
|
8557
|
+
const parsed = YAML.parse(raw);
|
|
8558
|
+
const rawSetting = parsed?.indexing?.csharp_indexer;
|
|
8559
|
+
if (rawSetting === "roslyn" || rawSetting === "tree-sitter") {
|
|
8560
|
+
setting = rawSetting;
|
|
8561
|
+
}
|
|
8562
|
+
} catch {
|
|
8563
|
+
}
|
|
8564
|
+
}
|
|
8565
|
+
if (setting === "roslyn" || setting === "tree-sitter") {
|
|
8566
|
+
return setting;
|
|
8567
|
+
}
|
|
8568
|
+
if (hasGlobalTool()) {
|
|
8569
|
+
return "roslyn";
|
|
8570
|
+
}
|
|
8571
|
+
if (hasIndexerProject()) {
|
|
8572
|
+
try {
|
|
8573
|
+
execFileSync2("dotnet", ["--version"], {
|
|
8574
|
+
encoding: "utf-8",
|
|
8575
|
+
timeout: 5e3
|
|
8576
|
+
});
|
|
8577
|
+
return "roslyn";
|
|
8578
|
+
} catch {
|
|
8579
|
+
}
|
|
8580
|
+
}
|
|
8581
|
+
return "tree-sitter";
|
|
8582
|
+
}
|
|
8583
|
+
function indexTypeScriptProject(db, options) {
|
|
8584
|
+
const start = Date.now();
|
|
8585
|
+
const service = options.service ?? "main";
|
|
8586
|
+
const { checker, sourceFiles, projectRoot } = createTsProgram(options);
|
|
8587
|
+
const existingHashes = getExistingHashes(db, service, "typescript");
|
|
8588
|
+
const changed = [];
|
|
8589
|
+
const currentPaths = /* @__PURE__ */ new Set();
|
|
8590
|
+
let filesSkipped = 0;
|
|
8591
|
+
for (const sf of sourceFiles) {
|
|
8592
|
+
const relPath = relative5(projectRoot, sf.fileName);
|
|
8593
|
+
currentPaths.add(relPath);
|
|
8594
|
+
const hash = hashContent(sf.getFullText());
|
|
8595
|
+
const existingHash = existingHashes.get(relPath);
|
|
8596
|
+
if (existingHash === hash) {
|
|
8597
|
+
filesSkipped++;
|
|
8598
|
+
} else {
|
|
8599
|
+
changed.push({ sourceFile: sf, relativePath: relPath, hash });
|
|
8600
|
+
}
|
|
8601
|
+
}
|
|
8602
|
+
const removed = [];
|
|
8603
|
+
for (const existingPath of existingHashes.keys()) {
|
|
8604
|
+
if (!currentPaths.has(existingPath)) {
|
|
8605
|
+
removed.push(existingPath);
|
|
7460
8606
|
}
|
|
7461
8607
|
}
|
|
7462
8608
|
const filesToClean = [
|
|
7463
8609
|
...removed,
|
|
7464
8610
|
...changed.map((f) => f.relativePath)
|
|
7465
8611
|
];
|
|
7466
|
-
|
|
8612
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "typescript");
|
|
7467
8613
|
const allSymbols = changed.flatMap(
|
|
7468
8614
|
(f) => extractSymbols(f.sourceFile, checker, f.relativePath, f.hash)
|
|
7469
8615
|
);
|
|
7470
8616
|
writeSymbols(db, allSymbols, service, "typescript");
|
|
7471
|
-
const allDbSymbols = db.prepare("SELECT id, file_path as filePath, name FROM symbols WHERE service = ?").all(service);
|
|
8617
|
+
const allDbSymbols = db.prepare("SELECT id, file_path as filePath, name FROM symbols WHERE service = ? AND language = 'typescript'").all(service);
|
|
7472
8618
|
const lookup = buildSymbolLookup(allDbSymbols);
|
|
7473
8619
|
const allDeps = sourceFiles.flatMap((sf) => {
|
|
7474
8620
|
const relPath = relative5(projectRoot, sf.fileName);
|
|
7475
8621
|
return extractDependencies(sf, checker, relPath, projectRoot, lookup);
|
|
7476
8622
|
});
|
|
7477
|
-
db.prepare("DELETE FROM dependencies WHERE source_symbol IN (SELECT id FROM symbols WHERE service = ?)").run(service);
|
|
8623
|
+
db.prepare("DELETE FROM dependencies WHERE source_symbol IN (SELECT id FROM symbols WHERE service = ? AND language = 'typescript')").run(service);
|
|
7478
8624
|
writeDependencies(db, allDeps);
|
|
7479
8625
|
const CLIENT_ONLY_TEMPLATES = /* @__PURE__ */ new Set(["react-vite", "angular-app"]);
|
|
7480
8626
|
const projectType = db.prepare("SELECT value FROM arcbridge_meta WHERE key = 'project_type'").get()?.value;
|
|
@@ -7805,21 +8951,21 @@ function safeParseJson(value, fallback) {
|
|
|
7805
8951
|
}
|
|
7806
8952
|
|
|
7807
8953
|
// src/sync/yaml-writer.ts
|
|
7808
|
-
import { join as
|
|
7809
|
-
import { existsSync as existsSync8, readFileSync as
|
|
8954
|
+
import { join as join17 } from "path";
|
|
8955
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync } from "fs";
|
|
7810
8956
|
import { parse as parse2, stringify as stringify4 } from "yaml";
|
|
7811
8957
|
function readTaskFile(projectRoot, phaseId) {
|
|
7812
|
-
const path =
|
|
8958
|
+
const path = join17(projectRoot, ".arcbridge", "plan", "tasks", `${phaseId}.yaml`);
|
|
7813
8959
|
if (!existsSync8(path)) return { error: "not-found" };
|
|
7814
|
-
const raw =
|
|
8960
|
+
const raw = readFileSync10(path, "utf-8");
|
|
7815
8961
|
const result = TaskFileSchema.safeParse(parse2(raw));
|
|
7816
8962
|
if (!result.success) return { error: "invalid" };
|
|
7817
8963
|
return { data: result.data, path };
|
|
7818
8964
|
}
|
|
7819
8965
|
function readPhasesFile(projectRoot) {
|
|
7820
|
-
const path =
|
|
8966
|
+
const path = join17(projectRoot, ".arcbridge", "plan", "phases.yaml");
|
|
7821
8967
|
if (!existsSync8(path)) return { error: "not-found" };
|
|
7822
|
-
const raw =
|
|
8968
|
+
const raw = readFileSync10(path, "utf-8");
|
|
7823
8969
|
const result = PhasesFileSchema.safeParse(parse2(raw));
|
|
7824
8970
|
if (!result.success) return { error: "invalid" };
|
|
7825
8971
|
return { data: result.data, path };
|
|
@@ -7839,14 +8985,14 @@ function syncTaskToYaml(projectRoot, phaseId, taskId, status, completedAt) {
|
|
|
7839
8985
|
writeFileSync5(taskPath, stringify4(taskFile), "utf-8");
|
|
7840
8986
|
}
|
|
7841
8987
|
function addTaskToYaml(projectRoot, phaseId, task) {
|
|
7842
|
-
const tasksDir =
|
|
8988
|
+
const tasksDir = join17(projectRoot, ".arcbridge", "plan", "tasks");
|
|
7843
8989
|
mkdirSync5(tasksDir, { recursive: true });
|
|
7844
8990
|
const readResult = readTaskFile(projectRoot, phaseId);
|
|
7845
8991
|
const taskFile = "error" in readResult ? { schema_version: 1, phase_id: phaseId, tasks: [] } : readResult.data;
|
|
7846
8992
|
if (!taskFile.tasks.some((t) => t.id === task.id)) {
|
|
7847
8993
|
taskFile.tasks.push(task);
|
|
7848
8994
|
}
|
|
7849
|
-
const taskPath =
|
|
8995
|
+
const taskPath = join17(tasksDir, `${phaseId}.yaml`);
|
|
7850
8996
|
writeFileSync5(taskPath, stringify4(taskFile), "utf-8");
|
|
7851
8997
|
}
|
|
7852
8998
|
function syncPhaseToYaml(projectRoot, phaseId, status, startedAt, completedAt) {
|
|
@@ -7879,9 +9025,9 @@ function addPhaseToYaml(projectRoot, phase) {
|
|
|
7879
9025
|
warning: `Phase number ${phase.phase_number} already used by '${conflicting?.id}'`
|
|
7880
9026
|
};
|
|
7881
9027
|
}
|
|
7882
|
-
const tasksDir =
|
|
9028
|
+
const tasksDir = join17(projectRoot, ".arcbridge", "plan", "tasks");
|
|
7883
9029
|
mkdirSync5(tasksDir, { recursive: true });
|
|
7884
|
-
const taskFilePath =
|
|
9030
|
+
const taskFilePath = join17(tasksDir, `${phase.id}.yaml`);
|
|
7885
9031
|
if (!existsSync8(taskFilePath)) {
|
|
7886
9032
|
writeFileSync5(
|
|
7887
9033
|
taskFilePath,
|
|
@@ -7911,14 +9057,14 @@ function addPhaseToYaml(projectRoot, phase) {
|
|
|
7911
9057
|
}
|
|
7912
9058
|
}
|
|
7913
9059
|
function syncScenarioToYaml(projectRoot, scenarioId, status, linkedTests, verification) {
|
|
7914
|
-
const scenarioPath =
|
|
9060
|
+
const scenarioPath = join17(
|
|
7915
9061
|
projectRoot,
|
|
7916
9062
|
".arcbridge",
|
|
7917
9063
|
"arc42",
|
|
7918
9064
|
"10-quality-scenarios.yaml"
|
|
7919
9065
|
);
|
|
7920
9066
|
if (!existsSync8(scenarioPath)) return;
|
|
7921
|
-
const raw =
|
|
9067
|
+
const raw = readFileSync10(scenarioPath, "utf-8");
|
|
7922
9068
|
const parsed = parse2(raw);
|
|
7923
9069
|
const result = QualityScenariosFileSchema.safeParse(parsed);
|
|
7924
9070
|
if (!result.success) return;
|
|
@@ -7977,7 +9123,7 @@ function deletePhaseFromYaml(projectRoot, phaseId) {
|
|
|
7977
9123
|
return { success: false, warning: `Phase '${phaseId}' not found in phases.yaml` };
|
|
7978
9124
|
}
|
|
7979
9125
|
writeFileSync5(phasesPath, stringify4(phasesFile), "utf-8");
|
|
7980
|
-
const taskFilePath =
|
|
9126
|
+
const taskFilePath = join17(projectRoot, ".arcbridge", "plan", "tasks", `${phaseId}.yaml`);
|
|
7981
9127
|
try {
|
|
7982
9128
|
unlinkSync(taskFilePath);
|
|
7983
9129
|
} catch (e) {
|
|
@@ -8121,7 +9267,7 @@ function safeParseJson2(value, fallback) {
|
|
|
8121
9267
|
|
|
8122
9268
|
// src/generators/sync-generator.ts
|
|
8123
9269
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
8124
|
-
import { join as
|
|
9270
|
+
import { join as join18, dirname as dirname6 } from "path";
|
|
8125
9271
|
|
|
8126
9272
|
// src/templates/sync/claude-skill.ts
|
|
8127
9273
|
function claudeSkillTemplate(config) {
|
|
@@ -8299,21 +9445,21 @@ To enable automatic sync:
|
|
|
8299
9445
|
function generateSyncFiles(targetDir, config) {
|
|
8300
9446
|
const generated = [];
|
|
8301
9447
|
const action = githubActionTemplate(config);
|
|
8302
|
-
const actionPath =
|
|
8303
|
-
mkdirSync6(
|
|
9448
|
+
const actionPath = join18(targetDir, action.relativePath);
|
|
9449
|
+
mkdirSync6(dirname6(actionPath), { recursive: true });
|
|
8304
9450
|
writeFileSync6(actionPath, action.content, "utf-8");
|
|
8305
9451
|
generated.push(action.relativePath);
|
|
8306
9452
|
if (config.platforms.includes("claude")) {
|
|
8307
9453
|
const skill = claudeSkillTemplate(config);
|
|
8308
|
-
const skillPath =
|
|
8309
|
-
mkdirSync6(
|
|
9454
|
+
const skillPath = join18(targetDir, skill.relativePath);
|
|
9455
|
+
mkdirSync6(dirname6(skillPath), { recursive: true });
|
|
8310
9456
|
writeFileSync6(skillPath, skill.content, "utf-8");
|
|
8311
9457
|
generated.push(skill.relativePath);
|
|
8312
9458
|
}
|
|
8313
9459
|
if (config.platforms.includes("copilot")) {
|
|
8314
9460
|
const hook = copilotHookTemplate(config);
|
|
8315
|
-
const hookPath =
|
|
8316
|
-
mkdirSync6(
|
|
9461
|
+
const hookPath = join18(targetDir, hook.relativePath);
|
|
9462
|
+
mkdirSync6(dirname6(hookPath), { recursive: true });
|
|
8317
9463
|
writeFileSync6(hookPath, hook.content, "utf-8");
|
|
8318
9464
|
generated.push(hook.relativePath);
|
|
8319
9465
|
}
|
|
@@ -8322,7 +9468,7 @@ function generateSyncFiles(targetDir, config) {
|
|
|
8322
9468
|
|
|
8323
9469
|
// src/metrics/activity.ts
|
|
8324
9470
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
8325
|
-
import { join as
|
|
9471
|
+
import { join as join19 } from "path";
|
|
8326
9472
|
function insertActivity(db, params) {
|
|
8327
9473
|
const totalTokens = params.totalTokens ?? (params.inputTokens != null && params.outputTokens != null ? params.inputTokens + params.outputTokens : null);
|
|
8328
9474
|
const stmt = db.prepare(`
|
|
@@ -8480,7 +9626,7 @@ function exportMetrics(db, projectRoot, format, params, maxRows = 1e5) {
|
|
|
8480
9626
|
});
|
|
8481
9627
|
const rows = result.rows;
|
|
8482
9628
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
8483
|
-
const dir =
|
|
9629
|
+
const dir = join19(projectRoot, ".arcbridge", "metrics");
|
|
8484
9630
|
mkdirSync7(dir, { recursive: true });
|
|
8485
9631
|
let content;
|
|
8486
9632
|
let filename;
|
|
@@ -8577,7 +9723,7 @@ function exportMetrics(db, projectRoot, format, params, maxRows = 1e5) {
|
|
|
8577
9723
|
break;
|
|
8578
9724
|
}
|
|
8579
9725
|
}
|
|
8580
|
-
const filePath =
|
|
9726
|
+
const filePath = join19(dir, filename);
|
|
8581
9727
|
writeFileSync7(filePath, content, "utf-8");
|
|
8582
9728
|
return filePath;
|
|
8583
9729
|
}
|
|
@@ -8781,7 +9927,7 @@ function parseStatusCode(code) {
|
|
|
8781
9927
|
// src/testing/runner.ts
|
|
8782
9928
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
8783
9929
|
import { existsSync as existsSync9 } from "fs";
|
|
8784
|
-
import { resolve as
|
|
9930
|
+
import { resolve as resolve5 } from "path";
|
|
8785
9931
|
function verifyScenarios(db, projectRoot, options) {
|
|
8786
9932
|
const results = [];
|
|
8787
9933
|
const errors = [];
|
|
@@ -8818,7 +9964,7 @@ function verifyScenarios(db, projectRoot, options) {
|
|
|
8818
9964
|
}
|
|
8819
9965
|
if (testPaths.length === 0) continue;
|
|
8820
9966
|
const missingPaths = testPaths.filter(
|
|
8821
|
-
(tp) => !existsSync9(
|
|
9967
|
+
(tp) => !existsSync9(resolve5(projectRoot, tp))
|
|
8822
9968
|
);
|
|
8823
9969
|
if (missingPaths.length === testPaths.length) {
|
|
8824
9970
|
results.push({
|
|
@@ -8833,7 +9979,7 @@ function verifyScenarios(db, projectRoot, options) {
|
|
|
8833
9979
|
continue;
|
|
8834
9980
|
}
|
|
8835
9981
|
const existingPaths = testPaths.filter(
|
|
8836
|
-
(tp) => existsSync9(
|
|
9982
|
+
(tp) => existsSync9(resolve5(projectRoot, tp))
|
|
8837
9983
|
);
|
|
8838
9984
|
const start = Date.now();
|
|
8839
9985
|
let passed;
|
|
@@ -8887,11 +10033,11 @@ ${output}`;
|
|
|
8887
10033
|
}
|
|
8888
10034
|
|
|
8889
10035
|
// src/roles/loader.ts
|
|
8890
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
8891
|
-
import { join as
|
|
10036
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync11 } from "fs";
|
|
10037
|
+
import { join as join20 } from "path";
|
|
8892
10038
|
import matter4 from "gray-matter";
|
|
8893
10039
|
function loadRoles(projectRoot) {
|
|
8894
|
-
const agentsDir =
|
|
10040
|
+
const agentsDir = join20(projectRoot, ".arcbridge", "agents");
|
|
8895
10041
|
const roles = [];
|
|
8896
10042
|
const errors = [];
|
|
8897
10043
|
let files;
|
|
@@ -8901,9 +10047,9 @@ function loadRoles(projectRoot) {
|
|
|
8901
10047
|
return { roles: [], errors: [`Agent directory not found: ${agentsDir}`] };
|
|
8902
10048
|
}
|
|
8903
10049
|
for (const file of files) {
|
|
8904
|
-
const filePath =
|
|
10050
|
+
const filePath = join20(agentsDir, file);
|
|
8905
10051
|
try {
|
|
8906
|
-
const raw =
|
|
10052
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
8907
10053
|
const parsed = matter4(raw);
|
|
8908
10054
|
const input = {
|
|
8909
10055
|
...parsed.data,
|
|
@@ -8928,9 +10074,9 @@ function loadRole(projectRoot, roleId) {
|
|
|
8928
10074
|
if (!/^[a-z0-9-]+$/.test(roleId)) {
|
|
8929
10075
|
return { role: null, error: `Invalid role ID: "${roleId}" (must be kebab-case)` };
|
|
8930
10076
|
}
|
|
8931
|
-
const filePath =
|
|
10077
|
+
const filePath = join20(projectRoot, ".arcbridge", "agents", `${roleId}.md`);
|
|
8932
10078
|
try {
|
|
8933
|
-
const raw =
|
|
10079
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
8934
10080
|
const parsed = matter4(raw);
|
|
8935
10081
|
const input = {
|
|
8936
10082
|
...parsed.data,
|