projscan 1.6.2 → 1.8.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 (108) hide show
  1. package/dist/core/applyFix.js +5 -46
  2. package/dist/core/applyFix.js.map +1 -1
  3. package/dist/core/embeddings.d.ts +9 -0
  4. package/dist/core/embeddings.js +45 -5
  5. package/dist/core/embeddings.js.map +1 -1
  6. package/dist/core/languages/LanguageAdapter.d.ts +1 -1
  7. package/dist/core/languages/cppAdapter.d.ts +2 -0
  8. package/dist/core/languages/cppAdapter.js +145 -0
  9. package/dist/core/languages/cppAdapter.js.map +1 -0
  10. package/dist/core/languages/cppCallSites.d.ts +13 -0
  11. package/dist/core/languages/cppCallSites.js +62 -0
  12. package/dist/core/languages/cppCallSites.js.map +1 -0
  13. package/dist/core/languages/cppCyclomatic.d.ts +25 -0
  14. package/dist/core/languages/cppCyclomatic.js +82 -0
  15. package/dist/core/languages/cppCyclomatic.js.map +1 -0
  16. package/dist/core/languages/cppExports.d.ts +13 -0
  17. package/dist/core/languages/cppExports.js +120 -0
  18. package/dist/core/languages/cppExports.js.map +1 -0
  19. package/dist/core/languages/cppFunctions.d.ts +30 -0
  20. package/dist/core/languages/cppFunctions.js +281 -0
  21. package/dist/core/languages/cppFunctions.js.map +1 -0
  22. package/dist/core/languages/cppImports.d.ts +25 -0
  23. package/dist/core/languages/cppImports.js +77 -0
  24. package/dist/core/languages/cppImports.js.map +1 -0
  25. package/dist/core/languages/cppManifests.d.ts +18 -0
  26. package/dist/core/languages/cppManifests.js +41 -0
  27. package/dist/core/languages/cppManifests.js.map +1 -0
  28. package/dist/core/languages/kotlinAdapter.d.ts +2 -0
  29. package/dist/core/languages/kotlinAdapter.js +141 -0
  30. package/dist/core/languages/kotlinAdapter.js.map +1 -0
  31. package/dist/core/languages/kotlinCallSites.d.ts +15 -0
  32. package/dist/core/languages/kotlinCallSites.js +62 -0
  33. package/dist/core/languages/kotlinCallSites.js.map +1 -0
  34. package/dist/core/languages/kotlinCyclomatic.d.ts +24 -0
  35. package/dist/core/languages/kotlinCyclomatic.js +54 -0
  36. package/dist/core/languages/kotlinCyclomatic.js.map +1 -0
  37. package/dist/core/languages/kotlinExports.d.ts +13 -0
  38. package/dist/core/languages/kotlinExports.js +103 -0
  39. package/dist/core/languages/kotlinExports.js.map +1 -0
  40. package/dist/core/languages/kotlinFunctions.d.ts +27 -0
  41. package/dist/core/languages/kotlinFunctions.js +147 -0
  42. package/dist/core/languages/kotlinFunctions.js.map +1 -0
  43. package/dist/core/languages/kotlinImports.d.ts +25 -0
  44. package/dist/core/languages/kotlinImports.js +74 -0
  45. package/dist/core/languages/kotlinImports.js.map +1 -0
  46. package/dist/core/languages/kotlinManifests.d.ts +20 -0
  47. package/dist/core/languages/kotlinManifests.js +46 -0
  48. package/dist/core/languages/kotlinManifests.js.map +1 -0
  49. package/dist/core/languages/registry.js +16 -1
  50. package/dist/core/languages/registry.js.map +1 -1
  51. package/dist/core/languages/swiftAdapter.d.ts +2 -0
  52. package/dist/core/languages/swiftAdapter.js +130 -0
  53. package/dist/core/languages/swiftAdapter.js.map +1 -0
  54. package/dist/core/languages/swiftCallSites.d.ts +14 -0
  55. package/dist/core/languages/swiftCallSites.js +60 -0
  56. package/dist/core/languages/swiftCallSites.js.map +1 -0
  57. package/dist/core/languages/swiftCyclomatic.d.ts +24 -0
  58. package/dist/core/languages/swiftCyclomatic.js +54 -0
  59. package/dist/core/languages/swiftCyclomatic.js.map +1 -0
  60. package/dist/core/languages/swiftExports.d.ts +13 -0
  61. package/dist/core/languages/swiftExports.js +95 -0
  62. package/dist/core/languages/swiftExports.js.map +1 -0
  63. package/dist/core/languages/swiftFunctions.d.ts +28 -0
  64. package/dist/core/languages/swiftFunctions.js +162 -0
  65. package/dist/core/languages/swiftFunctions.js.map +1 -0
  66. package/dist/core/languages/swiftImports.d.ts +26 -0
  67. package/dist/core/languages/swiftImports.js +45 -0
  68. package/dist/core/languages/swiftImports.js.map +1 -0
  69. package/dist/core/languages/swiftManifests.d.ts +17 -0
  70. package/dist/core/languages/swiftManifests.js +49 -0
  71. package/dist/core/languages/swiftManifests.js.map +1 -0
  72. package/dist/core/languages/treeSitterLoader.js +21 -1
  73. package/dist/core/languages/treeSitterLoader.js.map +1 -1
  74. package/dist/core/memory.d.ts +30 -0
  75. package/dist/core/memory.js +42 -0
  76. package/dist/core/memory.js.map +1 -1
  77. package/dist/core/session.d.ts +23 -4
  78. package/dist/core/session.js +28 -8
  79. package/dist/core/session.js.map +1 -1
  80. package/dist/core/taint.d.ts +17 -0
  81. package/dist/core/taint.js +19 -1
  82. package/dist/core/taint.js.map +1 -1
  83. package/dist/grammars/tree-sitter-cpp.wasm +0 -0
  84. package/dist/grammars/tree-sitter-kotlin.wasm +0 -0
  85. package/dist/grammars/tree-sitter-swift.wasm +0 -0
  86. package/dist/mcp/server.js +107 -11
  87. package/dist/mcp/server.js.map +1 -1
  88. package/dist/mcp/sessionTouchScanner.js +8 -2
  89. package/dist/mcp/sessionTouchScanner.js.map +1 -1
  90. package/dist/mcp/tools/_shared.d.ts +25 -1
  91. package/dist/mcp/tools/_shared.js.map +1 -1
  92. package/dist/mcp/tools/costSummary.d.ts +26 -0
  93. package/dist/mcp/tools/costSummary.js +152 -0
  94. package/dist/mcp/tools/costSummary.js.map +1 -0
  95. package/dist/mcp/tools/fixSuggest.js +23 -2
  96. package/dist/mcp/tools/fixSuggest.js.map +1 -1
  97. package/dist/mcp/tools/memory.js +37 -4
  98. package/dist/mcp/tools/memory.js.map +1 -1
  99. package/dist/mcp/tools/reviewWatch.d.ts +7 -0
  100. package/dist/mcp/tools/reviewWatch.js +228 -0
  101. package/dist/mcp/tools/reviewWatch.js.map +1 -0
  102. package/dist/mcp/tools.js +4 -0
  103. package/dist/mcp/tools.js.map +1 -1
  104. package/dist/tool-manifest.json +52 -4
  105. package/dist/utils/atomicWrite.d.ts +26 -0
  106. package/dist/utils/atomicWrite.js +69 -0
  107. package/dist/utils/atomicWrite.js.map +1 -0
  108. package/package.json +6 -2
