@outerlayer/cli 0.2.0 → 0.4.1

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 (141) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/README.md +229 -60
  3. package/dist/agent-setup-IFH3FB5X.js +197 -0
  4. package/dist/build-TZUUTRGH.js +9 -0
  5. package/dist/build-info.json +1 -1
  6. package/dist/check-7MFH6HNC.js +94 -0
  7. package/dist/chunk-3TFPDVTI.js +94 -0
  8. package/dist/{chunk-4TNZMO7V.js → chunk-4C4THABW.js} +5566 -754
  9. package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
  10. package/dist/chunk-5Y3QQRUH.js +300 -0
  11. package/dist/chunk-774HEQL6.js +178 -0
  12. package/dist/{work-pr-cmd-AZQPWH4H.js → chunk-7WERLFVR.js} +9 -26
  13. package/dist/chunk-ABIWDUMS.js +104 -0
  14. package/dist/chunk-AUFG23AA.js +534 -0
  15. package/dist/chunk-B2G7JHHB.js +42 -0
  16. package/dist/chunk-BCJNHMZT.js +34 -0
  17. package/dist/chunk-BCLJSQCV.js +56 -0
  18. package/dist/chunk-BFESLKPP.js +61 -0
  19. package/dist/chunk-BJ3KTMDH.js +63 -0
  20. package/dist/chunk-BKO6JEZI.js +47 -0
  21. package/dist/chunk-BOLTI6LR.js +37 -0
  22. package/dist/chunk-CBPPJSAR.js +89 -0
  23. package/dist/chunk-DM3VFDS3.js +1461 -0
  24. package/dist/{chunk-65WXAANR.js → chunk-DVDEBNQJ.js} +17 -2
  25. package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
  26. package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
  27. package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
  28. package/dist/chunk-F6GFZXZ3.js +60 -0
  29. package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
  30. package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
  31. package/dist/chunk-HZRNMGLF.js +2233 -0
  32. package/dist/chunk-LF6MLJCY.js +38 -0
  33. package/dist/{chunk-U32VSRLO.js → chunk-LT5TZHYH.js} +95 -634
  34. package/dist/chunk-LYCDNMOH.js +83 -0
  35. package/dist/chunk-M5POMKKW.js +1051 -0
  36. package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
  37. package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
  38. package/dist/chunk-N6LURUEF.js +64 -0
  39. package/dist/chunk-OGFGZQA3.js +23 -0
  40. package/dist/chunk-QDQEUVUF.js +9 -0
  41. package/dist/chunk-QOZKPLVJ.js +226 -0
  42. package/dist/chunk-QY22NZUW.js +1764 -0
  43. package/dist/chunk-RA3O54FC.js +30 -0
  44. package/dist/chunk-RFUPN6KX.js +71 -0
  45. package/dist/chunk-SY4GK4SP.js +55 -0
  46. package/dist/chunk-TBT347UY.js +41 -0
  47. package/dist/chunk-TWNWNS2Q.js +1084 -0
  48. package/dist/{chunk-NXLLURA4.js → chunk-UFFXNXLQ.js} +115 -71
  49. package/dist/chunk-USO2DKBD.js +421 -0
  50. package/dist/chunk-W4SQCOZU.js +206 -0
  51. package/dist/{chunk-Q6CL5THG.js → chunk-W5FVUZXV.js} +208 -61
  52. package/dist/chunk-WFJ65NUD.js +383 -0
  53. package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
  54. package/dist/chunk-WO2BXCTQ.js +83 -0
  55. package/dist/{chunk-I3ETLSNE.js → chunk-XCCLFVXM.js} +120 -11
  56. package/dist/{context-materialize-6WKT3RBQ.js → chunk-XDDW4FRS.js} +134 -26
  57. package/dist/chunk-Y7KJLXYN.js +232 -0
  58. package/dist/chunk-YFHIIH4B.js +102 -0
  59. package/dist/chunk-YYYXRJUW.js +101 -0
  60. package/dist/chunk-ZM2IMMYN.js +76 -0
  61. package/dist/chunk-ZMYPLFG3.js +71 -0
  62. package/dist/chunk-ZNA27WEV.js +470 -0
  63. package/dist/{cli-G7DILYJY.js → cli-W62AFRSW.js} +616 -878
  64. package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
  65. package/dist/config-XVJYZ4IQ.js +9 -0
  66. package/dist/connect-cmd-ZTJONP37.js +16 -0
  67. package/dist/context-adopt-UMA4O5NY.js +105 -0
  68. package/dist/context-materialize-G2FUFC72.js +19 -0
  69. package/dist/dist-44CQVWGT.js +6 -0
  70. package/dist/docker-NEGDLU6D.js +7 -0
  71. package/dist/doctor-53F2PYY7.js +43 -0
  72. package/dist/doctor-ZLGZYMDK.js +426 -0
  73. package/dist/{emit-artifact-cmd-UQVT3OKW.js → emit-artifact-cmd-AKZP7TMF.js} +87 -23
  74. package/dist/emit-cmd-DCERAU27.js +9 -0
  75. package/dist/emit-criteria-cmd-7MXQVCKD.js +166 -0
  76. package/dist/{emit-finding-cmd-FDOU2OPD.js → emit-finding-cmd-N2FTGCVC.js} +34 -31
  77. package/dist/{emit-result-cmd-H2T4C2K3.js → emit-result-cmd-4CPDBMN2.js} +34 -62
  78. package/dist/exec-client-4XGXV3XN.js +8 -0
  79. package/dist/guest-init-GENRHNP7.js +8 -0
  80. package/dist/hook-fast-EYENPK6F.js +9 -0
  81. package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
  82. package/dist/host-key-KDYZ5ECC.js +7 -0
  83. package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
  84. package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
  85. package/dist/index.js +4 -4
  86. package/dist/init-55Y4LHWF.js +36 -0
  87. package/dist/init-XXEKWZQP.js +221 -0
  88. package/dist/init-cmd-WLST2F7G.js +135 -0
  89. package/dist/install-cmd-7WXOIFDO.js +55 -0
  90. package/dist/lima-XN65D7GN.js +55 -0
  91. package/dist/login-browser-7XGSV7MA.js +10 -0
  92. package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
  93. package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
  94. package/dist/loop-JFIBP4B7.js +43 -0
  95. package/dist/machine-ZBPT2J3R.js +35 -0
  96. package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
  97. package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-N6UD2KBX.js} +20 -9
  98. package/dist/paths-OYKMVYJP.js +6 -0
  99. package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
  100. package/dist/real-deps-SU24ZA2K.js +21 -0
  101. package/dist/relay-L76HDX72.js +46 -0
  102. package/dist/settings-J2652U5N.js +7 -0
  103. package/dist/starter-pack-LIZYMKYQ.js +10 -0
  104. package/dist/{status-I27IST4L.js → status-UJXWZRN5.js} +30 -13
  105. package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
  106. package/dist/sync-cmd-4G4A7EVN.js +28 -0
  107. package/dist/version-BWM6VLDI.js +6 -0
  108. package/dist/{watch-3DTPJETH.js → watch-7SKJKGAA.js} +19 -8
  109. package/dist/{work-claim-cmd-5AUNCLS2.js → work-claim-cmd-5F7VZ42N.js} +34 -21
  110. package/dist/work-cmd-QAUZ7MTD.js +17 -0
  111. package/dist/work-comment-cmd-BNCSBTLH.js +106 -0
  112. package/dist/{work-launch-LT663PB3.js → work-launch-YI4CJDAP.js} +1 -1
  113. package/dist/work-open-pr-cmd-VFYZHIIW.js +156 -0
  114. package/dist/work-pr-cmd-PV32ZLSI.js +16 -0
  115. package/package.json +13 -3
  116. package/skill-pack/maintained/amend/SKILL.md +104 -0
  117. package/skill-pack/maintained/emitting-evidence/SKILL.md +108 -0
  118. package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
  119. package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
  120. package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
  121. package/skill-pack/template/build/SKILL.md +193 -0
  122. package/skill-pack/template/build/references/agent-briefs.md +243 -0
  123. package/skill-pack/template/build/references/criteria-judge.md +91 -0
  124. package/skill-pack/template/build/references/evidence.md +42 -0
  125. package/skill-pack/template/build/references/release.md +74 -0
  126. package/skill-pack/template/build/references/review-briefs.md +275 -0
  127. package/skill-pack/template/build/references/review-loop.md +158 -0
  128. package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
  129. package/skill-pack/template/spec/SKILL.md +84 -0
  130. package/skill-pack/template/writing-specs/SKILL.md +134 -0
  131. package/dist/chunk-DCNOXRMV.js +0 -589
  132. package/dist/chunk-JJP7YLMN.js +0 -25
  133. package/dist/chunk-OZ7C3XUE.js +0 -34
  134. package/dist/chunk-WQ6VGRGZ.js +0 -150
  135. package/dist/hook-fast-J5LCDHUJ.js +0 -8
  136. package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
  137. package/dist/init-PTBITAUO.js +0 -103
  138. package/dist/login-cmd-IRX6LZT7.js +0 -62
  139. package/dist/loop-ASZRX3CZ.js +0 -648
  140. package/dist/sync-cmd-BBAUZ5JD.js +0 -17
  141. package/dist/work-cmd-2P4BVX47.js +0 -18
