@visual-z/xeno 0.2.0 → 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 (259) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +106 -26
  3. package/docs/install.md +52 -9
  4. package/docs/providers.md +11 -0
  5. package/package.json +5 -4
  6. package/src/apps/approve.ts +3 -3
  7. package/src/apps/cli.ts +10 -11
  8. package/src/apps/commands.ts +38 -5
  9. package/src/apps/completions.ts +73 -19
  10. package/src/apps/image-draft.ts +46 -0
  11. package/src/apps/project-trust.ts +2 -42
  12. package/src/apps/provider-connect.ts +346 -0
  13. package/src/apps/render.ts +22 -47
  14. package/src/apps/resources.ts +5 -2
  15. package/src/apps/shell.ts +298 -1526
  16. package/src/apps/startup-input.ts +130 -0
  17. package/src/apps/startup.ts +143 -115
  18. package/src/apps/ui/app.ts +1383 -0
  19. package/src/apps/ui/extension-host.ts +194 -0
  20. package/src/apps/ui/image-cache.ts +78 -0
  21. package/src/apps/ui/palette.ts +88 -0
  22. package/src/apps/ui/project.ts +127 -0
  23. package/src/apps/ui/standalone.ts +146 -0
  24. package/src/apps/ui/status-model.ts +68 -0
  25. package/src/apps/ui/surface.ts +84 -0
  26. package/src/apps/ui/theme.ts +86 -0
  27. package/src/core/acceptance.ts +107 -0
  28. package/src/core/action-defaults.ts +47 -295
  29. package/src/core/benchmark.ts +2 -2
  30. package/src/core/branch-receipt.ts +24 -0
  31. package/src/core/checkpoints.ts +64 -0
  32. package/src/core/churn.ts +2 -0
  33. package/src/core/command-artifacts.ts +94 -0
  34. package/src/core/command-contract.ts +35 -0
  35. package/src/core/completion-review.ts +21 -5
  36. package/src/core/context-detail.ts +37 -0
  37. package/src/core/context-policy.ts +74 -1
  38. package/src/core/continuation.ts +17 -2
  39. package/src/core/derive.ts +29 -3
  40. package/src/core/draft-images.ts +56 -0
  41. package/src/core/environment.ts +145 -0
  42. package/src/core/evaluation-coverage.ts +74 -0
  43. package/src/core/event-validation.ts +80 -5
  44. package/src/core/events.ts +97 -3
  45. package/src/core/evidence-links.ts +7 -0
  46. package/src/core/evidence.ts +13 -3
  47. package/src/core/execution.ts +8 -2
  48. package/src/core/experiment.ts +133 -0
  49. package/src/core/export.ts +9 -0
  50. package/src/core/failure-recovery.ts +173 -0
  51. package/src/core/file-precondition.ts +72 -0
  52. package/src/core/handoff.ts +65 -4
  53. package/src/core/history-query.ts +38 -2
  54. package/src/core/history.ts +38 -1
  55. package/src/core/integration-health.ts +34 -0
  56. package/src/core/keymap.ts +8 -20
  57. package/src/core/model-picker.ts +64 -0
  58. package/src/core/native-eval.ts +25 -0
  59. package/src/core/note-guidance.ts +34 -0
  60. package/src/core/observation-view.ts +49 -8
  61. package/src/core/permissions.ts +24 -2
  62. package/src/core/process-receipt.ts +105 -0
  63. package/src/core/process-state.ts +37 -0
  64. package/src/core/project.ts +45 -18
  65. package/src/core/prompt-policy.ts +37 -0
  66. package/src/core/query-eval-contract.ts +14 -0
  67. package/src/core/read-observation.ts +82 -0
  68. package/src/core/recovery-corpus.ts +17 -0
  69. package/src/core/recovery-evaluation.ts +60 -0
  70. package/src/core/recovery-sources.ts +74 -0
  71. package/src/core/runtime-target.ts +94 -0
  72. package/src/core/search-observation.ts +97 -0
  73. package/src/core/session-tree.ts +147 -0
  74. package/src/core/settings.ts +26 -10
  75. package/src/core/skills.ts +44 -0
  76. package/src/core/sql-query.ts +211 -0
  77. package/src/core/theme.ts +36 -58
  78. package/src/core/tool-contract.ts +51 -2
  79. package/src/core/tool-discovery.ts +87 -0
  80. package/src/core/tool-failure.ts +60 -0
  81. package/src/core/tool-mode-protocol.ts +25 -0
  82. package/src/core/transcript.ts +19 -4
  83. package/src/core/trial-lifecycle.ts +14 -0
  84. package/src/core/tui-mode.ts +29 -0
  85. package/src/core/update-check.ts +97 -0
  86. package/src/core/version.ts +55 -0
  87. package/src/core/work-state.ts +49 -3
  88. package/src/core/working-set.ts +91 -0
  89. package/src/runtime/auth.ts +2 -2
  90. package/src/runtime/branch-jobs.ts +36 -0
  91. package/src/runtime/clipboard.ts +15 -4
  92. package/src/runtime/command-artifacts.ts +64 -0
  93. package/src/runtime/environment-tools.ts +60 -0
  94. package/src/runtime/evidence-notes.ts +4 -2
  95. package/src/runtime/exec-sessions.ts +206 -61
  96. package/src/runtime/execution-control.ts +1 -0
  97. package/src/runtime/extension-ui.ts +22 -30
  98. package/src/runtime/extensions.ts +95 -16
  99. package/src/runtime/harness-comparison.ts +27 -7
  100. package/src/runtime/harness-eval.ts +460 -0
  101. package/src/runtime/history-cache.ts +57 -0
  102. package/src/runtime/history-tools.ts +62 -12
  103. package/src/runtime/integrations.ts +210 -0
  104. package/src/runtime/loop.ts +1021 -268
  105. package/src/runtime/model-discovery.ts +208 -0
  106. package/src/runtime/native-eval-fixtures.ts +33 -0
  107. package/src/runtime/native-eval.ts +143 -0
  108. package/src/runtime/oauth.ts +1 -1
  109. package/src/runtime/provider-connect.ts +109 -0
  110. package/src/runtime/provider-presets.ts +87 -0
  111. package/src/runtime/query-eval-verifier.ts +28 -0
  112. package/src/runtime/recovery-coverage.ts +71 -0
  113. package/src/runtime/recovery-evidence.ts +76 -0
  114. package/src/runtime/recovery-fixtures.ts +116 -0
  115. package/src/runtime/runtime-target.ts +149 -0
  116. package/src/runtime/tool-broker.ts +137 -0
  117. package/src/runtime/tool-discovery.ts +57 -0
  118. package/src/runtime/tool-mode-eval.ts +146 -0
  119. package/src/runtime/tool-queue.ts +77 -0
  120. package/src/runtime/tools.ts +628 -183
  121. package/src/runtime/trial-control.ts +45 -0
  122. package/src/runtime/update-check.ts +47 -0
  123. package/src/runtime/window-trial.ts +2 -1
  124. package/src/runtime/working-notes.ts +33 -3
  125. package/src/runtime/workspace-lifecycle.ts +97 -0
  126. package/src/store/acceptance.ts +15 -0
  127. package/src/store/checkpoint-history.ts +43 -0
  128. package/src/store/command-artifacts.ts +96 -0
  129. package/src/store/command.ts +65 -37
  130. package/src/store/file-checkpoints.ts +104 -0
  131. package/src/store/file-index.ts +13 -1
  132. package/src/store/git-checkpoints.ts +67 -0
  133. package/src/store/observations.ts +61 -20
  134. package/src/store/process-receipts.ts +34 -0
  135. package/src/store/query-source.ts +13 -8
  136. package/src/store/recovery.ts +2 -3
  137. package/src/store/replay.ts +16 -4
  138. package/src/store/runtime-target.ts +64 -0
  139. package/src/store/search.ts +171 -132
  140. package/src/store/session-archive.ts +4 -0
  141. package/src/store/source-cancellation.ts +6 -0
  142. package/src/store/update-cache.ts +36 -0
  143. package/src/store/workspace-observation.ts +113 -0
  144. package/src/tui/ansi-spans.ts +126 -0
  145. package/src/tui/style.ts +19 -16
  146. package/src/tui/ui/atomic-draft.ts +121 -0
  147. package/src/tui/ui/canvas.ts +96 -0
  148. package/src/tui/ui/colors.ts +70 -0
  149. package/src/tui/ui/composer.ts +199 -0
  150. package/src/tui/ui/dialog.ts +379 -0
  151. package/src/tui/ui/draft.ts +23 -0
  152. package/src/tui/ui/edge.ts +186 -0
  153. package/src/tui/ui/image-size.ts +40 -0
  154. package/src/tui/ui/ink.ts +210 -0
  155. package/src/tui/ui/items.ts +40 -0
  156. package/src/tui/ui/keys.ts +40 -0
  157. package/src/tui/ui/layout.ts +74 -0
  158. package/src/tui/ui/list-model.ts +86 -0
  159. package/src/tui/ui/list-view.ts +172 -0
  160. package/src/tui/ui/mascot.ts +67 -0
  161. package/src/tui/ui/messages.ts +518 -0
  162. package/src/tui/ui/motion.ts +28 -0
  163. package/src/tui/ui/paths.ts +10 -0
  164. package/src/tui/ui/pictures.ts +78 -0
  165. package/src/tui/ui/screen.ts +421 -0
  166. package/src/tui/ui/status.ts +271 -0
  167. package/src/tui/ui/styled.ts +85 -0
  168. package/src/tui/ui/testing.ts +5 -0
  169. package/src/tui/ui/tool-card.ts +150 -0
  170. package/src/apps/extension-ui.ts +0 -330
  171. package/src/apps/resource-browser.ts +0 -75
  172. package/src/apps/session-browser.ts +0 -113
  173. package/src/apps/session-picker.ts +0 -55
  174. package/src/tui/ansi.ts +0 -67
  175. package/src/tui/autocomplete.ts +0 -29
  176. package/src/tui/chat-viewport.ts +0 -47
  177. package/src/tui/complete.ts +0 -87
  178. package/src/tui/editor.ts +0 -940
  179. package/src/tui/host.ts +0 -264
  180. package/src/tui/input.ts +0 -75
  181. package/src/tui/keys.ts +0 -378
  182. package/src/tui/markdown.ts +0 -17
  183. package/src/tui/render.ts +0 -153
  184. package/src/tui/select.ts +0 -258
  185. package/src/tui/settings.ts +0 -44
  186. package/src/tui/statusline.ts +0 -162
  187. package/src/tui/system-theme.ts +0 -700
  188. package/src/tui/terminal.ts +0 -30
  189. package/src/tui/text.ts +0 -52
  190. package/src/tui/toolview.ts +0 -386
  191. package/src/tui/transcript.ts +0 -123
  192. package/src/tui/tree.ts +0 -471
  193. package/src/vendor/pi-tui/LICENSE.upstream +0 -21
  194. package/src/vendor/pi-tui/UPSTREAM.json +0 -50
  195. package/src/vendor/pi-tui/VENDOR.md +0 -25
  196. package/src/vendor/pi-tui/alt-screen-search.ts +0 -327
  197. package/src/vendor/pi-tui/autocomplete.ts +0 -860
  198. package/src/vendor/pi-tui/colors.ts +0 -367
  199. package/src/vendor/pi-tui/components/alt-screen-flash.ts +0 -51
  200. package/src/vendor/pi-tui/components/box.ts +0 -166
  201. package/src/vendor/pi-tui/components/cancellable-loader.ts +0 -40
  202. package/src/vendor/pi-tui/components/editor.ts +0 -2472
  203. package/src/vendor/pi-tui/components/h-stack.ts +0 -44
  204. package/src/vendor/pi-tui/components/image.ts +0 -127
  205. package/src/vendor/pi-tui/components/input.ts +0 -494
  206. package/src/vendor/pi-tui/components/loader.ts +0 -101
  207. package/src/vendor/pi-tui/components/markdown.ts +0 -1021
  208. package/src/vendor/pi-tui/components/mouse-region.ts +0 -33
  209. package/src/vendor/pi-tui/components/scroll-view.ts +0 -224
  210. package/src/vendor/pi-tui/components/select-list.ts +0 -273
  211. package/src/vendor/pi-tui/components/settings-list.ts +0 -328
  212. package/src/vendor/pi-tui/components/spacer.ts +0 -28
  213. package/src/vendor/pi-tui/components/stack.ts +0 -154
  214. package/src/vendor/pi-tui/components/text.ts +0 -107
  215. package/src/vendor/pi-tui/components/truncated-text.ts +0 -65
  216. package/src/vendor/pi-tui/components/v-stack.ts +0 -33
  217. package/src/vendor/pi-tui/editor-component.ts +0 -74
  218. package/src/vendor/pi-tui/fuzzy.ts +0 -138
  219. package/src/vendor/pi-tui/index.ts +0 -186
  220. package/src/vendor/pi-tui/keybindings.ts +0 -320
  221. package/src/vendor/pi-tui/keys.ts +0 -1401
  222. package/src/vendor/pi-tui/kill-ring.ts +0 -46
  223. package/src/vendor/pi-tui/latex.ts +0 -1506
  224. package/src/vendor/pi-tui/layout-node.ts +0 -51
  225. package/src/vendor/pi-tui/layout.ts +0 -449
  226. package/src/vendor/pi-tui/native/UPSTREAM.json +0 -23
  227. package/src/vendor/pi-tui/native/clipboard.h +0 -208
  228. package/src/vendor/pi-tui/native/darwin/README.md +0 -22
  229. package/src/vendor/pi-tui/native/darwin/build.sh +0 -69
  230. package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  231. package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  232. package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +0 -132
  233. package/src/vendor/pi-tui/native/linux/README.md +0 -23
  234. package/src/vendor/pi-tui/native/linux/build.sh +0 -65
  235. package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  236. package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  237. package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +0 -70
  238. package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +0 -274
  239. package/src/vendor/pi-tui/native/napi.h +0 -91
  240. package/src/vendor/pi-tui/native/win32/README.md +0 -31
  241. package/src/vendor/pi-tui/native/win32/build.mjs +0 -232
  242. package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  243. package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  244. package/src/vendor/pi-tui/native/win32/src/win32-platform.c +0 -241
  245. package/src/vendor/pi-tui/native-modifiers.ts +0 -13
  246. package/src/vendor/pi-tui/native-module-path.ts +0 -31
  247. package/src/vendor/pi-tui/native-platform.ts +0 -65
  248. package/src/vendor/pi-tui/oklab.ts +0 -233
  249. package/src/vendor/pi-tui/stdin-buffer.ts +0 -444
  250. package/src/vendor/pi-tui/terminal-colors.ts +0 -91
  251. package/src/vendor/pi-tui/terminal-image.ts +0 -731
  252. package/src/vendor/pi-tui/terminal.ts +0 -554
  253. package/src/vendor/pi-tui/tui-alt-screen.ts +0 -1750
  254. package/src/vendor/pi-tui/tui-main-screen.ts +0 -655
  255. package/src/vendor/pi-tui/tui.ts +0 -1493
  256. package/src/vendor/pi-tui/undo-stack.ts +0 -28
  257. package/src/vendor/pi-tui/utils.ts +0 -1385
  258. package/src/vendor/pi-tui/wheel-scroll.ts +0 -82
  259. package/src/vendor/pi-tui/word-navigation.ts +0 -117
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # xeno changelog
2
2
 
