@outerlayer/cli 0.22.2 → 0.24.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 (136) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +4 -0
  3. package/dist/agent-setup-QQYRBPK2.js +30 -0
  4. package/dist/{artifact-link-cmd-OWRI5AJX.js → artifact-link-cmd-LEI6EOZE.js} +2 -2
  5. package/dist/{bridge-QFN3KJP6.js → bridge-4ZARDVGS.js} +2 -2
  6. package/dist/{build-6WRNZPVR.js → build-OVZIT2G2.js} +5 -5
  7. package/dist/build-info.json +1 -1
  8. package/dist/{builder-G3MLDMTI.js → builder-MECDEKXX.js} +6 -6
  9. package/dist/{check-N6WLZCER.js → check-YYKNYFNI.js} +46 -44
  10. package/dist/{chunk-IPS2JCHK.js → chunk-5ECUMU53.js} +7 -7
  11. package/dist/{chunk-XR56WEA5.js → chunk-5XCAPBE2.js} +4 -4
  12. package/dist/{chunk-KOIMUHIS.js → chunk-6GSA2SKT.js} +20 -20
  13. package/dist/{chunk-HWZCE745.js → chunk-755WXDBI.js} +2 -2
  14. package/dist/{chunk-SFHR45FA.js → chunk-A62QFN2M.js} +3 -3
  15. package/dist/{chunk-URWYRAIC.js → chunk-BN3CSI7S.js} +0 -2
  16. package/dist/{chunk-ZCXP35T3.js → chunk-BOQY756V.js} +9 -8
  17. package/dist/{chunk-BVR6MQLS.js → chunk-BUBCPP3F.js} +3 -3
  18. package/dist/{chunk-G3IJ3N6F.js → chunk-BUP5N3X6.js} +1 -1
  19. package/dist/{chunk-WXJXHZVB.js → chunk-CZUH2SLQ.js} +1 -1
  20. package/dist/{chunk-AEKSX4EY.js → chunk-D4AMAEZ7.js} +13 -12
  21. package/dist/{chunk-U6RKHNC7.js → chunk-D7K446H6.js} +8 -8
  22. package/dist/{chunk-V5CG75NX.js → chunk-D7X5ITVT.js} +1 -1
  23. package/dist/{chunk-OBCFK4QD.js → chunk-DG2ULWM6.js} +2 -2
  24. package/dist/{chunk-SNVXHNU7.js → chunk-E4OUL7ZP.js} +5 -5
  25. package/dist/{chunk-B42BIYC6.js → chunk-F2W2IYNP.js} +3 -3
  26. package/dist/{chunk-JD6GWYY7.js → chunk-F7Y2USIM.js} +5 -5
  27. package/dist/{chunk-QS43O4DR.js → chunk-FKXGQOF5.js} +1 -1
  28. package/dist/{chunk-JV4YGAAW.js → chunk-FQPN5S4Q.js} +4 -4
  29. package/dist/{chunk-XACXXKQ5.js → chunk-FWEWFW3F.js} +1 -1
  30. package/dist/{chunk-WKSBSEWT.js → chunk-GU4PKXN7.js} +13 -13
  31. package/dist/{chunk-2CPOAPU4.js → chunk-H6IUKXXI.js} +90 -24
  32. package/dist/{chunk-5OCIHFGE.js → chunk-HBISTTG3.js} +32 -23
  33. package/dist/{chunk-EFB3FGLC.js → chunk-HHXBOTRO.js} +1 -1
  34. package/dist/chunk-IDSHLKQA.js +66 -0
  35. package/dist/chunk-JKNYTNYD.js +151 -0
  36. package/dist/{chunk-IHNQWF6F.js → chunk-KF37SUDM.js} +1 -1
  37. package/dist/{chunk-4AVDD7VX.js → chunk-LBCZXXOX.js} +2 -2
  38. package/dist/{chunk-NYTZ7HVN.js → chunk-LY2L3O2O.js} +1 -1
  39. package/dist/{chunk-USWVXQIH.js → chunk-MQ743JX3.js} +3 -3
  40. package/dist/{chunk-CQAEV3Z5.js → chunk-NGZID7UI.js} +1 -1
  41. package/dist/{chunk-UF5L4BAI.js → chunk-NTQTDCYR.js} +1 -1
  42. package/dist/{chunk-2Q3PZ42G.js → chunk-PDIM5T65.js} +1 -1
  43. package/dist/{chunk-OW52QLK3.js → chunk-PVRBE7S4.js} +1 -1
  44. package/dist/{chunk-7VUK5RFS.js → chunk-QZBXLLTV.js} +36 -1
  45. package/dist/{chunk-WFSUUUAN.js → chunk-RKOTJQVG.js} +1 -1
  46. package/dist/{chunk-FQE5RDHI.js → chunk-RVYOOYOA.js} +7 -7
  47. package/dist/{chunk-IMCVQF5F.js → chunk-TQ6Y7YV6.js} +6 -6
  48. package/dist/{chunk-I2UOAV7X.js → chunk-TQMI335S.js} +4 -1
  49. package/dist/{chunk-3AZYLAJC.js → chunk-TTLVQZSH.js} +4 -4
  50. package/dist/{chunk-EMUNEAQK.js → chunk-V5Q3S4NM.js} +19 -7
  51. package/dist/{chunk-FVDVC2GF.js → chunk-VBEXKEBV.js} +1 -1
  52. package/dist/{chunk-V7SAQ645.js → chunk-VJBFQP3H.js} +21 -12
  53. package/dist/{chunk-ZNPE7FW5.js → chunk-VPEGE3QZ.js} +1 -1
  54. package/dist/{chunk-276D7BJ7.js → chunk-VV7ZD5LR.js} +1 -1
  55. package/dist/{chunk-W3SWJ2TD.js → chunk-VWFOKRYS.js} +5 -2
  56. package/dist/{chunk-4542B3RR.js → chunk-W7O4LA4E.js} +3 -3
  57. package/dist/{chunk-4OYKJJ2V.js → chunk-WH2QV3UL.js} +86 -6
  58. package/dist/{chunk-XCRZTXRL.js → chunk-WX2WEERU.js} +63 -43
  59. package/dist/{chunk-VSUXCMMH.js → chunk-XFGL53GU.js} +51 -33
  60. package/dist/{chunk-EAQSOE62.js → chunk-XSPA5ODY.js} +21 -19
  61. package/dist/{chunk-QZX6DAAW.js → chunk-ZFLOFAUG.js} +11 -11
  62. package/dist/{claude-launcher-QD3C427Y.js → claude-launcher-JCPSHDZQ.js} +5 -5
  63. package/dist/{cli-A2Y67WAB.js → cli-ZTBH3FBG.js} +173 -137
  64. package/dist/{command-Q5Q2EIO4.js → command-DO4TERRT.js} +34 -33
  65. package/dist/{config-H4QS332G.js → config-7Z5DHSGY.js} +2 -2
  66. package/dist/connect-cmd-NY5LIKVC.js +25 -0
  67. package/dist/container-start-OHBUZEJJ.js +170 -0
  68. package/dist/{context-cleanup-DSA3HGQU.js → context-cleanup-LTH77FC3.js} +3 -3
  69. package/dist/{control-plane-launch-HE6OSWKO.js → control-plane-launch-GKGI6HJA.js} +11 -11
  70. package/dist/{dist-7IH56ELA.js → dist-2APO44JG.js} +2 -2
  71. package/dist/{docker-JU422JL7.js → docker-RKGBJESN.js} +6 -6
  72. package/dist/{doctor-BRN4BGMA.js → doctor-DOAFZV4O.js} +47 -34
  73. package/dist/{emit-artifact-cmd-GPAJCCUC.js → emit-artifact-cmd-VZHIDPXQ.js} +18 -15
  74. package/dist/emit-cmd-WQH2NK7H.js +15 -0
  75. package/dist/{emit-criteria-cmd-R3FKHSQG.js → emit-criteria-cmd-SYLBCWHW.js} +15 -12
  76. package/dist/{emit-finding-cmd-IK7RAVP5.js → emit-finding-cmd-52M43NPC.js} +15 -12
  77. package/dist/{emit-result-cmd-5FJTF7YD.js → emit-result-cmd-BTMTGDAV.js} +18 -15
  78. package/dist/{emit-step-cmd-VRTD5YPP.js → emit-step-cmd-6XTJITZT.js} +16 -13
  79. package/dist/{exec-client-ATPF2DDB.js → exec-client-CKPK62K2.js} +3 -3
  80. package/dist/governing-source-GWN7GVEN.js +35 -0
  81. package/dist/{guest-init-2OZ4YC7R.js → guest-init-ZBL5CN7Y.js} +4 -4
  82. package/dist/{hook-fast-QUGEI2IT.js → hook-fast-3JL4PXLG.js} +1 -1
  83. package/dist/{host-key-ATB23GI3.js → host-key-W2DLZGZ7.js} +3 -3
  84. package/dist/image-tag-AO4RBAKW.js +10 -0
  85. package/dist/{import-capture-cmd-Q56DDZVO.js → import-capture-cmd-TCOKNVBM.js} +12 -7
  86. package/dist/{import-ruler-cmd-T6LLO6Q7.js → import-ruler-cmd-VJOHOJQ7.js} +2 -2
  87. package/dist/index.js +4 -4
  88. package/dist/{init-X7FCQDFX.js → init-EQTR7AEF.js} +32 -31
  89. package/dist/init-TSQ3EOQW.js +52 -0
  90. package/dist/{init-cmd-VCKH2DUQ.js → init-cmd-AF2MH36A.js} +24 -23
  91. package/dist/init-update-SHISHMXV.js +555 -0
  92. package/dist/{install-SFACZU7E.js → install-ORQC26ZE.js} +4 -4
  93. package/dist/{install-cmd-6HTP7I43.js → install-cmd-4LA3UJZ4.js} +6 -6
  94. package/dist/layout-UOJQGEPW.js +10 -0
  95. package/dist/{login-browser-BN5UKPHS.js → login-browser-4OMZFP3U.js} +4 -3
  96. package/dist/{logout-cmd-RZFWTNYD.js → logout-cmd-5V5MMBSY.js} +2 -2
  97. package/dist/{logs-N3ZX7HFV.js → logs-5EPHIPZB.js} +2 -2
  98. package/dist/loop-4WUCS5JP.js +77 -0
  99. package/dist/machine-6UNLIPLE.js +47 -0
  100. package/dist/{mcp-install-cmd-GD4QUP7Z.js → mcp-install-cmd-GGWB3JJ2.js} +2 -2
  101. package/dist/{mcp-serve-cmd-TQSPFRC7.js → mcp-serve-cmd-KMWEKM7C.js} +5 -5
  102. package/dist/{paths-AKX2OOLF.js → paths-XY5ADO32.js} +2 -2
  103. package/dist/{pending-7SGWZCEH.js → pending-35J3ZPLR.js} +3 -3
  104. package/dist/{pidfile-G2J5WBQS.js → pidfile-OJ7RP4Q2.js} +3 -3
  105. package/dist/{record-Q4IEYIVV.js → record-BEJB4J7N.js} +3 -3
  106. package/dist/{runner-env-GZHXM27A.js → runner-env-ZJWJOJKQ.js} +5 -5
  107. package/dist/{runner-identity-FTHZFMGY.js → runner-identity-2OOUJSFJ.js} +6 -6
  108. package/dist/{runner-key-O42EUXQU.js → runner-key-WDFBSJRX.js} +32 -31
  109. package/dist/{session-sender-HF6DXFA2.js → session-sender-X3BBQZMF.js} +19 -16
  110. package/dist/{spec-cmd-ADPH236D.js → spec-cmd-QRF5YXKK.js} +16 -13
  111. package/dist/{starter-pack-5EQYGSJF.js → starter-pack-55EFILC5.js} +7 -4
  112. package/dist/{status-LXFIVGP3.js → status-ZCAAF6MD.js} +43 -34
  113. package/dist/{sync-cmd-5XS3CUV5.js → sync-cmd-UQK3RYAC.js} +18 -15
  114. package/dist/{watch-FBZHZV56.js → watch-MOQ3GQTE.js} +18 -15
  115. package/dist/{work-claim-cmd-LIIINECF.js → work-claim-cmd-RT343CLA.js} +19 -16
  116. package/dist/{work-cmd-SM6DY2WK.js → work-cmd-APXSER2D.js} +16 -13
  117. package/dist/{work-comment-cmd-VYVXGT4W.js → work-comment-cmd-AT5NUYUU.js} +16 -13
  118. package/dist/{work-logs-cmd-XZSCWWMW.js → work-logs-cmd-Z5DMNEJP.js} +16 -13
  119. package/dist/{work-open-pr-cmd-YZNC3P3R.js → work-open-pr-cmd-ZTS6MJHL.js} +17 -14
  120. package/dist/{work-pr-cmd-AMN23KFJ.js → work-pr-cmd-IWZCC3WH.js} +16 -13
  121. package/dist/{work-stop-cmd-HSR2GOJJ.js → work-stop-cmd-DCAWFDZC.js} +16 -13
  122. package/package.json +1 -1
  123. package/skill-pack/maintained/outerlayer-build/references/agent-briefs.md +7 -1
  124. package/skill-pack/maintained/outerlayer-build/references/criteria-judge.md +10 -0
  125. package/skill-pack/maintained/outerlayer-build/references/review-briefs.md +4 -0
  126. package/skill-pack/maintained/outerlayer-cli/SKILL.md +5 -3
  127. package/dist/agent-setup-KXPDPTAJ.js +0 -27
  128. package/dist/connect-cmd-TNGEIWCA.js +0 -22
  129. package/dist/emit-cmd-3W5IO5ET.js +0 -14
  130. package/dist/governing-source-WM736XR4.js +0 -34
  131. package/dist/init-U3I43XCP.js +0 -51
  132. package/dist/init-update-TCMH7M3Q.js +0 -210
  133. package/dist/loop-P3QDQOE5.js +0 -75
  134. package/dist/machine-EEFOW32K.js +0 -46
  135. package/dist/{chunk-F35VMF5M.js → chunk-GOSFARMS.js} +1 -1
  136. package/dist/{chunk-DIB2WLBL.js → chunk-YVZJ6GKF.js} +1 -1
