@gotgenes/pi-subagents 18.0.0 → 18.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +22 -31
  3. package/docs/architecture/architecture.md +157 -204
  4. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  5. package/package.json +5 -6
  6. package/src/lifecycle/subagent.ts +5 -9
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/tools/get-result-report.ts +60 -0
  10. package/src/tools/get-result-tool.ts +25 -40
  11. package/src/types.ts +1 -1
  12. package/src/ui/session-navigator.ts +0 -2
  13. package/.prettierignore +0 -5
  14. package/AGENTS.md +0 -7
  15. package/docs/plans/0048-implement-subagents-api.md +0 -304
  16. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  17. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  18. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  19. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  20. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  21. package/docs/plans/0057-structured-debug-logging.md +0 -154
  22. package/docs/plans/0061-session-format-transcript.md +0 -284
  23. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  24. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  25. package/docs/plans/0070-extract-event-handlers.md +0 -306
  26. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  27. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  28. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  29. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  30. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  31. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  32. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  33. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  34. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  35. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  36. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  37. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  38. package/docs/plans/0109-extract-settings-manager.md +0 -276
  39. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  40. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  41. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  42. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  43. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  44. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  45. package/docs/plans/0116-type-housekeeping.md +0 -351
  46. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  47. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  48. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  49. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  50. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  51. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  52. package/docs/plans/0135-extract-display-helpers.md +0 -182
  53. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  54. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  55. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  56. package/docs/plans/0146-narrow-ui-context.md +0 -319
  57. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  58. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  59. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  60. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  61. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  62. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  63. package/docs/plans/0167-narrow-runner-io.md +0 -150
  64. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  65. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  66. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  67. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  68. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  69. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  70. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  71. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  72. package/docs/plans/0192-define-session-context-interface.md +0 -107
  73. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  74. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  75. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  76. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  77. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  78. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  79. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  80. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  81. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  82. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  83. package/docs/plans/0216-decompose-start-agent.md +0 -255
  84. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  85. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  86. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  87. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  88. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  89. package/docs/plans/0229-agent-born-complete.md +0 -564
  90. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  91. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  92. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  93. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  94. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  95. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  96. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  97. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  98. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  99. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  100. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  101. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  102. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  103. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  104. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  105. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  106. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  107. package/docs/plans/0373-extract-subagent-state.md +0 -250
  108. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  109. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  110. package/docs/plans/0376-extract-manager-observer.md +0 -232
  111. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  112. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  113. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  114. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  115. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  116. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  117. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  118. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  119. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  120. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  121. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  122. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  123. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  124. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  125. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  126. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  127. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  128. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  129. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  130. package/docs/plans/0445-native-session-navigation.md +0 -365
  131. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  132. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  133. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  134. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  135. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  136. package/docs/retro/0048-implement-subagents-api.md +0 -44
  137. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  138. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  139. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  140. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  141. package/docs/retro/0057-structured-debug-logging.md +0 -77
  142. package/docs/retro/0061-session-format-transcript.md +0 -41
  143. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  144. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  145. package/docs/retro/0070-extract-event-handlers.md +0 -49
  146. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  147. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  148. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  149. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  150. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  151. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  152. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  153. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  154. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  155. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  156. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  157. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  158. package/docs/retro/0109-extract-settings-manager.md +0 -55
  159. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  160. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  161. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  162. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  163. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  164. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  165. package/docs/retro/0116-type-housekeeping.md +0 -42
  166. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  167. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  168. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  169. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  170. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  171. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  172. package/docs/retro/0135-extract-display-helpers.md +0 -38
  173. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  174. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  175. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  176. package/docs/retro/0146-narrow-ui-context.md +0 -70
  177. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  178. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  179. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  180. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  181. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  182. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  183. package/docs/retro/0167-narrow-runner-io.md +0 -63
  184. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  185. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  186. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  187. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  188. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  189. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  190. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  191. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  192. package/docs/retro/0192-define-session-context-interface.md +0 -59
  193. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  194. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  195. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  196. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  197. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  198. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  199. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  200. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  201. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  202. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  203. package/docs/retro/0216-decompose-start-agent.md +0 -80
  204. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  205. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  206. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  207. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  208. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  209. package/docs/retro/0229-agent-born-complete.md +0 -89
  210. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  211. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  212. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  213. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  214. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  215. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  216. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  217. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  218. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  219. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  220. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  221. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  222. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  223. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  224. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  225. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  226. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  227. package/docs/retro/0373-extract-subagent-state.md +0 -94
  228. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  229. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  230. package/docs/retro/0376-extract-manager-observer.md +0 -81
  231. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  232. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  233. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  234. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  235. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  236. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  237. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  238. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  239. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  240. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  241. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  242. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  243. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  244. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  245. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  246. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  247. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -45
  248. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  249. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  250. package/docs/retro/0445-native-session-navigation.md +0 -122
  251. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  252. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  253. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  254. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  255. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  256. package/src/observation/notification-state.ts +0 -27
  257. package/vitest.config.ts +0 -14