@@ -5,7 +5,7 @@ import { mkdirSync, appendFileSync, readFileSync } from 'fs';
5
5
  import { join } from 'path';
6
6
 
7
7
  createRequire(import.meta.url);
8
- var HOW_TO_CREATE_AN_ITEM = "outerlayer work add";
8
+ var HOW_TO_CREATE_AN_ITEM = "outerlayer work build --local";
9
9
  function parseWorkItemEnv(raw) {
10
10
  if (raw === void 0) return { reason: "OUTERLAYER_WORK is not set" };
11
11
  const value = raw.trim();
@@ -58,6 +58,21 @@ function launchedItemForSession(sessionId, home, readImpl = readLaunchSpool) {
58
58
  }
59
59
  return found;
60
60
  }
61
+ function launchedItemsBySession(home, readImpl = readLaunchSpool) {
62
+ const items = /* @__PURE__ */ new Map();
63
+ const read = readImpl(launchesPath(home));
64
+ if (read.kind !== "read") return items;
65
+ for (const line of read.text.split("\n")) {
66
+ try {
67
+ const record = JSON.parse(line);
68
+ if (typeof record.sessionId === "string" && typeof record.item === "number") {
69
+ items.set(record.sessionId, record.item);
70
+ }
71
+ } catch {
72
+ }
73
+ }
74
+ return items;
75
+ }
61
76
  function readLaunchSpoolState(home, readImpl = readLaunchSpool) {
62
77
  const ids = /* @__PURE__ */ new Set();
63
78
  const read = readImpl(launchesPath(home));
@@ -78,4 +93,4 @@ function readLaunchSpoolState(home, readImpl = readLaunchSpool) {
78
93
  return { ids, ...malformedLines > 0 ? { malformedLines } : {} };
79
94
  }
80
95
 
81
- export { appendLaunchRecord, launchStatusIsDecisive, launchedItemForSession, launchesPath, parseWorkItemEnv, readLaunchSpoolState };
96
+ export { appendLaunchRecord, launchStatusIsDecisive, launchedItemForSession, launchedItemsBySession, launchesPath, parseWorkItemEnv, readLaunchSpoolState };
@@ -30,5 +30,16 @@ function writeCloudConfig(config, home = homeFromEnv()) {
30
30
  } catch {
31
31
  }
32
32
  }