@@ -1,6 +1,25 @@
1
1
  import { scanRepository } from '../../core/repositoryScanner.js';
2
2
  import { collectIssues } from '../../core/issueEngine.js';
3
3
  import { findIssue, suggestFixForIssue, syntheticIssue, } from '../../core/fixSuggest.js';
4
+ import { computeRuleConfidence, computeRuleConfidenceScore, loadMemory, } from '../../core/memory.js';
5
+ /**
6
+ * 1.7+ — Build a confidence sidecar from Project Memory's per-rule
7
+ * fix-rate. Returned alongside every fix suggestion so the agent can
8
+ * deprioritize rules the user has historically ignored. Best-effort —
9
+ * if memory is missing or unreadable, returns "medium".
10
+ */
11
+ async function attachConfidence(rootPath, ruleId) {
12
+ try {
13
+ const memory = await loadMemory(rootPath);
14
+ return {
15
+ level: computeRuleConfidence(memory, ruleId),
16
+ score: computeRuleConfidenceScore(memory, ruleId),
17
+ };
18
+ }
19
+ catch {
20
+ return { level: 'medium', score: 0.5 };
21
+ }
22
+ }
4
23
  export const fixSuggestTool = {
5
24
  name: 'projscan_fix_suggest',
6
25
  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.',
@@ -41,12 +60,14 @@ export const fixSuggestTool = {
41
60
  return { matched: false, reason: `No open issue with id "${issueId}" in current doctor run.` };
42
61
  }
43
62
  const fix = await suggestFixForIssue(found, rootPath);
44
- return { matched: true, fix };
63
+ const confidence = await attachConfidence(rootPath, found.id);
64
+ return { matched: true, fix, confidence };
45
65
  }
46
66
  if (file && rule) {
47
67
  const synthetic = syntheticIssue(rule, file, severity);
48
68
  const fix = await suggestFixForIssue(synthetic, rootPath);
49
- return { matched: true, fix, synthetic: true };
69
+ const confidence = await attachConfidence(rootPath, synthetic.id);
70
+ return { matched: true, fix, synthetic: true, confidence };
50
71
  }
