@selesai/code 0.1.0

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 (884) hide show
  1. package/README.md +198 -0
  2. package/dist/agents/architect.md +216 -0
  3. package/dist/agents/builder.md +119 -0
  4. package/dist/agents/commentator.md +128 -0
  5. package/dist/agents/explorer.md +51 -0
  6. package/dist/agents/recapper.md +24 -0
  7. package/dist/bun/cli.d.ts +3 -0
  8. package/dist/bun/cli.d.ts.map +1 -0
  9. package/dist/bun/cli.js +9 -0
  10. package/dist/bun/cli.js.map +1 -0
  11. package/dist/bun/register-bedrock.d.ts +2 -0
  12. package/dist/bun/register-bedrock.d.ts.map +1 -0
  13. package/dist/bun/register-bedrock.js +4 -0
  14. package/dist/bun/register-bedrock.js.map +1 -0
  15. package/dist/bun/restore-sandbox-env.d.ts +17 -0
  16. package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
  17. package/dist/bun/restore-sandbox-env.js +36 -0
  18. package/dist/bun/restore-sandbox-env.js.map +1 -0
  19. package/dist/cli/args.d.ts +57 -0
  20. package/dist/cli/args.d.ts.map +1 -0
  21. package/dist/cli/args.js +379 -0
  22. package/dist/cli/args.js.map +1 -0
  23. package/dist/cli/config-selector.d.ts +14 -0
  24. package/dist/cli/config-selector.d.ts.map +1 -0
  25. package/dist/cli/config-selector.js +31 -0
  26. package/dist/cli/config-selector.js.map +1 -0
  27. package/dist/cli/file-processor.d.ts +15 -0
  28. package/dist/cli/file-processor.d.ts.map +1 -0
  29. package/dist/cli/file-processor.js +82 -0
  30. package/dist/cli/file-processor.js.map +1 -0
  31. package/dist/cli/initial-message.d.ts +18 -0
  32. package/dist/cli/initial-message.d.ts.map +1 -0
  33. package/dist/cli/initial-message.js +22 -0
  34. package/dist/cli/initial-message.js.map +1 -0
  35. package/dist/cli/list-models.d.ts +9 -0
  36. package/dist/cli/list-models.d.ts.map +1 -0
  37. package/dist/cli/list-models.js +98 -0
  38. package/dist/cli/list-models.js.map +1 -0
  39. package/dist/cli/project-trust.d.ts +10 -0
  40. package/dist/cli/project-trust.d.ts.map +1 -0
  41. package/dist/cli/project-trust.js +48 -0
  42. package/dist/cli/project-trust.js.map +1 -0
  43. package/dist/cli/session-picker.d.ts +10 -0
  44. package/dist/cli/session-picker.d.ts.map +1 -0
  45. package/dist/cli/session-picker.js +36 -0
  46. package/dist/cli/session-picker.js.map +1 -0
  47. package/dist/cli/startup-ui.d.ts +23 -0
  48. package/dist/cli/startup-ui.d.ts.map +1 -0
  49. package/dist/cli/startup-ui.js +172 -0
  50. package/dist/cli/startup-ui.js.map +1 -0
  51. package/dist/cli.d.ts +3 -0
  52. package/dist/cli.d.ts.map +1 -0
  53. package/dist/cli.js +18 -0
  54. package/dist/cli.js.map +1 -0
  55. package/dist/config.d.ts +154 -0
  56. package/dist/config.d.ts.map +1 -0
  57. package/dist/config.js +579 -0
  58. package/dist/config.js.map +1 -0
  59. package/dist/core/agent-session-runtime.d.ts +119 -0
  60. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  61. package/dist/core/agent-session-runtime.js +303 -0
  62. package/dist/core/agent-session-runtime.js.map +1 -0
  63. package/dist/core/agent-session-services.d.ts +88 -0
  64. package/dist/core/agent-session-services.d.ts.map +1 -0
  65. package/dist/core/agent-session-services.js +119 -0
  66. package/dist/core/agent-session-services.js.map +1 -0
  67. package/dist/core/agent-session.d.ts +607 -0
  68. package/dist/core/agent-session.d.ts.map +1 -0
  69. package/dist/core/agent-session.js +2552 -0
  70. package/dist/core/agent-session.js.map +1 -0
  71. package/dist/core/agents.d.ts +53 -0
  72. package/dist/core/agents.d.ts.map +1 -0
  73. package/dist/core/agents.js +238 -0
  74. package/dist/core/agents.js.map +1 -0
  75. package/dist/core/auth-guidance.d.ts +5 -0
  76. package/dist/core/auth-guidance.d.ts.map +1 -0
  77. package/dist/core/auth-guidance.js +21 -0
  78. package/dist/core/auth-guidance.js.map +1 -0
  79. package/dist/core/auth-storage.d.ts +140 -0
  80. package/dist/core/auth-storage.d.ts.map +1 -0
  81. package/dist/core/auth-storage.js +434 -0
  82. package/dist/core/auth-storage.js.map +1 -0
  83. package/dist/core/bash-executor.d.ts +32 -0
  84. package/dist/core/bash-executor.d.ts.map +1 -0
  85. package/dist/core/bash-executor.js +111 -0
  86. package/dist/core/bash-executor.js.map +1 -0
  87. package/dist/core/compaction/branch-summarization.d.ts +92 -0
  88. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  89. package/dist/core/compaction/branch-summarization.js +249 -0
  90. package/dist/core/compaction/branch-summarization.js.map +1 -0
  91. package/dist/core/compaction/compaction.d.ts +122 -0
  92. package/dist/core/compaction/compaction.d.ts.map +1 -0
  93. package/dist/core/compaction/compaction.js +625 -0
  94. package/dist/core/compaction/compaction.js.map +1 -0
  95. package/dist/core/compaction/index.d.ts +7 -0
  96. package/dist/core/compaction/index.d.ts.map +1 -0
  97. package/dist/core/compaction/index.js +7 -0
  98. package/dist/core/compaction/index.js.map +1 -0
  99. package/dist/core/compaction/utils.d.ts +38 -0
  100. package/dist/core/compaction/utils.d.ts.map +1 -0
  101. package/dist/core/compaction/utils.js +153 -0
  102. package/dist/core/compaction/utils.js.map +1 -0
  103. package/dist/core/defaults.d.ts +3 -0
  104. package/dist/core/defaults.d.ts.map +1 -0
  105. package/dist/core/defaults.js +2 -0
  106. package/dist/core/defaults.js.map +1 -0
  107. package/dist/core/diagnostics.d.ts +15 -0
  108. package/dist/core/diagnostics.d.ts.map +1 -0
  109. package/dist/core/diagnostics.js +2 -0
  110. package/dist/core/diagnostics.js.map +1 -0
  111. package/dist/core/event-bus.d.ts +9 -0
  112. package/dist/core/event-bus.d.ts.map +1 -0
  113. package/dist/core/event-bus.js +25 -0
  114. package/dist/core/event-bus.js.map +1 -0
  115. package/dist/core/exec.d.ts +29 -0
  116. package/dist/core/exec.d.ts.map +1 -0
  117. package/dist/core/exec.js +75 -0
  118. package/dist/core/exec.js.map +1 -0
  119. package/dist/core/experimental.d.ts +2 -0
  120. package/dist/core/experimental.d.ts.map +1 -0
  121. package/dist/core/experimental.js +4 -0
  122. package/dist/core/experimental.js.map +1 -0
  123. package/dist/core/export-html/ansi-to-html.d.ts +22 -0
  124. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  125. package/dist/core/export-html/ansi-to-html.js +249 -0
  126. package/dist/core/export-html/ansi-to-html.js.map +1 -0
  127. package/dist/core/export-html/index.d.ts +37 -0
  128. package/dist/core/export-html/index.d.ts.map +1 -0
  129. package/dist/core/export-html/index.js +226 -0
  130. package/dist/core/export-html/index.js.map +1 -0
  131. package/dist/core/export-html/template.css +1066 -0
  132. package/dist/core/export-html/template.html +55 -0
  133. package/dist/core/export-html/template.js +1864 -0
  134. package/dist/core/export-html/tool-renderer.d.ts +34 -0
  135. package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  136. package/dist/core/export-html/tool-renderer.js +108 -0
  137. package/dist/core/export-html/tool-renderer.js.map +1 -0
  138. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  139. package/dist/core/export-html/vendor/marked.min.js +78 -0
  140. package/dist/core/extensions/index.d.ts +12 -0
  141. package/dist/core/extensions/index.d.ts.map +1 -0
  142. package/dist/core/extensions/index.js +9 -0
  143. package/dist/core/extensions/index.js.map +1 -0
  144. package/dist/core/extensions/loader.d.ts +23 -0
  145. package/dist/core/extensions/loader.d.ts.map +1 -0
  146. package/dist/core/extensions/loader.js +531 -0
  147. package/dist/core/extensions/loader.js.map +1 -0
  148. package/dist/core/extensions/runner.d.ts +166 -0
  149. package/dist/core/extensions/runner.d.ts.map +1 -0
  150. package/dist/core/extensions/runner.js +876 -0
  151. package/dist/core/extensions/runner.js.map +1 -0
  152. package/dist/core/extensions/types.d.ts +1209 -0
  153. package/dist/core/extensions/types.d.ts.map +1 -0
  154. package/dist/core/extensions/types.js +45 -0
  155. package/dist/core/extensions/types.js.map +1 -0
  156. package/dist/core/extensions/wrapper.d.ts +20 -0
  157. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  158. package/dist/core/extensions/wrapper.js +22 -0
  159. package/dist/core/extensions/wrapper.js.map +1 -0
  160. package/dist/core/footer-data-provider.d.ts +54 -0
  161. package/dist/core/footer-data-provider.d.ts.map +1 -0
  162. package/dist/core/footer-data-provider.js +338 -0
  163. package/dist/core/footer-data-provider.js.map +1 -0
  164. package/dist/core/http-dispatcher.d.ts +22 -0
  165. package/dist/core/http-dispatcher.d.ts.map +1 -0
  166. package/dist/core/http-dispatcher.js +64 -0
  167. package/dist/core/http-dispatcher.js.map +1 -0
  168. package/dist/core/index.d.ts +13 -0
  169. package/dist/core/index.d.ts.map +1 -0
  170. package/dist/core/index.js +13 -0
  171. package/dist/core/index.js.map +1 -0
  172. package/dist/core/keybindings.d.ts +353 -0
  173. package/dist/core/keybindings.d.ts.map +1 -0
  174. package/dist/core/keybindings.js +295 -0
  175. package/dist/core/keybindings.js.map +1 -0
  176. package/dist/core/messages.d.ts +77 -0
  177. package/dist/core/messages.d.ts.map +1 -0
  178. package/dist/core/messages.js +123 -0
  179. package/dist/core/messages.js.map +1 -0
  180. package/dist/core/model-registry.d.ts +151 -0
  181. package/dist/core/model-registry.d.ts.map +1 -0
  182. package/dist/core/model-registry.js +750 -0
  183. package/dist/core/model-registry.js.map +1 -0
  184. package/dist/core/model-resolver.d.ts +111 -0
  185. package/dist/core/model-resolver.d.ts.map +1 -0
  186. package/dist/core/model-resolver.js +534 -0
  187. package/dist/core/model-resolver.js.map +1 -0
  188. package/dist/core/output-guard.d.ts +7 -0
  189. package/dist/core/output-guard.d.ts.map +1 -0
  190. package/dist/core/output-guard.js +89 -0
  191. package/dist/core/output-guard.js.map +1 -0
  192. package/dist/core/package-manager.d.ts +207 -0
  193. package/dist/core/package-manager.d.ts.map +1 -0
  194. package/dist/core/package-manager.js +2088 -0
  195. package/dist/core/package-manager.js.map +1 -0
  196. package/dist/core/project-trust.d.ts +15 -0
  197. package/dist/core/project-trust.d.ts.map +1 -0
  198. package/dist/core/project-trust.js +59 -0
  199. package/dist/core/project-trust.js.map +1 -0
  200. package/dist/core/prompt-templates.d.ts +53 -0
  201. package/dist/core/prompt-templates.d.ts.map +1 -0
  202. package/dist/core/prompt-templates.js +236 -0
  203. package/dist/core/prompt-templates.js.map +1 -0
  204. package/dist/core/provider-attribution.d.ts +4 -0
  205. package/dist/core/provider-attribution.d.ts.map +1 -0
  206. package/dist/core/provider-attribution.js +82 -0
  207. package/dist/core/provider-attribution.js.map +1 -0
  208. package/dist/core/provider-display-names.d.ts +2 -0
  209. package/dist/core/provider-display-names.d.ts.map +1 -0
  210. package/dist/core/provider-display-names.js +36 -0
  211. package/dist/core/provider-display-names.js.map +1 -0
  212. package/dist/core/resolve-config-value.d.ts +30 -0
  213. package/dist/core/resolve-config-value.d.ts.map +1 -0
  214. package/dist/core/resolve-config-value.js +247 -0
  215. package/dist/core/resolve-config-value.js.map +1 -0
  216. package/dist/core/resource-loader.d.ts +230 -0
  217. package/dist/core/resource-loader.d.ts.map +1 -0
  218. package/dist/core/resource-loader.js +861 -0
  219. package/dist/core/resource-loader.js.map +1 -0
  220. package/dist/core/sdk.d.ts +109 -0
  221. package/dist/core/sdk.d.ts.map +1 -0
  222. package/dist/core/sdk.js +267 -0
  223. package/dist/core/sdk.js.map +1 -0
  224. package/dist/core/session-cwd.d.ts +19 -0
  225. package/dist/core/session-cwd.d.ts.map +1 -0
  226. package/dist/core/session-cwd.js +38 -0
  227. package/dist/core/session-cwd.js.map +1 -0
  228. package/dist/core/session-manager.d.ts +332 -0
  229. package/dist/core/session-manager.d.ts.map +1 -0
  230. package/dist/core/session-manager.js +1230 -0
  231. package/dist/core/session-manager.js.map +1 -0
  232. package/dist/core/settings-manager.d.ts +286 -0
  233. package/dist/core/settings-manager.d.ts.map +1 -0
  234. package/dist/core/settings-manager.js +874 -0
  235. package/dist/core/settings-manager.js.map +1 -0
  236. package/dist/core/skills.d.ts +69 -0
  237. package/dist/core/skills.d.ts.map +1 -0
  238. package/dist/core/skills.js +387 -0
  239. package/dist/core/skills.js.map +1 -0
  240. package/dist/core/slash-commands.d.ts +14 -0
  241. package/dist/core/slash-commands.d.ts.map +1 -0
  242. package/dist/core/slash-commands.js +26 -0
  243. package/dist/core/slash-commands.js.map +1 -0
  244. package/dist/core/source-info.d.ts +18 -0
  245. package/dist/core/source-info.d.ts.map +1 -0
  246. package/dist/core/source-info.js +19 -0
  247. package/dist/core/source-info.js.map +1 -0
  248. package/dist/core/system-prompt.d.ts +31 -0
  249. package/dist/core/system-prompt.d.ts.map +1 -0
  250. package/dist/core/system-prompt.js +128 -0
  251. package/dist/core/system-prompt.js.map +1 -0
  252. package/dist/core/telemetry.d.ts +3 -0
  253. package/dist/core/telemetry.d.ts.map +1 -0
  254. package/dist/core/telemetry.js +9 -0
  255. package/dist/core/telemetry.js.map +1 -0
  256. package/dist/core/timings.d.ts +8 -0
  257. package/dist/core/timings.d.ts.map +1 -0
  258. package/dist/core/timings.js +31 -0
  259. package/dist/core/timings.js.map +1 -0
  260. package/dist/core/tools/bash.d.ts +68 -0
  261. package/dist/core/tools/bash.d.ts.map +1 -0
  262. package/dist/core/tools/bash.js +346 -0
  263. package/dist/core/tools/bash.js.map +1 -0
  264. package/dist/core/tools/edit-diff.d.ts +106 -0
  265. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  266. package/dist/core/tools/edit-diff.js +424 -0
  267. package/dist/core/tools/edit-diff.js.map +1 -0
  268. package/dist/core/tools/edit.d.ts +51 -0
  269. package/dist/core/tools/edit.d.ts.map +1 -0
  270. package/dist/core/tools/edit.js +284 -0
  271. package/dist/core/tools/edit.js.map +1 -0
  272. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  273. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  274. package/dist/core/tools/file-mutation-queue.js +52 -0
  275. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  276. package/dist/core/tools/find.d.ts +35 -0
  277. package/dist/core/tools/find.d.ts.map +1 -0
  278. package/dist/core/tools/find.js +305 -0
  279. package/dist/core/tools/find.js.map +1 -0
  280. package/dist/core/tools/grep.d.ts +37 -0
  281. package/dist/core/tools/grep.d.ts.map +1 -0
  282. package/dist/core/tools/grep.js +304 -0
  283. package/dist/core/tools/grep.js.map +1 -0
  284. package/dist/core/tools/index.d.ts +40 -0
  285. package/dist/core/tools/index.d.ts.map +1 -0
  286. package/dist/core/tools/index.js +112 -0
  287. package/dist/core/tools/index.js.map +1 -0
  288. package/dist/core/tools/ls.d.ts +37 -0
  289. package/dist/core/tools/ls.d.ts.map +1 -0
  290. package/dist/core/tools/ls.js +167 -0
  291. package/dist/core/tools/ls.js.map +1 -0
  292. package/dist/core/tools/output-accumulator.d.ts +52 -0
  293. package/dist/core/tools/output-accumulator.d.ts.map +1 -0
  294. package/dist/core/tools/output-accumulator.js +184 -0
  295. package/dist/core/tools/output-accumulator.js.map +1 -0
  296. package/dist/core/tools/path-utils.d.ts +10 -0
  297. package/dist/core/tools/path-utils.d.ts.map +1 -0
  298. package/dist/core/tools/path-utils.js +99 -0
  299. package/dist/core/tools/path-utils.js.map +1 -0
  300. package/dist/core/tools/read.d.ts +35 -0
  301. package/dist/core/tools/read.d.ts.map +1 -0
  302. package/dist/core/tools/read.js +289 -0
  303. package/dist/core/tools/read.js.map +1 -0
  304. package/dist/core/tools/render-utils.d.ts +24 -0
  305. package/dist/core/tools/render-utils.d.ts.map +1 -0
  306. package/dist/core/tools/render-utils.js +65 -0
  307. package/dist/core/tools/render-utils.js.map +1 -0
  308. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  309. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  310. package/dist/core/tools/tool-definition-wrapper.js +34 -0
  311. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  312. package/dist/core/tools/truncate.d.ts +70 -0
  313. package/dist/core/tools/truncate.d.ts.map +1 -0
  314. package/dist/core/tools/truncate.js +215 -0
  315. package/dist/core/tools/truncate.js.map +1 -0
  316. package/dist/core/tools/write.d.ts +26 -0
  317. package/dist/core/tools/write.d.ts.map +1 -0
  318. package/dist/core/tools/write.js +197 -0
  319. package/dist/core/tools/write.js.map +1 -0
  320. package/dist/core/trust-manager.d.ts +36 -0
  321. package/dist/core/trust-manager.d.ts.map +1 -0
  322. package/dist/core/trust-manager.js +202 -0
  323. package/dist/core/trust-manager.js.map +1 -0
  324. package/dist/defaults/models.json +3 -0
  325. package/dist/defaults/settings.json +68 -0
  326. package/dist/extensions/copy-turn.ts +125 -0
  327. package/dist/extensions/gitignore-guard.ts +132 -0
  328. package/dist/extensions/hooks/claude-codex-hooks.json +44 -0
  329. package/dist/extensions/hooks/copilot-hooks.json +21 -0
  330. package/dist/extensions/hooks/ponytail-activate.js +91 -0
  331. package/dist/extensions/hooks/ponytail-config.js +122 -0
  332. package/dist/extensions/hooks/ponytail-instructions.js +94 -0
  333. package/dist/extensions/hooks/ponytail-mode-tracker.js +55 -0
  334. package/dist/extensions/hooks/ponytail-runtime.js +68 -0
  335. package/dist/extensions/hooks/ponytail-statusline.ps1 +21 -0
  336. package/dist/extensions/hooks/ponytail-statusline.sh +12 -0
  337. package/dist/extensions/hooks/ponytail-subagent.js +22 -0
  338. package/dist/extensions/package.json +19 -0
  339. package/dist/extensions/pi-extension/index.js +189 -0
  340. package/dist/extensions/pi-extension/package.json +8 -0
  341. package/dist/extensions/pi-extension/test/extension.test.js +167 -0
  342. package/dist/extensions/pi-extension/test/helpers.test.js +92 -0
  343. package/dist/extensions/pi-powerline-footer/CHANGELOG.md +516 -0
  344. package/dist/extensions/pi-powerline-footer/README.md +382 -0
  345. package/dist/extensions/pi-powerline-footer/banner.png +0 -0
  346. package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +556 -0
  347. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +397 -0
  348. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +151 -0
  349. package/dist/extensions/pi-powerline-footer/bash-mode/shell-session.ts +286 -0
  350. package/dist/extensions/pi-powerline-footer/bash-mode/transcript.ts +108 -0
  351. package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +59 -0
  352. package/dist/extensions/pi-powerline-footer/colors.ts +69 -0
  353. package/dist/extensions/pi-powerline-footer/context-usage.ts +41 -0
  354. package/dist/extensions/pi-powerline-footer/fixed-editor/cluster.ts +113 -0
  355. package/dist/extensions/pi-powerline-footer/fixed-editor/terminal-split.ts +1077 -0
  356. package/dist/extensions/pi-powerline-footer/git-status.ts +212 -0
  357. package/dist/extensions/pi-powerline-footer/icons.ts +181 -0
  358. package/dist/extensions/pi-powerline-footer/index.ts +2817 -0
  359. package/dist/extensions/pi-powerline-footer/package.json +46 -0
  360. package/dist/extensions/pi-powerline-footer/powerline-config.ts +182 -0
  361. package/dist/extensions/pi-powerline-footer/presets.ts +121 -0
  362. package/dist/extensions/pi-powerline-footer/render-scheduler.ts +24 -0
  363. package/dist/extensions/pi-powerline-footer/segments.ts +566 -0
  364. package/dist/extensions/pi-powerline-footer/separators.ts +57 -0
  365. package/dist/extensions/pi-powerline-footer/shortcuts.ts +47 -0
  366. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +1503 -0
  367. package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +38 -0
  368. package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +135 -0
  369. package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +180 -0
  370. package/dist/extensions/pi-powerline-footer/tests/fixed-editor.test.ts +1416 -0
  371. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +213 -0
  372. package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +32 -0
  373. package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +61 -0
  374. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +226 -0
  375. package/dist/extensions/pi-powerline-footer/theme.example.json +24 -0
  376. package/dist/extensions/pi-powerline-footer/theme.json +12 -0
  377. package/dist/extensions/pi-powerline-footer/theme.ts +227 -0
  378. package/dist/extensions/pi-powerline-footer/types.ts +191 -0
  379. package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +34 -0
  380. package/dist/extensions/pi-powerline-footer/welcome.ts +611 -0
  381. package/dist/extensions/pi-powerline-footer/working-vibes.ts +695 -0
  382. package/dist/extensions/prototype.ts +713 -0
  383. package/dist/extensions/question.ts +350 -0
  384. package/dist/extensions/rtk.ts +81 -0
  385. package/dist/extensions/tps-tracker.ts +280 -0
  386. package/dist/extensions/undo.ts +292 -0
  387. package/dist/index.d.ts +33 -0
  388. package/dist/index.d.ts.map +1 -0
  389. package/dist/index.js +46 -0
  390. package/dist/index.js.map +1 -0
  391. package/dist/main.d.ts +12 -0
  392. package/dist/main.d.ts.map +1 -0
  393. package/dist/main.js +700 -0
  394. package/dist/main.js.map +1 -0
  395. package/dist/migrations.d.ts +33 -0
  396. package/dist/migrations.d.ts.map +1 -0
  397. package/dist/migrations.js +281 -0
  398. package/dist/migrations.js.map +1 -0
  399. package/dist/modes/index.d.ts +9 -0
  400. package/dist/modes/index.d.ts.map +1 -0
  401. package/dist/modes/index.js +8 -0
  402. package/dist/modes/index.js.map +1 -0
  403. package/dist/modes/interactive/assets/clankolas.png +0 -0
  404. package/dist/modes/interactive/components/armin.d.ts +34 -0
  405. package/dist/modes/interactive/components/armin.d.ts.map +1 -0
  406. package/dist/modes/interactive/components/armin.js +333 -0
  407. package/dist/modes/interactive/components/armin.js.map +1 -0
  408. package/dist/modes/interactive/components/assistant-message.d.ts +20 -0
  409. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
  410. package/dist/modes/interactive/components/assistant-message.js +121 -0
  411. package/dist/modes/interactive/components/assistant-message.js.map +1 -0
  412. package/dist/modes/interactive/components/bash-execution.d.ts +34 -0
  413. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
  414. package/dist/modes/interactive/components/bash-execution.js +175 -0
  415. package/dist/modes/interactive/components/bash-execution.js.map +1 -0
  416. package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  417. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  418. package/dist/modes/interactive/components/bordered-loader.js +54 -0
  419. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  420. package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  421. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  422. package/dist/modes/interactive/components/branch-summary-message.js +44 -0
  423. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  424. package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  425. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  426. package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
  427. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  428. package/dist/modes/interactive/components/config-selector.d.ts +71 -0
  429. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
  430. package/dist/modes/interactive/components/config-selector.js +506 -0
  431. package/dist/modes/interactive/components/config-selector.js.map +1 -0
  432. package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  433. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  434. package/dist/modes/interactive/components/countdown-timer.js +33 -0
  435. package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  436. package/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  437. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
  438. package/dist/modes/interactive/components/custom-editor.js +70 -0
  439. package/dist/modes/interactive/components/custom-editor.js.map +1 -0
  440. package/dist/modes/interactive/components/custom-message.d.ts +20 -0
  441. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  442. package/dist/modes/interactive/components/custom-message.js +79 -0
  443. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  444. package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  445. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
  446. package/dist/modes/interactive/components/daxnuts.js +140 -0
  447. package/dist/modes/interactive/components/daxnuts.js.map +1 -0
  448. package/dist/modes/interactive/components/diff.d.ts +12 -0
  449. package/dist/modes/interactive/components/diff.d.ts.map +1 -0
  450. package/dist/modes/interactive/components/diff.js +133 -0
  451. package/dist/modes/interactive/components/diff.js.map +1 -0
  452. package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  453. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
  454. package/dist/modes/interactive/components/dynamic-border.js +21 -0
  455. package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
  456. package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  457. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
  458. package/dist/modes/interactive/components/earendil-announcement.js +40 -0
  459. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
  460. package/dist/modes/interactive/components/extension-editor.d.ts +20 -0
  461. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  462. package/dist/modes/interactive/components/extension-editor.js +119 -0
  463. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  464. package/dist/modes/interactive/components/extension-input.d.ts +23 -0
  465. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  466. package/dist/modes/interactive/components/extension-input.js +61 -0
  467. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  468. package/dist/modes/interactive/components/extension-selector.d.ts +26 -0
  469. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  470. package/dist/modes/interactive/components/extension-selector.js +83 -0
  471. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  472. package/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  473. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
  474. package/dist/modes/interactive/components/first-time-setup.js +103 -0
  475. package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
  476. package/dist/modes/interactive/components/footer.d.ts +28 -0
  477. package/dist/modes/interactive/components/footer.d.ts.map +1 -0
  478. package/dist/modes/interactive/components/footer.js +221 -0
  479. package/dist/modes/interactive/components/footer.js.map +1 -0
  480. package/dist/modes/interactive/components/index.d.ts +34 -0
  481. package/dist/modes/interactive/components/index.d.ts.map +1 -0
  482. package/dist/modes/interactive/components/index.js +35 -0
  483. package/dist/modes/interactive/components/index.js.map +1 -0
  484. package/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
  485. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  486. package/dist/modes/interactive/components/keybinding-hints.js +36 -0
  487. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  488. package/dist/modes/interactive/components/login-dialog.d.ts +52 -0
  489. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
  490. package/dist/modes/interactive/components/login-dialog.js +179 -0
  491. package/dist/modes/interactive/components/login-dialog.js.map +1 -0
  492. package/dist/modes/interactive/components/model-selector.d.ts +47 -0
  493. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
  494. package/dist/modes/interactive/components/model-selector.js +279 -0
  495. package/dist/modes/interactive/components/model-selector.js.map +1 -0
  496. package/dist/modes/interactive/components/oauth-selector.d.ts +31 -0
  497. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
  498. package/dist/modes/interactive/components/oauth-selector.js +165 -0
  499. package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
  500. package/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  501. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
  502. package/dist/modes/interactive/components/scoped-models-selector.js +293 -0
  503. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
  504. package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  505. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  506. package/dist/modes/interactive/components/session-selector-search.js +155 -0
  507. package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  508. package/dist/modes/interactive/components/session-selector.d.ts +95 -0
  509. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
  510. package/dist/modes/interactive/components/session-selector.js +867 -0
  511. package/dist/modes/interactive/components/session-selector.js.map +1 -0
  512. package/dist/modes/interactive/components/settings-selector.d.ts +73 -0
  513. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  514. package/dist/modes/interactive/components/settings-selector.js +570 -0
  515. package/dist/modes/interactive/components/settings-selector.js.map +1 -0
  516. package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  517. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
  518. package/dist/modes/interactive/components/show-images-selector.js +39 -0
  519. package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
  520. package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  521. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
  522. package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
  523. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
  524. package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  525. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
  526. package/dist/modes/interactive/components/theme-selector.js +50 -0
  527. package/dist/modes/interactive/components/theme-selector.js.map +1 -0
  528. package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  529. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
  530. package/dist/modes/interactive/components/thinking-selector.js +51 -0
  531. package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
  532. package/dist/modes/interactive/components/tool-execution.d.ts +63 -0
  533. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
  534. package/dist/modes/interactive/components/tool-execution.js +317 -0
  535. package/dist/modes/interactive/components/tool-execution.js.map +1 -0
  536. package/dist/modes/interactive/components/tree-selector.d.ts +89 -0
  537. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  538. package/dist/modes/interactive/components/tree-selector.js +1208 -0
  539. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  540. package/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  541. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
  542. package/dist/modes/interactive/components/trust-selector.js +91 -0
  543. package/dist/modes/interactive/components/trust-selector.js.map +1 -0
  544. package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  545. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
  546. package/dist/modes/interactive/components/user-message-selector.js +114 -0
  547. package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
  548. package/dist/modes/interactive/components/user-message.d.ts +10 -0
  549. package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
  550. package/dist/modes/interactive/components/user-message.js +29 -0
  551. package/dist/modes/interactive/components/user-message.js.map +1 -0
  552. package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  553. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
  554. package/dist/modes/interactive/components/visual-truncate.js +33 -0
  555. package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
  556. package/dist/modes/interactive/interactive-mode.d.ts +381 -0
  557. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
  558. package/dist/modes/interactive/interactive-mode.js +4802 -0
  559. package/dist/modes/interactive/interactive-mode.js.map +1 -0
  560. package/dist/modes/interactive/model-search.d.ts +12 -0
  561. package/dist/modes/interactive/model-search.d.ts.map +1 -0
  562. package/dist/modes/interactive/model-search.js +15 -0
  563. package/dist/modes/interactive/model-search.js.map +1 -0
  564. package/dist/modes/interactive/theme/dark.json +86 -0
  565. package/dist/modes/interactive/theme/light.json +85 -0
  566. package/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
  567. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -0
  568. package/dist/modes/interactive/theme/theme-controller.js +102 -0
  569. package/dist/modes/interactive/theme/theme-controller.js.map +1 -0
  570. package/dist/modes/interactive/theme/theme-schema.json +336 -0
  571. package/dist/modes/interactive/theme/theme.d.ts +119 -0
  572. package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  573. package/dist/modes/interactive/theme/theme.js +1056 -0
  574. package/dist/modes/interactive/theme/theme.js.map +1 -0
  575. package/dist/modes/print-mode.d.ts +28 -0
  576. package/dist/modes/print-mode.d.ts.map +1 -0
  577. package/dist/modes/print-mode.js +132 -0
  578. package/dist/modes/print-mode.js.map +1 -0
  579. package/dist/modes/rpc/jsonl.d.ts +17 -0
  580. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  581. package/dist/modes/rpc/jsonl.js +49 -0
  582. package/dist/modes/rpc/jsonl.js.map +1 -0
  583. package/dist/modes/rpc/rpc-client.d.ts +227 -0
  584. package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
  585. package/dist/modes/rpc/rpc-client.js +467 -0
  586. package/dist/modes/rpc/rpc-client.js.map +1 -0
  587. package/dist/modes/rpc/rpc-mode.d.ts +20 -0
  588. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  589. package/dist/modes/rpc/rpc-mode.js +637 -0
  590. package/dist/modes/rpc/rpc-mode.js.map +1 -0
  591. package/dist/modes/rpc/rpc-types.d.ts +428 -0
  592. package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
  593. package/dist/modes/rpc/rpc-types.js +8 -0
  594. package/dist/modes/rpc/rpc-types.js.map +1 -0
  595. package/dist/package-manager-cli.d.ts +8 -0
  596. package/dist/package-manager-cli.d.ts.map +1 -0
  597. package/dist/package-manager-cli.js +659 -0
  598. package/dist/package-manager-cli.js.map +1 -0
  599. package/dist/skills/grill-me/SKILL.md +10 -0
  600. package/dist/skills/handoff/SKILL.md +15 -0
  601. package/dist/skills/implanger/SKILL.md +68 -0
  602. package/dist/skills/improve-codebase/REFERENCE.md +78 -0
  603. package/dist/skills/improve-codebase/SKILL.md +178 -0
  604. package/dist/skills/planger/SKILL.md +165 -0
  605. package/dist/skills/ponytail/SKILL.md +117 -0
  606. package/dist/skills/ponytail-audit/SKILL.md +41 -0
  607. package/dist/skills/ponytail-debt/SKILL.md +44 -0
  608. package/dist/skills/ponytail-gain/SKILL.md +50 -0
  609. package/dist/skills/ponytail-help/SKILL.md +69 -0
  610. package/dist/skills/ponytail-review/SKILL.md +57 -0
  611. package/dist/skills/selesai-default/SKILL.md +16 -0
  612. package/dist/themes/powerline-footer/theme.json +33 -0
  613. package/dist/utils/ansi.d.ts +2 -0
  614. package/dist/utils/ansi.d.ts.map +1 -0
  615. package/dist/utils/ansi.js +52 -0
  616. package/dist/utils/ansi.js.map +1 -0
  617. package/dist/utils/changelog.d.ts +22 -0
  618. package/dist/utils/changelog.d.ts.map +1 -0
  619. package/dist/utils/changelog.js +165 -0
  620. package/dist/utils/changelog.js.map +1 -0
  621. package/dist/utils/child-process.d.ts +18 -0
  622. package/dist/utils/child-process.d.ts.map +1 -0
  623. package/dist/utils/child-process.js +106 -0
  624. package/dist/utils/child-process.js.map +1 -0
  625. package/dist/utils/clipboard-image.d.ts +11 -0
  626. package/dist/utils/clipboard-image.d.ts.map +1 -0
  627. package/dist/utils/clipboard-image.js +245 -0
  628. package/dist/utils/clipboard-image.js.map +1 -0
  629. package/dist/utils/clipboard-native.d.ts +10 -0
  630. package/dist/utils/clipboard-native.d.ts.map +1 -0
  631. package/dist/utils/clipboard-native.js +20 -0
  632. package/dist/utils/clipboard-native.js.map +1 -0
  633. package/dist/utils/clipboard.d.ts +2 -0
  634. package/dist/utils/clipboard.d.ts.map +1 -0
  635. package/dist/utils/clipboard.js +117 -0
  636. package/dist/utils/clipboard.js.map +1 -0
  637. package/dist/utils/deprecation.d.ts +4 -0
  638. package/dist/utils/deprecation.d.ts.map +1 -0
  639. package/dist/utils/deprecation.js +13 -0
  640. package/dist/utils/deprecation.js.map +1 -0
  641. package/dist/utils/exif-orientation.d.ts +5 -0
  642. package/dist/utils/exif-orientation.d.ts.map +1 -0
  643. package/dist/utils/exif-orientation.js +158 -0
  644. package/dist/utils/exif-orientation.js.map +1 -0
  645. package/dist/utils/frontmatter.d.ts +8 -0
  646. package/dist/utils/frontmatter.d.ts.map +1 -0
  647. package/dist/utils/frontmatter.js +26 -0
  648. package/dist/utils/frontmatter.js.map +1 -0
  649. package/dist/utils/fs-watch.d.ts +5 -0
  650. package/dist/utils/fs-watch.d.ts.map +1 -0
  651. package/dist/utils/fs-watch.js +25 -0
  652. package/dist/utils/fs-watch.js.map +1 -0
  653. package/dist/utils/git.d.ts +26 -0
  654. package/dist/utils/git.d.ts.map +1 -0
  655. package/dist/utils/git.js +195 -0
  656. package/dist/utils/git.js.map +1 -0
  657. package/dist/utils/html.d.ts +7 -0
  658. package/dist/utils/html.d.ts.map +1 -0
  659. package/dist/utils/html.js +40 -0
  660. package/dist/utils/html.js.map +1 -0
  661. package/dist/utils/image-convert.d.ts +9 -0
  662. package/dist/utils/image-convert.d.ts.map +1 -0
  663. package/dist/utils/image-convert.js +39 -0
  664. package/dist/utils/image-convert.js.map +1 -0
  665. package/dist/utils/image-resize-core.d.ts +30 -0
  666. package/dist/utils/image-resize-core.d.ts.map +1 -0
  667. package/dist/utils/image-resize-core.js +124 -0
  668. package/dist/utils/image-resize-core.js.map +1 -0
  669. package/dist/utils/image-resize-worker.d.ts +2 -0
  670. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  671. package/dist/utils/image-resize-worker.js +31 -0
  672. package/dist/utils/image-resize-worker.js.map +1 -0
  673. package/dist/utils/image-resize.d.ts +16 -0
  674. package/dist/utils/image-resize.d.ts.map +1 -0
  675. package/dist/utils/image-resize.js +97 -0
  676. package/dist/utils/image-resize.js.map +1 -0
  677. package/dist/utils/json.d.ts +3 -0
  678. package/dist/utils/json.d.ts.map +1 -0
  679. package/dist/utils/json.js +7 -0
  680. package/dist/utils/json.js.map +1 -0
  681. package/dist/utils/mime.d.ts +3 -0
  682. package/dist/utils/mime.d.ts.map +1 -0
  683. package/dist/utils/mime.js +69 -0
  684. package/dist/utils/mime.js.map +1 -0
  685. package/dist/utils/open-browser.d.ts +9 -0
  686. package/dist/utils/open-browser.d.ts.map +1 -0
  687. package/dist/utils/open-browser.js +22 -0
  688. package/dist/utils/open-browser.js.map +1 -0
  689. package/dist/utils/paths.d.ts +31 -0
  690. package/dist/utils/paths.d.ts.map +1 -0
  691. package/dist/utils/paths.js +92 -0
  692. package/dist/utils/paths.js.map +1 -0
  693. package/dist/utils/photon.d.ts +21 -0
  694. package/dist/utils/photon.d.ts.map +1 -0
  695. package/dist/utils/photon.js +121 -0
  696. package/dist/utils/photon.js.map +1 -0
  697. package/dist/utils/pi-user-agent.d.ts +2 -0
  698. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  699. package/dist/utils/pi-user-agent.js +5 -0
  700. package/dist/utils/pi-user-agent.js.map +1 -0
  701. package/dist/utils/shell.d.ts +31 -0
  702. package/dist/utils/shell.d.ts.map +1 -0
  703. package/dist/utils/shell.js +202 -0
  704. package/dist/utils/shell.js.map +1 -0
  705. package/dist/utils/sleep.d.ts +5 -0
  706. package/dist/utils/sleep.d.ts.map +1 -0
  707. package/dist/utils/sleep.js +17 -0
  708. package/dist/utils/sleep.js.map +1 -0
  709. package/dist/utils/syntax-highlight.d.ts +12 -0
  710. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  711. package/dist/utils/syntax-highlight.js +118 -0
  712. package/dist/utils/syntax-highlight.js.map +1 -0
  713. package/dist/utils/tools-manager.d.ts +3 -0
  714. package/dist/utils/tools-manager.d.ts.map +1 -0
  715. package/dist/utils/tools-manager.js +328 -0
  716. package/dist/utils/tools-manager.js.map +1 -0
  717. package/dist/utils/version-check.d.ts +15 -0
  718. package/dist/utils/version-check.d.ts.map +1 -0
  719. package/dist/utils/version-check.js +52 -0
  720. package/dist/utils/version-check.js.map +1 -0
  721. package/dist/utils/windows-self-update.d.ts +3 -0
  722. package/dist/utils/windows-self-update.d.ts.map +1 -0
  723. package/dist/utils/windows-self-update.js +77 -0
  724. package/dist/utils/windows-self-update.js.map +1 -0
  725. package/docs/compaction.md +396 -0
  726. package/docs/containerization.md +111 -0
  727. package/docs/custom-provider.md +737 -0
  728. package/docs/development.md +71 -0
  729. package/docs/docs.json +156 -0
  730. package/docs/extensions.md +2681 -0
  731. package/docs/images/doom-extension.png +0 -0
  732. package/docs/images/exy.png +0 -0
  733. package/docs/images/interactive-mode.png +0 -0
  734. package/docs/images/tree-view.png +0 -0
  735. package/docs/index.md +82 -0
  736. package/docs/json.md +82 -0
  737. package/docs/keybindings.md +197 -0
  738. package/docs/models.md +495 -0
  739. package/docs/packages.md +227 -0
  740. package/docs/prompt-templates.md +95 -0
  741. package/docs/providers.md +274 -0
  742. package/docs/quickstart.md +165 -0
  743. package/docs/rpc.md +1412 -0
  744. package/docs/sdk.md +1143 -0
  745. package/docs/security.md +59 -0
  746. package/docs/session-format.md +412 -0
  747. package/docs/sessions.md +145 -0
  748. package/docs/settings.md +308 -0
  749. package/docs/shell-aliases.md +13 -0
  750. package/docs/skills.md +231 -0
  751. package/docs/terminal-setup.md +142 -0
  752. package/docs/termux.md +127 -0
  753. package/docs/themes.md +295 -0
  754. package/docs/tmux.md +63 -0
  755. package/docs/tui.md +927 -0
  756. package/docs/usage.md +308 -0
  757. package/docs/windows.md +17 -0
  758. package/examples/README.md +25 -0
  759. package/examples/extensions/README.md +211 -0
  760. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  761. package/examples/extensions/bash-spawn-hook.ts +30 -0
  762. package/examples/extensions/bookmark.ts +50 -0
  763. package/examples/extensions/border-status-editor.ts +150 -0
  764. package/examples/extensions/built-in-tool-renderer.ts +249 -0
  765. package/examples/extensions/claude-rules.ts +86 -0
  766. package/examples/extensions/commands.ts +72 -0
  767. package/examples/extensions/confirm-destructive.ts +59 -0
  768. package/examples/extensions/custom-compaction.ts +127 -0
  769. package/examples/extensions/custom-footer.ts +64 -0
  770. package/examples/extensions/custom-header.ts +73 -0
  771. package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  772. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  773. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  774. package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -0
  775. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  776. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  777. package/examples/extensions/dirty-repo-guard.ts +56 -0
  778. package/examples/extensions/doom-overlay/README.md +46 -0
  779. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  780. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  781. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  782. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  783. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  784. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  785. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  786. package/examples/extensions/doom-overlay/index.ts +74 -0
  787. package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
  788. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  789. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  790. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  791. package/examples/extensions/dynamic-resources/index.ts +15 -0
  792. package/examples/extensions/dynamic-tools.ts +74 -0
  793. package/examples/extensions/event-bus.ts +43 -0
  794. package/examples/extensions/file-trigger.ts +41 -0
  795. package/examples/extensions/git-checkpoint.ts +53 -0
  796. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  797. package/examples/extensions/github-issue-autocomplete.ts +185 -0
  798. package/examples/extensions/gondolin/index.ts +531 -0
  799. package/examples/extensions/gondolin/package-lock.json +185 -0
  800. package/examples/extensions/gondolin/package.json +19 -0
  801. package/examples/extensions/handoff.ts +191 -0
  802. package/examples/extensions/hello.ts +26 -0
  803. package/examples/extensions/hidden-thinking-label.ts +53 -0
  804. package/examples/extensions/inline-bash.ts +94 -0
  805. package/examples/extensions/input-transform-streaming.ts +39 -0
  806. package/examples/extensions/input-transform.ts +43 -0
  807. package/examples/extensions/interactive-shell.ts +196 -0
  808. package/examples/extensions/mac-system-theme.ts +47 -0
  809. package/examples/extensions/message-renderer.ts +59 -0
  810. package/examples/extensions/minimal-mode.ts +426 -0
  811. package/examples/extensions/modal-editor.ts +85 -0
  812. package/examples/extensions/model-status.ts +31 -0
  813. package/examples/extensions/notify.ts +55 -0
  814. package/examples/extensions/overlay-qa-tests.ts +1450 -0
  815. package/examples/extensions/overlay-test.ts +153 -0
  816. package/examples/extensions/permission-gate.ts +34 -0
  817. package/examples/extensions/pirate.ts +47 -0
  818. package/examples/extensions/plan-mode/README.md +66 -0
  819. package/examples/extensions/plan-mode/index.ts +390 -0
  820. package/examples/extensions/plan-mode/utils.ts +168 -0
  821. package/examples/extensions/preset.ts +436 -0
  822. package/examples/extensions/project-trust.ts +64 -0
  823. package/examples/extensions/prompt-customizer.ts +97 -0
  824. package/examples/extensions/protected-paths.ts +30 -0
  825. package/examples/extensions/provider-payload.ts +18 -0
  826. package/examples/extensions/qna.ts +122 -0
  827. package/examples/extensions/question.ts +285 -0
  828. package/examples/extensions/questionnaire.ts +448 -0
  829. package/examples/extensions/rainbow-editor.ts +88 -0
  830. package/examples/extensions/reload-runtime.ts +37 -0
  831. package/examples/extensions/rpc-demo.ts +118 -0
  832. package/examples/extensions/sandbox/index.ts +321 -0
  833. package/examples/extensions/sandbox/package-lock.json +92 -0
  834. package/examples/extensions/sandbox/package.json +19 -0
  835. package/examples/extensions/send-user-message.ts +97 -0
  836. package/examples/extensions/session-name.ts +27 -0
  837. package/examples/extensions/shutdown-command.ts +63 -0
  838. package/examples/extensions/snake.ts +343 -0
  839. package/examples/extensions/space-invaders.ts +560 -0
  840. package/examples/extensions/ssh.ts +220 -0
  841. package/examples/extensions/status-line.ts +32 -0
  842. package/examples/extensions/structured-output.ts +65 -0
  843. package/examples/extensions/subagent/README.md +175 -0
  844. package/examples/extensions/subagent/agents/planner.md +37 -0
  845. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  846. package/examples/extensions/subagent/agents/scout.md +50 -0
  847. package/examples/extensions/subagent/agents/worker.md +24 -0
  848. package/examples/extensions/subagent/agents.ts +126 -0
  849. package/examples/extensions/subagent/index.ts +1015 -0
  850. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  851. package/examples/extensions/subagent/prompts/implement.md +10 -0
  852. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  853. package/examples/extensions/summarize.ts +206 -0
  854. package/examples/extensions/system-prompt-header.ts +17 -0
  855. package/examples/extensions/tic-tac-toe.ts +1008 -0
  856. package/examples/extensions/timed-confirm.ts +70 -0
  857. package/examples/extensions/titlebar-spinner.ts +58 -0
  858. package/examples/extensions/todo.ts +297 -0
  859. package/examples/extensions/tool-override.ts +144 -0
  860. package/examples/extensions/tools.ts +146 -0
  861. package/examples/extensions/trigger-compact.ts +50 -0
  862. package/examples/extensions/truncated-tool.ts +195 -0
  863. package/examples/extensions/widget-placement.ts +9 -0
  864. package/examples/extensions/with-deps/index.ts +32 -0
  865. package/examples/extensions/with-deps/package-lock.json +31 -0
  866. package/examples/extensions/with-deps/package.json +22 -0
  867. package/examples/extensions/working-indicator.ts +123 -0
  868. package/examples/extensions/working-message-test.ts +25 -0
  869. package/examples/rpc-extension-ui.ts +632 -0
  870. package/examples/sdk/01-minimal.ts +26 -0
  871. package/examples/sdk/02-custom-model.ts +53 -0
  872. package/examples/sdk/03-custom-prompt.ts +75 -0
  873. package/examples/sdk/04-skills.ts +55 -0
  874. package/examples/sdk/05-tools.ts +48 -0
  875. package/examples/sdk/06-extensions.ts +99 -0
  876. package/examples/sdk/07-context-files.ts +47 -0
  877. package/examples/sdk/08-prompt-templates.ts +51 -0
  878. package/examples/sdk/09-api-keys-and-oauth.ts +52 -0
  879. package/examples/sdk/10-settings.ts +53 -0
  880. package/examples/sdk/11-sessions.ts +52 -0
  881. package/examples/sdk/12-full-control.ts +77 -0
  882. package/examples/sdk/13-session-runtime.ts +67 -0
  883. package/examples/sdk/README.md +144 -0
  884. package/package.json +65 -0