3
+ ## 0.3.0 — 2026-10-07
4
+
5
+ - Replace the vendored Pi terminal UI with OpenTUI: a growing composer, command palette, model and login pickers, responsive status surfaces, terminal colours and bounded message rendering.
6
+ - Keep inline image tags atomic through cursor movement, deletion, undo/redo, history and queued drafts; send only retained attachments and preview the image beside the cursor. Share bounded image decoding across drafts, messages and tool results.
7
+ - Add plain-text mouse selection copying and optional daily version notices, with offline and non-interactive opt-outs.
8
+ - Preserve tool admission and nested-call receipts, cancellable process ownership, deferred discovery, recoverable output and integration health handling. Code Mode is removed.
9
+ - Strengthen file edit preconditions, workspace lifecycle and completion evidence; opening the editor no longer requires an eager workspace snapshot.
10
+ - Require Bun 1.3.0 or newer for the native renderer. Existing session logs remain append-only.
11
+
12
+ ## 0.2.1 — 2026-09-30
13
+
14
+ - Handle Ctrl+C and termination signals while startup awaits resources or workspace snapshots, restoring the terminal before exit.
15
+ - Show the current phase during slow initialization and retain early input until Pi's editor or startup selector owns it.
16
+ - Preserve pasted control characters, Unicode input and terminal state across startup failures and selector transitions.
17
+
3
18
  ## 0.2.0 — 2026-09-30
