@gotgenes/pi-subagents 18.0.0 → 18.0.2

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