@savvy-web/mcp 2.3.4 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/mcp",
3
- "version": "2.3.4",
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",
@@ -33,10 +33,10 @@
33
33
  "dependencies": {
34
34
  "@effect/platform-node": "4.0.0-beta.107",
35
35
  "@effected/commands": "^0.4.0",
36
- "@effected/git": "^0.7.0",
37
- "@effected/workspaces": "^0.12.0",
36
+ "@effected/git": "^0.8.0",
37
+ "@effected/workspaces": "^0.13.0",
38
38
  "@modelcontextprotocol/sdk": "^1.29.0",
39
- "@savvy-web/silk-effects": "5.8.0",
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), or 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). 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. 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.",
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),
@@ -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 restore."
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 };