@ucsandman/legcli 0.11.0 → 0.13.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 (65) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +95 -65
  3. package/bin/leg.mjs +123 -14
  4. package/docs/DECISIONS.md +18 -0
  5. package/docs/DEMO.md +20 -14
  6. package/docs/DEVIATIONS.md +1 -0
  7. package/docs/ERRORS.md +68 -0
  8. package/docs/ROADMAP-v2.md +50 -5
  9. package/docs/VOCABULARY.md +27 -0
  10. package/docs/board-guide.md +529 -96
  11. package/docs/cli-contracts.md +241 -5
  12. package/docs/concepts.md +167 -19
  13. package/docs/configuration.md +65 -1
  14. package/docs/faq.md +21 -5
  15. package/docs/getting-started.md +15 -11
  16. package/docs/redesign-2026-09-17.md +477 -0
  17. package/docs/screenshots/background-1280.png +0 -0
  18. package/docs/screenshots/board-400px.png +0 -0
  19. package/docs/screenshots/board-details-open.png +0 -0
  20. package/docs/screenshots/board-drawer.png +0 -0
  21. package/docs/screenshots/board-handoff.png +0 -0
  22. package/docs/screenshots/board-running.png +0 -0
  23. package/docs/screenshots/capacity-drawer-1280.png +0 -0
  24. package/docs/screenshots/floor.png +0 -0
  25. package/docs/screenshots/new-card-dialog.png +0 -0
  26. package/docs/screenshots/settings-ladder-1280.png +0 -0
  27. package/docs/screenshots/terminals-1280.png +0 -0
  28. package/fixtures/limits/claude/claude-fable-limit.json +11 -0
  29. package/fixtures/limits/claude/claude-model-limit.json +1 -1
  30. package/fixtures/limits/claude/claude-session-limit.json +1 -1
  31. package/fixtures/limits/claude/claude-weekly-limit.json +1 -1
  32. package/fixtures/live/claude/resume-model-probe.json +20 -0
  33. package/fixtures/live/claude/usage-oauth.json +87 -0
  34. package/fixtures/verified.json +1 -1
  35. package/package.json +3 -2
  36. package/scripts/board-jump-probe.mjs +335 -0
  37. package/scripts/seed-fake-cards.mjs +59 -6
  38. package/scripts/seed-wes-board.mjs +81 -12
  39. package/src/accounts.mjs +6 -1
  40. package/src/attach.mjs +378 -93
  41. package/src/audit.mjs +1 -1
  42. package/src/board/board.css +203 -11
  43. package/src/board/board.js +664 -200
  44. package/src/board/entry.js +343 -0
  45. package/src/board/floor.html +51 -39
  46. package/src/board/floor.js +585 -73
  47. package/src/board/index.html +122 -45
  48. package/src/board/sessions.js +1569 -141
  49. package/src/board/strip.js +163 -0
  50. package/src/buckets.mjs +101 -0
  51. package/src/cards.mjs +9 -1
  52. package/src/chain.mjs +13 -0
  53. package/src/hook.mjs +7 -1
  54. package/src/ledger.mjs +10 -2
  55. package/src/models.mjs +265 -0
  56. package/src/orchestrator.mjs +13 -4
  57. package/src/preferences.mjs +278 -5
  58. package/src/scheduler.mjs +24 -1
  59. package/src/server.mjs +625 -78
  60. package/src/sessions.mjs +17 -1
  61. package/src/taps/claude-usage.mjs +107 -3
  62. package/src/taps/claude.mjs +144 -5
  63. package/src/taps/codex.mjs +23 -3
  64. package/src/usage-poll.mjs +260 -0
  65. package/src/usage.mjs +439 -12
@@ -39,8 +39,20 @@
39
39
  <p id="verdict-sub"></p>
40
40
  </section>
41
41
 
