project-graph-mcp 1.5.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 (121) hide show
  1. package/README.md +128 -8
  2. package/package.json +12 -8
  3. package/src/.project-graph-cache.json +1 -1
  4. package/src/analysis/analysis-cache.js +7 -0
  5. package/src/analysis/complexity.js +14 -0
  6. package/src/analysis/custom-rules.js +36 -0
  7. package/src/analysis/db-analysis.js +9 -0
  8. package/src/analysis/dead-code.js +19 -0
  9. package/src/analysis/full-analysis.js +18 -0
  10. package/src/analysis/jsdoc-checker.js +24 -0
  11. package/src/analysis/jsdoc-generator.js +10 -0
  12. package/src/analysis/large-files.js +11 -0
  13. package/src/analysis/outdated-patterns.js +12 -0
  14. package/src/analysis/similar-functions.js +16 -0
  15. package/src/analysis/test-annotations.js +21 -0
  16. package/src/analysis/type-checker.js +8 -0
  17. package/src/analysis/undocumented.js +14 -0
  18. package/src/cli/cli-handlers.js +4 -0
  19. package/src/cli/cli.js +5 -0
  20. package/src/compact/ai-context.js +7 -0
  21. package/src/compact/compact.js +18 -0
  22. package/src/compact/compress.js +13 -0
  23. package/src/compact/ctx-to-jsdoc.js +29 -0
  24. package/src/compact/doc-dialect.js +30 -0
  25. package/src/compact/expand.js +37 -0
  26. package/src/compact/framework-references.js +5 -0
  27. package/src/compact/instructions.js +3 -0
  28. package/src/compact/mode-config.js +8 -0
  29. package/src/compact/validate-pipeline.js +9 -0
  30. package/src/core/event-bus.js +9 -0
  31. package/src/core/filters.js +14 -0
  32. package/src/core/graph-builder.js +12 -0
  33. package/src/core/parser.js +31 -0
  34. package/src/core/workspace.js +8 -0
  35. package/src/lang/lang-go.js +17 -0
  36. package/src/lang/lang-python.js +12 -0
  37. package/src/lang/lang-sql.js +23 -0
  38. package/src/lang/lang-typescript.js +9 -0
  39. package/src/lang/lang-utils.js +4 -0
  40. package/src/mcp/mcp-server.js +17 -0
  41. package/src/mcp/tool-defs.js +3 -0
  42. package/src/mcp/tools.js +25 -0
  43. package/src/network/backend-lifecycle.js +19 -0
  44. package/src/network/backend.js +5 -0
  45. package/src/network/local-gateway.js +23 -0
  46. package/src/network/mdns.js +13 -0
  47. package/src/network/server.js +10 -0
  48. package/src/network/web-server.js +34 -0
  49. package/web/.project-graph-cache.json +1 -0
  50. package/web/app.js +16 -0
  51. package/web/components/code-block.js +3 -0
  52. package/web/components/quick-open.js +5 -0
  53. package/web/dashboard-state.js +3 -0
  54. package/web/dashboard.html +27 -0
  55. package/web/dashboard.js +8 -0
  56. package/web/highlight.js +13 -0
  57. package/web/index.html +35 -0
  58. package/web/panels/ActionBoard/ActionBoard.css.js +1 -0
  59. package/web/panels/ActionBoard/ActionBoard.js +4 -0
  60. package/web/panels/ActionBoard/ActionBoard.tpl.js +1 -0
  61. package/web/panels/EventItem/EventItem.css.js +1 -0
  62. package/web/panels/EventItem/EventItem.js +4 -0
  63. package/web/panels/EventItem/EventItem.tpl.js +1 -0
  64. package/web/panels/ProjectItem/ProjectItem.css.js +1 -0
  65. package/web/panels/ProjectItem/ProjectItem.js +5 -0
  66. package/web/panels/ProjectItem/ProjectItem.tpl.js +1 -0
  67. package/web/panels/ProjectList/ProjectList.css.js +1 -0
  68. package/web/panels/ProjectList/ProjectList.js +4 -0
  69. package/web/panels/ProjectList/ProjectList.tpl.js +1 -0
  70. package/web/panels/SettingsPanel/.project-graph-cache.json +1 -0
  71. package/web/panels/SettingsPanel/SettingsPanel.css.js +1 -0
  72. package/web/panels/SettingsPanel/SettingsPanel.js +7 -0
  73. package/web/panels/SettingsPanel/SettingsPanel.tpl.js +1 -0
  74. package/web/panels/code-viewer.js +5 -0
  75. package/web/panels/ctx-panel.js +4 -0
  76. package/web/panels/dep-graph.js +6 -0
  77. package/web/panels/file-tree.js +188 -0
  78. package/web/panels/health-panel.js +3 -0
  79. package/web/panels/live-monitor.js +3 -0
  80. package/web/state.js +17 -0
  81. package/web/style.css +157 -0
  82. package/references/symbiote-3x.md +0 -834
  83. package/src/ai-context.js +0 -113
  84. package/src/analysis-cache.js +0 -155
  85. package/src/cli-handlers.js +0 -271
  86. package/src/cli.js +0 -95
  87. package/src/compact.js +0 -207
  88. package/src/complexity.js +0 -237
  89. package/src/compress.js +0 -319
  90. package/src/ctx-to-jsdoc.js +0 -514
  91. package/src/custom-rules.js +0 -584
  92. package/src/db-analysis.js +0 -194
  93. package/src/dead-code.js +0 -468
  94. package/src/doc-dialect.js +0 -716
  95. package/src/filters.js +0 -227
  96. package/src/framework-references.js +0 -177
  97. package/src/full-analysis.js +0 -470
  98. package/src/graph-builder.js +0 -299
  99. package/src/instructions.js +0 -73
  100. package/src/jsdoc-checker.js +0 -351
  101. package/src/jsdoc-generator.js +0 -203
  102. package/src/lang-go.js +0 -285
  103. package/src/lang-python.js +0 -197
  104. package/src/lang-sql.js +0 -309
  105. package/src/lang-typescript.js +0 -190
  106. package/src/lang-utils.js +0 -124
  107. package/src/large-files.js +0 -163
  108. package/src/mcp-server.js +0 -675
  109. package/src/mode-config.js +0 -127
  110. package/src/outdated-patterns.js +0 -296
  111. package/src/parser.js +0 -662
  112. package/src/server.js +0 -28
  113. package/src/similar-functions.js +0 -279
  114. package/src/test-annotations.js +0 -323
  115. package/src/tool-defs.js +0 -793
  116. package/src/tools.js +0 -470
  117. package/src/type-checker.js +0 -188
  118. package/src/undocumented.js +0 -259
  119. package/src/workspace.js +0 -70
  120. /package/{AGENT_ROLE.md → docs/examples/AGENT_ROLE.md} +0 -0
  121. /package/{AGENT_ROLE_MINIMAL.md → docs/examples/AGENT_ROLE_MINIMAL.md} +0 -0
