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
@@ -0,0 +1,67 @@
1
+ import { scanRepository } from '../../core/repositoryScanner.js';
2
+ import { buildCodeGraph } from '../../core/codeGraph.js';
3
+ import { computeTaint } from '../../core/taint.js';
4
+ import { loadConfig } from '../../utils/config.js';
5
+ /**
6
+ * `projscan_taint` (1.6+) — surface source-to-sink reachability flows
7
+ * over the per-function call graph. Sources (e.g. `process.env.X`,
8
+ * `req.body`) and sinks (e.g. `exec`, `eval`, `db.query`) come from a
9
+ * built-in default list; users add project-specific names via the
10
+ * `.projscanrc.json` `taint` block.
11
+ *
12
+ * The flow list is exact-match, dedup'd by (sourceFn, sinkFn). It does
13
+ * NOT track variable-level flow — if a function reads a source AND
14
+ * calls a sink (directly or transitively), it surfaces. False positives
15
+ * are expected for functions that launder taint safely; users can
16
+ * declare safe wrappers as sinks-with-disableRules.
17
+ */
18
+ export const taintTool = {
19
+ name: 'projscan_taint',
20
+ description: 'Source-to-sink reachability over the per-function call graph (1.6+). Surfaces "this function reads `process.env`/`req.body` and calls `exec`/`eval`/raw SQL" patterns. Defaults cover common JS / Python sources + sinks; project-specific names go in `.projscanrc.json` `taint`.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: {
24
+ sources: {
25
+ type: 'array',
26
+ items: { type: 'string' },
27
+ description: 'Additional source names to merge with the defaults. Match is by bare name (rightmost identifier), so `customSecretReader()` adds the literal "customSecretReader".',
28
+ },
29
+ sinks: {
30
+ type: 'array',
31
+ items: { type: 'string' },
32
+ description: 'Additional sink names to merge with the defaults. Useful for project-specific dangerous wrappers like `runRawSql` or `dangerouslyEval`.',
33
+ },
34
+ max_flows: {
35
+ type: 'number',
36
+ description: 'Cap the number of flows returned (most-direct first; same-function flows lead). Default 50.',
37
+ },
38
+ },
39
+ },
40
+ handler: async (args, rootPath) => {
41
+ const scan = await scanRepository(rootPath);
42
+ const graph = await buildCodeGraph(rootPath, scan.files);
43
+ const { config } = await loadConfig(rootPath);
44
+ const sources = [
45
+ ...(config.taint?.sources ?? []),
46
+ ...(Array.isArray(args.sources)
47
+ ? args.sources.filter((v) => typeof v === 'string')
48
+ : []),
49
+ ];
50
+ const sinks = [
51
+ ...(config.taint?.sinks ?? []),
52
+ ...(Array.isArray(args.sinks)
53
+ ? args.sinks.filter((v) => typeof v === 'string')
54
+ : []),
55
+ ];
56
+ const max = typeof args.max_flows === 'number' && args.max_flows > 0 ? args.max_flows : 50;
57
+ const report = computeTaint(graph, { sources, sinks });
58
+ if (!report.available)
59
+ return report;
60
+ return {
61
+ ...report,
62
+ flows: report.flows.slice(0, max),
63
+ truncated: report.flows.length > max,
64
+ };
65
+ },
66
+ };
67
+ //# sourceMappingURL=taint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taint.js","sourceRoot":"","sources":["../../../src/mcp/tools/taint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGnD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,SAAS,GAAY;IAChC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,oRAAoR;IACtR,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EACT,oKAAoK;aACvK;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EACT,yIAAyI;aAC5I;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,6FAA6F;aAChG;SACF;KACF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG;YACd,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC;YAChC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;gBAC7B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;gBAChE,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QACF,MAAM,KAAK,GAAG;YACZ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;YAC9B,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC3B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;gBAC9D,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;QACF,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,SAAS;YAAE,OAAO,MAAM,CAAC;QACrC,OAAO;YACL,GAAG,MAAM;YACT,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;YACjC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG;SACrC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -20,8 +20,9 @@ export const upgradeTool = {
20
20
  },