51
72
  return {
52
73
  matched: false,
@@ -1 +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"}
1
+ {"version":3,"file":"fixSuggest.js","sourceRoot":"","sources":["../../../src/mcp/tools/fixSuggest.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { findAcceptedHotspots, findStableRules, forgetHotspot, forgetRule, loadMemory, saveMemory, } from '../../core/memory.js';
1
+ import { computeRuleConfidence, computeRuleConfidenceScore, findAcceptedHotspots, findStableRules, forgetHotspot, forgetRule, loadMemory, saveMemory, } from '../../core/memory.js';
2
2
  /**
3
3
  * `projscan_memory` (1.5+) — surface the local Project Memory store
4
4
  * so an agent (or the user via the CLI) can see which issues this
@@ -24,8 +24,8 @@ export const memoryTool = {
24
24
  properties: {
25
25
  action: {
26
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.',
27
+ enum: ['current', 'stable', 'runs', 'accepted', 'confidence', '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). "confidence" (1.7+) returns each tracked rule labelled high/medium/low based on observed fix-rate — high = user has fixed instances; low = persistently ignored. "forget" drops one rule. "forget-hotspot" drops one file from hotspot memory.',
29
29
  },
30
30
  rule: {
31
31
  type: 'string',
@@ -49,6 +49,8 @@ export const memoryTool = {
49
49
  return runsView(memory);
50
50
  case 'accepted':
51
51
  return acceptedView(memory);
52
+ case 'confidence':
53
+ return confidenceView(memory);
52
54
  case 'forget': {
53
55
  const rule = typeof args.rule === 'string' ? args.rule : '';
54
56
  if (!rule)
@@ -68,7 +70,7 @@ export const memoryTool = {
68
70
  return { action: 'forget-hotspot', file, dropped: existed };
69
71
  }
70
72
  default:
71
- throw new Error(`Unknown action "${action}". Valid actions: current, stable, runs, accepted, forget, forget-hotspot.`);
73
+ throw new Error(`Unknown action "${action}". Valid actions: current, stable, runs, accepted, confidence, forget, forget-hotspot.`);
72
74
  }
73
75
  },
74
76
  };
@@ -131,4 +133,35 @@ function runsView(memory) {
131
133
  rules: all,
132
134
  };
133
135
  }
136
+ /**
137
+ * 1.7+ — per-rule confidence view. For each tracked rule, compute the
138
+ * derived `level` (high / medium / low) and a numeric `score` in [0, 1].
139
+ * Sorted by score descending so the agent sees the most actionable
140
+ * (highest-confidence) suggestions first. Includes a one-line summary
141
+ * counting rules per level so the agent can decide at a glance whether
142
+ * to call out a "noisy rules" deprioritization.
143
+ */
144
+ function confidenceView(memory) {
145
+ const ranked = Object.keys(memory.rules)
146
+ .map((ruleId) => {
147
+ const obs = memory.rules[ruleId];
148
+ return {
149
+ ruleId,
150
+ level: computeRuleConfidence(memory, ruleId),
151
+ score: Number(computeRuleConfidenceScore(memory, ruleId).toFixed(3)),
152
+ runCount: obs.runCount,
153
+ fixedCount: obs.fixedCount,
154
+ };
155
+ })
156
+ .sort((a, b) => b.score - a.score);
157
+ const counts = { high: 0, medium: 0, low: 0 };
158
+ for (const r of ranked)
159
+ counts[r.level] += 1;
160
+ return {
161
+ totalRuns: memory.totalRuns,
162
+ rulesTracked: ranked.length,
163
+ counts,
164
+ rules: ranked,
165
+ };
166
+ }
134
167
  //# sourceMappingURL=memory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/mcp/tools/memory.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,UAAU,EACV,UAAU,EACV,UAAU,GAEX,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAY;IACjC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,4PAA4P;IAC9P,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,gBAAgB,CAAC;gBAC3E,WAAW,EACT,wXAAwX;aAC3X;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kDAAkD;aAChE;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,6EAA6E;aAC3F;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;QAE1C,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,SAAS;gBACZ,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;YAC3B,KAAK,QAAQ;gBACX,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;YAC5B,KAAK,MAAM;gBACT,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC1B,KAAK,UAAU;gBACb,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;YAC9B,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;gBACvE,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACzC,IAAI,OAAO;oBAAE,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAChD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YACtD,CAAC;YACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;gBAC/E,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC5C,IAAI,OAAO;oBAAE,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAChD,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YAC9D,CAAC;YACD;gBACE,MAAM,IAAI,KAAK,CACb,mBAAmB,MAAM,4EAA4E,CACtG,CAAC;QACN,CAAC;IACH,CAAC;CACF,CAAC;AAEF,SAAS,SAAS,CAAC,MAAqB;IACtC,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IACnD,MAAM,aAAa,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC1D,OAAO;QACL,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM;QAC9C,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM;QAC1D,eAAe,EAAE,WAAW;QAC5B,oBAAoB,EAAE,aAAa;QACnC,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,MAAqB;IACzC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC9C,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,aAAa,EAAE,QAAQ,CAAC,MAAM;QAC9B,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,MAAqB;IACvC,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACvC,qEAAqE;IACrE,4CAA4C;IAC5C,MAAM,mBAAmB,GACvB,MAAM,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,CAAC;YACE,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;SAC1C;QACH,CAAC,CAAC,SAAS,CAAC;IAChB,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;SACzB,CAAC,CAAC;QACH,GAAG,CAAC,mBAAmB;YACrB,CAAC,CAAC,EAAE,gBAAgB,EAAE,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,EAAE;YACnE,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,MAAqB;IACrC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAChF,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EAAE,GAAG,CAAC,MAAM;QACxB,KAAK,EAAE,GAAG;KACX,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/mcp/tools/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { McpTool } from './_shared.js';
2
+ export declare const reviewWatchTool: McpTool;
3
+ /**
4
+ * Test-only: drop all watches in the module. Lets unit tests verify
5
+ * start/stop semantics without leaking timers across cases.
6
+ */
7
+ export declare function __resetReviewWatchesForTests(): void;
@@ -0,0 +1,228 @@
1
+ import { computeReview } from '../../core/review.js';
2
+ const watches = new Map();
3
+ const DEFAULT_INTERVAL_S = 30;
4
+ const MIN_INTERVAL_S = 5;
5
+ const MAX_INTERVAL_S = 600;
6
+ export const reviewWatchTool = {
7
+ name: 'projscan_review_watch',
8
+ description: 'Long-running PR review. Polls a base+head ref pair on an interval and emits a notifications/projscan/pr_changed notification whenever the review verdict, SHAs, flow count, or risky-function set changes. Pairs with projscan_review (one-shot) — use this when an agent wants to react to pushes on a PR without re-asking. Actions: start (returns initial review + watchId) / stop / list.',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ action: {
13
+ type: 'string',
14
+ enum: ['start', 'stop', 'list'],
15
+ description: '"start" begins polling (returns initial review + watchId). "stop" cancels a watch by id. "list" enumerates active watches.',
16
+ },
17
+ base: {
18
+ type: 'string',
19
+ description: 'Base ref. Default: origin/main → main → origin/master → master → HEAD~1. (start only)',
20
+ },
21
+ head: {
22
+ type: 'string',
23
+ description: 'Head ref. Default: HEAD. (start only)',
24
+ },
25
+ interval_seconds: {
26
+ type: 'number',
27
+ description: `Poll interval in seconds. Default: ${DEFAULT_INTERVAL_S}. Min: ${MIN_INTERVAL_S}, max: ${MAX_INTERVAL_S}. (start only)`,
28
+ },
29
+ watchId: {
30
+ type: 'string',
31
+ description: 'Watch identifier returned by a previous "start". (stop only)',
32
+ },
33
+ },
34
+ },
35
+ handler: async (args, rootPath, context) => {
36
+ const action = typeof args.action === 'string' ? args.action : 'start';
37
+ if (action === 'list')
38
+ return listWatches();
39
+ if (action === 'stop')
40
+ return stopWatch(args, context);
41
+ if (action === 'start')
42
+ return startWatch(args, rootPath, context);
43
+ throw new Error(`Unknown action "${action}". Valid actions: start, stop, list.`);
44
+ },
45
+ };
46
+ async function startWatch(args, rootPath, context) {
47
+ const base = typeof args.base === 'string' && args.base ? args.base : undefined;
48
+ const head = typeof args.head === 'string' && args.head ? args.head : undefined;
49
+ const requestedInterval = typeof args.interval_seconds === 'number' && Number.isFinite(args.interval_seconds)
50
+ ? args.interval_seconds
51
+ : DEFAULT_INTERVAL_S;
52
+ const intervalSeconds = clamp(requestedInterval, MIN_INTERVAL_S, MAX_INTERVAL_S);
53
+ const intervalMs = Math.round(intervalSeconds * 1000);
54
+ // Initial review — synchronous, returned with the response so the agent
55
+ // gets a useful payload right away and can react before the first tick.
56
+ const initial = await computeReview(rootPath, { base, head });
57
+ const initialSignature = signatureOf(initial);
58
+ if (!context?.registerWatch || !context.notify) {
59
+ // Caller is in a transport without a notify channel (CLI smoke,
60
+ // tests). Return the initial review with a clear note that no
61
+ // watch is registered, instead of silently dropping the request.
62
+ return {
63
+ action: 'start',
64
+ watchId: null,
65
+ registered: false,
66
+ reason: 'No notify channel — server was started without one (or this is a non-MCP caller). Initial review attached; subscribe to the MCP notify channel to receive incremental updates.',
67
+ base: initial.base?.ref ?? base ?? null,
68
+ head: initial.head?.ref ?? head ?? null,
69
+ intervalSeconds,
70
+ report: initial,
71
+ };
72
+ }
73
+ // 1.8+ — register FIRST so we have a stable watchId, then arm the
74
+ // timer. The previous order (timer first, watchId set second)
75
+ // captured a `null` watchId in the tick closure for a brief window;
76
+ // it would never have fired in that window (intervalMs >= 5000ms)
77
+ // but the order is fragile and easy to break under refactor.
78
+ //
79
+ // Use crypto.randomUUID() inside the server's registerWatch so
80
+ // collisions under burst (1000 starts in 1ms) are impossible — the
81
+ // older `Math.random().toString(36)` shape was 36^8 ≈ 2.8T values,
82
+ // good in practice but not guaranteed.
83
+ let timer = null;
84
+ const watchId = context.registerWatch(() => {
85
+ if (timer)
86
+ clearInterval(timer);
87
+ timer = null;
88
+ watches.delete(watchId);
89
+ });
90
+ watches.set(watchId, {
91
+ watchId,
92
+ base: base ?? '(default)',
93
+ head: head ?? 'HEAD',
94
+ intervalMs,
95
+ startedAt: new Date().toISOString(),
96
+ ticks: 0,
97
+ lastSignature: initialSignature,
98
+ lastReportAt: new Date().toISOString(),
99
+ inFlight: false,
100
+ });
101
+ const tick = () => {
102
+ void runTick(rootPath, watchId, base, head, context);
103
+ };
104
+ timer = setInterval(tick, intervalMs);
105
+ // Don't keep the process alive for a polling interval alone — if the
106
+ // caller exits stdin, close() will fire and clear this. unref so a
107
+ // dangling watch doesn't block process exit in tests. Safe in
108
+ // production: the MCP server's own stdin/stdout I/O keeps the
109
+ // process alive; close() (driven by stdin EOF) clears the timer.
110
+ if (typeof timer.unref === 'function')
111
+ timer.unref();
112
+ return {
113
+ action: 'start',
114
+ watchId,
115
+ registered: true,
116
+ base: initial.base?.ref ?? base ?? null,
117
+ head: initial.head?.ref ?? head ?? null,
118
+ intervalSeconds,
119
+ report: initial,
120
+ };
121
+ }
122
+ async function runTick(rootPath, watchId, base, head, context) {
123
+ const state = watches.get(watchId);
124
+ if (!state)
125
+ return; // already cancelled
126
+ // 1.8+ — single-flight guard. If a previous tick is still computing
127
+ // (slow git, large repo, network-bound worktree fetch), drop this
128
+ // one rather than queue. Notification ordering matters more than
129
+ // not-missing-an-update: the next tick picks up the latest state.
130
+ if (state.inFlight)
131
+ return;
132
+ state.inFlight = true;
133
+ state.ticks += 1;
134
+ try {
135
+ let report;
136
+ try {
137
+ report = await computeReview(rootPath, { base, head });
138
+ }
139
+ catch (err) {
140
+ const message = err instanceof Error ? err.message : String(err);
141
+ context.notify?.('notifications/projscan/pr_review_error', { watchId, error: message });
142
+ return;
143
+ }
144
+ const sig = signatureOf(report);
145
+ if (sig === state.lastSignature)
146
+ return; // unchanged — silent
147
+ state.lastSignature = sig;
148
+ state.lastReportAt = new Date().toISOString();
149
+ context.notify?.('notifications/projscan/pr_changed', {
150
+ watchId,
151
+ base: report.base?.ref ?? base ?? null,
152
+ head: report.head?.ref ?? head ?? null,
153
+ baseSha: report.base?.resolvedSha ?? null,
154
+ headSha: report.head?.resolvedSha ?? null,
155
+ report,
156
+ });
157
+ }
158
+ finally {
159
+ // Always clear the in-flight flag, even when the tick threw or
160
+ // returned early — otherwise a single failure orphans the watch.
161
+ state.inFlight = false;
162
+ }
163
+ }
164
+ function stopWatch(args, context) {
165
+ const watchId = typeof args.watchId === 'string' ? args.watchId : '';
166
+ if (!watchId)
167
+ throw new Error('stop action requires a "watchId" argument');
168
+ // 1.8+ — always remove from `watches` regardless of whether the
169
+ // server's registry knew about it. The previous shape only deleted
170
+ // on context.unregisterWatch success, leaking module-level state
171
+ // when callers stopped a watch via a different transport (or in
172
+ // tests without a context). The cancel callback registered by
173
+ // start ALSO deletes the entry, so this is idempotent.
174
+ const watchExisted = watches.has(watchId);
175
+ watches.delete(watchId);
176
+ const cancelled = context?.unregisterWatch ? context.unregisterWatch(watchId) : false;
177
+ return {
178
+ action: 'stop',
179
+ watchId,
180
+ cancelled: cancelled || watchExisted,
181
+ watchNotFound: !watchExisted && !cancelled,
182
+ };
183
+ }
184
+ function listWatches() {
185
+ return {
186
+ action: 'list',
187
+ count: watches.size,
188
+ watches: [...watches.values()].map((w) => ({
189
+ watchId: w.watchId,
190
+ base: w.base,
191
+ head: w.head,
192
+ intervalMs: w.intervalMs,
193
+ startedAt: w.startedAt,
194
+ ticks: w.ticks,
195
+ lastReportAt: w.lastReportAt,
196
+ })),
197
+ };
198
+ }
199
+ /**
200
+ * A change-detection signature: tracks the bits of the review the
201
+ * agent cares about (verdict, SHAs, flow / risky-fn counts and names).
202
+ * Two ticks with identical signatures are silent; a delta on any of
203
+ * these triggers a `pr_changed` notification with the full report.
204
+ */
205
+ function signatureOf(report) {
206
+ const parts = [];
207
+ parts.push(report.verdict ?? '');
208
+ parts.push(report.base?.resolvedSha ?? '');
209
+ parts.push(report.head?.resolvedSha ?? '');
210
+ parts.push(String(report.changedFiles?.length ?? 0));
211
+ parts.push(String(report.newTaintFlows?.length ?? 0));
212
+ const risky = (report.riskyFunctions ?? []).map((f) => f.name).sort().join(',');
213
+ parts.push(risky);
214
+ return parts.join('|');
215
+ }
216
+ function clamp(n, lo, hi) {
217
+ if (!Number.isFinite(n))
218
+ return lo;
219
+ return Math.max(lo, Math.min(hi, n));
220
+ }
221
+ /**
222
+ * Test-only: drop all watches in the module. Lets unit tests verify
223
+ * start/stop semantics without leaking timers across cases.
224
+ */
225
+ export function __resetReviewWatchesForTests() {
226
+ watches.clear();
227
+ }
228
+ //# sourceMappingURL=reviewWatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewWatch.js","sourceRoot":"","sources":["../../../src/mcp/tools/reviewWatch.ts"],"names":[],"mappings":"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"}
package/dist/mcp/tools.js CHANGED
@@ -33,6 +33,8 @@ import { memoryTool } from './tools/memory.js';
33
33
  import { workspaceGraphTool } from './tools/workspaceGraph.js';
34
34
  import { applyFixTool } from './tools/applyFix.js';
35
35
  import { taintTool } from './tools/taint.js';
36
+ import { costSummaryTool } from './tools/costSummary.js';
37
+ import { reviewWatchTool } from './tools/reviewWatch.js';
36
38
  const tools = [
37
39
  analyzeTool,
38
40
  doctorTool,
@@ -59,6 +61,8 @@ const tools = [
59
61
  workspaceGraphTool,
60
62
  applyFixTool,
61
63
  taintTool,
64
+ costSummaryTool,
65
+ reviewWatchTool,
62
66
  ];
63
67
  export function getToolDefinitions() {
64
68
  return tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }));
