@outerlayer/cli 0.1.1 → 0.4.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 (143) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +238 -77
  3. package/dist/agent-setup-IFH3FB5X.js +197 -0
  4. package/dist/build-TZUUTRGH.js +9 -0
  5. package/dist/build-info.json +1 -1
  6. package/dist/check-WQP2CZDW.js +94 -0
  7. package/dist/chunk-2E54Z3P5.js +232 -0
  8. package/dist/chunk-3TT7RQYB.js +55 -0
  9. package/dist/chunk-3YPKXE5L.js +1683 -0
  10. package/dist/{chunk-HNCCD2IX.js → chunk-5DLBAXY5.js} +209 -61
  11. package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
  12. package/dist/chunk-5Y3QQRUH.js +300 -0
  13. package/dist/chunk-6Y63SXFO.js +383 -0
  14. package/dist/chunk-774HEQL6.js +178 -0
  15. package/dist/chunk-7A6RVXPZ.js +2233 -0
  16. package/dist/chunk-7YIOOFVJ.js +71 -0
  17. package/dist/chunk-ABIWDUMS.js +104 -0
  18. package/dist/chunk-AUFG23AA.js +534 -0
  19. package/dist/chunk-B2G7JHHB.js +42 -0
  20. package/dist/chunk-BCJNHMZT.js +34 -0
  21. package/dist/chunk-BCLJSQCV.js +56 -0
  22. package/dist/chunk-BFESLKPP.js +61 -0
  23. package/dist/chunk-BJ3KTMDH.js +63 -0
  24. package/dist/chunk-BKO6JEZI.js +47 -0
  25. package/dist/chunk-BOLTI6LR.js +37 -0
  26. package/dist/chunk-CBPPJSAR.js +89 -0
  27. package/dist/chunk-CBZB6XY6.js +102 -0
  28. package/dist/{chunk-RCQXYLMO.js → chunk-DVDEBNQJ.js} +18 -18
  29. package/dist/{work-pr-cmd-GOYSPEN2.js → chunk-DVYHCMB2.js} +10 -25
  30. package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
  31. package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
  32. package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
  33. package/dist/chunk-F6GFZXZ3.js +60 -0
  34. package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
  35. package/dist/chunk-FAJSS2WN.js +1357 -0
  36. package/dist/chunk-FS2VYGZT.js +1764 -0
  37. package/dist/chunk-FV2CCGXK.js +9 -0
  38. package/dist/{chunk-BQB3U2VD.js → chunk-G7FSV7JX.js} +5113 -3729
  39. package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
  40. package/dist/chunk-HE2D4EY5.js +83 -0
  41. package/dist/chunk-L2DBRKRA.js +94 -0
  42. package/dist/chunk-LF6MLJCY.js +38 -0
  43. package/dist/chunk-M5POMKKW.js +1051 -0
  44. package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
  45. package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
  46. package/dist/chunk-N6LURUEF.js +64 -0
  47. package/dist/chunk-NYC54VBD.js +1084 -0
  48. package/dist/chunk-OGFGZQA3.js +23 -0
  49. package/dist/chunk-QOZKPLVJ.js +226 -0
  50. package/dist/chunk-RA3O54FC.js +30 -0
  51. package/dist/chunk-TBT347UY.js +41 -0
  52. package/dist/{chunk-3VQ4XXQA.js → chunk-UFFXNXLQ.js} +115 -71
  53. package/dist/chunk-USO2DKBD.js +421 -0
  54. package/dist/chunk-W4SQCOZU.js +206 -0
  55. package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
  56. package/dist/chunk-WO2BXCTQ.js +83 -0
  57. package/dist/chunk-WZBHBH3J.js +20 -0
  58. package/dist/{chunk-IWIUYLDR.js → chunk-XCCLFVXM.js} +118 -189
  59. package/dist/{context-materialize-J6CRQ7O7.js → chunk-XDDW4FRS.js} +134 -26
  60. package/dist/chunk-YYYXRJUW.js +101 -0
  61. package/dist/chunk-ZM2IMMYN.js +76 -0
  62. package/dist/chunk-ZMYPLFG3.js +71 -0
  63. package/dist/chunk-ZNA27WEV.js +470 -0
  64. package/dist/{cli-EFJIG2VT.js → cli-NHUXABYH.js} +597 -965
  65. package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
  66. package/dist/config-XVJYZ4IQ.js +9 -0
  67. package/dist/connect-cmd-OOG4TPU7.js +16 -0
  68. package/dist/context-adopt-UMA4O5NY.js +105 -0
  69. package/dist/context-materialize-G2FUFC72.js +19 -0
  70. package/dist/dist-RW4UPPC2.js +6 -0
  71. package/dist/docker-NEGDLU6D.js +7 -0
  72. package/dist/doctor-53F2PYY7.js +43 -0
  73. package/dist/doctor-UFVL4PZY.js +426 -0
  74. package/dist/{emit-artifact-cmd-KPPD3YLO.js → emit-artifact-cmd-KTYW2TN2.js} +25 -19
  75. package/dist/emit-cmd-DCERAU27.js +9 -0
  76. package/dist/emit-criteria-cmd-VAUUSAYD.js +166 -0
  77. package/dist/{emit-finding-cmd-VR5VEGXJ.js → emit-finding-cmd-UR4ID45Z.js} +35 -30
  78. package/dist/{emit-result-cmd-YOA5BJKL.js → emit-result-cmd-FDTUKGKE.js} +35 -61
  79. package/dist/exec-client-4XGXV3XN.js +8 -0
  80. package/dist/guest-init-GENRHNP7.js +8 -0
  81. package/dist/hook-fast-EYENPK6F.js +9 -0
  82. package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
  83. package/dist/host-key-KDYZ5ECC.js +7 -0
  84. package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
  85. package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
  86. package/dist/index.js +4 -4
  87. package/dist/init-DXBCOAJK.js +36 -0
  88. package/dist/init-YEXK35CF.js +221 -0
  89. package/dist/init-cmd-NHR7PRSQ.js +135 -0
  90. package/dist/install-cmd-7WXOIFDO.js +55 -0
  91. package/dist/lima-XN65D7GN.js +55 -0
  92. package/dist/login-browser-7XGSV7MA.js +10 -0
  93. package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
  94. package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
  95. package/dist/loop-VZOLL4WQ.js +43 -0
  96. package/dist/machine-WSG52J75.js +35 -0
  97. package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
  98. package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-JPTP5FMS.js} +20 -9
  99. package/dist/paths-OYKMVYJP.js +6 -0
  100. package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
  101. package/dist/real-deps-SU24ZA2K.js +21 -0
  102. package/dist/relay-L76HDX72.js +46 -0
  103. package/dist/settings-J2652U5N.js +7 -0
  104. package/dist/starter-pack-LIZYMKYQ.js +10 -0
  105. package/dist/{status-JHVSZYC5.js → status-2UZKKNB7.js} +31 -12
  106. package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
  107. package/dist/sync-cmd-VPPYCNNM.js +28 -0
  108. package/dist/version-BWM6VLDI.js +6 -0
  109. package/dist/{watch-NRLXJUST.js → watch-5C4ZBBGO.js} +20 -7
  110. package/dist/{work-claim-cmd-HXB7K27H.js → work-claim-cmd-LSO2B2EX.js} +34 -19
  111. package/dist/work-cmd-UOQO2AD4.js +17 -0
  112. package/dist/work-comment-cmd-WF3EOLAE.js +106 -0
  113. package/dist/{work-launch-YNNCKIH3.js → work-launch-YI4CJDAP.js} +2 -1
  114. package/dist/work-open-pr-cmd-4SSQZMYG.js +156 -0
  115. package/dist/work-pr-cmd-6AN6RRQJ.js +16 -0
  116. package/package.json +13 -3
  117. package/skill-pack/maintained/amend/SKILL.md +104 -0
  118. package/skill-pack/maintained/emitting-evidence/SKILL.md +99 -0
  119. package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
  120. package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
  121. package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
  122. package/skill-pack/template/build/SKILL.md +193 -0
  123. package/skill-pack/template/build/references/agent-briefs.md +243 -0
  124. package/skill-pack/template/build/references/criteria-judge.md +91 -0
  125. package/skill-pack/template/build/references/evidence.md +42 -0
  126. package/skill-pack/template/build/references/release.md +74 -0
  127. package/skill-pack/template/build/references/review-briefs.md +275 -0
  128. package/skill-pack/template/build/references/review-loop.md +158 -0
  129. package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
  130. package/skill-pack/template/spec/SKILL.md +84 -0
  131. package/skill-pack/template/writing-specs/SKILL.md +134 -0
  132. package/dist/chunk-JJP7YLMN.js +0 -25
  133. package/dist/chunk-KKEU6FFR.js +0 -589
  134. package/dist/chunk-OZ7C3XUE.js +0 -34
  135. package/dist/chunk-WQ6VGRGZ.js +0 -150
  136. package/dist/emit-commit-credit-cmd-656BRAQ4.js +0 -121
  137. package/dist/hook-fast-I2XTHDE6.js +0 -8
  138. package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
  139. package/dist/init-PTBITAUO.js +0 -103
  140. package/dist/login-cmd-IRX6LZT7.js +0 -62
  141. package/dist/loop-BWE2BS6J.js +0 -646
  142. package/dist/sync-cmd-S7Q6DWU3.js +0 -15
  143. package/dist/work-cmd-ABP6XDBP.js +0 -16
