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,308 @@
1
+ import type { ExtensionContext, ExtensionUIContext, Theme } from "@mariozechner/pi-coding-agent"
2
+ import type { DcpState } from "./state.js"
3
+
4
+ export interface DcpCompressionVisualDetails {
5
+ topic: string
6
+ blockIds: number[]
7
+ ranges: number
8
+ messages: number
9
+ itemCount: number
10
+ totalSummaryTokens: number
11
+ activeBlocks: number
12
+ totalBlocks: number
13
+ prunedTools: number
14
+ tokensSaved: number
15
+ contextTokens?: number | null
16
+ contextWindow?: number
17
+ contextPercent?: number | null
18
+ skippedMessages?: number
19
+ skippedMessageIssues?: string[]
20
+ }
21
+
22
+ export interface DcpContextUsage {
23
+ tokens: number | null
24
+ contextWindow: number
25
+ percent?: number | null
26
+ }
27
+
28
+ type RawDcpContextUsage = {
29
+ tokens?: number | null
30
+ contextWindow?: number
31
+ percent?: number | null
32
+ } | undefined
33
+
34
+ interface DcpVisualSnapshot {
35
+ manualMode: boolean
36
+ tokensSaved: number
37
+ prunedTools: number
38
+ activeBlocks: number
39
+ activeNudges: number
40
+ lastNudgeType?: string
41
+ }
42
+
43
+ function fg(theme: Theme | undefined, color: string, text: string): string {
44
+ return theme ? theme.fg(color as any, text) : text
45
+ }
46
+
47
+ function rgb(r: number, g: number, b: number, text: string): string {
48
+ return text ? `\x1b[38;2;${r};${g};${b}m${text}\x1b[39m` : ""
49
+ }
50
+
51
+ function contextUsageRgb(used: number, total: number): [number, number, number] {
52
+ const ratio = total > 0 ? Math.max(0, used) / total : 0
53
+ if (ratio <= 0.30) return [21, 128, 61]
54
+ if (ratio <= 0.50) return [161, 98, 7]
55
+ return [185, 28, 28]
56
+ }
57
+
58
+ function colorOccupiedContext(used: number, total: number, text: string): string {
59
+ return rgb(...contextUsageRgb(used, total), text)
60
+ }
61
+
62
+ function colorFreedContext(text: string): string {
63
+ return rgb(15, 118, 110, text)
64
+ }
65
+
66
+ function colorEmptyContext(text: string): string {
67
+ return rgb(55, 65, 81, text)
68
+ }
69
+
70
+ export function formatDcpTokenCount(n: number): string {
71
+ const safe = Math.max(0, Math.round(n || 0))
72
+ if (safe >= 1_000_000) return `${(safe / 1_000_000).toFixed(1)}M`
73
+ if (safe >= 1_000) return `${(safe / 1_000).toFixed(1)}K`
74
+ return String(safe)
75
+ }
76
+
77
+ function snapshotFromState(state: DcpState): DcpVisualSnapshot {
78
+ const activeBlocks = state.compressionBlocks.filter((block) => block.active)
79
+ return {
80
+ manualMode: state.manualMode,
81
+ tokensSaved: state.tokensSaved,
82
+ prunedTools: state.prunedToolIds.size,
83
+ activeBlocks: activeBlocks.length,
84
+ activeNudges: state.nudgeAnchors.length,
85
+ lastNudgeType: state.lastNudge?.type,
86
+ }
87
+ }
88
+
89
+ function formatNudgeType(type: string | undefined): string | undefined {
90
+ if (!type) return undefined
91
+ if (type === "context-strong") return "context!"
92
+ if (type === "context-soft") return "context"
93
+ return type
94
+ }
95
+
96
+ export function normalizeDcpContextUsage(usage: RawDcpContextUsage): DcpContextUsage | undefined {
97
+ if (!usage || typeof usage.contextWindow !== "number" || usage.contextWindow <= 0) return undefined
98
+ const contextWindow = usage.contextWindow
99
+ const percent = typeof usage.percent === "number" && Number.isFinite(usage.percent)
100
+ ? Math.max(0, usage.percent)
101
+ : usage.percent === null
102
+ ? null
103
+ : undefined
104
+ const tokens = typeof percent === "number"
105
+ ? Math.round((contextWindow * percent) / 100)
106
+ : typeof usage.tokens === "number"
107
+ ? usage.tokens
108
+ : null
109
+ return { tokens, contextWindow, percent }
110
+ }
111
+
112
+ function usageTokens(usage: DcpContextUsage): number {
113
+ if (usage.contextWindow > 0 && typeof usage.percent === "number" && Number.isFinite(usage.percent)) {
114
+ return Math.round((usage.contextWindow * Math.max(0, usage.percent)) / 100)
115
+ }
116
+ return usage.tokens ?? 0
117
+ }
118
+
119
+ function contextRatio(usage: DcpContextUsage | undefined): string | undefined {
120
+ if (!usage || typeof usage.contextWindow !== "number" || usage.contextWindow <= 0) return undefined
121
+ const used = usageTokens(usage)
122
+ return `${formatDcpTokenCount(used)}/${formatDcpTokenCount(usage.contextWindow)}`
123
+ }
124
+
125
+ function compressionContextScale(details: Pick<DcpCompressionVisualDetails, "contextTokens" | "contextWindow" | "contextPercent">): string | undefined {
126
+ const total = Math.max(0, Math.round(details.contextWindow ?? 0))
127
+ if (total <= 0) return undefined
128
+ if (typeof details.contextPercent === "number" && Number.isFinite(details.contextPercent)) {
129
+ return `${Math.max(0, details.contextPercent).toFixed(1)}%/${formatDcpTokenCount(total)}`
130
+ }
131
+ return `${formatDcpTokenCount(compressionContextTokens(details))}/${formatDcpTokenCount(total)}`
132
+ }
133
+
134
+ export function renderDcpStatusLabel(state: DcpState, theme?: Theme, usage?: DcpContextUsage): string {
135
+ const normalizedUsage = normalizeDcpContextUsage(usage)
136
+ const snapshot = snapshotFromState(state)
137
+ const parts: string[] = []
138
+
139
+ const ratio = contextRatio(normalizedUsage)
140
+ const headline = [
141
+ fg(theme, "accent", "DCP"),
142
+ ratio ? fg(theme, "customMessageText", ratio) : undefined,
143
+ normalizedUsage ? renderDcpUsageBar(theme, normalizedUsage, 14) : undefined,
144
+ snapshot.manualMode ? fg(theme, "warning", "manual") : undefined,
145
+ ].filter(Boolean).join(" ")
146
+ parts.push(headline)
147
+
148
+ if (snapshot.tokensSaved > 0) {
149
+ parts.push(`${fg(theme, "success", `-${formatDcpTokenCount(snapshot.tokensSaved)}`)} ${fg(theme, "dim", "saved")}`)
150
+ }
151
+
152
+ if (snapshot.prunedTools > 0) {
153
+ parts.push(`${fg(theme, "warning", String(snapshot.prunedTools))} ${fg(theme, "dim", "pruned")}`)
154
+ }
155
+
156
+ if (snapshot.activeBlocks > 0) {
157
+ parts.push(`${fg(theme, "accent", String(snapshot.activeBlocks))} ${fg(theme, "dim", snapshot.activeBlocks === 1 ? "block" : "blocks")}`)
158
+ }
159
+
160
+ const nudgeLabel = formatNudgeType(snapshot.lastNudgeType)
161
+ if (snapshot.activeNudges > 0 && nudgeLabel) {
162
+ parts.push(`${fg(theme, "warning", `nudge ${nudgeLabel}`)} ${fg(theme, "dim", `(${snapshot.activeNudges})`)}`)
163
+ }
164
+
165
+ return parts.join(fg(theme, "dim", " │ "))
166
+ }
167
+
168
+ function compressionContextTokens(details: Pick<DcpCompressionVisualDetails, "contextTokens" | "contextWindow" | "contextPercent">): number {
169
+ const total = Math.max(0, Math.round(details.contextWindow ?? 0))
170
+ if (total > 0 && typeof details.contextPercent === "number" && Number.isFinite(details.contextPercent)) {
171
+ return Math.round((total * Math.max(0, details.contextPercent)) / 100)
172
+ }
173
+ return Math.round(details.contextTokens ?? 0)
174
+ }
175
+
176
+ function contextParts(details: Pick<DcpCompressionVisualDetails, "contextTokens" | "contextWindow" | "contextPercent" | "tokensSaved">): { used: number; freed: number; empty: number; total: number } {
177
+ const total = Math.max(0, Math.round(details.contextWindow ?? 0))
178
+ if (total <= 0) {
179
+ const freed = Math.max(0, Math.round(details.tokensSaved ?? 0))
180
+ return { used: 0, freed, empty: 0, total: Math.max(1, freed) }
181
+ }
182
+
183
+ const footprint = Math.max(0, Math.min(total, compressionContextTokens(details)))
184
+ const freed = Math.max(0, Math.min(footprint, Math.round(details.tokensSaved ?? 0)))
185
+ const used = Math.max(0, footprint - freed)
186
+ const empty = Math.max(0, total - footprint)
187
+ return { used, freed, empty, total }
188
+ }
189
+
190
+ function singleLine(value: string): string {
191
+ return value.replace(/\s+/g, " ").trim()
192
+ }
193
+
194
+ function truncateLabel(value: string, maxChars: number): string {
195
+ const chars = Array.from(singleLine(value))
196
+ if (chars.length <= maxChars) return chars.join("")
197
+ if (maxChars <= 0) return ""
198
+ if (maxChars === 1) return "…"
199
+ return `${chars.slice(0, maxChars - 1).join("")}…`
200
+ }
201
+
202
+ function distributeBar(width: number, parts: { used: number; freed: number; empty: number; total: number }): { used: number; freed: number; empty: number } {
203
+ const safeWidth = Math.max(1, width)
204
+ const footprint = Math.max(0, parts.used + parts.freed)
205
+ const occupied = footprint > 0
206
+ ? Math.min(safeWidth, Math.max(1, Math.round((footprint / Math.max(1, parts.total)) * safeWidth)))
207
+ : 0
208
+ const empty = safeWidth - occupied
209
+ if (occupied === 0) return { used: 0, freed: 0, empty }
210
+ if (parts.used <= 0) return { used: 0, freed: occupied, empty }
211
+ if (parts.freed <= 0) return { used: occupied, freed: 0, empty }
212
+ if (occupied === 1) {
213
+ return parts.freed >= parts.used
214
+ ? { used: 0, freed: 1, empty }
215
+ : { used: 1, freed: 0, empty }
216
+ }
217
+ const used = Math.min(occupied - 1, Math.max(1, Math.round((parts.used / footprint) * occupied)))
218
+ return { used, freed: occupied - used, empty }
219
+ }
220
+
221
+ export function renderDcpUsageBar(_theme: Theme | undefined, usage: DcpContextUsage, width: number): string {
222
+ const total = Math.max(0, Math.round(usage.contextWindow ?? 0))
223
+ const used = Math.max(0, Math.round(usageTokens(usage)))
224
+ const totalCells = Math.max(1, width)
225
+ const safeTotal = Math.max(1, total)
226
+ const clampedUsed = Math.max(0, Math.min(safeTotal, used))
227
+ const usedCells = clampedUsed > 0 ? Math.max(1, Math.round((clampedUsed / safeTotal) * totalCells)) : 0
228
+ const occupied = Math.min(totalCells, usedCells)
229
+ return colorOccupiedContext(used, total, "█".repeat(occupied)) + colorEmptyContext("░".repeat(totalCells - occupied))
230
+ }
231
+
232
+ export function renderDcpContextBar(_theme: Theme | undefined, details: Pick<DcpCompressionVisualDetails, "contextTokens" | "contextWindow" | "contextPercent" | "tokensSaved">, width: number): string {
233
+ const parts = contextParts(details)
234
+ const cells = distributeBar(width, parts)
235
+ return [
236
+ colorOccupiedContext(parts.used, parts.total, "█".repeat(cells.used)),
237
+ colorFreedContext("░".repeat(cells.freed)),
238
+ colorEmptyContext("░".repeat(cells.empty)),
239
+ ].join("")
240
+ }
241
+
242
+ export function formatDcpCompressionMessageText(
243
+ details: DcpCompressionVisualDetails,
244
+ theme: Theme | undefined,
245
+ expanded: boolean,
246
+ ): string {
247
+ const ratio = compressionContextScale(details)
248
+ const bar = renderDcpContextBar(theme, details, expanded ? 24 : 18)
249
+ return [
250
+ fg(theme, "success", "✓"),
251
+ fg(theme, "accent", "compressed"),
252
+ fg(theme, "success", `saved ${formatDcpTokenCount(details.tokensSaved)}`),
253
+ bar,
254
+ ratio ? fg(theme, "customMessageText", ratio) : undefined,
255
+ details.topic ? fg(theme, "dim", `· ${truncateLabel(details.topic, expanded ? 64 : 36)}`) : undefined,
256
+ ].filter(Boolean).join(fg(theme, "dim", " "))
257
+ }
258
+
259
+ export function normalizeDcpCompressionDetails(content: unknown, details: unknown): DcpCompressionVisualDetails {
260
+ const raw = details && typeof details === "object" ? details as Partial<DcpCompressionVisualDetails> : {}
261
+ const topic = typeof raw.topic === "string"
262
+ ? raw.topic
263
+ : typeof content === "string"
264
+ ? content
265
+ : "Compressed context"
266
+ const blockIds = Array.isArray(raw.blockIds) ? raw.blockIds.filter((id): id is number => typeof id === "number") : []
267
+ const ranges = typeof raw.ranges === "number" ? raw.ranges : 0
268
+ const messages = typeof raw.messages === "number" ? raw.messages : 0
269
+ return {
270
+ topic,
271
+ blockIds,
272
+ ranges,
273
+ messages,
274
+ itemCount: typeof raw.itemCount === "number" ? raw.itemCount : ranges + messages,
275
+ totalSummaryTokens: typeof raw.totalSummaryTokens === "number" ? raw.totalSummaryTokens : 0,
276
+ activeBlocks: typeof raw.activeBlocks === "number" ? raw.activeBlocks : blockIds.length,
277
+ totalBlocks: typeof raw.totalBlocks === "number" ? raw.totalBlocks : blockIds.length,
278
+ prunedTools: typeof raw.prunedTools === "number" ? raw.prunedTools : 0,
279
+ tokensSaved: typeof raw.tokensSaved === "number" ? raw.tokensSaved : 0,
280
+ contextTokens: typeof raw.contextTokens === "number" || raw.contextTokens === null ? raw.contextTokens : undefined,
281
+ contextWindow: typeof raw.contextWindow === "number" ? raw.contextWindow : undefined,
282
+ contextPercent: typeof raw.contextPercent === "number" || raw.contextPercent === null ? raw.contextPercent : undefined,
283
+ skippedMessages: typeof raw.skippedMessages === "number" ? raw.skippedMessages : undefined,
284
+ skippedMessageIssues: Array.isArray(raw.skippedMessageIssues)
285
+ ? raw.skippedMessageIssues.filter((issue): issue is string => typeof issue === "string")
286
+ : undefined,
287
+ }
288
+ }
289
+
290
+ export class DcpUiController {
291
+ constructor(_state: DcpState) {}
292
+
293
+ setUICtx(_ctx: ExtensionUIContext): void {
294
+ // DCP intentionally does not render extension UI/status/toasts. The only
295
+ // user-visible DCP output should be the `compress` tool result itself.
296
+ }
297
+
298
+ update(_ctx?: ExtensionContext): void {
299
+ // No-op by design; reminders are injected into model context, not rendered.
300
+ }
301
+
302
+ dispose(): void {
303
+ // No-op by design.
304
+ }
305
+ }
306
+
307
+ export const __formatDcpCompressionMessageTextForTest = formatDcpCompressionMessageText
308
+ export const __renderDcpStatusLabelForTest = renderDcpStatusLabel
@@ -0,0 +1,176 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, parse, resolve } from "node:path";
4
+ import { parse as parseJsonc } from "jsonc-parser";
5
+
6
+ import { DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC } from "./default-pi-tools-suite-config.js";
7
+
8
+ export interface PiToolsSuiteConfig {
9
+ enabled: boolean;
10
+ disabledModules: string[];
11
+ }
12
+
13
+ type MutableConfig = {
14
+ enabled: boolean;
15
+ disabledModules: Set<string>;
16
+ };
17
+
18
+ type Env = Record<string, string | undefined>;
19
+
20
+ const TRUE_VALUES = new Set(["1", "true", "on", "yes"]);
21
+ const FALSE_VALUES = new Set(["0", "false", "off", "no"]);
22
+
23
+ const DISABLED_LIST_KEYS = ["disabledModules", "disabledExtensions"];
24
+ const ENABLED_LIST_KEYS = ["enabledModules", "enabledExtensions"];
25
+ const MODULE_MAP_KEYS = ["modules", "extensions"];
26
+
27
+ export function getPiToolsSuiteUserConfigPath(homeDir = homedir()): string {
28
+ return join(homeDir, ".config", "pi", "pi-tools-suite.jsonc");
29
+ }
30
+
31
+ function ensureUserConfig(filePath: string): void {
32
+ if (existsSync(filePath)) return;
33
+ try {
34
+ mkdirSync(dirname(filePath), { recursive: true });
35
+ writeFileSync(filePath, DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC, { encoding: "utf8", flag: "wx" });
36
+ } catch (error) {
37
+ if ((error as NodeJS.ErrnoException).code === "EEXIST") return;
38
+ // Config creation is best-effort; loading should still work in read-only homes.
39
+ }
40
+ }
41
+
42
+ export function ensurePiToolsSuiteUserConfig(_moduleNames: readonly string[] = [], options: { homeDir?: string } = {}): string {
43
+ const filePath = getPiToolsSuiteUserConfigPath(options.homeDir);
44
+ ensureUserConfig(filePath);
45
+ return filePath;
46
+ }
47
+
48
+ function isRecord(value: unknown): value is Record<string, unknown> {
49
+ return value !== null && typeof value === "object" && !Array.isArray(value);
50
+ }
51
+
52
+ function boolFromEnv(value: string | undefined): boolean | undefined {
53
+ if (value === undefined) return undefined;
54
+ const normalized = value.trim().toLowerCase();
55
+ if (TRUE_VALUES.has(normalized)) return true;
56
+ if (FALSE_VALUES.has(normalized)) return false;
57
+ return undefined;
58
+ }
59
+
60
+ function readJsonc(filePath: string): Record<string, unknown> {
61
+ if (!existsSync(filePath)) return {};
62
+ try {
63
+ const parsed = parseJsonc(readFileSync(filePath, "utf8"));
64
+ return isRecord(parsed) ? parsed : {};
65
+ } catch {
66
+ return {};
67
+ }
68
+ }
69
+
70
+ function splitNameList(value: unknown): string[] {
71
+ if (typeof value === "string") {
72
+ if (FALSE_VALUES.has(value.trim().toLowerCase())) return [];
73
+ return value
74
+ .split(/[\s,;]+/g)
75
+ .map((item) => item.trim())
76
+ .filter(Boolean);
77
+ }
78
+ if (!Array.isArray(value)) return [];
79
+ return value.filter((item): item is string => typeof item === "string").map((item) => item.trim()).filter(Boolean);
80
+ }
81
+
82
+ function canonicalModuleName(name: string, knownModules: ReadonlySet<string>): string | undefined {
83
+ const normalized = name.trim().toLowerCase();
84
+ if (!normalized) return undefined;
85
+ return knownModules.has(normalized) ? normalized : undefined;
86
+ }
87
+
88
+ function addDisabled(config: MutableConfig, value: unknown, knownModules: ReadonlySet<string>): void {
89
+ const names = splitNameList(value);
90
+ if (names.some((name) => name === "*" || name.toLowerCase() === "all")) {
91
+ for (const moduleName of knownModules) config.disabledModules.add(moduleName);
92
+ return;
93
+ }
94
+ for (const name of names) {
95
+ const moduleName = canonicalModuleName(name, knownModules);
96
+ if (moduleName) config.disabledModules.add(moduleName);
97
+ }
98
+ }
99
+
100
+ function removeDisabled(config: MutableConfig, value: unknown, knownModules: ReadonlySet<string>): void {
101
+ for (const name of splitNameList(value)) {
102
+ const moduleName = canonicalModuleName(name, knownModules);
103
+ if (moduleName) config.disabledModules.delete(moduleName);
104
+ }
105
+ }
106
+
107
+ function mergeConfigLayer(config: MutableConfig, raw: Record<string, unknown>, knownModules: ReadonlySet<string>): MutableConfig {
108
+ if (typeof raw.enabled === "boolean") config.enabled = raw.enabled;
109
+
110
+ for (const key of DISABLED_LIST_KEYS) addDisabled(config, raw[key], knownModules);
111
+ for (const key of ENABLED_LIST_KEYS) removeDisabled(config, raw[key], knownModules);
112
+
113
+ for (const key of MODULE_MAP_KEYS) {
114
+ const modules = raw[key];
115
+ if (!isRecord(modules)) continue;
116
+ for (const [name, value] of Object.entries(modules)) {
117
+ const moduleName = canonicalModuleName(name, knownModules);
118
+ if (!moduleName || typeof value !== "boolean") continue;
119
+ if (value) config.disabledModules.delete(moduleName);
120
+ else config.disabledModules.add(moduleName);
121
+ }
122
+ }
123
+
124
+ return config;
125
+ }
126
+
127
+ function findProjectConfig(startDir: string): string | undefined {
128
+ let dir = resolve(startDir);
129
+ const root = parse(dir).root;
130
+
131
+ while (true) {
132
+ const candidate = join(dir, ".pi", "pi-tools-suite.jsonc");
133
+ if (existsSync(candidate)) return candidate;
134
+ if (dir === root) return undefined;
135
+ const parent = dirname(dir);
136
+ if (parent === dir) return undefined;
137
+ dir = parent;
138
+ }
139
+ }
140
+
141
+ function applyEnv(config: MutableConfig, env: Env, knownModules: ReadonlySet<string>): MutableConfig {
142
+ const enabled = boolFromEnv(env.PI_TOOLS_SUITE_ENABLED);
143
+ if (enabled !== undefined) config.enabled = enabled;
144
+
145
+ const disabled = boolFromEnv(env.PI_TOOLS_SUITE_DISABLED);
146
+ if (disabled === true) config.enabled = false;
147
+ else if (disabled === false && enabled === undefined) config.enabled = true;
148
+
149
+ addDisabled(config, env.PI_TOOLS_SUITE_DISABLED_MODULES, knownModules);
150
+ addDisabled(config, env.PI_TOOLS_SUITE_DISABLED_EXTENSIONS, knownModules);
151
+
152
+ return config;
153
+ }
154
+
155
+ export function loadPiToolsSuiteConfig(moduleNames: readonly string[], options: { cwd?: string; env?: Env; homeDir?: string } = {}): PiToolsSuiteConfig {
156
+ const env = options.env ?? process.env;
157
+ const knownModules = new Set(moduleNames.map((name) => name.toLowerCase()));
158
+ const config: MutableConfig = { enabled: true, disabledModules: new Set() };
159
+ const userConfigPath = getPiToolsSuiteUserConfigPath(options.homeDir);
160
+
161
+ ensureUserConfig(userConfigPath);
162
+ mergeConfigLayer(config, readJsonc(userConfigPath), knownModules);
163
+
164
+ const piConfigDir = env.PI_CONFIG_DIR;
165
+ if (piConfigDir) mergeConfigLayer(config, readJsonc(join(piConfigDir, "pi-tools-suite.jsonc")), knownModules);
166
+
167
+ const projectConfig = findProjectConfig(options.cwd ?? process.cwd());
168
+ if (projectConfig) mergeConfigLayer(config, readJsonc(projectConfig), knownModules);
169
+
170
+ applyEnv(config, env, knownModules);
171
+
172
+ return {
173
+ enabled: config.enabled,
174
+ disabledModules: [...config.disabledModules].sort(),
175
+ };
176
+ }
@@ -0,0 +1,31 @@
1
+ export interface ContextUsageSnapshot {
2
+ tokens?: number | null
3
+ contextWindow?: number
4
+ percent?: number | null
5
+ }
6
+
7
+ export interface ContextUsageProvider {
8
+ getContextUsage?: () => ContextUsageSnapshot | undefined
9
+ }
10
+
11
+ export function isStaleExtensionContextError(error: unknown): boolean {
12
+ if (!(error instanceof Error)) return false
13
+ return /ctx is stale|stale after session replacement|stale after.*reload/i.test(error.message)
14
+ }
15
+
16
+ export function ignoreStaleExtensionContextError(error: unknown): void {
17
+ if (!isStaleExtensionContextError(error)) throw error
18
+ }
19
+
20
+ // Fork/newSession/switchSession/reload can invalidate a runner while late UI or
21
+ // context events from the old runner are still unwinding. In that race,
22
+ // ctx.getContextUsage() throws the Pi stale-ctx guard; treat it as unavailable
23
+ // usage instead of failing the replacement flow.
24
+ export function safeGetContextUsage(ctx: ContextUsageProvider | undefined): ContextUsageSnapshot | undefined {
25
+ try {
26
+ return ctx?.getContextUsage?.()
27
+ } catch (error) {
28
+ ignoreStaleExtensionContextError(error)
29
+ return undefined
30
+ }
31
+ }