@gotgenes/pi-subagents 6.16.0 → 6.16.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [6.16.2](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.16.1...pi-subagents-v6.16.2) (2026-05-23)
9
+
10
+
11
+ ### Documentation
12
+
13
+ * fix plan for narrow UI context ([#146](https://github.com/gotgenes/pi-packages/issues/146)) ([3d5b591](https://github.com/gotgenes/pi-packages/commit/3d5b591c0668ffcd9f66a6fc9a2c5d57236865af))
14
+ * mark Step N complete in architecture doc ([#146](https://github.com/gotgenes/pi-packages/issues/146)) ([9948869](https://github.com/gotgenes/pi-packages/commit/9948869b849817dac6f221f1e90db5e47fd12d31))
15
+ * plan narrow UI context for menu handlers ([#146](https://github.com/gotgenes/pi-packages/issues/146)) ([88318b4](https://github.com/gotgenes/pi-packages/commit/88318b4550fe95fc70b1a9ee1e904c608a2189a3))
16
+ * **retro:** add retro notes for issue [#148](https://github.com/gotgenes/pi-packages/issues/148) ([982fe51](https://github.com/gotgenes/pi-packages/commit/982fe51d7e27b7a127605710ced709bbd6291a0f))
17
+
18
+ ## [6.16.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.16.0...pi-subagents-v6.16.1) (2026-05-23)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * **pi-subagents:** theme parentheses and separator in formatSessionTokens ([33b67f6](https://github.com/gotgenes/pi-packages/commit/33b67f63915563c41605addb885af52b47844e96))
24
+
25
+
26
+ ### Documentation
27
+
28
+ * plan split AgentWidget rendering from lifecycle ([#148](https://github.com/gotgenes/pi-packages/issues/148)) ([24c11d5](https://github.com/gotgenes/pi-packages/commit/24c11d51f2b6c9d2712815fbe46ede72084ffcbb))
29
+ * **retro:** add retro notes for issue [#144](https://github.com/gotgenes/pi-packages/issues/144) ([f3cdfd4](https://github.com/gotgenes/pi-packages/commit/f3cdfd46fe223d6cecb5dad0d739f053e7468433))
30
+ * update architecture for widget rendering extraction ([#148](https://github.com/gotgenes/pi-packages/issues/148)) ([450707e](https://github.com/gotgenes/pi-packages/commit/450707e1d0f41ae78f1a655ab350fd8f4fd64125))
31
+
8
32
  ## [6.16.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.15.0...pi-subagents-v6.16.0) (2026-05-23)
9
33
 
10
34
 
@@ -69,7 +69,8 @@ renderer.ts - notification TUI component
69
69
  record-observer.ts - session-event observer for record statistics
70
70
 
71
71
  ui/display.ts - pure formatters, display helpers, and shared types (Theme, AgentDetails)
72
- ui/agent-widget.ts - above-editor live status widget
72
+ ui/agent-widget.ts - above-editor live status widget (thin lifecycle wrapper)
73
+ ui/widget-renderer.ts - pure rendering functions for agent widget
73
74
  ui/agent-menu.ts - /agents slash command menu
74
75
  ui/conversation-viewer.ts - scrollable session overlay
75
76
  ui/ui-observer.ts - session-event observer for UI streaming
@@ -615,20 +616,20 @@ Phase 9 targets the next layer: observation model consolidation, `ExtensionConte
615
616
 
616
617
  ### Current smells
617
618
 
618
- | Smell | Location | Evidence | Severity |
619
- | ------------------------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
620
- | `execute` does config resolution for its callees | `agent-tool.ts` (145-line `execute`) | ~60 lines unpack config, resolve model, compute metadata, repack into 16-field bags for spawners; `ctx` threaded 4 layers deep | Medium |
621
- | Wide `ctx` in menu handlers | `agent-menu.ts`, `agent-config-editor.ts`, `agent-creation-wizard.ts` | Functions declare `ctx: ExtensionContext` but only call `ctx.ui.select/confirm/input/notify/editor`; 43 `ctx as any` casts across 3 test files | Medium |
622
- | Direct SDK import in `conversation-viewer.ts` | `conversation-viewer.test.ts` | Hoisted `vi.mock("@earendil-works/pi-tui")` to intercept `wrapTextWithAnsi` | Low |
623
- | Widget mixes rendering, lifecycle, and state | `agent-widget.ts` (370 lines) | `renderWidget` is ~109 lines mixing data collection, formatting, and overflow layout; constructor takes 3 concrete collaborators | Low |
624
- | `deps.` prefix noise in function bodies | remaining modules across tools, UI, service-adapter | Functions accept a `deps` bag and access every field as `deps.foo`; hides real dependencies and lengthens every call line | Low |
619
+ | Smell | Location | Evidence | Severity |
620
+ | ------------------------------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------- |
621
+ | `execute` does config resolution for its callees | `agent-tool.ts` (145-line `execute`) | ~60 lines unpack config, resolve model, compute metadata, repack into 16-field bags for spawners; `ctx` threaded 4 layers deep | Medium |
622
+ | ~~Wide `ctx` in menu handlers~~ | ~~`agent-menu.ts`, `agent-config-editor.ts`, `agent-creation-wizard.ts`~~ | Resolved by #146: `MenuUI` interface introduced; 42 `ctx as any` casts eliminated across 5 test files | Done |
623
+ | Direct SDK import in `conversation-viewer.ts` | `conversation-viewer.test.ts` | Hoisted `vi.mock("@earendil-works/pi-tui")` to intercept `wrapTextWithAnsi` | Low |
624
+ | ~~Widget mixes rendering, lifecycle, and state~~ | ~~`agent-widget.ts` (370 lines)~~ | Resolved by #148: rendering extracted to `widget-renderer.ts`; widget is now 198 lines | Done |
625
+ | `deps.` prefix noise in function bodies | remaining modules across tools, UI, service-adapter | Functions accept a `deps` bag and access every field as `deps.foo`; hides real dependencies and lengthens every call line | Low |
625
626
 
626
627
  ### Dependency bag convention
627
628
 
628
629
  Applied incrementally as each step touches a module:
629
630
 
630
- - **≤4 fields** accept as plain parameters; drop the interface.
631
- - **≥5 fields** keep a named interface but destructure in the function signature (`{ manager, widget }: ForegroundDeps`) so the function body uses bare names, not `deps.foo`.
631
+ - **≤4 fields** - accept as plain parameters; drop the interface.
632
+ - **≥5 fields** - keep a named interface but destructure in the function signature (`{ manager, widget }: ForegroundDeps`) so the function body uses bare names, not `deps.foo`.
632
633
 
633
634
  This eliminates the `deps.` prefix noise across ~124 callsites in 12 modules.
634
635
 
@@ -664,19 +665,23 @@ After this step, `ExtensionContext` appears only in:
664
665
 
665
666
  Impact: `execute` dropped from ~145 to ~25 lines; eliminated 16-field parameter bags; eliminated `vi.mock("../src/parent-snapshot.js")` in `agent-manager.test.ts`; foreground/background runner tests no longer need `ctx` mocks; `AgentManager` operates entirely on domain types.
666
667
 
667
- ### Step N: Narrow UI context for menu handlers (#146)
668
+ ### Step N: Narrow UI context for menu handlers (#146)
668
669
 
669
- Define a `MenuUI` interface with `select`, `confirm`, `input`, `notify`, and `editor` methods.
670
- Menu handler functions (`showAgentsMenu`, `showAgentDetail`, `showCreateWizard`, etc.) accept `MenuUI` instead of `ExtensionContext`.
671
- `index.ts` passes `ctx.ui` at the call site.
670
+ Defined `MenuUI` interface (exported from `agent-menu.ts`) with `select`, `confirm`, `input`, `notify`, `editor`, and `custom` methods — the exact subset `ctx.ui` methods used by menu handlers.
671
+ All inner functions in `agent-menu.ts`, `agent-config-editor.ts`, and `agent-creation-wizard.ts` now accept `(ui: MenuUI)` instead of `(ctx: ExtensionContext)`.
672
+ `index.ts` passes `ctx.ui`, `ctx.modelRegistry`, and `buildParentSnapshot(ctx)` to the handler.
672
673
 
673
- Creation wizard’s `spawnAndWait` call changes: the narrow `AgentMenuManager.spawnAndWait` accepts `ParentSnapshot` (enabled by Step M) instead of `ExtensionContext`.
674
+ `AgentMenuManager.spawnAndWait` and `WizardManager.spawnAndWait` both accept `ParentSnapshot` (enabled by Step M).
675
+ Creation wizard threads `parentSnapshot` from `showCreateWizard(ui, parentSnapshot)` → `showGenerateWizard(ui, parentSnapshot, targetDir)` → `manager.spawnAndWait(parentSnapshot, ...)`.
674
676
 
675
- Apply the dependency bag convention to touched modules: `AgentConfigEditorDeps` (4 fields), `SteerToolDeps` (4 fields), and `GetResultDeps` (4 fields) become plain parameters; `AgentMenuDeps` (8 fields) and `AgentCreationWizardDeps` (5 fields) are destructured in the signature.
677
+ Applied the dependency bag convention:
676
678
 
677
- After Steps M and N, `ExtensionContext` appears only at true boundaries: `index.ts` closures, `service-adapter.ts` (cross-extension bridge), and `index.ts` (extension entry point).
679
+ - `AgentConfigEditorDeps` (4 fields), `GetResultDeps` (4 fields), `SteerToolDeps` (4 fields) dissolved into plain parameters.
680
+ - `AgentMenuDeps` (8 fields) and `AgentCreationWizardDeps` (5 fields) kept as interfaces, destructured in the function signature.
678
681
 
679
- Impact: eliminates ~43 `ctx as any` casts across menu, editor, and wizard test files; tests construct a plain object satisfying `MenuUI` with no cast.
682
+ After Steps M and N, `ExtensionContext` appears only at true boundaries: `index.ts` closures and `service-adapter.ts` (cross-extension bridge).
683
+
684
+ Impact: eliminated 42 `ctx as any` casts across 5 test files (`agent-menu.test.ts`: 8, `agent-config-editor.test.ts`: 20, `agent-creation-wizard.test.ts`: 14); tests construct plain `MenuUI`-shaped objects with no cast.
680
685
 
681
686
  ### Step O: Inject text wrapping into ConversationViewer (#147)
682
687
 
@@ -688,13 +693,11 @@ Apply the dependency bag convention: `ConversationViewerOptions` is destructured
688
693
 
689
694
  Impact: eliminates the hoisted `vi.mock("@earendil-works/pi-tui")` in `conversation-viewer.test.ts`.
690
695
 
691
- ### Step P: Split AgentWidget rendering (#148)
692
-
693
- Extract pure rendering functions from `AgentWidget` into `ui/widget-renderer.ts`.
694
- The widget becomes a thin lifecycle/polling wrapper that calls pure render functions.
695
- Rendering functions receive data (agent list, activity map, registry) and return formatted strings - testable without widget lifecycle.
696
+ ### Step P: Split AgentWidget rendering (#148)
696
697
 
697
- Depends on Step L: once the tracker drops stats fields, the renderer reads from `AgentRecord` for tool uses and usage, and from `AgentActivityTracker` only for live UI state (active tools, response text, turn count).
698
+ Extracted pure rendering functions (`renderWidgetLines`, `renderFinishedLine`, `renderRunningLines`) from `AgentWidget` into `ui/widget-renderer.ts`.
699
+ The widget is now a thin lifecycle/polling wrapper (198 lines, down from 374) that delegates to pure render functions.
700
+ Rendering functions receive data (agent list, activity map, registry) and return formatted strings - testable without widget lifecycle. 23 new unit tests cover all status variants, overflow, tree connectors, and empty states.
698
701
 
699
702
  ### Step dependencies
700
703
 
@@ -0,0 +1,319 @@
1
+ ---
2
+ issue: 146
3
+ issue_title: "Narrow UI context for menu handlers (Phase 9, Step N)"
4
+ ---
5
+
6
+ # Narrow UI context for menu handlers
7
+
8
+ ## Problem Statement
9
+
10
+ Menu handler functions (`showAgentsMenu`, `showAgentDetail`, `showCreateWizard`, etc.) declare `ctx: ExtensionContext` but only call `ctx.ui.select/confirm/input/notify/editor/custom` and `ctx.modelRegistry`.
11
+ This forces 42 `ctx as any` casts across 3 test files (`agent-menu.test.ts`: 8, `agent-config-editor.test.ts`: 20, `agent-creation-wizard.test.ts`: 14) because tests cannot construct a full `ExtensionContext`.
12
+
13
+ ## Goals
14
+
15
+ - Define a `MenuUI` interface with the subset of `ctx.ui` methods that menu handlers actually use (`select`, `confirm`, `input`, `notify`, `editor`, `custom`).
16
+ - Menu handler functions accept `MenuUI` (plus `modelRegistry` passed separately) instead of `ExtensionContext`.
17
+ - `index.ts` handler registration extracts `ctx.ui` and `ctx.modelRegistry` from the SDK `ExtensionContext`.
18
+ - Change `WizardManager.spawnAndWait` to accept `ParentSnapshot` (introduced by #145) instead of `ExtensionContext`.
19
+ - Apply dependency bag convention: dissolve ≤4-field deps into plain parameters; keep ≥5-field interfaces but destructure in signature.
20
+ - Eliminate all 42 `ctx as any` casts from menu, editor, and wizard test files.
21
+
22
+ ## Non-Goals
23
+
24
+ - Changing the behavior of `ctx.ui.custom` — pass-through only.
25
+ - Narrowing `ExtensionContext` usage in `index.ts` closures (the `as any` casts for `runtime.currentCtx?.ctx` are addressed separately).
26
+ - Injecting `modelRegistry` further (already a narrow interface from `model-resolver.ts`).
27
+
28
+ ## Background
29
+
30
+ ### Dependency: #145 (Step M) — Decompose execute
31
+
32
+ Issue #145 is **closed/implemented**.
33
+ `buildParentSnapshot(ctx)` converts `ExtensionContext` → `ParentSnapshot` at the call site.
34
+ This enables `WizardManager.spawnAndWait` to accept `ParentSnapshot` instead of `ExtensionContext`.
35
+
36
+ ### Existing modules
37
+
38
+ - `agent-menu.ts` (296 lines) — menu handler factory, 8-field `AgentMenuDeps`, all inner functions take `ctx: ExtensionContext`
39
+ - `agent-config-editor.ts` (202 lines) — `AgentConfigEditorDeps` (4 fields), `showAgentDetail` takes `ctx: ExtensionContext`
40
+ - `agent-creation-wizard.ts` (246 lines) — `AgentCreationWizardDeps` (5 fields), `WizardManager.spawnAndWait` takes `ctx: ExtensionContext`
41
+ - `tools/get-result-tool.ts` — `GetResultDeps` (4 fields)
42
+ - `tools/steer-tool.ts` — `SteerToolDeps` (4 fields)
43
+ - `index.ts` — wires everything, handler registration extracts `ctx.ui` and passes `ExtensionContext`
44
+
45
+ ### ExtensionContext usage in menu handlers
46
+
47
+ Every `ctx` reference in the three menu UI modules maps to exactly one of:
48
+
49
+ - `ctx.ui.select(...)` — 9 call sites
50
+ - `ctx.ui.confirm(...)` — 5 call sites
51
+ - `ctx.ui.input(...)` — 7 call sites
52
+ - `ctx.ui.notify(...)` — 15 call sites
53
+ - `ctx.ui.editor(...)` — 2 call sites
54
+ - `ctx.ui.custom(...)` — 1 call site (conversation viewer overlay)
55
+ - `ctx.modelRegistry` — 1 call site (model label resolution in `showAllAgentsList`)
56
+
57
+ No other `ExtensionContext` properties (session, tools, hooks, etc.) are accessed.
58
+
59
+ ## Design Overview
60
+
61
+ ### MenuUI interface
62
+
63
+ A narrow interface capturing only the `ctx.ui` methods used by menu handlers:
64
+
65
+ ```typescript
66
+ export interface MenuUI {
67
+ select(title: string, options: string[]): Promise<string | undefined>;
68
+ confirm(title: string, message: string): Promise<boolean>;
69
+ input(title: string, defaultValue?: string): Promise<string | undefined>;
70
+ notify(message: string, level: "info" | "warning" | "error"): void;
71
+ editor(title: string, content: string): Promise<string | undefined>;
72
+ custom<R>(component: any, options?: any): Promise<R>;
73
+ }
74
+ ```
75
+
76
+ `select` uses a plain `string` return (not a generic `<T extends string>`) to match the SDK's structural signature.
77
+
78
+ `modelRegistry` is not included in `MenuUI` — it is not a UI concern.
79
+ Instead, the handler registration in `index.ts` passes it separately.
80
+
81
+ ### Handler signature change
82
+
83
+ The menu handler currently receives `ExtensionContext` directly:
84
+
85
+ ```typescript
86
+ // index.ts — before
87
+ handler: async (_args, ctx) => { await agentsMenuHandler(ctx); },
88
+ ```
89
+
90
+ After this change, `index.ts` destructures what each handler needs:
91
+
92
+ ```typescript
93
+ // index.ts — after
94
+ handler: async (_args, ctx) => {
95
+ await agentsMenuHandler({
96
+ ui: ctx.ui,
97
+ modelRegistry: ctx.modelRegistry,
98
+ parentSnapshot: buildParentSnapshot(ctx),
99
+ });
100
+ },
101
+ ```
102
+
103
+ In `agent-menu.ts`, the return type changes from `(ctx: ExtensionContext) => Promise<void>` to a function that accepts `{ ui: MenuUI; modelRegistry: ModelRegistry; parentSnapshot: ParentSnapshot }`.
104
+ The `ExtensionContext` import is removed from `agent-menu.ts`, `agent-config-editor.ts`, and `agent-creation-wizard.ts`.
105
+
106
+ `modelRegistry` is threaded from the handler through `showAgentsMenu` → `showAllAgentsList` (the only consumer).
107
+ `parentSnapshot` is threaded from the handler through `showAgentsMenu` → `wizard.showCreateWizard` → `showGenerateWizard` (the only consumer).
108
+
109
+ ### Wizard spawnAndWait — drop ctx parameter
110
+
111
+ `WizardManager.spawnAndWait` currently takes `ctx: ExtensionContext` as its first parameter and passes it to `deps.manager.spawnAndWait(ctx, ...)`.
112
+ Once the menu handler no longer receives `ExtensionContext`, the wizard has no `ctx` to pass.
113
+
114
+ Thread `parentSnapshot` as a parameter from the handler through the wizard, keeping `AgentMenuManager.spawnAndWait` accepting `ParentSnapshot` as its first parameter (consistent with `AgentManager.spawnAndWait`).
115
+ The wizard's `showGenerateWizard` receives `parentSnapshot` and passes it to `deps.manager.spawnAndWait(parentSnapshot, ...)`.
116
+
117
+ ```typescript
118
+ // agent-creation-wizard.ts — after
119
+ async function showGenerateWizard(
120
+ ui: MenuUI,
121
+ parentSnapshot: ParentSnapshot,
122
+ targetDir: string,
123
+ ) {
124
+ // ...
125
+ const record = await deps.manager.spawnAndWait(
126
+ parentSnapshot, "general-purpose", generatePrompt, { ... },
127
+ );
128
+ }
129
+ ```
130
+
131
+ The creation wizard no longer imports `ExtensionContext`.
132
+
133
+ ### Dependency bag convention
134
+
135
+ Per `docs/architecture/architecture.md` § Dependency bag convention:
136
+
137
+ - **≤4 fields** → dissolve the interface, accept as plain parameters.
138
+ - **≥5 fields** → keep the interface but destructure in the function signature.
139
+
140
+ #### Dissolve (≤4 fields)
141
+
142
+ `AgentConfigEditorDeps` (4 fields: `fileOps`, `registry`, `personalAgentsDir`, `projectAgentsDir`) → plain parameters on `createAgentConfigEditor`.
143
+
144
+ `GetResultDeps` (4 fields: `getRecord`, `cancelNudge`, `getConversation`, `registry`) → plain parameters on `createGetResultTool`.
145
+
146
+ `SteerToolDeps` (4 fields: `getRecord`, `emitEvent`, `steerAgent`, `queueSteer`) → plain parameters on `createSteerTool`.
147
+
148
+ #### Keep + destructure (≥5 fields)
149
+
150
+ `AgentMenuDeps` (8 fields) — keep the interface, destructure in `createAgentsMenuHandler({ manager, registry, ... })`.
151
+
152
+ `AgentCreationWizardDeps` (5 fields) — keep the interface, destructure in `createAgentCreationWizard({ fileOps, manager, ... })`.
153
+
154
+ ### Consumer call-site sketch (menu handler registration)
155
+
156
+ ```typescript
157
+ // index.ts
158
+ pi.registerCommand('agents', {
159
+ description: 'Manage agents',
160
+ handler: async (_args, ctx) => {
161
+ await agentsMenuHandler({
162
+ ui: ctx.ui,
163
+ modelRegistry: ctx.modelRegistry,
164
+ parentSnapshot: buildParentSnapshot(ctx),
165
+ });
166
+ },
167
+ });
168
+ ```
169
+
170
+ ### Extracted module interaction sketch (agent-config-editor)
171
+
172
+ ```typescript
173
+ // agent-config-editor.ts — after dissolving deps
174
+ export function createAgentConfigEditor(
175
+ fileOps: AgentFileOps,
176
+ registry: AgentTypeRegistry,
177
+ personalAgentsDir: string,
178
+ projectAgentsDir: string,
179
+ ) {
180
+ // ... closures capture these directly; no deps.foo indirection
181
+ }
182
+ ```
183
+
184
+ No Tell-Don't-Ask violations — each parameter is a primitive or injectable collaborator.
185
+ No output-argument mutations — pure closure capture.
186
+
187
+ ## Module-Level Changes
188
+
189
+ ### New file: none
190
+
191
+ All changes are modifications to existing files.
192
+
193
+ ### Modified: `src/ui/agent-menu.ts`
194
+
195
+ - Add `MenuUI` interface export (the new narrow type).
196
+ - Import `ModelRegistry` from `model-resolver.js`.
197
+ - Remove `ExtensionContext` import.
198
+ - Change all inner function signatures from `(ctx: ExtensionContext)` to `(ui: MenuUI)`.
199
+ - Replace `ctx.ui.xxx(...)` → `ui.xxx(...)`.
200
+ - Replace `ctx.modelRegistry` → parameter `modelRegistry` threaded to `showAllAgentsList`.
201
+ - Change `AgentMenuDeps` usage: destructure in `createAgentsMenuHandler` signature.
202
+ - Change return type from `(ctx: ExtensionContext) => Promise<void>` to `(params: { ui: MenuUI; modelRegistry: ModelRegistry; parentSnapshot: ParentSnapshot }) => Promise<void>`.
203
+ - Thread `modelRegistry` from handler through `showAgentsMenu` → `showAllAgentsList`.
204
+ - Thread `parentSnapshot` from handler through `showAgentsMenu` → `wizard.showCreateWizard` → `showGenerateWizard`.
205
+ - Update `AgentMenuManager.spawnAndWait` to accept `ParentSnapshot` instead of `ExtensionContext`.
206
+ - Remove `Omit<AgentSpawnConfig, "isBackground">` in favor of plain inline type.
207
+
208
+ ### Modified: `src/ui/agent-config-editor.ts`
209
+
210
+ - Remove `ExtensionContext` import.
211
+ - Add `MenuUI` import from `agent-menu.js`.
212
+ - Change all inner function signatures from `(ctx: ExtensionContext)` to `(ui: MenuUI)`.
213
+ - Replace `ctx.ui.xxx(...)` → `ui.xxx(...)`.
214
+ - Dissolve `AgentConfigEditorDeps`: replace single deps parameter with 4 plain parameters.
215
+
216
+ ### Modified: `src/ui/agent-creation-wizard.ts`
217
+
218
+ - Remove `ExtensionContext` import.
219
+ - Add `MenuUI` import from `agent-menu.js`.
220
+ - Add `ParentSnapshot` import from `parent-snapshot.js`.
221
+ - Change all inner function signatures from `(ctx: ExtensionContext)` to `(ui: MenuUI)`.
222
+ - Replace `ctx.ui.xxx(...)` → `ui.xxx(...)`.
223
+ - Change `WizardManager.spawnAndWait` to accept `ParentSnapshot` instead of `ExtensionContext`.
224
+ - Thread `parentSnapshot` as a parameter from `showCreateWizard(ui, parentSnapshot)` → `showGenerateWizard(ui, parentSnapshot, targetDir)`.
225
+ - Destructure `AgentCreationWizardDeps` in signature.
226
+
227
+ ### Modified: `src/tools/get-result-tool.ts`
228
+
229
+ - Dissolve `GetResultDeps`: replace single deps parameter with 4 plain parameters.
230
+
231
+ ### Modified: `src/tools/steer-tool.ts`
232
+
233
+ - Dissolve `SteerToolDeps`: replace single deps parameter with 4 plain parameters.
234
+
235
+ ### Modified: `src/index.ts`
236
+
237
+ - Update `createAgentConfigEditor` call: pass 4 plain args instead of `AgentConfigEditorDeps`.
238
+ - Update `createAgentCreationWizard` call: pass 4 plain args instead of `AgentCreationWizardDeps` (registry is the `WizardRegistry`, not the full `AgentTypeRegistry` — pass `{ reload: () => registry.reload() }`).
239
+ - Update `createGetResultTool` call: pass 4 plain args instead of `GetResultDeps`.
240
+ - Update `createSteerTool` call: pass 4 plain args instead of `SteerToolDeps`.
241
+ - Update `spawnAndWait` in menu handler deps: keep `ParentSnapshot` as first parameter.
242
+ - Update `/agents` command handler to destructure `ctx.ui`, `ctx.modelRegistry`, and `buildParentSnapshot(ctx)`.
243
+
244
+ ### Modified: test files
245
+
246
+ - `test/ui/agent-menu.test.ts` — remove `ctx as any` casts; pass `{ ui: { ... }, modelRegistry: {}, parentSnapshot: {} }`.
247
+ - `test/ui/agent-config-editor.test.ts` — remove `ctx as any` casts; pass `MenuUI` objects directly.
248
+ - `test/ui/agent-creation-wizard.test.ts` — remove `ctx as any` casts; pass `MenuUI` and stub `ParentSnapshot`.
249
+ - `test/tools/get-result-tool.test.ts` — update `makeDeps` and `execute` helpers for dissolved parameters.
250
+ - `test/tools/steer-tool.test.ts` — update `makeDeps` and `execute` helpers for dissolved parameters.
251
+
252
+ ### Unchanged
253
+
254
+ - `src/ui/conversation-viewer.ts` — unrelated; uses its own deps.
255
+ - `src/ui/agent-widget.ts` — already narrow (no `ExtensionContext`).
256
+ - `src/agent-manager.ts` — already accepts `ParentSnapshot` from #145.
257
+ - `src/parent-snapshot.ts` — unchanged.
258
+
259
+ ## Test Impact Analysis
260
+
261
+ 1. **New unit tests enabled:** None — this is a signature change, not an extraction.
262
+ The existing test coverage already exercises menu navigation, editing, creation, and tool operations.
263
+
264
+ 2. **Existing tests that simplify:** All 42 `ctx as any` casts are removed from the three test files.
265
+ `makeCtx()` returns a plain `MenuUI`-shaped object (already structurally compatible).
266
+ The `makeCtx` helper in `agent-menu.test.ts` already returns the right shape — it just needs the cast removed and the handler-call interface updated.
267
+
268
+ 3. **Tests that must stay:** All existing test assertions stay — only the method of constructing the handler input changes.
269
+ `get-result-tool.test.ts` and `steer-tool.test.ts` may need minor updates if the deps dissolve changes the factory call signature, but no assertion changes.
270
+
271
+ ## TDD Order
272
+
273
+ Each step must leave `pnpm run check` green.
274
+ When a step changes a factory signature, it must also update the corresponding `index.ts` call site in the same commit.
275
+
276
+ 1. **Refactor:** Define and export `MenuUI` interface in `agent-menu.ts`.
277
+ No other changes — just add the interface alongside the existing code.
278
+ Commit: `refactor: add MenuUI interface (#146)`
279
+
280
+ 2. **Refactor:** Update `agent-config-editor.ts` — dissolve `AgentConfigEditorDeps` into 4 plain parameters; change `showAgentDetail(ctx)` to `showAgentDetail(ui: MenuUI)`; replace `ctx.ui.xxx` → `ui.xxx`.
281
+ Update `agent-config-editor.test.ts` — remove `ctx as any` casts, pass `MenuUI` objects directly.
282
+ Update `index.ts` — update `createAgentConfigEditor` call to pass 4 plain args.
283
+ Commit: `refactor: dissolve AgentConfigEditorDeps and narrow to MenuUI (#146)`
284
+
285
+ 3. **Refactor:** Update `agent-creation-wizard.ts` — destructure `AgentCreationWizardDeps`; change `showCreateWizard(ctx)` to `showCreateWizard(ui: MenuUI, parentSnapshot: ParentSnapshot)`; thread `parentSnapshot` to `showGenerateWizard`; change `WizardManager.spawnAndWait` to accept `ParentSnapshot`; replace `ctx.ui.xxx` → `ui.xxx`.
286
+ Update `agent-creation-wizard.test.ts` — remove `ctx as any` casts, pass `MenuUI` and stub `ParentSnapshot`.
287
+ Update `index.ts` — update `createAgentCreationWizard` call for destructured params.
288
+ Commit: `refactor: narrow creation wizard to MenuUI and ParentSnapshot (#146)`
289
+
290
+ 4. **Refactor:** Update `agent-menu.ts` — destructure `AgentMenuDeps`; change handler return type to accept `{ ui: MenuUI; modelRegistry: ModelRegistry; parentSnapshot: ParentSnapshot }`; thread `modelRegistry` to `showAllAgentsList`; thread `parentSnapshot` to `wizard.showCreateWizard`; update `AgentMenuManager.spawnAndWait` to accept `ParentSnapshot`; replace `ctx.ui.xxx` → `ui.xxx`.
291
+ Update `agent-menu.test.ts` — remove `ctx as any` casts, pass `{ ui, modelRegistry, parentSnapshot }`.
292
+ Update `index.ts` — update `/agents` handler to destructure `ctx.ui`, `ctx.modelRegistry`, and `buildParentSnapshot(ctx)`.
293
+ Commit: `refactor: narrow agent menu to MenuUI interface (#146)`
294
+
295
+ 5. **Refactor:** Update `get-result-tool.ts` — dissolve `GetResultDeps` into 4 plain parameters.
296
+ Update `test/tools/get-result-tool.test.ts` — update `makeDeps` and `execute` helpers.
297
+ Update `index.ts` — update `createGetResultTool` call to pass 4 plain args.
298
+ Commit: `refactor: dissolve GetResultDeps into plain parameters (#146)`
299
+
300
+ 6. **Refactor:** Update `steer-tool.ts` — dissolve `SteerToolDeps` into 4 plain parameters.
301
+ Update `test/tools/steer-tool.test.ts` — update `makeDeps` and `execute` helpers.
302
+ Update `index.ts` — update `createSteerTool` call to pass 4 plain args.
303
+ Commit: `refactor: dissolve SteerToolDeps into plain parameters (#146)`
304
+
305
+ 7. **Verify:** Run full test suite (`pnpm vitest run`) and type check (`pnpm run check`).
306
+ Confirm zero `ctx as any` in the three menu test files.
307
+ Commit: none (verification only).
308
+
309
+ ## Risks and Mitigations
310
+
311
+ | Risk | Mitigation |
312
+ | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
313
+ | `ctx.ui.custom` signature mismatch between `MenuUI` and real `ExtensionContext.ui` | `MenuUI.custom` uses `any` for the component and options parameters since these are opaque TUI types internal to the SDK. This matches the existing usage where `ctx.ui.custom<undefined>(...)` passes a TUI component constructor. |
314
+ | `ParentSnapshot` threading through menu → wizard call chain | The handler receives `parentSnapshot` from `index.ts` and threads it through `showAgentsMenu` → `showCreateWizard` → `showGenerateWizard`. Only `showGenerateWizard` uses it; the other functions relay it. This is acceptable since the parameter follows the existing `targetDir` threading pattern already in the wizard. |
315
+ | Deps dissolution breaks `index.ts` type check mid-sequence | Each TDD step updates the factory, its test file, AND the `index.ts` call site together, keeping `pnpm run check` green after every commit. |
316
+
317
+ ## Open Questions
318
+
319
+ - None — the design follows the architecture doc's Step N specification and the dependency (#145) is already implemented.