@ucsandman/legcli 0.7.0 → 0.9.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 (116) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/NOTICE +8 -0
  3. package/README.md +601 -558
  4. package/bin/fake-agent.mjs +4 -4
  5. package/bin/leg.mjs +64 -34
  6. package/docs/DECISIONS.md +20 -2
  7. package/docs/ERRORS.md +71 -0
  8. package/docs/README.md +2 -0
  9. package/docs/REUSE.md +1 -1
  10. package/docs/VOCABULARY.md +21 -0
  11. package/docs/adapters.md +17 -3
  12. package/docs/board-guide.md +13 -0
  13. package/docs/cli-contracts.md +57 -3
  14. package/docs/concepts.md +42 -3
  15. package/docs/configuration.md +42 -2
  16. package/docs/faq.md +19 -0
  17. package/docs/getting-started.md +272 -251
  18. package/docs/harness.md +319 -0
  19. package/fixtures/limits/grok/grok-rate-limit.json +11 -0
  20. package/fixtures/live/agy/limit-agy-resource-exhausted.json +11 -0
  21. package/fixtures/verified.json +1 -1
  22. package/package.json +8 -4
  23. package/scripts/build-docs-site.mjs +15 -7
  24. package/scripts/check-branding.mjs +118 -0
  25. package/scripts/check-claims.mjs +1 -1
  26. package/scripts/license-sign.mjs +1 -1
  27. package/scripts/limits-table.mjs +1 -1
  28. package/scripts/live-limits.mjs +1 -1
  29. package/scripts/npm-publish-gate.mjs +114 -0
  30. package/scripts/probe.mjs +4 -3
  31. package/scripts/seed-fake-cards.mjs +4 -3
  32. package/scripts/seed-floor-board.mjs +5 -4
  33. package/scripts/seed-wes-board.mjs +5 -4
  34. package/scripts/stripe-setup.mjs +1 -1
  35. package/scripts/sync-harness-engine.mjs +159 -0
  36. package/scripts/sync-leg-agents.mjs +127 -0
  37. package/src/accounts.mjs +10 -2
  38. package/src/adapters/codex.mjs +1 -1
  39. package/src/adapters/grok.mjs +4 -7
  40. package/src/attach.mjs +162 -37
  41. package/src/auth.mjs +2 -2
  42. package/src/board/board.css +45 -17
  43. package/src/board/board.js +4 -4
  44. package/src/board/floor.js +2 -2
  45. package/src/board/sessions.js +181 -38
  46. package/src/bundle.mjs +54 -8
  47. package/src/chain.mjs +1 -1
  48. package/src/contract.mjs +4 -3
  49. package/src/fsx.mjs +5 -2
  50. package/src/handoff.mjs +6 -6
  51. package/src/harness/cli.mjs +281 -0
  52. package/src/harness/fingerprint.mjs +68 -0
  53. package/src/harness/index.mjs +407 -0
  54. package/src/harness/registry.mjs +124 -0
  55. package/src/harness/vendor/agnostic-ai/LICENSE +21 -0
  56. package/src/harness/vendor/agnostic-ai/UPSTREAM.json +30 -0
  57. package/src/harness/vendor/agnostic-ai/core/safety/guards.json +96 -0
  58. package/src/harness/vendor/agnostic-ai/core/templates/targets.json +252 -0
  59. package/src/harness/vendor/agnostic-ai/engine/harness/README.md +199 -0
  60. package/src/harness/vendor/agnostic-ai/engine/harness/apply.cjs +247 -0
  61. package/src/harness/vendor/agnostic-ai/engine/harness/bundle.cjs +243 -0
  62. package/src/harness/vendor/agnostic-ai/engine/harness/capture.cjs +119 -0
  63. package/src/harness/vendor/agnostic-ai/engine/harness/common.cjs +375 -0
  64. package/src/harness/vendor/agnostic-ai/engine/harness/index.cjs +55 -0
  65. package/src/harness/vendor/agnostic-ai/engine/harness/sources/claude.cjs +330 -0
  66. package/src/harness/vendor/agnostic-ai/engine/harness/sources/codex.cjs +314 -0
  67. package/src/harness/vendor/agnostic-ai/engine/harness/status.cjs +171 -0
  68. package/src/harness/vendor/agnostic-ai/engine/harness/targets/agy.cjs +113 -0
  69. package/src/harness/vendor/agnostic-ai/engine/harness/targets/claude.cjs +158 -0
  70. package/src/harness/vendor/agnostic-ai/engine/harness/targets/codex.cjs +832 -0
  71. package/src/harness/vendor/agnostic-ai/engine/harness/targets/cursor.cjs +87 -0
  72. package/src/harness/vendor/agnostic-ai/engine/harness/targets/gemini.cjs +128 -0
  73. package/src/harness/vendor/agnostic-ai/engine/harness/targets/generic.cjs +424 -0
  74. package/src/harness/vendor/agnostic-ai/engine/harness/toml.cjs +149 -0
  75. package/src/harness/vendor/agnostic-ai/engine/hooks/shim.cjs +431 -0
  76. package/src/hook.mjs +49 -49
  77. package/src/land.mjs +660 -47
  78. package/src/launcher.mjs +40 -27
  79. package/src/ledger.mjs +6 -6
  80. package/src/license.mjs +10 -9
  81. package/src/live-capture.mjs +1 -1
  82. package/src/mergequeue.mjs +6 -6
  83. package/src/orchestrator.mjs +28 -4
  84. package/src/preferences.mjs +63 -9
  85. package/src/redact.mjs +1 -1
  86. package/src/resume.mjs +17 -15
  87. package/src/runner.mjs +3 -3
  88. package/src/scheduler.mjs +1 -1
  89. package/src/server.mjs +69 -20
  90. package/src/session-detail.mjs +15 -1
  91. package/src/sessions.mjs +9 -5
  92. package/src/share.mjs +2 -2
  93. package/src/stations/agent.mjs +1 -1
  94. package/src/sync/dashclaw.mjs +4 -4
  95. package/src/synthesis.mjs +165 -0
  96. package/src/taps/agy.mjs +2 -2
  97. package/src/taps/claude-usage.mjs +1 -1
  98. package/src/taps/claude.mjs +170 -170
  99. package/src/taps/codex.mjs +286 -286
  100. package/src/taps/grok.mjs +251 -0
  101. package/src/trust.mjs +205 -36
  102. package/src/usage.mjs +5 -1
  103. package/src/worktree.mjs +5 -4
  104. package/fixtures/live/agy/attempt-1-scratch-workspace.out.log +0 -1
  105. package/fixtures/live/agy/err.log +0 -0
  106. package/fixtures/live/agy/out.log +0 -1
  107. package/fixtures/live/agy/supervisor.log +0 -2
  108. package/fixtures/live/claude/err.log +0 -0
  109. package/fixtures/live/claude/out.log +0 -1
  110. package/fixtures/live/claude/supervisor.log +0 -2
  111. package/fixtures/live/codex/err.log +0 -1
  112. package/fixtures/live/codex/out.log +0 -8
  113. package/fixtures/live/codex/supervisor.log +0 -2
  114. package/fixtures/live/grok/err.log +0 -32
  115. package/fixtures/live/grok/out.log +0 -7
  116. package/fixtures/live/grok/supervisor.log +0 -2
