@outerlayer/cli 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +9 -4
  3. package/dist/{agent-setup-52LRZ6AY.js → agent-setup-QE6PSCEZ.js} +8 -8
  4. package/dist/build-info.json +1 -1
  5. package/dist/{builder-OF67QKDS.js → builder-UUS4ORDH.js} +3 -3
  6. package/dist/{check-RJOJU2WA.js → check-63IDCEEV.js} +27 -27
  7. package/dist/{chunk-XR3BJN33.js → chunk-22RMZBGW.js} +20 -1
  8. package/dist/{chunk-AHZQBGD2.js → chunk-2M63GG56.js} +1 -1
  9. package/dist/{chunk-27Y7AX2O.js → chunk-3D4OYFCD.js} +4 -4
  10. package/dist/{chunk-GGMRZPEA.js → chunk-3KTA5UU2.js} +1 -1
  11. package/dist/{chunk-USO2DKBD.js → chunk-4KSQKQXE.js} +8 -2
  12. package/dist/{chunk-C7N4TDHW.js → chunk-4QVZCVWT.js} +5 -5
  13. package/dist/{chunk-NPXQXYVA.js → chunk-6S3H3J6H.js} +2 -2
  14. package/dist/{chunk-DVT73QJO.js → chunk-C6IV5BV7.js} +10 -10
  15. package/dist/{chunk-JOXYKGUV.js → chunk-CNFVHDHG.js} +1 -1
  16. package/dist/{chunk-ANW7VHMH.js → chunk-DPKX26LT.js} +92 -16
  17. package/dist/{chunk-ZQUCTFXC.js → chunk-DXQ5MHCO.js} +1 -1
  18. package/dist/{chunk-GATMPAMK.js → chunk-EFZLAYPE.js} +2 -2
  19. package/dist/{chunk-SZOVKFWB.js → chunk-GI4XXD4I.js} +3 -3
  20. package/dist/{chunk-G5XY6CVG.js → chunk-HDTVTA6I.js} +2 -2
  21. package/dist/{chunk-325272HM.js → chunk-J5M7BSJQ.js} +11 -4
  22. package/dist/{chunk-3ZPYPFNM.js → chunk-JXYVA45C.js} +1 -1
  23. package/dist/{chunk-G5IEVZGI.js → chunk-LCEXHCHD.js} +1 -1
  24. package/dist/{chunk-GEBQXUFY.js → chunk-MJXXRX4M.js} +24 -10
  25. package/dist/{chunk-SKHN72JO.js → chunk-MZN7AQOB.js} +8 -8
  26. package/dist/{chunk-QEESCZ26.js → chunk-NX57IBNK.js} +1 -1
  27. package/dist/{chunk-6FH2AHJY.js → chunk-P4MM66C6.js} +30 -17
  28. package/dist/{chunk-IBB4D4KE.js → chunk-P6FX4D43.js} +1 -1
  29. package/dist/{chunk-A56PYPDF.js → chunk-R7OQTKWT.js} +2 -1
  30. package/dist/{chunk-2KLBKHT4.js → chunk-RLYZLUQ7.js} +1 -1
  31. package/dist/chunk-RMWLBUCJ.js +50 -0
  32. package/dist/{chunk-YEQL54G5.js → chunk-SFFHGX3G.js} +1 -1
  33. package/dist/{chunk-G4RXNF2L.js → chunk-SVEP7PVH.js} +12 -12
  34. package/dist/{chunk-VK25O2ZM.js → chunk-T5VPP6H7.js} +1 -1
  35. package/dist/{chunk-FO7KKPYG.js → chunk-TCIW27FM.js} +1 -1
  36. package/dist/{chunk-PDRYXERC.js → chunk-TOPDNXHZ.js} +5 -5
  37. package/dist/{chunk-5TGQHOUX.js → chunk-XJKL4BNU.js} +3 -3
  38. package/dist/{cli-JSSRGA6V.js → cli-WSBIAVZU.js} +116 -80
  39. package/dist/{command-3WYZ2ML5.js → command-N7BAB4XJ.js} +18 -18
  40. package/dist/{config-MKBX2ZQD.js → config-N3C6DFQA.js} +1 -1
  41. package/dist/{connect-cmd-A5NGODCJ.js → connect-cmd-FGYRLLXL.js} +5 -5
  42. package/dist/{context-adopt-BAHGXJZV.js → context-adopt-B3PWGFHL.js} +5 -5
  43. package/dist/{context-materialize-45OVRRK2.js → context-materialize-7S6NLLRO.js} +5 -5
  44. package/dist/{dist-EQDW7MZW.js → dist-LKDWQWZ7.js} +2 -2
  45. package/dist/{docker-VGWEJWI7.js → docker-BZ55BMBJ.js} +4 -4
  46. package/dist/{doctor-4FFXTQJ2.js → doctor-3H25Q7WT.js} +20 -20
  47. package/dist/{emit-artifact-cmd-QS7XNKBT.js → emit-artifact-cmd-IQCRXOH7.js} +8 -8
  48. package/dist/{emit-cmd-LXEIDKLT.js → emit-cmd-KDSM5VAO.js} +4 -4
  49. package/dist/{emit-criteria-cmd-XN6AKA53.js → emit-criteria-cmd-EYUXME26.js} +6 -6
  50. package/dist/{emit-finding-cmd-SQJSEHYS.js → emit-finding-cmd-P4RKBPBB.js} +6 -6
  51. package/dist/{emit-result-cmd-56RHPHOA.js → emit-result-cmd-6XYENU6H.js} +8 -8
  52. package/dist/{hook-fast-HLESEJAL.js → hook-fast-PXWKQPN6.js} +1 -1
  53. package/dist/{import-capture-cmd-NHMAFDM6.js → import-capture-cmd-2BKM5I2N.js} +3 -3
  54. package/dist/{import-ruler-cmd-UYB32YUT.js → import-ruler-cmd-IXR7YK47.js} +2 -2
  55. package/dist/index.js +3 -3
  56. package/dist/{init-UZKD4RY7.js → init-2LWVS4AH.js} +19 -19
  57. package/dist/{init-6AY5T3P6.js → init-ZL7ORPTF.js} +38 -28
  58. package/dist/{init-cmd-XGSX2BCC.js → init-cmd-JXOBCOIB.js} +14 -12
  59. package/dist/{install-VXXI7UR6.js → install-HDBIPHTX.js} +2 -2
  60. package/dist/{login-browser-7XGSV7MA.js → login-browser-QZCS3WW6.js} +2 -1
  61. package/dist/{loop-4NMI2USY.js → loop-XOAE6ZIY.js} +27 -27
  62. package/dist/{machine-4JX2D4TS.js → machine-G6PY2J2J.js} +15 -15
  63. package/dist/{mcp-install-cmd-XZARXNYT.js → mcp-install-cmd-VXVIH7PH.js} +1 -1
  64. package/dist/{runner-env-WAXTJXZK.js → runner-env-XTC53CHN.js} +3 -3
  65. package/dist/runner-key-TDTLNAKJ.js +154 -0
  66. package/dist/{session-sender-T7PN4YRC.js → session-sender-XVGZFWRK.js} +6 -6
  67. package/dist/{starter-pack-Z22VRFPF.js → starter-pack-B7JPGEDO.js} +4 -4
  68. package/dist/{status-OXBIRWJ7.js → status-MRTPG34B.js} +20 -20
  69. package/dist/{sync-cmd-EVXNR47N.js → sync-cmd-UBUZMMDN.js} +5 -5
  70. package/dist/{watch-7NSFOAL5.js → watch-STNPEWD6.js} +5 -5
  71. package/dist/{work-claim-cmd-KR7UR2AB.js → work-claim-cmd-CWGYEIJE.js} +10 -10
  72. package/dist/{work-cmd-JPM37MLF.js → work-cmd-X7NMJDFL.js} +7 -7
  73. package/dist/{work-comment-cmd-3DC6BC34.js → work-comment-cmd-FI2TMAYQ.js} +7 -7
  74. package/dist/{work-open-pr-cmd-XZQRJBNZ.js → work-open-pr-cmd-OVHJ5I5H.js} +8 -8
  75. package/dist/{work-pr-cmd-WHGZOP22.js → work-pr-cmd-Y5TDMQ4O.js} +7 -7
  76. package/package.json +1 -1
  77. package/skill-pack/template/build/SKILL.md +60 -33
  78. package/skill-pack/template/build/references/agent-briefs.md +4 -2
  79. package/skill-pack/template/build/references/release.md +1 -1
  80. package/skill-pack/template/build/references/review-loop.md +6 -2
  81. package/skill-pack/template/build/scripts/record-criteria.mjs +109 -143
  82. package/skill-pack/validators/code-review.yaml +4 -2
  83. package/dist/chunk-T6OIJQRT.js +0 -31
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @outerlayer/cli
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 76222d4: The starter `/build` skill no longer parses fixed shapes out of an issue. The agent reads the issue's acceptance criteria in any form and keeps their text as written. `record-criteria.mjs` now takes the agent's file with `--criteria-file`. It keeps the issue's ids when every criterion has one that fits the id grammar, and otherwise numbers them `AC-<item>-NN`. A partly numbered list is now numbered and recorded, not skipped. When an issue has no criteria, the build drafts them from the description and runs the script with `--drafted`, which records the list with `drafted: true`. `outerlayer emit criteria` reads an optional top-level `"drafted": true` from its file and sends it with the list.
8
+ - 07677fd: `outerlayer runner init` creates the runner's API key from your login when the config file has none. It uses the Runner preset, names the key after the machine (`--key-name` to override), creates it on the factory `--app-id` names, and checks it before any VM or service step. On a Mac with `--vm`, the key goes straight into the VM and is never written on the Mac. A login that cannot create keys gets a refusal naming the pasted-key path, which still works as before.
9
+
10
+ ### Patch Changes
11
+
12
+ - b831165: Fix rough edges a first-time user meets. `init` writes an MCP server entry that runs through npx, so it starts without a global install, and `doctor` warns when the entry's command is not on PATH. `init` marks a connection whose repository still needs linking with `!` and names the factory by its slug. `login` suggests `init` in a repository that is not set up. `doctor` warns when a factory has no connected repository, stops listing one Claude Code install twice, and says the first hook run creates the spool. `work build` names the item and the issue separately, prints the item's page, and honours NO_COLOR and non-terminal output. A runner keeps the build's item key in its own owner-only file instead of `job.json`. The starter build skill uses the branch a host allows, and the code review check says the report's link can take a minute to appear. `runner init`, `runner check` and `runner start` refuse an empty or blank runner key or factory id, or one still holding a `<...>` placeholder, naming the field and the config file.
13
+ - 836d0e0: A validator file can no longer declare `artifact`, `finding` or `findings` as its emit name, the same as `criteria`. Each is an `outerlayer emit` subcommand, so `outerlayer emit <name>` ran the subcommand and the check could never be recorded. A file that declares one now fails to load with a policy error naming the subcommand.
14
+
3
15
  ## 0.12.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -142,9 +142,14 @@ content:
