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.
Files changed (125) hide show
  1. package/README.md +171 -31
  2. package/docs/img/explorer-compact.jpg +0 -0
  3. package/docs/img/explorer-expanded.jpg +0 -0
  4. package/package.json +12 -8
  5. package/src/.project-graph-cache.json +1 -1
  6. package/src/analysis/analysis-cache.js +7 -0
  7. package/src/analysis/complexity.js +14 -0
  8. package/src/analysis/custom-rules.js +36 -0
  9. package/src/analysis/db-analysis.js +9 -0
  10. package/src/analysis/dead-code.js +19 -0
  11. package/src/analysis/full-analysis.js +18 -0
  12. package/src/analysis/jsdoc-checker.js +24 -0
  13. package/src/analysis/jsdoc-generator.js +10 -0
  14. package/src/analysis/large-files.js +11 -0
  15. package/src/analysis/outdated-patterns.js +12 -0
  16. package/src/analysis/similar-functions.js +16 -0
  17. package/src/analysis/test-annotations.js +21 -0
  18. package/src/analysis/type-checker.js +8 -0
  19. package/src/analysis/undocumented.js +14 -0
  20. package/src/cli/cli-handlers.js +4 -0
  21. package/src/cli/cli.js +5 -0
  22. package/src/compact/.project-graph-cache.json +1 -0
  23. package/src/compact/ai-context.js +7 -0
  24. package/src/compact/compact-migrate.js +17 -0
  25. package/src/compact/compact.js +18 -0
  26. package/src/compact/compress.js +14 -0
  27. package/src/compact/ctx-to-jsdoc.js +29 -0
  28. package/src/compact/doc-dialect.js +30 -0
  29. package/src/compact/expand.js +37 -0
  30. package/src/compact/framework-references.js +5 -0
  31. package/src/compact/instructions.js +3 -0
  32. package/src/compact/mode-config.js +8 -0
  33. package/src/compact/validate-pipeline.js +9 -0
  34. package/src/core/event-bus.js +9 -0
  35. package/src/core/filters.js +14 -0
  36. package/src/core/graph-builder.js +12 -0
  37. package/src/core/parser.js +31 -0
  38. package/src/core/workspace.js +8 -0
  39. package/src/lang/lang-go.js +17 -0
  40. package/src/lang/lang-python.js +12 -0
  41. package/src/lang/lang-sql.js +23 -0
  42. package/src/lang/lang-typescript.js +9 -0
  43. package/src/lang/lang-utils.js +4 -0
  44. package/src/mcp/mcp-server.js +17 -0
  45. package/src/mcp/tool-defs.js +3 -0
  46. package/src/mcp/tools.js +25 -0
  47. package/src/network/backend-lifecycle.js +19 -0
  48. package/src/network/backend.js +5 -0
  49. package/src/network/local-gateway.js +23 -0
  50. package/src/network/mdns.js +13 -0
  51. package/src/network/server.js +10 -0
  52. package/src/network/web-server.js +34 -0
  53. package/web/.project-graph-cache.json +1 -0
  54. package/web/app.js +17 -0
  55. package/web/components/code-block.js +3 -0
  56. package/web/components/quick-open.js +5 -0
  57. package/web/dashboard-state.js +3 -0
  58. package/web/dashboard.html +27 -0
  59. package/web/dashboard.js +8 -0
  60. package/web/highlight.js +13 -0
  61. package/web/index.html +35 -0
  62. package/web/panels/ActionBoard/ActionBoard.css.js +1 -0
  63. package/web/panels/ActionBoard/ActionBoard.js +4 -0
  64. package/web/panels/ActionBoard/ActionBoard.tpl.js +1 -0
  65. package/web/panels/EventItem/EventItem.css.js +1 -0
  66. package/web/panels/EventItem/EventItem.js +4 -0
  67. package/web/panels/EventItem/EventItem.tpl.js +1 -0
  68. package/web/panels/ProjectItem/ProjectItem.css.js +1 -0
  69. package/web/panels/ProjectItem/ProjectItem.js +5 -0
  70. package/web/panels/ProjectItem/ProjectItem.tpl.js +1 -0
  71. package/web/panels/ProjectList/ProjectList.css.js +1 -0
  72. package/web/panels/ProjectList/ProjectList.js +4 -0
  73. package/web/panels/ProjectList/ProjectList.tpl.js +1 -0
  74. package/web/panels/SettingsPanel/.project-graph-cache.json +1 -0
  75. package/web/panels/SettingsPanel/SettingsPanel.css.js +1 -0
  76. package/web/panels/SettingsPanel/SettingsPanel.js +7 -0
  77. package/web/panels/SettingsPanel/SettingsPanel.tpl.js +1 -0
  78. package/web/panels/code-viewer.js +5 -0
  79. package/web/panels/ctx-panel.js +4 -0
  80. package/web/panels/dep-graph.js +6 -0
  81. package/web/panels/file-tree.js +188 -0
  82. package/web/panels/health-panel.js +3 -0
  83. package/web/panels/live-monitor.js +3 -0
  84. package/web/state.js +17 -0
  85. package/web/style.css +157 -0
  86. package/references/symbiote-3x.md +0 -834
  87. package/src/ai-context.js +0 -113
  88. package/src/analysis-cache.js +0 -155
  89. package/src/cli-handlers.js +0 -271
  90. package/src/cli.js +0 -95
  91. package/src/compact.js +0 -207
  92. package/src/complexity.js +0 -237
  93. package/src/compress.js +0 -319
  94. package/src/ctx-to-jsdoc.js +0 -514
  95. package/src/custom-rules.js +0 -584
  96. package/src/db-analysis.js +0 -194
  97. package/src/dead-code.js +0 -468
  98. package/src/doc-dialect.js +0 -716
  99. package/src/filters.js +0 -227
  100. package/src/framework-references.js +0 -177
  101. package/src/full-analysis.js +0 -470
  102. package/src/graph-builder.js +0 -299
  103. package/src/instructions.js +0 -73
  104. package/src/jsdoc-checker.js +0 -351
  105. package/src/jsdoc-generator.js +0 -203
  106. package/src/lang-go.js +0 -285
  107. package/src/lang-python.js +0 -197
  108. package/src/lang-sql.js +0 -309
  109. package/src/lang-typescript.js +0 -190
  110. package/src/lang-utils.js +0 -124
  111. package/src/large-files.js +0 -163
  112. package/src/mcp-server.js +0 -675
  113. package/src/mode-config.js +0 -127
  114. package/src/outdated-patterns.js +0 -296
  115. package/src/parser.js +0 -662
  116. package/src/server.js +0 -28
  117. package/src/similar-functions.js +0 -279
  118. package/src/test-annotations.js +0 -323
  119. package/src/tool-defs.js +0 -793
  120. package/src/tools.js +0 -470
  121. package/src/type-checker.js +0 -188
  122. package/src/undocumented.js +0 -259
  123. package/src/workspace.js +0 -70
  124. /package/{AGENT_ROLE.md → docs/examples/AGENT_ROLE.md} +0 -0
  125. /package/{AGENT_ROLE_MINIMAL.md → docs/examples/AGENT_ROLE_MINIMAL.md} +0 -0
