@arcbridge/core 0.4.2 → 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 +16 -7
- package/dist/index.js +1380 -235
- 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
|
@@ -4964,8 +4964,9 @@ function ensureGitignore(targetDir) {
|
|
|
4964
4964
|
}
|
|
4965
4965
|
|
|
4966
4966
|
// src/indexer/index.ts
|
|
4967
|
-
import
|
|
4968
|
-
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";
|
|
4969
4970
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
4970
4971
|
import YAML from "yaml";
|
|
4971
4972
|
|
|
@@ -5333,7 +5334,7 @@ function buildSymbolLookup(symbols) {
|
|
|
5333
5334
|
function extractDependencies(sourceFile, checker, relativePath, projectRoot, lookup) {
|
|
5334
5335
|
const deps = [];
|
|
5335
5336
|
const seen = /* @__PURE__ */ new Set();
|
|
5336
|
-
function
|
|
5337
|
+
function addDep4(sourceId, targetId, kind) {
|
|
5337
5338
|
const key = `${sourceId}|${targetId}|${kind}`;
|
|
5338
5339
|
if (seen.has(key)) return;
|
|
5339
5340
|
if (!lookup.allIds.has(sourceId) || !lookup.allIds.has(targetId)) return;
|
|
@@ -5369,7 +5370,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5369
5370
|
if (!targetId) continue;
|
|
5370
5371
|
const fileSymbols = getFileTopLevelSymbols(relativePath, lookup);
|
|
5371
5372
|
for (const sourceId of fileSymbols) {
|
|
5372
|
-
|
|
5373
|
+
addDep4(sourceId, targetId, "imports");
|
|
5373
5374
|
}
|
|
5374
5375
|
}
|
|
5375
5376
|
}
|
|
@@ -5381,7 +5382,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5381
5382
|
if (targetId) {
|
|
5382
5383
|
const fileSymbols = getFileTopLevelSymbols(relativePath, lookup);
|
|
5383
5384
|
for (const sourceId of fileSymbols) {
|
|
5384
|
-
|
|
5385
|
+
addDep4(sourceId, targetId, "imports");
|
|
5385
5386
|
}
|
|
5386
5387
|
}
|
|
5387
5388
|
}
|
|
@@ -5403,7 +5404,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5403
5404
|
const resolved = exprSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(exprSymbol) : exprSymbol;
|
|
5404
5405
|
const targetId = resolveSymbolId(resolved);
|
|
5405
5406
|
if (targetId) {
|
|
5406
|
-
|
|
5407
|
+
addDep4(classId, targetId, kind);
|
|
5407
5408
|
}
|
|
5408
5409
|
}
|
|
5409
5410
|
}
|
|
@@ -5426,7 +5427,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5426
5427
|
const resolved = calledSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(calledSymbol) : calledSymbol;
|
|
5427
5428
|
const targetId = resolveSymbolId(resolved);
|
|
5428
5429
|
if (targetId) {
|
|
5429
|
-
|
|
5430
|
+
addDep4(ownerId, targetId, "calls");
|
|
5430
5431
|
}
|
|
5431
5432
|
}
|
|
5432
5433
|
}
|
|
@@ -5456,7 +5457,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5456
5457
|
const resolved = calledSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(calledSymbol) : calledSymbol;
|
|
5457
5458
|
const targetId = resolveSymbolId(resolved);
|
|
5458
5459
|
if (targetId) {
|
|
5459
|
-
|
|
5460
|
+
addDep4(callOwnerId, targetId, "calls");
|
|
5460
5461
|
}
|
|
5461
5462
|
}
|
|
5462
5463
|
}
|
|
@@ -5488,7 +5489,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5488
5489
|
const resolved = typeSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(typeSymbol) : typeSymbol;
|
|
5489
5490
|
const targetId = resolveSymbolId(resolved);
|
|
5490
5491
|
if (targetId) {
|
|
5491
|
-
|
|
5492
|
+
addDep4(ownerId, targetId, "uses_type");
|
|
5492
5493
|
}
|
|
5493
5494
|
}
|
|
5494
5495
|
}
|
|
@@ -5512,7 +5513,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5512
5513
|
const resolved = jsxSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(jsxSymbol) : jsxSymbol;
|
|
5513
5514
|
const targetId = resolveSymbolId(resolved);
|
|
5514
5515
|
if (targetId) {
|
|
5515
|
-
|
|
5516
|
+
addDep4(ownerId, targetId, "renders");
|
|
5516
5517
|
}
|
|
5517
5518
|
}
|
|
5518
5519
|
if (ts4.isPropertyAccessExpression(tagName) && ts4.isIdentifier(tagName.name) && tagName.name.text === "Provider") {
|
|
@@ -5521,7 +5522,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5521
5522
|
const resolved = ctxSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(ctxSymbol) : ctxSymbol;
|
|
5522
5523
|
const targetId = resolveSymbolId(resolved);
|
|
5523
5524
|
if (targetId) {
|
|
5524
|
-
|
|
5525
|
+
addDep4(ownerId, targetId, "provides_context");
|
|
5525
5526
|
}
|
|
5526
5527
|
}
|
|
5527
5528
|
}
|
|
@@ -5568,7 +5569,7 @@ function extractDependencies(sourceFile, checker, relativePath, projectRoot, loo
|
|
|
5568
5569
|
const resolved = ctxSymbol.flags & ts4.SymbolFlags.Alias ? checker.getAliasedSymbol(ctxSymbol) : ctxSymbol;
|
|
5569
5570
|
const targetId = resolveSymbolId(resolved);
|
|
5570
5571
|
if (targetId) {
|
|
5571
|
-
|
|
5572
|
+
addDep4(ownerId, targetId, "consumes_context");
|
|
5572
5573
|
}
|
|
5573
5574
|
}
|
|
5574
5575
|
}
|
|
@@ -6015,36 +6016,37 @@ function hashContent(content) {
|
|
|
6015
6016
|
}
|
|
6016
6017
|
|
|
6017
6018
|
// src/indexer/db-writer.ts
|
|
6018
|
-
function getExistingHashes(db, service) {
|
|
6019
|
-
const
|
|
6020
|
-
|
|
6021
|
-
).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);
|
|
6022
6023
|
const map = /* @__PURE__ */ new Map();
|
|
6023
6024
|
for (const row of rows) {
|
|
6024
6025
|
map.set(row.file_path, row.content_hash);
|
|
6025
6026
|
}
|
|
6026
6027
|
return map;
|
|
6027
6028
|
}
|
|
6028
|
-
function
|
|
6029
|
+
function removeScopedSymbolsForFiles(db, filePaths, service, language) {
|
|
6029
6030
|
if (filePaths.length === 0) return;
|
|
6030
|
-
const
|
|
6031
|
-
"DELETE FROM symbols WHERE file_path = ?"
|
|
6032
|
-
);
|
|
6031
|
+
const scope = "file_path = ? AND service = ? AND language = ?";
|
|
6033
6032
|
const deleteDepsSource = db.prepare(
|
|
6034
|
-
|
|
6033
|
+
`DELETE FROM dependencies WHERE source_symbol IN (SELECT id FROM symbols WHERE ${scope})`
|
|
6035
6034
|
);
|
|
6036
6035
|
const deleteDepsTarget = db.prepare(
|
|
6037
|
-
|
|
6036
|
+
`DELETE FROM dependencies WHERE target_symbol IN (SELECT id FROM symbols WHERE ${scope})`
|
|
6038
6037
|
);
|
|
6039
6038
|
const deleteComponents = db.prepare(
|
|
6040
|
-
|
|
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}`
|
|
6041
6043
|
);
|
|
6042
6044
|
transaction(db, () => {
|
|
6043
6045
|
for (const fp of filePaths) {
|
|
6044
|
-
deleteDepsSource.run(fp);
|
|
6045
|
-
deleteDepsTarget.run(fp);
|
|
6046
|
-
deleteComponents.run(fp);
|
|
6047
|
-
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);
|
|
6048
6050
|
}
|
|
6049
6051
|
});
|
|
6050
6052
|
}
|
|
@@ -6177,11 +6179,11 @@ function hasGlobalTool() {
|
|
|
6177
6179
|
return false;
|
|
6178
6180
|
}
|
|
6179
6181
|
function resolveIndexerProject() {
|
|
6180
|
-
const
|
|
6182
|
+
const currentDir4 = dirname2(fileURLToPath(import.meta.url));
|
|
6181
6183
|
const candidates = [
|
|
6182
|
-
resolve(
|
|
6183
|
-
resolve(
|
|
6184
|
-
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")
|
|
6185
6187
|
];
|
|
6186
6188
|
for (const candidate of candidates) {
|
|
6187
6189
|
if (existsSync5(candidate)) return candidate;
|
|
@@ -6247,7 +6249,7 @@ function indexDotnetProjectRoslyn(db, options) {
|
|
|
6247
6249
|
"No .sln or .csproj file found in project root. The .NET indexer requires a project or solution file."
|
|
6248
6250
|
);
|
|
6249
6251
|
}
|
|
6250
|
-
const existingHashes = getExistingHashes(db, service);
|
|
6252
|
+
const existingHashes = getExistingHashes(db, service, "csharp");
|
|
6251
6253
|
const hashesJson = JSON.stringify(Object.fromEntries(existingHashes));
|
|
6252
6254
|
const stdout = runDotnetIndexer(dotnetProject, hashesJson, projectRoot);
|
|
6253
6255
|
const lines = stdout.trim().split("\n");
|
|
@@ -6264,7 +6266,7 @@ function indexDotnetProjectRoslyn(db, options) {
|
|
|
6264
6266
|
);
|
|
6265
6267
|
}
|
|
6266
6268
|
const filesToClean = [...output.changedFiles, ...output.removedFiles];
|
|
6267
|
-
|
|
6269
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "csharp");
|
|
6268
6270
|
const symbols = output.symbols.map((s) => ({
|
|
6269
6271
|
id: s.id,
|
|
6270
6272
|
name: s.name,
|
|
@@ -7184,7 +7186,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7184
7186
|
ignore: ignorePatterns,
|
|
7185
7187
|
absolute: false
|
|
7186
7188
|
});
|
|
7187
|
-
const existingHashes = getExistingHashes(db, service);
|
|
7189
|
+
const existingHashes = getExistingHashes(db, service, "csharp");
|
|
7188
7190
|
const currentPaths = /* @__PURE__ */ new Set();
|
|
7189
7191
|
const fileCache = /* @__PURE__ */ new Map();
|
|
7190
7192
|
const changedFiles = [];
|
|
@@ -7211,7 +7213,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7211
7213
|
}
|
|
7212
7214
|
}
|
|
7213
7215
|
const filesToClean = [...removed, ...changedFiles];
|
|
7214
|
-
|
|
7216
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "csharp");
|
|
7215
7217
|
const allNewSymbols = [];
|
|
7216
7218
|
for (const relPath of changedFiles) {
|
|
7217
7219
|
const cached = fileCache.get(relPath);
|
|
@@ -7220,7 +7222,7 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7220
7222
|
allNewSymbols.push(...symbols);
|
|
7221
7223
|
}
|
|
7222
7224
|
writeSymbols(db, allNewSymbols, service, "csharp");
|
|
7223
|
-
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);
|
|
7224
7226
|
const symbolLookup = buildCSharpSymbolLookup(allDbSymbols);
|
|
7225
7227
|
const allDeps = [];
|
|
7226
7228
|
for (const [relPath, cached] of fileCache) {
|
|
@@ -7267,215 +7269,1358 @@ async function indexCSharpTreeSitter(db, options) {
|
|
|
7267
7269
|
};
|
|
7268
7270
|
}
|
|
7269
7271
|
|
|
7270
|
-
// src/indexer/
|
|
7272
|
+
// src/indexer/python/indexer.ts
|
|
7273
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
7271
7274
|
import { join as join12 } from "path";
|
|
7272
|
-
import {
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
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
|
+
}
|
|
7282
7299
|
}
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
const insert = db.prepare(
|
|
7286
|
-
"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."
|
|
7287
7302
|
);
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
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;
|
|
7291
7320
|
}
|
|
7292
|
-
});
|
|
7293
|
-
return
|
|
7321
|
+
})();
|
|
7322
|
+
return initPromise2;
|
|
7294
7323
|
}
|
|
7295
|
-
function
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
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);
|
|
7303
7353
|
}
|
|
7354
|
+
return;
|
|
7304
7355
|
}
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
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);
|
|
7308
7363
|
}
|
|
7364
|
+
return;
|
|
7309
7365
|
}
|
|
7310
|
-
return deps;
|
|
7311
|
-
} catch {
|
|
7312
|
-
return [];
|
|
7313
7366
|
}
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
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;
|
|
7331
7398
|
}
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
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);
|
|
7347
7421
|
}
|
|
7348
|
-
} catch {
|
|
7349
7422
|
}
|
|
7423
|
+
return;
|
|
7350
7424
|
}
|
|
7351
|
-
walk(dir, 0);
|
|
7352
|
-
return results;
|
|
7353
7425
|
}
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
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;
|
|
7374
7473
|
}
|
|
7474
|
+
return null;
|
|
7375
7475
|
}
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
if (existsSync7(join14(projectRoot, "ProjectSettings")) && existsSync7(join14(projectRoot, "Assets"))) {
|
|
7380
|
-
return "csharp";
|
|
7476
|
+
function findChild4(node, type) {
|
|
7477
|
+
for (const child of node.namedChildren) {
|
|
7478
|
+
if (child.type === type) return child;
|
|
7381
7479
|
}
|
|
7382
|
-
|
|
7383
|
-
if (existsSync7(join14(projectRoot, "package.json"))) return "typescript";
|
|
7384
|
-
if (findDotnetProject(projectRoot)) return "csharp";
|
|
7385
|
-
return "typescript";
|
|
7480
|
+
return null;
|
|
7386
7481
|
}
|
|
7387
|
-
|
|
7388
|
-
const
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
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);
|
|
7398
7524
|
}
|
|
7399
|
-
return await indexCSharpTreeSitter(db, {
|
|
7400
|
-
projectRoot: options.projectRoot,
|
|
7401
|
-
service: options.service
|
|
7402
|
-
});
|
|
7403
7525
|
}
|
|
7404
|
-
return
|
|
7526
|
+
return decorators;
|
|
7405
7527
|
}
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
}
|
|
7417
|
-
} 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]);
|
|
7418
7538
|
}
|
|
7419
7539
|
}
|
|
7420
|
-
|
|
7421
|
-
|
|
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;
|
|
7422
7557
|
}
|
|
7423
|
-
if (
|
|
7424
|
-
|
|
7558
|
+
if (node.type === "typed_parameter" || node.type === "typed_default_parameter") {
|
|
7559
|
+
extractTypeAnnotationDeps(node, fileSymbols, lookup, deps, seen);
|
|
7425
7560
|
}
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
execFileSync2("dotnet", ["--version"], {
|
|
7429
|
-
encoding: "utf-8",
|
|
7430
|
-
timeout: 5e3
|
|
7431
|
-
});
|
|
7432
|
-
return "roslyn";
|
|
7433
|
-
} catch {
|
|
7434
|
-
}
|
|
7561
|
+
for (const child of node.namedChildren) {
|
|
7562
|
+
walkForDependencies2(child, fileSymbols, lookup, deps, seen);
|
|
7435
7563
|
}
|
|
7436
|
-
return "tree-sitter";
|
|
7437
7564
|
}
|
|
7438
|
-
function
|
|
7439
|
-
const
|
|
7440
|
-
|
|
7441
|
-
const
|
|
7442
|
-
|
|
7443
|
-
const
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
const
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
}
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
const
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
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);
|
|
7461
8606
|
}
|
|
7462
8607
|
}
|
|
7463
8608
|
const filesToClean = [
|
|
7464
8609
|
...removed,
|
|
7465
8610
|
...changed.map((f) => f.relativePath)
|
|
7466
8611
|
];
|
|
7467
|
-
|
|
8612
|
+
removeScopedSymbolsForFiles(db, filesToClean, service, "typescript");
|
|
7468
8613
|
const allSymbols = changed.flatMap(
|
|
7469
8614
|
(f) => extractSymbols(f.sourceFile, checker, f.relativePath, f.hash)
|
|
7470
8615
|
);
|
|
7471
8616
|
writeSymbols(db, allSymbols, service, "typescript");
|
|
7472
|
-
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);
|
|
7473
8618
|
const lookup = buildSymbolLookup(allDbSymbols);
|
|
7474
8619
|
const allDeps = sourceFiles.flatMap((sf) => {
|
|
7475
8620
|
const relPath = relative5(projectRoot, sf.fileName);
|
|
7476
8621
|
return extractDependencies(sf, checker, relPath, projectRoot, lookup);
|
|
7477
8622
|
});
|
|
7478
|
-
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);
|
|
7479
8624
|
writeDependencies(db, allDeps);
|
|
7480
8625
|
const CLIENT_ONLY_TEMPLATES = /* @__PURE__ */ new Set(["react-vite", "angular-app"]);
|
|
7481
8626
|
const projectType = db.prepare("SELECT value FROM arcbridge_meta WHERE key = 'project_type'").get()?.value;
|
|
@@ -7806,21 +8951,21 @@ function safeParseJson(value, fallback) {
|
|
|
7806
8951
|
}
|
|
7807
8952
|
|
|
7808
8953
|
// src/sync/yaml-writer.ts
|
|
7809
|
-
import { join as
|
|
7810
|
-
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";
|
|
7811
8956
|
import { parse as parse2, stringify as stringify4 } from "yaml";
|
|
7812
8957
|
function readTaskFile(projectRoot, phaseId) {
|
|
7813
|
-
const path =
|
|
8958
|
+
const path = join17(projectRoot, ".arcbridge", "plan", "tasks", `${phaseId}.yaml`);
|
|
7814
8959
|
if (!existsSync8(path)) return { error: "not-found" };
|
|
7815
|
-
const raw =
|
|
8960
|
+
const raw = readFileSync10(path, "utf-8");
|
|
7816
8961
|
const result = TaskFileSchema.safeParse(parse2(raw));
|
|
7817
8962
|
if (!result.success) return { error: "invalid" };
|
|
7818
8963
|
return { data: result.data, path };
|
|
7819
8964
|
}
|
|
7820
8965
|
function readPhasesFile(projectRoot) {
|
|
7821
|
-
const path =
|
|
8966
|
+
const path = join17(projectRoot, ".arcbridge", "plan", "phases.yaml");
|
|
7822
8967
|
if (!existsSync8(path)) return { error: "not-found" };
|
|
7823
|
-
const raw =
|
|
8968
|
+
const raw = readFileSync10(path, "utf-8");
|
|
7824
8969
|
const result = PhasesFileSchema.safeParse(parse2(raw));
|
|
7825
8970
|
if (!result.success) return { error: "invalid" };
|
|
7826
8971
|
return { data: result.data, path };
|
|
@@ -7840,14 +8985,14 @@ function syncTaskToYaml(projectRoot, phaseId, taskId, status, completedAt) {
|
|
|
7840
8985
|
writeFileSync5(taskPath, stringify4(taskFile), "utf-8");
|
|
7841
8986
|
}
|
|
7842
8987
|
function addTaskToYaml(projectRoot, phaseId, task) {
|
|
7843
|
-
const tasksDir =
|
|
8988
|
+
const tasksDir = join17(projectRoot, ".arcbridge", "plan", "tasks");
|
|
7844
8989
|
mkdirSync5(tasksDir, { recursive: true });
|
|
7845
8990
|
const readResult = readTaskFile(projectRoot, phaseId);
|
|
7846
8991
|
const taskFile = "error" in readResult ? { schema_version: 1, phase_id: phaseId, tasks: [] } : readResult.data;
|
|
7847
8992
|
if (!taskFile.tasks.some((t) => t.id === task.id)) {
|
|
7848
8993
|
taskFile.tasks.push(task);
|
|
7849
8994
|
}
|
|
7850
|
-
const taskPath =
|
|
8995
|
+
const taskPath = join17(tasksDir, `${phaseId}.yaml`);
|
|
7851
8996
|
writeFileSync5(taskPath, stringify4(taskFile), "utf-8");
|
|
7852
8997
|
}
|
|
7853
8998
|
function syncPhaseToYaml(projectRoot, phaseId, status, startedAt, completedAt) {
|
|
@@ -7880,9 +9025,9 @@ function addPhaseToYaml(projectRoot, phase) {
|
|
|
7880
9025
|
warning: `Phase number ${phase.phase_number} already used by '${conflicting?.id}'`
|
|
7881
9026
|
};
|
|
7882
9027
|
}
|
|
7883
|
-
const tasksDir =
|
|
9028
|
+
const tasksDir = join17(projectRoot, ".arcbridge", "plan", "tasks");
|
|
7884
9029
|
mkdirSync5(tasksDir, { recursive: true });
|
|
7885
|
-
const taskFilePath =
|
|
9030
|
+
const taskFilePath = join17(tasksDir, `${phase.id}.yaml`);
|
|
7886
9031
|
if (!existsSync8(taskFilePath)) {
|
|
7887
9032
|
writeFileSync5(
|
|
7888
9033
|
taskFilePath,
|
|
@@ -7912,14 +9057,14 @@ function addPhaseToYaml(projectRoot, phase) {
|
|
|
7912
9057
|
}
|
|
7913
9058
|
}
|
|
7914
9059
|
function syncScenarioToYaml(projectRoot, scenarioId, status, linkedTests, verification) {
|
|
7915
|
-
const scenarioPath =
|
|
9060
|
+
const scenarioPath = join17(
|
|
7916
9061
|
projectRoot,
|
|
7917
9062
|
".arcbridge",
|
|
7918
9063
|
"arc42",
|
|
7919
9064
|
"10-quality-scenarios.yaml"
|
|
7920
9065
|
);
|
|
7921
9066
|
if (!existsSync8(scenarioPath)) return;
|
|
7922
|
-
const raw =
|
|
9067
|
+
const raw = readFileSync10(scenarioPath, "utf-8");
|
|
7923
9068
|
const parsed = parse2(raw);
|
|
7924
9069
|
const result = QualityScenariosFileSchema.safeParse(parsed);
|
|
7925
9070
|
if (!result.success) return;
|
|
@@ -7978,7 +9123,7 @@ function deletePhaseFromYaml(projectRoot, phaseId) {
|
|
|
7978
9123
|
return { success: false, warning: `Phase '${phaseId}' not found in phases.yaml` };
|
|
7979
9124
|
}
|
|
7980
9125
|
writeFileSync5(phasesPath, stringify4(phasesFile), "utf-8");
|
|
7981
|
-
const taskFilePath =
|
|
9126
|
+
const taskFilePath = join17(projectRoot, ".arcbridge", "plan", "tasks", `${phaseId}.yaml`);
|
|
7982
9127
|
try {
|
|
7983
9128
|
unlinkSync(taskFilePath);
|
|
7984
9129
|
} catch (e) {
|
|
@@ -8122,7 +9267,7 @@ function safeParseJson2(value, fallback) {
|
|
|
8122
9267
|
|
|
8123
9268
|
// src/generators/sync-generator.ts
|
|
8124
9269
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
8125
|
-
import { join as
|
|
9270
|
+
import { join as join18, dirname as dirname6 } from "path";
|
|
8126
9271
|
|
|
8127
9272
|
// src/templates/sync/claude-skill.ts
|
|
8128
9273
|
function claudeSkillTemplate(config) {
|
|
@@ -8300,21 +9445,21 @@ To enable automatic sync:
|
|
|
8300
9445
|
function generateSyncFiles(targetDir, config) {
|
|
8301
9446
|
const generated = [];
|
|
8302
9447
|
const action = githubActionTemplate(config);
|
|
8303
|
-
const actionPath =
|
|
8304
|
-
mkdirSync6(
|
|
9448
|
+
const actionPath = join18(targetDir, action.relativePath);
|
|
9449
|
+
mkdirSync6(dirname6(actionPath), { recursive: true });
|
|
8305
9450
|
writeFileSync6(actionPath, action.content, "utf-8");
|
|
8306
9451
|
generated.push(action.relativePath);
|
|
8307
9452
|
if (config.platforms.includes("claude")) {
|
|
8308
9453
|
const skill = claudeSkillTemplate(config);
|
|
8309
|
-
const skillPath =
|
|
8310
|
-
mkdirSync6(
|
|
9454
|
+
const skillPath = join18(targetDir, skill.relativePath);
|
|
9455
|
+
mkdirSync6(dirname6(skillPath), { recursive: true });
|
|
8311
9456
|
writeFileSync6(skillPath, skill.content, "utf-8");
|
|
8312
9457
|
generated.push(skill.relativePath);
|
|
8313
9458
|
}
|
|
8314
9459
|
if (config.platforms.includes("copilot")) {
|
|
8315
9460
|
const hook = copilotHookTemplate(config);
|
|
8316
|
-
const hookPath =
|
|
8317
|
-
mkdirSync6(
|
|
9461
|
+
const hookPath = join18(targetDir, hook.relativePath);
|
|
9462
|
+
mkdirSync6(dirname6(hookPath), { recursive: true });
|
|
8318
9463
|
writeFileSync6(hookPath, hook.content, "utf-8");
|
|
8319
9464
|
generated.push(hook.relativePath);
|
|
8320
9465
|
}
|
|
@@ -8323,7 +9468,7 @@ function generateSyncFiles(targetDir, config) {
|
|
|
8323
9468
|
|
|
8324
9469
|
// src/metrics/activity.ts
|
|
8325
9470
|
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
8326
|
-
import { join as
|
|
9471
|
+
import { join as join19 } from "path";
|
|
8327
9472
|
function insertActivity(db, params) {
|
|
8328
9473
|
const totalTokens = params.totalTokens ?? (params.inputTokens != null && params.outputTokens != null ? params.inputTokens + params.outputTokens : null);
|
|
8329
9474
|
const stmt = db.prepare(`
|
|
@@ -8481,7 +9626,7 @@ function exportMetrics(db, projectRoot, format, params, maxRows = 1e5) {
|
|
|
8481
9626
|
});
|
|
8482
9627
|
const rows = result.rows;
|
|
8483
9628
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
8484
|
-
const dir =
|
|
9629
|
+
const dir = join19(projectRoot, ".arcbridge", "metrics");
|
|
8485
9630
|
mkdirSync7(dir, { recursive: true });
|
|
8486
9631
|
let content;
|
|
8487
9632
|
let filename;
|
|
@@ -8578,7 +9723,7 @@ function exportMetrics(db, projectRoot, format, params, maxRows = 1e5) {
|
|
|
8578
9723
|
break;
|
|
8579
9724
|
}
|
|
8580
9725
|
}
|
|
8581
|
-
const filePath =
|
|
9726
|
+
const filePath = join19(dir, filename);
|
|
8582
9727
|
writeFileSync7(filePath, content, "utf-8");
|
|
8583
9728
|
return filePath;
|
|
8584
9729
|
}
|
|
@@ -8782,7 +9927,7 @@ function parseStatusCode(code) {
|
|
|
8782
9927
|
// src/testing/runner.ts
|
|
8783
9928
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
8784
9929
|
import { existsSync as existsSync9 } from "fs";
|
|
8785
|
-
import { resolve as
|
|
9930
|
+
import { resolve as resolve5 } from "path";
|
|
8786
9931
|
function verifyScenarios(db, projectRoot, options) {
|
|
8787
9932
|
const results = [];
|
|
8788
9933
|
const errors = [];
|
|
@@ -8819,7 +9964,7 @@ function verifyScenarios(db, projectRoot, options) {
|
|
|
8819
9964
|
}
|
|
8820
9965
|
if (testPaths.length === 0) continue;
|
|
8821
9966
|
const missingPaths = testPaths.filter(
|
|
8822
|
-
(tp) => !existsSync9(
|
|
9967
|
+
(tp) => !existsSync9(resolve5(projectRoot, tp))
|
|
8823
9968
|
);
|
|
8824
9969
|
if (missingPaths.length === testPaths.length) {
|
|
8825
9970
|
results.push({
|
|
@@ -8834,7 +9979,7 @@ function verifyScenarios(db, projectRoot, options) {
|
|
|
8834
9979
|
continue;
|
|
8835
9980
|
}
|
|
8836
9981
|
const existingPaths = testPaths.filter(
|
|
8837
|
-
(tp) => existsSync9(
|
|
9982
|
+
(tp) => existsSync9(resolve5(projectRoot, tp))
|
|
8838
9983
|
);
|
|
8839
9984
|
const start = Date.now();
|
|
8840
9985
|
let passed;
|
|
@@ -8888,11 +10033,11 @@ ${output}`;
|
|
|
8888
10033
|
}
|
|
8889
10034
|
|
|
8890
10035
|
// src/roles/loader.ts
|
|
8891
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
8892
|
-
import { join as
|
|
10036
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync11 } from "fs";
|
|
10037
|
+
import { join as join20 } from "path";
|
|
8893
10038
|
import matter4 from "gray-matter";
|
|
8894
10039
|
function loadRoles(projectRoot) {
|
|
8895
|
-
const agentsDir =
|
|
10040
|
+
const agentsDir = join20(projectRoot, ".arcbridge", "agents");
|
|
8896
10041
|
const roles = [];
|
|
8897
10042
|
const errors = [];
|
|
8898
10043
|
let files;
|
|
@@ -8902,9 +10047,9 @@ function loadRoles(projectRoot) {
|
|
|
8902
10047
|
return { roles: [], errors: [`Agent directory not found: ${agentsDir}`] };
|
|
8903
10048
|
}
|
|
8904
10049
|
for (const file of files) {
|
|
8905
|
-
const filePath =
|
|
10050
|
+
const filePath = join20(agentsDir, file);
|
|
8906
10051
|
try {
|
|
8907
|
-
const raw =
|
|
10052
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
8908
10053
|
const parsed = matter4(raw);
|
|
8909
10054
|
const input = {
|
|
8910
10055
|
...parsed.data,
|
|
@@ -8929,9 +10074,9 @@ function loadRole(projectRoot, roleId) {
|
|
|
8929
10074
|
if (!/^[a-z0-9-]+$/.test(roleId)) {
|
|
8930
10075
|
return { role: null, error: `Invalid role ID: "${roleId}" (must be kebab-case)` };
|
|
8931
10076
|
}
|
|
8932
|
-
const filePath =
|
|
10077
|
+
const filePath = join20(projectRoot, ".arcbridge", "agents", `${roleId}.md`);
|
|
8933
10078
|
try {
|
|
8934
|
-
const raw =
|
|
10079
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
8935
10080
|
const parsed = matter4(raw);
|
|
8936
10081
|
const input = {
|
|
8937
10082
|
...parsed.data,
|