carthagent 1.0.7 → 1.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/README.md +140 -83
  2. package/bin/ctg.mjs +14 -732
  3. package/docs/beta-rewards-e2e-plan.md +48 -0
  4. package/docs/beta-rewards-e2e-results.md +84 -0
  5. package/docs/glyph-review/host-chrome-glyph-compatibility-v4.md +83 -0
  6. package/docs/glyph-review/test-isolation-v1.md +26 -0
  7. package/docs/glyph-review/typecheck-base.log +1 -0
  8. package/docs/glyph-review/typecheck-v4.log +1 -0
  9. package/docs/glyph-review/unicode-inventory-v4.csv +402 -0
  10. package/docs/help-dispatch-review.md +69 -0
  11. package/docs/launch-readiness-live-results.md +65 -0
  12. package/docs/legacy-cli.md +150 -0
  13. package/docs/live-theme-plugin-api.md +18 -0
  14. package/docs/native-cli.md +102 -0
  15. package/docs/native-compatibility.md +16 -0
  16. package/docs/native-daily-quests.md +132 -0
  17. package/docs/native-glyph-pty.md +59 -0
  18. package/docs/native-manual-testing.md +336 -0
  19. package/docs/native-slash-acceptance-handoff.md +49 -0
  20. package/docs/native-terminal-paired-failure-2026-10-17.md +40 -0
  21. package/docs/native-terminal-performance.md +310 -0
  22. package/docs/native-terminal-semantics-evidence/README.md +25 -0
  23. package/docs/native-terminal-semantics-evidence/controlled-host-model-selftest.json +16 -0
  24. package/docs/native-terminal-semantics-evidence/current-acceptance-status.json +59 -0
  25. package/docs/native-terminal-semantics-evidence/final-binary-focused-85bb44a.json +154 -0
  26. package/docs/native-terminal-semantics-evidence/final-binary-full-incomplete-85bb44a.json +160 -0
  27. package/docs/native-terminal-semantics-evidence/final-binary-layout-85bb44a.json +149 -0
  28. package/docs/native-terminal-semantics-evidence/final-binary-layout-interactive-85bb44a.json +159 -0
  29. package/docs/native-terminal-semantics-evidence/final-binary-model-picker-escape-unchanged-85bb44a.json +44 -0
  30. package/docs/native-terminal-semantics-evidence/final-binary-model-picker-exact-identity-85bb44a.json +47 -0
  31. package/docs/native-terminal-semantics-evidence/final-binary-model-picker-unchanged-model-incomplete-85bb44a.json +58 -0
  32. package/docs/native-terminal-semantics-evidence/final-binary-model-selftest.json +16 -0
  33. package/docs/native-terminal-semantics-evidence/final-binary-picker-cancel-incomplete-85bb44a.json +62 -0
  34. package/docs/native-terminal-semantics-evidence/focused-clipboard-title-stale-binary.json +161 -0
  35. package/docs/native-terminal-semantics-evidence/layout-stale-binary-observation-not-accepted.json +153 -0
  36. package/docs/native-terminal-semantics-evidence/picker-latest-incomplete-attempt.json +62 -0
  37. package/docs/native-terminal-semantics-evidence/picker-previous-probe-pass.json +61 -0
  38. package/docs/native-terminal-semantics.md +89 -0
  39. package/docs/native-upgrade.md +175 -0
  40. package/docs/opencode-composed-source-review.md +89 -0
  41. package/docs/opencode-migration.md +225 -9
  42. package/docs/opencode-owned-runtime.md +241 -0
  43. package/docs/opencode-runtime.md +12 -0
  44. package/docs/opencode-theme-provider-proof/README.md +55 -0
  45. package/docs/opencode-theme-provider-proof/live-theme-api-base.patch +122 -0
  46. package/docs/opencode-theme-provider-proof/plugin-theme-provider.patch +107 -0
  47. package/docs/opencode-theme-provider-proof/session-ui-base.patch +1217 -0
  48. package/docs/owned-host-controls-proof.md +39 -0
  49. package/docs/owned-runtime-release-manifest.md +42 -0
  50. package/docs/owned-visual-clear-proof.md +40 -0
  51. package/docs/patches/opencode-v2.0.32-glyph-spinner-test-isolation-v1.patch +50 -0
  52. package/docs/patches/opencode-v2.0.32-glyphs.patch +3670 -0
  53. package/docs/patches/opencode-v2.0.32-help-dispatch.patch +156 -0
  54. package/docs/patches/opencode-v2.0.32-live-theme-api.patch +122 -0
  55. package/docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch +5272 -0
  56. package/docs/patches/opencode-v2.0.32-session-ui-combined-v2.patch +6052 -0
  57. package/docs/patches/opencode-v2.0.32-session-ui-combined-v3.patch +8657 -0
  58. package/docs/patches/opencode-v2.0.32-session-ui-combined-v4.patch +9928 -0
  59. package/docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch +13965 -0
  60. package/docs/patches/opencode-v2.0.32-session-ui.patch +5137 -0
  61. package/docs/release-notes-beta.1.md +60 -0
  62. package/docs/release-notes-native-draft.md +73 -0
  63. package/docs/release-notes-preview.10.md +121 -0
  64. package/docs/release-notes-preview.11.md +76 -0
  65. package/docs/release-notes-preview.12.md +55 -0
  66. package/docs/release-notes-preview.13.md +68 -0
  67. package/docs/release-notes-preview.9.md +130 -0
  68. package/docs/source-preview.md +120 -0
  69. package/evaluate.mjs +28 -1
  70. package/lib/budget.mjs +1 -1
  71. package/lib/codegraph.mjs +1 -1
  72. package/lib/delivery-review.mjs +19 -6
  73. package/lib/delivery.mjs +3 -2
  74. package/lib/environment.mjs +8 -0
  75. package/lib/guidance.mjs +1 -1
  76. package/lib/opencode/cli-runtime.mjs +1171 -0
  77. package/lib/opencode/cli.mjs +252 -0
  78. package/lib/opencode/credentials.mjs +63 -8
  79. package/lib/opencode/demo.mjs +53 -0
  80. package/lib/opencode/evaluation-budget-plugin/index.ts +3 -0
  81. package/lib/opencode/evaluation.mjs +32 -12
  82. package/lib/opencode/headless.mjs +22 -5
  83. package/lib/opencode/provider-policy.mjs +23 -0
  84. package/lib/opencode/review.mjs +37 -16
  85. package/lib/opencode/runtime-artifact.mjs +335 -0
  86. package/lib/opencode/runtime-github-transport.mjs +121 -0
  87. package/lib/opencode/runtime-release.json +36 -0
  88. package/lib/opencode/runtime.mjs +299 -20
  89. package/lib/opencode/service.mjs +77 -35
  90. package/lib/opencode/session-options.mjs +6 -2
  91. package/lib/opencode/update.mjs +222 -0
  92. package/lib/opencode/workspace.mjs +1 -1
  93. package/lib/planning-advice.mjs +2 -2
  94. package/lib/providers/experiential.mjs +2 -2
  95. package/lib/tui/framing.mjs +1 -80
  96. package/lib/update.mjs +10 -1
  97. package/package.json +33 -7
  98. package/plugins/carthagent/README.md +23 -0
  99. package/plugins/carthagent/src/bootstrap.ts +1 -1
  100. package/plugins/carthagent/src/daily-evidence.mjs +57 -0
  101. package/plugins/carthagent/src/daily-quests-rpc.ts +50 -0
  102. package/plugins/carthagent/src/daily-source-rpc.ts +52 -0
  103. package/plugins/carthagent/src/index.ts +71 -24
  104. package/plugins/carthagent/src/rpc.ts +51 -6
  105. package/plugins/carthagent/src/server/codegraph.ts +12 -5
  106. package/plugins/carthagent/src/server/daily-quests-rpc.ts +277 -0
  107. package/plugins/carthagent/src/server/daily-source-rpc.ts +243 -0
  108. package/plugins/carthagent/src/server/delivery.ts +227 -112
  109. package/plugins/carthagent/src/server/framing.mjs +80 -0
  110. package/plugins/carthagent/src/server/policy.ts +1 -1
  111. package/plugins/carthagent/src/server/providers.ts +59 -31
  112. package/plugins/carthagent/src/server/session-controls.ts +3 -1
  113. package/plugins/carthagent/src/server/ship-agents.ts +77 -0
  114. package/plugins/carthagent/src/server/ship-context.mjs +60 -0
  115. package/plugins/carthagent/src/server/ship-guidance.ts +41 -0
  116. package/plugins/carthagent/src/server/ship-rpc.ts +69 -0
  117. package/plugins/carthagent/src/server/voyages-rpc.ts +280 -0
  118. package/plugins/carthagent/src/testing.ts +5 -0
  119. package/plugins/carthagent/src/tui.tsx +496 -0
  120. package/plugins/carthagent/src/ui/client.ts +127 -0
  121. package/plugins/carthagent/src/ui/commands.ts +70 -0
  122. package/plugins/carthagent/src/ui/daily-editor-focus.mjs +52 -0
  123. package/plugins/carthagent/src/ui/daily-editor.mjs +4 -0
  124. package/plugins/carthagent/src/ui/daily-quests-events.mjs +18 -0
  125. package/plugins/carthagent/src/ui/daily-quests-state.mjs +218 -0
  126. package/plugins/carthagent/src/ui/daily-quests.tsx +235 -0
  127. package/plugins/carthagent/src/ui/host-state.ts +42 -0
  128. package/plugins/carthagent/src/ui/model-command.ts +32 -0
  129. package/plugins/carthagent/src/ui/native-navigation.ts +23 -0
  130. package/plugins/carthagent/src/ui/native-review.d.mts +33 -0
  131. package/plugins/carthagent/src/ui/native-review.mjs +159 -0
  132. package/plugins/carthagent/src/ui/resume-session.mjs +34 -0
  133. package/plugins/carthagent/src/ui/select-mouse.ts +17 -0
  134. package/plugins/carthagent/src/ui/session-preferences.d.mts +7 -0
  135. package/plugins/carthagent/src/ui/session-preferences.mjs +14 -0
  136. package/plugins/carthagent/src/ui/theme-provision.d.mts +3 -0
  137. package/plugins/carthagent/src/ui/theme-provision.mjs +134 -0
  138. package/plugins/carthagent/src/ui/voyages-navigation-state.mjs +56 -0
  139. package/plugins/carthagent/src/ui/voyages-navigation.tsx +131 -0
  140. package/plugins/carthagent/src/ui/voyages-state.mjs +165 -0
  141. package/plugins/carthagent/src/ui/voyages.tsx +131 -0
  142. package/plugins/carthagent/src/ui/workspace-live.ts +61 -0
  143. package/plugins/carthagent/src/ui/workspace.tsx +379 -0
  144. package/plugins/carthagent/src/voyages-rpc.ts +64 -0
  145. package/plugins/carthagent/src/workspace-rpc.ts +36 -0
  146. package/plugins/carthagent/tests/commands.test.ts +107 -0
  147. package/plugins/carthagent/tests/session-preferences.test.mjs +41 -0
  148. package/plugins/carthagent/tests/voyages-navigation.test.tsx +269 -0
  149. package/plugins/carthagent/themes/carthage.json +67 -0
  150. package/plugins/carthagent/tui.ts +26 -1
  151. package/script/ctg/build-owned-runtime.sh +166 -0
  152. package/tests/budget-engine.test.mjs +4 -3
  153. package/tests/cli.test.mjs +52 -96
  154. package/tests/daily-evidence.test.mjs +62 -0
  155. package/tests/delivery-review.test.mjs +57 -2
  156. package/tests/delivery.test.mjs +22 -1
  157. package/tests/fixtures/native-discovery-plugin/server.ts +15 -0
  158. package/tests/generate-owned-runtime-release.test.mjs +354 -0
  159. package/tests/installed-runtime-contract.test.mjs +43 -0
  160. package/tests/native-cli-host-smoke.mjs +457 -0
  161. package/tests/native-daily-editor-focus.test.mjs +213 -0
  162. package/tests/native-daily-editor.test.mjs +43 -0
  163. package/tests/native-daily-quests-layout.test.mjs +55 -0
  164. package/tests/native-daily-quests-rpc.test.mjs +456 -0
  165. package/tests/native-daily-quests-ui.test.mjs +778 -0
  166. package/tests/native-daily-source.test.mjs +525 -0
  167. package/tests/native-demo-ui.test.mjs +48 -0
  168. package/tests/native-evaluation-admission.test.mjs +24 -0
  169. package/tests/native-fixture-browser-cleanup.test.mjs +65 -0
  170. package/tests/native-fixture-runtime.test.mjs +111 -0
  171. package/tests/native-fixture-stop-diagnostics.test.mjs +67 -0
  172. package/tests/native-glyph-pty.test.mjs +107 -0
  173. package/tests/native-manual-projects.test.mjs +170 -0
  174. package/tests/native-model-command.test.mjs +67 -0
  175. package/tests/native-owned-runtime-env.test.mjs +140 -0
  176. package/tests/native-paired-guards.test.mjs +464 -0
  177. package/tests/native-provider-policy.test.mjs +31 -0
  178. package/tests/native-pty-invocation.test.mjs +114 -0
  179. package/tests/native-pty-runtime.test.mjs +45 -0
  180. package/tests/native-resume-session.test.mjs +46 -0
  181. package/tests/native-review-consumer.test.mjs +286 -0
  182. package/tests/native-runtime-identity.test.mjs +214 -0
  183. package/tests/native-select-mouse.test.mjs +23 -0
  184. package/tests/native-ship-guidance.test.mjs +16 -0
  185. package/tests/native-ship-parity.test.mjs +526 -0
  186. package/tests/native-ship-plan.test.mjs +85 -0
  187. package/tests/native-ship-rpc.test.mjs +71 -0
  188. package/tests/native-ui-commands.test.mjs +6 -0
  189. package/tests/native-voyages-layout.test.mjs +28 -0
  190. package/tests/native-voyages-navigation.test.mjs +169 -0
  191. package/tests/native-voyages-rpc.test.mjs +397 -0
  192. package/tests/native-voyages-ui.test.mjs +307 -0
  193. package/tests/opencode-atomic-write.test.mjs +55 -0
  194. package/tests/opencode-cli.test.mjs +1312 -0
  195. package/tests/opencode-codegraph-plugin.test.mjs +47 -0
  196. package/tests/opencode-delivery.test.mjs +16 -0
  197. package/tests/opencode-providers.test.mjs +245 -18
  198. package/tests/opencode-runtime.test.mjs +597 -8
  199. package/tests/opencode-session-controls.test.mjs +15 -0
  200. package/tests/opencode-ship-rpc.test.mjs +71 -0
  201. package/tests/opencode-update.test.mjs +475 -0
  202. package/tests/opencode-workspace.test.mjs +51 -2
  203. package/tests/owned-runtime-publish-workflow.test.mjs +477 -0
  204. package/tests/release-workflow.test.mjs +46 -0
  205. package/tests/runtime-artifact.test.mjs +269 -0
  206. package/tests/runtime-development-trust.test.mjs +231 -0
  207. package/tests/runtime-github-transport.test.mjs +132 -0
  208. package/tests/runtime-token-env.test.mjs +23 -0
  209. package/tests/server.test.mjs +3 -1
  210. package/tests/ship-telemetry.test.mjs +1 -1
  211. package/tests/source-preview.test.mjs +253 -0
  212. package/tests/test_native_terminal_probe.py +76 -0
  213. package/tests/test_native_terminal_semantics.py +232 -0
  214. package/tests/update.test.mjs +42 -5
  215. package/tests/verify-draft-runtime-gate.test.mjs +280 -0
  216. package/tests/verify-owned-runtime-release.test.mjs +371 -0
  217. package/tests/workspace-live.test.mjs +127 -0
  218. package/tools/benchmark/diagnose-request.mjs +59 -36
  219. package/tools/benchmark/eval-common.mjs +3 -3
  220. package/tools/benchmark/evaluators.test.mjs +4 -4
  221. package/tools/benchmark/guard.test.mjs +35 -2
  222. package/tools/benchmark/guard.ts +20 -7
  223. package/tools/benchmark/planning-startup.test.mjs +30 -34
  224. package/tools/benchmark/runner.mjs +12 -7
  225. package/tools/benchmark/runner.test.mjs +23 -7
  226. package/tools/benchmark/upstream-error.test.mjs +16 -2
  227. package/tools/generate-owned-runtime-release.mjs +217 -0
  228. package/tools/installed-runtime-contract.mjs +16 -0
  229. package/tools/manual-runtime-manifest.mjs +39 -0
  230. package/tools/native-fixture-browser-cleanup.mjs +59 -0
  231. package/tools/native-fixture-stop-diagnostics.mjs +43 -0
  232. package/tools/native-fixture.mjs +199 -40
  233. package/tools/native-glyph-helpers.py +127 -0
  234. package/tools/native-glyph-pty.py +144 -0
  235. package/tools/native-manual-projects.mjs +259 -0
  236. package/tools/native-manual.mjs +338 -0
  237. package/tools/native-model-picker-acceptance.py +306 -0
  238. package/tools/native-owned-runtime-env.sh +47 -0
  239. package/tools/native-paired-guards.mjs +282 -0
  240. package/tools/native-paired-manifest.mjs +72 -0
  241. package/tools/native-paired-orchestration.sh +51 -0
  242. package/tools/native-plugin-readiness.mjs +33 -0
  243. package/tools/native-plugin-readiness.test.mjs +53 -0
  244. package/tools/native-pty-invocation.mjs +26 -0
  245. package/tools/native-pty-runtime.mjs +83 -0
  246. package/tools/native-pty-smoke.py +147 -24
  247. package/tools/native-public-repo-shell-result.mjs +13 -0
  248. package/tools/native-public-repo-shell-result.test.mjs +28 -0
  249. package/tools/native-runtime-identity.mjs +201 -0
  250. package/tools/native-slash-acceptance-env.py +38 -0
  251. package/tools/native-terminal-semantics.py +876 -0
  252. package/tools/owned-shell-observer/index.ts +16 -0
  253. package/tools/owned-shell-observer/package.json +1 -0
  254. package/tools/owned-visual-clear-env.py +48 -0
  255. package/tools/owned-visual-clear-model.py +21 -0
  256. package/tools/pinned-owned-runtime-inputs.mjs +46 -0
  257. package/tools/prepare-native-manual.mjs +122 -0
  258. package/tools/run-native-terminal-paired.sh +403 -0
  259. package/tools/runtime-token-env.mjs +8 -0
  260. package/tools/seed-native-switch-fixture.mjs +79 -0
  261. package/tools/test-native-controls.mjs +3 -1
  262. package/tools/test-native-daily-quests.mjs +65 -0
  263. package/tools/test-native-default-cli.mjs +106 -0
  264. package/tools/test-native-delivery-host.mjs +102 -20
  265. package/tools/test-native-evaluation.mjs +7 -3
  266. package/tools/test-native-glyph-pty.mjs +94 -0
  267. package/tools/test-native-host.mjs +87 -63
  268. package/tools/test-native-outcomes.mjs +3 -1
  269. package/tools/test-native-package.mjs +206 -38
  270. package/tools/test-native-performance.mjs +26 -0
  271. package/tools/test-native-product.mjs +90 -0
  272. package/tools/test-native-pty.mjs +34 -9
  273. package/tools/test-native-public-repo.mjs +224 -0
  274. package/tools/test-native-refresh-reentry.mjs +106 -0
  275. package/tools/test-native-report.mjs +27 -20
  276. package/tools/test-native-review.mjs +75 -0
  277. package/tools/test-native-runtime.mjs +120 -0
  278. package/tools/test-native-ship-models.mjs +176 -0
  279. package/tools/test-native-ship-walkthrough.mjs +96 -0
  280. package/tools/test-native-slash-commands.mjs +125 -0
  281. package/tools/test-native-slash-commands.py +242 -0
  282. package/tools/test-native-swe-host.mjs +224 -55
  283. package/tools/test-native-tasks.mjs +77 -0
  284. package/tools/test-native-terminal-performance.py +887 -0
  285. package/tools/test-native-terminal-readiness.sh +85 -0
  286. package/tools/test-native-terminal-semantics-installed.mjs +38 -0
  287. package/tools/test-native-visual-clear-installed.mjs +48 -0
  288. package/tools/test-owned-host-controls.mjs +238 -0
  289. package/tools/test-owned-opencode-runtime.mjs +35 -0
  290. package/tools/test-owned-visual-clear-env.py +120 -0
  291. package/tools/test-owned-visual-clear.py +373 -0
  292. package/tools/verify-draft-runtime-gate.mjs +138 -0
  293. package/tools/verify-owned-runtime-release.mjs +114 -0
  294. package/tools/verify-release-version.mjs +21 -1
