projscan 1.4.0 → 1.6.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 (112) hide show
  1. package/README.md +24 -7
  2. package/dist/cli/commands/applyFix.d.ts +7 -0
  3. package/dist/cli/commands/applyFix.js +113 -0
  4. package/dist/cli/commands/applyFix.js.map +1 -0
  5. package/dist/cli/commands/coverage.js +13 -0
  6. package/dist/cli/commands/coverage.js.map +1 -1
  7. package/dist/cli/commands/doctor.js +18 -2
  8. package/dist/cli/commands/doctor.js.map +1 -1
  9. package/dist/cli/commands/explain.js +1 -0
  10. package/dist/cli/commands/explain.js.map +1 -1
  11. package/dist/cli/commands/impact.js +25 -1
  12. package/dist/cli/commands/impact.js.map +1 -1
  13. package/dist/cli/commands/init.d.ts +6 -0
  14. package/dist/cli/commands/init.js +70 -0
  15. package/dist/cli/commands/init.js.map +1 -0
  16. package/dist/cli/commands/installHook.d.ts +9 -0
  17. package/dist/cli/commands/installHook.js +90 -0
  18. package/dist/cli/commands/installHook.js.map +1 -0
  19. package/dist/cli/commands/memory.d.ts +11 -0
  20. package/dist/cli/commands/memory.js +175 -0
  21. package/dist/cli/commands/memory.js.map +1 -0
  22. package/dist/cli/commands/taint.d.ts +6 -0
  23. package/dist/cli/commands/taint.js +74 -0
  24. package/dist/cli/commands/taint.js.map +1 -0
  25. package/dist/cli/commands/workspace.d.ts +11 -0
  26. package/dist/cli/commands/workspace.js +115 -0
  27. package/dist/cli/commands/workspace.js.map +1 -0
  28. package/dist/cli/index.js +12 -0
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/core/applyFix.d.ts +52 -0
  31. package/dist/core/applyFix.js +220 -0
  32. package/dist/core/applyFix.js.map +1 -0
  33. package/dist/core/ast.d.ts +9 -0
  34. package/dist/core/ast.js +35 -4
  35. package/dist/core/ast.js.map +1 -1
  36. package/dist/core/codeGraph.js +190 -241
  37. package/dist/core/codeGraph.js.map +1 -1
  38. package/dist/core/fileInspector.js +40 -44
  39. package/dist/core/fileInspector.js.map +1 -1
  40. package/dist/core/fixSuggest.d.ts +6 -0
  41. package/dist/core/fixSuggest.js +195 -0
  42. package/dist/core/fixSuggest.js.map +1 -1
  43. package/dist/core/hotspotAnalyzer.js +65 -19
  44. package/dist/core/hotspotAnalyzer.js.map +1 -1
  45. package/dist/core/impact.d.ts +8 -0
  46. package/dist/core/impact.js +41 -3
  47. package/dist/core/impact.js.map +1 -1
  48. package/dist/core/indexCache.js +4 -1
  49. package/dist/core/indexCache.js.map +1 -1
  50. package/dist/core/issueEngine.js +24 -0
  51. package/dist/core/issueEngine.js.map +1 -1
  52. package/dist/core/languages/csharpImports.js +6 -4
  53. package/dist/core/languages/csharpImports.js.map +1 -1
  54. package/dist/core/memory.d.ts +154 -0
  55. package/dist/core/memory.js +277 -0
  56. package/dist/core/memory.js.map +1 -0
  57. package/dist/core/review.d.ts +25 -1
  58. package/dist/core/review.js +133 -2
  59. package/dist/core/review.js.map +1 -1
  60. package/dist/core/taint.d.ts +91 -0
  61. package/dist/core/taint.js +185 -0
  62. package/dist/core/taint.js.map +1 -0
  63. package/dist/core/workspace.d.ts +62 -0
  64. package/dist/core/workspace.js +127 -0
  65. package/dist/core/workspace.js.map +1 -0
  66. package/dist/mcp/prompts.js +274 -0
  67. package/dist/mcp/prompts.js.map +1 -1
  68. package/dist/mcp/server.js +162 -146
  69. package/dist/mcp/server.js.map +1 -1
  70. package/dist/mcp/tokenBudget.d.ts +22 -0
  71. package/dist/mcp/tokenBudget.js +26 -0
  72. package/dist/mcp/tokenBudget.js.map +1 -1
  73. package/dist/mcp/tools/applyFix.d.ts +16 -0
  74. package/dist/mcp/tools/applyFix.js +91 -0
  75. package/dist/mcp/tools/applyFix.js.map +1 -0
  76. package/dist/mcp/tools/doctor.js +65 -2
  77. package/dist/mcp/tools/doctor.js.map +1 -1
  78. package/dist/mcp/tools/explain.js +4 -3
  79. package/dist/mcp/tools/explain.js.map +1 -1
  80. package/dist/mcp/tools/explainIssue.js +3 -2
  81. package/dist/mcp/tools/explainIssue.js.map +1 -1
  82. package/dist/mcp/tools/file.js +3 -2
  83. package/dist/mcp/tools/file.js.map +1 -1
  84. package/dist/mcp/tools/graph.js +16 -11
  85. package/dist/mcp/tools/graph.js.map +1 -1
  86. package/dist/mcp/tools/impact.js +36 -3
  87. package/dist/mcp/tools/impact.js.map +1 -1
  88. package/dist/mcp/tools/memory.d.ts +19 -0
  89. package/dist/mcp/tools/memory.js +134 -0
  90. package/dist/mcp/tools/memory.js.map +1 -0
  91. package/dist/mcp/tools/review.js +25 -4
  92. package/dist/mcp/tools/review.js.map +1 -1
  93. package/dist/mcp/tools/taint.d.ts +15 -0
  94. package/dist/mcp/tools/taint.js +67 -0
  95. package/dist/mcp/tools/taint.js.map +1 -0
  96. package/dist/mcp/tools/upgrade.js +3 -2
  97. package/dist/mcp/tools/upgrade.js.map +1 -1
  98. package/dist/mcp/tools/workspaceGraph.d.ts +18 -0
  99. package/dist/mcp/tools/workspaceGraph.js +188 -0
  100. package/dist/mcp/tools/workspaceGraph.js.map +1 -0
  101. package/dist/mcp/tools.js +8 -0
  102. package/dist/mcp/tools.js.map +1 -1
  103. package/dist/reporters/consoleReporter.d.ts +12 -1
  104. package/dist/reporters/consoleReporter.js +289 -179
  105. package/dist/reporters/consoleReporter.js.map +1 -1
  106. package/dist/reporters/markdownReporter.js +185 -128
  107. package/dist/reporters/markdownReporter.js.map +1 -1
  108. package/dist/tool-manifest.json +129 -6
  109. package/dist/types.d.ts +67 -0
  110. package/dist/utils/config.js +93 -53
  111. package/dist/utils/config.js.map +1 -1
  112. package/package.json +9 -2