33
+ function removeCloudConfigKeys(keys, home = homeFromEnv()) {
34
+ const path = cloudConfigPath(home);
35
+ if (!existsSync(path)) return;
36
+ const stored = { ...readCloudConfig(home) };
37
+ for (const key of keys) delete stored[key];
38
+ writeFileSync(path, JSON.stringify(stored, null, 2) + "\n", { mode: 384 });
39
+ try {
40
+ chmodSync(path, 384);
41
+ } catch {
42
+ }
43
+ }
33
44
 
34
- export { cloudConfigPath, readCloudConfig, writeCloudConfig };
45
+ export { cloudConfigPath, readCloudConfig, removeCloudConfigKeys, writeCloudConfig };
@@ -7975,4 +7975,4 @@ function normalizeRepoName(repo) {
7975
7975
  var CONTEXT_PIN_FILENAME = ".outerlayer-pin.json";
7976
7976
  var CONTEXT_PIN_SCHEMA_VERSION = 1;
7977
7977
 
7978
- export { CONTEXT_PIN_FILENAME, CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_SOURCE_CONFIG_PATH, classifyTree, emitTree, generatedHeaderSource, normalizePath, normalizeRepoName, parseGoverns, parseOuterlayerConfig };
7978
+ export { CONTEXT_PIN_FILENAME, CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_SOURCE_CONFIG_PATH, classifyTree, emitTree, generatedHeaderSource, normalizePath, normalizeRepoName, parseGoverns, parseOuterlayerConfig, splitRawFile };
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { decodeWrappedCommand } from './chunk-R5KBGVII.js';
3
+ import { recordReportedCost } from './chunk-BKO6JEZI.js';
4
+ import { decodeWrappedCommand } from './chunk-5QRP3MVS.js';
4
5
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
6
  import { readFileSync } from 'fs';
6
7
  import { spawn } from 'child_process';
@@ -133,6 +134,7 @@ async function runStatuslineFast(args, opts = {}) {
133
134
  const now = (opts.now ?? Date.now)();
134
135
  const stdinText = (opts.read ?? readStdin)();
135
136
  const stdin = parseStatuslineStdin(stdinText);
137
+ recordReportedCost(home, stdin.session_id, stdin.cost?.total_cost_usd);
136
138
  const state = readStatuslineState(home);
137
139
  const ourLine = renderStatusline(stdin, state, now);
138
140
  const { wrapId } = parseStatuslineArgs(args);
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+
4
+ createRequire(import.meta.url);
5
+
6
+ // src/runner/container/disk.ts
7
+ var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3 };
8
+ function parseSize(text) {
9
+ const match = /^(\d+(?:\.\d+)?)([kKmMgG]?)$/.exec(text);
10
+ if (match === null) return void 0;
11
+ const bytes = Number(match[1]) * UNITS[match[2].toLowerCase()];
12
+ return bytes > 0 ? bytes : void 0;
13
+ }
14
+ function parseDuKilobytes(output) {
15
+ const match = /^(\d+)\s/.exec(output);
16
+ return match === null ? void 0 : Number(match[1]);
17
+ }
18
+ function exceedsDisk(usedKilobytes, limit) {
19
+ const bytes = parseSize(limit);
20
+ return usedKilobytes * 1024 > (bytes ?? Number.POSITIVE_INFINITY);
21
+ }
22
+
23
+ // src/runner/allow-hosts.ts
24
+ var MAX_ALLOW_HOSTS = 500;
25
+ var LABEL = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/;
26
+ var IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
27
+ function isAddress(text) {
28
+ if (IPV4.test(text)) return true;
29
+ try {
30
+ new URL(`http://[${text}]/`);
31
+ return true;
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+ function isHostName(text) {
37
+ const labels = text.split(".");
38
+ return text.length <= 253 && labels.every((label) => LABEL.test(label)) && !/^\d+$/.test(labels.at(-1));
39
+ }
40
+ function allowHostProblem(entry) {
41
+ if (typeof entry !== "string") return "is not a string";
42
+ const text = entry.toLowerCase();
43
+ if (isAddress(text)) return void 0;
44
+ if (text.startsWith("*.")) {
45
+ const domain = text.slice(2);
46
+ if (!isHostName(domain)) return "is not a host name after *.";
47
+ if (!domain.includes(".")) return "is too broad: a pattern needs a domain with at least two labels after *.";
48
+ return void 0;
49
+ }
50
+ return isHostName(text) ? void 0 : "is not a host name, a *. pattern or an IP address";
51
+ }
52
+ function hostAllowed(host, allowHosts) {
53
+ const name = host.toLowerCase().replace(/\.$/, "");
54
+ return allowHosts.some((entry) => {
55
+ const pattern = entry.toLowerCase();
56
+ return pattern.startsWith("*.") ? name.endsWith(pattern.slice(1)) : name === pattern;
57
+ });
58
+ }
59
+
60
+ export { MAX_ALLOW_HOSTS, allowHostProblem, exceedsDisk, hostAllowed, parseDuKilobytes, parseSize };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { decodeWrappedCommand } from './chunk-R5KBGVII.js';
3
+ import { decodeWrappedCommand } from './chunk-5QRP3MVS.js';
4
4
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
5
5
  import { readFileSync, mkdirSync, appendFileSync, writeFileSync } from 'fs';
6
6
  import { spawn } from 'child_process';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-KFYJV2ZG.js';
4
- import './chunk-Z5GEFMRW.js';
3
+ import { maintainedFilesForExistingSkills, loadMaintainedFiles, writeMaintainedFiles } from './chunk-WO2BXCTQ.js';
4
+ import { emitTree, parseOuterlayerConfig, classifyTree, generatedHeaderSource, CONTEXT_PIN_FILENAME, normalizePath } from './chunk-EMY4I27X.js';
5
5
  import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync, readdirSync } from 'fs';
6
6
  import { join, dirname, relative, sep } from 'path';
7
7
 
@@ -53,19 +53,21 @@ function walk(dir, repoRoot, insideOuterlayer, out) {
53
53
  }
54
54
  }
55
55
  }
