projscan 0.12.0 → 0.14.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 (105) hide show
  1. package/README.md +18 -13
  2. package/dist/analyzers/crossPackageImportCheck.d.ts +13 -0
  3. package/dist/analyzers/crossPackageImportCheck.js +136 -0
  4. package/dist/analyzers/crossPackageImportCheck.js.map +1 -0
  5. package/dist/analyzers/cycleCheck.d.ts +12 -0
  6. package/dist/analyzers/cycleCheck.js +65 -0
  7. package/dist/analyzers/cycleCheck.js.map +1 -0
  8. package/dist/cli/commands/audit.js +8 -2
  9. package/dist/cli/commands/audit.js.map +1 -1
  10. package/dist/cli/commands/dependencies.js +4 -3
  11. package/dist/cli/commands/dependencies.js.map +1 -1
  12. package/dist/cli/commands/explainIssue.d.ts +1 -0
  13. package/dist/cli/commands/explainIssue.js +49 -0
  14. package/dist/cli/commands/explainIssue.js.map +1 -0
  15. package/dist/cli/commands/fixSuggest.d.ts +1 -0
  16. package/dist/cli/commands/fixSuggest.js +71 -0
  17. package/dist/cli/commands/fixSuggest.js.map +1 -0
  18. package/dist/cli/commands/review.d.ts +1 -0
  19. package/dist/cli/commands/review.js +66 -0
  20. package/dist/cli/commands/review.js.map +1 -0
  21. package/dist/cli/index.js +6 -0
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/core/ast.d.ts +20 -0
  24. package/dist/core/ast.js +190 -0
  25. package/dist/core/ast.js.map +1 -1
  26. package/dist/core/auditRunner.d.ts +8 -0
  27. package/dist/core/auditRunner.js +50 -1
  28. package/dist/core/auditRunner.js.map +1 -1
  29. package/dist/core/codeGraph.d.ts +7 -1
  30. package/dist/core/codeGraph.js +2 -0
  31. package/dist/core/codeGraph.js.map +1 -1
  32. package/dist/core/dependencyAnalyzer.d.ts +15 -1
  33. package/dist/core/dependencyAnalyzer.js +115 -18
  34. package/dist/core/dependencyAnalyzer.js.map +1 -1
  35. package/dist/core/explainIssue.d.ts +9 -0
  36. package/dist/core/explainIssue.js +106 -0
  37. package/dist/core/explainIssue.js.map +1 -0
  38. package/dist/core/fileInspector.js +12 -0
  39. package/dist/core/fileInspector.js.map +1 -1
  40. package/dist/core/fixSuggest.d.ts +41 -0
  41. package/dist/core/fixSuggest.js +327 -0
  42. package/dist/core/fixSuggest.js.map +1 -0
  43. package/dist/core/indexCache.js +5 -1
  44. package/dist/core/indexCache.js.map +1 -1
  45. package/dist/core/issueEngine.js +18 -0
  46. package/dist/core/issueEngine.js.map +1 -1
  47. package/dist/core/languages/goAdapter.js +5 -0
  48. package/dist/core/languages/goAdapter.js.map +1 -1
  49. package/dist/core/languages/goFunctions.d.ts +24 -0
  50. package/dist/core/languages/goFunctions.js +99 -0
  51. package/dist/core/languages/goFunctions.js.map +1 -0
  52. package/dist/core/languages/javaAdapter.js +5 -0
  53. package/dist/core/languages/javaAdapter.js.map +1 -1
  54. package/dist/core/languages/javaFunctions.d.ts +22 -0
  55. package/dist/core/languages/javaFunctions.js +87 -0
  56. package/dist/core/languages/javaFunctions.js.map +1 -0
  57. package/dist/core/languages/pythonAdapter.js +5 -0
  58. package/dist/core/languages/pythonAdapter.js.map +1 -1
  59. package/dist/core/languages/pythonFunctions.d.ts +23 -0
  60. package/dist/core/languages/pythonFunctions.js +87 -0
  61. package/dist/core/languages/pythonFunctions.js.map +1 -0
  62. package/dist/core/languages/rubyAdapter.js +5 -0
  63. package/dist/core/languages/rubyAdapter.js.map +1 -1
  64. package/dist/core/languages/rubyFunctions.d.ts +22 -0
  65. package/dist/core/languages/rubyFunctions.js +91 -0
  66. package/dist/core/languages/rubyFunctions.js.map +1 -0
  67. package/dist/core/review.d.ts +21 -0
  68. package/dist/core/review.js +457 -0
  69. package/dist/core/review.js.map +1 -0
  70. package/dist/index.d.ts +5 -1
  71. package/dist/index.js +3 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/mcp/tools/audit.js +7 -2
  74. package/dist/mcp/tools/audit.js.map +1 -1
  75. package/dist/mcp/tools/dependencies.js +11 -5
  76. package/dist/mcp/tools/dependencies.js.map +1 -1
  77. package/dist/mcp/tools/explainIssue.d.ts +2 -0
  78. package/dist/mcp/tools/explainIssue.js +30 -0
  79. package/dist/mcp/tools/explainIssue.js.map +1 -0
  80. package/dist/mcp/tools/file.js +1 -1
  81. package/dist/mcp/tools/file.js.map +1 -1
  82. package/dist/mcp/tools/fixSuggest.d.ts +2 -0
  83. package/dist/mcp/tools/fixSuggest.js +57 -0
  84. package/dist/mcp/tools/fixSuggest.js.map +1 -0
  85. package/dist/mcp/tools/hotspots.js +40 -1
  86. package/dist/mcp/tools/hotspots.js.map +1 -1
  87. package/dist/mcp/tools/review.d.ts +2 -0
  88. package/dist/mcp/tools/review.js +54 -0
  89. package/dist/mcp/tools/review.js.map +1 -0
  90. package/dist/mcp/tools.js +6 -0
  91. package/dist/mcp/tools.js.map +1 -1
  92. package/dist/reporters/consoleReporter.d.ts +9 -1
  93. package/dist/reporters/consoleReporter.js +177 -0
  94. package/dist/reporters/consoleReporter.js.map +1 -1
  95. package/dist/reporters/jsonReporter.d.ts +9 -1
  96. package/dist/reporters/jsonReporter.js +9 -0
  97. package/dist/reporters/jsonReporter.js.map +1 -1
  98. package/dist/reporters/markdownReporter.d.ts +9 -1
  99. package/dist/reporters/markdownReporter.js +141 -0
  100. package/dist/reporters/markdownReporter.js.map +1 -1
  101. package/dist/tool-manifest.json +96 -8
  102. package/dist/types.d.ts +228 -0
  103. package/dist/utils/config.js +27 -0
  104. package/dist/utils/config.js.map +1 -1
  105. package/package.json +4 -2
