@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,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.