@outerlayer/cli 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +36 -2
  3. package/dist/agent-setup-TPR4BBFV.js +27 -0
  4. package/dist/{artifact-link-cmd-2F47LT7F.js → artifact-link-cmd-W7XF5BFB.js} +4 -3
  5. package/dist/{build-BG2YENGK.js → build-TQ6X5PYN.js} +5 -4
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-2D3WSOOY.js → builder-C4QNTYMS.js} +5 -6
  8. package/dist/{check-5XE7XMC4.js → check-UWQAL2MN.js} +56 -52
  9. package/dist/{chunk-R7PF5OOU.js → chunk-3CWUOFV6.js} +21 -14
  10. package/dist/{chunk-4NPZVMJU.js → chunk-3UZTIRLF.js} +60 -4
  11. package/dist/chunk-452TM7J4.js +502 -0
  12. package/dist/{chunk-NNRTPG7X.js → chunk-5GHGAF6P.js} +4 -4
  13. package/dist/{chunk-WVUIRTWA.js → chunk-5MYFOW5V.js} +196 -37
  14. package/dist/{chunk-HGECYFEI.js → chunk-5TN7GLYX.js} +138 -109
  15. package/dist/{chunk-VQYBXPI7.js → chunk-6D7GT22T.js} +5 -5
  16. package/dist/{chunk-G5NPRL2K.js → chunk-6U3MV7TZ.js} +687 -76
  17. package/dist/{chunk-42ZI52JW.js → chunk-6X4SQ5MY.js} +4 -3
  18. package/dist/{chunk-MAK5LYRF.js → chunk-7KSSBQTS.js} +278 -535
  19. package/dist/{chunk-NWQTTSBB.js → chunk-7SJPQWBM.js} +41 -16
  20. package/dist/{chunk-J5M7BSJQ.js → chunk-7VUK5RFS.js} +23 -4
  21. package/dist/{chunk-OZGHL5Y5.js → chunk-7YP7T6GV.js} +2 -2
  22. package/dist/{chunk-TI7B7UEO.js → chunk-AMFWYESY.js} +1 -1
  23. package/dist/{chunk-YRYMNBGQ.js → chunk-ANPNT6PM.js} +2 -1
  24. package/dist/{chunk-4KSQKQXE.js → chunk-BM63HB2M.js} +20 -16
  25. package/dist/{chunk-4Q7OMKAT.js → chunk-BQXFBI7T.js} +81 -5
  26. package/dist/{chunk-ADJRGVZC.js → chunk-BWSUPRDK.js} +1 -1
  27. package/dist/{chunk-3NSNIIBE.js → chunk-CG36IONK.js} +18 -17
  28. package/dist/chunk-CJJDJ7AT.js +56 -0
  29. package/dist/chunk-CRB3POP4.js +34 -0
  30. package/dist/{chunk-LWSJQJC6.js → chunk-DADWEUH3.js} +7 -7
  31. package/dist/{chunk-KG5ECZGJ.js → chunk-DJ3ZUSC3.js} +11 -11
  32. package/dist/{chunk-F47JBIAW.js → chunk-DSF2FWAZ.js} +2 -1
  33. package/dist/{chunk-N5FOE5PS.js → chunk-ECODQ7II.js} +4 -33
  34. package/dist/{chunk-HHJDN6AU.js → chunk-ESVW4MJY.js} +1 -1
  35. package/dist/{chunk-YO4PSLVB.js → chunk-FVMYKL6L.js} +1 -1
  36. package/dist/chunk-G2YNJIGF.js +44 -0
  37. package/dist/{chunk-WUYTWNMA.js → chunk-G3IJ3N6F.js} +5 -3
  38. package/dist/{chunk-QKFB4K7X.js → chunk-GGOMT6KF.js} +1 -1
  39. package/dist/{chunk-N6LURUEF.js → chunk-GOSFARMS.js} +1 -1
  40. package/dist/{chunk-S3NYLI2P.js → chunk-HPJNLETG.js} +93 -38
  41. package/dist/{chunk-IP7BIBXJ.js → chunk-IF2OSI5T.js} +35 -1
  42. package/dist/{chunk-C5Y5ZJJZ.js → chunk-IXYZUUYQ.js} +5 -4
  43. package/dist/chunk-JBJTC3UU.js +35 -0
  44. package/dist/chunk-KYS3TQAG.js +296 -0
  45. package/dist/chunk-LLM3I5YU.js +89 -0
  46. package/dist/{chunk-YU3VKZ5F.js → chunk-LRG3ZMZD.js} +6 -7
  47. package/dist/{chunk-V7NIVOFD.js → chunk-M2ZWWTWZ.js} +1 -1
  48. package/dist/{chunk-ERI5GBNI.js → chunk-MLAAT4S6.js} +3 -3
  49. package/dist/{chunk-O765JR4W.js → chunk-MTP2QEVO.js} +1 -1
  50. package/dist/{chunk-ZM2IMMYN.js → chunk-N2PXGQRL.js} +9 -1
  51. package/dist/{chunk-BWQ66M6N.js → chunk-NFIR3GEE.js} +98 -24
  52. package/dist/{chunk-POJMEEJJ.js → chunk-NNLA7LLQ.js} +1 -1
  53. package/dist/{chunk-PLMTLGYH.js → chunk-NTRCK4NH.js} +5 -4
  54. package/dist/{chunk-22RMZBGW.js → chunk-P53VEAOF.js} +1 -1
  55. package/dist/{chunk-RLYZLUQ7.js → chunk-QEFKA2QD.js} +2 -2
  56. package/dist/{chunk-JXYVA45C.js → chunk-QNLX477Q.js} +1200 -322
  57. package/dist/{chunk-UGP76IZ5.js → chunk-S5PCA3SN.js} +1 -1
  58. package/dist/{chunk-RMWLBUCJ.js → chunk-SADUQSO4.js} +1 -1
  59. package/dist/{chunk-GQD22KKN.js → chunk-T2Q7ISPE.js} +2 -1
  60. package/dist/chunk-TUSYOL2O.js +113 -0
  61. package/dist/{chunk-NBGDBTO2.js → chunk-TWF3RICI.js} +9 -8
  62. package/dist/{chunk-ONKJUM6R.js → chunk-UVG35MWF.js} +1 -1
  63. package/dist/{chunk-KMDLFI4P.js → chunk-VMJPFQNE.js} +1 -1
  64. package/dist/chunk-VRRGENS4.js +144 -0
  65. package/dist/{chunk-PTW6W5NR.js → chunk-VYAL7PUE.js} +1 -1
  66. package/dist/{chunk-OHVJA253.js → chunk-WFSUUUAN.js} +47 -30
  67. package/dist/{chunk-RSNSZ2LJ.js → chunk-X5H2WYMB.js} +1 -1
  68. package/dist/{chunk-HYZRY4KZ.js → chunk-XNGMQ27R.js} +16 -6
  69. package/dist/chunk-XSZD4AZ6.js +490 -0
  70. package/dist/chunk-YQR6E6OV.js +109 -0
  71. package/dist/{chunk-XCK2YAY5.js → chunk-YVZJ6GKF.js} +3 -2
  72. package/dist/{chunk-2MNPHL23.js → chunk-ZHAUSEEN.js} +38 -15
  73. package/dist/claude-launcher-FPPHZPMR.js +115 -0
  74. package/dist/{cli-A76AXELQ.js → cli-HROW5T7X.js} +567 -285
  75. package/dist/{command-R2QCVX55.js → command-OEAV7BVX.js} +42 -40
  76. package/dist/{config-N3C6DFQA.js → config-CLSGABD5.js} +2 -3
  77. package/dist/{connect-cmd-JBKAQNSF.js → connect-cmd-UNYKGDAL.js} +10 -9
  78. package/dist/context-cleanup-HSVYDGMI.js +9 -0
  79. package/dist/control-plane-launch-NYVDJESK.js +216 -0
  80. package/dist/{dist-LKDWQWZ7.js → dist-WO7NOJNG.js} +2 -2
  81. package/dist/{docker-4J77Q2BF.js → docker-BFU3AQGY.js} +7 -8
  82. package/dist/{doctor-4TAIARIG.js → doctor-23BBJWBX.js} +48 -42
  83. package/dist/{emit-artifact-cmd-MPZVR56N.js → emit-artifact-cmd-O6SCM7TV.js} +27 -24
  84. package/dist/emit-cmd-7SP3I6UZ.js +14 -0
  85. package/dist/{emit-criteria-cmd-EFGYI23V.js → emit-criteria-cmd-LQNERZRV.js} +17 -16
  86. package/dist/{emit-finding-cmd-5GF3V4LR.js → emit-finding-cmd-AJPKHIOV.js} +18 -17
  87. package/dist/{emit-result-cmd-I4OADKMD.js → emit-result-cmd-RUICOKWQ.js} +22 -19
  88. package/dist/emit-step-cmd-347XNGPQ.js +26 -0
  89. package/dist/{exec-client-CQV746UF.js → exec-client-7UMESIE2.js} +3 -4
  90. package/dist/governing-source-V5EEGPTA.js +34 -0
  91. package/dist/guest-init-KPBQOFE5.js +9 -0
  92. package/dist/{hook-fast-R4FVSZ2H.js → hook-fast-YAZG5MDX.js} +5 -2
  93. package/dist/host-key-U4B5QETR.js +8 -0
  94. package/dist/{import-capture-cmd-EQ35EARA.js → import-capture-cmd-KKFFOGWP.js} +7 -5
  95. package/dist/{import-ruler-cmd-BGAZXCQM.js → import-ruler-cmd-5P6JOXQN.js} +5 -3
  96. package/dist/index.js +8 -4
  97. package/dist/{init-PWU5YVL7.js → init-LWHDMZQG.js} +40 -38
  98. package/dist/init-MOINY2DD.js +53 -0
  99. package/dist/{init-cmd-XAVO7HYZ.js → init-cmd-HSMRID6A.js} +75 -30
  100. package/dist/{init-update-CVCWD6VS.js → init-update-LWX5MPPR.js} +29 -29
  101. package/dist/{install-HDBIPHTX.js → install-4SEFIIW2.js} +4 -5
  102. package/dist/{install-cmd-5HYMXLEW.js → install-cmd-QDOTR4OM.js} +5 -4
  103. package/dist/{login-browser-QZCS3WW6.js → login-browser-DX35MARZ.js} +5 -4
  104. package/dist/{logout-cmd-QAUFDJ6J.js → logout-cmd-5V5MMBSY.js} +3 -2
  105. package/dist/{logs-H55NWW4H.js → logs-AMLKC3VP.js} +3 -1
  106. package/dist/loop-SYYPVFYW.js +77 -0
  107. package/dist/machine-6X63G2KH.js +48 -0
  108. package/dist/{mcp-install-cmd-FDTPQQS6.js → mcp-install-cmd-GGWB3JJ2.js} +3 -2
  109. package/dist/{mcp-serve-cmd-3KVYEUXG.js → mcp-serve-cmd-N3UTY6EE.js} +11 -9
  110. package/dist/paths-7XP63OAX.js +7 -0
  111. package/dist/pending-532ABZYC.js +8 -0
  112. package/dist/{pidfile-UZRH774M.js → pidfile-3DM4OBKO.js} +4 -2
  113. package/dist/{record-C43U4PKS.js → record-3WP6V2K5.js} +3 -2
  114. package/dist/{runner-env-EN7YE7QX.js → runner-env-FRZFFHDF.js} +5 -6
  115. package/dist/{runner-key-6SLBI7PP.js → runner-key-276SJU5R.js} +40 -38
  116. package/dist/session-sender-GGIY5OAX.js +36 -0
  117. package/dist/{pending-MXLSRYMO.js → shell-function-NRD4KVUK.js} +1 -2
  118. package/dist/spec-cmd-SIZBRN7D.js +115 -0
  119. package/dist/{starter-pack-KF66HGZR.js → starter-pack-6LQVLBIA.js} +7 -5
  120. package/dist/{status-7VNQN5PW.js → status-NAK6DYSF.js} +46 -42
  121. package/dist/{statusline-fast-SVTMBMD7.js → statusline-fast-KN3BX67H.js} +2 -1
  122. package/dist/sync-cmd-G7GF4UTW.js +35 -0
  123. package/dist/{watch-23HSAEI6.js → watch-EVM5GSMP.js} +22 -15
  124. package/dist/{work-claim-cmd-LKL3GHFQ.js → work-claim-cmd-3J3QW56U.js} +26 -29
  125. package/dist/work-cmd-5EPPD33Y.js +24 -0
  126. package/dist/{work-comment-cmd-AWHP56M2.js → work-comment-cmd-2ACOZQAE.js} +16 -15
  127. package/dist/{work-launch-R5HTAP6X.js → work-launch-LEQ2MPM3.js} +3 -1
  128. package/dist/work-logs-cmd-QDD2NMJL.js +60 -0
  129. package/dist/{work-open-pr-cmd-IOM4AVQQ.js → work-open-pr-cmd-XNCEI4ZM.js} +18 -17
  130. package/dist/work-pr-cmd-H4OQR26N.js +25 -0
  131. package/package.json +3 -2
  132. package/skill-pack/maintained/outerlayer-amend/SKILL.md +4 -2
  133. package/skill-pack/maintained/outerlayer-cli/SKILL.md +9 -2
  134. package/skill-pack/maintained/outerlayer-cli/references/find-out-why-a-build-ended.md +76 -0
  135. package/skill-pack/shipped.json +18 -2
  136. package/skill-pack/template/build/SKILL.md +23 -10
  137. package/skill-pack/template/build/references/agent-briefs.md +3 -1
  138. package/skill-pack/template/build/references/criteria-judge.md +27 -0
  139. package/skill-pack/template/build/references/release.md +22 -29
  140. package/skill-pack/template/spec/SKILL.md +35 -10
  141. package/skill-pack/validators/acceptance-criteria.yaml +25 -26
  142. package/dist/agent-setup-T624CWPD.js +0 -27
  143. package/dist/chunk-6Y5RUTHO.js +0 -39
  144. package/dist/chunk-BYT5O5RV.js +0 -61
  145. package/dist/chunk-WTE3MQML.js +0 -133
  146. package/dist/chunk-XCCLFVXM.js +0 -401
  147. package/dist/chunk-YZ5F4PIS.js +0 -797
  148. package/dist/context-adopt-NFHCLVUB.js +0 -108
  149. package/dist/context-materialize-IV2TUUFO.js +0 -22
  150. package/dist/emit-cmd-IJ3GVRFD.js +0 -12
  151. package/dist/guest-init-LJP5GPZF.js +0 -9
  152. package/dist/host-key-YMLXY6QB.js +0 -7
  153. package/dist/init-6XPDRDTC.js +0 -51
  154. package/dist/loop-FK3LPMMZ.js +0 -73
  155. package/dist/machine-SQXEBAM2.js +0 -46
  156. package/dist/paths-OYKMVYJP.js +0 -6
  157. package/dist/session-sender-FO3LCREV.js +0 -29
  158. package/dist/sync-cmd-GVPPFZ3G.js +0 -28
  159. package/dist/work-cmd-Z2WXLZPH.js +0 -23
  160. package/dist/work-pr-cmd-HNEUIEN7.js +0 -24
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { homeFromEnv } from './chunk-VNVZDWO3.js';
4
+ import { join, sep } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ createRequire(import.meta.url);
8
+ var CLI_PACKAGE = "@outerlayer/cli";
9
+ function packageRunnerOf(bin) {
10
+ for (const part of bin.replace(/\\/g, "/").split("/")) {
11
+ if (part === "_npx") return "npx";
12
+ if (part === "dlx") return "pnpm dlx";
13
+ if (part.startsWith("dlx-")) return "yarn dlx";
14
+ if (part.startsWith("bunx-")) return "bunx";
15
+ }
16
+ return void 0;
17
+ }
18
+ function runningModule() {
19
+ try {
20
+ return fileURLToPath(import.meta.url);
21
+ } catch {
22
+ return "";
23
+ }
24
+ }
25
+ function cliInvocation(opts = {}) {
26
+ const bin = opts.bin ?? runningModule();
27
+ const runner = packageRunnerOf(bin);
28
+ if (runner !== void 0) return `${runner} ${CLI_PACKAGE}`;
29
+ const installedCopy = join(opts.home ?? homeFromEnv(), ".outerlayer", "cli") + sep;
30
+ if (bin.startsWith(installedCopy)) return `npx ${CLI_PACKAGE}`;
31
+ return "outerlayer";
32
+ }
33
+
34
+ export { cliInvocation, packageRunnerOf };
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { isPlaceholderCommand } from './chunk-VQYBXPI7.js';
4
- import { describeCallFailure } from './chunk-MAK5LYRF.js';
5
- import { WorkCommandError, getJson } from './chunk-S3NYLI2P.js';
6
- import { builtinKind } from './chunk-22RMZBGW.js';
7
- import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-4Q7OMKAT.js';
8
- import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
9
- import { signerFor } from './chunk-V7NIVOFD.js';
3
+ import { isPlaceholderCommand } from './chunk-6D7GT22T.js';
4
+ import { describeCallFailure } from './chunk-XSZD4AZ6.js';
5
+ import { WorkCommandError, getJson } from './chunk-HPJNLETG.js';
6
+ import { sanitizeTerminal } from './chunk-G3IJ3N6F.js';
7
+ import { builtinKind } from './chunk-P53VEAOF.js';
8
+ import { signerFor } from './chunk-M2ZWWTWZ.js';
9
+ import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-BQXFBI7T.js';
10
10
 