142
142
  request it makes with the host key, the way the runner does. The signature
143
143
  covers the request's method, path, query and body, and adds nothing else
144
144
  to it.
145
- - **`outerlayer runner init`** makes no network call, except `runner init --vm`
146
- on a Mac. That runs `limactl`, and Lima downloads a Linux VM image. Inside
147
- the VM, `apt` downloads Node and `npm` downloads this CLI. Your runner key goes to the VM on the standard
145
+ - **`outerlayer runner init`** makes no network call when the config file
146
+ already holds a runner key. With no key there and a saved login, it
147
+ creates one: `POST /v1/api-keys` with your login's token, sending the key's
148
+ name (this machine's name, or `--key-name`), the Runner preset's
149
+ permissions and the factory id. It then checks the new key, unsigned, with
150
+ `GET /v1/runner/host-key`. `runner init --vm` on a Mac also runs `limactl`,
151
+ and Lima downloads a Linux VM image. Inside the VM, `apt` downloads Node and
152
+ `npm` downloads this CLI. Your runner key goes to the VM on the standard
148
153
  input of a `limactl shell` command and is never a file or an argument on the
149
154
  Mac.
150
155
  - **`outerlayer login`**, with no key on stdin, signs in to your account by
@@ -386,7 +391,7 @@ before it gives up.
386
391
  | `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. The lease belongs to the runner key that claimed it: a live lease held by a different key is refused, even under the same host name, and claiming again with the same key extends it. The first claim creates the host key (`host-key.pem` beside the config file) and binds the runner key to it; from then on every request made with that key is signed. Lease length defaults to 900 seconds, the server's own cap. |
387
392
  | `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 runner key holds it (403 `claim_not_held`). |
388
393
  | `outerlayer work release --item <n>\|--issue <n> [--repo] [--host] [--session-id] [--outcome]` | Marks the lease this API key holds released, optionally recording how the attempt ended. Releasing an already-released lease is a no-op that returns the recorded release time and outcome. Run inside a session with no `--host`, it releases that session's own lease instead (`--session-id` names the session when it cannot be detected), so the item's review opens without ending the session. |
389
- | `outerlayer runner init [--vm\|--no-vm] [--config <path>]` | Writes a `runner` block with defaults, keeping every key the config already had. It names `builtin:vm` on a Linux machine where the user can use `/dev/kvm` and Docker Engine can build images once the guest kernel and helper are published (not yet), `builtin:container` on that machine until then, `builtin:container` on any other machine with Docker Engine, and `builtin:process` on one without, and writes no hook script. Refuses rather than overwriting an existing block. On macOS it asks whether to create a Linux VM with Lima, installs Docker Engine and the runner in it, and starts both at login; `--vm` answers yes and `--no-vm` answers no, and a run with no terminal answers no. The runner key and the host key then live only in the VM: the Mac's config loses `apiKey` and `runner` and gains a `runnerVm` note, which `outerlayer doctor` reads. |
394
+ | `outerlayer runner init [--vm\|--no-vm] [--app-id <id>] [--key-name <name>] [--config <path>]` | Writes a `runner` block with defaults, keeping every key the config already had. With no `apiKey` in the config and a saved login, it first creates a runner key from the login with the Runner preset, named after the machine or `--key-name`, on the factory `--app-id` names, and checks it before anything else changes. It names `builtin:vm` on a Linux machine where the user can use `/dev/kvm` and Docker Engine can build images once the guest kernel and helper are published (not yet), `builtin:container` on that machine until then, `builtin:container` on any other machine with Docker Engine, and `builtin:process` on one without, and writes no hook script. Refuses rather than overwriting an existing block. On macOS it asks whether to create a Linux VM with Lima, installs Docker Engine and the runner in it, and starts both at login; `--vm` answers yes and `--no-vm` answers no, and a run with no terminal answers no. The runner key and the host key then live only in the VM: the Mac's config loses `apiKey` and `runner` and gains a `runnerVm` note, which `outerlayer doctor` reads. |
390
395
  | `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. |
391
396
  | `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. |
392
397
  | `outerlayer runner image --repository <owner/name> [--remote <url>] [--config <path>] [--json]` | Builds the image a repository's builds run in. Fetches the head of the default branch into the runner's cache, builds its `.devcontainer/devcontainer.json` with the devcontainer CLI, ignores every setting that reaches the host or widens the container's privileges, and adds this CLI and a pinned Claude Code. Reuses the image until the recipe or the tools change, keeps the newest three, and writes `recipe.json` beside the image record. Refuses a Compose recipe (`recipe_needs_docker`) and a repository with only named configurations (`recipe_ambiguous`). |
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { runEmit } from './chunk-G5XY6CVG.js';
4
- import { addStdioMcpEntryIfAbsent, addDocsMcpEntryIfAbsent, DOCS_MCP_SERVER_NAME } from './chunk-A56PYPDF.js';
5
- import { installStarterPack } from './chunk-ZQUCTFXC.js';
3
+ import { runEmit } from './chunk-HDTVTA6I.js';
4
+ import { addStdioMcpEntryIfAbsent, addDocsMcpEntryIfAbsent, DOCS_MCP_SERVER_NAME } from './chunk-R7OQTKWT.js';
5
+ import { installStarterPack } from './chunk-DXQ5MHCO.js';
6
6
  import { C } from './chunk-YRYMNBGQ.js';
7
- import { resolveGitCommonDir, defaultGitRunner } from './chunk-SZOVKFWB.js';
7
+ import { resolveGitCommonDir, defaultGitRunner } from './chunk-GI4XXD4I.js';
8
8
  import './chunk-ZJSNLVCE.js';
9
- import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-FO7KKPYG.js';
10
- import { pinExists, readContextManifest } from './chunk-G5IEVZGI.js';
11
- import { generatedHeaderSource } from './chunk-AHZQBGD2.js';
9
+ import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-TCIW27FM.js';
10
+ import { pinExists, readContextManifest } from './chunk-LCEXHCHD.js';
11
+ import { generatedHeaderSource } from './chunk-2M63GG56.js';
12
12
  import './chunk-YO4PSLVB.js';
13
13
  import './chunk-PLMTLGYH.js';
14
- import './chunk-325272HM.js';
14
+ import './chunk-J5M7BSJQ.js';
15
15
  import './chunk-EABW6AJQ.js';
16
16
  import './chunk-BYT5O5RV.js';
17
17
  import './chunk-RA3O54FC.js';
@@ -1 +1 @@
1
- {"id":"2026-10-08T01:27:55.356Z+5a1c8e3","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
1
+ {"id":"2026-10-08T17:26:49.421Z+36ec792","root":"/home/runner/_work/outerlayer/outerlayer","pkg":"outerlayer"}
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- export { MIN_BUILDX_VERSION, builderName, builderSpec, buildxState, ensureBuilder, ensureFailureCount, isOutOfMemory, proxyEnvironment, trackImageBuild } from './chunk-QEESCZ26.js';
3
+ export { MIN_BUILDX_VERSION, builderName, builderSpec, buildxState, ensureBuilder, ensureFailureCount, isOutOfMemory, proxyEnvironment, trackImageBuild } from './chunk-NX57IBNK.js';
4
4
  import './chunk-CX22S5BA.js';
5
- import './chunk-3ZPYPFNM.js';
5
+ import './chunk-JXYVA45C.js';
6
6
  import './chunk-7RQ73DLO.js';
7
7
  import './chunk-4Q7OMKAT.js';
8
- import './chunk-325272HM.js';
8
+ import './chunk-J5M7BSJQ.js';
9
9
  import './chunk-BYT5O5RV.js';
10
10
  import './chunk-ZM2IMMYN.js';
11
11
  import './chunk-Z5GEFMRW.js';
@@ -1,69 +1,69 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { probeCredentials } from './chunk-G4RXNF2L.js';
4
- import './chunk-DVT73QJO.js';
3
+ import { probeCredentials } from './chunk-SVEP7PVH.js';
4
+ import './chunk-C6IV5BV7.js';
5
5
  import './chunk-O765JR4W.js';
6
6
  import './chunk-UGP76IZ5.js';
7
7
  import './chunk-3JVYMD4G.js';
8
- import './chunk-T6OIJQRT.js';
9
- import './chunk-I2UOAV7X.js';
10
8
  import './chunk-YETTWTAY.js';
9
+ import './chunk-RMWLBUCJ.js';
10
+ import './chunk-I2UOAV7X.js';
11
11
  import './chunk-FI5UYJMQ.js';
12
- import './chunk-5TGQHOUX.js';
13
- import './chunk-27Y7AX2O.js';
12
+ import './chunk-XJKL4BNU.js';
13
+ import './chunk-3D4OYFCD.js';
14
14
  import './chunk-HHJDN6AU.js';
15
15
  import './chunk-HYZRY4KZ.js';
16
- import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem } from './chunk-PDRYXERC.js';
16
+ import { placeholderQueues, placeholderProblem, permissionModeProblems, permissionModeProblem, needsClaudeCredential, claudeCredentialCheck, claimPermissionProblem } from './chunk-TOPDNXHZ.js';
17
17
  import './chunk-YYYRBYWG.js';
18
18
  import './chunk-TI7B7UEO.js';
19
19
  import './chunk-TBT347UY.js';
20
- import './chunk-GEBQXUFY.js';
21
- import './chunk-IBB4D4KE.js';
22
- import './chunk-SKHN72JO.js';
23
- import './chunk-VK25O2ZM.js';
24
- import './chunk-2KLBKHT4.js';
20
+ import './chunk-MJXXRX4M.js';
21
+ import './chunk-P6FX4D43.js';
22
+ import './chunk-MZN7AQOB.js';
23
+ import './chunk-T5VPP6H7.js';
24
+ import './chunk-RLYZLUQ7.js';
25
25
  import { parseRunnerEnv } from './chunk-6MZSX4M7.js';
26
26
  import './chunk-3FMPZPJQ.js';
27
27
  import './chunk-N5FOE5PS.js';
28
28
  import './chunk-OGFGZQA3.js';
29
- import './chunk-GGMRZPEA.js';
29
+ import './chunk-3KTA5UU2.js';
30
30
  import './chunk-QOZKPLVJ.js';
31
- import './chunk-QEESCZ26.js';
31
+ import './chunk-NX57IBNK.js';
32
32
  import './chunk-CX22S5BA.js';
33
33
  import './chunk-QKFB4K7X.js';
34
34
  import './chunk-RSNSZ2LJ.js';
35
35
  import './chunk-F7CU5ABH.js';
36
36
  import './chunk-BKO6JEZI.js';
37
- import './chunk-6FH2AHJY.js';
38
- import './chunk-GATMPAMK.js';
37
+ import './chunk-P4MM66C6.js';
38
+ import './chunk-EFZLAYPE.js';
39
39
  import './chunk-IP7BIBXJ.js';
40
- import './chunk-C7N4TDHW.js';
41
- import './chunk-JOXYKGUV.js';
42
- import './chunk-3ZPYPFNM.js';
43
- import './chunk-USO2DKBD.js';
40
+ import './chunk-4QVZCVWT.js';
41
+ import './chunk-CNFVHDHG.js';
42
+ import './chunk-JXYVA45C.js';
43
+ import './chunk-4KSQKQXE.js';
44
44
  import './chunk-N6LURUEF.js';
45
45
  import './chunk-YRYMNBGQ.js';
46
- import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-XR3BJN33.js';
46
+ import { loadRunnerConfig, RunnerConfigError, checkHooks } from './chunk-22RMZBGW.js';
47
47
  import './chunk-UXYLWVTQ.js';
48
48
  import './chunk-7RQ73DLO.js';
49
49
  import './chunk-4Q7OMKAT.js';
50
50
  import './chunk-GQD22KKN.js';
51
51
  import './chunk-WZBHBH3J.js';
52
52
  import './chunk-5QRP3MVS.js';
53
- import './chunk-SZOVKFWB.js';
53
+ import './chunk-GI4XXD4I.js';
54
54
  import './chunk-ZJSNLVCE.js';
55
- import './chunk-FO7KKPYG.js';
56
- import './chunk-G5IEVZGI.js';
57
- import './chunk-AHZQBGD2.js';
55
+ import './chunk-TCIW27FM.js';
56
+ import './chunk-LCEXHCHD.js';
57
+ import './chunk-2M63GG56.js';
58
58
  import './chunk-YO4PSLVB.js';
59
59
  import './chunk-PLMTLGYH.js';
60
- import './chunk-325272HM.js';
60
+ import './chunk-J5M7BSJQ.js';
61
61
  import './chunk-EABW6AJQ.js';
62
62
  import './chunk-BYT5O5RV.js';
63
63
  import './chunk-RA3O54FC.js';
64
64
  import './chunk-V7NIVOFD.js';
65
65
  import './chunk-ZM2IMMYN.js';
66
- import './chunk-YEQL54G5.js';
66
+ import './chunk-SFFHGX3G.js';
67
67
  import './chunk-XCCLFVXM.js';
68
68
  import './chunk-BJ3KTMDH.js';
69
69
  import './chunk-VNVZDWO3.js';
@@ -47,6 +47,23 @@ function defaultRunnerConfigPath(home = homeFromEnv()) {
47
47
  function fail(configPath, key, reason) {
48
48
  throw new RunnerConfigError(`${configPath}: ${key} ${reason}`);
49
49
  }
50
+ var PLACEHOLDER = /[<>]/;
51
+ function runnerCredentialProblem(config) {
52
+ for (const field of ["url", "apiKey", "appId"]) {
53
+ const value = config[field];
54
+ if (value === void 0) return { field, reason: "is missing" };
55
+ if (typeof value !== "string") return { field, reason: "is not text" };
56
+ if (value.trim() === "") return { field, reason: "is empty" };
57
+ }
58
+ if (PLACEHOLDER.test(config.apiKey)) {
59
+ return { field: "apiKey", reason: "is still a <...> placeholder. Put the factory's runner key there: an API key made with the Runner preset" };
60
+ }
61
+ const appId = config.appId;
62
+ if (PLACEHOLDER.test(appId)) {
63
+ return { field: "appId", reason: `is still the placeholder ${JSON.stringify(appId)}. Put the Factory Id from the factory's Settings \u2192 General there` };
64
+ }
65
+ return void 0;
66
+ }
50
67
  function readConfigFile(configPath) {
51
68
  if (!existsSync(configPath)) return {};
52
69
  const text = readFileSync(configPath, "utf8");
@@ -171,6 +188,8 @@ function loadRunnerConfig(configPath) {
171
188
  if (!config.url || !config.apiKey || !config.appId) {
172
189
  fail(configPath, "url/apiKey/appId", "missing \u2014 pipe a factory key into `outerlayer login` for this config file first; a runner uses a factory key, never a person's login");
173
190
  }
191
+ const credentialProblem = runnerCredentialProblem(config);
192
+ if (credentialProblem !== void 0) fail(configPath, credentialProblem.field, credentialProblem.reason);
174
193
  const runner = config.runner;
175
194
  if (!runner) {
176
195
  fail(configPath, "runner", "block is missing");
@@ -379,4 +398,4 @@ function resolveBuild(configPath, block) {
379
398
  };
380
399
  }
381
400
 
382
- export { DEFAULT_CHECK_TIME_LIMIT_MINUTES, DEFAULT_UPDATE_MAX_WAIT_HOURS, RunnerConfigError, builtinKind, checkHooks, defaultRunnerConfigPath, hasRunnerBlock, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, loadRunnerConfig, resolveBuild, resolveHousekeeping, runnerVmOf };
401
+ export { DEFAULT_CHECK_TIME_LIMIT_MINUTES, DEFAULT_UPDATE_MAX_WAIT_HOURS, RunnerConfigError, builtinKind, checkHooks, defaultRunnerConfigPath, hasRunnerBlock, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, loadRunnerConfig, resolveBuild, resolveHousekeeping, runnerCredentialProblem, runnerVmOf };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { RESERVED_EMIT_NAMES, ARTIFACT_KINDS, ArtifactCriterionIdSchema } from './chunk-325272HM.js';
3
+ import { RESERVED_EMIT_NAMES, ARTIFACT_KINDS, ArtifactCriterionIdSchema } from './chunk-J5M7BSJQ.js';
4
4
  import { __commonJS, __require, __toESM } from './chunk-Z5GEFMRW.js';
5
5
 
6
6
  createRequire(import.meta.url);
@@ -4,17 +4,17 @@ import { LaunchFilter } from './chunk-HHJDN6AU.js';
4
4
  import { RepoFilter } from './chunk-TBT347UY.js';
5
5
  import { hookExecSpoolPath } from './chunk-F7CU5ABH.js';
6
6
  import { applyReportedCost } from './chunk-BKO6JEZI.js';
7
- import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, acquireArtifactSpoolLock, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-JOXYKGUV.js';
8
- import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-3ZPYPFNM.js';
7
+ import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, acquireArtifactSpoolLock, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-CNFVHDHG.js';
8
+ import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-JXYVA45C.js';
9
9
  import { C } from './chunk-YRYMNBGQ.js';
10
10
  import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-GQD22KKN.js';
11
11
  import { readLatestProvenanceForSession, readContextProvenance } from './chunk-ZJSNLVCE.js';
12
12
  import { defaultFetch } from './chunk-YO4PSLVB.js';
13
13
  import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-PLMTLGYH.js';
14
- import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-325272HM.js';
14
+ import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-J5M7BSJQ.js';
15
15
  import { readCloudConfig, cloudConfigPath, writeCloudConfig } from './chunk-EABW6AJQ.js';
16
16
  import { signerFor, signRunnerRequest } from './chunk-V7NIVOFD.js';
17
- import { writeSyncStatus, spoolDir } from './chunk-YEQL54G5.js';
17
+ import { writeSyncStatus, spoolDir } from './chunk-SFFHGX3G.js';
18
18
  import { removeJournalHooks } from './chunk-XCCLFVXM.js';
19
19
  import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
20
20
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { RecipeError } from './chunk-QOZKPLVJ.js';
4
- import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-QEESCZ26.js';
4
+ import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-NX57IBNK.js';
5
5
  import { spawn } from 'child_process';
6
6
  import { existsSync, mkdirSync, createWriteStream, rmSync, readFileSync } from 'fs';
7
7
  import { join, dirname } from 'path';
@@ -3,6 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { revokeTokenOnDashboard } from './chunk-N6LURUEF.js';
4
4
  import { C } from './chunk-YRYMNBGQ.js';
5
5
  import { readCloudConfig, writeCloudConfig, cloudConfigPath } from './chunk-EABW6AJQ.js';
6
+ import { findGitDirs } from './chunk-BJ3KTMDH.js';
6
7
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
7
8
  import { generateKeyPairSync, privateDecrypt, constants, createPrivateKey } from 'crypto';
8
9
  import { spawn } from 'child_process';
@@ -269,14 +270,19 @@ async function finishApproved(flags, deps, home, dashboard, approved, privateKey
269
270
  }
270
271
  deps.stdout.write(
271
272
  `${C.green}\u2713${C.reset} Logged in as ${approved.email}. Saved the login to ${path}
272
- Next: run \`outerlayer connect\` to choose a factory.
273
- `
273
+ ` + nextAfterLogin(deps.cwd ?? process.cwd())
274
274
  );
275
275
  if (before.apiKey) {
276
276
  deps.stdout.write("A factory key is also saved on this machine, so commands here keep using the key, not this login.\n");
277
277
  }
278
278
  return 0;
279
279
  }
280
+ function nextAfterLogin(cwd) {
281
+ const dirs = findGitDirs(cwd);
282
+ if (dirs === void 0) return "Next: in your repository, run `outerlayer init --factory <org>/<factory>`.\n";
283
+ if (existsSync(join(dirs.repoRoot, ".outerlayer"))) return "Next: run `outerlayer connect` to choose a factory.\n";
284
+ return "Next: run `outerlayer init --factory <org>/<factory>` to set up this repository.\n";
285
+ }
280
286
  function fail(deps, message) {
281
287
  deps.stderr.write(`${C.red}\u2717${C.reset} ${message}
282
288
  `);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { sanitizeTerminal } from './chunk-JOXYKGUV.js';
4
- import { remoteLookupFailed } from './chunk-3ZPYPFNM.js';
5
- import { DEFAULT_DASHBOARD } from './chunk-USO2DKBD.js';
3
+ import { sanitizeTerminal } from './chunk-CNFVHDHG.js';
4
+ import { remoteLookupFailed } from './chunk-JXYVA45C.js';
5
+ import { DEFAULT_DASHBOARD } from './chunk-4KSQKQXE.js';
6
6
  import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
7
7
  import { readCloudConfig } from './chunk-EABW6AJQ.js';
8
8
  import { writeRepoConnection, findGitDirs, readRepoConnection } from './chunk-BJ3KTMDH.js';
@@ -181,7 +181,7 @@ function saveFactoryConnection(gitCommonDir, saved, factory, now) {
181
181
  });
182
182
  }
183
183
  function connectedLine(repository, factory, reason) {
184
- return `Connected ${repository} to factory "${factory.displayName}" in ${factory.organization.displayName} (${REASON_TEXT[reason]}).`;
184
+ return `Connected ${repository} to factory "${factory.displayName}" (${slug(factory)}) in ${factory.organization.displayName} (${REASON_TEXT[reason]}).`;
185
185
  }
186
186
  function autoConnectedLine(repository, factory, reason) {
187
187
  return `Using factory "${factory.displayName}" in ${factory.organization.displayName} for ${repository} (${REASON_TEXT[reason]}); saved for this checkout.`;
@@ -310,4 +310,4 @@ ${COMMIT_LINE}
310
310
  return r.code;
311
311
  }
312
312
 
313
- export { COMMIT_LINE, autoConnectedLine, fetchFactories, isLinkedRepository, promptOnTerminal, realConnectDeps, runConnect, runConnectCommand, saveFactoryConnection, selectFactory };
313
+ export { COMMIT_LINE, autoConnectedLine, fetchFactories, isLinkedRepository, promptOnTerminal, realConnectDeps, runConnect, runConnectCommand, saveFactoryConnection, selectFactory, workPageUrl };
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { autoConnect } from './chunk-GATMPAMK.js';
3
+ import { autoConnect } from './chunk-EFZLAYPE.js';
4
4
  import { detectActiveSession } from './chunk-IP7BIBXJ.js';
5
- import { resolveRepoIdentity } from './chunk-3ZPYPFNM.js';
5
+ import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
6
6
  import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
7
7
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
8
8
  import { defaultFetch } from './chunk-YO4PSLVB.js';
@@ -1,23 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { writeJobFile, readJobFile } from './chunk-T6OIJQRT.js';
4
- import { senderPidfile } from './chunk-5TGQHOUX.js';
5
- import { installScrubContext } from './chunk-27Y7AX2O.js';
3
+ import { writeJobFile, readJobFile } from './chunk-RMWLBUCJ.js';
4
+ import { senderPidfile } from './chunk-XJKL4BNU.js';
5
+ import { installScrubContext } from './chunk-3D4OYFCD.js';
6
6
  import { DOCKERD_LOG } from './chunk-HYZRY4KZ.js';
7
- import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, formatDuration, retrySend, formatClock, containerSenderCommand, syncInContainer, copyTranscriptsFromContainer, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, EVAL_INPUT_NAME, copyTranscripts, senderSignalScript } from './chunk-SKHN72JO.js';
7
+ import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, formatDuration, retrySend, formatClock, containerSenderCommand, syncInContainer, copyTranscriptsFromContainer, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, EVAL_INPUT_NAME, copyTranscripts, senderSignalScript } from './chunk-MZN7AQOB.js';
8
8
  import { readBranchHead, diffBetween, fetchCommit } from './chunk-3FMPZPJQ.js';
9
9
  import { isSameProcess, startTimeOf } from './chunk-N5FOE5PS.js';
10
- import { childEnvironment } from './chunk-GGMRZPEA.js';
10
+ import { childEnvironment } from './chunk-3KTA5UU2.js';
11
11
  import { CLAUDE_CODE_VERSION } from './chunk-CX22S5BA.js';
12
12
  import { waitForAgent, vmExecArgs } from './chunk-QKFB4K7X.js';
13
13
  import { socketPathProblem, vsockLinks, linkVsockSockets } from './chunk-RSNSZ2LJ.js';
14
- import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-6FH2AHJY.js';
15
- import { planArtifactUpload } from './chunk-JOXYKGUV.js';
16
- import { scrubText } from './chunk-3ZPYPFNM.js';
17
- import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-XR3BJN33.js';
14
+ import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-P4MM66C6.js';
15
+ import { planArtifactUpload } from './chunk-CNFVHDHG.js';
16
+ import { scrubText } from './chunk-JXYVA45C.js';
17
+ import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-22RMZBGW.js';
18
18
  import { parseSize } from './chunk-7RQ73DLO.js';
19
19
  import { claudeCredential, repositoriesOf, primaryOf, buildEnvironment, JOB_MOUNT, CHECKOUT_DIR, parseIsolation, proxyEnvironment, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, withTunnelRecord, RELAY_PORT, checkEnvironment, EVAL_INPUT_DIR } from './chunk-4Q7OMKAT.js';
20
- import { parsePolicy } from './chunk-AHZQBGD2.js';
20
+ import { parsePolicy } from './chunk-2M63GG56.js';
21
21
  import { readCloudConfig } from './chunk-EABW6AJQ.js';
22
22
  import { withHooksPath, authorEnvironment, commitIdentityRecord } from './chunk-BYT5O5RV.js';
23
23
  import { resolveVersion } from './chunk-RA3O54FC.js';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { ArtifactTombstoneRecordSchema, ArtifactSpoolRecordSchema } from './chunk-325272HM.js';
3
+ import { ArtifactTombstoneRecordSchema, ArtifactSpoolRecordSchema } from './chunk-J5M7BSJQ.js';
4
4
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
5
  import { mkdirSync, appendFileSync, writeFileSync, renameSync, unlinkSync, existsSync, readdirSync, readFileSync, statSync } from 'fs';
6
6
  import { join, dirname } from 'path';
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { readStatuslineState, STATE_FRESH_MS } from './chunk-F47JBIAW.js';
4
- import { resolveCredentials, getJson, WorkCommandError } from './chunk-6FH2AHJY.js';
5
- import { isLinkedRepository } from './chunk-C7N4TDHW.js';
6
- import { sanitizeTerminal } from './chunk-JOXYKGUV.js';
7
- import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-3ZPYPFNM.js';
4
+ import { resolveCredentials, getJson, WorkCommandError } from './chunk-P4MM66C6.js';
5
+ import { isLinkedRepository } from './chunk-4QVZCVWT.js';
6
+ import { sanitizeTerminal } from './chunk-CNFVHDHG.js';
7
+ import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-JXYVA45C.js';
8
8
  import { C } from './chunk-YRYMNBGQ.js';
9
9
  import { readSettings, settingsPath, isOurStatuslineSlot, statuslineWrappedOriginal, REGISTERED_EVENTS, isOurHookCommand, ourCommandBins, cliBinResolvable, ourDirectScriptBins, wrappedOriginalCommand } from './chunk-5QRP3MVS.js';
10
- import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-G5IEVZGI.js';
10
+ import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-LCEXHCHD.js';
11
11
  import { defaultFetch } from './chunk-YO4PSLVB.js';
12
12
  import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
13
13
  import { readCloudConfig } from './chunk-EABW6AJQ.js';
14
- import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-YEQL54G5.js';
14
+ import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-SFFHGX3G.js';
15
15
  import { deadHooksState, preparePackageManagerCommand, contextExcludesPresent } from './chunk-XCCLFVXM.js';
16
16
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
17
17
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
18
- import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, accessSync, constants, statSync, readdirSync } from 'fs';
19
- import { join, dirname } from 'path';
18
+ import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, accessSync, constants, statSync, readdirSync, realpathSync } from 'fs';
19
+ import { join, dirname, resolve, delimiter } from 'path';
20
20
  import { execFileSync } from 'child_process';
21
21
 
22
22
  createRequire(import.meta.url);
@@ -113,6 +113,57 @@ function recordStatusline(home, settingsPath2, outcome, original, now = Date.now
113
113
  };
114
114
  });
115
115
  }
116
+ var NAME = "MCP server";
117
+ var SERVER = "outerlayer";
118
+ var NPX_FIX = 'Change the "outerlayer" entry in .mcp.json to run `npx -y @outerlayer/cli mcp serve`, or install the CLI with `npm install -g @outerlayer/cli`.';
119
+ function expand(value, env) {
120
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? "");
121
+ }
122
+ function isExecutableFile(path, platform) {
123
+ try {
124
+ if (!statSync(path).isFile()) return false;
125
+ if (platform !== "win32") accessSync(path, constants.X_OK);
126
+ return true;
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+ function resolveCommand(command, root, env, platform) {
132
+ if (/[\\/]/.test(command)) {
133
+ const path = resolve(root, command);
134
+ return isExecutableFile(path, platform) ? path : void 0;
135
+ }
136
+ const exts = platform === "win32" ? ["", ...(env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
137
+ for (const dir of (env.PATH ?? "").split(delimiter)) {
138
+ for (const ext of exts) {
139
+ const candidate = join(dir, command + ext);
140
+ if (isExecutableFile(candidate, platform)) return candidate;
141
+ }
142
+ }
143
+ return void 0;
144
+ }
145
+ function checkMcpCommand(cwd, env = process.env, platform = process.platform) {
146
+ const root = findGitDirs(cwd ?? process.cwd())?.repoRoot;
147
+ if (root === void 0) return [];
148
+ const path = join(root, ".mcp.json");
149
+ if (!existsSync(path)) return [];
150
+ let entry;
151
+ try {
152
+ entry = JSON.parse(readFileSync(path, "utf8")).mcpServers?.[SERVER];
153
+ } catch {
154
+ return [{ name: NAME, status: "warn", detail: ".mcp.json is not valid JSON, so the agent starts none of its servers", fix: "Fix the JSON syntax in .mcp.json." }];
155
+ }
156
+ const command = entry?.command;
157
+ if (typeof command !== "string") return [];
158
+ const shown = sanitizeTerminal(command);
159
+ const resolved = resolveCommand(expand(command, env), root, env, platform);
160
+ if (resolved === void 0) {
161
+ return [{ name: NAME, status: "warn", detail: `the "outerlayer" server runs \`${shown}\`, which is not on PATH here, so the agent has no OuterLayer tools`, fix: NPX_FIX }];
162
+ }
163
+ return [{ name: NAME, status: "pass", detail: `the "outerlayer" server runs ${sanitizeTerminal(resolved)}` }];
164
+ }
165
+
166
+ // src/doctor.ts
116
167
  var LAPTOP_CHECKS_NOT_APPLICABLE = {
117
168
  name: "Laptop checks",
118
169
  status: "skipped",
@@ -135,6 +186,7 @@ function runDoctor(env = {}) {
135
186
  checkSettingsValid(home, env.cwd),
136
187
  ...laptop ? checkStatusline(home, now, env.cwd) : [],
137
188
  ...checkInstalls(env),
189
+ ...laptop ? checkMcpCommand(env.cwd, env.processEnv) : [],
138
190
  ...checkContextSource(home, env.cwd),
139
191
  ...checkInstalledCli(home, env.cwd ?? process.cwd())
140
192
  ];
@@ -462,7 +514,9 @@ function checkDeadHooksState(cwd) {
462
514
  function checkSpoolWritable(home) {
463
515
  const dir = spoolDir(home);
464
516
  try {
465
- if (!existsSync(dir)) return { name: "Spool writable", status: "warn", detail: "spool not created yet", fix: "Run `outerlayer init` (creates the spool on first hook)." };
517
+ if (!existsSync(dir)) {
518
+ return { name: "Spool writable", status: "warn", detail: "spool not created yet", fix: "The first hook run creates it: start a Claude Code session in the repository." };
519
+ }
466
520
  accessSync(dir, constants.W_OK);
467
521
  return { name: "Spool writable", status: "pass", detail: dir };
468
522
  } catch {
@@ -561,10 +615,25 @@ function checkSettingsValid(home, cwd) {
561
615
  }
562
616
  return { name: "Settings JSON valid", status: "pass", detail: "all settings files parse" };
563
617
  }
618
+ function distinctInstalls(paths) {
619
+ const seen = /* @__PURE__ */ new Set();
620
+ const out = [];
621
+ for (const path of paths) {
622
+ let real = path;
623
+ try {
624
+ real = realpathSync(path);
625
+ } catch {
626
+ }
627
+ if (seen.has(real)) continue;
628
+ seen.add(real);
629
+ out.push(path);
630
+ }
631
+ return out;
632
+ }
564
633
  function checkInstalls(env) {
565
634
  let installs;
566
635
  try {
567
- installs = (env.claudeInstalls ?? defaultClaudeInstalls)();
636
+ installs = distinctInstalls((env.claudeInstalls ?? defaultClaudeInstalls)());
568
637
  } catch {
569
638
  return [];
570
639
  }
@@ -708,7 +777,14 @@ async function githubAppChecks(options = {}) {
708
777
  return [{ name: "GitHub App", status: "warn", detail: "the gateway's answer listed no repositories", fix: "Update the outerlayer CLI and the gateway to the same release." }];
709
778
  }
710
779
  if (data.length === 0) {
711
- return [{ name: "GitHub App", status: "pass", detail: "the factory has no connected repositories" }];
780
+ return [
781
+ {
782
+ name: "GitHub App",
783
+ status: "warn",
784
+ detail: "the factory has no connected repositories, so it cannot build or check any work",
785
+ fix: `On the factory's Work page, choose Connect GitHub repository, then Link repository: ${CONNECT_REPOSITORY_DOCS}`
786
+ }
787
+ ];
712
788
  }
713
789
  return data.map(repositoryAccessCheck);
714
790
  }
@@ -1004,7 +1080,7 @@ function doctorExitCode(checks) {
1004
1080
 
1005
1081
  // src/doctor-report.ts
1006
1082
  async function gatherDoctorChecks() {
1007
- const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-MKBX2ZQD.js');
1083
+ const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-N3C6DFQA.js');
1008
1084
  const configPath = defaultRunnerConfigPath();
1009
1085
  const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
1010
1086
  const credential = await credentialChecks();
@@ -1016,7 +1092,7 @@ async function gatherDoctorChecks() {
1016
1092
  const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
1017
1093
  checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
1018
1094
  } else if (hasRunnerBlock(configPath)) {
1019
- const { runnerHostChecks } = await import('./doctor-4FFXTQJ2.js');
1095
+ const { runnerHostChecks } = await import('./doctor-3H25Q7WT.js');
1020
1096
  const hooks = (() => {
1021
1097
  try {
1022
1098
  return loadRunnerConfig(configPath).hooks;
@@ -1048,10 +1124,10 @@ async function gatherDoctorChecks() {
1048
1124
  }
1049
1125
  })();
1050
1126
  const service = await (async () => {
1051
- const { existsSync: existsSync2 } = await import('fs');
1052
- if (process.platform !== "linux" || !existsSync2("/run/systemd/system")) return void 0;
1127
+ const { existsSync: existsSync3 } = await import('fs');
1128
+ if (process.platform !== "linux" || !existsSync3("/run/systemd/system")) return void 0;
1053
1129
  const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
1054
- const { DEFAULT_UNIT_NAME } = await import('./install-VXXI7UR6.js');
1130
+ const { DEFAULT_UNIT_NAME } = await import('./install-HDBIPHTX.js');
1055
1131
  const { dirname: dirname2 } = await import('path');
1056
1132
  return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
1057
1133
  })();
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { C } from './chunk-YRYMNBGQ.js';
4
- import { resolvePackRoot, loadTemplateFiles, loadMaintainedFiles, TEMPLATE_SKILLS, skillDir, writeMaintainedFiles, MAINTAINED_SKILLS } from './chunk-FO7KKPYG.js';
4
+ import { resolvePackRoot, loadTemplateFiles, loadMaintainedFiles, TEMPLATE_SKILLS, skillDir, writeMaintainedFiles, MAINTAINED_SKILLS } from './chunk-TCIW27FM.js';
5
5
  import { resolvePackageVersion } from './chunk-RA3O54FC.js';
6
6
  import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
7
7
  import { join, dirname } from 'path';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { fetchFactories, selectFactory, saveFactoryConnection, autoConnectedLine } from './chunk-C7N4TDHW.js';
4
- import { resolveRepoIdentity } from './chunk-3ZPYPFNM.js';
3
+ import { fetchFactories, selectFactory, saveFactoryConnection, autoConnectedLine } from './chunk-4QVZCVWT.js';
4
+ import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
5
5
  import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
6
6
  import { findGitDirs, readRepoConnection } from './chunk-BJ3KTMDH.js';
7
7
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { appendContextProvenance } from './chunk-ZJSNLVCE.js';
4
- import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-FO7KKPYG.js';
5
- import { readPin, pinExists, readBuildHold, readContextManifest, manifestFilesPresent, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, isLastCheckFresh, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-G5IEVZGI.js';
6
- import { normalizeRepoName, parseGoverns, CONTEXT_SOURCE_CONFIG_PATH, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-AHZQBGD2.js';
4
+ import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-TCIW27FM.js';
5
+ import { readPin, pinExists, readBuildHold, readContextManifest, manifestFilesPresent, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, isLastCheckFresh, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-LCEXHCHD.js';
6
+ import { normalizeRepoName, parseGoverns, CONTEXT_SOURCE_CONFIG_PATH, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-2M63GG56.js';
7
7
  import { defaultFetch } from './chunk-YO4PSLVB.js';
8
8
  import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
9
9
  import { withTokenHeader } from './chunk-BYT5O5RV.js';