@csark0812/skeleton 1.5.7 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3037,7 +3037,6 @@ var require_utils = __commonJS((exports, module) => {
3037
3037
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3038
3038
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3039
3039
  var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3040
- var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/?]$/iu);
3041
3040
  function stringArrayToHexStripped(input) {
3042
3041
  let acc = "";
3043
3042
  let code = 0;
@@ -3181,7 +3180,7 @@ var require_utils = __commonJS((exports, module) => {
3181
3180
  continue;
3182
3181
  }
3183
3182
  } else if (input[0] === "/") {
3184
- if (input[1] === ".") {
3183
+ if (input[1] === "." || input[1] === "/") {
3185
3184
  output.push("/");
3186
3185
  break;
3187
3186
  }
@@ -3263,30 +3262,10 @@ var require_utils = __commonJS((exports, module) => {
3263
3262
  }
3264
3263
  return output;
3265
3264
  }
3266
- var BYTE_HEX = new Array(256);
3267
- {
3268
- const HEX_DIGITS = "0123456789ABCDEF";
3269
- for (let i = 0;i < 256; i++) {
3270
- BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
3271
- }
3272
- }
3273
- function isEscapeSafe(cp) {
3274
- return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
3275
- }
3276
- function percentEncodeNonAscii(cp) {
3277
- if (cp < 2048) {
3278
- return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
3279
- }
3280
- if (cp < 65536) {
3281
- return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3282
- }
3283
- return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3284
- }
3285
3265
  function normalizePathEncoding(input) {
3286
3266
  let output = "";
3287
3267
  for (let i = 0;i < input.length; i++) {
3288
- const ch = input[i];
3289
- if (ch === "%" && i + 2 < input.length) {
3268
+ if (input[i] === "%" && i + 2 < input.length) {
3290
3269
  const hex = input.slice(i + 1, i + 3);
3291
3270
  if (isHexPair(hex)) {
3292
3271
  const normalizedHex = hex.toUpperCase();
@@ -3300,66 +3279,10 @@ var require_utils = __commonJS((exports, module) => {
3300
3279
  continue;
3301
3280
  }
3302
3281
  }
3303
- if (isPathCharacter(ch)) {
3304
- output += ch;
3305
- } else {
3306
- const code = input.charCodeAt(i);
3307
- if (code < 128) {
3308
- output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3309
- } else if (code < 55296 || code > 57343) {
3310
- output += percentEncodeNonAscii(code);
3311
- } else if (code <= 56319 && i + 1 < input.length) {
3312
- const low = input.charCodeAt(i + 1);
3313
- if (low >= 56320 && low <= 57343) {
3314
- output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3315
- i++;
3316
- } else {
3317
- output += percentEncodeNonAscii(65533);
3318
- }
3319
- } else {
3320
- output += percentEncodeNonAscii(65533);
3321
- }
3322
- }
3323
- }
3324
- return output;
3325
- }
3326
- function normalizeQueryFragmentEncoding(input) {
3327
- let output = "";
3328
- for (let i = 0;i < input.length; i++) {
3329
- const ch = input[i];
3330
- if (ch === "%" && i + 2 < input.length) {
3331
- const hex = input.slice(i + 1, i + 3);
3332
- if (isHexPair(hex)) {
3333
- const normalizedHex = hex.toUpperCase();
3334
- const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3335
- if (isUnreserved(decoded)) {
3336
- output += decoded;
3337
- } else {
3338
- output += "%" + normalizedHex;
3339
- }
3340
- i += 2;
3341
- continue;
3342
- }
3343
- }
3344
- if (isQueryFragmentCharacter(ch)) {
3345
- output += ch;
3282
+ if (isPathCharacter(input[i])) {
3283
+ output += input[i];
3346
3284
  } else {
3347
- const code = input.charCodeAt(i);
3348
- if (code < 128) {
3349
- output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3350
- } else if (code < 55296 || code > 57343) {
3351
- output += percentEncodeNonAscii(code);
3352
- } else if (code <= 56319 && i + 1 < input.length) {
3353
- const low = input.charCodeAt(i + 1);
3354
- if (low >= 56320 && low <= 57343) {
3355
- output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3356
- i++;
3357
- } else {
3358
- output += percentEncodeNonAscii(65533);
3359
- }
3360
- } else {
3361
- output += percentEncodeNonAscii(65533);
3362
- }
3285
+ output += escape(input[i]);
3363
3286
  }
3364
3287
  }
3365
3288
  return output;
@@ -3367,8 +3290,7 @@ var require_utils = __commonJS((exports, module) => {
3367
3290
  function escapePreservingEscapes(input) {
3368
3291
  let output = "";
3369
3292
  for (let i = 0;i < input.length; i++) {
3370
- const ch = input[i];
3371
- if (ch === "%" && i + 2 < input.length) {
3293
+ if (input[i] === "%" && i + 2 < input.length) {
3372
3294
  const hex = input.slice(i + 1, i + 3);
3373
3295
  if (isHexPair(hex)) {
3374
3296
  output += "%" + hex.toUpperCase();
@@ -3376,22 +3298,7 @@ var require_utils = __commonJS((exports, module) => {
3376
3298
  continue;
3377
3299
  }
3378
3300
  }
3379
- const code = input.charCodeAt(i);
3380
- if (code < 128) {
3381
- output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3382
- } else if (code < 55296 || code > 57343) {
3383
- output += percentEncodeNonAscii(code);
3384
- } else if (code <= 56319 && i + 1 < input.length) {
3385
- const low = input.charCodeAt(i + 1);
3386
- if (low >= 56320 && low <= 57343) {
3387
- output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3388
- i++;
3389
- } else {
3390
- output += percentEncodeNonAscii(65533);
3391
- }
3392
- } else {
3393
- output += percentEncodeNonAscii(65533);
3394
- }
3301
+ output += escape(input[i]);
3395
3302
  }
3396
3303
  return output;
3397
3304
  }
@@ -3425,7 +3332,6 @@ var require_utils = __commonJS((exports, module) => {
3425
3332
  reescapeHostDelimiters,
3426
3333
  normalizePercentEncoding,
3427
3334
  normalizePathEncoding,
3428
- normalizeQueryFragmentEncoding,
3429
3335
  escapePreservingEscapes,
3430
3336
  removeDotSegments,
3431
3337
  isIPv4,
@@ -3611,7 +3517,7 @@ var require_schemes = __commonJS((exports, module) => {
3611
3517
 
3612
3518
  // node_modules/fast-uri/index.js
3613
3519
  var require_fast_uri = __commonJS((exports, module) => {
3614
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, normalizeQueryFragmentEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3520
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3615
3521
  var { SCHEMES, getSchemeHandler } = require_schemes();
3616
3522
  function normalize(uri, options) {
3617
3523
  if (typeof uri === "string") {
@@ -3623,12 +3529,7 @@ var require_fast_uri = __commonJS((exports, module) => {
3623
3529
  }
3624
3530
  function resolve(baseURI, relativeURI, options) {
3625
3531
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3626
- const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3627
- const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3628
- if (baseMalformed || relativeMalformed) {
3629
- throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3630
- }
3631
- const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3532
+ const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
3632
3533
  schemelessOptions.skipEscape = true;
3633
3534
  return serialize(resolved, schemelessOptions);
3634
3535
  }
@@ -3754,8 +3655,6 @@ var require_fast_uri = __commonJS((exports, module) => {
3754
3655
  return uriTokens.join("");
3755
3656
  }
3756
3657
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3757
- var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3758
- var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
3759
3658
  function getParseError(parsed, matches) {
3760
3659
  if (matches[2] !== undefined && parsed.path && parsed.path[0] !== "/") {
3761
3660
  return 'URI path must start with "/" when authority is present.';
@@ -3785,28 +3684,9 @@ var require_fast_uri = __commonJS((exports, module) => {
3785
3684
  uri = "//" + uri;
3786
3685
  }
3787
3686
  }
3788
- const authorityMatch = uri.match(AUTHORITY_PREFIX);
3789
- if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
3790
- parsed.error = "URI authority must not contain a literal backslash.";
3791
- malformedAuthorityOrPort = true;
3792
- }
3793
- const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
3794
- if (introducerMatch !== null) {
3795
- const region = introducerMatch[1];
3796
- const normalizedRegion = region.replace(/[\t\n\r]/g, "");
3797
- if (normalizedRegion.length >= 2) {
3798
- if (normalizedRegion.slice(0, 2) !== "//") {
3799
- parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
3800
- malformedAuthorityOrPort = true;
3801
- } else if (region.length !== normalizedRegion.length) {
3802
- parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
3803
- malformedAuthorityOrPort = true;
3804
- }
3805
- }
3806
- }
3807
3687
  const matches = uri.match(URI_PARSE);
3808
3688
  if (matches) {
3809
- parsed.scheme = matches[1] === undefined ? undefined : matches[1].toLowerCase();
3689
+ parsed.scheme = matches[1];
3810
3690
  parsed.userinfo = matches[3];
3811
3691
  parsed.host = matches[4];
3812
3692
  parsed.port = parseInt(matches[5], 10);
@@ -3865,11 +3745,12 @@ var require_fast_uri = __commonJS((exports, module) => {
3865
3745
  if (parsed.path) {
3866
3746
  parsed.path = normalizePathEncoding(parsed.path);
3867
3747
  }
3868
- if (parsed.query) {
3869
- parsed.query = normalizeQueryFragmentEncoding(parsed.query);
3870
- }
3871
3748
  if (parsed.fragment) {
3872
- parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
3749
+ try {
3750
+ parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3751
+ } catch {
3752
+ parsed.error = parsed.error || "URI malformed";
3753
+ }
3873
3754
  }
3874
3755
  }
3875
3756
  if (schemeHandler && schemeHandler.parse) {
@@ -7821,7 +7702,7 @@ var require_stringify = __commonJS((exports) => {
7821
7702
  props.push(doc.directives.tagString(tag));
7822
7703
  return props.join(" ");
7823
7704
  }
7824
- function stringify(item, ctx, onComment, onChompKeep) {
7705
+ function stringify2(item, ctx, onComment, onChompKeep) {
7825
7706
  if (identity.isPair(item))
7826
7707
  return item.toString(ctx, onComment, onChompKeep);
7827
7708
  if (identity.isAlias(item)) {
@@ -7850,14 +7731,14 @@ var require_stringify = __commonJS((exports) => {
7850
7731
  ${ctx.indent}${str}`;
7851
7732
  }
7852
7733
  exports.createStringifyContext = createStringifyContext;
7853
- exports.stringify = stringify;
7734
+ exports.stringify = stringify2;
7854
7735
  });
7855
7736
 
7856
7737
  // node_modules/yaml/dist/stringify/stringifyPair.js
7857
7738
  var require_stringifyPair = __commonJS((exports) => {
7858
7739
  var identity = require_identity();
7859
7740
  var Scalar = require_Scalar();
7860
- var stringify = require_stringify();
7741
+ var stringify2 = require_stringify();
7861
7742
  var stringifyComment = require_stringifyComment();
7862
7743
  function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
7863
7744
  const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
@@ -7879,7 +7760,7 @@ var require_stringifyPair = __commonJS((exports) => {
7879
7760
  });
7880
7761
  let keyCommentDone = false;
7881
7762
  let chompKeep = false;
7882
- let str = stringify.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
7763
+ let str = stringify2.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
7883
7764
  if (!explicitKey && !ctx.inFlow && str.length > 1024) {
7884
7765
  if (simpleKeys)
7885
7766
  throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
@@ -7931,7 +7812,7 @@ ${indent}:`;
7931
7812
  ctx.indent = ctx.indent.substring(2);
7932
7813
  }
7933
7814
  let valueCommentDone = false;
7934
- const valueStr = stringify.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
7815
+ const valueStr = stringify2.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
7935
7816
  let ws = " ";
7936
7817
  if (keyComment || vsb || vcb) {
7937
7818
  ws = vsb ? `
@@ -8069,7 +7950,7 @@ var require_merge = __commonJS((exports) => {
8069
7950
  var require_addPairToJSMap = __commonJS((exports) => {
8070
7951
  var log = require_log();
8071
7952
  var merge = require_merge();
8072
- var stringify = require_stringify();
7953
+ var stringify2 = require_stringify();
8073
7954
  var identity = require_identity();
8074
7955
  var toJS = require_toJS();
8075
7956
  function addPairToJSMap(ctx, map, { key, value }) {
@@ -8105,7 +7986,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
8105
7986
  if (typeof jsKey !== "object")
8106
7987
  return String(jsKey);
8107
7988
  if (identity.isNode(key) && ctx?.doc) {
8108
- const strCtx = stringify.createStringifyContext(ctx.doc, {});
7989
+ const strCtx = stringify2.createStringifyContext(ctx.doc, {});
8109
7990
  strCtx.anchors = new Set;
8110
7991
  for (const node of ctx.anchors.keys())
8111
7992
  strCtx.anchors.add(node.anchor);
@@ -8167,12 +8048,12 @@ var require_Pair = __commonJS((exports) => {
8167
8048
  // node_modules/yaml/dist/stringify/stringifyCollection.js
8168
8049
  var require_stringifyCollection = __commonJS((exports) => {
8169
8050
  var identity = require_identity();
8170
- var stringify = require_stringify();
8051
+ var stringify2 = require_stringify();
8171
8052
  var stringifyComment = require_stringifyComment();
8172
8053
  function stringifyCollection(collection, ctx, options) {
8173
8054
  const flow = ctx.inFlow ?? collection.flow;
8174
- const stringify2 = flow ? stringifyFlowCollection : stringifyBlockCollection;
8175
- return stringify2(collection, ctx, options);
8055
+ const stringify3 = flow ? stringifyFlowCollection : stringifyBlockCollection;
8056
+ return stringify3(collection, ctx, options);
8176
8057
  }
8177
8058
  function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
8178
8059
  const { indent, options: { commentString } } = ctx;
@@ -8197,7 +8078,7 @@ var require_stringifyCollection = __commonJS((exports) => {
8197
8078
  }
8198
8079
  }
8199
8080
  chompKeep = false;
8200
- let str2 = stringify.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
8081
+ let str2 = stringify2.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
8201
8082
  if (comment2)
8202
8083
  str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
8203
8084
  if (chompKeep && comment2)
@@ -8266,7 +8147,7 @@ ${indent}${line}` : `
8266
8147
  }
8267
8148
  if (comment)
8268
8149
  reqNewline = true;
8269
- let str = stringify.stringify(item, itemCtx, () => comment = null);
8150
+ let str = stringify2.stringify(item, itemCtx, () => comment = null);
8270
8151
  reqNewline || (reqNewline = lines.length > linesAtValue || str.includes(`
8271
8152
  `));
8272
8153
  if (i < items.length - 1) {
@@ -9507,7 +9388,7 @@ var require_Schema = __commonJS((exports) => {
9507
9388
  // node_modules/yaml/dist/stringify/stringifyDocument.js
9508
9389
  var require_stringifyDocument = __commonJS((exports) => {
9509
9390
  var identity = require_identity();
9510
- var stringify = require_stringify();
9391
+ var stringify2 = require_stringify();
9511
9392
  var stringifyComment = require_stringifyComment();
9512
9393
  function stringifyDocument(doc, options) {
9513
9394
  const lines = [];
@@ -9522,7 +9403,7 @@ var require_stringifyDocument = __commonJS((exports) => {
9522
9403
  }
9523
9404
  if (hasDirectives)
9524
9405
  lines.push("---");
9525
- const ctx = stringify.createStringifyContext(doc, options);
9406
+ const ctx = stringify2.createStringifyContext(doc, options);
9526
9407
  const { commentString } = ctx.options;
9527
9408
  if (doc.commentBefore) {
9528
9409
  if (lines.length !== 1)
@@ -9544,7 +9425,7 @@ var require_stringifyDocument = __commonJS((exports) => {
9544
9425
  contentComment = doc.contents.comment;
9545
9426
  }
9546
9427
  const onChompKeep = contentComment ? undefined : () => chompKeep = true;
9547
- let body = stringify.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
9428
+ let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
9548
9429
  if (contentComment)
9549
9430
  body += stringifyComment.lineComment(body, "", commentString(contentComment));
9550
9431
  if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
@@ -9552,7 +9433,7 @@ var require_stringifyDocument = __commonJS((exports) => {
9552
9433
  } else
9553
9434
  lines.push(body);
9554
9435
  } else {
9555
- lines.push(stringify.stringify(doc.contents, ctx));
9436
+ lines.push(stringify2.stringify(doc.contents, ctx));
9556
9437
  }
9557
9438
  if (doc.directives?.docEnd) {
9558
9439
  if (doc.comment) {
@@ -11571,7 +11452,7 @@ var require_cst_scalar = __commonJS((exports) => {
11571
11452
 
11572
11453
  // node_modules/yaml/dist/parse/cst-stringify.js
11573
11454
  var require_cst_stringify = __commonJS((exports) => {
11574
- var stringify = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
11455
+ var stringify2 = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
11575
11456
  function stringifyToken(token) {
11576
11457
  switch (token.type) {
11577
11458
  case "block-scalar": {
@@ -11624,7 +11505,7 @@ var require_cst_stringify = __commonJS((exports) => {
11624
11505
  res += stringifyToken(value);
11625
11506
  return res;
11626
11507
  }
11627
- exports.stringify = stringify;
11508
+ exports.stringify = stringify2;
11628
11509
  });
11629
11510
 
11630
11511
  // node_modules/yaml/dist/parse/cst-visit.js
@@ -13315,7 +13196,7 @@ var require_public_api = __commonJS((exports) => {
13315
13196
  }
13316
13197
  return doc;
13317
13198
  }
13318
- function parse(src, reviver, options) {
13199
+ function parse2(src, reviver, options) {
13319
13200
  let _reviver = undefined;
13320
13201
  if (typeof reviver === "function") {
13321
13202
  _reviver = reviver;
@@ -13334,7 +13215,7 @@ var require_public_api = __commonJS((exports) => {
13334
13215
  }
13335
13216
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
13336
13217
  }
13337
- function stringify(value, replacer, options) {
13218
+ function stringify2(value, replacer, options) {
13338
13219
  let _replacer = null;
13339
13220
  if (typeof replacer === "function" || Array.isArray(replacer)) {
13340
13221
  _replacer = replacer;
@@ -13356,10 +13237,10 @@ var require_public_api = __commonJS((exports) => {
13356
13237
  return value.toString(options);
13357
13238
  return new Document.Document(value, _replacer, options).toString(options);
13358
13239
  }
13359
- exports.parse = parse;
13240
+ exports.parse = parse2;
13360
13241
  exports.parseAllDocuments = parseAllDocuments;
13361
13242
  exports.parseDocument = parseDocument;
13362
- exports.stringify = stringify;
13243
+ exports.stringify = stringify2;
13363
13244
  });
13364
13245
 
13365
13246
  // node_modules/picomatch/lib/constants.js
@@ -14136,7 +14017,7 @@ var require_parse = __commonJS((exports, module) => {
14136
14017
  }
14137
14018
  return { risky: false };
14138
14019
  };
14139
- var parse = (input, options) => {
14020
+ var parse2 = (input, options) => {
14140
14021
  if (typeof input !== "string") {
14141
14022
  throw new TypeError("Expected a string");
14142
14023
  }
@@ -14307,7 +14188,7 @@ var require_parse = __commonJS((exports, module) => {
14307
14188
  output = token.close = `)$))${extglobStar}`;
14308
14189
  }
14309
14190
  if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
14310
- const expression = parse(rest, { ...options, fastpaths: false }).output;
14191
+ const expression = parse2(rest, { ...options, fastpaths: false }).output;
14311
14192
  output = token.close = `)${expression})${extglobStar})`;
14312
14193
  }
14313
14194
  if (token.prev.type === "bos") {
@@ -14833,7 +14714,7 @@ var require_parse = __commonJS((exports, module) => {
14833
14714
  }
14834
14715
  return state;
14835
14716
  };
14836
- parse.fastpaths = (input, options) => {
14717
+ parse2.fastpaths = (input, options) => {
14837
14718
  const opts = { ...options };
14838
14719
  const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
14839
14720
  const len = input.length;
@@ -14901,13 +14782,13 @@ var require_parse = __commonJS((exports, module) => {
14901
14782
  }
14902
14783
  return source;
14903
14784
  };
14904
- module.exports = parse;
14785
+ module.exports = parse2;
14905
14786
  });
14906
14787
 
14907
14788
  // node_modules/picomatch/lib/picomatch.js
14908
14789
  var require_picomatch = __commonJS((exports, module) => {
14909
14790
  var scan = require_scan();
14910
- var parse = require_parse();
14791
+ var parse2 = require_parse();
14911
14792
  var utils = require_utils2();
14912
14793
  var constants = require_constants();
14913
14794
  var isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
@@ -14997,7 +14878,7 @@ var require_picomatch = __commonJS((exports, module) => {
14997
14878
  picomatch.parse = (pattern, options) => {
14998
14879
  if (Array.isArray(pattern))
14999
14880
  return pattern.map((p) => picomatch.parse(p, options));
15000
- return parse(pattern, { ...options, fastpaths: false });
14881
+ return parse2(pattern, { ...options, fastpaths: false });
15001
14882
  };
15002
14883
  picomatch.scan = (input, options) => scan(input, options);
15003
14884
  picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
@@ -15023,10 +14904,10 @@ var require_picomatch = __commonJS((exports, module) => {
15023
14904
  }
15024
14905
  let parsed = { negated: false, fastpaths: true };
15025
14906
  if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) {
15026
- parsed.output = parse.fastpaths(input, options);
14907
+ parsed.output = parse2.fastpaths(input, options);
15027
14908
  }
15028
14909
  if (!parsed.output) {
15029
- parsed = parse(input, options);
14910
+ parsed = parse2(input, options);
15030
14911
  }
15031
14912
  return picomatch.compileRe(parsed, options, returnOutput, returnState);
15032
14913
  };
@@ -15070,13 +14951,13 @@ var require_ms = __commonJS((exports, module) => {
15070
14951
  options = options || {};
15071
14952
  var type = typeof val;
15072
14953
  if (type === "string" && val.length > 0) {
15073
- return parse(val);
14954
+ return parse2(val);
15074
14955
  } else if (type === "number" && isFinite(val)) {
15075
14956
  return options.long ? fmtLong(val) : fmtShort(val);
15076
14957
  }
15077
14958
  throw new Error("val is not a non-empty string or a valid number. val=" + JSON.stringify(val));
15078
14959
  };
15079
- function parse(str) {
14960
+ function parse2(str) {
15080
14961
  str = String(str);
15081
14962
  if (str.length > 100) {
15082
14963
  return;
@@ -15771,7 +15652,7 @@ var require_extend = __commonJS((exports, module) => {
15771
15652
  });
15772
15653
 
15773
15654
  // src/cli.ts
15774
- import { readFileSync as readFileSync22 } from "node:fs";
15655
+ import { readFileSync as readFileSync24 } from "node:fs";
15775
15656
  import process7 from "node:process";
15776
15657
 
15777
15658
  // src/audit/config/load.ts
@@ -15781,6 +15662,883 @@ import { dirname, join } from "node:path";
15781
15662
  import process2 from "node:process";
15782
15663
  import { fileURLToPath } from "node:url";
15783
15664
 
15665
+ // node_modules/smol-toml/dist/date.js
15666
+ /*!
15667
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
15668
+ * SPDX-License-Identifier: BSD-3-Clause
15669
+ *
15670
+ * Redistribution and use in source and binary forms, with or without
15671
+ * modification, are permitted provided that the following conditions are met:
15672
+ *
15673
+ * 1. Redistributions of source code must retain the above copyright notice, this
15674
+ * list of conditions and the following disclaimer.
15675
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
15676
+ * this list of conditions and the following disclaimer in the
15677
+ * documentation and/or other materials provided with the distribution.
15678
+ * 3. Neither the name of the copyright holder nor the names of its contributors
15679
+ * may be used to endorse or promote products derived from this software without
15680
+ * specific prior written permission.
15681
+ *
15682
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15683
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15684
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15685
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
15686
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15687
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15688
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
15689
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
15690
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15691
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15692
+ */
15693
+ var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
15694
+
15695
+ class TomlDate extends Date {
15696
+ #hasDate = false;
15697
+ #hasTime = false;
15698
+ #offset = null;
15699
+ constructor(date) {
15700
+ let hasDate = true;
15701
+ let hasTime = true;
15702
+ let offset = "Z";
15703
+ if (typeof date === "string") {
15704
+ let match = date.match(DATE_TIME_RE);
15705
+ if (match) {
15706
+ if (!match[1]) {
15707
+ hasDate = false;
15708
+ date = `0000-01-01T${date}`;
15709
+ }
15710
+ hasTime = !!match[2];
15711
+ hasTime && date[10] === " " && (date = date.replace(" ", "T"));
15712
+ if (match[2] && +match[2] > 23) {
15713
+ date = "";
15714
+ } else {
15715
+ offset = match[3] || null;
15716
+ date = date.toUpperCase();
15717
+ if (!offset && hasTime)
15718
+ date += "Z";
15719
+ }
15720
+ } else {
15721
+ date = "";
15722
+ }
15723
+ }
15724
+ super(date);
15725
+ if (!isNaN(this.getTime())) {
15726
+ this.#hasDate = hasDate;
15727
+ this.#hasTime = hasTime;
15728
+ this.#offset = offset;
15729
+ }
15730
+ }
15731
+ isDateTime() {
15732
+ return this.#hasDate && this.#hasTime;
15733
+ }
15734
+ isLocal() {
15735
+ return !this.#hasDate || !this.#hasTime || !this.#offset;
15736
+ }
15737
+ isDate() {
15738
+ return this.#hasDate && !this.#hasTime;
15739
+ }
15740
+ isTime() {
15741
+ return this.#hasTime && !this.#hasDate;
15742
+ }
15743
+ isValid() {
15744
+ return this.#hasDate || this.#hasTime;
15745
+ }
15746
+ toISOString() {
15747
+ let iso = super.toISOString();
15748
+ if (this.isDate())
15749
+ return iso.slice(0, 10);
15750
+ if (this.isTime())
15751
+ return iso.slice(11, 23);
15752
+ if (this.#offset === null)
15753
+ return iso.slice(0, -1);
15754
+ if (this.#offset === "Z")
15755
+ return iso;
15756
+ let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
15757
+ offset = this.#offset[0] === "-" ? offset : -offset;
15758
+ let offsetDate = new Date(this.getTime() - offset * 60000);
15759
+ return offsetDate.toISOString().slice(0, -1) + this.#offset;
15760
+ }
15761
+ static wrapAsOffsetDateTime(jsDate, offset = "Z") {
15762
+ let date = new TomlDate(jsDate);
15763
+ date.#offset = offset;
15764
+ return date;
15765
+ }
15766
+ static wrapAsLocalDateTime(jsDate) {
15767
+ let date = new TomlDate(jsDate);
15768
+ date.#offset = null;
15769
+ return date;
15770
+ }
15771
+ static wrapAsLocalDate(jsDate) {
15772
+ let date = new TomlDate(jsDate);
15773
+ date.#hasTime = false;
15774
+ date.#offset = null;
15775
+ return date;
15776
+ }
15777
+ static wrapAsLocalTime(jsDate) {
15778
+ let date = new TomlDate(jsDate);
15779
+ date.#hasDate = false;
15780
+ date.#offset = null;
15781
+ return date;
15782
+ }
15783
+ }
15784
+
15785
+ // node_modules/smol-toml/dist/error.js
15786
+ /*!
15787
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
15788
+ * SPDX-License-Identifier: BSD-3-Clause
15789
+ *
15790
+ * Redistribution and use in source and binary forms, with or without
15791
+ * modification, are permitted provided that the following conditions are met:
15792
+ *
15793
+ * 1. Redistributions of source code must retain the above copyright notice, this
15794
+ * list of conditions and the following disclaimer.
15795
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
15796
+ * this list of conditions and the following disclaimer in the
15797
+ * documentation and/or other materials provided with the distribution.
15798
+ * 3. Neither the name of the copyright holder nor the names of its contributors
15799
+ * may be used to endorse or promote products derived from this software without
15800
+ * specific prior written permission.
15801
+ *
15802
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15803
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15804
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15805
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
15806
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15807
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15808
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
15809
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
15810
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15811
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15812
+ */
15813
+ function getLineColFromPtr(string, ptr) {
15814
+ let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
15815
+ return [lines.length, lines.pop().length + 1];
15816
+ }
15817
+ function makeCodeBlock(string, line, column) {
15818
+ let lines = string.split(/\r\n|\n|\r/g);
15819
+ let codeblock = "";
15820
+ let numberLen = (Math.log10(line + 1) | 0) + 1;
15821
+ for (let i = line - 1;i <= line + 1; i++) {
15822
+ let l = lines[i - 1];
15823
+ if (!l)
15824
+ continue;
15825
+ codeblock += i.toString().padEnd(numberLen, " ");
15826
+ codeblock += ": ";
15827
+ codeblock += l;
15828
+ codeblock += `
15829
+ `;
15830
+ if (i === line) {
15831
+ codeblock += " ".repeat(numberLen + column + 2);
15832
+ codeblock += `^
15833
+ `;
15834
+ }
15835
+ }
15836
+ return codeblock;
15837
+ }
15838
+
15839
+ class TomlError extends Error {
15840
+ line;
15841
+ column;
15842
+ codeblock;
15843
+ constructor(message, options) {
15844
+ const [line, column] = getLineColFromPtr(options.toml, options.ptr);
15845
+ const codeblock = makeCodeBlock(options.toml, line, column);
15846
+ super(`Invalid TOML document: ${message}
15847
+
15848
+ ${codeblock}`, options);
15849
+ this.line = line;
15850
+ this.column = column;
15851
+ this.codeblock = codeblock;
15852
+ }
15853
+ }
15854
+
15855
+ // node_modules/smol-toml/dist/util.js
15856
+ /*!
15857
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
15858
+ * SPDX-License-Identifier: BSD-3-Clause
15859
+ *
15860
+ * Redistribution and use in source and binary forms, with or without
15861
+ * modification, are permitted provided that the following conditions are met:
15862
+ *
15863
+ * 1. Redistributions of source code must retain the above copyright notice, this
15864
+ * list of conditions and the following disclaimer.
15865
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
15866
+ * this list of conditions and the following disclaimer in the
15867
+ * documentation and/or other materials provided with the distribution.
15868
+ * 3. Neither the name of the copyright holder nor the names of its contributors
15869
+ * may be used to endorse or promote products derived from this software without
15870
+ * specific prior written permission.
15871
+ *
15872
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15873
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15874
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15875
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
15876
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15877
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15878
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
15879
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
15880
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15881
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15882
+ */
15883
+ function indexOfNewline(str, start = 0) {
15884
+ let idx = str.indexOf(`
15885
+ `, start);
15886
+ if (str.charCodeAt(idx - 1) === 13)
15887
+ idx--;
15888
+ return idx;
15889
+ }
15890
+ function skipComment(ctx) {
15891
+ for (;ctx.p < ctx.s.length; ctx.p++) {
15892
+ let c = ctx.s.charCodeAt(ctx.p);
15893
+ if (c === 10)
15894
+ break;
15895
+ if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10) {
15896
+ ctx.p++;
15897
+ break;
15898
+ }
15899
+ if (c < 32 && c !== 9 || c === 127) {
15900
+ throw new TomlError("control characters are not allowed in comments", {
15901
+ toml: ctx.s,
15902
+ ptr: ctx.p
15903
+ });
15904
+ }
15905
+ }
15906
+ }
15907
+ function skipVoid(ctx, banNewLines, banComments) {
15908
+ let c;
15909
+ while (true) {
15910
+ while ((c = ctx.s.charCodeAt(ctx.p)) === 32 || c === 9 || !banNewLines && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10))
15911
+ ctx.p++;
15912
+ if (banComments || c !== 35)
15913
+ break;
15914
+ skipComment(ctx);
15915
+ }
15916
+ }
15917
+ function skipUntil(ctx, sep, end) {
15918
+ let ptr = ctx.p;
15919
+ if (!end) {
15920
+ ptr = indexOfNewline(ctx.s, ptr);
15921
+ ctx.p = ptr < 0 ? ctx.s.length : ptr;
15922
+ return;
15923
+ }
15924
+ for (;ctx.p < ctx.s.length; ctx.p++) {
15925
+ let c = ctx.s.charCodeAt(ctx.p);
15926
+ if (c === 35) {
15927
+ skipComment(ctx);
15928
+ } else if (c === end || c === sep) {
15929
+ return;
15930
+ }
15931
+ }
15932
+ throw new TomlError("cannot find end of structure", {
15933
+ toml: ctx.s,
15934
+ ptr
15935
+ });
15936
+ }
15937
+
15938
+ // node_modules/smol-toml/dist/primitive.js
15939
+ /*!
15940
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
15941
+ * SPDX-License-Identifier: BSD-3-Clause
15942
+ *
15943
+ * Redistribution and use in source and binary forms, with or without
15944
+ * modification, are permitted provided that the following conditions are met:
15945
+ *
15946
+ * 1. Redistributions of source code must retain the above copyright notice, this
15947
+ * list of conditions and the following disclaimer.
15948
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
15949
+ * this list of conditions and the following disclaimer in the
15950
+ * documentation and/or other materials provided with the distribution.
15951
+ * 3. Neither the name of the copyright holder nor the names of its contributors
15952
+ * may be used to endorse or promote products derived from this software without
15953
+ * specific prior written permission.
15954
+ *
15955
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15956
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15957
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15958
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
15959
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15960
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15961
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
15962
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
15963
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15964
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15965
+ */
15966
+ var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
15967
+ var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
15968
+ var LEADING_ZERO = /^[+-]?0[0-9_]/;
15969
+ function parseString(ctx) {
15970
+ let start = ctx.p;
15971
+ let c = ctx.s.charCodeAt(ctx.p++);
15972
+ let first = c;
15973
+ let isLiteral = c === 39;
15974
+ let isMultiline = c === ctx.s.charCodeAt(ctx.p) && c === ctx.s.charCodeAt(ctx.p + 1);
15975
+ if (isMultiline) {
15976
+ if ((c = ctx.s.charCodeAt(ctx.p += 2)) === 10)
15977
+ ctx.p++;
15978
+ else if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)
15979
+ ctx.p += 2;
15980
+ }
15981
+ let parsed = "";
15982
+ let sliceStart = ctx.p;
15983
+ let state = 0;
15984
+ for (;ctx.p < ctx.s.length; ctx.p++) {
15985
+ c = ctx.s.charCodeAt(ctx.p);
15986
+ if (isMultiline && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)) {
15987
+ state = state && 3;
15988
+ } else if (c < 32 && c !== 9 || c === 127) {
15989
+ throw new TomlError("control characters are not allowed in strings", {
15990
+ toml: ctx.s,
15991
+ ptr: ctx.p
15992
+ });
15993
+ } else if ((!state || state === 3) && c === first && (!isMultiline || ctx.s.charCodeAt(ctx.p + 1) === first && ctx.s.charCodeAt(ctx.p + 2) === first)) {
15994
+ if (isMultiline) {
15995
+ if (ctx.s.charCodeAt(ctx.p + 3) === first)
15996
+ ctx.p++;
15997
+ if (ctx.s.charCodeAt(ctx.p + 3) === first)
15998
+ ctx.p++;
15999
+ }
16000
+ if (!state)
16001
+ parsed += ctx.s.slice(sliceStart, ctx.p);
16002
+ ctx.p += isMultiline ? 3 : 1;
16003
+ return parsed;
16004
+ } else if (!state) {
16005
+ if (!isLiteral && c === 92) {
16006
+ parsed += ctx.s.slice(sliceStart, sliceStart = ctx.p);
16007
+ state = 1;
16008
+ }
16009
+ } else if (state === 1) {
16010
+ if (c === 120 || c === 117 || c === 85) {
16011
+ let value = 0;
16012
+ let len = c === 120 ? 2 : c === 117 ? 4 : 8;
16013
+ for (let j = 0;j < len; j++, ctx.p++) {
16014
+ let hex = ctx.s.charCodeAt(ctx.p + 1);
16015
+ let digit = hex >= 48 && hex <= 57 ? hex - 48 : hex >= 65 && hex <= 70 ? hex - 65 + 10 : hex >= 97 && hex <= 102 ? hex - 97 + 10 : -1;
16016
+ if (digit < 0)
16017
+ throw new TomlError("invalid non-hex character in unicode escape", { toml: ctx.s, ptr: ctx.p + 1 });
16018
+ value = value << 4 | digit;
16019
+ }
16020
+ if (value < 0 || value > 1114111 || value >= 55296 && value <= 57343) {
16021
+ throw new TomlError("invalid unicode escape", { toml: ctx.s, ptr: ctx.p });
16022
+ }
16023
+ parsed += String.fromCodePoint(value);
16024
+ sliceStart = ctx.p + 1;
16025
+ state = 0;
16026
+ } else if (c === 32 || c === 9) {
16027
+ state = 2;
16028
+ } else {
16029
+ if (c === 98)
16030
+ parsed += "\b";
16031
+ else if (c === 116)
16032
+ parsed += "\t";
16033
+ else if (c === 110)
16034
+ parsed += `
16035
+ `;
16036
+ else if (c === 102)
16037
+ parsed += "\f";
16038
+ else if (c === 114)
16039
+ parsed += "\r";
16040
+ else if (c === 101)
16041
+ parsed += "\x1B";
16042
+ else if (c === 34)
16043
+ parsed += '"';
16044
+ else if (c === 92)
16045
+ parsed += "\\";
16046
+ else
16047
+ throw new TomlError("unrecognized escape sequence", { toml: ctx.s, ptr: ctx.p });
16048
+ sliceStart = ctx.p + 1;
16049
+ state = 0;
16050
+ }
16051
+ } else if (c !== 32 && c !== 9) {
16052
+ if (state === 2) {
16053
+ throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
16054
+ toml: ctx.s,
16055
+ ptr: sliceStart
16056
+ });
16057
+ }
16058
+ state = !isLiteral && c === 92 ? 1 : 0;
16059
+ sliceStart = ctx.p;
16060
+ }
16061
+ }
16062
+ throw new TomlError("unfinished string", { toml: ctx.s, ptr: start });
16063
+ }
16064
+ function sliceAndTrimEndOf(ctx, start, end) {
16065
+ let value = ctx.s.slice(start, end);
16066
+ let commentIdx = value.indexOf("#");
16067
+ if (commentIdx > 0) {
16068
+ skipComment({ s: value, p: commentIdx, d: 0 });
16069
+ value = value.slice(0, commentIdx);
16070
+ }
16071
+ return value.trimEnd();
16072
+ }
16073
+ function parseValue(ctx, integersAsBigInt, end) {
16074
+ let ptr = ctx.p;
16075
+ let err = { toml: ctx.s, ptr };
16076
+ skipUntil(ctx, 44, end);
16077
+ let value = sliceAndTrimEndOf(ctx, ptr, ctx.p);
16078
+ if (!value)
16079
+ throw new TomlError("incomplete declaration: value expected", err);
16080
+ if (value === "-inf")
16081
+ return -Infinity;
16082
+ if (value === "inf" || value === "+inf")
16083
+ return Infinity;
16084
+ if (value === "nan" || value === "+nan" || value === "-nan")
16085
+ return NaN;
16086
+ if (value === "-0")
16087
+ return integersAsBigInt ? 0n : 0;
16088
+ let isInt = INT_REGEX.test(value);
16089
+ if (isInt || FLOAT_REGEX.test(value)) {
16090
+ if (LEADING_ZERO.test(value)) {
16091
+ throw new TomlError("leading zeroes are not allowed", err);
16092
+ }
16093
+ value = value.replace(/_/g, "");
16094
+ let numeric = +value;
16095
+ if (isNaN(numeric)) {
16096
+ throw new TomlError("invalid number", err);
16097
+ }
16098
+ if (isInt) {
16099
+ if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
16100
+ throw new TomlError("integer value cannot be represented losslessly", err);
16101
+ }
16102
+ if (isInt || integersAsBigInt === true)
16103
+ numeric = BigInt(value);
16104
+ }
16105
+ return numeric;
16106
+ }
16107
+ const date = new TomlDate(value);
16108
+ if (!date.isValid())
16109
+ throw new TomlError("invalid value", err);
16110
+ return date;
16111
+ }
16112
+
16113
+ // node_modules/smol-toml/dist/extract.js
16114
+ /*!
16115
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
16116
+ * SPDX-License-Identifier: BSD-3-Clause
16117
+ *
16118
+ * Redistribution and use in source and binary forms, with or without
16119
+ * modification, are permitted provided that the following conditions are met:
16120
+ *
16121
+ * 1. Redistributions of source code must retain the above copyright notice, this
16122
+ * list of conditions and the following disclaimer.
16123
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
16124
+ * this list of conditions and the following disclaimer in the
16125
+ * documentation and/or other materials provided with the distribution.
16126
+ * 3. Neither the name of the copyright holder nor the names of its contributors
16127
+ * may be used to endorse or promote products derived from this software without
16128
+ * specific prior written permission.
16129
+ *
16130
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16131
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16132
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16133
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
16134
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16135
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16136
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16137
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
16138
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16139
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16140
+ */
16141
+ function extractValue(ctx, end, integersAsBigInt) {
16142
+ let ptr = ctx.p;
16143
+ let c = ctx.s.charCodeAt(ptr);
16144
+ if (c === 91 || c === 123) {
16145
+ if (!ctx.d--) {
16146
+ throw new TomlError("document contains excessively nested structures. aborting.", {
16147
+ toml: ctx.s,
16148
+ ptr
16149
+ });
16150
+ }
16151
+ let value = c === 91 ? parseArray(ctx, integersAsBigInt) : parseInlineTable(ctx, integersAsBigInt);
16152
+ ctx.d++;
16153
+ return value;
16154
+ }
16155
+ if (c === 34 || c === 39) {
16156
+ return parseString(ctx);
16157
+ }
16158
+ if (c === 116) {
16159
+ if (ctx.s.charCodeAt(++ctx.p) !== 114 || ctx.s.charCodeAt(++ctx.p) !== 117 || ctx.s.charCodeAt(++ctx.p) !== 101)
16160
+ throw new TomlError("invalid value", { toml: ctx.s, ptr });
16161
+ ctx.p++;
16162
+ return true;
16163
+ }
16164
+ if (c === 102) {
16165
+ if (ctx.s.charCodeAt(++ctx.p) !== 97 || ctx.s.charCodeAt(++ctx.p) !== 108 || ctx.s.charCodeAt(++ctx.p) !== 115 || ctx.s.charCodeAt(++ctx.p) !== 101)
16166
+ throw new TomlError("invalid value", { toml: ctx.s, ptr });
16167
+ ctx.p++;
16168
+ return false;
16169
+ }
16170
+ return parseValue(ctx, integersAsBigInt, end);
16171
+ }
16172
+
16173
+ // node_modules/smol-toml/dist/struct.js
16174
+ /*!
16175
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
16176
+ * SPDX-License-Identifier: BSD-3-Clause
16177
+ *
16178
+ * Redistribution and use in source and binary forms, with or without
16179
+ * modification, are permitted provided that the following conditions are met:
16180
+ *
16181
+ * 1. Redistributions of source code must retain the above copyright notice, this
16182
+ * list of conditions and the following disclaimer.
16183
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
16184
+ * this list of conditions and the following disclaimer in the
16185
+ * documentation and/or other materials provided with the distribution.
16186
+ * 3. Neither the name of the copyright holder nor the names of its contributors
16187
+ * may be used to endorse or promote products derived from this software without
16188
+ * specific prior written permission.
16189
+ *
16190
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16191
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16192
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16193
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
16194
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16195
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16196
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16197
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
16198
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16199
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16200
+ */
16201
+ var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
16202
+ function parseKey(ctx, end = "=") {
16203
+ let start = ctx.p;
16204
+ let dot = start - 1;
16205
+ let parsed = [];
16206
+ let endPtr = ctx.s.indexOf(end, start);
16207
+ if (endPtr < 0) {
16208
+ throw new TomlError("incomplete key-value: cannot find end of key", {
16209
+ toml: ctx.s,
16210
+ ptr: start
16211
+ });
16212
+ }
16213
+ do {
16214
+ let c = ctx.s.charCodeAt(ctx.p = ++dot);
16215
+ if (c !== 32 && c !== 9) {
16216
+ if (c === 34 || c === 39) {
16217
+ if (c === ctx.s.charCodeAt(ctx.p + 1) && c === ctx.s.charCodeAt(ctx.p + 2)) {
16218
+ throw new TomlError("multiline strings are not allowed in keys", {
16219
+ toml: ctx.s,
16220
+ ptr: ctx.p
16221
+ });
16222
+ }
16223
+ let part = parseString(ctx);
16224
+ dot = ctx.s.indexOf(".", ctx.p);
16225
+ let strEnd = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
16226
+ let newLine = indexOfNewline(strEnd);
16227
+ if (newLine > -1) {
16228
+ throw new TomlError("newlines are not allowed in keys", {
16229
+ toml: ctx.s,
16230
+ ptr: newLine
16231
+ });
16232
+ }
16233
+ if (strEnd.trimStart()) {
16234
+ throw new TomlError("found extra tokens after the string part", {
16235
+ toml: ctx.s,
16236
+ ptr: ctx.p
16237
+ });
16238
+ }
16239
+ if (endPtr < ctx.p) {
16240
+ endPtr = ctx.s.indexOf(end, ctx.p);
16241
+ if (endPtr < 0) {
16242
+ throw new TomlError("incomplete key-value: cannot find end of key", {
16243
+ toml: ctx.s,
16244
+ ptr: start
16245
+ });
16246
+ }
16247
+ }
16248
+ parsed.push(part);
16249
+ } else {
16250
+ dot = ctx.s.indexOf(".", ctx.p);
16251
+ let part = ctx.s.slice(ctx.p, dot < 0 || dot > endPtr ? endPtr : dot);
16252
+ if (!KEY_PART_RE.test(part)) {
16253
+ throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
16254
+ toml: ctx.s,
16255
+ ptr: ctx.p
16256
+ });
16257
+ }
16258
+ parsed.push(part.trimEnd());
16259
+ }
16260
+ }
16261
+ } while (dot + 1 && dot < endPtr);
16262
+ ctx.p = endPtr + 1;
16263
+ skipVoid(ctx, true, true);
16264
+ return parsed;
16265
+ }
16266
+ function parseInlineTable(ctx, integersAsBigInt) {
16267
+ let res = {};
16268
+ let seen = new Set;
16269
+ let c;
16270
+ ctx.p++;
16271
+ while (ctx.p < ctx.s.length) {
16272
+ skipVoid(ctx);
16273
+ if ((c = ctx.s.charCodeAt(ctx.p)) === 125) {
16274
+ ctx.p++;
16275
+ return res;
16276
+ }
16277
+ let k;
16278
+ let t = res;
16279
+ let hasOwn = false;
16280
+ let p = ctx.p;
16281
+ let key = parseKey(ctx);
16282
+ for (let i = 0;i < key.length; i++) {
16283
+ if (i)
16284
+ t = hasOwn ? t[k] : t[k] = {};
16285
+ k = key[i];
16286
+ if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
16287
+ throw new TomlError("trying to redefine an already defined value", {
16288
+ toml: ctx.s,
16289
+ ptr: p
16290
+ });
16291
+ }
16292
+ if (!hasOwn && k === "__proto__") {
16293
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
16294
+ }
16295
+ }
16296
+ if (hasOwn) {
16297
+ throw new TomlError("trying to redefine an already defined value", {
16298
+ toml: ctx.s,
16299
+ ptr: ctx.p
16300
+ });
16301
+ }
16302
+ let value = extractValue(ctx, 125, integersAsBigInt);
16303
+ seen.add(t[k] = value);
16304
+ skipVoid(ctx);
16305
+ if ((c = ctx.s.charCodeAt(ctx.p++)) === 125) {
16306
+ return res;
16307
+ }
16308
+ if (c !== 44) {
16309
+ throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
16310
+ }
16311
+ }
16312
+ throw new TomlError("unfinished table encountered", {
16313
+ toml: ctx.s,
16314
+ ptr: ctx.p
16315
+ });
16316
+ }
16317
+ function parseArray(ctx, integersAsBigInt) {
16318
+ let res = [];
16319
+ let c;
16320
+ ctx.p++;
16321
+ while (ctx.p < ctx.s.length) {
16322
+ skipVoid(ctx);
16323
+ if ((c = ctx.s.charCodeAt(ctx.p)) === 93) {
16324
+ ctx.p++;
16325
+ return res;
16326
+ }
16327
+ res.push(extractValue(ctx, 93, integersAsBigInt));
16328
+ skipVoid(ctx);
16329
+ if ((c = ctx.s.charCodeAt(ctx.p++)) === 93) {
16330
+ return res;
16331
+ }
16332
+ if (c !== 44) {
16333
+ throw new TomlError("expected comma or end of structure", { toml: ctx.s, ptr: ctx.p - 1 });
16334
+ }
16335
+ }
16336
+ throw new TomlError("unfinished array encountered", {
16337
+ toml: ctx.s,
16338
+ ptr: ctx.p
16339
+ });
16340
+ }
16341
+
16342
+ // node_modules/smol-toml/dist/parse.js
16343
+ /*!
16344
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
16345
+ * SPDX-License-Identifier: BSD-3-Clause
16346
+ *
16347
+ * Redistribution and use in source and binary forms, with or without
16348
+ * modification, are permitted provided that the following conditions are met:
16349
+ *
16350
+ * 1. Redistributions of source code must retain the above copyright notice, this
16351
+ * list of conditions and the following disclaimer.
16352
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
16353
+ * this list of conditions and the following disclaimer in the
16354
+ * documentation and/or other materials provided with the distribution.
16355
+ * 3. Neither the name of the copyright holder nor the names of its contributors
16356
+ * may be used to endorse or promote products derived from this software without
16357
+ * specific prior written permission.
16358
+ *
16359
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16360
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16361
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16362
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
16363
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16364
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16365
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16366
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
16367
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16368
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16369
+ */
16370
+ function peekTable(key, table, meta, type) {
16371
+ let t = table;
16372
+ let m = meta;
16373
+ let k;
16374
+ let hasOwn = false;
16375
+ let state;
16376
+ for (let i = 0;i < key.length; i++) {
16377
+ if (i) {
16378
+ t = hasOwn ? t[k] : t[k] = {};
16379
+ m = (state = m[k]).c;
16380
+ if (type === 0 && (state.t === 1 || state.t === 2)) {
16381
+ return null;
16382
+ }
16383
+ if (state.t === 2) {
16384
+ let l = t.length - 1;
16385
+ t = t[l];
16386
+ m = m[l].c;
16387
+ }
16388
+ }
16389
+ k = key[i];
16390
+ if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
16391
+ return null;
16392
+ }
16393
+ if (!hasOwn) {
16394
+ if (k === "__proto__") {
16395
+ Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
16396
+ Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
16397
+ }
16398
+ m[k] = {
16399
+ t: i < key.length - 1 && type === 2 ? 3 : type,
16400
+ d: false,
16401
+ i: 0,
16402
+ c: {}
16403
+ };
16404
+ }
16405
+ }
16406
+ state = m[k];
16407
+ if (state.t !== type && !(type === 1 && state.t === 3)) {
16408
+ return null;
16409
+ }
16410
+ if (type === 2) {
16411
+ if (!state.d) {
16412
+ state.d = true;
16413
+ t[k] = [];
16414
+ }
16415
+ t[k].push(t = {});
16416
+ state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
16417
+ }
16418
+ if (state.d) {
16419
+ return null;
16420
+ }
16421
+ state.d = true;
16422
+ if (type === 1) {
16423
+ t = hasOwn ? t[k] : t[k] = {};
16424
+ } else if (type === 0 && hasOwn) {
16425
+ return null;
16426
+ }
16427
+ return [k, t, state.c];
16428
+ }
16429
+ function parse(toml, { maxDepth = 1000, integersAsBigInt } = {}) {
16430
+ let ctx = { s: toml, p: 0, d: maxDepth };
16431
+ let res = {};
16432
+ let meta = {};
16433
+ let tmp;
16434
+ let tbl = res;
16435
+ let m = meta;
16436
+ skipVoid(ctx);
16437
+ while (ctx.p < toml.length) {
16438
+ if (toml.charCodeAt(ctx.p) === 91) {
16439
+ let isTableArray = toml.charCodeAt(++ctx.p) === 91;
16440
+ tmp = ctx.p += +isTableArray;
16441
+ let k = parseKey(ctx, "]");
16442
+ if (isTableArray) {
16443
+ if (toml.charCodeAt(ctx.p - 1) !== 93) {
16444
+ throw new TomlError("expected end of table declaration", {
16445
+ toml,
16446
+ ptr: ctx.p - 1
16447
+ });
16448
+ }
16449
+ ctx.p++;
16450
+ }
16451
+ let p = peekTable(k, res, meta, isTableArray ? 2 : 1);
16452
+ if (!p) {
16453
+ throw new TomlError("trying to redefine an already defined table or value", {
16454
+ toml,
16455
+ ptr: tmp
16456
+ });
16457
+ }
16458
+ m = p[2];
16459
+ tbl = p[1];
16460
+ } else {
16461
+ tmp = ctx.p;
16462
+ let k = parseKey(ctx);
16463
+ let p = peekTable(k, tbl, m, 0);
16464
+ if (!p) {
16465
+ throw new TomlError("trying to redefine an already defined table or value", {
16466
+ toml,
16467
+ ptr: tmp
16468
+ });
16469
+ }
16470
+ p[1][p[0]] = extractValue(ctx, undefined, integersAsBigInt);
16471
+ }
16472
+ skipVoid(ctx, true);
16473
+ if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 10 && tmp !== 13) {
16474
+ throw new TomlError("each key-value declaration must be followed by an end-of-line", {
16475
+ toml,
16476
+ ptr: ctx.p
16477
+ });
16478
+ }
16479
+ skipVoid(ctx);
16480
+ }
16481
+ return res;
16482
+ }
16483
+
16484
+ // node_modules/smol-toml/dist/stringify.js
16485
+ /*!
16486
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
16487
+ * SPDX-License-Identifier: BSD-3-Clause
16488
+ *
16489
+ * Redistribution and use in source and binary forms, with or without
16490
+ * modification, are permitted provided that the following conditions are met:
16491
+ *
16492
+ * 1. Redistributions of source code must retain the above copyright notice, this
16493
+ * list of conditions and the following disclaimer.
16494
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
16495
+ * this list of conditions and the following disclaimer in the
16496
+ * documentation and/or other materials provided with the distribution.
16497
+ * 3. Neither the name of the copyright holder nor the names of its contributors
16498
+ * may be used to endorse or promote products derived from this software without
16499
+ * specific prior written permission.
16500
+ *
16501
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16502
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16503
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16504
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
16505
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16506
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16507
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16508
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
16509
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16510
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16511
+ */
16512
+
16513
+ // node_modules/smol-toml/dist/index.js
16514
+ /*!
16515
+ * Copyright (c) Squirrel Chat et al., All rights reserved.
16516
+ * SPDX-License-Identifier: BSD-3-Clause
16517
+ *
16518
+ * Redistribution and use in source and binary forms, with or without
16519
+ * modification, are permitted provided that the following conditions are met:
16520
+ *
16521
+ * 1. Redistributions of source code must retain the above copyright notice, this
16522
+ * list of conditions and the following disclaimer.
16523
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
16524
+ * this list of conditions and the following disclaimer in the
16525
+ * documentation and/or other materials provided with the distribution.
16526
+ * 3. Neither the name of the copyright holder nor the names of its contributors
16527
+ * may be used to endorse or promote products derived from this software without
16528
+ * specific prior written permission.
16529
+ *
16530
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16531
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16532
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16533
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
16534
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16535
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16536
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16537
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
16538
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16539
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16540
+ */
16541
+
15784
16542
  // node_modules/yaml/dist/index.js
15785
16543
  var composer = require_composer();
15786
16544
  var Document = require_Document();
@@ -15828,11 +16586,9 @@ var $visit = visit.visit;
15828
16586
  var $visitAsync = visit.visitAsync;
15829
16587
 
15830
16588
  // src/audit/core/shared.ts
15831
- var REGISTRY_REL_PATH = ".skeleton/registry.md";
16589
+ var CATALOG_REL_PATH = ".skeleton/catalog.md";
15832
16590
  var REGISTRY_DIR_REL = ".skeleton";
15833
16591
  var EXTERNAL_LINK_RE = /^(https?:|mailto:|#)/;
15834
- var SOURCE_OF_TRUTH_BANNER_RE = /\*\*Source of truth for\*\*/;
15835
- var SOURCE_OF_TRUTH_BANNER_LINE_RE = /^\s*\*\*Source of truth for\*\*/m;
15836
16592
  var DOC_META_RE = /<!--\s*doc-meta:\s*owner=[^|]+\|\s*last-reviewed=\d{4}-\d{2}-\d{2}\s*-->/;
15837
16593
  var DOC_META_LAST_REVIEWED_RE = /last-reviewed=(\d{4}-\d{2}-\d{2})/;
15838
16594
  function docMetaLastReviewed(content) {
@@ -15933,6 +16689,8 @@ var SCHEMA_CANDIDATES = [
15933
16689
  join(dirname(fileURLToPath(import.meta.url)), "../schemas/config.schema.json"),
15934
16690
  join(dirname(fileURLToPath(import.meta.url)), "../../schemas/config.schema.json")
15935
16691
  ];
16692
+ var ROOT_CONFIG_TOML = "skeleton.toml";
16693
+ var LEGACY_CONFIG_YAML = join(".skeleton", "config.yaml");
15936
16694
  function resolveSchemaPath() {
15937
16695
  for (const candidate of SCHEMA_CANDIDATES) {
15938
16696
  if (existsSync(candidate))
@@ -15947,44 +16705,64 @@ var COVERAGE_BUILTIN_EXCLUDES = [
15947
16705
  "**/fixtures/**",
15948
16706
  "templates/**"
15949
16707
  ];
16708
+ function hasConfigMarker(dir) {
16709
+ return existsSync(join(dir, ROOT_CONFIG_TOML)) || existsSync(join(dir, LEGACY_CONFIG_YAML));
16710
+ }
15950
16711
  function findRepoRoot(startDir = process2.cwd()) {
15951
16712
  let dir = startDir;
15952
16713
  while (true) {
15953
- if (existsSync(join(dir, ".skeleton", "config.yaml")))
16714
+ if (hasConfigMarker(dir))
15954
16715
  return dir;
15955
16716
  const parent = dirname(dir);
15956
16717
  if (parent === dir) {
15957
- throw new Error("No .skeleton/config.yaml found — run skeleton init or create config manually");
16718
+ throw new Error(`No ${ROOT_CONFIG_TOML} or ${LEGACY_CONFIG_YAML} found — run skeleton init or create config manually`);
15958
16719
  }
15959
16720
  dir = parent;
15960
16721
  }
15961
16722
  }
15962
- function validateConfig(raw) {
16723
+ function validateConfig(raw, sourceLabel) {
15963
16724
  const schema = JSON.parse(readFileSync(resolveSchemaPath(), "utf8"));
15964
16725
  const ajv = new import_ajv.default({ allErrors: true, strict: false });
15965
16726
  const validate = ajv.compile(schema);
15966
16727
  if (!validate(raw)) {
15967
16728
  const detail = validate.errors?.map((e) => `${e.instancePath || "/"} ${e.message}`).join("; ");
15968
- throw new Error(`Invalid .skeleton/config.yaml: ${detail ?? "schema validation failed"}`);
16729
+ throw new Error(`Invalid ${sourceLabel}: ${detail ?? "schema validation failed"}`);
15969
16730
  }
15970
16731
  const config = raw;
15971
16732
  validateDraftPathPrefixes(config.draftPathPrefixes);
15972
16733
  return config;
15973
16734
  }
15974
- function loadConfig(root) {
15975
- const configPath = join(root, ".skeleton", "config.yaml");
15976
- if (!existsSync(configPath)) {
15977
- throw new Error(`Missing ${join(".skeleton", "config.yaml")}`);
16735
+ function loadConfigDetailed(root) {
16736
+ const tomlPath = join(root, ROOT_CONFIG_TOML);
16737
+ const yamlPath = join(root, LEGACY_CONFIG_YAML);
16738
+ const hasToml = existsSync(tomlPath);
16739
+ const hasYaml = existsSync(yamlPath);
16740
+ if (!(hasToml || hasYaml)) {
16741
+ throw new Error(`Missing ${ROOT_CONFIG_TOML} (or legacy ${LEGACY_CONFIG_YAML})`);
16742
+ }
16743
+ if (hasToml && hasYaml) {
16744
+ console.error(`warning: both ${ROOT_CONFIG_TOML} and ${LEGACY_CONFIG_YAML} exist — using ${ROOT_CONFIG_TOML}; legacy YAML is ignored`);
15978
16745
  }
15979
- const raw = $parse(readFileSync(configPath, "utf8"));
15980
- return validateConfig(raw);
16746
+ if (hasToml) {
16747
+ const raw2 = parse(readFileSync(tomlPath, "utf8"));
16748
+ return {
16749
+ config: validateConfig(raw2, ROOT_CONFIG_TOML),
16750
+ source: "toml",
16751
+ warnedDual: hasToml && hasYaml
16752
+ };
16753
+ }
16754
+ const raw = $parse(readFileSync(yamlPath, "utf8"));
16755
+ return {
16756
+ config: validateConfig(raw, LEGACY_CONFIG_YAML),
16757
+ source: "yaml"
16758
+ };
16759
+ }
16760
+ function loadConfig(root) {
16761
+ return loadConfigDetailed(root).config;
15981
16762
  }
15982
16763
  function mergedExcludes(config) {
15983
16764
  return [...new Set([...BUILTIN_EXCLUDES, ...config.scan.exclude])];
15984
16765
  }
15985
- function retiredSkills(config) {
15986
- return config.scan.retiredSkills ?? [];
15987
- }
15988
16766
  function nonPublicSkills(config) {
15989
16767
  return config.scan.nonPublicSkills ?? [];
15990
16768
  }
@@ -17523,11 +18301,12 @@ function collectScanFiles(config, root, skillIndex) {
17523
18301
  });
17524
18302
  }
17525
18303
  function collectBannedFiles(config, root) {
17526
- if (config.scan.banned.length === 0)
18304
+ const patterns = config.deny?.paths ?? [];
18305
+ if (patterns.length === 0)
17527
18306
  return [];
17528
18307
  const exclude = mergedExcludes(config);
17529
18308
  const files = new Set;
17530
- for (const pattern of config.scan.banned) {
18309
+ for (const pattern of patterns) {
17531
18310
  for (const abs of globSync(pattern, {
17532
18311
  cwd: root,
17533
18312
  absolute: true,
@@ -17592,7 +18371,7 @@ function collectDocMetaPaths(ctx) {
17592
18371
  for (const abs of expandPatterns(ctx.root, ["docs/*/README.md"], mergedExcludes(ctx.config))) {
17593
18372
  paths.push(normalizeRelPath(relative4(ctx.root, abs)));
17594
18373
  }
17595
- const extras = ["docs/README.md", ".skeleton/registry.md"];
18374
+ const extras = ["docs/README.md"];
17596
18375
  for (const file of extras) {
17597
18376
  const abs = join6(ctx.root, file);
17598
18377
  if (existsSync7(abs))
@@ -17661,31 +18440,108 @@ function relPath(absPath, root) {
17661
18440
  return normalizeRelPath(relative4(root, absPath));
17662
18441
  }
17663
18442
 
17664
- // src/audit/core/registry.ts
17665
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
17666
- import { join as join7, relative as relative5, resolve as resolve4 } from "node:path";
17667
- var REGISTRY_TABLE_ROW_RE = /^\|\s*[^|]+\|\s*\[[^\]]*\]\(([^)]+)\)\s*\|/;
17668
- var REGISTRY_TABLE_HEADER_RE = /\|\s*Topic\s*\|\s*Canonical file\s*\|/i;
17669
- function parseRegistry(root) {
17670
- const abs = join7(root, REGISTRY_REL_PATH);
17671
- if (!existsSync8(abs)) {
17672
- return { paths: [], hasTableHeader: false };
18443
+ // src/audit/core/ssot-collect.ts
18444
+ import { readFileSync as readFileSync6 } from "node:fs";
18445
+ import { relative as relative5 } from "node:path";
18446
+
18447
+ // src/audit/core/ssot.ts
18448
+ var SSOT_COMMENT_RE = /<!--\s*source-of-truth:\s*([\s\S]*?)\s*-->/gi;
18449
+ var HWS = "[ \\t]*";
18450
+ var SSOT_VISIBLE_LINE_RE = new RegExp(`^${HWS}source-of-truth:${HWS}(.+?)${HWS}$`, "gim");
18451
+ var LEGACY_BANNER_LINE_RE = new RegExp(`^${HWS}\\*\\*Source of truth for\\*\\*${HWS}(.+?)${HWS}$`, "gim");
18452
+ function cleanSummary(raw) {
18453
+ return raw.replace(/\s+/g, " ").trim().replace(/\.$/, "").trim();
18454
+ }
18455
+ function stripCode(content) {
18456
+ return content.replace(/```[\s\S]*?```/g, `
18457
+ `).replace(/`[^`\n]+`/g, " ");
18458
+ }
18459
+ function collectMatches(re, content) {
18460
+ const out = [];
18461
+ re.lastIndex = 0;
18462
+ let match = re.exec(content);
18463
+ while (match) {
18464
+ const summary = cleanSummary(match[1] ?? "");
18465
+ if (summary)
18466
+ out.push(summary);
18467
+ match = re.exec(content);
17673
18468
  }
17674
- const content = readFileSync6(abs, "utf8");
17675
- const hasTableHeader = REGISTRY_TABLE_HEADER_RE.test(content);
17676
- const paths = [];
17677
- for (const line of content.split(`
17678
- `)) {
17679
- const match = REGISTRY_TABLE_ROW_RE.exec(line);
17680
- if (!match?.[1])
18469
+ return out;
18470
+ }
18471
+ function parseSsot(content) {
18472
+ const prose = stripCode(content);
18473
+ const comments = collectMatches(SSOT_COMMENT_RE, prose);
18474
+ const visibles = collectMatches(SSOT_VISIBLE_LINE_RE, prose);
18475
+ const legacies = collectMatches(LEGACY_BANNER_LINE_RE, prose);
18476
+ const forms = [];
18477
+ if (comments.length)
18478
+ forms.push("comment");
18479
+ if (visibles.length)
18480
+ forms.push("visible");
18481
+ if (legacies.length)
18482
+ forms.push("legacy");
18483
+ if (forms.length === 0)
18484
+ return { status: "none" };
18485
+ if (forms.length > 1)
18486
+ return { status: "dual", forms };
18487
+ if (comments.length > 1 || visibles.length > 1 || legacies.length > 1) {
18488
+ return { status: "malformed", detail: "multiple source-of-truth markers of the same form" };
18489
+ }
18490
+ if (comments.length === 1) {
18491
+ const summary2 = comments[0];
18492
+ if (!summary2)
18493
+ return { status: "malformed", detail: "empty source-of-truth comment" };
18494
+ return { status: "ok", entry: { summary: summary2, form: "comment" } };
18495
+ }
18496
+ if (visibles.length === 1) {
18497
+ const summary2 = visibles[0];
18498
+ if (!summary2)
18499
+ return { status: "malformed", detail: "empty source-of-truth line" };
18500
+ return { status: "ok", entry: { summary: summary2, form: "visible" } };
18501
+ }
18502
+ const summary = legacies[0];
18503
+ if (!summary)
18504
+ return { status: "malformed", detail: "empty legacy Source of truth banner" };
18505
+ return { status: "ok", entry: { summary, form: "legacy" } };
18506
+ }
18507
+ function rewriteLegacySsotToComment(content) {
18508
+ const parsed = parseSsot(content);
18509
+ if (parsed.status !== "ok" || parsed.entry.form !== "legacy")
18510
+ return null;
18511
+ const comment = `<!-- source-of-truth: ${parsed.entry.summary} -->`;
18512
+ const next = content.replace(LEGACY_BANNER_LINE_RE, comment);
18513
+ return next === content ? null : next;
18514
+ }
18515
+
18516
+ // src/audit/core/ssot-collect.ts
18517
+ function collectSsotEntries(files, root) {
18518
+ const entries = [];
18519
+ const errors2 = [];
18520
+ for (const abs of files) {
18521
+ const rel = normalizeRelPath(relative5(root, abs));
18522
+ if (!(rel.endsWith(".md") || rel.endsWith(".mdc")))
17681
18523
  continue;
17682
- const linkTarget = match[1].trim();
17683
- if (linkTarget.startsWith("#"))
18524
+ const content = readFileSync6(abs, "utf8");
18525
+ const parsed = parseSsot(content);
18526
+ if (parsed.status === "none")
18527
+ continue;
18528
+ if (parsed.status === "dual") {
18529
+ errors2.push({
18530
+ path: rel,
18531
+ kind: "dual",
18532
+ detail: `multiple source-of-truth forms: ${parsed.forms.join(", ")}`,
18533
+ forms: parsed.forms
18534
+ });
17684
18535
  continue;
17685
- const resolved = resolve4(join7(root, REGISTRY_DIR_REL), linkTarget);
17686
- paths.push(normalizeRelPath(relative5(root, resolved)));
18536
+ }
18537
+ if (parsed.status === "malformed") {
18538
+ errors2.push({ path: rel, kind: "malformed", detail: parsed.detail });
18539
+ continue;
18540
+ }
18541
+ entries.push({ path: rel, summary: parsed.entry.summary, form: parsed.entry.form });
17687
18542
  }
17688
- return { paths: [...new Set(paths)], hasTableHeader };
18543
+ entries.sort((a, b) => a.path.localeCompare(b.path));
18544
+ return { entries, errors: errors2 };
17689
18545
  }
17690
18546
 
17691
18547
  // src/audit/core/context.ts
@@ -17701,11 +18557,12 @@ function createContext(options = {}) {
17701
18557
  files = includeExplicitMarkdownPaths(files, options.paths, root);
17702
18558
  files = filterToPaths(files, options.paths, root);
17703
18559
  }
17704
- const registry = parseRegistry(root);
18560
+ const ssot = collectSsotEntries(files, root);
18561
+ const ssotPaths = ssot.entries.map((e) => e.path);
17705
18562
  const allDocMetaPaths = collectDocMetaPaths({
17706
18563
  config,
17707
18564
  root,
17708
- registryPaths: registry.paths,
18565
+ registryPaths: ssotPaths,
17709
18566
  skillIndex
17710
18567
  });
17711
18568
  return {
@@ -17713,9 +18570,10 @@ function createContext(options = {}) {
17713
18570
  config,
17714
18571
  files,
17715
18572
  docMetaPaths: options.paths && options.paths.length > 0 ? filterDocMetaPaths(allDocMetaPaths, options.paths, skillIndex) : allDocMetaPaths,
17716
- registryPaths: registry.paths,
17717
- registryHasTableHeader: registry.hasTableHeader,
17718
- retiredSkills: new Set(retiredSkills(config)),
18573
+ ssotEntries: ssot.entries,
18574
+ ssotErrors: ssot.errors,
18575
+ registryPaths: [],
18576
+ registryHasTableHeader: false,
17719
18577
  skillIndex,
17720
18578
  lockedSkillSlugs: new Set(skillIndex.foreignSlugs),
17721
18579
  policies: options.policies ?? []
@@ -17723,12 +18581,12 @@ function createContext(options = {}) {
17723
18581
  }
17724
18582
 
17725
18583
  // src/audit/core/fix.ts
17726
- import { existsSync as existsSync11, realpathSync as realpathSync5, writeFileSync } from "node:fs";
17727
- import { dirname as dirname6, resolve as resolve6, sep as sep3 } from "node:path";
18584
+ import { existsSync as existsSync10, realpathSync as realpathSync5, writeFileSync } from "node:fs";
18585
+ import { dirname as dirname6, resolve as resolve5, sep as sep3 } from "node:path";
17728
18586
 
17729
18587
  // src/audit/fix/anchors.ts
17730
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
17731
- import { dirname as dirname5, resolve as resolve5 } from "node:path";
18588
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
18589
+ import { dirname as dirname5, resolve as resolve4 } from "node:path";
17732
18590
 
17733
18591
  // node_modules/github-slugger/regex.js
17734
18592
  var regex = /[\0-\x1F!-,\.\/:-@\[-\^`\{-\xA9\xAB-\xB4\xB6-\xB9\xBB-\xBF\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0378\u0379\u037E\u0380-\u0385\u0387\u038B\u038D\u03A2\u03F6\u0482\u0530\u0557\u0558\u055A-\u055F\u0589-\u0590\u05BE\u05C0\u05C3\u05C6\u05C8-\u05CF\u05EB-\u05EE\u05F3-\u060F\u061B-\u061F\u066A-\u066D\u06D4\u06DD\u06DE\u06E9\u06FD\u06FE\u0700-\u070F\u074B\u074C\u07B2-\u07BF\u07F6-\u07F9\u07FB\u07FC\u07FE\u07FF\u082E-\u083F\u085C-\u085F\u086B-\u089F\u08B5\u08C8-\u08D2\u08E2\u0964\u0965\u0970\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09F2-\u09FB\u09FD\u09FF\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF0-\u0AF8\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B54\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B70\u0B72-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BF0-\u0BFF\u0C0D\u0C11\u0C29\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5B-\u0C5F\u0C64\u0C65\u0C70-\u0C7F\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0CFF\u0D0D\u0D11\u0D45\u0D49\u0D4F-\u0D53\u0D58-\u0D5E\u0D64\u0D65\u0D70-\u0D79\u0D80\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DE5\u0DF0\u0DF1\u0DF4-\u0E00\u0E3B-\u0E3F\u0E4F\u0E5A-\u0E80\u0E83\u0E85\u0E8B\u0EA4\u0EA6\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F01-\u0F17\u0F1A-\u0F1F\u0F2A-\u0F34\u0F36\u0F38\u0F3A-\u0F3D\u0F48\u0F6D-\u0F70\u0F85\u0F98\u0FBD-\u0FC5\u0FC7-\u0FFF\u104A-\u104F\u109E\u109F\u10C6\u10C8-\u10CC\u10CE\u10CF\u10FB\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u1360-\u137F\u1390-\u139F\u13F6\u13F7\u13FE-\u1400\u166D\u166E\u1680\u169B-\u169F\u16EB-\u16ED\u16F9-\u16FF\u170D\u1715-\u171F\u1735-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17D4-\u17D6\u17D8-\u17DB\u17DE\u17DF\u17EA-\u180A\u180E\u180F\u181A-\u181F\u1879-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191F\u192C-\u192F\u193C-\u1945\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DA-\u19FF\u1A1C-\u1A1F\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1AA6\u1AA8-\u1AAF\u1AC1-\u1AFF\u1B4C-\u1B4F\u1B5A-\u1B6A\u1B74-\u1B7F\u1BF4-\u1BFF\u1C38-\u1C3F\u1C4A-\u1C4C\u1C7E\u1C7F\u1C89-\u1C8F\u1CBB\u1CBC\u1CC0-\u1CCF\u1CD3\u1CFB-\u1CFF\u1DFA\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FBD\u1FBF-\u1FC1\u1FC5\u1FCD-\u1FCF\u1FD4\u1FD5\u1FDC-\u1FDF\u1FED-\u1FF1\u1FF5\u1FFD-\u203E\u2041-\u2053\u2055-\u2070\u2072-\u207E\u2080-\u208F\u209D-\u20CF\u20F1-\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F-\u215F\u2189-\u24B5\u24EA-\u2BFF\u2C2F\u2C5F\u2CE5-\u2CEA\u2CF4-\u2CFF\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D70-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E00-\u2E2E\u2E30-\u3004\u3008-\u3020\u3030\u3036\u3037\u303D-\u3040\u3097\u3098\u309B\u309C\u30A0\u30FB\u3100-\u3104\u3130\u318F-\u319F\u31C0-\u31EF\u3200-\u33FF\u4DC0-\u4DFF\u9FFD-\u9FFF\uA48D-\uA4CF\uA4FE\uA4FF\uA60D-\uA60F\uA62C-\uA63F\uA673\uA67E\uA6F2-\uA716\uA720\uA721\uA789\uA78A\uA7C0\uA7C1\uA7CB-\uA7F4\uA828-\uA82B\uA82D-\uA83F\uA874-\uA87F\uA8C6-\uA8CF\uA8DA-\uA8DF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA954-\uA95F\uA97D-\uA97F\uA9C1-\uA9CE\uA9DA-\uA9DF\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A-\uAA5F\uAA77-\uAA79\uAAC3-\uAADA\uAADE\uAADF\uAAF0\uAAF1\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F\uAB5B\uAB6A-\uAB6F\uABEB\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB29\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBB2-\uFBD2\uFD3E-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFC-\uFDFF\uFE10-\uFE1F\uFE30-\uFE32\uFE35-\uFE4C\uFE50-\uFE6F\uFE75\uFEFD-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF3E\uFF40\uFF5B-\uFF65\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFFF]|\uD800[\uDC0C\uDC27\uDC3B\uDC3E\uDC4E\uDC4F\uDC5E-\uDC7F\uDCFB-\uDD3F\uDD75-\uDDFC\uDDFE-\uDE7F\uDE9D-\uDE9F\uDED1-\uDEDF\uDEE1-\uDEFF\uDF20-\uDF2C\uDF4B-\uDF4F\uDF7B-\uDF7F\uDF9E\uDF9F\uDFC4-\uDFC7\uDFD0\uDFD6-\uDFFF]|\uD801[\uDC9E\uDC9F\uDCAA-\uDCAF\uDCD4-\uDCD7\uDCFC-\uDCFF\uDD28-\uDD2F\uDD64-\uDDFF\uDF37-\uDF3F\uDF56-\uDF5F\uDF68-\uDFFF]|\uD802[\uDC06\uDC07\uDC09\uDC36\uDC39-\uDC3B\uDC3D\uDC3E\uDC56-\uDC5F\uDC77-\uDC7F\uDC9F-\uDCDF\uDCF3\uDCF6-\uDCFF\uDD16-\uDD1F\uDD3A-\uDD7F\uDDB8-\uDDBD\uDDC0-\uDDFF\uDE04\uDE07-\uDE0B\uDE14\uDE18\uDE36\uDE37\uDE3B-\uDE3E\uDE40-\uDE5F\uDE7D-\uDE7F\uDE9D-\uDEBF\uDEC8\uDEE7-\uDEFF\uDF36-\uDF3F\uDF56-\uDF5F\uDF73-\uDF7F\uDF92-\uDFFF]|\uD803[\uDC49-\uDC7F\uDCB3-\uDCBF\uDCF3-\uDCFF\uDD28-\uDD2F\uDD3A-\uDE7F\uDEAA\uDEAD-\uDEAF\uDEB2-\uDEFF\uDF1D-\uDF26\uDF28-\uDF2F\uDF51-\uDFAF\uDFC5-\uDFDF\uDFF7-\uDFFF]|\uD804[\uDC47-\uDC65\uDC70-\uDC7E\uDCBB-\uDCCF\uDCE9-\uDCEF\uDCFA-\uDCFF\uDD35\uDD40-\uDD43\uDD48-\uDD4F\uDD74\uDD75\uDD77-\uDD7F\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDFF\uDE12\uDE38-\uDE3D\uDE3F-\uDE7F\uDE87\uDE89\uDE8E\uDE9E\uDEA9-\uDEAF\uDEEB-\uDEEF\uDEFA-\uDEFF\uDF04\uDF0D\uDF0E\uDF11\uDF12\uDF29\uDF31\uDF34\uDF3A\uDF45\uDF46\uDF49\uDF4A\uDF4E\uDF4F\uDF51-\uDF56\uDF58-\uDF5C\uDF64\uDF65\uDF6D-\uDF6F\uDF75-\uDFFF]|\uD805[\uDC4B-\uDC4F\uDC5A-\uDC5D\uDC62-\uDC7F\uDCC6\uDCC8-\uDCCF\uDCDA-\uDD7F\uDDB6\uDDB7\uDDC1-\uDDD7\uDDDE-\uDDFF\uDE41-\uDE43\uDE45-\uDE4F\uDE5A-\uDE7F\uDEB9-\uDEBF\uDECA-\uDEFF\uDF1B\uDF1C\uDF2C-\uDF2F\uDF3A-\uDFFF]|\uD806[\uDC3B-\uDC9F\uDCEA-\uDCFE\uDD07\uDD08\uDD0A\uDD0B\uDD14\uDD17\uDD36\uDD39\uDD3A\uDD44-\uDD4F\uDD5A-\uDD9F\uDDA8\uDDA9\uDDD8\uDDD9\uDDE2\uDDE5-\uDDFF\uDE3F-\uDE46\uDE48-\uDE4F\uDE9A-\uDE9C\uDE9E-\uDEBF\uDEF9-\uDFFF]|\uD807[\uDC09\uDC37\uDC41-\uDC4F\uDC5A-\uDC71\uDC90\uDC91\uDCA8\uDCB7-\uDCFF\uDD07\uDD0A\uDD37-\uDD39\uDD3B\uDD3E\uDD48-\uDD4F\uDD5A-\uDD5F\uDD66\uDD69\uDD8F\uDD92\uDD99-\uDD9F\uDDAA-\uDEDF\uDEF7-\uDFAF\uDFB1-\uDFFF]|\uD808[\uDF9A-\uDFFF]|\uD809[\uDC6F-\uDC7F\uDD44-\uDFFF]|[\uD80A\uD80B\uD80E-\uD810\uD812-\uD819\uD824-\uD82B\uD82D\uD82E\uD830-\uD833\uD837\uD839\uD83D\uD83F\uD87B-\uD87D\uD87F\uD885-\uDB3F\uDB41-\uDBFF][\uDC00-\uDFFF]|\uD80D[\uDC2F-\uDFFF]|\uD811[\uDE47-\uDFFF]|\uD81A[\uDE39-\uDE3F\uDE5F\uDE6A-\uDECF\uDEEE\uDEEF\uDEF5-\uDEFF\uDF37-\uDF3F\uDF44-\uDF4F\uDF5A-\uDF62\uDF78-\uDF7C\uDF90-\uDFFF]|\uD81B[\uDC00-\uDE3F\uDE80-\uDEFF\uDF4B-\uDF4E\uDF88-\uDF8E\uDFA0-\uDFDF\uDFE2\uDFE5-\uDFEF\uDFF2-\uDFFF]|\uD821[\uDFF8-\uDFFF]|\uD823[\uDCD6-\uDCFF\uDD09-\uDFFF]|\uD82C[\uDD1F-\uDD4F\uDD53-\uDD63\uDD68-\uDD6F\uDEFC-\uDFFF]|\uD82F[\uDC6B-\uDC6F\uDC7D-\uDC7F\uDC89-\uDC8F\uDC9A-\uDC9C\uDC9F-\uDFFF]|\uD834[\uDC00-\uDD64\uDD6A-\uDD6C\uDD73-\uDD7A\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDE41\uDE45-\uDFFF]|\uD835[\uDC55\uDC9D\uDCA0\uDCA1\uDCA3\uDCA4\uDCA7\uDCA8\uDCAD\uDCBA\uDCBC\uDCC4\uDD06\uDD0B\uDD0C\uDD15\uDD1D\uDD3A\uDD3F\uDD45\uDD47-\uDD49\uDD51\uDEA6\uDEA7\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3\uDFCC\uDFCD]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85-\uDE9A\uDEA0\uDEB0-\uDFFF]|\uD838[\uDC07\uDC19\uDC1A\uDC22\uDC25\uDC2B-\uDCFF\uDD2D-\uDD2F\uDD3E\uDD3F\uDD4A-\uDD4D\uDD4F-\uDEBF\uDEFA-\uDFFF]|\uD83A[\uDCC5-\uDCCF\uDCD7-\uDCFF\uDD4C-\uDD4F\uDD5A-\uDFFF]|\uD83B[\uDC00-\uDDFF\uDE04\uDE20\uDE23\uDE25\uDE26\uDE28\uDE33\uDE38\uDE3A\uDE3C-\uDE41\uDE43-\uDE46\uDE48\uDE4A\uDE4C\uDE50\uDE53\uDE55\uDE56\uDE58\uDE5A\uDE5C\uDE5E\uDE60\uDE63\uDE65\uDE66\uDE6B\uDE73\uDE78\uDE7D\uDE7F\uDE8A\uDE9C-\uDEA0\uDEA4\uDEAA\uDEBC-\uDFFF]|\uD83C[\uDC00-\uDD2F\uDD4A-\uDD4F\uDD6A-\uDD6F\uDD8A-\uDFFF]|\uD83E[\uDC00-\uDFEF\uDFFA-\uDFFF]|\uD869[\uDEDE-\uDEFF]|\uD86D[\uDF35-\uDF3F]|\uD86E[\uDC1E\uDC1F]|\uD873[\uDEA2-\uDEAF]|\uD87A[\uDFE1-\uDFFF]|\uD87E[\uDE1E-\uDFFF]|\uD884[\uDF4B-\uDFFF]|\uDB40[\uDC00-\uDCFF\uDDF0-\uDFFF]/g;
@@ -20731,10 +21589,10 @@ function resolveAll(constructs2, events, context) {
20731
21589
  const called = [];
20732
21590
  let index = -1;
20733
21591
  while (++index < constructs2.length) {
20734
- const resolve5 = constructs2[index].resolveAll;
20735
- if (resolve5 && !called.includes(resolve5)) {
20736
- events = resolve5(events, context);
20737
- called.push(resolve5);
21592
+ const resolve4 = constructs2[index].resolveAll;
21593
+ if (resolve4 && !called.includes(resolve4)) {
21594
+ events = resolve4(events, context);
21595
+ called.push(resolve4);
20738
21596
  }
20739
21597
  }
20740
21598
  return events;
@@ -21979,9 +22837,9 @@ function factoryTitle(effects, ok2, nok, type, markerType, stringType) {
21979
22837
  return atBreak(code);
21980
22838
  }
21981
22839
  effects.consume(code);
21982
- return code === codes.backslash ? escape : inside;
22840
+ return code === codes.backslash ? escape2 : inside;
21983
22841
  }
21984
- function escape(code) {
22842
+ function escape2(code) {
21985
22843
  if (code === marker || code === codes.backslash) {
21986
22844
  effects.consume(code);
21987
22845
  return inside;
@@ -24042,7 +24900,7 @@ function serializeChunks(chunks, expandTabs) {
24042
24900
  }
24043
24901
 
24044
24902
  // node_modules/micromark/dev/lib/parse.js
24045
- function parse(options) {
24903
+ function parse2(options) {
24046
24904
  const settings = options || {};
24047
24905
  const constructs2 = combineExtensions([exports_constructs, ...settings.extensions || []]);
24048
24906
  const parser2 = {
@@ -24201,7 +25059,7 @@ function fromMarkdown(value, encoding, options) {
24201
25059
  options = encoding;
24202
25060
  encoding = undefined;
24203
25061
  }
24204
- return compiler(options)(postprocess(parse(options).document().write(preprocess()(value, encoding, true))));
25062
+ return compiler(options)(postprocess(parse2(options).document().write(preprocess()(value, encoding, true))));
24205
25063
  }
24206
25064
  function compiler(options) {
24207
25065
  const config = {
@@ -25908,7 +26766,7 @@ var handle = {
25908
26766
  };
25909
26767
 
25910
26768
  // node_modules/mdast-util-to-markdown/lib/join.js
25911
- var join8 = [joinDefaults];
26769
+ var join7 = [joinDefaults];
25912
26770
  function joinDefaults(left, right, parent, state) {
25913
26771
  if (right.type === "code" && formatCodeAsIndented(right, state) && (left.type === "list" || left.type === right.type && formatCodeAsIndented(left, state))) {
25914
26772
  return false;
@@ -26291,7 +27149,7 @@ function toMarkdown(tree, options) {
26291
27149
  handle: undefined,
26292
27150
  indentLines,
26293
27151
  indexStack: [],
26294
- join: [...join8],
27152
+ join: [...join7],
26295
27153
  options: {},
26296
27154
  safe: safeBound,
26297
27155
  stack: [],
@@ -26784,7 +27642,7 @@ class Processor extends CallableInstance {
26784
27642
  assertParser("process", this.parser || this.Parser);
26785
27643
  assertCompiler("process", this.compiler || this.Compiler);
26786
27644
  return done ? executor(undefined, done) : new Promise(executor);
26787
- function executor(resolve5, reject) {
27645
+ function executor(resolve4, reject) {
26788
27646
  const realFile = vfile(file);
26789
27647
  const parseTree = self.parse(realFile);
26790
27648
  self.run(parseTree, realFile, function(error, tree, file2) {
@@ -26803,8 +27661,8 @@ class Processor extends CallableInstance {
26803
27661
  function realDone(error, file2) {
26804
27662
  if (error || !file2) {
26805
27663
  reject(error);
26806
- } else if (resolve5) {
26807
- resolve5(file2);
27664
+ } else if (resolve4) {
27665
+ resolve4(file2);
26808
27666
  } else {
26809
27667
  ok(done, "`done` is defined if `resolve` is not");
26810
27668
  done(undefined, file2);
@@ -26837,7 +27695,7 @@ class Processor extends CallableInstance {
26837
27695
  file = undefined;
26838
27696
  }
26839
27697
  return done ? executor(undefined, done) : new Promise(executor);
26840
- function executor(resolve5, reject) {
27698
+ function executor(resolve4, reject) {
26841
27699
  ok(typeof file !== "function", "`file` can’t be a `done` anymore, we checked");
26842
27700
  const realFile = vfile(file);
26843
27701
  transformers.run(tree, realFile, realDone);
@@ -26845,8 +27703,8 @@ class Processor extends CallableInstance {
26845
27703
  const resultingTree = outputTree || tree;
26846
27704
  if (error) {
26847
27705
  reject(error);
26848
- } else if (resolve5) {
26849
- resolve5(resultingTree);
27706
+ } else if (resolve4) {
27707
+ resolve4(resultingTree);
26850
27708
  } else {
26851
27709
  ok(done, "`done` is defined if `resolve` is not");
26852
27710
  done(undefined, resultingTree, file2);
@@ -29289,7 +30147,7 @@ function resolveLink(sourceFile, target) {
29289
30147
  const withoutAnchor = target.split("#")[0]?.split("?")[0] ?? "";
29290
30148
  if (!withoutAnchor)
29291
30149
  return sourceFile;
29292
- return resolve5(dirname5(sourceFile), withoutAnchor);
30150
+ return resolve4(dirname5(sourceFile), withoutAnchor);
29293
30151
  }
29294
30152
  function replaceAnchorInTarget(target, oldAnchor, newAnchor) {
29295
30153
  const hashIndex = target.indexOf("#");
@@ -29313,7 +30171,7 @@ function anchorTargetReplacement(filePath, target) {
29313
30171
  if (!anchor)
29314
30172
  return null;
29315
30173
  const resolved = resolveLink(filePath, target);
29316
- if (!existsSync9(resolved))
30174
+ if (!existsSync8(resolved))
29317
30175
  return null;
29318
30176
  const targetContent = readFileSync7(resolved, "utf8");
29319
30177
  const slugs = extractHeadingSlugs(targetContent, resolved);
@@ -29397,8 +30255,8 @@ function collectAnchorFixes(ctx) {
29397
30255
  }
29398
30256
 
29399
30257
  // src/audit/fix/doc-meta.ts
29400
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
29401
- import { join as join9 } from "node:path";
30258
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
30259
+ import { join as join8 } from "node:path";
29402
30260
 
29403
30261
  // src/audit/core/git-meta.ts
29404
30262
  import { spawnSync } from "node:child_process";
@@ -29427,8 +30285,8 @@ function bumpDocMetaLastReviewed(content3, gitDate) {
29427
30285
  return replaceDocMetaLastReviewed(content3, gitDate);
29428
30286
  }
29429
30287
  function docMetaFixForPath(ctx, relPath2) {
29430
- const abs = join9(ctx.root, relPath2);
29431
- if (!existsSync10(abs))
30288
+ const abs = join8(ctx.root, relPath2);
30289
+ if (!existsSync9(abs))
29432
30290
  return null;
29433
30291
  const content3 = readFileSync8(abs, "utf8");
29434
30292
  if (!DOC_META_RE.test(content3))
@@ -29461,34 +30319,74 @@ function collectDocMetaFixes(ctx) {
29461
30319
  return edits;
29462
30320
  }
29463
30321
 
30322
+ // src/audit/fix/ssot.ts
30323
+ import { readFileSync as readFileSync9 } from "node:fs";
30324
+ import { join as join9 } from "node:path";
30325
+ function collectSsotFixes(ctx) {
30326
+ const edits = [];
30327
+ for (const entry of ctx.ssotEntries) {
30328
+ if (entry.form !== "legacy")
30329
+ continue;
30330
+ const abs = join9(ctx.root, entry.path);
30331
+ const content3 = readFileSync9(abs, "utf8");
30332
+ const next = rewriteLegacySsotToComment(content3);
30333
+ if (!next)
30334
+ continue;
30335
+ edits.push({
30336
+ file: entry.path,
30337
+ description: "rewrite legacy **Source of truth for** banner to <!-- source-of-truth: … -->",
30338
+ content: next
30339
+ });
30340
+ }
30341
+ return edits;
30342
+ }
30343
+
29464
30344
  // src/audit/core/fix.ts
29465
30345
  function collectFixes(ctx, kinds) {
29466
30346
  const meta = kinds.has("doc-meta") ? collectDocMetaFixes(ctx) : [];
29467
30347
  const anchors = kinds.has("anchors") ? collectAnchorFixes(ctx) : [];
29468
- return coalesceFixEdits(meta, anchors);
30348
+ const ssot = kinds.has("ssot") ? collectSsotFixes(ctx) : [];
30349
+ return coalesceFixEdits(meta, anchors, ssot);
29469
30350
  }
29470
- function coalesceFixEdits(metaEdits, anchorEdits) {
30351
+ function coalesceFixEdits(metaEdits, anchorEdits, ssotEdits = []) {
29471
30352
  const metaByFile = new Map(metaEdits.map((e) => [e.file, e]));
29472
30353
  const anchorByFile = new Map(anchorEdits.map((e) => [e.file, e]));
29473
- const files = new Set([...metaByFile.keys(), ...anchorByFile.keys()]);
30354
+ const ssotByFile = new Map(ssotEdits.map((e) => [e.file, e]));
30355
+ const files = new Set([...metaByFile.keys(), ...anchorByFile.keys(), ...ssotByFile.keys()]);
29474
30356
  const out = [];
29475
30357
  for (const file of [...files].sort()) {
29476
- const meta = metaByFile.get(file);
29477
- const anchors = anchorByFile.get(file);
29478
- if (meta && anchors) {
29479
- out.push({
29480
- file,
29481
- description: `${meta.description}; ${anchors.description}`,
29482
- content: overlayLastReviewed(anchors.content, meta.content)
29483
- });
29484
- } else if (meta) {
29485
- out.push(meta);
29486
- } else if (anchors) {
29487
- out.push(anchors);
29488
- }
30358
+ out.push(coalesceOneFile({
30359
+ file,
30360
+ meta: metaByFile.get(file),
30361
+ anchors: anchorByFile.get(file),
30362
+ ssot: ssotByFile.get(file)
30363
+ }));
29489
30364
  }
29490
30365
  return out;
29491
30366
  }
30367
+ function coalesceOneFile(input) {
30368
+ const { file, meta, anchors, ssot } = input;
30369
+ const descriptions = [];
30370
+ let content3 = anchors?.content ?? meta?.content ?? ssot?.content ?? "";
30371
+ if (meta)
30372
+ descriptions.push(meta.description);
30373
+ if (anchors) {
30374
+ descriptions.push(anchors.description);
30375
+ content3 = anchors.content;
30376
+ if (meta)
30377
+ content3 = overlayLastReviewed(content3, meta.content);
30378
+ } else if (meta) {
30379
+ content3 = meta.content;
30380
+ }
30381
+ if (ssot) {
30382
+ descriptions.push(ssot.description);
30383
+ content3 = rewriteSsotOnto(content3, ssot.content);
30384
+ }
30385
+ return { file, description: descriptions.join("; "), content: content3 };
30386
+ }
30387
+ function rewriteSsotOnto(base, ssotContent) {
30388
+ return ssotContent || base;
30389
+ }
29492
30390
  function overlayLastReviewed(targetContent, metaContent) {
29493
30391
  const date = docMetaLastReviewed(metaContent);
29494
30392
  if (!date)
@@ -29502,9 +30400,9 @@ function shouldStopPathWalk(rootResolved, parent, cursor) {
29502
30400
  return parent === cursor || !underRoot(rootResolved, parent) && parent !== rootResolved;
29503
30401
  }
29504
30402
  function resolveExistingRealPath(rootResolved, abs, relFile) {
29505
- const rootReal = existsSync11(rootResolved) ? realpathSync5(rootResolved) : rootResolved;
30403
+ const rootReal = existsSync10(rootResolved) ? realpathSync5(rootResolved) : rootResolved;
29506
30404
  let cursor = abs;
29507
- while (!existsSync11(cursor)) {
30405
+ while (!existsSync10(cursor)) {
29508
30406
  const parent = dirname6(cursor);
29509
30407
  if (shouldStopPathWalk(rootResolved, parent, cursor))
29510
30408
  return abs;
@@ -29517,8 +30415,8 @@ function resolveExistingRealPath(rootResolved, abs, relFile) {
29517
30415
  return abs;
29518
30416
  }
29519
30417
  function resolveWritePath(root2, relFile) {
29520
- const rootResolved = resolve6(root2);
29521
- const abs = resolve6(rootResolved, relFile);
30418
+ const rootResolved = resolve5(root2);
30419
+ const abs = resolve5(rootResolved, relFile);
29522
30420
  if (!underRoot(rootResolved, abs)) {
29523
30421
  throw new Error(`Refusing autofix outside repo root: ${relFile}`);
29524
30422
  }
@@ -29545,19 +30443,22 @@ function applyFixes(ctx, options) {
29545
30443
  }
29546
30444
  function parseFixKinds(raw) {
29547
30445
  if (raw === true)
29548
- return ["doc-meta", "anchors"];
30446
+ return ["doc-meta", "anchors", "ssot"];
29549
30447
  switch (raw) {
29550
30448
  case "doc-meta":
29551
30449
  return ["doc-meta"];
29552
30450
  case "anchors":
29553
30451
  return ["anchors"];
30452
+ case "ssot":
30453
+ return ["ssot"];
29554
30454
  default:
29555
- throw new Error(`Unknown --fix kind: ${raw}. Use doc-meta or anchors.`);
30455
+ throw new Error(`Unknown --fix kind: ${raw}. Use doc-meta, anchors, or ssot.`);
29556
30456
  }
29557
30457
  }
29558
30458
  var FIX_KIND_RULE = {
29559
30459
  "doc-meta": "doc-meta",
29560
- anchors: "links"
30460
+ anchors: "links",
30461
+ ssot: "ssot"
29561
30462
  };
29562
30463
  function fixKindsForOnly(kinds, only) {
29563
30464
  if (!only)
@@ -29636,7 +30537,7 @@ function printTextReport(ctx) {
29636
30537
  }
29637
30538
 
29638
30539
  // src/references/check.ts
29639
- import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync10 } from "node:fs";
30540
+ import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
29640
30541
  import { join as join11, relative as relative7 } from "node:path";
29641
30542
 
29642
30543
  // src/references/constants.ts
@@ -29660,11 +30561,11 @@ function isGeneratedReference(content3) {
29660
30561
  }
29661
30562
 
29662
30563
  // src/references/discover.ts
29663
- import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
30564
+ import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync10 } from "node:fs";
29664
30565
  import { join as join10, relative as relative6 } from "node:path";
29665
30566
  function walkMarkdownFiles(dir, root2) {
29666
30567
  const files = [];
29667
- if (!existsSync12(dir))
30568
+ if (!existsSync11(dir))
29668
30569
  return files;
29669
30570
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
29670
30571
  if (entry.name.startsWith("."))
@@ -29681,7 +30582,7 @@ function walkMarkdownFiles(dir, root2) {
29681
30582
  return files;
29682
30583
  }
29683
30584
  function canonicalExists(root2, refPath) {
29684
- return existsSync12(join10(root2, CANONICAL_REFS_DIR, refPath));
30585
+ return existsSync11(join10(root2, CANONICAL_REFS_DIR, refPath));
29685
30586
  }
29686
30587
  function findSharedRefLinks(content3, sourceFile) {
29687
30588
  const links = [];
@@ -29725,7 +30626,7 @@ function findLocalCanonicalLinks(root2, content3, sourceFile) {
29725
30626
  return links;
29726
30627
  }
29727
30628
  function collectLinksForFile(root2, relFile) {
29728
- const content3 = readFileSync9(join10(root2, relFile), "utf8");
30629
+ const content3 = readFileSync10(join10(root2, relFile), "utf8");
29729
30630
  if (isGeneratedReference(content3))
29730
30631
  return [];
29731
30632
  return [
@@ -29740,7 +30641,7 @@ function expandTransitiveRefs(input) {
29740
30641
  const refPath = queue.pop();
29741
30642
  if (!(refPath && canonicalExists(root2, refPath)))
29742
30643
  continue;
29743
- const canonicalContent = readFileSync9(join10(root2, CANONICAL_REFS_DIR, refPath), "utf8");
30644
+ const canonicalContent = readFileSync10(join10(root2, CANONICAL_REFS_DIR, refPath), "utf8");
29744
30645
  const syntheticSource = generatedRefPath(slug2, refPath);
29745
30646
  for (const link2 of findLocalCanonicalLinks(root2, canonicalContent, syntheticSource)) {
29746
30647
  if (refPaths.has(link2.refPath))
@@ -29753,7 +30654,7 @@ function expandTransitiveRefs(input) {
29753
30654
  }
29754
30655
  function planForSkill(root2, slug2) {
29755
30656
  const skillDir = join10(root2, slug2);
29756
- if (!existsSync12(join10(skillDir, "SKILL.md")))
30657
+ if (!existsSync11(join10(skillDir, "SKILL.md")))
29757
30658
  return null;
29758
30659
  const refPaths = new Set;
29759
30660
  const links = [];
@@ -29804,7 +30705,7 @@ function rewriteSharedRefLinks(content3, sourceFile, skill) {
29804
30705
 
29805
30706
  // src/references/check.ts
29806
30707
  function walkMarkdown(dir, onFile) {
29807
- if (!existsSync13(dir))
30708
+ if (!existsSync12(dir))
29808
30709
  return;
29809
30710
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
29810
30711
  if (entry.name.startsWith("."))
@@ -29822,7 +30723,7 @@ function walkMarkdown(dir, onFile) {
29822
30723
  function listAllGeneratedFiles(root2) {
29823
30724
  const files = [];
29824
30725
  walkMarkdown(root2, (fullPath) => {
29825
- const content3 = readFileSync10(fullPath, "utf8");
30726
+ const content3 = readFileSync11(fullPath, "utf8");
29826
30727
  if (isGeneratedReference(content3)) {
29827
30728
  files.push(normalizeRelPath(relative7(root2, fullPath)));
29828
30729
  }
@@ -29831,20 +30732,20 @@ function listAllGeneratedFiles(root2) {
29831
30732
  }
29832
30733
  function checkNeededCopy(root2, targetRel) {
29833
30734
  const targetPath = join11(root2, targetRel);
29834
- if (!existsSync13(targetPath)) {
30735
+ if (!existsSync12(targetPath)) {
29835
30736
  return issue("generated-references", targetRel, "missing generated copy — run skeleton references sync");
29836
30737
  }
29837
- const generated = readFileSync10(targetPath, "utf8");
30738
+ const generated = readFileSync11(targetPath, "utf8");
29838
30739
  if (!isGeneratedReference(generated)) {
29839
30740
  return issue("generated-references", targetRel, "expected generated-reference provenance header");
29840
30741
  }
29841
30742
  const body = stripGeneratedHeader(generated);
29842
30743
  const sourceRel = normalizeRelPath(generated.match(/source: ([^\n]+)/)?.[1] ?? join11(CANONICAL_REFS_DIR, targetRel.split("/references/")[1] ?? ""));
29843
30744
  const canonicalPath = join11(root2, sourceRel);
29844
- if (!existsSync13(canonicalPath)) {
30745
+ if (!existsSync12(canonicalPath)) {
29845
30746
  return issue("generated-references", targetRel, `canonical source missing: ${sourceRel}`);
29846
30747
  }
29847
- const canonical = readFileSync10(canonicalPath, "utf8");
30748
+ const canonical = readFileSync11(canonicalPath, "utf8");
29848
30749
  if (body !== canonical) {
29849
30750
  return issue("generated-references", targetRel, "stale generated copy — run skeleton references sync");
29850
30751
  }
@@ -29865,7 +30766,7 @@ function checkStaleSharedLinks(root2, skillDir) {
29865
30766
  const issues = [];
29866
30767
  walkMarkdown(skillDir, (fullPath) => {
29867
30768
  const relFile = normalizeRelPath(relative7(root2, fullPath));
29868
- const content3 = readFileSync10(fullPath, "utf8");
30769
+ const content3 = readFileSync11(fullPath, "utf8");
29869
30770
  if (!content3.match(SHARED_REF_LINK_RE))
29870
30771
  return;
29871
30772
  issues.push(issue("generated-references", relFile, "still links to shared root references/ — run skeleton references sync"));
@@ -29875,7 +30776,7 @@ function checkStaleSharedLinks(root2, skillDir) {
29875
30776
  function runGeneratedReferencesCheck(root2, ownership) {
29876
30777
  const issues = [];
29877
30778
  const canonicalDir = join11(root2, CANONICAL_REFS_DIR);
29878
- if (!existsSync13(canonicalDir))
30779
+ if (!existsSync12(canonicalDir))
29879
30780
  return issues;
29880
30781
  const skillIndex = buildSkillIndex(root2, ownership);
29881
30782
  const plans = discoverSkillReferencePlans(root2, ownership);
@@ -29910,14 +30811,14 @@ function runBannedRule(ctx) {
29910
30811
  const issues = [];
29911
30812
  for (const abs of collectBannedFiles(ctx.config, ctx.root)) {
29912
30813
  const rel = relPath(abs, ctx.root);
29913
- issues.push(issue("banned", rel, "file matches scan.banned — must not exist in repo"));
30814
+ issues.push(issue("banned", rel, "file matches deny.paths — must not exist in repo"));
29914
30815
  }
29915
30816
  return issues;
29916
30817
  }
29917
30818
  var bannedRule = { id: "banned", run: runBannedRule };
29918
30819
 
29919
30820
  // src/audit/rules/doc-meta.ts
29920
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
30821
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
29921
30822
  import { join as join12 } from "node:path";
29922
30823
  function checkDocMetaBanner(relPath2, content3) {
29923
30824
  if (DOC_META_RE.test(content3))
@@ -29969,9 +30870,9 @@ function runDocMetaRule(ctx) {
29969
30870
  const today = new Date;
29970
30871
  for (const relPath2 of ctx.docMetaPaths) {
29971
30872
  const abs = join12(ctx.root, relPath2);
29972
- if (!existsSync14(abs))
30873
+ if (!existsSync13(abs))
29973
30874
  continue;
29974
- const content3 = readFileSync11(abs, "utf8");
30875
+ const content3 = readFileSync12(abs, "utf8");
29975
30876
  const banner = checkDocMetaBanner(relPath2, content3);
29976
30877
  if (banner) {
29977
30878
  issues.push(banner);
@@ -29999,23 +30900,13 @@ function runDocMetaRule(ctx) {
29999
30900
  var docMetaRule = { id: "doc-meta", run: runDocMetaRule };
30000
30901
 
30001
30902
  // src/audit/rules/links.ts
30002
- import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
30003
- import { dirname as dirname7, resolve as resolve7 } from "node:path";
30903
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "node:fs";
30904
+ import { dirname as dirname7, resolve as resolve6 } from "node:path";
30004
30905
  function resolveLink2(sourceFile, target) {
30005
30906
  const withoutAnchor = target.split("#")[0]?.split("?")[0] ?? "";
30006
30907
  if (!withoutAnchor)
30007
30908
  return sourceFile;
30008
- return resolve7(dirname7(sourceFile), withoutAnchor);
30009
- }
30010
- function checkRetiredSkill(input) {
30011
- const skillMatch = SKILL_LINK_IN_TARGET_RE.exec(input.target);
30012
- const slug2 = skillMatch?.[1];
30013
- if (!(slug2 && input.ctx.retiredSkills.has(slug2)))
30014
- return null;
30015
- return issue("links", relPath(input.sourceFile, input.ctx.root), {
30016
- message: `references retired skill "${slug2}/SKILL.md"`,
30017
- link: input.linkLabel
30018
- });
30909
+ return resolve6(dirname7(sourceFile), withoutAnchor);
30019
30910
  }
30020
30911
  function checkMissingSkill(input, relSource) {
30021
30912
  if (!input.target.includes("/SKILL.md"))
@@ -30033,13 +30924,13 @@ function checkAgentFile(input, resolved, relSource) {
30033
30924
  return null;
30034
30925
  }
30035
30926
  const agentPath = resolved.endsWith(".md") ? resolved : `${resolved}.md`;
30036
- if (existsSync15(agentPath))
30927
+ if (existsSync14(agentPath))
30037
30928
  return null;
30038
30929
  return issue("links", relSource, { message: "missing agent file", link: input.linkLabel });
30039
30930
  }
30040
30931
  function checkBrokenPath(ctx) {
30041
30932
  const { input, pathPart, resolved, relSource, relTarget } = ctx;
30042
- if (!(pathPart && !existsSync15(resolved)))
30933
+ if (!(pathPart && !existsSync14(resolved)))
30043
30934
  return null;
30044
30935
  return issue("links", relSource, {
30045
30936
  message: `broken link → ${relTarget}`,
@@ -30048,9 +30939,9 @@ function checkBrokenPath(ctx) {
30048
30939
  }
30049
30940
  function checkBrokenAnchor(ctx) {
30050
30941
  const { input, anchor, resolved, relSource, relTarget } = ctx;
30051
- if (!(anchor && existsSync15(resolved)))
30942
+ if (!(anchor && existsSync14(resolved)))
30052
30943
  return null;
30053
- const targetContent = readFileSync12(resolved, "utf8");
30944
+ const targetContent = readFileSync13(resolved, "utf8");
30054
30945
  const slugs = extractHeadingSlugs(targetContent, resolved);
30055
30946
  const anchorSlug = slugifyAnchor(anchor);
30056
30947
  if (slugs.has(anchorSlug))
@@ -30075,9 +30966,6 @@ function validateTarget(input) {
30075
30966
  if (isPlaceholderLink(target))
30076
30967
  return [];
30077
30968
  const parts = resolveTargetParts(sourceFile, target, ctx.root);
30078
- const retired = checkRetiredSkill(input);
30079
- if (retired)
30080
- return [retired];
30081
30969
  const missingSkill = checkMissingSkill(input, parts.relSource);
30082
30970
  if (missingSkill)
30083
30971
  return [missingSkill];
@@ -30104,6 +30992,331 @@ function runLinksRule(ctx) {
30104
30992
  }
30105
30993
  var linksRule = { id: "links", run: runLinksRule };
30106
30994
 
30995
+ // src/audit/rules/near-duplicate.ts
30996
+ import { readFileSync as readFileSync14 } from "node:fs";
30997
+ import { join as join13 } from "node:path";
30998
+
30999
+ // src/audit/core/ssot-fit.ts
31000
+ var DEFAULT_SSOT_OVERLAP_MIN = 0.35;
31001
+ var DEFAULT_BETTER_MATCH_MARGIN = 0.15;
31002
+ var STOP = new Set([
31003
+ "a",
31004
+ "an",
31005
+ "the",
31006
+ "and",
31007
+ "or",
31008
+ "of",
31009
+ "for",
31010
+ "to",
31011
+ "in",
31012
+ "on",
31013
+ "with",
31014
+ "this",
31015
+ "that",
31016
+ "is",
31017
+ "are",
31018
+ "be",
31019
+ "as",
31020
+ "by",
31021
+ "from",
31022
+ "at",
31023
+ "it",
31024
+ "its"
31025
+ ]);
31026
+ var STEM_BLOCKLIST = new Set([
31027
+ "business",
31028
+ "analysis",
31029
+ "status",
31030
+ "process",
31031
+ "access",
31032
+ "address",
31033
+ "series",
31034
+ "species",
31035
+ "news",
31036
+ "means",
31037
+ "cross",
31038
+ "class",
31039
+ "glass",
31040
+ "less",
31041
+ "success",
31042
+ "progress",
31043
+ "express",
31044
+ "discuss",
31045
+ "focus",
31046
+ "bonus",
31047
+ "basis",
31048
+ "crisis",
31049
+ "thesis",
31050
+ "atlas",
31051
+ "canvas",
31052
+ "campus",
31053
+ "virus",
31054
+ "bus",
31055
+ "gas",
31056
+ "plus",
31057
+ "alias",
31058
+ "bias",
31059
+ "circus",
31060
+ "consensus",
31061
+ "census"
31062
+ ]);
31063
+ function lightStem(token) {
31064
+ if (token.length < 4)
31065
+ return token;
31066
+ if (STEM_BLOCKLIST.has(token))
31067
+ return token;
31068
+ if (token.endsWith("ies") && token.length > 4) {
31069
+ return `${token.slice(0, -3)}y`;
31070
+ }
31071
+ if (token.endsWith("sses") || token.endsWith("ches") || token.endsWith("shes") || token.endsWith("xes")) {
31072
+ return token.slice(0, -2);
31073
+ }
31074
+ if (token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
31075
+ return token.slice(0, -1);
31076
+ }
31077
+ return token;
31078
+ }
31079
+ function contentTokens(text5) {
31080
+ return text5.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length > 1 && !STOP.has(t)).map(lightStem);
31081
+ }
31082
+ function uniqueContentTokens(text5) {
31083
+ return [...new Set(contentTokens(text5))];
31084
+ }
31085
+ function stripCode2(content3) {
31086
+ return content3.replace(/```[\s\S]*?```/g, `
31087
+ `).replace(/`[^`\n]+`/g, " ");
31088
+ }
31089
+ function stripSsotAndMeta(content3) {
31090
+ return content3.replace(/<!--\s*source-of-truth:[\s\S]*?-->/gi, `
31091
+ `).replace(/^\s*source-of-truth:\s*.+$/gim, `
31092
+ `).replace(/^\s*\*\*Source of truth for\*\*\s*.+$/gim, `
31093
+ `).replace(/<!--\s*doc-meta:[\s\S]*?-->/gi, `
31094
+ `);
31095
+ }
31096
+ function extractH1(content3) {
31097
+ const prose = stripCode2(content3);
31098
+ const m = /^#\s+(.+)$/m.exec(prose);
31099
+ return m?.[1]?.trim() ?? "";
31100
+ }
31101
+ function extractLeadParagraph(content3) {
31102
+ const prose = stripSsotAndMeta(stripCode2(content3));
31103
+ const lines = prose.split(/\n/);
31104
+ const chunks = [];
31105
+ let buf = [];
31106
+ const flush = () => {
31107
+ const t = buf.join(" ").trim();
31108
+ if (t)
31109
+ chunks.push(t);
31110
+ buf = [];
31111
+ };
31112
+ for (const line of lines) {
31113
+ const trimmed = line.trim();
31114
+ if (!trimmed || trimmed.startsWith("#")) {
31115
+ flush();
31116
+ continue;
31117
+ }
31118
+ if (/^[-*|]/.test(trimmed) && chunks.length === 0 && buf.length === 0) {
31119
+ buf.push(trimmed.replace(/^[-*|]+\s*/, ""));
31120
+ continue;
31121
+ }
31122
+ buf.push(trimmed);
31123
+ }
31124
+ flush();
31125
+ return chunks[0] ?? "";
31126
+ }
31127
+ function buildEvidenceText(content3) {
31128
+ const h1 = extractH1(content3);
31129
+ const lead = extractLeadParagraph(content3);
31130
+ const body = stripSsotAndMeta(stripCode2(content3));
31131
+ return [h1, lead, body].filter(Boolean).join(`
31132
+
31133
+ `);
31134
+ }
31135
+ function ssotEvidenceOverlap(summary, evidence) {
31136
+ const st = uniqueContentTokens(summary);
31137
+ if (st.length === 0)
31138
+ return 0;
31139
+ const ev = new Set(contentTokens(evidence));
31140
+ let hit = 0;
31141
+ for (const t of st) {
31142
+ if (ev.has(t))
31143
+ hit++;
31144
+ }
31145
+ return hit / st.length;
31146
+ }
31147
+ function longestSummaryPhrase(summary) {
31148
+ const toks = contentTokens(summary);
31149
+ if (toks.length >= 3)
31150
+ return toks.slice(0, 3);
31151
+ if (toks.length >= 2)
31152
+ return toks.slice(0, 2);
31153
+ return null;
31154
+ }
31155
+ function evidenceHasPhrase(evidence, phrase) {
31156
+ if (phrase.length === 0)
31157
+ return true;
31158
+ const ev = contentTokens(evidence);
31159
+ const needle = phrase.join(" ");
31160
+ for (let i = 0;i <= ev.length - phrase.length; i++) {
31161
+ if (ev.slice(i, i + phrase.length).join(" ") === needle)
31162
+ return true;
31163
+ }
31164
+ return false;
31165
+ }
31166
+ function evaluateSsotFit(files, options = {}) {
31167
+ const overlapMin = options.overlapMin ?? DEFAULT_SSOT_OVERLAP_MIN;
31168
+ const margin = options.betterMatchMargin ?? DEFAULT_BETTER_MATCH_MARGIN;
31169
+ const phraseCheck = options.phraseCheck !== false;
31170
+ const prepared = files.map((f) => {
31171
+ const evidence = buildEvidenceText(f.content);
31172
+ const overlap = ssotEvidenceOverlap(f.summary, evidence);
31173
+ const summaryToks = uniqueContentTokens(f.summary);
31174
+ return { ...f, evidence, overlap, summaryToks };
31175
+ });
31176
+ const issues = [];
31177
+ for (const row of prepared) {
31178
+ if (row.summaryToks.length < 2) {
31179
+ issues.push({
31180
+ kind: "short",
31181
+ path: row.path,
31182
+ message: `source-of-truth summary too short to verify against body ("${row.summary}")`
31183
+ });
31184
+ continue;
31185
+ }
31186
+ if (row.overlap < overlapMin) {
31187
+ let message = `source-of-truth summary weakly matches this paper (token overlap ${row.overlap.toFixed(2)} < ${overlapMin})`;
31188
+ if (phraseCheck) {
31189
+ const phrase = longestSummaryPhrase(row.summary);
31190
+ if (phrase && !evidenceHasPhrase(row.evidence, phrase)) {
31191
+ message += ` — key phrase "${phrase.join(" ")}" not found in H1/lead/body`;
31192
+ }
31193
+ }
31194
+ issues.push({ kind: "weak", path: row.path, message });
31195
+ let best = null;
31196
+ for (const other of prepared) {
31197
+ if (other.path === row.path)
31198
+ continue;
31199
+ const cross = ssotEvidenceOverlap(row.summary, other.evidence);
31200
+ if (cross < overlapMin)
31201
+ continue;
31202
+ if (cross < row.overlap + margin)
31203
+ continue;
31204
+ if (!best || cross > best.overlap)
31205
+ best = { path: other.path, overlap: cross };
31206
+ }
31207
+ if (best) {
31208
+ issues.push({
31209
+ kind: "better-match",
31210
+ path: row.path,
31211
+ otherPath: best.path,
31212
+ message: `source-of-truth fits ${best.path} better (overlap ${best.overlap.toFixed(2)} vs own ${row.overlap.toFixed(2)}). ` + `Try: (1) rewrite this SSOT to match this paper, (2) move/fix the marker onto ${best.path}, ` + `or (3) if these pages are really one topic, consider combining them`
31213
+ });
31214
+ }
31215
+ }
31216
+ }
31217
+ return issues;
31218
+ }
31219
+
31220
+ // src/audit/rules/near-duplicate.ts
31221
+ var DEFAULT_THRESHOLD = 0.72;
31222
+ var SHINGLE_N = 3;
31223
+ function tokenize2(text5) {
31224
+ return contentTokens(text5);
31225
+ }
31226
+ function normalizeSummary(summary) {
31227
+ return contentTokens(summary).join(" ");
31228
+ }
31229
+ function shingles(tokens, n) {
31230
+ const out = new Set;
31231
+ if (tokens.length < n) {
31232
+ if (tokens.length > 0)
31233
+ out.add(tokens.join(" "));
31234
+ return out;
31235
+ }
31236
+ for (let i = 0;i <= tokens.length - n; i++) {
31237
+ out.add(tokens.slice(i, i + n).join(" "));
31238
+ }
31239
+ return out;
31240
+ }
31241
+ function jaccard2(a, b) {
31242
+ if (a.size === 0 && b.size === 0)
31243
+ return 1;
31244
+ if (a.size === 0 || b.size === 0)
31245
+ return 0;
31246
+ let inter = 0;
31247
+ for (const x of a) {
31248
+ if (b.has(x))
31249
+ inter++;
31250
+ }
31251
+ return inter / (a.size + b.size - inter);
31252
+ }
31253
+ function pairKey(a, b) {
31254
+ return a < b ? `${a}::${b}` : `${b}::${a}`;
31255
+ }
31256
+ function ignoredPairSet(ctx) {
31257
+ const pairs = ctx.config.docsLint?.ignorePairs ?? [];
31258
+ const set = new Set;
31259
+ for (const pair of pairs) {
31260
+ if (!pair || pair.length < 2)
31261
+ continue;
31262
+ const a = normalizeRelPath(pair[0] ?? "");
31263
+ const b = normalizeRelPath(pair[1] ?? "");
31264
+ if (a && b)
31265
+ set.add(pairKey(a, b));
31266
+ }
31267
+ return set;
31268
+ }
31269
+ function isIgnoredGlob(rel, globs) {
31270
+ return globs.some((g) => matchesGlobScope(rel, g));
31271
+ }
31272
+ function bodyWithoutSsotNoise(content3) {
31273
+ return content3.replace(/<!--\s*source-of-truth:[\s\S]*?-->/gi, " ").replace(/^\s*source-of-truth:\s*.+$/gim, " ").replace(/^\s*\*\*Source of truth for\*\*\s*.+$/gim, " ");
31274
+ }
31275
+ function eligibleEntries(ctx) {
31276
+ const globs = ctx.config.docsLint?.ignoreGlobs ?? [];
31277
+ return ctx.ssotEntries.filter((e) => !isIgnoredGlob(e.path, globs));
31278
+ }
31279
+ function runNearDuplicateRule(ctx) {
31280
+ const issues = [];
31281
+ const threshold = ctx.config.docsLint?.nearDuplicateThreshold ?? DEFAULT_THRESHOLD;
31282
+ const ignored = ignoredPairSet(ctx);
31283
+ const entries = eligibleEntries(ctx);
31284
+ const fingerprints = entries.map((e) => {
31285
+ const content3 = readFileSync14(join13(ctx.root, e.path), "utf8");
31286
+ const tokens = tokenize2(bodyWithoutSsotNoise(content3));
31287
+ return {
31288
+ path: e.path,
31289
+ summaryKey: normalizeSummary(e.summary),
31290
+ set: shingles(tokens, SHINGLE_N)
31291
+ };
31292
+ });
31293
+ for (let i = 0;i < fingerprints.length; i++) {
31294
+ for (let j = i + 1;j < fingerprints.length; j++) {
31295
+ const a = fingerprints[i];
31296
+ const b = fingerprints[j];
31297
+ if (!(a && b))
31298
+ continue;
31299
+ if (ignored.has(pairKey(a.path, b.path)))
31300
+ continue;
31301
+ const score = jaccard2(a.set, b.set);
31302
+ if (score >= threshold) {
31303
+ issues.push(issue("near-duplicate", a.path, {
31304
+ message: `near-duplicate of ${b.path} (shingle Jaccard ${score.toFixed(2)} ≥ ${threshold})`,
31305
+ severity: "warning"
31306
+ }));
31307
+ }
31308
+ if (a.summaryKey && a.summaryKey === b.summaryKey) {
31309
+ issues.push(issue("near-duplicate", a.path, {
31310
+ message: `duplicate source-of-truth summary also used by ${b.path}`,
31311
+ severity: "warning"
31312
+ }));
31313
+ }
31314
+ }
31315
+ }
31316
+ return issues;
31317
+ }
31318
+ var nearDuplicateRule = { id: "near-duplicate", run: runNearDuplicateRule };
31319
+
30107
31320
  // src/audit/rules/prose-policy.ts
30108
31321
  function checkDraftEntry(input) {
30109
31322
  const { rel, lines, entry, draftPrefixes } = input;
@@ -30164,53 +31377,6 @@ function runProsePolicyRule(ctx) {
30164
31377
  }
30165
31378
  var prosePolicyRule = { id: "prose-policy", run: runProsePolicyRule };
30166
31379
 
30167
- // src/audit/rules/registry.ts
30168
- import { existsSync as existsSync16, readFileSync as readFileSync13 } from "node:fs";
30169
- import { join as join13 } from "node:path";
30170
- function checkRegistryEntries(ctx) {
30171
- const issues = [];
30172
- for (const rel of ctx.registryPaths) {
30173
- const abs = join13(ctx.root, rel);
30174
- if (!existsSync16(abs)) {
30175
- issues.push(issue("registry", rel, "registry entry file missing"));
30176
- continue;
30177
- }
30178
- const content3 = readFileSync13(abs, "utf8");
30179
- if (!SOURCE_OF_TRUTH_BANNER_RE.test(content3)) {
30180
- issues.push(issue("registry", rel, "missing **Source of truth for** banner (required for registry entry)"));
30181
- }
30182
- }
30183
- return issues;
30184
- }
30185
- function checkUnregisteredBanners(ctx, registry) {
30186
- const issues = [];
30187
- for (const filePath of ctx.files) {
30188
- const rel = relPath(filePath, ctx.root);
30189
- if (rel === REGISTRY_REL_PATH)
30190
- continue;
30191
- if (!(rel.endsWith(".md") || rel.endsWith(".mdc")))
30192
- continue;
30193
- const content3 = readFileSync13(filePath, "utf8");
30194
- if (!SOURCE_OF_TRUTH_BANNER_LINE_RE.test(content3))
30195
- continue;
30196
- if (!registry.has(rel)) {
30197
- issues.push(issue("registry", rel, "has **Source of truth for** banner but is not in .skeleton/registry.md — add registry row or remove banner"));
30198
- }
30199
- }
30200
- return issues;
30201
- }
30202
- function runRegistryRule(ctx) {
30203
- const issues = [];
30204
- const registry = new Set(ctx.registryPaths);
30205
- if (ctx.registryHasTableHeader && ctx.registryPaths.length === 0) {
30206
- issues.push(issue("registry", REGISTRY_REL_PATH, "registry table header found but 0 rows parsed — check | Topic | Canonical file | format and link syntax"));
30207
- }
30208
- issues.push(...checkRegistryEntries(ctx));
30209
- issues.push(...checkUnregisteredBanners(ctx, registry));
30210
- return issues;
30211
- }
30212
- var registryRule = { id: "registry", run: runRegistryRule };
30213
-
30214
31380
  // src/audit/rules/scan-gaps.ts
30215
31381
  function runCoverageGapsRule(ctx) {
30216
31382
  const exclude = [...COVERAGE_BUILTIN_EXCLUDES, ...ctx.config.scan.exclude];
@@ -30221,7 +31387,7 @@ function runCoverageGapsRule(ctx) {
30221
31387
  if (scanned.has(rel))
30222
31388
  continue;
30223
31389
  issues.push(issue("coverage-gaps", rel, {
30224
- message: "markdown outside audit scan perimeter — extend .skeleton/config.yaml scan.include",
31390
+ message: "markdown outside audit scan perimeter — extend skeleton.toml (or legacy .skeleton/config.yaml) scan.include",
30225
31391
  severity: "warning"
30226
31392
  }));
30227
31393
  }
@@ -30240,11 +31406,11 @@ function runScanRootsRule(ctx) {
30240
31406
  var scanRootsRule = { id: "scan-roots", run: runScanRootsRule };
30241
31407
 
30242
31408
  // src/audit/rules/skill-index.ts
30243
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "node:fs";
31409
+ import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync15 } from "node:fs";
30244
31410
  import { join as join14, relative as relative8 } from "node:path";
30245
31411
  function walkSkillMarkdown(dir) {
30246
31412
  const files = [];
30247
- if (!existsSync17(dir))
31413
+ if (!existsSync15(dir))
30248
31414
  return files;
30249
31415
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
30250
31416
  if (entry.name.startsWith("."))
@@ -30276,17 +31442,13 @@ function parseReadmeTaxonomySlugs(content3) {
30276
31442
  function scanFileForSkillLinks(ctx, filePath, index2) {
30277
31443
  const issues = [];
30278
31444
  const rel = relative8(ctx.root, filePath).replace(/\\/g, "/");
30279
- const content3 = readFileSync14(filePath, "utf8");
31445
+ const content3 = readFileSync15(filePath, "utf8");
30280
31446
  if (isGeneratedReference(content3))
30281
31447
  return issues;
30282
31448
  for (const match of content3.matchAll(SKILL_LINK_RE)) {
30283
31449
  const slug2 = match[1];
30284
31450
  if (!slug2)
30285
31451
  continue;
30286
- if (ctx.retiredSkills.has(slug2)) {
30287
- issues.push(issue("skill-index", rel, `references retired skill "${slug2}/SKILL.md"`));
30288
- continue;
30289
- }
30290
31452
  if (!resolveSkillPath(index2, ctx.root, slug2)) {
30291
31453
  issues.push(issue("skill-index", rel, `links missing skill "${slug2}/SKILL.md"`));
30292
31454
  }
@@ -30296,13 +31458,13 @@ function scanFileForSkillLinks(ctx, filePath, index2) {
30296
31458
  function taxonomyIssuesForReadme(input) {
30297
31459
  const { ctx, index: index2, skillRoot, diskSlugs, nonPublic } = input;
30298
31460
  const readmePath = join14(ctx.root, skillRoot.relPath, "README.md");
30299
- if (!existsSync17(readmePath))
31461
+ if (!existsSync15(readmePath))
30300
31462
  return [];
30301
- const readme = readFileSync14(readmePath, "utf8");
31463
+ const readme = readFileSync15(readmePath, "utf8");
30302
31464
  if (!readme.includes("## Taxonomy"))
30303
31465
  return [];
30304
31466
  const taxonomySlugs = parseReadmeTaxonomySlugs(readme);
30305
- const nestedSlugs = diskSlugs.filter((slug2) => existsSync17(join14(ctx.root, skillRoot.relPath, slug2, "SKILL.md")));
31467
+ const nestedSlugs = diskSlugs.filter((slug2) => existsSync15(join14(ctx.root, skillRoot.relPath, slug2, "SKILL.md")));
30306
31468
  const foreign = new Set(index2.foreignSlugs);
30307
31469
  const publicSlugs = nestedSlugs.filter((slug2) => !(nonPublic.has(slug2) || foreign.has(slug2)));
30308
31470
  const relReadme = `${skillRoot.relPath}/README.md`;
@@ -30338,7 +31500,7 @@ function auditSkillRoot(input) {
30338
31500
  const base = skillRoot.kind === "nested" ? join14(ctx.root, skillRoot.relPath) : ctx.root;
30339
31501
  for (const slug2 of slugsForRoot(skillRoot, index2, owned)) {
30340
31502
  const skillDir = join14(base, slug2);
30341
- if (!existsSync17(skillDir))
31503
+ if (!existsSync15(skillDir))
30342
31504
  continue;
30343
31505
  for (const skillMd of walkSkillMarkdown(skillDir)) {
30344
31506
  issues.push(...scanFileForSkillLinks(ctx, skillMd, index2));
@@ -30377,10 +31539,46 @@ function skillAuditSuffix(ctx) {
30377
31539
  return ` (${owned} owned skills audited, ${foreign} foreign ignored)`;
30378
31540
  }
30379
31541
 
31542
+ // src/audit/rules/ssot.ts
31543
+ function runSsotRule(ctx) {
31544
+ const issues = [];
31545
+ for (const err of ctx.ssotErrors) {
31546
+ issues.push(issue("ssot", err.path, err.detail));
31547
+ }
31548
+ return issues;
31549
+ }
31550
+ var ssotRule = { id: "ssot", run: runSsotRule };
31551
+
31552
+ // src/audit/rules/ssot-summary.ts
31553
+ import { readFileSync as readFileSync16 } from "node:fs";
31554
+ import { join as join15 } from "node:path";
31555
+ function runSsotSummaryRule(ctx) {
31556
+ const overlapMin = ctx.config.docsLint?.ssotOverlapMin ?? DEFAULT_SSOT_OVERLAP_MIN;
31557
+ const margin = ctx.config.docsLint?.ssotBetterMatchMargin ?? DEFAULT_BETTER_MATCH_MARGIN;
31558
+ const phraseCheck = ctx.config.docsLint?.ssotPhraseCheck !== false;
31559
+ const files = ctx.ssotEntries.map((entry) => ({
31560
+ path: entry.path,
31561
+ summary: entry.summary,
31562
+ content: readFileSync16(join15(ctx.root, entry.path), "utf8")
31563
+ }));
31564
+ return evaluateSsotFit(files, {
31565
+ overlapMin,
31566
+ betterMatchMargin: margin,
31567
+ phraseCheck
31568
+ }).map((fit) => issue("ssot-summary", fit.path, {
31569
+ message: fit.message,
31570
+ severity: "warning",
31571
+ link: fit.otherPath
31572
+ }));
31573
+ }
31574
+ var ssotSummaryRule = { id: "ssot-summary", run: runSsotSummaryRule };
31575
+
30380
31576
  // src/audit/rules/index.ts
30381
31577
  var docsRules = [
30382
31578
  { ...scanRootsRule, global: true },
30383
- { ...registryRule, global: true },
31579
+ { ...ssotRule, global: true },
31580
+ { ...nearDuplicateRule, global: true },
31581
+ { ...ssotSummaryRule, global: true },
30384
31582
  { ...coverageGapsRule, global: true },
30385
31583
  linksRule,
30386
31584
  docMetaRule,
@@ -30444,8 +31642,8 @@ function rulesForSuite(suite, pluginRules = []) {
30444
31642
  function parseFixArg(argv, index2) {
30445
31643
  const next = argv[index2 + 1];
30446
31644
  if (next && !next.startsWith("-")) {
30447
- if (next !== "doc-meta" && next !== "anchors") {
30448
- throw new Error(`Unknown --fix kind: ${next}. Use --fix, --fix=doc-meta, or --fix=anchors.`);
31645
+ if (next !== "doc-meta" && next !== "anchors" && next !== "ssot") {
31646
+ throw new Error(`Unknown --fix kind: ${next}. Use --fix, --fix=doc-meta, --fix=anchors, or --fix=ssot.`);
30449
31647
  }
30450
31648
  return { fix: next, nextIndex: index2 + 1 };
30451
31649
  }
@@ -30536,7 +31734,7 @@ async function runAuditFixes(options, ctx, loaded) {
30536
31734
  }
30537
31735
  const kinds = fixKindsForOnly(parseFixKinds(options.fix), options.only);
30538
31736
  if (kinds.length === 0) {
30539
- console.error("--fix has no overlapping rules with --only (doc-meta → doc-meta, anchors → links).");
31737
+ console.error("--fix has no overlapping rules with --only (doc-meta → doc-meta, anchors → links, ssot → ssot).");
30540
31738
  return 1;
30541
31739
  }
30542
31740
  applyFixes(ctx, { kinds, dryRun: options.dryRun });
@@ -30580,41 +31778,89 @@ async function runAudit(options) {
30580
31778
  });
30581
31779
  }
30582
31780
 
31781
+ // src/catalog.ts
31782
+ import { existsSync as existsSync16, mkdirSync, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "node:fs";
31783
+ import { dirname as dirname8, join as join16 } from "node:path";
31784
+ function renderCatalog(entries) {
31785
+ const lines = [
31786
+ "# Skeleton catalog",
31787
+ "",
31788
+ "Generated by `skeleton catalog`. Gitignored — regenerate locally; do not commit.",
31789
+ "",
31790
+ "Agents: skim this list, then read only the SSOT line / first ~20 lines of a candidate before opening the full paper.",
31791
+ "",
31792
+ "| Summary | Path |",
31793
+ "| ------- | ---- |"
31794
+ ];
31795
+ for (const e of entries) {
31796
+ const summary = e.summary.replace(/\|/g, "\\|");
31797
+ lines.push(`| ${summary} | [${e.path}](../${e.path}) |`);
31798
+ }
31799
+ lines.push("");
31800
+ return lines.join(`
31801
+ `);
31802
+ }
31803
+ function buildCatalogContent(root2) {
31804
+ const config = loadConfig(root2);
31805
+ const skillIndex = buildSkillIndex(root2, config.skillOwnership);
31806
+ const files = collectScanFiles(config, root2, skillIndex);
31807
+ const { entries } = collectSsotEntries(files, root2);
31808
+ return { content: renderCatalog(entries), entries };
31809
+ }
31810
+ function checkCatalog(root2) {
31811
+ const { content: expected, entries } = buildCatalogContent(root2);
31812
+ const abs = join16(root2, CATALOG_REL_PATH);
31813
+ if (!existsSync16(abs)) {
31814
+ return { ok: false, missing: true, stale: false, expected, actual: null, entries };
31815
+ }
31816
+ const actual = readFileSync17(abs, "utf8");
31817
+ const stale = actual !== expected;
31818
+ return { ok: !stale, missing: false, stale, expected, actual, entries };
31819
+ }
31820
+ function writeCatalog(root2) {
31821
+ const { content: content3, entries } = buildCatalogContent(root2);
31822
+ const abs = join16(root2, CATALOG_REL_PATH);
31823
+ mkdirSync(dirname8(abs), { recursive: true });
31824
+ writeFileSync2(abs, content3, "utf8");
31825
+ return { path: normalizeRelPath(CATALOG_REL_PATH), entries };
31826
+ }
31827
+ function runCatalogCli(options = {}) {
31828
+ const root2 = options.root ?? findRepoRoot();
31829
+ if (options.check) {
31830
+ const result = checkCatalog(root2);
31831
+ if (result.missing) {
31832
+ console.error(`catalog: missing ${CATALOG_REL_PATH} — run \`skeleton catalog\``);
31833
+ return 0;
31834
+ }
31835
+ if (result.stale) {
31836
+ console.error(`catalog: ${CATALOG_REL_PATH} is outdated — run \`skeleton catalog\``);
31837
+ return 0;
31838
+ }
31839
+ console.log(`catalog: ${CATALOG_REL_PATH} up to date (${result.entries.length} entries)`);
31840
+ return 0;
31841
+ }
31842
+ const written = writeCatalog(root2);
31843
+ console.log(`catalog: wrote ${written.path} (${written.entries.length} entries)`);
31844
+ return 0;
31845
+ }
31846
+
30583
31847
  // src/customize/resolve.ts
30584
- import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
30585
- import { basename as basename3, join as join15, relative as relative9 } from "node:path";
30586
- var CUSTOMIZE_PREFIX = "Customize: ";
31848
+ import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
31849
+ import { basename as basename3, join as join17, relative as relative9 } from "node:path";
30587
31850
  function customizeDir(root2) {
30588
- return join15(root2, REGISTRY_DIR_REL, "customize");
31851
+ return join17(root2, REGISTRY_DIR_REL, "customize");
30589
31852
  }
30590
31853
  function customizePathForSlug(root2, slug2) {
30591
- return join15(customizeDir(root2), `${slug2}.md`);
30592
- }
30593
- function findCustomizeViaRegistry(root2, slug2) {
30594
- for (const rel of parseRegistry(root2).paths) {
30595
- const expected = `${REGISTRY_DIR_REL}/customize/${slug2}.md`;
30596
- if (normalizeRelPath(rel) === expected && existsSync18(join15(root2, rel))) {
30597
- return rel;
30598
- }
30599
- }
30600
- return null;
31854
+ return join17(customizeDir(root2), `${slug2}.md`);
30601
31855
  }
30602
31856
  function resolveSlugFile(root2, slug2) {
30603
31857
  const direct = customizePathForSlug(root2, slug2);
30604
- if (existsSync18(direct)) {
31858
+ if (existsSync17(direct)) {
30605
31859
  return {
30606
- content: readFileSync15(direct, "utf8"),
31860
+ content: readFileSync18(direct, "utf8"),
30607
31861
  path: normalizeRelPath(relative9(root2, direct))
30608
31862
  };
30609
31863
  }
30610
- const registryPath = findCustomizeViaRegistry(root2, slug2);
30611
- if (registryPath) {
30612
- const abs = join15(root2, registryPath);
30613
- return {
30614
- content: readFileSync15(abs, "utf8"),
30615
- path: registryPath
30616
- };
30617
- }
30618
31864
  return { content: null, path: null };
30619
31865
  }
30620
31866
  function alwaysIncludeBasenames(root2) {
@@ -30633,10 +31879,10 @@ function readAlwaysInclude(root2, basenames, skipBasename) {
30633
31879
  const file = basename3(name);
30634
31880
  if (skipBasename && file === skipBasename)
30635
31881
  continue;
30636
- const abs = join15(dir, file);
30637
- if (!existsSync18(abs))
31882
+ const abs = join17(dir, file);
31883
+ if (!existsSync17(abs))
30638
31884
  continue;
30639
- parts.push(readFileSync15(abs, "utf8").trimEnd());
31885
+ parts.push(readFileSync18(abs, "utf8").trimEnd());
30640
31886
  paths.push(normalizeRelPath(relative9(root2, abs)));
30641
31887
  }
30642
31888
  return { parts, paths };
@@ -30760,39 +32006,39 @@ Customize override for /${slug2} (from ${from}):
30760
32006
 
30761
32007
  // src/init/init.ts
30762
32008
  import { spawnSync as spawnSync2 } from "node:child_process";
30763
- import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync2, readFileSync as readFileSync17 } from "node:fs";
30764
- import { join as join19 } from "node:path";
32009
+ import { copyFileSync, existsSync as existsSync21, mkdirSync as mkdirSync3, readFileSync as readFileSync20 } from "node:fs";
32010
+ import { join as join21 } from "node:path";
30765
32011
  import process4 from "node:process";
30766
32012
 
30767
32013
  // src/init/merge-hooks.ts
30768
- import { existsSync as existsSync21, mkdirSync, readFileSync as readFileSync16, writeFileSync as writeFileSync2 } from "node:fs";
30769
- import { dirname as dirname10, join as join18 } from "node:path";
32014
+ import { existsSync as existsSync20, mkdirSync as mkdirSync2, readFileSync as readFileSync19, writeFileSync as writeFileSync3 } from "node:fs";
32015
+ import { dirname as dirname11, join as join20 } from "node:path";
30770
32016
 
30771
32017
  // src/init/package-paths.ts
30772
- import { existsSync as existsSync19 } from "node:fs";
30773
- import { dirname as dirname8, join as join16 } from "node:path";
32018
+ import { existsSync as existsSync18 } from "node:fs";
32019
+ import { dirname as dirname9, join as join18 } from "node:path";
30774
32020
  import { fileURLToPath as fileURLToPath5 } from "node:url";
30775
- var MODULE_DIR = dirname8(fileURLToPath5(import.meta.url));
30776
- var PACKAGE_ROOT_CANDIDATES = [join16(MODULE_DIR, "../.."), join16(MODULE_DIR, "..")];
32021
+ var MODULE_DIR = dirname9(fileURLToPath5(import.meta.url));
32022
+ var PACKAGE_ROOT_CANDIDATES = [join18(MODULE_DIR, "../.."), join18(MODULE_DIR, "..")];
30777
32023
  function resolvePackageRoot() {
30778
32024
  for (const candidate of PACKAGE_ROOT_CANDIDATES) {
30779
- if (existsSync19(join16(candidate, "package.json")))
32025
+ if (existsSync18(join18(candidate, "package.json")))
30780
32026
  return candidate;
30781
32027
  }
30782
32028
  throw new Error("Could not resolve @csark0812/skeleton package root");
30783
32029
  }
30784
32030
  function resolveTemplatesDir() {
30785
- const dir = join16(resolvePackageRoot(), "templates/skeleton-init");
30786
- if (!existsSync19(dir)) {
32031
+ const dir = join18(resolvePackageRoot(), "templates/skeleton-init");
32032
+ if (!existsSync18(dir)) {
30787
32033
  throw new Error("Missing templates/skeleton-init in package");
30788
32034
  }
30789
32035
  return dir;
30790
32036
  }
30791
32037
 
30792
32038
  // src/init/resolve-hook-command.ts
30793
- import { existsSync as existsSync20, realpathSync as realpathSync6 } from "node:fs";
32039
+ import { existsSync as existsSync19, realpathSync as realpathSync6 } from "node:fs";
30794
32040
  import { createRequire as createRequire3 } from "node:module";
30795
- import { dirname as dirname9, join as join17, relative as relative10, resolve as resolve8 } from "node:path";
32041
+ import { dirname as dirname10, join as join19, relative as relative10, resolve as resolve7 } from "node:path";
30796
32042
  var PACKAGE_NAME = "@csark0812/skeleton";
30797
32043
  var CLI_DIST = "dist/cli.js";
30798
32044
  var PACKAGE_ROOT = resolvePackageRoot();
@@ -30811,7 +32057,7 @@ function toRepoRelative(cwd, absPath) {
30811
32057
  }
30812
32058
  function tryResolvePublishedCli(cwd) {
30813
32059
  try {
30814
- const req = createRequire3(join17(cwd, "package.json"));
32060
+ const req = createRequire3(join19(cwd, "package.json"));
30815
32061
  return req.resolve(`${PACKAGE_NAME}/${CLI_DIST}`);
30816
32062
  } catch {
30817
32063
  return null;
@@ -30820,10 +32066,10 @@ function tryResolvePublishedCli(cwd) {
30820
32066
  function walkNodeModulesCli(cwd) {
30821
32067
  let dir = cwd;
30822
32068
  while (true) {
30823
- const candidate = join17(dir, "node_modules", PACKAGE_NAME, CLI_DIST);
30824
- if (existsSync20(candidate))
32069
+ const candidate = join19(dir, "node_modules", PACKAGE_NAME, CLI_DIST);
32070
+ if (existsSync19(candidate))
30825
32071
  return candidate;
30826
- const parent = dirname9(dir);
32072
+ const parent = dirname10(dir);
30827
32073
  if (parent === dir)
30828
32074
  break;
30829
32075
  dir = parent;
@@ -30831,7 +32077,7 @@ function walkNodeModulesCli(cwd) {
30831
32077
  return null;
30832
32078
  }
30833
32079
  function isInsidePackageRoot(cwd) {
30834
- const rel = relative10(PACKAGE_ROOT, resolve8(cwd)).replace(/\\/g, "/");
32080
+ const rel = relative10(PACKAGE_ROOT, resolve7(cwd)).replace(/\\/g, "/");
30835
32081
  return rel === "" || !(rel.startsWith("..") || rel.startsWith("/"));
30836
32082
  }
30837
32083
  function nodeCliHookCommand(cliPath) {
@@ -30862,21 +32108,21 @@ function identityKey(platform, event, matcher) {
30862
32108
  return `skeleton:customize:${platform}:${event}:${matcher}`;
30863
32109
  }
30864
32110
  function loadFragment(name, hookCommand) {
30865
- const raw = readFileSync16(join18(TEMPLATES_DIR, name), "utf8");
32111
+ const raw = readFileSync19(join20(TEMPLATES_DIR, name), "utf8");
30866
32112
  return JSON.parse(raw.replaceAll("{{HOOK_COMMAND}}", hookCommand));
30867
32113
  }
30868
32114
  function readJson(path2) {
30869
- if (!existsSync21(path2))
32115
+ if (!existsSync20(path2))
30870
32116
  return null;
30871
32117
  try {
30872
- return JSON.parse(readFileSync16(path2, "utf8"));
32118
+ return JSON.parse(readFileSync19(path2, "utf8"));
30873
32119
  } catch (error) {
30874
32120
  throw new Error(`Invalid JSON in ${path2}: ${error}`);
30875
32121
  }
30876
32122
  }
30877
32123
  function writeJson(path2, value) {
30878
- mkdirSync(dirname10(path2), { recursive: true });
30879
- writeFileSync2(path2, `${JSON.stringify(value, null, 2)}
32124
+ mkdirSync2(dirname11(path2), { recursive: true });
32125
+ writeFileSync3(path2, `${JSON.stringify(value, null, 2)}
30880
32126
  `, "utf8");
30881
32127
  }
30882
32128
  function deepEqual(a, b) {
@@ -31029,10 +32275,10 @@ function mergeNestedHooks(args) {
31029
32275
  }
31030
32276
  function mergeHookConfigs(opts) {
31031
32277
  const results = [];
31032
- const cursorPath = join18(opts.cwd, ".cursor/hooks.json");
32278
+ const cursorPath = join20(opts.cwd, ".cursor/hooks.json");
31033
32279
  const cursorFragment = loadFragment("cursor-hooks.fragment.json", opts.hookCommand);
31034
32280
  results.push(mergeCursorHooks(cursorPath, cursorFragment, opts));
31035
- const claudePath = join18(opts.cwd, ".claude/settings.json");
32281
+ const claudePath = join20(opts.cwd, ".claude/settings.json");
31036
32282
  const claudeFragment = loadFragment("claude-settings.fragment.json", opts.hookCommand);
31037
32283
  results.push(mergeNestedHooks({
31038
32284
  platform: "claude",
@@ -31041,8 +32287,8 @@ function mergeHookConfigs(opts) {
31041
32287
  eventName: "PostToolUse",
31042
32288
  opts
31043
32289
  }));
31044
- const codexPath = join18(opts.cwd, ".codex/hooks.json");
31045
- if (existsSync21(join18(opts.cwd, ".codex"))) {
32290
+ const codexPath = join20(opts.cwd, ".codex/hooks.json");
32291
+ if (existsSync20(join20(opts.cwd, ".codex"))) {
31046
32292
  const codexFragment = loadFragment("codex-hooks.fragment.json", opts.hookCommand);
31047
32293
  results.push(mergeNestedHooks({
31048
32294
  platform: "codex",
@@ -31057,11 +32303,11 @@ function mergeHookConfigs(opts) {
31057
32303
  return results;
31058
32304
  }
31059
32305
  function mergePackageJsonScripts(cwd) {
31060
- const pkgPath = join18(cwd, "package.json");
31061
- if (!existsSync21(pkgPath))
32306
+ const pkgPath = join20(cwd, "package.json");
32307
+ if (!existsSync20(pkgPath))
31062
32308
  return "skipped";
31063
- const fragment = JSON.parse(readFileSync16(join18(TEMPLATES_DIR, "package.json.scripts.fragment.json"), "utf8"));
31064
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
32309
+ const fragment = JSON.parse(readFileSync19(join20(TEMPLATES_DIR, "package.json.scripts.fragment.json"), "utf8"));
32310
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
31065
32311
  pkg.scripts ??= {};
31066
32312
  let changed = false;
31067
32313
  for (const [key, value] of Object.entries(fragment)) {
@@ -31072,7 +32318,7 @@ function mergePackageJsonScripts(cwd) {
31072
32318
  }
31073
32319
  if (!changed)
31074
32320
  return "skipped";
31075
- writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
32321
+ writeFileSync3(pkgPath, `${JSON.stringify(pkg, null, 2)}
31076
32322
  `, "utf8");
31077
32323
  return "updated";
31078
32324
  }
@@ -31117,27 +32363,23 @@ function skillsAddArgs(options = {}) {
31117
32363
  // src/init/init.ts
31118
32364
  var TEMPLATES_DIR2 = resolveTemplatesDir();
31119
32365
  function writeScaffold(cwd) {
31120
- const skeletonDir2 = join19(cwd, ".skeleton");
31121
- mkdirSync2(skeletonDir2, { recursive: true });
32366
+ const skeletonDir2 = join21(cwd, ".skeleton");
32367
+ mkdirSync3(skeletonDir2, { recursive: true });
31122
32368
  let created = false;
31123
- const configPath = join19(skeletonDir2, "config.yaml");
31124
- if (!existsSync22(configPath)) {
31125
- copyFileSync(join19(TEMPLATES_DIR2, "config.yaml"), configPath);
31126
- created = true;
31127
- }
31128
- const registryPath = join19(skeletonDir2, "registry.md");
31129
- if (!existsSync22(registryPath)) {
31130
- copyFileSync(join19(TEMPLATES_DIR2, "registry.md"), registryPath);
32369
+ const tomlPath = join21(cwd, "skeleton.toml");
32370
+ const legacyYaml = join21(skeletonDir2, "config.yaml");
32371
+ if (!(existsSync21(tomlPath) || existsSync21(legacyYaml))) {
32372
+ copyFileSync(join21(TEMPLATES_DIR2, "skeleton.toml"), tomlPath);
31131
32373
  created = true;
31132
32374
  }
31133
- mkdirSync2(join19(skeletonDir2, "customize"), { recursive: true });
32375
+ mkdirSync3(join21(skeletonDir2, "customize"), { recursive: true });
31134
32376
  return created ? "created" : "skipped";
31135
32377
  }
31136
32378
  function assertPackageResolvable(cwd) {
31137
- const pkgPath = join19(cwd, "package.json");
31138
- if (!existsSync22(pkgPath))
32379
+ const pkgPath = join21(cwd, "package.json");
32380
+ if (!existsSync21(pkgPath))
31139
32381
  return;
31140
- const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
32382
+ const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
31141
32383
  const hasDep = pkg.devDependencies?.["@csark0812/skeleton"] || pkg.dependencies?.["@csark0812/skeleton"];
31142
32384
  if (!hasDep) {
31143
32385
  try {
@@ -31186,9 +32428,9 @@ function runInit(options = {}) {
31186
32428
  for (const result of hooks)
31187
32429
  logHookMergeResult(result);
31188
32430
  if (scaffold === "created") {
31189
- console.log("init: wrote .skeleton/config.yaml and registry.md");
32431
+ console.log("init: wrote skeleton.toml (hooks optional — see docs)");
31190
32432
  } else {
31191
- console.log("init: .skeleton/ already present — skipped scaffold write");
32433
+ console.log("init: skeleton.toml or .skeleton/ already present — skipped scaffold write");
31192
32434
  }
31193
32435
  if (scripts === "updated") {
31194
32436
  console.log("init: merged validate/audit scripts into package.json");
@@ -31221,8 +32463,8 @@ function parseInitArgs(argv) {
31221
32463
  // src/plugins/build.ts
31222
32464
  import { spawnSync as spawnSync3 } from "node:child_process";
31223
32465
  import { createHash } from "node:crypto";
31224
- import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync3 } from "node:fs";
31225
- import { basename as basename4, dirname as dirname11, resolve as resolve9 } from "node:path";
32466
+ import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync4 } from "node:fs";
32467
+ import { basename as basename4, dirname as dirname12, resolve as resolve8 } from "node:path";
31226
32468
  function parseBuildPluginArgs(argv) {
31227
32469
  let check = false;
31228
32470
  let entry;
@@ -31247,7 +32489,7 @@ function collectPluginEntries(root2, config, entry) {
31247
32489
  return (config.plugins ?? []).map((e) => resolvePluginTsPath(root2, e));
31248
32490
  }
31249
32491
  function localImportPaths(tsAbs, content3) {
31250
- const dir = dirname11(tsAbs);
32492
+ const dir = dirname12(tsAbs);
31251
32493
  const deps = [];
31252
32494
  const re = /(?:from\s+|import\s*\(\s*|import\s+)["'](\.[^"']+)["']/g;
31253
32495
  for (const match of content3.matchAll(re)) {
@@ -31257,12 +32499,12 @@ function localImportPaths(tsAbs, content3) {
31257
32499
  const candidates = [];
31258
32500
  if (spec.endsWith(".js")) {
31259
32501
  const withoutJs = spec.slice(0, -".js".length);
31260
- candidates.push(resolve9(dir, `${withoutJs}.ts`), resolve9(dir, spec), resolve9(dir, withoutJs, "index.ts"));
32502
+ candidates.push(resolve8(dir, `${withoutJs}.ts`), resolve8(dir, spec), resolve8(dir, withoutJs, "index.ts"));
31261
32503
  } else {
31262
- candidates.push(resolve9(dir, spec), resolve9(dir, `${spec}.ts`), resolve9(dir, `${spec}.js`), resolve9(dir, spec, "index.ts"));
32504
+ candidates.push(resolve8(dir, spec), resolve8(dir, `${spec}.ts`), resolve8(dir, `${spec}.js`), resolve8(dir, spec, "index.ts"));
31263
32505
  }
31264
32506
  for (const candidate of candidates) {
31265
- if (existsSync23(candidate) && candidate.endsWith(".ts")) {
32507
+ if (existsSync22(candidate) && candidate.endsWith(".ts")) {
31266
32508
  deps.push(candidate);
31267
32509
  break;
31268
32510
  }
@@ -31279,7 +32521,7 @@ function sourceFingerprint(tsAbs, seen = new Set) {
31279
32521
  if (seen.has(abs))
31280
32522
  return;
31281
32523
  seen.add(abs);
31282
- const content3 = readFileSync18(abs, "utf8");
32524
+ const content3 = readFileSync21(abs, "utf8");
31283
32525
  hash.update(basename4(abs));
31284
32526
  hash.update("\x00");
31285
32527
  hash.update(content3);
@@ -31292,12 +32534,12 @@ function sourceFingerprint(tsAbs, seen = new Set) {
31292
32534
  return hash.digest("hex");
31293
32535
  }
31294
32536
  function writeStamp(tsAbs, mjsAbs) {
31295
- writeFileSync3(stampPathForMjs(mjsAbs), `${sourceFingerprint(tsAbs)}
32537
+ writeFileSync4(stampPathForMjs(mjsAbs), `${sourceFingerprint(tsAbs)}
31296
32538
  `, "utf8");
31297
32539
  }
31298
32540
  async function buildOne(tsAbs) {
31299
32541
  const mjsAbs = mjsPathForTs(tsAbs);
31300
- if (!existsSync23(tsAbs)) {
32542
+ if (!existsSync22(tsAbs)) {
31301
32543
  throw new Error(`Plugin source not found: ${tsAbs}`);
31302
32544
  }
31303
32545
  const proc = spawnSync3("bun", ["build", tsAbs, "--target=node", "--format=esm", `--outfile=${mjsAbs}`, "--packages=external"], { encoding: "utf8" });
@@ -31317,17 +32559,17 @@ ${proc.stderr || proc.stdout || `exit ${proc.status}`}`);
31317
32559
  }
31318
32560
  function checkOne(tsAbs) {
31319
32561
  const mjsAbs = mjsPathForTs(tsAbs);
31320
- if (!existsSync23(mjsAbs)) {
32562
+ if (!existsSync22(mjsAbs)) {
31321
32563
  throw new Error(`Plugin not built: ${tsAbs} (missing ${mjsAbs}). Run: skeleton build-plugin`);
31322
32564
  }
31323
- if (!existsSync23(tsAbs)) {
32565
+ if (!existsSync22(tsAbs)) {
31324
32566
  throw new Error(`Plugin source not found: ${tsAbs}`);
31325
32567
  }
31326
32568
  const stampAbs = stampPathForMjs(mjsAbs);
31327
- if (!existsSync23(stampAbs)) {
32569
+ if (!existsSync22(stampAbs)) {
31328
32570
  throw new Error(`Plugin stale: ${mjsAbs} has no fingerprint stamp. Run: skeleton build-plugin`);
31329
32571
  }
31330
- const expected = readFileSync18(stampAbs, "utf8").trim();
32572
+ const expected = readFileSync21(stampAbs, "utf8").trim();
31331
32573
  const actual = sourceFingerprint(tsAbs);
31332
32574
  if (expected !== actual) {
31333
32575
  throw new Error(`Plugin stale: ${mjsAbs} does not match ${tsAbs} (or local imports). Run: skeleton build-plugin`);
@@ -31360,14 +32602,14 @@ import process6 from "node:process";
31360
32602
 
31361
32603
  // src/references/sync.ts
31362
32604
  import {
31363
- existsSync as existsSync24,
31364
- mkdirSync as mkdirSync3,
32605
+ existsSync as existsSync23,
32606
+ mkdirSync as mkdirSync4,
31365
32607
  readdirSync as readdirSync6,
31366
- readFileSync as readFileSync19,
32608
+ readFileSync as readFileSync22,
31367
32609
  unlinkSync,
31368
- writeFileSync as writeFileSync4
32610
+ writeFileSync as writeFileSync5
31369
32611
  } from "node:fs";
31370
- import { dirname as dirname12, join as join20, relative as relative11 } from "node:path";
32612
+ import { dirname as dirname13, join as join22, relative as relative11 } from "node:path";
31371
32613
  import process5 from "node:process";
31372
32614
  function resolveOwnership(root2, override) {
31373
32615
  if (override !== undefined)
@@ -31378,12 +32620,12 @@ function resolveOwnership(root2, override) {
31378
32620
  }
31379
32621
  function walkMarkdownFiles2(dir, root2) {
31380
32622
  const files = [];
31381
- if (!existsSync24(dir))
32623
+ if (!existsSync23(dir))
31382
32624
  return files;
31383
32625
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
31384
32626
  if (entry.name.startsWith("."))
31385
32627
  continue;
31386
- const fullPath = join20(dir, entry.name);
32628
+ const fullPath = join22(dir, entry.name);
31387
32629
  if (entry.isDirectory()) {
31388
32630
  files.push(...walkMarkdownFiles2(fullPath, root2));
31389
32631
  continue;
@@ -31397,14 +32639,14 @@ function walkMarkdownFiles2(dir, root2) {
31397
32639
  function collectGeneratedInDir(input) {
31398
32640
  const { dir, refsDir, skill, files } = input;
31399
32641
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
31400
- const fullPath = join20(dir, entry.name);
32642
+ const fullPath = join22(dir, entry.name);
31401
32643
  if (entry.isDirectory()) {
31402
32644
  collectGeneratedInDir({ dir: fullPath, refsDir, skill, files });
31403
32645
  continue;
31404
32646
  }
31405
32647
  if (!entry.name.endsWith(".md"))
31406
32648
  continue;
31407
- const content3 = readFileSync19(fullPath, "utf8");
32649
+ const content3 = readFileSync22(fullPath, "utf8");
31408
32650
  if (!isGeneratedReference(content3))
31409
32651
  continue;
31410
32652
  const refPath = normalizeRelPath(relative11(refsDir, fullPath));
@@ -31412,8 +32654,8 @@ function collectGeneratedInDir(input) {
31412
32654
  }
31413
32655
  }
31414
32656
  function listGeneratedReferenceFiles(skillDir, skill) {
31415
- const refsDir = join20(skillDir, "references");
31416
- if (!existsSync24(refsDir))
32657
+ const refsDir = join22(skillDir, "references");
32658
+ if (!existsSync23(refsDir))
31417
32659
  return [];
31418
32660
  const files = [];
31419
32661
  collectGeneratedInDir({ dir: refsDir, refsDir, skill, files });
@@ -31421,21 +32663,21 @@ function listGeneratedReferenceFiles(skillDir, skill) {
31421
32663
  }
31422
32664
  function syncGeneratedCopy(ctx, refPath) {
31423
32665
  const { root: root2, plan, options, result } = ctx;
31424
- const sourceRel = normalizeRelPath(join20(CANONICAL_REFS_DIR, refPath));
31425
- const canonicalPath = join20(root2, sourceRel);
31426
- if (!existsSync24(canonicalPath)) {
32666
+ const sourceRel = normalizeRelPath(join22(CANONICAL_REFS_DIR, refPath));
32667
+ const canonicalPath = join22(root2, sourceRel);
32668
+ if (!existsSync23(canonicalPath)) {
31427
32669
  throw new Error(`canonical reference missing: ${sourceRel}`);
31428
32670
  }
31429
32671
  const targetRel = generatedRefPath(plan.skill, refPath);
31430
- const targetPath = join20(root2, targetRel);
31431
- const canonicalContent = readFileSync19(canonicalPath, "utf8");
32672
+ const targetPath = join22(root2, targetRel);
32673
+ const canonicalContent = readFileSync22(canonicalPath, "utf8");
31432
32674
  const nextContent = formatGeneratedHeader(sourceRel) + canonicalContent;
31433
32675
  if (!options.dryRun)
31434
- mkdirSync3(dirname12(targetPath), { recursive: true });
31435
- const existing = existsSync24(targetPath) ? readFileSync19(targetPath, "utf8") : null;
32676
+ mkdirSync4(dirname13(targetPath), { recursive: true });
32677
+ const existing = existsSync23(targetPath) ? readFileSync22(targetPath, "utf8") : null;
31436
32678
  if (existing !== nextContent) {
31437
32679
  if (!options.dryRun)
31438
- writeFileSync4(targetPath, nextContent, "utf8");
32680
+ writeFileSync5(targetPath, nextContent, "utf8");
31439
32681
  result.written.push(targetRel);
31440
32682
  } else {
31441
32683
  result.skipped.push(targetRel);
@@ -31446,13 +32688,13 @@ function rewritePlanLinks(ctx, skillDir) {
31446
32688
  if (options.rewriteLinks === false)
31447
32689
  return;
31448
32690
  for (const relFile of walkMarkdownFiles2(skillDir, root2)) {
31449
- const filePath = join20(root2, relFile);
31450
- const content3 = readFileSync19(filePath, "utf8");
32691
+ const filePath = join22(root2, relFile);
32692
+ const content3 = readFileSync22(filePath, "utf8");
31451
32693
  const next = rewriteSharedRefLinks(content3, relFile, plan.skill);
31452
32694
  if (next === content3)
31453
32695
  continue;
31454
32696
  if (!options.dryRun)
31455
- writeFileSync4(filePath, next, "utf8");
32697
+ writeFileSync5(filePath, next, "utf8");
31456
32698
  result.rewritten.push(relFile);
31457
32699
  }
31458
32700
  }
@@ -31463,12 +32705,12 @@ function removeStaleGenerated(ctx, skillDir) {
31463
32705
  if (plan.refPaths.has(refPath))
31464
32706
  continue;
31465
32707
  if (!options.dryRun)
31466
- unlinkSync(join20(root2, generatedRel));
32708
+ unlinkSync(join22(root2, generatedRel));
31467
32709
  result.removed.push(generatedRel);
31468
32710
  }
31469
32711
  }
31470
32712
  function syncPlan(ctx) {
31471
- const skillDir = join20(ctx.root, ctx.plan.skill);
32713
+ const skillDir = join22(ctx.root, ctx.plan.skill);
31472
32714
  for (const refPath of ctx.plan.refPaths) {
31473
32715
  syncGeneratedCopy(ctx, refPath);
31474
32716
  }
@@ -31477,8 +32719,8 @@ function syncPlan(ctx) {
31477
32719
  }
31478
32720
  function syncReferences(options = {}) {
31479
32721
  const root2 = options.root ?? process5.cwd();
31480
- const canonicalDir = join20(root2, CANONICAL_REFS_DIR);
31481
- if (!existsSync24(canonicalDir)) {
32722
+ const canonicalDir = join22(root2, CANONICAL_REFS_DIR);
32723
+ if (!existsSync23(canonicalDir)) {
31482
32724
  throw new Error(`canonical references dir not found: ${CANONICAL_REFS_DIR}`);
31483
32725
  }
31484
32726
  const result = { written: [], rewritten: [], removed: [], skipped: [] };
@@ -31523,183 +32765,10 @@ function printSyncResult(result) {
31523
32765
  }
31524
32766
  }
31525
32767
 
31526
- // src/register.ts
31527
- import { existsSync as existsSync25, readFileSync as readFileSync20, writeFileSync as writeFileSync5 } from "node:fs";
31528
- import { dirname as dirname13, join as join21, relative as relative12 } from "node:path";
31529
- var REGISTRY_TABLE_ROW_RE2 = /^\|\s*([^|]+)\|\s*\[[^\]]*\]\(([^)]+)\)\s*\|/;
31530
- var REGISTRY_TABLE_HEADER = "| Topic | Canonical file |";
31531
- function extractTopic(content3) {
31532
- const match = content3.match(/\*\*Source of truth for\*\*\s*(.+)/);
31533
- return match?.[1]?.trim().replace(/\s+$/, "") ?? null;
31534
- }
31535
- function toRegistryLink(root2, absPath) {
31536
- const fromRegistry = join21(root2, REGISTRY_DIR_REL);
31537
- return normalizeRelPath(relative12(fromRegistry, absPath));
31538
- }
31539
- function inferSection(registryLink) {
31540
- return registryLink.startsWith("customize/") ? "Customizations" : "Documentation";
31541
- }
31542
- function ensureCustomizeTopic(topic, registryLink) {
31543
- if (!registryLink.startsWith("customize/"))
31544
- return topic;
31545
- if (topic.startsWith(CUSTOMIZE_PREFIX))
31546
- return topic;
31547
- return `${CUSTOMIZE_PREFIX}${topic}`;
31548
- }
31549
- function parseRegistryRows(content3) {
31550
- const rows = [];
31551
- for (const line of content3.split(`
31552
- `)) {
31553
- const match = REGISTRY_TABLE_ROW_RE2.exec(line);
31554
- if (!(match?.[1] && match[2]))
31555
- continue;
31556
- rows.push({ topic: match[1].trim(), link: match[2].trim(), line });
31557
- }
31558
- return rows;
31559
- }
31560
- function pathFromRegistryLink(root2, link2) {
31561
- return normalizeRelPath(relative12(root2, join21(root2, REGISTRY_DIR_REL, link2)));
31562
- }
31563
- function isOutsideScan(root2, relPath2) {
31564
- const config = loadConfig(root2);
31565
- const skillIndex = buildSkillIndex(root2, config.skillOwnership);
31566
- const scanned = collectScanFiles(config, root2, skillIndex).map((abs) => normalizeRelPath(relative12(root2, abs)));
31567
- if (scanned.includes(relPath2))
31568
- return false;
31569
- return !config.scan.include.some((pattern) => matchesGlobScope(relPath2, pattern));
31570
- }
31571
- function defaultRegistryContent() {
31572
- return `# Registry
31573
-
31574
- <!-- doc-meta: owner=eng | last-reviewed=${new Date().toISOString().slice(0, 10)} -->
31575
-
31576
- **Source of truth for** topic routing in this repo. Edit rows here; edit content in canonical files only.
31577
-
31578
- ## Documentation
31579
-
31580
- ${REGISTRY_TABLE_HEADER}
31581
- |-------|------------------|
31582
-
31583
- `;
31584
- }
31585
- function upsertRow(opts) {
31586
- const { content: content3, topic, link: link2, section, root: root2 } = opts;
31587
- const rows = parseRegistryRows(content3);
31588
- const targetPath = link2;
31589
- const existingByLink = rows.find((row) => row.link === targetPath);
31590
- if (existingByLink && existingByLink.topic === topic) {
31591
- return { content: content3, action: "noop" };
31592
- }
31593
- const duplicateTopic = rows.find((row) => row.topic === topic && row.link !== targetPath);
31594
- if (duplicateTopic) {
31595
- throw new Error(`topic "${topic}" already registered for ${pathFromRegistryLink(root2, duplicateTopic.link)} — hand-edit registry or use different topic`);
31596
- }
31597
- const newLine = `| ${topic} | [${link2.split("/").pop()}](${link2}) |`;
31598
- if (existingByLink) {
31599
- const updated = content3.replace(existingByLink.line, newLine);
31600
- return { content: updated, action: "updated" };
31601
- }
31602
- const sectionHeader = `## ${section}`;
31603
- const sectionIdx = content3.indexOf(sectionHeader);
31604
- if (sectionIdx >= 0) {
31605
- const afterSection = content3.indexOf(REGISTRY_TABLE_HEADER, sectionIdx);
31606
- if (afterSection >= 0) {
31607
- const insertAt = content3.indexOf(`
31608
- `, afterSection + REGISTRY_TABLE_HEADER.length);
31609
- if (insertAt >= 0) {
31610
- const updated = `${content3.slice(0, insertAt + 1) + newLine}
31611
- ${content3.slice(insertAt + 1)}`;
31612
- return { content: updated, action: "added" };
31613
- }
31614
- }
31615
- }
31616
- const appended = `${content3.trimEnd()}
31617
-
31618
- ## ${section}
31619
-
31620
- ${REGISTRY_TABLE_HEADER}
31621
- |-------|------------------|
31622
- ${newLine}
31623
- `;
31624
- return { content: appended, action: "added" };
31625
- }
31626
- function printRegisterResult(options, result, relPath2) {
31627
- if (options.json) {
31628
- console.log(JSON.stringify(result, null, 2));
31629
- } else if (options.dryRun) {
31630
- console.log(`dry-run: would ${result.action} registry row for ${relPath2} → ${result.topic}`);
31631
- } else if (result.action === "noop") {
31632
- console.log(`register: ${relPath2} already registered (${result.topic})`);
31633
- } else {
31634
- console.log(`register: ${result.action} ${relPath2} → ${result.topic}`);
31635
- }
31636
- }
31637
- function resolveRegistrationTopic(input) {
31638
- const { options, root: root2, relPath: relPath2, absPath } = input;
31639
- const content3 = readFileSync20(absPath, "utf8");
31640
- let topic = options.topic ?? extractTopic(content3);
31641
- if (!topic) {
31642
- throw new Error(`No **Source of truth for** banner in ${relPath2} — add banner or pass --topic`);
31643
- }
31644
- const registryLink = toRegistryLink(root2, absPath);
31645
- topic = ensureCustomizeTopic(topic, registryLink);
31646
- return { topic, registryLink, section: inferSection(registryLink) };
31647
- }
31648
- function loadRegistryContent(root2) {
31649
- const registryAbs = join21(root2, REGISTRY_REL_PATH);
31650
- if (!(existsSync25(registryAbs) || existsSync25(join21(root2, ".skeleton/config.yaml")))) {
31651
- throw new Error("Missing .skeleton/config.yaml — run skeleton init first");
31652
- }
31653
- return existsSync25(registryAbs) ? readFileSync20(registryAbs, "utf8") : defaultRegistryContent();
31654
- }
31655
- function registerPath(options) {
31656
- const root2 = options.root ?? findRepoRoot();
31657
- const relPath2 = normalizeRelPath(options.path);
31658
- const absPath = join21(root2, relPath2);
31659
- if (!existsSync25(absPath)) {
31660
- throw new Error(`File not found: ${relPath2}`);
31661
- }
31662
- const { topic, registryLink, section } = resolveRegistrationTopic({
31663
- options,
31664
- root: root2,
31665
- relPath: relPath2,
31666
- absPath
31667
- });
31668
- let registryContent = loadRegistryContent(root2);
31669
- const { content: updated, action } = upsertRow({
31670
- content: registryContent,
31671
- topic,
31672
- link: registryLink,
31673
- section,
31674
- root: root2
31675
- });
31676
- registryContent = updated;
31677
- const result = {
31678
- topic,
31679
- registryLink,
31680
- section,
31681
- action,
31682
- warnOutsideScan: isOutsideScan(root2, relPath2)
31683
- };
31684
- const registryAbs = join21(root2, REGISTRY_REL_PATH);
31685
- if (!options.dryRun && action !== "noop") {
31686
- const dir = dirname13(registryAbs);
31687
- if (!existsSync25(dir)) {
31688
- throw new Error(`Missing ${REGISTRY_DIR_REL}/ directory`);
31689
- }
31690
- writeFileSync5(registryAbs, registryContent, "utf8");
31691
- }
31692
- if (result.warnOutsideScan) {
31693
- console.error(`warning: ${relPath2} is outside scan.include — register succeeded but audit will not scan it`);
31694
- }
31695
- printRegisterResult(options, result, relPath2);
31696
- return result;
31697
- }
31698
-
31699
32768
  // src/validate/changed.ts
31700
32769
  import { spawnSync as spawnSync5 } from "node:child_process";
31701
- import { existsSync as existsSync26, readFileSync as readFileSync21 } from "node:fs";
31702
- import { basename as basename5, extname as extname2, join as join22 } from "node:path";
32770
+ import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
32771
+ import { basename as basename5, extname as extname2, join as join23 } from "node:path";
31703
32772
 
31704
32773
  // src/validate/git-diff.ts
31705
32774
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -31792,9 +32861,9 @@ function parseJsonContent(content3) {
31792
32861
  }
31793
32862
  }
31794
32863
  function validateJson(relPath2, root2) {
31795
- const abs = join22(root2, relPath2);
32864
+ const abs = join23(root2, relPath2);
31796
32865
  try {
31797
- parseJsonContent(readFileSync21(abs, "utf8"));
32866
+ parseJsonContent(readFileSync23(abs, "utf8"));
31798
32867
  return 0;
31799
32868
  } catch (error) {
31800
32869
  console.error(`validate changed: invalid JSON in ${relPath2}: ${error}`);
@@ -31802,9 +32871,9 @@ function validateJson(relPath2, root2) {
31802
32871
  }
31803
32872
  }
31804
32873
  function validatePolicy(relPath2, root2) {
31805
- const abs = join22(root2, relPath2);
32874
+ const abs = join23(root2, relPath2);
31806
32875
  try {
31807
- loadPolicyFile(abs, readFileSync21(abs, "utf8"));
32876
+ loadPolicyFile(abs, readFileSync23(abs, "utf8"));
31808
32877
  return 0;
31809
32878
  } catch (error) {
31810
32879
  console.error(`validate changed: invalid policy ${relPath2}: ${error}`);
@@ -31812,7 +32881,7 @@ function validatePolicy(relPath2, root2) {
31812
32881
  }
31813
32882
  }
31814
32883
  function validateShell(relPath2, root2) {
31815
- const abs = join22(root2, relPath2);
32884
+ const abs = join23(root2, relPath2);
31816
32885
  const shellcheck = spawnSync5("shellcheck", [abs], { encoding: "utf8" });
31817
32886
  if (shellcheck.status === 0)
31818
32887
  return 0;
@@ -31833,11 +32902,11 @@ function resolvePaths(options) {
31833
32902
  });
31834
32903
  }
31835
32904
  function packageManagerFromPackageJson(root2) {
31836
- const pkgPath = join22(root2, "package.json");
31837
- if (!existsSync26(pkgPath))
32905
+ const pkgPath = join23(root2, "package.json");
32906
+ if (!existsSync24(pkgPath))
31838
32907
  return null;
31839
32908
  try {
31840
- const pkg = JSON.parse(readFileSync21(pkgPath, "utf8"));
32909
+ const pkg = JSON.parse(readFileSync23(pkgPath, "utf8"));
31841
32910
  const raw = pkg.packageManager?.split("@")[0];
31842
32911
  if (raw === "bun" || raw === "npm" || raw === "pnpm" || raw === "yarn")
31843
32912
  return raw;
@@ -31845,13 +32914,13 @@ function packageManagerFromPackageJson(root2) {
31845
32914
  return null;
31846
32915
  }
31847
32916
  function packageManagerFromLockfiles(root2) {
31848
- if (existsSync26(join22(root2, "bun.lock")) || existsSync26(join22(root2, "bun.lockb")))
32917
+ if (existsSync24(join23(root2, "bun.lock")) || existsSync24(join23(root2, "bun.lockb")))
31849
32918
  return "bun";
31850
- if (existsSync26(join22(root2, "pnpm-lock.yaml")))
32919
+ if (existsSync24(join23(root2, "pnpm-lock.yaml")))
31851
32920
  return "pnpm";
31852
- if (existsSync26(join22(root2, "yarn.lock")))
32921
+ if (existsSync24(join23(root2, "yarn.lock")))
31853
32922
  return "yarn";
31854
- if (existsSync26(join22(root2, "package-lock.json")))
32923
+ if (existsSync24(join23(root2, "package-lock.json")))
31855
32924
  return "npm";
31856
32925
  return null;
31857
32926
  }
@@ -31877,8 +32946,8 @@ function emptyBuckets() {
31877
32946
  function classifySinglePath(input) {
31878
32947
  const { relPath: relPath2, ctx, state, bucketCtx } = input;
31879
32948
  const normalized = normalizeRelPath(relPath2);
31880
- const abs = join22(ctx.root, normalized);
31881
- if (!existsSync26(abs)) {
32949
+ const abs = join23(ctx.root, normalized);
32950
+ if (!existsSync24(abs)) {
31882
32951
  state.missing++;
31883
32952
  console.error(`validate changed: path not found: ${relPath2}`);
31884
32953
  return;
@@ -32112,31 +33181,16 @@ function usage() {
32112
33181
  Commands:
32113
33182
  init [--force-hooks] [--skills] [--no-skills] [skills add flags…]
32114
33183
  audit docs|self|skills [--strict] [--json] [--paths=a,b] [--only=rule]
32115
- [--fix[=doc-meta|anchors]] [--dry-run]
33184
+ [--fix[=doc-meta|anchors|ssot]] [--dry-run]
32116
33185
  build-plugin [path] [--check]
32117
33186
  validate changed [paths…] [--staged] [--base <ref>]
32118
- register <path> [--topic=…] [--dry-run] [--json]
33187
+ catalog [--check] write or check .skeleton/catalog.md (gitignored)
32119
33188
  customize resolve <slug> [--json]
32120
33189
  hook customize (reads a host hook payload on stdin)
32121
33190
  references sync [--dry-run] [--no-rewrite-links]
32122
- references check [--json] [--strict]`);
32123
- }
32124
- function parseRegisterArgs(argv) {
32125
- let path2 = null;
32126
- let topic;
32127
- let dryRun = false;
32128
- let json = false;
32129
- for (const arg of argv) {
32130
- if (arg === "--dry-run")
32131
- dryRun = true;
32132
- else if (arg === "--json")
32133
- json = true;
32134
- else if (arg.startsWith("--topic="))
32135
- topic = arg.slice("--topic=".length);
32136
- else if (!(arg.startsWith("-") || path2))
32137
- path2 = arg;
32138
- }
32139
- return { path: path2, topic, dryRun, json };
33191
+ references check [--json] [--strict]
33192
+
33193
+ Note: \`register\` was removed — add a source-of-truth marker to the file and run \`skeleton catalog\`.`);
32140
33194
  }
32141
33195
  function parseValidateChangedArgs(rest) {
32142
33196
  const paths = [];
@@ -32180,19 +33234,12 @@ async function handleValidateChanged(argv) {
32180
33234
  const { paths, staged, base } = parseValidateChangedArgs(argv);
32181
33235
  return runValidateChanged({ paths, staged, base });
32182
33236
  }
32183
- function handleRegister(argv) {
32184
- const opts = parseRegisterArgs(argv);
32185
- if (!opts.path) {
32186
- console.error("register: path required");
32187
- return 1;
32188
- }
32189
- registerPath({
32190
- path: opts.path,
32191
- topic: opts.topic,
32192
- dryRun: opts.dryRun,
32193
- json: opts.json
32194
- });
32195
- return 0;
33237
+ function handleRegister() {
33238
+ console.error("register: removed add `<!-- source-of-truth: … -->` (or visible `source-of-truth:`) to the file, then run `skeleton catalog`.");
33239
+ return 1;
33240
+ }
33241
+ function handleCatalog(argv) {
33242
+ return runCatalogCli({ check: argv.includes("--check") });
32196
33243
  }
32197
33244
  function handleCustomizeResolve(argv) {
32198
33245
  const slug2 = argv[0];
@@ -32214,7 +33261,7 @@ function handleHook(argv) {
32214
33261
  usage();
32215
33262
  return 1;
32216
33263
  }
32217
- process7.stdout.write(runCustomizeHook(readFileSync22(0, "utf8")));
33264
+ process7.stdout.write(runCustomizeHook(readFileSync24(0, "utf8")));
32218
33265
  return 0;
32219
33266
  }
32220
33267
  function handleInit(argv) {
@@ -32250,7 +33297,9 @@ async function dispatchCommand(argv) {
32250
33297
  case "validate":
32251
33298
  return rest[0] === "changed" ? handleValidateChanged(rest.slice(1)) : null;
32252
33299
  case "register":
32253
- return handleRegister(rest);
33300
+ return handleRegister();
33301
+ case "catalog":
33302
+ return handleCatalog(rest);
32254
33303
  case "customize":
32255
33304
  return rest[0] === "resolve" ? handleCustomizeResolve(rest.slice(1)) : null;
32256
33305
  case "hook":