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