@@ -1,14 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { runEmit } from './chunk-XCRZTXRL.js';
4
- import { resolveGitCommonDir, defaultGitRunner } from './chunk-SNVXHNU7.js';
5
- import { addStdioMcpEntryIfAbsent, addDocsMcpEntryIfAbsent, DOCS_MCP_SERVER_NAME } from './chunk-DIB2WLBL.js';
6
- import { installStarterPack } from './chunk-V7SAQ645.js';
7
- import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-5OCIHFGE.js';
3
+ import { runEmit } from './chunk-WX2WEERU.js';
4
+ import { installStarterPack } from './chunk-VJBFQP3H.js';
5
+ import { resolveGitCommonDir, defaultGitRunner } from './chunk-E4OUL7ZP.js';
6
+ import { addStdioMcpEntryIfAbsent, addDocsMcpEntryIfAbsent, DOCS_MCP_SERVER_NAME } from './chunk-YVZJ6GKF.js';
8
7
  import { C } from './chunk-ANPNT6PM.js';
8
+ import { AGENTS_MD_LAYOUT, declareAgentsMdLayout, linkClaudeSkills, SKILLS_LINK_PATH, describeSkillsLink, isAgentsMdLayout, holdsTodaysLayoutSources, discoverOuterlayerPaths } from './chunk-JKNYTNYD.js';
9
+ import { loadTemplateFiles, loadMaintainedFiles, skillDir, AGENTS_SKILLS_BASE } from './chunk-HBISTTG3.js';
9
10
  import { cliInvocation } from './chunk-CRB3POP4.js';
