@hasna/skills 0.10.16 → 0.10.19
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 +15 -4
- package/bin/index.js +445 -39
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/runtime-copyfile.d.ts +9 -0
- package/dist/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -466,8 +466,18 @@ preserves and reads back the original policy before writing. Claude upgrades to
|
|
|
466
466
|
verbosity preferences. Provider mappings, environment, hooks, plugins, skill rules
|
|
467
467
|
and all unknown settings stay bound. Runtime hooks never silently upgrade a witness.
|
|
468
468
|
|
|
469
|
-
A
|
|
470
|
-
|
|
469
|
+
A Codex 0.159.2 native `skills/list` receipt can be captured through the
|
|
470
|
+
package-owned client into a new private file, then reviewed and passed to
|
|
471
|
+
`hook install` with `--codex-native-catalog <file>`:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
skills hook native-catalog --cwd /absolute/project --output /private/catalog.json --json
|
|
475
|
+
skills hook install --agent codex --codex-native-catalog /private/catalog.json --json
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The capture refuses an existing output file, writes mode `0600`, verifies the
|
|
479
|
+
exact bytes, and reports only version, directory, count, file path and SHA-256.
|
|
480
|
+
Its safe catalog shape is `{version,cwd,skills}`;
|
|
471
481
|
each skill has `{name,path,enabled,pluginId}`. The installer matches the native
|
|
472
482
|
qualified name against a bounded explicit frontmatter name and owning plugin
|
|
473
483
|
manifest. It preserves path disables and adds exact qualified name disables.
|
|
@@ -480,8 +490,9 @@ Only manifest version metadata may vary. Reviewed app declarations use the impli
|
|
|
480
490
|
file and its presence stay bound; it preserves existing connector availability and
|
|
481
491
|
native capability hints. Changed or unknown app fields, custom references, native
|
|
482
492
|
hooks, commands and MCP controls refuse. Name controls suppress Skills, not every
|
|
483
|
-
plugin capability. No prompt hook starts an app server to obtain this receipt:
|
|
484
|
-
startup can maintain caches.
|
|
493
|
+
plugin capability. No prompt hook starts an app server to obtain this receipt:
|
|
494
|
+
native startup can maintain caches. The explicit capture starts and closes only its
|
|
495
|
+
own bounded native client; it does not reload an existing agent session.
|
|
485
496
|
Neither a receipt nor a config write proves an existing session adopted new rules;
|
|
486
497
|
use the actual native session's supported acceptance path separately.
|
|
487
498
|
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.19",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51052,6 +51052,24 @@ function projectCodexNativeSkillCatalog(response, cwd2) {
|
|
|
51052
51052
|
return { name: value.name, path: value.path, enabled: value.enabled, pluginId: value.pluginId ?? null };
|
|
51053
51053
|
});
|
|
51054
51054
|
}
|
|
51055
|
+
async function captureCodexNativeSkillCatalog(options, connect = connectCodexHookRpc) {
|
|
51056
|
+
if (!absolutePath2(options.cwd) || !absolutePath2(options.home))
|
|
51057
|
+
refuse();
|
|
51058
|
+
const rpc = await connect({
|
|
51059
|
+
command: options.command,
|
|
51060
|
+
home: options.home,
|
|
51061
|
+
...options.codexHome === undefined ? {} : { codexHome: options.codexHome },
|
|
51062
|
+
...options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }
|
|
51063
|
+
});
|
|
51064
|
+
try {
|
|
51065
|
+
if (!SUPPORTED_NAME_CONTROL_VERSIONS.has(rpc.version))
|
|
51066
|
+
throw new Error("CODEX_NATIVE_SKILL_CATALOG_UNSUPPORTED_VERSION");
|
|
51067
|
+
const response = await rpc.request("skills/list", { cwds: [options.cwd], forceReload: true });
|
|
51068
|
+
return { version: rpc.version, cwd: options.cwd, skills: projectCodexNativeSkillCatalog(response, options.cwd) };
|
|
51069
|
+
} finally {
|
|
51070
|
+
await rpc.close();
|
|
51071
|
+
}
|
|
51072
|
+
}
|
|
51055
51073
|
function canonicalPath(path) {
|
|
51056
51074
|
try {
|
|
51057
51075
|
return realpathSync4(path);
|
|
@@ -108698,13 +108716,13 @@ function comparePackageTrees(expectedRoot, installedRoot) {
|
|
|
108698
108716
|
throw new Error("INSTALLED_PACKAGE_BYTES_MISMATCH");
|
|
108699
108717
|
return expected.digest;
|
|
108700
108718
|
}
|
|
108701
|
-
function replaceLink(path, target, backupPath, oldLinkTarget) {
|
|
108719
|
+
function replaceLink(path, target, backupPath, oldLinkTarget, oldTarget = resolve23(dirname30(path), oldLinkTarget)) {
|
|
108702
108720
|
const stat3 = lstatSync28(path);
|
|
108703
108721
|
if (!stat3.isSymbolicLink() || entryExists(backupPath))
|
|
108704
108722
|
throw new Error("LAUNCHER_PREIMAGE_DRIFT");
|
|
108705
108723
|
const temp = `${path}.skills-next-${randomUUID9()}`;
|
|
108706
108724
|
symlinkSync(oldLinkTarget, backupPath);
|
|
108707
|
-
if (readlinkSync5(backupPath) !== oldLinkTarget || realpathSync14(backupPath) !==
|
|
108725
|
+
if (readlinkSync5(backupPath) !== oldLinkTarget || realpathSync14(backupPath) !== oldTarget)
|
|
108708
108726
|
throw new Error("LAUNCHER_BACKUP_READBACK_MISMATCH");
|
|
108709
108727
|
symlinkSync(target, temp);
|
|
108710
108728
|
if (realpathSync14(temp) !== target)
|
|
@@ -108745,6 +108763,324 @@ function persistReceipt(path, value) {
|
|
|
108745
108763
|
}
|
|
108746
108764
|
atomicJson(path, value);
|
|
108747
108765
|
}
|
|
108766
|
+
function persistAliasReceipt(path, value) {
|
|
108767
|
+
if (entryExists(path)) {
|
|
108768
|
+
const priorBytes = readFileSync37(path);
|
|
108769
|
+
const prior = object9(JSON.parse(priorBytes.toString("utf8")), "ALIAS_RECEIPT_INVALID");
|
|
108770
|
+
if (prior.schema !== value.schema || prior.id !== value.id || typeof prior.state !== "string")
|
|
108771
|
+
throw new Error("ALIAS_RECEIPT_DRIFT");
|
|
108772
|
+
const nextBytes = Buffer.from(`${JSON.stringify(value, null, 2)}
|
|
108773
|
+
`);
|
|
108774
|
+
if (priorBytes.equals(nextBytes))
|
|
108775
|
+
return;
|
|
108776
|
+
const history = join55(dirname30(path), "history");
|
|
108777
|
+
if (!entryExists(history))
|
|
108778
|
+
mkdirSync24(history, { mode: 448 });
|
|
108779
|
+
const historyStat = lstatSync28(history);
|
|
108780
|
+
if (!historyStat.isDirectory() || historyStat.isSymbolicLink() || (historyStat.mode & 63) !== 0)
|
|
108781
|
+
throw new Error("ALIAS_RECEIPT_HISTORY_UNSAFE");
|
|
108782
|
+
const saved = join55(history, `${value.id}-${String(prior.state)}-${randomUUID9()}.json`);
|
|
108783
|
+
writeFileSync23(saved, priorBytes, { flag: "wx", mode: 256 });
|
|
108784
|
+
chmodSync4(saved, 256);
|
|
108785
|
+
if (!readFileSync37(saved).equals(priorBytes))
|
|
108786
|
+
throw new Error("ALIAS_RECEIPT_HISTORY_READBACK_MISMATCH");
|
|
108787
|
+
}
|
|
108788
|
+
atomicJson(path, value);
|
|
108789
|
+
}
|
|
108790
|
+
function assertOwnedSafePath(path, home, requireWritable = false) {
|
|
108791
|
+
assertNoSymlinkAncestors(path);
|
|
108792
|
+
for (let current = path;; current = dirname30(current)) {
|
|
108793
|
+
const stat3 = lstatSync28(current);
|
|
108794
|
+
if (!stat3.isDirectory() || ![0, process.getuid?.() ?? -1].includes(stat3.uid) || stat3.mode & 18)
|
|
108795
|
+
throw new Error("ALIAS_PATH_UNSAFE");
|
|
108796
|
+
if (current === path && requireWritable && (stat3.uid !== (process.getuid?.() ?? -1) || (stat3.mode & 128) === 0))
|
|
108797
|
+
throw new Error("ALIAS_DIRECTORY_NOT_OWNED");
|
|
108798
|
+
if (current === dirname30(current) || current === home)
|
|
108799
|
+
break;
|
|
108800
|
+
}
|
|
108801
|
+
}
|
|
108802
|
+
function activeAliasRuntime(home, pathValue) {
|
|
108803
|
+
const layout = resolveRuntimeLayout(home, pathValue);
|
|
108804
|
+
const runtimePath = dirname30(dirname30(dirname30(layout.currentPackageRoot)));
|
|
108805
|
+
const receiptPath = join55(runtimePath, "rollout-receipt.json");
|
|
108806
|
+
const rollout = object9(JSON.parse(readFileSync37(receiptPath, "utf8")), "ROLLOUT_RECEIPT_INVALID");
|
|
108807
|
+
if (rollout.schema !== "skills.copyfile-runtime-receipt.v1" || rollout.state !== "switched" || rollout.targetPackageRoot !== layout.currentPackageRoot || rollout.targetVersion !== layout.currentVersion || rollout.runtimeRoot !== layout.runtimeRoot || rollout.package !== PACKAGE_NAME || hash5(readFileSync37(join55(runtimePath, "verified-package.tgz"))) !== rollout.tarballSha256 || readTree(join55(runtimePath, "node_modules"), true).digest !== rollout.runtimeTreeSha256) {
|
|
108808
|
+
throw new Error("ACTIVE_RUNTIME_RECEIPT_DRIFT");
|
|
108809
|
+
}
|
|
108810
|
+
verifyPreimageManifest(runtimePath, rollout, home);
|
|
108811
|
+
return { layout, rollout };
|
|
108812
|
+
}
|
|
108813
|
+
function aliasDirs(home) {
|
|
108814
|
+
return [join55(home, ".local", "bin"), join55(home, ".bun", "bin"), "/opt/homebrew/bin", "/usr/local/bin"];
|
|
108815
|
+
}
|
|
108816
|
+
function aliasPackageLink(home, aliasPath) {
|
|
108817
|
+
const dir = dirname30(aliasPath);
|
|
108818
|
+
if (dir === join55(home, ".local", "bin"))
|
|
108819
|
+
return join55(home, ".local", "lib", "node_modules", "@hasna", "skills");
|
|
108820
|
+
if (dir === "/opt/homebrew/bin")
|
|
108821
|
+
return "/opt/homebrew/lib/node_modules/@hasna/skills";
|
|
108822
|
+
if (dir === "/usr/local/bin")
|
|
108823
|
+
return "/usr/local/lib/node_modules/@hasna/skills";
|
|
108824
|
+
return null;
|
|
108825
|
+
}
|
|
108826
|
+
function inspectAliasPackageChain(home, aliasPath, oldLinkTarget, oldTarget, bin, expected, verify = false) {
|
|
108827
|
+
const immediate = resolve23(dirname30(aliasPath), oldLinkTarget);
|
|
108828
|
+
if (immediate === oldTarget) {
|
|
108829
|
+
if (expected !== undefined)
|
|
108830
|
+
throw new Error("ALIAS_LINK_CHAIN_DRIFT");
|
|
108831
|
+
return;
|
|
108832
|
+
}
|
|
108833
|
+
const packageLinkPath = aliasPackageLink(home, aliasPath);
|
|
108834
|
+
const physicalPackage = dirname30(dirname30(oldTarget));
|
|
108835
|
+
if (!packageLinkPath || immediate !== join55(packageLinkPath, bin) || !isWithin(join55(home, ".hasna", "skills", "runtime"), physicalPackage)) {
|
|
108836
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108837
|
+
}
|
|
108838
|
+
assertOwnedSafePath(dirname30(packageLinkPath), home);
|
|
108839
|
+
let stat3, packageLinkTarget, resolved;
|
|
108840
|
+
try {
|
|
108841
|
+
stat3 = lstatSync28(packageLinkPath);
|
|
108842
|
+
packageLinkTarget = readlinkSync5(packageLinkPath);
|
|
108843
|
+
resolved = realpathSync14(packageLinkPath);
|
|
108844
|
+
} catch {
|
|
108845
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108846
|
+
}
|
|
108847
|
+
if (expected !== undefined && (expected.packageLinkPath !== packageLinkPath || expected.packageLinkTarget !== packageLinkTarget))
|
|
108848
|
+
throw new Error("ALIAS_LINK_CHAIN_DRIFT");
|
|
108849
|
+
if (!stat3.isSymbolicLink() || stat3.uid !== (process.getuid?.() ?? -1) || resolve23(dirname30(packageLinkPath), packageLinkTarget) !== physicalPackage || resolved !== physicalPackage)
|
|
108850
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108851
|
+
const chain = { packageLinkPath, packageLinkTarget };
|
|
108852
|
+
if (verify && expected === undefined)
|
|
108853
|
+
throw new Error("ALIAS_LINK_CHAIN_DRIFT");
|
|
108854
|
+
return chain;
|
|
108855
|
+
}
|
|
108856
|
+
function validateAliasSource(home, name, actual) {
|
|
108857
|
+
const packageRoot = dirname30(dirname30(actual));
|
|
108858
|
+
const roots = [join55(home, ".hasna", "skills", "runtime"), join55(home, ".bun", "install"), join55(home, ".local", "lib", "node_modules")];
|
|
108859
|
+
if (basename13(packageRoot) !== "skills" || basename13(dirname30(packageRoot)) !== "@hasna" || !roots.some((root2) => isWithin(root2, packageRoot))) {
|
|
108860
|
+
throw new Error("ALIAS_SOURCE_ROOT_UNSUPPORTED");
|
|
108861
|
+
}
|
|
108862
|
+
assertOwnedSafePath(packageRoot, home);
|
|
108863
|
+
const manifestPath = join55(packageRoot, "package.json");
|
|
108864
|
+
const manifestStat = lstatSync28(manifestPath), binaryStat = lstatSync28(actual);
|
|
108865
|
+
if (!manifestStat.isFile() || manifestStat.isSymbolicLink() || !binaryStat.isFile() || binaryStat.isSymbolicLink() || ![0, process.getuid?.() ?? -1].includes(manifestStat.uid) || ![0, process.getuid?.() ?? -1].includes(binaryStat.uid) || manifestStat.mode & 18)
|
|
108866
|
+
throw new Error("ALIAS_SOURCE_UNSAFE");
|
|
108867
|
+
const pkg = readPackage(manifestPath);
|
|
108868
|
+
if (!pkg.bins[name] || actual !== join55(packageRoot, pkg.bins[name]))
|
|
108869
|
+
throw new Error("ALIAS_SOURCE_BIN_MISMATCH");
|
|
108870
|
+
return { version: pkg.version, packageRoot, binarySha256: hash5(readFileSync37(actual)) };
|
|
108871
|
+
}
|
|
108872
|
+
function adoptCopyfileAliases(options = {}) {
|
|
108873
|
+
const home = realpathSync14(resolve23(options.homeDir ?? process.env.HOME ?? ""));
|
|
108874
|
+
const pathValue = options.pathValue ?? process.env.PATH ?? "";
|
|
108875
|
+
const { layout, rollout } = activeAliasRuntime(home, pathValue);
|
|
108876
|
+
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
108877
|
+
const id2 = randomUUID9();
|
|
108878
|
+
const aliases = [];
|
|
108879
|
+
const currentRuntime = dirname30(dirname30(dirname30(layout.currentPackageRoot)));
|
|
108880
|
+
const receiptDir = join55(currentRuntime, "alias-adoptions");
|
|
108881
|
+
const receiptPath = join55(receiptDir, `${id2}.json`);
|
|
108882
|
+
let receipt;
|
|
108883
|
+
try {
|
|
108884
|
+
const locked2 = activeAliasRuntime(home, pathValue);
|
|
108885
|
+
if (locked2.layout.currentPackageRoot !== layout.currentPackageRoot || locked2.rollout.id !== rollout.id)
|
|
108886
|
+
throw new Error("ACTIVE_RUNTIME_CHANGED_BEFORE_ALIAS_ADOPTION");
|
|
108887
|
+
for (const dir of aliasDirs(home)) {
|
|
108888
|
+
if (!entryExists(dir))
|
|
108889
|
+
continue;
|
|
108890
|
+
for (const [name, target] of Object.entries(layout.bin)) {
|
|
108891
|
+
const path = join55(dir, name);
|
|
108892
|
+
if (!entryExists(path))
|
|
108893
|
+
continue;
|
|
108894
|
+
assertOwnedSafePath(dir, home, true);
|
|
108895
|
+
const stat3 = lstatSync28(path);
|
|
108896
|
+
if (!stat3.isSymbolicLink() || stat3.uid !== (process.getuid?.() ?? -1))
|
|
108897
|
+
throw new Error("ALIAS_LINK_NOT_OWNED");
|
|
108898
|
+
const oldLinkTarget = readlinkSync5(path);
|
|
108899
|
+
let actual;
|
|
108900
|
+
try {
|
|
108901
|
+
actual = realpathSync14(path);
|
|
108902
|
+
} catch {
|
|
108903
|
+
throw new Error("ALIAS_LINK_CHAIN_UNSUPPORTED");
|
|
108904
|
+
}
|
|
108905
|
+
const newTarget = join55(layout.currentPackageRoot, target);
|
|
108906
|
+
if (actual === newTarget)
|
|
108907
|
+
continue;
|
|
108908
|
+
const chain = inspectAliasPackageChain(home, path, oldLinkTarget, actual, target);
|
|
108909
|
+
const source = validateAliasSource(home, name, actual);
|
|
108910
|
+
const backupPath = `${path}.skills-alias-prev-${id2}`;
|
|
108911
|
+
if (entryExists(backupPath))
|
|
108912
|
+
throw new Error("ALIAS_BACKUP_COLLISION");
|
|
108913
|
+
aliases.push({ path, oldTarget: actual, oldLinkTarget, oldVersion: source.version, oldBinarySha256: source.binarySha256, newTarget, backupPath, ...chain ? { chain } : {} });
|
|
108914
|
+
}
|
|
108915
|
+
}
|
|
108916
|
+
if (!aliases.length)
|
|
108917
|
+
return { adopted: true, version: layout.currentVersion, aliasCount: 0 };
|
|
108918
|
+
mkdirSync24(receiptDir, { recursive: true, mode: 448 });
|
|
108919
|
+
const receiptDirStat = lstatSync28(receiptDir);
|
|
108920
|
+
if (!receiptDirStat.isDirectory() || receiptDirStat.isSymbolicLink() || receiptDirStat.mode & 63)
|
|
108921
|
+
throw new Error("ALIAS_RECEIPT_DIRECTORY_UNSAFE");
|
|
108922
|
+
receipt = {
|
|
108923
|
+
schema: "skills.copyfile-alias-adoption.v1",
|
|
108924
|
+
id: id2,
|
|
108925
|
+
state: "prepared",
|
|
108926
|
+
targetVersion: layout.currentVersion,
|
|
108927
|
+
targetPackageRoot: layout.currentPackageRoot,
|
|
108928
|
+
rolloutReceiptId: rollout.id,
|
|
108929
|
+
aliases,
|
|
108930
|
+
switchedAliases: [],
|
|
108931
|
+
rollbackCompletedAliases: []
|
|
108932
|
+
};
|
|
108933
|
+
writeJsonPrivate(receiptPath, receipt);
|
|
108934
|
+
for (const item of aliases) {
|
|
108935
|
+
if (readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget)
|
|
108936
|
+
throw new Error("ALIAS_CHANGED_BEFORE_SWITCH");
|
|
108937
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108938
|
+
const source = validateAliasSource(home, basename13(item.path), item.oldTarget);
|
|
108939
|
+
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
108940
|
+
throw new Error("ALIAS_SOURCE_DRIFT");
|
|
108941
|
+
receipt.state = "switching";
|
|
108942
|
+
receipt.switchedAliases = [...receipt.switchedAliases, item.path];
|
|
108943
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108944
|
+
replaceLink(item.path, item.newTarget, item.backupPath, item.oldLinkTarget, item.oldTarget);
|
|
108945
|
+
if (realpathSync14(item.path) !== item.newTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108946
|
+
throw new Error("ALIAS_SWITCH_READBACK_MISMATCH");
|
|
108947
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108948
|
+
options.onAliasSwitched?.(item.path);
|
|
108949
|
+
}
|
|
108950
|
+
for (const item of aliases) {
|
|
108951
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108952
|
+
if (readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108953
|
+
throw new Error("ALIAS_BACKUP_DRIFT");
|
|
108954
|
+
}
|
|
108955
|
+
receipt.state = "switched";
|
|
108956
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108957
|
+
return { adopted: true, version: layout.currentVersion, receiptId: id2, aliasCount: aliases.length };
|
|
108958
|
+
} catch (error61) {
|
|
108959
|
+
if (receipt) {
|
|
108960
|
+
let failed = false;
|
|
108961
|
+
for (const item of [...aliases].reverse()) {
|
|
108962
|
+
try {
|
|
108963
|
+
if (!entryExists(item.backupPath)) {
|
|
108964
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108965
|
+
const source2 = validateAliasSource(home, basename13(item.path), item.oldTarget);
|
|
108966
|
+
if (!entryExists(item.path) || readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget || source2.version !== item.oldVersion || source2.binarySha256 !== item.oldBinarySha256)
|
|
108967
|
+
failed = true;
|
|
108968
|
+
continue;
|
|
108969
|
+
}
|
|
108970
|
+
if (readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
108971
|
+
throw new Error("ALIAS_BACKUP_DRIFT");
|
|
108972
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
108973
|
+
const source = validateAliasSource(home, basename13(item.path), item.oldTarget);
|
|
108974
|
+
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
108975
|
+
throw new Error("ALIAS_SOURCE_DRIFT");
|
|
108976
|
+
if (realpathSync14(item.path) === item.oldTarget && readlinkSync5(item.path) === item.oldLinkTarget)
|
|
108977
|
+
continue;
|
|
108978
|
+
if (realpathSync14(item.path) !== item.newTarget)
|
|
108979
|
+
throw new Error("ALIAS_CHANGED_DURING_ROLLBACK");
|
|
108980
|
+
const after2 = `${item.path}.skills-alias-after-${id2}`;
|
|
108981
|
+
if (entryExists(after2))
|
|
108982
|
+
throw new Error("ALIAS_AFTER_COLLISION");
|
|
108983
|
+
symlinkSync(item.newTarget, after2);
|
|
108984
|
+
const temp = `${item.path}.skills-alias-rollback-${randomUUID9()}`;
|
|
108985
|
+
symlinkSync(item.oldLinkTarget, temp);
|
|
108986
|
+
renameSync13(temp, item.path);
|
|
108987
|
+
if (readlinkSync5(item.path) !== item.oldLinkTarget)
|
|
108988
|
+
throw new Error("ALIAS_ROLLBACK_READBACK_MISMATCH");
|
|
108989
|
+
receipt.rollbackCompletedAliases = [...receipt.rollbackCompletedAliases, item.path];
|
|
108990
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108991
|
+
} catch {
|
|
108992
|
+
failed = true;
|
|
108993
|
+
}
|
|
108994
|
+
}
|
|
108995
|
+
receipt.state = failed ? "rollback-required" : "rolled-back";
|
|
108996
|
+
try {
|
|
108997
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
108998
|
+
} catch {
|
|
108999
|
+
failed = true;
|
|
109000
|
+
}
|
|
109001
|
+
throw new Error(failed ? "ALIAS_ADOPTION_ROLLBACK_REQUIRED" : "ALIAS_ADOPTION_ROLLED_BACK");
|
|
109002
|
+
}
|
|
109003
|
+
throw error61 instanceof Error ? error61 : new Error("ALIAS_ADOPTION_FAILED");
|
|
109004
|
+
} finally {
|
|
109005
|
+
releaseLock();
|
|
109006
|
+
}
|
|
109007
|
+
}
|
|
109008
|
+
function rollbackCopyfileAliases(receiptId, options = {}) {
|
|
109009
|
+
if (!/^[0-9a-f-]{36}$/.test(receiptId))
|
|
109010
|
+
throw new Error("ALIAS_RECEIPT_ID_INVALID");
|
|
109011
|
+
const home = realpathSync14(resolve23(options.homeDir ?? process.env.HOME ?? ""));
|
|
109012
|
+
const pathValue = options.pathValue ?? process.env.PATH ?? "";
|
|
109013
|
+
const { layout, rollout } = activeAliasRuntime(home, pathValue);
|
|
109014
|
+
const runtimePath = dirname30(dirname30(dirname30(layout.currentPackageRoot)));
|
|
109015
|
+
const receiptPath = join55(runtimePath, "alias-adoptions", `${receiptId}.json`);
|
|
109016
|
+
const releaseLock = acquireRuntimeLock(layout.runtimeRoot);
|
|
109017
|
+
try {
|
|
109018
|
+
const locked2 = activeAliasRuntime(home, pathValue);
|
|
109019
|
+
if (locked2.layout.currentPackageRoot !== layout.currentPackageRoot || locked2.rollout.id !== rollout.id)
|
|
109020
|
+
throw new Error("ACTIVE_RUNTIME_CHANGED_BEFORE_ALIAS_ROLLBACK");
|
|
109021
|
+
const receipt = object9(JSON.parse(readFileSync37(receiptPath, "utf8")), "ALIAS_RECEIPT_INVALID");
|
|
109022
|
+
if (receipt.schema !== "skills.copyfile-alias-adoption.v1" || receipt.id !== receiptId || !["switched", "switching", "rollback-required"].includes(receipt.state) || receipt.targetPackageRoot !== layout.currentPackageRoot || receipt.targetVersion !== layout.currentVersion || receipt.rolloutReceiptId !== rollout.id || !Array.isArray(receipt.aliases))
|
|
109023
|
+
throw new Error("ALIAS_RECEIPT_BINDING_INVALID");
|
|
109024
|
+
const seen = new Set;
|
|
109025
|
+
for (const item of receipt.aliases) {
|
|
109026
|
+
const name = basename13(item.path), target = layout.bin[name];
|
|
109027
|
+
if (!target || seen.has(item.path) || !aliasDirs(home).includes(dirname30(item.path)) || item.newTarget !== join55(layout.currentPackageRoot, target) || item.backupPath !== `${item.path}.skills-alias-prev-${receiptId}` || item.chain !== undefined && (!item.chain || typeof item.chain !== "object" || typeof item.chain.packageLinkPath !== "string" || typeof item.chain.packageLinkTarget !== "string")) {
|
|
109028
|
+
throw new Error("ALIAS_RECEIPT_LAUNCHER_INVALID");
|
|
109029
|
+
}
|
|
109030
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, target, item.chain, true);
|
|
109031
|
+
seen.add(item.path);
|
|
109032
|
+
assertOwnedSafePath(dirname30(item.path), home, true);
|
|
109033
|
+
const source = validateAliasSource(home, name, item.oldTarget);
|
|
109034
|
+
if (source.version !== item.oldVersion || source.binarySha256 !== item.oldBinarySha256)
|
|
109035
|
+
throw new Error("ALIAS_PREIMAGE_DRIFT");
|
|
109036
|
+
const stat3 = lstatSync28(item.path);
|
|
109037
|
+
if (!stat3.isSymbolicLink() || stat3.uid !== (process.getuid?.() ?? -1))
|
|
109038
|
+
throw new Error("ALIAS_LINK_DRIFT");
|
|
109039
|
+
const current = realpathSync14(item.path);
|
|
109040
|
+
if (current !== item.newTarget && (current !== item.oldTarget || readlinkSync5(item.path) !== item.oldLinkTarget))
|
|
109041
|
+
throw new Error("ALIAS_TARGET_DRIFT");
|
|
109042
|
+
if (entryExists(item.backupPath)) {
|
|
109043
|
+
const backup = lstatSync28(item.backupPath);
|
|
109044
|
+
if (!backup.isSymbolicLink() || readlinkSync5(item.backupPath) !== item.oldLinkTarget || realpathSync14(item.backupPath) !== item.oldTarget)
|
|
109045
|
+
throw new Error("ALIAS_PREIMAGE_DRIFT");
|
|
109046
|
+
} else if (current !== item.oldTarget) {
|
|
109047
|
+
throw new Error("ALIAS_BACKUP_MISSING_FOR_SWITCH");
|
|
109048
|
+
}
|
|
109049
|
+
}
|
|
109050
|
+
let restored = 0;
|
|
109051
|
+
for (const item of [...receipt.aliases].reverse()) {
|
|
109052
|
+
if (realpathSync14(item.path) === item.oldTarget && readlinkSync5(item.path) === item.oldLinkTarget)
|
|
109053
|
+
continue;
|
|
109054
|
+
receipt.state = "rollback-required";
|
|
109055
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
109056
|
+
const after2 = `${item.path}.skills-alias-after-${receiptId}`;
|
|
109057
|
+
if (!entryExists(after2)) {
|
|
109058
|
+
symlinkSync(item.newTarget, after2);
|
|
109059
|
+
if (realpathSync14(after2) !== item.newTarget)
|
|
109060
|
+
throw new Error("ALIAS_AFTER_READBACK_MISMATCH");
|
|
109061
|
+
} else if (realpathSync14(after2) !== item.newTarget)
|
|
109062
|
+
throw new Error("ALIAS_AFTER_DRIFT");
|
|
109063
|
+
if (realpathSync14(item.path) !== item.newTarget)
|
|
109064
|
+
throw new Error("ALIAS_TARGET_DRIFT");
|
|
109065
|
+
const temp = `${item.path}.skills-alias-rollback-${randomUUID9()}`;
|
|
109066
|
+
symlinkSync(item.oldLinkTarget, temp);
|
|
109067
|
+
renameSync13(temp, item.path);
|
|
109068
|
+
if (readlinkSync5(item.path) !== item.oldLinkTarget || realpathSync14(item.path) !== item.oldTarget)
|
|
109069
|
+
throw new Error("ALIAS_ROLLBACK_READBACK_MISMATCH");
|
|
109070
|
+
receipt.rollbackCompletedAliases = [...new Set([...receipt.rollbackCompletedAliases, item.path])];
|
|
109071
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
109072
|
+
restored++;
|
|
109073
|
+
}
|
|
109074
|
+
for (const item of receipt.aliases) {
|
|
109075
|
+
inspectAliasPackageChain(home, item.path, item.oldLinkTarget, item.oldTarget, layout.bin[basename13(item.path)], item.chain, true);
|
|
109076
|
+
}
|
|
109077
|
+
receipt.state = "rolled-back";
|
|
109078
|
+
persistAliasReceipt(receiptPath, receipt);
|
|
109079
|
+
return { rolledBack: true, receiptId, restoredAliasCount: restored };
|
|
109080
|
+
} finally {
|
|
109081
|
+
releaseLock();
|
|
109082
|
+
}
|
|
109083
|
+
}
|
|
108748
109084
|
async function updateCopyfileRuntime(version2, options = {}) {
|
|
108749
109085
|
if (!new RegExp(SEMVER_PATTERN).test(version2))
|
|
108750
109086
|
throw new Error("EXACT_VERSION_REQUIRED");
|
|
@@ -109091,12 +109427,40 @@ function registerRuntime(parent) {
|
|
|
109091
109427
|
});
|
|
109092
109428
|
const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "Accepted for compatibility; the API origin is always per-user", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
|
|
109093
109429
|
setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
|
|
109094
|
-
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").action(async (options) => {
|
|
109095
|
-
if (options.version
|
|
109096
|
-
console.error(JSON.stringify({ error: "
|
|
109430
|
+
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).option("--version <version>", "Install this exact @hasna/skills version as a staged copyfile runtime").option("--rollback <receipt-id>", "Restore launcher links from an exact copyfile rollout receipt").option("--adopt-aliases", "Switch verified legacy @hasna/skills aliases to the active copyfile runtime").option("--rollback-aliases <receipt-id>", "Restore aliases from an exact adoption receipt").action(async (options) => {
|
|
109431
|
+
if ([options.version, options.rollback, options.adoptAliases, options.rollbackAliases].filter(Boolean).length > 1) {
|
|
109432
|
+
console.error(JSON.stringify({ error: "SELF_UPDATE_OPERATIONS_ARE_MUTUALLY_EXCLUSIVE" }));
|
|
109097
109433
|
process.exitCode = 1;
|
|
109098
109434
|
return;
|
|
109099
109435
|
}
|
|
109436
|
+
if (options.adoptAliases) {
|
|
109437
|
+
try {
|
|
109438
|
+
const result2 = adoptCopyfileAliases();
|
|
109439
|
+
if (options.json)
|
|
109440
|
+
console.log(JSON.stringify(result2));
|
|
109441
|
+
else
|
|
109442
|
+
console.log(source_default.green(`Adopted ${String(result2.aliasCount)} verified Skills aliases into the active copyfile runtime.`));
|
|
109443
|
+
} catch (error61) {
|
|
109444
|
+
const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "ALIAS_ADOPTION_FAILED";
|
|
109445
|
+
console.error(JSON.stringify({ adopted: false, error: message }));
|
|
109446
|
+
process.exitCode = 1;
|
|
109447
|
+
}
|
|
109448
|
+
return;
|
|
109449
|
+
}
|
|
109450
|
+
if (options.rollbackAliases) {
|
|
109451
|
+
try {
|
|
109452
|
+
const result2 = rollbackCopyfileAliases(options.rollbackAliases);
|
|
109453
|
+
if (options.json)
|
|
109454
|
+
console.log(JSON.stringify(result2));
|
|
109455
|
+
else
|
|
109456
|
+
console.log(source_default.green(`Restored ${String(result2.restoredAliasCount)} Skills aliases from the adoption receipt.`));
|
|
109457
|
+
} catch (error61) {
|
|
109458
|
+
const message = error61 instanceof Error && /^[A-Z0-9_]+$/.test(error61.message) ? error61.message : "ALIAS_ROLLBACK_FAILED";
|
|
109459
|
+
console.error(JSON.stringify({ rolledBack: false, error: message }));
|
|
109460
|
+
process.exitCode = 1;
|
|
109461
|
+
}
|
|
109462
|
+
return;
|
|
109463
|
+
}
|
|
109100
109464
|
if (options.version) {
|
|
109101
109465
|
try {
|
|
109102
109466
|
const result2 = await updateCopyfileRuntime(options.version);
|
|
@@ -111848,7 +112212,9 @@ var exports_agent_integration = {};
|
|
|
111848
112212
|
__export(exports_agent_integration, {
|
|
111849
112213
|
registerAgentIntegration: () => registerAgentIntegration
|
|
111850
112214
|
});
|
|
111851
|
-
import { readFileSync as readFileSync41 } from "fs";
|
|
112215
|
+
import { existsSync as existsSync35, lstatSync as lstatSync32, readFileSync as readFileSync41, writeFileSync as writeFileSync26 } from "fs";
|
|
112216
|
+
import { createHash as createHash31 } from "crypto";
|
|
112217
|
+
import { homedir as homedir13 } from "os";
|
|
111852
112218
|
import { isAbsolute as isAbsolute29 } from "path";
|
|
111853
112219
|
function pinnedHookProfile(input3, configuredProfile) {
|
|
111854
112220
|
try {
|
|
@@ -111915,6 +112281,45 @@ function agents(value) {
|
|
|
111915
112281
|
}
|
|
111916
112282
|
function registerAgentIntegration(parent) {
|
|
111917
112283
|
const hook = parent.command("hook").description("Load selected Skills context through agent lifecycle hooks");
|
|
112284
|
+
hook.command("native-catalog").requiredOption("--cwd <path>", "Absolute project directory observed by native Codex skills/list").requiredOption("--output <file>", "New private file for the projected native catalog").option("--codex-command <path>", "Installed Codex executable used by the native client", "codex").option("--json", "Output bounded capture metadata as JSON", false).description("Capture a reviewed Codex native skill catalog for exact hook enrollment").action(async (options) => {
|
|
112285
|
+
try {
|
|
112286
|
+
if (!isAbsolute29(options.cwd) || !isAbsolute29(options.output))
|
|
112287
|
+
throw new Error("CODEX_NATIVE_SKILL_CATALOG_ABSOLUTE_PATH_REQUIRED");
|
|
112288
|
+
if (existsSync35(options.output))
|
|
112289
|
+
throw new Error("CODEX_NATIVE_SKILL_CATALOG_OUTPUT_EXISTS");
|
|
112290
|
+
const catalog = await captureCodexNativeSkillCatalog({
|
|
112291
|
+
command: options.codexCommand,
|
|
112292
|
+
home: homedir13(),
|
|
112293
|
+
...process.env.CODEX_HOME === undefined ? {} : { codexHome: process.env.CODEX_HOME },
|
|
112294
|
+
cwd: options.cwd
|
|
112295
|
+
});
|
|
112296
|
+
const bytes = JSON.stringify(catalog, null, 2) + `
|
|
112297
|
+
`;
|
|
112298
|
+
try {
|
|
112299
|
+
writeFileSync26(options.output, bytes, { flag: "wx", mode: 384 });
|
|
112300
|
+
} catch (error61) {
|
|
112301
|
+
if (error61 && typeof error61 === "object" && "code" in error61 && error61.code === "EEXIST")
|
|
112302
|
+
throw new Error("CODEX_NATIVE_SKILL_CATALOG_OUTPUT_EXISTS");
|
|
112303
|
+
throw error61;
|
|
112304
|
+
}
|
|
112305
|
+
const written = lstatSync32(options.output);
|
|
112306
|
+
if (!written.isFile() || written.isSymbolicLink() || (written.mode & 63) !== 0 || readFileSync41(options.output, "utf8") !== bytes)
|
|
112307
|
+
throw new Error("CODEX_NATIVE_SKILL_CATALOG_READBACK_FAILED");
|
|
112308
|
+
const receipt = {
|
|
112309
|
+
version: catalog.version,
|
|
112310
|
+
cwd: catalog.cwd,
|
|
112311
|
+
skillCount: catalog.skills.length,
|
|
112312
|
+
output: options.output,
|
|
112313
|
+
bytes: Buffer.byteLength(bytes),
|
|
112314
|
+
sha256: createHash31("sha256").update(bytes).digest("hex")
|
|
112315
|
+
};
|
|
112316
|
+
await writeCliOutput(options.json ? JSON.stringify(receipt) : `Captured ${receipt.skillCount} native Codex skill(s) in ${receipt.output}.`);
|
|
112317
|
+
} catch (error61) {
|
|
112318
|
+
const message = error61 instanceof Error && /^[A-Z][A-Z0-9_]{2,120}$/.test(error61.message) ? error61.message : "CODEX_NATIVE_SKILL_CATALOG_CAPTURE_FAILED";
|
|
112319
|
+
console.error(message);
|
|
112320
|
+
process.exitCode = 1;
|
|
112321
|
+
}
|
|
112322
|
+
});
|
|
111918
112323
|
hook.command("witness").requiredOption("--kind <kind>", "claude-settings-v3, claude-settings-v2, codex-settings-v1 or claude-marketplace-registry-v2").requiredOption("--path <path>", "Canonical absolute path to the reviewed settings or registry file").option("--json", "Output the discovery witness as JSON", false).description("Capture an explicit versioned review witness without installing it").action(async (options) => {
|
|
111919
112324
|
const capture2 = options.kind === "codex-settings-v1" ? captureCodexSettings : options.kind === "claude-settings-v3" ? captureClaudeSettingsV3 : options.kind === "claude-settings-v2" ? captureClaudeSettingsV2 : options.kind === "claude-marketplace-registry-v2" ? captureClaudeMarketplaceRegistryV2 : null;
|
|
111920
112325
|
if (!capture2)
|
|
@@ -112146,6 +112551,7 @@ var init_agent_integration2 = __esm(() => {
|
|
|
112146
112551
|
init_claude_settings_witness();
|
|
112147
112552
|
init_codex_settings_witness();
|
|
112148
112553
|
init_claude_marketplace_registry();
|
|
112554
|
+
init_codex_native_skill_catalog();
|
|
112149
112555
|
RECOVERABLE_CONTEXT_CACHE_ERRORS = new Set(["CACHED_PROFILE_EXPIRED", "CACHED_PROFILE_MISSING", "CACHED_BUNDLE_MISSING"]);
|
|
112150
112556
|
});
|
|
112151
112557
|
|
|
@@ -112284,7 +112690,7 @@ var exports_profiles = {};
|
|
|
112284
112690
|
__export(exports_profiles, {
|
|
112285
112691
|
registerProfiles: () => registerProfiles
|
|
112286
112692
|
});
|
|
112287
|
-
import { closeSync as closeSync21, constants as constants20, fstatSync as fstatSync21, openSync as openSync21, readSync as readSync17, writeFileSync as
|
|
112693
|
+
import { closeSync as closeSync21, constants as constants20, fstatSync as fstatSync21, openSync as openSync21, readSync as readSync17, writeFileSync as writeFileSync27 } from "fs";
|
|
112288
112694
|
function readProfileInput(path) {
|
|
112289
112695
|
const fd = openSync21(path, constants20.O_RDONLY | constants20.O_NONBLOCK);
|
|
112290
112696
|
try {
|
|
@@ -112312,7 +112718,7 @@ function registerProfiles(parent) {
|
|
|
112312
112718
|
try {
|
|
112313
112719
|
const profile = await readSkillProfile(id3), text4 = JSON.stringify(profile, null, 2);
|
|
112314
112720
|
if (options.save)
|
|
112315
|
-
|
|
112721
|
+
writeFileSync27(options.save, JSON.stringify(profile), { mode: 384, flag: "wx" });
|
|
112316
112722
|
await writeCliOutput(options.json ? text4 : `${profile.id} at ${profile.revision}: ${profile.selections.map((skill) => `${skill.slug}@${skill.version}`).join(", ")}`);
|
|
112317
112723
|
} catch (error61) {
|
|
112318
112724
|
console.error(error61.message);
|
|
@@ -112359,7 +112765,7 @@ import {
|
|
|
112359
112765
|
fstatSync as fstatSync23,
|
|
112360
112766
|
openSync as openSync23,
|
|
112361
112767
|
readSync as readSync18,
|
|
112362
|
-
writeFileSync as
|
|
112768
|
+
writeFileSync as writeFileSync28
|
|
112363
112769
|
} from "fs";
|
|
112364
112770
|
function readInput(path) {
|
|
112365
112771
|
const fd = openSync23(path, constants21.O_RDONLY | constants21.O_NONBLOCK);
|
|
@@ -112392,7 +112798,7 @@ function registerGrants(parent) {
|
|
|
112392
112798
|
try {
|
|
112393
112799
|
const policy = await readExecutionGrantPolicy(profile, options.revision);
|
|
112394
112800
|
if (options.save)
|
|
112395
|
-
|
|
112801
|
+
writeFileSync28(options.save, JSON.stringify(policy, null, 2), {
|
|
112396
112802
|
mode: 384,
|
|
112397
112803
|
flag: "wx"
|
|
112398
112804
|
});
|
|
@@ -112419,14 +112825,14 @@ var init_grants = __esm(() => {
|
|
|
112419
112825
|
});
|
|
112420
112826
|
|
|
112421
112827
|
// src/lib/station-hydrate.ts
|
|
112422
|
-
import { createHash as
|
|
112828
|
+
import { createHash as createHash32 } from "crypto";
|
|
112423
112829
|
import {
|
|
112424
112830
|
copyFileSync as copyFileSync3,
|
|
112425
112831
|
mkdirSync as mkdirSync28,
|
|
112426
112832
|
readdirSync as readdirSync24,
|
|
112427
112833
|
readFileSync as readFileSync42,
|
|
112428
112834
|
statSync as statSync22,
|
|
112429
|
-
writeFileSync as
|
|
112835
|
+
writeFileSync as writeFileSync29
|
|
112430
112836
|
} from "fs";
|
|
112431
112837
|
import { dirname as dirname35, join as join61, resolve as resolve26, sep as sep13 } from "path";
|
|
112432
112838
|
function fail5(code2, message, detail = []) {
|
|
@@ -112622,7 +113028,7 @@ function skillSha256(skill) {
|
|
|
112622
113028
|
return sha256File(skill.files[0].winner.fullPath);
|
|
112623
113029
|
}
|
|
112624
113030
|
const joined = skill.files.map((file2) => sha256File(file2.winner.fullPath));
|
|
112625
|
-
return
|
|
113031
|
+
return createHash32("sha256").update(joined.sort().join(`
|
|
112626
113032
|
`)).digest("hex");
|
|
112627
113033
|
}
|
|
112628
113034
|
function writeStationHydration(options) {
|
|
@@ -112704,7 +113110,7 @@ function writeStationHydration(options) {
|
|
|
112704
113110
|
};
|
|
112705
113111
|
const hydrationManifestPath = join61(dirname35(cacheRoot), `hydration-${options.stationId}.json`);
|
|
112706
113112
|
mkdirSync28(dirname35(hydrationManifestPath), { recursive: true });
|
|
112707
|
-
|
|
113113
|
+
writeFileSync29(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
112708
113114
|
`);
|
|
112709
113115
|
return {
|
|
112710
113116
|
...base3,
|
|
@@ -112779,8 +113185,8 @@ var init_hydrate = __esm(() => {
|
|
|
112779
113185
|
});
|
|
112780
113186
|
|
|
112781
113187
|
// src/lib/prepare-skill.ts
|
|
112782
|
-
import { createHash as
|
|
112783
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
113188
|
+
import { createHash as createHash33, randomUUID as randomUUID12 } from "crypto";
|
|
113189
|
+
import { chmodSync as chmodSync5, existsSync as existsSync36, lstatSync as lstatSync33, mkdirSync as mkdirSync29, mkdtempSync as mkdtempSync6, readFileSync as readFileSync43, readdirSync as readdirSync25, renameSync as renameSync14, rmSync as rmSync9, writeFileSync as writeFileSync30 } from "fs";
|
|
112784
113190
|
import { tmpdir as tmpdir2 } from "os";
|
|
112785
113191
|
import { dirname as dirname36, isAbsolute as isAbsolute30, join as join62, relative as relative10, resolve as resolve27, sep as sep14 } from "path";
|
|
112786
113192
|
function prepareSkill(name, options) {
|
|
@@ -112829,10 +113235,10 @@ function prepareSkill(name, options) {
|
|
|
112829
113235
|
for (const file2 of before2.files) {
|
|
112830
113236
|
const destination = join62(staging, file2.path);
|
|
112831
113237
|
mkdirSync29(dirname36(destination), { recursive: true });
|
|
112832
|
-
|
|
113238
|
+
writeFileSync30(destination, file2.bytes, { mode: file2.mode });
|
|
112833
113239
|
}
|
|
112834
113240
|
const stagedManifest = join62(staging, "skill.json");
|
|
112835
|
-
|
|
113241
|
+
writeFileSync30(stagedManifest, `${JSON.stringify(candidate2, null, 2)}
|
|
112836
113242
|
`);
|
|
112837
113243
|
const contentHash = computeContentHash(staging);
|
|
112838
113244
|
const changed3 = options.version !== previousVersion || kind !== manifest.kind || contentHash !== provenance?.content_hash;
|
|
@@ -112842,7 +113248,7 @@ function prepareSkill(name, options) {
|
|
|
112842
113248
|
candidate2.provenance.content_hash = contentHash;
|
|
112843
113249
|
const candidateBytes = `${JSON.stringify(candidate2, null, 2)}
|
|
112844
113250
|
`;
|
|
112845
|
-
|
|
113251
|
+
writeFileSync30(stagedManifest, candidateBytes);
|
|
112846
113252
|
validateRawCandidate(candidate2);
|
|
112847
113253
|
validateRuntimeEntrypoint(candidate2, staging);
|
|
112848
113254
|
const validation = validatePortableSkillDirectory(normalized, staging);
|
|
@@ -112856,7 +113262,7 @@ ${validation.issues.map((issue2) => `${issue2.code}: ${issue2.message}`).join(`
|
|
|
112856
113262
|
if (changed3 && !options.dryRun) {
|
|
112857
113263
|
const temporary = join62(path, `.skill-prepare-${randomUUID12()}.tmp`);
|
|
112858
113264
|
try {
|
|
112859
|
-
|
|
113265
|
+
writeFileSync30(temporary, candidateBytes, { flag: "wx", mode: manifestFile.mode });
|
|
112860
113266
|
chmodSync5(temporary, manifestFile.mode);
|
|
112861
113267
|
renameSync14(temporary, join62(path, "skill.json"));
|
|
112862
113268
|
} finally {
|
|
@@ -112889,7 +113295,7 @@ function validateRuntimeEntrypoint(candidate2, root2) {
|
|
|
112889
113295
|
if (typeof entry !== "string")
|
|
112890
113296
|
return;
|
|
112891
113297
|
const target = resolve27(root2, entry), rel = relative10(root2, target);
|
|
112892
|
-
if (isAbsolute30(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep14}`) || isAbsolute30(rel) || !
|
|
113298
|
+
if (isAbsolute30(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep14}`) || isAbsolute30(rel) || !existsSync36(target) || !lstatSync33(target).isFile()) {
|
|
112893
113299
|
throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
|
|
112894
113300
|
}
|
|
112895
113301
|
}
|
|
@@ -112974,11 +113380,11 @@ function compareVersions(left, right) {
|
|
|
112974
113380
|
return 0;
|
|
112975
113381
|
}
|
|
112976
113382
|
function snapshot2(root2) {
|
|
112977
|
-
const rootStat =
|
|
113383
|
+
const rootStat = lstatSync33(root2);
|
|
112978
113384
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory())
|
|
112979
113385
|
throw new Error("Skill root must be a real directory, not a symlink.");
|
|
112980
113386
|
const files = [], directories = [];
|
|
112981
|
-
const hash6 =
|
|
113387
|
+
const hash6 = createHash33("sha256").update(`${rootStat.dev}:${rootStat.ino}\x00`);
|
|
112982
113388
|
let count = 0, bytes = 0;
|
|
112983
113389
|
const walk2 = (relative11) => {
|
|
112984
113390
|
for (const name of readdirSync25(join62(root2, relative11)).sort()) {
|
|
@@ -112987,7 +113393,7 @@ function snapshot2(root2) {
|
|
|
112987
113393
|
const path = relative11 ? `${relative11}/${name}` : name;
|
|
112988
113394
|
if (++count > 20000)
|
|
112989
113395
|
throw new Error("Skill exceeds the preparation entry limit (20000).");
|
|
112990
|
-
const absolute = join62(root2, path), stat3 =
|
|
113396
|
+
const absolute = join62(root2, path), stat3 = lstatSync33(absolute);
|
|
112991
113397
|
if (stat3.isSymbolicLink())
|
|
112992
113398
|
throw new Error(`Skill contains a symlink: ${path}. No files were changed.`);
|
|
112993
113399
|
if (stat3.isDirectory() && name === "node_modules")
|
|
@@ -113402,7 +113808,7 @@ var init_schedule = __esm(() => {
|
|
|
113402
113808
|
});
|
|
113403
113809
|
|
|
113404
113810
|
// src/lib/registry-sync.ts
|
|
113405
|
-
import { mkdirSync as mkdirSync30, writeFileSync as
|
|
113811
|
+
import { mkdirSync as mkdirSync30, writeFileSync as writeFileSync31 } from "fs";
|
|
113406
113812
|
import { dirname as dirname37, relative as relative11 } from "path";
|
|
113407
113813
|
function createRegistrySyncArtifact(options = {}) {
|
|
113408
113814
|
const profile = options.profile ?? "all";
|
|
@@ -113461,7 +113867,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
113461
113867
|
}
|
|
113462
113868
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
113463
113869
|
mkdirSync30(dirname37(path), { recursive: true });
|
|
113464
|
-
|
|
113870
|
+
writeFileSync31(path, `${JSON.stringify(artifact, null, 2)}
|
|
113465
113871
|
`);
|
|
113466
113872
|
}
|
|
113467
113873
|
function buildDocs(name) {
|
|
@@ -113480,7 +113886,7 @@ var init_registry_sync = __esm(() => {
|
|
|
113480
113886
|
});
|
|
113481
113887
|
|
|
113482
113888
|
// src/lib/review-export.ts
|
|
113483
|
-
import { constants as constants22, closeSync as closeSync25, existsSync as
|
|
113889
|
+
import { constants as constants22, closeSync as closeSync25, existsSync as existsSync37, fstatSync as fstatSync25, fsyncSync as fsyncSync7, lstatSync as lstatSync34, openSync as openSync25, realpathSync as realpathSync18, statSync as statSync23, writeFileSync as writeFileSync32 } from "fs";
|
|
113484
113890
|
import { basename as basename15, dirname as dirname38, isAbsolute as isAbsolute31, join as join63, parse as parse12, resolve as resolve28, sep as sep15 } from "path";
|
|
113485
113891
|
function exactVersion(spec) {
|
|
113486
113892
|
const at2 = spec.indexOf("@");
|
|
@@ -113496,7 +113902,7 @@ function assertPrivateDestination(output2) {
|
|
|
113496
113902
|
throw new Error("Review output must be an absolute, normalized .tar.gz path.");
|
|
113497
113903
|
}
|
|
113498
113904
|
const parent = dirname38(output2);
|
|
113499
|
-
if (realpathSync18(parent) !== parent ||
|
|
113905
|
+
if (realpathSync18(parent) !== parent || lstatSync34(parent).isSymbolicLink()) {
|
|
113500
113906
|
throw new Error("Review output parent must be a real directory, not a symlink.");
|
|
113501
113907
|
}
|
|
113502
113908
|
const parentStat = statSync23(parent);
|
|
@@ -113508,7 +113914,7 @@ function assertPrivateDestination(output2) {
|
|
|
113508
113914
|
throw new Error("Review output cannot be placed in agent-native discovery or Skills installation paths.");
|
|
113509
113915
|
}
|
|
113510
113916
|
for (let current = parent;; current = dirname38(current)) {
|
|
113511
|
-
if (
|
|
113917
|
+
if (existsSync37(join63(current, ".git"))) {
|
|
113512
113918
|
throw new Error("Review output cannot be placed inside a Git worktree.");
|
|
113513
113919
|
}
|
|
113514
113920
|
if (current === parse12(current).root)
|
|
@@ -113519,7 +113925,7 @@ function assertPrivateDestination(output2) {
|
|
|
113519
113925
|
async function exportSkillVersionForReview(spec, output2, client2) {
|
|
113520
113926
|
const { slug: slug2, version: version2 } = exactVersion(spec);
|
|
113521
113927
|
const target = assertPrivateDestination(output2);
|
|
113522
|
-
if (
|
|
113928
|
+
if (existsSync37(target))
|
|
113523
113929
|
throw new Error("Review output already exists; choose a new filename.");
|
|
113524
113930
|
const metadata = await client2.getSkillVersion(slug2, version2);
|
|
113525
113931
|
if (!metadata)
|
|
@@ -113546,7 +113952,7 @@ async function exportSkillVersionForReview(spec, output2, client2) {
|
|
|
113546
113952
|
}
|
|
113547
113953
|
const fd = openSync25(target, constants22.O_WRONLY | constants22.O_CREAT | constants22.O_EXCL | constants22.O_NOFOLLOW, 384);
|
|
113548
113954
|
try {
|
|
113549
|
-
|
|
113955
|
+
writeFileSync32(fd, bytes);
|
|
113550
113956
|
fsyncSync7(fd);
|
|
113551
113957
|
if (fstatSync25(fd).size !== bytes.byteLength)
|
|
113552
113958
|
throw new Error("Incomplete review output write.");
|
|
@@ -113793,7 +114199,7 @@ __export(exports_publish, {
|
|
|
113793
114199
|
PushSkillError: () => PushSkillError
|
|
113794
114200
|
});
|
|
113795
114201
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
113796
|
-
import { existsSync as
|
|
114202
|
+
import { existsSync as existsSync38, readFileSync as readFileSync44 } from "fs";
|
|
113797
114203
|
import { hostname as hostname5 } from "os";
|
|
113798
114204
|
import { join as join64 } from "path";
|
|
113799
114205
|
function registerPublish(parent) {
|
|
@@ -113848,7 +114254,7 @@ async function pushSkill(name, options = {}) {
|
|
|
113848
114254
|
await verifyPackedCanonicalHash(skill.name, packed);
|
|
113849
114255
|
const versionManifest = buildVersionManifest(skill.path, packed);
|
|
113850
114256
|
const skillMdPath = join64(skill.path, "SKILL.md");
|
|
113851
|
-
const skillMd =
|
|
114257
|
+
const skillMd = existsSync38(skillMdPath) ? readFileSync44(skillMdPath, "utf-8") : undefined;
|
|
113852
114258
|
const base3 = {
|
|
113853
114259
|
slug: skill.name,
|
|
113854
114260
|
path: skill.path,
|
|
@@ -115567,7 +115973,7 @@ var init_storage2 = __esm(() => {
|
|
|
115567
115973
|
});
|
|
115568
115974
|
|
|
115569
115975
|
// src/lib/registry-reconcile.ts
|
|
115570
|
-
import { existsSync as
|
|
115976
|
+
import { existsSync as existsSync39, readFileSync as readFileSync45, statSync as statSync24, writeFileSync as writeFileSync33 } from "fs";
|
|
115571
115977
|
import { join as join65 } from "path";
|
|
115572
115978
|
function isDirectory2(path) {
|
|
115573
115979
|
try {
|
|
@@ -115584,7 +115990,7 @@ function migrationNeeded(options) {
|
|
|
115584
115990
|
}
|
|
115585
115991
|
function readBaseline(skillDir) {
|
|
115586
115992
|
const markerPath = join65(skillDir, PULL_MARKER_FILE);
|
|
115587
|
-
if (!
|
|
115993
|
+
if (!existsSync39(markerPath))
|
|
115588
115994
|
return;
|
|
115589
115995
|
try {
|
|
115590
115996
|
const marker = JSON.parse(readFileSync45(markerPath, "utf-8"));
|
|
@@ -115600,7 +116006,7 @@ function readBaseline(skillDir) {
|
|
|
115600
116006
|
}
|
|
115601
116007
|
function readCursor(root2) {
|
|
115602
116008
|
const path = join65(root2, SYNC_CURSOR_FILE);
|
|
115603
|
-
if (!
|
|
116009
|
+
if (!existsSync39(path))
|
|
115604
116010
|
return { runCount: 0 };
|
|
115605
116011
|
try {
|
|
115606
116012
|
const cursor2 = JSON.parse(readFileSync45(path, "utf-8"));
|
|
@@ -115631,7 +116037,7 @@ function remoteRowToSkill(record11) {
|
|
|
115631
116037
|
}
|
|
115632
116038
|
function recheckLocalSide(plannedLocal, localDir, ops = {
|
|
115633
116039
|
pack: (dir) => packSkillBundle(dir).sha256,
|
|
115634
|
-
exists:
|
|
116040
|
+
exists: existsSync39
|
|
115635
116041
|
}) {
|
|
115636
116042
|
let localNow;
|
|
115637
116043
|
try {
|
|
@@ -115882,7 +116288,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
115882
116288
|
runCount: readCursor(root2).runCount + 1,
|
|
115883
116289
|
summary
|
|
115884
116290
|
};
|
|
115885
|
-
|
|
116291
|
+
writeFileSync33(join65(root2, SYNC_CURSOR_FILE), `${JSON.stringify(cursor2, null, 2)}
|
|
115886
116292
|
`);
|
|
115887
116293
|
return {
|
|
115888
116294
|
corpusRoot: root2,
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.19",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.10.
|
|
24125
|
+
version: "0.10.19",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.10.
|
|
24128
|
+
version: "0.10.19",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
|
@@ -12,6 +12,15 @@ export declare function preflightTarball(bytes: Uint8Array, limits?: {
|
|
|
12
12
|
expandedBytes: number;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function readBodyCapped(response: Response, limit: number, errorCode: string): Promise<Uint8Array>;
|
|
15
|
+
export declare function adoptCopyfileAliases(options?: {
|
|
16
|
+
homeDir?: string;
|
|
17
|
+
pathValue?: string;
|
|
18
|
+
onAliasSwitched?: (path: string) => void;
|
|
19
|
+
}): JsonObject;
|
|
20
|
+
export declare function rollbackCopyfileAliases(receiptId: string, options?: {
|
|
21
|
+
homeDir?: string;
|
|
22
|
+
pathValue?: string;
|
|
23
|
+
}): JsonObject;
|
|
15
24
|
export declare function updateCopyfileRuntime(version: string, options?: {
|
|
16
25
|
homeDir?: string;
|
|
17
26
|
pathValue?: string;
|
package/dist/index.js
CHANGED
|
@@ -10877,7 +10877,7 @@ function replaceSkillSession(expected, replacement, plan, options = {}, beforeCo
|
|
|
10877
10877
|
// package.json
|
|
10878
10878
|
var package_default = {
|
|
10879
10879
|
name: "@hasna/skills",
|
|
10880
|
-
version: "0.10.
|
|
10880
|
+
version: "0.10.19",
|
|
10881
10881
|
description: "Skills library for AI coding agents",
|
|
10882
10882
|
type: "module",
|
|
10883
10883
|
bin: {
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.19",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|