depwire-cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-C3LAKUAJ.js → chunk-X2DOGIIG.js} +198 -90
- package/dist/index.js +6 -6
- package/dist/mcpb-entry.js +2 -2
- package/dist/parser/grammars/tree-sitter-go.wasm +0 -0
- package/dist/parser/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/parser/grammars/tree-sitter-python.wasm +0 -0
- package/dist/parser/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/parser/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +4 -8
|
@@ -54,9 +54,50 @@ function fileExists(filePath) {
|
|
|
54
54
|
// src/parser/detect.ts
|
|
55
55
|
import { extname as extname3 } from "path";
|
|
56
56
|
|
|
57
|
-
// src/parser/
|
|
58
|
-
import Parser from "tree-sitter";
|
|
59
|
-
import
|
|
57
|
+
// src/parser/wasm-init.ts
|
|
58
|
+
import { Parser, Language } from "web-tree-sitter";
|
|
59
|
+
import path from "path";
|
|
60
|
+
import { fileURLToPath } from "url";
|
|
61
|
+
import { existsSync as existsSync2 } from "fs";
|
|
62
|
+
var initialized = false;
|
|
63
|
+
var languages = /* @__PURE__ */ new Map();
|
|
64
|
+
async function initParser() {
|
|
65
|
+
if (initialized) return;
|
|
66
|
+
await Parser.init();
|
|
67
|
+
const __dirname3 = path.dirname(fileURLToPath(import.meta.url));
|
|
68
|
+
let grammarsDir = path.join(__dirname3, "parser", "grammars");
|
|
69
|
+
if (!existsSync2(grammarsDir)) {
|
|
70
|
+
grammarsDir = path.join(path.dirname(__dirname3), "parser", "grammars");
|
|
71
|
+
}
|
|
72
|
+
if (!existsSync2(grammarsDir)) {
|
|
73
|
+
grammarsDir = path.join(__dirname3, "grammars");
|
|
74
|
+
}
|
|
75
|
+
const grammarFiles = {
|
|
76
|
+
"typescript": "tree-sitter-typescript.wasm",
|
|
77
|
+
"tsx": "tree-sitter-tsx.wasm",
|
|
78
|
+
"javascript": "tree-sitter-javascript.wasm",
|
|
79
|
+
"python": "tree-sitter-python.wasm",
|
|
80
|
+
"go": "tree-sitter-go.wasm"
|
|
81
|
+
};
|
|
82
|
+
for (const [name, file] of Object.entries(grammarFiles)) {
|
|
83
|
+
const wasmPath = path.join(grammarsDir, file);
|
|
84
|
+
const lang = await Language.load(wasmPath);
|
|
85
|
+
languages.set(name, lang);
|
|
86
|
+
}
|
|
87
|
+
initialized = true;
|
|
88
|
+
}
|
|
89
|
+
function getParser(language) {
|
|
90
|
+
if (!initialized) {
|
|
91
|
+
throw new Error("Parser not initialized. Call initParser() first.");
|
|
92
|
+
}
|
|
93
|
+
const lang = languages.get(language);
|
|
94
|
+
if (!lang) {
|
|
95
|
+
throw new Error(`Language '${language}' not loaded.`);
|
|
96
|
+
}
|
|
97
|
+
const parser = new Parser();
|
|
98
|
+
parser.setLanguage(lang);
|
|
99
|
+
return parser;
|
|
100
|
+
}
|
|
60
101
|
|
|
61
102
|
// src/parser/resolver.ts
|
|
62
103
|
import { join as join2, dirname, resolve, relative as relative2 } from "path";
|
|
@@ -152,13 +193,10 @@ function resolveImportPath(importPath, fromFile, projectRoot) {
|
|
|
152
193
|
}
|
|
153
194
|
|
|
154
195
|
// src/parser/typescript.ts
|
|
155
|
-
var tsParser = new Parser();
|
|
156
|
-
tsParser.setLanguage(TypeScript.typescript);
|
|
157
|
-
var tsxParser = new Parser();
|
|
158
|
-
tsxParser.setLanguage(TypeScript.tsx);
|
|
159
196
|
function parseTypeScriptFile(filePath, sourceCode, projectRoot) {
|
|
160
|
-
const
|
|
161
|
-
const
|
|
197
|
+
const languageType = filePath.endsWith(".tsx") ? "tsx" : "typescript";
|
|
198
|
+
const parser = getParser(languageType);
|
|
199
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
162
200
|
const context = {
|
|
163
201
|
filePath,
|
|
164
202
|
projectRoot,
|
|
@@ -613,14 +651,11 @@ var typescriptParser = {
|
|
|
613
651
|
};
|
|
614
652
|
|
|
615
653
|
// src/parser/python.ts
|
|
616
|
-
import Parser2 from "tree-sitter";
|
|
617
|
-
import Python from "tree-sitter-python";
|
|
618
654
|
import { dirname as dirname2, join as join3 } from "path";
|
|
619
|
-
import { existsSync as
|
|
620
|
-
var pyParser = new Parser2();
|
|
621
|
-
pyParser.setLanguage(Python);
|
|
655
|
+
import { existsSync as existsSync3 } from "fs";
|
|
622
656
|
function parsePythonFile(filePath, sourceCode, projectRoot) {
|
|
623
|
-
const
|
|
657
|
+
const parser = getParser("python");
|
|
658
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
624
659
|
const context = {
|
|
625
660
|
filePath,
|
|
626
661
|
projectRoot,
|
|
@@ -907,13 +942,13 @@ function resolveImportPath2(moduleName, currentFile, projectRoot) {
|
|
|
907
942
|
join3(targetDir, modulePath2, "__init__.py")
|
|
908
943
|
];
|
|
909
944
|
for (const candidate of candidates2) {
|
|
910
|
-
if (
|
|
945
|
+
if (existsSync3(candidate)) {
|
|
911
946
|
return candidate.substring(projectRoot.length + 1);
|
|
912
947
|
}
|
|
913
948
|
}
|
|
914
949
|
} else {
|
|
915
950
|
const initPath = join3(targetDir, "__init__.py");
|
|
916
|
-
if (
|
|
951
|
+
if (existsSync3(initPath)) {
|
|
917
952
|
return initPath.substring(projectRoot.length + 1);
|
|
918
953
|
}
|
|
919
954
|
}
|
|
@@ -925,7 +960,7 @@ function resolveImportPath2(moduleName, currentFile, projectRoot) {
|
|
|
925
960
|
join3(projectRoot, modulePath, "__init__.py")
|
|
926
961
|
];
|
|
927
962
|
for (const candidate of candidates) {
|
|
928
|
-
if (
|
|
963
|
+
if (existsSync3(candidate)) {
|
|
929
964
|
return candidate.substring(projectRoot.length + 1);
|
|
930
965
|
}
|
|
931
966
|
}
|
|
@@ -982,14 +1017,11 @@ var pythonParser = {
|
|
|
982
1017
|
};
|
|
983
1018
|
|
|
984
1019
|
// src/parser/javascript.ts
|
|
985
|
-
import
|
|
986
|
-
import JavaScript from "tree-sitter-javascript";
|
|
987
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1020
|
+
import { existsSync as existsSync4 } from "fs";
|
|
988
1021
|
import { join as join4, dirname as dirname3, extname as extname2 } from "path";
|
|
989
|
-
var jsParser = new Parser3();
|
|
990
|
-
jsParser.setLanguage(JavaScript);
|
|
991
1022
|
function parseJavaScriptFile(filePath, sourceCode, projectRoot) {
|
|
992
|
-
const
|
|
1023
|
+
const parser = getParser("javascript");
|
|
1024
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
993
1025
|
const context = {
|
|
994
1026
|
filePath,
|
|
995
1027
|
projectRoot,
|
|
@@ -1402,20 +1434,20 @@ function resolveJavaScriptImport(importPath, currentFile, projectRoot) {
|
|
|
1402
1434
|
const indexFiles = ["index.js", "index.jsx", "index.mjs"];
|
|
1403
1435
|
if (extname2(importPath)) {
|
|
1404
1436
|
const fullPath = targetPath;
|
|
1405
|
-
if (
|
|
1437
|
+
if (existsSync4(fullPath)) {
|
|
1406
1438
|
return fullPath.substring(projectRoot.length + 1);
|
|
1407
1439
|
}
|
|
1408
1440
|
return null;
|
|
1409
1441
|
}
|
|
1410
1442
|
for (const ext of extensions) {
|
|
1411
1443
|
const candidate = `${targetPath}${ext}`;
|
|
1412
|
-
if (
|
|
1444
|
+
if (existsSync4(candidate)) {
|
|
1413
1445
|
return candidate.substring(projectRoot.length + 1);
|
|
1414
1446
|
}
|
|
1415
1447
|
}
|
|
1416
1448
|
for (const indexFile of indexFiles) {
|
|
1417
1449
|
const candidate = join4(targetPath, indexFile);
|
|
1418
|
-
if (
|
|
1450
|
+
if (existsSync4(candidate)) {
|
|
1419
1451
|
return candidate.substring(projectRoot.length + 1);
|
|
1420
1452
|
}
|
|
1421
1453
|
}
|
|
@@ -1486,13 +1518,10 @@ var javascriptParser = {
|
|
|
1486
1518
|
};
|
|
1487
1519
|
|
|
1488
1520
|
// src/parser/go.ts
|
|
1489
|
-
import
|
|
1490
|
-
import Go from "tree-sitter-go";
|
|
1491
|
-
import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
1521
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
1492
1522
|
import { join as join5, dirname as dirname4 } from "path";
|
|
1493
|
-
var parser = new Parser4();
|
|
1494
|
-
parser.setLanguage(Go);
|
|
1495
1523
|
function parseGoFile(filePath, sourceCode, projectRoot) {
|
|
1524
|
+
const parser = getParser("go");
|
|
1496
1525
|
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
1497
1526
|
const moduleName = readGoModuleName(projectRoot);
|
|
1498
1527
|
const context = {
|
|
@@ -1773,7 +1802,7 @@ function readGoModuleName(projectRoot) {
|
|
|
1773
1802
|
let currentDir = projectRoot;
|
|
1774
1803
|
for (let i = 0; i < 5; i++) {
|
|
1775
1804
|
const goModPath = join5(currentDir, "go.mod");
|
|
1776
|
-
if (
|
|
1805
|
+
if (existsSync5(goModPath)) {
|
|
1777
1806
|
try {
|
|
1778
1807
|
const content = readFileSync2(goModPath, "utf-8");
|
|
1779
1808
|
const lines = content.split("\n");
|
|
@@ -1804,13 +1833,13 @@ function resolveGoImport(importPath, projectRoot, moduleName) {
|
|
|
1804
1833
|
}
|
|
1805
1834
|
const segments = importPath.split("/");
|
|
1806
1835
|
const packageDir = join5(projectRoot, ...segments);
|
|
1807
|
-
if (
|
|
1836
|
+
if (existsSync5(packageDir)) {
|
|
1808
1837
|
return findGoFilesInDir(packageDir, projectRoot);
|
|
1809
1838
|
}
|
|
1810
1839
|
return [];
|
|
1811
1840
|
}
|
|
1812
1841
|
function findGoFilesInDir(dir, projectRoot) {
|
|
1813
|
-
if (!
|
|
1842
|
+
if (!existsSync5(dir)) return [];
|
|
1814
1843
|
try {
|
|
1815
1844
|
const files = readdirSync2(dir);
|
|
1816
1845
|
const goFiles = files.filter((f) => f.endsWith(".go") && !f.endsWith("_test.go"));
|
|
@@ -1938,7 +1967,8 @@ function shouldParseFile(fullPath) {
|
|
|
1938
1967
|
return false;
|
|
1939
1968
|
}
|
|
1940
1969
|
}
|
|
1941
|
-
function parseProject(projectRoot, options) {
|
|
1970
|
+
async function parseProject(projectRoot, options) {
|
|
1971
|
+
await initParser();
|
|
1942
1972
|
const files = scanDirectory(projectRoot);
|
|
1943
1973
|
const parsedFiles = [];
|
|
1944
1974
|
let skippedFiles = 0;
|
|
@@ -1965,14 +1995,14 @@ function parseProject(projectRoot, options) {
|
|
|
1965
1995
|
if (options?.verbose) {
|
|
1966
1996
|
console.error(`[Parser] Parsing: ${file}`);
|
|
1967
1997
|
}
|
|
1968
|
-
const
|
|
1969
|
-
if (!
|
|
1998
|
+
const parser = getParserForFile(file);
|
|
1999
|
+
if (!parser) {
|
|
1970
2000
|
console.error(`No parser found for file: ${file}`);
|
|
1971
2001
|
skippedFiles++;
|
|
1972
2002
|
continue;
|
|
1973
2003
|
}
|
|
1974
2004
|
const sourceCode = readFileSync3(fullPath, "utf-8");
|
|
1975
|
-
const parsed =
|
|
2005
|
+
const parsed = parser.parseFile(file, sourceCode, projectRoot);
|
|
1976
2006
|
parsedFiles.push(parsed);
|
|
1977
2007
|
} catch (err) {
|
|
1978
2008
|
errorFiles++;
|
|
@@ -2044,6 +2074,43 @@ function buildGraph(parsedFiles) {
|
|
|
2044
2074
|
}
|
|
2045
2075
|
|
|
2046
2076
|
// src/graph/queries.ts
|
|
2077
|
+
function findSymbols(graph, query) {
|
|
2078
|
+
if (query.includes("::")) {
|
|
2079
|
+
if (graph.hasNode(query)) {
|
|
2080
|
+
const attrs = graph.getNodeAttributes(query);
|
|
2081
|
+
return [{
|
|
2082
|
+
id: query,
|
|
2083
|
+
name: attrs.name,
|
|
2084
|
+
kind: attrs.kind,
|
|
2085
|
+
filePath: attrs.filePath,
|
|
2086
|
+
startLine: attrs.startLine,
|
|
2087
|
+
endLine: attrs.endLine,
|
|
2088
|
+
exported: attrs.exported,
|
|
2089
|
+
scope: attrs.scope,
|
|
2090
|
+
dependentCount: graph.inDegree(query)
|
|
2091
|
+
}];
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
const queryLower = query.toLowerCase();
|
|
2095
|
+
const results = [];
|
|
2096
|
+
graph.forEachNode((nodeId, attrs) => {
|
|
2097
|
+
if (attrs.name.toLowerCase() === queryLower) {
|
|
2098
|
+
results.push({
|
|
2099
|
+
id: nodeId,
|
|
2100
|
+
name: attrs.name,
|
|
2101
|
+
kind: attrs.kind,
|
|
2102
|
+
filePath: attrs.filePath,
|
|
2103
|
+
startLine: attrs.startLine,
|
|
2104
|
+
endLine: attrs.endLine,
|
|
2105
|
+
exported: attrs.exported,
|
|
2106
|
+
scope: attrs.scope,
|
|
2107
|
+
dependentCount: graph.inDegree(nodeId)
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
results.sort((a, b) => b.dependentCount - a.dependentCount);
|
|
2112
|
+
return results;
|
|
2113
|
+
}
|
|
2047
2114
|
function getDependencies(graph, symbolId) {
|
|
2048
2115
|
if (!graph.hasNode(symbolId)) return [];
|
|
2049
2116
|
const dependencies = [];
|
|
@@ -2360,10 +2427,10 @@ function watchProject(projectRoot, callbacks) {
|
|
|
2360
2427
|
// src/viz/server.ts
|
|
2361
2428
|
import express from "express";
|
|
2362
2429
|
import open from "open";
|
|
2363
|
-
import { fileURLToPath } from "url";
|
|
2430
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2364
2431
|
import { dirname as dirname5, join as join7 } from "path";
|
|
2365
2432
|
import { WebSocketServer } from "ws";
|
|
2366
|
-
var __filename =
|
|
2433
|
+
var __filename = fileURLToPath2(import.meta.url);
|
|
2367
2434
|
var __dirname2 = dirname5(__filename);
|
|
2368
2435
|
var activeServer = null;
|
|
2369
2436
|
async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
@@ -2436,7 +2503,7 @@ Depwire visualization running at ${url2}`);
|
|
|
2436
2503
|
onFileChanged: async (filePath) => {
|
|
2437
2504
|
console.error(`File changed: ${filePath} \u2014 re-parsing project...`);
|
|
2438
2505
|
try {
|
|
2439
|
-
const parsedFiles = parseProject(projectRoot, options);
|
|
2506
|
+
const parsedFiles = await parseProject(projectRoot, options);
|
|
2440
2507
|
const newGraph = buildGraph(parsedFiles);
|
|
2441
2508
|
graph.clear();
|
|
2442
2509
|
newGraph.forEachNode((node, attrs) => {
|
|
@@ -2455,7 +2522,7 @@ Depwire visualization running at ${url2}`);
|
|
|
2455
2522
|
onFileAdded: async (filePath) => {
|
|
2456
2523
|
console.error(`File added: ${filePath} \u2014 re-parsing project...`);
|
|
2457
2524
|
try {
|
|
2458
|
-
const parsedFiles = parseProject(projectRoot, options);
|
|
2525
|
+
const parsedFiles = await parseProject(projectRoot, options);
|
|
2459
2526
|
const newGraph = buildGraph(parsedFiles);
|
|
2460
2527
|
graph.clear();
|
|
2461
2528
|
newGraph.forEachNode((node, attrs) => {
|
|
@@ -2471,10 +2538,10 @@ Depwire visualization running at ${url2}`);
|
|
|
2471
2538
|
console.error(`Failed to update graph for ${filePath}:`, error);
|
|
2472
2539
|
}
|
|
2473
2540
|
},
|
|
2474
|
-
onFileDeleted: (filePath) => {
|
|
2541
|
+
onFileDeleted: async (filePath) => {
|
|
2475
2542
|
console.error(`File deleted: ${filePath} \u2014 re-parsing project...`);
|
|
2476
2543
|
try {
|
|
2477
|
-
const parsedFiles = parseProject(projectRoot, options);
|
|
2544
|
+
const parsedFiles = await parseProject(projectRoot, options);
|
|
2478
2545
|
const newGraph = buildGraph(parsedFiles);
|
|
2479
2546
|
graph.clear();
|
|
2480
2547
|
newGraph.forEachNode((node, attrs) => {
|
|
@@ -2572,7 +2639,7 @@ async function updateFileInGraph(graph, projectRoot, relativeFilePath) {
|
|
|
2572
2639
|
}
|
|
2573
2640
|
|
|
2574
2641
|
// src/docs/generator.ts
|
|
2575
|
-
import { writeFileSync as writeFileSync2, mkdirSync, existsSync as
|
|
2642
|
+
import { writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync7 } from "fs";
|
|
2576
2643
|
import { join as join10 } from "path";
|
|
2577
2644
|
|
|
2578
2645
|
// src/docs/architecture.ts
|
|
@@ -2688,7 +2755,7 @@ function generateProjectSummary(graph, parseTime) {
|
|
|
2688
2755
|
const fileCount = getFileCount(graph);
|
|
2689
2756
|
const symbolCount = graph.order;
|
|
2690
2757
|
const edgeCount = graph.size;
|
|
2691
|
-
const
|
|
2758
|
+
const languages2 = getLanguageStats(graph);
|
|
2692
2759
|
let output = "";
|
|
2693
2760
|
output += `- **Total Files:** ${formatNumber(fileCount)}
|
|
2694
2761
|
`;
|
|
@@ -2698,10 +2765,10 @@ function generateProjectSummary(graph, parseTime) {
|
|
|
2698
2765
|
`;
|
|
2699
2766
|
output += `- **Parse Time:** ${parseTime.toFixed(1)}s
|
|
2700
2767
|
`;
|
|
2701
|
-
if (Object.keys(
|
|
2768
|
+
if (Object.keys(languages2).length > 1) {
|
|
2702
2769
|
output += "\n**Languages:**\n\n";
|
|
2703
2770
|
const totalFiles = fileCount;
|
|
2704
|
-
for (const [lang, count] of Object.entries(
|
|
2771
|
+
for (const [lang, count] of Object.entries(languages2).sort((a, b) => b[1] - a[1])) {
|
|
2705
2772
|
output += `- ${lang}: ${count} files (${formatPercent(count, totalFiles)})
|
|
2706
2773
|
`;
|
|
2707
2774
|
}
|
|
@@ -3620,20 +3687,20 @@ function findLongestPaths(graph, limit) {
|
|
|
3620
3687
|
}
|
|
3621
3688
|
const allPaths = [];
|
|
3622
3689
|
const visited = /* @__PURE__ */ new Set();
|
|
3623
|
-
function dfs(file,
|
|
3690
|
+
function dfs(file, path2) {
|
|
3624
3691
|
visited.add(file);
|
|
3625
|
-
|
|
3692
|
+
path2.push(file);
|
|
3626
3693
|
const neighbors = fileGraph.get(file);
|
|
3627
3694
|
if (!neighbors || neighbors.size === 0) {
|
|
3628
|
-
allPaths.push([...
|
|
3695
|
+
allPaths.push([...path2]);
|
|
3629
3696
|
} else {
|
|
3630
3697
|
for (const neighbor of neighbors) {
|
|
3631
3698
|
if (!visited.has(neighbor)) {
|
|
3632
|
-
dfs(neighbor,
|
|
3699
|
+
dfs(neighbor, path2);
|
|
3633
3700
|
}
|
|
3634
3701
|
}
|
|
3635
3702
|
}
|
|
3636
|
-
|
|
3703
|
+
path2.pop();
|
|
3637
3704
|
visited.delete(file);
|
|
3638
3705
|
}
|
|
3639
3706
|
for (const root of roots.slice(0, 10)) {
|
|
@@ -3804,8 +3871,8 @@ function getLanguageStats2(graph) {
|
|
|
3804
3871
|
}
|
|
3805
3872
|
function generateQuickOrientation(graph) {
|
|
3806
3873
|
const fileCount = getFileCount4(graph);
|
|
3807
|
-
const
|
|
3808
|
-
const primaryLang = Object.entries(
|
|
3874
|
+
const languages2 = getLanguageStats2(graph);
|
|
3875
|
+
const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
|
|
3809
3876
|
const dirs = /* @__PURE__ */ new Set();
|
|
3810
3877
|
graph.forEachNode((node, attrs) => {
|
|
3811
3878
|
const dir = dirname7(attrs.filePath);
|
|
@@ -4137,11 +4204,11 @@ function generateDepwireUsage(projectRoot) {
|
|
|
4137
4204
|
}
|
|
4138
4205
|
|
|
4139
4206
|
// src/docs/metadata.ts
|
|
4140
|
-
import { existsSync as
|
|
4207
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
4141
4208
|
import { join as join9 } from "path";
|
|
4142
4209
|
function loadMetadata(outputDir) {
|
|
4143
4210
|
const metadataPath = join9(outputDir, "metadata.json");
|
|
4144
|
-
if (!
|
|
4211
|
+
if (!existsSync6(metadataPath)) {
|
|
4145
4212
|
return null;
|
|
4146
4213
|
}
|
|
4147
4214
|
try {
|
|
@@ -4196,7 +4263,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
4196
4263
|
const generated = [];
|
|
4197
4264
|
const errors = [];
|
|
4198
4265
|
try {
|
|
4199
|
-
if (!
|
|
4266
|
+
if (!existsSync7(options.outputDir)) {
|
|
4200
4267
|
mkdirSync(options.outputDir, { recursive: true });
|
|
4201
4268
|
if (options.verbose) {
|
|
4202
4269
|
console.log(`Created output directory: ${options.outputDir}`);
|
|
@@ -4303,11 +4370,11 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4303
4370
|
|
|
4304
4371
|
// src/mcp/tools.ts
|
|
4305
4372
|
import { dirname as dirname8, join as join12 } from "path";
|
|
4306
|
-
import { existsSync as
|
|
4373
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
4307
4374
|
|
|
4308
4375
|
// src/mcp/connect.ts
|
|
4309
4376
|
import simpleGit from "simple-git";
|
|
4310
|
-
import { existsSync as
|
|
4377
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4311
4378
|
import { join as join11, basename as basename3, resolve as resolve2 } from "path";
|
|
4312
4379
|
import { tmpdir, homedir } from "os";
|
|
4313
4380
|
function validateProjectPath(source) {
|
|
@@ -4352,7 +4419,7 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
4352
4419
|
const cloneDir = join11(reposDir, projectName);
|
|
4353
4420
|
console.error(`Connecting to GitHub repo: ${source}`);
|
|
4354
4421
|
const git = simpleGit();
|
|
4355
|
-
if (
|
|
4422
|
+
if (existsSync8(cloneDir)) {
|
|
4356
4423
|
console.error(`Repo already cloned at ${cloneDir}, pulling latest changes...`);
|
|
4357
4424
|
try {
|
|
4358
4425
|
await git.cwd(cloneDir).pull();
|
|
@@ -4379,7 +4446,7 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
4379
4446
|
message: validation2.error
|
|
4380
4447
|
};
|
|
4381
4448
|
}
|
|
4382
|
-
if (!
|
|
4449
|
+
if (!existsSync8(source)) {
|
|
4383
4450
|
return {
|
|
4384
4451
|
error: "Directory not found",
|
|
4385
4452
|
message: `Directory does not exist: ${source}`
|
|
@@ -4395,7 +4462,7 @@ async function connectToRepo(source, subdirectory, state) {
|
|
|
4395
4462
|
message: validation.error
|
|
4396
4463
|
};
|
|
4397
4464
|
}
|
|
4398
|
-
if (!
|
|
4465
|
+
if (!existsSync8(projectRoot)) {
|
|
4399
4466
|
return {
|
|
4400
4467
|
error: "Project root not found",
|
|
4401
4468
|
message: `Directory does not exist: ${projectRoot}`
|
|
@@ -4520,13 +4587,13 @@ function getToolsList() {
|
|
|
4520
4587
|
},
|
|
4521
4588
|
{
|
|
4522
4589
|
name: "get_symbol_info",
|
|
4523
|
-
description: "Look up detailed information about a symbol (function, class, variable, type, etc.) by name.
|
|
4590
|
+
description: "Look up detailed information about a symbol (function, class, variable, type, etc.) by name. Pass a symbol name (e.g., 'Router') or a fully qualified ID (e.g., 'src/router.ts::Router') for exact matching. If multiple symbols share the same name, returns all matches for disambiguation.",
|
|
4524
4591
|
inputSchema: {
|
|
4525
4592
|
type: "object",
|
|
4526
4593
|
properties: {
|
|
4527
4594
|
name: {
|
|
4528
4595
|
type: "string",
|
|
4529
|
-
description: "The symbol name to look up (e.g., 'UserService'
|
|
4596
|
+
description: "The symbol name to look up (e.g., 'UserService') or full ID (e.g., 'src/services/UserService.ts::UserService')"
|
|
4530
4597
|
}
|
|
4531
4598
|
},
|
|
4532
4599
|
required: ["name"]
|
|
@@ -4534,13 +4601,13 @@ function getToolsList() {
|
|
|
4534
4601
|
},
|
|
4535
4602
|
{
|
|
4536
4603
|
name: "get_dependencies",
|
|
4537
|
-
description: "Get all symbols that a given symbol depends on (what does this symbol use/import/call?).",
|
|
4604
|
+
description: "Get all symbols that a given symbol depends on (what does this symbol use/import/call?). Pass a symbol name (e.g., 'Router') or a fully qualified ID (e.g., 'src/router.ts::Router') for exact matching. If multiple symbols share the same name, returns all matches for disambiguation.",
|
|
4538
4605
|
inputSchema: {
|
|
4539
4606
|
type: "object",
|
|
4540
4607
|
properties: {
|
|
4541
4608
|
symbol: {
|
|
4542
4609
|
type: "string",
|
|
4543
|
-
description: "Symbol name or ID
|
|
4610
|
+
description: "Symbol name (e.g., 'Router') or full ID (e.g., 'src/router.ts::Router')"
|
|
4544
4611
|
}
|
|
4545
4612
|
},
|
|
4546
4613
|
required: ["symbol"]
|
|
@@ -4548,13 +4615,13 @@ function getToolsList() {
|
|
|
4548
4615
|
},
|
|
4549
4616
|
{
|
|
4550
4617
|
name: "get_dependents",
|
|
4551
|
-
description: "Get all symbols that depend on a given symbol (what uses this symbol?).",
|
|
4618
|
+
description: "Get all symbols that depend on a given symbol (what uses this symbol?). Pass a symbol name (e.g., 'Router') or a fully qualified ID (e.g., 'src/router.ts::Router') for exact matching. If multiple symbols share the same name, returns all matches for disambiguation.",
|
|
4552
4619
|
inputSchema: {
|
|
4553
4620
|
type: "object",
|
|
4554
4621
|
properties: {
|
|
4555
4622
|
symbol: {
|
|
4556
4623
|
type: "string",
|
|
4557
|
-
description: "Symbol name or ID
|
|
4624
|
+
description: "Symbol name (e.g., 'Router') or full ID (e.g., 'src/router.ts::Router')"
|
|
4558
4625
|
}
|
|
4559
4626
|
},
|
|
4560
4627
|
required: ["symbol"]
|
|
@@ -4562,13 +4629,13 @@ function getToolsList() {
|
|
|
4562
4629
|
},
|
|
4563
4630
|
{
|
|
4564
4631
|
name: "impact_analysis",
|
|
4565
|
-
description: "Analyze what would break if a symbol is changed, renamed, or removed. Shows direct dependents, transitive dependents (chain reaction), and all affected files. Use this before making changes to understand the blast radius.",
|
|
4632
|
+
description: "Analyze what would break if a symbol is changed, renamed, or removed. Shows direct dependents, transitive dependents (chain reaction), and all affected files. Pass a symbol name (e.g., 'Router') or a fully qualified ID (e.g., 'src/router.ts::Router') for exact matching. If multiple symbols share the same name, returns all matches for disambiguation. Use this before making changes to understand the blast radius.",
|
|
4566
4633
|
inputSchema: {
|
|
4567
4634
|
type: "object",
|
|
4568
4635
|
properties: {
|
|
4569
4636
|
symbol: {
|
|
4570
4637
|
type: "string",
|
|
4571
|
-
description: "
|
|
4638
|
+
description: "Symbol name (e.g., 'Router') or full ID (e.g., 'src/router.ts::Router')"
|
|
4572
4639
|
}
|
|
4573
4640
|
},
|
|
4574
4641
|
required: ["symbol"]
|
|
@@ -4790,12 +4857,39 @@ async function handleToolCall(name, args, state) {
|
|
|
4790
4857
|
};
|
|
4791
4858
|
}
|
|
4792
4859
|
}
|
|
4860
|
+
function createDisambiguationResponse(matches, queryName) {
|
|
4861
|
+
const suggestion = matches.length > 0 ? matches[0].id : "";
|
|
4862
|
+
return {
|
|
4863
|
+
ambiguous: true,
|
|
4864
|
+
message: `Found ${matches.length} symbols named '${queryName}'. Please specify which one by using the full ID (e.g., '${suggestion}').`,
|
|
4865
|
+
matches: matches.map((m, index) => ({
|
|
4866
|
+
id: m.id,
|
|
4867
|
+
kind: m.kind,
|
|
4868
|
+
filePath: m.filePath,
|
|
4869
|
+
line: m.startLine,
|
|
4870
|
+
dependents: m.dependentCount,
|
|
4871
|
+
hint: index === 0 && m.dependentCount > 0 ? "Most dependents \u2014 likely the one you want" : ""
|
|
4872
|
+
})),
|
|
4873
|
+
suggestion
|
|
4874
|
+
};
|
|
4875
|
+
}
|
|
4793
4876
|
function handleGetSymbolInfo(name, graph) {
|
|
4794
|
-
const matches =
|
|
4795
|
-
|
|
4796
|
-
|
|
4877
|
+
const matches = findSymbols(graph, name);
|
|
4878
|
+
if (matches.length === 0) {
|
|
4879
|
+
const fuzzyMatches = searchSymbols(graph, name).slice(0, 10);
|
|
4880
|
+
return {
|
|
4881
|
+
error: `Symbol '${name}' not found`,
|
|
4882
|
+
suggestion: fuzzyMatches.length > 0 ? `Did you mean: ${fuzzyMatches.map((m) => m.name).join(", ")}?` : "Try using search_symbols to find available symbols",
|
|
4883
|
+
fuzzyMatches: fuzzyMatches.map((m) => ({
|
|
4884
|
+
id: m.id,
|
|
4885
|
+
name: m.name,
|
|
4886
|
+
kind: m.kind,
|
|
4887
|
+
filePath: m.filePath
|
|
4888
|
+
}))
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4797
4891
|
return {
|
|
4798
|
-
matches:
|
|
4892
|
+
matches: matches.map((m) => ({
|
|
4799
4893
|
id: m.id,
|
|
4800
4894
|
name: m.name,
|
|
4801
4895
|
kind: m.kind,
|
|
@@ -4803,19 +4897,24 @@ function handleGetSymbolInfo(name, graph) {
|
|
|
4803
4897
|
startLine: m.startLine,
|
|
4804
4898
|
endLine: m.endLine,
|
|
4805
4899
|
exported: m.exported,
|
|
4806
|
-
scope: m.scope
|
|
4900
|
+
scope: m.scope,
|
|
4901
|
+
dependents: m.dependentCount
|
|
4807
4902
|
})),
|
|
4808
|
-
count:
|
|
4903
|
+
count: matches.length
|
|
4809
4904
|
};
|
|
4810
4905
|
}
|
|
4811
4906
|
function handleGetDependencies(symbol, graph) {
|
|
4812
|
-
const matches =
|
|
4907
|
+
const matches = findSymbols(graph, symbol);
|
|
4813
4908
|
if (matches.length === 0) {
|
|
4909
|
+
const fuzzyMatches = searchSymbols(graph, symbol).slice(0, 10);
|
|
4814
4910
|
return {
|
|
4815
4911
|
error: `Symbol '${symbol}' not found`,
|
|
4816
|
-
suggestion: "Try using search_symbols to find available symbols"
|
|
4912
|
+
suggestion: fuzzyMatches.length > 0 ? `Did you mean: ${fuzzyMatches.map((m) => m.name).join(", ")}?` : "Try using search_symbols to find available symbols"
|
|
4817
4913
|
};
|
|
4818
4914
|
}
|
|
4915
|
+
if (matches.length > 1) {
|
|
4916
|
+
return createDisambiguationResponse(matches, symbol);
|
|
4917
|
+
}
|
|
4819
4918
|
const target = matches[0];
|
|
4820
4919
|
const deps = getDependencies(graph, target.id);
|
|
4821
4920
|
const grouped = {};
|
|
@@ -4833,19 +4932,23 @@ function handleGetDependencies(symbol, graph) {
|
|
|
4833
4932
|
});
|
|
4834
4933
|
const totalCount = Object.values(grouped).reduce((sum, arr) => sum + arr.length, 0);
|
|
4835
4934
|
return {
|
|
4836
|
-
symbol:
|
|
4935
|
+
symbol: target.id,
|
|
4837
4936
|
dependencies: grouped,
|
|
4838
4937
|
totalCount
|
|
4839
4938
|
};
|
|
4840
4939
|
}
|
|
4841
4940
|
function handleGetDependents(symbol, graph) {
|
|
4842
|
-
const matches =
|
|
4941
|
+
const matches = findSymbols(graph, symbol);
|
|
4843
4942
|
if (matches.length === 0) {
|
|
4943
|
+
const fuzzyMatches = searchSymbols(graph, symbol).slice(0, 10);
|
|
4844
4944
|
return {
|
|
4845
4945
|
error: `Symbol '${symbol}' not found`,
|
|
4846
|
-
suggestion: "Try using search_symbols to find available symbols"
|
|
4946
|
+
suggestion: fuzzyMatches.length > 0 ? `Did you mean: ${fuzzyMatches.map((m) => m.name).join(", ")}?` : "Try using search_symbols to find available symbols"
|
|
4847
4947
|
};
|
|
4848
4948
|
}
|
|
4949
|
+
if (matches.length > 1) {
|
|
4950
|
+
return createDisambiguationResponse(matches, symbol);
|
|
4951
|
+
}
|
|
4849
4952
|
const target = matches[0];
|
|
4850
4953
|
const deps = getDependents(graph, target.id);
|
|
4851
4954
|
const grouped = {};
|
|
@@ -4863,19 +4966,23 @@ function handleGetDependents(symbol, graph) {
|
|
|
4863
4966
|
});
|
|
4864
4967
|
const totalCount = Object.values(grouped).reduce((sum, arr) => sum + arr.length, 0);
|
|
4865
4968
|
return {
|
|
4866
|
-
symbol:
|
|
4969
|
+
symbol: target.id,
|
|
4867
4970
|
dependents: grouped,
|
|
4868
4971
|
totalCount
|
|
4869
4972
|
};
|
|
4870
4973
|
}
|
|
4871
4974
|
function handleImpactAnalysis(symbol, graph) {
|
|
4872
|
-
const matches =
|
|
4975
|
+
const matches = findSymbols(graph, symbol);
|
|
4873
4976
|
if (matches.length === 0) {
|
|
4977
|
+
const fuzzyMatches = searchSymbols(graph, symbol).slice(0, 10);
|
|
4874
4978
|
return {
|
|
4875
4979
|
error: `Symbol '${symbol}' not found`,
|
|
4876
|
-
suggestion: "Try using search_symbols to find available symbols"
|
|
4980
|
+
suggestion: fuzzyMatches.length > 0 ? `Did you mean: ${fuzzyMatches.map((m) => m.name).join(", ")}?` : "Try using search_symbols to find available symbols"
|
|
4877
4981
|
};
|
|
4878
4982
|
}
|
|
4983
|
+
if (matches.length > 1) {
|
|
4984
|
+
return createDisambiguationResponse(matches, symbol);
|
|
4985
|
+
}
|
|
4879
4986
|
const target = matches[0];
|
|
4880
4987
|
const impact = getImpact(graph, target.id);
|
|
4881
4988
|
const directWithKinds = impact.directDependents.map((dep) => {
|
|
@@ -4898,6 +5005,7 @@ function handleImpactAnalysis(symbol, graph) {
|
|
|
4898
5005
|
const summary = `Changing ${target.name} would directly affect ${impact.directDependents.length} symbol(s) and transitively affect ${transitiveFormatted.length} more, across ${impact.affectedFiles.length} file(s).`;
|
|
4899
5006
|
return {
|
|
4900
5007
|
symbol: {
|
|
5008
|
+
id: target.id,
|
|
4901
5009
|
name: target.name,
|
|
4902
5010
|
filePath: target.filePath,
|
|
4903
5011
|
kind: target.kind
|
|
@@ -5097,7 +5205,7 @@ The server will keep running until you end the MCP session or press Ctrl+C.`;
|
|
|
5097
5205
|
}
|
|
5098
5206
|
async function handleGetProjectDocs(docType, state) {
|
|
5099
5207
|
const docsDir = join12(state.projectRoot, ".depwire");
|
|
5100
|
-
if (!
|
|
5208
|
+
if (!existsSync9(docsDir)) {
|
|
5101
5209
|
const errorMessage = `Project documentation has not been generated yet.
|
|
5102
5210
|
|
|
5103
5211
|
Run \`depwire docs ${state.projectRoot}\` to generate codebase documentation.
|
|
@@ -5128,7 +5236,7 @@ Available document types:
|
|
|
5128
5236
|
continue;
|
|
5129
5237
|
}
|
|
5130
5238
|
const filePath = join12(docsDir, metadata.documents[doc].file);
|
|
5131
|
-
if (!
|
|
5239
|
+
if (!existsSync9(filePath)) {
|
|
5132
5240
|
missing.push(doc);
|
|
5133
5241
|
continue;
|
|
5134
5242
|
}
|
|
@@ -5159,14 +5267,14 @@ async function handleUpdateProjectDocs(docType, state) {
|
|
|
5159
5267
|
const startTime = Date.now();
|
|
5160
5268
|
const docsDir = join12(state.projectRoot, ".depwire");
|
|
5161
5269
|
console.error("Regenerating project documentation...");
|
|
5162
|
-
const parsedFiles = parseProject(state.projectRoot);
|
|
5270
|
+
const parsedFiles = await parseProject(state.projectRoot);
|
|
5163
5271
|
const graph = buildGraph(parsedFiles);
|
|
5164
5272
|
const parseTime = (Date.now() - startTime) / 1e3;
|
|
5165
5273
|
state.graph = graph;
|
|
5166
5274
|
const packageJsonPath = join12(__dirname, "../../package.json");
|
|
5167
5275
|
const packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
|
|
5168
5276
|
const docsToGenerate = docType === "all" ? ["architecture", "conventions", "dependencies", "onboarding"] : [docType];
|
|
5169
|
-
const docsExist =
|
|
5277
|
+
const docsExist = existsSync9(docsDir);
|
|
5170
5278
|
const result = await generateDocs(graph, state.projectRoot, packageJson.version, parseTime, {
|
|
5171
5279
|
outputDir: docsDir,
|
|
5172
5280
|
format: "markdown",
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
startVizServer,
|
|
13
13
|
updateFileInGraph,
|
|
14
14
|
watchProject
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-X2DOGIIG.js";
|
|
16
16
|
|
|
17
17
|
// src/index.ts
|
|
18
18
|
import { Command } from "commander";
|
|
@@ -100,7 +100,7 @@ program.command("parse").description("Parse a TypeScript project and build depen
|
|
|
100
100
|
try {
|
|
101
101
|
const projectRoot = resolve(directory);
|
|
102
102
|
console.log(`Parsing project: ${projectRoot}`);
|
|
103
|
-
const parsedFiles = parseProject(projectRoot, {
|
|
103
|
+
const parsedFiles = await parseProject(projectRoot, {
|
|
104
104
|
exclude: options.exclude,
|
|
105
105
|
verbose: options.verbose
|
|
106
106
|
});
|
|
@@ -145,7 +145,7 @@ program.command("query").description("Query impact analysis for a symbol").argum
|
|
|
145
145
|
graph = importFromJSON(json);
|
|
146
146
|
} else {
|
|
147
147
|
console.log("Parsing project...");
|
|
148
|
-
const parsedFiles = parseProject(projectRoot);
|
|
148
|
+
const parsedFiles = await parseProject(projectRoot);
|
|
149
149
|
graph = buildGraph(parsedFiles);
|
|
150
150
|
}
|
|
151
151
|
const matches = searchSymbols(graph, symbolName);
|
|
@@ -186,7 +186,7 @@ program.command("viz").description("Launch interactive arc diagram visualization
|
|
|
186
186
|
try {
|
|
187
187
|
const projectRoot = resolve(directory);
|
|
188
188
|
console.log(`Parsing project: ${projectRoot}`);
|
|
189
|
-
const parsedFiles = parseProject(projectRoot, {
|
|
189
|
+
const parsedFiles = await parseProject(projectRoot, {
|
|
190
190
|
exclude: options.exclude,
|
|
191
191
|
verbose: options.verbose
|
|
192
192
|
});
|
|
@@ -210,7 +210,7 @@ program.command("mcp").description("Start MCP server for AI coding tools").argum
|
|
|
210
210
|
if (directory) {
|
|
211
211
|
const projectRoot = resolve(directory);
|
|
212
212
|
console.error(`Parsing project: ${projectRoot}`);
|
|
213
|
-
const parsedFiles = parseProject(projectRoot);
|
|
213
|
+
const parsedFiles = await parseProject(projectRoot);
|
|
214
214
|
console.error(`Parsed ${parsedFiles.length} files`);
|
|
215
215
|
const graph = buildGraph(parsedFiles);
|
|
216
216
|
console.error(`Built graph: ${graph.order} symbols, ${graph.size} edges`);
|
|
@@ -273,7 +273,7 @@ program.command("docs").description("Generate comprehensive codebase documentati
|
|
|
273
273
|
addToGitignore(projectRoot, ".depwire/");
|
|
274
274
|
}
|
|
275
275
|
console.log(`Parsing project: ${projectRoot}`);
|
|
276
|
-
const parsedFiles = parseProject(projectRoot, {
|
|
276
|
+
const parsedFiles = await parseProject(projectRoot, {
|
|
277
277
|
exclude: options.exclude,
|
|
278
278
|
verbose: options.verbose
|
|
279
279
|
});
|
package/dist/mcpb-entry.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
startMcpServer,
|
|
7
7
|
updateFileInGraph,
|
|
8
8
|
watchProject
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-X2DOGIIG.js";
|
|
10
10
|
|
|
11
11
|
// src/mcpb-entry.ts
|
|
12
12
|
import { resolve } from "path";
|
|
@@ -17,7 +17,7 @@ async function main() {
|
|
|
17
17
|
try {
|
|
18
18
|
const projectRoot = resolve(projectPath);
|
|
19
19
|
console.error(`[MCPB] Parsing project: ${projectRoot}`);
|
|
20
|
-
const parsedFiles = parseProject(projectRoot);
|
|
20
|
+
const parsedFiles = await parseProject(projectRoot);
|
|
21
21
|
console.error(`[MCPB] Parsed ${parsedFiles.length} files`);
|
|
22
22
|
const graph = buildGraph(parsedFiles);
|
|
23
23
|
console.error(`[MCPB] Built graph: ${graph.order} symbols, ${graph.size} edges`);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "depwire-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Code cross-reference visualization and AI context engine for TypeScript
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Code cross-reference visualization and AI context engine for TypeScript, JavaScript, Python, and Go. Zero native dependencies — works on Windows, macOS, and Linux.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"depwire": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsup src/index.ts src/mcpb-entry.ts --format esm --dts --clean && npm run copy-static",
|
|
11
|
-
"copy-static": "mkdir -p dist/viz/public && cp -r src/viz/public/* dist/viz/public/",
|
|
11
|
+
"copy-static": "mkdir -p dist/viz/public dist/parser/grammars && cp -r src/viz/public/* dist/viz/public/ && cp src/parser/grammars/*.wasm dist/parser/grammars/",
|
|
12
12
|
"dev": "tsup src/index.ts --format esm --watch",
|
|
13
13
|
"start": "node dist/index.js",
|
|
14
14
|
"build:mcpb": "npm run build && ./scripts/build-mcpb.sh"
|
|
@@ -59,11 +59,7 @@
|
|
|
59
59
|
"minimatch": "^10.2.4",
|
|
60
60
|
"open": "11.0.0",
|
|
61
61
|
"simple-git": "3.31.1",
|
|
62
|
-
"tree-sitter": "0.
|
|
63
|
-
"tree-sitter-go": "0.21.2",
|
|
64
|
-
"tree-sitter-javascript": "0.21.4",
|
|
65
|
-
"tree-sitter-python": "0.21.0",
|
|
66
|
-
"tree-sitter-typescript": "0.23.2",
|
|
62
|
+
"web-tree-sitter": "^0.26.6",
|
|
67
63
|
"ws": "8.19.0",
|
|
68
64
|
"zod": "4.3.6"
|
|
69
65
|
},
|