@gotgenes/pi-subagents 6.12.0 → 6.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/docs/architecture/architecture.md +17 -18
- package/docs/plans/0135-extract-display-helpers.md +182 -0
- package/docs/plans/0136-decompose-agent-menu.md +300 -0
- package/docs/retro/0134-reduce-as-any-casts.md +56 -0
- package/docs/retro/0135-extract-display-helpers.md +38 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/renderer.ts +1 -1
- package/src/tools/agent-tool.ts +2 -2
- package/src/tools/background-spawner.ts +1 -1
- package/src/tools/foreground-runner.ts +1 -1
- package/src/tools/get-result-tool.ts +1 -1
- package/src/tools/helpers.ts +1 -1
- package/src/ui/agent-config-editor.ts +202 -0
- package/src/ui/agent-creation-wizard.ts +246 -0
- package/src/ui/agent-file-ops.ts +59 -0
- package/src/ui/agent-menu.ts +22 -394
- package/src/ui/agent-widget.ts +13 -165
- package/src/ui/conversation-viewer.ts +1 -1
- package/src/ui/display.ts +178 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ 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.13.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.12.1...pi-subagents-v6.13.0) (2026-05-23)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add AgentFileOps interface and FsAgentFileOps ([#136](https://github.com/gotgenes/pi-packages/issues/136)) ([9625de6](https://github.com/gotgenes/pi-packages/commit/9625de60ace3cf58ad59c2d533f18fd7cdb8bba9))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* plan agent-menu decomposition ([#136](https://github.com/gotgenes/pi-packages/issues/136)) ([41ca6a6](https://github.com/gotgenes/pi-packages/commit/41ca6a6612cbc167a5b3d336ffb94d3f9666868f))
|
|
19
|
+
* **retro:** add retro notes for issue [#135](https://github.com/gotgenes/pi-packages/issues/135) ([83e255b](https://github.com/gotgenes/pi-packages/commit/83e255b4a5e6a56a287c933e4a5fa0b28121529e))
|
|
20
|
+
* update architecture for agent-menu decomposition ([#136](https://github.com/gotgenes/pi-packages/issues/136)) ([dba90e8](https://github.com/gotgenes/pi-packages/commit/dba90e86693e3480004a7c305d5082cb5a930d3f))
|
|
21
|
+
|
|
22
|
+
## [6.12.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.12.0...pi-subagents-v6.12.1) (2026-05-22)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
|
|
27
|
+
* mark Step J done, add ui/display.ts to module listing ([#135](https://github.com/gotgenes/pi-packages/issues/135)) ([37ced45](https://github.com/gotgenes/pi-packages/commit/37ced45e1a0287aa78e588cb8bc7905c0f969637))
|
|
28
|
+
* plan display helper extraction from agent-widget ([#135](https://github.com/gotgenes/pi-packages/issues/135)) ([9e65e7d](https://github.com/gotgenes/pi-packages/commit/9e65e7d93bf47d4c4582d367d2f31a2386a5cc8c))
|
|
29
|
+
* **retro:** add retro notes for issue [#134](https://github.com/gotgenes/pi-packages/issues/134) ([775ce99](https://github.com/gotgenes/pi-packages/commit/775ce99710153d4ebcf40f773eae21553c7f8a82))
|
|
30
|
+
|
|
8
31
|
## [6.12.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.11.0...pi-subagents-v6.12.0) (2026-05-22)
|
|
9
32
|
|
|
10
33
|
|
|
@@ -68,6 +68,7 @@ notification.ts — completion nudges, custom message renderer
|
|
|
68
68
|
renderer.ts — notification TUI component
|
|
69
69
|
record-observer.ts — session-event observer for record statistics
|
|
70
70
|
|
|
71
|
+
ui/display.ts — pure formatters, display helpers, and shared types (Theme, AgentDetails)
|
|
71
72
|
ui/agent-widget.ts — above-editor live status widget
|
|
72
73
|
ui/agent-menu.ts — /agents slash command menu
|
|
73
74
|
ui/conversation-viewer.ts — scrollable session overlay
|
|
@@ -272,7 +273,10 @@ src/
|
|
|
272
273
|
│ └── tool-start.ts — tool_execution_start handler
|
|
273
274
|
├── notification.ts — completion nudges, custom renderer
|
|
274
275
|
├── renderer.ts — notification TUI component
|
|
275
|
-
├── ui/agent-menu.ts — /agents slash command menu
|
|
276
|
+
├── ui/agent-menu.ts — /agents slash command menu (orchestration, listing, settings)
|
|
277
|
+
├── ui/agent-config-editor.ts — agent detail view (edit/delete/eject/disable/enable)
|
|
278
|
+
├── ui/agent-creation-wizard.ts — agent creation (AI-generation and manual-form)
|
|
279
|
+
├── ui/agent-file-ops.ts — AgentFileOps interface + FsAgentFileOps implementation
|
|
276
280
|
├── service-adapter.ts — SubagentsService implementation wrapping AgentManager
|
|
277
281
|
└── (existing domain modules unchanged)
|
|
278
282
|
```
|
|
@@ -564,28 +568,23 @@ Production changes:
|
|
|
564
568
|
|
|
565
569
|
Remaining 15 `as any` casts are: 8 menu-handler `ctx as any` (deferred — requires `AgentManager.spawn` to accept `ParentSnapshot` directly), 2 `print-mode.test.ts` (same ExtensionContext/API pattern), 2 private-field test access, 1 `createSession` SDK bridge in `index.ts`, 1 `foreground-runner.ts` `AgentToolResult<any>` detail, 1 `stub-ctx.ts` comment.
|
|
566
570
|
|
|
567
|
-
### Step J: Extract display helpers (#135)
|
|
571
|
+
### Step J: Extract display helpers (#135) ✓ done
|
|
568
572
|
|
|
569
|
-
`
|
|
570
|
-
|
|
573
|
+
`ui/display.ts` now contains all pure formatters, display helpers, constants, and shared types (`Theme`, `AgentDetails`).
|
|
574
|
+
`agent-widget.ts` dropped from 522 → ~340 lines.
|
|
575
|
+
All consumer modules (menu, tools, renderer, conversation viewer) import from `ui/display.ts` directly.
|
|
576
|
+
`test/agent-widget.test.ts` renamed to `test/display.test.ts`.
|
|
571
577
|
|
|
572
|
-
|
|
573
|
-
- Display helpers: `getDisplayName`, `getPromptModeLabel`, `buildInvocationTags`, `describeActivity`.
|
|
574
|
-
- Constants: `SPINNER`, `ERROR_STATUSES`, `TOOL_DISPLAY`.
|
|
578
|
+
### Step K: Decompose agent-menu.ts (#136) ✅
|
|
575
579
|
|
|
576
|
-
|
|
580
|
+
`agent-menu.ts` (668 lines) decomposed into four modules:
|
|
577
581
|
|
|
578
|
-
|
|
582
|
+
1. `ui/agent-file-ops.ts` — `AgentFileOps` interface (`exists`, `read`, `write`, `remove`, `ensureDir`, `findAgentFile`) + `FsAgentFileOps` production implementation.
|
|
583
|
+
2. `ui/agent-config-editor.ts` — `showAgentDetail` with edit/delete/reset/eject/disable/enable transitions (~200 lines).
|
|
584
|
+
3. `ui/agent-creation-wizard.ts` — AI-generation and manual-form creation paths (~250 lines).
|
|
585
|
+
4. `ui/agent-menu.ts` — menu orchestration, agent listing, running-agent viewer, settings form (~300 lines).
|
|
579
586
|
|
|
580
|
-
`agent-menu.ts`
|
|
581
|
-
Filesystem operations (read/write/delete agent `.md` files) are scattered throughout.
|
|
582
|
-
|
|
583
|
-
1. Extract `AgentFileOps` interface — `read`, `write`, `delete`, `findAgentFile` — abstracting the fs calls.
|
|
584
|
-
2. Extract `ui/agent-config-editor.ts` — `showAgentDetail` with enable/disable/reset/delete transitions.
|
|
585
|
-
3. Extract `ui/agent-creation-wizard.ts` — both AI-generation and manual form paths.
|
|
586
|
-
4. Leave menu orchestration, settings form, and running-agent viewer in `agent-menu.ts` (~200 lines).
|
|
587
|
-
|
|
588
|
-
Impact: `agent-menu.ts` drops from 650 → ~200 lines; extracted modules receive `AgentFileOps` via injection; wizard logic becomes independently testable.
|
|
587
|
+
Impact: `agent-menu.ts` dropped from 668 → 296 lines; extracted modules receive `AgentFileOps` via injection; `vi.mock("node:fs")` eliminated from `agent-menu.test.ts`.
|
|
589
588
|
|
|
590
589
|
### Step dependencies
|
|
591
590
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 135
|
|
3
|
+
issue_title: "Extract display helpers from `agent-widget.ts`"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Extract display helpers from agent-widget.ts
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`agent-widget.ts` (522 lines) exports 11 helper functions and constants that are general-purpose display utilities with no dependency on the widget's lifecycle or state.
|
|
11
|
+
Six other source modules (`agent-menu.ts`, `conversation-viewer.ts`, `renderer.ts`, `agent-tool.ts`, `foreground-runner.ts`, `get-result-tool.ts`) and two tool support modules (`helpers.ts`, `background-spawner.ts`) import formatting functions or display types from the widget — creating a false dependency on a lifecycle-heavy UI module.
|
|
12
|
+
|
|
13
|
+
This is Phase 8, Step J of the architecture plan.
|
|
14
|
+
|
|
15
|
+
## Goals
|
|
16
|
+
|
|
17
|
+
- Extract pure formatters, display helpers, constants, and associated types into `ui/display.ts`.
|
|
18
|
+
- Update all import sites to import from `ui/display.ts` instead of `ui/agent-widget.ts`.
|
|
19
|
+
- Reduce `agent-widget.ts` to only the `AgentWidget` class and its immediate dependencies (`UICtx`, private helpers).
|
|
20
|
+
- Unblock Step K (menu decomposition, #136) — extracted menu sub-modules will import display helpers without pulling in the widget.
|
|
21
|
+
|
|
22
|
+
## Non-Goals
|
|
23
|
+
|
|
24
|
+
- Decomposing `agent-menu.ts` — deferred to #136 (Step K).
|
|
25
|
+
- Changing any runtime behavior or public API.
|
|
26
|
+
- Extracting `UICtx` — it is a widget-lifecycle type used only by `AgentWidget`, `runtime.ts`, and `index.ts`.
|
|
27
|
+
|
|
28
|
+
## Background
|
|
29
|
+
|
|
30
|
+
The architecture doc (Phase 8 roadmap, Step J) prescribes exactly which symbols to extract.
|
|
31
|
+
The `code-design` skill's "Helpers stay in the file" rule applies: these helpers have accumulated to the point where they warrant their own module and tests.
|
|
32
|
+
AGENTS.md's "one concern per file" constraint also supports the extraction.
|
|
33
|
+
|
|
34
|
+
### Symbols to extract
|
|
35
|
+
|
|
36
|
+
#### Pure formatters (zero runtime dependencies)
|
|
37
|
+
|
|
38
|
+
1. `formatTokens(count)` — compact token count ("33.8k token").
|
|
39
|
+
2. `formatSessionTokens(tokens, percent, theme, compactions)` — annotated token string with threshold colors.
|
|
40
|
+
3. `formatTurns(turnCount, maxTurns)` — turn counter with optional limit.
|
|
41
|
+
4. `formatMs(ms)` — milliseconds → "1.2s".
|
|
42
|
+
5. `formatDuration(startedAt, completedAt)` — timestamp pair → human duration.
|
|
43
|
+
|
|
44
|
+
#### Display helpers (registry lookup only)
|
|
45
|
+
|
|
46
|
+
6. `getDisplayName(type, registry)` — resolved display name for an agent type.
|
|
47
|
+
7. `getPromptModeLabel(type, registry)` — "twin" for append mode, undefined otherwise.
|
|
48
|
+
8. `buildInvocationTags(invocation)` — config tags array from invocation options.
|
|
49
|
+
9. `describeActivity(activeTools, responseText)` — human-readable activity string.
|
|
50
|
+
|
|
51
|
+
#### Constants
|
|
52
|
+
|
|
53
|
+
10. `SPINNER` — braille spinner frames.
|
|
54
|
+
11. `ERROR_STATUSES` — set of error/non-success status strings.
|
|
55
|
+
12. `TOOL_DISPLAY` — tool name → action verb mapping (private, moves with `describeActivity`).
|
|
56
|
+
|
|
57
|
+
#### Types
|
|
58
|
+
|
|
59
|
+
13. `Theme` — used in `formatSessionTokens` signature; must co-locate.
|
|
60
|
+
14. `AgentDetails` — display metadata interface used by tools; no widget dependency.
|
|
61
|
+
|
|
62
|
+
`UICtx` stays in `agent-widget.ts` — it defines the widget's host contract and is only consumed by the widget class, `runtime.ts`, and `index.ts`.
|
|
63
|
+
|
|
64
|
+
### Current import graph (agent-widget.ts consumers)
|
|
65
|
+
|
|
66
|
+
| Consumer | Symbols imported |
|
|
67
|
+
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `ui/conversation-viewer.ts` | `buildInvocationTags`, `describeActivity`, `formatDuration`, `formatSessionTokens`, `getDisplayName`, `getPromptModeLabel`, `Theme` |
|
|
69
|
+
| `ui/agent-menu.ts` | `formatDuration`, `getDisplayName` |
|
|
70
|
+
| `tools/agent-tool.ts` | `AgentDetails`, `buildInvocationTags`, `formatMs`, `formatTurns`, `getDisplayName`, `getPromptModeLabel`, `SPINNER`, `UICtx` |
|
|
71
|
+
| `tools/foreground-runner.ts` | `AgentDetails`, `describeActivity`, `formatMs`, `SPINNER` |
|
|
72
|
+
| `tools/get-result-tool.ts` | `formatDuration`, `getDisplayName` |
|
|
73
|
+
| `tools/helpers.ts` | `AgentDetails`, `formatTokens` |
|
|
74
|
+
| `tools/background-spawner.ts` | `AgentDetails` |
|
|
75
|
+
| `renderer.ts` | `formatMs`, `formatTokens`, `formatTurns` |
|
|
76
|
+
| `runtime.ts` | `UICtx` |
|
|
77
|
+
| `index.ts` | `AgentWidget`, `UICtx` |
|
|
78
|
+
| `test/agent-widget.test.ts` | `formatSessionTokens`, `getDisplayName`, `getPromptModeLabel` |
|
|
79
|
+
| `test/conversation-viewer.test.ts` | `Theme` |
|
|
80
|
+
|
|
81
|
+
### Post-extraction import graph
|
|
82
|
+
|
|
83
|
+
After extraction, `ui/agent-widget.ts` imports `display.ts` for the symbols it still uses internally (e.g., `getDisplayName`, `formatMs`, `formatTurns`, `formatSessionTokens`, `ERROR_STATUSES`, `SPINNER`, `describeActivity`).
|
|
84
|
+
All other consumers switch their imports from `./agent-widget.js` to `./display.js` (or `../ui/display.js` for `tools/` and `renderer.ts`).
|
|
85
|
+
|
|
86
|
+
Only `index.ts` and `runtime.ts` continue to import from `agent-widget.ts` (for `AgentWidget` class and `UICtx` type).
|
|
87
|
+
`tools/agent-tool.ts` splits its import: `UICtx` from `agent-widget.ts`, everything else from `display.ts`.
|
|
88
|
+
|
|
89
|
+
## Design Overview
|
|
90
|
+
|
|
91
|
+
This is a pure code-motion refactoring — no behavior changes.
|
|
92
|
+
|
|
93
|
+
### New module: `ui/display.ts`
|
|
94
|
+
|
|
95
|
+
Contains all 12 exported symbols (5 formatters, 4 display helpers, 3 constants) plus 2 types (`Theme`, `AgentDetails`) and 1 private helper (`truncateLine`, used by `describeActivity`).
|
|
96
|
+
|
|
97
|
+
The module's only imports are:
|
|
98
|
+
|
|
99
|
+
- `AgentConfigLookup` from `../agent-types.js` (type-only, for `getDisplayName`/`getPromptModeLabel`).
|
|
100
|
+
- `SubagentType`, `AgentInvocation` from `../types.js` (type-only).
|
|
101
|
+
|
|
102
|
+
No SDK imports, no runtime dependencies — exactly the kind of pure utility module the code-design skill prescribes.
|
|
103
|
+
|
|
104
|
+
### Residual `agent-widget.ts`
|
|
105
|
+
|
|
106
|
+
After extraction, `agent-widget.ts` contains:
|
|
107
|
+
|
|
108
|
+
- `UICtx` type (widget host contract).
|
|
109
|
+
- `AgentWidget` class (~340 lines) with its private helpers.
|
|
110
|
+
- Imports from `./display.js` for the format/display functions used in rendering.
|
|
111
|
+
|
|
112
|
+
## Module-Level Changes
|
|
113
|
+
|
|
114
|
+
### New files
|
|
115
|
+
|
|
116
|
+
| File | Contents |
|
|
117
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `src/ui/display.ts` | All extracted symbols: `Theme`, `AgentDetails`, `SPINNER`, `ERROR_STATUSES`, `TOOL_DISPLAY`, `formatTokens`, `formatSessionTokens`, `formatTurns`, `formatMs`, `formatDuration`, `getDisplayName`, `getPromptModeLabel`, `buildInvocationTags`, `describeActivity`, private `truncateLine`. |
|
|
119
|
+
|
|
120
|
+
### Modified files
|
|
121
|
+
|
|
122
|
+
| File | Change |
|
|
123
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
124
|
+
| `src/ui/agent-widget.ts` | Remove all extracted symbols. Add `import` from `./display.js` for symbols `AgentWidget` still uses internally. Keep `UICtx`, `AgentWidget` class, private widget helpers. |
|
|
125
|
+
| `src/ui/conversation-viewer.ts` | Change import path from `./agent-widget.js` to `./display.js`. |
|
|
126
|
+
| `src/ui/agent-menu.ts` | Change import path from `./agent-widget.js` to `./display.js`. |
|
|
127
|
+
| `src/tools/agent-tool.ts` | Split import: `UICtx` from `../ui/agent-widget.js`; all others from `../ui/display.js`. |
|
|
128
|
+
| `src/tools/foreground-runner.ts` | Change import path from `../ui/agent-widget.js` to `../ui/display.js`. |
|
|
129
|
+
| `src/tools/get-result-tool.ts` | Change import path from `../ui/agent-widget.js` to `../ui/display.js`. |
|
|
130
|
+
| `src/tools/helpers.ts` | Change import path from `../ui/agent-widget.js` to `../ui/display.js`. |
|
|
131
|
+
| `src/tools/background-spawner.ts` | Change import path from `../ui/agent-widget.js` to `../ui/display.js`. |
|
|
132
|
+
| `src/renderer.ts` | Change import path from `./ui/agent-widget.js` to `./ui/display.js`. |
|
|
133
|
+
| `test/agent-widget.test.ts` | Change import path to `../src/ui/display.js`. Rename file to `test/display.test.ts` since it tests extracted functions. |
|
|
134
|
+
| `test/conversation-viewer.test.ts` | Change `Theme` import from `../src/ui/agent-widget.js` to `../src/ui/display.js`. |
|
|
135
|
+
|
|
136
|
+
### Unchanged files
|
|
137
|
+
|
|
138
|
+
| File | Reason |
|
|
139
|
+
| ---------------- | ------------------------------------------------------------------- |
|
|
140
|
+
| `src/runtime.ts` | Imports only `UICtx` — stays in `agent-widget.ts`. |
|
|
141
|
+
| `src/index.ts` | Imports `AgentWidget` and `UICtx` — both stay in `agent-widget.ts`. |
|
|
142
|
+
|
|
143
|
+
## Test Impact Analysis
|
|
144
|
+
|
|
145
|
+
1. The extraction enables dedicated `display.test.ts` that tests formatting functions in isolation without any widget class ceremony.
|
|
146
|
+
The existing `agent-widget.test.ts` already tests only extracted functions (`formatSessionTokens`, `getDisplayName`, `getPromptModeLabel`) — it becomes `display.test.ts` with no assertion changes, just a file rename and import path update.
|
|
147
|
+
2. No existing tests become redundant — the current test file already exercises the extracted layer exclusively.
|
|
148
|
+
3. No existing tests need assertion changes — this is a pure code-motion refactoring with no behavior change.
|
|
149
|
+
|
|
150
|
+
## TDD Order
|
|
151
|
+
|
|
152
|
+
1. **Create `ui/display.ts` with all extracted symbols; update `agent-widget.ts` to import from it.**
|
|
153
|
+
Move the 12 exported symbols, 2 types, and 1 private helper to `ui/display.ts`.
|
|
154
|
+
Remove them from `agent-widget.ts` and add imports from `./display.js` for symbols the `AgentWidget` class still references.
|
|
155
|
+
Commit: `refactor: extract display helpers into ui/display.ts (#135)`
|
|
156
|
+
|
|
157
|
+
2. **Update all consumer imports to point at `ui/display.ts`.**
|
|
158
|
+
Update the 8 source files (`conversation-viewer.ts`, `agent-menu.ts`, `agent-tool.ts`, `foreground-runner.ts`, `get-result-tool.ts`, `helpers.ts`, `background-spawner.ts`, `renderer.ts`) to import from the new module.
|
|
159
|
+
Commit: `refactor: update imports to use ui/display.ts (#135)`
|
|
160
|
+
|
|
161
|
+
3. **Rename test file and update test imports.**
|
|
162
|
+
Rename `test/agent-widget.test.ts` → `test/display.test.ts`.
|
|
163
|
+
Update import path to `../src/ui/display.js`.
|
|
164
|
+
Update `Theme` import in `test/conversation-viewer.test.ts`.
|
|
165
|
+
Commit: `test: rename agent-widget test to display test (#135)`
|
|
166
|
+
|
|
167
|
+
4. **Verify: run `pnpm run check` and `pnpm vitest run`.**
|
|
168
|
+
Confirm type-checking and all tests pass.
|
|
169
|
+
No commit needed — validation step.
|
|
170
|
+
|
|
171
|
+
## Risks and Mitigations
|
|
172
|
+
|
|
173
|
+
| Risk | Mitigation |
|
|
174
|
+
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| Missed import site causes runtime `undefined` import | Grep confirmed all 10 source consumers and 2 test consumers above. Step 4 validates with type-check + full test suite. |
|
|
176
|
+
| `TOOL_DISPLAY` made public unintentionally | Keep it non-exported in `display.ts` (only `describeActivity` uses it). |
|
|
177
|
+
| Circular dependency `display.ts` ↔ `agent-widget.ts` | `display.ts` has no imports from `agent-widget.ts`. `agent-widget.ts` imports from `display.ts` — one-directional. |
|
|
178
|
+
| Re-export churn for downstream consumers | No downstream consumers — these are all internal module imports, not public API. |
|
|
179
|
+
|
|
180
|
+
## Open Questions
|
|
181
|
+
|
|
182
|
+
None — the architecture doc and issue specify the exact extraction set and target module.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 136
|
|
3
|
+
issue_title: "Decompose `agent-menu.ts`"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Decompose agent-menu into config editor, creation wizard, and file-ops abstraction
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
`agent-menu.ts` (668 lines) has 8 distinct responsibilities: menu FSM orchestration, agent listing, config editing, agent ejection, two creation wizards, running-agent viewer, and settings form.
|
|
11
|
+
Filesystem operations (`readFileSync`, `writeFileSync`, `unlinkSync`, `existsSync`, `mkdirSync`) are scattered across 10+ call sites with no abstraction layer, forcing tests to use `vi.mock("node:fs")` rather than injecting stubs.
|
|
12
|
+
|
|
13
|
+
## Goals
|
|
14
|
+
|
|
15
|
+
- Extract an `AgentFileOps` interface abstracting all filesystem calls, with a production `FsAgentFileOps` implementation.
|
|
16
|
+
- Extract `ui/agent-config-editor.ts` (~170 lines) containing `showAgentDetail` with eject/disable/enable/edit/delete/reset transitions.
|
|
17
|
+
- Extract `ui/agent-creation-wizard.ts` (~200 lines) containing both the AI-generation and manual-form creation paths.
|
|
18
|
+
- Leave menu FSM, agent listing, running-agent viewer, and settings form in `agent-menu.ts` (~280 lines).
|
|
19
|
+
- Each extracted module receives dependencies via injection — no direct `node:fs` imports outside `FsAgentFileOps`.
|
|
20
|
+
- Enable unit testing of config editor and creation wizard without `vi.mock("node:fs")`.
|
|
21
|
+
|
|
22
|
+
## Non-Goals
|
|
23
|
+
|
|
24
|
+
- Refactoring the settings form or running-agent viewer — they stay in `agent-menu.ts`.
|
|
25
|
+
- Changing any user-facing behavior or menu flow.
|
|
26
|
+
- Extracting `showAllAgentsList` — it is menu orchestration (presents the list, delegates to editor for detail).
|
|
27
|
+
- Deduplicating the YAML frontmatter builders in eject and manual wizard (structurally different content shapes).
|
|
28
|
+
|
|
29
|
+
## Background
|
|
30
|
+
|
|
31
|
+
### Prerequisite
|
|
32
|
+
|
|
33
|
+
Issue #135 (extract display helpers) is **implemented** — `ui/display.ts` exists and provides `formatDuration`, `getDisplayName`, and other formatters.
|
|
34
|
+
Extracted menu sub-modules can import display helpers without pulling in the widget.
|
|
35
|
+
|
|
36
|
+
### Existing IO-injection convention
|
|
37
|
+
|
|
38
|
+
The codebase uses injectable IO interfaces to decouple domain logic from `node:fs` and the Pi SDK:
|
|
39
|
+
|
|
40
|
+
- `AssemblerIO` in `session-config.ts` — 4 methods for prompt assembly IO.
|
|
41
|
+
- `RunnerIO` in `agent-runner.ts` — 7 methods for session creation IO.
|
|
42
|
+
|
|
43
|
+
Both follow the same pattern: interface defined in the module that uses it, production implementation wired at the edge (`index.ts`), test stubs injected directly.
|
|
44
|
+
|
|
45
|
+
### Current fs call inventory in agent-menu.ts
|
|
46
|
+
|
|
47
|
+
| Function | fs calls |
|
|
48
|
+
| -------------------------------- | ------------------------------------------------------------- |
|
|
49
|
+
| `findAgentFile` | `existsSync` ×2 |
|
|
50
|
+
| `showAgentDetail` (edit) | `readFileSync`, `writeFileSync` |
|
|
51
|
+
| `showAgentDetail` (delete/reset) | `unlinkSync` |
|
|
52
|
+
| `ejectAgent` | `mkdirSync`, `existsSync`, `writeFileSync` |
|
|
53
|
+
| `disableAgent` | `readFileSync`, `writeFileSync`, `mkdirSync`, `writeFileSync` |
|
|
54
|
+
| `enableAgent` | `readFileSync`, `writeFileSync`, `unlinkSync` |
|
|
55
|
+
| `showGenerateWizard` | `mkdirSync`, `existsSync` ×2 |
|
|
56
|
+
| `showManualWizard` | `mkdirSync`, `existsSync`, `writeFileSync` |
|
|
57
|
+
|
|
58
|
+
### Current test file
|
|
59
|
+
|
|
60
|
+
`agent-menu.test.ts` (212 lines) uses `vi.mock("node:fs")` with `vi.hoisted` stubs.
|
|
61
|
+
Only 7 tests exist — they cover the top-level menu, agent listing, projectAgentsDir injection, and settings delegation.
|
|
62
|
+
No tests exercise config-editor transitions (edit/delete/eject/disable/enable) or creation wizards.
|
|
63
|
+
|
|
64
|
+
## Design Overview
|
|
65
|
+
|
|
66
|
+
### AgentFileOps interface
|
|
67
|
+
|
|
68
|
+
A narrow interface abstracting all agent `.md` file operations:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
interface AgentFileOps {
|
|
72
|
+
exists(filePath: string): boolean;
|
|
73
|
+
read(filePath: string): string | undefined;
|
|
74
|
+
write(filePath: string, content: string): void;
|
|
75
|
+
remove(filePath: string): void;
|
|
76
|
+
ensureDir(dirPath: string): void;
|
|
77
|
+
findAgentFile(name: string, dirs: string[]): string | undefined;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Design notes:
|
|
82
|
+
|
|
83
|
+
- `read` returns `undefined` when the file does not exist (wraps `readFileSync` with a try/catch).
|
|
84
|
+
- `write` ensures parent directories exist before writing (internalizes `mkdirSync`).
|
|
85
|
+
- `ensureDir` is needed separately because `showGenerateWizard` creates the directory before spawning an agent that writes via Pi's tool (not via `AgentFileOps.write`).
|
|
86
|
+
- `findAgentFile` takes an ordered list of directories and returns the first matching path.
|
|
87
|
+
The current code returns `{ path, location }` but only `showAgentDetail` uses `location` (for a confirmation dialog) — the full path already conveys the location to the user, so a plain `string | undefined` return suffices.
|
|
88
|
+
|
|
89
|
+
### FsAgentFileOps production implementation
|
|
90
|
+
|
|
91
|
+
Thin wrapper over `node:fs` synchronous APIs:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
class FsAgentFileOps implements AgentFileOps {
|
|
95
|
+
exists(filePath: string): boolean {
|
|
96
|
+
return existsSync(filePath);
|
|
97
|
+
}
|
|
98
|
+
read(filePath: string): string | undefined {
|
|
99
|
+
try { return readFileSync(filePath, "utf-8"); }
|
|
100
|
+
catch { return undefined; }
|
|
101
|
+
}
|
|
102
|
+
write(filePath: string, content: string): void {
|
|
103
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
104
|
+
writeFileSync(filePath, content, "utf-8");
|
|
105
|
+
}
|
|
106
|
+
remove(filePath: string): void {
|
|
107
|
+
unlinkSync(filePath);
|
|
108
|
+
}
|
|
109
|
+
ensureDir(dirPath: string): void {
|
|
110
|
+
mkdirSync(dirPath, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
findAgentFile(name: string, dirs: string[]): string | undefined {
|
|
113
|
+
for (const dir of dirs) {
|
|
114
|
+
const p = join(dir, `${name}.md`);
|
|
115
|
+
if (existsSync(p)) return p;
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Extracted module patterns
|
|
123
|
+
|
|
124
|
+
Both extracted modules follow the existing factory-function pattern (`createAgentsMenuHandler(deps)`) with narrow deps interfaces per ISP:
|
|
125
|
+
|
|
126
|
+
Config editor call-site sketch (from orchestrator):
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const editor = createAgentConfigEditor({
|
|
130
|
+
fileOps: deps.fileOps,
|
|
131
|
+
registry: deps.registry,
|
|
132
|
+
personalAgentsDir: deps.personalAgentsDir,
|
|
133
|
+
projectAgentsDir: deps.projectAgentsDir,
|
|
134
|
+
});
|
|
135
|
+
// ...
|
|
136
|
+
await editor.showAgentDetail(ctx, agentName);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Creation wizard call-site sketch (from orchestrator):
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const wizard = createAgentCreationWizard({
|
|
143
|
+
fileOps: deps.fileOps,
|
|
144
|
+
manager: deps.manager,
|
|
145
|
+
registry: deps.registry,
|
|
146
|
+
personalAgentsDir: deps.personalAgentsDir,
|
|
147
|
+
projectAgentsDir: deps.projectAgentsDir,
|
|
148
|
+
});
|
|
149
|
+
// ...
|
|
150
|
+
await wizard.showCreateWizard(ctx);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Deps interfaces (ISP-compliant)
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// agent-config-editor.ts
|
|
157
|
+
interface AgentConfigEditorDeps {
|
|
158
|
+
fileOps: AgentFileOps;
|
|
159
|
+
registry: AgentTypeRegistry;
|
|
160
|
+
personalAgentsDir: string;
|
|
161
|
+
projectAgentsDir: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// agent-creation-wizard.ts
|
|
165
|
+
interface AgentCreationWizardDeps {
|
|
166
|
+
fileOps: AgentFileOps;
|
|
167
|
+
manager: AgentMenuManager;
|
|
168
|
+
registry: AgentTypeRegistry;
|
|
169
|
+
personalAgentsDir: string;
|
|
170
|
+
projectAgentsDir: string;
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The config editor does not need `manager` (no agent spawning).
|
|
175
|
+
The creation wizard does not need `agentActivity`, `getModelLabel`, or `settings`.
|
|
176
|
+
|
|
177
|
+
### Updated AgentMenuDeps
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
interface AgentMenuDeps {
|
|
181
|
+
manager: AgentMenuManager;
|
|
182
|
+
registry: AgentTypeRegistry;
|
|
183
|
+
agentActivity: AgentActivityReader;
|
|
184
|
+
getModelLabel: (type: string, registry?: ModelRegistry) => string;
|
|
185
|
+
settings: AgentMenuSettings;
|
|
186
|
+
fileOps: AgentFileOps; // ← new
|
|
187
|
+
personalAgentsDir: string;
|
|
188
|
+
projectAgentsDir: string;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The single new field (`fileOps`) replaces all direct `node:fs` imports.
|
|
193
|
+
|
|
194
|
+
## Module-Level Changes
|
|
195
|
+
|
|
196
|
+
### New files
|
|
197
|
+
|
|
198
|
+
1. `src/ui/agent-file-ops.ts` — `AgentFileOps` interface + `FsAgentFileOps` class.
|
|
199
|
+
2. `src/ui/agent-config-editor.ts` — `AgentConfigEditorDeps` interface, `createAgentConfigEditor` factory.
|
|
200
|
+
Moves in: `showAgentDetail`, `ejectAgent`, `disableAgent`, `enableAgent`.
|
|
201
|
+
3. `src/ui/agent-creation-wizard.ts` — `AgentCreationWizardDeps` interface, `createAgentCreationWizard` factory.
|
|
202
|
+
Moves in: `showCreateWizard`, `showGenerateWizard`, `showManualWizard`.
|
|
203
|
+
4. `test/ui/agent-file-ops.test.ts` — unit tests for `FsAgentFileOps`.
|
|
204
|
+
5. `test/ui/agent-config-editor.test.ts` — unit tests for config editor transitions with injected `AgentFileOps` stubs.
|
|
205
|
+
6. `test/ui/agent-creation-wizard.test.ts` — unit tests for wizard paths with injected stubs.
|
|
206
|
+
|
|
207
|
+
### Modified files
|
|
208
|
+
|
|
209
|
+
1. `src/ui/agent-menu.ts`:
|
|
210
|
+
- Remove `import { existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs"`.
|
|
211
|
+
- Remove `findAgentFile`, `showAgentDetail`, `ejectAgent`, `disableAgent`, `enableAgent`, `showCreateWizard`, `showGenerateWizard`, `showManualWizard` (~375 lines removed).
|
|
212
|
+
- Add `fileOps: AgentFileOps` to `AgentMenuDeps`.
|
|
213
|
+
- Import and instantiate `createAgentConfigEditor` and `createAgentCreationWizard`.
|
|
214
|
+
- Update `showAllAgentsList` to call `editor.showAgentDetail(ctx, agentName)`.
|
|
215
|
+
- Update `showAgentsMenu` to call `wizard.showCreateWizard(ctx)`.
|
|
216
|
+
- Re-export `AgentMenuManager` (still needed by the wizard deps).
|
|
217
|
+
- Net result: ~280 lines (orchestration, listing, running agents, settings).
|
|
218
|
+
2. `src/index.ts`:
|
|
219
|
+
- Import `FsAgentFileOps` from `./ui/agent-file-ops.js`.
|
|
220
|
+
- Construct `new FsAgentFileOps()` and pass as `fileOps` in the `createAgentsMenuHandler` call.
|
|
221
|
+
3. `test/ui/agent-menu.test.ts`:
|
|
222
|
+
- Remove `vi.mock("node:fs")` and `vi.hoisted` stubs entirely.
|
|
223
|
+
- Add `fileOps` stub to `makeDeps` factory.
|
|
224
|
+
- Rewrite the "projectAgentsDir injection" test to verify that the orchestrator delegates to the editor (or move to `agent-config-editor.test.ts`).
|
|
225
|
+
|
|
226
|
+
### Removed symbols
|
|
227
|
+
|
|
228
|
+
Grep confirms these functions are only referenced within `agent-menu.ts` (closures inside `createAgentsMenuHandler`) — no external consumers:
|
|
229
|
+
|
|
230
|
+
- `findAgentFile` — moves to `agent-config-editor.ts` (reimplemented via `AgentFileOps.findAgentFile`).
|
|
231
|
+
- `showAgentDetail`, `ejectAgent`, `disableAgent`, `enableAgent` — move to `agent-config-editor.ts`.
|
|
232
|
+
- `showCreateWizard`, `showGenerateWizard`, `showManualWizard` — move to `agent-creation-wizard.ts`.
|
|
233
|
+
|
|
234
|
+
## Test Impact Analysis
|
|
235
|
+
|
|
236
|
+
### New unit tests enabled by extraction
|
|
237
|
+
|
|
238
|
+
1. **Config editor transitions** — `showAgentDetail` has 6 menu paths (edit, delete, reset, eject, disable, enable) with sub-branches (confirm/cancel, file exists/not-exists, default/custom agent).
|
|
239
|
+
Currently untestable in isolation because the functions are closures inside `createAgentsMenuHandler`.
|
|
240
|
+
After extraction, each transition is testable with injected `AgentFileOps` stubs — no `vi.mock("node:fs")` needed.
|
|
241
|
+
2. **Creation wizard flows** — generate wizard (spawn + check result) and manual wizard (multi-step form → write file) are currently untested.
|
|
242
|
+
After extraction, both are testable with injected stubs for `AgentFileOps` and `AgentMenuManager.spawnAndWait`.
|
|
243
|
+
3. **FsAgentFileOps** — thin tests verifying the production fs wrapper (read returns undefined on missing file, write ensures parent dirs, findAgentFile checks directories in order).
|
|
244
|
+
|
|
245
|
+
### Existing tests that become redundant
|
|
246
|
+
|
|
247
|
+
The "projectAgentsDir injection" test (`agent-menu.test.ts`) navigates through the main menu → agent types → agent detail, then asserts `mockExistsSync` was called with the correct path.
|
|
248
|
+
After extraction, this end-to-end path tests orchestration + editor together; a focused test in `agent-config-editor.test.ts` replaces it.
|
|
249
|
+
The orchestrator test can be simplified to verify it delegates to the editor.
|
|
250
|
+
|
|
251
|
+
### Existing tests that stay as-is
|
|
252
|
+
|
|
253
|
+
- Menu structure tests (shows options, reload, running agents) — these test the orchestrator directly.
|
|
254
|
+
- Settings delegation tests — these test code that remains in `agent-menu.ts`.
|
|
255
|
+
|
|
256
|
+
## TDD Order
|
|
257
|
+
|
|
258
|
+
### Cycle 1: AgentFileOps interface and FsAgentFileOps
|
|
259
|
+
|
|
260
|
+
1. `test:` write `test/ui/agent-file-ops.test.ts` — tests for `read` (existing file, missing file), `write` (ensures parent dir), `remove`, `exists`, `ensureDir`, `findAgentFile` (first-match ordering, no match).
|
|
261
|
+
2. `feat:` implement `src/ui/agent-file-ops.ts` — interface + `FsAgentFileOps` class.
|
|
262
|
+
3. Commit: `feat: add AgentFileOps interface and FsAgentFileOps (#136)`.
|
|
263
|
+
|
|
264
|
+
### Cycle 2: Extract agent-config-editor
|
|
265
|
+
|
|
266
|
+
1. `test:` write `test/ui/agent-config-editor.test.ts` — tests for `showAgentDetail` transitions: edit (save/cancel), delete (confirm/cancel), reset-to-default (confirm/cancel), eject (project/personal location, overwrite check), disable (existing file toggle, new disable-only file), enable (remove enabled:false line, remove empty override file).
|
|
267
|
+
All tests inject stub `AgentFileOps` — no `vi.mock`.
|
|
268
|
+
2. `refactor:` create `src/ui/agent-config-editor.ts` — move `showAgentDetail`, `ejectAgent`, `disableAgent`, `enableAgent` from `agent-menu.ts`.
|
|
269
|
+
Replace direct fs calls with `deps.fileOps.*` calls.
|
|
270
|
+
Replace the closure `findAgentFile` with `deps.fileOps.findAgentFile(name, [deps.projectAgentsDir, deps.personalAgentsDir])`.
|
|
271
|
+
3. `refactor:` update `src/ui/agent-menu.ts` — add `fileOps` to `AgentMenuDeps`, import `createAgentConfigEditor`, wire into `showAllAgentsList`.
|
|
272
|
+
4. `refactor:` update `src/index.ts` — import `FsAgentFileOps`, pass `fileOps: new FsAgentFileOps()` in deps.
|
|
273
|
+
5. `test:` update `test/ui/agent-menu.test.ts` — add `fileOps` stub to `makeDeps`, rewrite the "projectAgentsDir injection" test to verify orchestrator→editor delegation.
|
|
274
|
+
6. Run `pnpm run check` (interface change).
|
|
275
|
+
7. Commit: `refactor: extract agent-config-editor from agent-menu (#136)`.
|
|
276
|
+
|
|
277
|
+
### Cycle 3: Extract agent-creation-wizard
|
|
278
|
+
|
|
279
|
+
1. `test:` write `test/ui/agent-creation-wizard.test.ts` — tests for `showCreateWizard` (location + method selection), `showGenerateWizard` (spawn success, spawn error, overwrite check), `showManualWizard` (full form flow, overwrite check, tool/model/thinking selections).
|
|
280
|
+
All tests inject stub `AgentFileOps` + `AgentMenuManager` — no `vi.mock`.
|
|
281
|
+
2. `refactor:` create `src/ui/agent-creation-wizard.ts` — move `showCreateWizard`, `showGenerateWizard`, `showManualWizard` from `agent-menu.ts`.
|
|
282
|
+
Replace direct fs calls with `deps.fileOps.*` calls.
|
|
283
|
+
3. `refactor:` update `src/ui/agent-menu.ts` — import `createAgentCreationWizard`, wire into `showAgentsMenu`.
|
|
284
|
+
Remove the `import { ... } from "node:fs"` line (no longer needed).
|
|
285
|
+
4. `test:` remove `vi.mock("node:fs")` and `vi.hoisted` stubs from `test/ui/agent-menu.test.ts` entirely.
|
|
286
|
+
5. Run `pnpm run check`.
|
|
287
|
+
6. Commit: `refactor: extract agent-creation-wizard from agent-menu (#136)`.
|
|
288
|
+
|
|
289
|
+
## Risks and Mitigations
|
|
290
|
+
|
|
291
|
+
| Risk | Mitigation |
|
|
292
|
+
| --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
293
|
+
| Circular dependency between orchestrator and editor (editor calling back to menu) | The editor's `showAgentDetail` does not recurse to the agent list — the orchestrator does (`showAllAgentsList` calls editor then recurses itself). No circular dependency. |
|
|
294
|
+
| `FsAgentFileOps.write` silently creates parent directories when callers expect the directory to not exist | `write` mirrors current behavior — every call site already calls `mkdirSync` before `writeFileSync`. The consolidation only changes where the `mkdirSync` happens, not whether it runs. |
|
|
295
|
+
| Adding `fileOps` to `AgentMenuDeps` breaks existing test factory | Cycle 2 updates `makeDeps` in the same commit — the interface change and call-site update land together per the testing skill's single-call-site rule. |
|
|
296
|
+
| Generate wizard spawns an agent that writes via Pi tools, not via `AgentFileOps.write` | The wizard uses `fileOps.ensureDir` for directory creation and `fileOps.exists` for the post-spawn success check. The spawned agent's file write is outside the menu's control and is not abstracted. |
|
|
297
|
+
|
|
298
|
+
## Open Questions
|
|
299
|
+
|
|
300
|
+
- None — the issue's proposed changes section is unambiguous and the prerequisite (#135) is already implemented.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 134
|
|
3
|
+
issue_title: "Reduce `as any` casts in test suite"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #134 — Reduce as-any casts in test suite
|
|
7
|
+
|
|
8
|
+
## Final Retrospective (2026-05-22T17:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Reduced `as any` casts from 93 to 15 across the pi-subagents package.
|
|
13
|
+
Production changes added type guards (`getToolCallName`, `isBashExecution`), narrowed `SubagentRuntime.widget` to `WidgetLike`, typed `CreateSessionOptions.settingsManager` as `SettingsManager`, and fixed `ResourceLoaderOptions.appendSystemPromptOverride` to match the SDK.
|
|
14
|
+
Test improvements introduced `toAgentSession()`, `STUB_CTX`, and `makeRegistry()` helpers to centralise unavoidable bridge casts.
|
|
15
|
+
Also fixed 3 pre-existing lint issues.
|
|
16
|
+
|
|
17
|
+
This session also included #133 (plan + implement + ship) which preceded the #134 work.
|
|
18
|
+
|
|
19
|
+
### Observations
|
|
20
|
+
|
|
21
|
+
#### What went well
|
|
22
|
+
|
|
23
|
+
- The user's Kent Beck prompt ("make the change that makes the change easy") redirected the plan from test-only fixes to targeted production changes, yielding cleaner results — `WidgetLike`, type guards, and SDK-typed options eliminated casts that would have been impossible to remove from tests alone.
|
|
24
|
+
- The user's TDA observation on the `MenuCtx` step caught a deep architectural issue (context threaded 4 layers just to relay to `buildParentSnapshot`).
|
|
25
|
+
Skipping step 4 was the right call — attempting it would have added a production cast or cascaded changes through 6+ files.
|
|
26
|
+
|
|
27
|
+
#### What caused friction (agent side)
|
|
28
|
+
|
|
29
|
+
1. `rabbit-hole` — Step 1 planning involved extensive analysis of whether `CreateSessionOptions` could use full SDK types.
|
|
30
|
+
Spent significant reasoning tracing `ModelRegistry` / `SessionManager` private fields, structural compatibility, `ParentSnapshot` constructibility, and `ResourceLoader` interface width before concluding that only `settingsManager` and the callback signature could be fixed.
|
|
31
|
+
The plan's claim "widen to SDK types" was optimistic about class-with-private-fields constraints.
|
|
32
|
+
Impact: added friction but no rework — the conclusion was correct, just slow to reach.
|
|
33
|
+
|
|
34
|
+
2. `wrong-abstraction` — Step 4 (`MenuCtx`) attempted to mechanically narrow the handler parameter without questioning why `ctx` was threaded 4 layers deep.
|
|
35
|
+
Partially implemented the `MenuCtx` interface before the user's "What am I misunderstanding?"
|
|
36
|
+
prompt exposed the real issue: a TDA violation where intermediate functions carry `ExtensionContext` only to relay it.
|
|
37
|
+
Impact: partial implementation reverted via `git checkout src/ui/agent-menu.ts`; ~10 minutes of wasted edits.
|
|
38
|
+
|
|
39
|
+
3. `instruction-violation` (user-caught) — Used `pnpm exec biome check --write --unsafe` instead of `pnpm run lint:fix` to fix pre-existing lint issues.
|
|
40
|
+
The `/tdd-plan` prompt explicitly says "run `pnpm run lint:fix`."
|
|
41
|
+
Impact: no functional difference (same result), but violated the established convention and used an unnecessary `--unsafe` flag.
|
|
42
|
+
|
|
43
|
+
4. `instruction-violation` (user-caught) — Dismissed pre-existing lint issues as "not from our changes" without fixing them.
|
|
44
|
+
The user asked "Why do we have pre-existing lint issues?"
|
|
45
|
+
— the correct action was to fix them immediately rather than noting and ignoring them.
|
|
46
|
+
Impact: required an extra commit (`style: fix pre-existing lint issues`) that should have been folded into an earlier step.
|
|
47
|
+
|
|
48
|
+
#### What caused friction (user side)
|
|
49
|
+
|
|
50
|
+
- The user's interventions on the `MenuCtx` step and lint issues were both valuable redirections that improved the outcome.
|
|
51
|
+
No mechanical overhead identified — each intervention was strategic judgment that the agent couldn't have reached alone.
|
|
52
|
+
|
|
53
|
+
### Changes made
|
|
54
|
+
|
|
55
|
+
1. `.pi/prompts/tdd-plan.md` — added "Verify green baseline" section (check + lint + test before starting TDD); added "Fix all failures — including pre-existing ones" to the end-of-cycle lint step.
|
|
56
|
+
2. `.pi/prompts/build-plan.md` — same baseline check and fix-all rule for non-TDD plans.
|