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.
Files changed (113) hide show
  1. package/README.md +223 -17
  2. package/{AGENT_ROLE.md → docs/examples/AGENT_ROLE.md} +87 -30
  3. package/{AGENT_ROLE_MINIMAL.md → docs/examples/AGENT_ROLE_MINIMAL.md} +23 -8
  4. package/package.json +12 -8
  5. package/src/.project-graph-cache.json +1 -0
  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/ai-context.js +7 -0
  23. package/src/compact/compact.js +18 -0
  24. package/src/compact/compress.js +13 -0
  25. package/src/compact/ctx-to-jsdoc.js +29 -0
  26. package/src/compact/doc-dialect.js +30 -0
  27. package/src/compact/expand.js +37 -0
  28. package/src/compact/framework-references.js +5 -0
  29. package/src/compact/instructions.js +3 -0
  30. package/src/compact/mode-config.js +8 -0
  31. package/src/compact/validate-pipeline.js +9 -0
  32. package/src/core/event-bus.js +9 -0
  33. package/src/core/filters.js +14 -0
  34. package/src/core/graph-builder.js +12 -0
  35. package/src/core/parser.js +31 -0
  36. package/src/core/workspace.js +8 -0
  37. package/src/lang/lang-go.js +17 -0
  38. package/src/lang/lang-python.js +12 -0
  39. package/src/lang/lang-sql.js +23 -0
  40. package/src/lang/lang-typescript.js +9 -0
  41. package/src/lang/lang-utils.js +4 -0
  42. package/src/mcp/mcp-server.js +17 -0
  43. package/src/mcp/tool-defs.js +3 -0
  44. package/src/mcp/tools.js +25 -0
  45. package/src/network/backend-lifecycle.js +19 -0
  46. package/src/network/backend.js +5 -0
  47. package/src/network/local-gateway.js +23 -0
  48. package/src/network/mdns.js +13 -0
  49. package/src/network/server.js +10 -0
  50. package/src/network/web-server.js +34 -0
  51. package/vendor/terser.mjs +49 -0
  52. package/web/.project-graph-cache.json +1 -0
  53. package/web/app.js +16 -0
  54. package/web/components/code-block.js +3 -0
  55. package/web/components/quick-open.js +5 -0
  56. package/web/dashboard-state.js +3 -0
  57. package/web/dashboard.html +27 -0
  58. package/web/dashboard.js +8 -0
  59. package/web/highlight.js +13 -0
  60. package/web/index.html +35 -0
  61. package/web/panels/ActionBoard/ActionBoard.css.js +1 -0
  62. package/web/panels/ActionBoard/ActionBoard.js +4 -0
  63. package/web/panels/ActionBoard/ActionBoard.tpl.js +1 -0
  64. package/web/panels/EventItem/EventItem.css.js +1 -0
  65. package/web/panels/EventItem/EventItem.js +4 -0
  66. package/web/panels/EventItem/EventItem.tpl.js +1 -0
  67. package/web/panels/ProjectItem/ProjectItem.css.js +1 -0
  68. package/web/panels/ProjectItem/ProjectItem.js +5 -0
  69. package/web/panels/ProjectItem/ProjectItem.tpl.js +1 -0
  70. package/web/panels/ProjectList/ProjectList.css.js +1 -0
  71. package/web/panels/ProjectList/ProjectList.js +4 -0
  72. package/web/panels/ProjectList/ProjectList.tpl.js +1 -0
  73. package/web/panels/SettingsPanel/.project-graph-cache.json +1 -0
  74. package/web/panels/SettingsPanel/SettingsPanel.css.js +1 -0
  75. package/web/panels/SettingsPanel/SettingsPanel.js +7 -0
  76. package/web/panels/SettingsPanel/SettingsPanel.tpl.js +1 -0
  77. package/web/panels/code-viewer.js +5 -0
  78. package/web/panels/ctx-panel.js +4 -0
  79. package/web/panels/dep-graph.js +6 -0
  80. package/web/panels/file-tree.js +188 -0
  81. package/web/panels/health-panel.js +3 -0
  82. package/web/panels/live-monitor.js +3 -0
  83. package/web/state.js +17 -0
  84. package/web/style.css +157 -0
  85. package/references/symbiote-3x.md +0 -834
  86. package/src/cli-handlers.js +0 -140
  87. package/src/cli.js +0 -83
  88. package/src/complexity.js +0 -223
  89. package/src/custom-rules.js +0 -583
  90. package/src/db-analysis.js +0 -194
  91. package/src/dead-code.js +0 -468
  92. package/src/filters.js +0 -227
  93. package/src/framework-references.js +0 -177
  94. package/src/full-analysis.js +0 -174
  95. package/src/graph-builder.js +0 -299
  96. package/src/instructions.js +0 -175
  97. package/src/jsdoc-generator.js +0 -214
  98. package/src/lang-go.js +0 -285
  99. package/src/lang-python.js +0 -197
  100. package/src/lang-sql.js +0 -309
  101. package/src/lang-typescript.js +0 -190
  102. package/src/lang-utils.js +0 -124
  103. package/src/large-files.js +0 -162
  104. package/src/mcp-server.js +0 -468
  105. package/src/outdated-patterns.js +0 -295
  106. package/src/parser.js +0 -452
  107. package/src/server.js +0 -28
  108. package/src/similar-functions.js +0 -278
  109. package/src/test-annotations.js +0 -301
  110. package/src/tool-defs.js +0 -525
  111. package/src/tools.js +0 -470
  112. package/src/undocumented.js +0 -260
  113. package/src/workspace.js +0 -70
