@rhize/skill-forge 0.20.1 → 0.21.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.
package/dist/cli.js CHANGED
@@ -659,7 +659,7 @@ import { Command } from "commander";
659
659
  import { createRequire } from "module";
660
660
 
661
661
  // src/commands/add.ts
662
- import { join as join26 } from "path";
662
+ import { join as join27 } from "path";
663
663
 
664
664
  // src/install/resolve.ts
665
665
  import { existsSync as existsSync3 } from "fs";
@@ -4669,7 +4669,7 @@ function runHandoff(config, installedPath, options = {}) {
4669
4669
  // src/commands/init.ts
4670
4670
  import { existsSync as existsSync19 } from "fs";
4671
4671
  import { homedir as homedir5 } from "os";
4672
- import { join as join25 } from "path";
4672
+ import { join as join26 } from "path";
4673
4673
  import { createInterface as createInterface3 } from "readline/promises";
4674
4674
 
4675
4675
  // src/agents.ts
@@ -5093,7 +5093,7 @@ function readPluginInventory(path) {
5093
5093
 
5094
5094
  // src/commands/audit.ts
5095
5095
  import { existsSync as existsSync18 } from "fs";
5096
- import { join as join24, resolve as resolvePath5 } from "path";
5096
+ import { join as join25, resolve as resolvePath5 } from "path";
5097
5097
  import { createInterface as createInterface2 } from "readline/promises";
5098
5098
 
5099
5099
  // src/decision.ts
@@ -5459,6 +5459,129 @@ function loadUsageSnapshot(pathOrDir, opts = {}) {
5459
5459
  return toSnapshot(parsed, resolved);
5460
5460
  }
5461
5461
 
5462
+ // src/typedFindingTriage.ts
5463
+ import { createHash as createHash3, randomUUID as randomUUID4 } from "crypto";
5464
+ import { closeSync as closeSync3, constants as constants2, lstatSync as lstatSync4, mkdirSync as mkdirSync7, openSync as openSync3, writeFileSync as writeFileSync7 } from "fs";
5465
+ import { join as join24 } from "path";
5466
+ import { performance } from "perf_hooks";
5467
+ function digest(value) {
5468
+ return createHash3("sha256").update(JSON.stringify(value)).digest("hex");
5469
+ }
5470
+ function localEndpoint(baseUrl) {
5471
+ const url = new URL(baseUrl);
5472
+ if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
5473
+ throw new Error("local_endpoint_required");
5474
+ }
5475
+ return new URL("/v1/systemone", url);
5476
+ }
5477
+ async function rankFindingMetadata(findings, baseUrl = "http://127.0.0.1:8000", fetchImpl = fetch) {
5478
+ const metadata = findings.map((finding2) => ({
5479
+ fingerprint: finding2.fingerprint ?? "",
5480
+ severity: finding2.severity,
5481
+ rule: finding2.rule,
5482
+ classification: finding2.classification ?? "none"
5483
+ }));
5484
+ const sourceSha256 = digest(metadata);
5485
+ const candidates = metadata.filter((row) => /^[0-9a-f]{64}$/.test(row.fingerprint) && /^[a-z0-9-]{1,64}$/.test(row.rule)).slice(0, 8);
5486
+ const base = {
5487
+ schema: "skill-forge-typed-triage-v1",
5488
+ sourceSha256,
5489
+ evaluated: candidates.length,
5490
+ deferred: findings.length - candidates.length,
5491
+ gateChanged: false
5492
+ };
5493
+ if (!candidates.length) return { ...base, status: "not_applicable", variant: "A_incumbent" };
5494
+ const questions = Object.fromEntries(candidates.map((row, index) => [
5495
+ `c${index}`,
5496
+ { type: "noul", instructions: `Would reviewing finding ${row.fingerprint.slice(0, 16)} first improve triage? Do not downgrade or accept it.` }
5497
+ ]));
5498
+ const request = { model: "typed-decisions", state: { findings: candidates.map((row) => ({
5499
+ id: row.fingerprint.slice(0, 16),
5500
+ severity: row.severity,
5501
+ rule: row.rule,
5502
+ classification: row.classification
5503
+ })) }, questions };
5504
+ const controller = new AbortController();
5505
+ const timeout = setTimeout(() => controller.abort(), 8e3);
5506
+ const start = performance.now();
5507
+ try {
5508
+ const response = await fetchImpl(localEndpoint(baseUrl), {
5509
+ method: "POST",
5510
+ headers: { "Content-Type": "application/json" },
5511
+ body: JSON.stringify(request),
5512
+ signal: controller.signal
5513
+ });
5514
+ if (!response.ok) throw new Error("provider_http_error");
5515
+ if (!response.body) throw new Error("empty_response");
5516
+ const reader = response.body.getReader();
5517
+ const decoder = new TextDecoder();
5518
+ let raw = "";
5519
+ let total = 0;
5520
+ for (; ; ) {
5521
+ const chunk = await reader.read();
5522
+ if (chunk.done) break;
5523
+ total += chunk.value.byteLength;
5524
+ if (total > 262144) {
5525
+ await reader.cancel();
5526
+ throw new Error("oversized_response");
5527
+ }
5528
+ raw += decoder.decode(chunk.value, { stream: true });
5529
+ }
5530
+ raw += decoder.decode();
5531
+ const data = JSON.parse(raw);
5532
+ const routing = data.routing;
5533
+ const answers = data.answers;
5534
+ const usage = data.usage;
5535
+ if (routing?.model !== request.model || !answers || Object.keys(answers).sort().join() !== Object.keys(questions).sort().join()) {
5536
+ throw new Error("route_or_answer_mismatch");
5537
+ }
5538
+ if (!usage || !Number.isSafeInteger(usage.input_tokens) || !Number.isSafeInteger(usage.output_tokens) || Number(usage.input_tokens) < 0 || Number(usage.output_tokens) < 0) throw new Error("invalid_usage");
5539
+ const ranking = candidates.map((row, index) => {
5540
+ const answer = answers[`c${index}`];
5541
+ const score4 = answer?.type === "noul" ? answer.noul : void 0;
5542
+ if (typeof score4 !== "number" || !Number.isFinite(score4) || score4 < 0 || score4 > 1) throw new Error("invalid_score");
5543
+ return { fingerprint: row.fingerprint, severity: row.severity, score: score4 };
5544
+ }).sort((a, b) => b.score - a.score || a.fingerprint.localeCompare(b.fingerprint));
5545
+ return {
5546
+ ...base,
5547
+ status: "shadow",
5548
+ variant: "B_local_laya",
5549
+ ranking,
5550
+ routedModel: String(routing.model),
5551
+ latencyMs: Math.round((performance.now() - start) * 1e3) / 1e3,
5552
+ usage: { input_tokens: Number(usage.input_tokens), output_tokens: Number(usage.output_tokens) }
5553
+ };
5554
+ } catch (error) {
5555
+ const known = /* @__PURE__ */ new Set([
5556
+ "local_endpoint_required",
5557
+ "provider_http_error",
5558
+ "empty_response",
5559
+ "oversized_response",
5560
+ "route_or_answer_mismatch",
5561
+ "invalid_usage",
5562
+ "invalid_score"
5563
+ ]);
5564
+ const reasonCode = error instanceof Error && known.has(error.message) ? error.message : "provider_unavailable";
5565
+ return { ...base, status: "unavailable", variant: "A_incumbent", reasonCode };
5566
+ } finally {
5567
+ clearTimeout(timeout);
5568
+ }
5569
+ }
5570
+ function writeTypedTriageReceipt(home, result) {
5571
+ const dir = join24(home, "typed-finding-triage");
5572
+ mkdirSync7(dir, { recursive: true, mode: 448 });
5573
+ if (lstatSync4(dir).isSymbolicLink()) throw new Error("receipt_root_symlink");
5574
+ const data = JSON.stringify(result) + "\n";
5575
+ const path = join24(dir, `${randomUUID4()}.json`);
5576
+ const fd = openSync3(path, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL | (constants2.O_NOFOLLOW ?? 0), 384);
5577
+ try {
5578
+ writeFileSync7(fd, data);
5579
+ } finally {
5580
+ closeSync3(fd);
5581
+ }
5582
+ return createHash3("sha256").update(data).digest("hex");
5583
+ }
5584
+
5462
5585
  // src/commands/audit.ts
5463
5586
  var CURATION_PROMPT_ASSET = "curation-prompt.md";
5464
5587
  function resolvePluginAndUsageContext(config, options, deps = {}) {
@@ -5515,7 +5638,7 @@ async function captureBusinessProfile(rl, existing) {
5515
5638
  function resolveReportPath(reportOption) {
5516
5639
  if (reportOption) return resolvePath5(reportOption);
5517
5640
  const iso = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5518
- return join24(getHomeDir(), "reports", `audit-${iso}.md`);
5641
+ return join25(getHomeDir(), "reports", `audit-${iso}.md`);
5519
5642
  }
5520
5643
  function summarizePluginRecommendations(plugins2) {
5521
5644
  const counts = { keep: 0, review: 0, unobserved: 0, unknown: 0 };
@@ -5603,6 +5726,18 @@ async function auditCommand(options = {}, deps = {}) {
5603
5726
  const extraNotices = [...storeNotices, ...pluginUsageNotices];
5604
5727
  if (extraNotices.length > 0) report.notices = [...report.notices, ...extraNotices];
5605
5728
  if (options.compare) report.comparison = loadAuditComparison(report, options.compare);
5729
+ if (options.layaShadow) {
5730
+ const triage = await rankFindingMetadata(report.hygiene, options.layaBaseUrl);
5731
+ try {
5732
+ triage.receiptSha256 = writeTypedTriageReceipt(getHomeDir(), triage);
5733
+ } catch {
5734
+ triage.status = "unavailable";
5735
+ triage.variant = "A_incumbent";
5736
+ triage.reasonCode = "receipt_unavailable";
5737
+ delete triage.ranking;
5738
+ }
5739
+ report.typedFindingTriage = triage;
5740
+ }
5606
5741
  const reportPath = resolveReportPath(options.report);
5607
5742
  writeNewFile(reportPath, renderAuditMarkdown(report), "--report");
5608
5743
  try {
@@ -5931,7 +6066,7 @@ async function init(options = {}, deps = {}) {
5931
6066
  "[skill-forge] No detected agent CLI was found on PATH \u2014 keeping every detected target selected."
5932
6067
  );
5933
6068
  }
5934
- const installedPluginsPath = options.installedPluginsPath ?? join25(home, ".claude", "plugins", "installed_plugins.json");
6069
+ const installedPluginsPath = options.installedPluginsPath ?? join26(home, ".claude", "plugins", "installed_plugins.json");
5935
6070
  const claudePluginsRegistryExists = existsSync19(installedPluginsPath);
5936
6071
  if (options.defaults) {
5937
6072
  const selectedRoots = narrow ? rootGroups.filter(hasInstalledMember) : rootGroups;
@@ -6142,7 +6277,7 @@ async function addMcp(source, config, options) {
6142
6277
  envVars: detail.env ? Object.keys(detail.env) : void 0,
6143
6278
  candidateEntry: detail.command ? { command: detail.command, args: detail.args, env: detail.env } : void 0
6144
6279
  };
6145
- const mcpTargetPath = options.mcpTarget ?? config.mcpTargets?.[0] ?? join26(process.cwd(), ".mcp.json");
6280
+ const mcpTargetPath = options.mcpTarget ?? config.mcpTargets?.[0] ?? join27(process.cwd(), ".mcp.json");
6146
6281
  const result = promoteMcp(entry, serverProfile, mcpTargetPath, { force: options.force });
6147
6282
  reject(entry);
6148
6283
  logOutcome(`[skill-forge] promoted -> ${result.target} (server "${result.serverName}")`, options.json);
@@ -6238,7 +6373,7 @@ async function scan(source, options = {}) {
6238
6373
  }
6239
6374
 
6240
6375
  // src/commands/list.ts
6241
- import { join as join27 } from "path";
6376
+ import { join as join28 } from "path";
6242
6377
  async function list() {
6243
6378
  await maybeOfferInit();
6244
6379
  const config = loadConfig();
@@ -6251,14 +6386,14 @@ async function list() {
6251
6386
  }
6252
6387
  console.log(`[skill-forge] ${ids.length} held in quarantine (${quarantineDir}):`);
6253
6388
  for (const id of ids) {
6254
- const skillDirs = findSkillDirs(join27(quarantineDir, id));
6389
+ const skillDirs = findSkillDirs(join28(quarantineDir, id));
6255
6390
  if (skillDirs.length === 0) {
6256
6391
  console.log(` ${id} (no SKILL.md found)`);
6257
6392
  continue;
6258
6393
  }
6259
6394
  const names = skillDirs.map((dir) => {
6260
6395
  try {
6261
- return readFrontmatter(join27(dir, "SKILL.md")).name;
6396
+ return readFrontmatter(join28(dir, "SKILL.md")).name;
6262
6397
  } catch {
6263
6398
  return dir;
6264
6399
  }
@@ -6272,13 +6407,13 @@ import { existsSync as existsSync21 } from "fs";
6272
6407
 
6273
6408
  // src/commands/held.ts
6274
6409
  import { existsSync as existsSync20, realpathSync as realpathSync6 } from "fs";
6275
- import { basename as basename9, join as join28, resolve as resolvePath6, sep as sep7 } from "path";
6410
+ import { basename as basename9, join as join29, resolve as resolvePath6, sep as sep7 } from "path";
6276
6411
  var PRO_FEATURE_LABEL2 = "overlap analysis, the provenance ledger, and the pending-ingestion queue";
6277
6412
  function resolveHeldPath(quarantineDir, id) {
6278
6413
  if (id === "" || id !== basename9(id) || id === "." || id === "..") {
6279
6414
  throw new UserError(`invalid quarantine id "${id}" \u2014 pass a single id exactly as printed by \`skill-forge list\``);
6280
6415
  }
6281
- const candidate = join28(quarantineDir, id);
6416
+ const candidate = join29(quarantineDir, id);
6282
6417
  if (!existsSync20(candidate)) {
6283
6418
  throw new UserError(`no held entry with id "${id}" \u2014 run \`skill-forge list\` to see what is in quarantine`);
6284
6419
  }
@@ -6307,7 +6442,7 @@ function resolveTargetRoot(config, target) {
6307
6442
  function heldSkillNames(skillDirs) {
6308
6443
  return skillDirs.map((dir) => {
6309
6444
  try {
6310
- return readFrontmatter(join28(dir, "SKILL.md")).name;
6445
+ return readFrontmatter(join29(dir, "SKILL.md")).name;
6311
6446
  } catch {
6312
6447
  return basename9(dir);
6313
6448
  }
@@ -6315,7 +6450,7 @@ function heldSkillNames(skillDirs) {
6315
6450
  }
6316
6451
  function looksLikeMcpSandbox(quarantinePath) {
6317
6452
  const candidate = resolveMcpCandidatePath({ quarantinePath });
6318
- return [".mcp.json", "mcp.json", "package.json"].some((f) => existsSync20(join28(candidate, f)));
6453
+ return [".mcp.json", "mcp.json", "package.json"].some((f) => existsSync20(join29(candidate, f)));
6319
6454
  }
6320
6455
  async function promoteHeldMcp(id, entry, config, options) {
6321
6456
  const candidatePath = resolveMcpCandidatePath(entry);
@@ -6349,7 +6484,7 @@ async function promoteHeldMcp(id, entry, config, options) {
6349
6484
  envVars: detail.env ? Object.keys(detail.env) : void 0,
6350
6485
  candidateEntry: detail.command ? { command: detail.command, args: detail.args, env: detail.env } : void 0
6351
6486
  };
6352
- const mcpTargetPath = options.mcpTarget ?? config.mcpTargets?.[0] ?? join28(process.cwd(), ".mcp.json");
6487
+ const mcpTargetPath = options.mcpTarget ?? config.mcpTargets?.[0] ?? join29(process.cwd(), ".mcp.json");
6353
6488
  const result = promoteMcp(entry, serverProfile, mcpTargetPath, { force: options.force });
6354
6489
  reject(entry);
6355
6490
  logOutcome(`[skill-forge] promoted -> ${result.target} (server "${result.serverName}")`, options.json);
@@ -6827,17 +6962,17 @@ async function status() {
6827
6962
 
6828
6963
  // src/evolve.ts
6829
6964
  import { createInterface as createInterface5 } from "readline/promises";
6830
- import { createHash as createHash3 } from "crypto";
6831
- import { cpSync as cpSync3, existsSync as existsSync22, lstatSync as lstatSync4, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, rmSync as rmSync6 } from "fs";
6965
+ import { createHash as createHash4 } from "crypto";
6966
+ import { cpSync as cpSync3, existsSync as existsSync22, lstatSync as lstatSync5, mkdtempSync as mkdtempSync3, readFileSync as readFileSync21, rmSync as rmSync6 } from "fs";
6832
6967
  import { tmpdir as tmpdir2 } from "os";
6833
- import { basename as basename10, dirname as dirname9, join as join29, resolve as resolvePath7, sep as sep8 } from "path";
6968
+ import { basename as basename10, dirname as dirname9, join as join30, resolve as resolvePath7, sep as sep8 } from "path";
6834
6969
  var SKILLOPT_BIN = "skillopt-sleep";
6835
6970
  var PRO_FEATURE_LABEL3 = "self-evolution via SkillOpt-Sleep (evolve) \u2014 re-gating, provenance, and the pending-ingestion queue";
6836
6971
  async function evolve(targetSkillDir, options = {}, deps = {}) {
6837
6972
  const exec = deps.exec ?? defaultExec;
6838
6973
  const json = options.json;
6839
6974
  const resolvedTargetDir = resolvePath7(targetSkillDir);
6840
- const skillMdPath = join29(resolvedTargetDir, "SKILL.md");
6975
+ const skillMdPath = join30(resolvedTargetDir, "SKILL.md");
6841
6976
  if (!existsSync22(skillMdPath)) {
6842
6977
  throw new UserError(`no SKILL.md found at ${skillMdPath} \u2014 evolve requires an existing, installed skill directory`);
6843
6978
  }
@@ -6896,16 +7031,16 @@ async function evolve(targetSkillDir, options = {}, deps = {}) {
6896
7031
  }
6897
7032
  return;
6898
7033
  }
6899
- const stagingRoot = join29(project, ".skillopt-sleep", "staging");
7034
+ const stagingRoot = join30(project, ".skillopt-sleep", "staging");
6900
7035
  const stagingDir = resolvePath7(parsed.staging_dir);
6901
7036
  if (stagingDir !== stagingRoot && !stagingDir.startsWith(stagingRoot + sep8)) {
6902
7037
  throw new UserError(
6903
7038
  `skillopt-sleep reported a staging_dir outside the expected sandbox (${stagingRoot}): ${stagingDir} \u2014 refusing to use it`
6904
7039
  );
6905
7040
  }
6906
- const proposedSkillMd = join29(stagingDir, "proposed_SKILL.md");
6907
- const proposedClaudeMd = join29(stagingDir, "proposed_CLAUDE.md");
6908
- const reportMdPath = join29(stagingDir, "report.md");
7041
+ const proposedSkillMd = join30(stagingDir, "proposed_SKILL.md");
7042
+ const proposedClaudeMd = join30(stagingDir, "proposed_CLAUDE.md");
7043
+ const reportMdPath = join30(stagingDir, "report.md");
6909
7044
  if (!existsSync22(proposedSkillMd)) {
6910
7045
  throw new UserError(`skillopt-sleep reported an accepted proposal but ${proposedSkillMd} is missing`);
6911
7046
  }
@@ -6916,13 +7051,13 @@ async function evolve(targetSkillDir, options = {}, deps = {}) {
6916
7051
  assertNotSymlink(proposedClaudeMd, "proposed_CLAUDE.md");
6917
7052
  const scannedSkillHash = sha256File(proposedSkillMd);
6918
7053
  const scannedClaudeHash = sha256File(proposedClaudeMd);
6919
- const tempDir = mkdtempSync3(join29(tmpdir2(), "skill-forge-evolve-"));
7054
+ const tempDir = mkdtempSync3(join30(tmpdir2(), "skill-forge-evolve-"));
6920
7055
  let profile;
6921
7056
  let safety;
6922
7057
  try {
6923
- cpSync3(proposedSkillMd, join29(tempDir, "SKILL.md"));
7058
+ cpSync3(proposedSkillMd, join30(tempDir, "SKILL.md"));
6924
7059
  if (existsSync22(proposedClaudeMd)) {
6925
- cpSync3(proposedClaudeMd, join29(tempDir, "CLAUDE.md"));
7060
+ cpSync3(proposedClaudeMd, join30(tempDir, "CLAUDE.md"));
6926
7061
  }
6927
7062
  profile = asSkillProfile(profileSkill(tempDir), "for the evolved proposal");
6928
7063
  const strictness = loadConfig().strictness;
@@ -7063,7 +7198,7 @@ function validateRunOutput(raw) {
7063
7198
  return { accepted: Boolean(obj.accepted), staging_dir: stagingDirRaw ?? null };
7064
7199
  }
7065
7200
  function readManifest(stagingDir) {
7066
- const manifestPath = join29(stagingDir, "manifest.json");
7201
+ const manifestPath = join30(stagingDir, "manifest.json");
7067
7202
  let raw;
7068
7203
  try {
7069
7204
  raw = readFileSync21(manifestPath, "utf-8");
@@ -7104,8 +7239,8 @@ function filesIdentical(a, b) {
7104
7239
  return readFileSync21(a).equals(readFileSync21(b));
7105
7240
  }
7106
7241
  function isAlreadyAdopted(stagingDir, manifest) {
7107
- const proposedSkillMd = join29(stagingDir, "proposed_SKILL.md");
7108
- const proposedClaudeMd = join29(stagingDir, "proposed_CLAUDE.md");
7242
+ const proposedSkillMd = join30(stagingDir, "proposed_SKILL.md");
7243
+ const proposedClaudeMd = join30(stagingDir, "proposed_CLAUDE.md");
7109
7244
  let sawComparison = false;
7110
7245
  if (existsSync22(proposedSkillMd)) {
7111
7246
  sawComparison = true;
@@ -7120,7 +7255,7 @@ function isAlreadyAdopted(stagingDir, manifest) {
7120
7255
  function assertNotSymlink(path, label2) {
7121
7256
  let stat;
7122
7257
  try {
7123
- stat = lstatSync4(path);
7258
+ stat = lstatSync5(path);
7124
7259
  } catch {
7125
7260
  return;
7126
7261
  }
@@ -7130,7 +7265,7 @@ function assertNotSymlink(path, label2) {
7130
7265
  }
7131
7266
  function sha256File(path) {
7132
7267
  if (!existsSync22(path)) return null;
7133
- return createHash3("sha256").update(readFileSync21(path)).digest("hex");
7268
+ return createHash4("sha256").update(readFileSync21(path)).digest("hex");
7134
7269
  }
7135
7270
 
7136
7271
  // src/commands/evolve.ts
@@ -7249,7 +7384,7 @@ import { resolve as resolvePath9 } from "path";
7249
7384
 
7250
7385
  // src/organize.ts
7251
7386
  import { existsSync as existsSync24, readFileSync as readFileSync22, readdirSync as readdirSync10, realpathSync as realpathSync7, statSync as statSync10 } from "fs";
7252
- import { basename as basename11, join as join30, resolve as resolvePath8 } from "path";
7387
+ import { basename as basename11, join as join31, resolve as resolvePath8 } from "path";
7253
7388
  var PRO_FEATURE_LABEL4 = "set-level organizer reports";
7254
7389
  var TIERS = /* @__PURE__ */ new Set(["resource", "custom"]);
7255
7390
  var MATURITIES = /* @__PURE__ */ new Set(["seedling", "stable", "deprecated", "experimental"]);
@@ -7262,7 +7397,7 @@ function listSubdirs2(dir) {
7262
7397
  }
7263
7398
  const out = [];
7264
7399
  for (const entry of entries) {
7265
- const full = join30(dir, entry.name);
7400
+ const full = join31(dir, entry.name);
7266
7401
  if (entry.isDirectory()) {
7267
7402
  out.push(full);
7268
7403
  continue;
@@ -7280,14 +7415,14 @@ function findSkillDirsUnderRoot2(root) {
7280
7415
  const found = /* @__PURE__ */ new Set();
7281
7416
  const topDirs = listSubdirs2(root);
7282
7417
  for (const dir of topDirs) {
7283
- if (existsSync24(join30(dir, "SKILL.md"))) found.add(dir);
7418
+ if (existsSync24(join31(dir, "SKILL.md"))) found.add(dir);
7284
7419
  }
7285
- for (const dir of listSubdirs2(join30(root, "skills"))) {
7286
- if (existsSync24(join30(dir, "SKILL.md"))) found.add(dir);
7420
+ for (const dir of listSubdirs2(join31(root, "skills"))) {
7421
+ if (existsSync24(join31(dir, "SKILL.md"))) found.add(dir);
7287
7422
  }
7288
7423
  for (const topDir of topDirs) {
7289
- for (const dir of listSubdirs2(join30(topDir, "skills"))) {
7290
- if (existsSync24(join30(dir, "SKILL.md"))) found.add(dir);
7424
+ for (const dir of listSubdirs2(join31(topDir, "skills"))) {
7425
+ if (existsSync24(join31(dir, "SKILL.md"))) found.add(dir);
7291
7426
  }
7292
7427
  }
7293
7428
  return Array.from(found).filter((p) => !p.includes("/.git/"));
@@ -7490,7 +7625,7 @@ function buildRegistryEntries(candidates, warnings) {
7490
7625
  }
7491
7626
  if (seenRealPaths.has(realPath)) continue;
7492
7627
  seenRealPaths.add(realPath);
7493
- const skillMdPath = join30(realPath, "SKILL.md");
7628
+ const skillMdPath = join31(realPath, "SKILL.md");
7494
7629
  let text;
7495
7630
  try {
7496
7631
  text = readFileSync22(skillMdPath, "utf-8");
@@ -7900,14 +8035,14 @@ async function find(query, options = {}, deps = {}) {
7900
8035
 
7901
8036
  // src/commands/watch.ts
7902
8037
  import { realpathSync as realpathSync8 } from "fs";
7903
- import { join as join31, resolve as resolvePath10 } from "path";
8038
+ import { join as join32, resolve as resolvePath10 } from "path";
7904
8039
 
7905
8040
  // src/gate/skillMapDrift.ts
7906
- import { createHash as createHash4 } from "crypto";
8041
+ import { createHash as createHash5 } from "crypto";
7907
8042
  import { readFileSync as readFileSync23, existsSync as existsSync25 } from "fs";
7908
8043
  import { dirname as dirname10, isAbsolute as isAbsolute6, resolve as resolve8 } from "path";
7909
8044
  function sha256(text) {
7910
- return createHash4("sha256").update(text, "utf-8").digest("hex");
8045
+ return createHash5("sha256").update(text, "utf-8").digest("hex");
7911
8046
  }
7912
8047
  function shortHash(hash) {
7913
8048
  return hash.slice(0, 12);
@@ -8117,7 +8252,7 @@ async function runWatch(config, options = {}, deps = {}) {
8117
8252
  ledger = readProvenanceLedger(resolvedRoot);
8118
8253
  } catch (err) {
8119
8254
  const message = err instanceof Error ? err.message : String(err);
8120
- const ledgerPath = join31(resolvedRoot, "SOURCES.md");
8255
+ const ledgerPath = join32(resolvedRoot, "SOURCES.md");
8121
8256
  roots.push({ root, realPath, exists, ledgerPath, ledgerExists: false });
8122
8257
  warnings.push(`${ledgerPath}: could not read ledger: ${message}`);
8123
8258
  continue;
@@ -8228,7 +8363,7 @@ async function watchCommand(options = {}) {
8228
8363
 
8229
8364
  // src/commands/routine.ts
8230
8365
  import { existsSync as existsSync26, rmSync as rmSync7, statSync as statSync11 } from "fs";
8231
- import { join as join32 } from "path";
8366
+ import { join as join33 } from "path";
8232
8367
  var PRO_FEATURE_LABEL6 = "the scheduled maintenance routine";
8233
8368
  var SEVERITY_ORDER = ["LOW", "MEDIUM", "HIGH", "CRITICAL"];
8234
8369
  function parseFailOnFloor(failOn) {
@@ -8249,7 +8384,7 @@ function exceedsFloor(findings, failOn) {
8249
8384
  }
8250
8385
  function resolveReportPath2(override, generatedAt) {
8251
8386
  if (override) return override;
8252
- return join32(getHomeDir(), "reports", `routine-${generatedAt.replace(/[:.]/g, "-")}.md`);
8387
+ return join33(getHomeDir(), "reports", `routine-${generatedAt.replace(/[:.]/g, "-")}.md`);
8253
8388
  }
8254
8389
  function runHousekeeping(config, options) {
8255
8390
  const actions = [];
@@ -8258,7 +8393,7 @@ function runHousekeeping(config, options) {
8258
8393
  const pruneDays = options.pruneDays ?? 30;
8259
8394
  const cutoff = Date.now() - pruneDays * 864e5;
8260
8395
  for (const id of listQuarantineIds(config.quarantineDir)) {
8261
- const path = join32(config.quarantineDir, id);
8396
+ const path = join33(config.quarantineDir, id);
8262
8397
  let mtime;
8263
8398
  try {
8264
8399
  mtime = statSync11(path).mtimeMs;
@@ -8644,20 +8779,20 @@ async function queueClose(id, options = {}) {
8644
8779
  import { createInterface as createInterface6 } from "readline/promises";
8645
8780
  import {
8646
8781
  chmodSync as chmodSync3,
8647
- lstatSync as lstatSync6,
8648
- mkdirSync as mkdirSync9,
8782
+ lstatSync as lstatSync7,
8783
+ mkdirSync as mkdirSync10,
8649
8784
  readFileSync as readFileSync27,
8650
8785
  readSync as readSync3,
8651
- writeFileSync as writeFileSync8
8786
+ writeFileSync as writeFileSync9
8652
8787
  } from "fs";
8653
- import { dirname as dirname13, isAbsolute as isAbsolute9, join as join37, relative as relative8, resolve as resolvePath12 } from "path";
8788
+ import { dirname as dirname13, isAbsolute as isAbsolute9, join as join38, relative as relative8, resolve as resolvePath12 } from "path";
8654
8789
 
8655
8790
  // src/insights/inventory.ts
8656
8791
  import { readFileSync as readFileSync24 } from "fs";
8657
- import { join as join33 } from "path";
8792
+ import { join as join34 } from "path";
8658
8793
 
8659
8794
  // src/insights/models.ts
8660
- import { createHash as createHash5 } from "crypto";
8795
+ import { createHash as createHash6 } from "crypto";
8661
8796
  var INSIGHT_SCHEMA_VERSION = 1;
8662
8797
  var MAX_EVIDENCE_EXCERPT_CHARS = 500;
8663
8798
  var SOURCE_TYPES = ["url", "file", "directory", "connector", "stdin"];
@@ -8709,7 +8844,7 @@ function canonicalJson(value) {
8709
8844
  return JSON.stringify(normalize(value));
8710
8845
  }
8711
8846
  function stableHash(value) {
8712
- return createHash5("sha256").update(canonicalJson(value)).digest("hex");
8847
+ return createHash6("sha256").update(canonicalJson(value)).digest("hex");
8713
8848
  }
8714
8849
  function stableId(prefix, value, length = 16) {
8715
8850
  if (!/^[A-Z][A-Z0-9-]*$/.test(prefix)) {
@@ -8768,7 +8903,7 @@ function declaredMapCoverage(path, claim) {
8768
8903
  }
8769
8904
  function fingerprintInstalledSkill(path) {
8770
8905
  try {
8771
- return { fingerprint: stableHash(readFileSync24(join33(path, "SKILL.md"), "utf-8")) };
8906
+ return { fingerprint: stableHash(readFileSync24(join34(path, "SKILL.md"), "utf-8")) };
8772
8907
  } catch (error) {
8773
8908
  return { reason: `could not fingerprint SKILL.md: ${error.message}` };
8774
8909
  }
@@ -8919,26 +9054,26 @@ function buildInsightInventory(config, options = {}) {
8919
9054
 
8920
9055
  // src/insights/jira.ts
8921
9056
  import { existsSync as existsSync28, readFileSync as readFileSync26 } from "fs";
8922
- import { join as join36 } from "path";
9057
+ import { join as join37 } from "path";
8923
9058
 
8924
9059
  // src/insights/store.ts
8925
- import { chmodSync as chmodSync2, existsSync as existsSync27, mkdirSync as mkdirSync8, readFileSync as readFileSync25 } from "fs";
8926
- import { join as join35 } from "path";
9060
+ import { chmodSync as chmodSync2, existsSync as existsSync27, mkdirSync as mkdirSync9, readFileSync as readFileSync25 } from "fs";
9061
+ import { join as join36 } from "path";
8927
9062
 
8928
9063
  // src/insights/sources.ts
8929
- import { createHash as createHash6 } from "crypto";
9064
+ import { createHash as createHash7 } from "crypto";
8930
9065
  import {
8931
- closeSync as closeSync3,
8932
- constants as constants2,
9066
+ closeSync as closeSync4,
9067
+ constants as constants3,
8933
9068
  fstatSync as fstatSync2,
8934
- lstatSync as lstatSync5,
8935
- openSync as openSync3,
9069
+ lstatSync as lstatSync6,
9070
+ openSync as openSync4,
8936
9071
  readSync as readSync2,
8937
9072
  readdirSync as readdirSync11,
8938
9073
  realpathSync as realpathSync10,
8939
9074
  statSync as statSync12
8940
9075
  } from "fs";
8941
- import { basename as basename12, extname as extname8, isAbsolute as isAbsolute8, join as join34, relative as relative7, resolve as resolve9, sep as sep9 } from "path";
9076
+ import { basename as basename12, extname as extname8, isAbsolute as isAbsolute8, join as join35, relative as relative7, resolve as resolve9, sep as sep9 } from "path";
8942
9077
  var DEFAULT_MAX_SOURCE_FILE_BYTES = 5 * 1024 * 1024;
8943
9078
  var DEFAULT_MAX_SOURCE_TOTAL_BYTES = 25 * 1024 * 1024;
8944
9079
  var SKIP_DIRS4 = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", ".next", "out", "__pycache__", ".venv", "venv"]);
@@ -9037,12 +9172,12 @@ function observeFile(path, relativePath, declaredMediaType, maxFileBytes, remain
9037
9172
  }
9038
9173
  const mediaType = mediaTypeFor(path, declaredMediaType);
9039
9174
  if (!mediaType) return { path: relativePath, reason: "unsupported-media-type" };
9040
- const noFollow = typeof constants2.O_NOFOLLOW === "number" ? constants2.O_NOFOLLOW : 0;
9175
+ const noFollow = typeof constants3.O_NOFOLLOW === "number" ? constants3.O_NOFOLLOW : 0;
9041
9176
  let descriptor;
9042
9177
  let bytes;
9043
9178
  let descriptorStat;
9044
9179
  try {
9045
- descriptor = openSync3(path, constants2.O_RDONLY | noFollow);
9180
+ descriptor = openSync4(path, constants3.O_RDONLY | noFollow);
9046
9181
  descriptorStat = fstatSync2(descriptor);
9047
9182
  if (!descriptorStat.isFile()) return { path: relativePath, reason: "unsupported-media-type" };
9048
9183
  if (descriptorStat.size > maxFileBytes) {
@@ -9069,7 +9204,7 @@ function observeFile(path, relativePath, declaredMediaType, maxFileBytes, remain
9069
9204
  } catch (error) {
9070
9205
  throw new UserError(`unable to read source file ${relativePath}: ${error.message}`);
9071
9206
  } finally {
9072
- if (descriptor !== void 0) closeSync3(descriptor);
9207
+ if (descriptor !== void 0) closeSync4(descriptor);
9073
9208
  }
9074
9209
  if (!bytes || !descriptorStat) throw new UserError(`source file could not be observed: ${relativePath}`);
9075
9210
  let realPath;
@@ -9089,7 +9224,7 @@ function observeFile(path, relativePath, declaredMediaType, maxFileBytes, remain
9089
9224
  realPath,
9090
9225
  relativePath,
9091
9226
  byteLength: bytes.byteLength,
9092
- sha256: createHash6("sha256").update(bytes).digest("hex"),
9227
+ sha256: createHash7("sha256").update(bytes).digest("hex"),
9093
9228
  mediaType,
9094
9229
  suspiciousInstructionFlags: suspiciousInstructionFlags(bytes, mediaType),
9095
9230
  bytes
@@ -9173,7 +9308,7 @@ function normalizeStdin(input, retrievedAt, maxFileBytes, remainingBytes) {
9173
9308
  const bytes = Buffer.from(input.content);
9174
9309
  if (bytes.byteLength > maxFileBytes) throw new UserError(`stdin source exceeds the per-source byte limit (${maxFileBytes})`);
9175
9310
  if (bytes.byteLength > remainingBytes) throw new UserError(`stdin source exceeds the total source byte limit`);
9176
- const contentSha256 = createHash6("sha256").update(bytes).digest("hex");
9311
+ const contentSha256 = createHash7("sha256").update(bytes).digest("hex");
9177
9312
  const mediaType = input.mediaType ?? "text/plain";
9178
9313
  const fingerprint = stableHash({ type: input.type, contentSha256, mediaType });
9179
9314
  const common = normalizedCommon(input, retrievedAt);
@@ -9199,7 +9334,7 @@ function normalizeFile(input, retrievedAt, maxFileBytes, remainingBytes) {
9199
9334
  const resolved = resolve9(input.path);
9200
9335
  let lexicalStat;
9201
9336
  try {
9202
- lexicalStat = lstatSync5(resolved);
9337
+ lexicalStat = lstatSync6(resolved);
9203
9338
  } catch (err) {
9204
9339
  throw new UserError(`unable to read source file ${resolved}: ${err.message}`);
9205
9340
  }
@@ -9250,7 +9385,7 @@ function normalizeDirectory(input, retrievedAt, maxFileBytes, remainingBytes) {
9250
9385
  const resolved = resolve9(input.path);
9251
9386
  let lexicalStat;
9252
9387
  try {
9253
- lexicalStat = lstatSync5(resolved);
9388
+ lexicalStat = lstatSync6(resolved);
9254
9389
  } catch (err) {
9255
9390
  throw new UserError(`unable to read source directory ${resolved}: ${err.message}`);
9256
9391
  }
@@ -9270,7 +9405,7 @@ function normalizeDirectory(input, retrievedAt, maxFileBytes, remainingBytes) {
9270
9405
  throw new UserError(`unable to enumerate source directory ${dir}: ${err.message}`);
9271
9406
  }
9272
9407
  for (const entry of entries) {
9273
- const full = join34(dir, entry.name);
9408
+ const full = join35(dir, entry.name);
9274
9409
  const logicalPath = logicalDir ? `${logicalDir}/${entry.name}` : entry.name;
9275
9410
  if (entry.isDirectory() && SKIP_DIRS4.has(entry.name)) {
9276
9411
  skipped.push({ path: logicalPath, reason: "ignored-directory" });
@@ -9336,7 +9471,7 @@ function normalizeDirectory(input, retrievedAt, maxFileBytes, remainingBytes) {
9336
9471
  walk2(realRoot, "");
9337
9472
  observations.sort((a, b) => compareStrings2(a.relativePath, b.relativePath));
9338
9473
  skipped.sort((a, b) => compareStrings2(a.path, b.path) || compareStrings2(a.reason, b.reason));
9339
- const contentSha256 = createHash6("sha256");
9474
+ const contentSha256 = createHash7("sha256");
9340
9475
  for (const file of observations) contentSha256.update(`${file.relativePath}\0${file.sha256}
9341
9476
  `);
9342
9477
  const aggregateHash = contentSha256.digest("hex");
@@ -9422,17 +9557,17 @@ function normalizeSources(inputs, options = {}) {
9422
9557
  // src/insights/store.ts
9423
9558
  var STUDY_ID = /^STUDY-[0-9a-f]{16}$/;
9424
9559
  function getInsightsDir() {
9425
- return join35(getHomeDir(), "insights");
9560
+ return join36(getHomeDir(), "insights");
9426
9561
  }
9427
9562
  function getStudyDir(studyId) {
9428
9563
  assertStudyId(studyId);
9429
- return join35(getInsightsDir(), studyId);
9564
+ return join36(getInsightsDir(), studyId);
9430
9565
  }
9431
9566
  function getStudyPath(studyId) {
9432
- return join35(getStudyDir(studyId), "study.json");
9567
+ return join36(getStudyDir(studyId), "study.json");
9433
9568
  }
9434
9569
  function getStudyEventsPath(studyId) {
9435
- return join35(getStudyDir(studyId), "events.jsonl");
9570
+ return join36(getStudyDir(studyId), "events.jsonl");
9436
9571
  }
9437
9572
  function createStudyId(sourceInventoryHash, createdAt) {
9438
9573
  return stableId("STUDY", { sourceInventoryHash, createdAt });
@@ -9441,7 +9576,7 @@ function assertStudyId(studyId) {
9441
9576
  if (!STUDY_ID.test(studyId)) throw new UserError(`invalid insight study id: ${studyId}`);
9442
9577
  }
9443
9578
  function ensurePrivateDir(path) {
9444
- mkdirSync8(path, { recursive: true, mode: 448 });
9579
+ mkdirSync9(path, { recursive: true, mode: 448 });
9445
9580
  chmodSync2(path, 448);
9446
9581
  }
9447
9582
  function isRecord2(value) {
@@ -10365,7 +10500,7 @@ function buildJiraManifest(study, projectKey, options = {}) {
10365
10500
  return manifest;
10366
10501
  }
10367
10502
  function getJiraReceiptsPath(studyId) {
10368
- return join36(getStudyDir(studyId), "jira-receipts.jsonl");
10503
+ return join37(getStudyDir(studyId), "jira-receipts.jsonl");
10369
10504
  }
10370
10505
  function assertCurrentJiraManifest(study, value) {
10371
10506
  if (study.status !== "jira-manifested" || !study.jira) {
@@ -10771,7 +10906,7 @@ function positionalSource(value) {
10771
10906
  const resolved = resolvePath12(value);
10772
10907
  let stat;
10773
10908
  try {
10774
- stat = lstatSync6(resolved);
10909
+ stat = lstatSync7(resolved);
10775
10910
  } catch (error) {
10776
10911
  throw new UserError(`unable to read source ${resolved}: ${error.message}`);
10777
10912
  }
@@ -10787,10 +10922,10 @@ function collectSourceInputs(sourceArgs, options) {
10787
10922
  return inputs;
10788
10923
  }
10789
10924
  function contextPath(studyId) {
10790
- return join37(getStudyDir(studyId), CONTEXT_FILENAME);
10925
+ return join38(getStudyDir(studyId), CONTEXT_FILENAME);
10791
10926
  }
10792
10927
  function inventoryPath(studyId) {
10793
- return join37(getStudyDir(studyId), INVENTORY_FILENAME);
10928
+ return join38(getStudyDir(studyId), INVENTORY_FILENAME);
10794
10929
  }
10795
10930
  function writeCommandContext(studyId, skillMapPath, skillMapCoverage, inventory) {
10796
10931
  const context = {
@@ -10834,17 +10969,17 @@ function retainedPath(root, relativePath) {
10834
10969
  }
10835
10970
  function retainObservedSources(study, observedFiles) {
10836
10971
  if (observedFiles.length === 0) return study;
10837
- const root = join37(getStudyDir(study.studyId), "retained-sources");
10838
- mkdirSync9(root, { recursive: true, mode: 448 });
10972
+ const root = join38(getStudyDir(study.studyId), "retained-sources");
10973
+ mkdirSync10(root, { recursive: true, mode: 448 });
10839
10974
  chmodSync3(root, 448);
10840
10975
  const retainedSourceIds = /* @__PURE__ */ new Set();
10841
10976
  for (const file of observedFiles) {
10842
- const sourceRoot = join37(root, file.sourceId);
10843
- mkdirSync9(sourceRoot, { recursive: true, mode: 448 });
10977
+ const sourceRoot = join38(root, file.sourceId);
10978
+ mkdirSync10(sourceRoot, { recursive: true, mode: 448 });
10844
10979
  chmodSync3(sourceRoot, 448);
10845
10980
  const destination = retainedPath(sourceRoot, file.relativePath);
10846
- mkdirSync9(dirname13(destination), { recursive: true, mode: 448 });
10847
- writeFileSync8(destination, file.bytes, { flag: "wx", mode: 384 });
10981
+ mkdirSync10(dirname13(destination), { recursive: true, mode: 448 });
10982
+ writeFileSync9(destination, file.bytes, { flag: "wx", mode: 384 });
10848
10983
  chmodSync3(destination, 384);
10849
10984
  retainedSourceIds.add(file.sourceId);
10850
10985
  }
@@ -11077,7 +11212,7 @@ async function insightJira(studyId, options) {
11077
11212
  throw new UserError(`study ${studyId} cannot generate a Jira manifest: ${validation.blockers.map((issue) => issue.message).join("; ")}`);
11078
11213
  }
11079
11214
  if (localMode) {
11080
- const privatePath2 = join37(getStudyDir(studyId), JIRA_MANIFEST_FILENAME);
11215
+ const privatePath2 = join38(getStudyDir(studyId), JIRA_MANIFEST_FILENAME);
11081
11216
  const manifest2 = assertCurrentJiraManifest(study, parseJsonFile(privatePath2, "stored Jira manifest"));
11082
11217
  const receipt = options.receipt === void 0 ? void 0 : appendJiraReceipt(studyId, manifest2, parseJiraReceiptInput(parseJsonFile(options.receipt, "Jira receipt import")));
11083
11218
  const resume = buildJiraResumeState(manifest2, readJiraReceipts(studyId, manifest2));
@@ -11089,7 +11224,7 @@ async function insightJira(studyId, options) {
11089
11224
  return;
11090
11225
  }
11091
11226
  const manifest = buildJiraManifest(study, projectKey);
11092
- const privatePath = join37(getStudyDir(studyId), JIRA_MANIFEST_FILENAME);
11227
+ const privatePath = join38(getStudyDir(studyId), JIRA_MANIFEST_FILENAME);
11093
11228
  writeJsonAtomic(privatePath, manifest);
11094
11229
  if (options.out) {
11095
11230
  writeNewFile(resolvePath12(options.out), `${JSON.stringify(manifest, null, 2)}
@@ -11163,8 +11298,8 @@ async function insightClose(studyId, options) {
11163
11298
  import { tmpdir as tmpdir3 } from "os";
11164
11299
 
11165
11300
  // src/refine/activation.ts
11166
- import { lstatSync as lstatSync7, readFileSync as readFileSync28, readdirSync as readdirSync12, realpathSync as realpathSync11 } from "fs";
11167
- import { join as join38 } from "path";
11301
+ import { lstatSync as lstatSync8, readFileSync as readFileSync28, readdirSync as readdirSync12, realpathSync as realpathSync11 } from "fs";
11302
+ import { join as join39 } from "path";
11168
11303
 
11169
11304
  // src/refine/models.ts
11170
11305
  import { randomBytes } from "crypto";
@@ -11527,8 +11662,8 @@ function activationFiles(baseDir, overrideDir, name) {
11527
11662
  function walk2(dir, prefix = "") {
11528
11663
  for (const entry of readdirSync12(dir).sort()) {
11529
11664
  if (entry === ".git" || entry === "node_modules") continue;
11530
- const path = join38(dir, entry), relativePath = prefix ? `${prefix}/${entry}` : entry;
11531
- const stat = lstatSync7(path);
11665
+ const path = join39(dir, entry), relativePath = prefix ? `${prefix}/${entry}` : entry;
11666
+ const stat = lstatSync8(path);
11532
11667
  if (stat.isSymbolicLink()) throw new UserError(`Activation refuses symlinks: ${path}`);
11533
11668
  if (stat.isDirectory()) {
11534
11669
  walk2(path, relativePath);
@@ -11553,8 +11688,8 @@ function activationFiles(baseDir, overrideDir, name) {
11553
11688
  }
11554
11689
  let content = skill.bytes.toString("utf8");
11555
11690
  for (const artifact of artifacts) {
11556
- const path = join38(overrideDir, artifact);
11557
- const stat = lstatSync7(path);
11691
+ const path = join39(overrideDir, artifact);
11692
+ const stat = lstatSync8(path);
11558
11693
  if (!stat.isFile() || stat.isSymbolicLink()) throw new UserError("Override artifact must be a regular file, not a symlink.");
11559
11694
  if (stat.size > 2 * 1024 * 1024) throw new UserError("Override artifact exceeds 2 MiB.");
11560
11695
  const text = readFileSync28(path, "utf8");
@@ -11584,32 +11719,32 @@ ${content.slice(frontmatter[0].length)}`;
11584
11719
  }
11585
11720
 
11586
11721
  // src/commands/refine.ts
11587
- import { createHash as createHash8, randomUUID as randomUUID5 } from "crypto";
11588
- import { chmodSync as chmodSync4, existsSync as existsSync31, lstatSync as lstatSync8, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync4, rmdirSync, readFileSync as readFileSync30, realpathSync as realpathSync14, renameSync as renameSync6, rmSync as rmSync9, writeFileSync as writeFileSync10 } from "fs";
11589
- import { dirname as dirname15, isAbsolute as isAbsolute11, join as join41, relative as relative10, resolve as resolvePath15, sep as sep10 } from "path";
11722
+ import { createHash as createHash9, randomUUID as randomUUID6 } from "crypto";
11723
+ import { chmodSync as chmodSync4, existsSync as existsSync31, lstatSync as lstatSync9, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync4, rmdirSync, readFileSync as readFileSync30, realpathSync as realpathSync14, renameSync as renameSync6, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "fs";
11724
+ import { dirname as dirname15, isAbsolute as isAbsolute11, join as join42, relative as relative10, resolve as resolvePath15, sep as sep10 } from "path";
11590
11725
  import { createInterface as createInterface7 } from "readline/promises";
11591
11726
 
11592
11727
  // src/refine/context.ts
11593
11728
  import { existsSync as existsSync30, realpathSync as realpathSync13, statSync as statSync14 } from "fs";
11594
- import { isAbsolute as isAbsolute10, join as join40, relative as relative9, resolve as resolvePath14 } from "path";
11729
+ import { isAbsolute as isAbsolute10, join as join41, relative as relative9, resolve as resolvePath14 } from "path";
11595
11730
 
11596
11731
  // src/refine/store.ts
11597
- import { createHash as createHash7 } from "crypto";
11732
+ import { createHash as createHash8 } from "crypto";
11598
11733
  import { existsSync as existsSync29, readFileSync as readFileSync29, statSync as statSync13, realpathSync as realpathSync12 } from "fs";
11599
- import { join as join39, resolve as resolvePath13 } from "path";
11734
+ import { join as join40, resolve as resolvePath13 } from "path";
11600
11735
  var SIMILARITY_JOIN_THRESHOLD = 0.7;
11601
11736
  var READY_THRESHOLD = 2;
11602
11737
  function getRefinementsDir() {
11603
- return join39(getHomeDir(), "refinements");
11738
+ return join40(getHomeDir(), "refinements");
11604
11739
  }
11605
11740
  function getRefinementsPath() {
11606
- return join39(getRefinementsDir(), "refinements.json");
11741
+ return join40(getRefinementsDir(), "refinements.json");
11607
11742
  }
11608
11743
  function getPatternsPath() {
11609
- return join39(getRefinementsDir(), "patterns.json");
11744
+ return join40(getRefinementsDir(), "patterns.json");
11610
11745
  }
11611
11746
  function getBackupsDir() {
11612
- return join39(getRefinementsDir(), "backups");
11747
+ return join40(getRefinementsDir(), "backups");
11613
11748
  }
11614
11749
  function readJsonFile(path, label2) {
11615
11750
  let raw;
@@ -11854,11 +11989,11 @@ function createBackup(filePaths, deps = {}) {
11854
11989
  }
11855
11990
  const bytes = readFileSync29(path);
11856
11991
  const mode = statSync13(path).mode & 511;
11857
- const sha2562 = createHash7("sha256").update(bytes).digest("hex");
11992
+ const sha2562 = createHash8("sha256").update(bytes).digest("hex");
11858
11993
  return { path, existed: true, sha256: sha2562, mode, contentBase64: bytes.toString("base64") };
11859
11994
  });
11860
11995
  const manifest = { id, createdAt: now.toISOString(), files };
11861
- writeJsonAtomic(join39(getBackupsDir(), id, "manifest.json"), manifest);
11996
+ writeJsonAtomic(join40(getBackupsDir(), id, "manifest.json"), manifest);
11862
11997
  return manifest;
11863
11998
  }
11864
11999
  function validateBackupManifestShape(value, path) {
@@ -11907,7 +12042,7 @@ function readBackupManifest(id) {
11907
12042
  if (!isValidBackupId(id)) {
11908
12043
  throw new UserError(`invalid backup id "${id}" \u2014 expected the format produced by \`skill-forge refine promote\` (e.g. 20260720T142305Z-a1b2)`);
11909
12044
  }
11910
- const manifestPath = join39(getBackupsDir(), id, "manifest.json");
12045
+ const manifestPath = join40(getBackupsDir(), id, "manifest.json");
11911
12046
  if (!existsSync29(manifestPath)) {
11912
12047
  throw new UserError(`no backup with id: ${id}`);
11913
12048
  }
@@ -11942,8 +12077,8 @@ function isUnderRoot2(candidate, root) {
11942
12077
  function resolveScopes(config, cwd, skill, userRootFlag) {
11943
12078
  assertPlainSkillName(skill);
11944
12079
  const resolvedCwd = resolvePath14(cwd);
11945
- const localDir = join40(resolvedCwd, ".claude", "skills", skill);
11946
- const sharedDir = join40(resolvedCwd, "skills", skill);
12080
+ const localDir = join41(resolvedCwd, ".claude", "skills", skill);
12081
+ const sharedDir = join41(resolvedCwd, "skills", skill);
11947
12082
  const local = { scope: "local", dir: localDir, exists: existsSync30(localDir) };
11948
12083
  const shared = { scope: "shared", dir: sharedDir, exists: existsSync30(sharedDir) };
11949
12084
  const user = resolveUserScope(config, resolvedCwd, skill, userRootFlag);
@@ -11952,10 +12087,10 @@ function resolveScopes(config, cwd, skill, userRootFlag) {
11952
12087
  function resolveUserScope(config, resolvedCwd, skill, userRootFlag) {
11953
12088
  if (userRootFlag) {
11954
12089
  const root = resolvePath14(userRootFlag);
11955
- return { resolved: true, dir: join40(root, skill), root: userRootFlag, reason: "flag" };
12090
+ return { resolved: true, dir: join41(root, skill), root: userRootFlag, reason: "flag" };
11956
12091
  }
11957
12092
  const cwdReal = realpathOrNull2(resolvedCwd) ?? resolvedCwd;
11958
- const candidates = config.skillsRoots.map((root) => ({ root, dir: join40(resolveConfiguredRoot2(root), skill) }));
12093
+ const candidates = config.skillsRoots.map((root) => ({ root, dir: join41(resolveConfiguredRoot2(root), skill) }));
11959
12094
  for (const candidate of candidates) {
11960
12095
  const skillReal = realpathOrNull2(candidate.dir);
11961
12096
  if (skillReal) {
@@ -11990,12 +12125,12 @@ function inventoryScopeDir(scope2, dir) {
11990
12125
  scope: scope2,
11991
12126
  dir,
11992
12127
  dirExists: isDirectory(dir),
11993
- skillMd: isFile2(join40(dir, "SKILL.md")),
11994
- patchMd: isFile2(join40(dir, "SKILL.patch.md")),
11995
- extendMd: isFile2(join40(dir, "SKILL.extend.md")),
11996
- configJson: isFile2(join40(dir, "skill-config.json")),
11997
- hooksDir: isDirectory(join40(dir, "hooks")),
11998
- scriptsDir: isDirectory(join40(dir, "scripts"))
12128
+ skillMd: isFile2(join41(dir, "SKILL.md")),
12129
+ patchMd: isFile2(join41(dir, "SKILL.patch.md")),
12130
+ extendMd: isFile2(join41(dir, "SKILL.extend.md")),
12131
+ configJson: isFile2(join41(dir, "skill-config.json")),
12132
+ hooksDir: isDirectory(join41(dir, "hooks")),
12133
+ scriptsDir: isDirectory(join41(dir, "scripts"))
11999
12134
  };
12000
12135
  }
12001
12136
  function inventoryAllScopes(resolution) {
@@ -12181,14 +12316,14 @@ ${rendered}` : rendered;
12181
12316
  function ensureScopeDirSafe(scopeDir, resolvedCwd) {
12182
12317
  let lst;
12183
12318
  try {
12184
- lst = lstatSync8(scopeDir);
12319
+ lst = lstatSync9(scopeDir);
12185
12320
  } catch {
12186
12321
  lst = void 0;
12187
12322
  }
12188
12323
  if (lst?.isSymbolicLink()) {
12189
12324
  throw new UserError(`refusing to write under ${scopeDir}: scope directory is a symlink`);
12190
12325
  }
12191
- mkdirSync11(scopeDir, { recursive: true });
12326
+ mkdirSync12(scopeDir, { recursive: true });
12192
12327
  const real = resolveConfiguredRoot3(scopeDir);
12193
12328
  const lexical = resolvePath15(scopeDir);
12194
12329
  let realResolved;
@@ -12207,23 +12342,23 @@ function ensureScopeDirSafe(scopeDir, resolvedCwd) {
12207
12342
  }
12208
12343
  function writeOverrideFile(scopeDir, relPath, content, resolvedCwd) {
12209
12344
  const realScopeDir = ensureScopeDirSafe(scopeDir, resolvedCwd);
12210
- const destPath = join41(scopeDir, relPath);
12345
+ const destPath = join42(scopeDir, relPath);
12211
12346
  const destDir = dirname15(destPath);
12212
- mkdirSync11(destDir, { recursive: true });
12347
+ mkdirSync12(destDir, { recursive: true });
12213
12348
  const realDestDir = realpathSync14(destDir);
12214
12349
  if (!isUnderRoot3(realDestDir, realScopeDir)) {
12215
12350
  throw new UserError(`refusing to write ${destPath}: resolves outside scope directory ${scopeDir}`);
12216
12351
  }
12217
12352
  let destLst;
12218
12353
  try {
12219
- destLst = lstatSync8(destPath);
12354
+ destLst = lstatSync9(destPath);
12220
12355
  } catch {
12221
12356
  destLst = void 0;
12222
12357
  }
12223
12358
  if (destLst?.isSymbolicLink()) {
12224
12359
  throw new UserError(`refusing to write ${destPath}: destination is a symlink`);
12225
12360
  }
12226
- writeFileSync10(destPath, content, { mode: 384 });
12361
+ writeFileSync11(destPath, content, { mode: 384 });
12227
12362
  chmodSync4(destPath, 384);
12228
12363
  return destPath;
12229
12364
  }
@@ -12303,11 +12438,11 @@ Write ${preview.destPath}? [y/N] `);
12303
12438
  }
12304
12439
  function renderCaptureArtifactPreview(opts, content, refinementId, generatedAt, scopeDir) {
12305
12440
  const probeRelPath = opts.overrideType === "patch" ? "SKILL.patch.md" : opts.overrideType === "extend" || opts.overrideType === "new" ? "SKILL.extend.md" : void 0;
12306
- const probePath = probeRelPath ? join41(scopeDir, probeRelPath) : void 0;
12441
+ const probePath = probeRelPath ? join42(scopeDir, probeRelPath) : void 0;
12307
12442
  const existingContent = probePath && existsSync31(probePath) ? readFileSync30(probePath, "utf-8") : "";
12308
12443
  const rendered = renderCaptureArtifact(opts, content, refinementId, generatedAt, existingContent);
12309
12444
  const relPath = rendered.relPath;
12310
- const destPath = join41(scopeDir, relPath);
12445
+ const destPath = join42(scopeDir, relPath);
12311
12446
  const before = existsSync31(destPath) ? readFileSync30(destPath, "utf-8") : "";
12312
12447
  const diff = previewDiff(before, rendered.content, destPath);
12313
12448
  return { relPath, content: rendered.content, destPath, diff };
@@ -12323,8 +12458,8 @@ async function refineActivateCommand(options) {
12323
12458
  const scopes = resolveScopes(loadConfig(), cwd, options.skill);
12324
12459
  const overrideDir = options.scope === "shared" ? scopes.shared.dir : scopes.local.dir;
12325
12460
  if (realpathSync14(overrideDir) !== overrideDir) throw new UserError("Activation refuses a symlinked override directory.");
12326
- const hostRoot = join41(cwd, PROJECT_SKILL_ROOTS[options.host]);
12327
- const destination = join41(hostRoot, options.as);
12461
+ const hostRoot = join42(cwd, PROJECT_SKILL_ROOTS[options.host]);
12462
+ const destination = join42(hostRoot, options.as);
12328
12463
  if (existsSync31(destination) || resolveTargetRealPath(destination) !== destination) throw new UserError("Activation destination already exists or contains a symlink; choose a new --as name.");
12329
12464
  const files = activationFiles(options.base, overrideDir, options.as);
12330
12465
  const receiptName = ".skill-forge-activation.json";
@@ -12335,7 +12470,7 @@ async function refineActivateCommand(options) {
12335
12470
  skill: options.as,
12336
12471
  capture: options.skill,
12337
12472
  source: realpathSync14(options.base),
12338
- sourceSha256: createHash8("sha256").update(readFileSync30(join41(options.base, "SKILL.md"))).digest("hex"),
12473
+ sourceSha256: createHash9("sha256").update(readFileSync30(join42(options.base, "SKILL.md"))).digest("hex"),
12339
12474
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
12340
12475
  runtimeVerified: false
12341
12476
  };
@@ -12343,15 +12478,15 @@ async function refineActivateCommand(options) {
12343
12478
  if (files.length > 1e3 || files.some((f) => f.bytes.length > 2 * 1024 * 1024) || files.reduce((n, f) => n + f.bytes.length, 0) > 16 * 1024 * 1024) {
12344
12479
  throw new UserError("Composed activation exceeds file or total size limits.");
12345
12480
  }
12346
- const stage = mkdtempSync4(join41(tmpdir3(), "skill-forge-activate-"));
12481
+ const stage = mkdtempSync4(join42(tmpdir3(), "skill-forge-activate-"));
12347
12482
  try {
12348
12483
  for (const file of files) {
12349
- const path = join41(stage, file.relativePath);
12350
- mkdirSync11(dirname15(path), { recursive: true });
12351
- writeFileSync10(path, file.bytes, { mode: file.mode });
12484
+ const path = join42(stage, file.relativePath);
12485
+ mkdirSync12(dirname15(path), { recursive: true });
12486
+ writeFileSync11(path, file.bytes, { mode: file.mode });
12352
12487
  }
12353
- const safety = await scanSafety(stage, "block-high", files.map((f) => ({ file: join41(stage, f.relativePath), content: f.bytes.toString("utf8") })));
12354
- safety.findings = safety.findings.map((f) => ({ ...f, file: join41(destination, relative10(stage, f.file)) }));
12488
+ const safety = await scanSafety(stage, "block-high", files.map((f) => ({ file: join42(stage, f.relativePath), content: f.bytes.toString("utf8") })));
12489
+ safety.findings = safety.findings.map((f) => ({ ...f, file: join42(destination, relative10(stage, f.file)) }));
12355
12490
  const preview = {
12356
12491
  destination,
12357
12492
  provenance,
@@ -12360,7 +12495,7 @@ async function refineActivateCommand(options) {
12360
12495
  source: realpathSync14(options.base),
12361
12496
  files: files.map((f) => f.relativePath),
12362
12497
  safety,
12363
- diff: previewDiff(readFileSync30(join41(options.base, "SKILL.md"), "utf8"), files.find((f) => f.relativePath === "SKILL.md").bytes.toString("utf8"), join41(destination, "SKILL.md"))
12498
+ diff: previewDiff(readFileSync30(join42(options.base, "SKILL.md"), "utf8"), files.find((f) => f.relativePath === "SKILL.md").bytes.toString("utf8"), join42(destination, "SKILL.md"))
12364
12499
  };
12365
12500
  if (options.dryRun) {
12366
12501
  emit2(options.json, { ...preview, dryRun: true, activated: false }, () => console.log(JSON.stringify(preview, null, 2)));
@@ -12374,15 +12509,15 @@ async function refineActivateCommand(options) {
12374
12509
  }
12375
12510
  if (resolveTargetRealPath(destination) !== destination) throw new UserError("Activation destination became symlinked.");
12376
12511
  ensureScopeDirSafe(hostRoot, cwd);
12377
- mkdirSync11(destination);
12378
- const backup = createBackup(files.map((f) => join41(destination, f.relativePath)));
12379
- const hashes = files.map((f) => ({ path: join41(destination, f.relativePath), sha256: createHash8("sha256").update(f.bytes).digest("hex") }));
12512
+ mkdirSync12(destination);
12513
+ const backup = createBackup(files.map((f) => join42(destination, f.relativePath)));
12514
+ const hashes = files.map((f) => ({ path: join42(destination, f.relativePath), sha256: createHash9("sha256").update(f.bytes).digest("hex") }));
12380
12515
  writePostWriteSidecar(backup.id, hashes);
12381
12516
  const projectConfig = { ...loadConfig(), skillsRoots: [hostRoot] };
12382
12517
  try {
12383
- for (const file of files) safeRestoreWrite(join41(destination, file.relativePath), file.bytes, file.mode, projectConfig);
12518
+ for (const file of files) safeRestoreWrite(join42(destination, file.relativePath), file.bytes, file.mode, projectConfig);
12384
12519
  for (const file of hashes) {
12385
- if (createHash8("sha256").update(readFileSync30(file.path)).digest("hex") !== file.sha256) throw new UserError("Activation read-back hash mismatch.");
12520
+ if (createHash9("sha256").update(readFileSync30(file.path)).digest("hex") !== file.sha256) throw new UserError("Activation read-back hash mismatch.");
12386
12521
  }
12387
12522
  } catch (error) {
12388
12523
  throw new UserError(`Activation write failed; backup ${backup.id} records recovery paths. Inspect before using rollback --project --force: ${error.message}`);
@@ -12392,7 +12527,7 @@ async function refineActivateCommand(options) {
12392
12527
  activated: true,
12393
12528
  backupId: backup.id,
12394
12529
  runtimeVerified: false,
12395
- verification: `Open a fresh ${options.host} task in this project, invoke ${preview.invocation}, and verify it reads ${join41(destination, "SKILL.md")}.`,
12530
+ verification: `Open a fresh ${options.host} task in this project, invoke ${preview.invocation}, and verify it reads ${join42(destination, "SKILL.md")}.`,
12396
12531
  rollback: `skill-forge refine rollback ${backup.id} --project --yes`
12397
12532
  }, () => {
12398
12533
  console.log(`Activated ${destination}. Invoke ${preview.invocation} in a fresh ${options.host} task and verify the loaded file; runtime use is not yet observed.`);
@@ -12495,12 +12630,12 @@ async function refineWhichCommand(skill, options = {}) {
12495
12630
  }
12496
12631
  function writePostWriteSidecar(backupId, files) {
12497
12632
  const sidecar = { schemaVersion: 1, files };
12498
- const dir = join41(getBackupsDir(), backupId);
12499
- mkdirSync11(dir, { recursive: true });
12500
- writeFileSync10(join41(dir, "post.json"), JSON.stringify(sidecar, null, 2) + "\n", { mode: 384 });
12633
+ const dir = join42(getBackupsDir(), backupId);
12634
+ mkdirSync12(dir, { recursive: true });
12635
+ writeFileSync11(join42(dir, "post.json"), JSON.stringify(sidecar, null, 2) + "\n", { mode: 384 });
12501
12636
  }
12502
12637
  function readPostWriteSidecar(backupId) {
12503
- const path = join41(getBackupsDir(), backupId, "post.json");
12638
+ const path = join42(getBackupsDir(), backupId, "post.json");
12504
12639
  if (!existsSync31(path)) return { hashes: /* @__PURE__ */ new Map(), status: "missing" };
12505
12640
  let raw;
12506
12641
  try {
@@ -12562,7 +12697,7 @@ async function refinePromoteCommand(patternId, options = {}) {
12562
12697
  if (!isUnderRoot3(realSkillDir, realUserRoot)) {
12563
12698
  throw new UserError(`refusing to promote: resolved skill directory ${skillDir} is outside the configured user scope root ${scopes.user.root}`);
12564
12699
  }
12565
- const skillMdPath = join41(skillDir, "SKILL.md");
12700
+ const skillMdPath = join42(skillDir, "SKILL.md");
12566
12701
  if (!existsSync31(skillMdPath)) {
12567
12702
  throw new UserError(`user-scope SKILL.md not found at ${skillMdPath} \u2014 refusing to promote against a missing base skill.`);
12568
12703
  }
@@ -12603,16 +12738,16 @@ Promote pattern ${patternId} into ${skillMdPath}? [y/N] `);
12603
12738
  return;
12604
12739
  }
12605
12740
  const backup = createBackup([skillMdPath]);
12606
- const tempPath = `${skillMdPath}.tmp-${randomUUID5()}`;
12741
+ const tempPath = `${skillMdPath}.tmp-${randomUUID6()}`;
12607
12742
  try {
12608
- writeFileSync10(tempPath, after, "utf-8");
12743
+ writeFileSync11(tempPath, after, "utf-8");
12609
12744
  renameSync6(tempPath, skillMdPath);
12610
12745
  } catch (err) {
12611
12746
  rmSync9(tempPath, { force: true });
12612
12747
  throw err;
12613
12748
  }
12614
12749
  try {
12615
- const postSha256 = createHash8("sha256").update(readFileSync30(skillMdPath)).digest("hex");
12750
+ const postSha256 = createHash9("sha256").update(readFileSync30(skillMdPath)).digest("hex");
12616
12751
  writePostWriteSidecar(backup.id, [{ path: skillMdPath, sha256: postSha256 }]);
12617
12752
  markGeneralized(patternId, backup.id);
12618
12753
  const skillsRoot = resolveConfiguredRoot3(scopes.user.root);
@@ -12640,7 +12775,7 @@ function assertBackupDirContained(backupId) {
12640
12775
  const backupsDir = getBackupsDir();
12641
12776
  if (!existsSync31(backupsDir)) return;
12642
12777
  const realBackupsDir = realpathSync14(backupsDir);
12643
- const backupDir = join41(backupsDir, backupId);
12778
+ const backupDir = join42(backupsDir, backupId);
12644
12779
  if (!existsSync31(backupDir)) return;
12645
12780
  const realBackupDir = realpathSync14(backupDir);
12646
12781
  if (!isUnderRoot3(realBackupDir, realBackupsDir)) {
@@ -12653,7 +12788,7 @@ function resolveTargetRealPath(path) {
12653
12788
  try {
12654
12789
  const real = realpathSync14(lexicalAncestor);
12655
12790
  const remainder = relative10(lexicalAncestor, resolvePath15(path));
12656
- return remainder === "" ? real : join41(real, remainder);
12791
+ return remainder === "" ? real : join42(real, remainder);
12657
12792
  } catch {
12658
12793
  const parent = dirname15(lexicalAncestor);
12659
12794
  if (parent === lexicalAncestor) return resolvePath15(path);
@@ -12685,7 +12820,7 @@ function assertNoSymlinkLeaves(manifest) {
12685
12820
  for (const file of manifest.files) {
12686
12821
  let lst;
12687
12822
  try {
12688
- lst = lstatSync8(file.path);
12823
+ lst = lstatSync9(file.path);
12689
12824
  } catch {
12690
12825
  lst = void 0;
12691
12826
  }
@@ -12722,10 +12857,10 @@ function walkRealComponents(filePath, config, createMissing) {
12722
12857
  let current = rootReal;
12723
12858
  for (const component of dirComponents) {
12724
12859
  if (current === void 0) break;
12725
- const next = join41(current, component);
12860
+ const next = join42(current, component);
12726
12861
  let lst;
12727
12862
  try {
12728
- lst = lstatSync8(next);
12863
+ lst = lstatSync9(next);
12729
12864
  } catch {
12730
12865
  lst = void 0;
12731
12866
  }
@@ -12734,7 +12869,7 @@ function walkRealComponents(filePath, config, createMissing) {
12734
12869
  current = void 0;
12735
12870
  continue;
12736
12871
  }
12737
- mkdirSync11(next);
12872
+ mkdirSync12(next);
12738
12873
  current = next;
12739
12874
  continue;
12740
12875
  }
@@ -12753,22 +12888,22 @@ function safeRestoreWrite(filePath, contentBuffer, mode, config) {
12753
12888
  if (parentReal === void 0) {
12754
12889
  throw new UserError(`refusing to restore ${filePath}: could not establish a real parent directory`);
12755
12890
  }
12756
- const finalPath = join41(parentReal, leafComponent);
12891
+ const finalPath = join42(parentReal, leafComponent);
12757
12892
  if (!isUnderRoot3(finalPath, rootReal)) {
12758
12893
  throw new UserError(`refusing to restore ${filePath}: resolves outside root ${rootReal}`);
12759
12894
  }
12760
12895
  let leafLst;
12761
12896
  try {
12762
- leafLst = lstatSync8(finalPath);
12897
+ leafLst = lstatSync9(finalPath);
12763
12898
  } catch {
12764
12899
  leafLst = void 0;
12765
12900
  }
12766
12901
  if (leafLst?.isSymbolicLink()) {
12767
12902
  throw new UserError(`refusing to restore ${filePath}: it is now a symlink \u2014 refusing to follow or overwrite it.`);
12768
12903
  }
12769
- const tempPath = `${finalPath}.tmp-${randomUUID5()}`;
12904
+ const tempPath = `${finalPath}.tmp-${randomUUID6()}`;
12770
12905
  try {
12771
- writeFileSync10(tempPath, contentBuffer);
12906
+ writeFileSync11(tempPath, contentBuffer);
12772
12907
  chmodSync4(tempPath, mode);
12773
12908
  renameSync6(tempPath, finalPath);
12774
12909
  } catch (err) {
@@ -12779,13 +12914,13 @@ function safeRestoreWrite(filePath, contentBuffer, mode, config) {
12779
12914
  function safeDeleteTombstone(filePath, config) {
12780
12915
  const { parentReal, leafComponent, rootReal } = walkRealComponents(filePath, config, false);
12781
12916
  if (parentReal === void 0) return;
12782
- const finalPath = join41(parentReal, leafComponent);
12917
+ const finalPath = join42(parentReal, leafComponent);
12783
12918
  if (!isUnderRoot3(finalPath, rootReal)) {
12784
12919
  throw new UserError(`refusing to delete ${filePath}: resolves outside root ${rootReal}`);
12785
12920
  }
12786
12921
  let leafLst;
12787
12922
  try {
12788
- leafLst = lstatSync8(finalPath);
12923
+ leafLst = lstatSync9(finalPath);
12789
12924
  } catch {
12790
12925
  leafLst = void 0;
12791
12926
  }
@@ -12804,7 +12939,7 @@ async function refineRollbackCommand(backupId, options = {}) {
12804
12939
  assertBackupDirContained(backupId);
12805
12940
  const manifest = readBackupManifest(backupId);
12806
12941
  const config = loadConfig();
12807
- if (options.project) config.skillsRoots = Object.values(PROJECT_SKILL_ROOTS).map((p) => join41(realpathSync14(process.cwd()), p));
12942
+ if (options.project) config.skillsRoots = Object.values(PROJECT_SKILL_ROOTS).map((p) => join42(realpathSync14(process.cwd()), p));
12808
12943
  const assertProjectRoots = () => {
12809
12944
  if (options.project && config.skillsRoots.some((root) => resolveTargetRealPath(root) !== root)) {
12810
12945
  throw new UserError("refusing project rollback: a project skill root or its ancestor is symlinked.");
@@ -12831,7 +12966,7 @@ async function refineRollbackCommand(backupId, options = {}) {
12831
12966
  for (const file of manifest.files) {
12832
12967
  const expected = sidecar.hashes.get(file.path);
12833
12968
  if (expected === void 0) continue;
12834
- const currentSha256 = existsSync31(file.path) ? createHash8("sha256").update(readFileSync30(file.path)).digest("hex") : null;
12969
+ const currentSha256 = existsSync31(file.path) ? createHash9("sha256").update(readFileSync30(file.path)).digest("hex") : null;
12835
12970
  if (currentSha256 !== expected) mismatches.push(file.path);
12836
12971
  }
12837
12972
  if (mismatches.length > 0 && !options.force) {
@@ -12989,7 +13124,7 @@ configCmd.command("propose <key> <value>").description("Queue a preference for r
12989
13124
  configCmd.command("review").description("Review pending agent-proposed preferences one at a time").option("--accept-all", "accept every pending proposal without prompting").option("--reject-all", "reject every pending proposal without prompting").action(configReview);
12990
13125
  program.command("guide [topic]").description("What this tool does, what state you are in, and the next command to run").action(guide);
12991
13126
  program.command("evolve <target-skill-dir>").description("Self-evolve a skill via SkillOpt-Sleep, re-gate the proposal, then promote/hold/reject (Pro)").option("--project <dir>", "project dir passed to SkillOpt-Sleep as --project (defaults to cwd)").option("--dry-run", "SkillOpt-Sleep dry-run: report only, nothing staged").option("--backend <name>", "SkillOpt-Sleep backend (default: mock, offline \u2014 a real backend requires --yes or interactive confirmation)").option("--lookback-hours <n>", "hours of session history for SkillOpt-Sleep to harvest", parseLookbackHours).option("-y, --yes", "skip interactive prompts; honor the re-gate verdict automatically").option("--json", "print the re-gate result as JSON instead of the terminal report").option("--force", "allow re-adopting a staging dir whose proposed content already matches the live skill").action(evolveCommand);
12992
- program.command("audit").option("--compare <file>", "compare with a prior full audit JSON; preserve all current findings").alias("doctor").description("Doctor-style health check over the configured skill/MCP set: inventory, hygiene findings, and consolidation/refinement opportunities").option("--json", "print the audit report as JSON instead of the terminal summary (non-interactive: skips the business-profile prompt)").option("-y, --yes", "skip interactive prompts (business profile, foundation, handoff); never implies --foundation or --handoff").option("--report <file>", "write the report to this path instead of the default ~/.skill-forge/reports/audit-<timestamp>.md (refuses an existing path)").option("--foundation", "authorizes exactly one skills-root write: scaffold a business-foundation skill from the captured business profile").option("--handoff", "authorizes launching the configured agent with the bundled curation prompt against the written report").option("--plugin-inventory <file>", "import a static host inventory JSON; audit one host at a time").option("--claude-plugins", "include enabled Claude Code plugins' declared skills in this audit (default: config.claudePlugins, else off)").option("--no-claude-plugins", "exclude enabled Claude Code plugins from this audit even when config.claudePlugins is on").option("--usage-snapshot <file>", "join per-skill usage counts from a skill-monitor snapshot JSON file or a directory of them (default: config.usageSnapshotPath)").option("--usage-window <days>", "restrict directory-input usage-snapshot selection to a snapshot with this window_days (integer)", parseUsageWindow).action(auditCommand);
13127
+ program.command("audit").option("--compare <file>", "compare with a prior full audit JSON; preserve all current findings").alias("doctor").description("Doctor-style health check over the configured skill/MCP set: inventory, hygiene findings, and consolidation/refinement opportunities").option("--json", "print the audit report as JSON instead of the terminal summary (non-interactive: skips the business-profile prompt)").option("--laya-shadow", "score current finding metadata with local Laya in shadow; never changes a gate").option("--laya-base-url <url>", "loopback Laya endpoint for --laya-shadow (default http://127.0.0.1:8000)").option("-y, --yes", "skip interactive prompts (business profile, foundation, handoff); never implies --foundation or --handoff").option("--report <file>", "write the report to this path instead of the default ~/.skill-forge/reports/audit-<timestamp>.md (refuses an existing path)").option("--foundation", "authorizes exactly one skills-root write: scaffold a business-foundation skill from the captured business profile").option("--handoff", "authorizes launching the configured agent with the bundled curation prompt against the written report").option("--plugin-inventory <file>", "import a static host inventory JSON; audit one host at a time").option("--claude-plugins", "include enabled Claude Code plugins' declared skills in this audit (default: config.claudePlugins, else off)").option("--no-claude-plugins", "exclude enabled Claude Code plugins from this audit even when config.claudePlugins is on").option("--usage-snapshot <file>", "join per-skill usage counts from a skill-monitor snapshot JSON file or a directory of them (default: config.usageSnapshotPath)").option("--usage-window <days>", "restrict directory-input usage-snapshot selection to a snapshot with this window_days (integer)", parseUsageWindow).action(auditCommand);
12993
13128
  var finding = program.command("finding").description("Review and acknowledge audit findings \u2014 human-only, never a gate input (see docs/accepted-findings-schema.md)");
12994
13129
  finding.command("accept <fingerprint-prefix>").description("Accept a currently-active LOW/MEDIUM finding (no override for HIGH/CRITICAL); re-runs the audit engine to resolve it").requiredOption("--reason <text>", "why this finding is accepted (mandatory, 1-200 chars, no control characters)").option("--json", "print the accepted record as JSON").action(findingAcceptCommand);
12995
13130
  finding.command("revoke <fingerprint-prefix>").description("Remove a stored acceptance so the finding reports again").option("--json", "print the removed record as JSON").action(findingRevokeCommand);