maestro-flow 0.4.2 → 0.4.4

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 (302) hide show
  1. package/.claude/commands/maestro-analyze.md +1 -1
  2. package/.claude/commands/maestro-brainstorm.md +1 -1
  3. package/.claude/commands/maestro-collab.md +1 -1
  4. package/.claude/commands/maestro-execute.md +10 -1
  5. package/.claude/commands/maestro-guard.md +101 -0
  6. package/.claude/commands/maestro-impeccable.md +1 -1
  7. package/.claude/commands/maestro-plan.md +15 -2
  8. package/.claude/commands/maestro-ralph-execute.md +9 -2
  9. package/.claude/commands/maestro-ralph.md +8 -1
  10. package/.claude/commands/maestro-verify.md +15 -1
  11. package/.claude/commands/quality-auto-test.md +1 -1
  12. package/.claude/commands/quality-debug.md +1 -1
  13. package/.claude/commands/quality-refactor.md +1 -1
  14. package/.claude/commands/quality-retrospective.md +1 -1
  15. package/.claude/commands/quality-review.md +15 -1
  16. package/.claude/commands/quality-test.md +1 -1
  17. package/.claude/commands/security-audit.md +154 -0
  18. package/.claude/skills/maestro-help/index/catalog.json +2 -0
  19. package/.codex/skills/maestro-analyze/SKILL.md +18 -1
  20. package/.codex/skills/maestro-brainstorm/SKILL.md +17 -4
  21. package/.codex/skills/maestro-collab/SKILL.md +7 -1
  22. package/.codex/skills/maestro-execute/SKILL.md +365 -348
  23. package/.codex/skills/maestro-guard/SKILL.md +97 -0
  24. package/.codex/skills/maestro-impeccable/SKILL.md +1 -1
  25. package/.codex/skills/maestro-plan/SKILL.md +66 -7
  26. package/.codex/skills/maestro-ralph/SKILL.md +1 -1
  27. package/.codex/skills/maestro-verify/SKILL.md +18 -1
  28. package/.codex/skills/quality-auto-test/SKILL.md +13 -3
  29. package/.codex/skills/quality-debug/SKILL.md +362 -346
  30. package/.codex/skills/quality-refactor/SKILL.md +1 -1
  31. package/.codex/skills/quality-retrospective/SKILL.md +292 -292
  32. package/.codex/skills/quality-review/SKILL.md +374 -365
  33. package/.codex/skills/quality-test/SKILL.md +1 -1
  34. package/.codex/skills/security-audit/SKILL.md +154 -0
  35. package/bin/maestro-hook-runner.js +21 -1
  36. package/dashboard/dist/assets/{ArtifactsPage-iJZtYsmR.js → ArtifactsPage-CVh0Z2I2.js} +3 -3
  37. package/dashboard/dist/assets/{ChatInput-DNnDLdQF.js → ChatInput-CBI3qHQQ.js} +2 -2
  38. package/dashboard/dist/assets/ChatPage-BjJ9CYox.js +22 -0
  39. package/dashboard/dist/assets/CollabPage-CprGGO9y.js +1 -0
  40. package/dashboard/dist/assets/{ExecutionPanel-BKV3GZ7Q.js → ExecutionPanel-CClxD7cH.js} +1 -1
  41. package/dashboard/dist/assets/KanbanPage-copqjdPg.js +16 -0
  42. package/dashboard/dist/assets/MaestroCoordinatePage-CioZjQ9N.js +4 -0
  43. package/dashboard/dist/assets/{MarkdownRenderer-cYFfe1uX.js → MarkdownRenderer-CtUhoxCT.js} +1 -1
  44. package/dashboard/dist/assets/McpPage-BcPPcJpr.js +21 -0
  45. package/dashboard/dist/assets/{MeetingRoomPage-wnvT7wlB.js → MeetingRoomPage-BgmAKxU-.js} +1 -1
  46. package/dashboard/dist/assets/{OutputPanel-DDL90Idy.js → OutputPanel-DgT3gMyp.js} +1 -1
  47. package/dashboard/dist/assets/{ProblemsPanel-zFN9IIs0.js → ProblemsPanel-BmG7rxoG.js} +1 -1
  48. package/dashboard/dist/assets/{RequirementBoardPage-DOPJoT0I.js → RequirementBoardPage-k8YoeQ0r.js} +1 -1
  49. package/dashboard/dist/assets/{RequirementPage-CEVquRgM.js → RequirementPage-Da2354px.js} +1 -1
  50. package/dashboard/dist/assets/{RoomsPage-D5USEWDh.js → RoomsPage-BtqDiYaU.js} +1 -1
  51. package/dashboard/dist/assets/SpecsPage-ByPVH_M3.js +36 -0
  52. package/dashboard/dist/assets/{TeamsPage-DlcEmr_Q.js → TeamsPage-sFDLN30L.js} +1 -1
  53. package/dashboard/dist/assets/{TreeBrowser-Y48Wz-QY.js → TreeBrowser-oEx8YJXV.js} +1 -1
  54. package/dashboard/dist/assets/WorkflowPage-JrX7CVHh.js +6 -0
  55. package/dashboard/dist/assets/{arrow-left-NCUOENvg.js → arrow-left-DYvgSdIH.js} +1 -1
  56. package/dashboard/dist/assets/{check-o7nfGNHf.js → check-jcgYBWVR.js} +1 -1
  57. package/dashboard/dist/assets/{chevron-right-BXySK2fn.js → chevron-right-DvZ5sMOg.js} +1 -1
  58. package/dashboard/dist/assets/{circle-cooAwnAR.js → circle-DYT-zoRZ.js} +1 -1
  59. package/dashboard/dist/assets/{circle-alert-DcSBokh4.js → circle-alert-Bfbv3gt4.js} +1 -1
  60. package/dashboard/dist/assets/{circle-check-Cd-jce4j.js → circle-check-D82WnpbI.js} +1 -1
  61. package/dashboard/dist/assets/{circle-check-big-BN7Mdp4i.js → circle-check-big-CPVD1GKF.js} +1 -1
  62. package/dashboard/dist/assets/{code-BQa9oL1n.js → code-B3bKFGI4.js} +1 -1
  63. package/dashboard/dist/assets/{columns-3-BzgxelGx.js → columns-3-BeMAQCix.js} +1 -1
  64. package/dashboard/dist/assets/{download-swRJnate.js → download-BCtpoWYB.js} +1 -1
  65. package/dashboard/dist/assets/en-2h6fD0j8.js +1 -0
  66. package/dashboard/dist/assets/{folder-DbGbLNFN.js → folder-CTj6SNNu.js} +1 -1
  67. package/dashboard/dist/assets/index-6-yE5Yl9.css +1 -0
  68. package/dashboard/dist/assets/{index-DIoMBMbv.js → index-BxR_3IbJ.js} +1 -1
  69. package/dashboard/dist/assets/{index-DLBN_7fb.js → index-CWBJLu42.js} +1 -1
  70. package/dashboard/dist/assets/index-DpcPd-UG.js +236 -0
  71. package/dashboard/dist/assets/{list-BTRAIvDq.js → list-BMv8pIQn.js} +1 -1
  72. package/dashboard/dist/assets/{loader-NHtB6Mdn.js → loader-DdM4VOgF.js} +1 -1
  73. package/dashboard/dist/assets/{minus-BjMxRTET.js → minus-D15s2E__.js} +1 -1
  74. package/dashboard/dist/assets/{pen-line-C4_O16H0.js → pen-line-DO6o4xWz.js} +1 -1
  75. package/dashboard/dist/assets/{pencil-DTmKhyDY.js → pencil-qGxg9jOe.js} +1 -1
  76. package/dashboard/dist/assets/{proxy-Dtx5p6IO.js → proxy-DOffTzwA.js} +1 -1
  77. package/dashboard/dist/assets/refresh-cw-C9UNdLCy.js +6 -0
  78. package/dashboard/dist/assets/{rows-2-GR1dZtRu.js → rows-2-CifAA5SL.js} +1 -1
  79. package/dashboard/dist/assets/{search-DVtgy2W7.js → search-pe7pU1YN.js} +1 -1
  80. package/dashboard/dist/assets/{shallow-BOmvDNsv.js → shallow-r7YynQYA.js} +1 -1
  81. package/dashboard/dist/assets/table-Bh772iIw.js +6 -0
  82. package/dashboard/dist/assets/{team-types-BPeOvVdA.js → team-types-C_HqX2p2.js} +1 -1
  83. package/dashboard/dist/assets/{terminal-CzogW4cl.js → terminal-EpmtGBlw.js} +1 -1
  84. package/dashboard/dist/assets/{trash-2-52LATVfW.js → trash-2-Xen46iNQ.js} +1 -1
  85. package/dashboard/dist/assets/{users-DLFE2voE.js → users-BLueDPxF.js} +1 -1
  86. package/dashboard/dist/assets/{zap-DhiYlgyZ.js → zap-DsQCPF61.js} +1 -1
  87. package/dashboard/dist/assets/zh-CN-r3AvxxOL.js +1 -0
  88. package/dashboard/dist/index.html +2 -2
  89. package/dashboard/dist-server/dashboard/src/server/routes/settings.js +70 -1
  90. package/dashboard/dist-server/dashboard/src/server/routes/settings.js.map +1 -1
  91. package/dashboard/dist-server/src/coordinator/output-parser.js +27 -0
  92. package/dashboard/dist-server/src/coordinator/output-parser.js.map +1 -1
  93. package/dashboard/dist-server/src/types/index.d.ts +48 -6
  94. package/dist/src/commands/coordinate.d.ts.map +1 -1
  95. package/dist/src/commands/coordinate.js +4 -0
  96. package/dist/src/commands/coordinate.js.map +1 -1
  97. package/dist/src/commands/hooks.d.ts.map +1 -1
  98. package/dist/src/commands/hooks.js +239 -8
  99. package/dist/src/commands/hooks.js.map +1 -1
  100. package/dist/src/commands/spec.d.ts.map +1 -1
  101. package/dist/src/commands/spec.js +466 -0
  102. package/dist/src/commands/spec.js.map +1 -1
  103. package/dist/src/config/index.d.ts +5 -1
  104. package/dist/src/config/index.d.ts.map +1 -1
  105. package/dist/src/config/index.js +42 -0
  106. package/dist/src/config/index.js.map +1 -1
  107. package/dist/src/coordinator/output-parser.d.ts.map +1 -1
  108. package/dist/src/coordinator/output-parser.js +27 -0
  109. package/dist/src/coordinator/output-parser.js.map +1 -1
  110. package/dist/src/hooks/delegate-monitor.d.ts +1 -0
  111. package/dist/src/hooks/delegate-monitor.d.ts.map +1 -1
  112. package/dist/src/hooks/delegate-monitor.js +1 -1
  113. package/dist/src/hooks/delegate-monitor.js.map +1 -1
  114. package/dist/src/hooks/guards/workflow-guard.d.ts +15 -0
  115. package/dist/src/hooks/guards/workflow-guard.d.ts.map +1 -1
  116. package/dist/src/hooks/guards/workflow-guard.js +61 -1
  117. package/dist/src/hooks/guards/workflow-guard.js.map +1 -1
  118. package/dist/src/hooks/keyword-spec-injector.d.ts.map +1 -1
  119. package/dist/src/hooks/keyword-spec-injector.js +63 -4
  120. package/dist/src/hooks/keyword-spec-injector.js.map +1 -1
  121. package/dist/src/hooks/plugins/decision-log-plugin.d.ts +19 -0
  122. package/dist/src/hooks/plugins/decision-log-plugin.d.ts.map +1 -0
  123. package/dist/src/hooks/plugins/decision-log-plugin.js +28 -0
  124. package/dist/src/hooks/plugins/decision-log-plugin.js.map +1 -0
  125. package/dist/src/hooks/plugins/index.d.ts +2 -0
  126. package/dist/src/hooks/plugins/index.d.ts.map +1 -1
  127. package/dist/src/hooks/plugins/index.js +1 -0
  128. package/dist/src/hooks/plugins/index.js.map +1 -1
  129. package/dist/src/hooks/plugins/spec-analytics-plugin.d.ts +13 -0
  130. package/dist/src/hooks/plugins/spec-analytics-plugin.d.ts.map +1 -0
  131. package/dist/src/hooks/plugins/spec-analytics-plugin.js +92 -0
  132. package/dist/src/hooks/plugins/spec-analytics-plugin.js.map +1 -0
  133. package/dist/src/hooks/plugins/spec-injection-plugin.d.ts.map +1 -1
  134. package/dist/src/hooks/plugins/spec-injection-plugin.js +57 -4
  135. package/dist/src/hooks/plugins/spec-injection-plugin.js.map +1 -1
  136. package/dist/src/hooks/session-context.d.ts +1 -0
  137. package/dist/src/hooks/session-context.d.ts.map +1 -1
  138. package/dist/src/hooks/session-context.js +1 -1
  139. package/dist/src/hooks/session-context.js.map +1 -1
  140. package/dist/src/hooks/skill-context.d.ts +1 -0
  141. package/dist/src/hooks/skill-context.d.ts.map +1 -1
  142. package/dist/src/hooks/skill-context.js +1 -1
  143. package/dist/src/hooks/skill-context.js.map +1 -1
  144. package/dist/src/hooks/spec-analytics.d.ts +128 -0
  145. package/dist/src/hooks/spec-analytics.d.ts.map +1 -0
  146. package/dist/src/hooks/spec-analytics.js +311 -0
  147. package/dist/src/hooks/spec-analytics.js.map +1 -0
  148. package/dist/src/hooks/spec-injector.d.ts.map +1 -1
  149. package/dist/src/hooks/spec-injector.js +129 -5
  150. package/dist/src/hooks/spec-injector.js.map +1 -1
  151. package/dist/src/tools/spec-loader.d.ts +20 -0
  152. package/dist/src/tools/spec-loader.d.ts.map +1 -1
  153. package/dist/src/tools/spec-loader.js +58 -7
  154. package/dist/src/tools/spec-loader.js.map +1 -1
  155. package/dist/src/tui/config-ui/ConfigHub.d.ts +1 -1
  156. package/dist/src/tui/config-ui/ConfigHub.d.ts.map +1 -1
  157. package/dist/src/tui/config-ui/ConfigHub.js +16 -10
  158. package/dist/src/tui/config-ui/ConfigHub.js.map +1 -1
  159. package/dist/src/tui/config-ui/ConfigSourcesView.d.ts.map +1 -1
  160. package/dist/src/tui/config-ui/ConfigSourcesView.js +3 -2
  161. package/dist/src/tui/config-ui/ConfigSourcesView.js.map +1 -1
  162. package/dist/src/tui/config-ui/HooksPanel.d.ts.map +1 -1
  163. package/dist/src/tui/config-ui/HooksPanel.js +15 -19
  164. package/dist/src/tui/config-ui/HooksPanel.js.map +1 -1
  165. package/dist/src/tui/config-ui/SkillConfigDashboard.d.ts.map +1 -1
  166. package/dist/src/tui/config-ui/SkillConfigDashboard.js +5 -9
  167. package/dist/src/tui/config-ui/SkillConfigDashboard.js.map +1 -1
  168. package/dist/src/tui/config-ui/SkillParamEditor.d.ts.map +1 -1
  169. package/dist/src/tui/config-ui/SkillParamEditor.js +1 -3
  170. package/dist/src/tui/config-ui/SkillParamEditor.js.map +1 -1
  171. package/dist/src/tui/config-ui/SkillsList.d.ts.map +1 -1
  172. package/dist/src/tui/config-ui/SkillsList.js +7 -9
  173. package/dist/src/tui/config-ui/SkillsList.js.map +1 -1
  174. package/dist/src/tui/config-ui/SpecAnalyticsPanel.d.ts +6 -0
  175. package/dist/src/tui/config-ui/SpecAnalyticsPanel.d.ts.map +1 -0
  176. package/dist/src/tui/config-ui/SpecAnalyticsPanel.js +164 -0
  177. package/dist/src/tui/config-ui/SpecAnalyticsPanel.js.map +1 -0
  178. package/dist/src/tui/config-ui/SpecPanel.d.ts.map +1 -1
  179. package/dist/src/tui/config-ui/SpecPanel.js +918 -20
  180. package/dist/src/tui/config-ui/SpecPanel.js.map +1 -1
  181. package/dist/src/tui/config-ui/index.d.ts +3 -1
  182. package/dist/src/tui/config-ui/index.d.ts.map +1 -1
  183. package/dist/src/tui/config-ui/index.js +4 -0
  184. package/dist/src/tui/config-ui/index.js.map +1 -1
  185. package/dist/src/tui/install-ui/BackupConfig.d.ts.map +1 -1
  186. package/dist/src/tui/install-ui/BackupConfig.js +3 -2
  187. package/dist/src/tui/install-ui/BackupConfig.js.map +1 -1
  188. package/dist/src/tui/install-ui/BlueprintPreview.d.ts.map +1 -1
  189. package/dist/src/tui/install-ui/BlueprintPreview.js +7 -6
  190. package/dist/src/tui/install-ui/BlueprintPreview.js.map +1 -1
  191. package/dist/src/tui/install-ui/ComponentGrid.d.ts.map +1 -1
  192. package/dist/src/tui/install-ui/ComponentGrid.js +3 -2
  193. package/dist/src/tui/install-ui/ComponentGrid.js.map +1 -1
  194. package/dist/src/tui/install-ui/ConfigPanel.d.ts.map +1 -1
  195. package/dist/src/tui/install-ui/ConfigPanel.js +4 -3
  196. package/dist/src/tui/install-ui/ConfigPanel.js.map +1 -1
  197. package/dist/src/tui/install-ui/CyberItem.d.ts.map +1 -1
  198. package/dist/src/tui/install-ui/CyberItem.js +7 -6
  199. package/dist/src/tui/install-ui/CyberItem.js.map +1 -1
  200. package/dist/src/tui/install-ui/CyberdeckBlueprint.d.ts.map +1 -1
  201. package/dist/src/tui/install-ui/CyberdeckBlueprint.js +2 -1
  202. package/dist/src/tui/install-ui/CyberdeckBlueprint.js.map +1 -1
  203. package/dist/src/tui/install-ui/ExecutionView.d.ts.map +1 -1
  204. package/dist/src/tui/install-ui/ExecutionView.js +3 -2
  205. package/dist/src/tui/install-ui/ExecutionView.js.map +1 -1
  206. package/dist/src/tui/install-ui/GradientHeader.d.ts.map +1 -1
  207. package/dist/src/tui/install-ui/GradientHeader.js +2 -1
  208. package/dist/src/tui/install-ui/GradientHeader.js.map +1 -1
  209. package/dist/src/tui/install-ui/HooksConfig.d.ts.map +1 -1
  210. package/dist/src/tui/install-ui/HooksConfig.js +8 -8
  211. package/dist/src/tui/install-ui/HooksConfig.js.map +1 -1
  212. package/dist/src/tui/install-ui/InstallConfirm.d.ts.map +1 -1
  213. package/dist/src/tui/install-ui/InstallConfirm.js +4 -3
  214. package/dist/src/tui/install-ui/InstallConfirm.js.map +1 -1
  215. package/dist/src/tui/install-ui/InstallExecution.d.ts.map +1 -1
  216. package/dist/src/tui/install-ui/InstallExecution.js +3 -2
  217. package/dist/src/tui/install-ui/InstallExecution.js.map +1 -1
  218. package/dist/src/tui/install-ui/InstallFlow.d.ts.map +1 -1
  219. package/dist/src/tui/install-ui/InstallFlow.js +3 -2
  220. package/dist/src/tui/install-ui/InstallFlow.js.map +1 -1
  221. package/dist/src/tui/install-ui/InstallHub.d.ts.map +1 -1
  222. package/dist/src/tui/install-ui/InstallHub.js +9 -8
  223. package/dist/src/tui/install-ui/InstallHub.js.map +1 -1
  224. package/dist/src/tui/install-ui/InstallResult.d.ts.map +1 -1
  225. package/dist/src/tui/install-ui/InstallResult.js +3 -2
  226. package/dist/src/tui/install-ui/InstallResult.js.map +1 -1
  227. package/dist/src/tui/install-ui/McpConfig.d.ts.map +1 -1
  228. package/dist/src/tui/install-ui/McpConfig.js +4 -3
  229. package/dist/src/tui/install-ui/McpConfig.js.map +1 -1
  230. package/dist/src/tui/install-ui/ResultDashboard.d.ts.map +1 -1
  231. package/dist/src/tui/install-ui/ResultDashboard.js +3 -2
  232. package/dist/src/tui/install-ui/ResultDashboard.js.map +1 -1
  233. package/dist/src/tui/install-ui/ReviewPanel.d.ts.map +1 -1
  234. package/dist/src/tui/install-ui/ReviewPanel.js +2 -1
  235. package/dist/src/tui/install-ui/ReviewPanel.js.map +1 -1
  236. package/dist/src/tui/install-ui/StatuslineConfig.d.ts.map +1 -1
  237. package/dist/src/tui/install-ui/StatuslineConfig.js +3 -2
  238. package/dist/src/tui/install-ui/StatuslineConfig.js.map +1 -1
  239. package/dist/src/tui/install-ui/StepSelector.d.ts.map +1 -1
  240. package/dist/src/tui/install-ui/StepSelector.js +11 -10
  241. package/dist/src/tui/install-ui/StepSelector.js.map +1 -1
  242. package/dist/src/tui/overlay-ui/OverlayList.d.ts.map +1 -1
  243. package/dist/src/tui/overlay-ui/OverlayList.js +8 -12
  244. package/dist/src/tui/overlay-ui/OverlayList.js.map +1 -1
  245. package/dist/src/tui/shared/components.d.ts +56 -0
  246. package/dist/src/tui/shared/components.d.ts.map +1 -0
  247. package/dist/src/tui/shared/components.js +55 -0
  248. package/dist/src/tui/shared/components.js.map +1 -0
  249. package/dist/src/tui/shared/helpers.d.ts +24 -0
  250. package/dist/src/tui/shared/helpers.d.ts.map +1 -0
  251. package/dist/src/tui/shared/helpers.js +49 -0
  252. package/dist/src/tui/shared/helpers.js.map +1 -0
  253. package/dist/src/tui/shared/index.d.ts +4 -0
  254. package/dist/src/tui/shared/index.d.ts.map +1 -0
  255. package/dist/src/tui/shared/index.js +7 -0
  256. package/dist/src/tui/shared/index.js.map +1 -0
  257. package/dist/src/tui/shared/tokens.d.ts +79 -0
  258. package/dist/src/tui/shared/tokens.d.ts.map +1 -0
  259. package/dist/src/tui/shared/tokens.js +81 -0
  260. package/dist/src/tui/shared/tokens.js.map +1 -0
  261. package/dist/src/tui/tools-ui/CommandReference.d.ts.map +1 -1
  262. package/dist/src/tui/tools-ui/CommandReference.js +2 -4
  263. package/dist/src/tui/tools-ui/CommandReference.js.map +1 -1
  264. package/dist/src/tui/tools-ui/ConfigSources.d.ts.map +1 -1
  265. package/dist/src/tui/tools-ui/ConfigSources.js +3 -2
  266. package/dist/src/tui/tools-ui/ConfigSources.js.map +1 -1
  267. package/dist/src/tui/tools-ui/RegisterSettings.d.ts.map +1 -1
  268. package/dist/src/tui/tools-ui/RegisterSettings.js +2 -1
  269. package/dist/src/tui/tools-ui/RegisterSettings.js.map +1 -1
  270. package/dist/src/tui/tools-ui/RoleMappings.d.ts.map +1 -1
  271. package/dist/src/tui/tools-ui/RoleMappings.js +3 -5
  272. package/dist/src/tui/tools-ui/RoleMappings.js.map +1 -1
  273. package/dist/src/tui/tools-ui/ToolsDashboard.d.ts.map +1 -1
  274. package/dist/src/tui/tools-ui/ToolsDashboard.js +3 -5
  275. package/dist/src/tui/tools-ui/ToolsDashboard.js.map +1 -1
  276. package/dist/src/tui/tools-ui/ToolsOverview.d.ts.map +1 -1
  277. package/dist/src/tui/tools-ui/ToolsOverview.js +3 -5
  278. package/dist/src/tui/tools-ui/ToolsOverview.js.map +1 -1
  279. package/dist/src/tui/uninstall-ui/UninstallFlow.d.ts.map +1 -1
  280. package/dist/src/tui/uninstall-ui/UninstallFlow.js +8 -7
  281. package/dist/src/tui/uninstall-ui/UninstallFlow.js.map +1 -1
  282. package/dist/src/types/index.d.ts +48 -6
  283. package/dist/src/types/index.d.ts.map +1 -1
  284. package/package.json +1 -1
  285. package/workflows/debug.md +73 -0
  286. package/workflows/execute.md +27 -0
  287. package/workflows/plan.md +11 -0
  288. package/workflows/review.md +33 -1
  289. package/workflows/tdd.md +257 -0
  290. package/workflows/verify.md +57 -0
  291. package/dashboard/dist/assets/ChatPage-BjBibfE4.js +0 -22
  292. package/dashboard/dist/assets/CollabPage-8lUMUol_.js +0 -1
  293. package/dashboard/dist/assets/KanbanPage-diY3QmGd.js +0 -21
  294. package/dashboard/dist/assets/McpPage-COjuIf7U.js +0 -21
  295. package/dashboard/dist/assets/SpecsPage-BOwBin_o.js +0 -36
  296. package/dashboard/dist/assets/SupervisorPage-5iRyMU5T.js +0 -6
  297. package/dashboard/dist/assets/WorkflowPage-CmQaRVgL.js +0 -6
  298. package/dashboard/dist/assets/en-C_BD3UCD.js +0 -1
  299. package/dashboard/dist/assets/index-BEUaOz_b.css +0 -1
  300. package/dashboard/dist/assets/index-uIqUCT8y.js +0 -236
  301. package/dashboard/dist/assets/table-DCzuJAFh.js +0 -6
  302. package/dashboard/dist/assets/zh-CN-DvQKfow3.js +0 -1
