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.
Files changed (53) hide show
  1. package/README.md +72 -72
  2. package/dist/analyzers/deadCodeCheck.d.ts +2 -2
  3. package/dist/analyzers/deadCodeCheck.js +4 -4
  4. package/dist/analyzers/unusedDependencyCheck.js +1 -1
  5. package/dist/cli/index.js +11 -11
  6. package/dist/core/ast.d.ts +1 -1
  7. package/dist/core/ast.js +2 -2
  8. package/dist/core/auditRunner.d.ts +1 -1
  9. package/dist/core/auditRunner.js +6 -6
  10. package/dist/core/coverageJoin.d.ts +1 -1
  11. package/dist/core/coverageJoin.js +1 -1
  12. package/dist/core/coverageParser.js +3 -3
  13. package/dist/core/dependencyAnalyzer.js +6 -6
  14. package/dist/core/embeddings.d.ts +1 -1
  15. package/dist/core/embeddings.js +2 -2
  16. package/dist/core/fileInspector.js +6 -6
  17. package/dist/core/hotspotAnalyzer.js +2 -2
  18. package/dist/core/importGraph.d.ts +1 -1
  19. package/dist/core/importGraph.js +1 -1
  20. package/dist/core/indexCache.d.ts +2 -2
  21. package/dist/core/indexCache.js +4 -4
  22. package/dist/core/outdatedDetector.d.ts +1 -1
  23. package/dist/core/outdatedDetector.js +1 -1
  24. package/dist/core/searchIndex.js +2 -2
  25. package/dist/core/semanticSearch.d.ts +1 -1
  26. package/dist/core/semanticSearch.js +2 -2
  27. package/dist/core/upgradePreview.d.ts +12 -0
  28. package/dist/core/upgradePreview.js +55 -3
  29. package/dist/core/upgradePreview.js.map +1 -1
  30. package/dist/fixes/prettierFix.js +1 -1
  31. package/dist/fixes/testFix.js +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp/chunker.d.ts +1 -1
  36. package/dist/mcp/chunker.js +1 -1
  37. package/dist/mcp/pagination.d.ts +2 -2
  38. package/dist/mcp/pagination.js +2 -2
  39. package/dist/mcp/progress.d.ts +1 -1
  40. package/dist/mcp/prompts.js +3 -3
  41. package/dist/mcp/server.js +1 -1
  42. package/dist/mcp/tokenBudget.d.ts +1 -1
  43. package/dist/mcp/tokenBudget.js +2 -2
  44. package/dist/mcp/tools.js +8 -8
  45. package/dist/reporters/consoleReporter.js +11 -11
  46. package/dist/reporters/markdownReporter.js +14 -14
  47. package/dist/reporters/sarifReporter.js +1 -1
  48. package/dist/utils/banner.d.ts +3 -3
  49. package/dist/utils/banner.js +9 -9
  50. package/dist/utils/config.js +1 -1
  51. package/dist/utils/packageJsonLocator.d.ts +1 -1
  52. package/dist/utils/packageJsonLocator.js +1 -1
  53. 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 does not hit the npm registry. Supports cursor pagination.',
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" the scariest untested files. Requires a coverage file at coverage/lcov.info, coverage/coverage-final.json, or coverage/coverage-summary.json.',
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 it is much cheaper and more accurate.',
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 simple substring scan; ranking adds no value
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 walk the graph's export table; rank exact/prefix/substring
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 lexical BM25 by default, optionally semantic or hybrid
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 both require the peer
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 reciprocal rank fusion
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)`))} ${errors} error${errors !== 1 ? 's' : ''}, ${warnings} warning${warnings !== 1 ? 's' : ''}, ${infos} info ${status} (threshold: ${threshold})`);
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('')} ${title}`);
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('')} ${title}`);
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('')} ${delta.relativePath}`);
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} ${chalk.dim(result.error ?? 'unknown error')}`);
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 only one author has touched this.`);
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 ${preview.name}`));
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 "Scariest Untested Files"'));
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}** ${issue.description}`);
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 ${pass ? 'PASS' : 'FAIL'}`,
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}** ${issue.description}`);
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** only one author has touched this.');
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}** ${issue.description}`);
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 ?? ''} | ${p.drift} |`);
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 ${s.critical} critical · ${s.high} high · ${s.moderate} moderate · ${s.low} low · ${s.info} info`);
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 \`${preview.name}\``);
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 ? '' : `${e.coverage.toFixed(0)}%`;
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 doctor, x-ray, and architecture map.',
5
+ text: 'Instant codebase insights - doctor, x-ray, and architecture map.',
6
6
  },
7
7
  };
8
8
  const SEVERITY_TO_LEVEL = {
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Full two-panel welcome screen shown only on the default `projscan` command.
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 shown on subcommands (doctor, fix, etc.).
7
+ * Compact one-liner - shown on subcommands (doctor, fix, etc.).
8
8
  */
9
9
  export declare function showCompactBanner(): void;
10
10
  /**
11
- * Help screen shown by `projscan help`.
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;
@@ -14,7 +14,7 @@ function getVersion() {
14
14
  }
15
15
  }
16
16
  /**
17
- * Full two-panel welcome screen shown only on the default `projscan` command.
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 shown on subcommands (doctor, fix, etc.).
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 shown by `projscan help`.
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 detect issues and score your project' },
97
- { cmd: 'projscan hotspots', desc: 'Rank files by risk churn × complexity × issues × ownership' },
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 purpose, risk, ownership, issues' },
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 exit 1 if score below threshold' },
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 purpose, imports, exports' },
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 surface scariest untested files' },
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
  ];
@@ -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 try next candidate.
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 robust enough
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 robust enough
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.0",
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",
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",