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/full-analysis.js
DELETED
|
@@ -1,470 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Full Analysis - Comprehensive Code Health Report
|
|
3
|
-
* Runs all analysis tools and generates a health score
|
|
4
|
-
*
|
|
5
|
-
* Uses incremental caching for per-file metrics (complexity, undocumented, jsdocConsistency).
|
|
6
|
-
* Cross-file metrics (dead code, similarity) always run dynamically.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
10
|
-
import { join, relative, resolve } from 'path';
|
|
11
|
-
import { getDeadCode } from './dead-code.js';
|
|
12
|
-
import { checkUndocumentedFile } from './undocumented.js';
|
|
13
|
-
import { getSimilarFunctions } from './similar-functions.js';
|
|
14
|
-
import { analyzeComplexityFile } from './complexity.js';
|
|
15
|
-
import { getLargeFiles } from './large-files.js';
|
|
16
|
-
import { getOutdatedPatterns } from './outdated-patterns.js';
|
|
17
|
-
import { getTableUsage } from './db-analysis.js';
|
|
18
|
-
import { checkJSDocFile } from './jsdoc-checker.js';
|
|
19
|
-
import { readCache, writeCache, computeContentHash, isCacheValid } from './analysis-cache.js';
|
|
20
|
-
import { shouldExcludeDir, shouldExcludeFile, parseGitignore } from './filters.js';
|
|
21
|
-
import { getWorkspaceRoot } from './workspace.js';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @typedef {Object} AnalysisResult
|
|
25
|
-
* @property {Object} deadCode
|
|
26
|
-
* @property {Object} undocumented
|
|
27
|
-
* @property {Object} similar
|
|
28
|
-
* @property {Object} complexity
|
|
29
|
-
* @property {Object} largeFiles
|
|
30
|
-
* @property {Object} outdated
|
|
31
|
-
* @property {Object} overall
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Calculate health score from analysis results
|
|
36
|
-
* @param {Object} results
|
|
37
|
-
* @returns {{score: number, rating: string, topIssues: string[]}}
|
|
38
|
-
*/
|
|
39
|
-
function calculateHealthScore(results) {
|
|
40
|
-
let score = 100;
|
|
41
|
-
const topIssues = [];
|
|
42
|
-
|
|
43
|
-
// Dead code penalty: -2 per item (max -20)
|
|
44
|
-
const deadPenalty = Math.min(results.deadCode.total * 2, 20);
|
|
45
|
-
score -= deadPenalty;
|
|
46
|
-
if (results.deadCode.total > 0) {
|
|
47
|
-
topIssues.push(`${results.deadCode.total} unused functions/classes`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Undocumented penalty: -0.5 per item (max -15)
|
|
51
|
-
const undocPenalty = Math.min(results.undocumented.total * 0.5, 15);
|
|
52
|
-
score -= undocPenalty;
|
|
53
|
-
if (results.undocumented.total > 10) {
|
|
54
|
-
topIssues.push(`${results.undocumented.total} undocumented items`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Similar functions penalty: -3 per pair (max -15)
|
|
58
|
-
const similarPenalty = Math.min(results.similar.total * 3, 15);
|
|
59
|
-
score -= similarPenalty;
|
|
60
|
-
if (results.similar.total > 0) {
|
|
61
|
-
topIssues.push(`${results.similar.total} similar function pairs`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Complexity penalty: -5 per critical, -2 per high (max -20)
|
|
65
|
-
const criticalCount = results.complexity.stats?.critical || 0;
|
|
66
|
-
const highCount = results.complexity.stats?.high || 0;
|
|
67
|
-
const complexityPenalty = Math.min(criticalCount * 5 + highCount * 2, 20);
|
|
68
|
-
score -= complexityPenalty;
|
|
69
|
-
if (criticalCount > 0) {
|
|
70
|
-
topIssues.push(`${criticalCount} critical complexity functions`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Large files penalty: -4 per critical, -1 per warning (max -10)
|
|
74
|
-
const largeCritical = results.largeFiles.stats?.critical || 0;
|
|
75
|
-
const largeWarning = results.largeFiles.stats?.warning || 0;
|
|
76
|
-
const largePenalty = Math.min(largeCritical * 4 + largeWarning * 1, 10);
|
|
77
|
-
score -= largePenalty;
|
|
78
|
-
if (largeCritical > 0) {
|
|
79
|
-
topIssues.push(`${largeCritical} files need splitting`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Outdated patterns penalty: -3 per error, -1 per warning (max -10)
|
|
83
|
-
const errorPatterns = results.outdated.stats?.bySeverity?.error || 0;
|
|
84
|
-
const warningPatterns = results.outdated.stats?.bySeverity?.warning || 0;
|
|
85
|
-
const outdatedPenalty = Math.min(errorPatterns * 3 + warningPatterns * 1, 10);
|
|
86
|
-
score -= outdatedPenalty;
|
|
87
|
-
if (results.outdated.redundantDeps?.length > 0) {
|
|
88
|
-
topIssues.push(`${results.outdated.redundantDeps.length} redundant npm dependencies`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// JSDoc consistency penalty: -2 per error, -1 per warning (max -15)
|
|
92
|
-
if (results.jsdocConsistency) {
|
|
93
|
-
const jsdocErrors = results.jsdocConsistency.errors || 0;
|
|
94
|
-
const jsdocWarnings = results.jsdocConsistency.warnings || 0;
|
|
95
|
-
const jsdocPenalty = Math.min(jsdocErrors * 2 + jsdocWarnings * 1, 15);
|
|
96
|
-
score -= jsdocPenalty;
|
|
97
|
-
if (jsdocErrors > 0) {
|
|
98
|
-
topIssues.push(`${jsdocErrors} JSDoc consistency errors`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Clamp score
|
|
103
|
-
score = Math.max(0, Math.min(100, Math.round(score)));
|
|
104
|
-
|
|
105
|
-
// Determine rating
|
|
106
|
-
let rating;
|
|
107
|
-
if (score >= 90) rating = 'excellent';
|
|
108
|
-
else if (score >= 70) rating = 'good';
|
|
109
|
-
else if (score >= 50) rating = 'warning';
|
|
110
|
-
else rating = 'critical';
|
|
111
|
-
|
|
112
|
-
return { score, rating, topIssues: topIssues.slice(0, 5) };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Find all JS files in directory
|
|
117
|
-
* @param {string} dir
|
|
118
|
-
* @param {string} rootDir
|
|
119
|
-
* @returns {string[]}
|
|
120
|
-
*/
|
|
121
|
-
function findJSFiles(dir, rootDir = dir) {
|
|
122
|
-
if (dir === rootDir) parseGitignore(rootDir);
|
|
123
|
-
const files = [];
|
|
124
|
-
try {
|
|
125
|
-
for (const entry of readdirSync(dir)) {
|
|
126
|
-
const fullPath = join(dir, entry);
|
|
127
|
-
const relativePath = relative(rootDir, fullPath);
|
|
128
|
-
const stat = statSync(fullPath);
|
|
129
|
-
if (stat.isDirectory()) {
|
|
130
|
-
if (!shouldExcludeDir(entry, relativePath)) {
|
|
131
|
-
files.push(...findJSFiles(fullPath, rootDir));
|
|
132
|
-
}
|
|
133
|
-
} else if (entry.endsWith('.js') && !entry.endsWith('.css.js') && !entry.endsWith('.tpl.js')) {
|
|
134
|
-
if (!shouldExcludeFile(entry, relativePath)) {
|
|
135
|
-
files.push(fullPath);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch (e) { /* dir not found */ }
|
|
140
|
-
return files;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Run cacheable per-file analyses with cache support
|
|
145
|
-
* Returns aggregated complexity, undocumented, and jsdoc results
|
|
146
|
-
* @param {string} dir
|
|
147
|
-
* @param {string} contextDir
|
|
148
|
-
* @returns {{ complexity: Object[], undocumented: Object[], jsdocIssues: Object[], cacheStats: { hits: number, misses: number } }}
|
|
149
|
-
*/
|
|
150
|
-
function runCacheableAnalyses(dir, contextDir) {
|
|
151
|
-
const resolvedDir = resolve(dir);
|
|
152
|
-
const wsRoot = getWorkspaceRoot();
|
|
153
|
-
const files = findJSFiles(dir);
|
|
154
|
-
|
|
155
|
-
const allComplexity = [];
|
|
156
|
-
const allUndocumented = [];
|
|
157
|
-
const allJsdocIssues = [];
|
|
158
|
-
let cacheHits = 0;
|
|
159
|
-
let cacheMisses = 0;
|
|
160
|
-
|
|
161
|
-
for (const file of files) {
|
|
162
|
-
const relPath = relative(resolvedDir, file);
|
|
163
|
-
// Cache key: workspace-relative (src/parser.js), matches graph paths
|
|
164
|
-
const cacheKey = relative(wsRoot, file);
|
|
165
|
-
let code;
|
|
166
|
-
try {
|
|
167
|
-
code = readFileSync(file, 'utf-8');
|
|
168
|
-
} catch (e) {
|
|
169
|
-
continue; // File deleted between findJSFiles and read
|
|
170
|
-
}
|
|
171
|
-
const contentHash = computeContentHash(code);
|
|
172
|
-
|
|
173
|
-
// Check cache (key: workspace-relative)
|
|
174
|
-
const cached = readCache(contextDir, cacheKey);
|
|
175
|
-
|
|
176
|
-
if (cached && isCacheValid(cached, cached.sig, contentHash, 'content')) {
|
|
177
|
-
// Cache hit — use cached results
|
|
178
|
-
cacheHits++;
|
|
179
|
-
if (cached.complexity) allComplexity.push(...cached.complexity);
|
|
180
|
-
if (cached.undocumented) allUndocumented.push(...cached.undocumented);
|
|
181
|
-
if (cached.jsdocIssues) allJsdocIssues.push(...cached.jsdocIssues);
|
|
182
|
-
} else {
|
|
183
|
-
// Cache miss — compute fresh
|
|
184
|
-
cacheMisses++;
|
|
185
|
-
const complexity = analyzeComplexityFile(code, relPath);
|
|
186
|
-
const undocumented = checkUndocumentedFile(code, relPath, 'tests');
|
|
187
|
-
const jsdocIssues = checkJSDocFile(code, relPath);
|
|
188
|
-
|
|
189
|
-
allComplexity.push(...complexity);
|
|
190
|
-
allUndocumented.push(...undocumented);
|
|
191
|
-
allJsdocIssues.push(...jsdocIssues);
|
|
192
|
-
|
|
193
|
-
// Save to cache (key: workspace-relative)
|
|
194
|
-
writeCache(contextDir, cacheKey, {
|
|
195
|
-
sig: cached?.sig || contentHash,
|
|
196
|
-
contentHash,
|
|
197
|
-
complexity,
|
|
198
|
-
undocumented,
|
|
199
|
-
jsdocIssues,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
complexity: allComplexity,
|
|
206
|
-
undocumented: allUndocumented,
|
|
207
|
-
jsdocIssues: allJsdocIssues,
|
|
208
|
-
cacheStats: { hits: cacheHits, misses: cacheMisses },
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Aggregate complexity items into summary format
|
|
214
|
-
* @param {Object[]} items
|
|
215
|
-
* @param {number} minComplexity
|
|
216
|
-
* @returns {Object}
|
|
217
|
-
*/
|
|
218
|
-
function aggregateComplexity(items, minComplexity = 5) {
|
|
219
|
-
let filtered = items.filter(i => i.complexity >= minComplexity);
|
|
220
|
-
filtered.sort((a, b) => b.complexity - a.complexity);
|
|
221
|
-
|
|
222
|
-
const stats = {
|
|
223
|
-
low: filtered.filter(i => i.rating === 'low').length,
|
|
224
|
-
moderate: filtered.filter(i => i.rating === 'moderate').length,
|
|
225
|
-
high: filtered.filter(i => i.rating === 'high').length,
|
|
226
|
-
critical: filtered.filter(i => i.rating === 'critical').length,
|
|
227
|
-
average: filtered.length > 0
|
|
228
|
-
? Math.round(filtered.reduce((s, i) => s + i.complexity, 0) / filtered.length * 10) / 10
|
|
229
|
-
: 0,
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
return { total: filtered.length, stats, items: filtered.slice(0, 30) };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Aggregate undocumented items into summary format
|
|
237
|
-
* @param {Object[]} items
|
|
238
|
-
* @returns {Object}
|
|
239
|
-
*/
|
|
240
|
-
function aggregateUndocumented(items) {
|
|
241
|
-
const byType = {
|
|
242
|
-
class: items.filter(i => i.type === 'class').length,
|
|
243
|
-
function: items.filter(i => i.type === 'function').length,
|
|
244
|
-
method: items.filter(i => i.type === 'method').length,
|
|
245
|
-
};
|
|
246
|
-
return { total: items.length, byType, items: items.slice(0, 20) };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Aggregate JSDoc issues into summary format
|
|
251
|
-
* @param {Object[]} issues
|
|
252
|
-
* @returns {{ issues: Object[], summary: Object }}
|
|
253
|
-
*/
|
|
254
|
-
function aggregateJSDoc(issues) {
|
|
255
|
-
const errors = issues.filter(i => i.severity === 'error').length;
|
|
256
|
-
const warnings = issues.filter(i => i.severity === 'warning').length;
|
|
257
|
-
const byFile = {};
|
|
258
|
-
for (const issue of issues) {
|
|
259
|
-
byFile[issue.file] = (byFile[issue.file] || 0) + 1;
|
|
260
|
-
}
|
|
261
|
-
return { issues, summary: { total: issues.length, errors, warnings, byFile } };
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Run full analysis on directory
|
|
266
|
-
* Uses incremental cache for per-file metrics; cross-file metrics always recompute.
|
|
267
|
-
* @param {string} dir
|
|
268
|
-
* @param {Object} [options]
|
|
269
|
-
* @param {boolean} [options.includeItems=false] - Include individual items
|
|
270
|
-
* @returns {Promise<AnalysisResult>}
|
|
271
|
-
*/
|
|
272
|
-
export async function getFullAnalysis(dir, options = {}) {
|
|
273
|
-
const includeItems = options.includeItems || false;
|
|
274
|
-
const resolvedDir = resolve(dir);
|
|
275
|
-
const contextDir = join(getWorkspaceRoot(), '.context');
|
|
276
|
-
|
|
277
|
-
// Run cacheable per-file analyses (complexity, undocumented, jsdoc)
|
|
278
|
-
const cached = runCacheableAnalyses(dir, contextDir);
|
|
279
|
-
const complexity = aggregateComplexity(cached.complexity);
|
|
280
|
-
const undocumented = aggregateUndocumented(cached.undocumented);
|
|
281
|
-
const jsdocCheck = aggregateJSDoc(cached.jsdocIssues);
|
|
282
|
-
|
|
283
|
-
// Run cross-file analyses (always dynamic — NOT cacheable per-file)
|
|
284
|
-
const [deadCode, similar, largeFiles, outdated, dbUsage] = await Promise.all([
|
|
285
|
-
getDeadCode(dir).catch(() => ({ total: 0, byType: {}, items: [] })),
|
|
286
|
-
getSimilarFunctions(dir, { threshold: 70 }).catch(() => ({ total: 0, pairs: [] })),
|
|
287
|
-
getLargeFiles(dir).catch(() => ({ total: 0, stats: {}, items: [] })),
|
|
288
|
-
getOutdatedPatterns(dir).catch(() => ({ codePatterns: [], redundantDeps: [], stats: { totalPatterns: 0, bySeverity: {}, byPattern: {}, redundantDeps: 0 } })),
|
|
289
|
-
getTableUsage(dir).catch(() => ({ tables: [], totalTables: 0, totalQueries: 0 })),
|
|
290
|
-
]);
|
|
291
|
-
|
|
292
|
-
// Calculate overall health
|
|
293
|
-
const overall = calculateHealthScore({
|
|
294
|
-
deadCode,
|
|
295
|
-
undocumented,
|
|
296
|
-
similar,
|
|
297
|
-
complexity,
|
|
298
|
-
largeFiles,
|
|
299
|
-
outdated,
|
|
300
|
-
jsdocConsistency: jsdocCheck.summary,
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
// Build result
|
|
304
|
-
const result = {
|
|
305
|
-
deadCode: {
|
|
306
|
-
total: deadCode.total,
|
|
307
|
-
byType: deadCode.byType,
|
|
308
|
-
...(includeItems && { items: deadCode.items.slice(0, 10) }),
|
|
309
|
-
},
|
|
310
|
-
undocumented: {
|
|
311
|
-
total: undocumented.total,
|
|
312
|
-
byType: undocumented.byType,
|
|
313
|
-
...(includeItems && { items: undocumented.items.slice(0, 10) }),
|
|
314
|
-
},
|
|
315
|
-
similar: {
|
|
316
|
-
total: similar.total,
|
|
317
|
-
...(includeItems && { pairs: similar.pairs.slice(0, 5) }),
|
|
318
|
-
},
|
|
319
|
-
complexity: {
|
|
320
|
-
total: complexity.total,
|
|
321
|
-
stats: complexity.stats,
|
|
322
|
-
...(includeItems && { items: complexity.items.slice(0, 10) }),
|
|
323
|
-
},
|
|
324
|
-
largeFiles: {
|
|
325
|
-
total: largeFiles.total,
|
|
326
|
-
stats: largeFiles.stats,
|
|
327
|
-
...(includeItems && { items: largeFiles.items.slice(0, 10) }),
|
|
328
|
-
},
|
|
329
|
-
outdated: {
|
|
330
|
-
totalPatterns: outdated.stats.totalPatterns,
|
|
331
|
-
redundantDeps: outdated.redundantDeps,
|
|
332
|
-
...(includeItems && { codePatterns: outdated.codePatterns.slice(0, 10) }),
|
|
333
|
-
},
|
|
334
|
-
jsdocConsistency: {
|
|
335
|
-
total: jsdocCheck.summary.total,
|
|
336
|
-
errors: jsdocCheck.summary.errors,
|
|
337
|
-
warnings: jsdocCheck.summary.warnings,
|
|
338
|
-
...(includeItems && { issues: jsdocCheck.issues.slice(0, 10) }),
|
|
339
|
-
},
|
|
340
|
-
cache: cached.cacheStats,
|
|
341
|
-
overall,
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
// Add DB metrics if any SQL interactions found (non-scoring)
|
|
345
|
-
if (dbUsage.totalTables > 0) {
|
|
346
|
-
result.database = {
|
|
347
|
-
tablesUsed: dbUsage.totalTables,
|
|
348
|
-
totalQueries: dbUsage.totalQueries,
|
|
349
|
-
tables: dbUsage.tables.map(t => ({
|
|
350
|
-
name: t.table,
|
|
351
|
-
readers: t.totalReaders,
|
|
352
|
-
writers: t.totalWriters,
|
|
353
|
-
})),
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
return result;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Quick health check — runs only cached per-file metrics, skips cross-file.
|
|
362
|
-
* @param {string} dir - Path to scan
|
|
363
|
-
* @returns {{healthScore: number, complexity: number, undocumented: number, jsdocIssues: number}}
|
|
364
|
-
*/
|
|
365
|
-
export function getAnalysisSummaryOnly(dir) {
|
|
366
|
-
const contextDir = join(getWorkspaceRoot(), '.context');
|
|
367
|
-
const cached = runCacheableAnalyses(dir, contextDir);
|
|
368
|
-
const complexity = aggregateComplexity(cached.complexity);
|
|
369
|
-
const undocumented = aggregateUndocumented(cached.undocumented);
|
|
370
|
-
const jsdocCheck = aggregateJSDoc(cached.jsdocIssues);
|
|
371
|
-
|
|
372
|
-
// Reuse the same health score formula as getFullAnalysis
|
|
373
|
-
const overall = calculateHealthScore({
|
|
374
|
-
deadCode: { total: 0 },
|
|
375
|
-
undocumented,
|
|
376
|
-
similar: { total: 0 },
|
|
377
|
-
complexity,
|
|
378
|
-
largeFiles: { total: 0 },
|
|
379
|
-
outdated: { stats: { totalPatterns: 0 } },
|
|
380
|
-
jsdocConsistency: jsdocCheck.summary,
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
healthScore: overall.score,
|
|
385
|
-
grade: overall.rating,
|
|
386
|
-
complexity: complexity.total,
|
|
387
|
-
undocumented: undocumented.total,
|
|
388
|
-
jsdocIssues: jsdocCheck.summary.total,
|
|
389
|
-
cache: cached.cacheStats,
|
|
390
|
-
note: 'Partial score — cross-file analyses skipped for speed. Run get_full_analysis for complete health check.',
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Streaming analysis — yields results as each sub-analysis completes.
|
|
396
|
-
* Useful for large codebases where waiting for all analyses is too slow.
|
|
397
|
-
* @param {string} dir - Path to scan
|
|
398
|
-
* @param {Object} [options]
|
|
399
|
-
* @param {boolean} [options.includeItems=false]
|
|
400
|
-
* @returns {AsyncGenerator<{type: string, data: Object}>}
|
|
401
|
-
*/
|
|
402
|
-
export async function* getFullAnalysisStreaming(dir, options = {}) {
|
|
403
|
-
const includeItems = options.includeItems || false;
|
|
404
|
-
const contextDir = join(getWorkspaceRoot(), '.context');
|
|
405
|
-
|
|
406
|
-
// Phase 1: Cached per-file analyses (fast)
|
|
407
|
-
const cached = runCacheableAnalyses(dir, contextDir);
|
|
408
|
-
|
|
409
|
-
const complexity = aggregateComplexity(cached.complexity);
|
|
410
|
-
yield { type: 'complexity', data: {
|
|
411
|
-
total: complexity.total,
|
|
412
|
-
stats: complexity.stats,
|
|
413
|
-
...(includeItems && { items: complexity.items.slice(0, 10) }),
|
|
414
|
-
}};
|
|
415
|
-
|
|
416
|
-
const undocumented = aggregateUndocumented(cached.undocumented);
|
|
417
|
-
yield { type: 'undocumented', data: {
|
|
418
|
-
total: undocumented.total,
|
|
419
|
-
byType: undocumented.byType,
|
|
420
|
-
...(includeItems && { items: undocumented.items.slice(0, 10) }),
|
|
421
|
-
}};
|
|
422
|
-
|
|
423
|
-
const jsdocCheck = aggregateJSDoc(cached.jsdocIssues);
|
|
424
|
-
yield { type: 'jsdocConsistency', data: {
|
|
425
|
-
total: jsdocCheck.summary.total,
|
|
426
|
-
errors: jsdocCheck.summary.errors,
|
|
427
|
-
warnings: jsdocCheck.summary.warnings,
|
|
428
|
-
...(includeItems && { issues: jsdocCheck.issues.slice(0, 10) }),
|
|
429
|
-
}};
|
|
430
|
-
|
|
431
|
-
yield { type: 'cache', data: cached.cacheStats };
|
|
432
|
-
|
|
433
|
-
// Phase 2: Cross-file analyses (slow, one at a time)
|
|
434
|
-
try {
|
|
435
|
-
const deadCode = await getDeadCode(dir);
|
|
436
|
-
yield { type: 'deadCode', data: {
|
|
437
|
-
total: deadCode.total,
|
|
438
|
-
byType: deadCode.byType,
|
|
439
|
-
...(includeItems && { items: deadCode.items.slice(0, 10) }),
|
|
440
|
-
}};
|
|
441
|
-
} catch { yield { type: 'deadCode', data: { total: 0, byType: {}, error: 'analysis failed' } }; }
|
|
442
|
-
|
|
443
|
-
try {
|
|
444
|
-
const similar = await getSimilarFunctions(dir, { threshold: 70 });
|
|
445
|
-
yield { type: 'similar', data: {
|
|
446
|
-
total: similar.total,
|
|
447
|
-
...(includeItems && { pairs: similar.pairs.slice(0, 5) }),
|
|
448
|
-
}};
|
|
449
|
-
} catch { yield { type: 'similar', data: { total: 0, error: 'analysis failed' } }; }
|
|
450
|
-
|
|
451
|
-
try {
|
|
452
|
-
const largeFiles = await getLargeFiles(dir);
|
|
453
|
-
yield { type: 'largeFiles', data: {
|
|
454
|
-
total: largeFiles.total,
|
|
455
|
-
stats: largeFiles.stats,
|
|
456
|
-
...(includeItems && { items: largeFiles.items.slice(0, 10) }),
|
|
457
|
-
}};
|
|
458
|
-
} catch { yield { type: 'largeFiles', data: { total: 0, error: 'analysis failed' } }; }
|
|
459
|
-
|
|
460
|
-
try {
|
|
461
|
-
const outdated = await getOutdatedPatterns(dir);
|
|
462
|
-
yield { type: 'outdated', data: {
|
|
463
|
-
totalPatterns: outdated.stats.totalPatterns,
|
|
464
|
-
redundantDeps: outdated.redundantDeps,
|
|
465
|
-
...(includeItems && { codePatterns: outdated.codePatterns.slice(0, 10) }),
|
|
466
|
-
}};
|
|
467
|
-
} catch { yield { type: 'outdated', data: { totalPatterns: 0, error: 'analysis failed' } }; }
|
|
468
|
-
|
|
469
|
-
yield { type: 'done', data: { phases: 2, timestamp: new Date().toISOString() } };
|
|
470
|
-
}
|