gsd-pi 2.5.1 → 2.7.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 (1014) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +8 -2
  3. package/dist/onboarding.d.ts +1 -1
  4. package/dist/pi-migration.d.ts +1 -1
  5. package/dist/resource-loader.d.ts +1 -1
  6. package/dist/resource-loader.js +1 -1
  7. package/dist/wizard.d.ts +1 -1
  8. package/package.json +12 -6
  9. package/packages/pi-agent-core/dist/agent-loop.d.ts +21 -0
  10. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -0
  11. package/packages/pi-agent-core/dist/agent-loop.js +308 -0
  12. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -0
  13. package/packages/pi-agent-core/dist/agent.d.ts +162 -0
  14. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -0
  15. package/packages/pi-agent-core/dist/agent.js +399 -0
  16. package/packages/pi-agent-core/dist/agent.js.map +1 -0
  17. package/packages/pi-agent-core/dist/index.d.ts +5 -0
  18. package/packages/pi-agent-core/dist/index.d.ts.map +1 -0
  19. package/packages/pi-agent-core/dist/index.js +9 -0
  20. package/packages/pi-agent-core/dist/index.js.map +1 -0
  21. package/packages/pi-agent-core/dist/proxy.d.ts +85 -0
  22. package/packages/pi-agent-core/dist/proxy.d.ts.map +1 -0
  23. package/packages/pi-agent-core/dist/proxy.js +268 -0
  24. package/packages/pi-agent-core/dist/proxy.js.map +1 -0
  25. package/packages/pi-agent-core/dist/types.d.ts +178 -0
  26. package/packages/pi-agent-core/dist/types.d.ts.map +1 -0
  27. package/packages/pi-agent-core/dist/types.js +2 -0
  28. package/packages/pi-agent-core/dist/types.js.map +1 -0
  29. package/packages/pi-agent-core/package.json +14 -0
  30. package/packages/pi-agent-core/src/agent-loop.ts +417 -0
  31. package/packages/pi-agent-core/src/agent.ts +568 -0
  32. package/packages/pi-agent-core/src/index.ts +8 -0
  33. package/packages/pi-agent-core/src/proxy.ts +340 -0
  34. package/packages/pi-agent-core/src/types.ts +194 -0
  35. package/packages/pi-agent-core/tsconfig.json +27 -0
  36. package/packages/pi-ai/bedrock-provider.d.ts +1 -0
  37. package/packages/pi-ai/bedrock-provider.js +1 -0
  38. package/packages/pi-ai/dist/api-registry.d.ts +20 -0
  39. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -0
  40. package/packages/pi-ai/dist/api-registry.js +44 -0
  41. package/packages/pi-ai/dist/api-registry.js.map +1 -0
  42. package/packages/pi-ai/dist/bedrock-provider.d.ts +5 -0
  43. package/packages/pi-ai/dist/bedrock-provider.d.ts.map +1 -0
  44. package/packages/pi-ai/dist/bedrock-provider.js +6 -0
  45. package/packages/pi-ai/dist/bedrock-provider.js.map +1 -0
  46. package/packages/pi-ai/dist/cli.d.ts +3 -0
  47. package/packages/pi-ai/dist/cli.d.ts.map +1 -0
  48. package/packages/pi-ai/dist/cli.js +116 -0
  49. package/packages/pi-ai/dist/cli.js.map +1 -0
  50. package/packages/pi-ai/dist/env-api-keys.d.ts +9 -0
  51. package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -0
  52. package/packages/pi-ai/dist/env-api-keys.js +104 -0
  53. package/packages/pi-ai/dist/env-api-keys.js.map +1 -0
  54. package/packages/pi-ai/dist/index.d.ts +23 -0
  55. package/packages/pi-ai/dist/index.d.ts.map +1 -0
  56. package/packages/pi-ai/dist/index.js +21 -0
  57. package/packages/pi-ai/dist/index.js.map +1 -0
  58. package/packages/pi-ai/dist/models.d.ts +24 -0
  59. package/packages/pi-ai/dist/models.d.ts.map +1 -0
  60. package/packages/pi-ai/dist/models.generated.d.ts +13572 -0
  61. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -0
  62. package/packages/pi-ai/dist/models.generated.js +13368 -0
  63. package/packages/pi-ai/dist/models.generated.js.map +1 -0
  64. package/packages/pi-ai/dist/models.js +55 -0
  65. package/packages/pi-ai/dist/models.js.map +1 -0
  66. package/packages/pi-ai/dist/oauth.d.ts +2 -0
  67. package/packages/pi-ai/dist/oauth.d.ts.map +1 -0
  68. package/packages/pi-ai/dist/oauth.js +2 -0
  69. package/packages/pi-ai/dist/oauth.js.map +1 -0
  70. package/packages/pi-ai/dist/providers/amazon-bedrock.d.ts +15 -0
  71. package/packages/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -0
  72. package/packages/pi-ai/dist/providers/amazon-bedrock.js +600 -0
  73. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -0
  74. package/packages/pi-ai/dist/providers/anthropic.d.ts +33 -0
  75. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -0
  76. package/packages/pi-ai/dist/providers/anthropic.js +732 -0
  77. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -0
  78. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +15 -0
  79. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -0
  80. package/packages/pi-ai/dist/providers/azure-openai-responses.js +187 -0
  81. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -0
  82. package/packages/pi-ai/dist/providers/github-copilot-headers.d.ts +8 -0
  83. package/packages/pi-ai/dist/providers/github-copilot-headers.d.ts.map +1 -0
  84. package/packages/pi-ai/dist/providers/github-copilot-headers.js +29 -0
  85. package/packages/pi-ai/dist/providers/github-copilot-headers.js.map +1 -0
  86. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts +74 -0
  87. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -0
  88. package/packages/pi-ai/dist/providers/google-gemini-cli.js +757 -0
  89. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -0
  90. package/packages/pi-ai/dist/providers/google-shared.d.ts +65 -0
  91. package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -0
  92. package/packages/pi-ai/dist/providers/google-shared.js +300 -0
  93. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -0
  94. package/packages/pi-ai/dist/providers/google-vertex.d.ts +15 -0
  95. package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -0
  96. package/packages/pi-ai/dist/providers/google-vertex.js +374 -0
  97. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -0
  98. package/packages/pi-ai/dist/providers/google.d.ts +13 -0
  99. package/packages/pi-ai/dist/providers/google.d.ts.map +1 -0
  100. package/packages/pi-ai/dist/providers/google.js +355 -0
  101. package/packages/pi-ai/dist/providers/google.js.map +1 -0
  102. package/packages/pi-ai/dist/providers/mistral.d.ts +22 -0
  103. package/packages/pi-ai/dist/providers/mistral.d.ts.map +1 -0
  104. package/packages/pi-ai/dist/providers/mistral.js +498 -0
  105. package/packages/pi-ai/dist/providers/mistral.js.map +1 -0
  106. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +9 -0
  107. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -0
  108. package/packages/pi-ai/dist/providers/openai-codex-responses.js +704 -0
  109. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -0
  110. package/packages/pi-ai/dist/providers/openai-completions.d.ts +15 -0
  111. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -0
  112. package/packages/pi-ai/dist/providers/openai-completions.js +705 -0
  113. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -0
  114. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts +17 -0
  115. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -0
  116. package/packages/pi-ai/dist/providers/openai-responses-shared.js +442 -0
  117. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -0
  118. package/packages/pi-ai/dist/providers/openai-responses.d.ts +13 -0
  119. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -0
  120. package/packages/pi-ai/dist/providers/openai-responses.js +201 -0
  121. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -0
  122. package/packages/pi-ai/dist/providers/register-builtins.d.ts +11 -0
  123. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -0
  124. package/packages/pi-ai/dist/providers/register-builtins.js +138 -0
  125. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -0
  126. package/packages/pi-ai/dist/providers/simple-options.d.ts +8 -0
  127. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -0
  128. package/packages/pi-ai/dist/providers/simple-options.js +35 -0
  129. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -0
  130. package/packages/pi-ai/dist/providers/transform-messages.d.ts +8 -0
  131. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -0
  132. package/packages/pi-ai/dist/providers/transform-messages.js +155 -0
  133. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -0
  134. package/packages/pi-ai/dist/stream.d.ts +8 -0
  135. package/packages/pi-ai/dist/stream.d.ts.map +1 -0
  136. package/packages/pi-ai/dist/stream.js +27 -0
  137. package/packages/pi-ai/dist/stream.js.map +1 -0
  138. package/packages/pi-ai/dist/types.d.ts +285 -0
  139. package/packages/pi-ai/dist/types.d.ts.map +1 -0
  140. package/packages/pi-ai/dist/types.js +2 -0
  141. package/packages/pi-ai/dist/types.js.map +1 -0
  142. package/packages/pi-ai/dist/utils/event-stream.d.ts +21 -0
  143. package/packages/pi-ai/dist/utils/event-stream.d.ts.map +1 -0
  144. package/packages/pi-ai/dist/utils/event-stream.js +77 -0
  145. package/packages/pi-ai/dist/utils/event-stream.js.map +1 -0
  146. package/packages/pi-ai/dist/utils/hash.d.ts +3 -0
  147. package/packages/pi-ai/dist/utils/hash.d.ts.map +1 -0
  148. package/packages/pi-ai/dist/utils/hash.js +14 -0
  149. package/packages/pi-ai/dist/utils/hash.js.map +1 -0
  150. package/packages/pi-ai/dist/utils/json-parse.d.ts +9 -0
  151. package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -0
  152. package/packages/pi-ai/dist/utils/json-parse.js +29 -0
  153. package/packages/pi-ai/dist/utils/json-parse.js.map +1 -0
  154. package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts +17 -0
  155. package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts.map +1 -0
  156. package/packages/pi-ai/dist/utils/oauth/anthropic.js +104 -0
  157. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -0
  158. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts +30 -0
  159. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -0
  160. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +281 -0
  161. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -0
  162. package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts +26 -0
  163. package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -0
  164. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +373 -0
  165. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -0
  166. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts +26 -0
  167. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -0
  168. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +478 -0
  169. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -0
  170. package/packages/pi-ai/dist/utils/oauth/index.d.ts +61 -0
  171. package/packages/pi-ai/dist/utils/oauth/index.d.ts.map +1 -0
  172. package/packages/pi-ai/dist/utils/oauth/index.js +131 -0
  173. package/packages/pi-ai/dist/utils/oauth/index.js.map +1 -0
  174. package/packages/pi-ai/dist/utils/oauth/openai-codex.d.ts +34 -0
  175. package/packages/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -0
  176. package/packages/pi-ai/dist/utils/oauth/openai-codex.js +380 -0
  177. package/packages/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -0
  178. package/packages/pi-ai/dist/utils/oauth/pkce.d.ts +13 -0
  179. package/packages/pi-ai/dist/utils/oauth/pkce.d.ts.map +1 -0
  180. package/packages/pi-ai/dist/utils/oauth/pkce.js +31 -0
  181. package/packages/pi-ai/dist/utils/oauth/pkce.js.map +1 -0
  182. package/packages/pi-ai/dist/utils/oauth/types.d.ts +47 -0
  183. package/packages/pi-ai/dist/utils/oauth/types.d.ts.map +1 -0
  184. package/packages/pi-ai/dist/utils/oauth/types.js +2 -0
  185. package/packages/pi-ai/dist/utils/oauth/types.js.map +1 -0
  186. package/packages/pi-ai/dist/utils/overflow.d.ts +52 -0
  187. package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -0
  188. package/packages/pi-ai/dist/utils/overflow.js +117 -0
  189. package/packages/pi-ai/dist/utils/overflow.js.map +1 -0
  190. package/packages/pi-ai/dist/utils/sanitize-unicode.d.ts +22 -0
  191. package/packages/pi-ai/dist/utils/sanitize-unicode.d.ts.map +1 -0
  192. package/packages/pi-ai/dist/utils/sanitize-unicode.js +26 -0
  193. package/packages/pi-ai/dist/utils/sanitize-unicode.js.map +1 -0
  194. package/packages/pi-ai/dist/utils/typebox-helpers.d.ts +17 -0
  195. package/packages/pi-ai/dist/utils/typebox-helpers.d.ts.map +1 -0
  196. package/packages/pi-ai/dist/utils/typebox-helpers.js +21 -0
  197. package/packages/pi-ai/dist/utils/typebox-helpers.js.map +1 -0
  198. package/packages/pi-ai/dist/utils/validation.d.ts +18 -0
  199. package/packages/pi-ai/dist/utils/validation.d.ts.map +1 -0
  200. package/packages/pi-ai/dist/utils/validation.js +72 -0
  201. package/packages/pi-ai/dist/utils/validation.js.map +1 -0
  202. package/packages/pi-ai/package.json +40 -0
  203. package/packages/pi-ai/src/api-registry.ts +98 -0
  204. package/packages/pi-ai/src/bedrock-provider.ts +6 -0
  205. package/packages/pi-ai/src/cli.ts +133 -0
  206. package/packages/pi-ai/src/env-api-keys.ts +129 -0
  207. package/packages/pi-ai/src/index.ts +32 -0
  208. package/packages/pi-ai/src/models.generated.ts +13370 -0
  209. package/packages/pi-ai/src/models.ts +77 -0
  210. package/packages/pi-ai/src/oauth.ts +1 -0
  211. package/packages/pi-ai/src/providers/amazon-bedrock.ts +751 -0
  212. package/packages/pi-ai/src/providers/anthropic.ts +883 -0
  213. package/packages/pi-ai/src/providers/azure-openai-responses.ts +259 -0
  214. package/packages/pi-ai/src/providers/github-copilot-headers.ts +37 -0
  215. package/packages/pi-ai/src/providers/google-gemini-cli.ts +967 -0
  216. package/packages/pi-ai/src/providers/google-shared.ts +313 -0
  217. package/packages/pi-ai/src/providers/google-vertex.ts +485 -0
  218. package/packages/pi-ai/src/providers/google.ts +455 -0
  219. package/packages/pi-ai/src/providers/mistral.ts +582 -0
  220. package/packages/pi-ai/src/providers/openai-codex-responses.ts +875 -0
  221. package/packages/pi-ai/src/providers/openai-completions.ts +820 -0
  222. package/packages/pi-ai/src/providers/openai-responses-shared.ts +496 -0
  223. package/packages/pi-ai/src/providers/openai-responses.ts +262 -0
  224. package/packages/pi-ai/src/providers/register-builtins.ts +186 -0
  225. package/packages/pi-ai/src/providers/simple-options.ts +46 -0
  226. package/packages/pi-ai/src/providers/transform-messages.ts +172 -0
  227. package/packages/pi-ai/src/stream.ts +59 -0
  228. package/packages/pi-ai/src/types.ts +321 -0
  229. package/packages/pi-ai/src/utils/event-stream.ts +87 -0
  230. package/packages/pi-ai/src/utils/hash.ts +13 -0
  231. package/packages/pi-ai/src/utils/json-parse.ts +28 -0
  232. package/packages/pi-ai/src/utils/oauth/anthropic.ts +138 -0
  233. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +381 -0
  234. package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +457 -0
  235. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +599 -0
  236. package/packages/pi-ai/src/utils/oauth/index.ts +162 -0
  237. package/packages/pi-ai/src/utils/oauth/openai-codex.ts +455 -0
  238. package/packages/pi-ai/src/utils/oauth/pkce.ts +34 -0
  239. package/packages/pi-ai/src/utils/oauth/types.ts +59 -0
  240. package/packages/pi-ai/src/utils/overflow.ts +123 -0
  241. package/packages/pi-ai/src/utils/sanitize-unicode.ts +25 -0
  242. package/packages/pi-ai/src/utils/typebox-helpers.ts +24 -0
  243. package/packages/pi-ai/src/utils/validation.ts +84 -0
  244. package/packages/pi-ai/tsconfig.json +27 -0
  245. package/packages/pi-coding-agent/dist/cli/args.d.ts +48 -0
  246. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -0
  247. package/packages/pi-coding-agent/dist/cli/args.js +299 -0
  248. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -0
  249. package/packages/pi-coding-agent/dist/cli/config-selector.d.ts +14 -0
  250. package/packages/pi-coding-agent/dist/cli/config-selector.d.ts.map +1 -0
  251. package/packages/pi-coding-agent/dist/cli/config-selector.js +31 -0
  252. package/packages/pi-coding-agent/dist/cli/config-selector.js.map +1 -0
  253. package/packages/pi-coding-agent/dist/cli/file-processor.d.ts +15 -0
  254. package/packages/pi-coding-agent/dist/cli/file-processor.d.ts.map +1 -0
  255. package/packages/pi-coding-agent/dist/cli/file-processor.js +79 -0
  256. package/packages/pi-coding-agent/dist/cli/file-processor.js.map +1 -0
  257. package/packages/pi-coding-agent/dist/cli/list-models.d.ts +9 -0
  258. package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -0
  259. package/packages/pi-coding-agent/dist/cli/list-models.js +92 -0
  260. package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -0
  261. package/packages/pi-coding-agent/dist/cli/session-picker.d.ts +9 -0
  262. package/packages/pi-coding-agent/dist/cli/session-picker.d.ts.map +1 -0
  263. package/packages/pi-coding-agent/dist/cli/session-picker.js +34 -0
  264. package/packages/pi-coding-agent/dist/cli/session-picker.js.map +1 -0
  265. package/packages/pi-coding-agent/dist/cli.d.ts +3 -0
  266. package/packages/pi-coding-agent/dist/cli.d.ts.map +1 -0
  267. package/packages/pi-coding-agent/dist/cli.js +16 -0
  268. package/packages/pi-coding-agent/dist/cli.js.map +1 -0
  269. package/packages/pi-coding-agent/dist/config.d.ts +68 -0
  270. package/packages/pi-coding-agent/dist/config.d.ts.map +1 -0
  271. package/packages/pi-coding-agent/dist/config.js +203 -0
  272. package/packages/pi-coding-agent/dist/config.js.map +1 -0
  273. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +618 -0
  274. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -0
  275. package/packages/pi-coding-agent/dist/core/agent-session.js +2481 -0
  276. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -0
  277. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +134 -0
  278. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -0
  279. package/packages/pi-coding-agent/dist/core/auth-storage.js +415 -0
  280. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -0
  281. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts +47 -0
  282. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -0
  283. package/packages/pi-coding-agent/dist/core/bash-executor.js +212 -0
  284. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -0
  285. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +86 -0
  286. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  287. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js +242 -0
  288. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -0
  289. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +121 -0
  290. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -0
  291. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +610 -0
  292. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -0
  293. package/packages/pi-coding-agent/dist/core/compaction/index.d.ts +7 -0
  294. package/packages/pi-coding-agent/dist/core/compaction/index.d.ts.map +1 -0
  295. package/packages/pi-coding-agent/dist/core/compaction/index.js +7 -0
  296. package/packages/pi-coding-agent/dist/core/compaction/index.js.map +1 -0
  297. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +38 -0
  298. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -0
  299. package/packages/pi-coding-agent/dist/core/compaction/utils.js +153 -0
  300. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -0
  301. package/packages/pi-coding-agent/dist/core/defaults.d.ts +3 -0
  302. package/packages/pi-coding-agent/dist/core/defaults.d.ts.map +1 -0
  303. package/packages/pi-coding-agent/dist/core/defaults.js +2 -0
  304. package/packages/pi-coding-agent/dist/core/defaults.js.map +1 -0
  305. package/packages/pi-coding-agent/dist/core/diagnostics.d.ts +15 -0
  306. package/packages/pi-coding-agent/dist/core/diagnostics.d.ts.map +1 -0
  307. package/packages/pi-coding-agent/dist/core/diagnostics.js +2 -0
  308. package/packages/pi-coding-agent/dist/core/diagnostics.js.map +1 -0
  309. package/packages/pi-coding-agent/dist/core/event-bus.d.ts +9 -0
  310. package/packages/pi-coding-agent/dist/core/event-bus.d.ts.map +1 -0
  311. package/packages/pi-coding-agent/dist/core/event-bus.js +25 -0
  312. package/packages/pi-coding-agent/dist/core/event-bus.js.map +1 -0
  313. package/packages/pi-coding-agent/dist/core/exec.d.ts +29 -0
  314. package/packages/pi-coding-agent/dist/core/exec.d.ts.map +1 -0
  315. package/packages/pi-coding-agent/dist/core/exec.js +71 -0
  316. package/packages/pi-coding-agent/dist/core/exec.js.map +1 -0
  317. package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.d.ts +22 -0
  318. package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  319. package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.js +249 -0
  320. package/packages/pi-coding-agent/dist/core/export-html/ansi-to-html.js.map +1 -0
  321. package/packages/pi-coding-agent/dist/core/export-html/index.d.ts +37 -0
  322. package/packages/pi-coding-agent/dist/core/export-html/index.d.ts.map +1 -0
  323. package/packages/pi-coding-agent/dist/core/export-html/index.js +223 -0
  324. package/packages/pi-coding-agent/dist/core/export-html/index.js.map +1 -0
  325. package/packages/pi-coding-agent/dist/core/export-html/template.css +971 -0
  326. package/packages/pi-coding-agent/dist/core/export-html/template.html +54 -0
  327. package/packages/pi-coding-agent/dist/core/export-html/template.js +1583 -0
  328. package/packages/pi-coding-agent/dist/core/export-html/tool-renderer.d.ts +38 -0
  329. package/packages/pi-coding-agent/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  330. package/packages/pi-coding-agent/dist/core/export-html/tool-renderer.js +70 -0
  331. package/packages/pi-coding-agent/dist/core/export-html/tool-renderer.js.map +1 -0
  332. package/packages/pi-coding-agent/dist/core/export-html/vendor/highlight.min.js +1213 -0
  333. package/packages/pi-coding-agent/dist/core/export-html/vendor/marked.min.js +6 -0
  334. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +11 -0
  335. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -0
  336. package/packages/pi-coding-agent/dist/core/extensions/index.js +9 -0
  337. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -0
  338. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +25 -0
  339. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/extensions/loader.js +426 -0
  341. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +147 -0
  343. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/extensions/runner.js +672 -0
  345. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +1040 -0
  347. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -0
  348. package/packages/pi-coding-agent/dist/core/extensions/types.js +35 -0
  349. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -0
  350. package/packages/pi-coding-agent/dist/core/extensions/wrapper.d.ts +27 -0
  351. package/packages/pi-coding-agent/dist/core/extensions/wrapper.d.ts.map +1 -0
  352. package/packages/pi-coding-agent/dist/core/extensions/wrapper.js +102 -0
  353. package/packages/pi-coding-agent/dist/core/extensions/wrapper.js.map +1 -0
  354. package/packages/pi-coding-agent/dist/core/footer-data-provider.d.ts +32 -0
  355. package/packages/pi-coding-agent/dist/core/footer-data-provider.d.ts.map +1 -0
  356. package/packages/pi-coding-agent/dist/core/footer-data-provider.js +134 -0
  357. package/packages/pi-coding-agent/dist/core/footer-data-provider.js.map +1 -0
  358. package/packages/pi-coding-agent/dist/core/index.d.ts +9 -0
  359. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -0
  360. package/packages/pi-coding-agent/dist/core/index.js +9 -0
  361. package/packages/pi-coding-agent/dist/core/index.js.map +1 -0
  362. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +55 -0
  363. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -0
  364. package/packages/pi-coding-agent/dist/core/keybindings.js +151 -0
  365. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -0
  366. package/packages/pi-coding-agent/dist/core/messages.d.ts +77 -0
  367. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -0
  368. package/packages/pi-coding-agent/dist/core/messages.js +123 -0
  369. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -0
  370. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +113 -0
  371. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -0
  372. package/packages/pi-coding-agent/dist/core/model-registry.js +537 -0
  373. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -0
  374. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts +104 -0
  375. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -0
  376. package/packages/pi-coding-agent/dist/core/model-resolver.js +462 -0
  377. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -0
  378. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +156 -0
  379. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -0
  380. package/packages/pi-coding-agent/dist/core/package-manager.js +1442 -0
  381. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -0
  382. package/packages/pi-coding-agent/dist/core/prompt-templates.d.ts +50 -0
  383. package/packages/pi-coding-agent/dist/core/prompt-templates.d.ts.map +1 -0
  384. package/packages/pi-coding-agent/dist/core/prompt-templates.js +251 -0
  385. package/packages/pi-coding-agent/dist/core/prompt-templates.js.map +1 -0
  386. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +17 -0
  387. package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -0
  388. package/packages/pi-coding-agent/dist/core/resolve-config-value.js +59 -0
  389. package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -0
  390. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +184 -0
  391. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -0
  392. package/packages/pi-coding-agent/dist/core/resource-loader.js +633 -0
  393. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -0
  394. package/packages/pi-coding-agent/dist/core/sdk.d.ts +90 -0
  395. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -0
  396. package/packages/pi-coding-agent/dist/core/sdk.js +242 -0
  397. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -0
  398. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +323 -0
  399. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -0
  400. package/packages/pi-coding-agent/dist/core/session-manager.js +1094 -0
  401. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -0
  402. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +232 -0
  403. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -0
  404. package/packages/pi-coding-agent/dist/core/settings-manager.js +682 -0
  405. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -0
  406. package/packages/pi-coding-agent/dist/core/skills.d.ts +58 -0
  407. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -0
  408. package/packages/pi-coding-agent/dist/core/skills.js +364 -0
  409. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -0
  410. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts +15 -0
  411. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -0
  412. package/packages/pi-coding-agent/dist/core/slash-commands.js +22 -0
  413. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -0
  414. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +28 -0
  415. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -0
  416. package/packages/pi-coding-agent/dist/core/system-prompt.js +159 -0
  417. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -0
  418. package/packages/pi-coding-agent/dist/core/timings.d.ts +7 -0
  419. package/packages/pi-coding-agent/dist/core/timings.d.ts.map +1 -0
  420. package/packages/pi-coding-agent/dist/core/timings.js +25 -0
  421. package/packages/pi-coding-agent/dist/core/timings.js.map +1 -0
  422. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +55 -0
  423. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -0
  424. package/packages/pi-coding-agent/dist/core/tools/bash.js +269 -0
  425. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -0
  426. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +63 -0
  427. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -0
  428. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +243 -0
  429. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -0
  430. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts +39 -0
  431. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -0
  432. package/packages/pi-coding-agent/dist/core/tools/edit.js +146 -0
  433. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -0
  434. package/packages/pi-coding-agent/dist/core/tools/find.d.ts +39 -0
  435. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -0
  436. package/packages/pi-coding-agent/dist/core/tools/find.js +206 -0
  437. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -0
  438. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts +45 -0
  439. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -0
  440. package/packages/pi-coding-agent/dist/core/tools/grep.js +239 -0
  441. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -0
  442. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +73 -0
  443. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -0
  444. package/packages/pi-coding-agent/dist/core/tools/index.js +61 -0
  445. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -0
  446. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts +40 -0
  447. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -0
  448. package/packages/pi-coding-agent/dist/core/tools/ls.js +118 -0
  449. package/packages/pi-coding-agent/dist/core/tools/ls.js.map +1 -0
  450. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts +8 -0
  451. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -0
  452. package/packages/pi-coding-agent/dist/core/tools/path-utils.js +81 -0
  453. package/packages/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -0
  454. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +39 -0
  455. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -0
  456. package/packages/pi-coding-agent/dist/core/tools/read.js +166 -0
  457. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -0
  458. package/packages/pi-coding-agent/dist/core/tools/truncate.d.ts +70 -0
  459. package/packages/pi-coding-agent/dist/core/tools/truncate.d.ts.map +1 -0
  460. package/packages/pi-coding-agent/dist/core/tools/truncate.js +205 -0
  461. package/packages/pi-coding-agent/dist/core/tools/truncate.js.map +1 -0
  462. package/packages/pi-coding-agent/dist/core/tools/write.d.ts +29 -0
  463. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -0
  464. package/packages/pi-coding-agent/dist/core/tools/write.js +78 -0
  465. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -0
  466. package/packages/pi-coding-agent/dist/index.d.ts +27 -0
  467. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -0
  468. package/packages/pi-coding-agent/dist/index.js +42 -0
  469. package/packages/pi-coding-agent/dist/index.js.map +1 -0
  470. package/packages/pi-coding-agent/dist/main.d.ts +8 -0
  471. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -0
  472. package/packages/pi-coding-agent/dist/main.js +691 -0
  473. package/packages/pi-coding-agent/dist/main.js.map +1 -0
  474. package/packages/pi-coding-agent/dist/migrations.d.ts +33 -0
  475. package/packages/pi-coding-agent/dist/migrations.d.ts.map +1 -0
  476. package/packages/pi-coding-agent/dist/migrations.js +261 -0
  477. package/packages/pi-coding-agent/dist/migrations.js.map +1 -0
  478. package/packages/pi-coding-agent/dist/modes/index.d.ts +9 -0
  479. package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -0
  480. package/packages/pi-coding-agent/dist/modes/index.js +8 -0
  481. package/packages/pi-coding-agent/dist/modes/index.js.map +1 -0
  482. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.d.ts +34 -0
  483. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.d.ts.map +1 -0
  484. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.js +329 -0
  485. package/packages/pi-coding-agent/dist/modes/interactive/components/armin.js.map +1 -0
  486. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +16 -0
  487. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
  488. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +92 -0
  489. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -0
  490. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +35 -0
  491. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
  492. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +156 -0
  493. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -0
  494. package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
  495. package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  496. package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.js +48 -0
  497. package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  498. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  499. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  500. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +42 -0
  501. package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  502. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  503. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  504. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +43 -0
  505. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  506. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts +71 -0
  507. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
  508. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.js +470 -0
  509. package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -0
  510. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  511. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  512. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.js +28 -0
  513. package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  514. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  515. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
  516. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.js +63 -0
  517. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.js.map +1 -0
  518. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.d.ts +20 -0
  519. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  520. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.js +74 -0
  521. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.js.map +1 -0
  522. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  523. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
  524. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.js +138 -0
  525. package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.js.map +1 -0
  526. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +12 -0
  527. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -0
  528. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +133 -0
  529. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -0
  530. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  531. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
  532. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +20 -0
  533. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -0
  534. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts +20 -0
  535. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  536. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +106 -0
  537. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -0
  538. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +23 -0
  539. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  540. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +55 -0
  541. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -0
  542. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.d.ts +24 -0
  543. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  544. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js +71 -0
  545. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js.map +1 -0
  546. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +26 -0
  547. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -0
  548. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +196 -0
  549. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -0
  550. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +32 -0
  551. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -0
  552. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +33 -0
  553. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -0
  554. package/packages/pi-coding-agent/dist/modes/interactive/components/keybinding-hints.d.ts +41 -0
  555. package/packages/pi-coding-agent/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  556. package/packages/pi-coding-agent/dist/modes/interactive/components/keybinding-hints.js +61 -0
  557. package/packages/pi-coding-agent/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  558. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +42 -0
  559. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
  560. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +139 -0
  561. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -0
  562. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +47 -0
  563. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
  564. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +259 -0
  565. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -0
  566. package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.d.ts +19 -0
  567. package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
  568. package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.js +92 -0
  569. package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.js.map +1 -0
  570. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts +49 -0
  571. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
  572. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +271 -0
  573. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
  574. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  575. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  576. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.js +155 -0
  577. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  578. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts +95 -0
  579. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
  580. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +825 -0
  581. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -0
  582. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +58 -0
  583. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  584. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +295 -0
  585. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -0
  586. package/packages/pi-coding-agent/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  587. package/packages/pi-coding-agent/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
  588. package/packages/pi-coding-agent/dist/modes/interactive/components/show-images-selector.js +34 -0
  589. package/packages/pi-coding-agent/dist/modes/interactive/components/show-images-selector.js.map +1 -0
  590. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  591. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
  592. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +45 -0
  593. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
  594. package/packages/pi-coding-agent/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  595. package/packages/pi-coding-agent/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
  596. package/packages/pi-coding-agent/dist/modes/interactive/components/theme-selector.js +44 -0
  597. package/packages/pi-coding-agent/dist/modes/interactive/components/theme-selector.js.map +1 -0
  598. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  599. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
  600. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js +46 -0
  601. package/packages/pi-coding-agent/dist/modes/interactive/components/thinking-selector.js.map +1 -0
  602. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +77 -0
  603. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
  604. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +773 -0
  605. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -0
  606. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.d.ts +87 -0
  607. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  608. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js +1026 -0
  609. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js.map +1 -0
  610. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  611. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
  612. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.js +110 -0
  613. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.js.map +1 -0
  614. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +9 -0
  615. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -0
  616. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +27 -0
  617. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -0
  618. package/packages/pi-coding-agent/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  619. package/packages/pi-coding-agent/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
  620. package/packages/pi-coding-agent/dist/modes/interactive/components/visual-truncate.js +33 -0
  621. package/packages/pi-coding-agent/dist/modes/interactive/components/visual-truncate.js.map +1 -0
  622. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +330 -0
  623. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
  624. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3755 -0
  625. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -0
  626. package/packages/pi-coding-agent/dist/modes/interactive/theme/dark.json +85 -0
  627. package/packages/pi-coding-agent/dist/modes/interactive/theme/light.json +84 -0
  628. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.json +335 -0
  629. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +78 -0
  630. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  631. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +944 -0
  632. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -0
  633. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts +28 -0
  634. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -0
  635. package/packages/pi-coding-agent/dist/modes/print-mode.js +101 -0
  636. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -0
  637. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts +17 -0
  638. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -0
  639. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +49 -0
  640. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -0
  641. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +217 -0
  642. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -0
  643. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +400 -0
  644. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -0
  645. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +20 -0
  646. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  647. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +509 -0
  648. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -0
  649. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +409 -0
  650. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -0
  651. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js +8 -0
  652. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -0
  653. package/packages/pi-coding-agent/dist/utils/changelog.d.ts +21 -0
  654. package/packages/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -0
  655. package/packages/pi-coding-agent/dist/utils/changelog.js +87 -0
  656. package/packages/pi-coding-agent/dist/utils/changelog.js.map +1 -0
  657. package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts +11 -0
  658. package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts.map +1 -0
  659. package/packages/pi-coding-agent/dist/utils/clipboard-image.js +162 -0
  660. package/packages/pi-coding-agent/dist/utils/clipboard-image.js.map +1 -0
  661. package/packages/pi-coding-agent/dist/utils/clipboard-native.d.ts +7 -0
  662. package/packages/pi-coding-agent/dist/utils/clipboard-native.d.ts.map +1 -0
  663. package/packages/pi-coding-agent/dist/utils/clipboard-native.js +14 -0
  664. package/packages/pi-coding-agent/dist/utils/clipboard-native.js.map +1 -0
  665. package/packages/pi-coding-agent/dist/utils/clipboard.d.ts +2 -0
  666. package/packages/pi-coding-agent/dist/utils/clipboard.d.ts.map +1 -0
  667. package/packages/pi-coding-agent/dist/utils/clipboard.js +67 -0
  668. package/packages/pi-coding-agent/dist/utils/clipboard.js.map +1 -0
  669. package/packages/pi-coding-agent/dist/utils/frontmatter.d.ts +8 -0
  670. package/packages/pi-coding-agent/dist/utils/frontmatter.d.ts.map +1 -0
  671. package/packages/pi-coding-agent/dist/utils/frontmatter.js +26 -0
  672. package/packages/pi-coding-agent/dist/utils/frontmatter.js.map +1 -0
  673. package/packages/pi-coding-agent/dist/utils/git.d.ts +26 -0
  674. package/packages/pi-coding-agent/dist/utils/git.d.ts.map +1 -0
  675. package/packages/pi-coding-agent/dist/utils/git.js +163 -0
  676. package/packages/pi-coding-agent/dist/utils/git.js.map +1 -0
  677. package/packages/pi-coding-agent/dist/utils/image-convert.d.ts +9 -0
  678. package/packages/pi-coding-agent/dist/utils/image-convert.d.ts.map +1 -0
  679. package/packages/pi-coding-agent/dist/utils/image-convert.js +35 -0
  680. package/packages/pi-coding-agent/dist/utils/image-convert.js.map +1 -0
  681. package/packages/pi-coding-agent/dist/utils/image-resize.d.ts +36 -0
  682. package/packages/pi-coding-agent/dist/utils/image-resize.d.ts.map +1 -0
  683. package/packages/pi-coding-agent/dist/utils/image-resize.js +181 -0
  684. package/packages/pi-coding-agent/dist/utils/image-resize.js.map +1 -0
  685. package/packages/pi-coding-agent/dist/utils/mime.d.ts +2 -0
  686. package/packages/pi-coding-agent/dist/utils/mime.d.ts.map +1 -0
  687. package/packages/pi-coding-agent/dist/utils/mime.js +26 -0
  688. package/packages/pi-coding-agent/dist/utils/mime.js.map +1 -0
  689. package/packages/pi-coding-agent/dist/utils/photon.d.ts +21 -0
  690. package/packages/pi-coding-agent/dist/utils/photon.d.ts.map +1 -0
  691. package/packages/pi-coding-agent/dist/utils/photon.js +121 -0
  692. package/packages/pi-coding-agent/dist/utils/photon.js.map +1 -0
  693. package/packages/pi-coding-agent/dist/utils/shell.d.ts +26 -0
  694. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -0
  695. package/packages/pi-coding-agent/dist/utils/shell.js +186 -0
  696. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -0
  697. package/packages/pi-coding-agent/dist/utils/sleep.d.ts +5 -0
  698. package/packages/pi-coding-agent/dist/utils/sleep.d.ts.map +1 -0
  699. package/packages/pi-coding-agent/dist/utils/sleep.js +17 -0
  700. package/packages/pi-coding-agent/dist/utils/sleep.js.map +1 -0
  701. package/packages/pi-coding-agent/dist/utils/tools-manager.d.ts +3 -0
  702. package/packages/pi-coding-agent/dist/utils/tools-manager.d.ts.map +1 -0
  703. package/packages/pi-coding-agent/dist/utils/tools-manager.js +251 -0
  704. package/packages/pi-coding-agent/dist/utils/tools-manager.js.map +1 -0
  705. package/packages/pi-coding-agent/package.json +55 -0
  706. package/packages/pi-coding-agent/src/cli/args.ts +316 -0
  707. package/packages/pi-coding-agent/src/cli/config-selector.ts +52 -0
  708. package/packages/pi-coding-agent/src/cli/file-processor.ts +96 -0
  709. package/packages/pi-coding-agent/src/cli/list-models.ts +104 -0
  710. package/packages/pi-coding-agent/src/cli/session-picker.ts +51 -0
  711. package/packages/pi-coding-agent/src/cli.ts +18 -0
  712. package/packages/pi-coding-agent/src/config.ts +241 -0
  713. package/packages/pi-coding-agent/src/core/agent-session.ts +3050 -0
  714. package/packages/pi-coding-agent/src/core/auth-storage.ts +489 -0
  715. package/packages/pi-coding-agent/src/core/bash-executor.ts +278 -0
  716. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +352 -0
  717. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +813 -0
  718. package/packages/pi-coding-agent/src/core/compaction/index.ts +7 -0
  719. package/packages/pi-coding-agent/src/core/compaction/utils.ts +170 -0
  720. package/packages/pi-coding-agent/src/core/defaults.ts +3 -0
  721. package/packages/pi-coding-agent/src/core/diagnostics.ts +15 -0
  722. package/packages/pi-coding-agent/src/core/event-bus.ts +33 -0
  723. package/packages/pi-coding-agent/src/core/exec.ts +104 -0
  724. package/packages/pi-coding-agent/src/core/export-html/ansi-to-html.ts +258 -0
  725. package/packages/pi-coding-agent/src/core/export-html/index.ts +306 -0
  726. package/packages/pi-coding-agent/src/core/export-html/template.css +971 -0
  727. package/packages/pi-coding-agent/src/core/export-html/template.html +54 -0
  728. package/packages/pi-coding-agent/src/core/export-html/template.js +1583 -0
  729. package/packages/pi-coding-agent/src/core/export-html/tool-renderer.ts +114 -0
  730. package/packages/pi-coding-agent/src/core/export-html/vendor/highlight.min.js +1213 -0
  731. package/packages/pi-coding-agent/src/core/export-html/vendor/marked.min.js +6 -0
  732. package/packages/pi-coding-agent/src/core/extensions/index.ts +171 -0
  733. package/packages/pi-coding-agent/src/core/extensions/loader.ts +545 -0
  734. package/packages/pi-coding-agent/src/core/extensions/runner.ts +884 -0
  735. package/packages/pi-coding-agent/src/core/extensions/types.ts +1411 -0
  736. package/packages/pi-coding-agent/src/core/extensions/wrapper.ts +118 -0
  737. package/packages/pi-coding-agent/src/core/footer-data-provider.ts +144 -0
  738. package/packages/pi-coding-agent/src/core/index.ts +61 -0
  739. package/packages/pi-coding-agent/src/core/keybindings.ts +211 -0
  740. package/packages/pi-coding-agent/src/core/messages.ts +195 -0
  741. package/packages/pi-coding-agent/src/core/model-registry.ts +694 -0
  742. package/packages/pi-coding-agent/src/core/model-resolver.ts +594 -0
  743. package/packages/pi-coding-agent/src/core/package-manager.ts +1794 -0
  744. package/packages/pi-coding-agent/src/core/prompt-templates.ts +299 -0
  745. package/packages/pi-coding-agent/src/core/resolve-config-value.ts +64 -0
  746. package/packages/pi-coding-agent/src/core/resource-loader.ts +868 -0
  747. package/packages/pi-coding-agent/src/core/sdk.ts +373 -0
  748. package/packages/pi-coding-agent/src/core/session-manager.ts +1410 -0
  749. package/packages/pi-coding-agent/src/core/settings-manager.ts +942 -0
  750. package/packages/pi-coding-agent/src/core/skills.ts +459 -0
  751. package/packages/pi-coding-agent/src/core/slash-commands.ts +38 -0
  752. package/packages/pi-coding-agent/src/core/system-prompt.ts +218 -0
  753. package/packages/pi-coding-agent/src/core/timings.ts +25 -0
  754. package/packages/pi-coding-agent/src/core/tools/bash.ts +347 -0
  755. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +308 -0
  756. package/packages/pi-coding-agent/src/core/tools/edit.ts +227 -0
  757. package/packages/pi-coding-agent/src/core/tools/find.ts +273 -0
  758. package/packages/pi-coding-agent/src/core/tools/grep.ts +346 -0
  759. package/packages/pi-coding-agent/src/core/tools/index.ts +139 -0
  760. package/packages/pi-coding-agent/src/core/tools/ls.ts +170 -0
  761. package/packages/pi-coding-agent/src/core/tools/path-utils.ts +94 -0
  762. package/packages/pi-coding-agent/src/core/tools/read.ts +222 -0
  763. package/packages/pi-coding-agent/src/core/tools/truncate.ts +265 -0
  764. package/packages/pi-coding-agent/src/core/tools/write.ts +118 -0
  765. package/packages/pi-coding-agent/src/index.ts +333 -0
  766. package/packages/pi-coding-agent/src/main.ts +821 -0
  767. package/packages/pi-coding-agent/src/migrations.ts +295 -0
  768. package/packages/pi-coding-agent/src/modes/index.ts +9 -0
  769. package/packages/pi-coding-agent/src/modes/interactive/components/armin.ts +382 -0
  770. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +115 -0
  771. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +210 -0
  772. package/packages/pi-coding-agent/src/modes/interactive/components/bordered-loader.ts +66 -0
  773. package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +58 -0
  774. package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +59 -0
  775. package/packages/pi-coding-agent/src/modes/interactive/components/config-selector.ts +592 -0
  776. package/packages/pi-coding-agent/src/modes/interactive/components/countdown-timer.ts +38 -0
  777. package/packages/pi-coding-agent/src/modes/interactive/components/custom-editor.ts +80 -0
  778. package/packages/pi-coding-agent/src/modes/interactive/components/custom-message.ts +99 -0
  779. package/packages/pi-coding-agent/src/modes/interactive/components/daxnuts.ts +164 -0
  780. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +147 -0
  781. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +25 -0
  782. package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +147 -0
  783. package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +85 -0
  784. package/packages/pi-coding-agent/src/modes/interactive/components/extension-selector.ts +107 -0
  785. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +216 -0
  786. package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +32 -0
  787. package/packages/pi-coding-agent/src/modes/interactive/components/keybinding-hints.ts +66 -0
  788. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +174 -0
  789. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +329 -0
  790. package/packages/pi-coding-agent/src/modes/interactive/components/oauth-selector.ts +121 -0
  791. package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +346 -0
  792. package/packages/pi-coding-agent/src/modes/interactive/components/session-selector-search.ts +194 -0
  793. package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +1019 -0
  794. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +421 -0
  795. package/packages/pi-coding-agent/src/modes/interactive/components/show-images-selector.ts +45 -0
  796. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +55 -0
  797. package/packages/pi-coding-agent/src/modes/interactive/components/theme-selector.ts +62 -0
  798. package/packages/pi-coding-agent/src/modes/interactive/components/thinking-selector.ts +64 -0
  799. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +916 -0
  800. package/packages/pi-coding-agent/src/modes/interactive/components/tree-selector.ts +1184 -0
  801. package/packages/pi-coding-agent/src/modes/interactive/components/user-message-selector.ts +143 -0
  802. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +32 -0
  803. package/packages/pi-coding-agent/src/modes/interactive/components/visual-truncate.ts +50 -0
  804. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4464 -0
  805. package/packages/pi-coding-agent/src/modes/interactive/theme/dark.json +85 -0
  806. package/packages/pi-coding-agent/src/modes/interactive/theme/light.json +84 -0
  807. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json +335 -0
  808. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +1105 -0
  809. package/packages/pi-coding-agent/src/modes/print-mode.ts +124 -0
  810. package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +58 -0
  811. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +505 -0
  812. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +638 -0
  813. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +263 -0
  814. package/packages/pi-coding-agent/src/utils/changelog.ts +99 -0
  815. package/packages/pi-coding-agent/src/utils/clipboard-image.ts +207 -0
  816. package/packages/pi-coding-agent/src/utils/clipboard-native.ts +21 -0
  817. package/packages/pi-coding-agent/src/utils/clipboard.ts +62 -0
  818. package/packages/pi-coding-agent/src/utils/frontmatter.ts +39 -0
  819. package/packages/pi-coding-agent/src/utils/git.ts +192 -0
  820. package/packages/pi-coding-agent/src/utils/image-convert.ts +38 -0
  821. package/packages/pi-coding-agent/src/utils/image-resize.ts +231 -0
  822. package/packages/pi-coding-agent/src/utils/mime.ts +30 -0
  823. package/packages/pi-coding-agent/src/utils/photon.ts +139 -0
  824. package/packages/pi-coding-agent/src/utils/shell.ts +202 -0
  825. package/packages/pi-coding-agent/src/utils/sleep.ts +18 -0
  826. package/packages/pi-coding-agent/src/utils/tools-manager.ts +286 -0
  827. package/packages/pi-coding-agent/tsconfig.json +27 -0
  828. package/packages/pi-tui/dist/autocomplete.d.ts +50 -0
  829. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -0
  830. package/packages/pi-tui/dist/autocomplete.js +593 -0
  831. package/packages/pi-tui/dist/autocomplete.js.map +1 -0
  832. package/packages/pi-tui/dist/components/box.d.ts +22 -0
  833. package/packages/pi-tui/dist/components/box.d.ts.map +1 -0
  834. package/packages/pi-tui/dist/components/box.js +99 -0
  835. package/packages/pi-tui/dist/components/box.js.map +1 -0
  836. package/packages/pi-tui/dist/components/cancellable-loader.d.ts +22 -0
  837. package/packages/pi-tui/dist/components/cancellable-loader.d.ts.map +1 -0
  838. package/packages/pi-tui/dist/components/cancellable-loader.js +36 -0
  839. package/packages/pi-tui/dist/components/cancellable-loader.js.map +1 -0
  840. package/packages/pi-tui/dist/components/editor.d.ts +219 -0
  841. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -0
  842. package/packages/pi-tui/dist/components/editor.js +1693 -0
  843. package/packages/pi-tui/dist/components/editor.js.map +1 -0
  844. package/packages/pi-tui/dist/components/image.d.ts +28 -0
  845. package/packages/pi-tui/dist/components/image.d.ts.map +1 -0
  846. package/packages/pi-tui/dist/components/image.js +61 -0
  847. package/packages/pi-tui/dist/components/image.js.map +1 -0
  848. package/packages/pi-tui/dist/components/input.d.ts +37 -0
  849. package/packages/pi-tui/dist/components/input.d.ts.map +1 -0
  850. package/packages/pi-tui/dist/components/input.js +443 -0
  851. package/packages/pi-tui/dist/components/input.js.map +1 -0
  852. package/packages/pi-tui/dist/components/loader.d.ts +21 -0
  853. package/packages/pi-tui/dist/components/loader.d.ts.map +1 -0
  854. package/packages/pi-tui/dist/components/loader.js +46 -0
  855. package/packages/pi-tui/dist/components/loader.js.map +1 -0
  856. package/packages/pi-tui/dist/components/markdown.d.ts +95 -0
  857. package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -0
  858. package/packages/pi-tui/dist/components/markdown.js +641 -0
  859. package/packages/pi-tui/dist/components/markdown.js.map +1 -0
  860. package/packages/pi-tui/dist/components/select-list.d.ts +32 -0
  861. package/packages/pi-tui/dist/components/select-list.d.ts.map +1 -0
  862. package/packages/pi-tui/dist/components/select-list.js +148 -0
  863. package/packages/pi-tui/dist/components/select-list.js.map +1 -0
  864. package/packages/pi-tui/dist/components/settings-list.d.ts +50 -0
  865. package/packages/pi-tui/dist/components/settings-list.d.ts.map +1 -0
  866. package/packages/pi-tui/dist/components/settings-list.js +177 -0
  867. package/packages/pi-tui/dist/components/settings-list.js.map +1 -0
  868. package/packages/pi-tui/dist/components/spacer.d.ts +12 -0
  869. package/packages/pi-tui/dist/components/spacer.d.ts.map +1 -0
  870. package/packages/pi-tui/dist/components/spacer.js +22 -0
  871. package/packages/pi-tui/dist/components/spacer.js.map +1 -0
  872. package/packages/pi-tui/dist/components/text.d.ts +19 -0
  873. package/packages/pi-tui/dist/components/text.d.ts.map +1 -0
  874. package/packages/pi-tui/dist/components/text.js +81 -0
  875. package/packages/pi-tui/dist/components/text.js.map +1 -0
  876. package/packages/pi-tui/dist/components/truncated-text.d.ts +13 -0
  877. package/packages/pi-tui/dist/components/truncated-text.d.ts.map +1 -0
  878. package/packages/pi-tui/dist/components/truncated-text.js +48 -0
  879. package/packages/pi-tui/dist/components/truncated-text.js.map +1 -0
  880. package/packages/pi-tui/dist/editor-component.d.ts +39 -0
  881. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -0
  882. package/packages/pi-tui/dist/editor-component.js +2 -0
  883. package/packages/pi-tui/dist/editor-component.js.map +1 -0
  884. package/packages/pi-tui/dist/fuzzy.d.ts +16 -0
  885. package/packages/pi-tui/dist/fuzzy.d.ts.map +1 -0
  886. package/packages/pi-tui/dist/fuzzy.js +107 -0
  887. package/packages/pi-tui/dist/fuzzy.js.map +1 -0
  888. package/packages/pi-tui/dist/index.d.ts +23 -0
  889. package/packages/pi-tui/dist/index.d.ts.map +1 -0
  890. package/packages/pi-tui/dist/index.js +32 -0
  891. package/packages/pi-tui/dist/index.js.map +1 -0
  892. package/packages/pi-tui/dist/keybindings.d.ts +39 -0
  893. package/packages/pi-tui/dist/keybindings.d.ts.map +1 -0
  894. package/packages/pi-tui/dist/keybindings.js +116 -0
  895. package/packages/pi-tui/dist/keybindings.js.map +1 -0
  896. package/packages/pi-tui/dist/keys.d.ts +170 -0
  897. package/packages/pi-tui/dist/keys.d.ts.map +1 -0
  898. package/packages/pi-tui/dist/keys.js +1046 -0
  899. package/packages/pi-tui/dist/keys.js.map +1 -0
  900. package/packages/pi-tui/dist/kill-ring.d.ts +28 -0
  901. package/packages/pi-tui/dist/kill-ring.d.ts.map +1 -0
  902. package/packages/pi-tui/dist/kill-ring.js +46 -0
  903. package/packages/pi-tui/dist/kill-ring.js.map +1 -0
  904. package/packages/pi-tui/dist/stdin-buffer.d.ts +48 -0
  905. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -0
  906. package/packages/pi-tui/dist/stdin-buffer.js +316 -0
  907. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -0
  908. package/packages/pi-tui/dist/terminal-image.d.ts +68 -0
  909. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -0
  910. package/packages/pi-tui/dist/terminal-image.js +288 -0
  911. package/packages/pi-tui/dist/terminal-image.js.map +1 -0
  912. package/packages/pi-tui/dist/terminal.d.ts +85 -0
  913. package/packages/pi-tui/dist/terminal.d.ts.map +1 -0
  914. package/packages/pi-tui/dist/terminal.js +271 -0
  915. package/packages/pi-tui/dist/terminal.js.map +1 -0
  916. package/packages/pi-tui/dist/tui.d.ts +220 -0
  917. package/packages/pi-tui/dist/tui.d.ts.map +1 -0
  918. package/packages/pi-tui/dist/tui.js +976 -0
  919. package/packages/pi-tui/dist/tui.js.map +1 -0
  920. package/packages/pi-tui/dist/undo-stack.d.ts +17 -0
  921. package/packages/pi-tui/dist/undo-stack.d.ts.map +1 -0
  922. package/packages/pi-tui/dist/undo-stack.js +27 -0
  923. package/packages/pi-tui/dist/undo-stack.js.map +1 -0
  924. package/packages/pi-tui/dist/utils.d.ts +78 -0
  925. package/packages/pi-tui/dist/utils.d.ts.map +1 -0
  926. package/packages/pi-tui/dist/utils.js +817 -0
  927. package/packages/pi-tui/dist/utils.js.map +1 -0
  928. package/packages/pi-tui/package.json +21 -0
  929. package/packages/pi-tui/src/autocomplete.ts +736 -0
  930. package/packages/pi-tui/src/components/box.ts +137 -0
  931. package/packages/pi-tui/src/components/cancellable-loader.ts +40 -0
  932. package/packages/pi-tui/src/components/editor.ts +2035 -0
  933. package/packages/pi-tui/src/components/image.ts +104 -0
  934. package/packages/pi-tui/src/components/input.ts +521 -0
  935. package/packages/pi-tui/src/components/loader.ts +55 -0
  936. package/packages/pi-tui/src/components/markdown.ts +806 -0
  937. package/packages/pi-tui/src/components/select-list.ts +188 -0
  938. package/packages/pi-tui/src/components/settings-list.ts +250 -0
  939. package/packages/pi-tui/src/components/spacer.ts +28 -0
  940. package/packages/pi-tui/src/components/text.ts +106 -0
  941. package/packages/pi-tui/src/components/truncated-text.ts +65 -0
  942. package/packages/pi-tui/src/editor-component.ts +74 -0
  943. package/packages/pi-tui/src/fuzzy.ts +133 -0
  944. package/packages/pi-tui/src/index.ts +93 -0
  945. package/packages/pi-tui/src/keybindings.ts +189 -0
  946. package/packages/pi-tui/src/keys.ts +1255 -0
  947. package/packages/pi-tui/src/kill-ring.ts +46 -0
  948. package/packages/pi-tui/src/stdin-buffer.ts +386 -0
  949. package/packages/pi-tui/src/terminal-image.ts +381 -0
  950. package/packages/pi-tui/src/terminal.ts +349 -0
  951. package/packages/pi-tui/src/tui.ts +1212 -0
  952. package/packages/pi-tui/src/undo-stack.ts +28 -0
  953. package/packages/pi-tui/src/utils.ts +905 -0
  954. package/packages/pi-tui/tsconfig.json +27 -0
  955. package/pkg/dist/modes/interactive/theme/theme.d.ts +2 -2
  956. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  957. package/pkg/dist/modes/interactive/theme/theme.js +1 -6
  958. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  959. package/scripts/postinstall.js +1 -16
  960. package/src/resources/extensions/ask-user-questions.ts +2 -2
  961. package/src/resources/extensions/bg-shell/index.ts +4 -4
  962. package/src/resources/extensions/browser-tools/index.ts +3 -3
  963. package/src/resources/extensions/context7/index.ts +3 -3
  964. package/src/resources/extensions/get-secrets-from-user.ts +271 -54
  965. package/src/resources/extensions/google-search/index.ts +3 -3
  966. package/src/resources/extensions/gsd/activity-log.ts +1 -1
  967. package/src/resources/extensions/gsd/auto.ts +37 -9
  968. package/src/resources/extensions/gsd/commands.ts +2 -2
  969. package/src/resources/extensions/gsd/dashboard-overlay.ts +2 -2
  970. package/src/resources/extensions/gsd/doctor.ts +23 -4
  971. package/src/resources/extensions/gsd/files.ts +45 -1
  972. package/src/resources/extensions/gsd/git-service.ts +50 -9
  973. package/src/resources/extensions/gsd/guided-flow.ts +1 -1
  974. package/src/resources/extensions/gsd/index.ts +4 -4
  975. package/src/resources/extensions/gsd/metrics.ts +1 -1
  976. package/src/resources/extensions/gsd/migrate/command.ts +1 -1
  977. package/src/resources/extensions/gsd/preferences.ts +1 -1
  978. package/src/resources/extensions/gsd/session-forensics.ts +19 -6
  979. package/src/resources/extensions/gsd/skill-discovery.ts +1 -1
  980. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +196 -0
  981. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +469 -0
  982. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +170 -0
  983. package/src/resources/extensions/gsd/tests/git-service.test.ts +43 -0
  984. package/src/resources/extensions/gsd/tests/manifest-status.test.ts +283 -0
  985. package/src/resources/extensions/gsd/tests/parsers.test.ts +190 -0
  986. package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +2 -1
  987. package/src/resources/extensions/gsd/types.ts +7 -0
  988. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  989. package/src/resources/extensions/mac-tools/index.ts +2 -2
  990. package/src/resources/extensions/mcporter/index.ts +3 -3
  991. package/src/resources/extensions/remote-questions/remote-command.ts +3 -3
  992. package/src/resources/extensions/search-the-web/command-search-provider.ts +2 -2
  993. package/src/resources/extensions/search-the-web/index.ts +1 -1
  994. package/src/resources/extensions/search-the-web/provider.ts +1 -1
  995. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +3 -3
  996. package/src/resources/extensions/search-the-web/tool-llm-context.ts +4 -4
  997. package/src/resources/extensions/search-the-web/tool-search.ts +4 -4
  998. package/src/resources/extensions/shared/confirm-ui.ts +3 -3
  999. package/src/resources/extensions/shared/interview-ui.ts +3 -3
  1000. package/src/resources/extensions/shared/next-action-ui.ts +3 -3
  1001. package/src/resources/extensions/shared/progress-widget.ts +2 -2
  1002. package/src/resources/extensions/shared/thinking-widget.ts +3 -3
  1003. package/src/resources/extensions/shared/ui.ts +4 -4
  1004. package/src/resources/extensions/shared/wizard-ui.ts +4 -4
  1005. package/src/resources/extensions/slash-commands/audit.ts +1 -1
  1006. package/src/resources/extensions/slash-commands/clear.ts +1 -1
  1007. package/src/resources/extensions/slash-commands/create-extension.ts +2 -2
  1008. package/src/resources/extensions/slash-commands/create-slash-command.ts +2 -2
  1009. package/src/resources/extensions/slash-commands/index.ts +1 -1
  1010. package/src/resources/extensions/subagent/agents.ts +1 -1
  1011. package/src/resources/extensions/subagent/index.ts +5 -5
  1012. package/src/resources/extensions/voice/index.ts +3 -3
  1013. package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +0 -108
  1014. package/patches/@mariozechner+pi-tui+0.57.1.patch +0 -47
