@gotgenes/pi-subagents 6.16.1 → 6.16.3
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 +17 -0
- package/docs/architecture/architecture.md +22 -18
- package/docs/plans/0146-narrow-ui-context.md +319 -0
- package/docs/retro/0146-narrow-ui-context.md +70 -0
- package/docs/retro/0148-split-agent-widget-rendering.md +39 -0
- package/package.json +1 -1
- package/src/index.ts +19 -13
- package/src/tools/get-result-tool.ts +11 -14
- package/src/tools/steer-tool.ts +12 -15
- package/src/ui/agent-config-editor.ts +63 -67
- package/src/ui/agent-creation-wizard.ts +50 -39
- package/src/ui/agent-menu.ts +118 -74
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ 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.3](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.16.2...pi-subagents-v6.16.3) (2026-05-23)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* **retro:** add retro notes for issue [#146](https://github.com/gotgenes/pi-packages/issues/146) ([720fcb0](https://github.com/gotgenes/pi-packages/commit/720fcb07937fc62a1811e59448343d3dfbc1ab14))
|
|
14
|
+
|
|
15
|
+
## [6.16.2](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.16.1...pi-subagents-v6.16.2) (2026-05-23)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
|
|
20
|
+
* fix plan for narrow UI context ([#146](https://github.com/gotgenes/pi-packages/issues/146)) ([3d5b591](https://github.com/gotgenes/pi-packages/commit/3d5b591c0668ffcd9f66a6fc9a2c5d57236865af))
|
|
21
|
+
* 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))
|
|
22
|
+
* 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))
|
|
23
|
+
* **retro:** add retro notes for issue [#148](https://github.com/gotgenes/pi-packages/issues/148) ([982fe51](https://github.com/gotgenes/pi-packages/commit/982fe51d7e27b7a127605710ced709bbd6291a0f))
|
|
24
|
+
|
|
8
25
|
## [6.16.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.16.0...pi-subagents-v6.16.1) (2026-05-23)
|
|
9
26
|
|
|
10
27
|
|
|
@@ -616,20 +616,20 @@ Phase 9 targets the next layer: observation model consolidation, `ExtensionConte
|
|
|
616
616
|
|
|
617
617
|
### Current smells
|
|
618
618
|
|
|
619
|
-
| Smell | Location
|
|
620
|
-
| ------------------------------------------------ |
|
|
621
|
-
| `execute` does config resolution for its callees | `agent-tool.ts` (145-line `execute`)
|
|
622
|
-
| Wide `ctx` in menu handlers
|
|
623
|
-
| Direct SDK import in `conversation-viewer.ts` | `conversation-viewer.test.ts`
|
|
624
|
-
| ~~Widget mixes rendering, lifecycle, and state~~ | ~~`agent-widget.ts` (370 lines)~~
|
|
625
|
-
| `deps.` prefix noise in function bodies | remaining modules across tools, UI, service-adapter
|
|
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 |
|
|
626
626
|
|
|
627
627
|
### Dependency bag convention
|
|
628
628
|
|
|
629
629
|
Applied incrementally as each step touches a module:
|
|
630
630
|
|
|
631
|
-
- **≤4 fields**
|
|
632
|
-
- **≥5 fields**
|
|
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`.
|
|
633
633
|
|
|
634
634
|
This eliminates the `deps.` prefix noise across ~124 callsites in 12 modules.
|
|
635
635
|
|
|
@@ -665,19 +665,23 @@ After this step, `ExtensionContext` appears only in:
|
|
|
665
665
|
|
|
666
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.
|
|
667
667
|
|
|
668
|
-
### Step N: Narrow UI context for menu handlers (#146)
|
|
668
|
+
### Step N: Narrow UI context for menu handlers (#146) ✓
|
|
669
669
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
`index.ts` passes `ctx.ui`
|
|
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.
|
|
673
673
|
|
|
674
|
-
|
|
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, ...)`.
|
|
675
676
|
|
|
676
|
-
|
|
677
|
+
Applied the dependency bag convention:
|
|
677
678
|
|
|
678
|
-
|
|
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.
|
|
679
681
|
|
|
680
|
-
|
|
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.
|
|
681
685
|
|
|
682
686
|
### Step O: Inject text wrapping into ConversationViewer (#147)
|
|
683
687
|
|
|
@@ -693,7 +697,7 @@ Impact: eliminates the hoisted `vi.mock("@earendil-works/pi-tui")` in `conversat
|
|
|
693
697
|
|
|
694
698
|
Extracted pure rendering functions (`renderWidgetLines`, `renderFinishedLine`, `renderRunningLines`) from `AgentWidget` into `ui/widget-renderer.ts`.
|
|
695
699
|
The widget is now a thin lifecycle/polling wrapper (198 lines, down from 374) that delegates to pure render functions.
|
|
696
|
-
Rendering functions receive data (agent list, activity map, registry) and return formatted strings
|
|
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.
|
|
697
701
|
|
|
698
702
|
### Step dependencies
|
|
699
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.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 146
|
|
3
|
+
issue_title: "Narrow UI context for menu handlers (Phase 9, Step N)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #146 — Narrow UI context for menu handlers
|
|
7
|
+
|
|
8
|
+
## Final Retrospective (2026-05-23T10:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Introduced a `MenuUI` interface in `agent-menu.ts` capturing the 6 `ctx.ui` methods used by menu handlers.
|
|
13
|
+
Replaced `ExtensionContext` with `MenuUI` (plus explicit `ModelRegistry` and `ParentSnapshot`) in all inner functions across `agent-menu.ts`, `agent-config-editor.ts`, and `agent-creation-wizard.ts`.
|
|
14
|
+
Dissolved three ≤4-field dependency bags (`AgentConfigEditorDeps`, `GetResultDeps`, `SteerToolDeps`) into plain parameters; destructured `AgentMenuDeps` and `AgentCreationWizardDeps` in their factory signatures.
|
|
15
|
+
Eliminated 42 `ctx as any` casts across 5 test files.
|
|
16
|
+
Released as `pi-subagents-v6.16.2`.
|
|
17
|
+
|
|
18
|
+
### Observations
|
|
19
|
+
|
|
20
|
+
#### What went well
|
|
21
|
+
|
|
22
|
+
- **User-initiated plan review caught three ordering bugs before implementation.**
|
|
23
|
+
The user asked the agent to "review the plan and judge its quality and clarity" after the initial commit.
|
|
24
|
+
The review identified three execution-blocking problems in the TDD order: `MenuUI` not defined before consumers, `ParentSnapshot` not threaded through the handler return type, and `index.ts` call sites not updated alongside their factories.
|
|
25
|
+
All three were fixed before TDD execution began, saving significant implementation friction.
|
|
26
|
+
- The plan's TDD order (after fixes) worked well for a pure-refactoring change — all 806 tests stayed green throughout every step.
|
|
27
|
+
- The type checker served as an effective safety net: every call-site mismatch was caught immediately by `pnpm run check`, preventing runtime surprises.
|
|
28
|
+
|
|
29
|
+
#### What caused friction (agent side)
|
|
30
|
+
|
|
31
|
+
##### Planning session
|
|
32
|
+
|
|
33
|
+
- `missing-context` — The initial plan had three TDD ordering bugs: (1) `MenuUI` used before defined, (2) `ParentSnapshot` not threaded through handler return type, (3) `index.ts` call sites not updated alongside factory signature changes.
|
|
34
|
+
These were caught by a user-prompted plan review, not by the planning agent itself.
|
|
35
|
+
Impact: would have caused type-check failures during TDD execution; fixed before implementation started.
|
|
36
|
+
|
|
37
|
+
- `wrong-abstraction` — After fixing the plan, the agent ran `git commit --amend --no-edit` to update the plan commit, but it landed on the wrong commit (a stacked prompt-changes commit from a different session).
|
|
38
|
+
This caused divergent history with origin.
|
|
39
|
+
Recovery required `git reset --hard origin/main` and re-applying the plan fixes as a new commit (`3d5b591`).
|
|
40
|
+
Impact: ~5 minutes of git recovery and re-application of edits.
|
|
41
|
+
|
|
42
|
+
##### Implementation session
|
|
43
|
+
|
|
44
|
+
- `missing-context` — In Step 3, the plan listed the `WizardManager.spawnAndWait` signature change (to accept `ParentSnapshot`) and the `AgentMenuManager.spawnAndWait` change as separate steps (3 and 4).
|
|
45
|
+
But `agent-menu.ts` passes `deps.manager` (typed as `AgentMenuManager`) to `createAgentCreationWizard`, which expects `WizardManager`.
|
|
46
|
+
Changing `WizardManager` without also changing `AgentMenuManager` broke the type checker.
|
|
47
|
+
I had to pull the `AgentMenuManager` change forward from Step 4 into Step 3.
|
|
48
|
+
Impact: minor — a few minutes of diagnosis and one extra edit, no rework.
|
|
49
|
+
|
|
50
|
+
- `missing-context` — In Step 2, after dissolving `AgentConfigEditorDeps` and changing `showAgentDetail(ctx)` to `showAgentDetail(ui: MenuUI)`, I forgot to update the call site in `agent-menu.ts` from `editor.showAgentDetail(ctx, agentName)` to `editor.showAgentDetail(ctx.ui, agentName)`.
|
|
51
|
+
The type checker caught it immediately.
|
|
52
|
+
Impact: added friction but no rework — one extra `pnpm run check` cycle.
|
|
53
|
+
|
|
54
|
+
- `missing-context` — In Step 4, the `makeUI()` test helper used `modelRegistry: {}` which didn't satisfy the `ModelRegistry` interface (requires `find` and `getAll`).
|
|
55
|
+
Impact: one extra `pnpm run check` cycle and a one-line fix.
|
|
56
|
+
|
|
57
|
+
- `missing-context` — In Step 5, two tests called `createGetResultTool(makeDeps())` directly (not via the `execute()` helper).
|
|
58
|
+
After dissolving deps, the factory signature changed but I only updated `execute()`.
|
|
59
|
+
The type checker caught the two direct calls.
|
|
60
|
+
Impact: one extra `pnpm run check` cycle.
|
|
61
|
+
|
|
62
|
+
#### What caused friction (user side)
|
|
63
|
+
|
|
64
|
+
- None observed — the session ran autonomously with no user corrections needed.
|
|
65
|
+
|
|
66
|
+
#### What caused friction (user side — planning)
|
|
67
|
+
|
|
68
|
+
- The user had to explicitly ask for a plan review (“I'd like you to review the plan and judge its quality and clarity”) to surface three ordering bugs.
|
|
69
|
+
The planning agent should have caught these during plan authoring — the testing skill already contains rules about TDD step ordering and shared interface changes.
|
|
70
|
+
The user's intervention prevented significant implementation friction.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 148
|
|
3
|
+
issue_title: "Split AgentWidget rendering from lifecycle (Phase 9, Step P)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #148 — Split AgentWidget rendering from lifecycle
|
|
7
|
+
|
|
8
|
+
## Final Retrospective (2026-05-23T06:20:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Extracted pure rendering functions (`renderWidgetLines`, `renderFinishedLine`, `renderRunningLines`) from `AgentWidget` into `ui/widget-renderer.ts`.
|
|
13
|
+
The widget shrank from 374 to 198 lines — now a thin lifecycle wrapper. 23 new unit tests cover all status variants, overflow, tree connectors, and empty states.
|
|
14
|
+
Released as `pi-subagents-v6.16.1`.
|
|
15
|
+
|
|
16
|
+
### Observations
|
|
17
|
+
|
|
18
|
+
#### What went well
|
|
19
|
+
|
|
20
|
+
- TDD cycles were fast and clean: 9 commits, all tests green on first or second try, zero test regressions across the full 806-test suite.
|
|
21
|
+
- The `WidgetAgent` / `WidgetActivity` interfaces worked well as structural subsets — `AgentRecord` and `AgentActivityTracker` satisfy them without mapping code.
|
|
22
|
+
- The stub `Theme` pattern from `test/renderer.test.ts` (`fg: (c, t) => \`[\${c}:\${t}]\``) transferred cleanly to the new test file, keeping assertions readable.
|
|
23
|
+
|
|
24
|
+
#### What caused friction (agent side)
|
|
25
|
+
|
|
26
|
+
- `missing-context` — The plan's `renderWidgetLines` API spec omitted `theme` from its parameters, even though the heading, tree connectors, and per-agent render calls all require it.
|
|
27
|
+
Caught immediately at step 4 (first `renderWidgetLines` test) and fixed by adding `theme` to the params.
|
|
28
|
+
Impact: deviation note in commit body; no rework.
|
|
29
|
+
|
|
30
|
+
- `missing-context` — Step 3 (`renderRunningLines` implementation) initially missed importing `SPINNER` from `display.ts`.
|
|
31
|
+
The test caught it as a runtime `ReferenceError`, fixed in the same Red→Green cycle.
|
|
32
|
+
Impact: added friction but no rework.
|
|
33
|
+
|
|
34
|
+
- `wrong-abstraction` — Step 8 ("Extract into `widget-renderer.ts`") was a no-op because the module was created incrementally during steps 1–7 (tests must import from the new module to run).
|
|
35
|
+
Impact: step skipped; noted in summary.
|
|
36
|
+
|
|
37
|
+
#### What caused friction (user side)
|
|
38
|
+
|
|
39
|
+
- None observed — the session ran autonomously from plan through ship with no user corrections needed.
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -227,21 +227,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
227
227
|
|
|
228
228
|
// ---- get_subagent_result tool ----
|
|
229
229
|
|
|
230
|
-
pi.registerTool(defineTool(createGetResultTool(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
pi.registerTool(defineTool(createGetResultTool(
|
|
231
|
+
(id) => manager.getRecord(id),
|
|
232
|
+
(key) => notifications.cancelNudge(key),
|
|
233
|
+
(session) => getAgentConversation(session),
|
|
234
234
|
registry,
|
|
235
|
-
|
|
235
|
+
)));
|
|
236
236
|
|
|
237
237
|
// ---- steer_subagent tool ----
|
|
238
238
|
|
|
239
|
-
pi.registerTool(defineTool(createSteerTool(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
239
|
+
pi.registerTool(defineTool(createSteerTool(
|
|
240
|
+
(id) => manager.getRecord(id),
|
|
241
|
+
(name, data) => pi.events.emit(name, data),
|
|
242
|
+
(session, message) => steerAgent(session, message),
|
|
243
|
+
(id, message) => manager.queueSteer(id, message),
|
|
244
|
+
)));
|
|
245
245
|
|
|
246
246
|
// ---- /agents interactive menu ----
|
|
247
247
|
|
|
@@ -249,7 +249,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
249
249
|
manager: {
|
|
250
250
|
listAgents: () => manager.listAgents(),
|
|
251
251
|
getRecord: (id) => manager.getRecord(id),
|
|
252
|
-
spawnAndWait: (
|
|
252
|
+
spawnAndWait: (snapshot, type, prompt, opts) => manager.spawnAndWait(snapshot, type, prompt, opts),
|
|
253
253
|
},
|
|
254
254
|
registry,
|
|
255
255
|
agentActivity: runtime.agentActivity,
|
|
@@ -270,6 +270,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
270
270
|
|
|
271
271
|
pi.registerCommand('agents', {
|
|
272
272
|
description: 'Manage agents',
|
|
273
|
-
handler: async (_args, ctx) => {
|
|
273
|
+
handler: async (_args, ctx) => {
|
|
274
|
+
await agentsMenuHandler({
|
|
275
|
+
ui: ctx.ui,
|
|
276
|
+
modelRegistry: ctx.modelRegistry,
|
|
277
|
+
parentSnapshot: buildParentSnapshot(ctx),
|
|
278
|
+
});
|
|
279
|
+
},
|
|
274
280
|
});
|
|
275
281
|
}
|