package/docs/concepts.md CHANGED
@@ -108,12 +108,14 @@ things in order (`src/attach.mjs`, `src/bundle.mjs`):
108
108
  it again at the transition and during all-out waiting. Machine Settings is
109
109
  copied only when a new terminal starts.
110
110
  3. **Switch.** The agent process is stopped and the terminal restored. The
111
- bundle's `context-handoff-bundle load <id>` output is written to
111
+ bundle's `context-handoff-bundle load <id>` output (with the `## Synthesis`
112
+ section prepended if `.leg/SYNTHESIS-<session-id>.md` is present) is written to
112
113
  `.leg/RESUME-<session-id>.md` and copied to `.leg/RESUME.md`, and the next
113
114
  agent starts in the same terminal with a short pointer prompt as its first
114
115
  positional argument: `claude "<prompt>"`, `codex "<prompt>"`,
115
- `agy -i "<prompt>"`. The prompt names the per-session file, and says to check
116
- `git status` and `git diff`, continue, and not ask the human to restate the
116
+ `agy -i "<prompt>"`. The prompt names the per-session file, directs the agent
117
+ to read Synthesis first and treat ruled-out approaches as settled, and says to
118
+ check `git status` and `git diff`, continue, and not ask the human to restate the
117
119
  task.
118
120
  4. **All out.** If every option is walled, Leg prints each one with its reset
