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,259 +0,0 @@
1
- /**
2
- * Undocumented Code Finder (AST-based)
3
- * Finds methods/functions missing JSDoc annotations using Acorn AST parser
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
-
12
- /**
13
- * @typedef {Object} UndocumentedItem
14
- * @property {string} name - ClassName.methodName or functionName
15
- * @property {string} type - 'method' | 'function' | 'class'
16
- * @property {string} file
17
- * @property {number} line
18
- * @property {string} reason - What's missing
19
- */
20
-
21
- /**
22
- * Find all JS files in directory
23
- * @param {string} dir
24
- * @param {string} rootDir
25
- * @returns {string[]}
26
- */
27
- function findJSFiles(dir, rootDir = dir) {
28
- if (dir === rootDir) {
29
- parseGitignore(rootDir);
30
- }
31
-
32
- const files = [];
33
-
34
- try {
35
- for (const entry of readdirSync(dir)) {
36
- const fullPath = join(dir, entry);
37
- const relativePath = relative(rootDir, fullPath);
38
- const stat = statSync(fullPath);
39
-
40
- if (stat.isDirectory()) {
41
- if (!shouldExcludeDir(entry, relativePath)) {
42
- files.push(...findJSFiles(fullPath, rootDir));
43
- }
44
- } else if (entry.endsWith('.js') && !entry.endsWith('.css.js') && !entry.endsWith('.tpl.js')) {
45
- if (!shouldExcludeFile(entry, relativePath)) {
46
- files.push(fullPath);
47
- }
48
- }
49
- }
50
- } catch (e) {
51
- // Directory not found
52
- }
53
-
54
- return files;
55
- }
56
-
57
- /**
58
- * Extract JSDoc comments from code with their positions
59
- * @param {string} code
60
- * @returns {Array<{text: string, endLine: number}>}
61
- */
62
- function extractComments(code) {
63
- const comments = [];
64
- const regex = /\/\*\*[\s\S]*?\*\//g;
65
- let match;
66
-
67
- while ((match = regex.exec(code)) !== null) {
68
- const endLine = code.slice(0, match.index + match[0].length).split('\n').length;
69
- comments.push({ text: match[0], endLine });
70
- }
71
-
72
- return comments;
73
- }
74
-
75
- /**
76
- * Find JSDoc comment before a target line
77
- * @param {Array<{text: string, endLine: number}>} comments - Extracted JSDoc comments
78
- * @param {number} targetLine - Line number to search before
79
- * @returns {string|null}
80
- */
81
- function findJSDocBefore(comments, targetLine) {
82
- for (const comment of comments) {
83
- const gap = targetLine - comment.endLine;
84
- if (gap >= 0 && gap <= 2) {
85
- return comment.text;
86
- }
87
- }
88
- return null;
89
- }
90
-
91
- /**
92
- * Check what's missing from JSDoc based on level
93
- * @param {string|null} jsdoc
94
- * @param {'tests'|'params'|'all'} level
95
- * @returns {string[]}
96
- */
97
- function checkMissing(jsdoc, level) {
98
- const missing = [];
99
-
100
- if (!jsdoc) {
101
- if (level === 'all') missing.push('description');
102
- if (level === 'params' || level === 'all') missing.push('@param', '@returns');
103
- return missing;
104
- }
105
-
106
- if (level === 'params' || level === 'all') {
107
- if (!jsdoc.includes('@param')) missing.push('@param');
108
- if (!jsdoc.includes('@returns') && !jsdoc.includes('@return')) missing.push('@returns');
109
- }
110
-
111
- return missing;
112
- }
113
-
114
- /** Skip list for methods */
115
- const SKIP_METHODS = [
116
- 'constructor', 'connectedCallback', 'disconnectedCallback',
117
- 'attributeChangedCallback', 'renderCallback',
118
- ];
119
-
120
- /**
121
- * Parse file using AST and find undocumented items (per-file export for cache integration)
122
- * @param {string} code
123
- * @param {string} filePath
124
- * @param {'tests'|'params'|'all'} level
125
- * @returns {UndocumentedItem[]}
126
- */
127
- export function checkUndocumentedFile(code, filePath, level) {
128
- const results = [];
129
-
130
- let ast;
131
- try {
132
- ast = parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
133
- } catch (e) {
134
- return results;
135
- }
136
-
137
- const comments = extractComments(code);
138
-
139
- walk.simple(ast, {
140
- ClassDeclaration(node) {
141
- const className = node.id?.name || 'Anonymous';
142
-
143
- // Check class itself (only for 'all' level)
144
- if (level === 'all') {
145
- const classJsdoc = findJSDocBefore(comments, node.loc.start.line);
146
- if (!classJsdoc) {
147
- results.push({
148
- name: className,
149
- type: 'class',
150
- file: filePath,
151
- line: node.loc.start.line,
152
- reason: 'No JSDoc',
153
- });
154
- }
155
- }
156
-
157
- // Check methods
158
- for (const element of node.body.body) {
159
- if (element.type === 'MethodDefinition') {
160
- const methodName = element.key.name || element.key.value;
161
-
162
- // Skip: constructor, private, getters/setters, lifecycle
163
- if (element.kind === 'get' || element.kind === 'set') continue;
164
- if (methodName?.startsWith('_')) continue;
165
- if (SKIP_METHODS.includes(methodName)) continue;
166
-
167
- const jsdoc = findJSDocBefore(comments, element.loc.start.line);
168
- const missing = checkMissing(jsdoc, level);
169
-
170
- if (missing.length > 0) {
171
- results.push({
172
- name: `${className}.${methodName}`,
173
- type: 'method',
174
- file: filePath,
175
- line: element.loc.start.line,
176
- reason: missing.join(', '),
177
- });
178
- }
179
- }
180
- }
181
- },
182
-
183
- FunctionDeclaration(node) {
184
- if (!node.id) return;
185
- const funcName = node.id.name;
186
-
187
- // Skip private functions
188
- if (funcName.startsWith('_')) return;
189
-
190
- const jsdoc = findJSDocBefore(comments, node.loc.start.line);
191
- const missing = checkMissing(jsdoc, level);
192
-
193
- if (missing.length > 0) {
194
- results.push({
195
- name: funcName,
196
- type: 'function',
197
- file: filePath,
198
- line: node.loc.start.line,
199
- reason: missing.join(', '),
200
- });
201
- }
202
- },
203
- });
204
-
205
- return results;
206
- }
207
-
208
- /**
209
- * Get undocumented items from directory
210
- * @param {string} dir
211
- * @param {'tests'|'params'|'all'} level
212
- * @returns {UndocumentedItem[]}
213
- */
214
- export function getUndocumented(dir, level = 'tests') {
215
- const resolvedDir = resolve(dir);
216
- const files = findJSFiles(dir);
217
- const results = [];
218
-
219
- for (const file of files) {
220
- let content;
221
- try {
222
- content = readFileSync(file, 'utf-8');
223
- } catch (e) {
224
- continue; // File deleted between findJSFiles and read
225
- }
226
- const items = checkUndocumentedFile(content, relative(resolvedDir, file), level);
227
- results.push(...items);
228
- }
229
-
230
- return results;
231
- }
232
-
233
- /**
234
- * Get summary of undocumented items
235
- * @param {string} dir
236
- * @param {'tests'|'params'|'all'} level
237
- * @returns {Object}
238
- */
239
- export function getUndocumentedSummary(dir, level = 'tests') {
240
- const items = getUndocumented(dir, level);
241
-
242
- const byType = {
243
- class: items.filter(i => i.type === 'class').length,
244
- function: items.filter(i => i.type === 'function').length,
245
- method: items.filter(i => i.type === 'method').length,
246
- };
247
-
248
- const byReason = {};
249
- for (const item of items) {
250
- byReason[item.reason] = (byReason[item.reason] || 0) + 1;
251
- }
252
-
253
- return {
254
- total: items.length,
255
- byType,
256
- byReason,
257
- items: items.slice(0, 20),
258
- };
259
- }
package/src/workspace.js DELETED
@@ -1,70 +0,0 @@
1
- /**
2
- * Workspace Root Resolution
3
- *
4
- * Resolves relative paths against the correct workspace root.
5
- * Priority: MCP initialize roots → --workspace arg → PROJECT_ROOT env → process.cwd()
6
- */
7
-
8
- import { resolve, isAbsolute } from 'path';
9
-
10
- /** @type {string|null} */
11
- let workspaceRoot = null;
12
-
13
- // Auto-detect --workspace arg at module load
14
- const wsArg = process.argv.find(a => a.startsWith('--workspace='));
15
- if (wsArg) {
16
- workspaceRoot = wsArg.split('=')[1];
17
- console.error(`[project-graph] Workspace from arg: ${workspaceRoot}`);
18
- }
19
-
20
- /**
21
- * Set workspace root from MCP initialize roots
22
- * @param {Array<{uri: string, name?: string}>} roots
23
- */
24
- export function setRoots(roots) {
25
- if (roots && roots.length > 0) {
26
- let uri = roots[0].uri;
27
- // Strip file:// protocol if present
28
- if (uri.startsWith('file://')) {
29
- uri = uri.slice(7);
30
- }
31
- workspaceRoot = uri;
32
- console.error(`[project-graph] Workspace root: ${workspaceRoot}`);
33
- }
34
- }
35
-
36
- /**
37
- * Get current workspace root
38
- * @returns {string}
39
- */
40
- export function getWorkspaceRoot() {
41
- if (workspaceRoot) {
42
- return workspaceRoot;
43
- }
44
- if (process.env.PROJECT_ROOT) {
45
- return process.env.PROJECT_ROOT;
46
- }
47
- return process.cwd();
48
- }
49
-
50
- /**
51
- * Resolve a path argument against workspace root.
52
- * Absolute paths are returned as-is.
53
- * Relative paths are resolved against the workspace root.
54
- * @param {string} inputPath
55
- * @returns {string}
56
- */
57
- export function resolvePath(inputPath) {
58
- if (!inputPath) {
59
- return getWorkspaceRoot();
60
- }
61
- const root = getWorkspaceRoot();
62
- const resolved = isAbsolute(inputPath) ? inputPath : resolve(root, inputPath);
63
-
64
- // Prevent path traversal — resolved path must stay within workspace
65
- if (!resolved.startsWith(root)) {
66
- throw new Error(`Path traversal blocked: '${inputPath}' resolves outside workspace root '${root}'`);
67
- }
68
-
69
- return resolved;
70
- }
File without changes