pi-ui-extend 0.1.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 (420) hide show
  1. package/README.md +307 -0
  2. package/bin/pix.mjs +219 -0
  3. package/dist/app/app.d.ts +96 -0
  4. package/dist/app/app.js +871 -0
  5. package/dist/app/blink-controller.d.ts +23 -0
  6. package/dist/app/blink-controller.js +82 -0
  7. package/dist/app/cli.d.ts +8 -0
  8. package/dist/app/cli.js +83 -0
  9. package/dist/app/clipboard.d.ts +1 -0
  10. package/dist/app/clipboard.js +24 -0
  11. package/dist/app/command-controller.d.ts +18 -0
  12. package/dist/app/command-controller.js +58 -0
  13. package/dist/app/command-host.d.ts +44 -0
  14. package/dist/app/command-host.js +1 -0
  15. package/dist/app/command-model-actions.d.ts +12 -0
  16. package/dist/app/command-model-actions.js +176 -0
  17. package/dist/app/command-navigation-actions.d.ts +19 -0
  18. package/dist/app/command-navigation-actions.js +267 -0
  19. package/dist/app/command-registry.d.ts +32 -0
  20. package/dist/app/command-registry.js +225 -0
  21. package/dist/app/command-runtime.d.ts +5 -0
  22. package/dist/app/command-runtime.js +32 -0
  23. package/dist/app/command-session-actions.d.ts +20 -0
  24. package/dist/app/command-session-actions.js +295 -0
  25. package/dist/app/constants.d.ts +52 -0
  26. package/dist/app/constants.js +103 -0
  27. package/dist/app/conversation-entry-renderer.d.ts +21 -0
  28. package/dist/app/conversation-entry-renderer.js +81 -0
  29. package/dist/app/conversation-shell-renderer.d.ts +5 -0
  30. package/dist/app/conversation-shell-renderer.js +43 -0
  31. package/dist/app/conversation-tool-renderer.d.ts +16 -0
  32. package/dist/app/conversation-tool-renderer.js +216 -0
  33. package/dist/app/conversation-viewport.d.ts +55 -0
  34. package/dist/app/conversation-viewport.js +252 -0
  35. package/dist/app/dcp-stats.d.ts +2 -0
  36. package/dist/app/dcp-stats.js +116 -0
  37. package/dist/app/editor-layout-renderer.d.ts +31 -0
  38. package/dist/app/editor-layout-renderer.js +211 -0
  39. package/dist/app/editor-panels.d.ts +4 -0
  40. package/dist/app/editor-panels.js +130 -0
  41. package/dist/app/extension-actions-controller.d.ts +22 -0
  42. package/dist/app/extension-actions-controller.js +60 -0
  43. package/dist/app/extension-event-bus.d.ts +3 -0
  44. package/dist/app/extension-event-bus.js +23 -0
  45. package/dist/app/extension-ui-controller.d.ts +57 -0
  46. package/dist/app/extension-ui-controller.js +532 -0
  47. package/dist/app/file-link-opener.d.ts +2 -0
  48. package/dist/app/file-link-opener.js +66 -0
  49. package/dist/app/file-links.d.ts +10 -0
  50. package/dist/app/file-links.js +117 -0
  51. package/dist/app/guards.d.ts +3 -0
  52. package/dist/app/guards.js +9 -0
  53. package/dist/app/icons.d.ts +37 -0
  54. package/dist/app/icons.js +97 -0
  55. package/dist/app/id.d.ts +1 -0
  56. package/dist/app/id.js +4 -0
  57. package/dist/app/image-click-targets.d.ts +5 -0
  58. package/dist/app/image-click-targets.js +32 -0
  59. package/dist/app/image-opener.d.ts +2 -0
  60. package/dist/app/image-opener.js +64 -0
  61. package/dist/app/input-action-controller.d.ts +47 -0
  62. package/dist/app/input-action-controller.js +209 -0
  63. package/dist/app/input-controller.d.ts +60 -0
  64. package/dist/app/input-controller.js +425 -0
  65. package/dist/app/input-paste-handler.d.ts +27 -0
  66. package/dist/app/input-paste-handler.js +146 -0
  67. package/dist/app/menu-items-controller.d.ts +32 -0
  68. package/dist/app/menu-items-controller.js +182 -0
  69. package/dist/app/message-content.d.ts +8 -0
  70. package/dist/app/message-content.js +115 -0
  71. package/dist/app/model-ref.d.ts +13 -0
  72. package/dist/app/model-ref.js +50 -0
  73. package/dist/app/model-usage-controller.d.ts +35 -0
  74. package/dist/app/model-usage-controller.js +99 -0
  75. package/dist/app/model-usage-status.d.ts +125 -0
  76. package/dist/app/model-usage-status.js +749 -0
  77. package/dist/app/mouse-controller.d.ts +182 -0
  78. package/dist/app/mouse-controller.js +968 -0
  79. package/dist/app/native-modifiers.d.ts +3 -0
  80. package/dist/app/native-modifiers.js +60 -0
  81. package/dist/app/nerd-font-controller.d.ts +11 -0
  82. package/dist/app/nerd-font-controller.js +90 -0
  83. package/dist/app/popup-action-controller.d.ts +44 -0
  84. package/dist/app/popup-action-controller.js +278 -0
  85. package/dist/app/popup-menu-controller.d.ts +183 -0
  86. package/dist/app/popup-menu-controller.js +1070 -0
  87. package/dist/app/prompt-enhancer-controller.d.ts +40 -0
  88. package/dist/app/prompt-enhancer-controller.js +215 -0
  89. package/dist/app/queued-message-controller.d.ts +62 -0
  90. package/dist/app/queued-message-controller.js +377 -0
  91. package/dist/app/render-controller.d.ts +41 -0
  92. package/dist/app/render-controller.js +378 -0
  93. package/dist/app/render-text.d.ts +19 -0
  94. package/dist/app/render-text.js +67 -0
  95. package/dist/app/request-history.d.ts +23 -0
  96. package/dist/app/request-history.js +131 -0
  97. package/dist/app/runtime.d.ts +39 -0
  98. package/dist/app/runtime.js +195 -0
  99. package/dist/app/screen-selection.d.ts +6 -0
  100. package/dist/app/screen-selection.js +9 -0
  101. package/dist/app/screen-styler.d.ts +34 -0
  102. package/dist/app/screen-styler.js +168 -0
  103. package/dist/app/scroll-controller.d.ts +51 -0
  104. package/dist/app/scroll-controller.js +207 -0
  105. package/dist/app/session-event-controller.d.ts +69 -0
  106. package/dist/app/session-event-controller.js +338 -0
  107. package/dist/app/session-history.d.ts +23 -0
  108. package/dist/app/session-history.js +164 -0
  109. package/dist/app/session-lifecycle-controller.d.ts +55 -0
  110. package/dist/app/session-lifecycle-controller.js +116 -0
  111. package/dist/app/session-search.d.ts +24 -0
  112. package/dist/app/session-search.js +215 -0
  113. package/dist/app/shell-command.d.ts +27 -0
  114. package/dist/app/shell-command.js +176 -0
  115. package/dist/app/shell-controller.d.ts +28 -0
  116. package/dist/app/shell-controller.js +124 -0
  117. package/dist/app/slash-commands.d.ts +6 -0
  118. package/dist/app/slash-commands.js +75 -0
  119. package/dist/app/startup-checks.d.ts +8 -0
  120. package/dist/app/startup-checks.js +59 -0
  121. package/dist/app/startup-info.d.ts +3 -0
  122. package/dist/app/startup-info.js +176 -0
  123. package/dist/app/status-controller.d.ts +35 -0
  124. package/dist/app/status-controller.js +105 -0
  125. package/dist/app/status-line-renderer.d.ts +68 -0
  126. package/dist/app/status-line-renderer.js +453 -0
  127. package/dist/app/subagents-files.d.ts +10 -0
  128. package/dist/app/subagents-files.js +193 -0
  129. package/dist/app/subagents-model.d.ts +23 -0
  130. package/dist/app/subagents-model.js +224 -0
  131. package/dist/app/subagents-widget-controller.d.ts +43 -0
  132. package/dist/app/subagents-widget-controller.js +311 -0
  133. package/dist/app/tab-line-renderer.d.ts +26 -0
  134. package/dist/app/tab-line-renderer.js +222 -0
  135. package/dist/app/tabs-controller.d.ts +100 -0
  136. package/dist/app/tabs-controller.js +885 -0
  137. package/dist/app/terminal-controller.d.ts +40 -0
  138. package/dist/app/terminal-controller.js +135 -0
  139. package/dist/app/terminal-edit-shortcuts.d.ts +23 -0
  140. package/dist/app/terminal-edit-shortcuts.js +138 -0
  141. package/dist/app/terminal-output-buffer.d.ts +20 -0
  142. package/dist/app/terminal-output-buffer.js +52 -0
  143. package/dist/app/toast-controller.d.ts +13 -0
  144. package/dist/app/toast-controller.js +40 -0
  145. package/dist/app/toast-renderer.d.ts +9 -0
  146. package/dist/app/toast-renderer.js +79 -0
  147. package/dist/app/todo-model.d.ts +22 -0
  148. package/dist/app/todo-model.js +179 -0
  149. package/dist/app/todo-widget-controller.d.ts +21 -0
  150. package/dist/app/todo-widget-controller.js +59 -0
  151. package/dist/app/tool-block-renderer.d.ts +26 -0
  152. package/dist/app/tool-block-renderer.js +439 -0
  153. package/dist/app/types.d.ts +550 -0
  154. package/dist/app/types.js +1 -0
  155. package/dist/app/update.d.ts +36 -0
  156. package/dist/app/update.js +315 -0
  157. package/dist/app/voice-controller.d.ts +52 -0
  158. package/dist/app/voice-controller.js +600 -0
  159. package/dist/app/workspace-actions-controller.d.ts +40 -0
  160. package/dist/app/workspace-actions-controller.js +215 -0
  161. package/dist/app/workspace-undo.d.ts +44 -0
  162. package/dist/app/workspace-undo.js +215 -0
  163. package/dist/config.d.ts +62 -0
  164. package/dist/config.js +527 -0
  165. package/dist/context-progress-bar.d.ts +17 -0
  166. package/dist/context-progress-bar.js +48 -0
  167. package/dist/default-pix-config.d.ts +1 -0
  168. package/dist/default-pix-config.js +375 -0
  169. package/dist/fuzzy.d.ts +23 -0
  170. package/dist/fuzzy.js +165 -0
  171. package/dist/input-editor-files.d.ts +15 -0
  172. package/dist/input-editor-files.js +62 -0
  173. package/dist/input-editor.d.ts +186 -0
  174. package/dist/input-editor.js +835 -0
  175. package/dist/main.d.ts +1 -0
  176. package/dist/main.js +12 -0
  177. package/dist/markdown-format.d.ts +22 -0
  178. package/dist/markdown-format.js +542 -0
  179. package/dist/sdk.d.ts +3 -0
  180. package/dist/sdk.js +1 -0
  181. package/dist/syntax-highlight.d.ts +22 -0
  182. package/dist/syntax-highlight.js +528 -0
  183. package/dist/terminal-width.d.ts +6 -0
  184. package/dist/terminal-width.js +245 -0
  185. package/dist/theme.d.ts +56 -0
  186. package/dist/theme.js +118 -0
  187. package/dist/tool-renderers/apply-patch.d.ts +2 -0
  188. package/dist/tool-renderers/apply-patch.js +36 -0
  189. package/dist/tool-renderers/ast.d.ts +2 -0
  190. package/dist/tool-renderers/ast.js +5 -0
  191. package/dist/tool-renderers/compress.d.ts +2 -0
  192. package/dist/tool-renderers/compress.js +126 -0
  193. package/dist/tool-renderers/index.d.ts +3 -0
  194. package/dist/tool-renderers/index.js +44 -0
  195. package/dist/tool-renderers/question.d.ts +2 -0
  196. package/dist/tool-renderers/question.js +88 -0
  197. package/dist/tool-renderers/read.d.ts +2 -0
  198. package/dist/tool-renderers/read.js +36 -0
  199. package/dist/tool-renderers/repo.d.ts +2 -0
  200. package/dist/tool-renderers/repo.js +13 -0
  201. package/dist/tool-renderers/shell.d.ts +3 -0
  202. package/dist/tool-renderers/shell.js +27 -0
  203. package/dist/tool-renderers/skill.d.ts +2 -0
  204. package/dist/tool-renderers/skill.js +132 -0
  205. package/dist/tool-renderers/subagents.d.ts +2 -0
  206. package/dist/tool-renderers/subagents.js +51 -0
  207. package/dist/tool-renderers/todo.d.ts +2 -0
  208. package/dist/tool-renderers/todo.js +9 -0
  209. package/dist/tool-renderers/types.d.ts +42 -0
  210. package/dist/tool-renderers/types.js +1 -0
  211. package/dist/tool-renderers/utils.d.ts +31 -0
  212. package/dist/tool-renderers/utils.js +230 -0
  213. package/dist/tool-renderers/web.d.ts +3 -0
  214. package/dist/tool-renderers/web.js +9 -0
  215. package/dist/tool-renderers/write.d.ts +2 -0
  216. package/dist/tool-renderers/write.js +42 -0
  217. package/dist/ui.d.ts +56 -0
  218. package/dist/ui.js +94 -0
  219. package/docs/release.md +81 -0
  220. package/extensions/question/contract.ts +100 -0
  221. package/extensions/question/index.ts +34 -0
  222. package/extensions/question/render.ts +28 -0
  223. package/extensions/question/result.ts +86 -0
  224. package/extensions/question/tool-description.ts +11 -0
  225. package/extensions/question/tui.ts +629 -0
  226. package/extensions/question/types.ts +123 -0
  227. package/extensions/session-title/config.ts +169 -0
  228. package/extensions/session-title/index.ts +459 -0
  229. package/extensions/terminal-bell/index.ts +315 -0
  230. package/external/pi-tools-suite/README.md +242 -0
  231. package/external/pi-tools-suite/index.ts +1 -0
  232. package/external/pi-tools-suite/licenses/ollama-pi-web-search.MIT +21 -0
  233. package/external/pi-tools-suite/licenses/opencode-mystatus-MIT.txt +21 -0
  234. package/external/pi-tools-suite/package.json +53 -0
  235. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +194 -0
  236. package/external/pi-tools-suite/src/antigravity-auth/commands.ts +80 -0
  237. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +26 -0
  238. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +20 -0
  239. package/external/pi-tools-suite/src/antigravity-auth/index.ts +104 -0
  240. package/external/pi-tools-suite/src/antigravity-auth/models.ts +86 -0
  241. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +305 -0
  242. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +423 -0
  243. package/external/pi-tools-suite/src/antigravity-auth/status.ts +78 -0
  244. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +302 -0
  245. package/external/pi-tools-suite/src/antigravity-auth/types.ts +113 -0
  246. package/external/pi-tools-suite/src/ast-grep/index.ts +6 -0
  247. package/external/pi-tools-suite/src/ast-grep/render.ts +70 -0
  248. package/external/pi-tools-suite/src/ast-grep/schema.ts +109 -0
  249. package/external/pi-tools-suite/src/ast-grep/tool.ts +345 -0
  250. package/external/pi-tools-suite/src/ast-grep/types.ts +55 -0
  251. package/external/pi-tools-suite/src/ast-grep/utils.ts +65 -0
  252. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +222 -0
  253. package/external/pi-tools-suite/src/async-subagents/commands.ts +518 -0
  254. package/external/pi-tools-suite/src/async-subagents/constants.ts +11 -0
  255. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +74 -0
  256. package/external/pi-tools-suite/src/async-subagents/core/attachment-bridge.ts +133 -0
  257. package/external/pi-tools-suite/src/async-subagents/core/cleanup.ts +66 -0
  258. package/external/pi-tools-suite/src/async-subagents/core/concurrency.ts +90 -0
  259. package/external/pi-tools-suite/src/async-subagents/core/config.ts +819 -0
  260. package/external/pi-tools-suite/src/async-subagents/core/log-limits.ts +166 -0
  261. package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +133 -0
  262. package/external/pi-tools-suite/src/async-subagents/core/paths.ts +47 -0
  263. package/external/pi-tools-suite/src/async-subagents/core/pi-invocation.ts +35 -0
  264. package/external/pi-tools-suite/src/async-subagents/core/presets.ts +67 -0
  265. package/external/pi-tools-suite/src/async-subagents/core/process.ts +15 -0
  266. package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +66 -0
  267. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +224 -0
  268. package/external/pi-tools-suite/src/async-subagents/core/retry.ts +191 -0
  269. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +259 -0
  270. package/external/pi-tools-suite/src/async-subagents/core/sessions.ts +138 -0
  271. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +688 -0
  272. package/external/pi-tools-suite/src/async-subagents/core/state.ts +281 -0
  273. package/external/pi-tools-suite/src/async-subagents/core/stop.ts +131 -0
  274. package/external/pi-tools-suite/src/async-subagents/core/structured-result.ts +237 -0
  275. package/external/pi-tools-suite/src/async-subagents/core/tool-guard.ts +34 -0
  276. package/external/pi-tools-suite/src/async-subagents/core/types.ts +150 -0
  277. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +184 -0
  278. package/external/pi-tools-suite/src/async-subagents/core/utils.ts +11 -0
  279. package/external/pi-tools-suite/src/async-subagents/format.ts +41 -0
  280. package/external/pi-tools-suite/src/async-subagents/index.ts +422 -0
  281. package/external/pi-tools-suite/src/async-subagents/lib.ts +88 -0
  282. package/external/pi-tools-suite/src/async-subagents/live.ts +10 -0
  283. package/external/pi-tools-suite/src/async-subagents/polling.ts +83 -0
  284. package/external/pi-tools-suite/src/async-subagents/render.ts +230 -0
  285. package/external/pi-tools-suite/src/async-subagents/subagent-overlay.ts +77 -0
  286. package/external/pi-tools-suite/src/async-subagents/tasks.ts +120 -0
  287. package/external/pi-tools-suite/src/async-subagents/tools/cleanup.ts +99 -0
  288. package/external/pi-tools-suite/src/async-subagents/tools/result.ts +179 -0
  289. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +372 -0
  290. package/external/pi-tools-suite/src/async-subagents/tools/status.ts +60 -0
  291. package/external/pi-tools-suite/src/async-subagents/tools/stop.ts +79 -0
  292. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +152 -0
  293. package/external/pi-tools-suite/src/async-subagents/tools/wait.ts +67 -0
  294. package/external/pi-tools-suite/src/async-subagents/types.ts +45 -0
  295. package/external/pi-tools-suite/src/async-subagents/ui.ts +5 -0
  296. package/external/pi-tools-suite/src/compress/commands.ts +440 -0
  297. package/external/pi-tools-suite/src/compress/compress-tool.ts +368 -0
  298. package/external/pi-tools-suite/src/compress/compression-blocks.ts +524 -0
  299. package/external/pi-tools-suite/src/compress/config.ts +310 -0
  300. package/external/pi-tools-suite/src/compress/dcp-tui-filter.ts +498 -0
  301. package/external/pi-tools-suite/src/compress/index.ts +397 -0
  302. package/external/pi-tools-suite/src/compress/prompts.ts +269 -0
  303. package/external/pi-tools-suite/src/compress/pruner-candidates.ts +176 -0
  304. package/external/pi-tools-suite/src/compress/pruner-compression-blocks.ts +260 -0
  305. package/external/pi-tools-suite/src/compress/pruner-message-ids.ts +147 -0
  306. package/external/pi-tools-suite/src/compress/pruner-metadata.ts +268 -0
  307. package/external/pi-tools-suite/src/compress/pruner-nudge.ts +315 -0
  308. package/external/pi-tools-suite/src/compress/pruner-tools.ts +263 -0
  309. package/external/pi-tools-suite/src/compress/pruner-types.ts +25 -0
  310. package/external/pi-tools-suite/src/compress/pruner.ts +92 -0
  311. package/external/pi-tools-suite/src/compress/state.ts +486 -0
  312. package/external/pi-tools-suite/src/compress/ui.ts +308 -0
  313. package/external/pi-tools-suite/src/config.ts +176 -0
  314. package/external/pi-tools-suite/src/context-usage.ts +31 -0
  315. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +355 -0
  316. package/external/pi-tools-suite/src/index.ts +46 -0
  317. package/external/pi-tools-suite/src/lib/lsp.ts +62 -0
  318. package/external/pi-tools-suite/src/lib/project.ts +42 -0
  319. package/external/pi-tools-suite/src/lib/tool-args.ts +137 -0
  320. package/external/pi-tools-suite/src/lsp/_shared/config.ts +156 -0
  321. package/external/pi-tools-suite/src/lsp/_shared/glob.ts +60 -0
  322. package/external/pi-tools-suite/src/lsp/_shared/output.ts +102 -0
  323. package/external/pi-tools-suite/src/lsp/_shared/paths.ts +138 -0
  324. package/external/pi-tools-suite/src/lsp/_shared/runner.ts +64 -0
  325. package/external/pi-tools-suite/src/lsp/_shared/template.ts +23 -0
  326. package/external/pi-tools-suite/src/lsp/_shared/trust.ts +116 -0
  327. package/external/pi-tools-suite/src/lsp/_shared/types.ts +98 -0
  328. package/external/pi-tools-suite/src/lsp/async.ts +29 -0
  329. package/external/pi-tools-suite/src/lsp/child-process.ts +81 -0
  330. package/external/pi-tools-suite/src/lsp/client.ts +340 -0
  331. package/external/pi-tools-suite/src/lsp/constants.ts +9 -0
  332. package/external/pi-tools-suite/src/lsp/diagnostics-store.ts +64 -0
  333. package/external/pi-tools-suite/src/lsp/documents.ts +24 -0
  334. package/external/pi-tools-suite/src/lsp/index.ts +31 -0
  335. package/external/pi-tools-suite/src/lsp/lsp-utils.ts +37 -0
  336. package/external/pi-tools-suite/src/lsp/manager.ts +190 -0
  337. package/external/pi-tools-suite/src/lsp/mutation-events.ts +78 -0
  338. package/external/pi-tools-suite/src/lsp/renderer.ts +1 -0
  339. package/external/pi-tools-suite/src/lsp/tool-result.ts +6 -0
  340. package/external/pi-tools-suite/src/lsp/tsserver.ts +107 -0
  341. package/external/pi-tools-suite/src/lsp/types.ts +15 -0
  342. package/external/pi-tools-suite/src/model-tools/apply-patch.ts +590 -0
  343. package/external/pi-tools-suite/src/model-tools/index.ts +430 -0
  344. package/external/pi-tools-suite/src/model-tools/path-utils.ts +6 -0
  345. package/external/pi-tools-suite/src/model-tools/tool-args.ts +11 -0
  346. package/external/pi-tools-suite/src/prompt-commands/index.ts +349 -0
  347. package/external/pi-tools-suite/src/repo-discovery/index.ts +384 -0
  348. package/external/pi-tools-suite/src/repo-discovery/project.ts +7 -0
  349. package/external/pi-tools-suite/src/startup-section.ts +13 -0
  350. package/external/pi-tools-suite/src/terminal-bell/index.ts +309 -0
  351. package/external/pi-tools-suite/src/todo/index.ts +201 -0
  352. package/external/pi-tools-suite/src/todo/state/auto-clear.ts +13 -0
  353. package/external/pi-tools-suite/src/todo/state/invariants.ts +21 -0
  354. package/external/pi-tools-suite/src/todo/state/persistence.ts +94 -0
  355. package/external/pi-tools-suite/src/todo/state/replay.ts +38 -0
  356. package/external/pi-tools-suite/src/todo/state/selectors.ts +49 -0
  357. package/external/pi-tools-suite/src/todo/state/state-reducer.ts +362 -0
  358. package/external/pi-tools-suite/src/todo/state/state.ts +18 -0
  359. package/external/pi-tools-suite/src/todo/state/store.ts +52 -0
  360. package/external/pi-tools-suite/src/todo/state/task-graph.ts +57 -0
  361. package/external/pi-tools-suite/src/todo/todo.ts +487 -0
  362. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +143 -0
  363. package/external/pi-tools-suite/src/todo/tool/types.ts +188 -0
  364. package/external/pi-tools-suite/src/todo/view/format.ts +18 -0
  365. package/external/pi-tools-suite/src/todo/view/labels.ts +13 -0
  366. package/external/pi-tools-suite/src/tool-descriptions.ts +369 -0
  367. package/external/pi-tools-suite/src/usage/index.ts +152 -0
  368. package/external/pi-tools-suite/src/usage/lib/copilot.ts +535 -0
  369. package/external/pi-tools-suite/src/usage/lib/google.ts +478 -0
  370. package/external/pi-tools-suite/src/usage/lib/openai.ts +268 -0
  371. package/external/pi-tools-suite/src/usage/lib/types.ts +114 -0
  372. package/external/pi-tools-suite/src/usage/lib/utils.ts +134 -0
  373. package/external/pi-tools-suite/src/usage/lib/zhipu.ts +228 -0
  374. package/external/pi-tools-suite/src/web-search/index.ts +397 -0
  375. package/package.json +89 -0
  376. package/skills/context7/SKILL.md +69 -0
  377. package/skills/context7/scripts/context7.sh +73 -0
  378. package/skills/handoff/SKILL.md +15 -0
  379. package/skills/pdf/LICENSE.txt +30 -0
  380. package/skills/pdf/SKILL.md +314 -0
  381. package/skills/pdf/forms.md +294 -0
  382. package/skills/pdf/reference.md +612 -0
  383. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  384. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  385. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  386. package/skills/pdf/scripts/create_validation_image.py +37 -0
  387. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  388. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  389. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  390. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  391. package/skills/playwright-cli/SKILL.md +388 -0
  392. package/skills/playwright-cli/references/element-attributes.md +23 -0
  393. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  394. package/skills/playwright-cli/references/request-mocking.md +87 -0
  395. package/skills/playwright-cli/references/running-code.md +241 -0
  396. package/skills/playwright-cli/references/session-management.md +225 -0
  397. package/skills/playwright-cli/references/spec-driven-testing.md +305 -0
  398. package/skills/playwright-cli/references/storage-state.md +275 -0
  399. package/skills/playwright-cli/references/test-generation.md +134 -0
  400. package/skills/playwright-cli/references/tracing.md +139 -0
  401. package/skills/playwright-cli/references/video-recording.md +143 -0
  402. package/skills/simplify/SKILL.md +51 -0
  403. package/skills/skill-creator/LICENSE.txt +202 -0
  404. package/skills/skill-creator/SKILL.md +485 -0
  405. package/skills/skill-creator/agents/analyzer.md +274 -0
  406. package/skills/skill-creator/agents/comparator.md +202 -0
  407. package/skills/skill-creator/agents/grader.md +223 -0
  408. package/skills/skill-creator/assets/eval_review.html +146 -0
  409. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  410. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  411. package/skills/skill-creator/references/schemas.md +430 -0
  412. package/skills/skill-creator/scripts/__init__.py +0 -0
  413. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  414. package/skills/skill-creator/scripts/generate_report.py +326 -0
  415. package/skills/skill-creator/scripts/improve_description.py +247 -0
  416. package/skills/skill-creator/scripts/package_skill.py +136 -0
  417. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  418. package/skills/skill-creator/scripts/run_eval.py +310 -0
  419. package/skills/skill-creator/scripts/run_loop.py +328 -0
  420. package/skills/skill-creator/scripts/utils.py +47 -0
