@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.
Files changed (160) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +36 -2
  3. package/dist/agent-setup-TPR4BBFV.js +27 -0
  4. package/dist/{artifact-link-cmd-2F47LT7F.js → artifact-link-cmd-W7XF5BFB.js} +4 -3
  5. package/dist/{build-BG2YENGK.js → build-TQ6X5PYN.js} +5 -4
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-2D3WSOOY.js → builder-C4QNTYMS.js} +5 -6
  8. package/dist/{check-5XE7XMC4.js → check-UWQAL2MN.js} +56 -52
  9. package/dist/{chunk-R7PF5OOU.js → chunk-3CWUOFV6.js} +21 -14
  10. package/dist/{chunk-4NPZVMJU.js → chunk-3UZTIRLF.js} +60 -4
  11. package/dist/chunk-452TM7J4.js +502 -0
  12. package/dist/{chunk-NNRTPG7X.js → chunk-5GHGAF6P.js} +4 -4
  13. package/dist/{chunk-WVUIRTWA.js → chunk-5MYFOW5V.js} +196 -37
  14. package/dist/{chunk-HGECYFEI.js → chunk-5TN7GLYX.js} +138 -109
  15. package/dist/{chunk-VQYBXPI7.js → chunk-6D7GT22T.js} +5 -5
  16. package/dist/{chunk-G5NPRL2K.js → chunk-6U3MV7TZ.js} +687 -76
  17. package/dist/{chunk-42ZI52JW.js → chunk-6X4SQ5MY.js} +4 -3
  18. package/dist/{chunk-MAK5LYRF.js → chunk-7KSSBQTS.js} +278 -535
  19. package/dist/{chunk-NWQTTSBB.js → chunk-7SJPQWBM.js} +41 -16
  20. package/dist/{chunk-J5M7BSJQ.js → chunk-7VUK5RFS.js} +23 -4
  21. package/dist/{chunk-OZGHL5Y5.js → chunk-7YP7T6GV.js} +2 -2
  22. package/dist/{chunk-TI7B7UEO.js → chunk-AMFWYESY.js} +1 -1
  23. package/dist/{chunk-YRYMNBGQ.js → chunk-ANPNT6PM.js} +2 -1
  24. package/dist/{chunk-4KSQKQXE.js → chunk-BM63HB2M.js} +20 -16
  25. package/dist/{chunk-4Q7OMKAT.js → chunk-BQXFBI7T.js} +81 -5
  26. package/dist/{chunk-ADJRGVZC.js → chunk-BWSUPRDK.js} +1 -1
  27. package/dist/{chunk-3NSNIIBE.js → chunk-CG36IONK.js} +18 -17
  28. package/dist/chunk-CJJDJ7AT.js +56 -0
  29. package/dist/chunk-CRB3POP4.js +34 -0
  30. package/dist/{chunk-LWSJQJC6.js → chunk-DADWEUH3.js} +7 -7
  31. package/dist/{chunk-KG5ECZGJ.js → chunk-DJ3ZUSC3.js} +11 -11
  32. package/dist/{chunk-F47JBIAW.js → chunk-DSF2FWAZ.js} +2 -1
  33. package/dist/{chunk-N5FOE5PS.js → chunk-ECODQ7II.js} +4 -33
  34. package/dist/{chunk-HHJDN6AU.js → chunk-ESVW4MJY.js} +1 -1
  35. package/dist/{chunk-YO4PSLVB.js → chunk-FVMYKL6L.js} +1 -1
  36. package/dist/chunk-G2YNJIGF.js +44 -0
  37. package/dist/{chunk-WUYTWNMA.js → chunk-G3IJ3N6F.js} +5 -3
  38. package/dist/{chunk-QKFB4K7X.js → chunk-GGOMT6KF.js} +1 -1
  39. package/dist/{chunk-N6LURUEF.js → chunk-GOSFARMS.js} +1 -1
  40. package/dist/{chunk-S3NYLI2P.js → chunk-HPJNLETG.js} +93 -38
  41. package/dist/{chunk-IP7BIBXJ.js → chunk-IF2OSI5T.js} +35 -1
  42. package/dist/{chunk-C5Y5ZJJZ.js → chunk-IXYZUUYQ.js} +5 -4
  43. package/dist/chunk-JBJTC3UU.js +35 -0
  44. package/dist/chunk-KYS3TQAG.js +296 -0
  45. package/dist/chunk-LLM3I5YU.js +89 -0
  46. package/dist/{chunk-YU3VKZ5F.js → chunk-LRG3ZMZD.js} +6 -7
  47. package/dist/{chunk-V7NIVOFD.js → chunk-M2ZWWTWZ.js} +1 -1
  48. package/dist/{chunk-ERI5GBNI.js → chunk-MLAAT4S6.js} +3 -3
  49. package/dist/{chunk-O765JR4W.js → chunk-MTP2QEVO.js} +1 -1
  50. package/dist/{chunk-ZM2IMMYN.js → chunk-N2PXGQRL.js} +9 -1
  51. package/dist/{chunk-BWQ66M6N.js → chunk-NFIR3GEE.js} +98 -24
  52. package/dist/{chunk-POJMEEJJ.js → chunk-NNLA7LLQ.js} +1 -1
  53. package/dist/{chunk-PLMTLGYH.js → chunk-NTRCK4NH.js} +5 -4
  54. package/dist/{chunk-22RMZBGW.js → chunk-P53VEAOF.js} +1 -1
  55. package/dist/{chunk-RLYZLUQ7.js → chunk-QEFKA2QD.js} +2 -2
  56. package/dist/{chunk-JXYVA45C.js → chunk-QNLX477Q.js} +1200 -322
  57. package/dist/{chunk-UGP76IZ5.js → chunk-S5PCA3SN.js} +1 -1
  58. package/dist/{chunk-RMWLBUCJ.js → chunk-SADUQSO4.js} +1 -1
  59. package/dist/{chunk-GQD22KKN.js → chunk-T2Q7ISPE.js} +2 -1
  60. package/dist/chunk-TUSYOL2O.js +113 -0
  61. package/dist/{chunk-NBGDBTO2.js → chunk-TWF3RICI.js} +9 -8
  62. package/dist/{chunk-ONKJUM6R.js → chunk-UVG35MWF.js} +1 -1
  63. package/dist/{chunk-KMDLFI4P.js → chunk-VMJPFQNE.js} +1 -1
  64. package/dist/chunk-VRRGENS4.js +144 -0
  65. package/dist/{chunk-PTW6W5NR.js → chunk-VYAL7PUE.js} +1 -1
  66. package/dist/{chunk-OHVJA253.js → chunk-WFSUUUAN.js} +47 -30
  67. package/dist/{chunk-RSNSZ2LJ.js → chunk-X5H2WYMB.js} +1 -1
  68. package/dist/{chunk-HYZRY4KZ.js → chunk-XNGMQ27R.js} +16 -6
  69. package/dist/chunk-XSZD4AZ6.js +490 -0
  70. package/dist/chunk-YQR6E6OV.js +109 -0
  71. package/dist/{chunk-XCK2YAY5.js → chunk-YVZJ6GKF.js} +3 -2
  72. package/dist/{chunk-2MNPHL23.js → chunk-ZHAUSEEN.js} +38 -15
  73. package/dist/claude-launcher-FPPHZPMR.js +115 -0
  74. package/dist/{cli-A76AXELQ.js → cli-HROW5T7X.js} +567 -285
  75. package/dist/{command-R2QCVX55.js → command-OEAV7BVX.js} +42 -40
  76. package/dist/{config-N3C6DFQA.js → config-CLSGABD5.js} +2 -3
  77. package/dist/{connect-cmd-JBKAQNSF.js → connect-cmd-UNYKGDAL.js} +10 -9
  78. package/dist/context-cleanup-HSVYDGMI.js +9 -0
  79. package/dist/control-plane-launch-NYVDJESK.js +216 -0
  80. package/dist/{dist-LKDWQWZ7.js → dist-WO7NOJNG.js} +2 -2
  81. package/dist/{docker-4J77Q2BF.js → docker-BFU3AQGY.js} +7 -8
  82. package/dist/{doctor-4TAIARIG.js → doctor-23BBJWBX.js} +48 -42
  83. package/dist/{emit-artifact-cmd-MPZVR56N.js → emit-artifact-cmd-O6SCM7TV.js} +27 -24
  84. package/dist/emit-cmd-7SP3I6UZ.js +14 -0
  85. package/dist/{emit-criteria-cmd-EFGYI23V.js → emit-criteria-cmd-LQNERZRV.js} +17 -16
  86. package/dist/{emit-finding-cmd-5GF3V4LR.js → emit-finding-cmd-AJPKHIOV.js} +18 -17
  87. package/dist/{emit-result-cmd-I4OADKMD.js → emit-result-cmd-RUICOKWQ.js} +22 -19
  88. package/dist/emit-step-cmd-347XNGPQ.js +26 -0
  89. package/dist/{exec-client-CQV746UF.js → exec-client-7UMESIE2.js} +3 -4
  90. package/dist/governing-source-V5EEGPTA.js +34 -0
  91. package/dist/guest-init-KPBQOFE5.js +9 -0
  92. package/dist/{hook-fast-R4FVSZ2H.js → hook-fast-YAZG5MDX.js} +5 -2
  93. package/dist/host-key-U4B5QETR.js +8 -0
  94. package/dist/{import-capture-cmd-EQ35EARA.js → import-capture-cmd-KKFFOGWP.js} +7 -5
  95. package/dist/{import-ruler-cmd-BGAZXCQM.js → import-ruler-cmd-5P6JOXQN.js} +5 -3
  96. package/dist/index.js +8 -4
  97. package/dist/{init-PWU5YVL7.js → init-LWHDMZQG.js} +40 -38
  98. package/dist/init-MOINY2DD.js +53 -0
  99. package/dist/{init-cmd-XAVO7HYZ.js → init-cmd-HSMRID6A.js} +75 -30
  100. package/dist/{init-update-CVCWD6VS.js → init-update-LWX5MPPR.js} +29 -29
  101. package/dist/{install-HDBIPHTX.js → install-4SEFIIW2.js} +4 -5
  102. package/dist/{install-cmd-5HYMXLEW.js → install-cmd-QDOTR4OM.js} +5 -4
  103. package/dist/{login-browser-QZCS3WW6.js → login-browser-DX35MARZ.js} +5 -4
  104. package/dist/{logout-cmd-QAUFDJ6J.js → logout-cmd-5V5MMBSY.js} +3 -2
  105. package/dist/{logs-H55NWW4H.js → logs-AMLKC3VP.js} +3 -1
  106. package/dist/loop-SYYPVFYW.js +77 -0
  107. package/dist/machine-6X63G2KH.js +48 -0
  108. package/dist/{mcp-install-cmd-FDTPQQS6.js → mcp-install-cmd-GGWB3JJ2.js} +3 -2
  109. package/dist/{mcp-serve-cmd-3KVYEUXG.js → mcp-serve-cmd-N3UTY6EE.js} +11 -9
  110. package/dist/paths-7XP63OAX.js +7 -0
  111. package/dist/pending-532ABZYC.js +8 -0
  112. package/dist/{pidfile-UZRH774M.js → pidfile-3DM4OBKO.js} +4 -2
  113. package/dist/{record-C43U4PKS.js → record-3WP6V2K5.js} +3 -2
  114. package/dist/{runner-env-EN7YE7QX.js → runner-env-FRZFFHDF.js} +5 -6
  115. package/dist/{runner-key-6SLBI7PP.js → runner-key-276SJU5R.js} +40 -38
  116. package/dist/session-sender-GGIY5OAX.js +36 -0
  117. package/dist/{pending-MXLSRYMO.js → shell-function-NRD4KVUK.js} +1 -2
  118. package/dist/spec-cmd-SIZBRN7D.js +115 -0
  119. package/dist/{starter-pack-KF66HGZR.js → starter-pack-6LQVLBIA.js} +7 -5
  120. package/dist/{status-7VNQN5PW.js → status-NAK6DYSF.js} +46 -42
  121. package/dist/{statusline-fast-SVTMBMD7.js → statusline-fast-KN3BX67H.js} +2 -1
  122. package/dist/sync-cmd-G7GF4UTW.js +35 -0
  123. package/dist/{watch-23HSAEI6.js → watch-EVM5GSMP.js} +22 -15
  124. package/dist/{work-claim-cmd-LKL3GHFQ.js → work-claim-cmd-3J3QW56U.js} +26 -29
  125. package/dist/work-cmd-5EPPD33Y.js +24 -0
  126. package/dist/{work-comment-cmd-AWHP56M2.js → work-comment-cmd-2ACOZQAE.js} +16 -15
  127. package/dist/{work-launch-R5HTAP6X.js → work-launch-LEQ2MPM3.js} +3 -1
  128. package/dist/work-logs-cmd-QDD2NMJL.js +60 -0
  129. package/dist/{work-open-pr-cmd-IOM4AVQQ.js → work-open-pr-cmd-XNCEI4ZM.js} +18 -17
  130. package/dist/work-pr-cmd-H4OQR26N.js +25 -0
  131. package/package.json +3 -2
  132. package/skill-pack/maintained/outerlayer-amend/SKILL.md +4 -2
  133. package/skill-pack/maintained/outerlayer-cli/SKILL.md +9 -2
  134. package/skill-pack/maintained/outerlayer-cli/references/find-out-why-a-build-ended.md +76 -0
  135. package/skill-pack/shipped.json +18 -2
  136. package/skill-pack/template/build/SKILL.md +23 -10
  137. package/skill-pack/template/build/references/agent-briefs.md +3 -1
  138. package/skill-pack/template/build/references/criteria-judge.md +27 -0
  139. package/skill-pack/template/build/references/release.md +22 -29
  140. package/skill-pack/template/spec/SKILL.md +35 -10
  141. package/skill-pack/validators/acceptance-criteria.yaml +25 -26
  142. package/dist/agent-setup-T624CWPD.js +0 -27
  143. package/dist/chunk-6Y5RUTHO.js +0 -39
  144. package/dist/chunk-BYT5O5RV.js +0 -61
  145. package/dist/chunk-WTE3MQML.js +0 -133
  146. package/dist/chunk-XCCLFVXM.js +0 -401
  147. package/dist/chunk-YZ5F4PIS.js +0 -797
  148. package/dist/context-adopt-NFHCLVUB.js +0 -108
  149. package/dist/context-materialize-IV2TUUFO.js +0 -22
  150. package/dist/emit-cmd-IJ3GVRFD.js +0 -12
  151. package/dist/guest-init-LJP5GPZF.js +0 -9
  152. package/dist/host-key-YMLXY6QB.js +0 -7
  153. package/dist/init-6XPDRDTC.js +0 -51
  154. package/dist/loop-FK3LPMMZ.js +0 -73
  155. package/dist/machine-SQXEBAM2.js +0 -46
  156. package/dist/paths-OYKMVYJP.js +0 -6
  157. package/dist/session-sender-FO3LCREV.js +0 -29
  158. package/dist/sync-cmd-GVPPFZ3G.js +0 -28
  159. package/dist/work-cmd-Z2WXLZPH.js +0 -23
  160. package/dist/work-pr-cmd-HNEUIEN7.js +0 -24
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
3
+ import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-N2PXGQRL.js';
4
4
  import { readFileSync, mkdirSync, writeFileSync, renameSync } from 'fs';
