@gotgenes/pi-subagents 18.0.1 → 18.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/docs/architecture/architecture.md +165 -203
  3. package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
  4. package/package.json +6 -7
  5. package/src/config/default-agents.ts +3 -0
  6. package/src/lifecycle/subagent.ts +25 -14
  7. package/src/observation/notification.ts +23 -10
  8. package/src/observation/subagent-events-observer.ts +1 -3
  9. package/src/runtime.ts +2 -1
  10. package/src/service/service-adapter.ts +20 -17
  11. package/src/session/model-resolver.ts +31 -30
  12. package/src/tools/agent-tool.ts +17 -13
  13. package/src/tools/get-result-report.ts +60 -0
  14. package/src/tools/get-result-tool.ts +25 -40
  15. package/src/tools/helpers.ts +21 -5
  16. package/src/tools/spawn-config.ts +5 -5
  17. package/src/tools/steer-tool.ts +37 -27
  18. package/src/types.ts +4 -1
  19. package/src/ui/session-navigator.ts +0 -2
  20. package/.prettierignore +0 -5
  21. package/AGENTS.md +0 -7
  22. package/docs/plans/0048-implement-subagents-api.md +0 -304
  23. package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
  24. package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
  25. package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
  26. package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
  27. package/docs/plans/0054-decompose-index-into-modules.md +0 -302
  28. package/docs/plans/0057-structured-debug-logging.md +0 -154
  29. package/docs/plans/0061-session-format-transcript.md +0 -284
  30. package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
  31. package/docs/plans/0069-create-subagent-runtime.md +0 -345
  32. package/docs/plans/0070-extract-event-handlers.md +0 -306
  33. package/docs/plans/0071-extract-session-config-assembler.md +0 -362
  34. package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
  35. package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
  36. package/docs/plans/0077-inject-project-agents-dir.md +0 -115
  37. package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
  38. package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
  39. package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
  40. package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
  41. package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
  42. package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
  43. package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
  44. package/docs/plans/0108-extract-agent-type-registry.md +0 -322
  45. package/docs/plans/0109-extract-settings-manager.md +0 -276
  46. package/docs/plans/0110-agent-activity-tracker.md +0 -297
  47. package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
  48. package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
  49. package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
  50. package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
  51. package/docs/plans/0115-decompose-agent-tool.md +0 -337
  52. package/docs/plans/0116-type-housekeeping.md +0 -351
  53. package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
  54. package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
  55. package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
  56. package/docs/plans/0132-inject-io-into-session-config.md +0 -219
  57. package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
  58. package/docs/plans/0134-reduce-as-any-casts.md +0 -366
  59. package/docs/plans/0135-extract-display-helpers.md +0 -182
  60. package/docs/plans/0136-decompose-agent-menu.md +0 -300
  61. package/docs/plans/0144-consolidate-observation-model.md +0 -263
  62. package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
  63. package/docs/plans/0146-narrow-ui-context.md +0 -319
  64. package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
  65. package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
  66. package/docs/plans/0152-add-prompt-snippet.md +0 -91
  67. package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
  68. package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
  69. package/docs/plans/0166-extract-parent-session-info.md +0 -231
  70. package/docs/plans/0167-narrow-runner-io.md +0 -150
  71. package/docs/plans/0168-extract-tool-filter-config.md +0 -173
  72. package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
  73. package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
  74. package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
  75. package/docs/plans/0172-extract-turn-formatting.md +0 -206
  76. package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
  77. package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
  78. package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
  79. package/docs/plans/0192-define-session-context-interface.md +0 -107
  80. package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
  81. package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
  82. package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
  83. package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
  84. package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
  85. package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
  86. package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
  87. package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
  88. package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
  89. package/docs/plans/0215-decompose-build-parent-context.md +0 -166
  90. package/docs/plans/0216-decompose-start-agent.md +0 -255
  91. package/docs/plans/0217-extract-overwrite-guard.md +0 -176
  92. package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
  93. package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
  94. package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
  95. package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
  96. package/docs/plans/0229-agent-born-complete.md +0 -564
  97. package/docs/plans/0230-extract-concurrency-queue.md +0 -265
  98. package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
  99. package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
  100. package/docs/plans/0237-remove-disallowed-tools.md +0 -180
  101. package/docs/plans/0238-remove-extensions-filtering.md +0 -191
  102. package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
  103. package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
  104. package/docs/plans/0256-extract-worktree-isolation.md +0 -256
  105. package/docs/plans/0257-extract-child-session-factory.md +0 -285
  106. package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
  107. package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
  108. package/docs/plans/0265-born-complete-subagent-session.md +0 -333
  109. package/docs/plans/0270-type-consumable-public-surface.md +0 -204
  110. package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
  111. package/docs/plans/0277-encapsulate-agent-session.md +0 -304
  112. package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
  113. package/docs/plans/0360-custom-agents-default-append.md +0 -132
  114. package/docs/plans/0373-extract-subagent-state.md +0 -250
  115. package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
  116. package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
  117. package/docs/plans/0376-extract-manager-observer.md +0 -232
  118. package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
  119. package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
  120. package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
  121. package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
  122. package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
  123. package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
  124. package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
  125. package/docs/plans/0412-unify-session-mock-builders.md +0 -146
  126. package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
  127. package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
  128. package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
  129. package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
  130. package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
  131. package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
  132. package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
  133. package/docs/plans/0427-reconsider-ui-direction.md +0 -212
  134. package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
  135. package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
  136. package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
  137. package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
  138. package/docs/plans/0445-native-session-navigation.md +0 -365
  139. package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
  140. package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
  141. package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
  142. package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
  143. package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
  144. package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
  145. package/docs/retro/0048-implement-subagents-api.md +0 -44
  146. package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
  147. package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
  148. package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
  149. package/docs/retro/0054-decompose-index-into-modules.md +0 -53
  150. package/docs/retro/0057-structured-debug-logging.md +0 -77
  151. package/docs/retro/0061-session-format-transcript.md +0 -41
  152. package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
  153. package/docs/retro/0069-create-subagent-runtime.md +0 -43
  154. package/docs/retro/0070-extract-event-handlers.md +0 -49
  155. package/docs/retro/0071-extract-session-config-assembler.md +0 -60
  156. package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
  157. package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
  158. package/docs/retro/0077-inject-project-agents-dir.md +0 -33
  159. package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
  160. package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
  161. package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
  162. package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
  163. package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
  164. package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
  165. package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
  166. package/docs/retro/0108-extract-agent-type-registry.md +0 -41
  167. package/docs/retro/0109-extract-settings-manager.md +0 -55
  168. package/docs/retro/0110-agent-activity-tracker.md +0 -44
  169. package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
  170. package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
  171. package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
  172. package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
  173. package/docs/retro/0115-decompose-agent-tool.md +0 -51
  174. package/docs/retro/0116-type-housekeeping.md +0 -42
  175. package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
  176. package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
  177. package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
  178. package/docs/retro/0132-inject-io-into-session-config.md +0 -33
  179. package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
  180. package/docs/retro/0134-reduce-as-any-casts.md +0 -56
  181. package/docs/retro/0135-extract-display-helpers.md +0 -38
  182. package/docs/retro/0136-decompose-agent-menu.md +0 -43
  183. package/docs/retro/0144-consolidate-observation-model.md +0 -39
  184. package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
  185. package/docs/retro/0146-narrow-ui-context.md +0 -70
  186. package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
  187. package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
  188. package/docs/retro/0152-add-prompt-snippet.md +0 -34
  189. package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
  190. package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
  191. package/docs/retro/0166-extract-parent-session-info.md +0 -66
  192. package/docs/retro/0167-narrow-runner-io.md +0 -63
  193. package/docs/retro/0168-extract-tool-filter-config.md +0 -76
  194. package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
  195. package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
  196. package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
  197. package/docs/retro/0172-extract-turn-formatting.md +0 -80
  198. package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
  199. package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
  200. package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
  201. package/docs/retro/0192-define-session-context-interface.md +0 -59
  202. package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
  203. package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
  204. package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
  205. package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
  206. package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
  207. package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
  208. package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
  209. package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
  210. package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
  211. package/docs/retro/0215-decompose-build-parent-context.md +0 -61
  212. package/docs/retro/0216-decompose-start-agent.md +0 -80
  213. package/docs/retro/0217-extract-overwrite-guard.md +0 -63
  214. package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
  215. package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
  216. package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
  217. package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
  218. package/docs/retro/0229-agent-born-complete.md +0 -89
  219. package/docs/retro/0230-extract-concurrency-queue.md +0 -38
  220. package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
  221. package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
  222. package/docs/retro/0237-remove-disallowed-tools.md +0 -78
  223. package/docs/retro/0238-remove-extensions-filtering.md +0 -72
  224. package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
  225. package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
  226. package/docs/retro/0256-extract-worktree-isolation.md +0 -89
  227. package/docs/retro/0257-extract-child-session-factory.md +0 -33
  228. package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
  229. package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
  230. package/docs/retro/0265-born-complete-subagent-session.md +0 -97
  231. package/docs/retro/0270-type-consumable-public-surface.md +0 -108
  232. package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
  233. package/docs/retro/0277-encapsulate-agent-session.md +0 -80
  234. package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
  235. package/docs/retro/0360-custom-agents-default-append.md +0 -81
  236. package/docs/retro/0373-extract-subagent-state.md +0 -94
  237. package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
  238. package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
  239. package/docs/retro/0376-extract-manager-observer.md +0 -81
  240. package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
  241. package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
  242. package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
  243. package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
  244. package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
  245. package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
  246. package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
  247. package/docs/retro/0412-unify-session-mock-builders.md +0 -80
  248. package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
  249. package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
  250. package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
  251. package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
  252. package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
  253. package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
  254. package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
  255. package/docs/retro/0427-reconsider-ui-direction.md +0 -102
  256. package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
  257. package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
  258. package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
  259. package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
  260. package/docs/retro/0445-native-session-navigation.md +0 -122
  261. package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
  262. package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
  263. package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
  264. package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
  265. package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
  266. package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
  267. package/src/observation/notification-state.ts +0 -27
  268. package/vitest.config.ts +0 -14