4
19
 
5
20
  - Restore saved model identity on session entry; cross-model history moves through a mechanical checkpoint.
package/README.md CHANGED
@@ -17,7 +17,7 @@ is ever summarized by a model, and nothing is ever rewritten in place.
17
17
  messages[] frozen system prefix + handoff + this node's events
18
18
  ```
19
19
 
20
- Version **0.2.0**. The on-disk session format will still change; `0.x` is that
20
+ Version **0.3.0**. The on-disk session format will still change; `0.x` is that
21
21
  statement. Published as `@visual-z/xeno`; the license is `UNLICENSED` (readable and installable, no reuse granted).
22
22
 
23
23
  ## Install
@@ -52,6 +52,7 @@ Validation commands and current evidence are recorded in
52
52
  - Writing branches start from the parent's working copy, including uncommitted files, in an isolated Git worktree. Adopt continues that checkout.
53
53
  - Independent readers and read-only branches run in a bounded window; writes and state changes are barriers. Results remain in request order.
54
54
  - `note(kind="pin", work_id, event, what)` links original observations; `supersedes` replaces an earlier reference. `history(work_id=...)`, `history(resource=...)`, and `history(events=[...])` locate or batch-read the originals.
55
+ - Verified history projections use a bounded per-session cache. Original ancestry and sealed digests are checked before every hit; a lost cache is rebuilt from the log.
55
56
 
56
57
  Use `/new` or `--fresh` to start with the current tool protocol. Existing sessions
57
58
  keep their saved frozen schemas and concurrency policy. New sessions default to
@@ -78,6 +79,17 @@ authorized `--budget unlimited` on both `plan` and `run` records costs without a
78
79
  dollar ceiling; upstream errors stop the trial, with HTTP 402 marked as awaiting top-up.
79
80
  This checks a controlled configuration, not stock Pi or an official leaderboard.
80
81
 
82
+ The new single-factor protocol separately compares tool profiles, the carried
83
+ working set, and strict verification. It runs four isolated scenarios through
84
+ real sessions and grades files, fresh command evidence, and pending human review.
85
+ Use `bun scripts/eval-harness.ts smoke --output /absolute/new/eval-directory`,
86
+ then `bun scripts/eval-harness.ts audit --output /absolute/new/eval-directory`.
87
+ Smoke has no paid requests and reports synthetic usage. `plan` and `run` require
88
+ the same explicit numeric `--budget`; inputs, source, schemas, model settings,
89
+ and paired order are frozen before spending. Started directories cannot be
90
+ replayed automatically. Failures and missing trials remain in the denominator,
91
+ and unknown charges retain their reservation. Details are in `docs/benchmarks.md`.
92
+
81
93
  ## Usage
82
94
 
83
95
  ```
@@ -92,6 +104,8 @@ xeno [-m <model>] [--provider <name>] [--api-key <key>]
92
104
  [--system-prompt <text-or-file>] [--append-system-prompt <text>]