5
5
  import { join } from 'path';
6
6
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
3
+ import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-N2PXGQRL.js';
4
4
  import { existsSync, readFileSync, mkdirSync, chmodSync, writeFileSync } from 'fs';
5
5
  import { dirname, join } from 'path';
6
6
 
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { parseTrackerNumber } from './chunk-WZBHBH3J.js';
4
+ import { cliInvocation } from './chunk-CRB3POP4.js';
4
5
  import { mkdirSync, appendFileSync, readFileSync } from 'fs';
5
6
  import { join } from 'path';
6
7
 
7
8
  createRequire(import.meta.url);
8
- var HOW_TO_CREATE_AN_ITEM = "outerlayer work build --local";
9
+ var HOW_TO_CREATE_AN_ITEM = `${cliInvocation()} work build --local`;
9
10
  function parseWorkItemEnv(raw) {
10
11
  if (raw === void 0) return { reason: "OUTERLAYER_WORK is not set" };
11
12
  const value = raw.trim();
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { homeFromEnv } from './chunk-VNVZDWO3.js';
4
+ import { execFileSync } from 'child_process';
5
+ import { readdirSync, readFileSync, existsSync, unlinkSync } from 'fs';
6
+ import { join, isAbsolute, resolve } from 'path';
7
+
8
+ createRequire(import.meta.url);
9
+ var JOURNAL_HOOK_MARKER = "# outerlayer post-rewrite journal";
10
+ var PRE_COMMIT_GUARD_MARKER = "# outerlayer materialized-context commit guard";
11
+ function git(cwd, args) {
12
+ try {
13
+ return execFileSync("git", ["-C", cwd, ...args], {
14
+ encoding: "utf8",
15
+ stdio: ["ignore", "pipe", "ignore"],
16
+ timeout: 2e3
17
+ }).trim();
18
+ } catch {
19
+ return void 0;
20
+ }
21
+ }
22
+ function hooksDir(cwd) {
23
+ const dir = git(cwd, ["rev-parse", "--git-path", "hooks"]);
24
+ if (dir === void 0) return void 0;
25
+ return isAbsolute(dir) ? dir : resolve(cwd, dir);
26
+ }
27
+ function commonHooksDir(cwd) {
28
+ const abs = git(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir"]);
29
+ if (abs !== void 0) return join(abs, "hooks");
30
+ const rel = git(cwd, ["rev-parse", "--git-common-dir"]);
31
+ if (rel === void 0) return void 0;
32
+ return join(isAbsolute(rel) ? rel : join(cwd, rel), "hooks");
33
+ }
34
+ function removeOurHookFile(path, marker) {
35
+ if (!existsSync(path)) return false;
36
+ let content;
37
+ try {
38
+ content = readFileSync(path, "utf8");
39
+ } catch {
40
+ return false;
41
+ }
42
+ if (!isOwnRecognitionLine(content, marker)) return false;
43
+ unlinkSync(path);
44
+ return true;
45
+ }
46
+ function isOwnRecognitionLine(content, marker) {
47
+ const secondLine = content.split("\n", 2)[1];
48
+ return secondLine !== void 0 && secondLine.startsWith(marker);
49
+ }
50
+ function removeJournalHooks(cwd) {
51
+ const dir = hooksDir(cwd);
52
+ if (dir === void 0) return { removed: false };
53
+ let removed = false;
54
+ for (const name of ["post-rewrite", "post-commit", "prepare-commit-msg"]) {
55
+ if (removeOurHookFile(join(dir, name), JOURNAL_HOOK_MARKER)) removed = true;
56
+ }
57
+ return { removed, path: dir };
58
+ }
59
+ function removePreCommitGuardHook(cwd) {
60
+ const dir = commonHooksDir(cwd);
61
+ if (dir === void 0) return { removed: false };
62
+ const path = join(dir, "pre-commit");
63
+ return { removed: removeOurHookFile(path, PRE_COMMIT_GUARD_MARKER), path };
64
+ }
65
+ function expandHooksPathTilde(hooksPath) {
66
+ if (hooksPath === "~") return homeFromEnv();
67
+ if (hooksPath.startsWith("~/")) return join(homeFromEnv(), hooksPath.slice(2));
68
+ return hooksPath;
69
+ }
70
+ function deadHooksState(cwd) {
71
+ const inRepo = git(cwd, ["rev-parse", "--is-inside-work-tree"]);
72
+ if (inRepo !== "true") return { dead: false };
73
+ const hooksPath = git(cwd, ["config", "core.hooksPath"]) ?? "";
74
+ if (hooksPath === "") return { dead: false };
75
+ const top = git(cwd, ["rev-parse", "--show-toplevel"]);
76
+ if (top === void 0) return { dead: false };
77
+ const expanded = expandHooksPathTilde(hooksPath);
78
+ const hooksDir2 = isAbsolute(expanded) ? expanded : join(top, expanded);
79
+ let entries;
80
+ try {
81
+ entries = readdirSync(hooksDir2);
82
+ } catch (err) {
83
+ const code = err.code;
84
+ if (code === "ENOENT" || code === "ENOTDIR") return { dead: true, hooksDir: hooksDir2, repoRoot: top };
85
+ return { dead: false, hooksDir: hooksDir2, repoRoot: top, unreadableError: err };
86
+ }
87
+ const dead = entries.filter((e) => e !== ".gitignore").length === 0;
88
+ return { dead, hooksDir: hooksDir2, repoRoot: top };
89
+ }
90
+ var ALLOWED_PACKAGE_MANAGERS = ["yarn", "npm", "pnpm", "bun"];
91
+ function preparePackageManagerCommand(repoRoot) {
92
+ let declared = "";
93
+ try {
94
+ const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
95
+ if (typeof pkg.packageManager === "string") declared = pkg.packageManager.split("@")[0] ?? "";
96
+ } catch {
97
+ declared = "";
98
+ }
99
+ if (!ALLOWED_PACKAGE_MANAGERS.includes(declared)) declared = "";
100
+ if (declared === "yarn") return "yarn prepare";
101
+ return `${declared || "npm"} run prepare`;
102
+ }
103
+ function huskyPrepareScript(repoRoot) {
104
+ try {
105
+ const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
106
+ const script = pkg.scripts?.prepare;
107
+ if (typeof script === "string" && /husky/i.test(script)) return script;
108
+ } catch {
109
+ }
110
+ return null;
111
+ }
112
+
113
+ export { deadHooksState, huskyPrepareScript, preparePackageManagerCommand, removeJournalHooks, removePreCommitGuardHook };
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { mcpTargetPath, INIT_STDIO_ENTRY, INIT_DOCS_ENTRY, DOCS_MCP_SERVER_NAME } from './chunk-XCK2YAY5.js';
4
- import { POLICY_CONTENT, VALIDATOR_FILES, VALIDATORS_DIR, POLICY_PATH } from './chunk-42ZI52JW.js';
5
- import { pinExists, readContextManifest } from './chunk-WTE3MQML.js';
6
- import { resolvePackRoot, loadTemplateFiles, skillDir } from './chunk-POJMEEJJ.js';
3
+ import { mcpTargetPath, INIT_STDIO_ENTRY, INIT_DOCS_ENTRY, DOCS_MCP_SERVER_NAME } from './chunk-YVZJ6GKF.js';
4
+ import { POLICY_CONTENT, VALIDATOR_FILES, VALIDATORS_DIR, POLICY_PATH } from './chunk-6X4SQ5MY.js';
7
5
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
6
+ import { resolvePackRoot, loadTemplateFiles, skillDir } from './chunk-NNLA7LLQ.js';
7
+ import { cliInvocation } from './chunk-CRB3POP4.js';
8
+ import { cloneIsGoverned } from './chunk-YQR6E6OV.js';
8
9
  import { createHash } from 'crypto';
9
10
  import { existsSync, readFileSync } from 'fs';
10
11
  import { join } from 'path';
@@ -105,11 +106,11 @@ function inspectStarterFiles(root, opts = {}) {
105
106
  function itemLabel(item) {
106
107
  return item.kind === "file" ? item.path : `${item.path} "${item.server}" server`;
107
108
  }
108
- function contextIsGoverned(root, gitCommonDir) {
109
- return pinExists(root) || readContextManifest(gitCommonDir) !== void 0;
109
+ function contextIsGoverned(gitCommonDir) {
110
+ return cloneIsGoverned(gitCommonDir);
110
111
  }
111
112
  var NAME = "Starter files";
112
- var UPDATE = "outerlayer init --update";
113
+ var UPDATE = `${cliInvocation()} init --update`;
113
114
  function shownPaths(items, all) {
114
115
  const shown = [];
115
116
  const whole = /* @__PURE__ */ new Set();
@@ -130,7 +131,7 @@ function shownPaths(items, all) {
130
131
  function checkStarterFiles(cwd, opts = {}) {
131
132
  const none = { checks: [], mcpOutdated: false };
132
133
  const dirs = findGitDirs(cwd ?? process.cwd());
133
- if (dirs === void 0 || contextIsGoverned(dirs.repoRoot, dirs.gitCommonDir)) return none;
134
+ if (dirs === void 0 || contextIsGoverned(dirs.gitCommonDir)) return none;
134
135
  const report = inspectStarterFiles(dirs.repoRoot, opts);
135
136
  if (report.items.every((i) => i.kind === "mcp" || i.state === "missing")) return none;
136
137
  const by = (state) => report.items.filter((i) => i.state === state);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { MIN_ENGINE_MAJOR } from './chunk-MAK5LYRF.js';
3
+ import { MIN_ENGINE_MAJOR } from './chunk-7KSSBQTS.js';
4
4
  import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-CX22S5BA.js';
5
5
  import { spawnSync } from 'child_process';
6
6
  import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
+ import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-3UZTIRLF.js';
3
4
  import { RecipeError } from './chunk-QOZKPLVJ.js';
4
- import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-4NPZVMJU.js';
5
5
  import { spawn } from 'child_process';
6
6
  import { existsSync, mkdirSync, createWriteStream, rmSync, readFileSync } from 'fs';
7
7
  import { join, dirname } from 'path';
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { readContextManifest, removeContextManifest } from './chunk-YQR6E6OV.js';
4
+ import { removePreCommitGuardHook } from './chunk-TUSYOL2O.js';
5
+ import { execFileSync } from 'child_process';
6
+ import { existsSync, unlinkSync, lstatSync, readdirSync, rmdirSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
7
+ import { join, dirname, isAbsolute } from 'path';
8
+
9
+ createRequire(import.meta.url);
10
+ var CONTEXT_EXCLUDE_MARKER = "outerlayer context materialization";
11
+ var BEGIN_PREFIX = `# BEGIN ${CONTEXT_EXCLUDE_MARKER}`;
12
+ var END = `# END ${CONTEXT_EXCLUDE_MARKER}`;
13
+ function excludeFilePath(gitCommonDir) {
14
+ return join(gitCommonDir, "info", "exclude");
15
+ }
16
+ function readLines(path) {
17
+ if (!existsSync(path)) return [];
18
+ const content = readFileSync(path, "utf8");
19
+ if (content === "") return [];
20
+ const lines = content.split("\n");
21
+ if (lines[lines.length - 1] === "") lines.pop();
22
+ return lines;
23
+ }
24
+ function writeLines(path, lines) {
25
+ mkdirSync(dirname(path), { recursive: true });
26
+ writeFileSync(path, lines.length ? lines.join("\n") + "\n" : "");
27
+ }
28
+ function findWellFormedBlock(lines) {
29
+ let i = 0;
30
+ while (i < lines.length) {
31
+ if (!lines[i].startsWith(BEGIN_PREFIX)) {
32
+ i++;
33
+ continue;
34
+ }
35
+ const beginIdx = i;
36
+ let endIdx = -1;
37
+ let j = beginIdx + 1;
38
+ for (; j < lines.length; j++) {
39
+ if (lines[j] === END) {
40
+ endIdx = j;
41
+ break;
42
+ }
43
+ if (lines[j].startsWith(BEGIN_PREFIX)) break;
44
+ }
45
+ if (endIdx !== -1) return { beginIdx, endIdx };
46
+ i = j;
47
+ }
48
+ return void 0;
49
+ }
50
+ function removeContextExcludes(gitCommonDir) {
51
+ const path = excludeFilePath(gitCommonDir);
52
+ const lines = readLines(path);
53
+ const block = findWellFormedBlock(lines);
54
+ if (block === void 0) return { changed: false };
55
+ writeLines(path, [...lines.slice(0, block.beginIdx), ...lines.slice(block.endIdx + 1)]);
56
+ return { changed: true };
57
+ }
58
+ function contextExcludesPresent(gitCommonDir) {
59
+ return findWellFormedBlock(readLines(excludeFilePath(gitCommonDir))) !== void 0;
60
+ }
61
+
62
+ // src/context-cleanup.ts
63
+ function hasEarlierCopies(gitCommonDir) {
64
+ return readContextManifest(gitCommonDir) !== void 0 || contextExcludesPresent(gitCommonDir);
65
+ }
66
+ function isCopiedPath(rel) {
67
+ if (isAbsolute(rel) || rel.split("/").includes("..")) return false;
68
+ return rel === "AGENTS.md" || rel.startsWith(".claude/") || rel.startsWith(".outerlayer/");
69
+ }
70
+ function hasSymlinkComponent(root, rel) {
71
+ let current = root;
72
+ for (const segment of rel.split("/")) {
73
+ current = join(current, segment);
74
+ try {
75
+ if (lstatSync(current).isSymbolicLink()) return true;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ function trackedPaths(repoRoot) {
83
+ try {
84
+ const out = execFileSync("git", ["ls-files", "-z", "--", "AGENTS.md", ".claude", ".outerlayer"], {
85
+ cwd: repoRoot,
86
+ encoding: "utf8",
87
+ stdio: ["ignore", "pipe", "ignore"],
88
+ timeout: 5e3
89
+ });
90
+ return new Set(out.split("\0").filter((path) => path !== ""));
91
+ } catch {
92
+ return void 0;
93
+ }
94
+ }
95
+ function pruneEmptyDirs(start, root) {
96
+ let current = start;
97
+ while (current !== root && current.startsWith(root)) {
98
+ try {
99
+ if (readdirSync(current).length > 0) return;
100
+ rmdirSync(current);
101
+ } catch {
102
+ return;
103
+ }
104
+ current = dirname(current);
105
+ }
106
+ }
107
+ function cleanupEarlierCopies(input) {
108
+ const { repoRoot, gitCommonDir } = input;
109
+ const result = { removedFiles: [], excludeRemoved: false, guardRemoved: false };
110
+ if (!hasEarlierCopies(gitCommonDir)) return result;
111
+ let complete = true;
112
+ try {
113
+ const manifest = readContextManifest(gitCommonDir);
114
+ if (manifest !== void 0) {
115
+ const tracked = trackedPaths(repoRoot);
116
+ if (tracked === void 0) return result;
117
+ const isTracked = (rel) => [...tracked].some((path) => path === rel || rel.startsWith(`${path}/`));
118
+ for (const rel of manifest.files) {
119
+ if (!isCopiedPath(rel) || isTracked(rel) || hasSymlinkComponent(repoRoot, rel)) continue;
120
+ const abs = join(repoRoot, rel);
121
+ if (!existsSync(abs)) continue;
122
+ try {
123
+ unlinkSync(abs);
124
+ result.removedFiles.push(rel);
125
+ pruneEmptyDirs(dirname(abs), repoRoot);
126
+ } catch {
127
+ complete = false;
128
+ }
129
+ }
130
+ }
131
+ } catch {
132
+ return result;
133
+ }
134
+ if (!complete) return result;
135
+ try {
136
+ result.guardRemoved = removePreCommitGuardHook(repoRoot).removed;
137
+ result.excludeRemoved = removeContextExcludes(gitCommonDir).changed;
138
+ removeContextManifest(gitCommonDir);
139
+ } catch {
140
+ }
141
+ return result;
142
+ }
143
+
144
+ export { cleanupEarlierCopies };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { currentLink } from './chunk-TI7B7UEO.js';
3
+ import { currentLink } from './chunk-AMFWYESY.js';
4
4
  import { readFileSync, statSync, chmodSync, mkdirSync, writeFileSync, renameSync, rmSync, existsSync, lstatSync, accessSync, constants, realpathSync } from 'fs';
5
5
  import { randomBytes } from 'crypto';
6
6
  import { join, dirname, delimiter, resolve } from 'path';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { RESERVED_EMIT_NAMES, ARTIFACT_KINDS, ArtifactCriterionIdSchema } from './chunk-J5M7BSJQ.js';
3
+ import { RESERVED_EMIT_NAMES, ARTIFACT_KINDS, ArtifactCriterionIdSchema } from './chunk-7VUK5RFS.js';
4
4
  import { __commonJS, __require, __toESM } from './chunk-Z5GEFMRW.js';
5
5
 
6
6
  createRequire(import.meta.url);
@@ -7950,31 +7950,11 @@ function mergeCopies(perTarget) {
7950
7950
  const copies = [...byPath.entries()].filter(([path]) => !conflicted.has(path)).map(([toPath, fromSourcePath]) => ({ toPath, fromSourcePath })).sort((a, b) => a.toPath.localeCompare(b.toPath));
7951
7951
  return { copies, errors };
7952
7952
  }
7953
- var CONTEXT_SOURCE_CONFIG_PATH = ".outerlayer/config.json";
7954
- function parseGoverns(content) {
7955
- let parsed;
7956
- try {
7957
- parsed = JSON.parse(content);
7958
- } catch {
7959
- return { error: `${CONTEXT_SOURCE_CONFIG_PATH} is not valid JSON` };
7960
- }
7961
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
7962
- return { error: `${CONTEXT_SOURCE_CONFIG_PATH} must be a JSON object` };
7963
- }
7964
- const governs = parsed["governs"];
7965
- if (governs === void 0) {
7966
- return { governs: [] };
7967
- }
7968
- if (!Array.isArray(governs) || governs.some((entry) => typeof entry !== "string")) {
7969
- return { error: `"governs" must be an array of "owner/repo" strings` };
7970
- }
7971
- return { governs };
7972
- }
7973
7953
  function normalizeRepoName(repo) {
7974
7954
  return repo.trim().toLowerCase();
7975
7955
  }
7976
7956
  var CONTEXT_PIN_FILENAME = ".outerlayer-pin.json";
7977
- var CONTEXT_PIN_SCHEMA_VERSION = 1;
7957
+ var MAX_BRIEF_LENGTH = 300;
7978
7958
  var MAX_COMMAND_LENGTH = 2e3;
7979
7959
  var EXTENDS_PROBLEM = '"extends" is not a policy key \u2014 every check is a file in .outerlayer/validators/. Run outerlayer init --template default to write .outerlayer/validators/code-review.yaml, then delete the extends line';
7980
7960
  var FREED_BUILTINS = /* @__PURE__ */ new Set(["ci-passed", "acceptance-criteria", "code-review-ran"]);
@@ -7994,6 +7974,8 @@ var MAX_ROW_LENGTH = 200;
7994
7974
  var MAX_NAME_LENGTH = 40;
7995
7975
  var MAX_GLOB_LENGTH = 200;
7996
7976
  var MAX_WHEN_PATHS = 20;
7977
+ var MAX_WHEN_REPOSITORIES = 20;
7978
+ var REPOSITORY_NAME = /^[\w.-]+\/[\w.-]+$/;
7997
7979
  var NEEDS_ALIASES = {
7998
7980
  "tool-calls.commands": "commands",
7999
7981
  "tool-calls.edits": "edits",
@@ -8276,13 +8258,14 @@ function parseCustomFile(file, problems) {
8276
8258
  let whenPaths = null;
8277
8259
  let whenIssueType = null;
8278
8260
  let whenIssueLabels = null;
8261
+ let whenRepositories;
8279
8262
  if (root["when"] !== void 0) {
8280
8263
  const when = root["when"];
8281
8264
  if (!isRecord(when) || Object.keys(when).some(
8282
- (key) => key !== "paths" && key !== "issue.type" && key !== "issue.labels"
8265
+ (key) => key !== "paths" && key !== "repositories" && key !== "issue.type" && key !== "issue.labels"
8283
8266
  ) || Object.keys(when).length === 0) {
8284
8267
  return fail(
8285
- `"${id}" when: only "paths", "issue.type", and "issue.labels" scoping is supported`
8268
+ `"${id}" when: only "paths", "repositories", "issue.type", and "issue.labels" scoping is supported`
8286
8269
  );
8287
8270
  }
8288
8271
  const paths = when["paths"];
@@ -8301,6 +8284,18 @@ function parseCustomFile(file, problems) {
8301
8284
  }
8302
8285
  whenPaths = paths.map((p) => p.trim());
8303
8286
  }
8287
+ const repositories = when["repositories"];
8288
+ if (repositories !== void 0) {
8289
+ if (!Array.isArray(repositories) || repositories.length === 0 || repositories.some((name2) => typeof name2 !== "string" || !REPOSITORY_NAME.test(name2.trim()))) {
8290
+ return fail(`"${id}" when.repositories must be a non-empty list of owner/repo names`);
8291
+ }
8292
+ if (repositories.length > MAX_WHEN_REPOSITORIES) {
8293
+ return fail(
8294
+ `"${id}" when.repositories lists ${repositories.length} names \u2014 the cap is ${MAX_WHEN_REPOSITORIES}`
8295
+ );
8296
+ }
8297
+ whenRepositories = repositories.map((name2) => name2.trim());
8298
+ }
8304
8299
  const issueType = when["issue.type"];
8305
8300
  if (issueType !== void 0) {
8306
8301
  if (typeof issueType !== "string" || issueType.trim() === "") {
@@ -8325,10 +8320,11 @@ function parseCustomFile(file, problems) {
8325
8320
  let declaresEmit = null;
8326
8321
  let runWhere;
8327
8322
  let command;
8323
+ let judge;
8328
8324
  if (hasRun) {
8329
8325
  const run = root["run"];
8330
- if (!isRecord(run) || Object.keys(run).some((key) => key !== "where" && key !== "emit" && key !== "command")) {
8331
- return fail(`"${id}" run: must be a mapping of "where", "emit" and, for where: host, "command"`);
8326
+ if (!isRecord(run) || Object.keys(run).some((key) => key !== "where" && key !== "emit" && key !== "command" && key !== "judge")) {
8327
+ return fail(`"${id}" run: must be a mapping of "where", "emit" and, for where: host, "command" or "judge"`);
8332
8328
  }
8333
8329
  const where = run["where"];
8334
8330
  if (where !== "ci" && where !== "host") {
@@ -8346,10 +8342,29 @@ function parseCustomFile(file, problems) {
8346
8342
  if (where === "ci" && run["command"] !== void 0) {
8347
8343
  return fail(`"${id}" run.command is only for run.where: host \u2014 a where: ci check runs in your own CI`);
8348
8344
  }
8349
- if (where === "host") {
8345
+ if (where === "ci" && run["judge"] !== void 0) {
8346
+ return fail(`"${id}" run.judge is only for run.where: host \u2014 a where: ci check runs in your own CI`);
8347
+ }
8348
+ if (where === "host" && run["judge"] !== void 0) {
8349
+ if (run["command"] !== void 0) {
8350
+ return fail(`"${id}" run declares both "judge" and "command" \u2014 a host check has one or the other`);
8351
+ }
8352
+ const rawJudge = run["judge"];
8353
+ if (!isRecord(rawJudge) || Object.keys(rawJudge).some((key) => key !== "brief")) {
8354
+ return fail(`"${id}" run.judge must be a mapping with a "brief"`);
8355
+ }
8356
+ const brief = rawJudge["brief"];
8357
+ if (typeof brief !== "string" || brief.trim() === "" || brief.length > MAX_BRIEF_LENGTH || brief.startsWith("/") || brief.split("/").includes("..")) {
8358
+ return fail(
8359
+ `"${id}" run.judge.brief must be a relative path inside the repository, of at most ${MAX_BRIEF_LENGTH} characters, with no ".." segment`
8360
+ );
8361
+ }
8362
+ judge = { brief: brief.trim() };
8363
+ runWhere = "host";
8364
+ } else if (where === "host") {
8350
8365
  const rawCommand = run["command"];
8351
8366
  if (rawCommand === void 0) {
8352
- return fail(`"${id}" run.where: host needs a "command" \u2014 the shell line the host runs`);
8367
+ return fail(`"${id}" run.where: host needs a "command" or a "judge" \u2014 what the host runs`);
8353
8368
  }
8354
8369
  if (typeof rawCommand !== "string" || rawCommand.trim() === "" || rawCommand.length > MAX_COMMAND_LENGTH) {
8355
8370
  return fail(`"${id}" run.command must be a non-empty string of at most ${MAX_COMMAND_LENGTH} characters`);
@@ -8375,6 +8390,7 @@ function parseCustomFile(file, problems) {
8375
8390
  whenPaths,
8376
8391
  whenIssueType,
8377
8392
  whenIssueLabels,
8393
+ ...whenRepositories === void 0 ? {} : { whenRepositories, whenRepositoriesFile: file.path },
8378
8394
  requireAny,
8379
8395
  // Declared needs only — the author's own statement of what the rule
8380
8396
  // reads. A session.ran alternative is guarded per-alternative at
@@ -8384,7 +8400,8 @@ function parseCustomFile(file, problems) {
8384
8400
  needs: [...new Set(declaredNeeds)].sort(),
8385
8401
  declaresEmit,
8386
8402
  ...runWhere === void 0 ? {} : { runWhere },
8387
- ...command === void 0 ? {} : { command }
8403
+ ...command === void 0 ? {} : { command },
8404
+ ...judge === void 0 ? {} : { judge }
8388
8405
  },
8389
8406
  file: file.path,
8390
8407
  validatorRefs: requireAny.flatMap((alt) => alt.type === "validator" ? [alt.id] : []),
@@ -8685,4 +8702,4 @@ function sessionsToTimeline(sessions) {
8685
8702
  return out;
8686
8703
  }
8687
8704
 
8688
- export { ALL_TARGET_IDS, CONTEXT_PIN_FILENAME, CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_SOURCE_CONFIG_PATH, classifyTree, emitTree, extractFacts, generatedHeaderSource, normalizePath, normalizeRepoName, orderSessions, parseGoverns, parseOuterlayerConfig, parsePolicy, sessionsToTimeline, splitRawFile };
8705
+ export { ALL_TARGET_IDS, CONTEXT_PIN_FILENAME, classifyTree, emitTree, extractFacts, generatedHeaderSource, normalizePath, normalizeRepoName, orderSessions, parseOuterlayerConfig, parsePolicy, sessionsToTimeline, splitRawFile };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { RELAY_PORT, GATEWAY_SOCKET, GATEWAY_PORT, CLAUDE_SOCKET, CLAUDE_PORT, DESTINATION_BASE_PORT } from './chunk-4Q7OMKAT.js';
3
+ import { RELAY_PORT, GATEWAY_SOCKET, GATEWAY_PORT, CLAUDE_SOCKET, CLAUDE_PORT, DESTINATION_BASE_PORT } from './chunk-BQXFBI7T.js';
4
4
  import { symlinkSync, unlinkSync } from 'fs';
5
5
  import { join } from 'path';
6
6
 
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { EXEC_PORT } from './chunk-RSNSZ2LJ.js';
4
- import { RELAY_PORT } from './chunk-4Q7OMKAT.js';
3
+ import { EXEC_PORT } from './chunk-X5H2WYMB.js';
4
+ import { startTimeOf } from './chunk-JBJTC3UU.js';
5
+ import { RELAY_PORT } from './chunk-BQXFBI7T.js';
5
6
  import { spawn, spawnSync } from 'child_process';
6
7
  import { writeFileSync, chmodSync, mkdirSync } from 'fs';
7
8
 
8
9
  createRequire(import.meta.url);
9
10
  var HELPER_PATH = "/opt/outerlayer/bin/outerlayer-guest";
11
+ var HELPER_RECORD = "/run/outerlayer-helpers";
10
12
  var KERNEL_FLAG = "outerlayer.relay";
11
13
  var DOCKER_FLAG = "outerlayer.docker";
12
14
  var DOCKERD_CONFIG = "/run/outerlayer-dockerd.json";
@@ -62,9 +64,11 @@ function runGuestInit(host, cmdline) {
62
64
  }
63
65
  host.writeFile("/proc/sys/kernel/hostname", HOSTNAME);
64
66
  host.mount({ source: "tmpfs", target: "/outerlayer/job", type: "tmpfs", options: "mode=0755" });
65
- host.spawnDetached([HELPER_PATH, "relay", ...ports.flatMap((port) => ["--forward", String(port)])]);
67
+ const relay = host.spawnDetached([HELPER_PATH, "relay", ...ports.flatMap((port) => ["--forward", String(port)])]);
66
68
  if (dockerRequestedBy(cmdline)) startDocker(host);
67
- host.spawnDetached([HELPER_PATH, "exec-agent", "--port", String(EXEC_PORT)]);
69
+ const agent = host.spawnDetached([HELPER_PATH, "exec-agent", "--port", String(EXEC_PORT)]);
70
+ host.writeFile(HELPER_RECORD, [relay, agent].map((started) => `${started.pid} ${started.start}
71
+ `).join(""));
68
72
  }
69
73
  function realGuestHost() {
70
74
  return {
@@ -81,9 +85,15 @@ function realGuestHost() {
81
85
  chmod: (path, mode) => chmodSync(path, mode),
82
86
  writeFile: (path, text) => writeFileSync(path, text),
83
87
  spawnDetached(argv) {
84
- spawn(argv[0], argv.slice(1), { detached: true, stdio: ["ignore", "inherit", "inherit"] }).unref();
88
+ const child = spawn(argv[0], argv.slice(1), { detached: true, stdio: ["ignore", "inherit", "inherit"] });
89
+ child.unref();
90
+ child.once("error", () => {
91
+ });
92
+ const start = child.pid === void 0 ? null : startTimeOf(child.pid);
93
+ if (child.pid === void 0 || start === null) throw new Error(`${argv[0]} did not start`);
94
+ return { pid: child.pid, start };
85
95
  }
86
96
  };
87
97
  }
88
98
 
89
- export { DOCKERD_LOG, HELPER_PATH, realGuestHost, relayPortsFrom, runGuestInit };
99
+ export { DOCKERD_LOG, HELPER_PATH, HELPER_RECORD, realGuestHost, relayPortsFrom, runGuestInit };