project-graph-mcp 1.3.0 → 2.0.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/README.md +223 -17
- package/{AGENT_ROLE.md → docs/examples/AGENT_ROLE.md} +87 -30
- package/{AGENT_ROLE_MINIMAL.md → docs/examples/AGENT_ROLE_MINIMAL.md} +23 -8
- package/package.json +12 -8
- package/src/.project-graph-cache.json +1 -0
- package/src/analysis/analysis-cache.js +7 -0
- package/src/analysis/complexity.js +14 -0
- package/src/analysis/custom-rules.js +36 -0
- package/src/analysis/db-analysis.js +9 -0
- package/src/analysis/dead-code.js +19 -0
- package/src/analysis/full-analysis.js +18 -0
- package/src/analysis/jsdoc-checker.js +24 -0
- package/src/analysis/jsdoc-generator.js +10 -0
- package/src/analysis/large-files.js +11 -0
- package/src/analysis/outdated-patterns.js +12 -0
- package/src/analysis/similar-functions.js +16 -0
- package/src/analysis/test-annotations.js +21 -0
- package/src/analysis/type-checker.js +8 -0
- package/src/analysis/undocumented.js +14 -0
- package/src/cli/cli-handlers.js +4 -0
- package/src/cli/cli.js +5 -0
- package/src/compact/ai-context.js +7 -0
- package/src/compact/compact.js +18 -0
- package/src/compact/compress.js +13 -0
- package/src/compact/ctx-to-jsdoc.js +29 -0
- package/src/compact/doc-dialect.js +30 -0
- package/src/compact/expand.js +37 -0
- package/src/compact/framework-references.js +5 -0
- package/src/compact/instructions.js +3 -0
- package/src/compact/mode-config.js +8 -0
- package/src/compact/validate-pipeline.js +9 -0
- package/src/core/event-bus.js +9 -0
- package/src/core/filters.js +14 -0
- package/src/core/graph-builder.js +12 -0
- package/src/core/parser.js +31 -0
- package/src/core/workspace.js +8 -0
- package/src/lang/lang-go.js +17 -0
- package/src/lang/lang-python.js +12 -0
- package/src/lang/lang-sql.js +23 -0
- package/src/lang/lang-typescript.js +9 -0
- package/src/lang/lang-utils.js +4 -0
- package/src/mcp/mcp-server.js +17 -0
- package/src/mcp/tool-defs.js +3 -0
- package/src/mcp/tools.js +25 -0
- package/src/network/backend-lifecycle.js +19 -0
- package/src/network/backend.js +5 -0
- package/src/network/local-gateway.js +23 -0
- package/src/network/mdns.js +13 -0
- package/src/network/server.js +10 -0
- package/src/network/web-server.js +34 -0
- package/vendor/terser.mjs +49 -0
- package/web/.project-graph-cache.json +1 -0
- package/web/app.js +16 -0
- package/web/components/code-block.js +3 -0
- package/web/components/quick-open.js +5 -0
- package/web/dashboard-state.js +3 -0
- package/web/dashboard.html +27 -0
- package/web/dashboard.js +8 -0
- package/web/highlight.js +13 -0
- package/web/index.html +35 -0
- package/web/panels/ActionBoard/ActionBoard.css.js +1 -0
- package/web/panels/ActionBoard/ActionBoard.js +4 -0
- package/web/panels/ActionBoard/ActionBoard.tpl.js +1 -0
- package/web/panels/EventItem/EventItem.css.js +1 -0
- package/web/panels/EventItem/EventItem.js +4 -0
- package/web/panels/EventItem/EventItem.tpl.js +1 -0
- package/web/panels/ProjectItem/ProjectItem.css.js +1 -0
- package/web/panels/ProjectItem/ProjectItem.js +5 -0
- package/web/panels/ProjectItem/ProjectItem.tpl.js +1 -0
- package/web/panels/ProjectList/ProjectList.css.js +1 -0
- package/web/panels/ProjectList/ProjectList.js +4 -0
- package/web/panels/ProjectList/ProjectList.tpl.js +1 -0
- package/web/panels/SettingsPanel/.project-graph-cache.json +1 -0
- package/web/panels/SettingsPanel/SettingsPanel.css.js +1 -0
- package/web/panels/SettingsPanel/SettingsPanel.js +7 -0
- package/web/panels/SettingsPanel/SettingsPanel.tpl.js +1 -0
- package/web/panels/code-viewer.js +5 -0
- package/web/panels/ctx-panel.js +4 -0
- package/web/panels/dep-graph.js +6 -0
- package/web/panels/file-tree.js +188 -0
- package/web/panels/health-panel.js +3 -0
- package/web/panels/live-monitor.js +3 -0
- package/web/state.js +17 -0
- package/web/style.css +157 -0
- package/references/symbiote-3x.md +0 -834
- package/src/cli-handlers.js +0 -140
- package/src/cli.js +0 -83
- package/src/complexity.js +0 -223
- package/src/custom-rules.js +0 -583
- package/src/db-analysis.js +0 -194
- package/src/dead-code.js +0 -468
- package/src/filters.js +0 -227
- package/src/framework-references.js +0 -177
- package/src/full-analysis.js +0 -174
- package/src/graph-builder.js +0 -299
- package/src/instructions.js +0 -175
- package/src/jsdoc-generator.js +0 -214
- package/src/lang-go.js +0 -285
- package/src/lang-python.js +0 -197
- package/src/lang-sql.js +0 -309
- package/src/lang-typescript.js +0 -190
- package/src/lang-utils.js +0 -124
- package/src/large-files.js +0 -162
- package/src/mcp-server.js +0 -468
- package/src/outdated-patterns.js +0 -295
- package/src/parser.js +0 -452
- package/src/server.js +0 -28
- package/src/similar-functions.js +0 -278
- package/src/test-annotations.js +0 -301
- package/src/tool-defs.js +0 -525
- package/src/tools.js +0 -470
- package/src/undocumented.js +0 -260
- package/src/workspace.js +0 -70
package/src/parser.js
DELETED
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AST Parser for JavaScript files using Acorn
|
|
3
|
-
* Extracts classes, functions, methods, properties, imports, calls, and SQL queries
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
7
|
-
import { join, relative, resolve } from 'path';
|
|
8
|
-
import { parse } from '../vendor/acorn.mjs';
|
|
9
|
-
import * as walk from '../vendor/walk.mjs';
|
|
10
|
-
import { shouldExcludeDir, shouldExcludeFile, parseGitignore } from './filters.js';
|
|
11
|
-
import { parseTypeScript } from './lang-typescript.js';
|
|
12
|
-
import { parsePython } from './lang-python.js';
|
|
13
|
-
import { parseGo } from './lang-go.js';
|
|
14
|
-
import { parseSQL, extractSQLFromString, isSQLString } from './lang-sql.js';
|
|
15
|
-
|
|
16
|
-
/** Supported source file extensions */
|
|
17
|
-
const SOURCE_EXTENSIONS = ['.js', '.ts', '.tsx', '.py', '.go', '.sql'];
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} ClassInfo
|
|
21
|
-
* @property {string} name
|
|
22
|
-
* @property {string} [extends]
|
|
23
|
-
* @property {string[]} methods
|
|
24
|
-
* @property {string[]} properties
|
|
25
|
-
* @property {string[]} calls
|
|
26
|
-
* @property {string[]} [dbReads] - Tables read by SQL queries
|
|
27
|
-
* @property {string[]} [dbWrites] - Tables written by SQL queries
|
|
28
|
-
* @property {string} file
|
|
29
|
-
* @property {number} line
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @typedef {Object} FunctionInfo
|
|
34
|
-
* @property {string} name
|
|
35
|
-
* @property {boolean} exported
|
|
36
|
-
* @property {string[]} calls
|
|
37
|
-
* @property {string[]} [dbReads] - Tables read by SQL queries
|
|
38
|
-
* @property {string[]} [dbWrites] - Tables written by SQL queries
|
|
39
|
-
* @property {string} file
|
|
40
|
-
* @property {number} line
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @typedef {Object} ParseResult
|
|
45
|
-
* @property {string[]} files
|
|
46
|
-
* @property {ClassInfo[]} classes
|
|
47
|
-
* @property {FunctionInfo[]} functions
|
|
48
|
-
* @property {string[]} imports
|
|
49
|
-
* @property {string[]} exports
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Parse a JavaScript file content using AST
|
|
54
|
-
* @param {string} code
|
|
55
|
-
* @param {string} filename
|
|
56
|
-
* @returns {Promise<ParseResult>}
|
|
57
|
-
*/
|
|
58
|
-
export async function parseFile(code, filename) {
|
|
59
|
-
const result = {
|
|
60
|
-
file: filename,
|
|
61
|
-
classes: [],
|
|
62
|
-
functions: [],
|
|
63
|
-
imports: [],
|
|
64
|
-
exports: [],
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
let ast;
|
|
68
|
-
try {
|
|
69
|
-
ast = parse(code, {
|
|
70
|
-
ecmaVersion: 'latest',
|
|
71
|
-
sourceType: 'module',
|
|
72
|
-
locations: true,
|
|
73
|
-
});
|
|
74
|
-
} catch (e) {
|
|
75
|
-
// If parsing fails, return empty result
|
|
76
|
-
console.warn(`Parse error in ${filename}:`, e.message);
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Track exported names
|
|
81
|
-
const exportedNames = new Set();
|
|
82
|
-
|
|
83
|
-
// Walk the AST
|
|
84
|
-
walk.simple(ast, {
|
|
85
|
-
// Import declarations
|
|
86
|
-
ImportDeclaration(node) {
|
|
87
|
-
for (const spec of node.specifiers) {
|
|
88
|
-
if (spec.type === 'ImportDefaultSpecifier') {
|
|
89
|
-
result.imports.push(spec.local.name);
|
|
90
|
-
} else if (spec.type === 'ImportSpecifier') {
|
|
91
|
-
result.imports.push(spec.imported.name);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
// Export declarations
|
|
97
|
-
ExportNamedDeclaration(node) {
|
|
98
|
-
if (node.declaration) {
|
|
99
|
-
if (node.declaration.id) {
|
|
100
|
-
exportedNames.add(node.declaration.id.name);
|
|
101
|
-
} else if (node.declaration.declarations) {
|
|
102
|
-
for (const decl of node.declaration.declarations) {
|
|
103
|
-
exportedNames.add(decl.id.name);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (node.specifiers) {
|
|
108
|
-
for (const spec of node.specifiers) {
|
|
109
|
-
exportedNames.add(spec.exported.name);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
ExportDefaultDeclaration(node) {
|
|
115
|
-
if (node.declaration && node.declaration.id) {
|
|
116
|
-
exportedNames.add(node.declaration.id.name);
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
// Class declarations
|
|
121
|
-
ClassDeclaration(node) {
|
|
122
|
-
const classInfo = {
|
|
123
|
-
name: node.id.name,
|
|
124
|
-
extends: node.superClass ? node.superClass.name : null,
|
|
125
|
-
methods: [],
|
|
126
|
-
properties: [],
|
|
127
|
-
calls: [],
|
|
128
|
-
dbReads: [],
|
|
129
|
-
dbWrites: [],
|
|
130
|
-
file: filename,
|
|
131
|
-
line: node.loc.start.line,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// Extract methods and properties from class body
|
|
135
|
-
for (const element of node.body.body) {
|
|
136
|
-
if (element.type === 'MethodDefinition' && element.key.name !== 'constructor') {
|
|
137
|
-
classInfo.methods.push(element.key.name);
|
|
138
|
-
|
|
139
|
-
// Extract calls and SQL from method body
|
|
140
|
-
extractCallsAndSQL(element.value.body, classInfo.calls, classInfo.dbReads, classInfo.dbWrites);
|
|
141
|
-
} else if (element.type === 'PropertyDefinition') {
|
|
142
|
-
const propName = element.key.name;
|
|
143
|
-
|
|
144
|
-
// Check for init$ object properties
|
|
145
|
-
if (propName === 'init$' && element.value && element.value.type === 'ObjectExpression') {
|
|
146
|
-
for (const prop of element.value.properties) {
|
|
147
|
-
if (prop.key && prop.key.name) {
|
|
148
|
-
classInfo.properties.push(prop.key.name);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
result.classes.push(classInfo);
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
// Standalone function declarations
|
|
159
|
-
FunctionDeclaration(node) {
|
|
160
|
-
if (node.id) {
|
|
161
|
-
const funcInfo = {
|
|
162
|
-
name: node.id.name,
|
|
163
|
-
exported: false, // Will be updated later
|
|
164
|
-
calls: [],
|
|
165
|
-
dbReads: [],
|
|
166
|
-
dbWrites: [],
|
|
167
|
-
file: filename,
|
|
168
|
-
line: node.loc.start.line,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
extractCallsAndSQL(node.body, funcInfo.calls, funcInfo.dbReads, funcInfo.dbWrites);
|
|
172
|
-
result.functions.push(funcInfo);
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// Mark exported functions
|
|
178
|
-
for (const func of result.functions) {
|
|
179
|
-
func.exported = exportedNames.has(func.name);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Collect exports
|
|
183
|
-
result.exports = [...exportedNames];
|
|
184
|
-
|
|
185
|
-
return result;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** DB client method names that accept SQL as first argument */
|
|
189
|
-
const DB_METHODS = new Set(['query', 'execute', 'raw', 'exec', 'queryFile', 'none', 'one', 'many', 'any', 'oneOrNone', 'manyOrNone', 'result']);
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Extract method calls AND SQL queries from AST node in a single walk.
|
|
193
|
-
* Combines what was previously two separate walk.simple() calls.
|
|
194
|
-
* @param {Object} node
|
|
195
|
-
* @param {string[]} calls
|
|
196
|
-
* @param {string[]} [dbReads]
|
|
197
|
-
* @param {string[]} [dbWrites]
|
|
198
|
-
*/
|
|
199
|
-
function extractCallsAndSQL(node, calls, dbReads, dbWrites) {
|
|
200
|
-
if (!node) return;
|
|
201
|
-
|
|
202
|
-
walk.simple(node, {
|
|
203
|
-
CallExpression(callNode) {
|
|
204
|
-
const callee = callNode.callee;
|
|
205
|
-
|
|
206
|
-
// === Call extraction ===
|
|
207
|
-
if (callee.type === 'MemberExpression') {
|
|
208
|
-
const object = callee.object;
|
|
209
|
-
const property = callee.property;
|
|
210
|
-
|
|
211
|
-
if (property.type === 'Identifier') {
|
|
212
|
-
if (object.type === 'Identifier') {
|
|
213
|
-
const call = `${object.name}.${property.name}`;
|
|
214
|
-
if (!calls.includes(call)) calls.push(call);
|
|
215
|
-
} else if (object.type === 'MemberExpression' && object.property.type === 'Identifier') {
|
|
216
|
-
const call = `${object.property.name}.${property.name}`;
|
|
217
|
-
if (!calls.includes(call)) calls.push(call);
|
|
218
|
-
} else if (object.type === 'ThisExpression') {
|
|
219
|
-
const call = property.name;
|
|
220
|
-
if (!calls.includes(call)) calls.push(call);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
} else if (callee.type === 'Identifier') {
|
|
224
|
-
const call = callee.name;
|
|
225
|
-
if (!calls.includes(call)) calls.push(call);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// === SQL extraction from DB client calls ===
|
|
229
|
-
if (dbReads && dbWrites) {
|
|
230
|
-
const methodName = getCallMethodName(callNode);
|
|
231
|
-
if (methodName && DB_METHODS.has(methodName) && callNode.arguments.length > 0) {
|
|
232
|
-
const sqlStr = extractStringValue(callNode.arguments[0]);
|
|
233
|
-
if (sqlStr && isSQLString(sqlStr)) {
|
|
234
|
-
const ext = extractSQLFromString(sqlStr);
|
|
235
|
-
ext.reads.forEach(t => { if (!dbReads.includes(t)) dbReads.push(t); });
|
|
236
|
-
ext.writes.forEach(t => { if (!dbWrites.includes(t)) dbWrites.push(t); });
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
},
|
|
241
|
-
|
|
242
|
-
// === SQL: Tagged templates ===
|
|
243
|
-
TaggedTemplateExpression(tagNode) {
|
|
244
|
-
if (!dbReads || !dbWrites) return;
|
|
245
|
-
const tagName = getTagName(tagNode.tag);
|
|
246
|
-
if (tagName && /sql/i.test(tagName)) {
|
|
247
|
-
const sqlStr = templateToString(tagNode.quasi);
|
|
248
|
-
if (sqlStr) {
|
|
249
|
-
const ext = extractSQLFromString(sqlStr);
|
|
250
|
-
ext.reads.forEach(t => { if (!dbReads.includes(t)) dbReads.push(t); });
|
|
251
|
-
ext.writes.forEach(t => { if (!dbWrites.includes(t)) dbWrites.push(t); });
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
// === SQL: Standalone template literals ===
|
|
257
|
-
TemplateLiteral(tplNode) {
|
|
258
|
-
if (!dbReads || !dbWrites) return;
|
|
259
|
-
const sqlStr = templateToString(tplNode);
|
|
260
|
-
if (sqlStr && isSQLString(sqlStr)) {
|
|
261
|
-
const ext = extractSQLFromString(sqlStr);
|
|
262
|
-
ext.reads.forEach(t => { if (!dbReads.includes(t)) dbReads.push(t); });
|
|
263
|
-
ext.writes.forEach(t => { if (!dbWrites.includes(t)) dbWrites.push(t); });
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
// === SQL: String literals ===
|
|
268
|
-
Literal(litNode) {
|
|
269
|
-
if (!dbReads || !dbWrites) return;
|
|
270
|
-
if (typeof litNode.value === 'string' && isSQLString(litNode.value)) {
|
|
271
|
-
const ext = extractSQLFromString(litNode.value);
|
|
272
|
-
ext.reads.forEach(t => { if (!dbReads.includes(t)) dbReads.push(t); });
|
|
273
|
-
ext.writes.forEach(t => { if (!dbWrites.includes(t)) dbWrites.push(t); });
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Get tag name from tagged template expression.
|
|
281
|
-
* Handles: sql`...`, Prisma.sql`...`, db.sql`...`
|
|
282
|
-
* @param {Object} tag - AST node
|
|
283
|
-
* @returns {string|null}
|
|
284
|
-
*/
|
|
285
|
-
function getTagName(tag) {
|
|
286
|
-
if (tag.type === 'Identifier') return tag.name;
|
|
287
|
-
if (tag.type === 'MemberExpression' && tag.property.type === 'Identifier') {
|
|
288
|
-
return tag.property.name;
|
|
289
|
-
}
|
|
290
|
-
return null;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Get method name from a CallExpression callee.
|
|
295
|
-
* @param {Object} callNode
|
|
296
|
-
* @returns {string|null}
|
|
297
|
-
*/
|
|
298
|
-
function getCallMethodName(callNode) {
|
|
299
|
-
const callee = callNode.callee;
|
|
300
|
-
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
|
|
301
|
-
return callee.property.name;
|
|
302
|
-
}
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Extract string value from AST node (Literal or TemplateLiteral).
|
|
308
|
-
* For templates with expressions, substitutes $N placeholders.
|
|
309
|
-
* @param {Object} node
|
|
310
|
-
* @returns {string|null}
|
|
311
|
-
*/
|
|
312
|
-
function extractStringValue(node) {
|
|
313
|
-
if (!node) return null;
|
|
314
|
-
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
315
|
-
return node.value;
|
|
316
|
-
}
|
|
317
|
-
if (node.type === 'TemplateLiteral') {
|
|
318
|
-
return templateToString(node);
|
|
319
|
-
}
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Convert TemplateLiteral AST node to string.
|
|
325
|
-
* Expressions are replaced with $N placeholders.
|
|
326
|
-
* @param {Object} tplNode
|
|
327
|
-
* @returns {string}
|
|
328
|
-
*/
|
|
329
|
-
function templateToString(tplNode) {
|
|
330
|
-
if (!tplNode || !tplNode.quasis) return '';
|
|
331
|
-
let result = '';
|
|
332
|
-
for (let i = 0; i < tplNode.quasis.length; i++) {
|
|
333
|
-
result += tplNode.quasis[i].value.cooked || tplNode.quasis[i].value.raw || '';
|
|
334
|
-
if (i < tplNode.expressions?.length) {
|
|
335
|
-
result += '$' + (i + 1);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return result;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Parse all JS files in a directory
|
|
343
|
-
* @param {string} dir
|
|
344
|
-
* @returns {Promise<ParseResult>}
|
|
345
|
-
*/
|
|
346
|
-
export async function parseProject(dir) {
|
|
347
|
-
const result = {
|
|
348
|
-
files: [],
|
|
349
|
-
classes: [],
|
|
350
|
-
functions: [],
|
|
351
|
-
imports: [],
|
|
352
|
-
exports: [],
|
|
353
|
-
tables: [],
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
const resolvedDir = resolve(dir);
|
|
357
|
-
const files = findJSFiles(dir);
|
|
358
|
-
|
|
359
|
-
for (const file of files) {
|
|
360
|
-
const content = readFileSync(file, 'utf-8');
|
|
361
|
-
const relPath = relative(resolvedDir, file);
|
|
362
|
-
const parsed = await parseFileByExtension(content, relPath);
|
|
363
|
-
|
|
364
|
-
result.files.push(relPath);
|
|
365
|
-
result.classes.push(...parsed.classes);
|
|
366
|
-
result.functions.push(...parsed.functions);
|
|
367
|
-
result.imports.push(...parsed.imports);
|
|
368
|
-
result.exports.push(...parsed.exports);
|
|
369
|
-
if (parsed.tables?.length) {
|
|
370
|
-
result.tables.push(...parsed.tables);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Dedupe imports/exports
|
|
375
|
-
result.imports = [...new Set(result.imports)];
|
|
376
|
-
result.exports = [...new Set(result.exports)];
|
|
377
|
-
|
|
378
|
-
return result;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Route file to appropriate parser based on extension.
|
|
383
|
-
* @param {string} code
|
|
384
|
-
* @param {string} filename
|
|
385
|
-
* @returns {Promise<ParseResult>}
|
|
386
|
-
*/
|
|
387
|
-
async function parseFileByExtension(code, filename) {
|
|
388
|
-
if (filename.endsWith('.sql')) {
|
|
389
|
-
return parseSQL(code, filename);
|
|
390
|
-
}
|
|
391
|
-
if (filename.endsWith('.py')) {
|
|
392
|
-
return parsePython(code, filename);
|
|
393
|
-
}
|
|
394
|
-
if (filename.endsWith('.go')) {
|
|
395
|
-
return parseGo(code, filename);
|
|
396
|
-
}
|
|
397
|
-
if (filename.endsWith('.ts') || filename.endsWith('.tsx')) {
|
|
398
|
-
return parseTypeScript(code, filename);
|
|
399
|
-
}
|
|
400
|
-
// Default: JS via Acorn
|
|
401
|
-
return parseFile(code, filename);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Check if file is a supported source file.
|
|
406
|
-
* @param {string} filename
|
|
407
|
-
* @returns {boolean}
|
|
408
|
-
*/
|
|
409
|
-
function isSourceFile(filename) {
|
|
410
|
-
// Exclude Symbiote.js presentation files
|
|
411
|
-
if (filename.endsWith('.css.js') || filename.endsWith('.tpl.js')) {
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
return SOURCE_EXTENSIONS.some(ext => filename.endsWith(ext));
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Find all JS files recursively (uses filter configuration)
|
|
419
|
-
* @param {string} dir
|
|
420
|
-
* @param {string} [rootDir] - Root directory for relative path calculation
|
|
421
|
-
* @returns {string[]}
|
|
422
|
-
*/
|
|
423
|
-
export function findJSFiles(dir, rootDir = dir) {
|
|
424
|
-
// Parse gitignore on first call
|
|
425
|
-
if (dir === rootDir) {
|
|
426
|
-
parseGitignore(rootDir);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const files = [];
|
|
430
|
-
|
|
431
|
-
try {
|
|
432
|
-
for (const entry of readdirSync(dir)) {
|
|
433
|
-
const fullPath = join(dir, entry);
|
|
434
|
-
const stat = statSync(fullPath);
|
|
435
|
-
const relativePath = relative(rootDir, dir);
|
|
436
|
-
|
|
437
|
-
if (stat.isDirectory()) {
|
|
438
|
-
if (!shouldExcludeDir(entry, relativePath)) {
|
|
439
|
-
files.push(...findJSFiles(fullPath, rootDir));
|
|
440
|
-
}
|
|
441
|
-
} else if (isSourceFile(entry)) {
|
|
442
|
-
if (!shouldExcludeFile(entry, relativePath)) {
|
|
443
|
-
files.push(fullPath);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
} catch (e) {
|
|
448
|
-
console.warn(`Cannot read directory ${dir}:`, e.message);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return files;
|
|
452
|
-
}
|
package/src/server.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Entry Point for Project Graph MCP
|
|
4
|
-
*
|
|
5
|
-
* Decides whether to run in CLI mode or MCP Server mode (stdio)
|
|
6
|
-
* Usage:
|
|
7
|
-
* npx project-graph-mcp -> stdio server
|
|
8
|
-
* npx project-graph-mcp <cmd> [args] -> CLI execution
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { startStdioServer } from './mcp-server.js';
|
|
12
|
-
import { runCLI } from './cli.js';
|
|
13
|
-
|
|
14
|
-
// Main execution logic
|
|
15
|
-
// We check endsWith('server.js') to verify this is the main module being run
|
|
16
|
-
if (process.argv[1] && (process.argv[1].endsWith('server.js') || process.argv[1].endsWith('project-graph-mcp'))) {
|
|
17
|
-
const [, , command, ...args] = process.argv;
|
|
18
|
-
|
|
19
|
-
if (command) {
|
|
20
|
-
// CLI mode
|
|
21
|
-
runCLI(command, args);
|
|
22
|
-
} else {
|
|
23
|
-
// MCP stdio mode
|
|
24
|
-
// Use stderr for logs so stdout remains clean for JSON-RPC
|
|
25
|
-
console.error('Starting Project Graph MCP (stdio)...');
|
|
26
|
-
startStdioServer();
|
|
27
|
-
}
|
|
28
|
-
}
|