@@ -4,11 +4,15 @@ import { calculateScore } from '../../utils/scoreCalculator.js';
4
4
  import { PACKAGE_ARG_SCHEMA, resolvePackageFilter } from './_shared.js';
5
5
  export const doctorTool = {
6
6
  name: 'projscan_doctor',
7
- description: 'Run a health check on the project. Returns a 0-100 score, letter grade, and the list of issues (linting, formatting, tests, security, architecture).',
7
+ description: 'Run a health check on the project. Returns a 0-100 score, letter grade, and the list of issues (linting, formatting, tests, security, architecture). Pass `max_cost_tokens` (1.5+) for adaptive shaping: <3000 returns verdict-only (score + counts), <7000 returns a summary (top issues), otherwise full.',
8
8
  inputSchema: {
9
9
  type: 'object',
10
10
  properties: {
11
11
  package: PACKAGE_ARG_SCHEMA,
12
+ max_cost_tokens: {
13
+ type: 'number',
14
+ description: '1.5+ — adaptive shape budget. <3000 returns verdict-only (score + grade + per-severity counts); <7000 returns a summary (top-5 issues by severity, no descriptions); otherwise the full issue list. Different from `max_tokens` (post-hoc truncation): the tool reshapes BEFORE serializing.',
15
+ },
12
16
  },
13
17
  },
14
18
  handler: async (args, rootPath) => {
@@ -24,7 +28,66 @@ export const doctorTool = {
24
28
  });
25
29
  }
26
30
  const health = calculateScore(issues);