@@ -0,0 +1,1503 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { appendProjectHistory, matchHistoryEntries, readGlobalShellHistory } from "../bash-mode/history.ts";
7
+ import { BashTranscriptStore } from "../bash-mode/transcript.ts";
8
+ import {
9
+ BashAutocompleteProvider,
10
+ BashCompletionEngine,
11
+ getOneOffBashCommandContext,
12
+ ModeAwareAutocompleteProvider,
13
+ OneOffBashAutocompleteProvider,
14
+ } from "../bash-mode/completion.ts";
15
+ import { getIcons } from "../icons.ts";
16
+ import { ManagedShellSession } from "../bash-mode/shell-session.ts";
17
+
18
+ function getMethod(target: object, name: string): Function {
19
+ const method = Reflect.get(target, name);
20
+ if (typeof method !== "function") {
21
+ throw new Error(`Expected ${name} to be a function`);
22
+ }
23
+ return method;
24
+ }
25
+
26
+ function ensureEditorModuleLinks(): { cleanup: () => void } {
27
+ const nodeModulesDir = join(process.cwd(), "node_modules", "@earendil-works");
28
+ mkdirSync(nodeModulesDir, { recursive: true });
29
+ const links = [
30
+ {
31
+ link: join(nodeModulesDir, "pi-coding-agent"),
32
+ target: "/opt/homebrew/lib/node_modules/@earendil-works/pi-coding-agent",
33
+ },
34
+ {
35
+ link: join(nodeModulesDir, "pi-tui"),
36
+ target: "/opt/homebrew/lib/node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui",
37
+ },
38
+ ];
39
+
40
+ const createdLinks: string[] = [];
41
+ for (const { link, target } of links) {
42
+ if (!existsSync(link)) {
43
+ symlinkSync(target, link);
44
+ createdLinks.push(link);
45
+ }
46
+ }
47
+
48
+ return {
49
+ cleanup() {
50
+ for (const link of createdLinks.reverse()) {
51
+ if (existsSync(link)) {
52
+ rmSync(link, { recursive: true, force: true });
53
+ }
54
+ }
55
+ },
56
+ };
57
+ }
58
+
59
+ test("project history is stored newest-first and global zsh history parses histfile format", () => {
60
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-history-"));
61
+ const histfile = join(cwd, ".zsh_history");
62
+ process.env.HISTFILE = histfile;
63
+
64
+ appendProjectHistory(cwd, "git status", cwd);
65
+ appendProjectHistory(cwd, "git stash", cwd);
66
+ appendProjectHistory(cwd, "git status", cwd);
67
+
68
+ writeFileSync(histfile, [
69
+ ": 1711111111:0;git fetch",
70
+ ": 1711111112:0;git pull",
71
+ "plain-command",
72
+ "",
73
+ ].join("\n"));
74
+
75
+ const global = readGlobalShellHistory("/bin/zsh");
76
+ assert.deepEqual(global, ["plain-command", "git pull", "git fetch"]);
77
+ });
78
+
79
+ test("matchHistoryEntries returns newest entries when the prefix is empty", () => {
80
+ const matches = matchHistoryEntries([
81
+ "git stash",
82
+ "git status",
83
+ "git stash",
84
+ "git fetch",
85
+ ], "", 10);
86
+
87
+ assert.deepEqual(matches, ["git stash", "git status", "git fetch"]);
88
+ });
89
+
90
+ test("theme.json can override icons without touching colors", () => {
91
+ const themePath = join(process.cwd(), "theme.json");
92
+ const originalTheme = existsSync(themePath) ? readFileSync(themePath, "utf8") : null;
93
+ const originalNerdFonts = process.env.POWERLINE_NERD_FONTS;
94
+
95
+ try {
96
+ writeFileSync(themePath, JSON.stringify({ icons: { auto: "↯", warning: "" } }, null, 2) + "\n");
97
+ process.env.POWERLINE_NERD_FONTS = "0";
98
+
99
+ const icons = getIcons();
100
+ assert.equal(icons.auto, "↯");
101
+ assert.equal(icons.warning, "");
102
+ assert.equal(icons.folder, "dir");
103
+ } finally {
104
+ if (originalTheme === null) {
105
+ if (existsSync(themePath)) unlinkSync(themePath);
106
+ } else {
107
+ writeFileSync(themePath, originalTheme);
108
+ }
109
+
110
+ if (originalNerdFonts === undefined) {
111
+ delete process.env.POWERLINE_NERD_FONTS;
112
+ } else {
113
+ process.env.POWERLINE_NERD_FONTS = originalNerdFonts;
114
+ }
115
+ }
116
+ });
117
+
118
+ test("one-off bash command context strips ! and !! prefixes", () => {
119
+ assert.deepEqual(getOneOffBashCommandContext("!git status"), {
120
+ prefix: "!",
121
+ command: "git status",
122
+ offset: 1,
123
+ });
124
+
125
+ assert.deepEqual(getOneOffBashCommandContext("!!git status"), {
126
+ prefix: "!!",
127
+ command: "git status",
128
+ offset: 2,
129
+ });
130
+
131
+ assert.equal(getOneOffBashCommandContext(" !!git status"), null);
132
+ assert.equal(getOneOffBashCommandContext("git status"), null);
133
+ });
134
+
135
+ test("transcript store truncates oldest commands at command boundaries", () => {
136
+ const store = new BashTranscriptStore({ transcriptMaxLines: 3, transcriptMaxBytes: 1024 });
137
+ store.startCommand("a", "echo one", "/tmp");
138
+ store.appendOutput("a", "line-1\nline-2");
139
+ store.finishCommand("a", 0);
140
+
141
+ store.startCommand("b", "echo two", "/tmp");
142
+ store.appendOutput("b", "line-3\nline-4");
143
+ store.finishCommand("b", 0);
144
+
145
+ const snapshot = store.getSnapshot();
146
+ assert.equal(snapshot.commands.length, 1);
147
+ assert.equal(snapshot.commands[0]?.id, "b");
148
+ assert.equal(snapshot.truncatedCommands, 1);
149
+ });
150
+
151
+ test("transcript store keeps the active command even when it alone exceeds limits", () => {
152
+ const store = new BashTranscriptStore({ transcriptMaxLines: 3, transcriptMaxBytes: 1024 });
153
+ store.startCommand("a", "echo big", "/tmp");
154
+ store.appendOutput("a", "1\n2\n3\n4");
155
+
156
+ const snapshot = store.getSnapshot();
157
+ assert.equal(snapshot.commands.length, 1);
158
+ assert.equal(snapshot.commands[0]?.id, "a");
159
+ assert.deepEqual(snapshot.commands[0]?.output, ["1", "2", "3", "4"]);
160
+ });
161
+
162
+ test("ghost suggestion prefers project history over global history", async () => {
163
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-ghost-"));
164
+ const histfile = join(cwd, ".zsh_history");
165
+ process.env.HISTFILE = histfile;
166
+ writeFileSync(histfile, ": 1711111111:0;git switch\n");
167
+ appendProjectHistory(cwd, "git status", cwd);
168
+ appendProjectHistory(cwd, "git stash", cwd);
169
+
170
+ const engine = new BashCompletionEngine();
171
+ const suggestion = await engine.getGhostSuggestion(
172
+ "git st",
173
+ cwd,
174
+ "/bin/zsh",
175
+ new AbortController().signal,
176
+ );
177
+
178
+ assert.equal(suggestion?.value, "git stash");
179
+ assert.equal(suggestion?.source, "project-history");
180
+ });
181
+
182
+ test("ghost suggestion shows newest project history on an empty prompt", async () => {
183
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-empty-project-ghost-"));
184
+ const histfile = join(cwd, ".zsh_history");
185
+ process.env.HISTFILE = histfile;
186
+ writeFileSync(histfile, ": 1711111111:0;git pull\n");
187
+ appendProjectHistory(cwd, "git status", cwd);
188
+ appendProjectHistory(cwd, "git stash", cwd);
189
+
190
+ const engine = new BashCompletionEngine();
191
+ const suggestion = await engine.getGhostSuggestion(
192
+ "",
193
+ cwd,
194
+ "/bin/zsh",
195
+ new AbortController().signal,
196
+ );
197
+
198
+ assert.equal(suggestion?.value, "git stash");
199
+ assert.equal(suggestion?.source, "project-history");
200
+ });
201
+
202
+ test("ghost suggestion stays empty on an empty prompt when only global history exists", async () => {
203
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-empty-global-ghost-"));
204
+ const histfile = join(cwd, ".zsh_history");
205
+ process.env.HISTFILE = histfile;
206
+ writeFileSync(histfile, [
207
+ ": 1711111111:0;git fetch",
208
+ ": 1711111112:0;git pull",
209
+ ].join("\n"));
210
+
211
+ const engine = new BashCompletionEngine();
212
+ const suggestion = await engine.getGhostSuggestion(
213
+ "",
214
+ cwd,
215
+ "/bin/zsh",
216
+ new AbortController().signal,
217
+ );
218
+
219
+ assert.equal(suggestion, null);
220
+ });
221
+
222
+ test("ghost suggestion stays empty when the prompt is empty and no history exists", async () => {
223
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-empty-no-history-"));
224
+ const histfile = join(cwd, ".zsh_history");
225
+ process.env.HISTFILE = histfile;
226
+ writeFileSync(histfile, "");
227
+
228
+ const engine = new BashCompletionEngine();
229
+ const suggestion = await engine.getGhostSuggestion(
230
+ "",
231
+ cwd,
232
+ "/bin/zsh",
233
+ new AbortController().signal,
234
+ );
235
+
236
+ assert.equal(suggestion, null);
237
+ });
238
+
239
+ test("ghost suggestion can extend the current token from deterministic path completions", async () => {
240
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-inline-ghost-"));
241
+ mkdirSync(join(cwd, "dev"), { recursive: true });
242
+ mkdirSync(join(cwd, "My Folder"), { recursive: true });
243
+
244
+ const engine = new BashCompletionEngine();
245
+ const suggestion = await engine.getGhostSuggestion(
246
+ "cd d",
247
+ cwd,
248
+ "/bin/sh",
249
+ new AbortController().signal,
250
+ );
251
+ const escapedSuggestion = await engine.getGhostSuggestion(
252
+ "cd M",
253
+ cwd,
254
+ "/bin/sh",
255
+ new AbortController().signal,
256
+ );
257
+
258
+ assert.equal(suggestion?.value, "cd dev/");
259
+ assert.equal(suggestion?.source, "path");
260
+ assert.equal(escapedSuggestion?.value, "cd My\\ Folder/");
261
+ assert.equal(escapedSuggestion?.source, "path");
262
+ });
263
+
264
+ test("ghost suggestion does not invoke shell-native completion hooks", async () => {
265
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-no-native-ghost-"));
266
+ mkdirSync(join(cwd, "dev"), { recursive: true });
267
+
268
+ const engine = new BashCompletionEngine();
269
+ Reflect.set(engine, "getNativeSuggestions", async () => {
270
+ throw new Error("native completion should stay disabled");
271
+ });
272
+
273
+ const suggestion = await engine.getGhostSuggestion(
274
+ "cd d",
275
+ cwd,
276
+ "/bin/zsh",
277
+ new AbortController().signal,
278
+ );
279
+
280
+ assert.equal(suggestion?.value, "cd dev/");
281
+ assert.equal(suggestion?.source, "path");
282
+ });
283
+
284
+ test("command-position ghost prefers the newest successful project-history command", async () => {
285
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-command-project-history-"));
286
+ const histfile = join(cwd, ".zsh_history");
287
+ process.env.HISTFILE = histfile;
288
+ writeFileSync(histfile, "");
289
+ appendProjectHistory(cwd, "git status", cwd);
290
+
291
+ const engine = new BashCompletionEngine();
292
+ const suggestion = await engine.getGhostSuggestion(
293
+ "g",
294
+ cwd,
295
+ "/bin/zsh",
296
+ new AbortController().signal,
297
+ );
298
+
299
+ assert.equal(suggestion?.value, "git status");
300
+ assert.equal(suggestion?.source, "project-history");
301
+ });
302
+
303
+ test("command-position ghost uses guarded global git history when project history is absent", async () => {
304
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-command-global-history-"));
305
+ const histfile = join(cwd, ".zsh_history");
306
+ process.env.HISTFILE = histfile;
307
+ writeFileSync(histfile, ": 1711111111:0;git stash\n");
308
+
309
+ const engine = new BashCompletionEngine();
310
+ const shortStemSuggestion = await engine.getGhostSuggestion(
311
+ "g",
312
+ cwd,
313
+ "/bin/zsh",
314
+ new AbortController().signal,
315
+ );
316
+ const guardedSuggestion = await engine.getGhostSuggestion(
317
+ "gi",
318
+ cwd,
319
+ "/bin/zsh",
320
+ new AbortController().signal,
321
+ );
322
+
323
+ assert.equal(shortStemSuggestion?.value, "git status");
324
+ assert.equal(shortStemSuggestion?.source, "git");
325
+ assert.equal(guardedSuggestion?.value, "git stash");
326
+ assert.equal(guardedSuggestion?.source, "global-history");
327
+ });
328
+
329
+ test("command-position ghost falls back to git status when git is likely but history is absent", async () => {
330
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-command-git-default-"));
331
+ const histfile = join(cwd, ".zsh_history");
332
+ process.env.HISTFILE = histfile;
333
+ writeFileSync(histfile, "");
334
+
335
+ const engine = new BashCompletionEngine();
336
+ const suggestion = await engine.getGhostSuggestion(
337
+ "g",
338
+ cwd,
339
+ "/bin/zsh",
340
+ new AbortController().signal,
341
+ );
342
+
343
+ assert.equal(suggestion?.value, "git status");
344
+ assert.equal(suggestion?.source, "git");
345
+ });
346
+
347
+ test("command-position ghost falls back to cd dot-dot for the cd stem", async () => {
348
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-command-cd-default-"));
349
+ const histfile = join(cwd, ".zsh_history");
350
+ process.env.HISTFILE = histfile;
351
+ writeFileSync(histfile, "");
352
+
353
+ const engine = new BashCompletionEngine();
354
+ const suggestion = await engine.getGhostSuggestion(
355
+ "c",
356
+ cwd,
357
+ "/bin/zsh",
358
+ new AbortController().signal,
359
+ );
360
+
361
+ assert.equal(suggestion?.value, "cd ..");
362
+ assert.equal(suggestion?.source, "path");
363
+ });
364
+
365
+ test("command-position ghost stays empty when there is no supported history-backed stem", async () => {
366
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-command-empty-"));
367
+ const histfile = join(cwd, ".zsh_history");
368
+ process.env.HISTFILE = histfile;
369
+ writeFileSync(histfile, "");
370
+
371
+ const engine = new BashCompletionEngine();
372
+ const suggestion = await engine.getGhostSuggestion(
373
+ "x",
374
+ cwd,
375
+ "/bin/zsh",
376
+ new AbortController().signal,
377
+ );
378
+
379
+ assert.equal(suggestion, null);
380
+ });
381
+
382
+ test("ghost suggestion ignores invalid raw global history and keeps a deterministic git candidate", async () => {
383
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-global-history-ghost-"));
384
+ const histfile = join(cwd, ".zsh_history");
385
+ process.env.HISTFILE = histfile;
386
+ writeFileSync(histfile, ": 1711111111:0;git statis\n");
387
+
388
+ const engine = new BashCompletionEngine();
389
+ const suggestion = await engine.getGhostSuggestion(
390
+ "git st",
391
+ cwd,
392
+ "/bin/zsh",
393
+ new AbortController().signal,
394
+ );
395
+
396
+ assert.match(suggestion?.value ?? "", /^git sta(?:sh|tus)$/);
397
+ assert.equal(suggestion?.source, "git");
398
+ });
399
+
400
+ test("global history boosts already-valid deterministic git candidates", async () => {
401
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-global-history-tiebreak-ghost-"));
402
+ const histfile = join(cwd, ".zsh_history");
403
+ process.env.HISTFILE = histfile;
404
+ writeFileSync(histfile, ": 1711111111:0;git stash\n");
405
+
406
+ const engine = new BashCompletionEngine();
407
+ const suggestion = await engine.getGhostSuggestion(
408
+ "git st",
409
+ cwd,
410
+ "/bin/zsh",
411
+ new AbortController().signal,
412
+ );
413
+
414
+ assert.equal(suggestion?.value, "git stash");
415
+ assert.equal(suggestion?.source, "git");
416
+ });
417
+
418
+ test("deterministic path completion keeps directory suffixes for escaped paths", async () => {
419
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-path-escaped-"));
420
+ const histfile = join(cwd, ".zsh_history");
421
+ process.env.HISTFILE = histfile;
422
+ writeFileSync(histfile, "");
423
+ mkdirSync(join(cwd, "My Folder"), { recursive: true });
424
+
425
+ const engine = new BashCompletionEngine();
426
+ const suggestion = await engine.getGhostSuggestion(
427
+ "cd M",
428
+ cwd,
429
+ "/bin/zsh",
430
+ new AbortController().signal,
431
+ );
432
+
433
+ assert.equal(suggestion?.value, "cd My\\ Folder/");
434
+ assert.equal(suggestion?.source, "path");
435
+ });
436
+
437
+ test("deterministic path completion handles bash argument position", async () => {
438
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-bash-path-"));
439
+ mkdirSync(join(cwd, "devdir"), { recursive: true });
440
+
441
+ const engine = new BashCompletionEngine();
442
+ const suggestion = await engine.getGhostSuggestion(
443
+ "cd d",
444
+ cwd,
445
+ "/bin/bash",
446
+ new AbortController().signal,
447
+ );
448
+
449
+ assert.equal(suggestion?.value, "cd devdir/");
450
+ assert.equal(suggestion?.source, "path");
451
+ });
452
+
453
+ test("managed shell session preserves cwd changes across commands", async () => {
454
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-shell-"));
455
+ const childDir = join(cwd, "child");
456
+ mkdirSync(childDir, { recursive: true });
457
+ const store = new BashTranscriptStore({ transcriptMaxLines: 100, transcriptMaxBytes: 64 * 1024 });
458
+ const session = new ManagedShellSession("/bin/zsh", cwd, store, () => {}, () => {});
459
+
460
+ try {
461
+ await session.ensureReady();
462
+ await session.runCommand(`cd ${childDir}`);
463
+ const waitForCommand = async () => {
464
+ const start = Date.now();
465
+ while (session.state.running && Date.now() - start < 5000) {
466
+ await new Promise((resolve) => setTimeout(resolve, 25));
467
+ }
468
+ assert.equal(session.state.running, false);
469
+ };
470
+
471
+ await waitForCommand();
472
+ assert.equal(session.state.cwd, childDir);
473
+
474
+ await session.runCommand("pwd");
475
+ await waitForCommand();
476
+
477
+ const snapshot = store.getSnapshot();
478
+ const lastCommand = snapshot.commands[snapshot.commands.length - 1];
479
+ assert.ok(lastCommand?.output.includes(childDir));
480
+ } finally {
481
+ session.dispose();
482
+ }
483
+ });
484
+
485
+ test("managed shell session recovers cleanly after interrupt", async () => {
486
+ const cwd = mkdtempSync(join(tmpdir(), "powerline-shell-interrupt-"));
487
+ const store = new BashTranscriptStore({ transcriptMaxLines: 100, transcriptMaxBytes: 64 * 1024 });
488
+ const session = new ManagedShellSession("/bin/zsh", cwd, store, () => {}, () => {});
489
+
490
+ const waitForCommand = async () => {
491
+ const start = Date.now();
492
+ while (session.state.running && Date.now() - start < 5000) {
493
+ await new Promise((resolve) => setTimeout(resolve, 25));
494
+ }
495
+ assert.equal(session.state.running, false);
496
+ };
497
+
498
+ try {
499
+ await session.ensureReady();
500
+ await session.runCommand("sleep 5");
501
+ await new Promise((resolve) => setTimeout(resolve, 100));
502
+ session.interrupt();
503
+ await waitForCommand();
504
+
505
+ const interruptedCommand = store.getSnapshot().commands[0];
506
+ assert.equal(interruptedCommand?.exitCode, 130);
507
+
508
+ await session.runCommand("printf 'after\\n'");
509
+ await waitForCommand();
510
+
511
+ const snapshot = store.getSnapshot();
512
+ const lastCommand = snapshot.commands[snapshot.commands.length - 1];
513
+ assert.equal(lastCommand?.command, "printf 'after\\n'");
514
+ assert.equal(lastCommand?.exitCode, 0);
515
+ assert.ok(lastCommand?.output.includes("after"));
516
+ } finally {
517
+ session.dispose();
518
+ }
519
+ });
520
+
521
+ test("bash editor Tab accepts the current ghost suggestion without opening autocomplete", async () => {
522
+ const links = ensureEditorModuleLinks();
523
+
524
+ try {
525
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
526
+ let accepted = false;
527
+
528
+ getMethod(BashModeEditor.prototype, "handleInput").call({
529
+ optionsRef: {
530
+ isBashModeActive: () => true,
531
+ isShellRunning: () => false,
532
+ onExitBashMode() {},
533
+ onInterrupt() {},
534
+ onNotify() {},
535
+ onSubmitCommand() {},
536
+ },
537
+ keybindingsRef: {
538
+ matches(_data: string, id: string) {
539
+ return id === "tui.input.tab";
540
+ },
541
+ },
542
+ isShowingAutocomplete() {
543
+ return false;
544
+ },
545
+ acceptGhostSuggestion() {
546
+ accepted = true;
547
+ return true;
548
+ },
549
+ }, "tab");
550
+
551
+ assert.equal(accepted, true);
552
+ } finally {
553
+ links.cleanup();
554
+ }
555
+ });
556
+
557
+ test("bash editor does not submit pasted multiline input while bracketed paste is active", async () => {
558
+ const links = ensureEditorModuleLinks();
559
+
560
+ try {
561
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
562
+ const { CustomEditor } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-editor.js", import.meta.url).href);
563
+
564
+ let delegated = 0;
565
+ let submitted = 0;
566
+ const superHandleInput = CustomEditor.prototype.handleInput;
567
+ CustomEditor.prototype.handleInput = function handleInput() {
568
+ delegated += 1;
569
+ };
570
+
571
+ try {
572
+ getMethod(BashModeEditor.prototype, "handleInput").call({
573
+ isInPaste: true,
574
+ optionsRef: {
575
+ isBashModeActive: () => true,
576
+ isShellRunning: () => false,
577
+ onExitBashMode() {},
578
+ onInterrupt() {},
579
+ onNotify() {},
580
+ onSubmitCommand() {
581
+ submitted += 1;
582
+ },
583
+ getHistoryEntries() {
584
+ return [];
585
+ },
586
+ resolveGhostSuggestion: async () => null,
587
+ },
588
+ keybindingsRef: {
589
+ matches(data: string, id: string) {
590
+ return data === "\r" && id === "tui.input.submit";
591
+ },
592
+ },
593
+ }, "\r");
594
+ } finally {
595
+ CustomEditor.prototype.handleInput = superHandleInput;
596
+ }
597
+
598
+ assert.equal(submitted, 0);
599
+ assert.equal(delegated, 1);
600
+ } finally {
601
+ links.cleanup();
602
+ }
603
+ });
604
+
605
+ test("bash editor refreshes shell ghost state after a bracketed paste completes", async () => {
606
+ const links = ensureEditorModuleLinks();
607
+
608
+ try {
609
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
610
+ const { CustomEditor } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-editor.js", import.meta.url).href);
611
+
612
+ let delegated = 0;
613
+ let scheduled = 0;
614
+ const superHandleInput = CustomEditor.prototype.handleInput;
615
+ CustomEditor.prototype.handleInput = function handleInput() {
616
+ delegated += 1;
617
+ Reflect.set(this, "isInPaste", false);
618
+ };
619
+
620
+ try {
621
+ getMethod(BashModeEditor.prototype, "handleInput").call({
622
+ isInPaste: true,
623
+ optionsRef: {
624
+ isBashModeActive: () => true,
625
+ isShellRunning: () => false,
626
+ onExitBashMode() {},
627
+ onInterrupt() {},
628
+ onNotify() {},
629
+ onSubmitCommand() {},
630
+ getHistoryEntries() {
631
+ return [];
632
+ },
633
+ resolveGhostSuggestion: async () => null,
634
+ },
635
+ keybindingsRef: {
636
+ matches() {
637
+ return false;
638
+ },
639
+ },
640
+ getExpandedText() {
641
+ return "git status";
642
+ },
643
+ isShellCompletionContext() {
644
+ return true;
645
+ },
646
+ shellHistoryIndex: 3,
647
+ shellHistoryItems: ["git status"],
648
+ shellHistoryDraft: "git",
649
+ scheduleGhostUpdate() {
650
+ scheduled += 1;
651
+ },
652
+ }, "\r");
653
+ } finally {
654
+ CustomEditor.prototype.handleInput = superHandleInput;
655
+ }
656
+
657
+ assert.equal(delegated, 1);
658
+ assert.equal(scheduled, 1);
659
+ } finally {
660
+ links.cleanup();
661
+ }
662
+ });
663
+
664
+ test("bash editor inserts Finder file drops as path strings", async () => {
665
+ const links = ensureEditorModuleLinks();
666
+
667
+ try {
668
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
669
+ const { KeybindingsManager } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/core/keybindings.js", import.meta.url).href);
670
+ const keybindings = KeybindingsManager.create();
671
+ let scheduled = 0;
672
+ const editor = new BashModeEditor(
673
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
674
+ {},
675
+ keybindings,
676
+ {
677
+ keybindings,
678
+ isBashModeActive: () => false,
679
+ isShellRunning: () => false,
680
+ onExitBashMode() {},
681
+ onSubmitCommand() {},
682
+ onInterrupt() {},
683
+ onNotify() {},
684
+ getHistoryEntries: () => [],
685
+ resolveGhostSuggestion: async () => null,
686
+ },
687
+ );
688
+
689
+ editor.handleInput("\x1b[200~file:///Users/nico/Desktop/Screen%20Shot%202026-05-08.png\x1b[201~");
690
+ assert.equal(editor.getText(), "/Users/nico/Desktop/Screen Shot 2026-05-08.png");
691
+
692
+ editor.handleInput(" ");
693
+ editor.handleInput("\x1b[200~/Users/nico/Documents/Project\\ Folder\x1b[201~");
694
+ assert.equal(editor.getText(), "/Users/nico/Desktop/Screen Shot 2026-05-08.png /Users/nico/Documents/Project\\ Folder");
695
+
696
+ const shellEditor = new BashModeEditor(
697
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
698
+ {},
699
+ keybindings,
700
+ {
701
+ keybindings,
702
+ isBashModeActive: () => true,
703
+ isShellRunning: () => false,
704
+ onExitBashMode() {},
705
+ onSubmitCommand() {},
706
+ onInterrupt() {},
707
+ onNotify() {},
708
+ getHistoryEntries: () => [],
709
+ resolveGhostSuggestion: async () => null,
710
+ },
711
+ );
712
+ Reflect.set(shellEditor, "scheduleGhostUpdate", () => {
713
+ scheduled += 1;
714
+ });
715
+
716
+ shellEditor.handleInput("\x1b[200~file:///Users/nico/Pictures/Finder%20Image.png\nfile:///Users/nico/Desktop/Capture.png\x1b[201~");
717
+ assert.equal(shellEditor.getText(), "/Users/nico/Pictures/Finder Image.png /Users/nico/Desktop/Capture.png");
718
+ assert.equal(scheduled, 1);
719
+ } finally {
720
+ links.cleanup();
721
+ }
722
+ });
723
+
724
+ test("one-off bash autocomplete provider stays inactive even inside bang commands", async () => {
725
+ const provider = new OneOffBashAutocompleteProvider();
726
+ const suggestions = await provider.getSuggestions(
727
+ ["!!gi"],
728
+ 0,
729
+ 4,
730
+ { signal: new AbortController().signal },
731
+ );
732
+
733
+ assert.equal(suggestions, null);
734
+ });
735
+
736
+ test("bash autocomplete providers return null synchronously in shell contexts", () => {
737
+ const signal = new AbortController().signal;
738
+
739
+ const bashSuggestions = new BashAutocompleteProvider().getSuggestions(["git st"], 0, 6, { signal });
740
+ const oneOffSuggestions = new OneOffBashAutocompleteProvider().getSuggestions(["!git st"], 0, 7, { signal });
741
+
742
+ assert.equal(bashSuggestions, null);
743
+ assert.equal(oneOffSuggestions, null);
744
+ assert.equal(bashSuggestions instanceof Promise, false);
745
+ assert.equal(oneOffSuggestions instanceof Promise, false);
746
+ });
747
+
748
+ test("mode-aware autocomplete provider preserves synchronous default results", () => {
749
+ const signal = new AbortController().signal;
750
+ const syncResult = {
751
+ items: [{ value: "status", label: "status" }],
752
+ prefix: "st",
753
+ };
754
+ const provider = new ModeAwareAutocompleteProvider(
755
+ {
756
+ getSuggestions() {
757
+ return syncResult;
758
+ },
759
+ applyCompletion(lines: string[], cursorLine: number, cursorCol: number) {
760
+ return { lines, cursorLine, cursorCol };
761
+ },
762
+ },
763
+ new BashAutocompleteProvider(),
764
+ new OneOffBashAutocompleteProvider(),
765
+ () => false,
766
+ );
767
+
768
+ const suggestions = provider.getSuggestions(["st"], 0, 2, { signal });
769
+
770
+ assert.equal(suggestions, syncResult);
771
+ assert.equal(suggestions instanceof Promise, false);
772
+ });
773
+
774
+ test("one-off bash autocomplete provider stays inactive before the bang command starts", async () => {
775
+ const provider = new OneOffBashAutocompleteProvider();
776
+
777
+ assert.equal(provider.shouldTriggerFileCompletion(["!git status"], 0, 0), false);
778
+ assert.equal(
779
+ await provider.getSuggestions(["!git status"], 0, 0, { signal: new AbortController().signal }),
780
+ null,
781
+ );
782
+ });
783
+
784
+ test("bash editor refreshGhostSuggestion reuses the ghost scheduling path", async () => {
785
+ const links = ensureEditorModuleLinks();
786
+
787
+ try {
788
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
789
+ let scheduled = false;
790
+
791
+ getMethod(BashModeEditor.prototype, "refreshGhostSuggestion").call({
792
+ scheduleGhostUpdate() {
793
+ scheduled = true;
794
+ },
795
+ });
796
+
797
+ assert.equal(scheduled, true);
798
+ } finally {
799
+ links.cleanup();
800
+ }
801
+ });
802
+
803
+ test("bash editor dismiss clears autocomplete when mode turns off", async () => {
804
+ const links = ensureEditorModuleLinks();
805
+
806
+ try {
807
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
808
+ let aborted = false;
809
+ let cancelled = false;
810
+ let rendered = false;
811
+ const fakeAbort = { abort() { aborted = true; } };
812
+ const fakeEditor = {
813
+ historyIndex: 7,
814
+ shellHistoryIndex: 2,
815
+ shellHistoryItems: ["git status"],
816
+ shellHistoryDraft: "git st",
817
+ ghostAbort: fakeAbort,
818
+ ghost: { value: "git status", source: "project-history" },
819
+ clearGhostSuggestion() {
820
+ this.ghostAbort?.abort();
821
+ this.ghostAbort = null;
822
+ this.ghost = null;
823
+ },
824
+ cancelAutocomplete() {
825
+ cancelled = true;
826
+ },
827
+ tui: {
828
+ requestRender() {
829
+ rendered = true;
830
+ },
831
+ },
832
+ };
833
+
834
+ getMethod(BashModeEditor.prototype, "dismissBashModeUi").call(fakeEditor);
835
+
836
+ assert.equal(aborted, true);
837
+ assert.equal(cancelled, true);
838
+ assert.equal(rendered, true);
839
+ assert.equal(fakeEditor.historyIndex, 7);
840
+ assert.equal(fakeEditor.shellHistoryIndex, -1);
841
+ } finally {
842
+ links.cleanup();
843
+ }
844
+ });
845
+
846
+ test("bash editor shell history state does not clobber the base prompt history index", async () => {
847
+ const links = ensureEditorModuleLinks();
848
+
849
+ try {
850
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
851
+ const fakeEditor = {
852
+ historyIndex: 5,
853
+ shellHistoryIndex: -1,
854
+ shellHistoryItems: [],
855
+ shellHistoryDraft: "",
856
+ ghostAbort: null,
857
+ ghost: null,
858
+ optionsRef: {
859
+ getHistoryEntries: () => ["git stash", "git status"],
860
+ onNotify: () => {},
861
+ },
862
+ getExpandedText() {
863
+ return "git st";
864
+ },
865
+ setText() {},
866
+ clearGhostSuggestion() {},
867
+ scheduleGhostUpdate() {},
868
+ };
869
+
870
+ getMethod(BashModeEditor.prototype, "navigateShellHistory").call(fakeEditor, -1);
871
+
872
+ assert.equal(fakeEditor.historyIndex, 5);
873
+ assert.equal(fakeEditor.shellHistoryIndex, 0);
874
+ } finally {
875
+ links.cleanup();
876
+ }
877
+ });
878
+
879
+ test("bash editor escape exits bash mode", async () => {
880
+ const links = ensureEditorModuleLinks();
881
+
882
+ try {
883
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
884
+ let exited = false;
885
+ let interrupted = false;
886
+
887
+ getMethod(BashModeEditor.prototype, "handleInput").call({
888
+ optionsRef: {
889
+ isBashModeActive: () => true,
890
+ onExitBashMode: () => {
891
+ exited = true;
892
+ },
893
+ isShellRunning: () => false,
894
+ onInterrupt: () => {
895
+ interrupted = true;
896
+ },
897
+ },
898
+ keybindingsRef: {
899
+ matches(data: string, id: string) {
900
+ return data === "escape" && id === "app.interrupt";
901
+ },
902
+ },
903
+ }, "escape");
904
+
905
+ assert.equal(exited, true);
906
+ assert.equal(interrupted, false);
907
+ } finally {
908
+ links.cleanup();
909
+ }
910
+ });
911
+
912
+ test("bash editor right arrow accepts an empty-prompt ghost suggestion without submitting", async () => {
913
+ const links = ensureEditorModuleLinks();
914
+
915
+ try {
916
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
917
+ let accepted = false;
918
+ let submitted = false;
919
+
920
+ getMethod(BashModeEditor.prototype, "handleInput").call({
921
+ optionsRef: {
922
+ isBashModeActive: () => true,
923
+ isShellRunning: () => false,
924
+ onExitBashMode: () => {},
925
+ onSubmitCommand: () => {
926
+ submitted = true;
927
+ },
928
+ onInterrupt: () => {},
929
+ onNotify: () => {},
930
+ },
931
+ keybindingsRef: {
932
+ matches(data: string, id: string) {
933
+ return data === "right" && id === "tui.editor.cursorRight";
934
+ },
935
+ },
936
+ isShowingAutocomplete() {
937
+ return false;
938
+ },
939
+ acceptGhostSuggestion() {
940
+ accepted = true;
941
+ return true;
942
+ },
943
+ }, "right");
944
+
945
+ assert.equal(accepted, true);
946
+ assert.equal(submitted, false);
947
+ } finally {
948
+ links.cleanup();
949
+ }
950
+ });
951
+
952
+ test("bash editor right arrow accepts ghost text for one-off bang commands", async () => {
953
+ const links = ensureEditorModuleLinks();
954
+
955
+ try {
956
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
957
+ let accepted = false;
958
+
959
+ getMethod(BashModeEditor.prototype, "handleInput").call({
960
+ optionsRef: {
961
+ isBashModeActive: () => false,
962
+ },
963
+ keybindingsRef: {
964
+ matches(data: string, id: string) {
965
+ return data === "right" && id === "tui.editor.cursorRight";
966
+ },
967
+ },
968
+ getExpandedText() {
969
+ return "!git st";
970
+ },
971
+ isOneOffBashCommandContext() {
972
+ return true;
973
+ },
974
+ acceptGhostSuggestion() {
975
+ accepted = true;
976
+ return true;
977
+ },
978
+ }, "right");
979
+
980
+ assert.equal(accepted, true);
981
+ } finally {
982
+ links.cleanup();
983
+ }
984
+ });
985
+
986
+ test("bash editor runs copied Pi app action handlers for alt-enter", async () => {
987
+ const links = ensureEditorModuleLinks();
988
+
989
+ try {
990
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
991
+ const { KeybindingsManager } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/core/keybindings.js", import.meta.url).href);
992
+ const { setKittyProtocolActive } = await import(new URL("../node_modules/@earendil-works/pi-tui/dist/keys.js", import.meta.url).href);
993
+ const keybindings = KeybindingsManager.create();
994
+ const editor = new BashModeEditor(
995
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
996
+ {},
997
+ keybindings,
998
+ {
999
+ keybindings,
1000
+ isBashModeActive: () => false,
1001
+ isShellRunning: () => false,
1002
+ onExitBashMode() {},
1003
+ onSubmitCommand() {},
1004
+ onInterrupt() {},
1005
+ onNotify() {},
1006
+ getHistoryEntries: () => [],
1007
+ resolveGhostSuggestion: async () => null,
1008
+ },
1009
+ );
1010
+
1011
+ let handled = 0;
1012
+ editor.actionHandlers.set("app.message.followUp", () => {
1013
+ handled += 1;
1014
+ });
1015
+
1016
+ try {
1017
+ setKittyProtocolActive(false);
1018
+ editor.handleInput("\x1b\r");
1019
+ assert.equal(handled, 1);
1020
+
1021
+ setKittyProtocolActive(true);
1022
+ editor.handleInput("\x1b[13;3u");
1023
+ assert.equal(handled, 2);
1024
+ } finally {
1025
+ setKittyProtocolActive(false);
1026
+ }
1027
+ } finally {
1028
+ links.cleanup();
1029
+ }
1030
+ });
1031
+
1032
+ test("bash editor command-z undoes deleted text for supported encodings only", async () => {
1033
+ const links = ensureEditorModuleLinks();
1034
+
1035
+ try {
1036
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1037
+ const { KeybindingsManager } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/core/keybindings.js", import.meta.url).href);
1038
+ const keybindings = KeybindingsManager.create();
1039
+ const createEditor = (options: {
1040
+ keybindings?: typeof keybindings;
1041
+ isBashModeActive?: () => boolean;
1042
+ isShellRunning?: () => boolean;
1043
+ onExitBashMode?: () => void;
1044
+ onInterrupt?: () => void;
1045
+ resolveGhostSuggestion?: (text: string) => Promise<null>;
1046
+ } = {}) => new BashModeEditor(
1047
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
1048
+ {},
1049
+ options.keybindings ?? keybindings,
1050
+ {
1051
+ keybindings: options.keybindings ?? keybindings,
1052
+ isBashModeActive: options.isBashModeActive ?? (() => false),
1053
+ isShellRunning: options.isShellRunning ?? (() => false),
1054
+ onExitBashMode: options.onExitBashMode ?? (() => {}),
1055
+ onSubmitCommand() {},
1056
+ onInterrupt: options.onInterrupt ?? (() => {}),
1057
+ onNotify() {},
1058
+ getHistoryEntries: () => [],
1059
+ resolveGhostSuggestion: options.resolveGhostSuggestion ?? (async () => null),
1060
+ },
1061
+ );
1062
+
1063
+ for (const data of ["\x1b[122;9u", "\x1b[122;9:1u", "\x1b[122;9:2u", "\x1b[27;9;122~"]) {
1064
+ const editor = createEditor();
1065
+
1066
+ for (const char of "hello") editor.handleInput(char);
1067
+ editor.handleInput("\x7f");
1068
+ assert.equal(editor.getText(), "hell");
1069
+
1070
+ editor.handleInput(data);
1071
+ assert.equal(editor.getText(), "hello");
1072
+ }
1073
+
1074
+ const editor = createEditor();
1075
+
1076
+ for (const char of "hello") editor.handleInput(char);
1077
+ editor.handleInput("\x7f");
1078
+ editor.handleInput("\x1b[122;9u");
1079
+ assert.equal(editor.getText(), "hello");
1080
+
1081
+ editor.handleInput("\x1b[122;9:3u");
1082
+ assert.equal(editor.getText(), "hello");
1083
+
1084
+ editor.handleInput("\x7f");
1085
+ editor.handleInput("\x1b[27;9;90~");
1086
+ assert.equal(editor.getText(), "hell");
1087
+
1088
+ const plainEditor = createEditor();
1089
+ plainEditor.handleInput("z");
1090
+ assert.equal(plainEditor.getText(), "z");
1091
+
1092
+ for (const action of ["app.interrupt", "app.clear"]) {
1093
+ let exited = false;
1094
+ let interrupted = false;
1095
+ const customizedKeybindings = new KeybindingsManager({ [action]: "super+z" });
1096
+ assert.equal(customizedKeybindings.matches("\x1b[122;9u", action), true);
1097
+ const customizedEditor = createEditor({
1098
+ keybindings: customizedKeybindings,
1099
+ isBashModeActive: () => true,
1100
+ isShellRunning: () => true,
1101
+ onExitBashMode: () => {
1102
+ exited = true;
1103
+ },
1104
+ onInterrupt: () => {
1105
+ interrupted = true;
1106
+ },
1107
+ });
1108
+
1109
+ for (const char of "hello") customizedEditor.handleInput(char);
1110
+ customizedEditor.handleInput("\x7f");
1111
+ customizedEditor.handleInput("\x1b[122;9u");
1112
+
1113
+ assert.equal(customizedEditor.getText(), "hello");
1114
+ assert.equal(exited, false);
1115
+ assert.equal(interrupted, false);
1116
+ }
1117
+ } finally {
1118
+ links.cleanup();
1119
+ }
1120
+ });
1121
+
1122
+ test("bash editor command-z resets shell history and updates ghost state", async () => {
1123
+ const links = ensureEditorModuleLinks();
1124
+
1125
+ try {
1126
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1127
+ const { KeybindingsManager } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/core/keybindings.js", import.meta.url).href);
1128
+ const keybindings = KeybindingsManager.create();
1129
+ const createEditor = (options: {
1130
+ isBashModeActive?: () => boolean;
1131
+ resolveGhostSuggestion?: (text: string) => Promise<null>;
1132
+ } = {}) => new BashModeEditor(
1133
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
1134
+ {},
1135
+ keybindings,
1136
+ {
1137
+ keybindings,
1138
+ isBashModeActive: options.isBashModeActive ?? (() => false),
1139
+ isShellRunning: () => false,
1140
+ onExitBashMode() {},
1141
+ onSubmitCommand() {},
1142
+ onInterrupt() {},
1143
+ onNotify() {},
1144
+ getHistoryEntries: () => [],
1145
+ resolveGhostSuggestion: options.resolveGhostSuggestion ?? (async () => null),
1146
+ },
1147
+ );
1148
+ const ghostRefreshes: string[] = [];
1149
+ const shellEditor = createEditor({
1150
+ isBashModeActive: () => true,
1151
+ resolveGhostSuggestion: async (text) => {
1152
+ ghostRefreshes.push(text);
1153
+ return null;
1154
+ },
1155
+ });
1156
+
1157
+ shellEditor.handleInput("a");
1158
+ shellEditor.handleInput("\x7f");
1159
+ Reflect.set(shellEditor, "shellHistoryIndex", 0);
1160
+ Reflect.set(shellEditor, "shellHistoryItems", ["git status"]);
1161
+ Reflect.set(shellEditor, "shellHistoryDraft", "git");
1162
+ shellEditor.handleInput("\x1b[122;9u");
1163
+
1164
+ assert.equal(shellEditor.getText(), "a");
1165
+ assert.equal(Reflect.get(shellEditor, "shellHistoryIndex"), -1);
1166
+ assert.deepEqual(Reflect.get(shellEditor, "shellHistoryItems"), []);
1167
+ assert.equal(Reflect.get(shellEditor, "shellHistoryDraft"), "");
1168
+ assert.equal(ghostRefreshes.at(-1), "a");
1169
+
1170
+ const plainEditor = createEditor();
1171
+ plainEditor.handleInput("z");
1172
+ plainEditor.handleInput("\x7f");
1173
+ Reflect.set(plainEditor, "ghost", { value: "stale" });
1174
+ plainEditor.handleInput("\x1b[122;9u");
1175
+ assert.equal(Reflect.get(plainEditor, "ghost"), null);
1176
+ } finally {
1177
+ links.cleanup();
1178
+ }
1179
+ });
1180
+
1181
+ test("bash editor command arrows jump to editor boundaries", async () => {
1182
+ const links = ensureEditorModuleLinks();
1183
+
1184
+ try {
1185
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1186
+ const { KeybindingsManager } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/core/keybindings.js", import.meta.url).href);
1187
+ const keybindings = KeybindingsManager.create();
1188
+ let renderRequests = 0;
1189
+ const editor = new BashModeEditor(
1190
+ { requestRender() { renderRequests += 1; }, terminal: { columns: 80, rows: 24 } },
1191
+ {},
1192
+ keybindings,
1193
+ {
1194
+ keybindings,
1195
+ isBashModeActive: () => false,
1196
+ isShellRunning: () => false,
1197
+ onExitBashMode() {},
1198
+ onSubmitCommand() {},
1199
+ onInterrupt() {},
1200
+ onNotify() {},
1201
+ getHistoryEntries: () => [],
1202
+ resolveGhostSuggestion: async () => null,
1203
+ },
1204
+ );
1205
+
1206
+ editor.setText("alpha\nbravo\ncharlie");
1207
+ assert.deepEqual(editor.getCursor(), { line: 2, col: 7 });
1208
+
1209
+ editor.handleInput("\x1b[A");
1210
+ assert.notDeepEqual(editor.getCursor(), { line: 0, col: 0 });
1211
+ editor.handleInput("\x1b[B");
1212
+ assert.deepEqual(editor.getCursor(), { line: 2, col: 7 });
1213
+
1214
+ editor.handleInput("\x1b[1;9A");
1215
+ assert.notDeepEqual(editor.getCursor(), { line: 0, col: 0 });
1216
+
1217
+ editor.handleInput("\x1b[1;10A");
1218
+ assert.deepEqual(editor.getCursor(), { line: 0, col: 0 });
1219
+
1220
+ editor.handleInput("\x1b[27;10;66~");
1221
+ assert.deepEqual(editor.getCursor(), { line: 2, col: 7 });
1222
+
1223
+ editor.handleInput("\x1b[27;10;65~");
1224
+ assert.deepEqual(editor.getCursor(), { line: 0, col: 0 });
1225
+
1226
+ editor.handleInput("\x1b[57420;10u");
1227
+ assert.deepEqual(editor.getCursor(), { line: 2, col: 7 });
1228
+
1229
+ editor.handleInput("\x1b[57423;10u");
1230
+ assert.deepEqual(editor.getCursor(), { line: 0, col: 0 });
1231
+
1232
+ editor.handleInput("\x1b[1;10F");
1233
+ assert.deepEqual(editor.getCursor(), { line: 2, col: 7 });
1234
+
1235
+ editor.handleInput("\x1b[1;10:3A");
1236
+ assert.deepEqual(editor.getCursor(), { line: 2, col: 7 });
1237
+
1238
+ const customEditor = new BashModeEditor(
1239
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
1240
+ {},
1241
+ keybindings,
1242
+ {
1243
+ keybindings,
1244
+ isBashModeActive: () => false,
1245
+ isShellRunning: () => false,
1246
+ onExitBashMode() {},
1247
+ onSubmitCommand() {},
1248
+ editorBoundaryShortcuts: { start: "ctrl+shift+u", end: "ctrl+shift+d" },
1249
+ onInterrupt() {},
1250
+ onNotify() {},
1251
+ getHistoryEntries: () => [],
1252
+ resolveGhostSuggestion: async () => null,
1253
+ },
1254
+ );
1255
+ customEditor.setText("alpha\nbravo\ncharlie");
1256
+ customEditor.handleInput("\x1b[117;6u");
1257
+ assert.deepEqual(customEditor.getCursor(), { line: 0, col: 0 });
1258
+ customEditor.handleInput("\x1b[100;6u");
1259
+ assert.deepEqual(customEditor.getCursor(), { line: 2, col: 7 });
1260
+
1261
+ const configuredCommandEditor = new BashModeEditor(
1262
+ { requestRender() {}, terminal: { columns: 80, rows: 24 } },
1263
+ {},
1264
+ keybindings,
1265
+ {
1266
+ keybindings,
1267
+ isBashModeActive: () => false,
1268
+ isShellRunning: () => false,
1269
+ onExitBashMode() {},
1270
+ onSubmitCommand() {},
1271
+ editorBoundaryShortcuts: { start: "super+shift+up", end: "super+shift+down" },
1272
+ onInterrupt() {},
1273
+ onNotify() {},
1274
+ getHistoryEntries: () => [],
1275
+ resolveGhostSuggestion: async () => null,
1276
+ },
1277
+ );
1278
+ configuredCommandEditor.setText("alpha\nbravo\ncharlie");
1279
+ configuredCommandEditor.handleInput("\x1b[1;10A");
1280
+ assert.deepEqual(configuredCommandEditor.getCursor(), { line: 0, col: 0 });
1281
+ configuredCommandEditor.handleInput("\x1b[1;10B");
1282
+ assert.deepEqual(configuredCommandEditor.getCursor(), { line: 2, col: 7 });
1283
+
1284
+ assert.equal(renderRequests, 6);
1285
+ } finally {
1286
+ links.cleanup();
1287
+ }
1288
+ });
1289
+
1290
+ test("bash editor enter does not accept ghost text while a shell command is running", async () => {
1291
+ const links = ensureEditorModuleLinks();
1292
+
1293
+ try {
1294
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1295
+ let warned = false;
1296
+ let submitted = false;
1297
+
1298
+ getMethod(BashModeEditor.prototype, "handleInput").call({
1299
+ ghost: { value: "git status", source: "project-history" },
1300
+ optionsRef: {
1301
+ isBashModeActive: () => true,
1302
+ isShellRunning: () => true,
1303
+ onExitBashMode: () => {},
1304
+ onInterrupt: () => {},
1305
+ onSubmitCommand: () => {
1306
+ submitted = true;
1307
+ },
1308
+ onNotify: (message: string) => {
1309
+ warned = message === "Shell command already running";
1310
+ },
1311
+ },
1312
+ keybindingsRef: {
1313
+ matches(_data: string, id: string) {
1314
+ return id === "tui.input.submit";
1315
+ },
1316
+ },
1317
+ getExpandedText() {
1318
+ return "git st";
1319
+ },
1320
+ isShowingAutocomplete() {
1321
+ return false;
1322
+ },
1323
+ acceptGhostSuggestion() {
1324
+ throw new Error("ghost should not be accepted while running");
1325
+ },
1326
+ }, "enter");
1327
+
1328
+ assert.equal(warned, true);
1329
+ assert.equal(submitted, false);
1330
+ } finally {
1331
+ links.cleanup();
1332
+ }
1333
+ });
1334
+
1335
+ test("bash editor enter submits the typed command without accepting ghost text", async () => {
1336
+ const links = ensureEditorModuleLinks();
1337
+
1338
+ try {
1339
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1340
+ let submittedCommand = "";
1341
+
1342
+ getMethod(BashModeEditor.prototype, "handleInput").call({
1343
+ ghost: { value: "git diff --staged", source: "project-history" },
1344
+ optionsRef: {
1345
+ isBashModeActive: () => true,
1346
+ isShellRunning: () => false,
1347
+ onExitBashMode: () => {},
1348
+ onInterrupt: () => {},
1349
+ onNotify: () => {},
1350
+ onSubmitCommand: (command: string) => {
1351
+ submittedCommand = command;
1352
+ },
1353
+ },
1354
+ keybindingsRef: {
1355
+ matches(_data: string, id: string) {
1356
+ return id === "tui.input.submit";
1357
+ },
1358
+ },
1359
+ getExpandedText() {
1360
+ return "git diff";
1361
+ },
1362
+ acceptGhostSuggestion() {
1363
+ throw new Error("enter should not accept ghost text");
1364
+ },
1365
+ clearGhostSuggestion() {},
1366
+ setText() {},
1367
+ refreshGhostSuggestion() {},
1368
+ shellHistoryIndex: -1,
1369
+ shellHistoryItems: [],
1370
+ shellHistoryDraft: "",
1371
+ }, "enter");
1372
+
1373
+ assert.equal(submittedCommand, "git diff");
1374
+ } finally {
1375
+ links.cleanup();
1376
+ }
1377
+ });
1378
+
1379
+ test("one-off bang submit does not accept ghost text before submitting", async () => {
1380
+ const links = ensureEditorModuleLinks();
1381
+
1382
+ try {
1383
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1384
+ const { CustomEditor } = await import(new URL("../node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-editor.js", import.meta.url).href);
1385
+
1386
+ let delegated = 0;
1387
+ const superHandleInput = CustomEditor.prototype.handleInput;
1388
+ CustomEditor.prototype.handleInput = function handleInput() {
1389
+ delegated += 1;
1390
+ };
1391
+
1392
+ try {
1393
+ getMethod(BashModeEditor.prototype, "handleInput").call({
1394
+ ghost: { value: "!git diff --staged", source: "project-history" },
1395
+ optionsRef: {
1396
+ isBashModeActive: () => false,
1397
+ },
1398
+ keybindingsRef: {
1399
+ matches(_data: string, id: string) {
1400
+ return id === "tui.input.submit";
1401
+ },
1402
+ },
1403
+ getExpandedText() {
1404
+ return "!git diff";
1405
+ },
1406
+ isOneOffBashCommandContext() {
1407
+ return true;
1408
+ },
1409
+ isShellCompletionContext() {
1410
+ return true;
1411
+ },
1412
+ acceptGhostSuggestion() {
1413
+ throw new Error("enter should not accept ghost text for one-off bash commands");
1414
+ },
1415
+ }, "enter");
1416
+ } finally {
1417
+ CustomEditor.prototype.handleInput = superHandleInput;
1418
+ }
1419
+
1420
+ assert.equal(delegated, 1);
1421
+ } finally {
1422
+ links.cleanup();
1423
+ }
1424
+ });
1425
+
1426
+ test("bash editor does not accept a hidden ghost suggestion when the cursor is not at the end", async () => {
1427
+ const links = ensureEditorModuleLinks();
1428
+
1429
+ try {
1430
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1431
+ const accepted = getMethod(BashModeEditor.prototype, "acceptGhostSuggestion").call({
1432
+ ghost: { value: "git status", source: "project-history" },
1433
+ getExpandedText() {
1434
+ return "git st";
1435
+ },
1436
+ getCursor() {
1437
+ return { line: 0, col: 3 };
1438
+ },
1439
+ setText() {
1440
+ throw new Error("hidden ghost should not be accepted");
1441
+ },
1442
+ clearGhostSuggestion() {},
1443
+ });
1444
+
1445
+ assert.equal(accepted, false);
1446
+ } finally {
1447
+ links.cleanup();
1448
+ }
1449
+ });
1450
+
1451
+ test("bash editor submit clears the prompt and refreshes the empty ghost suggestion", async () => {
1452
+ const links = ensureEditorModuleLinks();
1453
+
1454
+ try {
1455
+ const { BashModeEditor } = await import("../bash-mode/editor.ts");
1456
+ let submitted = false;
1457
+ let cleared = false;
1458
+ let refreshed = false;
1459
+
1460
+ getMethod(BashModeEditor.prototype, "handleInput").call({
1461
+ optionsRef: {
1462
+ isBashModeActive: () => true,
1463
+ isShellRunning: () => false,
1464
+ onExitBashMode: () => {},
1465
+ onInterrupt: () => {},
1466
+ onNotify: () => {},
1467
+ onSubmitCommand: (command: string) => {
1468
+ submitted = command === "git status";
1469
+ },
1470
+ },
1471
+ keybindingsRef: {
1472
+ matches(_data: string, id: string) {
1473
+ return id === "tui.input.submit";
1474
+ },
1475
+ },
1476
+ isShowingAutocomplete() {
1477
+ return false;
1478
+ },
1479
+ acceptGhostSuggestion() {
1480
+ return false;
1481
+ },
1482
+ getExpandedText() {
1483
+ return "git status";
1484
+ },
1485
+ clearGhostSuggestion() {},
1486
+ setText(value: string) {
1487
+ cleared = value === "";
1488
+ },
1489
+ refreshGhostSuggestion() {
1490
+ refreshed = true;
1491
+ },
1492
+ shellHistoryIndex: 3,
1493
+ shellHistoryItems: ["git status"],
1494
+ shellHistoryDraft: "git st",
1495
+ }, "enter");
1496
+
1497
+ assert.equal(submitted, true);
1498
+ assert.equal(cleared, true);
1499
+ assert.equal(refreshed, true);
1500
+ } finally {
1501
+ links.cleanup();
1502
+ }
1503
+ });