@@ -1,127 +0,0 @@
1
- /**
2
- * Compact Code Mode Configuration
3
- *
4
- * Manages project-level mode selection for the compact code architecture.
5
- * Reads/writes `.context/config.json` to configure how agents interact with code.
6
- *
7
- * Modes:
8
- * 1 = Native Compact: code stored minified, agent edits directly
9
- * 2 = Full Storage: code stored formatted, agent reads compressed view, edits via edit_compressed
10
- * 3 = Future (IDE): compact storage with IDE virtual display (reserved)
11
- */
12
-
13
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
14
- import { join, dirname } from 'path';
15
-
16
- const CONFIG_FILE = '.context/config.json';
17
-
18
- /** Default configuration */
19
- const DEFAULTS = {
20
- mode: 2,
21
- beautify: true,
22
- autoValidate: false,
23
- stripJSDoc: false,
24
- };
25
-
26
- /**
27
- * Read project mode configuration from .context/config.json
28
- * Returns defaults if file doesn't exist.
29
- *
30
- * @param {string} projectDir - Project root directory
31
- * @returns {{ mode: number, beautify: boolean, autoValidate: boolean, stripJSDoc: boolean }}
32
- */
33
- export function getConfig(projectDir) {
34
- const configPath = join(projectDir, CONFIG_FILE);
35
-
36
- if (!existsSync(configPath)) {
37
- return { ...DEFAULTS };
38
- }
39
-
40
- try {
41
- const raw = readFileSync(configPath, 'utf-8');
42
- const parsed = JSON.parse(raw);
43
- return { ...DEFAULTS, ...parsed };
44
- } catch {
45
- return { ...DEFAULTS };
46
- }
47
- }
48
-
49
- /**
50
- * Write project mode configuration to .context/config.json
51
- *
52
- * @param {string} projectDir - Project root directory
53
- * @param {Object} config - Configuration to save (merged with existing)
54
- * @returns {{ saved: boolean, path: string, config: Object }}
55
- */
56
- export function setConfig(projectDir, config) {
57
- const configPath = join(projectDir, CONFIG_FILE);
58
- const dir = dirname(configPath);
59
-
60
- if (!existsSync(dir)) {
61
- mkdirSync(dir, { recursive: true });
62
- }
63
-
64
- // Merge with existing
65
- const existing = getConfig(projectDir);
66
- const merged = { ...existing, ...config };
67
-
68
- // Validate mode
69
- if (![1, 2, 3].includes(merged.mode)) {
70
- throw new Error(`Invalid mode: ${merged.mode}. Valid: 1 (compact), 2 (full), 3 (IDE)`);
71
- }
72
-
73
- writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
74
-
75
- return {
76
- saved: true,
77
- path: configPath,
78
- config: merged,
79
- };
80
- }
81
-
82
- /**
83
- * Get human-readable description of current mode
84
- * @param {number} mode
85
- * @returns {string}
86
- */
87
- export function getModeDescription(mode) {
88
- switch (mode) {
89
- case 1: return 'Native Compact — code stored minified, agent edits directly';
90
- case 2: return 'Full Storage — code stored formatted, agent uses get_compressed_file + edit_compressed';
91
- case 3: return 'IDE Virtual — compact storage with IDE virtual display (future)';
92
- default: return `Unknown mode: ${mode}`;
93
- }
94
- }
95
-
96
- /**
97
- * Get recommended workflow for current mode
98
- * @param {number} mode
99
- * @returns {{ read: string, edit: string, docs: string, validate: string }}
100
- */
101
- export function getModeWorkflow(mode) {
102
- switch (mode) {
103
- case 1:
104
- return {
105
- read: 'Read .js files directly (already compact)',
106
- edit: 'Edit .js files directly',
107
- docs: 'Read .ctx files for types and descriptions',
108
- validate: 'Run validate-ctx to check .ctx ↔ AST consistency',
109
- };
110
- case 2:
111
- return {
112
- read: 'Use get_compressed_file for token-efficient reading',
113
- edit: 'Use edit_compressed(path, symbol, code) for AST-safe editing',
114
- docs: 'Read .ctx files for types and descriptions',
115
- validate: 'Run validate-ctx to check .ctx ↔ AST consistency',
116
- };
117
- case 3:
118
- return {
119
- read: 'IDE renders full view from compact storage',
120
- edit: 'IDE handles bidirectional mapping',
121
- docs: 'Managed by IDE plugin',
122
- validate: 'Automatic via IDE integration',
123
- };
124
- default:
125
- return { read: 'N/A', edit: 'N/A', docs: 'N/A', validate: 'N/A' };
126
- }
127
- }
@@ -1,296 +0,0 @@
1
- /**
2
- * Outdated Patterns Detector
3
- * Finds legacy code patterns and redundant npm dependencies
4
- */
5
-
6
- import { readFileSync, readdirSync, statSync, existsSync } 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
-
12
- /**
13
- * Redundant npm packages that are now built into Node.js 18+
14
- */
15
- const REDUNDANT_DEPS = {
16
- 'node-fetch': { replacement: 'fetch()', since: 'Node 18' },
17
- 'cross-fetch': { replacement: 'fetch()', since: 'Node 18' },
18
- 'isomorphic-fetch': { replacement: 'fetch()', since: 'Node 18' },
19
- 'uuid': { replacement: 'crypto.randomUUID()', since: 'Node 19' },
20
- 'deep-clone': { replacement: 'structuredClone()', since: 'Node 17' },
21
- 'lodash.clonedeep': { replacement: 'structuredClone()', since: 'Node 17' },
22
- 'abort-controller': { replacement: 'AbortController (global)', since: 'Node 15' },
23
- 'form-data': { replacement: 'FormData (global)', since: 'Node 18' },
24
- 'web-streams-polyfill': { replacement: 'ReadableStream (global)', since: 'Node 18' },
25
- 'url-parse': { replacement: 'URL (global)', since: 'Node 10' },
26
- 'querystring': { replacement: 'URLSearchParams', since: 'Node 10' },
27
- 'rimraf': { replacement: 'fs.rm({ recursive: true })', since: 'Node 14' },
28
- 'mkdirp': { replacement: 'fs.mkdir({ recursive: true })', since: 'Node 10' },
29
- 'recursive-readdir': { replacement: 'fs.readdir({ recursive: true })', since: 'Node 20' },
30
- 'glob': { replacement: 'fs.glob()', since: 'Node 22' },
31
- };
32
-
33
- /**
34
- * Legacy code patterns to detect
35
- */
36
- const CODE_PATTERNS = [
37
- {
38
- name: 'var-usage',
39
- description: 'Use const/let instead of var',
40
- check: (node) => node.type === 'VariableDeclaration' && node.kind === 'var',
41
- severity: 'warning',
42
- replacement: 'const/let',
43
- },
44
- {
45
- name: 'require-usage',
46
- description: 'Use ESM import instead of require()',
47
- check: (node) => node.type === 'CallExpression' &&
48
- node.callee.type === 'Identifier' && node.callee.name === 'require',
49
- severity: 'info',
50
- replacement: 'import ... from',
51
- },
52
- {
53
- name: 'module-exports',
54
- description: 'Use ESM export instead of module.exports',
55
- check: (node) => node.type === 'AssignmentExpression' &&
56
- node.left.type === 'MemberExpression' &&
57
- node.left.object.type === 'Identifier' && node.left.object.name === 'module' &&
58
- node.left.property.type === 'Identifier' && node.left.property.name === 'exports',
59
- severity: 'info',
60
- replacement: 'export default/export',
61
- },
62
- {
63
- name: 'buffer-constructor',
64
- description: 'new Buffer() is deprecated',
65
- check: (node) => node.type === 'NewExpression' &&
66
- node.callee.type === 'Identifier' && node.callee.name === 'Buffer',
67
- severity: 'error',
68
- replacement: 'Buffer.from() / Buffer.alloc()',
69
- },
70
- {
71
- name: 'arguments-usage',
72
- description: 'Use rest parameters instead of arguments',
73
- check: (node) => node.type === 'Identifier' && node.name === 'arguments',
74
- severity: 'warning',
75
- replacement: '...args',
76
- },
77
- {
78
- name: 'promisify-usage',
79
- description: 'Use fs/promises instead of util.promisify',
80
- check: (node) => node.type === 'CallExpression' &&
81
- node.callee.type === 'MemberExpression' &&
82
- node.callee.object.type === 'Identifier' && node.callee.object.name === 'util' &&
83
- node.callee.property.type === 'Identifier' && node.callee.property.name === 'promisify',
84
- severity: 'info',
85
- replacement: 'fs/promises module',
86
- },
87
- {
88
- name: 'sync-in-async',
89
- description: 'Avoid sync methods in async context (readFileSync, etc.)',
90
- check: (node, context) => {
91
- if (node.type !== 'CallExpression') return false;
92
- const callee = node.callee;
93
- if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
94
- const name = callee.property.name;
95
- return name.endsWith('Sync') && context.inAsync;
96
- }
97
- return false;
98
- },
99
- severity: 'warning',
100
- replacement: 'async fs/promises methods',
101
- },
102
- ];
103
-
104
- /**
105
- * @typedef {Object} PatternMatch
106
- * @property {string} pattern
107
- * @property {string} description
108
- * @property {string} file
109
- * @property {number} line
110
- * @property {string} severity
111
- * @property {string} replacement
112
- */
113
-
114
- /**
115
- * @typedef {Object} RedundantDep
116
- * @property {string} name
117
- * @property {string} replacement
118
- * @property {string} since
119
- */
120
-
121
- /**
122
- * Find all JS files
123
- * @param {string} dir
124
- * @param {string} rootDir
125
- * @returns {string[]}
126
- */
127
- function findJSFiles(dir, rootDir = dir) {
128
- if (dir === rootDir) parseGitignore(rootDir);
129
- const files = [];
130
-
131
- try {
132
- for (const entry of readdirSync(dir)) {
133
- const fullPath = join(dir, entry);
134
- const relativePath = relative(rootDir, fullPath);
135
- const stat = statSync(fullPath);
136
-
137
- if (stat.isDirectory()) {
138
- if (!shouldExcludeDir(entry, relativePath)) {
139
- files.push(...findJSFiles(fullPath, rootDir));
140
- }
141
- } else if (entry.endsWith('.js') && !entry.endsWith('.css.js') && !entry.endsWith('.tpl.js')) {
142
- if (!shouldExcludeFile(entry, relativePath)) {
143
- files.push(fullPath);
144
- }
145
- }
146
- }
147
- } catch (e) { }
148
-
149
- return files;
150
- }
151
-
152
- /**
153
- * Analyze file for outdated patterns
154
- * @param {string} filePath
155
- * @param {string} rootDir - Root directory for relative path calculation
156
- * @returns {PatternMatch[]}
157
- */
158
- function analyzeFilePatterns(filePath, rootDir) {
159
- const code = readFileSync(filePath, 'utf-8');
160
- const relPath = relative(rootDir, filePath);
161
- const matches = [];
162
-
163
- let ast;
164
- try {
165
- ast = parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
166
- } catch (e) {
167
- return matches;
168
- }
169
-
170
- // Track async context
171
- const context = { inAsync: false };
172
-
173
- walk.simple(ast, {
174
- FunctionDeclaration(node) {
175
- context.inAsync = node.async;
176
- },
177
- ArrowFunctionExpression(node) {
178
- context.inAsync = node.async;
179
- },
180
- });
181
-
182
- // Reset and check patterns
183
- context.inAsync = false;
184
-
185
- walk.ancestor(ast, {
186
- '*'(node, ancestors) {
187
- // Update async context
188
- for (const anc of ancestors) {
189
- if ((anc.type === 'FunctionDeclaration' || anc.type === 'ArrowFunctionExpression' ||
190
- anc.type === 'FunctionExpression') && anc.async) {
191
- context.inAsync = true;
192
- break;
193
- }
194
- }
195
-
196
- for (const pattern of CODE_PATTERNS) {
197
- if (pattern.check(node, context)) {
198
- matches.push({
199
- pattern: pattern.name,
200
- description: pattern.description,
201
- file: relPath,
202
- line: node.loc?.start?.line || 0,
203
- severity: pattern.severity,
204
- replacement: pattern.replacement,
205
- });
206
- }
207
- }
208
- },
209
- });
210
-
211
- return matches;
212
- }
213
-
214
- /**
215
- * Analyze package.json for redundant dependencies
216
- * @param {string} dir
217
- * @returns {RedundantDep[]}
218
- */
219
- function analyzePackageJson(dir) {
220
- const pkgPath = join(dir, 'package.json');
221
- const redundant = [];
222
-
223
- if (!existsSync(pkgPath)) return redundant;
224
-
225
- try {
226
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
227
- const allDeps = {
228
- ...pkg.dependencies,
229
- ...pkg.devDependencies,
230
- };
231
-
232
- for (const depName of Object.keys(allDeps)) {
233
- if (REDUNDANT_DEPS[depName]) {
234
- redundant.push({
235
- name: depName,
236
- ...REDUNDANT_DEPS[depName],
237
- });
238
- }
239
- }
240
- } catch (e) { }
241
-
242
- return redundant;
243
- }
244
-
245
- /**
246
- * Get outdated patterns analysis
247
- * @param {string} dir
248
- * @param {Object} [options]
249
- * @param {boolean} [options.codeOnly=false] - Only check code patterns
250
- * @param {boolean} [options.depsOnly=false] - Only check dependencies
251
- * @returns {Promise<{codePatterns: PatternMatch[], redundantDeps: RedundantDep[], stats: Object}>}
252
- */
253
- export async function getOutdatedPatterns(dir, options = {}) {
254
- const codeOnly = options.codeOnly || false;
255
- const depsOnly = options.depsOnly || false;
256
- const resolvedDir = resolve(dir);
257
-
258
- let codePatterns = [];
259
- let redundantDeps = [];
260
-
261
- if (!depsOnly) {
262
- const files = findJSFiles(dir);
263
- for (const file of files) {
264
- codePatterns.push(...analyzeFilePatterns(file, resolvedDir));
265
- }
266
- // Sort by severity
267
- const severityOrder = { error: 0, warning: 1, info: 2 };
268
- codePatterns.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
269
- }
270
-
271
- if (!codeOnly) {
272
- redundantDeps = analyzePackageJson(dir);
273
- }
274
-
275
- const stats = {
276
- totalPatterns: codePatterns.length,
277
- byPattern: {},
278
- bySeverity: {
279
- error: codePatterns.filter(p => p.severity === 'error').length,
280
- warning: codePatterns.filter(p => p.severity === 'warning').length,
281
- info: codePatterns.filter(p => p.severity === 'info').length,
282
- },
283
- redundantDeps: redundantDeps.length,
284
- };
285
-
286
- // Group by pattern name
287
- for (const p of codePatterns) {
288
- stats.byPattern[p.pattern] = (stats.byPattern[p.pattern] || 0) + 1;
289
- }
290
-
291
- return {
292
- codePatterns: codePatterns.slice(0, 50),
293
- redundantDeps,
294
- stats,
295
- };
296
- }