119
121
  time, soonest first, then waits in the terminal with a one-line countdown
@@ -123,6 +125,33 @@ things in order (`src/attach.mjs`, `src/bundle.mjs`):
123
125
  (`{ agent, account, resets_at, since }`) and shows status `waiting`. Ctrl-C
124
126
  in the terminal, or End on the card, quits with exit 3.
125
127
 
128
+ ## The portable harness
129
+
130
+ Off by default. `leg harness enable` adds a step to the hand-off, between
131
+ **Choose** and **Switch**: the destination's working environment is prepared
132
+ before the destination starts. The source client (the one you actually
133
+ configure: Claude Code or Codex) is captured into a client-neutral bundle
134
+ under `~/.leg/harness/bundle/` (rules with their imports inlined, identity,
135
+ hooks, skills, subagents, slash commands, MCP servers with every credential
136
+ replaced by `${NAME}`, permissions), and that bundle is rendered into the
137
+ destination client's own files: `AGENTS.md`, `config.toml` regions and hook
138
+ trust entries for Codex; `GEMINI.md`, `hooks.json` and `mcp_config.json` for
139
+ agy; `leg-rules.md` plus one import line, hook groups and MCP servers for
140
+ Claude Code. Skills are linked, not copied. What a destination cannot
141
+ represent is dropped with a reason, and the session records exactly what
142
+ transferred (`session.harness`, the `harness` event, the **Harness** section
143
+ of the drawer).
144
+
145
+ The saved policy decides what an unattended hand-off may do: `warn` reports
146
+ and writes nothing, `sync` writes managed state when that is safe, `strict`
147
+ refuses a destination it cannot make safe and tries the next option. A
148
+ fingerprint of the source's files makes an unchanged environment free to
149
+ check. Everything Leg writes this way carries `GENERATED by Leg harness`, sits
150
+ in a marked region or an owned key inside files you also own, is backed up
151
+ before it is overwritten, and is skipped when you hand-edited it. The source
152
+ client is never written; credentials never move; which login runs is still the
153
+ account layer's decision. The whole contract is in [harness.md](harness.md).
154
+
126
155
  ## The resume pointer
127
156
 
128
157
  `.leg/RESUME.md` is the file humans and other agents open by habit, so Leg
@@ -303,6 +332,16 @@ notes carry six sections in the bundle's own vocabulary:
303
332
  The next leg's prompt starts with the bundle's `load` output (the resume
304
333
  text) followed by the same contract.
305
334
 
335
+ ### The synthesis layer
336
+
337
+ Alongside raw state, Leg supports an agent-maintained judgment record in `.leg/SYNTHESIS-<session-id>.md`. At handoff time, Leg reads this file and inlines it verbatim into `.leg/RESUME-<session-id>.md` and `.leg/RESUME.md` as a `## Synthesis` section before the raw bundle dump.
338
+
339
+ - **Schema v1**: A 2-line header (`synthesis_version: 1`, `session: <id> updated: <ISO-8601 UTC>`) followed by up to four optional sections in fixed order: `## Ruled out`, `## Decisions`, `## Next steps`, `## Open questions` (max 5 bullets each, one line per bullet).
340
+ - **Size cap**: 4 KB. Beyond that, Leg includes the first 4 KB plus a trailing `[synthesis truncated]` marker.
341
+ - **Resilience**: If the header is malformed, Leg still inlines the body prefixed with `[synthesis header invalid, rendering body as-is]`. If the file is absent or empty, no `## Synthesis` section is emitted and handoff degrades to today's raw dump.
342
+ - **Pointer prompt**: Directs the taking-over agent to read the Synthesis section first if present, treat ruled-out approaches as settled, and start from the top-ranked next step.
343
+ - **Board indicator**: Shows a `synthesis` chip on the terminal card when the synthesis file exists and was modified within the last 3 checkpoints.
344
+
306
345
  ## Worktrees