@@ -1,7 +1,7 @@
1
1
  import { inspectFile } from '../../core/fileInspector.js';
2
2
  export const fileTool = {
3
3
  name: 'projscan_file',
4
- description: 'Drill into a single file: purpose, imports, exports, churn/risk/ownership, related health issues, AST cyclomatic complexity, and coupling (fan-in / fan-out). Use this after projscan_hotspots when deciding how to approach a specific risky file.',
4
+ description: 'Drill into a single file: purpose, imports, exports, churn/risk/ownership, related health issues, AST cyclomatic complexity, coupling (fan-in / fan-out), and per-function CC ranked by complexity. Use this after projscan_hotspots when deciding how to approach a specific risky file.',
5
5
  inputSchema: {
6
6
  type: 'object',
7
7
  properties: {
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","sourceRoot":"","sources":["../../../src/mcp/tools/file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG1D,MAAM,CAAC,MAAM,QAAQ,GAAY;IAC/B,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,qPAAqP;IACvP,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACvD,OAAO,MAAM,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../../src/mcp/tools/file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG1D,MAAM,CAAC,MAAM,QAAQ,GAAY;IAC/B,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,2RAA2R;IAC7R,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACvD,OAAO,MAAM,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { McpTool } from './_shared.js';
2
+ export declare const fixSuggestTool: McpTool;
@@ -0,0 +1,57 @@
1
+ import { scanRepository } from '../../core/repositoryScanner.js';
2
+ import { collectIssues } from '../../core/issueEngine.js';
3
+ import { findIssue, suggestFixForIssue, syntheticIssue, } from '../../core/fixSuggest.js';
4
+ export const fixSuggestTool = {
5
+ name: 'projscan_fix_suggest',
6
+ description: 'Given an issue id (from projscan_doctor / projscan_analyze) OR a file + rule pair, return a structured action prompt: headline, why it matters, where to change, one-paragraph instruction the agent can execute, optional suggested test. Rule-driven; no LLM inside projscan. Use this to close the diagnose -> fix loop.',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ issue_id: {
11
+ type: 'string',
12
+ description: 'Issue id from a previous projscan_doctor / projscan_analyze response.',
13
+ },
14
+ file: {
15
+ type: 'string',
16
+ description: 'File path (repo-relative). Required when no `issue_id` is given - combined with `rule` to synthesize a fix request.',
17
+ },
18
+ rule: {
19
+ type: 'string',
20
+ description: 'Rule / issue-id prefix (e.g. "unused-dependency", "cycle-detected"). Required when no `issue_id` is given.',
21
+ },
22
+ severity: {
23
+ type: 'string',
24
+ enum: ['info', 'warning', 'error'],
25
+ description: 'Optional. When synthesizing via file+rule, sets the severity for the suggestion. Default: warning.',
26
+ },
27
+ },
28
+ },
29
+ handler: async (args, rootPath) => {
30
+ const issueId = typeof args.issue_id === 'string' ? args.issue_id : null;
31
+ const file = typeof args.file === 'string' ? args.file : null;
32
+ const rule = typeof args.rule === 'string' ? args.rule : null;
33
+ const severity = args.severity === 'info' || args.severity === 'warning' || args.severity === 'error'
34
+ ? args.severity
35
+ : 'warning';
36
+ if (issueId) {
37
+ const scan = await scanRepository(rootPath);
38
+ const issues = await collectIssues(rootPath, scan.files);
39
+ const found = findIssue(issues, issueId);
40
+ if (!found) {
41
+ return { matched: false, reason: `No open issue with id "${issueId}" in current doctor run.` };
42
+ }
43
+ const fix = await suggestFixForIssue(found, rootPath);
44
+ return { matched: true, fix };
45
+ }
46
+ if (file && rule) {
47
+ const synthetic = syntheticIssue(rule, file, severity);
48
+ const fix = await suggestFixForIssue(synthetic, rootPath);
49
+ return { matched: true, fix, synthetic: true };
50
+ }
51
+ return {
52
+ matched: false,
53
+ reason: 'Provide either `issue_id` or both `file` and `rule`.',
54
+ };
55
+ },
56
+ };
57
+ //# sourceMappingURL=fixSuggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixSuggest.js","sourceRoot":"","sources":["../../../src/mcp/tools/fixSuggest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAGlC,MAAM,CAAC,MAAM,cAAc,GAAY;IACrC,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EACT,6TAA6T;IAC/T,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,qHAAqH;aACxH;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,4GAA4G;aAC/G;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC;gBAClC,WAAW,EAAE,oGAAoG;aAClH;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;QACzE,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,MAAM,QAAQ,GACZ,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;YAClF,CAAC,CAAC,IAAI,CAAC,QAAQ;YACf,CAAC,CAAC,SAAS,CAAC;QAEhB,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,OAAO,0BAA0B,EAAE,CAAC;YACjG,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAChC,CAAC;QAED,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YACvD,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC1D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACjD,CAAC;QAED,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,sDAAsD;SAC/D,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -8,7 +8,7 @@ import { paginate, listChecksum, readPageParams } from '../pagination.js';
8
8
  import { emitProgress } from '../progress.js';
9
9
  export const hotspotsTool = {
10
10
  name: 'projscan_hotspots',
11
- description: 'Rank files by risk using git churn × AST cyclomatic complexity × open issues. Returns the most dangerous files to touch. Each hotspot includes `cyclomaticComplexity` (null for non-AST languages, where line count is used as fallback). Supports cursor-based pagination: pass the `nextCursor` from a previous response back as `cursor` to fetch the next page.',
11
+ description: 'Rank files by risk using git churn × AST cyclomatic complexity × open issues. Returns the most dangerous files to touch. Each hotspot includes `cyclomaticComplexity` (null for non-AST languages, where line count is used as fallback). Supports cursor-based pagination: pass the `nextCursor` from a previous response back as `cursor` to fetch the next page. Pass `view: "functions"` to flatten results into the top-N riskiest individual functions across all hotspots, ranked by per-function CC.',
12
12
  inputSchema: {
13
13
  type: 'object',
14
14
  properties: {
@@ -27,6 +27,11 @@ export const hotspotsTool = {
27
27
  type: 'string',
28
28
  description: 'Optional. Workspace package name (from projscan_workspaces) to scope hotspots to one package only.',
29
29
  },
30
+ view: {
31
+ type: 'string',
32
+ enum: ['files', 'functions'],
33
+ description: 'Output shape. "files" (default) returns ranked hotspot files. "functions" returns the top-N individual functions across all hotspots, sorted by per-function CC desc.',
34
+ },
30
35
  },
31
36
  },
32
37
  handler: async (args, rootPath) => {
@@ -47,6 +52,40 @@ export const hotspotsTool = {
47
52
  const allowed = new Set(filterFilesByPackage(ws, args.package, report.hotspots.map((h) => h.relativePath)));
48
53
  report.hotspots = report.hotspots.filter((h) => allowed.has(h.relativePath));
49
54
  }
55
+ // 0.13.0: function-level view. Flattens per-file `functions` lists into a
56
+ // single ranked list. Skips files with no per-function CC (e.g. ones the
57
+ // adapter parsed but found no function definitions, or non-AST languages).
58
+ if (args.view === 'functions') {
59
+ const ranked = [];
60
+ for (const h of report.hotspots) {
61
+ const gf = graph.files.get(h.relativePath);
62
+ if (!gf || !gf.functions || gf.functions.length === 0)
63
+ continue;
64
+ for (const fn of gf.functions) {
65
+ ranked.push({
66
+ file: h.relativePath,
67
+ name: fn.name,
68
+ line: fn.line,
69
+ endLine: fn.endLine,
70
+ cyclomaticComplexity: fn.cyclomaticComplexity,
71
+ fileRiskScore: h.riskScore,
72
+ });
73
+ }
74
+ }
75
+ ranked.sort((a, b) => b.cyclomaticComplexity - a.cyclomaticComplexity);
76
+ const page = paginate(ranked, readPageParams(args), listChecksum(ranked));
77
+ emitProgress(5, 5, 'done');
78
+ return {
79
+ available: report.available,
80
+ reason: report.reason,
81
+ window: report.window,
82
+ view: 'functions',
83
+ functions: page.items,
84
+ totalFunctionsRanked: ranked.length,
85
+ nextCursor: page.nextCursor,
86
+ total: page.total,
87
+ };
88
+ }
50
89
  emitProgress(4, 5, 'paginating');
51
90
  const page = paginate(report.hotspots, readPageParams(args), listChecksum(report.hotspots));
52
91
  emitProgress(5, 5, 'done');
@@ -1 +1 @@
1
- {"version":3,"file":"hotspots.js","sourceRoot":"","sources":["../../../src/mcp/tools/hotspots.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hotspots.js","sourceRoot":"","sources":["../../../src/mcp/tools/hotspots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { McpTool } from './_shared.js';
2
+ export declare const reviewTool: McpTool;
@@ -0,0 +1,54 @@
1
+ import { computeReview } from '../../core/review.js';
2
+ import { detectWorkspaces, filterFilesByPackage } from '../../core/monorepo.js';
3
+ import { emitProgress } from '../progress.js';
4
+ export const reviewTool = {
5
+ name: 'projscan_review',
6
+ description: 'One-call PR review. Combines projscan_pr_diff + per-changed-file risk score + new/expanded import cycles + risky function additions + dependency changes, plus a verdict ("ok" | "review" | "block") with a one-line summary. Use when an agent is asked "is this PR safe to merge?" Defaults: base=origin/main (falls back to main/master/HEAD~1), head=HEAD.',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ base: {
11
+ type: 'string',
12
+ description: 'Base ref (branch, tag, sha). Default: origin/main, falling back to main/master/HEAD~1.',
13
+ },
14
+ head: { type: 'string', description: 'Head ref. Default: HEAD.' },
15
+ max_tokens: { type: 'number', description: 'Cap the response to roughly this many tokens.' },
16
+ package: {
17
+ type: 'string',
18
+ description: 'Optional. Workspace package name to scope all sections of the review to a single package.',
19
+ },
20
+ },
21
+ },
22
+ handler: async (args, rootPath) => {
23
+ emitProgress(0, 4, 'resolving refs');
24
+ const base = typeof args.base === 'string' ? args.base : undefined;
25
+ const head = typeof args.head === 'string' ? args.head : undefined;
26
+ emitProgress(1, 4, 'building base + head graphs');
27
+ const report = await computeReview(rootPath, { base, head });
28
+ if (typeof args.package === 'string' && args.package.length > 0 && report.available) {
29
+ emitProgress(2, 4, 'scoping to workspace');
30
+ const ws = await detectWorkspaces(rootPath);
31
+ const target = args.package;
32
+ const allChangedPaths = [
33
+ ...report.prDiff.filesAdded,
34
+ ...report.prDiff.filesRemoved,
35
+ ...report.prDiff.filesModified.map((f) => f.relativePath),
36
+ ];
37
+ const allowed = new Set(filterFilesByPackage(ws, target, allChangedPaths));
38
+ report.prDiff.filesAdded = report.prDiff.filesAdded.filter((f) => allowed.has(f));
39
+ report.prDiff.filesRemoved = report.prDiff.filesRemoved.filter((f) => allowed.has(f));
40
+ report.prDiff.filesModified = report.prDiff.filesModified.filter((f) => allowed.has(f.relativePath));
41
+ report.prDiff.totalFilesChanged =
42
+ report.prDiff.filesAdded.length +
43
+ report.prDiff.filesRemoved.length +
44
+ report.prDiff.filesModified.length;
45
+ report.changedFiles = report.changedFiles.filter((f) => allowed.has(f.relativePath));
46
+ report.newCycles = report.newCycles.filter((c) => c.files.some((f) => allowed.has(f)));
47
+ report.riskyFunctions = report.riskyFunctions.filter((f) => allowed.has(f.file));
48
+ report.dependencyChanges = report.dependencyChanges.filter((d) => d.workspace === target);
49
+ }
50
+ emitProgress(4, 4, 'done');
51
+ return report;
52
+ },
53
+ };
54
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/mcp/tools/review.ts"],"names":[],"mappings":"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"}
package/dist/mcp/tools.js CHANGED
@@ -23,6 +23,9 @@ import { graphTool } from './tools/graph.js';
23
23
  import { couplingTool } from './tools/coupling.js';
24
24
  import { workspacesTool } from './tools/workspaces.js';
25
25
  import { prDiffTool } from './tools/prDiff.js';
26
+ import { reviewTool } from './tools/review.js';
27
+ import { fixSuggestTool } from './tools/fixSuggest.js';
28
+ import { explainIssueTool } from './tools/explainIssue.js';
26
29
  import { searchTool } from './tools/search.js';
27
30
  const tools = [
28
31
  analyzeTool,
@@ -40,6 +43,9 @@ const tools = [
40
43
  couplingTool,
41
44
  workspacesTool,
42
45
  prDiffTool,
46
+ reviewTool,
47
+ fixSuggestTool,
48
+ explainIssueTool,
43
49
  searchTool,
44
50
  ];
45
51
  export function getToolDefinitions() {
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM/C,MAAM,KAAK,GAAc;IACvB,WAAW;IACX,UAAU;IACV,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,SAAS;IACT,WAAW;IACX,YAAY;IACZ,SAAS;IACT,YAAY;IACZ,cAAc;IACd,UAAU;IACV,UAAU;CACX,CAAC;AAEF,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,OAAO,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM/C,MAAM,KAAK,GAAc;IACvB,WAAW;IACX,UAAU;IACV,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,SAAS;IACT,WAAW;IACX,YAAY;IACZ,SAAS;IACT,YAAY;IACZ,cAAc;IACd,UAAU;IACV,UAAU;IACV,cAAc;IACd,gBAAgB;IAChB,UAAU;CACX,CAAC;AAEF,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,OAAO,CAAC;AACrD,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, Fix, FixResult, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, UpgradePreview, WorkspaceInfo } from '../types.js';
1
+ import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, Fix, FixResult, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, ReviewReport, FixSuggestion, IssueExplanation, UpgradePreview, WorkspaceInfo } from '../types.js';
2
2
  export declare function reportAnalysis(report: AnalysisReport): void;
3
3
  export declare function reportHealth(issues: Issue[], scanTimeMs?: number): void;
4
4
  export declare function reportCi(issues: Issue[], threshold: number): void;
@@ -17,4 +17,12 @@ export declare function reportUpgrade(preview: UpgradePreview): void;
17
17
  export declare function reportCoverage(report: CoverageJoinedReport): void;
18
18
  export declare function reportCoupling(report: CouplingReport): void;
19
19
  export declare function reportPrDiff(report: PrDiffReport): void;
20
+ export declare function reportFixSuggest(result: {
21
+ matched: boolean;
22
+ fix?: FixSuggestion;
23
+ reason?: string;
24
+ synthetic?: boolean;
25
+ }): void;
26
+ export declare function reportExplainIssue(e: IssueExplanation): void;
27
+ export declare function reportReview(report: ReviewReport): void;
20
28
  export declare function reportWorkspaces(info: WorkspaceInfo): void;
@@ -109,6 +109,9 @@ export function reportHealth(issues, scanTimeMs) {
109
109
  for (const issue of issues) {
110
110
  console.log(` ${severityIcon(issue.severity)} ${issue.title}`);
111
111
  console.log(` ${chalk.dim(issue.description)}`);
112
+ if (issue.suggestedAction) {
113
+ console.log(` ${chalk.cyan('→')} ${chalk.dim(issue.suggestedAction.summary)} ${chalk.dim(`(projscan fix-suggest ${issue.id})`)}`);
114
+ }
112
115
  }
113
116
  // Recommendations
114
117
  const fixable = issues.filter((i) => i.fixAvailable);
@@ -399,6 +402,17 @@ export function reportFileInspection(insp) {
399
402
  console.log(` ${chalk.dim('•')} ${chalk.bold(exp.name)} ${chalk.dim(`(${exp.type})`)}`);
400
403
  }
401
404
  }
405
+ if (insp.functions && insp.functions.length > 0) {
406
+ console.log(header('Functions (top by CC)'));
407
+ const top = insp.functions.slice(0, 10);
408
+ for (const fn of top) {
409
+ const ccColor = fn.cyclomaticComplexity >= 10 ? chalk.red : fn.cyclomaticComplexity >= 5 ? chalk.yellow : chalk.dim;
410
+ console.log(` ${ccColor(`CC ${String(fn.cyclomaticComplexity).padStart(3)}`)} ${chalk.bold(fn.name)} ${chalk.dim(`L${fn.line}-${fn.endLine}`)}`);
411
+ }
412
+ if (insp.functions.length > 10) {
413
+ console.log(chalk.dim(` ... and ${insp.functions.length - 10} more`));
414
+ }
415
+ }
402
416
  console.log('');
403
417
  }
404
418
  function formatSize(bytes) {
@@ -670,6 +684,169 @@ export function reportPrDiff(report) {
670
684
  function signed(n) {
671
685
  return n >= 0 ? `+${n}` : String(n);
672
686
  }
687
+ // ── Report: fix-suggest ───────────────────────────────────
688
+ export function reportFixSuggest(result) {
689
+ console.log(header('Fix Suggestion'));
690
+ if (!result.matched || !result.fix) {
691
+ console.log(`\n ${chalk.yellow('⚠')} ${result.reason ?? 'No suggestion available.'}\n`);
692
+ return;
693
+ }
694
+ const fix = result.fix;
695
+ const sevColor = fix.severity === 'error' ? chalk.red : fix.severity === 'warning' ? chalk.yellow : chalk.dim;
696
+ console.log(`\n ${chalk.bold(fix.headline)}\n`);
697
+ console.log(chalk.dim(` ${sevColor(fix.severity)} · ${fix.category} · ${fix.issueId}${result.synthetic ? ' (synthetic)' : ''}\n`));
698
+ console.log(chalk.bold(' Why'));
699
+ for (const line of wrapLines(fix.why, 76))
700
+ console.log(` ${line}`);
701
+ if (fix.where.length > 0) {
702
+ console.log('\n' + chalk.bold(' Where'));
703
+ for (const w of fix.where) {
704
+ const loc = w.line ? `${w.file}:${w.line}` : w.file;
705
+ console.log(` ${chalk.cyan(loc)}`);
706
+ }
707
+ }
708
+ console.log('\n' + chalk.bold(' Action'));
709
+ for (const line of wrapLines(fix.instruction, 76))
710
+ console.log(` ${line}`);
711
+ if (fix.suggestedTest) {
712
+ console.log('\n' + chalk.bold(' Verify'));
713
+ for (const line of wrapLines(fix.suggestedTest, 76))
714
+ console.log(` ${line}`);
715
+ }
716
+ if (fix.relatedFiles && fix.relatedFiles.length > 0) {
717
+ console.log('\n' + chalk.bold(' Related files'));
718
+ for (const f of fix.relatedFiles)
719
+ console.log(` ${chalk.cyan(f)}`);
720
+ }
721
+ console.log('');
722
+ }
723
+ export function reportExplainIssue(e) {
724
+ console.log(header('Issue Explanation'));
725
+ console.log(`\n ${chalk.bold(e.title)} ${chalk.dim(`(${e.issueId})`)}`);
726
+ console.log(` ${chalk.dim(`severity: ${e.severity} · category: ${e.category}`)}\n`);
727
+ console.log(` ${chalk.bold(e.headline)}\n`);
728
+ if (e.excerpt) {
729
+ console.log(chalk.bold(` Code (${e.excerpt.file} L${e.excerpt.startLine}-${e.excerpt.endLine})`));
730
+ for (let i = 0; i < e.excerpt.lines.length; i++) {
731
+ const ln = e.excerpt.startLine + i;
732
+ console.log(` ${chalk.dim(String(ln).padStart(4))} ${e.excerpt.lines[i]}`);
733
+ }
734
+ console.log('');
735
+ }
736
+ if (e.relatedIssues.length > 0) {
737
+ console.log(chalk.bold(' Related issues in the same area:'));
738
+ for (const r of e.relatedIssues)
739
+ console.log(` ${chalk.dim('•')} ${r.id}: ${r.title}`);
740
+ console.log('');
741
+ }
742
+ if (e.similarFixes.length > 0) {
743
+ console.log(chalk.bold(' Past commits referencing this rule:'));
744
+ for (const f of e.similarFixes)
745
+ console.log(` ${chalk.dim(f.sha.slice(0, 7))} ${chalk.dim(`(${f.date})`)} ${f.subject}`);
746
+ console.log('');
747
+ }
748
+ if (e.fix) {
749
+ console.log(chalk.bold(' Suggested action:'));
750
+ for (const line of wrapLines(e.fix.instruction, 76))
751
+ console.log(` ${line}`);
752
+ if (e.fix.suggestedTest) {
753
+ console.log('\n ' + chalk.bold('Verify:') + ' ' + e.fix.suggestedTest);
754
+ }
755
+ console.log('');
756
+ }
757
+ }
758
+ function wrapLines(text, maxWidth) {
759
+ const out = [];
760
+ for (const para of text.split(/\n+/)) {
761
+ if (para.length <= maxWidth) {
762
+ out.push(para);
763
+ continue;
764
+ }
765
+ const words = para.split(/\s+/);
766
+ let cur = '';
767
+ for (const w of words) {
768
+ if ((cur + ' ' + w).trim().length > maxWidth) {
769
+ if (cur)
770
+ out.push(cur);
771
+ cur = w;
772
+ }
773
+ else {
774
+ cur = cur ? `${cur} ${w}` : w;
775
+ }
776
+ }
777
+ if (cur)
778
+ out.push(cur);
779
+ }
780
+ return out;
781
+ }
782
+ // ── Report: review ────────────────────────────────────────
783
+ export function reportReview(report) {
784
+ console.log(header('PR Review'));
785
+ if (!report.available) {
786
+ console.log(`\n ${chalk.yellow('⚠')} ${report.reason ?? 'Review unavailable.'}\n`);
787
+ return;
788
+ }
789
+ console.log(chalk.dim(`\n base ${report.base.ref} (${report.base.resolvedSha?.slice(0, 7) ?? '?'}) → head ${report.head.ref} (${report.head.resolvedSha?.slice(0, 7) ?? '?'})\n`));
790
+ const verdictColor = report.verdict === 'block' ? chalk.red : report.verdict === 'review' ? chalk.yellow : chalk.green;
791
+ const verdictLabel = report.verdict === 'block' ? '🚫 BLOCK' : report.verdict === 'review' ? '👀 REVIEW' : '✅ OK';
792
+ console.log(` ${chalk.bold('Verdict:')} ${verdictColor(verdictLabel)}\n`);
793
+ for (const s of report.summary) {
794
+ console.log(` ${chalk.dim('•')} ${s}`);
795
+ }
796
+ if (report.summary.length > 0)
797
+ console.log('');
798
+ if (report.changedFiles.length > 0) {
799
+ console.log(chalk.bold(' Changed files (top by risk):'));
800
+ for (const f of report.changedFiles.slice(0, 15)) {
801
+ const risk = f.riskScore !== null ? f.riskScore.toFixed(1).padStart(6) : ' - ';
802
+ const cc = f.cyclomaticComplexity !== null ? String(f.cyclomaticComplexity).padStart(3) : ' -';
803
+ const dcc = f.cyclomaticDelta === null ? ' ' : signed(f.cyclomaticDelta).padStart(3);
804
+ const statusColor = f.status === 'added' ? chalk.green : f.status === 'removed' ? chalk.red : chalk.yellow;
805
+ console.log(` ${statusColor(f.status.padEnd(8))} risk ${risk} CC ${cc} (Δ${dcc}) ${chalk.cyan(f.relativePath)}`);
806
+ }
807
+ if (report.changedFiles.length > 15) {
808
+ console.log(chalk.dim(` ... and ${report.changedFiles.length - 15} more`));
809
+ }
810
+ console.log('');
811
+ }
812
+ if (report.newCycles.length > 0) {
813
+ console.log(chalk.bold(` New / expanded cycles (${report.newCycles.length}):`));
814
+ for (const c of report.newCycles.slice(0, 5)) {
815
+ const tag = c.classification === 'new' ? chalk.red('NEW') : chalk.yellow('EXP');
816
+ console.log(` ${tag} (${c.size}): ${c.files.join(' → ')}`);
817
+ }
818
+ if (report.newCycles.length > 5) {
819
+ console.log(chalk.dim(` ... and ${report.newCycles.length - 5} more`));
820
+ }
821
+ console.log('');
822
+ }
823
+ if (report.riskyFunctions.length > 0) {
824
+ console.log(chalk.bold(` Risky functions (${report.riskyFunctions.length}):`));
825
+ for (const fn of report.riskyFunctions.slice(0, 10)) {
826
+ const cc = fn.cyclomaticComplexity >= 15 ? chalk.red : chalk.yellow;
827
+ const transition = fn.baseCc === null ? `(new)` : `(${fn.baseCc} → ${fn.cyclomaticComplexity})`;
828
+ console.log(` ${cc(`CC ${String(fn.cyclomaticComplexity).padStart(3)}`)} ${chalk.bold(fn.name)} ${chalk.dim(`${fn.file}:${fn.line}`)} ${chalk.dim(`[${fn.reason}] ${transition}`)}`);
829
+ }
830
+ if (report.riskyFunctions.length > 10) {
831
+ console.log(chalk.dim(` ... and ${report.riskyFunctions.length - 10} more`));
832
+ }
833
+ console.log('');
834
+ }
835
+ if (report.dependencyChanges.length > 0) {
836
+ console.log(chalk.bold(' Dependency changes:'));
837
+ for (const d of report.dependencyChanges) {
838
+ const wsLabel = d.workspace ? ` (${d.workspace})` : '';
839
+ console.log(` ${chalk.cyan(d.manifestFile)}${chalk.dim(wsLabel)}`);
840
+ for (const a of d.added)
841
+ console.log(` ${chalk.green('+')} ${a.name}@${a.version} ${chalk.dim(`(${a.kind})`)}`);
842
+ for (const r of d.removed)
843
+ console.log(` ${chalk.red('-')} ${r.name}@${r.version} ${chalk.dim(`(${r.kind})`)}`);
844
+ for (const b of d.bumped)
845
+ console.log(` ${chalk.yellow('~')} ${b.name}: ${b.from} → ${b.to} ${chalk.dim(`(${b.kind})`)}`);
846
+ }
847
+ console.log('');
848
+ }
849
+ }
673
850
  // ── Report: workspaces ────────────────────────────────────
674
851
  export function reportWorkspaces(info) {
675
852
  console.log(header('Workspaces'));