@savvy-web/mcp 2.5.2 → 2.6.1

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/README.md CHANGED
@@ -43,11 +43,11 @@ npx @modelcontextprotocol/inspector savvy-mcp .
43
43
 
44
44
  - `workspace_info` — returns a flat, structured projection of the workspace analysis: linked and fixed package groups as name arrays plus resolved registry targets. Backed by the same `silk-effects` analyzer the `savvy` CLI uses.
45
45
  - `turbo_inspect` — read-only Turborepo inspection over `turbo --dry`: diagnose a task's per-package cache hits, derive the task graph or list the packages affected by recent changes. It never runs a task. Backed by the same `silk-effects` `Turbo` inspector an agent would otherwise drive by hand.
46
- - `changeset_inspect` — read-only changeset analysis for release work: `mode=branch` diffs the current branch against its base and attributes every changed file to its owning package, `mode=config` surfaces the resolved `.changeset/config.json` with its release surfaces, version files and ignore list, and `mode=classify` reports how the branch's pending changesets classify each package's bump. It never writes a changeset. Backed by the same `silk-effects` changeset services the `savvy` CLI uses.
46
+ - `changeset_inspect` — read-only changeset analysis for release work: `mode=branch` diffs the current branch against its base and attributes every changed file to its owning package — an unmapped file carries a hint when a configured glob or a known template mirror explains it, and attribution works from a git worktree of the repository — `mode=config` surfaces the resolved `.changeset/config.json` with its release surfaces, version files and ignore list, and `mode=classify` reports how the branch's pending changesets classify each package's bump. It never writes a changeset. Backed by the same `silk-effects` changeset services the `savvy` CLI uses.
47
47
  - `changeset_validate` — read-only validation of the files in a changeset directory against the section-aware rules, returning typed diagnostics (file, rule, line, column, message) plus an ok flag and error count. Use it instead of shelling out to `savvy changeset lint`.
48
48
  - `changeset_preview` — read-only preview of the next release: it runs the genuine changesets engine over the pending changesets and returns each package's version bump (old to new) plus the rendered CHANGELOG block, exactly as it would ship. It never mutates the repo. Backed by the same `silk-effects` release planner the `savvy changeset version` command applies.
49
- - `changeset_deps_detect` — read-only preview of the cumulative dependency diff (merge-base to working tree): one entry per affected workspace package with its resolved dependency-table rows, `catalog:`/`workspace:` specifiers resolved to concrete versions. It never writes a changeset. Backed by `silk-effects`' `Changesets.DepsRegen.plan`.
50
- - `changeset_deps_regen` — regenerates pure-dependency changesets: deletes stale ones and writes fresh single-package, patch-bump changesets from the cumulative dependency diff. Mutating unless `dryRun` is set, in which case it reports what it would delete and write without touching the filesystem. Backed by `silk-effects`' `Changesets.DepsRegen`.
49
+ - `changeset_deps_detect` — read-only preview of the cumulative dependency diff (merge-base to working tree): one entry per affected workspace package with its resolved dependency-table rows — `catalog:` specifiers resolve per snapshot side to the range the catalog declares (e.g. `^0.7.0`), while `workspace:` specifiers resolve to the exact workspace version — plus a `coexisting` bucket naming the prose-only changesets that already release an affected package (informational, never touched). It never writes a changeset. Backed by `silk-effects`' `Changesets.DepsRegen.plan`.
50
+ - `changeset_deps_regen` — regenerates pure-dependency changesets: deletes stale ones and writes fresh single-package, patch-bump changesets from the cumulative dependency diff. Mutating unless `dryRun` is set, in which case it reports what it would delete and write without touching the filesystem. Results carry the same `coexisting` bucket as `changeset_deps_detect`. Backed by `silk-effects`' `Changesets.DepsRegen`.
51
51
  - `biome_check` — run Biome over a path and get structured diagnostics back: `mode=check` (lint, format and organize-imports) or `mode=lint`. Unlike most of the other tools it can mutate — pass `write` for safe fixes or `unsafe` for unsafe ones (both git-reversible) — so it returns the same diagnostics the Biome LSP surfaces for files you have edited.
