taskchef 8.6.2 → 8.7.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 (630) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/README.md +8 -4
  3. package/assets/README.md +19 -0
  4. package/assets/taskchef-app-rounded.png +0 -0
  5. package/assets/taskchef-avatar-square.png +0 -0
  6. package/assets/taskchef-avatar-square.svg +8 -0
  7. package/assets/taskchef-sidebar-dark.svg +6 -0
  8. package/assets/taskchef-sidebar-light.svg +6 -0
  9. package/assets/taskchef-sidebar.svg +6 -0
  10. package/docs/advanced-guide.md +20 -29
  11. package/docs/dashboard-ui.md +15 -0
  12. package/docs/diagrams/taskchef-next-reply-lookup.drawio +38 -0
  13. package/docs/diagrams/taskchef-next-reply-lookup.drawio.png +0 -0
  14. package/docs/diagrams/taskchef-next-storage.drawio +41 -0
  15. package/docs/diagrams/taskchef-next-storage.drawio.png +0 -0
  16. package/docs/images/taskchef-pr-icon-comparison.jpg +0 -0
  17. package/docs/images/taskchef-unified-status-popup.jpg +0 -0
  18. package/docs/mcp-app.md +278 -6
  19. package/docs/spec.md +24 -69
  20. package/docs/taskchef-next-github.md +206 -0
  21. package/docs/taskchef-next-status-research.md +102 -0
  22. package/docs/taskchef-next-storage.md +167 -0
  23. package/docs/workflows.md +4 -2
  24. package/node_modules/@napi-rs/keyring/LICENSE +21 -0
  25. package/node_modules/@napi-rs/keyring/README.md +45 -0
  26. package/node_modules/@napi-rs/keyring/index.d.ts +232 -0
  27. package/node_modules/@napi-rs/keyring/index.js +706 -0
  28. package/node_modules/@napi-rs/keyring/keytar.d.ts +53 -0
  29. package/node_modules/@napi-rs/keyring/keytar.js +26 -0
  30. package/node_modules/@napi-rs/keyring/package.json +126 -0
  31. package/node_modules/@napi-rs/keyring-darwin-arm64/README.md +3 -0
  32. package/node_modules/@napi-rs/keyring-darwin-arm64/keyring.darwin-arm64.node +0 -0
  33. package/node_modules/@napi-rs/keyring-darwin-arm64/package.json +32 -0
  34. package/node_modules/@napi-rs/keyring-darwin-x64/README.md +3 -0
  35. package/node_modules/@napi-rs/keyring-darwin-x64/keyring.darwin-x64.node +0 -0
  36. package/node_modules/@napi-rs/keyring-darwin-x64/package.json +32 -0
  37. package/node_modules/@napi-rs/keyring-linux-arm64-gnu/README.md +3 -0
  38. package/node_modules/@napi-rs/keyring-linux-arm64-gnu/keyring.linux-arm64-gnu.node +0 -0
  39. package/node_modules/@napi-rs/keyring-linux-arm64-gnu/package.json +35 -0
  40. package/node_modules/@napi-rs/keyring-linux-arm64-musl/README.md +3 -0
  41. package/node_modules/@napi-rs/keyring-linux-arm64-musl/keyring.linux-arm64-musl.node +0 -0
  42. package/node_modules/@napi-rs/keyring-linux-arm64-musl/package.json +35 -0
  43. package/node_modules/@napi-rs/keyring-linux-x64-gnu/README.md +3 -0
  44. package/node_modules/@napi-rs/keyring-linux-x64-gnu/keyring.linux-x64-gnu.node +0 -0
  45. package/node_modules/@napi-rs/keyring-linux-x64-gnu/package.json +35 -0
  46. package/node_modules/@napi-rs/keyring-linux-x64-musl/README.md +3 -0
  47. package/node_modules/@napi-rs/keyring-linux-x64-musl/keyring.linux-x64-musl.node +0 -0
  48. package/node_modules/@napi-rs/keyring-linux-x64-musl/package.json +35 -0
  49. package/node_modules/@napi-rs/keyring-win32-arm64-msvc/README.md +3 -0
  50. package/node_modules/@napi-rs/keyring-win32-arm64-msvc/keyring.win32-arm64-msvc.node +0 -0
  51. package/node_modules/@napi-rs/keyring-win32-arm64-msvc/package.json +32 -0
  52. package/node_modules/@napi-rs/keyring-win32-x64-msvc/README.md +3 -0
  53. package/node_modules/@napi-rs/keyring-win32-x64-msvc/keyring.win32-x64-msvc.node +0 -0
  54. package/node_modules/@napi-rs/keyring-win32-x64-msvc/package.json +32 -0
  55. package/node_modules/@types/debug/LICENSE +21 -0
  56. package/node_modules/@types/debug/README.md +69 -0
  57. package/node_modules/@types/debug/index.d.ts +50 -0
  58. package/node_modules/@types/debug/package.json +58 -0
  59. package/node_modules/@types/mdast/LICENSE +21 -0
  60. package/node_modules/@types/mdast/README.md +15 -0
  61. package/node_modules/@types/mdast/index.d.ts +1123 -0
  62. package/node_modules/@types/mdast/package.json +47 -0
  63. package/node_modules/@types/ms/LICENSE +21 -0
  64. package/node_modules/@types/ms/README.md +82 -0
  65. package/node_modules/@types/ms/index.d.ts +63 -0
  66. package/node_modules/@types/ms/package.json +26 -0
  67. package/node_modules/@types/unist/LICENSE +21 -0
  68. package/node_modules/@types/unist/README.md +15 -0
  69. package/node_modules/@types/unist/index.d.ts +119 -0
  70. package/node_modules/@types/unist/package.json +60 -0
  71. package/node_modules/character-entities/index.d.ts +6 -0
  72. package/node_modules/character-entities/index.js +2132 -0
  73. package/node_modules/character-entities/license +22 -0
  74. package/node_modules/character-entities/package.json +78 -0
  75. package/node_modules/character-entities/readme.md +152 -0
  76. package/node_modules/decode-named-character-reference/index.d.ts +13 -0
  77. package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
  78. package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
  79. package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
  80. package/node_modules/decode-named-character-reference/index.dom.js +32 -0
  81. package/node_modules/decode-named-character-reference/index.js +19 -0
  82. package/node_modules/decode-named-character-reference/license +22 -0
  83. package/node_modules/decode-named-character-reference/package.json +90 -0
  84. package/node_modules/decode-named-character-reference/readme.md +136 -0
  85. package/node_modules/dequal/dist/index.js +86 -0
  86. package/node_modules/dequal/dist/index.min.js +1 -0
  87. package/node_modules/dequal/dist/index.mjs +84 -0
  88. package/node_modules/dequal/index.d.ts +1 -0
  89. package/node_modules/dequal/license +21 -0
  90. package/node_modules/dequal/lite/index.d.ts +1 -0
  91. package/node_modules/dequal/lite/index.js +31 -0
  92. package/node_modules/dequal/lite/index.min.js +1 -0
  93. package/node_modules/dequal/lite/index.mjs +29 -0
  94. package/node_modules/dequal/package.json +57 -0
  95. package/node_modules/dequal/readme.md +112 -0
  96. package/node_modules/devlop/lib/default.js +9 -0
  97. package/node_modules/devlop/lib/development.d.ts +84 -0
  98. package/node_modules/devlop/lib/development.js +238 -0
  99. package/node_modules/devlop/license +22 -0
  100. package/node_modules/devlop/package.json +80 -0
  101. package/node_modules/devlop/readme.md +360 -0
  102. package/node_modules/mdast-util-from-markdown/dev/index.d.ts +23 -0
  103. package/node_modules/mdast-util-from-markdown/dev/index.js +2 -0
  104. package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts +53 -0
  105. package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts.map +1 -0
  106. package/node_modules/mdast-util-from-markdown/dev/lib/index.js +1348 -0
  107. package/node_modules/mdast-util-from-markdown/dev/lib/types.d.ts +295 -0
  108. package/node_modules/mdast-util-from-markdown/dev/lib/types.js +2 -0
  109. package/node_modules/mdast-util-from-markdown/index.d.ts +23 -0
  110. package/node_modules/mdast-util-from-markdown/index.js +2 -0
  111. package/node_modules/mdast-util-from-markdown/lib/index.d.ts +53 -0
  112. package/node_modules/mdast-util-from-markdown/lib/index.js +1177 -0
  113. package/node_modules/mdast-util-from-markdown/lib/types.d.ts +295 -0
  114. package/node_modules/mdast-util-from-markdown/lib/types.js +2 -0
  115. package/node_modules/mdast-util-from-markdown/license +22 -0
  116. package/node_modules/mdast-util-from-markdown/package.json +139 -0
  117. package/node_modules/mdast-util-from-markdown/readme.md +537 -0
  118. package/node_modules/mdast-util-to-string/index.d.ts +2 -0
  119. package/node_modules/mdast-util-to-string/index.js +5 -0
  120. package/node_modules/mdast-util-to-string/lib/index.d.ts +31 -0
  121. package/node_modules/mdast-util-to-string/lib/index.js +108 -0
  122. package/node_modules/mdast-util-to-string/license +22 -0
  123. package/node_modules/mdast-util-to-string/package.json +78 -0
  124. package/node_modules/mdast-util-to-string/readme.md +218 -0
  125. package/node_modules/micromark/dev/index.d.ts +82 -0
  126. package/node_modules/micromark/dev/index.d.ts.map +1 -0
  127. package/node_modules/micromark/dev/index.js +68 -0
  128. package/node_modules/micromark/dev/lib/compile.d.ts +16 -0
  129. package/node_modules/micromark/dev/lib/compile.d.ts.map +1 -0
  130. package/node_modules/micromark/dev/lib/compile.js +1162 -0
  131. package/node_modules/micromark/dev/lib/constructs.d.ts +73 -0
  132. package/node_modules/micromark/dev/lib/constructs.d.ts.map +1 -0
  133. package/node_modules/micromark/dev/lib/constructs.js +101 -0
  134. package/node_modules/micromark/dev/lib/create-tokenizer.d.ts +46 -0
  135. package/node_modules/micromark/dev/lib/create-tokenizer.d.ts.map +1 -0
  136. package/node_modules/micromark/dev/lib/create-tokenizer.js +718 -0
  137. package/node_modules/micromark/dev/lib/initialize/content.d.ts +4 -0
  138. package/node_modules/micromark/dev/lib/initialize/content.d.ts.map +1 -0
  139. package/node_modules/micromark/dev/lib/initialize/content.js +99 -0
  140. package/node_modules/micromark/dev/lib/initialize/document.d.ts +10 -0
  141. package/node_modules/micromark/dev/lib/initialize/document.d.ts.map +1 -0
  142. package/node_modules/micromark/dev/lib/initialize/document.js +449 -0
  143. package/node_modules/micromark/dev/lib/initialize/flow.d.ts +4 -0
  144. package/node_modules/micromark/dev/lib/initialize/flow.d.ts.map +1 -0
  145. package/node_modules/micromark/dev/lib/initialize/flow.js +86 -0
  146. package/node_modules/micromark/dev/lib/initialize/text.d.ts +8 -0
  147. package/node_modules/micromark/dev/lib/initialize/text.d.ts.map +1 -0
  148. package/node_modules/micromark/dev/lib/initialize/text.js +248 -0
  149. package/node_modules/micromark/dev/lib/parse.d.ts +10 -0
  150. package/node_modules/micromark/dev/lib/parse.d.ts.map +1 -0
  151. package/node_modules/micromark/dev/lib/parse.js +58 -0
  152. package/node_modules/micromark/dev/lib/postprocess.d.ts +9 -0
  153. package/node_modules/micromark/dev/lib/postprocess.d.ts.map +1 -0
  154. package/node_modules/micromark/dev/lib/postprocess.js +19 -0
  155. package/node_modules/micromark/dev/lib/preprocess.d.ts +13 -0
  156. package/node_modules/micromark/dev/lib/preprocess.d.ts.map +1 -0
  157. package/node_modules/micromark/dev/lib/preprocess.js +139 -0
  158. package/node_modules/micromark/dev/stream.d.ts +35 -0
  159. package/node_modules/micromark/dev/stream.d.ts.map +1 -0
  160. package/node_modules/micromark/dev/stream.js +270 -0
  161. package/node_modules/micromark/index.d.ts +82 -0
  162. package/node_modules/micromark/index.d.ts.map +1 -0
  163. package/node_modules/micromark/index.js +60 -0
  164. package/node_modules/micromark/lib/compile.d.ts +16 -0
  165. package/node_modules/micromark/lib/compile.d.ts.map +1 -0
  166. package/node_modules/micromark/lib/compile.js +1068 -0
  167. package/node_modules/micromark/lib/constructs.d.ts +73 -0
  168. package/node_modules/micromark/lib/constructs.d.ts.map +1 -0
  169. package/node_modules/micromark/lib/constructs.js +85 -0
  170. package/node_modules/micromark/lib/create-tokenizer.d.ts +46 -0
  171. package/node_modules/micromark/lib/create-tokenizer.d.ts.map +1 -0
  172. package/node_modules/micromark/lib/create-tokenizer.js +613 -0
  173. package/node_modules/micromark/lib/initialize/content.d.ts +4 -0
  174. package/node_modules/micromark/lib/initialize/content.d.ts.map +1 -0
  175. package/node_modules/micromark/lib/initialize/content.js +79 -0
  176. package/node_modules/micromark/lib/initialize/document.d.ts +10 -0
  177. package/node_modules/micromark/lib/initialize/document.d.ts.map +1 -0
  178. package/node_modules/micromark/lib/initialize/document.js +370 -0
  179. package/node_modules/micromark/lib/initialize/flow.d.ts +4 -0
  180. package/node_modules/micromark/lib/initialize/flow.d.ts.map +1 -0
  181. package/node_modules/micromark/lib/initialize/flow.js +58 -0
  182. package/node_modules/micromark/lib/initialize/text.d.ts +8 -0
  183. package/node_modules/micromark/lib/initialize/text.d.ts.map +1 -0
  184. package/node_modules/micromark/lib/initialize/text.js +216 -0
  185. package/node_modules/micromark/lib/parse.d.ts +10 -0
  186. package/node_modules/micromark/lib/parse.d.ts.map +1 -0
  187. package/node_modules/micromark/lib/parse.js +56 -0
  188. package/node_modules/micromark/lib/postprocess.d.ts +9 -0
  189. package/node_modules/micromark/lib/postprocess.d.ts.map +1 -0
  190. package/node_modules/micromark/lib/postprocess.js +18 -0
  191. package/node_modules/micromark/lib/preprocess.d.ts +13 -0
  192. package/node_modules/micromark/lib/preprocess.d.ts.map +1 -0
  193. package/node_modules/micromark/lib/preprocess.js +112 -0
  194. package/node_modules/micromark/license +22 -0
  195. package/node_modules/micromark/package.json +87 -0
  196. package/node_modules/micromark/readme.md +488 -0
  197. package/node_modules/micromark/stream.d.ts +35 -0
  198. package/node_modules/micromark/stream.d.ts.map +1 -0
  199. package/node_modules/micromark/stream.js +256 -0
  200. package/node_modules/micromark-core-commonmark/dev/index.d.ts +23 -0
  201. package/node_modules/micromark-core-commonmark/dev/index.d.ts.map +1 -0
  202. package/node_modules/micromark-core-commonmark/dev/index.js +22 -0
  203. package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts +4 -0
  204. package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts.map +1 -0
  205. package/node_modules/micromark-core-commonmark/dev/lib/attention.js +294 -0
  206. package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts +4 -0
  207. package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts.map +1 -0
  208. package/node_modules/micromark-core-commonmark/dev/lib/autolink.js +269 -0
  209. package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts +4 -0
  210. package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts.map +1 -0
  211. package/node_modules/micromark-core-commonmark/dev/lib/blank-line.js +62 -0
  212. package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts +4 -0
  213. package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts.map +1 -0
  214. package/node_modules/micromark-core-commonmark/dev/lib/block-quote.js +162 -0
  215. package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts +4 -0
  216. package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts.map +1 -0
  217. package/node_modules/micromark-core-commonmark/dev/lib/character-escape.js +69 -0
  218. package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts +4 -0
  219. package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts.map +1 -0
  220. package/node_modules/micromark-core-commonmark/dev/lib/character-reference.js +166 -0
  221. package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts +4 -0
  222. package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts.map +1 -0
  223. package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.js +472 -0
  224. package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts +4 -0
  225. package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts.map +1 -0
  226. package/node_modules/micromark-core-commonmark/dev/lib/code-indented.js +178 -0
  227. package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts +4 -0
  228. package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts.map +1 -0
  229. package/node_modules/micromark-core-commonmark/dev/lib/code-text.js +257 -0
  230. package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts +7 -0
  231. package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts.map +1 -0
  232. package/node_modules/micromark-core-commonmark/dev/lib/content.js +185 -0
  233. package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts +4 -0
  234. package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts.map +1 -0
  235. package/node_modules/micromark-core-commonmark/dev/lib/definition.js +295 -0
  236. package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts +4 -0
  237. package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts.map +1 -0
  238. package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.js +65 -0
  239. package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts +4 -0
  240. package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts.map +1 -0
  241. package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.js +229 -0
  242. package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts +4 -0
  243. package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts.map +1 -0
  244. package/node_modules/micromark-core-commonmark/dev/lib/html-flow.js +938 -0
  245. package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts +4 -0
  246. package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts.map +1 -0
  247. package/node_modules/micromark-core-commonmark/dev/lib/html-text.js +783 -0
  248. package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts +4 -0
  249. package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts.map +1 -0
  250. package/node_modules/micromark-core-commonmark/dev/lib/label-end.js +658 -0
  251. package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts +4 -0
  252. package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts.map +1 -0
  253. package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.js +119 -0
  254. package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts +4 -0
  255. package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts.map +1 -0
  256. package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.js +71 -0
  257. package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts +4 -0
  258. package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts.map +1 -0
  259. package/node_modules/micromark-core-commonmark/dev/lib/line-ending.js +34 -0
  260. package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts +4 -0
  261. package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts.map +1 -0
  262. package/node_modules/micromark-core-commonmark/dev/lib/list.js +306 -0
  263. package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts +4 -0
  264. package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts.map +1 -0
  265. package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.js +67 -0
  266. package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts +4 -0
  267. package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts.map +1 -0
  268. package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.js +213 -0
  269. package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts +4 -0
  270. package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts.map +1 -0
  271. package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.js +120 -0
  272. package/node_modules/micromark-core-commonmark/index.d.ts +23 -0
  273. package/node_modules/micromark-core-commonmark/index.d.ts.map +1 -0
  274. package/node_modules/micromark-core-commonmark/index.js +22 -0
  275. package/node_modules/micromark-core-commonmark/lib/attention.d.ts +4 -0
  276. package/node_modules/micromark-core-commonmark/lib/attention.d.ts.map +1 -0
  277. package/node_modules/micromark-core-commonmark/lib/attention.js +231 -0
  278. package/node_modules/micromark-core-commonmark/lib/autolink.d.ts +4 -0
  279. package/node_modules/micromark-core-commonmark/lib/autolink.d.ts.map +1 -0
  280. package/node_modules/micromark-core-commonmark/lib/autolink.js +233 -0
  281. package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts +4 -0
  282. package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts.map +1 -0
  283. package/node_modules/micromark-core-commonmark/lib/blank-line.js +61 -0
  284. package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts +4 -0
  285. package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts.map +1 -0
  286. package/node_modules/micromark-core-commonmark/lib/block-quote.js +143 -0
  287. package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts +4 -0
  288. package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts.map +1 -0
  289. package/node_modules/micromark-core-commonmark/lib/character-escape.js +64 -0
  290. package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts +4 -0
  291. package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts.map +1 -0
  292. package/node_modules/micromark-core-commonmark/lib/character-reference.js +149 -0
  293. package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts +4 -0
  294. package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts.map +1 -0
  295. package/node_modules/micromark-core-commonmark/lib/code-fenced.js +422 -0
  296. package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts +4 -0
  297. package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts.map +1 -0
  298. package/node_modules/micromark-core-commonmark/lib/code-indented.js +159 -0
  299. package/node_modules/micromark-core-commonmark/lib/code-text.d.ts +4 -0
  300. package/node_modules/micromark-core-commonmark/lib/code-text.d.ts.map +1 -0
  301. package/node_modules/micromark-core-commonmark/lib/code-text.js +225 -0
  302. package/node_modules/micromark-core-commonmark/lib/content.d.ts +7 -0
  303. package/node_modules/micromark-core-commonmark/lib/content.d.ts.map +1 -0
  304. package/node_modules/micromark-core-commonmark/lib/content.js +163 -0
  305. package/node_modules/micromark-core-commonmark/lib/definition.d.ts +4 -0
  306. package/node_modules/micromark-core-commonmark/lib/definition.d.ts.map +1 -0
  307. package/node_modules/micromark-core-commonmark/lib/definition.js +254 -0
  308. package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts +4 -0
  309. package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts.map +1 -0
  310. package/node_modules/micromark-core-commonmark/lib/hard-break-escape.js +60 -0
  311. package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts +4 -0
  312. package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts.map +1 -0
  313. package/node_modules/micromark-core-commonmark/lib/heading-atx.js +191 -0
  314. package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts +4 -0
  315. package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts.map +1 -0
  316. package/node_modules/micromark-core-commonmark/lib/html-flow.js +827 -0
  317. package/node_modules/micromark-core-commonmark/lib/html-text.d.ts +4 -0
  318. package/node_modules/micromark-core-commonmark/lib/html-text.d.ts.map +1 -0
  319. package/node_modules/micromark-core-commonmark/lib/html-text.js +678 -0
  320. package/node_modules/micromark-core-commonmark/lib/label-end.d.ts +4 -0
  321. package/node_modules/micromark-core-commonmark/lib/label-end.d.ts.map +1 -0
  322. package/node_modules/micromark-core-commonmark/lib/label-end.js +561 -0
  323. package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts +4 -0
  324. package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts.map +1 -0
  325. package/node_modules/micromark-core-commonmark/lib/label-start-image.js +110 -0
  326. package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts +4 -0
  327. package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts.map +1 -0
  328. package/node_modules/micromark-core-commonmark/lib/label-start-link.js +63 -0
  329. package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts +4 -0
  330. package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts.map +1 -0
  331. package/node_modules/micromark-core-commonmark/lib/line-ending.js +33 -0
  332. package/node_modules/micromark-core-commonmark/lib/list.d.ts +4 -0
  333. package/node_modules/micromark-core-commonmark/lib/list.d.ts.map +1 -0
  334. package/node_modules/micromark-core-commonmark/lib/list.js +213 -0
  335. package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts +4 -0
  336. package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts.map +1 -0
  337. package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.js +61 -0
  338. package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts +4 -0
  339. package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts.map +1 -0
  340. package/node_modules/micromark-core-commonmark/lib/setext-underline.js +187 -0
  341. package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts +4 -0
  342. package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts.map +1 -0
  343. package/node_modules/micromark-core-commonmark/lib/thematic-break.js +102 -0
  344. package/node_modules/micromark-core-commonmark/license +22 -0
  345. package/node_modules/micromark-core-commonmark/package.json +62 -0
  346. package/node_modules/micromark-core-commonmark/readme.md +171 -0
  347. package/node_modules/micromark-factory-destination/dev/index.d.ts +42 -0
  348. package/node_modules/micromark-factory-destination/dev/index.d.ts.map +1 -0
  349. package/node_modules/micromark-factory-destination/dev/index.js +255 -0
  350. package/node_modules/micromark-factory-destination/index.d.ts +42 -0
  351. package/node_modules/micromark-factory-destination/index.d.ts.map +1 -0
  352. package/node_modules/micromark-factory-destination/index.js +206 -0
  353. package/node_modules/micromark-factory-destination/license +22 -0
  354. package/node_modules/micromark-factory-destination/package.json +57 -0
  355. package/node_modules/micromark-factory-destination/readme.md +234 -0
  356. package/node_modules/micromark-factory-label/dev/index.d.ts +37 -0
  357. package/node_modules/micromark-factory-label/dev/index.d.ts.map +1 -0
  358. package/node_modules/micromark-factory-label/dev/index.js +172 -0
  359. package/node_modules/micromark-factory-label/index.d.ts +37 -0
  360. package/node_modules/micromark-factory-label/index.d.ts.map +1 -0
  361. package/node_modules/micromark-factory-label/index.js +148 -0
  362. package/node_modules/micromark-factory-label/license +22 -0
  363. package/node_modules/micromark-factory-label/package.json +60 -0
  364. package/node_modules/micromark-factory-label/readme.md +224 -0
  365. package/node_modules/micromark-factory-space/dev/index.d.ts +60 -0
  366. package/node_modules/micromark-factory-space/dev/index.d.ts.map +1 -0
  367. package/node_modules/micromark-factory-space/dev/index.js +122 -0
  368. package/node_modules/micromark-factory-space/index.d.ts +60 -0
  369. package/node_modules/micromark-factory-space/index.d.ts.map +1 -0
  370. package/node_modules/micromark-factory-space/index.js +116 -0
  371. package/node_modules/micromark-factory-space/license +22 -0
  372. package/node_modules/micromark-factory-space/package.json +46 -0
  373. package/node_modules/micromark-factory-space/readme.md +225 -0
  374. package/node_modules/micromark-factory-title/dev/index.d.ts +36 -0
  375. package/node_modules/micromark-factory-title/dev/index.d.ts.map +1 -0
  376. package/node_modules/micromark-factory-title/dev/index.js +169 -0
  377. package/node_modules/micromark-factory-title/index.d.ts +36 -0
  378. package/node_modules/micromark-factory-title/index.d.ts.map +1 -0
  379. package/node_modules/micromark-factory-title/index.js +158 -0
  380. package/node_modules/micromark-factory-title/license +22 -0
  381. package/node_modules/micromark-factory-title/package.json +58 -0
  382. package/node_modules/micromark-factory-title/readme.md +229 -0
  383. package/node_modules/micromark-factory-whitespace/dev/index.d.ts +22 -0
  384. package/node_modules/micromark-factory-whitespace/dev/index.d.ts.map +1 -0
  385. package/node_modules/micromark-factory-whitespace/dev/index.js +53 -0
  386. package/node_modules/micromark-factory-whitespace/index.d.ts +22 -0
  387. package/node_modules/micromark-factory-whitespace/index.d.ts.map +1 -0
  388. package/node_modules/micromark-factory-whitespace/index.js +44 -0
  389. package/node_modules/micromark-factory-whitespace/license +22 -0
  390. package/node_modules/micromark-factory-whitespace/package.json +57 -0
  391. package/node_modules/micromark-factory-whitespace/readme.md +205 -0
  392. package/node_modules/micromark-util-character/dev/index.d.ts +195 -0
  393. package/node_modules/micromark-util-character/dev/index.d.ts.map +1 -0
  394. package/node_modules/micromark-util-character/dev/index.js +252 -0
  395. package/node_modules/micromark-util-character/index.d.ts +195 -0
  396. package/node_modules/micromark-util-character/index.d.ts.map +1 -0
  397. package/node_modules/micromark-util-character/index.js +246 -0
  398. package/node_modules/micromark-util-character/license +22 -0
  399. package/node_modules/micromark-util-character/package.json +57 -0
  400. package/node_modules/micromark-util-character/readme.md +446 -0
  401. package/node_modules/micromark-util-chunked/dev/index.d.ts +41 -0
  402. package/node_modules/micromark-util-chunked/dev/index.d.ts.map +1 -0
  403. package/node_modules/micromark-util-chunked/dev/index.js +89 -0
  404. package/node_modules/micromark-util-chunked/index.d.ts +41 -0
  405. package/node_modules/micromark-util-chunked/index.d.ts.map +1 -0
  406. package/node_modules/micromark-util-chunked/index.js +81 -0
  407. package/node_modules/micromark-util-chunked/license +22 -0
  408. package/node_modules/micromark-util-chunked/package.json +57 -0
  409. package/node_modules/micromark-util-chunked/readme.md +219 -0
  410. package/node_modules/micromark-util-classify-character/dev/index.d.ts +18 -0
  411. package/node_modules/micromark-util-classify-character/dev/index.d.ts.map +1 -0
  412. package/node_modules/micromark-util-classify-character/dev/index.js +38 -0
  413. package/node_modules/micromark-util-classify-character/index.d.ts +18 -0
  414. package/node_modules/micromark-util-classify-character/index.d.ts.map +1 -0
  415. package/node_modules/micromark-util-classify-character/index.js +27 -0
  416. package/node_modules/micromark-util-classify-character/license +22 -0
  417. package/node_modules/micromark-util-classify-character/package.json +59 -0
  418. package/node_modules/micromark-util-classify-character/readme.md +205 -0
  419. package/node_modules/micromark-util-combine-extensions/index.d.ts +22 -0
  420. package/node_modules/micromark-util-combine-extensions/index.d.ts.map +1 -0
  421. package/node_modules/micromark-util-combine-extensions/index.js +143 -0
  422. package/node_modules/micromark-util-combine-extensions/license +22 -0
  423. package/node_modules/micromark-util-combine-extensions/package.json +52 -0
  424. package/node_modules/micromark-util-combine-extensions/readme.md +201 -0
  425. package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts +16 -0
  426. package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts.map +1 -0
  427. package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.js +42 -0
  428. package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts +16 -0
  429. package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts.map +1 -0
  430. package/node_modules/micromark-util-decode-numeric-character-reference/index.js +32 -0
  431. package/node_modules/micromark-util-decode-numeric-character-reference/license +22 -0
  432. package/node_modules/micromark-util-decode-numeric-character-reference/package.json +59 -0
  433. package/node_modules/micromark-util-decode-numeric-character-reference/readme.md +184 -0
  434. package/node_modules/micromark-util-decode-string/dev/index.d.ts +14 -0
  435. package/node_modules/micromark-util-decode-string/dev/index.d.ts.map +1 -0
  436. package/node_modules/micromark-util-decode-string/dev/index.js +53 -0
  437. package/node_modules/micromark-util-decode-string/index.d.ts +14 -0
  438. package/node_modules/micromark-util-decode-string/index.d.ts.map +1 -0
  439. package/node_modules/micromark-util-decode-string/index.js +45 -0
  440. package/node_modules/micromark-util-decode-string/license +22 -0
  441. package/node_modules/micromark-util-decode-string/package.json +63 -0
  442. package/node_modules/micromark-util-decode-string/readme.md +179 -0
  443. package/node_modules/micromark-util-edit-map/dev/index.d.ts +78 -0
  444. package/node_modules/micromark-util-edit-map/dev/index.d.ts.map +1 -0
  445. package/node_modules/micromark-util-edit-map/dev/index.js +237 -0
  446. package/node_modules/micromark-util-edit-map/index.d.ts +78 -0
  447. package/node_modules/micromark-util-edit-map/index.d.ts.map +1 -0
  448. package/node_modules/micromark-util-edit-map/index.js +225 -0
  449. package/node_modules/micromark-util-edit-map/license +22 -0
  450. package/node_modules/micromark-util-edit-map/package.json +48 -0
  451. package/node_modules/micromark-util-edit-map/readme.md +234 -0
  452. package/node_modules/micromark-util-encode/index.d.ts +14 -0
  453. package/node_modules/micromark-util-encode/index.d.ts.map +1 -0
  454. package/node_modules/micromark-util-encode/index.js +33 -0
  455. package/node_modules/micromark-util-encode/license +22 -0
  456. package/node_modules/micromark-util-encode/package.json +47 -0
  457. package/node_modules/micromark-util-encode/readme.md +176 -0
  458. package/node_modules/micromark-util-html-tag-name/index.d.ts +30 -0
  459. package/node_modules/micromark-util-html-tag-name/index.d.ts.map +1 -0
  460. package/node_modules/micromark-util-html-tag-name/index.js +93 -0
  461. package/node_modules/micromark-util-html-tag-name/license +22 -0
  462. package/node_modules/micromark-util-html-tag-name/package.json +47 -0
  463. package/node_modules/micromark-util-html-tag-name/readme.md +193 -0
  464. package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts +21 -0
  465. package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts.map +1 -0
  466. package/node_modules/micromark-util-normalize-identifier/dev/index.js +38 -0
  467. package/node_modules/micromark-util-normalize-identifier/index.d.ts +21 -0
  468. package/node_modules/micromark-util-normalize-identifier/index.d.ts.map +1 -0
  469. package/node_modules/micromark-util-normalize-identifier/index.js +33 -0
  470. package/node_modules/micromark-util-normalize-identifier/license +22 -0
  471. package/node_modules/micromark-util-normalize-identifier/package.json +58 -0
  472. package/node_modules/micromark-util-normalize-identifier/readme.md +187 -0
  473. package/node_modules/micromark-util-resolve-all/index.d.ts +22 -0
  474. package/node_modules/micromark-util-resolve-all/index.d.ts.map +1 -0
  475. package/node_modules/micromark-util-resolve-all/index.js +32 -0
  476. package/node_modules/micromark-util-resolve-all/license +22 -0
  477. package/node_modules/micromark-util-resolve-all/package.json +48 -0
  478. package/node_modules/micromark-util-resolve-all/readme.md +238 -0
  479. package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts +36 -0
  480. package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts.map +1 -0
  481. package/node_modules/micromark-util-sanitize-uri/dev/index.js +124 -0
  482. package/node_modules/micromark-util-sanitize-uri/index.d.ts +36 -0
  483. package/node_modules/micromark-util-sanitize-uri/index.d.ts.map +1 -0
  484. package/node_modules/micromark-util-sanitize-uri/index.js +107 -0
  485. package/node_modules/micromark-util-sanitize-uri/license +22 -0
  486. package/node_modules/micromark-util-sanitize-uri/package.json +59 -0
  487. package/node_modules/micromark-util-sanitize-uri/readme.md +214 -0
  488. package/node_modules/micromark-util-subtokenize/dev/index.d.ts +12 -0
  489. package/node_modules/micromark-util-subtokenize/dev/index.d.ts.map +1 -0
  490. package/node_modules/micromark-util-subtokenize/dev/index.js +284 -0
  491. package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts +161 -0
  492. package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts.map +1 -0
  493. package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.js +288 -0
  494. package/node_modules/micromark-util-subtokenize/index.d.ts +12 -0
  495. package/node_modules/micromark-util-subtokenize/index.d.ts.map +1 -0
  496. package/node_modules/micromark-util-subtokenize/index.js +228 -0
  497. package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts +161 -0
  498. package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts.map +1 -0
  499. package/node_modules/micromark-util-subtokenize/lib/splice-buffer.js +251 -0
  500. package/node_modules/micromark-util-subtokenize/license +22 -0
  501. package/node_modules/micromark-util-subtokenize/package.json +60 -0
  502. package/node_modules/micromark-util-subtokenize/readme.md +181 -0
  503. package/node_modules/micromark-util-symbol/lib/codes.d.ts +139 -0
  504. package/node_modules/micromark-util-symbol/lib/codes.d.ts.map +1 -0
  505. package/node_modules/micromark-util-symbol/lib/codes.js +158 -0
  506. package/node_modules/micromark-util-symbol/lib/constants.d.ts +37 -0
  507. package/node_modules/micromark-util-symbol/lib/constants.d.ts.map +1 -0
  508. package/node_modules/micromark-util-symbol/lib/constants.js +44 -0
  509. package/node_modules/micromark-util-symbol/lib/default.d.ts +5 -0
  510. package/node_modules/micromark-util-symbol/lib/default.d.ts.map +1 -0
  511. package/node_modules/micromark-util-symbol/lib/default.js +4 -0
  512. package/node_modules/micromark-util-symbol/lib/types.d.ts +106 -0
  513. package/node_modules/micromark-util-symbol/lib/types.d.ts.map +1 -0
  514. package/node_modules/micromark-util-symbol/lib/types.js +453 -0
  515. package/node_modules/micromark-util-symbol/lib/values.d.ts +102 -0
  516. package/node_modules/micromark-util-symbol/lib/values.d.ts.map +1 -0
  517. package/node_modules/micromark-util-symbol/lib/values.js +109 -0
  518. package/node_modules/micromark-util-symbol/license +22 -0
  519. package/node_modules/micromark-util-symbol/package.json +43 -0
  520. package/node_modules/micromark-util-symbol/readme.md +168 -0
  521. package/node_modules/micromark-util-types/index.d.ts +1315 -0
  522. package/node_modules/micromark-util-types/index.js +2 -0
  523. package/node_modules/micromark-util-types/license +22 -0
  524. package/node_modules/micromark-util-types/package.json +37 -0
  525. package/node_modules/micromark-util-types/readme.md +151 -0
  526. package/node_modules/smol-toml/LICENSE +24 -0
  527. package/node_modules/smol-toml/README.md +605 -0
  528. package/node_modules/smol-toml/dist/date.d.ts +41 -0
  529. package/node_modules/smol-toml/dist/date.js +156 -0
  530. package/node_modules/smol-toml/dist/error.d.ts +37 -0
  531. package/node_modules/smol-toml/dist/error.js +67 -0
  532. package/node_modules/smol-toml/dist/extract.js +289 -0
  533. package/node_modules/smol-toml/dist/index.cjs +770 -0
  534. package/node_modules/smol-toml/dist/index.d.ts +44 -0
  535. package/node_modules/smol-toml/dist/index.js +34 -0
  536. package/node_modules/smol-toml/dist/parse.d.ts +73 -0
  537. package/node_modules/smol-toml/dist/parse.js +164 -0
  538. package/node_modules/smol-toml/dist/primitive.js +151 -0
  539. package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
  540. package/node_modules/smol-toml/dist/stringify.js +219 -0
  541. package/node_modules/smol-toml/dist/struct.js +156 -0
  542. package/node_modules/smol-toml/dist/util.d.ts +43 -0
  543. package/node_modules/smol-toml/dist/util.js +57 -0
  544. package/node_modules/smol-toml/package.json +68 -0
  545. package/node_modules/unist-util-stringify-position/index.d.ts +1 -0
  546. package/node_modules/unist-util-stringify-position/index.js +1 -0
  547. package/node_modules/unist-util-stringify-position/lib/index.d.ts +61 -0
  548. package/node_modules/unist-util-stringify-position/lib/index.js +84 -0
  549. package/node_modules/unist-util-stringify-position/license +22 -0
  550. package/node_modules/unist-util-stringify-position/package.json +80 -0
  551. package/node_modules/unist-util-stringify-position/readme.md +206 -0
  552. package/package.json +27 -8
  553. package/scripts/prepare-github-keyring.js +29 -0
  554. package/src/codex-app.js +20 -2
  555. package/src/codex-pricing.js +79 -0
  556. package/src/codex-projects.js +90 -0
  557. package/src/codex-session-scanner.js +748 -0
  558. package/src/codex-session-usage.js +332 -0
  559. package/src/dashboard/app.js +2 -2
  560. package/src/dashboard/dist/app.js +135 -60
  561. package/src/dashboard/dist/app.js.map +1 -1
  562. package/src/dashboard/dist/styles.css +1 -1
  563. package/src/dashboard/react/components/AnimatedUsage.test.tsx +78 -0
  564. package/src/dashboard/react/components/CardStatusIcons.test.tsx +146 -0
  565. package/src/dashboard/react/components/CardStatusIcons.tsx +138 -0
  566. package/src/dashboard/react/components/ImageGallery.tsx +22 -0
  567. package/src/dashboard/react/components/ModelSettings.test.tsx +4 -4
  568. package/src/dashboard/react/components/ModelSettings.tsx +1 -1
  569. package/src/dashboard/react/components/OpenChatButton.test.tsx +19 -0
  570. package/src/dashboard/react/components/OpenChatButton.tsx +2 -1
  571. package/src/dashboard/react/components/PrGlyph.tsx +35 -0
  572. package/src/dashboard/react/components/PullRequestBadges.tsx +18 -0
  573. package/src/dashboard/react/components/QueuedPrompts.test.tsx +42 -0
  574. package/src/dashboard/react/components/QueuedPrompts.tsx +34 -0
  575. package/src/dashboard/react/components/RelativeTime.test.tsx +100 -0
  576. package/src/dashboard/react/components/RelativeTime.tsx +46 -11
  577. package/src/dashboard/react/components/ReplyCover.test.tsx +30 -0
  578. package/src/dashboard/react/components/ReplyCover.tsx +33 -0
  579. package/src/dashboard/react/components/ReplyImagePreview.test.tsx +90 -0
  580. package/src/dashboard/react/components/ReplyImagePreview.tsx +34 -0
  581. package/src/dashboard/react/components/ReplyMarkdown.test.tsx +108 -0
  582. package/src/dashboard/react/components/ReplyMarkdown.tsx +112 -0
  583. package/src/dashboard/react/components/SavedUsageFormat.test.tsx +11 -0
  584. package/src/dashboard/react/components/SavedUsageFormat.tsx +68 -0
  585. package/src/dashboard/react/components/StatusBadge.tsx +2 -2
  586. package/src/dashboard/react/components/TaskBoard.test.tsx +256 -1
  587. package/src/dashboard/react/components/TaskBoard.tsx +136 -28
  588. package/src/dashboard/react/components/TaskCard.tsx +7 -4
  589. package/src/dashboard/react/components/TaskDetail.test.tsx +92 -1
  590. package/src/dashboard/react/components/TaskDetail.tsx +37 -10
  591. package/src/dashboard/react/components/TurnContent.test.tsx +44 -0
  592. package/src/dashboard/react/components/TurnContent.tsx +53 -0
  593. package/src/dashboard/react/components/UsagePanel.tsx +2 -2
  594. package/src/dashboard/react/image-cache.test.ts +23 -0
  595. package/src/dashboard/react/image-cache.ts +33 -0
  596. package/src/dashboard/react/presentation.test.tsx +31 -0
  597. package/src/dashboard/react/styles.css +126 -2
  598. package/src/dashboard/react/types.ts +55 -1
  599. package/src/dashboard/time.js +39 -0
  600. package/src/dashboard.js +4 -3
  601. package/src/data/codex-pricing.json +537 -0
  602. package/src/mcp-app/dist/index.html +216 -112
  603. package/src/mcp-app/react/AppearancePicker.test.tsx +50 -0
  604. package/src/mcp-app/react/AppearancePicker.tsx +56 -0
  605. package/src/mcp-app/react/GitHubConnection.test.tsx +78 -0
  606. package/src/mcp-app/react/GitHubConnection.tsx +78 -0
  607. package/src/mcp-app/react/NextNotificationCenter.test.tsx +121 -0
  608. package/src/mcp-app/react/NextNotificationCenter.tsx +94 -0
  609. package/src/mcp-app/react/NextTaskDetail.test.tsx +124 -0
  610. package/src/mcp-app/react/NextTaskDetail.tsx +147 -0
  611. package/src/mcp-app/react/ProjectPicker.tsx +49 -0
  612. package/src/mcp-app/react/main.test.tsx +499 -49
  613. package/src/mcp-app/react/main.tsx +359 -86
  614. package/src/mcp-app/react/styles.css +186 -9
  615. package/src/mcp-app/react/themes.test.ts +34 -0
  616. package/src/mcp-app/react/themes.ts +112 -0
  617. package/src/mcp-app.js +238 -120
  618. package/src/mcp.js +8 -1
  619. package/src/next-cache.js +16 -0
  620. package/src/next-github.js +273 -0
  621. package/src/next-notifications.js +90 -0
  622. package/src/recorded-usage-tracker.js +92 -0
  623. package/src/reply-image.js +65 -0
  624. package/src/usage-tracker.js +7 -6
  625. package/src/usage.js +7 -355
  626. package/src/user-prompt.js +55 -0
  627. package/src/version.js +2 -1
  628. package/node_modules/proper-lockfile/CHANGELOG.md +0 -108
  629. package/node_modules/which/CHANGELOG.md +0 -166
  630. package/src/resolve-ccusage.js +0 -17