@@ -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;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;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAM7C,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;IACV,UAAU;IACV,WAAW;IACX,UAAU;IACV,kBAAkB;IAClB,YAAY;IACZ,SAAS;CACV,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;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAMzD,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;IACV,UAAU;IACV,WAAW;IACX,UAAU;IACV,kBAAkB;IAClB,YAAY;IACZ,SAAS;IACT,eAAe;IACf,eAAe;CAChB,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,9 +1,9 @@
1
1
  {
2
2
  "name": "projscan",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "mcpProtocolVersion": "2025-03-26",
5
- "generatedAt": "2026-05-06T21:15:34.708Z",
6
- "toolCount": 25,
5
+ "generatedAt": "2026-05-07T22:25:02.535Z",
6
+ "toolCount": 27,
7
7
  "tools": [
8
8
  {
9
9
  "name": "projscan_analyze",
@@ -544,10 +544,11 @@
544
544
  "stable",
545
545
  "runs",
546
546
  "accepted",
547
+ "confidence",
547
548
  "forget",
548
549
  "forget-hotspot"
549
550
  ],
550
- "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."
551
+ "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). \"confidence\" (1.7+) returns each tracked rule labelled high/medium/low based on observed fix-rate — high = user has fixed instances; low = persistently ignored. \"forget\" drops one rule. \"forget-hotspot\" drops one file from hotspot memory."
551
552
  },
552
553
  "rule": {
553
554
  "type": "string",
@@ -641,6 +642,53 @@
641
642
  }
642
643
  }
