@outerlayer/cli 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -0
- package/README.md +36 -2
- package/dist/agent-setup-TPR4BBFV.js +27 -0
- package/dist/{artifact-link-cmd-2F47LT7F.js → artifact-link-cmd-W7XF5BFB.js} +4 -3
- package/dist/{build-BG2YENGK.js → build-TQ6X5PYN.js} +5 -4
- package/dist/build-info.json +1 -1
- package/dist/{builder-2D3WSOOY.js → builder-C4QNTYMS.js} +5 -6
- package/dist/{check-5XE7XMC4.js → check-UWQAL2MN.js} +56 -52
- package/dist/{chunk-R7PF5OOU.js → chunk-3CWUOFV6.js} +21 -14
- package/dist/{chunk-4NPZVMJU.js → chunk-3UZTIRLF.js} +60 -4
- package/dist/chunk-452TM7J4.js +502 -0
- package/dist/{chunk-NNRTPG7X.js → chunk-5GHGAF6P.js} +4 -4
- package/dist/{chunk-WVUIRTWA.js → chunk-5MYFOW5V.js} +196 -37
- package/dist/{chunk-HGECYFEI.js → chunk-5TN7GLYX.js} +138 -109
- package/dist/{chunk-VQYBXPI7.js → chunk-6D7GT22T.js} +5 -5
- package/dist/{chunk-G5NPRL2K.js → chunk-6U3MV7TZ.js} +687 -76
- package/dist/{chunk-42ZI52JW.js → chunk-6X4SQ5MY.js} +4 -3
- package/dist/{chunk-MAK5LYRF.js → chunk-7KSSBQTS.js} +278 -535
- package/dist/{chunk-NWQTTSBB.js → chunk-7SJPQWBM.js} +41 -16
- package/dist/{chunk-J5M7BSJQ.js → chunk-7VUK5RFS.js} +23 -4
- package/dist/{chunk-OZGHL5Y5.js → chunk-7YP7T6GV.js} +2 -2
- package/dist/{chunk-TI7B7UEO.js → chunk-AMFWYESY.js} +1 -1
- package/dist/{chunk-YRYMNBGQ.js → chunk-ANPNT6PM.js} +2 -1
- package/dist/{chunk-4KSQKQXE.js → chunk-BM63HB2M.js} +20 -16
- package/dist/{chunk-4Q7OMKAT.js → chunk-BQXFBI7T.js} +81 -5
- package/dist/{chunk-ADJRGVZC.js → chunk-BWSUPRDK.js} +1 -1
- package/dist/{chunk-3NSNIIBE.js → chunk-CG36IONK.js} +18 -17
- package/dist/chunk-CJJDJ7AT.js +56 -0
- package/dist/chunk-CRB3POP4.js +34 -0
- package/dist/{chunk-LWSJQJC6.js → chunk-DADWEUH3.js} +7 -7
- package/dist/{chunk-KG5ECZGJ.js → chunk-DJ3ZUSC3.js} +11 -11
- package/dist/{chunk-F47JBIAW.js → chunk-DSF2FWAZ.js} +2 -1
- package/dist/{chunk-N5FOE5PS.js → chunk-ECODQ7II.js} +4 -33
- package/dist/{chunk-HHJDN6AU.js → chunk-ESVW4MJY.js} +1 -1
- package/dist/{chunk-YO4PSLVB.js → chunk-FVMYKL6L.js} +1 -1
- package/dist/chunk-G2YNJIGF.js +44 -0
- package/dist/{chunk-WUYTWNMA.js → chunk-G3IJ3N6F.js} +5 -3
- package/dist/{chunk-QKFB4K7X.js → chunk-GGOMT6KF.js} +1 -1
- package/dist/{chunk-N6LURUEF.js → chunk-GOSFARMS.js} +1 -1
- package/dist/{chunk-S3NYLI2P.js → chunk-HPJNLETG.js} +93 -38
- package/dist/{chunk-IP7BIBXJ.js → chunk-IF2OSI5T.js} +35 -1
- package/dist/{chunk-C5Y5ZJJZ.js → chunk-IXYZUUYQ.js} +5 -4
- package/dist/chunk-JBJTC3UU.js +35 -0
- package/dist/chunk-KYS3TQAG.js +296 -0
- package/dist/chunk-LLM3I5YU.js +89 -0
- package/dist/{chunk-YU3VKZ5F.js → chunk-LRG3ZMZD.js} +6 -7
- package/dist/{chunk-V7NIVOFD.js → chunk-M2ZWWTWZ.js} +1 -1
- package/dist/{chunk-ERI5GBNI.js → chunk-MLAAT4S6.js} +3 -3
- package/dist/{chunk-O765JR4W.js → chunk-MTP2QEVO.js} +1 -1
- package/dist/{chunk-ZM2IMMYN.js → chunk-N2PXGQRL.js} +9 -1
- package/dist/{chunk-BWQ66M6N.js → chunk-NFIR3GEE.js} +98 -24
- package/dist/{chunk-POJMEEJJ.js → chunk-NNLA7LLQ.js} +1 -1
- package/dist/{chunk-PLMTLGYH.js → chunk-NTRCK4NH.js} +5 -4
- package/dist/{chunk-22RMZBGW.js → chunk-P53VEAOF.js} +1 -1
- package/dist/{chunk-RLYZLUQ7.js → chunk-QEFKA2QD.js} +2 -2
- package/dist/{chunk-JXYVA45C.js → chunk-QNLX477Q.js} +1200 -322
- package/dist/{chunk-UGP76IZ5.js → chunk-S5PCA3SN.js} +1 -1
- package/dist/{chunk-RMWLBUCJ.js → chunk-SADUQSO4.js} +1 -1
- package/dist/{chunk-GQD22KKN.js → chunk-T2Q7ISPE.js} +2 -1
- package/dist/chunk-TUSYOL2O.js +113 -0
- package/dist/{chunk-NBGDBTO2.js → chunk-TWF3RICI.js} +9 -8
- package/dist/{chunk-ONKJUM6R.js → chunk-UVG35MWF.js} +1 -1
- package/dist/{chunk-KMDLFI4P.js → chunk-VMJPFQNE.js} +1 -1
- package/dist/chunk-VRRGENS4.js +144 -0
- package/dist/{chunk-PTW6W5NR.js → chunk-VYAL7PUE.js} +1 -1
- package/dist/{chunk-OHVJA253.js → chunk-WFSUUUAN.js} +47 -30
- package/dist/{chunk-RSNSZ2LJ.js → chunk-X5H2WYMB.js} +1 -1
- package/dist/{chunk-HYZRY4KZ.js → chunk-XNGMQ27R.js} +16 -6
- package/dist/chunk-XSZD4AZ6.js +490 -0
- package/dist/chunk-YQR6E6OV.js +109 -0
- package/dist/{chunk-XCK2YAY5.js → chunk-YVZJ6GKF.js} +3 -2
- package/dist/{chunk-2MNPHL23.js → chunk-ZHAUSEEN.js} +38 -15
- package/dist/claude-launcher-FPPHZPMR.js +115 -0
- package/dist/{cli-A76AXELQ.js → cli-HROW5T7X.js} +567 -285
- package/dist/{command-R2QCVX55.js → command-OEAV7BVX.js} +42 -40
- package/dist/{config-N3C6DFQA.js → config-CLSGABD5.js} +2 -3
- package/dist/{connect-cmd-JBKAQNSF.js → connect-cmd-UNYKGDAL.js} +10 -9
- package/dist/context-cleanup-HSVYDGMI.js +9 -0
- package/dist/control-plane-launch-NYVDJESK.js +216 -0
- package/dist/{dist-LKDWQWZ7.js → dist-WO7NOJNG.js} +2 -2
- package/dist/{docker-4J77Q2BF.js → docker-BFU3AQGY.js} +7 -8
- package/dist/{doctor-4TAIARIG.js → doctor-23BBJWBX.js} +48 -42
- package/dist/{emit-artifact-cmd-MPZVR56N.js → emit-artifact-cmd-O6SCM7TV.js} +27 -24
- package/dist/emit-cmd-7SP3I6UZ.js +14 -0
- package/dist/{emit-criteria-cmd-EFGYI23V.js → emit-criteria-cmd-LQNERZRV.js} +17 -16
- package/dist/{emit-finding-cmd-5GF3V4LR.js → emit-finding-cmd-AJPKHIOV.js} +18 -17
- package/dist/{emit-result-cmd-I4OADKMD.js → emit-result-cmd-RUICOKWQ.js} +22 -19
- package/dist/emit-step-cmd-347XNGPQ.js +26 -0
- package/dist/{exec-client-CQV746UF.js → exec-client-7UMESIE2.js} +3 -4
- package/dist/governing-source-V5EEGPTA.js +34 -0
- package/dist/guest-init-KPBQOFE5.js +9 -0
- package/dist/{hook-fast-R4FVSZ2H.js → hook-fast-YAZG5MDX.js} +5 -2
- package/dist/host-key-U4B5QETR.js +8 -0
- package/dist/{import-capture-cmd-EQ35EARA.js → import-capture-cmd-KKFFOGWP.js} +7 -5
- package/dist/{import-ruler-cmd-BGAZXCQM.js → import-ruler-cmd-5P6JOXQN.js} +5 -3
- package/dist/index.js +8 -4
- package/dist/{init-PWU5YVL7.js → init-LWHDMZQG.js} +40 -38
- package/dist/init-MOINY2DD.js +53 -0
- package/dist/{init-cmd-XAVO7HYZ.js → init-cmd-HSMRID6A.js} +75 -30
- package/dist/{init-update-CVCWD6VS.js → init-update-LWX5MPPR.js} +29 -29
- package/dist/{install-HDBIPHTX.js → install-4SEFIIW2.js} +4 -5
- package/dist/{install-cmd-5HYMXLEW.js → install-cmd-QDOTR4OM.js} +5 -4
- package/dist/{login-browser-QZCS3WW6.js → login-browser-DX35MARZ.js} +5 -4
- package/dist/{logout-cmd-QAUFDJ6J.js → logout-cmd-5V5MMBSY.js} +3 -2
- package/dist/{logs-H55NWW4H.js → logs-AMLKC3VP.js} +3 -1
- package/dist/loop-SYYPVFYW.js +77 -0
- package/dist/machine-6X63G2KH.js +48 -0
- package/dist/{mcp-install-cmd-FDTPQQS6.js → mcp-install-cmd-GGWB3JJ2.js} +3 -2
- package/dist/{mcp-serve-cmd-3KVYEUXG.js → mcp-serve-cmd-N3UTY6EE.js} +11 -9
- package/dist/paths-7XP63OAX.js +7 -0
- package/dist/pending-532ABZYC.js +8 -0
- package/dist/{pidfile-UZRH774M.js → pidfile-3DM4OBKO.js} +4 -2
- package/dist/{record-C43U4PKS.js → record-3WP6V2K5.js} +3 -2
- package/dist/{runner-env-EN7YE7QX.js → runner-env-FRZFFHDF.js} +5 -6
- package/dist/{runner-key-6SLBI7PP.js → runner-key-276SJU5R.js} +40 -38
- package/dist/session-sender-GGIY5OAX.js +36 -0
- package/dist/{pending-MXLSRYMO.js → shell-function-NRD4KVUK.js} +1 -2
- package/dist/spec-cmd-SIZBRN7D.js +115 -0
- package/dist/{starter-pack-KF66HGZR.js → starter-pack-6LQVLBIA.js} +7 -5
- package/dist/{status-7VNQN5PW.js → status-NAK6DYSF.js} +46 -42
- package/dist/{statusline-fast-SVTMBMD7.js → statusline-fast-KN3BX67H.js} +2 -1
- package/dist/sync-cmd-G7GF4UTW.js +35 -0
- package/dist/{watch-23HSAEI6.js → watch-EVM5GSMP.js} +22 -15
- package/dist/{work-claim-cmd-LKL3GHFQ.js → work-claim-cmd-3J3QW56U.js} +26 -29
- package/dist/work-cmd-5EPPD33Y.js +24 -0
- package/dist/{work-comment-cmd-AWHP56M2.js → work-comment-cmd-2ACOZQAE.js} +16 -15
- package/dist/{work-launch-R5HTAP6X.js → work-launch-LEQ2MPM3.js} +3 -1
- package/dist/work-logs-cmd-QDD2NMJL.js +60 -0
- package/dist/{work-open-pr-cmd-IOM4AVQQ.js → work-open-pr-cmd-XNCEI4ZM.js} +18 -17
- package/dist/work-pr-cmd-H4OQR26N.js +25 -0
- package/package.json +3 -2
- package/skill-pack/maintained/outerlayer-amend/SKILL.md +4 -2
- package/skill-pack/maintained/outerlayer-cli/SKILL.md +9 -2
- package/skill-pack/maintained/outerlayer-cli/references/find-out-why-a-build-ended.md +76 -0
- package/skill-pack/shipped.json +18 -2
- package/skill-pack/template/build/SKILL.md +23 -10
- package/skill-pack/template/build/references/agent-briefs.md +3 -1
- package/skill-pack/template/build/references/criteria-judge.md +27 -0
- package/skill-pack/template/build/references/release.md +22 -29
- package/skill-pack/template/spec/SKILL.md +35 -10
- package/skill-pack/validators/acceptance-criteria.yaml +25 -26
- package/dist/agent-setup-T624CWPD.js +0 -27
- package/dist/chunk-6Y5RUTHO.js +0 -39
- package/dist/chunk-BYT5O5RV.js +0 -61
- package/dist/chunk-WTE3MQML.js +0 -133
- package/dist/chunk-XCCLFVXM.js +0 -401
- package/dist/chunk-YZ5F4PIS.js +0 -797
- package/dist/context-adopt-NFHCLVUB.js +0 -108
- package/dist/context-materialize-IV2TUUFO.js +0 -22
- package/dist/emit-cmd-IJ3GVRFD.js +0 -12
- package/dist/guest-init-LJP5GPZF.js +0 -9
- package/dist/host-key-YMLXY6QB.js +0 -7
- package/dist/init-6XPDRDTC.js +0 -51
- package/dist/loop-FK3LPMMZ.js +0 -73
- package/dist/machine-SQXEBAM2.js +0 -46
- package/dist/paths-OYKMVYJP.js +0 -6
- package/dist/session-sender-FO3LCREV.js +0 -29
- package/dist/sync-cmd-GVPPFZ3G.js +0 -28
- package/dist/work-cmd-Z2WXLZPH.js +0 -23
- package/dist/work-pr-cmd-HNEUIEN7.js +0 -24
|
@@ -1,37 +1,40 @@
|
|
|
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-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
6
|
-
import './chunk-
|
|
7
|
-
import './chunk-
|
|
8
|
-
import './chunk-
|
|
3
|
+
import { gatherDoctorChecks, doctorExitCode, summarizeChecks, renderDoctorText, recordWrapped, recordUnwrapped, recordRemoval, readManifest, recordInstall, recordStatusline } from './chunk-5TN7GLYX.js';
|
|
4
|
+
import './chunk-TWF3RICI.js';
|
|
5
|
+
import { verifiedTrackerNames, TRACKERS } from './chunk-HPJNLETG.js';
|
|
6
|
+
import './chunk-IF2OSI5T.js';
|
|
7
|
+
import './chunk-CG36IONK.js';
|
|
8
|
+
import './chunk-G3IJ3N6F.js';
|
|
9
|
+
import './chunk-BM63HB2M.js';
|
|
10
|
+
import { hookFiredMarkerPath, installedCliDistDir } from './chunk-NFIR3GEE.js';
|
|
11
|
+
import './chunk-DSF2FWAZ.js';
|
|
9
12
|
import './chunk-BKO6JEZI.js';
|
|
10
|
-
import './chunk-
|
|
11
|
-
import './chunk-
|
|
12
|
-
import './chunk-
|
|
13
|
-
import './chunk-
|
|
14
|
-
import './chunk-
|
|
15
|
-
import './chunk-4KSQKQXE.js';
|
|
16
|
-
import './chunk-N6LURUEF.js';
|
|
17
|
-
import { C, NO_COLOR_FLAG, warnDeprecated, stripNoColor, API_KEY_FLAG_DEPRECATION } from './chunk-YRYMNBGQ.js';
|
|
13
|
+
import './chunk-QNLX477Q.js';
|
|
14
|
+
import './chunk-YVZJ6GKF.js';
|
|
15
|
+
import './chunk-6X4SQ5MY.js';
|
|
16
|
+
import './chunk-GOSFARMS.js';
|
|
17
|
+
import { C, warnDeprecated, API_KEY_FLAG_DEPRECATION, NO_COLOR_FLAG, stripNoColor } from './chunk-ANPNT6PM.js';
|
|
18
18
|
import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
|
|
19
19
|
import { settingsPath, readSettings, SettingsParseError, listHookCandidates, wrapHooks, writeSettings, unwrapHooks, REGISTERED_EVENTS, cliCommand, removeHooks, removeStatusline, cliBinResolvable, mergeHooks, repointWrappedHooks, AUTO_WRAP_EVENTS, mergeStatusline } from './chunk-5QRP3MVS.js';
|
|
20
|
-
import
|
|
21
|
-
import './chunk-
|
|
22
|
-
import './chunk-
|
|
23
|
-
import './chunk-
|
|
24
|
-
import './chunk-
|
|
25
|
-
import './chunk-
|
|
26
|
-
import './chunk-
|
|
20
|
+
import './chunk-IXYZUUYQ.js';
|
|
21
|
+
import './chunk-FVMYKL6L.js';
|
|
22
|
+
import './chunk-NTRCK4NH.js';
|
|
23
|
+
import './chunk-ZJSNLVCE.js';
|
|
24
|
+
import { findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
25
|
+
import './chunk-G2YNJIGF.js';
|
|
26
|
+
import './chunk-NNLA7LLQ.js';
|
|
27
|
+
import './chunk-WFSUUUAN.js';
|
|
28
|
+
import './chunk-7VUK5RFS.js';
|
|
27
29
|
import './chunk-EABW6AJQ.js';
|
|
28
|
-
import {
|
|
29
|
-
import './chunk-
|
|
30
|
-
import './chunk-
|
|
31
|
-
import { hookFiredMarkerPath, installedCliDistDir } from './chunk-BWQ66M6N.js';
|
|
32
|
-
import { managedHookScripts, removeJournalHooks, installPreCommitGuardHook, removePreCommitGuardHook } from './chunk-XCCLFVXM.js';
|
|
33
|
-
import './chunk-BJ3KTMDH.js';
|
|
30
|
+
import { cliInvocation, packageRunnerOf } from './chunk-CRB3POP4.js';
|
|
31
|
+
import { cloneIsGoverned } from './chunk-YQR6E6OV.js';
|
|
32
|
+
import { removeJournalHooks, removePreCommitGuardHook } from './chunk-TUSYOL2O.js';
|
|
34
33
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
34
|
+
import { resolveVersion } from './chunk-RA3O54FC.js';
|
|
35
|
+
import './chunk-M2ZWWTWZ.js';
|
|
36
|
+
import './chunk-N2PXGQRL.js';
|
|
37
|
+
import './chunk-BQXFBI7T.js';
|
|
35
38
|
import './chunk-Z5GEFMRW.js';
|
|
36
39
|
import { EventEmitter } from 'events';
|
|
37
40
|
import childProcess, { execFileSync } from 'child_process';
|
|
@@ -3400,7 +3403,7 @@ function useColor() {
|
|
|
3400
3403
|
// ../../node_modules/commander/index.js
|
|
3401
3404
|
new Command();
|
|
3402
3405
|
function isEphemeralBin(bin) {
|
|
3403
|
-
return bin
|
|
3406
|
+
return packageRunnerOf(bin) !== void 0;
|
|
3404
3407
|
}
|
|
3405
3408
|
function initHookBin(opts) {
|
|
3406
3409
|
const { runningBin } = opts;
|
|
@@ -3544,7 +3547,6 @@ function runInit(opts) {
|
|
|
3544
3547
|
recordStatusline(manifestHome, path2, statuslineWrappedCommand !== void 0 ? "wrapped" : "installed", statuslineWrappedCommand);
|
|
3545
3548
|
}
|
|
3546
3549
|
const gitHookRemoval = removeJournalHooks(opts.cwd ?? process.cwd());
|
|
3547
|
-
const guardHook = installPreCommitGuardHook(opts.cwd ?? process.cwd());
|
|
3548
3550
|
let gitignoreUpdated = false;
|
|
3549
3551
|
if (scope === "project" && opts.addGitignore) {
|
|
3550
3552
|
gitignoreUpdated = ensureGitignore(opts.cwd ?? process.cwd());
|
|
@@ -3556,7 +3558,6 @@ function runInit(opts) {
|
|
|
3556
3558
|
events: REGISTERED_EVENTS,
|
|
3557
3559
|
gitignoreUpdated,
|
|
3558
3560
|
...gitHookRemoval.removed ? { gitHookRemoved: true } : {},
|
|
3559
|
-
guardHook,
|
|
3560
3561
|
wrapped,
|
|
3561
3562
|
...statusline ? { statusline } : {},
|
|
3562
3563
|
...statuslineWrappedCommand ? { statuslineWrappedCommand } : {}
|
|
@@ -3574,8 +3575,12 @@ function tracksOwnOuterlayerDir(cwd) {
|
|
|
3574
3575
|
return false;
|
|
3575
3576
|
}
|
|
3576
3577
|
}
|
|
3578
|
+
function isGovernedCheckout(cwd) {
|
|
3579
|
+
const dirs = findGitDirs(cwd);
|
|
3580
|
+
return dirs !== void 0 && cloneIsGoverned(dirs.gitCommonDir);
|
|
3581
|
+
}
|
|
3577
3582
|
function ensureGitignore(cwd) {
|
|
3578
|
-
if (tracksOwnOuterlayerDir(cwd) ||
|
|
3583
|
+
if (tracksOwnOuterlayerDir(cwd) || isGovernedCheckout(cwd)) return false;
|
|
3579
3584
|
const gitignore = join(cwd, ".gitignore");
|
|
3580
3585
|
const entry = ".outerlayer/";
|
|
3581
3586
|
let contents = "";
|
|
@@ -3610,11 +3615,12 @@ function orgRolloutSnippet(cliBin) {
|
|
|
3610
3615
|
"managed-settings.json:",
|
|
3611
3616
|
managed,
|
|
3612
3617
|
"",
|
|
3613
|
-
|
|
3618
|
+
`Each developer still runs \`${cliInvocation()} login\` once to attribute sessions.`
|
|
3614
3619
|
].join("\n");
|
|
3615
3620
|
}
|
|
3616
3621
|
|
|
3617
3622
|
// src/cli.ts
|
|
3623
|
+
var TRACKER_OPTION_FLAG = `--tracker <${verifiedTrackerNames(TRACKERS).join("|")}>`;
|
|
3618
3624
|
function resolveCliBin() {
|
|
3619
3625
|
try {
|
|
3620
3626
|
return fileURLToPath(new URL("./index.js", import.meta.url));
|
|
@@ -3640,13 +3646,10 @@ function writeJson(value) {
|
|
|
3640
3646
|
`);
|
|
3641
3647
|
}
|
|
3642
3648
|
function apiKeyOption() {
|
|
3643
|
-
return new Option("--api-key <key>",
|
|
3644
|
-
}
|
|
3645
|
-
function warnIfApiKeyFlag(opts) {
|
|
3646
|
-
if (opts.apiKey !== void 0) warnDeprecated(API_KEY_FLAG_DEPRECATION);
|
|
3649
|
+
return new Option("--api-key <key>", `deprecated: use \`${cliInvocation()} login\` or OUTERLAYER_API_KEY`).hideHelp();
|
|
3647
3650
|
}
|
|
3648
3651
|
async function installStarterPackInRepo() {
|
|
3649
|
-
const { installStarterPack, formatStarterPackOutput } = await import('./starter-pack-
|
|
3652
|
+
const { installStarterPack, formatStarterPackOutput } = await import('./starter-pack-6LQVLBIA.js');
|
|
3650
3653
|
const cwd = process.cwd();
|
|
3651
3654
|
let repoRoot = cwd;
|
|
3652
3655
|
try {
|
|
@@ -3687,7 +3690,7 @@ ${C.dim} backup: ${result.backupPath}${C.reset}` : ""}
|
|
|
3687
3690
|
}
|
|
3688
3691
|
if (result.wrapped.length > 0) {
|
|
3689
3692
|
lines.push(
|
|
3690
|
-
`${C.green}\u2713${C.reset} Auto-wrapped ${result.wrapped.length} hook(s) for execution evidence (adds one spawn per firing \u2014 see ${C.yellow}
|
|
3693
|
+
`${C.green}\u2713${C.reset} Auto-wrapped ${result.wrapped.length} hook(s) for execution evidence (adds one spawn per firing \u2014 see ${C.yellow}${cliInvocation()} hooks unwrap${C.reset} to undo):
|
|
3691
3694
|
`
|
|
3692
3695
|
);
|
|
3693
3696
|
for (const w of result.wrapped) {
|
|
@@ -3729,45 +3732,15 @@ cannot be disabled.
|
|
|
3729
3732
|
(opts.next === false ? "\n" : `
|
|
3730
3733
|
Next: launch a session naming its work item, then sync it:
|
|
3731
3734
|
${C.yellow}OUTERLAYER_WORK=42 claude${C.reset}
|
|
3732
|
-
${C.yellow}
|
|
3735
|
+
${C.yellow}${cliInvocation()} sync${C.reset}
|
|
3733
3736
|
|
|
3734
|
-
`) + (opts.connected ? "This repository is connected to a factory, so launched sessions sync\nautomatically in the background after each agent turn and at the end\nof the session, at most once every five minutes.\n" :
|
|
3737
|
+
`) + (opts.connected ? "This repository is connected to a factory, so launched sessions sync\nautomatically in the background after each agent turn and at the end\nof the session, at most once every five minutes.\n" : `Once you have run ${cliInvocation()} login and ${cliInvocation()} init in this repository,
|
|
3738
|
+
launched sessions sync automatically in the background after each agent
|
|
3739
|
+
turn and at the end of the session, at most once every five minutes.
|
|
3740
|
+
`)
|
|
3735
3741
|
);
|
|
3736
3742
|
return lines.join("");
|
|
3737
3743
|
}
|
|
3738
|
-
function formatGuardInstallOutput(guard) {
|
|
3739
|
-
switch (guard.status) {
|
|
3740
|
-
case "installed":
|
|
3741
|
-
case "updated":
|
|
3742
|
-
case "unchanged":
|
|
3743
|
-
return { lines: [`${C.green}\u2713${C.reset} materialized-context commit guard ${guard.status} \u2192 ${guard.path}`] };
|
|
3744
|
-
case "skipped-foreign":
|
|
3745
|
-
if (guard.adopted) {
|
|
3746
|
-
return { lines: [`${C.green}\u2713${C.reset} materialized-context commit guard hand-adopted into ${guard.path} \u2014 left untouched.`] };
|
|
3747
|
-
}
|
|
3748
|
-
return {
|
|
3749
|
-
lines: [
|
|
3750
|
-
`${C.yellow}!${C.reset} a pre-commit hook we did not write already exists (${guard.path}) \u2014 left untouched.`,
|
|
3751
|
-
` Append the guard script yourself: ${C.yellow}outerlayer hooks install-git --print${C.reset}`,
|
|
3752
|
-
` Place it before any exit in the existing hook \u2014 code after an early exit never runs.`
|
|
3753
|
-
],
|
|
3754
|
-
exitCode: 1
|
|
3755
|
-
};
|
|
3756
|
-
case "skipped-custom-hooks-path":
|
|
3757
|
-
if (guard.adopted) {
|
|
3758
|
-
return { lines: [`${C.green}\u2713${C.reset} materialized-context commit guard hand-adopted at ${guard.path} (core.hooksPath) \u2014 left untouched.`] };
|
|
3759
|
-
}
|
|
3760
|
-
return {
|
|
3761
|
-
lines: [
|
|
3762
|
-
`${C.yellow}!${C.reset} this repo manages hooks in its working tree (core.hooksPath) \u2014 nothing written.`,
|
|
3763
|
-
` Add the script to your hook manager instead: ${C.yellow}outerlayer hooks install-git --print${C.reset}`
|
|
3764
|
-
],
|
|
3765
|
-
exitCode: 1
|
|
3766
|
-
};
|
|
3767
|
-
case "not-a-repo":
|
|
3768
|
-
return { lines: [], error: "not inside a git repository." };
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
3744
|
async function installHooks(opts, resolvedBin, connected, starterTemplate, withNext = true) {
|
|
3772
3745
|
const scope = opts.project ? "project" : "user";
|
|
3773
3746
|
let cliBin;
|
|
@@ -3775,7 +3748,7 @@ async function installHooks(opts, resolvedBin, connected, starterTemplate, withN
|
|
|
3775
3748
|
cliBin = initHookBin({ home: homeFromEnv(), runningBin: resolvedBin, remove: opts.remove === true });
|
|
3776
3749
|
} catch (err) {
|
|
3777
3750
|
const text = `could not copy the CLI to ~/.outerlayer/cli: ${err instanceof Error ? err.message : String(err)}
|
|
3778
|
-
Hooks were not changed. Fix the permissions on ~/.outerlayer, then re-run ${C.yellow}
|
|
3751
|
+
Hooks were not changed. Fix the permissions on ~/.outerlayer, then re-run ${C.yellow}${cliInvocation()} init${C.reset}.`;
|
|
3779
3752
|
return { ok: false, text, detail: "could not copy the CLI to ~/.outerlayer/cli" };
|
|
3780
3753
|
}
|
|
3781
3754
|
try {
|
|
@@ -3789,7 +3762,7 @@ async function installHooks(opts, resolvedBin, connected, starterTemplate, withN
|
|
|
3789
3762
|
});
|
|
3790
3763
|
if (result.cliBinUnresolved) {
|
|
3791
3764
|
const text = `the resolved CLI path (${cliBin}) does not exist \u2014 refusing to write hook entries pointing at it.
|
|
3792
|
-
Reinstall OuterLayer, then re-run ${C.yellow}
|
|
3765
|
+
Reinstall OuterLayer, then re-run ${C.yellow}${cliInvocation()} init${C.reset}.`;
|
|
3793
3766
|
return { ok: false, text, detail: "the CLI path does not exist" };
|
|
3794
3767
|
}
|
|
3795
3768
|
const starter = starterTemplate === void 0 ? null : await installStarterPackInRepo();
|
|
@@ -3806,8 +3779,8 @@ ${starter.output}`),
|
|
|
3806
3779
|
}
|
|
3807
3780
|
}
|
|
3808
3781
|
async function realConnectDepsForCwd() {
|
|
3809
|
-
const { realConnectDeps } = await import('./connect-cmd-
|
|
3810
|
-
const { resolveRepoIdentity } = await import('./dist-
|
|
3782
|
+
const { realConnectDeps } = await import('./connect-cmd-UNYKGDAL.js');
|
|
3783
|
+
const { resolveRepoIdentity } = await import('./dist-WO7NOJNG.js');
|
|
3811
3784
|
return {
|
|
3812
3785
|
...realConnectDeps(),
|
|
3813
3786
|
home: homeFromEnv(),
|
|
@@ -3822,13 +3795,55 @@ function invokedCommandToken(argv) {
|
|
|
3822
3795
|
}
|
|
3823
3796
|
function buildProgram(argv = process.argv) {
|
|
3824
3797
|
const program2 = new Command();
|
|
3825
|
-
program2.name(
|
|
3826
|
-
|
|
3798
|
+
program2.name(cliInvocation()).description("OuterLayer \u2014 see what your coding agents actually do").enablePositionalOptions().version(resolveVersion()).hook("preAction", (_root, command) => {
|
|
3799
|
+
if (command.opts().apiKey !== void 0) warnDeprecated(API_KEY_FLAG_DEPRECATION);
|
|
3800
|
+
}).option(NO_COLOR_FLAG, "disable color (NO_COLOR=1 does the same)").addHelpText("after", SUPPORT_TEXT);
|
|
3801
|
+
program2.command("init").description("Connect this repository, install hooks and agent skills, run doctor").option("--user", "install into user settings [default]").option("--project", "install into project settings (./.claude/settings.json)").option("--org", "print a managed-settings.json snippet for MDM/GPO rollout").option("--remove", "uninstall OuterLayer hooks").option("--local", "install the hooks only: no connect step and no network call").option("--factory <org/factory>", `the factory to connect this repository to (see \`${cliInvocation()} connect\`)`).option("--gitignore", "also add .outerlayer/ to .gitignore (project scope)").option("--template <name>", "install the starter skills and a policy file (only: default)").option("--update", "update the files init wrote that you have not edited, and write missing ones").option("--dry-run", "with --update: print the plan and write nothing").option("--no-wrap-hooks", "leave PreToolUse/PostToolUse hooks unwrapped").option("--statusline", "install the status-line segment [default]").option("--no-statusline", "leave the statusLine slot untouched").option("--shell-function", "make plain claude run `outerlayer claude` (edits ~/.zshrc or ~/.bashrc)").option("--no-shell-function", "do not set up the shell function, and remove it if it is there").option("--json", "machine-readable JSON output").addHelpText(
|
|
3827
3802
|
"after",
|
|
3828
|
-
|
|
3803
|
+
`
|
|
3804
|
+
In a repository, init runs four steps: connect the repository to a
|
|
3805
|
+
factory (\`${cliInvocation()} connect\`), install the hooks, set up the coding agent,
|
|
3806
|
+
and run \`${cliInvocation()} doctor\`. It never signs in. With no
|
|
3807
|
+
login it skips the connect step and says to run \`${cliInvocation()} login\`. With
|
|
3808
|
+
--json it prints one document naming the outcome of each step: done,
|
|
3809
|
+
skipped or failed. The exit code is 1 when a step failed.
|
|
3810
|
+
|
|
3811
|
+
The agent setup step installs the starter skills and the \`outerlayer\` skill
|
|
3812
|
+
into .outerlayer/skills/, writes .outerlayer/config.json for Claude Code when
|
|
3813
|
+
there is none, runs \`${cliInvocation()} context emit\` so the skills appear in
|
|
3814
|
+
.claude/skills/, and adds the outerlayer and outerlayer-docs MCP servers to
|
|
3815
|
+
.mcp.json. Nothing you wrote is changed: an existing config, policy, edited
|
|
3816
|
+
skill, MCP entry or root CLAUDE.md and AGENTS.md is left as it is, and a
|
|
3817
|
+
hand-written file the emit would replace is kept and listed. The files are
|
|
3818
|
+
left uncommitted.
|
|
3819
|
+
The step is skipped outside a git repository and in a repository whose
|
|
3820
|
+
context comes from a control plane, where it arrives at session start.
|
|
3821
|
+
|
|
3822
|
+
--local installs only the hooks: no connect step, no agent setup and no
|
|
3823
|
+
network call. Add \`--template default\` to also write the starter skills and
|
|
3824
|
+
a policy file into .outerlayer/ without compiling them; \`${cliInvocation()} context
|
|
3825
|
+
emit\` does that.
|
|
3826
|
+
|
|
3827
|
+
Idempotent: settings are backed up before any write, and a second run
|
|
3828
|
+
changes nothing. Wrapping adds one spawn per hook firing; undo it with
|
|
3829
|
+
\`${cliInvocation()} hooks unwrap\`. An occupied status-line slot is wrapped, never
|
|
3830
|
+
replaced.
|
|
3831
|
+
|
|
3832
|
+
Three of the starter skills are copied once and are yours to edit. The
|
|
3833
|
+
others are rewritten from this CLI on every \`context emit\`.
|
|
3834
|
+
|
|
3835
|
+
--update brings the files init wrote once up to this CLI's version: the
|
|
3836
|
+
template skills, the policy, the two starter checks and the two MCP
|
|
3837
|
+
entries. A file still equal to a version an earlier release wrote is
|
|
3838
|
+
replaced, and a missing one is written. A file you edited is never
|
|
3839
|
+
touched. It then runs \`${cliInvocation()} context emit\` and leaves the changes
|
|
3840
|
+
uncommitted. --dry-run prints the plan, with a command to compare each
|
|
3841
|
+
edited file, and writes nothing. \`${cliInvocation()} doctor\` reports the same
|
|
3842
|
+
states under Starter files.
|
|
3843
|
+
`
|
|
3829
3844
|
).action(async (opts) => {
|
|
3830
3845
|
if (opts.update || opts.dryRun) {
|
|
3831
|
-
const { runInitUpdate, formatInitUpdate, updateFlagProblem } = await import('./init-update-
|
|
3846
|
+
const { runInitUpdate, formatInitUpdate, updateFlagProblem } = await import('./init-update-LWX5MPPR.js');
|
|
3832
3847
|
const problem = updateFlagProblem(opts);
|
|
3833
3848
|
if (problem !== void 0) {
|
|
3834
3849
|
fail(problem);
|
|
@@ -3842,7 +3857,7 @@ function buildProgram(argv = process.argv) {
|
|
|
3842
3857
|
}
|
|
3843
3858
|
let starterTemplate;
|
|
3844
3859
|
if (opts.template !== void 0) {
|
|
3845
|
-
const { assertKnownTemplate, StarterPackError } = await import('./starter-pack-
|
|
3860
|
+
const { assertKnownTemplate, StarterPackError } = await import('./starter-pack-6LQVLBIA.js');
|
|
3846
3861
|
try {
|
|
3847
3862
|
assertKnownTemplate(opts.template);
|
|
3848
3863
|
} catch (err) {
|
|
@@ -3869,13 +3884,19 @@ function buildProgram(argv = process.argv) {
|
|
|
3869
3884
|
fail(outcome.text);
|
|
3870
3885
|
return;
|
|
3871
3886
|
}
|
|
3887
|
+
if (opts.remove) {
|
|
3888
|
+
const { removeShellFunction } = await import('./shell-function-NRD4KVUK.js');
|
|
3889
|
+
const changed = removeShellFunction({ home: homeFromEnv() });
|
|
3890
|
+
if (changed.length > 0 && !opts.json) process.stdout.write(`Removed the claude function from ${changed.join(", ")}.
|
|
3891
|
+
`);
|
|
3892
|
+
}
|
|
3872
3893
|
if (opts.json) writeJson(outcome.result);
|
|
3873
3894
|
else process.stdout.write(outcome.text);
|
|
3874
3895
|
return;
|
|
3875
3896
|
}
|
|
3876
|
-
const { runInitCommand } = await import('./init-cmd-
|
|
3897
|
+
const { runInitCommand } = await import('./init-cmd-HSMRID6A.js');
|
|
3877
3898
|
const code = await runInitCommand(
|
|
3878
|
-
{ json: opts.json, factory: opts.factory },
|
|
3899
|
+
{ json: opts.json, factory: opts.factory, shellFunction: opts.shellFunction },
|
|
3879
3900
|
{
|
|
3880
3901
|
connect: await realConnectDepsForCwd(),
|
|
3881
3902
|
stdout: process.stdout,
|
|
@@ -3885,19 +3906,55 @@ function buildProgram(argv = process.argv) {
|
|
|
3885
3906
|
return { ok, text, detail };
|
|
3886
3907
|
},
|
|
3887
3908
|
runAgentSetup: async () => {
|
|
3888
|
-
const { runAgentSetup } = await import('./agent-setup-
|
|
3909
|
+
const { runAgentSetup } = await import('./agent-setup-TPR4BBFV.js');
|
|
3889
3910
|
return runAgentSetup({ cwd: process.cwd() });
|
|
3890
3911
|
},
|
|
3891
|
-
runDoctorChecks: gatherDoctorChecks
|
|
3912
|
+
runDoctorChecks: gatherDoctorChecks,
|
|
3913
|
+
shell: {
|
|
3914
|
+
home: homeFromEnv(),
|
|
3915
|
+
shellPath: process.env.SHELL,
|
|
3916
|
+
interactive: Boolean(process.stdin.isTTY && process.stderr.isTTY) && opts.json !== true,
|
|
3917
|
+
ask: async () => {
|
|
3918
|
+
const { promptOnTerminal } = await import('./connect-cmd-UNYKGDAL.js');
|
|
3919
|
+
const answer = await promptOnTerminal({
|
|
3920
|
+
message: `Should plain claude start Claude Code through ${cliInvocation()} claude? That is how a session in a governed repository gets its control plane. It adds one block to your zsh or bash startup file.`,
|
|
3921
|
+
options: [
|
|
3922
|
+
{ label: "Yes, add the block", value: "yes" },
|
|
3923
|
+
{ label: "No, I will run outerlayer claude myself", value: "no" }
|
|
3924
|
+
]
|
|
3925
|
+
});
|
|
3926
|
+
return answer === "yes";
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3892
3929
|
}
|
|
3893
3930
|
);
|
|
3894
3931
|
if (code !== 0) process.exitCode = code;
|
|
3895
3932
|
});
|
|
3896
3933
|
program2.command("connect").description("Choose the factory this repository's work goes to").option("--factory <org/factory>", "the factory to use: <org>/<factory> as in its dashboard address, or a factory id").option("--json", "write one JSON document to stdout; the text goes to stderr").addHelpText(
|
|
3897
3934
|
"after",
|
|
3898
|
-
|
|
3935
|
+
`
|
|
3936
|
+
Run it in a repository, after \`${cliInvocation()} login\`. With a login, connect lists
|
|
3937
|
+
the factories you can use and takes the first rule that leaves one answer:
|
|
3938
|
+
1. --factory <org>/<factory>, or a factory id
|
|
3939
|
+
2. the factory this repository is already connected to
|
|
3940
|
+
3. the one factory this repository is linked to
|
|
3941
|
+
4. the one factory you can use
|
|
3942
|
+
5. on a terminal, ask; with no terminal, exit 1 and list each --factory value
|
|
3943
|
+
|
|
3944
|
+
It says what it chose and why. The choice is saved in this repository's git
|
|
3945
|
+
directory (outerlayer/connection.json), shared by every worktree of the
|
|
3946
|
+
clone and never committed. A factory key already belongs to one factory, so
|
|
3947
|
+
connect changes nothing for it. Connect never links a repository.
|
|
3948
|
+
|
|
3949
|
+
After it saves the choice, connect checks that this repository is linked to
|
|
3950
|
+
the factory. If it is not, connect says so, prints the factory's Work page
|
|
3951
|
+
link where you link it, and exits 2. The choice is saved either way. Run
|
|
3952
|
+
connect again once the repository is linked. With --json, the document has
|
|
3953
|
+
linked (true, false, or null when it could not be checked) and, when false,
|
|
3954
|
+
linkUrl.
|
|
3955
|
+
`
|
|
3899
3956
|
).action(async (opts) => {
|
|
3900
|
-
const { runConnectCommand } = await import('./connect-cmd-
|
|
3957
|
+
const { runConnectCommand } = await import('./connect-cmd-UNYKGDAL.js');
|
|
3901
3958
|
const code = await runConnectCommand({ factory: opts.factory, json: opts.json }, await realConnectDepsForCwd(), {
|
|
3902
3959
|
stdout: process.stdout,
|
|
3903
3960
|
stderr: process.stderr
|
|
@@ -3915,9 +3972,28 @@ function buildProgram(argv = process.argv) {
|
|
|
3915
3972
|
});
|
|
3916
3973
|
program2.command("login").description("Sign in to your account by browser approval, or save a piped factory key").option("--dashboard <address>", "the dashboard to sign in on (default: https://app.outerlayer.ai)").option("--no-browser", "print the approve link without opening a browser").option("--check <id>", "check a login that is still waiting for approval, once").option("--url <url>", "gateway base URL for a piped key (browser login gets it from the dashboard)").option("--app-id <id>", "factory id a piped key is bound to (Settings \u2192 General)").option("--no-input", "never wait on a person; fail if the key is not on stdin").option("--json", "machine-readable JSON output").addHelpText(
|
|
3917
3974
|
"after",
|
|
3918
|
-
|
|
3975
|
+
`
|
|
3976
|
+
With no key on stdin, login prints an approve link and a code. A signed-in
|
|
3977
|
+
person approves it on the dashboard. The login is a token for that person's
|
|
3978
|
+
account, made for this machine, saved here and never printed. It picks no
|
|
3979
|
+
factory: run \`${cliInvocation()} connect\` for that. \`${cliInvocation()} logout\` revokes it.
|
|
3980
|
+
|
|
3981
|
+
When stdout is not a terminal, login does not wait. It prints one JSON
|
|
3982
|
+
document with the link, the code and a \`login --check <id>\` command, and
|
|
3983
|
+
exits 75. Run the check until it stops exiting 75: 0 is signed in, 1 is
|
|
3984
|
+
denied or expired. A dropped connection or a 5xx also exits 75.
|
|
3985
|
+
|
|
3986
|
+
A factory key piped on stdin is saved as it always was, never as a flag and
|
|
3987
|
+
never printed back. A factory key always wins over the login, so CI and
|
|
3988
|
+
runners keep acting as their key.
|
|
3989
|
+
|
|
3990
|
+
echo "$OUTERLAYER_KEY" | ${cliInvocation()} login --url <gateway url> --app-id <factory id>
|
|
3991
|
+
|
|
3992
|
+
Every later command reads the saved file, so none of them needs a key on
|
|
3993
|
+
its command line. A saved key is checked for real on the first \`sync\`.
|
|
3994
|
+
`
|
|
3919
3995
|
).action(async (opts) => {
|
|
3920
|
-
const { runLoginCommand, realLoginDeps } = await import('./login-browser-
|
|
3996
|
+
const { runLoginCommand, realLoginDeps } = await import('./login-browser-DX35MARZ.js');
|
|
3921
3997
|
const code = await runLoginCommand(
|
|
3922
3998
|
{
|
|
3923
3999
|
browser: opts.browser !== false,
|
|
@@ -3934,9 +4010,14 @@ function buildProgram(argv = process.argv) {
|
|
|
3934
4010
|
});
|
|
3935
4011
|
program2.command("logout").description("Revoke this machine's login and remove it").addHelpText(
|
|
3936
4012
|
"after",
|
|
3937
|
-
|
|
4013
|
+
`
|
|
4014
|
+
Revokes the token \`${cliInvocation()} login\` saved, then deletes it from the config
|
|
4015
|
+
file. A factory key and the chosen factory are left alone. When the
|
|
4016
|
+
dashboard cannot be reached the local copy is still deleted and the exit
|
|
4017
|
+
code is 1: revoke the token on /profile/security.
|
|
4018
|
+
`
|
|
3938
4019
|
).action(async () => {
|
|
3939
|
-
const { runLogoutCommand, realLogoutDeps } = await import('./logout-cmd-
|
|
4020
|
+
const { runLogoutCommand, realLogoutDeps } = await import('./logout-cmd-5V5MMBSY.js');
|
|
3940
4021
|
const code = await runLogoutCommand(realLogoutDeps());
|
|
3941
4022
|
if (code !== 0) process.exitCode = code;
|
|
3942
4023
|
});
|
|
@@ -3944,15 +4025,14 @@ function buildProgram(argv = process.argv) {
|
|
|
3944
4025
|
"after",
|
|
3945
4026
|
"\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"
|
|
3946
4027
|
).action(async (opts) => {
|
|
3947
|
-
warnIfApiKeyFlag(opts);
|
|
3948
4028
|
if (opts.link !== void 0) {
|
|
3949
|
-
const { lookupArtifactLink } = await import('./artifact-link-cmd-
|
|
4029
|
+
const { lookupArtifactLink } = await import('./artifact-link-cmd-W7XF5BFB.js');
|
|
3950
4030
|
const found = lookupArtifactLink(opts.link);
|
|
3951
4031
|
if (found.ok) process.stdout.write(found.url + "\n");
|
|
3952
4032
|
else fail(found.reason);
|
|
3953
4033
|
return;
|
|
3954
4034
|
}
|
|
3955
|
-
const { runSync, SyncConfigError, SyncTransportError } = await import('./sync-cmd-
|
|
4035
|
+
const { runSync, SyncConfigError, SyncTransportError } = await import('./sync-cmd-G7GF4UTW.js');
|
|
3956
4036
|
try {
|
|
3957
4037
|
const result = await runSync({
|
|
3958
4038
|
url: opts.url,
|
|
@@ -3977,11 +4057,13 @@ function buildProgram(argv = process.argv) {
|
|
|
3977
4057
|
throw err;
|
|
3978
4058
|
}
|
|
3979
4059
|
});
|
|
3980
|
-
program2.command("daemon").alias("watch").description("Run the copy-out daemon that mirrors transcripts before they are deleted").option("--once", "do a single mirror sweep and exit").addHelpText("after",
|
|
4060
|
+
program2.command("daemon").alias("watch").description("Run the copy-out daemon that mirrors transcripts before they are deleted").option("--once", "do a single mirror sweep and exit").addHelpText("after", `
|
|
4061
|
+
\`${cliInvocation()} watch\` is the former name and still works, with a warning.
|
|
4062
|
+
`).action(async (opts) => {
|
|
3981
4063
|
if (invokedCommandToken(argv) === "watch") {
|
|
3982
|
-
warnDeprecated(
|
|
4064
|
+
warnDeprecated(`\`${cliInvocation()} watch\` is now \`${cliInvocation()} daemon\`; the old name will be removed in a future release.`);
|
|
3983
4065
|
}
|
|
3984
|
-
const { runWatch } = await import('./watch-
|
|
4066
|
+
const { runWatch } = await import('./watch-EVM5GSMP.js');
|
|
3985
4067
|
await runWatch({ once: opts.once });
|
|
3986
4068
|
});
|
|
3987
4069
|
program2.command("hook <event>", { hidden: true }).description("capture-hook fast path, installed by `init`").action(() => {
|
|
@@ -3990,6 +4072,8 @@ function buildProgram(argv = process.argv) {
|
|
|
3990
4072
|
});
|
|
3991
4073
|
program2.command("hook-wrap", { hidden: true }).description("runs a hook wrapped by `hooks wrap`, recording exec evidence").option("--id <base64>", "the original hook command, base64-encoded").option("--capture-output", "tee stdout/stderr into a bounded tail").action(() => {
|
|
3992
4074
|
});
|
|
4075
|
+
program2.command("claude [args...]").description("start Claude Code with the repository's control plane attached").allowUnknownOption().helpOption(false).action(() => {
|
|
4076
|
+
});
|
|
3993
4077
|
program2.command("broker-relay", { hidden: true }).description("relay a build's loopback connections to the runner's tunnel and destinations").requiredOption("--port <port>", "the loopback port to listen on").requiredOption("--socket <path>", "the runner's tunnel socket").option("--forward <port=socket>", "also relay a loopback port to a destination's socket; repeatable", (value, previous) => [...previous, value], []).action(async (opts) => {
|
|
3994
4078
|
const { startRelay } = await import('./relay-L76HDX72.js');
|
|
3995
4079
|
const port = Number(opts.port);
|
|
@@ -4010,7 +4094,7 @@ function buildProgram(argv = process.argv) {
|
|
|
4010
4094
|
await startRelay({ port, socketPath: opts.socket, forwards });
|
|
4011
4095
|
});
|
|
4012
4096
|
program2.command("vm-exec", { hidden: true }).description("run a command in a microVM build's guest through its exec agent").requiredOption("--socket <path>", "the Unix socket Firecracker serves the build's vsock device on").option("--user <user>", "the user to run as", "").option("--cwd <dir>", "the working directory", "/").option("--env <NAME=value>", "an environment value the command line carries; repeatable", (value, previous) => [...previous, value], []).option("--pass <NAME>", "an environment variable whose value is read from this process; repeatable", (value, previous) => [...previous, value], []).argument("<argv...>", "the command and its arguments").action(async (argv2, opts) => {
|
|
4013
|
-
const { runVmExec } = await import('./exec-client-
|
|
4097
|
+
const { runVmExec } = await import('./exec-client-7UMESIE2.js');
|
|
4014
4098
|
const env = {};
|
|
4015
4099
|
for (const entry of opts.env) {
|
|
4016
4100
|
const eq = entry.indexOf("=");
|
|
@@ -4035,7 +4119,7 @@ function buildProgram(argv = process.argv) {
|
|
|
4035
4119
|
}
|
|
4036
4120
|
});
|
|
4037
4121
|
program2.command("session-sender", { hidden: true }).description("stream a runner build's sessions to its work item while the build runs").action(async () => {
|
|
4038
|
-
const { runSessionSender } = await import('./session-sender-
|
|
4122
|
+
const { runSessionSender } = await import('./session-sender-GGIY5OAX.js');
|
|
4039
4123
|
try {
|
|
4040
4124
|
await runSessionSender();
|
|
4041
4125
|
} catch (err) {
|
|
@@ -4043,7 +4127,7 @@ function buildProgram(argv = process.argv) {
|
|
|
4043
4127
|
}
|
|
4044
4128
|
});
|
|
4045
4129
|
program2.command("vm-init", { hidden: true }).description("lay out a microVM build's guest; the guest helper runs it").action(async () => {
|
|
4046
|
-
const { realGuestHost, runGuestInit } = await import('./guest-init-
|
|
4130
|
+
const { realGuestHost, runGuestInit } = await import('./guest-init-KPBQOFE5.js');
|
|
4047
4131
|
const { readFileSync: readFileSync3 } = await import('fs');
|
|
4048
4132
|
try {
|
|
4049
4133
|
runGuestInit(realGuestHost(), readFileSync3("/proc/cmdline", "utf8"));
|
|
@@ -4057,6 +4141,7 @@ function buildProgram(argv = process.argv) {
|
|
|
4057
4141
|
registerMcpCommands(program2);
|
|
4058
4142
|
registerContextCommands(program2);
|
|
4059
4143
|
registerWorkCommands(program2);
|
|
4144
|
+
registerSpecCommands(program2);
|
|
4060
4145
|
registerRunnerCommands(program2);
|
|
4061
4146
|
return program2;
|
|
4062
4147
|
}
|
|
@@ -4070,7 +4155,7 @@ async function runCli(processArgv) {
|
|
|
4070
4155
|
await buildProgram(argv).parseAsync(argv);
|
|
4071
4156
|
}
|
|
4072
4157
|
async function compileContext(opts) {
|
|
4073
|
-
const { runEmit, EmitError } = await import('./emit-cmd-
|
|
4158
|
+
const { runEmit, EmitError } = await import('./emit-cmd-7SP3I6UZ.js');
|
|
4074
4159
|
try {
|
|
4075
4160
|
const result = runEmit({ cwd: opts.dir, check: opts.check, json: opts.json });
|
|
4076
4161
|
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
@@ -4085,15 +4170,39 @@ async function compileContext(opts) {
|
|
|
4085
4170
|
function registerEmitCommands(program2) {
|
|
4086
4171
|
const emitCmd = program2.command("emit").enablePositionalOptions().description("Record evidence for a work item: a named check result").argument("[name]", "the check name a validator declares (e.g. smoke.pass)").option("--link <url>", "proof link, usually the CI run URL").option("--body <text>", "one sentence recording your judgment").option("--body-file <path>", "read the sentence from a file; - reads standard input").option("--result <pass|fail>", "check outcome (default: pass)").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)").addOption(new Option("--check", "deprecated: use `context emit --check`").hideHelp()).addOption(new Option("--dir <path>", "deprecated: use `context emit --dir`").hideHelp()).addHelpText(
|
|
4087
4172
|
"after",
|
|
4088
|
-
|
|
4173
|
+
`
|
|
4174
|
+
${cliInvocation()} emit smoke.pass --item 412 --link https://ci.example.com/run/42
|
|
4175
|
+
${cliInvocation()} emit code-review --result fail --item 412 --body "The button should be red."
|
|
4176
|
+
|
|
4177
|
+
Records that a named check passed or failed, anchored to a work item.
|
|
4178
|
+
--item is always required, in or out of a recorded session \u2014 there is no
|
|
4179
|
+
session fallback. A check that ran carries the run URL as proof (--link);
|
|
4180
|
+
a judgment you are making yourself carries one sentence (--body, or
|
|
4181
|
+
--body-file to read it from a file or standard input). A fail needs at
|
|
4182
|
+
least one of the two. Run from inside a session, the check is stored as
|
|
4183
|
+
the session's; a session cannot record over a person's fail. With
|
|
4184
|
+
nothing to attach it to, the command refuses.
|
|
4185
|
+
|
|
4186
|
+
Who recorded a check is decided by your API key, never by what you send:
|
|
4187
|
+
a key bound to your membership records it as yours, and nothing automated
|
|
4188
|
+
can then clear it.
|
|
4189
|
+
|
|
4190
|
+
A person's pass or fail on one criterion's proof is recorded on that
|
|
4191
|
+
criterion's thread instead \u2014 see \`${cliInvocation()} work comment\`.
|
|
4192
|
+
|
|
4193
|
+
Subcommands: \`emit artifact <file>\` for screenshots, recordings, reports and
|
|
4194
|
+
logs; \`emit finding\` / \`emit findings <file>\` to record a problem in the factory;
|
|
4195
|
+
\`emit criteria <file>\` to record a work item's acceptance criteria;
|
|
4196
|
+
\`emit step <name>\` to report that you started a step.
|
|
4197
|
+
Compiling .outerlayer/ moved to \`${cliInvocation()} context emit\`.
|
|
4198
|
+
`
|
|
4089
4199
|
).action(async (name, opts) => {
|
|
4090
4200
|
if (name === void 0) {
|
|
4091
|
-
warnDeprecated(
|
|
4201
|
+
warnDeprecated(`\`${cliInvocation()} emit\` with no name now lives at \`${cliInvocation()} context emit\`; the bare form will be removed in a future release.`);
|
|
4092
4202
|
await compileContext({ dir: opts.dir, check: opts.check, json: opts.json });
|
|
4093
4203
|
return;
|
|
4094
4204
|
}
|
|
4095
|
-
|
|
4096
|
-
const { runEmitResult, EmitResultError } = await import('./emit-result-cmd-I4OADKMD.js');
|
|
4205
|
+
const { runEmitResult, EmitResultError } = await import('./emit-result-cmd-RUICOKWQ.js');
|
|
4097
4206
|
try {
|
|
4098
4207
|
const result = await runEmitResult({
|
|
4099
4208
|
name,
|
|
@@ -4123,10 +4232,10 @@ function registerEmitCommands(program2) {
|
|
|
4123
4232
|
).option("--replaces <ids>", "artifact id(s) this one corrects; comma-separate 2-20 to retire them all").option("--pr <number>", "pull request number to anchor to").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(
|
|
4124
4233
|
"after",
|
|
4125
4234
|
`
|
|
4126
|
-
|
|
4235
|
+
${cliInvocation()} emit artifact shot.png --caption "the dot row, current dot marked" --for LOGIN-01
|
|
4127
4236
|
|
|
4128
4237
|
Inside a recorded Claude Code session the artifact spools locally and ships,
|
|
4129
|
-
bound to that session, on the next
|
|
4238
|
+
bound to that session, on the next \`${cliInvocation()} sync\` \u2014 but only if that
|
|
4130
4239
|
session was launched with OUTERLAYER_WORK; without it nothing the session
|
|
4131
4240
|
produces may leave the machine, and the command refuses rather than queue an
|
|
4132
4241
|
artifact that can never ship. Otherwise it uploads immediately, anchored to a
|
|
@@ -4143,8 +4252,7 @@ everywhere, bytes and deep link untouched; a still-queued one is cancelled
|
|
|
4143
4252
|
locally so it never uploads. One correction can retire several duplicates.
|
|
4144
4253
|
`
|
|
4145
4254
|
).action(async (file, opts) => {
|
|
4146
|
-
|
|
4147
|
-
const { runEmitArtifact, EmitArtifactError } = await import('./emit-artifact-cmd-MPZVR56N.js');
|
|
4255
|
+
const { runEmitArtifact, EmitArtifactError } = await import('./emit-artifact-cmd-O6SCM7TV.js');
|
|
4148
4256
|
let pr;
|
|
4149
4257
|
if (opts.pr !== void 0) {
|
|
4150
4258
|
pr = parseTrackerNumber(opts.pr);
|
|
@@ -4176,10 +4284,34 @@ locally so it never uploads. One correction can retire several duplicates.
|
|
|
4176
4284
|
});
|
|
4177
4285
|
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 hit the problem (default: first output showing --error)").option("--ended-at <event>", "event id where you got past it (default: the newest event)").option("--untimed", "record no time, only when the problem cost none in this session").option("--find <text>", "list this session's events that contain the text, and record nothing").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(
|
|
4178
4286
|
"after",
|
|
4179
|
-
|
|
4287
|
+
`
|
|
4288
|
+
${cliInvocation()} emit finding --id gate-skip --category tooling \\
|
|
4289
|
+
--title "The pre-push gate prints green when a step was skipped" \\
|
|
4290
|
+
--file scripts/git/pre-push-checks.mjs --where "build session"
|
|
4291
|
+
|
|
4292
|
+
Records one problem you hit in the factory: an instruction that is wrong or
|
|
4293
|
+
missing (context), a flaky test, a misbehaving script (tooling), a local
|
|
4294
|
+
setup trap (environment), a bug outside your own change (defect), or
|
|
4295
|
+
anything else. A problem in your own change is fixed, not recorded.
|
|
4296
|
+
A context finding needs --rule-path and --rule-relation, and --rule-quote
|
|
4297
|
+
unless the relation is missing. Add --cause, --detail and --suggestion, and
|
|
4298
|
+
--command, --error (first error line), --test or --symbol when there is one.
|
|
4299
|
+
|
|
4300
|
+
Every finding joins a factory-level problem. When one of its keys matches a
|
|
4301
|
+
known problem it joins that problem. Otherwise nothing is stored: the command
|
|
4302
|
+
prints the closest problems and exits with code 3. Run it again with
|
|
4303
|
+
--problem <id> --link-reason <why> (repeat --problem for each problem the
|
|
4304
|
+
finding reports), or with --new if none of them is the same problem.
|
|
4305
|
+
A finding records the time it cost, read from two events of your session or
|
|
4306
|
+
its subagents. The start is --started-at, else the first tool output that
|
|
4307
|
+
shows --error. The end is --ended-at, else the newest event. When the session
|
|
4308
|
+
has events and no start is found, nothing is stored: the command lists the
|
|
4309
|
+
recent events with their ids. --find <text> searches all of them.
|
|
4310
|
+
--item names the work item; without it, inside a recorded session, the
|
|
4311
|
+
item that session was launched for is used automatically.
|
|
4312
|
+
`
|
|
4180
4313
|
).action(async (opts) => {
|
|
4181
|
-
|
|
4182
|
-
const { runEmitFinding, refuseRemovedFindingFlags, EmitFindingError } = await import('./emit-finding-cmd-5GF3V4LR.js');
|
|
4314
|
+
const { runEmitFinding, refuseRemovedFindingFlags, EmitFindingError } = await import('./emit-finding-cmd-AJPKHIOV.js');
|
|
4183
4315
|
try {
|
|
4184
4316
|
refuseRemovedFindingFlags(opts);
|
|
4185
4317
|
const result = await runEmitFinding({
|
|
@@ -4229,10 +4361,24 @@ locally so it never uploads. One correction can retire several duplicates.
|
|
|
4229
4361
|
});
|
|
4230
4362
|
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(
|
|
4231
4363
|
"after",
|
|
4232
|
-
|
|
4364
|
+
`
|
|
4365
|
+
${cliInvocation()} emit findings review.json --item 412
|
|
4366
|
+
|
|
4367
|
+
Records a whole batch at once \u2014 a FindingBatch JSON file (schemaVersion 3,
|
|
4368
|
+
optional headSha / contextSourceSha, and 1-500 findings), validated against
|
|
4369
|
+
the same contract \`emit finding\` uses before anything is sent. Re-emitting
|
|
4370
|
+
an id already on the item replaces that row rather than adding a second one.
|
|
4371
|
+
Findings that need a choice between candidate problems are listed and not
|
|
4372
|
+
stored; the rest are stored, and the exit code is 3.
|
|
4373
|
+
Each finding records its time as \`emit finding\` does: "startedAt" is an event
|
|
4374
|
+
id, else the first tool output that shows its "error"; "endedAt" defaults to
|
|
4375
|
+
the newest event. A finding with neither, in a session that has events, stops
|
|
4376
|
+
the whole batch. "untimed": true records one with no time on purpose.
|
|
4377
|
+
--item names the work item; without it, inside a recorded session, the item
|
|
4378
|
+
that session was launched for is used automatically.
|
|
4379
|
+
`
|
|
4233
4380
|
).action(async (file, opts) => {
|
|
4234
|
-
|
|
4235
|
-
const { runEmitFindings, EmitFindingError } = await import('./emit-finding-cmd-5GF3V4LR.js');
|
|
4381
|
+
const { runEmitFindings, EmitFindingError } = await import('./emit-finding-cmd-AJPKHIOV.js');
|
|
4236
4382
|
try {
|
|
4237
4383
|
const result = await runEmitFindings({
|
|
4238
4384
|
file,
|
|
@@ -4251,10 +4397,44 @@ locally so it never uploads. One correction can retire several duplicates.
|
|
|
4251
4397
|
throw err;
|
|
4252
4398
|
}
|
|
4253
4399
|
});
|
|
4400
|
+
emitCmd.command("step <name>").description("Report that you started a step; the work item's Activity tab shows it").option("--item <number>", "work item to report on, 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(
|
|
4401
|
+
"after",
|
|
4402
|
+
`
|
|
4403
|
+
${cliInvocation()} emit step "Review round 2"
|
|
4404
|
+
${cliInvocation()} emit step "Plan" --item 412
|
|
4405
|
+
|
|
4406
|
+
Run it when you start a step. The name is free text: 1 to 80 characters on one
|
|
4407
|
+
line. A step has a start and nothing else, so run it again for the next step.
|
|
4408
|
+
--item names the work item; without it, inside a recorded session, the item that
|
|
4409
|
+
session was launched for is used.
|
|
4410
|
+
|
|
4411
|
+
If the gateway cannot be reached, the step is queued with the time you ran the
|
|
4412
|
+
command, the command exits 0, and \`sync\` sends it later. If the gateway refuses
|
|
4413
|
+
the step, the command prints why and exits 1.
|
|
4414
|
+
`
|
|
4415
|
+
).action(async (name, opts) => {
|
|
4416
|
+
const { runEmitStep, EmitStepError } = await import('./emit-step-cmd-347XNGPQ.js');
|
|
4417
|
+
try {
|
|
4418
|
+
await runEmitStep({
|
|
4419
|
+
name,
|
|
4420
|
+
item: opts.item,
|
|
4421
|
+
json: opts.json,
|
|
4422
|
+
url: opts.url,
|
|
4423
|
+
apiKey: opts.apiKey,
|
|
4424
|
+
appId: opts.appId
|
|
4425
|
+
});
|
|
4426
|
+
} catch (err) {
|
|
4427
|
+
if (err instanceof EmitStepError) {
|
|
4428
|
+
fail(err.message);
|
|
4429
|
+
return;
|
|
4430
|
+
}
|
|
4431
|
+
throw err;
|
|
4432
|
+
}
|
|
4433
|
+
});
|
|
4254
4434
|
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(
|
|
4255
4435
|
"after",
|
|
4256
4436
|
`
|
|
4257
|
-
|
|
4437
|
+
${cliInvocation()} emit criteria criteria.json --item 412
|
|
4258
4438
|
|
|
4259
4439
|
Records the item's acceptance criteria as one list. The file is JSON:
|
|
4260
4440
|
{ "criteria": [ { "id": "AC-1", "text": "Given ...", "proof": null },
|
|
@@ -4271,8 +4451,7 @@ agent session, with your own login. A key bound to no member cannot replace it e
|
|
|
4271
4451
|
session was launched for is used automatically.
|
|
4272
4452
|
`
|
|
4273
4453
|
).action(async (file, opts) => {
|
|
4274
|
-
|
|
4275
|
-
const { runEmitCriteria, EmitCriteriaError } = await import('./emit-criteria-cmd-EFGYI23V.js');
|
|
4454
|
+
const { runEmitCriteria, EmitCriteriaError } = await import('./emit-criteria-cmd-LQNERZRV.js');
|
|
4276
4455
|
try {
|
|
4277
4456
|
const result = await runEmitCriteria({
|
|
4278
4457
|
file,
|
|
@@ -4292,14 +4471,54 @@ session was launched for is used automatically.
|
|
|
4292
4471
|
}
|
|
4293
4472
|
});
|
|
4294
4473
|
}
|
|
4474
|
+
function registerSpecCommands(program2) {
|
|
4475
|
+
const specCmd = program2.command("spec").description("Find, file and edit the issue a spec lives in, in your factory's tracker");
|
|
4476
|
+
const credentialOptions = (command) => command.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)");
|
|
4477
|
+
const run = async (action) => {
|
|
4478
|
+
const { WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4479
|
+
try {
|
|
4480
|
+
await action(await import('./spec-cmd-SIZBRN7D.js'));
|
|
4481
|
+
} catch (err) {
|
|
4482
|
+
if (err instanceof WorkCommandError) {
|
|
4483
|
+
fail(err.message);
|
|
4484
|
+
return;
|
|
4485
|
+
}
|
|
4486
|
+
throw err;
|
|
4487
|
+
}
|
|
4488
|
+
};
|
|
4489
|
+
credentialOptions(
|
|
4490
|
+
specCmd.command("target").description("Say where a spec would be filed, and whether this factory can file there").option("--tracker <name>", "one connected tracker, when the factory has more than one")
|
|
4491
|
+
).action((opts) => run((spec) => spec.runSpecTarget(opts)));
|
|
4492
|
+
credentialOptions(
|
|
4493
|
+
specCmd.command("search").description("Find open and closed issues that match a query, before filing a duplicate").argument("<query>", "words from the title or body").option("--tracker <name>", "one connected tracker, when the factory has more than one")
|
|
4494
|
+
).action((query, opts) => run((spec) => spec.runSpecSearch({ ...opts, query })));
|
|
4495
|
+
credentialOptions(
|
|
4496
|
+
specCmd.command("file").description("File a spec as an issue and print its key, URL and the command that builds it").requiredOption("--title <text>", "the issue title").option("--body-file <path>", "the spec as Markdown; - reads standard input").option("--repo <owner/name>", "the repository to file in (GitHub), or the one the work lands in").option("--tracker <name>", "the tracker, when the factory files in more than one").option("--scope <key>", "the Linear team or Jira project, when the connection covers several")
|
|
4497
|
+
).action((opts) => run((spec) => spec.runSpecFile(opts)));
|
|
4498
|
+
credentialOptions(
|
|
4499
|
+
specCmd.command("edit").description("Replace the body of an issue that holds a spec").argument("<key>", "the issue key, such as acme/api#12 or ENG-142").option("--body-file <path>", "the new body as Markdown; - reads standard input").option("--updated-at <timestamp>", "the updatedAt you read; the edit is refused if the issue has changed since")
|
|
4500
|
+
).action((key, opts) => run((spec) => spec.runSpecEdit({ ...opts, key })));
|
|
4501
|
+
}
|
|
4295
4502
|
function registerWorkCommands(program2) {
|
|
4296
4503
|
const workCmd = program2.command("work").description("Start, remove, or read what the factory is working on (the Work page)");
|
|
4297
|
-
workCmd.command("build").description("Start work on an item: a host builds it, or with --local you do").option("--issue <number>", "issue number, or a Linear/Jira key like ENG-123 (adds it to Work first)").option(
|
|
4504
|
+
workCmd.command("build").description("Start work on an item: a host builds it, or with --local you do").option("--issue <number>", "issue number, or a Linear/Jira key like ENG-123 (adds it to Work first)").option(TRACKER_OPTION_FLAG, "tracker for a keyed --issue \u2014 required if more than one is connected").option("--item <number>", "the item's own number inside this factory \u2014 must already exist").option("--local", "you build it in a session you start; no host takes it").option("--repo <owner/name>", "repository \u2014 the checkout's remote for a number, never guessed for a key").option("--note <text>", "a short note stored with the request").option("--session-id <id>", "session id to record as-is, skipping detection").option("--retry <count>", "retries after a gateway answer worth trying again").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(
|
|
4298
4505
|
"after",
|
|
4299
|
-
|
|
4506
|
+
`
|
|
4507
|
+
${cliInvocation()} work build --issue 42 # a host builds it
|
|
4508
|
+
${cliInvocation()} work build --item 7 # retry an item already on Work
|
|
4509
|
+
${cliInvocation()} work build --issue 42 --local # you build it
|
|
4510
|
+
${cliInvocation()} work build --issue ENG-123 --tracker linear # a Linear issue
|
|
4511
|
+
|
|
4512
|
+
Without --local, records a build request: a host takes the item when it
|
|
4513
|
+
has a free slot. It never links the calling session and never takes a
|
|
4514
|
+
lease, even run from inside one.
|
|
4515
|
+
|
|
4516
|
+
With --local, puts the item on Work with no build request, so no host
|
|
4517
|
+
takes it, and prints the line that starts your session on it. That
|
|
4518
|
+
session links itself and holds the item's lease while it works.
|
|
4519
|
+
`
|
|
4300
4520
|
).action(async (opts) => {
|
|
4301
|
-
|
|
4302
|
-
const { runWorkBuild, WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4521
|
+
const { runWorkBuild, WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4303
4522
|
try {
|
|
4304
4523
|
await runWorkBuild(opts);
|
|
4305
4524
|
} catch (err) {
|
|
@@ -4312,10 +4531,17 @@ function registerWorkCommands(program2) {
|
|
|
4312
4531
|
});
|
|
4313
4532
|
workCmd.command("link-session").description("Link the calling session to a work item that already exists").argument("<number>", "the item's number inside this factory").option("--session-id <id>", "session id to record as-is, skipping detection (used by the SessionStart hook)").option("--retry <count>", "resend after a failure that may pass, with backoff").option("--no-lease", "link only \u2014 ask for no session lease (used by a host's own job)").option("--ended", "release this session's lease instead of taking or renewing one").addOption(new Option("--log-failures", "append a failure to the hook error log (used by hooks)").hideHelp()).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(
|
|
4314
4533
|
"after",
|
|
4315
|
-
|
|
4534
|
+
`
|
|
4535
|
+
${cliInvocation()} work link-session 42
|
|
4536
|
+
|
|
4537
|
+
Names an item that already exists on the calling session \u2014 it never
|
|
4538
|
+
creates one. Create one with \`${cliInvocation()} work build --local\`. Linking
|
|
4539
|
+
the same session to the same item twice is a no-op. Also takes or
|
|
4540
|
+
renews that session's own 20-minute lease, unless --no-lease or
|
|
4541
|
+
--ended is given, or another worker already holds a live one.
|
|
4542
|
+
`
|
|
4316
4543
|
).action(async (number, opts) => {
|
|
4317
|
-
|
|
4318
|
-
const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4544
|
+
const { runWorkLinkSession, WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4319
4545
|
try {
|
|
4320
4546
|
await runWorkLinkSession({ ...opts, number, noLease: opts.lease === false, logFailures: opts.logFailures === true });
|
|
4321
4547
|
} catch (err) {
|
|
@@ -4329,7 +4555,7 @@ function registerWorkCommands(program2) {
|
|
|
4329
4555
|
workCmd.command("comment").description("Post a comment on a work item's general thread or one of its criterion threads").requiredOption("--item <number>", "the item's number inside this factory").option("--criterion <id>", "the criterion thread to post on (default: the general thread)").option("--body <text>", "the comment's text").option("--artifact <id>", "artifact id \u2014 required with --attach, naming the criterion's new proof").option("--pass", "record a pass (a person's verdict; refused from an agent session or a key)").option("--fail", "record a fail (a person's verdict; refused from an agent session or a key)").option("--attach", "make --artifact the named criterion's proof").option("--ready", "hand the thread back to a person (refused from a person)").option("--session-id <id>", "session id to record as-is, skipping detection (used by the SessionStart hook)").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(
|
|
4330
4556
|
"after",
|
|
4331
4557
|
`
|
|
4332
|
-
|
|
4558
|
+
${cliInvocation()} work comment --item 7 --criterion AC-9-01 --body "fixed" --artifact 3f2a1c9d --attach
|
|
4333
4559
|
|
|
4334
4560
|
Posts one comment. Omitting --criterion posts on the general thread;
|
|
4335
4561
|
naming one posts on that criterion's own thread. --pass/--fail are a
|
|
@@ -4339,9 +4565,8 @@ criterion's new proof; --ready hands the thread back to a person. At
|
|
|
4339
4565
|
most one action flag may be given.
|
|
4340
4566
|
`
|
|
4341
4567
|
).action(async (opts) => {
|
|
4342
|
-
|
|
4343
|
-
const {
|
|
4344
|
-
const { WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4568
|
+
const { runWorkComment, WorkCommentCommandError } = await import('./work-comment-cmd-2ACOZQAE.js');
|
|
4569
|
+
const { WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4345
4570
|
try {
|
|
4346
4571
|
await runWorkComment(opts);
|
|
4347
4572
|
} catch (err) {
|
|
@@ -4354,11 +4579,16 @@ most one action flag may be given.
|
|
|
4354
4579
|
});
|
|
4355
4580
|
workCmd.command("threads").description("Read a work item's general thread and its criterion threads").requiredOption("--item <number>", "the item's number inside 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(
|
|
4356
4581
|
"after",
|
|
4357
|
-
|
|
4582
|
+
`
|
|
4583
|
+
${cliInvocation()} work threads --item 7
|
|
4584
|
+
|
|
4585
|
+
Prints the item's general thread, then one thread per acceptance
|
|
4586
|
+
criterion that carries proof or a comment, each with who is waiting
|
|
4587
|
+
on whom next.
|
|
4588
|
+
`
|
|
4358
4589
|
).action(async (opts) => {
|
|
4359
|
-
|
|
4360
|
-
const {
|
|
4361
|
-
const { WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4590
|
+
const { runWorkThreads, WorkCommentCommandError } = await import('./work-comment-cmd-2ACOZQAE.js');
|
|
4591
|
+
const { WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4362
4592
|
try {
|
|
4363
4593
|
await runWorkThreads(opts);
|
|
4364
4594
|
} catch (err) {
|
|
@@ -4369,18 +4599,42 @@ most one action flag may be given.
|
|
|
4369
4599
|
throw err;
|
|
4370
4600
|
}
|
|
4371
4601
|
});
|
|
4372
|
-
workCmd.command("
|
|
4602
|
+
workCmd.command("logs").description("Read the runner's log for a host attempt on a work item").requiredOption("--item <number>", "the item's number inside this factory").option("--attempt <claim id>", "an attempt's id, from `work status --json` (default: the newest)").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(
|
|
4603
|
+
"after",
|
|
4604
|
+
`
|
|
4605
|
+
${cliInvocation()} work logs --item 7
|
|
4606
|
+
${cliInvocation()} work logs --item 7 --attempt 7b0a3c52-9f1e-4d6b-8a21-5c3e9d2f1a40
|
|
4607
|
+
|
|
4608
|
+
Prints what the runner did around the agent on that attempt, one line
|
|
4609
|
+
per event with its timestamp, with secrets removed on the host. The
|
|
4610
|
+
runner sends it when it releases the attempt, so an attempt that is
|
|
4611
|
+
still running, or that an older runner released, has no log and the
|
|
4612
|
+
command exits 1.
|
|
4613
|
+
`
|
|
4614
|
+
).action(async (opts) => {
|
|
4615
|
+
const { runWorkLogs, WorkLogsCommandError } = await import('./work-logs-cmd-QDD2NMJL.js');
|
|
4616
|
+
const { WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4617
|
+
try {
|
|
4618
|
+
await runWorkLogs(opts);
|
|
4619
|
+
} catch (err) {
|
|
4620
|
+
if (err instanceof WorkLogsCommandError || err instanceof WorkCommandError) {
|
|
4621
|
+
fail(err.message);
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
throw err;
|
|
4625
|
+
}
|
|
4626
|
+
});
|
|
4627
|
+
workCmd.command("remove").description("Withdraw a source's addition \u2014 never deletes the item").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number or Linear/Jira key (default: named by --item instead)").option(TRACKER_OPTION_FLAG, "tracker for a keyed --issue, when both hold the key").option("--repo <owner/name>", "repository for an issue number (default: the checkout's remote)").requiredOption("--reason <text>", "why this source is no longer working on it").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(
|
|
4373
4628
|
"after",
|
|
4374
4629
|
`
|
|
4375
|
-
|
|
4630
|
+
${cliInvocation()} work remove --item 42 --reason "done"
|
|
4376
4631
|
|
|
4377
4632
|
Withdraws the caller's own addition, recording the reason. Never
|
|
4378
4633
|
deletes anything \u2014 the item is removed from Work only once no
|
|
4379
4634
|
live addition remains on it.
|
|
4380
4635
|
`
|
|
4381
4636
|
).action(async (opts) => {
|
|
4382
|
-
|
|
4383
|
-
const { runWorkRemove, WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4637
|
+
const { runWorkRemove, WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4384
4638
|
try {
|
|
4385
4639
|
await runWorkRemove(opts);
|
|
4386
4640
|
} catch (err) {
|
|
@@ -4391,9 +4645,8 @@ live addition remains on it.
|
|
|
4391
4645
|
throw err;
|
|
4392
4646
|
}
|
|
4393
4647
|
});
|
|
4394
|
-
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
|
|
4395
|
-
|
|
4396
|
-
const { runWorkStatus, WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4648
|
+
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, or a Linear/Jira key like ENG-123").option(TRACKER_OPTION_FLAG, "tracker for a keyed --issue, when both hold the key").option("--repo <owner/name>", "repository for an issue number (default: the 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) => {
|
|
4649
|
+
const { runWorkStatus, WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4397
4650
|
try {
|
|
4398
4651
|
await runWorkStatus(opts);
|
|
4399
4652
|
} catch (err) {
|
|
@@ -4405,8 +4658,7 @@ live addition remains on it.
|
|
|
4405
4658
|
}
|
|
4406
4659
|
});
|
|
4407
4660
|
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) => {
|
|
4408
|
-
|
|
4409
|
-
const { runWorkList, WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4661
|
+
const { runWorkList, WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4410
4662
|
try {
|
|
4411
4663
|
await runWorkList(opts);
|
|
4412
4664
|
} catch (err) {
|
|
@@ -4417,12 +4669,17 @@ live addition remains on it.
|
|
|
4417
4669
|
throw err;
|
|
4418
4670
|
}
|
|
4419
4671
|
});
|
|
4420
|
-
workCmd.command("claim").description("Claim a work item under a lease this API key holds before working on it").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number (default: named by --item instead)").option("--repo <owner/name>", "repository (default: the
|
|
4672
|
+
workCmd.command("claim").description("Claim a work item under a lease this API key holds before working on it").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number or Linear/Jira key (default: named by --item instead)").option(TRACKER_OPTION_FLAG, "tracker for a keyed --issue, when both hold the key").option("--repo <owner/name>", "repository for an issue number (default: the checkout's remote)").requiredOption("--kind <implement|amend>", "what this host is doing with the item").option("--host <name>", "the claiming host (default: this machine's hostname)").option("--seconds <n>", "lease length in seconds, clamped to 900 (default: 900)").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(
|
|
4421
4673
|
"after",
|
|
4422
|
-
|
|
4674
|
+
`
|
|
4675
|
+
${cliInvocation()} work claim --item 42 --kind implement
|
|
4676
|
+
|
|
4677
|
+
Records a lease held by the API key that sends it. A live lease held
|
|
4678
|
+
by a different key is refused; claiming again with the same key extends
|
|
4679
|
+
it. --host is a label only.
|
|
4680
|
+
`
|
|
4423
4681
|
).action(async (opts) => {
|
|
4424
|
-
|
|
4425
|
-
const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-LKL3GHFQ.js');
|
|
4682
|
+
const { runWorkClaim, WorkCommandError } = await import('./work-claim-cmd-3J3QW56U.js');
|
|
4426
4683
|
try {
|
|
4427
4684
|
await runWorkClaim(opts);
|
|
4428
4685
|
} catch (err) {
|
|
@@ -4433,12 +4690,16 @@ live addition remains on it.
|
|
|
4433
4690
|
throw err;
|
|
4434
4691
|
}
|
|
4435
4692
|
});
|
|
4436
|
-
workCmd.command("renew").description("Extend the lease this API key holds on a work item").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number (default: named by --item instead)").option("--repo <owner/name>", "repository (default: the
|
|
4693
|
+
workCmd.command("renew").description("Extend the lease this API key holds on a work item").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number or Linear/Jira key (default: named by --item instead)").option(TRACKER_OPTION_FLAG, "tracker for a keyed --issue, when both hold the key").option("--repo <owner/name>", "repository for an issue number (default: the checkout's remote)").option("--host <name>", "the claiming host (default: this machine's hostname)").option("--seconds <n>", "lease length in seconds, clamped to 900 (default: 900)").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(
|
|
4437
4694
|
"after",
|
|
4438
|
-
|
|
4695
|
+
`
|
|
4696
|
+
${cliInvocation()} work renew --item 42
|
|
4697
|
+
|
|
4698
|
+
Extends the live lease this API key holds. Refused with a 410 if the
|
|
4699
|
+
lease has expired, or a 403 if a different key holds it.
|
|
4700
|
+
`
|
|
4439
4701
|
).action(async (opts) => {
|
|
4440
|
-
|
|
4441
|
-
const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-LKL3GHFQ.js');
|
|
4702
|
+
const { runWorkRenew, WorkCommandError } = await import('./work-claim-cmd-3J3QW56U.js');
|
|
4442
4703
|
try {
|
|
4443
4704
|
await runWorkRenew(opts);
|
|
4444
4705
|
} catch (err) {
|
|
@@ -4449,15 +4710,28 @@ live addition remains on it.
|
|
|
4449
4710
|
throw err;
|
|
4450
4711
|
}
|
|
4451
4712
|
});
|
|
4452
|
-
workCmd.command("release").description("Release the lease this API key holds on a work item, or this session's own lease").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number (default: named by --item instead)").option("--repo <owner/name>", "repository (default: the
|
|
4713
|
+
workCmd.command("release").description("Release the lease this API key holds on a work item, or this session's own lease").option("--item <number>", "the item's number inside this factory").option("--issue <number>", "issue number or Linear/Jira key (default: named by --item instead)").option(TRACKER_OPTION_FLAG, "tracker for a keyed --issue, when both hold the key").option("--repo <owner/name>", "repository for an issue number (default: the checkout's remote)").option("--host <name>", "the claiming host (default: this machine's hostname); releases a host's lease").option("--session-id <id>", "the session whose lease to release, skipping detection").option(
|
|
4453
4714
|
"--outcome <ok|incomplete|failed|timed_out|idle|lease_lost|interrupted|stopped>",
|
|
4454
4715
|
"how a host's attempt ended (default: none)"
|
|
4455
4716
|
).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(
|
|
4456
4717
|
"after",
|
|
4457
|
-
|
|
4718
|
+
`
|
|
4719
|
+
${cliInvocation()} work release --item 42 --outcome ok
|
|
4720
|
+
${cliInvocation()} work release --item 42
|
|
4721
|
+
|
|
4722
|
+
Marks the lease this API key holds released, optionally with an
|
|
4723
|
+
outcome. A lease held by a different key is refused. Releasing an
|
|
4724
|
+
already-released lease is a no-op that returns the recorded release
|
|
4725
|
+
time and outcome.
|
|
4726
|
+
|
|
4727
|
+
Run inside a session with no --host, it releases that session's own
|
|
4728
|
+
lease instead, so the item's review opens without ending the
|
|
4729
|
+
session. It never releases another session's lease. --outcome
|
|
4730
|
+
applies to a host's lease only. A later tool call in the session
|
|
4731
|
+
takes the lease again.
|
|
4732
|
+
`
|
|
4458
4733
|
).action(async (opts) => {
|
|
4459
|
-
|
|
4460
|
-
const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-LKL3GHFQ.js');
|
|
4734
|
+
const { runWorkRelease, WorkCommandError } = await import('./work-claim-cmd-3J3QW56U.js');
|
|
4461
4735
|
try {
|
|
4462
4736
|
await runWorkRelease(opts);
|
|
4463
4737
|
} catch (err) {
|
|
@@ -4470,10 +4744,21 @@ live addition remains on it.
|
|
|
4470
4744
|
});
|
|
4471
4745
|
workCmd.command("pr <number>").description("Declare a pull request onto the launched work item, or withdraw it").option("--repo <owner/name>", "repository (default: the current checkout's remote)").option("--session-id <id>", "session id to record as-is, skipping detection").option("--withdraw", "remove the declaration instead of making one (needs --item)").option("--item <number>", "with --withdraw, the item to remove the declaration from").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(
|
|
4472
4746
|
"after",
|
|
4473
|
-
|
|
4747
|
+
`
|
|
4748
|
+
${cliInvocation()} work pr 481
|
|
4749
|
+
${cliInvocation()} work pr 481 --withdraw --item 250
|
|
4750
|
+
|
|
4751
|
+
Run inside a session already on a work item. Records that pull
|
|
4752
|
+
request 481 in the checkout's repository belongs to the item named by
|
|
4753
|
+
OUTERLAYER_WORK, or refuses when the session is not on it. The same
|
|
4754
|
+
fact a \`pr-link\` in the session's own transcript records on sync.
|
|
4755
|
+
Idempotent: declaring the same pull request twice is a no-op.
|
|
4756
|
+
|
|
4757
|
+
--withdraw removes one item's declaration of the pull request. It
|
|
4758
|
+
needs your own login; an agent session or a factory key is refused.
|
|
4759
|
+
`
|
|
4474
4760
|
).action(async (number, opts) => {
|
|
4475
|
-
|
|
4476
|
-
const { runWorkPr, WorkPrCommandError } = await import('./work-pr-cmd-HNEUIEN7.js');
|
|
4761
|
+
const { runWorkPr, WorkPrCommandError } = await import('./work-pr-cmd-H4OQR26N.js');
|
|
4477
4762
|
try {
|
|
4478
4763
|
await runWorkPr({ ...opts, number });
|
|
4479
4764
|
} catch (err) {
|
|
@@ -4486,11 +4771,20 @@ live addition remains on it.
|
|
|
4486
4771
|
});
|
|
4487
4772
|
workCmd.command("open-pr").description("Open, or edit, the pull request for the work item this session is on").requiredOption("--title <title>", "the pull request's title").requiredOption("--body-file <path>", "a file holding the pull request's description").option("--repository <owner/name>", "repository to open the pull request in (default: the primary)").option("--repo <owner/name>", "same as --repository, on the gh path").option("--session-id <id>", "session id to record as-is, on the gh path, skipping detection").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(
|
|
4488
4773
|
"after",
|
|
4489
|
-
|
|
4774
|
+
`
|
|
4775
|
+
${cliInvocation()} work open-pr --title "Add the thing" --body-file pr.md
|
|
4776
|
+
${cliInvocation()} work open-pr --repository acme/web --title "Add the thing" --body-file pr.md
|
|
4777
|
+
|
|
4778
|
+
In a host build, which holds an item key, the gateway opens the pull
|
|
4779
|
+
request as the factory's GitHub App, from the branch the claim named,
|
|
4780
|
+
or edits the one already open on that branch. No gh command runs.
|
|
4781
|
+
In your own session it runs \`gh pr create\` (or \`gh pr edit\`) under your
|
|
4782
|
+
login, then declares the pull request on the item, as \`work pr\` does.
|
|
4783
|
+
Run it again after a change to the title or description.
|
|
4784
|
+
`
|
|
4490
4785
|
).action(async (opts) => {
|
|
4491
|
-
|
|
4492
|
-
const {
|
|
4493
|
-
const { WorkCommandError } = await import('./work-cmd-Z2WXLZPH.js');
|
|
4786
|
+
const { runWorkOpenPr, WorkOpenPrCommandError } = await import('./work-open-pr-cmd-XNCEI4ZM.js');
|
|
4787
|
+
const { WorkCommandError } = await import('./work-cmd-5EPPD33Y.js');
|
|
4494
4788
|
try {
|
|
4495
4789
|
await runWorkOpenPr(opts);
|
|
4496
4790
|
} catch (err) {
|
|
@@ -4506,9 +4800,9 @@ function registerRunnerCommands(program2) {
|
|
|
4506
4800
|
const runnerCmd = program2.command("runner").description("Take work from the implement and amend queues on this machine");
|
|
4507
4801
|
const configOption = "the cloud config file to run against (default: ~/.outerlayer/config.json)";
|
|
4508
4802
|
runnerCmd.command("start").description("Run the loop: claim work, run it, report, clean up, repeat").option("--config <path>", configOption).action(async (opts) => {
|
|
4509
|
-
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-
|
|
4510
|
-
const { start, RunnerStartError } = await import('./loop-
|
|
4511
|
-
const { RunnerAlreadyRunningError } = await import('./pidfile-
|
|
4803
|
+
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-CLSGABD5.js');
|
|
4804
|
+
const { start, RunnerStartError } = await import('./loop-SYYPVFYW.js');
|
|
4805
|
+
const { RunnerAlreadyRunningError } = await import('./pidfile-3DM4OBKO.js');
|
|
4512
4806
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4513
4807
|
try {
|
|
4514
4808
|
const { realUpdateDeps } = await import('./real-deps-KNNBQC75.js');
|
|
@@ -4522,8 +4816,8 @@ function registerRunnerCommands(program2) {
|
|
|
4522
4816
|
}
|
|
4523
4817
|
});
|
|
4524
4818
|
runnerCmd.command("check").description("Validate the config, the hooks and the key, and print the effective settings").option("--config <path>", configOption).action(async (opts) => {
|
|
4525
|
-
const { defaultRunnerConfigPath } = await import('./config-
|
|
4526
|
-
const { runRunnerCheck } = await import('./check-
|
|
4819
|
+
const { defaultRunnerConfigPath } = await import('./config-CLSGABD5.js');
|
|
4820
|
+
const { runRunnerCheck } = await import('./check-UWQAL2MN.js');
|
|
4527
4821
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4528
4822
|
const result = await runRunnerCheck(configPath);
|
|
4529
4823
|
if (!result.ok) {
|
|
@@ -4539,8 +4833,8 @@ function registerRunnerCommands(program2) {
|
|
|
4539
4833
|
`);
|
|
4540
4834
|
});
|
|
4541
4835
|
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) => {
|
|
4542
|
-
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-
|
|
4543
|
-
const { runRunnerInstall, RunnerInstallError, launcherSummary, restartNote } = await import('./install-cmd-
|
|
4836
|
+
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-CLSGABD5.js');
|
|
4837
|
+
const { runRunnerInstall, RunnerInstallError, launcherSummary, restartNote } = await import('./install-cmd-QDOTR4OM.js');
|
|
4544
4838
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4545
4839
|
if (opts.service !== true) {
|
|
4546
4840
|
for (const flag of ["print", "user", "unitName"]) {
|
|
@@ -4552,7 +4846,7 @@ function registerRunnerCommands(program2) {
|
|
|
4552
4846
|
}
|
|
4553
4847
|
try {
|
|
4554
4848
|
if (opts.service === true) {
|
|
4555
|
-
const { installServiceCommand } = await import('./command-
|
|
4849
|
+
const { installServiceCommand } = await import('./command-OEAV7BVX.js');
|
|
4556
4850
|
const result2 = await installServiceCommand({
|
|
4557
4851
|
configPath,
|
|
4558
4852
|
print: opts.print,
|
|
@@ -4590,7 +4884,7 @@ function registerRunnerCommands(program2) {
|
|
|
4590
4884
|
printLauncher(result, launcherSummary);
|
|
4591
4885
|
process.stdout.write(
|
|
4592
4886
|
`
|
|
4593
|
-
To keep it running, run ${C.yellow}
|
|
4887
|
+
To keep it running, run ${C.yellow}${cliInvocation()} runner install --service${C.reset}. To start the runner from the install layout yourself, use this line, then restart the service:
|
|
4594
4888
|
${C.yellow}${result.execStart}${C.reset}
|
|
4595
4889
|
`
|
|
4596
4890
|
);
|
|
@@ -4604,11 +4898,11 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
|
|
|
4604
4898
|
});
|
|
4605
4899
|
runnerCmd.command("uninstall").description("Stop the runner's service and remove it (needs --service)").option("--config <path>", configOption).option("--service", "remove the service, and the launch agent or scheduled task").option("--unit-name <name>", "the unit's name, when it is not the default").action(async (opts) => {
|
|
4606
4900
|
if (opts.service !== true) {
|
|
4607
|
-
fail(
|
|
4901
|
+
fail(`pass --service: \`${cliInvocation()} runner uninstall --service\` removes the runner's service.`);
|
|
4608
4902
|
return;
|
|
4609
4903
|
}
|
|
4610
|
-
const { defaultRunnerConfigPath } = await import('./config-
|
|
4611
|
-
const { uninstallServiceCommand } = await import('./command-
|
|
4904
|
+
const { defaultRunnerConfigPath } = await import('./config-CLSGABD5.js');
|
|
4905
|
+
const { uninstallServiceCommand } = await import('./command-OEAV7BVX.js');
|
|
4612
4906
|
const result = await uninstallServiceCommand({ configPath: opts.config ?? defaultRunnerConfigPath(), unitName: opts.unitName, home: homeFromEnv() });
|
|
4613
4907
|
if (result.ok) for (const line of result.lines) process.stdout.write(`${C.green}\u2713${C.reset} ${line}
|
|
4614
4908
|
`);
|
|
@@ -4616,14 +4910,31 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
|
|
|
4616
4910
|
});
|
|
4617
4911
|
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("--factory <org/factory>", "factory to create the runner key on, as <org>/<factory>").option("--app-id <id>", "factory id, instead of --factory (Settings \u2192 General)").addHelpText(
|
|
4618
4912
|
"after",
|
|
4619
|
-
|
|
4913
|
+
`
|
|
4914
|
+
With no apiKey in the config file, and a login saved by \`${cliInvocation()} login\`,
|
|
4915
|
+
runner init names the factory the runner is for: --factory, then --app-id,
|
|
4916
|
+
OUTERLAYER_APP_ID, or this repository's connection. With none of those, a
|
|
4917
|
+
login that can use one factory uses it, and one that can use several lists
|
|
4918
|
+
them. It then prints the factory's API keys page, where you create a key with
|
|
4919
|
+
the Runner preset, and asks you to paste it. Nothing is shown as you paste.
|
|
4920
|
+
With no terminal to ask on, it stops and says what to put in the config file.
|
|
4921
|
+
|
|
4922
|
+
With --agent claude, the queue commands start Claude Code instead of the
|
|
4923
|
+
placeholder, and Claude's credential is read from stdin: a token from
|
|
4924
|
+
\`claude setup-token\` (sk-ant-oat...) or an API key (sk-ant-api...). It is
|
|
4925
|
+
never printed, and is stored in the runner.env beside the config with mode
|
|
4926
|
+
600. Other lines in the file are kept. With nothing piped, a credential
|
|
4927
|
+
already in runner.env is used.
|
|
4928
|
+
|
|
4929
|
+
printf '%s' "$CLAUDE_CODE_OAUTH_TOKEN" | ${cliInvocation()} runner init --agent claude --factory <org>/<factory>
|
|
4930
|
+
`
|
|
4620
4931
|
).action(async (opts) => {
|
|
4621
|
-
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-
|
|
4622
|
-
const { prepareRunnerInit, runRunnerInit, RunnerInitError, writeRunnerInit } = await import('./init-
|
|
4623
|
-
const { obtainRunnerKey, runnerKeyLogin } = await import('./runner-key-
|
|
4624
|
-
const { detectMachine } = await import('./machine-
|
|
4625
|
-
const { chooseVm, runVmInit, VmInitError } = await import('./init-
|
|
4626
|
-
const { AGENT_COMMANDS, credentialFromStdin, isAgentName, restartForCredential, RunnerEnvError } = await import('./runner-env-
|
|
4932
|
+
const { defaultRunnerConfigPath, RunnerConfigError } = await import('./config-CLSGABD5.js');
|
|
4933
|
+
const { prepareRunnerInit, runRunnerInit, RunnerInitError, writeRunnerInit } = await import('./init-MOINY2DD.js');
|
|
4934
|
+
const { obtainRunnerKey, runnerKeyLogin } = await import('./runner-key-276SJU5R.js');
|
|
4935
|
+
const { detectMachine } = await import('./machine-6X63G2KH.js');
|
|
4936
|
+
const { chooseVm, runVmInit, VmInitError } = await import('./init-LWHDMZQG.js');
|
|
4937
|
+
const { AGENT_COMMANDS, credentialFromStdin, isAgentName, restartForCredential, RunnerEnvError } = await import('./runner-env-FRZFFHDF.js');
|
|
4627
4938
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4628
4939
|
if (opts.agent !== void 0 && !isAgentName(opts.agent)) {
|
|
4629
4940
|
fail(`--agent ${opts.agent}: runner init writes commands for ${Object.keys(AGENT_COMMANDS).join(", ")}. For another agent, leave --agent out and edit runner.commands.`);
|
|
@@ -4637,7 +4948,7 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
|
|
|
4637
4948
|
try {
|
|
4638
4949
|
let credential;
|
|
4639
4950
|
if (agent !== void 0) {
|
|
4640
|
-
const { readPipedKey } = await import('./login-browser-
|
|
4951
|
+
const { readPipedKey } = await import('./login-browser-DX35MARZ.js');
|
|
4641
4952
|
credential = credentialFromStdin(
|
|
4642
4953
|
await readPipedKey(process.stdin, {
|
|
4643
4954
|
onWaiting: () => process.stderr.write(`runner init is waiting for Claude's credential on stdin; close stdin to use the one already in runner.env
|
|
@@ -4654,7 +4965,7 @@ To keep it running, run ${C.yellow}outerlayer runner install --service${C.reset}
|
|
|
4654
4965
|
const chunks = [];
|
|
4655
4966
|
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
4656
4967
|
const result2 = runRunnerInit(configPath, { machine, configFromStdin: Buffer.concat(chunks).toString("utf8"), requireContainer: true });
|
|
4657
|
-
const { ensureHostKey: ensureHostKey2 } = await import('./host-key-
|
|
4968
|
+
const { ensureHostKey: ensureHostKey2 } = await import('./host-key-U4B5QETR.js');
|
|
4658
4969
|
const hostKey2 = await ensureHostKey2(configPath);
|
|
4659
4970
|
process.stdout.write(`host key ${hostKey2.fingerprint}
|
|
4660
4971
|
hooks: ${result2.hooks}
|
|
@@ -4713,7 +5024,7 @@ hooks: ${result2.hooks}
|
|
|
4713
5024
|
const prepared = prepareRunnerInit(configPath, { machine, allowProcess, agent, credential, keyFromLogin: makeKey !== void 0 });
|
|
4714
5025
|
const created = makeKey === void 0 ? void 0 : await makeKey();
|
|
4715
5026
|
const result = writeRunnerInit(prepared, created);
|
|
4716
|
-
const { ensureHostKey } = await import('./host-key-
|
|
5027
|
+
const { ensureHostKey } = await import('./host-key-U4B5QETR.js');
|
|
4717
5028
|
const hostKey = await ensureHostKey(configPath);
|
|
4718
5029
|
if (created !== void 0) {
|
|
4719
5030
|
process.stdout.write(`${C.green}\u2713${C.reset} saved the runner key in ${result.configPath}
|
|
@@ -4738,10 +5049,10 @@ ${C.yellow}${result.note}${C.reset}
|
|
|
4738
5049
|
`);
|
|
4739
5050
|
if (machine.platform === "darwin" && opts.vm === void 0) {
|
|
4740
5051
|
process.stdout.write(`
|
|
4741
|
-
To isolate builds in containers instead, run ${C.yellow}
|
|
5052
|
+
To isolate builds in containers instead, run ${C.yellow}${cliInvocation()} runner init --vm${C.reset}.
|
|
4742
5053
|
`);
|
|
4743
5054
|
}
|
|
4744
|
-
const { runRunnerInstall, RunnerInstallError, RunnerDowngradeError, launcherSummary } = await import('./install-cmd-
|
|
5055
|
+
const { runRunnerInstall, RunnerInstallError, RunnerDowngradeError, launcherSummary } = await import('./install-cmd-QDOTR4OM.js');
|
|
4745
5056
|
let installedLayout = false;
|
|
4746
5057
|
let noService = "it starts the runner from the install layout, which needs a CLI installed with npm";
|
|
4747
5058
|
try {
|
|
@@ -4773,7 +5084,7 @@ ${C.yellow}The runner will not update itself: ${installErr.message}${C.reset}
|
|
|
4773
5084
|
let serviceUp = false;
|
|
4774
5085
|
let restart;
|
|
4775
5086
|
if (installedLayout) {
|
|
4776
|
-
const { installServiceCommand } = await import('./command-
|
|
5087
|
+
const { installServiceCommand } = await import('./command-OEAV7BVX.js');
|
|
4777
5088
|
const service = await installServiceCommand({
|
|
4778
5089
|
configPath,
|
|
4779
5090
|
entryPath: process.argv[1] ?? "",
|
|
@@ -4795,14 +5106,14 @@ ${C.yellow}The runner will not update itself: ${installErr.message}${C.reset}
|
|
|
4795
5106
|
if (restart !== void 0) process.stdout.write(`
|
|
4796
5107
|
${C.yellow}${restart}${C.reset}
|
|
4797
5108
|
`);
|
|
4798
|
-
const steps = agent === void 0 ? [`Edit the command lines in ${result.configPath} to start your agent.`, `${C.yellow}
|
|
4799
|
-
if (!serviceUp) steps.push(`${C.yellow}
|
|
5109
|
+
const steps = agent === void 0 ? [`Edit the command lines in ${result.configPath} to start your agent.`, `${C.yellow}${cliInvocation()} runner check${C.reset}`] : [`${C.yellow}${cliInvocation()} runner check${C.reset}`];
|
|
5110
|
+
if (!serviceUp) steps.push(`${C.yellow}${cliInvocation()} runner start${C.reset}, or ${C.yellow}${cliInvocation()} runner install --service${C.reset} to keep it running.`);
|
|
4800
5111
|
process.stdout.write(
|
|
4801
5112
|
`
|
|
4802
5113
|
Next:
|
|
4803
5114
|
` + steps.map((step, i) => ` ${i + 1}. ${step}
|
|
4804
|
-
`).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}
|
|
4805
|
-
` : serviceUp ? ` The runner service is running; ${C.yellow}
|
|
5115
|
+
`).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}${cliInvocation()} runner status${C.reset} shows what it is doing.
|
|
5116
|
+
` : serviceUp ? ` The runner service is running; ${C.yellow}${cliInvocation()} runner status${C.reset} shows what it is doing.
|
|
4806
5117
|
` : "")
|
|
4807
5118
|
);
|
|
4808
5119
|
} catch (err) {
|
|
@@ -4813,29 +5124,42 @@ Next:
|
|
|
4813
5124
|
throw err;
|
|
4814
5125
|
}
|
|
4815
5126
|
});
|
|
4816
|
-
runnerCmd.command("image").description("Build a repository's image from its
|
|
5127
|
+
runnerCmd.command("image").description("Build a repository's image from its governing repository's devcontainer").requiredOption("--repository <owner/name>", "the repository to build the image for").option("--remote <url>", "where to fetch from (default: the repository's GitHub URL)").option("--config <path>", configOption).option("--json", "machine-readable JSON output").addHelpText(
|
|
4817
5128
|
"after",
|
|
4818
|
-
"\
|
|
5129
|
+
"\nA repository governed by a control plane builds the control plane's image, at the commit\nthe gateway has stored for it. A repository that governs itself fetches the head of its\ndefault branch into the runner's cache. Either way it reads .devcontainer/devcontainer.json\nand 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"
|
|
4819
5130
|
).action(async (opts) => {
|
|
4820
|
-
const { defaultRunnerConfigPath } = await import('./config-
|
|
4821
|
-
const { buildRepositoryImage, RecipeError } = await import('./build-
|
|
5131
|
+
const { defaultRunnerConfigPath } = await import('./config-CLSGABD5.js');
|
|
5132
|
+
const { buildRepositoryImage, RecipeError } = await import('./build-TQ6X5PYN.js');
|
|
4822
5133
|
const { currentCliBuild } = await import('./cli-build-K56DK4DS.js');
|
|
4823
|
-
const { createDockerHost } = await import('./docker-
|
|
4824
|
-
const { builderSpec } = await import('./builder-
|
|
4825
|
-
const { loadRunnerConfig } = await import('./config-
|
|
5134
|
+
const { createDockerHost } = await import('./docker-BFU3AQGY.js');
|
|
5135
|
+
const { builderSpec } = await import('./builder-C4QNTYMS.js');
|
|
5136
|
+
const { loadRunnerConfig } = await import('./config-CLSGABD5.js');
|
|
4826
5137
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4827
|
-
const
|
|
5138
|
+
const loaded = (() => {
|
|
4828
5139
|
try {
|
|
4829
|
-
return loadRunnerConfig(configPath)
|
|
5140
|
+
return loadRunnerConfig(configPath);
|
|
4830
5141
|
} catch {
|
|
4831
|
-
return
|
|
5142
|
+
return void 0;
|
|
4832
5143
|
}
|
|
4833
5144
|
})();
|
|
5145
|
+
const memory = loaded?.build?.memory ?? "8g";
|
|
4834
5146
|
try {
|
|
5147
|
+
const { imageTargetFor } = await import('./governing-source-V5EEGPTA.js');
|
|
5148
|
+
const target = await imageTargetFor({ config: loaded, repository: opts.repository, fetchImpl: fetch });
|
|
5149
|
+
if (!target.ok) {
|
|
5150
|
+
if (opts.json) {
|
|
5151
|
+
writeJson({ error: { code: "governing_source_unavailable", message: target.reason } });
|
|
5152
|
+
process.exitCode = 1;
|
|
5153
|
+
return;
|
|
5154
|
+
}
|
|
5155
|
+
fail(target.reason);
|
|
5156
|
+
return;
|
|
5157
|
+
}
|
|
4835
5158
|
const result = await buildRepositoryImage(
|
|
4836
5159
|
{
|
|
4837
|
-
repository:
|
|
4838
|
-
|
|
5160
|
+
repository: target.repository,
|
|
5161
|
+
...target.pinned === void 0 ? {} : { pinned: target.pinned },
|
|
5162
|
+
remote: target.pinned === void 0 ? opts.remote : void 0,
|
|
4839
5163
|
configPath,
|
|
4840
5164
|
cli: currentCliBuild(),
|
|
4841
5165
|
log: (line) => {
|
|
@@ -4883,22 +5207,22 @@ Next:
|
|
|
4883
5207
|
}
|
|
4884
5208
|
});
|
|
4885
5209
|
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) => {
|
|
4886
|
-
const { defaultRunnerConfigPath } = await import('./config-
|
|
4887
|
-
const { requestStop } = await import('./loop-
|
|
5210
|
+
const { defaultRunnerConfigPath } = await import('./config-CLSGABD5.js');
|
|
5211
|
+
const { requestStop } = await import('./loop-SYYPVFYW.js');
|
|
4888
5212
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4889
5213
|
await requestStop(configPath, Boolean(opts.now));
|
|
4890
5214
|
});
|
|
4891
5215
|
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) => {
|
|
4892
|
-
const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-
|
|
4893
|
-
const { jobsDir, runnerDir } = await import('./paths-
|
|
4894
|
-
const { listJobFiles, runnerProcessState } = await import('./loop-
|
|
4895
|
-
const { renderStatus } = await import('./status-
|
|
4896
|
-
const { readHousekeepingRecord } = await import('./record-
|
|
5216
|
+
const { defaultRunnerConfigPath, loadRunnerConfig } = await import('./config-CLSGABD5.js');
|
|
5217
|
+
const { jobsDir, runnerDir } = await import('./paths-7XP63OAX.js');
|
|
5218
|
+
const { listJobFiles, runnerProcessState } = await import('./loop-SYYPVFYW.js');
|
|
5219
|
+
const { renderStatus } = await import('./status-NAK6DYSF.js');
|
|
5220
|
+
const { readHousekeepingRecord } = await import('./record-3WP6V2K5.js');
|
|
4897
5221
|
const path2 = await import('path');
|
|
4898
5222
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4899
5223
|
const all = listJobFiles(configPath);
|
|
4900
5224
|
const housekeeping = readHousekeepingRecord(runnerDir(configPath));
|
|
4901
|
-
const { readPendingUpdate } = await import('./pending-
|
|
5225
|
+
const { readPendingUpdate } = await import('./pending-532ABZYC.js');
|
|
4902
5226
|
const pendingUpdate = readPendingUpdate(runnerDir(configPath));
|
|
4903
5227
|
let host = "unknown";
|
|
4904
5228
|
let concurrency = 1;
|
|
@@ -4912,7 +5236,7 @@ Next:
|
|
|
4912
5236
|
const service = await (async () => {
|
|
4913
5237
|
if (process.platform !== "linux" || !existsSync("/run/systemd/system")) return void 0;
|
|
4914
5238
|
const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
|
|
4915
|
-
const { DEFAULT_UNIT_NAME } = await import('./install-
|
|
5239
|
+
const { DEFAULT_UNIT_NAME } = await import('./install-4SEFIIW2.js');
|
|
4916
5240
|
const facts = inspectService(DEFAULT_UNIT_NAME, path2.dirname(configPath), realInspectProbes());
|
|
4917
5241
|
return facts === void 0 ? void 0 : { unit: facts.unit, running: facts.mainPid !== void 0, delegated: facts.delegate };
|
|
4918
5242
|
})();
|
|
@@ -4947,9 +5271,9 @@ Next:
|
|
|
4947
5271
|
);
|
|
4948
5272
|
});
|
|
4949
5273
|
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) => {
|
|
4950
|
-
const { defaultRunnerConfigPath } = await import('./config-
|
|
4951
|
-
const { listJobFiles } = await import('./loop-
|
|
4952
|
-
const { newestAttempt, logPathFor, tail, RunnerLogsError } = await import('./logs-
|
|
5274
|
+
const { defaultRunnerConfigPath } = await import('./config-CLSGABD5.js');
|
|
5275
|
+
const { listJobFiles } = await import('./loop-SYYPVFYW.js');
|
|
5276
|
+
const { newestAttempt, logPathFor, tail, RunnerLogsError } = await import('./logs-AMLKC3VP.js');
|
|
4953
5277
|
const configPath = opts.config ?? defaultRunnerConfigPath();
|
|
4954
5278
|
const item = parseTrackerNumber(itemArg);
|
|
4955
5279
|
if (item === void 0) {
|
|
@@ -4983,32 +5307,22 @@ function registerHooksCommands(program2) {
|
|
|
4983
5307
|
"after",
|
|
4984
5308
|
"\n`init` auto-wraps PreToolUse/PostToolUse hooks; these subcommands cover the\nrest: manual re-scans, other events, and undo.\n"
|
|
4985
5309
|
);
|
|
4986
|
-
hooksCmd.command("install-git").description("
|
|
5310
|
+
hooksCmd.command("install-git").description("Remove the git hooks an earlier version installed in this repo").addHelpText(
|
|
4987
5311
|
"after",
|
|
4988
|
-
"\
|
|
4989
|
-
).action((
|
|
4990
|
-
if (opts.print) {
|
|
4991
|
-
for (const [name, script] of managedHookScripts()) {
|
|
4992
|
-
process.stdout.write(`# ==== ${name} ====
|
|
4993
|
-
${script}
|
|
4994
|
-
`);
|
|
4995
|
-
}
|
|
4996
|
-
return;
|
|
4997
|
-
}
|
|
5312
|
+
"\nEarlier versions installed a pre-commit guard and commit journal hooks. This removes\nthe ones OuterLayer wrote and leaves any hook it did not write untouched.\n"
|
|
5313
|
+
).action(() => {
|
|
4998
5314
|
const removal = removeJournalHooks(process.cwd());
|
|
4999
5315
|
if (removal.removed) {
|
|
5000
5316
|
process.stdout.write(`${C.green}\u2713${C.reset} removed the commit journal hooks an earlier version installed \u2192 ${removal.path}
|
|
5001
5317
|
`);
|
|
5002
5318
|
}
|
|
5003
|
-
const guard =
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
return;
|
|
5319
|
+
const guard = removePreCommitGuardHook(process.cwd());
|
|
5320
|
+
if (guard.removed) {
|
|
5321
|
+
process.stdout.write(`${C.green}\u2713${C.reset} removed the pre-commit guard an earlier version installed \u2192 ${guard.path}
|
|
5322
|
+
`);
|
|
5008
5323
|
}
|
|
5009
|
-
|
|
5324
|
+
if (!removal.removed && !guard.removed) process.stdout.write(`${C.dim}No git hooks from an earlier version were found.${C.reset}
|
|
5010
5325
|
`);
|
|
5011
|
-
if (report.exitCode !== void 0) process.exitCode = report.exitCode;
|
|
5012
5326
|
});
|
|
5013
5327
|
hooksCmd.command("status").description("List hook entries in user and project settings, marking the wrapped ones").option("--json", "machine-readable JSON output").action((opts) => {
|
|
5014
5328
|
const report = [];
|
|
@@ -5170,9 +5484,22 @@ function registerMcpCommands(program2) {
|
|
|
5170
5484
|
const mcpCmd = program2.command("mcp").description("Configure MCP clients against the OuterLayer gateway");
|
|
5171
5485
|
mcpCmd.command("install").description("Write or update the OuterLayer entry in .mcp.json or .outerlayer/mcp.json").option("--transport <stdio|http>", "how the client connects (default: stdio)").option("--command <exe>", "stdio only: executable the client spawns (default: outerlayer)").option("--url <url>", "gateway MCP endpoint (defaults to the gateway you logged in to)").option("--name <name>", 'mcpServers key to write under (default: "outerlayer")').option("--dir <path>", "repo root to write into (default: cwd)").option("--app-id <uuid>", "self-host only: factory id sent as X-Outerlayer-App-Id").option("--json", "machine-readable JSON output").addHelpText(
|
|
5172
5486
|
"after",
|
|
5173
|
-
|
|
5487
|
+
`
|
|
5488
|
+
The API key is never a flag and never written to .mcp.json as a literal value.
|
|
5489
|
+
stdio (default): the client spawns \`${cliInvocation()} mcp serve\`, which reads the key
|
|
5490
|
+
from ~/.outerlayer/config.json (or OUTERLAYER_API_KEY) on every connect, so a
|
|
5491
|
+
rotated key is picked up without rewriting the entry.
|
|
5492
|
+
http: the entry carries the \${OUTERLAYER_API_KEY} placeholder, which the client
|
|
5493
|
+
resolves from the environment it was launched with.
|
|
5494
|
+
|
|
5495
|
+
--command: use an absolute or \${HOME}-relative path when the CLI is not on PATH.
|
|
5496
|
+
--url: with stdio the endpoint resolves from OUTERLAYER_URL / the config file at
|
|
5497
|
+
connect time; for self-host pass your gateway-node origin + /v1/mcp.
|
|
5498
|
+
Self-host deployments also need --app-id: without a key service to resolve the
|
|
5499
|
+
caller from the bearer token, the gateway answers 401 without that header.
|
|
5500
|
+
`
|
|
5174
5501
|
).action(async (opts) => {
|
|
5175
|
-
const { runMcpInstall, handleMcpInstallError, McpInstallError } = await import('./mcp-install-cmd-
|
|
5502
|
+
const { runMcpInstall, handleMcpInstallError, McpInstallError } = await import('./mcp-install-cmd-GGWB3JJ2.js');
|
|
5176
5503
|
try {
|
|
5177
5504
|
if (opts.transport !== void 0 && opts.transport !== "stdio" && opts.transport !== "http") {
|
|
5178
5505
|
throw new McpInstallError(`--transport must be "stdio" or "http", got "${opts.transport}"`);
|
|
@@ -5194,7 +5521,7 @@ function registerMcpCommands(program2) {
|
|
|
5194
5521
|
"after",
|
|
5195
5522
|
"\nWhat the stdio .mcp.json entry spawns. Reads newline-delimited JSON-RPC 2.0 on\nstdin and writes responses on stdout. The API key comes from OUTERLAYER_API_KEY\nor ~/.outerlayer/config.json at start-up; with neither the command exits 1 and\nprints why, which the MCP client shows as the connect error.\n"
|
|
5196
5523
|
).action(async (opts) => {
|
|
5197
|
-
const { runMcpServe, handleMcpServeError } = await import('./mcp-serve-cmd-
|
|
5524
|
+
const { runMcpServe, handleMcpServeError } = await import('./mcp-serve-cmd-N3UTY6EE.js');
|
|
5198
5525
|
try {
|
|
5199
5526
|
await runMcpServe({ url: opts.url, appId: opts.appId });
|
|
5200
5527
|
} catch (err) {
|
|
@@ -5205,7 +5532,7 @@ function registerMcpCommands(program2) {
|
|
|
5205
5532
|
function registerImportCommands(program2) {
|
|
5206
5533
|
const importCmd = program2.command("import").description("Import an existing config format into .outerlayer/");
|
|
5207
5534
|
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) => {
|
|
5208
|
-
const { runImportRuler, ImportRulerError } = await import('./import-ruler-cmd-
|
|
5535
|
+
const { runImportRuler, ImportRulerError } = await import('./import-ruler-cmd-5P6JOXQN.js');
|
|
5209
5536
|
try {
|
|
5210
5537
|
const result = runImportRuler({ cwd: opts.dir, json: opts.json });
|
|
5211
5538
|
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
@@ -5221,7 +5548,7 @@ function registerImportCommands(program2) {
|
|
|
5221
5548
|
"after",
|
|
5222
5549
|
"\nInstalls the four maintained skills (outerlayer-evidence, outerlayer-amend,\nouterlayer-findings, outerlayer-cli) 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"
|
|
5223
5550
|
).action(async (opts) => {
|
|
5224
|
-
const { runImportCapture, ImportCaptureError } = await import('./import-capture-cmd-
|
|
5551
|
+
const { runImportCapture, ImportCaptureError } = await import('./import-capture-cmd-KKFFOGWP.js');
|
|
5225
5552
|
try {
|
|
5226
5553
|
runImportCapture({ cwd: opts.dir, json: opts.json });
|
|
5227
5554
|
} catch (err) {
|
|
@@ -5233,59 +5560,14 @@ function registerImportCommands(program2) {
|
|
|
5233
5560
|
}
|
|
5234
5561
|
});
|
|
5235
5562
|
}
|
|
5236
|
-
function formatContextMaterializeOutput(result) {
|
|
5237
|
-
switch (result.status) {
|
|
5238
|
-
case "not-a-repo":
|
|
5239
|
-
return { stdout: [], stderr: [`${C.red}\u2717${C.reset} not inside a git repository.`], exitCode: 1 };
|
|
5240
|
-
case "self":
|
|
5241
|
-
return { stdout: [`${C.dim}This repository is self-sourced \u2014 nothing to materialize.${C.reset}`], stderr: [] };
|
|
5242
|
-
case "materialized": {
|
|
5243
|
-
const stdout = [`${C.green}\u2713${C.reset} Materialized context at ${C.dim}${result.sha?.slice(0, 7)}${C.reset}`];
|
|
5244
|
-
if (result.message) stdout.push(`${C.yellow}!${C.reset} ${result.message}`);
|
|
5245
|
-
return { stdout, stderr: [] };
|
|
5246
|
-
}
|
|
5247
|
-
case "debounced": {
|
|
5248
|
-
const stdout = [`${C.green}\u2713${C.reset} Already current at ${C.dim}${result.sha?.slice(0, 7)}${C.reset} (verification: ${result.verification})`];
|
|
5249
|
-
if (result.message) stdout.push(`${C.yellow}!${C.reset} ${result.message}`);
|
|
5250
|
-
return { stdout, stderr: [] };
|
|
5251
|
-
}
|
|
5252
|
-
case "adoption-conflict":
|
|
5253
|
-
return { stdout: [`${C.yellow}!${C.reset} ${result.message}`], stderr: [], exitCode: 1 };
|
|
5254
|
-
default:
|
|
5255
|
-
return { stdout: [`${C.yellow}!${C.reset} ${result.message ?? result.status}`], stderr: [], exitCode: 1 };
|
|
5256
|
-
}
|
|
5257
|
-
}
|
|
5258
5563
|
function registerContextCommands(program2) {
|
|
5259
|
-
const contextCmd = program2.command("context").description("Compile
|
|
5564
|
+
const contextCmd = program2.command("context").description("Compile agent context");
|
|
5260
5565
|
contextCmd.command("emit").description("Compile .outerlayer/ into each configured target tool's native files").option("--check", "compare against disk, write nothing, exit 1 on drift (CI mode)").option("--dir <path>", "repo root to read from (default: cwd)").option("--json", "machine-readable JSON output").addHelpText(
|
|
5261
5566
|
"after",
|
|
5262
5567
|
'\nTargets come from .outerlayer/config.json only; there is no --target flag.\nMissing or empty config: {"targets": [...]}, e.g. {"targets": ["claude-code"]}.\nKnown targets: claude-code, cursor, codex, copilot, factory.\n\nThe maintained skills (outerlayer-evidence, outerlayer-amend, outerlayer-findings,\nouterlayer-cli) follow this CLI: emit rewrites one that differs from its bundle, and --check\nreports it as drift. A copy under an earlier name (amend, emitting-evidence,\nreporting-findings, outerlayer) is deleted, and --check reports it as retired.\nTemplates (spec, writing-specs, writing-tests, build) are never rewritten or reported.\n\nOrphan detection (--check) only covers header-carrying outputs: every markdown\ntarget file gets a generated-by comment, but .mcp.json and .cursor/mcp.json are\nJSON with no comment syntax, so they can never be flagged as orphaned; missing\nand content drift still apply to them.\n'
|
|
5263
5568
|
).action(async (opts) => {
|
|
5264
5569
|
await compileContext({ dir: opts.dir, check: opts.check, json: opts.json });
|
|
5265
5570
|
});
|
|
5266
|
-
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) => {
|
|
5267
|
-
const { runContextAdopt } = await import('./context-adopt-NFHCLVUB.js');
|
|
5268
|
-
const { exitCode, stdout } = runContextAdopt(stagedDir, process.cwd());
|
|
5269
|
-
process.stdout.write(stdout);
|
|
5270
|
-
process.exitCode = exitCode;
|
|
5271
|
-
});
|
|
5272
|
-
contextCmd.command("materialize").description("Materialize this repository's governed context from its control plane").option("--json", "machine-readable JSON output").addHelpText(
|
|
5273
|
-
"after",
|
|
5274
|
-
"\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"
|
|
5275
|
-
).action(async (opts) => {
|
|
5276
|
-
const { materializeContext } = await import('./context-materialize-IV2TUUFO.js');
|
|
5277
|
-
const result = await materializeContext({ cwd: process.cwd(), sessionId: `manual-${Date.now()}` });
|
|
5278
|
-
const output = formatContextMaterializeOutput(result);
|
|
5279
|
-
if (output.exitCode !== void 0) process.exitCode = output.exitCode;
|
|
5280
|
-
if (opts.json) {
|
|
5281
|
-
writeJson(result);
|
|
5282
|
-
return;
|
|
5283
|
-
}
|
|
5284
|
-
for (const line of output.stdout) process.stdout.write(`${line}
|
|
5285
|
-
`);
|
|
5286
|
-
for (const line of output.stderr) process.stderr.write(`${line}
|
|
5287
|
-
`);
|
|
5288
|
-
});
|
|
5289
5571
|
}
|
|
5290
5572
|
|
|
5291
|
-
export { buildProgram,
|
|
5573
|
+
export { buildProgram, formatInitOutput, runCli };
|