@outerlayer/cli 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +16 -7
  3. package/dist/{agent-setup-YEFN74Y4.js → agent-setup-QE6PSCEZ.js} +8 -8
  4. package/dist/build-info.json +1 -1
  5. package/dist/{builder-PP436VGZ.js → builder-UUS4ORDH.js} +3 -3
  6. package/dist/{check-YDXOLVJ5.js → check-63IDCEEV.js} +31 -30
  7. package/dist/{chunk-XR3BJN33.js → chunk-22RMZBGW.js} +20 -1
  8. package/dist/{chunk-TB7EP24C.js → chunk-2M63GG56.js} +1 -1
  9. package/dist/{chunk-Y6P5XX2B.js → chunk-3D4OYFCD.js} +4 -4
  10. package/dist/{chunk-2GBCTEN4.js → chunk-3KTA5UU2.js} +1 -1
  11. package/dist/{chunk-USO2DKBD.js → chunk-4KSQKQXE.js} +8 -2
  12. package/dist/{chunk-UES67HLJ.js → chunk-4QVZCVWT.js} +5 -5
  13. package/dist/{chunk-I263FZ3A.js → chunk-6S3H3J6H.js} +3 -3
  14. package/dist/{chunk-BHOBM3IZ.js → chunk-C6IV5BV7.js} +10 -10
  15. package/dist/{chunk-VDI5POCC.js → chunk-CNFVHDHG.js} +1 -1
  16. package/dist/{chunk-EOWWTFUU.js → chunk-DPKX26LT.js} +92 -16
  17. package/dist/{chunk-SUMWRA7K.js → chunk-DXQ5MHCO.js} +1 -1
  18. package/dist/{chunk-ZQ5JGHUO.js → chunk-EFZLAYPE.js} +2 -2
  19. package/dist/{chunk-RWQWPZB4.js → chunk-GI4XXD4I.js} +3 -3
  20. package/dist/{chunk-YU5GL7QS.js → chunk-HDTVTA6I.js} +2 -2
  21. package/dist/{chunk-BFESLKPP.js → chunk-IP7BIBXJ.js} +19 -1
  22. package/dist/{chunk-YT35WGIP.js → chunk-J5M7BSJQ.js} +99 -7
  23. package/dist/{chunk-IU4NZROR.js → chunk-JXYVA45C.js} +1 -1
  24. package/dist/{chunk-BMX3L7CR.js → chunk-LCEXHCHD.js} +1 -1
  25. package/dist/{chunk-7C4T5BGN.js → chunk-MJXXRX4M.js} +54 -12
  26. package/dist/{chunk-E5DC4VZL.js → chunk-MZN7AQOB.js} +8 -8
  27. package/dist/{chunk-CJGQQZWB.js → chunk-NX57IBNK.js} +1 -1
  28. package/dist/{chunk-DTDZXDQ3.js → chunk-P4MM66C6.js} +31 -18
  29. package/dist/{chunk-BEZSECPA.js → chunk-P6FX4D43.js} +1 -1
  30. package/dist/{chunk-A56PYPDF.js → chunk-R7OQTKWT.js} +2 -1
  31. package/dist/{chunk-TFIOXPZT.js → chunk-RLYZLUQ7.js} +67 -5
  32. package/dist/chunk-RMWLBUCJ.js +50 -0
  33. package/dist/{chunk-4HMXH2OA.js → chunk-SFFHGX3G.js} +1 -1
  34. package/dist/{chunk-3YFCWYBU.js → chunk-SVEP7PVH.js} +14 -14
  35. package/dist/chunk-T5VPP6H7.js +65 -0
  36. package/dist/{chunk-BGWTYRSS.js → chunk-TCIW27FM.js} +1 -1
  37. package/dist/{chunk-R7BJYFD2.js → chunk-TOPDNXHZ.js} +5 -5
  38. package/dist/{chunk-HKUH5AR2.js → chunk-XJKL4BNU.js} +3 -3
  39. package/dist/{cli-RQZXTOEI.js → cli-WSBIAVZU.js} +176 -88
  40. package/dist/{command-U6UGIBVT.js → command-N7BAB4XJ.js} +21 -22
  41. package/dist/{config-MKBX2ZQD.js → config-N3C6DFQA.js} +1 -1
  42. package/dist/{connect-cmd-5MIUNYAY.js → connect-cmd-FGYRLLXL.js} +5 -5
  43. package/dist/{context-adopt-A77G67CP.js → context-adopt-B3PWGFHL.js} +5 -5
  44. package/dist/{context-materialize-KQZYATQZ.js → context-materialize-7S6NLLRO.js} +5 -5
  45. package/dist/{dist-3M3ZMEZF.js → dist-LKDWQWZ7.js} +2 -2
  46. package/dist/{docker-WVRRMK6R.js → docker-BZ55BMBJ.js} +4 -4
  47. package/dist/{doctor-ZF4PYWVN.js → doctor-3H25Q7WT.js} +23 -20
  48. package/dist/{emit-artifact-cmd-VKPW577P.js → emit-artifact-cmd-IQCRXOH7.js} +9 -9
  49. package/dist/{emit-cmd-SU62J2PB.js → emit-cmd-KDSM5VAO.js} +4 -4
  50. package/dist/{emit-criteria-cmd-U2TL6FYY.js → emit-criteria-cmd-EYUXME26.js} +7 -7
  51. package/dist/emit-finding-cmd-P4RKBPBB.js +465 -0
  52. package/dist/{emit-result-cmd-GKBVPMU4.js → emit-result-cmd-6XYENU6H.js} +9 -9
  53. package/dist/{hook-fast-AQCAQIA6.js → hook-fast-PXWKQPN6.js} +1 -1
  54. package/dist/{import-capture-cmd-5NJVL2HN.js → import-capture-cmd-2BKM5I2N.js} +3 -3
  55. package/dist/{import-ruler-cmd-TUK3MRPC.js → import-ruler-cmd-IXR7YK47.js} +2 -2
  56. package/dist/index.js +3 -3
  57. package/dist/{init-PUY2BUHH.js → init-2LWVS4AH.js} +21 -17
  58. package/dist/{init-M5IBAGVE.js → init-ZL7ORPTF.js} +53 -32
  59. package/dist/{init-cmd-NPNR23UL.js → init-cmd-JXOBCOIB.js} +16 -14
  60. package/dist/{install-HKOVISUP.js → install-HDBIPHTX.js} +2 -4
  61. package/dist/{login-browser-7XGSV7MA.js → login-browser-QZCS3WW6.js} +2 -1
  62. package/dist/{loop-3FNROLW7.js → loop-XOAE6ZIY.js} +32 -30
  63. package/dist/{machine-RWDDJRWB.js → machine-G6PY2J2J.js} +16 -16
  64. package/dist/{mcp-install-cmd-XZARXNYT.js → mcp-install-cmd-VXVIH7PH.js} +1 -1
  65. package/dist/runner-env-XTC53CHN.js +15 -0
  66. package/dist/runner-key-TDTLNAKJ.js +154 -0
  67. package/dist/{session-sender-E3UHGSNV.js → session-sender-XVGZFWRK.js} +7 -7
  68. package/dist/{starter-pack-I35VNYAR.js → starter-pack-B7JPGEDO.js} +4 -4
  69. package/dist/{status-2OW5AKWL.js → status-MRTPG34B.js} +22 -22
  70. package/dist/{sync-cmd-ULKXCHO2.js → sync-cmd-UBUZMMDN.js} +6 -6
  71. package/dist/{watch-IU6NHB4K.js → watch-STNPEWD6.js} +6 -6
  72. package/dist/{work-claim-cmd-LXXSNEGF.js → work-claim-cmd-CWGYEIJE.js} +11 -11
  73. package/dist/{work-cmd-ZJY3NA2G.js → work-cmd-X7NMJDFL.js} +8 -8
  74. package/dist/{work-comment-cmd-34FTLPUO.js → work-comment-cmd-FI2TMAYQ.js} +8 -8
  75. package/dist/{work-open-pr-cmd-YZAENDP6.js → work-open-pr-cmd-OVHJ5I5H.js} +9 -9
  76. package/dist/{work-pr-cmd-EXTOGBJC.js → work-pr-cmd-Y5TDMQ4O.js} +8 -8
  77. package/package.json +1 -1
  78. package/skill-pack/maintained/reporting-findings/SKILL.md +84 -5
  79. package/skill-pack/template/build/SKILL.md +60 -33
  80. package/skill-pack/template/build/references/agent-briefs.md +4 -2
  81. package/skill-pack/template/build/references/release.md +1 -1
  82. package/skill-pack/template/build/references/review-loop.md +6 -2
  83. package/skill-pack/template/build/scripts/record-criteria.mjs +109 -143
  84. package/skill-pack/validators/code-review.yaml +4 -2
  85. package/dist/chunk-FBTE2A2X.js +0 -38
  86. package/dist/chunk-JKIWMRFH.js +0 -27
  87. package/dist/chunk-T6OIJQRT.js +0 -31
  88. package/dist/emit-finding-cmd-TMOOKHPD.js +0 -251