@@ -0,0 +1,2035 @@
1
+ import type { AutocompleteProvider, CombinedAutocompleteProvider } from "../autocomplete.js";
2
+ import { getEditorKeybindings } from "../keybindings.js";
3
+ import { decodeKittyPrintable, matchesKey } from "../keys.js";
4
+ import { KillRing } from "../kill-ring.js";
5
+ import { type Component, CURSOR_MARKER, type Focusable, type TUI } from "../tui.js";
6
+ import { UndoStack } from "../undo-stack.js";
7
+ import { getSegmenter, isPunctuationChar, isWhitespaceChar, visibleWidth } from "../utils.js";
8
+ import { SelectList, type SelectListTheme } from "./select-list.js";
9
+
10
+ const segmenter = getSegmenter();
11
+
12
+ /**
13
+ * Represents a chunk of text for word-wrap layout.
14
+ * Tracks both the text content and its position in the original line.
15
+ */
16
+ export interface TextChunk {
17
+ text: string;
18
+ startIndex: number;
19
+ endIndex: number;
20
+ }
21
+
22
+ /**
23
+ * Split a line into word-wrapped chunks.
24
+ * Wraps at word boundaries when possible, falling back to character-level
25
+ * wrapping for words longer than the available width.
26
+ *
27
+ * @param line - The text line to wrap
28
+ * @param maxWidth - Maximum visible width per chunk
29
+ * @returns Array of chunks with text and position information
30
+ */
31
+ export function wordWrapLine(line: string, maxWidth: number): TextChunk[] {
32
+ if (!line || maxWidth <= 0) {
33
+ return [{ text: "", startIndex: 0, endIndex: 0 }];
34
+ }
35
+
36
+ const lineWidth = visibleWidth(line);
37
+ if (lineWidth <= maxWidth) {
38
+ return [{ text: line, startIndex: 0, endIndex: line.length }];
39
+ }
40
+
41
+ const chunks: TextChunk[] = [];
42
+ const segments = [...segmenter.segment(line)];
43
+
44
+ let currentWidth = 0;
45
+ let chunkStart = 0;
46
+
47
+ // Wrap opportunity: the position after the last whitespace before a non-whitespace
48
+ // grapheme, i.e. where a line break is allowed.
49
+ let wrapOppIndex = -1;
50
+ let wrapOppWidth = 0;
51
+
52
+ for (let i = 0; i < segments.length; i++) {
53
+ const seg = segments[i]!;
54
+ const grapheme = seg.segment;
55
+ const gWidth = visibleWidth(grapheme);
56
+ const charIndex = seg.index;
57
+ const isWs = isWhitespaceChar(grapheme);
58
+
59
+ // Overflow check before advancing.
60
+ if (currentWidth + gWidth > maxWidth) {
61
+ if (wrapOppIndex >= 0) {
62
+ // Backtrack to last wrap opportunity.
63
+ chunks.push({ text: line.slice(chunkStart, wrapOppIndex), startIndex: chunkStart, endIndex: wrapOppIndex });
64
+ chunkStart = wrapOppIndex;
65
+ currentWidth -= wrapOppWidth;
66
+ } else if (chunkStart < charIndex) {
67
+ // No wrap opportunity: force-break at current position.
68
+ chunks.push({ text: line.slice(chunkStart, charIndex), startIndex: chunkStart, endIndex: charIndex });
69
+ chunkStart = charIndex;
70
+ currentWidth = 0;
71
+ }
72
+ wrapOppIndex = -1;
73
+ }
74
+
75
+ // Advance.
76
+ currentWidth += gWidth;
77
+
78
+ // Record wrap opportunity: whitespace followed by non-whitespace.
79
+ // Multiple spaces join (no break between them); the break point is
80
+ // after the last space before the next word.
81
+ const next = segments[i + 1];
82
+ if (isWs && next && !isWhitespaceChar(next.segment)) {
83
+ wrapOppIndex = next.index;
84
+ wrapOppWidth = currentWidth;
85
+ }
86
+ }
87
+
88
+ // Push final chunk.
89
+ chunks.push({ text: line.slice(chunkStart), startIndex: chunkStart, endIndex: line.length });
90
+
91
+ return chunks;
92
+ }
93
+
94
+ // Kitty CSI-u sequences for printable keys, including optional shifted/base codepoints.
95
+ interface EditorState {
96
+ lines: string[];
97
+ cursorLine: number;
98
+ cursorCol: number;
99
+ }
100
+
101
+ interface LayoutLine {
102
+ text: string;
103
+ hasCursor: boolean;
104
+ cursorPos?: number;
105
+ }
106
+
107
+ export interface EditorTheme {
108
+ borderColor: (str: string) => string;
109
+ selectList: SelectListTheme;
110
+ }
111
+
112
+ export interface EditorOptions {
113
+ paddingX?: number;
114
+ autocompleteMaxVisible?: number;
115
+ }
116
+
117
+ export class Editor implements Component, Focusable {
118
+ private state: EditorState = {
119
+ lines: [""],
120
+ cursorLine: 0,
121
+ cursorCol: 0,
122
+ };
123
+
124
+ /** Focusable interface - set by TUI when focus changes */
125
+ focused: boolean = false;
126
+
127
+ protected tui: TUI;
128
+ private theme: EditorTheme;
129
+ private paddingX: number = 0;
130
+
131
+ // Store last render width for cursor navigation
132
+ private lastWidth: number = 80;
133
+
134
+ // Vertical scrolling support
135
+ private scrollOffset: number = 0;
136
+
137
+ // Border color (can be changed dynamically)
138
+ public borderColor: (str: string) => string;
139
+
140
+ // Autocomplete support
141
+ private autocompleteProvider?: AutocompleteProvider;
142
+ private autocompleteList?: SelectList;
143
+ private autocompleteState: "regular" | "force" | null = null;
144
+ private autocompletePrefix: string = "";
145
+ private autocompleteMaxVisible: number = 5;
146
+
147
+ // Paste tracking for large pastes
148
+ private pastes: Map<number, string> = new Map();
149
+ private pasteCounter: number = 0;
150
+
151
+ // Bracketed paste mode buffering
152
+ private pasteBuffer: string = "";
153
+ private isInPaste: boolean = false;
154
+
155
+ // Prompt history for up/down navigation
156
+ private history: string[] = [];
157
+ private historyIndex: number = -1; // -1 = not browsing, 0 = most recent, 1 = older, etc.
158
+
159
+ // Kill ring for Emacs-style kill/yank operations
160
+ private killRing = new KillRing();
161
+ private lastAction: "kill" | "yank" | "type-word" | null = null;
162
+
163
+ // Character jump mode
164
+ private jumpMode: "forward" | "backward" | null = null;
165
+
166
+ // Preferred visual column for vertical cursor movement (sticky column)
167
+ private preferredVisualCol: number | null = null;
168
+
169
+ // Undo support
170
+ private undoStack = new UndoStack<EditorState>();
171
+
172
+ public onSubmit?: (text: string) => void;
173
+ public onChange?: (text: string) => void;
174
+ public disableSubmit: boolean = false;
175
+
176
+ constructor(tui: TUI, theme: EditorTheme, options: EditorOptions = {}) {
177
+ this.tui = tui;
178
+ this.theme = theme;
179
+ this.borderColor = theme.borderColor;
180
+ const paddingX = options.paddingX ?? 0;
181
+ this.paddingX = Number.isFinite(paddingX) ? Math.max(0, Math.floor(paddingX)) : 0;
182
+ const maxVisible = options.autocompleteMaxVisible ?? 5;
183
+ this.autocompleteMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;
184
+ }
185
+
186
+ getPaddingX(): number {
187
+ return this.paddingX;
188
+ }
189
+
190
+ setPaddingX(padding: number): void {
191
+ const newPadding = Number.isFinite(padding) ? Math.max(0, Math.floor(padding)) : 0;
192
+ if (this.paddingX !== newPadding) {
193
+ this.paddingX = newPadding;
194
+ this.tui.requestRender();
195
+ }
196
+ }
197
+
198
+ getAutocompleteMaxVisible(): number {
199
+ return this.autocompleteMaxVisible;
200
+ }
201
+
202
+ setAutocompleteMaxVisible(maxVisible: number): void {
203
+ const newMaxVisible = Number.isFinite(maxVisible) ? Math.max(3, Math.min(20, Math.floor(maxVisible))) : 5;
204
+ if (this.autocompleteMaxVisible !== newMaxVisible) {
205
+ this.autocompleteMaxVisible = newMaxVisible;
206
+ this.tui.requestRender();
207
+ }
208
+ }
209
+
210
+ setAutocompleteProvider(provider: AutocompleteProvider): void {
211
+ this.autocompleteProvider = provider;
212
+ }
213
+
214
+ /**
215
+ * Add a prompt to history for up/down arrow navigation.
216
+ * Called after successful submission.
217
+ */
218
+ addToHistory(text: string): void {
219
+ const trimmed = text.trim();
220
+ if (!trimmed) return;
221
+ // Don't add consecutive duplicates
222
+ if (this.history.length > 0 && this.history[0] === trimmed) return;
223
+ this.history.unshift(trimmed);
224
+ // Limit history size
225
+ if (this.history.length > 100) {
226
+ this.history.pop();
227
+ }
228
+ }
229
+
230
+ private isEditorEmpty(): boolean {
231
+ return this.state.lines.length === 1 && this.state.lines[0] === "";
232
+ }
233
+
234
+ private isOnFirstVisualLine(): boolean {
235
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
236
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
237
+ return currentVisualLine === 0;
238
+ }
239
+
240
+ private isOnLastVisualLine(): boolean {
241
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
242
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
243
+ return currentVisualLine === visualLines.length - 1;
244
+ }
245
+
246
+ private navigateHistory(direction: 1 | -1): void {
247
+ this.lastAction = null;
248
+ if (this.history.length === 0) return;
249
+
250
+ const newIndex = this.historyIndex - direction; // Up(-1) increases index, Down(1) decreases
251
+ if (newIndex < -1 || newIndex >= this.history.length) return;
252
+
253
+ // Capture state when first entering history browsing mode
254
+ if (this.historyIndex === -1 && newIndex >= 0) {
255
+ this.pushUndoSnapshot();
256
+ }
257
+
258
+ this.historyIndex = newIndex;
259
+
260
+ if (this.historyIndex === -1) {
261
+ // Returned to "current" state - clear editor
262
+ this.setTextInternal("");
263
+ } else {
264
+ this.setTextInternal(this.history[this.historyIndex] || "");
265
+ }
266
+ }
267
+
268
+ /** Internal setText that doesn't reset history state - used by navigateHistory */
269
+ private setTextInternal(text: string): void {
270
+ const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
271
+ this.state.lines = lines.length === 0 ? [""] : lines;
272
+ this.state.cursorLine = this.state.lines.length - 1;
273
+ this.setCursorCol(this.state.lines[this.state.cursorLine]?.length || 0);
274
+ // Reset scroll - render() will adjust to show cursor
275
+ this.scrollOffset = 0;
276
+
277
+ if (this.onChange) {
278
+ this.onChange(this.getText());
279
+ }
280
+ }
281
+
282
+ invalidate(): void {
283
+ // No cached state to invalidate currently
284
+ }
285
+
286
+ render(width: number): string[] {
287
+ const maxPadding = Math.max(0, Math.floor((width - 1) / 2));
288
+ const paddingX = Math.min(this.paddingX, maxPadding);
289
+ const contentWidth = Math.max(1, width - paddingX * 2);
290
+
291
+ // Layout width: with padding the cursor can overflow into it,
292
+ // without padding we reserve 1 column for the cursor.
293
+ const layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));
294
+
295
+ // Store for cursor navigation (must match wrapping width)
296
+ this.lastWidth = layoutWidth;
297
+
298
+ const horizontal = this.borderColor("─");
299
+
300
+ // Layout the text
301
+ const layoutLines = this.layoutText(layoutWidth);
302
+
303
+ // Calculate max visible lines: 30% of terminal height, minimum 5 lines
304
+ const terminalRows = this.tui.terminal.rows;
305
+ const maxVisibleLines = Math.max(5, Math.floor(terminalRows * 0.3));
306
+
307
+ // Find the cursor line index in layoutLines
308
+ let cursorLineIndex = layoutLines.findIndex((line) => line.hasCursor);
309
+ if (cursorLineIndex === -1) cursorLineIndex = 0;
310
+
311
+ // Adjust scroll offset to keep cursor visible
312
+ if (cursorLineIndex < this.scrollOffset) {
313
+ this.scrollOffset = cursorLineIndex;
314
+ } else if (cursorLineIndex >= this.scrollOffset + maxVisibleLines) {
315
+ this.scrollOffset = cursorLineIndex - maxVisibleLines + 1;
316
+ }
317
+
318
+ // Clamp scroll offset to valid range
319
+ const maxScrollOffset = Math.max(0, layoutLines.length - maxVisibleLines);
320
+ this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxScrollOffset));
321
+
322
+ // Get visible lines slice
323
+ const visibleLines = layoutLines.slice(this.scrollOffset, this.scrollOffset + maxVisibleLines);
324
+
325
+ const result: string[] = [];
326
+ const leftPadding = " ".repeat(paddingX);
327
+ const rightPadding = leftPadding;
328
+
329
+ // Render top border (with scroll indicator if scrolled down)
330
+ if (this.scrollOffset > 0) {
331
+ const indicator = `─── ↑ ${this.scrollOffset} more `;
332
+ const remaining = width - visibleWidth(indicator);
333
+ result.push(this.borderColor(indicator + "─".repeat(Math.max(0, remaining))));
334
+ } else {
335
+ result.push(horizontal.repeat(width));
336
+ }
337
+
338
+ // Render each visible layout line
339
+ // Emit hardware cursor marker only when focused and not showing autocomplete
340
+ const emitCursorMarker = this.focused && !this.autocompleteState;
341
+
342
+ for (const layoutLine of visibleLines) {
343
+ let displayText = layoutLine.text;
344
+ let lineVisibleWidth = visibleWidth(layoutLine.text);
345
+ let cursorInPadding = false;
346
+
347
+ // Add cursor if this line has it
348
+ if (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {
349
+ const before = displayText.slice(0, layoutLine.cursorPos);
350
+ const after = displayText.slice(layoutLine.cursorPos);
351
+
352
+ // Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)
353
+ const marker = emitCursorMarker ? CURSOR_MARKER : "";
354
+
355
+ if (after.length > 0) {
356
+ // Cursor is on a character (grapheme) - replace it with highlighted version
357
+ // Get the first grapheme from 'after'
358
+ const afterGraphemes = [...segmenter.segment(after)];
359
+ const firstGrapheme = afterGraphemes[0]?.segment || "";
360
+ const restAfter = after.slice(firstGrapheme.length);
361
+ const cursor = `\x1b[7m${firstGrapheme}\x1b[0m`;
362
+ displayText = before + marker + cursor + restAfter;
363
+ // lineVisibleWidth stays the same - we're replacing, not adding
364
+ } else {
365
+ // Cursor is at the end - add highlighted space
366
+ const cursor = "\x1b[7m \x1b[0m";
367
+ displayText = before + marker + cursor;
368
+ lineVisibleWidth = lineVisibleWidth + 1;
369
+ // If cursor overflows content width into the padding, flag it
370
+ if (lineVisibleWidth > contentWidth && paddingX > 0) {
371
+ cursorInPadding = true;
372
+ }
373
+ }
374
+ }
375
+
376
+ // Calculate padding based on actual visible width
377
+ const padding = " ".repeat(Math.max(0, contentWidth - lineVisibleWidth));
378
+ const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;
379
+
380
+ // Render the line (no side borders, just horizontal lines above and below)
381
+ result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);
382
+ }
383
+
384
+ // Render bottom border (with scroll indicator if more content below)
385
+ const linesBelow = layoutLines.length - (this.scrollOffset + visibleLines.length);
386
+ if (linesBelow > 0) {
387
+ const indicator = `─── ↓ ${linesBelow} more `;
388
+ const remaining = width - visibleWidth(indicator);
389
+ result.push(this.borderColor(indicator + "─".repeat(Math.max(0, remaining))));
390
+ } else {
391
+ result.push(horizontal.repeat(width));
392
+ }
393
+
394
+ // Add autocomplete list if active
395
+ if (this.autocompleteState && this.autocompleteList) {
396
+ const autocompleteResult = this.autocompleteList.render(contentWidth);
397
+ for (const line of autocompleteResult) {
398
+ const lineWidth = visibleWidth(line);
399
+ const linePadding = " ".repeat(Math.max(0, contentWidth - lineWidth));
400
+ result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);
401
+ }
402
+ }
403
+
404
+ return result;
405
+ }
406
+
407
+ handleInput(data: string): void {
408
+ const kb = getEditorKeybindings();
409
+
410
+ // Handle character jump mode (awaiting next character to jump to)
411
+ if (this.jumpMode !== null) {
412
+ // Cancel if the hotkey is pressed again
413
+ if (kb.matches(data, "jumpForward") || kb.matches(data, "jumpBackward")) {
414
+ this.jumpMode = null;
415
+ return;
416
+ }
417
+
418
+ if (data.charCodeAt(0) >= 32) {
419
+ // Printable character - perform the jump
420
+ const direction = this.jumpMode;
421
+ this.jumpMode = null;
422
+ this.jumpToChar(data, direction);
423
+ return;
424
+ }
425
+
426
+ // Control character - cancel and fall through to normal handling
427
+ this.jumpMode = null;
428
+ }
429
+
430
+ // Handle bracketed paste mode
431
+ if (data.includes("\x1b[200~")) {
432
+ this.isInPaste = true;
433
+ this.pasteBuffer = "";
434
+ data = data.replace("\x1b[200~", "");
435
+ }
436
+
437
+ if (this.isInPaste) {
438
+ this.pasteBuffer += data;
439
+ const endIndex = this.pasteBuffer.indexOf("\x1b[201~");
440
+ if (endIndex !== -1) {
441
+ const pasteContent = this.pasteBuffer.substring(0, endIndex);
442
+ if (pasteContent.length > 0) {
443
+ this.handlePaste(pasteContent);
444
+ }
445
+ this.isInPaste = false;
446
+ const remaining = this.pasteBuffer.substring(endIndex + 6);
447
+ this.pasteBuffer = "";
448
+ if (remaining.length > 0) {
449
+ this.handleInput(remaining);
450
+ }
451
+ return;
452
+ }
453
+ return;
454
+ }
455
+
456
+ // Ctrl+C - let parent handle (exit/clear)
457
+ if (kb.matches(data, "copy")) {
458
+ return;
459
+ }
460
+
461
+ // Undo
462
+ if (kb.matches(data, "undo")) {
463
+ this.undo();
464
+ return;
465
+ }
466
+
467
+ // Handle autocomplete mode
468
+ if (this.autocompleteState && this.autocompleteList) {
469
+ if (kb.matches(data, "selectCancel")) {
470
+ this.cancelAutocomplete();
471
+ return;
472
+ }
473
+
474
+ if (kb.matches(data, "selectUp") || kb.matches(data, "selectDown")) {
475
+ this.autocompleteList.handleInput(data);
476
+ return;
477
+ }
478
+
479
+ if (kb.matches(data, "tab")) {
480
+ const selected = this.autocompleteList.getSelectedItem();
481
+ if (selected && this.autocompleteProvider) {
482
+ const shouldChainSlashArgumentAutocomplete = this.shouldChainSlashArgumentAutocompleteOnTabSelection();
483
+
484
+ this.pushUndoSnapshot();
485
+ this.lastAction = null;
486
+ const result = this.autocompleteProvider.applyCompletion(
487
+ this.state.lines,
488
+ this.state.cursorLine,
489
+ this.state.cursorCol,
490
+ selected,
491
+ this.autocompletePrefix,
492
+ );
493
+ this.state.lines = result.lines;
494
+ this.state.cursorLine = result.cursorLine;
495
+ this.setCursorCol(result.cursorCol);
496
+ this.cancelAutocomplete();
497
+ if (this.onChange) this.onChange(this.getText());
498
+
499
+ if (shouldChainSlashArgumentAutocomplete && this.isBareCompletedSlashCommandAtCursor()) {
500
+ this.tryTriggerAutocomplete();
501
+ }
502
+ }
503
+ return;
504
+ }
505
+
506
+ if (kb.matches(data, "selectConfirm")) {
507
+ const selected = this.autocompleteList.getSelectedItem();
508
+ if (selected && this.autocompleteProvider) {
509
+ this.pushUndoSnapshot();
510
+ this.lastAction = null;
511
+ const result = this.autocompleteProvider.applyCompletion(
512
+ this.state.lines,
513
+ this.state.cursorLine,
514
+ this.state.cursorCol,
515
+ selected,
516
+ this.autocompletePrefix,
517
+ );
518
+ this.state.lines = result.lines;
519
+ this.state.cursorLine = result.cursorLine;
520
+ this.setCursorCol(result.cursorCol);
521
+
522
+ if (this.autocompletePrefix.startsWith("/")) {
523
+ this.cancelAutocomplete();
524
+ // Fall through to submit
525
+ } else {
526
+ this.cancelAutocomplete();
527
+ if (this.onChange) this.onChange(this.getText());
528
+ return;
529
+ }
530
+ }
531
+ }
532
+ }
533
+
534
+ // Tab - trigger completion
535
+ if (kb.matches(data, "tab") && !this.autocompleteState) {
536
+ this.handleTabCompletion();
537
+ return;
538
+ }
539
+
540
+ // Deletion actions
541
+ if (kb.matches(data, "deleteToLineEnd")) {
542
+ this.deleteToEndOfLine();
543
+ return;
544
+ }
545
+ if (kb.matches(data, "deleteToLineStart")) {
546
+ this.deleteToStartOfLine();
547
+ return;
548
+ }
549
+ if (kb.matches(data, "deleteWordBackward")) {
550
+ this.deleteWordBackwards();
551
+ return;
552
+ }
553
+ if (kb.matches(data, "deleteWordForward")) {
554
+ this.deleteWordForward();
555
+ return;
556
+ }
557
+ if (kb.matches(data, "deleteCharBackward") || matchesKey(data, "shift+backspace")) {
558
+ this.handleBackspace();
559
+ return;
560
+ }
561
+ if (kb.matches(data, "deleteCharForward") || matchesKey(data, "shift+delete")) {
562
+ this.handleForwardDelete();
563
+ return;
564
+ }
565
+
566
+ // Kill ring actions
567
+ if (kb.matches(data, "yank")) {
568
+ this.yank();
569
+ return;
570
+ }
571
+ if (kb.matches(data, "yankPop")) {
572
+ this.yankPop();
573
+ return;
574
+ }
575
+
576
+ // Cursor movement actions
577
+ if (kb.matches(data, "cursorLineStart")) {
578
+ this.moveToLineStart();
579
+ return;
580
+ }
581
+ if (kb.matches(data, "cursorLineEnd")) {
582
+ this.moveToLineEnd();
583
+ return;
584
+ }
585
+ if (kb.matches(data, "cursorWordLeft")) {
586
+ this.moveWordBackwards();
587
+ return;
588
+ }
589
+ if (kb.matches(data, "cursorWordRight")) {
590
+ this.moveWordForwards();
591
+ return;
592
+ }
593
+
594
+ // New line
595
+ if (
596
+ kb.matches(data, "newLine") ||
597
+ (data.charCodeAt(0) === 10 && data.length > 1) ||
598
+ data === "\x1b\r" ||
599
+ data === "\x1b[13;2~" ||
600
+ (data.length > 1 && data.includes("\x1b") && data.includes("\r")) ||
601
+ (data === "\n" && data.length === 1)
602
+ ) {
603
+ if (this.shouldSubmitOnBackslashEnter(data, kb)) {
604
+ this.handleBackspace();
605
+ this.submitValue();
606
+ return;
607
+ }
608
+ this.addNewLine();
609
+ return;
610
+ }
611
+
612
+ // Submit (Enter)
613
+ if (kb.matches(data, "submit")) {
614
+ if (this.disableSubmit) return;
615
+
616
+ // Workaround for terminals without Shift+Enter support:
617
+ // If char before cursor is \, delete it and insert newline instead of submitting.
618
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
619
+ if (this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === "\\") {
620
+ this.handleBackspace();
621
+ this.addNewLine();
622
+ return;
623
+ }
624
+
625
+ this.submitValue();
626
+ return;
627
+ }
628
+
629
+ // Arrow key navigation (with history support)
630
+ if (kb.matches(data, "cursorUp")) {
631
+ if (this.isEditorEmpty()) {
632
+ this.navigateHistory(-1);
633
+ } else if (this.historyIndex > -1 && this.isOnFirstVisualLine()) {
634
+ this.navigateHistory(-1);
635
+ } else if (this.isOnFirstVisualLine()) {
636
+ // Already at top - jump to start of line
637
+ this.moveToLineStart();
638
+ } else {
639
+ this.moveCursor(-1, 0);
640
+ }
641
+ return;
642
+ }
643
+ if (kb.matches(data, "cursorDown")) {
644
+ if (this.historyIndex > -1 && this.isOnLastVisualLine()) {
645
+ this.navigateHistory(1);
646
+ } else if (this.isOnLastVisualLine()) {
647
+ // Already at bottom - jump to end of line
648
+ this.moveToLineEnd();
649
+ } else {
650
+ this.moveCursor(1, 0);
651
+ }
652
+ return;
653
+ }
654
+ if (kb.matches(data, "cursorRight")) {
655
+ this.moveCursor(0, 1);
656
+ return;
657
+ }
658
+ if (kb.matches(data, "cursorLeft")) {
659
+ this.moveCursor(0, -1);
660
+ return;
661
+ }
662
+
663
+ // Page up/down - scroll by page and move cursor
664
+ if (kb.matches(data, "pageUp")) {
665
+ this.pageScroll(-1);
666
+ return;
667
+ }
668
+ if (kb.matches(data, "pageDown")) {
669
+ this.pageScroll(1);
670
+ return;
671
+ }
672
+
673
+ // Character jump mode triggers
674
+ if (kb.matches(data, "jumpForward")) {
675
+ this.jumpMode = "forward";
676
+ return;
677
+ }
678
+ if (kb.matches(data, "jumpBackward")) {
679
+ this.jumpMode = "backward";
680
+ return;
681
+ }
682
+
683
+ // Shift+Space - insert regular space
684
+ if (matchesKey(data, "shift+space")) {
685
+ this.insertCharacter(" ");
686
+ return;
687
+ }
688
+
689
+ const kittyPrintable = decodeKittyPrintable(data);
690
+ if (kittyPrintable !== undefined) {
691
+ this.insertCharacter(kittyPrintable);
692
+ return;
693
+ }
694
+
695
+ // Regular characters
696
+ if (data.charCodeAt(0) >= 32) {
697
+ this.insertCharacter(data);
698
+ }
699
+ }
700
+
701
+ private layoutText(contentWidth: number): LayoutLine[] {
702
+ const layoutLines: LayoutLine[] = [];
703
+
704
+ if (this.state.lines.length === 0 || (this.state.lines.length === 1 && this.state.lines[0] === "")) {
705
+ // Empty editor
706
+ layoutLines.push({
707
+ text: "",
708
+ hasCursor: true,
709
+ cursorPos: 0,
710
+ });
711
+ return layoutLines;
712
+ }
713
+
714
+ // Process each logical line
715
+ for (let i = 0; i < this.state.lines.length; i++) {
716
+ const line = this.state.lines[i] || "";
717
+ const isCurrentLine = i === this.state.cursorLine;
718
+ const lineVisibleWidth = visibleWidth(line);
719
+
720
+ if (lineVisibleWidth <= contentWidth) {
721
+ // Line fits in one layout line
722
+ if (isCurrentLine) {
723
+ layoutLines.push({
724
+ text: line,
725
+ hasCursor: true,
726
+ cursorPos: this.state.cursorCol,
727
+ });
728
+ } else {
729
+ layoutLines.push({
730
+ text: line,
731
+ hasCursor: false,
732
+ });
733
+ }
734
+ } else {
735
+ // Line needs wrapping - use word-aware wrapping
736
+ const chunks = wordWrapLine(line, contentWidth);
737
+
738
+ for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
739
+ const chunk = chunks[chunkIndex];
740
+ if (!chunk) continue;
741
+
742
+ const cursorPos = this.state.cursorCol;
743
+ const isLastChunk = chunkIndex === chunks.length - 1;
744
+
745
+ // Determine if cursor is in this chunk
746
+ // For word-wrapped chunks, we need to handle the case where
747
+ // cursor might be in trimmed whitespace at end of chunk
748
+ let hasCursorInChunk = false;
749
+ let adjustedCursorPos = 0;
750
+
751
+ if (isCurrentLine) {
752
+ if (isLastChunk) {
753
+ // Last chunk: cursor belongs here if >= startIndex
754
+ hasCursorInChunk = cursorPos >= chunk.startIndex;
755
+ adjustedCursorPos = cursorPos - chunk.startIndex;
756
+ } else {
757
+ // Non-last chunk: cursor belongs here if in range [startIndex, endIndex)
758
+ // But we need to handle the visual position in the trimmed text
759
+ hasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;
760
+ if (hasCursorInChunk) {
761
+ adjustedCursorPos = cursorPos - chunk.startIndex;
762
+ // Clamp to text length (in case cursor was in trimmed whitespace)
763
+ if (adjustedCursorPos > chunk.text.length) {
764
+ adjustedCursorPos = chunk.text.length;
765
+ }
766
+ }
767
+ }
768
+ }
769
+
770
+ if (hasCursorInChunk) {
771
+ layoutLines.push({
772
+ text: chunk.text,
773
+ hasCursor: true,
774
+ cursorPos: adjustedCursorPos,
775
+ });
776
+ } else {
777
+ layoutLines.push({
778
+ text: chunk.text,
779
+ hasCursor: false,
780
+ });
781
+ }
782
+ }
783
+ }
784
+ }
785
+
786
+ return layoutLines;
787
+ }
788
+
789
+ getText(): string {
790
+ return this.state.lines.join("\n");
791
+ }
792
+
793
+ /**
794
+ * Get text with paste markers expanded to their actual content.
795
+ * Use this when you need the full content (e.g., for external editor).
796
+ */
797
+ getExpandedText(): string {
798
+ let result = this.state.lines.join("\n");
799
+ for (const [pasteId, pasteContent] of this.pastes) {
800
+ const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g");
801
+ result = result.replace(markerRegex, pasteContent);
802
+ }
803
+ return result;
804
+ }
805
+
806
+ getLines(): string[] {
807
+ return [...this.state.lines];
808
+ }
809
+
810
+ getCursor(): { line: number; col: number } {
811
+ return { line: this.state.cursorLine, col: this.state.cursorCol };
812
+ }
813
+
814
+ setText(text: string): void {
815
+ this.lastAction = null;
816
+ this.historyIndex = -1; // Exit history browsing mode
817
+ // Push undo snapshot if content differs (makes programmatic changes undoable)
818
+ if (this.getText() !== text) {
819
+ this.pushUndoSnapshot();
820
+ }
821
+ this.setTextInternal(text);
822
+ }
823
+
824
+ /**
825
+ * Insert text at the current cursor position.
826
+ * Used for programmatic insertion (e.g., clipboard image markers).
827
+ * This is atomic for undo - single undo restores entire pre-insert state.
828
+ */
829
+ insertTextAtCursor(text: string): void {
830
+ if (!text) return;
831
+ this.pushUndoSnapshot();
832
+ this.lastAction = null;
833
+ this.historyIndex = -1;
834
+ this.insertTextAtCursorInternal(text);
835
+ }
836
+
837
+ /**
838
+ * Internal text insertion at cursor. Handles single and multi-line text.
839
+ * Does not push undo snapshots or trigger autocomplete - caller is responsible.
840
+ * Normalizes line endings and calls onChange once at the end.
841
+ */
842
+ private insertTextAtCursorInternal(text: string): void {
843
+ if (!text) return;
844
+
845
+ // Normalize line endings
846
+ const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
847
+ const insertedLines = normalized.split("\n");
848
+
849
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
850
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
851
+ const afterCursor = currentLine.slice(this.state.cursorCol);
852
+
853
+ if (insertedLines.length === 1) {
854
+ // Single line - insert at cursor position
855
+ this.state.lines[this.state.cursorLine] = beforeCursor + normalized + afterCursor;
856
+ this.setCursorCol(this.state.cursorCol + normalized.length);
857
+ } else {
858
+ // Multi-line insertion
859
+ this.state.lines = [
860
+ // All lines before current line
861
+ ...this.state.lines.slice(0, this.state.cursorLine),
862
+
863
+ // The first inserted line merged with text before cursor
864
+ beforeCursor + insertedLines[0],
865
+
866
+ // All middle inserted lines
867
+ ...insertedLines.slice(1, -1),
868
+
869
+ // The last inserted line with text after cursor
870
+ insertedLines[insertedLines.length - 1] + afterCursor,
871
+
872
+ // All lines after current line
873
+ ...this.state.lines.slice(this.state.cursorLine + 1),
874
+ ];
875
+
876
+ this.state.cursorLine += insertedLines.length - 1;
877
+ this.setCursorCol((insertedLines[insertedLines.length - 1] || "").length);
878
+ }
879
+
880
+ if (this.onChange) {
881
+ this.onChange(this.getText());
882
+ }
883
+ }
884
+
885
+ // All the editor methods from before...
886
+ private insertCharacter(char: string, skipUndoCoalescing?: boolean): void {
887
+ this.historyIndex = -1; // Exit history browsing mode
888
+
889
+ // Undo coalescing (fish-style):
890
+ // - Consecutive word chars coalesce into one undo unit
891
+ // - Space captures state before itself (so undo removes space+following word together)
892
+ // - Each space is separately undoable
893
+ // Skip coalescing when called from atomic operations (e.g., handlePaste)
894
+ if (!skipUndoCoalescing) {
895
+ if (isWhitespaceChar(char) || this.lastAction !== "type-word") {
896
+ this.pushUndoSnapshot();
897
+ }
898
+ this.lastAction = "type-word";
899
+ }
900
+
901
+ const line = this.state.lines[this.state.cursorLine] || "";
902
+
903
+ const before = line.slice(0, this.state.cursorCol);
904
+ const after = line.slice(this.state.cursorCol);
905
+
906
+ this.state.lines[this.state.cursorLine] = before + char + after;
907
+ this.setCursorCol(this.state.cursorCol + char.length);
908
+
909
+ if (this.onChange) {
910
+ this.onChange(this.getText());
911
+ }
912
+
913
+ // Check if we should trigger or update autocomplete
914
+ if (!this.autocompleteState) {
915
+ // Auto-trigger for "/" at the start of a line (slash commands)
916
+ if (char === "/" && this.isAtStartOfMessage()) {
917
+ this.tryTriggerAutocomplete();
918
+ }
919
+ // Auto-trigger for "@" file reference (fuzzy search)
920
+ else if (char === "@") {
921
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
922
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
923
+ // Only trigger if @ is after whitespace or at start of line
924
+ const charBeforeAt = textBeforeCursor[textBeforeCursor.length - 2];
925
+ if (textBeforeCursor.length === 1 || charBeforeAt === " " || charBeforeAt === "\t") {
926
+ this.tryTriggerAutocomplete();
927
+ }
928
+ }
929
+ // Also auto-trigger when typing letters in a slash command context
930
+ else if (/[a-zA-Z0-9.\-_]/.test(char)) {
931
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
932
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
933
+ // Check if we're in a slash command (with or without space for arguments)
934
+ if (this.isInSlashCommandContext(textBeforeCursor)) {
935
+ this.tryTriggerAutocomplete();
936
+ }
937
+ // Check if we're in an @ file reference context
938
+ else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
939
+ this.tryTriggerAutocomplete();
940
+ }
941
+ }
942
+ } else {
943
+ this.updateAutocomplete();
944
+ }
945
+ }
946
+
947
+ private handlePaste(pastedText: string): void {
948
+ this.historyIndex = -1; // Exit history browsing mode
949
+ this.lastAction = null;
950
+
951
+ this.pushUndoSnapshot();
952
+
953
+ // Clean the pasted text
954
+ const cleanText = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
955
+
956
+ // Convert tabs to spaces (4 spaces per tab)
957
+ const tabExpandedText = cleanText.replace(/\t/g, " ");
958
+
959
+ // Filter out non-printable characters except newlines
960
+ let filteredText = tabExpandedText
961
+ .split("")
962
+ .filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
963
+ .join("");
964
+
965
+ // If pasting a file path (starts with /, ~, or .) and the character before
966
+ // the cursor is a word character, prepend a space for better readability
967
+ if (/^[/~.]/.test(filteredText)) {
968
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
969
+ const charBeforeCursor = this.state.cursorCol > 0 ? currentLine[this.state.cursorCol - 1] : "";
970
+ if (charBeforeCursor && /\w/.test(charBeforeCursor)) {
971
+ filteredText = ` ${filteredText}`;
972
+ }
973
+ }
974
+
975
+ // Split into lines to check for large paste
976
+ const pastedLines = filteredText.split("\n");
977
+
978
+ // Check if this is a large paste (> 10 lines or > 1000 characters)
979
+ const totalChars = filteredText.length;
980
+ if (pastedLines.length > 10 || totalChars > 1000) {
981
+ // Store the paste and insert a marker
982
+ this.pasteCounter++;
983
+ const pasteId = this.pasteCounter;
984
+ this.pastes.set(pasteId, filteredText);
985
+
986
+ // Insert marker like "[paste #1 +123 lines]" or "[paste #1 1234 chars]"
987
+ const marker =
988
+ pastedLines.length > 10
989
+ ? `[paste #${pasteId} +${pastedLines.length} lines]`
990
+ : `[paste #${pasteId} ${totalChars} chars]`;
991
+ this.insertTextAtCursorInternal(marker);
992
+ return;
993
+ }
994
+
995
+ if (pastedLines.length === 1) {
996
+ // Single line - insert atomically (do not trigger autocomplete during paste)
997
+ this.insertTextAtCursorInternal(filteredText);
998
+ return;
999
+ }
1000
+
1001
+ // Multi-line paste - use direct state manipulation
1002
+ this.insertTextAtCursorInternal(filteredText);
1003
+ }
1004
+
1005
+ private addNewLine(): void {
1006
+ this.historyIndex = -1; // Exit history browsing mode
1007
+ this.lastAction = null;
1008
+
1009
+ this.pushUndoSnapshot();
1010
+
1011
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1012
+
1013
+ const before = currentLine.slice(0, this.state.cursorCol);
1014
+ const after = currentLine.slice(this.state.cursorCol);
1015
+
1016
+ // Split current line
1017
+ this.state.lines[this.state.cursorLine] = before;
1018
+ this.state.lines.splice(this.state.cursorLine + 1, 0, after);
1019
+
1020
+ // Move cursor to start of new line
1021
+ this.state.cursorLine++;
1022
+ this.setCursorCol(0);
1023
+
1024
+ if (this.onChange) {
1025
+ this.onChange(this.getText());
1026
+ }
1027
+ }
1028
+
1029
+ private shouldSubmitOnBackslashEnter(data: string, kb: ReturnType<typeof getEditorKeybindings>): boolean {
1030
+ if (this.disableSubmit) return false;
1031
+ if (!matchesKey(data, "enter")) return false;
1032
+ const submitKeys = kb.getKeys("submit");
1033
+ const hasShiftEnter = submitKeys.includes("shift+enter") || submitKeys.includes("shift+return");
1034
+ if (!hasShiftEnter) return false;
1035
+
1036
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1037
+ return this.state.cursorCol > 0 && currentLine[this.state.cursorCol - 1] === "\\";
1038
+ }
1039
+
1040
+ private submitValue(): void {
1041
+ let result = this.state.lines.join("\n").trim();
1042
+ for (const [pasteId, pasteContent] of this.pastes) {
1043
+ const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g");
1044
+ result = result.replace(markerRegex, pasteContent);
1045
+ }
1046
+
1047
+ this.state = { lines: [""], cursorLine: 0, cursorCol: 0 };
1048
+ this.pastes.clear();
1049
+ this.pasteCounter = 0;
1050
+ this.historyIndex = -1;
1051
+ this.scrollOffset = 0;
1052
+ this.undoStack.clear();
1053
+ this.lastAction = null;
1054
+
1055
+ if (this.onChange) this.onChange("");
1056
+ if (this.onSubmit) this.onSubmit(result);
1057
+ }
1058
+
1059
+ private handleBackspace(): void {
1060
+ this.historyIndex = -1; // Exit history browsing mode
1061
+ this.lastAction = null;
1062
+
1063
+ if (this.state.cursorCol > 0) {
1064
+ this.pushUndoSnapshot();
1065
+
1066
+ // Delete grapheme before cursor (handles emojis, combining characters, etc.)
1067
+ const line = this.state.lines[this.state.cursorLine] || "";
1068
+ const beforeCursor = line.slice(0, this.state.cursorCol);
1069
+
1070
+ // Find the last grapheme in the text before cursor
1071
+ const graphemes = [...segmenter.segment(beforeCursor)];
1072
+ const lastGrapheme = graphemes[graphemes.length - 1];
1073
+ const graphemeLength = lastGrapheme ? lastGrapheme.segment.length : 1;
1074
+
1075
+ const before = line.slice(0, this.state.cursorCol - graphemeLength);
1076
+ const after = line.slice(this.state.cursorCol);
1077
+
1078
+ this.state.lines[this.state.cursorLine] = before + after;
1079
+ this.setCursorCol(this.state.cursorCol - graphemeLength);
1080
+ } else if (this.state.cursorLine > 0) {
1081
+ this.pushUndoSnapshot();
1082
+
1083
+ // Merge with previous line
1084
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1085
+ const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
1086
+
1087
+ this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
1088
+ this.state.lines.splice(this.state.cursorLine, 1);
1089
+
1090
+ this.state.cursorLine--;
1091
+ this.setCursorCol(previousLine.length);
1092
+ }
1093
+
1094
+ if (this.onChange) {
1095
+ this.onChange(this.getText());
1096
+ }
1097
+
1098
+ // Update or re-trigger autocomplete after backspace
1099
+ if (this.autocompleteState) {
1100
+ this.updateAutocomplete();
1101
+ } else {
1102
+ // If autocomplete was cancelled (no matches), re-trigger if we're in a completable context
1103
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1104
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1105
+ // Slash command context
1106
+ if (this.isInSlashCommandContext(textBeforeCursor)) {
1107
+ this.tryTriggerAutocomplete();
1108
+ }
1109
+ // @ file reference context
1110
+ else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
1111
+ this.tryTriggerAutocomplete();
1112
+ }
1113
+ }
1114
+ }
1115
+
1116
+ /**
1117
+ * Set cursor column and clear preferredVisualCol.
1118
+ * Use this for all non-vertical cursor movements to reset sticky column behavior.
1119
+ */
1120
+ private setCursorCol(col: number): void {
1121
+ this.state.cursorCol = col;
1122
+ this.preferredVisualCol = null;
1123
+ }
1124
+
1125
+ /**
1126
+ * Move cursor to a target visual line, applying sticky column logic.
1127
+ * Shared by moveCursor() and pageScroll().
1128
+ */
1129
+ private moveToVisualLine(
1130
+ visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
1131
+ currentVisualLine: number,
1132
+ targetVisualLine: number,
1133
+ ): void {
1134
+ const currentVL = visualLines[currentVisualLine];
1135
+ const targetVL = visualLines[targetVisualLine];
1136
+
1137
+ if (currentVL && targetVL) {
1138
+ const currentVisualCol = this.state.cursorCol - currentVL.startCol;
1139
+
1140
+ // For non-last segments, clamp to length-1 to stay within the segment
1141
+ const isLastSourceSegment =
1142
+ currentVisualLine === visualLines.length - 1 ||
1143
+ visualLines[currentVisualLine + 1]?.logicalLine !== currentVL.logicalLine;
1144
+ const sourceMaxVisualCol = isLastSourceSegment ? currentVL.length : Math.max(0, currentVL.length - 1);
1145
+
1146
+ const isLastTargetSegment =
1147
+ targetVisualLine === visualLines.length - 1 ||
1148
+ visualLines[targetVisualLine + 1]?.logicalLine !== targetVL.logicalLine;
1149
+ const targetMaxVisualCol = isLastTargetSegment ? targetVL.length : Math.max(0, targetVL.length - 1);
1150
+
1151
+ const moveToVisualCol = this.computeVerticalMoveColumn(
1152
+ currentVisualCol,
1153
+ sourceMaxVisualCol,
1154
+ targetMaxVisualCol,
1155
+ );
1156
+
1157
+ // Set cursor position
1158
+ this.state.cursorLine = targetVL.logicalLine;
1159
+ const targetCol = targetVL.startCol + moveToVisualCol;
1160
+ const logicalLine = this.state.lines[targetVL.logicalLine] || "";
1161
+ this.state.cursorCol = Math.min(targetCol, logicalLine.length);
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * Compute the target visual column for vertical cursor movement.
1167
+ * Implements the sticky column decision table:
1168
+ *
1169
+ * | P | S | T | U | Scenario | Set Preferred | Move To |
1170
+ * |---|---|---|---| ---------------------------------------------------- |---------------|-------------|
1171
+ * | 0 | * | 0 | - | Start nav, target fits | null | current |
1172
+ * | 0 | * | 1 | - | Start nav, target shorter | current | target end |
1173
+ * | 1 | 0 | 0 | 0 | Clamped, target fits preferred | null | preferred |
1174
+ * | 1 | 0 | 0 | 1 | Clamped, target longer but still can't fit preferred | keep | target end |
1175
+ * | 1 | 0 | 1 | - | Clamped, target even shorter | keep | target end |
1176
+ * | 1 | 1 | 0 | - | Rewrapped, target fits current | null | current |
1177
+ * | 1 | 1 | 1 | - | Rewrapped, target shorter than current | current | target end |
1178
+ *
1179
+ * Where:
1180
+ * - P = preferred col is set
1181
+ * - S = cursor in middle of source line (not clamped to end)
1182
+ * - T = target line shorter than current visual col
1183
+ * - U = target line shorter than preferred col
1184
+ */
1185
+ private computeVerticalMoveColumn(
1186
+ currentVisualCol: number,
1187
+ sourceMaxVisualCol: number,
1188
+ targetMaxVisualCol: number,
1189
+ ): number {
1190
+ const hasPreferred = this.preferredVisualCol !== null; // P
1191
+ const cursorInMiddle = currentVisualCol < sourceMaxVisualCol; // S
1192
+ const targetTooShort = targetMaxVisualCol < currentVisualCol; // T
1193
+
1194
+ if (!hasPreferred || cursorInMiddle) {
1195
+ if (targetTooShort) {
1196
+ // Cases 2 and 7
1197
+ this.preferredVisualCol = currentVisualCol;
1198
+ return targetMaxVisualCol;
1199
+ }
1200
+
1201
+ // Cases 1 and 6
1202
+ this.preferredVisualCol = null;
1203
+ return currentVisualCol;
1204
+ }
1205
+
1206
+ const targetCantFitPreferred = targetMaxVisualCol < this.preferredVisualCol!; // U
1207
+ if (targetTooShort || targetCantFitPreferred) {
1208
+ // Cases 4 and 5
1209
+ return targetMaxVisualCol;
1210
+ }
1211
+
1212
+ // Case 3
1213
+ const result = this.preferredVisualCol!;
1214
+ this.preferredVisualCol = null;
1215
+ return result;
1216
+ }
1217
+
1218
+ private moveToLineStart(): void {
1219
+ this.lastAction = null;
1220
+ this.setCursorCol(0);
1221
+ }
1222
+
1223
+ private moveToLineEnd(): void {
1224
+ this.lastAction = null;
1225
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1226
+ this.setCursorCol(currentLine.length);
1227
+ }
1228
+
1229
+ private deleteToStartOfLine(): void {
1230
+ this.historyIndex = -1; // Exit history browsing mode
1231
+
1232
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1233
+
1234
+ if (this.state.cursorCol > 0) {
1235
+ this.pushUndoSnapshot();
1236
+
1237
+ // Calculate text to be deleted and save to kill ring (backward deletion = prepend)
1238
+ const deletedText = currentLine.slice(0, this.state.cursorCol);
1239
+ this.killRing.push(deletedText, { prepend: true, accumulate: this.lastAction === "kill" });
1240
+ this.lastAction = "kill";
1241
+
1242
+ // Delete from start of line up to cursor
1243
+ this.state.lines[this.state.cursorLine] = currentLine.slice(this.state.cursorCol);
1244
+ this.setCursorCol(0);
1245
+ } else if (this.state.cursorLine > 0) {
1246
+ this.pushUndoSnapshot();
1247
+
1248
+ // At start of line - merge with previous line, treating newline as deleted text
1249
+ this.killRing.push("\n", { prepend: true, accumulate: this.lastAction === "kill" });
1250
+ this.lastAction = "kill";
1251
+
1252
+ const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
1253
+ this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
1254
+ this.state.lines.splice(this.state.cursorLine, 1);
1255
+ this.state.cursorLine--;
1256
+ this.setCursorCol(previousLine.length);
1257
+ }
1258
+
1259
+ if (this.onChange) {
1260
+ this.onChange(this.getText());
1261
+ }
1262
+ }
1263
+
1264
+ private deleteToEndOfLine(): void {
1265
+ this.historyIndex = -1; // Exit history browsing mode
1266
+
1267
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1268
+
1269
+ if (this.state.cursorCol < currentLine.length) {
1270
+ this.pushUndoSnapshot();
1271
+
1272
+ // Calculate text to be deleted and save to kill ring (forward deletion = append)
1273
+ const deletedText = currentLine.slice(this.state.cursorCol);
1274
+ this.killRing.push(deletedText, { prepend: false, accumulate: this.lastAction === "kill" });
1275
+ this.lastAction = "kill";
1276
+
1277
+ // Delete from cursor to end of line
1278
+ this.state.lines[this.state.cursorLine] = currentLine.slice(0, this.state.cursorCol);
1279
+ } else if (this.state.cursorLine < this.state.lines.length - 1) {
1280
+ this.pushUndoSnapshot();
1281
+
1282
+ // At end of line - merge with next line, treating newline as deleted text
1283
+ this.killRing.push("\n", { prepend: false, accumulate: this.lastAction === "kill" });
1284
+ this.lastAction = "kill";
1285
+
1286
+ const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
1287
+ this.state.lines[this.state.cursorLine] = currentLine + nextLine;
1288
+ this.state.lines.splice(this.state.cursorLine + 1, 1);
1289
+ }
1290
+
1291
+ if (this.onChange) {
1292
+ this.onChange(this.getText());
1293
+ }
1294
+ }
1295
+
1296
+ private deleteWordBackwards(): void {
1297
+ this.historyIndex = -1; // Exit history browsing mode
1298
+
1299
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1300
+
1301
+ // If at start of line, behave like backspace at column 0 (merge with previous line)
1302
+ if (this.state.cursorCol === 0) {
1303
+ if (this.state.cursorLine > 0) {
1304
+ this.pushUndoSnapshot();
1305
+
1306
+ // Treat newline as deleted text (backward deletion = prepend)
1307
+ this.killRing.push("\n", { prepend: true, accumulate: this.lastAction === "kill" });
1308
+ this.lastAction = "kill";
1309
+
1310
+ const previousLine = this.state.lines[this.state.cursorLine - 1] || "";
1311
+ this.state.lines[this.state.cursorLine - 1] = previousLine + currentLine;
1312
+ this.state.lines.splice(this.state.cursorLine, 1);
1313
+ this.state.cursorLine--;
1314
+ this.setCursorCol(previousLine.length);
1315
+ }
1316
+ } else {
1317
+ this.pushUndoSnapshot();
1318
+
1319
+ // Save lastAction before cursor movement (moveWordBackwards resets it)
1320
+ const wasKill = this.lastAction === "kill";
1321
+
1322
+ const oldCursorCol = this.state.cursorCol;
1323
+ this.moveWordBackwards();
1324
+ const deleteFrom = this.state.cursorCol;
1325
+ this.setCursorCol(oldCursorCol);
1326
+
1327
+ const deletedText = currentLine.slice(deleteFrom, this.state.cursorCol);
1328
+ this.killRing.push(deletedText, { prepend: true, accumulate: wasKill });
1329
+ this.lastAction = "kill";
1330
+
1331
+ this.state.lines[this.state.cursorLine] =
1332
+ currentLine.slice(0, deleteFrom) + currentLine.slice(this.state.cursorCol);
1333
+ this.setCursorCol(deleteFrom);
1334
+ }
1335
+
1336
+ if (this.onChange) {
1337
+ this.onChange(this.getText());
1338
+ }
1339
+ }
1340
+
1341
+ private deleteWordForward(): void {
1342
+ this.historyIndex = -1; // Exit history browsing mode
1343
+
1344
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1345
+
1346
+ // If at end of line, merge with next line (delete the newline)
1347
+ if (this.state.cursorCol >= currentLine.length) {
1348
+ if (this.state.cursorLine < this.state.lines.length - 1) {
1349
+ this.pushUndoSnapshot();
1350
+
1351
+ // Treat newline as deleted text (forward deletion = append)
1352
+ this.killRing.push("\n", { prepend: false, accumulate: this.lastAction === "kill" });
1353
+ this.lastAction = "kill";
1354
+
1355
+ const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
1356
+ this.state.lines[this.state.cursorLine] = currentLine + nextLine;
1357
+ this.state.lines.splice(this.state.cursorLine + 1, 1);
1358
+ }
1359
+ } else {
1360
+ this.pushUndoSnapshot();
1361
+
1362
+ // Save lastAction before cursor movement (moveWordForwards resets it)
1363
+ const wasKill = this.lastAction === "kill";
1364
+
1365
+ const oldCursorCol = this.state.cursorCol;
1366
+ this.moveWordForwards();
1367
+ const deleteTo = this.state.cursorCol;
1368
+ this.setCursorCol(oldCursorCol);
1369
+
1370
+ const deletedText = currentLine.slice(this.state.cursorCol, deleteTo);
1371
+ this.killRing.push(deletedText, { prepend: false, accumulate: wasKill });
1372
+ this.lastAction = "kill";
1373
+
1374
+ this.state.lines[this.state.cursorLine] =
1375
+ currentLine.slice(0, this.state.cursorCol) + currentLine.slice(deleteTo);
1376
+ }
1377
+
1378
+ if (this.onChange) {
1379
+ this.onChange(this.getText());
1380
+ }
1381
+ }
1382
+
1383
+ private handleForwardDelete(): void {
1384
+ this.historyIndex = -1; // Exit history browsing mode
1385
+ this.lastAction = null;
1386
+
1387
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1388
+
1389
+ if (this.state.cursorCol < currentLine.length) {
1390
+ this.pushUndoSnapshot();
1391
+
1392
+ // Delete grapheme at cursor position (handles emojis, combining characters, etc.)
1393
+ const afterCursor = currentLine.slice(this.state.cursorCol);
1394
+
1395
+ // Find the first grapheme at cursor
1396
+ const graphemes = [...segmenter.segment(afterCursor)];
1397
+ const firstGrapheme = graphemes[0];
1398
+ const graphemeLength = firstGrapheme ? firstGrapheme.segment.length : 1;
1399
+
1400
+ const before = currentLine.slice(0, this.state.cursorCol);
1401
+ const after = currentLine.slice(this.state.cursorCol + graphemeLength);
1402
+ this.state.lines[this.state.cursorLine] = before + after;
1403
+ } else if (this.state.cursorLine < this.state.lines.length - 1) {
1404
+ this.pushUndoSnapshot();
1405
+
1406
+ // At end of line - merge with next line
1407
+ const nextLine = this.state.lines[this.state.cursorLine + 1] || "";
1408
+ this.state.lines[this.state.cursorLine] = currentLine + nextLine;
1409
+ this.state.lines.splice(this.state.cursorLine + 1, 1);
1410
+ }
1411
+
1412
+ if (this.onChange) {
1413
+ this.onChange(this.getText());
1414
+ }
1415
+
1416
+ // Update or re-trigger autocomplete after forward delete
1417
+ if (this.autocompleteState) {
1418
+ this.updateAutocomplete();
1419
+ } else {
1420
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1421
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1422
+ // Slash command context
1423
+ if (this.isInSlashCommandContext(textBeforeCursor)) {
1424
+ this.tryTriggerAutocomplete();
1425
+ }
1426
+ // @ file reference context
1427
+ else if (textBeforeCursor.match(/(?:^|[\s])@[^\s]*$/)) {
1428
+ this.tryTriggerAutocomplete();
1429
+ }
1430
+ }
1431
+ }
1432
+
1433
+ /**
1434
+ * Build a mapping from visual lines to logical positions.
1435
+ * Returns an array where each element represents a visual line with:
1436
+ * - logicalLine: index into this.state.lines
1437
+ * - startCol: starting column in the logical line
1438
+ * - length: length of this visual line segment
1439
+ */
1440
+ private buildVisualLineMap(width: number): Array<{ logicalLine: number; startCol: number; length: number }> {
1441
+ const visualLines: Array<{ logicalLine: number; startCol: number; length: number }> = [];
1442
+
1443
+ for (let i = 0; i < this.state.lines.length; i++) {
1444
+ const line = this.state.lines[i] || "";
1445
+ const lineVisWidth = visibleWidth(line);
1446
+ if (line.length === 0) {
1447
+ // Empty line still takes one visual line
1448
+ visualLines.push({ logicalLine: i, startCol: 0, length: 0 });
1449
+ } else if (lineVisWidth <= width) {
1450
+ visualLines.push({ logicalLine: i, startCol: 0, length: line.length });
1451
+ } else {
1452
+ // Line needs wrapping - use word-aware wrapping
1453
+ const chunks = wordWrapLine(line, width);
1454
+ for (const chunk of chunks) {
1455
+ visualLines.push({
1456
+ logicalLine: i,
1457
+ startCol: chunk.startIndex,
1458
+ length: chunk.endIndex - chunk.startIndex,
1459
+ });
1460
+ }
1461
+ }
1462
+ }
1463
+
1464
+ return visualLines;
1465
+ }
1466
+
1467
+ /**
1468
+ * Find the visual line index for the current cursor position.
1469
+ */
1470
+ private findCurrentVisualLine(
1471
+ visualLines: Array<{ logicalLine: number; startCol: number; length: number }>,
1472
+ ): number {
1473
+ for (let i = 0; i < visualLines.length; i++) {
1474
+ const vl = visualLines[i];
1475
+ if (!vl) continue;
1476
+ if (vl.logicalLine === this.state.cursorLine) {
1477
+ const colInSegment = this.state.cursorCol - vl.startCol;
1478
+ // Cursor is in this segment if it's within range
1479
+ // For the last segment of a logical line, cursor can be at length (end position)
1480
+ const isLastSegmentOfLine =
1481
+ i === visualLines.length - 1 || visualLines[i + 1]?.logicalLine !== vl.logicalLine;
1482
+ if (colInSegment >= 0 && (colInSegment < vl.length || (isLastSegmentOfLine && colInSegment <= vl.length))) {
1483
+ return i;
1484
+ }
1485
+ }
1486
+ }
1487
+ // Fallback: return last visual line
1488
+ return visualLines.length - 1;
1489
+ }
1490
+
1491
+ private moveCursor(deltaLine: number, deltaCol: number): void {
1492
+ this.lastAction = null;
1493
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
1494
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
1495
+
1496
+ if (deltaLine !== 0) {
1497
+ const targetVisualLine = currentVisualLine + deltaLine;
1498
+
1499
+ if (targetVisualLine >= 0 && targetVisualLine < visualLines.length) {
1500
+ this.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);
1501
+ }
1502
+ }
1503
+
1504
+ if (deltaCol !== 0) {
1505
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1506
+
1507
+ if (deltaCol > 0) {
1508
+ // Moving right - move by one grapheme (handles emojis, combining characters, etc.)
1509
+ if (this.state.cursorCol < currentLine.length) {
1510
+ const afterCursor = currentLine.slice(this.state.cursorCol);
1511
+ const graphemes = [...segmenter.segment(afterCursor)];
1512
+ const firstGrapheme = graphemes[0];
1513
+ this.setCursorCol(this.state.cursorCol + (firstGrapheme ? firstGrapheme.segment.length : 1));
1514
+ } else if (this.state.cursorLine < this.state.lines.length - 1) {
1515
+ // Wrap to start of next logical line
1516
+ this.state.cursorLine++;
1517
+ this.setCursorCol(0);
1518
+ } else {
1519
+ // At end of last line - can't move, but set preferredVisualCol for up/down navigation
1520
+ const currentVL = visualLines[currentVisualLine];
1521
+ if (currentVL) {
1522
+ this.preferredVisualCol = this.state.cursorCol - currentVL.startCol;
1523
+ }
1524
+ }
1525
+ } else {
1526
+ // Moving left - move by one grapheme (handles emojis, combining characters, etc.)
1527
+ if (this.state.cursorCol > 0) {
1528
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1529
+ const graphemes = [...segmenter.segment(beforeCursor)];
1530
+ const lastGrapheme = graphemes[graphemes.length - 1];
1531
+ this.setCursorCol(this.state.cursorCol - (lastGrapheme ? lastGrapheme.segment.length : 1));
1532
+ } else if (this.state.cursorLine > 0) {
1533
+ // Wrap to end of previous logical line
1534
+ this.state.cursorLine--;
1535
+ const prevLine = this.state.lines[this.state.cursorLine] || "";
1536
+ this.setCursorCol(prevLine.length);
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+
1542
+ /**
1543
+ * Scroll by a page (direction: -1 for up, 1 for down).
1544
+ * Moves cursor by the page size while keeping it in bounds.
1545
+ */
1546
+ private pageScroll(direction: -1 | 1): void {
1547
+ this.lastAction = null;
1548
+ const terminalRows = this.tui.terminal.rows;
1549
+ const pageSize = Math.max(5, Math.floor(terminalRows * 0.3));
1550
+
1551
+ const visualLines = this.buildVisualLineMap(this.lastWidth);
1552
+ const currentVisualLine = this.findCurrentVisualLine(visualLines);
1553
+ const targetVisualLine = Math.max(0, Math.min(visualLines.length - 1, currentVisualLine + direction * pageSize));
1554
+
1555
+ this.moveToVisualLine(visualLines, currentVisualLine, targetVisualLine);
1556
+ }
1557
+
1558
+ private moveWordBackwards(): void {
1559
+ this.lastAction = null;
1560
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1561
+
1562
+ // If at start of line, move to end of previous line
1563
+ if (this.state.cursorCol === 0) {
1564
+ if (this.state.cursorLine > 0) {
1565
+ this.state.cursorLine--;
1566
+ const prevLine = this.state.lines[this.state.cursorLine] || "";
1567
+ this.setCursorCol(prevLine.length);
1568
+ }
1569
+ return;
1570
+ }
1571
+
1572
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1573
+ const graphemes = [...segmenter.segment(textBeforeCursor)];
1574
+ let newCol = this.state.cursorCol;
1575
+
1576
+ // Skip trailing whitespace
1577
+ while (graphemes.length > 0 && isWhitespaceChar(graphemes[graphemes.length - 1]?.segment || "")) {
1578
+ newCol -= graphemes.pop()?.segment.length || 0;
1579
+ }
1580
+
1581
+ if (graphemes.length > 0) {
1582
+ const lastGrapheme = graphemes[graphemes.length - 1]?.segment || "";
1583
+ if (isPunctuationChar(lastGrapheme)) {
1584
+ // Skip punctuation run
1585
+ while (graphemes.length > 0 && isPunctuationChar(graphemes[graphemes.length - 1]?.segment || "")) {
1586
+ newCol -= graphemes.pop()?.segment.length || 0;
1587
+ }
1588
+ } else {
1589
+ // Skip word run
1590
+ while (
1591
+ graphemes.length > 0 &&
1592
+ !isWhitespaceChar(graphemes[graphemes.length - 1]?.segment || "") &&
1593
+ !isPunctuationChar(graphemes[graphemes.length - 1]?.segment || "")
1594
+ ) {
1595
+ newCol -= graphemes.pop()?.segment.length || 0;
1596
+ }
1597
+ }
1598
+ }
1599
+
1600
+ this.setCursorCol(newCol);
1601
+ }
1602
+
1603
+ /**
1604
+ * Yank (paste) the most recent kill ring entry at cursor position.
1605
+ */
1606
+ private yank(): void {
1607
+ if (this.killRing.length === 0) return;
1608
+
1609
+ this.pushUndoSnapshot();
1610
+
1611
+ const text = this.killRing.peek()!;
1612
+ this.insertYankedText(text);
1613
+
1614
+ this.lastAction = "yank";
1615
+ }
1616
+
1617
+ /**
1618
+ * Cycle through kill ring (only works immediately after yank or yank-pop).
1619
+ * Replaces the last yanked text with the previous entry in the ring.
1620
+ */
1621
+ private yankPop(): void {
1622
+ // Only works if we just yanked and have more than one entry
1623
+ if (this.lastAction !== "yank" || this.killRing.length <= 1) return;
1624
+
1625
+ this.pushUndoSnapshot();
1626
+
1627
+ // Delete the previously yanked text (still at end of ring before rotation)
1628
+ this.deleteYankedText();
1629
+
1630
+ // Rotate the ring: move end to front
1631
+ this.killRing.rotate();
1632
+
1633
+ // Insert the new most recent entry (now at end after rotation)
1634
+ const text = this.killRing.peek()!;
1635
+ this.insertYankedText(text);
1636
+
1637
+ this.lastAction = "yank";
1638
+ }
1639
+
1640
+ /**
1641
+ * Insert text at cursor position (used by yank operations).
1642
+ */
1643
+ private insertYankedText(text: string): void {
1644
+ this.historyIndex = -1; // Exit history browsing mode
1645
+ const lines = text.split("\n");
1646
+
1647
+ if (lines.length === 1) {
1648
+ // Single line - insert at cursor
1649
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1650
+ const before = currentLine.slice(0, this.state.cursorCol);
1651
+ const after = currentLine.slice(this.state.cursorCol);
1652
+ this.state.lines[this.state.cursorLine] = before + text + after;
1653
+ this.setCursorCol(this.state.cursorCol + text.length);
1654
+ } else {
1655
+ // Multi-line insert
1656
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1657
+ const before = currentLine.slice(0, this.state.cursorCol);
1658
+ const after = currentLine.slice(this.state.cursorCol);
1659
+
1660
+ // First line merges with text before cursor
1661
+ this.state.lines[this.state.cursorLine] = before + (lines[0] || "");
1662
+
1663
+ // Insert middle lines
1664
+ for (let i = 1; i < lines.length - 1; i++) {
1665
+ this.state.lines.splice(this.state.cursorLine + i, 0, lines[i] || "");
1666
+ }
1667
+
1668
+ // Last line merges with text after cursor
1669
+ const lastLineIndex = this.state.cursorLine + lines.length - 1;
1670
+ this.state.lines.splice(lastLineIndex, 0, (lines[lines.length - 1] || "") + after);
1671
+
1672
+ // Update cursor position
1673
+ this.state.cursorLine = lastLineIndex;
1674
+ this.setCursorCol((lines[lines.length - 1] || "").length);
1675
+ }
1676
+
1677
+ if (this.onChange) {
1678
+ this.onChange(this.getText());
1679
+ }
1680
+ }
1681
+
1682
+ /**
1683
+ * Delete the previously yanked text (used by yank-pop).
1684
+ * The yanked text is derived from killRing[end] since it hasn't been rotated yet.
1685
+ */
1686
+ private deleteYankedText(): void {
1687
+ const yankedText = this.killRing.peek();
1688
+ if (!yankedText) return;
1689
+
1690
+ const yankLines = yankedText.split("\n");
1691
+
1692
+ if (yankLines.length === 1) {
1693
+ // Single line - delete backward from cursor
1694
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1695
+ const deleteLen = yankedText.length;
1696
+ const before = currentLine.slice(0, this.state.cursorCol - deleteLen);
1697
+ const after = currentLine.slice(this.state.cursorCol);
1698
+ this.state.lines[this.state.cursorLine] = before + after;
1699
+ this.setCursorCol(this.state.cursorCol - deleteLen);
1700
+ } else {
1701
+ // Multi-line delete - cursor is at end of last yanked line
1702
+ const startLine = this.state.cursorLine - (yankLines.length - 1);
1703
+ const startCol = (this.state.lines[startLine] || "").length - (yankLines[0] || "").length;
1704
+
1705
+ // Get text after cursor on current line
1706
+ const afterCursor = (this.state.lines[this.state.cursorLine] || "").slice(this.state.cursorCol);
1707
+
1708
+ // Get text before yank start position
1709
+ const beforeYank = (this.state.lines[startLine] || "").slice(0, startCol);
1710
+
1711
+ // Remove all lines from startLine to cursorLine and replace with merged line
1712
+ this.state.lines.splice(startLine, yankLines.length, beforeYank + afterCursor);
1713
+
1714
+ // Update cursor
1715
+ this.state.cursorLine = startLine;
1716
+ this.setCursorCol(startCol);
1717
+ }
1718
+
1719
+ if (this.onChange) {
1720
+ this.onChange(this.getText());
1721
+ }
1722
+ }
1723
+
1724
+ private pushUndoSnapshot(): void {
1725
+ this.undoStack.push(this.state);
1726
+ }
1727
+
1728
+ private undo(): void {
1729
+ this.historyIndex = -1; // Exit history browsing mode
1730
+ const snapshot = this.undoStack.pop();
1731
+ if (!snapshot) return;
1732
+ Object.assign(this.state, snapshot);
1733
+ this.lastAction = null;
1734
+ this.preferredVisualCol = null;
1735
+ if (this.onChange) {
1736
+ this.onChange(this.getText());
1737
+ }
1738
+ }
1739
+
1740
+ /**
1741
+ * Jump to the first occurrence of a character in the specified direction.
1742
+ * Multi-line search. Case-sensitive. Skips the current cursor position.
1743
+ */
1744
+ private jumpToChar(char: string, direction: "forward" | "backward"): void {
1745
+ this.lastAction = null;
1746
+ const isForward = direction === "forward";
1747
+ const lines = this.state.lines;
1748
+
1749
+ const end = isForward ? lines.length : -1;
1750
+ const step = isForward ? 1 : -1;
1751
+
1752
+ for (let lineIdx = this.state.cursorLine; lineIdx !== end; lineIdx += step) {
1753
+ const line = lines[lineIdx] || "";
1754
+ const isCurrentLine = lineIdx === this.state.cursorLine;
1755
+
1756
+ // Current line: start after/before cursor; other lines: search full line
1757
+ const searchFrom = isCurrentLine
1758
+ ? isForward
1759
+ ? this.state.cursorCol + 1
1760
+ : this.state.cursorCol - 1
1761
+ : undefined;
1762
+
1763
+ const idx = isForward ? line.indexOf(char, searchFrom) : line.lastIndexOf(char, searchFrom);
1764
+
1765
+ if (idx !== -1) {
1766
+ this.state.cursorLine = lineIdx;
1767
+ this.setCursorCol(idx);
1768
+ return;
1769
+ }
1770
+ }
1771
+ // No match found - cursor stays in place
1772
+ }
1773
+
1774
+ private moveWordForwards(): void {
1775
+ this.lastAction = null;
1776
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1777
+
1778
+ // If at end of line, move to start of next line
1779
+ if (this.state.cursorCol >= currentLine.length) {
1780
+ if (this.state.cursorLine < this.state.lines.length - 1) {
1781
+ this.state.cursorLine++;
1782
+ this.setCursorCol(0);
1783
+ }
1784
+ return;
1785
+ }
1786
+
1787
+ const textAfterCursor = currentLine.slice(this.state.cursorCol);
1788
+ const segments = segmenter.segment(textAfterCursor);
1789
+ const iterator = segments[Symbol.iterator]();
1790
+ let next = iterator.next();
1791
+ let newCol = this.state.cursorCol;
1792
+
1793
+ // Skip leading whitespace
1794
+ while (!next.done && isWhitespaceChar(next.value.segment)) {
1795
+ newCol += next.value.segment.length;
1796
+ next = iterator.next();
1797
+ }
1798
+
1799
+ if (!next.done) {
1800
+ const firstGrapheme = next.value.segment;
1801
+ if (isPunctuationChar(firstGrapheme)) {
1802
+ // Skip punctuation run
1803
+ while (!next.done && isPunctuationChar(next.value.segment)) {
1804
+ newCol += next.value.segment.length;
1805
+ next = iterator.next();
1806
+ }
1807
+ } else {
1808
+ // Skip word run
1809
+ while (!next.done && !isWhitespaceChar(next.value.segment) && !isPunctuationChar(next.value.segment)) {
1810
+ newCol += next.value.segment.length;
1811
+ next = iterator.next();
1812
+ }
1813
+ }
1814
+ }
1815
+
1816
+ this.setCursorCol(newCol);
1817
+ }
1818
+
1819
+ // Slash menu only allowed on the first line of the editor
1820
+ private isSlashMenuAllowed(): boolean {
1821
+ return this.state.cursorLine === 0;
1822
+ }
1823
+
1824
+ // Helper method to check if cursor is at start of message (for slash command detection)
1825
+ private isAtStartOfMessage(): boolean {
1826
+ if (!this.isSlashMenuAllowed()) return false;
1827
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1828
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1829
+ return beforeCursor.trim() === "" || beforeCursor.trim() === "/";
1830
+ }
1831
+
1832
+ private isInSlashCommandContext(textBeforeCursor: string): boolean {
1833
+ return this.isSlashMenuAllowed() && textBeforeCursor.trimStart().startsWith("/");
1834
+ }
1835
+
1836
+ private shouldChainSlashArgumentAutocompleteOnTabSelection(): boolean {
1837
+ if (this.autocompleteState !== "regular") {
1838
+ return false;
1839
+ }
1840
+
1841
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1842
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
1843
+ return this.isInSlashCommandContext(textBeforeCursor) && !textBeforeCursor.trimStart().includes(" ");
1844
+ }
1845
+
1846
+ private isBareCompletedSlashCommandAtCursor(): boolean {
1847
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1848
+ if (this.state.cursorCol !== currentLine.length) {
1849
+ return false;
1850
+ }
1851
+
1852
+ const textBeforeCursor = currentLine.slice(0, this.state.cursorCol).trimStart();
1853
+ return /^\/\S+ $/.test(textBeforeCursor);
1854
+ }
1855
+
1856
+ // Autocomplete methods
1857
+ /**
1858
+ * Find the best autocomplete item index for the given prefix.
1859
+ * Returns -1 if no match is found.
1860
+ *
1861
+ * Match priority:
1862
+ * 1. Exact match (prefix === item.value) -> always selected
1863
+ * 2. Prefix match -> first item whose value starts with prefix
1864
+ * 3. No match -> -1 (keep default highlight)
1865
+ *
1866
+ * Matching is case-sensitive and checks item.value only.
1867
+ */
1868
+ private getBestAutocompleteMatchIndex(items: Array<{ value: string; label: string }>, prefix: string): number {
1869
+ if (!prefix) return -1;
1870
+
1871
+ let firstPrefixIndex = -1;
1872
+
1873
+ for (let i = 0; i < items.length; i++) {
1874
+ const value = items[i]!.value;
1875
+ if (value === prefix) {
1876
+ return i; // Exact match always wins
1877
+ }
1878
+ if (firstPrefixIndex === -1 && value.startsWith(prefix)) {
1879
+ firstPrefixIndex = i;
1880
+ }
1881
+ }
1882
+
1883
+ return firstPrefixIndex;
1884
+ }
1885
+
1886
+ private tryTriggerAutocomplete(explicitTab: boolean = false): void {
1887
+ if (!this.autocompleteProvider) return;
1888
+
1889
+ // Check if we should trigger file completion on Tab
1890
+ if (explicitTab) {
1891
+ const provider = this.autocompleteProvider as CombinedAutocompleteProvider;
1892
+ const shouldTrigger =
1893
+ !provider.shouldTriggerFileCompletion ||
1894
+ provider.shouldTriggerFileCompletion(this.state.lines, this.state.cursorLine, this.state.cursorCol);
1895
+ if (!shouldTrigger) {
1896
+ return;
1897
+ }
1898
+ }
1899
+
1900
+ const suggestions = this.autocompleteProvider.getSuggestions(
1901
+ this.state.lines,
1902
+ this.state.cursorLine,
1903
+ this.state.cursorCol,
1904
+ );
1905
+
1906
+ if (suggestions && suggestions.items.length > 0) {
1907
+ this.autocompletePrefix = suggestions.prefix;
1908
+ this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
1909
+
1910
+ // If typed prefix exactly matches one of the suggestions, select that item
1911
+ const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
1912
+ if (bestMatchIndex >= 0) {
1913
+ this.autocompleteList.setSelectedIndex(bestMatchIndex);
1914
+ }
1915
+
1916
+ this.autocompleteState = "regular";
1917
+ } else {
1918
+ this.cancelAutocomplete();
1919
+ }
1920
+ }
1921
+
1922
+ private handleTabCompletion(): void {
1923
+ if (!this.autocompleteProvider) return;
1924
+
1925
+ const currentLine = this.state.lines[this.state.cursorLine] || "";
1926
+ const beforeCursor = currentLine.slice(0, this.state.cursorCol);
1927
+
1928
+ // Check if we're in a slash command context
1929
+ if (this.isInSlashCommandContext(beforeCursor) && !beforeCursor.trimStart().includes(" ")) {
1930
+ this.handleSlashCommandCompletion();
1931
+ } else {
1932
+ this.forceFileAutocomplete(true);
1933
+ }
1934
+ }
1935
+
1936
+ private handleSlashCommandCompletion(): void {
1937
+ this.tryTriggerAutocomplete(true);
1938
+ }
1939
+
1940
+ /*
1941
+ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/559322883
1942
+ 17 this job fails with https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19
1943
+ 536643416/job/55932288317 havea look at .gi
1944
+ */
1945
+ private forceFileAutocomplete(explicitTab: boolean = false): void {
1946
+ if (!this.autocompleteProvider) return;
1947
+
1948
+ // Check if provider supports force file suggestions via runtime check
1949
+ const provider = this.autocompleteProvider as {
1950
+ getForceFileSuggestions?: CombinedAutocompleteProvider["getForceFileSuggestions"];
1951
+ };
1952
+ if (typeof provider.getForceFileSuggestions !== "function") {
1953
+ this.tryTriggerAutocomplete(true);
1954
+ return;
1955
+ }
1956
+
1957
+ const suggestions = provider.getForceFileSuggestions(
1958
+ this.state.lines,
1959
+ this.state.cursorLine,
1960
+ this.state.cursorCol,
1961
+ );
1962
+
1963
+ if (suggestions && suggestions.items.length > 0) {
1964
+ // If there's exactly one suggestion, apply it immediately
1965
+ if (explicitTab && suggestions.items.length === 1) {
1966
+ const item = suggestions.items[0]!;
1967
+ this.pushUndoSnapshot();
1968
+ this.lastAction = null;
1969
+ const result = this.autocompleteProvider.applyCompletion(
1970
+ this.state.lines,
1971
+ this.state.cursorLine,
1972
+ this.state.cursorCol,
1973
+ item,
1974
+ suggestions.prefix,
1975
+ );
1976
+ this.state.lines = result.lines;
1977
+ this.state.cursorLine = result.cursorLine;
1978
+ this.setCursorCol(result.cursorCol);
1979
+ if (this.onChange) this.onChange(this.getText());
1980
+ return;
1981
+ }
1982
+
1983
+ this.autocompletePrefix = suggestions.prefix;
1984
+ this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
1985
+
1986
+ // If typed prefix exactly matches one of the suggestions, select that item
1987
+ const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
1988
+ if (bestMatchIndex >= 0) {
1989
+ this.autocompleteList.setSelectedIndex(bestMatchIndex);
1990
+ }
1991
+
1992
+ this.autocompleteState = "force";
1993
+ } else {
1994
+ this.cancelAutocomplete();
1995
+ }
1996
+ }
1997
+
1998
+ private cancelAutocomplete(): void {
1999
+ this.autocompleteState = null;
2000
+ this.autocompleteList = undefined;
2001
+ this.autocompletePrefix = "";
2002
+ }
2003
+
2004
+ public isShowingAutocomplete(): boolean {
2005
+ return this.autocompleteState !== null;
2006
+ }
2007
+
2008
+ private updateAutocomplete(): void {
2009
+ if (!this.autocompleteState || !this.autocompleteProvider) return;
2010
+
2011
+ if (this.autocompleteState === "force") {
2012
+ this.forceFileAutocomplete();
2013
+ return;
2014
+ }
2015
+
2016
+ const suggestions = this.autocompleteProvider.getSuggestions(
2017
+ this.state.lines,
2018
+ this.state.cursorLine,
2019
+ this.state.cursorCol,
2020
+ );
2021
+ if (suggestions && suggestions.items.length > 0) {
2022
+ this.autocompletePrefix = suggestions.prefix;
2023
+ // Always create new SelectList to ensure update
2024
+ this.autocompleteList = new SelectList(suggestions.items, this.autocompleteMaxVisible, this.theme.selectList);
2025
+
2026
+ // If typed prefix exactly matches one of the suggestions, select that item
2027
+ const bestMatchIndex = this.getBestAutocompleteMatchIndex(suggestions.items, suggestions.prefix);
2028
+ if (bestMatchIndex >= 0) {
2029
+ this.autocompleteList.setSelectedIndex(bestMatchIndex);
2030
+ }
2031
+ } else {
2032
+ this.cancelAutocomplete();
2033
+ }
2034
+ }
2035
+ }