@armadra/agent 0.0.0-stage → 0.3.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 (433) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +470 -2
  4. package/dist/agent/agent.d.ts +56 -0
  5. package/dist/agent/agent.js +129 -0
  6. package/dist/agent/loop.d.ts +51 -0
  7. package/dist/agent/loop.js +158 -0
  8. package/dist/agent/queue.d.ts +25 -0
  9. package/dist/agent/queue.js +55 -0
  10. package/dist/agent/retry.d.ts +22 -0
  11. package/dist/agent/retry.js +92 -0
  12. package/dist/agent/schema.d.ts +27 -0
  13. package/dist/agent/schema.js +186 -0
  14. package/dist/agent/session-cache.d.ts +100 -0
  15. package/dist/agent/session-cache.js +514 -0
  16. package/dist/agent/session-compaction.d.ts +49 -0
  17. package/dist/agent/session-compaction.js +251 -0
  18. package/dist/agent/session-core.d.ts +103 -0
  19. package/dist/agent/session-core.js +7 -0
  20. package/dist/agent/session-run.d.ts +55 -0
  21. package/dist/agent/session-run.js +246 -0
  22. package/dist/agent/session-state.d.ts +39 -0
  23. package/dist/agent/session-state.js +119 -0
  24. package/dist/agent/session-subagent.d.ts +54 -0
  25. package/dist/agent/session-subagent.js +157 -0
  26. package/dist/agent/session-sync.d.ts +20 -0
  27. package/dist/agent/session-sync.js +111 -0
  28. package/dist/agent/session-tools.d.ts +24 -0
  29. package/dist/agent/session-tools.js +277 -0
  30. package/dist/agent/session.d.ts +117 -0
  31. package/dist/agent/session.js +485 -0
  32. package/dist/agent/system-prompt.d.ts +49 -0
  33. package/dist/agent/system-prompt.js +121 -0
  34. package/dist/agent/tool-runner.d.ts +64 -0
  35. package/dist/agent/tool-runner.js +309 -0
  36. package/dist/agent/transform.d.ts +26 -0
  37. package/dist/agent/transform.js +171 -0
  38. package/dist/agent/types.d.ts +378 -0
  39. package/dist/agent/types.js +21 -0
  40. package/dist/ai/apis/anthropic-messages.d.ts +26 -0
  41. package/dist/ai/apis/anthropic-messages.js +214 -0
  42. package/dist/ai/apis/anthropic-request.d.ts +42 -0
  43. package/dist/ai/apis/anthropic-request.js +282 -0
  44. package/dist/ai/apis/api.d.ts +28 -0
  45. package/dist/ai/apis/api.js +102 -0
  46. package/dist/ai/apis/cache-params.d.ts +48 -0
  47. package/dist/ai/apis/cache-params.js +163 -0
  48. package/dist/ai/apis/google-generative-ai.d.ts +32 -0
  49. package/dist/ai/apis/google-generative-ai.js +249 -0
  50. package/dist/ai/apis/google-request.d.ts +40 -0
  51. package/dist/ai/apis/google-request.js +255 -0
  52. package/dist/ai/apis/openai-compat.d.ts +35 -0
  53. package/dist/ai/apis/openai-compat.js +142 -0
  54. package/dist/ai/apis/openai-completions.d.ts +28 -0
  55. package/dist/ai/apis/openai-completions.js +246 -0
  56. package/dist/ai/apis/openai-request.d.ts +35 -0
  57. package/dist/ai/apis/openai-request.js +303 -0
  58. package/dist/ai/apis/openai-responses-request.d.ts +52 -0
  59. package/dist/ai/apis/openai-responses-request.js +266 -0
  60. package/dist/ai/apis/openai-responses.d.ts +28 -0
  61. package/dist/ai/apis/openai-responses.js +304 -0
  62. package/dist/ai/apis/shared.d.ts +56 -0
  63. package/dist/ai/apis/shared.js +179 -0
  64. package/dist/ai/cache/economics.d.ts +24 -0
  65. package/dist/ai/cache/economics.js +69 -0
  66. package/dist/ai/cache/fingerprint.d.ts +17 -0
  67. package/dist/ai/cache/fingerprint.js +39 -0
  68. package/dist/ai/cache/miss.d.ts +49 -0
  69. package/dist/ai/cache/miss.js +96 -0
  70. package/dist/ai/cache/reporting.d.ts +39 -0
  71. package/dist/ai/cache/reporting.js +103 -0
  72. package/dist/ai/cache/types.d.ts +94 -0
  73. package/dist/ai/cache/types.js +19 -0
  74. package/dist/ai/cache/warmer.d.ts +94 -0
  75. package/dist/ai/cache/warmer.js +224 -0
  76. package/dist/ai/context.d.ts +35 -0
  77. package/dist/ai/context.js +114 -0
  78. package/dist/ai/cost.d.ts +21 -0
  79. package/dist/ai/cost.js +60 -0
  80. package/dist/ai/event-stream.d.ts +33 -0
  81. package/dist/ai/event-stream.js +101 -0
  82. package/dist/ai/fake/fake-provider.d.ts +68 -0
  83. package/dist/ai/fake/fake-provider.js +278 -0
  84. package/dist/ai/fake/fake-script.d.ts +69 -0
  85. package/dist/ai/fake/fake-script.js +142 -0
  86. package/dist/ai/http.d.ts +54 -0
  87. package/dist/ai/http.js +212 -0
  88. package/dist/ai/json-partial.d.ts +17 -0
  89. package/dist/ai/json-partial.js +243 -0
  90. package/dist/ai/overflow.d.ts +22 -0
  91. package/dist/ai/overflow.js +61 -0
  92. package/dist/ai/providers/auth.d.ts +71 -0
  93. package/dist/ai/providers/auth.js +202 -0
  94. package/dist/ai/providers/builtin.d.ts +23 -0
  95. package/dist/ai/providers/builtin.js +148 -0
  96. package/dist/ai/providers/catalog-data.d.ts +5 -0
  97. package/dist/ai/providers/catalog-data.js +19 -0
  98. package/dist/ai/providers/catalog.d.ts +28 -0
  99. package/dist/ai/providers/catalog.js +166 -0
  100. package/dist/ai/providers/channels.d.ts +51 -0
  101. package/dist/ai/providers/channels.js +96 -0
  102. package/dist/ai/providers/enrich.d.ts +34 -0
  103. package/dist/ai/providers/enrich.js +86 -0
  104. package/dist/ai/providers/models-dev-cache.d.ts +53 -0
  105. package/dist/ai/providers/models-dev-cache.js +147 -0
  106. package/dist/ai/providers/models-dev.d.ts +99 -0
  107. package/dist/ai/providers/models-dev.js +315 -0
  108. package/dist/ai/providers/registry.d.ts +114 -0
  109. package/dist/ai/providers/registry.js +458 -0
  110. package/dist/ai/sse.d.ts +32 -0
  111. package/dist/ai/sse.js +140 -0
  112. package/dist/ai/thinking.d.ts +42 -0
  113. package/dist/ai/thinking.js +101 -0
  114. package/dist/ai/types.d.ts +448 -0
  115. package/dist/ai/types.js +19 -0
  116. package/dist/bundle/ama-sandbox.cjs +324 -0
  117. package/dist/bundle/ama.cjs +27837 -0
  118. package/dist/bundle.d.ts +9 -0
  119. package/dist/bundle.js +15 -0
  120. package/dist/cli/args.d.ts +85 -0
  121. package/dist/cli/args.js +408 -0
  122. package/dist/cli/bootstrap.d.ts +18 -0
  123. package/dist/cli/bootstrap.js +399 -0
  124. package/dist/cli/compose-providers.d.ts +37 -0
  125. package/dist/cli/compose-providers.js +107 -0
  126. package/dist/cli/compose-session.d.ts +72 -0
  127. package/dist/cli/compose-session.js +370 -0
  128. package/dist/cli/compose-store.d.ts +24 -0
  129. package/dist/cli/compose-store.js +125 -0
  130. package/dist/cli/compose.d.ts +108 -0
  131. package/dist/cli/compose.js +221 -0
  132. package/dist/cli/default-model.d.ts +16 -0
  133. package/dist/cli/default-model.js +24 -0
  134. package/dist/cli/deps.d.ts +207 -0
  135. package/dist/cli/deps.js +12 -0
  136. package/dist/cli/exit-codes.d.ts +29 -0
  137. package/dist/cli/exit-codes.js +40 -0
  138. package/dist/cli/main.d.ts +27 -0
  139. package/dist/cli/main.js +177 -0
  140. package/dist/cli/runtime.d.ts +90 -0
  141. package/dist/cli/runtime.js +8 -0
  142. package/dist/cli/startup-screen.d.ts +15 -0
  143. package/dist/cli/startup-screen.js +56 -0
  144. package/dist/cli/startup-steps.d.ts +35 -0
  145. package/dist/cli/startup-steps.js +202 -0
  146. package/dist/cli/subcommands/auth.d.ts +12 -0
  147. package/dist/cli/subcommands/auth.js +91 -0
  148. package/dist/cli/subcommands/config.d.ts +53 -0
  149. package/dist/cli/subcommands/config.js +277 -0
  150. package/dist/cli/subcommands/context.d.ts +21 -0
  151. package/dist/cli/subcommands/context.js +42 -0
  152. package/dist/cli/subcommands/doctor.d.ts +11 -0
  153. package/dist/cli/subcommands/doctor.js +246 -0
  154. package/dist/cli/subcommands/init.d.ts +6 -0
  155. package/dist/cli/subcommands/init.js +22 -0
  156. package/dist/cli/subcommands/model-meta.d.ts +16 -0
  157. package/dist/cli/subcommands/model-meta.js +54 -0
  158. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  159. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  160. package/dist/cli/subcommands/models-discover.d.ts +46 -0
  161. package/dist/cli/subcommands/models-discover.js +232 -0
  162. package/dist/cli/subcommands/models.d.ts +40 -0
  163. package/dist/cli/subcommands/models.js +152 -0
  164. package/dist/cli/subcommands/providers-list.d.ts +8 -0
  165. package/dist/cli/subcommands/providers-list.js +97 -0
  166. package/dist/cli/subcommands/providers-plan.d.ts +79 -0
  167. package/dist/cli/subcommands/providers-plan.js +215 -0
  168. package/dist/cli/subcommands/providers.d.ts +27 -0
  169. package/dist/cli/subcommands/providers.js +433 -0
  170. package/dist/cli/subcommands/sessions.d.ts +11 -0
  171. package/dist/cli/subcommands/sessions.js +100 -0
  172. package/dist/codemode/capability.d.ts +37 -0
  173. package/dist/codemode/capability.js +55 -0
  174. package/dist/codemode/declarations.d.ts +44 -0
  175. package/dist/codemode/declarations.js +111 -0
  176. package/dist/codemode/host-side.d.ts +59 -0
  177. package/dist/codemode/host-side.js +253 -0
  178. package/dist/codemode/modes.d.ts +23 -0
  179. package/dist/codemode/modes.js +40 -0
  180. package/dist/codemode/protocol.d.ts +91 -0
  181. package/dist/codemode/protocol.js +114 -0
  182. package/dist/codemode/sandbox-entry.d.ts +25 -0
  183. package/dist/codemode/sandbox-entry.js +323 -0
  184. package/dist/codemode/store.d.ts +26 -0
  185. package/dist/codemode/store.js +53 -0
  186. package/dist/codemode/tool.d.ts +105 -0
  187. package/dist/codemode/tool.js +283 -0
  188. package/dist/compaction/branch-summary.d.ts +30 -0
  189. package/dist/compaction/branch-summary.js +79 -0
  190. package/dist/compaction/breaker.d.ts +32 -0
  191. package/dist/compaction/breaker.js +65 -0
  192. package/dist/compaction/cut-point.d.ts +23 -0
  193. package/dist/compaction/cut-point.js +70 -0
  194. package/dist/compaction/estimate.d.ts +30 -0
  195. package/dist/compaction/estimate.js +114 -0
  196. package/dist/compaction/prune-tier.d.ts +31 -0
  197. package/dist/compaction/prune-tier.js +83 -0
  198. package/dist/compaction/serialize.d.ts +20 -0
  199. package/dist/compaction/serialize.js +115 -0
  200. package/dist/compaction/summarize-tier.d.ts +111 -0
  201. package/dist/compaction/summarize-tier.js +305 -0
  202. package/dist/config/auth-file.d.ts +39 -0
  203. package/dist/config/auth-file.js +92 -0
  204. package/dist/config/context-files.d.ts +30 -0
  205. package/dist/config/context-files.js +77 -0
  206. package/dist/config/init.d.ts +30 -0
  207. package/dist/config/init.js +92 -0
  208. package/dist/config/json-schema.d.ts +14 -0
  209. package/dist/config/json-schema.js +182 -0
  210. package/dist/config/load.d.ts +45 -0
  211. package/dist/config/load.js +97 -0
  212. package/dist/config/merge.d.ts +73 -0
  213. package/dist/config/merge.js +248 -0
  214. package/dist/config/paths.d.ts +38 -0
  215. package/dist/config/paths.js +108 -0
  216. package/dist/config/profile.d.ts +27 -0
  217. package/dist/config/profile.js +65 -0
  218. package/dist/config/schema.d.ts +34 -0
  219. package/dist/config/schema.js +476 -0
  220. package/dist/config/trust.d.ts +42 -0
  221. package/dist/config/trust.js +122 -0
  222. package/dist/config/types.d.ts +190 -0
  223. package/dist/config/types.js +29 -0
  224. package/dist/config/write.d.ts +10 -0
  225. package/dist/config/write.js +25 -0
  226. package/dist/errors.d.ts +28 -0
  227. package/dist/errors.js +30 -0
  228. package/dist/hooks/config.d.ts +49 -0
  229. package/dist/hooks/config.js +102 -0
  230. package/dist/hooks/dispatcher.d.ts +41 -0
  231. package/dist/hooks/dispatcher.js +104 -0
  232. package/dist/hooks/matcher.d.ts +22 -0
  233. package/dist/hooks/matcher.js +108 -0
  234. package/dist/hooks/protocol.d.ts +33 -0
  235. package/dist/hooks/protocol.js +240 -0
  236. package/dist/hooks/runner.d.ts +30 -0
  237. package/dist/hooks/runner.js +143 -0
  238. package/dist/hooks/types.d.ts +147 -0
  239. package/dist/hooks/types.js +20 -0
  240. package/dist/host/api-impl.d.ts +75 -0
  241. package/dist/host/api-impl.js +173 -0
  242. package/dist/host/loader.d.ts +31 -0
  243. package/dist/host/loader.js +147 -0
  244. package/dist/host/types.d.ts +169 -0
  245. package/dist/host/types.js +14 -0
  246. package/dist/host.d.ts +5 -0
  247. package/dist/host.js +4 -0
  248. package/dist/index.d.ts +41 -0
  249. package/dist/index.js +28 -0
  250. package/dist/modes/commands-core.d.ts +44 -0
  251. package/dist/modes/commands-core.js +171 -0
  252. package/dist/modes/image-input.d.ts +27 -0
  253. package/dist/modes/image-input.js +78 -0
  254. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  255. package/dist/modes/interactive/approval-dialog.js +202 -0
  256. package/dist/modes/interactive/commands.d.ts +41 -0
  257. package/dist/modes/interactive/commands.js +201 -0
  258. package/dist/modes/interactive/completion.d.ts +53 -0
  259. package/dist/modes/interactive/completion.js +157 -0
  260. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  261. package/dist/modes/interactive/interactive-mode.js +491 -0
  262. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  263. package/dist/modes/interactive/key-dispatch.js +107 -0
  264. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  265. package/dist/modes/interactive/line/line-editor.js +232 -0
  266. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  267. package/dist/modes/interactive/line/line-mode.js +218 -0
  268. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  269. package/dist/modes/interactive/line/line-render.js +171 -0
  270. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  271. package/dist/modes/interactive/line/paste-state.js +72 -0
  272. package/dist/modes/interactive/message-view.d.ts +77 -0
  273. package/dist/modes/interactive/message-view.js +269 -0
  274. package/dist/modes/interactive/pickers.d.ts +42 -0
  275. package/dist/modes/interactive/pickers.js +115 -0
  276. package/dist/modes/interactive/startup-ui.d.ts +33 -0
  277. package/dist/modes/interactive/startup-ui.js +268 -0
  278. package/dist/modes/interactive/status-bar.d.ts +48 -0
  279. package/dist/modes/interactive/status-bar.js +139 -0
  280. package/dist/modes/interactive/tool-view.d.ts +95 -0
  281. package/dist/modes/interactive/tool-view.js +344 -0
  282. package/dist/modes/print/json-event.d.ts +15 -0
  283. package/dist/modes/print/json-event.js +39 -0
  284. package/dist/modes/print/print-mode.d.ts +17 -0
  285. package/dist/modes/print/print-mode.js +107 -0
  286. package/dist/modes/rpc/commands.d.ts +40 -0
  287. package/dist/modes/rpc/commands.js +263 -0
  288. package/dist/modes/rpc/jsonl.d.ts +16 -0
  289. package/dist/modes/rpc/jsonl.js +55 -0
  290. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  291. package/dist/modes/rpc/rpc-mode.js +153 -0
  292. package/dist/modes/session-report.d.ts +55 -0
  293. package/dist/modes/session-report.js +278 -0
  294. package/dist/modes/shared.d.ts +13 -0
  295. package/dist/modes/shared.js +52 -0
  296. package/dist/modes/startup-ui-text.d.ts +16 -0
  297. package/dist/modes/startup-ui-text.js +103 -0
  298. package/dist/permissions/broker.d.ts +47 -0
  299. package/dist/permissions/broker.js +107 -0
  300. package/dist/permissions/dangerous.d.ts +47 -0
  301. package/dist/permissions/dangerous.js +370 -0
  302. package/dist/permissions/pipeline.d.ts +51 -0
  303. package/dist/permissions/pipeline.js +181 -0
  304. package/dist/permissions/preview.d.ts +43 -0
  305. package/dist/permissions/preview.js +463 -0
  306. package/dist/permissions/rules.d.ts +58 -0
  307. package/dist/permissions/rules.js +249 -0
  308. package/dist/permissions/types.d.ts +100 -0
  309. package/dist/permissions/types.js +18 -0
  310. package/dist/rpc.d.ts +173 -0
  311. package/dist/rpc.js +13 -0
  312. package/dist/sdk.d.ts +98 -0
  313. package/dist/sdk.js +195 -0
  314. package/dist/session/manager.d.ts +70 -0
  315. package/dist/session/manager.js +278 -0
  316. package/dist/session/migrate.d.ts +19 -0
  317. package/dist/session/migrate.js +53 -0
  318. package/dist/session/projection.d.ts +50 -0
  319. package/dist/session/projection.js +184 -0
  320. package/dist/session/store.d.ts +47 -0
  321. package/dist/session/store.js +210 -0
  322. package/dist/session/tree.d.ts +21 -0
  323. package/dist/session/tree.js +88 -0
  324. package/dist/session/types.d.ts +201 -0
  325. package/dist/session/types.js +18 -0
  326. package/dist/skills/discover.d.ts +60 -0
  327. package/dist/skills/discover.js +160 -0
  328. package/dist/skills/expand.d.ts +35 -0
  329. package/dist/skills/expand.js +46 -0
  330. package/dist/skills/frontmatter.d.ts +27 -0
  331. package/dist/skills/frontmatter.js +157 -0
  332. package/dist/skills/index-prompt.d.ts +13 -0
  333. package/dist/skills/index-prompt.js +42 -0
  334. package/dist/skills/templates.d.ts +52 -0
  335. package/dist/skills/templates.js +177 -0
  336. package/dist/tools/bash.d.ts +49 -0
  337. package/dist/tools/bash.js +222 -0
  338. package/dist/tools/edit-fuzzy.d.ts +36 -0
  339. package/dist/tools/edit-fuzzy.js +129 -0
  340. package/dist/tools/edit.d.ts +38 -0
  341. package/dist/tools/edit.js +241 -0
  342. package/dist/tools/file-mutex.d.ts +8 -0
  343. package/dist/tools/file-mutex.js +29 -0
  344. package/dist/tools/glob.d.ts +44 -0
  345. package/dist/tools/glob.js +217 -0
  346. package/dist/tools/grep.d.ts +39 -0
  347. package/dist/tools/grep.js +179 -0
  348. package/dist/tools/ignore.d.ts +40 -0
  349. package/dist/tools/ignore.js +174 -0
  350. package/dist/tools/image-file.d.ts +31 -0
  351. package/dist/tools/image-file.js +114 -0
  352. package/dist/tools/ls.d.ts +21 -0
  353. package/dist/tools/ls.js +96 -0
  354. package/dist/tools/output-accumulator.d.ts +48 -0
  355. package/dist/tools/output-accumulator.js +106 -0
  356. package/dist/tools/paths.d.ts +20 -0
  357. package/dist/tools/paths.js +58 -0
  358. package/dist/tools/presets.d.ts +71 -0
  359. package/dist/tools/presets.js +128 -0
  360. package/dist/tools/process-tree.d.ts +35 -0
  361. package/dist/tools/process-tree.js +125 -0
  362. package/dist/tools/read.d.ts +24 -0
  363. package/dist/tools/read.js +132 -0
  364. package/dist/tools/registry.d.ts +43 -0
  365. package/dist/tools/registry.js +106 -0
  366. package/dist/tools/shell.d.ts +38 -0
  367. package/dist/tools/shell.js +91 -0
  368. package/dist/tools/task.d.ts +29 -0
  369. package/dist/tools/task.js +107 -0
  370. package/dist/tools/todo.d.ts +28 -0
  371. package/dist/tools/todo.js +107 -0
  372. package/dist/tools/truncate.d.ts +44 -0
  373. package/dist/tools/truncate.js +175 -0
  374. package/dist/tools/types.d.ts +135 -0
  375. package/dist/tools/types.js +19 -0
  376. package/dist/tools/write.d.ts +20 -0
  377. package/dist/tools/write.js +84 -0
  378. package/dist/tui/ansi.d.ts +29 -0
  379. package/dist/tui/ansi.js +420 -0
  380. package/dist/tui/component.d.ts +38 -0
  381. package/dist/tui/component.js +13 -0
  382. package/dist/tui/components/box.d.ts +26 -0
  383. package/dist/tui/components/box.js +65 -0
  384. package/dist/tui/components/container.d.ts +14 -0
  385. package/dist/tui/components/container.js +39 -0
  386. package/dist/tui/components/editor-buffer.d.ts +67 -0
  387. package/dist/tui/components/editor-buffer.js +300 -0
  388. package/dist/tui/components/editor-paste.d.ts +29 -0
  389. package/dist/tui/components/editor-paste.js +64 -0
  390. package/dist/tui/components/editor.d.ts +114 -0
  391. package/dist/tui/components/editor.js +530 -0
  392. package/dist/tui/components/key-value.d.ts +29 -0
  393. package/dist/tui/components/key-value.js +48 -0
  394. package/dist/tui/components/loader.d.ts +35 -0
  395. package/dist/tui/components/loader.js +71 -0
  396. package/dist/tui/components/markdown.d.ts +90 -0
  397. package/dist/tui/components/markdown.js +462 -0
  398. package/dist/tui/components/meter.d.ts +29 -0
  399. package/dist/tui/components/meter.js +47 -0
  400. package/dist/tui/components/overlay.d.ts +24 -0
  401. package/dist/tui/components/overlay.js +52 -0
  402. package/dist/tui/components/select-list.d.ts +57 -0
  403. package/dist/tui/components/select-list.js +179 -0
  404. package/dist/tui/components/spacer.d.ts +11 -0
  405. package/dist/tui/components/spacer.js +16 -0
  406. package/dist/tui/components/text.d.ts +30 -0
  407. package/dist/tui/components/text.js +65 -0
  408. package/dist/tui/keybindings.d.ts +66 -0
  409. package/dist/tui/keybindings.js +120 -0
  410. package/dist/tui/keys.d.ts +38 -0
  411. package/dist/tui/keys.js +275 -0
  412. package/dist/tui/stdin-buffer.d.ts +41 -0
  413. package/dist/tui/stdin-buffer.js +206 -0
  414. package/dist/tui/terminal.d.ts +79 -0
  415. package/dist/tui/terminal.js +153 -0
  416. package/dist/tui/theme.d.ts +31 -0
  417. package/dist/tui/theme.js +199 -0
  418. package/dist/tui/tui.d.ts +103 -0
  419. package/dist/tui/tui.js +363 -0
  420. package/dist/tui/vt-screen.d.ts +46 -0
  421. package/dist/tui/vt-screen.js +251 -0
  422. package/dist/tui.d.ts +30 -0
  423. package/dist/tui.js +31 -0
  424. package/dist/version.d.ts +8 -0
  425. package/dist/version.js +24 -0
  426. package/docs/codemode.md +102 -0
  427. package/docs/hooks.md +196 -0
  428. package/docs/host-api.md +161 -0
  429. package/docs/providers.md +531 -0
  430. package/docs/rpc.md +253 -0
  431. package/docs/session-format.md +119 -0
  432. package/docs/tui.md +170 -0
  433. package/package.json +101 -4
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 单行余量表(第三波 A8):`ctx ▮▮▮▮▮▮▮▯▯▯ 72%`。[W3-B9a-2]
3
+ *
4
+ * - `value` 是 0–1 的比例(越界夹紧);undefined 显示 `—`,格子全空;
5
+ * - 阈值着色(需要 theme):≥ `dangerAt`(缺省 0.9)error,≥ `warnAt`(缺省 0.7)warning,否则 success;
6
+ * 空格子 dim;
7
+ * - 宽度不够时先去掉格子,只剩「标签 百分比」,再不够就截断。
8
+ */
9
+ import { truncateToWidth, visibleWidth } from "../ansi.js";
10
+ export const METER_FULL = "▮";
11
+ export const METER_EMPTY = "▯";
12
+ export class Meter {
13
+ value;
14
+ options;
15
+ constructor(value = undefined, options = {}) {
16
+ this.value = value;
17
+ this.options = options;
18
+ }
19
+ setValue(value) {
20
+ this.value = value;
21
+ }
22
+ /** 当前阈值档位对应的语义色。 */
23
+ level() {
24
+ const v = this.value ?? 0;
25
+ if (v >= (this.options.dangerAt ?? 0.9))
26
+ return "error";
27
+ if (v >= (this.options.warnAt ?? 0.7))
28
+ return "warning";
29
+ return "success";
30
+ }
31
+ render(width) {
32
+ const { theme, label, cells = 10 } = this.options;
33
+ const ratio = this.value === undefined ? undefined : Math.min(1, Math.max(0, this.value));
34
+ const percent = ratio === undefined ? "—" : `${Math.round(ratio * 100)}%`;
35
+ const color = (c, text) => (theme ? theme.fg(c, text) : text);
36
+ const head = label === undefined || label === "" ? "" : `${label} `;
37
+ const filled = ratio === undefined ? 0 : Math.round(ratio * cells);
38
+ const bar = color(this.level(), METER_FULL.repeat(filled)) +
39
+ color("dim", METER_EMPTY.repeat(cells - filled));
40
+ const tail = color(ratio === undefined ? "dim" : this.level(), percent);
41
+ const full = `${head}${bar} ${tail}`;
42
+ if (visibleWidth(full) <= width)
43
+ return [full];
44
+ return [truncateToWidth(`${head}${tail}`, width)];
45
+ }
46
+ invalidate() { }
47
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 覆盖层合成(设计 §12.1、§12.2 第 2 步):只支持 `center` 与 `bottom` 两种锚点。[B4]
3
+ *
4
+ * 覆盖层画在「终端视口」内(内容的最后 height 行);内容不足覆盖层高度时在末尾补空行。
5
+ * 合成发生在差分之前,所以覆盖层的出现 / 消失与普通内容变化一样只重写变化行。
6
+ */
7
+ import type { Component } from "../component.js";
8
+ export type OverlayAnchor = "center" | "bottom";
9
+ export interface OverlayOptions {
10
+ /** 缺省 `center`。 */
11
+ anchor?: OverlayAnchor;
12
+ /** 覆盖层列宽;缺省 center 为 min(width - 4, 80),bottom 为整宽。 */
13
+ width?: number;
14
+ /** 最大行数;缺省为终端高度。 */
15
+ maxHeight?: number;
16
+ }
17
+ export interface OverlayLayer {
18
+ readonly component: Component;
19
+ readonly options: OverlayOptions;
20
+ }
21
+ /** 把一行 base 的 [col, col + w) 列替换为 overlay 行。 */
22
+ export declare function spliceLine(base: string, overlay: string, col: number, w: number, width: number): string;
23
+ /** 按顺序合成全部覆盖层(后加入的在上)。 */
24
+ export declare function compositeOverlays(lines: readonly string[], layers: readonly OverlayLayer[], width: number, height: number): string[];
@@ -0,0 +1,52 @@
1
+ /**
2
+ * 覆盖层合成(设计 §12.1、§12.2 第 2 步):只支持 `center` 与 `bottom` 两种锚点。[B4]
3
+ *
4
+ * 覆盖层画在「终端视口」内(内容的最后 height 行);内容不足覆盖层高度时在末尾补空行。
5
+ * 合成发生在差分之前,所以覆盖层的出现 / 消失与普通内容变化一样只重写变化行。
6
+ */
7
+ import { SGR_RESET, padToWidth, sliceByColumn, truncateToWidth } from "../ansi.js";
8
+ function overlayWidth(options, width) {
9
+ const anchor = options.anchor ?? "center";
10
+ const wanted = options.width ?? (anchor === "bottom" ? width : Math.min(width - 4, 80));
11
+ return Math.max(1, Math.min(width, wanted));
12
+ }
13
+ /** 把一行 base 的 [col, col + w) 列替换为 overlay 行。 */
14
+ export function spliceLine(base, overlay, col, w, width) {
15
+ const left = padToWidth(sliceByColumn(base, 0, col), col);
16
+ const mid = padToWidth(truncateToWidth(overlay, w), w);
17
+ const right = sliceByColumn(base, col + w, width);
18
+ const midReset = mid.includes("\x1b") ? SGR_RESET : "";
19
+ return left + mid + midReset + right;
20
+ }
21
+ /** 按顺序合成全部覆盖层(后加入的在上)。 */
22
+ export function compositeOverlays(lines, layers, width, height) {
23
+ let result = [...lines];
24
+ for (const layer of layers)
25
+ result = compositeOne(result, layer, width, height);
26
+ return result;
27
+ }
28
+ function compositeOne(lines, layer, width, height) {
29
+ const ow = overlayWidth(layer.options, width);
30
+ const maxHeight = Math.max(1, Math.min(height, layer.options.maxHeight ?? height));
31
+ let body = layer.component.render(ow);
32
+ if (body.length > maxHeight)
33
+ body = body.slice(body.length - maxHeight);
34
+ const oh = body.length;
35
+ if (oh === 0)
36
+ return lines;
37
+ const result = [...lines];
38
+ while (result.length < oh)
39
+ result.push("");
40
+ const viewportTop = Math.max(0, result.length - height);
41
+ const visible = result.length - viewportTop;
42
+ const anchor = layer.options.anchor ?? "center";
43
+ const startRow = anchor === "bottom"
44
+ ? result.length - oh
45
+ : viewportTop + Math.max(0, Math.floor((visible - oh) / 2));
46
+ const col = anchor === "bottom" && ow === width ? 0 : Math.floor((width - ow) / 2);
47
+ for (let i = 0; i < oh; i++) {
48
+ const row = startRow + i;
49
+ result[row] = spliceLine(result[row] ?? "", body[i] ?? "", col, ow, width);
50
+ }
51
+ return result;
52
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 选择列表(设计 §12.6):过滤、分页(视窗跟随选中项)、描述列、分组标题。[B4]
3
+ *
4
+ * 键位走 `tui.select.*`:上下移动(循环)、翻页、确认(Enter / Tab)、取消(Esc / Ctrl+C)。
5
+ * `filterable` 时可打印字符追加到过滤词、退格删除;过滤为大小写不敏感的分词子串匹配
6
+ * (每个词都要出现在 label / value / group / description 之一中)。
7
+ */
8
+ import type { Component, Focusable, Theme } from "../component.js";
9
+ import { type Keybindings } from "../keybindings.js";
10
+ export interface SelectItem {
11
+ value: string;
12
+ label: string;
13
+ description?: string;
14
+ /** 相邻项 group 不同时插入分组标题行。 */
15
+ group?: string;
16
+ }
17
+ export interface SelectListOptions {
18
+ /** 一屏最多显示的条目数,缺省 10。 */
19
+ maxVisible?: number;
20
+ theme?: Theme;
21
+ keybindings?: Keybindings;
22
+ filterable?: boolean;
23
+ emptyText?: string;
24
+ onSelect?(item: SelectItem): void;
25
+ onCancel?(): void;
26
+ onSelectionChange?(item: SelectItem | undefined): void;
27
+ }
28
+ export declare function filterItems(items: readonly SelectItem[], filter: string): SelectItem[];
29
+ export declare class SelectList implements Component, Focusable {
30
+ private readonly options;
31
+ focused: boolean;
32
+ private items;
33
+ private filtered;
34
+ private filter;
35
+ private selected;
36
+ private scrollTop;
37
+ private readonly keys;
38
+ constructor(items: readonly SelectItem[], options?: SelectListOptions);
39
+ private get maxVisible();
40
+ setItems(items: readonly SelectItem[]): void;
41
+ getItems(): readonly SelectItem[];
42
+ getFilter(): string;
43
+ setFilter(filter: string): void;
44
+ getSelected(): SelectItem | undefined;
45
+ getSelectedIndex(): number;
46
+ setSelectedIndex(index: number): void;
47
+ /** 按 value 选中。 */
48
+ selectValue(value: string): boolean;
49
+ moveSelection(delta: number, wrap?: boolean): void;
50
+ handleInput(data: string): void;
51
+ render(width: number): string[];
52
+ invalidate(): void;
53
+ private renderItem;
54
+ private labelColumnWidth;
55
+ private applyFilter;
56
+ private ensureVisible;
57
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * 选择列表(设计 §12.6):过滤、分页(视窗跟随选中项)、描述列、分组标题。[B4]
3
+ *
4
+ * 键位走 `tui.select.*`:上下移动(循环)、翻页、确认(Enter / Tab)、取消(Esc / Ctrl+C)。
5
+ * `filterable` 时可打印字符追加到过滤词、退格删除;过滤为大小写不敏感的分词子串匹配
6
+ * (每个词都要出现在 label / value / group / description 之一中)。
7
+ */
8
+ import { padToWidth, truncateToWidth, visibleWidth } from "../ansi.js";
9
+ import { defaultKeybindings } from "../keybindings.js";
10
+ import { isPrintableText, matchesKey } from "../keys.js";
11
+ export function filterItems(items, filter) {
12
+ const terms = filter.toLowerCase().split(/\s+/).filter(Boolean);
13
+ if (terms.length === 0)
14
+ return [...items];
15
+ return items.filter((item) => {
16
+ const hay = [item.label, item.value, item.group ?? "", item.description ?? ""]
17
+ .join("\n")
18
+ .toLowerCase();
19
+ return terms.every((term) => hay.includes(term));
20
+ });
21
+ }
22
+ export class SelectList {
23
+ options;
24
+ focused = false;
25
+ items;
26
+ filtered;
27
+ filter = "";
28
+ selected = 0;
29
+ scrollTop = 0;
30
+ keys;
31
+ constructor(items, options = {}) {
32
+ this.options = options;
33
+ this.items = [...items];
34
+ this.filtered = [...items];
35
+ this.keys = options.keybindings ?? defaultKeybindings;
36
+ }
37
+ get maxVisible() {
38
+ return Math.max(1, this.options.maxVisible ?? 10);
39
+ }
40
+ setItems(items) {
41
+ this.items = [...items];
42
+ this.applyFilter();
43
+ }
44
+ getItems() {
45
+ return this.filtered;
46
+ }
47
+ getFilter() {
48
+ return this.filter;
49
+ }
50
+ setFilter(filter) {
51
+ this.filter = filter;
52
+ this.applyFilter();
53
+ }
54
+ getSelected() {
55
+ return this.filtered[this.selected];
56
+ }
57
+ getSelectedIndex() {
58
+ return this.selected;
59
+ }
60
+ setSelectedIndex(index) {
61
+ if (this.filtered.length === 0)
62
+ return;
63
+ this.selected = Math.max(0, Math.min(this.filtered.length - 1, index));
64
+ this.ensureVisible();
65
+ this.options.onSelectionChange?.(this.getSelected());
66
+ }
67
+ /** 按 value 选中。 */
68
+ selectValue(value) {
69
+ const index = this.filtered.findIndex((item) => item.value === value);
70
+ if (index === -1)
71
+ return false;
72
+ this.setSelectedIndex(index);
73
+ return true;
74
+ }
75
+ moveSelection(delta, wrap = true) {
76
+ const n = this.filtered.length;
77
+ if (n === 0)
78
+ return;
79
+ let next = this.selected + delta;
80
+ if (wrap && Math.abs(delta) === 1)
81
+ next = (next + n) % n;
82
+ this.setSelectedIndex(next);
83
+ }
84
+ handleInput(data) {
85
+ const k = this.keys;
86
+ if (k.matches(data, "tui.select.up"))
87
+ this.moveSelection(-1);
88
+ else if (k.matches(data, "tui.select.down"))
89
+ this.moveSelection(1);
90
+ else if (k.matches(data, "tui.select.pageUp"))
91
+ this.moveSelection(-this.maxVisible, false);
92
+ else if (k.matches(data, "tui.select.pageDown"))
93
+ this.moveSelection(this.maxVisible, false);
94
+ else if (k.matches(data, "tui.select.confirm")) {
95
+ const item = this.getSelected();
96
+ if (item)
97
+ this.options.onSelect?.(item);
98
+ }
99
+ else if (k.matches(data, "tui.select.cancel"))
100
+ this.options.onCancel?.();
101
+ else if (this.options.filterable) {
102
+ if (matchesKey(data, "backspace")) {
103
+ if (this.filter.length > 0)
104
+ this.setFilter([...this.filter].slice(0, -1).join(""));
105
+ }
106
+ else if (isPrintableText(data)) {
107
+ this.setFilter(this.filter + data);
108
+ }
109
+ }
110
+ }
111
+ render(width) {
112
+ const theme = this.options.theme;
113
+ const dim = (s) => (theme ? theme.fg("dim", s) : s);
114
+ const lines = [];
115
+ if (this.options.filterable) {
116
+ const prompt = theme ? theme.fg("accent", "> ") : "> ";
117
+ lines.push(truncateToWidth(prompt + this.filter + (this.focused ? "▏" : ""), width));
118
+ }
119
+ if (this.filtered.length === 0) {
120
+ lines.push(truncateToWidth(dim(` ${this.options.emptyText ?? "(no matches)"}`), width));
121
+ return lines;
122
+ }
123
+ const end = Math.min(this.filtered.length, this.scrollTop + this.maxVisible);
124
+ const labelWidth = this.labelColumnWidth(end);
125
+ let lastGroup;
126
+ for (let i = this.scrollTop; i < end; i++) {
127
+ const item = this.filtered[i];
128
+ if (item.group !== undefined && item.group !== lastGroup) {
129
+ lines.push(truncateToWidth(dim(item.group), width));
130
+ }
131
+ lastGroup = item.group;
132
+ lines.push(this.renderItem(item, i === this.selected, labelWidth, width));
133
+ }
134
+ if (this.filtered.length > this.maxVisible) {
135
+ lines.push(truncateToWidth(dim(` (${this.selected + 1}/${this.filtered.length})`), width));
136
+ }
137
+ return lines;
138
+ }
139
+ invalidate() { }
140
+ renderItem(item, selected, labelWidth, width) {
141
+ const theme = this.options.theme;
142
+ const prefix = selected ? "› " : " ";
143
+ const label = truncateToWidth(item.label, Math.max(1, width - 2));
144
+ let line = prefix + padToWidth(label, Math.min(labelWidth, width - 2));
145
+ if (selected && theme)
146
+ line = theme.fg("accent", theme.bold(line));
147
+ const room = width - visibleWidth(line) - 2;
148
+ if (item.description && room > 4) {
149
+ const desc = truncateToWidth(item.description, room);
150
+ line += " " + (theme ? theme.fg("dim", desc) : desc);
151
+ }
152
+ return truncateToWidth(line, width);
153
+ }
154
+ labelColumnWidth(end) {
155
+ let max = 0;
156
+ for (let i = this.scrollTop; i < end; i++) {
157
+ max = Math.max(max, visibleWidth(this.filtered[i].label));
158
+ }
159
+ return max;
160
+ }
161
+ applyFilter() {
162
+ const previous = this.getSelected()?.value;
163
+ this.filtered = filterItems(this.items, this.filter);
164
+ const keep = previous === undefined ? -1 : this.filtered.findIndex((i) => i.value === previous);
165
+ this.selected = keep === -1 ? 0 : keep;
166
+ this.scrollTop = 0;
167
+ this.ensureVisible();
168
+ this.options.onSelectionChange?.(this.getSelected());
169
+ }
170
+ ensureVisible() {
171
+ if (this.selected < this.scrollTop)
172
+ this.scrollTop = this.selected;
173
+ else if (this.selected >= this.scrollTop + this.maxVisible) {
174
+ this.scrollTop = this.selected - this.maxVisible + 1;
175
+ }
176
+ this.scrollTop = Math.max(0, Math.min(this.scrollTop, this.filtered.length - this.maxVisible));
177
+ this.scrollTop = Math.max(0, this.scrollTop);
178
+ }
179
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 占位空行(设计 §12.1)。[B4]
3
+ */
4
+ import type { Component } from "../component.js";
5
+ export declare class Spacer implements Component {
6
+ private lines;
7
+ constructor(lines?: number);
8
+ setLines(lines: number): void;
9
+ render(_width: number): string[];
10
+ invalidate(): void;
11
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 占位空行(设计 §12.1)。[B4]
3
+ */
4
+ export class Spacer {
5
+ lines;
6
+ constructor(lines = 1) {
7
+ this.lines = lines;
8
+ }
9
+ setLines(lines) {
10
+ this.lines = Math.max(0, lines);
11
+ }
12
+ render(_width) {
13
+ return new Array(this.lines).fill("");
14
+ }
15
+ invalidate() { }
16
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 文本组件(设计 §12.1)。[B4]
3
+ *
4
+ * - `Text`:自动换行(保留 ANSI 样式)、可选左右 / 上下内边距;按 (width, text) 缓存。
5
+ * - `TruncatedText`:只取第一行并截断到宽度(状态栏、标题行)。
6
+ */
7
+ import type { Component } from "../component.js";
8
+ export interface TextOptions {
9
+ paddingX?: number;
10
+ paddingY?: number;
11
+ }
12
+ export declare class Text implements Component {
13
+ private text;
14
+ private cache;
15
+ private readonly paddingX;
16
+ private readonly paddingY;
17
+ constructor(text?: string, options?: TextOptions);
18
+ getText(): string;
19
+ setText(text: string): void;
20
+ render(width: number): string[];
21
+ invalidate(): void;
22
+ }
23
+ export declare class TruncatedText implements Component {
24
+ private text;
25
+ private readonly paddingX;
26
+ constructor(text?: string, paddingX?: number);
27
+ setText(text: string): void;
28
+ render(width: number): string[];
29
+ invalidate(): void;
30
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * 文本组件(设计 §12.1)。[B4]
3
+ *
4
+ * - `Text`:自动换行(保留 ANSI 样式)、可选左右 / 上下内边距;按 (width, text) 缓存。
5
+ * - `TruncatedText`:只取第一行并截断到宽度(状态栏、标题行)。
6
+ */
7
+ import { truncateToWidth, wrapTextWithAnsi } from "../ansi.js";
8
+ export class Text {
9
+ text;
10
+ cache = null;
11
+ paddingX;
12
+ paddingY;
13
+ constructor(text = "", options = {}) {
14
+ this.text = text;
15
+ this.paddingX = options.paddingX ?? 0;
16
+ this.paddingY = options.paddingY ?? 0;
17
+ }
18
+ getText() {
19
+ return this.text;
20
+ }
21
+ setText(text) {
22
+ if (text === this.text)
23
+ return;
24
+ this.text = text;
25
+ this.cache = null;
26
+ }
27
+ render(width) {
28
+ if (this.cache && this.cache.width === width && this.cache.text === this.text) {
29
+ return this.cache.lines;
30
+ }
31
+ const inner = Math.max(1, width - this.paddingX * 2);
32
+ const pad = " ".repeat(this.paddingX);
33
+ const lines = [];
34
+ for (let i = 0; i < this.paddingY; i++)
35
+ lines.push("");
36
+ if (this.text !== "") {
37
+ for (const line of wrapTextWithAnsi(this.text, inner))
38
+ lines.push(pad + line);
39
+ }
40
+ for (let i = 0; i < this.paddingY; i++)
41
+ lines.push("");
42
+ this.cache = { width, text: this.text, lines };
43
+ return lines;
44
+ }
45
+ invalidate() {
46
+ this.cache = null;
47
+ }
48
+ }
49
+ export class TruncatedText {
50
+ text;
51
+ paddingX;
52
+ constructor(text = "", paddingX = 0) {
53
+ this.text = text;
54
+ this.paddingX = paddingX;
55
+ }
56
+ setText(text) {
57
+ this.text = text;
58
+ }
59
+ render(width) {
60
+ const firstLine = this.text.split("\n", 1)[0] ?? "";
61
+ const pad = " ".repeat(this.paddingX);
62
+ return [pad + truncateToWidth(firstLine, Math.max(0, width - this.paddingX * 2))];
63
+ }
64
+ invalidate() { }
65
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * 键位表(设计 §12.3):动作 id → KeyId 列表;固定缺省表 + `keybindings.json` 覆盖。[B4]
3
+ *
4
+ * - `tui.*` 由组件库自己消费(编辑器、选择列表);`app.*` 供交互模式(B7)分派。
5
+ * - 覆盖时整组替换;空数组表示禁用该动作。未知动作 id 与非字符串键会被报告并忽略。
6
+ * - 文件位置由调用方决定(配置目录归 B5),这里只负责解析与合并。
7
+ */
8
+ export declare const DEFAULT_KEYBINDINGS: {
9
+ readonly "tui.editor.submit": readonly ["enter"];
10
+ readonly "tui.editor.newLine": readonly ["shift+enter", "ctrl+j"];
11
+ readonly "tui.editor.cursorLeft": readonly ["left", "ctrl+b"];
12
+ readonly "tui.editor.cursorRight": readonly ["right", "ctrl+f"];
13
+ readonly "tui.editor.cursorUp": readonly ["up", "ctrl+p"];
14
+ readonly "tui.editor.cursorDown": readonly ["down", "ctrl+n"];
15
+ readonly "tui.editor.wordLeft": readonly ["alt+left", "ctrl+left", "alt+b"];
16
+ readonly "tui.editor.wordRight": readonly ["alt+right", "ctrl+right", "alt+f"];
17
+ readonly "tui.editor.lineStart": readonly ["home", "ctrl+a"];
18
+ readonly "tui.editor.lineEnd": readonly ["end", "ctrl+e"];
19
+ readonly "tui.editor.deleteBackward": readonly ["backspace", "shift+backspace"];
20
+ readonly "tui.editor.deleteForward": readonly ["delete"];
21
+ readonly "tui.editor.deleteWordBackward": readonly ["ctrl+w", "alt+backspace", "ctrl+backspace"];
22
+ readonly "tui.editor.deleteWordForward": readonly ["alt+d", "alt+delete", "ctrl+delete"];
23
+ readonly "tui.editor.deleteToLineStart": readonly ["ctrl+u"];
24
+ readonly "tui.editor.deleteToLineEnd": readonly ["ctrl+k"];
25
+ readonly "tui.editor.undo": readonly ["ctrl+z", "ctrl+-"];
26
+ readonly "tui.editor.complete": readonly ["tab"];
27
+ readonly "tui.select.up": readonly ["up", "ctrl+p"];
28
+ readonly "tui.select.down": readonly ["down", "ctrl+n"];
29
+ readonly "tui.select.pageUp": readonly ["pageup"];
30
+ readonly "tui.select.pageDown": readonly ["pagedown"];
31
+ readonly "tui.select.confirm": readonly ["enter", "tab"];
32
+ readonly "tui.select.cancel": readonly ["escape", "ctrl+c"];
33
+ readonly "app.interrupt": readonly ["escape"];
34
+ readonly "app.clear": readonly ["ctrl+c"];
35
+ readonly "app.exit": readonly ["ctrl+d"];
36
+ readonly "app.message.followUp": readonly ["alt+enter"];
37
+ readonly "app.message.dequeue": readonly ["alt+up"];
38
+ readonly "app.permission.cycle": readonly ["shift+tab"];
39
+ readonly "app.tools.expand": readonly ["ctrl+o"];
40
+ readonly "app.model.select": readonly ["ctrl+l"];
41
+ readonly "app.thinking.select": readonly ["ctrl+t"];
42
+ };
43
+ export type ActionId = keyof typeof DEFAULT_KEYBINDINGS;
44
+ export type KeybindingOverrides = Partial<Record<ActionId, readonly string[]>>;
45
+ export interface ParsedKeybindings {
46
+ overrides: KeybindingOverrides;
47
+ /** 被忽略的条目说明(未知动作、类型错误)。 */
48
+ warnings: string[];
49
+ }
50
+ export declare function isActionId(id: string): id is ActionId;
51
+ /** 解析 keybindings.json 的对象形状:`{ "<action>": "key" | ["key", ...] }`。 */
52
+ export declare function parseKeybindings(value: unknown): ParsedKeybindings;
53
+ /** 读取并解析 keybindings.json;文件不存在返回空覆盖,JSON 错误记入 warnings。 */
54
+ export declare function loadKeybindingsFile(path: string): ParsedKeybindings;
55
+ export declare class Keybindings {
56
+ private readonly table;
57
+ constructor(overrides?: KeybindingOverrides);
58
+ /** 动作绑定的 KeyId 列表(已规范化)。 */
59
+ keys(action: ActionId): readonly string[];
60
+ /** 原始输入数据是否触发该动作。 */
61
+ matches(data: string, action: ActionId): boolean;
62
+ /** 输入数据触发的全部动作(按缺省表顺序),用于分派。 */
63
+ actionsFor(data: string): ActionId[];
64
+ }
65
+ /** 进程内共享的缺省键位(组件未显式传入时使用)。 */
66
+ export declare const defaultKeybindings: Keybindings;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * 键位表(设计 §12.3):动作 id → KeyId 列表;固定缺省表 + `keybindings.json` 覆盖。[B4]
3
+ *
4
+ * - `tui.*` 由组件库自己消费(编辑器、选择列表);`app.*` 供交互模式(B7)分派。
5
+ * - 覆盖时整组替换;空数组表示禁用该动作。未知动作 id 与非字符串键会被报告并忽略。
6
+ * - 文件位置由调用方决定(配置目录归 B5),这里只负责解析与合并。
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+ import { normalizeKeyId, parseKey } from "./keys.js";
10
+ export const DEFAULT_KEYBINDINGS = {
11
+ "tui.editor.submit": ["enter"],
12
+ "tui.editor.newLine": ["shift+enter", "ctrl+j"],
13
+ "tui.editor.cursorLeft": ["left", "ctrl+b"],
14
+ "tui.editor.cursorRight": ["right", "ctrl+f"],
15
+ "tui.editor.cursorUp": ["up", "ctrl+p"],
16
+ "tui.editor.cursorDown": ["down", "ctrl+n"],
17
+ "tui.editor.wordLeft": ["alt+left", "ctrl+left", "alt+b"],
18
+ "tui.editor.wordRight": ["alt+right", "ctrl+right", "alt+f"],
19
+ "tui.editor.lineStart": ["home", "ctrl+a"],
20
+ "tui.editor.lineEnd": ["end", "ctrl+e"],
21
+ "tui.editor.deleteBackward": ["backspace", "shift+backspace"],
22
+ "tui.editor.deleteForward": ["delete"],
23
+ "tui.editor.deleteWordBackward": ["ctrl+w", "alt+backspace", "ctrl+backspace"],
24
+ "tui.editor.deleteWordForward": ["alt+d", "alt+delete", "ctrl+delete"],
25
+ "tui.editor.deleteToLineStart": ["ctrl+u"],
26
+ "tui.editor.deleteToLineEnd": ["ctrl+k"],
27
+ "tui.editor.undo": ["ctrl+z", "ctrl+-"],
28
+ "tui.editor.complete": ["tab"],
29
+ "tui.select.up": ["up", "ctrl+p"],
30
+ "tui.select.down": ["down", "ctrl+n"],
31
+ "tui.select.pageUp": ["pageup"],
32
+ "tui.select.pageDown": ["pagedown"],
33
+ "tui.select.confirm": ["enter", "tab"],
34
+ "tui.select.cancel": ["escape", "ctrl+c"],
35
+ "app.interrupt": ["escape"],
36
+ "app.clear": ["ctrl+c"],
37
+ "app.exit": ["ctrl+d"],
38
+ "app.message.followUp": ["alt+enter"],
39
+ "app.message.dequeue": ["alt+up"],
40
+ "app.permission.cycle": ["shift+tab"],
41
+ "app.tools.expand": ["ctrl+o"],
42
+ "app.model.select": ["ctrl+l"],
43
+ "app.thinking.select": ["ctrl+t"],
44
+ };
45
+ export function isActionId(id) {
46
+ return Object.prototype.hasOwnProperty.call(DEFAULT_KEYBINDINGS, id);
47
+ }
48
+ /** 解析 keybindings.json 的对象形状:`{ "<action>": "key" | ["key", ...] }`。 */
49
+ export function parseKeybindings(value) {
50
+ const overrides = {};
51
+ const warnings = [];
52
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
53
+ return { overrides: {}, warnings: ["keybindings 必须是对象"] };
54
+ }
55
+ for (const [action, keys] of Object.entries(value)) {
56
+ if (!isActionId(action)) {
57
+ warnings.push(`未知动作:${action}`);
58
+ continue;
59
+ }
60
+ const list = typeof keys === "string" ? [keys] : keys;
61
+ if (!Array.isArray(list) || !list.every((k) => typeof k === "string")) {
62
+ warnings.push(`${action}:键必须是字符串或字符串数组`);
63
+ continue;
64
+ }
65
+ overrides[action] = list.map((k) => normalizeKeyId(k));
66
+ }
67
+ return { overrides: overrides, warnings };
68
+ }
69
+ /** 读取并解析 keybindings.json;文件不存在返回空覆盖,JSON 错误记入 warnings。 */
70
+ export function loadKeybindingsFile(path) {
71
+ let text;
72
+ try {
73
+ text = readFileSync(path, "utf8");
74
+ }
75
+ catch (error) {
76
+ if (error.code === "ENOENT")
77
+ return { overrides: {}, warnings: [] };
78
+ return { overrides: {}, warnings: [`读取 ${path} 失败:${error.message}`] };
79
+ }
80
+ try {
81
+ return parseKeybindings(JSON.parse(text));
82
+ }
83
+ catch (error) {
84
+ return { overrides: {}, warnings: [`${path} 不是合法 JSON:${error.message}`] };
85
+ }
86
+ }
87
+ export class Keybindings {
88
+ table;
89
+ constructor(overrides = {}) {
90
+ this.table = new Map();
91
+ for (const action of Object.keys(DEFAULT_KEYBINDINGS)) {
92
+ const keys = overrides[action] ?? DEFAULT_KEYBINDINGS[action];
93
+ this.table.set(action, keys.map((k) => normalizeKeyId(k)));
94
+ }
95
+ }
96
+ /** 动作绑定的 KeyId 列表(已规范化)。 */
97
+ keys(action) {
98
+ return this.table.get(action) ?? [];
99
+ }
100
+ /** 原始输入数据是否触发该动作。 */
101
+ matches(data, action) {
102
+ const key = parseKey(data);
103
+ if (key === undefined)
104
+ return false;
105
+ return this.keys(action).includes(key.id);
106
+ }
107
+ /** 输入数据触发的全部动作(按缺省表顺序),用于分派。 */
108
+ actionsFor(data) {
109
+ const key = parseKey(data);
110
+ if (key === undefined)
111
+ return [];
112
+ const out = [];
113
+ for (const [action, keys] of this.table)
114
+ if (keys.includes(key.id))
115
+ out.push(action);
116
+ return out;
117
+ }
118
+ }
119
+ /** 进程内共享的缺省键位(组件未显式传入时使用)。 */
120
+ export const defaultKeybindings = new Keybindings();