11
11
  createRequire(import.meta.url);
12
12
 
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { runEmit } from './chunk-2MNPHL23.js';
4
- import { addStdioMcpEntryIfAbsent, addDocsMcpEntryIfAbsent, DOCS_MCP_SERVER_NAME } from './chunk-XCK2YAY5.js';
5
- import { installStarterPack } from './chunk-42ZI52JW.js';
6
- import { C } from './chunk-YRYMNBGQ.js';
7
- import { resolveGitCommonDir, defaultGitRunner } from './chunk-YZ5F4PIS.js';
8
- import { pinExists, readContextManifest } from './chunk-WTE3MQML.js';
9
- import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-POJMEEJJ.js';
10
- import { generatedHeaderSource } from './chunk-OHVJA253.js';
3
+ import { runEmit } from './chunk-ZHAUSEEN.js';
4
+ import { addStdioMcpEntryIfAbsent, addDocsMcpEntryIfAbsent, DOCS_MCP_SERVER_NAME } from './chunk-YVZJ6GKF.js';
5
+ import { installStarterPack } from './chunk-6X4SQ5MY.js';
6
+ import { C } from './chunk-ANPNT6PM.js';
7
+ import { resolveGitCommonDir, defaultGitRunner } from './chunk-452TM7J4.js';
8
+ import { loadTemplateFiles, loadMaintainedFiles, skillDir } from './chunk-NNLA7LLQ.js';
9
+ import { generatedHeaderSource } from './chunk-WFSUUUAN.js';
10
+ import { cliInvocation } from './chunk-CRB3POP4.js';
11
+ import { cloneIsGoverned } from './chunk-YQR6E6OV.js';
11
12
  import { execFileSync } from 'child_process';