11
+ import { linkedAgentsPath } from './chunk-VWFOKRYS.js';
10
12
  import { cloneIsGoverned } from './chunk-YQR6E6OV.js';
11
- import { generatedHeaderSource } from './chunk-WFSUUUAN.js';
13
+ import { generatedHeaderSource } from './chunk-RKOTJQVG.js';
12
14
  import { execFileSync } from 'child_process';
13
15
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
14
16
  import { join, dirname } from 'path';
@@ -19,6 +21,18 @@ var MCP_OUTPUT_PATH = ".mcp.json";
19
21
  var POLICY_PATH = ".outerlayer/policy.yaml";
20
22
  var DEFAULT_CONFIG = `${JSON.stringify({ targets: ["claude-code"] }, null, 2)}
21
23
  `;
24
+ var AGENTS_MD_CONFIG = `${JSON.stringify({ targets: ["claude-code"], layout: AGENTS_MD_LAYOUT }, null, 2)}
25
+ `;
26
+ var STARTER_AGENTS_MD = `# Agent instructions
27
+
28
+ Write here what an agent needs to work in this repository: how to build and test it, the rules it follows, and what to check before it opens a pull request.
29
+
30
+ Claude Code and Codex read this file as it is. Skills are in \`.agents/skills/\`.
31
+ `;
32
+ function inTodaysLayout(repoRoot) {
33
+ if (isAgentsMdLayout(repoRoot)) return false;
34
+ return existsSync(join(repoRoot, CONFIG_PATH)) || holdsTodaysLayoutSources(discoverOuterlayerPaths(repoRoot));
35
+ }
22
36
  var errorMessage = (err) => err instanceof Error ? err.message : String(err);
