@shrkcrft/mcp-server 0.1.0-alpha.7 → 0.1.0-alpha.8
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/server/create-mcp-server.d.ts.map +1 -1
- package/dist/server/create-mcp-server.js +19 -7
- package/dist/tools/all-tools.d.ts.map +1 -1
- package/dist/tools/all-tools.js +5 -7
- 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/primary-tools.d.ts +24 -0
- package/dist/tools/primary-tools.d.ts.map +1 -0
- package/dist/tools/primary-tools.js +70 -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/package.json +15 -15
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-mcp-server.d.ts","sourceRoot":"","sources":["../../src/server/create-mcp-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAQnE,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,eAAe,EAEf,eAAe,EAChB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-mcp-server.d.ts","sourceRoot":"","sources":["../../src/server/create-mcp-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAQnE,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,eAAe,EAEf,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAM9B,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,6BAA8B,SAAQ,gBAAgB;IACrE,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACzC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,mCAAmC;IACnC,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,CAIR;AAqBD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,6BAA6B,GAAG;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;CACpB,CAgIA;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAmEvF"}
|
|
@@ -5,6 +5,7 @@ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSche
|
|
|
5
5
|
import { inspectSharkcraft } from '@shrkcrft/inspector';
|
|
6
6
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION, } from "./mcp-server-config.js";
|
|
7
7
|
import { ALL_TOOLS } from "../tools/index.js";
|
|
8
|
+
import { PRIMARY_MCP_TOOLS, shouldAdvertiseFullToolset } from "../tools/primary-tools.js";
|
|
8
9
|
import { validateToolInput } from "./tool-input-validators.js";
|
|
9
10
|
import { buildResourceList, readResource } from "../resources/index.js";
|
|
10
11
|
/**
|
|
@@ -52,13 +53,24 @@ export function createSharkcraftServer(config) {
|
|
|
52
53
|
resources: { listChanged: true },
|
|
53
54
|
},
|
|
54
55
|
});
|
|
55
|
-
server.setRequestHandler(ListToolsRequestSchema, async () =>
|
|
56
|
-
tools
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
57
|
+
// Default `tools/list` advertises only the ~30 primary tools to
|
|
58
|
+
// keep the agent's tool-selection surface focused. The other ~200
|
|
59
|
+
// tools stay CALLABLE via `tools/call` for agents that already
|
|
60
|
+
// know the name (e.g. via `shrk export claude-skill` referencing
|
|
61
|
+
// them). Set SHRK_MCP_FULL_TOOLS=1 to advertise everything.
|
|
62
|
+
const advertiseFull = shouldAdvertiseFullToolset();
|
|
63
|
+
const advertised = advertiseFull
|
|
64
|
+
? ALL_TOOLS
|
|
65
|
+
: ALL_TOOLS.filter((t) => PRIMARY_MCP_TOOLS.has(t.name));
|
|
66
|
+
return {
|
|
67
|
+
tools: advertised.map((t) => ({
|
|
68
|
+
name: t.name,
|
|
69
|
+
description: t.description,
|
|
70
|
+
inputSchema: t.inputSchema,
|
|
71
|
+
})),
|
|
72
|
+
};
|
|
73
|
+
});
|
|
62
74
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
63
75
|
const name = request.params.name;
|
|
64
76
|
const args = (request.params.arguments ?? {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"all-tools.d.ts","sourceRoot":"","sources":["../../src/tools/all-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"all-tools.d.ts","sourceRoot":"","sources":["../../src/tools/all-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAiTpE,eAAO,MAAM,SAAS,EAAE,SAAS,eAAe,EA0P9C,CAAC"}
|
package/dist/tools/all-tools.js
CHANGED
|
@@ -116,7 +116,8 @@ import { getContractTemplateTool, getLanguageCommandsTool, getLanguageProfilesTo
|
|
|
116
116
|
import { getTaskContextTool, understandTaskTool, validateChangeContextTool, } from "./r26-task-context.tool.js";
|
|
117
117
|
import { getLanguageCacheStatusTool, getLanguageProfilesLiveTool, getLanguageRunPlanTool, getPolyglotBoundaryReportTool, } from "./r27-polyglot.tool.js";
|
|
118
118
|
import { getChangedBoundaryReportTool } from "./r28-changed-boundary.tool.js";
|
|
119
|
-
import {
|
|
119
|
+
import { getDiffCheckReportTool } from "./diff-check.tool.js";
|
|
120
|
+
import { getFileAdviceTool } from "./file-advice.tool.js";
|
|
120
121
|
import { getHelperTool, listHelpersTool, previewHelperPlanTool } from "./r28-helpers.tool.js";
|
|
121
122
|
import { getPackDevStatusTool, previewPackTestsTool } from "./r28-pack-author.tool.js";
|
|
122
123
|
import { getRegistryLifecycleReportTool } from "./r28-registry-lifecycle.tool.js";
|
|
@@ -135,7 +136,7 @@ import { suggestCommandsTool, searchCommandsTool, explainCommandTool, } from "./
|
|
|
135
136
|
import { previewFixTool, listFixKindsTool, } from "./r31-fix-preview.tool.js";
|
|
136
137
|
import { getScaffoldCoverageReportTool } from "./r31-scaffold-coverage.tool.js";
|
|
137
138
|
import { getChangesSummaryTool, getPrSummaryPreviewTool, getCiIntegrityReportTool, } from "./r31-changes-pr-ci.tool.js";
|
|
138
|
-
import { listProfilesTool, getProfileTool, getProfilesDoctorTool,
|
|
139
|
+
import { listProfilesTool, getProfileTool, getProfilesDoctorTool, } from "./r32-profiles.tool.js";
|
|
139
140
|
import { getProjectCouplingReportTool } from "./r32-project-coupling.tool.js";
|
|
140
141
|
import { getPackContributionsTool, getPackConflictsTool, } from "./r33-pack-contributions.tool.js";
|
|
141
142
|
import { listConventionsTool, getConventionTool, getConventionsDoctorTool, } from "./r33-conventions.tool.js";
|
|
@@ -334,8 +335,8 @@ export const ALL_TOOLS = Object.freeze([
|
|
|
334
335
|
getLanguageCacheStatusTool,
|
|
335
336
|
getLanguageProfilesLiveTool,
|
|
336
337
|
getChangedBoundaryReportTool,
|
|
337
|
-
|
|
338
|
-
|
|
338
|
+
getDiffCheckReportTool,
|
|
339
|
+
getFileAdviceTool,
|
|
339
340
|
listHelpersTool,
|
|
340
341
|
getHelperTool,
|
|
341
342
|
previewHelperPlanTool,
|
|
@@ -370,9 +371,6 @@ export const ALL_TOOLS = Object.freeze([
|
|
|
370
371
|
listProfilesTool,
|
|
371
372
|
getProfileTool,
|
|
372
373
|
getProfilesDoctorTool,
|
|
373
|
-
listPluginLifecycleProfilesTool,
|
|
374
|
-
getPluginLifecycleProfileTool,
|
|
375
|
-
getPluginLifecycleProfileDoctorTool,
|
|
376
374
|
getProjectCouplingReportTool,
|
|
377
375
|
getPackContributionsTool,
|
|
378
376
|
getPackConflictsTool,
|
|
@@ -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,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primary MCP tools — the ~30 tools advertised to a connected agent
|
|
3
|
+
* by default. Every tool in {@link ALL_TOOLS} stays callable (so an
|
|
4
|
+
* agent that already knows the name can use it), but `tools/list`
|
|
5
|
+
* only advertises the primary set. Smaller surface = better
|
|
6
|
+
* tool-selection accuracy for the agent.
|
|
7
|
+
*
|
|
8
|
+
* Picked to match the CLI's allowlist semantics: anything an agent
|
|
9
|
+
* could realistically reach for during a normal task — discovery,
|
|
10
|
+
* context, planning, validation — is in. Internal introspection
|
|
11
|
+
* (catalog dumps, fix-preview internals, drift baselines) is out;
|
|
12
|
+
* still callable, just not in the default tool list.
|
|
13
|
+
*
|
|
14
|
+
* Escape hatch: set `SHRK_MCP_FULL_TOOLS=1` to advertise the full
|
|
15
|
+
* catalog (useful when debugging an agent's tool selection).
|
|
16
|
+
*/
|
|
17
|
+
export declare const PRIMARY_MCP_TOOLS: ReadonlySet<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Should `tools/list` advertise the full catalog instead of the
|
|
20
|
+
* primary set? Driven by the env var so an agent operator can flip it
|
|
21
|
+
* without rebuilding.
|
|
22
|
+
*/
|
|
23
|
+
export declare function shouldAdvertiseFullToolset(): boolean;
|
|
24
|
+
//# sourceMappingURL=primary-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-tools.d.ts","sourceRoot":"","sources":["../../src/tools/primary-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CA4ChD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAGpD"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primary MCP tools — the ~30 tools advertised to a connected agent
|
|
3
|
+
* by default. Every tool in {@link ALL_TOOLS} stays callable (so an
|
|
4
|
+
* agent that already knows the name can use it), but `tools/list`
|
|
5
|
+
* only advertises the primary set. Smaller surface = better
|
|
6
|
+
* tool-selection accuracy for the agent.
|
|
7
|
+
*
|
|
8
|
+
* Picked to match the CLI's allowlist semantics: anything an agent
|
|
9
|
+
* could realistically reach for during a normal task — discovery,
|
|
10
|
+
* context, planning, validation — is in. Internal introspection
|
|
11
|
+
* (catalog dumps, fix-preview internals, drift baselines) is out;
|
|
12
|
+
* still callable, just not in the default tool list.
|
|
13
|
+
*
|
|
14
|
+
* Escape hatch: set `SHRK_MCP_FULL_TOOLS=1` to advertise the full
|
|
15
|
+
* catalog (useful when debugging an agent's tool selection).
|
|
16
|
+
*/
|
|
17
|
+
export const PRIMARY_MCP_TOOLS = new Set([
|
|
18
|
+
// Project orientation
|
|
19
|
+
'inspect_workspace',
|
|
20
|
+
'get_project_overview',
|
|
21
|
+
'get_agent_instructions',
|
|
22
|
+
'get_start_here',
|
|
23
|
+
// Context / task routing
|
|
24
|
+
'get_relevant_context',
|
|
25
|
+
'get_task_packet',
|
|
26
|
+
'get_action_hints',
|
|
27
|
+
'create_agent_brief',
|
|
28
|
+
'get_relevant_rules',
|
|
29
|
+
'explain_command',
|
|
30
|
+
// Browse the registries
|
|
31
|
+
'list_knowledge',
|
|
32
|
+
'get_knowledge',
|
|
33
|
+
'list_rules',
|
|
34
|
+
'get_rule',
|
|
35
|
+
'list_path_conventions',
|
|
36
|
+
'list_templates',
|
|
37
|
+
'get_template',
|
|
38
|
+
'list_pipelines',
|
|
39
|
+
'get_pipeline',
|
|
40
|
+
'list_presets',
|
|
41
|
+
'get_preset',
|
|
42
|
+
'list_packs',
|
|
43
|
+
'get_pack',
|
|
44
|
+
// Safe code generation (plan-first)
|
|
45
|
+
'create_generation_plan',
|
|
46
|
+
'render_template_preview',
|
|
47
|
+
'explain_generation_target',
|
|
48
|
+
// Validation gates (read-only)
|
|
49
|
+
'check_boundaries',
|
|
50
|
+
'get_diff_check_report',
|
|
51
|
+
'get_file_advice',
|
|
52
|
+
'get_architecture_constraints',
|
|
53
|
+
'get_architecture_violations',
|
|
54
|
+
// Doctor / readiness
|
|
55
|
+
'get_ai_readiness_report',
|
|
56
|
+
'doctor_packs',
|
|
57
|
+
// Search
|
|
58
|
+
'search_all',
|
|
59
|
+
'search_knowledge',
|
|
60
|
+
'search_commands',
|
|
61
|
+
]);
|
|
62
|
+
/**
|
|
63
|
+
* Should `tools/list` advertise the full catalog instead of the
|
|
64
|
+
* primary set? Driven by the env var so an agent operator can flip it
|
|
65
|
+
* without rebuilding.
|
|
66
|
+
*/
|
|
67
|
+
export function shouldAdvertiseFullToolset() {
|
|
68
|
+
const v = process.env.SHRK_MCP_FULL_TOOLS;
|
|
69
|
+
return v === '1' || v === 'true' || v === 'yes';
|
|
70
|
+
}
|
|
@@ -93,7 +93,7 @@ export const getCommandTaxonomyTool = {
|
|
|
93
93
|
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
94
94
|
async handler() {
|
|
95
95
|
// The CLI carries the canonical catalog; here we describe the surface
|
|
96
|
-
// without importing it (no inspector → CLI dependency).
|
|
96
|
+
// without importing it (no inspector → CLI dependency). Consumers can
|
|
97
97
|
// run `shrk commands taxonomy` for the full grouped output.
|
|
98
98
|
return {
|
|
99
99
|
data: {
|
|
@@ -2,7 +2,4 @@ import type { IToolDefinition } from '../server/tool-definition.js';
|
|
|
2
2
|
export declare const listProfilesTool: IToolDefinition;
|
|
3
3
|
export declare const getProfileTool: IToolDefinition;
|
|
4
4
|
export declare const getProfilesDoctorTool: IToolDefinition;
|
|
5
|
-
export declare const listPluginLifecycleProfilesTool: IToolDefinition;
|
|
6
|
-
export declare const getPluginLifecycleProfileTool: IToolDefinition;
|
|
7
|
-
export declare const getPluginLifecycleProfileDoctorTool: IToolDefinition;
|
|
8
5
|
//# sourceMappingURL=r32-profiles.tool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r32-profiles.tool.d.ts","sourceRoot":"","sources":["../../src/tools/r32-profiles.tool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"r32-profiles.tool.d.ts","sourceRoot":"","sources":["../../src/tools/r32-profiles.tool.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,eAAO,MAAM,gBAAgB,EAAE,eAe9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,eAsB5B,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAOnC,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Read-only MCP tools for profiles
|
|
2
|
+
* Read-only MCP tools for profiles.
|
|
3
3
|
*
|
|
4
4
|
* Engine never writes from MCP. These tools surface pack-contributed and
|
|
5
5
|
* locally configured profiles so an agent can decide which one to pass to
|
|
6
|
-
*
|
|
6
|
+
* a profile-aware command (the human runs the CLI).
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { findProfile, listProfileIssues, listProfiles, ProfileKind, } from '@shrkcrft/inspector';
|
|
9
9
|
export const listProfilesTool = {
|
|
10
10
|
name: 'list_profiles',
|
|
11
11
|
description: 'List all pack-contributed and locally configured profiles. Read-only.',
|
|
@@ -54,54 +54,3 @@ export const getProfilesDoctorTool = {
|
|
|
54
54
|
return { data: { issues: await listProfileIssues(ctx.inspection) } };
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
|
-
export const listPluginLifecycleProfilesTool = {
|
|
58
|
-
name: 'list_plugin_lifecycle_profiles',
|
|
59
|
-
description: 'List registered plugin lifecycle profiles. Read-only.',
|
|
60
|
-
inputSchema: { type: 'object', additionalProperties: false, properties: {} },
|
|
61
|
-
async handler(_input, ctx) {
|
|
62
|
-
return { data: await listPluginLifecycleProfiles(ctx.inspection) };
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
export const getPluginLifecycleProfileTool = {
|
|
66
|
-
name: 'get_plugin_lifecycle_profile',
|
|
67
|
-
description: 'Get one plugin lifecycle profile by id. Read-only.',
|
|
68
|
-
inputSchema: {
|
|
69
|
-
type: 'object',
|
|
70
|
-
additionalProperties: false,
|
|
71
|
-
required: ['id'],
|
|
72
|
-
properties: { id: { type: 'string' } },
|
|
73
|
-
},
|
|
74
|
-
async handler(input, ctx) {
|
|
75
|
-
const id = typeof input.id === 'string' ? input.id : '';
|
|
76
|
-
if (!id)
|
|
77
|
-
return { isError: true, error: { code: 'invalid-input', message: 'id is required.' } };
|
|
78
|
-
const entries = await listPluginLifecycleProfiles(ctx.inspection);
|
|
79
|
-
const entry = entries.find((e) => e.profile.id === id);
|
|
80
|
-
if (!entry) {
|
|
81
|
-
return { isError: true, error: { code: 'not-found', message: `Unknown lifecycle profile "${id}".` } };
|
|
82
|
-
}
|
|
83
|
-
return { data: entry };
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
export const getPluginLifecycleProfileDoctorTool = {
|
|
87
|
-
name: 'get_plugin_lifecycle_profile_doctor',
|
|
88
|
-
description: 'Health check for plugin lifecycle profiles. Read-only.',
|
|
89
|
-
inputSchema: {
|
|
90
|
-
type: 'object',
|
|
91
|
-
additionalProperties: false,
|
|
92
|
-
properties: { id: { type: 'string' } },
|
|
93
|
-
},
|
|
94
|
-
async handler(input, ctx) {
|
|
95
|
-
const id = typeof input.id === 'string' ? input.id : undefined;
|
|
96
|
-
const entries = await listPluginLifecycleProfiles(ctx.inspection);
|
|
97
|
-
const targets = id ? entries.filter((e) => e.profile.id === id) : entries;
|
|
98
|
-
if (id && targets.length === 0) {
|
|
99
|
-
return { isError: true, error: { code: 'not-found', message: `Unknown lifecycle profile "${id}".` } };
|
|
100
|
-
}
|
|
101
|
-
const health = {};
|
|
102
|
-
for (const e of targets) {
|
|
103
|
-
health[e.profile.id] = checkPluginLifecycleProfileHealth(ctx.cwd, e.profile);
|
|
104
|
-
}
|
|
105
|
-
return { data: { health } };
|
|
106
|
-
},
|
|
107
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shrkcrft/mcp-server",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "SharkCraft MCP server: 25 tools over @modelcontextprotocol/sdk's stdio transport.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SharkCraft contributors",
|
|
@@ -44,20 +44,20 @@
|
|
|
44
44
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@shrkcrft/core": "^0.1.0-alpha.
|
|
48
|
-
"@shrkcrft/config": "^0.1.0-alpha.
|
|
49
|
-
"@shrkcrft/workspace": "^0.1.0-alpha.
|
|
50
|
-
"@shrkcrft/knowledge": "^0.1.0-alpha.
|
|
51
|
-
"@shrkcrft/context": "^0.1.0-alpha.
|
|
52
|
-
"@shrkcrft/rules": "^0.1.0-alpha.
|
|
53
|
-
"@shrkcrft/paths": "^0.1.0-alpha.
|
|
54
|
-
"@shrkcrft/templates": "^0.1.0-alpha.
|
|
55
|
-
"@shrkcrft/pipelines": "^0.1.0-alpha.
|
|
56
|
-
"@shrkcrft/presets": "^0.1.0-alpha.
|
|
57
|
-
"@shrkcrft/boundaries": "^0.1.0-alpha.
|
|
58
|
-
"@shrkcrft/packs": "^0.1.0-alpha.
|
|
59
|
-
"@shrkcrft/generator": "^0.1.0-alpha.
|
|
60
|
-
"@shrkcrft/inspector": "^0.1.0-alpha.
|
|
47
|
+
"@shrkcrft/core": "^0.1.0-alpha.8",
|
|
48
|
+
"@shrkcrft/config": "^0.1.0-alpha.8",
|
|
49
|
+
"@shrkcrft/workspace": "^0.1.0-alpha.8",
|
|
50
|
+
"@shrkcrft/knowledge": "^0.1.0-alpha.8",
|
|
51
|
+
"@shrkcrft/context": "^0.1.0-alpha.8",
|
|
52
|
+
"@shrkcrft/rules": "^0.1.0-alpha.8",
|
|
53
|
+
"@shrkcrft/paths": "^0.1.0-alpha.8",
|
|
54
|
+
"@shrkcrft/templates": "^0.1.0-alpha.8",
|
|
55
|
+
"@shrkcrft/pipelines": "^0.1.0-alpha.8",
|
|
56
|
+
"@shrkcrft/presets": "^0.1.0-alpha.8",
|
|
57
|
+
"@shrkcrft/boundaries": "^0.1.0-alpha.8",
|
|
58
|
+
"@shrkcrft/packs": "^0.1.0-alpha.8",
|
|
59
|
+
"@shrkcrft/generator": "^0.1.0-alpha.8",
|
|
60
|
+
"@shrkcrft/inspector": "^0.1.0-alpha.8",
|
|
61
61
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
62
62
|
"zod": "^3.25.0 || ^4.0.0"
|
|
63
63
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"r28-plugin-lifecycle.tool.d.ts","sourceRoot":"","sources":["../../src/tools/r28-plugin-lifecycle.tool.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAMpE,eAAO,MAAM,uBAAuB,EAAE,eA0CrC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAuCrC,CAAC"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plan-only plugin lifecycle previews (profile-driven).
|
|
3
|
-
*
|
|
4
|
-
* preview_plugin_rename — returns the rename plan (replace ops + manual steps).
|
|
5
|
-
* preview_plugin_remove — returns the destructive remove plan.
|
|
6
|
-
*
|
|
7
|
-
* Both tools never write source — the human runs `shrk plugin rename|remove`
|
|
8
|
-
* to produce the saved plan and applies it via `shrk apply --verify-signature`.
|
|
9
|
-
*
|
|
10
|
-
* A `profile` input is required. If exactly one lifecycle profile is
|
|
11
|
-
* registered, the tool implicitly uses it; otherwise the caller must supply
|
|
12
|
-
* `profile`.
|
|
13
|
-
*/
|
|
14
|
-
import { buildPluginRemovePlan, buildPluginRenamePlan, resolvePluginLifecycleProfile, } from '@shrkcrft/inspector';
|
|
15
|
-
function nextHint(cmd) {
|
|
16
|
-
return `Next: \`${cmd}\` (CLI is the only write path).`;
|
|
17
|
-
}
|
|
18
|
-
export const previewPluginRenameTool = {
|
|
19
|
-
name: 'preview_plugin_rename',
|
|
20
|
-
description: 'Preview a plugin rename plan. Read-only — returns the structured plan only. Requires a registered plugin lifecycle profile (pass `profile` if more than one is registered).',
|
|
21
|
-
inputSchema: {
|
|
22
|
-
type: 'object',
|
|
23
|
-
additionalProperties: false,
|
|
24
|
-
required: ['oldName', 'newName'],
|
|
25
|
-
properties: {
|
|
26
|
-
oldName: { type: 'string' },
|
|
27
|
-
newName: { type: 'string' },
|
|
28
|
-
profile: { type: 'string' },
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
async handler(input, ctx) {
|
|
32
|
-
const oldName = String(input.oldName ?? '');
|
|
33
|
-
const newName = String(input.newName ?? '');
|
|
34
|
-
const profileId = typeof input.profile === 'string' ? input.profile : undefined;
|
|
35
|
-
const resolved = await resolvePluginLifecycleProfile(ctx.inspection, {
|
|
36
|
-
profileId,
|
|
37
|
-
allowSingleDefault: true,
|
|
38
|
-
});
|
|
39
|
-
if (!resolved.entry) {
|
|
40
|
-
return {
|
|
41
|
-
isError: true,
|
|
42
|
-
error: { code: 'profile-required', message: resolved.error ?? 'Profile resolution failed' },
|
|
43
|
-
data: { availableProfiles: resolved.availableIds },
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
const plan = buildPluginRenamePlan({
|
|
47
|
-
projectRoot: ctx.cwd,
|
|
48
|
-
profile: resolved.entry.profile,
|
|
49
|
-
oldName,
|
|
50
|
-
newName,
|
|
51
|
-
});
|
|
52
|
-
return {
|
|
53
|
-
text: nextHint(`shrk plugin rename ${oldName} ${newName} --profile ${resolved.entry.profile.id} --output /tmp/plan.json`),
|
|
54
|
-
data: plan,
|
|
55
|
-
};
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
export const previewPluginRemoveTool = {
|
|
59
|
-
name: 'preview_plugin_remove',
|
|
60
|
-
description: 'Preview a destructive plugin remove plan. Read-only — returns the structured plan only. Requires a registered plugin lifecycle profile (pass `profile` if more than one is registered).',
|
|
61
|
-
inputSchema: {
|
|
62
|
-
type: 'object',
|
|
63
|
-
additionalProperties: false,
|
|
64
|
-
required: ['name'],
|
|
65
|
-
properties: {
|
|
66
|
-
name: { type: 'string' },
|
|
67
|
-
profile: { type: 'string' },
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
async handler(input, ctx) {
|
|
71
|
-
const name = String(input.name ?? '');
|
|
72
|
-
const profileId = typeof input.profile === 'string' ? input.profile : undefined;
|
|
73
|
-
const resolved = await resolvePluginLifecycleProfile(ctx.inspection, {
|
|
74
|
-
profileId,
|
|
75
|
-
allowSingleDefault: true,
|
|
76
|
-
});
|
|
77
|
-
if (!resolved.entry) {
|
|
78
|
-
return {
|
|
79
|
-
isError: true,
|
|
80
|
-
error: { code: 'profile-required', message: resolved.error ?? 'Profile resolution failed' },
|
|
81
|
-
data: { availableProfiles: resolved.availableIds },
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
const plan = buildPluginRemovePlan({
|
|
85
|
-
projectRoot: ctx.cwd,
|
|
86
|
-
profile: resolved.entry.profile,
|
|
87
|
-
oldName: name,
|
|
88
|
-
});
|
|
89
|
-
return {
|
|
90
|
-
text: nextHint(`shrk plugin remove ${name} --profile ${resolved.entry.profile.id} --output /tmp/plan.json`),
|
|
91
|
-
data: plan,
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
};
|