package/src/ai-context.js DELETED
@@ -1,113 +0,0 @@
1
- /**
2
- * AI Context Boot — Single-call agent bootstrap
3
- *
4
- * Combines skeleton + doc-dialect + optional compressed files
5
- * into one response for AI agent initialization.
6
- */
7
-
8
- import { resolve, extname } from 'path';
9
- import { getSkeleton, getGraph } from './tools.js';
10
- import { getProjectDocs } from './doc-dialect.js';
11
- import { compressFile } from './compress.js';
12
- import { findJSFiles } from './parser.js';
13
-
14
- /** Supported extensions for compression */
15
- const COMPRESSIBLE = new Set(['.js', '.mjs', '.ts', '.tsx']);
16
-
17
- /**
18
- * Estimate tokens for any value (string or object)
19
- * @param {*} value
20
- * @returns {number}
21
- */
22
- function estimateTokens(value) {
23
- const str = typeof value === 'string' ? value : JSON.stringify(value);
24
- return Math.ceil(str.length / 4);
25
- }
26
-
27
- /**
28
- * Load complete AI context for agent bootstrap
29
- * @param {string} dirPath - Project directory
30
- * @param {Object} [options]
31
- * @param {string[]} [options.includeFiles] - Specific files to include compressed
32
- * @param {boolean} [options.includeDocs=true] - Include doc-dialect
33
- * @param {boolean} [options.includeSkeleton=true] - Include skeleton
34
- * @returns {Promise<{skeleton?: Object, docs?: string, files?: Object, totalTokens: number, vsOriginal: number, savings: string}>}
35
- */
36
- export async function getAiContext(dirPath, options = {}) {
37
- const {
38
- includeFiles = [],
39
- includeDocs = true,
40
- includeSkeleton = true,
41
- } = options;
42
-
43
- const projectPath = resolve(dirPath);
44
- const result = {};
45
- let totalTokens = 0;
46
-
47
- // 1. Skeleton
48
- if (includeSkeleton) {
49
- result.skeleton = await getSkeleton(projectPath);
50
- totalTokens += estimateTokens(result.skeleton);
51
- }
52
-
53
- // 2. Doc Dialect
54
- if (includeDocs) {
55
- const graph = await getGraph(projectPath);
56
- result.docs = getProjectDocs(graph, projectPath);
57
- totalTokens += estimateTokens(result.docs);
58
- }
59
-
60
- // 3. Compressed files
61
- if (includeFiles.length > 0) {
62
- result.files = {};
63
- const allFiles = findJSFiles(projectPath);
64
-
65
- for (const requestedFile of includeFiles) {
66
- // Find matching file (by name or path)
67
- const match = allFiles.find(f =>
68
- f.endsWith(requestedFile) || f.endsWith('/' + requestedFile)
69
- );
70
-
71
- if (!match) {
72
- result.files[requestedFile] = { error: `File not found: ${requestedFile}` };
73
- continue;
74
- }
75
-
76
- const ext = extname(match).toLowerCase();
77
- if (!COMPRESSIBLE.has(ext)) {
78
- result.files[requestedFile] = { error: `Unsupported file type: ${ext}` };
79
- continue;
80
- }
81
-
82
- try {
83
- const compressed = await compressFile(match, { beautify: true, legend: true });
84
- result.files[requestedFile] = compressed.code;
85
- totalTokens += compressed.compressed;
86
- } catch (e) {
87
- result.files[requestedFile] = { error: e.message };
88
- }
89
- }
90
- }
91
-
92
- // Estimate original size for savings calculation
93
- const allFiles = findJSFiles(projectPath);
94
- let vsOriginal = 0;
95
- for (const file of allFiles) {
96
- try {
97
- const { readFileSync } = await import('fs');
98
- vsOriginal += estimateTokens(readFileSync(file, 'utf-8'));
99
- } catch {
100
- // skip unreadable
101
- }
102
- }
103
-
104
- const savings = vsOriginal > 0
105
- ? Math.round((1 - totalTokens / vsOriginal) * 100)
106
- : 0;
107
-
108
- result.totalTokens = totalTokens;
109
- result.vsOriginal = vsOriginal;
110
- result.savings = `${savings}%`;
111
-
112
- return result;
113
- }
@@ -1,155 +0,0 @@
1
- /**
2
- * Analysis Cache Manager
3
- * Persistent file-based cache in .context/.cache/ with dual hashing
4
- *
5
- * - sig: interface hash (function names, params, exports) → invalidates docs
6
- * - contentHash: full source hash → invalidates body-dependent metrics
7
- *
8
- * Cacheable (per-file): complexity, undocumented, jsdocConsistency
9
- * NOT cacheable (cross-file): deadCode, similarity
10
- */
11
-
12
- import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'fs';
13
- import { join, dirname } from 'path';
14
- import { createHash } from 'crypto';
15
-
16
- /**
17
- * @typedef {Object} CacheEntry
18
- * @property {string} sig - Interface hash
19
- * @property {string} contentHash - Full content hash
20
- * @property {Object} [complexity] - Cached complexity results
21
- * @property {Array} [undocumented] - Cached undocumented items
22
- * @property {Array} [jsdocIssues] - Cached JSDoc consistency issues
23
- * @property {string} cachedAt - ISO timestamp
24
- */
25
-
26
- /**
27
- * Compute interface signature hash
28
- * Matches @sig logic from doc-dialect.js
29
- * @param {Object} fileData - Parsed file data with functions, classes, exports
30
- * @returns {string} - 8-char hex hash
31
- */
32
- export function computeSig(fileData) {
33
- const parts = [];
34
-
35
- // Function signatures
36
- if (fileData.functions) {
37
- for (const fn of fileData.functions) {
38
- parts.push(`fn:${fn.name}:${fn.params?.length || 0}`);
39
- }
40
- }
41
-
42
- // Class signatures
43
- if (fileData.classes) {
44
- for (const cls of fileData.classes) {
45
- parts.push(`cls:${cls.name}`);
46
- if (cls.methods) {
47
- for (const m of cls.methods) {
48
- parts.push(`m:${cls.name}.${m}`);
49
- }
50
- }
51
- }
52
- }
53
-
54
- // Exports
55
- if (fileData.exports) {
56
- for (const exp of fileData.exports) {
57
- parts.push(`exp:${exp}`);
58
- }
59
- }
60
-
61
- const hash = createHash('md5').update(parts.sort().join('|')).digest('hex');
62
- return hash.slice(0, 8);
63
- }
64
-
65
- /**
66
- * Compute full content hash (body-inclusive)
67
- * Used for complexity and other body-dependent metrics
68
- * @param {string} code - Source code
69
- * @returns {string} - 8-char hex hash
70
- */
71
- export function computeContentHash(code) {
72
- return createHash('md5').update(code).digest('hex').slice(0, 8);
73
- }
74
-
75
- /**
76
- * Get cache file path for a source file
77
- * @param {string} contextDir - .context directory path
78
- * @param {string} relPath - Relative path to source file (e.g., "src/parser.js")
79
- * @returns {string} - Cache file path
80
- */
81
- export function getCachePath(contextDir, relPath) {
82
- // src/parser.js → .context/.cache/src/parser.json
83
- const cacheName = relPath.replace(/\.[^.]+$/, '.json');
84
- return join(contextDir, '.cache', cacheName);
85
- }
86
-
87
- /**
88
- * Read cached analysis for a file
89
- * @param {string} contextDir
90
- * @param {string} relPath
91
- * @returns {CacheEntry|null}
92
- */
93
- export function readCache(contextDir, relPath) {
94
- const cachePath = getCachePath(contextDir, relPath);
95
- try {
96
- if (!existsSync(cachePath)) return null;
97
- return JSON.parse(readFileSync(cachePath, 'utf-8'));
98
- } catch (e) {
99
- return null;
100
- }
101
- }
102
-
103
- /**
104
- * Write cache entry for a file
105
- * @param {string} contextDir
106
- * @param {string} relPath
107
- * @param {CacheEntry} data
108
- */
109
- export function writeCache(contextDir, relPath, data) {
110
- const cachePath = getCachePath(contextDir, relPath);
111
- try {
112
- mkdirSync(dirname(cachePath), { recursive: true });
113
- writeFileSync(cachePath, JSON.stringify({
114
- ...data,
115
- cachedAt: new Date().toISOString(),
116
- }, null, 2));
117
- } catch (e) {
118
- // Cache write failure is non-fatal
119
- }
120
- }
121
-
122
- /**
123
- * Check if cache is still valid
124
- * @param {CacheEntry|null} cached
125
- * @param {string} currentSig - Current interface hash
126
- * @param {string} currentContentHash - Current content hash
127
- * @param {'sig'|'content'} level - Which hash to check
128
- * @returns {boolean}
129
- */
130
- export function isCacheValid(cached, currentSig, currentContentHash, level = 'content') {
131
- if (!cached) return false;
132
- if (!cached.sig || !cached.contentHash) return false;
133
-
134
- if (level === 'sig') {
135
- return cached.sig === currentSig;
136
- }
137
-
138
- // For body-dependent metrics, both hashes must match
139
- return cached.sig === currentSig && cached.contentHash === currentContentHash;
140
- }
141
-
142
- /**
143
- * Invalidate all caches (e.g., after structural changes)
144
- * @param {string} contextDir
145
- */
146
- export function invalidateAllCaches(contextDir) {
147
- const cacheDir = join(contextDir, '.cache');
148
- try {
149
- if (existsSync(cacheDir)) {
150
- rmSync(cacheDir, { recursive: true, force: true });
151
- }
152
- } catch (e) {
153
- // Non-fatal
154
- }
155
- }
@@ -1,271 +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 { compressFile } from './compress.js';
19
- import { getProjectDocs, generateContextFiles } from './doc-dialect.js';
20
- import { getGraph } from './tools.js';
21
- import { parseProject } from './parser.js';
22
- import { resolvePath } from './workspace.js';
23
- import { checkJSDocConsistency } from './jsdoc-checker.js';
24
- import { checkTypes } from './type-checker.js';
25
- import { compactProject, expandProject } from './compact.js';
26
- import { injectJSDoc, stripJSDoc, validateCtxContracts } from './ctx-to-jsdoc.js';
27
- import { getConfig, setConfig, getModeDescription, getModeWorkflow } from './mode-config.js';
28
-
29
- /**
30
- * Parse named argument from args array
31
- * @param {string[]} args
32
- * @param {string} name
33
- * @returns {string|undefined}
34
- */
35
- function getArg(args, name) {
36
- const arg = args.find(a => a.startsWith(`--${name}=`));
37
- return arg ? arg.split('=')[1] : undefined;
38
- }
39
-
40
- /**
41
- * Get path argument (first non-flag arg), resolved against workspace root
42
- * @param {string[]} args
43
- * @returns {string}
44
- */
45
- function getPath(args) {
46
- const raw = args.find(a => !a.startsWith('--')) || '.';
47
- return resolvePath(raw);
48
- }
49
-
50
- /**
51
- * CLI command handlers registry
52
- * Each handler returns a result or throws an error
53
- */
54
- export const CLI_HANDLERS = {
55
- skeleton: {
56
- requiresArg: true,
57
- argError: 'Path required: skeleton <path>',
58
- handler: async (args) => getSkeleton(resolvePath(args[0])),
59
- },
60
-
61
- expand: {
62
- requiresArg: true,
63
- argError: 'Symbol required: expand <symbol>',
64
- handler: async (args) => expand(args[0]),
65
- },
66
-
67
- deps: {
68
- requiresArg: true,
69
- argError: 'Symbol required: deps <symbol>',
70
- handler: async (args) => deps(args[0]),
71
- },
72
-
73
- usages: {
74
- requiresArg: true,
75
- argError: 'Symbol required: usages <symbol>',
76
- handler: async (args) => usages(args[0]),
77
- },
78
-
79
- pending: {
80
- handler: async (args) => getPendingTests(getPath(args)),
81
- },
82
-
83
- summary: {
84
- handler: async (args) => getTestSummary(getPath(args)),
85
- },
86
-
87
- filters: {
88
- handler: async () => getFilters(),
89
- },
90
-
91
- instructions: {
92
- rawOutput: true,
93
- handler: async () => getInstructions(),
94
- },
95
-
96
- undocumented: {
97
- handler: async (args) => {
98
- const level = getArg(args, 'level') || 'tests';
99
- return getUndocumentedSummary(getPath(args), level);
100
- },
101
- },
102
-
103
- deadcode: {
104
- handler: async (args) => getDeadCode(getPath(args)),
105
- },
106
-
107
- jsdoc: {
108
- requiresArg: true,
109
- argError: 'Usage: jsdoc <file>',
110
- handler: async (args) => generateJSDoc(resolvePath(args[0])),
111
- },
112
-
113
- similar: {
114
- handler: async (args) => {
115
- const threshold = parseInt(getArg(args, 'threshold')) || 60;
116
- return getSimilarFunctions(getPath(args), { threshold });
117
- },
118
- },
119
-
120
- complexity: {
121
- handler: async (args) => {
122
- const minComplexity = parseInt(getArg(args, 'min')) || 1;
123
- const onlyProblematic = args.includes('--problematic');
124
- return getComplexity(getPath(args), { minComplexity, onlyProblematic });
125
- },
126
- },
127
-
128
- largefiles: {
129
- handler: async (args) => {
130
- const onlyProblematic = args.includes('--problematic');
131
- return getLargeFiles(getPath(args), { onlyProblematic });
132
- },
133
- },
134
-
135
- outdated: {
136
- handler: async (args) => {
137
- const codeOnly = args.includes('--code');
138
- const depsOnly = args.includes('--deps');
139
- return getOutdatedPatterns(getPath(args), { codeOnly, depsOnly });
140
- },
141
- },
142
-
143
- analyze: {
144
- handler: async (args) => {
145
- const includeItems = args.includes('--items');
146
- return getFullAnalysis(getPath(args), { includeItems });
147
- },
148
- },
149
-
150
- 'jsdoc-check': {
151
- handler: async (args) => checkJSDocConsistency(getPath(args)),
152
- },
153
-
154
- types: {
155
- handler: async (args) => {
156
- const maxDiagnostics = parseInt(getArg(args, 'max')) || 50;
157
- return checkTypes(getPath(args), { maxDiagnostics });
158
- },
159
- },
160
-
161
- compress: {
162
- requiresArg: true,
163
- argError: 'Usage: compress <file> [--no-beautify] [--no-legend]',
164
- handler: async (args) => {
165
- const beautify = !args.includes('--no-beautify');
166
- const legend = !args.includes('--no-legend');
167
- return compressFile(resolvePath(args[0]), { beautify, legend });
168
- },
169
- },
170
-
171
- docs: {
172
- requiresArg: true,
173
- argError: 'Usage: docs <path> [--file=<filename>]',
174
- handler: async (args) => {
175
- const projectPath = resolvePath(args[0]);
176
- const graph = await getGraph(projectPath);
177
- const file = args.find(a => a.startsWith('--file='))?.split('=')[1];
178
- return getProjectDocs(graph, projectPath, { file });
179
- },
180
- },
181
-
182
- 'generate-ctx': {
183
- requiresArg: true,
184
- argError: 'Usage: generate-ctx <path> [--overwrite] [--scope=focus|all]',
185
- handler: async (args) => {
186
- const projectPath = resolvePath(args[0]);
187
- const graph = await getGraph(projectPath);
188
- const parsed = await parseProject(projectPath);
189
- const overwrite = args.includes('--overwrite');
190
- const scope = args.find(a => a.startsWith('--scope='))?.split('=')[1] || 'all';
191
- return generateContextFiles(graph, projectPath, parsed, { overwrite, scope });
192
- },
193
- },
194
-
195
- compact: {
196
- requiresArg: true,
197
- argError: 'Usage: compact <path> [--dry-run]',
198
- handler: async (args) => {
199
- const projectPath = resolvePath(args[0]);
200
- const dryRun = args.includes('--dry-run');
201
- return compactProject(projectPath, { dryRun });
202
- },
203
- },
204
-
205
- beautify: {
206
- requiresArg: true,
207
- argError: 'Usage: beautify <path> [--dry-run]',
208
- handler: async (args) => {
209
- const projectPath = resolvePath(args[0]);
210
- const dryRun = args.includes('--dry-run');
211
- return expandProject(projectPath, { dryRun });
212
- },
213
- },
214
-
215
- 'inject-jsdoc': {
216
- requiresArg: true,
217
- argError: 'Usage: inject-jsdoc <path> [--dry-run]',
218
- handler: async (args) => {
219
- const projectPath = resolvePath(args[0]);
220
- const dryRun = args.includes('--dry-run');
221
- return injectJSDoc(projectPath, { dryRun });
222
- },
223
- },
224
-
225
- 'strip-jsdoc': {
226
- requiresArg: true,
227
- argError: 'Usage: strip-jsdoc <path> [--dry-run]',
228
- handler: async (args) => {
229
- const projectPath = resolvePath(args[0]);
230
- const dryRun = args.includes('--dry-run');
231
- return stripJSDoc(projectPath, { dryRun });
232
- },
233
- },
234
-
235
- 'validate-ctx': {
236
- requiresArg: true,
237
- argError: 'Usage: validate-ctx <path> [--strict]',
238
- handler: async (args) => {
239
- const projectPath = resolvePath(args[0]);
240
- const strict = args.includes('--strict');
241
- return validateCtxContracts(projectPath, { strict });
242
- },
243
- },
244
-
245
- mode: {
246
- requiresArg: true,
247
- argError: 'Usage: mode <path>',
248
- handler: async (args) => {
249
- const dir = resolvePath(args[0]);
250
- const config = getConfig(dir);
251
- return {
252
- ...config,
253
- description: getModeDescription(config.mode),
254
- workflow: getModeWorkflow(config.mode),
255
- };
256
- },
257
- },
258
-
259
- 'set-mode': {
260
- requiresArg: true,
261
- argError: 'Usage: set-mode <path> <1|2|3>',
262
- handler: async (args) => {
263
- const dir = resolvePath(args[0]);
264
- const mode = parseInt(args[1], 10);
265
- if (!mode || ![1, 2, 3].includes(mode)) {
266
- throw new Error('Mode must be 1, 2, or 3');
267
- }
268
- return setConfig(dir, { mode });
269
- },
270
- },
271
- };
package/src/cli.js DELETED
@@ -1,95 +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 .ctx.md test checklists
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
- jsdoc-check <path> Validate JSDoc ↔ function signatures
34
- types <path> Run tsc type checking (--max=50)
35
- compress <file> Compress JS file for AI (--no-beautify, --no-legend)
36
- compact <path> Compact all JS files — strips comments/whitespace (--dry-run)
37
- beautify <path> Beautify/expand all JS files — inverse of compact (--dry-run)
38
- inject-jsdoc <path> Generate JSDoc from .ctx files and inject into source
39
- strip-jsdoc <path> Strip all JSDoc blocks from source files
40
- docs <path> Get project docs in doc-dialect format (--file=<name>)
41
- generate-ctx <path> Generate .context/ docs (--overwrite --scope=focus)
42
- validate-ctx <path> Validate .ctx contracts against source AST (--strict)
43
- mode <path> Show current compact code mode and workflow
44
- set-mode <path> <1|2|3> Set compact code mode (1=compact, 2=full, 3=IDE)
45
- filters Show current filter configuration
46
- instructions Show agent guidelines (JSDoc, Arch)
47
- help Show this help
48
-
49
- Examples:
50
- npx project-graph-mcp skeleton src/components
51
- npx project-graph-mcp expand SN
52
- npx project-graph-mcp compact src/ --dry-run
53
- `);
54
- }
55
-
56
- /**
57
- * Run CLI command
58
- * @param {string} command
59
- * @param {string[]} args
60
- */
61
- export async function runCLI(command, args) {
62
- // Handle help commands
63
- if (!command || command === 'help' || command === '--help' || command === '-h') {
64
- printHelp();
65
- return;
66
- }
67
-
68
- // Look up handler
69
- const def = CLI_HANDLERS[command];
70
- if (!def) {
71
- console.error(`Unknown command: ${command}`);
72
- console.error('Run with "help" for usage information');
73
- process.exit(1);
74
- }
75
-
76
- // Validate required arg
77
- if (def.requiresArg && !args[0]) {
78
- console.error(def.argError || `Argument required for: ${command}`);
79
- process.exit(1);
80
- }
81
-
82
- try {
83
- const result = await def.handler(args);
84
-
85
- // Handle raw output (like instructions)
86
- if (def.rawOutput) {
87
- console.log(result);
88
- } else {
89
- console.log(JSON.stringify(result, null, 2));
90
- }
91
- } catch (error) {
92
- console.error('Error:', error.message);
93
- process.exit(1);
94
- }
95
- }