643
644
  }
645
+ },
646
+ {
647
+ "name": "projscan_cost_summary",
648
+ "description": "Aggregate token-cost analytics from the current session's tool-call history. Returns total tokens spent, top spenders, per-tool typical/p95 estimates, and a static expected-cost catalog so the agent can budget pre-call. Pairs with the `_cost` sidecar attached to every tool result. Read-only. Note: the session event log is bounded at 500 entries, so the view reflects recent activity rather than the full lifetime of the session — for long-running sessions, older calls are dropped.",
649
+ "inputSchema": {
650
+ "type": "object",
651
+ "properties": {
652
+ "top": {
653
+ "type": "number",
654
+ "description": "Optional. Number of top spenders to return. Default: 10."
655
+ }
656
+ }
657
+ }
658
+ },
659
+ {
660
+ "name": "projscan_review_watch",
661
+ "description": "Long-running PR review. Polls a base+head ref pair on an interval and emits a notifications/projscan/pr_changed notification whenever the review verdict, SHAs, flow count, or risky-function set changes. Pairs with projscan_review (one-shot) — use this when an agent wants to react to pushes on a PR without re-asking. Actions: start (returns initial review + watchId) / stop / list.",
662
+ "inputSchema": {
663
+ "type": "object",
664
+ "properties": {
665
+ "action": {
666
+ "type": "string",
667
+ "enum": [
668
+ "start",
669
+ "stop",
670
+ "list"
671
+ ],
672
+ "description": "\"start\" begins polling (returns initial review + watchId). \"stop\" cancels a watch by id. \"list\" enumerates active watches."
673
+ },
674
+ "base": {
675
+ "type": "string",
676
+ "description": "Base ref. Default: origin/main → main → origin/master → master → HEAD~1. (start only)"
677
+ },
678
+ "head": {
679
+ "type": "string",
680
+ "description": "Head ref. Default: HEAD. (start only)"
681
+ },
682
+ "interval_seconds": {
683
+ "type": "number",
684
+ "description": "Poll interval in seconds. Default: 30. Min: 5, max: 600. (start only)"
685
+ },
686
+ "watchId": {
687
+ "type": "string",
688
+ "description": "Watch identifier returned by a previous \"start\". (stop only)"
689
+ }
690
+ }
691
+ }
644
692
  }