@@ -1,29 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-EOWWTFUU.js';
3
+ import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-DPKX26LT.js';
4
4
  import './chunk-F47JBIAW.js';
5
5
  import './chunk-BKO6JEZI.js';
6
- import './chunk-DTDZXDQ3.js';
7
- import './chunk-ZQ5JGHUO.js';
8
- import './chunk-BFESLKPP.js';
9
- import './chunk-UES67HLJ.js';
10
- import './chunk-VDI5POCC.js';
11
- import './chunk-IU4NZROR.js';
12
- import './chunk-USO2DKBD.js';
6
+ import './chunk-P4MM66C6.js';
7
+ import './chunk-EFZLAYPE.js';
8
+ import './chunk-IP7BIBXJ.js';
9
+ import './chunk-4QVZCVWT.js';
10
+ import './chunk-CNFVHDHG.js';
11
+ import './chunk-JXYVA45C.js';
12
+ import './chunk-4KSQKQXE.js';
13
13
  import './chunk-N6LURUEF.js';
14
14
  import { C, NO_COLOR_FLAG, warnDeprecated, stripNoColor, API_KEY_FLAG_DEPRECATION } from './chunk-YRYMNBGQ.js';
15
15
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
16
16
  import { settingsPath, readSettings, SettingsParseError, listHookCandidates, wrapHooks, writeSettings, unwrapHooks, REGISTERED_EVENTS, cliCommand, removeHooks, removeStatusline, cliBinResolvable, mergeHooks, repointWrappedHooks, AUTO_WRAP_EVENTS, mergeStatusline } from './chunk-5QRP3MVS.js';
17
- import { pinExists } from './chunk-BMX3L7CR.js';
18
- import './chunk-TB7EP24C.js';
17
+ import { pinExists } from './chunk-LCEXHCHD.js';
18
+ import './chunk-2M63GG56.js';
19
19
  import './chunk-YO4PSLVB.js';
20
20
  import './chunk-PLMTLGYH.js';
21
- import './chunk-YT35WGIP.js';
21
+ import './chunk-J5M7BSJQ.js';
22
22
  import './chunk-EABW6AJQ.js';
23
23
  import { resolveVersion } from './chunk-RA3O54FC.js';
24
24
  import './chunk-V7NIVOFD.js';
25
25
  import './chunk-ZM2IMMYN.js';
26
- import { hookFiredMarkerPath, installedCliDistDir } from './chunk-4HMXH2OA.js';
26
+ import { hookFiredMarkerPath, installedCliDistDir } from './chunk-SFFHGX3G.js';
27
27
  import { managedHookScripts, removeJournalHooks, installPreCommitGuardHook, removePreCommitGuardHook } from './chunk-XCCLFVXM.js';
28
28
  import './chunk-BJ3KTMDH.js';
29
29
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
@@ -3617,7 +3617,7 @@ function resolveCliBin() {
3617
3617
  return "outerlayer";
3618
3618
  }
3619
3619
  }
