@outerlayer/cli 0.18.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 (102) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +21 -2
  3. package/dist/{agent-setup-RBTEACYU.js → agent-setup-TPR4BBFV.js} +15 -16
  4. package/dist/{artifact-link-cmd-AVM2EKTV.js → artifact-link-cmd-W7XF5BFB.js} +2 -2
  5. package/dist/build-info.json +1 -1
  6. package/dist/{builder-DUI2ZH2H.js → builder-C4QNTYMS.js} +3 -3
  7. package/dist/{check-5C4YDZYV.js → check-UWQAL2MN.js} +44 -43
  8. package/dist/{chunk-S26M3YBJ.js → chunk-3UZTIRLF.js} +1 -1
  9. package/dist/{chunk-W5VPCONO.js → chunk-452TM7J4.js} +33 -227
  10. package/dist/{chunk-UYOYTKE2.js → chunk-5GHGAF6P.js} +3 -3
  11. package/dist/{chunk-QQM2UJ6T.js → chunk-5MYFOW5V.js} +182 -28
  12. package/dist/{chunk-Y5GE76XC.js → chunk-5TN7GLYX.js} +33 -81
  13. package/dist/{chunk-GOBOJMOJ.js → chunk-6D7GT22T.js} +4 -4
  14. package/dist/{chunk-3OFZM4X6.js → chunk-6U3MV7TZ.js} +151 -20
  15. package/dist/{chunk-F4XSVQJ4.js → chunk-6X4SQ5MY.js} +2 -2
  16. package/dist/{chunk-Y5MXET4M.js → chunk-7KSSBQTS.js} +11 -10
  17. package/dist/{chunk-JF42URPU.js → chunk-7SJPQWBM.js} +33 -9
  18. package/dist/{chunk-N3CUDFCW.js → chunk-7VUK5RFS.js} +15 -2
  19. package/dist/{chunk-SWCTGCSC.js → chunk-7YP7T6GV.js} +1 -1
  20. package/dist/{chunk-KDVIU23F.js → chunk-BWSUPRDK.js} +1 -1
  21. package/dist/{chunk-S76AYB4M.js → chunk-CG36IONK.js} +5 -5
  22. package/dist/chunk-CJJDJ7AT.js +56 -0
  23. package/dist/{chunk-FUB7LRUE.js → chunk-DADWEUH3.js} +5 -5
  24. package/dist/{chunk-RAZTWYZN.js → chunk-DJ3ZUSC3.js} +7 -8
  25. package/dist/{chunk-MQJSXHV7.js → chunk-G3IJ3N6F.js} +5 -3
  26. package/dist/{chunk-KK4VBOH6.js → chunk-HPJNLETG.js} +10 -11
  27. package/dist/{chunk-IP7BIBXJ.js → chunk-IF2OSI5T.js} +35 -1
  28. package/dist/{chunk-T4Y6SCG3.js → chunk-IXYZUUYQ.js} +2 -2
  29. package/dist/chunk-KYS3TQAG.js +296 -0
  30. package/dist/chunk-LLM3I5YU.js +89 -0
  31. package/dist/{chunk-B3UKB46N.js → chunk-LRG3ZMZD.js} +4 -5
  32. package/dist/{chunk-ULSIJGLS.js → chunk-NFIR3GEE.js} +56 -14
  33. package/dist/{chunk-MNTSH4CL.js → chunk-NNLA7LLQ.js} +1 -1
  34. package/dist/{chunk-WGAUY5LE.js → chunk-QEFKA2QD.js} +1 -1
  35. package/dist/{chunk-BMYGAGBJ.js → chunk-QNLX477Q.js} +155 -23
  36. package/dist/chunk-TUSYOL2O.js +113 -0
  37. package/dist/{chunk-ESC6UR76.js → chunk-TWF3RICI.js} +6 -6
  38. package/dist/{chunk-NL4XPC7W.js → chunk-UVG35MWF.js} +1 -1
  39. package/dist/{chunk-L2USZT6T.js → chunk-VMJPFQNE.js} +1 -1
  40. package/dist/chunk-VRRGENS4.js +144 -0
  41. package/dist/{chunk-X5N6KC6V.js → chunk-WFSUUUAN.js} +2 -3
  42. package/dist/{chunk-6CHIYEZZ.js → chunk-XSZD4AZ6.js} +9 -83
  43. package/dist/{chunk-LXAY4A2G.js → chunk-YQR6E6OV.js} +34 -49
  44. package/dist/{chunk-47TTLEDX.js → chunk-ZHAUSEEN.js} +35 -13
  45. package/dist/{claude-launcher-YVXI76NX.js → claude-launcher-FPPHZPMR.js} +20 -7
  46. package/dist/{cli-C77H54BV.js → cli-HROW5T7X.js} +203 -228
  47. package/dist/{command-H5CILO4S.js → command-OEAV7BVX.js} +29 -30
  48. package/dist/{config-FXDG4NPJ.js → config-CLSGABD5.js} +2 -2
  49. package/dist/{connect-cmd-ZW5FV3ZW.js → connect-cmd-UNYKGDAL.js} +7 -7
  50. package/dist/context-cleanup-HSVYDGMI.js +9 -0
  51. package/dist/control-plane-launch-NYVDJESK.js +216 -0
  52. package/dist/{dist-QTX4RSCL.js → dist-WO7NOJNG.js} +2 -2
  53. package/dist/{docker-RC3PRX3Q.js → docker-BFU3AQGY.js} +4 -4
  54. package/dist/{doctor-2NQQML6P.js → doctor-23BBJWBX.js} +32 -33
  55. package/dist/{emit-artifact-cmd-7MP63KXU.js → emit-artifact-cmd-O6SCM7TV.js} +17 -17
  56. package/dist/{emit-cmd-PPN4QQF2.js → emit-cmd-7SP3I6UZ.js} +6 -6
  57. package/dist/{emit-criteria-cmd-3RXPDLNK.js → emit-criteria-cmd-LQNERZRV.js} +11 -12
  58. package/dist/{emit-finding-cmd-OL7JB6H3.js → emit-finding-cmd-AJPKHIOV.js} +12 -13
  59. package/dist/{emit-result-cmd-UZVMOCF4.js → emit-result-cmd-RUICOKWQ.js} +17 -17
  60. package/dist/emit-step-cmd-347XNGPQ.js +26 -0
  61. package/dist/{governing-source-BUU4IABS.js → governing-source-V5EEGPTA.js} +21 -22
  62. package/dist/{hook-fast-W5MKHMJT.js → hook-fast-YAZG5MDX.js} +3 -3
  63. package/dist/{import-capture-cmd-ZO6KGHI2.js → import-capture-cmd-KKFFOGWP.js} +4 -4
  64. package/dist/{import-ruler-cmd-KJMWL5V6.js → import-ruler-cmd-5P6JOXQN.js} +2 -2
  65. package/dist/index.js +4 -4
  66. package/dist/{init-WNXXYMO7.js → init-LWHDMZQG.js} +30 -31
  67. package/dist/{init-VWXIZEAN.js → init-MOINY2DD.js} +30 -31
  68. package/dist/{init-cmd-WS6TIRYT.js → init-cmd-HSMRID6A.js} +67 -26
  69. package/dist/{init-update-E3J6TVIN.js → init-update-LWX5MPPR.js} +21 -22
  70. package/dist/{install-CBWIEF3R.js → install-4SEFIIW2.js} +3 -3
  71. package/dist/{login-browser-O63QZN7D.js → login-browser-DX35MARZ.js} +2 -2
  72. package/dist/{loop-UGI2BJOT.js → loop-SYYPVFYW.js} +44 -43
  73. package/dist/{machine-JOW4RNZ7.js → machine-6X63G2KH.js} +26 -27
  74. package/dist/{mcp-serve-cmd-DOTY5FKY.js → mcp-serve-cmd-N3UTY6EE.js} +5 -5
  75. package/dist/{runner-env-4NB2GA7O.js → runner-env-FRZFFHDF.js} +4 -4
  76. package/dist/{runner-key-TC3NFWFV.js → runner-key-276SJU5R.js} +30 -31
  77. package/dist/{session-sender-NNVPSPY7.js → session-sender-GGIY5OAX.js} +19 -15
  78. package/dist/shell-function-NRD4KVUK.js +6 -0
  79. package/dist/{spec-cmd-OWVTBB4J.js → spec-cmd-SIZBRN7D.js} +12 -13
  80. package/dist/{starter-pack-OPWUDOWX.js → starter-pack-6LQVLBIA.js} +5 -5
  81. package/dist/{status-TKCLLKFG.js → status-NAK6DYSF.js} +33 -32
  82. package/dist/{sync-cmd-Q6VYNPWG.js → sync-cmd-G7GF4UTW.js} +18 -14
  83. package/dist/{watch-ARAGADTF.js → watch-EVM5GSMP.js} +18 -14
  84. package/dist/{work-claim-cmd-34FERLYX.js → work-claim-cmd-3J3QW56U.js} +16 -17
  85. package/dist/{work-cmd-7S5B2H2P.js → work-cmd-5EPPD33Y.js} +12 -13
  86. package/dist/{work-comment-cmd-SZJSPM5S.js → work-comment-cmd-2ACOZQAE.js} +12 -13
  87. package/dist/work-logs-cmd-QDD2NMJL.js +60 -0
  88. package/dist/{work-open-pr-cmd-RPXKCBWS.js → work-open-pr-cmd-XNCEI4ZM.js} +13 -14
  89. package/dist/{work-pr-cmd-5LGANL32.js → work-pr-cmd-H4OQR26N.js} +12 -13
  90. package/package.json +2 -2
  91. package/skill-pack/maintained/outerlayer-cli/SKILL.md +9 -2
  92. package/skill-pack/maintained/outerlayer-cli/references/find-out-why-a-build-ended.md +76 -0
  93. package/skill-pack/shipped.json +1 -0
  94. package/skill-pack/template/build/references/release.md +3 -1
  95. package/dist/chunk-WBT5X3NH.js +0 -40
  96. package/dist/chunk-XCCLFVXM.js +0 -401
  97. package/dist/context-adopt-O5OFXMKI.js +0 -109
  98. package/dist/context-materialize-Y5IJ4MQT.js +0 -23
  99. package/dist/{chunk-NACDBXUG.js → chunk-BM63HB2M.js} +1 -1
  100. package/dist/{chunk-ZRL6HKBY.js → chunk-FVMYKL6L.js} +1 -1
  101. package/dist/{chunk-52NC2FIU.js → chunk-NTRCK4NH.js} +1 -1
  102. package/dist/{chunk-XGP3VYOG.js → chunk-P53VEAOF.js} +1 -1
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { WorkCommandError, postJson } from './chunk-KK4VBOH6.js';
4
- import { defaultGitRunner, materializeContext, isStagingWriteFault, lookupGoverningSource } from './chunk-W5VPCONO.js';
5
- import { readDegraded, readContextManifest, readSourceCache } from './chunk-LXAY4A2G.js';
6
- import { signingFetchFor } from './chunk-ZRL6HKBY.js';
7
- import { MAINTAINED_SKILLS } from './chunk-MNTSH4CL.js';
3
+ import { WorkCommandError, postJson } from './chunk-HPJNLETG.js';
4
+ import { composeDelivery } from './chunk-LLM3I5YU.js';
5
+ import { defaultGitRunner, materializeContext, isStagingWriteFault, lookupGoverningSource } from './chunk-452TM7J4.js';
6
+ import { signingFetchFor } from './chunk-FVMYKL6L.js';
7
+ import { SHIM_SCRIPT } from './chunk-G2YNJIGF.js';
8
+ import { readDegraded, readContextManifest, readSourceCache } from './chunk-YQR6E6OV.js';
8
9
  import { signerFor } from './chunk-M2ZWWTWZ.js';