645
693
  ]
646
694
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Crash-safe file write: write to a tmp path, fsync the data, rename
3
+ * over the target, then best-effort fsync the parent directory.
4
+ *
5
+ * Three hardenings over a naive writeFile + rename:
6
+ *
7
+ * 1. Tmp filename uses randomUUID() so the path is unpredictable. A
8
+ * shared-system attacker can't pre-create a symlink at the tmp path
9
+ * before our write lands.
10
+ *
11
+ * 2. Open with the 'wx' flag (O_CREAT | O_EXCL). If the tmp path
12
+ * already exists for any reason (race, leftover, planted symlink),
13
+ * fs.open throws EEXIST instead of following the symlink. Belt-
14
+ * and-suspenders with #1.
15
+ *
16
+ * 3. fsync the file before rename, and best-effort fsync the parent
17
+ * dir after rename. Rename is atomic at the journal-record level
18
+ * on most filesystems, but without fsync the rename can survive a
19
+ * crash with empty tmp content (file metadata persists, data
20
+ * doesn't). Parent-dir sync is best-effort: Windows doesn't
21
+ * support it and some FUSE backends treat it as a no-op.
22
+ *
23
+ * Used by `applyFix` (1.6+) and `session.saveSession` (1.8+) to ensure
24
+ * that crashes mid-write never leave partially-written state on disk.
25
+ */
26
+ export declare function atomicWriteFile(absPath: string, content: string): Promise<void>;
@@ -0,0 +1,69 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ /**
5
+ * Crash-safe file write: write to a tmp path, fsync the data, rename
6
+ * over the target, then best-effort fsync the parent directory.
7
+ *
8
+ * Three hardenings over a naive writeFile + rename:
9
+ *
10
+ * 1. Tmp filename uses randomUUID() so the path is unpredictable. A
11
+ * shared-system attacker can't pre-create a symlink at the tmp path
12
+ * before our write lands.
13
+ *
14
+ * 2. Open with the 'wx' flag (O_CREAT | O_EXCL). If the tmp path
15
+ * already exists for any reason (race, leftover, planted symlink),
16
+ * fs.open throws EEXIST instead of following the symlink. Belt-
17
+ * and-suspenders with #1.
18
+ *
19
+ * 3. fsync the file before rename, and best-effort fsync the parent
20
+ * dir after rename. Rename is atomic at the journal-record level
21
+ * on most filesystems, but without fsync the rename can survive a
22
+ * crash with empty tmp content (file metadata persists, data
23
+ * doesn't). Parent-dir sync is best-effort: Windows doesn't
24
+ * support it and some FUSE backends treat it as a no-op.
25
+ *
26
+ * Used by `applyFix` (1.6+) and `session.saveSession` (1.8+) to ensure
27
+ * that crashes mid-write never leave partially-written state on disk.
28
+ */
29
+ export async function atomicWriteFile(absPath, content) {
30
+ const tmp = `${absPath}.projscan-tmp-${randomUUID()}`;
31
+ const handle = await fs.open(tmp, 'wx');
32
+ try {
33
+ await handle.writeFile(content, 'utf-8');
34
+ await handle.sync();
35
+ }
36
+ finally {
37
+ await handle.close();
38
+ }
39
+ // 1.8+ — clean up the tmp file if rename fails (e.g., the target is
40
+ // on a read-only filesystem, EACCES, or its parent dir was removed
41
+ // mid-write). Without this, retried writes leak abandoned tmp files
42
+ // forever — invisible until they pile up. Re-throw the original
43
+ // error so callers see the real cause; the unlink is best-effort.
44
+ try {
45
+ await fs.rename(tmp, absPath);
46
+ }
47
+ catch (err) {
48
+ try {
49
+ await fs.unlink(tmp);
50
+ }
51
+ catch {
52
+ // best-effort cleanup
53
+ }
54
+ throw err;
55
+ }
56
+ try {
57
+ const dir = await fs.open(path.dirname(absPath), 'r');
58
+ try {
59
+ await dir.sync();
60
+ }
61
+ finally {
62
+ await dir.close();
63
+ }
64
+ }
65
+ catch {
66
+ // ignore — best-effort
67
+ }
68
+ }
69
+ //# sourceMappingURL=atomicWrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomicWrite.js","sourceRoot":"","sources":["../../src/utils/atomicWrite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe,EAAE,OAAe;IACpE,MAAM,GAAG,GAAG,GAAG,OAAO,iBAAiB,UAAU,EAAE,EAAE,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IACD,oEAAoE;IACpE,mEAAmE;IACnE,oEAAoE;IACpE,gEAAgE;IAChE,kEAAkE;IAClE,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "projscan",
3
3
  "mcpName": "io.github.abhiyoheswaran1/projscan",
4
- "version": "1.6.2",
5
- "description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing for JavaScript, TypeScript, Python, Go, Java, Ruby, Rust, PHP, and C#; code graph, file + per-function AST cyclomatic complexity, per-function fan-in + fan-out, coupling + cycle detection, structural PR diff with HTML reporter, coverage report with HTML reporter, one-call PR review (projscan_review) with new-taint-flow detection, rule-driven fix suggestions + mechanical apply layer with rollback (projscan_apply_fix, projscan_fix_suggest, projscan_explain_issue), source-to-sink taint analysis (projscan_taint), transitive blast-radius analysis with cross-repo mode (projscan_impact for files and symbols), cross-repo workspace registration + intelligence (projscan_workspace_graph), per-function semantic search chunks (sub-file embeddings), monorepo workspace awareness with cross-package import policy + per-package dependencies / outdated / audit, BM25 + optional semantic search, cursor pagination, progress notifications, context-budgeted output, and a stable-surface CI guard. CLI on the side.",
4
+ "version": "1.8.0",
5
+ "description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing for JavaScript, TypeScript, Python, Go, Java, Ruby, Rust, PHP, C#, Kotlin, Swift, and C++; code graph, file + per-function AST cyclomatic complexity, per-function fan-in + fan-out, coupling + cycle detection, structural PR diff with HTML reporter, coverage report with HTML reporter, one-call PR review (projscan_review) and long-running PR-watch mode (projscan_review_watch), rule-driven fix suggestions + mechanical apply layer with rollback (projscan_apply_fix, projscan_fix_suggest, projscan_explain_issue), source-to-sink taint analysis (projscan_taint) with truncation reporting, transitive blast-radius analysis with cross-repo mode (projscan_impact for files and symbols), cross-repo workspace registration + intelligence (projscan_workspace_graph), per-function semantic search chunks (sub-file embeddings), per-rule confidence + cost-summary analytics (projscan_cost_summary), monorepo workspace awareness with cross-package import policy + per-package dependencies / outdated / audit, BM25 + optional semantic search, cursor pagination, progress notifications, context-budgeted output, and a stable-surface CI guard. CLI on the side.",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
@@ -84,6 +84,10 @@
84
84
  "@types/node": "^22.0.0",
85
85
  "@xenova/transformers": "^2.17.0",
86
86
  "eslint": "^10.0.3",
87
+ "tree-sitter-cli": "^0.26.8",
88
+ "tree-sitter-cpp": "^0.23.4",
89
+ "tree-sitter-kotlin": "^0.3.8",
90
+ "tree-sitter-swift": "^0.7.1",
87
91
  "typescript": "^5.6.0",
88
92
  "typescript-eslint": "^8.57.0",
89
93
  "vitest": "^2.1.0"