27
- return { health, issues };
31
+ // 1.5+ adaptive shape based on max_cost_tokens. With no budget,
32
+ // returns the full report unchanged (preserves 1.4-and-earlier
33
+ // shape; no `tier` field). With a budget, picks a tier and shapes
34
+ // — `tier` is surfaced and lifted into _cost.tier by the dispatcher.
35
+ const maxCostTokens = typeof args.max_cost_tokens === 'number' && Number.isFinite(args.max_cost_tokens)
36
+ ? args.max_cost_tokens
37
+ : undefined;
38
+ if (maxCostTokens === undefined)
39
+ return { health, issues };
40
+ return shapeDoctorForBudget(health, issues, maxCostTokens);
28
41
  },
29
42
  };
43
+ function selectDoctorTier(maxCostTokens) {
44
+ if (!Number.isFinite(maxCostTokens) || maxCostTokens <= 0)
45
+ return 'full';
46
+ if (maxCostTokens < 3000)
47
+ return 'verdict-only';
48
+ if (maxCostTokens < 7000)
49
+ return 'summary';
50
+ return 'full';
51
+ }
52
+ function shapeDoctorForBudget(health, issues, maxCostTokens) {
53
+ const tier = selectDoctorTier(maxCostTokens);
54
+ const counts = countBySeverityAndCategory(issues);
55
+ if (tier === 'verdict-only') {
56
+ return { health, counts, tier };
57
+ }
58
+ if (tier === 'summary') {
59
+ // Top 5 of each severity, with the heavy `description` and
60
+ // `locations` fields stripped.
61
+ const top = (sev) => issues
62
+ .filter((i) => i.severity === sev)
63
+ .slice(0, 5)
64
+ .map((i) => ({ id: i.id, title: i.title, severity: i.severity, category: i.category }));
65
+ return {
66
+ health,
67
+ counts,
68
+ topIssues: {
69
+ error: top('error'),
70
+ warning: top('warning'),
71
+ info: top('info'),
72
+ },
73
+ tier,
74
+ };
75
+ }
76
+ return { health, issues, counts, tier };
77
+ }
78
+ function countBySeverityAndCategory(issues) {
79
+ const bySeverity = { error: 0, warning: 0, info: 0 };
80
+ const byCategory = {};
81
+ for (const i of issues) {
82
+ if (i.severity === 'error')
83
+ bySeverity.error += 1;
84
+ else if (i.severity === 'warning')
85
+ bySeverity.warning += 1;
86
+ else if (i.severity === 'info')
87
+ bySeverity.info += 1;
88
+ const cat = i.category ?? 'other';
89
+ byCategory[cat] = (byCategory[cat] ?? 0) + 1;
90
+ }
91
+ return { bySeverity, byCategory };
92
+ }
30
93
  //# sourceMappingURL=doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/mcp/tools/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAgB,MAAM,cAAc,CAAC;AAEtF,MAAM,CAAC,MAAM,UAAU,GAAY;IACjC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,sJAAsJ;IACxJ,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE,kBAAkB;SAC5B;KACF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3B,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;gBAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,KAAK,CAAC;gBACpC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACtC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/mcp/tools/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAgB,MAAM,cAAc,CAAC;AAGtF,MAAM,CAAC,MAAM,UAAU,GAAY;IACjC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,6SAA6S;IAC/S,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE,kBAAkB;YAC3B,eAAe,EAAE;gBACf,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,8RAA8R;aACjS;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3B,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;gBAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,KAAK,CAAC;gBACpC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QAEtC,kEAAkE;QAClE,+DAA+D;QAC/D,kEAAkE;QAClE,qEAAqE;QACrE,MAAM,aAAa,GACjB,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC;YAC/E,CAAC,CAAC,IAAI,CAAC,eAAe;YACtB,CAAC,CAAC,SAAS,CAAC;QAChB,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAC3D,OAAO,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IAC7D,CAAC;CACF,CAAC;AAIF,SAAS,gBAAgB,CAAC,aAAqB;IAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACzE,IAAI,aAAa,GAAG,IAAI;QAAE,OAAO,cAAc,CAAC;IAChD,IAAI,aAAa,GAAG,IAAI;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,oBAAoB,CAC3B,MAAyC,EACzC,MAAe,EACf,aAAqB;IAErB,MAAM,IAAI,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAElD,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,2DAA2D;QAC3D,+BAA+B;QAC/B,MAAM,GAAG,GAAG,CAAC,GAAiC,EAAgE,EAAE,CAC9G,MAAM;aACH,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC;aACjC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC5F,OAAO;YACL,MAAM;YACN,MAAM;YACN,SAAS,EAAE;gBACT,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;gBACnB,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC;gBACvB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC;aAClB;YACD,IAAI;SACL,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,0BAA0B,CAAC,MAAe;IAIjD,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrD,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,UAAU,CAAC,KAAK,IAAI,CAAC,CAAC;aAC7C,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;YAAE,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;aACtD,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM;YAAE,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;QAClC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC"}