12
13
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
13
14
  import { join, dirname } from 'path';
@@ -39,10 +40,9 @@ function gitTopLevel(cwd) {
39
40
  }
40
41
  }
41
42
  function contextComesFromControlPlane(repoRoot) {
42
- if (pinExists(repoRoot)) return true;
43
43
  const gitCommonDir = resolveGitCommonDir(defaultGitRunner, repoRoot);
44
44
  if (gitCommonDir === void 0) throw new GitLookupError("git could not report this repository's git directory");
45
- return readContextManifest(gitCommonDir) !== void 0;
45
+ return cloneIsGoverned(gitCommonDir);
46
46
  }
47
47
  function mcpReplacementLosesNothing(existing, content) {
48
48
  try {
@@ -176,7 +176,7 @@ function renderPaths(paths, kept) {
176
176
  `;
177
177
  }
178
178
  function renderDone(paths, kept, mcpKept) {
179
- const mcpNote = mcpKept ? ` ${C.yellow}!${C.reset} The outerlayer and ${DOCS_MCP_SERVER_NAME} entries were not added to .mcp.json, so their MCP servers are not set up. Add them to .mcp.json by hand, or move the servers you wrote into .outerlayer/mcp.json and run \`outerlayer context emit\`.
179
+ const mcpNote = mcpKept ? ` ${C.yellow}!${C.reset} The outerlayer and ${DOCS_MCP_SERVER_NAME} entries were not added to .mcp.json, so their MCP servers are not set up. Add them to .mcp.json by hand, or move the servers you wrote into .outerlayer/mcp.json and run \`${cliInvocation()} context emit\`.
180
180
  ` : "";
181
181
  const what = mcpKept ? "the OuterLayer skills" : "the OuterLayer skills and MCP servers";
182
182
  if (paths.length === 0) {
@@ -2,6 +2,7 @@
2
2
  import { createRequire } from 'node:module';
3
3
  import { recordReportedCost } from './chunk-BKO6JEZI.js';
4
4
  import { decodeWrappedCommand } from './chunk-5QRP3MVS.js';
5
+ import { cliInvocation } from './chunk-CRB3POP4.js';
5
6
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
6
7
  import { readFileSync } from 'fs';
7
8
  import { spawn } from 'child_process';
@@ -69,7 +70,7 @@ function renderStatusline(stdin, state, now) {
69
70
  segments.push(`${state.unsynced} unsynced`);
70
71
  }
71
72
  if (!fresh) {
72
- segments.push(dim("outerlayer doctor"));
73
+ segments.push(dim(`${cliInvocation()} doctor`));
73
74
  }
74
75
  const prefix = "\u2B22 OL";
75
76
  return segments.length === 0 ? prefix : `${prefix} ${segments.join(dim(" \xB7 "))}`;
@@ -1,40 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
4
- import { readFileSync, existsSync, rmSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { isSameProcess } from './chunk-JBJTC3UU.js';
4
+ import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-N2PXGQRL.js';
5
+ import { existsSync, readFileSync, rmSync, mkdirSync, writeFileSync } from 'fs';
5
6
  import { dirname } from 'path';
6
- import { execFileSync } from 'child_process';
7
7
 
8
8
  createRequire(import.meta.url);
9
- function startTimeOf(pid) {
10
- if (process.platform === "linux") return startTimeOfLinux(pid);
11
- return startTimeOfPs(pid);
12
- }
13
- function startTimeOfLinux(pid) {
14
- try {
15
- const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
16
- const afterComm = stat.slice(stat.lastIndexOf(")") + 1).trim();
17
- const fields = afterComm.split(/\s+/);
18
- if (fields[0] === "Z" || fields[0] === "X") return null;
19
- const starttime = fields[19];
20
- return starttime ?? null;
21
- } catch {
22
- return null;
23
- }
24
- }
25
- function startTimeOfPs(pid) {
26
- try {
27
- const out = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" }).trim();
28
- return out.length > 0 ? out : null;
29
- } catch {
30
- return null;
31
- }
32
- }
33
- function isSameProcess(pid, start) {
34
- return startTimeOf(pid) === start;
35
- }
36
-
37
- // src/runner/pidfile.ts
38
9
  function readPidFile(path) {
39
10
  if (!existsSync(path)) return null;
40
11
  try {
@@ -68,4 +39,4 @@ function claimPidFile(path, self, isSameProcessFn = isSameProcess) {
68
39
  writePidFile(path, self);
69
40
  }
70
41
 
71
- export { RunnerAlreadyRunningError, claimPidFile, isSameProcess, readPidFile, releasePidFile, startTimeOf };
42
+ export { RunnerAlreadyRunningError, claimPidFile, readPidFile, releasePidFile };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { readLaunchSpoolState, launchesPath } from './chunk-GQD22KKN.js';
3
+ import { readLaunchSpoolState, launchesPath } from './chunk-T2Q7ISPE.js';
4
4
 
5
5
  createRequire(import.meta.url);
6
6
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { readCloudConfig, cloudConfigPath } from './chunk-EABW6AJQ.js';
4
- import { signRunnerRequest, signerFor } from './chunk-V7NIVOFD.js';
5
4
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
+ import { signRunnerRequest, signerFor } from './chunk-M2ZWWTWZ.js';
6
6
 
7
7
  createRequire(import.meta.url);
8
8
 
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+
4
+ createRequire(import.meta.url);
5
+
6
+ // src/control-plane-paths.ts
7
+ var CONTROL_PLANE_DIR_VARIABLE = "OUTERLAYER_CONTROL_PLANE_DIR";
8
+ var PROJECT_DIR_SPELLINGS = [
9
+ /"\$\{?CLAUDE_PROJECT_DIR\}?"\/\.claude(?![\w.-])/g,
10
+ /\$\{CLAUDE_PROJECT_DIR\}\/\.claude(?![\w.-])/g,
11
+ /\$CLAUDE_PROJECT_DIR\/\.claude(?![\w.-])/g
12
+ ];
13
+ var CONTROL_PLANE_VARIABLE_FORMS = [
14
+ new RegExp(`\\$\\{${CONTROL_PLANE_DIR_VARIABLE}:-[^}]*\\}/\\.claude(?![\\w.-])`, "g"),
15
+ new RegExp(`\\$\\{${CONTROL_PLANE_DIR_VARIABLE}\\}/\\.claude(?![\\w.-])`, "g"),
16
+ new RegExp(`\\$${CONTROL_PLANE_DIR_VARIABLE}/\\.claude(?![\\w.-])`, "g")
17
+ ];
18
+ function rewriteHookCommand(command, dir) {
19
+ return PROJECT_DIR_SPELLINGS.reduce((text, pattern) => text.replace(pattern, () => `${dir}/.claude`), command);
20
+ }
21
+ function rewriteControlPlanePaths(text, dir) {
22
+ return text.replace(/(?<![\w./~$}:-])(\.outerlayer|\.claude)\//g, (_match, root) => `${dir}/${root}/`);
23
+ }
24
+ function hookCommandEscapesControlPlane(command) {
25
+ const remaining = [...PROJECT_DIR_SPELLINGS, ...CONTROL_PLANE_VARIABLE_FORMS].reduce((text, pattern) => text.replace(pattern, " "), command);
26
+ return /\.claude(?:\/|["'\s;|&)]|$)/.test(remaining) || /\$\{?CLAUDE_PROJECT_DIR\}?"?\/\.claude/.test(remaining);
27
+ }
28
+ var MINIMUM_CLAUDE_VERSION = "2.1.284";
29
+ function versionParts(text) {
30
+ const found = /(\d+)\.(\d+)\.(\d+)/.exec(text);
31
+ return found === null ? void 0 : [Number(found[1]), Number(found[2]), Number(found[3])];
32
+ }
33
+ function versionAtLeast(text, minimum = MINIMUM_CLAUDE_VERSION) {
34
+ const have = versionParts(text);
35
+ const need = versionParts(minimum);
36
+ if (have === void 0 || need === void 0) return false;
37
+ const difference = have.map((part, i) => part - need[i]).find((delta) => delta !== 0);
38
+ return (difference ?? 0) >= 0;
39
+ }
40
+ var SHIM_SCRIPT = `#!/bin/sh
41
+ exec outerlayer claude "$@"
42
+ `;
43
+
44
+ export { CONTROL_PLANE_DIR_VARIABLE, MINIMUM_CLAUDE_VERSION, SHIM_SCRIPT, hookCommandEscapesControlPlane, rewriteControlPlanePaths, rewriteHookCommand, versionAtLeast };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { ArtifactTombstoneRecordSchema, ArtifactSpoolRecordSchema } from './chunk-J5M7BSJQ.js';
3
+ import { ArtifactTombstoneRecordSchema, ArtifactSpoolRecordSchema } from './chunk-7VUK5RFS.js';
4
4
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
5
  import { mkdirSync, appendFileSync, writeFileSync, renameSync, unlinkSync, existsSync, readdirSync, readFileSync, statSync } from 'fs';
6
6
  import { join, dirname } from 'path';
@@ -273,7 +273,9 @@ function artifactLockIsStale(path, nowMs, isAlive) {
273
273
  }
274
274
  }
275
275
  async function acquireArtifactSpoolLock(home = homeFromEnv(), opts = {}) {
276
- const path = artifactsLockPath(home);
276
+ return acquireSpoolLockAt(artifactsLockPath(home), opts);
277
+ }
278
+ async function acquireSpoolLockAt(path, opts = {}) {
277
279
  const waitMs = opts.waitMs ?? ARTIFACT_LOCK_WAIT_MS;
278
280
  const sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
279
281
  const isAlive = opts.isAlive ?? processIsAlive;
@@ -460,4 +462,4 @@ function resolveArtifactTurnIndex(turns, filename) {
460
462
  return found;
461
463
  }
462
464
 
463
- export { ARTIFACT_RETRY_MAX_AGE_MS, acquireArtifactSpoolLock, appendArtifactRecord, appendArtifactTombstone, artifactBlobsDir, artifactsSpoolPath, artifactsStatePath, findPendingArtifactRecord, isArtifactTombstoned, localArtifactIds, migrateLegacyArtifactSidecars, neededArtifactShas, planArtifactUpload, readEffectiveArtifactSpoolState, reclaimUnneededArtifactBlobs, resolveArtifactTurnIndex, sanitizeTerminal, writeArtifactSpoolState };
465
+ export { ARTIFACT_RETRY_MAX_AGE_MS, acquireArtifactSpoolLock, acquireSpoolLockAt, appendArtifactRecord, appendArtifactTombstone, artifactBlobsDir, artifactsSpoolPath, artifactsStatePath, findPendingArtifactRecord, isArtifactTombstoned, localArtifactIds, migrateLegacyArtifactSidecars, neededArtifactShas, planArtifactUpload, readEffectiveArtifactSpoolState, reclaimUnneededArtifactBlobs, resolveArtifactTurnIndex, sanitizeTerminal, writeArtifactSpoolState };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { EXEC_PORT } from './chunk-RSNSZ2LJ.js';
3
+ import { EXEC_PORT } from './chunk-X5H2WYMB.js';
4
4
  import { createConnection } from 'net';
5
5
 
6
6
  createRequire(import.meta.url);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { C } from './chunk-YRYMNBGQ.js';
3
+ import { C } from './chunk-ANPNT6PM.js';
4
4
  import { readCloudConfig, removeCloudConfigKeys } from './chunk-EABW6AJQ.js';
5
5
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
6
6
 
@@ -1,20 +1,69 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { autoConnect } from './chunk-6Y5RUTHO.js';
4
- import { detectActiveSession } from './chunk-IP7BIBXJ.js';
5
- import { workPageUrl } from './chunk-3NSNIIBE.js';
6
- import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
7
- import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
8
- import { C } from './chunk-YRYMNBGQ.js';
3
+ import { detectActiveSession, autoConnect } from './chunk-IF2OSI5T.js';
4
+ import { workPageUrl } from './chunk-CG36IONK.js';
5
+ import { sanitizeTerminal } from './chunk-G3IJ3N6F.js';
6
+ import { resolveRepoIdentity } from './chunk-QNLX477Q.js';
7
+ import { C } from './chunk-ANPNT6PM.js';
9
8
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
10
- import { defaultFetch } from './chunk-YO4PSLVB.js';
11
- import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-PLMTLGYH.js';
12
- import { signRunnerRequest } from './chunk-V7NIVOFD.js';
9
+ import { defaultFetch } from './chunk-FVMYKL6L.js';
10
+ import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-NTRCK4NH.js';
13
11
  import { readRepoConnection } from './chunk-BJ3KTMDH.js';
14
12
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
13
+ import { signRunnerRequest } from './chunk-M2ZWWTWZ.js';
15
14
 
16
15
  createRequire(import.meta.url);
17
16
 
17
+ // ../trackers/src/descriptor.ts
18
+ var TRACKERS = {
19
+ github: {
20
+ label: "GitHub",
21
+ hasStoredConnection: false,
22
+ verifiedKeys: false,
23
+ issueRef: "number",
24
+ scopeNoun: "repository",
25
+ connect: { kind: "builtin" }
26
+ },
27
+ linear: {
28
+ label: "Linear",
29
+ hasStoredConnection: true,
30
+ verifiedKeys: true,
31
+ issueRef: "key",
32
+ scopeNoun: "team",
33
+ connect: {
34
+ kind: "oauth",
35
+ summary: "Read the teams you pick in a workspace, and file specs as issues. One authorization; Linear registers its own webhook."
36
+ }
37
+ },
38
+ jira: {
39
+ label: "Jira",
40
+ hasStoredConnection: true,
41
+ verifiedKeys: true,
42
+ issueRef: "key",
43
+ scopeNoun: "project",
44
+ connect: {
45
+ kind: "form",
46
+ summary: "Read the projects you pick on a site, and file specs as issues, with a service account. One webhook is created in Jira by an admin.",
47
+ intro: "Uses a service account's OAuth client credential. Create one under Atlassian administration, then paste it here. Give it read:jira-work, and write:jira-work to file specs. A credential's scopes are fixed when it is created.",
48
+ notice: "After connecting, you get a webhook URL and secret to put on one webhook in Jira. That is the only manual step, and the only time the secret is shown.",
49
+ fields: [
50
+ { name: "siteUrl", label: "Site URL", placeholder: "https://your-team.atlassian.net" },
51
+ { name: "clientId", label: "Client ID" },
52
+ { name: "clientSecret", label: "Client secret", secret: true },
53
+ {
54
+ name: "projectKeys",
55
+ label: "Projects to read",
56
+ placeholder: "ENG, OPS",
57
+ helperText: "Project keys, comma separated. Only issues in these projects are listed."
58
+ }
59
+ ]
60
+ }
61
+ }
62
+ };
63
+ function verifiedTrackerNames(descriptors) {
64
+ return Object.keys(descriptors).filter((name) => descriptors[name].verifiedKeys);
65
+ }
66
+
18
67
  // src/runner/signature-refusals.ts
19
68
  var MESSAGES = /* @__PURE__ */ new Map([
20
69
  [
@@ -101,14 +150,8 @@ function parseRetries(raw) {
101
150
  const n = parseTrackerNumber(raw);
102
151
  return n === void 0 ? 0 : Math.min(n, WORK_LINK_RETRY_DELAYS_MS.length);
103
152
  }
104
- function resolveSubject(opts) {
105
- const issue = parsePositiveInt(opts.issue, "--issue");
106
- if (issue === void 0) {
107
- throw new WorkCommandError("pass --issue");
108
- }
109
- return { issue };
110
- }
111
- var TRACKER_FLAG_HELP = 'expected "linear" or "jira" \u2014 GitHub issues are named by number, with no --tracker';
153
+ var VERIFIED_TRACKERS = verifiedTrackerNames(TRACKERS);
154
+ var TRACKER_FLAG_HELP = `expected ${VERIFIED_TRACKERS.map((name) => `"${name}"`).join(" or ")} \u2014 GitHub issues are named by number, with no --tracker`;
112
155
  function resolveWorkSubject(opts) {
113
156
  if (opts.issue === void 0) {
114
157
  throw new WorkCommandError("pass --issue");
@@ -120,32 +163,36 @@ function resolveWorkSubject(opts) {
120
163
  }
121
164
  return { repository: resolveRepository(opts.repo, opts.cwd), issue: asNumber };
122
165
  }
123
- if (opts.tracker !== void 0 && opts.tracker !== "linear" && opts.tracker !== "jira") {
166
+ const tracker = opts.tracker === void 0 ? void 0 : VERIFIED_TRACKERS.find((name) => name === opts.tracker);
167
+ if (opts.tracker !== void 0 && tracker === void 0) {
124
168
  throw new WorkCommandError(`invalid --tracker "${opts.tracker}": ${TRACKER_FLAG_HELP}`);
125
169
  }
126
- return { key: String(opts.issue), tracker: opts.tracker, repository: opts.repo };
170
+ return { key: String(opts.issue), tracker, repository: opts.repo };
127
171
  }
128
172
  async function postJson(url, path, apiKey, appId, body, fetchImpl, timeoutMs, signer) {
129
173
  const deadline = timeoutMs === void 0 ? void 0 : startDeadline(timeoutMs);
130
174
  try {
131
- return await postJsonBefore(url, path, apiKey, appId, body, fetchImpl, deadline, signer);
175
+ return await sendJsonBefore("POST", url, path, apiKey, appId, body, fetchImpl, deadline, signer);
132
176
  } finally {
133
177
  deadline?.clear();
134
178
  }
135
179
  }
180
+ function patchJson(url, path, apiKey, appId, body, fetchImpl) {
181
+ return sendJsonBefore("PATCH", url, path, apiKey, appId, body, fetchImpl, void 0, void 0);
182
+ }
136
183
  function startDeadline(ms) {
137
184
  const controller = new AbortController();
138
185
  const timer = setTimeout(() => controller.abort(), ms);
139
186
  return { signal: controller.signal, ms, clear: () => clearTimeout(timer) };
140
187
  }
141
- async function postJsonBefore(url, path, apiKey, appId, body, fetchImpl, deadline, signer) {
188
+ async function sendJsonBefore(method, url, path, apiKey, appId, body, fetchImpl, deadline, signer) {
142
189
  let response;
143
190
  const endpoint = new URL(path, url).toString();
144
191
  const payload = JSON.stringify(body);
145
- const signature = await signatureHeaders("POST", endpoint, payload ?? "", signer);
192
+ const signature = await signatureHeaders(method, endpoint, payload ?? "", signer);
146
193
  try {
147
194
  response = await fetchImpl(endpoint, {
148
- method: "POST",
195
+ method,
149
196
  headers: {
150
197
  "content-type": "application/json",
151
198
  authorization: `Bearer ${apiKey}`,
@@ -264,6 +311,22 @@ async function findLiveItem(creds, repository, subject, fetchImpl) {
264
311
  const items = page.data ?? [];
265
312
  return items[0] ?? null;
266
313
  }
314
+ async function findLiveItemForIssue(creds, opts, fetchImpl) {
315
+ const subject = resolveWorkSubject(opts);
316
+ if ("issue" in subject) {
317
+ const item = await findLiveItem(creds, subject.repository, { issue: subject.issue }, fetchImpl);
318
+ return { item, label: `${subject.repository}#${subject.issue}` };
319
+ }
320
+ const query = new URLSearchParams({ key: subject.key });
321
+ if (subject.tracker !== void 0) query.set("tracker", subject.tracker);
322
+ const page = await getJson(creds.url, `/v1/work-items?${query.toString()}`, creds.apiKey, creds.appId, fetchImpl);
323
+ const items = page.data ?? [];
324
+ if (items.length > 1) {
325
+ const trackers = items.map((i) => i.tracker ?? "unknown").join(" and ");
326
+ throw new WorkCommandError(`${subject.key} names an item in ${trackers}. Pass --tracker to say which.`);
327
+ }
328
+ return { item: items[0] ?? null, label: subject.key };
329
+ }
267
330
  function environmentFacts(opts) {
268
331
  const identity = resolveRepoIdentity(opts.cwd);
269
332
  const sessionId = opts.sessionIdOverride ?? detectActiveSession({
@@ -336,9 +399,7 @@ async function runWorkBuild(opts) {
336
399
  const { data: data2 } = await postJsonWithRetry(url, "/v1/work-items", apiKey, appId, body2, fetchImpl, retries, sleep);
337
400
  const label = data2.number != null ? `#${data2.number}` : String(data2.workItemId);
338
401
  const launch = data2.number != null ? `OUTERLAYER_WORK=${data2.number} claude` : null;
339
- const branch = typeof data2.branch === "string" && data2.branch !== "" ? data2.branch : null;
340
- const output2 = opts.json ? JSON.stringify({ ...data2, ...launch !== null ? { launchCommand: launch } : {} }) : `${C.green}\u2713${C.reset} on Work for you to build ${C.dim}\xB7${C.reset} ${label} ${C.dim}\xB7${C.reset} no host will take it` + (branch !== null ? `
341
- branch: ${branch}` : "") + (launch !== null ? `
402
+ const output2 = opts.json ? JSON.stringify({ ...data2, ...launch !== null ? { launchCommand: launch } : {} }) : `${C.green}\u2713${C.reset} on Work for you to build ${C.dim}\xB7${C.reset} ${label} ${C.dim}\xB7${C.reset} no host will take it` + (launch !== null ? `
342
403
  start your session with: ${launch}` : "");
343
404
  if (!opts.quiet) process.stdout.write(output2 + "\n");
344
405
  return { data: data2, output: output2 };
@@ -360,7 +421,7 @@ async function runWorkLinkSession(opts) {
360
421
  return await linkSession(opts);
361
422
  } catch (err) {
362
423
  if (opts.logFailures) {
363
- const { logHookError } = await import('./hook-fast-R4FVSZ2H.js');
424
+ const { logHookError } = await import('./hook-fast-YAZG5MDX.js');
364
425
  const reason = err instanceof Error ? err.message : String(err);
365
426
  logHookError(opts.home ?? homeFromEnv(opts.env ?? process.env), new Error(`work link-session #${opts.number} failed: ${reason}`));
366
427
  }
@@ -428,13 +489,10 @@ async function runWorkRemove(opts) {
428
489
  if (!opts.quiet) process.stdout.write(output2 + "\n");
429
490
  return { data: data2, output: output2 };
430
491
  }
431
- const subject = resolveSubject(opts);
432
- const repository = resolveRepository(opts.repo, cwd);
433
- const number = subject.issue;
434
- const item = await findLiveItem(creds, repository, subject, fetchImpl);
492
+ const { item, label } = await findLiveItemForIssue(creds, { ...opts, cwd }, fetchImpl);
435
493
  if (!item) {
436
494
  const data2 = { workItemId: null, removed: true, itemWithdrawn: false, noLiveAddition: true };
437
- const output2 = opts.json ? JSON.stringify(data2) : `${C.green}\u2713${C.reset} nothing to remove ${C.dim}\xB7${C.reset} no live addition on ${repository}#${number}`;
495
+ const output2 = opts.json ? JSON.stringify(data2) : `${C.green}\u2713${C.reset} nothing to remove ${C.dim}\xB7${C.reset} no live addition on ${label}`;
438
496
  if (!opts.quiet) process.stdout.write(output2 + "\n");
439
497
  return { data: data2, output: output2 };
440
498
  }
@@ -485,13 +543,10 @@ async function runWorkStatus(opts) {
485
543
  if (!opts.quiet) process.stdout.write(body + "\n");
486
544
  return { data: data2, output: body };
487
545
  }
488
- const subject = resolveSubject(opts);
489
- const repository = resolveRepository(opts.repo, cwd);
490
546
  const fetchImpl = opts.fetchImpl ?? defaultFetch;
491
- const number = subject.issue;
492
- const item = await findLiveItem(creds, repository, subject, fetchImpl);
547
+ const { item, label } = await findLiveItemForIssue(creds, { ...opts, cwd }, fetchImpl);
493
548
  if (!item) {
494
- throw new WorkCommandError(`no live item for ${repository}#${number}`);
549
+ throw new WorkCommandError(`no live item for ${label}`);
495
550
  }
496
551
  const { data } = await getJson(creds.url, `/v1/work-items/${item.id}`, creds.apiKey, creds.appId, fetchImpl);
497
552
  const output = statusOutput(data, opts.json === true);
@@ -536,4 +591,4 @@ async function runWorkList(opts) {
536
591
  return { data, output };
537
592
  }
538
593
 
539
- export { WorkCommandError, environmentFacts, findLiveItem, getJson, isSignatureRefusal, parsePositiveInt, postJson, resolveCredentials, resolveRepository, runWorkBuild, runWorkLinkSession, runWorkList, runWorkRemove, runWorkStatus, signatureRefusalMessage };
594
+ export { TRACKERS, WorkCommandError, environmentFacts, findLiveItemForIssue, getJson, isSignatureRefusal, parsePositiveInt, patchJson, postJson, resolveCredentials, runWorkBuild, runWorkLinkSession, runWorkList, runWorkRemove, runWorkStatus, signatureRefusalMessage, verifiedTrackerNames };
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
+ import { fetchFactories, selectFactory, saveFactoryConnection, autoConnectedLine } from './chunk-CG36IONK.js';
4
+ import { resolveRepoIdentity } from './chunk-QNLX477Q.js';
5
+ import { resolveGatewayCredentials } from './chunk-NTRCK4NH.js';
6
+ import { findGitDirs, readRepoConnection } from './chunk-BJ3KTMDH.js';
7
+ import { cliInvocation } from './chunk-CRB3POP4.js';
8
+ import { homeFromEnv } from './chunk-VNVZDWO3.js';
3
9
  import { openSync, fstatSync, readSync, closeSync } from 'fs';
4
10
  import { join } from 'path';
5
11
 
@@ -76,4 +82,32 @@ function transcriptPathForSession(sessionId, home, readTailImpl = readTail) {
76
82
  return found;
77
83
  }
78
84
 
79
- export { detectActiveSession, transcriptPathForSession };
85
+ // src/auto-connect.ts
86
+ async function autoConnect(opts) {
87
+ const env = opts.env ?? process.env;
88
+ const home = opts.home ?? homeFromEnv(env);
89
+ const cwd = opts.cwd ?? process.cwd();
90
+ const resolved = resolveGatewayCredentials({ url: opts.url, apiKey: opts.apiKey, appId: opts.appId, home, env, cwd });
91
+ if (resolved.ok || resolved.kind !== "login" || !resolved.credential || !resolved.url) return void 0;
92
+ const dirs = findGitDirs(cwd);
93
+ const repository = opts.repository ?? resolveRepoIdentity(cwd).gitRepo;
94
+ if (dirs === void 0 || repository === void 0) return void 0;
95
+ const listed = await fetchFactories(opts.fetchImpl ?? fetch, resolved.url, resolved.credential, repository);
96
+ if (!listed.ok || listed.factories.length === 0) return void 0;
97
+ const chosen = selectFactory(listed.factories, readRepoConnection(cwd), repository);
98
+ if (chosen.factory === void 0) {
99
+ return `you are logged in, but no factory is chosen for this repository, and more than one fits. Run \`${cliInvocation()} connect --factory <org>/<factory>\` with one of:
100
+ ${chosen.text}
101
+ Or pass --app-id / set OUTERLAYER_APP_ID.`;
102
+ }
103
+ try {
104
+ saveFactoryConnection(dirs.gitCommonDir, void 0, chosen.factory, /* @__PURE__ */ new Date());
105
+ } catch {
106
+ return void 0;
107
+ }
108
+ process.stderr.write(`${autoConnectedLine(repository, chosen.factory, chosen.reason)}
109
+ `);
110
+ return void 0;
111
+ }
112
+
113
+ export { autoConnect, detectActiveSession, transcriptPathForSession };
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { skillDir, FORMER_MAINTAINED_NAMES, isMaintainedCopy } from './chunk-POJMEEJJ.js';
4
- import { emitTree, ALL_TARGET_IDS, classifyTree } from './chunk-OHVJA253.js';
3
+ import { skillDir, FORMER_MAINTAINED_NAMES, isMaintainedCopy } from './chunk-NNLA7LLQ.js';
4
+ import { emitTree, ALL_TARGET_IDS, classifyTree } from './chunk-WFSUUUAN.js';
5
+ import { cliInvocation } from './chunk-CRB3POP4.js';
5
6
  import { readFileSync, existsSync, unlinkSync, rmdirSync, readdirSync } from 'fs';
6
7
  import { join, dirname, relative, sep } from 'path';
7
8
 
@@ -70,7 +71,7 @@ function checkFormerSkillNames(repoRoot) {
70
71
  name: "Renamed skills",
71
72
  status: "warn",
72
73
  detail: `copies under earlier names: ${retired.map(skillDir).join(", ")}`,
73
- fix: "Run `outerlayer context emit` to replace them with the current names, then commit."
74
+ fix: `Run \`${cliInvocation()} context emit\` to replace them with the current names, then commit.`
74
75
  });
75
76
  }
76
77
  const hits = filesNamingFormerSkills(repoRoot);
@@ -79,7 +80,7 @@ function checkFormerSkillNames(repoRoot) {
79
80
  name: "Skill names",
80
81
  status: "warn",
81
82
  detail: `${hits.map((h) => `${h.path} (${h.formers.join(", ")})`).join("; ")} still ${hits.length === 1 ? "names" : "name"} a skill by an earlier name`,
82
- fix: `Replace each earlier name with the current one: ${Object.entries(FORMER_MAINTAINED_NAMES).map(([former, current]) => `${former} \u2192 ${current}`).join(", ")}. \`outerlayer init --update\` does this for a file you have not edited.`
83
+ fix: `Replace each earlier name with the current one: ${Object.entries(FORMER_MAINTAINED_NAMES).map(([former, current]) => `${former} \u2192 ${current}`).join(", ")}. \`${cliInvocation()} init --update\` does this for a file you have not edited.`
83
84
  });
84
85
  }
85
86
  return checks;
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { readFileSync } from 'fs';
4
+ import { execFileSync } from 'child_process';
5
+
6
+ createRequire(import.meta.url);
7
+ function startTimeOf(pid) {
8
+ if (process.platform === "linux") return startTimeOfLinux(pid);
9
+ return startTimeOfPs(pid);
10
+ }
11
+ function startTimeOfLinux(pid) {
12
+ try {
13
+ const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
14
+ const afterComm = stat.slice(stat.lastIndexOf(")") + 1).trim();
15
+ const fields = afterComm.split(/\s+/);
16
+ if (fields[0] === "Z" || fields[0] === "X") return null;
17
+ const starttime = fields[19];
18
+ return starttime ?? null;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+ function startTimeOfPs(pid) {
24
+ try {
25
+ const out = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" }).trim();
26
+ return out.length > 0 ? out : null;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ function isSameProcess(pid, start) {
32
+ return startTimeOf(pid) === start;
33
+ }
34
+
35
+ export { isSameProcess, startTimeOf };