@outerlayer/cli 0.1.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 (55) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +202 -0
  3. package/README.md +320 -0
  4. package/dist/build-info.json +1 -0
  5. package/dist/chunk-4H56Y7O6.js +589 -0
  6. package/dist/chunk-A3WLZX2F.js +120 -0
  7. package/dist/chunk-A6NNKRJU.js +650 -0
  8. package/dist/chunk-D77LS3UI.js +42 -0
  9. package/dist/chunk-DEDV5V4U.js +87324 -0
  10. package/dist/chunk-ECSYRPCC.js +334 -0
  11. package/dist/chunk-IWIUYLDR.js +472 -0
  12. package/dist/chunk-JJP7YLMN.js +25 -0
  13. package/dist/chunk-KFYJV2ZG.js +7978 -0
  14. package/dist/chunk-OZ7C3XUE.js +34 -0
  15. package/dist/chunk-RCQXYLMO.js +96 -0
  16. package/dist/chunk-TFUIDMOB.js +34 -0
  17. package/dist/chunk-VNVZDWO3.js +10 -0
  18. package/dist/chunk-VU34RWDU.js +339 -0
  19. package/dist/chunk-WQ6VGRGZ.js +150 -0
  20. package/dist/chunk-XN2XZFTK.js +165 -0
  21. package/dist/chunk-YMTXR7PJ.js +200 -0
  22. package/dist/chunk-YRYMNBGQ.js +54 -0
  23. package/dist/chunk-Z5GEFMRW.js +45 -0
  24. package/dist/chunk-ZJSNLVCE.js +58 -0
  25. package/dist/cli-3JYGL2OB.js +5311 -0
  26. package/dist/config-POF7DEQW.js +7 -0
  27. package/dist/context-materialize-J6CRQ7O7.js +589 -0
  28. package/dist/emit-artifact-cmd-23F4YKQ3.js +339 -0
  29. package/dist/emit-cmd-TWSYYEZI.js +208 -0
  30. package/dist/emit-commit-credit-cmd-2GX5M2BR.js +121 -0
  31. package/dist/emit-finding-cmd-6K7ERPKG.js +235 -0
  32. package/dist/emit-result-cmd-NDFHMJX7.js +225 -0
  33. package/dist/hook-fast-CBW4ZETX.js +8 -0
  34. package/dist/hook-wrap-fast-36WGE6EX.js +8 -0
  35. package/dist/import-capture-cmd-EUMBGIV3.js +176 -0
  36. package/dist/import-ruler-cmd-7LH2QOMG.js +173 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +25 -0
  39. package/dist/init-PTBITAUO.js +103 -0
  40. package/dist/login-cmd-IRX6LZT7.js +62 -0
  41. package/dist/logs-TRNPQM42.js +63 -0
  42. package/dist/loop-NFSOBUUJ.js +646 -0
  43. package/dist/mcp-install-cmd-FDQH6SEN.js +75 -0
  44. package/dist/mcp-serve-cmd-57EZZOTL.js +123 -0
  45. package/dist/paths-D2VGWWFI.js +6 -0
  46. package/dist/pidfile-PTW76F56.js +8 -0
  47. package/dist/status-WGOJTXZF.js +94 -0
  48. package/dist/statusline-fast-Z5U5CEC6.js +8 -0
  49. package/dist/sync-cmd-C5UGI4SF.js +15 -0
  50. package/dist/watch-V3K4PESQ.js +70 -0
  51. package/dist/work-claim-cmd-FQDWQVT2.js +98 -0
  52. package/dist/work-cmd-CW26EZZO.js +16 -0
  53. package/dist/work-launch-YNNCKIH3.js +6 -0
  54. package/dist/work-pr-cmd-W2QNMWXP.js +115 -0
  55. package/package.json +58 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @outerlayer/cli
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e27bd86: First release under the `@outerlayer/cli` name. The published package carries no dependencies: everything the CLI needs is bundled into its `dist`, so `npx @outerlayer/cli` installs the single tarball and nothing else.
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Magu Studios, Inc.
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,320 @@
1
+ # outerlayer
2
+
3
+ Capture what your coding agents actually did, and sync it to your OuterLayer
4
+ cloud workspace.
5
+
6
+ OuterLayer captures the sessions your coding agents already write to disk
7
+ (Claude Code, Codex CLI, Cursor) and syncs them to your OuterLayer cloud
8
+ workspace, where you and your team can see what your agents did across every
9
+ repo. Capture itself is local: it reads the session files your agents already
10
+ write to disk. Uploading is a separate, launch-gated step — what reaches the
11
+ network, and when, is below.
12
+
13
+ Launch a session naming the work it's for, then sync:
14
+
15
+ ```
16
+ npx @outerlayer/cli init # install the capture hooks
17
+ npx @outerlayer/cli work add --issue 42 # create the work item; prints its number
18
+ OUTERLAYER_WORK=7 claude # launch a session naming that number
19
+ npx @outerlayer/cli sync --dry-run # see exactly what would leave your machine
20
+ npx @outerlayer/cli sync # upload sessions launched that way
21
+ ```
22
+
23
+ ## Privacy, stated plainly
24
+
25
+ **A session uploads only when you launch it with `OUTERLAYER_WORK` naming
26
+ the work item it's for.** Create the item first with `outerlayer work add`,
27
+ which prints its factory-scoped number, then set that number before
28
+ starting your agent — `OUTERLAYER_WORK=7 claude`. The session-start hook
29
+ then records the launch and the session uploads whole, from its first turn,
30
+ until it ends. A session launched without the variable never leaves the machine,
31
+ in any tool, however `sync` is invoked. There is no way to turn upload on
32
+ mid-session — the decision is made once, at launch.
33
+
34
+ Only Claude Code has a session-start hook today, so only Claude Code sessions
35
+ can be launched as work. Codex CLI and Cursor sessions are captured locally
36
+ and never upload.
37
+
38
+ **`outerlayer sync` is the complete upload, and it sends only launched
39
+ sessions.** It runs when you run it. The hooks also fire `outerlayer sync
40
+ --quiet` in the background after each agent turn and when a session ends, at
41
+ most once every five minutes, as soon as `outerlayer login` has saved
42
+ credentials. So a launched session uploads while it is still running, not
43
+ only once it is over. Set `"autoSync": false` in `~/.outerlayer/config.json`
44
+ to leave every automatic upload — the background sync and the daemon's
45
+ streaming below — to your own command.
46
+
47
+ **`outerlayer daemon` uploads a launched session as it grows, turn by turn,
48
+ while it runs, and nothing else.** The same launch gate as `sync` applies: a
49
+ session started without `OUTERLAYER_WORK` is never streamed, in whole or in
50
+ part, and neither is a session the repo filter excludes. It sends only the
51
+ turns since the last one it already sent, and it stops sending the moment
52
+ the session ends. `"autoSync": false` stops it too, and it is read on every
53
+ send, so turning it off takes effect without restarting the daemon.
54
+ `outerlayer sync` still runs on top of this and remains the complete,
55
+ authoritative upload — the daemon exists so a session's page can follow
56
+ along before that sync ever runs.
57
+
58
+ These commands also talk to your workspace. None of them sends session
59
+ content:
60
+
61
+ - **`outerlayer work add`, `remove`, `status`, `list`, `link-session`, `pr`,
62
+ `claim`, `renew`, `release`** read and write your Floor. The session-start
63
+ hook spawns `outerlayer work link-session` when you launch a session with
64
+ `OUTERLAYER_WORK`, naming the item and the session id — the item itself
65
+ must already exist, created ahead of time with `outerlayer work add`.
66
+ `outerlayer work pr` sends the session id and the pull request number and
67
+ repository — never the session's own content. `outerlayer work claim`
68
+ records a lease for this host before it starts working on an item, so two
69
+ hosts never build the same piece of work at once; `renew` extends it and
70
+ `release` marks it done. A runner key needs `git_connection.read` to list
71
+ and read items, `work_item_claim.insert` to claim, and
72
+ `work_item_claim.update` to renew or release — the two claim permissions
73
+ are not granted to a dashboard role by default, since a claim is a host's
74
+ lease, not a person's.
75
+ - **`outerlayer runner start`** lists, claims, renews and releases work
76
+ items the same way `work claim`/`renew`/`release` do, on the schedule its
77
+ config sets — it sends nothing about the job it runs beyond that; the
78
+ agent it starts is a separate process with its own credentials, from the
79
+ `runner` block, never the copy-out daemon's. `runner check` makes one
80
+ list call to confirm the key works and sends nothing else; `runner init`,
81
+ `runner status` and `runner logs` make no network call at all.
82
+ - **`outerlayer emit artifact`** uploads the file you name — a screenshot, a
83
+ recording, a report, a log — along with its caption. With no recorded
84
+ session to attach it to, it uploads immediately, anchored to a pull request
85
+ or to the git checkout.
86
+ - **`outerlayer emit <name>`** and **`outerlayer emit commit-credit`** send
87
+ one check's outcome, and one commit's attribution, for a work item. Run
88
+ from inside a session, they send only when that session carries a launch
89
+ record — the session's own content may not leave by a second route. Run
90
+ from CI or a plain shell, where there is no session, they send as they
91
+ always have.
92
+ - **`outerlayer emit finding`** and **`outerlayer emit findings <file>`** send
93
+ one finding, or a whole batch of them, for a work item — the same
94
+ anchoring as `emit <name>` (`--item`, or the recorded session's own item),
95
+ except a session may record findings on the item it was launched for.
96
+ - **`outerlayer mcp serve`** is the stdio MCP server your editor spawns. It
97
+ forwards every JSON-RPC message the editor sends to the gateway and returns
98
+ the reply.
99
+
100
+ The session-start hook reaches the network in two more cases:
101
+
102
+ - **In a repository your factory governs**, it asks the control plane which
103
+ context repository and ref this checkout is pinned to (`GET
104
+ /v1/context/source`, with your API key), then `git fetch`es that ref. It
105
+ sends the repository name; it degrades to the last known commit when the
106
+ control plane cannot be reached.
107
+ - **When your repository's git hooks are missing**, it runs that
108
+ repository's own `prepare` script (`yarn prepare` / `npm run prepare`).
109
+ What that script does is your repository's business; installers commonly
110
+ download packages.
111
+
112
+ The daemon's one-shot sweep (`outerlayer daemon --once`) makes no network
113
+ calls — the copy-out mirror it performs is the same one the long-running
114
+ daemon runs continuously, and neither the sweep nor an ineligible session
115
+ in the long-running daemon opens a connection. The session-start hook makes
116
+ none either, in a repository your factory does not govern. All three are
117
+ checked rather than promised: a test runs each of them with `fetch` and
118
+ `net.Socket.prototype.connect` replaced by stubs that throw, and asserts
119
+ nothing tried to connect. Those two stubs cover every outbound path this
120
+ process opens itself — anything built on `node:http`, `node:https` or
121
+ `node:tls` opens its socket through `connect`, and `fetch` opens its own
122
+ below that method. A child process opens its sockets in its own address
123
+ space, past both stubs, so the same test records every program each run
124
+ starts and asserts none of them fetches over the network.
125
+
126
+ Failures the hook cannot show you — a refused `OUTERLAYER_WORK` value, a
127
+ `work add` that could not reach the Floor — are appended to
128
+ `~/.outerlayer/spool/hook-errors.log`, and the next session start says so.
129
+ The addition retries a gateway it cannot reach a few times, with backoff,
130
+ before it gives up.
131
+
132
+ - **The tier is applied before anything leaves.** The default tier is
133
+ `full`: message text, thinking, images, and tool input/output all ship.
134
+ `--tier redacted` strips that content client-side, keeping only structure
135
+ (repos, branches, file paths, tool names, error signatures) and metrics;
136
+ `--tier metrics` strips identifiers too (the server additionally clamps to
137
+ your org's configured ceiling — sending more than it allows stores less).
138
+ - **`--dry-run` shows exactly what would leave** — per-session rows, image
139
+ bytes, and the precise field classes stripped at the chosen tier. Add
140
+ `--json` to inspect the literal request payloads. Zero network calls.
141
+
142
+ ## Commands
143
+
144
+ | Command | What it does |
145
+ |---|---|
146
+ | `outerlayer sync` | Upload sessions launched with `OUTERLAYER_WORK` to your OuterLayer cloud workspace (incremental — only what's new since the last sync). Tier-gated client-side (`--tier metrics\|redacted\|full`, default `full`); `--dry-run` prints exactly what would leave the machine; `--all` re-sends everything (idempotent server-side). Credentials come from `outerlayer login`, `OUTERLAYER_*` env vars, or `--url/--app-id`. |
147
+ | `outerlayer login [--url] [--app-id]` | Save the cloud URL, app id, and API key to `~/.outerlayer/config.json` once. The key is read from stdin (`echo "$KEY" \| outerlayer login …`) or a prompt with echo off; it is never a flag. `--no-input` refuses to prompt. |
148
+ | `outerlayer init` | Install the capture hooks and the status-line segment. `--json` for scripts. Claude Code deletes transcripts after ~30 days; run `outerlayer daemon` separately to mirror them first — `init` does not start it for you. |
149
+ | `outerlayer daemon` | Run the copy-out daemon in the foreground (`--once` for a single sweep, which uploads nothing). Once cloud credentials exist, it also streams a launched session's new turns as they land — see Privacy, above. `outerlayer watch` is the former name and still works, with a warning. |
150
+ | `outerlayer doctor` | Check the installation: hooks, status-line freshness, and sync health. `--json` prints the checks and a summary for scripts. |
151
+ | `outerlayer context emit [--check]` | Compile `.outerlayer/` into each configured target tool's native files (targets come from `.outerlayer/config.json`). `--check` computes outputs and diffs against disk without writing (CI mode). Bare `outerlayer emit` with no name still compiles, with a deprecation warning. |
152
+ | `outerlayer import ruler` | Port a `.ruler/` tree ([Ruler](https://github.com/intellectronica/ruler)) into the equivalent `.outerlayer/` tree — mostly a rename; never overwrites an existing `.outerlayer/`. |
153
+ | `outerlayer hooks wrap` / `outerlayer hooks unwrap` | Auto-wrap (or undo wrapping) `PreToolUse`/`PostToolUse` hooks for execution evidence — one spawn per firing. |
154
+ | `outerlayer emit artifact <file> --caption <text> [--for <criterion-id>] [--pr <n>]` | Upload a proof artifact — screenshot, recording, report, or log — with its caption. Inside a recorded session it spools locally and ships on the next `sync`; otherwise it uploads immediately, anchored to a pull request or the current checkout. `--replaces` retires artifacts an earlier run uploaded. |
155
+ | `outerlayer emit <name> --result <pass\|fail> [--link <url>] [--body <text>\|--body-file <path>] --item <number>` | Record one named check's outcome on a work item. A check that ran carries the run URL as its proof (`--link`); a judgment you are making yourself carries one sentence (`--body`, or `--body-file` to read it from a file — `-` reads standard input). A `fail` needs at least one of the two; a `pass` needs neither. `--item` names the work item by the number printed when the item was created — always required, in or out of a recorded session; from inside a session it can only name an item that session was NOT launched for (a session cannot record a check on its own item). Prints the recorded check's id. Who recorded it comes from your API key, never from what you send. |
156
+ | `outerlayer emit artifact-review --result <pass\|fail> --artifact <id> [--body <text>\|--body-file <path>]` | Record a person's own pass or fail on one artifact — evidence already emitted, bound to a criterion. `--artifact` names it and replaces `--item`; the gateway resolves the work item from the artifact's own pull request. `--link` is refused. A `fail` reads its sentence from `--body`/`--body-file`, or from standard input when neither is given. Refused from inside a recorded session — an artifact verdict is a person's act, the same rule the gateway enforces. Prints the recorded verdict's id. |
157
+ | `outerlayer emit commit-credit --pr <n> …` | Send one commit's attribution for a pull request. |
158
+ | `outerlayer emit finding --id <id> --subject <change\|context> --title <text> --file <path> --kind <behavior\|hygiene\|proof> --verdict <confirmed\|refuted\|unverified> --source <implementer\|reviewer\|refuter\|gate> --where <label> [--item <number>] …` | Record one finding — what was found wrong about the change (`--subject change`) or about a rule it ran on (`--subject context`, which then needs `--rule-path`, `--rule-quote` and `--rule-relation` together — all three are required, not just `--rule-path`). Validated against the same contract the gateway checks before anything is sent. `--item` names the work item; without it, inside a recorded session, the item that session was launched for is used automatically — unlike `emit <name>`, a session may record findings on its own item. Re-emitting the same `--id` on the item replaces that finding. |
159
+ | `outerlayer emit findings <file> [--item <number>]` | Record a whole batch at once, read from a `FindingBatch` JSON file (`-` for standard input) — the same shape and validation as `emit finding`, one record per subject/title/file/kind/verdict/source/where. Anchored the same way: `--item`, else a recorded session's own item. |
160
+ | `outerlayer mcp install [--transport stdio\|http] [--url] [--name] [--command]` | Write (or update) an `mcpServers` entry in `.mcp.json` for the OuterLayer gateway. Default `stdio`: the client spawns `outerlayer mcp serve`, which reads the API key from `~/.outerlayer/config.json` (or `OUTERLAYER_API_KEY`) each time it connects, so a reconnect picks up a newly saved or rotated key. `--transport http` writes a direct `POST /v1/mcp` entry referencing `${OUTERLAYER_API_KEY}`, resolved by the client from the environment it was launched with. Never writes an API key. Pass `--url`/`--app-id` for self-host. |
161
+ | `outerlayer mcp serve [--url] [--app-id]` | Stdio MCP server bridging stdin/stdout JSON-RPC to the gateway's `POST /v1/mcp`. What the stdio `.mcp.json` entry runs; exits 1 with a clear message when no API key is configured. |
162
+ | `outerlayer work add --issue <n>\|--pr <n> [--repo] [--note]` | Records that a source — a recorded session, a CI run, or the API key's bound member — is working on an issue or pull request. The only way an item becomes visible on the Floor. |
163
+ | `outerlayer work remove --issue <n>\|--pr <n> --reason <text> [--repo]` | Withdraws the caller's own addition, recording the reason. Never deletes anything; the item leaves the Floor only once no live addition remains on it. Removing again is a no-op that still succeeds. |
164
+ | `outerlayer work status --issue <n>\|--pr <n> [--repo]` | Shows one item's stage, section, gate ledger, linked pull requests and sessions, and its additions. |
165
+ | `outerlayer work list [--stage] [--section] [--repo] [--unclaimed] [--startable] [--needs amend]` | Lists live work items for the current factory, filterable by stage, section, claim state and whether an open fail is waiting for an answer. |
166
+ | `outerlayer work pr <n> [--repo] [--session-id]` | Run inside a session on a work item: declares that pull request `<n>` in the checkout's repository belongs to that item. Idempotent — declaring the same pull request twice is a no-op. Refuses when the session is on no item, or the repository is not connected. |
167
+ | `outerlayer work claim --item <n>\|--issue <n> --kind implement\|amend [--repo] [--host] [--seconds]` | Records a lease for this host (default: its own hostname), so two hosts never build the same item at once. A live lease already held by a different host is refused; claiming again under the same host extends it. Lease length defaults to 900 seconds, the server's own cap. |
168
+ | `outerlayer work renew --item <n>\|--issue <n> [--repo] [--host] [--seconds]` | Extends this host's own live lease. Refused if the lease has expired, or if a different host holds it. |
169
+ | `outerlayer work release --item <n>\|--issue <n> [--repo] [--host] [--outcome]` | Marks this host's lease released, optionally recording how the attempt ended. Releasing an already-released lease is a no-op that returns the recorded release time and outcome. |
170
+ | `outerlayer runner init [--config <path>]` | Writes a `runner` block with defaults and three example hooks, keeping every key the config already had. Refuses rather than overwriting an existing block. |
171
+ | `outerlayer runner check [--config <path>]` | Validates the config, the hooks and the key, prints the settings the runner would use, and exits non-zero on a problem. Takes no work and writes no pid file. |
172
+ | `outerlayer runner start [--config <path>]` | Runs the loop: claim work, run it, sync, clean up, report, release with an outcome. Repeat. Reads the `runner` block from the config file (default `~/.outerlayer/config.json`). Refuses to start on a bad config, an unrunnable hook, or a refused key. |
173
+ | `outerlayer runner stop [--config <path>] [--now]` | Takes no more work and waits for running jobs to finish, naming each every five seconds. `--now` ends them immediately with outcome `stopped`. Ctrl-C on a foreground runner drains; a second one stops now. |
174
+ | `outerlayer runner status [--config <path>] [--recent] [--json]` | A header naming the runner's own state, then a row per running job — item, queue, stage, elapsed, started, lease, job directory. `--recent` adds finished jobs, newest first, capped at 20, with their outcomes. |
175
+ | `outerlayer runner logs <item> [--config <path>] [-f]` | Prints the newest attempt's log for one item, following it with `-f`. |
176
+
177
+ The `work` commands need an API key that carries `Ingest traces`
178
+ (`trace.write`) to add, remove, and declare a pull request (`pr`), and `Read
179
+ the Work page` (`git_connection.read`) to look an item up — which `remove` and
180
+ `status` both do before they act. Tick both when you mint the key, in
181
+ Settings → API keys. Claiming a lease needs `Claim work items`
182
+ (`work_item_claim.insert`); renewing or releasing one needs `Renew or
183
+ release work item claims` (`work_item_claim.update`). Withdrawing an
184
+ addition somebody else made additionally needs `Withdraw others' work from
185
+ the Work page` (`git_connection.update`).
186
+
187
+ ### Recording your own pass or fail on a check
188
+
189
+ `outerlayer emit` also records a judgment you make yourself, under a check
190
+ name a validator declares, on the work item — not any one pull request, so
191
+ it holds across every pull request the item has. Say what is wrong, in one
192
+ sentence:
193
+
194
+ ```
195
+ outerlayer emit code-review --result fail --item 412 \
196
+ --body "The button should be the destructive red, not grey."
197
+ ```
198
+
199
+ The sentence can come from a file, or from standard input with `-`:
200
+
201
+ ```
202
+ echo "The button should be the destructive red, not grey." \
203
+ | outerlayer emit code-review --result fail --item 412 --body-file -
204
+ ```
205
+
206
+ Record a pass once the work is right:
207
+
208
+ ```
209
+ outerlayer emit code-review --result pass --item 412
210
+ ```
211
+
212
+ `--item` is always required, in or out of a recorded session. A session
213
+ records a check only on an item it was NOT launched for, by naming it with
214
+ `--item`; a check on the item the session itself was launched for needs a
215
+ machine key (an API key run outside the recorded session) or CI.
216
+
217
+ Three things to know. Who recorded a check is decided by the API key you
218
+ used, never by anything in the request. A key bound to a membership records
219
+ the check against that person, and once a fail is recorded, only a person's
220
+ key can record over it. A failing check needs a run link or a sentence,
221
+ because a fail with neither is something nobody can act on. And a check only
222
+ holds a gate once a validator in `.outerlayer/validators/` declares its emit
223
+ name; without one the check is recorded and read back, and blocks nothing.
224
+
225
+ A review of the work itself is different: it is recorded from the Review
226
+ tab on the work item page, not the CLI, and holds every pull request of the
227
+ item until a person records a pass — no validator declaration needed.
228
+ `outerlayer emit` refuses `work-review` outright; recording it anywhere but
229
+ the work item page is not supported. The gateway refuses a review, and an
230
+ artifact verdict, on an item that is closed or shipped, has no evaluation
231
+ yet, or whose evaluation is still waiting on a session link, with the code
232
+ `item_not_reviewable`. A recorded pass carries the head commit of every
233
+ pull request the item had at the time; once one of them moves, that pass
234
+ stops counting and a fresh review is needed. A repository can require a
235
+ current pass before the evidence check completes by setting the base
236
+ branch's `review` policy key to `required` (default `optional`, alongside
237
+ `merge_gate`) — the check then stays in progress, not failed, until a
238
+ current pass exists.
239
+
240
+ `outerlayer emit artifact-review` sits between the two: a person's own pass
241
+ or fail, like a review, but on one piece of evidence rather than the whole
242
+ item. A fail here holds the item the same way a review's fail does, and
243
+ counts the work as bad on Quality once, however many artifacts carry one —
244
+ it also lists the item on a queue the API exposes, naming the artifact and
245
+ why it failed, so a host with a session linked to the item can answer it
246
+ with a replacement, or the fail's own recorder can pass over it directly.
247
+ The item's own pass stays available only once every required artifact has
248
+ a pass and no fail is still open.
249
+
250
+ Every command accepts `--no-color`, and color is off on its own when stdout is not a terminal, when `NO_COLOR` is set, or when `TERM` is `dumb`. `FORCE_COLOR=1` turns it on for a pipe. Commands that print anything accept `--json`.
251
+
252
+ ## Status line
253
+
254
+ `init` also adds an ambient Claude Code status-line segment showing what your
255
+ session and your agents are costing today:
256
+
257
+ ```
258
+ ⬢ OL $0.87 session · $23.40 today across 3 agents · 12 unsynced
259
+ ```
260
+
261
+ The session figure comes straight from Claude Code's own cost field, so it
262
+ always matches what Claude Code itself would show. The cross-agent total and
263
+ unsynced count come from `~/.outerlayer/statusline.json`, a small file the
264
+ `watch` daemon keeps fresh — the status line itself never parses transcripts,
265
+ so it stays well under Claude Code's refresh budget. On a day with only one
266
+ active agent, the scope adapts: `across N agents` becomes `across N sessions`
267
+ if you ran several sessions with it, or drops entirely to a bare `$X today`
268
+ for a single session — "across 1 agent" never appears.
269
+
270
+ If a `statusLine` command is already configured, `init` **wraps it rather
271
+ than replacing it**: your existing command's output is printed first, the
272
+ OuterLayer segment appends after. A hang or failure in the wrapped command
273
+ never blanks the line — it times out and OuterLayer's segment prints alone.
274
+ `outerlayer init --remove` restores the original command exactly.
275
+
276
+ Without `outerlayer watch` running, the line degrades gracefully to the
277
+ session figure alone plus a dim `outerlayer doctor` hint — run `outerlayer
278
+ doctor` to see why (usually: the daemon isn't running, or hasn't refreshed
279
+ recently).
280
+
281
+ Opt out of the segment with `outerlayer init --no-statusline`.
282
+
283
+ ## Supported agents
284
+
285
+ | Agent | Source | Status |
286
+ |---|---|---|
287
+ | Claude Code | `~/.claude/projects` (+ raw mirror) | full: turns, tool I/O, thinking, images, subagents, cost |
288
+ | Codex CLI | `~/.codex/sessions` | captured locally only — turns, tool I/O, edits (apply_patch), errors, usage |
289
+ | Cursor | `~/.cursor/chats` | captured locally only — turns, thinking, tool I/O, edits, errors, no cost (Cursor stores no token usage) |
290
+
291
+ Upload needs a session-start hook to record the launch, and only Claude Code
292
+ has one, so only Claude Code sessions can be launched as work today.
293
+
294
+ Sessions from every agent land in one canonical schema
295
+ (`@outerlayer/session-schema`), so sync and everything downstream treat them
296
+ identically. Adding an agent is one source adapter.
297
+
298
+ ## How capture works
299
+
300
+ Your agents already write complete transcripts to disk — OuterLayer treats
301
+ those as the source of truth rather than wrapping or proxying the agent:
302
+
303
+ - **`init`** adds a <50ms hook that notes each session event and installs
304
+ the status-line segment.
305
+ - **`outerlayer daemon`** mirrors transcripts before the agent deletes them
306
+ — so history survives even for agents with retention windows. It's a
307
+ separate, long-running process you start yourself; `init` doesn't start
308
+ it for you.
309
+ - **`sync`** parses whatever is on disk and ships the sessions you launched
310
+ with `OUTERLAYER_WORK`, incrementally, only what's new since the last run.
311
+
312
+ No API keys, no model calls, no interception. If you uninstall OuterLayer,
313
+ your agents never notice.
314
+
315
+ ## Requirements
316
+
317
+ Node 22+. macOS and Linux; Windows untested (issues welcome). Capturing
318
+ **Cursor** sessions additionally needs Node 22.5+ (it reads Cursor's SQLite
319
+ chat store via the `node:sqlite` builtin); on older Node, Cursor is not
320
+ captured at all.
@@ -0,0 +1 @@
1
+ {"id":"2026-09-23T16:41:05.295Z+7ad24522","root":"/Users/carbonteq/manage-ai/control-plane/cli-release","pkg":"outerlayer"}