opencode-swarm 7.112.2 → 7.113.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 (66) hide show
  1. package/.opencode/skills/brainstorm/SKILL.md +1 -0
  2. package/.opencode/skills/ci-failure-batching/SKILL.md +2 -1
  3. package/.opencode/skills/ci-fix-monitor/SKILL.md +203 -0
  4. package/.opencode/skills/clarify/SKILL.md +1 -0
  5. package/.opencode/skills/clarify-spec/SKILL.md +1 -0
  6. package/.opencode/skills/codebase-review-swarm/SKILL.md +1 -0
  7. package/.opencode/skills/commit-pr/SKILL.md +15 -0
  8. package/.opencode/skills/consult/SKILL.md +1 -0
  9. package/.opencode/skills/council/SKILL.md +1 -0
  10. package/.opencode/skills/critic-gate/SKILL.md +1 -0
  11. package/.opencode/skills/deep-dive/SKILL.md +1 -0
  12. package/.opencode/skills/deep-research/SKILL.md +1 -0
  13. package/.opencode/skills/design-docs/SKILL.md +2 -1
  14. package/.opencode/skills/discover/SKILL.md +1 -0
  15. package/.opencode/skills/engineering-conventions/SKILL.md +2 -1
  16. package/.opencode/skills/execute/SKILL.md +1 -0
  17. package/.opencode/skills/fork-pr-operations/SKILL.md +136 -0
  18. package/.opencode/skills/gate-attribution/SKILL.md +2 -1
  19. package/.opencode/skills/issue-ingest/SKILL.md +1 -0
  20. package/.opencode/skills/loop/SKILL.md +7 -6
  21. package/.opencode/skills/merge-queue-readiness/SKILL.md +2 -1
  22. package/.opencode/skills/parallel-work-check/SKILL.md +133 -0
  23. package/.opencode/skills/phase-wrap/SKILL.md +1 -0
  24. package/.opencode/skills/plan/SKILL.md +1 -0
  25. package/.opencode/skills/pre-phase-briefing/SKILL.md +1 -0
  26. package/.opencode/skills/resume/SKILL.md +1 -0
  27. package/.opencode/skills/running-tests/SKILL.md +1 -0
  28. package/.opencode/skills/skill-edit-validation/SKILL.md +2 -1
  29. package/.opencode/skills/specify/SKILL.md +1 -0
  30. package/.opencode/skills/swarm/SKILL.md +1 -0
  31. package/.opencode/skills/swarm-ci-monitor/SKILL.md +5 -4
  32. package/.opencode/skills/swarm-implement/SKILL.md +4 -4
  33. package/.opencode/skills/swarm-pr-feedback/SKILL.md +26 -5
  34. package/.opencode/skills/swarm-pr-review/SKILL.md +2 -1
  35. package/.opencode/skills/swarm-pr-subscribe/SKILL.md +1 -0
  36. package/.opencode/skills/test-file-split/SKILL.md +102 -0
  37. package/.opencode/skills/worktree-retry-cleanup/SKILL.md +2 -1
  38. package/.opencode/skills/writing-tests/SKILL.md +43 -0
  39. package/dist/cli/{config-doctor-mmw4c50z.js → config-doctor-az11cwf5.js} +2 -2
  40. package/dist/cli/{curator-llm-factory-5bns4xy6.js → curator-llm-factory-nmhzc774.js} +5 -5
  41. package/dist/cli/{curator-snvp9542.js → curator-nm2c9y1p.js} +5 -5
  42. package/dist/cli/{guardrail-explain-4s7djf9p.js → guardrail-explain-q1np1xaw.js} +6 -6
  43. package/dist/cli/{guardrail-log-xjgr00eq.js → guardrail-log-hysxdwg9.js} +3 -3
  44. package/dist/cli/{hive-promoter-zs295trk.js → hive-promoter-z4dezw62.js} +5 -5
  45. package/dist/cli/{index-xehrmy76.js → index-0gf6de8b.js} +1 -1
  46. package/dist/cli/{index-cq2hgvd2.js → index-1ey3dxq7.js} +9 -1
  47. package/dist/cli/{index-npxmfj48.js → index-8f256d7t.js} +1 -1
  48. package/dist/cli/{index-82v0d72f.js → index-h389ed8y.js} +424 -87
  49. package/dist/cli/{index-9fjpecax.js → index-ky8eb4q6.js} +2 -2
  50. package/dist/cli/{index-snmfqhez.js → index-npd9rypp.js} +2009 -958
  51. package/dist/cli/{index-w84stmkx.js → index-r9fjzr3k.js} +1 -1
  52. package/dist/cli/{index-k6wd3chp.js → index-rqpm9cwe.js} +7 -7
  53. package/dist/cli/index.js +5 -5
  54. package/dist/cli/{schema-2p15yrvn.js → schema-qb313fyy.js} +1 -1
  55. package/dist/cli/{skill-generator-fe6q8xgm.js → skill-generator-nnwn4sq0.js} +1 -1
  56. package/dist/commands/loop.d.ts +1 -1
  57. package/dist/commands/registry.d.ts +1 -1
  58. package/dist/config/bundled-skills.d.ts +15 -5
  59. package/dist/config/project-init.d.ts +1 -1
  60. package/dist/config/schema.d.ts +2 -0
  61. package/dist/hooks/skill-propagation-gate.d.ts +45 -5
  62. package/dist/hooks/skill-scoring.d.ts +22 -1
  63. package/dist/index.js +88 -88
  64. package/dist/sast/rules/index.d.ts +0 -9
  65. package/dist/utils/gitignore-warning.d.ts +8 -4
  66. package/package.json +5 -1
@@ -19,6 +19,7 @@ import {
19
19
  } from "./index-bfwt3abw.js";
20
20
  import {
21
21
  init_logger,
22
+ log,
22
23
  warn
23
24
  } from "./index-zgwm4ryv.js";
