pi-agent-flow 1.8.40 → 2.0.1

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 (331) hide show
  1. package/README.md +33 -37
  2. package/agents/audit.md +21 -22
  3. package/agents/build.md +23 -22
  4. package/agents/craft.md +23 -27
  5. package/agents/debug.md +24 -28
  6. package/agents/ideas.md +21 -101
  7. package/agents/scout.md +20 -19
  8. package/dist/batch/batch-bash.d.ts +2 -2
  9. package/dist/batch/batch-bash.d.ts.map +1 -1
  10. package/dist/batch/batch-bash.js +30 -7
  11. package/dist/batch/batch-bash.js.map +1 -1
  12. package/dist/batch/constants.d.ts +31 -5
  13. package/dist/batch/constants.d.ts.map +1 -1
  14. package/dist/batch/constants.js +50 -3
  15. package/dist/batch/constants.js.map +1 -1
  16. package/dist/batch/execute.d.ts +0 -1
  17. package/dist/batch/execute.d.ts.map +1 -1
  18. package/dist/batch/execute.js +210 -6
  19. package/dist/batch/execute.js.map +1 -1
  20. package/dist/batch/fuzzy-edit.d.ts +0 -6
  21. package/dist/batch/fuzzy-edit.d.ts.map +1 -1
  22. package/dist/batch/fuzzy-edit.js +1 -1
  23. package/dist/batch/fuzzy-edit.js.map +1 -1
  24. package/dist/batch/index.d.ts.map +1 -1
  25. package/dist/batch/index.js +87 -16
  26. package/dist/batch/index.js.map +1 -1
  27. package/dist/batch/render.d.ts +0 -1
  28. package/dist/batch/render.d.ts.map +1 -1
  29. package/dist/batch/render.js +7 -101
  30. package/dist/batch/render.js.map +1 -1
  31. package/dist/batch/shell-compress.d.ts +25 -0
  32. package/dist/batch/shell-compress.d.ts.map +1 -0
  33. package/dist/batch/shell-compress.js +602 -0
  34. package/dist/batch/shell-compress.js.map +1 -0
  35. package/dist/batch/summary.d.ts +5 -0
  36. package/dist/batch/summary.d.ts.map +1 -0
  37. package/dist/batch/summary.js +101 -0
  38. package/dist/batch/summary.js.map +1 -0
  39. package/dist/batch/symbols.d.ts.map +1 -1
  40. package/dist/batch/symbols.js +12 -7
  41. package/dist/batch/symbols.js.map +1 -1
  42. package/dist/{config.d.ts → config/config.d.ts} +39 -2
  43. package/dist/config/config.d.ts.map +1 -0
  44. package/dist/{config.js → config/config.js} +220 -9
  45. package/dist/config/config.js.map +1 -0
  46. package/dist/config/log.d.ts +27 -0
  47. package/dist/config/log.d.ts.map +1 -0
  48. package/dist/config/log.js +104 -0
  49. package/dist/config/log.js.map +1 -0
  50. package/dist/config/models.d.ts +2 -0
  51. package/dist/config/models.d.ts.map +1 -0
  52. package/dist/config/models.js +49 -0
  53. package/dist/config/models.js.map +1 -0
  54. package/dist/{settings-resolver.d.ts → config/settings-resolver.d.ts} +9 -2
  55. package/dist/config/settings-resolver.d.ts.map +1 -0
  56. package/dist/config/settings-resolver.js +275 -0
  57. package/dist/config/settings-resolver.js.map +1 -0
  58. package/dist/core/agents.d.ts.map +1 -0
  59. package/dist/{agents.js → core/agents.js} +13 -12
  60. package/dist/core/agents.js.map +1 -0
  61. package/dist/core/delegation.d.ts +24 -0
  62. package/dist/core/delegation.d.ts.map +1 -0
  63. package/dist/core/delegation.js +48 -0
  64. package/dist/core/delegation.js.map +1 -0
  65. package/dist/core/depth.d.ts.map +1 -0
  66. package/dist/{depth.js → core/depth.js} +9 -8
  67. package/dist/core/depth.js.map +1 -0
  68. package/dist/{executor.d.ts → core/executor.d.ts} +18 -3
  69. package/dist/core/executor.d.ts.map +1 -0
  70. package/dist/{executor.js → core/executor.js} +53 -14
  71. package/dist/core/executor.js.map +1 -0
  72. package/dist/{flow.d.ts → core/flow.d.ts} +13 -1
  73. package/dist/core/flow.d.ts.map +1 -0
  74. package/dist/{flow.js → core/flow.js} +125 -64
  75. package/dist/core/flow.js.map +1 -0
  76. package/dist/{session-mode.d.ts → core/session-mode.d.ts} +2 -1
  77. package/dist/core/session-mode.d.ts.map +1 -0
  78. package/dist/{session-mode.js → core/session-mode.js} +2 -1
  79. package/dist/core/session-mode.js.map +1 -0
  80. package/dist/core/session-registry.d.ts +16 -0
  81. package/dist/core/session-registry.d.ts.map +1 -0
  82. package/dist/core/session-registry.js +30 -0
  83. package/dist/core/session-registry.js.map +1 -0
  84. package/dist/core/transitions.d.ts.map +1 -0
  85. package/dist/{transitions.js → core/transitions.js} +1 -1
  86. package/dist/core/transitions.js.map +1 -0
  87. package/dist/flow/auto-warp.d.ts +12 -0
  88. package/dist/flow/auto-warp.d.ts.map +1 -0
  89. package/dist/flow/auto-warp.js +29 -0
  90. package/dist/flow/auto-warp.js.map +1 -0
  91. package/dist/flow/command.d.ts +8 -0
  92. package/dist/flow/command.d.ts.map +1 -0
  93. package/dist/flow/command.js +194 -0
  94. package/dist/flow/command.js.map +1 -0
  95. package/dist/flow/continuation.d.ts +16 -0
  96. package/dist/flow/continuation.d.ts.map +1 -0
  97. package/dist/flow/continuation.js +188 -0
  98. package/dist/flow/continuation.js.map +1 -0
  99. package/dist/flow/index.d.ts +18 -0
  100. package/dist/flow/index.d.ts.map +1 -0
  101. package/dist/flow/index.js +25 -0
  102. package/dist/flow/index.js.map +1 -0
  103. package/dist/flow/loop-command.d.ts +8 -0
  104. package/dist/flow/loop-command.d.ts.map +1 -0
  105. package/dist/flow/loop-command.js +99 -0
  106. package/dist/flow/loop-command.js.map +1 -0
  107. package/dist/flow/loop-templates.d.ts +7 -0
  108. package/dist/flow/loop-templates.d.ts.map +1 -0
  109. package/dist/flow/loop-templates.js +38 -0
  110. package/dist/flow/loop-templates.js.map +1 -0
  111. package/dist/flow/loop.d.ts +19 -0
  112. package/dist/flow/loop.d.ts.map +1 -0
  113. package/dist/flow/loop.js +95 -0
  114. package/dist/flow/loop.js.map +1 -0
  115. package/dist/flow/perform-warp.d.ts +28 -0
  116. package/dist/flow/perform-warp.d.ts.map +1 -0
  117. package/dist/flow/perform-warp.js +127 -0
  118. package/dist/flow/perform-warp.js.map +1 -0
  119. package/dist/flow/settings-command.d.ts +51 -0
  120. package/dist/flow/settings-command.d.ts.map +1 -0
  121. package/dist/flow/settings-command.js +937 -0
  122. package/dist/flow/settings-command.js.map +1 -0
  123. package/dist/flow/store.d.ts +26 -0
  124. package/dist/flow/store.d.ts.map +1 -0
  125. package/dist/flow/store.js +166 -0
  126. package/dist/flow/store.js.map +1 -0
  127. package/dist/flow/template-shared.d.ts +9 -0
  128. package/dist/flow/template-shared.d.ts.map +1 -0
  129. package/dist/flow/template-shared.js +13 -0
  130. package/dist/flow/template-shared.js.map +1 -0
  131. package/dist/flow/template-strings.d.ts +8 -0
  132. package/dist/flow/template-strings.d.ts.map +1 -0
  133. package/dist/flow/template-strings.js +36 -0
  134. package/dist/flow/template-strings.js.map +1 -0
  135. package/dist/flow/types.d.ts +61 -0
  136. package/dist/flow/types.d.ts.map +1 -0
  137. package/dist/flow/types.js +5 -0
  138. package/dist/flow/types.js.map +1 -0
  139. package/dist/flow/warp-command.d.ts +8 -0
  140. package/dist/flow/warp-command.d.ts.map +1 -0
  141. package/dist/flow/warp-command.js +144 -0
  142. package/dist/flow/warp-command.js.map +1 -0
  143. package/dist/flow/warp-utils.d.ts +11 -0
  144. package/dist/flow/warp-utils.d.ts.map +1 -0
  145. package/dist/flow/warp-utils.js +187 -0
  146. package/dist/flow/warp-utils.js.map +1 -0
  147. package/dist/index.d.ts +4 -1
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +115 -31
  150. package/dist/index.js.map +1 -1
  151. package/dist/{notify-state.d.ts → notify/notify-state.d.ts} +2 -1
  152. package/dist/notify/notify-state.d.ts.map +1 -0
  153. package/dist/notify/notify-state.js.map +1 -0
  154. package/dist/notify/notify.d.ts.map +1 -0
  155. package/dist/{notify.js → notify/notify.js} +3 -2
  156. package/dist/notify/notify.js.map +1 -0
  157. package/dist/{cli-args.d.ts → snapshot/cli-args.d.ts} +4 -2
  158. package/dist/snapshot/cli-args.d.ts.map +1 -0
  159. package/dist/{cli-args.js → snapshot/cli-args.js} +13 -1
  160. package/dist/snapshot/cli-args.js.map +1 -0
  161. package/dist/snapshot/index.d.ts +2 -0
  162. package/dist/snapshot/index.d.ts.map +1 -0
  163. package/dist/snapshot/index.js +2 -0
  164. package/dist/snapshot/index.js.map +1 -0
  165. package/dist/{reasoning-strip.d.ts → snapshot/reasoning-strip.d.ts} +0 -4
  166. package/dist/snapshot/reasoning-strip.d.ts.map +1 -0
  167. package/dist/{reasoning-strip.js → snapshot/reasoning-strip.js} +2 -2
  168. package/dist/snapshot/reasoning-strip.js.map +1 -0
  169. package/dist/snapshot/runner-events.d.ts.map +1 -0
  170. package/dist/{runner-events.js → snapshot/runner-events.js} +1 -1
  171. package/dist/snapshot/runner-events.js.map +1 -0
  172. package/dist/{snapshot.d.ts → snapshot/snapshot.d.ts} +24 -18
  173. package/dist/snapshot/snapshot.d.ts.map +1 -0
  174. package/dist/snapshot/snapshot.js +1791 -0
  175. package/dist/snapshot/snapshot.js.map +1 -0
  176. package/dist/{structured-output.d.ts → snapshot/structured-output.d.ts} +1 -1
  177. package/dist/snapshot/structured-output.d.ts.map +1 -0
  178. package/dist/snapshot/structured-output.js.map +1 -0
  179. package/dist/{flow-prompt.d.ts → steering/flow-prompt.d.ts} +2 -2
  180. package/dist/steering/flow-prompt.d.ts.map +1 -0
  181. package/dist/{flow-prompt.js → steering/flow-prompt.js} +1 -1
  182. package/dist/steering/flow-prompt.js.map +1 -0
  183. package/dist/{sliding-prompt.d.ts → steering/sliding-prompt.d.ts} +8 -7
  184. package/dist/steering/sliding-prompt.d.ts.map +1 -0
  185. package/dist/{sliding-prompt.js → steering/sliding-prompt.js} +18 -64
  186. package/dist/steering/sliding-prompt.js.map +1 -0
  187. package/dist/{tool-utils.d.ts → steering/tool-utils.d.ts} +1 -0
  188. package/dist/steering/tool-utils.d.ts.map +1 -0
  189. package/dist/{tool-utils.js → steering/tool-utils.js} +10 -3
  190. package/dist/steering/tool-utils.js.map +1 -0
  191. package/dist/{ask-user.d.ts → tools/ask-user.d.ts} +3 -15
  192. package/dist/tools/ask-user.d.ts.map +1 -0
  193. package/dist/tools/ask-user.js +778 -0
  194. package/dist/tools/ask-user.js.map +1 -0
  195. package/dist/{timed-bash.d.ts → tools/timed-bash.d.ts} +2 -7
  196. package/dist/tools/timed-bash.d.ts.map +1 -0
  197. package/dist/{timed-bash.js → tools/timed-bash.js} +11 -2
  198. package/dist/tools/timed-bash.js.map +1 -0
  199. package/dist/{web-tool.d.ts → tools/web-tool.d.ts} +1 -1
  200. package/dist/tools/web-tool.d.ts.map +1 -0
  201. package/dist/{web-tool.js → tools/web-tool.js} +8 -7
  202. package/dist/tools/web-tool.js.map +1 -0
  203. package/dist/tui/flow-colors.d.ts +55 -0
  204. package/dist/tui/flow-colors.d.ts.map +1 -0
  205. package/dist/tui/flow-colors.js +22 -0
  206. package/dist/tui/flow-colors.js.map +1 -0
  207. package/dist/{render-utils.d.ts → tui/render-utils.d.ts} +6 -2
  208. package/dist/tui/render-utils.d.ts.map +1 -0
  209. package/dist/{render-utils.js → tui/render-utils.js} +40 -12
  210. package/dist/tui/render-utils.js.map +1 -0
  211. package/dist/tui/render.d.ts +21 -0
  212. package/dist/tui/render.d.ts.map +1 -0
  213. package/dist/tui/render.js +786 -0
  214. package/dist/tui/render.js.map +1 -0
  215. package/dist/tui/scramble/algorithm.d.ts +7 -0
  216. package/dist/tui/scramble/algorithm.d.ts.map +1 -0
  217. package/dist/tui/scramble/algorithm.js +227 -0
  218. package/dist/tui/scramble/algorithm.js.map +1 -0
  219. package/dist/tui/scramble/constants.d.ts +99 -0
  220. package/dist/tui/scramble/constants.d.ts.map +1 -0
  221. package/dist/tui/scramble/constants.js +101 -0
  222. package/dist/tui/scramble/constants.js.map +1 -0
  223. package/dist/tui/scramble/index.d.ts +6 -0
  224. package/dist/tui/scramble/index.d.ts.map +1 -0
  225. package/dist/tui/scramble/index.js +6 -0
  226. package/dist/tui/scramble/index.js.map +1 -0
  227. package/dist/tui/scramble/manager.d.ts +44 -0
  228. package/dist/tui/scramble/manager.d.ts.map +1 -0
  229. package/dist/tui/scramble/manager.js +899 -0
  230. package/dist/tui/scramble/manager.js.map +1 -0
  231. package/dist/tui/scramble/utils.d.ts +18 -0
  232. package/dist/tui/scramble/utils.d.ts.map +1 -0
  233. package/dist/tui/scramble/utils.js +145 -0
  234. package/dist/tui/scramble/utils.js.map +1 -0
  235. package/dist/tui/single-select-layout.d.ts +17 -0
  236. package/dist/tui/single-select-layout.d.ts.map +1 -0
  237. package/dist/{single-select-layout.js → tui/single-select-layout.js} +8 -25
  238. package/dist/tui/single-select-layout.js.map +1 -0
  239. package/dist/types/flow.d.ts +112 -0
  240. package/dist/types/flow.d.ts.map +1 -0
  241. package/dist/{types.js → types/flow.js} +3 -54
  242. package/dist/types/flow.js.map +1 -0
  243. package/dist/types/index.d.ts +8 -0
  244. package/dist/types/index.d.ts.map +1 -0
  245. package/dist/types/index.js +7 -0
  246. package/dist/types/index.js.map +1 -0
  247. package/dist/types/output.d.ts +110 -0
  248. package/dist/types/output.d.ts.map +1 -0
  249. package/dist/types/output.js +5 -0
  250. package/dist/types/output.js.map +1 -0
  251. package/dist/types/ui.d.ts +24 -0
  252. package/dist/types/ui.d.ts.map +1 -0
  253. package/dist/types/ui.js +55 -0
  254. package/dist/types/ui.js.map +1 -0
  255. package/package.json +1 -1
  256. package/dist/agents.d.ts.map +0 -1
  257. package/dist/agents.js.map +0 -1
  258. package/dist/ask-user.d.ts.map +0 -1
  259. package/dist/ask-user.js +0 -1405
  260. package/dist/ask-user.js.map +0 -1
  261. package/dist/batch.d.ts +0 -12
  262. package/dist/batch.d.ts.map +0 -1
  263. package/dist/batch.js +0 -11
  264. package/dist/batch.js.map +0 -1
  265. package/dist/cli-args.d.ts.map +0 -1
  266. package/dist/cli-args.js.map +0 -1
  267. package/dist/config.d.ts.map +0 -1
  268. package/dist/config.js.map +0 -1
  269. package/dist/depth.d.ts.map +0 -1
  270. package/dist/depth.js.map +0 -1
  271. package/dist/executor.d.ts.map +0 -1
  272. package/dist/executor.js.map +0 -1
  273. package/dist/flow-prompt.d.ts.map +0 -1
  274. package/dist/flow-prompt.js.map +0 -1
  275. package/dist/flow.d.ts.map +0 -1
  276. package/dist/flow.js.map +0 -1
  277. package/dist/notify-state.d.ts.map +0 -1
  278. package/dist/notify-state.js.map +0 -1
  279. package/dist/notify.d.ts.map +0 -1
  280. package/dist/notify.js.map +0 -1
  281. package/dist/reasoning-strip.d.ts.map +0 -1
  282. package/dist/reasoning-strip.js.map +0 -1
  283. package/dist/render-utils.d.ts.map +0 -1
  284. package/dist/render-utils.js.map +0 -1
  285. package/dist/render.d.ts +0 -24
  286. package/dist/render.d.ts.map +0 -1
  287. package/dist/render.js +0 -592
  288. package/dist/render.js.map +0 -1
  289. package/dist/runner-events.d.ts.map +0 -1
  290. package/dist/runner-events.js.map +0 -1
  291. package/dist/scramble.d.ts +0 -183
  292. package/dist/scramble.d.ts.map +0 -1
  293. package/dist/scramble.js +0 -2478
  294. package/dist/scramble.js.map +0 -1
  295. package/dist/session-mode.d.ts.map +0 -1
  296. package/dist/session-mode.js.map +0 -1
  297. package/dist/settings-resolver.d.ts.map +0 -1
  298. package/dist/settings-resolver.js +0 -148
  299. package/dist/settings-resolver.js.map +0 -1
  300. package/dist/single-select-layout.d.ts +0 -20
  301. package/dist/single-select-layout.d.ts.map +0 -1
  302. package/dist/single-select-layout.js.map +0 -1
  303. package/dist/sliding-prompt.d.ts.map +0 -1
  304. package/dist/sliding-prompt.js.map +0 -1
  305. package/dist/snapshot.d.ts.map +0 -1
  306. package/dist/snapshot.js +0 -797
  307. package/dist/snapshot.js.map +0 -1
  308. package/dist/spec-mode.d.ts +0 -13
  309. package/dist/spec-mode.d.ts.map +0 -1
  310. package/dist/spec-mode.js +0 -90
  311. package/dist/spec-mode.js.map +0 -1
  312. package/dist/structured-output.d.ts.map +0 -1
  313. package/dist/structured-output.js.map +0 -1
  314. package/dist/timed-bash.d.ts.map +0 -1
  315. package/dist/timed-bash.js.map +0 -1
  316. package/dist/tool-utils.d.ts.map +0 -1
  317. package/dist/tool-utils.js.map +0 -1
  318. package/dist/transitions.d.ts.map +0 -1
  319. package/dist/transitions.js.map +0 -1
  320. package/dist/types.d.ts +0 -224
  321. package/dist/types.d.ts.map +0 -1
  322. package/dist/types.js.map +0 -1
  323. package/dist/web-tool.d.ts.map +0 -1
  324. package/dist/web-tool.js.map +0 -1
  325. /package/dist/{agents.d.ts → core/agents.d.ts} +0 -0
  326. /package/dist/{depth.d.ts → core/depth.d.ts} +0 -0
  327. /package/dist/{transitions.d.ts → core/transitions.d.ts} +0 -0
  328. /package/dist/{notify-state.js → notify/notify-state.js} +0 -0
  329. /package/dist/{notify.d.ts → notify/notify.d.ts} +0 -0
  330. /package/dist/{runner-events.d.ts → snapshot/runner-events.d.ts} +0 -0
  331. /package/dist/{structured-output.js → snapshot/structured-output.js} +0 -0
