@shrkcrft/mcp-server 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (189) hide show
  1. package/dist/index.d.ts +4 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +4 -0
  4. package/dist/server/columnar-format.d.ts +34 -0
  5. package/dist/server/columnar-format.d.ts.map +1 -0
  6. package/dist/server/columnar-format.js +95 -0
  7. package/dist/server/create-mcp-server.d.ts +3 -0
  8. package/dist/server/create-mcp-server.d.ts.map +1 -1
  9. package/dist/server/create-mcp-server.js +24 -9
  10. package/dist/server/fit-array-to-budget.d.ts +24 -0
  11. package/dist/server/fit-array-to-budget.d.ts.map +1 -0
  12. package/dist/server/fit-array-to-budget.js +60 -0
  13. package/dist/server/serialize-tool-data.d.ts +15 -0
  14. package/dist/server/serialize-tool-data.d.ts.map +1 -0
  15. package/dist/server/serialize-tool-data.js +22 -0
  16. package/dist/server/tool-definition.d.ts +15 -0
  17. package/dist/server/tool-definition.d.ts.map +1 -1
  18. package/dist/server/tool-input-validators.d.ts.map +1 -1
  19. package/dist/server/tool-input-validators.js +43 -0
  20. package/dist/tools/agent-brief.tool.d.ts.map +1 -1
  21. package/dist/tools/agent-brief.tool.js +20 -0
  22. package/dist/tools/align-cache.tool.d.ts +11 -0
  23. package/dist/tools/align-cache.tool.d.ts.map +1 -0
  24. package/dist/tools/align-cache.tool.js +76 -0
  25. package/dist/tools/all-tools.d.ts.map +1 -1
  26. package/dist/tools/all-tools.js +72 -7
  27. package/dist/tools/architecture-map.tool.d.ts.map +1 -1
  28. package/dist/tools/architecture-map.tool.js +4 -2
  29. package/dist/tools/code-find-usages.tool.d.ts +16 -0
  30. package/dist/tools/code-find-usages.tool.d.ts.map +1 -0
  31. package/dist/tools/code-find-usages.tool.js +180 -0
  32. package/dist/tools/command-catalog.tool.d.ts.map +1 -1
  33. package/dist/tools/command-catalog.tool.js +11 -7
  34. package/dist/tools/compress-context.tool.d.ts +8 -0
  35. package/dist/tools/compress-context.tool.d.ts.map +1 -0
  36. package/dist/tools/compress-context.tool.js +81 -0
  37. package/dist/tools/dashboard-summary.tool.d.ts.map +1 -1
  38. package/dist/tools/dashboard-summary.tool.js +2 -4
  39. package/dist/tools/delegate-task.tool.d.ts +3 -0
  40. package/dist/tools/delegate-task.tool.d.ts.map +1 -0
  41. package/dist/tools/delegate-task.tool.js +94 -0
  42. package/dist/tools/deps-audit.tool.d.ts +10 -0
  43. package/dist/tools/deps-audit.tool.d.ts.map +1 -0
  44. package/dist/tools/deps-audit.tool.js +251 -0
  45. package/dist/tools/diff-check.tool.d.ts +15 -0
  46. package/dist/tools/diff-check.tool.d.ts.map +1 -0
  47. package/dist/tools/diff-check.tool.js +157 -0
  48. package/dist/tools/file-advice.tool.d.ts +22 -0
  49. package/dist/tools/file-advice.tool.d.ts.map +1 -0
  50. package/dist/tools/file-advice.tool.js +88 -0
  51. package/dist/tools/get-api-surface-diff.tool.d.ts +3 -0
  52. package/dist/tools/get-api-surface-diff.tool.d.ts.map +1 -0
  53. package/dist/tools/get-api-surface-diff.tool.js +60 -0
  54. package/dist/tools/get-arch-violations.tool.d.ts +3 -0
  55. package/dist/tools/get-arch-violations.tool.d.ts.map +1 -0
  56. package/dist/tools/get-arch-violations.tool.js +30 -0
  57. package/dist/tools/get-code-intelligence-state.tool.d.ts +11 -0
  58. package/dist/tools/get-code-intelligence-state.tool.d.ts.map +1 -0
  59. package/dist/tools/get-code-intelligence-state.tool.js +60 -0
  60. package/dist/tools/get-context-pack.tool.d.ts +3 -0
  61. package/dist/tools/get-context-pack.tool.d.ts.map +1 -0
  62. package/dist/tools/get-context-pack.tool.js +40 -0
  63. package/dist/tools/get-framework-entities.tool.d.ts +3 -0
  64. package/dist/tools/get-framework-entities.tool.d.ts.map +1 -0
  65. package/dist/tools/get-framework-entities.tool.js +68 -0
  66. package/dist/tools/get-graph-callers.tool.d.ts +3 -0
  67. package/dist/tools/get-graph-callers.tool.d.ts.map +1 -0
  68. package/dist/tools/get-graph-callers.tool.js +94 -0
  69. package/dist/tools/get-graph-context.tool.d.ts +3 -0
  70. package/dist/tools/get-graph-context.tool.d.ts.map +1 -0
  71. package/dist/tools/get-graph-context.tool.js +125 -0
  72. package/dist/tools/get-graph-cycles.tool.d.ts +10 -0
  73. package/dist/tools/get-graph-cycles.tool.d.ts.map +1 -0
  74. package/dist/tools/get-graph-cycles.tool.js +58 -0
  75. package/dist/tools/get-graph-deps.tool.d.ts +12 -0
  76. package/dist/tools/get-graph-deps.tool.d.ts.map +1 -0
  77. package/dist/tools/get-graph-deps.tool.js +80 -0
  78. package/dist/tools/get-graph-hubs.tool.d.ts +3 -0
  79. package/dist/tools/get-graph-hubs.tool.d.ts.map +1 -0
  80. package/dist/tools/get-graph-hubs.tool.js +61 -0
  81. package/dist/tools/get-graph-impact-analysis.tool.d.ts +3 -0
  82. package/dist/tools/get-graph-impact-analysis.tool.d.ts.map +1 -0
  83. package/dist/tools/get-graph-impact-analysis.tool.js +44 -0
  84. package/dist/tools/get-graph-impact.tool.d.ts +3 -0
  85. package/dist/tools/get-graph-impact.tool.d.ts.map +1 -0
  86. package/dist/tools/get-graph-impact.tool.js +150 -0
  87. package/dist/tools/get-graph-path.tool.d.ts +3 -0
  88. package/dist/tools/get-graph-path.tool.d.ts.map +1 -0
  89. package/dist/tools/get-graph-path.tool.js +144 -0
  90. package/dist/tools/get-graph-search.tool.d.ts +3 -0
  91. package/dist/tools/get-graph-search.tool.d.ts.map +1 -0
  92. package/dist/tools/get-graph-search.tool.js +95 -0
  93. package/dist/tools/get-graph-status.tool.d.ts +11 -0
  94. package/dist/tools/get-graph-status.tool.d.ts.map +1 -0
  95. package/dist/tools/get-graph-status.tool.js +55 -0
  96. package/dist/tools/get-graph-unresolved.tool.d.ts +11 -0
  97. package/dist/tools/get-graph-unresolved.tool.d.ts.map +1 -0
  98. package/dist/tools/get-graph-unresolved.tool.js +85 -0
  99. package/dist/tools/get-impact-baseline.tool.d.ts +9 -0
  100. package/dist/tools/get-impact-baseline.tool.d.ts.map +1 -0
  101. package/dist/tools/get-impact-baseline.tool.js +65 -0
  102. package/dist/tools/get-intent-benchmark-run.tool.d.ts +12 -0
  103. package/dist/tools/get-intent-benchmark-run.tool.d.ts.map +1 -0
  104. package/dist/tools/get-intent-benchmark-run.tool.js +55 -0
  105. package/dist/tools/get-knowledge-graph.tool.d.ts +7 -0
  106. package/dist/tools/get-knowledge-graph.tool.d.ts.map +1 -1
  107. package/dist/tools/get-knowledge-graph.tool.js +62 -3
  108. package/dist/tools/get-migrations.tool.d.ts +3 -0
  109. package/dist/tools/get-migrations.tool.d.ts.map +1 -0
  110. package/dist/tools/get-migrations.tool.js +70 -0
  111. package/dist/tools/get-pattern-registry.tool.d.ts +8 -0
  112. package/dist/tools/get-pattern-registry.tool.d.ts.map +1 -0
  113. package/dist/tools/get-pattern-registry.tool.js +40 -0
  114. package/dist/tools/get-quality-gate.tool.d.ts +3 -0
  115. package/dist/tools/get-quality-gate.tool.d.ts.map +1 -0
  116. package/dist/tools/get-quality-gate.tool.js +27 -0
  117. package/dist/tools/get-relevant-context.tool.d.ts.map +1 -1
  118. package/dist/tools/get-relevant-context.tool.js +30 -6
  119. package/dist/tools/get-rules-for-file.tool.d.ts +3 -0
  120. package/dist/tools/get-rules-for-file.tool.d.ts.map +1 -0
  121. package/dist/tools/get-rules-for-file.tool.js +54 -0
  122. package/dist/tools/get-structural-rewrite-plan.tool.d.ts +3 -0
  123. package/dist/tools/get-structural-rewrite-plan.tool.d.ts.map +1 -0
  124. package/dist/tools/get-structural-rewrite-plan.tool.js +46 -0
  125. package/dist/tools/get-structural-search.tool.d.ts +3 -0
  126. package/dist/tools/get-structural-search.tool.d.ts.map +1 -0
  127. package/dist/tools/get-structural-search.tool.js +35 -0
  128. package/dist/tools/get-task-packet.tool.d.ts.map +1 -1
  129. package/dist/tools/get-task-packet.tool.js +26 -22
  130. package/dist/tools/graph-staleness.d.ts +34 -0
  131. package/dist/tools/graph-staleness.d.ts.map +1 -0
  132. package/dist/tools/graph-staleness.js +36 -0
  133. package/dist/tools/list-boundary-rules.tool.d.ts.map +1 -1
  134. package/dist/tools/list-boundary-rules.tool.js +20 -16
  135. package/dist/tools/list-knowledge.tool.d.ts.map +1 -1
  136. package/dist/tools/list-knowledge.tool.js +14 -13
  137. package/dist/tools/list-packs.tool.d.ts.map +1 -1
  138. package/dist/tools/list-packs.tool.js +19 -15
  139. package/dist/tools/list-path-conventions.tool.d.ts.map +1 -1
  140. package/dist/tools/list-path-conventions.tool.js +19 -15
  141. package/dist/tools/list-pipelines.tool.d.ts.map +1 -1
  142. package/dist/tools/list-pipelines.tool.js +18 -14
  143. package/dist/tools/list-presets.tool.d.ts.map +1 -1
  144. package/dist/tools/list-presets.tool.js +25 -21
  145. package/dist/tools/list-rules.tool.d.ts.map +1 -1
  146. package/dist/tools/list-rules.tool.js +18 -14
  147. package/dist/tools/list-templates.tool.d.ts.map +1 -1
  148. package/dist/tools/list-templates.tool.js +18 -14
  149. package/dist/tools/plan-quality-review.tool.d.ts +21 -0
  150. package/dist/tools/plan-quality-review.tool.d.ts.map +1 -0
  151. package/dist/tools/plan-quality-review.tool.js +294 -0
  152. package/dist/tools/primary-tools.d.ts +24 -0
  153. package/dist/tools/primary-tools.d.ts.map +1 -0
  154. package/dist/tools/primary-tools.js +86 -0
  155. package/dist/tools/r19-extras.tool.js +1 -1
  156. package/dist/tools/r32-profiles.tool.d.ts +0 -3
  157. package/dist/tools/r32-profiles.tool.d.ts.map +1 -1
  158. package/dist/tools/r32-profiles.tool.js +3 -54
  159. package/dist/tools/retrieve-original.tool.d.ts +9 -0
  160. package/dist/tools/retrieve-original.tool.d.ts.map +1 -0
  161. package/dist/tools/retrieve-original.tool.js +47 -0
  162. package/dist/tools/runtime-reports.tool.d.ts.map +1 -1
  163. package/dist/tools/runtime-reports.tool.js +1 -3
  164. package/dist/tools/safety-audit.tool.d.ts.map +1 -1
  165. package/dist/tools/safety-audit.tool.js +1 -4
  166. package/dist/tools/search-knowledge.tool.d.ts.map +1 -1
  167. package/dist/tools/search-knowledge.tool.js +17 -13
  168. package/dist/tools/search.tool.d.ts.map +1 -1
  169. package/dist/tools/search.tool.js +11 -8
  170. package/dist/tools/smart-context-bundle.tool.d.ts +17 -0
  171. package/dist/tools/smart-context-bundle.tool.d.ts.map +1 -0
  172. package/dist/tools/smart-context-bundle.tool.js +110 -0
  173. package/dist/tools/smart-context-feed.tool.d.ts +17 -0
  174. package/dist/tools/smart-context-feed.tool.d.ts.map +1 -0
  175. package/dist/tools/smart-context-feed.tool.js +138 -0
  176. package/dist/tools/start-here.tool.js +2 -2
  177. package/package.json +28 -16
  178. package/dist/tools/r22-extras.tool.d.ts +0 -4
  179. package/dist/tools/r22-extras.tool.d.ts.map +0 -1
  180. package/dist/tools/r22-extras.tool.js +0 -42
  181. package/dist/tools/r26-ingest.tool.d.ts +0 -10
  182. package/dist/tools/r26-ingest.tool.d.ts.map +0 -1
  183. package/dist/tools/r26-ingest.tool.js +0 -174
  184. package/dist/tools/r28-plugin-lifecycle.tool.d.ts +0 -4
  185. package/dist/tools/r28-plugin-lifecycle.tool.d.ts.map +0 -1
  186. package/dist/tools/r28-plugin-lifecycle.tool.js +0 -94
  187. package/dist/tools/r34-search-unified.tool.d.ts +0 -3
  188. package/dist/tools/r34-search-unified.tool.d.ts.map +0 -1
  189. package/dist/tools/r34-search-unified.tool.js +0 -38