24
25
  import {
@@ -27,34 +28,351 @@ import {
27
28
 
28
29
  // src/services/skill-generator.ts
29
30
  import { existsSync as existsSync2, unlinkSync } from "fs";
30
- import { mkdir as mkdir3, readFile as readFile3, rename as rename2, unlink, writeFile as writeFile3 } from "fs/promises";
31
- import * as path3 from "path";
31
+ import { mkdir as mkdir4, readFile as readFile4, rename as rename3, unlink, writeFile as writeFile4 } from "fs/promises";
32
+ import * as path4 from "path";
33
+
34
+ // src/config/bundled-skills.ts
35
+ import { randomUUID } from "crypto";
36
+ import * as fsp from "fs/promises";
37
+ import * as path from "path";
38
+
39
+ // src/services/warning-buffer.ts
40
+ init_logger();
41
+ var deferredWarnings = [];
42
+ var MAX_DEFERRED_WARNINGS = 50;
43
+ var deferredWarningsTruncated = false;
44
+ function addDeferredWarning(warning) {
45
+ if (deferredWarnings.length < MAX_DEFERRED_WARNINGS - 1) {
46
+ deferredWarnings.push(warning);
47
+ return;
48
+ }
49
+ if (!deferredWarningsTruncated) {
50
+ deferredWarnings.push(`[opencode-swarm] ${MAX_DEFERRED_WARNINGS - 1} deferred warnings buffered; additional advisories were dropped (cap reached). Run with OPENCODE_SWARM_DEBUG=1 to see all advisories in the debug log.`);
51
+ deferredWarningsTruncated = true;
52
+ }
53
+ }
54
+ function advisoryWarn(message, data) {
55
+ addDeferredWarning(message);
56
+ log(message, data);
57
+ }
58
+ function getDeferredWarnings() {
59
+ return [...deferredWarnings];
60
+ }
61
+
62
+ // src/config/bundled-skills.ts
63
+ init_logger();
64
+ var BUNDLED_PROJECT_SKILLS = [
65
+ "brainstorm",
66
+ "specify",
67
+ "clarify-spec",
68
+ "resume",
69
+ "clarify",
70
+ "discover",
71
+ "consult",
72
+ "pre-phase-briefing",
73
+ "council",
74
+ "deep-dive",
75
+ "deep-research",
76
+ "codebase-review-swarm",
77
+ "swarm-implement",
78
+ "design-docs",
79
+ "swarm-pr-review",
80
+ "swarm",
81
+ "swarm-pr-feedback",
82
+ "swarm-pr-subscribe",
83
+ "swarm-ci-monitor",
84
+ "issue-ingest",
85
+ "plan",
86
+ "critic-gate",
87
+ "execute",
88
+ "phase-wrap",
89
+ "loop",
90
+ "writing-tests",
91
+ "running-tests",
92
+ "engineering-conventions",
93
+ "commit-pr",
94
+ "ci-failure-batching",
95
+ "gate-attribution",
96
+ "merge-queue-readiness",
97
+ "skill-edit-validation",
98
+ "worktree-retry-cleanup",
99
+ "test-file-split",
100
+ "fork-pr-operations",
101
+ "parallel-work-check",
102
+ "ci-fix-monitor"
103
+ ];
104
+ var BUNDLED_PROJECT_SKILL_ROOT = ".swarm/bundled-skills";
105
+ function bundledProjectSkillFileReference(slug) {
106
+ return `file:${BUNDLED_PROJECT_SKILL_ROOT}/${slug}/SKILL.md`;
107
+ }
108
+ var MAX_SKILL_FILES = 64;
109
+ var MAX_SKILL_BYTES = 512000;
110
+ var MAX_IN_FLIGHT_SYNCS = 64;
111
+ var inFlightSyncs = new Map;
112
+ function describeBundledSkillSyncFailure(err) {
113
+ const message = err instanceof Error ? err.message : String(err);
114
+ return `[opencode-swarm] Could not install bundled project skills; continuing without sync: ${message}`;
115
+ }
116
+ async function isSymbolicLinkAsync(p) {
117
+ try {
118
+ return (await fsp.lstat(p)).isSymbolicLink();
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+ async function ensureNotSymlinkedDirectoryAsync(p) {
124
+ try {
125
+ const stat2 = await fsp.lstat(p);
126
+ return stat2.isDirectory() && !stat2.isSymbolicLink();
127
+ } catch (err) {
128
+ return err.code === "ENOENT";
129
+ }
130
+ }
131
+ async function pathExistsAsync(p) {
132
+ try {
133
+ await fsp.access(p);
134
+ return true;
135
+ } catch {
136
+ return false;
137
+ }
138
+ }
139
+ async function collectBundledSkillFilesBoundedAsync(sourceDir, state, relativeDir = "") {
140
+ const currentSource = path.join(sourceDir, relativeDir);
141
+ const entries = await fsp.readdir(currentSource, { withFileTypes: true });
142
+ entries.sort((a, b) => a.name.localeCompare(b.name));
143
+ const files = [];
144
+ for (const entry of entries) {
145
+ const relativeEntry = path.join(relativeDir, entry.name);
146
+ const sourcePath = path.join(sourceDir, relativeEntry);
147
+ if (entry.isSymbolicLink() || await isSymbolicLinkAsync(sourcePath)) {
148
+ throw new Error("refusing to copy symlinked bundled skill entry");
149
+ }
150
+ if (entry.isDirectory()) {
151
+ files.push(...await collectBundledSkillFilesBoundedAsync(sourceDir, state, relativeEntry));
152
+ continue;
153
+ }
154
+ if (!entry.isFile())
155
+ continue;
156
+ const stat2 = await fsp.stat(sourcePath);
157
+ const nextFiles = state.files + 1;
158
+ const nextBytes = state.bytes + stat2.size;
159
+ if (nextFiles > MAX_SKILL_FILES || nextBytes > MAX_SKILL_BYTES) {
160
+ throw new Error("bundled skill package exceeds copy bounds");
161
+ }
162
+ state.files = nextFiles;
163
+ state.bytes = nextBytes;
164
+ files.push({ relativePath: relativeEntry });
165
+ }
166
+ return files;
167
+ }
168
+ async function ensureContainedDirectoryAsync(rootDir, directory) {
169
+ const safeRoot = path.resolve(rootDir);
170
+ const safeDirectory = path.resolve(directory);
171
+ const relative2 = path.relative(safeRoot, safeDirectory);
172
+ if (relative2.startsWith("..") || path.isAbsolute(relative2)) {
173
+ throw new Error("bundled skill destination escaped its private root");
174
+ }
175
+ try {
176
+ const rootStat = await fsp.lstat(safeRoot);
177
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
178
+ throw new Error("refusing to traverse unsafe bundled skill directory");
179
+ }
180
+ } catch (err) {
181
+ if (err.code !== "ENOENT")
182
+ throw err;
183
+ await fsp.mkdir(safeRoot, { recursive: true });
184
+ const rootStat = await fsp.lstat(safeRoot);
185
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
186
+ throw new Error("refusing to traverse unsafe bundled skill directory");
187
+ }
188
+ }
189
+ let current = safeRoot;
190
+ for (const segment of relative2.split(path.sep).filter(Boolean)) {
191
+ current = path.join(current, segment);
192
+ try {
193
+ const stat2 = await fsp.lstat(current);
194
+ if (stat2.isSymbolicLink() || !stat2.isDirectory()) {
195
+ throw new Error("refusing to traverse unsafe bundled skill directory");
196
+ }
197
+ } catch (err) {
198
+ if (err.code !== "ENOENT")
199
+ throw err;
200
+ try {
201
+ await fsp.mkdir(current);
202
+ } catch (mkdirErr) {
203
+ if (mkdirErr.code !== "EEXIST") {
204
+ throw mkdirErr;
205
+ }
206
+ }
207
+ const stat2 = await fsp.lstat(current);
208
+ if (stat2.isSymbolicLink() || !stat2.isDirectory()) {
209
+ throw new Error("refusing to traverse unsafe bundled skill directory");
210
+ }
211
+ }
212
+ }
213
+ }
214
+ async function rollbackCopiedFilesAsync(copiedFiles, destDir, overwrittenFiles = []) {
215
+ const safeDestDir = path.resolve(destDir);
216
+ const dirs = new Set;
217
+ for (const copiedFile of copiedFiles) {
218
+ const resolvedFile = path.resolve(copiedFile);
219
+ const relative2 = path.relative(safeDestDir, resolvedFile);
220
+ if (relative2.startsWith("..") || path.isAbsolute(relative2))
221
+ continue;
222
+ try {
223
+ await fsp.rm(resolvedFile, { force: true });
224
+ } catch {}
225
+ dirs.add(path.dirname(resolvedFile));
226
+ }
227
+ for (const overwritten of overwrittenFiles.reverse()) {
228
+ const resolvedFile = path.resolve(overwritten.path);
229
+ const relative2 = path.relative(safeDestDir, resolvedFile);
230
+ if (relative2.startsWith("..") || path.isAbsolute(relative2))
231
+ continue;
232
+ try {
233
+ await fsp.writeFile(resolvedFile, overwritten.contents);
234
+ } catch {}
235
+ }
236
+ for (const dir of [...dirs].sort((a, b) => b.length - a.length)) {
237
+ const relative2 = path.relative(safeDestDir, path.resolve(dir));
238
+ if (relative2.startsWith("..") || path.isAbsolute(relative2))
239
+ continue;
240
+ try {
241
+ await fsp.rmdir(dir);
242
+ } catch {}
243
+ }
244
+ }
245
+ async function copyFileAtomicAsync(sourcePath, destPath) {
246
+ const tempPath = path.join(path.dirname(destPath), `.${path.basename(destPath)}.tmp.${randomUUID()}`);
247
+ try {
248
+ await fsp.copyFile(sourcePath, tempPath);
249
+ await fsp.rename(tempPath, destPath);
250
+ } catch (err) {
251
+ try {
252
+ await fsp.rm(tempPath, { force: true });
253
+ } catch {}
254
+ throw err;
255
+ }
256
+ }
257
+ async function copyBundledDirectoryBoundedAsync(sourceDir, destDir) {
258
+ const files = await collectBundledSkillFilesBoundedAsync(sourceDir, {
259
+ files: 0,
260
+ bytes: 0
261
+ });
262
+ const copiedFiles = [];
263
+ const overwrittenFiles = [];
264
+ try {
265
+ for (const file of files) {
266
+ const sourcePath = path.join(sourceDir, file.relativePath);
267
+ const destPath = path.join(destDir, file.relativePath);
268
+ await ensureContainedDirectoryAsync(destDir, path.dirname(destPath));
269
+ if (await pathExistsAsync(destPath)) {
270
+ if (await isSymbolicLinkAsync(destPath)) {
271
+ throw new Error("refusing to overwrite symlinked bundled skill file");
272
+ }
273
+ const [sourceContents, destContents] = await Promise.all([
274
+ fsp.readFile(sourcePath),
275
+ fsp.readFile(destPath)
276
+ ]);
277
+ if (sourceContents.equals(destContents)) {
278
+ continue;
279
+ }
280
+ overwrittenFiles.push({ path: destPath, contents: destContents });
281
+ await copyFileAtomicAsync(sourcePath, destPath);
282
+ } else {
283
+ await copyFileAtomicAsync(sourcePath, destPath);
284
+ copiedFiles.push(destPath);
285
+ }
286
+ }
287
+ } catch (err) {
288
+ await rollbackCopiedFilesAsync(copiedFiles, destDir, overwrittenFiles);
289
+ throw err;
290
+ }
291
+ }
292
+ async function performBundledProjectSkillSyncAsync(projectDirectory, packageRoot, quiet) {
293
+ try {
294
+ const sourceRoot = path.join(packageRoot, ".opencode", "skills");
295
+ const swarmDir = path.join(projectDirectory, ".swarm");
296
+ const skillsDir = path.join(projectDirectory, BUNDLED_PROJECT_SKILL_ROOT);
297
+ if (!await ensureNotSymlinkedDirectoryAsync(sourceRoot)) {
298
+ throw new Error("refusing to copy symlinked bundled skill source root");
299
+ }
300
+ if (!await ensureNotSymlinkedDirectoryAsync(swarmDir))
301
+ return;
302
+ if (!await ensureNotSymlinkedDirectoryAsync(skillsDir))
303
+ return;
304
+ for (const slug of BUNDLED_PROJECT_SKILLS) {
305
+ const sourceDir = path.join(sourceRoot, slug);
306
+ const sourceSkill = path.join(sourceDir, "SKILL.md");
307
+ const destDir = path.join(skillsDir, slug);
308
+ if (!await pathExistsAsync(sourceSkill))
309
+ continue;
310
+ if (!await ensureNotSymlinkedDirectoryAsync(sourceDir)) {
311
+ throw new Error("refusing to copy symlinked bundled skill directory");
312
+ }
313
+ if (!await ensureNotSymlinkedDirectoryAsync(destDir))
314
+ continue;
315
+ await copyBundledDirectoryBoundedAsync(sourceDir, destDir);
316
+ log("synchronized bundled skill", {
317
+ slug: bundledProjectSkillFileReference(slug)
318
+ });
319
+ }
320
+ } catch (err) {
321
+ const failureMsg = describeBundledSkillSyncFailure(err);
322
+ if (quiet) {
323
+ advisoryWarn(failureMsg);
324
+ } else {
325
+ console.warn(failureMsg);
326
+ }
327
+ }
328
+ }
329
+ async function syncBundledProjectSkillsIfMissingAsync(projectDirectory, packageRoot, quiet = false) {
330
+ const syncKey = path.resolve(projectDirectory, BUNDLED_PROJECT_SKILL_ROOT);
331
+ const existing = inFlightSyncs.get(syncKey);
332
+ if (existing)
333
+ return existing;
334
+ if (inFlightSyncs.size >= MAX_IN_FLIGHT_SYNCS) {
335
+ log("skipping bundled skill sync because the in-flight limit was reached", {
336
+ limit: MAX_IN_FLIGHT_SYNCS
337
+ });
338
+ return;
339
+ }
340
+ const syncPromise = performBundledProjectSkillSyncAsync(projectDirectory, packageRoot, quiet).finally(() => {
341
+ if (inFlightSyncs.get(syncKey) === syncPromise) {
342
+ inFlightSyncs.delete(syncKey);
343
+ }
344
+ });
345
+ inFlightSyncs.set(syncKey, syncPromise);
346
+ return syncPromise;
347
+ }
348
+
349
+ // src/services/skill-generator.ts
32
350
  init_logger();
33
351
 
34
352
  // src/services/skill-changelog.ts
35
353
  init_logger();
36
- import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
37
- import * as path from "path";
354
+ import { appendFile, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
355
+ import * as path2 from "path";
38
356
  var MAX_CHANGELOG_ENTRIES_PER_SKILL = 200;
39
357
  function resolveSkillChangelogPath(directory, slug) {
40
358
  if (slug.includes("..") || slug.includes("/") || slug.includes("\\")) {
41
359
  throw new Error(`Invalid skill slug: ${slug} \u2014 must not contain "..", "/" or "\\"`);
42
360
  }
43
- return path.join(directory, ".swarm", "skill-changelogs", `${slug}.jsonl`);
361
+ return path2.join(directory, ".swarm", "skill-changelogs", `${slug}.jsonl`);
44
362
  }
45
363
  async function appendSkillChangelog(directory, slug, entry) {
46
364
  const filePath = resolveSkillChangelogPath(directory, slug);
47
- const dirPath = path.dirname(filePath);
48
- await mkdir(dirPath, { recursive: true });
365
+ const dirPath = path2.dirname(filePath);
366
+ await mkdir2(dirPath, { recursive: true });
49
367
  await appendFile(filePath, `${JSON.stringify(entry)}
50
368
  `, "utf-8");
51
369
  try {
52
- const content = await readFile(filePath, "utf-8");
370
+ const content = await readFile2(filePath, "utf-8");
53
371
  const lines = content.split(`
54
372
  `).filter((line) => line.trim().length > 0);
55
373
  if (lines.length > MAX_CHANGELOG_ENTRIES_PER_SKILL) {
56
374
  const trimmed = lines.slice(lines.length - MAX_CHANGELOG_ENTRIES_PER_SKILL);
57
- await writeFile(filePath, `${trimmed.join(`
375
+ await writeFile2(filePath, `${trimmed.join(`
58
376
  `)}
59
377
  `, "utf-8");
60
378
  }
@@ -67,16 +385,16 @@ async function appendSkillChangelog(directory, slug, entry) {
67
385
  import { createHash } from "crypto";
68
386
  import { existsSync } from "fs";
69
387
  import {
70
- lstat,
71
- mkdir as mkdir2,
72
- readdir,
73
- readFile as readFile2,
388
+ lstat as lstat2,
389
+ mkdir as mkdir3,
390
+ readdir as readdir2,
391
+ readFile as readFile3,
74
392
  realpath,
75
- rename,
76
- stat,
77
- writeFile as writeFile2
393
+ rename as rename2,
394
+ stat as stat2,
395
+ writeFile as writeFile3
78
396
  } from "fs/promises";
79
- import * as path2 from "path";
397
+ import * as path3 from "path";
80
398
  var SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
81
399
  var MAX_EVAL_FILES = 50;
82
400
  var MAX_EVAL_FILE_BYTES = 64 * 1024;
@@ -142,15 +460,15 @@ function casesFromParsedJson(parsed, fileLabel) {
142
460
  return cases;
143
461
  }
144
462
  function evalRoot(directory, slug) {
145
- return path2.join(directory, ".swarm", "skills", "evals", slug);
463
+ return path3.join(directory, ".swarm", "skills", "evals", slug);
146
464
  }
147
465
  function isInsidePath(root, target) {
148
- const resolvedRoot = path2.resolve(root);
149
- const resolvedTarget = path2.resolve(target);
150
- return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path2.sep);
466
+ const resolvedRoot = path3.resolve(root);
467
+ const resolvedTarget = path3.resolve(target);
468
+ return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path3.sep);
151
469
  }
152
470
  function rejectedEditsPath(directory) {
153
- return path2.join(directory, ".swarm", "skills", "rejected-edits.jsonl");
471
+ return path3.join(directory, ".swarm", "skills", "rejected-edits.jsonl");
154
472
  }
155
473
  async function loadEvalSet(directory, slug) {
156
474
  if (!isValidSlug(slug)) {
@@ -187,7 +505,7 @@ async function loadEvalSet(directory, slug) {
187
505
  }
188
506
  let entries;
189
507
  try {
190
- entries = await readdir(root);
508
+ entries = await readdir2(root);
191
509
  } catch (err) {
192
510
  return {
193
511
  status: "invalid",
@@ -203,9 +521,9 @@ async function loadEvalSet(directory, slug) {
203
521
  const cases = [];
204
522
  const fileLabels = [];
205
523
  for (const file of files) {
206
- const fullPath = path2.join(root, file);
207
- const resolved = path2.resolve(fullPath);
208
- const resolvedRoot = path2.resolve(root);
524
+ const fullPath = path3.join(root, file);
525
+ const resolved = path3.resolve(fullPath);
526
+ const resolvedRoot = path3.resolve(root);
209
527
  if (!isInsidePath(resolvedRoot, resolved)) {
210
528
  return {
211
529
  status: "invalid",
@@ -217,7 +535,7 @@ async function loadEvalSet(directory, slug) {
217
535
  let readPath = fullPath;
218
536
  let info;
219
537
  try {
220
- const linkInfo = await lstat(fullPath);
538
+ const linkInfo = await lstat2(fullPath);
221
539
  if (linkInfo.isSymbolicLink()) {
222
540
  const realFile = await realpath(fullPath);
223
541
  if (!isInsidePath(realRoot, realFile)) {
@@ -229,7 +547,7 @@ async function loadEvalSet(directory, slug) {
229
547
  };
230
548
  }
231
549
  readPath = realFile;
232
- info = await stat(realFile);
550
+ info = await stat2(realFile);
233
551
  } else {
234
552
  if (!linkInfo.isFile())
235
553
  continue;
@@ -255,7 +573,7 @@ async function loadEvalSet(directory, slug) {
255
573
  }
256
574
  let parsed;
257
575
  try {
258
- parsed = JSON.parse(await readFile2(readPath, "utf-8"));
576
+ parsed = JSON.parse(await readFile3(readPath, "utf-8"));
259
577
  } catch (err) {
260
578
  return {
261
579
  status: "invalid",
@@ -264,7 +582,7 @@ async function loadEvalSet(directory, slug) {
264
582
  reason: `eval file invalid JSON: ${file}: ${err instanceof Error ? err.message : String(err)}`
265
583
  };
266
584
  }
267
- fileLabels.push(path2.relative(directory, fullPath).replace(/\\/g, "/"));
585
+ fileLabels.push(path3.relative(directory, fullPath).replace(/\\/g, "/"));
268
586
  cases.push(...casesFromParsedJson(parsed, file));
269
587
  if (cases.length >= MAX_EVAL_CASES)
270
588
  break;
@@ -382,10 +700,10 @@ async function evaluateSkillChange(req) {
382
700
  };
383
701
  }
384
702
  async function atomicWrite(filePath, content) {
385
- await mkdir2(path2.dirname(filePath), { recursive: true });
703
+ await mkdir3(path3.dirname(filePath), { recursive: true });
386
704
  const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
387
- await writeFile2(tmp, content, "utf-8");
388
- await rename(tmp, filePath);
705
+ await writeFile3(tmp, content, "utf-8");
706
+ await rename2(tmp, filePath);
389
707
  }
390
708
  async function appendRejectedSkillEdit(req, evaluation) {
391
709
  const filePath = rejectedEditsPath(req.directory);
@@ -405,7 +723,7 @@ async function appendRejectedSkillEdit(req, evaluation) {
405
723
  };
406
724
  let prior = [];
407
725
  try {
408
- prior = (await readFile2(filePath, "utf-8")).split(/\r?\n/).filter((line) => line.trim().length > 0);
726
+ prior = (await readFile3(filePath, "utf-8")).split(/\r?\n/).filter((line) => line.trim().length > 0);
409
727
  } catch {
410
728
  prior = [];
411
729
  }
@@ -422,7 +740,7 @@ async function isRejectedSkillContent(directory, slug, content) {
422
740
  const normalizedHash = hashNormalizedContent(content);
423
741
  let raw = "";
424
742
  try {
425
- raw = await readFile2(rejectedEditsPath(directory), "utf-8");
743
+ raw = await readFile3(rejectedEditsPath(directory), "utf-8");
426
744
  } catch {
427
745
  return false;
428
746
  }
@@ -449,6 +767,10 @@ async function isRejectedSkillContent(directory, slug, content) {
449
767
 
450
768
  // src/services/skill-generator.ts
451
769
  var SLUG_PATTERN2 = /^[a-z0-9][a-z0-9-]{0,63}$/;
770
+ var RESERVED_ACTIVE_SKILL_SLUGS = new Set(BUNDLED_PROJECT_SKILLS);
771
+ function reservedActiveSlugReason(slug) {
772
+ return `reserved bundled skill slug "${slug}" cannot be used for an active generated skill`;
773
+ }
452
774
  function sanitizeSlug(input) {
453
775
  const lc = input.toLowerCase().trim();
454
776
  const mapped = lc.replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-");
@@ -459,10 +781,10 @@ function isValidSlug2(slug) {
459
781
  return SLUG_PATTERN2.test(slug);
460
782
  }
461
783
  function proposalPath(directory, slug) {
462
- return path3.join(directory, ".swarm", "skills", "proposals", `${slug}.md`);
784
+ return path4.join(directory, ".swarm", "skills", "proposals", `${slug}.md`);
463
785
  }
464
786
  function activePath(directory, slug) {
465
- return path3.join(directory, ".opencode", "skills", "generated", slug, "SKILL.md");
787
+ return path4.join(directory, ".opencode", "skills", "generated", slug, "SKILL.md");
466
788
  }
467
789
  function activeRepoRelativePath(slug) {
468
790
  return `.opencode/skills/generated/${slug}/SKILL.md`;
@@ -486,12 +808,12 @@ function generateEvalStub(cluster) {
486
808
  ];
487
809
  }
488
810
  async function writeEvalStub(directory, slug, cases) {
489
- const stubPath = path3.join(directory, ".swarm", "skills", "evals", slug, "auto-stub.json");
811
+ const stubPath = path4.join(directory, ".swarm", "skills", "evals", slug, "auto-stub.json");
490
812
  if (cases.length === 0) {
491
813
  return { written: false, path: stubPath, reason: "no directive fields" };
492
814
  }
493
815
  try {
494
- await mkdir3(path3.dirname(stubPath), { recursive: true });
816
+ await mkdir4(path4.dirname(stubPath), { recursive: true });
495
817
  await atomicWrite2(stubPath, `${JSON.stringify({ cases })}
496
818
  `);
497
819
  return { written: true, path: stubPath };
@@ -726,10 +1048,10 @@ function escapeMarkdown(s) {
726
1048
  return s.replace(/[\r\n]+/g, " ").slice(0, 280);
727
1049
  }
728
1050
  async function atomicWrite2(p, content) {
729
- await mkdir3(path3.dirname(p), { recursive: true });
1051
+ await mkdir4(path4.dirname(p), { recursive: true });
730
1052
  const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
731
- await writeFile3(tmp, content, "utf-8");
732
- await rename2(tmp, p);
1053
+ await writeFile4(tmp, content, "utf-8");
1054
+ await rename3(tmp, p);
733
1055
  }
734
1056
  async function generateSkills(req) {
735
1057
  const minConfidence = req.minConfidence ?? DEFAULT_SKILL_MIN_CONFIDENCE;
@@ -778,8 +1100,15 @@ async function generateSkills(req) {
778
1100
  });
779
1101
  continue;
780
1102
  }
1103
+ if (req.mode === "active" && RESERVED_ACTIVE_SKILL_SLUGS.has(cluster.slug)) {
1104
+ result.skipped.push({
1105
+ slug: cluster.slug,
1106
+ reason: reservedActiveSlugReason(cluster.slug)
1107
+ });
1108
+ continue;
1109
+ }
781
1110
  const targetPath = req.mode === "active" ? activePath(req.directory, cluster.slug) : proposalPath(req.directory, cluster.slug);
782
- const repoRel = path3.relative(req.directory, targetPath).replace(/\\/g, "/");
1111
+ const repoRel = path4.relative(req.directory, targetPath).replace(/\\/g, "/");
783
1112
  if (!validateSkillPath(repoRel)) {
784
1113
  result.skipped.push({
785
1114
  slug: cluster.slug,
@@ -789,7 +1118,7 @@ async function generateSkills(req) {
789
1118
  }
790
1119
  let preserved = false;
791
1120
  if (req.mode === "active" && existsSync2(targetPath) && !req.force) {
792
- const existing = await readFile3(targetPath, "utf-8");
1121
+ const existing = await readFile4(targetPath, "utf-8");
793
1122
  if (!existing.includes("generated by opencode-swarm skill-generator")) {
794
1123
  preserved = true;
795
1124
  result.skipped.push({
@@ -814,7 +1143,7 @@ async function generateSkills(req) {
814
1143
  const existingActivePath = activePath(req.directory, cluster.slug);
815
1144
  if (existsSync2(existingActivePath)) {
816
1145
  try {
817
- incumbentContent = await readFile3(existingActivePath, "utf-8");
1146
+ incumbentContent = await readFile4(existingActivePath, "utf-8");
818
1147
  } catch {
819
1148
  incumbentContent = undefined;
820
1149
  }
@@ -1089,6 +1418,14 @@ async function activateProposal(directory, slug, force = false, options = {}) {
1089
1418
  }
1090
1419
  const from = proposalPath(directory, cleanSlug);
1091
1420
  const to = activePath(directory, cleanSlug);
1421
+ if (RESERVED_ACTIVE_SKILL_SLUGS.has(cleanSlug)) {
1422
+ return {
1423
+ activated: false,
1424
+ from,
1425
+ to,
1426
+ reason: reservedActiveSlugReason(cleanSlug)
1427
+ };
1428
+ }
1092
1429
  if (!existsSync2(from)) {
1093
1430
  return {
1094
1431
  activated: false,
@@ -1098,7 +1435,7 @@ async function activateProposal(directory, slug, force = false, options = {}) {
1098
1435
  };
1099
1436
  }
1100
1437
  if (existsSync2(to) && !force) {
1101
- const existing = await readFile3(to, "utf-8");
1438
+ const existing = await readFile4(to, "utf-8");
1102
1439
  if (!existing.includes("generated by opencode-swarm skill-generator")) {
1103
1440
  return {
1104
1441
  activated: false,
@@ -1110,7 +1447,7 @@ async function activateProposal(directory, slug, force = false, options = {}) {
1110
1447
  }
1111
1448
  let proposalContent;
1112
1449
  try {
1113
- proposalContent = await readFile3(from, "utf-8");
1450
+ proposalContent = await readFile4(from, "utf-8");
1114
1451
  } catch (readErr) {
1115
1452
  return {
1116
1453
  activated: false,
@@ -1133,7 +1470,7 @@ async function activateProposal(directory, slug, force = false, options = {}) {
1133
1470
  let incumbentContent;
1134
1471
  if (existsSync2(to)) {
1135
1472
  try {
1136
- incumbentContent = await readFile3(to, "utf-8");
1473
+ incumbentContent = await readFile4(to, "utf-8");
1137
1474
  } catch {
1138
1475
  incumbentContent = undefined;
1139
1476
  }
@@ -1208,7 +1545,7 @@ async function activateProposal(directory, slug, force = false, options = {}) {
1208
1545
  }
1209
1546
  }
1210
1547
  async function findSkillsBySourceKnowledgeId(directory, sourceId) {
1211
- const activeDir = path3.join(directory, ".opencode", "skills", "generated");
1548
+ const activeDir = path4.join(directory, ".opencode", "skills", "generated");
1212
1549
  const fs = await import("fs/promises");
1213
1550
  if (!existsSync2(activeDir))
1214
1551
  return [];
@@ -1217,14 +1554,14 @@ async function findSkillsBySourceKnowledgeId(directory, sourceId) {
1217
1554
  for (const e of entries) {
1218
1555
  if (!e.isDirectory())
1219
1556
  continue;
1220
- const skillDir = path3.join(activeDir, e.name);
1221
- const retiredMarker = path3.join(skillDir, "retired.marker");
1557
+ const skillDir = path4.join(activeDir, e.name);
1558
+ const retiredMarker = path4.join(skillDir, "retired.marker");
1222
1559
  if (existsSync2(retiredMarker))
1223
1560
  continue;
1224
- const staleMarker = path3.join(skillDir, "stale.marker");
1561
+ const staleMarker = path4.join(skillDir, "stale.marker");
1225
1562
  if (existsSync2(staleMarker))
1226
1563
  continue;
1227
- const skillPath = path3.join(skillDir, "SKILL.md");
1564
+ const skillPath = path4.join(skillDir, "SKILL.md");
1228
1565
  if (!existsSync2(skillPath))
1229
1566
  continue;
1230
1567
  let content;
@@ -1241,7 +1578,7 @@ async function findSkillsBySourceKnowledgeId(directory, sourceId) {
1241
1578
  return matches;
1242
1579
  }
1243
1580
  async function findStaleSkillsBySourceKnowledgeId(directory, archivedIds) {
1244
- const activeDir = path3.join(directory, ".opencode", "skills", "generated");
1581
+ const activeDir = path4.join(directory, ".opencode", "skills", "generated");
1245
1582
  if (!existsSync2(activeDir))
1246
1583
  return [];
1247
1584
  const fs = await import("fs/promises");
@@ -1250,14 +1587,14 @@ async function findStaleSkillsBySourceKnowledgeId(directory, archivedIds) {
1250
1587
  for (const e of entries) {
1251
1588
  if (!e.isDirectory())
1252
1589
  continue;
1253
- const skillDir = path3.join(activeDir, e.name);
1254
- const retiredMarker = path3.join(skillDir, "retired.marker");
1590
+ const skillDir = path4.join(activeDir, e.name);
1591
+ const retiredMarker = path4.join(skillDir, "retired.marker");
1255
1592
  if (existsSync2(retiredMarker))
1256
1593
  continue;
1257
- const staleMarker = path3.join(skillDir, "stale.marker");
1594
+ const staleMarker = path4.join(skillDir, "stale.marker");
1258
1595
  if (!existsSync2(staleMarker))
1259
1596
  continue;
1260
- const skillPath = path3.join(skillDir, "SKILL.md");
1597
+ const skillPath = path4.join(skillDir, "SKILL.md");
1261
1598
  if (!existsSync2(skillPath))
1262
1599
  continue;
1263
1600
  let content;
@@ -1283,8 +1620,8 @@ async function listSkills(directory) {
1283
1620
  active: [],
1284
1621
  stale: []
1285
1622
  };
1286
- const proposalsDir = path3.join(directory, ".swarm", "skills", "proposals");
1287
- const activeDir = path3.join(directory, ".opencode", "skills", "generated");
1623
+ const proposalsDir = path4.join(directory, ".swarm", "skills", "proposals");
1624
+ const activeDir = path4.join(directory, ".opencode", "skills", "generated");
1288
1625
  const fs = await import("fs/promises");
1289
1626
  if (existsSync2(proposalsDir)) {
1290
1627
  const entries = await fs.readdir(proposalsDir);
@@ -1294,7 +1631,7 @@ async function listSkills(directory) {
1294
1631
  const slug = f.replace(/\.md$/, "");
1295
1632
  result.proposals.push({
1296
1633
  slug,
1297
- path: path3.join(proposalsDir, f)
1634
+ path: path4.join(proposalsDir, f)
1298
1635
  });
1299
1636
  }
1300
1637
  }
@@ -1303,10 +1640,10 @@ async function listSkills(directory) {
1303
1640
  for (const e of entries) {
1304
1641
  if (!e.isDirectory())
1305
1642
  continue;
1306
- const retiredMarker = path3.join(activeDir, e.name, "retired.marker");
1643
+ const retiredMarker = path4.join(activeDir, e.name, "retired.marker");
1307
1644
  if (existsSync2(retiredMarker))
1308
1645
  continue;
1309
- const staleMarker = path3.join(activeDir, e.name, "stale.marker");
1646
+ const staleMarker = path4.join(activeDir, e.name, "stale.marker");
1310
1647
  if (existsSync2(staleMarker)) {
1311
1648
  let reason = "stale";
1312
1649
  try {
@@ -1316,7 +1653,7 @@ async function listSkills(directory) {
1316
1653
  result.stale.push({ slug: e.name, reason });
1317
1654
  continue;
1318
1655
  }
1319
- const skillPath = path3.join(activeDir, e.name, "SKILL.md");
1656
+ const skillPath = path4.join(activeDir, e.name, "SKILL.md");
1320
1657
  if (existsSync2(skillPath)) {
1321
1658
  result.active.push({
1322
1659
  slug: e.name,
@@ -1341,7 +1678,7 @@ async function autoApplyProposals(directory, llmDelegate) {
1341
1678
  }
1342
1679
  let content;
1343
1680
  try {
1344
- content = await readFile3(proposal.path, "utf-8");
1681
+ content = await readFile4(proposal.path, "utf-8");
1345
1682
  } catch {
1346
1683
  result.skipped.push(proposal.slug);
1347
1684
  continue;
@@ -1407,7 +1744,7 @@ async function inspectSkill(directory, slug, prefer = "auto") {
1407
1744
  candidates.push({ p: proposalPath(directory, cleanSlug), m: "draft" });
1408
1745
  for (const c of candidates) {
1409
1746
  if (existsSync2(c.p)) {
1410
- const content = await readFile3(c.p, "utf-8");
1747
+ const content = await readFile4(c.p, "utf-8");
1411
1748
  const result = { found: true, path: c.p, content, mode: c.m };
1412
1749
  const fm = parseDraftFrontmatter(content);
1413
1750
  if (fm && fm.sourceKnowledgeIds.length > 0) {
@@ -1427,11 +1764,11 @@ async function inspectSkill(directory, slug, prefer = "auto") {
1427
1764
  });
1428
1765
  }
1429
1766
  if (c.m === "active") {
1430
- const skillDir = path3.join(directory, ".opencode", "skills", "generated", cleanSlug);
1431
- const staleMarker = path3.join(skillDir, "stale.marker");
1767
+ const skillDir = path4.join(directory, ".opencode", "skills", "generated", cleanSlug);
1768
+ const staleMarker = path4.join(skillDir, "stale.marker");
1432
1769
  if (existsSync2(staleMarker)) {
1433
1770
  try {
1434
- result.stale_reason = await readFile3(staleMarker, "utf-8");
1771
+ result.stale_reason = await readFile4(staleMarker, "utf-8");
1435
1772
  } catch {}
1436
1773
  }
1437
1774
  }
@@ -1446,7 +1783,7 @@ async function retireSkill(directory, slug, reason) {
1446
1783
  return {
1447
1784
  retired: false,
1448
1785
  path: activePath(directory, cleanSlug),
1449
- markerPath: path3.join(directory, ".opencode", "skills", "generated", cleanSlug, "retired.marker"),
1786
+ markerPath: path4.join(directory, ".opencode", "skills", "generated", cleanSlug, "retired.marker"),
1450
1787
  reason: "invalid slug"
1451
1788
  };
1452
1789
  }
@@ -1455,18 +1792,18 @@ async function retireSkill(directory, slug, reason) {
1455
1792
  return {
1456
1793
  retired: false,
1457
1794
  path: skillPath,
1458
- markerPath: path3.join(directory, ".opencode", "skills", "generated", cleanSlug, "retired.marker"),
1795
+ markerPath: path4.join(directory, ".opencode", "skills", "generated", cleanSlug, "retired.marker"),
1459
1796
  reason: "active skill not found"
1460
1797
  };
1461
1798
  }
1462
- const markerDir = path3.join(directory, ".opencode", "skills", "generated", cleanSlug);
1463
- const markerPath = path3.join(markerDir, "retired.marker");
1799
+ const markerDir = path4.join(directory, ".opencode", "skills", "generated", cleanSlug);
1800
+ const markerPath = path4.join(markerDir, "retired.marker");
1464
1801
  const markerContent = JSON.stringify({
1465
1802
  retiredAt: new Date().toISOString(),
1466
1803
  reason: reason ?? "manual_retire"
1467
1804
  });
1468
- await mkdir3(markerDir, { recursive: true });
1469
- await writeFile3(markerPath, markerContent, "utf-8");
1805
+ await mkdir4(markerDir, { recursive: true });
1806
+ await writeFile4(markerPath, markerContent, "utf-8");
1470
1807
  let clearedLinks = [];
1471
1808
  try {
1472
1809
  clearedLinks = await clearRetiredSkillLinks(directory, skillPath, cleanSlug);
@@ -1490,7 +1827,7 @@ async function clearRetiredSkillLinks(directory, skillFilePath, slug) {
1490
1827
  async function clearSkillLinks(directory, skillFilePath, slug, mode) {
1491
1828
  let content;
1492
1829
  try {
1493
- content = await readFile3(skillFilePath, "utf-8");
1830
+ content = await readFile4(skillFilePath, "utf-8");
1494
1831
  } catch (err) {
1495
1832
  warn(`[skill-generator] clearSkillLinks: failed to read '${skillFilePath}' for slug '${slug}': ${err instanceof Error ? err.message : String(err)}`);
1496
1833
  return [];
@@ -1555,11 +1892,11 @@ async function clearSkillLinks(directory, skillFilePath, slug, mode) {
1555
1892
  return cleared;
1556
1893
  }
1557
1894
  async function markSkillStale(skillDir, reason) {
1558
- await mkdir3(skillDir, { recursive: true });
1559
- await writeFile3(path3.join(skillDir, "stale.marker"), reason, "utf-8");
1895
+ await mkdir4(skillDir, { recursive: true });
1896
+ await writeFile4(path4.join(skillDir, "stale.marker"), reason, "utf-8");
1560
1897
  }
1561
1898
  async function clearSkillStale(skillDir) {
1562
- const markerPath = path3.join(skillDir, "stale.marker");
1899
+ const markerPath = path4.join(skillDir, "stale.marker");
1563
1900
  try {
1564
1901
  await unlink(markerPath);
1565
1902
  } catch (err) {
@@ -1571,10 +1908,10 @@ async function clearSkillStale(skillDir) {
1571
1908
  }
1572
1909
  async function retireOrMarkStale(directory, skillDir, archivedKnowledgeIds) {
1573
1910
  const fs = await import("fs/promises");
1574
- const skillPath = path3.join(skillDir, "SKILL.md");
1911
+ const skillPath = path4.join(skillDir, "SKILL.md");
1575
1912
  if (!existsSync2(skillPath)) {
1576
1913
  await markSkillStale(skillDir, "source knowledge archived, SKILL.md missing");
1577
- const slug2 = path3.basename(skillDir);
1914
+ const slug2 = path4.basename(skillDir);
1578
1915
  return { action: "stale", slug: slug2, skillDir };
1579
1916
  }
1580
1917
  let content;
@@ -1582,18 +1919,18 @@ async function retireOrMarkStale(directory, skillDir, archivedKnowledgeIds) {
1582
1919
  content = await fs.readFile(skillPath, "utf-8");
1583
1920
  } catch {
1584
1921
  await markSkillStale(skillDir, "source knowledge archived, SKILL.md unreadable");
1585
- const slug2 = path3.basename(skillDir);
1922
+ const slug2 = path4.basename(skillDir);
1586
1923
  return { action: "stale", slug: slug2, skillDir };
1587
1924
  }
1588
1925
  const fm = parseDraftFrontmatter(content);
1589
1926
  const sourceIds = fm?.sourceKnowledgeIds ?? [];
1590
1927
  if (sourceIds.length === 0) {
1591
1928
  await markSkillStale(skillDir, "source knowledge archived, no source_knowledge_ids in frontmatter");
1592
- const slug2 = path3.basename(skillDir);
1929
+ const slug2 = path4.basename(skillDir);
1593
1930
  return { action: "stale", slug: slug2, skillDir };
1594
1931
  }
1595
1932
  const allArchived = sourceIds.every((id) => archivedKnowledgeIds.has(id));
1596
- const slug = path3.basename(skillDir);
1933
+ const slug = path4.basename(skillDir);
1597
1934
  if (allArchived) {
1598
1935
  await retireSkill(directory, slug, "all source knowledge entries archived or deleted");
1599
1936
  return { action: "retire", slug, skillDir };
@@ -1623,7 +1960,7 @@ async function regenerateSkill(directory, slug, options = {}) {
1623
1960
  }
1624
1961
  let existingContent;
1625
1962
  try {
1626
- existingContent = await readFile3(skillPath, "utf-8");
1963
+ existingContent = await readFile4(skillPath, "utf-8");
1627
1964
  } catch (err) {
1628
1965
  return {
1629
1966
  regenerated: false,
@@ -1762,7 +2099,7 @@ async function regenerateSkill(directory, slug, options = {}) {
1762
2099
  try {
1763
2100
  await atomicWrite2(skillPath, content);
1764
2101
  await stampSourceEntries(directory, cleanSlug, matchedEntries.map((e) => e.id));
1765
- await clearSkillStale(path3.dirname(activePath(directory, cleanSlug)));
2102
+ await clearSkillStale(path4.dirname(activePath(directory, cleanSlug)));
1766
2103
  } catch (writeErr) {
1767
2104
  return {
1768
2105
  regenerated: false,
@@ -1815,4 +2152,4 @@ var _internals = {
1815
2152
  clearRetiredSkillLinks
1816
2153
  };
1817
2154
 
1818
- export { appendSkillChangelog, evaluateSkillChange, appendRejectedSkillEdit, sanitizeSlug, isValidSlug2 as isValidSlug, proposalPath, activePath, activeRepoRelativePath, proposalRepoRelativePath, generateEvalStub, writeEvalStub, DEFAULT_SKILL_MIN_CONFIDENCE, DEFAULT_SKILL_MIN_CONFIRMATIONS, STRONG_SKILL_OUTCOME_COUNT, HIGH_PRIORITY_SKILL_MIN_CONFIDENCE, selectCandidateEntries, isSkillMaturityEligible, clusterEntries, buildKnowledgeCluster, renderSkillMarkdown, generateSkills, parseDraftFrontmatter, activateProposal, findSkillsBySourceKnowledgeId, findStaleSkillsBySourceKnowledgeId, listSkills, autoApplyProposals, inspectSkill, retireSkill, clearDraftSkillLinks, clearRetiredSkillLinks, markSkillStale, clearSkillStale, retireOrMarkStale, regenerateSkill, _internals };
2155
+ export { advisoryWarn, getDeferredWarnings, BUNDLED_PROJECT_SKILL_ROOT, bundledProjectSkillFileReference, syncBundledProjectSkillsIfMissingAsync, appendSkillChangelog, evaluateSkillChange, appendRejectedSkillEdit, sanitizeSlug, isValidSlug2 as isValidSlug, proposalPath, activePath, activeRepoRelativePath, proposalRepoRelativePath, generateEvalStub, writeEvalStub, DEFAULT_SKILL_MIN_CONFIDENCE, DEFAULT_SKILL_MIN_CONFIRMATIONS, STRONG_SKILL_OUTCOME_COUNT, HIGH_PRIORITY_SKILL_MIN_CONFIDENCE, selectCandidateEntries, isSkillMaturityEligible, clusterEntries, buildKnowledgeCluster, renderSkillMarkdown, generateSkills, parseDraftFrontmatter, activateProposal, findSkillsBySourceKnowledgeId, findStaleSkillsBySourceKnowledgeId, listSkills, autoApplyProposals, inspectSkill, retireSkill, clearDraftSkillLinks, clearRetiredSkillLinks, markSkillStale, clearSkillStale, retireOrMarkStale, regenerateSkill, _internals };