@hasna/skills 0.10.36 → 0.10.39

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/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.36",
20022
+ version: "0.10.39",
20023
20023
  description: "Skills library for AI coding agents",
20024
20024
  type: "module",
20025
20025
  bin: {
@@ -20069,8 +20069,8 @@ var init_package = __esm(() => {
20069
20069
  types: "./dist/index.d.ts",
20070
20070
  scripts: {
20071
20071
  clean: "rm -rf bin/ dist/",
20072
- build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
20073
- "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
20072
+ build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/contracts/kernel-lock ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
20073
+ "build:js": "rm -rf dist && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
20074
20074
  test: "bun test --timeout 30000",
20075
20075
  dev: "bun run ./src/cli/index.tsx",
20076
20076
  "dev:watch": "bun --watch run ./src/cli/index.tsx",
@@ -20108,7 +20108,6 @@ var init_package = __esm(() => {
20108
20108
  author: "Hasna",
20109
20109
  license: "Apache-2.0",
20110
20110
  devDependencies: {
20111
- "@hasna/contracts": "1.1.0",
20112
20111
  "@types/bun": "1.3.14",
20113
20112
  "@types/node": "25.2.3",
20114
20113
  "@types/react": "^18.2.0",
@@ -20130,7 +20129,8 @@ var init_package = __esm(() => {
20130
20129
  "ink-spinner": "^5.0.0",
20131
20130
  "ink-text-input": "^6.0.0",
20132
20131
  react: "^18.2.0",
20133
- zod: "^4.3.6"
20132
+ zod: "^4.3.6",
20133
+ "@hasna/contracts": "1.3.5"
20134
20134
  },
20135
20135
  engines: {
20136
20136
  bun: ">=1.0.0"
@@ -20424,11 +20424,14 @@ function readCredentialFile(path, apiKeyKeys, pointerKey) {
20424
20424
  }
20425
20425
  return values[0] === undefined ? null : { apiKey: values[0] };
20426
20426
  }
20427
- function appConfigDiskValue(name, env, keys) {
20427
+ function appConfigDiskValue(name, env, keys, profile = null) {
20428
+ if (profile !== null && !SAFE_PROFILE.test(profile)) {
20429
+ throw new CredentialResolutionError(name, "The selected configuration profile is invalid.", ["profile"]);
20430
+ }
20428
20431
  const wanted = keys.filter((key) => !CREDENTIAL_SHAPED_KEY.test(key));
20429
20432
  if (wanted.length === 0)
20430
20433
  return null;
20431
- for (const path of credentialDiskSources(name, env)) {
20434
+ for (const path of profileDiskSources(name, env, profile)) {
20432
20435
  const parsed = readAppConfigFile(path);
20433
20436
  if (!parsed)
20434
20437
  continue;
@@ -38255,7 +38258,7 @@ function assertAgentPolicyCollections(policy) {
38255
38258
  }
38256
38259
  if (bridge.codexPluginSkills !== undefined)
38257
38260
  for (const control of array3(bridge.codexPluginSkills, 4096)) {
38258
- requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId"].includes(key)));
38261
+ requireBound(object4(control) && Object.keys(control).every((key) => ["name", "pluginId", "namespace", "pluginParent", "manifestSha256", "appSha256", "mcpSha256", "remotePluginId", "skillsOnly"].includes(key)));
38259
38262
  text(control.name, 129);
38260
38263
  text(control.pluginId, 1024);
38261
38264
  text(control.namespace, 64);
@@ -38263,6 +38266,8 @@ function assertAgentPolicyCollections(policy) {
38263
38266
  requireBound(isAbsolute3(control.pluginParent) && resolve2(control.pluginParent) === control.pluginParent && /^[a-f0-9]{64}$/.test(control.manifestSha256));
38264
38267
  if (control.remotePluginId !== undefined)
38265
38268
  requireBound(typeof control.remotePluginId === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(control.remotePluginId));
38269
+ if (control.skillsOnly !== undefined)
38270
+ requireBound(control.skillsOnly === true && control.appSha256 === undefined && control.mcpSha256 === undefined);
38266
38271
  if (control.mcpSha256 !== undefined)
38267
38272
  requireBound(typeof control.mcpSha256 === "string" && /^[a-f0-9]{64}$/.test(control.mcpSha256));
38268
38273
  if (control.appSha256 !== undefined)
@@ -38292,6 +38297,26 @@ function assertAgentPolicyCollections(policy) {
38292
38297
  text(agent, 128);
38293
38298
  requireBound(object4(value));
38294
38299
  text(value.agent, 128);
38300
+ if (value.codexRetiredMaterializations !== undefined) {
38301
+ const proof = record3(value.codexRetiredMaterializations, 3), roots = array3(proof.roots, AGENT_POLICY_LIMITS.discoveryRoots), parents = proof.parents === undefined ? [] : array3(proof.parents, AGENT_POLICY_LIMITS.discoveryRoots);
38302
+ requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && Object.keys(proof).every((key) => ["roots", "parents", "directories"].includes(key)) && roots.length + parents.length > 0 && new Set(roots).size === roots.length && new Set(parents).size === parents.length);
38303
+ for (const root of roots) {
38304
+ text(root);
38305
+ requireBound(isAbsolute3(root) && resolve2(root) === root);
38306
+ }
38307
+ for (const parent of parents) {
38308
+ text(parent);
38309
+ requireBound(isAbsolute3(parent) && resolve2(parent) === parent);
38310
+ }
38311
+ for (const directory of array3(proof.directories, AGENT_POLICY_LIMITS.discoveryDirectories)) {
38312
+ requireBound(object4(directory));
38313
+ text(directory.path);
38314
+ requireBound(directory.sha256 === null || typeof directory.sha256 === "string" && /^[a-f0-9]{64}$/.test(directory.sha256));
38315
+ if (directory.entries !== undefined)
38316
+ for (const entry of array3(directory.entries, AGENT_POLICY_LIMITS.discoveryDirectoryEntries))
38317
+ text(entry, AGENT_POLICY_LIMITS.pathCharacters * 6 + 100);
38318
+ }
38319
+ }
38295
38320
  if (value.codexInstallationInputs !== undefined) {
38296
38321
  const proof = record3(value.codexInstallationInputs, 4), inputs = array3(proof.plugins, 4096), ids = new Set;
38297
38322
  requireBound(agent === "codex" && value.agent === "codex" && value.method === "reviewed" && proof.version === "codex-cli 0.160.0" && typeof proof.catalogSha256 === "string" && /^[a-f0-9]{64}$/.test(proof.catalogSha256) && inputs.length > 0 && object4(bridge.codexPluginSkillReview) && bridge.codexPluginSkillReview.version === proof.version && bridge.codexPluginSkillReview.catalogSha256 === proof.catalogSha256);
@@ -45411,8 +45436,10 @@ function safe(path) {
45411
45436
  function same(before, after) {
45412
45437
  return Boolean(after && after.isDirectory() && before.dev === after.dev && before.ino === after.ino && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs);
45413
45438
  }
45414
- function capture(path, budget, excludedRoots) {
45439
+ function capture(path, budget, excludedRoots, omittedRoots = [], rows) {
45415
45440
  safe(path);
45441
+ if (omittedRoots.some((root2) => path === root2 || path.startsWith(root2 + "/")))
45442
+ return { path, sha256: null };
45416
45443
  const root = lstatSync5(path, { throwIfNoEntry: false });
45417
45444
  if (!root)
45418
45445
  return { path, sha256: null };
@@ -45432,6 +45459,8 @@ function capture(path, budget, excludedRoots) {
45432
45459
  need(new Set(names).size === names.length, "Ambiguous native discovery directory names");
45433
45460
  for (const name of names.sort()) {
45434
45461
  const child = join11(current, name), childRelative = relative3 ? `${relative3}/${name}` : name;
45462
+ if (omittedRoots.includes(child))
45463
+ continue;
45435
45464
  need(child.length <= AGENT_POLICY_LIMITS.pathCharacters, "Native discovery directory path limit exceeded");
45436
45465
  const stat = lstatSync5(child);
45437
45466
  need(stat.isFile() || stat.isDirectory(), "Refusing symlink or special native discovery directory member");
@@ -45440,6 +45469,7 @@ function capture(path, budget, excludedRoots) {
45440
45469
  budget.bytes += Buffer.byteLength(row);
45441
45470
  need(budget.bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Native discovery directory metadata limit exceeded");
45442
45471
  hash2.update(row);
45472
+ rows?.push(row);
45443
45473
  if (stat.isDirectory() && !excludedRoots.includes(child))
45444
45474
  walk(child, childRelative, stat, depth + 1);
45445
45475
  }
@@ -45449,6 +45479,44 @@ function capture(path, budget, excludedRoots) {
45449
45479
  safe(path);
45450
45480
  return { path, sha256: hash2.digest("hex") };
45451
45481
  }
45482
+ function captureDiscoveryDirectoryProjection(paths, omittedRoots) {
45483
+ need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid projected discovery directory collection");
45484
+ need(omittedRoots.length <= AGENT_POLICY_LIMITS.discoveryRoots && new Set(omittedRoots).size === omittedRoots.length, "Invalid inactive discovery roots");
45485
+ for (const root of omittedRoots)
45486
+ safe(root);
45487
+ const budget = { entries: 0, bytes: 0 };
45488
+ return paths.map((path) => {
45489
+ const entries = [];
45490
+ return { ...capture(path, budget, [], omittedRoots, entries), entries };
45491
+ });
45492
+ }
45493
+ function verifyDiscoveryDirectoryProjection(directories, omittedRoots) {
45494
+ need(directories.length <= AGENT_POLICY_LIMITS.discoveryDirectories, "Invalid projected discovery directory collection");
45495
+ let count = 0, bytes = 0;
45496
+ const expected = directories.map((directory) => {
45497
+ safe(directory.path);
45498
+ need(Array.isArray(directory.entries), "Missing reviewed directory membership inventory");
45499
+ const hash2 = createHash2("sha256");
45500
+ const kept = [];
45501
+ for (const row of directory.entries) {
45502
+ need(typeof row === "string" && ++count <= AGENT_POLICY_LIMITS.discoveryDirectoryEntries, "Invalid reviewed directory membership rows");
45503
+ bytes += Buffer.byteLength(row);
45504
+ need(bytes <= AGENT_POLICY_LIMITS.discoveryDirectoryBytes, "Reviewed directory membership byte limit exceeded");
45505
+ const value = JSON.parse(row);
45506
+ need(Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && value[0] && !isAbsolute5(value[0]) && !value[0].split("/").some((part) => !part || part === "." || part === "..") && ["file", "directory"].includes(value[1]) && row === JSON.stringify(value) + `
45507
+ `, "Invalid reviewed directory member");
45508
+ hash2.update(row);
45509
+ const path = join11(directory.path, value[0]);
45510
+ if (!omittedRoots.some((root) => path === root || path.startsWith(root + "/")))
45511
+ kept.push(row);
45512
+ }
45513
+ need(directory.sha256 === null ? directory.entries.length === 0 : hash2.digest("hex") === directory.sha256, "Reviewed directory inventory does not match its fingerprint");
45514
+ const absent = omittedRoots.some((root) => directory.path === root || directory.path.startsWith(root + "/"));
45515
+ return { path: directory.path, sha256: absent || directory.sha256 === null ? null : createHash2("sha256").update(kept.join("")).digest("hex") };
45516
+ });
45517
+ const current = captureDiscoveryDirectoryProjection(directories.map((item) => item.path), omittedRoots);
45518
+ need(current.every((item, index) => item.sha256 === expected[index].sha256), "Native discovery directory membership changed outside retired Codex roots");
45519
+ }
45452
45520
  function captureDiscoveryDirectories(paths, excludedRoots = []) {
45453
45521
  need(Array.isArray(paths) && paths.length <= AGENT_POLICY_LIMITS.discoveryDirectories && new Set(paths).size === paths.length, "Invalid native discovery directory collection");
45454
45522
  const budget = { entries: 0, bytes: 0 };
@@ -47297,6 +47365,10 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
47297
47365
  var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation", RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.", DEFAULT_PREPARATION_TIMEOUT_MS = 60000;
47298
47366
  var init_dependency_preparation = () => {};
47299
47367
 
47368
+ // src/lib/codex-corpus-write.ts
47369
+ import { KernelLock } from "@hasna/contracts/kernel-lock";
47370
+ var init_codex_corpus_write = () => {};
47371
+
47300
47372
  // src/lib/codex-local-mcp-controls.ts
47301
47373
  function isCodexLocalStdioMcpServer(name, server) {
47302
47374
  const identifier2 = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(value);
@@ -47664,6 +47736,7 @@ var init_codex_native_compatibility = __esm(() => {
47664
47736
 
47665
47737
  // src/lib/codex-hook-rpc.ts
47666
47738
  var init_codex_hook_rpc = __esm(() => {
47739
+ init_codex_corpus_write();
47667
47740
  init_codex_native_compatibility();
47668
47741
  init_codex_native_compatibility();
47669
47742
  });
@@ -47673,6 +47746,7 @@ function codexPluginSourceIsConfigControlled(plugin) {
47673
47746
  const remoteRealms = ["openai-curated-remote", "created-by-me-remote", "workspace-directory", "workspace-shared-with-me", "workspace-shared-with-me-private", "workspace-shared-with-me-unlisted"];
47674
47747
  return Boolean(plugin.sourceType && ["local", "git", "npm"].includes(plugin.sourceType) && plugin.sourceSha256 && /^[a-f0-9]{64}$/.test(plugin.sourceSha256) && !plugin.remotePluginId && !remoteRealms.includes(plugin.id.slice(plugin.id.lastIndexOf("@") + 1)));
47675
47748
  }
47749
+ var remotePluginIdentifier = (value) => typeof value === "string" && /^[A-Za-z0-9_~-]{1,1024}$/.test(value);
47676
47750
  var init_codex_native_skill_catalog = __esm(() => {
47677
47751
  init_codex_hook_rpc();
47678
47752
  init_codex_native_compatibility();
@@ -47685,6 +47759,214 @@ import { lstatSync as lstatSync11, readdirSync as readdirSync6, readlinkSync as
47685
47759
  function refuse() {
47686
47760
  throw new Error("CODEX_NATIVE_SKILL_IDENTITY_UNSUPPORTED: review native plugin names and hook controls");
47687
47761
  }
47762
+ function remoteInstallationMatches(parent, remotePluginId, read) {
47763
+ try {
47764
+ if (!remotePluginIdentifier(remotePluginId) || !lstatSync11(parent).isDirectory() || realpathSync3(parent) !== parent)
47765
+ return false;
47766
+ const path = join16(parent, ".codex-remote-plugin-install.json"), stat = lstatSync11(path, { throwIfNoEntry: false });
47767
+ if (!stat?.isFile() || stat.isSymbolicLink() || stat.size > 16384)
47768
+ return false;
47769
+ const text2 = read(path);
47770
+ hashNativeJsonControls(text2);
47771
+ const value = JSON.parse(text2);
47772
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 2 && value.schema_version === 1 && value.remote_plugin_id === remotePluginId);
47773
+ } catch {
47774
+ return false;
47775
+ }
47776
+ }
47777
+ function appControls(path, read) {
47778
+ const text2 = read(path);
47779
+ let value, sha2562;
47780
+ try {
47781
+ sha2562 = hashNativeJsonControls(text2);
47782
+ value = JSON.parse(text2);
47783
+ } catch {
47784
+ refuse();
47785
+ }
47786
+ const object7 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
47787
+ if (!object7(value) || Object.keys(value).length !== 1 || !object7(value.apps))
47788
+ refuse();
47789
+ const apps = Object.entries(value.apps);
47790
+ if (!apps.length || apps.length > 256)
47791
+ refuse();
47792
+ for (const [name, entry] of apps) {
47793
+ if (!identifier2(name) || !object7(entry) || !Object.keys(entry).every((key) => ["id", "category", "required"].includes(key)) || typeof entry.id !== "string" || !/^[A-Za-z0-9_-]{1,256}$/.test(entry.id) || entry.category !== undefined && (typeof entry.category !== "string" || !entry.category.trim() || entry.category.length > 256 || /[\x00-\x1f\x7f]/.test(entry.category)) || entry.required !== undefined && typeof entry.required !== "boolean")
47794
+ refuse();
47795
+ }
47796
+ return sha2562;
47797
+ }
47798
+ function mcpControls(path, read) {
47799
+ const text2 = read(path);
47800
+ let value, sha2562;
47801
+ try {
47802
+ sha2562 = hashNativeJsonControls(text2);
47803
+ value = JSON.parse(text2);
47804
+ } catch {
47805
+ refuse();
47806
+ }
47807
+ const object7 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
47808
+ if (!object7(value) || Object.keys(value).length !== 1 || !object7(value.mcpServers))
47809
+ refuse();
47810
+ const servers = Object.entries(value.mcpServers);
47811
+ if (!servers.length || servers.length > 256)
47812
+ refuse();
47813
+ for (const [name, server] of servers)
47814
+ if (!isCodexLocalStdioMcpServer(name, server))
47815
+ refuse();
47816
+ return sha2562;
47817
+ }
47818
+ function isBundledCleanupHooks(root, namespace, value) {
47819
+ const object7 = (v) => Boolean(v && typeof v === "object" && !Array.isArray(v));
47820
+ const exactKeys = (v, keys) => Object.keys(v).length === keys.length && keys.every((key) => Object.hasOwn(v, key));
47821
+ if (basename5(dirname10(root)) !== namespace || basename5(dirname10(dirname10(root))) !== "openai-bundled")
47822
+ return false;
47823
+ const server = namespace === "unified-computer-use" ? "cua_repl" : ["browser", "chrome", "chrome-dev", "chrome-internal", "computer-use"].includes(namespace) ? "node_repl" : undefined;
47824
+ if (!server || !object7(value) || !exactKeys(value, ["hooks"]) || !object7(value.hooks))
47825
+ return false;
47826
+ const events = Object.entries(value.hooks);
47827
+ if (!events.length || events.length > 3)
47828
+ return false;
47829
+ for (const [event, groups] of events) {
47830
+ if (!["Interrupt", "SubagentStop", "Stop"].includes(event) || !Array.isArray(groups) || groups.length !== 1)
47831
+ return false;
47832
+ const group = groups[0];
47833
+ if (!object7(group) || !exactKeys(group, ["hooks"]) || !Array.isArray(group.hooks) || group.hooks.length !== 1)
47834
+ return false;
47835
+ const handler = group.hooks[0];
47836
+ if (!object7(handler) || !exactKeys(handler, ["type", "server", "tool", "input"]) || handler.type !== "mcp_tool" || handler.server !== server || handler.tool !== "turn_ended" || !object7(handler.input) || !exactKeys(handler.input, ["hook_event_name", "session_id", "turn_id"]) || handler.input.hook_event_name !== "${hook_event_name}" || handler.input.turn_id !== "${turn_id}" || handler.input.session_id !== (event === "SubagentStop" ? "${agent_id}" : "${session_id}"))
47837
+ return false;
47838
+ }
47839
+ return true;
47840
+ }
47841
+ function rootControls(root, read) {
47842
+ const manifestPath = join16(root, ".codex-plugin/plugin.json"), manifestFile = lstatSync11(manifestPath, { throwIfNoEntry: false });
47843
+ if (!lstatSync11(join16(root, ".codex-plugin"), { throwIfNoEntry: false })?.isDirectory() || !manifestFile?.isFile() || manifestFile.size > 1024 * 1024)
47844
+ refuse();
47845
+ let manifest, manifestSha256;
47846
+ try {
47847
+ const text2 = read(manifestPath);
47848
+ manifestSha256 = hashNativeJsonControls(text2, "version");
47849
+ manifest = JSON.parse(text2);
47850
+ } catch {
47851
+ refuse();
47852
+ }
47853
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest) || !identifier2(manifest.name))
47854
+ refuse();
47855
+ const cacheVersion = basename5(root), declaredVersion = manifest.version === undefined ? "" : typeof manifest.version === "string" ? manifest.version.trim() : null;
47856
+ if (declaredVersion === null || (declaredVersion ? declaredVersion !== cacheVersion : cacheVersion !== CODEX_DEFAULT_AGENT_PLUGIN_VERSION))
47857
+ refuse();
47858
+ if (manifest.hooks !== undefined && !isBundledCleanupHooks(root, manifest.name, manifest.hooks) || manifest.commands !== undefined || manifest.mcpServers !== undefined && manifest.mcpServers !== "./.mcp.json" || manifest.apps !== undefined && manifest.apps !== "./.app.json" || lstatSync11(join16(root, "hooks"), { throwIfNoEntry: false }))
47859
+ refuse();
47860
+ const mcp = join16(root, ".mcp.json"), mcpPresent = lstatSync11(mcp, { throwIfNoEntry: false });
47861
+ if (manifest.hooks !== undefined && (manifest.mcpServers !== undefined || mcpPresent))
47862
+ refuse();
47863
+ if (mcpPresent && (!mcpPresent.isFile() || mcpPresent.isSymbolicLink() || mcpPresent.size > 1024 * 1024 || realpathSync3(mcp) !== mcp))
47864
+ refuse();
47865
+ if (Boolean(mcpPresent) !== (manifest.mcpServers !== undefined))
47866
+ refuse();
47867
+ const mcpSha256 = mcpPresent ? mcpControls(mcp, read) : undefined;
47868
+ const app = join16(root, ".app.json"), present = lstatSync11(app, { throwIfNoEntry: false });
47869
+ if (present && (!present.isFile() || present.isSymbolicLink() || present.size > 1024 * 1024))
47870
+ refuse();
47871
+ if (manifest.apps !== undefined && !present)
47872
+ refuse();
47873
+ const appSha256 = present ? appControls(app, read) : undefined;
47874
+ return { namespace: manifest.name, manifestSha256, ...appSha256 ? { appSha256 } : {}, ...mcpSha256 ? { mcpSha256 } : {} };
47875
+ }
47876
+ function reviewedDisabledCodexPluginParent(cache, parent, controls, rules) {
47877
+ try {
47878
+ const parts = relative4(cache, parent).split(sep4), enrolled = controls.filter((control) => control.pluginParent === parent);
47879
+ if (resolve9(parent) !== parent || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !enrolled.length || !Array.isArray(rules))
47880
+ return false;
47881
+ for (let cursor = parent;; cursor = dirname10(cursor)) {
47882
+ const stat = lstatSync11(cursor, { throwIfNoEntry: false });
47883
+ if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
47884
+ return false;
47885
+ if (dirname10(cursor) === cursor)
47886
+ break;
47887
+ }
47888
+ return enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path.startsWith(parent + sep4))) && rule.enabled !== false));
47889
+ } catch {
47890
+ return false;
47891
+ }
47892
+ }
47893
+ function absentDisabledCodexPluginParent(cache, parent, controls, rules) {
47894
+ return reviewedDisabledCodexPluginParent(cache, parent, controls, rules) && !lstatSync11(parent, { throwIfNoEntry: false });
47895
+ }
47896
+ function reviewedRetiredCodexRoot(cache, root, controls, rules, read, allowAbsent = false) {
47897
+ try {
47898
+ const parent = dirname10(root), parts = relative4(cache, parent).split(sep4), enrolled = controls.filter((control) => control.pluginParent === parent);
47899
+ if (resolve9(root) !== root || parts.length !== 2 || parts.some((part) => !identifier2(part)) || !basename5(root) || basename5(root) === "latest" || !enrolled.length || !Array.isArray(rules))
47900
+ return false;
47901
+ for (let cursor = root;; cursor = dirname10(cursor)) {
47902
+ const stat = lstatSync11(cursor, { throwIfNoEntry: false });
47903
+ if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
47904
+ return false;
47905
+ if (dirname10(cursor) === cursor)
47906
+ break;
47907
+ }
47908
+ if (!enrolled.every((control) => control.namespace === parts[1] && control.pluginId === `${parts[1]}@${parts[0]}` && remotePluginIdentifier(control.remotePluginId) && control.skillsOnly === true && !control.appSha256 && !control.mcpSha256 && control.name.startsWith(control.namespace + ":") && /^[a-f0-9]{64}$/.test(control.manifestSha256) && rules.some((rule) => typeof rule?.name === "string" && rule.name.trim() === control.name && rule.enabled === false) && !rules.some((rule) => (typeof rule?.name === "string" && rule.name.trim() === control.name || typeof rule?.path === "string" && (rule.path === parent || rule.path === root || rule.path.startsWith(root + sep4))) && rule.enabled !== false)))
47909
+ return false;
47910
+ if (!lstatSync11(root, { throwIfNoEntry: false }))
47911
+ return allowAbsent && (lstatSync11(parent, { throwIfNoEntry: false }) ? enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read)) : absentDisabledCodexPluginParent(cache, parent, controls, rules));
47912
+ if (!enrolled.every((control) => remoteInstallationMatches(parent, control.remotePluginId, read)))
47913
+ return false;
47914
+ const parsed = rootControls(root, read);
47915
+ if (JSON.parse(read(join16(root, ".codex-plugin/plugin.json"))).hooks !== undefined)
47916
+ return false;
47917
+ if (!enrolled.some((control) => control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && !parsed.appSha256 && !parsed.mcpSha256))
47918
+ return false;
47919
+ let entries = 0;
47920
+ const emptySkills = (directory, depth) => depth <= 64 && readdirSync6(directory).every((name) => {
47921
+ if (++entries > 4096 || name === "SKILL.md")
47922
+ return false;
47923
+ const path = join16(directory, name), stat = lstatSync11(path);
47924
+ return !stat.isSymbolicLink() && (stat.isFile() || stat.isDirectory() && emptySkills(path, depth + 1));
47925
+ });
47926
+ return emptySkills(root, 0);
47927
+ } catch {
47928
+ return false;
47929
+ }
47930
+ }
47931
+ function reviewedCodexPluginCapabilitiesUnchanged(cache, controls, read, rules) {
47932
+ if (!Array.isArray(controls) || controls.length > 4096)
47933
+ return false;
47934
+ let entries = 0;
47935
+ try {
47936
+ for (const parent of new Set(controls.map((control) => control.pluginParent))) {
47937
+ if (absentDisabledCodexPluginParent(cache, parent, controls, rules))
47938
+ continue;
47939
+ if (!parent.startsWith(cache + sep4) || !lstatSync11(parent, { throwIfNoEntry: false })?.isDirectory() || realpathSync3(parent) !== parent)
47940
+ return false;
47941
+ for (const control of controls.filter((control2) => control2.pluginParent === parent)) {
47942
+ if (control.remotePluginId !== undefined && !remoteInstallationMatches(parent, control.remotePluginId, read))
47943
+ return false;
47944
+ }
47945
+ const names = readdirSync6(parent);
47946
+ entries += names.length;
47947
+ if (entries > 4096)
47948
+ return false;
47949
+ for (const name of names) {
47950
+ let root = join16(parent, name), stat = lstatSync11(root);
47951
+ if (stat.isSymbolicLink()) {
47952
+ const target = resolve9(parent, readlinkSync3(root));
47953
+ if (name !== "latest" || dirname10(target) !== parent || !lstatSync11(target).isDirectory() || realpathSync3(target) !== target)
47954
+ return false;
47955
+ root = target;
47956
+ stat = lstatSync11(root);
47957
+ }
47958
+ if (!stat.isDirectory() || ![".codex-plugin/plugin.json", ".app.json", "hooks", ".mcp.json"].some((path) => lstatSync11(join16(root, path), { throwIfNoEntry: false })))
47959
+ continue;
47960
+ const parsed = rootControls(root, read);
47961
+ if (!controls.some((control) => control.pluginParent === parent && control.namespace === parsed.namespace && control.manifestSha256 === parsed.manifestSha256 && control.appSha256 === parsed.appSha256 && control.mcpSha256 === parsed.mcpSha256))
47962
+ return false;
47963
+ }
47964
+ }
47965
+ return true;
47966
+ } catch {
47967
+ return false;
47968
+ }
47969
+ }
47688
47970
  function sourceInputUnchanged(input2, cache, read) {
47689
47971
  try {
47690
47972
  const parts = relative4(cache, input2.pluginParent).split(sep4);
@@ -47709,7 +47991,7 @@ function reviewedCodexPluginSourceRoots(cache, inputs, read) {
47709
47991
  refuse();
47710
47992
  return roots;
47711
47993
  }
47712
- var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v);
47994
+ var identifier2 = (v) => typeof v === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(v), CODEX_DEFAULT_AGENT_PLUGIN_VERSION = "1.0.0";
47713
47995
  var init_codex_plugin_skill_controls = __esm(() => {
47714
47996
  init_claude_settings_witness();
47715
47997
  init_codex_native_compatibility();
@@ -48235,6 +48517,7 @@ var init_codex_hook_trust_files = () => {};
48235
48517
 
48236
48518
  // src/lib/agent-integration.ts
48237
48519
  var init_agent_integration = __esm(() => {
48520
+ init_codex_corpus_write();
48238
48521
  init_codex_plugin_skill_controls();
48239
48522
  init_codex_native_skill_catalog();
48240
48523
  init_codex_settings_witness();
@@ -48261,6 +48544,7 @@ var init_home_migration = __esm(() => {
48261
48544
 
48262
48545
  // src/lib/agent-sync.ts
48263
48546
  var init_agent_sync = __esm(() => {
48547
+ init_codex_corpus_write();
48264
48548
  init_agent_integration();
48265
48549
  init_portable_skills();
48266
48550
  init_home_migration();
@@ -48502,6 +48786,7 @@ function readBundledSkillVersion(name) {
48502
48786
  }
48503
48787
  var AGENT_TARGETS, AGENT_LABELS;
48504
48788
  var init_installer = __esm(() => {
48789
+ init_codex_corpus_write();
48505
48790
  init_agent_sync();
48506
48791
  init_agent_integration();
48507
48792
  init_portable_skills();
@@ -49921,6 +50206,94 @@ function projected(source, changes, budget = discoveryByteBudget()) {
49921
50206
  return digest(text2);
49922
50207
  return projectNativeDiscoveryFields(text2, source.format, source.fields ?? [], source.path);
49923
50208
  }
50209
+ function absentCodexSkillRoots(binding) {
50210
+ const controls = binding.codexDisabledPluginSkills;
50211
+ if (controls === undefined || Array.isArray(controls) && !controls.length)
50212
+ return [];
50213
+ if (binding.agent !== "codex" || !Array.isArray(controls) || controls.length > 4096)
50214
+ throw new Error("Invalid disabled Codex discovery proof");
50215
+ if (binding.method !== "reviewed" || controls.every((control) => lstatSync18(control.pluginParent, { throwIfNoEntry: false })))
50216
+ return [];
50217
+ const cache = dirname15(dirname15(controls[0].pluginParent)), config2 = join25(dirname15(dirname15(cache)), "config.toml");
50218
+ const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
50219
+ if (!source || projected(source) !== source.sha256)
50220
+ throw new Error("Missing current disabled Codex configuration witness");
50221
+ const rules = Bun.TOML.parse(read(config2)).skills?.config ?? [];
50222
+ return [...new Set(controls.map((control) => control.pluginParent))].filter((parent) => !codexDirectHookSelects(cache, parent) && absentDisabledCodexPluginParent(cache, parent, controls, rules));
50223
+ }
50224
+ function codexDirectHookSelects(cache, root) {
50225
+ const codex = dirname15(dirname15(cache)), config2 = Bun.TOML.parse(read(join25(codex, "config.toml")) ?? "");
50226
+ const hooks = read(join25(codex, "hooks.json"));
50227
+ if (hooks !== null)
50228
+ hashNativeJsonControls(hooks);
50229
+ return JSON.stringify(config2.hooks ?? {}).includes(root) || hooks !== null && JSON.stringify(JSON.parse(hooks)).includes(root);
50230
+ }
50231
+ function retiredCodexRoots(binding) {
50232
+ const proof = binding.codexRetiredMaterializations;
50233
+ if (proof === undefined)
50234
+ return [];
50235
+ const controls = binding.codexDisabledPluginSkills;
50236
+ if (binding.agent !== "codex" || binding.method !== "reviewed" || !Array.isArray(controls) || !controls.length || !Array.isArray(proof.roots) || proof.roots.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.roots).size !== proof.roots.length || !Array.isArray(proof.directories) || proof.parents !== undefined && (!Array.isArray(proof.parents) || proof.parents.length > AGENT_POLICY_LIMITS.discoveryRoots || new Set(proof.parents).size !== proof.parents.length) || !proof.roots.length && !proof.parents?.length)
50237
+ throw new Error("Invalid retired Codex materialization proof");
50238
+ const cache = dirname15(dirname15(controls[0].pluginParent)), config2 = join25(dirname15(dirname15(cache)), "config.toml");
50239
+ const source = binding.sources.find((source2) => source2.path === config2 && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined);
50240
+ if (!source || projected(source) !== source.sha256)
50241
+ throw new Error("Missing current retired Codex configuration witness");
50242
+ const rules = Bun.TOML.parse(read(config2)).skills?.config ?? [];
50243
+ if ((proof.parents ?? []).some((parent) => codexDirectHookSelects(cache, parent) || !reviewedDisabledCodexPluginParent(cache, parent, controls, rules)) || !reviewedCodexPluginCapabilitiesUnchanged(cache, controls, (path) => read(path), rules))
50244
+ throw new Error("Retired Codex parent capabilities changed");
50245
+ for (const root of proof.roots) {
50246
+ const manifest = binding.sources.find((source2) => source2.path === join25(root, ".codex-plugin/plugin.json") && source2.sha256 !== null && source2.format === undefined && source2.fields === undefined && [undefined, "bytes"].includes(source2.hashMode));
50247
+ if (!manifest || codexDirectHookSelects(cache, root) || !reviewedRetiredCodexRoot(cache, root, controls, rules, (path) => read(path), true))
50248
+ throw new Error("Retired Codex materialization changed");
50249
+ }
50250
+ return proof.roots;
50251
+ }
50252
+ function absentRetiredClaudeRoots(binding) {
50253
+ if (binding.agent !== "claude" || binding.method !== "reviewed")
50254
+ return [];
50255
+ const result = new Set;
50256
+ for (const source of binding.sources) {
50257
+ if (source.format !== undefined || source.fields !== undefined || source.managedPlugins !== undefined || ![undefined, "bytes"].includes(source.hashMode) || source.sha256 === null)
50258
+ continue;
50259
+ const marker = sep5 + ".claude" + sep5 + "plugins" + sep5 + "cache" + sep5, at = source.path.indexOf(marker);
50260
+ if (at < 0 || source.path.indexOf(marker, at + 1) >= 0)
50261
+ continue;
50262
+ const cache = source.path.slice(0, at + marker.length - 1), parts = source.path.slice(at + marker.length).split(sep5);
50263
+ if (parts.length < 4 || parts.slice(0, 3).some((part) => !part || part === "." || part === ".."))
50264
+ continue;
50265
+ const root = join25(cache, ...parts.slice(0, 3));
50266
+ safe4(root);
50267
+ if (lstatSync18(root, { throwIfNoEntry: false }))
50268
+ continue;
50269
+ const claude = dirname15(dirname15(cache)), settings = join25(claude, "settings.json"), registry2 = join25(claude, "plugins/installed_plugins.json");
50270
+ const full = (path) => binding.sources.find((item) => item.path === path && item.sha256 !== null && item.format === undefined && item.fields === undefined);
50271
+ const configSource = full(settings), registrySource = full(registry2);
50272
+ if (!configSource || !registrySource || projected(configSource) !== configSource.sha256 || projected(registrySource) !== registrySource.sha256)
50273
+ continue;
50274
+ const settingsText = read(settings), registryText = read(registry2);
50275
+ hashNativeJsonControls(settingsText);
50276
+ hashNativeJsonControls(registryText);
50277
+ const config2 = JSON.parse(settingsText), installed = JSON.parse(registryText), id = `${parts[1]}@${parts[0]}`;
50278
+ if (installed.version !== 2 || !installed.plugins || typeof installed.plugins !== "object" || Array.isArray(installed.plugins) || config2.enabledPlugins?.[id] !== true)
50279
+ continue;
50280
+ if (JSON.stringify(config2.hooks ?? {}).includes(root))
50281
+ continue;
50282
+ const rows = installed.plugins[id];
50283
+ if (!Array.isArray(rows) || rows.length !== 1 || rows[0]?.scope !== "user" || typeof rows[0]?.installPath !== "string")
50284
+ continue;
50285
+ const active = rows[0].installPath;
50286
+ if (rows[0].version !== basename8(active) || resolve15(active) !== active || dirname15(active) !== dirname15(root) || active === root || !lstatSync18(active, { throwIfNoEntry: false })?.isDirectory() || realpathSync5(active) !== active)
50287
+ continue;
50288
+ const activeManifest = full(join25(active, ".claude-plugin/plugin.json"));
50289
+ if (!activeManifest || projected(activeManifest) !== activeManifest.sha256)
50290
+ continue;
50291
+ if (Object.values(installed.plugins).some((value) => !Array.isArray(value) || value.some((row) => !row || typeof row.installPath !== "string" || row.installPath === root || row.installPath.startsWith(root + sep5) || root.startsWith(row.installPath + sep5))))
50292
+ continue;
50293
+ result.add(root);
50294
+ }
50295
+ return [...result];
50296
+ }
49924
50297
  function verifyAgentDiscovery(binding, codexRecovery) {
49925
50298
  if (codexRecovery)
49926
50299
  assertCodexHookDiscoveryRecovery(binding, codexRecovery);
@@ -49929,15 +50302,33 @@ function verifyAgentDiscovery(binding, codexRecovery) {
49929
50302
  if (binding.agent === "hermes" && !binding.directories?.length)
49930
50303
  throw new Error("Hermes discovery requires directory membership coverage; run skills hook install with a fresh discovery review");
49931
50304
  const installationRoots = codexInstallationRoots(binding);
50305
+ const retiredRoots = retiredCodexRoots(binding);
49932
50306
  const installationInput = (path) => installationRoots.some((root) => path === root || path.startsWith(root + sep5));
49933
50307
  if (binding.directories !== undefined) {
49934
50308
  const projected2 = binding.codexInstallationInputs?.directories ?? [];
49935
50309
  const ancestors2 = binding.directories.filter((directory) => installationRoots.some((root) => root.startsWith(directory.path + sep5)));
49936
50310
  if (projected2.length !== ancestors2.length || projected2.some((directory) => !ancestors2.some((original) => original.path === directory.path)))
49937
50311
  throw new Error("Missing installation input directory projection");
49938
- verifyDiscoveryDirectories(binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory)));
50312
+ const ordinary = binding.directories.filter((directory) => !installationInput(directory.path) && !ancestors2.includes(directory));
50313
+ if (binding.codexRetiredMaterializations) {
50314
+ const projection = binding.codexRetiredMaterializations.directories;
50315
+ if (projection.length !== ordinary.length || projection.some((item, index) => item.path !== ordinary[index]?.path))
50316
+ throw new Error("Missing retired Codex directory projection");
50317
+ const omitted = [...retiredRoots, ...(binding.codexRetiredMaterializations.parents ?? []).filter((parent) => !lstatSync18(parent, { throwIfNoEntry: false }))];
50318
+ if (projection.every((item) => Array.isArray(item.entries)))
50319
+ verifyDiscoveryDirectoryProjection(projection, omitted);
50320
+ else {
50321
+ if (omitted.some((root) => !retiredRoots.includes(root)))
50322
+ throw new Error("Missing reviewed directory membership inventory; provide a fresh discovery review");
50323
+ const current = captureDiscoveryDirectoryProjection(ordinary.map((item) => item.path), retiredRoots).map(({ entries, ...item }) => item);
50324
+ if (!isDeepStrictEqual3(current, projection))
50325
+ throw new Error("Native discovery directory membership changed outside retired Codex roots");
50326
+ }
50327
+ } else
50328
+ verifyDiscoveryDirectories(ordinary);
49939
50329
  verifyDiscoveryDirectories(projected2, installationRoots);
49940
50330
  }
50331
+ const absentRoots = [...absentCodexSkillRoots(binding), ...absentRetiredClaudeRoots(binding), ...retiredRoots.filter((root) => !lstatSync18(root, { throwIfNoEntry: false }))];
49941
50332
  const budget = discoveryByteBudget();
49942
50333
  for (const source of binding.sources) {
49943
50334
  assertMarketplaceBinding(binding, source);
@@ -49951,12 +50342,21 @@ function verifyAgentDiscovery(binding, codexRecovery) {
49951
50342
  safe4(source.path);
49952
50343
  continue;
49953
50344
  }
50345
+ if (absentRoots.some((root) => source.path.startsWith(root + sep5)) && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && [undefined, "bytes"].includes(source.hashMode)) {
50346
+ safe4(source.path);
50347
+ continue;
50348
+ }
49954
50349
  const current = projected(source, undefined, budget);
49955
50350
  if (current !== source.sha256 && !verifiesCodexHookDiscoverySource(binding, source, current, codexRecovery))
49956
50351
  throw new Error(`Native discovery input changed; run skills hook install with a fresh discovery review: ${source.path}`);
49957
50352
  }
49958
50353
  for (const root of binding.roots)
49959
50354
  safe4(root);
50355
+ for (const root of absentRoots) {
50356
+ safe4(root);
50357
+ if (lstatSync18(root, { throwIfNoEntry: false }))
50358
+ throw new Error("Retired native materialization changed during discovery verification");
50359
+ }
49960
50360
  }
49961
50361
  function codexInstallationRoots(binding) {
49962
50362
  const proof = binding.codexInstallationInputs;
package/bin/migrate.js CHANGED
@@ -7,7 +7,7 @@ import { join as join5 } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@hasna/skills",
10
- version: "0.10.36",
10
+ version: "0.10.39",
11
11
  description: "Skills library for AI coding agents",
12
12
  type: "module",
13
13
  bin: {
@@ -57,8 +57,8 @@ var package_default = {
57
57
  types: "./dist/index.d.ts",
58
58
  scripts: {
59
59
  clean: "rm -rf bin/ dist/",
60
- build: "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
61
- "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
60
+ build: "bun run clean && bun build --external @hasna/contracts/kernel-lock ./src/cli/index.tsx --outdir ./bin --target bun && bun build --external @hasna/contracts/kernel-lock ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
61
+ "build:js": "rm -rf dist && bun build --external @hasna/contracts/kernel-lock ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
62
62
  test: "bun test --timeout 30000",
63
63
  dev: "bun run ./src/cli/index.tsx",
64
64
  "dev:watch": "bun --watch run ./src/cli/index.tsx",
@@ -96,7 +96,6 @@ var package_default = {
96
96
  author: "Hasna",
97
97
  license: "Apache-2.0",
98
98
  devDependencies: {
99
- "@hasna/contracts": "1.1.0",
100
99
  "@types/bun": "1.3.14",
101
100
  "@types/node": "25.2.3",
102
101
  "@types/react": "^18.2.0",
@@ -118,7 +117,8 @@ var package_default = {
118
117
  "ink-spinner": "^5.0.0",
119
118
  "ink-text-input": "^6.0.0",
120
119
  react: "^18.2.0",
121
- zod: "^4.3.6"
120
+ zod: "^4.3.6",
121
+ "@hasna/contracts": "1.3.5"
122
122
  },
123
123
  engines: {
124
124
  bun: ">=1.0.0"