@hasna/skills 0.9.7 → 0.9.8
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/bin/index.js +116 -47
- 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/index.js +1 -1
- package/dist/lib/agent-codex-trust.d.ts +15 -0
- package/dist/sdk/index.js +1 -1
- package/package.json +1 -1
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.9.
|
|
36871
|
+
version: "0.9.8",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -108256,11 +108256,33 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
108256
108256
|
});
|
|
108257
108257
|
|
|
108258
108258
|
// src/lib/agent-codex-trust.ts
|
|
108259
|
-
import { lstatSync as lstatSync28, mkdirSync as mkdirSync25, readdirSync as readdirSync20 } from "fs";
|
|
108259
|
+
import { lstatSync as lstatSync28, mkdirSync as mkdirSync25, readdirSync as readdirSync20, readFileSync as readFileSync37, realpathSync as realpathSync13, statSync as statSync21 } from "fs";
|
|
108260
108260
|
import { createHash as createHash26, randomUUID as randomUUID10 } from "crypto";
|
|
108261
|
-
import {
|
|
108261
|
+
import { execFileSync } from "child_process";
|
|
108262
|
+
import { dirname as dirname30, isAbsolute as isAbsolute25, join as join56, resolve as resolve22 } from "path";
|
|
108262
108263
|
import { homedir as homedir11 } from "os";
|
|
108263
108264
|
import { isDeepStrictEqual as isDeepStrictEqual6 } from "util";
|
|
108265
|
+
function verifyReleaseArtifact(proof, skillsCli) {
|
|
108266
|
+
need4(isAbsolute25(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
108267
|
+
const tarPath = realpathSync13(proof.packageTarPath);
|
|
108268
|
+
need4(statSync21(tarPath).isFile(), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
108269
|
+
const tarBytes = readFileSync37(tarPath);
|
|
108270
|
+
need4(tarBytes.byteLength === proof.packageTarBytes && createHash26("sha256").update(tarBytes).digest("hex") === proof.packageTarSha256, "RECONCILE_RELEASE_ARTIFACT_CHANGED");
|
|
108271
|
+
const packageRoot = dirname30(dirname30(realpathSync13(skillsCli.receipt.path)));
|
|
108272
|
+
const manifest = readFileSync37(join56(packageRoot, "package.json"));
|
|
108273
|
+
need4(createHash26("sha256").update(manifest).digest("hex") === skillsCli.receipt.manifestSha256 && skillsCli.receipt.manifestSha256 === proof.manifestSha256, "RECONCILE_RELEASE_MANIFEST_CHANGED");
|
|
108274
|
+
need4(createHash26("sha256").update(readFileSync37(skillsCli.receipt.path)).digest("hex") === proof.executableSha256 && skillsCli.receipt.sha256 === proof.executableSha256, "RECONCILE_RELEASE_EXECUTABLE_CHANGED");
|
|
108275
|
+
const members2 = [["package/package.json", manifest], ["package/bin/index.js", readFileSync37(skillsCli.receipt.path)]];
|
|
108276
|
+
for (const [member, expected] of members2) {
|
|
108277
|
+
let actual;
|
|
108278
|
+
try {
|
|
108279
|
+
actual = execFileSync("tar", ["-xOf", tarPath, member], { maxBuffer: 32 * 1024 * 1024 });
|
|
108280
|
+
} catch {
|
|
108281
|
+
throw new Error("RECONCILE_RELEASE_ARTIFACT_MEMBER_MISSING");
|
|
108282
|
+
}
|
|
108283
|
+
need4(Buffer.compare(actual, expected) === 0, "RECONCILE_RELEASE_ARTIFACT_MEMBER_CHANGED");
|
|
108284
|
+
}
|
|
108285
|
+
}
|
|
108264
108286
|
async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc) {
|
|
108265
108287
|
let rpc;
|
|
108266
108288
|
try {
|
|
@@ -108283,26 +108305,54 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108283
108305
|
need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
108284
108306
|
need4(isDeepStrictEqual6(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
108285
108307
|
need4(configBefore.sha256 === intent.beforeSha256 && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
108308
|
+
const policyCurrent = snapshot(join56(dataDir3, "agent-policy.json"), true);
|
|
108309
|
+
let supersededBinding = false;
|
|
108310
|
+
if (policyCurrent.sha256 !== policyBefore.sha256) {
|
|
108311
|
+
need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
108312
|
+
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
108313
|
+
need4(isDeepStrictEqual6(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
108314
|
+
supersededBinding = true;
|
|
108315
|
+
}
|
|
108316
|
+
if (supersededBinding) {
|
|
108317
|
+
need4(Array.isArray(intent.nativeHooks) && intent.nativeHooks.length > 0, "RECONCILE_NATIVE_INVENTORY_REQUIRED");
|
|
108318
|
+
const proof = options.releaseProof;
|
|
108319
|
+
need4(!!proof && isAbsolute25(proof.packageTarPath) && /^[a-f0-9]{64}$/.test(proof.packageTarSha256) && Number.isInteger(proof.packageTarBytes) && proof.packageTarBytes > 0 && /^[a-f0-9]{64}$/.test(proof.manifestSha256) && /^[a-f0-9]{64}$/.test(proof.executableSha256), "RECONCILE_RELEASE_PROVENANCE_REQUIRED");
|
|
108320
|
+
}
|
|
108286
108321
|
const currentConfig = snapshot(intent.configPath, true);
|
|
108287
108322
|
const managedKeys = intent.hooks.map((h) => h.key);
|
|
108288
|
-
|
|
108289
|
-
|
|
108323
|
+
const witnessSame = codexTrustReconcileWitness(configBefore.text, managedKeys) === codexTrustReconcileWitness(currentConfig.text, managedKeys);
|
|
108324
|
+
const unmanagedSame = codexTrustUnmanagedSemantic(configBefore.text, managedKeys) === codexTrustUnmanagedSemantic(currentConfig.text, managedKeys);
|
|
108325
|
+
need4(unmanagedSame && (witnessSame || supersededBinding), "RECONCILE_UNRELATED_CONFIG_CHANGED");
|
|
108290
108326
|
const before2 = structure(configBefore.text), current = structure(currentConfig.text), expectedConfig = structuredClone(before2);
|
|
108291
108327
|
expectedConfig.hooks ??= {};
|
|
108292
108328
|
expectedConfig.hooks.state ??= {};
|
|
108293
108329
|
for (const hook of intent.hooks)
|
|
108294
108330
|
expectedConfig.hooks.state[hook.key] = { ...expectedConfig.hooks.state[hook.key], enabled: true, trusted_hash: hook.currentHash };
|
|
108295
|
-
|
|
108296
|
-
|
|
108297
|
-
|
|
108298
|
-
|
|
108331
|
+
if (supersededBinding) {
|
|
108332
|
+
for (const hook of intent.hooks) {
|
|
108333
|
+
const state = current.hooks?.state?.[hook.key];
|
|
108334
|
+
need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
108335
|
+
}
|
|
108336
|
+
} else
|
|
108337
|
+
need4(isDeepStrictEqual6(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
108338
|
+
const policy = parseManagedSkillPolicy(policyCurrent.text), binding = policy.bridge;
|
|
108299
108339
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
|
|
108300
108340
|
const skillsCli = bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
108301
|
-
|
|
108341
|
+
if (supersededBinding)
|
|
108342
|
+
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
108343
|
+
const skillsBindingChanged = !isDeepStrictEqual6(skillsCli.receipt, intent.skillsCli);
|
|
108344
|
+
need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
108302
108345
|
const alias = binding.rootAliases?.find((item) => item.agent === "codex"), codexHome = alias?.target ?? join56(home, ".codex"), hooksPath = join56(codexHome, "hooks.json");
|
|
108303
108346
|
need4(resolve22(intent.configPath) === resolve22(join56(codexHome, "config.toml")), "RECONCILE_CONFIG_PATH_CHANGED");
|
|
108304
108347
|
const hooksCurrent = snapshot(hooksPath);
|
|
108305
|
-
|
|
108348
|
+
if (hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
108349
|
+
need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
108350
|
+
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
108351
|
+
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
108352
|
+
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
108353
|
+
need4(isDeepStrictEqual6(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
108354
|
+
}
|
|
108355
|
+
}
|
|
108306
108356
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
108307
108357
|
need4(rpc.version === intent.nativeVersion, "RECONCILE_NATIVE_VERSION_CHANGED");
|
|
108308
108358
|
const discovered = await rpc.request("hooks/list", { cwds: [home] });
|
|
@@ -108312,12 +108362,15 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108312
108362
|
if (recordedNativeHooks)
|
|
108313
108363
|
need4(nativeHooks.length === recordedNativeHooks.length, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
108314
108364
|
const plannedKeys = new Set(intent.hooks.map((hook) => hook.key));
|
|
108365
|
+
let nativeTrustPending = false;
|
|
108315
108366
|
for (const expected of recordedNativeHooks ?? []) {
|
|
108316
108367
|
const matches2 = nativeHooks.filter((hook) => hook.key === expected.key);
|
|
108317
108368
|
need4(matches2.length === 1, "RECONCILE_NATIVE_HOOK_LIST_CHANGED");
|
|
108318
108369
|
const actual = safeNativeHook(matches2[0]);
|
|
108319
108370
|
const stable = (hook) => {
|
|
108320
108371
|
const { enabled, trustStatus, ...identity4 } = hook;
|
|
108372
|
+
if (supersededBinding)
|
|
108373
|
+
delete identity4.command;
|
|
108321
108374
|
return identity4;
|
|
108322
108375
|
};
|
|
108323
108376
|
need4(isDeepStrictEqual6(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
@@ -108328,7 +108381,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108328
108381
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join56(home, ".codex/hooks.json"), hooksPath));
|
|
108329
108382
|
need4(matches2.length === 1, "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
108330
108383
|
const h = matches2[0];
|
|
108331
|
-
|
|
108384
|
+
const trusted = h.trustStatus === "trusted";
|
|
108385
|
+
need4(h.enabled === true && (trusted || supersededBinding && h.trustStatus === "modified") && (h.currentHash === wanted.currentHash || supersededBinding && /^sha256:[a-f0-9]{64}$/.test(h.currentHash)), "RECONCILE_NATIVE_STATE_INCOMPLETE");
|
|
108386
|
+
if (!trusted)
|
|
108387
|
+
nativeTrustPending = true;
|
|
108388
|
+
if (supersededBinding) {
|
|
108389
|
+
const nativeToCliEvent = { userPromptSubmit: "UserPromptSubmit", sessionStart: "SessionStart", subagentStart: "SubagentStart" };
|
|
108390
|
+
need4(h.command === renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, nativeToCliEvent[wanted.event] ?? ""), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
108391
|
+
}
|
|
108332
108392
|
}
|
|
108333
108393
|
const config2 = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
108334
108394
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === intent.configPath && !layer.name.profile);
|
|
@@ -108348,7 +108408,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
108348
108408
|
unchanged2(currentConfig);
|
|
108349
108409
|
skillsCli.recheck();
|
|
108350
108410
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home });
|
|
108351
|
-
const receipt = { version: 1, status: "reconciled", reconciled: true, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified:
|
|
108411
|
+
const receipt = { version: 1, status: supersededBinding ? "superseded" : "reconciled", reconciled: true, supersededBinding, automaticRollback: false, journal, planDigest: intent.planDigest, nativeVersion: rpc.version, nativeStateVerified: !nativeTrustPending, nativeTrustPending, nativeExecutionVerified: false, configSha256: currentConfig.sha256, hooksSha256: hooksCurrent.sha256, policySha256: policyBefore.sha256, policyBeforeSha256: policyBefore.sha256, policyCurrentSha256: policyCurrent.sha256, previousSkillsCli: intent.skillsCli, skillsCli: skillsCli.receipt, releaseProof: options.releaseProof, nativeInventoryScope: recordedNativeHooks ? "complete" : "declared-managed-hooks-only", unrelatedNativeHooksPreserved: !!recordedNativeHooks, unrelatedSettingsAndCommentsPreserved: true };
|
|
108352
108412
|
save2(join56(journal, "receipt.json"), json2(receipt));
|
|
108353
108413
|
return receipt;
|
|
108354
108414
|
} catch (error61) {
|
|
@@ -108478,7 +108538,12 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
108478
108538
|
}
|
|
108479
108539
|
}
|
|
108480
108540
|
var json2 = (value) => JSON.stringify(value, null, 2) + `
|
|
108481
|
-
`, structure = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), eventNames, makePlanDigest = (value) => createHash26("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus })
|
|
108541
|
+
`, structure = (text3) => JSON.parse(JSON.stringify(Bun.TOML.parse(text3))), eventNames, makePlanDigest = (value) => createHash26("sha256").update(json2(value)).digest("hex"), safeNativeHook = (hook) => ({ key: hook.key, eventName: hook.eventName, handlerType: hook.handlerType, command: hook.command, matcher: hook.matcher, timeoutSec: hook.timeoutSec, async: hook.async, statusMessage: hook.statusMessage, additionalContextLimit: hook.additionalContextLimit, sourcePath: hook.sourcePath, source: hook.source, pluginId: hook.pluginId, isManaged: hook.isManaged, currentHash: hook.currentHash, enabled: hook.enabled, trustStatus: hook.trustStatus }), withoutCodexCommand = (value) => {
|
|
108542
|
+
const copy = structuredClone(value);
|
|
108543
|
+
if (copy?.bridge?.commands && Object.hasOwn(copy.bridge.commands, "codex"))
|
|
108544
|
+
copy.bridge.commands.codex = "<reviewed-codex-binding>";
|
|
108545
|
+
return copy;
|
|
108546
|
+
};
|
|
108482
108547
|
var init_agent_codex_trust = __esm(() => {
|
|
108483
108548
|
init_config();
|
|
108484
108549
|
init_managed_policy();
|
|
@@ -108571,8 +108636,8 @@ var exports_agent_integration = {};
|
|
|
108571
108636
|
__export(exports_agent_integration, {
|
|
108572
108637
|
registerAgentIntegration: () => registerAgentIntegration
|
|
108573
108638
|
});
|
|
108574
|
-
import { readFileSync as
|
|
108575
|
-
import { isAbsolute as
|
|
108639
|
+
import { readFileSync as readFileSync38 } from "fs";
|
|
108640
|
+
import { isAbsolute as isAbsolute26 } from "path";
|
|
108576
108641
|
function pinnedHookProfile(input3, configuredProfile) {
|
|
108577
108642
|
try {
|
|
108578
108643
|
const parsed = parseSkillContextInput(JSON.stringify(input3));
|
|
@@ -108643,7 +108708,7 @@ function registerAgentIntegration(parent) {
|
|
|
108643
108708
|
});
|
|
108644
108709
|
hook.command("install").option("--agent <agent>", `Agent to configure: ${INTEGRATION_AGENTS.join(", ")}, all`, "all").option("--command <path>", "Skills executable used by the hook (preserves existing binding; new agents use skills)").option("--selection-profile <id>", "Selection profile (preserves existing binding; new agents use default)").option("--include-vendor", "Retained for compatibility; vendor system skills are always inventoried and disabled", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes for unsupported registrations").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Apply the plan, preserving prior configuration in private backups", false).option("--json", "Output a receipt as JSON", false).description("Plan or install one Skills CLI bridge plus native prompt hooks").action(async (options) => {
|
|
108645
108710
|
try {
|
|
108646
|
-
const discoveryInputs = options.discoveryInputs ? JSON.parse(
|
|
108711
|
+
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync38(options.discoveryInputs, "utf8")) : undefined;
|
|
108647
108712
|
const plan = planAgentIntegration({ agents: agents(options.agent), command: options.command, profileId: options.selectionProfile, includeVendor: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
|
|
108648
108713
|
const result2 = options.apply ? applyAgentIntegration(plan) : { changed: [], backups: [] };
|
|
108649
108714
|
const receipt = { applied: options.apply, planned: plan.changes.map((change) => change.path), ...result2, rootAliases: plan.rootAliases ?? [], discovery: plan.discoveryAfter, nativeSkills: plan.nativeSkills.map((entry) => ({ agent: entry.agent, path: entry.path, managed: entry.managed, vendor: entry.vendor, system: entry.system === true, bridge: entry.bridge === true })), requiresNativeRetirement: plan.nativeSkills.some((entry) => !entry.bridge && !entry.system) };
|
|
@@ -108672,11 +108737,15 @@ function registerAgentIntegration(parent) {
|
|
|
108672
108737
|
process.exitCode = 1;
|
|
108673
108738
|
}
|
|
108674
108739
|
});
|
|
108675
|
-
trust.command("reconcile").requiredOption("--agent <agent>", "Native trust adapter (codex)").requiredOption("--journal <path>", "Private incomplete native trust journal to inspect").option("--codex-command <path>", "Installed Codex executable used for its native configuration API", "codex").option("--json", "Output the reconciliation receipt", false).description("Verify an interrupted Codex native trust write and resolve its private journal").action(async (options) => {
|
|
108740
|
+
trust.command("reconcile").requiredOption("--agent <agent>", "Native trust adapter (codex)").requiredOption("--journal <path>", "Private incomplete native trust journal to inspect").option("--codex-command <path>", "Installed Codex executable used for its native configuration API", "codex").option("--supersede-binding", "Resolve a stale journal only when current policy differs solely by the reviewed Codex CLI binding", false).option("--skills-package-tar <path>", "Exact package tarball consumed by the reviewed executable").option("--skills-package-tar-sha256 <sha256>", "SHA-256 of the reviewed Skills package tarball").option("--skills-package-tar-bytes <bytes>", "Byte length of the reviewed Skills package tarball").option("--skills-package-manifest-sha256 <sha256>", "SHA-256 of the reviewed Skills package manifest").option("--skills-executable-sha256 <sha256>", "SHA-256 of the reviewed running Skills executable").option("--json", "Output the reconciliation receipt", false).description("Verify an interrupted Codex native trust write and resolve its private journal").action(async (options) => {
|
|
108676
108741
|
try {
|
|
108677
108742
|
if (options.agent !== "codex")
|
|
108678
108743
|
throw new Error("Native trust reconciliation currently supports --agent codex only");
|
|
108679
|
-
const
|
|
108744
|
+
const proofFields = [options.skillsPackageTar, options.skillsPackageTarSha256, options.skillsPackageTarBytes, options.skillsPackageManifestSha256, options.skillsExecutableSha256];
|
|
108745
|
+
if (proofFields.some((value) => value !== undefined) && proofFields.some((value) => value === undefined))
|
|
108746
|
+
throw new Error("Release provenance requires package tar path, tar hash/bytes, manifest hash, and executable hash together");
|
|
108747
|
+
const releaseProof = proofFields.every((value) => value !== undefined) ? { packageTarPath: options.skillsPackageTar, packageTarSha256: options.skillsPackageTarSha256, packageTarBytes: Number(options.skillsPackageTarBytes), manifestSha256: options.skillsPackageManifestSha256, executableSha256: options.skillsExecutableSha256 } : undefined;
|
|
108748
|
+
const result2 = await reconcileCodexNativeHooks({ journal: options.journal, codexCommand: options.codexCommand, supersedeBinding: options.supersedeBinding, releaseProof });
|
|
108680
108749
|
if (options.json)
|
|
108681
108750
|
await writeCliOutput(JSON.stringify(result2));
|
|
108682
108751
|
else
|
|
@@ -108693,7 +108762,7 @@ function registerAgentIntegration(parent) {
|
|
|
108693
108762
|
try {
|
|
108694
108763
|
if (agents(options.agent).length !== 1)
|
|
108695
108764
|
throw new Error("A hook invocation requires one agent");
|
|
108696
|
-
const inputText =
|
|
108765
|
+
const inputText = readFileSync38(0, "utf8");
|
|
108697
108766
|
if (inputText.length > 1024 * 1024)
|
|
108698
108767
|
throw new Error("Hook input is too large");
|
|
108699
108768
|
let input3 = JSON.parse(inputText);
|
|
@@ -108706,12 +108775,12 @@ function registerAgentIntegration(parent) {
|
|
|
108706
108775
|
input3.hook_event_name = event;
|
|
108707
108776
|
const projects = [process.cwd()];
|
|
108708
108777
|
if (input3.cwd !== undefined) {
|
|
108709
|
-
if (typeof input3.cwd !== "string" || !
|
|
108778
|
+
if (typeof input3.cwd !== "string" || !isAbsolute26(input3.cwd) || input3.cwd.includes("\x00"))
|
|
108710
108779
|
throw new Error("Invalid native hook working directory");
|
|
108711
108780
|
projects.push(input3.cwd);
|
|
108712
108781
|
}
|
|
108713
108782
|
if (options.agent === "cursor") {
|
|
108714
|
-
if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path) => typeof path !== "string" || !
|
|
108783
|
+
if (!Array.isArray(input3.workspace_roots) || input3.workspace_roots.length > 32 || input3.workspace_roots.some((path) => typeof path !== "string" || !isAbsolute26(path) || path.includes("\x00")))
|
|
108715
108784
|
throw new Error("Invalid Cursor workspace roots");
|
|
108716
108785
|
projects.push(...input3.workspace_roots);
|
|
108717
108786
|
input3.cwd ??= input3.workspace_roots[0] ?? process.cwd();
|
|
@@ -108789,7 +108858,7 @@ function registerAgentIntegration(parent) {
|
|
|
108789
108858
|
const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
|
|
108790
108859
|
migrate.command("native").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
|
|
108791
108860
|
try {
|
|
108792
|
-
const discoveryInputs = options.discoveryInputs ? JSON.parse(
|
|
108861
|
+
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync38(options.discoveryInputs, "utf8")) : undefined;
|
|
108793
108862
|
if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
|
|
108794
108863
|
throw new Error("--target-manifest cannot be combined with broad native migration selectors");
|
|
108795
108864
|
const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
|
|
@@ -109094,11 +109163,11 @@ import {
|
|
|
109094
109163
|
copyFileSync as copyFileSync2,
|
|
109095
109164
|
mkdirSync as mkdirSync26,
|
|
109096
109165
|
readdirSync as readdirSync21,
|
|
109097
|
-
readFileSync as
|
|
109098
|
-
statSync as
|
|
109166
|
+
readFileSync as readFileSync39,
|
|
109167
|
+
statSync as statSync22,
|
|
109099
109168
|
writeFileSync as writeFileSync26
|
|
109100
109169
|
} from "fs";
|
|
109101
|
-
import { dirname as
|
|
109170
|
+
import { dirname as dirname31, join as join57, resolve as resolve23, sep as sep10 } from "path";
|
|
109102
109171
|
function fail5(code2, message, detail = []) {
|
|
109103
109172
|
throw new StationSnapshotError(code2, message, detail);
|
|
109104
109173
|
}
|
|
@@ -109110,7 +109179,7 @@ function readSnapshotManifest(repoRoot, stationId) {
|
|
|
109110
109179
|
const manifestPath = join57(snapshotRoot, "sync-manifest.json");
|
|
109111
109180
|
let manifest;
|
|
109112
109181
|
try {
|
|
109113
|
-
manifest = JSON.parse(
|
|
109182
|
+
manifest = JSON.parse(readFileSync39(manifestPath, "utf8"));
|
|
109114
109183
|
} catch (error61) {
|
|
109115
109184
|
fail5("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error61.message}`);
|
|
109116
109185
|
}
|
|
@@ -109192,7 +109261,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
109192
109261
|
});
|
|
109193
109262
|
continue;
|
|
109194
109263
|
}
|
|
109195
|
-
const info =
|
|
109264
|
+
const info = statSync22(entry.fullPath);
|
|
109196
109265
|
const manifestHash = manifestHashes.get(`${agent}${MANIFEST_HASH_KEY_SEP}${homeRelative}`) ?? null;
|
|
109197
109266
|
let verified = false;
|
|
109198
109267
|
if (manifestHash !== null) {
|
|
@@ -109246,7 +109315,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
109246
109315
|
for (const copy of copies) {
|
|
109247
109316
|
let isStub = false;
|
|
109248
109317
|
try {
|
|
109249
|
-
isStub = isPointerSkillMd(
|
|
109318
|
+
isStub = isPointerSkillMd(readFileSync39(copy.fullPath, "utf8"));
|
|
109250
109319
|
} catch {
|
|
109251
109320
|
isStub = false;
|
|
109252
109321
|
}
|
|
@@ -109351,7 +109420,7 @@ function writeStationHydration(options) {
|
|
|
109351
109420
|
}
|
|
109352
109421
|
let written = 0;
|
|
109353
109422
|
for (const entry of toWrite) {
|
|
109354
|
-
mkdirSync26(
|
|
109423
|
+
mkdirSync26(dirname31(entry.destination), { recursive: true });
|
|
109355
109424
|
copyFileSync2(entry.fullPath, entry.destination);
|
|
109356
109425
|
written += 1;
|
|
109357
109426
|
}
|
|
@@ -109372,8 +109441,8 @@ function writeStationHydration(options) {
|
|
|
109372
109441
|
},
|
|
109373
109442
|
skills: resultSkills
|
|
109374
109443
|
};
|
|
109375
|
-
const hydrationManifestPath = join57(
|
|
109376
|
-
mkdirSync26(
|
|
109444
|
+
const hydrationManifestPath = join57(dirname31(cacheRoot), `hydration-${options.stationId}.json`);
|
|
109445
|
+
mkdirSync26(dirname31(hydrationManifestPath), { recursive: true });
|
|
109377
109446
|
writeFileSync26(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
109378
109447
|
`);
|
|
109379
109448
|
return {
|
|
@@ -109450,9 +109519,9 @@ var init_hydrate = __esm(() => {
|
|
|
109450
109519
|
|
|
109451
109520
|
// src/lib/prepare-skill.ts
|
|
109452
109521
|
import { createHash as createHash28, randomUUID as randomUUID11 } from "crypto";
|
|
109453
|
-
import { chmodSync as chmodSync3, existsSync as existsSync34, lstatSync as lstatSync29, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync6, readFileSync as
|
|
109522
|
+
import { chmodSync as chmodSync3, existsSync as existsSync34, lstatSync as lstatSync29, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync6, readFileSync as readFileSync40, readdirSync as readdirSync22, renameSync as renameSync12, rmSync as rmSync9, writeFileSync as writeFileSync27 } from "fs";
|
|
109454
109523
|
import { tmpdir as tmpdir2 } from "os";
|
|
109455
|
-
import { dirname as
|
|
109524
|
+
import { dirname as dirname32, isAbsolute as isAbsolute27, join as join58, relative as relative9, resolve as resolve24, sep as sep11 } from "path";
|
|
109456
109525
|
function prepareSkill(name, options) {
|
|
109457
109526
|
const normalized = normalizePortableSkillName(name);
|
|
109458
109527
|
if (!new RegExp(SEMVER_PATTERN).test(options.version))
|
|
@@ -109497,7 +109566,7 @@ function prepareSkill(name, options) {
|
|
|
109497
109566
|
mkdirSync27(join58(staging, directory2), { recursive: true });
|
|
109498
109567
|
for (const file2 of before2.files) {
|
|
109499
109568
|
const destination = join58(staging, file2.path);
|
|
109500
|
-
mkdirSync27(
|
|
109569
|
+
mkdirSync27(dirname32(destination), { recursive: true });
|
|
109501
109570
|
writeFileSync27(destination, file2.bytes, { mode: file2.mode });
|
|
109502
109571
|
}
|
|
109503
109572
|
const stagedManifest = join58(staging, "skill.json");
|
|
@@ -109558,7 +109627,7 @@ function validateRuntimeEntrypoint(candidate2, root2) {
|
|
|
109558
109627
|
if (typeof entry !== "string")
|
|
109559
109628
|
return;
|
|
109560
109629
|
const target = resolve24(root2, entry), rel = relative9(root2, target);
|
|
109561
|
-
if (
|
|
109630
|
+
if (isAbsolute27(entry) || /^[a-zA-Z]:/.test(entry) || entry.includes("\\") || !rel || rel === ".." || rel.startsWith(`..${sep11}`) || isAbsolute27(rel) || !existsSync34(target) || !lstatSync29(target).isFile()) {
|
|
109562
109631
|
throw new Error("Executable runtime.entrypoint must name an existing regular file inside the skill directory.");
|
|
109563
109632
|
}
|
|
109564
109633
|
}
|
|
@@ -109669,7 +109738,7 @@ function snapshot2(root2) {
|
|
|
109669
109738
|
bytes += stat3.size;
|
|
109670
109739
|
if (bytes > 50000000)
|
|
109671
109740
|
throw new Error("Skill exceeds the preparation size limit (50000000 bytes).");
|
|
109672
|
-
const content =
|
|
109741
|
+
const content = readFileSync40(absolute);
|
|
109673
109742
|
if (content.length !== stat3.size)
|
|
109674
109743
|
throw new Error("Skill changed during preparation.");
|
|
109675
109744
|
files.push({ path, bytes: content, mode: stat3.mode & 511 });
|
|
@@ -110071,7 +110140,7 @@ var init_schedule = __esm(() => {
|
|
|
110071
110140
|
|
|
110072
110141
|
// src/lib/registry-sync.ts
|
|
110073
110142
|
import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync28 } from "fs";
|
|
110074
|
-
import { dirname as
|
|
110143
|
+
import { dirname as dirname33, relative as relative10 } from "path";
|
|
110075
110144
|
function createRegistrySyncArtifact(options = {}) {
|
|
110076
110145
|
const profile = options.profile ?? "all";
|
|
110077
110146
|
const includeDocs = options.includeDocs ?? true;
|
|
@@ -110128,7 +110197,7 @@ function createRegistrySyncArtifact(options = {}) {
|
|
|
110128
110197
|
};
|
|
110129
110198
|
}
|
|
110130
110199
|
function writeRegistrySyncArtifact(path, artifact) {
|
|
110131
|
-
mkdirSync28(
|
|
110200
|
+
mkdirSync28(dirname33(path), { recursive: true });
|
|
110132
110201
|
writeFileSync28(path, `${JSON.stringify(artifact, null, 2)}
|
|
110133
110202
|
`);
|
|
110134
110203
|
}
|
|
@@ -110349,8 +110418,8 @@ __export(exports_publish, {
|
|
|
110349
110418
|
buildVersionManifest: () => buildVersionManifest,
|
|
110350
110419
|
PushSkillError: () => PushSkillError
|
|
110351
110420
|
});
|
|
110352
|
-
import { execFileSync } from "child_process";
|
|
110353
|
-
import { existsSync as existsSync35, readFileSync as
|
|
110421
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
110422
|
+
import { existsSync as existsSync35, readFileSync as readFileSync41 } from "fs";
|
|
110354
110423
|
import { hostname as hostname4 } from "os";
|
|
110355
110424
|
import { join as join59 } from "path";
|
|
110356
110425
|
function registerPublish(parent) {
|
|
@@ -110405,7 +110474,7 @@ async function pushSkill(name, options = {}) {
|
|
|
110405
110474
|
await verifyPackedCanonicalHash(skill.name, packed);
|
|
110406
110475
|
const versionManifest = buildVersionManifest(skill.path, packed);
|
|
110407
110476
|
const skillMdPath = join59(skill.path, "SKILL.md");
|
|
110408
|
-
const skillMd = existsSync35(skillMdPath) ?
|
|
110477
|
+
const skillMd = existsSync35(skillMdPath) ? readFileSync41(skillMdPath, "utf-8") : undefined;
|
|
110409
110478
|
const base3 = {
|
|
110410
110479
|
slug: skill.name,
|
|
110411
110480
|
path: skill.path,
|
|
@@ -110562,7 +110631,7 @@ function sanitizeGitRemote(url2) {
|
|
|
110562
110631
|
}
|
|
110563
110632
|
function gitValue(dir, args2) {
|
|
110564
110633
|
try {
|
|
110565
|
-
const out =
|
|
110634
|
+
const out = execFileSync2("git", ["-C", dir, ...args2], { stdio: ["ignore", "pipe", "ignore"], encoding: "utf-8" }).trim();
|
|
110566
110635
|
return out || null;
|
|
110567
110636
|
} catch {
|
|
110568
110637
|
return null;
|
|
@@ -112026,11 +112095,11 @@ var init_storage2 = __esm(() => {
|
|
|
112026
112095
|
});
|
|
112027
112096
|
|
|
112028
112097
|
// src/lib/registry-reconcile.ts
|
|
112029
|
-
import { existsSync as existsSync36, readFileSync as
|
|
112098
|
+
import { existsSync as existsSync36, readFileSync as readFileSync42, statSync as statSync23, writeFileSync as writeFileSync29 } from "fs";
|
|
112030
112099
|
import { join as join60 } from "path";
|
|
112031
112100
|
function isDirectory2(path) {
|
|
112032
112101
|
try {
|
|
112033
|
-
return
|
|
112102
|
+
return statSync23(path).isDirectory();
|
|
112034
112103
|
} catch {
|
|
112035
112104
|
return false;
|
|
112036
112105
|
}
|
|
@@ -112046,7 +112115,7 @@ function readBaseline(skillDir) {
|
|
|
112046
112115
|
if (!existsSync36(markerPath))
|
|
112047
112116
|
return;
|
|
112048
112117
|
try {
|
|
112049
|
-
const marker = JSON.parse(
|
|
112118
|
+
const marker = JSON.parse(readFileSync42(markerPath, "utf-8"));
|
|
112050
112119
|
if (!isSkillsOwnershipMarker(marker))
|
|
112051
112120
|
return;
|
|
112052
112121
|
return {
|
|
@@ -112062,7 +112131,7 @@ function readCursor(root2) {
|
|
|
112062
112131
|
if (!existsSync36(path))
|
|
112063
112132
|
return { runCount: 0 };
|
|
112064
112133
|
try {
|
|
112065
|
-
const cursor2 = JSON.parse(
|
|
112134
|
+
const cursor2 = JSON.parse(readFileSync42(path, "utf-8"));
|
|
112066
112135
|
return { runCount: typeof cursor2.runCount === "number" ? cursor2.runCount : 0 };
|
|
112067
112136
|
} catch {
|
|
112068
112137
|
return { runCount: 0 };
|
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.9.
|
|
20022
|
+
version: "0.9.8",
|
|
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.9.
|
|
24125
|
+
version: "0.9.8",
|
|
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.9.
|
|
24128
|
+
version: "0.9.8",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -11448,7 +11448,7 @@ import { isAbsolute as isAbsolute10, join as join20, resolve as resolve10 } from
|
|
|
11448
11448
|
// package.json
|
|
11449
11449
|
var package_default = {
|
|
11450
11450
|
name: "@hasna/skills",
|
|
11451
|
-
version: "0.9.
|
|
11451
|
+
version: "0.9.8",
|
|
11452
11452
|
description: "Skills library for AI coding agents",
|
|
11453
11453
|
type: "module",
|
|
11454
11454
|
bin: {
|
|
@@ -8,12 +8,21 @@ export interface CodexNativeHookTrustOptions {
|
|
|
8
8
|
reviewedPlanDigest?: string;
|
|
9
9
|
reviewedSkillsCli?: ReviewedSkillsCli;
|
|
10
10
|
}
|
|
11
|
+
export interface SkillsCliReleaseProof {
|
|
12
|
+
packageTarPath: string;
|
|
13
|
+
packageTarSha256: string;
|
|
14
|
+
packageTarBytes: number;
|
|
15
|
+
manifestSha256: string;
|
|
16
|
+
executableSha256: string;
|
|
17
|
+
}
|
|
11
18
|
export interface CodexNativeHookReconcileOptions {
|
|
12
19
|
home?: string;
|
|
13
20
|
dataDir?: string;
|
|
14
21
|
codexCommand?: string;
|
|
15
22
|
journal: string;
|
|
16
23
|
reviewedSkillsCli?: ReviewedSkillsCli;
|
|
24
|
+
releaseProof?: SkillsCliReleaseProof;
|
|
25
|
+
supersedeBinding?: boolean;
|
|
17
26
|
}
|
|
18
27
|
type Connect = typeof connectCodexHookRpc;
|
|
19
28
|
/** Resolve an interrupted native write only after the native consumer and every
|
|
@@ -24,21 +33,27 @@ export declare function reconcileCodexNativeHooks(options: CodexNativeHookReconc
|
|
|
24
33
|
version: number;
|
|
25
34
|
status: string;
|
|
26
35
|
reconciled: boolean;
|
|
36
|
+
supersededBinding: boolean;
|
|
27
37
|
automaticRollback: boolean;
|
|
28
38
|
journal: string;
|
|
29
39
|
planDigest: any;
|
|
30
40
|
nativeVersion: string;
|
|
31
41
|
nativeStateVerified: boolean;
|
|
42
|
+
nativeTrustPending: boolean;
|
|
32
43
|
nativeExecutionVerified: boolean;
|
|
33
44
|
configSha256: string;
|
|
34
45
|
hooksSha256: string;
|
|
35
46
|
policySha256: string;
|
|
47
|
+
policyBeforeSha256: string;
|
|
48
|
+
policyCurrentSha256: string;
|
|
49
|
+
previousSkillsCli: any;
|
|
36
50
|
skillsCli: {
|
|
37
51
|
path: string;
|
|
38
52
|
version: string;
|
|
39
53
|
sha256: string;
|
|
40
54
|
manifestSha256: string;
|
|
41
55
|
};
|
|
56
|
+
releaseProof: SkillsCliReleaseProof | undefined;
|
|
42
57
|
nativeInventoryScope: string;
|
|
43
58
|
unrelatedNativeHooksPreserved: boolean;
|
|
44
59
|
unrelatedSettingsAndCommentsPreserved: boolean;
|
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.9.
|
|
32376
|
+
version: "0.9.8",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|