@shrkcrft/mcp-server 0.1.0-alpha.15 → 0.1.0-alpha.16
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.
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/server/columnar-format.d.ts +34 -0
- package/dist/server/columnar-format.d.ts.map +1 -0
- package/dist/server/columnar-format.js +95 -0
- package/dist/server/create-mcp-server.d.ts +3 -0
- package/dist/server/create-mcp-server.d.ts.map +1 -1
- package/dist/server/create-mcp-server.js +5 -2
- package/dist/server/fit-array-to-budget.d.ts +20 -0
- package/dist/server/fit-array-to-budget.d.ts.map +1 -0
- package/dist/server/fit-array-to-budget.js +28 -0
- package/dist/server/serialize-tool-data.d.ts +15 -0
- package/dist/server/serialize-tool-data.d.ts.map +1 -0
- package/dist/server/serialize-tool-data.js +22 -0
- package/dist/server/tool-definition.d.ts +15 -0
- package/dist/server/tool-definition.d.ts.map +1 -1
- package/dist/server/tool-input-validators.d.ts.map +1 -1
- package/dist/server/tool-input-validators.js +43 -0
- package/dist/tools/agent-brief.tool.d.ts.map +1 -1
- package/dist/tools/agent-brief.tool.js +20 -0
- package/dist/tools/align-cache.tool.d.ts +11 -0
- package/dist/tools/align-cache.tool.d.ts.map +1 -0
- package/dist/tools/align-cache.tool.js +76 -0
- package/dist/tools/all-tools.d.ts.map +1 -1
- package/dist/tools/all-tools.js +7 -0
- package/dist/tools/architecture-map.tool.d.ts.map +1 -1
- package/dist/tools/architecture-map.tool.js +4 -2
- package/dist/tools/code-find-usages.tool.d.ts.map +1 -1
- package/dist/tools/code-find-usages.tool.js +15 -10
- package/dist/tools/command-catalog.tool.d.ts.map +1 -1
- package/dist/tools/command-catalog.tool.js +11 -7
- package/dist/tools/compress-context.tool.d.ts +8 -0
- package/dist/tools/compress-context.tool.d.ts.map +1 -0
- package/dist/tools/compress-context.tool.js +80 -0
- package/dist/tools/dashboard-summary.tool.d.ts.map +1 -1
- package/dist/tools/dashboard-summary.tool.js +2 -4
- package/dist/tools/deps-audit.tool.d.ts.map +1 -1
- package/dist/tools/deps-audit.tool.js +32 -2
- package/dist/tools/get-code-intelligence-state.tool.d.ts.map +1 -1
- package/dist/tools/get-code-intelligence-state.tool.js +8 -7
- package/dist/tools/get-graph-callers.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-callers.tool.js +9 -8
- package/dist/tools/get-graph-context.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-context.tool.js +19 -19
- package/dist/tools/get-graph-cycles.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-cycles.tool.js +11 -10
- package/dist/tools/get-graph-impact-analysis.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-impact-analysis.tool.js +3 -1
- package/dist/tools/get-graph-impact.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-impact.tool.js +14 -13
- package/dist/tools/get-graph-search.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-search.tool.js +9 -8
- package/dist/tools/get-graph-unresolved.tool.d.ts.map +1 -1
- package/dist/tools/get-graph-unresolved.tool.js +13 -9
- package/dist/tools/get-knowledge-graph.tool.d.ts +7 -0
- package/dist/tools/get-knowledge-graph.tool.d.ts.map +1 -1
- package/dist/tools/get-knowledge-graph.tool.js +62 -3
- package/dist/tools/get-relevant-context.tool.d.ts.map +1 -1
- package/dist/tools/get-relevant-context.tool.js +30 -6
- package/dist/tools/get-task-packet.tool.d.ts.map +1 -1
- package/dist/tools/get-task-packet.tool.js +26 -22
- package/dist/tools/list-boundary-rules.tool.d.ts.map +1 -1
- package/dist/tools/list-boundary-rules.tool.js +20 -16
- package/dist/tools/list-knowledge.tool.d.ts.map +1 -1
- package/dist/tools/list-knowledge.tool.js +14 -13
- package/dist/tools/list-packs.tool.d.ts.map +1 -1
- package/dist/tools/list-packs.tool.js +19 -15
- package/dist/tools/list-path-conventions.tool.d.ts.map +1 -1
- package/dist/tools/list-path-conventions.tool.js +19 -15
- package/dist/tools/list-pipelines.tool.d.ts.map +1 -1
- package/dist/tools/list-pipelines.tool.js +18 -14
- package/dist/tools/list-presets.tool.d.ts.map +1 -1
- package/dist/tools/list-presets.tool.js +25 -21
- package/dist/tools/list-rules.tool.d.ts.map +1 -1
- package/dist/tools/list-rules.tool.js +18 -14
- package/dist/tools/list-templates.tool.d.ts.map +1 -1
- package/dist/tools/list-templates.tool.js +18 -14
- package/dist/tools/primary-tools.d.ts.map +1 -1
- package/dist/tools/primary-tools.js +5 -0
- package/dist/tools/retrieve-original.tool.d.ts +9 -0
- package/dist/tools/retrieve-original.tool.d.ts.map +1 -0
- package/dist/tools/retrieve-original.tool.js +47 -0
- package/dist/tools/runtime-reports.tool.d.ts.map +1 -1
- package/dist/tools/runtime-reports.tool.js +1 -3
- package/dist/tools/safety-audit.tool.d.ts.map +1 -1
- package/dist/tools/safety-audit.tool.js +1 -4
- package/dist/tools/search-knowledge.tool.d.ts.map +1 -1
- package/dist/tools/search-knowledge.tool.js +17 -13
- package/dist/tools/search.tool.d.ts.map +1 -1
- package/dist/tools/search.tool.js +11 -8
- package/dist/tools/smart-context-bundle.tool.d.ts.map +1 -1
- package/dist/tools/smart-context-bundle.tool.js +4 -2
- package/package.json +27 -26
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import * as nodePath from 'node:path';
|
|
3
3
|
import { EdgeKind, GraphQueryApi, GraphStore, NodeKind } from '@shrkcrft/graph';
|
|
4
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
4
5
|
/**
|
|
5
6
|
* `code_find_usages` — structured usage finder backed by the
|
|
6
7
|
* SharkCraft graph (file + symbol nodes + import/declare edges).
|
|
@@ -16,13 +17,14 @@ import { EdgeKind, GraphQueryApi, GraphStore, NodeKind } from '@shrkcrft/graph';
|
|
|
16
17
|
*/
|
|
17
18
|
export const codeFindUsagesTool = {
|
|
18
19
|
name: 'code_find_usages',
|
|
19
|
-
description: 'Find structured usages of a symbol via the SharkCraft graph (file + symbol nodes). Read-only. Distinguishes definition, import-of-declaring-file, and neighbouring symbols.',
|
|
20
|
+
description: 'Find structured usages of a symbol via the SharkCraft graph (file + symbol nodes). Read-only. Distinguishes definition, import-of-declaring-file, and neighbouring symbols. Pass `format:"table"` for a token-efficient columnar encoding of the definitions/importers/neighbours arrays.',
|
|
20
21
|
inputSchema: {
|
|
21
22
|
type: 'object',
|
|
22
23
|
properties: {
|
|
23
24
|
symbolName: { type: 'string' },
|
|
24
25
|
kindHint: { type: 'string' },
|
|
25
26
|
maxResults: { type: 'number' },
|
|
27
|
+
...FORMAT_INPUT_PROPERTY,
|
|
26
28
|
},
|
|
27
29
|
required: ['symbolName'],
|
|
28
30
|
additionalProperties: false,
|
|
@@ -96,16 +98,19 @@ export const codeFindUsagesTool = {
|
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
note: 'importersOfDeclaringFile = files that import the declaring file. This is a structural signal — it may include type-only imports and unused references. Pair with `shrk impact` for a tighter blast radius.',
|
|
107
|
-
},
|
|
101
|
+
const data = {
|
|
102
|
+
symbol: { name: symbolName, kind: matches[0]?.kind ?? 'unknown' },
|
|
103
|
+
definitions,
|
|
104
|
+
importersOfDeclaringFile: [...importerSet.values()],
|
|
105
|
+
neighbouringSymbols: neighbours.slice(0, 12),
|
|
106
|
+
totalSymbolMatches: matches.length,
|
|
107
|
+
note: 'importersOfDeclaringFile = files that import the declaring file. This is a structural signal — it may include type-only imports and unused references. Pair with `shrk impact` for a tighter blast radius.',
|
|
108
108
|
};
|
|
109
|
+
// `format:"table"` columnar-encodes the homogeneous object-array fields
|
|
110
|
+
// (definitions, importersOfDeclaringFile, neighbouringSymbols); the scalar
|
|
111
|
+
// `symbol` object, totalSymbolMatches, and the note string pass through
|
|
112
|
+
// untouched. Default/`format:"json"` returns the bare object unchanged.
|
|
113
|
+
return { data: formatObjectArrays(data, input) };
|
|
109
114
|
},
|
|
110
115
|
};
|
|
111
116
|
function declaringFileOf(api, symbolId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-catalog.tool.d.ts","sourceRoot":"","sources":["../../src/tools/command-catalog.tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"command-catalog.tool.d.ts","sourceRoot":"","sources":["../../src/tools/command-catalog.tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAkBpE,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,cAAc,CAAC;CAC7C;AAwBD,eAAO,MAAM,sBAAsB,EAAE,SAAS,aAAa,EAgFzD,CAAC;AA+BH,eAAO,MAAM,qBAAqB,EAAE,eA+BnC,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
1
2
|
const MCP_BOOTSTRAP_CORE = new Set([
|
|
2
3
|
'doctor',
|
|
3
4
|
'init',
|
|
@@ -123,6 +124,7 @@ export const getCommandCatalogTool = {
|
|
|
123
124
|
properties: {
|
|
124
125
|
safetyLevel: { type: 'string', description: 'Filter by safety level (read-only, writes-session, writes-drafts, writes-source, runs-shell, requires-review).' },
|
|
125
126
|
category: { type: 'string', description: 'Filter by category.' },
|
|
127
|
+
...FORMAT_INPUT_PROPERTY,
|
|
126
128
|
},
|
|
127
129
|
additionalProperties: false,
|
|
128
130
|
},
|
|
@@ -134,14 +136,16 @@ export const getCommandCatalogTool = {
|
|
|
134
136
|
entries = entries.filter((e) => e.safetyLevel === safety);
|
|
135
137
|
if (typeof category === 'string')
|
|
136
138
|
entries = entries.filter((e) => e.category === category);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
returned: entries.length,
|
|
143
|
-
},
|
|
139
|
+
const data = {
|
|
140
|
+
entries,
|
|
141
|
+
totals: {
|
|
142
|
+
total: COMMAND_CATALOG_EXPORT.length,
|
|
143
|
+
returned: entries.length,
|
|
144
144
|
},
|
|
145
145
|
};
|
|
146
|
+
// `format:"table"` columnar-encodes the homogeneous `entries` array
|
|
147
|
+
// (the ~11-field catalog rows); the `totals` scalar object is left
|
|
148
|
+
// untouched. Default/`format:"json"` returns the object unchanged.
|
|
149
|
+
return { data: formatObjectArrays(data, input) };
|
|
146
150
|
},
|
|
147
151
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IToolDefinition } from '../server/tool-definition.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic context compressor exposed to agents — shrink a blob before it
|
|
4
|
+
* re-enters the prompt, with no model in the loop: it's a pure function of the
|
|
5
|
+
* input. Reversible via the CCR store wired into the server context.
|
|
6
|
+
*/
|
|
7
|
+
export declare const compressContextTool: IToolDefinition;
|
|
8
|
+
//# sourceMappingURL=compress-context.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compress-context.tool.d.ts","sourceRoot":"","sources":["../../src/tools/compress-context.tool.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAIpE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,eA4EjC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { compressContent, EContentType, } from '@shrkcrft/compress';
|
|
2
|
+
const CONTENT_TYPES = new Set(Object.values(EContentType));
|
|
3
|
+
/**
|
|
4
|
+
* Deterministic context compressor exposed to agents — shrink a blob before it
|
|
5
|
+
* re-enters the prompt, with no model in the loop: it's a pure function of the
|
|
6
|
+
* input. Reversible via the CCR store wired into the server context.
|
|
7
|
+
*/
|
|
8
|
+
export const compressContextTool = {
|
|
9
|
+
name: 'compress_context',
|
|
10
|
+
description: 'Deterministically compress a blob (tool output, build/test log, grep/search results, unified diff, or JSON) BEFORE you feed it back to the model — same information, far fewer tokens. Routes by content type, hoists JSON object-array schemas into dense tables, and reduces logs/search/diffs to their highest-signal lines. Reversible: when a lossy pass drops detail it caches the original and emits a `<<ccr:KEY>>` marker — call `retrieve_original` to get the full text back. No AI involved; same bytes in, same bytes out. Read-only.',
|
|
11
|
+
inputSchema: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
content: { type: 'string', description: 'The text to compress.' },
|
|
15
|
+
contentType: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Force a content class instead of auto-detecting: json | json-array | git-diff | search-results | build-log | source-code | markdown | plain-text.',
|
|
18
|
+
},
|
|
19
|
+
query: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Optional task/query text that biases which lines or matches are kept.',
|
|
22
|
+
},
|
|
23
|
+
maxItems: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
description: 'Soft cap on retained lines / matches / hunks (compressor-specific).',
|
|
26
|
+
},
|
|
27
|
+
maxTokens: {
|
|
28
|
+
type: 'number',
|
|
29
|
+
minimum: 1,
|
|
30
|
+
description: 'Token budget for a JSON array. When set and the lossless columnar form still exceeds it, falls back to the lossy SmartCrusher row-sampler (kept rows + CCR original).',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: ['content'],
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
handler(input, ctx) {
|
|
37
|
+
const content = typeof input.content === 'string' ? input.content : '';
|
|
38
|
+
if (content.length === 0) {
|
|
39
|
+
return {
|
|
40
|
+
isError: true,
|
|
41
|
+
text: 'compress_context requires a non-empty "content" string.',
|
|
42
|
+
error: { code: 'invalid-input', message: 'content is required' },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const opts = {};
|
|
46
|
+
if (ctx.ccrStore)
|
|
47
|
+
opts.store = ctx.ccrStore;
|
|
48
|
+
if (typeof input.query === 'string')
|
|
49
|
+
opts.query = input.query;
|
|
50
|
+
if (typeof input.maxItems === 'number' && input.maxItems > 0) {
|
|
51
|
+
opts.maxItems = Math.floor(input.maxItems);
|
|
52
|
+
}
|
|
53
|
+
if (typeof input.maxTokens === 'number' && input.maxTokens > 0) {
|
|
54
|
+
opts.maxTokens = Math.floor(input.maxTokens);
|
|
55
|
+
}
|
|
56
|
+
if (typeof input.contentType === 'string' && CONTENT_TYPES.has(input.contentType)) {
|
|
57
|
+
opts.contentType = input.contentType;
|
|
58
|
+
}
|
|
59
|
+
const result = compressContent(content, opts);
|
|
60
|
+
const pct = Math.round(result.savings.ratio * 100);
|
|
61
|
+
return {
|
|
62
|
+
data: {
|
|
63
|
+
contentType: result.contentType,
|
|
64
|
+
strategy: result.strategy,
|
|
65
|
+
lossy: result.lossy,
|
|
66
|
+
tokensBefore: result.savings.before,
|
|
67
|
+
tokensAfter: result.savings.after,
|
|
68
|
+
tokensSaved: result.savings.saved,
|
|
69
|
+
savedRatio: result.savings.ratio,
|
|
70
|
+
ccrKey: result.ccrKey ?? null,
|
|
71
|
+
note: result.note,
|
|
72
|
+
compressed: result.compressed,
|
|
73
|
+
...(result.ccrKey
|
|
74
|
+
? { retrieveWith: `retrieve_original { "key": "${result.ccrKey}" }` }
|
|
75
|
+
: {}),
|
|
76
|
+
},
|
|
77
|
+
text: `${result.strategy}: ${result.savings.before} → ${result.savings.after} tokens (−${pct}%)${result.ccrKey ? ` · original cached as ${result.ccrKey}` : ''}`,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-summary.tool.d.ts","sourceRoot":"","sources":["../../src/tools/dashboard-summary.tool.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard-summary.tool.d.ts","sourceRoot":"","sources":["../../src/tools/dashboard-summary.tool.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAQpE,eAAO,MAAM,uBAAuB,EAAE,eA0LrC,CAAC"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { buildAreaMap, buildAiReadinessReport, buildCoverageReport, buildDriftReport, buildQualityReport, buildReleaseReadiness, buildSafetyAudit, evaluatePolicy, listConstructs, listDevSessionsDetailed, listFeatureBundles, listPlaybooks, } from '@shrkcrft/inspector';
|
|
2
2
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
3
3
|
import * as nodePath from 'node:path';
|
|
4
|
-
// DX#4 — derive audit view from ALL_TOOLS at runtime.
|
|
5
|
-
import { ALL_TOOLS } from "./all-tools.js";
|
|
6
4
|
export const getDashboardSummaryTool = {
|
|
7
5
|
name: 'get_dashboard_summary',
|
|
8
6
|
description: 'Compact dashboard summary aggregating quality, safety, readiness, coverage, drift, packs, sessions, bundles, command-safety totals, and recommended next commands. Read-only.',
|
|
@@ -75,7 +73,7 @@ export const getDashboardSummaryTool = {
|
|
|
75
73
|
const safety = buildSafetyAudit({
|
|
76
74
|
inspection,
|
|
77
75
|
catalog: [],
|
|
78
|
-
mcpTools:
|
|
76
|
+
mcpTools: (ctx.allTools ?? []).map((t) => ({ name: t.name, description: t.description })),
|
|
79
77
|
planSecretEnv: 'SHARKCRAFT_PLAN_SECRET',
|
|
80
78
|
planSecretConfigured: typeof process.env['SHARKCRAFT_PLAN_SECRET'] === 'string' &&
|
|
81
79
|
process.env['SHARKCRAFT_PLAN_SECRET'].length > 0,
|
|
@@ -155,7 +153,7 @@ export const getDashboardSummaryTool = {
|
|
|
155
153
|
invalid: inspection.packs.invalidPacks?.length ?? 0,
|
|
156
154
|
},
|
|
157
155
|
policy: policySummary,
|
|
158
|
-
mcpTools: { total:
|
|
156
|
+
mcpTools: { total: ctx.allTools?.length ?? 0, anyWritable: safety.mcp.anyWritable },
|
|
159
157
|
reportSite: { available: siteAvailable, dir: reportSiteDir },
|
|
160
158
|
releaseReadiness: releaseReadinessSummary,
|
|
161
159
|
releaseSmoke: smokeSummary,
|
|
@@ -1 +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;
|
|
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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import * as nodePath from 'node:path';
|
|
3
3
|
import { GraphQueryApi, GraphStore, NodeKind } 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";
|
|
4
6
|
/**
|
|
5
7
|
* `deps_audit` — MCP wrapper around `shrk deps-audit`. Pure read-only.
|
|
6
8
|
* Returns the same shape the CLI's `--json` mode emits.
|
|
@@ -10,11 +12,17 @@ import { GraphQueryApi, GraphStore, NodeKind } from '@shrkcrft/graph';
|
|
|
10
12
|
*/
|
|
11
13
|
export const depsAuditTool = {
|
|
12
14
|
name: 'deps_audit',
|
|
13
|
-
description: 'Per-package audit of declared dependencies (package.json) vs actually-imported specifiers (graph). Reports missing + unused deps. Read-only.',
|
|
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.',
|
|
14
16
|
inputSchema: {
|
|
15
17
|
type: 'object',
|
|
16
18
|
properties: {
|
|
17
19
|
package: { type: 'string' },
|
|
20
|
+
...FORMAT_INPUT_PROPERTY,
|
|
21
|
+
maxTokens: {
|
|
22
|
+
type: 'number',
|
|
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
|
+
},
|
|
18
26
|
},
|
|
19
27
|
additionalProperties: false,
|
|
20
28
|
},
|
|
@@ -38,7 +46,29 @@ export const depsAuditTool = {
|
|
|
38
46
|
acc.unused += r.unusedDeps.length;
|
|
39
47
|
return acc;
|
|
40
48
|
}, { missing: 0, unused: 0 });
|
|
41
|
-
|
|
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) };
|
|
42
72
|
},
|
|
43
73
|
};
|
|
44
74
|
function listWorkspacePackages(cwd, onlyName) {
|
|
@@ -1 +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;
|
|
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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildCodeIntelligenceChecks, DoctorSeverity, } from '@shrkcrft/inspector';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
/**
|
|
3
4
|
* Read-only MCP mirror of `shrk code-intel`. Returns the same 14
|
|
4
5
|
* code-intelligence doctor findings in one shot — agents can pull
|
|
@@ -16,6 +17,7 @@ export const getCodeIntelligenceStateTool = {
|
|
|
16
17
|
properties: {
|
|
17
18
|
only: { type: 'array', items: { type: 'string' } },
|
|
18
19
|
checkId: { type: 'string' },
|
|
20
|
+
...FORMAT_INPUT_PROPERTY,
|
|
19
21
|
},
|
|
20
22
|
additionalProperties: false,
|
|
21
23
|
},
|
|
@@ -30,14 +32,13 @@ export const getCodeIntelligenceStateTool = {
|
|
|
30
32
|
checks = checks.filter((c) => allowed.has(c.severity));
|
|
31
33
|
}
|
|
32
34
|
const summary = summarize(checks);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
checks,
|
|
39
|
-
},
|
|
35
|
+
const data = {
|
|
36
|
+
schema: 'sharkcraft.code-intelligence-state/v1',
|
|
37
|
+
totalChecks: checks.length,
|
|
38
|
+
summary,
|
|
39
|
+
checks,
|
|
40
40
|
};
|
|
41
|
+
return { data: formatObjectArrays(data, input) };
|
|
41
42
|
},
|
|
42
43
|
};
|
|
43
44
|
function summarize(checks) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-graph-callers.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-callers.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"get-graph-callers.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-callers.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAUpE,eAAO,MAAM,mBAAmB,EAAE,eA0DjC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphQueryApi, GraphStore } from '@shrkcrft/graph';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
const NEXT = 'shrk graph index';
|
|
3
4
|
export const getGraphCallersTool = {
|
|
4
5
|
name: 'get_graph_callers',
|
|
@@ -9,6 +10,7 @@ export const getGraphCallersTool = {
|
|
|
9
10
|
properties: {
|
|
10
11
|
symbol: { type: 'string' },
|
|
11
12
|
mode: { type: 'string', enum: ['call', 'reference'] },
|
|
13
|
+
...FORMAT_INPUT_PROPERTY,
|
|
12
14
|
},
|
|
13
15
|
required: ['symbol'],
|
|
14
16
|
additionalProperties: false,
|
|
@@ -47,15 +49,14 @@ export const getGraphCallersTool = {
|
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
const hits = mode === 'reference' ? api.referencesOf(sym.id) : api.callersOf(sym.id);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
callers: hits.slice(0, 200).map(summarise),
|
|
57
|
-
},
|
|
52
|
+
const data = {
|
|
53
|
+
schema: 'sharkcraft.graph-callers/v1',
|
|
54
|
+
symbol: summarise(sym),
|
|
55
|
+
mode,
|
|
56
|
+
total: hits.length,
|
|
57
|
+
callers: hits.slice(0, 200).map(summarise),
|
|
58
58
|
};
|
|
59
|
+
return { data: formatObjectArrays(data, input) };
|
|
59
60
|
},
|
|
60
61
|
};
|
|
61
62
|
function resolveSymbol(api, target) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-graph-context.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-context.tool.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"get-graph-context.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-context.tool.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AASpE,eAAO,MAAM,mBAAmB,EAAE,eAgEjC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphQueryApi, GraphStore, NodeKind, } from '@shrkcrft/graph';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
const NEXT = 'shrk graph index';
|
|
3
4
|
export const getGraphContextTool = {
|
|
4
5
|
name: 'get_graph_context',
|
|
@@ -6,7 +7,7 @@ export const getGraphContextTool = {
|
|
|
6
7
|
cliCommand: 'graph context',
|
|
7
8
|
inputSchema: {
|
|
8
9
|
type: 'object',
|
|
9
|
-
properties: { target: { type: 'string' } },
|
|
10
|
+
properties: { target: { type: 'string' }, ...FORMAT_INPUT_PROPERTY },
|
|
10
11
|
required: ['target'],
|
|
11
12
|
additionalProperties: false,
|
|
12
13
|
},
|
|
@@ -44,25 +45,24 @@ export const getGraphContextTool = {
|
|
|
44
45
|
}
|
|
45
46
|
const neighbours = api.neighbours(anchor.id);
|
|
46
47
|
const symbols = anchor.kind === NodeKind.File ? api.symbolsIn(anchor.id) : [];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
symbols: symbols.slice(0, 50).map(summarise),
|
|
64
|
-
},
|
|
48
|
+
const data = {
|
|
49
|
+
schema: 'sharkcraft.graph-context/v1',
|
|
50
|
+
anchor: summarise(anchor),
|
|
51
|
+
importsFrom: neighbours.out
|
|
52
|
+
.filter((o) => o.edge.kind === 'imports-file')
|
|
53
|
+
.slice(0, 50)
|
|
54
|
+
.map((o) => ('resolved' in o.target
|
|
55
|
+
? { id: o.target.id, resolved: false }
|
|
56
|
+
: { ...summarise(o.target), resolved: true })),
|
|
57
|
+
importedBy: neighbours.in
|
|
58
|
+
.filter((i) => i.edge.kind === 'imports-file')
|
|
59
|
+
.slice(0, 50)
|
|
60
|
+
.map((i) => ('resolved' in i.source
|
|
61
|
+
? { id: i.source.id, resolved: false }
|
|
62
|
+
: { ...summarise(i.source), resolved: true })),
|
|
63
|
+
symbols: symbols.slice(0, 50).map(summarise),
|
|
65
64
|
};
|
|
65
|
+
return { data: formatObjectArrays(data, input) };
|
|
66
66
|
},
|
|
67
67
|
};
|
|
68
68
|
function resolveAnchor(api, target) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-graph-cycles.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-cycles.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"get-graph-cycles.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-cycles.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAYpE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAmDhC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphQueryApi, GraphStore } from '@shrkcrft/graph';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
const NEXT = 'shrk graph index';
|
|
3
4
|
/**
|
|
4
5
|
* Read-only MCP mirror of `shrk graph cycles`. Returns the full SCC
|
|
@@ -16,6 +17,7 @@ export const getGraphCyclesTool = {
|
|
|
16
17
|
properties: {
|
|
17
18
|
limit: { type: 'number' },
|
|
18
19
|
minSize: { type: 'number' },
|
|
20
|
+
...FORMAT_INPUT_PROPERTY,
|
|
19
21
|
},
|
|
20
22
|
additionalProperties: false,
|
|
21
23
|
},
|
|
@@ -42,16 +44,15 @@ export const getGraphCyclesTool = {
|
|
|
42
44
|
const all = api.cycles();
|
|
43
45
|
const filtered = all.filter((c) => c.size >= minSize);
|
|
44
46
|
const limited = filtered.slice(0, rawLimit);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})),
|
|
54
|
-
},
|
|
47
|
+
const data = {
|
|
48
|
+
schema: 'sharkcraft.graph-cycles/v1',
|
|
49
|
+
total: filtered.length,
|
|
50
|
+
truncated: filtered.length > rawLimit,
|
|
51
|
+
cycles: limited.map((c) => ({
|
|
52
|
+
size: c.size,
|
|
53
|
+
paths: c.paths ?? c.nodeIds.map((id) => id.replace(/^file:/, '')),
|
|
54
|
+
})),
|
|
55
55
|
};
|
|
56
|
+
return { data: formatObjectArrays(data, input) };
|
|
56
57
|
},
|
|
57
58
|
};
|
|
@@ -1 +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;
|
|
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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { analyzeGraphImpact } from '@shrkcrft/impact-engine';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
export const getGraphImpactAnalysisTool = {
|
|
3
4
|
name: 'get_graph_impact_analysis',
|
|
4
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.',
|
|
@@ -11,6 +12,7 @@ export const getGraphImpactAnalysisTool = {
|
|
|
11
12
|
gitref: { type: 'string' },
|
|
12
13
|
maxDepth: { type: 'number' },
|
|
13
14
|
limit: { type: 'number' },
|
|
15
|
+
...FORMAT_INPUT_PROPERTY,
|
|
14
16
|
},
|
|
15
17
|
additionalProperties: false,
|
|
16
18
|
},
|
|
@@ -34,7 +36,7 @@ export const getGraphImpactAnalysisTool = {
|
|
|
34
36
|
limit: clamp(args.limit ?? 200, 1, 2000),
|
|
35
37
|
maxDepth: clamp(args.maxDepth ?? 5, 1, 10),
|
|
36
38
|
});
|
|
37
|
-
return { data: analysis };
|
|
39
|
+
return { data: formatObjectArrays(analysis, input) };
|
|
38
40
|
},
|
|
39
41
|
};
|
|
40
42
|
function clamp(n, lo, hi) {
|
|
@@ -1 +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;
|
|
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;AAWpE,eAAO,MAAM,kBAAkB,EAAE,eAmEhC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphQueryApi, GraphStore } from '@shrkcrft/graph';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
const NEXT = 'shrk graph index';
|
|
3
4
|
export const getGraphImpactTool = {
|
|
4
5
|
name: 'get_graph_impact',
|
|
@@ -10,6 +11,7 @@ export const getGraphImpactTool = {
|
|
|
10
11
|
target: { type: 'string' },
|
|
11
12
|
maxDepth: { type: 'number' },
|
|
12
13
|
limit: { type: 'number' },
|
|
14
|
+
...FORMAT_INPUT_PROPERTY,
|
|
13
15
|
},
|
|
14
16
|
required: ['target'],
|
|
15
17
|
additionalProperties: false,
|
|
@@ -51,20 +53,19 @@ export const getGraphImpactTool = {
|
|
|
51
53
|
const closure = reverseClosure(api, anchor.id, maxDepth, limit);
|
|
52
54
|
const direct = closure.layer[1] ?? [];
|
|
53
55
|
const transitive = closure.all.filter((id) => id !== anchor.id && !direct.includes(id));
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
totalReached: closure.all.length - 1,
|
|
66
|
-
},
|
|
56
|
+
const data = {
|
|
57
|
+
schema: 'sharkcraft.graph-impact/v1',
|
|
58
|
+
anchor: summarise(anchor),
|
|
59
|
+
maxDepth,
|
|
60
|
+
limit,
|
|
61
|
+
truncated: closure.truncated,
|
|
62
|
+
directDependents: direct.map((id) => summarise(api.neighbours(id).node)),
|
|
63
|
+
transitiveDependents: transitive
|
|
64
|
+
.slice(0, limit)
|
|
65
|
+
.map((id) => summarise(api.neighbours(id).node)),
|
|
66
|
+
totalReached: closure.all.length - 1,
|
|
67
67
|
};
|
|
68
|
+
return { data: formatObjectArrays(data, input) };
|
|
68
69
|
},
|
|
69
70
|
};
|
|
70
71
|
function resolveAnchor(api, target) {
|
|
@@ -1 +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;
|
|
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,eA6DhC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphQueryApi, GraphStore } from '@shrkcrft/graph';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
2
3
|
const NEXT = 'shrk graph index';
|
|
3
4
|
export const getGraphSearchTool = {
|
|
4
5
|
name: 'get_graph_search',
|
|
@@ -11,6 +12,7 @@ export const getGraphSearchTool = {
|
|
|
11
12
|
kind: { type: 'string', enum: ['file', 'symbol', 'package'] },
|
|
12
13
|
limit: { type: 'number' },
|
|
13
14
|
exact: { type: 'boolean' },
|
|
15
|
+
...FORMAT_INPUT_PROPERTY,
|
|
14
16
|
},
|
|
15
17
|
required: ['query'],
|
|
16
18
|
additionalProperties: false,
|
|
@@ -53,15 +55,14 @@ export const getGraphSearchTool = {
|
|
|
53
55
|
if (p)
|
|
54
56
|
matches.push(p.node);
|
|
55
57
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
matches: matches.slice(0, limit).map(summarise),
|
|
63
|
-
},
|
|
58
|
+
const data = {
|
|
59
|
+
schema: 'sharkcraft.graph-search/v1',
|
|
60
|
+
query,
|
|
61
|
+
kind: args.kind ?? 'any',
|
|
62
|
+
total: Math.min(matches.length, limit),
|
|
63
|
+
matches: matches.slice(0, limit).map(summarise),
|
|
64
64
|
};
|
|
65
|
+
return { data: formatObjectArrays(data, input) };
|
|
65
66
|
},
|
|
66
67
|
};
|
|
67
68
|
function summarise(n) {
|
|
@@ -1 +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;
|
|
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"}
|