9
10
  import { SHIM_DIR, stagedContextDir, contextHomeDir, deliveryDir } from './chunk-N2PXGQRL.js';
10
- import { SHIM_SCRIPT, rewriteControlPlanePaths, rewriteHookCommand } from './chunk-G2YNJIGF.js';
11
- import { fstatSync, existsSync, readdirSync, mkdirSync, writeFileSync, chmodSync, rmSync, copyFileSync, lstatSync, readFileSync } from 'fs';
11
+ import { fstatSync, mkdirSync, writeFileSync, chmodSync, rmSync, lstatSync } from 'fs';
12
12
  import { relative, join, dirname } from 'path';
13
13
 
14
14
  createRequire(import.meta.url);
@@ -300,82 +300,8 @@ var BuildTokens = class {
300
300
  );
301
301
  }
302
302
  };
303
- function deliverySection(dir) {
304
- return `This repository's control plane is not in the checkout. Its files are in \`${dir}\`. When an instruction or skill names a path under \`.outerlayer/\` or \`.claude/\`, read it under \`${dir}\`.`;
305
- }
306
- function isFile(path) {
307
- try {
308
- return lstatSync(path).isFile();
309
- } catch {
310
- return false;
311
- }
312
- }
313
- function filesUnder(dir, base = dir) {
314
- if (!existsSync(dir) || !lstatSync(dir).isDirectory()) return [];
315
- const out = [];
316
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
317
- const abs = join(dir, entry.name);
318
- if (entry.isDirectory()) out.push(...filesUnder(abs, base));
319
- else if (entry.isFile()) out.push(abs.slice(base.length + 1));
320
- }
321
- return out.sort();
322
- }
323
- function readText(path) {
324
- if (!isFile(path)) return void 0;
325
- const content = readFileSync(path);
326
- return content.includes(0) ? void 0 : content.toString("utf8");
327
- }
328
- function deliveredSettings(raw, dir) {
329
- if (raw === void 0) return "{}\n";
330
- const rewrite = (value) => {
331
- if (typeof value === "string") return rewriteHookCommand(value, dir);
332
- if (Array.isArray(value)) return value.map(rewrite);
333
- if (typeof value === "object" && value !== null) return Object.fromEntries(Object.entries(value).map(([key, inner]) => [key, rewrite(inner)]));
334
- return value;
335
- };
336
- try {
337
- return `${JSON.stringify(rewrite(JSON.parse(raw)), null, 2)}
338
- `;
339
- } catch {
340
- return rewriteHookCommand(raw, dir);
341
- }
342
- }
343
- function composeDelivery(input) {
344
- const { tree, dir } = input;
345
- const added = [];
346
- const put = (rel, content) => {
347
- mkdirSync(dirname(join(tree, rel)), { recursive: true });
348
- writeFileSync(join(tree, rel), content);
349
- added.push(rel);
350
- };
351
- for (const skill of MAINTAINED_SKILLS) {
352
- for (const rel of filesUnder(join(tree, ".outerlayer/skills", skill))) {
353
- const target = `.claude/skills/${skill}/${rel}`;
354
- if (existsSync(join(tree, target))) continue;
355
- mkdirSync(dirname(join(tree, target)), { recursive: true });
356
- copyFileSync(join(tree, ".outerlayer/skills", skill, rel), join(tree, target));
357
- added.push(target);
358
- }
359
- }
360
- for (const rel of [...filesUnder(join(tree, ".claude/skills"), tree), ...filesUnder(join(tree, ".claude/agents"), tree)]) {
361
- const text = readText(join(tree, rel));
362
- if (text === void 0) continue;
363
- const rewritten = rewriteControlPlanePaths(text, dir);
364
- if (rewritten !== text) writeFileSync(join(tree, rel), rewritten);
365
- }
366
- const agents = readText(join(tree, "AGENTS.md"));
367
- put("CLAUDE.md", agents === void 0 ? `${deliverySection(dir)}
368
- ` : `${deliverySection(dir)}
369
303
 
370
- ${rewriteControlPlanePaths(agents, dir)}`);
371
- put("settings.json", deliveredSettings(readText(join(tree, ".claude/settings.json")), dir));
372
- return added;
373
- }
374
- function sharedSkillNames(tree) {
375
- const dir = join(tree, ".claude/skills");
376
- if (!existsSync(dir)) return [];
377
- return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
378
- }
304
+ // src/runner/control-plane-delivery.ts
379
305
  function shimScript() {
380
306
  return SHIM_SCRIPT;
381
307
  }
