@savvy-web/mcp 2.3.5 → 2.4.0
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/package.json +2 -2
- package/server.js +4 -2
- package/tools/repos-manage.js +33 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The savvy MCP server — Silk Suite tooling and library knowledge for coding agents",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/mcp",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@effected/git": "^0.8.0",
|
|
37
37
|
"@effected/workspaces": "^0.13.0",
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
-
"@savvy-web/silk-effects": "5.
|
|
39
|
+
"@savvy-web/silk-effects": "5.9.0",
|
|
40
40
|
"effect": "4.0.0-beta.107",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
}
|
package/server.js
CHANGED
|
@@ -170,7 +170,7 @@ function buildServer(ctx) {
|
|
|
170
170
|
});
|
|
171
171
|
server.registerTool("repos_manage", {
|
|
172
172
|
title: "Manage vendored repos",
|
|
173
|
-
description: "Mutating: sync (initialize/reconcile submodules per the manifest), pin (re-pin a repo to a new ref), add (vendor a new repo), note (add/remove/promote an agent note), remove (unvendor a repo), rename (rename a vendored repo's manifest key and worktree),
|
|
173
|
+
description: "Mutating: sync (initialize/reconcile submodules per the manifest), pin (re-pin a repo to a new ref), add (vendor a new repo), note (add/remove/promote an agent note), remove (unvendor a repo), rename (rename a vendored repo's manifest key and worktree), restore (hard-reset a repo's worktree back to its pinned gitlink commit and re-apply sparse paths — DESTRUCTIVE to uncommitted worktree edits; never run implicitly), or deregister (clear a STALE submodule.<section> registration from the superproject's local git config — the phantom entry repos_inspect drift reports as localRegistrationDivergence with no matching manifest entry; refuses a section outside .repos/ and any section still backing a live manifest entry — canonically named or gitdir-diverged — and touches local config only, so nothing is staged). Pass action plus the fields that action needs: pin needs name+ref; add needs url+ref+purpose (name/sparse/orientation optional — pass orientation back from a preceding remove's removedEntry to make a re-vendor lossless); note needs name+op, plus note (op=add), id (op=remove), or id+into (op=promote); remove needs name; rename needs name (the old name) + newName; restore takes an optional names list — omitted, it restores every dirty repo and reports the clean ones as skipped; given, it restores exactly those repos even if already clean; deregister needs section (the registration name exactly as the drift report states it, e.g. .repos/old-name — no submodule. prefix). A decode failure names the missing field. The pin result's markdown surfaces commitMessage and staleNoteIds — review and commit after pinning. The remove result's markdown surfaces commitMessage, removedNotes and the removed entry's orientation block — promote any durable notes elsewhere, keep the orientation if you intend to re-vendor, then review and commit. The rename result's markdown surfaces commitMessage — review and commit after renaming. The restore result's markdown names exactly what was discarded. The deregister result's markdown lists the config keys the removed section carried — nothing to commit afterwards.",
|
|
174
174
|
inputSchema: {
|
|
175
175
|
action: z.enum([
|
|
176
176
|
"sync",
|
|
@@ -179,7 +179,8 @@ function buildServer(ctx) {
|
|
|
179
179
|
"note",
|
|
180
180
|
"remove",
|
|
181
181
|
"rename",
|
|
182
|
-
"restore"
|
|
182
|
+
"restore",
|
|
183
|
+
"deregister"
|
|
183
184
|
]).describe("Which mutation to perform."),
|
|
184
185
|
name: z.optional(z.string()).describe("Repo name (pin, note, remove, rename; optional override for add)."),
|
|
185
186
|
newName: z.optional(z.string()).describe("New repo name (rename)."),
|
|
@@ -201,6 +202,7 @@ function buildServer(ctx) {
|
|
|
201
202
|
id: z.optional(z.string()).describe("Note id (note, op=remove|promote)."),
|
|
202
203
|
into: z.optional(z.enum(["layout", "startHere"])).describe("Orientation target (note, op=promote)."),
|
|
203
204
|
names: z.optional(z.array(z.string())).describe("Repo names to restore (restore); omitted restores every dirty repo."),
|
|
205
|
+
section: z.optional(z.string()).describe("Stale registration name to clear (deregister), exactly as the drift report states it (e.g. .repos/old-name); the submodule. prefix is implied."),
|
|
204
206
|
cwd: z.optional(z.string()).describe("Directory to resolve the workspace root from.")
|
|
205
207
|
},
|
|
206
208
|
outputSchema: effectToZodSchema(ReposManageResult),
|
package/tools/repos-manage.js
CHANGED
|
@@ -54,6 +54,12 @@ const RenameRequest = Schema.TaggedStruct("rename", {
|
|
|
54
54
|
});
|
|
55
55
|
/** `restore`'s `names` is optional and repeatable, mirroring `add`'s `sparse`; omitted means "every dirty entry". */
|
|
56
56
|
const RestoreRequest = Schema.TaggedStruct("restore", { names: Schema.optional(Schema.Array(Schema.String)) });
|
|
57
|
+
/**
|
|
58
|
+
* `deregister` requires `section` — the stale registration name exactly as the
|
|
59
|
+
* drift report states it (e.g. `.repos/old-name`); the `submodule.` prefix is
|
|
60
|
+
* implied, never passed.
|
|
61
|
+
*/
|
|
62
|
+
const DeregisterRequest = Schema.TaggedStruct("deregister", { section: Schema.String });
|
|
57
63
|
/** Internal tagged-union request the flat wire args decode into. */
|
|
58
64
|
const ReposManageRequest = Schema.Union([
|
|
59
65
|
SyncRequest,
|
|
@@ -62,7 +68,8 @@ const ReposManageRequest = Schema.Union([
|
|
|
62
68
|
NoteRequest,
|
|
63
69
|
RemoveRequest,
|
|
64
70
|
RenameRequest,
|
|
65
|
-
RestoreRequest
|
|
71
|
+
RestoreRequest,
|
|
72
|
+
DeregisterRequest
|
|
66
73
|
]);
|
|
67
74
|
/** `sync` result variant. */
|
|
68
75
|
const ReposManageSyncResult = Schema.Struct({
|
|
@@ -99,6 +106,11 @@ const ReposManageRestoreResult = Schema.Struct({
|
|
|
99
106
|
action: Schema.Literal("restore"),
|
|
100
107
|
result: Repos.ReposRestoreResult
|
|
101
108
|
}).annotate({ identifier: "ReposManageRestoreResult" });
|
|
109
|
+
/** `deregister` result variant. */
|
|
110
|
+
const ReposManageDeregisterResult = Schema.Struct({
|
|
111
|
+
action: Schema.Literal("deregister"),
|
|
112
|
+
result: Repos.ReposDeregisterResult
|
|
113
|
+
}).annotate({ identifier: "ReposManageDeregisterResult" });
|
|
102
114
|
/** The `repos_manage` tool result — a discriminated union keyed by `action`. */
|
|
103
115
|
const ReposManageResult = Schema.Union([
|
|
104
116
|
ReposManageSyncResult,
|
|
@@ -107,11 +119,12 @@ const ReposManageResult = Schema.Union([
|
|
|
107
119
|
ReposManageNoteResult,
|
|
108
120
|
ReposManageRemoveResult,
|
|
109
121
|
ReposManageRenameResult,
|
|
110
|
-
ReposManageRestoreResult
|
|
122
|
+
ReposManageRestoreResult,
|
|
123
|
+
ReposManageDeregisterResult
|
|
111
124
|
]).annotate({
|
|
112
125
|
identifier: "ReposManageResult",
|
|
113
126
|
title: "repos_manage result",
|
|
114
|
-
description: "Result of a mutating repos action: sync, pin, add, note, remove, rename, or
|
|
127
|
+
description: "Result of a mutating repos action: sync, pin, add, note, remove, rename, restore, or deregister."
|
|
115
128
|
});
|
|
116
129
|
/** Render the structured result as a markdown transcript. */
|
|
117
130
|
const renderMarkdown = (data) => {
|
|
@@ -227,6 +240,18 @@ const renderMarkdown = (data) => {
|
|
|
227
240
|
if (r.stillDirty.length > 0) lines.push(``, `## Still dirty — RESTORE DID NOT FULLY SUCCEED`, ``, `The reset ran but these worktrees remain dirty, so treat the restore as incomplete rather than done:`, ``, ...r.stillDirty.map((name) => `- ${mdInline(name)}`), ``, `Run repos_inspect (mode: drift) — a nestedSubmoduleDivergence or a permission problem is the usual cause.`);
|
|
228
241
|
return lines.join("\n");
|
|
229
242
|
}
|
|
243
|
+
case "deregister": {
|
|
244
|
+
const r = data.result;
|
|
245
|
+
return [
|
|
246
|
+
`# repos deregister — ${mdInline(r.section)}`,
|
|
247
|
+
``,
|
|
248
|
+
`Removed the stale ${mdInline(`submodule.${r.section}`)} section from the superproject's LOCAL git config. Keys cleared:`,
|
|
249
|
+
``,
|
|
250
|
+
...r.removedKeys.map((key) => `- ${mdInline(key)}`),
|
|
251
|
+
``,
|
|
252
|
+
`Local config only — nothing is staged and there is nothing to commit.`
|
|
253
|
+
].join("\n");
|
|
254
|
+
}
|
|
230
255
|
}
|
|
231
256
|
};
|
|
232
257
|
/** One-way transform: result to markdown. Encoding back is forbidden. */
|
|
@@ -296,8 +321,12 @@ const reposManage = (args, fallbackCwd) => Effect.gen(function* () {
|
|
|
296
321
|
action: "restore",
|
|
297
322
|
result: yield* manager.restore(root, request.names)
|
|
298
323
|
};
|
|
324
|
+
case "deregister": return {
|
|
325
|
+
action: "deregister",
|
|
326
|
+
result: yield* manager.deregister(root, request.section)
|
|
327
|
+
};
|
|
299
328
|
}
|
|
300
329
|
});
|
|
301
330
|
|
|
302
331
|
//#endregion
|
|
303
|
-
export { ReposManageAddResult, ReposManageAsMarkdown, ReposManageNoteResult, ReposManagePinResult, ReposManageRemoveResult, ReposManageRenameResult, ReposManageRestoreResult, ReposManageResult, ReposManageSyncResult, reposManage };
|
|
332
|
+
export { ReposManageAddResult, ReposManageAsMarkdown, ReposManageDeregisterResult, ReposManageNoteResult, ReposManagePinResult, ReposManageRemoveResult, ReposManageRenameResult, ReposManageRestoreResult, ReposManageResult, ReposManageSyncResult, reposManage };
|