@@ -0,0 +1,120 @@
1
+ # Projects sidebar source preview
2
+
3
+ This preview runs the redesigned Projects sidebar from OpenCode source plus the
4
+ Carthagent plugin in this checkout. It is a development preview. Nothing here
5
+ builds, publishes, installs, or changes the published `ctg` runtime.
6
+
7
+ ## What is pinned where
8
+
9
+ | Purpose | Patch | Recipe | Hash reference |
10
+ | --- | --- | --- | --- |
11
+ | Published runtime (preview.13) | `docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch` | `script/ctg/build-owned-runtime.sh`, publish workflow | `CTG_RUNTIME_PATCH_SHA256`, `lib/opencode/runtime-release.json` |
12
+ | Source preview (development) | `docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch` | `script/ctg/prepare-development-runtime.sh` | `CTG_DEVELOPMENT_RUNTIME_PATCH_SHA256` |
13
+
14
+ Both patches apply to clean upstream `https://github.com/anomalyco/opencode` at
15
+ `843a6c514e606b436a7fe713422ace0a93ad3d61`. v5 is a replacement patch: it
16
+ contains everything in v4 plus the sidebar, folder browser and new-session
17
+ setup sources and their tests under `packages/tui/test/sidebar`. Do not apply
18
+ v4 first.
19
+
20
+ ## Setup
21
+
22
+ 1. `npm ci` in this checkout (the plugin runs from `plugins/carthagent`).
23
+ 2. Clone upstream into a new directory, check out the pinned commit, and run
24
+ `script/ctg/prepare-development-runtime.sh <checkout>`. The script refuses a
25
+ different commit, a dirty checkout or a patch with the wrong SHA-256, then
26
+ runs `git apply --check` and `git apply`. It does not install or compile.
27
+ 3. Run `bun install` with Bun 1.4.2 in the prepared checkout.
28
+
29
+ ## Launch
30
+
31
+ ```sh
32
+ script/ctg/source-preview.sh --source DIR --profile DIR --bun PATH [--workspace DIR] [-- ARGS...]
33
+ ```
34
+
35
+ - All three of `--source`, `--profile` and `--bun` are required. Relative
36
+ paths resolve against your current directory, so you can launch from
37
+ anywhere. Paths with spaces work when quoted.
38
+ - The launcher checks that `packages/cli/src/index.ts` and `node_modules`
39
+ exist and that `bun --version` matches the checkout's `packageManager` pin.
40
+ It fails before touching the profile when a prerequisite is missing.
41
+ - It runs Bun with `--no-env-file` from `<source>/packages/cli` (required for
42
+ JSX resolution) under `env -i`. Only these variables reach OpenCode: `HOME`,
43
+ `PATH` (Bun's directory, Node's directory if found, `/usr/bin`, `/bin`),
44
+ `TERM`, `COLORTERM`, `TMPDIR`, the four `XDG_*` directories,
45
+ `OPENCODE_CONFIG_DIR`, `OPENCODE_TEST_HOME`,
46
+ `OPENCODE_DISABLE_PROJECT_CONFIG=1`, `OPENCODE_DISABLE_AUTOUPDATE=1` and
47
+ `BUN_RUNTIME_TRANSPILER_CACHE_PATH=0`. Provider API-key variables are never
48
+ passed through.
49
+ - First launch creates the profile (0700) with `config/opencode/opencode.json`
50
+ (the plugin path), `cli.json` (Carthage theme) and `themes/carthage.json`
51
+ (all 0600). Each file is created only if absent; existing configuration,
52
+ sessions and authentication are left untouched. If an existing
53
+ `opencode.json` does not list the plugin, the launcher prints a warning and
54
+ still leaves the file alone.
55
+ - `--standalone` is always used, so the CLI starts its own private server on
56
+ a loopback ephemeral port (`--port 0`) that exits with the TUI. `--server`
57
+ is rejected. Without `--`, the workspace (default: current directory) is
58
+ opened. With `--`, the remaining arguments run another CLI command in the
59
+ same profile, for example `-- auth login`.
60
+
61
+ ## Use
62
+
63
+ - **Folder card** (top of Projects): current folder, abbreviated path (click
64
+ for the full path), Browse, New session and the model action. Narrow layouts
65
+ show compact `⌕` search, `▤` Browse and `◇` model controls.
66
+ - **Browse** (`/browse`, palette “Browse folders”): Filter and Path inputs
67
+ (Tab switches), Up/Down select, Enter opens, Alt+Left/Right/Up for
68
+ Back/Forward/Up, Alt+H for Home, Ctrl+S confirms, Escape cancels.
69
+ - **New session** (`Ctrl+X N`, `/new`, palette “New session” or “New session
70
+ in folder…”, folder card, compact `+`, folder right-click menu): choose the
71
+ folder, then the model. Global entries start at the folder a plain new
72
+ session would use; project entries start at that project's folder. Nothing
73
+ is created until the first message is sent; cancelling leaves the current
74
+ session, draft and model unchanged. The new session keeps the agent you were
75
+ using and, when you keep the same model, its variant (including the default).
76
+ If the chosen agent or model is not available at the folder when you send,
77
+ the send is blocked with your text kept and no session created; pick another
78
+ agent (`Shift+Tab`) or model (`/model`, `Ctrl+X M`) and send again. There is
79
+ no automatic fallback to another model. `/model` also works before a session
80
+ exists. The model step inside setup has no integration shortcut; connect
81
+ integrations from the standalone model picker (`Ctrl+X M`).
82
+ - **Exit**: `/exit` or palette “Exit the app”. The private server exits with
83
+ the TUI.
84
+ - **Resume**: relaunch with the same `--profile` and select the session in
85
+ Projects. Automatic resume is not part of this preview.
86
+
87
+ ## Tests
88
+
89
+ Run serially. Close the preview first.
90
+
91
+ ```sh
92
+ # Product (from this checkout)
93
+ node --test --test-concurrency=1 tests/source-preview.test.mjs tests/native-ship-plan.test.mjs tests/native-model-command.test.mjs
94
+ npx eslint . && npm run typecheck
95
+
96
+ # Native (from <source>/packages/tui, using the same isolated variables as the launcher)
97
+ bun --no-env-file test --timeout 30000 test/navigation-rail.test.tsx \
98
+ test/component/session-tabs-rail.test.tsx test/context/session-tabs-model.test.ts \
99
+ test/ui/layout.test.ts test/sidebar
100
+ ```
101
+
102
+ `tests/source-preview.test.mjs` uses a Bun test double and synthetic profiles,
103
+ so it proves argument handling, isolation and config preservation, not a real
104
+ launch. It runs the launcher with `/bin/sh` on Linux and macOS and is skipped
105
+ on native Windows, where the POSIX launcher is not supported.
106
+
107
+ ## Limits
108
+
109
+ - Source preview only. No native binary, package or release includes these
110
+ changes, and the published runtime remains v4.
111
+ - The launcher is a POSIX `sh` script for Linux and macOS (GNU and BSD
112
+ utilities). Native Windows is not supported.
113
+ - Real launches were verified on Linux with Bun 1.4.2. No cross-platform
114
+ launch matrix was run.
115
+ - The native type checker (`tsgo`) and native build are not part of this
116
+ workflow; Bun tests compile the changed modules but are not a full static
117
+ type check.
118
+ - Some native tests outside the focused list fail in the same way on the
119
+ pre-change sources (for example missing `ToastProvider` in
120
+ `session-tabs-status` harnesses).
package/evaluate.mjs CHANGED
@@ -23,7 +23,33 @@ const { values: args } = parseArgs({ options: {
23
23
  timeout: { type: 'string', default: '600' }, 'max-turns': { type: 'string', default: '100' }, 'max-cost': { type: 'string', default: '3' },
24
24
  python: { type: 'string' }, 'seed-from': { type: 'string' }, 'allow-live': { type: 'boolean', default: false },
25
25
  'dry-run': { type: 'boolean', default: false },
26
+ help: { type: 'boolean', short: 'h', default: false },
26
27
  } });
