@outerlayer/cli 0.12.0 → 0.14.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 (98) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +9 -4
  3. package/dist/agent-setup-CN2HUBEJ.js +26 -0
  4. package/dist/{build-LELVCWHV.js → build-BG2YENGK.js} +2 -2
  5. package/dist/build-info.json +1 -1
  6. package/dist/{builder-OF67QKDS.js → builder-2D3WSOOY.js} +4 -4
  7. package/dist/{check-RJOJU2WA.js → check-SRGR36KI.js} +37 -37
  8. package/dist/{chunk-XR3BJN33.js → chunk-22RMZBGW.js} +20 -1
  9. package/dist/{chunk-AHZQBGD2.js → chunk-2M63GG56.js} +1 -1
  10. package/dist/{chunk-SZOVKFWB.js → chunk-3NLOVYJQ.js} +4 -4
  11. package/dist/{chunk-USO2DKBD.js → chunk-4KSQKQXE.js} +8 -2
  12. package/dist/{chunk-QEESCZ26.js → chunk-4NPZVMJU.js} +2 -2
  13. package/dist/{chunk-DVT73QJO.js → chunk-4OGPT6I2.js} +14 -14
  14. package/dist/{chunk-ZQUCTFXC.js → chunk-FOJGZYFC.js} +11 -5
  15. package/dist/{chunk-G5XY6CVG.js → chunk-HFKKFTXU.js} +12 -16
  16. package/dist/{chunk-ANW7VHMH.js → chunk-HFKQIZXL.js} +95 -16
  17. package/dist/{chunk-325272HM.js → chunk-J5M7BSJQ.js} +11 -4
  18. package/dist/{chunk-NPXQXYVA.js → chunk-JJEGKEFV.js} +6 -8
  19. package/dist/{chunk-3ZPYPFNM.js → chunk-JXYVA45C.js} +1 -1
  20. package/dist/{chunk-IBB4D4KE.js → chunk-JYIEHQIF.js} +1 -1
  21. package/dist/{chunk-5TGQHOUX.js → chunk-KFRM23WD.js} +3 -3
  22. package/dist/{chunk-GGMRZPEA.js → chunk-KMDLFI4P.js} +1 -1
  23. package/dist/{chunk-G5IEVZGI.js → chunk-LCEXHCHD.js} +1 -1
  24. package/dist/{chunk-27Y7AX2O.js → chunk-MSASO5O4.js} +44 -8
  25. package/dist/chunk-MTF3PHVZ.js +161 -0
  26. package/dist/{chunk-G4RXNF2L.js → chunk-NHDVD7DU.js} +12 -12
  27. package/dist/{chunk-GATMPAMK.js → chunk-NPQWKGUA.js} +2 -2
  28. package/dist/{chunk-YEQL54G5.js → chunk-PQUM37BK.js} +1 -1
  29. package/dist/{chunk-GEBQXUFY.js → chunk-Q2UK3C4P.js} +24 -10
  30. package/dist/{chunk-SKHN72JO.js → chunk-RBKIH5ST.js} +13 -13
  31. package/dist/{chunk-2KLBKHT4.js → chunk-RLYZLUQ7.js} +1 -1
  32. package/dist/chunk-RMWLBUCJ.js +50 -0
  33. package/dist/{chunk-PDRYXERC.js → chunk-SMHOCDCI.js} +5 -5
  34. package/dist/{chunk-6FH2AHJY.js → chunk-SWHQ4LUM.js} +30 -17
  35. package/dist/{agent-setup-52LRZ6AY.js → chunk-SXWS25JC.js} +8 -21
  36. package/dist/{chunk-VK25O2ZM.js → chunk-T5VPP6H7.js} +1 -1
  37. package/dist/{chunk-JOXYKGUV.js → chunk-WUYTWNMA.js} +4 -2
  38. package/dist/{chunk-A56PYPDF.js → chunk-XCK2YAY5.js} +14 -4
  39. package/dist/{chunk-C7N4TDHW.js → chunk-YDSMROTB.js} +5 -5
  40. package/dist/{chunk-FO7KKPYG.js → chunk-ZA5CHAFY.js} +2 -2
  41. package/dist/{cli-JSSRGA6V.js → cli-UW2H3UUW.js} +138 -85
  42. package/dist/{command-3WYZ2ML5.js → command-5LD7G2QQ.js} +27 -27
  43. package/dist/{config-MKBX2ZQD.js → config-N3C6DFQA.js} +1 -1
  44. package/dist/{connect-cmd-A5NGODCJ.js → connect-cmd-JUAW6MU4.js} +5 -5
  45. package/dist/{context-adopt-BAHGXJZV.js → context-adopt-PJUMW2EZ.js} +6 -6
  46. package/dist/{context-materialize-45OVRRK2.js → context-materialize-FDHYKNDT.js} +6 -6
  47. package/dist/{dist-EQDW7MZW.js → dist-LKDWQWZ7.js} +2 -2
  48. package/dist/{docker-VGWEJWI7.js → docker-4J77Q2BF.js} +5 -5
  49. package/dist/{doctor-4FFXTQJ2.js → doctor-H7REBSLQ.js} +29 -29
  50. package/dist/{emit-artifact-cmd-QS7XNKBT.js → emit-artifact-cmd-FOGNJXVR.js} +18 -21
  51. package/dist/emit-cmd-ZEYWLC2C.js +11 -0
  52. package/dist/{emit-criteria-cmd-XN6AKA53.js → emit-criteria-cmd-HBP6XS5C.js} +8 -11
  53. package/dist/{emit-finding-cmd-SQJSEHYS.js → emit-finding-cmd-DLI4MKMN.js} +8 -11
  54. package/dist/{emit-result-cmd-56RHPHOA.js → emit-result-cmd-MUSL7GQA.js} +12 -15
  55. package/dist/{hook-fast-HLESEJAL.js → hook-fast-WYQD6D7T.js} +1 -1
  56. package/dist/{import-capture-cmd-NHMAFDM6.js → import-capture-cmd-6QVCOIHK.js} +10 -13
  57. package/dist/{import-ruler-cmd-UYB32YUT.js → import-ruler-cmd-KLRGRBCP.js} +7 -10
  58. package/dist/index.js +3 -3
  59. package/dist/{init-UZKD4RY7.js → init-NTXA3SN7.js} +28 -28
  60. package/dist/{init-6AY5T3P6.js → init-UBXK6MF7.js} +47 -37
  61. package/dist/{init-cmd-XGSX2BCC.js → init-cmd-NARDUPIG.js} +20 -13
  62. package/dist/init-update-O5YYFGF4.js +165 -0
  63. package/dist/{install-VXXI7UR6.js → install-HDBIPHTX.js} +2 -2
  64. package/dist/{login-browser-7XGSV7MA.js → login-browser-QZCS3WW6.js} +2 -1
  65. package/dist/{loop-4NMI2USY.js → loop-SW2M5VDD.js} +37 -37
  66. package/dist/{machine-4JX2D4TS.js → machine-U66FKVDP.js} +23 -23
  67. package/dist/mcp-install-cmd-FDTPQQS6.js +9 -0
  68. package/dist/{runner-env-WAXTJXZK.js → runner-env-XTC53CHN.js} +3 -3
  69. package/dist/runner-key-3JSMDRJA.js +154 -0
  70. package/dist/{session-sender-T7PN4YRC.js → session-sender-4M247XSV.js} +7 -7
  71. package/dist/starter-pack-IB4UUIX3.js +11 -0
  72. package/dist/{status-OXBIRWJ7.js → status-STZYPU3W.js} +30 -30
  73. package/dist/{sync-cmd-EVXNR47N.js → sync-cmd-UWD5JOFU.js} +6 -6
  74. package/dist/{watch-7NSFOAL5.js → watch-DBNB7R6X.js} +6 -6
  75. package/dist/{work-claim-cmd-KR7UR2AB.js → work-claim-cmd-LY4U6QHN.js} +13 -13
  76. package/dist/{work-cmd-JPM37MLF.js → work-cmd-RVRQ32EH.js} +7 -7
  77. package/dist/{work-comment-cmd-3DC6BC34.js → work-comment-cmd-FFUM4TJU.js} +10 -13
  78. package/dist/{work-open-pr-cmd-XZQRJBNZ.js → work-open-pr-cmd-HLDWP4T2.js} +10 -13
  79. package/dist/{work-pr-cmd-WHGZOP22.js → work-pr-cmd-L77K32WK.js} +7 -7
  80. package/package.json +1 -1
  81. package/skill-pack/maintained/emitting-evidence/SKILL.md +8 -2
  82. package/skill-pack/shipped.json +70 -0
  83. package/skill-pack/template/build/SKILL.md +66 -35
  84. package/skill-pack/template/build/references/agent-briefs.md +4 -2
  85. package/skill-pack/template/build/references/release.md +36 -8
  86. package/skill-pack/template/build/references/review-loop.md +6 -2
  87. package/skill-pack/template/build/scripts/record-criteria.mjs +109 -143
  88. package/skill-pack/template/spec/SKILL.md +33 -22
  89. package/skill-pack/template/writing-specs/SKILL.md +36 -3
  90. package/skill-pack/template/writing-specs/references/agents-snippet.md +57 -0
  91. package/skill-pack/template/writing-tests/SKILL.md +117 -0
  92. package/skill-pack/validators/acceptance-criteria.yaml +35 -0
  93. package/skill-pack/validators/code-review.yaml +4 -2
  94. package/dist/chunk-T6OIJQRT.js +0 -31
  95. package/dist/emit-cmd-LXEIDKLT.js +0 -10
  96. package/dist/mcp-install-cmd-XZARXNYT.js +0 -9
  97. package/dist/starter-pack-Z22VRFPF.js +0 -11
  98. package/dist/{chunk-3FMPZPJQ.js → chunk-R7PF5OOU.js} +1 -1