23
37
  var skipped = (reason) => ({
24
38
  outcome: "skipped",
@@ -75,6 +89,7 @@ function runAgentSetup(opts) {
75
89
  } catch (err) {
76
90
  return failed(errorMessage(err), /* @__PURE__ */ new Set(), []);
77
91
  }
92
+ if (!inTodaysLayout(repoRoot)) return runAgentsMdSetup(repoRoot, opts.skillsLink !== false);
78
93
  const written = /* @__PURE__ */ new Set();
79
94
  const kept = [];
80
95
  const problems = [];
@@ -142,6 +157,71 @@ function runAgentSetup(opts) {
142
157
  kept: keptPaths
143
158
  };
144
159
  }
160
+ function runAgentsMdSetup(repoRoot, skillsLink) {
161
+ const written = /* @__PURE__ */ new Set();
162
+ const kept = [];
163
+ const problems = [];
164
+ const linked = linkedAgentsPath(repoRoot);
165
+ if (linked !== void 0) return failed(`${linked} is a link, and a write there would land outside the repository; replace it with a folder and run this again`, written, kept);
166
+ try {
167
+ const pack = installStarterPack({ repoRoot, base: AGENTS_SKILLS_BASE });
168
+ for (const file of loadTemplateFiles(void 0, void 0, AGENTS_SKILLS_BASE).filter((f) => pack.copied.includes(f.skill))) written.add(file.path);
169
+ for (const path of pack.maintained) written.add(path);
170
+ for (const skill of pack.kept) kept.push(`${skillDir(skill, AGENTS_SKILLS_BASE)}/`);
171
+ if (pack.policy === "written") written.add(POLICY_PATH);
172
+ else kept.push(POLICY_PATH);
173
+ for (const path of pack.validatorsWritten) written.add(path);
174
+ kept.push(...pack.validatorsKept);
175
+ if (existsSync(join(repoRoot, "AGENTS.md")) || existsSync(join(repoRoot, "CLAUDE.md"))) {
176
+ kept.push(existsSync(join(repoRoot, "AGENTS.md")) ? "AGENTS.md" : "CLAUDE.md");
177
+ } else {
178
+ writeFileSync(join(repoRoot, "AGENTS.md"), STARTER_AGENTS_MD);
179
+ written.add("AGENTS.md");
180
+ }
181
+ if (existsSync(join(repoRoot, CONFIG_PATH))) {
182
+ kept.push(CONFIG_PATH);
183
+ } else {
184
+ mkdirSync(dirname(join(repoRoot, CONFIG_PATH)), { recursive: true });
185
+ writeFileSync(join(repoRoot, CONFIG_PATH), AGENTS_MD_CONFIG);
186
+ written.add(CONFIG_PATH);
187
+ }
188
+ declareAgentsMdLayout(repoRoot);
189
+ } catch (err) {
190
+ return failed(`could not install the skills: ${errorMessage(err)}`, written, kept);
191
+ }
192
+ try {
193
+ const mcp = addStdioMcpEntryIfAbsent(repoRoot);
194
+ if (mcp.added) written.add(mcp.path);
195
+ else kept.push(`${mcp.path} (already has an outerlayer server)`);
196
+ const docs = addDocsMcpEntryIfAbsent(repoRoot);
197
+ if (docs.added) written.add(docs.path);
198
+ else kept.push(`${docs.path} (already has an ${DOCS_MCP_SERVER_NAME} server)`);
199
+ } catch (err) {
200
+ problems.push(errorMessage(err));
201
+ }
202
+ let linkNote = "";
203
+ if (skillsLink) {
204
+ const link = linkClaudeSkills(repoRoot);
205
+ if (link.status === "linked") written.add(SKILLS_LINK_PATH);
206
+ linkNote = describeSkillsLink(link);
207
+ }
208
+ if (problems.length > 0) return failed(problems.join("; "), written, kept);
209
+ const paths = [...written].sort();
210
+ const lines = [
211
+ `${C.green}\u2713${C.reset} Agent setup: ${paths.length === 0 ? "already in place, nothing changed" : "instructions in AGENTS.md, skills in .agents/skills/"}`,
212
+ renderPaths(paths, kept).trimEnd()
213
+ ];
214
+ if (linkNote !== "") lines.push(` ${linkNote}`);
215
+ if (paths.length > 0) lines.push(` ${C.dim}Commit AGENTS.md, .agents/ and .mcp.json with .outerlayer/. Nothing is generated, so there is no emit step.${C.reset}`);
216
+ return {
217
+ outcome: "done",
218
+ reason: paths.length === 0 ? "already set up" : `${paths.length} files written`,
219
+ text: `${lines.join("\n")}
220
+ `,
221
+ paths,
222
+ kept: []
223
+ };
224
+ }
145
225
  function writtenBeforeFailure(repoRoot, pending, keep) {
146
226
  if (pending === void 0 || pending.length === 0) return [];
147
227
  try {
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { retiredSkillPaths, removeRetiredSkills } from './chunk-USWVXQIH.js';
4
- import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-5OCIHFGE.js';
3
+ import { retiredSkillPaths, removeRetiredSkills } from './chunk-MQ743JX3.js';
5
4
  import { C } from './chunk-ANPNT6PM.js';
5
+ import { isAgentsMdLayout, discoverOuterlayerPaths, isForeignTree, toRepoRelative } from './chunk-JKNYTNYD.js';
6
+ import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-HBISTTG3.js';
6
7
  import { cliInvocation } from './chunk-CRB3POP4.js';
7
- import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-WFSUUUAN.js';
8
+ import { emitTree, generatedHeaderSource, parseOuterlayerConfig, classifyTree } from './chunk-RKOTJQVG.js';
8
9
  import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, unlinkSync, rmdirSync, readdirSync } from 'fs';
9
- import { join, dirname, relative, sep } from 'path';
10
+ import { join, dirname } from 'path';
10
11
 
11
12
  createRequire(import.meta.url);
12
13
  var CONTENT_KINDS = /* @__PURE__ */ new Set(["instructions", "skill", "skill-reference", "command", "mcp"]);
@@ -14,6 +15,13 @@ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git"]);
14
15
  var NO_CONFIG_MESSAGE = 'no .outerlayer/config.json with a non-empty targets array \u2014 create one, e.g. {"targets": ["claude-code"]}';
15
16
  var EmitError = class extends Error {
16
17
  };
18
+ var EMIT_NOT_NEEDED_NOTICE = `Claude Code and Codex read AGENTS.md and .agents/skills/ without a generated copy. Run \`${cliInvocation()} init --update\` to move this repository there and stop committing generated files.`;
19
+ var NATIVE_TARGETS = /* @__PURE__ */ new Set(["claude-code", "codex"]);
20
+ function noticeFor(repoRoot, targets) {
21
+ if (targets.length === 0 || !targets.every((t) => NATIVE_TARGETS.has(t))) return void 0;
22
+ return isAgentsMdLayout(repoRoot) ? void 0 : EMIT_NOT_NEEDED_NOTICE;
23
+ }
24
+ var HAND_OWNED_OUTPUT = /(^|\/)(CLAUDE\.md|AGENTS\.md|\.claude\/skills\/|\.agents\/skills\/)/;
17
25
  function resolveTargetsFromConfig(repoRoot) {
18
26
  const configPath = join(repoRoot, ".outerlayer", "config.json");
19
27
  if (!existsSync(configPath)) throw new EmitError(NO_CONFIG_MESSAGE);
@@ -24,36 +32,6 @@ function resolveTargetsFromConfig(repoRoot) {
24
32
  const detail = result.errors.map((e) => `${e.path}: ${e.message}`).join("; ");
25
33
  throw new EmitError(`invalid .outerlayer/config.json \u2014 ${detail}`);
26
34
  }
27
- function toRepoRelative(repoRoot, absPath) {
28
- return normalizePath(relative(repoRoot, absPath).split(sep).join("/"));
29
- }
30
- function discoverOuterlayerPaths(repoRoot) {
31
- const out = [];
32
- walk(repoRoot, repoRoot, false, out);
33
- return out.sort();
34
- }
35
- function isForeignTree(dir) {
36
- return existsSync(join(dir, ".git")) || existsSync(join(dir, CONTEXT_PIN_FILENAME));
37
- }
38
- function walk(dir, repoRoot, insideOuterlayer, out) {
39
- let entries;
40
- try {
41
- entries = readdirSync(dir, { withFileTypes: true });
42
- } catch {
43
- return;
44
- }
45
- for (const entry of entries) {
46
- if (entry.isSymbolicLink()) continue;
47
- const full = join(dir, entry.name);
48
- if (entry.isDirectory()) {
49
- if (!insideOuterlayer && SKIP_DIRS.has(entry.name)) continue;
50
- if (!insideOuterlayer && isForeignTree(full)) continue;
51
- walk(full, repoRoot, insideOuterlayer || entry.name === ".outerlayer", out);
52
- } else if (entry.isFile() && insideOuterlayer) {
53
- out.push(toRepoRelative(repoRoot, full));
54
- }
55
- }
56
- }
57
35
  function buildEmitInput(repoRoot, targets, maintained, excluded = /* @__PURE__ */ new Set()) {
58
36
  const bundled = new Map(maintained.map((f) => [f.path, f.content]));
59
37
  const discovered = [.../* @__PURE__ */ new Set([...discoverOuterlayerPaths(repoRoot).filter((p) => !excluded.has(p)), ...bundled.keys()])].sort();
@@ -143,10 +121,10 @@ function walkAll(dir, repoRoot, out) {
143
121
  }
144
122
  }
145
123
  }
146
- function findOrphans(repoRoot, computedFiles, sourcePaths) {
124
+ function findOrphans(repoRoot, computedFiles, sourcePaths, ignore) {
147
125
  const orphans = [];
148
126
  for (const path of discoverAllFiles(repoRoot)) {
149
- if (computedFiles.has(path)) continue;
127
+ if (computedFiles.has(path) || ignore?.(path) === true) continue;
150
128
  let content;
151
129
  try {
152
130
  content = readFileSync(join(repoRoot, path), "utf8");
@@ -200,17 +178,20 @@ function runEmit(opts = {}) {
200
178
  const cwd = opts.cwd ?? process.cwd();
201
179
  if (!existsSync(cwd)) throw new EmitError(`no such directory: ${cwd}`);
202
180
  const targets = resolveTargetsFromConfig(cwd);
203
- const maintained = maintainedFilesForExistingSkills(cwd, loadMaintainedFiles());
181
+ const agentsMd = isAgentsMdLayout(cwd);
182
+ const ignoreOrphan = agentsMd ? (path) => HAND_OWNED_OUTPUT.test(path) : void 0;
183
+ const maintained = agentsMd ? [] : maintainedFilesForExistingSkills(cwd, loadMaintainedFiles());
184
+ const notice = noticeFor(cwd, targets);
204
185
  const retired = retiredSkillPaths(cwd);
205
186
  const built = buildEmitInput(cwd, targets, maintained, new Set(retired));
206
187
  const emitted = emitTree(built.input);
207
188
  const { result, kept } = withholdKept(cwd, emitted, opts.keep);
208
189
  const hasErrors = result.errors.length > 0;
209
190
  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
+ if (opts.check) return runCheck(cwd, opts, targets, result, hasErrors, outputs, built.sourcePaths, maintained, kept, retired, notice, ignoreOrphan);
211
192
  const written = hasErrors ? { files: [], copies: [], maintained: [] } : writeOutputs(cwd, result, maintained);
212
193
  const removed = hasErrors ? [] : removeRetiredSkills(cwd);
213
- const orphaned = hasErrors ? [] : removeOrphans(cwd, findOrphans(cwd, outputs, built.sourcePaths));
194
+ const orphaned = hasErrors ? [] : removeOrphans(cwd, findOrphans(cwd, outputs, built.sourcePaths, ignoreOrphan));
214
195
  const exitCode = hasErrors ? 1 : 0;
215
196
  const output = opts.json ? JSON.stringify({
216
197
  targets,
@@ -232,8 +213,11 @@ function runEmit(opts = {}) {
232
213
  for (const w of result.warnings) process.stderr.write(` ${C.yellow}!${C.reset} ${w.path}: ${w.message}
233
214
  `);
234
215
  }
216
+ if (notice !== void 0 && !opts.json) process.stderr.write(`${C.dim}${notice}${C.reset}
217
+ `);
235
218
  }
236
219
  return {
220
+ ...notice === void 0 ? {} : { notice },
237
221
  targets,
238
222
  files: written.files,
239
223
  copies: written.copies,
@@ -247,7 +231,7 @@ function runEmit(opts = {}) {
247
231
  exitCode
248
232
  };
249
233
  }
250
- function runCheck(cwd, opts, targets, result, hasErrors, outputs, sourcePaths, maintained, kept, retired) {
234
+ function runCheck(cwd, opts, targets, result, hasErrors, outputs, sourcePaths, maintained, kept, retired, notice, ignoreOrphan) {
251
235
  const drift = [];
252
236
  if (!hasErrors) {
253
237
  for (const path of retired) drift.push({ kind: "retired", path });
@@ -264,7 +248,7 @@ function runCheck(cwd, opts, targets, result, hasErrors, outputs, sourcePaths, m
264
248
  if (d) drift.push(d);
265
249
  }
266
250
  const retiredPaths = new Set(retired);
267
- drift.push(...findOrphans(cwd, outputs, sourcePaths).filter((d) => !retiredPaths.has(d.path)));
251
+ drift.push(...findOrphans(cwd, outputs, sourcePaths, ignoreOrphan).filter((d) => !retiredPaths.has(d.path)));
268
252
  drift.sort((a, b) => a.path.localeCompare(b.path) || a.kind.localeCompare(b.kind));
269
253
  }
270
254
  const exitCode = hasErrors || drift.length > 0 ? 1 : 0;
@@ -279,8 +263,44 @@ function runCheck(cwd, opts, targets, result, hasErrors, outputs, sourcePaths, m
279
263
  for (const w of result.warnings) process.stderr.write(` ${C.yellow}!${C.reset} ${w.path}: ${w.message}
280
264
  `);
281
265
  }
266
+ if (notice !== void 0 && !opts.json) process.stderr.write(`${C.dim}${notice}${C.reset}
267
+ `);
268
+ }
269
+ return { ...notice === void 0 ? {} : { notice }, targets, files: [], copies: [], maintained: [], kept, retired: [], orphaned: [], warnings: result.warnings, errors: result.errors, drift, output, exitCode };
270
+ }
271
+ var REPLACED_OUTPUT = /^(?:(?:.*\/)?(?:CLAUDE|AGENTS)\.md|(?:.*\/)?\.(?:claude|agents)\/skills\/.+)$/;
272
+ function inspectReplacedOutputs(repoRoot) {
273
+ let configured = [];
274
+ try {
275
+ configured = resolveTargetsFromConfig(repoRoot);
276
+ } catch {
277
+ }
278
+ const targets = [.../* @__PURE__ */ new Set([...configured, "claude-code", "codex"])];
279
+ const maintained = maintainedFilesForExistingSkills(repoRoot, loadMaintainedFiles());
280
+ const maintainedPaths = new Set(maintained.map((f) => f.path));
281
+ const built = buildEmitInput(repoRoot, targets, maintained, new Set(retiredSkillPaths(repoRoot)));
282
+ const emitted = emitTree(built.input);
283
+ const generated = [];
284
+ const edited = [];
285
+ const replacedDiffering = [];
286
+ const classify = (path, same) => {
287
+ (same ? generated : edited).push(path);
288
+ };
289
+ for (const file of emitted.files) {
290
+ if (!REPLACED_OUTPUT.test(file.path) || !existsSync(join(repoRoot, file.path))) continue;
291
+ const disk = readFileSync(join(repoRoot, file.path), "utf8");
292
+ const source = generatedHeaderSource(disk);
293
+ const stale = source !== null && maintainedPaths.has(source);
294
+ const same = stale ? true : disk === file.content;
295
+ if (stale && disk !== file.content) replacedDiffering.push(file.path);
296
+ classify(file.path, same);
297
+ }
298
+ for (const copy of emitted.copies) {
299
+ if (!REPLACED_OUTPUT.test(copy.toPath) || !existsSync(join(repoRoot, copy.toPath))) continue;
300
+ const source = join(repoRoot, copy.fromSourcePath);
301
+ classify(copy.toPath, existsSync(source) && readFileSync(join(repoRoot, copy.toPath)).equals(readFileSync(source)));
282
302
  }
283
- return { targets, files: [], copies: [], maintained: [], kept, retired: [], orphaned: [], warnings: result.warnings, errors: result.errors, drift, output, exitCode };
303
+ return { generated: generated.sort(), edited: edited.sort(), replacedDiffering: replacedDiffering.sort() };
284
304
  }
285
305
 
286
- export { EmitError, runEmit };
306
+ export { EMIT_NOT_NEEDED_NOTICE, EmitError, inspectReplacedOutputs, runEmit };
@@ -1,29 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
+ import { POLICY_CONTENT, VALIDATOR_FILES, VALIDATORS_DIR, POLICY_PATH } from './chunk-VJBFQP3H.js';
3
4
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
4
- import { mcpTargetPath, INIT_STDIO_ENTRY, INIT_DOCS_ENTRY, DOCS_MCP_SERVER_NAME } from './chunk-DIB2WLBL.js';
5
- import { POLICY_CONTENT, VALIDATOR_FILES, VALIDATORS_DIR, POLICY_PATH } from './chunk-V7SAQ645.js';
6
- import { resolvePackRoot, skillDir, namesCore, loadTemplateFiles, TEMPLATE_SKILLS } from './chunk-5OCIHFGE.js';
5
+ import { mcpTargetPath, INIT_STDIO_ENTRY, INIT_DOCS_ENTRY, DOCS_MCP_SERVER_NAME } from './chunk-YVZJ6GKF.js';
6
+ import { skillsBaseFor, isAgentsMdLayout } from './chunk-JKNYTNYD.js';
7
+ import { resolvePackRoot, OUTERLAYER_SKILLS_BASE, skillDir, namesCore, loadTemplateFiles, rewriteSkillPaths, restoreSkillPaths, TEMPLATE_SKILLS } from './chunk-HBISTTG3.js';
7
8
  import { cliInvocation } from './chunk-CRB3POP4.js';
8
- import { hasSymlinkComponent } from './chunk-W3SWJ2TD.js';
9
+ import { hasSymlinkComponent } from './chunk-VWFOKRYS.js';
9
10
  import { cloneIsGoverned } from './chunk-YQR6E6OV.js';
10
- import { emitTree, ALL_TARGET_IDS, classifyTree, splitRawFile } from './chunk-WFSUUUAN.js';
11
+ import { emitTree, ALL_TARGET_IDS, classifyTree, splitRawFile } from './chunk-RKOTJQVG.js';
11
12
  import { createHash } from 'crypto';
12
- import { existsSync, readFileSync } from 'fs';
13
+ import { existsSync, readFileSync, realpathSync } from 'fs';
13
14
  import { join, dirname } from 'path';
14
15
 
15
16
  createRequire(import.meta.url);
16
17
  var SHIPPED_MANIFEST = "shipped.json";
17
- function currentStarterFiles(packRoot = resolvePackRoot(), policy = POLICY_CONTENT) {
18
- const templates = loadTemplateFiles(packRoot).map((f) => ({
18
+ function currentStarterFiles(packRoot = resolvePackRoot(), policy = POLICY_CONTENT, base = OUTERLAYER_SKILLS_BASE) {
19
+ const templates = loadTemplateFiles(packRoot, void 0, base).map((f) => ({
19
20
  path: f.path,
20
21
  content: f.content,
21
22
  skill: f.skill,
22
- source: join(packRoot, "template", f.skill, f.path.slice(skillDir(f.skill).length + 1))
23
+ source: join(packRoot, "template", f.skill, f.path.slice(skillDir(f.skill, base).length + 1))
23
24
  }));
24
25
  const validators = VALIDATOR_FILES.map(({ name }) => {
25
26
  const source = join(packRoot, "validators", name);
26
- return { path: `${VALIDATORS_DIR}/${name}`, content: readFileSync(source, "utf8"), source };
27
+ return { path: `${VALIDATORS_DIR}/${name}`, content: rewriteSkillPaths(readFileSync(source, "utf8"), base), source };
27
28
  });
28
29
  return [...templates, ...validators, { path: POLICY_PATH, content: policy }];
29
30
  }
@@ -66,12 +67,17 @@ function canonical(value) {
66
67
  }
67
68
  return JSON.stringify(value);
68
69
  }
69
- function fileState(root, file, manifest) {
70
+ function shippedPath(path, base) {
71
+ return path.startsWith(`${base}/`) ? `${OUTERLAYER_SKILLS_BASE}/${path.slice(base.length + 1)}` : path;
72
+ }
73
+ function fileState(root, file, manifest, base) {
70
74
  const abs = join(root, file.path);
71
75
  if (!existsSync(abs)) return "missing";
72
- const hash = contentHash(readFileSync(abs, "utf8"));
73
- if (hash === contentHash(file.content)) return "current";
74
- if (manifest.ok && manifest.files.get(file.path)?.has(hash) === true) return "outdated";
76
+ const disk = readFileSync(abs, "utf8");
77
+ if (contentHash(disk) === contentHash(file.content)) return "current";
78
+ const hash = contentHash(restoreSkillPaths(disk, base));
79
+ if (hash === contentHash(restoreSkillPaths(file.content, base))) return "outdated";
80
+ if (manifest.ok && manifest.files.get(shippedPath(file.path, base))?.has(hash) === true) return "outdated";
75
81
  return "edited";
76
82
  }
77
83
  function mcpItems(root) {
@@ -98,17 +104,18 @@ function mcpItems(root) {
98
104
  return { ...base, state: "edited" };
99
105
  });
100
106
  }
101
- function retiredFiles(root, shipped, manifest) {
107
+ function retiredFiles(root, shipped, manifest, base) {
102
108
  if (!manifest.ok) return [];
103
- const current = new Set(shipped.map((f) => f.path));
109
+ const current = new Set(shipped.map((f) => shippedPath(f.path, base)));
104
110
  const prefixes = TEMPLATE_SKILLS.map((skill) => `${skillDir(skill)}/`);
105
111
  const retired = [];
106
- for (const [path, hashes] of manifest.files) {
107
- if (current.has(path) || !prefixes.some((prefix) => path.startsWith(prefix))) continue;
112
+ for (const [known, hashes] of manifest.files) {
113
+ if (current.has(known) || !prefixes.some((prefix) => known.startsWith(prefix))) continue;
114
+ const path = base === OUTERLAYER_SKILLS_BASE ? known : `${base}/${known.slice(OUTERLAYER_SKILLS_BASE.length + 1)}`;
108
115
  const abs = join(root, path);
109
116
  if (!existsSync(abs) || hasSymlinkComponent(root, path)) continue;
110
117
  try {
111
- retired.push({ path, state: hashes.has(contentHash(readFileSync(abs, "utf8"))) ? "unedited" : "edited" });
118
+ retired.push({ path, state: hashes.has(contentHash(restoreSkillPaths(readFileSync(abs, "utf8"), base))) ? "unedited" : "edited" });
112
119
  } catch {
113
120
  }
114
121
  }
@@ -116,13 +123,14 @@ function retiredFiles(root, shipped, manifest) {
116
123
  }
117
124
  function inspectStarterFiles(root, opts = {}) {
118
125
  const packRoot = opts.packRoot ?? resolvePackRoot();
126
+ const base = opts.base ?? skillsBaseFor(root);
119
127
  const manifest = readShippedManifest(packRoot);
120
- const shipped = currentStarterFiles(packRoot);
121
- const files = shipped.map((file) => ({ kind: "file", path: file.path, state: fileState(root, file, manifest), file }));
122
- const buildEdited = files.some((f) => f.path === `${skillDir("build")}/SKILL.md` && f.state === "edited");
123
- const buildPrefix = `${skillDir("build")}/`;
124
- const retired = retiredFiles(root, shipped, manifest).filter((f) => !(buildEdited && f.path.startsWith(buildPrefix)));
125
- return { items: [...files, ...mcpItems(root)], retired, manifest: manifest.ok ? "ok" : manifest.problem };
128
+ const shipped = currentStarterFiles(packRoot, void 0, base);
129
+ const files = shipped.map((file) => ({ kind: "file", path: file.path, state: fileState(root, file, manifest, base), file }));
130
+ const buildEdited = files.some((f) => f.path === `${skillDir("build", base)}/SKILL.md` && f.state === "edited");
131
+ const buildPrefix = `${skillDir("build", base)}/`;
132
+ const retired = retiredFiles(root, shipped, manifest, base).filter((f) => !(buildEdited && f.path.startsWith(buildPrefix)));
133
+ return { base, items: [...files, ...mcpItems(root)], retired, manifest: manifest.ok ? "ok" : manifest.problem };
126
134
  }
127
135
  function itemLabel(item) {
128
136
  return item.kind === "file" ? item.path : `${item.path} "${item.server}" server`;
@@ -132,7 +140,7 @@ function contextIsGoverned(gitCommonDir) {
132
140
  }
133
141
  var NAME = "Starter files";
134
142
  var UPDATE = `${cliInvocation()} init --update`;
135
- function shownPaths(items, all) {
143
+ function shownPaths(items, all, base) {
136
144
  const shown = [];
137
145
  const whole = /* @__PURE__ */ new Set();
138
146
  for (const item of items) {
@@ -140,7 +148,7 @@ function shownPaths(items, all) {
140
148
  if (skill !== void 0) {
141
149
  const ofSkill = all.filter((i) => i.kind === "file" && i.file.skill === skill);
142
150
  if (ofSkill.every((i) => items.includes(i))) {
143
- if (!whole.has(skill)) shown.push(`${skillDir(skill)}/`);
151
+ if (!whole.has(skill)) shown.push(`${skillDir(skill, base)}/`);
144
152
  whole.add(skill);
145
153
  continue;
146
154
  }
@@ -164,8 +172,8 @@ function checkStarterFiles(cwd, opts = {}) {
164
172
  const retired = report.retired.filter((f) => f.state === "unedited");
165
173
  const parts = [];
166
174
  if (current.length > 0) parts.push(`${current.length} current`);
167
- if (outdated.length > 0) parts.push(`outdated and unedited: ${shownPaths(outdated, report.items).join(", ")}`);
168
- if (missing.length > 0) parts.push(`missing: ${shownPaths(missing, report.items).join(", ")}`);
175
+ if (outdated.length > 0) parts.push(`outdated and unedited: ${shownPaths(outdated, report.items, report.base).join(", ")}`);
176
+ if (missing.length > 0) parts.push(`missing: ${shownPaths(missing, report.items, report.base).join(", ")}`);
169
177
  if (retired.length > 0) parts.push(`no longer shipped, unedited: ${retired.map((f) => f.path).join(", ")}`);
170
178
  if (edited.length > 0) parts.push(`edited by you, left as they are: ${edited.map(itemLabel).join(", ")}`);
171
179
  if (unreadable.length > 0) parts.push(`${unreadable[0].path} is not valid JSON, so its servers were not read`);
@@ -244,15 +252,25 @@ ${shortBuild()}`
244
252
  fix: `Rename each heading to a phase: ${BUILD_PHASES.join(", ")}.`
245
253
  });
246
254
  }
247
- const lacking = folders.filter(
248
- (folder) => namesCore(read(join(repoRoot, folder, "build", "SKILL.md")) ?? "") && !existsSync(join(repoRoot, folder, "outerlayer-build", "SKILL.md"))
249
- );
255
+ const agentsMd = isAgentsMdLayout(repoRoot);
256
+ const seen = /* @__PURE__ */ new Set();
257
+ const lacking = folders.filter((folder) => {
258
+ if (!namesCore(read(join(repoRoot, folder, "build", "SKILL.md")) ?? "") || existsSync(join(repoRoot, folder, "outerlayer-build", "SKILL.md"))) return false;
259
+ let key = folder;
260
+ try {
261
+ key = realpathSync(join(repoRoot, folder));
262
+ } catch {
263
+ }
264
+ if (seen.has(key)) return false;
265
+ seen.add(key);
266
+ return true;
267
+ });
250
268
  if (lacking.length > 0) {
251
269
  checks.push({
252
270
  name: NAME2,
253
271
  status: "warn",
254
272
  detail: `${lacking.join(", ")} holds build with no outerlayer-build beside it`,
255
- fix: `Run \`${cliInvocation()} context emit\` to write outerlayer-build, then commit.`
273
+ fix: `Run \`${cliInvocation()} ${agentsMd ? "init --update" : "context emit"}\` to write outerlayer-build, then commit.`
256
274
  });
257
275
  }
258
276
  return checks;
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { revokeTokenOnDashboard } from './chunk-F35VMF5M.js';
3
+ import { revokeTokenOnDashboard } from './chunk-GOSFARMS.js';
4
4
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
5
- import { readCloudConfig, writeCloudConfig, cloudConfigPath } from './chunk-EABW6AJQ.js';
6
5
  import { C } from './chunk-ANPNT6PM.js';
6
+ import { readCloudConfig, writeCloudConfig, cloudConfigPath } from './chunk-EABW6AJQ.js';
7
7
  import { cliInvocation } from './chunk-CRB3POP4.js';
8
+ import { CLI_LOGIN_MACHINE_NAME_MAX, CliLoginPollResponseSchema, CLI_LOGIN_KIND, CliLoginRefusalSchema, CliLoginStartedSchema } from './chunk-QZBXLLTV.js';
8
9
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
9
10
  import { generateKeyPairSync, privateDecrypt, constants, createPrivateKey } from 'crypto';
10
11
  import { spawn } from 'child_process';
@@ -45,7 +46,6 @@ var PIPED_KEY_GIVE_UP_MS = 3e4;
45
46
  var REQUEST_TIMEOUT_MS = 15e3;
46
47
  var MAX_POLL_FAILURES = 5;
47
48
  var DEFAULT_POLL_INTERVAL_S = 3;
48
- var MACHINE_NAME_MAX = 255;
49
49
  function realLoginDeps() {
50
50
  return {
51
51
  fetchImpl: fetch,
@@ -74,7 +74,7 @@ function openInBrowser(url) {
74
74
  }
75
75
  function sanitizeMachineName(raw) {
76
76
  const cleaned = raw.replace(/[^\w\s\-().]/g, "").trim();
77
- return (cleaned.length > 0 ? cleaned : "machine").slice(0, MACHINE_NAME_MAX);
77
+ return (cleaned.length > 0 ? cleaned : "machine").slice(0, CLI_LOGIN_MACHINE_NAME_MAX);
78
78
  }
79
79
  function stdinKind(stdin) {
80
80
  if (stdin.isTTY) return "terminal";
@@ -168,16 +168,13 @@ function purgeExpiredPending(home, now, keepId) {
168
168
  }
169
169
  }
170
170
  }
171
- function asRecord(value) {
172
- return typeof value === "object" && value !== null ? value : {};
173
- }
174
171
  async function startLogin(deps, dashboard, publicKey, machineName) {
175
172
  let res;
176
173
  try {
177
174
  res = await deps.fetchImpl(`${dashboard}/api/cli/login`, {
178
175
  method: "POST",
179
176
  headers: { "content-type": "application/json" },
180
- body: JSON.stringify({ public_key: publicKey, machine_name: machineName, kind: "account" }),
177
+ body: JSON.stringify({ public_key: publicKey, machine_name: machineName, kind: CLI_LOGIN_KIND }),
181
178
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
182
179
  });
183
180
  } catch (err) {
@@ -189,16 +186,18 @@ async function startLogin(deps, dashboard, publicKey, machineName) {
189
186
  `${dashboard} is limiting logins from this address` + (wait > 0 ? `; try again in ${wait} seconds` : "; try again shortly")
190
187
  );
191
188
  }
192
- const body = asRecord(await res.json().catch(() => null));
189
+ const body = await res.json().catch(() => null);
193
190
  if (!res.ok) {
194
- const reason = typeof body.message === "string" ? `: ${body.message}` : "";
191
+ const refusal = CliLoginRefusalSchema.safeParse(body);
192
+ const reason = refusal.success && refusal.data.message !== void 0 ? `: ${refusal.data.message}` : "";
195
193
  throw new LoginError(`${dashboard} refused to start a login (HTTP ${res.status})${reason}`);
196
194
  }
197
- if (typeof body.id !== "string" || typeof body.code !== "string" || typeof body.expires_at !== "string") {
195
+ const started = CliLoginStartedSchema.safeParse(body);
196
+ if (!started.success) {
198
197
  throw new LoginError(`${dashboard} answered with something that is not a login; is --dashboard the dashboard's address?`);
199
198
  }
200
- const interval = typeof body.interval === "number" && body.interval > 0 ? body.interval : DEFAULT_POLL_INTERVAL_S;
201
- return { id: body.id, code: body.code, interval, expiresAt: body.expires_at };
199
+ const { id, code, interval, expires_at: expiresAt } = started.data;
200
+ return { id, code, interval: interval ?? DEFAULT_POLL_INTERVAL_S, expiresAt };
202
201
  }
203
202
  async function pollLogin(deps, dashboard, id) {
204
203
  let res;
@@ -215,13 +214,16 @@ async function pollLogin(deps, dashboard, id) {
215
214
  if (res.status === 202) return { kind: "pending" };
216
215
  if (res.status >= 500) return { kind: "unavailable", message: `HTTP ${res.status}` };
217
216
  if (!res.ok) return { kind: "error", message: `HTTP ${res.status}` };
218
- const body = asRecord(await res.json().catch(() => null));
219
- if (body.status === "pending") return { kind: "pending" };
220
- if (body.status === "denied") return { kind: "denied" };
221
- if (body.status === "approved" && typeof body.encrypted_token === "string" && typeof body.gateway_url === "string" && typeof body.email === "string") {
222
- return { kind: "approved", encryptedToken: body.encrypted_token, gatewayUrl: body.gateway_url, email: body.email };
217
+ const answer = CliLoginPollResponseSchema.safeParse(await res.json().catch(() => null));
218
+ if (!answer.success) return { kind: "error", message: "unexpected answer" };
219
+ switch (answer.data.status) {
220
+ case "pending":
221
+ return { kind: "pending" };
222
+ case "denied":
223
+ return { kind: "denied" };
224
+ case "approved":
225
+ return { kind: "approved", encryptedToken: answer.data.encrypted_token, gatewayUrl: answer.data.gateway_url, email: answer.data.email };
223
226
  }
224
- return { kind: "error", message: "unexpected answer" };
225
227
  }
226
228
  function decryptToken(privateKeyPem, encryptedToken) {
227
229
  try {
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { checkStarterFiles, contextIsGoverned, checkBuildSkill } from './chunk-VSUXCMMH.js';
4
- import { resolveCredentials, getJson, WorkCommandError } from './chunk-FQE5RDHI.js';
5
- import { isLinkedRepository } from './chunk-EMUNEAQK.js';
6
- import { sanitizeTerminal } from './chunk-G3IJ3N6F.js';
7
- import { checkFormerSkillNames } from './chunk-USWVXQIH.js';
8
- import { resolveGatewayCredentials, defaultFetch } from './chunk-QS43O4DR.js';
9
- import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-HWZCE745.js';
3
+ import { checkStarterFiles, contextIsGoverned, checkBuildSkill } from './chunk-XFGL53GU.js';
4
+ import { resolveCredentials, getJson, WorkCommandError } from './chunk-RVYOOYOA.js';
5
+ import { isLinkedRepository } from './chunk-V5Q3S4NM.js';
6
+ import { sanitizeTerminal } from './chunk-BUP5N3X6.js';
7
+ import { checkFormerSkillNames } from './chunk-MQ743JX3.js';
8
+ import { resolveGatewayCredentials, defaultFetch } from './chunk-FKXGQOF5.js';
9
+ import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-755WXDBI.js';
10
10
  import { readStatuslineState, STATE_FRESH_MS } from './chunk-DXDF43YG.js';
11
11
  import { hookCommandEscapesControlPlane, CONTROL_PLANE_DIR_VARIABLE } from './chunk-HCVLP7XA.js';
12
12
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
13
- import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-AEKSX4EY.js';
13
+ import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-D4AMAEZ7.js';
14
14
  import { C } from './chunk-ANPNT6PM.js';
15
15
  import { cliInvocation } from './chunk-CRB3POP4.js';
16
16
  import { readSettings, settingsPath, isOurStatuslineSlot, statuslineWrappedOriginal, REGISTERED_EVENTS, isOurHookCommand, ourCommandBins, cliBinResolvable, ourDirectScriptBins, wrappedOriginalCommand } from './chunk-GNPTU4BM.js';
@@ -1174,7 +1174,7 @@ function doctorExitCode(checks) {
1174
1174
 
1175
1175
  // src/doctor-report.ts
1176
1176
  async function gatherDoctorChecks() {
1177
- const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-H4QS332G.js');
1177
+ const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-7Z5DHSGY.js');
1178
1178
  const configPath = defaultRunnerConfigPath();
1179
1179
  const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
1180
1180
  const credential = await credentialChecks();
@@ -1186,7 +1186,7 @@ async function gatherDoctorChecks() {
1186
1186
  const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
1187
1187
  checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
1188
1188
  } else if (hasRunnerBlock(configPath)) {
1189
- const { runnerHostChecks } = await import('./doctor-BRN4BGMA.js');
1189
+ const { runnerHostChecks } = await import('./doctor-DOAFZV4O.js');
1190
1190
  const hooks = (() => {
1191
1191
  try {
1192
1192
  return loadRunnerConfig(configPath).hooks;
@@ -1221,7 +1221,7 @@ async function gatherDoctorChecks() {
1221
1221
  const { existsSync: existsSync3 } = await import('fs');
1222
1222
  if (process.platform !== "linux" || !existsSync3("/run/systemd/system")) return void 0;
1223
1223
  const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
1224
- const { DEFAULT_UNIT_NAME } = await import('./install-SFACZU7E.js');
1224
+ const { DEFAULT_UNIT_NAME } = await import('./install-ORQC26ZE.js');
1225
1225
  const { dirname: dirname2 } = await import('path');
1226
1226
  return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
1227
1227
  })();
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { sharedSkillNames } from './chunk-ZNPE7FW5.js';
3
+ import { sharedSkillNames } from './chunk-VPEGE3QZ.js';
4
4
  import { GOVERNANCE_VARIABLE, versionAtLeast, MINIMUM_CLAUDE_VERSION, SHIM_SCRIPT, CONTROL_PLANE_DIR_VARIABLE } from './chunk-HCVLP7XA.js';
5
5
  export { MINIMUM_CLAUDE_VERSION } from './chunk-HCVLP7XA.js';
6
6
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
7
- import './chunk-5OCIHFGE.js';
8
- import './chunk-WFSUUUAN.js';
9
- import './chunk-7VUK5RFS.js';
7
+ import './chunk-HBISTTG3.js';
8
+ import './chunk-RKOTJQVG.js';
9
+ import './chunk-QZBXLLTV.js';
10
10
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
11
11
  import './chunk-RA3O54FC.js';
12
12
  import './chunk-Z5GEFMRW.js';
@@ -236,7 +236,7 @@ async function runClaudeLauncher(args, deps = {}) {
236
236
  return 1;
237
237
  }
238
238
  } else {
239
- const { resolveLaunchPlane } = await import('./control-plane-launch-HE6OSWKO.js');
239
+ const { resolveLaunchPlane } = await import('./control-plane-launch-GKGI6HJA.js');
240
240
  const plane = env[GOVERNANCE_VARIABLE] === "self" ? { kind: "none" } : await resolveLaunchPlane({ cwd, env, home, fetchImpl: deps.fetchImpl, stderr });
241
241
  if (plane.kind === "none") {
242
242
  const own2 = ownSkillsDirFor(cwd, home, [], stderr);