@@ -0,0 +1,937 @@
1
+ /**
2
+ * /flow:settings slash command registration.
3
+ *
4
+ * Subcommands: steering, strategic-hint, animation, glitch,
5
+ * tool-optimize, structured-output, session-mode, max-concurrency, reset
6
+ *
7
+ * When called with no arguments, opens an interactive TUI overlay.
8
+ */
9
+ import { DynamicBorder } from "@mariozechner/pi-coding-agent";
10
+ import { loadFlowSettings, writeFlowSetting, loadFlowModelConfigs, writeGlobalFlowMode, writeFlowModelConfig } from "../config/config.js";
11
+ import { configureSteering } from "../steering/sliding-prompt.js";
12
+ import { configureStrategicHint } from "../steering/tool-utils.js";
13
+ import { scrambleManager } from "../tui/scramble/index.js";
14
+ import { getLoop } from "./loop.js";
15
+ import { Container, Input, matchesKey, SelectList, Key, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@mariozechner/pi-tui";
16
+ // Module-level refs for submenu builders (set during handler call)
17
+ let _modelRegistry = null;
18
+ let _theme = null;
19
+ let _keybindings = null;
20
+ export class SettingsList {
21
+ items;
22
+ theme;
23
+ selectedIndex = 0;
24
+ maxVisible;
25
+ onChange;
26
+ onCancel;
27
+ keybindings;
28
+ submenuComponent = null;
29
+ submenuItemIndex = null;
30
+ constructor(items, maxVisible, theme, keybindings, onChange, onCancel) {
31
+ this.items = items;
32
+ this.maxVisible = maxVisible;
33
+ this.theme = theme;
34
+ this.keybindings = keybindings;
35
+ this.onChange = onChange;
36
+ this.onCancel = onCancel;
37
+ }
38
+ updateValue(id, newValue) {
39
+ const item = this.items.find((i) => i.id === id);
40
+ if (item) {
41
+ item.currentValue = newValue;
42
+ }
43
+ }
44
+ invalidate() {
45
+ this.submenuComponent?.invalidate?.();
46
+ }
47
+ render(width) {
48
+ if (this.submenuComponent) {
49
+ return this.submenuComponent.render(width);
50
+ }
51
+ return this.renderMainList(width);
52
+ }
53
+ renderMainList(width) {
54
+ const lines = [];
55
+ if (this.items.length === 0) {
56
+ lines.push(this.theme.hint(" No settings available"));
57
+ this.addHintLine(lines);
58
+ return lines;
59
+ }
60
+ const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.items.length - this.maxVisible));
61
+ const endIndex = Math.min(startIndex + this.maxVisible, this.items.length);
62
+ const maxLabelWidth = Math.min(30, Math.max(...this.items.map((item) => visibleWidth(item.label))));
63
+ for (let i = startIndex; i < endIndex; i++) {
64
+ const item = this.items[i];
65
+ if (!item)
66
+ continue;
67
+ const isSelected = i === this.selectedIndex;
68
+ const prefix = isSelected ? this.theme.cursor : " ";
69
+ const prefixWidth = visibleWidth(prefix);
70
+ const labelPadded = item.label + " ".repeat(Math.max(0, maxLabelWidth - visibleWidth(item.label)));
71
+ const labelText = this.theme.label(labelPadded, isSelected);
72
+ const separator = " ";
73
+ const usedWidth = prefixWidth + maxLabelWidth + visibleWidth(separator);
74
+ const valueMaxWidth = Math.max(1, width - usedWidth - 2);
75
+ const optionLines = isSelected && item.values && item.values.length > 0
76
+ ? wrapTextWithAnsi(this.formatOptionsInline(item, item.values), valueMaxWidth)
77
+ : null;
78
+ const valueText = optionLines
79
+ ? optionLines[0] ?? ""
80
+ : this.theme.value(truncateToWidth(item.currentValue, valueMaxWidth, ""), isSelected);
81
+ const line = prefix + labelText + separator + valueText;
82
+ lines.push(truncateToWidth(line, width, ""));
83
+ if (optionLines && optionLines.length > 1) {
84
+ const indent = " ".repeat(prefixWidth + maxLabelWidth + visibleWidth(separator));
85
+ for (const continuation of optionLines.slice(1)) {
86
+ lines.push(truncateToWidth(indent + continuation, width, ""));
87
+ }
88
+ }
89
+ }
90
+ if (startIndex > 0 || endIndex < this.items.length) {
91
+ const scrollText = ` (${this.selectedIndex + 1}/${this.items.length})`;
92
+ lines.push(this.theme.hint(truncateToWidth(scrollText, width - 2, "")));
93
+ }
94
+ const selectedItem = this.items[this.selectedIndex];
95
+ if (selectedItem?.description) {
96
+ lines.push("");
97
+ const wrapWidth = Math.max(1, width - 4);
98
+ const wrappedDesc = wrapTextWithAnsi(selectedItem.description, wrapWidth);
99
+ for (const line of wrappedDesc) {
100
+ const prefixed = ` ${line}`;
101
+ lines.push(this.theme.description(truncateToWidth(prefixed, width, "")));
102
+ }
103
+ }
104
+ this.addHintLine(lines);
105
+ return lines;
106
+ }
107
+ handleInput(data) {
108
+ if (this.submenuComponent) {
109
+ this.submenuComponent.handleInput?.(data);
110
+ return;
111
+ }
112
+ if (this.keybindings.matches(data, "tui.select.up") || matchesKey(data, Key.ctrl("k"))) {
113
+ if (this.items.length === 0)
114
+ return;
115
+ this.selectedIndex = this.selectedIndex === 0 ? this.items.length - 1 : this.selectedIndex - 1;
116
+ }
117
+ else if (this.keybindings.matches(data, "tui.select.down") || matchesKey(data, Key.ctrl("j"))) {
118
+ if (this.items.length === 0)
119
+ return;
120
+ this.selectedIndex = this.selectedIndex === this.items.length - 1 ? 0 : this.selectedIndex + 1;
121
+ }
122
+ else if (this.keybindings.matches(data, "tui.editor.cursorLeft") || data === "\u001b[D") {
123
+ this.stepValue(-1);
124
+ }
125
+ else if (this.keybindings.matches(data, "tui.editor.cursorRight") || data === "\u001b[C") {
126
+ this.stepValue(1);
127
+ }
128
+ else if (this.keybindings.matches(data, "tui.select.confirm") ||
129
+ data === "\r" ||
130
+ data === "\n" ||
131
+ data === " ") {
132
+ this.activateItem();
133
+ }
134
+ else if (this.keybindings.matches(data, "tui.select.cancel") || matchesKey(data, Key.escape)) {
135
+ this.onCancel();
136
+ }
137
+ }
138
+ stepValue(direction) {
139
+ const item = this.items[this.selectedIndex];
140
+ if (!item || !item.values || item.values.length === 0)
141
+ return;
142
+ const values = item.values;
143
+ let currentIndex = values.indexOf(item.currentValue);
144
+ if (currentIndex === -1) {
145
+ currentIndex = direction > 0 ? 0 : values.length - 1;
146
+ }
147
+ const nextIndex = (currentIndex + direction + values.length) % values.length;
148
+ const newValue = values[nextIndex];
149
+ item.currentValue = newValue;
150
+ this.onChange(item.id, newValue);
151
+ }
152
+ activateItem() {
153
+ const item = this.items[this.selectedIndex];
154
+ if (!item)
155
+ return;
156
+ if (item.submenu) {
157
+ this.openSubmenu(item);
158
+ }
159
+ }
160
+ closeSubmenu() {
161
+ this.submenuComponent = null;
162
+ if (this.submenuItemIndex !== null) {
163
+ this.selectedIndex = this.submenuItemIndex;
164
+ this.submenuItemIndex = null;
165
+ }
166
+ }
167
+ formatOptionsInline(item, values) {
168
+ const separator = this.theme.description(" • ");
169
+ return values
170
+ .map((value) => {
171
+ const selected = value === item.currentValue;
172
+ return this.theme.value(value, selected);
173
+ })
174
+ .join(separator);
175
+ }
176
+ openSubmenu(item) {
177
+ if (!item.submenu)
178
+ return;
179
+ this.submenuItemIndex = this.selectedIndex;
180
+ this.submenuComponent = item.submenu(item.currentValue, (selectedValue) => {
181
+ if (selectedValue !== undefined) {
182
+ item.currentValue = selectedValue;
183
+ this.onChange(item.id, selectedValue);
184
+ }
185
+ this.closeSubmenu();
186
+ });
187
+ }
188
+ addHintLine(lines) {
189
+ lines.push("");
190
+ lines.push(this.theme.hint(" ←/→ change • Enter/Space edit custom • Esc to cancel"));
191
+ }
192
+ }
193
+ function getMainMenuItems(settings, cwd) {
194
+ const steeringEnabled = settings.steering?.enabled ?? true;
195
+ const animationEnabled = settings.animation?.enabled ?? true;
196
+ const toolOptimize = settings.toolOptimize ?? true;
197
+ const structuredOutput = settings.structuredOutput ?? true;
198
+ const sessionMode = settings.sessionMode ?? "default";
199
+ const askUserEnabled = settings.askUser?.enabled ?? false;
200
+ const askUserTimeout = settings.askUser?.timeout ?? 300;
201
+ const loaded = loadFlowModelConfigs(cwd);
202
+ const strategyName = loaded.selectedName;
203
+ const litePrimary = loaded.strategy.lite?.primary ?? "(default)";
204
+ const primaryModelShort = litePrimary.includes("/") ? litePrimary.split("/").pop() : litePrimary;
205
+ const loop = getLoop(cwd);
206
+ const loopDescription = loop ? `${loop.status} • ${loop.sessionCount} sessions` : "none";
207
+ return [
208
+ {
209
+ value: "steering",
210
+ label: "Steering Settings",
211
+ description: steeringEnabled ? "enabled" : "disabled",
212
+ tooltip: "Configure orchestrator steering and strategic hints",
213
+ },
214
+ {
215
+ value: "animation",
216
+ label: "Animation Settings",
217
+ description: animationEnabled ? "enabled" : "disabled",
218
+ tooltip: "Toggle animation effects and glitch/scramble",
219
+ },
220
+ {
221
+ value: "tools",
222
+ label: "Tool Settings",
223
+ description: `tool-optimize: ${toolOptimize ? "on" : "off"}, structured-output: ${structuredOutput ? "on" : "off"}`,
224
+ tooltip: "Configure tool optimization and structured output",
225
+ },
226
+ {
227
+ value: "session",
228
+ label: "Session Settings",
229
+ description: `mode: ${sessionMode}`,
230
+ tooltip: "Set default session mode and concurrency",
231
+ },
232
+ {
233
+ value: "ask-user",
234
+ label: "Ask User Settings",
235
+ description: `enabled: ${askUserEnabled ? "on" : "off"}, timeout: ${askUserTimeout}s`,
236
+ tooltip: "Configure ask_user timeout and countdown",
237
+ },
238
+ {
239
+ value: "model-config",
240
+ label: "Model Config",
241
+ description: `${strategyName} ▸ lite: ${primaryModelShort}`,
242
+ tooltip: "Configure LLM models for lite, flash, and full flow tiers",
243
+ },
244
+ {
245
+ value: "loop",
246
+ label: "Loop Status",
247
+ description: loopDescription,
248
+ tooltip: "Endless loop state and statistics",
249
+ },
250
+ {
251
+ value: "reset",
252
+ label: "Reset to Defaults",
253
+ description: "restore all settings",
254
+ tooltip: "Restore all flow settings to their default values",
255
+ },
256
+ ];
257
+ }
258
+ function getSteeringItems(settings) {
259
+ const steering = settings.steering ?? {};
260
+ return [
261
+ {
262
+ id: "steering.enabled",
263
+ label: "enabled",
264
+ description: "Toggle steering injection",
265
+ currentValue: (steering.enabled ?? true) ? "on" : "off",
266
+ values: ["on", "off"],
267
+ },
268
+ {
269
+ id: "steering.strategicHint",
270
+ label: "strategic-hint",
271
+ description: "Toggle [Hint: Plan next step...]",
272
+ currentValue: (steering.strategicHint ?? true) ? "on" : "off",
273
+ values: ["on", "off"],
274
+ },
275
+ {
276
+ id: "steering.customPrompt",
277
+ label: "custom-prompt",
278
+ description: "Enter custom steering prompt or type 'default' to reset",
279
+ currentValue: steering.customPrompt ?? "(default)",
280
+ submenu: buildInputSubmenu("Custom prompt (or 'default')", (v) => {
281
+ const trimmed = v.trim();
282
+ if (!trimmed || trimmed.toLowerCase() === "default")
283
+ return "(default)";
284
+ return trimmed;
285
+ }),
286
+ },
287
+ ];
288
+ }
289
+ function getAnimationItems(settings) {
290
+ const animation = settings.animation ?? {};
291
+ return [
292
+ {
293
+ id: "animation.enabled",
294
+ label: "enabled",
295
+ description: "Master animation switch",
296
+ currentValue: (animation.enabled ?? true) ? "on" : "off",
297
+ values: ["on", "off"],
298
+ },
299
+ {
300
+ id: "animation.glitch",
301
+ label: "glitch",
302
+ description: "Glitch/scramble effect",
303
+ currentValue: (animation.glitch ?? true) ? "on" : "off",
304
+ values: ["on", "off"],
305
+ },
306
+ ];
307
+ }
308
+ function getToolItems(settings) {
309
+ return [
310
+ {
311
+ id: "toolOptimize",
312
+ label: "tool-optimize",
313
+ description: "Unified batch tool vs separate tools",
314
+ currentValue: (settings.toolOptimize ?? true) ? "on" : "off",
315
+ values: ["on", "off"],
316
+ },
317
+ {
318
+ id: "structuredOutput",
319
+ label: "structured-output",
320
+ description: "Structured JSON output from flows",
321
+ currentValue: (settings.structuredOutput ?? true) ? "on" : "off",
322
+ values: ["on", "off"],
323
+ },
324
+ ];
325
+ }
326
+ function getSessionItems(settings) {
327
+ return [
328
+ {
329
+ id: "sessionMode",
330
+ label: "session-mode",
331
+ description: "Session safety mode",
332
+ currentValue: settings.sessionMode ?? "default",
333
+ values: ["fast", "default", "long", "extreme_long"],
334
+ },
335
+ {
336
+ id: "maxConcurrency",
337
+ label: "max-concurrency",
338
+ description: "Maximum concurrent flows",
339
+ currentValue: String(settings.maxConcurrency ?? 4),
340
+ values: ["1", "2", "3", "4", "5", "6", "7", "8"],
341
+ submenu: buildInputSubmenu("Max concurrency (1-20)", (v) => {
342
+ const n = Number(v.trim());
343
+ if (!Number.isSafeInteger(n) || n < 1 || n > 20)
344
+ return null;
345
+ return String(n);
346
+ }),
347
+ },
348
+ ];
349
+ }
350
+ function getAskUserItems(settings) {
351
+ const askUser = settings.askUser ?? {};
352
+ return [
353
+ {
354
+ id: "askUser.enabled",
355
+ label: "enabled",
356
+ description: "Show countdown timer in ask_user prompt",
357
+ currentValue: (askUser.enabled ?? false) ? "on" : "off",
358
+ values: ["on", "off"],
359
+ },
360
+ {
361
+ id: "askUser.timeout",
362
+ label: "timeout",
363
+ description: "Auto-dismiss timeout in seconds",
364
+ currentValue: String(askUser.timeout ?? 300),
365
+ values: ["30", "60", "120", "300", "600"],
366
+ submenu: buildInputSubmenu("Timeout in seconds (10-3600)", (v) => {
367
+ const n = Number(v.trim());
368
+ if (!Number.isSafeInteger(n) || n < 10 || n > 3600)
369
+ return null;
370
+ return String(n);
371
+ }),
372
+ },
373
+ ];
374
+ }
375
+ function getLoopItems(_settings, cwd) {
376
+ const loop = getLoop(cwd);
377
+ if (!loop) {
378
+ return [
379
+ {
380
+ id: "loop.status",
381
+ label: "status",
382
+ description: "No active loop",
383
+ currentValue: "none",
384
+ },
385
+ ];
386
+ }
387
+ return [
388
+ {
389
+ id: "loop.status",
390
+ label: "status",
391
+ description: "Current loop status",
392
+ currentValue: loop.status,
393
+ },
394
+ {
395
+ id: "loop.objective",
396
+ label: "objective",
397
+ description: "Loop objective",
398
+ currentValue: loop.objective,
399
+ },
400
+ {
401
+ id: "loop.sessions",
402
+ label: "sessions",
403
+ description: "Number of warped sessions",
404
+ currentValue: String(loop.sessionCount),
405
+ },
406
+ {
407
+ id: "loop.flows",
408
+ label: "flows",
409
+ description: "Total flows across sessions",
410
+ currentValue: String(loop.totalFlowsAcrossSessions),
411
+ },
412
+ {
413
+ id: "loop.tokens",
414
+ label: "tokens",
415
+ description: "Total tokens across sessions",
416
+ currentValue: String(loop.totalTokensAcrossSessions),
417
+ },
418
+ ];
419
+ }
420
+ function getModelConfigItems(settings, cwd) {
421
+ const loaded = loadFlowModelConfigs(cwd);
422
+ const strategyName = loaded.selectedName;
423
+ const strategy = loaded.strategy;
424
+ const items = [
425
+ {
426
+ id: "modelConfig.strategy",
427
+ label: "strategy",
428
+ description: "Active model strategy",
429
+ currentValue: strategyName,
430
+ values: Object.keys(loaded.configs).sort(),
431
+ },
432
+ ];
433
+ for (const tier of ["lite", "flash", "full"]) {
434
+ const tierConfig = strategy[tier];
435
+ const primary = tierConfig?.primary ?? "(default)";
436
+ const failover = tierConfig?.failover?.join(", ") ?? "(none)";
437
+ items.push({
438
+ id: `modelConfig.${tier}.primary`,
439
+ label: `${tier}: primary`,
440
+ description: `Primary model for ${tier} tier`,
441
+ currentValue: primary,
442
+ submenu: buildModelPickerSubmenu(primary, tier, "primary"),
443
+ });
444
+ items.push({
445
+ id: `modelConfig.${tier}.failover`,
446
+ label: `${tier}: failover`,
447
+ description: `Failover models for ${tier} tier`,
448
+ currentValue: failover,
449
+ submenu: buildModelPickerSubmenu(failover, tier, "failover"),
450
+ });
451
+ }
452
+ return items;
453
+ }
454
+ // ---------------------------------------------------------------------------
455
+ // Submenu helpers
456
+ // ---------------------------------------------------------------------------
457
+ function buildInputSubmenu(label, parseValue) {
458
+ return (currentValue, done) => {
459
+ const input = new Input();
460
+ input.focused = true;
461
+ input.setValue(currentValue);
462
+ input.onSubmit = (value) => {
463
+ const parsed = parseValue(value);
464
+ if (parsed === null)
465
+ return;
466
+ done(parsed);
467
+ };
468
+ input.onEscape = () => {
469
+ done();
470
+ };
471
+ const container = new Container();
472
+ container.addChild(new Text(label, 1, 0));
473
+ container.addChild(new Spacer(1));
474
+ container.addChild(input);
475
+ return {
476
+ render: (width) => container.render(width),
477
+ invalidate: () => container.invalidate(),
478
+ handleInput: (data) => input.handleInput(data),
479
+ };
480
+ };
481
+ }
482
+ function buildModelPickerSubmenu(currentValue, tier, slot) {
483
+ return (_currentValue, done) => {
484
+ const models = _modelRegistry?.getAvailable() ?? [];
485
+ const items = models.map((m) => ({
486
+ value: `${m.provider}/${m.id}`,
487
+ label: `${m.provider}/${m.id}`,
488
+ description: m.name ?? "",
489
+ }));
490
+ items.unshift({ value: "(default)", label: "(default)", description: "Use the default model" });
491
+ const selectList = new SelectList(items, 15, {
492
+ selectedPrefix: (t) => _theme?.fg("accent", t) ?? t,
493
+ selectedText: (t) => _theme?.fg("accent", t) ?? t,
494
+ description: (t) => _theme?.fg("muted", t) ?? t,
495
+ scrollInfo: (t) => _theme?.fg("dim", t) ?? t,
496
+ noMatch: (t) => _theme?.fg("warning", t) ?? t,
497
+ });
498
+ selectList.onSelect = (item) => {
499
+ done(item.value);
500
+ };
501
+ selectList.onCancel = () => {
502
+ done();
503
+ };
504
+ return {
505
+ render(width) {
506
+ return selectList.render(width);
507
+ },
508
+ invalidate() {
509
+ selectList.invalidate?.();
510
+ },
511
+ handleInput(data) {
512
+ selectList.handleInput(data);
513
+ },
514
+ };
515
+ };
516
+ }
517
+ // ---------------------------------------------------------------------------
518
+ // Command registration
519
+ // ---------------------------------------------------------------------------
520
+ export function setupSettingsCommand(pi) {
521
+ pi.registerCommand("flow:settings", {
522
+ description: "Manage flow settings. Subcommands: steering <on|off>, strategic-hint <on|off>, animation <on|off>, glitch <on|off>, tool-optimize <on|off>, structured-output <on|off>, session-mode <mode>, max-concurrency <n>, ask-user {enabled <on|off> | timeout <seconds>}, reset. Call with no args for interactive TUI.",
523
+ handler: async (args, ctx) => {
524
+ const cwd = ctx.cwd;
525
+ const trimmed = args.trim().toLowerCase();
526
+ const parts = trimmed.split(/\s+/);
527
+ const sub = parts[0] ?? "";
528
+ const value = parts[1] ?? "";
529
+ if (!sub) {
530
+ const settings = loadFlowSettings(cwd);
531
+ _modelRegistry = ctx.modelRegistry;
532
+ await ctx.ui.custom((tui, theme, keybindings, done) => {
533
+ _theme = theme;
534
+ _keybindings = keybindings;
535
+ let currentCategory = "main";
536
+ let container = new Container();
537
+ let activeList = null;
538
+ function rebuild() {
539
+ container = new Container();
540
+ activeList = null;
541
+ // Header
542
+ container.addChild(new DynamicBorder());
543
+ container.addChild(new Text(theme.fg("accent", theme.bold("Flow Settings")), 1, 0));
544
+ container.addChild(new Spacer(1));
545
+ const currentSettings = loadFlowSettings(cwd);
546
+ if (currentCategory === "main") {
547
+ const items = getMainMenuItems(currentSettings, cwd);
548
+ const selectList = new SelectList(items, Math.min(items.length, 10), {
549
+ selectedPrefix: (t) => theme.fg("accent", t),
550
+ selectedText: (t) => theme.fg("accent", t),
551
+ description: (t) => theme.fg("muted", t),
552
+ scrollInfo: (t) => theme.fg("dim", t),
553
+ noMatch: (t) => theme.fg("warning", t),
554
+ });
555
+ selectList.onSelect = (item) => {
556
+ if (item.value === "reset") {
557
+ writeFlowSetting(cwd, "", {});
558
+ configureSteering({ enabled: true, customPrompt: undefined });
559
+ configureStrategicHint(true);
560
+ scrambleManager.setAnimationConfig({ enabled: true, glitch: true });
561
+ ctx.ui.notify?.("Flow settings reset to defaults", "info");
562
+ rebuild();
563
+ tui.requestRender();
564
+ }
565
+ else {
566
+ currentCategory = item.value;
567
+ rebuild();
568
+ tui.requestRender();
569
+ }
570
+ };
571
+ selectList.onCancel = () => {
572
+ done(loadFlowSettings(cwd));
573
+ };
574
+ activeList = selectList;
575
+ container.addChild(selectList);
576
+ const tooltipText = new Text("", 1, 0);
577
+ selectList.onSelectionChange = (item) => {
578
+ tooltipText.setText(theme.fg("dim", item.tooltip ?? ""));
579
+ };
580
+ const initialItem = selectList.getSelectedItem();
581
+ if (initialItem) {
582
+ tooltipText.setText(theme.fg("dim", initialItem.tooltip ?? ""));
583
+ }
584
+ container.addChild(new Spacer(1));
585
+ container.addChild(tooltipText);
586
+ container.addChild(new Spacer(1));
587
+ container.addChild(new Text(theme.fg("dim", "▲▼ navigate • Enter/Space select • Esc back"), 1, 0));
588
+ container.addChild(new DynamicBorder());
589
+ }
590
+ else {
591
+ let items;
592
+ let handleChange;
593
+ const backCategory = "main";
594
+ if (currentCategory === "steering") {
595
+ items = getSteeringItems(currentSettings);
596
+ handleChange = (id, value) => {
597
+ if (id === "steering.enabled") {
598
+ const boolValue = value === "on";
599
+ writeFlowSetting(cwd, "steering.enabled", boolValue);
600
+ configureSteering({
601
+ enabled: boolValue,
602
+ customPrompt: currentSettings.steering?.customPrompt,
603
+ });
604
+ }
605
+ else if (id === "steering.strategicHint") {
606
+ const boolValue = value === "on";
607
+ writeFlowSetting(cwd, "steering.strategicHint", boolValue);
608
+ configureStrategicHint(boolValue);
609
+ }
610
+ else if (id === "steering.customPrompt") {
611
+ if (value === "(default)") {
612
+ writeFlowSetting(cwd, "steering.customPrompt", undefined);
613
+ configureSteering({
614
+ enabled: currentSettings.steering?.enabled ?? true,
615
+ customPrompt: undefined,
616
+ });
617
+ }
618
+ else {
619
+ writeFlowSetting(cwd, "steering.customPrompt", value);
620
+ configureSteering({
621
+ enabled: currentSettings.steering?.enabled ?? true,
622
+ customPrompt: value,
623
+ });
624
+ }
625
+ }
626
+ rebuild();
627
+ tui.requestRender();
628
+ };
629
+ }
630
+ else if (currentCategory === "animation") {
631
+ items = getAnimationItems(currentSettings);
632
+ handleChange = (id, value) => {
633
+ if (id === "animation.enabled") {
634
+ const boolValue = value === "on";
635
+ writeFlowSetting(cwd, "animation.enabled", boolValue);
636
+ scrambleManager.setAnimationConfig({
637
+ enabled: boolValue,
638
+ glitch: currentSettings.animation?.glitch ?? true,
639
+ });
640
+ }
641
+ else if (id === "animation.glitch") {
642
+ const boolValue = value === "on";
643
+ writeFlowSetting(cwd, "animation.glitch", boolValue);
644
+ scrambleManager.setAnimationConfig({
645
+ enabled: currentSettings.animation?.enabled ?? true,
646
+ glitch: boolValue,
647
+ });
648
+ }
649
+ rebuild();
650
+ tui.requestRender();
651
+ };
652
+ }
653
+ else if (currentCategory === "tools") {
654
+ items = getToolItems(currentSettings);
655
+ handleChange = (id, value) => {
656
+ if (id === "toolOptimize") {
657
+ writeFlowSetting(cwd, "toolOptimize", value === "on");
658
+ }
659
+ else if (id === "structuredOutput") {
660
+ writeFlowSetting(cwd, "structuredOutput", value === "on");
661
+ }
662
+ rebuild();
663
+ tui.requestRender();
664
+ };
665
+ }
666
+ else if (currentCategory === "session") {
667
+ items = getSessionItems(currentSettings);
668
+ handleChange = (id, value) => {
669
+ if (id === "sessionMode") {
670
+ writeFlowSetting(cwd, "sessionMode", value);
671
+ }
672
+ else if (id === "maxConcurrency") {
673
+ writeFlowSetting(cwd, "maxConcurrency", Number(value));
674
+ }
675
+ rebuild();
676
+ tui.requestRender();
677
+ };
678
+ }
679
+ else if (currentCategory === "ask-user") {
680
+ items = getAskUserItems(currentSettings);
681
+ handleChange = (id, value) => {
682
+ if (id === "askUser.enabled") {
683
+ writeFlowSetting(cwd, "askUser.enabled", value === "on");
684
+ }
685
+ else if (id === "askUser.timeout") {
686
+ writeFlowSetting(cwd, "askUser.timeout", Number(value));
687
+ }
688
+ rebuild();
689
+ tui.requestRender();
690
+ };
691
+ }
692
+ else if (currentCategory === "model-config") {
693
+ items = getModelConfigItems(currentSettings, cwd);
694
+ handleChange = (id, value) => {
695
+ if (id === "modelConfig.strategy") {
696
+ try {
697
+ writeGlobalFlowMode(value);
698
+ }
699
+ catch (e) {
700
+ /* ignore */
701
+ }
702
+ }
703
+ else if (id.startsWith("modelConfig.")) {
704
+ const match = id.match(/^modelConfig\.(lite|flash|full)\.(primary|failover)$/);
705
+ if (match) {
706
+ const tier = match[1];
707
+ const slot = match[2];
708
+ const loaded = loadFlowModelConfigs(cwd);
709
+ const strategyName = loaded.selectedName;
710
+ if (slot === "primary") {
711
+ if (value === "(default)") {
712
+ writeFlowModelConfig(cwd, strategyName, tier, { primary: null, failover: null });
713
+ }
714
+ else {
715
+ writeFlowModelConfig(cwd, strategyName, tier, { primary: value });
716
+ }
717
+ }
718
+ else {
719
+ if (value === "(default)") {
720
+ writeFlowModelConfig(cwd, strategyName, tier, { failover: [] });
721
+ }
722
+ else {
723
+ writeFlowModelConfig(cwd, strategyName, tier, { failover: [value] });
724
+ }
725
+ }
726
+ }
727
+ }
728
+ rebuild();
729
+ tui.requestRender();
730
+ };
731
+ }
732
+ else if (currentCategory === "loop") {
733
+ items = getLoopItems(currentSettings, cwd);
734
+ handleChange = () => { };
735
+ }
736
+ else {
737
+ items = [];
738
+ handleChange = () => { };
739
+ }
740
+ const settingsTheme = {
741
+ label: (text, selected) => selected ? theme.fg("accent", theme.bold(text)) : theme.fg("text", text),
742
+ value: (text, selected) => selected ? theme.fg("accent", text) : theme.fg("muted", text),
743
+ description: (text) => theme.fg("muted", text),
744
+ cursor: theme.fg("accent", "▶ "),
745
+ hint: (text) => theme.fg("dim", text),
746
+ };
747
+ const settingsList = new SettingsList(items, Math.min(items.length + 2, 15), settingsTheme, keybindings, handleChange, () => {
748
+ currentCategory = backCategory;
749
+ rebuild();
750
+ tui.requestRender();
751
+ });
752
+ activeList = settingsList;
753
+ container.addChild(settingsList);
754
+ container.addChild(new DynamicBorder());
755
+ }
756
+ }
757
+ rebuild();
758
+ return {
759
+ render(width) {
760
+ return container.render(width);
761
+ },
762
+ invalidate() {
763
+ container.invalidate();
764
+ },
765
+ handleInput(data) {
766
+ if (activeList?.handleInput) {
767
+ activeList.handleInput(data);
768
+ }
769
+ tui.requestRender();
770
+ },
771
+ };
772
+ });
773
+ return;
774
+ }
775
+ // -----------------------------------------------------------------
776
+ // Text-based fallback for subcommands with arguments
777
+ // -----------------------------------------------------------------
778
+ const parseOnOff = (v) => {
779
+ if (v === "on" || v === "true" || v === "1")
780
+ return true;
781
+ if (v === "off" || v === "false" || v === "0")
782
+ return false;
783
+ return null;
784
+ };
785
+ switch (sub) {
786
+ case "steering": {
787
+ const parsed = parseOnOff(value);
788
+ if (parsed === null) {
789
+ ctx.ui.notify?.("Usage: /flow:settings steering <on|off>", "error");
790
+ return;
791
+ }
792
+ writeFlowSetting(cwd, "steering.enabled", parsed);
793
+ configureSteering({ enabled: parsed, customPrompt: undefined });
794
+ ctx.ui.notify?.(`steering.enabled = ${parsed}`, "info");
795
+ break;
796
+ }
797
+ case "strategic-hint": {
798
+ const parsed = parseOnOff(value);
799
+ if (parsed === null) {
800
+ ctx.ui.notify?.("Usage: /flow:settings strategic-hint <on|off>", "error");
801
+ return;
802
+ }
803
+ writeFlowSetting(cwd, "steering.strategicHint", parsed);
804
+ configureStrategicHint(parsed);
805
+ ctx.ui.notify?.(`steering.strategicHint = ${parsed}`, "info");
806
+ break;
807
+ }
808
+ case "animation": {
809
+ const parsed = parseOnOff(value);
810
+ if (parsed === null) {
811
+ ctx.ui.notify?.("Usage: /flow:settings animation <on|off>", "error");
812
+ return;
813
+ }
814
+ writeFlowSetting(cwd, "animation.enabled", parsed);
815
+ scrambleManager.setAnimationConfig({ enabled: parsed, glitch: true });
816
+ ctx.ui.notify?.(`animation.enabled = ${parsed}`, "info");
817
+ break;
818
+ }
819
+ case "glitch": {
820
+ const parsed = parseOnOff(value);
821
+ if (parsed === null) {
822
+ ctx.ui.notify?.("Usage: /flow:settings glitch <on|off>", "error");
823
+ return;
824
+ }
825
+ writeFlowSetting(cwd, "animation.glitch", parsed);
826
+ scrambleManager.setAnimationConfig({ enabled: true, glitch: parsed });
827
+ ctx.ui.notify?.(`animation.glitch = ${parsed}`, "info");
828
+ break;
829
+ }
830
+ case "tool-optimize": {
831
+ const parsed = parseOnOff(value);
832
+ if (parsed === null) {
833
+ ctx.ui.notify?.("Usage: /flow:settings tool-optimize <on|off>", "error");
834
+ return;
835
+ }
836
+ writeFlowSetting(cwd, "toolOptimize", parsed);
837
+ ctx.ui.notify?.(`toolOptimize = ${parsed}`, "info");
838
+ break;
839
+ }
840
+ case "structured-output": {
841
+ const parsed = parseOnOff(value);
842
+ if (parsed === null) {
843
+ ctx.ui.notify?.("Usage: /flow:settings structured-output <on|off>", "error");
844
+ return;
845
+ }
846
+ writeFlowSetting(cwd, "structuredOutput", parsed);
847
+ ctx.ui.notify?.(`structuredOutput = ${parsed}`, "info");
848
+ break;
849
+ }
850
+ case "session-mode": {
851
+ const validModes = ["snap", "fast", "default", "long", "extreme_long"];
852
+ if (!validModes.includes(value)) {
853
+ ctx.ui.notify?.("Usage: /flow:settings session-mode <snap|fast|default|long|extreme_long>", "error");
854
+ return;
855
+ }
856
+ writeFlowSetting(cwd, "sessionMode", value);
857
+ ctx.ui.notify?.(`sessionMode = ${value}`, "info");
858
+ break;
859
+ }
860
+ case "max-concurrency": {
861
+ const n = Number(value);
862
+ if (!Number.isSafeInteger(n) || n < 1) {
863
+ ctx.ui.notify?.("Usage: /flow:settings max-concurrency <n>", "error");
864
+ return;
865
+ }
866
+ writeFlowSetting(cwd, "maxConcurrency", n);
867
+ ctx.ui.notify?.(`maxConcurrency = ${n}`, "info");
868
+ break;
869
+ }
870
+ case "ask-user": {
871
+ const askParts = trimmed.split(/\s+/);
872
+ const askSub = askParts[1] ?? "";
873
+ const askValue = askParts[2] ?? "";
874
+ if (askSub === "enabled") {
875
+ const parsed = parseOnOff(askValue);
876
+ if (parsed === null) {
877
+ ctx.ui.notify?.("Usage: /flow:settings ask-user enabled <on|off>", "error");
878
+ return;
879
+ }
880
+ writeFlowSetting(cwd, "askUser.enabled", parsed);
881
+ ctx.ui.notify?.(`askUser.enabled = ${parsed}`, "info");
882
+ }
883
+ else if (askSub === "timeout") {
884
+ const n = Number(askValue);
885
+ if (!Number.isSafeInteger(n) || n < 10) {
886
+ ctx.ui.notify?.("Usage: /flow:settings ask-user timeout <seconds>", "error");
887
+ return;
888
+ }
889
+ writeFlowSetting(cwd, "askUser.timeout", n);
890
+ ctx.ui.notify?.(`askUser.timeout = ${n}`, "info");
891
+ }
892
+ else {
893
+ ctx.ui.notify?.("Usage: /flow:settings ask-user {enabled <on|off> | timeout <seconds>}", "error");
894
+ }
895
+ break;
896
+ }
897
+ case "reset": {
898
+ writeFlowSetting(cwd, "", {});
899
+ ctx.ui.notify?.("Flow settings reset to defaults", "info");
900
+ break;
901
+ }
902
+ case "show": {
903
+ const currentSettings = loadFlowSettings(cwd);
904
+ const loop = getLoop(cwd);
905
+ const lines = [
906
+ `toolOptimize: ${currentSettings.toolOptimize ?? true}`,
907
+ `structuredOutput: ${currentSettings.structuredOutput ?? true}`,
908
+ `sessionMode: ${currentSettings.sessionMode ?? "default"}`,
909
+ `maxConcurrency: ${currentSettings.maxConcurrency ?? 4}`,
910
+ `steering.enabled: ${currentSettings.steering?.enabled ?? true}`,
911
+ `steering.strategicHint: ${currentSettings.steering?.strategicHint ?? true}`,
912
+ `animation.enabled: ${currentSettings.animation?.enabled ?? true}`,
913
+ `animation.glitch: ${currentSettings.animation?.glitch ?? true}`,
914
+ `askUser.enabled: ${currentSettings.askUser?.enabled ?? false}`,
915
+ `askUser.timeout: ${currentSettings.askUser?.timeout ?? 300}`,
916
+ ];
917
+ if (loop) {
918
+ lines.push("");
919
+ lines.push(`loop.status: ${loop.status}`);
920
+ lines.push(`loop.objective: ${loop.objective}`);
921
+ lines.push(`loop.sessions: ${loop.sessionCount}`);
922
+ lines.push(`loop.flows: ${loop.totalFlowsAcrossSessions}`);
923
+ lines.push(`loop.tokens: ${loop.totalTokensAcrossSessions}`);
924
+ if (loop.terminationReason)
925
+ lines.push(`loop.terminationReason: ${loop.terminationReason}`);
926
+ }
927
+ ctx.ui.notify?.(lines.join("\n"), "info");
928
+ break;
929
+ }
930
+ default: {
931
+ ctx.ui.notify?.("Unknown subcommand. Usage: /flow:settings {steering|strategic-hint|animation|glitch|tool-optimize|structured-output|session-mode|max-concurrency|ask-user|reset|show}", "error");
932
+ }
933
+ }
934
+ },
935
+ });
936
+ }
937
+ //# sourceMappingURL=settings-command.js.map