307
346
 
308
347
  Every card runs in its own git worktree: `<repo>/.leg-worktrees/<card-id>`
@@ -20,13 +20,16 @@ variables yourself in that case.
20
20
 
21
21
  ## Interactive sessions
22
22
 
23
- These apply to `leg claude|codex|agy`.
23
+ These apply to `leg claude|codex|agy|grok`.
24
24
 
25
25
  | variable | default | meaning | read in |
26
26
  |----------|---------|---------|---------|
27
27
  | `LEG_ACCOUNT` | `default` | start the session on a named login instead of the CLI's own home | `src/attach.mjs` |
28
+ | `LEG_AUTO_APPROVE` | `1` | launch interactive agents in auto-approve mode (set to `0`, `false`, or `off` to disable) | `src/preferences.mjs` |
29
+ | `LEG_NO_AUTO_APPROVE` | (unset) | set to `1` to opt out of auto-approve mode | `src/preferences.mjs` |
28
30
  | `LEG_WARN_PCT` | `85` | the percentage of either usage window that turns the card amber, records a `warning` event and rings the terminal bell once | `src/usage.mjs` |
29
31
  | `LEG_NO_HANDOFF` | (unset, hand-off on) | set to `1` to warn and record but never switch agents | `src/attach.mjs` |
32
+ | `LEG_HARNESS_HOME` | the OS home | where the portable harness reads and writes client configuration (`~/.claude`, `~/.codex`, `~/.gemini` under it); the test suite points it at a throwaway directory. `CLAUDE_CONFIG_DIR`, `CODEX_HOME` and `GEMINI_CONFIG_DIR` move one client each, as everywhere else in Leg | `src/harness/registry.mjs` |
30
33
  | `LEG_NO_OPEN` | (unset, opens once) | set to `1` to start the board without opening a browser | `bin/leg.mjs` |
31
34
  | `LEG_NO_BOARD` | (unset) | set to `1` to run a session with no board at all (the record under `$LEG_HOME/sessions/` is still kept; the test suite uses this) | `src/attach.mjs` |
32
35
  | `LEG_WAIT_TICK_MS` | `1000` | how often the all-out countdown redraws and re-checks Ctrl-C / End while waiting for the first reset | `src/attach.mjs` |
@@ -45,6 +48,23 @@ record under `$LEG_HOME/sessions/`.
45
48
  `CLAUDE_CONFIG_DIR` and `CODEX_HOME` are set for the child when the session