52
52
  - `repos_inspect` — read-only inspection of vendored repositories: `mode=status` reports per-repo presence, the gitlink as a staged/committed/checked-out triple, working-tree dirtiness, and stale note ids; `mode=config` surfaces the validated `.repos/config.json` manifest and its entries; `mode=drift` reconciles the manifest, `.gitmodules`, the worktree, `git submodule status` and the repo's local git config, reporting any mismatch as a typed drift kind; `mode=gitmodules` lists the parsed `.gitmodules` entries. Returns markdown-escaped output since vendored-repo content is untrusted input. Backed by the same `silk-effects` `Repos` services the `savvy` CLI uses.
53
53
  - `repos_manage` — manages vendored repositories (mutating counterpart to repos_inspect): `action=sync` initializes any missing submodules (`git submodule update --init --depth 1`), applies sparse-checkout from the manifest, and clears stale git locks; `action=pin` fetches and checks out the new ref, staging the updated gitlink and manifest; `action=add` adds a new repo entry, optionally with the `orientation` block a preceding `remove` reported, so a re-vendor keeps it; `action=note` appends a short note to a repo; `action=remove` unvendors an entry and echoes back the entry it removed; `action=rename` renames one in place; `action=restore` hard-resets one or more dirty checkouts back to their pinned commit, and names any that stayed dirty afterwards instead of reporting the reset as a success. Pin, add, remove and rename stage git changes for the caller to commit; restore only resets the submodule's own working tree to its already-staged or already-committed gitlink commit, so there is nothing new to stage. `sync`, `pin`, `add`, `remove`, `rename` and `restore` unlock the vendored worktree for the duration of their git work and leave it read-only again afterwards (files `0444`, directories `0555` — an executable file locks at `0555` instead, unlocking back to `0755` rather than losing its executable bit), so an agent that hits `EACCES` writing into `.repos/` should route the change through this tool rather than `chmod`. The submodule gitdir stays writable and `sync`/`add` mark the boundary in local git config, so ordinary git commands skip these trees rather than failing against them. Backed by the same `silk-effects` `Repos` services the `savvy` CLI uses.
package/index.d.ts CHANGED
@@ -34,7 +34,8 @@ interface McpContext {
34
34
  * + `Path` from the host's platform layer (`NodeServices.layer` in bin.ts).
35
35
  *
36
36
  * @remarks
37
- * The kit graph (`Workspaces.layerWithGit`) mints a fresh layer reference per
37
+ * The kit graph (`Workspaces.layerWithGitAndConfigDependenciesSubprocess`)
38
+ * mints a fresh layer reference per
38
39
  * call, so it is bound to a `const` and provided ONCE via `Layer.provideMerge`
39
40
  * — layer memoization by reference then constructs each kit service exactly
40
41
  * once and exposes `WorkspaceRoot` on the runtime. The same discipline gives
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/mcp",
3
- "version": "2.5.2",
3
+ "version": "2.6.1",
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-rc.109",
35
35
  "@effected/commands": "^0.5.0",
36
- "@effected/git": "^0.9.0",
37
- "@effected/workspaces": "^0.17.2",
36
+ "@effected/git": "^0.10.0",
37
+ "@effected/workspaces": "^0.18.1",
38
38
  "@modelcontextprotocol/sdk": "^1.30.0",
39
- "@savvy-web/silk-effects": "7.0.1",
39
+ "@savvy-web/silk-effects": "7.1.1",
40
40
  "effect": "4.0.0-rc.109",
41
41
  "zod": "^4.4.3"
42
42
  }
package/runtime.js CHANGED
@@ -25,7 +25,8 @@ import { Effect, Layer } from "effect";
25
25
  * + `Path` from the host's platform layer (`NodeServices.layer` in bin.ts).
26
26
  *
27
27
  * @remarks
28
- * The kit graph (`Workspaces.layerWithGit`) mints a fresh layer reference per
28
+ * The kit graph (`Workspaces.layerWithGitAndConfigDependenciesSubprocess`)
29
+ * mints a fresh layer reference per
29
30
  * call, so it is bound to a `const` and provided ONCE via `Layer.provideMerge`
30
31
  * — layer memoization by reference then constructs each kit service exactly
31
32
  * once and exposes `WorkspaceRoot` on the runtime. The same discipline gives