@@ -561,4 +487,4 @@ async function imageTargetFor(input) {
561
487
  return { ok: true, repository: resolved.source.repository, pinned: { commit: resolved.source.sha, ref: resolved.source.ref } };
562
488
  }
563
489
 
564
- export { BuildTokens, classifyRefusal, commandWithShim, controlPlaneTokenFailure, createRunnerLog, describeCallFailure, formatClock, formatDuration, imageTargetFor, pinnedSourceOf, refusalCode, relativeToRunner, removeStagedContext, resolveGoverningSource, retrySend, sharedSkillNames, shimScript, shortClaim, stageGovernedContext };
490
+ export { BuildTokens, classifyRefusal, commandWithShim, controlPlaneTokenFailure, createRunnerLog, describeCallFailure, formatClock, formatDuration, imageTargetFor, logStamp, pinnedSourceOf, refusalCode, relativeToRunner, removeStagedContext, resolveGoverningSource, retrySend, shimScript, shortClaim, stageGovernedContext };
@@ -1,34 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_PIN_FILENAME } from './chunk-X5N6KC6V.js';
4
- import { readFileSync, existsSync, writeFileSync, mkdirSync, utimesSync, unlinkSync } from 'fs';
5
- import { join, dirname } from 'path';
3
+ import { unlinkSync, readFileSync, mkdirSync, writeFileSync, existsSync } from 'fs';
4
+ import { isAbsolute, join, dirname } from 'path';
6
5
 