@@ -0,0 +1,50 @@
1
+ import { act, cleanup, fireEvent, render, screen } from "@testing-library/react";
2
+ import { MantineProvider } from "@mantine/core";
3
+ import { afterEach, beforeEach, expect, test, vi } from "vitest";
4
+ import { AppearancePicker } from "./AppearancePicker";
5
+
6
+ beforeEach(() => vi.stubGlobal("PointerEvent", class extends MouseEvent {
7
+ pointerType: string;
8
+ constructor(type: string, init: PointerEventInit = {}) { super(type, init); this.pointerType = init.pointerType ?? "mouse"; }
9
+ }));
10
+ afterEach(() => { cleanup(); vi.useRealTimers(); vi.unstubAllGlobals(); });
11
+ test("hover stays open while crossing into the panel and closes after leaving", () => {
12
+ vi.useFakeTimers();
13
+ render(<MantineProvider><AppearancePicker value="dark" onChange={vi.fn()} /></MantineProvider>);
14
+ const button = screen.getByRole("button", { name: "Appearance" });
15
+ fireEvent.pointerEnter(button, { pointerType: "mouse" });
16
+ const panel = screen.getByRole("region", { name: "Appearance settings" });
17
+ fireEvent.pointerLeave(button, { pointerType: "mouse" });
18
+ fireEvent.pointerEnter(panel, { pointerType: "mouse" });
19
+ act(() => vi.advanceTimersByTime(250));
20
+ expect(panel).toBeVisible();
21
+ fireEvent.pointerLeave(panel, { pointerType: "mouse" });
22
+ act(() => vi.advanceTimersByTime(250));
23
+ expect(screen.queryByRole("region", { name: "Appearance settings" })).not.toBeInTheDocument();
24
+ });
25
+ test("tap opens, appearance changes, and Escape restores focus", () => {
26
+ const onChange = vi.fn();
27
+ render(<MantineProvider><AppearancePicker value="dark" onChange={onChange} /></MantineProvider>);
28
+ const button = screen.getByRole("button", { name: "Appearance" });
29
+ fireEvent.pointerEnter(button, { pointerType: "touch" });
30
+ expect(screen.queryByRole("region", { name: "Appearance settings" })).not.toBeInTheDocument();
31
+ fireEvent.click(button);
32
+ fireEvent.click(screen.getByRole("radio", { name: "DAY" }));
33
+ expect(onChange).toHaveBeenCalledWith("light");
34
+ fireEvent.keyDown(document, { key: "Escape" });
35
+ expect(screen.queryByRole("region", { name: "Appearance settings" })).not.toBeInTheDocument();
36
+ expect(button).toHaveFocus();
37
+ });
38
+ test("theme rows use native variants and keyboard selection", () => {
39
+ const onVisualThemeChange = vi.fn();
40
+ render(<MantineProvider><AppearancePicker value="light" appearance="light" visualThemeId="chatgpt" onChange={vi.fn()} onVisualThemeChange={onVisualThemeChange} /></MantineProvider>);
41
+ fireEvent.click(screen.getByRole("button", { name: "Appearance" }));
42
+ expect(screen.queryByRole("radio", { name: "Dracula" })).not.toBeInTheDocument();
43
+ expect(screen.getByRole("radio", { name: "Proof" })).toBeVisible();
44
+ const selected = screen.getByRole("radio", { name: "ChatGPT" });
45
+ selected.focus();
46
+ fireEvent.keyDown(selected, { key: "ArrowDown" });
47
+ expect(screen.queryByRole("radio", { name: "Ayu" })).not.toBeInTheDocument();
48
+ expect(onVisualThemeChange).toHaveBeenCalledWith("absolutely");
49
+ expect(screen.getByRole("radio", { name: "Absolutely" })).toHaveFocus();
50
+ });
@@ -0,0 +1,56 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { SegmentedControl } from "@mantine/core";
3
+ import { IconCheck, IconPalette } from "@tabler/icons-react";
4
+
5
+ import { VISUAL_THEMES, themeVariables, type Appearance } from "./themes";
6
+ export function AppearancePicker({ value, onChange, visualThemeId = "chatgpt", appearance = "dark", onVisualThemeChange }: { value: "light" | "dark" | "system"; onChange: (value: string) => void; visualThemeId?: string; appearance?: Appearance; onVisualThemeChange?: (id: string) => void }) {
7
+ const [opened, setOpened] = useState(false);
8
+ const root = useRef<HTMLDivElement>(null);
9
+ const button = useRef<HTMLButtonElement>(null);
10
+ const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
11
+ const cancelClose = () => { if (timer.current !== null) clearTimeout(timer.current); timer.current = null; };
12
+ const close = () => { cancelClose(); setOpened(false); };
13
+ const open = () => { cancelClose(); setOpened(true); };
14
+ const enter = (event: React.PointerEvent) => { if (event.pointerType !== "touch") open(); };
15
+ const leave = (event: React.PointerEvent) => {
16
+ if (event.pointerType === "touch") return;
17
+ cancelClose();
18
+ timer.current = setTimeout(close, 200);
19
+ };
20
+ useEffect(() => cancelClose, []);
21
+ useEffect(() => {
22
+ if (!opened) return;
23
+ const outside = (event: PointerEvent) => { if (!root.current?.contains(event.target as Node)) close(); };
24
+ const escape = (event: KeyboardEvent) => { if (event.key === "Escape") { close(); button.current?.focus(); } };
25
+ document.addEventListener("pointerdown", outside);
26
+ document.addEventListener("keydown", escape);
27
+ return () => { document.removeEventListener("pointerdown", outside); document.removeEventListener("keydown", escape); };
28
+ }, [opened]);
29
+ return <div className="next-appearance" ref={root}>
30
+ <button ref={button} className="next-icon-button" aria-label="Appearance" title="Appearance" aria-expanded={opened} aria-controls="next-appearance-panel" onPointerEnter={enter} onPointerLeave={leave} onClick={open}><IconPalette size={18} /></button>
31
+ {opened && <section id="next-appearance-panel" className="next-appearance-panel" aria-label="Appearance settings" onPointerEnter={enter} onPointerLeave={leave}>
32
+ <h2>Appearance</h2>
33
+ <SegmentedControl fullWidth aria-label="Light and dark appearance" value={value} onChange={onChange} data={[{ value: "light", label: "DAY" }, { value: "dark", label: "NIGHT" }, { value: "system", label: "SYSTEM" }]} size="xs" />
34
+ <h3>Theme</h3>
35
+ <div className="next-theme-list" role="radiogroup" aria-label="Theme" onKeyDown={event => {
36
+ if (!["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) return;
37
+ const buttons = Array.from(event.currentTarget.querySelectorAll<HTMLButtonElement>('button[role="radio"]'));
38
+ const current = buttons.indexOf(event.target as HTMLButtonElement);
39
+ if (current < 0) return;
40
+ event.preventDefault();
41
+ const index = event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : (current + (event.key === "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length;
42
+ buttons[index].focus(); buttons[index].click();
43
+ }}>
44
+ {VISUAL_THEMES.filter(item => item.variants[appearance]).sort((a, b) => a.id === "chatgpt" ? -1 : b.id === "chatgpt" ? 1 : a.label.localeCompare(b.label)).map((item, index) => {
45
+ const colors = themeVariables(item, appearance);
46
+ return <div key={item.id}>
47
+ {index === 1 && <div className="next-theme-group">Codex themes</div>}
48
+ <button role="radio" tabIndex={visualThemeId === item.id ? 0 : -1} aria-checked={visualThemeId === item.id} className="next-appearance-theme" onClick={() => onVisualThemeChange?.(item.id)}>
49
+ <span className="next-appearance-preview" style={{ background: colors["--taskchef-surface"], color: colors["--taskchef-link"], borderColor: colors["--taskchef-border-heavy"], fontFamily: colors["--taskchef-font"] }} aria-hidden="true">Aa</span><span>{item.label}</span>{visualThemeId === item.id && <IconCheck size={16} aria-hidden="true" />}
50
+ </button>
51
+ </div>;
52
+ })}
53
+ </div>
54
+ </section>}
55
+ </div>;
56
+ }
@@ -0,0 +1,78 @@
1
+ import { MantineProvider } from "@mantine/core";
2
+ import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
3
+ import { afterEach, expect, test, vi } from "vitest";
4
+ import { GitHubConnection } from "./GitHubConnection";
5
+
6
+ afterEach(() => { cleanup(); vi.useRealTimers(); vi.restoreAllMocks(); });
7
+ const auth = { configured: true, connected: false, login: null };
8
+ function setup(props: Partial<Parameters<typeof GitHubConnection>[0]> = {}) {
9
+ const request = vi.fn().mockResolvedValue({ ...auth, pending: { userCode: "ABCD-EFGH", verificationUrl: "https://github.com/login/device", expiresAt: Date.now() + 900_000 } });
10
+ const openLink = vi.fn().mockResolvedValue({});
11
+ const refresh = vi.fn().mockResolvedValue({});
12
+ const openSettings = vi.fn().mockResolvedValue({});
13
+ render(<MantineProvider><GitHubConnection auth={auth} request={request} openLink={openLink} refresh={refresh} openSettings={openSettings} {...props} /></MantineProvider>);
14
+ fireEvent.click(screen.getByRole("button", { name: "GitHub connection" }));
15
+ return { request, openLink, refresh, openSettings };
16
+ }
17
+ test("unconfigured connection directs users to native plugin settings", async () => {
18
+ const c = setup({ auth: { ...auth, configured: false } });
19
+ fireEvent.click(await screen.findByRole("button", { name: "Open plugin settings" }));
20
+ await waitFor(() => expect(c.openSettings).toHaveBeenCalledOnce());
21
+ });
22
+ test("sign-in displays only the user code and opens GitHub's verification page", async () => {
23
+ const c = setup();
24
+ expect(await screen.findByText(/read PR and CI status for your selected repositories/)).toBeInTheDocument();
25
+ expect(screen.getByRole("button", { name: "Repository access" })).toHaveAttribute("data-variant", "default");
26
+ fireEvent.click(await screen.findByRole("button", { name: "Sign in with GitHub" }));
27
+ expect(await screen.findByText("ABCD-EFGH")).toBeInTheDocument();
28
+ expect(c.request).toHaveBeenCalledWith("start");
29
+ fireEvent.click(screen.getByRole("button", { name: "Open GitHub" }));
30
+ await waitFor(() => expect(c.openLink).toHaveBeenCalledWith("https://github.com/login/device"));
31
+ });
32
+ test("connected account can remove its local sign-in and refresh the board", async () => {
33
+ const request = vi.fn().mockResolvedValue(auth);
34
+ const c = setup({ auth: { ...auth, connected: true, login: "tester" }, request });
35
+ expect(await screen.findByText(/Install TaskChef in each organization/)).toBeInTheDocument();
36
+ expect(screen.queryByText(/cached for one minute/)).not.toBeInTheDocument();
37
+ expect(screen.getByRole("button", { name: "Manage repository access" })).toHaveAttribute("data-variant", "default");
38
+ expect(await screen.findByText("tester")).toBeInTheDocument();
39
+ fireEvent.click(screen.getByRole("button", { name: "Disconnect this computer" }));
40
+ await waitFor(() => expect(request).toHaveBeenCalledWith("disconnect"));
41
+ await waitFor(() => expect(c.refresh).toHaveBeenCalledOnce());
42
+ expect(await screen.findByRole("button", { name: "Sign in with GitHub" })).toBeInTheDocument();
43
+ });
44
+
45
+ test("five-second board snapshots do not reset the pending sign-in timer", async () => {
46
+ vi.useFakeTimers();
47
+ const pending = { userCode: "ABCD-EFGH", verificationUrl: "https://github.com/login/device", expiresAt: Date.now() + 900_000 };
48
+ const request = vi.fn().mockResolvedValue({ ...auth, pending });
49
+ const refresh = vi.fn().mockResolvedValue({});
50
+ const props = { request, refresh, openSettings: vi.fn(), openLink: vi.fn() };
51
+ const { rerender } = render(<MantineProvider><GitHubConnection {...props} auth={{ ...auth, pending }} /></MantineProvider>);
52
+ fireEvent.click(screen.getByRole("button", { name: "GitHub connection" }));
53
+ await act(async () => { vi.advanceTimersByTime(4000); });
54
+ rerender(<MantineProvider><GitHubConnection {...props} auth={{ ...auth, pending: { ...pending } }} /></MantineProvider>);
55
+ await act(async () => { vi.advanceTimersByTime(1000); });
56
+ expect(request).toHaveBeenCalledWith("poll");
57
+ });
58
+
59
+
60
+ test("pending sign-in mounted hidden resumes polling after the panel becomes visible", async () => {
61
+ vi.useFakeTimers();
62
+ let visibility: DocumentVisibilityState = "hidden";
63
+ vi.spyOn(document, "visibilityState", "get").mockImplementation(() => visibility);
64
+ const pending = {userCode:"ABCD-EFGH",verificationUrl:"https://github.com/login/device",expiresAt:Date.now()+900000};
65
+ const request = vi.fn().mockResolvedValue({...auth,connected:true,login:"tester"});
66
+ const refresh = vi.fn().mockResolvedValue({});
67
+ render(<MantineProvider><GitHubConnection inline auth={{...auth,pending}} request={request} refresh={refresh} openSettings={vi.fn()} openLink={vi.fn()} /></MantineProvider>);
68
+ await act(async () => { vi.advanceTimersByTime(10000); });
69
+ expect(request).not.toHaveBeenCalled();
70
+ visibility = "visible";
71
+ fireEvent(document,new Event("visibilitychange"));
72
+ await act(async () => { vi.advanceTimersByTime(5000); });
73
+ expect(request).toHaveBeenCalledWith("poll");
74
+ expect(refresh).toHaveBeenCalledOnce();
75
+ visibility = "hidden";
76
+ await act(async () => { vi.advanceTimersByTime(10000); });
77
+ expect(request).toHaveBeenCalledTimes(1);
78
+ });
@@ -0,0 +1,78 @@
1
+ import { ActionIcon, Alert, Button, Group, Modal, Stack, Text } from "@mantine/core";
2
+ import { IconBrandGithub } from "@tabler/icons-react";
3
+ import { useEffect, useState } from "react";
4
+
5
+ export interface GitHubAuth {
6
+ configured: boolean;
7
+ connected: boolean;
8
+ login: string | null;
9
+ error?: string;
10
+ pending?: { userCode: string; verificationUrl: string; expiresAt: number };
11
+ }
12
+ export function GitHubConnection({ auth, request, openLink, refresh, openSettings, openSignal = 0, inline = false, hideTrigger = false }: {
13
+ openSignal?: number;
14
+ inline?: boolean;
15
+ hideTrigger?: boolean;
16
+ auth: GitHubAuth;
17
+ request: (action: "status" | "start" | "poll" | "disconnect") => Promise<GitHubAuth>;
18
+ openLink: (url: string) => Promise<unknown>;
19
+ refresh: () => Promise<unknown>;
20
+ openSettings: () => Promise<unknown>;
21
+ }) {
22
+ const [opened, setOpened] = useState(inline);
23
+ const [current, setCurrent] = useState(auth);
24
+ const [busy, setBusy] = useState(false);
25
+ const [error, setError] = useState<string | null>(null);
26
+ useEffect(() => { setCurrent(auth); }, [auth]);
27
+ useEffect(() => { if (openSignal > 0) { setError(null); setOpened(true); } }, [openSignal]);
28
+ useEffect(() => {
29
+ if (!opened || !current.pending) return;
30
+ let cancelled = false;
31
+ let polling = false;
32
+ const timer = window.setInterval(async () => {
33
+ if (polling || document.visibilityState === "hidden") return;
34
+ polling = true;
35
+ try {
36
+ const next = await request("poll");
37
+ if (cancelled) return;
38
+ setCurrent(next);
39
+ if (next.connected) await refresh();
40
+ } catch (cause) { if (!cancelled) { setError(String(cause)); setCurrent((value) => ({ ...value, pending: undefined })); } }
41
+ finally { polling = false; }
42
+ }, 5000);
43
+ return () => { cancelled = true; window.clearInterval(timer); };
44
+ // Snapshot polling supplies a new pending object; the code itself stays stable.
45
+ }, [opened, current.pending?.userCode, request, refresh]);
46
+ async function action(operation: () => Promise<unknown>) {
47
+ setBusy(true); setError(null);
48
+ try { await operation(); } catch (cause) { setError(String(cause)); }
49
+ finally { setBusy(false); }
50
+ }
51
+ const controls = <Stack gap="sm">
52
+ {(error || current.error) && <Alert color="red" role="alert">{error || current.error}</Alert>}
53
+ {!current.configured ? <>
54
+ <Text size="sm">GitHub connection is not ready. Open plugin settings and try again.</Text>
55
+ <Button onClick={() => void action(openSettings)}>Open plugin settings</Button>
56
+ </> : current.connected ? <>
57
+ <Text size="sm">Connected as <strong>{current.login}</strong>.</Text>
58
+ <Text size="sm">Install TaskChef in each organization you want to use, and select the repositories it can access. An organization owner may need to approve the installation.</Text>
59
+ <Button variant="default" onClick={() => void action(() => openLink("https://github.com/settings/installations"))}>Manage repository access</Button>
60
+ <Button disabled={busy} variant="default" onClick={() => void action(async () => { setCurrent(await request("disconnect")); await refresh(); })}>Disconnect this computer</Button>
61
+ <Text c="dimmed" size="xs">You can reconnect at any time.</Text>
62
+ </> : current.pending ? <>
63
+ <Text size="sm">Enter this code on GitHub:</Text>
64
+ <Text fw={700} size="xl" style={{ letterSpacing: "0.1em" }}>{current.pending.userCode}</Text>
65
+ <Button disabled={busy} onClick={() => void action(() => openLink(current.pending!.verificationUrl))}>Open GitHub</Button>
66
+ <Text c="dimmed" size="xs">Waiting for your approval. Keep this dialog open. The code expires in 15 minutes.</Text>
67
+ </> : <>
68
+ <Text size="sm">Let TaskChef read PR and CI status for your selected repositories and update the board automatically.</Text>
69
+ <Group><Button disabled={busy} onClick={() => void action(async () => setCurrent(await request("start")))}>Sign in with GitHub</Button>
70
+ <Button variant="default" onClick={() => void action(() => openLink("https://github.com/settings/installations"))}>Repository access</Button></Group>
71
+ </>}
72
+ </Stack>;
73
+ if (inline) return controls;
74
+ return <>
75
+ {!hideTrigger && <ActionIcon aria-label="GitHub connection" title={auth.connected ? `GitHub: ${auth.login}` : "Connect GitHub"} variant="subtle" onClick={() => { setError(null); setOpened(true); }}><IconBrandGithub size={17} /></ActionIcon>}
76
+ <Modal opened={opened} onClose={() => setOpened(false)} title="GitHub PR status" centered>{controls}</Modal>
77
+ </>;
78
+ }
@@ -0,0 +1,121 @@
1
+ import { act, cleanup, fireEvent, render, screen, within } from "@testing-library/react";
2
+ import { afterEach, expect, test, vi } from "vitest";
3
+ import { NextNotificationCenter, type NextNotification } from "./NextNotificationCenter";
4
+
5
+ afterEach(cleanup);
6
+ const notice = (id: string, extra: Partial<NextNotification> = {}): NextNotification => ({
7
+ id, taskId: "chat", title: "Fix login is ready for input or review", detail: "", kind: "ready", read: false,
8
+ timestamp: "2026-10-09T07:00:00Z", ...extra,
9
+ });
10
+
11
+ test("existing repeated chat messages keep the latest timestamp and read state", () => {
12
+ const onAction = vi.fn().mockResolvedValue(undefined);
13
+ const onOpen = vi.fn();
14
+ const latest = notice("latest", { timestamp: "2026-10-09T08:00:00Z", read: true });
15
+ render(<NextNotificationCenter state={{ revision: 1, items: [latest, notice("old"), notice("older")] }} toasts={[]} onAction={onAction} onOpen={onOpen} onDismiss={vi.fn()} />);
16
+ expect(screen.getByRole("button", { name: "Notifications" })).toBeVisible();
17
+ fireEvent.click(screen.getByRole("button", { name: "Notifications" }));
18
+ const center = screen.getByRole("region", { name: "Notification center" });
19
+ expect(within(center).getAllByRole("article")).toHaveLength(1);
20
+ expect(center.querySelector("time")).toHaveAttribute("datetime", latest.timestamp);
21
+ fireEvent.click(within(center).getByRole("button", { name: "Unread" }));
22
+ expect(within(center).getByText("No unread notifications")).toBeVisible();
23
+ fireEvent.click(within(center).getByRole("button", { name: "All" }));
24
+ fireEvent.click(within(center).getByRole("button", { name: /Fix login is ready/ }));
25
+ expect(onAction).toHaveBeenCalledWith("read", "latest");
26
+ expect(onOpen).toHaveBeenCalledWith(latest);
27
+ });
28
+
29
+ test("new repeats replace the visible entry while other chats and messages stay separate", () => {
30
+ const props = { toasts: [], onAction: vi.fn().mockResolvedValue(undefined), onOpen: vi.fn(), onDismiss: vi.fn() };
31
+ const original = notice("old");
32
+ const separate = [notice("other-chat", { taskId: "another" }), notice("interrupted", { kind: "interrupted", title: "Fix login was interrupted" }), notice("detail", { detail: "Different details" }), notice("global-one", { taskId: null }), notice("global-two", { taskId: null })];
33
+ const { rerender } = render(<NextNotificationCenter {...props} state={{ revision: 1, items: [original, ...separate] }} />);
34
+ fireEvent.click(screen.getByRole("button", { name: "Notifications, unread" }));
35
+ const latest = notice("latest", { timestamp: "2026-10-09T08:00:00Z" });
36
+ rerender(<NextNotificationCenter {...props} state={{ revision: 2, items: [latest, original, ...separate] }} />);
37
+ const center = screen.getByRole("region", { name: "Notification center" });
38
+ const entries = within(center).getAllByRole("article");
39
+ expect(entries).toHaveLength(6);
40
+ expect(entries[0].querySelector("time")).toHaveAttribute("datetime", latest.timestamp);
41
+ expect(within(center).getByRole("button", { name: "Unread (6)" })).toBeVisible();
42
+ });
43
+
44
+
45
+ test("opening and scrolling marks only notifications observed in the panel read", async () => {
46
+ const observers: Array<{ callback: IntersectionObserverCallback; observe: ReturnType<typeof vi.fn>; disconnect: ReturnType<typeof vi.fn> }> = [];
47
+ vi.stubGlobal("IntersectionObserver", class {
48
+ observe = vi.fn(); disconnect = vi.fn();
49
+ constructor(callback: IntersectionObserverCallback, options: IntersectionObserverInit) { expect(options.threshold).toBe(0.01); observers.push({ callback, observe: this.observe, disconnect: this.disconnect }); }
50
+ });
51
+ try {
52
+ const onAction = vi.fn().mockResolvedValue(undefined);
53
+ const props = { toasts: [], onAction, onOpen: vi.fn(), onDismiss: vi.fn() };
54
+ render(<NextNotificationCenter {...props} state={{ revision: 1, items: [notice("visible"), notice("offscreen", { taskId: "other" })] }} />);
55
+ expect(onAction).not.toHaveBeenCalled();
56
+ fireEvent.click(screen.getByRole("button", { name: "Notifications, unread" }));
57
+ const entries = screen.getAllByRole("article");
58
+ await act(async () => observers[0].callback([{ target: entries[0], isIntersecting: true }, { target: entries[1], isIntersecting: false }] as unknown as IntersectionObserverEntry[], {} as IntersectionObserver));
59
+ expect(onAction).toHaveBeenCalledExactlyOnceWith("read", "visible");
60
+ await act(async () => observers[0].callback([{ target: entries[0], isIntersecting: true }, { target: entries[1], isIntersecting: true }] as unknown as IntersectionObserverEntry[], {} as IntersectionObserver));
61
+ expect(onAction).toHaveBeenCalledTimes(2);
62
+ expect(onAction).toHaveBeenLastCalledWith("read", "offscreen");
63
+ fireEvent.click(screen.getByRole("button", { name: "Close notifications" }));
64
+ expect(observers[0].disconnect).toHaveBeenCalled();
65
+ } finally { vi.unstubAllGlobals(); }
66
+ });
67
+
68
+
69
+ test("failed auto-read requests retry when the panel opens again", async () => {
70
+ const observers: IntersectionObserverCallback[] = [];
71
+ vi.stubGlobal("IntersectionObserver", class {
72
+ observe = vi.fn(); disconnect = vi.fn();
73
+ constructor(callback: IntersectionObserverCallback) { observers.push(callback); }
74
+ });
75
+ try {
76
+ const onAction = vi.fn().mockRejectedValueOnce(new Error("Temporary failure")).mockResolvedValue(undefined);
77
+ render(<NextNotificationCenter state={{revision: 1, items: [notice("retry")]}} toasts={[]} onAction={onAction} onOpen={vi.fn()} onDismiss={vi.fn()} />);
78
+ fireEvent.click(screen.getByRole("button", {name: "Notifications, unread"}));
79
+ await act(async () => observers[0]([{target: screen.getByRole("article"), isIntersecting: true}] as unknown as IntersectionObserverEntry[], {} as IntersectionObserver));
80
+ fireEvent.click(screen.getByRole("button", {name: "Close notifications"}));
81
+ fireEvent.click(screen.getByRole("button", {name: "Notifications, unread"}));
82
+ await act(async () => observers[1]([{target: screen.getByRole("article"), isIntersecting: true}] as unknown as IntersectionObserverEntry[], {} as IntersectionObserver));
83
+ expect(onAction).toHaveBeenCalledTimes(2);
84
+ } finally {vi.unstubAllGlobals();}
85
+ });
86
+
87
+
88
+ test("hover opens the center, crossing to the panel keeps it open, and leaving closes it", () => {
89
+ vi.useFakeTimers();
90
+ try {
91
+ render(<NextNotificationCenter state={{revision: 1, items: []}} toasts={[]} onAction={vi.fn()} onOpen={vi.fn()} onDismiss={vi.fn()} />);
92
+ const bell = screen.getByRole("button", {name: "Notifications"});
93
+ fireEvent.pointerEnter(bell, {pointerType: "mouse"});
94
+ const panel = screen.getByRole("region", {name: "Notification center"});
95
+ fireEvent.pointerLeave(bell, {pointerType: "mouse"});
96
+ fireEvent.pointerEnter(panel, {pointerType: "mouse"});
97
+ act(() => vi.advanceTimersByTime(250));
98
+ expect(panel).toBeVisible();
99
+ fireEvent.click(bell);
100
+ expect(panel).toBeVisible();
101
+ fireEvent.pointerLeave(panel, {pointerType: "mouse"});
102
+ act(() => vi.advanceTimersByTime(250));
103
+ expect(screen.queryByRole("region", {name: "Notification center"})).not.toBeInTheDocument();
104
+ } finally {vi.useRealTimers();}
105
+ });
106
+
107
+
108
+ test("Escape cancels a pending hover close before keyboard reopening", () => {
109
+ vi.useFakeTimers();
110
+ try {
111
+ render(<NextNotificationCenter state={{revision: 1, items: []}} toasts={[]} onAction={vi.fn()} onOpen={vi.fn()} onDismiss={vi.fn()} />);
112
+ const bell = screen.getByRole("button", {name: "Notifications"});
113
+ fireEvent.pointerEnter(bell, {pointerType: "mouse"});
114
+ fireEvent.pointerLeave(screen.getByRole("region", {name: "Notification center"}), {pointerType: "mouse"});
115
+ fireEvent.keyDown(document, {key: "Escape"});
116
+ expect(screen.queryByRole("region", {name: "Notification center"})).not.toBeInTheDocument();
117
+ fireEvent.click(bell);
118
+ act(() => vi.advanceTimersByTime(250));
119
+ expect(screen.getByRole("region", {name: "Notification center"})).toBeVisible();
120
+ } finally {vi.useRealTimers();}
121
+ });
@@ -0,0 +1,94 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { IconBell, IconX } from "@tabler/icons-react";
3
+
4
+ export interface NextNotification { id: string; taskId: string | null; title: string; detail: string; kind: "ready" | "interrupted" | "confirmation" | "error"; read: boolean; timestamp: string; }
5
+ export interface NextNotificationState { revision: number; items: NextNotification[]; }
6
+
7
+ // Saved notifications arrive newest first. Keep the latest copy of each chat message.
8
+ function latestChatMessages(items: NextNotification[]) {
9
+ const seen = new Set<string>();
10
+ return items.filter((item) => {
11
+ if (!item.taskId) return true;
12
+ const key = JSON.stringify([item.taskId, item.kind, item.title, item.detail]);
13
+ if (seen.has(key)) return false;
14
+ seen.add(key);
15
+ return true;
16
+ });
17
+ }
18
+ export function NextNotificationCenter({ state, toasts, onAction, onOpen, onDismiss }: {
19
+ state: NextNotificationState; toasts: NextNotification[];
20
+ onAction: (action: "read" | "read_all" | "clear", id?: string) => Promise<void>;
21
+ onOpen: (item: NextNotification) => void; onDismiss: (id: string) => void;
22
+ }) {
23
+ const [opened, setOpened] = useState(false);
24
+ const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
25
+ const cancelClose = () => { if (closeTimer.current !== null) clearTimeout(closeTimer.current); closeTimer.current = null; };
26
+ const closeCenter = () => { cancelClose(); setOpened(false); };
27
+ const openOnHover = (event: React.PointerEvent) => { if (event.pointerType === "touch") return; cancelClose(); setOpened(true); };
28
+ const closeAfterHover = (event: React.PointerEvent) => { if (event.pointerType === "touch") return; cancelClose(); closeTimer.current = setTimeout(() => { setOpened(false); closeTimer.current = null; }, 200); };
29
+ useEffect(() => () => cancelClose(), []);
30
+ const [filter, setFilter] = useState<"all" | "unread">("all");
31
+ const [busy, setBusy] = useState(false);
32
+ const root = useRef<HTMLDivElement>(null);
33
+ const bell = useRef<HTMLButtonElement>(null);
34
+ const list = useRef<HTMLDivElement>(null);
35
+ const reading = useRef(new Set<string>());
36
+ const actionRef = useRef(onAction);
37
+ actionRef.current = onAction;
38
+ const latest = latestChatMessages(state.items);
39
+ const unread = latest.filter((item) => !item.read).length;
40
+ const items = latest.filter((item) => filter === "all" || !item.read);
41
+ useEffect(() => {
42
+ if (!opened) return;
43
+ const close = (event: PointerEvent) => { if (!root.current?.contains(event.target as Node)) closeCenter(); };
44
+ const escape = (event: KeyboardEvent) => { if (event.key === "Escape") { closeCenter(); bell.current?.focus(); } };
45
+ document.addEventListener("pointerdown", close);
46
+ document.addEventListener("keydown", escape);
47
+ return () => { document.removeEventListener("pointerdown", close); document.removeEventListener("keydown", escape); };
48
+ }, [opened]);
49
+ const unreadKey = JSON.stringify(items.filter(item => !item.read).map(item => item.id));
50
+ useEffect(() => {
51
+ if (!opened || !list.current || typeof IntersectionObserver === "undefined") return;
52
+ const observer = new IntersectionObserver(entries => {
53
+ for (const entry of entries) {
54
+ const id = (entry.target as HTMLElement).dataset.notificationId;
55
+ if (!entry.isIntersecting || !id || reading.current.has(id)) continue;
56
+ reading.current.add(id);
57
+ void actionRef.current("read", id).catch(() => reading.current.delete(id));
58
+ }
59
+ }, { root: list.current, threshold: 0.01 });
60
+ for (const item of list.current.querySelectorAll("[data-notification-id][data-unread=true]")) observer.observe(item);
61
+ return () => observer.disconnect();
62
+ }, [opened, unreadKey]);
63
+ async function mutate(action: "read" | "read_all" | "clear", id?: string) {
64
+ setBusy(true);
65
+ try { await onAction(action, id); } catch { /* The caller displays the error. */ } finally { setBusy(false); }
66
+ }
67
+ return <div className="next-notifications" ref={root}>
68
+ <button ref={bell} className="next-icon-button" aria-label={unread > 0 ? "Notifications, unread" : "Notifications"} aria-expanded={opened} aria-controls="next-notification-panel" onPointerEnter={openOnHover} onPointerLeave={closeAfterHover} onClick={() => { cancelClose(); setOpened(true); }} title={unread ? "Unread notifications" : "Notifications"}>
69
+ <IconBell size={18} />{unread > 0 && <span className="next-unread-dot" aria-hidden="true" />}
70
+ </button>
71
+ {opened && <section onPointerEnter={openOnHover} onPointerLeave={closeAfterHover} id="next-notification-panel" className="next-notification-panel" aria-label="Notification center">
72
+ <div className="next-notification-heading"><strong>Notifications</strong><button className="next-icon-button" aria-label="Close notifications" onClick={() => { closeCenter(); bell.current?.focus(); }}><IconX size={16} /></button></div>
73
+ <div className="next-notification-controls">
74
+ <div role="group" aria-label="Notification filter"><button aria-pressed={filter === "all"} onClick={() => setFilter("all")}>All</button><button aria-pressed={filter === "unread"} onClick={() => setFilter("unread")}>Unread{unread > 0 ? ` (${unread})` : ""}</button></div>
75
+ <button disabled={busy || unread === 0} onClick={() => void mutate("read_all")}>Mark all read</button>
76
+ <button disabled={busy || state.items.length === 0} onClick={() => void mutate("clear")}>Clear all</button>
77
+ </div>
78
+ <div className="next-notification-list" ref={list}>
79
+ {items.length === 0 && <p className="next-notification-empty">{filter === "unread" ? "No unread notifications" : "No notifications yet"}</p>}
80
+ {items.map((item) => <article key={item.id} data-notification-id={item.id} data-unread={!item.read} className={`next-notification-item${item.read ? "" : " next-notification-unread"}`}>
81
+ <button className="next-notification-message" disabled={busy} onClick={() => { void mutate("read", item.id); if (item.taskId) { closeCenter(); onOpen(item); } }}>
82
+ <strong>{item.title}</strong>{item.detail && <span>{item.detail}</span>}<time dateTime={item.timestamp}>{new Date(item.timestamp).toLocaleString([], { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" })}</time>
83
+ </button>
84
+ </article>)}
85
+ </div>
86
+ </section>}
87
+ <div className="next-toast-stack" aria-live="polite" aria-atomic="false">
88
+ {toasts.map((item) => <article key={item.id} className={`next-toast next-toast-${item.kind}`}>
89
+ <button className="next-toast-message" onClick={() => { if (item.taskId) onOpen(item); void mutate("read", item.id); onDismiss(item.id); }}><strong>{item.title}</strong>{item.detail && <span>{item.detail}</span>}</button>
90
+ <button className="next-icon-button" aria-label={`Dismiss ${item.title}`} onClick={() => onDismiss(item.id)}><IconX size={16} /></button>
91
+ </article>)}
92
+ </div>
93
+ </div>;
94
+ }
@@ -0,0 +1,124 @@
1
+ import { MantineProvider } from "@mantine/core";
2
+ import { cleanup, fireEvent, render, screen, within } from "@testing-library/react";
3
+ import { afterEach, expect, test, vi } from "vitest";
4
+ import { fixtureTask } from "../../dashboard/react/fixtures";
5
+ import { NextTaskDetail } from "./NextTaskDetail";
6
+
7
+ afterEach(cleanup);
8
+ test("details show ordered PRs, one access alert per repo, and useful collapsed fields", () => {
9
+ const task = { ...fixtureTask(), replyExcerpt: "The **update** is ready.", relatedGitHubLinks: [], inputSource: "ordinary" as const,
10
+ observed: { archive: false, source: "vscode", lastTurnEvent: "completed", lastTurnEventAt: null, recentFileActivity: false, latestTurnDurationMs: 480000, recordedChatDurationMs: 1200000, directChildCount: 155 },
11
+ detailPullRequests: [
12
+ { url: "https://github.com/example/repo/pull/3", title: "Latest change", state: "open" as const, checks: "passed" as const },
13
+ { url: "https://github.com/example/repo/pull/2", title: "Older change", state: "unknown" as const, checks: "unknown" as const, accessIssue: "denied" as const },
14
+ { url: "https://github.com/example/other/pull/1", title: "Other repo", state: "unknown" as const, checks: "unknown" as const, accessIssue: "denied" as const },
15
+ ] };
16
+ render(<MantineProvider><NextTaskDetail task={task} opened busy={false} error={null} onClose={() => {}} onOpenCodex={() => {}} /></MantineProvider>);
17
+ const prs = screen.getByRole("region", { name: "Pull requests" });
18
+ expect(within(prs).getAllByRole("link").map(link => link.textContent)).toEqual(["#3 Latest change", "Grant access", "#2 Older change", "#1 Other repo", "Grant access"]);
19
+ expect(screen.getByRole("region", { name: "Activity" }).compareDocumentPosition(prs) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
20
+ expect(screen.getByText("Latest turn worked for 8m")).toBeVisible();
21
+ expect(screen.getByText("Total chat worked for 20m")).toBeVisible();
22
+ expect(within(prs).queryByText("CI passed")).toBeNull();
23
+ expect(screen.getByRole("dialog", { name: task.title })).toBeVisible();
24
+ expect(screen.getAllByText(task.title)).toHaveLength(1);
25
+ expect(screen.getByText("155 subagents")).toBeVisible();
26
+ expect(screen.queryByText("Latest saved reply (excerpt)")).toBeNull();
27
+ const details = screen.getByText("Technical details").closest("details")!;
28
+ expect(details.open).toBe(false);
29
+ fireEvent.click(screen.getByText("Technical details"));
30
+ expect(within(details).getByText("Desktop")).toBeVisible();
31
+ expect(within(details).getByText("Human")).toBeVisible();
32
+ expect(screen.queryByRole("button", {name: "Chat actions"})).toBeNull();
33
+ expect(screen.queryByRole("button", {name: "Copy Chat ID"})).toBeNull();
34
+ });
35
+
36
+
37
+ test("a missing older PR keeps its warning after that PR rather than the accessible latest PR", () => {
38
+ const task = { ...fixtureTask(), relatedGitHubLinks: [], detailPullRequests: [
39
+ { url: "https://github.com/example/repo/pull/3", title: "Accessible latest", state: "open" as const, checks: "passed" as const },
40
+ { url: "https://github.com/example/repo/pull/2", title: "Missing older", state: "unknown" as const, checks: "unknown" as const, accessIssue: "not_found" as const },
41
+ ] };
42
+ render(<MantineProvider><NextTaskDetail task={task} opened busy={false} error={null} onClose={() => {}} onOpenCodex={() => {}} /></MantineProvider>);
43
+ expect(within(screen.getByRole("region", { name: "Pull requests" })).getAllByRole("link").map(link => link.textContent)).toEqual(["#3 Accessible latest", "#2 Missing older", "Check access"]);
44
+ });
45
+
46
+
47
+ test("running details use Worked for rather than the finished-turn label", () => {
48
+ const task = { ...fixtureTask(), observed: { archive: false, lastTurnEvent: "inProgress", lastTurnEventAt: new Date(Date.now()-120000).toISOString(), recentFileActivity: true, recordedChatDurationMs: 180000 } };
49
+ render(<MantineProvider><NextTaskDetail task={task} opened busy={false} error={null} onClose={()=>{}} onOpenCodex={()=>{}} /></MantineProvider>);
50
+ expect(screen.getByText("Worked for 2m")).toBeVisible();
51
+ expect(screen.getByText("Total chat worked for 5m")).toBeVisible();
52
+ expect(screen.queryByText(/Latest turn worked for/)).toBeNull();
53
+ });
54
+
55
+ test("usage keeps later-turn tokens visible when historical cost is partial", () => {
56
+ const latest = {tokens: {input_tokens: 100, cached_input_tokens: 20, cache_write_input_tokens: 0, output_tokens: 10, reasoning_output_tokens: 5, total_tokens: 110}, costUsd: 0.2, samples: 1, partial: false, costPartial: false};
57
+ const task = {...fixtureTask(), status: "needs_input" as const, sessionUsage: {latest, total: {...latest,tokens: {...latest.tokens,total_tokens: 10000},costUsd: 3.2, partial: true,costPartial: true}, subagents: 3, pricingDate: "2026-10-10"}};
58
+ render(<MantineProvider><NextTaskDetail task={task} opened busy={false} error={null} onClose={()=>{}} onOpenCodex={()=>{}} /></MantineProvider>);
59
+ const usage=screen.getByRole("region",{name:"Usage"});
60
+ expect(within(usage).getByText("Total chat")).toBeVisible();
61
+ expect(within(usage).getByRole("link", {name: "OpenAI API pricing"})).toHaveAttribute("href", "https://developers.openai.com/api/docs/pricing");
62
+ expect(within(usage).queryByText(/Includes 3 subagents/)).toBeNull();
63
+ expect(within(usage).getByText("110")).toBeVisible();
64
+ expect(within(usage).getByText("≥10.00K")).toBeVisible();
65
+ expect(within(usage).getByRole("columnheader", {name:"Tokens 2"})).toBeVisible();
66
+ expect(within(usage).getByRole("columnheader", {name:"API-equivalent cost 1,2"})).toBeVisible();
67
+ expect(within(usage).getByText("$0.20")).toBeVisible();
68
+ expect(within(usage).getByText("≥$3.20")).toBeVisible();
69
+ expect(within(usage).getByText(/Incomplete estimate:/)).toBeVisible();
70
+ });
71
+
72
+ test("detail header keeps Open chat and Done without the old action menu", () => {
73
+ const open=vi.fn();
74
+ render(<MantineProvider><NextTaskDetail task={fixtureTask()} opened busy={false} error={null} onClose={()=>{}} onOpenCodex={open} extraActions={<button>Mark Done</button>} /></MantineProvider>);
75
+ fireEvent.click(screen.getByRole("button", {name: /^Open chat for/}));
76
+ expect(open).toHaveBeenCalledTimes(1);
77
+ expect(screen.getByRole("button", {name: "Mark Done"})).toBeVisible();
78
+ expect(screen.queryByRole("button", {name: "Chat actions"})).toBeNull();
79
+ });
80
+
81
+ test("unpriced usage marks the cost label with the incomplete footnote", () => {
82
+ const value={tokens:{input_tokens:10,cached_input_tokens:0,cache_write_input_tokens:0,output_tokens:1,reasoning_output_tokens:0,total_tokens:11},samples:1,costUsd:0,costPartial:true,partial:false};
83
+ const task={...fixtureTask(),sessionUsage:{latest:value,total:value,subagents:0,pricingDate:"2026-10-10"}};
84
+ render(<MantineProvider><NextTaskDetail task={task} opened busy={false} error={null} onClose={()=>{}} onOpenCodex={()=>{}} /></MantineProvider>);
85
+ expect(within(screen.getByRole("region",{name:"Usage"})).getAllByText("—")).toHaveLength(2);
86
+ });
87
+
88
+
89
+ test("Usage stays visible while loading and when data is unavailable", () => {
90
+ const props={task:fixtureTask(),opened:true,busy:false,error:null,onClose:()=>{},onOpenCodex:()=>{}};
91
+ const view=render(<MantineProvider><NextTaskDetail {...props} usageLoading /></MantineProvider>);
92
+ const section=screen.getByRole("region",{name:"Usage"});
93
+ expect(within(section).getByRole("heading",{name:"Usage"})).toBeVisible();
94
+ expect(within(section).getByRole("status")).toHaveTextContent("Loading…");
95
+ view.rerender(<MantineProvider><NextTaskDetail {...props} usageLoading={false} /></MantineProvider>);
96
+ expect(screen.getByRole("region",{name:"Usage"})).toBe(section);
97
+ expect(within(section).getByRole("status")).toHaveTextContent("Usage unavailable.");
98
+ const value={tokens:{input_tokens:100,cached_input_tokens:0,cache_write_input_tokens:0,output_tokens:10,reasoning_output_tokens:0,total_tokens:110},samples:1,costUsd:0.1,costPartial:false,partial:false};
99
+ view.rerender(<MantineProvider><NextTaskDetail {...props} task={{...props.task,sessionUsage:{latest:value,total:value,subagents:0,pricingDate:"2026-10-10"}}} /></MantineProvider>);
100
+ expect(screen.getByRole("region",{name:"Usage"})).toBe(section);
101
+ expect(within(section).getByRole("table")).toBeVisible();
102
+ expect(within(section).queryByRole("status")).toBeNull();
103
+ });
104
+
105
+
106
+ test("a resolved usage read with zero samples is unavailable", () => {
107
+ const empty={tokens:{input_tokens:0,cached_input_tokens:0,cache_write_input_tokens:0,output_tokens:0,reasoning_output_tokens:0,total_tokens:0},samples:0,costUsd:0,costPartial:true,partial:true};
108
+ render(<MantineProvider><NextTaskDetail task={{...fixtureTask(),sessionUsage:{latest:empty,total:empty,subagents:0,pricingDate:"2026-10-10"}}} opened busy={false} error={null} onClose={()=>{}} onOpenCodex={()=>{}} /></MantineProvider>);
109
+ const usage=screen.getByRole("region",{name:"Usage"});expect(within(usage).getByRole("status")).toHaveTextContent("Usage unavailable.");expect(within(usage).queryByRole("table")).toBeNull();
110
+ });
111
+
112
+ test('Needs answer adds an open-chat action without replacing the saved prompt or response', () => {
113
+ const open=vi.fn();
114
+ const task={...fixtureTask(),needsAnswer:true,pendingQuestions:[{id:'question',title:'May I retry the scan?',options:['Retry once','Stop']}],promptText:'Run the scan.',replyText:'The connection failed. Approval requested.'};
115
+ render(<MantineProvider><NextTaskDetail task={task} opened busy={false} error={null} onClose={()=>{}} onOpenCodex={open}/></MantineProvider>);
116
+ expect(screen.getByText('Needs answer')).toBeInTheDocument();
117
+ const questions=screen.getByRole('region',{name:'Pending questions'});
118
+ expect(within(questions).getByText('May I retry the scan?')).toBeVisible();
119
+ expect(within(questions).getAllByRole('listitem').map(item=>item.textContent)).toEqual(['1Retry once','2Stop']);
120
+ expect(within(questions).getByRole('button',{name:'Open chat to answer'})).toBeVisible();
121
+ expect(screen.getByText('Run the scan.')).toBeInTheDocument();
122
+ expect(screen.getByText('The connection failed. Approval requested.')).toBeInTheDocument();
123
+ fireEvent.click(screen.getByRole('button',{name:'Open chat to answer'}));expect(open).toHaveBeenCalledTimes(1);
124
+ });