@@ -1,264 +0,0 @@
1
- ---
2
- issue: 195
3
- issue_title: "Convert tool factories to classes"
4
- ---
5
-
6
- # Convert tool factories to classes
7
-
8
- ## Problem Statement
9
-
10
- `createAgentTool`, `createGetResultTool`, and `createSteerTool` are closure factories that capture dependencies and return tool definitions.
11
- This pattern hides dependencies in closures, forces `index.ts` to build narrow adapter objects at each call site, and prevents direct structural typing between real objects and tool interfaces.
12
- With #193 (runtime owns queries) and #194 (interfaces aligned) both complete, the conversions are mechanical.
13
-
14
- ## Goals
15
-
16
- - Convert all three tool factories to classes with constructor-injected dependencies.
17
- - Each class exposes a `toToolDefinition()` method that wraps the tool with `defineTool()`.
18
- - `index.ts` passes real objects directly — no adapter closures for tool wiring.
19
- - Eliminate 16+ adapter closures from `index.ts`.
20
-
21
- ## Non-Goals
22
-
23
- - Converting `createAgentRunner` or `createAgentsMenuHandler` — those belong to #196.
24
- - Simplifying `index.ts` beyond tool wiring — that's #196 Layer 4.
25
- - Changing tool behavior or parameters — this is a purely structural refactoring.
26
-
27
- ## Background
28
-
29
- ### Dependencies (both closed)
30
-
31
- - Issue #193 moved context queries (`buildSnapshot`, `getModelInfo`, `getSessionInfo`) onto `SubagentRuntime`.
32
- - Issue #194 aligned tool interfaces so `AgentManager` and `SubagentRuntime` structurally satisfy the narrow tool interfaces without adapters.
33
-
34
- ### Existing module layout
35
-
36
- The three factory functions live in:
37
-
38
- - `src/tools/agent-tool.ts` — `createAgentTool(deps: AgentToolDeps)`
39
- - `src/tools/get-result-tool.ts` — `createGetResultTool(getRecord, cancelNudge, getConversation, registry)`
40
- - `src/tools/steer-tool.ts` — `createSteerTool(getRecord, emitEvent, steerAgent, queueSteer)`
41
-
42
- Each returns a plain object with `name`, `label`, `description`, `parameters`, `execute`, and optional render methods.
43
- `index.ts` wraps each in `defineTool()` at registration time.
44
-
45
- ### Architecture doc reference
46
-
47
- Phase 11, Layer 3 in `docs/architecture/architecture.md` (lines 689–709).
48
-
49
- ## Design Overview
50
-
51
- ### Class structure
52
-
53
- Each class stores constructor-injected dependencies as instance fields and exposes:
54
-
55
- 1. A `toToolDefinition()` method that calls `defineTool(this.buildToolSpec())` (or similar) — returning the Pi SDK tool registration object.
56
- 2. Private methods that correspond to the existing function body sections.
57
-
58
- ### Constructor signatures
59
-
60
- | Class | Constructor params |
61
- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
62
- | `AgentTool` | `manager: AgentToolManager`, `runtime: AgentToolRuntime`, `settings: AgentToolSettings`, `registry: AgentTypeRegistry`, `agentDir: string` |
63
- | `GetResultTool` | `manager: GetResultToolManager`, `notifications: GetResultToolNotifications`, `registry: AgentConfigLookup` |
64
- | `SteerTool` | `manager: SteerToolManager`, `events: SteerToolEvents` |
65
-
66
- ### Narrow interfaces
67
-
68
- Each class defines its own narrow interface for the collaborators it uses.
69
- These already exist (e.g., `AgentToolManager`, `BackgroundManagerDeps`) — they stay as-is; the class uses them directly.
70
-
71
- #### `AgentTool`
72
-
73
- The existing `AgentToolDeps` interface (8 fields) collapses into 5 constructor params.
74
- The `widget`, `agentActivity`, `buildSnapshot`, `getModelInfo`, and `getSessionInfo` fields merge into a single `runtime` param because `SubagentRuntime` structurally satisfies all of them after #193/#194.
75
-
76
- New narrow interface for the runtime slice:
77
-
78
- ```typescript
79
- export interface AgentToolRuntime {
80
- readonly agentActivity: AgentActivityAccess;
81
- setUICtx(ctx: UICtx): void;
82
- ensureTimer(): void;
83
- update(): void;
84
- markFinished(id: string): void;
85
- buildSnapshot(inheritContext: boolean): ParentSnapshot;
86
- getModelInfo(): ModelInfo;
87
- getSessionInfo(): { parentSessionFile: string; parentSessionId: string };
88
- }
89
- ```
90
-
91
- `SubagentRuntime` satisfies this structurally — no adapter needed in `index.ts`.
92
-
93
- #### `GetResultTool`
94
-
95
- New narrow interfaces:
96
-
97
- ```typescript
98
- export interface GetResultToolManager {
99
- getRecord(id: string): AgentRecord | undefined;
100
- }
101
-
102
- export interface GetResultToolNotifications {
103
- cancelNudge(key: string): void;
104
- }
105
- ```
106
-
107
- `getAgentConversation` is a pure function — imported directly by the class, not injected.
108
-
109
- #### `SteerTool`
110
-
111
- New narrow interfaces:
112
-
113
- ```typescript
114
- export interface SteerToolManager {
115
- getRecord(id: string): AgentRecord | undefined;
116
- queueSteer(id: string, message: string): boolean;
117
- }
118
-
119
- export interface SteerToolEvents {
120
- emit(name: string, data: unknown): void;
121
- }
122
- ```
123
-
124
- `steerAgent` is a pure function — imported directly by the class, not injected.
125
-
126
- ### Call site after conversion
127
-
128
- ```typescript
129
- const agentTool = new AgentTool(manager, runtime, settings, registry, getAgentDir());
130
- const getResultTool = new GetResultTool(manager, notifications, registry);
131
- const steerTool = new SteerTool(manager, pi.events);
132
- pi.registerTool(agentTool.toToolDefinition());
133
- pi.registerTool(getResultTool.toToolDefinition());
134
- pi.registerTool(steerTool.toToolDefinition());
135
- ```
136
-
137
- ### Design decisions
138
-
139
- 1. **`toToolDefinition()` on each class** — encapsulates the `defineTool()` call so `index.ts` doesn't import `defineTool` for each tool.
140
- 2. **Pure functions imported directly** — `steerAgent` and `getAgentConversation` are pure utilities with no state; injecting them adds indirection without testability benefit.
141
- 3. **`agentDir` stays a constructor param** — it's a static string computed once at startup; putting it on the registry would add scope responsibility the registry doesn't need.
142
- 4. **Keep existing `AgentToolDeps` temporarily** — remove it in the same PR once the class replaces all usage.
143
-
144
- ## Module-Level Changes
145
-
146
- ### `src/tools/agent-tool.ts`
147
-
148
- - Add `AgentToolRuntime` interface (narrow runtime slice).
149
- - Add `AgentToolSettings` type alias (existing inline type, extracted for clarity).
150
- - Add `AgentTool` class with constructor storing `manager`, `runtime`, `settings`, `registry`, `agentDir`.
151
- - Move the existing factory body into class methods: `toToolDefinition()`, private `execute()`.
152
- - Remove `createAgentTool` function and `AgentToolDeps` interface.
153
- - Keep `AgentToolManager`, `AgentToolWidget`, `AgentActivityAccess` interfaces (still used by `background-spawner` and `foreground-runner` — though `AgentToolWidget` may be replaceable by `AgentToolRuntime`).
154
-
155
- ### `src/tools/get-result-tool.ts`
156
-
157
- - Add `GetResultToolManager` and `GetResultToolNotifications` interfaces.
158
- - Add `GetResultTool` class with constructor.
159
- - Import `getAgentConversation` directly.
160
- - Move factory body into class method.
161
- - Remove `createGetResultTool` function.
162
-
163
- ### `src/tools/steer-tool.ts`
164
-
165
- - Add `SteerToolManager` and `SteerToolEvents` interfaces.
166
- - Add `SteerTool` class with constructor.
167
- - Import `steerAgent` directly.
168
- - Move factory body into class method.
169
- - Remove `createSteerTool` function.
170
-
171
- ### `src/tools/background-spawner.ts`
172
-
173
- - Update `BackgroundWidgetDeps` to reference `AgentToolRuntime` instead (or keep as-is since `AgentToolRuntime` is a superset).
174
- Actually no change needed — `spawnBackground` already accepts narrow interfaces; the `AgentTool` class passes `this.runtime` which satisfies `BackgroundWidgetDeps` structurally.
175
-
176
- ### `src/tools/foreground-runner.ts`
177
-
178
- - Same as above — no change needed.
179
- The `AgentTool` class passes `this.runtime` which satisfies `ForegroundWidgetDeps` structurally.
180
-
181
- ### `src/index.ts`
182
-
183
- - Remove adapter closures for all three tool registrations.
184
- - Replace with class construction + `toToolDefinition()` calls.
185
- - Remove `defineTool` import (moves into tool classes).
186
- - Remove unused imports that were only needed for adapter closures.
187
-
188
- ### `test/tools/agent-tool.test.ts`
189
-
190
- - Replace `createToolDeps()` with class construction in test helpers.
191
- - Update `execute()` helper to instantiate `AgentTool` and call its execute.
192
- - Keep all existing test cases — behavior is unchanged.
193
-
194
- ### `test/tools/get-result-tool.test.ts`
195
-
196
- - Replace positional args with class construction.
197
- - Update `makeDeps` and `execute` helpers.
198
- - Keep all existing test cases.
199
-
200
- ### `test/tools/steer-tool.test.ts`
201
-
202
- - Replace positional args with class construction.
203
- - Update `makeDeps` and `execute` helpers.
204
- - Keep all existing test cases.
205
-
206
- ### `test/helpers/make-deps.ts`
207
-
208
- - Update `createToolDeps` to construct `AgentTool`-compatible deps (or remove if no longer needed).
209
- Likely transforms into a factory that builds mock `AgentToolRuntime`, `AgentToolManager`, etc.
210
-
211
- ### `docs/architecture/architecture.md`
212
-
213
- - Update the Layer 3 table to mark #195 as done.
214
- - Update the file listing for `src/tools/` to reflect class names (if the listing is that detailed).
215
-
216
- ## Test Impact Analysis
217
-
218
- 1. **No new test surfaces** — this is a structural refactoring with identical behavior.
219
- Existing tests already cover all tool paths.
220
- 2. **Test helper changes** — `createToolDeps()` and per-tool `makeDeps()` functions need updating to use class construction instead of function calls, but the mock shapes remain the same.
221
- 3. **All existing tests stay** — they exercise the tool execution logic which doesn't change.
222
-
223
- ## TDD Order
224
-
225
- 1. **Convert `SteerTool` to class** — smallest tool (fewest deps, no render methods).
226
- - Update `steer-tool.ts`: add interfaces, add class, remove factory.
227
- - Update `test/tools/steer-tool.test.ts`: use class construction.
228
- - Verify: `pnpm vitest run test/tools/steer-tool.test.ts`
229
- - Commit: `refactor: convert createSteerTool to SteerTool class (#195)`
230
-
231
- 2. **Convert `GetResultTool` to class** — medium complexity (3 deps, no render methods).
232
- - Update `get-result-tool.ts`: add interfaces, add class, import `getAgentConversation`, remove factory.
233
- - Update `test/tools/get-result-tool.test.ts`: use class construction.
234
- - Verify: `pnpm vitest run test/tools/get-result-tool.test.ts`
235
- - Commit: `refactor: convert createGetResultTool to GetResultTool class (#195)`
236
-
237
- 3. **Convert `AgentTool` to class** — largest (5 deps, render methods, delegates to spawner/runner).
238
- - Add `AgentToolRuntime` and `AgentToolSettings` interfaces.
239
- - Add `AgentTool` class.
240
- - Remove `createAgentTool` and `AgentToolDeps`.
241
- - Update `test/tools/agent-tool.test.ts` and `test/helpers/make-deps.ts`.
242
- - Verify: `pnpm vitest run test/tools/agent-tool.test.ts`
243
- - Commit: `refactor: convert createAgentTool to AgentTool class (#195)`
244
-
245
- 4. **Update `index.ts`** — wire class constructors, remove adapter closures and `defineTool` import.
246
- - Verify: `pnpm vitest run` (full suite) + `pnpm run check` (type-check).
247
- - Commit: `refactor: wire tool classes in index.ts, remove adapter closures (#195)`
248
-
249
- 5. **Update architecture doc** — mark Layer 3 (partial) as complete.
250
- - Commit: `docs: mark #195 complete in architecture roadmap`
251
-
252
- ## Risks and Mitigations
253
-
254
- | Risk | Mitigation |
255
- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
256
- | `background-spawner` and `foreground-runner` accept narrow interfaces that differ from `AgentToolRuntime` | `AgentToolRuntime` is a superset — pass `this.runtime` and structural typing handles it. |
257
- | Test factories import `AgentToolDeps` which will be removed | Step 3 updates all test imports in the same commit. |
258
- | `defineTool` import removed from `index.ts` — must live somewhere | Each class's `toToolDefinition()` imports it internally. |
259
- | Spreading class instances in tests breaks method access | Tests will construct the class, not spread it. Step 3 uses lift-and-shift for `make-deps.ts`. |
260
-
261
- ## Open Questions
262
-
263
- - Should `AgentToolWidget` interface be removed entirely (replaced by `AgentToolRuntime`)?
264
- Defer to implementation — if `background-spawner` and `foreground-runner` can accept `AgentToolRuntime` directly, remove it; otherwise keep the narrow subset interfaces.
@@ -1,268 +0,0 @@
1
- ---
2
- issue: 196
3
- issue_title: "Convert AgentRunner and AgentsMenuHandler to classes, simplify index.ts"
4
- ---
5
-
6
- # Convert AgentRunner and AgentsMenuHandler to classes, simplify index.ts
7
-
8
- ## Problem Statement
9
-
10
- Two remaining closure factories in pi-subagents mask class-shaped code:
11
-
12
- 1. `createAgentRunner(runnerIO)` captures `RunnerIO` and returns `{ run, resume }` — the `AgentRunner` interface already exists but lacks a concrete class implementation.
13
- 2. `createAgentsMenuHandler({...})` captures an 8-field deps object and returns a handler function.
14
-
15
- With #195 (tool factory → class conversions) complete, these are the last two closure factories.
16
- After converting them, `index.ts` can be simplified: adapter closures drop, fan-out decreases, and the composition root becomes pure object construction.
17
-
18
- ## Goals
19
-
20
- - Convert `createAgentRunner` factory to a concrete `ConcreteAgentRunner` class implementing `AgentRunner`.
21
- - Convert `createAgentsMenuHandler` factory to an `AgentsMenuHandler` class.
22
- - Internalize the `getModelLabel` closure from `index.ts` into `AgentsMenuHandler` (it uses `resolveModel` and `getModelLabelFromConfig`, both pure functions the class can import directly).
23
- - Pass `AgentManager` directly as the `manager` param (it structurally satisfies `AgentMenuManager`), eliminating 3 adapter closures.
24
- - Simplify `index.ts` by removing eliminated adapter closures and unused imports.
25
- - Update architecture doc to mark Layer 3 and Layer 4 as done.
26
-
27
- ## Non-Goals
28
-
29
- - Changing `runAgent()` or `resumeAgent()` function signatures — they remain as free functions called by the class.
30
- - Removing the `AgentRunner` interface — it stays as the contract for `AgentManager`.
31
- - Removing `RunnerIO` type — it stays as the IO boundary for `runAgent()`.
32
- - Changing the `AgentMenuManager` interface — it stays as the narrow contract; `AgentManager` satisfies it structurally.
33
- - Removing `AgentMenuDeps` — it is replaced by the class constructor; the type itself is removed.
34
- - Refactoring `NotificationManager`, `SettingsManager`, or `SessionLifecycleHandler` — those are already class-shaped.
35
- - Phase 12 work (decompose `renderWidgetLines`, consolidate test duplication).
36
-
37
- ## Background
38
-
39
- ### Phase 11 layer structure
40
-
41
- Phase 11 in `docs/architecture/architecture.md` converts closure factories to classes in four layers:
42
-
43
- - Layer 0: `SessionContext` interface (#192) ✓
44
- - Layer 1: Runtime owns context queries (#193) ✓
45
- - Layer 2: Align interfaces for structural typing (#194) ✓
46
- - Layer 3: Convert closure factories to classes (#195 tools ✓, #196 runner + menu)
47
- - Layer 4: Simplify `index.ts` (#196)
48
-
49
- Issue #196 completes Layer 3 (runner + menu) and Layer 4 (index.ts simplification).
50
-
51
- ### Current state
52
-
53
- `createAgentRunner` (3 lines) wraps `runAgent`/`resumeAgent` in an object literal:
54
-
55
- ```typescript
56
- export function createAgentRunner(io: RunnerIO): AgentRunner {
57
- return {
58
- run: (snapshot, type, prompt, options) => runAgent(snapshot, type, prompt, options, io),
59
- resume: resumeAgent,
60
- };
61
- }
62
- ```
63
-
64
- `createAgentsMenuHandler` (200+ lines) captures 8 deps and returns a handler function.
65
- The deps bag includes `getModelLabel` (a closure built in `index.ts`) and `agentActivity` (a `Map` from runtime).
66
-
67
- `index.ts` currently has ~23 arrow closures and 27 imports at 229 lines.
68
-
69
- ### Structural typing confirmation
70
-
71
- `AgentManager` already has `listAgents()`, `getRecord()`, and `spawnAndWait()` methods that structurally satisfy `AgentMenuManager`.
72
- The `spawnAndWait` parameter type (`Omit<AgentSpawnConfig, "isBackground">`) is a superset of the menu's `{ description, maxTurns }`, so structural typing matches.
73
-
74
- ## Design Overview
75
-
76
- ### ConcreteAgentRunner
77
-
78
- A minimal class that implements `AgentRunner` by delegating to the free functions:
79
-
80
- ```typescript
81
- export class ConcreteAgentRunner implements AgentRunner {
82
- constructor(private readonly io: RunnerIO) {}
83
-
84
- async run(snapshot: ParentSnapshot, type: SubagentType, prompt: string, options: RunOptions): Promise<RunResult> {
85
- return runAgent(snapshot, type, prompt, options, this.io);
86
- }
87
-
88
- async resume(session: AgentSession, prompt: string, options?: ResumeOptions): Promise<string> {
89
- return resumeAgent(session, prompt, options);
90
- }
91
- }
92
- ```
93
-
94
- The factory function `createAgentRunner` is removed.
95
- The free functions `runAgent`, `resumeAgent`, `getAgentConversation`, and `normalizeMaxTurns` remain exported — they are used directly by tests and other modules.
96
-
97
- ### AgentsMenuHandler
98
-
99
- The class replaces `createAgentsMenuHandler` and `AgentMenuDeps`.
100
- Constructor params are the subset of deps that are true collaborators:
101
-
102
- ```typescript
103
- export class AgentsMenuHandler {
104
- constructor(
105
- private readonly manager: AgentMenuManager,
106
- private readonly registry: AgentTypeRegistry,
107
- private readonly agentActivity: AgentActivityReader,
108
- private readonly settings: AgentMenuSettings,
109
- private readonly fileOps: AgentFileOps,
110
- private readonly personalAgentsDir: string,
111
- private readonly projectAgentsDir: string,
112
- ) {}
113
-
114
- async handle(ctx: { ui: MenuUI; modelRegistry: ModelRegistry; parentSnapshot: ParentSnapshot }): Promise<void> { ... }
115
- }
116
- ```
117
-
118
- Key design decisions:
119
-
120
- 1. **`agentActivity` stays as a constructor param** — it is a collaborator used in `viewAgentConversation`.
121
- The issue's proposed signature omits it, but the class needs it at runtime.
122
- 2. **`getModelLabel` is internalized** — the class imports `resolveModel` and `getModelLabelFromConfig` directly and computes the label in a private method.
123
- This eliminates the closure from `index.ts` and removes the `getModelLabel` field from the deps interface.
124
- 3. **`AgentMenuDeps` is removed** — the class constructor replaces it.
125
- 4. **The `handle` method** replaces the returned function.
126
- The inner helpers (`showAgentsMenu`, `showAllAgentsList`, etc.) become private methods.
127
-
128
- ### index.ts simplification
129
-
130
- After both conversions:
131
-
132
- ```typescript
133
- // Before (adapter closures):
134
- const agentsMenuHandler = createAgentsMenuHandler({
135
- manager: {
136
- listAgents: () => manager.listAgents(),
137
- getRecord: (id) => manager.getRecord(id),
138
- spawnAndWait: (...) => manager.spawnAndWait(...),
139
- },
140
- registry,
141
- agentActivity: runtime.agentActivity,
142
- getModelLabel: (type, modelRegistry) => { ... }, // 7-line closure
143
- settings,
144
- fileOps: new FsAgentFileOps(),
145
- personalAgentsDir: join(getAgentDir(), 'agents'),
146
- projectAgentsDir: join(process.cwd(), '.pi', 'agents'),
147
- });
148
-
149
- // After:
150
- const agentsMenu = new AgentsMenuHandler(
151
- manager, registry, runtime.agentActivity,
152
- settings, new FsAgentFileOps(),
153
- join(getAgentDir(), 'agents'),
154
- join(process.cwd(), '.pi', 'agents'),
155
- );
156
- ```
157
-
158
- Eliminated closures: 4 (3 manager method adapters + 1 getModelLabel closure).
159
- Eliminated imports: `getModelLabelFromConfig`, `resolveModel` (from index.ts), `createAgentRunner`, `type RunnerIO`, `createAgentsMenuHandler`.
160
-
161
- Remaining adapter closures in `index.ts` (~15) are necessary: event handler registrations, SDK factory callbacks, `pi.sendMessage`/`pi.exec` adapters.
162
- These are structural — they bridge the Pi SDK's callback-based API to the extension's object-oriented internals.
163
-
164
- ## Module-Level Changes
165
-
166
- ### `src/lifecycle/agent-runner.ts`
167
-
168
- - Add `ConcreteAgentRunner` class implementing `AgentRunner`.
169
- - Remove `createAgentRunner` factory function.
170
- - Keep all free functions (`runAgent`, `resumeAgent`, `getAgentConversation`, `normalizeMaxTurns`) and all types exported.
171
-
172
- ### `src/ui/agent-menu.ts`
173
-
174
- - Replace `createAgentsMenuHandler` factory with `AgentsMenuHandler` class.
175
- - Remove `AgentMenuDeps` interface.
176
- - Add private `getModelLabel` method (internalizes the closure from `index.ts`).
177
- - Convert inner functions (`showAgentsMenu`, `showAllAgentsList`, `showRunningAgents`, `viewAgentConversation`, `showSettings`) to private methods.
178
- - Add imports: `resolveModel` from `#src/session/model-resolver`, `getModelLabelFromConfig` from `#src/tools/helpers`.
179
- - Keep exported interfaces: `AgentMenuManager`, `AgentMenuSettings`, `AgentActivityReader`, `MenuUI`.
180
-
181
- ### `src/index.ts`
182
-
183
- - Replace `createAgentRunner(runnerIO)` with `new ConcreteAgentRunner(runnerIO)`.
184
- - Replace `createAgentsMenuHandler({...})` with `new AgentsMenuHandler(...)`.
185
- - Replace `agentsMenuHandler({...})` with `agentsMenu.handle({...})`.
186
- - Remove adapter closures for `manager.listAgents`, `manager.getRecord`, `manager.spawnAndWait`, and `getModelLabel`.
187
- - Remove unused imports: `createAgentRunner`, `type RunnerIO` → `ConcreteAgentRunner`; `createAgentsMenuHandler` → `AgentsMenuHandler`; `getModelLabelFromConfig`, `resolveModel`.
188
- - Net effect: ~15 lines removed, 5 imports removed.
189
-
190
- ### `docs/architecture/architecture.md`
191
-
192
- - Mark Layer 3 remaining items (runner, menu) as done.
193
- - Mark Layer 4 as done.
194
- - Update the factory→class table entries for `createAgentRunner` and `createAgentsMenuHandler` with ✓.
195
-
196
- ## Test Impact Analysis
197
-
198
- ### `test/lifecycle/agent-runner.test.ts` (and siblings)
199
-
200
- No changes needed.
201
- Tests call `runAgent()` and `resumeAgent()` directly — they never use `createAgentRunner`.
202
- The `ConcreteAgentRunner` class is a trivial two-method delegation wrapper tested implicitly through `index.ts` integration and explicitly through one new unit test.
203
-
204
- ### `test/ui/agent-menu.test.ts`
205
-
206
- Tests need updating:
207
-
208
- 1. Replace `createAgentsMenuHandler(makeDeps())` with `new AgentsMenuHandler(...)`.
209
- 2. Replace `handler(params)` with `handler.handle(params)`.
210
- 3. Remove `getModelLabel` from `makeDeps()` — it is now an internal method.
211
- 4. Remove `AgentMenuDeps` import; update `makeDeps` to construct positional args or a helper that returns a handler directly.
212
-
213
- No test logic changes — only call-site updates for the new API shape.
214
-
215
- ### New tests
216
-
217
- One new unit test for `ConcreteAgentRunner`: verify it delegates `run` and `resume` to the underlying functions.
218
-
219
- ## TDD Order
220
-
221
- 1. **Add `ConcreteAgentRunner` class alongside factory.**
222
- Add the class to `agent-runner.ts`, keep `createAgentRunner` temporarily.
223
- Add a unit test verifying delegation.
224
- `test: add ConcreteAgentRunner delegation test`
225
-
226
- 2. **Switch `index.ts` to `ConcreteAgentRunner`, remove factory.**
227
- Replace `createAgentRunner(runnerIO)` with `new ConcreteAgentRunner(runnerIO)`.
228
- Remove the `createAgentRunner` factory function.
229
- Update imports.
230
- `refactor: replace createAgentRunner with ConcreteAgentRunner class`
231
-
232
- 3. **Convert `createAgentsMenuHandler` to `AgentsMenuHandler` class.**
233
- Replace factory function with class.
234
- Move inner functions to private methods.
235
- Internalize `getModelLabel` as a private method.
236
- Remove `AgentMenuDeps` interface.
237
- `refactor: convert createAgentsMenuHandler to AgentsMenuHandler class`
238
-
239
- 4. **Update `agent-menu.test.ts` for class API.**
240
- Replace `createAgentsMenuHandler(makeDeps())` with class construction.
241
- Replace `handler(params)` with `handler.handle(params)`.
242
- Remove `getModelLabel` from test deps.
243
- All existing tests pass with updated call sites.
244
- `test: update agent-menu tests for AgentsMenuHandler class`
245
-
246
- 5. **Simplify `index.ts` wiring.**
247
- Replace `createAgentsMenuHandler({...})` with `new AgentsMenuHandler(...)`.
248
- Pass `manager` directly (structural typing).
249
- Remove adapter closures and unused imports.
250
- `refactor: simplify index.ts wiring for AgentsMenuHandler`
251
-
252
- 6. **Update architecture doc.**
253
- Mark Layer 3 remaining items and Layer 4 as done in `docs/architecture/architecture.md`.
254
- `docs: mark Phase 11 Layer 3 and Layer 4 complete`
255
-
256
- ## Risks and Mitigations
257
-
258
- | Risk | Mitigation |
259
- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
260
- | `AgentManager` might not structurally satisfy `AgentMenuManager` | Confirmed: `listAgents()`, `getRecord()`, `spawnAndWait()` signatures are compatible. `pnpm run check` in step 5 verifies. |
261
- | Internalized `getModelLabel` might diverge from the closure | The private method uses the same `resolveModel` and `getModelLabelFromConfig` imports — identical logic, just moved. |
262
- | Tests that use `AgentMenuDeps` type break when removed | Step 4 updates all test call sites before step 5 changes production code. The test file is 215 lines — manageable in one step. |
263
- | `agentActivity` missing from constructor | Included in the class constructor (diverging from the issue's proposed signature which omits it). |
264
-
265
- ## Open Questions
266
-
267
- None — the issue's proposed design is clear and the implementation is mechanical.
268
- The one deviation (keeping `agentActivity` as a constructor param) is necessary and minimal.
@@ -1,140 +0,0 @@
1
- ---
2
- issue: 205
3
- issue_title: "Decompose renderWidgetLines (cognitive 44)"
4
- ---
5
-
6
- # Decompose `renderWidgetLines`
7
-
8
- ## Problem Statement
9
-
10
- `renderWidgetLines` in `ui/widget-renderer.ts` has cognitive complexity 44 (CRITICAL per fallow health).
11
- It handles agent categorization, per-status line building with tree connectors, non-overflow assembly with last-connector fixup, and overflow-budget assembly — all in a single 106-line function.
12
- This is the highest-complexity function remaining in the codebase (Phase 12, Step 1).
13
-
14
- ## Goals
15
-
16
- - Extract distinct concerns into separate pure functions, each with cognitive complexity < 10.
17
- - Preserve all existing behavior — no visual or behavioral changes.
18
- - Keep all extracted functions in `widget-renderer.ts` (they are private helpers, not a separate module).
19
-
20
- ## Non-Goals
21
-
22
- - Decomposing `showAgentDetail` (#206), `update` (#207), or shared test fixtures (#208) — those are sibling Phase 12 steps.
23
- - Changing the widget's visual output or tree-connector style.
24
- - Modifying `renderFinishedLine` or `renderRunningLines` — those are already single-concern functions.
25
-
26
- ## Background
27
-
28
- `widget-renderer.ts` was extracted from `AgentWidget` in #148.
29
- The per-agent renderers (`renderFinishedLine`, `renderRunningLines`) are already clean single-concern functions.
30
- The remaining complexity lives entirely in `renderWidgetLines`, which orchestrates categorization, section building, and assembly.
31
-
32
- The function has five interwoven concerns:
33
-
34
- 1. **Agent categorization** — filtering into running/queued/finished buckets.
35
- 2. **Section building** — rendering each bucket into pre-formatted line arrays with `├─` tree connectors.
36
- 3. **Heading construction** — choosing icon/color based on active vs. finished-only.
37
- 4. **Non-overflow assembly** — concatenating sections when under `MAX_WIDGET_LINES`, then fixing the last connector (`├─` → `└─`).
38
- 5. **Overflow assembly** — budget-based prioritized assembly (running > queued > finished) with an overflow indicator line.
39
-
40
- ## Design Overview
41
-
42
- Extract four helper functions from the body of `renderWidgetLines`:
43
-
44
- ### `categorizeAgents`
45
-
46
- Accepts the agents array and `shouldShowFinished` callback.
47
- Returns `{ running, queued, finished }` arrays.
48
- Pure filter — no rendering.
49
-
50
- ### `buildSections`
51
-
52
- Accepts categorized agents, `activityMap`, `registry`, `spinnerFrame`, `theme`, and a `truncate` function.
53
- Returns `{ finishedLines, runningLines, queuedLine }` — the pre-formatted line arrays with `├─` connectors.
54
- Calls `renderFinishedLine` and `renderRunningLines` internally.
55
-
56
- ### `assembleWithinBudget`
57
-
58
- Accepts `finishedLines`, `runningLines`, `queuedLine`, and the heading line.
59
- Handles the non-overflow path: concatenates sections and fixes the last tree connector (`├─` → `└─`, `│` → space).
60
- Returns the assembled `string[]`.
61
-
62
- ### `assembleOverflow`
63
-
64
- Accepts `finishedLines`, `runningLines`, `queuedLine`, heading line, `maxBody` budget, `truncate`, and `theme`.
65
- Handles the overflow path: budget-based prioritized assembly with an overflow indicator.
66
- Returns the assembled `string[]`.
67
-
68
- After extraction, `renderWidgetLines` becomes a thin orchestrator:
69
-
70
- ```typescript
71
- export function renderWidgetLines(params: { ... }): string[] {
72
- const { running, queued, finished } = categorizeAgents(agents, shouldShowFinished);
73
- if (running.length === 0 && queued.length === 0 && finished.length === 0) return [];
74
-
75
- const truncate = (line: string) => truncateToWidth(line, terminalWidth);
76
- const heading = buildHeadingLine(running, queued, truncate, theme);
77
- const sections = buildSections(running, queued, finished, activityMap, registry, spinnerFrame, theme, truncate);
78
- const totalBody = sections.finishedLines.length + sections.runningLines.length * 2 + (sections.queuedLine ? 1 : 0);
79
-
80
- if (totalBody <= MAX_WIDGET_LINES - 1) {
81
- return assembleWithinBudget(heading, sections);
82
- }
83
- return assembleOverflow(heading, sections, MAX_WIDGET_LINES - 1, truncate, theme);
84
- }
85
- ```
86
-
87
- Each helper is a pure function with a single concern and low branching.
88
-
89
- ## Module-Level Changes
90
-
91
- ### Changed: `src/ui/widget-renderer.ts`
92
-
93
- - Add `categorizeAgents` (private) — extracts the three `agents.filter(...)` calls.
94
- - Add `buildSections` (private) — extracts the three section-building loops.
95
- - Add `assembleWithinBudget` (private) — extracts the non-overflow assembly + connector fixup.
96
- - Add `assembleOverflow` (private) — extracts the overflow-budget assembly + indicator line.
97
- - Simplify `renderWidgetLines` to a thin orchestrator calling the four helpers.
98
-
99
- No exports are added, removed, or renamed.
100
- No other files change.
101
-
102
- ## Test Impact Analysis
103
-
104
- 1. No new unit tests for the extracted helpers are needed — they are private functions tested through `renderWidgetLines`.
105
- The existing `renderWidgetLines` tests in `test/widget-renderer.test.ts` (8 tests) cover all branches: single running, mixed agents, filtered finished, overflow priority, empty arrays, dim heading.
106
- 2. No existing tests become redundant — they all exercise `renderWidgetLines` end-to-end, which is the correct level for assembly logic.
107
- 3. All existing tests must stay as-is — the extraction is purely internal.
108
-
109
- ## TDD Order
110
-
111
- 1. **Red → Green:** Extract `categorizeAgents` and call it from `renderWidgetLines`.
112
- All existing tests pass (no behavior change).
113
- Commit: `refactor: extract categorizeAgents from renderWidgetLines`
114
-
115
- 2. **Red → Green:** Extract `buildSections` and call it from `renderWidgetLines`.
116
- All existing tests pass.
117
- Commit: `refactor: extract buildSections from renderWidgetLines`
118
-
119
- 3. **Red → Green:** Extract `assembleWithinBudget` and call it from `renderWidgetLines`.
120
- All existing tests pass.
121
- Commit: `refactor: extract assembleWithinBudget from renderWidgetLines`
122
-
123
- 4. **Red → Green:** Extract `assembleOverflow` and call it from `renderWidgetLines`.
124
- All existing tests pass.
125
- Commit: `refactor: extract assembleOverflow from renderWidgetLines`
126
-
127
- 5. **Verify:** Run `pnpm run check` and `pnpm vitest run test/widget-renderer.test.ts` to confirm no regressions.
128
- Commit: n/a (verification only).
129
-
130
- ## Risks and Mitigations
131
-
132
- | Risk | Mitigation |
133
- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
134
- | Tree-connector fixup logic is fragile (string replacement on Unicode chars) | Keep the fixup in `assembleWithinBudget` as-is — same logic, just relocated. Existing tests verify exact connector output. |
135
- | Extracted helpers have many parameters | Accept a `sections` object from `buildSections` to bundle `finishedLines`, `runningLines`, `queuedLine` — avoids long parameter lists. |
136
- | Intermediate commits break tests | Each extraction step is self-contained — the function body moves into a helper and the call site replaces it in the same commit. |
137
-
138
- ## Open Questions
139
-
140
- None — the decomposition is purely mechanical extraction of existing code into named helpers.