56
- function buildEmitInput(repoRoot, targets) {
57
- const discovered = discoverOuterlayerPaths(repoRoot);
56
+ function buildEmitInput(repoRoot, targets, maintained) {
57
+ const bundled = new Map(maintained.map((f) => [f.path, f.content]));
58
+ const discovered = [.../* @__PURE__ */ new Set([...discoverOuterlayerPaths(repoRoot), ...bundled.keys()])].sort();
58
59
  const { entries } = classifyTree(discovered);
59
60
  const classified = new Set(entries.map((e) => e.path));
60
61
  const contents = /* @__PURE__ */ new Map();
61
62
  for (const entry of entries) {
62
63
  if (!CONTENT_KINDS.has(entry.kind)) continue;
63
- contents.set(entry.path, readFileSync(join(repoRoot, entry.path), "utf8"));
64
+ contents.set(entry.path, bundled.get(entry.path) ?? readFileSync(join(repoRoot, entry.path), "utf8"));
64
65
  }
65
66
  const assetPaths = discovered.filter((p) => !classified.has(p));
66
67
  return { input: { entries, contents, assetPaths, targets }, sourcePaths: classified };
67
68
  }
68
- function writeOutputs(repoRoot, result) {
69
+ function writeOutputs(repoRoot, result, maintained) {
70
+ const changedMaintained = writeMaintainedFiles(repoRoot, maintained);
69
71
  const files = [];
70
72
  for (const file of result.files) {
71
73
  const abs = join(repoRoot, file.path);
@@ -80,11 +82,15 @@ function writeOutputs(repoRoot, result) {
80
82
  copyFileSync(join(repoRoot, copy.fromSourcePath), abs);
81
83
  copies.push(copy.toPath);
82
84
  }
83
- return { files, copies };
85
+ return { files, copies, maintained: [...changedMaintained.created, ...changedMaintained.rewritten] };
84
86
  }
85
- function renderSuccess(targets, files, copies) {
87
+ function renderSuccess(targets, files, copies, maintained) {
86
88
  const copySuffix = copies.length > 0 ? `, ${copies.length} ${copies.length === 1 ? "copy" : "copies"}` : "";
87
- return `${GREEN}\u2713${RESET} emitted ${files.length} file${files.length === 1 ? "" : "s"}${copySuffix} for ${DIM}${targets.join(", ")}${RESET}`;
89
+ const lines = [
90
+ `${GREEN}\u2713${RESET} emitted ${files.length} file${files.length === 1 ? "" : "s"}${copySuffix} for ${DIM}${targets.join(", ")}${RESET}`
91
+ ];
92
+ for (const path of maintained) lines.push(` ${DIM}updated ${path} to this CLI's version${RESET}`);
93
+ return lines.join("\n");
88
94
  }
89
95
  function renderFailure(errors) {
90
96
  const lines = [`${RED}\u2717${RESET} emit failed \u2014 nothing written`];
@@ -150,17 +156,48 @@ function findOrphans(repoRoot, computedFiles, sourcePaths) {
150
156
  }
151
157
  return orphans;
152
158
  }
159
+ function withholdKept(repoRoot, result, keep) {
160
+ if (keep === void 0) return { result, kept: [] };
161
+ const kept = [];
162
+ const files = result.files.filter((file) => {
163
+ const abs = join(repoRoot, file.path);
164
+ if (!existsSync(abs)) return true;
165
+ const existing = readFileSync(abs, "utf8");
166
+ if (existing === file.content || !keep({ kind: "file", path: file.path, existing, content: file.content })) return true;
167
+ kept.push(file.path);
168
+ return false;
169
+ });
170
+ const copies = result.copies.filter((copy) => {
171
+ const abs = join(repoRoot, copy.toPath);
172
+ if (!existsSync(abs)) return true;
173
+ const src = join(repoRoot, copy.fromSourcePath);
174
+ if (existsSync(src) && readFileSync(abs).equals(readFileSync(src))) return true;
175
+ if (!keep({ kind: "copy", path: copy.toPath, source: copy.fromSourcePath })) return true;
176
+ kept.push(copy.toPath);
177
+ return false;
178
+ });
179
+ return { result: { ...result, files, copies }, kept: kept.sort() };
180
+ }
153
181
  function runEmit(opts = {}) {
154
182
  const cwd = opts.cwd ?? process.cwd();
155
183
  if (!existsSync(cwd)) throw new EmitError(`no such directory: ${cwd}`);
156
184
  const targets = resolveTargetsFromConfig(cwd);
157
- const built = buildEmitInput(cwd, targets);
158
- const result = emitTree(built.input);
185
+ const maintained = maintainedFilesForExistingSkills(cwd, loadMaintainedFiles());
186
+ const built = buildEmitInput(cwd, targets, maintained);
187
+ const emitted = emitTree(built.input);
188
+ const { result, kept } = withholdKept(cwd, emitted, opts.keep);
159
189
  const hasErrors = result.errors.length > 0;
160
- if (opts.check) return runCheck(cwd, opts, targets, result, hasErrors, built.sourcePaths);
161
- const written = hasErrors ? { files: [], copies: [] } : writeOutputs(cwd, result);
190
+ if (opts.check) return runCheck(cwd, opts, targets, result, hasErrors, built.sourcePaths, maintained, kept);
191
+ const written = hasErrors ? { files: [], copies: [], maintained: [] } : writeOutputs(cwd, result, maintained);
162
192
  const exitCode = hasErrors ? 1 : 0;
163
- const output = opts.json ? JSON.stringify({ targets, files: written.files, copies: written.copies, warnings: result.warnings, errors: result.errors }) : hasErrors ? renderFailure(result.errors) : renderSuccess(targets, written.files, written.copies);
193
+ const output = opts.json ? JSON.stringify({
194
+ targets,
195
+ files: written.files,
196
+ copies: written.copies,
197
+ maintained: written.maintained,
198
+ warnings: result.warnings,
199
+ errors: result.errors
200
+ }) : hasErrors ? renderFailure(result.errors) : renderSuccess(targets, written.files, written.copies, written.maintained);
164
201
  if (!opts.quiet) {
165
202
  if (opts.json) {
166
203
  process.stdout.write(output + "\n");
@@ -172,11 +209,25 @@ function runEmit(opts = {}) {
172
209
  `);
173
210
  }
174
211
  }
175
- return { targets, files: written.files, copies: written.copies, warnings: result.warnings, errors: result.errors, output, exitCode };
212
+ return {
213
+ targets,
214
+ files: written.files,
215
+ copies: written.copies,
216
+ maintained: written.maintained,
217
+ kept,
218
+ warnings: result.warnings,
219
+ errors: result.errors,
220
+ output,
221
+ exitCode
222
+ };
176
223
  }
177
- function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths) {
224
+ function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths, maintained, kept) {
178
225
  const drift = [];
179
226
  if (!hasErrors) {
227
+ for (const file of maintained) {
228
+ const d = compareFile(cwd, file.path, file.content);
229
+ if (d) drift.push(d);
230
+ }
180
231
  const computedFiles = new Set(result.files.map((f) => f.path));
181
232
  for (const file of result.files) {
182
233
  const d = compareFile(cwd, file.path, file.content);
@@ -202,7 +253,7 @@ function runCheck(cwd, opts, targets, result, hasErrors, sourcePaths) {
202
253
  `);
203
254
  }
204
255
  }
205
- return { targets, files: [], copies: [], warnings: result.warnings, errors: result.errors, drift, output, exitCode };
256
+ return { targets, files: [], copies: [], maintained: [], kept, warnings: result.warnings, errors: result.errors, drift, output, exitCode };
206
257
  }
207
258
 
208
259
  export { EmitError, runEmit };