21
21
  handler: async (args, rootPath) => {
22
22
  const pkgName = typeof args.package === 'string' ? args.package : '';
23
- if (!pkgName)
24
- throw new Error('package argument is required');
23
+ if (!pkgName) {
24
+ throw new Error('package argument is required: pass an npm package name (e.g. "chalk" or "@types/node"). List candidates with projscan_outdated or projscan_dependencies.');
25
+ }
25
26
  const checkRegistry = args.check_registry === true;
26
27
  const scan = await scanRepository(rootPath);
27
28
  if (await isPythonDominated(rootPath, scan.files)) {
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../../src/mcp/tools/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAgB,MAAM,cAAc,CAAC;AAE/D,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,2PAA2P;IAC7P,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,iCAAiC;aAC/C;YACD,cAAc,EAAE;gBACd,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,mJAAmJ;aACtJ;SACF;QACD,QAAQ,EAAE,CAAC,SAAS,CAAC;KACtB;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAE5C,IAAI,MAAM,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,MAAM,EACJ,mHAAmH;gBACrH,IAAI,EAAE,OAAO;gBACb,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,SAAS;gBAChB,eAAe,EAAE,EAAE;gBACnB,SAAS,EAAE,EAAE;aACd,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;IAChF,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../../src/mcp/tools/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAgB,MAAM,cAAc,CAAC;AAE/D,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,2PAA2P;IAC7P,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,iCAAiC;aAC/C;YACD,cAAc,EAAE;gBACd,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,mJAAmJ;aACtJ;SACF;QACD,QAAQ,EAAE,CAAC,SAAS,CAAC;KACtB;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,0JAA0J,CAC3J,CAAC;QACJ,CAAC;QACD,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;QAE5C,IAAI,MAAM,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,MAAM,EACJ,mHAAmH;gBACrH,IAAI,EAAE,OAAO;gBACb,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,SAAS;gBAChB,eAAe,EAAE,EAAE;gBACnB,SAAS,EAAE,EAAE;aACd,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;IAChF,CAAC;CACF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { McpTool } from './_shared.js';
2
+ /**
3
+ * `projscan_workspace_graph` (1.6+) — cross-repo intelligence over the
4
+ * sibling repos registered via `projscan workspace add`. Distinct from
5
+ * `projscan_workspaces` (plural) which surfaces intra-repo monorepo
6
+ * packages within a single codebase.
7
+ *
8
+ * Subactions:
9
+ * - "list" — registered repos with parsed-file counts.
10
+ * - "graph" — every shared symbol exported by ≥ 2 registered repos
11
+ * (a candidate refactor / API contract surface).
12
+ * - "file_importers" — given a file in one registered repo, list all
13
+ * repos whose graphs include that file as a local importer
14
+ * (cross-repo impact spotlight).
15
+ *
16
+ * Read-only; does not mutate workspace state.
17
+ */
18
+ export declare const workspaceGraphTool: McpTool;
@@ -0,0 +1,188 @@
1
+ import path from 'node:path';
2
+ import { loadWorkspace } from '../../core/workspace.js';
3
+ import { scanRepository } from '../../core/repositoryScanner.js';
4
+ import { buildCodeGraph } from '../../core/codeGraph.js';
5
+ /**
6
+ * `projscan_workspace_graph` (1.6+) — cross-repo intelligence over the
7
+ * sibling repos registered via `projscan workspace add`. Distinct from
8
+ * `projscan_workspaces` (plural) which surfaces intra-repo monorepo
9
+ * packages within a single codebase.
10
+ *
11
+ * Subactions:
12
+ * - "list" — registered repos with parsed-file counts.
13
+ * - "graph" — every shared symbol exported by ≥ 2 registered repos
14
+ * (a candidate refactor / API contract surface).
15
+ * - "file_importers" — given a file in one registered repo, list all
16
+ * repos whose graphs include that file as a local importer
17
+ * (cross-repo impact spotlight).
18
+ *
19
+ * Read-only; does not mutate workspace state.
20
+ */
21
+ export const workspaceGraphTool = {
22
+ name: 'projscan_workspace_graph',
23
+ description: 'Cross-repo intelligence over sibling repos registered via `projscan workspace add`. Use to answer "what other repos import this file/symbol?" or "what symbols are shared across the workspace?" Read-only.',
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {
27
+ action: {
28
+ type: 'string',
29
+ enum: ['list', 'graph', 'file_importers'],
30
+ description: 'Subaction. Default "list" returns the registered repos with file counts. "graph" returns every symbol exported by ≥ 2 repos. "file_importers" needs `file` (and optionally `repo`); returns the cross-repo importers list.',
31
+ },
32
+ file: {
33
+ type: 'string',
34
+ description: '"file_importers" only — repo-relative path inside the source repo (e.g. "src/auth.ts").',
35
+ },
36
+ repo: {
37
+ type: 'string',
38
+ description: '"file_importers" only — registered repo name (defaults to the cwd if registered).',
39
+ },
40
+ },
41
+ },
42
+ handler: async (args, rootPath) => {
43
+ const action = typeof args.action === 'string' ? args.action : 'list';
44
+ const workspace = await loadWorkspace(rootPath);
45
+ if (!workspace || workspace.repos.length === 0) {
46
+ return {
47
+ action,
48
+ available: false,
49
+ reason: 'No cross-repo workspace registered. Run `projscan workspace add <path>` to register sibling repos.',
50
+ repos: [],
51
+ };
52
+ }
53
+ switch (action) {
54
+ case 'list':
55
+ return await listView(workspace, rootPath);
56
+ case 'graph':
57
+ return await graphView(workspace);
58
+ case 'file_importers': {
59
+ const file = typeof args.file === 'string' ? args.file : '';
60
+ const repo = typeof args.repo === 'string' ? args.repo : undefined;
61
+ if (!file) {
62
+ throw new Error('file_importers requires a `file` argument (repo-relative path inside the source repo).');
63
+ }
64
+ return await fileImportersView(workspace, rootPath, file, repo);
65
+ }
66
+ default:
67
+ throw new Error(`Unknown action "${action}". Valid: list, graph, file_importers.`);
68
+ }
69
+ },
70
+ };
71
+ async function buildPerRepoGraph(repoPath) {
72
+ try {
73
+ const scan = await scanRepository(repoPath);
74
+ return await buildCodeGraph(repoPath, scan.files);
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ async function listView(workspace, rootPath) {
81
+ if (!workspace)
82
+ return { repos: [] };
83
+ const summaries = [];
84
+ for (const repo of workspace.repos) {
85
+ const graph = await buildPerRepoGraph(repo.path);
86
+ if (!graph) {
87
+ summaries.push({ name: repo.name, path: repo.path, parsedFiles: 0, exports: 0 });
88
+ continue;
89
+ }
90
+ let exports = 0;
91
+ for (const f of graph.files.values())
92
+ exports += f.exports.length;
93
+ summaries.push({
94
+ name: repo.name,
95
+ path: repo.path,
96
+ parsedFiles: graph.scannedFiles,
97
+ exports,
98
+ });
99
+ }
100
+ return {
101
+ action: 'list',
102
+ workspaceRoot: rootPath,
103
+ totalRepos: workspace.repos.length,
104
+ repos: summaries,
105
+ };
106
+ }
107
+ async function graphView(workspace) {
108
+ if (!workspace)
109
+ return { sharedSymbols: [] };
110
+ const symbolToRepos = new Map();
111
+ for (const repo of workspace.repos) {
112
+ const graph = await buildPerRepoGraph(repo.path);
113
+ if (!graph)
114
+ continue;
115
+ for (const f of graph.files.values()) {
116
+ for (const exp of f.exports) {
117
+ if (!exp.name)
118
+ continue;
119
+ let set = symbolToRepos.get(exp.name);
120
+ if (!set) {
121
+ set = new Set();
122
+ symbolToRepos.set(exp.name, set);
123
+ }
124
+ set.add(repo.name);
125
+ }
126
+ }
127
+ }
128
+ const shared = [...symbolToRepos.entries()]
129
+ .filter(([, repos]) => repos.size >= 2)
130
+ .map(([symbol, repos]) => ({ symbol, repos: [...repos].sort() }))
131
+ .sort((a, b) => b.repos.length - a.repos.length || a.symbol.localeCompare(b.symbol));
132
+ return {
133
+ action: 'graph',
134
+ totalRepos: workspace.repos.length,
135
+ sharedSymbolCount: shared.length,
136
+ sharedSymbols: shared.slice(0, 200),
137
+ truncated: shared.length > 200,
138
+ };
139
+ }
140
+ async function fileImportersView(workspace, rootPath, file, repoFilter) {
141
+ if (!workspace)
142
+ return { importers: [] };
143
+ // Treat the file as living in a specific repo. If `repo` is given,
144
+ // use that; otherwise assume the file is in the cwd-rooted repo.
145
+ let sourceRepoPath = rootPath;
146
+ if (repoFilter) {
147
+ const match = workspace.repos.find((r) => r.name === repoFilter);
148
+ if (!match) {
149
+ throw new Error(`repo "${repoFilter}" is not registered. Run \`projscan workspace list\` to see options.`);
150
+ }
151
+ sourceRepoPath = match.path;
152
+ }
153
+ const sourceAbs = path.resolve(sourceRepoPath, file);
154
+ // Heuristic: an importer is any registered repo that has at least
155
+ // one file whose `imports[].source` resolves to a path matching
156
+ // sourceAbs. Lightweight scan — full cross-repo resolution is an
157
+ // expensive future improvement.
158
+ const importers = [];
159
+ for (const repo of workspace.repos) {
160
+ if (repo.path === sourceRepoPath)
161
+ continue;
162
+ const graph = await buildPerRepoGraph(repo.path);
163
+ if (!graph)
164
+ continue;
165
+ for (const f of graph.files.values()) {
166
+ for (const imp of f.imports) {
167
+ if (typeof imp.source !== 'string')
168
+ continue;
169
+ const resolved = path.resolve(repo.path, path.dirname(f.relativePath), imp.source);
170
+ const looksLikeMatch = resolved === sourceAbs ||
171
+ resolved.startsWith(sourceAbs + '.') ||
172
+ imp.source.endsWith(file);
173
+ if (looksLikeMatch) {
174
+ importers.push({ repo: repo.name, file: f.relativePath, source: imp.source });
175
+ }
176
+ }
177
+ }
178
+ }
179
+ return {
180
+ action: 'file_importers',
181
+ file,
182
+ sourceRepo: repoFilter ?? '(cwd)',
183
+ importerCount: importers.length,
184
+ importers: importers.slice(0, 200),
185
+ truncated: importers.length > 200,
186
+ };
187
+ }
188
+ //# sourceMappingURL=workspaceGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaceGraph.js","sourceRoot":"","sources":["../../../src/mcp/tools/workspaceGraph.ts"],"names":[],"mappings":"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"}
package/dist/mcp/tools.js CHANGED
@@ -29,6 +29,10 @@ import { explainIssueTool } from './tools/explainIssue.js';
29
29
  import { impactTool } from './tools/impact.js';
30
30
  import { searchTool } from './tools/search.js';
31
31
  import { sessionTool } from './tools/session.js';
32
+ import { memoryTool } from './tools/memory.js';
33
+ import { workspaceGraphTool } from './tools/workspaceGraph.js';
34
+ import { applyFixTool } from './tools/applyFix.js';
35
+ import { taintTool } from './tools/taint.js';
32
36
  const tools = [
33
37
  analyzeTool,
34
38
  doctorTool,
@@ -51,6 +55,10 @@ const tools = [
51
55
  impactTool,
52
56
  searchTool,
53
57
  sessionTool,
58
+ memoryTool,
59
+ workspaceGraphTool,
60
+ applyFixTool,
61
+ taintTool,
54
62
  ];
55
63
  export function getToolDefinitions() {
56
64
  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;AAMjD,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;CACZ,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;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,6 +1,17 @@
1
1
  import type { AnalysisReport, AuditReport, CoverageJoinedReport, CouplingReport, Issue, Fix, FixResult, FileExplanation, FileInspection, ArchitectureLayer, DirectoryNode, DependencyReport, DiffResult, HotspotReport, OutdatedReport, PrDiffReport, ReviewReport, FixSuggestion, ImpactReport, IssueExplanation, UpgradePreview, WorkspaceInfo } from '../types.js';
2
2
  export declare function reportAnalysis(report: AnalysisReport): void;
3
- export declare function reportHealth(issues: Issue[], scanTimeMs?: number): void;
3
+ export interface ReportHealthOptions {
4
+ /** Scan duration in milliseconds; surfaced under the score line. */
5
+ scanTimeMs?: number;
6
+ /**
7
+ * 1.5+ — count of stable rules from Project Memory. When ≥ 1, doctor
8
+ * surfaces a one-line tip pointing at `projscan memory stable`.
9
+ * Caller (the doctor CLI) is responsible for loading memory and
10
+ * passing the count; reporters stay sync.
11
+ */
12
+ stableRuleCount?: number;
13
+ }
14
+ export declare function reportHealth(issues: Issue[], scanTimeMsOrOptions?: number | ReportHealthOptions): void;
4
15
  export declare function reportCi(issues: Issue[], threshold: number): void;
5
16
  export declare function reportDiff(diff: DiffResult): void;
6
17
  export declare function reportDetectedIssues(issues: Issue[], fixes: Fix[]): void;