@@ -1,49 +1,105 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect } from 'react';
3
3
  import { Box, Text, useInput, useApp } from 'ink';
4
+ import { C, SYM, SP, BORDER, pad, SectionHeader } from '../shared/index.js';
5
+ // ---------------------------------------------------------------------------
6
+ // Constants
7
+ // ---------------------------------------------------------------------------
4
8
  const SCOPE_LABELS = {
5
9
  project: 'Project',
6
10
  global: 'Global',
7
11
  team: 'Team',
8
12
  };
13
+ const MODE_TABS = [
14
+ { key: 'v', mode: 'view', label: 'view' },
15
+ { key: 'b', mode: 'browse', label: 'browse' },
16
+ { key: 'p', mode: 'preview', label: 'preview' },
17
+ { key: 'c', mode: 'config', label: 'config' },
18
+ ];
19
+ /**
20
+ * Agent types from AGENT_CATEGORY_MAP in spec-injector — kept as a static
21
+ * list so the panel does not import the internal constant directly.
22
+ */
23
+ const AGENT_TYPES = [
24
+ 'code-developer',
25
+ 'tdd-developer',
26
+ 'workflow-executor',
27
+ 'universal-executor',
28
+ 'test-fix-agent',
29
+ 'cli-lite-planning-agent',
30
+ 'action-planning-agent',
31
+ 'workflow-planner',
32
+ 'workflow-reviewer',
33
+ 'debug-explore-agent',
34
+ 'workflow-debugger',
35
+ 'general',
36
+ ];
37
+ // ---------------------------------------------------------------------------
38
+ // Main panel
39
+ // ---------------------------------------------------------------------------
9
40
  export function SpecPanel({ workDir, onBack }) {
10
41
  const { exit } = useApp();
42
+ const [mode, setMode] = useState('view');
43
+ // Shared: scope data for View mode (loaded once)
11
44
  const [scopes, setScopes] = useState([]);
12
- const [activeScope, setActiveScope] = useState(0);
13
- const [cursor, setCursor] = useState(0);
14
45
  useEffect(() => {
15
- loadStatus();
46
+ loadScopeStatus();
16
47
  }, []);
17
- async function loadStatus() {
48
+ async function loadScopeStatus() {
18
49
  const { existsSync, readdirSync, readFileSync } = await import('node:fs');
19
50
  const { join } = await import('node:path');
20
51
  const { resolveSpecDir } = await import('../../tools/spec-loader.js');
52
+ const { parseSpecEntries } = await import('../../tools/spec-entry-parser.js');
21
53
  const result = [];
22
54
  for (const scope of ['project', 'global', 'team']) {
23
55
  const dir = resolveSpecDir(workDir, scope);
24
56
  const exists = existsSync(dir);
25
57
  const files = [];
26
58
  if (exists) {
27
- const entries = readdirSync(dir).filter((f) => f.endsWith('.md'));
28
- for (const file of entries) {
59
+ const mdFiles = readdirSync(dir).filter((f) => f.endsWith('.md'));
60
+ for (const file of mdFiles) {
29
61
  const content = readFileSync(join(dir, file), 'utf-8');
30
- const entryCount = (content.match(/<spec-entry\b/g) || []).length;
31
- files.push({ name: file, entries: entryCount, size: content.length });
62
+ const parsed = parseSpecEntries(content);
63
+ const entryBriefs = parsed.entries.map(e => ({
64
+ title: e.title || '(untitled)',
65
+ keywords: e.keywords,
66
+ }));
67
+ files.push({ name: file, entries: entryBriefs.length, size: content.length, entryBriefs });
32
68
  }
33
69
  }
34
70
  result.push({ scope, exists, files });
35
71
  }
36
72
  setScopes(result);
37
73
  }
38
- const currentScope = scopes[activeScope];
39
- const fileCount = currentScope?.files.length ?? 0;
74
+ // Mode switching input — only at top level (sub-components handle their own)
40
75
  useInput((input, key) => {
41
76
  if (input === 'q' || key.escape) {
42
77
  if (onBack)
43
78
  onBack();
44
79
  else
45
80
  exit();
81
+ return;
82
+ }
83
+ for (const tab of MODE_TABS) {
84
+ if (input === tab.key) {
85
+ setMode(tab.mode);
86
+ return;
87
+ }
46
88
  }
89
+ });
90
+ return (_jsx(Box, { flexDirection: "column", paddingX: SP.detailPadX, children: _jsxs(Box, { ...BORDER.primary, flexDirection: "column", paddingX: SP.panelPadX, paddingY: SP.panelPadY, children: [_jsx(SectionHeader, { title: "SPEC SYSTEM" }), _jsx(Text, { children: " " }), _jsx(Box, { gap: 1, children: MODE_TABS.map(tab => (_jsx(Box, { children: mode === tab.mode
91
+ ? _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` [${tab.key}]${tab.label} ` })
92
+ : _jsx(Text, { dimColor: true, children: ` [${tab.key}]${tab.label} ` }) }, tab.mode))) }), _jsx(Text, { children: " " }), mode === 'view' && _jsx(ViewMode, { scopes: scopes }), mode === 'browse' && _jsx(BrowseMode, { workDir: workDir }), mode === 'preview' && _jsx(PreviewMode, { workDir: workDir }), mode === 'config' && _jsx(ConfigMode, { workDir: workDir }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " [v/b/p/c] mode [q] back" })] }) }));
93
+ }
94
+ // ---------------------------------------------------------------------------
95
+ // View mode (existing behavior)
96
+ // ---------------------------------------------------------------------------
97
+ function ViewMode({ scopes }) {
98
+ const [activeScope, setActiveScope] = useState(0);
99
+ const [cursor, setCursor] = useState(0);
100
+ const currentScope = scopes[activeScope];
101
+ const fileCount = currentScope?.files.length ?? 0;
102
+ useInput((input, key) => {
47
103
  if (key.leftArrow) {
48
104
  setActiveScope(s => Math.max(0, s - 1));
49
105
  setCursor(0);
@@ -60,20 +116,862 @@ export function SpecPanel({ workDir, onBack }) {
60
116
  if (scopes.length === 0) {
61
117
  return _jsx(Text, { dimColor: true, children: "Loading spec status..." });
62
118
  }
63
- return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsxs(Box, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "SPEC SYSTEM" }), _jsx(Text, { children: " " }), _jsx(Box, { gap: 1, children: scopes.map((s, i) => (_jsx(Box, { paddingX: 1, children: i === activeScope
64
- ? _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` ${SCOPE_LABELS[s.scope]} ` })
65
- : _jsx(Text, { dimColor: true, children: ` ${SCOPE_LABELS[s.scope]} ` }) }, s.scope))) }), _jsx(Text, { children: " " }), !currentScope.exists ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: " Directory not initialized" }), _jsxs(Text, { dimColor: true, children: [" Run: maestro spec init --scope ", currentScope.scope] })] })) : currentScope.files.length === 0 ? (_jsx(Text, { dimColor: true, children: " No spec files found" })) : (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: pad('', 2) }), _jsx(Text, { dimColor: true, bold: true, children: pad('File', 30) }), _jsx(Text, { dimColor: true, bold: true, children: pad('Entries', 10) }), _jsx(Text, { dimColor: true, bold: true, children: "Size" })] }), currentScope.files.map((f, i) => {
66
- const isCurrent = i === cursor;
67
- const hasEntries = f.entries > 0;
68
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "cyan", children: isCurrent ? '›' : ' ' }), _jsx(Text, { color: hasEntries ? 'green' : 'yellow', children: hasEntries ? '' : '' }), _jsx(Text, { bold: isCurrent, children: pad(f.name, 29) }), _jsx(Text, { dimColor: !isCurrent, children: pad(String(f.entries), 10) }), _jsx(Text, { dimColor: true, children: formatSize(f.size) })] }, f.name));
69
- })] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " \u2190/\u2192 scope \u2191/\u2193 navigate [q] back" }), _jsxs(Text, { dimColor: true, children: [" CLI: maestro spec ", '<', "init|load|add|list|status", '>'] })] }) }));
119
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { gap: 1, children: scopes.map((s, i) => (_jsx(Box, { paddingX: 1, children: i === activeScope
120
+ ? _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` ${SCOPE_LABELS[s.scope]} ` })
121
+ : _jsx(Text, { dimColor: true, children: ` ${SCOPE_LABELS[s.scope]} ` }) }, s.scope))) }), _jsx(Text, { children: " " }), !currentScope.exists ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: " Directory not initialized" }), _jsxs(Text, { dimColor: true, children: [" Run: maestro spec init --scope ", currentScope.scope] })] })) : currentScope.files.length === 0 ? (_jsx(Text, { dimColor: true, children: " No spec files found" })) : (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: pad('', 2) }), _jsx(Text, { dimColor: true, bold: true, children: pad('File', 30) }), _jsx(Text, { dimColor: true, bold: true, children: pad('Entries', 10) }), _jsx(Text, { dimColor: true, bold: true, children: "Size" })] }), currentScope.files.map((f, i) => {
122
+ const isCurrent = i === cursor;
123
+ const hasEntries = f.entries > 0;
124
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: C.primary, children: isCurrent ? SYM.cursor : SYM.cursorBlank }), _jsx(Text, { color: hasEntries ? 'green' : 'yellow', children: hasEntries ? '+' : 'o' }), _jsx(Text, { bold: isCurrent, children: pad(f.name, 29) }), _jsx(Text, { dimColor: !isCurrent, children: pad(String(f.entries), 10) }), _jsx(Text, { dimColor: true, children: formatSize(f.size) })] }, f.name));
125
+ })] })), currentScope?.files[cursor]?.entryBriefs.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Box, { ...BORDER.detail, flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { bold: true, dimColor: true, children: ["Entries in ", currentScope.files[cursor].name, ":"] }), currentScope.files[cursor].entryBriefs.map((e, i) => (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "green", children: "*" }), _jsx(Text, { children: truncate(e.title, 30) }), _jsxs(Text, { dimColor: true, color: "yellow", children: ["[", truncate(e.keywords.join(', '), 40), "]"] })] }, `${e.title}-${i}`)))] })] })), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: [" ", '\u2190', "/", '\u2192', " scope ", '\u2191', "/", '\u2193', " navigate"] }), _jsxs(Text, { dimColor: true, children: [" CLI: maestro spec ", '<', "init|load|add|list|status", '>'] })] }));
70
126
  }