@@ -1,140 +0,0 @@
1
- /**
2
- * CLI Command Handlers Registry
3
- * Extracted from cli.js to reduce cyclomatic complexity
4
- */
5
-
6
- import { getSkeleton, getFocusZone, expand, deps, usages } from './tools.js';
7
- import { getPendingTests, getTestSummary } from './test-annotations.js';
8
- import { getFilters } from './filters.js';
9
- import { getInstructions } from './instructions.js';
10
- import { getUndocumentedSummary } from './undocumented.js';
11
- import { getDeadCode } from './dead-code.js';
12
- import { generateJSDoc } from './jsdoc-generator.js';
13
- import { getSimilarFunctions } from './similar-functions.js';
14
- import { getComplexity } from './complexity.js';
15
- import { getLargeFiles } from './large-files.js';
16
- import { getOutdatedPatterns } from './outdated-patterns.js';
17
- import { getFullAnalysis } from './full-analysis.js';
18
- import { resolvePath } from './workspace.js';
19
-
20
- /**
21
- * Parse named argument from args array
22
- * @param {string[]} args
23
- * @param {string} name
24
- * @returns {string|undefined}
25
- */
26
- function getArg(args, name) {
27
- const arg = args.find(a => a.startsWith(`--${name}=`));
28
- return arg ? arg.split('=')[1] : undefined;
29
- }
30
-
31
- /**
32
- * Get path argument (first non-flag arg), resolved against workspace root
33
- * @param {string[]} args
34
- * @returns {string}
35
- */
36
- function getPath(args) {
37
- const raw = args.find(a => !a.startsWith('--')) || '.';
38
- return resolvePath(raw);
39
- }
40
-
41
- /**
42
- * CLI command handlers registry
43
- * Each handler returns a result or throws an error
44
- */
45
- export const CLI_HANDLERS = {
46
- skeleton: {
47
- requiresArg: true,
48
- argError: 'Path required: skeleton <path>',
49
- handler: async (args) => getSkeleton(resolvePath(args[0])),
50
- },
51
-
52
- expand: {
53
- requiresArg: true,
54
- argError: 'Symbol required: expand <symbol>',
55
- handler: async (args) => expand(args[0]),
56
- },
57
-
58
- deps: {
59
- requiresArg: true,
60
- argError: 'Symbol required: deps <symbol>',
61
- handler: async (args) => deps(args[0]),
62
- },
63
-
64
- usages: {
65
- requiresArg: true,
66
- argError: 'Symbol required: usages <symbol>',
67
- handler: async (args) => usages(args[0]),
68
- },
69
-
70
- pending: {
71
- handler: async (args) => getPendingTests(getPath(args)),
72
- },
73
-
74
- summary: {
75
- handler: async (args) => getTestSummary(getPath(args)),
76
- },
77
-
78
- filters: {
79
- handler: async () => getFilters(),
80
- },
81
-
82
- instructions: {
83
- rawOutput: true,
84
- handler: async () => getInstructions(),
85
- },
86
-
87
- undocumented: {
88
- handler: async (args) => {
89
- const level = getArg(args, 'level') || 'tests';
90
- return getUndocumentedSummary(getPath(args), level);
91
- },
92
- },
93
-
94
- deadcode: {
95
- handler: async (args) => getDeadCode(getPath(args)),
96
- },
97
-
98
- jsdoc: {
99
- requiresArg: true,
100
- argError: 'Usage: jsdoc <file>',
101
- handler: async (args) => generateJSDoc(resolvePath(args[0])),
102
- },
103
-
104
- similar: {
105
- handler: async (args) => {
106
- const threshold = parseInt(getArg(args, 'threshold')) || 60;
107
- return getSimilarFunctions(getPath(args), { threshold });
108
- },
109
- },
110
-
111
- complexity: {
112
- handler: async (args) => {
113
- const minComplexity = parseInt(getArg(args, 'min')) || 1;
114
- const onlyProblematic = args.includes('--problematic');
115
- return getComplexity(getPath(args), { minComplexity, onlyProblematic });
116
- },
117
- },
118
-
119
- largefiles: {
120
- handler: async (args) => {
121
- const onlyProblematic = args.includes('--problematic');
122
- return getLargeFiles(getPath(args), { onlyProblematic });
123
- },
124
- },
125
-
126
- outdated: {
127
- handler: async (args) => {
128
- const codeOnly = args.includes('--code');
129
- const depsOnly = args.includes('--deps');
130
- return getOutdatedPatterns(getPath(args), { codeOnly, depsOnly });
131
- },
132
- },
133
-
134
- analyze: {
135
- handler: async (args) => {
136
- const includeItems = args.includes('--items');
137
- return getFullAnalysis(getPath(args), { includeItems });
138
- },
139
- },
140
- };
package/src/cli.js DELETED
@@ -1,83 +0,0 @@
1
- /**
2
- * CLI Entry Point for Project Graph MCP
3
- */
4
-
5
- import { CLI_HANDLERS } from './cli-handlers.js';
6
-
7
- /**
8
- * Print CLI help
9
- */
10
- export function printHelp() {
11
- console.log(`
12
- project-graph-mcp - MCP server for AI agents
13
-
14
- Usage:
15
- npx project-graph-mcp Start MCP stdio server
16
- npx project-graph-mcp <command> [args] Run CLI command
17
-
18
- Commands:
19
- skeleton <path> Get compact project overview
20
- expand <symbol> Expand minified symbol (e.g., SN, SN.togglePin)
21
- deps <symbol> Get dependency tree
22
- usages <symbol> Find all usages
23
- pending <path> List pending @test/@expect tests
24
- summary <path> Get test progress summary
25
- undocumented <path> Find missing JSDoc (--level=tests|params|all)
26
- deadcode <path> Find unused functions/classes
27
- jsdoc <file> Generate JSDoc for file
28
- similar <path> Find similar functions (--threshold=60)
29
- complexity <path> Analyze cyclomatic complexity (--min=1)
30
- largefiles <path> Find files needing split (--problematic)
31
- outdated <path> Find legacy patterns & redundant deps
32
- analyze <path> Run ALL checks with Health Score
33
- filters Show current filter configuration
34
- instructions Show agent guidelines (JSDoc, Arch)
35
- help Show this help
36
-
37
- Examples:
38
- npx project-graph-mcp skeleton src/components
39
- npx project-graph-mcp expand SN
40
- npx project-graph-mcp pending src/
41
- `);
42
- }
43
-
44
- /**
45
- * Run CLI command
46
- * @param {string} command
47
- * @param {string[]} args
48
- */
49
- export async function runCLI(command, args) {
50
- // Handle help commands
51
- if (!command || command === 'help' || command === '--help' || command === '-h') {
52
- printHelp();
53
- return;
54
- }
55
-
56
- // Look up handler
57
- const def = CLI_HANDLERS[command];
58
- if (!def) {
59
- console.error(`Unknown command: ${command}`);
60
- console.error('Run with "help" for usage information');
61
- process.exit(1);
62
- }
63
-
64
- // Validate required arg
65
- if (def.requiresArg && !args[0]) {
66
- console.error(def.argError || `Argument required for: ${command}`);
67
- process.exit(1);
68
- }
69
-
70
- try {
71
- const result = await def.handler(args);
72
-
73
- // Handle raw output (like instructions)
74
- if (def.rawOutput) {
75
- console.log(result);
76
- } else {
77
- console.log(JSON.stringify(result, null, 2));
78
- }
79
- } catch (error) {
80
- console.error('Error:', error.message);
81
- process.exit(1);
82
- }
83
- }
package/src/complexity.js DELETED
@@ -1,223 +0,0 @@
1
- /**
2
- * Cyclomatic Complexity Analyzer
3
- * Measures function complexity based on decision points
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} ComplexityItem
14
- * @property {string} name
15
- * @property {string} type - 'function' | 'method'
16
- * @property {string} file
17
- * @property {number} line
18
- * @property {number} complexity - Cyclomatic complexity score
19
- * @property {string} rating - 'low' | 'moderate' | 'high' | 'critical'
20
- */
21
-
22
- /**
23
- * Find all JS files
24
- * @param {string} dir
25
- * @param {string} rootDir
26
- * @returns {string[]}
27
- */
28
- function findJSFiles(dir, rootDir = dir) {
29
- if (dir === rootDir) parseGitignore(rootDir);
30
- const files = [];
31
-
32
- try {
33
- for (const entry of readdirSync(dir)) {
34
- const fullPath = join(dir, entry);
35
- const relativePath = relative(rootDir, fullPath);
36
- const stat = statSync(fullPath);
37
-
38
- if (stat.isDirectory()) {
39
- if (!shouldExcludeDir(entry, relativePath)) {
40
- files.push(...findJSFiles(fullPath, rootDir));
41
- }
42
- } else if (entry.endsWith('.js') && !entry.endsWith('.css.js') && !entry.endsWith('.tpl.js')) {
43
- if (!shouldExcludeFile(entry, relativePath)) {
44
- files.push(fullPath);
45
- }
46
- }
47
- }
48
- } catch (e) { }
49
-
50
- return files;
51
- }
52
-
53
- /**
54
- * Calculate complexity of a function body
55
- * @param {Object} body
56
- * @returns {number}
57
- */
58
- function calculateComplexity(body) {
59
- let complexity = 1; // Base complexity
60
-
61
- walk.simple(body, {
62
- // Branching
63
- IfStatement() { complexity++; },
64
- ConditionalExpression() { complexity++; }, // ternary
65
-
66
- // Loops
67
- ForStatement() { complexity++; },
68
- ForOfStatement() { complexity++; },
69
- ForInStatement() { complexity++; },
70
- WhileStatement() { complexity++; },
71
- DoWhileStatement() { complexity++; },
72
-
73
- // Switch cases
74
- SwitchCase(node) {
75
- if (node.test) complexity++; // Skip default case
76
- },
77
-
78
- // Logical operators
79
- LogicalExpression(node) {
80
- if (node.operator === '&&' || node.operator === '||') {
81
- complexity++;
82
- }
83
- },
84
-
85
- // Nullish coalescing
86
- BinaryExpression(node) {
87
- if (node.operator === '??') {
88
- complexity++;
89
- }
90
- },
91
-
92
- // Error handling
93
- CatchClause() { complexity++; },
94
- });
95
-
96
- return complexity;
97
- }
98
-
99
- /**
100
- * Get rating from complexity score
101
- * @param {number} complexity
102
- * @returns {string}
103
- */
104
- function getRating(complexity) {
105
- if (complexity <= 5) return 'low';
106
- if (complexity <= 10) return 'moderate';
107
- if (complexity <= 20) return 'high';
108
- return 'critical';
109
- }
110
-
111
- /**
112
- * Analyze complexity of file
113
- * @param {string} filePath
114
- * @returns {ComplexityItem[]}
115
- */
116
- function analyzeFile(filePath, rootDir) {
117
- const code = readFileSync(filePath, 'utf-8');
118
- const relPath = relative(rootDir, filePath);
119
- const items = [];
120
-
121
- let ast;
122
- try {
123
- ast = parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
124
- } catch (e) {
125
- return items;
126
- }
127
-
128
- walk.simple(ast, {
129
- FunctionDeclaration(node) {
130
- if (!node.id) return;
131
- const complexity = calculateComplexity(node.body);
132
- items.push({
133
- name: node.id.name,
134
- type: 'function',
135
- file: relPath,
136
- line: node.loc.start.line,
137
- complexity,
138
- rating: getRating(complexity),
139
- });
140
- },
141
-
142
- ArrowFunctionExpression(node) {
143
- // Skip small arrow functions
144
- if (node.body.type !== 'BlockStatement') return;
145
- const complexity = calculateComplexity(node.body);
146
- if (complexity > 5) {
147
- // Only report complex arrow functions
148
- items.push({
149
- name: '(arrow)',
150
- type: 'function',
151
- file: relPath,
152
- line: node.loc.start.line,
153
- complexity,
154
- rating: getRating(complexity),
155
- });
156
- }
157
- },
158
-
159
- MethodDefinition(node) {
160
- if (node.kind !== 'method') return;
161
- const name = node.key.name || node.key.value;
162
- const complexity = calculateComplexity(node.value.body);
163
- items.push({
164
- name,
165
- type: 'method',
166
- file: relPath,
167
- line: node.loc.start.line,
168
- complexity,
169
- rating: getRating(complexity),
170
- });
171
- },
172
- });
173
-
174
- return items;
175
- }
176
-
177
- /**
178
- * Get complexity analysis for directory
179
- * @param {string} dir
180
- * @param {Object} [options]
181
- * @param {number} [options.minComplexity=1] - Minimum complexity to include
182
- * @param {boolean} [options.onlyProblematic=false] - Only show high/critical
183
- * @returns {Promise<{total: number, stats: Object, items: ComplexityItem[]}>}
184
- */
185
- export async function getComplexity(dir, options = {}) {
186
- const minComplexity = options.minComplexity || 1;
187
- const onlyProblematic = options.onlyProblematic || false;
188
- const resolvedDir = resolve(dir);
189
-
190
- const files = findJSFiles(dir);
191
- let allItems = [];
192
-
193
- for (const file of files) {
194
- allItems.push(...analyzeFile(file, resolvedDir));
195
- }
196
-
197
- // Filter
198
- allItems = allItems.filter(item => {
199
- if (item.complexity < minComplexity) return false;
200
- if (onlyProblematic && (item.rating === 'low' || item.rating === 'moderate')) return false;
201
- return true;
202
- });
203
-
204
- // Sort by complexity descending
205
- allItems.sort((a, b) => b.complexity - a.complexity);
206
-
207
- // Calculate stats
208
- const stats = {
209
- low: allItems.filter(i => i.rating === 'low').length,
210
- moderate: allItems.filter(i => i.rating === 'moderate').length,
211
- high: allItems.filter(i => i.rating === 'high').length,
212
- critical: allItems.filter(i => i.rating === 'critical').length,
213
- average: allItems.length > 0
214
- ? Math.round(allItems.reduce((s, i) => s + i.complexity, 0) / allItems.length * 10) / 10
215
- : 0,
216
- };
217
-
218
- return {
219
- total: allItems.length,
220
- stats,
221
- items: allItems.slice(0, 30),
222
- };
223
- }