@gotgenes/pi-subagents 18.0.1 → 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 (260) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/docs/architecture/architecture.md +157 -204
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +5 -6
  5. package/src/lifecycle/subagent.ts +5 -9
  6. package/src/observation/notification.ts +23 -10
  7. package/src/observation/subagent-events-observer.ts +1 -3
  8. package/src/tools/get-result-report.ts +60 -0
  9. package/src/tools/get-result-tool.ts +25 -40
  10. package/src/types.ts +1 -1
  11. package/src/ui/session-navigator.ts +0 -2
  12. package/.prettierignore +0 -5
  13. package/AGENTS.md +0 -7
  14. package/docs/plans/0048-implement-subagents-api.md +0 -304
  15. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  16. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  17. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  18. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  19. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  20. package/docs/plans/0057-structured-debug-logging.md +0 -154
  21. package/docs/plans/0061-session-format-transcript.md +0 -284
  22. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  23. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  24. package/docs/plans/0070-extract-event-handlers.md +0 -306
  25. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  26. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  27. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  28. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  29. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  30. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  31. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  32. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  33. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  34. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  35. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  36. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  37. package/docs/plans/0109-extract-settings-manager.md +0 -276
  38. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  39. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  40. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  41. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  42. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  43. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  44. package/docs/plans/0116-type-housekeeping.md +0 -351
  45. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  46. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  47. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  48. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  49. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  50. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  51. package/docs/plans/0135-extract-display-helpers.md +0 -182
  52. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  53. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  54. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  55. package/docs/plans/0146-narrow-ui-context.md +0 -319
  56. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  57. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  58. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  59. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  60. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  61. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  62. package/docs/plans/0167-narrow-runner-io.md +0 -150
  63. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  64. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  65. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  66. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  67. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  68. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  69. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  70. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  71. package/docs/plans/0192-define-session-context-interface.md +0 -107
  72. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  73. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  74. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  75. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  76. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  77. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  78. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  79. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  80. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  81. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  82. package/docs/plans/0216-decompose-start-agent.md +0 -255
  83. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  84. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  85. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  86. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  87. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  88. package/docs/plans/0229-agent-born-complete.md +0 -564
  89. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  90. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  91. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  92. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  93. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  94. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  95. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  96. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  97. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  98. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  99. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  100. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  101. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  102. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  103. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  104. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  105. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  106. package/docs/plans/0373-extract-subagent-state.md +0 -250
  107. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  108. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  109. package/docs/plans/0376-extract-manager-observer.md +0 -232
  110. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  111. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  112. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  113. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  114. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  115. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  116. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  117. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  118. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  119. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  120. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  121. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  122. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  123. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  124. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  125. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  126. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  127. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  128. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  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/plans/0470-refresh-readme-command-surface.md +0 -159
  137. package/docs/retro/0048-implement-subagents-api.md +0 -44
  138. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  139. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  140. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  141. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  142. package/docs/retro/0057-structured-debug-logging.md +0 -77
  143. package/docs/retro/0061-session-format-transcript.md +0 -41
  144. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  145. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  146. package/docs/retro/0070-extract-event-handlers.md +0 -49
  147. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  148. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  149. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  150. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  151. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  152. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  153. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  154. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  155. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  156. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  157. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  158. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  159. package/docs/retro/0109-extract-settings-manager.md +0 -55
  160. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  161. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  162. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  163. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  164. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  165. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  166. package/docs/retro/0116-type-housekeeping.md +0 -42
  167. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  168. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  169. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  170. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  171. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  172. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  173. package/docs/retro/0135-extract-display-helpers.md +0 -38
  174. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  175. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  176. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  177. package/docs/retro/0146-narrow-ui-context.md +0 -70
  178. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  179. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  180. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  181. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  182. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  183. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  184. package/docs/retro/0167-narrow-runner-io.md +0 -63
  185. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  186. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  187. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  188. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  189. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  190. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  191. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  192. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  193. package/docs/retro/0192-define-session-context-interface.md +0 -59
  194. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  195. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  196. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  197. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  198. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  199. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  200. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  201. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  202. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  203. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  204. package/docs/retro/0216-decompose-start-agent.md +0 -80
  205. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  206. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  207. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  208. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  209. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  210. package/docs/retro/0229-agent-born-complete.md +0 -89
  211. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  212. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  213. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  214. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  215. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  216. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  217. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  218. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  219. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  220. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  221. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  222. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  223. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  224. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  225. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  226. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  227. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  228. package/docs/retro/0373-extract-subagent-state.md +0 -94
  229. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  230. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  231. package/docs/retro/0376-extract-manager-observer.md +0 -81
  232. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  233. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  234. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  235. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  236. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  237. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  238. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  239. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  240. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  241. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  242. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  243. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  244. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  245. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  246. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  247. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  248. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  249. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  250. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  251. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  252. package/docs/retro/0445-native-session-navigation.md +0 -122
  253. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  254. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  255. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  256. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  257. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  258. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  259. package/src/observation/notification-state.ts +0 -27
  260. 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.