7
6
  createRequire(import.meta.url);
8
- function pinPath(workpieceRoot) {
9
- return join(workpieceRoot, CONTEXT_PIN_FILENAME);
10
- }
11
- function readPin(workpieceRoot) {
12
- try {
13
- const parsed = JSON.parse(readFileSync(pinPath(workpieceRoot), "utf8"));
14
- if (typeof parsed === "object" && parsed !== null && typeof parsed.sha === "string" && typeof parsed.schemaVersion === "number") {
15
- return parsed;
16
- }
17
- return void 0;
18
- } catch {
19
- return void 0;
20
- }
21
- }
22
- function pinExists(workpieceRoot) {
23
- return existsSync(pinPath(workpieceRoot));
24
- }
25
- function writePinIfChanged(workpieceRoot, sha) {
26
- const existing = readPin(workpieceRoot);
27
- if (existing?.sha === sha && existing.schemaVersion === CONTEXT_PIN_SCHEMA_VERSION) return false;
28
- const pin = { schemaVersion: CONTEXT_PIN_SCHEMA_VERSION, sha };
29
- writeFileSync(pinPath(workpieceRoot), JSON.stringify(pin) + "\n");
30
- return true;
31
- }
32
7
  function stateDir(gitCommonDir) {
33
8
  return join(gitCommonDir, "outerlayer");
34
9
  }