@@ -1,22 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
+ import { checkStarterFiles } from './chunk-MTF3PHVZ.js';
4
+ import { resolveCredentials, getJson, WorkCommandError } from './chunk-SWHQ4LUM.js';
3
5
  import { readStatuslineState, STATE_FRESH_MS } from './chunk-F47JBIAW.js';
4
- import { resolveCredentials, getJson, WorkCommandError } from './chunk-6FH2AHJY.js';
5
- import { isLinkedRepository } from './chunk-C7N4TDHW.js';
6
- import { sanitizeTerminal } from './chunk-JOXYKGUV.js';
7
- import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-3ZPYPFNM.js';
6
+ import { isLinkedRepository } from './chunk-YDSMROTB.js';
7
+ import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
8
+ import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-JXYVA45C.js';
8
9
  import { C } from './chunk-YRYMNBGQ.js';
9
10
  import { readSettings, settingsPath, isOurStatuslineSlot, statuslineWrappedOriginal, REGISTERED_EVENTS, isOurHookCommand, ourCommandBins, cliBinResolvable, ourDirectScriptBins, wrappedOriginalCommand } from './chunk-5QRP3MVS.js';
10
- import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-G5IEVZGI.js';
11
+ import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-LCEXHCHD.js';
11
12
  import { defaultFetch } from './chunk-YO4PSLVB.js';
12
13
  import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
13
14
  import { readCloudConfig } from './chunk-EABW6AJQ.js';
14
- import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-YEQL54G5.js';
15
+ import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-PQUM37BK.js';
15
16
  import { deadHooksState, preparePackageManagerCommand, contextExcludesPresent } from './chunk-XCCLFVXM.js';
16
17
  import { findGitDirs } from './chunk-BJ3KTMDH.js';