93
105
  [--no-context-files|-nc]
94
106
  [--tools names] [--exclude-tools names] [--no-tools]
107
+ [--tool-profile coding|readonly|research|data|full]
108
+ [--acceptance-file <contract.json>]
95
109
  [--allow-tools names] [--approve|--no-approve]
96
110
  [--theme <name|path>] [--use-theme <name>] [--no-themes]
97
111
  [--tui-mode regular|fullscreen]
@@ -102,6 +116,28 @@ xeno [-m <model>] [--provider <name>] [--api-key <key>]
102
116
  ```
103
117
 
104
118
  - No prompt, a tty: interactive session.
119
+ - `/acceptance` shows declared command checks and human criteria. `/accept ID`
120
+ records your acceptance of a human criterion; subsequent edits invalidate it.
121
+ Use `--acceptance-file` or a trusted project's `.xeno/acceptance.json` with
122
+ `{ "version": 1, "id": "task", "criteria": [{ "id": "tests", "kind": "command", "description": "tests pass", "command": "bun test" }] }`.
123
+ The declaration is frozen in the session. A normal execution exit does not
124
+ imply that human acceptance has been granted.
125
+ A contract may additionally declare `targets`, each with `id`, `build`
126
+ (a command criterion ID), `artifacts: { paths, output_roots }`, and `entry`
127
+ (one of those artifact paths). Optional `interpreter` is an absolute executable
128
+ path; `args` are literal strings, `workdir` stays inside the workspace, and
129
+ `port` declares an IPv4 loopback listener. A human criterion binds to it with
130
+ `target: "ID"`. Enable the `target` tool explicitly with `--tools` or the
131
+ `full` profile; the default coding profile stays compact. Run the declared
132
+ build with its exact artifact plan, then `target({action:"start",target:"ID"})`.
133
+ `inspect` rechecks the owned instance, build inputs, artifact/executable identity
134
+ and port ownership. `/acceptance` shows the entry, build, PID and session;
135
+ `/accept ID` binds your decision to that observed instance. Rebuilds, changed
136
+ files, process exits or lost ownership require fresh acceptance. The first
137
+ implementation observes macOS executable identity with the system `lsof` and
138
+ Linux identity with `/proc`; port checks require system `lsof`. Missing OS
139
+ evidence remains unknown. It never adopts an existing server or proves visual
140
+ correctness automatically, and a resumed session cannot reattach an old PID.
105
141
  - A prompt, or `-p` / `--print`: one shot, then exit. `-p` puts only the
106
142
  assistant's final text on stdout; the banner, usage, thinking, and tool
107
143
  lines go to stderr. Piped stdin is appended after the command-line prompt.
@@ -231,19 +267,43 @@ Project trust (`/trust`, `--approve`) only decides whether to load
231
267
  It does not change what tools can do. `AGENTS.md` is loaded whether the
232
268
  project is trusted or not.
233
269
 
270
+ New sessions default to the nine-tool `coding` profile: read, grep, find,
271
+ write, edit, exec_command, write_stdin, history and note. `readonly`,
272
+ `research` (with branch/resolve), `data` (with query) and `full` choose other
273
+ builtin defaults. Extensions remain available; explicit `--tools` overrides
274
+ the profile and exclusions apply last. Resumed sessions keep their frozen tools.
275
+
234
276
  `--tools` / `--exclude-tools` / `--no-tools` remove tools from the schema
235
- the model sees. `--allow-tools bash` lets bash run without asking, which
277
+ the model sees. `--allow-tools exec_command` lets commands run without asking, which
236
278
  is what a CI job that has no tty has to pass explicitly. Without a tty,
237
279
  an `ask` is a deny that names that flag.
238
280
 
239
281
  ## Workspace data
240
282
 
283
+ Opening a session does not snapshot the working directory. File tools save the
284
+ original bytes before editing and record the observed result. `/back` restores
285
+ only recorded paths, preserves unrelated files, and refuses conflicting external
286
+ edits. Conversation-only navigation leaves files unchanged.
287
+
288
+ Shell effects are not automatically reversible. Opt in for a new session with
289
+ `--checkpoint-shell` to capture eligible Git files around commands. This uses a
290
+ private index and object store without changing your staging area or HEAD. Git
291
+ ignored paths, non-regular files, untracked files over 2 MiB, and effects outside
292
+ the workspace are excluded. Capture is best effort; unavailable recovery does
293
+ not prevent a conversation or change the command's exit result. A running process
294
+ has a pending checkpoint until its exit is observed; overlapping writes make that
295
+ checkpoint unavailable. Initial capture can take time.
296
+
297
+ Verification observes a bounded source scope separately from file recovery.
298
+ Unknown observations cannot certify a completion claim. Old snapshot logs remain
299
+ readable; new file records and scope markers are appended without rewriting them.
300
+
241
301
  A session writes into the workspace:
242
302
 
243
303
  | Path | |
244
304
  |---|---|
245
305
  | `.xeno/sessions/<id>/` | node logs, handoffs, metadata journal, derived manifest, and observation artifacts |
246
- | `.xeno/blobs/` | content-addressed file snapshots used by `/back` |
306
+ | `.xeno/blobs/` | content-addressed before/after file versions used by `/back` |
247
307
 
248
308
  That tree grows with every turn and every snapshot. There is no garbage
249
309
  collection yet. Dead branches from `/back` stay on disk.
@@ -297,16 +357,29 @@ Shipped, not planned:
297
357
  Repeated content, renamed completion claims and gate regressions do not reset
298
358
  the three-segment stop rule. This is an anti-stall heuristic, not proof of
299
359
  relevance or completion; [`research M.4`](docs/agent-harness-research.md) states its limits.
300
- - A terminal UI using the pinned Pi renderer, raw input, editor, autocomplete,
301
- Markdown, searchable pickers and settings. Durable history is a pure log
302
- projection across handoff boundaries. Includes tool diffs, historical folds,
303
- Pi dark/light palettes, `keybindings.json`, `/settings` and `/hotkeys`.
360
+ - A terminal UI using pinned OpenTUI, a native text editor, autocomplete,
361
+ Markdown, a command palette and searchable pickers. Durable history is a pure
362
+ log projection across handoff boundaries. Includes tool diffs, historical
363
+ folds, terminal colours, `keybindings.json`, `/settings` and `/hotkeys`.
304
364
 
305
365
  - Context files: `AGENTS.md` / `CLAUDE.md` and the override chain, plus
306
366
  `--system-prompt` / `--append-system-prompt` / `-nc`.
307
367
  - Skills (`~/.xeno/skills/`, `/skill:name`) and prompt templates
308
- (`~/.xeno/prompts/`, `/name`). Untrusted projects do not load the
368
+ support progressive loading, user visibility, explicit tool intersections
369
+ and isolated `context: fork`. Unsupported execution fields are diagnosed.
370
+ Loaded instructions keep frozen source hashes and recoverable artifacts.
371
+ Templates use `~/.xeno/prompts/` and `/name`. Untrusted projects do not load
309
372
  project-level copies.
373
+ - `/tasks` shows dependency readiness and claims. Long tasks can use
374
+ `note(kind="next", work_id="b", depends_on=["a"], ...)`, then
375
+ `claim_work` / `release_work`; these are scheduling intent, separate from
376
+ the `done` command proof. Ordinary short tasks do not need a graph.
377
+ - `branch(background=true)` returns an owned task reference. `/branches`
378
+ lists its current capability; `/branches wait|cancel|status <node>` joins or
379
+ controls it without a model request. `resolve` exposes the same actions.
380
+ Restarted unfinished tasks remain unknown; sealed outcomes can be joined.
381
+ - `/processes` separates the last logged observation from a live handle.
382
+ Session exit drains its own processes and archives unread output.
310
383
  - `/reload` refreshes local models, settings, keybindings, context files, skills,
311
384
  templates and themes without losing the draft or active model. A changed system
312
385
  prefix checkpoints first; unchanged resources do not create a new segment.
@@ -330,7 +403,11 @@ Shipped, not planned:
330
403
  Copilot subscription flows. See Known gaps.
331
404
 
332
405
  Images can be attached with `@path`, a dropped image path, or Ctrl+V (files,
333
- image, then text). `/attachments` removes draft images; queue recall and history
406
+ image, then text). Pasted and dropped images become `[Image #N]` tags at the
407
+ caret. Left/Right cross a whole tag; Backspace/Delete remove it in one step,
408
+ and Undo restores it. Moving the caret next to a tag shows its preview above
409
+ the input (filename and dimensions when image display is unavailable).
410
+ `/attachments` remains available for removal; queue recall and history
334
411
  forks retain them. The log stores immutable blob references. Provider adapters
335
412
  load verified bytes for Anthropic, Chat Completions and Responses; text-only
336
413
  models receive a visible placeholder. Custom models declare `input: ["text", "image"]`
@@ -360,27 +437,30 @@ unblocking affects new submissions, preserving historical request prefixes.
360
437
  - **`/back` does not restore symlinks.**
361
438
  - **`.xeno/` is not collected.** Snapshots and abandoned session branches
362
439
  stay until you delete them.
363
- - **No telemetry, `/share`, auto-update, or `/compact`.** Those are
364
- refusals (`docs/PI_PARITY.md` D15, D16), not backlog.
440
+ - **No telemetry, `/share`, self-installing updates, or `/compact`.** Those
441
+ are refusals (`docs/PI_PARITY.md` D15, D16), not backlog. xeno does say
442
+ when a newer version is published, at most once a day and never installing
443
+ it; `docs/install.md` (Updates) lists what that request reveals and every
444
+ way to turn it off.
365
445
 
366
446
  `docs/TUI_ARCHITECTURE.md` describes the active interaction path and verification.
367
447
  `docs/PI_PARITY.md` records deliberate differences and remaining Pi features.
368
- Pi's editor supports undo, not redo; an old `editor.redo` binding is diagnosed
369
- as unsupported rather than silently accepted. Ctrl+- invokes Pi undo.
448
+ The native editor supports undo (Ctrl+_ or Ctrl+/) and redo (Ctrl+Shift+Z).
449
+ Registered image tags move and delete as whole units and share text undo history.
370
450
  Ctrl+Z suspends the CLI on POSIX; `fg` restores the terminal and unsent draft.
371
451
 
372
- `/settings tuiMode fullscreen` uses Pi's alternate-screen viewport, transcript
373
- search (Ctrl+Shift+F), prompt navigation (Ctrl+Up/Down), mouse scrolling and text
374
- selection. `/settings fullscreenExitOutput resume-hint` leaves a resume command
375
- when exiting; `transcript` prints the conversation. Switching modes preserves
376
- the editor, history and undo. Scrollbar, wheel speed, copy-on-select, editor
377
- padding, autocomplete rows, cursor, output padding and double-Escape behavior
378
- are configurable in `/settings`.
452
+ Fullscreen is the default. Ctrl+P opens commands, Ctrl+L selects a model,
453
+ PgUp/PgDn scroll, and Ctrl+Home/End jump to the start or latest messages.
454
+ Ctrl+O expands tools, Ctrl+T expands thinking, and Ctrl+B toggles the sidebar.
455
+ Mouse selection copies plain text when `fullscreenCopyOnSelect` is enabled;
456
+ Ctrl+X copies a selection or the last reply. Exiting restores the terminal and
457
+ prints the conversation and a resume command. The composer stays at the bottom
458
+ and grows with the draft; pickers and forms use the same input area.
379
459
 
380
460
  `/settings theme` previews built-in and custom themes from the config directory's
381
461
  `themes/`. Escape restores the prior theme. Custom files reload after valid edits;
382
- `auto` follows terminal light/dark changes and `system` derives Pi's palette from
383
- terminal color replies. Pi's namespaced action IDs work in `keybindings.json`
462
+ `auto` follows terminal light/dark changes. Built-in themes keep the terminal's
463
+ background and derive readable accents from its colour replies. Namespaced action IDs work in `keybindings.json`
384
464
  alongside existing xeno names; an empty array disables an action in its own scope.
385
465
 
386
466
  ## Why this shape
@@ -575,10 +655,10 @@ Every path below was checked with `test -f` on this checkout.
575
655
  | `runtime/benchmark.ts` | actual Session trials, bounded spending and cache diagnostics |
576
656
  | `runtime/loop.ts` | orchestration; every judgement delegates to a pure function in core |
577
657
  | `runtime/auth.ts` / `oauth.ts` | credential order and subscription flows |
578
- | **`src/vendor/pi-tui/`** | unmodified Pi terminal and component library, pinned in `VENDOR.md` |
579
- | **`src/tui/`** | Pi input, component and theme adapters; no harness imports |
580
- | `tui/markdown.ts` / `toolview.ts` / `statusline.ts` / `tree.ts` | display |
581
- | **`src/apps/`** | `cli.ts` — Pi main-screen UI for TTY, clean print/JSON output otherwise |
658
+ | **`src/tui/`** | OpenTUI view components and pure display helpers; no harness imports |
659
+ | `tui/ui/composer.ts` / `atomic-draft.ts` / `messages.ts` / `screen.ts` | native editing, atomic image tags, messages and layout |
660
+ | **`src/apps/`** | `cli.ts` — OpenTUI for TTY, clean print/JSON output otherwise |
661
+ | `apps/ui/app.ts` / `project.ts` / `status-model.ts` | controller, transcript projection and status facts |
582
662
  | `apps/startup.ts` / `shell.ts` / `commands.ts` | flags, session, slash commands |
583
663
 
584
664
  ## Properties the tests pin
package/docs/install.md CHANGED
@@ -16,7 +16,7 @@ The entry point is `src/apps/cli.ts` with `#!/usr/bin/env bun`. The
16
16
  process uses Bun APIs (`Bun.file`, `Bun.spawn`, `Bun.spawnSync`,
17
17
  `for await (const line of console)`). Those are why the runtime is Bun.
18
18
 
19
- `engines.bun` is `>=1.1.0`. This checkout was run on **Bun 1.3.10**.
19
+ `engines.bun` is `>=1.3.0` (required by the OpenTUI renderer, D20). This checkout was run on **Bun 1.3.10**.
20
20
  Node is untested. That is a fact about this tree, not a promise that
21
21
  Node support is coming.
22
22
 
@@ -30,16 +30,14 @@ cd xeno
30
30
  bun install
31
31
  ```
32
32
 
33
- `bun install` pulls the provider SDK, Pi text layout/Markdown dependencies,
34
- syntax highlighting, Mermaid, Photon image processing, and TypeScript tooling.
33
+ `bun install` pulls the provider SDKs, OpenTUI and its platform renderer,
34
+ Markdown, syntax highlighting, Mermaid, Photon image processing, and TypeScript tooling.
35
35
  The exact versions are in `package.json` and `bun.lock`.
36
36
 
37
- Pi native helpers for macOS, Linux X11 and Windows (arm64/x64) ship with the
38
- package. They are pinned upstream builds with matching source and hashes;
39
- startup does not download or compile them. Missing platform support uses the
40
- clipboard command fallbacks. The packaged macOS helper was load-tested on
41
- this machine; Linux/Windows payloads and routing are checked without claiming
42
- native device acceptance.
37
+ OpenTUI supplies the native renderer through its platform dependencies.
38
+ Clipboard integration uses terminal OSC 52 and platform commands; the old
39
+ vendored Pi native helpers are no longer packaged. Release package checks run
40
+ on macOS arm64; they do not establish Linux or Windows device acceptance.
43
41
 
44
42
  Run the CLI without linking:
45
43
 
@@ -68,6 +66,50 @@ Each invocation starts a new session. `xeno -c` continues the newest one;
68
66
  and nothing is deleted. Resume restores the saved model. An explicit
69
67
  `--model` switches at a checkpoint while keeping the original log intact.
70
68
 
69
+ Startup loads resources and opens or restores the session. Workspace snapshots
70
+ are deferred until file tools need them, so opening an editor does not scan the
71
+ whole project. Slow initialization shows its current stage; Ctrl+C cancels it
72
+ and restores the terminal. Start inside the intended project or pass
73
+ `--workspace <path>`.
74
+
75
+ ## Updates
76
+
77
+ xeno says when a newer version is published. It never installs one: the
78
+ notice names the command, and you run it.
79
+
80
+ ```
81
+ xeno 0.3.0 is available (you have 0.2.1)
82
+ bun add -g @visual-z/xeno@latest
83
+ ```
84
+
85
+ The command follows where this copy is installed: `bun add -g` under
86
+ `~/.bun/install/global/`, `npm i -g` under an npm global prefix
87
+ (`…/lib/node_modules/`, or `…\npm\node_modules\` on Windows). Anywhere else
88
+ the notice points to this file. A checkout updates with `git pull`.
89
+
90
+ - **Request.** `GET https://registry.npmjs.org/-/package/@visual-z%2fxeno/dist-tags`
91
+ (an 18-byte answer). If that fails, `GET …/@visual-z%2fxeno/latest`.
92
+ `npm_config_registry` replaces the host. The timeout is 3 seconds.
93
+ - **What the registry learns.** Your IP address, the time, the package name
94
+ and Bun's default `User-Agent` (`Bun/<version>`). No identifier, no query
95
+ string, nothing from your session.
96
+ - **How often.** At most once every 24 hours. The time and the version seen
97
+ are kept in `~/.xeno/update-check.json`. A failed attempt is recorded too,
98
+ so a closed network is not retried on every start.
99
+ - **When.** In the background after the editor opens. Startup never waits for
100
+ it, and quitting cancels it. A version found during a session is shown only
101
+ while that session is still empty. Otherwise it waits for the next start,
102
+ so it never interrupts a turn.
103
+
104
+ Nothing is sent when any of these holds:
105
+
106
+ - `--offline` or `XENO_OFFLINE=1`
107
+ - `XENO_NO_UPDATE_CHECK` or `NO_UPDATE_NOTIFIER` is set (to anything but empty, `0` or `false`)
108
+ - `"updateCheck": false` in `settings.json` (*Check for updates* in `/settings`)
109
+ - `CI` or `CONTINUOUS_INTEGRATION` is set, by the same rule
110
+ - the run is not the interactive app: `-p`, `--mode json`, `--export`, `--list-models`, `models --refresh`, package and `config` commands, `--version`
111
+ - xeno runs from a checkout: its package root is not under `node_modules`, or has a `.git` (`bun src/apps/cli.ts`, `bun run agent`, `bun link`)
112
+
71
113
  ## Credentials
72
114
 
73
115
  Resolution order, first match wins:
@@ -124,6 +166,7 @@ settings.json theme, thinking, defaults
124
166
  trust.json which directories may load project settings
125
167
  models.json optional user provider/model configuration
126
168
  models-cache.json generated catalog cache (written by `xeno models --refresh`)
169
+ update-check.json when the last update check ran, and the newest version it saw
127
170
  keybindings.json optional
128
171
  prompts/ skills/ optional
129
172
  ```
package/docs/providers.md CHANGED
@@ -4,6 +4,17 @@ xeno 内置 Pi 0.99.1 和 OpenCode 所用 Models.dev 的固定源并集:226
4
4
 
5
5
  来源固定为 Pi `1b347794e2a630e4359f2584f4eea388145d0ddf`、OpenCode `2fa3363c924c5c3e367b84a87ae478296a0ed59b`,Models.dev JSON SHA256 `7345de7358c471e78ac756aed556cd804677cd0d8748b71edc3931225440158f`。SDK 固定为 `@earendil-works/pi-ai@0.99.1`;引入它是为了复用维护中的原生协议和签名处理,避免在 xeno 重写十套 SDK。SDK 仅在 runtime;原有 TUI vendor 的版本与升级方式独立。
6
6
 
7
+ ## 最快的接入:`/login`
8
+
9
+ 在交互界面输入 `/login`(`/login <provider>` 直达;`/connect` 是同一命令的别名)。一个列表里包含全部接入方式:
10
+
11
+ - **订阅账户**:Claude Pro/Max、ChatGPT Plus/Pro(Codex)、GitHub Copilot,走浏览器或设备码登录。
12
+ - **API key**:选择目录中的供应商(Anthropic、OpenAI、Gemini、DeepSeek、OpenRouter、Kimi、智谱、硅基流动、通义、豆包等,可搜索),粘贴 key(掩码显示;`$MY_KEY` 读取环境变量)。
13
+ - **+ Custom endpoint…**:OpenAI Chat Completions / OpenAI Responses / Anthropic Messages / Gemini 兼容地址,或 Ollama、LM Studio。填 base URL(粘贴完整的 `…/chat/completions`、`…/v1/messages` 会自动截断)、ID 和 key 后,xeno 用这个 key 向端点列出模型(OpenAI `GET /models`;Anthropic `GET /v1/models`;Gemini `GET /v1beta/models`),勾选要启用的模型后保存。
14
+ - **⚙ Manage connected…**:重新列出并选择模型、更换 key 或断开。
15
+
16
+ `/model` 只显示已接入且凭据可用的模型,最近使用的排在最前。保存位置与手写配置相同:key 在 `~/.xeno/auth.json`(0600),端点和启用的模型在 `~/.xeno/models.json` 的 `providers.<id>`。
17
+
7
18
  ## 目录和选择
8
19
 
9
20
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visual-z/xeno",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "description": "Segmented append-only agent log with pure context and handoff projections",
@@ -15,10 +15,11 @@
15
15
  "docs/providers.md"
16
16
  ],
17
17
  "engines": {
18
- "bun": ">=1.1.0"
18
+ "bun": ">=1.3.0"
19
19
  },
20
20
  "repository": {
21
- "type": "git"
21
+ "type": "git",
22
+ "url": "git+https://github.com/visual-z/xeno.git"
22
23
  },
23
24
  "scripts": {
24
25
  "test": "bun test",
@@ -37,8 +38,8 @@
37
38
  "dependencies": {
38
39
  "@anthropic-ai/sdk": "^0.127.0",
39
40
  "@earendil-works/pi-ai": "0.99.1",
41
+ "@opentui/core": "0.5.14",
40
42
  "@silvia-odwyer/photon-node": "0.3.4",
41
- "get-east-asian-width": "1.6.0",
42
43
  "grok-mermaid": "0.2.3",
43
44
  "highlight.js": "10.7.3",
44
45
  "marked": "18.0.11"
@@ -9,7 +9,7 @@
9
9
 
10
10
  import type { PermissionRequest } from "../core/permissions.ts";
11
11
  import type { ApprovalChoice } from "../runtime/tools.ts";
12
- import type { SelectItem } from "../tui/select.ts";
12
+ import type { ListItem } from "../tui/ui/list-model.ts";
13
13
 
14
14
  export function approvalHeading(request: PermissionRequest): string {
15
15
  if (request.command !== undefined) {
@@ -24,8 +24,8 @@ export function approvalHeading(request: PermissionRequest): string {
24
24
  return `${request.tool} wants to run. Approval is the last look, not a boundary.`;
25
25
  }
26
26
 
27
- export function approvalItems(request: PermissionRequest): SelectItem[] {
28
- const items: SelectItem[] = [{ value: "once", label: "this once" }];
27
+ export function approvalItems(request: PermissionRequest): ListItem[] {
28
+ const items: ListItem[] = [{ value: "once", label: "this once" }];
29
29
  if (request.command !== undefined) {
30
30
  items.push({ value: "command", label: "this command this session" });
31
31
  }
package/src/apps/cli.ts CHANGED
@@ -13,18 +13,16 @@
13
13
  */
14
14
 
15
15
  import {
16
- parseArgs, startup, readPipedStdin, mergePrintPrompt, silenceTtyEarly, printCliHelp,
16
+ parseArgs, startup, readPipedStdin, mergePrintPrompt, isInteractiveSession, printCliHelp,
17
17
  } from "./startup.ts";
18
+ import { createStartupInput } from "./startup-input.ts";
18
19
  import { runPrompt, runRepl } from "./shell.ts";
19
20
  import { runPackageCommand } from "../runtime/package-manager.ts";
20
21
  import { exportHtmlFile } from "./export-cli.ts";
21
22
 
22
23
  const args = parseArgs(process.argv.slice(2), { deferExtensions: true });
23
- // Before startup reads auth, scans context files and prints the header:
24
- // in cooked mode the tty would echo anything typed during that window
25
- // straight into the banner.
26
- const restoreTty = silenceTtyEarly({ interactive: !args.packageCommand && !args.help && !args.version && !args.exportFile && !args.print && !args.prompt });
27
- process.once("exit", restoreTty);
24
+ const input = createStartupInput({ interactive: isInteractiveSession(args) && !args.packageCommand && !args.version && !args.exportFile && !args.listModels && !args.modelsCommand });
25
+ process.once("exit", input.restore);
28
26
  try {
29
27
  if (args.help && (args.packageCommand || args.configCommand || args.exportFile || args.version || args.listModels || args.modelsCommand)) {
30
28
  printCliHelp();
@@ -44,22 +42,23 @@ try {
44
42
  catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = controller.signal.aborted ? 130 : 1; }
45
43
  finally { process.off("SIGINT", cancel); process.off("SIGTERM", cancel); }
46
44
  } else {
47
- const app = await startup(args);
45
+ const app = await startup(args, input);
48
46
  if (args.print) {
49
47
  // With a prompt already given, stdin is optional context: wait briefly for it.
50
48
  args.prompt = mergePrintPrompt(args.prompt,
51
49
  await readPipedStdin(process.stdin, args.prompt.length > 0 ? { firstByteMs: 3_000 } : {}));
52
50
  }
53
51
  if (args.prompt) {
54
- restoreTty();
52
+ input.restore();
55
53
  await runPrompt(app, args.prompt);
56
54
  } else {
57
- await runRepl(app, restoreTty);
55
+ input.phase("Loading conversation");
56
+ await runRepl(app, input.handoff);
58
57
  }
59
58
  }
60
59
  } catch (error) {
61
60
  console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1;
62
61
  } finally {
63
- process.off("exit", restoreTty);
64
- restoreTty();
62
+ process.off("exit", input.restore);
63
+ input.restore();
65
64
  }
@@ -16,10 +16,16 @@ import { skillInvocationMessage, type Skill } from "../core/skills.ts";
16
16
 
17
17
  export type Command =
18
18
  | { kind: "extension"; name: string; args: string }
19
- | { kind: "prompt"; text: string }
19
+ | { kind: "prompt"; text: string; skill?: Skill }
20
20
  | { kind: "quit" }
21
21
  | { kind: "usage" }
22
22
  | { kind: "state" }
23
+ | { kind: "context" }
24
+ | { kind: "acceptance" }
25
+ | { kind: "tasks" }
26
+ | { kind: "branches"; action?: "wait" | "cancel" | "status"; node?: number }
27
+ | { kind: "processes" }
28
+ | { kind: "accept"; criterion: string }
23
29
  | { kind: "model"; pattern?: string }
24
30
  | { kind: "scoped-models" }
25
31
  | { kind: "login"; provider?: string }
@@ -54,7 +60,7 @@ export const COMMANDS = [
54
60
  { name: "scoped-models", description: "Enable and order models for cycling", busy: "idle" },
55
61
  { name: "thinking", description: "Set reasoning effort", argumentHint: "[level]", busy: "idle" },
56
62
  { name: "settings", description: "Configure appearance, input and model behavior", argumentHint: "[key value]", busy: "idle" },
57
- { name: "login", description: "Sign in or store an API key", argumentHint: "[provider]", busy: "idle" },
63
+ { name: "login", description: "Connect a provider: subscription, API key, gateway or local server", argumentHint: "[provider]", busy: "idle" },
58
64
  { name: "logout", description: "Remove a stored credential", argumentHint: "[provider]", busy: "idle" },
59
65
  { name: "new", description: "Start a new session", busy: "idle" },
60
66
  { name: "resume", description: "Search saved sessions", argumentHint: "[id]", busy: "idle" },
@@ -66,6 +72,12 @@ export const COMMANDS = [
66
72
  { name: "session", description: "Show session details", busy: "run" },
67
73
  { name: "usage", description: "Show token usage and costs", busy: "run" },
68
74
  { name: "state", description: "Show log and handoff state", busy: "run" },
75
+ { name: "context", description: "Explain context sources, omissions and token estimates", busy: "run" },
76
+ { name: "acceptance", description: "Show declared criteria and current evidence", busy: "idle" },
77
+ { name: "tasks", description: "Show work dependencies, readiness and ownership", busy: "run" },
78
+ { name: "branches", description: "Inspect or join owned background work", argumentHint: "[wait|cancel|status node]", busy: "idle" },
79
+ { name: "processes", description: "Show process observations and available handles", busy: "run" },
80
+ { name: "accept", description: "Record your acceptance of a human criterion", argumentHint: "<criterion ID>", busy: "idle" },
69
81
  { name: "export", description: "Export HTML or JSONL", argumentHint: "[file]", busy: "idle" },
70
82
  { name: "import", description: "Import a portable JSONL session", argumentHint: "<file>", busy: "idle" },
71
83
  { name: "debug", description: "Write local terminal diagnostics", argumentHint: "[file]", busy: "run" },
@@ -84,8 +96,15 @@ export const COMMANDS = [
84
96
 
85
97
  export const BUILTIN_COMMAND_NAMES = COMMANDS.map((command) => command.name);
86
98
 
99
+ /**
100
+ * Hidden spellings of a listed command. Not in COMMANDS, so the `/` menu,
101
+ * the startup hint and completion show one entry per job.
102
+ */
103
+ const ALIASES: Readonly<Record<string, string>> = { connect: "login" };
104
+
87
105
  export function commandDefinition(input: string): (typeof COMMANDS)[number] | undefined {
88
- const name = input.trim().split(/\s/, 1)[0]?.slice(1).toLowerCase();
106
+ const typed = input.trim().split(/\s/, 1)[0]?.slice(1).toLowerCase() ?? "";
107
+ const name = ALIASES[typed] ?? typed;
89
108
  return COMMANDS.find((command) => command.name === name);
90
109
  }
91
110
 
@@ -95,6 +114,7 @@ export interface SlashCatalog {
95
114
  readonly prompts: ReadonlyMap<string, PromptTemplate>;
96
115
  readonly skills: ReadonlyMap<string, Skill>;
97
116
  readonly skillCommands?: boolean;
117
+ readonly skillExecution?: boolean;
98
118
  }
99
119
 
100
120
  export function resolveSlashCatalog(
@@ -140,7 +160,8 @@ export function parseCommand(input: string, catalog?: SlashCatalog): Command {
140
160
  if (!text.startsWith("/")) return { kind: "prompt", text };
141
161
 
142
162
  const [head, ...rest] = text.split(/\s+/);
143
- const token = (head ?? "").toLowerCase();
163
+ const typed = (head ?? "").toLowerCase();
164
+ const token = `/${ALIASES[typed.slice(1)] ?? typed.slice(1)}`;
144
165
  const arg = rest.join(" ");
145
166
 
146
167
  if (!commandDefinition(text)) return resolveRegistered(token, arg, text, rest.length, catalog);
@@ -157,6 +178,17 @@ export function parseCommand(input: string, catalog?: SlashCatalog): Command {
157
178
  return { kind: "usage" };
158
179
  case "/state":
159
180
  return { kind: "state" };
181
+ case "/context": return { kind: "context" };
182
+ case "/acceptance": return { kind: "acceptance" };
183
+ case "/tasks": return { kind: "tasks" };
184
+ case "/processes": return { kind: "processes" };
185
+ case "/branches": {
186
+ if (!arg) return { kind: "branches" };
187
+ const action = rest[0], node = Number(rest[1]);
188
+ return (action === "wait" || action === "cancel" || action === "status") && Number.isSafeInteger(node) && node >= 0 && rest.length === 2
189
+ ? { kind: "branches", action, node } : { kind: "unknown", input: text };
190
+ }
191
+ case "/accept": return { kind: "accept", criterion: arg };
160
192
  case "/model":
161
193
  return arg.length > 0 ? { kind: "model", pattern: arg } : { kind: "model" };
162
194
  case "/login":
@@ -240,7 +272,8 @@ function resolveRegistered(
240
272
  const name = token.slice("/skill:".length);
241
273
  const skill = catalog.skills.get(name);
242
274
  if (skill !== undefined) {
243
- return { kind: "prompt", text: skillInvocationMessage(skill, arg) };
275
+ if (skill.userInvocable === false) return { kind: "unknown", input: text };
276
+ return { kind: "prompt", text: skillInvocationMessage(skill, arg), ...(catalog.skillExecution ? { skill } : {}) };
244
277
  }
245
278
  }
246
279
  const prompt = catalog.prompts.get(token.slice(1));