42
- <section class="logins" id="accounts" aria-label="Logins"></section>
43
- <p class="head-caption" id="head-caption">Times are local.</p>
42
+ <!-- Usage is a property of the work, not a region of its own. The strip is
43
+ one band: a token per login carrying the bucket that will actually stop
44
+ it. The panels are not rewritten, they move intact behind the disclosure
45
+ at the end of the strip, with every gauge, notch and spoken sentence. -->
46
+ <section class="capacity" aria-label="Capacity by login">
47
+ <div class="capacity-strip">
48
+ <div class="cap-tokens" id="capacity-tokens"></div>
49
+ <button type="button" class="btn btn-secondary disclosure cap-open" id="capacity-toggle" aria-expanded="false" aria-controls="capacity-drawer">Capacity and models &gt;</button>
50
+ </div>
51
+ <div class="drawer capacity-drawer" id="capacity-drawer" hidden>
52
+ <section class="logins" id="accounts" aria-label="Logins"></section>
53
+ <p class="head-caption" id="head-caption">Times are local.</p>
54
+ </div>
55
+ </section>
44
56
 
45
57
  <section class="region-terminals" aria-labelledby="terminals-head">
46
58
  <div class="section-head">
@@ -58,6 +70,22 @@
58
70
  <p class="empty-line" hidden>No terminal is running. Start one in any repo: <code>leg claude</code>, <code>leg codex</code> or <code>leg agy</code>. It appears here within a second of the agent's first turn.</p>
59
71
  </section>
60
72
 
73
+ <!-- C.1: a card is a terminal you are not sitting at. Live cards are rows
74
+ here, directly under Terminals; finished ones fall into the ledger
75
+ cell below instead of piling up as rows. -->
76
+ <section class="region-background" id="background" aria-labelledby="background-head" hidden>
77
+ <div class="section-head">
78
+ <h2 id="background-head">Background</h2>
79
+ <p class="region-meta" role="status" aria-atomic="true"></p>
80
+ </div>
81
+ <!-- the live card rows are `.row`: the four-cell grid whose `.r4
82
+ .row-actions` block is written against the terminal rows, so the two
83
+ row kinds line up. `.card-list` is the gap between them, the same
84
+ container the cards drawer uses. -->
85
+ <div class="card-list" id="background-grid"></div>
86
+ <div id="card-entry" hidden></div>
87
+ </section>
88
+
61
89
  <!-- Finished terminals, what landed, and what is queued are all history or
62
90
  background. They sit on the ground rather than on a panel, because a
63
91
  raised surface here would compete with the terminals that are live. -->
@@ -151,13 +179,33 @@
151
179
  first request there is no address above to point at. -->
152
180
  <p class="field-help">Stored in this browser's localStorage and sent as a bearer token with every request. Clear the field to sign out.</p>
153
181
  </div>
182
+ <!-- The fallback ladder (docs/redesign-2026-09-17.md B.3). A rung is a
183
+ destination, not an agent: claude/opus and claude/sonnet are two
184
+ separate steps down before Leg leaves the login at all. The ids are
185
+ the ones the order editor used, because the same save button, status
186
+ span and list container do the same job for the longer shape. -->
154
187
  <fieldset class="default-order" id="default-order" hidden>
155
- <legend>New terminal handoff order</legend>
156
- <p class="field-help">Used by terminals started after you save.</p>
188
+ <legend>New terminal handoff ladder</legend>
189
+ <p class="field-help">Each rung is an agent, a login and a model. Leg walks down from the top and takes the first rung that is open. Used by terminals started after you save; a running terminal keeps the copy it started with, and Details on its row edits that one.</p>
157
190
  <div id="default-order-list"></div>
158
- <button type="button" class="btn btn-secondary" id="default-order-save">Save default</button>
191
+ <div class="ladder-policy" id="ladder-spend"></div>
192
+ <fieldset class="ladder-policy" id="ladder-climb"></fieldset>
193
+ <div class="ladder-policy" id="ladder-reserve"></div>
194
+ <button type="button" class="btn btn-secondary" id="default-order-save">Save ladder</button>
159
195
  <span class="field-status" id="default-order-status" aria-live="polite"></span>
160
196
  </fieldset>
