projscan 0.9.0 → 0.9.2
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 +72 -72
- package/dist/analyzers/deadCodeCheck.d.ts +2 -2
- package/dist/analyzers/deadCodeCheck.js +4 -4
- package/dist/analyzers/unusedDependencyCheck.js +1 -1
- package/dist/cli/index.js +11 -11
- package/dist/core/ast.d.ts +1 -1
- package/dist/core/ast.js +2 -2
- package/dist/core/auditRunner.d.ts +1 -1
- package/dist/core/auditRunner.js +6 -6
- package/dist/core/coverageJoin.d.ts +1 -1
- package/dist/core/coverageJoin.js +1 -1
- package/dist/core/coverageParser.js +3 -3
- package/dist/core/dependencyAnalyzer.js +6 -6
- package/dist/core/embeddings.d.ts +1 -1
- package/dist/core/embeddings.js +2 -2
- package/dist/core/fileInspector.js +6 -6
- package/dist/core/hotspotAnalyzer.js +2 -2
- package/dist/core/importGraph.d.ts +1 -1
- package/dist/core/importGraph.js +1 -1
- package/dist/core/indexCache.d.ts +2 -2
- package/dist/core/indexCache.js +4 -4
- package/dist/core/outdatedDetector.d.ts +1 -1
- package/dist/core/outdatedDetector.js +1 -1
- package/dist/core/searchIndex.js +2 -2
- package/dist/core/semanticSearch.d.ts +1 -1
- package/dist/core/semanticSearch.js +2 -2
- package/dist/core/upgradePreview.d.ts +12 -0
- package/dist/core/upgradePreview.js +55 -3
- package/dist/core/upgradePreview.js.map +1 -1
- package/dist/fixes/prettierFix.js +1 -1
- package/dist/fixes/testFix.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/chunker.d.ts +1 -1
- package/dist/mcp/chunker.js +1 -1
- package/dist/mcp/pagination.d.ts +2 -2
- package/dist/mcp/pagination.js +2 -2
- package/dist/mcp/progress.d.ts +1 -1
- package/dist/mcp/prompts.js +3 -3
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/tokenBudget.d.ts +1 -1
- package/dist/mcp/tokenBudget.js +2 -2
- package/dist/mcp/tools.js +8 -8
- package/dist/reporters/consoleReporter.js +11 -11
- package/dist/reporters/markdownReporter.js +14 -14
- package/dist/reporters/sarifReporter.js +1 -1
- package/dist/utils/banner.d.ts +3 -3
- package/dist/utils/banner.js +9 -9
- package/dist/utils/config.js +1 -1
- package/dist/utils/packageJsonLocator.d.ts +1 -1
- package/dist/utils/packageJsonLocator.js +1 -1
- package/package.json +2 -2
package/dist/mcp/tools.js
CHANGED
|
@@ -197,7 +197,7 @@ const tools = [
|
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
199
|
name: 'projscan_outdated',
|
|
200
|
-
description: 'Compare declared vs installed versions of every package. Reports drift (patch/minor/major). Offline
|
|
200
|
+
description: 'Compare declared vs installed versions of every package. Reports drift (patch/minor/major). Offline - does not hit the npm registry. Supports cursor pagination.',
|
|
201
201
|
inputSchema: {
|
|
202
202
|
type: 'object',
|
|
203
203
|
properties: {
|
|
@@ -271,7 +271,7 @@ const tools = [
|
|
|
271
271
|
},
|
|
272
272
|
{
|
|
273
273
|
name: 'projscan_coverage',
|
|
274
|
-
description: 'Join test coverage with hotspot risk. Returns files ranked by "risk × uncovered fraction"
|
|
274
|
+
description: 'Join test coverage with hotspot risk. Returns files ranked by "risk × uncovered fraction" - the scariest untested files. Requires a coverage file at coverage/lcov.info, coverage/coverage-final.json, or coverage/coverage-summary.json.',
|
|
275
275
|
inputSchema: {
|
|
276
276
|
type: 'object',
|
|
277
277
|
properties: {
|
|
@@ -311,7 +311,7 @@ const tools = [
|
|
|
311
311
|
},
|
|
312
312
|
{
|
|
313
313
|
name: 'projscan_graph',
|
|
314
|
-
description: 'Query the AST-based code graph directly. Returns imports, exports, importers, or symbol definitions for a file or symbol. Agents should prefer this over analyze/doctor/explain for targeted structural questions
|
|
314
|
+
description: 'Query the AST-based code graph directly. Returns imports, exports, importers, or symbol definitions for a file or symbol. Agents should prefer this over analyze/doctor/explain for targeted structural questions - it is much cheaper and more accurate.',
|
|
315
315
|
inputSchema: {
|
|
316
316
|
type: 'object',
|
|
317
317
|
properties: {
|
|
@@ -421,7 +421,7 @@ const tools = [
|
|
|
421
421
|
const cached = await loadCachedGraph(rootPath);
|
|
422
422
|
const graph = await buildCodeGraph(rootPath, scan.files, cached);
|
|
423
423
|
await saveCachedGraph(rootPath, graph);
|
|
424
|
-
// Files scope
|
|
424
|
+
// Files scope - simple substring scan; ranking adds no value
|
|
425
425
|
if (scope === 'files') {
|
|
426
426
|
const q = query.toLowerCase();
|
|
427
427
|
const all = scan.files
|
|
@@ -430,7 +430,7 @@ const tools = [
|
|
|
430
430
|
const page = paginate(all, readPageParams(args), listChecksum(all));
|
|
431
431
|
return { scope, query, matches: page.items, total: page.total, nextCursor: page.nextCursor };
|
|
432
432
|
}
|
|
433
|
-
// Symbols scope
|
|
433
|
+
// Symbols scope - walk the graph's export table; rank exact/prefix/substring
|
|
434
434
|
if (scope === 'symbols') {
|
|
435
435
|
const q = query.toLowerCase();
|
|
436
436
|
const rawMatches = [];
|
|
@@ -453,7 +453,7 @@ const tools = [
|
|
|
453
453
|
const page = paginate(cleaned, readPageParams(args), listChecksum(cleaned));
|
|
454
454
|
return { scope, query, matches: page.items, total: page.total, nextCursor: page.nextCursor };
|
|
455
455
|
}
|
|
456
|
-
// Content or auto scope
|
|
456
|
+
// Content or auto scope - lexical BM25 by default, optionally semantic or hybrid
|
|
457
457
|
const mode = String(args.mode ?? 'lexical');
|
|
458
458
|
const index = await buildSearchIndex(rootPath, scan.files, graph);
|
|
459
459
|
const lexicalHits = searchIndex(index, query, { limit });
|
|
@@ -471,7 +471,7 @@ const tools = [
|
|
|
471
471
|
nextCursor: page.nextCursor,
|
|
472
472
|
};
|
|
473
473
|
}
|
|
474
|
-
// Semantic or hybrid
|
|
474
|
+
// Semantic or hybrid - both require the peer
|
|
475
475
|
const hasSemantic = await isSemanticAvailable();
|
|
476
476
|
if (!hasSemantic) {
|
|
477
477
|
return {
|
|
@@ -518,7 +518,7 @@ const tools = [
|
|
|
518
518
|
nextCursor: page.nextCursor,
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
|
-
// Hybrid
|
|
521
|
+
// Hybrid - reciprocal rank fusion
|
|
522
522
|
const fused = reciprocalRankFusion([lexicalHits, semHits]).slice(0, limit);
|
|
523
523
|
const enriched = await attachExcerpts(rootPath, fused.map((f) => ({
|
|
524
524
|
file: f.file,
|
|
@@ -127,7 +127,7 @@ export function reportCi(issues, threshold) {
|
|
|
127
127
|
const pass = score >= threshold;
|
|
128
128
|
const status = pass ? chalk.green('PASS') : chalk.red('FAIL');
|
|
129
129
|
const gradeColor = grade === 'A' || grade === 'B' ? chalk.green : grade === 'C' ? chalk.yellow : chalk.red;
|
|
130
|
-
console.log(`projscan: ${gradeColor(chalk.bold(`${grade} (${score}/100)`))}
|
|
130
|
+
console.log(`projscan: ${gradeColor(chalk.bold(`${grade} (${score}/100)`))} - ${errors} error${errors !== 1 ? 's' : ''}, ${warnings} warning${warnings !== 1 ? 's' : ''}, ${infos} info - ${status} (threshold: ${threshold})`);
|
|
131
131
|
if (!pass) {
|
|
132
132
|
for (const issue of issues) {
|
|
133
133
|
console.log(` ${severityIcon(issue.severity)} ${issue.title}`);
|
|
@@ -137,20 +137,20 @@ export function reportCi(issues, threshold) {
|
|
|
137
137
|
// ── Report: diff ──────────────────────────────────────────
|
|
138
138
|
export function reportDiff(diff) {
|
|
139
139
|
console.log(header('Health Diff'));
|
|
140
|
-
const arrow = diff.scoreDelta > 0 ? chalk.green('↑') : diff.scoreDelta < 0 ? chalk.red('↓') : chalk.dim('
|
|
140
|
+
const arrow = diff.scoreDelta > 0 ? chalk.green('↑') : diff.scoreDelta < 0 ? chalk.red('↓') : chalk.dim('-');
|
|
141
141
|
const delta = diff.scoreDelta > 0 ? `+${diff.scoreDelta}` : String(diff.scoreDelta);
|
|
142
142
|
console.log(`\n Score: ${diff.before.score} → ${diff.after.score} (${delta}) ${arrow}`);
|
|
143
143
|
console.log(` Grade: ${diff.before.grade} → ${diff.after.grade}`);
|
|
144
144
|
if (diff.resolvedIssues.length > 0) {
|
|
145
145
|
console.log(`\n ${chalk.green('✓')} Resolved (${diff.resolvedIssues.length}):`);
|
|
146
146
|
for (const title of diff.resolvedIssues) {
|
|
147
|
-
console.log(` ${chalk.green('
|
|
147
|
+
console.log(` ${chalk.green('-')} ${title}`);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
if (diff.newIssues.length > 0) {
|
|
151
151
|
console.log(`\n ${chalk.red('✗')} New (${diff.newIssues.length}):`);
|
|
152
152
|
for (const title of diff.newIssues) {
|
|
153
|
-
console.log(` ${chalk.red('
|
|
153
|
+
console.log(` ${chalk.red('-')} ${title}`);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
if (diff.resolvedIssues.length === 0 && diff.newIssues.length === 0) {
|
|
@@ -182,7 +182,7 @@ export function reportDiff(diff) {
|
|
|
182
182
|
if (hd.resolved.length > 0) {
|
|
183
183
|
console.log(`\n ${chalk.green('✓')} No longer tracked (${hd.resolved.length}):`);
|
|
184
184
|
for (const delta of hd.resolved.slice(0, 5)) {
|
|
185
|
-
console.log(` ${chalk.green('
|
|
185
|
+
console.log(` ${chalk.green('-')} ${delta.relativePath}`);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
}
|
|
@@ -209,7 +209,7 @@ export function reportFixResults(results) {
|
|
|
209
209
|
console.log(` ${chalk.green('✔')} ${result.fix.title}`);
|
|
210
210
|
}
|
|
211
211
|
else {
|
|
212
|
-
console.log(` ${chalk.red('✗')} ${result.fix.title}
|
|
212
|
+
console.log(` ${chalk.red('✗')} ${result.fix.title} - ${chalk.dim(result.error ?? 'unknown error')}`);
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
console.log('');
|
|
@@ -356,7 +356,7 @@ export function reportFileInspection(insp) {
|
|
|
356
356
|
console.log(` ${chalk.bold('Last change:')} ${h.daysSinceLastChange} days ago`);
|
|
357
357
|
}
|
|
358
358
|
if (h.busFactorOne) {
|
|
359
|
-
console.log(` ${chalk.red('⚠')} Bus factor 1
|
|
359
|
+
console.log(` ${chalk.red('⚠')} Bus factor 1 - only one author has touched this.`);
|
|
360
360
|
}
|
|
361
361
|
if (h.reasons.length > 0) {
|
|
362
362
|
console.log(` ${chalk.dim(h.reasons.join(', '))}`);
|
|
@@ -493,10 +493,10 @@ export function reportUpgrade(preview) {
|
|
|
493
493
|
console.log(chalk.yellow(`\n ${preview.reason ?? 'Upgrade preview unavailable'}\n`));
|
|
494
494
|
return;
|
|
495
495
|
}
|
|
496
|
-
console.log(header(`Upgrade Preview
|
|
496
|
+
console.log(header(`Upgrade Preview - ${preview.name}`));
|
|
497
497
|
const drift = DRIFT_COLORS[preview.drift] ?? chalk.dim;
|
|
498
|
-
console.log(` Declared: ${chalk.dim(preview.declared ?? '
|
|
499
|
-
console.log(` Installed: ${chalk.bold(preview.installed ?? '
|
|
498
|
+
console.log(` Declared: ${chalk.dim(preview.declared ?? '-')}`);
|
|
499
|
+
console.log(` Installed: ${chalk.bold(preview.installed ?? '-')}`);
|
|
500
500
|
console.log(` Drift: ${drift(preview.drift.toUpperCase())}`);
|
|
501
501
|
console.log('');
|
|
502
502
|
if (preview.breakingMarkers.length > 0) {
|
|
@@ -539,7 +539,7 @@ export function reportCoverage(report) {
|
|
|
539
539
|
console.log(chalk.yellow(`\n ${report.reason ?? 'Coverage report unavailable'}\n`));
|
|
540
540
|
return;
|
|
541
541
|
}
|
|
542
|
-
console.log(header('Coverage × Hotspots
|
|
542
|
+
console.log(header('Coverage × Hotspots - "Scariest Untested Files"'));
|
|
543
543
|
const src = report.coverageSourceFile ? ` (${report.coverageSourceFile})` : '';
|
|
544
544
|
console.log(chalk.dim(` Source: ${report.coverageSource}${src}`));
|
|
545
545
|
console.log('');
|
|
@@ -55,7 +55,7 @@ export function reportHealthMarkdown(issues) {
|
|
|
55
55
|
lines.push('');
|
|
56
56
|
for (const issue of issues) {
|
|
57
57
|
const icon = issue.severity === 'error' ? '❌' : issue.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
58
|
-
lines.push(`- ${icon} **${issue.title}**
|
|
58
|
+
lines.push(`- ${icon} **${issue.title}** - ${issue.description}`);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
console.log(lines.join('\n'));
|
|
@@ -64,7 +64,7 @@ export function reportCiMarkdown(issues, threshold) {
|
|
|
64
64
|
const { score, grade } = calculateScore(issues);
|
|
65
65
|
const pass = score >= threshold;
|
|
66
66
|
const lines = [
|
|
67
|
-
`# Projscan CI
|
|
67
|
+
`# Projscan CI - ${pass ? 'PASS' : 'FAIL'}`,
|
|
68
68
|
'',
|
|
69
69
|
`| Metric | Value |`,
|
|
70
70
|
`| --- | --- |`,
|
|
@@ -77,14 +77,14 @@ export function reportCiMarkdown(issues, threshold) {
|
|
|
77
77
|
lines.push('', '## Issues', '');
|
|
78
78
|
for (const issue of issues) {
|
|
79
79
|
const icon = issue.severity === 'error' ? '❌' : issue.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
80
|
-
lines.push(`- ${icon} **${issue.title}**
|
|
80
|
+
lines.push(`- ${icon} **${issue.title}** - ${issue.description}`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
console.log(lines.join('\n'));
|
|
84
84
|
}
|
|
85
85
|
export function reportDiffMarkdown(diff) {
|
|
86
86
|
const delta = diff.scoreDelta > 0 ? `+${diff.scoreDelta}` : String(diff.scoreDelta);
|
|
87
|
-
const arrow = diff.scoreDelta > 0 ? '↑' : diff.scoreDelta < 0 ? '↓' : '
|
|
87
|
+
const arrow = diff.scoreDelta > 0 ? '↑' : diff.scoreDelta < 0 ? '↓' : '-';
|
|
88
88
|
const lines = [
|
|
89
89
|
'# Health Diff',
|
|
90
90
|
'',
|
|
@@ -224,7 +224,7 @@ export function reportFileMarkdown(insp) {
|
|
|
224
224
|
if (h.daysSinceLastChange !== null)
|
|
225
225
|
lines.push(`- **Last change:** ${h.daysSinceLastChange} days ago`);
|
|
226
226
|
if (h.busFactorOne)
|
|
227
|
-
lines.push('- ⚠️ **Bus factor 1**
|
|
227
|
+
lines.push('- ⚠️ **Bus factor 1** - only one author has touched this.');
|
|
228
228
|
if (h.reasons.length > 0)
|
|
229
229
|
lines.push(`- ${h.reasons.join(', ')}`);
|
|
230
230
|
}
|
|
@@ -232,7 +232,7 @@ export function reportFileMarkdown(insp) {
|
|
|
232
232
|
lines.push('', '## Related Issues', '');
|
|
233
233
|
for (const issue of insp.issues) {
|
|
234
234
|
const icon = issue.severity === 'error' ? '❌' : issue.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
235
|
-
lines.push(`- ${icon} **${issue.title}**
|
|
235
|
+
lines.push(`- ${icon} **${issue.title}** - ${issue.description}`);
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
if (insp.potentialIssues.length > 0) {
|
|
@@ -273,7 +273,7 @@ export function reportHotspotsMarkdown(report) {
|
|
|
273
273
|
lines.push('| --- | ---: | --- | ---: | ---: | ---: | --- |');
|
|
274
274
|
for (let i = 0; i < report.hotspots.length; i++) {
|
|
275
275
|
const h = report.hotspots[i];
|
|
276
|
-
const reasons = h.reasons.length > 0 ? h.reasons.join(', ') : '
|
|
276
|
+
const reasons = h.reasons.length > 0 ? h.reasons.join(', ') : '-';
|
|
277
277
|
lines.push(`| ${i + 1} | ${h.riskScore.toFixed(1)} | \`${h.relativePath}\` | ${h.churn} | ${h.lineCount} | ${h.issueCount} | ${reasons} |`);
|
|
278
278
|
}
|
|
279
279
|
console.log(lines.join('\n'));
|
|
@@ -298,7 +298,7 @@ export function reportOutdatedMarkdown(report) {
|
|
|
298
298
|
lines.push('| Package | Scope | Declared | Installed | Drift |');
|
|
299
299
|
lines.push('| --- | --- | --- | --- | --- |');
|
|
300
300
|
for (const p of drifting) {
|
|
301
|
-
lines.push(`| \`${p.name}\` | ${p.scope === 'devDependency' ? 'dev' : 'prod'} | ${p.declared} | ${p.installed ?? '
|
|
301
|
+
lines.push(`| \`${p.name}\` | ${p.scope === 'devDependency' ? 'dev' : 'prod'} | ${p.declared} | ${p.installed ?? '-'} | ${p.drift} |`);
|
|
302
302
|
}
|
|
303
303
|
console.log(lines.join('\n'));
|
|
304
304
|
}
|
|
@@ -313,7 +313,7 @@ export function reportAuditMarkdown(report) {
|
|
|
313
313
|
}
|
|
314
314
|
const s = report.summary;
|
|
315
315
|
const total = s.critical + s.high + s.moderate + s.low + s.info;
|
|
316
|
-
lines.push(`**${total}** findings
|
|
316
|
+
lines.push(`**${total}** findings - ${s.critical} critical · ${s.high} high · ${s.moderate} moderate · ${s.low} low · ${s.info} info`);
|
|
317
317
|
lines.push('');
|
|
318
318
|
if (report.findings.length === 0) {
|
|
319
319
|
lines.push('_No known vulnerabilities._');
|
|
@@ -330,15 +330,15 @@ export function reportAuditMarkdown(report) {
|
|
|
330
330
|
}
|
|
331
331
|
export function reportUpgradeMarkdown(preview) {
|
|
332
332
|
const lines = [];
|
|
333
|
-
lines.push(`# Upgrade Preview
|
|
333
|
+
lines.push(`# Upgrade Preview - \`${preview.name}\``);
|
|
334
334
|
lines.push('');
|
|
335
335
|
if (!preview.available) {
|
|
336
336
|
lines.push(`_${preview.reason ?? 'unavailable'}_`);
|
|
337
337
|
console.log(lines.join('\n'));
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
340
|
-
lines.push(`- Declared: \`${preview.declared ?? '
|
|
341
|
-
lines.push(`- Installed: \`${preview.installed ?? '
|
|
340
|
+
lines.push(`- Declared: \`${preview.declared ?? '-'}\``);
|
|
341
|
+
lines.push(`- Installed: \`${preview.installed ?? '-'}\``);
|
|
342
342
|
lines.push(`- Drift: **${preview.drift}**`);
|
|
343
343
|
lines.push('');
|
|
344
344
|
if (preview.breakingMarkers.length > 0) {
|
|
@@ -383,8 +383,8 @@ export function reportCoverageMarkdown(report) {
|
|
|
383
383
|
lines.push('| Priority | Coverage | Risk | Churn | File | Reasons |');
|
|
384
384
|
lines.push('| ---: | ---: | ---: | ---: | --- | --- |');
|
|
385
385
|
for (const e of report.entries) {
|
|
386
|
-
const cov = e.coverage === null ? '
|
|
387
|
-
const reasons = e.reasons.length > 0 ? e.reasons.join(', ') : '
|
|
386
|
+
const cov = e.coverage === null ? '-' : `${e.coverage.toFixed(0)}%`;
|
|
387
|
+
const reasons = e.reasons.length > 0 ? e.reasons.join(', ') : '-';
|
|
388
388
|
lines.push(`| ${e.priority.toFixed(1)} | ${cov} | ${e.riskScore.toFixed(1)} | ${e.churn} | \`${e.relativePath}\` | ${reasons} |`);
|
|
389
389
|
}
|
|
390
390
|
console.log(lines.join('\n'));
|
|
@@ -2,7 +2,7 @@ const TOOL_INFO = {
|
|
|
2
2
|
name: 'projscan',
|
|
3
3
|
informationUri: 'https://github.com/abhiyoheswaran1/projscan',
|
|
4
4
|
shortDescription: {
|
|
5
|
-
text: 'Instant codebase insights
|
|
5
|
+
text: 'Instant codebase insights - doctor, x-ray, and architecture map.',
|
|
6
6
|
},
|
|
7
7
|
};
|
|
8
8
|
const SEVERITY_TO_LEVEL = {
|
package/dist/utils/banner.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Full two-panel welcome screen
|
|
2
|
+
* Full two-panel welcome screen - shown only on the default `projscan` command.
|
|
3
3
|
* Left panel: ASCII logo + version. Right panel: commands + what's new.
|
|
4
4
|
*/
|
|
5
5
|
export declare function showBanner(): void;
|
|
6
6
|
/**
|
|
7
|
-
* Compact one-liner
|
|
7
|
+
* Compact one-liner - shown on subcommands (doctor, fix, etc.).
|
|
8
8
|
*/
|
|
9
9
|
export declare function showCompactBanner(): void;
|
|
10
10
|
/**
|
|
11
|
-
* Help screen
|
|
11
|
+
* Help screen - shown by `projscan help`.
|
|
12
12
|
* Displays the full banner + detailed command reference.
|
|
13
13
|
*/
|
|
14
14
|
export declare function showHelp(): void;
|
package/dist/utils/banner.js
CHANGED
|
@@ -14,7 +14,7 @@ function getVersion() {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Full two-panel welcome screen
|
|
17
|
+
* Full two-panel welcome screen - shown only on the default `projscan` command.
|
|
18
18
|
* Left panel: ASCII logo + version. Right panel: commands + what's new.
|
|
19
19
|
*/
|
|
20
20
|
export function showBanner() {
|
|
@@ -74,7 +74,7 @@ export function showBanner() {
|
|
|
74
74
|
console.log('');
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
* Compact one-liner
|
|
77
|
+
* Compact one-liner - shown on subcommands (doctor, fix, etc.).
|
|
78
78
|
*/
|
|
79
79
|
export function showCompactBanner() {
|
|
80
80
|
const version = getVersion();
|
|
@@ -82,7 +82,7 @@ export function showCompactBanner() {
|
|
|
82
82
|
console.log(` ${chalk.cyan('◆')} ${chalk.white.bold('ProjScan')} ${chalk.dim(`v${version}`)}`);
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
* Help screen
|
|
85
|
+
* Help screen - shown by `projscan help`.
|
|
86
86
|
* Displays the full banner + detailed command reference.
|
|
87
87
|
*/
|
|
88
88
|
export function showHelp() {
|
|
@@ -93,26 +93,26 @@ export function showHelp() {
|
|
|
93
93
|
const g = chalk.gray;
|
|
94
94
|
const commands = [
|
|
95
95
|
{ cmd: 'projscan', desc: 'Full project analysis (default)' },
|
|
96
|
-
{ cmd: 'projscan doctor', desc: 'Health check
|
|
97
|
-
{ cmd: 'projscan hotspots', desc: 'Rank files by risk
|
|
96
|
+
{ cmd: 'projscan doctor', desc: 'Health check - detect issues and score your project' },
|
|
97
|
+
{ cmd: 'projscan hotspots', desc: 'Rank files by risk - churn × complexity × issues × ownership' },
|
|
98
98
|
{ cmd: 'projscan search <query>', desc: 'BM25-ranked search across content, symbols, and paths' },
|
|
99
|
-
{ cmd: 'projscan file <path>', desc: 'Drill into a file
|
|
99
|
+
{ cmd: 'projscan file <path>', desc: 'Drill into a file - purpose, risk, ownership, issues' },
|
|
100
100
|
{ cmd: 'projscan fix', desc: 'Auto-fix detected issues (interactive)' },
|
|
101
101
|
{ cmd: 'projscan fix -y', desc: 'Auto-fix without prompting' },
|
|
102
|
-
{ cmd: 'projscan ci', desc: 'CI gate
|
|
102
|
+
{ cmd: 'projscan ci', desc: 'CI gate - exit 1 if score below threshold' },
|
|
103
103
|
{ cmd: 'projscan ci --min-score 80', desc: 'Set custom minimum score' },
|
|
104
104
|
{ cmd: 'projscan ci --changed-only', desc: 'Gate only on issues in this PR\'s diff' },
|
|
105
105
|
{ cmd: 'projscan ci --format sarif', desc: 'Emit SARIF 2.1.0 for GitHub Code Scanning' },
|
|
106
106
|
{ cmd: 'projscan diff', desc: 'Compare current health against saved baseline' },
|
|
107
107
|
{ cmd: 'projscan diff --save-baseline', desc: 'Save current state as baseline' },
|
|
108
|
-
{ cmd: 'projscan explain <file>', desc: 'Explain a file
|
|
108
|
+
{ cmd: 'projscan explain <file>', desc: 'Explain a file - purpose, imports, exports' },
|
|
109
109
|
{ cmd: 'projscan diagram', desc: 'Show architecture layer diagram' },
|
|
110
110
|
{ cmd: 'projscan structure', desc: 'Show directory structure overview' },
|
|
111
111
|
{ cmd: 'projscan dependencies', desc: 'Analyze project dependencies' },
|
|
112
112
|
{ cmd: 'projscan outdated', desc: 'Declared-vs-installed drift (offline)' },
|
|
113
113
|
{ cmd: 'projscan audit', desc: 'Run npm audit; SARIF-ready vulnerability report' },
|
|
114
114
|
{ cmd: 'projscan upgrade <pkg>', desc: 'Preview upgrade impact (CHANGELOG + importers, offline)' },
|
|
115
|
-
{ cmd: 'projscan coverage', desc: 'Coverage × hotspots
|
|
115
|
+
{ cmd: 'projscan coverage', desc: 'Coverage × hotspots - surface scariest untested files' },
|
|
116
116
|
{ cmd: 'projscan badge', desc: 'Generate a health badge for your README' },
|
|
117
117
|
{ cmd: 'projscan mcp', desc: 'Run as MCP server for AI agents (Claude Code, Cursor, …)' },
|
|
118
118
|
];
|
package/dist/utils/config.js
CHANGED
|
@@ -19,7 +19,7 @@ export async function loadConfig(rootPath, explicitPath) {
|
|
|
19
19
|
raw = await fs.readFile(candidate, 'utf-8');
|
|
20
20
|
}
|
|
21
21
|
catch {
|
|
22
|
-
// File not present
|
|
22
|
+
// File not present - try next candidate.
|
|
23
23
|
continue;
|
|
24
24
|
}
|
|
25
25
|
const parsed = safeParse(raw, candidate);
|
|
@@ -5,7 +5,7 @@ export interface PackageJsonLocations {
|
|
|
5
5
|
/**
|
|
6
6
|
* Parse package.json and find the line number of each dependency name
|
|
7
7
|
* (within dependencies, devDependencies, peerDependencies, optionalDependencies).
|
|
8
|
-
* Line numbers are 1-based. Uses regex against the raw text
|
|
8
|
+
* Line numbers are 1-based. Uses regex against the raw text - robust enough
|
|
9
9
|
* for typical formatted package.json files.
|
|
10
10
|
*/
|
|
11
11
|
export declare function findDependencyLines(rootPath: string): Promise<PackageJsonLocations | null>;
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
/**
|
|
4
4
|
* Parse package.json and find the line number of each dependency name
|
|
5
5
|
* (within dependencies, devDependencies, peerDependencies, optionalDependencies).
|
|
6
|
-
* Line numbers are 1-based. Uses regex against the raw text
|
|
6
|
+
* Line numbers are 1-based. Uses regex against the raw text - robust enough
|
|
7
7
|
* for typical formatted package.json files.
|
|
8
8
|
*/
|
|
9
9
|
export async function findDependencyLines(rootPath) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projscan",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "Agent-first code intelligence
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing, code graph, BM25 + optional semantic search, cursor pagination, progress notifications, and context-budgeted output. CLI on the side.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|