@hasna/skills 0.10.73 → 0.10.75

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 CHANGED
@@ -36888,7 +36888,7 @@ var package_default;
36888
36888
  var init_package = __esm(() => {
36889
36889
  package_default = {
36890
36890
  name: "@hasna/skills",
36891
- version: "0.10.73",
36891
+ version: "0.10.75",
36892
36892
  description: "Skills library for AI coding agents",
36893
36893
  type: "module",
36894
36894
  bin: {
@@ -39496,8 +39496,8 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
39496
39496
  displayName: manifest.displayName ?? displayName(manifest.name),
39497
39497
  category: manifest.category ?? "Development Tools",
39498
39498
  tags: manifest.tags?.length ? manifest.tags : ["custom", manifest.name],
39499
- inputs: [],
39500
- commands: []
39499
+ inputs: Array.isArray(manifest.inputs) ? manifest.inputs : [],
39500
+ commands: Array.isArray(manifest.commands) ? manifest.commands : []
39501
39501
  };
39502
39502
  if (!existsSync10(join10(skillPath, "SKILL.md"))) {
39503
39503
  writeFileSync2(join10(skillPath, "SKILL.md"), renderSkillMd(next));
@@ -58192,11 +58192,19 @@ function planAgentIntegration(options) {
58192
58192
  const executable2 = discoveryExecutable(agent) ?? (prior ? recordedDiscoveryExecutable(agent, prior) : null);
58193
58193
  executables.set(agent, executable2);
58194
58194
  const retainedReview = options.discoveryInputs === undefined && policy.bridge?.discovery?.[agent]?.method === "reviewed" ? { ...policy.bridge.discovery[agent], ...agent === "codex" ? { codexDisabledPluginSkills: policy.bridge.codexPluginSkills ?? [] } : {} } : undefined;
58195
- if (retainedReview) {
58196
- assertManagedAgentBridge(agent, { home, dataDir: dataDir3, projectDir: options.projectDir ?? home });
58197
- retainedAgents.push(agent);
58195
+ try {
58196
+ if (retainedReview) {
58197
+ assertRetainedReviewState(agent, { home, dataDir: dataDir3, projectDir: options.projectDir ?? home });
58198
+ retainedAgents.push(agent);
58199
+ }
58200
+ return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
58201
+ } catch (error) {
58202
+ if (isDiscoveryRootUnresolved(error) || error instanceof SumiPathResolverError || error instanceof NativeSkillCopiesDriftError)
58203
+ throw error;
58204
+ if (!retainedReview)
58205
+ throw error;
58206
+ throw new Error(`NATIVE_SKILL_DRIFT: ${error.message.replace(/^NATIVE_SKILL_DRIFT: /, "")}. This agent reuses a saved discovery review and that review no longer verifies, so a fresh explicit review is required: skills hook install --agent ${agent} --discovery-inputs <review> --apply.`);
58198
58207
  }
58199
- return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases), executable: executable2 });
58200
58208
  });
58201
58209
  const codexConfigBefore = options.agents.includes("codex") ? readOptional(canonicalAgentPath(join34(home, ".codex", "config.toml"), aliases)) : null;
58202
58210
  const codexConfig = Bun.TOML.parse(codexConfigBefore ?? "");
@@ -58867,7 +58875,7 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
58867
58875
  assertCurrent();
58868
58876
  recheckRootAliases(aliases);
58869
58877
  for (const agent of plan.retainedReviewChecks?.agents ?? [])
58870
- assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
58878
+ assertRetainedReviewState(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
58871
58879
  if (plan.reviewArtifactMigration) {
58872
58880
  const verified = planReviewedArtifactMigration(plan.reviewArtifactMigration);
58873
58881
  if (JSON.stringify(verified.changes) !== JSON.stringify(plan.changes) || JSON.stringify(verified.discoveryAfter) !== JSON.stringify(plan.discoveryAfter))
@@ -58910,6 +58918,8 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
58910
58918
  if (!plan.changes.length) {
58911
58919
  assertCurrent();
58912
58920
  recheckRootAliases(aliases);
58921
+ for (const agent of plan.retainedReviewChecks?.agents ?? [])
58922
+ assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
58913
58923
  return { changed: [], backups, ...aliases.length ? { rootAliases: aliases } : {} };
58914
58924
  }
58915
58925
  assertSafePath(backupRoot);
@@ -58959,6 +58969,8 @@ function applyAgentIntegrationUnlocked(plan, options = {}, assertCurrent) {
58959
58969
  }
58960
58970
  for (const agent of plan.managedAgentChecks?.agents ?? [])
58961
58971
  assertManagedAgentBridge(agent, { home: plan.managedAgentChecks.home, dataDir: plan.dataDir, projectDir: plan.managedAgentChecks.projectDir ?? plan.managedAgentChecks.home });
58972
+ for (const agent of plan.retainedReviewChecks?.agents ?? [])
58973
+ assertManagedAgentBridge(agent, { ...plan.retainedReviewChecks, dataDir: plan.dataDir });
58962
58974
  atomicWrite2(join34(backupRoot, "receipt.json"), JSON.stringify({ version: 1, changes: written.map((change) => ({ path: change.path, beforeHash: change.before === null ? null : sha4(change.before), afterHash: sha4(change.after) })), backups, ...plan.cacheWitnessUpgrade ? { cacheWitnessUpgrade: plan.cacheWitnessUpgrade } : {}, ...plan.retainedReviewArtifacts?.length ? { retainedReviewArtifacts: plan.retainedReviewArtifacts.map((item) => ({ path: item.path, ...item.artifact })) } : {}, ...aliases.length ? { rootAliases: aliases } : {} }) + `
58963
58975
  `);
58964
58976
  } catch (error) {
@@ -59299,7 +59311,7 @@ function codexPromptDriftWarning(report) {
59299
59311
  function assertManagedAgentBridge(agent, options = {}) {
59300
59312
  return assertManagedAgentBridgeWithDiscovery(agent, options);
59301
59313
  }
59302
- function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
59314
+ function assertManagedBridgeCustody(agent, options = {}, provenDiscovery) {
59303
59315
  const home = resolve24(options.home ?? homedir3()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
59304
59316
  assertSafePath(join34(dataDir3, "agent-policy.json"));
59305
59317
  const snapshot2 = readManagedSkillPolicySnapshot(dataDir3);
@@ -59316,7 +59328,6 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
59316
59328
  if (!isOwnedCliBridge(expected, [expected]))
59317
59329
  throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
59318
59330
  const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
59319
- const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep13}.agents${sep13}skills${sep13}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
59320
59331
  const discovery = provenDiscovery ?? binding.discovery?.[agent];
59321
59332
  assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases), discovery);
59322
59333
  for (const source of discovery?.sources ?? [])
@@ -59331,11 +59342,27 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
59331
59342
  throw new Error("NATIVE_SKILL_DRIFT: the native hook command/profile binding is missing");
59332
59343
  if (options.profileId !== undefined && options.profileId !== profile)
59333
59344
  throw new Error("NATIVE_SKILL_DRIFT: the hook selection profile differs from its managed binding; restart the native client after reviewing skills hook install");
59345
+ return { home, dataDir: dataDir3, binding, aliases, expected, roots, discovery, configPath, config, command, profile };
59346
+ }
59347
+ function assertUnrepairableBridgeProtection(custody, agent) {
59348
+ if (agent !== "hermes")
59349
+ return;
59350
+ const { home, dataDir: dataDir3, binding, config, command, profile } = custody;
59351
+ const supervisor = binding.supervisors?.hermes;
59352
+ if (supervisor?.path !== join34(dataDir3, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
59353
+ throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
59354
+ assertHermesProtection(home, config, command, profile, supervisor);
59355
+ }
59356
+ function assertRetainedReviewState(agent, options) {
59357
+ const custody = assertManagedBridgeCustody(agent, options);
59358
+ assertUnrepairableBridgeProtection(custody, agent);
59359
+ assertManagedNativeDiscovery(custody, agent, options);
59360
+ }
59361
+ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscovery) {
59362
+ const custody = assertManagedBridgeCustody(agent, options, provenDiscovery);
59363
+ const { home, aliases, expected, discovery, config, command, profile } = custody;
59334
59364
  if (agent === "hermes") {
59335
- const supervisor = binding.supervisors?.hermes;
59336
- if (supervisor?.path !== join34(dataDir3, "agent-hooks", "hermes.js") || supervisor?.sha256 !== sha4(renderHermesSupervisor(command, profile)))
59337
- throw new Error("NATIVE_SKILL_DRIFT: Hermes supervisor binding changed; run skills hook install");
59338
- assertHermesProtection(home, config, command, profile, supervisor);
59365
+ assertUnrepairableBridgeProtection(custody, agent);
59339
59366
  } else if (agent === "sumi") {
59340
59367
  if (JSON.stringify(config.permissions?.filter((rule) => rule.action === "skill")) !== JSON.stringify(SUMI_SKILL_PERMISSIONS) || !isManagedSumiPlugin(readOptional(join34(sumiConfigDirectory(home, process.env, options.projectDir ?? process.cwd()), "plugins", "skills-cli.js")), command, profile))
59341
59368
  throw new Error("NATIVE_SKILL_DRIFT: Sumi bridge protection changed; run skills hook install");
@@ -59357,6 +59384,21 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
59357
59384
  if (agent === "gemini" && (config.hooksConfig?.enabled === false || config.skills?.enabled !== true || !["antigravity-support", "skill-creator"].every((name) => config.skills?.disabled?.includes(name)) || config.skills?.disabled?.includes(CLI_BRIDGE_NAME)))
59358
59385
  throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
59359
59386
  }
59387
+ if (agent === "codex") {
59388
+ const codexConfig = Bun.TOML.parse(readOptional(canonicalAgentPath(join34(home, ".codex", "config.toml"), aliases)) ?? "");
59389
+ if (codexConfig.skills?.bundled?.enabled !== false)
59390
+ throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
59391
+ const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join34(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
59392
+ if ([...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
59393
+ throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
59394
+ }
59395
+ if (agent === "gemini" && discovery && !discovery.builtinNames?.every((name) => config.skills.disabled.includes(name)))
59396
+ throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
59397
+ return assertManagedNativeDiscovery(custody, agent, options);
59398
+ }
59399
+ function assertManagedNativeDiscovery(custody, agent, options) {
59400
+ const { home, dataDir: dataDir3, binding, aliases, expected, roots, discovery } = custody;
59401
+ const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes", "sumi"].includes(agent) && entry.path.includes(`${sep13}.agents${sep13}skills${sep13}`) || ["opencode", "sumi"].includes(agent) && entry.agent === "claude";
59360
59402
  const codexPath = canonicalAgentPath(join34(home, ".codex", "config.toml"), aliases);
59361
59403
  const vendorCache = canonicalAgentPath(join34(home, ".codex", "plugins", "cache"), aliases);
59362
59404
  const witnessTolerance = agent === "codex" && options.codexPromptHook === true ? {
@@ -59367,11 +59409,6 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
59367
59409
  if (options.codexDiscoveryRecovery && (agent !== "codex" || options.codexDiscoveryRecovery.configPath !== codexPath))
59368
59410
  throw new Error("NATIVE_SKILL_DRIFT: Codex trust recovery names a different configuration root");
59369
59411
  const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
59370
- if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
59371
- throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
59372
- const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join34(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
59373
- if (agent === "codex" && [...setting(expected), ...(codexConfig.skills?.config ?? []).filter((entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME)].some((entry) => entry.enabled === false))
59374
- throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
59375
59412
  if (agent === "codex" && !reviewedCodexPluginCapabilitiesUnchanged(canonicalAgentPath(join34(home, ".codex/plugins/cache"), aliases), binding.codexPluginSkills ?? [], (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)), codexConfig.skills?.config ?? []))
59376
59413
  throw new Error("NATIVE_SKILL_DRIFT: reviewed native plugin capability controls changed; run skills hook install with a fresh discovery review");
59377
59414
  const disabledPlugin = (entry) => {
@@ -59408,8 +59445,6 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
59408
59445
  };
59409
59446
  if (!discovery || discovery.agent !== agent)
59410
59447
  throw new Error("NATIVE_SKILL_DRIFT: native discovery coverage is missing; run skills hook install");
59411
- if (agent === "gemini" && !discovery.builtinNames?.every((name) => config.skills.disabled.includes(name)))
59412
- throw new Error("NATIVE_SKILL_DRIFT: an installed Gemini builtin is not disabled");
59413
59448
  try {
59414
59449
  verifyAgentDiscovery(agent === "codex" ? { ...discovery, codexDisabledPluginSkills: binding.codexPluginSkills ?? [] } : discovery, options.codexDiscoveryRecovery, aliases, witnessTolerance);
59415
59450
  if (agent === "sumi" && discovery.method === "reviewed")
@@ -59464,7 +59499,7 @@ function assertManagedAgentBridgeWithDiscovery(agent, options = {}, provenDiscov
59464
59499
  return false;
59465
59500
  }
59466
59501
  }, read: (path) => new TextDecoder("utf-8", { fatal: true }).decode(readNativeBytes(path, 1024 * 1024)) });
59467
- throw new Error(`NATIVE_SKILL_DRIFT: ${blocking.length} unexpected native skill copies were found: ${nativeSkillPathList(blocking.map((entry) => entry.path))}. ${remediation}${adapterRefusal ? ` Native policy adapter refused: ${adapterRefusal}` : ""}`);
59502
+ throw new NativeSkillCopiesDriftError(`NATIVE_SKILL_DRIFT: ${blocking.length} unexpected native skill copies were found: ${nativeSkillPathList(blocking.map((entry) => entry.path))}. ${remediation}${adapterRefusal ? ` Native policy adapter refused: ${adapterRefusal}` : ""}`);
59468
59503
  }
59469
59504
  recheckRootAliases(aliases);
59470
59505
  const discoveryChanges = (witnessTolerance?.changes ?? []).filter((change, index, all) => all.findIndex((other) => other.kind === change.kind && other.path === change.path) === index);
@@ -59505,7 +59540,7 @@ function assertNativeExportAllowed(dataDir3 = getDataDirReadOnly()) {
59505
59540
  if (requiresCliSkillLoading(dataDir3))
59506
59541
  throw new Error("NATIVE_SKILL_EXPORT_DISABLED: this station loads skills through the Skills CLI; use skills sync --selection-profile <id>");
59507
59542
  }
59508
- var sha4 = (data) => createHash26("sha256").update(data).digest("hex"), HOOK_EVENTS, ROOTS, SKILLS_LOADING_POLICY = "Skills loading policy: discover skills with `skills list` or `skills search`, and read selected instructions with `skills load <slug>`. Use `skills sync` to refresh the shared profile. The only native skill is the owned skills-cli bridge. Create and edit payload skills in the Skills authoring workspace; do not copy payload instructions into native discovery folders. Context loading does not authorize execution; use `skills run` only when the task calls for running a skill.";
59543
+ var sha4 = (data) => createHash26("sha256").update(data).digest("hex"), HOOK_EVENTS, ROOTS, NativeSkillCopiesDriftError, SKILLS_LOADING_POLICY = "Skills loading policy: discover skills with `skills list` or `skills search`, and read selected instructions with `skills load <slug>`. Use `skills sync` to refresh the shared profile. The only native skill is the owned skills-cli bridge. Create and edit payload skills in the Skills authoring workspace; do not copy payload instructions into native discovery folders. Context loading does not authorize execution; use `skills run` only when the task calls for running a skill.";
59509
59544
  var init_agent_integration = __esm(() => {
59510
59545
  init_sumi_settings_witness();
59511
59546
  init_codex_corpus_write();
@@ -59534,6 +59569,8 @@ var init_agent_integration = __esm(() => {
59534
59569
  init_retained_review_artifacts();
59535
59570
  HOOK_EVENTS = ["UserPromptSubmit", "SessionStart", "SubagentStart"];
59536
59571
  ROOTS = NATIVE_SKILL_ROOTS;
59572
+ NativeSkillCopiesDriftError = class NativeSkillCopiesDriftError extends Error {
59573
+ };
59537
59574
  });
59538
59575
 
59539
59576
  // src/lib/home-migration.ts
@@ -60951,6 +60988,118 @@ var init_skillinfo = __esm(() => {
60951
60988
  GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM|REPLICATE|FAL|STABILITY|EXA|FIRECRAWL|TWILIO|SENDGRID|RESEND|SLACK|DISCORD|NOTION|LINEAR|GITHUB|AWS|GOOGLE|CLOUDFLARE|VERCEL|SUPABASE|STRIPE)_[A-Z_]+)\b/g;
60952
60989
  });
60953
60990
 
60991
+ // src/lib/search.ts
60992
+ function editDistance(a, b) {
60993
+ if (a === b)
60994
+ return 0;
60995
+ if (a.length === 0)
60996
+ return b.length;
60997
+ if (b.length === 0)
60998
+ return a.length;
60999
+ const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
61000
+ const curr = new Array(b.length + 1);
61001
+ for (let i = 1;i <= a.length; i++) {
61002
+ curr[0] = i;
61003
+ for (let j = 1;j <= b.length; j++) {
61004
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
61005
+ curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
61006
+ }
61007
+ prev.splice(0, prev.length, ...curr);
61008
+ }
61009
+ return prev[b.length];
61010
+ }
61011
+ function fuzzyMatchScore(word, target) {
61012
+ if (target.includes(word))
61013
+ return 1;
61014
+ const tokens = target.split(/[\s\-_]+/).filter(Boolean);
61015
+ for (const token of tokens) {
61016
+ if (token.startsWith(word))
61017
+ return 0.6;
61018
+ }
61019
+ if (word.length >= 3) {
61020
+ const maxDist = word.length <= 3 ? 1 : 2;
61021
+ for (const token of tokens) {
61022
+ if (Math.abs(token.length - word.length) <= maxDist) {
61023
+ const dist = editDistance(word, token);
61024
+ if (dist <= maxDist)
61025
+ return 0.4;
61026
+ }
61027
+ }
61028
+ }
61029
+ return 0;
61030
+ }
61031
+ function searchSkills(query, registry = loadRegistry()) {
61032
+ const words2 = query.toLowerCase().split(/\s+/).filter(Boolean);
61033
+ if (words2.length === 0)
61034
+ return [];
61035
+ const scored = [];
61036
+ for (const skill of registry) {
61037
+ const nameLower = skill.name.toLowerCase();
61038
+ const displayNameLower = skill.displayName.toLowerCase();
61039
+ const descriptionLower = skill.description.toLowerCase();
61040
+ const tagsLower = skill.tags.map((t2) => t2.toLowerCase());
61041
+ const tagsCombined = tagsLower.join(" ");
61042
+ let score = 0;
61043
+ let allWordsMatch = true;
61044
+ for (const word of words2) {
61045
+ let wordScore = 0;
61046
+ const nameMatch = fuzzyMatchScore(word, nameLower);
61047
+ if (nameMatch > 0)
61048
+ wordScore += 10 * nameMatch;
61049
+ const displayMatch = fuzzyMatchScore(word, displayNameLower);
61050
+ if (displayMatch > 0)
61051
+ wordScore += 7 * displayMatch;
61052
+ const tagMatch = Math.max(...tagsLower.map((t2) => fuzzyMatchScore(word, t2)), fuzzyMatchScore(word, tagsCombined));
61053
+ if (tagMatch > 0)
61054
+ wordScore += 5 * tagMatch;
61055
+ const descMatch = fuzzyMatchScore(word, descriptionLower);
61056
+ if (descMatch > 0)
61057
+ wordScore += 2 * descMatch;
61058
+ if (wordScore === 0) {
61059
+ allWordsMatch = false;
61060
+ break;
61061
+ }
61062
+ score += wordScore;
61063
+ }
61064
+ if (allWordsMatch && score > 0) {
61065
+ scored.push({ skill, score });
61066
+ }
61067
+ }
61068
+ scored.sort((a, b) => b.score - a.score);
61069
+ return scored.map((s) => s.skill);
61070
+ }
61071
+ function findCloseSkillNames(query, names, maxResults = 3) {
61072
+ if (maxResults <= 0)
61073
+ return [];
61074
+ const q = query.trim().toLowerCase();
61075
+ if (q.length === 0)
61076
+ return [];
61077
+ const tokens = q.split("-").filter(Boolean);
61078
+ const ranked = [];
61079
+ if (q.length >= 3) {
61080
+ for (const name of names) {
61081
+ const candidate = name.toLowerCase();
61082
+ const rank = candidate === q ? 0 : candidate.endsWith(`-${q}`) || candidate.startsWith(`${q}-`) ? 1 : tokens.length > 1 && tokens.every((token) => candidate.split("-").includes(token)) ? 2 : undefined;
61083
+ if (rank !== undefined)
61084
+ ranked.push([name, rank]);
61085
+ }
61086
+ }
61087
+ ranked.sort((a, b) => a[1] - b[1] || a[0].length - b[0].length || a[0].localeCompare(b[0]));
61088
+ const found = ranked.slice(0, maxResults).map(([name]) => name);
61089
+ if (found.length === maxResults)
61090
+ return found;
61091
+ const taken = new Set(found);
61092
+ const threshold = Math.max(3, Math.floor(q.length / 2));
61093
+ const byDistance = names.filter((name) => !taken.has(name)).map((name) => ({ name, distance: editDistance(q, name.toLowerCase()) })).filter((entry) => entry.distance <= threshold).sort((a, b) => a.distance - b.distance || a.name.localeCompare(b.name));
61094
+ return [...found, ...byDistance.slice(0, maxResults - found.length).map((entry) => entry.name)];
61095
+ }
61096
+ function findSimilarSkills(query, maxResults = 3, registry = loadRegistry()) {
61097
+ return findCloseSkillNames(query, registry.map((skill) => skill.name), maxResults);
61098
+ }
61099
+ var init_search = __esm(() => {
61100
+ init_registry();
61101
+ });
61102
+
60954
61103
  // src/lib/selected-document.ts
60955
61104
  function readSelectedDocument(entries, file) {
60956
61105
  const entry = file !== undefined ? entries.find((candidate) => candidate.path === file) : DOCUMENT_PATHS.map((path) => entries.find((candidate) => candidate.path === path && candidate.bytes.length > 0)).find(Boolean);
@@ -61071,11 +61220,30 @@ function exactProfileSelection(spec, profile) {
61071
61220
  const at2 = spec.lastIndexOf("@");
61072
61221
  const slug = at2 > 0 ? spec.slice(0, at2) : spec;
61073
61222
  const version = at2 > 0 ? spec.slice(at2 + 1) : undefined;
61074
- const selection = profile.selections.find((entry) => (entry.slug === slug || entry.aliases?.includes(slug)) && (version === undefined || version === entry.version));
61075
- if (!selection || version === "")
61076
- throw new SkillSelectionError("SKILL_NOT_SELECTED", "The requested exact skill version is not selected by this profile or project lock.");
61223
+ const named = profile.selections.filter((entry) => entry.slug === slug || entry.aliases?.includes(slug));
61224
+ if (version === "")
61225
+ throw new SkillSelectionError("SKILL_NOT_SELECTED", emptyVersionRefusal(spec));
61226
+ if (named.length === 0)
61227
+ throw new SkillSelectionError("SKILL_NOT_SELECTED", unknownNameRefusal(slug, profile));
61228
+ const selection = version === undefined ? named[0] : named.find((entry) => entry.version === version);
61229
+ if (!selection)
61230
+ throw new SkillSelectionError("SKILL_NOT_SELECTED", wrongVersionRefusal(slug, version, named));
61077
61231
  return selection;
61078
61232
  }
61233
+ function emptyVersionRefusal(spec) {
61234
+ return `The requested name '${spec}' has an empty version. ${ACCEPTED_FORM}`;
61235
+ }
61236
+ function wrongVersionRefusal(slug, version, named) {
61237
+ const versions = [...new Set(named.map((entry) => entry.version))].sort();
61238
+ const canonical9 = named[0].slug;
61239
+ return `Skill '${slug}' is selected at version '${versions.join("', '")}', but '${version}' was requested. ` + `Accepted form: '${canonical9}@${versions[0]}'.`;
61240
+ }
61241
+ function unknownNameRefusal(slug, profile) {
61242
+ const names = profile.selections.flatMap((entry) => [entry.slug, ...entry.aliases ?? []]);
61243
+ const close = findCloseSkillNames(slug, names);
61244
+ const hint = close.length > 0 ? ` Did you mean: ${close.join(", ")}?` : "";
61245
+ return `Skill '${slug}' is not selected by this Skills profile or project lock.${hint} ` + `Read the accepted names with 'skills search <term>' or 'skills list --all --json'; accept one with 'skills pin <name>'. ${ACCEPTED_FORM}`;
61246
+ }
61079
61247
  async function loadSelectedSkill(spec, profileId, options = {}) {
61080
61248
  const context = await resolveSelectionContext(profileId, options);
61081
61249
  const selection = exactProfileSelection(spec, context.receipt.profile);
@@ -61120,9 +61288,11 @@ async function selectedSkillRequirements(spec, profileId, options = {}) {
61120
61288
  }
61121
61289
  return { ...getSkillRequirementsFromContent(selection.slug, texts, dependencies), selection };
61122
61290
  }
61291
+ var ACCEPTED_FORM = "Accepted form: '<slug>' or '<slug>@<version>'.";
61123
61292
  var init_selection_resolver = __esm(() => {
61124
61293
  init_profile_client();
61125
61294
  init_skillinfo();
61295
+ init_search();
61126
61296
  init_selected_document();
61127
61297
  init_selection_cache();
61128
61298
  });
@@ -62715,6 +62885,9 @@ function verifyAgentDiscovery(binding, codexRecovery, reviewedRootAliases, witne
62715
62885
  function claudeControlSource(source) {
62716
62886
  return CLAUDE_CONTROL_WITNESS_MODES.has(source.hashMode ?? "") || CLAUDE_CONTROL_FILES.has(basename16(source.path)) && !source.path.includes(`${sep14}.claude${sep14}plugins${sep14}cache${sep14}`);
62717
62887
  }
62888
+ function retainedAbsentClaudeRoot(absentRoots, source) {
62889
+ return claudeControlSource(source) ? undefined : retainedAbsentRoot(absentRoots, source);
62890
+ }
62718
62891
  function classifyClaudeDiscoverySources(binding) {
62719
62892
  if (!binding || binding.agent !== "claude" || !Array.isArray(binding.sources))
62720
62893
  throw new Error("Invalid Claude discovery binding");
@@ -62735,7 +62908,7 @@ function classifyClaudeDiscoverySources(binding) {
62735
62908
  };
62736
62909
  const budget2 = discoveryByteBudget();
62737
62910
  return binding.sources.map((source) => {
62738
- const root2 = claudeControlSource(source) ? undefined : retainedAbsentRoot(absentRoots, source);
62911
+ const root2 = retainedAbsentClaudeRoot(absentRoots, source);
62739
62912
  if (root2 !== undefined)
62740
62913
  return { status: "retained-absent", root: root2, witness: retainableAbsentWitness(source) };
62741
62914
  let current;
@@ -63150,7 +63323,10 @@ function resolveAgentDiscovery(options) {
63150
63323
  if (!review.sources.some((source) => source.path === canonical9(configPath)))
63151
63324
  throw new Error("Discovery review must include the agent configuration source");
63152
63325
  const reviewedSources = supersedesSettingsProjection(agent, review.sources, canonical9(configPath)) ? sources.filter((source) => !isSupersededSettingsProjection(agent, canonical9(configPath), source)) : sources;
63326
+ const absentRoots = absentRetiredClaudeRoots(supplied);
63153
63327
  const reviewedSourcesWithManifestProjection = review.sources.map((source) => {
63328
+ if (retainedAbsentClaudeRoot(absentRoots, source) !== undefined)
63329
+ return source;
63154
63330
  if (agent === "claude" && source.format === undefined && source.fields === undefined && source.managedPlugins === undefined && source.reviewArtifact === undefined && [undefined, "bytes", "path-bytes"].includes(source.hashMode) && isClaudePluginCacheOrphanMarker(source.path)) {
63155
63331
  return { path: source.path, hashMode: CLAUDE_ORPHAN_MARKER_MODE, sha256: hashClaudePluginCacheOrphanMarker(source.path) };
63156
63332
  }
@@ -63183,6 +63359,11 @@ function resolveAgentDiscovery(options) {
63183
63359
  });
63184
63360
  if (agent === "claude")
63185
63361
  assertReviewedClaudeCacheClosures(home, config, review, canonical9);
63362
+ for (const root2 of absentRoots) {
63363
+ safe6(root2);
63364
+ if (lstatSync32(root2, { throwIfNoEntry: false }))
63365
+ throw new Error("Retired native materialization changed during discovery review");
63366
+ }
63186
63367
  const combined = [...reviewedSources, ...reviewedSourcesWithManifestProjection];
63187
63368
  const unique = combined.filter((source, index) => combined.findIndex((candidate2) => isDeepStrictEqual13(candidate2, source)) === index);
63188
63369
  return { ...supplied, roots: [...new Set([...roots, ...supplied.roots])].sort(), sources: unique, ...agent === "gemini" ? { builtinNames } : {} };
@@ -63933,95 +64114,6 @@ var init_registry_types = __esm(() => {
63933
64114
  BASIC_SKILL_NAMES = [];
63934
64115
  });
63935
64116
 
63936
- // src/lib/search.ts
63937
- function editDistance(a, b) {
63938
- if (a === b)
63939
- return 0;
63940
- if (a.length === 0)
63941
- return b.length;
63942
- if (b.length === 0)
63943
- return a.length;
63944
- const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
63945
- const curr = new Array(b.length + 1);
63946
- for (let i = 1;i <= a.length; i++) {
63947
- curr[0] = i;
63948
- for (let j = 1;j <= b.length; j++) {
63949
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
63950
- curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
63951
- }
63952
- prev.splice(0, prev.length, ...curr);
63953
- }
63954
- return prev[b.length];
63955
- }
63956
- function fuzzyMatchScore(word, target) {
63957
- if (target.includes(word))
63958
- return 1;
63959
- const tokens = target.split(/[\s\-_]+/).filter(Boolean);
63960
- for (const token of tokens) {
63961
- if (token.startsWith(word))
63962
- return 0.6;
63963
- }
63964
- if (word.length >= 3) {
63965
- const maxDist = word.length <= 3 ? 1 : 2;
63966
- for (const token of tokens) {
63967
- if (Math.abs(token.length - word.length) <= maxDist) {
63968
- const dist = editDistance(word, token);
63969
- if (dist <= maxDist)
63970
- return 0.4;
63971
- }
63972
- }
63973
- }
63974
- return 0;
63975
- }
63976
- function searchSkills(query, registry = loadRegistry()) {
63977
- const words2 = query.toLowerCase().split(/\s+/).filter(Boolean);
63978
- if (words2.length === 0)
63979
- return [];
63980
- const scored = [];
63981
- for (const skill of registry) {
63982
- const nameLower = skill.name.toLowerCase();
63983
- const displayNameLower = skill.displayName.toLowerCase();
63984
- const descriptionLower = skill.description.toLowerCase();
63985
- const tagsLower = skill.tags.map((t2) => t2.toLowerCase());
63986
- const tagsCombined = tagsLower.join(" ");
63987
- let score = 0;
63988
- let allWordsMatch = true;
63989
- for (const word of words2) {
63990
- let wordScore = 0;
63991
- const nameMatch = fuzzyMatchScore(word, nameLower);
63992
- if (nameMatch > 0)
63993
- wordScore += 10 * nameMatch;
63994
- const displayMatch = fuzzyMatchScore(word, displayNameLower);
63995
- if (displayMatch > 0)
63996
- wordScore += 7 * displayMatch;
63997
- const tagMatch = Math.max(...tagsLower.map((t2) => fuzzyMatchScore(word, t2)), fuzzyMatchScore(word, tagsCombined));
63998
- if (tagMatch > 0)
63999
- wordScore += 5 * tagMatch;
64000
- const descMatch = fuzzyMatchScore(word, descriptionLower);
64001
- if (descMatch > 0)
64002
- wordScore += 2 * descMatch;
64003
- if (wordScore === 0) {
64004
- allWordsMatch = false;
64005
- break;
64006
- }
64007
- score += wordScore;
64008
- }
64009
- if (allWordsMatch && score > 0) {
64010
- scored.push({ skill, score });
64011
- }
64012
- }
64013
- scored.sort((a, b) => b.score - a.score);
64014
- return scored.map((s) => s.skill);
64015
- }
64016
- function findSimilarSkills(query, maxResults = 3, registry = loadRegistry()) {
64017
- const q = query.toLowerCase();
64018
- const scored = registry.map((s) => ({ name: s.name, dist: editDistance(q, s.name.toLowerCase()) })).filter((s) => s.dist <= Math.max(3, Math.floor(q.length / 2))).sort((a, b) => a.dist - b.dist);
64019
- return scored.slice(0, maxResults).map((s) => s.name);
64020
- }
64021
- var init_search = __esm(() => {
64022
- init_registry();
64023
- });
64024
-
64025
64117
  // src/lib/registry.ts
64026
64118
  var exports_registry = {};
64027
64119
  __export(exports_registry, {
@@ -88723,6 +88815,24 @@ function optionPrefix(program2, args) {
88723
88815
  return { indices, separator: -1 };
88724
88816
  }
88725
88817
 
88818
+ // src/cli/stray-arguments.ts
88819
+ function permitStrayArguments(command) {
88820
+ selfRefusing.add(command);
88821
+ return command.allowExcessArguments(true);
88822
+ }
88823
+ function rejectStrayArguments(command) {
88824
+ for (const child2 of command.commands) {
88825
+ if (child2.registeredArguments.length === 0 && !selfRefusing.has(child2)) {
88826
+ child2.allowExcessArguments(false);
88827
+ }
88828
+ rejectStrayArguments(child2);
88829
+ }
88830
+ }
88831
+ var selfRefusing;
88832
+ var init_stray_arguments = __esm(() => {
88833
+ selfRefusing = new WeakSet;
88834
+ });
88835
+
88726
88836
  // src/lib/revision.ts
88727
88837
  import { createHash as createHash33 } from "crypto";
88728
88838
  function revisionIdOf(content) {
@@ -90772,7 +90882,13 @@ function handleReadRefusal(error61) {
90772
90882
  async function handleInfoError(name, options, error61) {
90773
90883
  if (isSkillsFleetCredentialError(error61))
90774
90884
  return handleReadRefusal(error61);
90775
- const notFound = await resolveRemoteNotFound(name, options.remote, error61.message);
90885
+ let remote = options.remote === true;
90886
+ if (!remote) {
90887
+ try {
90888
+ remote = (await requireSkillsReadAccess()).mode === "hosted";
90889
+ } catch {}
90890
+ }
90891
+ const notFound = await resolveRemoteNotFound(name, remote, error61.message);
90776
90892
  if (options.json)
90777
90893
  console.log(JSON.stringify(notFound));
90778
90894
  else
@@ -94457,7 +94573,11 @@ var exports_selected_run = {};
94457
94573
  __export(exports_selected_run, {
94458
94574
  resolveSelectedRun: () => resolveSelectedRun,
94459
94575
  prepareSelectedSecretBindings: () => prepareSelectedSecretBindings,
94460
- executeSelectedLocal: () => executeSelectedLocal
94576
+ instructionRunRefusal: () => instructionRunRefusal,
94577
+ instructionEntrypointMismatch: () => instructionEntrypointMismatch,
94578
+ instructionCommandEntry: () => instructionCommandEntry,
94579
+ executeSelectedLocal: () => executeSelectedLocal,
94580
+ declaredOperation: () => declaredOperation
94461
94581
  });
94462
94582
  import { createHash as createHash36 } from "crypto";
94463
94583
  import { spawn as spawn3 } from "child_process";
@@ -94470,12 +94590,51 @@ async function resolveSelectedRun(spec, profileId, options = {}) {
94470
94590
  const entries = await readSelectedEntries(selection2, context, options);
94471
94591
  return { selection: selection2, profileId: context.receipt.profile.profileId, entries, ...describeEntries(entries), cacheDir: selectionCacheRoot(options) };
94472
94592
  }
94593
+ function declaredOperation(manifest) {
94594
+ const entry = instructionCommandEntry(manifest);
94595
+ if (!entry)
94596
+ return null;
94597
+ const runtime = manifest.runtime;
94598
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime))
94599
+ return null;
94600
+ if (runtime.runtime !== "bun" && runtime.runtime !== "node" && runtime.runtime !== "python3")
94601
+ return null;
94602
+ if (instructionEntrypointMismatch(manifest))
94603
+ return null;
94604
+ return { entry };
94605
+ }
94606
+ function instructionCommandEntry(manifest) {
94607
+ const commands = Array.isArray(manifest.commands) ? manifest.commands : [];
94608
+ const command = commands[0];
94609
+ if (!command || typeof command !== "object" || typeof command.entry !== "string" || command.entry.length === 0)
94610
+ return null;
94611
+ return command.entry;
94612
+ }
94613
+ function instructionEntrypointMismatch(manifest) {
94614
+ const entry = instructionCommandEntry(manifest);
94615
+ const declared = manifest.runtime;
94616
+ if (!entry || !declared || typeof declared !== "object" || Array.isArray(declared))
94617
+ return null;
94618
+ const runtimeEntrypoint = declared.entrypoint;
94619
+ return typeof runtimeEntrypoint === "string" && runtimeEntrypoint !== entry ? runtimeEntrypoint : null;
94620
+ }
94621
+ function instructionRunRefusal(manifest, isBundledFile) {
94622
+ const declaredEntry = instructionCommandEntry(manifest);
94623
+ if (declaredEntry && !isBundledFile(declaredEntry))
94624
+ return { code: "LOCAL_ENTRYPOINT_MISSING", message: `This selected instruction skill declares its operation entry as ${declaredEntry}, which is not a regular file in the selected bundle; a different runtime.entrypoint never substitutes for it.` };
94625
+ const mismatch = instructionEntrypointMismatch(manifest);
94626
+ if (mismatch)
94627
+ return { code: "LOCAL_ENTRYPOINT_MISMATCH", message: `This selected instruction skill declares its operation entry as ${declaredEntry}, but its runtime.entrypoint is ${mismatch}; the two must agree.` };
94628
+ if (!declaredOperation(manifest))
94629
+ return { code: "INSTRUCTION_SKILL", message: "This selected skill contains instructions. Use skills load instead of skills run." };
94630
+ return null;
94631
+ }
94473
94632
  async function prepareSelectedSecretBindings(selected, cwd2 = process.cwd()) {
94474
94633
  const entries = await readCachedSelection(selected.selection, { cacheDir: selected.cacheDir });
94475
94634
  if (!entries)
94476
94635
  throw new SkillSelectionError("CACHED_BUNDLE_MISSING", "The selected execution bundle is not cached.");
94477
94636
  const { kind, manifest } = describeEntries(entries);
94478
- if (kind !== "executable")
94637
+ if (kind !== "executable" && !declaredOperation(manifest))
94479
94638
  throw new SkillSelectionError("INSTRUCTION_SKILL", "Instruction skills do not accept execution secret bindings.");
94480
94639
  const names = declaredSecretNames(manifest.runtime?.env);
94481
94640
  if (!names.length)
@@ -94488,8 +94647,12 @@ async function executeSelectedLocal(selected, options = {}) {
94488
94647
  if (!entries)
94489
94648
  throw new SkillSelectionError("CACHED_BUNDLE_MISSING", "The selected local execution bundle is not cached.");
94490
94649
  const { kind, manifest, packageJson } = describeEntries(entries);
94491
- if (kind === "instruction")
94492
- throw new SkillSelectionError("INSTRUCTION_SKILL", "This selected skill contains instructions. Use skills load instead of skills run.");
94650
+ const operation = declaredOperation(manifest);
94651
+ if (kind === "instruction") {
94652
+ const refusal3 = instructionRunRefusal(manifest, (path) => entries.some((entry) => entry.path === path));
94653
+ if (refusal3)
94654
+ throw new SkillSelectionError(refusal3.code, refusal3.message);
94655
+ }
94493
94656
  const runtime = manifest.runtime ?? {};
94494
94657
  if (!runtime || typeof runtime !== "object" || Array.isArray(runtime))
94495
94658
  throw new SkillSelectionError("INVALID_SKILL_MANIFEST", "The selected runtime declaration is invalid.");
@@ -94508,9 +94671,9 @@ async function executeSelectedLocal(selected, options = {}) {
94508
94671
  const executable2 = runtimeName === "bun" ? process.execPath : Bun.which(runtimeName);
94509
94672
  if (!executable2)
94510
94673
  throw new SkillSelectionError("LOCAL_RUNTIME_UNAVAILABLE", "The selected runtime is not installed on this station.");
94511
- const declaredEntry = runtime.entrypoint ?? (typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin && typeof packageJson.bin === "object" ? Object.values(packageJson.bin)[0] : undefined);
94674
+ const declaredEntry = (kind === "instruction" ? operation?.entry : undefined) ?? runtime.entrypoint ?? (typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin && typeof packageJson.bin === "object" ? Object.values(packageJson.bin)[0] : undefined);
94512
94675
  if (typeof declaredEntry !== "string" || !entries.some((entry) => entry.path === declaredEntry))
94513
- throw new SkillSelectionError("LOCAL_ENTRYPOINT_MISSING", "The selected bundle must declare a regular-file entrypoint in its runtime manifest or package bin.");
94676
+ throw new SkillSelectionError("LOCAL_ENTRYPOINT_MISSING", kind === "instruction" ? "The selected instruction skill must declare the operation entrypoint it ships in its manifest, and that declared entry must be a regular file in the selected bundle; a different runtime.entrypoint never substitutes for it." : "The selected bundle must declare a regular-file entrypoint in its runtime manifest or package bin.");
94514
94677
  const args = options.args ?? [];
94515
94678
  if (args.length > 128 || args.some((arg) => typeof arg !== "string" || arg.length > 16384 || arg.includes("\x00")))
94516
94679
  throw new SkillSelectionError("INVALID_RUN_INPUT", "Local skill arguments exceed their limits.");
@@ -94585,6 +94748,7 @@ async function executeSelectedLocal(selected, options = {}) {
94585
94748
  selection: selection2,
94586
94749
  target: "local",
94587
94750
  runtime: runtimeName,
94751
+ entrypoint: declaredEntry,
94588
94752
  inputDigest: `sha256:${createHash36("sha256").update(input2).digest("hex")}`,
94589
94753
  runDirectory,
94590
94754
  ...executionGrant ? { executionGrant } : {},
@@ -113378,13 +113542,18 @@ function registerOperationTools(server) {
113378
113542
  return mcpError("CONFLICTING_EXECUTION_MODES", "Use remote for the configured server catalog and quote, or target for selected execution, not both.");
113379
113543
  if (managed && !remote) {
113380
113544
  try {
113381
- const { resolveSelectedRun: resolveSelectedRun2, executeSelectedLocal: executeSelectedLocal2 } = await Promise.resolve().then(() => (init_selected_run(), exports_selected_run));
113545
+ const { resolveSelectedRun: resolveSelectedRun2, executeSelectedLocal: executeSelectedLocal2, instructionRunRefusal: instructionRunRefusal2 } = await Promise.resolve().then(() => (init_selected_run(), exports_selected_run));
113382
113546
  const resolved = await resolveSelectedRun2(name, selectedProfileId(), { projectDir: process.cwd() });
113383
- if (resolved.kind === "instruction")
113384
- return mcpError("INSTRUCTION_SKILL", "This selected skill contains instructions. Load it with skills load instead of running it.");
113547
+ if (resolved.kind === "instruction") {
113548
+ const refusal3 = instructionRunRefusal2(resolved.manifest, (path) => resolved.entries.some((entry) => entry.path === path));
113549
+ if (refusal3)
113550
+ return mcpError(refusal3.code, refusal3.message);
113551
+ }
113385
113552
  if (files?.length)
113386
113553
  return mcpError("SELECTED_INPUT_REQUIRED", "Selected executions require their declared JSON input; use the CLI cloud execution input contract for files.");
113387
113554
  if (target === "cloud") {
113555
+ if (resolved.kind === "instruction")
113556
+ return mcpError("INSTRUCTION_SKILL", "Cloud execution requires an executable skill version; run the declared operation locally on a managed station, or load the instructions with skills load.");
113388
113557
  if (args?.length || quoteReceipt || maxCredits !== undefined || maxCostCents !== undefined)
113389
113558
  return mcpError("SELECTED_INPUT_REQUIRED", "The selected cloud execution contract accepts declared JSON input, not legacy run arguments or quote receipts.");
113390
113559
  const { CloudExecutionClient: CloudExecutionClient2 } = await Promise.resolve().then(() => (init_cloud_executions(), exports_cloud_executions));
@@ -117170,14 +117339,14 @@ function registerRuntime(parent) {
117170
117339
  const exportsCommand = parent.command("exports").description("Inspect or open skill run exports");
117171
117340
  exportsCommand.command("open").argument("<run-id>", "Run id").option("--json", "Output as JSON", false).description("Open the export directory for a run").action((runId2, options) => handleExportsOpen(runId2, options));
117172
117341
  exportsCommand.command("download").argument("<run-id>", "Remote run id").option("--json", "Output as JSON", false).description("Download remote run artifacts into .skills/exports").action((runId2, options) => handleExportsDownload(runId2, options));
117173
- parent.command("mcp").option("--register <agent>", "Register MCP server with agent").option("--json", "Output registration result as JSON", false).allowExcessArguments(true).description("Start MCP server (stdio) or register with an agent").action(async (options, command) => {
117342
+ permitStrayArguments(parent.command("mcp").option("--register <agent>", "Register MCP server with agent").option("--json", "Output registration result as JSON", false).description("Start MCP server (stdio) or register with an agent").action(async (options, command) => {
117174
117343
  const stray = command.args[0];
117175
117344
  if (stray !== undefined) {
117176
117345
  console.error(source_default.red(`error: unknown argument '${stray}'. 'skills mcp' takes no positional arguments. ` + `Valid forms: 'skills mcp' (start the MCP stdio server), ` + `'skills mcp --register <agent>', 'skills mcp --register all'`));
117177
117346
  process.exit(1);
117178
117347
  }
117179
117348
  await handleMcp(options);
117180
- });
117349
+ }));
117181
117350
  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));
117182
117351
  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 }));
117183
117352
  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("--min-release-age <days>", "Require a positive integer dependency release age in days for an exact-version update").option("--min-release-age-exclude <pattern>", "Exclude matching package identities from the release-age policy (repeatable)", (pattern, prior) => [...prior, pattern], []).option("--reviewed-lock <path>", "Install from this private absolute package-lock.json path without resolving dependency ranges").option("--reviewed-lock-sha256 <sha256>", "Exact reviewed SHA-256 of the supplied package-lock.json").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) => {
@@ -117550,8 +117719,11 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
117550
117719
  projectDir: process.cwd(),
117551
117720
  sessionId: options.session
117552
117721
  }) : undefined;
117553
- if (selected?.kind === "instruction")
117554
- throw new Error("This selected skill contains instructions. Use skills load instead of skills run.");
117722
+ if (selected?.kind === "instruction") {
117723
+ const refusal3 = instructionRunRefusal(selected.manifest, (path) => selected.entries.some((entry) => entry.path === path));
117724
+ if (refusal3)
117725
+ throw new Error(refusal3.message);
117726
+ }
117555
117727
  if (selected) {
117556
117728
  slug2 = selected.selection.slug;
117557
117729
  version2 = selected.selection.version;
@@ -117576,6 +117748,8 @@ async function handleRun(name, args2, options, separatorBeforeSkill) {
117576
117748
  process.exitCode = result4.exitCode;
117577
117749
  return;
117578
117750
  }
117751
+ if (selected?.kind === "instruction")
117752
+ throw new Error("Cloud execution requires an executable skill version; run the declared operation locally on a managed station, or use skills load for the instructions.");
117579
117753
  if (args2.length || options.remote || options.file?.length)
117580
117754
  throw new Error("Cloud execution accepts structured --input only");
117581
117755
  if (!version2 || options.input === undefined)
@@ -118267,6 +118441,7 @@ var init_runtime = __esm(() => {
118267
118441
  init_execution_secrets();
118268
118442
  init_context();
118269
118443
  init_registry();
118444
+ init_stray_arguments();
118270
118445
  init_skillinfo();
118271
118446
  init_skill_contract();
118272
118447
  init_blog_article();
@@ -121866,6 +122041,369 @@ var init_profiles = __esm(() => {
121866
122041
  init_remote_permissions();
121867
122042
  });
121868
122043
 
122044
+ // src/lib/selection-provenance.ts
122045
+ function label(value) {
122046
+ return value !== undefined && SAFE_LABEL2.test(value) ? value : "an unnamed value";
122047
+ }
122048
+ async function attempt2(read3) {
122049
+ try {
122050
+ return { ok: true, value: await read3() };
122051
+ } catch {
122052
+ return { ok: false, code: "READ_REFUSED" };
122053
+ }
122054
+ }
122055
+ function sameRevision(observed, reference) {
122056
+ return observed !== undefined && reference !== undefined && observed === reference;
122057
+ }
122058
+ function sameIdentity2(observed, reference) {
122059
+ if (observed.profileId !== undefined && reference.profileId !== undefined && observed.profileId !== reference.profileId)
122060
+ return false;
122061
+ if (observed.authority !== undefined && reference.authority !== undefined && observed.authority !== reference.authority)
122062
+ return false;
122063
+ if (observed.workspaceId !== undefined && reference.workspaceId !== undefined && observed.workspaceId !== reference.workspaceId)
122064
+ return false;
122065
+ return true;
122066
+ }
122067
+ function supportedIdentityValue(value) {
122068
+ return typeof value === "string" && value.trim() !== "";
122069
+ }
122070
+ function uncomparableFields(observed, reference, required2) {
122071
+ return required2.filter((field3) => !supportedIdentityValue(observed[field3]) || !supportedIdentityValue(reference[field3]));
122072
+ }
122073
+ function supportedIdentityFields(source) {
122074
+ const out = {};
122075
+ for (const field3 of ["profileId", "profileRevision", "authority", "workspaceId"]) {
122076
+ const value = source[field3];
122077
+ if (supportedIdentityValue(value))
122078
+ out[field3] = value;
122079
+ }
122080
+ return out;
122081
+ }
122082
+ function revisionReason(labelText, observed, reference, status) {
122083
+ const seen = label(observed);
122084
+ if (status === "current")
122085
+ return `${labelText} revision ${seen} is the selected revision`;
122086
+ return `${labelText} revision ${seen} is not the selected revision ${label(reference)}`;
122087
+ }
122088
+ async function resolveReference(input3, options) {
122089
+ const fromSnapshot = (reasonCode, because) => {
122090
+ let receipt;
122091
+ try {
122092
+ receipt = readSelectionProfile(input3.profileId, options);
122093
+ } catch {
122094
+ return { profileId: input3.profileId, source: "unavailable", reasonCode: "REFERENCE_UNAVAILABLE", reason: `${because} The locally synced snapshot is unreadable too.`, selections: noSelections };
122095
+ }
122096
+ if (!receipt)
122097
+ return { profileId: input3.profileId, source: "unavailable", reasonCode: "REFERENCE_UNAVAILABLE", reason: `${because} Nothing is synced locally either, so no consumer can be compared.`, selections: noSelections };
122098
+ return {
122099
+ profileId: receipt.profile.profileId,
122100
+ ...supportedIdentityFields({ profileRevision: receipt.profile.profileRevision, authority: receipt.profile.authority, workspaceId: receipt.profile.workspaceId }),
122101
+ selectionCount: receipt.profile.selections.length,
122102
+ observedAt: receipt.verifiedAt,
122103
+ source: "synced-snapshot",
122104
+ reasonCode,
122105
+ reason: `${because} Comparisons use the locally synced snapshot (posted work is not proof of adoption), so every consumer below is reported unverified rather than current.`,
122106
+ selections: receipt.profile.selections
122107
+ };
122108
+ };
122109
+ if (input3.cached)
122110
+ return fromSnapshot("REFERENCE_NOT_AUTHORITY", "--cached was requested, so the authority was not read.");
122111
+ const read3 = options.readSelected ?? (async (profileId) => {
122112
+ const client2 = await createProfileClient();
122113
+ const profile2 = await client2.resolveProfile(profileId);
122114
+ return { profileId: profile2.profileId, profileRevision: profile2.profileRevision, authority: profile2.authority, workspaceId: profile2.workspaceId, selections: profile2.selections };
122115
+ });
122116
+ const attempted = await attempt2(() => read3(input3.profileId));
122117
+ if (!attempted.ok)
122118
+ return fromSnapshot("READ_REFUSED", "The authority did not answer for the selected profile.");
122119
+ const profile = attempted.value;
122120
+ if (!profile?.profileId || !profile.profileRevision || !Array.isArray(profile.selections)) {
122121
+ return { profileId: input3.profileId, source: "unavailable", reasonCode: "REFERENCE_UNAVAILABLE", reason: "The authority returned a selection profile this client does not recognise, so no consumer can be compared.", selections: noSelections };
122122
+ }
122123
+ return {
122124
+ profileId: profile.profileId,
122125
+ profileRevision: profile.profileRevision,
122126
+ selectionCount: profile.selections.length,
122127
+ ...profile.authority === undefined ? {} : { authority: profile.authority },
122128
+ ...profile.workspaceId === undefined ? {} : { workspaceId: profile.workspaceId },
122129
+ source: "authority",
122130
+ reasonCode: "REFERENCE_FROM_AUTHORITY",
122131
+ reason: `Read from the authority for profile ${label(profile.profileId)}.`,
122132
+ selections: profile.selections
122133
+ };
122134
+ }
122135
+ function activationConsumer(input3, reference, options) {
122136
+ const base3 = {
122137
+ kind: "station",
122138
+ source: "local-receipt",
122139
+ id: input3.localStationId,
122140
+ idKind: "local-host",
122141
+ status: "unverified",
122142
+ reasonCode: "NO_LOCAL_RECEIPT",
122143
+ reason: ""
122144
+ };
122145
+ const refuse10 = (reasonCode, reason2) => ({ consumer: { ...base3, reasonCode, reason: reason2 }, compared: noSelections });
122146
+ let receipt;
122147
+ try {
122148
+ receipt = readSelectionProfile(input3.profileId, options);
122149
+ } catch {
122150
+ return refuse10("READ_REFUSED", `This machine's activated snapshot for profile ${label(input3.profileId)} could not be read.`);
122151
+ }
122152
+ if (!receipt)
122153
+ return refuse10("NO_LOCAL_RECEIPT", `This machine holds no activated snapshot for profile ${label(input3.profileId)}.`);
122154
+ const profile = receipt.profile;
122155
+ const observed = {
122156
+ ...base3,
122157
+ ...supportedIdentityFields({ profileId: profile.profileId, profileRevision: profile.profileRevision, authority: profile.authority, workspaceId: profile.workspaceId }),
122158
+ observedAt: receipt.verifiedAt,
122159
+ selectionCount: profile.selections.length
122160
+ };
122161
+ if (!sameIdentity2(observed, reference)) {
122162
+ return { consumer: { ...observed, status: "unverified", reasonCode: "IDENTITY_MISMATCH", reason: `This machine's activated snapshot is not the selected selection's identity: it differs in profile, authority or workspace, so it is not current whatever its revision.` }, compared: noSelections };
122163
+ }
122164
+ if (reference.source !== "authority") {
122165
+ return { consumer: { ...observed, status: "unverified", reasonCode: reference.reasonCode, reason: `${reference.reason} This snapshot alone cannot say whether the machine is current.` }, compared: noSelections };
122166
+ }
122167
+ const missingActivation = uncomparableFields(observed, reference, REQUIRED_IDENTITY["local-receipt"]);
122168
+ if (missingActivation.length > 0) {
122169
+ return { consumer: { ...observed, status: "unverified", reasonCode: "INCOMPLETE_RECEIPT", reason: `This machine's activated snapshot does not record ${missingActivation.join(", ")}, so its identity and revision cannot be compared; it is reported unverified rather than current or behind.` }, compared: noSelections };
122170
+ }
122171
+ const status = sameRevision(profile.profileRevision, reference.profileRevision) ? "current" : "behind";
122172
+ return {
122173
+ consumer: {
122174
+ ...observed,
122175
+ status,
122176
+ reasonCode: status === "current" ? "CURRENT_SCHEMA_MATCH" : "REVISION_BEHIND",
122177
+ reason: status === "current" ? revisionReason("This machine's activated snapshot", profile.profileRevision, reference.profileRevision, "current") : `${revisionReason("This machine's activated snapshot", profile.profileRevision, reference.profileRevision, "behind")}. Run skills sync to activate the selected revision here.`
122178
+ },
122179
+ compared: profile.selections
122180
+ };
122181
+ }
122182
+ async function stationConsumer(input3, reference, options) {
122183
+ const base3 = {
122184
+ kind: "station",
122185
+ source: "authority-receipt",
122186
+ id: input3.stationId,
122187
+ idKind: "requested-station",
122188
+ status: "unverified",
122189
+ reasonCode: "NO_STATION_REPORTED",
122190
+ reason: ""
122191
+ };
122192
+ const refuse10 = (reasonCode, reason2) => ({ consumer: { ...base3, reasonCode, reason: reason2 }, compared: noSelections });
122193
+ if (reference.source !== "authority") {
122194
+ return refuse10("REFERENCE_NOT_AUTHORITY", "The station receipt was not compared: this receipt has no authenticated selection to compare it with.");
122195
+ }
122196
+ const read3 = options.readStation ?? (async (stationId) => {
122197
+ const state = await readStationSkillState(stationId);
122198
+ return { profileId: state.profileId, profileRevision: state.profileRevision, appliedAt: state.appliedAt, workspaceId: state.workspaceId, selections: state.selections };
122199
+ });
122200
+ const attempted = await attempt2(() => read3(input3.stationId));
122201
+ if (!attempted.ok)
122202
+ return refuse10("READ_REFUSED", `The authority did not answer for station ${label(input3.stationId)}.`);
122203
+ const station = attempted.value;
122204
+ const observed = {
122205
+ ...base3,
122206
+ ...supportedIdentityFields({ profileId: station.profileId, profileRevision: station.profileRevision, workspaceId: station.workspaceId }),
122207
+ observedAt: station.appliedAt,
122208
+ selectionCount: station.selections.length
122209
+ };
122210
+ if (!sameIdentity2(observed, reference)) {
122211
+ return { consumer: { ...observed, status: "unverified", reasonCode: "IDENTITY_MISMATCH", reason: `Station ${label(input3.stationId)} last applied a different selection identity: it differs in profile or workspace from the selected profile.` }, compared: noSelections };
122212
+ }
122213
+ const missing2 = uncomparableFields(observed, reference, REQUIRED_IDENTITY["authority-receipt"]);
122214
+ if (missing2.length > 0) {
122215
+ return { consumer: { ...observed, status: "unverified", reasonCode: "INCOMPLETE_RECEIPT", reason: `The receipt the authority holds for station ${label(input3.stationId)} does not record ${missing2.join(", ")}, so its identity and revision cannot be compared; it is reported unverified rather than current or behind.` }, compared: noSelections };
122216
+ }
122217
+ if (sameRevision(station.profileRevision, reference.profileRevision)) {
122218
+ return { consumer: { ...observed, status: "current", reasonCode: "CURRENT_SCHEMA_MATCH", reason: revisionReason(`The receipt the authority holds for station ${label(input3.stationId)}`, station.profileRevision, reference.profileRevision, "current") }, compared: station.selections };
122219
+ }
122220
+ return {
122221
+ consumer: {
122222
+ ...observed,
122223
+ status: "behind",
122224
+ reasonCode: "REVISION_BEHIND",
122225
+ reason: `${revisionReason(`The receipt the authority holds for station ${label(input3.stationId)}`, station.profileRevision, reference.profileRevision, "behind")}. Only an explicit skills sync writes this record, so report it before treating the machine as adrift.`
122226
+ },
122227
+ compared: station.selections
122228
+ };
122229
+ }
122230
+ function sessionConsumer(input3, reference, options) {
122231
+ const unnamed = {
122232
+ kind: "session",
122233
+ source: "session-receipt",
122234
+ id: "unnamed",
122235
+ idKind: "local-host",
122236
+ status: "unverified",
122237
+ reasonCode: "NO_SESSION_NAMED",
122238
+ reason: "No session was named, so its pin and load receipts were not read; pass --session <id>."
122239
+ };
122240
+ if (!input3.sessionId)
122241
+ return { consumer: unnamed, compared: noSelections };
122242
+ const base3 = {
122243
+ kind: "session",
122244
+ source: "session-receipt",
122245
+ id: input3.sessionId,
122246
+ idKind: "local-host",
122247
+ status: "unverified",
122248
+ reasonCode: "NO_SESSION_RECEIPT",
122249
+ reason: ""
122250
+ };
122251
+ const refuse10 = (reasonCode, reason2) => ({ consumer: { ...base3, reasonCode, reason: reason2 }, compared: noSelections });
122252
+ let session;
122253
+ try {
122254
+ session = inspectSkillSession(input3.sessionId, options);
122255
+ } catch {
122256
+ return refuse10("READ_REFUSED", `The session receipt for the named session could not be read.`);
122257
+ }
122258
+ const consumed = session.selections.filter((selection2) => selection2.loaded).map((selection2) => ({ slug: selection2.slug, version: selection2.version, bundleDigest: selection2.bundleDigest }));
122259
+ const observed = {
122260
+ ...base3,
122261
+ ...supportedIdentityFields({ profileId: session.profileId, profileRevision: session.profileRevision, authority: session.authority, workspaceId: session.workspaceId }),
122262
+ observedAt: session.verifiedAt,
122263
+ selectionCount: session.selectionCount,
122264
+ loadedCount: session.loadedCount,
122265
+ consumed
122266
+ };
122267
+ if (!sameIdentity2(observed, reference)) {
122268
+ return { consumer: { ...observed, status: "unverified", reasonCode: "IDENTITY_MISMATCH", reason: "The session pin is not the selected selection's identity: it differs in profile, authority or workspace, so it is not current whatever its revision." }, compared: noSelections };
122269
+ }
122270
+ if (reference.source !== "authority") {
122271
+ return { consumer: { ...observed, status: "unverified", reasonCode: reference.reasonCode, reason: `${reference.reason} This pin alone cannot say whether the session is current.` }, compared: noSelections };
122272
+ }
122273
+ const missingPin = uncomparableFields(observed, reference, REQUIRED_IDENTITY["session-receipt"]);
122274
+ if (missingPin.length > 0) {
122275
+ return { consumer: { ...observed, status: "unverified", reasonCode: "INCOMPLETE_RECEIPT", reason: `The session receipt does not record ${missingPin.join(", ")}, so its identity and revision cannot be compared; it is reported unverified rather than current, pinned or behind.` }, compared: noSelections };
122276
+ }
122277
+ if (sameRevision(session.profileRevision, reference.profileRevision)) {
122278
+ return {
122279
+ consumer: { ...observed, status: "current", reasonCode: "CURRENT_SCHEMA_MATCH", reason: `${revisionReason("The session pin", session.profileRevision, reference.profileRevision, "current")}; it loaded ${session.loadedCount} of ${session.selectionCount} selections.` },
122280
+ compared: consumed
122281
+ };
122282
+ }
122283
+ return {
122284
+ consumer: {
122285
+ ...observed,
122286
+ status: "pinned",
122287
+ reasonCode: "PINNED_BY_DESIGN",
122288
+ reason: `${revisionReason("The session pin", session.profileRevision, reference.profileRevision, "pinned")}. A session keeps its revision when the profile moves on, so this is custody by design; skills sessions reconcile moves it intentionally.`
122289
+ },
122290
+ compared: consumed
122291
+ };
122292
+ }
122293
+ function driftFor(result2, reference) {
122294
+ if (reference.source !== "authority" || result2.compared.length === 0)
122295
+ return [];
122296
+ if (result2.consumer.status === "unverified" && result2.consumer.reasonCode === "IDENTITY_MISMATCH")
122297
+ return [];
122298
+ const selected = new Map(reference.selections.map((selection2) => [selection2.slug, selection2.version]));
122299
+ return result2.compared.filter((selection2) => selected.has(selection2.slug) && selected.get(selection2.slug) !== selection2.version).map((selection2) => ({ source: result2.consumer.source, id: result2.consumer.id, slug: selection2.slug, selectedVersion: selected.get(selection2.slug), observedVersion: selection2.version })).sort((left, right) => left.slug.localeCompare(right.slug) || left.source.localeCompare(right.source));
122300
+ }
122301
+ async function readSelectionProvenance(input3, options = {}) {
122302
+ if (!input3.profileId?.trim())
122303
+ throw new Error("A selection profile id is required to read selection provenance.");
122304
+ if (!input3.stationId?.trim() || input3.stationId.length > LABEL_MAX_CHARS)
122305
+ throw new Error("A station id is required to read selection provenance.");
122306
+ if (!input3.localStationId?.trim() || input3.localStationId.length > LABEL_MAX_CHARS)
122307
+ throw new Error("The local host id is required to read selection provenance.");
122308
+ const reference = await resolveReference(input3, options);
122309
+ const results2 = [activationConsumer(input3, reference, options), await stationConsumer(input3, reference, options), sessionConsumer(input3, reference, options)];
122310
+ const drift = results2.flatMap((result2) => driftFor(result2, reference));
122311
+ return {
122312
+ schemaVersion: 1,
122313
+ readOnly: true,
122314
+ selectionProfile: input3.profileId,
122315
+ selected: {
122316
+ profileId: reference.profileId,
122317
+ ...supportedIdentityValue(reference.profileRevision) ? { profileRevision: reference.profileRevision, selectionCount: reference.selectionCount } : {},
122318
+ ...supportedIdentityValue(reference.authority) ? { authority: reference.authority } : {},
122319
+ ...supportedIdentityValue(reference.workspaceId) ? { workspaceId: reference.workspaceId } : {},
122320
+ ...reference.observedAt === undefined ? {} : { observedAt: reference.observedAt },
122321
+ source: reference.source,
122322
+ reasonCode: reference.reasonCode,
122323
+ reason: reference.reason
122324
+ },
122325
+ consumers: results2.map((result2) => result2.consumer),
122326
+ drift: drift.slice(0, PROVENANCE_DRIFT_LIMIT),
122327
+ driftTruncated: drift.length > PROVENANCE_DRIFT_LIMIT,
122328
+ notAttested: [
122329
+ "Published version and digest evidence for skill payloads: read it with skills versions and skills review-export.",
122330
+ "Native discovery, native locks and project locks: read them with skills hook check and skills sessions recover-lock.",
122331
+ "Live consumer adoption: a receipt shows what a session loaded, never that an agent honoured it."
122332
+ ]
122333
+ };
122334
+ }
122335
+ var PROVENANCE_DRIFT_LIMIT = 200, SAFE_LABEL2, LABEL_MAX_CHARS = 200, noSelections, REQUIRED_IDENTITY;
122336
+ var init_selection_provenance = __esm(() => {
122337
+ init_session_reconciliation();
122338
+ init_profile_admin();
122339
+ init_profile_client();
122340
+ init_selection_cache();
122341
+ SAFE_LABEL2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
122342
+ noSelections = [];
122343
+ REQUIRED_IDENTITY = {
122344
+ "local-receipt": ["profileId", "profileRevision", "authority", "workspaceId"],
122345
+ "session-receipt": ["profileId", "profileRevision", "authority", "workspaceId"],
122346
+ "authority-receipt": ["profileId", "profileRevision", "workspaceId"]
122347
+ };
122348
+ });
122349
+
122350
+ // src/cli/commands/provenance.ts
122351
+ var exports_provenance = {};
122352
+ __export(exports_provenance, {
122353
+ registerProvenance: () => registerProvenance,
122354
+ provenanceLines: () => provenanceLines
122355
+ });
122356
+ import { hostname as hostname5 } from "os";
122357
+ function provenanceLines(receipt) {
122358
+ const selected = receipt.selected;
122359
+ const lines = [
122360
+ `Selection profile ${receipt.selectionProfile}`,
122361
+ `Selected: ${selected.profileRevision ?? "unavailable"} (${selected.selectionCount ?? 0} selections, ${selected.source})`
122362
+ ];
122363
+ for (const consumer of receipt.consumers)
122364
+ lines.push(...consumerLines(consumer));
122365
+ if (receipt.drift.length > 0) {
122366
+ lines.push(`Drift (${receipt.drift.length}${receipt.driftTruncated ? ", truncated" : ""}):`);
122367
+ for (const row2 of receipt.drift)
122368
+ lines.push(`- ${row2.source} ${row2.id}: ${row2.slug} ${row2.selectedVersion} -> ${row2.observedVersion}`);
122369
+ }
122370
+ return lines;
122371
+ }
122372
+ function consumerLines(consumer) {
122373
+ const label2 = consumer.kind === "session" ? "Pinned" : consumer.source === "local-receipt" ? "Activated" : "Applied";
122374
+ const identity6 = consumer.kind === "session" ? `session ${consumer.id}` : consumer.idKind === "local-host" ? `this machine (${consumer.id})` : `station ${consumer.id}`;
122375
+ const lines = [`${label2}: ${identity6} ${consumer.profileRevision ?? "no receipt"} [${consumer.status}] ${consumer.reasonCode}`, ` ${consumer.reason}`];
122376
+ if (consumer.kind === "session" && consumer.consumed) {
122377
+ lines.push(`Consumed: ${consumer.loadedCount ?? 0} of ${consumer.selectionCount ?? 0} selections loaded`);
122378
+ for (const selection2 of consumer.consumed)
122379
+ lines.push(`- ${selection2.slug}@${selection2.version} ${selection2.bundleDigest}`);
122380
+ }
122381
+ return lines;
122382
+ }
122383
+ function registerProvenance(parent) {
122384
+ parent.command("provenance").description("Read-only receipt of the selected, activated, applied, pinned and actually loaded skill versions").option("--selection-profile <id>", "Selection profile to read (default: the managed profile)").option("--station <id>", "Station whose sync receipt to read (default: HASNA_STATION, then this host)").option("--session <id>", "Also report one session's pin and the versions it actually loaded").option("--cached", "Read the reference from the locally synced snapshot and never ask the authority", false).option("--json", "Return the receipt as JSON", false).action(async (options) => {
122385
+ try {
122386
+ const localStationId = process.env.HASNA_STATION ?? hostname5();
122387
+ const receipt = await readSelectionProvenance({
122388
+ profileId: selectedProfileId(options.selectionProfile),
122389
+ localStationId,
122390
+ stationId: options.station ?? localStationId,
122391
+ ...options.session === undefined ? {} : { sessionId: options.session },
122392
+ ...options.cached ? { cached: true } : {}
122393
+ });
122394
+ await writeCliOutput(options.json ? JSON.stringify(receipt) : provenanceLines(receipt).join(`
122395
+ `));
122396
+ } catch (error61) {
122397
+ console.error(error61.message);
122398
+ process.exitCode = 1;
122399
+ }
122400
+ });
122401
+ }
122402
+ var init_provenance = __esm(() => {
122403
+ init_selection_provenance();
122404
+ init_context();
122405
+ });
122406
+
121869
122407
  // src/cli/commands/grants.ts
121870
122408
  var exports_grants = {};
121871
122409
  __export(exports_grants, {
@@ -123312,7 +123850,7 @@ __export(exports_publish, {
123312
123850
  });
123313
123851
  import { execFileSync as execFileSync3 } from "child_process";
123314
123852
  import { existsSync as existsSync39, readFileSync as readFileSync52 } from "fs";
123315
- import { hostname as hostname5 } from "os";
123853
+ import { hostname as hostname6 } from "os";
123316
123854
  import { join as join79 } from "path";
123317
123855
  function registerPublish(parent) {
123318
123856
  parent.command("push").argument("<name>", "Name of a skill in the local corpus (~/.hasna/skills/installed or the migrated ~/.hasna/skills/skills)").option("--version <version>", "Override the version recorded on the instance").option("--force-new-version", "If name@version already exists with different content, publish as the next patch version", false).option("--dry-run", "Pack and validate without uploading", false).option("--json", "Output result as JSON", false).description("Publish a local skill to the configured skills instance").action(async (name, options) => {
@@ -123496,7 +124034,7 @@ function buildVersionManifest(skillDir, packed) {
123496
124034
  unpackedByteSize: packed.unpackedByteSize,
123497
124035
  bundleSha256: packed.sha256,
123498
124036
  provenance: {
123499
- machine: hostname5(),
124037
+ machine: hostname6(),
123500
124038
  agent: process.env.SKILLS_AGENT_ID ?? process.env.HASNA_AGENT_ID ?? process.env.AGENT_ID ?? null,
123501
124039
  cliVersion: package_default.version,
123502
124040
  gitRemote: sanitizeGitRemote(gitValue(skillDir, ["remote", "get-url", "origin"])),
@@ -123875,9 +124413,9 @@ function printWhoami(payload) {
123875
124413
  console.log(source_default.dim(`Auth: ${payload.authSource}`));
123876
124414
  const permissions = recordField(payload.permissions);
123877
124415
  if (permissions) {
123878
- const label = (value) => value === true ? "allowed" : value === false ? "denied" : "unknown";
123879
- console.log(`Publish: ${label(permissions.publish)}`);
123880
- console.log(`Profiles write: ${label(permissions.profilesWrite)}`);
124416
+ const label2 = (value) => value === true ? "allowed" : value === false ? "denied" : "unknown";
124417
+ console.log(`Publish: ${label2(permissions.publish)}`);
124418
+ console.log(`Profiles write: ${label2(permissions.profilesWrite)}`);
123881
124419
  }
123882
124420
  if (payload.offline)
123883
124421
  console.log(source_default.dim("(offline \u2014 showing cached info)"));
@@ -124503,7 +125041,7 @@ function promptInvitationRecoveryCode() {
124503
125041
  }
124504
125042
  function promptInvitationProof(kind) {
124505
125043
  const length = kind === "token" ? 43 : 6;
124506
- const label = kind === "token" ? "invitation token" : "recovery code";
125044
+ const label2 = kind === "token" ? "invitation token" : "recovery code";
124507
125045
  const character = kind === "token" ? /^[A-Za-z0-9_-]$/ : /^\d$/;
124508
125046
  const { stdin, stderr: output2 } = process, wasRaw = stdin.isRaw, wasFlowing = stdin.readableFlowing;
124509
125047
  return new Promise((resolve49) => {
@@ -124534,13 +125072,13 @@ function promptInvitationProof(kind) {
124534
125072
  value = "";
124535
125073
  overflow = false;
124536
125074
  output2.write(`
124537
- Enter exactly ${length} ${label} characters: `);
125075
+ Enter exactly ${length} ${label2} characters: `);
124538
125076
  return;
124539
125077
  }
124540
125078
  if (value.length === length)
124541
125079
  return finish(value);
124542
125080
  output2.write(`
124543
- Enter the complete ${label}: `);
125081
+ Enter the complete ${label2}: `);
124544
125082
  value = "";
124545
125083
  return;
124546
125084
  }
@@ -124555,7 +125093,7 @@ Enter the complete ${label}: `);
124555
125093
  if (value.length === length) {
124556
125094
  overflow = true;
124557
125095
  output2.write(`
124558
- The ${label} is too long. Press Enter to start again.`);
125096
+ The ${label2} is too long. Press Enter to start again.`);
124559
125097
  } else {
124560
125098
  value += text4;
124561
125099
  output2.write("*");
@@ -124563,7 +125101,7 @@ The ${label} is too long. Press Enter to start again.`);
124563
125101
  } else if (text4 && !key2.ctrl && !key2.meta) {
124564
125102
  overflow = true;
124565
125103
  output2.write(`
124566
- The ${label} contains invalid characters. Press Enter to start again.`);
125104
+ The ${label2} contains invalid characters. Press Enter to start again.`);
124567
125105
  }
124568
125106
  };
124569
125107
  const timer = setTimeout(cancel, 5 * 60 * 1000);
@@ -133645,6 +134183,7 @@ init_registry();
133645
134183
  init_discovery();
133646
134184
  init_fleet_credentials();
133647
134185
  init_read_access();
134186
+ init_stray_arguments();
133648
134187
  var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
133649
134188
  var isTTY2 = (process.stdout.isTTY ?? false) && (process.stdin.isTTY ?? false);
133650
134189
  var program2 = new Command;
@@ -133654,7 +134193,7 @@ program2.hook("preAction", () => {
133654
134193
  recordEnvironmentProfile(process.env.HASNA_PROFILE);
133655
134194
  selectCliSkillsCredentialProfile(profile);
133656
134195
  });
133657
- program2.command("interactive", { isDefault: true }).alias("i").allowExcessArguments(true).description("Interactive skill browser (TUI)").action(async (_options, command) => {
134196
+ permitStrayArguments(program2.command("interactive", { isDefault: true }).alias("i").description("Interactive skill browser (TUI)").action(async (_options, command) => {
133658
134197
  const stray = command.args[0];
133659
134198
  if (stray !== undefined) {
133660
134199
  const verbs = program2.commands.map((c) => c.name()).filter((n2) => n2 !== "interactive").sort();
@@ -133681,7 +134220,7 @@ program2.command("interactive", { isDefault: true }).alias("i").allowExcessArgum
133681
134220
  render_default(/* @__PURE__ */ jsx_dev_runtime8.jsxDEV(App2, {
133682
134221
  initialAccess: access
133683
134222
  }, undefined, false, undefined, this));
133684
- });
134223
+ }));
133685
134224
  var { registerInstall: registerInstall2 } = await Promise.resolve().then(() => (init_install(), exports_install));
133686
134225
  registerInstall2(program2);
133687
134226
  var { registerBrowse: registerBrowse2 } = await Promise.resolve().then(() => (init_list(), exports_list));
@@ -133712,6 +134251,8 @@ var { registerPluginAdmission: registerPluginAdmission2 } = await Promise.resolv
133712
134251
  registerPluginAdmission2(program2);
133713
134252
  var { registerProfiles: registerProfiles2 } = await Promise.resolve().then(() => (init_profiles(), exports_profiles));
133714
134253
  registerProfiles2(program2);
134254
+ var { registerProvenance: registerProvenance2 } = await Promise.resolve().then(() => (init_provenance(), exports_provenance));
134255
+ registerProvenance2(program2);
133715
134256
  var { registerGrants: registerGrants2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
133716
134257
  registerGrants2(program2);
133717
134258
  var { registerHydrate: registerHydrate2 } = await Promise.resolve().then(() => (init_hydrate(), exports_hydrate));
@@ -133741,6 +134282,7 @@ registerStorage2(program2);
133741
134282
  var { registerRegistryReconcile: registerRegistryReconcile2 } = await Promise.resolve().then(() => (init_registry_reconcile2(), exports_registry_reconcile));
133742
134283
  registerRegistryReconcile2(program2);
133743
134284
  registerEventsCommands(program2, { source: "skills" });
134285
+ rejectStrayArguments(program2);
133744
134286
  var cliArgs = process.argv.slice(2);
133745
134287
  var colorFlag = optionPrefix(program2, cliArgs).indices.find((index) => cliArgs[index] === "--no-color");
133746
134288
  if (colorFlag !== undefined) {