71
- function pad(s, width) {
72
- return s.length >= width ? s : s + ' '.repeat(width - s.length);
127
+ // ---------------------------------------------------------------------------
128
+ // Browse mode keyword-granularity content viewer
129
+ // ---------------------------------------------------------------------------
130
+ function BrowseMode({ workDir }) {
131
+ const [entries, setEntries] = useState([]);
132
+ const [cursor, setCursor] = useState(0);
133
+ const [filterMode, setFilterMode] = useState(false);
134
+ const [filterText, setFilterText] = useState('');
135
+ const [loading, setLoading] = useState(true);
136
+ useEffect(() => {
137
+ loadEntries();
138
+ }, []);
139
+ async function loadEntries() {
140
+ const { existsSync, readdirSync, readFileSync } = await import('node:fs');
141
+ const { join } = await import('node:path');
142
+ const { resolveSpecDir } = await import('../../tools/spec-loader.js');
143
+ const { parseSpecEntries } = await import('../../tools/spec-entry-parser.js');
144
+ const { CATEGORY_MAP } = await import('../../tools/spec-loader.js');
145
+ const allEntries = [];
146
+ for (const scope of ['project', 'global', 'team']) {
147
+ const dir = resolveSpecDir(workDir, scope);
148
+ if (!existsSync(dir))
149
+ continue;
150
+ let files;
151
+ try {
152
+ files = readdirSync(dir).filter((f) => f.endsWith('.md'));
153
+ }
154
+ catch {
155
+ continue;
156
+ }
157
+ for (const file of files) {
158
+ const content = readFileSync(join(dir, file), 'utf-8');
159
+ const parsed = parseSpecEntries(content);
160
+ // Derive category from CATEGORY_MAP or filename
161
+ const fileCategory = CATEGORY_MAP[file] ?? file.replace('.md', '');
162
+ for (const entry of parsed.entries) {
163
+ allEntries.push({
164
+ title: entry.title || '(untitled)',
165
+ category: entry.category || fileCategory,
166
+ keywords: entry.keywords,
167
+ content: entry.content,
168
+ });
169
+ }
170
+ }
171
+ }
172
+ setEntries(allEntries);
173
+ setLoading(false);
174
+ }
175
+ // Filter entries by keyword text
176
+ const filtered = filterText
177
+ ? entries.filter(e => e.keywords.some(kw => kw.toLowerCase().includes(filterText.toLowerCase())))
178
+ : entries;
179
+ useInput((input, key) => {
180
+ // Filter mode: capture text
181
+ if (filterMode) {
182
+ if (key.escape || key.return) {
183
+ setFilterMode(false);
184
+ setCursor(0);
185
+ return;
186
+ }
187
+ if (key.backspace || key.delete) {
188
+ setFilterText(t => t.slice(0, -1));
189
+ setCursor(0);
190
+ return;
191
+ }
192
+ if (input && !key.ctrl && !key.meta) {
193
+ setFilterText(t => t + input);
194
+ setCursor(0);
195
+ return;
196
+ }
197
+ return;
198
+ }
199
+ // Normal mode
200
+ if (input === '/') {
201
+ setFilterMode(true);
202
+ setFilterText('');
203
+ return;
204
+ }
205
+ if (key.upArrow)
206
+ setCursor(c => Math.max(0, c - 1));
207
+ if (key.downArrow)
208
+ setCursor(c => Math.min(filtered.length - 1, c + 1));
209
+ });
210
+ if (loading) {
211
+ return _jsx(Text, { dimColor: true, children: "Loading spec entries..." });
212
+ }
213
+ if (entries.length === 0) {
214
+ return _jsx(Text, { dimColor: true, children: "No spec entries found across any scope." });
215
+ }
216
+ const selected = filtered[cursor];
217
+ // Determine visible window for scrolling
218
+ const MAX_VISIBLE = 12;
219
+ const windowStart = Math.max(0, cursor - Math.floor(MAX_VISIBLE / 2));
220
+ const visibleEntries = filtered.slice(windowStart, windowStart + MAX_VISIBLE);
221
+ const visibleOffset = windowStart;
222
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Filter:" }), filterMode ? (_jsxs(Text, { color: "yellow", children: ["/", filterText, _jsx(Text, { inverse: true, children: " " })] })) : filterText ? (_jsxs(Text, { color: "green", children: ["/", filterText] })) : (_jsx(Text, { dimColor: true, children: "(press / to filter by keyword)" })), _jsxs(Text, { dimColor: true, children: [" [", filtered.length, "/", entries.length, "]"] })] }), _jsx(Text, { children: " " }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: pad('', 2) }), _jsx(Text, { dimColor: true, bold: true, children: pad('Title', 30) }), _jsx(Text, { dimColor: true, bold: true, children: pad('Category', 12) }), _jsx(Text, { dimColor: true, bold: true, children: "Keywords" })] }), visibleEntries.map((e, i) => {
223
+ const realIdx = visibleOffset + i;
224
+ const isCurrent = realIdx === cursor;
225
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: C.primary, children: isCurrent ? SYM.cursor : SYM.cursorBlank }), _jsx(Text, { color: "green", children: "*" }), _jsx(Text, { bold: isCurrent, children: pad(truncate(e.title, 28), 29) }), _jsx(Text, { dimColor: !isCurrent, color: "yellow", children: pad(e.category, 12) }), _jsx(Text, { dimColor: true, children: truncate(e.keywords.join(', '), 40) })] }, `${e.category}-${e.title}-${realIdx}`));
226
+ }), filtered.length > MAX_VISIBLE && (_jsxs(Text, { dimColor: true, children: [" ... ", filtered.length - MAX_VISIBLE, " more (scroll with arrows)"] }))] }), selected && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Box, { ...BORDER.detail, flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: selected.title }), _jsxs(Text, { dimColor: true, children: ["[", selected.category, "] ", selected.keywords.join(', ')] }), _jsx(Text, { children: " " }), _jsx(Text, { children: truncate(selected.content.replace(/^###\s+.+\n*/m, '').trim(), 300) })] })] })), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: [" ", '\u2191', "/", '\u2193', " navigate [/] filter [esc] clear filter"] })] }));
73
227
  }
228
+ // ---------------------------------------------------------------------------
229
+ // Preview mode — injection preview for agent types
230
+ // ---------------------------------------------------------------------------
231
+ function PreviewMode({ workDir }) {
232
+ const [agentIdx, setAgentIdx] = useState(0);
233
+ const [result, setResult] = useState(null);
234
+ const [loading, setLoading] = useState(false);
235
+ const agentType = AGENT_TYPES[agentIdx];
236
+ useEffect(() => {
237
+ runPreview();
238
+ }, [agentIdx]);
239
+ async function runPreview() {
240
+ setLoading(true);
241
+ try {
242
+ const { evaluateSpecInjection } = await import('../../hooks/spec-injector.js');
243
+ const { loadSpecInjectionConfig } = await import('../../config/index.js');
244
+ const config = loadSpecInjectionConfig(workDir);
245
+ const res = evaluateSpecInjection(agentType, workDir, undefined, config);
246
+ setResult(res);
247
+ }
248
+ catch {
249
+ setResult({ inject: false });
250
+ }
251
+ setLoading(false);
252
+ }
253
+ useInput((_input, key) => {
254
+ if (key.leftArrow) {
255
+ setAgentIdx(i => (i > 0 ? i - 1 : AGENT_TYPES.length - 1));
256
+ }
257
+ if (key.rightArrow) {
258
+ setAgentIdx(i => (i < AGENT_TYPES.length - 1 ? i + 1 : 0));
259
+ }
260
+ });
261
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: '\u2190' }), _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` ${agentType} ` }), _jsx(Text, { dimColor: true, children: '\u2192' }), _jsxs(Text, { dimColor: true, children: [" (", agentIdx + 1, "/", AGENT_TYPES.length, ")"] })] }), _jsx(Text, { children: " " }), loading ? (_jsx(Text, { dimColor: true, children: "Evaluating injection..." })) : result ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Inject:" }), result.inject
262
+ ? _jsx(Text, { bold: true, color: "green", children: "yes" })
263
+ : _jsx(Text, { bold: true, color: "red", children: "no" })] }), result.inject && (_jsxs(_Fragment, { children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Categories:" }), _jsx(Text, { color: "yellow", children: result.categories?.join(', ') ?? '-' })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Matched entries:" }), _jsx(Text, { children: result.specCount ?? 0 })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Content size:" }), _jsx(Text, { children: formatSize(result.content?.length ?? 0) })] }), result.budgetAction && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Budget action:" }), _jsx(Text, { color: result.budgetAction === 'skip' ? 'red' : 'yellow', children: result.budgetAction })] }))] })), result.inject && result.content && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Box, { ...BORDER.detail, flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, dimColor: true, children: "Content preview (first 500 chars):" }), _jsx(Text, { children: truncate(result.content, 500) })] })] }))] })) : (_jsx(Text, { dimColor: true, children: "No result." })), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: [" ", '\u2190', "/", '\u2192', " select agent type"] })] }));
264
+ }
265
+ // ---------------------------------------------------------------------------
266
+ // Config mode — interactive spec injection config editor
267
+ // ---------------------------------------------------------------------------
268
+ const ALL_CATEGORIES = ['coding', 'arch', 'debug', 'test', 'review', 'learning', 'ui'];
269
+ const CONFIG_SECTIONS = [
270
+ { key: '1', id: 'agents', label: 'Agent Mappings' },
271
+ { key: '2', id: 'catdocs', label: 'Category Docs' },
272
+ { key: '3', id: 'always', label: 'Always Inject' },
273
+ { key: '4', id: 'filters', label: 'Global Filters' },
274
+ { key: '5', id: 'preview', label: 'Preview' },
275
+ ];
276
+ function ConfigMode({ workDir }) {
277
+ const [config, setConfig] = useState({});
278
+ const [loading, setLoading] = useState(true);
279
+ const [section, setSection] = useState('agents');
280
+ const [statusMsg, setStatusMsg] = useState('');
281
+ useEffect(() => {
282
+ loadConfig();
283
+ }, []);
284
+ async function loadConfig() {
285
+ try {
286
+ const { loadSpecInjectionConfig } = await import('../../config/index.js');
287
+ const c = loadSpecInjectionConfig(workDir);
288
+ setConfig(c);
289
+ }
290
+ catch {
291
+ setConfig({});
292
+ }
293
+ setLoading(false);
294
+ }
295
+ async function persistConfig(next) {
296
+ setConfig(next);
297
+ try {
298
+ const { saveSpecInjectionConfig } = await import('../../config/index.js');
299
+ saveSpecInjectionConfig(workDir, next);
300
+ setStatusMsg('Saved');
301
+ }
302
+ catch {
303
+ setStatusMsg('Save failed');
304
+ }
305
+ setTimeout(() => setStatusMsg(''), 2000);
306
+ }
307
+ if (loading) {
308
+ return _jsx(Text, { dimColor: true, children: "Loading config..." });
309
+ }
310
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { gap: 1, children: CONFIG_SECTIONS.map(s => (_jsx(Box, { children: section === s.id
311
+ ? _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` [${s.key}]${s.label} ` })
312
+ : _jsx(Text, { dimColor: true, children: ` [${s.key}]${s.label} ` }) }, s.id))) }), _jsx(Text, { children: " " }), section === 'agents' && (_jsx(AgentMappingsSection, { config: config, onChange: persistConfig, onStatus: setStatusMsg })), section === 'catdocs' && (_jsx(CategoryDocsSection, { config: config, onChange: persistConfig, onStatus: setStatusMsg })), section === 'always' && (_jsx(AlwaysInjectSection, { config: config, onChange: persistConfig, onStatus: setStatusMsg })), section === 'filters' && (_jsx(GlobalFiltersSection, { config: config, onChange: persistConfig, onStatus: setStatusMsg })), section === 'preview' && (_jsx(ConfigPreviewSection, { workDir: workDir, config: config })), _jsx(Text, { children: " " }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: " [1-5] section" }), statusMsg ? _jsxs(Text, { color: "green", children: [" | ", statusMsg] }) : null] }), _jsx(ConfigSectionSwitcher, { section: section, onSwitch: setSection })] }));
313
+ }
314
+ /** Invisible component that handles 1-5 section switching at ConfigMode level. */
315
+ function ConfigSectionSwitcher({ section: _section, onSwitch, }) {
316
+ useInput((input, _key) => {
317
+ for (const s of CONFIG_SECTIONS) {
318
+ if (input === s.key) {
319
+ onSwitch(s.id);
320
+ return;
321
+ }
322
+ }
323
+ });
324
+ return null;
325
+ }
326
+ // ---------------------------------------------------------------------------
327
+ // Section 1: Agent Mappings
328
+ // ---------------------------------------------------------------------------
329
+ function AgentMappingsSection({ config, onChange, onStatus, }) {
330
+ const mapping = config.mapping ?? {};
331
+ const agentNames = Object.keys(mapping);
332
+ const [cursor, setCursor] = useState(0);
333
+ const [expanded, setExpanded] = useState(null);
334
+ const [subCursor, setSubCursor] = useState(0);
335
+ const [inputMode, setInputMode] = useState('none');
336
+ const [inputBuf, setInputBuf] = useState('');
337
+ // For add-agent: pick from AGENT_TYPES list
338
+ const [addAgentCursor, setAddAgentCursor] = useState(0);
339
+ // Sub-editor rows for expanded agent
340
+ function getSubRows(name) {
341
+ const m = mapping[name];
342
+ if (!m)
343
+ return [];
344
+ const rows = [];
345
+ // Category toggles header
346
+ rows.push('__categories__');
347
+ // Include keywords
348
+ const incl = m.includeKeywords ?? [];
349
+ rows.push('__include_header__');
350
+ for (const kw of incl)
351
+ rows.push(`inc:${kw}`);
352
+ rows.push('__include_add__');
353
+ // Exclude keywords
354
+ const excl = m.excludeKeywords ?? [];
355
+ rows.push('__exclude_header__');
356
+ for (const kw of excl)
357
+ rows.push(`exc:${kw}`);
358
+ rows.push('__exclude_add__');
359
+ // Extras
360
+ const extras = m.extras ?? [];
361
+ rows.push('__extras_header__');
362
+ for (const p of extras)
363
+ rows.push(`ext:${p}`);
364
+ rows.push('__extras_add__');
365
+ return rows;
366
+ }
367
+ useInput((input, key) => {
368
+ // --- Add agent mode: pick from AGENT_TYPES ---
369
+ if (inputMode === 'add-agent') {
370
+ const available = AGENT_TYPES.filter(a => !mapping[a]);
371
+ if (key.escape) {
372
+ setInputMode('none');
373
+ return;
374
+ }
375
+ if (key.upArrow) {
376
+ setAddAgentCursor(c => Math.max(0, c - 1));
377
+ return;
378
+ }
379
+ if (key.downArrow) {
380
+ setAddAgentCursor(c => Math.min(available.length - 1, c + 1));
381
+ return;
382
+ }
383
+ if (key.return && available.length > 0) {
384
+ const chosen = available[addAgentCursor];
385
+ const next = { ...config, mapping: { ...mapping, [chosen]: { categories: [] } } };
386
+ onChange(next);
387
+ onStatus(`Added ${chosen}`);
388
+ setInputMode('none');
389
+ }
390
+ return;
391
+ }
392
+ // --- Text input modes ---
393
+ if (inputMode === 'add-include' || inputMode === 'add-exclude' || inputMode === 'add-extra') {
394
+ if (key.escape) {
395
+ setInputMode('none');
396
+ setInputBuf('');
397
+ return;
398
+ }
399
+ if (key.return && inputBuf.trim() && expanded) {
400
+ const m = { ...(mapping[expanded] ?? { categories: [] }) };
401
+ if (inputMode === 'add-include') {
402
+ m.includeKeywords = [...(m.includeKeywords ?? []), inputBuf.trim()];
403
+ }
404
+ else if (inputMode === 'add-exclude') {
405
+ m.excludeKeywords = [...(m.excludeKeywords ?? []), inputBuf.trim()];
406
+ }
407
+ else {
408
+ m.extras = [...(m.extras ?? []), inputBuf.trim()];
409
+ }
410
+ onChange({ ...config, mapping: { ...mapping, [expanded]: m } });
411
+ setInputBuf('');
412
+ setInputMode('none');
413
+ return;
414
+ }
415
+ if (key.backspace || key.delete) {
416
+ setInputBuf(b => b.slice(0, -1));
417
+ return;
418
+ }
419
+ if (input && !key.ctrl && !key.meta) {
420
+ setInputBuf(b => b + input);
421
+ return;
422
+ }
423
+ return;
424
+ }
425
+ // --- Expanded sub-editor ---
426
+ if (expanded) {
427
+ const rows = getSubRows(expanded);
428
+ if (key.escape) {
429
+ setExpanded(null);
430
+ setSubCursor(0);
431
+ return;
432
+ }
433
+ if (key.upArrow) {
434
+ setSubCursor(c => Math.max(0, c - 1));
435
+ return;
436
+ }
437
+ if (key.downArrow) {
438
+ setSubCursor(c => Math.min(rows.length - 1, c + 1));
439
+ return;
440
+ }
441
+ const currentRow = rows[subCursor];
442
+ // Toggle category
443
+ if (currentRow === '__categories__' && input === ' ') {
444
+ // Noop on header; actual toggling not here
445
+ }
446
+ // Space on categories row: open a toggle cycle through categories
447
+ if (currentRow === '__categories__') {
448
+ if (key.return) {
449
+ // Not used here; categories shown inline
450
+ }
451
+ }
452
+ // Handle [+] add rows
453
+ if (key.return) {
454
+ if (currentRow === '__include_add__') {
455
+ setInputMode('add-include');
456
+ setInputBuf('');
457
+ return;
458
+ }
459
+ if (currentRow === '__exclude_add__') {
460
+ setInputMode('add-exclude');
461
+ setInputBuf('');
462
+ return;
463
+ }
464
+ if (currentRow === '__extras_add__') {
465
+ setInputMode('add-extra');
466
+ setInputBuf('');
467
+ return;
468
+ }
469
+ }
470
+ // Space toggles on categories row
471
+ if (currentRow === '__categories__' && input) {
472
+ const catIdx = parseInt(input, 10);
473
+ if (catIdx >= 1 && catIdx <= ALL_CATEGORIES.length) {
474
+ const cat = ALL_CATEGORIES[catIdx - 1];
475
+ const m = { ...(mapping[expanded] ?? { categories: [] }) };
476
+ const cats = [...m.categories];
477
+ const idx = cats.indexOf(cat);
478
+ if (idx >= 0)
479
+ cats.splice(idx, 1);
480
+ else
481
+ cats.push(cat);
482
+ m.categories = cats;
483
+ onChange({ ...config, mapping: { ...mapping, [expanded]: m } });
484
+ return;
485
+ }
486
+ }
487
+ // [d] delete on keyword/extra items
488
+ if (input === 'd') {
489
+ if (currentRow?.startsWith('inc:')) {
490
+ const kw = currentRow.slice(4);
491
+ const m = { ...(mapping[expanded] ?? { categories: [] }) };
492
+ m.includeKeywords = (m.includeKeywords ?? []).filter(k => k !== kw);
493
+ onChange({ ...config, mapping: { ...mapping, [expanded]: m } });
494
+ setSubCursor(c => Math.max(0, c - 1));
495
+ return;
496
+ }
497
+ if (currentRow?.startsWith('exc:')) {
498
+ const kw = currentRow.slice(4);
499
+ const m = { ...(mapping[expanded] ?? { categories: [] }) };
500
+ m.excludeKeywords = (m.excludeKeywords ?? []).filter(k => k !== kw);
501
+ onChange({ ...config, mapping: { ...mapping, [expanded]: m } });
502
+ setSubCursor(c => Math.max(0, c - 1));
503
+ return;
504
+ }
505
+ if (currentRow?.startsWith('ext:')) {
506
+ const p = currentRow.slice(4);
507
+ const m = { ...(mapping[expanded] ?? { categories: [] }) };
508
+ m.extras = (m.extras ?? []).filter(e => e !== p);
509
+ onChange({ ...config, mapping: { ...mapping, [expanded]: m } });
510
+ setSubCursor(c => Math.max(0, c - 1));
511
+ return;
512
+ }
513
+ }
514
+ return;
515
+ }
516
+ // --- Top-level agent list ---
517
+ if (key.upArrow) {
518
+ setCursor(c => Math.max(0, c - 1));
519
+ return;
520
+ }
521
+ if (key.downArrow) {
522
+ setCursor(c => Math.min(agentNames.length - 1, c + 1));
523
+ return;
524
+ }
525
+ if (key.return && agentNames[cursor]) {
526
+ setExpanded(agentNames[cursor]);
527
+ setSubCursor(0);
528
+ return;
529
+ }
530
+ if (input === 'a') {
531
+ setInputMode('add-agent');
532
+ setAddAgentCursor(0);
533
+ return;
534
+ }
535
+ if (input === 'd' && agentNames[cursor]) {
536
+ const name = agentNames[cursor];
537
+ const next = { ...mapping };
538
+ delete next[name];
539
+ onChange({ ...config, mapping: next });
540
+ setCursor(c => Math.max(0, c - 1));
541
+ onStatus(`Deleted ${name}`);
542
+ return;
543
+ }
544
+ });
545
+ // --- Add agent picker overlay ---
546
+ if (inputMode === 'add-agent') {
547
+ const available = AGENT_TYPES.filter(a => !mapping[a]);
548
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "Select agent type to add:" }), available.length === 0 ? (_jsx(Text, { dimColor: true, children: "All agent types already mapped." })) : (available.map((a, i) => (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: i === addAgentCursor ? '> ' : ' ' }), _jsx(Text, { bold: i === addAgentCursor, children: a })] }, a)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Enter to select, Esc to cancel" })] }));
549
+ }
550
+ // --- Text input overlay ---
551
+ if (inputMode === 'add-include' || inputMode === 'add-exclude' || inputMode === 'add-extra') {
552
+ const label = inputMode === 'add-include' ? 'include keyword'
553
+ : inputMode === 'add-exclude' ? 'exclude keyword'
554
+ : 'extra doc path';
555
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, color: "cyan", children: ["Add ", label, ":"] }), _jsxs(Text, { color: "yellow", children: [inputBuf, _jsx(Text, { inverse: true, children: " " })] })] }), _jsx(Text, { dimColor: true, children: " Enter to confirm, Esc to cancel" })] }));
556
+ }
557
+ // --- Expanded agent sub-editor ---
558
+ if (expanded) {
559
+ const m = mapping[expanded] ?? { categories: [] };
560
+ const rows = getSubRows(expanded);
561
+ const incl = m.includeKeywords ?? [];
562
+ const excl = m.excludeKeywords ?? [];
563
+ const extras = m.extras ?? [];
564
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: expanded }), _jsx(Text, { children: " " }), rows.map((row, i) => {
565
+ const isCur = i === subCursor;
566
+ const prefix = isCur ? '> ' : ' ';
567
+ if (row === '__categories__') {
568
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: isCur, children: [prefix, "Categories (press 1-7 to toggle):"] }), _jsx(Box, { paddingLeft: 4, gap: 1, flexWrap: "wrap", children: ALL_CATEGORIES.map((cat, ci) => {
569
+ const on = m.categories.includes(cat);
570
+ return (_jsx(Text, { color: on ? 'green' : 'gray', children: `[${on ? 'x' : ' '}]${ci + 1}:${cat}` }, cat));
571
+ }) })] }, "cats"));
572
+ }
573
+ if (row === '__include_header__') {
574
+ return _jsxs(Text, { dimColor: true, children: [prefix, "Include keywords (", incl.length, "):"] }, "inh");
575
+ }
576
+ if (row === '__exclude_header__') {
577
+ return _jsxs(Text, { dimColor: true, children: [prefix, "Exclude keywords (", excl.length, "):"] }, "exh");
578
+ }
579
+ if (row === '__extras_header__') {
580
+ return _jsxs(Text, { dimColor: true, children: [prefix, "Extras (", extras.length, "):"] }, "exth");
581
+ }
582
+ if (row === '__include_add__' || row === '__exclude_add__' || row === '__extras_add__') {
583
+ return _jsxs(Text, { color: isCur ? 'yellow' : 'gray', children: [prefix, "[+] add"] }, row);
584
+ }
585
+ if (row.startsWith('inc:')) {
586
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: prefix }), _jsx(Text, { color: "green", bold: isCur, children: row.slice(4) }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `i-${row}`));
587
+ }
588
+ if (row.startsWith('exc:')) {
589
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: prefix }), _jsx(Text, { color: "red", bold: isCur, children: row.slice(4) }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `e-${row}`));
590
+ }
591
+ if (row.startsWith('ext:')) {
592
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: prefix }), _jsx(Text, { bold: isCur, children: row.slice(4) }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `x-${row}`));
593
+ }
594
+ return null;
595
+ }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Esc back | Enter expand | 1-7 toggle cat | d delete" })] }));
596
+ }
597
+ // --- Agent list ---
598
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Agent Mappings" }), _jsx(Text, { children: " " }), agentNames.length === 0 ? (_jsx(Text, { dimColor: true, children: "No agent mappings configured. Press [a] to add." })) : (agentNames.map((name, i) => {
599
+ const m = mapping[name];
600
+ const isCur = i === cursor;
601
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: C.primary, children: isCur ? SYM.cursor : SYM.cursorBlank }), _jsx(Text, { bold: isCur, children: pad(name, 25) }), _jsx(Box, { gap: 1, children: m.categories.map(c => (_jsx(Text, { color: "yellow", children: c }, c))) }), _jsx(Text, { dimColor: true, children: ` kw:${(m.includeKeywords?.length ?? 0) + (m.excludeKeywords?.length ?? 0)}` })] }, name));
602
+ })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Enter expand | [a]dd | [d]el | Up/Down navigate" })] }));
603
+ }
604
+ // ---------------------------------------------------------------------------
605
+ // Section 2: Category Docs
606
+ // ---------------------------------------------------------------------------
607
+ function CategoryDocsSection({ config, onChange, onStatus, }) {
608
+ const catDocs = config.categoryDocs ?? {};
609
+ const [cursor, setCursor] = useState(0);
610
+ const [expanded, setExpanded] = useState(null);
611
+ const [subCursor, setSubCursor] = useState(0);
612
+ const [inputMode, setInputMode] = useState('none');
613
+ const [inputBuf, setInputBuf] = useState('');
614
+ function getSubRows(cat) {
615
+ const cd = catDocs[cat];
616
+ const rows = [];
617
+ rows.push('__specs_header__');
618
+ for (const f of cd?.specFiles ?? [])
619
+ rows.push(`spec:${f}`);
620
+ rows.push('__specs_add__');
621
+ rows.push('__docs_header__');
622
+ for (const d of cd?.docs ?? [])
623
+ rows.push(`doc:${d}`);
624
+ rows.push('__docs_add__');
625
+ return rows;
626
+ }
627
+ useInput((input, key) => {
628
+ // Text input
629
+ if (inputMode === 'add-spec' || inputMode === 'add-doc') {
630
+ if (key.escape) {
631
+ setInputMode('none');
632
+ setInputBuf('');
633
+ return;
634
+ }
635
+ if (key.return && inputBuf.trim() && expanded) {
636
+ const cd = { ...(catDocs[expanded] ?? {}) };
637
+ if (inputMode === 'add-spec') {
638
+ cd.specFiles = [...(cd.specFiles ?? []), inputBuf.trim()];
639
+ }
640
+ else {
641
+ cd.docs = [...(cd.docs ?? []), inputBuf.trim()];
642
+ }
643
+ onChange({ ...config, categoryDocs: { ...catDocs, [expanded]: cd } });
644
+ setInputBuf('');
645
+ setInputMode('none');
646
+ return;
647
+ }
648
+ if (key.backspace || key.delete) {
649
+ setInputBuf(b => b.slice(0, -1));
650
+ return;
651
+ }
652
+ if (input && !key.ctrl && !key.meta) {
653
+ setInputBuf(b => b + input);
654
+ return;
655
+ }
656
+ return;
657
+ }
658
+ // Expanded sub-editor
659
+ if (expanded) {
660
+ const rows = getSubRows(expanded);
661
+ if (key.escape) {
662
+ setExpanded(null);
663
+ setSubCursor(0);
664
+ return;
665
+ }
666
+ if (key.upArrow) {
667
+ setSubCursor(c => Math.max(0, c - 1));
668
+ return;
669
+ }
670
+ if (key.downArrow) {
671
+ setSubCursor(c => Math.min(rows.length - 1, c + 1));
672
+ return;
673
+ }
674
+ const currentRow = rows[subCursor];
675
+ if (key.return) {
676
+ if (currentRow === '__specs_add__') {
677
+ setInputMode('add-spec');
678
+ setInputBuf('');
679
+ return;
680
+ }
681
+ if (currentRow === '__docs_add__') {
682
+ setInputMode('add-doc');
683
+ setInputBuf('');
684
+ return;
685
+ }
686
+ }
687
+ if (input === 'd') {
688
+ if (currentRow?.startsWith('spec:')) {
689
+ const f = currentRow.slice(5);
690
+ const cd = { ...(catDocs[expanded] ?? {}) };
691
+ cd.specFiles = (cd.specFiles ?? []).filter(s => s !== f);
692
+ onChange({ ...config, categoryDocs: { ...catDocs, [expanded]: cd } });
693
+ setSubCursor(c => Math.max(0, c - 1));
694
+ return;
695
+ }
696
+ if (currentRow?.startsWith('doc:')) {
697
+ const d = currentRow.slice(4);
698
+ const cd = { ...(catDocs[expanded] ?? {}) };
699
+ cd.docs = (cd.docs ?? []).filter(x => x !== d);
700
+ onChange({ ...config, categoryDocs: { ...catDocs, [expanded]: cd } });
701
+ setSubCursor(c => Math.max(0, c - 1));
702
+ return;
703
+ }
704
+ }
705
+ return;
706
+ }
707
+ // Top-level category list
708
+ if (key.upArrow) {
709
+ setCursor(c => Math.max(0, c - 1));
710
+ return;
711
+ }
712
+ if (key.downArrow) {
713
+ setCursor(c => Math.min(ALL_CATEGORIES.length - 1, c + 1));
714
+ return;
715
+ }
716
+ if (key.return) {
717
+ const cat = ALL_CATEGORIES[cursor];
718
+ setExpanded(cat);
719
+ setSubCursor(0);
720
+ return;
721
+ }
722
+ if (input === 'd' && ALL_CATEGORIES[cursor]) {
723
+ const cat = ALL_CATEGORIES[cursor];
724
+ if (catDocs[cat]) {
725
+ const next = { ...catDocs };
726
+ delete next[cat];
727
+ onChange({ ...config, categoryDocs: next });
728
+ onStatus(`Cleared docs for ${cat}`);
729
+ }
730
+ return;
731
+ }
732
+ });
733
+ // Text input overlay
734
+ if (inputMode === 'add-spec' || inputMode === 'add-doc') {
735
+ const label = inputMode === 'add-spec' ? 'spec file' : 'doc path';
736
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, color: "cyan", children: ["Add ", label, " to ", expanded, ":"] }), _jsxs(Text, { color: "yellow", children: [inputBuf, _jsx(Text, { inverse: true, children: " " })] })] }), _jsx(Text, { dimColor: true, children: " Enter to confirm, Esc to cancel" })] }));
737
+ }
738
+ // Expanded sub-editor
739
+ if (expanded) {
740
+ const cd = catDocs[expanded] ?? {};
741
+ const rows = getSubRows(expanded);
742
+ const specs = cd.specFiles ?? [];
743
+ const docs = cd.docs ?? [];
744
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: expanded }), _jsx(Text, { children: " " }), rows.map((row, i) => {
745
+ const isCur = i === subCursor;
746
+ const prefix = isCur ? '> ' : ' ';
747
+ if (row === '__specs_header__') {
748
+ return _jsxs(Text, { dimColor: true, children: [prefix, "Spec files (", specs.length, "):"] }, "sh");
749
+ }
750
+ if (row === '__docs_header__') {
751
+ return _jsxs(Text, { dimColor: true, children: [prefix, "Doc paths (", docs.length, "):"] }, "dh");
752
+ }
753
+ if (row === '__specs_add__' || row === '__docs_add__') {
754
+ return _jsxs(Text, { color: isCur ? 'yellow' : 'gray', children: [prefix, "[+] add"] }, row);
755
+ }
756
+ if (row.startsWith('spec:')) {
757
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: prefix }), _jsx(Text, { bold: isCur, children: row.slice(5) }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `s-${row}`));
758
+ }
759
+ if (row.startsWith('doc:')) {
760
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: prefix }), _jsx(Text, { bold: isCur, children: row.slice(4) }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `d-${row}`));
761
+ }
762
+ return null;
763
+ }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Esc back | Enter add | d delete" })] }));
764
+ }
765
+ // Category list
766
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Category Docs" }), _jsx(Text, { children: " " }), ALL_CATEGORIES.map((cat, i) => {
767
+ const isCur = i === cursor;
768
+ const cd = catDocs[cat];
769
+ const hasData = cd && ((cd.specFiles?.length ?? 0) + (cd.docs?.length ?? 0) > 0);
770
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: C.primary, children: isCur ? SYM.cursor : SYM.cursorBlank }), _jsx(Text, { bold: isCur, color: hasData ? 'green' : undefined, children: pad(cat, 12) }), hasData ? (_jsxs(Text, { dimColor: true, children: ["specs:", cd.specFiles?.length ?? 0, " docs:", cd.docs?.length ?? 0] })) : (_jsx(Text, { dimColor: true, children: "-" }))] }, cat));
771
+ }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Enter expand | [d]el docs | Up/Down navigate" })] }));
772
+ }
773
+ // ---------------------------------------------------------------------------
774
+ // Section 3: Always Inject
775
+ // ---------------------------------------------------------------------------
776
+ function AlwaysInjectSection({ config, onChange, onStatus: _onStatus, }) {
777
+ const always = config.always ?? {};
778
+ const TABS = ['docs', 'keywords', 'categories'];
779
+ const [tab, setTab] = useState('docs');
780
+ const [cursor, setCursor] = useState(0);
781
+ const [inputMode, setInputMode] = useState(false);
782
+ const [inputBuf, setInputBuf] = useState('');
783
+ const currentList = always[tab] ?? [];
784
+ useInput((input, key) => {
785
+ if (inputMode) {
786
+ if (key.escape) {
787
+ setInputMode(false);
788
+ setInputBuf('');
789
+ return;
790
+ }
791
+ if (key.return && inputBuf.trim()) {
792
+ const updated = { ...always, [tab]: [...currentList, inputBuf.trim()] };
793
+ onChange({ ...config, always: updated });
794
+ setInputBuf('');
795
+ setInputMode(false);
796
+ return;
797
+ }
798
+ if (key.backspace || key.delete) {
799
+ setInputBuf(b => b.slice(0, -1));
800
+ return;
801
+ }
802
+ if (input && !key.ctrl && !key.meta) {
803
+ setInputBuf(b => b + input);
804
+ return;
805
+ }
806
+ return;
807
+ }
808
+ if (key.tab) {
809
+ const idx = TABS.indexOf(tab);
810
+ setTab(TABS[(idx + 1) % TABS.length]);
811
+ setCursor(0);
812
+ return;
813
+ }
814
+ if (key.upArrow) {
815
+ setCursor(c => Math.max(0, c - 1));
816
+ return;
817
+ }
818
+ if (key.downArrow) {
819
+ setCursor(c => Math.min(currentList.length - 1, c + 1));
820
+ return;
821
+ }
822
+ if (input === 'a') {
823
+ setInputMode(true);
824
+ setInputBuf('');
825
+ return;
826
+ }
827
+ if (input === 'd' && currentList[cursor]) {
828
+ const next = currentList.filter((_, i) => i !== cursor);
829
+ const updated = { ...always, [tab]: next.length > 0 ? next : undefined };
830
+ onChange({ ...config, always: updated });
831
+ setCursor(c => Math.max(0, c - 1));
832
+ return;
833
+ }
834
+ });
835
+ if (inputMode) {
836
+ const hint = tab === 'docs' ? 'doc path' : tab === 'keywords' ? 'keyword' : 'category';
837
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, color: "cyan", children: ["Add ", hint, ":"] }), _jsxs(Text, { color: "yellow", children: [inputBuf, _jsx(Text, { inverse: true, children: " " })] })] }), _jsx(Text, { dimColor: true, children: " Enter to confirm, Esc to cancel" })] }));
838
+ }
839
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Always Inject (session start)" }), _jsx(Text, { children: " " }), _jsxs(Box, { gap: 1, children: [TABS.map(t => (_jsx(Box, { children: t === tab
840
+ ? _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` ${t} ` })
841
+ : _jsx(Text, { dimColor: true, children: ` ${t} ` }) }, t))), _jsx(Text, { dimColor: true, children: "(Tab to switch)" })] }), _jsx(Text, { children: " " }), currentList.length === 0 ? (_jsxs(Text, { dimColor: true, children: ["No ", tab, " configured. Press [a] to add."] })) : (currentList.map((p, i) => {
842
+ const isCur = i === cursor;
843
+ const color = tab === 'keywords' ? 'green' : tab === 'categories' ? 'yellow' : undefined;
844
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: C.primary, children: isCur ? SYM.cursor : SYM.cursorBlank }), _jsx(Text, { bold: isCur, color: color, children: p }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `${p}-${i}`));
845
+ })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " [a]dd | [d]el | Tab switch | Up/Down navigate" })] }));
846
+ }
847
+ // ---------------------------------------------------------------------------
848
+ // Section 4: Global Filters
849
+ // ---------------------------------------------------------------------------
850
+ function GlobalFiltersSection({ config, onChange, onStatus: _onStatus, }) {
851
+ const filters = config.keywordFilters ?? {};
852
+ const includeKw = filters.include ?? [];
853
+ const excludeKw = filters.exclude ?? [];
854
+ const [activeList, setActiveList] = useState('include');
855
+ const [cursor, setCursor] = useState(0);
856
+ const [inputMode, setInputMode] = useState(false);
857
+ const [inputBuf, setInputBuf] = useState('');
858
+ const currentList = activeList === 'include' ? includeKw : excludeKw;
859
+ useInput((input, key) => {
860
+ if (inputMode) {
861
+ if (key.escape) {
862
+ setInputMode(false);
863
+ setInputBuf('');
864
+ return;
865
+ }
866
+ if (key.return && inputBuf.trim()) {
867
+ const f = { ...filters };
868
+ if (activeList === 'include') {
869
+ f.include = [...includeKw, inputBuf.trim()];
870
+ }
871
+ else {
872
+ f.exclude = [...excludeKw, inputBuf.trim()];
873
+ }
874
+ onChange({ ...config, keywordFilters: f });
875
+ setInputBuf('');
876
+ setInputMode(false);
877
+ return;
878
+ }
879
+ if (key.backspace || key.delete) {
880
+ setInputBuf(b => b.slice(0, -1));
881
+ return;
882
+ }
883
+ if (input && !key.ctrl && !key.meta) {
884
+ setInputBuf(b => b + input);
885
+ return;
886
+ }
887
+ return;
888
+ }
889
+ if (key.tab) {
890
+ setActiveList(l => l === 'include' ? 'exclude' : 'include');
891
+ setCursor(0);
892
+ return;
893
+ }
894
+ if (key.upArrow) {
895
+ setCursor(c => Math.max(0, c - 1));
896
+ return;
897
+ }
898
+ if (key.downArrow) {
899
+ setCursor(c => Math.min(currentList.length - 1, c + 1));
900
+ return;
901
+ }
902
+ if (input === 'a') {
903
+ setInputMode(true);
904
+ setInputBuf('');
905
+ return;
906
+ }
907
+ if (input === 'd' && currentList[cursor]) {
908
+ const f = { ...filters };
909
+ if (activeList === 'include') {
910
+ f.include = includeKw.filter((_, i) => i !== cursor);
911
+ }
912
+ else {
913
+ f.exclude = excludeKw.filter((_, i) => i !== cursor);
914
+ }
915
+ onChange({ ...config, keywordFilters: f });
916
+ setCursor(c => Math.max(0, c - 1));
917
+ return;
918
+ }
919
+ });
920
+ if (inputMode) {
921
+ const label = activeList === 'include' ? 'include keyword' : 'exclude keyword';
922
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsxs(Text, { bold: true, color: "cyan", children: ["Add ", label, ":"] }), _jsxs(Text, { color: "yellow", children: [inputBuf, _jsx(Text, { inverse: true, children: " " })] })] }), _jsx(Text, { dimColor: true, children: " Enter to confirm, Esc to cancel" })] }));
923
+ }
924
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Global Filters" }), _jsx(Text, { children: " " }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: activeList === 'include', inverse: activeList === 'include', color: "green", children: ' Include ' }), _jsx(Text, { bold: activeList === 'exclude', inverse: activeList === 'exclude', color: "red", children: ' Exclude ' }), _jsx(Text, { dimColor: true, children: "(Tab to switch)" })] }), _jsx(Text, { children: " " }), currentList.length === 0 ? (_jsxs(Text, { dimColor: true, children: ["No ", activeList, " keywords. Press [a] to add."] })) : (currentList.map((kw, i) => {
925
+ const isCur = i === cursor;
926
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: C.primary, children: isCur ? SYM.cursor : SYM.cursorBlank }), _jsx(Text, { bold: isCur, color: activeList === 'include' ? 'green' : 'red', children: kw }), isCur && _jsx(Text, { dimColor: true, children: " [d]del" })] }, `${kw}-${i}`));
927
+ })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Tab switch | [a]dd | [d]el | Up/Down navigate" })] }));
928
+ }
929
+ // ---------------------------------------------------------------------------
930
+ // Section 5: Config Preview (reuses PreviewMode logic)
931
+ // ---------------------------------------------------------------------------
932
+ function ConfigPreviewSection({ workDir, config, }) {
933
+ const [agentIdx, setAgentIdx] = useState(0);
934
+ const [result, setResult] = useState(null);
935
+ const [loading, setLoading] = useState(false);
936
+ const agentType = AGENT_TYPES[agentIdx];
937
+ useEffect(() => {
938
+ runPreview();
939
+ }, [agentIdx, config]);
940
+ async function runPreview() {
941
+ setLoading(true);
942
+ try {
943
+ const { evaluateSpecInjection } = await import('../../hooks/spec-injector.js');
944
+ const res = evaluateSpecInjection(agentType, workDir, undefined, config);
945
+ setResult(res);
946
+ }
947
+ catch {
948
+ setResult({ inject: false });
949
+ }
950
+ setLoading(false);
951
+ }
952
+ useInput((_input, key) => {
953
+ if (key.leftArrow) {
954
+ setAgentIdx(i => (i > 0 ? i - 1 : AGENT_TYPES.length - 1));
955
+ }
956
+ if (key.rightArrow) {
957
+ setAgentIdx(i => (i < AGENT_TYPES.length - 1 ? i + 1 : 0));
958
+ }
959
+ });
960
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Preview (live from current config)" }), _jsx(Text, { children: " " }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: '\u2190' }), _jsx(Text, { bold: true, inverse: true, color: "cyan", children: ` ${agentType} ` }), _jsx(Text, { dimColor: true, children: '\u2192' }), _jsxs(Text, { dimColor: true, children: [" (", agentIdx + 1, "/", AGENT_TYPES.length, ")"] })] }), _jsx(Text, { children: " " }), loading ? (_jsx(Text, { dimColor: true, children: "Evaluating injection..." })) : result ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Inject:" }), result.inject
961
+ ? _jsx(Text, { bold: true, color: "green", children: "yes" })
962
+ : _jsx(Text, { bold: true, color: "red", children: "no" })] }), result.inject && (_jsxs(_Fragment, { children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Categories:" }), _jsx(Text, { color: "yellow", children: result.categories?.join(', ') ?? '-' })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Matched entries:" }), _jsx(Text, { children: result.specCount ?? 0 })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Content size:" }), _jsx(Text, { children: formatSize(result.content?.length ?? 0) })] }), result.budgetAction && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Budget action:" }), _jsx(Text, { color: result.budgetAction === 'skip' ? 'red' : 'yellow', children: result.budgetAction })] }))] })), result.inject && result.content && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Box, { ...BORDER.detail, flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, dimColor: true, children: "Content preview (first 500 chars):" }), _jsx(Text, { children: truncate(result.content, 500) })] })] }))] })) : (_jsx(Text, { dimColor: true, children: "No result." })), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: [" ", '\u2190', "/", '\u2192', " select agent type"] })] }));
963
+ }
964
+ // ---------------------------------------------------------------------------
965
+ // Utility helpers
966
+ // ---------------------------------------------------------------------------
74
967
  function formatSize(bytes) {
75
968
  if (bytes < 1024)
76
969
  return `${bytes} B`;
77
970
  return `${(bytes / 1024).toFixed(1)} KB`;
78
971
  }
972
+ function truncate(s, maxLen) {
973
+ if (s.length <= maxLen)
974
+ return s;
975
+ return s.slice(0, maxLen - 3) + '...';
976
+ }
79
977
  //# sourceMappingURL=SpecPanel.js.map