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,281 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { isDir } from "./paths.js";
4
+ import { readStructuredResult } from "./structured-result.js";
5
+ import type { AgentResult, AgentState, RpcEventRecord, RunState } from "./types.js";
6
+
7
+ const MAX_RPC_EVENT_LINE_CHARS = 1024 * 1024;
8
+
9
+ interface AgentStateReadOptions {
10
+ includeLineCounts?: boolean;
11
+ checkRpcPromptFailure?: boolean;
12
+ }
13
+
14
+ export function getAgentState(
15
+ runDir: string,
16
+ agentId: string,
17
+ options: AgentStateReadOptions = {},
18
+ ): AgentState | null {
19
+ const agentDir = path.join(runDir, agentId);
20
+ if (!isDir(agentDir)) return null;
21
+ if (!fs.existsSync(path.join(agentDir, "prompt.md"))) return null;
22
+ const includeLineCounts = options.includeLineCounts ?? true;
23
+ const checkRpcPromptFailure = options.checkRpcPromptFailure ?? true;
24
+
25
+ const state: AgentState = { id: agentId, status: "planned" };
26
+ const pid = readPid(path.join(agentDir, "pid"));
27
+ if (pid !== undefined) state.pid = pid;
28
+
29
+ const exitCodeFile = path.join(agentDir, "exit_code");
30
+ if (fs.existsSync(exitCodeFile)) {
31
+ const code = parseInt(fs.readFileSync(exitCodeFile, "utf-8").trim(), 10);
32
+ if (isNaN(code)) {
33
+ state.status = "stopped";
34
+ } else {
35
+ state.exitCode = code;
36
+ state.status = code === 0 ? "done" : "failed";
37
+ }
38
+ } else if (
39
+ checkRpcPromptFailure &&
40
+ hasRpcPromptFailure(path.join(agentDir, "events.jsonl"))
41
+ ) {
42
+ state.exitCode = 1;
43
+ state.status = "failed";
44
+ } else {
45
+ if (pid !== undefined) {
46
+ try {
47
+ process.kill(pid, 0);
48
+ state.status = "running";
49
+ } catch {
50
+ state.status = "stopped";
51
+ }
52
+ }
53
+ }
54
+
55
+ const startedAtFile = path.join(agentDir, "started_at");
56
+ if (fs.existsSync(startedAtFile)) {
57
+ state.startedAt = fs.readFileSync(startedAtFile, "utf-8").trim();
58
+ }
59
+
60
+ const finishedAtFile = path.join(agentDir, "finished_at");
61
+ if (fs.existsSync(finishedAtFile)) {
62
+ state.finishedAt = fs.readFileSync(finishedAtFile, "utf-8").trim();
63
+ }
64
+
65
+ const retryPendingFile = path.join(agentDir, "retry_pending");
66
+ const stopRequestedFile = path.join(agentDir, "stop_requested");
67
+ if (fs.existsSync(retryPendingFile) && !fs.existsSync(stopRequestedFile) && state.status !== "running" && state.status !== "done") {
68
+ state.status = "retrying";
69
+ const nextRetryAt = readTrimmed(path.join(agentDir, "next_retry_at"));
70
+ if (nextRetryAt) state.nextRetryAt = nextRetryAt;
71
+ }
72
+
73
+ if (includeLineCounts) {
74
+ const resultLines = countFileLines(path.join(agentDir, "result.md"));
75
+ if (resultLines !== undefined) state.resultLines = resultLines;
76
+
77
+ const stderrLines = countFileLines(path.join(agentDir, "stderr.log"));
78
+ if (stderrLines !== undefined && stderrLines > 0)
79
+ state.stderrLines = stderrLines;
80
+
81
+ const eventLines = countFileLines(path.join(agentDir, "events.jsonl"));
82
+ if (eventLines !== undefined) state.eventLines = eventLines;
83
+ }
84
+
85
+ // Read retry count if present.
86
+ const retryCountFile = path.join(agentDir, "retry_count");
87
+ if (fs.existsSync(retryCountFile)) {
88
+ const count = parseInt(fs.readFileSync(retryCountFile, "utf-8").trim(), 10);
89
+ if (!isNaN(count) && count > 0) state.retryCount = count;
90
+ }
91
+
92
+ return state;
93
+ }
94
+
95
+ function readTrimmed(filePath: string): string | undefined {
96
+ if (!fs.existsSync(filePath)) return undefined;
97
+ const value = fs.readFileSync(filePath, "utf-8").trim();
98
+ return value || undefined;
99
+ }
100
+
101
+ function readPid(pidFile: string): number | undefined {
102
+ if (!fs.existsSync(pidFile)) return undefined;
103
+ const pid = parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
104
+ return isNaN(pid) ? undefined : pid;
105
+ }
106
+
107
+ function hasRpcPromptFailure(eventsFile: string): boolean {
108
+ if (!fs.existsSync(eventsFile)) return false;
109
+ let fd: number | undefined;
110
+ try {
111
+ fd = fs.openSync(eventsFile, "r");
112
+ const buffer = Buffer.allocUnsafe(64 * 1024);
113
+ const decoder = new TextDecoder();
114
+ let pending = "";
115
+
116
+ while (true) {
117
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
118
+ if (bytesRead === 0) break;
119
+ pending += decoder.decode(buffer.subarray(0, bytesRead), { stream: true });
120
+ if (pending.length > MAX_RPC_EVENT_LINE_CHARS) return false;
121
+ const lines = pending.split(/\r?\n/);
122
+ pending = lines.pop() ?? "";
123
+ for (const line of lines) {
124
+ if (isRpcPromptFailureLine(line)) return true;
125
+ }
126
+ }
127
+
128
+ pending += decoder.decode();
129
+ if (isRpcPromptFailureLine(pending)) return true;
130
+ } catch {
131
+ return false;
132
+ } finally {
133
+ if (fd !== undefined) fs.closeSync(fd);
134
+ }
135
+ return false;
136
+ }
137
+
138
+ function isRpcPromptFailureLine(line: string): boolean {
139
+ if (!line.trim()) return false;
140
+ const event = JSON.parse(line) as RpcEventRecord;
141
+ return event.type === "response" && event.command === "prompt" && event.success === false;
142
+ }
143
+
144
+ function countFileLines(filePath: string): number | undefined {
145
+ if (!fs.existsSync(filePath)) return undefined;
146
+ let fd: number | undefined;
147
+ try {
148
+ fd = fs.openSync(filePath, "r");
149
+ const buffer = Buffer.allocUnsafe(64 * 1024);
150
+ let newlines = 0;
151
+ let bytesTotal = 0;
152
+ let lastByte: number | undefined;
153
+
154
+ while (true) {
155
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
156
+ if (bytesRead === 0) break;
157
+ bytesTotal += bytesRead;
158
+ for (let i = 0; i < bytesRead; i++) {
159
+ if (buffer[i] === 0x0a) newlines++;
160
+ }
161
+ lastByte = buffer[bytesRead - 1];
162
+ }
163
+
164
+ if (bytesTotal === 0) return 0;
165
+ return lastByte === 0x0a ? newlines : newlines + 1;
166
+ } catch {
167
+ return undefined;
168
+ } finally {
169
+ if (fd !== undefined) fs.closeSync(fd);
170
+ }
171
+ }
172
+
173
+ export function getRunState(
174
+ runDir: string,
175
+ filterIds?: string[],
176
+ options: AgentStateReadOptions = {},
177
+ ): RunState {
178
+ const agents: AgentState[] = [];
179
+ const launchedIds = new Set<string>();
180
+
181
+ if (!isDir(runDir)) return { runDir, agents };
182
+
183
+ // Read launched agent dirs
184
+ for (const entry of fs.readdirSync(runDir, { withFileTypes: true })) {
185
+ if (!entry.isDirectory()) continue;
186
+ if (!fs.existsSync(path.join(runDir, entry.name, "prompt.md"))) continue;
187
+ if (filterIds && !filterIds.includes(entry.name)) continue;
188
+ const state = getAgentState(runDir, entry.name, options);
189
+ if (state) {
190
+ agents.push(state);
191
+ launchedIds.add(entry.name);
192
+ }
193
+ }
194
+
195
+ // Check for planned agents (prompts without launched agent dirs)
196
+ const promptsDir = path.join(runDir, "prompts");
197
+ if (isDir(promptsDir)) {
198
+ for (const pf of fs.readdirSync(promptsDir)) {
199
+ if (!pf.endsWith(".md")) continue;
200
+ const id = pf.slice(0, -3);
201
+ if (launchedIds.has(id)) continue;
202
+ if (filterIds && !filterIds.includes(id)) continue;
203
+ agents.push({ id, status: "planned" });
204
+ }
205
+ }
206
+
207
+ return { runDir, agents };
208
+ }
209
+
210
+ export function readResult(
211
+ runDir: string,
212
+ agentId: string,
213
+ ): AgentResult | null {
214
+ const state = getAgentState(runDir, agentId);
215
+ if (!state) return null;
216
+
217
+ const agentDir = path.join(runDir, agentId);
218
+ let result: string | undefined;
219
+ let stderr: string | undefined;
220
+
221
+ const resultFile = path.join(agentDir, "result.md");
222
+ if (fs.existsSync(resultFile)) {
223
+ result = fs.readFileSync(resultFile, "utf-8");
224
+ }
225
+
226
+ const stderrFile = path.join(agentDir, "stderr.log");
227
+ if (fs.existsSync(stderrFile)) {
228
+ const content = fs.readFileSync(stderrFile, "utf-8");
229
+ if (content.trim()) stderr = content;
230
+ }
231
+
232
+ const structured = readStructuredResult(agentDir);
233
+
234
+ return { result, stderr, exitCode: state.exitCode, state, structured };
235
+ }
236
+
237
+ export async function waitForAgents(
238
+ runDir: string,
239
+ agentIds: string[] | undefined,
240
+ options: {
241
+ timeout?: number;
242
+ interval?: number;
243
+ failFast?: boolean;
244
+ signal?: AbortSignal;
245
+ } = {},
246
+ ): Promise<RunState> {
247
+ const timeout = options.timeout ?? 300;
248
+ const interval = options.interval ?? 3;
249
+ const failFast = options.failFast ?? false;
250
+
251
+ const start = Date.now();
252
+
253
+ while (true) {
254
+ const state = getRunState(runDir, agentIds);
255
+ const terminal = state.agents.filter(
256
+ (a) =>
257
+ a.status === "done" || a.status === "failed" || a.status === "stopped",
258
+ );
259
+
260
+ if (
261
+ failFast &&
262
+ terminal.some((a) => a.status === "failed" || a.status === "stopped")
263
+ ) {
264
+ return state;
265
+ }
266
+
267
+ if (state.agents.length === 0 || terminal.length === state.agents.length) {
268
+ return state;
269
+ }
270
+
271
+ if (Date.now() - start >= timeout * 1000) {
272
+ return state;
273
+ }
274
+
275
+ if (options.signal?.aborted) {
276
+ return state;
277
+ }
278
+
279
+ await new Promise((resolve) => setTimeout(resolve, interval * 1000));
280
+ }
281
+ }
@@ -0,0 +1,131 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { getAgentState, getRunState } from "./state.js";
4
+ import { terminateProcess } from "./process.js";
5
+ import { writeStructuredResult } from "./structured-result.js";
6
+ import type { AgentState } from "./types.js";
7
+ import { isoNow } from "./utils.js";
8
+
9
+ export type StopSignal = "SIGTERM" | "SIGINT" | "SIGKILL";
10
+
11
+ export interface StopAgentResult {
12
+ id: string;
13
+ previousStatus: AgentState["status"];
14
+ pid?: number;
15
+ stopped: boolean;
16
+ signal?: StopSignal;
17
+ message?: string;
18
+ error?: string;
19
+ }
20
+
21
+ const stopSignals = new Set<StopSignal>(["SIGTERM", "SIGINT", "SIGKILL"]);
22
+
23
+ export function validateStopSignal(signal: string): StopSignal {
24
+ if (stopSignals.has(signal as StopSignal)) return signal as StopSignal;
25
+ throw new Error(`Unsupported stop signal "${signal}". Use SIGTERM, SIGINT, or SIGKILL.`);
26
+ }
27
+
28
+ export function stopAgents(
29
+ runDir: string,
30
+ agentIds?: string[],
31
+ options: { signal?: StopSignal } = {},
32
+ ): StopAgentResult[] {
33
+ const signal = options.signal ?? "SIGTERM";
34
+ const state = getRunState(runDir, agentIds);
35
+
36
+ return state.agents.map((agent) => stopAgent(runDir, agent, signal));
37
+ }
38
+
39
+ function stopAgent(runDir: string, agent: AgentState, signal: StopSignal): StopAgentResult {
40
+ const result: StopAgentResult = {
41
+ id: agent.id,
42
+ previousStatus: agent.status,
43
+ pid: agent.pid,
44
+ stopped: false,
45
+ };
46
+
47
+ if (agent.status === "planned" || agent.status === "retrying") {
48
+ markStopped(runDir, agent.id, signal, agent.status === "planned" ? "Sub-agent stopped before launch." : "Sub-agent retry cancelled before relaunch.");
49
+ return {
50
+ ...result,
51
+ stopped: true,
52
+ signal,
53
+ message: `marked ${agent.status} agent stopped`,
54
+ };
55
+ }
56
+
57
+ if (agent.status !== "running") {
58
+ result.message = `agent is ${agent.status}`;
59
+ return result;
60
+ }
61
+
62
+ if (!agent.pid) {
63
+ markStopped(runDir, agent.id, signal);
64
+ return {
65
+ ...result,
66
+ stopped: true,
67
+ signal,
68
+ message: "running status had no pid; marked stopped",
69
+ };
70
+ }
71
+
72
+ try {
73
+ terminateProcess(agent.pid, signal);
74
+ markStopped(runDir, agent.id, signal);
75
+ return {
76
+ ...result,
77
+ stopped: true,
78
+ signal,
79
+ message: "stop signal sent",
80
+ };
81
+ } catch (error) {
82
+ const code = typeof error === "object" && error && "code" in error ? String((error as { code?: unknown }).code) : undefined;
83
+ if (code === "ESRCH") {
84
+ markStopped(runDir, agent.id, signal);
85
+ const refreshed = getAgentState(runDir, agent.id);
86
+ return {
87
+ ...result,
88
+ previousStatus: refreshed?.status ?? agent.status,
89
+ stopped: true,
90
+ signal,
91
+ message: "process was already gone; marked stopped",
92
+ };
93
+ }
94
+
95
+ return {
96
+ ...result,
97
+ error: error instanceof Error ? error.message : String(error),
98
+ };
99
+ }
100
+ }
101
+
102
+ function markStopped(runDir: string, agentId: string, signal: StopSignal, resultText = "Sub-agent stop requested."): void {
103
+ const agentDir = path.join(runDir, agentId);
104
+ const now = isoNow();
105
+ fs.mkdirSync(agentDir, { recursive: true });
106
+ ensurePromptFile(runDir, agentId, agentDir, resultText);
107
+ fs.writeFileSync(path.join(agentDir, "stop_requested"), now, "utf-8");
108
+ fs.writeFileSync(path.join(agentDir, "stop_signal"), signal, "utf-8");
109
+ fs.rmSync(path.join(agentDir, "retry_pending"), { force: true });
110
+ fs.rmSync(path.join(agentDir, "next_retry_at"), { force: true });
111
+ if (!fs.existsSync(path.join(agentDir, "result.md"))) fs.writeFileSync(path.join(agentDir, "result.md"), resultText, "utf-8");
112
+ fs.writeFileSync(path.join(agentDir, "exit_code"), "stopped", "utf-8");
113
+ fs.writeFileSync(path.join(agentDir, "finished_at"), now, "utf-8");
114
+ const state = getAgentState(runDir, agentId, { includeLineCounts: false }) ?? { id: agentId, status: "stopped" as const, finishedAt: now };
115
+ try {
116
+ writeStructuredResult({ agentDir, agentId, state });
117
+ } catch {
118
+ // Stop is best-effort; failure to write metadata must not hide the stop result.
119
+ }
120
+ }
121
+
122
+ function ensurePromptFile(runDir: string, agentId: string, agentDir: string, fallback: string): void {
123
+ const promptFile = path.join(agentDir, "prompt.md");
124
+ if (fs.existsSync(promptFile)) return;
125
+ const queuedPromptFile = path.join(runDir, "prompts", `${agentId}.md`);
126
+ if (fs.existsSync(queuedPromptFile)) {
127
+ fs.copyFileSync(queuedPromptFile, promptFile);
128
+ return;
129
+ }
130
+ fs.writeFileSync(promptFile, fallback, "utf-8");
131
+ }
@@ -0,0 +1,237 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { AgentState, StructuredFileReference, StructuredFinding, StructuredResult, StructuredRisk, StructuredSeverity } from "./types.js";
4
+
5
+ const DEFAULT_MAX_RESULT_BYTES = 100_000;
6
+ const STDERR_PREVIEW_CHARS = 500;
7
+
8
+ export interface WriteStructuredResultOptions {
9
+ agentDir: string;
10
+ agentId: string;
11
+ state: AgentState;
12
+ subagentType?: string;
13
+ model?: string;
14
+ maxResultBytes?: number;
15
+ }
16
+
17
+ /**
18
+ * Build a StructuredResult from the current agent state and files.
19
+ * Reads result.md and stderr.log from agentDir.
20
+ */
21
+ export function buildStructuredResult(opts: WriteStructuredResultOptions): StructuredResult {
22
+ const { agentDir, agentId, state, subagentType, model, maxResultBytes } = opts;
23
+ const maxBytes = maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES;
24
+
25
+ const structured: StructuredResult = {
26
+ schemaVersion: 2,
27
+ agentId,
28
+ status: state.status,
29
+ };
30
+
31
+ if (state.exitCode !== undefined) structured.exitCode = state.exitCode;
32
+ if (state.startedAt) structured.startedAt = state.startedAt;
33
+ if (state.finishedAt) structured.finishedAt = state.finishedAt;
34
+
35
+ // Compute duration
36
+ if (state.startedAt && state.finishedAt) {
37
+ const start = new Date(state.startedAt).getTime();
38
+ const end = new Date(state.finishedAt).getTime();
39
+ if (!isNaN(start) && !isNaN(end) && end >= start) {
40
+ structured.durationSeconds = Math.round((end - start) / 1000);
41
+ }
42
+ }
43
+
44
+ if (state.retryCount && state.retryCount > 0) structured.retryCount = state.retryCount;
45
+ if (subagentType) structured.subagentType = subagentType;
46
+ if (model) structured.model = model;
47
+
48
+ // Read result.md
49
+ const resultFile = path.join(agentDir, "result.md");
50
+ if (fs.existsSync(resultFile)) {
51
+ const fullText = fs.readFileSync(resultFile, "utf-8");
52
+ const originalBytes = Buffer.byteLength(fullText, "utf-8");
53
+ const structuredText = maxBytes > 0 && originalBytes > maxBytes
54
+ ? truncateToBytes(fullText, maxBytes)
55
+ : fullText;
56
+ if (maxBytes > 0 && originalBytes > maxBytes) {
57
+ // Truncate result.json's resultText at a character boundary close to the byte limit.
58
+ structured.resultText = structuredText;
59
+ structured.resultTruncated = true;
60
+ structured.resultOriginalBytes = originalBytes;
61
+ } else {
62
+ structured.resultText = structuredText;
63
+ }
64
+ Object.assign(structured, extractStructuredFields(fullText));
65
+ }
66
+
67
+ // Read stderr preview
68
+ const stderrFile = path.join(agentDir, "stderr.log");
69
+ if (fs.existsSync(stderrFile)) {
70
+ const stderr = fs.readFileSync(stderrFile, "utf-8").trim();
71
+ if (stderr) {
72
+ structured.stderrPreview = stderr.length > STDERR_PREVIEW_CHARS
73
+ ? stderr.slice(0, STDERR_PREVIEW_CHARS) + "..."
74
+ : stderr;
75
+ }
76
+ }
77
+
78
+ return structured;
79
+ }
80
+
81
+ function extractStructuredFields(text: string): Pick<StructuredResult, "summary" | "findings" | "files" | "risks" | "nextActions" | "confidence"> {
82
+ const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
83
+ const result: Pick<StructuredResult, "summary" | "findings" | "files" | "risks" | "nextActions" | "confidence"> = {};
84
+ const summary = firstSummaryLine(lines);
85
+ if (summary) result.summary = trimMarkdownMarker(summary);
86
+ const findings = extractFindings(lines);
87
+ if (findings.length > 0) result.findings = findings;
88
+ const files = extractFileReferences(text);
89
+ if (files.length > 0) result.files = files;
90
+ const risks = extractRisks(lines);
91
+ if (risks.length > 0) result.risks = risks;
92
+ const nextActions = extractNextActions(lines);
93
+ if (nextActions.length > 0) result.nextActions = nextActions;
94
+ const confidence = extractConfidence(text);
95
+ if (confidence) result.confidence = confidence;
96
+ return result;
97
+ }
98
+
99
+ function firstSummaryLine(lines: string[]): string | undefined {
100
+ for (const line of lines) {
101
+ const stripped = trimMarkdownMarker(line);
102
+ if (!stripped || /^#{1,6}\s/.test(line)) continue;
103
+ if (/^(summary|итог|вывод)\s*:?$/i.test(stripped)) continue;
104
+ return stripped.length > 300 ? `${stripped.slice(0, 297)}...` : stripped;
105
+ }
106
+ return undefined;
107
+ }
108
+
109
+ function extractFindings(lines: string[]): StructuredFinding[] {
110
+ const findings: StructuredFinding[] = [];
111
+ for (const line of lines) {
112
+ if (!/^([-*•]|\d+[.)])\s+/.test(line)) continue;
113
+ const text = trimMarkdownMarker(line);
114
+ if (!text) continue;
115
+ const finding: StructuredFinding = { text: text.length > 500 ? `${text.slice(0, 497)}...` : text };
116
+ const severity = extractSeverity(text);
117
+ if (severity) finding.severity = severity;
118
+ const file = extractFirstFileReference(text);
119
+ if (file) Object.assign(finding, file);
120
+ findings.push(finding);
121
+ if (findings.length >= 20) break;
122
+ }
123
+ return findings;
124
+ }
125
+
126
+ function extractRisks(lines: string[]): StructuredRisk[] {
127
+ const risks: StructuredRisk[] = [];
128
+ for (const line of lines) {
129
+ if (!/(risk|risky|danger|unsafe|security|vulnerab|ошиб|риск|опас|уязвим|критич)/i.test(line)) continue;
130
+ const text = trimMarkdownMarker(line);
131
+ if (!text) continue;
132
+ const risk: StructuredRisk = { text: text.length > 500 ? `${text.slice(0, 497)}...` : text };
133
+ const severity = extractSeverity(text);
134
+ if (severity) risk.severity = severity;
135
+ risks.push(risk);
136
+ if (risks.length >= 10) break;
137
+ }
138
+ return risks;
139
+ }
140
+
141
+ function extractNextActions(lines: string[]): string[] {
142
+ const actions: string[] = [];
143
+ for (const line of lines) {
144
+ if (!/(next|recommend|todo|fix|should|нужно|след|рекоменд|исправ|добав)/i.test(line)) continue;
145
+ const text = trimMarkdownMarker(line);
146
+ if (!text) continue;
147
+ actions.push(text.length > 500 ? `${text.slice(0, 497)}...` : text);
148
+ if (actions.length >= 10) break;
149
+ }
150
+ return actions;
151
+ }
152
+
153
+ function extractFileReferences(text: string): StructuredFileReference[] {
154
+ const refs = new Map<string, StructuredFileReference>();
155
+ const pattern = /(?:^|[\s`'"(])((?:[\w.-]+\/)+(?:[\w.-]+)(?::(\d+))?)/g;
156
+ let match: RegExpExecArray | null;
157
+ while ((match = pattern.exec(text))) {
158
+ const rawPath = match[1];
159
+ if (!/\.[A-Za-z0-9]+(?::\d+)?$/.test(rawPath)) continue;
160
+ const filePath = rawPath.replace(/:\d+$/, "");
161
+ const line = match[2] ? Number.parseInt(match[2], 10) : undefined;
162
+ const key = `${filePath}:${line ?? ""}`;
163
+ if (!refs.has(key)) refs.set(key, line ? { path: filePath, line } : { path: filePath });
164
+ if (refs.size >= 50) break;
165
+ }
166
+ return Array.from(refs.values());
167
+ }
168
+
169
+ function extractFirstFileReference(text: string): { file: string; line?: number } | undefined {
170
+ const [first] = extractFileReferences(text);
171
+ return first ? { file: first.path, line: first.line } : undefined;
172
+ }
173
+
174
+ function extractSeverity(text: string): StructuredSeverity | undefined {
175
+ if (/\b(critical|blocker|urgent|критич|блокер)\b/i.test(text)) return "critical";
176
+ if (/\b(high|major|высок)\b/i.test(text)) return "high";
177
+ if (/\b(medium|moderate|средн)\b/i.test(text)) return "medium";
178
+ if (/\b(low|minor|низк)\b/i.test(text)) return "low";
179
+ return undefined;
180
+ }
181
+
182
+ function extractConfidence(text: string): "low" | "medium" | "high" | undefined {
183
+ const match = /confidence\s*[:=-]\s*(low|medium|high)/i.exec(text);
184
+ return match ? match[1].toLowerCase() as "low" | "medium" | "high" : undefined;
185
+ }
186
+
187
+ function trimMarkdownMarker(line: string): string {
188
+ let stripped = line
189
+ .replace(/^#{1,6}\s+/, "")
190
+ .replace(/^([-*•]|\d+[.)])\s+/, "")
191
+ .trim();
192
+ const boldPrefix = /^\*\*(.*?)\*\*:?\s*(.*)$/.exec(stripped);
193
+ if (boldPrefix) stripped = `${boldPrefix[1].replace(/:$/, "")}: ${boldPrefix[2]}`.trim();
194
+ return stripped;
195
+ }
196
+
197
+ /**
198
+ * Write result.json to the agent directory.
199
+ */
200
+ export function writeStructuredResult(opts: WriteStructuredResultOptions): StructuredResult {
201
+ const structured = buildStructuredResult(opts);
202
+ const jsonPath = path.join(opts.agentDir, "result.json");
203
+ fs.writeFileSync(jsonPath, JSON.stringify(structured, null, 2), "utf-8");
204
+ return structured;
205
+ }
206
+
207
+ /**
208
+ * Read result.json from agent directory, if it exists.
209
+ */
210
+ export function readStructuredResult(agentDir: string): StructuredResult | undefined {
211
+ const jsonPath = path.join(agentDir, "result.json");
212
+ if (!fs.existsSync(jsonPath)) return undefined;
213
+ try {
214
+ return JSON.parse(fs.readFileSync(jsonPath, "utf-8")) as StructuredResult;
215
+ } catch {
216
+ return undefined;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Truncate a string so its UTF-8 byte length is at most maxBytes.
222
+ */
223
+ function truncateToBytes(text: string, maxBytes: number): string {
224
+ if (Buffer.byteLength(text, "utf-8") <= maxBytes) return text;
225
+ // Binary search for the right character count
226
+ let lo = 0;
227
+ let hi = text.length;
228
+ while (lo < hi) {
229
+ const mid = (lo + hi + 1) >> 1;
230
+ if (Buffer.byteLength(text.slice(0, mid), "utf-8") <= maxBytes) {
231
+ lo = mid;
232
+ } else {
233
+ hi = mid - 1;
234
+ }
235
+ }
236
+ return text.slice(0, lo);
237
+ }
@@ -0,0 +1,34 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+
3
+ export const SUBAGENT_DENIED_TOOLS = new Set([
4
+ "question",
5
+ "subagents",
6
+ "async_subagents_spawn",
7
+ "async_subagents_status",
8
+ "async_subagents_wait",
9
+ "async_subagents_result",
10
+ "async_subagents_stop",
11
+ "async_subagents_cleanup",
12
+ ]);
13
+
14
+ export function filterSubagentTools(tools: readonly string[] | undefined): string[] | undefined {
15
+ if (!tools) return undefined;
16
+ return tools.filter((tool) => !SUBAGENT_DENIED_TOOLS.has(tool));
17
+ }
18
+
19
+ export default function subagentToolGuard(pi: ExtensionAPI): void {
20
+ const toolApi = pi as ExtensionAPI & {
21
+ getActiveTools?: () => string[];
22
+ setActiveTools?: (tools: string[]) => void;
23
+ };
24
+
25
+ const applyGuard = () => {
26
+ const activeTools = toolApi.getActiveTools?.() ?? [];
27
+ const filtered = filterSubagentTools(activeTools) ?? [];
28
+ if (filtered.length === activeTools.length) return;
29
+ toolApi.setActiveTools?.(filtered);
30
+ };
31
+
32
+ pi.on("session_start", applyGuard);
33
+ pi.on("model_select", applyGuard);
34
+ }