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,268 @@
1
+ import type { DcpConfig } from "./config.js";
2
+ import type { DcpState } from "./state.js";
3
+ import type { NudgeThresholds } from "./pruner-types.js";
4
+ import { createRequire } from "node:module";
5
+
6
+ // Roles that get message IDs injected
7
+ export const ID_ELIGIBLE_ROLES = new Set(["user", "assistant", "toolResult", "bashExecution"]);
8
+
9
+ // Roles that are PI-internal and should pass through unchanged
10
+ export const PASSTHROUGH_ROLES = new Set(["compaction", "branch_summary", "custom_message"]);
11
+ const DCP_ID_TAG_MATCH_RE = /<dcp-id(?:>|=)(m\d+)<\/dcp-id>/gi;
12
+ const DCP_BLOCK_ID_TAG_MATCH_RE = /<dcp-block-id(?:>|=)b(\d+)<\/dcp-block-id>/gi;
13
+ const DCP_ID_MARKDOWN_REF_MATCH_RE = /^\s*\[dcp-id\]:\s*#\s*\((m\d+)(?:\s+priority=(low|medium|high))?\)(?:\s+priority=(low|medium|high))?\s*$/gim;
14
+ const DCP_BLOCK_ID_MARKDOWN_REF_MATCH_RE = /^\s*\[dcp-block-id\]:\s*#\s*\(b(\d+)\)\s*$/gim;
15
+ const DCP_ID_METADATA_LINE_RE = /^\s*<dcp-id(?:>|=)m\d+(?:<\/dcp-id>|\s+dcp-id>)\s*$/i;
16
+ const DCP_BLOCK_ID_METADATA_LINE_RE = /^\s*<dcp-block-id(?:>|=)b\d+(?:<\/dcp-block-id>|\s+dcp-block-id>)\s*$/i;
17
+ const DCP_ID_MARKDOWN_REF_LINE_RE = /^\s*\[dcp-id\]:\s*#\s*\(m\d+(?:\s+priority=(?:low|medium|high))?\)(?:\s+priority=(?:low|medium|high))?\s*$/i;
18
+ const DCP_BLOCK_ID_MARKDOWN_REF_LINE_RE = /^\s*\[dcp-block-id\]:\s*#\s*\(b\d+\)\s*$/i;
19
+ const DCP_MARKDOWN_REF_FRAGMENT_LINE_RE = /^\s*\[dcp[^\n]*$/i;
20
+ const DCP_SYSTEM_REMINDER_START_LINE_RE = /^\s*<dcp-system-reminder>/i;
21
+ const DCP_SYSTEM_REMINDER_END_LINE_RE = /(?:<\/dcp-system-reminder>|(?<!<)dcp-system-reminder>)\s*$/i;
22
+ const MARKDOWN_FENCE_LINE_RE = /^\s*(```|~~~)/;
23
+
24
+ type TokenCounter = ((text: string) => number) | undefined;
25
+
26
+ let optionalTokenCounter: TokenCounter | null = null;
27
+
28
+ function loadOptionalTokenCounter(): TokenCounter {
29
+ if (optionalTokenCounter !== null) return optionalTokenCounter;
30
+
31
+ try {
32
+ const require = createRequire(import.meta.url);
33
+ const tokenizer = require("@anthropic-ai/tokenizer") as {
34
+ countTokens?: unknown;
35
+ default?: { countTokens?: unknown };
36
+ };
37
+ const counter = tokenizer.countTokens ?? tokenizer.default?.countTokens;
38
+ optionalTokenCounter = typeof counter === "function"
39
+ ? (counter as (text: string) => number)
40
+ : undefined;
41
+ } catch {
42
+ optionalTokenCounter = undefined;
43
+ }
44
+
45
+ return optionalTokenCounter;
46
+ }
47
+
48
+ export function estimateTokens(text: string): number {
49
+ if (!text) return 0;
50
+ const counter = loadOptionalTokenCounter();
51
+ if (counter) {
52
+ try {
53
+ const counted = counter(text);
54
+ if (Number.isFinite(counted)) return Math.max(0, Math.round(counted));
55
+ } catch {
56
+ // Fall through to the cheap heuristic if the optional tokenizer fails for
57
+ // a particular input or runtime.
58
+ }
59
+ }
60
+ return Math.round(text.length / 4);
61
+ }
62
+
63
+ /**
64
+ * Estimate tokens from a message's content, whatever shape it takes.
65
+ */
66
+ export function estimateMessageTokens(msg: any): number {
67
+ if (!msg) return 0;
68
+ const content = msg.content;
69
+ if (!content) return 0;
70
+ if (typeof content === "string") return estimateTokens(content);
71
+ if (Array.isArray(content)) {
72
+ let total = 0;
73
+ for (const part of content) {
74
+ if (part && typeof part === "object") {
75
+ if (typeof part.text === "string") total += estimateTokens(part.text);
76
+ else if (typeof part.thinking === "string") total += estimateTokens(part.thinking);
77
+ else if (part.type === "image") total += 500; // rough estimate for images
78
+ }
79
+ }
80
+ return total;
81
+ }
82
+ return 0;
83
+ }
84
+
85
+ export function messageText(msg: any): string {
86
+ if (!msg) return "";
87
+ if (typeof msg.content === "string") return msg.content;
88
+ if (!Array.isArray(msg.content)) return "";
89
+ return msg.content
90
+ .map((part: any) => {
91
+ if (!part || typeof part !== "object") return "";
92
+ if (typeof part.text === "string") return part.text;
93
+ if (typeof part.thinking === "string") return part.thinking;
94
+ return "";
95
+ })
96
+ .join("\n");
97
+ }
98
+
99
+ export function getActiveSummaryTokenEstimate(state: DcpState): number {
100
+ return state.compressionBlocks
101
+ .filter((block) => block.active)
102
+ .reduce((sum, block) => sum + Math.max(0, block.summaryTokenEstimate ?? 0), 0);
103
+ }
104
+
105
+ export function resolveContextThresholds(
106
+ config: DcpConfig,
107
+ modelKeys: Array<string | undefined> = [],
108
+ contextWindow?: number,
109
+ ): Required<NudgeThresholds> {
110
+ const candidates = modelKeys.filter((key): key is string => typeof key === "string" && key.length > 0);
111
+
112
+ const resolveThresholdValue = (value: number | string | undefined): number | undefined => {
113
+ if (typeof value === "string") {
114
+ const trimmed = value.trim();
115
+ if (trimmed.endsWith("%")) {
116
+ const percent = Number.parseFloat(trimmed.slice(0, -1));
117
+ return Number.isFinite(percent) ? percent / 100 : undefined;
118
+ }
119
+ const parsed = Number.parseFloat(trimmed);
120
+ if (!Number.isFinite(parsed)) return undefined;
121
+ value = parsed;
122
+ }
123
+
124
+ if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
125
+ if (value <= 1) return value;
126
+ if (contextWindow && Number.isFinite(contextWindow) && contextWindow > 0) {
127
+ return value / contextWindow;
128
+ }
129
+ return undefined;
130
+ };
131
+
132
+ const resolveOverride = (map: Record<string, number | string> | undefined): number | undefined => {
133
+ if (!map) return undefined;
134
+ for (const key of candidates) {
135
+ const value = resolveThresholdValue(map[key]);
136
+ if (value !== undefined) return value;
137
+ }
138
+ return undefined;
139
+ };
140
+
141
+ const min =
142
+ resolveOverride(config.compress.modelMinContextLimits) ??
143
+ resolveOverride(config.compress.modelMinContextPercent);
144
+ const max =
145
+ resolveOverride(config.compress.modelMaxContextLimits) ??
146
+ resolveOverride(config.compress.modelMaxContextPercent);
147
+
148
+ return {
149
+ minContextPercent: min ??
150
+ resolveThresholdValue(config.compress.minContextLimit) ??
151
+ resolveThresholdValue(config.compress.minContextPercent) ??
152
+ 0.4,
153
+ maxContextPercent: max ??
154
+ resolveThresholdValue(config.compress.maxContextLimit) ??
155
+ resolveThresholdValue(config.compress.maxContextPercent) ??
156
+ 0.8,
157
+ };
158
+ }
159
+
160
+ function lastRegexMatch(text: string, regex: RegExp): RegExpExecArray | undefined {
161
+ regex.lastIndex = 0;
162
+ let last: RegExpExecArray | undefined;
163
+ let match: RegExpExecArray | null;
164
+ while ((match = regex.exec(text)) !== null) {
165
+ last = match;
166
+ }
167
+ regex.lastIndex = 0;
168
+ return last;
169
+ }
170
+
171
+ export function extractBlockId(text: string): number | undefined {
172
+ const match =
173
+ lastRegexMatch(text, DCP_BLOCK_ID_MARKDOWN_REF_MATCH_RE) ??
174
+ lastRegexMatch(text, DCP_BLOCK_ID_TAG_MATCH_RE);
175
+ return match ? parseInt(match[1]!, 10) : undefined;
176
+ }
177
+
178
+ export function extractMessageId(text: string): string | undefined {
179
+ return (
180
+ lastRegexMatch(text, DCP_ID_MARKDOWN_REF_MATCH_RE) ??
181
+ lastRegexMatch(text, DCP_ID_TAG_MATCH_RE)
182
+ )?.[1];
183
+ }
184
+
185
+ function stripStaleDcpMetadataLines(text: string): string {
186
+ if (!text.includes("<dcp-") && !text.includes("[dcp-")) return text;
187
+
188
+ const lines = text.split("\n");
189
+ const kept: string[] = [];
190
+ let inSystemReminder = false;
191
+ let inMarkdownFence = false;
192
+
193
+ for (const line of lines) {
194
+ if (MARKDOWN_FENCE_LINE_RE.test(line)) {
195
+ kept.push(line);
196
+ inMarkdownFence = !inMarkdownFence;
197
+ continue;
198
+ }
199
+
200
+ if (inMarkdownFence) {
201
+ kept.push(line);
202
+ continue;
203
+ }
204
+
205
+ if (inSystemReminder) {
206
+ if (DCP_SYSTEM_REMINDER_END_LINE_RE.test(line)) inSystemReminder = false;
207
+ continue;
208
+ }
209
+
210
+ if (DCP_SYSTEM_REMINDER_START_LINE_RE.test(line)) {
211
+ if (!DCP_SYSTEM_REMINDER_END_LINE_RE.test(line)) inSystemReminder = true;
212
+ continue;
213
+ }
214
+
215
+ if (
216
+ DCP_ID_METADATA_LINE_RE.test(line) ||
217
+ DCP_BLOCK_ID_METADATA_LINE_RE.test(line) ||
218
+ DCP_ID_MARKDOWN_REF_LINE_RE.test(line) ||
219
+ DCP_BLOCK_ID_MARKDOWN_REF_LINE_RE.test(line) ||
220
+ DCP_MARKDOWN_REF_FRAGMENT_LINE_RE.test(line)
221
+ ) continue;
222
+ kept.push(line);
223
+ }
224
+
225
+ return kept.join("\n").trimEnd();
226
+ }
227
+
228
+ function stripStaleDcpMetadataFromAssistantBlock(block: any): any | undefined {
229
+ if (!block || typeof block !== "object") return block;
230
+ const next = { ...block };
231
+ let touched = false;
232
+
233
+ if (typeof next.text === "string") {
234
+ const stripped = stripStaleDcpMetadataLines(next.text);
235
+ if (stripped !== next.text) {
236
+ touched = true;
237
+ next.text = stripped;
238
+ delete next.textSignature;
239
+ }
240
+ }
241
+
242
+ if (typeof next.thinking === "string") {
243
+ const stripped = stripStaleDcpMetadataLines(next.thinking);
244
+ if (stripped !== next.thinking) {
245
+ touched = true;
246
+ next.thinking = stripped;
247
+ delete next.thinkingSignature;
248
+ }
249
+ }
250
+
251
+ if (touched && next.type === "text" && typeof next.text === "string" && next.text.trim() === "") return undefined;
252
+ if (touched && next.type === "thinking" && typeof next.thinking === "string" && next.thinking.trim() === "") return undefined;
253
+ return next;
254
+ }
255
+
256
+ export function stripStaleDcpMetadataFromAssistantMessage(message: any): any {
257
+ if (!message || typeof message !== "object" || message.role !== "assistant") return message;
258
+ if (typeof message.content === "string") {
259
+ return { ...message, content: stripStaleDcpMetadataLines(message.content) };
260
+ }
261
+ if (!Array.isArray(message.content)) return message;
262
+ return {
263
+ ...message,
264
+ content: message.content
265
+ .map(stripStaleDcpMetadataFromAssistantBlock)
266
+ .filter((block: any) => block !== undefined),
267
+ };
268
+ }
@@ -0,0 +1,315 @@
1
+ import type { DcpConfig } from "./config.js";
2
+ import type { DcpNudgeAnchor, DcpState } from "./state.js";
3
+ import type {
4
+ CompressionCandidate,
5
+ DcpNudgeType,
6
+ MessageCompressionCandidate,
7
+ NudgeThresholds,
8
+ } from "./pruner-types.js";
9
+ import { extractBlockId, messageText } from "./pruner-metadata.js";
10
+ import { stableMessageId } from "./pruner-message-ids.js";
11
+
12
+ function coercePercentThreshold(value: number | string | undefined, fallback: number): number {
13
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
14
+ if (typeof value !== "string") return fallback;
15
+
16
+ const trimmed = value.trim();
17
+ if (!trimmed) return fallback;
18
+ const parsed = Number.parseFloat(trimmed.endsWith("%") ? trimmed.slice(0, -1) : trimmed);
19
+ if (!Number.isFinite(parsed)) return fallback;
20
+ if (trimmed.endsWith("%")) return parsed / 100;
21
+ return parsed <= 1 ? parsed : fallback;
22
+ }
23
+
24
+ export function injectNudge(messages: any[], nudgeText: string): void {
25
+ messages.push({
26
+ role: "user",
27
+ content: nudgeText,
28
+ timestamp: Date.now(),
29
+ });
30
+ }
31
+
32
+ function typePriority(type: DcpNudgeType): number {
33
+ switch (type) {
34
+ case "context-strong": return 4;
35
+ case "context-soft": return 3;
36
+ case "iteration": return 2;
37
+ case "turn": return 1;
38
+ }
39
+ }
40
+
41
+ function nudgeTypeLabel(type: DcpNudgeType): string {
42
+ switch (type) {
43
+ case "context-strong": return "context limit (strong)";
44
+ case "context-soft": return "context limit";
45
+ case "iteration": return "iteration";
46
+ case "turn": return "turn";
47
+ }
48
+ }
49
+
50
+ function isRealAnchorCandidate(msg: any): boolean {
51
+ const role = msg?.role ?? "";
52
+ if (role !== "user" && role !== "assistant") return false;
53
+ const text = messageText(msg);
54
+ if (text.includes("<dcp-system-reminder>")) return false;
55
+ if (extractBlockId(text) !== undefined) return false;
56
+ return true;
57
+ }
58
+
59
+ function findAnchorMessage(messages: any[]): { msg: any; index: number; stableId: string; timestamp: number; role: string } | null {
60
+ // Prefer the latest real user message: it gives the reminder direct user-like
61
+ // salience without creating a new synthetic message at the end of context.
62
+ for (let i = messages.length - 1; i >= 0; i--) {
63
+ const msg = messages[i];
64
+ if (msg?.role !== "user" || !isRealAnchorCandidate(msg)) continue;
65
+ if (!Number.isFinite(msg.timestamp)) continue;
66
+ return {
67
+ msg,
68
+ index: i,
69
+ stableId: stableMessageId(msg, i),
70
+ timestamp: msg.timestamp,
71
+ role: msg.role,
72
+ };
73
+ }
74
+
75
+ // Fallback to assistant messages if no raw user message is present.
76
+ for (let i = messages.length - 1; i >= 0; i--) {
77
+ const msg = messages[i];
78
+ if (msg?.role !== "assistant" || !isRealAnchorCandidate(msg)) continue;
79
+ if (!Number.isFinite(msg.timestamp)) continue;
80
+ return {
81
+ msg,
82
+ index: i,
83
+ stableId: stableMessageId(msg, i),
84
+ timestamp: msg.timestamp,
85
+ role: msg.role,
86
+ };
87
+ }
88
+
89
+ return null;
90
+ }
91
+
92
+ function anchorMatchesMessage(anchor: DcpNudgeAnchor, msg: any, index: number): boolean {
93
+ if (anchor.anchorStableId && stableMessageId(msg, index) === anchor.anchorStableId) return true;
94
+ return msg?.timestamp === anchor.anchorTimestamp;
95
+ }
96
+
97
+ function appendTextToMessage(msg: any, text: string): void {
98
+ const suffix = `\n\n${text}`;
99
+ if (typeof msg.content === "string") {
100
+ msg.content += suffix;
101
+ return;
102
+ }
103
+
104
+ if (!Array.isArray(msg.content)) {
105
+ msg.content = [{ type: "text", text }];
106
+ return;
107
+ }
108
+
109
+ const textBlock = { type: "text", text: suffix };
110
+ if (msg.role !== "assistant") {
111
+ msg.content = [...msg.content, textBlock];
112
+ return;
113
+ }
114
+
115
+ // Keep provider ordering constraints: assistant text must appear before
116
+ // toolCall blocks for models that enforce text/thinking/tool_use ordering.
117
+ const firstToolCallIdx = msg.content.findIndex((block: any) => block?.type === "toolCall");
118
+ if (firstToolCallIdx === -1) {
119
+ msg.content = [...msg.content, textBlock];
120
+ } else {
121
+ msg.content = [
122
+ ...msg.content.slice(0, firstToolCallIdx),
123
+ textBlock,
124
+ ...msg.content.slice(firstToolCallIdx),
125
+ ];
126
+ }
127
+ }
128
+
129
+ function insertBeforeReminderClose(reminder: string, detail: string): string {
130
+ if (!detail.trim()) return reminder;
131
+ const close = "</dcp-system-reminder>";
132
+ const idx = reminder.toLowerCase().lastIndexOf(close.toLowerCase());
133
+ if (idx === -1) return `${reminder}\n\n${detail}`;
134
+ return `${reminder.slice(0, idx).trimEnd()}\n\n${detail}\n${reminder.slice(idx)}`;
135
+ }
136
+
137
+ function formatActiveBlocks(state: DcpState): string {
138
+ const blocks = state.compressionBlocks
139
+ .filter((block) => block.active)
140
+ .sort((a, b) => a.id - b.id)
141
+ .slice(0, 12)
142
+ .map((block) => {
143
+ const topic = block.topic.replace(/\s+/g, " ").trim();
144
+ const label = topic.length > 42 ? `${topic.slice(0, 41)}…` : topic;
145
+ return `b${block.id}${label ? ` "${label.replace(/"/g, "'")}"` : ""}`;
146
+ });
147
+ if (blocks.length === 0) return "";
148
+ return `Active compressed blocks: ${blocks.join(", ")}. If your selected range includes one, include the required \`(bN)\` placeholder exactly once.`;
149
+ }
150
+
151
+ function formatCandidateActions(
152
+ candidate: CompressionCandidate | null,
153
+ messageCandidates: MessageCompressionCandidate[],
154
+ state: DcpState,
155
+ ): string {
156
+ const parts: string[] = [];
157
+
158
+ if (candidate) {
159
+ parts.push(
160
+ `Recommended range candidate: ${candidate.startId}..${candidate.endId} (${candidate.messageCount} messages, ~${candidate.estimatedTokens} tokens, ${candidate.reason}). Compress this before the next search/read/test if it is closed.`,
161
+ );
162
+ if (candidate.includedBlockIds.length > 0) {
163
+ parts.push(
164
+ `This range includes existing block(s): ${candidate.includedBlockIds.map((id) => `b${id}`).join(", ")}. Preserve each required \`(bN)\` placeholder exactly once in the summary.`,
165
+ );
166
+ }
167
+ }
168
+
169
+ const highPriority = messageCandidates.filter((item) => item.priority === "high");
170
+ const listedMessages = (highPriority.length > 0 ? highPriority : messageCandidates).slice(0, 8);
171
+ if (listedMessages.length > 0) {
172
+ parts.push(
173
+ `Recommended message candidates: ${listedMessages
174
+ .map((item) => `${item.messageId} (${item.priority}, ${item.role}, ~${item.estimatedTokens} tokens)`)
175
+ .join(", ")}. High-priority stale messages MUST be compressed once their full text is no longer needed. Batch multiple messages in one compress call when possible.`,
176
+ );
177
+ }
178
+
179
+ const activeBlocks = formatActiveBlocks(state);
180
+ if (activeBlocks) parts.push(activeBlocks);
181
+
182
+ if (parts.length === 0) {
183
+ parts.push("No automatic candidate is certain; scan the older closed context now and compress any completed research, implementation, verification, CI-log inspection, or dead-end debugging slice before accumulating more tool output.");
184
+ }
185
+
186
+ return [`CONCRETE NEXT ACTION`, ...parts].join("\n");
187
+ }
188
+
189
+ export function appendConcreteNudgeGuidance(
190
+ reminder: string,
191
+ candidate: CompressionCandidate | null,
192
+ messageCandidates: MessageCompressionCandidate[],
193
+ state: DcpState,
194
+ ): string {
195
+ return insertBeforeReminderClose(
196
+ reminder,
197
+ formatCandidateActions(candidate, messageCandidates, state),
198
+ );
199
+ }
200
+
201
+ export function upsertNudgeAnchor(
202
+ messages: any[],
203
+ state: DcpState,
204
+ type: DcpNudgeType,
205
+ options: { contextPercent?: number } = {},
206
+ ): { anchor: DcpNudgeAnchor | null; created: boolean; updated: boolean } {
207
+ const target = findAnchorMessage(messages);
208
+ if (!target) return { anchor: null, created: false, updated: false };
209
+
210
+ const key = `${target.stableId}|${target.timestamp}`;
211
+ const existing = state.nudgeAnchors.find(
212
+ (anchor) => `${anchor.anchorStableId ?? ""}|${anchor.anchorTimestamp}` === key,
213
+ );
214
+
215
+ const now = Date.now();
216
+ if (existing) {
217
+ const shouldUpgrade = typePriority(type) > typePriority(existing.type);
218
+ if (shouldUpgrade) existing.type = type;
219
+ existing.updatedAt = now;
220
+ existing.contextPercent = options.contextPercent ?? existing.contextPercent;
221
+ state.lastNudge = {
222
+ type: existing.type,
223
+ anchorId: existing.id,
224
+ anchorTimestamp: existing.anchorTimestamp,
225
+ anchorStableId: existing.anchorStableId,
226
+ contextPercent: existing.contextPercent,
227
+ createdAt: now,
228
+ };
229
+ return { anchor: existing, created: false, updated: shouldUpgrade };
230
+ }
231
+
232
+ const anchor: DcpNudgeAnchor = {
233
+ id: state.nextNudgeAnchorId++,
234
+ type,
235
+ anchorTimestamp: target.timestamp,
236
+ anchorStableId: target.stableId,
237
+ anchorRole: target.role,
238
+ turnIndex: state.currentTurn,
239
+ contextPercent: options.contextPercent,
240
+ createdAt: now,
241
+ updatedAt: now,
242
+ };
243
+ state.nudgeAnchors.push(anchor);
244
+ state.lastNudge = {
245
+ type,
246
+ anchorId: anchor.id,
247
+ anchorTimestamp: anchor.anchorTimestamp,
248
+ anchorStableId: anchor.anchorStableId,
249
+ contextPercent: anchor.contextPercent,
250
+ createdAt: now,
251
+ };
252
+ return { anchor, created: true, updated: true };
253
+ }
254
+
255
+ export function applyAnchoredNudges(
256
+ messages: any[],
257
+ state: DcpState,
258
+ render: (anchor: DcpNudgeAnchor) => string,
259
+ ): void {
260
+ if (state.nudgeAnchors.length === 0) return;
261
+
262
+ const activeAnchors: DcpNudgeAnchor[] = [];
263
+ for (const anchor of state.nudgeAnchors) {
264
+ const index = messages.findIndex((msg, messageIndex) => anchorMatchesMessage(anchor, msg, messageIndex));
265
+ if (index === -1) continue;
266
+ activeAnchors.push(anchor);
267
+ appendTextToMessage(messages[index], render(anchor));
268
+ }
269
+
270
+ state.nudgeAnchors = activeAnchors;
271
+ }
272
+
273
+ export function clearDcpNudgeAnchors(state: DcpState): number {
274
+ const cleared = state.nudgeAnchors.length;
275
+ state.nudgeAnchors = [];
276
+ state.nudgeCounter = 0;
277
+ state.lastNudge = undefined;
278
+ return cleared;
279
+ }
280
+
281
+ export { nudgeTypeLabel };
282
+
283
+ export function getNudgeType(
284
+ contextPercent: number,
285
+ state: DcpState,
286
+ config: DcpConfig,
287
+ toolCallsSinceLastUser: number,
288
+ thresholds: NudgeThresholds = {},
289
+ ): DcpNudgeType | null {
290
+ const { nudgeFrequency, nudgeForce, iterationNudgeThreshold } =
291
+ config.compress;
292
+ const minContextPercent = coercePercentThreshold(
293
+ thresholds.minContextPercent ?? config.compress.minContextPercent,
294
+ 0.4,
295
+ );
296
+ const maxContextPercent = coercePercentThreshold(
297
+ thresholds.maxContextPercent ?? config.compress.maxContextPercent,
298
+ 0.8,
299
+ );
300
+ const cadence = Math.max(1, Math.floor(nudgeFrequency));
301
+
302
+ if (!Number.isFinite(contextPercent)) return null;
303
+ if (contextPercent <= minContextPercent) return null;
304
+ if (state.nudgeCounter + 1 < cadence) return null;
305
+
306
+ if (contextPercent > maxContextPercent) {
307
+ return nudgeForce === "strong" ? "context-strong" : "context-soft";
308
+ }
309
+
310
+ if (toolCallsSinceLastUser >= iterationNudgeThreshold) {
311
+ return "iteration";
312
+ }
313
+
314
+ return "turn";
315
+ }