46
49
  runs on a named account; see [Accounts](#accounts) below.
47
50
 
51
+ ### Auto-approve launch mode
52
+
53
+ By default, Leg starts interactive sessions (`leg claude`, `leg codex`, `leg agy`, and `leg grok`) in permissive auto-approve mode so you never sit through repetitive tool permission prompts:
54
+
55
+ - Claude: `--dangerously-skip-permissions`
56
+ - Codex: `--ask-for-approval never`
57
+ - agy: `--dangerously-skip-permissions`
58
+ - Grok: `--always-approve`
59
+
60
+ These flags are injected at spawn time and only affect sessions launched through Leg. Your global CLI configurations (`~/.claude.json`, `~/.codex/config.toml`, `~/.gemini/settings.json`) remain untouched.
61
+
62
+ To opt out and keep standard approval prompts:
63
+
64
+ 1. CLI flag: pass `--no-auto-approve` when starting a session (e.g. `leg claude --no-auto-approve`).
65
+ 2. Environment variable: set `LEG_AUTO_APPROVE=0` or `LEG_NO_AUTO_APPROVE=1` (or legacy `BATON_AUTO_APPROVE=0` / `BATON_NO_AUTO_APPROVE=1`).
66
+ 3. Persistent preference: set `"auto_approve": false` in `~/.leg/preferences.json`.
67
+
48
68
  ## Core
49
69
 
50
70
  | variable | default | meaning | read in |
@@ -80,7 +100,7 @@ Off unless explicitly enabled; a sync failure never blocks or fails a card
80
100
  ## Seller and checkout site
81
101
 
82
102
  These are for the checkout/key-delivery site and its deployment helpers, not
83
- normal Leg CLI use.
103
+ normal Leg use.
84
104
 
85
105
  | variable | meaning | read in |
86
106
  |----------|---------|---------|
@@ -152,6 +172,26 @@ junctions as links, never following them, then deletes the directory. Nothing
152
172
  under your real home is written at any point. Start a session on a named
153
173
  account with `LEG_ACCOUNT=<name>`, or let a limit hand off to it.
154
174
 
175
+ ## The portable harness
176
+
177
+ Off until `leg harness enable`. Its settings live in `~/.leg/preferences.json`
178
+ beside the hand-off order:
179
+
180
+ ```json
181
+ { "harness": { "enabled": true, "policy": "sync", "source": "claude" } }
182
+ ```
183
+
184
+ | key | values | meaning |
185
+ |-----|--------|---------|
186
+ | `enabled` | `true`, `false` (default) | the consent `leg harness enable` records; `leg harness disable` clears it and removes nothing |
187
+ | `policy` | `warn`, `sync` (default on enable), `strict` | what an unattended hand-off may do: report only; write managed state when safe; refuse a destination that cannot be made safe |
188
+ | `source` | `claude`, `codex` | the client whose harness is carried; auto-detected from `~/.claude/CLAUDE.md` then `~/.codex/AGENTS.md` when unset |
189
+
190
+ `~/.leg/harness/policy.json` (optional) names what is deliberately not
191
+ carried: sections to drop, hooks, skills and MCP servers to exclude, each with
192
+ a reason, and the Codex model ladder. State, backups and the evidence trail
193
+ live under `~/.leg/harness/`. Every detail: [harness.md](harness.md).
194
+
155
195
  ## Test and development seams
156
196
 
157
197
  Not meant for normal use; documented for completeness.
package/docs/faq.md CHANGED
@@ -162,6 +162,25 @@ pre-commit hook both run `scripts/privacy-check.mjs`, which additionally
162
162
  scans the whole tree for a short list of strings specific to the private
163
163
  codebase Leg's runner/ledger were ported from.
164
164
 
165
+ **Does the next agent get my rules, hooks, skills and MCP servers, or just the task?**
166
+ Just the task, unless you turn on the [portable harness](harness.md)
167
+ (`leg harness enable`). Then a hand-off also renders the source client's
168
+ global working agreement, identity, hooks, skills, subagents, slash commands,
169
+ MCP servers and permissions into the destination's own files, as far as that
170
+ client can represent them, and the terminal's details say what was dropped and
171
+ why (Codex has no SSE transport; agy has no `SessionStart` hook; a skill that
172
+ already exists as a real directory is left alone). Credentials never move: an
173
+ MCP key becomes `${NAME}` and you export it for each client. Off by default,
174
+ and `leg harness disable` turns it off without removing anything.
175
+
176
+ **Will the portable harness overwrite a file I edited?**
177
+ No. Every file it writes carries `GENERATED by Leg harness` in its first lines,
178
+ and inside a file you also own it writes a marked `leg harness` region. A
179
+ file whose content changed since Leg wrote it is backed up once under
180
+ `~/.leg/harness/backups` and skipped; the sync reports `attention` with the
181
+ path, and `leg harness sync --force` is the only way it gets replaced. The
182
+ source client is never written at all.
183
+
165
184
  **What happens if I close the terminal instead of Ctrl-C?**
166
185
  The board server and any running agents keep running as detached processes.
167
186
  Run `node bin/leg.mjs down` (or `npm run stop`) from another terminal to