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

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 @@
1
+ {"version":3,"file":"deps-audit.tool.d.ts","sourceRoot":"","sources":["../../src/tools/deps-audit.tool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAIpE;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,eAkE3B,CAAC"}
@@ -0,0 +1,251 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ import { GraphQueryApi, GraphStore, NodeKind, loadGraphApiCached } from '@shrkcrft/graph';
4
+ import { FORMAT_INPUT_PROPERTY, formatObjectArrays, COLUMNAR_LEGEND } from "../server/columnar-format.js";
5
+ import { fitArrayToBudget } from "../server/fit-array-to-budget.js";
6
+ /**
7
+ * `deps_audit` — MCP wrapper around `shrk deps-audit`. Pure read-only.
8
+ * Returns the same shape the CLI's `--json` mode emits.
9
+ *
10
+ * NOTE: we re-implement the body here rather than shell out so MCP
11
+ * tools never spawn subprocesses. Identical logic, identical output.
12
+ */
13
+ export const depsAuditTool = {
14
+ name: 'deps_audit',
15
+ description: 'Per-package audit of declared dependencies (package.json) vs actually-imported specifiers (graph). Reports missing + unused deps. Pass `format:"table"` for a token-efficient columnar encoding of the per-package report list. Read-only.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ package: { type: 'string' },
20
+ ...FORMAT_INPUT_PROPERTY,
21
+ maxTokens: {
22
+ type: 'integer',
23
+ minimum: 1,
24
+ description: 'Token budget for the per-package report list. When set and the columnar form still exceeds it, falls back to the lossy SmartCrusher row-sampler (full original cached — retrieve via the returned ccrKey).',
25
+ },
26
+ },
27
+ additionalProperties: false,
28
+ },
29
+ async handler(input, ctx) {
30
+ const onlyPackage = typeof input['package'] === 'string' ? input['package'] : null;
31
+ const store = new GraphStore(ctx.cwd);
32
+ if (!store.exists()) {
33
+ return {
34
+ data: {
35
+ error: 'no-graph',
36
+ message: 'The SharkCraft graph index is required for deps-audit.',
37
+ nextCommand: 'shrk graph index',
38
+ },
39
+ };
40
+ }
41
+ const api = loadGraphApiCached(ctx.cwd) ?? GraphQueryApi.fromStore(ctx.cwd);
42
+ const packages = listWorkspacePackages(ctx.cwd, onlyPackage);
43
+ const reports = packages.map((p) => buildPackageReport(api, ctx.cwd, p));
44
+ const totals = reports.reduce((acc, r) => {
45
+ acc.missing += r.missingDeps.length;
46
+ acc.unused += r.unusedDeps.length;
47
+ return acc;
48
+ }, { missing: 0, unused: 0 });
49
+ // P5.2: an explicit token budget routes the per-package report list through
50
+ // the SmartCrusher row-sampler (lossy, CCR-recoverable) when even the
51
+ // columnar form is over budget.
52
+ const maxTokens = typeof input.maxTokens === 'number' && input.maxTokens > 0 ? Math.floor(input.maxTokens) : undefined;
53
+ if (maxTokens) {
54
+ const fitted = fitArrayToBudget(reports, maxTokens, ctx.ccrStore);
55
+ return {
56
+ data: {
57
+ _format: 'table',
58
+ _legend: COLUMNAR_LEGEND,
59
+ totals,
60
+ packages: fitted.value,
61
+ ...(fitted.ccrKey
62
+ ? { ccrKey: fitted.ccrKey, retrieveWith: `retrieve_original { "key": "${fitted.ccrKey}" }` }
63
+ : {}),
64
+ },
65
+ };
66
+ }
67
+ // `format:"table"` columnar-encodes the homogeneous `packages` report
68
+ // list; the `totals` scalar object is left untouched. The per-package
69
+ // string arrays (importedSpecifiers/missingDeps/unusedDeps) ride along
70
+ // inside each compacted row and reconstruct losslessly.
71
+ return { data: formatObjectArrays({ totals, packages: reports }, input) };
72
+ },
73
+ };
74
+ function listWorkspacePackages(cwd, onlyName) {
75
+ const roots = ['packages', 'libs', 'apps'].map((r) => nodePath.join(cwd, r)).filter((d) => existsSync(d));
76
+ const out = [];
77
+ for (const root of roots) {
78
+ let entries;
79
+ try {
80
+ entries = readdirSync(root);
81
+ }
82
+ catch {
83
+ continue;
84
+ }
85
+ for (const entry of entries) {
86
+ const dir = nodePath.join(root, entry);
87
+ let stat;
88
+ try {
89
+ stat = statSync(dir);
90
+ }
91
+ catch {
92
+ continue;
93
+ }
94
+ if (!stat.isDirectory())
95
+ continue;
96
+ const pkgJsonPath = nodePath.join(dir, 'package.json');
97
+ if (!existsSync(pkgJsonPath))
98
+ continue;
99
+ let pkgJson;
100
+ try {
101
+ pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
102
+ }
103
+ catch {
104
+ continue;
105
+ }
106
+ if (!pkgJson.name)
107
+ continue;
108
+ if (onlyName !== null && pkgJson.name !== onlyName)
109
+ continue;
110
+ out.push({ name: pkgJson.name, dir, pkgJsonPath });
111
+ }
112
+ }
113
+ return out;
114
+ }
115
+ function buildPackageReport(api, cwd, pkg) {
116
+ const declared = readDeclaredDeps(pkg.pkgJsonPath);
117
+ const importedSpecifiers = collectImportedSpecifiersForPackage(api, cwd, pkg.dir);
118
+ const importerCounts = new Map();
119
+ for (const s of importedSpecifiers)
120
+ importerCounts.set(s, (importerCounts.get(s) ?? 0) + 1);
121
+ const distinctImported = new Set(importedSpecifiers);
122
+ const declaredAll = new Map();
123
+ for (const [section, map] of [
124
+ ['dependencies', declared.dependencies],
125
+ ['devDependencies', declared.devDependencies],
126
+ ['peerDependencies', declared.peerDependencies],
127
+ ['optionalDependencies', declared.optionalDependencies],
128
+ ]) {
129
+ for (const k of Object.keys(map))
130
+ declaredAll.set(k, section);
131
+ }
132
+ const missingDeps = [];
133
+ for (const spec of distinctImported) {
134
+ if (declaredAll.has(spec))
135
+ continue;
136
+ if (spec === pkg.name)
137
+ continue;
138
+ missingDeps.push({ specifier: spec, importedFromCount: importerCounts.get(spec) ?? 0 });
139
+ }
140
+ missingDeps.sort((a, b) => b.importedFromCount - a.importedFromCount);
141
+ const unusedDeps = [];
142
+ for (const [spec, section] of declaredAll.entries()) {
143
+ if (distinctImported.has(spec))
144
+ continue;
145
+ unusedDeps.push({ specifier: spec, section });
146
+ }
147
+ unusedDeps.sort((a, b) => a.specifier.localeCompare(b.specifier));
148
+ return {
149
+ packageName: pkg.name,
150
+ packageDir: nodePath.relative(cwd, pkg.dir) || '.',
151
+ importedSpecifiers: [...distinctImported],
152
+ missingDeps,
153
+ unusedDeps,
154
+ };
155
+ }
156
+ function readDeclaredDeps(pkgJsonPath) {
157
+ try {
158
+ const body = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
159
+ return {
160
+ dependencies: asStringMap(body['dependencies']),
161
+ devDependencies: asStringMap(body['devDependencies']),
162
+ peerDependencies: asStringMap(body['peerDependencies']),
163
+ optionalDependencies: asStringMap(body['optionalDependencies']),
164
+ };
165
+ }
166
+ catch {
167
+ return { dependencies: {}, devDependencies: {}, peerDependencies: {}, optionalDependencies: {} };
168
+ }
169
+ }
170
+ function asStringMap(value) {
171
+ if (!value || typeof value !== 'object' || Array.isArray(value))
172
+ return {};
173
+ const out = {};
174
+ for (const [k, v] of Object.entries(value)) {
175
+ if (typeof v === 'string')
176
+ out[k] = v;
177
+ }
178
+ return out;
179
+ }
180
+ function collectImportedSpecifiersForPackage(api, cwd, packageDir) {
181
+ const out = [];
182
+ const relDir = nodePath.relative(cwd, packageDir).replace(/\\/g, '/');
183
+ for (const file of api.allFiles()) {
184
+ if (file.kind !== NodeKind.File)
185
+ continue;
186
+ const p = file.path ?? '';
187
+ if (!p.startsWith(relDir + '/src/') && !p.startsWith(relDir + '/'))
188
+ continue;
189
+ const abs = nodePath.isAbsolute(p) ? p : nodePath.join(cwd, p);
190
+ if (!existsSync(abs))
191
+ continue;
192
+ let body;
193
+ try {
194
+ body = readFileSync(abs, 'utf8');
195
+ }
196
+ catch {
197
+ continue;
198
+ }
199
+ for (const spec of extractRootSpecifiers(body)) {
200
+ if (isBuiltinModule(spec))
201
+ continue;
202
+ if (spec.startsWith('.') || spec.startsWith('/'))
203
+ continue;
204
+ out.push(rootOfSpecifier(spec));
205
+ }
206
+ }
207
+ return out;
208
+ }
209
+ const IMPORT_FROM_RE = /(?:^|\n)\s*(?:import|export)\s+[^;]*?\s+from\s+['"]([^'"]+)['"]/g;
210
+ const REQUIRE_RE = /\brequire\(\s*['"]([^'"]+)['"]\s*\)/g;
211
+ // `await import('pkg')` / `import('pkg')` — bare dynamic imports.
212
+ // Note: we intentionally match `\bimport\s*\(` not just `import(` so we
213
+ // don't false-trigger on `LocaleImport(...)` etc.
214
+ const DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
215
+ function extractRootSpecifiers(body) {
216
+ const out = [];
217
+ for (const m of body.matchAll(IMPORT_FROM_RE)) {
218
+ if (m[1])
219
+ out.push(m[1]);
220
+ }
221
+ for (const m of body.matchAll(REQUIRE_RE)) {
222
+ if (m[1])
223
+ out.push(m[1]);
224
+ }
225
+ for (const m of body.matchAll(DYNAMIC_IMPORT_RE)) {
226
+ if (m[1])
227
+ out.push(m[1]);
228
+ }
229
+ return out;
230
+ }
231
+ function rootOfSpecifier(spec) {
232
+ if (spec.startsWith('@')) {
233
+ const parts = spec.split('/');
234
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : spec;
235
+ }
236
+ return spec.split('/')[0];
237
+ }
238
+ function isBuiltinModule(spec) {
239
+ if (spec.startsWith('node:'))
240
+ return true;
241
+ // Bun runtime builtins (`bun:test`, `bun:sqlite`, …) are runtime-provided,
242
+ // never an npm dependency — so they are not "missing".
243
+ if (spec.startsWith('bun:'))
244
+ return true;
245
+ return new Set([
246
+ 'fs', 'path', 'os', 'crypto', 'http', 'https', 'url', 'util', 'stream',
247
+ 'events', 'child_process', 'process', 'buffer', 'querystring', 'zlib',
248
+ 'tls', 'net', 'dns', 'dgram', 'cluster', 'worker_threads', 'perf_hooks',
249
+ 'readline', 'tty', 'vm',
250
+ ]).has(spec);
251
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Read-only MCP tool: get_diff_check_report.
3
+ *
4
+ * The MCP-side mirror of `shrk diff-check`. Same envelope, same
5
+ * verdict logic — the agent gets a single structured answer to "did
6
+ * my edits pass this project's boundary + import-hygiene gates?".
7
+ *
8
+ * Still read-only: this tool DOES NOT fix anything, even when it
9
+ * could trivially suggest the fix. The agent reads the envelope, then
10
+ * the human (or the agent, via a separate write-path tool) runs the
11
+ * fix on the CLI. Keeps the safety contract intact.
12
+ */
13
+ import type { IToolDefinition } from '../server/tool-definition.js';
14
+ export declare const getDiffCheckReportTool: IToolDefinition;
15
+ //# sourceMappingURL=diff-check.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-check.tool.d.ts","sourceRoot":"","sources":["../../src/tools/diff-check.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAyBpE,eAAO,MAAM,sBAAsB,EAAE,eAyHpC,CAAC"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Read-only MCP tool: get_diff_check_report.
3
+ *
4
+ * The MCP-side mirror of `shrk diff-check`. Same envelope, same
5
+ * verdict logic — the agent gets a single structured answer to "did
6
+ * my edits pass this project's boundary + import-hygiene gates?".
7
+ *
8
+ * Still read-only: this tool DOES NOT fix anything, even when it
9
+ * could trivially suggest the fix. The agent reads the envelope, then
10
+ * the human (or the agent, via a separate write-path tool) runs the
11
+ * fix on the CLI. Keeps the safety contract intact.
12
+ */
13
+ import { buildImportHygieneReport, filterViolationsToChangedScope, resolveChangedFiles, } from '@shrkcrft/inspector';
14
+ import { evaluateBoundaries, loadTsconfigPaths, scanImports, } from '@shrkcrft/boundaries';
15
+ const SCHEMA = 'sharkcraft.diff-check/v1';
16
+ function resolveScopeFromInput(input, cwd) {
17
+ const staged = input.staged === true;
18
+ const since = typeof input.since === 'string' ? input.since : undefined;
19
+ const files = Array.isArray(input.files)
20
+ ? input.files.filter((f) => typeof f === 'string')
21
+ : [];
22
+ if (files.length > 0) {
23
+ return { mode: 'files', options: { projectRoot: cwd, files } };
24
+ }
25
+ if (staged) {
26
+ return { mode: 'staged', options: { projectRoot: cwd, staged: true } };
27
+ }
28
+ if (since) {
29
+ return { mode: 'since', options: { projectRoot: cwd, since } };
30
+ }
31
+ return { mode: 'worktree', options: { projectRoot: cwd, includeWorktree: true } };
32
+ }
33
+ export const getDiffCheckReportTool = {
34
+ name: 'get_diff_check_report',
35
+ description: 'Self-check the current git diff against this project\'s boundary + import-hygiene rules. Single-call composite of the boundary-check and import-hygiene engines, scoped to the changed files, with one verdict (ok | warnings | errors) and one nextAction line. Use after editing code so you can validate before declaring done. Read-only.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ additionalProperties: false,
39
+ properties: {
40
+ staged: { type: 'boolean', description: 'Scope to staged changes only.' },
41
+ since: { type: 'string', description: 'Compare against ref (HEAD, origin/main, SHA).' },
42
+ files: {
43
+ type: 'array',
44
+ items: { type: 'string' },
45
+ description: 'Explicit file list (overrides --staged / --since).',
46
+ },
47
+ },
48
+ },
49
+ async handler(input, ctx) {
50
+ const cwd = ctx.cwd;
51
+ const { mode, options: scopeOptions } = resolveScopeFromInput(input, cwd);
52
+ const changed = resolveChangedFiles(scopeOptions);
53
+ const changedFiles = changed.files;
54
+ const rules = ctx.inspection.boundaryRegistry.list();
55
+ let boundaryBlock = {
56
+ ran: false,
57
+ rulesEvaluated: 0,
58
+ counts: { error: 0, warning: 0, info: 0 },
59
+ violations: [],
60
+ };
61
+ if (rules.length > 0 && changedFiles.length > 0) {
62
+ const scan = scanImports({ projectRoot: cwd });
63
+ const tsconfigPaths = loadTsconfigPaths(cwd);
64
+ const evalResult = evaluateBoundaries(scan, rules, {
65
+ ...(tsconfigPaths.aliases.size > 0 ? { tsconfigPaths } : {}),
66
+ });
67
+ const filtered = filterViolationsToChangedScope(evalResult.violations, scopeOptions);
68
+ boundaryBlock = {
69
+ ran: true,
70
+ rulesEvaluated: evalResult.rulesEvaluated,
71
+ counts: {
72
+ error: filtered.includedViolations.filter((v) => v.severity === 'error').length,
73
+ warning: filtered.includedViolations.filter((v) => v.severity === 'warning').length,
74
+ info: filtered.includedViolations.filter((v) => v.severity === 'info').length,
75
+ },
76
+ violations: filtered.includedViolations,
77
+ };
78
+ }
79
+ else if (rules.length > 0) {
80
+ boundaryBlock = { ...boundaryBlock, ran: true, rulesEvaluated: rules.length };
81
+ }
82
+ let importsBlock = {
83
+ ran: false,
84
+ verdict: 'skipped',
85
+ counts: {},
86
+ findings: [],
87
+ };
88
+ if (changedFiles.length > 0) {
89
+ const report = buildImportHygieneReport(cwd, { files: changedFiles });
90
+ importsBlock = {
91
+ ran: true,
92
+ verdict: report.verdict,
93
+ counts: report.counts ?? {},
94
+ findings: report.findings,
95
+ };
96
+ }
97
+ // Derive verdict — same logic as the CLI command, duplicated
98
+ // intentionally to keep the MCP tool self-contained (no CLI
99
+ // import — preserves the package dependency direction).
100
+ const bErr = boundaryBlock.counts.error;
101
+ const bWarn = boundaryBlock.counts.warning;
102
+ const iErr = importsBlock.verdict === 'errors' ? (importsBlock.counts.error ?? importsBlock.findings.length) : 0;
103
+ const iWarn = importsBlock.verdict === 'warnings' ? (importsBlock.counts.warning ?? importsBlock.findings.length) : 0;
104
+ let verdict;
105
+ let summary;
106
+ let nextAction;
107
+ if (changedFiles.length === 0) {
108
+ verdict = 'ok';
109
+ summary = 'No files changed in the current diff scope.';
110
+ nextAction =
111
+ 'Nothing to check. If you expected changes, verify the `staged` / `since` argument or save edits first.';
112
+ }
113
+ else if (bErr > 0 || iErr > 0) {
114
+ verdict = 'errors';
115
+ const parts = [];
116
+ if (bErr > 0)
117
+ parts.push(`${bErr} boundary violation${bErr === 1 ? '' : 's'}`);
118
+ if (iErr > 0)
119
+ parts.push(`${iErr} import-hygiene error${iErr === 1 ? '' : 's'}`);
120
+ summary = `Diff fails the gate: ${parts.join(', ')}.`;
121
+ nextAction =
122
+ 'Fix every error in `boundaries.violations` and `imports.findings` (each entry\'s `suggestedFix` shows the fix), then re-run.';
123
+ }
124
+ else if (bWarn > 0 || iWarn > 0) {
125
+ verdict = 'warnings';
126
+ const parts = [];
127
+ if (bWarn > 0)
128
+ parts.push(`${bWarn} boundary warning${bWarn === 1 ? '' : 's'}`);
129
+ if (iWarn > 0)
130
+ parts.push(`${iWarn} import-hygiene warning${iWarn === 1 ? '' : 's'}`);
131
+ summary = `Diff passes the gate with ${parts.join(', ')}.`;
132
+ nextAction = 'Safe to declare done. Review warnings if the diff touches a sensitive area.';
133
+ }
134
+ else {
135
+ verdict = 'ok';
136
+ summary = `Diff passes the gate (${changedFiles.length} file${changedFiles.length === 1 ? '' : 's'}, 0 violations).`;
137
+ nextAction = 'Safe to declare done.';
138
+ }
139
+ return {
140
+ text: `verdict=${verdict}. ${summary} ${nextAction}`,
141
+ data: {
142
+ schema: SCHEMA,
143
+ generatedAt: new Date().toISOString(),
144
+ scope: {
145
+ mode,
146
+ files: changedFiles,
147
+ fileCount: changedFiles.length,
148
+ },
149
+ boundaries: boundaryBlock,
150
+ imports: importsBlock,
151
+ verdict,
152
+ summary,
153
+ nextAction,
154
+ },
155
+ };
156
+ },
157
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Read-only MCP tool: get_file_advice.
3
+ *
4
+ * For a given file path, returns the rules, path conventions, boundary
5
+ * rules, and knowledge entries that apply to it. The agent equivalent
6
+ * of `shrk why <file>` — same engine (`buildWhyReport`), shaped for
7
+ * MCP consumption.
8
+ *
9
+ * Why this exists alongside `inspect_workspace` and the various
10
+ * `list_*` tools: agents tend to over-explore when given browsable
11
+ * catalogs. Asking "give me everything for this file" in one call
12
+ * keeps the prompt window tight and the answer focused — the agent
13
+ * doesn't have to discover which rule matches the file's path glob,
14
+ * which boundary rule constrains its imports, or which knowledge
15
+ * entry is the right one to read first.
16
+ *
17
+ * Use after a `shrk diff-check` flags a violation, or before editing
18
+ * an unfamiliar file. Read-only.
19
+ */
20
+ import type { IToolDefinition } from '../server/tool-definition.js';
21
+ export declare const getFileAdviceTool: IToolDefinition;
22
+ //# sourceMappingURL=file-advice.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-advice.tool.d.ts","sourceRoot":"","sources":["../../src/tools/file-advice.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,eAAO,MAAM,iBAAiB,EAAE,eAyC/B,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Read-only MCP tool: get_file_advice.
3
+ *
4
+ * For a given file path, returns the rules, path conventions, boundary
5
+ * rules, and knowledge entries that apply to it. The agent equivalent
6
+ * of `shrk why <file>` — same engine (`buildWhyReport`), shaped for
7
+ * MCP consumption.
8
+ *
9
+ * Why this exists alongside `inspect_workspace` and the various
10
+ * `list_*` tools: agents tend to over-explore when given browsable
11
+ * catalogs. Asking "give me everything for this file" in one call
12
+ * keeps the prompt window tight and the answer focused — the agent
13
+ * doesn't have to discover which rule matches the file's path glob,
14
+ * which boundary rule constrains its imports, or which knowledge
15
+ * entry is the right one to read first.
16
+ *
17
+ * Use after a `shrk diff-check` flags a violation, or before editing
18
+ * an unfamiliar file. Read-only.
19
+ */
20
+ import { buildWhyReport } from '@shrkcrft/inspector';
21
+ export const getFileAdviceTool = {
22
+ name: 'get_file_advice',
23
+ description: 'For a given file path, return the rules, path conventions, boundary rules, and knowledge entries that apply to it. Single-call replacement for browsing the registry to figure out what constrains one file. Read-only.',
24
+ inputSchema: {
25
+ type: 'object',
26
+ additionalProperties: false,
27
+ required: ['file'],
28
+ properties: {
29
+ file: {
30
+ type: 'string',
31
+ description: 'Path to the file (absolute, or relative to the project root). The file does not need to exist — path-string matching still works.',
32
+ },
33
+ limit: {
34
+ type: 'number',
35
+ description: 'Cap rules and knowledge entries returned (default 10).',
36
+ },
37
+ },
38
+ },
39
+ async handler(input, ctx) {
40
+ const file = typeof input.file === 'string' ? input.file : '';
41
+ if (!file) {
42
+ return {
43
+ text: 'Error: `file` argument is required.',
44
+ data: { error: 'missing-argument', argument: 'file' },
45
+ };
46
+ }
47
+ const limit = typeof input.limit === 'number' && input.limit > 0 ? Math.floor(input.limit) : 10;
48
+ const report = buildWhyReport({
49
+ inspection: ctx.inspection,
50
+ projectRoot: ctx.cwd,
51
+ target: file,
52
+ limit,
53
+ });
54
+ const summary = buildSummary(report);
55
+ return {
56
+ text: summary,
57
+ data: report,
58
+ };
59
+ },
60
+ };
61
+ function buildSummary(report) {
62
+ const lines = [];
63
+ lines.push(`File: ${report.target.relativePath} (${report.target.kind})`);
64
+ if (report.inferredPackage)
65
+ lines.push(`Package: ${report.inferredPackage}`);
66
+ if (report.inferredLayer)
67
+ lines.push(`Layer: ${report.inferredLayer}`);
68
+ const counts = {
69
+ rules: report.rules.length,
70
+ boundaries: report.boundaries.length,
71
+ paths: report.pathConventions.length,
72
+ knowledge: report.knowledge.length,
73
+ };
74
+ lines.push(`Matches: ${counts.rules} rule${counts.rules === 1 ? '' : 's'}, ` +
75
+ `${counts.boundaries} boundary rule${counts.boundaries === 1 ? '' : 's'}, ` +
76
+ `${counts.paths} path convention${counts.paths === 1 ? '' : 's'}, ` +
77
+ `${counts.knowledge} knowledge entr${counts.knowledge === 1 ? 'y' : 'ies'}.`);
78
+ if (counts.rules === 0 &&
79
+ counts.boundaries === 0 &&
80
+ counts.paths === 0 &&
81
+ counts.knowledge === 0) {
82
+ lines.push('No registry entries matched. The file may be outside the conventions, or the workspace may not have rules / paths defined yet.');
83
+ }
84
+ else {
85
+ lines.push('See `data` for the full per-category list.');
86
+ }
87
+ return lines.join('\n');
88
+ }
@@ -0,0 +1,3 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ export declare const getApiSurfaceDiffTool: IToolDefinition;
3
+ //# sourceMappingURL=get-api-surface-diff.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-api-surface-diff.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-api-surface-diff.tool.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAUpE,eAAO,MAAM,qBAAqB,EAAE,eAsDnC,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ import { diffApiSurfaces, extractApiSurface, } from '@shrkcrft/api-surface-diff';
4
+ import { GraphStore } from '@shrkcrft/graph';
5
+ const NEXT = 'shrk graph index';
6
+ export const getApiSurfaceDiffTool = {
7
+ name: 'get_api_surface_diff',
8
+ description: 'Read-only: compare the current code-graph public-API surface to a baseline. Provide `baselinePath` (file system path to a previously captured `IApiSurface`) OR `baseline` (inline). Optional `packages` filter restricts both sides to those workspace packages.',
9
+ cliCommand: 'api-diff',
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ baselinePath: { type: 'string' },
14
+ baseline: { type: 'object' },
15
+ packages: { type: 'array', items: { type: 'string' } },
16
+ },
17
+ additionalProperties: false,
18
+ },
19
+ handler(input, ctx) {
20
+ const args = input;
21
+ const store = new GraphStore(ctx.inspection.projectRoot);
22
+ if (!store.exists()) {
23
+ return {
24
+ isError: true,
25
+ error: {
26
+ code: 'graph-missing',
27
+ message: "code-graph store missing",
28
+ details: { nextCommand: NEXT },
29
+ },
30
+ };
31
+ }
32
+ let baseline = args.baseline;
33
+ if (!baseline && args.baselinePath) {
34
+ const abs = nodePath.isAbsolute(args.baselinePath)
35
+ ? args.baselinePath
36
+ : nodePath.resolve(ctx.inspection.projectRoot, args.baselinePath);
37
+ try {
38
+ baseline = JSON.parse(readFileSync(abs, 'utf8'));
39
+ }
40
+ catch (e) {
41
+ return {
42
+ isError: true,
43
+ error: { code: 'invalid-input', message: `baseline read failed: ${e.message}` },
44
+ };
45
+ }
46
+ }
47
+ if (!baseline) {
48
+ return {
49
+ isError: true,
50
+ error: { code: 'invalid-input', message: 'baseline or baselinePath is required' },
51
+ };
52
+ }
53
+ const snap = store.loadSnapshot();
54
+ const current = extractApiSurface(snap, {
55
+ ...(args.packages && args.packages.length > 0 ? { packageFilter: args.packages } : {}),
56
+ });
57
+ const diff = diffApiSurfaces(baseline, current);
58
+ return { data: diff };
59
+ },
60
+ };
@@ -0,0 +1,3 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ export declare const getArchViolationsTool: IToolDefinition;
3
+ //# sourceMappingURL=get-arch-violations.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-arch-violations.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-arch-violations.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAWpE,eAAO,MAAM,qBAAqB,EAAE,eA6BnC,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { runArchCheck } from '@shrkcrft/architecture-guard';
2
+ export const getArchViolationsTool = {
3
+ name: 'get_arch_violations',
4
+ description: 'Run the architecture-guard checks (public-API misuse, barrel risks, cycle severity) against the code graph. Reads `sharkcraft/arch.ts` if present for project-specific contracts. Read-only.',
5
+ cliCommand: 'arch check',
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ enable: {
10
+ type: 'object',
11
+ properties: {
12
+ publicApi: { type: 'boolean' },
13
+ barrels: { type: 'boolean' },
14
+ cycles: { type: 'boolean' },
15
+ contract: { type: 'boolean' },
16
+ },
17
+ additionalProperties: false,
18
+ },
19
+ },
20
+ additionalProperties: false,
21
+ },
22
+ handler(input, ctx) {
23
+ const args = input;
24
+ const report = runArchCheck({
25
+ projectRoot: ctx.inspection.projectRoot,
26
+ ...(args.enable ? { enable: args.enable } : {}),
27
+ });
28
+ return { data: report };
29
+ },
30
+ };
@@ -0,0 +1,11 @@
1
+ import type { IToolDefinition } from '../server/tool-definition.js';
2
+ /**
3
+ * Read-only MCP mirror of `shrk code-intel`. Returns the same 14
4
+ * code-intelligence doctor findings in one shot — agents can pull
5
+ * the entire state without iterating `shrk doctor`'s full check list.
6
+ *
7
+ * Stable output schema (`sharkcraft.code-intelligence-state/v1`) so
8
+ * downstream renderers (dashboard, CI bots) can rely on the shape.
9
+ */
10
+ export declare const getCodeIntelligenceStateTool: IToolDefinition;
11
+ //# sourceMappingURL=get-code-intelligence-state.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-code-intelligence-state.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-code-intelligence-state.tool.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAUpE;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,EAAE,eAiC1C,CAAC"}