@ucsandman/legcli 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/CHANGELOG.md +560 -0
  2. package/LICENSE +58 -0
  3. package/NOTICE +4 -0
  4. package/README.md +558 -0
  5. package/bin/fake-agent.mjs +143 -0
  6. package/bin/leg.mjs +449 -0
  7. package/docs/DECISIONS.md +119 -0
  8. package/docs/DEMO.md +214 -0
  9. package/docs/DEVIATIONS.md +182 -0
  10. package/docs/ERRORS.md +326 -0
  11. package/docs/README.md +40 -0
  12. package/docs/REUSE.md +289 -0
  13. package/docs/ROADMAP-v2.md +55 -0
  14. package/docs/VOCABULARY.md +243 -0
  15. package/docs/adapters.md +300 -0
  16. package/docs/board-guide.md +579 -0
  17. package/docs/cli-contracts.md +401 -0
  18. package/docs/concepts.md +426 -0
  19. package/docs/configuration.md +220 -0
  20. package/docs/faq.md +206 -0
  21. package/docs/getting-started.md +251 -0
  22. package/docs/real-run.md +80 -0
  23. package/docs/review-2026-09-14.md +72 -0
  24. package/docs/screenshots/board-400px.png +0 -0
  25. package/docs/screenshots/board-details-open.png +0 -0
  26. package/docs/screenshots/board-done.png +0 -0
  27. package/docs/screenshots/board-drawer.png +0 -0
  28. package/docs/screenshots/board-empty.png +0 -0
  29. package/docs/screenshots/board-handoff.png +0 -0
  30. package/docs/screenshots/board-running.png +0 -0
  31. package/docs/screenshots/demo-1-claude-running.png +0 -0
  32. package/docs/screenshots/demo-2-limit-hit.png +0 -0
  33. package/docs/screenshots/demo-3-handoff-bundle.png +0 -0
  34. package/docs/screenshots/demo-4-codex-running.png +0 -0
  35. package/docs/screenshots/demo-5-done.png +0 -0
  36. package/docs/screenshots/floor-final-1280.png +0 -0
  37. package/docs/screenshots/floor-final-400.png +0 -0
  38. package/docs/screenshots/floor-landing.png +0 -0
  39. package/docs/screenshots/floor.png +0 -0
  40. package/docs/screenshots/share-guest-1280.png +0 -0
  41. package/docs/screenshots/share-owner-1280.png +0 -0
  42. package/docs/screenshots/terminals-1280.png +0 -0
  43. package/docs/screenshots/thumb-done.jpg +0 -0
  44. package/docs/screenshots/thumb-handoff-details.jpg +0 -0
  45. package/docs/screenshots/thumb-limit-hit.jpg +0 -0
  46. package/docs/screenshots/youtube-thumbnail.jpg +0 -0
  47. package/fixtures/limits/agy/agy-resource-exhausted.json +11 -0
  48. package/fixtures/limits/claude/claude-budget-limit.json +11 -0
  49. package/fixtures/limits/claude/claude-max-turns.json +11 -0
  50. package/fixtures/limits/claude/claude-model-limit.json +11 -0
  51. package/fixtures/limits/claude/claude-session-limit.json +11 -0
  52. package/fixtures/limits/claude/claude-weekly-limit.json +11 -0
  53. package/fixtures/limits/codex/codex-quota-exceeded.json +11 -0
  54. package/fixtures/limits/codex/codex-rate-limit-exceeded.json +11 -0
  55. package/fixtures/limits/codex/codex-skills-notice.json +11 -0
  56. package/fixtures/limits/codex/codex-usage-limit-reached.json +11 -0
  57. package/fixtures/limits/codex/codex-usage-limit.json +11 -0
  58. package/fixtures/limits/generic/generic-429.json +11 -0
  59. package/fixtures/limits/generic/generic-overloaded.json +11 -0
  60. package/fixtures/limits/generic/generic-quota.json +11 -0
  61. package/fixtures/limits/generic/generic-rate-limit.json +11 -0
  62. package/fixtures/limits/generic/generic-resource-exhausted.json +11 -0
  63. package/fixtures/limits/generic/generic-usage-limit.json +11 -0
  64. package/fixtures/limits/grok/grok-not-logged-in.json +11 -0
  65. package/fixtures/limits/negative/auth-source-set.json +11 -0
  66. package/fixtures/limits/negative/compile-error.json +11 -0
  67. package/fixtures/limits/negative/empty-stdout-exit-0.json +11 -0
  68. package/fixtures/live/agy/attempt-1-scratch-workspace.out.log +1 -0
  69. package/fixtures/live/agy/cmd.txt +1 -0
  70. package/fixtures/live/agy/err.log +0 -0
  71. package/fixtures/live/agy/out.log +1 -0
  72. package/fixtures/live/agy/parsed.json +5 -0
  73. package/fixtures/live/agy/run.json +18 -0
  74. package/fixtures/live/agy/supervisor.log +2 -0
  75. package/fixtures/live/claude/cmd.txt +1 -0
  76. package/fixtures/live/claude/err.log +0 -0
  77. package/fixtures/live/claude/limit-rate_limit.json +21 -0
  78. package/fixtures/live/claude/out.log +1 -0
  79. package/fixtures/live/claude/parsed.json +11 -0
  80. package/fixtures/live/claude/run.json +18 -0
  81. package/fixtures/live/claude/supervisor.log +2 -0
  82. package/fixtures/live/codex/cmd.txt +1 -0
  83. package/fixtures/live/codex/err.log +1 -0
  84. package/fixtures/live/codex/last.md +2 -0
  85. package/fixtures/live/codex/limit-usage_limit_exceeded.json +25 -0
  86. package/fixtures/live/codex/out.log +8 -0
  87. package/fixtures/live/codex/parsed.json +15 -0
  88. package/fixtures/live/codex/run.json +18 -0
  89. package/fixtures/live/codex/supervisor.log +2 -0
  90. package/fixtures/live/grok/cmd.txt +1 -0
  91. package/fixtures/live/grok/err.log +32 -0
  92. package/fixtures/live/grok/out.log +7 -0
  93. package/fixtures/live/grok/parsed.json +5 -0
  94. package/fixtures/live/grok/run.json +18 -0
  95. package/fixtures/live/grok/supervisor.log +2 -0
  96. package/fixtures/verified.json +34 -0
  97. package/package.json +68 -0
  98. package/scripts/board-shots.mjs +69 -0
  99. package/scripts/build-docs-site.mjs +308 -0
  100. package/scripts/check-claims.mjs +150 -0
  101. package/scripts/clean-clone-check.sh +42 -0
  102. package/scripts/license-sign.mjs +23 -0
  103. package/scripts/limits-table.mjs +38 -0
  104. package/scripts/live-limits.mjs +36 -0
  105. package/scripts/privacy-check.mjs +63 -0
  106. package/scripts/probe.mjs +92 -0
  107. package/scripts/seed-fake-cards.mjs +34 -0
  108. package/scripts/seed-floor-board.mjs +69 -0
  109. package/scripts/seed-wes-board.mjs +91 -0
  110. package/scripts/stripe-setup.mjs +99 -0
  111. package/scripts/vercel-env.mjs +40 -0
  112. package/src/accounts.mjs +119 -0
  113. package/src/adapters/agy.mjs +56 -0
  114. package/src/adapters/claude.mjs +54 -0
  115. package/src/adapters/codex.mjs +73 -0
  116. package/src/adapters/common.mjs +32 -0
  117. package/src/adapters/fake.mjs +48 -0
  118. package/src/adapters/grok.mjs +51 -0
  119. package/src/adapters/index.mjs +27 -0
  120. package/src/adapters/resolve.mjs +40 -0
  121. package/src/attach.mjs +592 -0
  122. package/src/auth.mjs +85 -0
  123. package/src/board/board.css +543 -0
  124. package/src/board/board.js +1307 -0
  125. package/src/board/favicon.svg +1 -0
  126. package/src/board/floor.html +127 -0
  127. package/src/board/floor.js +670 -0
  128. package/src/board/fonts/OFL-atkinson-hyperlegible-next.txt +93 -0
  129. package/src/board/fonts/OFL-azeret-mono.txt +94 -0
  130. package/src/board/fonts/atkinson-board.woff2 +0 -0
  131. package/src/board/fonts/azeret-board.woff2 +0 -0
  132. package/src/board/index.html +251 -0
  133. package/src/board/sessions.js +1451 -0
  134. package/src/bundle.mjs +103 -0
  135. package/src/cards.mjs +114 -0
  136. package/src/chain.mjs +264 -0
  137. package/src/commands.mjs +79 -0
  138. package/src/contract.mjs +73 -0
  139. package/src/env.mjs +19 -0
  140. package/src/fsx.mjs +72 -0
  141. package/src/git-snapshot.mjs +152 -0
  142. package/src/handoff.mjs +171 -0
  143. package/src/hook.mjs +49 -0
  144. package/src/land.mjs +106 -0
  145. package/src/launcher.mjs +272 -0
  146. package/src/leases.mjs +86 -0
  147. package/src/ledger.mjs +369 -0
  148. package/src/license.mjs +168 -0
  149. package/src/limits.mjs +97 -0
  150. package/src/live-capture.mjs +69 -0
  151. package/src/mergequeue.mjs +225 -0
  152. package/src/orchestrator.mjs +365 -0
  153. package/src/pipeline.mjs +100 -0
  154. package/src/preferences.mjs +47 -0
  155. package/src/presets.mjs +23 -0
  156. package/src/ratelimit.mjs +49 -0
  157. package/src/redact.mjs +47 -0
  158. package/src/resume.mjs +354 -0
  159. package/src/runner.mjs +438 -0
  160. package/src/scheduler.mjs +118 -0
  161. package/src/server.mjs +845 -0
  162. package/src/session-detail.mjs +129 -0
  163. package/src/sessions.mjs +202 -0
  164. package/src/share.mjs +163 -0
  165. package/src/stations/agent.mjs +42 -0
  166. package/src/stations/build.mjs +9 -0
  167. package/src/stations/human.mjs +5 -0
  168. package/src/stations/land.mjs +6 -0
  169. package/src/stations/plan.mjs +8 -0
  170. package/src/stations/pr.mjs +31 -0
  171. package/src/stations/review.mjs +8 -0
  172. package/src/stations/test.mjs +27 -0
  173. package/src/store.mjs +90 -0
  174. package/src/sync/dashclaw.mjs +70 -0
  175. package/src/sync/index.mjs +54 -0
  176. package/src/sync/workboard.mjs +54 -0
  177. package/src/taps/agy.mjs +64 -0
  178. package/src/taps/claude-usage.mjs +67 -0
  179. package/src/taps/claude.mjs +170 -0
  180. package/src/taps/codex.mjs +286 -0
  181. package/src/trust.mjs +323 -0
  182. package/src/usage.mjs +179 -0
  183. package/src/wait.mjs +30 -0
  184. package/src/worktree.mjs +207 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,560 @@