197
+ <!-- Where a terminal that is waiting on a human says so when the board is
198
+ not the window you are looking at. The tab badge needs no toggle: it
199
+ needs no permission and costs nothing, so it is always on. -->
200
+ <fieldset class="notify" id="notify-settings" hidden>
201
+ <legend>Notifications</legend>
202
+ <p class="field-help">The tab title says <code>(1) Leg</code> and the favicon gains a dot whenever a terminal is waiting on you. That one is always on.</p>
203
+ <label class="notify-toggle" for="notify-terminal"><input type="checkbox" id="notify-terminal"> Terminal toast when a terminal waits on you</label>
204
+ <p class="field-help">Claude Code only, through its Notification hook: the toast reaches the window the agent is already running in.</p>
205
+ <label class="notify-toggle" for="notify-board"><input type="checkbox" id="notify-board"> Browser notification when a terminal waits on you</label>
206
+ <p class="field-help" id="notify-board-help"></p>
207
+ <span class="field-status" id="notify-status" aria-live="polite"></span>
208
+ </fieldset>
161
209
  <!-- Who did what, across every terminal and every card. The owner's, like
162
210
  the rest of this panel: the trail names repositories and people. It
163
211
  loads when asked rather than on every board render. -->
@@ -196,35 +244,67 @@
196
244
  <div class="detail-inner" id="session-drawer-content"></div>
197
245
  </div>
198
246
 
199
- <dialog id="new-card-dialog" aria-label="New card">
200
- <h2 class="dialog-title">New background card</h2>
247
+ <!-- the announced name is the heading on screen: an aria-label here would be a
248
+ second name for one thing, and the one a voice-control user says out loud
249
+ is the one they can read -->
250
+ <dialog id="new-card-dialog" aria-labelledby="new-card-dialog-title">
251
+ <h2 class="dialog-title" id="new-card-dialog-title">More settings</h2>
201
252
  <p class="dialog-intro">An agent works without an interactive terminal in a separate git worktree. The default Build workflow stops with changes in that worktree and does not merge them.</p>
202
253
  <form id="new-card-form">
203
254
  <div class="form-error" id="new-card-error" hidden></div>
204
255
 
205
- <div class="form-row">
206
- <label for="nc-repo">Repo path</label>
207
- <input type="text" id="nc-repo" required autocomplete="off">
208
- </div>
256
+ <!-- Two questions, side by side on a wide screen and stacked on a phone:
257
+ WHAT is being asked for (left) and WHO runs it (right). The right
258
+ column opens showing the saved handoff ladder, so the dialog and the
259
+ one-line entry row above it say the same thing until the reader
260
+ changes one of them. -->
261
+ <div class="dialog-cols">
262
+ <div class="dialog-col">
263
+
264
+ <div class="form-row">
265
+ <label for="nc-task">Task</label>
266
+ <span class="field-help">Describe one concrete outcome the agent can finish.</span>
267
+ <textarea id="nc-task" rows="5" required autofocus></textarea>
268
+ </div>
209
269
 
210
- <div class="form-row">
211
- <label for="nc-task">Task</label>
212
- <span class="field-help">Describe one concrete outcome the agent can finish.</span>
213
- <textarea id="nc-task" rows="3" required></textarea>
214
- </div>
270
+ <div class="form-row">
271
+ <label for="nc-repo-known">Repository</label>
272
+ <select id="nc-repo-known"></select>
273
+ <label for="nc-repo" class="sub-label">Path</label>
274
+ <input type="text" id="nc-repo" required autocomplete="off" spellcheck="false">
275
+ </div>
215
276
 
