projscan 0.9.0 → 0.9.1
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.js +1 -1
- package/dist/fixes/prettierFix.js +1 -1
- package/dist/fixes/testFix.js +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
|
@@ -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.1",
|
|
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",
|