28
+ if (args.help) {
29
+ console.log(`Usage: ctg eval [options]
30
+
31
+ Opt-in live A/B evaluation of baseline vs custom configurations against a
32
+ scenario manifest under scenarios/<name>/. Live calls spend API credit and
33
+ generated code runs with your permissions, so a run must be explicit.
34
+
35
+ Options:
36
+ --mode baseline|custom|both What to evaluate (default both)
37
+ --task <name> Scenario short name (default cli)
38
+ --scenario <name> Scenario directory (overrides --task)
39
+ --model <id> Model id
40
+ --provider <name> Provider name; needs <PROVIDER>_API_KEY
41
+ --thinking <level> Thinking level (default medium)
42
+ --timeout <seconds> Per-run timeout, max 3600 (default 600)
43
+ --max-turns <n> Turn budget, max 1000 (default 100)
44
+ --max-cost <usd> Cost budget (default 3)
45
+ --python <path> Python executable for scenarios that need it
46
+ --seed-from <dir> Continue from a previous candidate directory
47
+ --allow-live Confirm live provider calls
48
+ --dry-run Assemble and preview without calling a provider
49
+ --help, -h Show this help
50
+ `);
51
+ process.exit(0);
52
+ }
27
53
  if (!args['allow-live'] && !args['dry-run']) throw Error('Live calls spend API credit and generated code runs with your permissions. Use --allow-live explicitly (or --dry-run to preview the assembled run without calling a provider).');
