@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.
- 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 +24 -9
- package/dist/server/fit-array-to-budget.d.ts +24 -0
- package/dist/server/fit-array-to-budget.d.ts.map +1 -0
- package/dist/server/fit-array-to-budget.js +60 -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 +72 -7
- 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 +16 -0
- package/dist/tools/code-find-usages.tool.d.ts.map +1 -0
- package/dist/tools/code-find-usages.tool.js +180 -0
- 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 +81 -0
- package/dist/tools/dashboard-summary.tool.d.ts.map +1 -1
- package/dist/tools/dashboard-summary.tool.js +2 -4
- package/dist/tools/delegate-task.tool.d.ts +3 -0
- package/dist/tools/delegate-task.tool.d.ts.map +1 -0
- package/dist/tools/delegate-task.tool.js +94 -0
- package/dist/tools/deps-audit.tool.d.ts +10 -0
- package/dist/tools/deps-audit.tool.d.ts.map +1 -0
- package/dist/tools/deps-audit.tool.js +251 -0
- package/dist/tools/diff-check.tool.d.ts +15 -0
- package/dist/tools/diff-check.tool.d.ts.map +1 -0
- package/dist/tools/diff-check.tool.js +157 -0
- package/dist/tools/file-advice.tool.d.ts +22 -0
- package/dist/tools/file-advice.tool.d.ts.map +1 -0
- package/dist/tools/file-advice.tool.js +88 -0
- package/dist/tools/get-api-surface-diff.tool.d.ts +3 -0
- package/dist/tools/get-api-surface-diff.tool.d.ts.map +1 -0
- package/dist/tools/get-api-surface-diff.tool.js +60 -0
- package/dist/tools/get-arch-violations.tool.d.ts +3 -0
- package/dist/tools/get-arch-violations.tool.d.ts.map +1 -0
- package/dist/tools/get-arch-violations.tool.js +30 -0
- package/dist/tools/get-code-intelligence-state.tool.d.ts +11 -0
- package/dist/tools/get-code-intelligence-state.tool.d.ts.map +1 -0
- package/dist/tools/get-code-intelligence-state.tool.js +60 -0
- package/dist/tools/get-context-pack.tool.d.ts +3 -0
- package/dist/tools/get-context-pack.tool.d.ts.map +1 -0
- package/dist/tools/get-context-pack.tool.js +40 -0
- package/dist/tools/get-framework-entities.tool.d.ts +3 -0
- package/dist/tools/get-framework-entities.tool.d.ts.map +1 -0
- package/dist/tools/get-framework-entities.tool.js +68 -0
- package/dist/tools/get-graph-callers.tool.d.ts +3 -0
- package/dist/tools/get-graph-callers.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-callers.tool.js +94 -0
- package/dist/tools/get-graph-context.tool.d.ts +3 -0
- package/dist/tools/get-graph-context.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-context.tool.js +125 -0
- package/dist/tools/get-graph-cycles.tool.d.ts +10 -0
- package/dist/tools/get-graph-cycles.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-cycles.tool.js +58 -0
- package/dist/tools/get-graph-deps.tool.d.ts +12 -0
- package/dist/tools/get-graph-deps.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-deps.tool.js +80 -0
- package/dist/tools/get-graph-hubs.tool.d.ts +3 -0
- package/dist/tools/get-graph-hubs.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-hubs.tool.js +61 -0
- package/dist/tools/get-graph-impact-analysis.tool.d.ts +3 -0
- package/dist/tools/get-graph-impact-analysis.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-impact-analysis.tool.js +44 -0
- package/dist/tools/get-graph-impact.tool.d.ts +3 -0
- package/dist/tools/get-graph-impact.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-impact.tool.js +150 -0
- package/dist/tools/get-graph-path.tool.d.ts +3 -0
- package/dist/tools/get-graph-path.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-path.tool.js +144 -0
- package/dist/tools/get-graph-search.tool.d.ts +3 -0
- package/dist/tools/get-graph-search.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-search.tool.js +95 -0
- package/dist/tools/get-graph-status.tool.d.ts +11 -0
- package/dist/tools/get-graph-status.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-status.tool.js +55 -0
- package/dist/tools/get-graph-unresolved.tool.d.ts +11 -0
- package/dist/tools/get-graph-unresolved.tool.d.ts.map +1 -0
- package/dist/tools/get-graph-unresolved.tool.js +85 -0
- package/dist/tools/get-impact-baseline.tool.d.ts +9 -0
- package/dist/tools/get-impact-baseline.tool.d.ts.map +1 -0
- package/dist/tools/get-impact-baseline.tool.js +65 -0
- package/dist/tools/get-intent-benchmark-run.tool.d.ts +12 -0
- package/dist/tools/get-intent-benchmark-run.tool.d.ts.map +1 -0
- package/dist/tools/get-intent-benchmark-run.tool.js +55 -0
- 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-migrations.tool.d.ts +3 -0
- package/dist/tools/get-migrations.tool.d.ts.map +1 -0
- package/dist/tools/get-migrations.tool.js +70 -0
- package/dist/tools/get-pattern-registry.tool.d.ts +8 -0
- package/dist/tools/get-pattern-registry.tool.d.ts.map +1 -0
- package/dist/tools/get-pattern-registry.tool.js +40 -0
- package/dist/tools/get-quality-gate.tool.d.ts +3 -0
- package/dist/tools/get-quality-gate.tool.d.ts.map +1 -0
- package/dist/tools/get-quality-gate.tool.js +27 -0
- 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-rules-for-file.tool.d.ts +3 -0
- package/dist/tools/get-rules-for-file.tool.d.ts.map +1 -0
- package/dist/tools/get-rules-for-file.tool.js +54 -0
- package/dist/tools/get-structural-rewrite-plan.tool.d.ts +3 -0
- package/dist/tools/get-structural-rewrite-plan.tool.d.ts.map +1 -0
- package/dist/tools/get-structural-rewrite-plan.tool.js +46 -0
- package/dist/tools/get-structural-search.tool.d.ts +3 -0
- package/dist/tools/get-structural-search.tool.d.ts.map +1 -0
- package/dist/tools/get-structural-search.tool.js +35 -0
- 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/graph-staleness.d.ts +34 -0
- package/dist/tools/graph-staleness.d.ts.map +1 -0
- package/dist/tools/graph-staleness.js +36 -0
- 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/plan-quality-review.tool.d.ts +21 -0
- package/dist/tools/plan-quality-review.tool.d.ts.map +1 -0
- package/dist/tools/plan-quality-review.tool.js +294 -0
- package/dist/tools/primary-tools.d.ts +24 -0
- package/dist/tools/primary-tools.d.ts.map +1 -0
- package/dist/tools/primary-tools.js +86 -0
- package/dist/tools/r19-extras.tool.js +1 -1
- package/dist/tools/r32-profiles.tool.d.ts +0 -3
- package/dist/tools/r32-profiles.tool.d.ts.map +1 -1
- package/dist/tools/r32-profiles.tool.js +3 -54
- 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 +17 -0
- package/dist/tools/smart-context-bundle.tool.d.ts.map +1 -0
- package/dist/tools/smart-context-bundle.tool.js +110 -0
- package/dist/tools/smart-context-feed.tool.d.ts +17 -0
- package/dist/tools/smart-context-feed.tool.d.ts.map +1 -0
- package/dist/tools/smart-context-feed.tool.js +138 -0
- package/dist/tools/start-here.tool.js +2 -2
- package/package.json +28 -16
- package/dist/tools/r22-extras.tool.d.ts +0 -4
- package/dist/tools/r22-extras.tool.d.ts.map +0 -1
- package/dist/tools/r22-extras.tool.js +0 -42
- package/dist/tools/r26-ingest.tool.d.ts +0 -10
- package/dist/tools/r26-ingest.tool.d.ts.map +0 -1
- package/dist/tools/r26-ingest.tool.js +0 -174
- package/dist/tools/r28-plugin-lifecycle.tool.d.ts +0 -4
- package/dist/tools/r28-plugin-lifecycle.tool.d.ts.map +0 -1
- package/dist/tools/r28-plugin-lifecycle.tool.js +0 -94
- package/dist/tools/r34-search-unified.tool.d.ts +0 -3
- package/dist/tools/r34-search-unified.tool.d.ts.map +0 -1
- package/dist/tools/r34-search-unified.tool.js +0 -38
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { buildCodeIntelligenceChecks, DoctorSeverity, } from '@shrkcrft/inspector';
|
|
2
|
+
import { FORMAT_INPUT_PROPERTY, formatObjectArrays } from "../server/columnar-format.js";
|
|
3
|
+
/**
|
|
4
|
+
* Read-only MCP mirror of `shrk code-intel`. Returns the same 14
|
|
5
|
+
* code-intelligence doctor findings in one shot — agents can pull
|
|
6
|
+
* the entire state without iterating `shrk doctor`'s full check list.
|
|
7
|
+
*
|
|
8
|
+
* Stable output schema (`sharkcraft.code-intelligence-state/v1`) so
|
|
9
|
+
* downstream renderers (dashboard, CI bots) can rely on the shape.
|
|
10
|
+
*/
|
|
11
|
+
export const getCodeIntelligenceStateTool = {
|
|
12
|
+
name: 'get_code_intelligence_state',
|
|
13
|
+
description: 'Return all code-intelligence doctor findings (graph, rule-graph, api-surface, quality-gate, migrations, architecture, impact, framework, structural-search, context-planner) in one payload. Read-only.',
|
|
14
|
+
cliCommand: 'code-intel',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
only: { type: 'array', items: { type: 'string' } },
|
|
19
|
+
checkId: { type: 'string' },
|
|
20
|
+
...FORMAT_INPUT_PROPERTY,
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
},
|
|
24
|
+
handler(input, ctx) {
|
|
25
|
+
const args = input;
|
|
26
|
+
let checks = buildCodeIntelligenceChecks(ctx.inspection.projectRoot);
|
|
27
|
+
if (args.checkId) {
|
|
28
|
+
checks = checks.filter((c) => c.id === args.checkId);
|
|
29
|
+
}
|
|
30
|
+
if (args.only && args.only.length > 0) {
|
|
31
|
+
const allowed = new Set(args.only.map((s) => s.toLowerCase()));
|
|
32
|
+
checks = checks.filter((c) => allowed.has(c.severity));
|
|
33
|
+
}
|
|
34
|
+
const summary = summarize(checks);
|
|
35
|
+
const data = {
|
|
36
|
+
schema: 'sharkcraft.code-intelligence-state/v1',
|
|
37
|
+
totalChecks: checks.length,
|
|
38
|
+
summary,
|
|
39
|
+
checks,
|
|
40
|
+
};
|
|
41
|
+
return { data: formatObjectArrays(data, input) };
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function summarize(checks) {
|
|
45
|
+
let ok = 0;
|
|
46
|
+
let info = 0;
|
|
47
|
+
let warnings = 0;
|
|
48
|
+
let errors = 0;
|
|
49
|
+
for (const c of checks) {
|
|
50
|
+
if (c.severity === DoctorSeverity.Ok)
|
|
51
|
+
ok += 1;
|
|
52
|
+
else if (c.severity === DoctorSeverity.Info)
|
|
53
|
+
info += 1;
|
|
54
|
+
else if (c.severity === DoctorSeverity.Warning)
|
|
55
|
+
warnings += 1;
|
|
56
|
+
else if (c.severity === DoctorSeverity.Error)
|
|
57
|
+
errors += 1;
|
|
58
|
+
}
|
|
59
|
+
return { ok, info, warnings, errors };
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-context-pack.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-context-pack.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAUpE,eAAO,MAAM,kBAAkB,EAAE,eAoChC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { planContext } from '@shrkcrft/context-planner';
|
|
2
|
+
export const getContextPackTool = {
|
|
3
|
+
name: 'get_context_pack',
|
|
4
|
+
description: 'Produce a deterministic, token-budgeted context pack (`sharkcraft.context-pack/v1`) for an AI coding agent: ranked relevant files, applicable rules, paths, templates, likely tests, surfaced risks, do-not-touch zones. Read-only.',
|
|
5
|
+
cliCommand: 'plan-context',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
task: { type: 'string' },
|
|
10
|
+
budgetTokens: { type: 'number' },
|
|
11
|
+
hintedFiles: { type: 'array', items: { type: 'string' } },
|
|
12
|
+
hintedPackages: { type: 'array', items: { type: 'string' } },
|
|
13
|
+
maxFiles: { type: 'number' },
|
|
14
|
+
},
|
|
15
|
+
required: ['task'],
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
handler(input, ctx) {
|
|
19
|
+
const args = input;
|
|
20
|
+
const task = (args.task ?? '').trim();
|
|
21
|
+
if (!task) {
|
|
22
|
+
return {
|
|
23
|
+
isError: true,
|
|
24
|
+
error: { code: 'invalid-input', message: 'task is required' },
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const pack = planContext({
|
|
28
|
+
projectRoot: ctx.inspection.projectRoot,
|
|
29
|
+
task,
|
|
30
|
+
budgetTokens: clamp(args.budgetTokens ?? 8000, 500, 64000),
|
|
31
|
+
maxFiles: clamp(args.maxFiles ?? 30, 1, 200),
|
|
32
|
+
...(args.hintedFiles ? { hintedFiles: args.hintedFiles } : {}),
|
|
33
|
+
...(args.hintedPackages ? { hintedPackages: args.hintedPackages } : {}),
|
|
34
|
+
});
|
|
35
|
+
return { data: pack };
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
function clamp(n, lo, hi) {
|
|
39
|
+
return Math.max(lo, Math.min(hi, n));
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-framework-entities.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-framework-entities.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAYpE,eAAO,MAAM,wBAAwB,EAAE,eA+DtC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { FrameworkQueryApi } from '@shrkcrft/framework-scanners';
|
|
2
|
+
const NEXT = 'shrk framework index';
|
|
3
|
+
export const getFrameworkEntitiesTool = {
|
|
4
|
+
name: 'get_framework_entities',
|
|
5
|
+
description: 'Read-only: list framework entities discovered by the extractors (NestJS controllers/modules/providers/routes, React components/hook usages). Filters: framework, subtype, file. Pass `routes: true` for the NestJS route table.',
|
|
6
|
+
cliCommand: 'framework list',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
framework: { type: 'string' },
|
|
11
|
+
subtype: { type: 'string' },
|
|
12
|
+
file: { type: 'string' },
|
|
13
|
+
limit: { type: 'number' },
|
|
14
|
+
routes: { type: 'boolean' },
|
|
15
|
+
},
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
handler(input, ctx) {
|
|
19
|
+
const args = input;
|
|
20
|
+
const missing = FrameworkQueryApi.missingDescription(ctx.inspection.projectRoot);
|
|
21
|
+
if (missing) {
|
|
22
|
+
return {
|
|
23
|
+
isError: true,
|
|
24
|
+
error: { code: 'framework-missing', message: missing, details: { nextCommand: NEXT } },
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const api = FrameworkQueryApi.fromStore(ctx.inspection.projectRoot);
|
|
28
|
+
if (args.routes) {
|
|
29
|
+
const routes = api.routes();
|
|
30
|
+
return {
|
|
31
|
+
data: {
|
|
32
|
+
schema: 'sharkcraft.framework-routes/v1',
|
|
33
|
+
total: routes.length,
|
|
34
|
+
routes,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const limit = clamp(args.limit ?? 100, 1, 2000);
|
|
39
|
+
const entities = api.list({
|
|
40
|
+
...(args.framework ? { framework: args.framework } : {}),
|
|
41
|
+
...(args.subtype ? { subtype: args.subtype } : {}),
|
|
42
|
+
...(args.file ? { file: args.file } : {}),
|
|
43
|
+
limit,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
data: {
|
|
47
|
+
schema: 'sharkcraft.framework-list/v1',
|
|
48
|
+
filters: {
|
|
49
|
+
framework: args.framework ?? null,
|
|
50
|
+
subtype: args.subtype ?? null,
|
|
51
|
+
file: args.file ?? null,
|
|
52
|
+
},
|
|
53
|
+
total: entities.length,
|
|
54
|
+
entities: entities.map((n) => ({
|
|
55
|
+
id: n.id,
|
|
56
|
+
label: n.label,
|
|
57
|
+
path: n.path,
|
|
58
|
+
framework: n.data?.['framework'] ?? null,
|
|
59
|
+
subtype: n.data?.['subtype'] ?? null,
|
|
60
|
+
data: n.data,
|
|
61
|
+
})),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
function clamp(n, lo, hi) {
|
|
67
|
+
return Math.max(lo, Math.min(hi, n));
|
|
68
|
+
}
|
|
@@ -0,0 +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;AAWpE,eAAO,MAAM,mBAAmB,EAAE,eAsEjC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { GraphQueryApi, GraphStore, loadGraphApiCached } 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 getGraphCallersTool = {
|
|
6
|
+
name: 'get_graph_callers',
|
|
7
|
+
description: 'Find who calls/references a symbol (use this instead of grep before changing a function/type). Returns each caller as path:line of the first call site. Mode "call" → calls-symbol edges; mode "reference" → both references-symbol and calls-symbol. Read-only; needs `shrk graph index`.',
|
|
8
|
+
cliCommand: 'graph callers',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
symbol: { type: 'string' },
|
|
13
|
+
mode: { type: 'string', enum: ['call', 'reference'] },
|
|
14
|
+
...FORMAT_INPUT_PROPERTY,
|
|
15
|
+
},
|
|
16
|
+
required: ['symbol'],
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
},
|
|
19
|
+
handler(input, ctx) {
|
|
20
|
+
const args = input;
|
|
21
|
+
const target = (args.symbol ?? '').trim();
|
|
22
|
+
if (!target) {
|
|
23
|
+
return {
|
|
24
|
+
isError: true,
|
|
25
|
+
error: { code: 'invalid-input', message: 'symbol is required' },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const mode = args.mode ?? 'call';
|
|
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 api = loadGraphApiCached(ctx.inspection.projectRoot) ?? GraphQueryApi.fromStore(ctx.inspection.projectRoot);
|
|
41
|
+
const sym = resolveSymbol(api, target);
|
|
42
|
+
if (!sym) {
|
|
43
|
+
return {
|
|
44
|
+
isError: true,
|
|
45
|
+
error: {
|
|
46
|
+
code: 'not-found',
|
|
47
|
+
message: `No symbol matched "${target}".`,
|
|
48
|
+
details: { target },
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const cwd = ctx.inspection.projectRoot;
|
|
53
|
+
const sites = mode === 'reference' ? api.referenceSitesOf(sym.id) : api.callerSitesOf(sym.id);
|
|
54
|
+
// Targeted staleness over the result files: drop callers whose file was
|
|
55
|
+
// deleted on disk, flag those whose content changed since indexing — so a
|
|
56
|
+
// stale index never silently serves a wrong/dead caller. Read-only.
|
|
57
|
+
const fresh = graphResultStaleness(api, cwd, [sym.path, ...sites.map((s) => s.node.path)]);
|
|
58
|
+
const live = sites.filter((s) => !s.node.path || !fresh.deletedSet.has(s.node.path));
|
|
59
|
+
const note = callGraphLanguageNote(api, sym);
|
|
60
|
+
const data = {
|
|
61
|
+
schema: 'sharkcraft.graph-callers/v1',
|
|
62
|
+
symbol: summarise(sym),
|
|
63
|
+
mode,
|
|
64
|
+
total: live.length,
|
|
65
|
+
callers: live.slice(0, 200).map((s) => ({
|
|
66
|
+
...summarise(s.node),
|
|
67
|
+
...(s.line ? { line: s.line } : {}),
|
|
68
|
+
})),
|
|
69
|
+
...(note ? { note } : {}),
|
|
70
|
+
...(fresh.field ?? {}),
|
|
71
|
+
};
|
|
72
|
+
return { data: formatObjectArrays(data, input) };
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
function resolveSymbol(api, target) {
|
|
76
|
+
if (target.startsWith('symbol:'))
|
|
77
|
+
return api.neighbours(target)?.node;
|
|
78
|
+
const syms = api.findSymbol(target, { exact: true, limit: 5 });
|
|
79
|
+
if (syms.length === 0)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (syms.length === 1)
|
|
82
|
+
return syms[0];
|
|
83
|
+
const exported = syms.find((s) => (s.data?.['isExported'] ?? false) === true);
|
|
84
|
+
return exported ?? syms[0];
|
|
85
|
+
}
|
|
86
|
+
function summarise(n) {
|
|
87
|
+
return {
|
|
88
|
+
id: n.id,
|
|
89
|
+
kind: n.kind,
|
|
90
|
+
label: n.label,
|
|
91
|
+
...(n.path ? { path: n.path } : {}),
|
|
92
|
+
...(n.line ? { line: n.line } : {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-graph-context.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-context.tool.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAUpE,eAAO,MAAM,mBAAmB,EAAE,eAsGjC,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { GraphQueryApi, GraphStore, NodeKind, 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 getGraphContextTool = {
|
|
6
|
+
name: 'get_graph_context',
|
|
7
|
+
description: 'Return graph context for a file or symbol: declared symbols, files this imports, files that import it. Read-only.',
|
|
8
|
+
cliCommand: 'graph context',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: { target: { type: 'string' }, ...FORMAT_INPUT_PROPERTY },
|
|
12
|
+
required: ['target'],
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
},
|
|
15
|
+
handler(input, ctx) {
|
|
16
|
+
const args = input;
|
|
17
|
+
const target = (args.target ?? '').trim();
|
|
18
|
+
if (!target) {
|
|
19
|
+
return {
|
|
20
|
+
isError: true,
|
|
21
|
+
error: { code: 'invalid-input', message: 'target is required' },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const store = new GraphStore(ctx.inspection.projectRoot);
|
|
25
|
+
if (!store.exists()) {
|
|
26
|
+
return {
|
|
27
|
+
isError: true,
|
|
28
|
+
error: {
|
|
29
|
+
code: 'graph-missing',
|
|
30
|
+
message: `Code-intelligence index is missing. Run '${NEXT}'.`,
|
|
31
|
+
details: { nextCommand: NEXT },
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const api = loadGraphApiCached(ctx.inspection.projectRoot) ?? GraphQueryApi.fromStore(ctx.inspection.projectRoot);
|
|
36
|
+
const anchor = resolveAnchor(api, target);
|
|
37
|
+
if (!anchor) {
|
|
38
|
+
return {
|
|
39
|
+
isError: true,
|
|
40
|
+
error: {
|
|
41
|
+
code: 'not-found',
|
|
42
|
+
message: `No graph node matched "${target}".`,
|
|
43
|
+
details: { target },
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// A SYMBOL has no imports-file edges (those are file→file), so its import
|
|
48
|
+
// context is the DECLARING FILE's imports — compute neighbours on that file
|
|
49
|
+
// (mirrors the CLI; otherwise importsFrom/importedBy are wrongly empty).
|
|
50
|
+
const declaringFile = anchor.kind === NodeKind.Symbol
|
|
51
|
+
? api.declaringFileOf(anchor.id) ?? (anchor.path ? api.findFile(anchor.path) : undefined)
|
|
52
|
+
: undefined;
|
|
53
|
+
const subjectId = anchor.kind === NodeKind.File ? anchor.id : declaringFile?.id ?? anchor.id;
|
|
54
|
+
const neighbours = api.neighbours(subjectId);
|
|
55
|
+
const symbols = anchor.kind === NodeKind.File ? api.symbolsIn(anchor.id) : [];
|
|
56
|
+
// Who uses this symbol — references + calls (the CLI provides these; the MCP
|
|
57
|
+
// previously omitted them, returning a confidently-wrong "nothing uses this").
|
|
58
|
+
const referencedBy = anchor.kind === NodeKind.Symbol ? api.referencesOf(anchor.id) : [];
|
|
59
|
+
const calledBy = anchor.kind === NodeKind.Symbol ? api.callersOf(anchor.id) : [];
|
|
60
|
+
// Typed subtype/supertype edges (extends / implements) — the precise
|
|
61
|
+
// "who implements this interface" answer for a symbol anchor.
|
|
62
|
+
const subtypes = anchor.kind === NodeKind.Symbol ? api.subtypesOf(anchor.id) : [];
|
|
63
|
+
const supertypes = anchor.kind === NodeKind.Symbol ? api.supertypesOf(anchor.id) : [];
|
|
64
|
+
const importsFrom = neighbours.out
|
|
65
|
+
.filter((o) => o.edge.kind === 'imports-file')
|
|
66
|
+
.slice(0, 50)
|
|
67
|
+
.map((o) => 'resolved' in o.target
|
|
68
|
+
? { id: o.target.id, resolved: false }
|
|
69
|
+
: { ...summarise(o.target), resolved: true });
|
|
70
|
+
const importedBy = neighbours.in
|
|
71
|
+
.filter((i) => i.edge.kind === 'imports-file')
|
|
72
|
+
.slice(0, 50)
|
|
73
|
+
.map((i) => 'resolved' in i.source
|
|
74
|
+
? { id: i.source.id, resolved: false }
|
|
75
|
+
: { ...summarise(i.source), resolved: true });
|
|
76
|
+
const referencedByRows = referencedBy.slice(0, 50).map(summarise);
|
|
77
|
+
const calledByRows = calledBy.slice(0, 50).map(summarise);
|
|
78
|
+
// Drop imports/refs to/from files deleted on disk; flag the rest if changed.
|
|
79
|
+
const fresh = graphResultStaleness(api, ctx.inspection.projectRoot, [
|
|
80
|
+
anchor.path,
|
|
81
|
+
...importsFrom.map((x) => ('path' in x ? x.path : undefined)),
|
|
82
|
+
...importedBy.map((x) => ('path' in x ? x.path : undefined)),
|
|
83
|
+
...referencedByRows.map((x) => x.path),
|
|
84
|
+
...calledByRows.map((x) => x.path),
|
|
85
|
+
]);
|
|
86
|
+
const data = {
|
|
87
|
+
schema: 'sharkcraft.graph-context/v1',
|
|
88
|
+
anchor: summarise(anchor),
|
|
89
|
+
...(declaringFile ? { declaredIn: summarise(declaringFile) } : {}),
|
|
90
|
+
importsFrom: dropDeleted(importsFrom, fresh.deletedSet),
|
|
91
|
+
importedBy: dropDeleted(importedBy, fresh.deletedSet),
|
|
92
|
+
symbols: symbols.slice(0, 50).map(summarise),
|
|
93
|
+
...(referencedByRows.length > 0 ? { referencedBy: dropDeleted(referencedByRows, fresh.deletedSet) } : {}),
|
|
94
|
+
...(calledByRows.length > 0 ? { calledBy: dropDeleted(calledByRows, fresh.deletedSet) } : {}),
|
|
95
|
+
...(subtypes.length > 0 ? { subtypes: subtypes.slice(0, 50).map(summarise) } : {}),
|
|
96
|
+
...(supertypes.length > 0 ? { supertypes: supertypes.slice(0, 50).map(summarise) } : {}),
|
|
97
|
+
...(fresh.field ?? {}),
|
|
98
|
+
};
|
|
99
|
+
return { data: formatObjectArrays(data, input) };
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
function resolveAnchor(api, target) {
|
|
103
|
+
const direct = api.neighbours(target);
|
|
104
|
+
if (direct)
|
|
105
|
+
return direct.node;
|
|
106
|
+
if (target.startsWith('file:') || target.startsWith('symbol:') || target.startsWith('package:')) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const f = api.findFile(target);
|
|
110
|
+
if (f)
|
|
111
|
+
return f;
|
|
112
|
+
const syms = api.findSymbol(target, { exact: true, limit: 1 });
|
|
113
|
+
if (syms.length > 0)
|
|
114
|
+
return syms[0];
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
function summarise(n) {
|
|
118
|
+
return {
|
|
119
|
+
id: n.id,
|
|
120
|
+
kind: n.kind,
|
|
121
|
+
label: n.label,
|
|
122
|
+
...(n.path ? { path: n.path } : {}),
|
|
123
|
+
...(n.line ? { line: n.line } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IToolDefinition } from '../server/tool-definition.js';
|
|
2
|
+
/**
|
|
3
|
+
* Read-only MCP mirror of `shrk graph cycles`. Returns the full SCC
|
|
4
|
+
* list (sorted by size desc) so agents can answer "show me every
|
|
5
|
+
* import cycle in this repo" in a single tool call. Mirrors the safety
|
|
6
|
+
* contract: read-only, structured error with nextCommand when state
|
|
7
|
+
* is missing.
|
|
8
|
+
*/
|
|
9
|
+
export declare const getGraphCyclesTool: IToolDefinition;
|
|
10
|
+
//# sourceMappingURL=get-graph-cycles.tool.d.ts.map
|
|
@@ -0,0 +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;AAYpE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAmDhC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Read-only MCP mirror of `shrk graph cycles`. Returns the full SCC
|
|
6
|
+
* list (sorted by size desc) so agents can answer "show me every
|
|
7
|
+
* import cycle in this repo" in a single tool call. Mirrors the safety
|
|
8
|
+
* contract: read-only, structured error with nextCommand when state
|
|
9
|
+
* is missing.
|
|
10
|
+
*/
|
|
11
|
+
export const getGraphCyclesTool = {
|
|
12
|
+
name: 'get_graph_cycles',
|
|
13
|
+
description: 'Return every import cycle in the code graph (strongly-connected components of size ≥ 2 over `imports-file` edges). Sorted by size desc. Read-only.',
|
|
14
|
+
cliCommand: 'graph cycles',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
limit: { type: 'number' },
|
|
19
|
+
minSize: { type: 'number' },
|
|
20
|
+
...FORMAT_INPUT_PROPERTY,
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
},
|
|
24
|
+
handler(input, ctx) {
|
|
25
|
+
const args = input;
|
|
26
|
+
const rawLimit = typeof args.limit === 'number' && Number.isFinite(args.limit) && args.limit > 0
|
|
27
|
+
? args.limit
|
|
28
|
+
: 50;
|
|
29
|
+
const minSize = typeof args.minSize === 'number' && Number.isFinite(args.minSize) && args.minSize >= 2
|
|
30
|
+
? args.minSize
|
|
31
|
+
: 2;
|
|
32
|
+
const store = new GraphStore(ctx.inspection.projectRoot);
|
|
33
|
+
if (!store.exists()) {
|
|
34
|
+
return {
|
|
35
|
+
isError: true,
|
|
36
|
+
error: {
|
|
37
|
+
code: 'graph-missing',
|
|
38
|
+
message: `Code-intelligence index is missing. Run '${NEXT}'.`,
|
|
39
|
+
details: { nextCommand: NEXT },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const api = loadGraphApiCached(ctx.inspection.projectRoot) ?? GraphQueryApi.fromStore(ctx.inspection.projectRoot);
|
|
44
|
+
const all = api.cycles();
|
|
45
|
+
const filtered = all.filter((c) => c.size >= minSize);
|
|
46
|
+
const limited = filtered.slice(0, rawLimit);
|
|
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
|
+
};
|
|
56
|
+
return { data: formatObjectArrays(data, input) };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IToolDefinition } from '../server/tool-definition.js';
|
|
2
|
+
/**
|
|
3
|
+
* Read-only MCP mirror of `shrk graph deps`. Returns the workspace
|
|
4
|
+
* package's outbound (`depends on`) and inbound (`depended on by`)
|
|
5
|
+
* `package-depends-on` edges from the persisted code graph.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors the safety contract: structured `graph-missing` error when
|
|
8
|
+
* the index isn't built; `not-found` when no `package:<name>` node
|
|
9
|
+
* exists.
|
|
10
|
+
*/
|
|
11
|
+
export declare const getGraphDepsTool: IToolDefinition;
|
|
12
|
+
//# sourceMappingURL=get-graph-deps.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-graph-deps.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-deps.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AASpE;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAmE9B,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { EdgeKind, GraphQueryApi, GraphStore, loadGraphApiCached } from '@shrkcrft/graph';
|
|
2
|
+
const NEXT = 'shrk graph index';
|
|
3
|
+
/**
|
|
4
|
+
* Read-only MCP mirror of `shrk graph deps`. Returns the workspace
|
|
5
|
+
* package's outbound (`depends on`) and inbound (`depended on by`)
|
|
6
|
+
* `package-depends-on` edges from the persisted code graph.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the safety contract: structured `graph-missing` error when
|
|
9
|
+
* the index isn't built; `not-found` when no `package:<name>` node
|
|
10
|
+
* exists.
|
|
11
|
+
*/
|
|
12
|
+
export const getGraphDepsTool = {
|
|
13
|
+
name: 'get_graph_deps',
|
|
14
|
+
description: 'Return inbound + outbound `package-depends-on` edges for a workspace package. Read-only.',
|
|
15
|
+
cliCommand: 'graph deps',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
package: { type: 'string' },
|
|
20
|
+
},
|
|
21
|
+
required: ['package'],
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
},
|
|
24
|
+
handler(input, ctx) {
|
|
25
|
+
const args = input;
|
|
26
|
+
const target = (args.package ?? '').trim();
|
|
27
|
+
if (!target) {
|
|
28
|
+
return {
|
|
29
|
+
isError: true,
|
|
30
|
+
error: { code: 'invalid-input', message: 'package is required' },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const store = new GraphStore(ctx.inspection.projectRoot);
|
|
34
|
+
if (!store.exists()) {
|
|
35
|
+
return {
|
|
36
|
+
isError: true,
|
|
37
|
+
error: {
|
|
38
|
+
code: 'graph-missing',
|
|
39
|
+
message: `Code-intelligence index is missing. Run '${NEXT}'.`,
|
|
40
|
+
details: { nextCommand: NEXT },
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const api = loadGraphApiCached(ctx.inspection.projectRoot) ?? GraphQueryApi.fromStore(ctx.inspection.projectRoot);
|
|
45
|
+
const pkgId = `package:${target}`;
|
|
46
|
+
const pkgNode = api.neighbours(pkgId)?.node;
|
|
47
|
+
if (!pkgNode) {
|
|
48
|
+
return {
|
|
49
|
+
isError: true,
|
|
50
|
+
error: {
|
|
51
|
+
code: 'not-found',
|
|
52
|
+
message: `No workspace package node "${target}" in the graph.`,
|
|
53
|
+
details: { target },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const dependsOn = api
|
|
58
|
+
.packageDeps(target)
|
|
59
|
+
.map((n) => n.id.replace(/^package:/, ''))
|
|
60
|
+
.sort();
|
|
61
|
+
const dependedOnBy = [];
|
|
62
|
+
for (const p of api.allPackages()) {
|
|
63
|
+
const name = p.id.replace(/^package:/, '');
|
|
64
|
+
if (name === target)
|
|
65
|
+
continue;
|
|
66
|
+
if (api.packageDeps(name).some((n) => n.id === pkgId))
|
|
67
|
+
dependedOnBy.push(name);
|
|
68
|
+
}
|
|
69
|
+
dependedOnBy.sort();
|
|
70
|
+
void EdgeKind;
|
|
71
|
+
return {
|
|
72
|
+
data: {
|
|
73
|
+
schema: 'sharkcraft.graph-deps/v1',
|
|
74
|
+
package: target,
|
|
75
|
+
dependsOn,
|
|
76
|
+
dependedOnBy,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-graph-hubs.tool.d.ts","sourceRoot":"","sources":["../../src/tools/get-graph-hubs.tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAWpE,eAAO,MAAM,gBAAgB,EAAE,eA4C9B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 getGraphHubsTool = {
|
|
5
|
+
name: 'get_graph_hubs',
|
|
6
|
+
description: 'The most-depended-on code: symbols ranked by how many DISTINCT files reference them, files by how many import them. The "load-bearing code" to change most carefully (biggest blast radius) and understand first when onboarding — the companion to get_graph_impact. Pass `path` (e.g. "packages/foo") to scope to one subsystem. Read-only; needs `shrk graph index`.',
|
|
7
|
+
cliCommand: 'graph hubs',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
limit: { type: 'number' },
|
|
12
|
+
path: { type: 'string' },
|
|
13
|
+
...FORMAT_INPUT_PROPERTY,
|
|
14
|
+
},
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
},
|
|
17
|
+
handler(input, ctx) {
|
|
18
|
+
const args = input;
|
|
19
|
+
const limit = clampLimit(args.limit);
|
|
20
|
+
const projectRoot = ctx.inspection.projectRoot;
|
|
21
|
+
const store = new GraphStore(projectRoot);
|
|
22
|
+
if (!store.exists()) {
|
|
23
|
+
return {
|
|
24
|
+
isError: true,
|
|
25
|
+
error: {
|
|
26
|
+
code: 'graph-missing',
|
|
27
|
+
message: `Code-intelligence index is missing. Run '${NEXT}'.`,
|
|
28
|
+
details: { nextCommand: NEXT },
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const api = loadGraphApiCached(projectRoot) ?? GraphQueryApi.fromStore(projectRoot);
|
|
33
|
+
const pathScope = typeof args.path === 'string' && args.path.trim().length > 0 ? args.path.trim() : undefined;
|
|
34
|
+
const hubs = api.topHubs(limit, pathScope);
|
|
35
|
+
const row = (h) => ({
|
|
36
|
+
...summarise(h.node),
|
|
37
|
+
inDegree: h.inDegree,
|
|
38
|
+
});
|
|
39
|
+
const data = {
|
|
40
|
+
schema: 'sharkcraft.graph-hubs/v1',
|
|
41
|
+
...(pathScope ? { path: pathScope } : {}),
|
|
42
|
+
symbols: hubs.symbols.map(row),
|
|
43
|
+
files: hubs.files.map(row),
|
|
44
|
+
};
|
|
45
|
+
return { data: formatObjectArrays(data, input) };
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
function clampLimit(raw) {
|
|
49
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw))
|
|
50
|
+
return 15;
|
|
51
|
+
return Math.max(1, Math.min(100, Math.floor(raw)));
|
|
52
|
+
}
|
|
53
|
+
function summarise(n) {
|
|
54
|
+
return {
|
|
55
|
+
id: n.id,
|
|
56
|
+
kind: n.kind,
|
|
57
|
+
label: n.label,
|
|
58
|
+
...(n.path ? { path: n.path } : {}),
|
|
59
|
+
...(n.line ? { line: n.line } : {}),
|
|
60
|
+
};
|
|
61
|
+
}
|