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,152 @@
1
+ import { getAgentDir, type ExtensionAPI, type ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { readFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import { publishStartupSection } from "../startup-section";
7
+ import { queryGoogleUsage } from "./lib/google";
8
+ import { queryOpenAIUsage } from "./lib/openai";
9
+ import { type AuthData, type QueryResult } from "./lib/types";
10
+ import { queryZaiUsage, queryZhipuUsage } from "./lib/zhipu";
11
+
12
+ const CUSTOM_MESSAGE_TYPE = "usage";
13
+
14
+ type PiAuthCredential = {
15
+ type?: string;
16
+ access?: string;
17
+ refresh?: string;
18
+ expires?: number;
19
+ key?: string;
20
+ };
21
+
22
+ type PiAuthData = Record<string, PiAuthCredential | undefined>;
23
+
24
+ async function readOpenCodeAuth(): Promise<{ authData: AuthData; error?: string }> {
25
+ const authPath = join(homedir(), ".local/share/opencode/auth.json");
26
+
27
+ try {
28
+ const content = await readFile(authPath, "utf-8");
29
+ return { authData: JSON.parse(content) as AuthData };
30
+ } catch (error) {
31
+ return {
32
+ authData: {},
33
+ error: `❌ Failed to read auth file: ${authPath}\nError: ${error instanceof Error ? error.message : String(error)}`,
34
+ };
35
+ }
36
+ }
37
+
38
+ async function readPiAuth(): Promise<PiAuthData> {
39
+ try {
40
+ const content = await readFile(join(getAgentDir(), "auth.json"), "utf-8");
41
+ return JSON.parse(content) as PiAuthData;
42
+ } catch {
43
+ return {};
44
+ }
45
+ }
46
+
47
+ function isExpired(credential: { expires?: number } | undefined): boolean {
48
+ return typeof credential?.expires === "number" && credential.expires < Date.now();
49
+ }
50
+
51
+ async function readAuthData(): Promise<{ authData: AuthData; error?: string }> {
52
+ const { authData, error } = await readOpenCodeAuth();
53
+ const piAuth = await readPiAuth();
54
+
55
+ const piOpenAI = piAuth["openai-codex"];
56
+ if ((!authData.openai || isExpired(authData.openai)) && piOpenAI?.type === "oauth" && piOpenAI.access) {
57
+ authData.openai = {
58
+ type: "oauth",
59
+ access: piOpenAI.access,
60
+ refresh: piOpenAI.refresh,
61
+ expires: piOpenAI.expires,
62
+ };
63
+ }
64
+
65
+ const piZai = piAuth.zai;
66
+ if (!authData["zai-coding-plan"] && piZai?.key && (piZai.type === "api" || piZai.type === "api_key")) {
67
+ authData["zai-coding-plan"] = { type: "api", key: piZai.key };
68
+ }
69
+
70
+ return { authData, error };
71
+ }
72
+
73
+ function collectResult(result: QueryResult | null, title: string, results: string[], errors: string[]): void {
74
+ if (!result) return;
75
+
76
+ if (result.success && result.output) {
77
+ if (results.length > 0) results.push("");
78
+ results.push(title);
79
+ results.push("");
80
+ results.push(result.output);
81
+ } else if (result.error) {
82
+ errors.push(result.error);
83
+ }
84
+ }
85
+
86
+ export async function queryUsage(): Promise<string> {
87
+ const { authData, error: authReadError } = await readAuthData();
88
+
89
+ const [openaiResult, zhipuResult, zaiResult, googleResult] = await Promise.all([
90
+ queryOpenAIUsage(authData.openai),
91
+ queryZhipuUsage(authData["zhipuai-coding-plan"]),
92
+ queryZaiUsage(authData["zai-coding-plan"]),
93
+ queryGoogleUsage(),
94
+ ]);
95
+
96
+ const results: string[] = [];
97
+ const errors: string[] = [];
98
+
99
+ collectResult(openaiResult, "## OpenAI Account Quota", results, errors);
100
+ collectResult(zhipuResult, "## Zhipu AI Account Quota", results, errors);
101
+ collectResult(zaiResult, "## Z.ai Account Quota", results, errors);
102
+ collectResult(googleResult, "## Google Cloud Account Quota", results, errors);
103
+
104
+ if (results.length === 0 && errors.length === 0) {
105
+ const noAccounts = "No configured accounts found.\n\nSupported account types:\n- OpenAI (Plus/Team/Pro subscribers)\n- Zhipu AI (Coding Plan)\n- Z.ai (Coding Plan)\n- Google Cloud (Antigravity)";
106
+ return authReadError ? `${noAccounts}\n\n${authReadError}` : noAccounts;
107
+ }
108
+
109
+ let output = results.join("\n");
110
+ if (errors.length > 0) {
111
+ if (output) output += "\n\n";
112
+ output += "❌ Failed to query accounts:\n" + errors.join("\n");
113
+ }
114
+
115
+ return output;
116
+ }
117
+
118
+ function sendStatusMessage(pi: ExtensionAPI, text: string): void {
119
+ pi.sendMessage({
120
+ customType: CUSTOM_MESSAGE_TYPE,
121
+ content: text,
122
+ display: true,
123
+ details: { generatedAt: new Date().toISOString() },
124
+ });
125
+ }
126
+
127
+ export default function usage(pi: ExtensionAPI) {
128
+ publishStartupSection({
129
+ id: "usage",
130
+ title: "usage",
131
+ body: "/usage — query quota usage for configured AI accounts",
132
+ });
133
+
134
+ async function showStatusFromCommand(ctx: ExtensionCommandContext): Promise<void> {
135
+ const text = await queryUsage();
136
+
137
+ if (ctx.hasUI) {
138
+ ctx.ui.notify("usage: quota usage refreshed", "info");
139
+ } else {
140
+ console.log(text);
141
+ }
142
+
143
+ sendStatusMessage(pi, text);
144
+ }
145
+
146
+ pi.registerCommand("usage", {
147
+ description: "Query quota usage for configured AI accounts, including per-model Antigravity/Gemini usage where available.",
148
+ handler: async (_args, ctx) => {
149
+ await showStatusFromCommand(ctx);
150
+ },
151
+ });
152
+ }
@@ -0,0 +1,535 @@
1
+ /**
2
+ * GitHub Copilot Premium Requests Quota Module
3
+ *
4
+ * [Input]: GitHub token from ~/.local/share/opencode/auth.json (github-copilot provider)
5
+ * [Output]: Formatted quota usage information with progress bars
6
+ * [Location]: Called by usage.ts to handle GitHub Copilot accounts
7
+ * [Sync]: usage.ts, types.ts, utils.ts
8
+ *
9
+ * [Updated]: Jan 2026 - Handle new OpenCode official partnership auth flow
10
+ * The new OAuth tokens (gho_) need to be exchanged for Copilot session tokens
11
+ * before calling the internal quota API.
12
+ */
13
+
14
+ import {
15
+ type QueryResult,
16
+ type CopilotAuthData,
17
+ type CopilotQuotaConfig,
18
+ type CopilotTier,
19
+ } from "./types";
20
+ import { createProgressBar, fetchWithTimeout } from "./utils";
21
+ import * as fs from "fs";
22
+ import * as path from "path";
23
+ import * as os from "os";
24
+
25
+ // ============================================================================
26
+ // Type Definitions
27
+ // ============================================================================
28
+
29
+ interface QuotaDetail {
30
+ entitlement: number;
31
+ overage_count: number;
32
+ overage_permitted: boolean;
33
+ percent_remaining: number;
34
+ quota_id: string;
35
+ quota_remaining: number;
36
+ remaining: number;
37
+ unlimited: boolean;
38
+ }
39
+
40
+ interface QuotaSnapshots {
41
+ chat?: QuotaDetail;
42
+ completions?: QuotaDetail;
43
+ premium_interactions: QuotaDetail;
44
+ }
45
+
46
+ interface CopilotUsageResponse {
47
+ access_type_sku: string;
48
+ analytics_tracking_id: string;
49
+ assigned_date: string;
50
+ can_signup_for_limited: boolean;
51
+ chat_enabled: boolean;
52
+ copilot_plan: string;
53
+ organization_login_list: unknown[];
54
+ organization_list: unknown[];
55
+ quota_reset_date: string;
56
+ quota_snapshots: QuotaSnapshots;
57
+ }
58
+
59
+ interface CopilotTokenResponse {
60
+ token: string;
61
+ expires_at: number;
62
+ refresh_in: number;
63
+ endpoints: {
64
+ api: string;
65
+ };
66
+ }
67
+
68
+ // Public Billing API response types
69
+ interface BillingUsageItem {
70
+ product: string;
71
+ sku: string;
72
+ model?: string;
73
+ unitType: string;
74
+ grossQuantity: number;
75
+ netQuantity: number;
76
+ limit?: number;
77
+ }
78
+
79
+ interface BillingUsageResponse {
80
+ timePeriod: { year: number; month?: number };
81
+ user: string;
82
+ usageItems: BillingUsageItem[];
83
+ }
84
+
85
+ // ============================================================================
86
+ // Constants
87
+ // ============================================================================
88
+
89
+ const GITHUB_API_BASE_URL = "https://api.github.com";
90
+
91
+ // Config file path for user's fine-grained PAT
92
+ const COPILOT_QUOTA_CONFIG_PATH = path.join(
93
+ os.homedir(),
94
+ ".config",
95
+ "opencode",
96
+ "copilot-quota-token.json",
97
+ );
98
+
99
+ // Updated to match current VS Code Copilot extension version
100
+ const COPILOT_VERSION = "0.35.0";
101
+ const EDITOR_VERSION = "vscode/1.107.0";
102
+ const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`;
103
+ const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`;
104
+
105
+ // Headers matching opencode-copilot-auth plugin (required for token exchange)
106
+ const COPILOT_HEADERS = {
107
+ "User-Agent": USER_AGENT,
108
+ "Editor-Version": EDITOR_VERSION,
109
+ "Editor-Plugin-Version": EDITOR_PLUGIN_VERSION,
110
+ "Copilot-Integration-Id": "vscode-chat",
111
+ };
112
+
113
+ const COPILOT_QUOTA_UNAVAILABLE =
114
+ "⚠️ GitHub Copilot quota query unavailable.\n" +
115
+ "OpenCode's new OAuth integration doesn't support quota API access.";
116
+
117
+ const COPILOT_QUOTA_WORKAROUND =
118
+ "Solution:\n" +
119
+ "1. Create a fine-grained PAT (visit https://github.com/settings/tokens?type=beta)\n" +
120
+ "2. Under 'Account permissions', set 'Plan' to 'Read-only'\n" +
121
+ "3. Create config file ~/.config/opencode/copilot-quota-token.json:\n" +
122
+ ' {"token": "github_pat_xxx...", "username": "YourUsername", "tier": "pro"}\n\n' +
123
+ "Alternatives:\n" +
124
+ "• Click the Copilot icon in VS Code status bar to view quota\n" +
125
+ "• Visit https://github.com/settings/billing for usage info";
126
+
127
+ // ============================================================================
128
+ // Token Exchange (New auth flow for official OpenCode partnership)
129
+ // ============================================================================
130
+
131
+ /**
132
+ * Read optional Copilot quota config from user's config file
133
+ * Returns null if file doesn't exist or is invalid
134
+ */
135
+ function readQuotaConfig(): CopilotQuotaConfig | null {
136
+ try {
137
+ if (!fs.existsSync(COPILOT_QUOTA_CONFIG_PATH)) {
138
+ return null;
139
+ }
140
+ const content = fs.readFileSync(COPILOT_QUOTA_CONFIG_PATH, "utf-8");
141
+ const config = JSON.parse(content) as CopilotQuotaConfig;
142
+
143
+ // Validate required fields
144
+ if (!config.token || !config.username || !config.tier) {
145
+ return null;
146
+ }
147
+
148
+ // Validate tier is valid
149
+ const validTiers: CopilotTier[] = [
150
+ "free",
151
+ "pro",
152
+ "pro+",
153
+ "business",
154
+ "enterprise",
155
+ ];
156
+ if (!validTiers.includes(config.tier)) {
157
+ return null;
158
+ }
159
+
160
+ return config;
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Fetch quota using the public GitHub REST API
168
+ * Requires a fine-grained PAT with "Plan" read permission
169
+ */
170
+ async function fetchPublicBillingUsage(
171
+ config: CopilotQuotaConfig,
172
+ ): Promise<BillingUsageResponse> {
173
+ const response = await fetchWithTimeout(
174
+ `${GITHUB_API_BASE_URL}/users/${config.username}/settings/billing/premium_request/usage`,
175
+ {
176
+ headers: {
177
+ Accept: "application/vnd.github+json",
178
+ Authorization: `Bearer ${config.token}`,
179
+ "X-GitHub-Api-Version": "2022-11-28",
180
+ },
181
+ },
182
+ );
183
+
184
+ if (!response.ok) {
185
+ const errorText = await response.text();
186
+ throw new Error(`GitHub Copilot API request failed (${response.status}): ${errorText}`);
187
+ }
188
+
189
+ return response.json() as Promise<BillingUsageResponse>;
190
+ }
191
+
192
+ /**
193
+ * Exchange OAuth token for a Copilot session token
194
+ * Required for the new OpenCode official partnership auth flow (Jan 2026+)
195
+ */
196
+ async function exchangeForCopilotToken(
197
+ oauthToken: string,
198
+ ): Promise<string | null> {
199
+ try {
200
+ const response = await fetchWithTimeout(
201
+ `${GITHUB_API_BASE_URL}/copilot_internal/v2/token`,
202
+ {
203
+ headers: {
204
+ Accept: "application/json",
205
+ Authorization: `Bearer ${oauthToken}`,
206
+ ...COPILOT_HEADERS,
207
+ },
208
+ },
209
+ );
210
+
211
+ if (!response.ok) {
212
+ // Token exchange failed - might be old token format or API change
213
+ return null;
214
+ }
215
+
216
+ const tokenData: CopilotTokenResponse = await response.json();
217
+ return tokenData.token;
218
+ } catch {
219
+ return null;
220
+ }
221
+ }
222
+
223
+ // ============================================================================
224
+ // API Call
225
+ // ============================================================================
226
+
227
+ /**
228
+ * Build headers for GitHub API requests (quota endpoint)
229
+ */
230
+ function buildGitHubHeaders(token: string): Record<string, string> {
231
+ return {
232
+ "Content-Type": "application/json",
233
+ Accept: "application/json",
234
+ Authorization: `Bearer ${token}`,
235
+ ...COPILOT_HEADERS,
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Build headers for legacy token format
241
+ */
242
+ function buildLegacyHeaders(token: string): Record<string, string> {
243
+ return {
244
+ "Content-Type": "application/json",
245
+ Accept: "application/json",
246
+ Authorization: `token ${token}`,
247
+ ...COPILOT_HEADERS,
248
+ };
249
+ }
250
+
251
+ /**
252
+ * Fetch GitHub Copilot usage data
253
+ * Tries multiple authentication methods to handle both old and new token formats
254
+ */
255
+ async function fetchCopilotUsage(
256
+ authData: CopilotAuthData,
257
+ ): Promise<CopilotUsageResponse> {
258
+ // Use refresh token as the OAuth token (required)
259
+ // In new auth flow, access === refresh (both are the OAuth token)
260
+ const oauthToken = authData.refresh || authData.access;
261
+ if (!oauthToken) {
262
+ throw new Error("No OAuth token found in auth data");
263
+ }
264
+
265
+ const cachedAccessToken = authData.access;
266
+ const tokenExpiry = authData.expires || 0;
267
+
268
+ // Strategy 1: If we have a valid cached access token (from previous exchange), use it
269
+ if (
270
+ cachedAccessToken &&
271
+ cachedAccessToken !== oauthToken &&
272
+ tokenExpiry > Date.now()
273
+ ) {
274
+ const response = await fetchWithTimeout(
275
+ `${GITHUB_API_BASE_URL}/copilot_internal/user`,
276
+ { headers: buildGitHubHeaders(cachedAccessToken) },
277
+ );
278
+
279
+ if (response.ok) {
280
+ return response.json() as Promise<CopilotUsageResponse>;
281
+ }
282
+ }
283
+
284
+ // Strategy 2: Try direct call with OAuth token (works with older token formats)
285
+ const directResponse = await fetchWithTimeout(
286
+ `${GITHUB_API_BASE_URL}/copilot_internal/user`,
287
+ { headers: buildLegacyHeaders(oauthToken) },
288
+ );
289
+
290
+ if (directResponse.ok) {
291
+ return directResponse.json() as Promise<CopilotUsageResponse>;
292
+ }
293
+
294
+ // Strategy 3: Exchange OAuth token for Copilot session token (new auth flow)
295
+ const copilotToken = await exchangeForCopilotToken(oauthToken);
296
+
297
+ if (copilotToken) {
298
+ const exchangedResponse = await fetchWithTimeout(
299
+ `${GITHUB_API_BASE_URL}/copilot_internal/user`,
300
+ { headers: buildGitHubHeaders(copilotToken) },
301
+ );
302
+
303
+ if (exchangedResponse.ok) {
304
+ return exchangedResponse.json() as Promise<CopilotUsageResponse>;
305
+ }
306
+
307
+ const errorText = await exchangedResponse.text();
308
+ throw new Error(`GitHub Copilot API request failed (${exchangedResponse.status}): ${errorText}`);
309
+ }
310
+
311
+ // All strategies failed - likely due to OpenCode's OAuth token lacking copilot scope
312
+ // The new OpenCode partnership uses a different OAuth client that doesn't grant
313
+ // access to the /copilot_internal/* endpoints
314
+ throw new Error(`${COPILOT_QUOTA_UNAVAILABLE}\n\n${COPILOT_QUOTA_WORKAROUND}`);
315
+ }
316
+
317
+ // ============================================================================
318
+ // Formatting
319
+ // ============================================================================
320
+
321
+ /**
322
+ * Format a single quota line
323
+ */
324
+ function formatQuotaLine(
325
+ name: string,
326
+ quota: QuotaDetail | undefined,
327
+ width: number = 20,
328
+ ): string {
329
+ if (!quota) return "";
330
+
331
+ if (quota.unlimited) {
332
+ return `${name.padEnd(14)} Unlimited`;
333
+ }
334
+
335
+ const total = quota.entitlement;
336
+ const used = total - quota.remaining;
337
+ const percentRemaining = Math.round(quota.percent_remaining);
338
+ const progressBar = createProgressBar(percentRemaining, width);
339
+
340
+ return `${name.padEnd(14)} ${progressBar} ${percentRemaining}% (${used}/${total})`;
341
+ }
342
+
343
+ /**
344
+ * Calculate days until reset
345
+ */
346
+ function getResetCountdown(resetDate: string): string {
347
+ const reset = new Date(resetDate);
348
+ const now = new Date();
349
+ const diffMs = reset.getTime() - now.getTime();
350
+
351
+ if (diffMs <= 0) return "Resets soon";
352
+
353
+ const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));
354
+ const hours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
355
+
356
+ if (days > 0) {
357
+ return `${days}d ${hours}h`;
358
+ }
359
+ return `${hours}h`;
360
+ }
361
+
362
+ /**
363
+ * Format GitHub Copilot usage information
364
+ */
365
+ function formatCopilotUsage(data: CopilotUsageResponse): string {
366
+ const lines: string[] = [];
367
+
368
+ // Account info
369
+ lines.push(`Account: GitHub Copilot (${data.copilot_plan})`);
370
+ lines.push("");
371
+
372
+ // Premium requests (main quota)
373
+ const premium = data.quota_snapshots.premium_interactions;
374
+ if (premium) {
375
+ const premiumLine = formatQuotaLine("Premium", premium);
376
+ if (premiumLine) lines.push(premiumLine);
377
+
378
+ // Show overage info if applicable
379
+ if (premium.overage_count > 0) {
380
+ lines.push(`Overage: ${premium.overage_count} requests`);
381
+ }
382
+ }
383
+
384
+ // Chat quota (if separate)
385
+ const chat = data.quota_snapshots.chat;
386
+ if (chat && !chat.unlimited) {
387
+ const chatLine = formatQuotaLine("Chat", chat);
388
+ if (chatLine) lines.push(chatLine);
389
+ }
390
+
391
+ // Completions quota (if separate)
392
+ const completions = data.quota_snapshots.completions;
393
+ if (completions && !completions.unlimited) {
394
+ const completionsLine = formatQuotaLine("Completions", completions);
395
+ if (completionsLine) lines.push(completionsLine);
396
+ }
397
+
398
+ // Reset date
399
+ lines.push("");
400
+ const resetCountdown = getResetCountdown(data.quota_reset_date);
401
+ lines.push(`Quota resets: ${resetCountdown} (${data.quota_reset_date})`);
402
+
403
+ return lines.join("\n");
404
+ }
405
+
406
+ // Copilot plan limits (premium requests per month)
407
+ // Source: https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot
408
+ const COPILOT_PLAN_LIMITS: Record<CopilotTier, number> = {
409
+ free: 50, // Copilot Free: 50 premium requests/month
410
+ pro: 300, // Copilot Pro: 300 premium requests/month
411
+ "pro+": 1500, // Copilot Pro+: 1500 premium requests/month
412
+ business: 300, // Copilot Business: 300 premium requests/month
413
+ enterprise: 1000, // Copilot Enterprise: 1000 premium requests/month
414
+ };
415
+
416
+ /**
417
+ * Format public billing API response
418
+ * Different structure from internal API - aggregates usage items
419
+ * Uses grossQuantity (total requests made) since netQuantity shows post-discount amount
420
+ */
421
+ function formatPublicBillingUsage(
422
+ data: BillingUsageResponse,
423
+ tier: CopilotTier,
424
+ ): string {
425
+ const lines: string[] = [];
426
+
427
+ // Account info
428
+ lines.push(`Account: GitHub Copilot (@${data.user})`);
429
+ lines.push("");
430
+
431
+ // Aggregate all premium request usage (sum grossQuantity across all models)
432
+ const premiumItems = data.usageItems.filter(
433
+ (item) =>
434
+ item.sku === "Copilot Premium Request" || item.sku.includes("Premium"),
435
+ );
436
+
437
+ const totalUsed = premiumItems.reduce(
438
+ (sum, item) => sum + item.grossQuantity,
439
+ 0,
440
+ );
441
+
442
+ // Get limit from tier
443
+ const limit = COPILOT_PLAN_LIMITS[tier];
444
+
445
+ const remaining = Math.max(0, limit - totalUsed);
446
+ const percentRemaining = Math.round((remaining / limit) * 100);
447
+ const progressBar = createProgressBar(percentRemaining, 20);
448
+ lines.push(
449
+ `${"Premium".padEnd(14)} ${progressBar} ${percentRemaining}% (${totalUsed}/${limit})`,
450
+ );
451
+
452
+ // Show model breakdown
453
+ const modelItems = data.usageItems.filter(
454
+ (item) => item.model && item.grossQuantity > 0,
455
+ );
456
+
457
+ if (modelItems.length > 0) {
458
+ lines.push("");
459
+ lines.push("Model breakdown:");
460
+ // Sort by usage descending
461
+ const sortedItems = [...modelItems].sort(
462
+ (a, b) => b.grossQuantity - a.grossQuantity,
463
+ );
464
+ for (const item of sortedItems.slice(0, 5)) {
465
+ // Show top 5 models
466
+ lines.push(` ${item.model}: ${item.grossQuantity} ${item.unitType}`);
467
+ }
468
+ }
469
+
470
+ // Time period info
471
+ lines.push("");
472
+ const period = data.timePeriod;
473
+ const periodStr = period.month
474
+ ? `${period.year}-${String(period.month).padStart(2, "0")}`
475
+ : `${period.year}`;
476
+ lines.push(`Period: ${periodStr}`);
477
+
478
+ return lines.join("\n");
479
+ }
480
+
481
+ // ============================================================================
482
+ // Export Interface
483
+ // ============================================================================
484
+
485
+ export type { CopilotAuthData };
486
+
487
+ /**
488
+ * Query GitHub Copilot account quota
489
+ * @param authData GitHub Copilot authentication data (optional if using PAT config)
490
+ * @returns Query result, null if no account configured
491
+ */
492
+ export async function queryCopilotUsage(
493
+ authData: CopilotAuthData | undefined,
494
+ ): Promise<QueryResult | null> {
495
+ // Strategy 1: Try public billing API with user's fine-grained PAT
496
+ const quotaConfig = readQuotaConfig();
497
+ if (quotaConfig) {
498
+ try {
499
+ const billingUsage = await fetchPublicBillingUsage(quotaConfig);
500
+ return {
501
+ success: true,
502
+ output: formatPublicBillingUsage(billingUsage, quotaConfig.tier),
503
+ };
504
+ } catch (err) {
505
+ // PAT config exists but failed - report the error
506
+ return {
507
+ success: false,
508
+ error: err instanceof Error ? err.message : String(err),
509
+ };
510
+ }
511
+ }
512
+
513
+ // Strategy 2: Try internal API with OAuth token (legacy, may not work with new OpenCode auth)
514
+ // Check if account exists and has a refresh token (the GitHub OAuth token)
515
+ if (!authData || authData.type !== "oauth" || !authData.refresh) {
516
+ // No auth data and no PAT config - show setup instructions
517
+ return {
518
+ success: false,
519
+ error: `${COPILOT_QUOTA_UNAVAILABLE}\n\n${COPILOT_QUOTA_WORKAROUND}`,
520
+ };
521
+ }
522
+
523
+ try {
524
+ const usage = await fetchCopilotUsage(authData);
525
+ return {
526
+ success: true,
527
+ output: formatCopilotUsage(usage),
528
+ };
529
+ } catch (err) {
530
+ return {
531
+ success: false,
532
+ error: err instanceof Error ? err.message : String(err),
533
+ };
534
+ }
535
+ }