@@ -0,0 +1,819 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
6
+ import { ensurePiToolsSuiteUserConfig, getPiToolsSuiteUserConfigPath } from "../../config.js";
7
+ import type { AgentTask, RetryConfig } from "./types.js";
8
+
9
+ export interface SubagentTypeConfig {
10
+ description?: string;
11
+ model?: string;
12
+ /** Ordered model fallbacks used when the selected model hits quota/rate limits. */
13
+ fallbackModels?: string[];
14
+ thinking?: string;
15
+ tools?: string[];
16
+ extraArgs?: string[];
17
+ /** Extra prompt text appended after the generated or overridden prompt. */
18
+ promptAppend?: string;
19
+ /** Full prompt replacement. Supports prompt template variables. */
20
+ promptOverride?: string;
21
+ /** Retry configuration for agents of this type (overrides global retry). */
22
+ retry?: Partial<RetryConfig>;
23
+ /** Maximum bytes kept in result.json resultText; result.md remains the full raw output. */
24
+ maxResultBytes?: number;
25
+ /** Per-agent wall-clock timeout in milliseconds. */
26
+ timeoutMs?: number;
27
+ }
28
+
29
+ export interface SubagentRoutingConfig {
30
+ /** Ask a lightweight model to choose subagentType when a task omits it. */
31
+ enabled?: boolean;
32
+ /** Router model in provider/model form. Falls back to the current parent model if unavailable. */
33
+ model?: string;
34
+ /** Maximum task/scope characters sent to the router per task. */
35
+ maxTaskChars?: number;
36
+ /** Maximum router response tokens. */
37
+ maxTokens?: number;
38
+ /** Router complete() retries. */
39
+ maxRetries?: number;
40
+ /** Router sampling temperature. */
41
+ temperature?: number;
42
+ /** Router request timeout. */
43
+ timeoutMs?: number;
44
+ /** Show best-effort UI warnings when routing falls back. */
45
+ debug?: boolean;
46
+ }
47
+
48
+ export type ResolvedSubagentRoutingConfig = Required<SubagentRoutingConfig>;
49
+
50
+ export interface SubagentVisionConfig {
51
+ /** Glob-like model refs that should be treated as unable to inspect images, regardless of provider metadata. */
52
+ blindModelPatterns?: string[];
53
+ }
54
+
55
+ export interface SubagentPreset {
56
+ description?: string;
57
+ model?: string;
58
+ /** Ordered global model fallbacks used when this preset's selected model hits quota/rate limits. */
59
+ fallbackModels?: string[];
60
+ thinking?: string;
61
+ extraArgs?: string[];
62
+ /** Per-agent wall-clock timeout in milliseconds. */
63
+ timeoutMs?: number;
64
+ /** Optional per-subagentType overrides applied by this preset. */
65
+ types?: Record<string, SubagentPresetTypeOverride>;
66
+ }
67
+
68
+ export interface SubagentPresetTypeOverride {
69
+ model?: string;
70
+ /** Ordered per-role fallbacks used before preset-level fallbackModels. */
71
+ fallbackModels?: string[];
72
+ thinking?: string;
73
+ extraArgs?: string[];
74
+ /** Per-agent wall-clock timeout in milliseconds. */
75
+ timeoutMs?: number;
76
+ }
77
+
78
+ export interface SubagentConfig {
79
+ defaultType?: string;
80
+ types: Record<string, SubagentTypeConfig>;
81
+ /** LLM-based role routing for tasks that omit subagentType. */
82
+ routing?: SubagentRoutingConfig;
83
+ /** Vision capability overrides for parent-model guidance. */
84
+ vision?: SubagentVisionConfig;
85
+ /** Named global spawn defaults selected with /subagent-preset. */
86
+ presets?: Record<string, SubagentPreset>;
87
+ /** Maximum concurrent agents per spawn batch (default 5, 0 = unlimited). */
88
+ maxConcurrent?: number;
89
+ /** Global retry defaults for all agent types. Per-type retry overrides these. */
90
+ retry?: Partial<RetryConfig>;
91
+ /** Maximum bytes kept in result.json resultText globally; per-type maxResultBytes overrides. */
92
+ maxResultBytes?: number;
93
+ /** Global per-agent wall-clock timeout in milliseconds. Defaults to the built-in 30 minutes. */
94
+ timeoutMs?: number;
95
+ }
96
+
97
+ export interface CopySubagentConfigSampleResult {
98
+ copied: boolean;
99
+ targetPath: string;
100
+ samplePath: string;
101
+ existingFiles: string[];
102
+ }
103
+
104
+ export interface ResolvedAgentTaskConfig {
105
+ task: AgentTask;
106
+ extraArgs: string[];
107
+ /** Ordered model fallbacks for the resolved model. Current-process exhausted models are skipped before spawning. */
108
+ fallbackModels: string[];
109
+ profile?: SubagentTypeConfig;
110
+ /** Resolved retry config (merged from global + per-type). */
111
+ retry: RetryConfig;
112
+ /** Resolved max result bytes (per-type overrides global). */
113
+ maxResultBytes?: number;
114
+ /** Resolved per-agent wall-clock timeout in milliseconds. */
115
+ timeoutMs?: number;
116
+ }
117
+
118
+ export interface ResolveAgentTaskOptions {
119
+ /** Default model for spawned sub-agents when task/profile do not specify one. */
120
+ model?: string;
121
+ /** Default thinking level for spawned sub-agents when task/profile do not specify one. */
122
+ defaultThinking?: string;
123
+ /** Selected config preset. Supports global defaults plus per-subagentType overrides. */
124
+ preset?: SubagentPreset;
125
+ /** Forced thinking level, e.g. from the spawn action's global `thinking` parameter. */
126
+ thinking?: string;
127
+ extraArgs?: string[];
128
+ /** Force every sub-agent to use this model, ignoring task/profile/env model selection. */
129
+ forcedModel?: string;
130
+ /** Force a wall-clock timeout for every sub-agent spawned by this call. */
131
+ timeoutMs?: number;
132
+ }
133
+
134
+ const TRUE_ENV_PATTERN = /^(1|true|yes|on)$/i;
135
+ const FALSE_ENV_PATTERN = /^(0|false|no|off)$/i;
136
+
137
+ /** Default retry configuration: no retries, 2s base backoff. */
138
+ export const DEFAULT_RETRY_CONFIG: RetryConfig = {
139
+ maxRetries: 0,
140
+ backoffMs: 2000,
141
+ };
142
+
143
+ /** Default maximum concurrent agents per spawn batch. */
144
+ export const DEFAULT_MAX_CONCURRENT = 5;
145
+
146
+ /** Default lightweight LLM router used when subagentType is omitted. */
147
+ export const DEFAULT_ROUTING_CONFIG: ResolvedSubagentRoutingConfig = {
148
+ enabled: true,
149
+ model: "zai/glm-4.5-air",
150
+ maxTaskChars: 1200,
151
+ maxTokens: 512,
152
+ maxRetries: 1,
153
+ temperature: 0,
154
+ timeoutMs: 12_000,
155
+ debug: false,
156
+ };
157
+
158
+ const BUILTIN_CONFIG: SubagentConfig = {
159
+ maxConcurrent: DEFAULT_MAX_CONCURRENT,
160
+ routing: { ...DEFAULT_ROUTING_CONFIG },
161
+ vision: {
162
+ blindModelPatterns: ["zai/glm*", "glm*", "*/glm*"],
163
+ },
164
+ types: {
165
+ quick: {
166
+ description: "Use for tiny cheap tasks: answer a simple question, inspect one known file, or verify one fact. Not for broad repo search.",
167
+ thinking: "off",
168
+ },
169
+ scan: {
170
+ description: "Use for finding files, symbols, text, or inventory across a repo. Return paths/facts; do not judge code quality.",
171
+ thinking: "off",
172
+ },
173
+ research: {
174
+ description: "Use for multi-file codebase research: read several files and explain how something works. No edits.",
175
+ thinking: "low",
176
+ },
177
+ docs: {
178
+ description: "Use for documentation work: README/API docs review, docs gaps, changelog, migration notes, examples.",
179
+ thinking: "low",
180
+ },
181
+ frontend: {
182
+ description: "Use for frontend UI/UX visual work: styling, layout, typography, animation, responsive states, component polish, and accessibility. Avoid backend/business logic unless needed for UI behavior.",
183
+ thinking: "medium",
184
+ promptAppend: [
185
+ "Act as a frontend UI/UX engineer for visual and product-facing work.",
186
+ "Prioritize layout, typography, spacing, color, motion, responsive states, accessibility, and consistency with the existing design system.",
187
+ "Before editing, inspect nearby components/styles and infer the project's design language. Avoid backend/business-logic changes unless required for UI behavior.",
188
+ "When no mockup exists, choose a clear aesthetic direction and explain it briefly. Verify with targeted build/lint/tests or screenshot-relevant checks when possible.",
189
+ ].join("\n"),
190
+ },
191
+ implement: {
192
+ description: "Use when the sub-agent should make or plan code changes for a feature, bug fix, or refactor.",
193
+ thinking: "high",
194
+ },
195
+ tests: {
196
+ description: "Use for tests: locate coverage, find gaps, run/check targeted test commands, diagnose failing tests.",
197
+ thinking: "medium",
198
+ },
199
+ review: {
200
+ description: "Use for review/audit of existing code or changes: correctness, security, performance, maintainability, API risks, quality. Do not implement new code.",
201
+ thinking: "high",
202
+ },
203
+ deep: {
204
+ description: "Use for broad hard reasoning: architecture, root-cause analysis, cross-module impact, complex debugging or tradeoffs.",
205
+ thinking: "high",
206
+ },
207
+ vision: {
208
+ description: "Use only when task has imagePaths, screenshots, or asks to inspect visible UI/image content for a text-only parent.",
209
+ model: "openai-codex/gpt-5.4-mini",
210
+ thinking: "off",
211
+ promptAppend: [
212
+ "You are a vision helper for a parent model that may not be able to see images.",
213
+ "Inspect any attached images and any image paths mentioned in the task/scope. Describe concrete visible details, UI state, text, layout, errors, and uncertainties.",
214
+ "If focus instructions are provided, prioritize them, but still mention other important visible findings.",
215
+ "Do not make code changes. Return a compact visual description that the parent agent can rely on.",
216
+ ].join("\n"),
217
+ },
218
+ },
219
+ };
220
+
221
+ export function loadSubagentConfig(cwd: string, env: NodeJS.ProcessEnv = process.env): SubagentConfig {
222
+ const config = cloneConfig(BUILTIN_CONFIG);
223
+ if (!explicitSubagentConfigPath(cwd, env)) {
224
+ for (const file of piToolsSuiteConfigFiles(cwd, env)) {
225
+ mergeConfig(config, readPiToolsSuiteSubagentConfig(file));
226
+ }
227
+ }
228
+ for (const file of configFiles(cwd, env)) {
229
+ mergeConfig(config, readConfigFile(file));
230
+ }
231
+ applyEnvModelOverrides(config, env);
232
+ applyEnvRoutingOverrides(config, env);
233
+ return config;
234
+ }
235
+
236
+ export function configFiles(cwd: string, env: NodeJS.ProcessEnv = process.env): string[] {
237
+ const explicit = explicitSubagentConfigPath(cwd, env);
238
+ if (explicit) {
239
+ if (!fs.existsSync(explicit)) throw new Error(`Subagent config not found: ${explicit}`);
240
+ return [explicit];
241
+ }
242
+
243
+ return existingSubagentConfigFiles(cwd, env);
244
+ }
245
+
246
+ function explicitSubagentConfigPath(cwd: string, env: NodeJS.ProcessEnv): string | undefined {
247
+ const explicit = trimString(env.ASYNC_SUBAGENTS_CONFIG || env.PI_SUBAGENTS_CONFIG);
248
+ return explicit ? path.resolve(cwd, expandHome(explicit)) : undefined;
249
+ }
250
+
251
+ export function getDefaultSubagentConfigPath(): string {
252
+ return getPiToolsSuiteUserConfigPath();
253
+ }
254
+
255
+ export function getSubagentConfigSamplePath(): string {
256
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "async-subagents.sample.jsonc");
257
+ }
258
+
259
+ export function getSubagentConfigInitTargetPath(cwd: string, env: NodeJS.ProcessEnv = process.env): string {
260
+ return explicitSubagentConfigPath(cwd, env) ?? getDefaultSubagentConfigPath();
261
+ }
262
+
263
+ export function existingSubagentConfigFiles(cwd: string, env: NodeJS.ProcessEnv = process.env): string[] {
264
+ const explicit = explicitSubagentConfigPath(cwd, env);
265
+ if (explicit) return fs.existsSync(explicit) ? [explicit] : [];
266
+ return piToolsSuiteConfigFiles(cwd, env).filter(hasPiToolsSuiteSubagentConfig);
267
+ }
268
+
269
+ function piToolsSuiteConfigFiles(cwd: string, env: NodeJS.ProcessEnv): string[] {
270
+ return [
271
+ getPiToolsSuiteUserConfigPath(),
272
+ env.PI_CONFIG_DIR ? path.join(env.PI_CONFIG_DIR, "pi-tools-suite.jsonc") : undefined,
273
+ findProjectPiToolsSuiteConfig(cwd),
274
+ ].filter((file): file is string => typeof file === "string" && fs.existsSync(file));
275
+ }
276
+
277
+ function findProjectPiToolsSuiteConfig(startDir: string): string | undefined {
278
+ let dir = path.resolve(startDir);
279
+ const root = path.parse(dir).root;
280
+ while (true) {
281
+ const candidate = path.join(dir, ".pi", "pi-tools-suite.jsonc");
282
+ if (fs.existsSync(candidate)) return candidate;
283
+ if (dir === root) return undefined;
284
+ const parent = path.dirname(dir);
285
+ if (parent === dir) return undefined;
286
+ dir = parent;
287
+ }
288
+ }
289
+
290
+ export function copySubagentConfigSample(cwd: string, env: NodeJS.ProcessEnv = process.env): CopySubagentConfigSampleResult {
291
+ const samplePath = getSubagentConfigSamplePath();
292
+ const existingFiles = existingSubagentConfigFiles(cwd, env);
293
+ if (existingFiles.length > 0) return { copied: false, targetPath: existingFiles[0], samplePath, existingFiles };
294
+
295
+ const explicit = explicitSubagentConfigPath(cwd, env);
296
+ const targetPath = getSubagentConfigInitTargetPath(cwd, env);
297
+ if (explicit) {
298
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
299
+ try {
300
+ fs.copyFileSync(samplePath, targetPath, fs.constants.COPYFILE_EXCL);
301
+ } catch (error) {
302
+ if (isNodeError(error) && error.code === "EEXIST") {
303
+ return { copied: false, targetPath, samplePath, existingFiles: [targetPath] };
304
+ }
305
+ throw error;
306
+ }
307
+ } else {
308
+ writePiToolsSuiteSubagentConfig(targetPath, readConfigFile(samplePath));
309
+ }
310
+ return { copied: true, targetPath, samplePath, existingFiles: [] };
311
+ }
312
+
313
+ export function resolveAgentTaskConfig(
314
+ task: AgentTask,
315
+ config: SubagentConfig,
316
+ globalOptions: ResolveAgentTaskOptions = {},
317
+ ): ResolvedAgentTaskConfig {
318
+ const selectedType = selectSubagentType(task, config);
319
+ const profile = selectedType ? config.types[selectedType] : undefined;
320
+ const preset = globalOptions.preset;
321
+ const presetType = selectedType ? preset?.types?.[selectedType] : undefined;
322
+ const explicitType = trimString(task.subagentType);
323
+ const taskExtraArgs = arrayOfStrings(task.extraArgs) ?? [];
324
+ const profileExtraArgs = arrayOfStrings(profile?.extraArgs) ?? [];
325
+ const presetTypeExtraArgs = arrayOfStrings(presetType?.extraArgs) ?? [];
326
+ const presetExtraArgs = arrayOfStrings(preset?.extraArgs) ?? [];
327
+ const globalExtraArgs = arrayOfStrings(globalOptions.extraArgs) ?? [];
328
+ const promptAppend = joinTextBlocks(profile?.promptAppend, task.promptAppend);
329
+ const forcedModel = trimString(globalOptions.forcedModel);
330
+ const taskModel = trimString(task.model);
331
+ const presetTypeModel = trimString(presetType?.model);
332
+ const globalModel = trimString(globalOptions.model);
333
+ const presetModel = trimString(preset?.model);
334
+ const profileModel = trimString(profile?.model);
335
+ const model = forcedModel || taskModel || presetTypeModel || globalModel || presetModel || profileModel;
336
+ const fallbackModels = forcedModel || taskModel
337
+ ? []
338
+ : resolveFallbackModels({ model, presetType, preset, profile });
339
+ const extraArgs = forcedModel
340
+ ? stripModelArgs([...profileExtraArgs, ...presetTypeExtraArgs, ...taskExtraArgs, ...presetExtraArgs, ...globalExtraArgs])
341
+ : [...profileExtraArgs, ...presetTypeExtraArgs, ...taskExtraArgs, ...presetExtraArgs, ...globalExtraArgs];
342
+ const timeoutMs = task.timeoutMs ?? globalOptions.timeoutMs ?? presetType?.timeoutMs ?? preset?.timeoutMs ?? profile?.timeoutMs ?? config.timeoutMs;
343
+
344
+ return {
345
+ profile,
346
+ extraArgs,
347
+ fallbackModels,
348
+ retry: resolveRetryConfig(config.retry, profile?.retry),
349
+ maxResultBytes: profile?.maxResultBytes ?? config.maxResultBytes,
350
+ timeoutMs,
351
+ task: {
352
+ ...task,
353
+ subagentType: explicitType || selectedType,
354
+ model,
355
+ thinking: trimString(globalOptions.thinking) || trimString(task.thinking) || trimString(presetType?.thinking) || trimString(globalOptions.defaultThinking) || trimString(preset?.thinking) || trimString(profile?.thinking),
356
+ promptAppend,
357
+ promptOverride: trimString(task.promptOverride) || trimString(profile?.promptOverride),
358
+ tools: task.tools && task.tools.length > 0 ? task.tools : arrayOfStrings(profile?.tools),
359
+ extraArgs: taskExtraArgs.length > 0 ? taskExtraArgs : undefined,
360
+ },
361
+ };
362
+ }
363
+
364
+ export function resolveSubagentRoutingConfig(config: SubagentConfig): ResolvedSubagentRoutingConfig {
365
+ return { ...DEFAULT_ROUTING_CONFIG, ...(config.routing ?? {}) };
366
+ }
367
+
368
+ export function defaultSubagentType(config: SubagentConfig): string | undefined {
369
+ return trimString(config.defaultType) || Object.keys(config.types).find((name) => trimString(name));
370
+ }
371
+
372
+ /** Merge global and per-type retry partials into a fully resolved RetryConfig. Per-type wins. */
373
+ export function resolveRetryConfig(
374
+ globalRetry?: Partial<RetryConfig>,
375
+ typeRetry?: Partial<RetryConfig>,
376
+ ): RetryConfig {
377
+ return {
378
+ ...DEFAULT_RETRY_CONFIG,
379
+ ...stripUndefined(globalRetry),
380
+ ...stripUndefined(typeRetry),
381
+ };
382
+ }
383
+
384
+ export function shouldForceCurrentSubagentModel(env: NodeJS.ProcessEnv = process.env): boolean {
385
+ return [
386
+ env.ASYNC_SUBAGENTS_FORCE_CURRENT_MODEL,
387
+ env.PI_SUBAGENTS_FORCE_CURRENT_MODEL,
388
+ env.ASYNC_SUBAGENTS_USE_CURRENT_MODEL,
389
+ env.PI_SUBAGENTS_USE_CURRENT_MODEL,
390
+ ].some((value) => typeof value === "string" && TRUE_ENV_PATTERN.test(value.trim()));
391
+ }
392
+
393
+ export function currentModelRef(model: unknown): string | undefined {
394
+ if (!isRecord(model)) return undefined;
395
+ const id = trimString(model.id);
396
+ if (!id) return undefined;
397
+ const provider = trimString(model.provider);
398
+ return provider && !id.includes("/") ? `${provider}/${id}` : id;
399
+ }
400
+
401
+ export function isBlindModelRef(modelRef: string | undefined, config: SubagentConfig): boolean {
402
+ if (!modelRef) return false;
403
+ return matchesAnyModelPattern(modelRef, config.vision?.blindModelPatterns ?? []);
404
+ }
405
+
406
+ export function selectSubagentType(task: AgentTask, config: SubagentConfig): string | undefined {
407
+ const explicit = trimString(task.subagentType);
408
+ if (explicit) return explicit;
409
+ return defaultSubagentType(config);
410
+ }
411
+
412
+ function readConfigFile(file: string): Partial<SubagentConfig> {
413
+ const raw = fs.readFileSync(file, "utf-8");
414
+ const parsed = JSON.parse(stripJsonComments(raw)) as unknown;
415
+ if (!isRecord(parsed)) throw new Error(`Subagent config must be an object: ${file}`);
416
+ return normalizeConfig(parsed, file);
417
+ }
418
+
419
+ function readPiToolsSuiteSubagentConfig(file: string): Partial<SubagentConfig> {
420
+ const raw = fs.readFileSync(file, "utf-8");
421
+ const parsed = parseJsonc(raw) as unknown;
422
+ if (!isRecord(parsed)) return {};
423
+ const section = parsed.asyncSubagents ?? parsed["async-subagents"] ?? parsed.subagents;
424
+ return isRecord(section) ? normalizeConfig(section, file) : {};
425
+ }
426
+
427
+ function hasPiToolsSuiteSubagentConfig(file: string): boolean {
428
+ const raw = fs.readFileSync(file, "utf-8");
429
+ const parsed = parseJsonc(raw) as unknown;
430
+ if (!isRecord(parsed)) return false;
431
+ return isRecord(parsed.asyncSubagents ?? parsed["async-subagents"] ?? parsed.subagents);
432
+ }
433
+
434
+ function writePiToolsSuiteSubagentConfig(file: string, config: Partial<SubagentConfig>): void {
435
+ ensurePiToolsSuiteUserConfig();
436
+ const original = fs.existsSync(file) ? fs.readFileSync(file, "utf-8") : "{}\n";
437
+ const edits = modify(original, ["asyncSubagents"], config, {
438
+ formattingOptions: { insertSpaces: true, tabSize: 2, eol: "\n" },
439
+ });
440
+ const updated = applyEdits(original, edits);
441
+ fs.mkdirSync(path.dirname(file), { recursive: true });
442
+ fs.writeFileSync(file, updated.endsWith("\n") ? updated : `${updated}\n`, "utf-8");
443
+ }
444
+
445
+ function normalizeConfig(value: Record<string, unknown>, file: string): Partial<SubagentConfig> {
446
+ const output: Partial<SubagentConfig> = {};
447
+ if (typeof value.defaultType === "string") output.defaultType = value.defaultType.trim();
448
+ if (typeof value.routing === "boolean") output.routing = { enabled: value.routing };
449
+ else if (isRecord(value.routing)) output.routing = normalizeRoutingConfig(value.routing);
450
+ const vision = normalizeVisionConfig(value);
451
+ if (vision) output.vision = vision;
452
+ const maxConcurrent = finiteNumber(value.maxConcurrent);
453
+ if (maxConcurrent !== undefined) output.maxConcurrent = Math.max(0, Math.round(maxConcurrent));
454
+ const maxResultBytes = finiteNumber(value.maxResultBytes);
455
+ if (maxResultBytes !== undefined) output.maxResultBytes = Math.max(0, Math.round(maxResultBytes));
456
+ const timeoutMs = positiveMilliseconds(value.timeoutMs);
457
+ if (timeoutMs !== undefined) output.timeoutMs = timeoutMs;
458
+ if (isRecord(value.retry)) output.retry = normalizeRetryConfig(value.retry);
459
+ if (value.presets !== undefined) {
460
+ if (!isRecord(value.presets)) throw new Error(`Subagent config "presets" must be an object: ${file}`);
461
+ const presets: Record<string, SubagentPreset> = {};
462
+ for (const [name, rawPreset] of Object.entries(value.presets)) {
463
+ if (!isRecord(rawPreset)) throw new Error(`Subagent preset "${name}" must be an object: ${file}`);
464
+ presets[name] = {
465
+ description: trimString(rawPreset.description),
466
+ model: trimString(rawPreset.model),
467
+ fallbackModels: modelList(rawPreset.fallbackModels, rawPreset.fallbackModel),
468
+ thinking: trimString(rawPreset.thinking),
469
+ extraArgs: arrayOfStrings(rawPreset.extraArgs),
470
+ timeoutMs: positiveMilliseconds(rawPreset.timeoutMs),
471
+ types: normalizePresetTypeOverrides(rawPreset.types, file, name),
472
+ };
473
+ }
474
+ output.presets = presets;
475
+ }
476
+ if (value.types === undefined) return output;
477
+ if (!isRecord(value.types)) throw new Error(`Subagent config "types" must be an object: ${file}`);
478
+
479
+ const types: Record<string, SubagentTypeConfig> = {};
480
+ for (const [name, rawProfile] of Object.entries(value.types)) {
481
+ if (!isRecord(rawProfile)) throw new Error(`Subagent type "${name}" must be an object: ${file}`);
482
+ types[name] = {
483
+ description: trimString(rawProfile.description),
484
+ model: trimString(rawProfile.model),
485
+ fallbackModels: modelList(rawProfile.fallbackModels, rawProfile.fallbackModel),
486
+ thinking: trimString(rawProfile.thinking),
487
+ tools: arrayOfStrings(rawProfile.tools),
488
+ extraArgs: arrayOfStrings(rawProfile.extraArgs),
489
+ promptAppend: textBlock(rawProfile.promptAppend),
490
+ promptOverride: textBlock(rawProfile.promptOverride),
491
+ retry: isRecord(rawProfile.retry) ? normalizeRetryConfig(rawProfile.retry) : undefined,
492
+ maxResultBytes: finiteNumber(rawProfile.maxResultBytes) !== undefined ? Math.max(0, Math.round(finiteNumber(rawProfile.maxResultBytes)!)) : undefined,
493
+ timeoutMs: positiveMilliseconds(rawProfile.timeoutMs),
494
+ };
495
+ }
496
+ output.types = types;
497
+ return output;
498
+ }
499
+
500
+ function mergeConfig(target: SubagentConfig, source: Partial<SubagentConfig>): void {
501
+ if (source.defaultType) target.defaultType = source.defaultType;
502
+ if (source.routing) target.routing = { ...(target.routing ?? {}), ...source.routing };
503
+ if (source.vision) target.vision = { ...(target.vision ?? {}), ...compactVisionConfig(source.vision) };
504
+ if (source.maxConcurrent !== undefined) target.maxConcurrent = source.maxConcurrent;
505
+ if (source.maxResultBytes !== undefined) target.maxResultBytes = source.maxResultBytes;
506
+ if (source.timeoutMs !== undefined) target.timeoutMs = source.timeoutMs;
507
+ if (source.retry) target.retry = { ...(target.retry ?? {}), ...source.retry };
508
+ for (const [name, profile] of Object.entries(source.types ?? {})) {
509
+ target.types[name] = { ...(target.types[name] ?? {}), ...compactProfile(profile) };
510
+ }
511
+ for (const [name, preset] of Object.entries(source.presets ?? {})) {
512
+ target.presets = target.presets ?? {};
513
+ target.presets[name] = { ...(target.presets[name] ?? {}), ...compactPreset(preset) };
514
+ }
515
+ }
516
+
517
+ function normalizeRoutingConfig(value: Record<string, unknown>): SubagentRoutingConfig {
518
+ const routing: SubagentRoutingConfig = {};
519
+ if (typeof value.enabled === "boolean") routing.enabled = value.enabled;
520
+ if (typeof value.model === "string" && value.model.trim()) routing.model = value.model.trim();
521
+ if (typeof value.debug === "boolean") routing.debug = value.debug;
522
+ const maxTaskChars = finiteNumber(value.maxTaskChars);
523
+ if (maxTaskChars !== undefined) routing.maxTaskChars = Math.max(100, Math.round(maxTaskChars));
524
+ const maxTokens = finiteNumber(value.maxTokens);
525
+ if (maxTokens !== undefined) routing.maxTokens = Math.max(8, Math.round(maxTokens));
526
+ const maxRetries = finiteNumber(value.maxRetries);
527
+ if (maxRetries !== undefined) routing.maxRetries = Math.max(0, Math.round(maxRetries));
528
+ const temperature = finiteNumber(value.temperature);
529
+ if (temperature !== undefined) routing.temperature = Math.min(2, Math.max(0, temperature));
530
+ const timeoutMs = finiteNumber(value.timeoutMs);
531
+ if (timeoutMs !== undefined) routing.timeoutMs = Math.max(1000, Math.round(timeoutMs));
532
+ return routing;
533
+ }
534
+
535
+ function normalizeVisionConfig(value: Record<string, unknown>): SubagentVisionConfig | undefined {
536
+ const rawVision = value.vision;
537
+ const output: SubagentVisionConfig = {};
538
+ if (isRecord(rawVision)) {
539
+ const patterns = patternList(rawVision.blindModelPatterns, rawVision.blindModelPattern, rawVision.blindModels, rawVision.blindModelMasks, rawVision.blindModelMask);
540
+ if (patterns) output.blindModelPatterns = patterns;
541
+ }
542
+ const topLevelPatterns = patternList(value.blindModelPatterns, value.blindModelPattern, value.blindModels, value.blindModelMasks, value.blindModelMask);
543
+ if (topLevelPatterns) output.blindModelPatterns = topLevelPatterns;
544
+ return output.blindModelPatterns !== undefined ? output : undefined;
545
+ }
546
+
547
+ function matchesAnyModelPattern(modelRef: string, patterns: string[]): boolean {
548
+ return patterns.some((pattern) => modelPatternRegExp(pattern).test(modelRef));
549
+ }
550
+
551
+ function modelPatternRegExp(pattern: string): RegExp {
552
+ const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/\*/g, ".*");
553
+ return new RegExp(`^${escaped}$`, "i");
554
+ }
555
+
556
+ function compactProfile(profile: SubagentTypeConfig): SubagentTypeConfig {
557
+ const compact: SubagentTypeConfig = {};
558
+ if (profile.description) compact.description = profile.description;
559
+ if (profile.model) compact.model = profile.model;
560
+ if (profile.fallbackModels && profile.fallbackModels.length > 0) compact.fallbackModels = profile.fallbackModels;
561
+ if (profile.thinking) compact.thinking = profile.thinking;
562
+ if (profile.tools && profile.tools.length > 0) compact.tools = profile.tools;
563
+ if (profile.extraArgs && profile.extraArgs.length > 0) compact.extraArgs = profile.extraArgs;
564
+ if (profile.promptAppend) compact.promptAppend = profile.promptAppend;
565
+ if (profile.promptOverride) compact.promptOverride = profile.promptOverride;
566
+ if (profile.retry) compact.retry = profile.retry;
567
+ if (profile.maxResultBytes !== undefined) compact.maxResultBytes = profile.maxResultBytes;
568
+ if (profile.timeoutMs !== undefined) compact.timeoutMs = profile.timeoutMs;
569
+ return compact;
570
+ }
571
+
572
+ function compactPreset(preset: SubagentPreset): SubagentPreset {
573
+ const compact: SubagentPreset = {};
574
+ if (preset.description) compact.description = preset.description;
575
+ if (preset.model) compact.model = preset.model;
576
+ if (preset.fallbackModels && preset.fallbackModels.length > 0) compact.fallbackModels = preset.fallbackModels;
577
+ if (preset.thinking) compact.thinking = preset.thinking;
578
+ if (preset.extraArgs && preset.extraArgs.length > 0) compact.extraArgs = preset.extraArgs;
579
+ if (preset.timeoutMs !== undefined) compact.timeoutMs = preset.timeoutMs;
580
+ if (preset.types && Object.keys(preset.types).length > 0) compact.types = preset.types;
581
+ return compact;
582
+ }
583
+
584
+ function compactVisionConfig(vision: SubagentVisionConfig): SubagentVisionConfig {
585
+ const compact: SubagentVisionConfig = {};
586
+ if (vision.blindModelPatterns) compact.blindModelPatterns = vision.blindModelPatterns;
587
+ return compact;
588
+ }
589
+
590
+ function normalizePresetTypeOverrides(value: unknown, file: string, presetName: string): Record<string, SubagentPresetTypeOverride> | undefined {
591
+ if (value === undefined) return undefined;
592
+ if (!isRecord(value)) throw new Error(`Subagent preset "${presetName}" types must be an object: ${file}`);
593
+ const types: Record<string, SubagentPresetTypeOverride> = {};
594
+ for (const [name, rawOverride] of Object.entries(value)) {
595
+ if (!isRecord(rawOverride)) throw new Error(`Subagent preset "${presetName}" type override "${name}" must be an object: ${file}`);
596
+ const override: SubagentPresetTypeOverride = {};
597
+ const model = trimString(rawOverride.model);
598
+ const fallbackModels = modelList(rawOverride.fallbackModels, rawOverride.fallbackModel);
599
+ const thinking = trimString(rawOverride.thinking);
600
+ const extraArgs = arrayOfStrings(rawOverride.extraArgs);
601
+ const timeoutMs = positiveMilliseconds(rawOverride.timeoutMs);
602
+ if (model) override.model = model;
603
+ if (fallbackModels && fallbackModels.length > 0) override.fallbackModels = fallbackModels;
604
+ if (thinking) override.thinking = thinking;
605
+ if (extraArgs && extraArgs.length > 0) override.extraArgs = extraArgs;
606
+ if (timeoutMs !== undefined) override.timeoutMs = timeoutMs;
607
+ if (override.model || override.fallbackModels || override.thinking || override.extraArgs || override.timeoutMs !== undefined) types[name] = override;
608
+ }
609
+ return Object.keys(types).length > 0 ? types : undefined;
610
+ }
611
+
612
+ function resolveFallbackModels(options: {
613
+ model?: string;
614
+ presetType?: SubagentPresetTypeOverride;
615
+ preset?: SubagentPreset;
616
+ profile?: SubagentTypeConfig;
617
+ }): string[] {
618
+ const fallbacks = [
619
+ ...(options.presetType?.fallbackModels ?? []),
620
+ ...(options.preset?.fallbackModels ?? []),
621
+ ...(options.profile?.fallbackModels ?? []),
622
+ ];
623
+ const seen = new Set<string>();
624
+ if (options.model) seen.add(options.model);
625
+ const result: string[] = [];
626
+ for (const fallback of fallbacks) {
627
+ const model = trimString(fallback);
628
+ if (!model || seen.has(model)) continue;
629
+ seen.add(model);
630
+ result.push(model);
631
+ }
632
+ return result;
633
+ }
634
+
635
+ function applyEnvModelOverrides(config: SubagentConfig, env: NodeJS.ProcessEnv): void {
636
+ for (const [name, profile] of Object.entries(config.types)) {
637
+ const key = typeEnvKey(name);
638
+ const model = trimString(env[`ASYNC_SUBAGENTS_${key}_MODEL`] || env[`PI_SUBAGENTS_${key}_MODEL`]);
639
+ if (model) profile.model = model;
640
+ }
641
+ }
642
+
643
+ function applyEnvRoutingOverrides(config: SubagentConfig, env: NodeJS.ProcessEnv): void {
644
+ const routing = { ...DEFAULT_ROUTING_CONFIG, ...(config.routing ?? {}) };
645
+ const enabled = trimString(env.ASYNC_SUBAGENTS_ROUTING || env.PI_SUBAGENTS_ROUTING);
646
+ if (enabled) {
647
+ if (FALSE_ENV_PATTERN.test(enabled)) routing.enabled = false;
648
+ else if (TRUE_ENV_PATTERN.test(enabled)) routing.enabled = true;
649
+ }
650
+ const model = trimString(env.ASYNC_SUBAGENTS_ROUTING_MODEL || env.PI_SUBAGENTS_ROUTING_MODEL || env.ASYNC_SUBAGENTS_ROUTER_MODEL || env.PI_SUBAGENTS_ROUTER_MODEL);
651
+ if (model) routing.model = model;
652
+ const timeoutMs = finiteEnvNumber(env.ASYNC_SUBAGENTS_ROUTING_TIMEOUT_MS || env.PI_SUBAGENTS_ROUTING_TIMEOUT_MS);
653
+ if (timeoutMs !== undefined) routing.timeoutMs = Math.max(1000, Math.round(timeoutMs));
654
+ const debug = trimString(env.ASYNC_SUBAGENTS_ROUTING_DEBUG || env.PI_SUBAGENTS_ROUTING_DEBUG);
655
+ if (debug) routing.debug = TRUE_ENV_PATTERN.test(debug);
656
+ config.routing = routing;
657
+ }
658
+
659
+ function typeEnvKey(typeName: string): string {
660
+ return typeName.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
661
+ }
662
+
663
+ function stripJsonComments(input: string): string {
664
+ let output = "";
665
+ let inString = false;
666
+ let quote = "";
667
+ let escaped = false;
668
+ for (let i = 0; i < input.length; i++) {
669
+ const ch = input[i];
670
+ const next = input[i + 1];
671
+ if (inString) {
672
+ output += ch;
673
+ if (escaped) escaped = false;
674
+ else if (ch === "\\") escaped = true;
675
+ else if (ch === quote) inString = false;
676
+ continue;
677
+ }
678
+ if (ch === '"' || ch === "'") {
679
+ inString = true;
680
+ quote = ch;
681
+ output += ch;
682
+ continue;
683
+ }
684
+ if (ch === "/" && next === "/") {
685
+ while (i < input.length && input[i] !== "\n") i++;
686
+ output += "\n";
687
+ continue;
688
+ }
689
+ if (ch === "/" && next === "*") {
690
+ i += 2;
691
+ while (i < input.length && !(input[i] === "*" && input[i + 1] === "/")) i++;
692
+ i++;
693
+ continue;
694
+ }
695
+ output += ch;
696
+ }
697
+ return output.replace(/,\s*([}\]])/g, "$1");
698
+ }
699
+
700
+ function cloneConfig(config: SubagentConfig): SubagentConfig {
701
+ return JSON.parse(JSON.stringify(config)) as SubagentConfig;
702
+ }
703
+
704
+ function expandHome(value: string): string {
705
+ return value === "~" || value.startsWith("~/") ? path.join(os.homedir(), value.slice(2)) : value;
706
+ }
707
+
708
+ function trimString(value: unknown): string | undefined {
709
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
710
+ }
711
+
712
+ function finiteNumber(value: unknown): number | undefined {
713
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
714
+ }
715
+
716
+ function positiveMilliseconds(value: unknown): number | undefined {
717
+ const number = finiteNumber(value);
718
+ return number !== undefined ? Math.max(1, Math.round(number)) : undefined;
719
+ }
720
+
721
+ function finiteEnvNumber(value: unknown): number | undefined {
722
+ if (typeof value !== "string" || !value.trim()) return undefined;
723
+ const parsed = Number(value);
724
+ return Number.isFinite(parsed) ? parsed : undefined;
725
+ }
726
+
727
+ function isNodeError(error: unknown): error is NodeJS.ErrnoException {
728
+ return error instanceof Error && "code" in error;
729
+ }
730
+
731
+ function arrayOfStrings(value: unknown): string[] | undefined {
732
+ if (!Array.isArray(value)) return undefined;
733
+ const items = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
734
+ return items.length > 0 ? items : undefined;
735
+ }
736
+
737
+ function modelList(...values: unknown[]): string[] | undefined {
738
+ const seen = new Set<string>();
739
+ const models: string[] = [];
740
+ for (const value of values) {
741
+ const items = Array.isArray(value) ? value : [value];
742
+ for (const item of items) {
743
+ const model = trimString(item);
744
+ if (!model || seen.has(model)) continue;
745
+ seen.add(model);
746
+ models.push(model);
747
+ }
748
+ }
749
+ return models.length > 0 ? models : undefined;
750
+ }
751
+
752
+ function patternList(...values: unknown[]): string[] | undefined {
753
+ let sawArray = false;
754
+ const seen = new Set<string>();
755
+ const patterns: string[] = [];
756
+ for (const value of values) {
757
+ if (Array.isArray(value)) sawArray = true;
758
+ const items = Array.isArray(value) ? value : [value];
759
+ for (const item of items) {
760
+ const pattern = trimString(item);
761
+ if (!pattern || seen.has(pattern)) continue;
762
+ seen.add(pattern);
763
+ patterns.push(pattern);
764
+ }
765
+ }
766
+ return sawArray || patterns.length > 0 ? patterns : undefined;
767
+ }
768
+
769
+ function textBlock(value: unknown): string | undefined {
770
+ if (typeof value === "string") return trimString(value);
771
+ const lines = arrayOfStrings(value);
772
+ return lines && lines.length > 0 ? lines.join("\n") : undefined;
773
+ }
774
+
775
+ function joinTextBlocks(...values: Array<string | undefined>): string | undefined {
776
+ const parts = values.map((value) => trimString(value)).filter((value): value is string => Boolean(value));
777
+ return parts.length > 0 ? parts.join("\n\n") : undefined;
778
+ }
779
+
780
+ function normalizeRetryConfig(value: Record<string, unknown>): Partial<RetryConfig> {
781
+ const retry: Partial<RetryConfig> = {};
782
+ const maxRetries = finiteNumber(value.maxRetries);
783
+ if (maxRetries !== undefined) retry.maxRetries = Math.max(0, Math.round(maxRetries));
784
+ const backoffMs = finiteNumber(value.backoffMs);
785
+ if (backoffMs !== undefined) retry.backoffMs = Math.max(0, Math.round(backoffMs));
786
+ if (Array.isArray(value.retryableExitCodes)) {
787
+ retry.retryableExitCodes = value.retryableExitCodes
788
+ .filter((code): code is number => typeof code === "number" && Number.isFinite(code))
789
+ .map((code) => Math.round(code));
790
+ }
791
+ return retry;
792
+ }
793
+
794
+ function stripUndefined<T extends Record<string, unknown>>(obj?: Partial<T>): Partial<T> {
795
+ if (!obj) return {};
796
+ const result: Partial<T> = {};
797
+ for (const [key, value] of Object.entries(obj)) {
798
+ if (value !== undefined) (result as Record<string, unknown>)[key] = value;
799
+ }
800
+ return result;
801
+ }
802
+
803
+ function stripModelArgs(args: string[]): string[] {
804
+ const output: string[] = [];
805
+ for (let i = 0; i < args.length; i++) {
806
+ const arg = args[i];
807
+ if (arg === "--model" || arg === "-m") {
808
+ i++;
809
+ continue;
810
+ }
811
+ if (arg.startsWith("--model=")) continue;
812
+ output.push(arg);
813
+ }
814
+ return output;
815
+ }
816
+
817
+ function isRecord(value: unknown): value is Record<string, unknown> {
818
+ return typeof value === "object" && value !== null;
819
+ }