17
18
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
18
- import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, accessSync, constants, statSync, readdirSync } from 'fs';
19
- import { join, dirname } from 'path';
19
+ import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, accessSync, constants, statSync, readdirSync, realpathSync } from 'fs';
20
+ import { join, dirname, resolve, delimiter } from 'path';
20
21
  import { execFileSync } from 'child_process';
21
22
 
22
23
  createRequire(import.meta.url);
@@ -113,6 +114,57 @@ function recordStatusline(home, settingsPath2, outcome, original, now = Date.now
113
114
  };
114
115
  });
115
116
  }
117
+ var NAME = "MCP server";
118
+ var SERVER = "outerlayer";
119
+ var NPX_FIX = 'Change the "outerlayer" entry in .mcp.json to run `npx -y @outerlayer/cli mcp serve`, or install the CLI with `npm install -g @outerlayer/cli`.';
120
+ function expand(value, env) {
121
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? "");
122
+ }
123
+ function isExecutableFile(path, platform) {
124
+ try {
125
+ if (!statSync(path).isFile()) return false;
126
+ if (platform !== "win32") accessSync(path, constants.X_OK);
127
+ return true;
128
+ } catch {
129
+ return false;
130
+ }
131
+ }
132
+ function resolveCommand(command, root, env, platform) {
133
+ if (/[\\/]/.test(command)) {
134
+ const path = resolve(root, command);
135
+ return isExecutableFile(path, platform) ? path : void 0;
136
+ }
137
+ const exts = platform === "win32" ? ["", ...(env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
138
+ for (const dir of (env.PATH ?? "").split(delimiter)) {
139
+ for (const ext of exts) {
140
+ const candidate = join(dir, command + ext);
141
+ if (isExecutableFile(candidate, platform)) return candidate;
142
+ }
143
+ }
144
+ return void 0;
145
+ }
146
+ function checkMcpCommand(cwd, env = process.env, platform = process.platform) {
147
+ const root = findGitDirs(cwd ?? process.cwd())?.repoRoot;
148
+ if (root === void 0) return [];
149
+ const path = join(root, ".mcp.json");
150
+ if (!existsSync(path)) return [];
151
+ let entry;
152
+ try {
153
+ entry = JSON.parse(readFileSync(path, "utf8")).mcpServers?.[SERVER];
154
+ } catch {
155
+ return [{ name: NAME, status: "warn", detail: ".mcp.json is not valid JSON, so the agent starts none of its servers", fix: "Fix the JSON syntax in .mcp.json." }];
156
+ }
157
+ const command = entry?.command;
158
+ if (typeof command !== "string") return [];
159
+ const shown = sanitizeTerminal(command);
160
+ const resolved = resolveCommand(expand(command, env), root, env, platform);
161
+ if (resolved === void 0) {
162
+ return [{ name: NAME, status: "warn", detail: `the "outerlayer" server runs \`${shown}\`, which is not on PATH here, so the agent has no OuterLayer tools`, fix: NPX_FIX }];
163
+ }
164
+ return [{ name: NAME, status: "pass", detail: `the "outerlayer" server runs ${sanitizeTerminal(resolved)}` }];
165
+ }
166
+
167
+ // src/doctor.ts
116
168
  var LAPTOP_CHECKS_NOT_APPLICABLE = {
117
169
  name: "Laptop checks",
118
170
  status: "skipped",
@@ -123,6 +175,7 @@ function runDoctor(env = {}) {
123
175
  const now = env.now ?? Date.now;
124
176
  const laptop = env.runnerHost !== true;
125
177
  const transcripts = laptop ? scanTranscripts(home) : void 0;
178
+ const starter = laptop ? checkStarterFiles(env.cwd) : void 0;
126
179
  return [
127
180
  ...laptop ? [checkClaudeDir(home), checkTranscripts(transcripts, now), ...checkHooks(home, env.cwd, transcripts, now)] : [LAPTOP_CHECKS_NOT_APPLICABLE],
128
181
  ...checkDeadHooksState(env.cwd),
@@ -135,6 +188,8 @@ function runDoctor(env = {}) {
135
188
  checkSettingsValid(home, env.cwd),
136
189
  ...laptop ? checkStatusline(home, now, env.cwd) : [],
137
190
  ...checkInstalls(env),
191
+ ...starter?.checks ?? [],
192
+ ...starter !== void 0 && !starter.mcpOutdated ? checkMcpCommand(env.cwd, env.processEnv) : [],
138
193
  ...checkContextSource(home, env.cwd),
139
194
  ...checkInstalledCli(home, env.cwd ?? process.cwd())
140
195
  ];
@@ -462,7 +517,9 @@ function checkDeadHooksState(cwd) {
462
517
  function checkSpoolWritable(home) {
463
518
  const dir = spoolDir(home);
464
519
  try {
465
- if (!existsSync(dir)) return { name: "Spool writable", status: "warn", detail: "spool not created yet", fix: "Run `outerlayer init` (creates the spool on first hook)." };
520
+ if (!existsSync(dir)) {
521
+ return { name: "Spool writable", status: "warn", detail: "spool not created yet", fix: "The first hook run creates it: start a Claude Code session in the repository." };
522
+ }
466
523
  accessSync(dir, constants.W_OK);
467
524
  return { name: "Spool writable", status: "pass", detail: dir };
468
525
  } catch {
@@ -561,10 +618,25 @@ function checkSettingsValid(home, cwd) {
561
618
  }
562
619
  return { name: "Settings JSON valid", status: "pass", detail: "all settings files parse" };
563
620
  }
621
+ function distinctInstalls(paths) {
622
+ const seen = /* @__PURE__ */ new Set();
623
+ const out = [];
624
+ for (const path of paths) {
625
+ let real = path;
626
+ try {
627
+ real = realpathSync(path);
628
+ } catch {
629
+ }
630
+ if (seen.has(real)) continue;
631
+ seen.add(real);
632
+ out.push(path);
633
+ }
634
+ return out;
635
+ }
564
636
  function checkInstalls(env) {
565
637
  let installs;
566
638
  try {
567
- installs = (env.claudeInstalls ?? defaultClaudeInstalls)();
639
+ installs = distinctInstalls((env.claudeInstalls ?? defaultClaudeInstalls)());
568
640
  } catch {
569
641
  return [];
570
642
  }
@@ -708,7 +780,14 @@ async function githubAppChecks(options = {}) {
708
780
  return [{ name: "GitHub App", status: "warn", detail: "the gateway's answer listed no repositories", fix: "Update the outerlayer CLI and the gateway to the same release." }];
709
781
  }
710
782
  if (data.length === 0) {
711
- return [{ name: "GitHub App", status: "pass", detail: "the factory has no connected repositories" }];
783
+ return [
784
+ {
785
+ name: "GitHub App",
786
+ status: "warn",
787
+ detail: "the factory has no connected repositories, so it cannot build or check any work",
788
+ fix: `On the factory's Work page, choose Connect GitHub repository, then Link repository: ${CONNECT_REPOSITORY_DOCS}`
789
+ }
790
+ ];
712
791
  }
713
792
  return data.map(repositoryAccessCheck);
714
793
  }
@@ -1004,7 +1083,7 @@ function doctorExitCode(checks) {
1004
1083
 
1005
1084
  // src/doctor-report.ts
1006
1085
  async function gatherDoctorChecks() {
1007
- const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-MKBX2ZQD.js');
1086
+ const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-N3C6DFQA.js');
1008
1087
  const configPath = defaultRunnerConfigPath();
1009
1088
  const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
1010
1089
  const credential = await credentialChecks();
@@ -1016,7 +1095,7 @@ async function gatherDoctorChecks() {
1016
1095
  const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
1017
1096
  checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
1018
1097
  } else if (hasRunnerBlock(configPath)) {
1019
- const { runnerHostChecks } = await import('./doctor-4FFXTQJ2.js');
1098
+ const { runnerHostChecks } = await import('./doctor-H7REBSLQ.js');
1020
1099
  const hooks = (() => {
1021
1100
  try {
1022
1101
  return loadRunnerConfig(configPath).hooks;
@@ -1048,10 +1127,10 @@ async function gatherDoctorChecks() {
1048
1127
  }
1049
1128
  })();
1050
1129
  const service = await (async () => {
1051
- const { existsSync: existsSync2 } = await import('fs');
1052
- if (process.platform !== "linux" || !existsSync2("/run/systemd/system")) return void 0;
1130
+ const { existsSync: existsSync3 } = await import('fs');
1131
+ if (process.platform !== "linux" || !existsSync3("/run/systemd/system")) return void 0;
1053
1132
  const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
1054
- const { DEFAULT_UNIT_NAME } = await import('./install-VXXI7UR6.js');
1133
+ const { DEFAULT_UNIT_NAME } = await import('./install-HDBIPHTX.js');
1055
1134
  const { dirname: dirname2 } = await import('path');
1056
1135
  return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
1057
1136
  })();
@@ -22273,9 +22273,14 @@ var ArtifactTombstoneRecordSchema = external_exports.looseObject({
22273
22273
  });
22274
22274
  external_exports.union([ArtifactSpoolRecordSchema, ArtifactTombstoneRecordSchema]);
22275
22275
  var EMITTED_RESULTS = ["pass", "fail"];
22276
- var RESERVED_EMIT_NAMES = ["criteria"];
22277
- var EmittedNameSchema = external_exports.string().regex(/^[a-z][a-z0-9._-]{0,63}$/).refine((name) => !RESERVED_EMIT_NAMES.includes(name), {
22278
- message: "is reserved: `outerlayer emit criteria` records a work item's acceptance criteria"
22276
+ var RESERVED_EMIT_NAMES = ["artifact", "criteria", "finding", "findings"];
22277
+ var EmittedNameSchema = external_exports.string().regex(/^[a-z][a-z0-9._-]{0,63}$/).superRefine((name, ctx) => {
22278
+ if (RESERVED_EMIT_NAMES.includes(name)) {
22279
+ ctx.addIssue({
22280
+ code: "custom",
22281
+ message: `is reserved: \`outerlayer emit ${name}\` is a subcommand, so a check cannot be recorded under that name`
22282
+ });
22283
+ }
22279
22284
  });
22280
22285
  var EMITTED_RESULT_MAX_LINK_LENGTH = 500;
22281
22286
  var EMITTED_RESULT_MAX_BODY_LENGTH = 2e3;
@@ -22292,6 +22297,7 @@ var CriterionSchema = external_exports.object({
22292
22297
  proof: external_exports.enum(ARTIFACT_KINDS).nullable()
22293
22298
  }).strict();
22294
22299
  var CriteriaArraySchema = external_exports.array(CriterionSchema).min(1).max(CRITERIA_MAX);
22300
+ var DraftedSchema = external_exports.boolean().optional();
22295
22301
  function refuseDuplicateIds(criteria, ctx) {
22296
22302
  const seen = /* @__PURE__ */ new Set();
22297
22303
  criteria.forEach((criterion, index) => {
@@ -22305,9 +22311,10 @@ function refuseDuplicateIds(criteria, ctx) {
22305
22311
  seen.add(criterion.id);
22306
22312
  });
22307
22313
  }
22308
- var CriteriaListSchema = external_exports.object({ criteria: CriteriaArraySchema }).strict().superRefine((list, ctx) => refuseDuplicateIds(list.criteria, ctx));
22314
+ var CriteriaListSchema = external_exports.object({ criteria: CriteriaArraySchema, drafted: DraftedSchema }).strict().superRefine((list, ctx) => refuseDuplicateIds(list.criteria, ctx));
22309
22315
  external_exports.object({
22310
22316
  criteria: CriteriaArraySchema,
22317
+ drafted: DraftedSchema,
22311
22318
  item: external_exports.object({ number: external_exports.number().int().positive() }).optional(),
22312
22319
  sessionId: external_exports.string().optional()
22313
22320
  }).superRefine((request, ctx) => refuseDuplicateIds(request.criteria, ctx));
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { autoConnect } from './chunk-GATMPAMK.js';
3
+ import { autoConnect } from './chunk-NPQWKGUA.js';
4
4
  import { detectActiveSession } from './chunk-IP7BIBXJ.js';
5
- import { resolveRepoIdentity } from './chunk-3ZPYPFNM.js';
5
+ import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
6
+ import { C } from './chunk-YRYMNBGQ.js';
6
7
  import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
7
8
  import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
8
9
  import { defaultFetch } from './chunk-YO4PSLVB.js';
@@ -12,9 +13,6 @@ import { homeFromEnv } from './chunk-VNVZDWO3.js';
12
13
  createRequire(import.meta.url);
13
14
 
14
15
  // src/work-pr-cmd.ts
15
- var GREEN = "\x1B[32m";
16
- var DIM = "\x1B[2m";
17
- var RESET = "\x1B[0m";
18
16
  var WorkPrCommandError = class extends Error {
19
17
  };
20
18
  function resolveCredentials(opts) {
@@ -43,7 +41,7 @@ function sessionNotOnItemMessage(error, sessionId) {
43
41
  }
44
42
  function itemLabel(data) {
45
43
  if (data.workItemNumber == null) return data.workItemId;
46
- return data.workItemTitle ? `item ${data.workItemNumber} ${DIM}\xB7${RESET} ${data.workItemTitle}` : `item ${data.workItemNumber}`;
44
+ return data.workItemTitle ? `item ${data.workItemNumber} ${C.dim}\xB7${C.reset} ${data.workItemTitle}` : `item ${data.workItemNumber}`;
47
45
  }
48
46
  async function runWorkPrWithdraw(opts, ctx) {
49
47
  const itemNumber = parseTrackerNumber(opts.item);
@@ -88,7 +86,7 @@ async function runWorkPrWithdraw(opts, ctx) {
88
86
  throw new WorkPrCommandError(json.error?.message ?? `request failed (${response.status})`);
89
87
  }
90
88
  const data = json.data;
91
- const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} withdrawn ${DIM}\xB7${RESET} ${data.repository}#${data.prNumber} ${DIM}from${RESET} item ${itemNumber}`;
89
+ const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} withdrawn ${C.dim}\xB7${C.reset} ${data.repository}#${data.prNumber} ${C.dim}from${C.reset} item ${itemNumber}`;
92
90
  if (!opts.quiet) process.stdout.write(output + "\n");
93
91
  return { data, output };
94
92
  }
@@ -170,7 +168,7 @@ async function runWorkPr(opts) {
170
168
  }
171
169
  const data = json.data;
172
170
  const verb = data.created ? "declared" : "already declared";
173
- const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} ${verb} ${DIM}\xB7${RESET} ${data.repository}#${data.prNumber} ${DIM}\u2192${RESET} ${itemLabel(data)}`;
171
+ const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} ${verb} ${C.dim}\xB7${C.reset} ${data.repository}#${data.prNumber} ${C.dim}\u2192${C.reset} ${itemLabel(data)}`;
174
172
  if (!opts.quiet) process.stdout.write(output + "\n");
175
173
  return { data, output };
176
174
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { EVENT_TYPES, isHumanUserTurn, parseAgentSession, SCHEMA_VERSION, SPEED_MAX_LENGTH, inputBandOf, BUCKET_MODEL_MAX_LENGTH } from './chunk-325272HM.js';
3
+ import { EVENT_TYPES, isHumanUserTurn, parseAgentSession, SCHEMA_VERSION, SPEED_MAX_LENGTH, inputBandOf, BUCKET_MODEL_MAX_LENGTH } from './chunk-J5M7BSJQ.js';
4
4
  import { createHash } from 'crypto';
5
5
  import { readFileSync, readdirSync, statSync, mkdirSync, copyFileSync, renameSync, unlinkSync, writeFileSync, stat as stat$1, unwatchFile, watchFile, watch as watch$1 } from 'fs';
6
6
  import { type, homedir } from 'os';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { MIN_ENGINE_MAJOR } from './chunk-SKHN72JO.js';
3
+ import { MIN_ENGINE_MAJOR } from './chunk-RBKIH5ST.js';
4
4
  import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-CX22S5BA.js';
5
5
  import { spawnSync } from 'child_process';
6
6
  import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { LiveUploader } from './chunk-27Y7AX2O.js';
4
- import { CaptureDaemon } from './chunk-3ZPYPFNM.js';
3
+ import { LiveUploader } from './chunk-MSASO5O4.js';
4
+ import { CaptureDaemon } from './chunk-JXYVA45C.js';
5
5
  import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
6
- import { spoolDir } from './chunk-YEQL54G5.js';
6
+ import { spoolDir } from './chunk-PQUM37BK.js';
7
7
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
8
8
  import { mkdirSync, writeFileSync, rmSync } from 'fs';
9
9
  import { join } from 'path';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { RecipeError } from './chunk-QOZKPLVJ.js';
4
- import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-QEESCZ26.js';
4
+ import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-4NPZVMJU.js';
5
5
  import { spawn } from 'child_process';
6
6
  import { existsSync, mkdirSync, createWriteStream, rmSync, readFileSync } from 'fs';
7
7
  import { join, dirname } from 'path';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_PIN_FILENAME } from './chunk-AHZQBGD2.js';
3
+ import { CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_PIN_FILENAME } from './chunk-2M63GG56.js';
4
4
  import { readFileSync, existsSync, writeFileSync, mkdirSync, utimesSync, statSync, unlinkSync } from 'fs';
5
5
  import { join, dirname } from 'path';
6
6
 
@@ -4,17 +4,17 @@ import { LaunchFilter } from './chunk-HHJDN6AU.js';
4
4
  import { RepoFilter } from './chunk-TBT347UY.js';
5
5
  import { hookExecSpoolPath } from './chunk-F7CU5ABH.js';
6
6
  import { applyReportedCost } from './chunk-BKO6JEZI.js';
7
- import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, acquireArtifactSpoolLock, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-JOXYKGUV.js';
8
- import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-3ZPYPFNM.js';
7
+ import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, acquireArtifactSpoolLock, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-WUYTWNMA.js';
8
+ import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-JXYVA45C.js';
9
9
  import { C } from './chunk-YRYMNBGQ.js';
10
10
  import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-GQD22KKN.js';
11
11
  import { readLatestProvenanceForSession, readContextProvenance } from './chunk-ZJSNLVCE.js';
12
12
  import { defaultFetch } from './chunk-YO4PSLVB.js';
13
13
  import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-PLMTLGYH.js';
14
- import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-325272HM.js';
14
+ import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-J5M7BSJQ.js';
15
15
  import { readCloudConfig, cloudConfigPath, writeCloudConfig } from './chunk-EABW6AJQ.js';
16
16
  import { signerFor, signRunnerRequest } from './chunk-V7NIVOFD.js';
17
- import { writeSyncStatus, spoolDir } from './chunk-YEQL54G5.js';
17
+ import { writeSyncStatus, spoolDir } from './chunk-PQUM37BK.js';
18
18
  import { removeJournalHooks } from './chunk-XCCLFVXM.js';
19
19
  import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
20
20
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
@@ -928,6 +928,21 @@ var NEVER_REACHED_SERVER_ERROR_CODES = /* @__PURE__ */ new Set([
928
928
  "payload_too_large",
929
929
  "unreadable_test_results"
930
930
  ]);
931
+ async function readUploadedIdentity(response) {
932
+ let data;
933
+ try {
934
+ data = (await response.json()).data;
935
+ } catch {
936
+ }
937
+ return {
938
+ id: typeof data?.id === "string" ? data.id : null,
939
+ url: typeof data?.url === "string" ? data.url : null
940
+ };
941
+ }
942
+ function uploadedArtifactLine(artifact) {
943
+ const where = artifact.url !== null ? sanitizeTerminal(artifact.url) : artifact.id !== null ? `id ${sanitizeTerminal(artifact.id)} ${C.dim}(the gateway returned no link)${C.reset}` : `${C.dim}(the gateway returned no link)${C.reset}`;
944
+ return ` ${C.green}\u2191${C.reset} ${sanitizeTerminal(artifact.filename)} ${C.dim}\xB7${C.reset} ${sanitizeTerminal(artifact.caption)} ${C.dim}\xB7${C.reset} ${where}`;
945
+ }
931
946
  function splitPayload(payload) {
932
947
  const mid = Math.ceil(payload.sessions.length / 2);
933
948
  const first = payload.sessions.slice(0, mid);
@@ -1553,10 +1568,12 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1553
1568
  }
1554
1569
  let artifactsUploaded;
1555
1570
  let artifactsHeld;
1571
+ let uploadedArtifacts;
1556
1572
  const artifactLock = combinedRecords.length > 0 || artifactPlan.held.length > 0 ? await acquireArtifactSpoolLock(home, { waitMs: opts.artifactLockWaitMs }) : void 0;
1557
1573
  if (artifactLock !== void 0 && !artifactLock.ok) {
1558
1574
  artifactsUploaded = 0;
1559
1575
  artifactsHeld = combinedRecords.length + artifactPlan.held.length;
1576
+ uploadedArtifacts = [];
1560
1577
  if (!opts.quiet) {
1561
1578
  process.stderr.write(
1562
1579
  `${C.yellow}!${C.reset} artifacts not attempted: ${artifactLock.reason} \u2014 ${artifactsHeld} record(s) stay queued for the next sync
@@ -1575,6 +1592,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1575
1592
  let uploadedCount = 0;
1576
1593
  let heldCount = livePlan.held.length;
1577
1594
  let droppedCount = 0;
1595
+ const uploadedThisRun = [];
1578
1596
  const warn = (message) => {
1579
1597
  if (!opts.quiet) process.stderr.write(`${C.yellow}!${C.reset} ${message}
1580
1598
  `);
@@ -1651,6 +1669,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1651
1669
  let status;
1652
1670
  let errorCode = "";
1653
1671
  let serverMessage = "";
1672
+ let accepted;
1654
1673
  try {
1655
1674
  const response = await fetchImpl(artifactEndpoint, {
1656
1675
  method: "POST",
@@ -1670,19 +1689,31 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1670
1689
  if (typeof body.error?.message === "string") serverMessage = body.error.message;
1671
1690
  } catch {
1672
1691
  }
1692
+ } else {
1693
+ accepted = response;
1673
1694
  }
1674
1695
  } catch (err) {
1675
1696
  failure = String(err);
1676
1697
  }
1677
- if (!failure) {
1698
+ if (accepted !== void 0) {
1699
+ const identity = await readUploadedIdentity(accepted);
1678
1700
  state.set(record.artifactId, {
1679
1701
  state: "uploaded",
1680
1702
  attempts: (existing?.attempts ?? 0) + 1,
1681
1703
  firstSeen: firstSeenIso,
1682
1704
  sha256: record.sha256,
1683
- reachedServer: true
1705
+ reachedServer: true,
1706
+ ...identity.id !== null ? { serverId: identity.id } : {},
1707
+ ...identity.url !== null ? { url: identity.url } : {}
1684
1708
  });
1685
1709
  uploadedCount += 1;
1710
+ uploadedThisRun.push({
1711
+ artifactId: record.artifactId,
1712
+ filename: record.filename,
1713
+ caption: record.caption,
1714
+ id: identity.id,
1715
+ url: identity.url
1716
+ });
1686
1717
  continue;
1687
1718
  }
1688
1719
  if (status === 401) {
@@ -1739,11 +1770,15 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1739
1770
  artifactsUploaded = uploadedCount;
1740
1771
  artifactsHeld = heldCount;
1741
1772
  artifactsDropped = droppedCount;
1773
+ uploadedArtifacts = uploadedThisRun;
1742
1774
  if (!opts.quiet) {
1743
1775
  process.stderr.write(
1744
1776
  `${C.dim}artifacts${C.reset} ${uploadedCount} uploaded \xB7 ${heldCount} held \xB7 ${droppedCount} dropped
1745
1777
  `
1746
1778
  );
1779
+ if (!opts.json) {
1780
+ for (const uploaded of uploadedThisRun) process.stdout.write(uploadedArtifactLine(uploaded) + "\n");
1781
+ }
1747
1782
  }
1748
1783
  }
1749
1784
  const persisted = writeArtifactSpoolState(state, home);
@@ -1776,6 +1811,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1776
1811
  );
1777
1812
  }
1778
1813
  }
1814
+ const artifactReport = artifactsUploaded !== void 0 ? { artifactsUploaded, artifactsHeld, uploadedArtifacts } : {};
1779
1815
  let output;
1780
1816
  if (opts.json) {
1781
1817
  output = JSON.stringify({
@@ -1787,7 +1823,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1787
1823
  artifactsDropped,
1788
1824
  tier,
1789
1825
  url,
1790
- ...artifactsUploaded !== void 0 ? { artifactsUploaded, artifactsHeld } : {}
1826
+ ...artifactReport
1791
1827
  });
1792
1828
  } else {
1793
1829
  const lines = [];
@@ -1826,7 +1862,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
1826
1862
  url,
1827
1863
  dryRun: false,
1828
1864
  watermarkMs,
1829
- ...artifactsUploaded !== void 0 ? { artifactsUploaded, artifactsHeld } : {},
1865
+ ...artifactReport,
1830
1866
  output
1831
1867
  };
1832
1868
  }
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { mcpTargetPath, INIT_STDIO_ENTRY, INIT_DOCS_ENTRY, DOCS_MCP_SERVER_NAME } from './chunk-XCK2YAY5.js';
4
+ import { POLICY_CONTENT, VALIDATOR_FILES, VALIDATORS_DIR, POLICY_PATH } from './chunk-FOJGZYFC.js';
5
+ import { pinExists, readContextManifest } from './chunk-LCEXHCHD.js';
6
+ import { resolvePackRoot, loadTemplateFiles, skillDir } from './chunk-ZA5CHAFY.js';
7
+ import { findGitDirs } from './chunk-BJ3KTMDH.js';
8
+ import { createHash } from 'crypto';
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { join } from 'path';
11
+
12
+ createRequire(import.meta.url);
13
+ var SHIPPED_MANIFEST = "shipped.json";
14
+ function currentStarterFiles(packRoot = resolvePackRoot(), policy = POLICY_CONTENT) {
15
+ const templates = loadTemplateFiles(packRoot).map((f) => ({
16
+ path: f.path,
17
+ content: f.content,
18
+ skill: f.skill,
19
+ source: join(packRoot, "template", f.skill, f.path.slice(skillDir(f.skill).length + 1))
20
+ }));
21
+ const validators = VALIDATOR_FILES.map(({ name }) => {
22
+ const source = join(packRoot, "validators", name);
23
+ return { path: `${VALIDATORS_DIR}/${name}`, content: readFileSync(source, "utf8"), source };
24
+ });
25
+ return [...templates, ...validators, { path: POLICY_PATH, content: policy }];
26
+ }
27
+ function contentHash(content) {
28
+ return createHash("sha256").update(content.replace(/\r\n/g, "\n")).digest("hex");
29
+ }
30
+ function readShippedManifest(packRoot = resolvePackRoot()) {
31
+ const path = join(packRoot, SHIPPED_MANIFEST);
32
+ if (!existsSync(path)) return { ok: false, problem: "missing" };
33
+ try {
34
+ const doc = JSON.parse(readFileSync(path, "utf8"));
35
+ if (doc.schemaVersion !== 1 || doc.files === null || typeof doc.files !== "object" || Array.isArray(doc.files)) {
36
+ return { ok: false, problem: "unreadable" };
37
+ }
38
+ const files = /* @__PURE__ */ new Map();
39
+ for (const [file, hashes] of Object.entries(doc.files)) {
40
+ if (!Array.isArray(hashes) || !hashes.every((h) => typeof h === "string" && /^[0-9a-f]{64}$/.test(h))) {
41
+ return { ok: false, problem: "unreadable" };
42
+ }
43
+ files.set(file, new Set(hashes));
44
+ }
45
+ return { ok: true, files };
46
+ } catch {
47
+ return { ok: false, problem: "unreadable" };
48
+ }
49
+ }
50
+ function mcpItemsInit() {
51
+ return [
52
+ // Through 0.12.0 `init` wrote a command that needs a global install on every teammate's machine.
53
+ { name: "outerlayer", current: INIT_STDIO_ENTRY, shipped: [{ type: "stdio", command: "outerlayer", args: ["mcp", "serve"] }] },
54
+ { name: DOCS_MCP_SERVER_NAME, current: INIT_DOCS_ENTRY, shipped: [] }
55
+ ];
56
+ }
57
+ function canonical(value) {
58
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
59
+ if (value !== null && typeof value === "object") {
60
+ const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
61
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}`;
62
+ }
63
+ return JSON.stringify(value);
64
+ }
65
+ function fileState(root, file, manifest) {
66
+ const abs = join(root, file.path);
67
+ if (!existsSync(abs)) return "missing";
68
+ const hash = contentHash(readFileSync(abs, "utf8"));
69
+ if (hash === contentHash(file.content)) return "current";
70
+ if (manifest.ok && manifest.files.get(file.path)?.has(hash) === true) return "outdated";
71
+ return "edited";
72
+ }
73
+ function mcpItems(root) {
74
+ const path = mcpTargetPath(root);
75
+ let servers;
76
+ let unreadable = false;
77
+ if (existsSync(join(root, path))) {
78
+ try {
79
+ const doc = JSON.parse(readFileSync(join(root, path), "utf8"));
80
+ const value = doc.mcpServers;
81
+ if (value !== void 0 && (value === null || typeof value !== "object" || Array.isArray(value))) unreadable = true;
82
+ else servers = value ?? {};
83
+ } catch {
84
+ unreadable = true;
85
+ }
86
+ }
87
+ return mcpItemsInit().map((item) => {
88
+ const base = { kind: "mcp", path, server: item.name, entry: item.current };
89
+ if (unreadable) return { ...base, state: "unreadable" };
90
+ if (servers === void 0 || !(item.name in servers)) return { ...base, state: "missing" };
91
+ const have = canonical(servers[item.name]);
92
+ if (have === canonical(item.current)) return { ...base, state: "current" };
93
+ if (item.shipped.some((entry) => canonical(entry) === have)) return { ...base, state: "outdated" };
94
+ return { ...base, state: "edited" };
95
+ });
96
+ }
97
+ function inspectStarterFiles(root, opts = {}) {
98
+ const packRoot = opts.packRoot ?? resolvePackRoot();
99
+ const manifest = readShippedManifest(packRoot);
100
+ const files = currentStarterFiles(packRoot).map(
101
+ (file) => ({ kind: "file", path: file.path, state: fileState(root, file, manifest), file })
102
+ );
103
+ return { items: [...files, ...mcpItems(root)], manifest: manifest.ok ? "ok" : manifest.problem };
104
+ }
105
+ function itemLabel(item) {
106
+ return item.kind === "file" ? item.path : `${item.path} "${item.server}" server`;
107
+ }
108
+ function contextIsGoverned(root, gitCommonDir) {
109
+ return pinExists(root) || readContextManifest(gitCommonDir) !== void 0;
110
+ }
111
+ var NAME = "Starter files";
112
+ var UPDATE = "outerlayer init --update";
113
+ function shownPaths(items, all) {
114
+ const shown = [];
115
+ const whole = /* @__PURE__ */ new Set();
116
+ for (const item of items) {
117
+ const skill = item.kind === "file" ? item.file.skill : void 0;
118
+ if (skill !== void 0) {
119
+ const ofSkill = all.filter((i) => i.kind === "file" && i.file.skill === skill);
120
+ if (ofSkill.every((i) => items.includes(i))) {
121
+ if (!whole.has(skill)) shown.push(`${skillDir(skill)}/`);
122
+ whole.add(skill);
123
+ continue;
124
+ }
125
+ }
126
+ shown.push(itemLabel(item));
127
+ }
128
+ return shown;
129
+ }
130
+ function checkStarterFiles(cwd, opts = {}) {
131
+ const none = { checks: [], mcpOutdated: false };
132
+ const dirs = findGitDirs(cwd ?? process.cwd());
133
+ if (dirs === void 0 || contextIsGoverned(dirs.repoRoot, dirs.gitCommonDir)) return none;
134
+ const report = inspectStarterFiles(dirs.repoRoot, opts);
135
+ if (report.items.every((i) => i.kind === "mcp" || i.state === "missing")) return none;
136
+ const by = (state) => report.items.filter((i) => i.state === state);
137
+ const current = by("current");
138
+ const outdated = by("outdated");
139
+ const missing = by("missing");
140
+ const edited = by("edited");
141
+ const unreadable = by("unreadable");
142
+ const parts = [];
143
+ if (current.length > 0) parts.push(`${current.length} current`);
144
+ if (outdated.length > 0) parts.push(`outdated and unedited: ${shownPaths(outdated, report.items).join(", ")}`);
145
+ if (missing.length > 0) parts.push(`missing: ${shownPaths(missing, report.items).join(", ")}`);
146
+ if (edited.length > 0) parts.push(`edited by you, left as they are: ${edited.map(itemLabel).join(", ")}`);
147
+ if (unreadable.length > 0) parts.push(`${unreadable[0].path} is not valid JSON, so its servers were not read`);
148
+ if (report.manifest !== "ok") {
149
+ parts.push(`this CLI's record of earlier versions is ${report.manifest}, so an outdated file cannot be told from an edited one`);
150
+ }
151
+ const mcpOutdated = outdated.some((i) => i.kind === "mcp" && i.server === "outerlayer");
152
+ const actionable = outdated.length + missing.length > 0;
153
+ const compare = edited.length > 0 ? ` Compare an edited file with this CLI's version: \`${UPDATE} --dry-run\`.` : "";
154
+ if (!actionable && report.manifest === "ok") {
155
+ return { checks: [{ name: NAME, status: "pass", detail: parts.join("; ") + (compare === "" ? "" : `.${compare}`) }], mcpOutdated };
156
+ }
157
+ const fix = actionable ? `Run \`${UPDATE}\` to update the outdated files and write the missing ones. It never changes a file you edited.${compare}` : "Reinstall the CLI: `npm install -g @outerlayer/cli`, or run it with `npx -y @outerlayer/cli`.";
158
+ return { checks: [{ name: NAME, status: "warn", detail: parts.join("; "), fix }], mcpOutdated };
159
+ }
160
+
161
+ export { canonical, checkStarterFiles, contextIsGoverned, inspectStarterFiles };