216
- <div class="form-row">
217
- <label for="nc-first-agent">First agent</label>
218
- <select id="nc-first-agent" required></select>
219
- <span class="field-help" id="nc-fallback-summary">If it cannot continue, Leg tries the fallback agents shown under Advanced options.</span>
220
- </div>
277
+ <div class="form-row">
278
+ <label for="nc-trunk">Branch to cut from</label>
279
+ <input type="text" id="nc-trunk" value="main" autocomplete="off" spellcheck="false">
280
+ <span class="field-help" id="nc-trunk-help">The card works in its own checkout cut from this branch.</span>
281
+ </div>
282
+
283
+ <div class="form-row">
284
+ <label for="nc-queue">
285
+ <input type="checkbox" id="nc-queue" checked>
286
+ Run now
287
+ </label>
288
+ <span class="field-help">Turn this off to save a draft in Backlog.</span>
289
+ </div>
290
+
291
+ </div>
292
+ <div class="dialog-col">
293
+
294
+ <div class="form-row">
295
+ <span class="form-label" id="nc-chain-label">Who runs it</span>
296
+ <span class="field-help">Leg starts at the top and takes the next row only when the row above it cannot continue. Each row is a provider and one of its models.</span>
297
+ <div id="nc-chain-rows" role="group" aria-labelledby="nc-chain-label"></div>
298
+ <button type="button" class="btn btn-secondary" id="nc-add-row">Add a fallback</button>
299
+ <span class="field-help" id="nc-fallback-summary"></span>
300
+ <label for="nc-save-ladder" class="notify-toggle">
301
+ <input type="checkbox" id="nc-save-ladder">
302
+ Save as my default ladder
303
+ </label>
304
+ <span class="field-help">Writes these rows to Settings, so the one-line entry row and every new terminal use them too.</span>
305
+ </div>
221
306
 
222
- <div class="form-row">
223
- <label for="nc-queue">
224
- <input type="checkbox" id="nc-queue" checked>
225
- Run now
226
- </label>
227
- <span class="field-help">Turn this off to save a draft in Backlog.</span>
307
+ </div>
228
308
  </div>
229
309
 
230
310
  <details class="advanced-options">
@@ -241,21 +321,9 @@
241
321
  <textarea id="nc-custom-pipeline" rows="4" hidden aria-label="Custom workflow JSON" placeholder='[{"name":"build","kind":"agent"}]'></textarea>
242
322
  </div>
243
323
 
244
- <div class="form-row">
245
- <label>First-agent permissions and limits</label>
246
- <div id="nc-first-controls"></div>
247
- </div>
248
-
249
- <div class="form-row">
250
- <label>Fallback agents</label>
251
- <span class="field-help">Leg tries each row in order only when the agent before it cannot continue.</span>
252
- <div id="nc-chain-rows"></div>
253
- <button type="button" class="btn btn-secondary" id="nc-add-row">Add fallback agent</button>
254
- </div>
255
-
256
324
  <div class="form-row">
257
325
  <label for="nc-test-adapter">Scripted first agent (test/demo only)</label>
258
- <select id="nc-test-adapter"><option value="">Use the real first agent above</option></select>
326
+ <select id="nc-test-adapter"><option value="">Use the real first row above</option></select>
259
327
  </div>
260
328
 
261
329
  <div class="form-row">
@@ -264,11 +332,6 @@
264
332
  <input type="text" id="nc-leases">
265
333
  </div>
266
334
 
267
- <div class="form-row">
268
- <label for="nc-trunk">Trunk branch</label>
269
- <input type="text" id="nc-trunk" value="main">
270
- </div>
271
-
272
335
  <div class="form-row">
273
336
  <label for="nc-land-mode">Merge method</label>
274
337
  <select id="nc-land-mode">
@@ -296,8 +359,22 @@
296
359
  </form>
297
360
  </dialog>
298
361
 
362
+ <!-- D14: every binding clicks a button that is already on a row, so this map is
363
+ the list of those buttons and nothing else can drift from it. sessions.js
364
+ fills it from the same table the key handler reads. -->
365
+ <div class="keymap" id="keymap" role="dialog" aria-label="Keyboard shortcuts" hidden>
366
+ <h3 class="drawer-head">Keyboard</h3>
367
+ <dl class="keymap-list" id="keymap-list"></dl>
368
+ <button type="button" class="btn btn-secondary" id="keymap-close">Close</button>
369
+ </div>
370
+
299
371
  <div class="sysmsg" id="toast" aria-live="polite"></div>
300
372
 
373
+ <!-- strip.js and entry.js are shared with /floor and are read at load by the
374
+ two files under them (the capacity tokens, the one-line entry), so they
375
+ are first: deferred scripts run in document order. -->
376
+ <script src="strip.js" defer></script>
377
+ <script src="entry.js" defer></script>
301
378
  <script src="board.js" defer></script>
302
379
  <script src="sessions.js" defer></script>
303
380
  <script src="history.js" defer></script>