@csark0812/skeleton 1.5.7 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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,107 @@ 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 SSOT_VISIBLE_LINE_RE = /^\s*source-of-truth:\s*(.+?)\s*$/gim;
18450
+ var LEGACY_BANNER_LINE_RE = /^\s*\*\*Source of truth for\*\*\s*(.+?)\s*$/gim;
18451
+ function cleanSummary(raw) {
18452
+ return raw.replace(/\s+/g, " ").trim().replace(/\.$/, "").trim();
18453
+ }
18454
+ function stripCode(content) {
18455
+ return content.replace(/```[\s\S]*?```/g, `
18456
+ `).replace(/`[^`\n]+`/g, " ");
18457
+ }
18458
+ function collectMatches(re, content) {
18459
+ const out = [];
18460
+ re.lastIndex = 0;
18461
+ let match = re.exec(content);
18462
+ while (match) {
18463
+ const summary = cleanSummary(match[1] ?? "");
18464
+ if (summary)
18465
+ out.push(summary);
18466
+ match = re.exec(content);
17673
18467
  }
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])
18468
+ return out;
18469
+ }
18470
+ function parseSsot(content) {
18471
+ const prose = stripCode(content);
18472
+ const comments = collectMatches(SSOT_COMMENT_RE, prose);
18473
+ const visibles = collectMatches(SSOT_VISIBLE_LINE_RE, prose);
18474
+ const legacies = collectMatches(LEGACY_BANNER_LINE_RE, prose);
18475
+ const forms = [];
18476
+ if (comments.length)
18477
+ forms.push("comment");
18478
+ if (visibles.length)
18479
+ forms.push("visible");
18480
+ if (legacies.length)
18481
+ forms.push("legacy");
18482
+ if (forms.length === 0)
18483
+ return { status: "none" };
18484
+ if (forms.length > 1)
18485
+ return { status: "dual", forms };
18486
+ if (comments.length > 1 || visibles.length > 1 || legacies.length > 1) {
18487
+ return { status: "malformed", detail: "multiple source-of-truth markers of the same form" };
18488
+ }
18489
+ if (comments.length === 1) {
18490
+ const summary2 = comments[0];
18491
+ if (!summary2)
18492
+ return { status: "malformed", detail: "empty source-of-truth comment" };
18493
+ return { status: "ok", entry: { summary: summary2, form: "comment" } };
18494
+ }
18495
+ if (visibles.length === 1) {
18496
+ const summary2 = visibles[0];
18497
+ if (!summary2)
18498
+ return { status: "malformed", detail: "empty source-of-truth line" };
18499
+ return { status: "ok", entry: { summary: summary2, form: "visible" } };
18500
+ }
18501
+ const summary = legacies[0];
18502
+ if (!summary)
18503
+ return { status: "malformed", detail: "empty legacy Source of truth banner" };
18504
+ return { status: "ok", entry: { summary, form: "legacy" } };
18505
+ }
18506
+ function rewriteLegacySsotToComment(content) {
18507
+ const parsed = parseSsot(content);
18508
+ if (parsed.status !== "ok" || parsed.entry.form !== "legacy")
18509
+ return null;
18510
+ const comment = `<!-- source-of-truth: ${parsed.entry.summary} -->`;
18511
+ const next = content.replace(LEGACY_BANNER_LINE_RE, comment);
18512
+ return next === content ? null : next;
18513
+ }
18514
+
18515
+ // src/audit/core/ssot-collect.ts
18516
+ function collectSsotEntries(files, root) {
18517
+ const entries = [];
18518
+ const errors2 = [];
18519
+ for (const abs of files) {
18520
+ const rel = normalizeRelPath(relative5(root, abs));
18521
+ if (!(rel.endsWith(".md") || rel.endsWith(".mdc")))
17681
18522
  continue;
17682
- const linkTarget = match[1].trim();
17683
- if (linkTarget.startsWith("#"))
18523
+ const content = readFileSync6(abs, "utf8");
18524
+ const parsed = parseSsot(content);
18525
+ if (parsed.status === "none")
18526
+ continue;
18527
+ if (parsed.status === "dual") {
18528
+ errors2.push({
18529
+ path: rel,
18530
+ kind: "dual",
18531
+ detail: `multiple source-of-truth forms: ${parsed.forms.join(", ")}`,
18532
+ forms: parsed.forms
18533
+ });
17684
18534
  continue;
17685
- const resolved = resolve4(join7(root, REGISTRY_DIR_REL), linkTarget);
17686
- paths.push(normalizeRelPath(relative5(root, resolved)));
18535
+ }
18536
+ if (parsed.status === "malformed") {
18537
+ errors2.push({ path: rel, kind: "malformed", detail: parsed.detail });
18538
+ continue;
18539
+ }
18540
+ entries.push({ path: rel, summary: parsed.entry.summary, form: parsed.entry.form });
17687
18541
  }
17688
- return { paths: [...new Set(paths)], hasTableHeader };
18542
+ entries.sort((a, b) => a.path.localeCompare(b.path));
18543
+ return { entries, errors: errors2 };
17689
18544
  }
17690
18545
 
17691
18546
  // src/audit/core/context.ts
@@ -17701,11 +18556,12 @@ function createContext(options = {}) {
17701
18556
  files = includeExplicitMarkdownPaths(files, options.paths, root);
17702
18557
  files = filterToPaths(files, options.paths, root);
17703
18558
  }
17704
- const registry = parseRegistry(root);
18559
+ const ssot = collectSsotEntries(files, root);
18560
+ const ssotPaths = ssot.entries.map((e) => e.path);
17705
18561
  const allDocMetaPaths = collectDocMetaPaths({
17706
18562
  config,
17707
18563
  root,
17708
- registryPaths: registry.paths,
18564
+ registryPaths: ssotPaths,
17709
18565
  skillIndex
17710
18566
  });
17711
18567
  return {
@@ -17713,9 +18569,10 @@ function createContext(options = {}) {
17713
18569
  config,
17714
18570
  files,
17715
18571
  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)),
18572
+ ssotEntries: ssot.entries,
18573
+ ssotErrors: ssot.errors,
18574
+ registryPaths: [],
18575
+ registryHasTableHeader: false,
17719
18576
  skillIndex,
17720
18577
  lockedSkillSlugs: new Set(skillIndex.foreignSlugs),
17721
18578
  policies: options.policies ?? []
@@ -17723,12 +18580,12 @@ function createContext(options = {}) {
17723
18580
  }
17724
18581
 
17725
18582
  // 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";
18583
+ import { existsSync as existsSync10, realpathSync as realpathSync5, writeFileSync } from "node:fs";
18584
+ import { dirname as dirname6, resolve as resolve5, sep as sep3 } from "node:path";
17728
18585
 
17729
18586
  // 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";
18587
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
18588
+ import { dirname as dirname5, resolve as resolve4 } from "node:path";
17732
18589
 
17733
18590
  // node_modules/github-slugger/regex.js
17734
18591
  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 +21588,10 @@ function resolveAll(constructs2, events, context) {
20731
21588
  const called = [];
20732
21589
  let index = -1;
20733
21590
  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);
21591
+ const resolve4 = constructs2[index].resolveAll;
21592
+ if (resolve4 && !called.includes(resolve4)) {
21593
+ events = resolve4(events, context);
21594
+ called.push(resolve4);
20738
21595
  }
20739
21596
  }
20740
21597
  return events;
@@ -21979,9 +22836,9 @@ function factoryTitle(effects, ok2, nok, type, markerType, stringType) {
21979
22836
  return atBreak(code);
21980
22837
  }
21981
22838
  effects.consume(code);
21982
- return code === codes.backslash ? escape : inside;
22839
+ return code === codes.backslash ? escape2 : inside;
21983
22840
  }
21984
- function escape(code) {
22841
+ function escape2(code) {
21985
22842
  if (code === marker || code === codes.backslash) {
21986
22843
  effects.consume(code);
21987
22844
  return inside;
@@ -24042,7 +24899,7 @@ function serializeChunks(chunks, expandTabs) {
24042
24899
  }
24043
24900
 
24044
24901
  // node_modules/micromark/dev/lib/parse.js
24045
- function parse(options) {
24902
+ function parse2(options) {
24046
24903
  const settings = options || {};
24047
24904
  const constructs2 = combineExtensions([exports_constructs, ...settings.extensions || []]);
24048
24905
  const parser2 = {
@@ -24201,7 +25058,7 @@ function fromMarkdown(value, encoding, options) {
24201
25058
  options = encoding;
24202
25059
  encoding = undefined;
24203
25060
  }
24204
- return compiler(options)(postprocess(parse(options).document().write(preprocess()(value, encoding, true))));
25061
+ return compiler(options)(postprocess(parse2(options).document().write(preprocess()(value, encoding, true))));
24205
25062
  }
24206
25063
  function compiler(options) {
24207
25064
  const config = {
@@ -25908,7 +26765,7 @@ var handle = {
25908
26765
  };
25909
26766
 
25910
26767
  // node_modules/mdast-util-to-markdown/lib/join.js
25911
- var join8 = [joinDefaults];
26768
+ var join7 = [joinDefaults];
25912
26769
  function joinDefaults(left, right, parent, state) {
25913
26770
  if (right.type === "code" && formatCodeAsIndented(right, state) && (left.type === "list" || left.type === right.type && formatCodeAsIndented(left, state))) {
25914
26771
  return false;
@@ -26291,7 +27148,7 @@ function toMarkdown(tree, options) {
26291
27148
  handle: undefined,
26292
27149
  indentLines,
26293
27150
  indexStack: [],
26294
- join: [...join8],
27151
+ join: [...join7],
26295
27152
  options: {},
26296
27153
  safe: safeBound,
26297
27154
  stack: [],
@@ -26784,7 +27641,7 @@ class Processor extends CallableInstance {
26784
27641
  assertParser("process", this.parser || this.Parser);
26785
27642
  assertCompiler("process", this.compiler || this.Compiler);
26786
27643
  return done ? executor(undefined, done) : new Promise(executor);
26787
- function executor(resolve5, reject) {
27644
+ function executor(resolve4, reject) {
26788
27645
  const realFile = vfile(file);
26789
27646
  const parseTree = self.parse(realFile);
26790
27647
  self.run(parseTree, realFile, function(error, tree, file2) {
@@ -26803,8 +27660,8 @@ class Processor extends CallableInstance {
26803
27660
  function realDone(error, file2) {
26804
27661
  if (error || !file2) {
26805
27662
  reject(error);
26806
- } else if (resolve5) {
26807
- resolve5(file2);
27663
+ } else if (resolve4) {
27664
+ resolve4(file2);
26808
27665
  } else {
26809
27666
  ok(done, "`done` is defined if `resolve` is not");
26810
27667
  done(undefined, file2);
@@ -26837,7 +27694,7 @@ class Processor extends CallableInstance {
26837
27694
  file = undefined;
26838
27695
  }
26839
27696
  return done ? executor(undefined, done) : new Promise(executor);
26840
- function executor(resolve5, reject) {
27697
+ function executor(resolve4, reject) {
26841
27698
  ok(typeof file !== "function", "`file` can’t be a `done` anymore, we checked");
26842
27699
  const realFile = vfile(file);
26843
27700
  transformers.run(tree, realFile, realDone);
@@ -26845,8 +27702,8 @@ class Processor extends CallableInstance {
26845
27702
  const resultingTree = outputTree || tree;
26846
27703
  if (error) {
26847
27704
  reject(error);
26848
- } else if (resolve5) {
26849
- resolve5(resultingTree);
27705
+ } else if (resolve4) {
27706
+ resolve4(resultingTree);
26850
27707
  } else {
26851
27708
  ok(done, "`done` is defined if `resolve` is not");
26852
27709
  done(undefined, resultingTree, file2);
@@ -29289,7 +30146,7 @@ function resolveLink(sourceFile, target) {
29289
30146
  const withoutAnchor = target.split("#")[0]?.split("?")[0] ?? "";
29290
30147
  if (!withoutAnchor)
29291
30148
  return sourceFile;
29292
- return resolve5(dirname5(sourceFile), withoutAnchor);
30149
+ return resolve4(dirname5(sourceFile), withoutAnchor);
29293
30150
  }
29294
30151
  function replaceAnchorInTarget(target, oldAnchor, newAnchor) {
29295
30152
  const hashIndex = target.indexOf("#");
@@ -29313,7 +30170,7 @@ function anchorTargetReplacement(filePath, target) {
29313
30170
  if (!anchor)
29314
30171
  return null;
29315
30172
  const resolved = resolveLink(filePath, target);
29316
- if (!existsSync9(resolved))
30173
+ if (!existsSync8(resolved))
29317
30174
  return null;
29318
30175
  const targetContent = readFileSync7(resolved, "utf8");
29319
30176
  const slugs = extractHeadingSlugs(targetContent, resolved);
@@ -29397,8 +30254,8 @@ function collectAnchorFixes(ctx) {
29397
30254
  }
29398
30255
 
29399
30256
  // 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";
30257
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
30258
+ import { join as join8 } from "node:path";
29402
30259
 
29403
30260
  // src/audit/core/git-meta.ts
29404
30261
  import { spawnSync } from "node:child_process";
@@ -29427,8 +30284,8 @@ function bumpDocMetaLastReviewed(content3, gitDate) {
29427
30284
  return replaceDocMetaLastReviewed(content3, gitDate);
29428
30285
  }
29429
30286
  function docMetaFixForPath(ctx, relPath2) {
29430
- const abs = join9(ctx.root, relPath2);
29431
- if (!existsSync10(abs))
30287
+ const abs = join8(ctx.root, relPath2);
30288
+ if (!existsSync9(abs))
29432
30289
  return null;
29433
30290
  const content3 = readFileSync8(abs, "utf8");
29434
30291
  if (!DOC_META_RE.test(content3))
@@ -29461,34 +30318,74 @@ function collectDocMetaFixes(ctx) {
29461
30318
  return edits;
29462
30319
  }
29463
30320
 
30321
+ // src/audit/fix/ssot.ts
30322
+ import { readFileSync as readFileSync9 } from "node:fs";
30323
+ import { join as join9 } from "node:path";
30324
+ function collectSsotFixes(ctx) {
30325
+ const edits = [];
30326
+ for (const entry of ctx.ssotEntries) {
30327
+ if (entry.form !== "legacy")
30328
+ continue;
30329
+ const abs = join9(ctx.root, entry.path);
30330
+ const content3 = readFileSync9(abs, "utf8");
30331
+ const next = rewriteLegacySsotToComment(content3);
30332
+ if (!next)
30333
+ continue;
30334
+ edits.push({
30335
+ file: entry.path,
30336
+ description: "rewrite legacy **Source of truth for** banner to <!-- source-of-truth: … -->",
30337
+ content: next
30338
+ });
30339
+ }
30340
+ return edits;
30341
+ }
30342
+
29464
30343
  // src/audit/core/fix.ts
29465
30344
  function collectFixes(ctx, kinds) {
29466
30345
  const meta = kinds.has("doc-meta") ? collectDocMetaFixes(ctx) : [];
29467
30346
  const anchors = kinds.has("anchors") ? collectAnchorFixes(ctx) : [];
29468
- return coalesceFixEdits(meta, anchors);
30347
+ const ssot = kinds.has("ssot") ? collectSsotFixes(ctx) : [];
30348
+ return coalesceFixEdits(meta, anchors, ssot);
29469
30349
  }
29470
- function coalesceFixEdits(metaEdits, anchorEdits) {
30350
+ function coalesceFixEdits(metaEdits, anchorEdits, ssotEdits = []) {
29471
30351
  const metaByFile = new Map(metaEdits.map((e) => [e.file, e]));
29472
30352
  const anchorByFile = new Map(anchorEdits.map((e) => [e.file, e]));
29473
- const files = new Set([...metaByFile.keys(), ...anchorByFile.keys()]);
30353
+ const ssotByFile = new Map(ssotEdits.map((e) => [e.file, e]));
30354
+ const files = new Set([...metaByFile.keys(), ...anchorByFile.keys(), ...ssotByFile.keys()]);
29474
30355
  const out = [];
29475
30356
  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
- }
30357
+ out.push(coalesceOneFile({
30358
+ file,
30359
+ meta: metaByFile.get(file),
30360
+ anchors: anchorByFile.get(file),
30361
+ ssot: ssotByFile.get(file)
30362
+ }));
29489
30363
  }
29490
30364
  return out;
29491
30365
  }
30366
+ function coalesceOneFile(input) {
30367
+ const { file, meta, anchors, ssot } = input;
30368
+ const descriptions = [];
30369
+ let content3 = anchors?.content ?? meta?.content ?? ssot?.content ?? "";
30370
+ if (meta)
30371
+ descriptions.push(meta.description);
30372
+ if (anchors) {
30373
+ descriptions.push(anchors.description);
30374
+ content3 = anchors.content;
30375
+ if (meta)
30376
+ content3 = overlayLastReviewed(content3, meta.content);
30377
+ } else if (meta) {
30378
+ content3 = meta.content;
30379
+ }
30380
+ if (ssot) {
30381
+ descriptions.push(ssot.description);
30382
+ content3 = rewriteSsotOnto(content3, ssot.content);
30383
+ }
30384
+ return { file, description: descriptions.join("; "), content: content3 };
30385
+ }
30386
+ function rewriteSsotOnto(base, ssotContent) {
30387
+ return ssotContent || base;
30388
+ }
29492
30389
  function overlayLastReviewed(targetContent, metaContent) {
29493
30390
  const date = docMetaLastReviewed(metaContent);
29494
30391
  if (!date)
@@ -29502,9 +30399,9 @@ function shouldStopPathWalk(rootResolved, parent, cursor) {
29502
30399
  return parent === cursor || !underRoot(rootResolved, parent) && parent !== rootResolved;
29503
30400
  }
29504
30401
  function resolveExistingRealPath(rootResolved, abs, relFile) {
29505
- const rootReal = existsSync11(rootResolved) ? realpathSync5(rootResolved) : rootResolved;
30402
+ const rootReal = existsSync10(rootResolved) ? realpathSync5(rootResolved) : rootResolved;
29506
30403
  let cursor = abs;
29507
- while (!existsSync11(cursor)) {
30404
+ while (!existsSync10(cursor)) {
29508
30405
  const parent = dirname6(cursor);
29509
30406
  if (shouldStopPathWalk(rootResolved, parent, cursor))
29510
30407
  return abs;
@@ -29517,8 +30414,8 @@ function resolveExistingRealPath(rootResolved, abs, relFile) {
29517
30414
  return abs;
29518
30415
  }
29519
30416
  function resolveWritePath(root2, relFile) {
29520
- const rootResolved = resolve6(root2);
29521
- const abs = resolve6(rootResolved, relFile);
30417
+ const rootResolved = resolve5(root2);
30418
+ const abs = resolve5(rootResolved, relFile);
29522
30419
  if (!underRoot(rootResolved, abs)) {
29523
30420
  throw new Error(`Refusing autofix outside repo root: ${relFile}`);
29524
30421
  }
@@ -29545,19 +30442,22 @@ function applyFixes(ctx, options) {
29545
30442
  }
29546
30443
  function parseFixKinds(raw) {
29547
30444
  if (raw === true)
29548
- return ["doc-meta", "anchors"];
30445
+ return ["doc-meta", "anchors", "ssot"];
29549
30446
  switch (raw) {
29550
30447
  case "doc-meta":
29551
30448
  return ["doc-meta"];
29552
30449
  case "anchors":
29553
30450
  return ["anchors"];
30451
+ case "ssot":
30452
+ return ["ssot"];
29554
30453
  default:
29555
- throw new Error(`Unknown --fix kind: ${raw}. Use doc-meta or anchors.`);
30454
+ throw new Error(`Unknown --fix kind: ${raw}. Use doc-meta, anchors, or ssot.`);
29556
30455
  }
29557
30456
  }
29558
30457
  var FIX_KIND_RULE = {
29559
30458
  "doc-meta": "doc-meta",
29560
- anchors: "links"
30459
+ anchors: "links",
30460
+ ssot: "ssot"
29561
30461
  };
29562
30462
  function fixKindsForOnly(kinds, only) {
29563
30463
  if (!only)
@@ -29636,7 +30536,7 @@ function printTextReport(ctx) {
29636
30536
  }
29637
30537
 
29638
30538
  // src/references/check.ts
29639
- import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync10 } from "node:fs";
30539
+ import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
29640
30540
  import { join as join11, relative as relative7 } from "node:path";
29641
30541
 
29642
30542
  // src/references/constants.ts
@@ -29660,11 +30560,11 @@ function isGeneratedReference(content3) {
29660
30560
  }
29661
30561
 
29662
30562
  // src/references/discover.ts
29663
- import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
30563
+ import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync10 } from "node:fs";
29664
30564
  import { join as join10, relative as relative6 } from "node:path";
29665
30565
  function walkMarkdownFiles(dir, root2) {
29666
30566
  const files = [];
29667
- if (!existsSync12(dir))
30567
+ if (!existsSync11(dir))
29668
30568
  return files;
29669
30569
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
29670
30570
  if (entry.name.startsWith("."))
@@ -29681,7 +30581,7 @@ function walkMarkdownFiles(dir, root2) {
29681
30581
  return files;
29682
30582
  }
29683
30583
  function canonicalExists(root2, refPath) {
29684
- return existsSync12(join10(root2, CANONICAL_REFS_DIR, refPath));
30584
+ return existsSync11(join10(root2, CANONICAL_REFS_DIR, refPath));
29685
30585
  }
29686
30586
  function findSharedRefLinks(content3, sourceFile) {
29687
30587
  const links = [];
@@ -29725,7 +30625,7 @@ function findLocalCanonicalLinks(root2, content3, sourceFile) {
29725
30625
  return links;
29726
30626
  }
29727
30627
  function collectLinksForFile(root2, relFile) {
29728
- const content3 = readFileSync9(join10(root2, relFile), "utf8");
30628
+ const content3 = readFileSync10(join10(root2, relFile), "utf8");
29729
30629
  if (isGeneratedReference(content3))
29730
30630
  return [];
29731
30631
  return [
@@ -29740,7 +30640,7 @@ function expandTransitiveRefs(input) {
29740
30640
  const refPath = queue.pop();
29741
30641
  if (!(refPath && canonicalExists(root2, refPath)))
29742
30642
  continue;
29743
- const canonicalContent = readFileSync9(join10(root2, CANONICAL_REFS_DIR, refPath), "utf8");
30643
+ const canonicalContent = readFileSync10(join10(root2, CANONICAL_REFS_DIR, refPath), "utf8");
29744
30644
  const syntheticSource = generatedRefPath(slug2, refPath);
29745
30645
  for (const link2 of findLocalCanonicalLinks(root2, canonicalContent, syntheticSource)) {
29746
30646
  if (refPaths.has(link2.refPath))
@@ -29753,7 +30653,7 @@ function expandTransitiveRefs(input) {
29753
30653
  }
29754
30654
  function planForSkill(root2, slug2) {
29755
30655
  const skillDir = join10(root2, slug2);
29756
- if (!existsSync12(join10(skillDir, "SKILL.md")))
30656
+ if (!existsSync11(join10(skillDir, "SKILL.md")))
29757
30657
  return null;
29758
30658
  const refPaths = new Set;
29759
30659
  const links = [];
@@ -29804,7 +30704,7 @@ function rewriteSharedRefLinks(content3, sourceFile, skill) {
29804
30704
 
29805
30705
  // src/references/check.ts
29806
30706
  function walkMarkdown(dir, onFile) {
29807
- if (!existsSync13(dir))
30707
+ if (!existsSync12(dir))
29808
30708
  return;
29809
30709
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
29810
30710
  if (entry.name.startsWith("."))
@@ -29822,7 +30722,7 @@ function walkMarkdown(dir, onFile) {
29822
30722
  function listAllGeneratedFiles(root2) {
29823
30723
  const files = [];
29824
30724
  walkMarkdown(root2, (fullPath) => {
29825
- const content3 = readFileSync10(fullPath, "utf8");
30725
+ const content3 = readFileSync11(fullPath, "utf8");
29826
30726
  if (isGeneratedReference(content3)) {
29827
30727
  files.push(normalizeRelPath(relative7(root2, fullPath)));
29828
30728
  }
@@ -29831,20 +30731,20 @@ function listAllGeneratedFiles(root2) {
29831
30731
  }
29832
30732
  function checkNeededCopy(root2, targetRel) {
29833
30733
  const targetPath = join11(root2, targetRel);
29834
- if (!existsSync13(targetPath)) {
30734
+ if (!existsSync12(targetPath)) {
29835
30735
  return issue("generated-references", targetRel, "missing generated copy — run skeleton references sync");
29836
30736
  }
29837
- const generated = readFileSync10(targetPath, "utf8");
30737
+ const generated = readFileSync11(targetPath, "utf8");
29838
30738
  if (!isGeneratedReference(generated)) {
29839
30739
  return issue("generated-references", targetRel, "expected generated-reference provenance header");
29840
30740
  }
29841
30741
  const body = stripGeneratedHeader(generated);
29842
30742
  const sourceRel = normalizeRelPath(generated.match(/source: ([^\n]+)/)?.[1] ?? join11(CANONICAL_REFS_DIR, targetRel.split("/references/")[1] ?? ""));
29843
30743
  const canonicalPath = join11(root2, sourceRel);
29844
- if (!existsSync13(canonicalPath)) {
30744
+ if (!existsSync12(canonicalPath)) {
29845
30745
  return issue("generated-references", targetRel, `canonical source missing: ${sourceRel}`);
29846
30746
  }
29847
- const canonical = readFileSync10(canonicalPath, "utf8");
30747
+ const canonical = readFileSync11(canonicalPath, "utf8");
29848
30748
  if (body !== canonical) {
29849
30749
  return issue("generated-references", targetRel, "stale generated copy — run skeleton references sync");
29850
30750
  }
@@ -29865,7 +30765,7 @@ function checkStaleSharedLinks(root2, skillDir) {
29865
30765
  const issues = [];
29866
30766
  walkMarkdown(skillDir, (fullPath) => {
29867
30767
  const relFile = normalizeRelPath(relative7(root2, fullPath));
29868
- const content3 = readFileSync10(fullPath, "utf8");
30768
+ const content3 = readFileSync11(fullPath, "utf8");
29869
30769
  if (!content3.match(SHARED_REF_LINK_RE))
29870
30770
  return;
29871
30771
  issues.push(issue("generated-references", relFile, "still links to shared root references/ — run skeleton references sync"));
@@ -29875,7 +30775,7 @@ function checkStaleSharedLinks(root2, skillDir) {
29875
30775
  function runGeneratedReferencesCheck(root2, ownership) {
29876
30776
  const issues = [];
29877
30777
  const canonicalDir = join11(root2, CANONICAL_REFS_DIR);
29878
- if (!existsSync13(canonicalDir))
30778
+ if (!existsSync12(canonicalDir))
29879
30779
  return issues;
29880
30780
  const skillIndex = buildSkillIndex(root2, ownership);
29881
30781
  const plans = discoverSkillReferencePlans(root2, ownership);
@@ -29910,14 +30810,14 @@ function runBannedRule(ctx) {
29910
30810
  const issues = [];
29911
30811
  for (const abs of collectBannedFiles(ctx.config, ctx.root)) {
29912
30812
  const rel = relPath(abs, ctx.root);
29913
- issues.push(issue("banned", rel, "file matches scan.banned — must not exist in repo"));
30813
+ issues.push(issue("banned", rel, "file matches deny.paths — must not exist in repo"));
29914
30814
  }
29915
30815
  return issues;
29916
30816
  }
29917
30817
  var bannedRule = { id: "banned", run: runBannedRule };
29918
30818
 
29919
30819
  // src/audit/rules/doc-meta.ts
29920
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
30820
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
29921
30821
  import { join as join12 } from "node:path";
29922
30822
  function checkDocMetaBanner(relPath2, content3) {
29923
30823
  if (DOC_META_RE.test(content3))
@@ -29969,9 +30869,9 @@ function runDocMetaRule(ctx) {
29969
30869
  const today = new Date;
29970
30870
  for (const relPath2 of ctx.docMetaPaths) {
29971
30871
  const abs = join12(ctx.root, relPath2);
29972
- if (!existsSync14(abs))
30872
+ if (!existsSync13(abs))
29973
30873
  continue;
29974
- const content3 = readFileSync11(abs, "utf8");
30874
+ const content3 = readFileSync12(abs, "utf8");
29975
30875
  const banner = checkDocMetaBanner(relPath2, content3);
29976
30876
  if (banner) {
29977
30877
  issues.push(banner);
@@ -29999,23 +30899,13 @@ function runDocMetaRule(ctx) {
29999
30899
  var docMetaRule = { id: "doc-meta", run: runDocMetaRule };
30000
30900
 
30001
30901
  // 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";
30902
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "node:fs";
30903
+ import { dirname as dirname7, resolve as resolve6 } from "node:path";
30004
30904
  function resolveLink2(sourceFile, target) {
30005
30905
  const withoutAnchor = target.split("#")[0]?.split("?")[0] ?? "";
30006
30906
  if (!withoutAnchor)
30007
30907
  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
- });
30908
+ return resolve6(dirname7(sourceFile), withoutAnchor);
30019
30909
  }
30020
30910
  function checkMissingSkill(input, relSource) {
30021
30911
  if (!input.target.includes("/SKILL.md"))
@@ -30033,13 +30923,13 @@ function checkAgentFile(input, resolved, relSource) {
30033
30923
  return null;
30034
30924
  }
30035
30925
  const agentPath = resolved.endsWith(".md") ? resolved : `${resolved}.md`;
30036
- if (existsSync15(agentPath))
30926
+ if (existsSync14(agentPath))
30037
30927
  return null;
30038
30928
  return issue("links", relSource, { message: "missing agent file", link: input.linkLabel });
30039
30929
  }
30040
30930
  function checkBrokenPath(ctx) {
30041
30931
  const { input, pathPart, resolved, relSource, relTarget } = ctx;
30042
- if (!(pathPart && !existsSync15(resolved)))
30932
+ if (!(pathPart && !existsSync14(resolved)))
30043
30933
  return null;
30044
30934
  return issue("links", relSource, {
30045
30935
  message: `broken link → ${relTarget}`,
@@ -30048,9 +30938,9 @@ function checkBrokenPath(ctx) {
30048
30938
  }
30049
30939
  function checkBrokenAnchor(ctx) {
30050
30940
  const { input, anchor, resolved, relSource, relTarget } = ctx;
30051
- if (!(anchor && existsSync15(resolved)))
30941
+ if (!(anchor && existsSync14(resolved)))
30052
30942
  return null;
30053
- const targetContent = readFileSync12(resolved, "utf8");
30943
+ const targetContent = readFileSync13(resolved, "utf8");
30054
30944
  const slugs = extractHeadingSlugs(targetContent, resolved);
30055
30945
  const anchorSlug = slugifyAnchor(anchor);
30056
30946
  if (slugs.has(anchorSlug))
@@ -30075,9 +30965,6 @@ function validateTarget(input) {
30075
30965
  if (isPlaceholderLink(target))
30076
30966
  return [];
30077
30967
  const parts = resolveTargetParts(sourceFile, target, ctx.root);
30078
- const retired = checkRetiredSkill(input);
30079
- if (retired)
30080
- return [retired];
30081
30968
  const missingSkill = checkMissingSkill(input, parts.relSource);
30082
30969
  if (missingSkill)
30083
30970
  return [missingSkill];
@@ -30104,6 +30991,331 @@ function runLinksRule(ctx) {
30104
30991
  }
30105
30992
  var linksRule = { id: "links", run: runLinksRule };
30106
30993
 
30994
+ // src/audit/rules/near-duplicate.ts
30995
+ import { readFileSync as readFileSync14 } from "node:fs";
30996
+ import { join as join13 } from "node:path";
30997
+
30998
+ // src/audit/core/ssot-fit.ts
30999
+ var DEFAULT_SSOT_OVERLAP_MIN = 0.35;
31000
+ var DEFAULT_BETTER_MATCH_MARGIN = 0.15;
31001
+ var STOP = new Set([
31002
+ "a",
31003
+ "an",
31004
+ "the",
31005
+ "and",
31006
+ "or",
31007
+ "of",
31008
+ "for",
31009
+ "to",
31010
+ "in",
31011
+ "on",
31012
+ "with",
31013
+ "this",
31014
+ "that",
31015
+ "is",
31016
+ "are",
31017
+ "be",
31018
+ "as",
31019
+ "by",
31020
+ "from",
31021
+ "at",
31022
+ "it",
31023
+ "its"
31024
+ ]);
31025
+ var STEM_BLOCKLIST = new Set([
31026
+ "business",
31027
+ "analysis",
31028
+ "status",
31029
+ "process",
31030
+ "access",
31031
+ "address",
31032
+ "series",
31033
+ "species",
31034
+ "news",
31035
+ "means",
31036
+ "cross",
31037
+ "class",
31038
+ "glass",
31039
+ "less",
31040
+ "success",
31041
+ "progress",
31042
+ "express",
31043
+ "discuss",
31044
+ "focus",
31045
+ "bonus",
31046
+ "basis",
31047
+ "crisis",
31048
+ "thesis",
31049
+ "atlas",
31050
+ "canvas",
31051
+ "campus",
31052
+ "virus",
31053
+ "bus",
31054
+ "gas",
31055
+ "plus",
31056
+ "alias",
31057
+ "bias",
31058
+ "circus",
31059
+ "consensus",
31060
+ "census"
31061
+ ]);
31062
+ function lightStem(token) {
31063
+ if (token.length < 4)
31064
+ return token;
31065
+ if (STEM_BLOCKLIST.has(token))
31066
+ return token;
31067
+ if (token.endsWith("ies") && token.length > 4) {
31068
+ return `${token.slice(0, -3)}y`;
31069
+ }
31070
+ if (token.endsWith("sses") || token.endsWith("ches") || token.endsWith("shes") || token.endsWith("xes")) {
31071
+ return token.slice(0, -2);
31072
+ }
31073
+ if (token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
31074
+ return token.slice(0, -1);
31075
+ }
31076
+ return token;
31077
+ }
31078
+ function contentTokens(text5) {
31079
+ return text5.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length > 1 && !STOP.has(t)).map(lightStem);
31080
+ }
31081
+ function uniqueContentTokens(text5) {
31082
+ return [...new Set(contentTokens(text5))];
31083
+ }
31084
+ function stripCode2(content3) {
31085
+ return content3.replace(/```[\s\S]*?```/g, `
31086
+ `).replace(/`[^`\n]+`/g, " ");
31087
+ }
31088
+ function stripSsotAndMeta(content3) {
31089
+ return content3.replace(/<!--\s*source-of-truth:[\s\S]*?-->/gi, `
31090
+ `).replace(/^\s*source-of-truth:\s*.+$/gim, `
31091
+ `).replace(/^\s*\*\*Source of truth for\*\*\s*.+$/gim, `
31092
+ `).replace(/<!--\s*doc-meta:[\s\S]*?-->/gi, `
31093
+ `);
31094
+ }
31095
+ function extractH1(content3) {
31096
+ const prose = stripCode2(content3);
31097
+ const m = /^#\s+(.+)$/m.exec(prose);
31098
+ return m?.[1]?.trim() ?? "";
31099
+ }
31100
+ function extractLeadParagraph(content3) {
31101
+ const prose = stripSsotAndMeta(stripCode2(content3));
31102
+ const lines = prose.split(/\n/);
31103
+ const chunks = [];
31104
+ let buf = [];
31105
+ const flush = () => {
31106
+ const t = buf.join(" ").trim();
31107
+ if (t)
31108
+ chunks.push(t);
31109
+ buf = [];
31110
+ };
31111
+ for (const line of lines) {
31112
+ const trimmed = line.trim();
31113
+ if (!trimmed || trimmed.startsWith("#")) {
31114
+ flush();
31115
+ continue;
31116
+ }
31117
+ if (/^[-*|]/.test(trimmed) && chunks.length === 0 && buf.length === 0) {
31118
+ buf.push(trimmed.replace(/^[-*|]+\s*/, ""));
31119
+ continue;
31120
+ }
31121
+ buf.push(trimmed);
31122
+ }
31123
+ flush();
31124
+ return chunks[0] ?? "";
31125
+ }
31126
+ function buildEvidenceText(content3) {
31127
+ const h1 = extractH1(content3);
31128
+ const lead = extractLeadParagraph(content3);
31129
+ const body = stripSsotAndMeta(stripCode2(content3));
31130
+ return [h1, lead, body].filter(Boolean).join(`
31131
+
31132
+ `);
31133
+ }
31134
+ function ssotEvidenceOverlap(summary, evidence) {
31135
+ const st = uniqueContentTokens(summary);
31136
+ if (st.length === 0)
31137
+ return 0;
31138
+ const ev = new Set(contentTokens(evidence));
31139
+ let hit = 0;
31140
+ for (const t of st) {
31141
+ if (ev.has(t))
31142
+ hit++;
31143
+ }
31144
+ return hit / st.length;
31145
+ }
31146
+ function longestSummaryPhrase(summary) {
31147
+ const toks = contentTokens(summary);
31148
+ if (toks.length >= 3)
31149
+ return toks.slice(0, 3);
31150
+ if (toks.length >= 2)
31151
+ return toks.slice(0, 2);
31152
+ return null;
31153
+ }
31154
+ function evidenceHasPhrase(evidence, phrase) {
31155
+ if (phrase.length === 0)
31156
+ return true;
31157
+ const ev = contentTokens(evidence);
31158
+ const needle = phrase.join(" ");
31159
+ for (let i = 0;i <= ev.length - phrase.length; i++) {
31160
+ if (ev.slice(i, i + phrase.length).join(" ") === needle)
31161
+ return true;
31162
+ }
31163
+ return false;
31164
+ }
31165
+ function evaluateSsotFit(files, options = {}) {
31166
+ const overlapMin = options.overlapMin ?? DEFAULT_SSOT_OVERLAP_MIN;
31167
+ const margin = options.betterMatchMargin ?? DEFAULT_BETTER_MATCH_MARGIN;
31168
+ const phraseCheck = options.phraseCheck !== false;
31169
+ const prepared = files.map((f) => {
31170
+ const evidence = buildEvidenceText(f.content);
31171
+ const overlap = ssotEvidenceOverlap(f.summary, evidence);
31172
+ const summaryToks = uniqueContentTokens(f.summary);
31173
+ return { ...f, evidence, overlap, summaryToks };
31174
+ });
31175
+ const issues = [];
31176
+ for (const row of prepared) {
31177
+ if (row.summaryToks.length < 2) {
31178
+ issues.push({
31179
+ kind: "short",
31180
+ path: row.path,
31181
+ message: `source-of-truth summary too short to verify against body ("${row.summary}")`
31182
+ });
31183
+ continue;
31184
+ }
31185
+ if (row.overlap < overlapMin) {
31186
+ let message = `source-of-truth summary weakly matches this paper (token overlap ${row.overlap.toFixed(2)} < ${overlapMin})`;
31187
+ if (phraseCheck) {
31188
+ const phrase = longestSummaryPhrase(row.summary);
31189
+ if (phrase && !evidenceHasPhrase(row.evidence, phrase)) {
31190
+ message += ` — key phrase "${phrase.join(" ")}" not found in H1/lead/body`;
31191
+ }
31192
+ }
31193
+ issues.push({ kind: "weak", path: row.path, message });
31194
+ let best = null;
31195
+ for (const other of prepared) {
31196
+ if (other.path === row.path)
31197
+ continue;
31198
+ const cross = ssotEvidenceOverlap(row.summary, other.evidence);
31199
+ if (cross < overlapMin)
31200
+ continue;
31201
+ if (cross < row.overlap + margin)
31202
+ continue;
31203
+ if (!best || cross > best.overlap)
31204
+ best = { path: other.path, overlap: cross };
31205
+ }
31206
+ if (best) {
31207
+ issues.push({
31208
+ kind: "better-match",
31209
+ path: row.path,
31210
+ otherPath: best.path,
31211
+ 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`
31212
+ });
31213
+ }
31214
+ }
31215
+ }
31216
+ return issues;
31217
+ }
31218
+
31219
+ // src/audit/rules/near-duplicate.ts
31220
+ var DEFAULT_THRESHOLD = 0.72;
31221
+ var SHINGLE_N = 3;
31222
+ function tokenize2(text5) {
31223
+ return contentTokens(text5);
31224
+ }
31225
+ function normalizeSummary(summary) {
31226
+ return contentTokens(summary).join(" ");
31227
+ }
31228
+ function shingles(tokens, n) {
31229
+ const out = new Set;
31230
+ if (tokens.length < n) {
31231
+ if (tokens.length > 0)
31232
+ out.add(tokens.join(" "));
31233
+ return out;
31234
+ }
31235
+ for (let i = 0;i <= tokens.length - n; i++) {
31236
+ out.add(tokens.slice(i, i + n).join(" "));
31237
+ }
31238
+ return out;
31239
+ }
31240
+ function jaccard2(a, b) {
31241
+ if (a.size === 0 && b.size === 0)
31242
+ return 1;
31243
+ if (a.size === 0 || b.size === 0)
31244
+ return 0;
31245
+ let inter = 0;
31246
+ for (const x of a) {
31247
+ if (b.has(x))
31248
+ inter++;
31249
+ }
31250
+ return inter / (a.size + b.size - inter);
31251
+ }
31252
+ function pairKey(a, b) {
31253
+ return a < b ? `${a}::${b}` : `${b}::${a}`;
31254
+ }
31255
+ function ignoredPairSet(ctx) {
31256
+ const pairs = ctx.config.docsLint?.ignorePairs ?? [];
31257
+ const set = new Set;
31258
+ for (const pair of pairs) {
31259
+ if (!pair || pair.length < 2)
31260
+ continue;
31261
+ const a = normalizeRelPath(pair[0] ?? "");
31262
+ const b = normalizeRelPath(pair[1] ?? "");
31263
+ if (a && b)
31264
+ set.add(pairKey(a, b));
31265
+ }
31266
+ return set;
31267
+ }
31268
+ function isIgnoredGlob(rel, globs) {
31269
+ return globs.some((g) => matchesGlobScope(rel, g));
31270
+ }
31271
+ function bodyWithoutSsotNoise(content3) {
31272
+ 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, " ");
31273
+ }
31274
+ function eligibleEntries(ctx) {
31275
+ const globs = ctx.config.docsLint?.ignoreGlobs ?? [];
31276
+ return ctx.ssotEntries.filter((e) => !isIgnoredGlob(e.path, globs));
31277
+ }
31278
+ function runNearDuplicateRule(ctx) {
31279
+ const issues = [];
31280
+ const threshold = ctx.config.docsLint?.nearDuplicateThreshold ?? DEFAULT_THRESHOLD;
31281
+ const ignored = ignoredPairSet(ctx);
31282
+ const entries = eligibleEntries(ctx);
31283
+ const fingerprints = entries.map((e) => {
31284
+ const content3 = readFileSync14(join13(ctx.root, e.path), "utf8");
31285
+ const tokens = tokenize2(bodyWithoutSsotNoise(content3));
31286
+ return {
31287
+ path: e.path,
31288
+ summaryKey: normalizeSummary(e.summary),
31289
+ set: shingles(tokens, SHINGLE_N)
31290
+ };
31291
+ });
31292
+ for (let i = 0;i < fingerprints.length; i++) {
31293
+ for (let j = i + 1;j < fingerprints.length; j++) {
31294
+ const a = fingerprints[i];
31295
+ const b = fingerprints[j];
31296
+ if (!(a && b))
31297
+ continue;
31298
+ if (ignored.has(pairKey(a.path, b.path)))
31299
+ continue;
31300
+ const score = jaccard2(a.set, b.set);
31301
+ if (score >= threshold) {
31302
+ issues.push(issue("near-duplicate", a.path, {
31303
+ message: `near-duplicate of ${b.path} (shingle Jaccard ${score.toFixed(2)} ≥ ${threshold})`,
31304
+ severity: "warning"
31305
+ }));
31306
+ }
31307
+ if (a.summaryKey && a.summaryKey === b.summaryKey) {
31308
+ issues.push(issue("near-duplicate", a.path, {
31309
+ message: `duplicate source-of-truth summary also used by ${b.path}`,
31310
+ severity: "warning"
31311
+ }));
31312
+ }
31313
+ }
31314
+ }
31315
+ return issues;
31316
+ }
31317
+ var nearDuplicateRule = { id: "near-duplicate", run: runNearDuplicateRule };
31318
+
30107
31319
  // src/audit/rules/prose-policy.ts
30108
31320
  function checkDraftEntry(input) {
30109
31321
  const { rel, lines, entry, draftPrefixes } = input;
@@ -30164,53 +31376,6 @@ function runProsePolicyRule(ctx) {
30164
31376
  }
30165
31377
  var prosePolicyRule = { id: "prose-policy", run: runProsePolicyRule };
30166
31378
 
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
31379
  // src/audit/rules/scan-gaps.ts
30215
31380
  function runCoverageGapsRule(ctx) {
30216
31381
  const exclude = [...COVERAGE_BUILTIN_EXCLUDES, ...ctx.config.scan.exclude];
@@ -30221,7 +31386,7 @@ function runCoverageGapsRule(ctx) {
30221
31386
  if (scanned.has(rel))
30222
31387
  continue;
30223
31388
  issues.push(issue("coverage-gaps", rel, {
30224
- message: "markdown outside audit scan perimeter — extend .skeleton/config.yaml scan.include",
31389
+ message: "markdown outside audit scan perimeter — extend skeleton.toml (or legacy .skeleton/config.yaml) scan.include",
30225
31390
  severity: "warning"
30226
31391
  }));
30227
31392
  }
@@ -30240,11 +31405,11 @@ function runScanRootsRule(ctx) {
30240
31405
  var scanRootsRule = { id: "scan-roots", run: runScanRootsRule };
30241
31406
 
30242
31407
  // src/audit/rules/skill-index.ts
30243
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "node:fs";
31408
+ import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync15 } from "node:fs";
30244
31409
  import { join as join14, relative as relative8 } from "node:path";
30245
31410
  function walkSkillMarkdown(dir) {
30246
31411
  const files = [];
30247
- if (!existsSync17(dir))
31412
+ if (!existsSync15(dir))
30248
31413
  return files;
30249
31414
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
30250
31415
  if (entry.name.startsWith("."))
@@ -30276,17 +31441,13 @@ function parseReadmeTaxonomySlugs(content3) {
30276
31441
  function scanFileForSkillLinks(ctx, filePath, index2) {
30277
31442
  const issues = [];
30278
31443
  const rel = relative8(ctx.root, filePath).replace(/\\/g, "/");
30279
- const content3 = readFileSync14(filePath, "utf8");
31444
+ const content3 = readFileSync15(filePath, "utf8");
30280
31445
  if (isGeneratedReference(content3))
30281
31446
  return issues;
30282
31447
  for (const match of content3.matchAll(SKILL_LINK_RE)) {
30283
31448
  const slug2 = match[1];
30284
31449
  if (!slug2)
30285
31450
  continue;
30286
- if (ctx.retiredSkills.has(slug2)) {
30287
- issues.push(issue("skill-index", rel, `references retired skill "${slug2}/SKILL.md"`));
30288
- continue;
30289
- }
30290
31451
  if (!resolveSkillPath(index2, ctx.root, slug2)) {
30291
31452
  issues.push(issue("skill-index", rel, `links missing skill "${slug2}/SKILL.md"`));
30292
31453
  }
@@ -30296,13 +31457,13 @@ function scanFileForSkillLinks(ctx, filePath, index2) {
30296
31457
  function taxonomyIssuesForReadme(input) {
30297
31458
  const { ctx, index: index2, skillRoot, diskSlugs, nonPublic } = input;
30298
31459
  const readmePath = join14(ctx.root, skillRoot.relPath, "README.md");
30299
- if (!existsSync17(readmePath))
31460
+ if (!existsSync15(readmePath))
30300
31461
  return [];
30301
- const readme = readFileSync14(readmePath, "utf8");
31462
+ const readme = readFileSync15(readmePath, "utf8");
30302
31463
  if (!readme.includes("## Taxonomy"))
30303
31464
  return [];
30304
31465
  const taxonomySlugs = parseReadmeTaxonomySlugs(readme);
30305
- const nestedSlugs = diskSlugs.filter((slug2) => existsSync17(join14(ctx.root, skillRoot.relPath, slug2, "SKILL.md")));
31466
+ const nestedSlugs = diskSlugs.filter((slug2) => existsSync15(join14(ctx.root, skillRoot.relPath, slug2, "SKILL.md")));
30306
31467
  const foreign = new Set(index2.foreignSlugs);
30307
31468
  const publicSlugs = nestedSlugs.filter((slug2) => !(nonPublic.has(slug2) || foreign.has(slug2)));
30308
31469
  const relReadme = `${skillRoot.relPath}/README.md`;
@@ -30338,7 +31499,7 @@ function auditSkillRoot(input) {
30338
31499
  const base = skillRoot.kind === "nested" ? join14(ctx.root, skillRoot.relPath) : ctx.root;
30339
31500
  for (const slug2 of slugsForRoot(skillRoot, index2, owned)) {
30340
31501
  const skillDir = join14(base, slug2);
30341
- if (!existsSync17(skillDir))
31502
+ if (!existsSync15(skillDir))
30342
31503
  continue;
30343
31504
  for (const skillMd of walkSkillMarkdown(skillDir)) {
30344
31505
  issues.push(...scanFileForSkillLinks(ctx, skillMd, index2));
@@ -30377,10 +31538,46 @@ function skillAuditSuffix(ctx) {
30377
31538
  return ` (${owned} owned skills audited, ${foreign} foreign ignored)`;
30378
31539
  }
30379
31540
 
31541
+ // src/audit/rules/ssot.ts
31542
+ function runSsotRule(ctx) {
31543
+ const issues = [];
31544
+ for (const err of ctx.ssotErrors) {
31545
+ issues.push(issue("ssot", err.path, err.detail));
31546
+ }
31547
+ return issues;
31548
+ }
31549
+ var ssotRule = { id: "ssot", run: runSsotRule };
31550
+
31551
+ // src/audit/rules/ssot-summary.ts
31552
+ import { readFileSync as readFileSync16 } from "node:fs";
31553
+ import { join as join15 } from "node:path";
31554
+ function runSsotSummaryRule(ctx) {
31555
+ const overlapMin = ctx.config.docsLint?.ssotOverlapMin ?? DEFAULT_SSOT_OVERLAP_MIN;
31556
+ const margin = ctx.config.docsLint?.ssotBetterMatchMargin ?? DEFAULT_BETTER_MATCH_MARGIN;
31557
+ const phraseCheck = ctx.config.docsLint?.ssotPhraseCheck !== false;
31558
+ const files = ctx.ssotEntries.map((entry) => ({
31559
+ path: entry.path,
31560
+ summary: entry.summary,
31561
+ content: readFileSync16(join15(ctx.root, entry.path), "utf8")
31562
+ }));
31563
+ return evaluateSsotFit(files, {
31564
+ overlapMin,
31565
+ betterMatchMargin: margin,
31566
+ phraseCheck
31567
+ }).map((fit) => issue("ssot-summary", fit.path, {
31568
+ message: fit.message,
31569
+ severity: "warning",
31570
+ link: fit.otherPath
31571
+ }));
31572
+ }
31573
+ var ssotSummaryRule = { id: "ssot-summary", run: runSsotSummaryRule };
31574
+
30380
31575
  // src/audit/rules/index.ts
30381
31576
  var docsRules = [
30382
31577
  { ...scanRootsRule, global: true },
30383
- { ...registryRule, global: true },
31578
+ { ...ssotRule, global: true },
31579
+ { ...nearDuplicateRule, global: true },
31580
+ { ...ssotSummaryRule, global: true },
30384
31581
  { ...coverageGapsRule, global: true },
30385
31582
  linksRule,
30386
31583
  docMetaRule,
@@ -30444,8 +31641,8 @@ function rulesForSuite(suite, pluginRules = []) {
30444
31641
  function parseFixArg(argv, index2) {
30445
31642
  const next = argv[index2 + 1];
30446
31643
  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.`);
31644
+ if (next !== "doc-meta" && next !== "anchors" && next !== "ssot") {
31645
+ throw new Error(`Unknown --fix kind: ${next}. Use --fix, --fix=doc-meta, --fix=anchors, or --fix=ssot.`);
30449
31646
  }
30450
31647
  return { fix: next, nextIndex: index2 + 1 };
30451
31648
  }
@@ -30536,7 +31733,7 @@ async function runAuditFixes(options, ctx, loaded) {
30536
31733
  }
30537
31734
  const kinds = fixKindsForOnly(parseFixKinds(options.fix), options.only);
30538
31735
  if (kinds.length === 0) {
30539
- console.error("--fix has no overlapping rules with --only (doc-meta → doc-meta, anchors → links).");
31736
+ console.error("--fix has no overlapping rules with --only (doc-meta → doc-meta, anchors → links, ssot → ssot).");
30540
31737
  return 1;
30541
31738
  }
30542
31739
  applyFixes(ctx, { kinds, dryRun: options.dryRun });
@@ -30580,41 +31777,89 @@ async function runAudit(options) {
30580
31777
  });
30581
31778
  }
30582
31779
 
31780
+ // src/catalog.ts
31781
+ import { existsSync as existsSync16, mkdirSync, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "node:fs";
31782
+ import { dirname as dirname8, join as join16 } from "node:path";
31783
+ function renderCatalog(entries) {
31784
+ const lines = [
31785
+ "# Skeleton catalog",
31786
+ "",
31787
+ "Generated by `skeleton catalog`. Gitignored — regenerate locally; do not commit.",
31788
+ "",
31789
+ "Agents: skim this list, then read only the SSOT line / first ~20 lines of a candidate before opening the full paper.",
31790
+ "",
31791
+ "| Summary | Path |",
31792
+ "| ------- | ---- |"
31793
+ ];
31794
+ for (const e of entries) {
31795
+ const summary = e.summary.replace(/\|/g, "\\|");
31796
+ lines.push(`| ${summary} | [${e.path}](../${e.path}) |`);
31797
+ }
31798
+ lines.push("");
31799
+ return lines.join(`
31800
+ `);
31801
+ }
31802
+ function buildCatalogContent(root2) {
31803
+ const config = loadConfig(root2);
31804
+ const skillIndex = buildSkillIndex(root2, config.skillOwnership);
31805
+ const files = collectScanFiles(config, root2, skillIndex);
31806
+ const { entries } = collectSsotEntries(files, root2);
31807
+ return { content: renderCatalog(entries), entries };
31808
+ }
31809
+ function checkCatalog(root2) {
31810
+ const { content: expected, entries } = buildCatalogContent(root2);
31811
+ const abs = join16(root2, CATALOG_REL_PATH);
31812
+ if (!existsSync16(abs)) {
31813
+ return { ok: false, missing: true, stale: false, expected, actual: null, entries };
31814
+ }
31815
+ const actual = readFileSync17(abs, "utf8");
31816
+ const stale = actual !== expected;
31817
+ return { ok: !stale, missing: false, stale, expected, actual, entries };
31818
+ }
31819
+ function writeCatalog(root2) {
31820
+ const { content: content3, entries } = buildCatalogContent(root2);
31821
+ const abs = join16(root2, CATALOG_REL_PATH);
31822
+ mkdirSync(dirname8(abs), { recursive: true });
31823
+ writeFileSync2(abs, content3, "utf8");
31824
+ return { path: normalizeRelPath(CATALOG_REL_PATH), entries };
31825
+ }
31826
+ function runCatalogCli(options = {}) {
31827
+ const root2 = options.root ?? findRepoRoot();
31828
+ if (options.check) {
31829
+ const result = checkCatalog(root2);
31830
+ if (result.missing) {
31831
+ console.error(`catalog: missing ${CATALOG_REL_PATH} — run \`skeleton catalog\``);
31832
+ return 0;
31833
+ }
31834
+ if (result.stale) {
31835
+ console.error(`catalog: ${CATALOG_REL_PATH} is outdated — run \`skeleton catalog\``);
31836
+ return 0;
31837
+ }
31838
+ console.log(`catalog: ${CATALOG_REL_PATH} up to date (${result.entries.length} entries)`);
31839
+ return 0;
31840
+ }
31841
+ const written = writeCatalog(root2);
31842
+ console.log(`catalog: wrote ${written.path} (${written.entries.length} entries)`);
31843
+ return 0;
31844
+ }
31845
+
30583
31846
  // 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: ";
31847
+ import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
31848
+ import { basename as basename3, join as join17, relative as relative9 } from "node:path";
30587
31849
  function customizeDir(root2) {
30588
- return join15(root2, REGISTRY_DIR_REL, "customize");
31850
+ return join17(root2, REGISTRY_DIR_REL, "customize");
30589
31851
  }
30590
31852
  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;
31853
+ return join17(customizeDir(root2), `${slug2}.md`);
30601
31854
  }
30602
31855
  function resolveSlugFile(root2, slug2) {
30603
31856
  const direct = customizePathForSlug(root2, slug2);
30604
- if (existsSync18(direct)) {
31857
+ if (existsSync17(direct)) {
30605
31858
  return {
30606
- content: readFileSync15(direct, "utf8"),
31859
+ content: readFileSync18(direct, "utf8"),
30607
31860
  path: normalizeRelPath(relative9(root2, direct))
30608
31861
  };
30609
31862
  }
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
31863
  return { content: null, path: null };
30619
31864
  }
30620
31865
  function alwaysIncludeBasenames(root2) {
@@ -30633,10 +31878,10 @@ function readAlwaysInclude(root2, basenames, skipBasename) {
30633
31878
  const file = basename3(name);
30634
31879
  if (skipBasename && file === skipBasename)
30635
31880
  continue;
30636
- const abs = join15(dir, file);
30637
- if (!existsSync18(abs))
31881
+ const abs = join17(dir, file);
31882
+ if (!existsSync17(abs))
30638
31883
  continue;
30639
- parts.push(readFileSync15(abs, "utf8").trimEnd());
31884
+ parts.push(readFileSync18(abs, "utf8").trimEnd());
30640
31885
  paths.push(normalizeRelPath(relative9(root2, abs)));
30641
31886
  }
30642
31887
  return { parts, paths };
@@ -30760,39 +32005,39 @@ Customize override for /${slug2} (from ${from}):
30760
32005
 
30761
32006
  // src/init/init.ts
30762
32007
  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";
32008
+ import { copyFileSync, existsSync as existsSync21, mkdirSync as mkdirSync3, readFileSync as readFileSync20 } from "node:fs";
32009
+ import { join as join21 } from "node:path";
30765
32010
  import process4 from "node:process";
30766
32011
 
30767
32012
  // 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";
32013
+ import { existsSync as existsSync20, mkdirSync as mkdirSync2, readFileSync as readFileSync19, writeFileSync as writeFileSync3 } from "node:fs";
32014
+ import { dirname as dirname11, join as join20 } from "node:path";
30770
32015
 
30771
32016
  // src/init/package-paths.ts
30772
- import { existsSync as existsSync19 } from "node:fs";
30773
- import { dirname as dirname8, join as join16 } from "node:path";
32017
+ import { existsSync as existsSync18 } from "node:fs";
32018
+ import { dirname as dirname9, join as join18 } from "node:path";
30774
32019
  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, "..")];
32020
+ var MODULE_DIR = dirname9(fileURLToPath5(import.meta.url));
32021
+ var PACKAGE_ROOT_CANDIDATES = [join18(MODULE_DIR, "../.."), join18(MODULE_DIR, "..")];
30777
32022
  function resolvePackageRoot() {
30778
32023
  for (const candidate of PACKAGE_ROOT_CANDIDATES) {
30779
- if (existsSync19(join16(candidate, "package.json")))
32024
+ if (existsSync18(join18(candidate, "package.json")))
30780
32025
  return candidate;
30781
32026
  }
30782
32027
  throw new Error("Could not resolve @csark0812/skeleton package root");
30783
32028
  }
30784
32029
  function resolveTemplatesDir() {
30785
- const dir = join16(resolvePackageRoot(), "templates/skeleton-init");
30786
- if (!existsSync19(dir)) {
32030
+ const dir = join18(resolvePackageRoot(), "templates/skeleton-init");
32031
+ if (!existsSync18(dir)) {
30787
32032
  throw new Error("Missing templates/skeleton-init in package");
30788
32033
  }
30789
32034
  return dir;
30790
32035
  }
30791
32036
 
30792
32037
  // src/init/resolve-hook-command.ts
30793
- import { existsSync as existsSync20, realpathSync as realpathSync6 } from "node:fs";
32038
+ import { existsSync as existsSync19, realpathSync as realpathSync6 } from "node:fs";
30794
32039
  import { createRequire as createRequire3 } from "node:module";
30795
- import { dirname as dirname9, join as join17, relative as relative10, resolve as resolve8 } from "node:path";
32040
+ import { dirname as dirname10, join as join19, relative as relative10, resolve as resolve7 } from "node:path";
30796
32041
  var PACKAGE_NAME = "@csark0812/skeleton";
30797
32042
  var CLI_DIST = "dist/cli.js";
30798
32043
  var PACKAGE_ROOT = resolvePackageRoot();
@@ -30811,7 +32056,7 @@ function toRepoRelative(cwd, absPath) {
30811
32056
  }
30812
32057
  function tryResolvePublishedCli(cwd) {
30813
32058
  try {
30814
- const req = createRequire3(join17(cwd, "package.json"));
32059
+ const req = createRequire3(join19(cwd, "package.json"));
30815
32060
  return req.resolve(`${PACKAGE_NAME}/${CLI_DIST}`);
30816
32061
  } catch {
30817
32062
  return null;
@@ -30820,10 +32065,10 @@ function tryResolvePublishedCli(cwd) {
30820
32065
  function walkNodeModulesCli(cwd) {
30821
32066
  let dir = cwd;
30822
32067
  while (true) {
30823
- const candidate = join17(dir, "node_modules", PACKAGE_NAME, CLI_DIST);
30824
- if (existsSync20(candidate))
32068
+ const candidate = join19(dir, "node_modules", PACKAGE_NAME, CLI_DIST);
32069
+ if (existsSync19(candidate))
30825
32070
  return candidate;
30826
- const parent = dirname9(dir);
32071
+ const parent = dirname10(dir);
30827
32072
  if (parent === dir)
30828
32073
  break;
30829
32074
  dir = parent;
@@ -30831,7 +32076,7 @@ function walkNodeModulesCli(cwd) {
30831
32076
  return null;
30832
32077
  }
30833
32078
  function isInsidePackageRoot(cwd) {
30834
- const rel = relative10(PACKAGE_ROOT, resolve8(cwd)).replace(/\\/g, "/");
32079
+ const rel = relative10(PACKAGE_ROOT, resolve7(cwd)).replace(/\\/g, "/");
30835
32080
  return rel === "" || !(rel.startsWith("..") || rel.startsWith("/"));
30836
32081
  }
30837
32082
  function nodeCliHookCommand(cliPath) {
@@ -30862,21 +32107,21 @@ function identityKey(platform, event, matcher) {
30862
32107
  return `skeleton:customize:${platform}:${event}:${matcher}`;
30863
32108
  }
30864
32109
  function loadFragment(name, hookCommand) {
30865
- const raw = readFileSync16(join18(TEMPLATES_DIR, name), "utf8");
32110
+ const raw = readFileSync19(join20(TEMPLATES_DIR, name), "utf8");
30866
32111
  return JSON.parse(raw.replaceAll("{{HOOK_COMMAND}}", hookCommand));
30867
32112
  }
30868
32113
  function readJson(path2) {
30869
- if (!existsSync21(path2))
32114
+ if (!existsSync20(path2))
30870
32115
  return null;
30871
32116
  try {
30872
- return JSON.parse(readFileSync16(path2, "utf8"));
32117
+ return JSON.parse(readFileSync19(path2, "utf8"));
30873
32118
  } catch (error) {
30874
32119
  throw new Error(`Invalid JSON in ${path2}: ${error}`);
30875
32120
  }
30876
32121
  }
30877
32122
  function writeJson(path2, value) {
30878
- mkdirSync(dirname10(path2), { recursive: true });
30879
- writeFileSync2(path2, `${JSON.stringify(value, null, 2)}
32123
+ mkdirSync2(dirname11(path2), { recursive: true });
32124
+ writeFileSync3(path2, `${JSON.stringify(value, null, 2)}
30880
32125
  `, "utf8");
30881
32126
  }
30882
32127
  function deepEqual(a, b) {
@@ -31029,10 +32274,10 @@ function mergeNestedHooks(args) {
31029
32274
  }
31030
32275
  function mergeHookConfigs(opts) {
31031
32276
  const results = [];
31032
- const cursorPath = join18(opts.cwd, ".cursor/hooks.json");
32277
+ const cursorPath = join20(opts.cwd, ".cursor/hooks.json");
31033
32278
  const cursorFragment = loadFragment("cursor-hooks.fragment.json", opts.hookCommand);
31034
32279
  results.push(mergeCursorHooks(cursorPath, cursorFragment, opts));
31035
- const claudePath = join18(opts.cwd, ".claude/settings.json");
32280
+ const claudePath = join20(opts.cwd, ".claude/settings.json");
31036
32281
  const claudeFragment = loadFragment("claude-settings.fragment.json", opts.hookCommand);
31037
32282
  results.push(mergeNestedHooks({
31038
32283
  platform: "claude",
@@ -31041,8 +32286,8 @@ function mergeHookConfigs(opts) {
31041
32286
  eventName: "PostToolUse",
31042
32287
  opts
31043
32288
  }));
31044
- const codexPath = join18(opts.cwd, ".codex/hooks.json");
31045
- if (existsSync21(join18(opts.cwd, ".codex"))) {
32289
+ const codexPath = join20(opts.cwd, ".codex/hooks.json");
32290
+ if (existsSync20(join20(opts.cwd, ".codex"))) {
31046
32291
  const codexFragment = loadFragment("codex-hooks.fragment.json", opts.hookCommand);
31047
32292
  results.push(mergeNestedHooks({
31048
32293
  platform: "codex",
@@ -31057,11 +32302,11 @@ function mergeHookConfigs(opts) {
31057
32302
  return results;
31058
32303
  }
31059
32304
  function mergePackageJsonScripts(cwd) {
31060
- const pkgPath = join18(cwd, "package.json");
31061
- if (!existsSync21(pkgPath))
32305
+ const pkgPath = join20(cwd, "package.json");
32306
+ if (!existsSync20(pkgPath))
31062
32307
  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"));
32308
+ const fragment = JSON.parse(readFileSync19(join20(TEMPLATES_DIR, "package.json.scripts.fragment.json"), "utf8"));
32309
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
31065
32310
  pkg.scripts ??= {};
31066
32311
  let changed = false;
31067
32312
  for (const [key, value] of Object.entries(fragment)) {
@@ -31072,7 +32317,7 @@ function mergePackageJsonScripts(cwd) {
31072
32317
  }
31073
32318
  if (!changed)
31074
32319
  return "skipped";
31075
- writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
32320
+ writeFileSync3(pkgPath, `${JSON.stringify(pkg, null, 2)}
31076
32321
  `, "utf8");
31077
32322
  return "updated";
31078
32323
  }
@@ -31117,27 +32362,23 @@ function skillsAddArgs(options = {}) {
31117
32362
  // src/init/init.ts
31118
32363
  var TEMPLATES_DIR2 = resolveTemplatesDir();
31119
32364
  function writeScaffold(cwd) {
31120
- const skeletonDir2 = join19(cwd, ".skeleton");
31121
- mkdirSync2(skeletonDir2, { recursive: true });
32365
+ const skeletonDir2 = join21(cwd, ".skeleton");
32366
+ mkdirSync3(skeletonDir2, { recursive: true });
31122
32367
  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);
32368
+ const tomlPath = join21(cwd, "skeleton.toml");
32369
+ const legacyYaml = join21(skeletonDir2, "config.yaml");
32370
+ if (!(existsSync21(tomlPath) || existsSync21(legacyYaml))) {
32371
+ copyFileSync(join21(TEMPLATES_DIR2, "skeleton.toml"), tomlPath);
31131
32372
  created = true;
31132
32373
  }
31133
- mkdirSync2(join19(skeletonDir2, "customize"), { recursive: true });
32374
+ mkdirSync3(join21(skeletonDir2, "customize"), { recursive: true });
31134
32375
  return created ? "created" : "skipped";
31135
32376
  }
31136
32377
  function assertPackageResolvable(cwd) {
31137
- const pkgPath = join19(cwd, "package.json");
31138
- if (!existsSync22(pkgPath))
32378
+ const pkgPath = join21(cwd, "package.json");
32379
+ if (!existsSync21(pkgPath))
31139
32380
  return;
31140
- const pkg = JSON.parse(readFileSync17(pkgPath, "utf8"));
32381
+ const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
31141
32382
  const hasDep = pkg.devDependencies?.["@csark0812/skeleton"] || pkg.dependencies?.["@csark0812/skeleton"];
31142
32383
  if (!hasDep) {
31143
32384
  try {
@@ -31186,9 +32427,9 @@ function runInit(options = {}) {
31186
32427
  for (const result of hooks)
31187
32428
  logHookMergeResult(result);
31188
32429
  if (scaffold === "created") {
31189
- console.log("init: wrote .skeleton/config.yaml and registry.md");
32430
+ console.log("init: wrote skeleton.toml (hooks optional — see docs)");
31190
32431
  } else {
31191
- console.log("init: .skeleton/ already present — skipped scaffold write");
32432
+ console.log("init: skeleton.toml or .skeleton/ already present — skipped scaffold write");
31192
32433
  }
31193
32434
  if (scripts === "updated") {
31194
32435
  console.log("init: merged validate/audit scripts into package.json");
@@ -31221,8 +32462,8 @@ function parseInitArgs(argv) {
31221
32462
  // src/plugins/build.ts
31222
32463
  import { spawnSync as spawnSync3 } from "node:child_process";
31223
32464
  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";
32465
+ import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync4 } from "node:fs";
32466
+ import { basename as basename4, dirname as dirname12, resolve as resolve8 } from "node:path";
31226
32467
  function parseBuildPluginArgs(argv) {
31227
32468
  let check = false;
31228
32469
  let entry;
@@ -31247,7 +32488,7 @@ function collectPluginEntries(root2, config, entry) {
31247
32488
  return (config.plugins ?? []).map((e) => resolvePluginTsPath(root2, e));
31248
32489
  }
31249
32490
  function localImportPaths(tsAbs, content3) {
31250
- const dir = dirname11(tsAbs);
32491
+ const dir = dirname12(tsAbs);
31251
32492
  const deps = [];
31252
32493
  const re = /(?:from\s+|import\s*\(\s*|import\s+)["'](\.[^"']+)["']/g;
31253
32494
  for (const match of content3.matchAll(re)) {
@@ -31257,12 +32498,12 @@ function localImportPaths(tsAbs, content3) {
31257
32498
  const candidates = [];
31258
32499
  if (spec.endsWith(".js")) {
31259
32500
  const withoutJs = spec.slice(0, -".js".length);
31260
- candidates.push(resolve9(dir, `${withoutJs}.ts`), resolve9(dir, spec), resolve9(dir, withoutJs, "index.ts"));
32501
+ candidates.push(resolve8(dir, `${withoutJs}.ts`), resolve8(dir, spec), resolve8(dir, withoutJs, "index.ts"));
31261
32502
  } else {
31262
- candidates.push(resolve9(dir, spec), resolve9(dir, `${spec}.ts`), resolve9(dir, `${spec}.js`), resolve9(dir, spec, "index.ts"));
32503
+ candidates.push(resolve8(dir, spec), resolve8(dir, `${spec}.ts`), resolve8(dir, `${spec}.js`), resolve8(dir, spec, "index.ts"));
31263
32504
  }
31264
32505
  for (const candidate of candidates) {
31265
- if (existsSync23(candidate) && candidate.endsWith(".ts")) {
32506
+ if (existsSync22(candidate) && candidate.endsWith(".ts")) {
31266
32507
  deps.push(candidate);
31267
32508
  break;
31268
32509
  }
@@ -31279,7 +32520,7 @@ function sourceFingerprint(tsAbs, seen = new Set) {
31279
32520
  if (seen.has(abs))
31280
32521
  return;
31281
32522
  seen.add(abs);
31282
- const content3 = readFileSync18(abs, "utf8");
32523
+ const content3 = readFileSync21(abs, "utf8");
31283
32524
  hash.update(basename4(abs));
31284
32525
  hash.update("\x00");
31285
32526
  hash.update(content3);
@@ -31292,12 +32533,12 @@ function sourceFingerprint(tsAbs, seen = new Set) {
31292
32533
  return hash.digest("hex");
31293
32534
  }
31294
32535
  function writeStamp(tsAbs, mjsAbs) {
31295
- writeFileSync3(stampPathForMjs(mjsAbs), `${sourceFingerprint(tsAbs)}
32536
+ writeFileSync4(stampPathForMjs(mjsAbs), `${sourceFingerprint(tsAbs)}
31296
32537
  `, "utf8");
31297
32538
  }
31298
32539
  async function buildOne(tsAbs) {
31299
32540
  const mjsAbs = mjsPathForTs(tsAbs);
31300
- if (!existsSync23(tsAbs)) {
32541
+ if (!existsSync22(tsAbs)) {
31301
32542
  throw new Error(`Plugin source not found: ${tsAbs}`);
31302
32543
  }
31303
32544
  const proc = spawnSync3("bun", ["build", tsAbs, "--target=node", "--format=esm", `--outfile=${mjsAbs}`, "--packages=external"], { encoding: "utf8" });
@@ -31317,17 +32558,17 @@ ${proc.stderr || proc.stdout || `exit ${proc.status}`}`);
31317
32558
  }
31318
32559
  function checkOne(tsAbs) {
31319
32560
  const mjsAbs = mjsPathForTs(tsAbs);
31320
- if (!existsSync23(mjsAbs)) {
32561
+ if (!existsSync22(mjsAbs)) {
31321
32562
  throw new Error(`Plugin not built: ${tsAbs} (missing ${mjsAbs}). Run: skeleton build-plugin`);
31322
32563
  }
31323
- if (!existsSync23(tsAbs)) {
32564
+ if (!existsSync22(tsAbs)) {
31324
32565
  throw new Error(`Plugin source not found: ${tsAbs}`);
31325
32566
  }
31326
32567
  const stampAbs = stampPathForMjs(mjsAbs);
31327
- if (!existsSync23(stampAbs)) {
32568
+ if (!existsSync22(stampAbs)) {
31328
32569
  throw new Error(`Plugin stale: ${mjsAbs} has no fingerprint stamp. Run: skeleton build-plugin`);
31329
32570
  }
31330
- const expected = readFileSync18(stampAbs, "utf8").trim();
32571
+ const expected = readFileSync21(stampAbs, "utf8").trim();
31331
32572
  const actual = sourceFingerprint(tsAbs);
31332
32573
  if (expected !== actual) {
31333
32574
  throw new Error(`Plugin stale: ${mjsAbs} does not match ${tsAbs} (or local imports). Run: skeleton build-plugin`);
@@ -31360,14 +32601,14 @@ import process6 from "node:process";
31360
32601
 
31361
32602
  // src/references/sync.ts
31362
32603
  import {
31363
- existsSync as existsSync24,
31364
- mkdirSync as mkdirSync3,
32604
+ existsSync as existsSync23,
32605
+ mkdirSync as mkdirSync4,
31365
32606
  readdirSync as readdirSync6,
31366
- readFileSync as readFileSync19,
32607
+ readFileSync as readFileSync22,
31367
32608
  unlinkSync,
31368
- writeFileSync as writeFileSync4
32609
+ writeFileSync as writeFileSync5
31369
32610
  } from "node:fs";
31370
- import { dirname as dirname12, join as join20, relative as relative11 } from "node:path";
32611
+ import { dirname as dirname13, join as join22, relative as relative11 } from "node:path";
31371
32612
  import process5 from "node:process";
31372
32613
  function resolveOwnership(root2, override) {
31373
32614
  if (override !== undefined)
@@ -31378,12 +32619,12 @@ function resolveOwnership(root2, override) {
31378
32619
  }
31379
32620
  function walkMarkdownFiles2(dir, root2) {
31380
32621
  const files = [];
31381
- if (!existsSync24(dir))
32622
+ if (!existsSync23(dir))
31382
32623
  return files;
31383
32624
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
31384
32625
  if (entry.name.startsWith("."))
31385
32626
  continue;
31386
- const fullPath = join20(dir, entry.name);
32627
+ const fullPath = join22(dir, entry.name);
31387
32628
  if (entry.isDirectory()) {
31388
32629
  files.push(...walkMarkdownFiles2(fullPath, root2));
31389
32630
  continue;
@@ -31397,14 +32638,14 @@ function walkMarkdownFiles2(dir, root2) {
31397
32638
  function collectGeneratedInDir(input) {
31398
32639
  const { dir, refsDir, skill, files } = input;
31399
32640
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
31400
- const fullPath = join20(dir, entry.name);
32641
+ const fullPath = join22(dir, entry.name);
31401
32642
  if (entry.isDirectory()) {
31402
32643
  collectGeneratedInDir({ dir: fullPath, refsDir, skill, files });
31403
32644
  continue;
31404
32645
  }
31405
32646
  if (!entry.name.endsWith(".md"))
31406
32647
  continue;
31407
- const content3 = readFileSync19(fullPath, "utf8");
32648
+ const content3 = readFileSync22(fullPath, "utf8");
31408
32649
  if (!isGeneratedReference(content3))
31409
32650
  continue;
31410
32651
  const refPath = normalizeRelPath(relative11(refsDir, fullPath));
@@ -31412,8 +32653,8 @@ function collectGeneratedInDir(input) {
31412
32653
  }
31413
32654
  }
31414
32655
  function listGeneratedReferenceFiles(skillDir, skill) {
31415
- const refsDir = join20(skillDir, "references");
31416
- if (!existsSync24(refsDir))
32656
+ const refsDir = join22(skillDir, "references");
32657
+ if (!existsSync23(refsDir))
31417
32658
  return [];
31418
32659
  const files = [];
31419
32660
  collectGeneratedInDir({ dir: refsDir, refsDir, skill, files });
@@ -31421,21 +32662,21 @@ function listGeneratedReferenceFiles(skillDir, skill) {
31421
32662
  }
31422
32663
  function syncGeneratedCopy(ctx, refPath) {
31423
32664
  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)) {
32665
+ const sourceRel = normalizeRelPath(join22(CANONICAL_REFS_DIR, refPath));
32666
+ const canonicalPath = join22(root2, sourceRel);
32667
+ if (!existsSync23(canonicalPath)) {
31427
32668
  throw new Error(`canonical reference missing: ${sourceRel}`);
31428
32669
  }
31429
32670
  const targetRel = generatedRefPath(plan.skill, refPath);
31430
- const targetPath = join20(root2, targetRel);
31431
- const canonicalContent = readFileSync19(canonicalPath, "utf8");
32671
+ const targetPath = join22(root2, targetRel);
32672
+ const canonicalContent = readFileSync22(canonicalPath, "utf8");
31432
32673
  const nextContent = formatGeneratedHeader(sourceRel) + canonicalContent;
31433
32674
  if (!options.dryRun)
31434
- mkdirSync3(dirname12(targetPath), { recursive: true });
31435
- const existing = existsSync24(targetPath) ? readFileSync19(targetPath, "utf8") : null;
32675
+ mkdirSync4(dirname13(targetPath), { recursive: true });
32676
+ const existing = existsSync23(targetPath) ? readFileSync22(targetPath, "utf8") : null;
31436
32677
  if (existing !== nextContent) {
31437
32678
  if (!options.dryRun)
31438
- writeFileSync4(targetPath, nextContent, "utf8");
32679
+ writeFileSync5(targetPath, nextContent, "utf8");
31439
32680
  result.written.push(targetRel);
31440
32681
  } else {
31441
32682
  result.skipped.push(targetRel);
@@ -31446,13 +32687,13 @@ function rewritePlanLinks(ctx, skillDir) {
31446
32687
  if (options.rewriteLinks === false)
31447
32688
  return;
31448
32689
  for (const relFile of walkMarkdownFiles2(skillDir, root2)) {
31449
- const filePath = join20(root2, relFile);
31450
- const content3 = readFileSync19(filePath, "utf8");
32690
+ const filePath = join22(root2, relFile);
32691
+ const content3 = readFileSync22(filePath, "utf8");
31451
32692
  const next = rewriteSharedRefLinks(content3, relFile, plan.skill);
31452
32693
  if (next === content3)
31453
32694
  continue;
31454
32695
  if (!options.dryRun)
31455
- writeFileSync4(filePath, next, "utf8");
32696
+ writeFileSync5(filePath, next, "utf8");
31456
32697
  result.rewritten.push(relFile);
31457
32698
  }
31458
32699
  }
@@ -31463,12 +32704,12 @@ function removeStaleGenerated(ctx, skillDir) {
31463
32704
  if (plan.refPaths.has(refPath))
31464
32705
  continue;
31465
32706
  if (!options.dryRun)
31466
- unlinkSync(join20(root2, generatedRel));
32707
+ unlinkSync(join22(root2, generatedRel));
31467
32708
  result.removed.push(generatedRel);
31468
32709
  }
31469
32710
  }
31470
32711
  function syncPlan(ctx) {
31471
- const skillDir = join20(ctx.root, ctx.plan.skill);
32712
+ const skillDir = join22(ctx.root, ctx.plan.skill);
31472
32713
  for (const refPath of ctx.plan.refPaths) {
31473
32714
  syncGeneratedCopy(ctx, refPath);
31474
32715
  }
@@ -31477,8 +32718,8 @@ function syncPlan(ctx) {
31477
32718
  }
31478
32719
  function syncReferences(options = {}) {
31479
32720
  const root2 = options.root ?? process5.cwd();
31480
- const canonicalDir = join20(root2, CANONICAL_REFS_DIR);
31481
- if (!existsSync24(canonicalDir)) {
32721
+ const canonicalDir = join22(root2, CANONICAL_REFS_DIR);
32722
+ if (!existsSync23(canonicalDir)) {
31482
32723
  throw new Error(`canonical references dir not found: ${CANONICAL_REFS_DIR}`);
31483
32724
  }
31484
32725
  const result = { written: [], rewritten: [], removed: [], skipped: [] };
@@ -31523,183 +32764,10 @@ function printSyncResult(result) {
31523
32764
  }
31524
32765
  }
31525
32766
 
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
32767
  // src/validate/changed.ts
31700
32768
  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";
32769
+ import { existsSync as existsSync24, readFileSync as readFileSync23 } from "node:fs";
32770
+ import { basename as basename5, extname as extname2, join as join23 } from "node:path";
31703
32771
 
31704
32772
  // src/validate/git-diff.ts
31705
32773
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -31792,9 +32860,9 @@ function parseJsonContent(content3) {
31792
32860
  }
31793
32861
  }
31794
32862
  function validateJson(relPath2, root2) {
31795
- const abs = join22(root2, relPath2);
32863
+ const abs = join23(root2, relPath2);
31796
32864
  try {
31797
- parseJsonContent(readFileSync21(abs, "utf8"));
32865
+ parseJsonContent(readFileSync23(abs, "utf8"));
31798
32866
  return 0;
31799
32867
  } catch (error) {
31800
32868
  console.error(`validate changed: invalid JSON in ${relPath2}: ${error}`);
@@ -31802,9 +32870,9 @@ function validateJson(relPath2, root2) {
31802
32870
  }
31803
32871
  }
31804
32872
  function validatePolicy(relPath2, root2) {
31805
- const abs = join22(root2, relPath2);
32873
+ const abs = join23(root2, relPath2);
31806
32874
  try {
31807
- loadPolicyFile(abs, readFileSync21(abs, "utf8"));
32875
+ loadPolicyFile(abs, readFileSync23(abs, "utf8"));
31808
32876
  return 0;
31809
32877
  } catch (error) {
31810
32878
  console.error(`validate changed: invalid policy ${relPath2}: ${error}`);
@@ -31812,7 +32880,7 @@ function validatePolicy(relPath2, root2) {
31812
32880
  }
31813
32881
  }
31814
32882
  function validateShell(relPath2, root2) {
31815
- const abs = join22(root2, relPath2);
32883
+ const abs = join23(root2, relPath2);
31816
32884
  const shellcheck = spawnSync5("shellcheck", [abs], { encoding: "utf8" });
31817
32885
  if (shellcheck.status === 0)
31818
32886
  return 0;
@@ -31833,11 +32901,11 @@ function resolvePaths(options) {
31833
32901
  });
31834
32902
  }
31835
32903
  function packageManagerFromPackageJson(root2) {
31836
- const pkgPath = join22(root2, "package.json");
31837
- if (!existsSync26(pkgPath))
32904
+ const pkgPath = join23(root2, "package.json");
32905
+ if (!existsSync24(pkgPath))
31838
32906
  return null;
31839
32907
  try {
31840
- const pkg = JSON.parse(readFileSync21(pkgPath, "utf8"));
32908
+ const pkg = JSON.parse(readFileSync23(pkgPath, "utf8"));
31841
32909
  const raw = pkg.packageManager?.split("@")[0];
31842
32910
  if (raw === "bun" || raw === "npm" || raw === "pnpm" || raw === "yarn")
31843
32911
  return raw;
@@ -31845,13 +32913,13 @@ function packageManagerFromPackageJson(root2) {
31845
32913
  return null;
31846
32914
  }
31847
32915
  function packageManagerFromLockfiles(root2) {
31848
- if (existsSync26(join22(root2, "bun.lock")) || existsSync26(join22(root2, "bun.lockb")))
32916
+ if (existsSync24(join23(root2, "bun.lock")) || existsSync24(join23(root2, "bun.lockb")))
31849
32917
  return "bun";
31850
- if (existsSync26(join22(root2, "pnpm-lock.yaml")))
32918
+ if (existsSync24(join23(root2, "pnpm-lock.yaml")))
31851
32919
  return "pnpm";
31852
- if (existsSync26(join22(root2, "yarn.lock")))
32920
+ if (existsSync24(join23(root2, "yarn.lock")))
31853
32921
  return "yarn";
31854
- if (existsSync26(join22(root2, "package-lock.json")))
32922
+ if (existsSync24(join23(root2, "package-lock.json")))
31855
32923
  return "npm";
31856
32924
  return null;
31857
32925
  }
@@ -31877,8 +32945,8 @@ function emptyBuckets() {
31877
32945
  function classifySinglePath(input) {
31878
32946
  const { relPath: relPath2, ctx, state, bucketCtx } = input;
31879
32947
  const normalized = normalizeRelPath(relPath2);
31880
- const abs = join22(ctx.root, normalized);
31881
- if (!existsSync26(abs)) {
32948
+ const abs = join23(ctx.root, normalized);
32949
+ if (!existsSync24(abs)) {
31882
32950
  state.missing++;
31883
32951
  console.error(`validate changed: path not found: ${relPath2}`);
31884
32952
  return;
@@ -32112,31 +33180,16 @@ function usage() {
32112
33180
  Commands:
32113
33181
  init [--force-hooks] [--skills] [--no-skills] [skills add flags…]
32114
33182
  audit docs|self|skills [--strict] [--json] [--paths=a,b] [--only=rule]
32115
- [--fix[=doc-meta|anchors]] [--dry-run]
33183
+ [--fix[=doc-meta|anchors|ssot]] [--dry-run]
32116
33184
  build-plugin [path] [--check]
32117
33185
  validate changed [paths…] [--staged] [--base <ref>]
32118
- register <path> [--topic=…] [--dry-run] [--json]
33186
+ catalog [--check] write or check .skeleton/catalog.md (gitignored)
32119
33187
  customize resolve <slug> [--json]
32120
33188
  hook customize (reads a host hook payload on stdin)
32121
33189
  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 };
33190
+ references check [--json] [--strict]
33191
+
33192
+ Note: \`register\` was removed — add a source-of-truth marker to the file and run \`skeleton catalog\`.`);
32140
33193
  }
32141
33194
  function parseValidateChangedArgs(rest) {
32142
33195
  const paths = [];
@@ -32180,19 +33233,12 @@ async function handleValidateChanged(argv) {
32180
33233
  const { paths, staged, base } = parseValidateChangedArgs(argv);
32181
33234
  return runValidateChanged({ paths, staged, base });
32182
33235
  }
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;
33236
+ function handleRegister() {
33237
+ console.error("register: removed add `<!-- source-of-truth: … -->` (or visible `source-of-truth:`) to the file, then run `skeleton catalog`.");
33238
+ return 1;
33239
+ }
33240
+ function handleCatalog(argv) {
33241
+ return runCatalogCli({ check: argv.includes("--check") });
32196
33242
  }
32197
33243
  function handleCustomizeResolve(argv) {
32198
33244
  const slug2 = argv[0];
@@ -32214,7 +33260,7 @@ function handleHook(argv) {
32214
33260
  usage();
32215
33261
  return 1;
32216
33262
  }
32217
- process7.stdout.write(runCustomizeHook(readFileSync22(0, "utf8")));
33263
+ process7.stdout.write(runCustomizeHook(readFileSync24(0, "utf8")));
32218
33264
  return 0;
32219
33265
  }
32220
33266
  function handleInit(argv) {
@@ -32250,7 +33296,9 @@ async function dispatchCommand(argv) {
32250
33296
  case "validate":
32251
33297
  return rest[0] === "changed" ? handleValidateChanged(rest.slice(1)) : null;
32252
33298
  case "register":
32253
- return handleRegister(rest);
33299
+ return handleRegister();
33300
+ case "catalog":
33301
+ return handleCatalog(rest);
32254
33302
  case "customize":
32255
33303
  return rest[0] === "resolve" ? handleCustomizeResolve(rest.slice(1)) : null;
32256
33304
  case "hook":