@@ -1,17 +1,55 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { readPin, pinExists, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, readContextManifest, isLastCheckFresh, manifestFilesPresent, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-A3WLZX2F.js';
4
- import { parseGoverns, normalizeRepoName, CONTEXT_SOURCE_CONFIG_PATH } from './chunk-KFYJV2ZG.js';
3
+ import { readPin, pinExists, readBuildHold, readContextManifest, manifestFilesPresent, writeDegraded, writeSourceCache, SOURCE_CACHE_TTL_RETRY_MS, SOURCE_CACHE_TTL_SUCCESS_MS, isLastCheckFresh, readDegraded, touchLastCheck, clearDegraded, writeContextManifest, writePinIfChanged, readSourceCache, isSourceCacheFresh } from './chunk-WIOZAJ2W.js';
4
+ import { loadMaintainedFiles, writeMaintainedFiles } from './chunk-WO2BXCTQ.js';
5
+ import { parseGoverns, normalizeRepoName, CONTEXT_SOURCE_CONFIG_PATH, parseOuterlayerConfig, classifyTree, emitTree } from './chunk-EMY4I27X.js';
5
6
  import { appendContextProvenance } from './chunk-ZJSNLVCE.js';
6
- import { ensureContextExcludes, ensurePreCommitGuardHook } from './chunk-IWIUYLDR.js';
7
- import { readCloudConfig } from './chunk-TFUIDMOB.js';
7
+ import { defaultFetch } from './chunk-BCLJSQCV.js';
8
+ import { resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
9
+ import { ensureContextExcludes, ensurePreCommitGuardHook } from './chunk-XCCLFVXM.js';
8
10
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
9
- import './chunk-Z5GEFMRW.js';
10
11
  import { execFileSync } from 'child_process';
11
- import { existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync, readdirSync, rmSync, lstatSync } from 'fs';
12
- import { isAbsolute, join, dirname, relative } from 'path';
12
+ import { lstatSync, existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync, readdirSync, rmSync, readFileSync, copyFileSync } from 'fs';
13
+ import { join, isAbsolute, dirname, relative } from 'path';
13
14
 
14
15
  createRequire(import.meta.url);
16
+ var CONFIG_PATH = ".outerlayer/config.json";
17
+ var CONTENT_KINDS = /* @__PURE__ */ new Set(["skill", "skill-reference"]);
18
+ function overlayMaintainedSkills(repoRoot, unsafe) {
19
+ const sources = loadMaintainedFiles().filter((file) => !unsafe(file.path));
20
+ writeMaintainedFiles(repoRoot, sources);
21
+ const paths = sources.map((file) => file.path);
22
+ const configAbs = join(repoRoot, CONFIG_PATH);
23
+ if (!existsSync(configAbs)) return paths;
24
+ const config = parseOuterlayerConfig(readFileSync(configAbs, "utf8"));
25
+ if (!config.ok || !config.config) return paths;
26
+ const { entries } = classifyTree(sources.map((file) => file.path));
27
+ const input = {
28
+ entries,
29
+ contents: new Map(entries.filter((e) => CONTENT_KINDS.has(e.kind)).map((e) => [e.path, sources.find((f) => f.path === e.path).content])),
30
+ assetPaths: [],
31
+ targets: config.config.targets
32
+ };
33
+ const compiled = emitTree(input);
34
+ if (compiled.errors.length > 0) return paths;
35
+ for (const file of compiled.files) {
36
+ if (unsafe(file.path)) continue;
37
+ const abs = join(repoRoot, file.path);
38
+ mkdirSync(dirname(abs), { recursive: true });
39
+ if (!existsSync(abs) || readFileSync(abs, "utf8") !== file.content) writeFileSync(abs, file.content);
40
+ paths.push(file.path);
41
+ }
42
+ for (const copy of compiled.copies) {
43
+ if (unsafe(copy.toPath)) continue;
44
+ const abs = join(repoRoot, copy.toPath);
45
+ mkdirSync(dirname(abs), { recursive: true });
46
+ copyFileSync(join(repoRoot, copy.fromSourcePath), abs);
47
+ paths.push(copy.toPath);
48
+ }
49
+ return paths;
50
+ }
51
+
52
+ // src/context-materialize.ts
15
53
  var defaultGitRunner = (args, opts) => {
16
54
  try {
17
55
  const stdout = execFileSync("git", args, {
@@ -54,8 +92,11 @@ function gitText(git, cwd, args, timeoutMs) {
54
92
  var NOT_GOVERNED_MESSAGE = "this repository's control plane no longer governs it";
55
93
  var ADOPTION_MESSAGE = "committed context files shadow the control plane \u2014 remove them in a commit";
56
94
  var CANNOT_IDENTIFY_MESSAGE = "this repository is governed, but the control plane cannot be identified \u2014 check OuterLayer connectivity/config";
57
- var DESIGNATION_AUTH_REJECTED_MESSAGE = "the OuterLayer API key was rejected (401/403) \u2014 check apiKey in ~/.outerlayer/config.json";
95
+ function designationAuthRejectedMessage(credential) {
96
+ return credential.startsWith("olu_") ? "the OuterLayer login was rejected (401/403) \u2014 run `outerlayer login` to sign in again" : "the OuterLayer API key was rejected (401/403) \u2014 check apiKey in ~/.outerlayer/config.json";
97
+ }
58
98
  var PARTIAL_WRITE_MESSAGE = "control-plane content could not be fully written \u2014 running on previous context";
99
+ var OVERLAY_FAILED_MESSAGE = "control-plane content was written but the maintained skills could not be refreshed \u2014 retrying next session";
59
100
  var COMMIT_GUARD_FOREIGN_NOT_INSTALLED_MESSAGE = "the materialized-context commit guard is NOT installed \u2014 a pre-commit hook this clone did not write occupies that slot \u2014 resolve the conflict or run `outerlayer hooks install-git`";
60
101
  var COMMIT_GUARD_HOOKSPATH_NOT_INSTALLED_MESSAGE = "the materialized-context commit guard is NOT installed \u2014 hooks are managed via core.hooksPath, which this clone never writes to automatically \u2014 adopt it yourself: `outerlayer hooks install-git --print`";
61
102
  function guardNotInstalledMessage(guard) {
@@ -100,7 +141,7 @@ async function materializeContext(deps) {
100
141
  const home = deps.home ?? homeFromEnv();
101
142
  const now = deps.now ?? Date.now;
102
143
  const git = deps.git ?? defaultGitRunner;
103
- const fetchImpl = deps.fetchImpl ?? fetch;
144
+ const fetchImpl = deps.fetchImpl ?? defaultFetch;
104
145
  const repoRoot = gitText(git, deps.cwd, ["rev-parse", "--show-toplevel"], 2e3);
105
146
  if (repoRoot === void 0) {
106
147
  return { status: "not-a-repo", sha: null, verification: "none" };
@@ -125,34 +166,53 @@ async function materializeContext(deps) {
125
166
  clearDegraded(gitCommonDir);
126
167
  return { status: "self", sha: null, verification: "none" };
127
168
  };
169
+ const hold = readBuildHold(gitCommonDir);
170
+ if (hold !== void 0) {
171
+ const heldManifest = readContextManifest(gitCommonDir);
172
+ if (heldManifest !== void 0 && heldManifest.sha === hold.sha && pin?.sha === hold.sha && manifestFilesPresent(repoRoot, heldManifest)) {
173
+ ensureContextExcludes(gitCommonDir);
174
+ recordMaintainedOverlay(repoRoot, gitCommonDir, heldManifest);
175
+ const guardMessage2 = guardNotInstalledMessage(ensurePreCommitGuardHook(repoRoot, gitCommonDir));
176
+ record(hold.sha, "verified", hold.repository, hold.ref);
177
+ return {
178
+ status: "debounced",
179
+ sha: hold.sha,
180
+ verification: "verified",
181
+ ...guardMessage2 ? { message: guardMessage2 } : {}
182
+ };
183
+ }
184
+ }
128
185
  const ownRepoName = deriveOwnRepoName(git, repoRoot);
129
186
  if (ownRepoName === void 0) {
130
187
  return selfOutcome();
131
188
  }
132
- const designation = await resolveDesignation({ git, fetchImpl, home, now, gitCommonDir, ownRepoName });
189
+ const designation = await resolveDesignation({ git, fetchImpl, home, cloud: deps.cloud, now, gitCommonDir, ownRepoName, repoRoot });
190
+ const answered = { designation: "answered" };
133
191
  if (designation.status === "self") {
134
- return selfOutcome();
192
+ return { ...selfOutcome(), ...answered };
135
193
  }
136
194
  if (designation.status === "unknown") {
137
- const message = designation.authRejected ? DESIGNATION_AUTH_REJECTED_MESSAGE : CANNOT_IDENTIFY_MESSAGE;
195
+ const message = designation.authRejected ?? CANNOT_IDENTIFY_MESSAGE;
138
196
  if (hasPin) {
139
197
  writeDegraded(gitCommonDir, { at: new Date(now()).toISOString(), kind: "designation-unverified", message });
140
198
  record(null, "none");
141
- return { status: "designation-unverified", message, sha: null, verification: "none" };
199
+ return { status: "designation-unverified", message, sha: null, verification: "none", designation: "unknown" };
142
200
  }
143
201
  if (designation.authRejected) {
144
202
  writeDegraded(gitCommonDir, { at: new Date(now()).toISOString(), kind: "designation-unverified", message });
145
203
  }
146
204
  writeSourceCache(gitCommonDir, { checkedAt: new Date(now()).toISOString(), status: "self", ttlMs: SOURCE_CACHE_TTL_RETRY_MS });
147
- return { status: "self", sha: null, verification: "none" };
205
+ return { status: "self", sha: null, verification: "none", designation: "unknown", ...designation.authRejected ? { authRejected: true } : {} };
148
206
  }
149
207
  let repository;
150
208
  let ref;
151
209
  let unverifiedIdentity;
152
210
  let preFetched;
211
+ let unreadableCandidates = [];
153
212
  if (designation.status === "candidates") {
154
213
  const sortedCandidates = [...designation.candidates].sort((a, b) => a.repository.localeCompare(b.repository));
155
214
  const claimants = [];
215
+ const unfetched = [];
156
216
  for (const candidate of sortedCandidates) {
157
217
  const candidateUrl = deps.resolveControlPlaneUrl ? deps.resolveControlPlaneUrl(candidate.repository, candidate.ref, repoRoot) : buildControlPlaneUrl(git, repoRoot, candidate.repository);
158
218
  const candidateHost = parseRemoteHost(candidateUrl).host || "unknown-host";
@@ -162,9 +222,15 @@ async function materializeContext(deps) {
162
222
  cwd: candidateCacheDir,
163
223
  timeoutMs: 1e4
164
224
  });
165
- if (!candidateFetch.ok) continue;
225
+ if (!candidateFetch.ok) {
226
+ unfetched.push(candidate.repository);
227
+ continue;
228
+ }
166
229
  const candidateSha = gitText(git, candidateCacheDir, ["rev-parse", candidateLocalRef], 2e3);
167
- if (candidateSha === void 0) continue;
230
+ if (candidateSha === void 0) {
231
+ unfetched.push(candidate.repository);
232
+ continue;
233
+ }
168
234
  const candidateConfig = readBlobAtPath(git, candidateCacheDir, candidateSha, CONTEXT_SOURCE_CONFIG_PATH);
169
235
  const candidateGoverns = candidateConfig === void 0 ? { governs: [] } : parseGoverns(candidateConfig);
170
236
  const candidateGovernsList = "error" in candidateGoverns ? [] : candidateGoverns.governs;
@@ -174,7 +240,7 @@ async function materializeContext(deps) {
174
240
  }
175
241
  if (claimants.length === 0) {
176
242
  writeSourceCache(gitCommonDir, { checkedAt: new Date(now()).toISOString(), status: "self", ttlMs: SOURCE_CACHE_TTL_SUCCESS_MS });
177
- return selfOutcome();
243
+ return { ...selfOutcome(), ...answered, ...unfetched.length > 0 ? { unfetched } : {} };
178
244
  }
179
245
  if (claimants.length > 1) {
180
246
  const names = claimants.map((c) => c.repository).sort();
@@ -186,6 +252,7 @@ async function materializeContext(deps) {
186
252
  record(lastGood, verification);
187
253
  return { status: "conflict", message, sha: lastGood, verification };
188
254
  }
255
+ unreadableCandidates = unfetched;
189
256
  const winner = claimants[0];
190
257
  writeSourceCache(gitCommonDir, {
191
258
  checkedAt: new Date(now()).toISOString(),
@@ -227,6 +294,7 @@ async function materializeContext(deps) {
227
294
  const degraded = readDegraded(gitCommonDir);
228
295
  const verification = degraded ? "unverified" : "verified";
229
296
  ensureContextExcludes(gitCommonDir);
297
+ recordMaintainedOverlay(repoRoot, gitCommonDir, manifest);
230
298
  const guardMessage2 = guardNotInstalledMessage(ensurePreCommitGuardHook(repoRoot, gitCommonDir));
231
299
  record(manifest.sha, verification, repository, ref);
232
300
  return {
@@ -255,6 +323,7 @@ async function materializeContext(deps) {
255
323
  touchLastCheck(gitCommonDir, now());
256
324
  clearOrFlagIdentity();
257
325
  ensureContextExcludes(gitCommonDir);
326
+ recordMaintainedOverlay(repoRoot, gitCommonDir, manifest);
258
327
  const guardMessage2 = guardNotInstalledMessage(ensurePreCommitGuardHook(repoRoot, gitCommonDir));
259
328
  writeSourceCache(gitCommonDir, { checkedAt: new Date(now()).toISOString(), status: "governed", repository, ref, ttlMs: SOURCE_CACHE_TTL_SUCCESS_MS });
260
329
  record(remoteSha, "verified", repository, ref);
@@ -335,14 +404,36 @@ async function materializeContext(deps) {
335
404
  if (!writeResult.ok) {
336
405
  return partialWrite();
337
406
  }
338
- const deleteResult = manifest ? deleteRemovedFiles(repoRoot, manifest.files, writeResult.writtenPaths) : { skipped: [] };
407
+ let overlayPaths = [];
408
+ let overlayFailed = false;
409
+ try {
410
+ overlayPaths = overlayMaintainedSkills(repoRoot, (rel) => hasSymlinkComponent(repoRoot, rel));
411
+ } catch {
412
+ overlayFailed = true;
413
+ }
414
+ const writtenPaths = [.../* @__PURE__ */ new Set([...writeResult.writtenPaths, ...overlayPaths])];
415
+ const deleteResult = manifest ? deleteRemovedFiles(repoRoot, manifest.files, writtenPaths) : { skipped: [] };
416
+ if (overlayFailed && deleteResult.skipped.length === 0) {
417
+ writeContextManifest(gitCommonDir, { schemaVersion: 1, sha, files: writtenPaths, materializedAt: new Date(now()).toISOString() });
418
+ writeDegraded(gitCommonDir, {
419
+ at: new Date(now()).toISOString(),
420
+ kind: "unreachable",
421
+ message: OVERLAY_FAILED_MESSAGE,
422
+ repository,
423
+ ref,
424
+ lastGoodSha: sha
425
+ });
426
+ if (!evidenced) return { status: "unreachable", sha: null, verification: "none" };
427
+ record(sha, "unverified", repository, ref);
428
+ return { status: "unreachable", message: OVERLAY_FAILED_MESSAGE, sha, verification: "unverified" };
429
+ }
339
430
  if (deleteResult.skipped.length > 0) {
340
431
  return partialWrite();
341
432
  }
342
433
  const newManifest = {
343
434
  schemaVersion: 1,
344
435
  sha,
345
- files: writeResult.writtenPaths,
436
+ files: writtenPaths,
346
437
  materializedAt: new Date(now()).toISOString()
347
438
  };
348
439
  writeContextManifest(gitCommonDir, newManifest);
@@ -357,23 +448,40 @@ async function materializeContext(deps) {
357
448
  status: "materialized",
358
449
  sha,
359
450
  verification: "verified",
360
- ...guardMessage ? { message: guardMessage } : {}
451
+ ...guardMessage ? { message: guardMessage } : {},
452
+ ...unreadableCandidates.length > 0 ? { unfetched: unreadableCandidates } : {}
361
453
  };
362
454
  }
455
+ function recordMaintainedOverlay(repoRoot, gitCommonDir, manifest) {
456
+ let overlayPaths;
457
+ try {
458
+ overlayPaths = overlayMaintainedSkills(repoRoot, (rel) => hasSymlinkComponent(repoRoot, rel));
459
+ } catch {
460
+ return;
461
+ }
462
+ const known = new Set(manifest.files);
463
+ const added = overlayPaths.filter((path) => !known.has(path));
464
+ if (added.length > 0) writeContextManifest(gitCommonDir, { ...manifest, files: [...manifest.files, ...added] });
465
+ }
363
466
  async function resolveDesignation(ctx) {
364
467
  const cache = readSourceCache(ctx.gitCommonDir);
365
468
  if (cache && isSourceCacheFresh(cache, ctx.now())) {
366
469
  return cache.status === "governed" && cache.repository && cache.ref ? { status: "governed", repository: cache.repository, ref: cache.ref } : { status: "self" };
367
470
  }
368
- let authRejected = false;
369
- const config = readCloudConfig(ctx.home);
370
- if (config.url && config.apiKey && config.appId) {
471
+ let authRejected;
472
+ const destination = resolveGatewayCredentials({
473
+ home: ctx.home,
474
+ env: {},
475
+ cwd: ctx.repoRoot,
476
+ ...ctx.cloud === void 0 ? {} : { url: ctx.cloud.url, apiKey: ctx.cloud.apiKey, appId: ctx.cloud.appId }
477
+ });
478
+ if (destination.ok) {
371
479
  const sources = await fetchContextSource(
372
- config,
480
+ { url: destination.url, apiKey: destination.credential, appId: destination.appId },
373
481
  ctx.ownRepoName,
374
482
  ctx.fetchImpl,
375
483
  () => {
376
- authRejected = true;
484
+ authRejected = designationAuthRejectedMessage(destination.credential);
377
485
  }
378
486
  );
379
487
  if (sources !== void 0) {
@@ -586,4 +694,4 @@ function pruneEmptyDirs(dir, stopAt) {
586
694
  }
587
695
  }
588
696
 
589
- export { defaultGitRunner, materializeContext, parseOwnerRepo, parseRemoteHost };
697
+ export { defaultGitRunner, hasSymlinkComponent, materializeContext, parseOwnerRepo, parseRemoteHost, resolveGitCommonDir };
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { runnerDir, RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
4
+ import { readlinkSync, realpathSync, mkdirSync, unlinkSync, symlinkSync, renameSync, readFileSync, writeFileSync, rmSync, existsSync, readdirSync } from 'fs';
5
+ import { join, basename, sep, resolve } from 'path';
6
+
7
+ createRequire(import.meta.url);
8
+ var PACKAGE_PATH = join("node_modules", "@outerlayer", "cli");
9
+ var STAGING_PREFIX = ".staging-";
10
+ function versionsDir(configPath) {
11
+ return join(runnerDir(configPath), "versions");
12
+ }
13
+ function versionDir(configPath, version) {
14
+ return join(versionsDir(configPath), version);
15
+ }
16
+ function stagingDir(configPath, version) {
17
+ return join(versionsDir(configPath), `${STAGING_PREFIX}${version}`);
18
+ }
19
+ function currentLink(configPath) {
20
+ return join(runnerDir(configPath), "current");
21
+ }
22
+ function markerPath(configPath) {
23
+ return join(runnerDir(configPath), "update-marker.json");
24
+ }
25
+ function packageRootIn(dir) {
26
+ return join(dir, PACKAGE_PATH);
27
+ }
28
+ function entryIn(dir) {
29
+ return join(packageRootIn(dir), "dist", "index.js");
30
+ }
31
+ function currentVersion(configPath) {
32
+ try {
33
+ return basename(readlinkSync(currentLink(configPath)));
34
+ } catch {
35
+ return void 0;
36
+ }
37
+ }
38
+ function isInLayout(configPath, entryPath) {
39
+ try {
40
+ const versions = realpathSync(versionsDir(configPath));
41
+ return realpathSync(entryPath).startsWith(`${versions}${sep}`);
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+ function startedThroughCurrent(configPath, entryPath) {
47
+ const parts = resolve(entryPath).split(sep);
48
+ const at = parts.lastIndexOf("current");
49
+ if (at <= 0 || parts.slice(at + 1).join(sep) !== join(PACKAGE_PATH, "dist", "index.js")) return false;
50
+ try {
51
+ return realpathSync(parts.slice(0, at).join(sep) || sep) === realpathSync(runnerDir(configPath));
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+ function switchCurrent(configPath, version) {
57
+ mkdirSync(runnerDir(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
58
+ const temporary = `${currentLink(configPath)}.new`;
59
+ try {
60
+ unlinkSync(temporary);
61
+ } catch (error) {
62
+ if (error.code !== "ENOENT") throw error;
63
+ }
64
+ symlinkSync(versionDir(configPath, version), temporary);
65
+ renameSync(temporary, currentLink(configPath));
66
+ }
67
+ function readMarker(configPath) {
68
+ try {
69
+ const parsed = JSON.parse(readFileSync(markerPath(configPath), "utf8"));
70
+ if (typeof parsed.from !== "string" || typeof parsed.to !== "string") return void 0;
71
+ return { from: parsed.from, to: parsed.to, starts: typeof parsed.starts === "number" && Number.isInteger(parsed.starts) ? parsed.starts : 0 };
72
+ } catch {
73
+ return void 0;
74
+ }
75
+ }
76
+ function writeMarker(configPath, marker) {
77
+ mkdirSync(runnerDir(configPath), { recursive: true, mode: RUNNER_DIR_MODE });
78
+ const temporary = `${markerPath(configPath)}.tmp`;
79
+ writeFileSync(temporary, `${JSON.stringify(marker)}
80
+ `, { mode: RUNNER_FILE_MODE });
81
+ renameSync(temporary, markerPath(configPath));
82
+ }
83
+ function removeMarker(configPath) {
84
+ rmSync(markerPath(configPath), { force: true });
85
+ }
86
+ function cleanVersions(configPath, keep) {
87
+ const dir = versionsDir(configPath);
88
+ if (!existsSync(dir)) return;
89
+ for (const name of readdirSync(dir)) {
90
+ if (keep.includes(name)) continue;
91
+ rmSync(join(dir, name), { recursive: true, force: true });
92
+ }
93
+ }
94
+ function execStartCommand(configPath, nodePath) {
95
+ return `${nodePath} ${join(currentLink(configPath), "node_modules", "@outerlayer", "cli", "dist", "index.js")} runner start --config ${configPath}`;
96
+ }
97
+ function execStartLine(configPath, nodePath) {
98
+ return `ExecStart=${execStartCommand(configPath, nodePath)}`;
99
+ }
100
+
101
+ export { cleanVersions, currentVersion, entryIn, execStartCommand, execStartLine, isInLayout, packageRootIn, readMarker, removeMarker, stagingDir, startedThroughCurrent, switchCurrent, versionDir, writeMarker };
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { join, dirname } from 'path';
4
+
5
+ createRequire(import.meta.url);
6
+ var RUNNER_DIR_MODE = 448;
7
+ var RUNNER_FILE_MODE = 384;
8
+ function runnerDir(configPath) {
9
+ return join(dirname(configPath), "runner");
10
+ }
11
+ function hostKeyPath(configPath) {
12
+ return join(runnerDir(configPath), "host-key.pem");
13
+ }
14
+ function pidFilePath(configPath) {
15
+ return join(runnerDir(configPath), "runner.pid");
16
+ }
17
+ function jobsDir(configPath) {
18
+ return join(runnerDir(configPath), "jobs");
19
+ }
20
+ function jobDir(configPath, queue, item, claimId, attempt) {
21
+ const suffix = attempt === void 0 ? "" : `-${attempt}`;
22
+ return join(jobsDir(configPath), `${queue}-${item}-${claimId}${suffix}`);
23
+ }
24
+ function attemptStamp(at) {
25
+ return at.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
26
+ }
27
+ function imagesDir(configPath) {
28
+ return join(runnerDir(configPath), "images");
29
+ }
30
+ function repoCacheDir(configPath, owner, name) {
31
+ return join(runnerDir(configPath), "repos", owner, name);
32
+ }
33
+ function buildsDir(configPath) {
34
+ return join(runnerDir(configPath), "builds");
35
+ }
36
+ function buildDir(configPath, buildId) {
37
+ return join(buildsDir(configPath), buildId);
38
+ }
39
+ function ghConfigDir(buildDirPath) {
40
+ return join(buildDirPath, "gh");
41
+ }
42
+ function mirrorDir(runnerDirPath, owner, name) {
43
+ return join(runnerDirPath, "mirrors", owner, `${name}.git`);
44
+ }
45
+ function spoolDir(runnerDirPath) {
46
+ return join(runnerDirPath, "spool");
47
+ }
48
+ function contextHomeDir(configPath) {
49
+ return join(runnerDir(configPath), "context-home");
50
+ }
51
+ function stagedContextDir(buildDirPath) {
52
+ return join(buildDirPath, "context");
53
+ }
54
+ function runnerDirOfBuild(buildDirPath) {
55
+ return dirname(dirname(buildDirPath));
56
+ }
57
+ function vmDir(runnerDirPath) {
58
+ return join(runnerDirPath, "vm");
59
+ }
60
+ function vmAssetsDir(runnerDirPath) {
61
+ return join(vmDir(runnerDirPath), "assets");
62
+ }
63
+ function rootfsDir(runnerDirPath) {
64
+ return join(vmDir(runnerDirPath), "rootfs");
65
+ }
66
+ function rootfsPathFor(runnerDirPath, imageId) {
67
+ return join(rootfsDir(runnerDirPath), `${imageId.replace(/^sha256:/, "").replace(/[^A-Za-z0-9._-]/g, "_")}.ext4`);
68
+ }
69
+ function diskTemplatesDir(runnerDirPath) {
70
+ return join(vmDir(runnerDirPath), "templates");
71
+ }
72
+ function vsockPath(buildDirPath) {
73
+ return join(buildDirPath, "v.sock");
74
+ }
75
+
76
+ export { RUNNER_DIR_MODE, RUNNER_FILE_MODE, attemptStamp, buildDir, contextHomeDir, diskTemplatesDir, ghConfigDir, hostKeyPath, imagesDir, jobDir, jobsDir, mirrorDir, pidFilePath, repoCacheDir, rootfsDir, rootfsPathFor, runnerDir, runnerDirOfBuild, spoolDir, stagedContextDir, vmAssetsDir, vsockPath };
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { C } from './chunk-YRYMNBGQ.js';
4
+ import { resolvePackRoot, loadTemplateFiles, loadMaintainedFiles, TEMPLATE_SKILLS, skillDir, writeMaintainedFiles, MAINTAINED_SKILLS } from './chunk-WO2BXCTQ.js';
5
+ import { resolvePackageVersion } from './chunk-RA3O54FC.js';
6
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
7
+ import { join, dirname } from 'path';
8
+
9
+ createRequire(import.meta.url);
10
+ var DEFAULT_TEMPLATE = "default";
11
+ var POLICY_PATH = ".outerlayer/policy.yaml";
12
+ var POLICY_CONTENT = "extends: outerlayer:recommended@v1\nreview: required\n";
13
+ var StarterPackError = class extends Error {
14
+ };
15
+ function assertKnownTemplate(name) {
16
+ if (name !== DEFAULT_TEMPLATE) {
17
+ throw new StarterPackError(`unknown template "${name}" \u2014 the only template is "${DEFAULT_TEMPLATE}"`);
18
+ }
19
+ }
20
+ function installStarterPack(opts) {
21
+ const packRoot = opts.packRoot ?? resolvePackRoot();
22
+ const version = opts.version ?? resolvePackageVersion();
23
+ const templateFiles = loadTemplateFiles(packRoot, version);
24
+ const maintainedFiles = loadMaintainedFiles(packRoot, version);
25
+ const copied = [];
26
+ const kept = [];
27
+ for (const skill of TEMPLATE_SKILLS) {
28
+ if (existsSync(join(opts.repoRoot, skillDir(skill)))) {
29
+ kept.push(skill);
30
+ continue;
31
+ }
32
+ for (const file of templateFiles.filter((f) => f.skill === skill)) {
33
+ const abs = join(opts.repoRoot, file.path);
34
+ mkdirSync(dirname(abs), { recursive: true });
35
+ writeFileSync(abs, file.content);
36
+ }
37
+ copied.push(skill);
38
+ }
39
+ const written = writeMaintainedFiles(opts.repoRoot, maintainedFiles);
40
+ let policy = "kept";
41
+ const policyAbs = join(opts.repoRoot, POLICY_PATH);
42
+ if (!existsSync(policyAbs)) {
43
+ mkdirSync(dirname(policyAbs), { recursive: true });
44
+ writeFileSync(policyAbs, POLICY_CONTENT);
45
+ policy = "written";
46
+ }
47
+ return { copied, kept, maintained: [...written.created, ...written.rewritten], policy };
48
+ }
49
+ var skillFile = (skill) => `${skillDir(skill)}/SKILL.md`;
50
+ function formatStarterPackOutput(result) {
51
+ const lines = [];
52
+ lines.push(`${C.green}\u2713${C.reset} Installed the starter skills in .outerlayer/skills/`, "");
53
+ lines.push("Yours to edit \u2014 copied once, never overwritten:");
54
+ for (const skill of TEMPLATE_SKILLS) {
55
+ lines.push(
56
+ result.kept.includes(skill) ? ` ${C.dim}kept${C.reset} ${skillDir(skill)}/ ${C.dim}(already exists, left exactly as it is)${C.reset}` : ` ${skillFile(skill)}`
57
+ );
58
+ }
59
+ lines.push("", "Rewritten on every emit \u2014 edits are overwritten by the installed CLI:");
60
+ for (const skill of MAINTAINED_SKILLS) lines.push(` ${skillFile(skill)}`);
61
+ lines.push(
62
+ "",
63
+ result.policy === "written" ? `${C.green}\u2713${C.reset} Wrote ${POLICY_PATH} (extends the recommended checks, review required)` : `${C.dim}${POLICY_PATH} already exists \u2014 left as it is${C.reset}`,
64
+ "",
65
+ `Next: create .outerlayer/config.json naming your agent tools, for example {"targets": ["claude-code"]},`,
66
+ `then run ${C.yellow}outerlayer context emit${C.reset} to compile the skills into their native files.`
67
+ );
68
+ return lines.join("\n") + "\n";
69
+ }
70
+
71
+ export { StarterPackError, assertKnownTemplate, formatStarterPackOutput, installStarterPack };