@csark0812/skeleton 1.5.1 → 1.5.2

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 (2) hide show
  1. package/dist/cli.js +245 -185
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -16984,7 +16984,7 @@ async function loadPlugins(root, config) {
16984
16984
  }
16985
16985
 
16986
16986
  // src/audit/core/collect.ts
16987
- import { existsSync as existsSync6, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
16987
+ import { existsSync as existsSync6, readFileSync as readFileSync4, realpathSync as realpathSync4, statSync as statSync2 } from "node:fs";
16988
16988
  import { join as join5, relative as relative4 } from "node:path";
16989
16989
 
16990
16990
  // src/audit/core/skill-roots.ts
@@ -17109,6 +17109,37 @@ function isSkillPath(relPath, index) {
17109
17109
  }
17110
17110
  return false;
17111
17111
  }
17112
+ var NESTED_SKILL_SLUG_RE = /(?:^|\/)\.(?:claude|agents)\/skills\/([a-z0-9-]+)\//;
17113
+ var FLAT_SKILL_REFERENCES_RE = /(?:^|\/)([a-z0-9-]+)\/references(?:\/|$)/;
17114
+ function slugFromSkillPath(relPath) {
17115
+ const normalized = normalizeRelPath(relPath);
17116
+ const nested = normalized.match(NESTED_SKILL_SLUG_RE);
17117
+ if (nested?.[1])
17118
+ return nested[1];
17119
+ if (normalized.endsWith("/SKILL.md")) {
17120
+ const parts = normalized.split("/");
17121
+ return parts.at(-2) ?? null;
17122
+ }
17123
+ return null;
17124
+ }
17125
+ function slugFromPath(filePath, workspaceRoot) {
17126
+ const normalized = normalizeRelPath(filePath);
17127
+ const nested = normalized.match(NESTED_SKILL_SLUG_RE);
17128
+ if (nested?.[1])
17129
+ return nested[1];
17130
+ if (normalized.endsWith("/SKILL.md")) {
17131
+ return slugFromSkillPath(normalized);
17132
+ }
17133
+ const flatRef = normalized.match(FLAT_SKILL_REFERENCES_RE);
17134
+ const flatSlug = flatRef?.[1];
17135
+ if (!flatSlug || FLAT_SKILL_DENYLIST.has(flatSlug))
17136
+ return null;
17137
+ if (workspaceRoot) {
17138
+ if (!existsSync5(join4(workspaceRoot, flatSlug, "SKILL.md")))
17139
+ return null;
17140
+ }
17141
+ return flatSlug;
17142
+ }
17112
17143
  function skillCollectAugments(index) {
17113
17144
  const patterns = [];
17114
17145
  for (const skillRoot of index.roots) {
@@ -17156,7 +17187,7 @@ function shouldExclude(relPath, exclude) {
17156
17187
  return exclude.some((pattern) => matchesGlobScope(relPath, pattern));
17157
17188
  }
17158
17189
  function expandPatterns(root, patterns, exclude) {
17159
- const files = new Set;
17190
+ const byReal = new Map;
17160
17191
  for (const pattern of patterns) {
17161
17192
  for (const abs of globSync(pattern, {
17162
17193
  cwd: root,
@@ -17169,10 +17200,19 @@ function expandPatterns(root, patterns, exclude) {
17169
17200
  const rel = normalizeRelPath(relative4(root, abs));
17170
17201
  if (shouldExclude(rel, exclude))
17171
17202
  continue;
17172
- files.add(abs);
17203
+ let real;
17204
+ try {
17205
+ real = realpathSync4(abs);
17206
+ } catch {
17207
+ real = abs;
17208
+ }
17209
+ const existing = byReal.get(real);
17210
+ if (existing === undefined || abs === real && existing !== real) {
17211
+ byReal.set(real, abs);
17212
+ }
17173
17213
  }
17174
17214
  }
17175
- return [...files];
17215
+ return [...byReal.values()];
17176
17216
  }
17177
17217
  function collectScanFiles(config, root, skillIndex) {
17178
17218
  const exclude = mergedExcludes(config);
@@ -17331,6 +17371,22 @@ function parseRegistryPaths(root) {
17331
17371
  return parseRegistry(root).paths;
17332
17372
  }
17333
17373
 
17374
+ // src/audit/core/skills-lock.ts
17375
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
17376
+ import { join as join7 } from "node:path";
17377
+ var SKILLS_LOCK_REL = "skills-lock.json";
17378
+ function lockedSkillSlugs(root) {
17379
+ const abs = join7(root, SKILLS_LOCK_REL);
17380
+ if (!existsSync8(abs))
17381
+ return new Set;
17382
+ try {
17383
+ const parsed = JSON.parse(readFileSync6(abs, "utf8"));
17384
+ return new Set(Object.keys(parsed.skills ?? {}));
17385
+ } catch {
17386
+ return new Set;
17387
+ }
17388
+ }
17389
+
17334
17390
  // src/audit/core/context.ts
17335
17391
  function createContext(options = {}) {
17336
17392
  const root = options.root ?? findRepoRoot();
@@ -17355,16 +17411,17 @@ function createContext(options = {}) {
17355
17411
  registryHasTableHeader: registry.hasTableHeader,
17356
17412
  retiredSkills: new Set(retiredSkills(config)),
17357
17413
  skillIndex,
17414
+ lockedSkillSlugs: lockedSkillSlugs(root),
17358
17415
  policies: options.policies ?? []
17359
17416
  };
17360
17417
  }
17361
17418
 
17362
17419
  // src/audit/core/fix.ts
17363
- import { existsSync as existsSync10, realpathSync as realpathSync4, writeFileSync } from "node:fs";
17420
+ import { existsSync as existsSync11, realpathSync as realpathSync5, writeFileSync } from "node:fs";
17364
17421
  import { dirname as dirname6, resolve as resolve6, sep as sep3 } from "node:path";
17365
17422
 
17366
17423
  // src/audit/fix/anchors.ts
17367
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
17424
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
17368
17425
  import { dirname as dirname5, resolve as resolve5 } from "node:path";
17369
17426
 
17370
17427
  // node_modules/github-slugger/regex.js
@@ -25545,7 +25602,7 @@ var handle = {
25545
25602
  };
25546
25603
 
25547
25604
  // node_modules/mdast-util-to-markdown/lib/join.js
25548
- var join7 = [joinDefaults];
25605
+ var join8 = [joinDefaults];
25549
25606
  function joinDefaults(left, right, parent, state) {
25550
25607
  if (right.type === "code" && formatCodeAsIndented(right, state) && (left.type === "list" || left.type === right.type && formatCodeAsIndented(left, state))) {
25551
25608
  return false;
@@ -25928,7 +25985,7 @@ function toMarkdown(tree, options) {
25928
25985
  handle: undefined,
25929
25986
  indentLines,
25930
25987
  indexStack: [],
25931
- join: [...join7],
25988
+ join: [...join8],
25932
25989
  options: {},
25933
25990
  safe: safeBound,
25934
25991
  stack: [],
@@ -28918,9 +28975,9 @@ function collectAnchorFixes(ctx) {
28918
28975
  if (!anchor)
28919
28976
  continue;
28920
28977
  const resolved = resolveLink(filePath, target);
28921
- if (!existsSync8(resolved))
28978
+ if (!existsSync9(resolved))
28922
28979
  continue;
28923
- const targetContent = readFileSync6(resolved, "utf8");
28980
+ const targetContent = readFileSync7(resolved, "utf8");
28924
28981
  const slugs = extractHeadingSlugs(targetContent, resolved);
28925
28982
  const anchorSlug = slugifyAnchor(anchor);
28926
28983
  if (slugs.has(anchorSlug))
@@ -28969,8 +29026,8 @@ function collectAnchorFixes(ctx) {
28969
29026
  }
28970
29027
 
28971
29028
  // src/audit/fix/doc-meta.ts
28972
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
28973
- import { join as join8 } from "node:path";
29029
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
29030
+ import { join as join9 } from "node:path";
28974
29031
 
28975
29032
  // src/audit/core/git-meta.ts
28976
29033
  import { spawnSync } from "node:child_process";
@@ -29001,10 +29058,10 @@ function bumpDocMetaLastReviewed(content3, gitDate) {
29001
29058
  function collectDocMetaFixes(ctx) {
29002
29059
  const edits = [];
29003
29060
  for (const relPath2 of ctx.docMetaPaths) {
29004
- const abs = join8(ctx.root, relPath2);
29005
- if (!existsSync9(abs))
29061
+ const abs = join9(ctx.root, relPath2);
29062
+ if (!existsSync10(abs))
29006
29063
  continue;
29007
- const content3 = readFileSync7(abs, "utf8");
29064
+ const content3 = readFileSync8(abs, "utf8");
29008
29065
  if (!DOC_META_RE.test(content3))
29009
29066
  continue;
29010
29067
  const reviewedStr = docMetaLastReviewed(content3);
@@ -29071,11 +29128,11 @@ function resolveWritePath(root2, relFile) {
29071
29128
  if (!underRoot(rootResolved, abs)) {
29072
29129
  throw new Error(`Refusing autofix outside repo root: ${relFile}`);
29073
29130
  }
29074
- const rootReal = existsSync10(rootResolved) ? realpathSync4(rootResolved) : rootResolved;
29131
+ const rootReal = existsSync11(rootResolved) ? realpathSync5(rootResolved) : rootResolved;
29075
29132
  let cursor = abs;
29076
29133
  while (true) {
29077
- if (existsSync10(cursor)) {
29078
- const real = realpathSync4(cursor);
29134
+ if (existsSync11(cursor)) {
29135
+ const real = realpathSync5(cursor);
29079
29136
  if (!underRoot(rootReal, real)) {
29080
29137
  throw new Error(`Refusing autofix outside repo root: ${relFile}`);
29081
29138
  }
@@ -29184,8 +29241,8 @@ function printReport(issues, options) {
29184
29241
  }
29185
29242
 
29186
29243
  // src/references/check.ts
29187
- import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
29188
- import { join as join10, relative as relative7 } from "node:path";
29244
+ import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync10 } from "node:fs";
29245
+ import { join as join11, relative as relative7 } from "node:path";
29189
29246
 
29190
29247
  // src/references/constants.ts
29191
29248
  var CANONICAL_REFS_DIR = ".skeleton/references";
@@ -29208,16 +29265,16 @@ function isGeneratedReference(content3) {
29208
29265
  }
29209
29266
 
29210
29267
  // src/references/discover.ts
29211
- import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "node:fs";
29212
- import { join as join9, relative as relative6 } from "node:path";
29268
+ import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
29269
+ import { join as join10, relative as relative6 } from "node:path";
29213
29270
  function walkMarkdownFiles(dir, root2) {
29214
29271
  const files = [];
29215
- if (!existsSync11(dir))
29272
+ if (!existsSync12(dir))
29216
29273
  return files;
29217
29274
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
29218
29275
  if (entry.name.startsWith("."))
29219
29276
  continue;
29220
- const fullPath = join9(dir, entry.name);
29277
+ const fullPath = join10(dir, entry.name);
29221
29278
  if (entry.isDirectory()) {
29222
29279
  files.push(...walkMarkdownFiles(fullPath, root2));
29223
29280
  continue;
@@ -29229,7 +29286,7 @@ function walkMarkdownFiles(dir, root2) {
29229
29286
  return files;
29230
29287
  }
29231
29288
  function canonicalExists(root2, refPath) {
29232
- return existsSync11(join9(root2, CANONICAL_REFS_DIR, refPath));
29289
+ return existsSync12(join10(root2, CANONICAL_REFS_DIR, refPath));
29233
29290
  }
29234
29291
  function findSharedRefLinks(content3, sourceFile) {
29235
29292
  const links = [];
@@ -29260,7 +29317,7 @@ function findLocalCanonicalLinks(root2, content3, sourceFile) {
29260
29317
  const raw = normalizeRelPath(match[1] ?? "");
29261
29318
  if (!raw)
29262
29319
  continue;
29263
- const refPath = withinDir ? normalizeRelPath(join9(withinDir, raw)) : raw;
29320
+ const refPath = withinDir ? normalizeRelPath(join10(withinDir, raw)) : raw;
29264
29321
  if (!canonicalExists(root2, refPath))
29265
29322
  continue;
29266
29323
  links.push({ refPath, sourceFile });
@@ -29272,13 +29329,13 @@ function discoverSkillReferencePlans(root2) {
29272
29329
  const index2 = buildSkillIndex(root2);
29273
29330
  const plans = [];
29274
29331
  for (const slug2 of index2.slugs) {
29275
- const skillDir = join9(root2, slug2);
29276
- if (!existsSync11(join9(skillDir, "SKILL.md")))
29332
+ const skillDir = join10(root2, slug2);
29333
+ if (!existsSync12(join10(skillDir, "SKILL.md")))
29277
29334
  continue;
29278
29335
  const refPaths = new Set;
29279
29336
  const links = [];
29280
29337
  for (const relFile of walkMarkdownFiles(skillDir, root2)) {
29281
- const content3 = readFileSync8(join9(root2, relFile), "utf8");
29338
+ const content3 = readFileSync9(join10(root2, relFile), "utf8");
29282
29339
  if (isGeneratedReference(content3))
29283
29340
  continue;
29284
29341
  for (const link2 of findSharedRefLinks(content3, relFile)) {
@@ -29295,7 +29352,7 @@ function discoverSkillReferencePlans(root2) {
29295
29352
  const refPath = queue.pop();
29296
29353
  if (!refPath || !canonicalExists(root2, refPath))
29297
29354
  continue;
29298
- const canonicalContent = readFileSync8(join9(root2, CANONICAL_REFS_DIR, refPath), "utf8");
29355
+ const canonicalContent = readFileSync9(join10(root2, CANONICAL_REFS_DIR, refPath), "utf8");
29299
29356
  const syntheticSource = generatedRefPath(slug2, refPath);
29300
29357
  for (const link2 of findLocalCanonicalLinks(root2, canonicalContent, syntheticSource)) {
29301
29358
  if (refPaths.has(link2.refPath))
@@ -29312,7 +29369,7 @@ function discoverSkillReferencePlans(root2) {
29312
29369
  return plans.sort((a, b) => a.skill.localeCompare(b.skill));
29313
29370
  }
29314
29371
  function generatedRefPath(skill, refPath) {
29315
- return normalizeRelPath(join9(skill, "references", refPath));
29372
+ return normalizeRelPath(join10(skill, "references", refPath));
29316
29373
  }
29317
29374
  function rewriteSharedRefTarget(sourceFile, skill, refPath) {
29318
29375
  const sourceDir = sourceFile.slice(0, sourceFile.lastIndexOf("/"));
@@ -29341,19 +29398,19 @@ function rewriteSharedRefLinks(content3, sourceFile, skill) {
29341
29398
  function listAllGeneratedFiles(root2) {
29342
29399
  const files = [];
29343
29400
  const walk = (dir) => {
29344
- if (!existsSync12(dir))
29401
+ if (!existsSync13(dir))
29345
29402
  return;
29346
29403
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
29347
29404
  if (entry.name.startsWith("."))
29348
29405
  continue;
29349
- const fullPath = join10(dir, entry.name);
29406
+ const fullPath = join11(dir, entry.name);
29350
29407
  if (entry.isDirectory()) {
29351
29408
  walk(fullPath);
29352
29409
  continue;
29353
29410
  }
29354
29411
  if (!entry.name.endsWith(".md"))
29355
29412
  continue;
29356
- const content3 = readFileSync9(fullPath, "utf8");
29413
+ const content3 = readFileSync10(fullPath, "utf8");
29357
29414
  if (isGeneratedReference(content3)) {
29358
29415
  files.push(normalizeRelPath(relative7(root2, fullPath)));
29359
29416
  }
@@ -29364,8 +29421,8 @@ function listAllGeneratedFiles(root2) {
29364
29421
  }
29365
29422
  function runGeneratedReferencesCheck(root2) {
29366
29423
  const issues = [];
29367
- const canonicalDir = join10(root2, CANONICAL_REFS_DIR);
29368
- if (!existsSync12(canonicalDir))
29424
+ const canonicalDir = join11(root2, CANONICAL_REFS_DIR);
29425
+ if (!existsSync13(canonicalDir))
29369
29426
  return issues;
29370
29427
  const plans = discoverSkillReferencePlans(root2);
29371
29428
  const needed = new Set;
@@ -29375,24 +29432,24 @@ function runGeneratedReferencesCheck(root2) {
29375
29432
  }
29376
29433
  }
29377
29434
  for (const targetRel of needed) {
29378
- const targetPath = join10(root2, targetRel);
29379
- if (!existsSync12(targetPath)) {
29435
+ const targetPath = join11(root2, targetRel);
29436
+ if (!existsSync13(targetPath)) {
29380
29437
  issues.push(issue("generated-references", targetRel, "missing generated copy — run skeleton references sync"));
29381
29438
  continue;
29382
29439
  }
29383
- const generated = readFileSync9(targetPath, "utf8");
29440
+ const generated = readFileSync10(targetPath, "utf8");
29384
29441
  if (!isGeneratedReference(generated)) {
29385
29442
  issues.push(issue("generated-references", targetRel, "expected generated-reference provenance header"));
29386
29443
  continue;
29387
29444
  }
29388
29445
  const body = stripGeneratedHeader(generated);
29389
- const sourceRel = normalizeRelPath(generated.match(/source: ([^\n]+)/)?.[1] ?? join10(CANONICAL_REFS_DIR, targetRel.split("/references/")[1] ?? ""));
29390
- const canonicalPath = join10(root2, sourceRel);
29391
- if (!existsSync12(canonicalPath)) {
29446
+ const sourceRel = normalizeRelPath(generated.match(/source: ([^\n]+)/)?.[1] ?? join11(CANONICAL_REFS_DIR, targetRel.split("/references/")[1] ?? ""));
29447
+ const canonicalPath = join11(root2, sourceRel);
29448
+ if (!existsSync13(canonicalPath)) {
29392
29449
  issues.push(issue("generated-references", targetRel, `canonical source missing: ${sourceRel}`));
29393
29450
  continue;
29394
29451
  }
29395
- const canonical = readFileSync9(canonicalPath, "utf8");
29452
+ const canonical = readFileSync10(canonicalPath, "utf8");
29396
29453
  if (body !== canonical) {
29397
29454
  issues.push(issue("generated-references", targetRel, "stale generated copy — run skeleton references sync"));
29398
29455
  }
@@ -29403,14 +29460,14 @@ function runGeneratedReferencesCheck(root2) {
29403
29460
  }
29404
29461
  }
29405
29462
  for (const plan of plans) {
29406
- const skillDir = join10(root2, plan.skill);
29407
- if (!existsSync12(skillDir))
29463
+ const skillDir = join11(root2, plan.skill);
29464
+ if (!existsSync13(skillDir))
29408
29465
  continue;
29409
29466
  const walk = (dir) => {
29410
29467
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
29411
29468
  if (entry.name.startsWith("."))
29412
29469
  continue;
29413
- const fullPath = join10(dir, entry.name);
29470
+ const fullPath = join11(dir, entry.name);
29414
29471
  if (entry.isDirectory()) {
29415
29472
  walk(fullPath);
29416
29473
  continue;
@@ -29418,7 +29475,7 @@ function runGeneratedReferencesCheck(root2) {
29418
29475
  if (!entry.name.endsWith(".md"))
29419
29476
  continue;
29420
29477
  const relFile = normalizeRelPath(relative7(root2, fullPath));
29421
- const content3 = readFileSync9(fullPath, "utf8");
29478
+ const content3 = readFileSync10(fullPath, "utf8");
29422
29479
  if (content3.match(SHARED_REF_LINK_RE)) {
29423
29480
  issues.push(issue("generated-references", relFile, "still links to shared root references/ — run skeleton references sync"));
29424
29481
  }
@@ -29449,16 +29506,16 @@ function runBannedRule(ctx) {
29449
29506
  var bannedRule = { id: "banned", run: runBannedRule };
29450
29507
 
29451
29508
  // src/audit/rules/doc-meta.ts
29452
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
29453
- import { join as join11 } from "node:path";
29509
+ import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
29510
+ import { join as join12 } from "node:path";
29454
29511
  function runDocMetaRule(ctx) {
29455
29512
  const issues = [];
29456
29513
  const today = new Date;
29457
29514
  for (const relPath2 of ctx.docMetaPaths) {
29458
- const abs = join11(ctx.root, relPath2);
29459
- if (!existsSync13(abs))
29515
+ const abs = join12(ctx.root, relPath2);
29516
+ if (!existsSync14(abs))
29460
29517
  continue;
29461
- const content3 = readFileSync10(abs, "utf8");
29518
+ const content3 = readFileSync11(abs, "utf8");
29462
29519
  if (!DOC_META_RE.test(content3)) {
29463
29520
  issues.push(issue("doc-meta", relPath2, "missing doc-meta comment (owner + last-reviewed)"));
29464
29521
  continue;
@@ -29473,6 +29530,9 @@ function runDocMetaRule(ctx) {
29473
29530
  if (ageDays > ctx.config.daysUntilStale) {
29474
29531
  issues.push(issue("doc-meta", relPath2, `doc-meta last-reviewed ${reviewedStr} is stale (>${ctx.config.daysUntilStale} days)`, { severity: "warning" }));
29475
29532
  }
29533
+ const slug2 = slugFromPath(relPath2, ctx.root);
29534
+ if (slug2 !== null && ctx.lockedSkillSlugs.has(slug2))
29535
+ continue;
29476
29536
  const gitDate = lastGitCommitDate(relPath2, ctx.root);
29477
29537
  if (!gitDate)
29478
29538
  continue;
@@ -29488,7 +29548,7 @@ function runDocMetaRule(ctx) {
29488
29548
  var docMetaRule = { id: "doc-meta", run: runDocMetaRule };
29489
29549
 
29490
29550
  // src/audit/rules/links.ts
29491
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
29551
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
29492
29552
  import { dirname as dirname7, resolve as resolve7 } from "node:path";
29493
29553
  function resolveLink2(sourceFile, target) {
29494
29554
  const withoutAnchor = target.split("#")[0]?.split("?")[0] ?? "";
@@ -29525,19 +29585,19 @@ function validateTarget(ctx, sourceFile, target, linkLabel) {
29525
29585
  }
29526
29586
  if ((target.includes(".claude/agents/") || target.includes(".cursor/agents/")) && target.endsWith(".md")) {
29527
29587
  const agentPath = resolved.endsWith(".md") ? resolved : `${resolved}.md`;
29528
- if (!existsSync14(agentPath)) {
29588
+ if (!existsSync15(agentPath)) {
29529
29589
  issues.push(issue("links", relSource, "missing agent file", { link: linkLabel }));
29530
29590
  }
29531
29591
  return issues;
29532
29592
  }
29533
- if (pathPart && !existsSync14(resolved)) {
29593
+ if (pathPart && !existsSync15(resolved)) {
29534
29594
  issues.push(issue("links", relSource, `broken link → ${relTarget}`, {
29535
29595
  link: linkLabel
29536
29596
  }));
29537
29597
  return issues;
29538
29598
  }
29539
- if (anchor && existsSync14(resolved)) {
29540
- const targetContent = readFileSync11(resolved, "utf8");
29599
+ if (anchor && existsSync15(resolved)) {
29600
+ const targetContent = readFileSync12(resolved, "utf8");
29541
29601
  const slugs = extractHeadingSlugs(targetContent, resolved);
29542
29602
  const anchorSlug = slugifyAnchor(anchor);
29543
29603
  if (!slugs.has(anchorSlug)) {
@@ -29612,8 +29672,8 @@ function runProsePolicyRule(ctx) {
29612
29672
  var prosePolicyRule = { id: "prose-policy", run: runProsePolicyRule };
29613
29673
 
29614
29674
  // src/audit/rules/registry.ts
29615
- import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
29616
- import { join as join12 } from "node:path";
29675
+ import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
29676
+ import { join as join13 } from "node:path";
29617
29677
  function runRegistryRule(ctx) {
29618
29678
  const issues = [];
29619
29679
  const registry = new Set(ctx.registryPaths);
@@ -29621,12 +29681,12 @@ function runRegistryRule(ctx) {
29621
29681
  issues.push(issue("registry", REGISTRY_REL_PATH, "registry table header found but 0 rows parsed — check | Topic | Canonical file | format and link syntax"));
29622
29682
  }
29623
29683
  for (const rel of ctx.registryPaths) {
29624
- const abs = join12(ctx.root, rel);
29625
- if (!existsSync15(abs)) {
29684
+ const abs = join13(ctx.root, rel);
29685
+ if (!existsSync16(abs)) {
29626
29686
  issues.push(issue("registry", rel, "registry entry file missing"));
29627
29687
  continue;
29628
29688
  }
29629
- const content3 = readFileSync12(abs, "utf8");
29689
+ const content3 = readFileSync13(abs, "utf8");
29630
29690
  if (!SOURCE_OF_TRUTH_BANNER_RE.test(content3)) {
29631
29691
  issues.push(issue("registry", rel, "missing **Source of truth for** banner (required for registry entry)"));
29632
29692
  }
@@ -29637,7 +29697,7 @@ function runRegistryRule(ctx) {
29637
29697
  continue;
29638
29698
  if (!rel.endsWith(".md") && !rel.endsWith(".mdc"))
29639
29699
  continue;
29640
- const content3 = readFileSync12(filePath, "utf8");
29700
+ const content3 = readFileSync13(filePath, "utf8");
29641
29701
  if (!SOURCE_OF_TRUTH_BANNER_LINE_RE.test(content3))
29642
29702
  continue;
29643
29703
  if (!registry.has(rel)) {
@@ -29674,16 +29734,16 @@ function runScanRootsRule(ctx) {
29674
29734
  var scanRootsRule = { id: "scan-roots", run: runScanRootsRule };
29675
29735
 
29676
29736
  // src/audit/rules/skill-index.ts
29677
- import { existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync13 } from "node:fs";
29678
- import { join as join13, relative as relative8 } from "node:path";
29737
+ import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "node:fs";
29738
+ import { join as join14, relative as relative8 } from "node:path";
29679
29739
  function walkSkillMarkdown(dir) {
29680
29740
  const files = [];
29681
- if (!existsSync16(dir))
29741
+ if (!existsSync17(dir))
29682
29742
  return files;
29683
29743
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
29684
29744
  if (entry.name.startsWith("."))
29685
29745
  continue;
29686
- const fullPath = join13(dir, entry.name);
29746
+ const fullPath = join14(dir, entry.name);
29687
29747
  if (entry.isDirectory()) {
29688
29748
  files.push(...walkSkillMarkdown(fullPath));
29689
29749
  continue;
@@ -29710,7 +29770,7 @@ function parseReadmeTaxonomySlugs(content3) {
29710
29770
  function scanFileForSkillLinks(ctx, filePath, index2) {
29711
29771
  const issues = [];
29712
29772
  const rel = relative8(ctx.root, filePath).replace(/\\/g, "/");
29713
- const content3 = readFileSync13(filePath, "utf8");
29773
+ const content3 = readFileSync14(filePath, "utf8");
29714
29774
  if (isGeneratedReference(content3))
29715
29775
  return issues;
29716
29776
  for (const match of content3.matchAll(SKILL_LINK_RE)) {
@@ -29733,14 +29793,14 @@ function validateReadmeTaxonomy(ctx, index2, diskSlugs) {
29733
29793
  for (const skillRoot of index2.roots) {
29734
29794
  if (skillRoot.kind !== "nested")
29735
29795
  continue;
29736
- const readmePath = join13(ctx.root, skillRoot.relPath, "README.md");
29737
- if (!existsSync16(readmePath))
29796
+ const readmePath = join14(ctx.root, skillRoot.relPath, "README.md");
29797
+ if (!existsSync17(readmePath))
29738
29798
  continue;
29739
- const readme = readFileSync13(readmePath, "utf8");
29799
+ const readme = readFileSync14(readmePath, "utf8");
29740
29800
  if (!readme.includes("## Taxonomy"))
29741
29801
  continue;
29742
29802
  const taxonomySlugs = parseReadmeTaxonomySlugs(readme);
29743
- const nestedSlugs = diskSlugs.filter((slug2) => existsSync16(join13(ctx.root, skillRoot.relPath, slug2, "SKILL.md")));
29803
+ const nestedSlugs = diskSlugs.filter((slug2) => existsSync17(join14(ctx.root, skillRoot.relPath, slug2, "SKILL.md")));
29744
29804
  const publicSlugs = nestedSlugs.filter((slug2) => !nonPublic.has(slug2));
29745
29805
  const relReadme = `${skillRoot.relPath}/README.md`;
29746
29806
  for (const slug2 of publicSlugs) {
@@ -29762,16 +29822,16 @@ function runSkillIndexRule(ctx) {
29762
29822
  const diskSlugs = listSkillSlugs(index2);
29763
29823
  issues.push(...validateReadmeTaxonomy(ctx, index2, diskSlugs));
29764
29824
  for (const skillRoot of index2.roots) {
29765
- const scanRoot = skillRoot.kind === "nested" ? join13(ctx.root, skillRoot.relPath) : join13(ctx.root);
29766
- if (skillRoot.kind === "nested" && existsSync16(scanRoot)) {
29825
+ const scanRoot = skillRoot.kind === "nested" ? join14(ctx.root, skillRoot.relPath) : join14(ctx.root);
29826
+ if (skillRoot.kind === "nested" && existsSync17(scanRoot)) {
29767
29827
  for (const skillMd of walkSkillMarkdown(scanRoot)) {
29768
29828
  issues.push(...scanFileForSkillLinks(ctx, skillMd, index2));
29769
29829
  }
29770
29830
  }
29771
29831
  if (skillRoot.kind === "flat") {
29772
29832
  for (const slug2 of index2.slugs) {
29773
- const skillDir = join13(ctx.root, slug2);
29774
- if (existsSync16(skillDir)) {
29833
+ const skillDir = join14(ctx.root, slug2);
29834
+ if (existsSync17(skillDir)) {
29775
29835
  for (const skillMd of walkSkillMarkdown(skillDir)) {
29776
29836
  issues.push(...scanFileForSkillLinks(ctx, skillMd, index2));
29777
29837
  }
@@ -29957,19 +30017,19 @@ async function runAudit(options) {
29957
30017
  }
29958
30018
 
29959
30019
  // src/customize/resolve.ts
29960
- import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
29961
- import { basename as basename3, join as join14, relative as relative9 } from "node:path";
30020
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
30021
+ import { basename as basename3, join as join15, relative as relative9 } from "node:path";
29962
30022
  var CUSTOMIZE_PREFIX = "Customize: ";
29963
30023
  function customizeDir(root2) {
29964
- return join14(root2, REGISTRY_DIR_REL, "customize");
30024
+ return join15(root2, REGISTRY_DIR_REL, "customize");
29965
30025
  }
29966
30026
  function customizePathForSlug(root2, slug2) {
29967
- return join14(customizeDir(root2), `${slug2}.md`);
30027
+ return join15(customizeDir(root2), `${slug2}.md`);
29968
30028
  }
29969
30029
  function findCustomizeViaRegistry(root2, slug2) {
29970
30030
  for (const rel of parseRegistryPaths(root2)) {
29971
30031
  const expected = `${REGISTRY_DIR_REL}/customize/${slug2}.md`;
29972
- if (normalizeRelPath(rel) === expected && existsSync17(join14(root2, rel))) {
30032
+ if (normalizeRelPath(rel) === expected && existsSync18(join15(root2, rel))) {
29973
30033
  return rel;
29974
30034
  }
29975
30035
  }
@@ -29977,17 +30037,17 @@ function findCustomizeViaRegistry(root2, slug2) {
29977
30037
  }
29978
30038
  function resolveSlugFile(root2, slug2) {
29979
30039
  const direct = customizePathForSlug(root2, slug2);
29980
- if (existsSync17(direct)) {
30040
+ if (existsSync18(direct)) {
29981
30041
  return {
29982
- content: readFileSync14(direct, "utf8"),
30042
+ content: readFileSync15(direct, "utf8"),
29983
30043
  path: normalizeRelPath(relative9(root2, direct))
29984
30044
  };
29985
30045
  }
29986
30046
  const registryPath = findCustomizeViaRegistry(root2, slug2);
29987
30047
  if (registryPath) {
29988
- const abs = join14(root2, registryPath);
30048
+ const abs = join15(root2, registryPath);
29989
30049
  return {
29990
- content: readFileSync14(abs, "utf8"),
30050
+ content: readFileSync15(abs, "utf8"),
29991
30051
  path: registryPath
29992
30052
  };
29993
30053
  }
@@ -30009,10 +30069,10 @@ function readAlwaysInclude(root2, basenames, skipBasename) {
30009
30069
  const file = basename3(name);
30010
30070
  if (skipBasename && file === skipBasename)
30011
30071
  continue;
30012
- const abs = join14(dir, file);
30013
- if (!existsSync17(abs))
30072
+ const abs = join15(dir, file);
30073
+ if (!existsSync18(abs))
30014
30074
  continue;
30015
- parts.push(readFileSync14(abs, "utf8").trimEnd());
30075
+ parts.push(readFileSync15(abs, "utf8").trimEnd());
30016
30076
  paths.push(normalizeRelPath(relative9(root2, abs)));
30017
30077
  }
30018
30078
  return { parts, paths };
@@ -30053,38 +30113,38 @@ function resolveCustomizeFromRoot(slug2, startDir) {
30053
30113
 
30054
30114
  // src/init/init.ts
30055
30115
  import { spawnSync as spawnSync2 } from "node:child_process";
30056
- import { copyFileSync, existsSync as existsSync21, mkdirSync as mkdirSync2, readFileSync as readFileSync16 } from "node:fs";
30057
- import { join as join18 } from "node:path";
30116
+ import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync2, readFileSync as readFileSync17 } from "node:fs";
30117
+ import { join as join19 } from "node:path";
30058
30118
 
30059
30119
  // src/init/merge-hooks.ts
30060
- import { existsSync as existsSync20, mkdirSync, readFileSync as readFileSync15, writeFileSync as writeFileSync2 } from "node:fs";
30061
- import { dirname as dirname10, join as join17 } from "node:path";
30120
+ import { existsSync as existsSync21, mkdirSync, readFileSync as readFileSync16, writeFileSync as writeFileSync2 } from "node:fs";
30121
+ import { dirname as dirname10, join as join18 } from "node:path";
30062
30122
 
30063
30123
  // src/init/package-paths.ts
30064
- import { existsSync as existsSync18 } from "node:fs";
30065
- import { dirname as dirname8, join as join15 } from "node:path";
30124
+ import { existsSync as existsSync19 } from "node:fs";
30125
+ import { dirname as dirname8, join as join16 } from "node:path";
30066
30126
  import { fileURLToPath as fileURLToPath5 } from "node:url";
30067
30127
  var MODULE_DIR = dirname8(fileURLToPath5(import.meta.url));
30068
- var PACKAGE_ROOT_CANDIDATES = [join15(MODULE_DIR, "../.."), join15(MODULE_DIR, "..")];
30128
+ var PACKAGE_ROOT_CANDIDATES = [join16(MODULE_DIR, "../.."), join16(MODULE_DIR, "..")];
30069
30129
  function resolvePackageRoot() {
30070
30130
  for (const candidate of PACKAGE_ROOT_CANDIDATES) {
30071
- if (existsSync18(join15(candidate, "package.json")))
30131
+ if (existsSync19(join16(candidate, "package.json")))
30072
30132
  return candidate;
30073
30133
  }
30074
30134
  throw new Error("Could not resolve @csark0812/skeleton package root");
30075
30135
  }
30076
30136
  function resolveTemplatesDir() {
30077
- const dir = join15(resolvePackageRoot(), "templates/skeleton-init");
30078
- if (!existsSync18(dir)) {
30137
+ const dir = join16(resolvePackageRoot(), "templates/skeleton-init");
30138
+ if (!existsSync19(dir)) {
30079
30139
  throw new Error("Missing templates/skeleton-init in package");
30080
30140
  }
30081
30141
  return dir;
30082
30142
  }
30083
30143
 
30084
30144
  // src/init/resolve-hook-command.ts
30085
- import { existsSync as existsSync19 } from "node:fs";
30145
+ import { existsSync as existsSync20 } from "node:fs";
30086
30146
  import { createRequire as createRequire3 } from "node:module";
30087
- import { dirname as dirname9, join as join16, relative as relative10, resolve as resolve8 } from "node:path";
30147
+ import { dirname as dirname9, join as join17, relative as relative10, resolve as resolve8 } from "node:path";
30088
30148
  var PACKAGE_NAME = "@csark0812/skeleton";
30089
30149
  var HOOK_DIST = "dist/hooks/customize-on-skill-read.js";
30090
30150
  var HOOK_SRC = "src/hooks/customize-on-skill-read.ts";
@@ -30095,7 +30155,7 @@ function toRepoRelative(cwd, absPath) {
30095
30155
  }
30096
30156
  function tryResolvePublished(cwd) {
30097
30157
  try {
30098
- const req = createRequire3(join16(cwd, "package.json"));
30158
+ const req = createRequire3(join17(cwd, "package.json"));
30099
30159
  return req.resolve(`${PACKAGE_NAME}/${HOOK_DIST}`);
30100
30160
  } catch {
30101
30161
  return null;
@@ -30104,8 +30164,8 @@ function tryResolvePublished(cwd) {
30104
30164
  function walkNodeModules(cwd) {
30105
30165
  let dir = cwd;
30106
30166
  while (true) {
30107
- const candidate = join16(dir, "node_modules", PACKAGE_NAME, HOOK_DIST);
30108
- if (existsSync19(candidate))
30167
+ const candidate = join17(dir, "node_modules", PACKAGE_NAME, HOOK_DIST);
30168
+ if (existsSync20(candidate))
30109
30169
  return candidate;
30110
30170
  const parent = dirname9(dir);
30111
30171
  if (parent === dir)
@@ -30126,11 +30186,11 @@ function resolveHookCommand(cwd) {
30126
30186
  if (hoisted)
30127
30187
  return toRepoRelative(cwd, hoisted);
30128
30188
  if (isInsidePackageRoot(cwd)) {
30129
- const distHook = join16(PACKAGE_ROOT, HOOK_DIST);
30130
- if (existsSync19(distHook))
30189
+ const distHook = join17(PACKAGE_ROOT, HOOK_DIST);
30190
+ if (existsSync20(distHook))
30131
30191
  return toRepoRelative(cwd, distHook);
30132
- const srcHook = join16(PACKAGE_ROOT, HOOK_SRC);
30133
- if (existsSync19(srcHook)) {
30192
+ const srcHook = join17(PACKAGE_ROOT, HOOK_SRC);
30193
+ if (existsSync20(srcHook)) {
30134
30194
  const rel = toRepoRelative(cwd, srcHook);
30135
30195
  return rel.includes("/") ? `bun ${rel}` : `bun ./${rel}`;
30136
30196
  }
@@ -30149,14 +30209,14 @@ function identityKey(platform, event, matcher) {
30149
30209
  return `skeleton:customize:${platform}:${event}:${matcher}`;
30150
30210
  }
30151
30211
  function loadFragment(name, hookCommand) {
30152
- const raw = readFileSync15(join17(TEMPLATES_DIR, name), "utf8");
30212
+ const raw = readFileSync16(join18(TEMPLATES_DIR, name), "utf8");
30153
30213
  return JSON.parse(raw.replaceAll("{{HOOK_COMMAND}}", hookCommand));
30154
30214
  }
30155
30215
  function readJson(path2) {
30156
- if (!existsSync20(path2))
30216
+ if (!existsSync21(path2))
30157
30217
  return null;
30158
30218
  try {
30159
- return JSON.parse(readFileSync15(path2, "utf8"));
30219
+ return JSON.parse(readFileSync16(path2, "utf8"));
30160
30220
  } catch (error) {
30161
30221
  throw new Error(`Invalid JSON in ${path2}: ${error}`);
30162
30222
  }
@@ -30262,14 +30322,14 @@ function mergeNestedHooks(platform, targetPath, fragment, eventName, opts) {
30262
30322
  }
30263
30323
  function mergeHookConfigs(opts) {
30264
30324
  const results = [];
30265
- const cursorPath = join17(opts.cwd, ".cursor/hooks.json");
30325
+ const cursorPath = join18(opts.cwd, ".cursor/hooks.json");
30266
30326
  const cursorFragment = loadFragment("cursor-hooks.fragment.json", opts.hookCommand);
30267
30327
  results.push(mergeCursorHooks(cursorPath, cursorFragment, opts));
30268
- const claudePath = join17(opts.cwd, ".claude/settings.json");
30328
+ const claudePath = join18(opts.cwd, ".claude/settings.json");
30269
30329
  const claudeFragment = loadFragment("claude-settings.fragment.json", opts.hookCommand);
30270
30330
  results.push(mergeNestedHooks("claude", claudePath, claudeFragment, "PostToolUse", opts));
30271
- const codexPath = join17(opts.cwd, ".codex/hooks.json");
30272
- if (existsSync20(join17(opts.cwd, ".codex"))) {
30331
+ const codexPath = join18(opts.cwd, ".codex/hooks.json");
30332
+ if (existsSync21(join18(opts.cwd, ".codex"))) {
30273
30333
  const codexFragment = loadFragment("codex-hooks.fragment.json", opts.hookCommand);
30274
30334
  results.push(mergeNestedHooks("codex", codexPath, codexFragment, "PostToolUse", opts));
30275
30335
  } else {
@@ -30278,11 +30338,11 @@ function mergeHookConfigs(opts) {
30278
30338
  return results;
30279
30339
  }
30280
30340
  function mergePackageJsonScripts(cwd) {
30281
- const pkgPath = join17(cwd, "package.json");
30282
- if (!existsSync20(pkgPath))
30341
+ const pkgPath = join18(cwd, "package.json");
30342
+ if (!existsSync21(pkgPath))
30283
30343
  return "skipped";
30284
- const fragment = JSON.parse(readFileSync15(join17(TEMPLATES_DIR, "package.json.scripts.fragment.json"), "utf8"));
30285
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
30344
+ const fragment = JSON.parse(readFileSync16(join18(TEMPLATES_DIR, "package.json.scripts.fragment.json"), "utf8"));
30345
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
30286
30346
  pkg.scripts ??= {};
30287
30347
  let changed = false;
30288
30348
  for (const [key, value] of Object.entries(fragment)) {
@@ -30338,27 +30398,27 @@ function skillsAddArgs(options = {}) {
30338
30398
  // src/init/init.ts
30339
30399
  var TEMPLATES_DIR2 = resolveTemplatesDir();
30340
30400
  function writeScaffold(cwd) {
30341
- const skeletonDir2 = join18(cwd, ".skeleton");
30401
+ const skeletonDir2 = join19(cwd, ".skeleton");
30342
30402
  mkdirSync2(skeletonDir2, { recursive: true });
30343
30403
  let created = false;
30344
- const configPath = join18(skeletonDir2, "config.yaml");
30345
- if (!existsSync21(configPath)) {
30346
- copyFileSync(join18(TEMPLATES_DIR2, "config.yaml"), configPath);
30404
+ const configPath = join19(skeletonDir2, "config.yaml");
30405
+ if (!existsSync22(configPath)) {
30406
+ copyFileSync(join19(TEMPLATES_DIR2, "config.yaml"), configPath);
30347
30407
  created = true;
30348
30408
  }
30349
- const registryPath = join18(skeletonDir2, "registry.md");
30350
- if (!existsSync21(registryPath)) {
30351
- copyFileSync(join18(TEMPLATES_DIR2, "registry.md"), registryPath);
30409
+ const registryPath = join19(skeletonDir2, "registry.md");
30410
+ if (!existsSync22(registryPath)) {
30411
+ copyFileSync(join19(TEMPLATES_DIR2, "registry.md"), registryPath);
30352
30412
  created = true;
30353
30413
  }
30354
- mkdirSync2(join18(skeletonDir2, "customize"), { recursive: true });
30414
+ mkdirSync2(join19(skeletonDir2, "customize"), { recursive: true });
30355
30415
  return created ? "created" : "skipped";
30356
30416
  }
30357
30417
  function assertPackageResolvable(cwd) {
30358
- const pkgPath = join18(cwd, "package.json");
30359
- if (!existsSync21(pkgPath))
30418
+ const pkgPath = join19(cwd, "package.json");
30419
+ if (!existsSync22(pkgPath))
30360
30420
  return;
30361
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
30421
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
30362
30422
  const hasDep = pkg.devDependencies?.["@csark0812/skeleton"] || pkg.dependencies?.["@csark0812/skeleton"];
30363
30423
  if (!hasDep) {
30364
30424
  try {
@@ -30442,7 +30502,7 @@ function parseInitArgs(argv) {
30442
30502
  // src/plugins/build.ts
30443
30503
  import { spawnSync as spawnSync3 } from "node:child_process";
30444
30504
  import { createHash } from "node:crypto";
30445
- import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync3 } from "node:fs";
30505
+ import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync3 } from "node:fs";
30446
30506
  import { basename as basename4, dirname as dirname11, resolve as resolve9 } from "node:path";
30447
30507
  function parseBuildPluginArgs(argv) {
30448
30508
  let check = false;
@@ -30483,7 +30543,7 @@ function localImportPaths(tsAbs, content3) {
30483
30543
  candidates.push(resolve9(dir, spec), resolve9(dir, `${spec}.ts`), resolve9(dir, `${spec}.js`), resolve9(dir, spec, "index.ts"));
30484
30544
  }
30485
30545
  for (const candidate of candidates) {
30486
- if (existsSync22(candidate) && candidate.endsWith(".ts")) {
30546
+ if (existsSync23(candidate) && candidate.endsWith(".ts")) {
30487
30547
  deps.push(candidate);
30488
30548
  break;
30489
30549
  }
@@ -30500,7 +30560,7 @@ function sourceFingerprint(tsAbs, seen = new Set) {
30500
30560
  if (seen.has(abs))
30501
30561
  return;
30502
30562
  seen.add(abs);
30503
- const content3 = readFileSync17(abs, "utf8");
30563
+ const content3 = readFileSync18(abs, "utf8");
30504
30564
  hash.update(basename4(abs));
30505
30565
  hash.update("\x00");
30506
30566
  hash.update(content3);
@@ -30518,7 +30578,7 @@ function writeStamp(tsAbs, mjsAbs) {
30518
30578
  }
30519
30579
  async function buildOne(tsAbs) {
30520
30580
  const mjsAbs = mjsPathForTs(tsAbs);
30521
- if (!existsSync22(tsAbs)) {
30581
+ if (!existsSync23(tsAbs)) {
30522
30582
  throw new Error(`Plugin source not found: ${tsAbs}`);
30523
30583
  }
30524
30584
  const proc = spawnSync3("bun", ["build", tsAbs, "--target=node", "--format=esm", `--outfile=${mjsAbs}`, "--packages=external"], { encoding: "utf8" });
@@ -30538,17 +30598,17 @@ ${proc.stderr || proc.stdout || `exit ${proc.status}`}`);
30538
30598
  }
30539
30599
  function checkOne(tsAbs) {
30540
30600
  const mjsAbs = mjsPathForTs(tsAbs);
30541
- if (!existsSync22(mjsAbs)) {
30601
+ if (!existsSync23(mjsAbs)) {
30542
30602
  throw new Error(`Plugin not built: ${tsAbs} (missing ${mjsAbs}). Run: skeleton build-plugin`);
30543
30603
  }
30544
- if (!existsSync22(tsAbs)) {
30604
+ if (!existsSync23(tsAbs)) {
30545
30605
  throw new Error(`Plugin source not found: ${tsAbs}`);
30546
30606
  }
30547
30607
  const stampAbs = stampPathForMjs(mjsAbs);
30548
- if (!existsSync22(stampAbs)) {
30608
+ if (!existsSync23(stampAbs)) {
30549
30609
  throw new Error(`Plugin stale: ${mjsAbs} has no fingerprint stamp. Run: skeleton build-plugin`);
30550
30610
  }
30551
- const expected = readFileSync17(stampAbs, "utf8").trim();
30611
+ const expected = readFileSync18(stampAbs, "utf8").trim();
30552
30612
  const actual = sourceFingerprint(tsAbs);
30553
30613
  if (expected !== actual) {
30554
30614
  throw new Error(`Plugin stale: ${mjsAbs} does not match ${tsAbs} (or local imports). Run: skeleton build-plugin`);
@@ -30578,22 +30638,22 @@ async function runBuildPlugin(options = {}) {
30578
30638
 
30579
30639
  // src/references/sync.ts
30580
30640
  import {
30581
- existsSync as existsSync23,
30641
+ existsSync as existsSync24,
30582
30642
  mkdirSync as mkdirSync3,
30583
30643
  readdirSync as readdirSync6,
30584
- readFileSync as readFileSync18,
30644
+ readFileSync as readFileSync19,
30585
30645
  unlinkSync,
30586
30646
  writeFileSync as writeFileSync4
30587
30647
  } from "node:fs";
30588
- import { dirname as dirname12, join as join19, relative as relative11 } from "node:path";
30648
+ import { dirname as dirname12, join as join20, relative as relative11 } from "node:path";
30589
30649
  function walkMarkdownFiles2(dir, root2) {
30590
30650
  const files = [];
30591
- if (!existsSync23(dir))
30651
+ if (!existsSync24(dir))
30592
30652
  return files;
30593
30653
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
30594
30654
  if (entry.name.startsWith("."))
30595
30655
  continue;
30596
- const fullPath = join19(dir, entry.name);
30656
+ const fullPath = join20(dir, entry.name);
30597
30657
  if (entry.isDirectory()) {
30598
30658
  files.push(...walkMarkdownFiles2(fullPath, root2));
30599
30659
  continue;
@@ -30605,20 +30665,20 @@ function walkMarkdownFiles2(dir, root2) {
30605
30665
  return files;
30606
30666
  }
30607
30667
  function listGeneratedReferenceFiles(skillDir, skill) {
30608
- const refsDir = join19(skillDir, "references");
30609
- if (!existsSync23(refsDir))
30668
+ const refsDir = join20(skillDir, "references");
30669
+ if (!existsSync24(refsDir))
30610
30670
  return [];
30611
30671
  const files = [];
30612
30672
  const walk = (dir) => {
30613
30673
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
30614
- const fullPath = join19(dir, entry.name);
30674
+ const fullPath = join20(dir, entry.name);
30615
30675
  if (entry.isDirectory()) {
30616
30676
  walk(fullPath);
30617
30677
  continue;
30618
30678
  }
30619
30679
  if (!entry.name.endsWith(".md"))
30620
30680
  continue;
30621
- const content3 = readFileSync18(fullPath, "utf8");
30681
+ const content3 = readFileSync19(fullPath, "utf8");
30622
30682
  if (isGeneratedReference(content3)) {
30623
30683
  const refPath = normalizeRelPath(relative11(refsDir, fullPath));
30624
30684
  files.push(generatedRefPath(skill, refPath));
@@ -30630,8 +30690,8 @@ function listGeneratedReferenceFiles(skillDir, skill) {
30630
30690
  }
30631
30691
  function syncReferences(options = {}) {
30632
30692
  const root2 = options.root ?? process.cwd();
30633
- const canonicalDir = join19(root2, CANONICAL_REFS_DIR);
30634
- if (!existsSync23(canonicalDir)) {
30693
+ const canonicalDir = join20(root2, CANONICAL_REFS_DIR);
30694
+ if (!existsSync24(canonicalDir)) {
30635
30695
  throw new Error(`canonical references dir not found: ${CANONICAL_REFS_DIR}`);
30636
30696
  }
30637
30697
  const result = {
@@ -30642,21 +30702,21 @@ function syncReferences(options = {}) {
30642
30702
  };
30643
30703
  const plans = discoverSkillReferencePlans(root2);
30644
30704
  for (const plan of plans) {
30645
- const skillDir = join19(root2, plan.skill);
30705
+ const skillDir = join20(root2, plan.skill);
30646
30706
  for (const refPath of plan.refPaths) {
30647
- const sourceRel = normalizeRelPath(join19(CANONICAL_REFS_DIR, refPath));
30648
- const canonicalPath = join19(root2, sourceRel);
30649
- if (!existsSync23(canonicalPath)) {
30707
+ const sourceRel = normalizeRelPath(join20(CANONICAL_REFS_DIR, refPath));
30708
+ const canonicalPath = join20(root2, sourceRel);
30709
+ if (!existsSync24(canonicalPath)) {
30650
30710
  throw new Error(`canonical reference missing: ${sourceRel}`);
30651
30711
  }
30652
30712
  const targetRel = generatedRefPath(plan.skill, refPath);
30653
- const targetPath = join19(root2, targetRel);
30654
- const canonicalContent = readFileSync18(canonicalPath, "utf8");
30713
+ const targetPath = join20(root2, targetRel);
30714
+ const canonicalContent = readFileSync19(canonicalPath, "utf8");
30655
30715
  const nextContent = formatGeneratedHeader(sourceRel) + canonicalContent;
30656
30716
  if (!options.dryRun) {
30657
30717
  mkdirSync3(dirname12(targetPath), { recursive: true });
30658
30718
  }
30659
- const existing = existsSync23(targetPath) ? readFileSync18(targetPath, "utf8") : null;
30719
+ const existing = existsSync24(targetPath) ? readFileSync19(targetPath, "utf8") : null;
30660
30720
  if (existing !== nextContent) {
30661
30721
  if (!options.dryRun)
30662
30722
  writeFileSync4(targetPath, nextContent, "utf8");
@@ -30667,8 +30727,8 @@ function syncReferences(options = {}) {
30667
30727
  }
30668
30728
  if (options.rewriteLinks !== false) {
30669
30729
  for (const relFile of walkMarkdownFiles2(skillDir, root2)) {
30670
- const filePath = join19(root2, relFile);
30671
- const content3 = readFileSync18(filePath, "utf8");
30730
+ const filePath = join20(root2, relFile);
30731
+ const content3 = readFileSync19(filePath, "utf8");
30672
30732
  const next = rewriteSharedRefLinks(content3, relFile, plan.skill);
30673
30733
  if (next !== content3) {
30674
30734
  if (!options.dryRun)
@@ -30680,7 +30740,7 @@ function syncReferences(options = {}) {
30680
30740
  for (const generatedRel of listGeneratedReferenceFiles(skillDir, plan.skill)) {
30681
30741
  const refPath = generatedRel.slice(`${plan.skill}/references/`.length);
30682
30742
  if (!plan.refPaths.has(refPath)) {
30683
- const fullPath = join19(root2, generatedRel);
30743
+ const fullPath = join20(root2, generatedRel);
30684
30744
  if (!options.dryRun)
30685
30745
  unlinkSync(fullPath);
30686
30746
  result.removed.push(generatedRel);
@@ -30725,8 +30785,8 @@ function printSyncResult(result) {
30725
30785
  }
30726
30786
 
30727
30787
  // src/register.ts
30728
- import { existsSync as existsSync24, readFileSync as readFileSync19, writeFileSync as writeFileSync5 } from "node:fs";
30729
- import { dirname as dirname13, join as join20, relative as relative12 } from "node:path";
30788
+ import { existsSync as existsSync25, readFileSync as readFileSync20, writeFileSync as writeFileSync5 } from "node:fs";
30789
+ import { dirname as dirname13, join as join21, relative as relative12 } from "node:path";
30730
30790
  var REGISTRY_TABLE_ROW_RE2 = /^\|\s*([^|]+)\|\s*\[[^\]]*\]\(([^)]+)\)\s*\|/;
30731
30791
  var REGISTRY_TABLE_HEADER = "| Topic | Canonical file |";
30732
30792
  function extractTopic(content3) {
@@ -30734,7 +30794,7 @@ function extractTopic(content3) {
30734
30794
  return match?.[1]?.trim().replace(/\s+$/, "") ?? null;
30735
30795
  }
30736
30796
  function toRegistryLink(root2, absPath) {
30737
- const fromRegistry = join20(root2, REGISTRY_DIR_REL);
30797
+ const fromRegistry = join21(root2, REGISTRY_DIR_REL);
30738
30798
  return normalizeRelPath(relative12(fromRegistry, absPath));
30739
30799
  }
30740
30800
  function inferSection(registryLink) {
@@ -30759,7 +30819,7 @@ function parseRegistryRows(content3) {
30759
30819
  return rows;
30760
30820
  }
30761
30821
  function pathFromRegistryLink(root2, link2) {
30762
- return normalizeRelPath(relative12(root2, join20(root2, REGISTRY_DIR_REL, link2)));
30822
+ return normalizeRelPath(relative12(root2, join21(root2, REGISTRY_DIR_REL, link2)));
30763
30823
  }
30764
30824
  function isOutsideScan(root2, relPath2) {
30765
30825
  const config = loadConfig(root2);
@@ -30826,11 +30886,11 @@ ${newLine}
30826
30886
  function registerPath(options) {
30827
30887
  const root2 = options.root ?? findRepoRoot();
30828
30888
  const relPath2 = normalizeRelPath(options.path);
30829
- const absPath = join20(root2, relPath2);
30830
- if (!existsSync24(absPath)) {
30889
+ const absPath = join21(root2, relPath2);
30890
+ if (!existsSync25(absPath)) {
30831
30891
  throw new Error(`File not found: ${relPath2}`);
30832
30892
  }
30833
- const content3 = readFileSync19(absPath, "utf8");
30893
+ const content3 = readFileSync20(absPath, "utf8");
30834
30894
  let topic = options.topic ?? extractTopic(content3);
30835
30895
  if (!topic) {
30836
30896
  throw new Error(`No **Source of truth for** banner in ${relPath2} — add banner or pass --topic`);
@@ -30838,9 +30898,9 @@ function registerPath(options) {
30838
30898
  const registryLink = toRegistryLink(root2, absPath);
30839
30899
  topic = ensureCustomizeTopic(topic, registryLink);
30840
30900
  const section = inferSection(registryLink);
30841
- const registryAbs = join20(root2, REGISTRY_REL_PATH);
30842
- let registryContent = existsSync24(registryAbs) ? readFileSync19(registryAbs, "utf8") : defaultRegistryContent();
30843
- if (!existsSync24(registryAbs) && !existsSync24(join20(root2, ".skeleton/config.yaml"))) {
30901
+ const registryAbs = join21(root2, REGISTRY_REL_PATH);
30902
+ let registryContent = existsSync25(registryAbs) ? readFileSync20(registryAbs, "utf8") : defaultRegistryContent();
30903
+ if (!existsSync25(registryAbs) && !existsSync25(join21(root2, ".skeleton/config.yaml"))) {
30844
30904
  throw new Error("Missing .skeleton/config.yaml — run skeleton init first");
30845
30905
  }
30846
30906
  const { content: updated, action } = upsertRow(registryContent, topic, registryLink, section, root2);
@@ -30854,7 +30914,7 @@ function registerPath(options) {
30854
30914
  };
30855
30915
  if (!options.dryRun && action !== "noop") {
30856
30916
  const dir = dirname13(registryAbs);
30857
- if (!existsSync24(dir)) {
30917
+ if (!existsSync25(dir)) {
30858
30918
  throw new Error(`Missing ${REGISTRY_DIR_REL}/ directory`);
30859
30919
  }
30860
30920
  writeFileSync5(registryAbs, registryContent, "utf8");
@@ -30876,8 +30936,8 @@ function registerPath(options) {
30876
30936
 
30877
30937
  // src/validate/changed.ts
30878
30938
  import { spawnSync as spawnSync5 } from "node:child_process";
30879
- import { existsSync as existsSync25, readFileSync as readFileSync20 } from "node:fs";
30880
- import { basename as basename5, extname as extname2, join as join21 } from "node:path";
30939
+ import { existsSync as existsSync26, readFileSync as readFileSync21 } from "node:fs";
30940
+ import { basename as basename5, extname as extname2, join as join22 } from "node:path";
30881
30941
 
30882
30942
  // src/validate/git-diff.ts
30883
30943
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -30960,9 +31020,9 @@ function parseJsonContent(content3) {
30960
31020
  }
30961
31021
  }
30962
31022
  function validateJson(relPath2, root2) {
30963
- const abs = join21(root2, relPath2);
31023
+ const abs = join22(root2, relPath2);
30964
31024
  try {
30965
- parseJsonContent(readFileSync20(abs, "utf8"));
31025
+ parseJsonContent(readFileSync21(abs, "utf8"));
30966
31026
  return 0;
30967
31027
  } catch (error) {
30968
31028
  console.error(`validate changed: invalid JSON in ${relPath2}: ${error}`);
@@ -30970,9 +31030,9 @@ function validateJson(relPath2, root2) {
30970
31030
  }
30971
31031
  }
30972
31032
  function validatePolicy(relPath2, root2) {
30973
- const abs = join21(root2, relPath2);
31033
+ const abs = join22(root2, relPath2);
30974
31034
  try {
30975
- loadPolicyFile(abs, readFileSync20(abs, "utf8"));
31035
+ loadPolicyFile(abs, readFileSync21(abs, "utf8"));
30976
31036
  return 0;
30977
31037
  } catch (error) {
30978
31038
  console.error(`validate changed: invalid policy ${relPath2}: ${error}`);
@@ -30980,7 +31040,7 @@ function validatePolicy(relPath2, root2) {
30980
31040
  }
30981
31041
  }
30982
31042
  function validateShell(relPath2, root2) {
30983
- const abs = join21(root2, relPath2);
31043
+ const abs = join22(root2, relPath2);
30984
31044
  const shellcheck = spawnSync5("shellcheck", [abs], { encoding: "utf8" });
30985
31045
  if (shellcheck.status === 0)
30986
31046
  return 0;
@@ -31002,23 +31062,23 @@ function resolvePaths(options) {
31002
31062
  }
31003
31063
  function codeValidationHint(root2) {
31004
31064
  let pm2 = null;
31005
- const pkgPath = join21(root2, "package.json");
31006
- if (existsSync25(pkgPath)) {
31065
+ const pkgPath = join22(root2, "package.json");
31066
+ if (existsSync26(pkgPath)) {
31007
31067
  try {
31008
- const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
31068
+ const pkg = JSON.parse(readFileSync21(pkgPath, "utf8"));
31009
31069
  const raw = pkg.packageManager?.split("@")[0];
31010
31070
  if (raw === "bun" || raw === "npm" || raw === "pnpm" || raw === "yarn")
31011
31071
  pm2 = raw;
31012
31072
  } catch {}
31013
31073
  }
31014
31074
  if (!pm2) {
31015
- if (existsSync25(join21(root2, "bun.lock")) || existsSync25(join21(root2, "bun.lockb")))
31075
+ if (existsSync26(join22(root2, "bun.lock")) || existsSync26(join22(root2, "bun.lockb")))
31016
31076
  pm2 = "bun";
31017
- else if (existsSync25(join21(root2, "pnpm-lock.yaml")))
31077
+ else if (existsSync26(join22(root2, "pnpm-lock.yaml")))
31018
31078
  pm2 = "pnpm";
31019
- else if (existsSync25(join21(root2, "yarn.lock")))
31079
+ else if (existsSync26(join22(root2, "yarn.lock")))
31020
31080
  pm2 = "yarn";
31021
- else if (existsSync25(join21(root2, "package-lock.json")))
31081
+ else if (existsSync26(join22(root2, "package-lock.json")))
31022
31082
  pm2 = "npm";
31023
31083
  }
31024
31084
  switch (pm2) {
@@ -31061,8 +31121,8 @@ async function runValidateChanged(options = {}) {
31061
31121
  const orphans = [];
31062
31122
  for (const relPath2 of relPaths) {
31063
31123
  const normalized = normalizeRelPath(relPath2);
31064
- const abs = join21(root2, normalized);
31065
- if (!existsSync25(abs)) {
31124
+ const abs = join22(root2, normalized);
31125
+ if (!existsSync26(abs)) {
31066
31126
  missing++;
31067
31127
  console.error(`validate changed: path not found: ${relPath2}`);
31068
31128
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csark0812/skeleton",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "SSOT audit CLI for agent harness repos",
5
5
  "type": "module",
6
6
  "bin": {