@@ -0,0 +1,3 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ export declare const getGraphImpactAnalysisTool: IToolDefinition;
3
+ //# sourceMappingURL=get-graph-impact-analysis.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-graph-impact-analysis.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-impact-analysis.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAWpE,eAAO,MAAM,0BAA0B,EAAE,eAoCxC,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { analyzeGraphImpact } from '@shrkcrft/impact-engine';
2
+ import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
3
+ export const getGraphImpactAnalysisTool = {
4
+ name: 'get_graph_impact_analysis',
5
+ description: 'Rich graph-backed change analysis (schema sharkcraft.graph-impact-analysis/v3). Inputs: file list OR symbol OR git ref. Returns affected symbols/files/packages/rules/templates/tests/risk + recommended validation commands. Read-only.',
6
+ cliCommand: 'graph impact',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ files: { type: 'array', items: { type: 'string' } },
11
+ symbol: { type: 'string' },
12
+ gitref: { type: 'string' },
13
+ maxDepth: { type: 'number' },
14
+ limit: { type: 'number' },
15
+ ...FORMAT_INPUT_PROPERTY,
16
+ },
17
+ additionalProperties: false,
18
+ },
19
+ handler(input, ctx) {
20
+ const args = input;
21
+ let resolved;
22
+ if (args.files && args.files.length > 0)
23
+ resolved = { kind: 'files', files: args.files };
24
+ else if (args.symbol)
25
+ resolved = { kind: 'symbol', symbolId: args.symbol };
26
+ else if (args.gitref)
27
+ resolved = { kind: 'gitref', ref: args.gitref };
28
+ if (!resolved) {
29
+ return {
30
+ isError: true,
31
+ error: { code: 'invalid-input', message: 'One of `files`, `symbol`, or `gitref` is required.' },
32
+ };
33
+ }
34
+ const analysis = analyzeGraphImpact(resolved, {
35
+ projectRoot: ctx.inspection.projectRoot,
36
+ limit: clamp(args.limit ?? 200, 1, 2000),
37
+ maxDepth: clamp(args.maxDepth ?? 5, 1, 10),
38
+ });
39
+ return { data: formatObjectArrays(analysis, input) };
40
+ },
41
+ };
42
+ function clamp(n, lo, hi) {
43
+ return Math.max(lo, Math.min(hi, n));
44
+ }
@@ -0,0 +1,3 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ export declare const getGraphImpactTool: IToolDefinition;
3
+ //# sourceMappingURL=get-graph-impact.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-graph-impact.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-impact.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAYpE,eAAO,MAAM,kBAAkB,EAAE,eA4EhC,CAAC"}
@@ -0,0 +1,150 @@
1
+ import { GraphQueryApi, GraphStore, loadGraphApiCached } from '@shrkcrft/graph';
2
+ import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
3
+ import { dropDeleted, graphResultStaleness } from "./graph-staleness.js";
4
+ const NEXT = 'shrk graph index';
5
+ export const getGraphImpactTool = {
6
+ name: 'get_graph_impact',
7
+ description: 'Compute reverse closure (importers + transitive) for a file or symbol. Returns direct and transitive dependents capped by limit. Read-only.',
8
+ cliCommand: 'graph impact',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ target: { type: 'string' },
13
+ maxDepth: { type: 'number' },
14
+ limit: { type: 'number' },
15
+ ...FORMAT_INPUT_PROPERTY,
16
+ },
17
+ required: ['target'],
18
+ additionalProperties: false,
19
+ },
20
+ handler(input, ctx) {
21
+ const args = input;
22
+ const target = (args.target ?? '').trim();
23
+ if (!target) {
24
+ return {
25
+ isError: true,
26
+ error: { code: 'invalid-input', message: 'target is required' },
27
+ };
28
+ }
29
+ const maxDepth = clamp(args.maxDepth ?? 5, 1, 10);
30
+ const limit = clamp(args.limit ?? 200, 1, 2000);
31
+ const store = new GraphStore(ctx.inspection.projectRoot);
32
+ if (!store.exists()) {
33
+ return {
34
+ isError: true,
35
+ error: {
36
+ code: 'graph-missing',
37
+ message: `Code-intelligence index is missing. Run '${NEXT}'.`,
38
+ details: { nextCommand: NEXT },
39
+ },
40
+ };
41
+ }
42
+ const api = loadGraphApiCached(ctx.inspection.projectRoot) ?? GraphQueryApi.fromStore(ctx.inspection.projectRoot);
43
+ const anchor = resolveAnchor(api, target);
44
+ if (!anchor) {
45
+ return {
46
+ isError: true,
47
+ error: {
48
+ code: 'not-found',
49
+ message: `No graph node matched "${target}".`,
50
+ details: { target },
51
+ },
52
+ };
53
+ }
54
+ const closure = reverseClosure(api, anchor, maxDepth, limit);
55
+ const direct = closure.layer[1] ?? [];
56
+ const transitive = closure.all.filter((id) => id !== anchor.id && !direct.includes(id));
57
+ const directNodes = direct.map((id) => summarise(api.neighbours(id).node));
58
+ const transitiveNodes = transitive.slice(0, limit).map((id) => summarise(api.neighbours(id).node));
59
+ // Targeted staleness over the blast-radius files: drop dependents whose
60
+ // file was deleted (they can't break), flag those whose content changed —
61
+ // so a stale index never misroutes which tests/files the agent trusts.
62
+ const fresh = graphResultStaleness(api, ctx.inspection.projectRoot, [
63
+ anchor.path,
64
+ ...directNodes.map((n) => n.path),
65
+ ...transitiveNodes.map((n) => n.path),
66
+ ]);
67
+ const data = {
68
+ schema: 'sharkcraft.graph-impact/v1',
69
+ anchor: summarise(anchor),
70
+ maxDepth,
71
+ limit,
72
+ truncated: closure.truncated,
73
+ directDependents: dropDeleted(directNodes, fresh.deletedSet),
74
+ transitiveDependents: dropDeleted(transitiveNodes, fresh.deletedSet),
75
+ totalReached: closure.all.length - 1,
76
+ ...(fresh.field ?? {}),
77
+ };
78
+ return { data: formatObjectArrays(data, input) };
79
+ },
80
+ };
81
+ function resolveAnchor(api, target) {
82
+ const direct = api.neighbours(target);
83
+ if (direct)
84
+ return direct.node;
85
+ if (target.startsWith('file:') || target.startsWith('symbol:') || target.startsWith('package:')) {
86
+ return undefined;
87
+ }
88
+ const f = api.findFile(target);
89
+ if (f)
90
+ return f;
91
+ const syms = api.findSymbol(target, { exact: true, limit: 1 });
92
+ if (syms.length > 0)
93
+ return syms[0];
94
+ return undefined;
95
+ }
96
+ function reverseClosure(api, anchor, maxDepth, limit) {
97
+ const seen = new Set([anchor.id]);
98
+ const layer = {};
99
+ let truncated = false;
100
+ // Layer 1 uses the anchor-kind-aware direct dependents (importersOf alone
101
+ // returns NOTHING for a symbol anchor — symbols have no import edges).
102
+ let frontier = directDependents(api, anchor).filter((id) => !seen.has(id));
103
+ if (frontier.length > limit) {
104
+ frontier = frontier.slice(0, limit);
105
+ truncated = true;
106
+ }
107
+ for (const id of frontier)
108
+ seen.add(id);
109
+ if (frontier.length > 0)
110
+ layer[1] = frontier;
111
+ let depth = 2;
112
+ while (depth <= maxDepth && frontier.length > 0 && !truncated) {
113
+ const next = [];
114
+ for (const id of frontier) {
115
+ for (const imp of api.importersOf(id)) {
116
+ if (seen.has(imp.id))
117
+ continue;
118
+ seen.add(imp.id);
119
+ next.push(imp.id);
120
+ if (seen.size - 1 >= limit) {
121
+ truncated = true;
122
+ break;
123
+ }
124
+ }
125
+ if (truncated)
126
+ break;
127
+ }
128
+ if (next.length > 0)
129
+ layer[depth] = next;
130
+ frontier = next;
131
+ depth += 1;
132
+ }
133
+ return { all: [...seen], layer, truncated };
134
+ }
135
+ /** Kind-aware direct dependents — the shared `GraphQueryApi` implementation. */
136
+ function directDependents(api, anchor) {
137
+ return api.directDependentsOf(anchor).map((n) => n.id);
138
+ }
139
+ function summarise(n) {
140
+ return {
141
+ id: n.id,
142
+ kind: n.kind,
143
+ label: n.label,
144
+ ...(n.path ? { path: n.path } : {}),
145
+ ...(n.line ? { line: n.line } : {}),
146
+ };
147
+ }
148
+ function clamp(n, lo, hi) {
149
+ return Math.max(lo, Math.min(hi, n));
150
+ }
@@ -0,0 +1,3 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ export declare const getGraphPathTool: IToolDefinition;
3
+ //# sourceMappingURL=get-graph-path.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-graph-path.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-path.tool.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAYpE,eAAO,MAAM,gBAAgB,EAAE,eAgG9B,CAAC"}
@@ -0,0 +1,144 @@
1
+ import { EdgeKind, GraphQueryApi, GraphStore, loadGraphApiCached, NodeKind, } from '@shrkcrft/graph';
2
+ import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
3
+ import { callGraphLanguageNote, graphResultStaleness } from "./graph-staleness.js";
4
+ const NEXT = 'shrk graph index';
5
+ export const getGraphPathTool = {
6
+ name: 'get_graph_path',
7
+ description: 'Is code A actually wired to code B? Returns the shortest directed CODE path (import/call/reference/declare/re-export/extends/implements edges) from `from` to `to`, hop by hop with edge kind and call-site line — the deterministic answer to "is X wired to Y" that grep cannot give. If A does not reach B it also checks B→A and reports the direction. Each endpoint is a file path or a symbol name. Read-only; needs `shrk graph index`.',
8
+ cliCommand: 'graph path',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ from: { type: 'string' },
13
+ to: { type: 'string' },
14
+ maxDepth: { type: 'number' },
15
+ ...FORMAT_INPUT_PROPERTY,
16
+ },
17
+ required: ['from', 'to'],
18
+ additionalProperties: false,
19
+ },
20
+ handler(input, ctx) {
21
+ const args = input;
22
+ const fromArg = (args.from ?? '').trim();
23
+ const toArg = (args.to ?? '').trim();
24
+ if (!fromArg || !toArg) {
25
+ return { isError: true, error: { code: 'invalid-input', message: 'from and to are required' } };
26
+ }
27
+ const maxDepth = clampDepth(args.maxDepth);
28
+ const cwd = ctx.inspection.projectRoot;
29
+ const store = new GraphStore(cwd);
30
+ if (!store.exists()) {
31
+ return {
32
+ isError: true,
33
+ error: {
34
+ code: 'graph-missing',
35
+ message: `Code-intelligence index is missing. Run '${NEXT}'.`,
36
+ details: { nextCommand: NEXT },
37
+ },
38
+ };
39
+ }
40
+ const api = loadGraphApiCached(cwd) ?? GraphQueryApi.fromStore(cwd);
41
+ const from = resolveAnchor(api, fromArg);
42
+ const to = resolveAnchor(api, toArg);
43
+ if (!from || !to) {
44
+ const missing = !from ? fromArg : toArg;
45
+ return {
46
+ isError: true,
47
+ error: { code: 'not-found', message: `No graph node matched "${missing}".`, details: { target: missing } },
48
+ };
49
+ }
50
+ // A symbol has no OUTGOING code edges (references are recorded file→symbol),
51
+ // so trace from its declaring file. The target may stay a symbol.
52
+ const fromStart = bfsStartNode(api, from);
53
+ const toStart = bfsStartNode(api, to);
54
+ const forward = api.pathBetween(fromStart.id, to.id, { maxDepth });
55
+ const reverse = forward.found ? null : api.pathBetween(toStart.id, from.id, { maxDepth });
56
+ const direction = forward.found
57
+ ? 'forward'
58
+ : reverse?.found
59
+ ? 'reverse'
60
+ : 'none';
61
+ const chosen = forward.found ? forward : reverse?.found ? reverse : forward;
62
+ const startEndpoint = direction === 'reverse' ? to : from;
63
+ const startFile = direction === 'reverse' ? toStart : fromStart;
64
+ const startNote = direction !== 'none' && startFile.id !== startEndpoint.id && startEndpoint.kind === NodeKind.Symbol
65
+ ? `\`${startEndpoint.label}\` is declared in ${startFile.path ?? startFile.id}; path traced from that file (per-symbol out-edges are not tracked).`
66
+ : undefined;
67
+ const hops = chosen.hops.map((h) => ({
68
+ from: h.from.path ?? h.from.id,
69
+ to: h.to.path ?? h.to.id,
70
+ kind: h.kind,
71
+ label: h.to.label,
72
+ ...(h.line ? { line: h.line } : {}),
73
+ }));
74
+ const fresh = graphResultStaleness(api, cwd, [
75
+ from.path,
76
+ to.path,
77
+ ...chosen.hops.map((h) => h.from.path),
78
+ ...chosen.hops.map((h) => h.to.path),
79
+ ]);
80
+ // A no-path answer between non-TS endpoints may just be missing call edges.
81
+ const langNote = direction === 'none' ? callGraphLanguageNote(api, from) ?? callGraphLanguageNote(api, to) : undefined;
82
+ const data = {
83
+ schema: 'sharkcraft.graph-path/v1',
84
+ from: summarise(from),
85
+ to: summarise(to),
86
+ found: direction !== 'none',
87
+ direction,
88
+ ...(direction !== 'none' && startFile.id !== startEndpoint.id ? { tracedFrom: summarise(startFile) } : {}),
89
+ hops,
90
+ hopCount: hops.length,
91
+ explored: forward.found ? forward.explored : reverse?.explored ?? forward.explored,
92
+ ...(direction === 'none' && chosen.reason ? { reason: chosen.reason } : {}),
93
+ ...(startNote ?? langNote ? { note: startNote ?? langNote } : {}),
94
+ ...(fresh.field ?? {}),
95
+ };
96
+ return { data: formatObjectArrays(data, input) };
97
+ },
98
+ };
99
+ function clampDepth(raw) {
100
+ if (typeof raw !== 'number' || !Number.isFinite(raw))
101
+ return 16;
102
+ return Math.max(1, Math.min(32, Math.floor(raw)));
103
+ }
104
+ function resolveAnchor(api, target) {
105
+ const direct = api.neighbours(target);
106
+ if (direct)
107
+ return direct.node;
108
+ if (target.startsWith('file:') || target.startsWith('symbol:') || target.startsWith('package:')) {
109
+ return undefined;
110
+ }
111
+ const f = api.findFile(target);
112
+ if (f)
113
+ return f;
114
+ const syms = api.findSymbol(target, { exact: true, limit: 1 });
115
+ if (syms.length > 0)
116
+ return syms[0];
117
+ return undefined;
118
+ }
119
+ /** A file is its own BFS start; a symbol resolves to its declaring file. */
120
+ function bfsStartNode(api, node) {
121
+ if (node.kind !== NodeKind.Symbol)
122
+ return node;
123
+ const neighbours = api.neighbours(node.id);
124
+ if (neighbours) {
125
+ for (const incoming of neighbours.in) {
126
+ if (incoming.edge.kind !== EdgeKind.DeclaresSymbol)
127
+ continue;
128
+ if ('resolved' in incoming.source)
129
+ continue;
130
+ if (incoming.source.kind === NodeKind.File)
131
+ return incoming.source;
132
+ }
133
+ }
134
+ return (node.path ? api.findFile(node.path) : undefined) ?? node;
135
+ }
136
+ function summarise(n) {
137
+ return {
138
+ id: n.id,
139
+ kind: n.kind,
140
+ label: n.label,
141
+ ...(n.path ? { path: n.path } : {}),
142
+ ...(n.line ? { line: n.line } : {}),
143
+ };
144
+ }
@@ -0,0 +1,3 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ export declare const getGraphSearchTool: IToolDefinition;
3
+ //# sourceMappingURL=get-graph-search.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-graph-search.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-search.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAYpE,eAAO,MAAM,kBAAkB,EAAE,eA8EhC,CAAC"}
@@ -0,0 +1,95 @@
1
+ import { GraphQueryApi, GraphStore, loadGraphApiCached } from '@shrkcrft/graph';
2
+ import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
3
+ const NEXT = 'shrk graph index';
4
+ export const getGraphSearchTool = {
5
+ name: 'get_graph_search',
6
+ description: 'Search the code graph by file path, symbol name, or package name. Returns ranked node summaries. Read-only.',
7
+ cliCommand: 'graph search',
8
+ inputSchema: {
9
+ type: 'object',
10
+ properties: {
11
+ query: { type: 'string' },
12
+ kind: { type: 'string', enum: ['file', 'symbol', 'package'] },
13
+ limit: { type: 'number' },
14
+ exact: { type: 'boolean' },
15
+ ...FORMAT_INPUT_PROPERTY,
16
+ },
17
+ required: ['query'],
18
+ additionalProperties: false,
19
+ },
20
+ handler(input, ctx) {
21
+ const args = input;
22
+ const query = (args.query ?? '').trim();
23
+ if (!query) {
24
+ return {
25
+ isError: true,
26
+ error: { code: 'invalid-input', message: 'query is required' },
27
+ };
28
+ }
29
+ const limit = Math.max(1, Math.min(200, args.limit ?? 20));
30
+ const store = new GraphStore(ctx.inspection.projectRoot);
31
+ if (!store.exists()) {
32
+ return {
33
+ isError: true,
34
+ error: {
35
+ code: 'graph-missing',
36
+ message: `Code-intelligence index is missing. Run '${NEXT}'.`,
37
+ details: { nextCommand: NEXT },
38
+ },
39
+ };
40
+ }
41
+ const api = loadGraphApiCached(ctx.inspection.projectRoot) ?? GraphQueryApi.fromStore(ctx.inspection.projectRoot);
42
+ const exact = args.exact ?? false;
43
+ const matches = [];
44
+ if (!args.kind || args.kind === 'file') {
45
+ const f = api.findFile(query);
46
+ if (f)
47
+ matches.push(f);
48
+ // Fuzzy fallback (mirrors the CLI): substring match on path/basename so a
49
+ // bare name like `Foo` finds `packages/x/Foo.ts` without the full path —
50
+ // otherwise the MCP returned an empty list where the CLI found the file.
51
+ if (!exact && matches.length < limit) {
52
+ const q = query.toLowerCase();
53
+ const seen = new Set(matches.map((n) => n.id));
54
+ for (const node of api.allFiles()) {
55
+ if (seen.has(node.id))
56
+ continue;
57
+ const p = node.path?.toLowerCase() ?? '';
58
+ const base = p.includes('/') ? p.slice(p.lastIndexOf('/') + 1) : p;
59
+ if (base.includes(q) || p.includes(q)) {
60
+ matches.push(node);
61
+ seen.add(node.id);
62
+ if (matches.length >= limit)
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ if (!args.kind || args.kind === 'symbol') {
69
+ for (const s of api.findSymbol(query, { exact, limit }))
70
+ matches.push(s);
71
+ }
72
+ if (!args.kind || args.kind === 'package') {
73
+ const p = api.neighbours(`package:${query}`);
74
+ if (p)
75
+ matches.push(p.node);
76
+ }
77
+ const data = {
78
+ schema: 'sharkcraft.graph-search/v1',
79
+ query,
80
+ kind: args.kind ?? 'any',
81
+ total: Math.min(matches.length, limit),
82
+ matches: matches.slice(0, limit).map(summarise),
83
+ };
84
+ return { data: formatObjectArrays(data, input) };
85
+ },
86
+ };
87
+ function summarise(n) {
88
+ return {
89
+ id: n.id,
90
+ kind: n.kind,
91
+ label: n.label,
92
+ ...(n.path ? { path: n.path } : {}),
93
+ ...(n.line ? { line: n.line } : {}),
94
+ };
95
+ }
@@ -0,0 +1,11 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ /**
3
+ * Read-only status for the on-disk code graph. Returns
4
+ * { state: 'fresh' | 'stale' | 'corrupt' | 'missing' } and counters.
5
+ * `corrupt` (store self-integrity) and `stale` (files changed on disk since
6
+ * indexing) are orthogonal — a store can be digest-valid yet stale — so
7
+ * precedence is corrupt > stale > fresh. On 'missing', `isError` +
8
+ * `nextCommand` direct the caller to refresh.
9
+ */
10
+ export declare const getGraphStatusTool: IToolDefinition;
11
+ //# sourceMappingURL=get-graph-status.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-graph-status.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-status.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAKpE;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,eA4ChC,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { detectGraphFreshness, GraphStore } from '@shrkcrft/graph';
2
+ const NEXT = 'shrk graph index';
3
+ const STALE_HINT = `Code-intelligence index is missing or stale. Run '${NEXT}' to build it.`;
4
+ /**
5
+ * Read-only status for the on-disk code graph. Returns
6
+ * { state: 'fresh' | 'stale' | 'corrupt' | 'missing' } and counters.
7
+ * `corrupt` (store self-integrity) and `stale` (files changed on disk since
8
+ * indexing) are orthogonal — a store can be digest-valid yet stale — so
9
+ * precedence is corrupt > stale > fresh. On 'missing', `isError` +
10
+ * `nextCommand` direct the caller to refresh.
11
+ */
12
+ export const getGraphStatusTool = {
13
+ name: 'get_graph_status',
14
+ description: 'Read-only status of the SharkCraft code-intelligence graph: state (fresh/stale/corrupt/missing), file/node/edge counts, and how many files changed/added/deleted since indexing. Returns nextCommand when stale or missing so the agent knows to refresh before trusting graph answers.',
15
+ cliCommand: 'graph status',
16
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
17
+ handler(_input, ctx) {
18
+ const store = new GraphStore(ctx.inspection.projectRoot);
19
+ if (!store.exists()) {
20
+ return {
21
+ isError: true,
22
+ error: {
23
+ code: 'graph-missing',
24
+ message: STALE_HINT,
25
+ details: { nextCommand: NEXT, state: 'missing' },
26
+ },
27
+ };
28
+ }
29
+ const verify = store.verifyDigest();
30
+ const snap = store.loadSnapshot();
31
+ const fresh = detectGraphFreshness(ctx.inspection.projectRoot);
32
+ const behind = fresh.modified.length + fresh.added.length + fresh.deleted.length;
33
+ const state = !verify.ok ? 'corrupt' : behind > 0 ? 'stale' : 'fresh';
34
+ return {
35
+ data: {
36
+ schema: snap.manifest.schema,
37
+ state,
38
+ digestOk: verify.ok,
39
+ fileCount: snap.manifest.filesIndexed,
40
+ nodeCount: snap.nodes.size,
41
+ edgeCount: snap.edges.size,
42
+ nodesByKind: snap.manifest.nodesByKind,
43
+ edgesByKind: snap.manifest.edgesByKind,
44
+ workspacePackages: snap.manifest.workspacePackages,
45
+ lastIndexedAt: snap.manifest.lastIndexedAt,
46
+ lastIndexDurationMs: snap.manifest.lastIndexDurationMs,
47
+ modifiedSinceIndex: fresh.modified.length,
48
+ newSinceIndex: fresh.added.length,
49
+ deletedSinceIndex: fresh.deleted.length,
50
+ ...(behind > 0 ? { nextCommand: 'shrk graph index --changed' } : {}),
51
+ ...(verify.ok ? {} : { expectedDigest: verify.expected, actualDigest: verify.actual }),
52
+ },
53
+ };
54
+ },
55
+ };
@@ -0,0 +1,11 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ /**
3
+ * Read-only MCP mirror of `shrk graph unresolved`. Returns every
4
+ * `unresolved:<spec>` ImportsFile edge grouped by source file.
5
+ * Sorted by unresolved-count desc so the worst offenders surface first.
6
+ *
7
+ * Same safety contract as the other graph tools: structured
8
+ * `graph-missing` error when the index isn't built yet.
9
+ */
10
+ export declare const getGraphUnresolvedTool: IToolDefinition;
11
+ //# sourceMappingURL=get-graph-unresolved.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-graph-unresolved.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-unresolved.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAUpE;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAyEpC,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { EdgeKind, GraphStore } from '@shrkcrft/graph';
2
+ import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
3
+ const NEXT = 'shrk graph index';
4
+ /**
5
+ * Read-only MCP mirror of `shrk graph unresolved`. Returns every
6
+ * `unresolved:<spec>` ImportsFile edge grouped by source file.
7
+ * Sorted by unresolved-count desc so the worst offenders surface first.
8
+ *
9
+ * Same safety contract as the other graph tools: structured
10
+ * `graph-missing` error when the index isn't built yet.
11
+ */
12
+ export const getGraphUnresolvedTool = {
13
+ name: 'get_graph_unresolved',
14
+ description: 'List every unresolved import in the code graph, grouped by source file. Sorted by unresolved-count desc. Pass `format:"table"` for a token-efficient columnar encoding of the file list. Read-only.',
15
+ cliCommand: 'graph unresolved',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ limit: { type: 'number' },
20
+ ...FORMAT_INPUT_PROPERTY,
21
+ },
22
+ additionalProperties: false,
23
+ },
24
+ handler(input, ctx) {
25
+ const args = input;
26
+ const limit = typeof args.limit === 'number' && Number.isFinite(args.limit) && args.limit > 0
27
+ ? args.limit
28
+ : 200;
29
+ const store = new GraphStore(ctx.inspection.projectRoot);
30
+ if (!store.exists()) {
31
+ return {
32
+ isError: true,
33
+ error: {
34
+ code: 'graph-missing',
35
+ message: `Code-intelligence index is missing. Run '${NEXT}'.`,
36
+ details: { nextCommand: NEXT },
37
+ },
38
+ };
39
+ }
40
+ const snap = store.loadSnapshot();
41
+ const groups = new Map();
42
+ for (const e of snap.edges.values()) {
43
+ if (e.kind !== EdgeKind.ImportsFile)
44
+ continue;
45
+ if (!e.to.startsWith('unresolved:'))
46
+ continue;
47
+ const fromNode = snap.nodes.get(e.from);
48
+ const spec = e.to.slice('unresolved:'.length);
49
+ const existing = groups.get(e.from);
50
+ if (existing) {
51
+ existing.specifiers.add(spec);
52
+ }
53
+ else {
54
+ groups.set(e.from, {
55
+ from: e.from,
56
+ ...(fromNode?.path ? { path: fromNode.path } : {}),
57
+ specifiers: new Set([spec]),
58
+ });
59
+ }
60
+ }
61
+ const list = [...groups.values()]
62
+ .map((g) => ({
63
+ path: g.path ?? g.from.replace(/^file:/, ''),
64
+ unresolved: [...g.specifiers].sort(),
65
+ }))
66
+ .sort((a, b) => {
67
+ if (b.unresolved.length !== a.unresolved.length) {
68
+ return b.unresolved.length - a.unresolved.length;
69
+ }
70
+ return a.path.localeCompare(b.path);
71
+ });
72
+ const totalEdges = list.reduce((n, g) => n + g.unresolved.length, 0);
73
+ const data = {
74
+ schema: 'sharkcraft.graph-unresolved/v1',
75
+ totalEdges,
76
+ totalFiles: list.length,
77
+ truncated: list.length > limit,
78
+ files: list.slice(0, limit),
79
+ };
80
+ // `format:"table"` columnar-encodes the top-level `files` array (one row per
81
+ // source file); scalars (schema/totalEdges/…) and each row's inner
82
+ // `unresolved` string array are left untouched by the helper.
83
+ return { data: formatObjectArrays(data, input) };
84
+ },
85
+ };
@@ -0,0 +1,9 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ /**
3
+ * Read-only MCP mirror of `shrk impact baseline show`. Returns the
4
+ * frozen baseline + the most recent run + the delta between them.
5
+ * When either side is missing the response carries a structured hint
6
+ * pointing at the command that fills the gap.
7
+ */
8
+ export declare const getImpactBaselineTool: IToolDefinition;
9
+ //# sourceMappingURL=get-impact-baseline.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-impact-baseline.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-impact-baseline.tool.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,eA0DnC,CAAC"}