@ucsandman/legcli 0.8.0 → 0.10.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.
- package/CHANGELOG.md +121 -0
- package/NOTICE +8 -0
- package/README.md +639 -560
- package/bin/fake-agent.mjs +4 -4
- package/bin/leg.mjs +43 -12
- package/docs/DECISIONS.md +20 -2
- package/docs/ERRORS.md +205 -0
- package/docs/README.md +5 -1
- package/docs/REUSE.md +1 -1
- package/docs/VOCABULARY.md +22 -0
- package/docs/board-guide.md +33 -1
- package/docs/cli-contracts.md +36 -1
- package/docs/concepts.md +42 -3
- package/docs/configuration.md +23 -1
- package/docs/faq.md +19 -0
- package/docs/getting-started.md +272 -251
- package/docs/harness.md +319 -0
- package/docs/history.md +172 -0
- package/docs/runtime-tap.md +156 -0
- package/fixtures/verified.json +1 -1
- package/package.json +7 -3
- package/scripts/build-docs-site.mjs +18 -4
- package/scripts/check-branding.mjs +118 -0
- package/scripts/check-claims.mjs +1 -1
- package/scripts/license-sign.mjs +1 -1
- package/scripts/limits-table.mjs +1 -1
- package/scripts/live-limits.mjs +1 -1
- package/scripts/npm-publish-gate.mjs +114 -0
- package/scripts/probe.mjs +4 -3
- package/scripts/seed-fake-cards.mjs +4 -3
- package/scripts/seed-floor-board.mjs +5 -4
- package/scripts/seed-wes-board.mjs +5 -4
- package/scripts/stripe-setup.mjs +1 -1
- package/scripts/sync-harness-engine.mjs +159 -0
- package/scripts/sync-leg-agents.mjs +127 -0
- package/src/accounts.mjs +6 -4
- package/src/adapters/codex.mjs +1 -1
- package/src/attach.mjs +125 -23
- package/src/auth.mjs +2 -2
- package/src/board/board.css +23 -1
- package/src/board/board.js +17 -5
- package/src/board/history.js +377 -0
- package/src/board/index.html +33 -0
- package/src/board/sessions.js +95 -7
- package/src/bundle.mjs +54 -8
- package/src/chain.mjs +1 -1
- package/src/contract.mjs +4 -3
- package/src/fsx.mjs +5 -2
- package/src/handoff.mjs +6 -6
- package/src/harness/cli.mjs +281 -0
- package/src/harness/fingerprint.mjs +68 -0
- package/src/harness/index.mjs +407 -0
- package/src/harness/registry.mjs +124 -0
- package/src/harness/vendor/agnostic-ai/LICENSE +21 -0
- package/src/harness/vendor/agnostic-ai/UPSTREAM.json +30 -0
- package/src/harness/vendor/agnostic-ai/core/safety/guards.json +96 -0
- package/src/harness/vendor/agnostic-ai/core/templates/targets.json +252 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/README.md +199 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/apply.cjs +247 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/bundle.cjs +243 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/capture.cjs +119 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/common.cjs +375 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/index.cjs +55 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/sources/claude.cjs +330 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/sources/codex.cjs +314 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/status.cjs +171 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/targets/agy.cjs +113 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/targets/claude.cjs +158 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/targets/codex.cjs +832 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/targets/cursor.cjs +87 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/targets/gemini.cjs +128 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/targets/generic.cjs +424 -0
- package/src/harness/vendor/agnostic-ai/engine/harness/toml.cjs +149 -0
- package/src/harness/vendor/agnostic-ai/engine/hooks/shim.cjs +431 -0
- package/src/history/cli.mjs +159 -0
- package/src/history/common.mjs +119 -0
- package/src/history/index.mjs +429 -0
- package/src/history/providers/agy.mjs +91 -0
- package/src/history/providers/claude.mjs +161 -0
- package/src/history/providers/codex.mjs +133 -0
- package/src/history/providers/copilot.mjs +94 -0
- package/src/history/providers/grok.mjs +138 -0
- package/src/history/worktrees.mjs +116 -0
- package/src/hook.mjs +49 -49
- package/src/land.mjs +7 -35
- package/src/launcher.mjs +38 -26
- package/src/ledger.mjs +6 -6
- package/src/license.mjs +10 -9
- package/src/live-capture.mjs +1 -1
- package/src/mergequeue.mjs +5 -5
- package/src/orchestrator.mjs +28 -4
- package/src/preferences.mjs +37 -3
- package/src/redact.mjs +24 -6
- package/src/resume.mjs +17 -15
- package/src/runner.mjs +2 -2
- package/src/scheduler.mjs +1 -1
- package/src/server.mjs +224 -18
- package/src/session-detail.mjs +15 -1
- package/src/sessions.mjs +15 -3
- package/src/share.mjs +2 -2
- package/src/stations/agent.mjs +1 -1
- package/src/sync/dashclaw.mjs +4 -4
- package/src/synthesis.mjs +165 -0
- package/src/taps/agy.mjs +2 -2
- package/src/taps/claude-usage.mjs +1 -1
- package/src/taps/claude.mjs +177 -170
- package/src/taps/codex.mjs +286 -286
- package/src/taps/grok.mjs +2 -2
- package/src/taps/mod.mjs +340 -0
- package/src/trust.mjs +205 -36
- package/src/usage.mjs +5 -1
- package/src/worktree.mjs +6 -5
- package/fixtures/live/agy/attempt-1-scratch-workspace.out.log +0 -1
- package/fixtures/live/agy/err.log +0 -0
- package/fixtures/live/agy/out.log +0 -1
- package/fixtures/live/agy/supervisor.log +0 -2
- package/fixtures/live/claude/err.log +0 -0
- package/fixtures/live/claude/out.log +0 -1
- package/fixtures/live/claude/supervisor.log +0 -2
- package/fixtures/live/codex/err.log +0 -1
- package/fixtures/live/codex/out.log +0 -8
- package/fixtures/live/codex/supervisor.log +0 -2
- package/fixtures/live/grok/err.log +0 -32
- package/fixtures/live/grok/out.log +0 -7
- package/fixtures/live/grok/supervisor.log +0 -2
package/README.md
CHANGED
|
@@ -1,560 +1,639 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
**Type `leg claude`, `leg codex`, `leg agy` or `leg grok` instead of the bare command. You get the same interactive agent; Leg opens a board next to it, watches the usage limit, keeps a handoff bundle current, and when the limit hits it starts the next agent in the same terminal from that bundle.**
|
|
4
|
-
|
|
5
|
-
[](https://legcli.com/license)
|
|
6
|
-
[](https://legcli.com/docs/getting-started)
|
|
7
|
-
[](https://legcli.com/docs)
|
|
8
|
-
[](https://legcli.com/docs/configuration)
|
|
9
|
-
|
|
10
|
-

|
|
11
|
-
|
|
12
|
-
*Claude hits the five-hour wall. The terminal reads `handing off to codex`, and codex carries on there. Nothing is retyped. ([the full 53-second run](https://legcli.com/#handoff))*
|
|
13
|
-
|
|
14
|
-

|
|
15
|
-
|
|
16
|
-
You keep using your coding agents exactly as you do today, in any terminal,
|
|
17
|
-
from your own config directory: Leg adds its hooks in a separate per-session
|
|
18
|
-
settings file and never edits yours
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`
|
|
45
|
-
`
|
|
46
|
-
`
|
|
47
|
-
`
|
|
48
|
-
`
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
that
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
|
70
|
-
|
|
71
|
-
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
- [
|
|
93
|
-
- [
|
|
94
|
-
- [
|
|
95
|
-
- [
|
|
96
|
-
- [
|
|
97
|
-
- [
|
|
98
|
-
- [
|
|
99
|
-
- [
|
|
100
|
-
- [
|
|
101
|
-
- [
|
|
102
|
-
- [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
agent
|
|
188
|
-
at
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
terminal
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
The
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
`
|
|
444
|
-
`
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
##
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
1
|
+
# Leg
|
|
2
|
+
|
|
3
|
+
**Type `leg claude`, `leg codex`, `leg agy` or `leg grok` instead of the bare command. You get the same interactive agent; Leg opens a board next to it, watches the usage limit, keeps a handoff bundle current, and when the limit hits it starts the next agent in the same terminal from that bundle.**
|
|
4
|
+
|
|
5
|
+
[](https://legcli.com/license)
|
|
6
|
+
[](https://legcli.com/docs/getting-started)
|
|
7
|
+
[](https://legcli.com/docs)
|
|
8
|
+
[](https://legcli.com/docs/configuration)
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
*Claude hits the five-hour wall. The terminal reads `handing off to codex`, and codex carries on there. Nothing is retyped. ([the full 53-second run](https://legcli.com/#handoff))*
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
You keep using your coding agents exactly as you do today, in any terminal,
|
|
17
|
+
from your own config directory: Leg adds its hooks in a separate per-session
|
|
18
|
+
settings file and never edits yours (unless you turn on the [portable
|
|
19
|
+
harness](docs/harness.md), which writes only marked, backed-up, Leg-owned
|
|
20
|
+
files and regions). `leg claude --model opus` is
|
|
21
|
+
`claude --model opus` with four things running alongside it:
|
|
22
|
+
|
|
23
|
+
1. **A board.** Opened once in your browser, reused after that. Every Leg
|
|
24
|
+
session in every terminal is a card on it: agent, account, repo and branch,
|
|
25
|
+
the task, the files it is touching, its 5h and 7d usage, what has landed on
|
|
26
|
+
trunk. Two sessions editing the same file in one repo are flagged on both
|
|
27
|
+
cards, and a second session in a checkout that already has one gets its
|
|
28
|
+
own worktree and a **Land** button instead of writing over the first.
|
|
29
|
+
2. **Usage tracking** per agent and account, from what each CLI already
|
|
30
|
+
exposes: Claude Code's usage endpoint and its `StopFailure` hook, Codex's
|
|
31
|
+
read-only app-server rate-limit read, agy's log, and Grok's billing proxy endpoint.
|
|
32
|
+
3. **A context handoff bundle** ([context-handoff-bundle](https://pypi.org/project/context-handoff-bundle/))
|
|
33
|
+
refreshed as the session goes, so the work is always ready to hand off.
|
|
34
|
+
4. **The handoff itself.** Near the limit you get a warning. At the limit Leg
|
|
35
|
+
saves the bundle, stops the agent, and starts the next option in the same
|
|
36
|
+
terminal from that bundle: another login of the same agent if you added
|
|
37
|
+
one, otherwise the next agent in the order shown on the terminal card.
|
|
38
|
+
The default is claude -> codex -> agy (with grok supported in handoff order),
|
|
39
|
+
and Settings changes the default for new terminals. Nothing is retyped.
|
|
40
|
+
When every option is out, it tells you which resets first and when, waits
|
|
41
|
+
for that reset with a countdown, and starts that agent from the bundle.
|
|
42
|
+
|
|
43
|
+
Subscription logins only: Leg strips `ANTHROPIC_API_KEY`,
|
|
44
|
+
`ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_CUSTOM_HEADERS`,
|
|
45
|
+
`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_API_BASE`, `GEMINI_API_KEY`,
|
|
46
|
+
`GOOGLE_API_KEY`, `GOOGLE_GEMINI_BASE_URL`, `GOOGLE_GENAI_USE_VERTEXAI`,
|
|
47
|
+
`GOOGLE_GENAI_USE_ENTERPRISE`, `GOOGLE_CLOUD_PROJECT`,
|
|
48
|
+
`GOOGLE_CLOUD_LOCATION`, `GOOGLE_APPLICATION_CREDENTIALS`, `GROK_API_KEY`,
|
|
49
|
+
`XAI_API_KEY`, `CLAUDECODE`, `CLAUDE_CODE_*`, `CLAUDE_EFFORT`, and
|
|
50
|
+
`CLAUDE_PLUGIN_DATA` before any agent starts. It then sets
|
|
51
|
+
`CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0` for a detached
|
|
52
|
+
Claude print session. Leg never edits `~/.claude/settings.json` or any other
|
|
53
|
+
settings file of yours; its hooks ride in a separate per-session `--settings`
|
|
54
|
+
file. The one thing it does write outside `~/.leg` by default is the
|
|
55
|
+
folder-trust answer, below; the optional [portable harness](docs/harness.md)
|
|
56
|
+
is the other, and only after `leg harness enable`. `leg uninstall` removes only `~/.leg`.
|
|
57
|
+
|
|
58
|
+
### The folder-trust answer
|
|
59
|
+
|
|
60
|
+
Each agent CLI asks once, the first time it runs in a directory, whether you
|
|
61
|
+
trust that folder, and Claude Code asks a second question when a `CLAUDE.md`
|
|
62
|
+
above the repo imports a file from outside it. A handoff fires when the limit
|
|
63
|
+
hits, which is usually when nobody is watching, so an agent that stopped on
|
|
64
|
+
that prompt would sit there until morning with the bundle already written.
|
|
65
|
+
|
|
66
|
+
Before starting an agent, Leg records the same answer you would have given,
|
|
67
|
+
for the repository you already chose by typing `leg claude` in it:
|
|
68
|
+
|
|
69
|
+
| agent | file | what is written |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| claude | `~/.claude.json` (or `$CLAUDE_CONFIG_DIR/.claude.json`) | `projects["<repo>"].hasTrustDialogAccepted: true` |
|
|
72
|
+
| claude | the same entry, only when such an import exists | `hasClaudeMdExternalIncludesApproved`, `hasClaudeMdExternalIncludesWarningShown` |
|
|
73
|
+
| codex | `~/.codex/config.toml` | `[projects."<repo>"] trust_level = "trusted"` |
|
|
74
|
+
| agy | `~/.gemini/antigravity-cli/settings.json` | `trustedWorkspaces: ["<repo>", "<worktree>"]` |
|
|
75
|
+
|
|
76
|
+
For Claude Code this is the documented remedy: its permissions guide says to
|
|
77
|
+
set `projects["<path>"].hasTrustDialogAccepted` to `true` in `~/.claude.json`,
|
|
78
|
+
where `<path>` is the repository root.
|
|
79
|
+
|
|
80
|
+
Leg never creates one of those files: if it is not there, that CLI has not
|
|
81
|
+
run as you yet and its own first-run flow is next, with you at the keyboard. It
|
|
82
|
+
never rewrites a file to say what it already says, and it never removes what is
|
|
83
|
+
already in one. When it approves an external `CLAUDE.md` import it prints the
|
|
84
|
+
full path of every file it approved, to the terminal and to the session
|
|
85
|
+
timeline on the board, so the approval is on the record rather than invisible.
|
|
86
|
+
|
|
87
|
+
Set `LEG_TRUST=never` to switch all of it off and answer the prompts
|
|
88
|
+
yourself.
|
|
89
|
+
|
|
90
|
+
## Contents
|
|
91
|
+
|
|
92
|
+
- [60-second run](#60-second-run)
|
|
93
|
+
- [What Leg reads from each agent](#what-leg-reads-from-each-agent)
|
|
94
|
+
- [How a handoff works](#how-a-handoff-works)
|
|
95
|
+
- [Two sessions in one repo](#two-sessions-in-one-repo)
|
|
96
|
+
- [More than one human](#more-than-one-human)
|
|
97
|
+
- [The board](#the-board)
|
|
98
|
+
- [Every conversation on this machine](#every-conversation-on-this-machine)
|
|
99
|
+
- [Second accounts, and what the terms say](#second-accounts-and-what-the-terms-say)
|
|
100
|
+
- [What is and is not touched](#what-is-and-is-not-touched)
|
|
101
|
+
- [CLI reference](#cli-reference)
|
|
102
|
+
- [Background tasks: the v0.1 extras](#background-tasks-the-v01-extras)
|
|
103
|
+
- [Troubleshooting](#troubleshooting)
|
|
104
|
+
- [Documentation](#documentation)
|
|
105
|
+
- [Contributing](#contributing)
|
|
106
|
+
- [License](#license)
|
|
107
|
+
|
|
108
|
+
## 60-second run
|
|
109
|
+
|
|
110
|
+
Prerequisites: Node 22 or newer, git, Python 3 with pip, and at least one
|
|
111
|
+
logged-in agent CLI (`claude`, `codex`, `agy` or `grok`).
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
npm install -g @ucsandman/legcli
|
|
115
|
+
pip install -U context-handoff-bundle
|
|
116
|
+
cd <any repo>
|
|
117
|
+
leg claude
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`npm install -g leg-agents` is the same release: it pins this version of
|
|
121
|
+
`@ucsandman/legcli` and exposes the same `leg` binary.
|
|
122
|
+
|
|
123
|
+
That is the whole setup. The first `leg <agent>` starts the board on
|
|
124
|
+
http://127.0.0.1:4747 and opens it; later sessions reuse it. Anything after the
|
|
125
|
+
agent name passes straight through (`leg codex -m gpt-5.3-codex-spark`,
|
|
126
|
+
`leg claude --resume`). The agent's own prompt and permission flags pass
|
|
127
|
+
through unchanged, and your settings file is never edited: Leg's hooks ride
|
|
128
|
+
in a separate per-session `--settings` file.
|
|
129
|
+
|
|
130
|
+
The source repository is private. Leg is commercial, source-available
|
|
131
|
+
software: every `.mjs` file that runs is in the package you just installed, at
|
|
132
|
+
`$(npm root -g)/legcli/src`, and the license lets you read it and modify
|
|
133
|
+
your own copy. There is nothing compiled, minified or bundled to see through.
|
|
134
|
+
|
|
135
|
+
## What Leg reads from each agent
|
|
136
|
+
|
|
137
|
+
Nothing is guessed from screen scraping. Each tap was read from the CLI's
|
|
138
|
+
source or documentation and then checked on a real machine (2026-09-11,
|
|
139
|
+
Claude Code 2.1.268, codex-cli 0.153.4, agy 1.2.0); the rightmost column says
|
|
140
|
+
which.
|
|
141
|
+
|
|
142
|
+
| agent | usage percentages | the wall (limit hit) | how Leg attaches | status |
|
|
143
|
+
|-------|-------------------|----------------------|--------------------|--------|
|
|
144
|
+
| claude | `GET api.anthropic.com/api/oauth/usage` with the login Claude Code stored, the same data as `/usage` and the built-in status line (`five_hour`, `seven_day`, `utilization`, `resets_at`); polled every 60 s | `StopFailure` hook with `error: rate_limit` ([docs](https://code.claude.com/docs/en/hooks#stopfailure)) | one extra settings file per session via `--settings`, carrying only Leg's own hooks; `autoContinueAtUsageLimit` is set to `false` because Leg owns the handoff | observed live |
|
|
145
|
+
| codex | read-only `account/rateLimits/read` through the app-server, polled every 60 s by the board and active attach; windows are identified by duration (300 minutes = 5h, 10080 = 7d) | `task_complete.error.codex_error_info: usage_limit_exceeded`, message "You've hit your usage limit … try again at …" (`codex-rs/protocol/src/error.rs`) | no model turn and no hook are injected; the board reads the CLI backend and records only returned windows | verified by source and regression tests |
|
|
146
|
+
| agy | none exposed (agy's own status line fetches a quota summary that is written nowhere) | `RESOURCE_EXHAUSTED`, "it resets in …", "out of quota" in the log | `--log-file` per session; `~/.gemini/antigravity-cli/history.jsonl` gives the prompts and conversation id | observed live (a real `RESOURCE_EXHAUSTED` with its reset was read from the log on 2026-09-11) |
|
|
147
|
+
| grok | `GET cli-chat-proxy.grok.com/v1/billing?format=credits` and `GET cli-chat-proxy.grok.com/v1/user?include=subscription` with OAuth token from `~/.grok/auth.json` (`creditUsagePercent`, `currentPeriod` weekly reset); polled every 60 s | `-32003` rate limit error, "You've hit the rate limit for your plan. Try again later.", "Rate limited (429)", `StopFailureKind::RateLimit` (`xai-org/grok-build`) | `--debug-file grok.log` per session; `~/.grok/sessions/<encoded-cwd>/prompt_history.jsonl` gives prompts and session id | verified by source and proxy endpoint |
|
|
148
|
+
|
|
149
|
+
Why not Claude Code's status line JSON (`rate_limits.five_hour.used_percentage`):
|
|
150
|
+
on 2.1.268 the custom `statusLine` Leg passes through `--settings` did not
|
|
151
|
+
run, so the endpoint poll is the source. Leg still writes a `statusLine`
|
|
152
|
+
entry that records the same fields, so the moment a build honours it the poll
|
|
153
|
+
becomes a fallback.
|
|
154
|
+
|
|
155
|
+
For Codex, Leg does not infer availability from a lower percentage: only an
|
|
156
|
+
explicit available answer from the backend clears an earlier wall. The board
|
|
157
|
+
labels each bar as `<n>% used` and marks an old reading as stale rather than
|
|
158
|
+
presenting it as current.
|
|
159
|
+
|
|
160
|
+
## How a handoff works
|
|
161
|
+
|
|
162
|
+
1. **Warning.** At 85 % of any window (`LEG_WARN_PCT`) the session card turns
|
|
163
|
+
amber, the event log names the next option, and the terminal bell rings once.
|
|
164
|
+
2. **Limit.** claude: the `StopFailure` hook fires with `rate_limit`. codex: the
|
|
165
|
+
rollout reports `usage_limit_exceeded`. agy: the log says
|
|
166
|
+
`RESOURCE_EXHAUSTED`. The account is marked walled until the reset the CLI
|
|
167
|
+
reported (or the soonest known window reset).
|
|
168
|
+
3. **Bundle.** Leg writes structured notes (task, the last messages from the
|
|
169
|
+
transcript, `git diff --stat`, dirty files, files edited this session, recent
|
|
170
|
+
commits, why it stopped) and runs `context-handoff-bundle save --repo-local`
|
|
171
|
+
with one slug per leg; each save writes its own timestamped bundle next to
|
|
172
|
+
the last one. A checkpoint of the same bundle is taken every two minutes
|
|
173
|
+
while the session is active. If the session maintained `.leg/SYNTHESIS-<session-id>.md`,
|
|
174
|
+
Leg inlines it into the resume file as a `## Synthesis` section ahead of the raw dump.
|
|
175
|
+
4. **Switch.** The agent process is stopped, the terminal is restored, and the
|
|
176
|
+
next option starts in the same terminal with a short pointer prompt:
|
|
177
|
+
read `.leg/RESUME-<session-id>.md` (the `context-handoff-bundle load`
|
|
178
|
+
output, the `## Synthesis` section if present, and the reason for the switch),
|
|
179
|
+
check `git status` and `git diff`, continue, do not ask the human to restate
|
|
180
|
+
the task. The pointer prompt directs the next agent to read Synthesis first,
|
|
181
|
+
treat ruled-out approaches as settled, and start from the top-ranked next step.
|
|
182
|
+
The same text is copied to `.leg/RESUME.md`, the file people open by habit, and
|
|
183
|
+
both are stamped with the commit and the live terminals they describe.
|
|
184
|
+
`claude "<prompt>"`, `codex "<prompt>"` and `agy -i "<prompt>"` all open the
|
|
185
|
+
normal interactive session with that first turn.
|
|
186
|
+
5. **Order.** Other accounts of the same agent come first, then every other
|
|
187
|
+
agent in the saved order, each tried once. The order is a priority list, not
|
|
188
|
+
a rotation: put agy at the bottom and agy is the last option from a Claude
|
|
189
|
+
terminal and from a Codex terminal alike. The board shows the exact
|
|
190
|
+
sequence with the agent running now skipped, plus the preferred option and
|
|
191
|
+
the first option eligible from current install and limit state. Use **Change
|
|
192
|
+
order** on a terminal card to change that terminal, or Settings to set the
|
|
193
|
+
default copied by new terminals. An option whose CLI is missing or whose
|
|
194
|
+
wall has not reset is skipped.
|
|
195
|
+
6. **All out.** The terminal prints each option with its reset time, soonest
|
|
196
|
+
first, then stays open with a countdown to the first reset and starts that
|
|
197
|
+
agent from the bundle when it arrives. The card says `waiting for <agent>
|
|
198
|
+
at <time>`. Ctrl-C (or End on the card) quits with exit 3 instead.
|
|
199
|
+
|
|
200
|
+
You can force a handoff any time: the **Hand off now** button on the card, or
|
|
201
|
+
`leg sessions handoff <id>`. Verified on this machine: `leg claude` opened
|
|
202
|
+
the real Claude Code TUI with Leg's hooks firing into the session log, the
|
|
203
|
+
usage poll recorded 36 % of the 5h window and 74 % of the 7d window, the
|
|
204
|
+
warning fired at 96 % of the 7d window and named codex as the next option, and
|
|
205
|
+
a limit saved the bundle, stopped claude and started codex in the same
|
|
206
|
+
terminal with the pointer prompt. A real `StopFailure` arrived on 2026-09-11
|
|
207
|
+
and is kept at `fixtures/live/claude/limit-rate_limit.json`; to drive the path
|
|
208
|
+
on demand, `leg sessions simulate-limit <id>` sends the same `StopFailure`
|
|
209
|
+
`rate_limit` payload Claude Code would send through Leg's hook: verified end
|
|
210
|
+
to end on a haiku session, the hook set the limit, the runner saved the
|
|
211
|
+
bundle, stopped claude and started codex, which read `.leg/RESUME.md` on its
|
|
212
|
+
first turn. The simulated wall clears after two minutes and is never kept as
|
|
213
|
+
evidence. The first real `StopFailure` was saved that way, with secrets
|
|
214
|
+
scrubbed, at `fixtures/live/claude/limit-rate_limit.json`, and the claude docs
|
|
215
|
+
row flipped to observed-live (`node scripts/live-limits.mjs`). The same
|
|
216
|
+
capture is wired for codex `usage_limit_exceeded` and agy `RESOURCE_EXHAUSTED`;
|
|
217
|
+
no payload for either has been kept yet.
|
|
218
|
+
|
|
219
|
+
Terminals started by this version can change order while they run. An older
|
|
220
|
+
terminal stays on the order it started with; its card says a restart is needed
|
|
221
|
+
and can save the desired default for the next launch. A normal agent exit ends
|
|
222
|
+
the terminal. It does not trigger a handoff.
|
|
223
|
+
|
|
224
|
+
### The working environment travels too (optional)
|
|
225
|
+
|
|
226
|
+
With the [portable harness](docs/harness.md) enabled (`leg harness enable`,
|
|
227
|
+
off by default), the hand-off also prepares the destination's environment
|
|
228
|
+
before it starts: the source agent's global rules, identity, hooks, skills,
|
|
229
|
+
subagents, slash commands, MCP servers and permissions, rendered into the
|
|
230
|
+
destination's own files as far as it can represent them. The terminal then
|
|
231
|
+
reads, for instance, `codex harness partial · 8/8 components, 3 dropped ·
|
|
232
|
+
1 file(s) written`, and the terminal's details list what was dropped and why.
|
|
233
|
+
The source is fingerprinted so an unchanged environment costs a few stat
|
|
234
|
+
calls; a policy (`warn`, `sync`, `strict`) says whether a hand-off may write,
|
|
235
|
+
and strict refuses a destination it cannot make safe. Credentials never move
|
|
236
|
+
and which login runs stays the account layer's decision.
|
|
237
|
+
|
|
238
|
+
## Two sessions in one repo
|
|
239
|
+
|
|
240
|
+
Two agents in one working tree write over each other's files. So when you
|
|
241
|
+
start `leg codex` in a checkout where `leg claude` is already live, the new
|
|
242
|
+
session gets its own git worktree, `<repo>/.leg-worktrees/<session-id>` on
|
|
243
|
+
branch `leg/<session-id>`, cut from the branch the checkout has out, and the
|
|
244
|
+
terminal prints one line saying where it is. The agent starts there; the card,
|
|
245
|
+
the usage tracking and the handoff work the same. `--no-worktree` shares the
|
|
246
|
+
checkout on purpose (Leg takes the flag out; the agent never sees it).
|
|
247
|
+
|
|
248
|
+
The card of a session with its own worktree has a **Land** button. It sends
|
|
249
|
+
the branch through the merge queue: whatever the agent left uncommitted is
|
|
250
|
+
committed on the branch, the branch is rebased onto its base, the repo's test
|
|
251
|
+
command runs (`package.json` `test`, `pytest`, or none with a warning), and the
|
|
252
|
+
base is fast-forwarded, never merged. When a step fails nothing lands and the
|
|
253
|
+
card says why: `rebase-conflict` with the files, `tests-red` with the end of
|
|
254
|
+
the output, `dirty-trunk` when the checkout has local changes the landing
|
|
255
|
+
would overwrite. Local changes it would not touch are left alone. The
|
|
256
|
+
landed-on-trunk list says which terminal landed each commit. **Remove** safely
|
|
257
|
+
prunes a finished session only when its worktree is clean and its branch is
|
|
258
|
+
already on the base. **Remove record** is a separate visible button with a
|
|
259
|
+
confirmation: it removes only Leg's saved session record and deliberately
|
|
260
|
+
keeps the worktree, branch, unmerged commits, and dirty files.
|
|
261
|
+
|
|
262
|
+
Verified live on 2026-09-11 with three haiku sessions in a throwaway repo. The
|
|
263
|
+
first stayed in the checkout; the second and third each got a worktree and
|
|
264
|
+
appended a line to README.md. Land on the second, clicked on the real board,
|
|
265
|
+
ran the repo's tests and fast-forwarded main; Land on the third bounced with
|
|
266
|
+
`rebase-conflict` on README.md and kept its commit on its branch; the
|
|
267
|
+
landed-on-trunk list named the second terminal (the screenshot above).
|
|
268
|
+
|
|
269
|
+
## More than one human
|
|
270
|
+
|
|
271
|
+
Off until you run it. `leg share on` binds the board to your Tailscale
|
|
272
|
+
address (or `--bind lan`, or an address you name) and gives every human their
|
|
273
|
+
own name and token; until then the board stays on `127.0.0.1` and there is no
|
|
274
|
+
token at all.
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
leg share on your own link, printed once
|
|
278
|
+
leg share add sam sam's link, printed once
|
|
279
|
+
leg share who is on the board (never a token again)
|
|
280
|
+
leg share rotate sam sam's old link stops working
|
|
281
|
+
leg share off back to 127.0.0.1; every link stops working
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
A token is kept as a sha256 hash, so a lost link is re-issued, never re-read.
|
|
285
|
+
The board takes the token out of the address bar and keeps it in the browser.
|
|
286
|
+
Your own browser on this machine needs no token.
|
|
287
|
+
|
|
288
|
+
What another human sees is the Terminals region, read-only. Each panel says
|
|
289
|
+
whose terminal it is. On a panel that is not theirs there is no prompt, no file
|
|
290
|
+
name, no path, no bundle and no event log; what stays is the agent and session
|
|
291
|
+
tail, the status word, the sentence `read-only: wes owns this terminal`,
|
|
292
|
+
repo@branch, the worktree line, the elapsed clock, and one button,
|
|
293
|
+
**Request handoff**. The instrument head prints `not shared` in place of every
|
|
294
|
+
percentage. A request lands on the owner's panel as `sam asked to take this
|
|
295
|
+
terminal at 11:04 PM` with **Approve sam** and **Dismiss sam**. The background
|
|
296
|
+
side of the board (cards, logs, the floor) stays the owner's alone. A terminal
|
|
297
|
+
belongs to the human who started it: `LEG_PERSON=sam leg claude` on the
|
|
298
|
+
same machine is sam's card, not yours.
|
|
299
|
+
|
|
300
|
+
The security pass that goes with it: every `/api` route needs a token, the
|
|
301
|
+
event stream included; twenty wrong tokens from one address and that address
|
|
302
|
+
waits a minute; one identity gets 600 requests a minute; a guest gets 403 on
|
|
303
|
+
everything that is not theirs; and the tests send a bad and a missing token to
|
|
304
|
+
every route. There is still no TLS, so keep this on Tailscale or a network you
|
|
305
|
+
trust. Verified live on 2026-09-11: two terminals on one machine, one wes's and
|
|
306
|
+
one sam's; sam's board showed wes's card with the prompt hidden and only
|
|
307
|
+
**Request handoff**, and sam's request reached wes's board (`~/.leg/board.log`:
|
|
308
|
+
"hand-off requested … by sam").
|
|
309
|
+
|
|
310
|
+
## The board
|
|
311
|
+
|
|
312
|
+
`leg <agent>` opens it; `leg open` reopens it; `leg down` stops it.
|
|
313
|
+
|
|
314
|
+
- **Instrument head**: one row per login, sticky at the top of the board and of
|
|
315
|
+
the floor. Each row carries the 5h and 7d rails, the percentage, when that
|
|
316
|
+
window resets and how long that is, a burn-rate sentence under the 5h rail,
|
|
317
|
+
where and when Leg read the number, and one word for the state: `under 60`,
|
|
318
|
+
`over 60`, `over 85`, `stale <n>m`, `at the wall` or `no reading`. A login
|
|
319
|
+
at its wall keeps both rails and gains `at the wall`, `back <day time>` and
|
|
320
|
+
`in <duration>` beside them.
|
|
321
|
+
- **Terminals**: one full-width panel per session, the ones that need an answer
|
|
322
|
+
first. Agent and session tail, the status word, the first prompt as a button,
|
|
323
|
+
exactly one sentence (the highest-ranked thing true about the terminal), an
|
|
324
|
+
`also:` disclosure naming the rest, the files as comma-separated text,
|
|
325
|
+
`repo@branch`, `own worktree, from main` when the session cut its own
|
|
326
|
+
worktree, and an elapsed clock. Two live sessions in one repo touching the
|
|
327
|
+
same file print `codex (codex-99ab) is changing src/server.mjs in another
|
|
328
|
+
checkout; whoever lands second rebases` on both panels, and a panel that needs
|
|
329
|
+
you rises one step and says `waiting on you` in place of its status word.
|
|
330
|
+
After a Land the sentence is `landed on <base>, <7-char sha>, <n> files,
|
|
331
|
+
+<added>/-<removed>`, or `Land was attempted at <time> onto <base> and
|
|
332
|
+
bounced: <first line of the reason>. The branch still holds every commit;
|
|
333
|
+
nothing was lost.`
|
|
334
|
+
- **Landed on main**: one flat list across every repo the board can see, newest
|
|
335
|
+
first, each row with the short sha, the subject, a `repo@branch` chip, and
|
|
336
|
+
when plus who. A commit a Land put there says `landed by <agent> (<id tail>)`.
|
|
337
|
+
- **Buttons**, in a fixed order that never reflows: Land, Hand off now,
|
|
338
|
+
Details, End. Once a session has ended, Remove and Remove record take End's
|
|
339
|
+
place. Details opens an expansion in flow under the panel.
|
|
340
|
+
- Below it, **Conversations**: every coding-agent conversation on this machine
|
|
341
|
+
as a count that opens, the ones Leg started and the ones Claude Code, Codex,
|
|
342
|
+
Grok, Antigravity and Copilot keep in their own stores. Filter by agent,
|
|
343
|
+
search, repository, or only what Leg started; a row opens its last messages
|
|
344
|
+
in place, with the `leg history continue` command to copy where the agent
|
|
345
|
+
can resume by id. The same drawer lists every checkout Leg can see. Then
|
|
346
|
+
optional **Background tasks** an agent runs in a separate worktree without
|
|
347
|
+
joining the terminal conversation (see below), then **Settings**.
|
|
348
|
+
|
|
349
|
+
The board reads `~/.leg/sessions/*/session.json` over server-sent events; a
|
|
350
|
+
session whose runner process is gone is marked `lost`, never shown as live.
|
|
351
|
+
|
|
352
|
+
## Every conversation on this machine
|
|
353
|
+
|
|
354
|
+
Claude Code, Codex, Grok, Antigravity and Copilot each keep their history in
|
|
355
|
+
their own place. `leg history` is one list over all of them, whether or not
|
|
356
|
+
Leg started the conversation, and `leg worktrees` is one list over every
|
|
357
|
+
checkout: git's, Leg's own, and the ones those conversations ran in.
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
leg history newest first, every agent; --provider, --repo, --search, --json
|
|
361
|
+
leg history show claude:0fc5 where it ran, its last messages, whether Leg can continue it
|
|
362
|
+
leg history continue claude:0fc5 leg claude --resume <id> in that folder, supervised like any session
|
|
363
|
+
leg worktrees path, repo, branch, exists, uncommitted, owner, conversations, stale
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Nothing moves: each agent's store stays where it was, Leg writes only its own
|
|
367
|
+
`~/.leg/history/index.json`, reads transcripts from their head and tail, and
|
|
368
|
+
opens messages only when you open a conversation. A session Leg started and
|
|
369
|
+
the same conversation in the agent's store are one row, marked `leg`; the rest
|
|
370
|
+
are `external`. On a shared board the whole group is the owner's. The support
|
|
371
|
+
matrix (which agents list, show messages, continue) and every file read are in
|
|
372
|
+
[docs/history.md](docs/history.md).
|
|
373
|
+
|
|
374
|
+
## Second accounts, and what the terms say
|
|
375
|
+
|
|
376
|
+
Optional. `leg accounts add claude work` creates
|
|
377
|
+
`~/.leg/accounts/claude/work`, junctions your `hooks`, `skills`, `agents`,
|
|
378
|
+
`commands`, `plugins`, `rules`, `scripts`, `output-styles` and `tools` into it,
|
|
379
|
+
copies `settings.json`, `CLAUDE.md` and the status-line scripts (refreshed from
|
|
380
|
+
your real `~/.claude` before every launch), and prints one line to paste:
|
|
381
|
+
|
|
382
|
+
```
|
|
383
|
+
$env:CLAUDE_CONFIG_DIR='C:\Users\you\.leg\accounts\claude\work'; claude auth login
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Same for codex (`CODEX_HOME`; `config.toml`, `AGENTS.md`, `skills`, `prompts`,
|
|
387
|
+
`rules`, `plugins`, `agents`, `hooks`, `memories` shared). agy 1.2.0 has no
|
|
388
|
+
config-directory override, so it stays one account. Only the login lives in
|
|
389
|
+
the account directory; `leg accounts rm` removes the junctions and the
|
|
390
|
+
directory and never touches your real home.
|
|
391
|
+
|
|
392
|
+
The terms, as published (effective dates below):
|
|
393
|
+
|
|
394
|
+
- Anthropic Consumer Terms (effective 2025-10-08): "You may not share your
|
|
395
|
+
Account login information, Anthropic API key, or Account credentials with
|
|
396
|
+
anyone else" and you "must not … bypass any of our systems or protective
|
|
397
|
+
measures."
|
|
398
|
+
- Anthropic Usage Policy (effective 2025-09-15): do not "Coordinate malicious
|
|
399
|
+
activity across multiple accounts to avoid detection or circumvent product
|
|
400
|
+
guardrails" or "Utilize automation in account creation."
|
|
401
|
+
- OpenAI Terms of Use (effective 2026-01-01): "You may not share your account
|
|
402
|
+
credentials or make your account available to anyone else" and you may not
|
|
403
|
+
"circumvent any rate limits or restrictions or bypass any protective
|
|
404
|
+
measures."
|
|
405
|
+
|
|
406
|
+
Owning two paid subscriptions is not named as prohibited by either. Rotating to
|
|
407
|
+
a second account of the same vendor because the first one is rate-limited sits
|
|
408
|
+
close to OpenAI's "circumvent any rate limits" wording and Anthropic's
|
|
409
|
+
"circumvent product guardrails". Leg's default chain switches vendors
|
|
410
|
+
(claude → codex → agy), which is plainly fine. Same-vendor rotation only
|
|
411
|
+
happens after you run `leg accounts add`; that is your call.
|
|
412
|
+
|
|
413
|
+
## What is and is not touched
|
|
414
|
+
|
|
415
|
+
- **Never edited**: `~/.claude/settings.json`, `~/.claude.json`,
|
|
416
|
+
`~/.codex/config.toml`, agy's files, your repo's settings. Claude Code gets
|
|
417
|
+
hooks through a per-session `--settings` file under `~/.leg`; codex and
|
|
418
|
+
agy get nothing injected.
|
|
419
|
+
- **Read, never written**: each agent's own history (`~/.claude/projects`,
|
|
420
|
+
`~/.codex/sessions`, `~/.grok/sessions`, `~/.gemini/antigravity-cli`,
|
|
421
|
+
`~/.copilot/session-state`) for `leg history`; the index it builds lives
|
|
422
|
+
under `~/.leg/history/`, and no SQLite file is ever opened.
|
|
423
|
+
- **Written only after `leg harness enable`** ([the portable harness](docs/harness.md),
|
|
424
|
+
off by default): the destination client's global rules file
|
|
425
|
+
(`~/.codex/AGENTS.md`, `~/.gemini/GEMINI.md`, `~/.claude/leg-rules.md` plus
|
|
426
|
+
one `@` line in `CLAUDE.md`), marked `leg harness` regions inside the
|
|
427
|
+
client's own config files, per-skill directory links, and one file per
|
|
428
|
+
subagent and slash command. Every file carries `GENERATED by Leg harness`,
|
|
429
|
+
every overwrite is backed up under `~/.leg/harness/backups`, a hand-edited
|
|
430
|
+
file is skipped and named, the source client is never written, and no
|
|
431
|
+
credential ever moves (an MCP key becomes `${NAME}`). `leg harness disable`
|
|
432
|
+
stops it and removes nothing.
|
|
433
|
+
- **Written in your repo**: `.leg/` (session notes, `RESUME.md` and one
|
|
434
|
+
`RESUME-<session-id>.md` per hand-off),
|
|
435
|
+
`.context-handoffs/` (the bundles) and `.leg-worktrees/` (a second
|
|
436
|
+
session's worktree), all added to `.git/info/exclude`, plus the
|
|
437
|
+
`leg/<session-id>` branch of a session with its own worktree. Landing
|
|
438
|
+
fast-forwards your branch; nothing is ever pushed.
|
|
439
|
+
- **Stripped from every agent's environment**: `ANTHROPIC_API_KEY`,
|
|
440
|
+
`ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_CUSTOM_HEADERS`,
|
|
441
|
+
`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_API_BASE`, `GEMINI_API_KEY`,
|
|
442
|
+
`GOOGLE_API_KEY`, `GOOGLE_GEMINI_BASE_URL`, `GOOGLE_GENAI_USE_VERTEXAI`,
|
|
443
|
+
`GOOGLE_GENAI_USE_ENTERPRISE`, `GOOGLE_CLOUD_PROJECT`,
|
|
444
|
+
`GOOGLE_CLOUD_LOCATION`, `GOOGLE_APPLICATION_CREDENTIALS`, `CLAUDECODE`,
|
|
445
|
+
`CLAUDE_CODE_*`, `CLAUDE_EFFORT`, and `CLAUDE_PLUGIN_DATA`. `CLAUDE_CODE_*`
|
|
446
|
+
does not include `CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS`: Leg sets that one
|
|
447
|
+
to `0` for a detached Claude print session.
|
|
448
|
+
- **Read but never written or printed**: Claude Code's stored login, sent only
|
|
449
|
+
to `api.anthropic.com` for the usage numbers. The ledger scrubs bearer tokens
|
|
450
|
+
and key shapes from every line regardless.
|
|
451
|
+
- **`leg uninstall --yes`**: removes `~/.leg` (sessions, usage, extra
|
|
452
|
+
account directories with their junctions, v0.1 cards, the board pidfile) and
|
|
453
|
+
nothing else; then `npm rm -g @ucsandman/legcli`.
|
|
454
|
+
|
|
455
|
+
## CLI reference
|
|
456
|
+
|
|
457
|
+
```
|
|
458
|
+
leg claude|codex|agy|grok [agent args…] the interactive agent, board alongside, handoff on limit
|
|
459
|
+
[--no-worktree] share the checkout with a live session instead of a worktree
|
|
460
|
+
leg sessions ls [--json] every session and its usage
|
|
461
|
+
leg sessions show|events <id>
|
|
462
|
+
leg sessions handoff|end <id> same as the board buttons
|
|
463
|
+
leg sessions rm <id> forget an ended session
|
|
464
|
+
leg sessions simulate-limit <id> the real limit path without a real wall (claude, agy, grok)
|
|
465
|
+
leg history [ls] [--provider p] [--repo r] [--search q] [--managed|--external] [--live] [--all] [--json]
|
|
466
|
+
every conversation on this machine, Leg's own and the agents' own (read only)
|
|
467
|
+
leg history show <id> [--messages n] [--json] | continue <id> [agent args…] | refresh [--full] | providers
|
|
468
|
+
leg worktrees [--repo <path>] [--no-dirty] [--json] every checkout: git's, Leg's, the conversations' (read only)
|
|
469
|
+
leg accounts ls logins and their 5h/7d usage
|
|
470
|
+
leg accounts add <claude|codex|grok> <name> | rm <agent> <name> | terms
|
|
471
|
+
leg harness status|inspect|check|explain|history [--json] the portable harness, read-only
|
|
472
|
+
leg harness enable [--source claude|codex] [--policy warn|sync|strict] [--yes]
|
|
473
|
+
leg harness sync [--to codex,agy] [--force] [--dry-run] | diff <client> | doctor
|
|
474
|
+
leg harness capture [claude|codex] | source <client> | policy <mode> | disable
|
|
475
|
+
leg license the license on this machine, or where to buy one
|
|
476
|
+
leg license activate <key> | deactivate | refresh (refresh renews a Team key)
|
|
477
|
+
leg share who is on the board (off by default; Team plan)
|
|
478
|
+
leg share on [--bind tailscale|lan|<addr>] [--port N] | off
|
|
479
|
+
leg share add|rotate|rm <name> one link per human, printed once
|
|
480
|
+
leg open | down | status the board
|
|
481
|
+
leg uninstall [--yes]
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Environment, all optional: `LEG_HOME` (default `~/.leg`), `LEG_PORT`
|
|
485
|
+
(4747), `LEG_ACCOUNT` (start on a named login), `LEG_WARN_PCT` (85),
|
|
486
|
+
`LEG_NO_HANDOFF=1` (warn and record, never switch), `LEG_NO_OPEN=1` (do not
|
|
487
|
+
open the browser), `LEG_USAGE_POLL_MS` (60000), `LEG_CLAUDE_ARGS` /
|
|
488
|
+
`LEG_CODEX_ARGS` / `LEG_AGY_ARGS` / `LEG_GROK_ARGS` (extra args for a leg Leg starts after
|
|
489
|
+
a hand-off, e.g. `-m gpt-5.3-codex-spark`), `LEG_CLAUDE_BIN`,
|
|
490
|
+
`LEG_CODEX_BIN`, `LEG_AGY_BIN`, `LEG_GROK_BIN`, `LEG_CHB_BIN`, `LEG_PERSON` (whose
|
|
491
|
+
terminal this is when the board is shared), `LEG_RATE_MAX` (600 requests a
|
|
492
|
+
minute per human) and `LEG_RATE_MAX_FAILURES` (20 wrong tokens per address).
|
|
493
|
+
|
|
494
|
+
## Background tasks: the v0.1 extras
|
|
495
|
+
|
|
496
|
+
Version 0.1 was the other way round: you dropped a task card on the board and
|
|
497
|
+
Leg ran the agents headless in a git worktree, one per card, with a fallback
|
|
498
|
+
chain, path leases, a scheduler and a merge queue. All of that still works and
|
|
499
|
+
lives below the terminals lane, but it is no longer the way in.
|
|
500
|
+
|
|
501
|
+
The New background card form starts with a repo, task, and real first agent.
|
|
502
|
+
**Run now** queues it; turning that off saves a draft in Backlog. The default
|
|
503
|
+
**Build only** workflow stops with its changes in the card's worktree and does
|
|
504
|
+
not merge them. The Advanced **Build, test, and merge** and **Factory**
|
|
505
|
+
workflows include an automatic land station; their labels say so before you
|
|
506
|
+
choose them. Fallback agents, permissions, approval gates, turn caps, leases,
|
|
507
|
+
trunk, merge method, tests, title, and scripted test/demo adapters are also
|
|
508
|
+
under Advanced options.
|
|
509
|
+
|
|
510
|
+
- `leg up` boots the board with the scheduler and merge queue and streams
|
|
511
|
+
redacted logs; `leg card add --repo <path> --task "<t>" --chain claude,codex --queue`
|
|
512
|
+
creates a card; presets `build`, `build-land`, `factory`; station kinds
|
|
513
|
+
agent, test, land, human.
|
|
514
|
+
- Adapters spawn the CLIs headless as argv, never through a shell, with their
|
|
515
|
+
own permission modes and never a bypass flag: `claude -p --output-format json
|
|
516
|
+
--permission-mode <m>`, `codex exec --json -s <m> -C <worktree>`,
|
|
517
|
+
`agy -p --output-format json --mode <m> --add-dir <worktree>`; `fake`,
|
|
518
|
+
`fake-claude`, `fake-codex`, `fake-agy` for tests and demos.
|
|
519
|
+
- A leg that ends on a limit signal, a stall, a crash or exit 0 without
|
|
520
|
+
`.leg/DONE` hands off with a bundle to the next adapter in the same
|
|
521
|
+
worktree; a `land` station rebases, tests and fast-forwards trunk or bounces
|
|
522
|
+
the card with the failure in the bundle.
|
|
523
|
+
- Optional mirrors, off unless set in `.env`: OpenClaw Workboard
|
|
524
|
+
(`LEG_SYNC_WORKBOARD=1`) and DashClaw (`LEG_SYNC_DASHCLAW=1`).
|
|
525
|
+
|
|
526
|
+
The full v0.1 story, with the fake-limit demo and the real claude→codex run,
|
|
527
|
+
is in [docs/concepts.md](docs/concepts.md), [docs/DEMO.md](docs/DEMO.md),
|
|
528
|
+
[docs/real-run.md](docs/real-run.md) and [docs/board-guide.md](docs/board-guide.md).
|
|
529
|
+
|
|
530
|
+
### Network exposure
|
|
531
|
+
|
|
532
|
+
Leg binds `127.0.0.1`. `leg share on` is the supported way to listen
|
|
533
|
+
anywhere else: it binds your Tailscale or LAN address and every human gets
|
|
534
|
+
their own token (see [More than one human](#more-than-one-human)). Without
|
|
535
|
+
share, setting `LEG_BIND` to a non-loopback address needs `LEG_TOKEN` too,
|
|
536
|
+
or the server refuses to start (exit 3), and requests then need
|
|
537
|
+
`Authorization: Bearer <token>`. Tokenless owner access also requires a
|
|
538
|
+
loopback hostname (`127.0.0.1`, `localhost`, or `[::1]`), which prevents a
|
|
539
|
+
DNS-rebound hostname from inheriting local access. Either way there is no TLS.
|
|
540
|
+
|
|
541
|
+
## Troubleshooting
|
|
542
|
+
|
|
543
|
+
- **The board did not open**: `leg open`, or visit http://127.0.0.1:4747.
|
|
544
|
+
`~/.leg/board.log` has the server's output.
|
|
545
|
+
- **claude's card shows "usage unknown"**: Claude Code has no stored claude.ai
|
|
546
|
+
login in that config directory (run `claude auth login`), the stored token
|
|
547
|
+
expired (start `claude` once, it refreshes), or the usage endpoint answered
|
|
548
|
+
with something Leg does not recognise. The card says which. The wall is
|
|
549
|
+
still caught through the hook; only the percentages are missing.
|
|
550
|
+
- **codex usage is unavailable or stale**: the read-only Codex app-server quota
|
|
551
|
+
request failed or has not completed in the last five minutes. The board
|
|
552
|
+
retries every minute; an active Codex session also keeps its rollout tap as a
|
|
553
|
+
fallback for percentages and the wall signal.
|
|
554
|
+
- **agy's card has no percentage**: expected, agy exposes none. Leg sees the
|
|
555
|
+
wall when agy hits it.
|
|
556
|
+
- **A session shows `lost`**: the terminal that ran `leg <agent>` is gone
|
|
557
|
+
(closed, crashed, machine slept through a kill). Remove it from the board.
|
|
558
|
+
- **Nested session**: `leg claude` typed inside a Claude Code shell works;
|
|
559
|
+
the parent's `CLAUDECODE` markers are stripped so the child starts.
|
|
560
|
+
- **`npm install` dies with `edgesOut`** (clone only): the global npm is older
|
|
561
|
+
than Node; run `npx --yes npm@latest install` once.
|
|
562
|
+
|
|
563
|
+
More in [docs/faq.md](docs/faq.md).
|
|
564
|
+
|
|
565
|
+
## Documentation
|
|
566
|
+
|
|
567
|
+
| guide | read it when |
|
|
568
|
+
|-------|--------------|
|
|
569
|
+
| [Getting started](docs/getting-started.md) | you want `leg claude` running in five minutes |
|
|
570
|
+
| [Concepts](docs/concepts.md) | sessions, accounts, bundles, and the v0.1 cards, stations, chains and leases |
|
|
571
|
+
| [Board guide](docs/board-guide.md) | every word, number and button on the board explained |
|
|
572
|
+
| [Configuration](docs/configuration.md) | environment variables and options |
|
|
573
|
+
| [Portable harness](docs/harness.md) | carrying rules, hooks, skills, agents, commands and MCP servers to the agent a hand-off lands on: what moves, what does not, policies, ownership, secrets |
|
|
574
|
+
| [History](docs/history.md) | `leg history` and `leg worktrees`: every conversation and checkout on this machine across agents, the support matrix, what is read and what is written |
|
|
575
|
+
| [Adapters](docs/adapters.md) | what each CLI exposes and how Leg attaches to it |
|
|
576
|
+
| [CLI contracts](docs/cli-contracts.md) | exact argv per CLI and the limit-signal table with sources |
|
|
577
|
+
| [FAQ](docs/faq.md) | a question the others did not answer |
|
|
578
|
+
| [Demo](docs/DEMO.md) and [real run](docs/real-run.md) | the v0.1 handoff, fake and real |
|
|
579
|
+
| [Vocabulary](docs/VOCABULARY.md) | statuses, outcomes and event types |
|
|
580
|
+
| [Roadmap v2](docs/ROADMAP-v2.md) | where this is going |
|
|
581
|
+
| [Reuse](docs/REUSE.md) and [deviations](docs/DEVIATIONS.md) | what was ported and every place the plan changed |
|
|
582
|
+
| [Website](site/) | the public page: static HTML in `site/`, preview with `python -m http.server 4780 --directory site`, deployed to Vercel from that directory; PRODUCT.md and DESIGN.md at the root carry its brief and tokens |
|
|
583
|
+
|
|
584
|
+
## Contributing
|
|
585
|
+
|
|
586
|
+
Issues and pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
587
|
+
for the dev setup and the rules (zero runtime deps, argv spawns only, no
|
|
588
|
+
bypass flags, a privacy check on every commit). Security reports go through
|
|
589
|
+
[SECURITY.md](SECURITY.md).
|
|
590
|
+
|
|
591
|
+
```
|
|
592
|
+
npm install
|
|
593
|
+
npm test # node --test + privacy check
|
|
594
|
+
npm run lint
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Any real agent session started only to test Leg runs on the cheapest model
|
|
598
|
+
(`leg claude --model haiku`); the live checks in `test/` never start one.
|
|
599
|
+
|
|
600
|
+
Maintainer releases use npm trusted publishing with no `NPM_TOKEN`. Bump the
|
|
601
|
+
package, lockfile, site metadata and release notes, then push `main`.
|
|
602
|
+
`npm version` (and `npm run sync-alias`) writes `packages/leg-agents` to the
|
|
603
|
+
same version and pins `@ucsandman/legcli` to it; `npm test` fails if they
|
|
604
|
+
drift. CI waits for the Ubuntu and Windows test matrix, validates both
|
|
605
|
+
`@ucsandman/legcli` and `leg-agents` against npm, and publishes each only
|
|
606
|
+
when that version is missing and newer than the stable `latest`. Existing
|
|
607
|
+
versions skip cleanly; older, prerelease, lockstep, and registry-error cases
|
|
608
|
+
fail the job. The npm trusted publisher is bound to `ucsandman/legcli` and
|
|
609
|
+
`.github/workflows/ci.yml` (bind `leg-agents` the same way). The repository
|
|
610
|
+
stays private, so publication uses `--provenance=false`.
|
|
611
|
+
|
|
612
|
+
## Privacy and attribution
|
|
613
|
+
|
|
614
|
+
Parts of the runner, ledger and git snapshot were ported from a private
|
|
615
|
+
repository that was MIT licensed (see [NOTICE](NOTICE) and
|
|
616
|
+
[docs/REUSE.md](docs/REUSE.md)), with chat identifiers, machine paths and
|
|
617
|
+
personal names removed. The test suite runs a privacy check on every commit,
|
|
618
|
+
and the fixtures store home paths as `~`.
|
|
619
|
+
|
|
620
|
+
## License and pricing
|
|
621
|
+
|
|
622
|
+
Leg is commercial software under the [Leg License Agreement](LICENSE).
|
|
623
|
+
It ships as readable JavaScript so you can see what it does on your machine,
|
|
624
|
+
and you may modify it for your own use, but not redistribute it or work
|
|
625
|
+
around the license check. Versions 0.2.0 and 0.3.0 were published under MIT
|
|
626
|
+
and remain available. The version in this source tree is 0.8.0; see
|
|
627
|
+
[npm](https://www.npmjs.com/package/legcli) for published versions and
|
|
628
|
+
[CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
629
|
+
|
|
630
|
+
Using it needs a license: **Personal, $79 once**, one human on any number of
|
|
631
|
+
machines, every release for 12 months and the version you have keeps working
|
|
632
|
+
after that; **Team, $12 per seat per month**, Personal plus `leg share` for
|
|
633
|
+
more than one human on the board. Buy at the site, then
|
|
634
|
+
`leg license activate <key>`. There is no trial; there is a **30-day
|
|
635
|
+
money-back guarantee**, so the way to evaluate Leg is to use it on real work
|
|
636
|
+
and ask for a refund if it does not earn its place. A key is a signed token
|
|
637
|
+
checked offline with the public key in `src/license.mjs`; only a Team key
|
|
638
|
+
renewal talks to the site. The bare agent CLIs are never affected by any of
|
|
639
|
+
this; only what Leg adds is licensed.
|