3620
- var SUPPORT_TEXT = "\nDocs: https://github.com/outerlayerai/outerlayer#readme\nIssues: https://github.com/outerlayerai/outerlayer/issues\n";
3620
+ var SUPPORT_TEXT = "\nDocs: https://docs.outerlayer.ai\nIssues: https://github.com/outerlayerai/outerlayer/issues\n";
3621
3621
  function fail(message) {
3622
3622
  process.stderr.write(`${C.red}\u2717${C.reset} ${message}
3623
3623
  `);
@@ -3641,7 +3641,7 @@ function warnIfApiKeyFlag(opts) {
3641
3641
  if (opts.apiKey !== void 0) warnDeprecated(API_KEY_FLAG_DEPRECATION);
3642
3642
  }
3643
3643
  async function installStarterPackInRepo() {
3644
- const { installStarterPack, formatStarterPackOutput } = await import('./starter-pack-I35VNYAR.js');
3644
+ const { installStarterPack, formatStarterPackOutput } = await import('./starter-pack-B7JPGEDO.js');
3645
3645
  const cwd = process.cwd();
3646
3646
  let repoRoot = cwd;
3647
3647
  try {
@@ -3801,8 +3801,8 @@ ${starter.output}`),
3801
3801
  }
3802
3802
  }
3803
3803
  async function realConnectDepsForCwd() {
3804
- const { realConnectDeps } = await import('./connect-cmd-5MIUNYAY.js');
3805
- const { resolveRepoIdentity } = await import('./dist-3M3ZMEZF.js');
3804
+ const { realConnectDeps } = await import('./connect-cmd-FGYRLLXL.js');
3805
+ const { resolveRepoIdentity } = await import('./dist-LKDWQWZ7.js');
3806
3806
  return {
3807
3807
  ...realConnectDeps(),
3808
3808
  home: homeFromEnv(),
@@ -3824,7 +3824,7 @@ function buildProgram(argv = process.argv) {
3824
3824
  ).action(async (opts) => {
3825
3825
  let starterTemplate;
3826
3826
  if (opts.template !== void 0) {
3827
- const { assertKnownTemplate, StarterPackError } = await import('./starter-pack-I35VNYAR.js');
3827
+ const { assertKnownTemplate, StarterPackError } = await import('./starter-pack-B7JPGEDO.js');
3828
3828
  try {
3829
3829
  assertKnownTemplate(opts.template);
3830
3830
  } catch (err) {
@@ -3855,7 +3855,7 @@ function buildProgram(argv = process.argv) {
3855
3855
  else process.stdout.write(outcome.text);
3856
3856
  return;
3857
3857
  }
3858
- const { runInitCommand } = await import('./init-cmd-NPNR23UL.js');
3858
+ const { runInitCommand } = await import('./init-cmd-JXOBCOIB.js');
3859
3859
  const code = await runInitCommand(
3860
3860
  { json: opts.json, factory: opts.factory },
3861
3861
  {
@@ -3867,7 +3867,7 @@ function buildProgram(argv = process.argv) {
3867
3867
  return { ok, text, detail };
3868
3868
  },
3869
3869
  runAgentSetup: async () => {
3870
- const { runAgentSetup } = await import('./agent-setup-YEFN74Y4.js');
3870
+ const { runAgentSetup } = await import('./agent-setup-QE6PSCEZ.js');
3871
3871
  return runAgentSetup({ cwd: process.cwd() });
3872
3872
  },
3873
3873
  runDoctorChecks: gatherDoctorChecks
@@ -3879,7 +3879,7 @@ function buildProgram(argv = process.argv) {
3879
3879
  "after",
3880
3880
  "\nRun it in a repository, after `outerlayer login`. With a login, connect lists\nthe factories you can use and takes the first rule that leaves one answer:\n 1. --factory <org>/<factory>, or a factory id\n 2. the factory this repository is already connected to\n 3. the one factory this repository is linked to\n 4. the one factory you can use\n 5. on a terminal, ask; with no terminal, exit 1 and list each --factory value\n\nIt says what it chose and why. The choice is saved in this repository's git\ndirectory (outerlayer/connection.json), shared by every worktree of the\nclone and never committed. A factory key already belongs to one factory, so\nconnect changes nothing for it. Connect never links a repository.\n\nAfter it saves the choice, connect checks that this repository is linked to\nthe factory. If it is not, connect says so, prints the factory's Work page\nlink where you link it, and exits 2. The choice is saved either way. Run\nconnect again once the repository is linked. With --json, the document has\nlinked (true, false, or null when it could not be checked) and, when false,\nlinkUrl.\n"
3881
3881
  ).action(async (opts) => {
3882
- const { runConnectCommand } = await import('./connect-cmd-5MIUNYAY.js');
3882
+ const { runConnectCommand } = await import('./connect-cmd-FGYRLLXL.js');
3883
3883
  const code = await runConnectCommand({ factory: opts.factory, json: opts.json }, await realConnectDepsForCwd(), {
3884
3884
  stdout: process.stdout,
3885
3885
  stderr: process.stderr
@@ -3899,7 +3899,7 @@ function buildProgram(argv = process.argv) {
3899
3899
  "after",
3900
3900
  '\nWith no key on stdin, login prints an approve link and a code. A signed-in\nperson approves it on the dashboard. The login is a token for that person\'s\naccount, made for this machine, saved here and never printed. It picks no\nfactory: run `outerlayer connect` for that. `outerlayer logout` revokes it.\n\nWhen stdout is not a terminal, login does not wait. It prints one JSON\ndocument with the link, the code and a `login --check <id>` command, and\nexits 75. Run the check until it stops exiting 75: 0 is signed in, 1 is\ndenied or expired. A dropped connection or a 5xx also exits 75.\n\nA factory key piped on stdin is saved as it always was, never as a flag and\nnever printed back. A factory key always wins over the login, so CI and\nrunners keep acting as their key.\n\n echo "$OUTERLAYER_KEY" | outerlayer login --url <gateway url> --app-id <factory id>\n\nEvery later command reads the saved file, so none of them needs a key on\nits command line. A saved key is checked for real on the first `sync`.\n'
3901
3901
  ).action(async (opts) => {
3902
- const { runLoginCommand, realLoginDeps } = await import('./login-browser-7XGSV7MA.js');
3902
+ const { runLoginCommand, realLoginDeps } = await import('./login-browser-QZCS3WW6.js');
3903
3903
  const code = await runLoginCommand(
3904
3904
  {
3905
3905
  browser: opts.browser !== false,
@@ -3927,7 +3927,7 @@ function buildProgram(argv = process.argv) {
3927
3927
  "\nIncremental: only sessions newer than the last checkpoint ship. The tier\nis applied on this machine before anything leaves it; `--dry-run` prints\nexactly what would. The hook-triggered background sync runs `--quiet`.\nExit 2 means the server rejected at least one session.\n"
3928
3928
  ).action(async (opts) => {
3929
3929
  warnIfApiKeyFlag(opts);
3930
- const { runSync, SyncConfigError, SyncTransportError } = await import('./sync-cmd-ULKXCHO2.js');
3930
+ const { runSync, SyncConfigError, SyncTransportError } = await import('./sync-cmd-UBUZMMDN.js');
3931
3931
  try {
3932
3932
  const result = await runSync({
3933
3933
  url: opts.url,
@@ -3956,7 +3956,7 @@ function buildProgram(argv = process.argv) {
3956
3956
  if (invokedCommandToken(argv) === "watch") {
3957
3957
  warnDeprecated("`outerlayer watch` is now `outerlayer daemon`; the old name will be removed in a future release.");
3958
3958
  }
3959
- const { runWatch } = await import('./watch-IU6NHB4K.js');
3959
+ const { runWatch } = await import('./watch-STNPEWD6.js');
3960
3960
  await runWatch({ once: opts.once });
3961
3961
  });
3962
3962
  program2.command("hook <event>", { hidden: true }).description("capture-hook fast path, installed by `init`").action(() => {
@@ -4010,7 +4010,7 @@ function buildProgram(argv = process.argv) {
4010
4010
  }
4011
4011
  });
4012
4012
  program2.command("session-sender", { hidden: true }).description("stream a runner build's sessions to its work item while the build runs").action(async () => {
4013
- const { runSessionSender } = await import('./session-sender-E3UHGSNV.js');
4013
+ const { runSessionSender } = await import('./session-sender-XVGZFWRK.js');
4014
4014
  try {
4015
4015
  await runSessionSender();
4016
4016
  } catch (err) {
@@ -4045,7 +4045,7 @@ async function runCli(processArgv) {
4045
4045
  await buildProgram(argv).parseAsync(argv);
4046
4046
  }
4047
4047
  async function compileContext(opts) {
4048
- const { runEmit, EmitError } = await import('./emit-cmd-SU62J2PB.js');
4048
+ const { runEmit, EmitError } = await import('./emit-cmd-KDSM5VAO.js');
4049
4049
  try {
4050
4050
  const result = runEmit({ cwd: opts.dir, check: opts.check, json: opts.json });
4051
4051
  if (result.exitCode !== 0) process.exitCode = result.exitCode;
@@ -4068,7 +4068,7 @@ function registerEmitCommands(program2) {
4068
4068
  return;
4069
4069
  }
4070
4070
  warnIfApiKeyFlag(opts);
4071
- const { runEmitResult, EmitResultError } = await import('./emit-result-cmd-GKBVPMU4.js');
4071
+ const { runEmitResult, EmitResultError } = await import('./emit-result-cmd-6XYENU6H.js');
4072
4072
  try {
4073
4073
  const result = await runEmitResult({
4074
4074
  name,
@@ -4119,7 +4119,7 @@ locally so it never uploads. One correction can retire several duplicates.
4119
4119
  `
4120
4120
  ).action(async (file, opts) => {
4121
4121
  warnIfApiKeyFlag(opts);
4122
- const { runEmitArtifact, EmitArtifactError } = await import('./emit-artifact-cmd-VKPW577P.js');
4122
+ const { runEmitArtifact, EmitArtifactError } = await import('./emit-artifact-cmd-IQCRXOH7.js');
4123
4123
  let pr;
4124
4124
  if (opts.pr !== void 0) {
4125
4125
  pr = parseTrackerNumber(opts.pr);
@@ -4149,12 +4149,12 @@ locally so it never uploads. One correction can retire several duplicates.
4149
4149
  throw err;
4150
4150
  }
4151
4151
  });
4152
- emitCmd.command("finding").description("Record one finding: a problem you hit in the factory").requiredOption("--id <id>", "client id \u2014 re-emitting the same id replaces it").option("--category <context|flaky-test|tooling|environment|defect|other>", "what kind of problem this is").requiredOption("--title <text>", "one sentence").requiredOption("--file <path>", "the file the problem is in, or the instruction's own path").option("--line <number>", "line number in --file").option("--rule-path <path>", "the instruction's own path \u2014 required for a context finding").option("--rule-line <number>", "line number in --rule-path").option("--rule-quote <text>", "the instruction's quoted sentence \u2014 required unless --rule-relation is missing").option("--rule-relation <wrong|broken|missing>", "how the instruction fails \u2014 required for a context finding").requiredOption("--where <label>", 'where this was recorded, e.g. "build session"').option("--trace-id <id>", "the session's trace id, when there is one").option("--turn <number>", "turn index within the session").option("--round <number>", "round number, when there is one").addOption(new Option("--subject <value>").hideHelp()).addOption(new Option("--kind <value>").hideHelp()).addOption(new Option("--severity <value>").hideHelp()).addOption(new Option("--verdict <value>").hideHelp()).addOption(new Option("--fixed").hideHelp()).addOption(new Option("--source <value>").hideHelp()).option("--head-sha <sha>", "the commit this batch was recorded against").option("--context-source-sha <sha>", "the context-source commit this batch was recorded against").option("--item <number>", "work item to record on directly, by its number in this factory").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").addHelpText(
4152
+ emitCmd.command("finding").description("Record one finding: a problem you hit in the factory").requiredOption("--id <id>", "client id \u2014 re-emitting the same id replaces it").option("--category <context|flaky-test|tooling|environment|defect|other>", "what kind of problem this is").requiredOption("--title <text>", "one sentence").requiredOption("--file <path>", "the file the problem is in, or the instruction's own path").option("--line <number>", "line number in --file").option("--rule-path <path>", "the instruction's own path \u2014 required for a context finding").option("--rule-line <number>", "line number in --rule-path").option("--rule-quote <text>", "the instruction's quoted sentence \u2014 required unless --rule-relation is missing").option("--rule-relation <wrong|broken|missing>", "how the instruction fails \u2014 required for a context finding").requiredOption("--where <label>", 'where this was recorded, e.g. "build session"').option("--detail <text>", "what happened, a few paragraphs").option("--cause <text>", 'what is actually wrong, or "unknown"').option("--suggestion <text>", "what would help").option("--command <text>", "the command that misbehaved").option("--error <text>", "the first error line").option("--test <name>", "the test name, for a flaky test").option("--symbol <name>", "the function the problem is in, for a defect").option("--started-at <event>", "event id where you first hit the problem (ids are listed when you omit this)").option("--ended-at <event>", "event id where you got past it").option("--problem <id>", "a problem this finding reports; repeat for each one (needs --link-reason)", (value, previous = []) => [...previous, value]).option("--link-reason <text>", "why the finding reports each --problem").option("--new", "record a new problem instead of linking an existing one").option("--trace-id <id>", "the session's trace id, when there is one").option("--turn <number>", "turn index within the session").option("--round <number>", "round number, when there is one").addOption(new Option("--subject <value>").hideHelp()).addOption(new Option("--kind <value>").hideHelp()).addOption(new Option("--severity <value>").hideHelp()).addOption(new Option("--verdict <value>").hideHelp()).addOption(new Option("--fixed").hideHelp()).addOption(new Option("--source <value>").hideHelp()).option("--head-sha <sha>", "the commit this batch was recorded against").option("--context-source-sha <sha>", "the context-source commit this batch was recorded against").option("--item <number>", "work item to record on directly, by its number in this factory").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").addHelpText(
4153
4153
  "after",
4154
- '\n outerlayer emit finding --id gate-skip --category tooling \\\n --title "The pre-push gate prints green when a step was skipped" \\\n --file scripts/git/pre-push-checks.mjs --where "build session"\n\nRecords one problem you hit in the factory: an instruction that is wrong or\nmissing (context), a flaky test, a misbehaving script (tooling), a local\nsetup trap (environment), a bug outside your own change (defect), or\nanything else. A problem in your own change is fixed, not recorded.\nA context finding needs --rule-path and --rule-relation, and --rule-quote\nunless the relation is missing.\n--item names the work item; without it, inside a recorded session, the\nitem that session was launched for is used automatically.\n'
4154
+ '\n outerlayer emit finding --id gate-skip --category tooling \\\n --title "The pre-push gate prints green when a step was skipped" \\\n --file scripts/git/pre-push-checks.mjs --where "build session"\n\nRecords one problem you hit in the factory: an instruction that is wrong or\nmissing (context), a flaky test, a misbehaving script (tooling), a local\nsetup trap (environment), a bug outside your own change (defect), or\nanything else. A problem in your own change is fixed, not recorded.\nA context finding needs --rule-path and --rule-relation, and --rule-quote\nunless the relation is missing. Add --cause, --detail and --suggestion, and\n--command, --error (first error line), --test or --symbol when there is one.\n\nEvery finding joins a factory-level problem. When one of its keys matches a\nknown problem it joins that problem. Otherwise nothing is stored: the command\nprints the closest problems and exits with code 3. Run it again with\n--problem <id> --link-reason <why> (repeat --problem for each problem the\nfinding reports), or with --new if none of them is the same problem.\nWithout --started-at and --ended-at it lists your recent session events with\nids and times; name the event where you hit the problem and the one where\nyou got past it to record the time it cost.\n--item names the work item; without it, inside a recorded session, the\nitem that session was launched for is used automatically.\n'
4155
4155
  ).action(async (opts) => {
4156
4156
  warnIfApiKeyFlag(opts);
4157
- const { runEmitFinding, refuseRemovedFindingFlags, EmitFindingError } = await import('./emit-finding-cmd-TMOOKHPD.js');
4157
+ const { runEmitFinding, refuseRemovedFindingFlags, EmitFindingError } = await import('./emit-finding-cmd-P4RKBPBB.js');
4158
4158
  try {
4159
4159
  refuseRemovedFindingFlags(opts);
4160
4160
  const result = await runEmitFinding({
@@ -4168,6 +4168,18 @@ locally so it never uploads. One correction can retire several duplicates.
4168
4168
  ruleQuote: opts.ruleQuote,
4169
4169
  ruleRelation: opts.ruleRelation,
4170
4170
  where: opts.where,
4171
+ detail: opts.detail,
4172
+ cause: opts.cause,
4173
+ suggestion: opts.suggestion,
4174
+ command: opts.command,
4175
+ error: opts.error,
4176
+ test: opts.test,
4177
+ symbol: opts.symbol,
4178
+ startedAt: opts.startedAt,
4179
+ endedAt: opts.endedAt,
4180
+ problem: opts.problem,
4181
+ linkReason: opts.linkReason,
4182
+ new: opts.new,
4171
4183
  traceId: opts.traceId,
4172
4184
  turn: opts.turn,
4173
4185
  round: opts.round,
@@ -4190,10 +4202,10 @@ locally so it never uploads. One correction can retire several duplicates.
4190
4202
  });
4191
4203
  emitCmd.command("findings <file>").description("Record a batch of findings from a FindingBatch JSON file (- for standard input)").option("--item <number>", "work item to record on directly, by its number in this factory").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").addHelpText(
4192
4204
  "after",
4193
- "\n outerlayer emit findings review.json --item 412\n\nRecords a whole batch at once \u2014 a FindingBatch JSON file (schemaVersion,\noptional headSha / contextSourceSha, and 1-500 findings), validated against\nthe same contract `emit finding` uses before anything is sent. Re-emitting\nan id already on the item replaces that row rather than adding a second one.\n--item names the work item; without it, inside a recorded session, the item\nthat session was launched for is used automatically.\n"
4205
+ "\n outerlayer emit findings review.json --item 412\n\nRecords a whole batch at once \u2014 a FindingBatch JSON file (schemaVersion 3,\noptional headSha / contextSourceSha, and 1-500 findings), validated against\nthe same contract `emit finding` uses before anything is sent. Re-emitting\nan id already on the item replaces that row rather than adding a second one.\nFindings that need a choice between candidate problems are listed and not\nstored; the rest are stored, and the exit code is 3.\n--item names the work item; without it, inside a recorded session, the item\nthat session was launched for is used automatically.\n"
4194
4206
  ).action(async (file, opts) => {
4195
4207
  warnIfApiKeyFlag(opts);
4196
- const { runEmitFindings, EmitFindingError } = await import('./emit-finding-cmd-TMOOKHPD.js');
4208
+ const { runEmitFindings, EmitFindingError } = await import('./emit-finding-cmd-P4RKBPBB.js');
4197
4209
  try {
4198
4210
  const result = await runEmitFindings({
4199
4211
  file,
@@ -4214,10 +4226,26 @@ locally so it never uploads. One correction can retire several duplicates.
4214
4226
  });
4215
4227
  emitCmd.command("criteria <file>").description("Record a work item's acceptance criteria from a JSON file (- for stdin)").option("--item <number>", "work item to record on directly, by its number in this factory").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").addHelpText(
4216
4228
  "after",
4217
- '\n outerlayer emit criteria criteria.json --item 412\n\nRecords the item\'s acceptance criteria as one list. The file is JSON:\n { "criteria": [ { "id": "AC-1", "text": "Given ...", "proof": null },\n { "id": "login.redirect", "text": "...", "proof": "screenshot" } ] }\nAn id is 1-64 characters of A-Z a-z 0-9 . _ : - and is the id evidence is bound to with\n`emit artifact --for`. proof is video, screenshot, report, log, test, file or null (any bound\nartifact proves it). The list holds 1 to 200 criteria, each text at most 2,000\ncharacters, and no id twice. The list is validated before anything is sent.\nOnce an item has a list, only a person can replace it: run the command outside an\nagent session, with your own login. A key bound to no member cannot replace it either.\n--item names the work item; without it, inside a recorded session, the item that\nsession was launched for is used automatically.\n'
4229
+ `
4230
+ outerlayer emit criteria criteria.json --item 412
4231
+
4232
+ Records the item's acceptance criteria as one list. The file is JSON:
4233
+ { "criteria": [ { "id": "AC-1", "text": "Given ...", "proof": null },
4234
+ { "id": "login.redirect", "text": "...", "proof": "screenshot" } ] }
4235
+ An id is 1-64 characters of A-Z a-z 0-9 . _ : - and is the id evidence is bound to with
4236
+ \`emit artifact --for\`. proof is video, screenshot, report, log, test, file or null (any bound
4237
+ artifact proves it). The list holds 1 to 200 criteria, each text at most 2,000
4238
+ characters, and no id twice. The list is validated before anything is sent.
4239
+ An optional top-level "drafted": true marks a list the build drafted from the issue's
4240
+ description. Only a list an agent records keeps the mark; a person's list never does.
4241
+ Once an item has a list, only a person can replace it: run the command outside an
4242
+ agent session, with your own login. A key bound to no member cannot replace it either.
4243
+ --item names the work item; without it, inside a recorded session, the item that
4244
+ session was launched for is used automatically.
4245
+ `
4218
4246
  ).action(async (file, opts) => {
4219
4247
  warnIfApiKeyFlag(opts);
4220
- const { runEmitCriteria, EmitCriteriaError } = await import('./emit-criteria-cmd-U2TL6FYY.js');
4248
+ const { runEmitCriteria, EmitCriteriaError } = await import('./emit-criteria-cmd-EYUXME26.js');
4221
4249
  try {
4222
4250
  const result = await runEmitCriteria({
4223
4251
  file,
@@ -4244,7 +4272,7 @@ function registerWorkCommands(program2) {
4244
4272
  "\n outerlayer work build --issue 42 # a host builds it\n outerlayer work build --item 7 # retry an item already on Work\n outerlayer work build --issue 42 --local # you build it\n outerlayer work build --issue ENG-123 --tracker linear # a Linear issue\n\nWithout --local, records a build request: a host takes the item when it\nhas a free slot. It never links the calling session and never takes a\nlease, even run from inside one.\n\nWith --local, puts the item on Work with no build request, so no host\ntakes it, and prints the line that starts your session on it. That\nsession links itself and holds the item's lease while it works.\n"
4245
4273
  ).action(async (opts) => {
4246
4274
  warnIfApiKeyFlag(opts);
4247
- const { runWorkBuild, WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4275
+ const { runWorkBuild, WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4248
4276
  try {
4249
4277
  await runWorkBuild(opts);
4250
4278
  } catch (err) {
@@ -4260,7 +4288,7 @@ function registerWorkCommands(program2) {
4260
4288
  "\n outerlayer work link-session 42\n\nNames an item that already exists on the calling session \u2014 it never\ncreates one. Create one with `outerlayer work build --local`. Linking\nthe same session to the same item twice is a no-op. Also takes or\nrenews that session's own 20-minute lease, unless --no-lease or\n--ended is given, or another worker already holds a live one.\n"
4261
4289
  ).action(async (number, opts) => {
4262
4290
  warnIfApiKeyFlag(opts);
4263
- const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4291
+ const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4264
4292
  try {
4265
4293
  await runWorkLinkSession({ ...opts, number, noLease: opts.lease === false, logFailures: opts.logFailures === true });
4266
4294
  } catch (err) {
@@ -4285,8 +4313,8 @@ most one action flag may be given.
4285
4313
  `
4286
4314
  ).action(async (opts) => {
4287
4315
  warnIfApiKeyFlag(opts);
4288
- const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-34FTLPUO.js');
4289
- const { WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4316
+ const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-FI2TMAYQ.js');
4317
+ const { WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4290
4318
  try {
4291
4319
  await runWorkComment(opts);
4292
4320
  } catch (err) {
@@ -4302,8 +4330,8 @@ most one action flag may be given.
4302
4330
  "\n outerlayer work threads --item 7\n\nPrints the item's general thread, then one thread per acceptance\ncriterion that carries proof or a comment, each with who is waiting\non whom next.\n"
4303
4331
  ).action(async (opts) => {
4304
4332
  warnIfApiKeyFlag(opts);
4305
- const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-34FTLPUO.js');
4306
- const { WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4333
+ const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-FI2TMAYQ.js');
4334
+ const { WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4307
4335
  try {
4308
4336
  await runWorkThreads(opts);
4309
4337
  } catch (err) {
@@ -4325,7 +4353,7 @@ live addition remains on it.
4325
4353
  `
4326
4354
  ).action(async (opts) => {
4327
4355
  warnIfApiKeyFlag(opts);
4328
- const { runWorkRemove, WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4356
+ const { runWorkRemove, WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4329
4357
  try {
4330
4358
  await runWorkRemove(opts);
4331
4359
  } catch (err) {
@@ -4338,7 +4366,7 @@ live addition remains on it.
4338
4366
  });
4339
4367
  workCmd.command("status").description("Show one item's stage, gate ledger, and additions").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").action(async (opts) => {
4340
4368
  warnIfApiKeyFlag(opts);
4341
- const { runWorkStatus, WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4369
+ const { runWorkStatus, WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4342
4370
  try {
4343
4371
  await runWorkStatus(opts);
4344
4372
  } catch (err) {
@@ -4351,7 +4379,7 @@ live addition remains on it.
4351
4379
  });
4352
4380
  workCmd.command("list").description("List live work items for this factory").option("--stage <spec|build|review>", "filter by stage").option("--section <attention|waiting|flowing>", "filter by Queue section").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--unclaimed", "only items with no live lease").option("--claimed", "only items with a live lease, held by a host or a local session").option("--startable", "only items a host can start: a build request, no open PR, no live lease").option("--needs <amend>", "only items with a thread waiting on an agent").option("--json", "machine-readable JSON output").option("--url <url>", "cloud base URL (or OUTERLAYER_URL / config)").addOption(apiKeyOption()).option("--app-id <id>", "factory id the key is bound to (or OUTERLAYER_APP_ID / config)").action(async (opts) => {
4353
4381
  warnIfApiKeyFlag(opts);
4354
- const { runWorkList, WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4382
+ const { runWorkList, WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4355
4383
  try {
4356
4384
  await runWorkList(opts);
4357
4385
  } catch (err) {
@@ -4367,7 +4395,7 @@ live addition remains on it.
4367
4395
  "\n outerlayer work claim --item 42 --kind implement\n\nRecords a lease held by the API key that sends it. A live lease held\nby a different key is refused; claiming again with the same key extends\nit. --host is a label only.\n"
4368
4396
  ).action(async (opts) => {
4369
4397
  warnIfApiKeyFlag(opts);
4370
- const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-LXXSNEGF.js');
4398
+ const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-CWGYEIJE.js');
4371
4399
  try {
4372
4400
  await runWorkClaim(opts);
4373
4401
  } catch (err) {
@@ -4383,7 +4411,7 @@ live addition remains on it.
4383
4411
  "\n outerlayer work renew --item 42\n\nExtends the live lease this API key holds. Refused with a 410 if the\nlease has expired, or a 403 if a different key holds it.\n"
4384
4412
  ).action(async (opts) => {
4385
4413
  warnIfApiKeyFlag(opts);
4386
- const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-LXXSNEGF.js');
4414
+ const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-CWGYEIJE.js');
4387
4415
  try {
4388
4416
  await runWorkRenew(opts);
4389
4417
  } catch (err) {
@@ -4402,7 +4430,7 @@ live addition remains on it.
4402
4430
  "\n outerlayer work release --item 42 --outcome ok\n outerlayer work release --item 42\n\nMarks the lease this API key holds released, optionally with an\noutcome. A lease held by a different key is refused. Releasing an\nalready-released lease is a no-op that returns the recorded release\ntime and outcome.\n\nRun inside a session with no --host, it releases that session's own\nlease instead, so the item's review opens without ending the\nsession. It never releases another session's lease. --outcome\napplies to a host's lease only. A later tool call in the session\ntakes the lease again.\n"
4403
4431
  ).action(async (opts) => {
4404
4432
  warnIfApiKeyFlag(opts);
4405
- const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-LXXSNEGF.js');
4433
+ const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-CWGYEIJE.js');
4406
4434
  try {
4407
4435
  await runWorkRelease(opts);
4408
4436
  } catch (err) {
@@ -4418,7 +4446,7 @@ live addition remains on it.
4418
4446
  "\n outerlayer work pr 481\n outerlayer work pr 481 --withdraw --item 250\n\nRun inside a session already on a work item. Records that pull\nrequest 481 in the checkout's repository belongs to the item named by\nOUTERLAYER_WORK, or refuses when the session is not on it. The same\nfact a `pr-link` in the session's own transcript records on sync.\nIdempotent: declaring the same pull request twice is a no-op.\n\n--withdraw removes one item's declaration of the pull request. It\nneeds your own login; an agent session or a factory key is refused.\n"
4419
4447
  ).action(async (number, opts) => {
4420
4448
  warnIfApiKeyFlag(opts);
4421
- const { runWorkPr, WorkPrCommandError } = await import('./work-pr-cmd-EXTOGBJC.js');
4449
+ const { runWorkPr, WorkPrCommandError } = await import('./work-pr-cmd-Y5TDMQ4O.js');
4422
4450
  try {
4423
4451
  await runWorkPr({ ...opts, number });
4424
4452
  } catch (err) {
@@ -4434,8 +4462,8 @@ live addition remains on it.
4434
4462
  '\n outerlayer work open-pr --title "Add the thing" --body-file pr.md\n outerlayer work open-pr --repository acme/web --title "Add the thing" --body-file pr.md\n\nIn a host build, which holds an item key, the gateway opens the pull\nrequest as the factory\'s GitHub App, from the branch the claim named,\nor edits the one already open on that branch. No gh command runs.\nIn your own session it runs `gh pr create` (or `gh pr edit`) under your\nlogin, then declares the pull request on the item, as `work pr` does.\nRun it again after a change to the title or description.\n'
4435
4463
  ).action(async (opts) => {
4436
4464
  warnIfApiKeyFlag(opts);
4437
- const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-YZAENDP6.js');
4438
- const { WorkCommandError } = await import('./work-cmd-ZJY3NA2G.js');
4465
+ const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-OVHJ5I5H.js');
4466
+ const { WorkCommandError } = await import('./work-cmd-X7NMJDFL.js');
4439
4467
  try {
4440
4468
  await runWorkOpenPr(opts);
4441
4469
  } catch (err) {
@@ -4451,8 +4479,8 @@ function registerRunnerCommands(program2) {
4451
4479
  const runnerCmd = program2.command("runner").description("Take work from the implement and amend queues on this machine");
4452
4480
  const configOption = "the cloud config file to run against (default: ~/.outerlayer/config.json)";
4453
4481
  runnerCmd.command("start").description("Run the loop: claim work, run it, report, clean up, repeat").option("--config <path>", configOption).action(async (opts) => {
4454
- const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-MKBX2ZQD.js');
4455
- const { start, RunnerStartError } = await import('./loop-3FNROLW7.js');
4482
+ const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-N3C6DFQA.js');
4483
+ const { start, RunnerStartError } = await import('./loop-XOAE6ZIY.js');
4456
4484
  const { RunnerAlreadyRunningError } = await import('./pidfile-UZRH774M.js');
4457
4485
  const configPath = opts.config ?? defaultRunnerConfigPath();
4458
4486
  try {
@@ -4467,8 +4495,8 @@ function registerRunnerCommands(program2) {
4467
4495
  }
4468
4496
  });
4469
4497
  runnerCmd.command("check").description("Validate the config, the hooks and the key, and print the effective settings").option("--config <path>", configOption).action(async (opts) => {
4470
- const { defaultRunnerConfigPath } = await import('./config-MKBX2ZQD.js');
4471
- const { runRunnerCheck } = await import('./check-YDXOLVJ5.js');
4498
+ const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
4499
+ const { runRunnerCheck } = await import('./check-63IDCEEV.js');
4472
4500
  const configPath = opts.config ?? defaultRunnerConfigPath();
4473
4501
  const result = await runRunnerCheck(configPath);
4474
4502
  if (!result.ok) {
@@ -4484,7 +4512,7 @@ function registerRunnerCommands(program2) {
4484
4512
  `);
4485
4513
  });
4486
4514
  runnerCmd.command("install").description("Put this CLI in the runner's install layout; --service adds its service").option("--config <path>", configOption).option("--service", "set up the service that keeps the runner running").option("--print", "with --service, print the unit and set up nothing").option("--user <account>", "with --service, the account the service runs as").option("--replace", "replace a unit or launcher file this command did not write").option("--allow-downgrade", "install this CLI even when it is older than the version the runner runs").option("--unit-name <name>", "with --service, the unit's name, to run a second runner beside the first").action(async (opts) => {
4487
- const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-MKBX2ZQD.js');
4515
+ const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-N3C6DFQA.js');
4488
4516
  const { runRunnerInstall, RunnerInstallError, launcherSummary, restartNote } = await import('./install-cmd-5HYMXLEW.js');
4489
4517
  const configPath = opts.config ?? defaultRunnerConfigPath();
4490
4518
  if (opts.service !== true) {
@@ -4497,7 +4525,7 @@ function registerRunnerCommands(program2) {
4497
4525
  }
4498
4526
  try {
4499
4527
  if (opts.service === true) {
4500
- const { installServiceCommand } = await import('./command-U6UGIBVT.js');
4528
+ const { installServiceCommand } = await import('./command-N7BAB4XJ.js');
4501
4529
  const result2 = await installServiceCommand({
4502
4530
  configPath,
4503
4531
  print: opts.print,
@@ -4552,22 +4580,50 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
4552
4580
  fail("pass --service: `outerlayer runner uninstall --service` removes the runner's service.");
4553
4581
  return;
4554
4582
  }
4555
- const { defaultRunnerConfigPath } = await import('./config-MKBX2ZQD.js');
4556
- const { uninstallServiceCommand } = await import('./command-U6UGIBVT.js');
4583
+ const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
4584
+ const { uninstallServiceCommand } = await import('./command-N7BAB4XJ.js');
4557
4585
  const result = await uninstallServiceCommand({ configPath: opts.config ?? defaultRunnerConfigPath(), unitName: opts.unitName, home: homeFromEnv() });
4558
4586
  if (result.ok) for (const line of result.lines) process.stdout.write(`${C.green}\u2713${C.reset} ${line}
4559
4587
  `);
4560
4588
  else fail(result.lines.length > 0 ? result.lines.join("\n ") : "the service was not removed");
4561
4589
  });
4562
- runnerCmd.command("init").description("Write a runner block that fits this machine; on macOS, offers a Linux VM").option("--config <path>", configOption).option("--vm", "on macOS, run the runner in a Linux VM (Lima), with container builds").option("--no-vm", "on macOS, skip the VM and run builds as unisolated processes").option("--allow-process-builds", "where builds cannot be isolated, run them as processes anyway").addOption(new Option("--config-stdin", "the VM-side hand-off: read the whole config as JSON on stdin").hideHelp()).option("--vm-cli-package <path>", "with --vm, install this packed CLI tarball in the VM").option("--unit-name <name>", "the service unit's name, to run a second runner beside the first").action(async (opts) => {
4563
- const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-MKBX2ZQD.js');
4564
- const { runRunnerInit, RunnerInitError } = await import('./init-PUY2BUHH.js');
4565
- const { detectMachine } = await import('./machine-RWDDJRWB.js');
4566
- const { chooseVm, runVmInit, VmInitError } = await import('./init-M5IBAGVE.js');
4590
+ runnerCmd.command("init").description("Write a runner block that fits this machine; on macOS, offers a Linux VM").option("--config <path>", configOption).option("--vm", "on macOS, run the runner in a Linux VM (Lima), with container builds").option("--no-vm", "on macOS, skip the VM and run builds as unisolated processes").option("--allow-process-builds", "where builds cannot be isolated, run them as processes anyway").addOption(new Option("--config-stdin", "the VM-side hand-off: read the whole config as JSON on stdin").hideHelp()).option("--vm-cli-package <path>", "with --vm, install this packed CLI tarball in the VM").option("--unit-name <name>", "the service unit's name, to run a second runner beside the first").option("--agent <name>", "write commands that start this agent (claude); its credential comes on stdin").option("--app-id <id>", "factory id to create the runner key on, when it comes from your login").option("--key-name <name>", "name of the runner key created from your login (default: machine name)").addHelpText(
4591
+ "after",
4592
+ "\nWith no apiKey in the config file, and a login saved by `outerlayer login`,\nrunner init creates the runner key itself, with the Runner preset, on the\nfactory --app-id names (or OUTERLAYER_APP_ID, or this repository's\nconnection). Your login needs factory_keys.insert on that factory.\n\nWith --agent claude, the queue commands start Claude Code instead of the\nplaceholder, and Claude's credential is read from stdin: a token from\n`claude setup-token` (sk-ant-oat...) or an API key (sk-ant-api...). It is\nnever printed, and is stored in the runner.env beside the config with mode\n600. Other lines in the file are kept. With nothing piped, a credential\nalready in runner.env is used.\n\n printf '%s' \"$CLAUDE_CODE_OAUTH_TOKEN\" | outerlayer runner init --agent claude\n"
4593
+ ).action(async (opts) => {
4594
+ const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-N3C6DFQA.js');
4595
+ const { prepareRunnerInit, runRunnerInit, RunnerInitError, writeRunnerInit } = await import('./init-2LWVS4AH.js');
4596
+ const { createRunnerKey, runnerKeyLogin } = await import('./runner-key-TDTLNAKJ.js');
4597
+ const { detectMachine } = await import('./machine-G6PY2J2J.js');
4598
+ const { chooseVm, runVmInit, VmInitError } = await import('./init-ZL7ORPTF.js');
4599
+ const { AGENT_COMMANDS, credentialFromStdin, isAgentName, restartForCredential, RunnerEnvError } = await import('./runner-env-XTC53CHN.js');
4567
4600
  const configPath = opts.config ?? defaultRunnerConfigPath();
4601
+ if (opts.agent !== void 0 && !isAgentName(opts.agent)) {
4602
+ fail(`--agent ${opts.agent}: runner init writes commands for ${Object.keys(AGENT_COMMANDS).join(", ")}. For another agent, leave --agent out and edit runner.commands.`);
4603
+ return;
4604
+ }
4605
+ const agent = opts.agent;
4606
+ if (agent !== void 0 && opts.configStdin === true) {
4607
+ fail("the VM-side hand-off names the agent in its config on stdin; pass no --agent with it.");
4608
+ return;
4609
+ }
4568
4610
  try {
4611
+ let credential;
4612
+ if (agent !== void 0) {
4613
+ const { readPipedKey } = await import('./login-browser-QZCS3WW6.js');
4614
+ credential = credentialFromStdin(
4615
+ await readPipedKey(process.stdin, {
4616
+ onWaiting: () => process.stderr.write(`runner init is waiting for Claude's credential on stdin; close stdin to use the one already in runner.env
4617
+ `)
4618
+ })
4619
+ );
4620
+ }
4569
4621
  const machine = detectMachine();
4570
4622
  if (opts.configStdin === true) {
4623
+ if (opts.appId !== void 0 || opts.keyName !== void 0) {
4624
+ fail("the VM-side hand-off carries its runner key in its config on stdin; pass no --app-id or --key-name with it.");
4625
+ return;
4626
+ }
4571
4627
  const chunks = [];
4572
4628
  for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
4573
4629
  const result2 = runRunnerInit(configPath, { machine, configFromStdin: Buffer.concat(chunks).toString("utf8"), requireContainer: true });
@@ -4575,9 +4631,19 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
4575
4631
  const hostKey2 = await ensureHostKey2(configPath);
4576
4632
  process.stdout.write(`host key ${hostKey2.fingerprint}
4577
4633
  hooks: ${result2.hooks}
4634
+ `);
4635
+ if (result2.credential !== void 0) process.stdout.write(`stored ${result2.credential.variable} in ${result2.credential.path}
4578
4636
  `);
4579
4637
  return;
4580
4638
  }
4639
+ const login = runnerKeyLogin(configPath, { appId: opts.appId });
4640
+ if (login === void 0 && opts.keyName !== void 0) {
4641
+ fail(
4642
+ `--key-name names the runner key runner init creates from your login, and it creates none here: ${configPath} already has an apiKey, or no login is saved. Run \`outerlayer login\` first, or leave --key-name out.`
4643
+ );
4644
+ return;
4645
+ }
4646
+ const makeKey = login === void 0 ? void 0 : () => createRunnerKey(login, { name: opts.keyName });
4581
4647
  let declinedVm = opts.vm === false;
4582
4648
  const useVm = await chooseVm({
4583
4649
  platform: machine.platform,
@@ -4605,6 +4671,9 @@ hooks: ${result2.hooks}
4605
4671
  hostMemoryBytes: totalmem(),
4606
4672
  version: resolvePackageVersion(),
4607
4673
  cliPackage: opts.vmCliPackage,
4674
+ agent,
4675
+ credential,
4676
+ runnerKey: makeKey,
4608
4677
  progress: (line) => process.stdout.write(`${line}
4609
4678
  `)
4610
4679
  });
@@ -4613,11 +4682,23 @@ hooks: ${result2.hooks}
4613
4682
  return;
4614
4683
  }
4615
4684
  const allowProcess = opts.allowProcessBuilds === true || machine.platform === "darwin" && declinedVm;
4616
- const result = runRunnerInit(configPath, { machine, allowProcess });
4685
+ const prepared = prepareRunnerInit(configPath, { machine, allowProcess, agent, credential, keyFromLogin: makeKey !== void 0 });
4686
+ const created = makeKey === void 0 ? void 0 : await makeKey();
4687
+ const result = writeRunnerInit(prepared, created);
4617
4688
  const { ensureHostKey } = await import('./host-key-YMLXY6QB.js');
4618
4689
  const hostKey = await ensureHostKey(configPath);
4690
+ if (created !== void 0) {
4691
+ process.stdout.write(`${C.green}\u2713${C.reset} created the runner key ${created.name} from your login, with the Runner preset, and saved it in ${result.configPath}
4692
+ `);
4693
+ }
4619
4694
  process.stdout.write(`${C.green}\u2713${C.reset} wrote a runner block into ${result.configPath}
4620
4695
  `);
4696
+ if (agent !== void 0) process.stdout.write(`${C.green}\u2713${C.reset} commands: implement and amend start ${agent}
4697
+ `);
4698
+ if (result.credential !== void 0) {
4699
+ process.stdout.write(`${C.green}\u2713${C.reset} stored ${result.credential.variable} in ${result.credential.path}, readable only by this account
4700
+ `);
4701
+ }
4621
4702
  process.stdout.write(
4622
4703
  `${C.green}\u2713${C.reset} host key ${hostKey.fingerprint}: the runner key binds to it on its first claim
4623
4704
  `
@@ -4662,35 +4743,42 @@ ${C.yellow}The runner will not update itself: ${installErr.message}${C.reset}
4662
4743
  }
4663
4744
  }
4664
4745
  let serviceUp = false;
4746
+ let restart;
4665
4747
  if (installedLayout) {
4666
- const { installServiceCommand } = await import('./command-U6UGIBVT.js');
4748
+ const { installServiceCommand } = await import('./command-N7BAB4XJ.js');
4667
4749
  const service = await installServiceCommand({
4668
4750
  configPath,
4669
4751
  entryPath: process.argv[1] ?? "",
4670
4752
  nodePath: process.execPath,
4671
4753
  home: homeFromEnv(),
4672
4754
  unitName: opts.unitName,
4673
- layoutInstalled: true
4755
+ layoutInstalled: true,
4756
+ restart: result.credential !== void 0
4674
4757
  });
4675
4758
  serviceUp = service.ok && service.state !== "needs-wsl-shutdown";
4676
4759
  for (const line of service.lines) process.stdout.write(service.ok ? `${C.green}\u2713${C.reset} ${line}
4677
4760
  ` : `${C.yellow}${line}${C.reset}
4678
4761
  `);
4762
+ if (result.credential !== void 0) restart = restartForCredential(service.state, opts.unitName);
4679
4763
  } else {
4680
4764
  process.stdout.write(`${C.yellow}No service was set up: ${noService}.${C.reset}
4681
4765
  `);
4682
4766
  }
4767
+ if (restart !== void 0) process.stdout.write(`
4768
+ ${C.yellow}${restart}${C.reset}
4769
+ `);
4770
+ const steps = agent === void 0 ? [`Edit the command lines in ${result.configPath} to start your agent.`, `${C.yellow}outerlayer runner check${C.reset}`] : [`${C.yellow}outerlayer runner check${C.reset}`];
4771
+ if (!serviceUp) steps.push(`${C.yellow}outerlayer runner start${C.reset}, or ${C.yellow}outerlayer runner install --service${C.reset} to keep it running.`);
4683
4772
  process.stdout.write(
4684
4773
  `
4685
4774
  Next:
4686
- 1. Edit the command lines in ${result.configPath} to start your agent.
4687
- 2. ${C.yellow}outerlayer runner check${C.reset}
4688
- ` + (serviceUp ? ` The runner service claims nothing until step 1 is done and a Claude credential is in ${join(dirname(configPath), "runner.env")}; ${C.yellow}outerlayer runner status${C.reset} shows what it is doing.
4689
- ` : ` 3. ${C.yellow}outerlayer runner start${C.reset}, or ${C.yellow}outerlayer runner install --service${C.reset} to keep it running.
4690
- `)
4775
+ ` + steps.map((step, i) => ` ${i + 1}. ${step}
4776
+ `).join("") + (serviceUp && agent === void 0 ? ` The runner service claims nothing until step 1 is done and a Claude credential is in ${join(dirname(configPath), "runner.env")}; ${C.yellow}outerlayer runner status${C.reset} shows what it is doing.
4777
+ ` : serviceUp ? ` The runner service is running; ${C.yellow}outerlayer runner status${C.reset} shows what it is doing.
4778
+ ` : "")
4691
4779
  );
4692
4780
  } catch (err) {
4693
- if (err instanceof RunnerInitError || err instanceof VmInitError || err instanceof RunnerConfigError) {
4781
+ if (err instanceof RunnerInitError || err instanceof VmInitError || err instanceof RunnerConfigError || err instanceof RunnerEnvError) {
4694
4782
  fail(err.message);
4695
4783
  return;
4696
4784
  }
@@ -4701,12 +4789,12 @@ Next:
4701
4789
  "after",
4702
4790
  "\nFetches the head of the default branch into the runner's cache, reads its\n.devcontainer/devcontainer.json, and builds the image with the devcontainer CLI. Settings\nthat reach the host or widen the container's privileges are ignored and listed. The\nimage holds this CLI and a pinned Claude Code. Nothing runs inside the image.\n"
4703
4791
  ).action(async (opts) => {
4704
- const { defaultRunnerConfigPath } = await import('./config-MKBX2ZQD.js');
4792
+ const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
4705
4793
  const { buildRepositoryImage, RecipeError } = await import('./build-LELVCWHV.js');
4706
4794
  const { currentCliBuild } = await import('./cli-build-K56DK4DS.js');
4707
- const { createDockerHost } = await import('./docker-WVRRMK6R.js');
4708
- const { builderSpec } = await import('./builder-PP436VGZ.js');
4709
- const { loadRunnerConfig } = await import('./config-MKBX2ZQD.js');
4795
+ const { createDockerHost } = await import('./docker-BZ55BMBJ.js');
4796
+ const { builderSpec } = await import('./builder-UUS4ORDH.js');
4797
+ const { loadRunnerConfig } = await import('./config-N3C6DFQA.js');
4710
4798
  const configPath = opts.config ?? defaultRunnerConfigPath();
4711
4799
  const memory = (() => {
4712
4800
  try {
@@ -4767,16 +4855,16 @@ Next:
4767
4855
  }
4768
4856
  });
4769
4857
  runnerCmd.command("stop").description("Take no more work; wait for running jobs to finish, then exit").option("--config <path>", "the cloud config file naming the runner to stop").option("--now", "end running jobs immediately, with outcome stopped").action(async (opts) => {
4770
- const { defaultRunnerConfigPath } = await import('./config-MKBX2ZQD.js');
4771
- const { requestStop } = await import('./loop-3FNROLW7.js');
4858
+ const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
4859
+ const { requestStop } = await import('./loop-XOAE6ZIY.js');
4772
4860
  const configPath = opts.config ?? defaultRunnerConfigPath();
4773
4861
  await requestStop(configPath, Boolean(opts.now));
4774
4862
  });
4775
4863
  runnerCmd.command("status").description("List running jobs; with --recent, finished ones too").option("--config <path>", "the cloud config file naming the runner to inspect").option("--recent", "also list finished jobs, newest first, capped at 20").option("--json", "machine-readable JSON output").action(async (opts) => {
4776
- const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-MKBX2ZQD.js');
4864
+ const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-N3C6DFQA.js');
4777
4865
  const { jobsDir, runnerDir } = await import('./paths-OYKMVYJP.js');
4778
- const { listJobFiles, runnerProcessState } = await import('./loop-3FNROLW7.js');
4779
- const { renderStatus } = await import('./status-2OW5AKWL.js');
4866
+ const { listJobFiles, runnerProcessState } = await import('./loop-XOAE6ZIY.js');
4867
+ const { renderStatus } = await import('./status-MRTPG34B.js');
4780
4868
  const { readHousekeepingRecord } = await import('./record-C43U4PKS.js');
4781
4869
  const path2 = await import('path');
4782
4870
  const configPath = opts.config ?? defaultRunnerConfigPath();
@@ -4796,7 +4884,7 @@ Next:
4796
4884
  const service = await (async () => {
4797
4885
  if (process.platform !== "linux" || !existsSync("/run/systemd/system")) return void 0;
4798
4886
  const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
4799
- const { DEFAULT_UNIT_NAME } = await import('./install-HKOVISUP.js');
4887
+ const { DEFAULT_UNIT_NAME } = await import('./install-HDBIPHTX.js');
4800
4888
  const facts = inspectService(DEFAULT_UNIT_NAME, path2.dirname(configPath), realInspectProbes());
4801
4889
  return facts === void 0 ? void 0 : { unit: facts.unit, running: facts.mainPid !== void 0, delegated: facts.delegate };
4802
4890
  })();
@@ -4831,8 +4919,8 @@ Next:
4831
4919
  );
4832
4920
  });
4833
4921
  runnerCmd.command("logs").argument("<item>", "the work item's number in the factory").description("Print the newest attempt's log for one item").option("--config <path>", "the cloud config file naming the runner to inspect").option("-f, --follow", "keep printing as the log grows").action(async (itemArg, opts) => {
4834
- const { defaultRunnerConfigPath } = await import('./config-MKBX2ZQD.js');
4835
- const { listJobFiles } = await import('./loop-3FNROLW7.js');
4922
+ const { defaultRunnerConfigPath } = await import('./config-N3C6DFQA.js');
4923
+ const { listJobFiles } = await import('./loop-XOAE6ZIY.js');
4836
4924
  const { newestAttempt, logPathFor, tail, RunnerLogsError } = await import('./logs-H55NWW4H.js');
4837
4925
  const configPath = opts.config ?? defaultRunnerConfigPath();
4838
4926
  const item = parseTrackerNumber(itemArg);
@@ -5056,7 +5144,7 @@ function registerMcpCommands(program2) {
5056
5144
  "after",
5057
5145
  "\nThe API key is never a flag and never written to .mcp.json as a literal value.\n stdio (default): the client spawns `outerlayer mcp serve`, which reads the key\n from ~/.outerlayer/config.json (or OUTERLAYER_API_KEY) on every connect, so a\n rotated key is picked up without rewriting the entry.\n http: the entry carries the ${OUTERLAYER_API_KEY} placeholder, which the client\n resolves from the environment it was launched with.\n\n--command: use an absolute or ${HOME}-relative path when the CLI is not on PATH.\n--url: with stdio the endpoint resolves from OUTERLAYER_URL / the config file at\nconnect time; for self-host pass your gateway-node origin + /v1/mcp.\nSelf-host deployments also need --app-id: without a key service to resolve the\ncaller from the bearer token, the gateway answers 401 without that header.\n"
5058
5146
  ).action(async (opts) => {
5059
- const { runMcpInstall, handleMcpInstallError, McpInstallError } = await import('./mcp-install-cmd-XZARXNYT.js');
5147
+ const { runMcpInstall, handleMcpInstallError, McpInstallError } = await import('./mcp-install-cmd-VXVIH7PH.js');
5060
5148
  try {
5061
5149
  if (opts.transport !== void 0 && opts.transport !== "stdio" && opts.transport !== "http") {
5062
5150
  throw new McpInstallError(`--transport must be "stdio" or "http", got "${opts.transport}"`);
@@ -5089,7 +5177,7 @@ function registerMcpCommands(program2) {
5089
5177
  function registerImportCommands(program2) {
5090
5178
  const importCmd = program2.command("import").description("Import an existing config format into .outerlayer/");
5091
5179
  importCmd.command("ruler").description("Port a .ruler/ tree into the equivalent .outerlayer/ tree").option("--dir <path>", "repo root to read from (default: cwd)").option("--json", "machine-readable JSON output").addHelpText("after", "\nRuler: https://github.com/intellectronica/ruler. Mostly a rename.\n").action(async (opts) => {
5092
- const { runImportRuler, ImportRulerError } = await import('./import-ruler-cmd-TUK3MRPC.js');
5180
+ const { runImportRuler, ImportRulerError } = await import('./import-ruler-cmd-IXR7YK47.js');
5093
5181
  try {
5094
5182
  const result = runImportRuler({ cwd: opts.dir, json: opts.json });
5095
5183
  if (result.exitCode !== 0) process.exitCode = result.exitCode;
@@ -5105,7 +5193,7 @@ function registerImportCommands(program2) {
5105
5193
  "after",
5106
5194
  "\nInstalls the four maintained skills (emitting-evidence, amend,\nreporting-findings, outerlayer) and an AGENTS.md snippet teaching agents to run\n`emit artifact`. A skill that differs from this CLI's bundle is rewritten to it, and the output says so.\n"
5107
5195
  ).action(async (opts) => {
5108
- const { runImportCapture, ImportCaptureError } = await import('./import-capture-cmd-5NJVL2HN.js');
5196
+ const { runImportCapture, ImportCaptureError } = await import('./import-capture-cmd-2BKM5I2N.js');
5109
5197
  try {
5110
5198
  runImportCapture({ cwd: opts.dir, json: opts.json });
5111
5199
  } catch (err) {
@@ -5148,7 +5236,7 @@ function registerContextCommands(program2) {
5148
5236
  await compileContext({ dir: opts.dir, check: opts.check, json: opts.json });
5149
5237
  });
5150
5238
  contextCmd.command("adopt <stagedDir>", { hidden: true }).description("Copy context the runner staged into this checkout (container builds)").option("--json", "machine-readable JSON output").action(async (stagedDir) => {
5151
- const { runContextAdopt } = await import('./context-adopt-A77G67CP.js');
5239
+ const { runContextAdopt } = await import('./context-adopt-B3PWGFHL.js');
5152
5240
  const { exitCode, stdout } = runContextAdopt(stagedDir, process.cwd());
5153
5241
  process.stdout.write(stdout);
5154
5242
  process.exitCode = exitCode;
@@ -5157,7 +5245,7 @@ function registerContextCommands(program2) {
5157
5245
  "after",
5158
5246
  "\nThe same flow SessionStart runs automatically, exposed for a manual retry or a\nfirst check outside a session. Writes AGENTS.md, .claude/**, and .outerlayer/**.\n"
5159
5247
  ).action(async (opts) => {
5160
- const { materializeContext } = await import('./context-materialize-KQZYATQZ.js');
5248
+ const { materializeContext } = await import('./context-materialize-7S6NLLRO.js');
5161
5249
  const result = await materializeContext({ cwd: process.cwd(), sessionId: `manual-${Date.now()}` });
5162
5250
  const output = formatContextMaterializeOutput(result);
5163
5251
  if (output.exitCode !== void 0) process.exitCode = output.exitCode;