28
54
  if (!['baseline', 'custom', 'both'].includes(args.mode)) throw Error('Use --mode baseline|custom|both');
29
55
  for (const key of ['timeout', 'max-turns', 'max-cost']) if (!(Number(args[key]) > 0 && Number.isFinite(Number(args[key])))) throw Error(`Invalid ${key}`);
@@ -197,7 +223,8 @@ for (const mode of args.mode === 'both' ? ['baseline', 'custom'] : [args.mode])
197
223
  cwd,
198
224
  evidenceSchema: 2,
199
225
  runtime: NATIVE_EVALUATION_RUNTIME,
200
- runtimeVersion: result.opencodeVersion,
226
+ compatibilityVersion: result.compatibilityVersion,
227
+ runtimeVersion: result.runtimeVersion,
201
228
  exitCode: result.exitCode,
202
229
  timedOut: result.timedOut,
203
230
  budgetReason: result.budgetReason,
package/lib/budget.mjs CHANGED
@@ -22,7 +22,7 @@ export function budgetReason(budget, action, now = Date.now()) {
22
22
 
23
23
  export function validateBudgetOptions(opts, { allowWithoutDelivery = false } = {}) {
24
24
  const keys = ['maxTools', 'maxSeconds', 'maxRepairs'];
25
- if (!allowWithoutDelivery && opts.delivery === false && keys.some(key => opts[key] !== undefined)) {
25
+ if (!allowWithoutDelivery && opts.delivery === false && keys.some(key => opts[key] !== undefined)) {
26
26
  throw Error('Budget options require the delivery extension');
27
27
  }
28
28
  budgetLimits(flag => opts[{ 'delivery-max-tools': 'maxTools', 'delivery-max-seconds': 'maxSeconds', 'delivery-max-repairs': 'maxRepairs' }[flag]]);
package/lib/codegraph.mjs CHANGED
@@ -153,7 +153,7 @@ const parsers = new Map(); // lang -> dedicated parser (setLanguage once; concur
153
153
  async function parserFor(lang) {
154
154
  if (!runtimePromise) {
155
155
  runtimePromise = (async () => {
156
- const Parser = require_('web-tree-sitter');
156
+ const Parser = require_('web-tree-sitter-legacy');
157
157
  await Parser.init();
158
158
  const wasmDir = require_.resolve('tree-sitter-wasms/package.json').replace(/package\.json$/, 'out');
159
159
  const languages = new Map();
@@ -1,6 +1,6 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import { readFileSync, writeFileSync, mkdirSync, realpathSync, statSync } from 'node:fs';
3
- import { dirname, join, resolve } from 'node:path';
3
+ import { dirname, join, resolve, relative, isAbsolute, sep } from 'node:path';
4
4
  import { fingerprint, runCommand, localPath, pendingRequirements, evidenceExcludedDirectories, evidenceFresh } from './delivery.mjs';
5
5
 
6
6
  // Captures real Git output and new source contents. The receipt binds a
@@ -13,15 +13,18 @@ export async function captureDeliveryReview(state, cwd, directory, signal) {
13
13
  text = 'No Git repository available. Manually inspect source files and describe the review scope honestly; no diff comparison was possible.\n';
14
14
  } else {
15
15
  if (top.timedOut || top.cancelled || top.outputLimit) throw Error('Git repository discovery failed.');
16
- if (realpathSync.native(resolve(top.output.trim())) !== realpathSync.native(resolve(cwd))) throw Error('Review requires the workspace Git root, not an unrelated ancestor repository.');
16
+ const root = realpathSync.native(resolve(top.output.trim()));
17
+ const workspace = realpathSync.native(resolve(cwd));
18
+ const scope = relative(root, workspace);
19
+ if (isAbsolute(scope) || scope === '..' || scope.startsWith(`..${sep}`)) throw Error('Review requires a workspace inside the discovered Git repository.');
17
20
  const log = join(directory, `diff-${randomUUID()}.log`);
18
21
  const exclusions = evidenceExcludedDirectories.map(name => `:(exclude,glob)**/${name}/**`);
19
- const diff = await runCommand(['git', 'diff', '--no-ext-diff', '--no-textconv', 'HEAD', '--', '.', ...exclusions], { cwd, signal, timeoutSeconds: 30, logPath: log });
22
+ const diff = await runCommand(['git', 'diff', '--relative', '--no-renames', '--no-ext-diff', '--no-textconv', 'HEAD', '--', '.', ...exclusions], { cwd, signal, timeoutSeconds: 30, logPath: log });
20
23
  if (diff.code !== 0 || diff.timedOut || diff.cancelled || diff.outputLimit) throw Error('Cannot capture complete Git diff (requires a committed baseline and bounded output).');
21
24
  const untrackedLog = join(directory, `untracked-${randomUUID()}.log`);
22
- const status = await runCommand(['git', 'ls-files', '-z', '--others', '--exclude-standard', ...evidenceExcludedDirectories.map(name => `--exclude=${name}/`)], { cwd, signal, timeoutSeconds: 10, logPath: untrackedLog });
25
+ const status = await runCommand(['git', 'ls-files', '-z', '--others', '--exclude-standard', ...evidenceExcludedDirectories.map(name => `--exclude=${name}/`), '--', '.'], { cwd, signal, timeoutSeconds: 10, logPath: untrackedLog });
23
26
  if (status.code !== 0 || status.cancelled || status.timedOut || status.outputLimit) throw Error('Cannot enumerate untracked files for review.');
24
- text = `Review scope excludes dependency/cache/output directories: ${evidenceExcludedDirectories.join(', ')}.\n${readFileSync(log, 'utf8')}\nNew source files:\n`;
27
+ text = `Review scope: workspace ${JSON.stringify(scope || '.')} relative to Git root; paths below are workspace-relative.\nReview scope excludes dependency/cache/output directories: ${evidenceExcludedDirectories.join(', ')}.\n${readFileSync(log, 'utf8')}\nNew source files:\n`;
25
28
  for (const name of readFileSync(untrackedLog, 'utf8').split('\0').filter(Boolean).sort()) {
26
29
  const path = localPath(cwd, name);
27
30
  if (statSync(path).size > 1024 * 1024) throw Error(`New file too large for review capture: ${JSON.stringify(name)}. Inspect it separately or narrow the workspace.`);
@@ -41,13 +44,23 @@ export async function captureDeliveryReview(state, cwd, directory, signal) {
41
44
  return { ...capture, diff: text.slice(0, 12000), truncated: text.length > 12000, unresolvedFindings: (state.reviewFindings || []).filter(f => !f.resolution), next: 'Read the full capture path if truncated; new text source is included. Inspect binary assets separately. Probe critical component connections and user outcomes, not only individual functions or artifact existence. Record coverage, findings and limitations; earlier findings require explicit resolutions with evidence, never silently move them to limitations.' };
42
45
  }
43
46
 
47
+ /** Models commonly send `assertion` or a string array; both carry the same claim. */
48
+ function coverageAssertions(item) {
49
+ const value = item.assertions ?? item.assertion;
50
+ if (Array.isArray(value)) return value.filter(v => typeof v === 'string' && v.trim()).join('; ');
51
+ return typeof value === 'string' ? value : '';
52
+ }
53
+
44
54
  export function recordDeliveryReview(state, cwd, params) {
45
55
  const capture = state.reviewCapture;
46
56
  const hash = fingerprint(cwd, ['.']);
47
57
  if (!capture || capture.id !== params.captureId || capture.runId !== state.runId || capture.revision !== state.revision || capture.fingerprint !== hash) throw Error('Review capture missing or stale. Use delivery_review action=inspect after final source edits.');
48
58
  if (pendingRequirements(state, hash).length) throw Error('Run fresh checks for every requirement before recording review.');
49
59
  const nonempty = value => typeof value === 'string' && value.trim().length > 0;
50
- if (!Array.isArray(params.coverage) || (state.plan.acceptance || []).some(a => !params.coverage.some(c => c.requirement === a.requirement && nonempty(c.assertions)))) throw Error('Review coverage must explain concrete assertions for every exact acceptance requirement, including required validators.');
60
+ if (!Array.isArray(params.coverage) || params.coverage.some(c => !c || typeof c !== 'object')) throw Error('coverage must be an array of {requirement, assertions, status?} objects.');
61
+ params = { ...params, coverage: params.coverage.map(c => ({ ...c, assertions: coverageAssertions(c) })) };
62
+ const uncovered = (state.plan.acceptance || []).filter(a => !params.coverage.some(c => c.requirement === a.requirement && nonempty(c.assertions))).map(a => a.requirement);
63
+ if (uncovered.length) throw Error(`Review coverage must explain concrete assertions for every exact acceptance requirement, including required validators. Missing: ${JSON.stringify(uncovered.slice(0, 10))}${uncovered.length > 10 ? ` (+${uncovered.length - 10} more)` : ''}. Each item: {requirement: <exact acceptance text>, assertions: "<what the checks assert>", status?: "demonstrated"|"unverified"}.`);
51
64
  if (params.coverage.some(c => c.status !== undefined && !['demonstrated', 'unverified'].includes(c.status))) throw Error('Coverage status must be demonstrated or unverified.');
52
65
  if (!Array.isArray(params.probes) || !params.probes.length || params.probes.some(p => !nonempty(p))) throw Error('Record boundary/metamorphic probes and observed results, not just a generic review claim.');
53
66
  if (!Array.isArray(params.findings) || params.findings.some(p => !nonempty(p))) throw Error('findings must be an array; unresolved findings require repairs or a blocked handoff.');
package/lib/delivery.mjs CHANGED
@@ -4,6 +4,7 @@ import { createHash, randomUUID } from 'node:crypto';
4
4
  import { existsSync, lstatSync, readdirSync, readFileSync, mkdirSync, writeFileSync, renameSync, openSync, writeSync, closeSync } from 'node:fs';
5
5
  import { resolve, relative, isAbsolute, join, dirname, basename } from 'node:path';
6
6
  import { spawn } from 'node:child_process';
7
+ import { withoutRuntimeGithubToken } from './environment.mjs';
7
8
 
8
9
  // Generic dependency/cache/output directories excluded from the source fingerprint.
9
10
  // Scenarios or projects can add more via the manifest; these are the common defaults.
@@ -425,7 +426,7 @@ export function atomicJson(path, data) {
425
426
  export function killTree(child) {
426
427
  if (!child.pid) return;
427
428
  if (process.platform === 'win32') {
428
- const killer = spawn('taskkill.exe', ['/pid', String(child.pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
429
+ const killer = spawn('taskkill.exe', ['/pid', String(child.pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore', env: withoutRuntimeGithubToken() });
429
430
  killer.on('error', () => child.kill());
430
431
  } else {
431
432
  try { process.kill(-child.pid, 'SIGKILL'); } catch { child.kill('SIGKILL'); }
@@ -443,7 +444,7 @@ export async function runCommand(argv, { cwd, timeoutSeconds = 60, signal, logPa
443
444
  const started = Date.now();
444
445
  return new Promise(resolveResult => {
445
446
  let tail = '', written = 0, timedOut = false, cancelled = false, outputLimit = false;
446
- const child = spawn(argv[0], argv.slice(1), { cwd, shell: false, windowsHide: true, detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'] });
447
+ const child = spawn(argv[0], argv.slice(1), { cwd, env: withoutRuntimeGithubToken(), shell: false, windowsHide: true, detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'] });
447
448
  const stop = () => killTree(child);
448
449
  const abort = () => { cancelled = true; stop(); };
449
450
  const timer = setTimeout(() => { timedOut = true; stop(); }, timeoutSeconds * 1000);
@@ -0,0 +1,8 @@
1
+ /** Return a copy of an environment without the optional runtime transport token. */
2
+ export function withoutRuntimeGithubToken(env = process.env) {
3
+ const clean = { ...env };
4
+ for (const key of Object.keys(clean)) {
5
+ if (key.toUpperCase() === 'CTG_RUNTIME_GITHUB_TOKEN') delete clean[key];
6
+ }
7
+ return clean;
8
+ }
package/lib/guidance.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { unframe } from './tui/framing.mjs';
4
+ import { unframe } from '../plugins/carthagent/src/server/framing.mjs';
5
5
 
6
6
  const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
7
  export const DEFAULT_PROFILE_DIR = join(root, 'guidance', 'profiles');