1
+ # Changelog
2
+
3
+ ## 0.7.0 (2026-09-15)
4
+
5
+ - **Rebranded to Leg.** Package is now `legcli` on npm, binary command is `leg`,
6
+ and site domain is `https://legcli.com`.
7
+ - **Clean break on the binary:** The `baton` CLI binary is removed; use `leg`.
8
+ - **State migration and fallback:** Leg stores its state in `~/.leg` (or
9
+ repo-local `.leg/`), and reads from `~/.baton` (or `.baton/`) as fallback.
10
+ Environment variables now use the `LEG_` prefix (`LEG_PUBLIC_KEY_B64`,
11
+ `LEG_TRUST`, `LEG_SITE`, `LEG_CHB_BIN`, `LEG_SYNC_DASHCLAW`) with automatic
12
+ fallback to `BATON_*`.
13
+ - **License keys:** Newly issued licenses carry the `LEG-` prefix. The CLI
14
+ accepts both `LEG-` and legacy `BATON-` keys indefinitely. Activations normalize
15
+ and store the `LEG-` form.
16
+
17
+ ## 0.6.1 (2026-09-15)
18
+
19
+ - **The documentation is on the web.** The repository is private and stays
20
+ private, which meant every "read the source", README, FAQ and changelog link
21
+ on the site and in the package pointed at a 404 for anyone who is not the
22
+ owner — the first click a stranger makes. `npm run docs` renders the public
23
+ subset of `docs/` plus the README and this changelog to static pages under
24
+ `/docs`, committed to the repo because Vercel serves `site/` with no build
25
+ step. CI regenerates them and fails if the committed output has drifted.
26
+ Working files (DECISIONS, DEVIATIONS, ERRORS, REUSE, ROADMAP-v2, DEMO) stay
27
+ out.
28
+ - **Nothing dangles at a 404.** The clone instructions in the README and
29
+ getting-started are replaced by where the source actually is for a buyer:
30
+ `$(npm root -g)/baton-agents/src`, plain `.mjs`, nothing bundled. Links to
31
+ files with a public home go there; links to files that only ship inside the
32
+ package point at the package. The README's images are now absolute URLs,
33
+ because npm rewrites relative image paths to `raw.githubusercontent.com`,
34
+ which 404s for a private repository — the hero image on the npm page had been
35
+ broken.
36
+ - **A support page**, at `/support`: one address, the refund terms, license-key
37
+ troubleshooting, what to put in a bug report, and the security reporting path.
38
+ A private repository has no issue tracker to point at, so this is the whole
39
+ path. `SECURITY.md` said only `0.1.x` was supported, four minor versions late,
40
+ and pointed at a GitHub advisory form strangers cannot reach.
41
+ - **The claims check themselves.** `fixtures/verified.json` pins the verified
42
+ CLI versions, the verification date and the test count. `npm test` now asserts
43
+ every public surface agrees with it, and a weekly job
44
+ (`npm run claims:drift`) compares the pins against what those CLIs publish
45
+ now, because "verified against Claude Code 2.1.268" quietly stops being true.
46
+ On its first run it found `llms.txt` claiming 463 tests and the landing page
47
+ claiming 406 while the suite reported 454, and that all three pinned CLIs had
48
+ already moved.
49
+ - **The handoff is on film.** The landing page carries the 53-second recording
50
+ of a real run — claude's five-hour gauge reaching 100 percent, the headline
51
+ turning to *at the wall*, the terminal continuing on codex — and the README
52
+ leads with a GIF of the same moment. `preload="none"`, so the page weight is
53
+ unchanged until someone presses play.
54
+ - **macOS is in CI.** The matrix was Ubuntu and Windows only.
55
+ - Removed a stray `hello-fake.txt` that a fake-agent run had committed to the
56
+ repository root.
57
+
58
+ ## 0.6.0 (2026-09-15)
59
+
60
+ - **Removed: the 14-day trial.** Baton is bought up front, and the risk
61
+ reversal is a **30-day money-back guarantee** instead. A trial suits a product
62
+ used daily by habit; Baton's value is bursty — it pays off in the one moment a
63
+ limit lands mid-flow, which a fortnight of evaluation does not reliably
64
+ contain, and a fortnight in which it never fires reads as "meh". The buyer is
65
+ already paying for two or three agent subscriptions, so at $79 they are not
66
+ price-sensitive, they are trust-sensitive, and a trial does not answer trust.
67
+ `trial.json`, `TRIAL_DAYS` and the trial branch in `entitlement()` are gone;
68
+ `baton <agent>` without a key exits 4 and names the buy URL and the guarantee.
69
+ - **Fixed: the card row was never ported to the dark product surface.** `.r1`
70
+ through `.r4` — the card row's whole four-column layout — carried no rule in
71
+ `board.css`, so Background tasks rendered on browser defaults while every
72
+ other row had been redesigned. `.row-title` never reset the native button
73
+ either, which put the board's near-white text on the user agent's light-grey
74
+ fill and made the card title unreadable.
75
+ - **Fixed: a finished card named the wrong agent.** `summarize()` read its
76
+ adapter off `chain[card.leg]`, which is reset when a station ends, so a card
77
+ that handed off from one agent to the next and finished reported the agent
78
+ that *started* the work. A finished card also showed `--:--` where its run
79
+ time belongs, and now shows how long it took.
80
+ - **Fixed: the settings panel asked for a token no one could need.** On a
81
+ loopback board with share off, `src/auth.mjs` lets the request in with no
82
+ token at all, so the API token field is no longer drawn there. It comes back
83
+ for a token already stored, for a guest, for an off-loopback bind, and
84
+ whenever health has not answered. No message names an address the server did
85
+ not give: a refused or redacted health call used to print `127.0.0.1:4747` at
86
+ someone looking at a different machine, and the owner of a shared board was
87
+ told their requests were "unauthenticated" when auth had recognised them.
88
+ - **Fixed: three places set a flat `.chip` against other text with no gap**
89
+ (`claudeclaude-2fbf`, `Files2 changed`, `10:40 AMstarted`), the floor printed
90
+ `blocked by blocked by card "X"`, and it showed the raw key `needs_approval`
91
+ where the board says `needs approval`.
92
+ - All nineteen screenshots in `docs/screenshots/` retaken against the current
93
+ build. `scripts/seed-wes-board.mjs` and the new
94
+ `scripts/seed-floor-board.mjs` produce the fixtures they need.
95
+ - The landing page shows a real handoff and the real board, not only
96
+ recreations, and says what a second agent gets without assuming the reader
97
+ knows git worktrees.
98
+
99
+ ## 0.5.1 (2026-09-15)
100
+
101
+ - **Fixed: "All the terminal is on claude".** The verdict's plural sentence is
102
+ about everything riding on one login, and with a single terminal there is no
103
+ "all" to make. One terminal now reads `Your terminal is on claude, and claude
104
+ has 1% left.` Caught by rendering the real board rather than the seeded one.
105
+ - The tab mark is grey, not the accent blue, and the board and the site carry
106
+ the same mark. The site favicon had spent the one colour that means "this is
107
+ the action to take" on a browser tab, and the board carried a third, older
108
+ mark from before the identity colours settled.
109
+ - **Fixed: a merge to `main` did not deploy the site.**
110
+ `ignoreCommand: git diff --quiet HEAD^ HEAD .` asks whether the single most
111
+ recent commit touched `site/`. A push is not a commit: five commits landed
112
+ with `site/` changed in the first, so Vercel cancelled the build and reported
113
+ it as a green tick. Removed; every push to `main` deploys.
114
+
115
+ ## 0.5.0 (2026-09-15)
116
+
117
+ - **The board is a dark product surface, and the largest thing on it is a
118
+ sentence.** The instrument that shipped through 0.4.3 was built against a
119
+ brief in `DESIGN.md` reading "exact, unhurried, mechanical, the printed
120
+ operator's manual for a piece of test equipment": a saturated navy ground,
121
+ hairline rules as the only structure, 2px radii, no elevation, and a monospace
122
+ face for prose. Six rounds were built against it and all six were rejected.
123
+ The brief, not the execution, was the defect, and it has been rewritten. What
124
+ ships instead:
125
+ - The board opens with the finding, not the data: "All 4 terminals are on
126
+ claude, and claude has 5% left", at 52px, written from the live view. Under
127
+ it sits the age of the reading and which direction it is wrong in — "Measured
128
+ 2h 13m ago. 4 terminals have been running since, so the real figure is higher
129
+ than 95 percent, never lower." A reading taken two hours ago is a floor, and
130
+ saying so is the whole reason to print its age.
131
+ - Size encodes importance. The login carrying the terminals gets a wide panel
132
+ lit from above with both of its gauges; a login with one fact to report gets
133
+ a half panel; a login that publishes no figure draws no instrument at all,
134
+ because an empty track reads as a measurement of zero. Previously every login
135
+ got an identical row, so the layout said nothing before you read it.
136
+ - A terminal is a row inside one panel, not a card of its own: state, where,
137
+ the prompt, elapsed, and its buttons in a fixed 2x2 grid so every row's
138
+ controls sit in the same place. Land is the primary action only when it can
139
+ actually run; when it is blocked the accent goes to Hand off now, because a
140
+ disabled control should not wear the one accent colour in the design.
141
+ - Finished terminals, what landed on trunk and background tasks leave the live
142
+ area entirely and become three counts on the ground that open on click. On a
143
+ real board after a day's work those were most of the list and they buried the
144
+ one or two terminals that were live.
145
+ - A fact true of every terminal is said once, at the region, instead of once
146
+ per row. The per-row copy stays in the DOM, visually hidden, so each Land
147
+ button's `aria-describedby` still resolves to its own reason.
148
+ - Ground `#0E1012` neutral near-black, panels raised with a 1px top highlight
149
+ and a shadow beneath, 16px radii, type from 13 to 52px (it ran 13 to 21,
150
+ which is why it read as a spreadsheet), one accent blue spent on the primary
151
+ action and nowhere else, and severity painted inside a gauge track rather
152
+ than used as the colour of a word.
153
+ - Measured against a board shaped like a real one — mostly `lost` and `ended`
154
+ terminals with long absolute paths — the page went from 3,302px to 2,107px at
155
+ 1280 and from 6,530px to 3,937px at 400, with terminal rows from 180-277px
156
+ down to 135-165px.
157
+ - **Prompts, file lists and the trunk log stop printing machine noise.** A pasted
158
+ screenshot arrived in the prompt as an `<image name=... path=...>` tag carrying
159
+ an absolute temp path with the operator's home directory in it, and printed as
160
+ two lines of noise in front of the sentence; it now reads `(image)` and the
161
+ text. A touched file prints its basename, not 90 characters of temp path. The
162
+ trunk log shows what landed as a count that opens, instead of eighteen rows of
163
+ `git log` at the same visual weight as the live terminals.
164
+ - **The marketing site runs on the board's own hex values.** `site/` was a light
165
+ page recreating the board in a different palette, from the card anatomy the
166
+ board stopped having two commits earlier. Both recreations are rebuilt against
167
+ what the board actually renders now, the page is on the same ramp as the
168
+ product, emphasis between folds is elevation rather than a slab of hue, and the
169
+ brand mark, favicon and OG card follow. `llms.txt` and every "card" in the copy
170
+ corrected. A marketing page that recreates the product in a different palette
171
+ is a picture of a different product.
172
+ - `DESIGN.md` rewritten to describe what ships, opening with what it replaced and
173
+ why it must not come back, and covering both surfaces from one set of tokens.
174
+ - `README.md` and its screenshot recaptured against the board that now exists.
175
+
176
+ - **A resume pointer that cannot describe a picture that is no longer true.**
177
+ `.baton/RESUME.md` used to be an unowned convenience copy: written once per
178
+ hand-off, never touched again, with no stamp and no expiry, so a terminal that
179
+ exited normally left hours old text sitting there looking live. Four changes,
180
+ in `src/resume.mjs`:
181
+ - Every resume file Baton writes carries a stamp (an HTML comment, invisible
182
+ in rendered markdown) of the commit, branch, working-tree fingerprint and
183
+ live terminals it was written against. The fingerprint is a count and a
184
+ 12-character hash of the sorted paths, never the file names, so a shared
185
+ board cannot leak what someone is working on.
186
+ - Baton owns `RESUME.md`. A session ending rewrites it to "nothing in flight",
187
+ naming the last hand-off, its date and where its full text still lives; the
188
+ board rewrites it at start for any checkout whose pointer describes a
189
+ terminal that is gone, or that no Baton stamped. A terminal that is still
190
+ running keeps its own hand-off text.
191
+ - `baton resume [--check] [--json] [--path <dir>]` recomputes freshness from
192
+ git at read time and never from the file: exit 0 current, 1 stale or
193
+ unstamped, 3 no pointer in this checkout. Stale still prints the body behind
194
+ a loud banner, because a stale hand-off beats nothing when a human chooses
195
+ to read it; the exit code is what scripts and hooks key on.
196
+ - The terminal drawer's "What happens next" section carries the same verdict,
197
+ recomputed on every poll.
198
+ - **The terminal drawer reads newest first.** The conversation and the timeline
199
+ both put the last thing that happened at the top, so the drawer can be left
200
+ open beside the work without scrolling to find the current state.
201
+ - **Fixed: a scrolled box in the drawer snapped back to the top every three
202
+ seconds.** The panel is rebuilt on every poll so relative timestamps stay
203
+ honest, which threw away where the reader had scrolled inside the task box, a
204
+ message or the timeline. Every scrollable box now carries a stable key and its
205
+ offset is carried across the rebuild.
206
+
207
+ ## 0.4.3 (2026-09-14)
208
+
209
+ - **Choose terminal handoff order.** Settings stores the priority order copied
210
+ by new terminals, and each newly attached terminal can change its own order
211
+ while it is running. The order is absolute, not a rotation anchored on the
212
+ agent running now: an agent moved to the bottom (agy, say) is the last option
213
+ from every starting agent instead of jumping to the front of the list for the
214
+ agents above it. The card shows Now, the exact fallback candidates, the
215
+ preferred next option, and the first currently eligible option. Missing CLIs,
216
+ usage walls, same-agent account fallback, all-out waiting, owner controls,
217
+ and normal-exit behavior are unchanged. Older terminals say that a restart
218
+ is required and can save the next-launch default.
219
+ - **See what a terminal is doing.** **Details** on a terminal card (or a click
220
+ on its prompt) opens a drawer: the agent's last turn, the prompt the terminal
221
+ started from, its last eight messages, every file it changed with the line
222
+ counts and a diff per file, the event timeline, and the handoff order that
223
+ comes next. It refreshes every three seconds while open and pauses with the
224
+ tab or the button. Transcripts and diffs are scrubbed for secrets before they
225
+ leave the machine, and a terminal that belongs to another human on a shared
226
+ board stays refused.
227
+ - **The terminal is left the way the agent found it.** An agent killed by End,
228
+ Hand off now or the usage limit never runs its own cleanup, so its terminal
229
+ modes outlived it: the mouse wheel printed raw mouse reports into the shell
230
+ after the session ended, and a leftover scrolling region made the next leg's
231
+ output land on top of the lines already on screen. The restore now turns off
232
+ every mouse reporting mode, bracketed paste and focus events, puts cursor
233
+ keys, the keypad and autowrap back, resets the scrolling region without
234
+ moving the cursor, and clears only the dead agent's half-drawn frame.
235
+ - **Clearer background cards.** The New background card form leads with repo,
236
+ task, and a real first agent; explains its separate worktree and Run now
237
+ versus draft; and puts workflow, fallback, permissions, approvals, limits,
238
+ leases, landing, tests, titles, and labeled test/demo adapters under Advanced
239
+ options. Build only explicitly says it does not merge, while workflows with
240
+ an automatic merge say so in their labels.
241
+
242
+ ## 0.4.2 (2026-09-14)
243
+
244
+ - **Automatic npm publication.** A successful push to `main` now waits for the
245
+ complete Ubuntu and Windows CI matrix, validates package and lock metadata,
246
+ and publishes only when the exact version is missing and newer than npm's
247
+ stable `latest`. It uses npm trusted publishing with OIDC and no repository
248
+ token secret; stale versions and registry errors fail closed.
249
+
250
+ ## 0.4.1 (2026-09-14)
251
+
252
+ - **Codex quota and account display.** Codex usage now comes from the read-only
253
+ app-server `account/rateLimits/read` response, polled every 60 seconds by the
254
+ board and active attach. Returned 300- and 10080-minute windows map to 5h
255
+ and 7d; an explicit backend available answer clears an old wall. The board
256
+ says `<n>% used` and labels stale readings instead of implying current data.
257
+ - **Session and landing safety.** The board offers **Remove record** with a
258
+ confirmation that preserves an own worktree, branch, unmerged commits, and
259
+ dirty files. Normal removal remains a safe prune. Concurrent End and Hand off
260
+ use the session file lock; landing rejects the wrong base branch, proves a
261
+ merged branch before pruning, reruns tests after a retry rebase, and recovers
262
+ orphaned `handing_off` cards.
263
+ - **Board freshness and roles.** Switching from a guest token back to an owner
264
+ restores pipeline controls without a reload; guest tokens still reconnect the
265
+ terminal stream. Card-list, drawer, log, Floor, and SSE responses now reject
266
+ stale asynchronous completions.
267
+ - **Loopback board authorization.** Tokenless owner access now requires both a
268
+ loopback peer and a loopback `Host`, preventing DNS-rebound hostnames from
269
+ inheriting owner access to read and state-changing API routes.
270
+ - **License delivery.** Team refresh is a signed-key POST: an expired signed
271
+ Team key can refresh while its subscription is active, without individual-seat
272
+ revocation. Webhooks accept rotated Stripe `v1` signatures; Resend receives a
273
+ stable event-and-delivery idempotency key, retained by Resend for 24 hours.
274
+
275
+ ## 0.4.0 (2026-09-11)
276
+
277
+ Baton is a paid product as of 14 September 2026: a 14-day trial, then a Personal
278
+ license ($79 once, 12 months of releases) or Team ($12 per seat per month,
279
+ adds `baton share`). The license is the Baton License Agreement (commercial, source readable);
280
+ 0.2.0 and 0.3.0 remain published under MIT and are not deprecated.
281
+
282
+ - **License gate.** `src/license.mjs`: Ed25519-signed keys checked offline
283
+ against the embedded public key; a Personal key is a window over release
284
+ dates (`RELEASE_DATE`), a Team key expires and `baton license refresh`
285
+ renews it; the trial is recorded under `$BATON_HOME`. `baton <agent>` exits
286
+ 4 once nothing is left; `baton share on` needs Team (the trial counts).
287
+ `baton license status|activate|deactivate|refresh`. `scripts/license-sign.mjs`
288
+ signs a key by hand. Ten tests.
289
+ - **The npm package runs.** `files` now ships `fixtures/limits` and
290
+ `fixtures/live`, which `src/limits.mjs` and `src/live-capture.mjs` read at
291
+ load; 0.3.0 from npm crashed on every command for that reason. A lessons
292
+ test packs the tarball and checks it.
293
+ - **Checkout.** Stripe payment links, a `site/api/key` function that turns a
294
+ paid checkout into a key (deterministic per purchase), a webhook that emails
295
+ it through Resend, and `site/thanks` that shows it. `scripts/stripe-setup.mjs`
296
+ creates the plans, links and webhook idempotently; `scripts/vercel-env.mjs`
297
+ pushes the secrets.
298
+ - **A marketing site.** `site/` holds a static page for the product (hero, the handoff played in a recreated terminal, the board and the two-session cards recreated in HTML, the changeover, what is never touched, install), with the SEO floor (title from measured search volume, description, canonical, OG image, robots.txt, sitemap.xml, llms.txt) and Vercel headers. PRODUCT.md and DESIGN.md at the root carry the brief and the design tokens; docs/DECISIONS.md records why cobalt. Not part of the npm package.
299
+
300
+ ## 0.3.2 (2026-09-11)
301
+
302
+ A second adversarial review over 0.3.1, this time of the whole tree: 49
303
+ confirmed bugs fixed, each with a regression test seen red first (340 tests).
304
+
305
+ - **Cards no longer wedge or double-launch.** Hand off now and Reassign on a
306
+ running leg used to throw out of the orchestrator and leave the card in
307
+ `handing_off` holding a concurrency slot with no bundle; both now stop the
308
+ leg, write the bundle, and continue in the same driver. A card is claimed by
309
+ a `driver.lock` for the whole of `runCard`, so a `card run` beside the board's
310
+ scheduler (or two schedulers: `scheduler start` now refuses when one runs and
311
+ the pidfile is created atomically) can never launch two legs into one
312
+ worktree; `runner launch` writes `run.json` with its driver before spawning
313
+ the supervisor. Kill during the launching window kills the supervisor tree
314
+ instead of nothing. A card left running at a test or land station by a crash
315
+ is recovered by the scheduler. Kill or Pause during a test or land station is
316
+ no longer undone by the late result. A land bounce with no agent station, and
317
+ a red test whose only agent station comes later, fail the card instead of
318
+ throwing or skipping ahead. Hand off now is offered only when a next leg
319
+ exists. `--model <adapter>=<name>` (and a chain entry's `network`) now reach
320
+ the agent argv; they were dropped on the way to the runner.
321
+ - **The board stays up.** The test station ran the repo's suite with
322
+ `spawnSync` inside the board server, freezing HTTP, SSE, the scheduler and
323
+ the merge queue for its whole duration; it is async now. The drawer no
324
+ longer refetches on every log byte, the log tail refreshes when the run
325
+ changes, an open drawer is refetched after an SSE reconnect, and the floor
326
+ stops polling on a 401 and shows a way back.
327
+ - **No lost writes.** `run.json` and `card.json` are read-modify-written under
328
+ a cross-process lock (a human Kill was silently reverted by a driver's status
329
+ write). `withFileLock` itself treated Windows' EPERM during a sibling's
330
+ unlink as "give up and run unlocked": it now retries, which also closes the
331
+ hole in the 0.3.1 session and usage locks. `ledger sync` renames the buffer
332
+ before flushing so a record buffered mid-flush survives, and refuses clearly
333
+ when sync is off. The merge queue's turn is a file lock keyed on the
334
+ canonical repo root, so two spellings of one checkout, or two processes,
335
+ never land into it at once.
336
+ - **`baton share` and the launcher.** `share add sam` before `share on` no
337
+ longer makes the guest the owner. A non-loopback board no longer falls open
338
+ to everyone the moment share.json reads as off (`share off` stops the
339
+ listener first, and the bind is re-checked per request). Share off no longer
340
+ promotes a guest's live SSE stream; a stale tab repeating one rotated token
341
+ no longer locks the machine out; `share on|off` no longer kill every running
342
+ agent; `share rm` refuses to take the last owner; the SSE health frame is
343
+ filtered for guests; a redacted session names its branch. The pidfile is
344
+ confirmed by a probe, so `status` cannot report a dead board as running and
345
+ `down` cannot kill whatever reused the PID. Card removal with `?branch=delete`
346
+ no longer escalates to `git branch -D` without `?force=1`, a status check
347
+ that fails no longer reads as clean, and `sessions rm` refuses mid-land.
348
+ - **The limit path.** codex's rollout is found for a session started in the
349
+ local evening (the day directory is local, the filter compared UTC). agy's
350
+ wall is seen behind non-ASCII log lines (a byte count was used as a string
351
+ index) and an old wall is not re-fired by a second agy leg. After a hand-off
352
+ the previous agent's usage bars are cleared, the near-limit warning fires
353
+ again, the chain shown as "next" is recomputed, `all_out` clears after the
354
+ wait, an agy leg adds to the turn count, and a board Hand off with the other
355
+ agents walled restarts the current agent instead of waiting. The resume
356
+ prompt names the absolute RESUME file.
357
+ - Smaller: an agent-created `.env.example` is no longer dropped from a land;
358
+ a card whose trunk does not exist is refused at `card add` naming the repo's
359
+ default branch (it used to burn every land attempt); a rebase git refuses for
360
+ a non-conflict reason bounces `rebase-failed` with git's words, and a
361
+ conflicting retry is no longer reported as `trunk-moved`; `sk-` no longer
362
+ matches ordinary hyphenated words (a task saying "risk-free" was refused as a
363
+ secret); `OPENAI_BASE_URL` is stripped from agent legs; with share off and a
364
+ token, the rate limit is per address, not one shared bucket.
365
+
366
+ ## 0.3.1 (2026-09-11)
367
+
368
+ A security and correctness pass over 0.3.0, after an adversarial review. No new
369
+ features; 44 fixes, all covered by the test suite (281 tests).
370
+
371
+ - **`baton share` security.** A guest could read the owner's pipeline through
372
+ `/api/trunk` (now on the guest deny-list) and through the SSE stream (the
373
+ card/removed broadcasts now carry the per-viewer filter, and every push
374
+ re-checks the viewer against the live roster). `share add|rotate|rm` now take
375
+ effect on a live board instead of after a restart: the server re-reads
376
+ `share.json` per request, so a removed or rotated link stops at once and a new
377
+ one works at once. `baton share add` before `baton share on` can no longer
378
+ make a guest the board's owner. A non-loopback bind also listens on 127.0.0.1
379
+ so the owner keeps a tokenless URL while a remote peer still needs a token.
380
+ Cross-origin state-changing requests are refused. New `test/share-security.test.mjs`
381
+ sends a missing, wrong, other-human, rotated and removed token to every route
382
+ including SSE.
383
+ - **No lost work.** Land refuses a worktree with a rebase/merge already in
384
+ progress (it used to commit the half-state and then abort it, deleting the
385
+ human's uncommitted files). Remove and `baton sessions rm` no longer drop a
386
+ session record while its worktree still holds unlanded commits. `git branch -d`
387
+ before `-D`. A Land never commits a `.env`. A git failure during a Land no
388
+ longer crashes the board process.
389
+ - **No lost writes.** `session.json` and the usage files are written under a
390
+ cross-process lock with a reducer form, so concurrent hooks, taps and pollers
391
+ stop losing files, turns and limit walls to a last-writer-wins race.
392
+ - **The limit path.** The handoff chooser skips agents that are not installed
393
+ (a missing next agent used to kill the session exit 127), the all-out wait
394
+ counts down to the true soonest reset including the current agent, a weekly
395
+ wall is no longer recorded as a 5-hour one, and agy reset durations with more
396
+ than one unit (`71h19m42s`) are summed, not truncated. `live-capture` now
397
+ redacts the home and repo paths, not only secret shapes. The env sanitizer
398
+ also strips the Google/Gemini keys the agy leg kept.
399
+ - A hung test command no longer leaves a card in `landing` forever and wedging
400
+ the repo's merge queue. A crash during `git worktree add` no longer orphans a
401
+ worktree with no card. Two sessions in one checkout get their own
402
+ `RESUME-<id>.md`.
403
+
404
+ ## 0.3.0 (2026-09-11)
405
+
406
+ Closing the gaps against the two tweets: a limit path proved live, waiting
407
+ instead of quitting, collisions stopped instead of flagged, and more than one
408
+ human on the board.
409
+
410
+ - `baton sessions simulate-limit <id>`: a `StopFailure` `rate_limit` payload in
411
+ the shape Claude Code sends (marked simulated) goes through Baton's hook, so
412
+ the whole path runs for real (limit status, bundle, agent stopped, the next
413
+ agent started in the same terminal). Verified live: a haiku session handed
414
+ off to codex, which read `.baton/RESUME.md` on its first turn. The simulated
415
+ wall clears after two minutes and is never kept as evidence. agy: the
416
+ RESOURCE_EXHAUSTED line is appended to the session's own log. codex is
417
+ refused (use `handoff`).
418
+ - The first real claude `StopFailure` is saved with secrets scrubbed under
419
+ `fixtures/live/<agent>/` in a dev clone (outside a clone it goes to
420
+ `~/.baton/live/`, a path nothing has written yet), and the docs row for it
421
+ flips from docs-only to observed-live (`scripts/live-limits.mjs`). The same
422
+ capture is wired for codex `usage_limit_exceeded` and agy
423
+ `RESOURCE_EXHAUSTED`; no real payload for either has been kept yet, and
424
+ their rows are still docs-only.
425
+ - `BATON_CLAUDE_ARGS`, `BATON_CODEX_ARGS`, `BATON_AGY_ARGS`: extra arguments
426
+ for a leg Baton starts after a hand-off (keep a chain on cheap models).
427
+ - codex's card no longer shows the injected AGENTS.md block as the task.
428
+ - When every option is out the terminal no longer exits 3: it prints the reset
429
+ times, counts down to the first one, and starts that agent from the bundle
430
+ when it arrives (`src/wait.mjs`). The card shows `waiting for <agent> at
431
+ <time>`. End on the card quits with exit 3; Ctrl-C is wired to the same exit
432
+ (the End path is the one under test). Verified live: with codex walled for
433
+ 80 s and agy for 200 s, a simulated limit on a haiku session waited and then
434
+ started codex from `.baton/RESUME.md`.
435
+ - A second live session in a checkout that already has one gets its own git
436
+ worktree, `<repo>/.baton-worktrees/<session-id>` on branch
437
+ `baton/<session-id>`, cut from the branch the checkout has out; the terminal
438
+ prints one line saying where. `--no-worktree` shares the checkout (the flag
439
+ never reaches the agent).
440
+ - **Land** on the card of a session with its own worktree sends its branch
441
+ through the merge queue: commit what the agent left, rebase onto the base,
442
+ run the tests, fast-forward only, or bounce with the reason
443
+ (`rebase-conflict` with the files, `tests-red` with the output tail,
444
+ `dirty-trunk` when the checkout has local changes the landing would
445
+ overwrite). The landed-on-trunk list says who landed each commit
446
+ (`~/.baton/landings.jsonl`). Remove takes the worktree and branch along only
447
+ when the worktree is clean and the branch is already on its base. Verified
448
+ live: three haiku sessions in a throwaway repo, the second and third in
449
+ worktrees both appending to README.md; Land on one ran the tests and
450
+ fast-forwarded main, Land on the other bounced with `rebase-conflict` on
451
+ README.md.
452
+ - `baton share`: more than one human on the board, off by default. `baton share
453
+ on` binds the Tailscale address (or `--bind lan`, or one you name) and gives
454
+ every human a name and their own token, kept as a sha256 hash and printed
455
+ once (`baton share rotate <name>` issues a new one). A terminal belongs to
456
+ the human who started it (`BATON_PERSON`). Another human's card is
457
+ read-only: no prompt, no file names, no paths, no limit message, no bundle,
458
+ no events, no logs — the agent, the branch, the usage bars and the reset
459
+ time still show — and one **Request handoff** button that the owner
460
+ approves or dismisses on their own card; the pipeline side stays the
461
+ owner's. The security pass: a token on every `/api` route including the
462
+ event stream (which is computed per viewer), 403 for a guest on anything
463
+ that is not theirs, a lockout after repeated wrong tokens from one address
464
+ and a per-human request ceiling (defaults 20 failures and 600 requests a
465
+ minute, `src/ratelimit.mjs`; the tests drive both at lower thresholds), and
466
+ tests that send a bad and a missing token to every route. Verified live
467
+ over Tailscale with two terminals on one machine, one wes's and one sam's:
468
+ sam's board redacted wes's card, and sam's **Request handoff** reached
469
+ wes's board (the approve-then-hand-off step is covered by tests).
470
+ - The merge queue runs its tests through an async spawn instead of a blocking
471
+ one, so a Land no longer parks the board's event loop.
472
+ - Fixed: a board that asks for a token answered `ensureBoard`'s health probe
473
+ with 401, and Baton read that as "no board" and started a second one.
474
+ - `/api/health` asks each adapter where its binary is instead of running the
475
+ bare name, so the board no longer says `codex: false` while the runner starts
476
+ codex fine.
477
+ - When the claude usage endpoint 404s, answers with something that is not JSON,
478
+ or changes shape, the card says `usage unknown (<why>) · the limit still
479
+ hands off` instead of showing empty bars; the wall still arrives through the
480
+ `StopFailure` hook. `BATON_CLAUDE_USAGE_URL` (or `fetchClaudeUsage({ url })`)
481
+ accepts an http test double.
482
+ - Fixed: the first dirty file on a card (and in the bundle notes) lost its
483
+ first letter (`EADME.md`) because the porcelain status was trimmed;
484
+ `.dashclaw-local/` no longer counts as a file being touched, and it joins
485
+ `.baton/`, `.baton-worktrees/` and `.context-handoffs/` in
486
+ `.git/info/exclude` so a landing never commits it.
487
+ - `BATON_NO_BOARD=1` runs a session without the board; a stub-agent end-to-end
488
+ test (`test/attach-e2e.test.mjs`) now runs `baton claude` through the hook,
489
+ the hand-off, the wait and the restart in CI.
490
+
491
+ ## 0.2.0 (2026-09-11)
492
+
493
+ The way in is now `baton claude`, `baton codex` or `baton agy`: the normal
494
+ interactive agent in your terminal, with Baton alongside it. The v0.1 form,
495
+ pipelines, leases and merge queue stay as extras.
496
+
497
+ - `baton <agent> [args…]` starts the real interactive CLI with your settings,
498
+ hooks and skills; extra args pass straight through. API-key variables are
499
+ stripped; Baton writes its own per-session settings file
500
+ (`~/.baton/sessions/<id>/claude-settings.json`) and passes it with
501
+ `--settings`, instead of editing `~/.claude`, `~/.codex` or agy's home.
502
+ - The board opens once and is reused; every session in every terminal is a
503
+ card: agent, account, repo@branch, task, turns, files being touched, 5h/7d
504
+ usage, warning/limit/handoff state. Two live sessions editing the same file
505
+ in one repo are flagged on both cards. A "landed on trunk" list per repo.
506
+ Buttons: Hand off now, End, Remove.
507
+ - Usage tracking per agent and account. claude: Claude Code's usage endpoint
508
+ with the stored login (a status-line JSON route is written as a fallback;
509
+ on the build machine, Claude Code 2.1.268 was not seen running a custom
510
+ status line — see docs/adapters.md) plus the `StopFailure` `rate_limit`
511
+ hook. codex: the session rollout file
512
+ (`token_count.rate_limits`, `usage_limit_exceeded`). agy: its log
513
+ (`RESOURCE_EXHAUSTED`, "it resets in"); no percentage is exposed.
514
+ - A context-handoff-bundle per session, refreshed every two minutes and at
515
+ every warning/limit/handoff (`save --update <slug>`).
516
+ - Hand-off on limit or on request: bundle, stop the agent, restore the
517
+ terminal, start the next option in the same terminal from `.baton/RESUME.md`.
518
+ Order: other logins of the same agent, then the next agents (claude → codex
519
+ → agy). When every option is out: each reset time, soonest first, then exit
520
+ 3. (0.3.0 waits for the first reset instead.)
521
+ - Optional second logins: `baton accounts add <claude|codex> <name>` (config-dir
522
+ junctions for the shared harness, one login line to paste). Both vendors'
523
+ terms are in the README and are printed by `baton accounts terms`.
524
+ - `baton uninstall` removes only `~/.baton` (it prints what it will remove and
525
+ asks for confirmation).
526
+ - gemini removed everywhere (Google retired Gemini CLI in favour of agy).
527
+ - 0 runtime dependencies still. New modules: src/attach.mjs, src/sessions.mjs,
528
+ src/usage.mjs, src/accounts.mjs, src/bundle.mjs, src/hook.mjs, src/taps/*.
529
+
530
+ ## 0.1.0 (2026-09-10)
531
+
532
+ First public release (source on GitHub; not on npm yet).
533
+
534
+ - Ported the detached runner, the append-only ledger and the git snapshot tool
535
+ from a private ucsandman repository (team tooling) under the same MIT license,
536
+ with Telegram delivery, chat identifiers and machine paths removed (see
537
+ NOTICE, docs/REUSE.md, docs/DEVIATIONS.md).
538
+ - Adapters for claude, codex, gemini and agy, each spawned as argv with a
539
+ sanitized child environment (subscription logins only, no YOLO flags); grok
540
+ written from `--help` but unregistered until a live probe passes.
541
+ - Limit detector built from 25 recorded signals (docs/cli-contracts.md keeps
542
+ the observed-live vs docs-only tag per row); DONE-marker completion contract.
543
+ - Handoffs through context-handoff-bundle (repo-local bundles, `load` resume
544
+ prompt); pipeline presets factory / build / build-land; scheduler with path
545
+ leases; land station as a merge queue (rebase → test → ff-only, bounce with
546
+ the failure in the bundle); `pr` land mode stub.
547
+ - Board (kanban from the pipeline, every judgment a button, detail drawer) and
548
+ floor view (running cards, leases, trunk lane) over a ledger-backed server
549
+ with SSE and a BATON_BIND / BATON_TOKEN seam.
550
+ - Launcher: `npm start` = `baton up` (preflight, health check, prefixed
551
+ redacted logs, Ctrl-C teardown), `up --dry`, `down`, `status`, `open`.
552
+ - Optional syncs, off by default: OpenClaw Workboard (argv) and DashClaw action
553
+ recording (verified live once).
554
+ - Demo (fake limit on the board, five screenshots) and one real run: claude
555
+ hit `--max-turns 2`, codex finished from the bundle, tests green in 2 m 50 s.
556
+ - Polish pass: a missing CLI ends the card `failed` (was stuck `running`);
557
+ runs re-attach after `baton down` instead of relaunching; bounce and
558
+ blocked chips name their reason; repo path must be a git root outside
559
+ BATON_HOME; SSE re-reads only the changed card; reduced-motion and contrast
560
+ checks; docs/VOCABULARY.md.
package/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ Leg License Agreement
2
+
3
+ Copyright 2026 Wes Sander (Practical Systems). All rights reserved.
4
+
5
+ This is a legal agreement between you and Wes Sander ("we") for the Leg
6
+ software, including its source files, documentation and updates ("Leg").
7
+ By installing or using Leg you accept it. If you do not accept it, do not
8
+ install or use Leg.
9
+
10
+ 1. License. When you buy a license we grant you a non-exclusive,
11
+ non-transferable license to install and use Leg as follows.
12
+ Personal: one named person, on any number of machines that person uses.
13
+ Team: one named person per seat purchased. A Team license also covers use
14
+ of the shared board (leg share) by the people holding seats. A Personal
15
+ license covers every Leg release dated on or before the date printed in
16
+ the license key; releases dated after that require a renewal, and the
17
+ releases already covered keep working. A Team license covers releases
18
+ while the subscription is active.
19
+
20
+ 2. Source. Leg ships as readable JavaScript so that you can inspect what it
21
+ does on your machine. You may read it and modify it for your own use under
22
+ your license. You may not publish, distribute, sublicense, rent, sell or
23
+ host Leg or any modified version for others, and you may not remove,
24
+ disable or work around the license check.
25
+
26
+ 3. What is yours. Everything Leg writes for you (session notes, handoff
27
+ bundles, worktrees, your repositories) is yours. Leg sends nothing about
28
+ your work anywhere; the only network calls it makes on its own behalf are
29
+ to this product's site to renew a Team key, and to the agent vendors'
30
+ endpoints that the agent CLIs themselves use.
31
+
32
+ 4. Third parties. Leg starts the coding-agent CLIs you already have and use
33
+ under your own accounts and their vendors' terms. You are responsible for
34
+ complying with those terms; see `leg accounts terms`.
35
+
36
+ 5. Ownership. Leg is licensed, not sold. We keep all rights not expressly
37
+ granted here.
38
+
39
+ 6. Money-back guarantee. Within 30 days of purchase, reply to your receipt
40
+ and we refund it in full. You do not have to give a reason, and you do not
41
+ have to have stopped using Leg before you ask.
42
+
43
+ 7. No warranty. Leg is provided "as is", without warranty of any kind,
44
+ express or implied, including fitness for a particular purpose. Coding
45
+ agents act on your code; you review what they do.
46
+
47
+ 8. Limitation of liability. To the extent the law allows, our total liability
48
+ for any claim related to Leg is limited to the amount you paid for it in
49
+ the 12 months before the claim, and we are not liable for indirect,
50
+ incidental or consequential damages.
51
+
52
+ 9. Termination. This agreement ends if you breach it; on termination you
53
+ stop using Leg. Sections 5, 7 and 8 survive.
54
+
55
+ 10. Law. This agreement is governed by the laws of the State of Ohio, USA,
56
+ without regard to conflict-of-law rules.
57
+
58
+ Contact: legcli@practicalsystems.io
package/NOTICE ADDED
@@ -0,0 +1,4 @@
1
+ Portions of this software (src/runner.mjs, src/ledger.mjs, src/git-snapshot.mjs
2
+ and their tests) were ported on 2026-09-10 from a private ucsandman repository
3
+ (team tooling) under the MIT license; that code is included here under the Leg License Agreement (see LICENSE), which its author also holds. Telegram delivery, chat identifiers,
4
+ and machine-specific paths were removed in the port.