@@ -16,12 +16,13 @@ export const explainTool = {
16
16
  },
17
17
  handler: async (args, rootPath) => {
18
18
  const rel = typeof args.file === 'string' ? args.file : '';
19
- if (!rel)
20
- throw new Error('file argument is required');
19
+ if (!rel) {
20
+ throw new Error('file argument is required: pass a repo-relative path (e.g. "src/auth.ts").');
21
+ }
21
22
  const absolutePath = path.resolve(rootPath, rel);
22
23
  const resolvedRoot = path.resolve(rootPath);
23
24
  if (!absolutePath.startsWith(resolvedRoot + path.sep) && absolutePath !== resolvedRoot) {
24
- throw new Error('file must be inside the project root');
25
+ throw new Error(`file must be inside the project root (got "${rel}"; absolute or "../" paths are rejected for security).`);
25
26
  }
26
27
  const content = await fs.readFile(absolutePath, 'utf-8');
27
28
  return explainFile(absolutePath, content, rootPath);
@@ -1 +1 @@
1
- {"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/mcp/tools/explain.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,kIAAkI;IACpI,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,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,WAAW,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/mcp/tools/explain.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,kIAAkI;IACpI,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,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CACb,8CAA8C,GAAG,wDAAwD,CAC1G,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,WAAW,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC;CACF,CAAC"}
@@ -16,8 +16,9 @@ export const explainIssueTool = {
16
16
  },
17
17
  handler: async (args, rootPath) => {
18
18
  const issueId = typeof args.issue_id === 'string' ? args.issue_id : '';
19
- if (!issueId)
20
- throw new Error('issue_id is required');
19
+ if (!issueId) {
20
+ throw new Error('issue_id is required. Get one from projscan_doctor or projscan_analyze (each issue has an `id` field).');
21
+ }
21
22
  const scan = await scanRepository(rootPath);
22
23
  const issues = await collectIssues(rootPath, scan.files);
23
24
  const explanation = await explainIssue(rootPath, issues, issueId);
@@ -1 +1 @@
1
- {"version":3,"file":"explainIssue.js","sourceRoot":"","sources":["../../../src/mcp/tools/explainIssue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,MAAM,CAAC,MAAM,gBAAgB,GAAY;IACvC,IAAI,EAAE,wBAAwB;IAC9B,WAAW,EACT,0TAA0T;IAC5T,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;SACF;QACD,QAAQ,EAAE,CAAC,UAAU,CAAC;KACvB;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,EAAE,CAAC;QACvE,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAClE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,OAAO,0BAA0B,EAAE,CAAC;QACjG,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACxC,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"explainIssue.js","sourceRoot":"","sources":["../../../src/mcp/tools/explainIssue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG1D,MAAM,CAAC,MAAM,gBAAgB,GAAY;IACvC,IAAI,EAAE,wBAAwB;IAC9B,WAAW,EACT,0TAA0T;IAC5T,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;aACrF;SACF;QACD,QAAQ,EAAE,CAAC,UAAU,CAAC;KACvB;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,EAAE,CAAC;QACvE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,wGAAwG,CACzG,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAClE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,OAAO,0BAA0B,EAAE,CAAC;QACjG,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IACxC,CAAC;CACF,CAAC"}
@@ -14,8 +14,9 @@ export const fileTool = {
14
14
  },
15
15
  handler: async (args, rootPath) => {
16
16
  const rel = typeof args.file === 'string' ? args.file : '';
17
- if (!rel)
18
- throw new Error('file argument is required');
17
+ if (!rel) {
18
+ throw new Error('file argument is required: pass a repo-relative path (e.g. "src/auth.ts"). Use projscan_search { scope: "files" } to find one.');
19
+ }
19
20
  return await inspectFile(rootPath, rel);
20
21
  },
21
22
  };
@@ -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,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"}
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,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,gIAAgI,CACjI,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;CACF,CAAC"}
@@ -36,33 +36,38 @@ export const graphTool = {
36
36
  const limit = Math.max(1, Math.min(500, typeof args.limit === 'number' ? args.limit : 50));
37
37
  switch (direction) {
38
38
  case 'imports': {
39
- if (!file)
40
- throw new Error('file argument is required for direction=imports');
39
+ if (!file) {
40
+ throw new Error('direction=imports requires a `file` argument (repo-relative path, e.g. "src/auth.ts").');
41
+ }
41
42
  return { file, imports: importsOf(graph, file).slice(0, limit) };
42
43
  }
43
44
  case 'exports': {
44
- if (!file)
45
- throw new Error('file argument is required for direction=exports');
45
+ if (!file) {
46
+ throw new Error('direction=exports requires a `file` argument (repo-relative path).');
47
+ }
46
48
  return { file, exports: exportsOf(graph, file).slice(0, limit) };
47
49
  }
48
50
  case 'importers': {
49
- if (!file)
50
- throw new Error('file argument is required for direction=importers');
51
+ if (!file) {
52
+ throw new Error('direction=importers requires a `file` argument (repo-relative path).');
53
+ }
51
54
  return { file, importers: filesImportingFile(graph, file).slice(0, limit) };
52
55
  }
53
56
  case 'symbol_defs': {
54
- if (!symbol)
55
- throw new Error('symbol argument is required for direction=symbol_defs');
57
+ if (!symbol) {
58
+ throw new Error('direction=symbol_defs requires a `symbol` argument (the exported name to look up, e.g. "authenticate").');
59
+ }
56
60
  return { symbol, definedIn: filesDefiningSymbol(graph, symbol).slice(0, limit) };
57
61
  }
58
62
  case 'package_importers': {
59
63
  const pkg = symbol ?? file;
60
- if (!pkg)
61
- throw new Error('symbol (or file) argument is required for direction=package_importers');
64
+ if (!pkg) {
65
+ throw new Error('direction=package_importers requires either `symbol` or `file` arg (the npm package name, e.g. "chalk").');
66
+ }
62
67
  return { package: pkg, importers: filesImportingPackage(graph, pkg).slice(0, limit) };
63
68
  }
64
69
  default:
65
- throw new Error(`unknown direction: ${direction}`);
70
+ throw new Error(`unknown direction "${direction}". Valid: imports, exports, importers, symbol_defs, package_importers.`);
66
71
  }
67
72
  },
68
73
  };
@@ -1 +1 @@
1
- {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../../src/mcp/tools/graph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../../../src/mcp/tools/graph.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { scanRepository } from '../../core/repositoryScanner.js';
2
2
  import { buildCodeGraph } from '../../core/codeGraph.js';
3
3
  import { loadCachedGraph, saveCachedGraph } from '../../core/indexCache.js';
4
4
  import { computeImpact } from '../../core/impact.js';
5
+ import { loadWorkspace } from '../../core/workspace.js';
5
6
  import { paginate, listChecksum, readPageParams } from '../pagination.js';
6
7
  import { emitProgress } from '../progress.js';
7
8
  export const impactTool = {
@@ -22,6 +23,10 @@ export const impactTool = {
22
23
  type: 'number',
23
24
  description: 'Maximum BFS hops from the target. Default 10. Reports `truncated: true` when exceeded.',
24
25
  },
26
+ cross_repo: {
27
+ type: 'boolean',
28
+ description: '1.6+ — when true, also fold in callers from sibling repos registered via `projscan workspace add`. Each cross-repo file is annotated with its repo name. Symbol-mode only; file-mode cross-repo requires path resolution that this first cut does not perform.',
29
+ },
25
30
  cursor: { type: 'string', description: 'Opaque cursor from a previous response.' },
26
31
  page_size: { type: 'number', description: 'Items per page (default 50, max 500).' },
27
32
  max_tokens: { type: 'number', description: 'Cap response to roughly this many tokens.' },
@@ -31,10 +36,10 @@ export const impactTool = {
31
36
  const file = typeof args.file === 'string' && args.file.length > 0 ? args.file : null;
32
37
  const symbol = typeof args.symbol === 'string' && args.symbol.length > 0 ? args.symbol : null;
33
38
  if (!file && !symbol) {
34
- throw new Error('Provide either `file` or `symbol`.');
39
+ throw new Error('projscan_impact needs exactly one of `file` (a repo-relative path) or `symbol` (an exported name). Pass `file` for "what files transitively import this?" and `symbol` for "what calls this exported name?"');
35
40
  }
36
41
  if (file && symbol) {
37
- throw new Error('`file` and `symbol` are mutually exclusive - pass exactly one.');
42
+ throw new Error('`file` and `symbol` are mutually exclusive pass exactly one. Use `file` for file-level blast radius, `symbol` for symbol-level callers.');
38
43
  }
39
44
  const maxDistance = typeof args.max_distance === 'number' ? args.max_distance : undefined;
40
45
  emitProgress(0, 3, 'scanning repository');
@@ -45,7 +50,12 @@ export const impactTool = {
45
50
  await saveCachedGraph(rootPath, graph);
46
51
  emitProgress(2, 3, 'computing impact');
47
52
  const target = file ? { kind: 'file', value: file } : { kind: 'symbol', value: symbol };
48
- const report = computeImpact(graph, target, maxDistance !== undefined ? { maxDistance } : {});
53
+ const crossRepo = args.cross_repo === true;
54
+ const crossRepoGraphs = crossRepo ? await buildCrossRepoGraphs(rootPath) : undefined;
55
+ const report = computeImpact(graph, target, {
56
+ ...(maxDistance !== undefined ? { maxDistance } : {}),
57
+ ...(crossRepoGraphs ? { crossRepoGraphs } : {}),
58
+ });
49
59
  const page = paginate(report.reachable, readPageParams(args), listChecksum(report.reachable));
50
60
  emitProgress(3, 3, 'done');
51
61
  return {
@@ -56,4 +66,27 @@ export const impactTool = {
56
66
  };
57
67
  },
58
68
  };
69
+ /**
70
+ * 1.6+ — load the cross-repo workspace and build a CodeGraph for each
71
+ * registered sibling. Returns an empty Map if no workspace is
72
+ * registered or no siblings exist; the impact module handles that
73
+ * case as a no-op cross-repo fold.
74
+ */
75
+ async function buildCrossRepoGraphs(rootPath) {
76
+ const out = new Map();
77
+ const workspace = await loadWorkspace(rootPath);
78
+ if (!workspace || workspace.repos.length === 0)
79
+ return out;
80
+ for (const repo of workspace.repos) {
81
+ try {
82
+ const scan = await scanRepository(repo.path);
83
+ const repoGraph = await buildCodeGraph(repo.path, scan.files);
84
+ out.set(repo.name, repoGraph);
85
+ }
86
+ catch {
87
+ // Skip repos that fail to scan (transient I/O, missing dir, etc.).
88
+ }
89
+ }
90
+ return out;
91
+ }
59
92
  //# sourceMappingURL=impact.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../../src/mcp/tools/impact.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../../src/mcp/tools/impact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { McpTool } from './_shared.js';
2
+ /**
3
+ * `projscan_memory` (1.5+) — surface the local Project Memory store
4
+ * so an agent (or the user via the CLI) can see which issues this
5
+ * project has been carrying across many runs and act on them.
6
+ *
7
+ * Subactions:
8
+ * - "current" (default): aggregate counts (total runs, rules tracked,
9
+ * stable-rule count, last update timestamp).
10
+ * - "stable": rules that have surfaced across enough runs over enough
11
+ * time to count as "user has accepted" — paired with a ready-to-paste
12
+ * `.projscanrc.disableRules` snippet.
13
+ * - "runs": every tracked rule with its observation history. Useful
14
+ * for debugging the memory's view of the project.
15
+ * - "forget": drop a single rule's history (requires `rule` arg).
16
+ *
17
+ * Read-only except `forget`.
18
+ */
19
+ export declare const memoryTool: McpTool;
@@ -0,0 +1,134 @@
1
+ import { findAcceptedHotspots, findStableRules, forgetHotspot, forgetRule, loadMemory, saveMemory, } from '../../core/memory.js';
2
+ /**
3
+ * `projscan_memory` (1.5+) — surface the local Project Memory store
4
+ * so an agent (or the user via the CLI) can see which issues this
5
+ * project has been carrying across many runs and act on them.
6
+ *
7
+ * Subactions:
8
+ * - "current" (default): aggregate counts (total runs, rules tracked,
9
+ * stable-rule count, last update timestamp).
10
+ * - "stable": rules that have surfaced across enough runs over enough
11
+ * time to count as "user has accepted" — paired with a ready-to-paste
12
+ * `.projscanrc.disableRules` snippet.
13
+ * - "runs": every tracked rule with its observation history. Useful
14
+ * for debugging the memory's view of the project.
15
+ * - "forget": drop a single rule's history (requires `rule` arg).
16
+ *
17
+ * Read-only except `forget`.
18
+ */
19
+ export const memoryTool = {
20
+ name: 'projscan_memory',
21
+ description: 'Inspect or prune the local Project Memory: which analyzer rules have been surfacing repeatedly without being addressed, and what to do about them. Use when an agent wants to know "what is this project tolerating and could quiet down via .projscanrc?"',
22
+ inputSchema: {
23
+ type: 'object',
24
+ properties: {
25
+ action: {
26
+ type: 'string',
27
+ enum: ['current', 'stable', 'runs', 'accepted', 'forget', 'forget-hotspot'],
28
+ description: 'Subaction. "current" returns aggregate counts. "stable" returns long-running rules with a config-snippet suggestion. "runs" returns every tracked rule. "accepted" (1.5+) returns files Project Memory marks as accepted load-bearing debt (top-K hotspot for ≥ 5 runs over ≥ 7 days without improving). "forget" drops one rule. "forget-hotspot" drops one file from hotspot memory.',
29
+ },
30
+ rule: {
31
+ type: 'string',
32
+ description: '"forget" only — the rule id to drop from memory.',
33
+ },
34
+ file: {
35
+ type: 'string',
36
+ description: '"forget-hotspot" only — the repo-relative path to drop from hotspot memory.',
37
+ },
38
+ },
39
+ },
40
+ handler: async (args, rootPath) => {
41
+ const action = typeof args.action === 'string' ? args.action : 'current';
42
+ const memory = await loadMemory(rootPath);
43
+ switch (action) {
44
+ case 'current':
45
+ return summarize(memory);
46
+ case 'stable':
47
+ return stableView(memory);
48
+ case 'runs':
49
+ return runsView(memory);
50
+ case 'accepted':
51
+ return acceptedView(memory);
52
+ case 'forget': {
53
+ const rule = typeof args.rule === 'string' ? args.rule : '';
54
+ if (!rule)
55
+ throw new Error('forget action requires a "rule" argument');
56
+ const existed = forgetRule(memory, rule);
57
+ if (existed)
58
+ await saveMemory(rootPath, memory);
59
+ return { action: 'forget', rule, dropped: existed };
60
+ }
61
+ case 'forget-hotspot': {
62
+ const file = typeof args.file === 'string' ? args.file : '';
63
+ if (!file)
64
+ throw new Error('forget-hotspot action requires a "file" argument');
65
+ const existed = forgetHotspot(memory, file);
66
+ if (existed)
67
+ await saveMemory(rootPath, memory);
68
+ return { action: 'forget-hotspot', file, dropped: existed };
69
+ }
70
+ default:
71
+ throw new Error(`Unknown action "${action}". Valid actions: current, stable, runs, accepted, forget, forget-hotspot.`);
72
+ }
73
+ },
74
+ };
75
+ function summarize(memory) {
76
+ const stableCount = findStableRules(memory).length;
77
+ const acceptedCount = findAcceptedHotspots(memory).length;
78
+ return {
79
+ schemaVersion: memory.schemaVersion,
80
+ totalRuns: memory.totalRuns,
81
+ rulesTracked: Object.keys(memory.rules).length,
82
+ hotspotsTracked: Object.keys(memory.hotspots ?? {}).length,
83
+ stableRuleCount: stableCount,
84
+ acceptedHotspotCount: acceptedCount,
85
+ lastUpdatedAt: memory.lastUpdatedAt,
86
+ };
87
+ }
88
+ function acceptedView(memory) {
89
+ const accepted = findAcceptedHotspots(memory);
90
+ return {
91
+ totalRuns: memory.totalRuns,
92
+ acceptedCount: accepted.length,
93
+ accepted: accepted.map((o) => ({
94
+ file: o.file,
95
+ runCount: o.runCount,
96
+ firstSeenAt: o.firstSeenAt,
97
+ lastSeenAt: o.lastSeenAt,
98
+ lastCc: o.lastCc,
99
+ lastChurn: o.lastChurn,
100
+ })),
101
+ };
102
+ }
103
+ function stableView(memory) {
104
+ const stable = findStableRules(memory);
105
+ // A ready-to-paste config snippet so the user can disable everything
106
+ // they've effectively accepted in one move.
107
+ const disableRulesSnippet = stable.length > 0
108
+ ? {
109
+ disableRules: stable.map((r) => r.ruleId),
110
+ }
111
+ : undefined;
112
+ return {
113
+ totalRuns: memory.totalRuns,
114
+ stableCount: stable.length,
115
+ stable: stable.map((r) => ({
116
+ ruleId: r.ruleId,
117
+ runCount: r.runCount,
118
+ firstSeenAt: r.firstSeenAt,
119
+ lastSeenAt: r.lastSeenAt,
120
+ })),
121
+ ...(disableRulesSnippet
122
+ ? { configSuggestion: { '.projscanrc.json': disableRulesSnippet } }
123
+ : {}),
124
+ };
125
+ }
126
+ function runsView(memory) {
127
+ const all = Object.values(memory.rules).sort((a, b) => b.runCount - a.runCount);
128
+ return {
129
+ totalRuns: memory.totalRuns,
130
+ rulesTracked: all.length,
131
+ rules: all,
132
+ };
133
+ }
134
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/mcp/tools/memory.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import { computeReview } from '../../core/review.js';
1
+ import { computeReview, selectReviewTier, shapeReviewForTier } from '../../core/review.js';
2
2
  import { detectWorkspaces, filterFilesByPackage } from '../../core/monorepo.js';
3
3
  import { emitProgress } from '../progress.js';
4
4
  export const reviewTool = {
5
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.',
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. Pass `max_cost_tokens` (1.5+) to get a budget-shaped response: <3000 returns verdict-only, <7000 returns a summary, otherwise the full review.',
7
7
  inputSchema: {
8
8
  type: 'object',
9
9
  properties: {
@@ -12,7 +12,14 @@ export const reviewTool = {
12
12
  description: 'Base ref (branch, tag, sha). Default: origin/main, falling back to main/master/HEAD~1.',
13
13
  },
14
14
  head: { type: 'string', description: 'Head ref. Default: HEAD.' },
15
- max_tokens: { type: 'number', description: 'Cap the response to roughly this many tokens.' },
15
+ max_tokens: {
16
+ type: 'number',
17
+ description: 'Cap the response via post-hoc array trimming.',
18
+ },
19
+ max_cost_tokens: {
20
+ type: 'number',
21
+ description: '1.5+ — adaptive shape budget. Tool picks a tier based on this value: <3000 verdict-only, <7000 summary, otherwise full. Differs from `max_tokens` (post-hoc truncation): the tool reshapes the response BEFORE serializing, so an agent on a tight budget gets a response sized to fit instead of a truncated full one.',
22
+ },
16
23
  package: {
17
24
  type: 'string',
18
25
  description: 'Optional. Workspace package name to scope all sections of the review to a single package.',
@@ -48,7 +55,21 @@ export const reviewTool = {
48
55
  report.dependencyChanges = report.dependencyChanges.filter((d) => d.workspace === target);
49
56
  }
50
57
  emitProgress(4, 4, 'done');
51
- return report;
58
+ // 1.5 — adaptive shape based on max_cost_tokens. With no budget,
59
+ // returns the full report unchanged. With a budget, picks a tier
60
+ // and reshapes — the response carries `tier` so the agent can
61
+ // detect what they got and re-call at a higher budget if needed.
62
+ const maxCostTokens = typeof args.max_cost_tokens === 'number' && Number.isFinite(args.max_cost_tokens)
63
+ ? args.max_cost_tokens
64
+ : undefined;
65
+ const tier = selectReviewTier(maxCostTokens);
66
+ if (tier === 'full' && maxCostTokens === undefined) {
67
+ // Preserve existing 1.4-and-earlier shape (no `tier` field) when
68
+ // no budget was given. Adding the field unconditionally would be
69
+ // additive but slightly noisy on every call.
70
+ return report;
71
+ }
72
+ return shapeReviewForTier(report, tier);
52
73
  },
53
74
  };
54
75
  //# sourceMappingURL=review.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/mcp/tools/review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/mcp/tools/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { McpTool } from './_shared.js';
2
+ /**
3
+ * `projscan_taint` (1.6+) — surface source-to-sink reachability flows
4
+ * over the per-function call graph. Sources (e.g. `process.env.X`,
5
+ * `req.body`) and sinks (e.g. `exec`, `eval`, `db.query`) come from a
6
+ * built-in default list; users add project-specific names via the
7
+ * `.projscanrc.json` `taint` block.
8
+ *
9
+ * The flow list is exact-match, dedup'd by (sourceFn, sinkFn). It does
10
+ * NOT track variable-level flow — if a function reads a source AND
11
+ * calls a sink (directly or transitively), it surfaces. False positives
12
+ * are expected for functions that launder taint safely; users can
13
+ * declare safe wrappers as sinks-with-disableRules.
14
+ */
15
+ export declare const taintTool: McpTool;