@@ -56,19 +31,9 @@ function readContextManifest(gitCommonDir) {
56
31
  function writeContextManifest(gitCommonDir, manifest) {
57
32
  writeJson(manifestPath(gitCommonDir), manifest);
58
33
  }
59
- function manifestFilesPresent(workpieceRoot, manifest) {
60
- return manifest.files.every((f) => existsSync(join(workpieceRoot, f)));
61
- }
62
- function lastCheckPath(gitCommonDir) {
63
- return join(stateDir(gitCommonDir), "last-check");
64
- }
65
- function touchLastCheck(gitCommonDir, now) {
66
- const path = lastCheckPath(gitCommonDir);
67
- mkdirSync(dirname(path), { recursive: true });
68
- writeFileSync(path, String(now));
69
- const seconds = now / 1e3;
34
+ function removeContextManifest(gitCommonDir) {
70
35
  try {
71
- utimesSync(path, seconds, seconds);
36
+ unlinkSync(manifestPath(gitCommonDir));
72
37
  } catch {
73
38
  }
74
39
  }
@@ -107,18 +72,38 @@ function writeSourceCache(gitCommonDir, cache) {
107
72
  function isSourceCacheFresh(cache, now) {
108
73
  return now - Date.parse(cache.checkedAt) < cache.ttlMs;
109
74
  }
110
- function buildHoldPath(gitCommonDir) {
111
- return join(stateDir(gitCommonDir), "build-hold.json");
112
- }
113
- function readBuildHold(gitCommonDir) {
114
- const parsed = readJson(buildHoldPath(gitCommonDir));
115
- if (parsed && typeof parsed.sha === "string" && typeof parsed.repository === "string" && typeof parsed.ref === "string" && typeof parsed.at === "string") {
116
- return parsed;
75
+ function deliveryRecordPath(gitCommonDir) {
76
+ return join(stateDir(gitCommonDir), "delivery.json");
77
+ }
78
+ function readDeliveryRecord(gitCommonDir) {
79
+ const parsed = readJson(deliveryRecordPath(gitCommonDir));
80
+ if (typeof parsed?.dir === "string" && isAbsolute(parsed.dir) && typeof parsed.repository === "string" && typeof parsed.sha === "string") {
81
+ return {
82
+ ...typeof parsed.schemaVersion === "number" ? { schemaVersion: parsed.schemaVersion } : {},
83
+ repository: parsed.repository,
84
+ ...typeof parsed.ref === "string" ? { ref: parsed.ref } : {},
85
+ sha: parsed.sha,
86
+ dir: parsed.dir,
87
+ ...parsed.launcher === true ? { launcher: true } : {}
88
+ };
117
89
  }
118
90
  return void 0;
119
91
  }
120
- function writeBuildHold(gitCommonDir, hold) {
121
- writeJson(buildHoldPath(gitCommonDir), hold);
92
+ function removeLauncherDeliveryRecord(gitCommonDir) {
93
+ if (readDeliveryRecord(gitCommonDir)?.launcher !== true) return;
94
+ try {
95
+ unlinkSync(deliveryRecordPath(gitCommonDir));
96
+ } catch {
97
+ }
98
+ }
99
+ function deliveryRecordExists(gitCommonDir) {
100
+ return existsSync(deliveryRecordPath(gitCommonDir));
101
+ }
102
+ function writeDeliveryRecord(gitCommonDir, record) {
103
+ writeJson(deliveryRecordPath(gitCommonDir), record);
104
+ }
105
+ function cloneIsGoverned(gitCommonDir) {
106
+ return readSourceCache(gitCommonDir)?.status === "governed" || deliveryRecordExists(gitCommonDir);
122
107
  }
123
108
 
124
- export { SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, clearDegraded, isSourceCacheFresh, manifestFilesPresent, pinExists, readBuildHold, readContextManifest, readDegraded, readPin, readSourceCache, touchLastCheck, writeBuildHold, writeContextManifest, writeDegraded, writePinIfChanged, writeSourceCache };
109
+ export { SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, clearDegraded, cloneIsGoverned, isSourceCacheFresh, readContextManifest, readDegraded, readDeliveryRecord, readSourceCache, removeContextManifest, removeLauncherDeliveryRecord, writeContextManifest, writeDegraded, writeDeliveryRecord, writeSourceCache };
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { C } from './chunk-ANPNT6PM.js';
4
- import { retiredSkillPaths, removeRetiredSkills } from './chunk-T4Y6SCG3.js';
5
- import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-MNTSH4CL.js';
6
- import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-X5N6KC6V.js';
4
+ import { retiredSkillPaths, removeRetiredSkills } from './chunk-IXYZUUYQ.js';
5
+ import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-NNLA7LLQ.js';
6
+ import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-WFSUUUAN.js';
7
7
  import { cliInvocation } from './chunk-CRB3POP4.js';
8
- import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, readdirSync } from 'fs';
8
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, unlinkSync, rmdirSync, readdirSync } from 'fs';
9
9
  import { join, dirname, relative, sep } from 'path';
10
10
 
11
11
  createRequire(import.meta.url);
@@ -32,6 +32,9 @@ function discoverOuterlayerPaths(repoRoot) {
32
32
  walk(repoRoot, repoRoot, false, out);
33
33
  return out.sort();
34
34
  }
35
+ function isForeignTree(dir) {
36
+ return existsSync(join(dir, ".git")) || existsSync(join(dir, CONTEXT_PIN_FILENAME));
37
+ }
35
38
  function walk(dir, repoRoot, insideOuterlayer, out) {
36
39
  let entries;
37
40
  try {
@@ -44,7 +47,7 @@ function walk(dir, repoRoot, insideOuterlayer, out) {
44
47
  const full = join(dir, entry.name);
45
48
  if (entry.isDirectory()) {
46
49
  if (!insideOuterlayer && SKIP_DIRS.has(entry.name)) continue;
47
- if (!insideOuterlayer && existsSync(join(full, CONTEXT_PIN_FILENAME))) continue;
50
+ if (!insideOuterlayer && isForeignTree(full)) continue;
48
51
  walk(full, repoRoot, insideOuterlayer || entry.name === ".outerlayer", out);
49
52
  } else if (entry.isFile() && insideOuterlayer) {
50
53
  out.push(toRepoRelative(repoRoot, full));
@@ -82,13 +85,14 @@ function writeOutputs(repoRoot, result, maintained) {
82
85
  }
83
86
  return { files, copies, maintained: [...changedMaintained.created, ...changedMaintained.rewritten] };
84
87
  }
85
- function renderSuccess(targets, files, copies, maintained, retired) {
88
+ function renderSuccess(targets, files, copies, maintained, retired, orphaned) {
86
89
  const copySuffix = copies.length > 0 ? `, ${copies.length} ${copies.length === 1 ? "copy" : "copies"}` : "";
87
90
  const lines = [
88
91
  `${C.green}\u2713${C.reset} emitted ${files.length} file${files.length === 1 ? "" : "s"}${copySuffix} for ${C.dim}${targets.join(", ")}${C.reset}`
89
92
  ];
90
93
  for (const path of maintained) lines.push(` ${C.dim}updated ${path} to this CLI's version${C.reset}`);
91
94
  for (const path of retired) lines.push(` ${C.dim}removed ${path}: this skill has a new name${C.reset}`);
95
+ for (const path of orphaned) lines.push(` ${C.dim}removed ${path}: its source is gone${C.reset}`);
92
96
  return lines.join("\n");
93
97
  }
94
98
  function renderFailure(errors) {
@@ -132,7 +136,7 @@ function walkAll(dir, repoRoot, out) {
132
136
  if (entry.isSymbolicLink()) continue;
133
137
  const full = join(dir, entry.name);
134
138
  if (entry.isDirectory()) {
135
- if (SKIP_DIRS.has(entry.name) || entry.name === ".outerlayer") continue;
139
+ if (SKIP_DIRS.has(entry.name) || entry.name === ".outerlayer" || isForeignTree(full)) continue;
136
140
  walkAll(full, repoRoot, out);
137
141
  } else if (entry.isFile()) {
138
142
  out.push(toRepoRelative(repoRoot, full));
@@ -155,6 +159,21 @@ function findOrphans(repoRoot, computedFiles, sourcePaths) {
155
159
  }
156
160
  return orphans;
157
161
  }
162
+ function removeOrphans(repoRoot, orphans) {
163
+ const removed = [];
164
+ for (const { path } of orphans) {
165
+ unlinkSync(join(repoRoot, path));
166
+ removed.push(path);
167
+ for (let dir = dirname(path); dir !== "."; dir = dirname(dir)) {
168
+ try {
169
+ rmdirSync(join(repoRoot, dir));
170
+ } catch {
171
+ break;
172
+ }
173
+ }
174
+ }
175
+ return removed;
176
+ }
158
177
  function withholdKept(repoRoot, result, keep) {
159
178
  if (keep === void 0) return { result, kept: [] };
160
179
  const kept = [];
@@ -187,9 +206,11 @@ function runEmit(opts = {}) {
187
206
  const emitted = emitTree(built.input);
188
207
  const { result, kept } = withholdKept(cwd, emitted, opts.keep);
189
208
  const hasErrors = result.errors.length > 0;
190
- if (opts.check) return runCheck(cwd, opts, targets, result, hasErrors, built.sourcePaths, maintained, kept, retired);
209
+ const outputs = new Set(emitted.files.map((f) => f.path));
210
+ if (opts.check) return runCheck(cwd, opts, targets, result, hasErrors, outputs, built.sourcePaths, maintained, kept, retired);
191
211
  const written = hasErrors ? { files: [], copies: [], maintained: [] } : writeOutputs(cwd, result, maintained);
192
212
  const removed = hasErrors ? [] : removeRetiredSkills(cwd);
213
+ const orphaned = hasErrors ? [] : removeOrphans(cwd, findOrphans(cwd, outputs, built.sourcePaths));
193
214
  const exitCode = hasErrors ? 1 : 0;
194
215
  const output = opts.json ? JSON.stringify({
195
216
  targets,
@@ -197,9 +218,10 @@ function runEmit(opts = {}) {
197
218
  copies: written.copies,
198
219
  maintained: written.maintained,
199
220
  retired: removed,
221
+ orphaned,
200
222
  warnings: result.warnings,
201
223
  errors: result.errors
202
- }) : hasErrors ? renderFailure(result.errors) : renderSuccess(targets, written.files, written.copies, written.maintained, removed);
224
+ }) : hasErrors ? renderFailure(result.errors) : renderSuccess(targets, written.files, written.copies, written.maintained, removed, orphaned);
203
225
  if (!opts.quiet) {
204
226
  if (opts.json) {
205
227
  process.stdout.write(output + "\n");
@@ -218,13 +240,14 @@ function runEmit(opts = {}) {
218
240
  maintained: written.maintained,
219
241
  kept,
220
242
  retired: removed,
243
+ orphaned,
221
244
  warnings: result.warnings,
222
245
  errors: result.errors,
223
246
  output,
224
247
  exitCode
225
248
  };
226
249
  }
227
- function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths, maintained, kept, retired) {
250
+ function runCheck(cwd, opts, targets, result, hasErrors, outputs, sourcePaths, maintained, kept, retired) {
228
251
  const drift = [];
229
252
  if (!hasErrors) {
230
253
  for (const path of retired) drift.push({ kind: "retired", path });
@@ -232,7 +255,6 @@ function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths, maintained
232
255
  const d = compareFile(cwd, file.path, file.content);
233
256
  if (d) drift.push(d);
234
257
  }
235
- const computedFiles = new Set(result.files.map((f) => f.path));
236
258
  for (const file of result.files) {
237
259
  const d = compareFile(cwd, file.path, file.content);
238
260
  if (d) drift.push(d);
@@ -242,7 +264,7 @@ function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths, maintained
242
264
  if (d) drift.push(d);
243
265
  }
244
266
  const retiredPaths = new Set(retired);
245
- drift.push(...findOrphans(cwd, computedFiles, sourcePaths).filter((d) => !retiredPaths.has(d.path)));
267
+ drift.push(...findOrphans(cwd, outputs, sourcePaths).filter((d) => !retiredPaths.has(d.path)));
246
268
  drift.sort((a, b) => a.path.localeCompare(b.path) || a.kind.localeCompare(b.kind));
247
269
  }
248
270
  const exitCode = hasErrors || drift.length > 0 ? 1 : 0;
@@ -258,7 +280,7 @@ function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths, maintained
258
280
  `);
259
281
  }
260
282
  }
261
- return { targets, files: [], copies: [], maintained: [], kept, retired: [], warnings: result.warnings, errors: result.errors, drift, output, exitCode };
283
+ return { targets, files: [], copies: [], maintained: [], kept, retired: [], orphaned: [], warnings: result.warnings, errors: result.errors, drift, output, exitCode };
262
284
  }
263
285
 
264
286
  export { EmitError, runEmit };
@@ -2,6 +2,7 @@
2
2
  import { createRequire } from 'node:module';
3
3
  import { versionAtLeast, MINIMUM_CLAUDE_VERSION, SHIM_SCRIPT, CONTROL_PLANE_DIR_VARIABLE } from './chunk-G2YNJIGF.js';
4
4
  export { MINIMUM_CLAUDE_VERSION } from './chunk-G2YNJIGF.js';
5
+ import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
6
  import './chunk-Z5GEFMRW.js';
6
7
  import { spawnSync, execFileSync, spawn } from 'child_process';
7
8
  import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
@@ -20,7 +21,7 @@ function readDeliveryRecord(cwd) {
20
21
  try {
21
22
  const parsed = JSON.parse(readFileSync(path, "utf8"));
22
23
  if (typeof parsed.dir === "string" && isAbsolute(parsed.dir) && typeof parsed.repository === "string" && typeof parsed.sha === "string") {
23
- return { kind: "found", record: { repository: parsed.repository, sha: parsed.sha, dir: parsed.dir } };
24
+ return { kind: "found", record: { repository: parsed.repository, sha: parsed.sha, dir: parsed.dir, launcher: parsed.launcher === true } };
24
25
  }
25
26
  } catch {
26
27
  }
@@ -72,17 +73,29 @@ async function runClaudeLauncher(args, deps = {}) {
72
73
  return 127;
73
74
  }
74
75
  const found = readDeliveryRecord(cwd);
75
- if (found.kind === "none") return run(claude, args, env, cwd, stderr);
76
- if (found.kind === "invalid") {
76
+ const build = found.kind === "found" && found.record.launcher !== true ? found.record : void 0;
77
+ if (found.kind === "invalid" && env.OUTERLAYER_JOB_DIR !== void 0) {
77
78
  stderr(`outerlayer claude: ${found.path} is not a readable delivery record, so the control plane cannot be attached
78
79
  `);
79
80
  return 1;
80
81
  }
81
- const { dir } = found.record;
82
- if (!existsSync(dir)) {
83
- stderr(`outerlayer claude: the control plane of ${found.record.repository} is not at ${dir}, so it cannot be attached
82
+ let dir;
83
+ if (build !== void 0) {
84
+ dir = build.dir;
85
+ if (!existsSync(dir)) {
86
+ stderr(`outerlayer claude: the control plane of ${build.repository} is not at ${dir}, so it cannot be attached
84
87
  `);
85
- return 1;
88
+ return 1;
89
+ }
90
+ } else {
91
+ const { resolveLaunchPlane } = await import('./control-plane-launch-NYVDJESK.js');
92
+ const plane = await resolveLaunchPlane({ cwd, env, home: homeFromEnv(env), fetchImpl: deps.fetchImpl, stderr });
93
+ if (plane.kind === "none") return run(claude, args, env, cwd, stderr);
94
+ if (plane.kind === "refused") {
95
+ stderr(plane.message);
96
+ return 1;
97
+ }
98
+ dir = plane.dir;
86
99
  }
87
100
  const reported = spawnSync(claude, ["--version"], { env, encoding: "utf8", timeout: 15e3, stdio: ["ignore", "pipe", "ignore"] }).stdout ?? "";
88
101
  if (/\d+\.\d+\.\d+/.test(reported) && !versionAtLeast(reported)) {