@gotgenes/pi-subagents 6.11.0 → 6.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/docs/architecture/architecture.md +17 -16
- package/docs/plans/0134-reduce-as-any-casts.md +366 -0
- package/docs/plans/0135-extract-display-helpers.md +182 -0
- package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +45 -0
- package/docs/retro/0134-reduce-as-any-casts.md +56 -0
- package/package.json +1 -1
- package/src/agent-runner.ts +23 -6
- package/src/index.ts +1 -1
- package/src/renderer.ts +1 -1
- package/src/runtime.ts +14 -2
- package/src/tools/agent-tool.ts +3 -3
- 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 +2 -2
- package/src/ui/agent-menu.ts +1 -1
- package/src/ui/agent-widget.ts +13 -165
- package/src/ui/conversation-viewer.ts +39 -9
- package/src/ui/display.ts +178 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ 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.12.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.12.0...pi-subagents-v6.12.1) (2026-05-22)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* 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))
|
|
14
|
+
* 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))
|
|
15
|
+
* **retro:** add retro notes for issue [#134](https://github.com/gotgenes/pi-packages/issues/134) ([775ce99](https://github.com/gotgenes/pi-packages/commit/775ce99710153d4ebcf40f773eae21553c7f8a82))
|
|
16
|
+
|
|
17
|
+
## [6.12.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.11.0...pi-subagents-v6.12.0) (2026-05-22)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* narrow runtime widget field to WidgetLike interface ([#134](https://github.com/gotgenes/pi-packages/issues/134)) ([afa70ab](https://github.com/gotgenes/pi-packages/commit/afa70ab430109248a8f61ccd182b0f3acd1fa7e1))
|
|
23
|
+
* use SDK types in CreateSessionOptions ([#134](https://github.com/gotgenes/pi-packages/issues/134)) ([c2452af](https://github.com/gotgenes/pi-packages/commit/c2452af0ee3d47d778878443a634ca787f8d0bfb))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* replace message-shape as-any casts with type guards ([#134](https://github.com/gotgenes/pi-packages/issues/134)) ([d7ad65a](https://github.com/gotgenes/pi-packages/commit/d7ad65a61267790ae1ae8414b0c2aa9ebc8ad59c))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Documentation
|
|
32
|
+
|
|
33
|
+
* plan as-any cast reduction in test suite ([#134](https://github.com/gotgenes/pi-packages/issues/134)) ([f7cb1aa](https://github.com/gotgenes/pi-packages/commit/f7cb1aac0963021ae0545b73c88f950a7adb5fd2))
|
|
34
|
+
* **retro:** add retro notes for issue [#133](https://github.com/gotgenes/pi-packages/issues/133) ([be32640](https://github.com/gotgenes/pi-packages/commit/be32640048943059a98fc79797a35dfefd70fc34))
|
|
35
|
+
* update architecture doc for Step I completion ([#134](https://github.com/gotgenes/pi-packages/issues/134)) ([fd4aca7](https://github.com/gotgenes/pi-packages/commit/fd4aca79c74da2b8c4e3c58e2376e0612941d7d9))
|
|
36
|
+
|
|
8
37
|
## [6.11.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v6.10.0...pi-subagents-v6.11.0) (2026-05-22)
|
|
9
38
|
|
|
10
39
|
|
|
@@ -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
|
|
@@ -517,7 +518,7 @@ They are included here because the display extraction unblocks menu decompositio
|
|
|
517
518
|
| ----------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------- |
|
|
518
519
|
| ~~7 `vi.mock()` calls~~ | ~~`agent-runner.test.ts`~~ | ~~Resolved by Step H (#133)~~ |
|
|
519
520
|
| ~~7 `vi.mock()` calls~~ | ~~`agent-runner-extension-tools.test.ts`~~ | ~~Resolved by Step H (#133)~~ |
|
|
520
|
-
| 52 `as any` casts
|
|
521
|
+
| ~~52 `as any` casts~~ | ~~Across test suite~~ | ~~Reduced to 15 by Step I (#134)~~ |
|
|
521
522
|
| 3× duplicated `mockSession()` | agent-manager, record-observer, ui-observer tests | No shared test fixture |
|
|
522
523
|
| 3× duplicated `makeDeps()` | agent-tool, background-spawner, foreground-runner tests | No shared tool-deps fixture |
|
|
523
524
|
| Weak assertions | lifecycle, renderer, session-config tests | `toHaveBeenCalled()` without args, `toContain()` on large strings |
|
|
@@ -549,27 +550,27 @@ Impact: reduces test boilerplate; single source of truth for mock shapes; change
|
|
|
549
550
|
|
|
550
551
|
Impact: all 7 `vi.mock()` calls eliminated from both `agent-runner.test.ts` and `agent-runner-extension-tools.test.ts`; tests verify behavior (turn limits, tool filtering, response collection) through injected stubs; SDK imports moved to the extension entry point.
|
|
551
552
|
|
|
552
|
-
### Step I: Reduce `as any` casts in tests (#134)
|
|
553
|
+
### Step I: Reduce `as any` casts in tests (#134) ✓ done
|
|
553
554
|
|
|
554
|
-
|
|
555
|
-
Remaining casts are addressed by:
|
|
555
|
+
Reduced `as any` count from 93 to 15 (plus 13 explicit `as unknown as T` bridge casts).
|
|
556
556
|
|
|
557
|
-
|
|
558
|
-
2. Replacing `const mockCtx = { cwd: "/tmp" } as any` with properly typed `AssemblerContext` or `ParentSnapshot` objects.
|
|
559
|
-
3. Using `satisfies` assertions where possible instead of `as any`.
|
|
557
|
+
Production changes:
|
|
560
558
|
|
|
561
|
-
|
|
559
|
+
- `ResourceLoaderOptions.appendSystemPromptOverride` typed to match `DefaultResourceLoaderOptions`; `createResourceLoader` factory cast removed from `index.ts`.
|
|
560
|
+
- `CreateSessionOptions.settingsManager` / `RunnerIO.createSettingsManager` typed as `SettingsManager`.
|
|
561
|
+
- `WidgetLike` interface in `runtime.ts` narrows the widget field.
|
|
562
|
+
- Local `ToolCallContent` / `BashExecutionMessage` type guards replace `as any` duck-typing in `conversation-viewer.ts` and `agent-runner.ts`.
|
|
563
|
+
- `textResult()` return no longer casts `details as any`.
|
|
564
|
+
- `toAgentSession()` helper and `STUB_CTX` constant centralise unavoidable bridge casts.
|
|
562
565
|
|
|
563
|
-
|
|
566
|
+
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.
|
|
564
567
|
|
|
565
|
-
|
|
566
|
-
Extract these into `ui/display.ts`:
|
|
568
|
+
### Step J: Extract display helpers (#135) ✓ done
|
|
567
569
|
|
|
568
|
-
|
|
569
|
-
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
Impact: `agent-widget.ts` drops from 600 → ~420 lines; shared display logic has a single import point; menu and tool modules stop importing from the widget.
|
|
570
|
+
`ui/display.ts` now contains all pure formatters, display helpers, constants, and shared types (`Theme`, `AgentDetails`).
|
|
571
|
+
`agent-widget.ts` dropped from 522 → ~340 lines.
|
|
572
|
+
All consumer modules (menu, tools, renderer, conversation viewer) import from `ui/display.ts` directly.
|
|
573
|
+
`test/agent-widget.test.ts` renamed to `test/display.test.ts`.
|
|
573
574
|
|
|
574
575
|
### Step K: Decompose agent-menu.ts (#136)
|
|
575
576
|
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 134
|
|
3
|
+
issue_title: "Reduce `as any` casts in test suite"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reduce as-any casts in test suite
|
|
7
|
+
|
|
8
|
+
## Problem Statement
|
|
9
|
+
|
|
10
|
+
The test suite contains 93 `as any` casts (plus 2 `as any[]`).
|
|
11
|
+
These casts silence the type checker, hiding real errors — if a production interface adds a required field, tests silently pass with incomplete mocks instead of failing at compile time.
|
|
12
|
+
The heaviest offenders are `renderer.test.ts` (29), `runtime.test.ts` (10), `agent-menu.test.ts` (8), and `helpers.test.ts` (7).
|
|
13
|
+
The production source has 8 `as any` casts — 2 SDK bridge casts in `index.ts` and 6 message-shape casts in `conversation-viewer.ts` and `agent-runner.ts`.
|
|
14
|
+
|
|
15
|
+
## Goals
|
|
16
|
+
|
|
17
|
+
- Target near-zero `as any` casts across both source and test code.
|
|
18
|
+
- Widen `CreateSessionOptions` and `ResourceLoaderOptions` to use SDK types, eliminating the SDK bridge casts in `index.ts`.
|
|
19
|
+
- Define local message-content types and type guards in `conversation-viewer.ts` and `agent-runner.ts`, eliminating polymorphic duck-typing casts.
|
|
20
|
+
- Define narrow production interfaces (`MenuCtx`, `WidgetLike`) where SDK types are too wide for test construction.
|
|
21
|
+
- Define typed test factories where partial mocks currently require casting.
|
|
22
|
+
- Preserve existing test coverage — no behavior changes.
|
|
23
|
+
|
|
24
|
+
## Non-Goals
|
|
25
|
+
|
|
26
|
+
- Changing production behavior or public API shapes.
|
|
27
|
+
- Reaching literally zero — 1–3 casts may remain for private-field test access (`(manager as any).cleanupInterval`) where the alternative (exposing internals) is worse than the cast.
|
|
28
|
+
|
|
29
|
+
## Background
|
|
30
|
+
|
|
31
|
+
### Prerequisites
|
|
32
|
+
|
|
33
|
+
Issues #132 and #133 (IO injection) are both closed.
|
|
34
|
+
These eliminated the `vi.mock()`-heavy test patterns and introduced narrow injectable interfaces (`AssemblerIO`, `RunnerIO`), which already removed some `as any` casts from `agent-runner.test.ts` and `session-config.test.ts`.
|
|
35
|
+
|
|
36
|
+
### Current as-any inventory by pattern
|
|
37
|
+
|
|
38
|
+
| Pattern | Count | Primary files |
|
|
39
|
+
| ------------------------------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| Renderer — theme, message, result access | 29 | `renderer.test.ts` |
|
|
41
|
+
| Context — `ctx as any` for handler/menu | 14 | `agent-menu.test.ts`, `agent-manager.test.ts`, `print-mode.test.ts`, `parent-snapshot.test.ts`, `make-deps.test.ts` |
|
|
42
|
+
| Session — `{ session: {} as any }` | 10 | tool tests, `service-adapter.test.ts`, `print-mode.test.ts`, `agent-manager.test.ts` |
|
|
43
|
+
| Runtime/widget — `fakeWidget as any` | 9 | `runtime.test.ts` |
|
|
44
|
+
| Conversation viewer — message shapes | 8 | `conversation-viewer.test.ts`, `src/conversation-viewer.ts` |
|
|
45
|
+
| Helpers — registry, activity, details | 7 | `helpers.test.ts` |
|
|
46
|
+
| RunOptions — `} as any, io)` | 3 | `agent-runner.test.ts` |
|
|
47
|
+
| Tool execute — `{} as any` for ctx | 5 | `steer-tool.test.ts`, `get-result-tool.test.ts`, `make-deps.test.ts` |
|
|
48
|
+
| SDK bridge — `opts as any` in index.ts | 2 | `src/index.ts` |
|
|
49
|
+
| Other — `messages: [] as any[]`, internal access | 6 | `agent-runner*.test.ts`, `usage.test.ts`, `agent-manager.test.ts` |
|
|
50
|
+
|
|
51
|
+
### Constraints from AGENTS.md
|
|
52
|
+
|
|
53
|
+
- Avoid `any` unless absolutely necessary.
|
|
54
|
+
- Prefer explicit configuration over hidden behavior.
|
|
55
|
+
- Keep scope tight; prefer small, reversible changes.
|
|
56
|
+
|
|
57
|
+
## Design Overview
|
|
58
|
+
|
|
59
|
+
### Production changes that make the test changes easy
|
|
60
|
+
|
|
61
|
+
Three targeted source-code changes remove the structural blockers that force casts elsewhere.
|
|
62
|
+
|
|
63
|
+
#### 1. SDK-typed option interfaces (eliminates 2 source casts)
|
|
64
|
+
|
|
65
|
+
`CreateSessionOptions` and `ResourceLoaderOptions` currently use `unknown` for opaque fields (`settingsManager`, `modelRegistry`, `model`).
|
|
66
|
+
Tests never construct these option objects — they mock `io.createSession` and `io.createResourceLoader` at the function level.
|
|
67
|
+
The `unknown` fields don't buy testability; the function-level mock does.
|
|
68
|
+
|
|
69
|
+
The SDK exports `CreateAgentSessionOptions` with all fields optional, using `ModelRegistry`, `Model<any>`, `ResourceLoader`, `SessionManager`, `SettingsManager`.
|
|
70
|
+
Widening `CreateSessionOptions` to use these SDK types makes the `as any` cast in `index.ts` unnecessary while preserving full test mockability.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Before (agent-runner.ts)
|
|
74
|
+
export interface CreateSessionOptions {
|
|
75
|
+
settingsManager: unknown;
|
|
76
|
+
modelRegistry: unknown;
|
|
77
|
+
model?: unknown;
|
|
78
|
+
// ...
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// After — use SDK types (type-only imports)
|
|
82
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
83
|
+
import type {
|
|
84
|
+
ModelRegistry,
|
|
85
|
+
ResourceLoader,
|
|
86
|
+
SessionManager,
|
|
87
|
+
SettingsManager,
|
|
88
|
+
} from "@earendil-works/pi-coding-agent";
|
|
89
|
+
|
|
90
|
+
export interface CreateSessionOptions {
|
|
91
|
+
settingsManager: SettingsManager;
|
|
92
|
+
modelRegistry: ModelRegistry;
|
|
93
|
+
model?: Model<any>;
|
|
94
|
+
resourceLoader: ResourceLoader;
|
|
95
|
+
sessionManager: SessionManager;
|
|
96
|
+
// ...
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The `RunnerIO.createSettingsManager` return type also changes from `unknown` to `SettingsManager`.
|
|
101
|
+
|
|
102
|
+
For `ResourceLoaderOptions`, the options are all primitives and callbacks — the SDK's `DefaultResourceLoader` constructor accepts them structurally.
|
|
103
|
+
The fix is to ensure the option interface matches the constructor's parameter type so `new DefaultResourceLoader(opts)` works without `as any`.
|
|
104
|
+
|
|
105
|
+
#### 2. Local message-content types + type guards (eliminates 6 source casts)
|
|
106
|
+
|
|
107
|
+
`conversation-viewer.ts` and `agent-runner.ts` both do:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
(c as any).name ?? (c as any).toolName ?? "unknown"
|
|
111
|
+
(msg as any).role === "bashExecution"
|
|
112
|
+
const bash = msg as any;
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Fix: define local discriminated-union types for the content items and message roles the code actually handles, plus type guards:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
/** Tool-call content item — SDK doesn't export a narrow type for this variant. */
|
|
119
|
+
interface ToolCallContent {
|
|
120
|
+
type: "toolCall";
|
|
121
|
+
name?: string;
|
|
122
|
+
toolName?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getToolCallName(c: { type: string }): string | undefined {
|
|
126
|
+
if (c.type !== "toolCall") return undefined;
|
|
127
|
+
const tc = c as ToolCallContent;
|
|
128
|
+
return tc.name ?? tc.toolName;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Bash execution message — not in the standard role union. */
|
|
132
|
+
interface BashExecutionMessage {
|
|
133
|
+
role: "bashExecution";
|
|
134
|
+
command: string;
|
|
135
|
+
output?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isBashExecution(msg: { role: string }): msg is BashExecutionMessage {
|
|
139
|
+
return msg.role === "bashExecution";
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
These are small, file-local types that document the shapes the code already handles at runtime.
|
|
144
|
+
They make the duck-typing explicit and compile-time checked.
|
|
145
|
+
|
|
146
|
+
#### 3. Narrow production interfaces (`MenuCtx`, `WidgetLike`)
|
|
147
|
+
|
|
148
|
+
Already described in the test-side patterns below.
|
|
149
|
+
These are production-code changes that make the test-side casts disappear.
|
|
150
|
+
|
|
151
|
+
### Test-side patterns
|
|
152
|
+
|
|
153
|
+
#### Pattern A: renderer.test.ts (29 casts → 0)
|
|
154
|
+
|
|
155
|
+
The renderer already defines narrow interfaces (`RendererMessage`, `RendererTheme`, `RenderOptions`).
|
|
156
|
+
The test casts because it doesn't use them — `stubTheme()` and `{ details: makeDetails() }` already satisfy the interfaces structurally.
|
|
157
|
+
The return type `Text` has a `.text` property — `(result as any).text` can become `result!.text` (non-null assertion after `expect(result).toBeDefined()`).
|
|
158
|
+
|
|
159
|
+
Fix: remove the casts; structural typing handles it.
|
|
160
|
+
For result access, define a minimal `{ text: string }` type or use non-null assertion.
|
|
161
|
+
|
|
162
|
+
#### Pattern B: context casts (14 casts → 0)
|
|
163
|
+
|
|
164
|
+
- `agent-menu.test.ts` (8): define `MenuCtx` in production; type `makeCtx()` return.
|
|
165
|
+
- `agent-manager.test.ts` (1): `mockCtx` is consumed by a mocked `buildParentSnapshot` — type as `unknown`.
|
|
166
|
+
- `parent-snapshot.test.ts` (1): define a test-local interface matching the fields `buildParentSnapshot` reads.
|
|
167
|
+
- `print-mode.test.ts` (2): type `makeHeadlessCtx()` return.
|
|
168
|
+
- `runtime.test.ts` (1): pass a structurally valid `UICtx` (fixed by `WidgetLike` step).
|
|
169
|
+
- `make-deps.test.ts` (3): type ctx args or use `unknown`.
|
|
170
|
+
|
|
171
|
+
#### Pattern C: session casts (10 casts → ~1)
|
|
172
|
+
|
|
173
|
+
Most are `{ session: {} as any, outputFile: ... }` for `record.execution`.
|
|
174
|
+
Fix: expand `createMockSession()` to include the fields these tests need (`dispose`, `steer`, `getSessionStats`), then use it.
|
|
175
|
+
One cast may remain for truly minimal session stubs.
|
|
176
|
+
|
|
177
|
+
#### Pattern D: runtime/widget casts (9 casts → 0)
|
|
178
|
+
|
|
179
|
+
Define `WidgetLike` in `runtime.ts`; change the `widget` field type.
|
|
180
|
+
Use real `AgentActivityTracker` instances (constructor takes only `maxTurns?`).
|
|
181
|
+
|
|
182
|
+
#### Pattern E: tool-execute ctx casts (5 casts → 0)
|
|
183
|
+
|
|
184
|
+
Define a `STUB_CTX` constant in `test/helpers/` satisfying the tool execute's context parameter.
|
|
185
|
+
|
|
186
|
+
#### Pattern F: RunOptions casts (3 casts → 0)
|
|
187
|
+
|
|
188
|
+
Check whether `defaultMaxTurns` and `graceTurns` are on `RunOptions` — they are.
|
|
189
|
+
The `as any` casts are vestigial; remove them.
|
|
190
|
+
|
|
191
|
+
#### Pattern G: helpers.test.ts (7 casts → 0)
|
|
192
|
+
|
|
193
|
+
Construct typed `TypeListRegistry` mocks.
|
|
194
|
+
Fix `textResult` return type to avoid `details as any`.
|
|
195
|
+
Use real `AgentActivityTracker` instances.
|
|
196
|
+
|
|
197
|
+
#### Pattern H: conversation-viewer test casts (4 test casts → ~1)
|
|
198
|
+
|
|
199
|
+
Type message mock objects using the local types from production change #2.
|
|
200
|
+
Keep `(viewer as any).buildContentLines()` (private method access — the alternative of exposing the method is worse).
|
|
201
|
+
|
|
202
|
+
#### Pattern I: other (6 casts → ~2)
|
|
203
|
+
|
|
204
|
+
- `messages: [] as any[]` → type as `unknown[]`.
|
|
205
|
+
- `(manager as any).cleanupInterval` → keep (private field assertion is intentional).
|
|
206
|
+
- `usage.test.ts` (2) → type mock objects.
|
|
207
|
+
|
|
208
|
+
## Module-Level Changes
|
|
209
|
+
|
|
210
|
+
### Modified source files
|
|
211
|
+
|
|
212
|
+
1. `src/agent-runner.ts`
|
|
213
|
+
- Import SDK types (`Model`, `ModelRegistry`, `SettingsManager`, `SessionManager`, `ResourceLoader`) as type-only imports.
|
|
214
|
+
- Widen `CreateSessionOptions` fields from `unknown` to SDK types.
|
|
215
|
+
- Change `RunnerIO.createSettingsManager` return type from `unknown` to `SettingsManager`.
|
|
216
|
+
- Define `getToolCallName()` helper and local `ToolCallContent` interface.
|
|
217
|
+
- Replace `(c as any).name ?? (c as any).toolName` with `getToolCallName(c)` in `getAgentConversation`.
|
|
218
|
+
|
|
219
|
+
2. `src/ui/conversation-viewer.ts`
|
|
220
|
+
- Define local `ToolCallContent`, `BashExecutionMessage` interfaces and type guards.
|
|
221
|
+
- Replace `(c as any).name ?? (c as any).toolName` with typed helper.
|
|
222
|
+
- Replace `(msg as any).role === "bashExecution"` / `const bash = msg as any` with type guard.
|
|
223
|
+
|
|
224
|
+
3. `src/ui/agent-menu.ts`
|
|
225
|
+
- Define and export `MenuCtx` interface.
|
|
226
|
+
- Change handler parameter type from `ExtensionContext` to `MenuCtx`.
|
|
227
|
+
|
|
228
|
+
4. `src/runtime.ts`
|
|
229
|
+
- Define and export `WidgetLike` interface.
|
|
230
|
+
- Change `widget` field type from `AgentWidget | null` to `WidgetLike | null`.
|
|
231
|
+
|
|
232
|
+
5. `src/tools/helpers.ts`
|
|
233
|
+
- Change `textResult` to avoid `details as any` — type the return properly.
|
|
234
|
+
|
|
235
|
+
6. `src/index.ts`
|
|
236
|
+
- Remove `opts as any` casts in `createResourceLoader` and `createSession` factories (enabled by SDK-typed option interfaces).
|
|
237
|
+
|
|
238
|
+
### Modified test files
|
|
239
|
+
|
|
240
|
+
1. `test/renderer.test.ts` — remove all 29 casts.
|
|
241
|
+
2. `test/runtime.test.ts` — use `WidgetLike` stubs and real `AgentActivityTracker`.
|
|
242
|
+
3. `test/ui/agent-menu.test.ts` — type `makeCtx()` as `MenuCtx`.
|
|
243
|
+
4. `test/tools/helpers.test.ts` — typed registry mocks, real `AgentActivityTracker`.
|
|
244
|
+
5. `test/conversation-viewer.test.ts` — typed message mocks.
|
|
245
|
+
6. `test/agent-runner.test.ts` — remove `as any` on RunOptions; type messages.
|
|
246
|
+
7. `test/agent-runner-extension-tools.test.ts` — type messages.
|
|
247
|
+
8. `test/agent-manager.test.ts` — type `mockCtx`.
|
|
248
|
+
9. `test/print-mode.test.ts` — type `makeHeadlessCtx()`.
|
|
249
|
+
10. `test/parent-snapshot.test.ts` — type mock context.
|
|
250
|
+
11. `test/service-adapter.test.ts` — use `createMockSession()`.
|
|
251
|
+
12. `test/tools/steer-tool.test.ts` — stub ctx, use `createMockSession()`.
|
|
252
|
+
13. `test/tools/get-result-tool.test.ts` — stub ctx, use `createMockSession()`.
|
|
253
|
+
14. `test/tools/foreground-runner.test.ts` — use `createMockSession()`.
|
|
254
|
+
15. `test/tools/background-spawner.test.ts` — use `createMockSession()`.
|
|
255
|
+
16. `test/tools/agent-tool.test.ts` — use `createMockSession()`.
|
|
256
|
+
17. `test/helpers/make-deps.test.ts` — type ctx args.
|
|
257
|
+
18. `test/usage.test.ts` — type mock objects.
|
|
258
|
+
|
|
259
|
+
## Test Impact Analysis
|
|
260
|
+
|
|
261
|
+
1. No new test coverage — this is a type-safety improvement on existing tests.
|
|
262
|
+
2. No tests become redundant.
|
|
263
|
+
3. All existing tests stay as-is in terms of assertions; only type annotations and mock construction change.
|
|
264
|
+
4. `pnpm run check` is the primary validation — every step must pass type checking since the goal is eliminating type holes.
|
|
265
|
+
5. Expanding `createMockSession()` affects multiple consumers — diff existing defaults before changing (per testing skill).
|
|
266
|
+
|
|
267
|
+
## TDD Order
|
|
268
|
+
|
|
269
|
+
Steps are ordered by independence and impact (production changes first, then largest cast-count test reductions).
|
|
270
|
+
|
|
271
|
+
1. **Widen option interfaces to SDK types; remove index.ts casts (2 source casts → 0).**
|
|
272
|
+
Import SDK types as type-only in `agent-runner.ts`.
|
|
273
|
+
Widen `CreateSessionOptions` fields (`settingsManager`, `modelRegistry`, `model`, `resourceLoader`, `sessionManager`) to SDK types.
|
|
274
|
+
Change `RunnerIO.createSettingsManager` return type to `SettingsManager`.
|
|
275
|
+
Remove `as any` casts from `index.ts` factories.
|
|
276
|
+
Run `pnpm run check` + full suite.
|
|
277
|
+
Commit: `feat: use SDK types in CreateSessionOptions (#134)`
|
|
278
|
+
|
|
279
|
+
2. **Add message-content type guards; remove viewer and runner source casts (6 source casts → 0).**
|
|
280
|
+
Define `ToolCallContent` interface and `getToolCallName()` helper in `conversation-viewer.ts`.
|
|
281
|
+
Define `BashExecutionMessage` interface and `isBashExecution()` guard in `conversation-viewer.ts`.
|
|
282
|
+
Replace all source `as any` casts in `conversation-viewer.ts`.
|
|
283
|
+
Define the same `getToolCallName()` helper (or extract a shared one) in `agent-runner.ts` and replace its cast.
|
|
284
|
+
Run `pnpm run check` + affected tests.
|
|
285
|
+
Commit: `fix: replace message-shape as-any casts with type guards (#134)`
|
|
286
|
+
|
|
287
|
+
3. **Remove renderer test casts (29 → 0).**
|
|
288
|
+
Remove all `as any` casts on `stubTheme()`, message objects, and result access.
|
|
289
|
+
Use `result!.text` (non-null assertion after `toBeDefined()` guard) for result access.
|
|
290
|
+
Run `pnpm run check` + `pnpm vitest run test/renderer.test.ts`.
|
|
291
|
+
Commit: `test: remove as-any casts in renderer tests (#134)`
|
|
292
|
+
|
|
293
|
+
4. **Add `MenuCtx` interface; remove menu-test casts (8 → 0).**
|
|
294
|
+
Define `MenuCtx` in `agent-menu.ts`.
|
|
295
|
+
Change handler parameter from `ExtensionContext` to `MenuCtx`.
|
|
296
|
+
Type `makeCtx()` return in test.
|
|
297
|
+
Run `pnpm run check` + `pnpm vitest run test/ui/agent-menu.test.ts`.
|
|
298
|
+
Commit: `feat: narrow menu handler to MenuCtx interface (#134)`
|
|
299
|
+
|
|
300
|
+
5. **Add `WidgetLike`; remove runtime casts (9 → 0).**
|
|
301
|
+
Define `WidgetLike` in `runtime.ts`.
|
|
302
|
+
Change `widget` field type.
|
|
303
|
+
Update `runtime.test.ts`: typed stubs, real `AgentActivityTracker` instances.
|
|
304
|
+
Run `pnpm run check` + `pnpm vitest run test/runtime.test.ts`.
|
|
305
|
+
Commit: `feat: narrow runtime widget field to WidgetLike interface (#134)`
|
|
306
|
+
|
|
307
|
+
6. **Expand `createMockSession`; remove session casts (10 → ~1).**
|
|
308
|
+
Add fields to `createMockSession()` that tool/service tests need.
|
|
309
|
+
Use it in tool tests, service-adapter, agent-manager for `record.execution.session`.
|
|
310
|
+
Run `pnpm run check` + full suite.
|
|
311
|
+
Commit: `test: use createMockSession for session execution casts (#134)`
|
|
312
|
+
|
|
313
|
+
7. **Remove helpers.test.ts casts (7 → 0).**
|
|
314
|
+
Typed `TypeListRegistry` mocks.
|
|
315
|
+
Fix `textResult` return type in `src/tools/helpers.ts`.
|
|
316
|
+
Real `AgentActivityTracker` instances.
|
|
317
|
+
Run `pnpm run check` + `pnpm vitest run test/tools/helpers.test.ts`.
|
|
318
|
+
Commit: `test: remove as-any casts in helpers tests (#134)`
|
|
319
|
+
|
|
320
|
+
8. **Remove context casts across remaining test files (6 → 0).**
|
|
321
|
+
Type `mockCtx` in `agent-manager.test.ts`.
|
|
322
|
+
Type `makeHeadlessCtx()` in `print-mode.test.ts`.
|
|
323
|
+
Type mock context in `parent-snapshot.test.ts`.
|
|
324
|
+
Type `{} as any` in `make-deps.test.ts`.
|
|
325
|
+
Run `pnpm run check` + full suite.
|
|
326
|
+
Commit: `test: remove remaining context as-any casts (#134)`
|
|
327
|
+
|
|
328
|
+
9. **Remove RunOptions and message-array casts (5 → 0).**
|
|
329
|
+
Remove vestigial `as any` on RunOptions objects.
|
|
330
|
+
Type `messages` arrays as `unknown[]`.
|
|
331
|
+
Run `pnpm run check` + affected tests.
|
|
332
|
+
Commit: `test: remove RunOptions and message-array casts (#134)`
|
|
333
|
+
|
|
334
|
+
10. **Remove tool-execute ctx casts (5 → 0).**
|
|
335
|
+
Define `STUB_CTX` in `test/helpers/`.
|
|
336
|
+
Use it in `steer-tool.test.ts`, `get-result-tool.test.ts`, `make-deps.test.ts`.
|
|
337
|
+
Run `pnpm run check` + affected tests.
|
|
338
|
+
Commit: `test: remove tool-execute context casts (#134)`
|
|
339
|
+
|
|
340
|
+
11. **Clean up conversation-viewer test and usage casts (6 → ~2).**
|
|
341
|
+
Type message mocks in `conversation-viewer.test.ts`.
|
|
342
|
+
Type usage mock objects in `usage.test.ts`.
|
|
343
|
+
Keep `(viewer as any).buildContentLines()` and `(manager as any).cleanupInterval` (private access, intentional).
|
|
344
|
+
Run `pnpm run check` + full suite.
|
|
345
|
+
Commit: `test: remove conversation-viewer and usage as-any casts (#134)`
|
|
346
|
+
|
|
347
|
+
## Risks and Mitigations
|
|
348
|
+
|
|
349
|
+
| Risk | Mitigation |
|
|
350
|
+
| ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
351
|
+
| Widening `CreateSessionOptions` to SDK types re-couples `agent-runner.ts` to SDK | These are type-only imports — no runtime coupling. Tests still mock at the `RunnerIO` function level. The `RunnerIO` interface is the decoupling boundary, not the option types. |
|
|
352
|
+
| Narrowing `widget` to `WidgetLike` could break callers accessing `AgentWidget`-specific methods | Grep all `runtime.widget` access sites first; verify they only use `update()`, `markFinished()`, `setUICtx()`. |
|
|
353
|
+
| Narrowing menu handler to `MenuCtx` could break callers passing `ExtensionContext` | `ExtensionContext` is structurally a superset of `MenuCtx` — callers pass it unchanged. |
|
|
354
|
+
| Expanding `createMockSession()` could break consumers with different default expectations | Diff existing consumers' default expectations before adding fields (per testing skill). |
|
|
355
|
+
| Message type guards add code to conversation-viewer and agent-runner | Each guard is 2–4 lines; they replace unsafe `as any` access with documented, compile-checked types. Net code stays similar. |
|
|
356
|
+
| 11-step plan is large | Each step is independently committable. No step depends on a subsequent one. Steps can be reordered or skipped. |
|
|
357
|
+
|
|
358
|
+
## Open Questions
|
|
359
|
+
|
|
360
|
+
- Should `getToolCallName()` be shared between `conversation-viewer.ts` and `agent-runner.ts`, or duplicated?
|
|
361
|
+
Both files handle the same SDK message shape.
|
|
362
|
+
A shared helper in a common module (e.g., `context.ts` which already has `extractText`) avoids duplication.
|
|
363
|
+
Alternatively, the duplication is cheap (3 lines) and the two files have different concerns.
|
|
364
|
+
- Should `STUB_CTX` live in `test/helpers/stub-ctx.ts` or inline?
|
|
365
|
+
Centralize — 3+ tool tests share the pattern.
|
|
366
|
+
- Estimated final count: 2–3 remaining casts (`(viewer as any).buildContentLines()`, `(manager as any).cleanupInterval`, possibly 1 more for an unexported SDK type).
|
|
@@ -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.
|