@csark0812/skeleton 4.0.2 → 5.0.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/README.md +5 -9
- package/dist/audit/config/types.d.ts +0 -5
- package/dist/cli.js +111 -574
- package/package.json +2 -2
- package/schemas/config.schema.json +1 -17
- package/templates/skeleton-init/config.yaml +0 -5
- package/templates/skeleton-init/skeleton.toml +1 -5
- package/dist/hooks/customize-on-skill-read.js +0 -14813
- package/templates/skeleton-init/claude-settings.fragment.json +0 -24
- package/templates/skeleton-init/codex-hooks.fragment.json +0 -15
- package/templates/skeleton-init/cursor-hooks.fragment.json +0 -11
package/dist/cli.js
CHANGED
|
@@ -15968,8 +15968,7 @@ var require_extend = __commonJS((exports, module) => {
|
|
|
15968
15968
|
});
|
|
15969
15969
|
|
|
15970
15970
|
// src/cli.ts
|
|
15971
|
-
import
|
|
15972
|
-
import process6 from "node:process";
|
|
15971
|
+
import process5 from "node:process";
|
|
15973
15972
|
|
|
15974
15973
|
// src/audit/config/load.ts
|
|
15975
15974
|
var import_ajv = __toESM(require_ajv(), 1);
|
|
@@ -16903,7 +16902,6 @@ var $visitAsync = visit.visitAsync;
|
|
|
16903
16902
|
|
|
16904
16903
|
// src/audit/core/shared.ts
|
|
16905
16904
|
var CATALOG_REL_PATH = ".skeleton/catalog.md";
|
|
16906
|
-
var REGISTRY_DIR_REL = ".skeleton";
|
|
16907
16905
|
var EXTERNAL_LINK_RE = /^(https?:|mailto:|#)/;
|
|
16908
16906
|
var DOC_META_RE = /<!--\s*doc-meta:\s*owner=[^|]+\|\s*last-reviewed=\d{4}-\d{2}-\d{2}\s*-->/;
|
|
16909
16907
|
var DOC_META_LAST_REVIEWED_RE = /last-reviewed=(\d{4}-\d{2}-\d{2})/;
|
|
@@ -17002,8 +17000,7 @@ function isDraftPlacementAllowed(relPath, draftPathPrefixes) {
|
|
|
17002
17000
|
// src/audit/config/load.ts
|
|
17003
17001
|
var SCHEMA_CANDIDATES = [
|
|
17004
17002
|
join(dirname(fileURLToPath(import.meta.url)), "../../../schemas/config.schema.json"),
|
|
17005
|
-
join(dirname(fileURLToPath(import.meta.url)), "../schemas/config.schema.json")
|
|
17006
|
-
join(dirname(fileURLToPath(import.meta.url)), "../../schemas/config.schema.json")
|
|
17003
|
+
join(dirname(fileURLToPath(import.meta.url)), "../schemas/config.schema.json")
|
|
17007
17004
|
];
|
|
17008
17005
|
var ROOT_CONFIG_TOML = "skeleton.toml";
|
|
17009
17006
|
var LEGACY_CONFIG_YAML = join(".skeleton", "config.yaml");
|
|
@@ -18226,7 +18223,6 @@ function listOwnedSkillSlugs(index) {
|
|
|
18226
18223
|
|
|
18227
18224
|
// src/audit/core/collect.ts
|
|
18228
18225
|
var MARKDOWN_GLOBS = ["**/*.md", "**/*.mdc"];
|
|
18229
|
-
var BUILTIN_INCLUDE_PATTERNS = [".skeleton/customize/**"];
|
|
18230
18226
|
function isMarkdownFile(absPath) {
|
|
18231
18227
|
return absPath.endsWith(".md") || absPath.endsWith(".mdc");
|
|
18232
18228
|
}
|
|
@@ -18268,7 +18264,7 @@ function expandPatterns(root, patterns, exclude) {
|
|
|
18268
18264
|
}
|
|
18269
18265
|
function collectScanFiles(config, root, skillIndex) {
|
|
18270
18266
|
const exclude = mergedExcludes(config);
|
|
18271
|
-
const includePatterns = [...
|
|
18267
|
+
const includePatterns = [...config.scan.include];
|
|
18272
18268
|
if (skillIndex) {
|
|
18273
18269
|
includePatterns.push(...skillCollectAugments(skillIndex));
|
|
18274
18270
|
}
|
|
@@ -18606,8 +18602,7 @@ import { basename as basename2, dirname as dirname4, extname, join as join6 } fr
|
|
|
18606
18602
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
18607
18603
|
var SCHEMA_CANDIDATES2 = [
|
|
18608
18604
|
join6(dirname4(fileURLToPath2(import.meta.url)), "../../../schemas/policy-file.schema.json"),
|
|
18609
|
-
join6(dirname4(fileURLToPath2(import.meta.url)), "../schemas/policy-file.schema.json")
|
|
18610
|
-
join6(dirname4(fileURLToPath2(import.meta.url)), "../../schemas/policy-file.schema.json")
|
|
18605
|
+
join6(dirname4(fileURLToPath2(import.meta.url)), "../schemas/policy-file.schema.json")
|
|
18611
18606
|
];
|
|
18612
18607
|
function resolvePolicySchemaPath() {
|
|
18613
18608
|
for (const candidate of SCHEMA_CANDIDATES2) {
|
|
@@ -31952,9 +31947,10 @@ var DEFAULT_REVIEW_COVERAGE_EXCLUDE = [
|
|
|
31952
31947
|
"**/*.spec.*",
|
|
31953
31948
|
"**/fixtures/**",
|
|
31954
31949
|
"templates/**",
|
|
31955
|
-
"dist/**",
|
|
31956
|
-
"node_modules/**",
|
|
31957
|
-
"
|
|
31950
|
+
"**/dist/**",
|
|
31951
|
+
"**/node_modules/**",
|
|
31952
|
+
"**/.venv/**",
|
|
31953
|
+
"**/.git/**",
|
|
31958
31954
|
".skeleton/plugins/**"
|
|
31959
31955
|
];
|
|
31960
31956
|
function reviewCoveragePatterns(config) {
|
|
@@ -32604,489 +32600,39 @@ async function runAudit(options) {
|
|
|
32604
32600
|
return printAuditResult(await evaluateAudit(options), options.json);
|
|
32605
32601
|
}
|
|
32606
32602
|
|
|
32607
|
-
// src/customize/resolve.ts
|
|
32608
|
-
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
|
|
32609
|
-
import { basename as basename3, join as join16, relative as relative8 } from "node:path";
|
|
32610
|
-
function customizeDir(root2) {
|
|
32611
|
-
return join16(root2, REGISTRY_DIR_REL, "customize");
|
|
32612
|
-
}
|
|
32613
|
-
function customizePathForSlug(root2, slug2) {
|
|
32614
|
-
return join16(customizeDir(root2), `${slug2}.md`);
|
|
32615
|
-
}
|
|
32616
|
-
function resolveSlugFile(root2, slug2) {
|
|
32617
|
-
const direct = customizePathForSlug(root2, slug2);
|
|
32618
|
-
if (existsSync18(direct)) {
|
|
32619
|
-
return {
|
|
32620
|
-
content: readFileSync18(direct, "utf8"),
|
|
32621
|
-
path: normalizeRelPath(relative8(root2, direct))
|
|
32622
|
-
};
|
|
32623
|
-
}
|
|
32624
|
-
return { content: null, path: null };
|
|
32625
|
-
}
|
|
32626
|
-
function alwaysIncludeBasenames(root2) {
|
|
32627
|
-
try {
|
|
32628
|
-
const config = loadConfig(root2);
|
|
32629
|
-
return config.customize?.alwaysInclude ?? [];
|
|
32630
|
-
} catch {
|
|
32631
|
-
return [];
|
|
32632
|
-
}
|
|
32633
|
-
}
|
|
32634
|
-
function readAlwaysInclude(root2, basenames, skipBasename) {
|
|
32635
|
-
const parts = [];
|
|
32636
|
-
const paths = [];
|
|
32637
|
-
const dir = customizeDir(root2);
|
|
32638
|
-
for (const name of basenames) {
|
|
32639
|
-
const file = basename3(name);
|
|
32640
|
-
if (skipBasename && file === skipBasename)
|
|
32641
|
-
continue;
|
|
32642
|
-
const abs = join16(dir, file);
|
|
32643
|
-
if (!existsSync18(abs))
|
|
32644
|
-
continue;
|
|
32645
|
-
parts.push(readFileSync18(abs, "utf8").trimEnd());
|
|
32646
|
-
paths.push(normalizeRelPath(relative8(root2, abs)));
|
|
32647
|
-
}
|
|
32648
|
-
return { parts, paths };
|
|
32649
|
-
}
|
|
32650
|
-
function resolveCustomize(root2, slug2) {
|
|
32651
|
-
const slugFile = resolveSlugFile(root2, slug2);
|
|
32652
|
-
const alwaysNames = alwaysIncludeBasenames(root2);
|
|
32653
|
-
const skip = slugFile.path !== null && slugFile.path !== undefined ? basename3(slugFile.path) : null;
|
|
32654
|
-
const always = readAlwaysInclude(root2, alwaysNames, skip);
|
|
32655
|
-
const parts = [];
|
|
32656
|
-
const included = [];
|
|
32657
|
-
if (slugFile.content !== null && slugFile.content !== undefined && slugFile.content.trim().length > 0) {
|
|
32658
|
-
parts.push(slugFile.content.trimEnd());
|
|
32659
|
-
if (slugFile.path)
|
|
32660
|
-
included.push(slugFile.path);
|
|
32661
|
-
}
|
|
32662
|
-
parts.push(...always.parts);
|
|
32663
|
-
included.push(...always.paths);
|
|
32664
|
-
if (parts.length === 0) {
|
|
32665
|
-
return { slug: slug2, content: null, path: slugFile.path, included: [] };
|
|
32666
|
-
}
|
|
32667
|
-
return {
|
|
32668
|
-
slug: slug2,
|
|
32669
|
-
content: `${parts.join(`
|
|
32670
|
-
|
|
32671
|
-
---
|
|
32672
|
-
|
|
32673
|
-
`)}
|
|
32674
|
-
`,
|
|
32675
|
-
path: slugFile.path ?? always.paths[0] ?? null,
|
|
32676
|
-
included
|
|
32677
|
-
};
|
|
32678
|
-
}
|
|
32679
|
-
function resolveCustomizeFromRoot(slug2, startDir) {
|
|
32680
|
-
const root2 = findRepoRoot(startDir);
|
|
32681
|
-
return resolveCustomize(root2, slug2);
|
|
32682
|
-
}
|
|
32683
|
-
|
|
32684
|
-
// src/hooks/run.ts
|
|
32685
|
-
import process4 from "node:process";
|
|
32686
|
-
function parsePayload(raw) {
|
|
32687
|
-
if (!raw.trim())
|
|
32688
|
-
return {};
|
|
32689
|
-
return JSON.parse(raw);
|
|
32690
|
-
}
|
|
32691
|
-
function extractPath(payload) {
|
|
32692
|
-
const input = payload.tool_input ?? payload.toolInput ?? {};
|
|
32693
|
-
const candidates = [
|
|
32694
|
-
input.path,
|
|
32695
|
-
input.file_path,
|
|
32696
|
-
input.filePath,
|
|
32697
|
-
input.target_file,
|
|
32698
|
-
input.targetFile
|
|
32699
|
-
];
|
|
32700
|
-
for (const value of candidates) {
|
|
32701
|
-
if (typeof value === "string" && value.trim())
|
|
32702
|
-
return normalizeRelPath(value.trim());
|
|
32703
|
-
}
|
|
32704
|
-
return null;
|
|
32705
|
-
}
|
|
32706
|
-
function extractSkillSlug(payload) {
|
|
32707
|
-
const tool = payload.tool_name ?? payload.toolName ?? "";
|
|
32708
|
-
if (tool === "Skill" || tool === "skill") {
|
|
32709
|
-
const input = payload.tool_input ?? payload.toolInput ?? {};
|
|
32710
|
-
const slug2 = input.skill ?? input.slug ?? input.name;
|
|
32711
|
-
if (typeof slug2 === "string" && slug2.trim())
|
|
32712
|
-
return slug2.trim();
|
|
32713
|
-
}
|
|
32714
|
-
const path3 = extractPath(payload);
|
|
32715
|
-
if (path3)
|
|
32716
|
-
return slugFromPath(path3, process4.cwd());
|
|
32717
|
-
return null;
|
|
32718
|
-
}
|
|
32719
|
-
function cursorResponse(content3) {
|
|
32720
|
-
return JSON.stringify({ additional_context: content3 });
|
|
32721
|
-
}
|
|
32722
|
-
function claudeResponse(content3) {
|
|
32723
|
-
return JSON.stringify({
|
|
32724
|
-
hookSpecificOutput: {
|
|
32725
|
-
additionalContext: content3
|
|
32726
|
-
}
|
|
32727
|
-
});
|
|
32728
|
-
}
|
|
32729
|
-
function codexResponse(content3) {
|
|
32730
|
-
return JSON.stringify({ additionalContext: content3 });
|
|
32731
|
-
}
|
|
32732
|
-
function formatResponse(payload, content3) {
|
|
32733
|
-
const tool = payload.tool_name ?? payload.toolName ?? "";
|
|
32734
|
-
if (tool === "read_file")
|
|
32735
|
-
return codexResponse(content3);
|
|
32736
|
-
if (payload.hook_event_name?.toLowerCase().includes("claude"))
|
|
32737
|
-
return claudeResponse(content3);
|
|
32738
|
-
if (tool === "Read" && payload.hook_event_name === "postToolUse")
|
|
32739
|
-
return cursorResponse(content3);
|
|
32740
|
-
if (tool === "Read" || tool === "Skill")
|
|
32741
|
-
return claudeResponse(content3);
|
|
32742
|
-
return cursorResponse(content3);
|
|
32743
|
-
}
|
|
32744
|
-
function runCustomizeHook(raw) {
|
|
32745
|
-
try {
|
|
32746
|
-
const payload = parsePayload(raw);
|
|
32747
|
-
const slug2 = extractSkillSlug(payload);
|
|
32748
|
-
if (!slug2)
|
|
32749
|
-
return "{}";
|
|
32750
|
-
const resolved = resolveCustomizeFromRoot(slug2);
|
|
32751
|
-
if (!resolved.content)
|
|
32752
|
-
return "{}";
|
|
32753
|
-
const from = resolved.included.length > 0 ? resolved.included.join(", ") : resolved.path ?? ".skeleton/customize";
|
|
32754
|
-
const prefix = `
|
|
32755
|
-
|
|
32756
|
-
---
|
|
32757
|
-
Customize override for /${slug2} (from ${from}):
|
|
32758
|
-
|
|
32759
|
-
`;
|
|
32760
|
-
return formatResponse(payload, prefix + resolved.content);
|
|
32761
|
-
} catch (error) {
|
|
32762
|
-
console.error(`customize hook error: ${error}`);
|
|
32763
|
-
return "{}";
|
|
32764
|
-
}
|
|
32765
|
-
}
|
|
32766
|
-
|
|
32767
32603
|
// src/init/init.ts
|
|
32768
32604
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
32769
|
-
import { copyFileSync, existsSync as
|
|
32770
|
-
import { join as
|
|
32771
|
-
import
|
|
32605
|
+
import { copyFileSync, existsSync as existsSync21, mkdirSync as mkdirSync3, readFileSync as readFileSync20 } from "node:fs";
|
|
32606
|
+
import { join as join19 } from "node:path";
|
|
32607
|
+
import process4 from "node:process";
|
|
32772
32608
|
|
|
32773
|
-
// src/init/merge-
|
|
32774
|
-
import { existsSync as
|
|
32775
|
-
import {
|
|
32609
|
+
// src/init/merge-precommit.ts
|
|
32610
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync4 } from "node:fs";
|
|
32611
|
+
import { join as join17 } from "node:path";
|
|
32776
32612
|
|
|
32777
32613
|
// src/init/package-paths.ts
|
|
32778
|
-
import { existsSync as
|
|
32779
|
-
import { dirname as dirname10, join as
|
|
32614
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
32615
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
32780
32616
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
32781
32617
|
var MODULE_DIR = dirname10(fileURLToPath4(import.meta.url));
|
|
32782
|
-
var PACKAGE_ROOT_CANDIDATES = [
|
|
32618
|
+
var PACKAGE_ROOT_CANDIDATES = [join16(MODULE_DIR, "../.."), join16(MODULE_DIR, "..")];
|
|
32783
32619
|
function resolvePackageRoot() {
|
|
32784
32620
|
for (const candidate of PACKAGE_ROOT_CANDIDATES) {
|
|
32785
|
-
if (
|
|
32621
|
+
if (existsSync18(join16(candidate, "package.json")))
|
|
32786
32622
|
return candidate;
|
|
32787
32623
|
}
|
|
32788
32624
|
throw new Error("Could not resolve @csark0812/skeleton package root");
|
|
32789
32625
|
}
|
|
32790
32626
|
function resolveTemplatesDir() {
|
|
32791
|
-
const dir =
|
|
32792
|
-
if (!
|
|
32627
|
+
const dir = join16(resolvePackageRoot(), "templates/skeleton-init");
|
|
32628
|
+
if (!existsSync18(dir)) {
|
|
32793
32629
|
throw new Error("Missing templates/skeleton-init in package");
|
|
32794
32630
|
}
|
|
32795
32631
|
return dir;
|
|
32796
32632
|
}
|
|
32797
32633
|
|
|
32798
|
-
// src/init/resolve-hook-command.ts
|
|
32799
|
-
import { existsSync as existsSync20, realpathSync as realpathSync5 } from "node:fs";
|
|
32800
|
-
import { createRequire as createRequire3 } from "node:module";
|
|
32801
|
-
import { dirname as dirname11, join as join18, relative as relative9, resolve as resolve6 } from "node:path";
|
|
32802
|
-
var PACKAGE_NAME = "@csark0812/skeleton";
|
|
32803
|
-
var CLI_DIST = "dist/cli.js";
|
|
32804
|
-
var PACKAGE_ROOT = resolvePackageRoot();
|
|
32805
|
-
var DEV_HOOK_COMMAND = "bun src/cli.ts hook customize";
|
|
32806
|
-
var FALLBACK_HOOK_COMMAND = `node node_modules/${PACKAGE_NAME}/${CLI_DIST} hook customize`;
|
|
32807
|
-
function safeRealpath2(path3) {
|
|
32808
|
-
try {
|
|
32809
|
-
return realpathSync5(path3);
|
|
32810
|
-
} catch {
|
|
32811
|
-
return path3;
|
|
32812
|
-
}
|
|
32813
|
-
}
|
|
32814
|
-
function toRepoRelative(cwd, absPath) {
|
|
32815
|
-
const rel = relative9(safeRealpath2(cwd), safeRealpath2(absPath)).replace(/\\/g, "/");
|
|
32816
|
-
return rel.startsWith("..") ? absPath.replace(/\\/g, "/") : rel;
|
|
32817
|
-
}
|
|
32818
|
-
function tryResolvePublishedCli(cwd) {
|
|
32819
|
-
try {
|
|
32820
|
-
const req = createRequire3(join18(cwd, "package.json"));
|
|
32821
|
-
return req.resolve(`${PACKAGE_NAME}/${CLI_DIST}`);
|
|
32822
|
-
} catch {
|
|
32823
|
-
return null;
|
|
32824
|
-
}
|
|
32825
|
-
}
|
|
32826
|
-
function walkNodeModulesCli(cwd) {
|
|
32827
|
-
let dir = cwd;
|
|
32828
|
-
while (true) {
|
|
32829
|
-
const candidate = join18(dir, "node_modules", PACKAGE_NAME, CLI_DIST);
|
|
32830
|
-
if (existsSync20(candidate))
|
|
32831
|
-
return candidate;
|
|
32832
|
-
const parent = dirname11(dir);
|
|
32833
|
-
if (parent === dir)
|
|
32834
|
-
break;
|
|
32835
|
-
dir = parent;
|
|
32836
|
-
}
|
|
32837
|
-
return null;
|
|
32838
|
-
}
|
|
32839
|
-
function isInsidePackageRoot(cwd) {
|
|
32840
|
-
const rel = relative9(PACKAGE_ROOT, resolve6(cwd)).replace(/\\/g, "/");
|
|
32841
|
-
return rel === "" || !(rel.startsWith("..") || rel.startsWith("/"));
|
|
32842
|
-
}
|
|
32843
|
-
function nodeCliHookCommand(cliPath) {
|
|
32844
|
-
return `node ${cliPath} hook customize`;
|
|
32845
|
-
}
|
|
32846
|
-
function resolveHookCommand(cwd) {
|
|
32847
|
-
if (isInsidePackageRoot(cwd))
|
|
32848
|
-
return DEV_HOOK_COMMAND;
|
|
32849
|
-
const published = tryResolvePublishedCli(cwd);
|
|
32850
|
-
if (published)
|
|
32851
|
-
return nodeCliHookCommand(toRepoRelative(cwd, published));
|
|
32852
|
-
const hoisted = walkNodeModulesCli(cwd);
|
|
32853
|
-
if (hoisted)
|
|
32854
|
-
return nodeCliHookCommand(toRepoRelative(cwd, hoisted));
|
|
32855
|
-
return FALLBACK_HOOK_COMMAND;
|
|
32856
|
-
}
|
|
32857
|
-
function isSkeletonHookCommand(command) {
|
|
32858
|
-
if (!command)
|
|
32859
|
-
return false;
|
|
32860
|
-
if (/\bhook\s+customize\b/.test(command))
|
|
32861
|
-
return true;
|
|
32862
|
-
return /customize-on-skill-read\.(js|ts)\b/.test(command);
|
|
32863
|
-
}
|
|
32864
|
-
|
|
32865
|
-
// src/init/merge-hooks.ts
|
|
32866
|
-
var TEMPLATES_DIR = resolveTemplatesDir();
|
|
32867
|
-
function identityKey(platform, event, matcher) {
|
|
32868
|
-
return `skeleton:customize:${platform}:${event}:${matcher}`;
|
|
32869
|
-
}
|
|
32870
|
-
function loadFragment(name, hookCommand) {
|
|
32871
|
-
const raw = readFileSync19(join19(TEMPLATES_DIR, name), "utf8");
|
|
32872
|
-
return JSON.parse(raw.replaceAll("{{HOOK_COMMAND}}", hookCommand));
|
|
32873
|
-
}
|
|
32874
|
-
function readJson(path3) {
|
|
32875
|
-
if (!existsSync21(path3))
|
|
32876
|
-
return null;
|
|
32877
|
-
try {
|
|
32878
|
-
return JSON.parse(readFileSync19(path3, "utf8"));
|
|
32879
|
-
} catch (error) {
|
|
32880
|
-
throw new Error(`Invalid JSON in ${path3}: ${error}`);
|
|
32881
|
-
}
|
|
32882
|
-
}
|
|
32883
|
-
function writeJson(path3, value) {
|
|
32884
|
-
mkdirSync3(dirname12(path3), { recursive: true });
|
|
32885
|
-
writeFileSync4(path3, `${JSON.stringify(value, null, 2)}
|
|
32886
|
-
`, "utf8");
|
|
32887
|
-
}
|
|
32888
|
-
function deepEqual(a, b) {
|
|
32889
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
32890
|
-
}
|
|
32891
|
-
function mergeExistingCursorHook(input) {
|
|
32892
|
-
const { postToolUse, skeletonIdx, canonical, opts } = input;
|
|
32893
|
-
const current = postToolUse[skeletonIdx];
|
|
32894
|
-
const userEdited = current && !opts.forceHooks && (current.matcher !== canonical.matcher || !isSkeletonHookCommand(current.command));
|
|
32895
|
-
if (userEdited) {
|
|
32896
|
-
return {
|
|
32897
|
-
platform: "cursor",
|
|
32898
|
-
action: "conflict",
|
|
32899
|
-
message: identityKey("cursor", "postToolUse", String(current.matcher ?? "Read"))
|
|
32900
|
-
};
|
|
32901
|
-
}
|
|
32902
|
-
const extras = Object.fromEntries(Object.entries(current ?? {}).filter(([key]) => !["command", "matcher"].includes(key)));
|
|
32903
|
-
const merged = { ...extras, ...canonical };
|
|
32904
|
-
if (deepEqual(current, merged))
|
|
32905
|
-
return { platform: "cursor", action: "skipped" };
|
|
32906
|
-
postToolUse[skeletonIdx] = merged;
|
|
32907
|
-
return null;
|
|
32908
|
-
}
|
|
32909
|
-
function persistCursorHooks(input) {
|
|
32910
|
-
const { targetPath, existing, hooks, postToolUse, skeletonIdx } = input;
|
|
32911
|
-
const next = {
|
|
32912
|
-
...existing,
|
|
32913
|
-
version: existing.version ?? 1,
|
|
32914
|
-
hooks: { ...hooks, postToolUse }
|
|
32915
|
-
};
|
|
32916
|
-
if (deepEqual(existing, next))
|
|
32917
|
-
return { platform: "cursor", action: "skipped" };
|
|
32918
|
-
writeJson(targetPath, next);
|
|
32919
|
-
return { platform: "cursor", action: skeletonIdx >= 0 ? "updated" : "added" };
|
|
32920
|
-
}
|
|
32921
|
-
function mergeCursorHooks(targetPath, fragment, opts) {
|
|
32922
|
-
const existing = readJson(targetPath) ?? {};
|
|
32923
|
-
const hooks = existing.hooks ?? {};
|
|
32924
|
-
const postToolUse = [...hooks.postToolUse ?? []];
|
|
32925
|
-
const incoming = fragment.hooks?.postToolUse?.[0];
|
|
32926
|
-
if (!incoming)
|
|
32927
|
-
return { platform: "cursor", action: "skipped" };
|
|
32928
|
-
const skeletonIdx = postToolUse.findIndex((entry) => isSkeletonHookCommand(entry.command));
|
|
32929
|
-
const canonical = { ...incoming, matcher: incoming.matcher ?? "Read" };
|
|
32930
|
-
if (skeletonIdx >= 0) {
|
|
32931
|
-
const conflict = mergeExistingCursorHook({ postToolUse, skeletonIdx, canonical, opts });
|
|
32932
|
-
if (conflict)
|
|
32933
|
-
return conflict;
|
|
32934
|
-
} else {
|
|
32935
|
-
postToolUse.push(canonical);
|
|
32936
|
-
}
|
|
32937
|
-
return persistCursorHooks({ targetPath, existing, hooks, postToolUse, skeletonIdx });
|
|
32938
|
-
}
|
|
32939
|
-
function mergeExistingNestedSkeletonHook(input) {
|
|
32940
|
-
const { hooks, skeletonIdx, incomingHook, opts, platform, eventName, matcher } = input;
|
|
32941
|
-
const current = hooks[skeletonIdx];
|
|
32942
|
-
const userEdited = current && !opts.forceHooks && (current.type !== incomingHook.type || !isSkeletonHookCommand(current.command));
|
|
32943
|
-
if (userEdited) {
|
|
32944
|
-
return {
|
|
32945
|
-
changed: false,
|
|
32946
|
-
conflict: {
|
|
32947
|
-
platform,
|
|
32948
|
-
action: "conflict",
|
|
32949
|
-
message: identityKey(platform, eventName, matcher)
|
|
32950
|
-
}
|
|
32951
|
-
};
|
|
32952
|
-
}
|
|
32953
|
-
const extras = Object.fromEntries(Object.entries(current ?? {}).filter(([key]) => !["type", "command"].includes(key)));
|
|
32954
|
-
const merged = { ...extras, ...incomingHook };
|
|
32955
|
-
if (!deepEqual(current, merged)) {
|
|
32956
|
-
hooks[skeletonIdx] = merged;
|
|
32957
|
-
return { changed: true };
|
|
32958
|
-
}
|
|
32959
|
-
return { changed: false };
|
|
32960
|
-
}
|
|
32961
|
-
function mergeIncomingHookGroup(args) {
|
|
32962
|
-
const { group, incomingHook, eventHooks, opts, platform, eventName } = args;
|
|
32963
|
-
const matcher = group.matcher ?? "";
|
|
32964
|
-
const groupIdx = eventHooks.findIndex((g) => g.matcher === matcher);
|
|
32965
|
-
if (groupIdx < 0) {
|
|
32966
|
-
eventHooks.push({ ...group, hooks: [{ ...incomingHook }] });
|
|
32967
|
-
return { changed: true };
|
|
32968
|
-
}
|
|
32969
|
-
const existingGroup = eventHooks[groupIdx];
|
|
32970
|
-
const hooks = [...existingGroup?.hooks ?? []];
|
|
32971
|
-
const skeletonIdx = hooks.findIndex((entry) => isSkeletonHookCommand(entry.command));
|
|
32972
|
-
if (skeletonIdx >= 0) {
|
|
32973
|
-
const result = mergeExistingNestedSkeletonHook({
|
|
32974
|
-
hooks,
|
|
32975
|
-
skeletonIdx,
|
|
32976
|
-
incomingHook,
|
|
32977
|
-
opts,
|
|
32978
|
-
platform,
|
|
32979
|
-
eventName,
|
|
32980
|
-
matcher
|
|
32981
|
-
});
|
|
32982
|
-
if (result.conflict)
|
|
32983
|
-
return { changed: false, conflict: result.conflict };
|
|
32984
|
-
eventHooks[groupIdx] = { ...existingGroup, matcher, hooks };
|
|
32985
|
-
return { changed: result.changed };
|
|
32986
|
-
}
|
|
32987
|
-
hooks.push({ ...incomingHook });
|
|
32988
|
-
eventHooks[groupIdx] = { ...existingGroup, matcher, hooks };
|
|
32989
|
-
return { changed: true };
|
|
32990
|
-
}
|
|
32991
|
-
function mergeIncomingGroups(args) {
|
|
32992
|
-
let changed = false;
|
|
32993
|
-
for (const incomingGroup of args.incomingGroups) {
|
|
32994
|
-
const incomingHook = incomingGroup.hooks?.[0];
|
|
32995
|
-
if (!incomingHook)
|
|
32996
|
-
continue;
|
|
32997
|
-
const result = mergeIncomingHookGroup({
|
|
32998
|
-
group: incomingGroup,
|
|
32999
|
-
incomingHook,
|
|
33000
|
-
eventHooks: args.eventHooks,
|
|
33001
|
-
opts: args.opts,
|
|
33002
|
-
platform: args.platform,
|
|
33003
|
-
eventName: args.eventName
|
|
33004
|
-
});
|
|
33005
|
-
if (result.conflict)
|
|
33006
|
-
return { changed: false, conflict: result.conflict };
|
|
33007
|
-
if (result.changed)
|
|
33008
|
-
changed = true;
|
|
33009
|
-
}
|
|
33010
|
-
return { changed };
|
|
33011
|
-
}
|
|
33012
|
-
function mergeNestedHooks(args) {
|
|
33013
|
-
const { platform, targetPath, eventName } = args;
|
|
33014
|
-
const existing = readJson(targetPath) ?? {};
|
|
33015
|
-
const rootHooks = existing.hooks ?? {};
|
|
33016
|
-
const eventHooks = [...rootHooks[eventName] ?? []];
|
|
33017
|
-
const incomingGroups = args.fragment.hooks?.[eventName] ?? [];
|
|
33018
|
-
const result = mergeIncomingGroups({
|
|
33019
|
-
incomingGroups,
|
|
33020
|
-
eventHooks,
|
|
33021
|
-
opts: args.opts,
|
|
33022
|
-
platform,
|
|
33023
|
-
eventName
|
|
33024
|
-
});
|
|
33025
|
-
if (result.conflict)
|
|
33026
|
-
return result.conflict;
|
|
33027
|
-
if (!result.changed)
|
|
33028
|
-
return { platform, action: "skipped" };
|
|
33029
|
-
const next = {
|
|
33030
|
-
...existing,
|
|
33031
|
-
hooks: { ...rootHooks, [eventName]: eventHooks }
|
|
33032
|
-
};
|
|
33033
|
-
writeJson(targetPath, next);
|
|
33034
|
-
return { platform, action: "updated" };
|
|
33035
|
-
}
|
|
33036
|
-
function mergeHookConfigs(opts) {
|
|
33037
|
-
const results = [];
|
|
33038
|
-
const cursorPath = join19(opts.cwd, ".cursor/hooks.json");
|
|
33039
|
-
const cursorFragment = loadFragment("cursor-hooks.fragment.json", opts.hookCommand);
|
|
33040
|
-
results.push(mergeCursorHooks(cursorPath, cursorFragment, opts));
|
|
33041
|
-
const claudePath = join19(opts.cwd, ".claude/settings.json");
|
|
33042
|
-
const claudeFragment = loadFragment("claude-settings.fragment.json", opts.hookCommand);
|
|
33043
|
-
results.push(mergeNestedHooks({
|
|
33044
|
-
platform: "claude",
|
|
33045
|
-
targetPath: claudePath,
|
|
33046
|
-
fragment: claudeFragment,
|
|
33047
|
-
eventName: "PostToolUse",
|
|
33048
|
-
opts
|
|
33049
|
-
}));
|
|
33050
|
-
const codexPath = join19(opts.cwd, ".codex/hooks.json");
|
|
33051
|
-
if (existsSync21(join19(opts.cwd, ".codex"))) {
|
|
33052
|
-
const codexFragment = loadFragment("codex-hooks.fragment.json", opts.hookCommand);
|
|
33053
|
-
results.push(mergeNestedHooks({
|
|
33054
|
-
platform: "codex",
|
|
33055
|
-
targetPath: codexPath,
|
|
33056
|
-
fragment: codexFragment,
|
|
33057
|
-
eventName: "PostToolUse",
|
|
33058
|
-
opts
|
|
33059
|
-
}));
|
|
33060
|
-
} else {
|
|
33061
|
-
results.push({ platform: "codex", action: "skipped", message: "missing .codex directory" });
|
|
33062
|
-
}
|
|
33063
|
-
return results;
|
|
33064
|
-
}
|
|
33065
|
-
function mergePackageJsonScripts(cwd) {
|
|
33066
|
-
const pkgPath = join19(cwd, "package.json");
|
|
33067
|
-
if (!existsSync21(pkgPath))
|
|
33068
|
-
return "skipped";
|
|
33069
|
-
const fragment = JSON.parse(readFileSync19(join19(TEMPLATES_DIR, "package.json.scripts.fragment.json"), "utf8"));
|
|
33070
|
-
const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
|
|
33071
|
-
pkg.scripts ??= {};
|
|
33072
|
-
let changed = false;
|
|
33073
|
-
for (const [key, value] of Object.entries(fragment)) {
|
|
33074
|
-
if (pkg.scripts[key] !== value) {
|
|
33075
|
-
pkg.scripts[key] = value;
|
|
33076
|
-
changed = true;
|
|
33077
|
-
}
|
|
33078
|
-
}
|
|
33079
|
-
if (!changed)
|
|
33080
|
-
return "skipped";
|
|
33081
|
-
writeFileSync4(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
33082
|
-
`, "utf8");
|
|
33083
|
-
return "updated";
|
|
33084
|
-
}
|
|
33085
|
-
|
|
33086
32634
|
// src/init/merge-precommit.ts
|
|
33087
|
-
|
|
33088
|
-
import { join as join20 } from "node:path";
|
|
33089
|
-
var TEMPLATES_DIR2 = resolveTemplatesDir();
|
|
32635
|
+
var TEMPLATES_DIR = resolveTemplatesDir();
|
|
33090
32636
|
var PRECOMMIT_NAME = ".pre-commit-config.yaml";
|
|
33091
32637
|
var HOOK_ID = "id: skeleton-validate-staged";
|
|
33092
32638
|
var LOCAL_HOOK_BLOCK = ` - repo: local
|
|
@@ -33098,19 +32644,44 @@ var LOCAL_HOOK_BLOCK = ` - repo: local
|
|
|
33098
32644
|
pass_filenames: false
|
|
33099
32645
|
`;
|
|
33100
32646
|
function mergePrecommitConfig(cwd) {
|
|
33101
|
-
const target =
|
|
33102
|
-
const template =
|
|
33103
|
-
if (!
|
|
33104
|
-
|
|
32647
|
+
const target = join17(cwd, PRECOMMIT_NAME);
|
|
32648
|
+
const template = readFileSync18(join17(TEMPLATES_DIR, "pre-commit-config.yaml"), "utf8");
|
|
32649
|
+
if (!existsSync19(target)) {
|
|
32650
|
+
writeFileSync4(target, template, "utf8");
|
|
33105
32651
|
return "added";
|
|
33106
32652
|
}
|
|
33107
|
-
const existing =
|
|
32653
|
+
const existing = readFileSync18(target, "utf8");
|
|
33108
32654
|
if (existing.includes(HOOK_ID))
|
|
33109
32655
|
return "skipped";
|
|
33110
32656
|
const suffix = existing.includes("repos:") ? `
|
|
33111
32657
|
${LOCAL_HOOK_BLOCK}` : `
|
|
33112
32658
|
${template}`;
|
|
33113
|
-
|
|
32659
|
+
writeFileSync4(target, `${existing.trimEnd()}${suffix}`, "utf8");
|
|
32660
|
+
return "updated";
|
|
32661
|
+
}
|
|
32662
|
+
|
|
32663
|
+
// src/init/merge-scripts.ts
|
|
32664
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync5 } from "node:fs";
|
|
32665
|
+
import { join as join18 } from "node:path";
|
|
32666
|
+
var TEMPLATES_DIR2 = resolveTemplatesDir();
|
|
32667
|
+
function mergePackageJsonScripts(cwd) {
|
|
32668
|
+
const pkgPath = join18(cwd, "package.json");
|
|
32669
|
+
if (!existsSync20(pkgPath))
|
|
32670
|
+
return "skipped";
|
|
32671
|
+
const fragment = JSON.parse(readFileSync19(join18(TEMPLATES_DIR2, "package.json.scripts.fragment.json"), "utf8"));
|
|
32672
|
+
const pkg = JSON.parse(readFileSync19(pkgPath, "utf8"));
|
|
32673
|
+
pkg.scripts ??= {};
|
|
32674
|
+
let changed = false;
|
|
32675
|
+
for (const [key, value] of Object.entries(fragment)) {
|
|
32676
|
+
if (pkg.scripts[key] !== value) {
|
|
32677
|
+
pkg.scripts[key] = value;
|
|
32678
|
+
changed = true;
|
|
32679
|
+
}
|
|
32680
|
+
}
|
|
32681
|
+
if (!changed)
|
|
32682
|
+
return "skipped";
|
|
32683
|
+
writeFileSync5(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
32684
|
+
`, "utf8");
|
|
33114
32685
|
return "updated";
|
|
33115
32686
|
}
|
|
33116
32687
|
|
|
@@ -33154,23 +32725,22 @@ function skillsAddArgs(options = {}) {
|
|
|
33154
32725
|
// src/init/init.ts
|
|
33155
32726
|
var TEMPLATES_DIR3 = resolveTemplatesDir();
|
|
33156
32727
|
function writeScaffold(cwd) {
|
|
33157
|
-
const skeletonDir2 =
|
|
33158
|
-
|
|
32728
|
+
const skeletonDir2 = join19(cwd, ".skeleton");
|
|
32729
|
+
mkdirSync3(skeletonDir2, { recursive: true });
|
|
33159
32730
|
let created = false;
|
|
33160
|
-
const tomlPath =
|
|
33161
|
-
const legacyYaml =
|
|
33162
|
-
if (!(
|
|
33163
|
-
copyFileSync(
|
|
32731
|
+
const tomlPath = join19(cwd, "skeleton.toml");
|
|
32732
|
+
const legacyYaml = join19(skeletonDir2, "config.yaml");
|
|
32733
|
+
if (!(existsSync21(tomlPath) || existsSync21(legacyYaml))) {
|
|
32734
|
+
copyFileSync(join19(TEMPLATES_DIR3, "skeleton.toml"), tomlPath);
|
|
33164
32735
|
created = true;
|
|
33165
32736
|
}
|
|
33166
|
-
mkdirSync4(join21(skeletonDir2, "customize"), { recursive: true });
|
|
33167
32737
|
return created ? "created" : "skipped";
|
|
33168
32738
|
}
|
|
33169
32739
|
function assertPackageResolvable(cwd) {
|
|
33170
|
-
const pkgPath =
|
|
33171
|
-
if (!
|
|
32740
|
+
const pkgPath = join19(cwd, "package.json");
|
|
32741
|
+
if (!existsSync21(pkgPath))
|
|
33172
32742
|
return;
|
|
33173
|
-
const pkg = JSON.parse(
|
|
32743
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
33174
32744
|
const hasDep = pkg.devDependencies?.["@csark0812/skeleton"] || pkg.dependencies?.["@csark0812/skeleton"];
|
|
33175
32745
|
if (!hasDep) {
|
|
33176
32746
|
try {
|
|
@@ -33188,16 +32758,6 @@ function runSkillsAdd(args, cwd) {
|
|
|
33188
32758
|
});
|
|
33189
32759
|
return result.status ?? 1;
|
|
33190
32760
|
}
|
|
33191
|
-
function logHookMergeResult(result) {
|
|
33192
|
-
if (result.action === "conflict") {
|
|
33193
|
-
console.error(`init: skipped ${result.platform} hook (user-edited) — re-run with --force-hooks to restore`);
|
|
33194
|
-
return;
|
|
33195
|
-
}
|
|
33196
|
-
if (result.action === "added")
|
|
33197
|
-
console.log(`init: added ${result.platform} customize hook`);
|
|
33198
|
-
if (result.action === "updated")
|
|
33199
|
-
console.log(`init: updated ${result.platform} customize hook`);
|
|
33200
|
-
}
|
|
33201
32761
|
function installSkillsIfRequested(options, cwd) {
|
|
33202
32762
|
if (!(options.skills && !options.noSkills))
|
|
33203
32763
|
return "skipped";
|
|
@@ -33210,17 +32770,13 @@ function installSkillsIfRequested(options, cwd) {
|
|
|
33210
32770
|
return "installed";
|
|
33211
32771
|
}
|
|
33212
32772
|
function runInit(options = {}) {
|
|
33213
|
-
const cwd = options.cwd ??
|
|
32773
|
+
const cwd = options.cwd ?? process4.cwd();
|
|
33214
32774
|
assertPackageResolvable(cwd);
|
|
33215
32775
|
const scaffold = writeScaffold(cwd);
|
|
33216
|
-
const hookCommand = resolveHookCommand(cwd);
|
|
33217
|
-
const hooks = mergeHookConfigs({ cwd, hookCommand, forceHooks: options.forceHooks });
|
|
33218
32776
|
const scripts = mergePackageJsonScripts(cwd);
|
|
33219
32777
|
const precommit = mergePrecommitConfig(cwd);
|
|
33220
|
-
for (const result of hooks)
|
|
33221
|
-
logHookMergeResult(result);
|
|
33222
32778
|
if (scaffold === "created") {
|
|
33223
|
-
console.log("init: wrote skeleton.toml
|
|
32779
|
+
console.log("init: wrote skeleton.toml");
|
|
33224
32780
|
} else {
|
|
33225
32781
|
console.log("init: skeleton.toml or .skeleton/ already present — skipped scaffold write");
|
|
33226
32782
|
}
|
|
@@ -33233,13 +32789,15 @@ function runInit(options = {}) {
|
|
|
33233
32789
|
console.log("init: added skeleton validate hook to .pre-commit-config.yaml");
|
|
33234
32790
|
}
|
|
33235
32791
|
const skills = installSkillsIfRequested(options, cwd);
|
|
33236
|
-
return { scaffold,
|
|
32792
|
+
return { scaffold, scripts, skills, precommit };
|
|
33237
32793
|
}
|
|
33238
32794
|
|
|
33239
32795
|
// src/init/parse-args.ts
|
|
33240
|
-
var SKELETON_INIT_FLAGS = new Set(["--
|
|
32796
|
+
var SKELETON_INIT_FLAGS = new Set(["--skills", "--no-skills"]);
|
|
33241
32797
|
function parseInitArgs(argv) {
|
|
33242
|
-
|
|
32798
|
+
if (argv.includes("--force-hooks")) {
|
|
32799
|
+
throw new Error("init: --force-hooks was removed with customize hooks");
|
|
32800
|
+
}
|
|
33243
32801
|
const noSkills = argv.includes("--no-skills");
|
|
33244
32802
|
const skills = argv.includes("--skills");
|
|
33245
32803
|
const skillsFlags = [];
|
|
@@ -33254,14 +32812,14 @@ function parseInitArgs(argv) {
|
|
|
33254
32812
|
if (skills)
|
|
33255
32813
|
skillsFlags.push(arg);
|
|
33256
32814
|
}
|
|
33257
|
-
return {
|
|
32815
|
+
return { skills, noSkills, skillsFlags };
|
|
33258
32816
|
}
|
|
33259
32817
|
|
|
33260
32818
|
// src/plugins/build.ts
|
|
33261
32819
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
33262
32820
|
import { createHash as createHash2 } from "node:crypto";
|
|
33263
|
-
import { existsSync as
|
|
33264
|
-
import { basename as
|
|
32821
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync6 } from "node:fs";
|
|
32822
|
+
import { basename as basename3, dirname as dirname11, resolve as resolve6 } from "node:path";
|
|
33265
32823
|
function parseBuildPluginArgs(argv) {
|
|
33266
32824
|
let check = false;
|
|
33267
32825
|
let entry;
|
|
@@ -33286,7 +32844,7 @@ function collectPluginEntries(root2, config, entry) {
|
|
|
33286
32844
|
return (config.plugins ?? []).map((e) => resolvePluginTsPath(root2, e));
|
|
33287
32845
|
}
|
|
33288
32846
|
function localImportPaths(tsAbs, content3) {
|
|
33289
|
-
const dir =
|
|
32847
|
+
const dir = dirname11(tsAbs);
|
|
33290
32848
|
const deps = [];
|
|
33291
32849
|
const re = /(?:from\s+|import\s*\(\s*|import\s+)["'](\.[^"']+)["']/g;
|
|
33292
32850
|
for (const match of content3.matchAll(re)) {
|
|
@@ -33296,12 +32854,12 @@ function localImportPaths(tsAbs, content3) {
|
|
|
33296
32854
|
const candidates = [];
|
|
33297
32855
|
if (spec.endsWith(".js")) {
|
|
33298
32856
|
const withoutJs = spec.slice(0, -".js".length);
|
|
33299
|
-
candidates.push(
|
|
32857
|
+
candidates.push(resolve6(dir, `${withoutJs}.ts`), resolve6(dir, spec), resolve6(dir, withoutJs, "index.ts"));
|
|
33300
32858
|
} else {
|
|
33301
|
-
candidates.push(
|
|
32859
|
+
candidates.push(resolve6(dir, spec), resolve6(dir, `${spec}.ts`), resolve6(dir, `${spec}.js`), resolve6(dir, spec, "index.ts"));
|
|
33302
32860
|
}
|
|
33303
32861
|
for (const candidate of candidates) {
|
|
33304
|
-
if (
|
|
32862
|
+
if (existsSync22(candidate) && candidate.endsWith(".ts")) {
|
|
33305
32863
|
deps.push(candidate);
|
|
33306
32864
|
break;
|
|
33307
32865
|
}
|
|
@@ -33318,8 +32876,8 @@ function sourceFingerprint(tsAbs, seen = new Set) {
|
|
|
33318
32876
|
if (seen.has(abs))
|
|
33319
32877
|
return;
|
|
33320
32878
|
seen.add(abs);
|
|
33321
|
-
const content3 =
|
|
33322
|
-
hash2.update(
|
|
32879
|
+
const content3 = readFileSync21(abs, "utf8");
|
|
32880
|
+
hash2.update(basename3(abs));
|
|
33323
32881
|
hash2.update("\x00");
|
|
33324
32882
|
hash2.update(content3);
|
|
33325
32883
|
hash2.update("\x00");
|
|
@@ -33336,7 +32894,7 @@ function writeStamp(tsAbs, mjsAbs) {
|
|
|
33336
32894
|
}
|
|
33337
32895
|
async function buildOne(tsAbs) {
|
|
33338
32896
|
const mjsAbs = mjsPathForTs(tsAbs);
|
|
33339
|
-
if (!
|
|
32897
|
+
if (!existsSync22(tsAbs)) {
|
|
33340
32898
|
throw new Error(`Plugin source not found: ${tsAbs}`);
|
|
33341
32899
|
}
|
|
33342
32900
|
const proc = spawnSync4("bun", ["build", tsAbs, "--target=node", "--format=esm", `--outfile=${mjsAbs}`, "--packages=external"], { encoding: "utf8" });
|
|
@@ -33356,17 +32914,17 @@ ${proc.stderr || proc.stdout || `exit ${proc.status}`}`);
|
|
|
33356
32914
|
}
|
|
33357
32915
|
function checkOne(tsAbs) {
|
|
33358
32916
|
const mjsAbs = mjsPathForTs(tsAbs);
|
|
33359
|
-
if (!
|
|
32917
|
+
if (!existsSync22(mjsAbs)) {
|
|
33360
32918
|
throw new Error(`Plugin not built: ${tsAbs} (missing ${mjsAbs}). Run: skeleton build-plugin`);
|
|
33361
32919
|
}
|
|
33362
|
-
if (!
|
|
32920
|
+
if (!existsSync22(tsAbs)) {
|
|
33363
32921
|
throw new Error(`Plugin source not found: ${tsAbs}`);
|
|
33364
32922
|
}
|
|
33365
32923
|
const stampAbs = stampPathForMjs(mjsAbs);
|
|
33366
|
-
if (!
|
|
32924
|
+
if (!existsSync22(stampAbs)) {
|
|
33367
32925
|
throw new Error(`Plugin stale: ${mjsAbs} has no fingerprint stamp. Run: skeleton build-plugin`);
|
|
33368
32926
|
}
|
|
33369
|
-
const expected =
|
|
32927
|
+
const expected = readFileSync21(stampAbs, "utf8").trim();
|
|
33370
32928
|
const actual = sourceFingerprint(tsAbs);
|
|
33371
32929
|
if (expected !== actual) {
|
|
33372
32930
|
throw new Error(`Plugin stale: ${mjsAbs} does not match ${tsAbs} (or local imports). Run: skeleton build-plugin`);
|
|
@@ -33396,8 +32954,8 @@ async function runBuildPlugin(options = {}) {
|
|
|
33396
32954
|
|
|
33397
32955
|
// src/validate/changed.ts
|
|
33398
32956
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
33399
|
-
import { existsSync as
|
|
33400
|
-
import { basename as
|
|
32957
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
|
|
32958
|
+
import { basename as basename4, extname as extname2, join as join20 } from "node:path";
|
|
33401
32959
|
|
|
33402
32960
|
// src/validate/git-diff.ts
|
|
33403
32961
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -33477,7 +33035,7 @@ function isSkeletonYamlCandidate(normalized, ext) {
|
|
|
33477
33035
|
if (!(normalized.startsWith(".skeleton/") || normalized.startsWith(".skeleton\\"))) {
|
|
33478
33036
|
return false;
|
|
33479
33037
|
}
|
|
33480
|
-
const name =
|
|
33038
|
+
const name = basename4(normalized).toLowerCase();
|
|
33481
33039
|
if (name === "config.yaml" || name === "config.yml")
|
|
33482
33040
|
return false;
|
|
33483
33041
|
return true;
|
|
@@ -33502,7 +33060,7 @@ function bucketForDocPath(normalized, ctx) {
|
|
|
33502
33060
|
function bucketFor(relPath2, ctx) {
|
|
33503
33061
|
const normalized = normalizeRelPath(relPath2);
|
|
33504
33062
|
const ext = extname2(normalized).toLowerCase();
|
|
33505
|
-
const name =
|
|
33063
|
+
const name = basename4(normalized);
|
|
33506
33064
|
if (CODE_EXTENSIONS.has(ext))
|
|
33507
33065
|
return "code";
|
|
33508
33066
|
if (COMMAND_CONFIG_NAMES.has(name))
|
|
@@ -33564,7 +33122,7 @@ function validatePolicy(relPath2, root2, fileSource) {
|
|
|
33564
33122
|
if (content3 === null)
|
|
33565
33123
|
return validationIssue("invalid-policy", relPath2, "path not found");
|
|
33566
33124
|
try {
|
|
33567
|
-
loadPolicyFile(
|
|
33125
|
+
loadPolicyFile(join20(root2, relPath2), content3);
|
|
33568
33126
|
return null;
|
|
33569
33127
|
} catch (error) {
|
|
33570
33128
|
return validationIssue("invalid-policy", relPath2, `invalid policy: ${error}`);
|
|
@@ -33580,7 +33138,7 @@ function validateShell(relPath2, root2, fileSource) {
|
|
|
33580
33138
|
return null;
|
|
33581
33139
|
return validationIssue("invalid-shell", relPath2, `shell syntax check failed: ${bash2.stderr}`);
|
|
33582
33140
|
}
|
|
33583
|
-
const abs =
|
|
33141
|
+
const abs = join20(root2, relPath2);
|
|
33584
33142
|
const shellcheck = spawnSync6("shellcheck", [abs], { encoding: "utf8" });
|
|
33585
33143
|
if (shellcheck.status === 0)
|
|
33586
33144
|
return null;
|
|
@@ -33604,11 +33162,11 @@ function resolvePaths(options) {
|
|
|
33604
33162
|
};
|
|
33605
33163
|
}
|
|
33606
33164
|
function packageManagerFromPackageJson(root2) {
|
|
33607
|
-
const pkgPath =
|
|
33608
|
-
if (!
|
|
33165
|
+
const pkgPath = join20(root2, "package.json");
|
|
33166
|
+
if (!existsSync23(pkgPath))
|
|
33609
33167
|
return null;
|
|
33610
33168
|
try {
|
|
33611
|
-
const pkg = JSON.parse(
|
|
33169
|
+
const pkg = JSON.parse(readFileSync22(pkgPath, "utf8"));
|
|
33612
33170
|
const raw = pkg.packageManager?.split("@")[0];
|
|
33613
33171
|
if (raw === "bun" || raw === "npm" || raw === "pnpm" || raw === "yarn")
|
|
33614
33172
|
return raw;
|
|
@@ -33616,13 +33174,13 @@ function packageManagerFromPackageJson(root2) {
|
|
|
33616
33174
|
return null;
|
|
33617
33175
|
}
|
|
33618
33176
|
function packageManagerFromLockfiles(root2) {
|
|
33619
|
-
if (
|
|
33177
|
+
if (existsSync23(join20(root2, "bun.lock")) || existsSync23(join20(root2, "bun.lockb")))
|
|
33620
33178
|
return "bun";
|
|
33621
|
-
if (
|
|
33179
|
+
if (existsSync23(join20(root2, "pnpm-lock.yaml")))
|
|
33622
33180
|
return "pnpm";
|
|
33623
|
-
if (
|
|
33181
|
+
if (existsSync23(join20(root2, "yarn.lock")))
|
|
33624
33182
|
return "yarn";
|
|
33625
|
-
if (
|
|
33183
|
+
if (existsSync23(join20(root2, "package-lock.json")))
|
|
33626
33184
|
return "npm";
|
|
33627
33185
|
return null;
|
|
33628
33186
|
}
|
|
@@ -33648,8 +33206,8 @@ function emptyBuckets() {
|
|
|
33648
33206
|
function classifySinglePath(input) {
|
|
33649
33207
|
const { relPath: relPath2, ctx, state, bucketCtx } = input;
|
|
33650
33208
|
const normalized = normalizeRelPath(relPath2);
|
|
33651
|
-
const abs =
|
|
33652
|
-
if (!
|
|
33209
|
+
const abs = join20(ctx.root, normalized);
|
|
33210
|
+
if (!existsSync23(abs)) {
|
|
33653
33211
|
state.missing.push(normalized);
|
|
33654
33212
|
return;
|
|
33655
33213
|
}
|
|
@@ -33950,7 +33508,8 @@ async function evaluateValidateChanged(options = {}) {
|
|
|
33950
33508
|
skillIndex,
|
|
33951
33509
|
fileSource
|
|
33952
33510
|
});
|
|
33953
|
-
const
|
|
33511
|
+
const livePaths = relPaths.filter((path3) => !resolvedPaths.deleted.has(path3));
|
|
33512
|
+
const coverageCandidateCount = livePaths.filter((path3) => pathRequiresReviewCoverage(path3, config)).length;
|
|
33954
33513
|
const diagnostics = [
|
|
33955
33514
|
...classificationDiagnostics({
|
|
33956
33515
|
classification,
|
|
@@ -33958,7 +33517,7 @@ async function evaluateValidateChanged(options = {}) {
|
|
|
33958
33517
|
base: options.base,
|
|
33959
33518
|
coverageCandidateCount
|
|
33960
33519
|
}),
|
|
33961
|
-
...uncoveredChangedPathDiagnostics(
|
|
33520
|
+
...uncoveredChangedPathDiagnostics(livePaths, config, ownerPatterns),
|
|
33962
33521
|
...stageRequiredDiagnostics({
|
|
33963
33522
|
staged: options.staged ?? false,
|
|
33964
33523
|
stagedPaths: relPaths,
|
|
@@ -34033,16 +33592,14 @@ function usage() {
|
|
|
34033
33592
|
console.error(`Usage: skeleton <command>
|
|
34034
33593
|
|
|
34035
33594
|
Commands:
|
|
34036
|
-
init [--
|
|
33595
|
+
init [--skills] [--no-skills] [skills add flags…]
|
|
34037
33596
|
audit docs|self|skills [--strict] [--json] [--paths=a,b] [--only=rule]
|
|
34038
33597
|
[--fix[=doc-meta|anchors|ssot]] [--dry-run]
|
|
34039
33598
|
[--confirm-reviewed (doc-meta only; requires --paths)]
|
|
34040
33599
|
build-plugin [path] [--check]
|
|
34041
33600
|
validate changed [paths…] [--staged] [--base <ref>]
|
|
34042
33601
|
catalog [--check] [--strict] write or check .skeleton/catalog.md (gitignored)
|
|
34043
|
-
|
|
34044
|
-
hook customize (reads a host hook payload on stdin)
|
|
34045
|
-
Note: \`register\` was removed — add a source-of-truth marker to the file and run \`skeleton catalog\`.`);
|
|
33602
|
+
Note: \`register\`, \`customize\`, and \`hook\` were removed.`);
|
|
34046
33603
|
}
|
|
34047
33604
|
function parseValidateChangedArgs(rest) {
|
|
34048
33605
|
const paths = [];
|
|
@@ -34105,28 +33662,9 @@ function handleCatalog(argv) {
|
|
|
34105
33662
|
}
|
|
34106
33663
|
return runCatalogCli({ check: argv.includes("--check"), strict: argv.includes("--strict") });
|
|
34107
33664
|
}
|
|
34108
|
-
function
|
|
34109
|
-
|
|
34110
|
-
|
|
34111
|
-
if (!slug2) {
|
|
34112
|
-
console.error("customize resolve: slug required");
|
|
34113
|
-
return 1;
|
|
34114
|
-
}
|
|
34115
|
-
const result = resolveCustomizeFromRoot(slug2);
|
|
34116
|
-
if (json) {
|
|
34117
|
-
console.log(JSON.stringify(result, null, 2));
|
|
34118
|
-
} else if (result.content) {
|
|
34119
|
-
process6.stdout.write(result.content);
|
|
34120
|
-
}
|
|
34121
|
-
return 0;
|
|
34122
|
-
}
|
|
34123
|
-
function handleHook(argv) {
|
|
34124
|
-
if (argv[0] !== "customize") {
|
|
34125
|
-
usage();
|
|
34126
|
-
return 1;
|
|
34127
|
-
}
|
|
34128
|
-
process6.stdout.write(runCustomizeHook(readFileSync24(0, "utf8")));
|
|
34129
|
-
return 0;
|
|
33665
|
+
function handleRemovedOverlay(command) {
|
|
33666
|
+
console.error(`${command}: removed — overlay inject is gone. Edit the skill in its owning repo.`);
|
|
33667
|
+
return 1;
|
|
34130
33668
|
}
|
|
34131
33669
|
function handleInit(argv) {
|
|
34132
33670
|
runInit(parseInitArgs(argv));
|
|
@@ -34147,9 +33685,8 @@ async function dispatchCommand(argv) {
|
|
|
34147
33685
|
case "catalog":
|
|
34148
33686
|
return handleCatalog(rest);
|
|
34149
33687
|
case "customize":
|
|
34150
|
-
return rest[0] === "resolve" ? handleCustomizeResolve(rest.slice(1)) : null;
|
|
34151
33688
|
case "hook":
|
|
34152
|
-
return
|
|
33689
|
+
return handleRemovedOverlay(command);
|
|
34153
33690
|
case "init":
|
|
34154
33691
|
return handleInit(rest);
|
|
34155
33692
|
default:
|
|
@@ -34157,22 +33694,22 @@ async function dispatchCommand(argv) {
|
|
|
34157
33694
|
}
|
|
34158
33695
|
}
|
|
34159
33696
|
async function main() {
|
|
34160
|
-
const argv =
|
|
33697
|
+
const argv = process5.argv.slice(2);
|
|
34161
33698
|
const command = argv[0];
|
|
34162
33699
|
if (!command || command === "--help" || command === "-h") {
|
|
34163
33700
|
usage();
|
|
34164
|
-
|
|
33701
|
+
process5.exit(command ? 0 : 1);
|
|
34165
33702
|
}
|
|
34166
33703
|
try {
|
|
34167
33704
|
const exitCode = await dispatchCommand(argv);
|
|
34168
33705
|
if (exitCode === null) {
|
|
34169
33706
|
usage();
|
|
34170
|
-
|
|
33707
|
+
process5.exit(1);
|
|
34171
33708
|
}
|
|
34172
|
-
|
|
33709
|
+
process5.exit(exitCode);
|
|
34173
33710
|
} catch (error) {
|
|
34174
33711
|
console.error(String(error));
|
|
34175
|
-
|
|
33712
|
+
process5.exit(1);
|
|
34176
33713
|
}
|
|
34177
33714
|
}
|
|
34178
33715
|
main();
|