project-graph-mcp 1.5.0 → 2.1.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 +171 -31
- package/docs/img/explorer-compact.jpg +0 -0
- package/docs/img/explorer-expanded.jpg +0 -0
- package/package.json +12 -8
- package/src/.project-graph-cache.json +1 -1
- 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/.project-graph-cache.json +1 -0
- package/src/compact/ai-context.js +7 -0
- package/src/compact/compact-migrate.js +17 -0
- package/src/compact/compact.js +18 -0
- package/src/compact/compress.js +14 -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/web/.project-graph-cache.json +1 -0
- package/web/app.js +17 -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/ai-context.js +0 -113
- package/src/analysis-cache.js +0 -155
- package/src/cli-handlers.js +0 -271
- package/src/cli.js +0 -95
- package/src/compact.js +0 -207
- package/src/complexity.js +0 -237
- package/src/compress.js +0 -319
- package/src/ctx-to-jsdoc.js +0 -514
- package/src/custom-rules.js +0 -584
- package/src/db-analysis.js +0 -194
- package/src/dead-code.js +0 -468
- package/src/doc-dialect.js +0 -716
- package/src/filters.js +0 -227
- package/src/framework-references.js +0 -177
- package/src/full-analysis.js +0 -470
- package/src/graph-builder.js +0 -299
- package/src/instructions.js +0 -73
- package/src/jsdoc-checker.js +0 -351
- package/src/jsdoc-generator.js +0 -203
- 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 -163
- package/src/mcp-server.js +0 -675
- package/src/mode-config.js +0 -127
- package/src/outdated-patterns.js +0 -296
- package/src/parser.js +0 -662
- package/src/server.js +0 -28
- package/src/similar-functions.js +0 -279
- package/src/test-annotations.js +0 -323
- package/src/tool-defs.js +0 -793
- package/src/tools.js +0 -470
- package/src/type-checker.js +0 -188
- package/src/undocumented.js +0 -259
- package/src/workspace.js +0 -70
- /package/{AGENT_ROLE.md → docs/examples/AGENT_ROLE.md} +0 -0
- /package/{AGENT_ROLE_MINIMAL.md → docs/examples/AGENT_ROLE_MINIMAL.md} +0 -0
package/src/compress.js
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Code Compression for AI Context
|
|
3
|
-
*
|
|
4
|
-
* Terser-based minification of JS source files for token-efficient AI consumption.
|
|
5
|
-
* Preserves exported names and structure while stripping comments, whitespace,
|
|
6
|
-
* and redundant syntax. Optionally generates a JSDoc legend header.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { readFileSync } from 'fs';
|
|
10
|
-
import { basename, extname } from 'path';
|
|
11
|
-
import { minify } from '../vendor/terser.mjs';
|
|
12
|
-
import { parse } from '../vendor/acorn.mjs';
|
|
13
|
-
import { simple as walk } from '../vendor/walk.mjs';
|
|
14
|
-
|
|
15
|
-
/** Supported file extensions for compression */
|
|
16
|
-
const SUPPORTED_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.tsx']);
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Estimate token count (rough: ~4 chars per token for code)
|
|
20
|
-
* @param {string} text
|
|
21
|
-
* @returns {number}
|
|
22
|
-
*/
|
|
23
|
-
function estimateTokens(text) {
|
|
24
|
-
return Math.ceil(text.length / 4);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Extract JSDoc legend from source — exported symbols with their descriptions
|
|
29
|
-
* @param {string} source - Original source code
|
|
30
|
-
* @param {string} filePath
|
|
31
|
-
* @returns {string} Compact legend string
|
|
32
|
-
*/
|
|
33
|
-
function extractLegend(source, filePath) {
|
|
34
|
-
const lines = [];
|
|
35
|
-
lines.push(`--- ${basename(filePath)} ---`);
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const ast = parse(source, { ecmaVersion: 2022, sourceType: 'module', locations: true });
|
|
39
|
-
|
|
40
|
-
walk(ast, {
|
|
41
|
-
ExportNamedDeclaration(node) {
|
|
42
|
-
const decl = node.declaration;
|
|
43
|
-
if (!decl) return;
|
|
44
|
-
|
|
45
|
-
// Extract preceding JSDoc comment — first line description only
|
|
46
|
-
let jsdoc = '';
|
|
47
|
-
if (node.start > 0) {
|
|
48
|
-
// Only look at the 500 chars immediately before node to avoid module-level JSDoc
|
|
49
|
-
const searchStart = Math.max(0, node.start - 500);
|
|
50
|
-
const beforeNode = source.slice(searchStart, node.start).trimEnd();
|
|
51
|
-
const jsdocMatch = beforeNode.match(/\/\*\*[\s\S]*?\*\/\s*$/);
|
|
52
|
-
if (jsdocMatch) {
|
|
53
|
-
// Ensure the JSDoc block is close to the declaration (within 3 blank lines)
|
|
54
|
-
const gap = source.slice(searchStart + jsdocMatch.index + jsdocMatch[0].length, node.start);
|
|
55
|
-
if (gap.split('\n').length <= 3) {
|
|
56
|
-
const desc = jsdocMatch[0]
|
|
57
|
-
.replace(/\/\*\*\s*\n?/, '')
|
|
58
|
-
.replace(/\s*\*\//, '')
|
|
59
|
-
.split('\n')
|
|
60
|
-
.map(l => l.replace(/^\s*\*\s?/, '').trim())
|
|
61
|
-
.filter(l => l && !l.startsWith('@'))
|
|
62
|
-
.join(' ')
|
|
63
|
-
.trim();
|
|
64
|
-
if (desc) jsdoc = desc.length > 80 ? desc.slice(0, 77) + '...' : desc;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (decl.type === 'FunctionDeclaration') {
|
|
70
|
-
const name = decl.id?.name || 'anonymous';
|
|
71
|
-
const paramList = decl.params.map(p => {
|
|
72
|
-
if (p.type === 'Identifier') return p.name;
|
|
73
|
-
if (p.type === 'AssignmentPattern' && p.left.type === 'Identifier') return p.left.name + '=';
|
|
74
|
-
return '...';
|
|
75
|
-
}).join(',');
|
|
76
|
-
const line = `${decl.async ? 'async ' : ''}${name}(${paramList})`;
|
|
77
|
-
lines.push(jsdoc ? `${line}|${jsdoc}` : line);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (decl.type === 'ClassDeclaration') {
|
|
81
|
-
const name = decl.id?.name || 'AnonymousClass';
|
|
82
|
-
const ext = decl.superClass ? ` extends ${decl.superClass.name || '?'}` : '';
|
|
83
|
-
lines.push(`class ${name}${ext}${jsdoc ? '|' + jsdoc : ''}`);
|
|
84
|
-
|
|
85
|
-
// List methods
|
|
86
|
-
for (const method of decl.body.body) {
|
|
87
|
-
if (method.type === 'MethodDefinition' && method.key?.name) {
|
|
88
|
-
const mParams = method.value.params.map(p => {
|
|
89
|
-
if (p.type === 'Identifier') return p.name;
|
|
90
|
-
if (p.type === 'AssignmentPattern' && p.left.type === 'Identifier') return p.left.name + '=';
|
|
91
|
-
return '...';
|
|
92
|
-
}).join(',');
|
|
93
|
-
lines.push(` .${method.key.name}(${mParams})`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (decl.type === 'VariableDeclaration') {
|
|
99
|
-
for (const declarator of decl.declarations) {
|
|
100
|
-
if (declarator.id?.name) {
|
|
101
|
-
lines.push(`${decl.kind} ${declarator.id.name}${jsdoc ? '|' + jsdoc : ''}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
} catch (e) {
|
|
108
|
-
lines.push(`PARSE_ERROR: ${e.message}`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return lines.join('\n');
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Compress a source file for AI consumption
|
|
116
|
-
* @param {string} filePath - Path to JS/MJS file
|
|
117
|
-
* @param {Object} [options]
|
|
118
|
-
* @param {boolean} [options.beautify=true] - Readable multi-line output
|
|
119
|
-
* @param {boolean} [options.legend=true] - Add compact legend header
|
|
120
|
-
* @returns {Promise<{code: string, legend: string, original: number, compressed: number, savings: string}>}
|
|
121
|
-
*/
|
|
122
|
-
export async function compressFile(filePath, options = {}) {
|
|
123
|
-
const { beautify = true, legend: includeLegend = true } = options;
|
|
124
|
-
|
|
125
|
-
// Validate file extension
|
|
126
|
-
const ext = extname(filePath).toLowerCase();
|
|
127
|
-
if (!SUPPORTED_EXTENSIONS.has(ext)) {
|
|
128
|
-
throw new Error(`Unsupported file type: ${ext}. Supported: ${[...SUPPORTED_EXTENSIONS].join(', ')}`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const source = readFileSync(filePath, 'utf-8');
|
|
132
|
-
const originalTokens = estimateTokens(source);
|
|
133
|
-
|
|
134
|
-
// Handle empty files
|
|
135
|
-
if (!source.trim()) {
|
|
136
|
-
return {
|
|
137
|
-
code: '',
|
|
138
|
-
legend: '',
|
|
139
|
-
original: 0,
|
|
140
|
-
compressed: 0,
|
|
141
|
-
savings: '0%',
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const terserOptions = {
|
|
146
|
-
compress: {
|
|
147
|
-
dead_code: true,
|
|
148
|
-
drop_console: false,
|
|
149
|
-
passes: 2,
|
|
150
|
-
},
|
|
151
|
-
mangle: false, // Preserve all names for AI readability
|
|
152
|
-
module: true, // Support ES modules
|
|
153
|
-
output: {
|
|
154
|
-
beautify,
|
|
155
|
-
comments: false, // Strip all comments — legend replaces them
|
|
156
|
-
semicolons: !beautify,
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
let compressedSource;
|
|
161
|
-
try {
|
|
162
|
-
const result = await minify(source, terserOptions);
|
|
163
|
-
if (result.error) {
|
|
164
|
-
throw result.error;
|
|
165
|
-
}
|
|
166
|
-
compressedSource = result.code;
|
|
167
|
-
} catch (e) {
|
|
168
|
-
// Graceful fallback: return original code stripped of comments
|
|
169
|
-
compressedSource = source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*/g, '').replace(/\n{3,}/g, '\n\n').trim();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const legend = includeLegend ? extractLegend(source, filePath) : '';
|
|
173
|
-
const compressedCode = legend
|
|
174
|
-
? `/*\n${legend}\n*/\n${compressedSource}`
|
|
175
|
-
: compressedSource;
|
|
176
|
-
|
|
177
|
-
const compressedTokens = estimateTokens(compressedCode);
|
|
178
|
-
const savings = originalTokens > 0
|
|
179
|
-
? Math.round((1 - compressedTokens / originalTokens) * 100)
|
|
180
|
-
: 0;
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
code: compressedCode,
|
|
184
|
-
legend,
|
|
185
|
-
original: originalTokens,
|
|
186
|
-
compressed: compressedTokens,
|
|
187
|
-
savings: `${savings}%`,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Edit a function/class in a source file by symbol name.
|
|
193
|
-
* Agent sends new code (compressed or full); server replaces in the original file.
|
|
194
|
-
* Supports: replace entire function, replace function body only, or add new function.
|
|
195
|
-
*
|
|
196
|
-
* @param {string} filePath - Path to JS/MJS file
|
|
197
|
-
* @param {string} symbol - Function or class name to edit
|
|
198
|
-
* @param {string} newCode - New code for the symbol (full function/class definition)
|
|
199
|
-
* @param {Object} [options]
|
|
200
|
-
* @param {boolean} [options.beautify=true] - Beautify the result after editing
|
|
201
|
-
* @param {boolean} [options.dryRun=false] - Preview without writing
|
|
202
|
-
* @returns {Promise<{success: boolean, file: string, symbol: string, oldRange: {start: number, end: number}, newLength: number, dryRun?: boolean}>}
|
|
203
|
-
*/
|
|
204
|
-
export async function editCompressed(filePath, symbol, newCode, options = {}) {
|
|
205
|
-
const { beautify: shouldBeautify = true, dryRun = false } = options;
|
|
206
|
-
|
|
207
|
-
const source = readFileSync(filePath, 'utf-8');
|
|
208
|
-
|
|
209
|
-
// Parse AST to find the symbol
|
|
210
|
-
let ast;
|
|
211
|
-
try {
|
|
212
|
-
ast = parse(source, {
|
|
213
|
-
ecmaVersion: 'latest',
|
|
214
|
-
sourceType: 'module',
|
|
215
|
-
locations: true,
|
|
216
|
-
});
|
|
217
|
-
} catch (e) {
|
|
218
|
-
throw new Error(`Failed to parse ${filePath}: ${e.message}`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Find the symbol (function or class) and its range
|
|
222
|
-
const match = findSymbolRange(ast, source, symbol);
|
|
223
|
-
if (!match) {
|
|
224
|
-
throw new Error(`Symbol "${symbol}" not found in ${filePath}`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Build new source: before + newCode + after
|
|
228
|
-
const before = source.slice(0, match.start);
|
|
229
|
-
const after = source.slice(match.end);
|
|
230
|
-
let newSource = before + newCode + after;
|
|
231
|
-
|
|
232
|
-
// Optionally beautify the result
|
|
233
|
-
if (shouldBeautify) {
|
|
234
|
-
try {
|
|
235
|
-
const result = await minify(newSource, {
|
|
236
|
-
compress: false,
|
|
237
|
-
mangle: false,
|
|
238
|
-
module: true,
|
|
239
|
-
output: { beautify: true, comments: true, semicolons: false },
|
|
240
|
-
});
|
|
241
|
-
if (result.code) {
|
|
242
|
-
newSource = result.code;
|
|
243
|
-
}
|
|
244
|
-
} catch {
|
|
245
|
-
// If beautify fails, use raw replacement
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Validate the new source parses correctly
|
|
250
|
-
try {
|
|
251
|
-
parse(newSource, { ecmaVersion: 'latest', sourceType: 'module' });
|
|
252
|
-
} catch (e) {
|
|
253
|
-
throw new Error(`Edit would create invalid syntax: ${e.message}`);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (!dryRun) {
|
|
257
|
-
const { writeFileSync } = await import('fs');
|
|
258
|
-
writeFileSync(filePath, newSource, 'utf-8');
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return {
|
|
262
|
-
success: true,
|
|
263
|
-
file: filePath,
|
|
264
|
-
symbol,
|
|
265
|
-
oldRange: { start: match.start, end: match.end },
|
|
266
|
-
newLength: newCode.length,
|
|
267
|
-
...(dryRun ? { dryRun: true } : {}),
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Find the character range of a symbol (function or class) in source.
|
|
273
|
-
* Handles: FunctionDeclaration, ExportNamedDeclaration wrapping functions,
|
|
274
|
-
* ClassDeclaration, variable-assigned functions (const foo = ...).
|
|
275
|
-
*
|
|
276
|
-
* @param {Object} ast - Acorn AST
|
|
277
|
-
* @param {string} source - Full source code
|
|
278
|
-
* @param {string} symbol - Symbol name to find
|
|
279
|
-
* @returns {{start: number, end: number, type: string}|null}
|
|
280
|
-
*/
|
|
281
|
-
function findSymbolRange(ast, source, symbol) {
|
|
282
|
-
let match = null;
|
|
283
|
-
|
|
284
|
-
walk(ast, {
|
|
285
|
-
FunctionDeclaration(node) {
|
|
286
|
-
if (node.id?.name === symbol) {
|
|
287
|
-
match = { start: node.start, end: node.end, type: 'FunctionDeclaration' };
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
ClassDeclaration(node) {
|
|
291
|
-
if (node.id?.name === symbol) {
|
|
292
|
-
match = { start: node.start, end: node.end, type: 'ClassDeclaration' };
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
VariableDeclaration(node) {
|
|
296
|
-
for (const decl of node.declarations) {
|
|
297
|
-
if (decl.id?.name === symbol) {
|
|
298
|
-
match = { start: node.start, end: node.end, type: 'VariableDeclaration' };
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
ExportNamedDeclaration(node) {
|
|
303
|
-
if (node.declaration) {
|
|
304
|
-
const decl = node.declaration;
|
|
305
|
-
const name = decl.id?.name || decl.declarations?.[0]?.id?.name;
|
|
306
|
-
if (name === symbol) {
|
|
307
|
-
match = { start: node.start, end: node.end, type: 'ExportNamedDeclaration' };
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
ExportDefaultDeclaration(node) {
|
|
312
|
-
if (node.declaration?.id?.name === symbol) {
|
|
313
|
-
match = { start: node.start, end: node.end, type: 'ExportDefaultDeclaration' };
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
return match;
|
|
319
|
-
}
|