@@ -39,7 +40,7 @@ import { Effect, Layer } from "effect";
39
40
  * ignored", identical to the savvy CLI).
40
41
  */
41
42
  const makeSilkRuntimeLayer = (cwd) => {
42
- const kitGraph = Workspaces.layerWithGit({ cwd });
43
+ const kitGraph = Workspaces.layerWithGitAndConfigDependenciesSubprocess({ cwd });
43
44
  const analyzerDeps = ChangesetConfigReader.layer;
44
45
  /**
45
46
  * `BranchAnalyzer` + `ReleasePlanner` + the ONE shared `ConfigInspector`,
package/server.js CHANGED
@@ -77,7 +77,7 @@ function buildServer(ctx) {
77
77
  return structuredResult(text, data);
78
78
  });
79
79
  server.registerTool("changeset_inspect", {
80
- description: "Read-only changeset analysis for the changeset-manager workflow. mode=branch diffs the current branch against its base and classifies every changed file by owning package (with packagesAffected and the unmapped paths to ask the user about). mode=config surfaces the resolved .changeset/config.json (release surfaces, versionFiles, ignore list). mode=classify maps arbitrary repo-relative paths to their owning package. Prefer this over shelling out to the savvy CLI.",
80
+ description: "Read-only changeset analysis for the changeset-manager workflow. mode=branch diffs the current branch against its base and classifies every changed file by owning package (with packagesAffected and the unmapped paths to ask the user about; an unmapped path may carry a machine-readable unmappedHint reason — e.g. a deleted versionFiles/additionalScopes target or a known template mirror — meaning it is probably already accounted for). mode=config surfaces the resolved .changeset/config.json (release surfaces, versionFiles, ignore list). mode=classify maps arbitrary repo-relative paths to their owning package. Prefer this over shelling out to the savvy CLI.",
81
81
  inputSchema: {
82
82
  mode: z.enum([
83
83
  "branch",
@@ -109,7 +109,7 @@ function buildServer(ctx) {
109
109
  return structuredResult(text, data);
110
110
  });
111
111
  server.registerTool("changeset_deps_detect", {
112
- description: "Read-only preview of the cumulative dependency diff (merge-base -> working tree) per workspace package. Returns each affected package's resolved dependency-table rows (catalog:/workspace: specifiers resolved to concrete versions; devDependencies retained) as the exact rows a pure-dependency changeset would carry. Does NOT write or delete any file. Prefer this over shelling out to savvy changeset deps detect.",
112
+ description: "Read-only preview of the cumulative dependency diff (merge-base -> working tree) per workspace package. Returns each affected package's resolved dependency-table rows (catalog:/workspace: specifiers resolved per side; devDependencies retained) as the exact rows a pure-dependency changeset would carry, plus a coexisting list of untouched prose-only changesets that reference an in-scope package (informational — no need to re-list .changeset/). Does NOT write or delete any file. Prefer this over shelling out to savvy changeset deps detect.",
113
113
  inputSchema: {
114
114
  base: z.optional(z.string()).describe("Override the base branch used to compute the merge-base."),
115
115
  package: z.optional(z.string()).describe("Restrict output to a single workspace package."),
@@ -135,7 +135,7 @@ function buildServer(ctx) {
135
135
  return structuredResult(text, data);
136
136
  });
137
137
  server.registerTool("changeset_deps_regen", {
138
- description: "Regenerate pure-dependency changesets: delete stale single-package Dependencies-only changesets and write fresh single-package, patch-bump changesets from the cumulative dependency diff (catalog:/workspace: resolved; devDependencies dropped). Mixed changesets (Dependencies plus other content) are left untouched. Set dryRun=true to preview the plan without touching the filesystem. NOTE: without dryRun this tool MUTATES .changeset/*.md (git-reversible). Prefer this over shelling out to savvy changeset deps regen.",
138
+ description: "Regenerate pure-dependency changesets: delete stale single-package Dependencies-only changesets and write fresh single-package, patch-bump changesets from the cumulative dependency diff (catalog:/workspace: resolved; devDependencies dropped). Mixed changesets (Dependencies plus other content) are left untouched, and the result's coexisting list accounts for untouched prose-only changesets that reference an in-scope package (informational — no need to re-list .changeset/). Set dryRun=true to preview the plan without touching the filesystem. NOTE: without dryRun this tool MUTATES .changeset/*.md (git-reversible). Prefer this over shelling out to savvy changeset deps regen.",
139
139
  inputSchema: {
140
140
  base: z.optional(z.string()).describe("Override the base branch used to compute the merge-base."),
141
141
  package: z.optional(z.string()).describe("Restrict regeneration to a single workspace package."),
@@ -9,10 +9,20 @@ const ChangesetDepsDetectPackage = Schema.Struct({
9
9
  relativePath: Schema.String,
10
10
  rows: Schema.Array(Changesets.DependencyTableRowSchema)
11
11
  }).annotate({ identifier: "ChangesetDepsDetectPackage" });
12
+ /**
13
+ * An untouched prose-only changeset releasing a package in scope for the run
14
+ * (#279) — informational, so the result accounts for every changeset touching
15
+ * an in-scope package.
16
+ */
17
+ const ChangesetDepsDetectCoexisting = Schema.Struct({
18
+ file: Schema.String,
19
+ packages: Schema.Array(Schema.String)
20
+ }).annotate({ identifier: "ChangesetDepsDetectCoexisting" });
12
21
  /** The `changeset_deps_detect` tool result. */
13
22
  const ChangesetDepsDetectResult = Schema.Struct({
14
23
  root: Schema.String,
15
- packages: Schema.Array(ChangesetDepsDetectPackage)
24
+ packages: Schema.Array(ChangesetDepsDetectPackage),
25
+ coexisting: Schema.Array(ChangesetDepsDetectCoexisting).annotate({ description: "Prose-only changesets referencing an in-scope package (no Dependencies section). Informational — a regen run would leave these untouched." })
16
26
  }).annotate({
17
27
  identifier: "ChangesetDepsDetectResult",
18
28
  title: "changeset_deps_detect result",
@@ -35,7 +45,17 @@ const mdInline = (value) => {
35
45
  };
36
46
  /** Render the structured result as a markdown transcript. */
37
47
  const renderMarkdown = (data) => {
38
- if (data.packages.length === 0) return `# changeset deps detect — ${mdInline(data.root)}\n\nNo dependency changes detected.`;
48
+ const coexistingLines = data.coexisting.length === 0 ? [] : [
49
+ ``,
50
+ `## Coexisting prose changesets (untouched by regen)`,
51
+ ``,
52
+ ...data.coexisting.map((c) => `- ${mdInline(c.file)} (${c.packages.map(mdInline).join(", ")})`)
53
+ ];
54
+ if (data.packages.length === 0) return [
55
+ `# changeset deps detect — ${mdInline(data.root)}`,
56
+ ``,
57
+ `No dependency changes detected.`
58
+ ].concat(coexistingLines).join("\n").trimEnd();
39
59
  const lines = [`# changeset deps detect — ${mdInline(data.root)}`, ``];
40
60
  for (const pkg of data.packages) {
41
61
  lines.push(`## ${mdInline(pkg.package)} — ${mdInline(pkg.relativePath)}`, ``);
@@ -43,6 +63,7 @@ const renderMarkdown = (data) => {
43
63
  for (const r of pkg.rows) lines.push(`| ${mdInline(r.dependency)} | ${r.type} | ${r.action} | ${mdInline(r.from)} | ${mdInline(r.to)} |`);
44
64
  lines.push(``);
45
65
  }
66
+ lines.push(...coexistingLines);
46
67
  return lines.join("\n").trimEnd();
47
68
  };
48
69
  /** One-way transform: result to markdown. Encoding back is forbidden. */
@@ -58,22 +79,27 @@ const ChangesetDepsDetectAsMarkdown = ChangesetDepsDetectResult.pipe(Schema.deco
58
79
  */
59
80
  const changesetDepsDetect = (args, fallbackCwd) => Effect.gen(function* () {
60
81
  const root = yield* (yield* WorkspaceRoot).find(args.cwd ?? fallbackCwd);
82
+ const plan = yield* (yield* Changesets.DepsRegen).plan({
83
+ cwd: root,
84
+ includeDevDeps: true,
85
+ ...args.base ? { base: args.base } : {},
86
+ ...args.package ? { package: args.package } : {},
87
+ ...args.packages && args.packages.length > 0 ? { packages: args.packages } : {},
88
+ ...args.exclude && args.exclude.length > 0 ? { exclude: args.exclude } : {}
89
+ });
61
90
  return {
62
91
  root,
63
- packages: (yield* (yield* Changesets.DepsRegen).plan({
64
- cwd: root,
65
- includeDevDeps: true,
66
- ...args.base ? { base: args.base } : {},
67
- ...args.package ? { package: args.package } : {},
68
- ...args.packages && args.packages.length > 0 ? { packages: args.packages } : {},
69
- ...args.exclude && args.exclude.length > 0 ? { exclude: args.exclude } : {}
70
- })).toWrite.map((entry) => ({
92
+ packages: plan.toWrite.map((entry) => ({
71
93
  package: entry.diff.package,
72
94
  relativePath: entry.diff.relativePath,
73
95
  rows: entry.diff.rows
96
+ })),
97
+ coexisting: plan.coexisting.map((entry) => ({
98
+ file: entry.file,
99
+ packages: [...entry.packages]
74
100
  }))
75
101
  };
76
102
  });
77
103
 
78
104
  //#endregion
79
- export { ChangesetDepsDetectAsMarkdown, ChangesetDepsDetectPackage, ChangesetDepsDetectResult, changesetDepsDetect };
105
+ export { ChangesetDepsDetectAsMarkdown, ChangesetDepsDetectCoexisting, ChangesetDepsDetectPackage, ChangesetDepsDetectResult, changesetDepsDetect };
@@ -3,12 +3,22 @@ import { Changesets } from "@savvy-web/silk-effects";
3
3
  import { Effect, Schema, SchemaGetter } from "effect";
4
4
 
5
5
  //#region src/tools/changeset-deps-regen.ts
6
+ /**
7
+ * An untouched prose-only changeset releasing a package in scope for the run
8
+ * (#279) — informational, so the result accounts for every changeset touching
9
+ * an in-scope package.
10
+ */
11
+ const ChangesetCoexistingEntry = Schema.Struct({
12
+ file: Schema.String,
13
+ packages: Schema.Array(Schema.String)
14
+ }).annotate({ identifier: "ChangesetCoexistingEntry" });
6
15
  /** The `changeset_deps_regen` tool result. */
7
16
  const ChangesetDepsRegenResult = Schema.Struct({
8
17
  root: Schema.String,
9
18
  deleted: Schema.Array(Schema.String),
10
19
  written: Schema.Array(Schema.String),
11
20
  skippedMixed: Schema.Array(Schema.String),
21
+ coexisting: Schema.Array(ChangesetCoexistingEntry).annotate({ description: "Prose-only changesets referencing an in-scope package that this run left untouched (no Dependencies section). Informational — no need to re-list .changeset/ to confirm a package's full changeset picture." }),
12
22
  dryRun: Schema.Boolean
13
23
  }).annotate({
14
24
  identifier: "ChangesetDepsRegenResult",
@@ -31,7 +41,7 @@ const mdInline = (value) => {
31
41
  /** Render the structured result as a markdown transcript. */
32
42
  const renderMarkdown = (data) => {
33
43
  const heading = `# changeset deps regen — ${mdInline(data.root)}${data.dryRun ? " (dry run)" : ""}`;
34
- if (data.deleted.length === 0 && data.written.length === 0 && data.skippedMixed.length === 0) return `${heading}\n\nNo dependency changes to regenerate.`;
44
+ if (data.deleted.length === 0 && data.written.length === 0 && data.skippedMixed.length === 0 && data.coexisting.length === 0) return `${heading}\n\nNo dependency changes to regenerate.`;
35
45
  const lines = [heading, ``];
36
46
  const verb = data.dryRun ? "Would delete" : "Deleted";
37
47
  if (data.deleted.length > 0) {
@@ -47,6 +57,11 @@ const renderMarkdown = (data) => {
47
57
  if (data.skippedMixed.length > 0) {
48
58
  lines.push(`Skipped ${data.skippedMixed.length} mixed changeset(s):`);
49
59
  for (const file of data.skippedMixed) lines.push(`- ${mdInline(file)}`);
60
+ lines.push(``);
61
+ }
62
+ if (data.coexisting.length > 0) {
63
+ lines.push(`Coexisting prose changeset(s) for in-scope packages, left untouched:`);
64
+ for (const entry of data.coexisting) lines.push(`- ${mdInline(entry.file)} (${entry.packages.map(mdInline).join(", ")})`);
50
65
  }
51
66
  return lines.join("\n").trimEnd();
52
67
  };
@@ -76,6 +91,10 @@ const changesetDepsRegen = (args, fallbackCwd) => Effect.gen(function* () {
76
91
  deleted: plan.toDelete.map((entry) => entry.file),
77
92
  written: plan.toWrite.map((entry) => entry.file),
78
93
  skippedMixed: [...plan.skippedMixed],
94
+ coexisting: plan.coexisting.map((entry) => ({
95
+ file: entry.file,
96
+ packages: [...entry.packages]
97
+ })),
79
98
  dryRun: true
80
99
  };
81
100
  const result = yield* service.execute(plan);
@@ -84,9 +103,13 @@ const changesetDepsRegen = (args, fallbackCwd) => Effect.gen(function* () {
84
103
  deleted: [...result.deleted],
85
104
  written: [...result.written],
86
105
  skippedMixed: [...result.skippedMixed],
106
+ coexisting: result.coexisting.map((entry) => ({
107
+ file: entry.file,
108
+ packages: [...entry.packages]
109
+ })),
87
110
  dryRun: false
88
111
  };
89
112
  });
90
113
 
91
114
  //#endregion
92
- export { ChangesetDepsRegenAsMarkdown, ChangesetDepsRegenResult, changesetDepsRegen };
115
+ export { ChangesetCoexistingEntry, ChangesetDepsRegenAsMarkdown, ChangesetDepsRegenResult, changesetDepsRegen };
@@ -54,8 +54,13 @@ const renderMarkdown = (data) => {
54
54
  lines.push(`- ${mdInline(f.status)} ${mdInline(f.path)} -> ${owner}`);
55
55
  }
56
56
  if (r.unmappedFiles.length > 0) {
57
+ const hints = /* @__PURE__ */ new Map();
58
+ for (const f of r.files) if (typeof f.reason === "object" && f.reason !== null && f.reason.kind === "unmappedHint") hints.set(f.path, f.reason.hint);
57
59
  lines.push(``, `## Unmapped (ask the user)`);
58
- for (const p of r.unmappedFiles) lines.push(`- ${mdInline(p)}`);
60
+ for (const p of r.unmappedFiles) {
61
+ const hint = hints.get(p);
62
+ lines.push(hint === void 0 ? `- ${mdInline(p)}` : `- ${mdInline(p)} — ${mdInline(hint)}`);
63
+ }
59
64
  }
60
65
  return lines.join("\n");
61
66
  }
@@ -83,7 +88,8 @@ const renderMarkdown = (data) => {
83
88
  const lines = [`# changeset classify`, ``];
84
89
  for (const c of data.result) {
85
90
  const owner = c.package ? mdInline(c.package) : mdInline("<unmapped>");
86
- lines.push(`- ${mdInline(c.path)} -> ${owner}`);
91
+ const hint = typeof c.reason === "object" && c.reason !== null && c.reason.kind === "unmappedHint" ? ` — ${mdInline(c.reason.hint)}` : "";
92
+ lines.push(`- ${mdInline(c.path)} -> ${owner}${hint}`);
87
93
  }
88
94
  if (data.result.length === 0) lines.push("(no paths)");
89
95
  return lines.join("\n");
@@ -102,7 +108,7 @@ const ChangesetInspectAsMarkdown = ChangesetInspectResult.pipe(Schema.decodeTo(S
102
108
  const changesetInspect = (args, fallbackCwd) => Effect.gen(function* () {
103
109
  const root = yield* (yield* WorkspaceRoot).find(args.cwd ?? fallbackCwd);
104
110
  const inspector = yield* Changesets.ConfigInspector;
105
- yield* inspector.refresh();
111
+ yield* inspector.refreshIn(root);
106
112
  switch (args.mode) {
107
113
  case "branch": return {
108
114
  mode: "branch",