@@ -1,211 +0,0 @@
1
- ---
2
- issue: 206
3
- issue_title: "Decompose showAgentDetail (cognitive 33)"
4
- ---
5
-
6
- # Decompose `showAgentDetail`
7
-
8
- ## Problem Statement
9
-
10
- `showAgentDetail` in `ui/agent-config-editor.ts` has cognitive complexity 33 (CRITICAL per fallow health).
11
- It interleaves menu-option computation, user-choice dispatch, and three inline action handlers (edit, delete, reset) in a single 67-line function.
12
- `ejectAgent` in the same file has cognitive complexity 20 from its 14-branch frontmatter builder.
13
- Phase 12, Step 2 targets cognitive complexity < 10 per function.
14
-
15
- ## Goals
16
-
17
- - Extract menu-option computation and inline action handlers into separate functions, each with cognitive complexity < 10.
18
- - Extract frontmatter building from `ejectAgent` into a pure function.
19
- - Preserve all existing behavior — no user-visible or API changes.
20
- - Add unit tests for the two new exported pure functions.
21
-
22
- ## Non-Goals
23
-
24
- - Decomposing `renderWidgetLines` (#205), `update` (#207), or shared test fixtures (#208) — sibling Phase 12 steps.
25
- - Changing the menu structure, option labels, or action semantics.
26
- - Decomposing `disableAgent` or `enableAgent` — their cognitive complexity is already manageable (< 15).
27
-
28
- ## Background
29
-
30
- `agent-config-editor.ts` was extracted from `agent-menu.ts` in Phase 8 (#136).
31
- The file exposes a single factory `createAgentConfigEditor` that returns `{ showAgentDetail }`.
32
- Internally the factory closes over `fileOps`, `registry`, `personalAgentsDir`, and `projectAgentsDir`.
33
-
34
- Three action handlers already exist as closure-level functions: `ejectAgent`, `disableAgent`, `enableAgent`.
35
- The remaining three actions — Edit, Delete, and Reset to default — are inlined in `showAgentDetail`'s if/else dispatch chain.
36
-
37
- The existing test suite (`test/ui/agent-config-editor.test.ts`, 18 tests) covers all menu-option combinations and action branches through the public `showAgentDetail` entry point.
38
-
39
- ### Complexity sources
40
-
41
- `showAgentDetail` (cognitive 33):
42
-
43
- 1. Menu-option building — 4-branch if/else chain with 3 boolean conditions (`disabled`, `isDefault`, `file`).
44
- 2. Action dispatch — 6-branch if/else chain based on `choice`.
45
- 3. Inline Edit handler — 3 nested `if` guards (`file`, `content`, `edited !== content`).
46
- 4. Inline Delete handler — nested `if (file)` + `if (confirmed)`.
47
- 5. Inline Reset handler — nested `if (file)` + `if (confirmed)`.
48
-
49
- `ejectAgent` (cognitive 20):
50
-
51
- 1. Location selection + overwrite check — 2 early returns with nested ifs.
52
- 2. Frontmatter field building — 14 conditional `if`/`else if` branches.
53
-
54
- ## Design Overview
55
-
56
- ### Extracted from `showAgentDetail`
57
-
58
- #### `buildMenuOptions` (exported, pure)
59
-
60
- ```typescript
61
- export function buildMenuOptions(
62
- cfg: { isDefault?: boolean; enabled?: boolean },
63
- file: string | undefined,
64
- ): string[]
65
- ```
66
-
67
- Accepts the minimal config shape and file path.
68
- Returns the menu option array.
69
- Pure computation — no IO, no side effects.
70
- Exported for direct unit testing.
71
-
72
- #### `handleEdit` (closure-internal)
73
-
74
- Handles the Edit action: reads the file, opens the editor, writes if changed.
75
- Signature: `(ui: MenuUI, name: string, file: string) => Promise<void>`.
76
- Called only when `file` is defined (guaranteed by menu-option construction).
77
-
78
- #### `handleDelete` (closure-internal)
79
-
80
- Handles the Delete action: confirms with user, removes file, reloads registry.
81
- Signature: `(ui: MenuUI, name: string, file: string) => Promise<void>`.
82
-
83
- #### `handleReset` (closure-internal)
84
-
85
- Handles the Reset to default action: confirms, removes override file, reloads registry.
86
- Signature: `(ui: MenuUI, name: string, file: string) => Promise<void>`.
87
-
88
- ### Extracted from `ejectAgent`
89
-
90
- #### `buildEjectContent` (exported, pure)
91
-
92
- ```typescript
93
- export function buildEjectContent(cfg: AgentConfig): string
94
- ```
95
-
96
- Builds the full `.md` file content (frontmatter + system prompt) for an ejected agent.
97
- Pure function — no IO.
98
- Exported for direct unit testing.
99
-
100
- ### After refactoring
101
-
102
- `showAgentDetail` becomes a thin orchestrator (~15 lines):
103
-
104
- ```typescript
105
- async function showAgentDetail(ui: MenuUI, name: string) {
106
- if (registry.resolveType(name) == null) {
107
- ui.notify(`Agent config not found for "${name}".`, "warning");
108
- return;
109
- }
110
- const cfg = registry.resolveAgentConfig(name);
111
- const file = fileOps.findAgentFile(name, agentDirs());
112
-
113
- const choice = await ui.select(name, buildMenuOptions(cfg, file));
114
- if (!choice || choice === "Back") return;
115
-
116
- if (choice === "Edit" && file) await handleEdit(ui, name, file);
117
- else if (choice === "Delete" && file) await handleDelete(ui, name, file);
118
- else if (choice === "Reset to default" && file) await handleReset(ui, name, file);
119
- else if (choice.startsWith("Eject")) await ejectAgent(ui, name, cfg);
120
- else if (choice === "Disable") await disableAgent(ui, name);
121
- else if (choice === "Enable") await enableAgent(ui, name);
122
- }
123
- ```
124
-
125
- Cognitive complexity: ~5 (one null-check early return + flat dispatch chain with no nesting).
126
-
127
- `ejectAgent` becomes:
128
-
129
- ```typescript
130
- async function ejectAgent(ui: MenuUI, name: string, cfg: AgentConfig) {
131
- const location = await ui.select("Choose location", [...]);
132
- if (!location) return;
133
- const targetDir = location.startsWith("Project") ? projectAgentsDir : personalAgentsDir;
134
- const targetPath = join(targetDir, `${name}.md`);
135
- if (fileOps.exists(targetPath)) {
136
- const overwrite = await ui.confirm("Overwrite", `${targetPath} already exists. Overwrite?`);
137
- if (!overwrite) return;
138
- }
139
- fileOps.write(targetPath, buildEjectContent(cfg));
140
- registry.reload();
141
- ui.notify(`Ejected ${name} to ${targetPath}`, "info");
142
- }
143
- ```
144
-
145
- Cognitive complexity: ~6 (two early returns, one ternary, one nested if).
146
-
147
- ## Module-Level Changes
148
-
149
- ### Changed: `src/ui/agent-config-editor.ts`
150
-
151
- - Add exported `buildMenuOptions(cfg, file)` — pure function extracted from `showAgentDetail`.
152
- - Add exported `buildEjectContent(cfg)` — pure function extracted from `ejectAgent`.
153
- - Add closure-internal `handleEdit(ui, name, file)` — extracted from `showAgentDetail` inline logic.
154
- - Add closure-internal `handleDelete(ui, name, file)` — extracted from `showAgentDetail` inline logic.
155
- - Add closure-internal `handleReset(ui, name, file)` — extracted from `showAgentDetail` inline logic.
156
- - Simplify `showAgentDetail` to orchestrate: resolve → build menu → select → dispatch.
157
- - Simplify `ejectAgent` to delegate frontmatter building to `buildEjectContent`.
158
-
159
- No exports are removed or renamed.
160
- The public API (`createAgentConfigEditor` returning `{ showAgentDetail }`) is unchanged.
161
-
162
- ### Changed: `test/ui/agent-config-editor.test.ts`
163
-
164
- - Add `describe("buildMenuOptions")` with tests for each menu-option combination (5 cases from existing tests, restructured as direct function calls).
165
- - Add `describe("buildEjectContent")` with tests for minimal config and config with all optional fields.
166
- - Existing `showAgentDetail` tests remain unchanged as integration coverage.
167
-
168
- ### Changed: `docs/architecture/architecture.md`
169
-
170
- - Update the complexity hotspots table: `showAgentDetail` drops from 25/33 to ~5/5; `ejectAgent` drops from 21/20 to ~6/6.
171
-
172
- ## Test Impact Analysis
173
-
174
- 1. **New tests enabled:** Direct unit tests for `buildMenuOptions` (pure function with 5 state combinations) and `buildEjectContent` (pure function with many optional fields).
175
- These were previously impossible to test in isolation because the logic was embedded in async UI flows.
176
- 2. **Existing tests that stay:** All 18 `showAgentDetail` tests remain as integration coverage — they exercise the full resolve → menu → dispatch → action pipeline.
177
- 3. **No tests become redundant:** The existing menu-option-structure tests (5 tests) overlap with `buildMenuOptions` unit tests, but they remain valuable as integration tests verifying the full flow produces the correct menu.
178
-
179
- ## TDD Order
180
-
181
- 1. **Red → Green:** Add `buildMenuOptions` unit tests (5 cases: default no-file, default with-file, custom with-file, disabled-default with-file, disabled-custom with-file).
182
- Export `buildMenuOptions` as a pure function.
183
- Extract the menu-option computation from `showAgentDetail` into it.
184
- Verify all existing tests pass.
185
- Commit: `refactor: extract buildMenuOptions from showAgentDetail`
186
-
187
- 2. **Red → Green:** Extract `handleEdit`, `handleDelete`, `handleReset` as closure-internal functions.
188
- Simplify `showAgentDetail` dispatch to a flat if/else chain calling named handlers.
189
- Verify all existing tests pass.
190
- Commit: `refactor: extract inline handlers from showAgentDetail`
191
-
192
- 3. **Red → Green:** Add `buildEjectContent` unit tests (minimal config, config with all optional fields, config with array extensions/skills).
193
- Export `buildEjectContent` as a pure function.
194
- Extract the frontmatter-building logic from `ejectAgent` into it.
195
- Verify all existing tests pass.
196
- Commit: `refactor: extract buildEjectContent from ejectAgent`
197
-
198
- 4. **Docs:** Update the complexity hotspots table in `docs/architecture/architecture.md`.
199
- Commit: `docs: update complexity table after showAgentDetail decomposition`
200
-
201
- ## Risks and Mitigations
202
-
203
- | Risk | Mitigation |
204
- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
205
- | `buildMenuOptions` return order must match existing select mock expectations | Unit tests verify exact array equality; integration tests remain as safety net. |
206
- | `buildEjectContent` frontmatter field ordering is load-bearing for eject tests | Unit tests verify the full content string; existing eject integration test uses `stringContaining` (flexible). |
207
- | Closure-internal handlers share mutable `fileOps`/`registry` references | No change from current behavior — they already close over these references. |
208
-
209
- ## Open Questions
210
-
211
- None — the decomposition is mechanical extraction of existing code into named functions.
@@ -1,242 +0,0 @@
1
- ---
2
- issue: 207
3
- issue_title: "Decompose update in agent-widget.ts (cognitive 31)"
4
- ---
5
-
6
- # Decompose `update` in `agent-widget.ts`
7
-
8
- ## Problem Statement
9
-
10
- `update` in `ui/agent-widget.ts` has cognitive complexity 31 (CRITICAL per fallow health).
11
- It mixes agent state categorization, widget teardown, status bar management, and widget registration dispatch in a single 70-line method.
12
- Phase 12, Step 3 targets cognitive complexity < 10 per function.
13
-
14
- ## Goals
15
-
16
- - Extract `assembleWidgetState` as an exported pure function (agent list → lightweight counts/flags) that is directly unit-testable.
17
- - Extract `clearWidget` as a private method encapsulating the "nothing to show" teardown path.
18
- - Extract `updateStatusBar` as a private method encapsulating status text computation and conditional update.
19
- - Simplify `update` to a thin orchestrator: guard → assemble → if idle clear → else update status + register.
20
- - Target: cognitive complexity < 10 for every function in the file.
21
-
22
- ## Non-Goals
23
-
24
- - Decomposing `renderWidgetLines` (#205, done), `showAgentDetail` (#206, done), or shared test fixtures (#208) — sibling Phase 12 steps.
25
- - Extracting a separate timer-manager class — the timer lifecycle (`ensureTimer` + `clearWidget`) is two lines of `setInterval`/`clearInterval`.
26
- - Changing the widget's visual output, status bar format, registration timing, or timer interval.
27
- - Narrowing the `AgentManager` dependency to an interface — tracked separately.
28
- - Adding end-to-end tests for `AgentWidget` — the widget depends on the Pi TUI context.
29
- - Changing `dispose` behavior — it remains a shutdown-only teardown path.
30
-
31
- ## Background
32
-
33
- `agent-widget.ts` was substantially decomposed in #148 (Phase 9, Step P), which extracted pure rendering into `widget-renderer.ts`.
34
- The widget shrank from 374 to ~198 lines.
35
- `update` remained as a 70-line orchestrator with five interwoven concerns:
36
-
37
- 1. **Guard** — early return when `uiCtx` is not yet set.
38
- 2. **Agent state categorization** — counting running/queued/finished agents from `listAgents()`.
39
- 3. **Clear path** — unregistering widget, clearing status, stopping timer, cleaning stale `finishedTurnAge` entries.
40
- 4. **Status bar update** — computing status text from running/queued counts, conditionally calling `setStatus`.
41
- 5. **Widget lifecycle** — incrementing `widgetFrame`, registering the widget factory on first use, calling `requestRender()` on subsequent ticks.
42
-
43
- Concerns 3–4 consume counts from concern 2 but each has a distinct responsibility.
44
-
45
- `categorizeAgents` in `widget-renderer.ts` performs a similar filter but returns full `WidgetAgent[]` arrays for rendering.
46
- `assembleWidgetState` returns lightweight counts for lifecycle decisions — different outputs for different consumers, not duplication.
47
-
48
- No existing tests cover `AgentWidget` methods — `test/widget-renderer.test.ts` covers the rendering layer.
49
-
50
- ### Complexity sources in `update` (cognitive 31)
51
-
52
- 1. Agent counting loop with 3-branch if/else (`status` checks + `shouldShowFinished`) — contributes ~6.
53
- 2. Conditional widget clear path: nested checks for `widgetRegistered`, `lastStatusText`, `widgetInterval`, plus a `for`-loop with nested `if` for stale-entry cleanup — contributes ~12.
54
- 3. Status text computation with conditional `if (hasActive)` and nested `if`/push branches — contributes ~5.
55
- 4. Conditional status update (`newStatusText !== this.lastStatusText`) — contributes ~2.
56
- 5. Widget registration dispatch (`if (!widgetRegistered) ... else ...`) — contributes ~4.
57
-
58
- ### `dispose` is not a duplication target
59
-
60
- `dispose` (10 lines, cognitive ~2) and `update`'s idle path share *some* statements — `clearInterval`, `setWidget(undefined)`, `setStatus(undefined)`, flag resets — but differ in two ways:
61
-
62
- - **Guards:** `update`'s idle path guards each call (`if (widgetRegistered)`, `if (lastStatusText !== undefined)`) to avoid redundant SDK calls during repeated timer ticks.
63
- `dispose` unconditionally calls `setWidget`/`setStatus` (when `uiCtx` exists) as a correctness guarantee during shutdown.
64
- - **Stale-entry cleanup:** `update`'s idle path cleans `finishedTurnAge` entries for agents no longer in `listAgents()`.
65
- `dispose` skips this — the Map is about to be garbage collected.
66
-
67
- Per the code-design skill ("duplication is far cheaper than the wrong abstraction" — verify structural context before extracting), these are different lifecycle semantics.
68
- `dispose` stays as-is; `clearWidget` is extracted only from `update`'s idle path.
69
-
70
- ## Design Overview
71
-
72
- ### `assembleWidgetState` (exported, pure)
73
-
74
- ```typescript
75
- /** Minimal agent shape needed for widget lifecycle decisions. */
76
- interface AgentSummary {
77
- readonly id: string;
78
- readonly status: string;
79
- readonly completedAt?: number;
80
- }
81
-
82
- export interface WidgetState {
83
- readonly runningCount: number;
84
- readonly queuedCount: number;
85
- readonly hasFinished: boolean;
86
- readonly hasActive: boolean;
87
- }
88
-
89
- export function assembleWidgetState(
90
- agents: readonly AgentSummary[],
91
- shouldShowFinished: (agentId: string, status: string) => boolean,
92
- ): WidgetState
93
- ```
94
-
95
- The input uses a local `AgentSummary` interface (3 fields) rather than `WidgetAgent` (10+ fields).
96
- This follows ISP — `assembleWidgetState` only reads `id`, `status`, and `completedAt`.
97
- Tests can pass plain objects without constructing full agent fixtures.
98
- `AgentRecord` satisfies `AgentSummary` structurally, so no adapter is needed at the call site.
99
-
100
- `hasActive` is derived from the counts (`runningCount > 0 || queuedCount > 0`) but included for call-site readability.
101
- Only `assembleWidgetState` constructs `WidgetState`, so consistency is guaranteed.
102
-
103
- ### `clearWidget` (private method)
104
-
105
- Encapsulates `update`'s "nothing to show" teardown path:
106
-
107
- - Unregister widget via `setWidget("agents", undefined)` if `widgetRegistered`.
108
- - Clear status via `setStatus("subagents", undefined)` if `lastStatusText` is set.
109
- - Stop timer via `clearInterval` if `widgetInterval` is running.
110
- - Reset lifecycle flags (`widgetRegistered`, `tui`, `lastStatusText`, `widgetInterval`).
111
- - Clean stale `finishedTurnAge` entries (agents no longer in `allAgents`).
112
-
113
- Accepts `allAgents` as a parameter for the stale-entry cleanup.
114
-
115
- ```typescript
116
- private clearWidget(allAgents: readonly AgentSummary[]): void
117
- ```
118
-
119
- `dispose` does **not** delegate to `clearWidget` — see "dispose is not a duplication target" above.
120
-
121
- ### `updateStatusBar` (private method)
122
-
123
- Encapsulates the status text concern:
124
-
125
- - Compute status text from `runningCount` / `queuedCount` (undefined when `!hasActive`).
126
- - Call `setStatus("subagents", text)` only when text differs from `lastStatusText`.
127
- - Cache the new value in `lastStatusText`.
128
-
129
- ```typescript
130
- private updateStatusBar(state: WidgetState): void
131
- ```
132
-
133
- ### After refactoring
134
-
135
- ```typescript
136
- update() {
137
- if (!this.uiCtx) return;
138
-
139
- const allAgents = this.manager.listAgents();
140
- const state = assembleWidgetState(allAgents, (id, status) => this.shouldShowFinished(id, status));
141
-
142
- if (!state.hasActive && !state.hasFinished) {
143
- this.clearWidget(allAgents);
144
- return;
145
- }
146
-
147
- this.updateStatusBar(state);
148
- this.widgetFrame++;
149
-
150
- if (!this.widgetRegistered) {
151
- this.uiCtx.setWidget("agents", (tui, theme) => {
152
- this.tui = tui;
153
- return {
154
- render: () => this.renderWidget(tui, theme),
155
- invalidate: () => {
156
- this.widgetRegistered = false;
157
- this.tui = undefined;
158
- },
159
- };
160
- }, { placement: "aboveEditor" });
161
- this.widgetRegistered = true;
162
- } else {
163
- this.tui?.requestRender();
164
- }
165
- }
166
- ```
167
-
168
- Cognitive complexity: ~4 (one guard early return + one if/else branch + flat registration dispatch).
169
-
170
- ### Complexity budget
171
-
172
- | Function | Estimated cognitive complexity |
173
- | --------------------- | ----------------------------------------------- |
174
- | `assembleWidgetState` | ~3 (flat loop with 3 branches) |
175
- | `clearWidget` | ~6 (4 guards + loop with if) |
176
- | `updateStatusBar` | ~4 (hasActive check + diff check) |
177
- | `update` (after) | ~4 (guard + idle check + registration dispatch) |
178
- | `dispose` (unchanged) | ~2 |
179
-
180
- All under 10.
181
-
182
- ## Module-Level Changes
183
-
184
- ### Changed: `src/ui/agent-widget.ts`
185
-
186
- - Add local `AgentSummary` interface (3 fields: `id`, `status`, `completedAt?`).
187
- - Add exported `WidgetState` interface.
188
- - Add exported `assembleWidgetState(agents, shouldShowFinished)` pure function.
189
- - Add private `clearWidget(allAgents)` method — extracted from `update`'s idle path.
190
- - Add private `updateStatusBar(state)` method — extracted from `update`'s status bar logic.
191
- - Simplify `update` to orchestrate: guard → assemble → if idle clear → else update status + register.
192
- - `dispose` is unchanged.
193
-
194
- No exports are removed or renamed.
195
- The public API (`AgentWidget` class with `setUICtx`, `onTurnStart`, `ensureTimer`, `markFinished`, `update`, `dispose`) is unchanged.
196
- `UICtx` type stays exported.
197
-
198
- ### Unchanged: `test/widget-renderer.test.ts`
199
-
200
- No changes — this file covers `widget-renderer.ts` functions, not `agent-widget.ts`.
201
-
202
- ### Changed: `docs/architecture/architecture.md`
203
-
204
- - Update the complexity hotspots table: remove `update` row (no longer ≥ 21 cyclomatic).
205
- - Also remove the `renderWidgetLines` row if #205 is already implemented (it is — closed).
206
-
207
- ## Test Impact Analysis
208
-
209
- 1. **New tests enabled:** Direct unit tests for `assembleWidgetState` — a pure function accepting plain objects.
210
- Tests cover: empty list, running-only, queued-only, finished-only, mixed states, `shouldShowFinished` filtering, agents without `completedAt` excluded from finished.
211
- The narrow `AgentSummary` input type means test fixtures are 3-field objects — no need for full agent records.
212
- 2. **No existing tests become redundant** — there are currently no tests for `AgentWidget`.
213
- 3. **No existing tests must change** — `test/widget-renderer.test.ts` (23 tests) exercises the renderer layer and is unaffected.
214
-
215
- ## TDD Order
216
-
217
- 1. **Red → Green:** Write unit tests for `assembleWidgetState` covering all agent status combinations.
218
- Add the `AgentSummary` interface, `WidgetState` interface, and `assembleWidgetState` function as exported module-level items.
219
- Implement to make tests pass.
220
- Commit: `feat: extract assembleWidgetState from agent-widget update`
221
-
222
- 2. **Refactor:** Wire `update` to use `assembleWidgetState`.
223
- Extract `clearWidget(allAgents)` method from the idle path.
224
- Extract `updateStatusBar(state)` method from the status bar logic.
225
- All existing tests pass (no behavior change, no export changes, `dispose` unchanged).
226
- Commit: `refactor: decompose update into clearWidget and updateStatusBar`
227
-
228
- 3. **Docs:** Update the complexity hotspots table in `docs/architecture/architecture.md`.
229
- Remove both `renderWidgetLines` (done in #205) and `update` rows.
230
- Commit: `docs: update complexity hotspots after widget decomposition`
231
-
232
- ## Risks and Mitigations
233
-
234
- | Risk | Mitigation |
235
- | --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
236
- | `assembleWidgetState` has overlapping categorization with `categorizeAgents` in `widget-renderer.ts` | Different outputs for different consumers: counts+flags for lifecycle vs. full arrays for rendering. Not duplication. |
237
- | No existing tests for `AgentWidget` — refactoring risks are higher for `clearWidget`/`updateStatusBar` | The pure function `assembleWidgetState` is tested directly. The method extractions are mechanical code moves with no semantic change — the type checker verifies structural integrity. |
238
- | `clearWidget` guards redundant SDK calls (`if (widgetRegistered)`) — caller might expect unconditional teardown | Only `update`'s idle path calls `clearWidget`. `dispose` stays as-is with its own unconditional teardown semantics. |
239
-
240
- ## Open Questions
241
-
242
- None — the decomposition is a mechanical extraction of existing code into named functions and methods, following the pattern established by Phase 12 Steps 1 and 2.