@hasna/skills 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/bin/index.js +129 -47
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/commands/context.d.ts +1 -0
- package/dist/index.js +28 -11
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/session-reconciliation.d.ts +7 -1
- package/dist/sdk/index.js +28 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -453,17 +453,22 @@ during installation. A successful update does not reload an existing session.
|
|
|
453
453
|
At session start, the hook authenticates and refreshes the profile. Prompt hooks
|
|
454
454
|
select complete skill instructions from that verified cache using explicit
|
|
455
455
|
`$skill` references, profile keywords, paths and always-required selections.
|
|
456
|
-
A session retains its
|
|
456
|
+
A session retains its loaded selections; compaction restores loaded instructions,
|
|
457
457
|
and subagents inherit the parent's selection. Instructions that exceed the
|
|
458
458
|
context budget produce an explicit `skills load` command. A hook never executes
|
|
459
|
-
a skill. Cached use is explicit and expires after 24 hours. When
|
|
460
|
-
expires
|
|
461
|
-
|
|
462
|
-
receipt
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
459
|
+
a skill. Cached use is explicit and expires after 24 hours. When a session
|
|
460
|
+
receipt expires, the hook authenticates the current profile. If every loaded
|
|
461
|
+
selection still has identical metadata and bundle digest, it archives the old
|
|
462
|
+
receipt and advances that session to the current profile under an exact receipt
|
|
463
|
+
precondition. Unloaded selections may be added or removed. If a loaded selection
|
|
464
|
+
changed or disappeared, the hook blocks and requires the explicit reconciliation
|
|
465
|
+
below. A missing cache may be recovered through authenticated context.
|
|
466
|
+
Authentication, integrity and native discovery failures still block; they do
|
|
467
|
+
not silently switch to a local catalog.
|
|
468
|
+
|
|
469
|
+
An intentional change to a loaded selection or profile identity for an already
|
|
470
|
+
running session uses a separate reviewed operation. `sync` and hook
|
|
471
|
+
reinstallation do not migrate session pins:
|
|
467
472
|
|
|
468
473
|
```bash
|
|
469
474
|
skills sessions show <session-id> --json
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.10.
|
|
36871
|
+
version: "0.10.4",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51164,7 +51164,7 @@ function parseNativeMigrationTargetManifest(input) {
|
|
|
51164
51164
|
if (!target || typeof target !== "object" || Array.isArray(target))
|
|
51165
51165
|
throw new Error("Invalid native migration target");
|
|
51166
51166
|
const item = target;
|
|
51167
|
-
if (Object.keys(item).some((key) => !["agent", "projectRoot", "path", "treeSha256"].includes(key)) || typeof item.agent !== "string" || typeof item.projectRoot !== "string" || typeof item.path !== "string" || typeof item.treeSha256 !== "string")
|
|
51167
|
+
if (Object.keys(item).some((key) => !["agent", "projectRoot", "path", "treeSha256", "vendor"].includes(key)) || typeof item.agent !== "string" || typeof item.projectRoot !== "string" || typeof item.path !== "string" || typeof item.treeSha256 !== "string" || item.vendor !== undefined && item.vendor !== true)
|
|
51168
51168
|
throw new Error("Invalid native migration target fields");
|
|
51169
51169
|
if (!ROOTS.some(([agent]) => agent === item.agent))
|
|
51170
51170
|
throw new Error(`Unsupported native migration target agent: ${item.agent}`);
|
|
@@ -51183,7 +51183,7 @@ function parseNativeMigrationTargetManifest(input) {
|
|
|
51183
51183
|
if (identities.has(identity4))
|
|
51184
51184
|
throw new Error(`Duplicate native migration target: ${item.path}`);
|
|
51185
51185
|
identities.add(identity4);
|
|
51186
|
-
targets.push({ agent: item.agent, projectRoot, path: normalized, treeSha256: item.treeSha256 });
|
|
51186
|
+
targets.push({ agent: item.agent, projectRoot, path: normalized, treeSha256: item.treeSha256, ...item.vendor === true ? { vendor: true } : {} });
|
|
51187
51187
|
}
|
|
51188
51188
|
return { schema: "hasna.skills-native-migration-targets.v1", targets, digest };
|
|
51189
51189
|
}
|
|
@@ -51211,8 +51211,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
51211
51211
|
if (matches2.length !== 1)
|
|
51212
51212
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
51213
51213
|
const entry = matches2[0];
|
|
51214
|
-
if (entry.bridge || entry.system || entry.vendor)
|
|
51215
|
-
throw new Error(`Native migration target is protected or vendor
|
|
51214
|
+
if (entry.bridge || entry.system || entry.vendor !== (target.vendor === true))
|
|
51215
|
+
throw new Error(`Native migration target is protected or vendor classification changed: ${entry.path}`);
|
|
51216
51216
|
if (entry.hash !== target.treeSha256 || treeHash(entry.path) !== target.treeSha256)
|
|
51217
51217
|
throw new Error(`Native migration target changed after review: ${entry.path}`);
|
|
51218
51218
|
selected.push(entry);
|
|
@@ -51235,6 +51235,8 @@ function projectAncestorDirectories(projects) {
|
|
|
51235
51235
|
return [...directories];
|
|
51236
51236
|
}
|
|
51237
51237
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
51238
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute9(options.reviewedCacheAlias) || resolve10(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
51239
|
+
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
51238
51240
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
51239
51241
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
51240
51242
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
@@ -51255,14 +51257,22 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
51255
51257
|
if (discoveryPathBytes > 4 * 1024 * 1024)
|
|
51256
51258
|
throw new Error("Native skill discovery metadata limit exceeded");
|
|
51257
51259
|
}
|
|
51258
|
-
|
|
51260
|
+
let reviewedCacheAliasFound = false;
|
|
51261
|
+
function verifiedSiblingCacheAlias(path, parent) {
|
|
51259
51262
|
const link2 = readlinkSync3(path), target = resolve10(parent, link2);
|
|
51260
51263
|
if (dirname10(target) !== parent || (isAbsolute9(link2) ? link2 !== target : dirname10(link2) !== "."))
|
|
51261
51264
|
return false;
|
|
51262
51265
|
if (!lstatSync14(target, { throwIfNoEntry: false })?.isDirectory())
|
|
51263
51266
|
return false;
|
|
51264
51267
|
const scan = cacheScans.get(target);
|
|
51265
|
-
|
|
51268
|
+
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
51269
|
+
return false;
|
|
51270
|
+
if (scan.hasSkills) {
|
|
51271
|
+
if (path !== options.reviewedCacheAlias)
|
|
51272
|
+
return false;
|
|
51273
|
+
reviewedCacheAliasFound = true;
|
|
51274
|
+
}
|
|
51275
|
+
return true;
|
|
51266
51276
|
}
|
|
51267
51277
|
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
51268
51278
|
if (!admitted)
|
|
@@ -51319,7 +51329,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
51319
51329
|
const child = join20(path, name);
|
|
51320
51330
|
if (isVendor && lstatSync14(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
51321
51331
|
const target = statSync7(child, { throwIfNoEntry: false });
|
|
51322
|
-
if (target?.isFile() || pluginCache && target?.isDirectory() &&
|
|
51332
|
+
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(child, path)) {
|
|
51323
51333
|
scan.entries++;
|
|
51324
51334
|
continue;
|
|
51325
51335
|
}
|
|
@@ -51353,6 +51363,8 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
51353
51363
|
for (const root of [...options.agentRoots ?? [], ...configured])
|
|
51354
51364
|
if (includesAgent(root.agent))
|
|
51355
51365
|
scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true);
|
|
51366
|
+
if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
|
|
51367
|
+
throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
|
|
51356
51368
|
recheckRootAliases(aliases);
|
|
51357
51369
|
return entries;
|
|
51358
51370
|
}
|
|
@@ -51934,12 +51946,17 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51934
51946
|
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join20(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
51935
51947
|
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))
|
|
51936
51948
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
51937
|
-
const
|
|
51938
|
-
if (agent !== "codex" || !entry.vendor
|
|
51949
|
+
const disabledVendorSkill = (entry) => {
|
|
51950
|
+
if (agent !== "codex" || !entry.vendor)
|
|
51939
51951
|
return false;
|
|
51940
|
-
const matched = Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
51941
51952
|
const settings = setting(entry.path);
|
|
51942
|
-
|
|
51953
|
+
if (settings.length !== 1 || settings[0]?.enabled !== false)
|
|
51954
|
+
return false;
|
|
51955
|
+
if (entry.path.startsWith(canonicalAgentPath(join20(home, ".codex", "plugins", "cache"), aliases) + sep4))
|
|
51956
|
+
return true;
|
|
51957
|
+
if (!entry.path.startsWith(canonicalAgentPath(join20(home, ".codex", "skills", ".system"), aliases) + sep4))
|
|
51958
|
+
return false;
|
|
51959
|
+
return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
51943
51960
|
};
|
|
51944
51961
|
if (!discovery || discovery.agent !== agent)
|
|
51945
51962
|
throw new Error("NATIVE_SKILL_DRIFT: native discovery coverage is missing; run skills hook install");
|
|
@@ -51956,7 +51973,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51956
51973
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
51957
51974
|
}
|
|
51958
51975
|
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
|
|
51959
|
-
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !
|
|
51976
|
+
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
|
|
51960
51977
|
if (unexpected.length) {
|
|
51961
51978
|
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
51962
51979
|
const escaped = JSON.stringify(entry.path).replace(/[\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
@@ -81557,6 +81574,7 @@ var init_skill_context = __esm(() => {
|
|
|
81557
81574
|
|
|
81558
81575
|
// src/lib/session-reconciliation.ts
|
|
81559
81576
|
import { createHash as createHash22 } from "crypto";
|
|
81577
|
+
import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
|
|
81560
81578
|
function exactTimestamp(value) {
|
|
81561
81579
|
const parsed = value === undefined ? Number.NaN : Date.parse(value);
|
|
81562
81580
|
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
@@ -81589,6 +81607,52 @@ function inspectSkillSession(sessionId, options = {}) {
|
|
|
81589
81607
|
loadedCount: receipt.loaded.length
|
|
81590
81608
|
};
|
|
81591
81609
|
}
|
|
81610
|
+
async function reconcileSkillSessionIfSafe(sessionId, profileId, options = {}) {
|
|
81611
|
+
const snapshot2 = readSkillSessionSnapshotIfExists(sessionId, options);
|
|
81612
|
+
if (!snapshot2)
|
|
81613
|
+
return false;
|
|
81614
|
+
const old = snapshot2.receipt;
|
|
81615
|
+
if (old.profile.profileId !== profileId)
|
|
81616
|
+
throw new SkillSelectionError("PROFILE_LOCK_MISMATCH", "The session is pinned to a different Skills profile.");
|
|
81617
|
+
const now3 = (options.now ?? Date.now)();
|
|
81618
|
+
const age = now3 - Date.parse(old.verifiedAt);
|
|
81619
|
+
if (age >= 0 && age <= MAX_CACHED_PROFILE_AGE_MS)
|
|
81620
|
+
return false;
|
|
81621
|
+
if (age < 0)
|
|
81622
|
+
throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "The session receipt has a future timestamp; inspect it before changing its pin.");
|
|
81623
|
+
const client2 = options.client ?? await createProfileClient();
|
|
81624
|
+
const target = structuredClone(await client2.resolveProfile(profileId));
|
|
81625
|
+
validateResolvedProfile(target, client2.authority);
|
|
81626
|
+
if (target.profileId !== profileId || target.authority !== old.profile.authority || target.workspaceId !== old.profile.workspaceId) {
|
|
81627
|
+
throw new SkillSelectionError("PROFILE_IDENTITY_MISMATCH", "The authenticated Skills profile does not match this session's profile, authority and workspace.");
|
|
81628
|
+
}
|
|
81629
|
+
const oldByKey = new Map(old.profile.selections.map((selection) => [selectionKey(selection), selection]));
|
|
81630
|
+
const targetBySlug = new Map(target.selections.map((selection) => [selection.slug, selection]));
|
|
81631
|
+
for (const key of old.loaded) {
|
|
81632
|
+
const previous = oldByKey.get(key);
|
|
81633
|
+
const current = targetBySlug.get(previous.slug);
|
|
81634
|
+
if (!current || !isDeepStrictEqual8({ ...previous, profileRevision: target.profileRevision }, current)) {
|
|
81635
|
+
throw new SkillSelectionError("SESSION_RECONCILIATION_REQUIRED", "A loaded Skills selection was removed or changed; review skills sessions reconcile before changing this session's pin.");
|
|
81636
|
+
}
|
|
81637
|
+
}
|
|
81638
|
+
const replacement = {
|
|
81639
|
+
...old,
|
|
81640
|
+
generation: nextSkillSessionGeneration(snapshot2.generation),
|
|
81641
|
+
verifiedAt: new Date((options.now ?? Date.now)()).toISOString(),
|
|
81642
|
+
profile: target,
|
|
81643
|
+
loaded: [...old.loaded]
|
|
81644
|
+
};
|
|
81645
|
+
replaceSkillSession(skillSessionSnapshotBinding(snapshot2), replacement, {
|
|
81646
|
+
schemaVersion: 1,
|
|
81647
|
+
kind: "managed-hook-safe-reconciliation",
|
|
81648
|
+
sessionId,
|
|
81649
|
+
fromProfileRevision: old.profile.profileRevision,
|
|
81650
|
+
toProfileRevision: target.profileRevision,
|
|
81651
|
+
retainedLoadedCount: old.loaded.length,
|
|
81652
|
+
retiredLoadedCount: 0
|
|
81653
|
+
}, options);
|
|
81654
|
+
return true;
|
|
81655
|
+
}
|
|
81592
81656
|
async function reconcileSkillSession(input2, options = {}) {
|
|
81593
81657
|
if (![input2.fromProfile, input2.fromRevision, input2.selectionProfile, input2.profileRevision].every((value) => typeof value === "string" && value.trim()) || !/^[a-f0-9]{64}$/.test(input2.receiptSha256)) {
|
|
81594
81658
|
throw new SkillSelectionError("INVALID_SESSION_RECONCILIATION", "Name the exact old receipt SHA256, old profile/revision and intended target profile/revision.");
|
|
@@ -81707,7 +81771,7 @@ Review digest: ${result2.reviewDigest}`);
|
|
|
81707
81771
|
reportContextError(error61, options.json);
|
|
81708
81772
|
}
|
|
81709
81773
|
});
|
|
81710
|
-
sessions.command("reconcile <id>").requiredOption("--from-profile <id>", "Expected current session profile").requiredOption("--from-revision <revision>", "Expected current session profile revision").requiredOption("--receipt-sha256 <sha256>", "Expected exact current receipt bytes").requiredOption("--selection-profile <id>", "Intended target selection profile").requiredOption("--profile-revision <revision>", "Expected current API target revision").option("--apply", "Archive the old receipt and atomically apply the reviewed plan", false).option("--plan-digest <sha256>", "Exact reviewed plan digest; required with --apply").option("--plan-issued-at <timestamp>", "Exact reviewed plan issuedAt; required with --apply").option("--plan-expires-at <timestamp>", "Exact reviewed plan expiresAt; required with --apply").option("--json", "Return the plan or application receipt as JSON", false).description("Plan an intentional migration of one session pin
|
|
81774
|
+
sessions.command("reconcile <id>").requiredOption("--from-profile <id>", "Expected current session profile").requiredOption("--from-revision <revision>", "Expected current session profile revision").requiredOption("--receipt-sha256 <sha256>", "Expected exact current receipt bytes").requiredOption("--selection-profile <id>", "Intended target selection profile").requiredOption("--profile-revision <revision>", "Expected current API target revision").option("--apply", "Archive the old receipt and atomically apply the reviewed plan", false).option("--plan-digest <sha256>", "Exact reviewed plan digest; required with --apply").option("--plan-issued-at <timestamp>", "Exact reviewed plan issuedAt; required with --apply").option("--plan-expires-at <timestamp>", "Exact reviewed plan expiresAt; required with --apply").option("--json", "Return the plan or application receipt as JSON", false).description("Plan an intentional migration of one session pin when loaded selections changed").action(async (id, options) => {
|
|
81711
81775
|
try {
|
|
81712
81776
|
const result2 = await reconcileSkillSession({ ...options, sessionId: id });
|
|
81713
81777
|
await writeCliOutput(options.json ? JSON.stringify(result2) : result2.applied ? `Reconciled ${JSON.stringify(id)} to ${result2.plan.target.profileId} at ${result2.plan.target.profileRevision}.
|
|
@@ -81729,18 +81793,27 @@ Preserved original receipt: ${result2.archivePath}` : `Planned one session recon
|
|
|
81729
81793
|
reportContextError(error61, options.json);
|
|
81730
81794
|
}
|
|
81731
81795
|
});
|
|
81732
|
-
parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Maximum injected characters", "8000").option("--max-skills <count>", "Maximum loaded skills", "3").option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
|
|
81796
|
+
parent.command("context [prompt]").description("Select bounded, versioned skill context for this prompt without running skills").option("--stdin", "Read native hook JSON or a prompt from stdin", false).option("--selection-profile <id>", "Selection profile (separate from the credential --profile)").option("--cached", "Use only an explicitly selected verified cache, at most 24 hours old", false).option("--auto-reconcile-safe", "For managed hooks, archive and renew an expired session only when all loaded selections are unchanged", false).option("--session <id>", "Keep the same skill versions across this session").option("--restore", "Re-emit loaded session context after compaction or resume", false).option("--max-chars <count>", "Maximum injected characters", "8000").option("--max-skills <count>", "Maximum loaded skills", "3").option("--json", "Return context and selection receipts as JSON", false).action(async (prompt, options) => {
|
|
81733
81797
|
try {
|
|
81798
|
+
if (options.autoReconcileSafe && !options.cached)
|
|
81799
|
+
throw new SkillSelectionError("INVALID_CONTEXT_INPUT", "--auto-reconcile-safe requires --cached.");
|
|
81734
81800
|
const input2 = options.stdin ? parseSkillContextInput(await readContextStdin()) : { prompt: prompt ?? "" };
|
|
81735
81801
|
input2.profileId = selectedProfileId(options.selectionProfile ?? input2.profileId);
|
|
81736
81802
|
input2.cwd ??= process.cwd();
|
|
81737
81803
|
input2.sessionId = options.session ?? input2.sessionId;
|
|
81738
81804
|
input2.restore ||= options.restore;
|
|
81739
|
-
const
|
|
81740
|
-
|
|
81741
|
-
|
|
81742
|
-
|
|
81743
|
-
})
|
|
81805
|
+
const resolverOptions = { ...contextResolverOptions(options), maxChars: Number(options.maxChars), maxSkills: Number(options.maxSkills) };
|
|
81806
|
+
let result2;
|
|
81807
|
+
try {
|
|
81808
|
+
result2 = await buildSkillContext(input2, resolverOptions);
|
|
81809
|
+
} catch (error61) {
|
|
81810
|
+
if (!options.cached || !options.autoReconcileSafe || !(error61 instanceof SkillSelectionError) || error61.code !== "CACHED_PROFILE_EXPIRED" || !input2.sessionId)
|
|
81811
|
+
throw error61;
|
|
81812
|
+
const sessionId = input2.agentId ? `${input2.sessionId}:${input2.agentId}` : input2.sessionId;
|
|
81813
|
+
if (!await reconcileSkillSessionIfSafe(sessionId, input2.profileId, resolverOptions))
|
|
81814
|
+
throw error61;
|
|
81815
|
+
result2 = await buildSkillContext(input2, resolverOptions);
|
|
81816
|
+
}
|
|
81744
81817
|
await writeCliOutput(options.json ? JSON.stringify(result2) : result2.context);
|
|
81745
81818
|
} catch (error61) {
|
|
81746
81819
|
reportContextError(error61, options.json);
|
|
@@ -86158,7 +86231,7 @@ var init_execution_grants = __esm(() => {
|
|
|
86158
86231
|
});
|
|
86159
86232
|
|
|
86160
86233
|
// src/lib/execution-grant-client.ts
|
|
86161
|
-
import { isDeepStrictEqual as
|
|
86234
|
+
import { isDeepStrictEqual as isDeepStrictEqual9 } from "util";
|
|
86162
86235
|
function id(value) {
|
|
86163
86236
|
if (!grantIdentifier(value))
|
|
86164
86237
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -86210,7 +86283,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
86210
86283
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
86211
86284
|
body: JSON.stringify({ grants: snapshot2 })
|
|
86212
86285
|
}));
|
|
86213
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
86286
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual9(policy.grants, snapshot2)) {
|
|
86214
86287
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
86215
86288
|
}
|
|
86216
86289
|
return policy;
|
|
@@ -86221,7 +86294,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
86221
86294
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
86222
86295
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
86223
86296
|
}
|
|
86224
|
-
if (!
|
|
86297
|
+
if (!isDeepStrictEqual9(value.bindings.selection, snapshot2.selection) || !isDeepStrictEqual9(value.bindings.consumer, snapshot2.consumer)) {
|
|
86225
86298
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
86226
86299
|
}
|
|
86227
86300
|
return value;
|
|
@@ -109440,7 +109513,7 @@ var init_create_sync_config = __esm(() => {
|
|
|
109440
109513
|
});
|
|
109441
109514
|
|
|
109442
109515
|
// src/lib/codex-native-config.ts
|
|
109443
|
-
import { isDeepStrictEqual as
|
|
109516
|
+
import { isDeepStrictEqual as isDeepStrictEqual10 } from "util";
|
|
109444
109517
|
function isRecord7(value) {
|
|
109445
109518
|
if (!value || typeof value !== "object")
|
|
109446
109519
|
return false;
|
|
@@ -109462,7 +109535,7 @@ function decimalIdentity(text4) {
|
|
|
109462
109535
|
return `${match[1]}${coefficient}e${exponent}`;
|
|
109463
109536
|
}
|
|
109464
109537
|
function codexNativeConfigEqual(native, expected) {
|
|
109465
|
-
if (
|
|
109538
|
+
if (isDeepStrictEqual10(native, expected))
|
|
109466
109539
|
return true;
|
|
109467
109540
|
if (typeof expected === "number") {
|
|
109468
109541
|
if (!Number.isFinite(expected) || !isRecord7(native))
|
|
@@ -109628,7 +109701,7 @@ var init_codex_hook_rpc = __esm(() => {
|
|
|
109628
109701
|
// src/lib/codex-hook-trust-identity.ts
|
|
109629
109702
|
import { realpathSync as realpathSync13 } from "fs";
|
|
109630
109703
|
import { dirname as dirname31, isAbsolute as isAbsolute25, join as join57 } from "path";
|
|
109631
|
-
import { isDeepStrictEqual as
|
|
109704
|
+
import { isDeepStrictEqual as isDeepStrictEqual11 } from "util";
|
|
109632
109705
|
function inspectRecordedSkillsCli(command, expected) {
|
|
109633
109706
|
need4(isAbsolute25(command) && !command.includes("\x00"), "RECONCILE_RECORDED_COMMAND_REQUIRED");
|
|
109634
109707
|
const resolved = realpathSync13(command);
|
|
@@ -109638,7 +109711,7 @@ function inspectRecordedSkillsCli(command, expected) {
|
|
|
109638
109711
|
need4(pkg.name === "@hasna/skills" && /^\d+\.\d+\.\d+$/.test(pkg.version) && pkg.bin?.skills === "bin/index.js" && resolved === join57(dirname31(manifest.file), "bin/index.js"), "SKILLS_PACKAGE_MISMATCH");
|
|
109639
109712
|
need4((cli.stat.mode & 0o100n) !== 0n, "SKILLS_COMMAND_NOT_EXECUTABLE");
|
|
109640
109713
|
const receipt = { path: resolved, version: pkg.version, sha256: cli.sha256, manifestSha256: manifest.sha256 };
|
|
109641
|
-
need4(
|
|
109714
|
+
need4(isDeepStrictEqual11(receipt, expected), "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
109642
109715
|
return { receipt, recheck() {
|
|
109643
109716
|
need4(realpathSync13(command) === resolved, "SKILLS_COMMAND_CHANGED");
|
|
109644
109717
|
unchanged2(cli);
|
|
@@ -109674,19 +109747,19 @@ var init_codex_hook_trust_identity = __esm(() => {
|
|
|
109674
109747
|
});
|
|
109675
109748
|
|
|
109676
109749
|
// src/lib/codex-hook-policy-recovery.ts
|
|
109677
|
-
import { isDeepStrictEqual as
|
|
109750
|
+
import { isDeepStrictEqual as isDeepStrictEqual12 } from "util";
|
|
109678
109751
|
function reviewedClaudeDiscoveryChange(beforeText, currentText, options) {
|
|
109679
109752
|
need4(typeof options.reason === "string" && options.reason.trim().length > 0 && options.reason.length <= 512 && !/[\x00-\x1f\x7f]/.test(options.reason), "RECONCILE_RECOVERY_REASON_REQUIRED");
|
|
109680
109753
|
need4(options.reviewedPlanDigest === undefined || /^[a-f0-9]{64}$/.test(options.reviewedPlanDigest), "RECONCILE_RECOVERY_PLAN_CHANGED");
|
|
109681
109754
|
const before2 = parseManagedSkillPolicy(beforeText), current = parseManagedSkillPolicy(currentText);
|
|
109682
109755
|
const oldDiscovery = before2.bridge?.discovery?.claude, newDiscovery = current.bridge?.discovery?.claude;
|
|
109683
109756
|
need4(oldDiscovery?.agent === "claude" && newDiscovery?.agent === "claude" && oldDiscovery.method === "reviewed" && newDiscovery.method === "reviewed", "RECONCILE_POLICY_CHANGED");
|
|
109684
|
-
const changes = ["roots", "sources"].filter((key) => !
|
|
109757
|
+
const changes = ["roots", "sources"].filter((key) => !isDeepStrictEqual12(oldDiscovery[key], newDiscovery[key])).map((key) => ({ path: `bridge.discovery.claude.${key}`, before: oldDiscovery[key], after: newDiscovery[key] }));
|
|
109685
109758
|
need4(changes.length > 0, "RECONCILE_POLICY_CHANGED");
|
|
109686
109759
|
const compared = structuredClone(current);
|
|
109687
109760
|
compared.bridge.discovery.claude.roots = oldDiscovery.roots;
|
|
109688
109761
|
compared.bridge.discovery.claude.sources = oldDiscovery.sources;
|
|
109689
|
-
need4(
|
|
109762
|
+
need4(isDeepStrictEqual12(before2, compared), "RECONCILE_POLICY_CHANGED");
|
|
109690
109763
|
verifyAgentDiscovery(newDiscovery);
|
|
109691
109764
|
return { reason: options.reason, changes };
|
|
109692
109765
|
}
|
|
@@ -109702,7 +109775,7 @@ import { createHash as createHash28, randomUUID as randomUUID10 } from "crypto";
|
|
|
109702
109775
|
import { execFileSync } from "child_process";
|
|
109703
109776
|
import { dirname as dirname32, isAbsolute as isAbsolute26, join as join58, resolve as resolve23 } from "path";
|
|
109704
109777
|
import { homedir as homedir12 } from "os";
|
|
109705
|
-
import { isDeepStrictEqual as
|
|
109778
|
+
import { isDeepStrictEqual as isDeepStrictEqual13 } from "util";
|
|
109706
109779
|
function verifyReleaseArtifact(proof, skillsCli) {
|
|
109707
109780
|
need4(isAbsolute26(proof.packageTarPath) && !proof.packageTarPath.includes("\x00"), "RECONCILE_RELEASE_PROVENANCE_PATH");
|
|
109708
109781
|
const tarPath = realpathSync14(proof.packageTarPath);
|
|
@@ -109744,7 +109817,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
109744
109817
|
const configSha256 = intent.configSha256 ?? configBefore.sha256, configVersion = intent.configVersion ?? intent.expectedVersion;
|
|
109745
109818
|
const digestInput = { version: 1, home: intent.home ?? home, configPath: intent.configPath, ...intent.nativeConfigPath !== undefined ? { nativeConfigPath: intent.nativeConfigPath } : {}, configSha256, configVersion, policySha256: intent.policySha256, hooksSha256: intent.hooksSha256, skillsCli: intent.skillsCli, nativeVersion: intent.nativeVersion, declarations, admitted };
|
|
109746
109819
|
need4(makePlanDigest(digestInput) === intent.planDigest, "RECONCILE_PLAN_CHANGED");
|
|
109747
|
-
need4(
|
|
109820
|
+
need4(isDeepStrictEqual13(intent.hooks, admitted.filter((hook) => !hook.enabled || hook.trustStatus !== "trusted")), "RECONCILE_INTENT_HOOKS_CHANGED");
|
|
109748
109821
|
need4(configBefore.sha256 === (intent.beforeSha256 ?? intent.configSha256) && hooksBefore.sha256 === intent.hooksSha256 && policyBefore.sha256 === intent.policySha256, "RECONCILE_JOURNAL_CHANGED");
|
|
109749
109822
|
const policyCurrent = snapshot(join58(dataDir3, "agent-policy.json"), true);
|
|
109750
109823
|
const { claudeDiscoveryRecovery: claudeRecovery, nativeHookAdditionsRecovery: additionsRecovery } = options;
|
|
@@ -109757,7 +109830,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
109757
109830
|
if (policyCurrent.sha256 !== policyBefore.sha256 && !claudeRecovery) {
|
|
109758
109831
|
need4(options.supersedeBinding === true, "RECONCILE_POLICY_CHANGED");
|
|
109759
109832
|
const oldPolicy = JSON.parse(policyBefore.text), currentPolicy = JSON.parse(policyCurrent.text);
|
|
109760
|
-
need4(
|
|
109833
|
+
need4(isDeepStrictEqual13(withoutCodexCommand(oldPolicy), withoutCodexCommand(currentPolicy)), "RECONCILE_POLICY_CHANGED");
|
|
109761
109834
|
supersededBinding = true;
|
|
109762
109835
|
}
|
|
109763
109836
|
if (supersededBinding || recovery) {
|
|
@@ -109793,7 +109866,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
109793
109866
|
need4(state?.enabled === true && typeof state.trusted_hash === "string" && /^sha256:[a-f0-9]{64}$/.test(state.trusted_hash), "RECONCILE_CONFIG_DRIFT");
|
|
109794
109867
|
}
|
|
109795
109868
|
} else
|
|
109796
|
-
need4(
|
|
109869
|
+
need4(isDeepStrictEqual13(expectedConfig, current), "RECONCILE_CONFIG_DRIFT");
|
|
109797
109870
|
const configDiscoveryRecovery = recovery && binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, configBefore, currentConfig, intent.hooks, additions?.trustProof) : undefined;
|
|
109798
109871
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: configDiscoveryRecovery });
|
|
109799
109872
|
const recoveryCli = recovery ? bindSkillsCli("skills", options.reviewedSkillsCli) : undefined;
|
|
@@ -109802,14 +109875,14 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
109802
109875
|
const skillsCli = recovery ? inspectRecordedSkillsCli(binding.commands.codex, intent.skillsCli) : bindSkillsCli(binding.commands.codex, options.reviewedSkillsCli);
|
|
109803
109876
|
if (supersededBinding)
|
|
109804
109877
|
verifyReleaseArtifact(options.releaseProof, skillsCli);
|
|
109805
|
-
const skillsBindingChanged = !
|
|
109878
|
+
const skillsBindingChanged = !isDeepStrictEqual13(skillsCli.receipt, intent.skillsCli);
|
|
109806
109879
|
need4(!skillsBindingChanged || supersededBinding, "RECONCILE_SKILLS_BINDING_CHANGED");
|
|
109807
109880
|
if (!additions && hooksCurrent.sha256 !== intent.hooksSha256 && hooksCurrent.sha256 !== hooksBefore.sha256) {
|
|
109808
109881
|
need4(supersededBinding, "RECONCILE_HOOKS_CHANGED");
|
|
109809
109882
|
const currentDeclarations = JSON.parse(hooksCurrent.text).hooks;
|
|
109810
109883
|
for (const [event, [nativeEvent]] of Object.entries(eventNames)) {
|
|
109811
109884
|
const command = renderAgentHookCommand(binding.commands.codex, "codex", binding.profiles.codex, event);
|
|
109812
|
-
need4(
|
|
109885
|
+
need4(isDeepStrictEqual13(currentDeclarations?.[event], [{ hooks: [{ type: "command", command, timeout: 15 }] }]), "RECONCILE_BINDING_HOOK_CHANGED");
|
|
109813
109886
|
}
|
|
109814
109887
|
}
|
|
109815
109888
|
rpc = await connect({ command: options.codexCommand ?? "codex", home, codexHome });
|
|
@@ -109832,9 +109905,9 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
109832
109905
|
delete identity4.command;
|
|
109833
109906
|
return identity4;
|
|
109834
109907
|
};
|
|
109835
|
-
need4(
|
|
109908
|
+
need4(isDeepStrictEqual13(stable(actual), stable(expected)), "RECONCILE_NATIVE_IDENTITY_CHANGED");
|
|
109836
109909
|
if (!plannedKeys.has(expected.key))
|
|
109837
|
-
need4(
|
|
109910
|
+
need4(isDeepStrictEqual13(actual, expected), "RECONCILE_UNRELATED_HOOK_CHANGED");
|
|
109838
109911
|
}
|
|
109839
109912
|
for (const wanted of intent.hooks) {
|
|
109840
109913
|
const matches2 = nativeHooks.filter((h2) => h2.key === wanted.key || h2.key === wanted.key.replace(join58(home, ".codex/hooks.json"), hooksPath));
|
|
@@ -109853,7 +109926,7 @@ async function reconcileCodexNativeHooks(options, connect = connectCodexHookRpc)
|
|
|
109853
109926
|
const layers = config2?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
109854
109927
|
need4(layers?.length === 1 && !layers[0].disabledReason && codexNativeConfigEqual(layers[0].config, current), "RECONCILE_NATIVE_CONFIG_MISMATCH");
|
|
109855
109928
|
const finalDiscovery = await rpc.request("hooks/list", { cwds: [home] });
|
|
109856
|
-
need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks ||
|
|
109929
|
+
need4(Array.isArray(finalDiscovery?.data) && finalDiscovery.data.length === 1 && finalDiscovery.data[0].cwd === home && !finalDiscovery.data[0].errors?.length && !finalDiscovery.data[0].warnings?.length && (!recordedNativeHooks || isDeepStrictEqual13(finalDiscovery.data[0].hooks.map(safeNativeHook), nativeHooks.map(safeNativeHook))), "RECONCILE_NATIVE_STATE_CHANGED");
|
|
109857
109930
|
const finalConfig = await rpc.request("config/read", { includeLayers: true, cwd: home });
|
|
109858
109931
|
const finalLayers = finalConfig?.layers?.filter((layer) => layer.name?.type === "user" && layer.name.file === nativeConfigPath && !layer.name.profile);
|
|
109859
109932
|
need4(finalLayers?.length === 1 && !finalLayers[0].disabledReason && codexNativeConfigEqual(finalLayers[0].config, current), "RECONCILE_NATIVE_CONFIG_CHANGED");
|
|
@@ -109914,7 +109987,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
109914
109987
|
const groups = declarations[event];
|
|
109915
109988
|
need4(Array.isArray(groups), "DECLARATION_CHANGED");
|
|
109916
109989
|
const matches2 = groups.flatMap((group, gi) => (group.hooks ?? []).flatMap((entry, hi) => entry.command === command ? [{ group, entry, gi, hi }] : []));
|
|
109917
|
-
need4(matches2.length === 1 &&
|
|
109990
|
+
need4(matches2.length === 1 && isDeepStrictEqual13(matches2[0].group, { hooks: [{ type: "command", command, timeout: 15 }] }), "DECLARATION_CHANGED");
|
|
109918
109991
|
expected.push({ event: nativeEvent, key: `${join58(home, ".codex/hooks.json")}:${label}:${matches2[0].gi}:${matches2[0].hi}`, command });
|
|
109919
109992
|
}
|
|
109920
109993
|
codexTrustTextWitness(configFile.text, expected.map((h) => h.key));
|
|
@@ -109980,7 +110053,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
109980
110053
|
save(join58(journal, "hooks.before.json"), hooksFile.bytes);
|
|
109981
110054
|
save(join58(journal, "policy.before.json"), policyFile.bytes);
|
|
109982
110055
|
save(join58(journal, "intent.json"), json2({ ...digestInput, planDigest, hooks: planned, nativeHooks: discovered.map(safeNativeHook) }));
|
|
109983
|
-
need4(
|
|
110056
|
+
need4(isDeepStrictEqual13(admit(await list2()), admitted), "NATIVE_DISCOVERY_CHANGED");
|
|
109984
110057
|
checkInputs();
|
|
109985
110058
|
let result2;
|
|
109986
110059
|
try {
|
|
@@ -109994,7 +110067,7 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
109994
110067
|
expectedConfig.hooks.state ??= {};
|
|
109995
110068
|
for (const h of planned)
|
|
109996
110069
|
expectedConfig.hooks.state[h.key] = { ...expectedConfig.hooks.state[h.key], enabled: true, trusted_hash: h.currentHash };
|
|
109997
|
-
need4(
|
|
110070
|
+
need4(isDeepStrictEqual13(structure3(after2.text), expectedConfig) && codexTrustTextWitness(after2.text, keys3) === preservationText, "PRESERVATION_FAILED");
|
|
109998
110071
|
const savedConfig = snapshot(join58(journal, "config.before.toml"), true);
|
|
109999
110072
|
need4(savedConfig.sha256 === configFile.sha256, "JOURNAL_CHANGED");
|
|
110000
110073
|
const discoveryTransition = binding.discovery?.codex ? createCodexHookDiscoveryRecovery(binding.discovery.codex, savedConfig, after2, planned) : undefined;
|
|
@@ -110002,8 +110075,8 @@ async function enrollCodexNativeHooks(options = {}, connect = connectCodexHookRp
|
|
|
110002
110075
|
unchanged2(hooksFile);
|
|
110003
110076
|
assertManagedAgentBridge("codex", { home, dataDir: dataDir3, projectDir: home, codexDiscoveryRecovery: discoveryTransition });
|
|
110004
110077
|
const current = await list2(), confirmed = admit(current);
|
|
110005
|
-
need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") &&
|
|
110006
|
-
need4(
|
|
110078
|
+
need4(confirmed.every((h) => h.enabled && h.trustStatus === "trusted") && isDeepStrictEqual13(confirmed.map((h) => [h.key, h.currentHash]), admitted.map((h) => [h.key, h.currentHash])), "NATIVE_NOT_ELIGIBLE");
|
|
110079
|
+
need4(isDeepStrictEqual13(current.filter((h) => !keys3.includes(h.key)), discovered.filter((h) => !keys3.includes(h.key))), "UNRELATED_HOOK_CHANGED");
|
|
110007
110080
|
unchanged2(after2);
|
|
110008
110081
|
unchanged2(savedConfig);
|
|
110009
110082
|
unchanged2(hooksFile);
|
|
@@ -110070,6 +110143,9 @@ function hookFailureReason(error61, profileId) {
|
|
|
110070
110143
|
if (code2 === "PROFILE_LOCK_MISMATCH") {
|
|
110071
110144
|
return `${prefix} This session or project is pinned to a different profile. Inspect the session with skills sessions show <session-id> --json; review skills sessions reconcile for an intentional session change, or explicit project sync for a project change. Sync alone does not change that pin.`;
|
|
110072
110145
|
}
|
|
110146
|
+
if (code2 === "SESSION_RECONCILIATION_REQUIRED") {
|
|
110147
|
+
return `${prefix} A loaded skill changed or was removed. Inspect this session with skills sessions show <session-id> --json, then review skills sessions reconcile for an intentional change. Sync alone does not change this pin.`;
|
|
110148
|
+
}
|
|
110073
110149
|
if (code2.startsWith("SESSION_")) {
|
|
110074
110150
|
return `${prefix} Inspect the session with skills sessions show <session-id> --json and review its parent or concurrent writer before retrying. Do not remove or rewrite its receipt to bypass this refusal.`;
|
|
110075
110151
|
}
|
|
@@ -110112,6 +110188,7 @@ var init_hook_diagnostics = __esm(() => {
|
|
|
110112
110188
|
"SESSION_GENERATION_EXHAUSTED",
|
|
110113
110189
|
"SESSION_PARENT_CHANGED",
|
|
110114
110190
|
"SESSION_RECEIPT_CHANGED",
|
|
110191
|
+
"SESSION_RECONCILIATION_REQUIRED",
|
|
110115
110192
|
"SESSION_RECONCILIATION_INCOMPLETE",
|
|
110116
110193
|
"SESSION_WRITE_LOCKED",
|
|
110117
110194
|
"SESSION_WRITE_LOCK_CHANGED",
|
|
@@ -110156,7 +110233,7 @@ async function contextForHook(input3, profileId, cached2, deadline) {
|
|
|
110156
110233
|
throw new HookDiagnosticError("SKILLS_HOOK_TIMEOUT", "context");
|
|
110157
110234
|
const args2 = [process.execPath, process.argv[1], "context", "--stdin", "--json", "--selection-profile", profileId];
|
|
110158
110235
|
if (cached2)
|
|
110159
|
-
args2.push("--cached");
|
|
110236
|
+
args2.push("--cached", "--auto-reconcile-safe");
|
|
110160
110237
|
const child = Bun.spawn(args2, { stdin: new Blob([JSON.stringify(input3)]), stdout: "pipe", stderr: "pipe", env: { ...process.env, NO_COLOR: "1" } });
|
|
110161
110238
|
let timedOut = false;
|
|
110162
110239
|
const timer = setTimeout(() => {
|
|
@@ -110363,13 +110440,18 @@ function registerAgentIntegration(parent) {
|
|
|
110363
110440
|
}
|
|
110364
110441
|
});
|
|
110365
110442
|
const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
|
|
110366
|
-
migrate.command("native").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
|
|
110443
|
+
migrate.command("native").option("--agent <agent>", "Limit inventory and migration to one maintained agent").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--reviewed-cache-alias <path>", "Exact skill-containing vendor cache alias reviewed for migration; runtime hooks still refuse it").option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
|
|
110367
110444
|
try {
|
|
110445
|
+
if (options.agent && !INTEGRATION_AGENTS.includes(options.agent))
|
|
110446
|
+
throw new Error(`Unsupported migration agent: ${options.agent}`);
|
|
110368
110447
|
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync40(options.discoveryInputs, "utf8")) : undefined;
|
|
110369
110448
|
if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
|
|
110370
110449
|
throw new Error("--target-manifest cannot be combined with broad native migration selectors");
|
|
110371
110450
|
const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
|
|
110372
|
-
|
|
110451
|
+
if (options.apply && options.reviewedCacheAlias && !targetManifest)
|
|
110452
|
+
throw new Error("Applying a reviewed cache alias requires an exact --target-manifest");
|
|
110453
|
+
const includeVendor = options.includeVendor || Boolean(targetManifest?.targets.some((target) => target.vendor));
|
|
110454
|
+
const inventory = inventoryNativeSkills(undefined, { projectDirs: [process.cwd(), ...options.project ? [options.project] : []], agents: options.agent ? [options.agent] : undefined, includeVendor, configured: includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias });
|
|
110373
110455
|
if (targetManifest)
|
|
110374
110456
|
selectNativeMigrationTargets(inventory, targetManifest);
|
|
110375
110457
|
const result2 = options.apply ? archiveNativeSkills(inventory, { includeUnmanaged: options.includeUnmanaged, includeVendor: options.includeVendor, allowRootAliases: options.allowRootAliases, targetManifest }) : { entries: [], ...targetManifest ? { targetManifest: { schema: targetManifest.schema, digest: targetManifest.digest, targetCount: targetManifest.targets.length } } : {} };
|
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.10.
|
|
20022
|
+
version: "0.10.4",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24122,7 +24122,7 @@ var init_dist_es9 = __esm(() => {
|
|
|
24122
24122
|
// package.json
|
|
24123
24123
|
var package_default = {
|
|
24124
24124
|
name: "@hasna/skills",
|
|
24125
|
-
version: "0.10.
|
|
24125
|
+
version: "0.10.4",
|
|
24126
24126
|
description: "Skills library for AI coding agents",
|
|
24127
24127
|
type: "module",
|
|
24128
24128
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24125,7 +24125,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24125
24125
|
// package.json
|
|
24126
24126
|
var package_default = {
|
|
24127
24127
|
name: "@hasna/skills",
|
|
24128
|
-
version: "0.10.
|
|
24128
|
+
version: "0.10.4",
|
|
24129
24129
|
description: "Skills library for AI coding agents",
|
|
24130
24130
|
type: "module",
|
|
24131
24131
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -9611,7 +9611,7 @@ import { isAbsolute as isAbsolute11, join as join23, resolve as resolve11 } from
|
|
|
9611
9611
|
// package.json
|
|
9612
9612
|
var package_default = {
|
|
9613
9613
|
name: "@hasna/skills",
|
|
9614
|
-
version: "0.10.
|
|
9614
|
+
version: "0.10.4",
|
|
9615
9615
|
description: "Skills library for AI coding agents",
|
|
9616
9616
|
type: "module",
|
|
9617
9617
|
bin: {
|
|
@@ -13309,8 +13309,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
13309
13309
|
if (matches.length !== 1)
|
|
13310
13310
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
13311
13311
|
const entry = matches[0];
|
|
13312
|
-
if (entry.bridge || entry.system || entry.vendor)
|
|
13313
|
-
throw new Error(`Native migration target is protected or vendor
|
|
13312
|
+
if (entry.bridge || entry.system || entry.vendor !== (target.vendor === true))
|
|
13313
|
+
throw new Error(`Native migration target is protected or vendor classification changed: ${entry.path}`);
|
|
13314
13314
|
if (entry.hash !== target.treeSha256 || treeHash(entry.path) !== target.treeSha256)
|
|
13315
13315
|
throw new Error(`Native migration target changed after review: ${entry.path}`);
|
|
13316
13316
|
selected.push(entry);
|
|
@@ -13333,6 +13333,8 @@ function projectAncestorDirectories(projects) {
|
|
|
13333
13333
|
return [...directories];
|
|
13334
13334
|
}
|
|
13335
13335
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
13336
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute9(options.reviewedCacheAlias) || resolve9(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
13337
|
+
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
13336
13338
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
13337
13339
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
13338
13340
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
@@ -13353,14 +13355,22 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
13353
13355
|
if (discoveryPathBytes > 4 * 1024 * 1024)
|
|
13354
13356
|
throw new Error("Native skill discovery metadata limit exceeded");
|
|
13355
13357
|
}
|
|
13356
|
-
|
|
13358
|
+
let reviewedCacheAliasFound = false;
|
|
13359
|
+
function verifiedSiblingCacheAlias(path, parent) {
|
|
13357
13360
|
const link = readlinkSync3(path), target = resolve9(parent, link);
|
|
13358
13361
|
if (dirname10(target) !== parent || (isAbsolute9(link) ? link !== target : dirname10(link) !== "."))
|
|
13359
13362
|
return false;
|
|
13360
13363
|
if (!lstatSync14(target, { throwIfNoEntry: false })?.isDirectory())
|
|
13361
13364
|
return false;
|
|
13362
13365
|
const scan = cacheScans.get(target);
|
|
13363
|
-
|
|
13366
|
+
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
13367
|
+
return false;
|
|
13368
|
+
if (scan.hasSkills) {
|
|
13369
|
+
if (path !== options.reviewedCacheAlias)
|
|
13370
|
+
return false;
|
|
13371
|
+
reviewedCacheAliasFound = true;
|
|
13372
|
+
}
|
|
13373
|
+
return true;
|
|
13364
13374
|
}
|
|
13365
13375
|
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
13366
13376
|
if (!admitted)
|
|
@@ -13417,7 +13427,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
13417
13427
|
const child = join16(path, name);
|
|
13418
13428
|
if (isVendor && lstatSync14(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
13419
13429
|
const target = statSync7(child, { throwIfNoEntry: false });
|
|
13420
|
-
if (target?.isFile() || pluginCache && target?.isDirectory() &&
|
|
13430
|
+
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(child, path)) {
|
|
13421
13431
|
scan.entries++;
|
|
13422
13432
|
continue;
|
|
13423
13433
|
}
|
|
@@ -13451,6 +13461,8 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
13451
13461
|
for (const root of [...options.agentRoots ?? [], ...configured])
|
|
13452
13462
|
if (includesAgent(root.agent))
|
|
13453
13463
|
scanRoot(root.agent, canonicalAgentPath(root.path, aliases), true);
|
|
13464
|
+
if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
|
|
13465
|
+
throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
|
|
13454
13466
|
recheckRootAliases(aliases);
|
|
13455
13467
|
return entries;
|
|
13456
13468
|
}
|
|
@@ -14113,12 +14125,17 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
14113
14125
|
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join16(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
14114
14126
|
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))
|
|
14115
14127
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
14116
|
-
const
|
|
14117
|
-
if (agent !== "codex" || !entry.vendor
|
|
14128
|
+
const disabledVendorSkill = (entry) => {
|
|
14129
|
+
if (agent !== "codex" || !entry.vendor)
|
|
14118
14130
|
return false;
|
|
14119
|
-
const matched = Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
14120
14131
|
const settings = setting(entry.path);
|
|
14121
|
-
|
|
14132
|
+
if (settings.length !== 1 || settings[0]?.enabled !== false)
|
|
14133
|
+
return false;
|
|
14134
|
+
if (entry.path.startsWith(canonicalAgentPath(join16(home, ".codex", "plugins", "cache"), aliases) + sep4))
|
|
14135
|
+
return true;
|
|
14136
|
+
if (!entry.path.startsWith(canonicalAgentPath(join16(home, ".codex", "skills", ".system"), aliases) + sep4))
|
|
14137
|
+
return false;
|
|
14138
|
+
return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
14122
14139
|
};
|
|
14123
14140
|
if (!discovery || discovery.agent !== agent)
|
|
14124
14141
|
throw new Error("NATIVE_SKILL_DRIFT: native discovery coverage is missing; run skills hook install");
|
|
@@ -14135,7 +14152,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
14135
14152
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
14136
14153
|
}
|
|
14137
14154
|
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
|
|
14138
|
-
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !
|
|
14155
|
+
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
|
|
14139
14156
|
if (unexpected.length) {
|
|
14140
14157
|
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
14141
14158
|
const escaped = JSON.stringify(entry.path).replace(/[\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
@@ -29,6 +29,7 @@ export interface NativeMigrationTarget {
|
|
|
29
29
|
projectRoot: string;
|
|
30
30
|
path: string;
|
|
31
31
|
treeSha256: string;
|
|
32
|
+
vendor?: true;
|
|
32
33
|
}
|
|
33
34
|
export interface NativeMigrationTargetManifest {
|
|
34
35
|
schema: "hasna.skills-native-migration-targets.v1";
|
|
@@ -84,6 +85,7 @@ export declare function inventoryNativeSkills(home?: string, options?: {
|
|
|
84
85
|
configured?: boolean;
|
|
85
86
|
discoveryInputs?: ReviewedDiscoveryInputs;
|
|
86
87
|
allowRootAliases?: boolean;
|
|
88
|
+
reviewedCacheAlias?: string;
|
|
87
89
|
}): NativeSkillEntry[];
|
|
88
90
|
/** Planning is read-only; credentials and unrelated settings never appear in CLI output. */
|
|
89
91
|
export declare function planAgentIntegration(options: {
|
|
@@ -30,7 +30,13 @@ export declare function inspectSkillSession(sessionId: string, options?: Selecti
|
|
|
30
30
|
selectionCount: number;
|
|
31
31
|
loadedCount: number;
|
|
32
32
|
};
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Managed hooks may move an expired pin only when every skill already loaded
|
|
35
|
+
* by this session is still selected with exactly the same behavior and bytes.
|
|
36
|
+
* The old receipt is archived and replaced under its exact snapshot lock.
|
|
37
|
+
*/
|
|
38
|
+
export declare function reconcileSkillSessionIfSafe(sessionId: string, profileId: string, options?: SessionReconciliationOptions): Promise<boolean>;
|
|
39
|
+
/** Explicit review for changed loaded selections or intentional profile migration. */
|
|
34
40
|
export declare function reconcileSkillSession(input: SessionReconciliationInput, options?: SessionReconciliationOptions): Promise<{
|
|
35
41
|
applied: false;
|
|
36
42
|
plan: {
|
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.4",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -41180,8 +41180,8 @@ function selectNativeMigrationTargets(inventory, manifest) {
|
|
|
41180
41180
|
if (matches.length !== 1)
|
|
41181
41181
|
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
41182
41182
|
const entry = matches[0];
|
|
41183
|
-
if (entry.bridge || entry.system || entry.vendor)
|
|
41184
|
-
throw new Error(`Native migration target is protected or vendor
|
|
41183
|
+
if (entry.bridge || entry.system || entry.vendor !== (target.vendor === true))
|
|
41184
|
+
throw new Error(`Native migration target is protected or vendor classification changed: ${entry.path}`);
|
|
41185
41185
|
if (entry.hash !== target.treeSha256 || treeHash(entry.path) !== target.treeSha256)
|
|
41186
41186
|
throw new Error(`Native migration target changed after review: ${entry.path}`);
|
|
41187
41187
|
selected.push(entry);
|
|
@@ -41204,6 +41204,8 @@ function projectAncestorDirectories(projects) {
|
|
|
41204
41204
|
return [...directories];
|
|
41205
41205
|
}
|
|
41206
41206
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
41207
|
+
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute15(options.reviewedCacheAlias) || resolve14(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
41208
|
+
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
41207
41209
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
41208
41210
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
41209
41211
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
@@ -41224,14 +41226,22 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
41224
41226
|
if (discoveryPathBytes > 4 * 1024 * 1024)
|
|
41225
41227
|
throw new Error("Native skill discovery metadata limit exceeded");
|
|
41226
41228
|
}
|
|
41227
|
-
|
|
41229
|
+
let reviewedCacheAliasFound = false;
|
|
41230
|
+
function verifiedSiblingCacheAlias(path, parent) {
|
|
41228
41231
|
const link = readlinkSync3(path), target = resolve14(parent, link);
|
|
41229
41232
|
if (dirname14(target) !== parent || (isAbsolute15(link) ? link !== target : dirname14(link) !== "."))
|
|
41230
41233
|
return false;
|
|
41231
41234
|
if (!lstatSync18(target, { throwIfNoEntry: false })?.isDirectory())
|
|
41232
41235
|
return false;
|
|
41233
41236
|
const scan = cacheScans.get(target);
|
|
41234
|
-
|
|
41237
|
+
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
41238
|
+
return false;
|
|
41239
|
+
if (scan.hasSkills) {
|
|
41240
|
+
if (path !== options.reviewedCacheAlias)
|
|
41241
|
+
return false;
|
|
41242
|
+
reviewedCacheAliasFound = true;
|
|
41243
|
+
}
|
|
41244
|
+
return true;
|
|
41235
41245
|
}
|
|
41236
41246
|
function visit2(agent, path, vendor = false, depth = 0, pluginCache = false, admitted = false) {
|
|
41237
41247
|
if (!admitted)
|
|
@@ -41288,7 +41298,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
41288
41298
|
const child = join21(path, name);
|
|
41289
41299
|
if (isVendor && lstatSync18(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
41290
41300
|
const target = statSync5(child, { throwIfNoEntry: false });
|
|
41291
|
-
if (target?.isFile() || pluginCache && target?.isDirectory() &&
|
|
41301
|
+
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(child, path)) {
|
|
41292
41302
|
scan.entries++;
|
|
41293
41303
|
continue;
|
|
41294
41304
|
}
|
|
@@ -41322,6 +41332,8 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
41322
41332
|
for (const root2 of [...options.agentRoots ?? [], ...configured])
|
|
41323
41333
|
if (includesAgent(root2.agent))
|
|
41324
41334
|
scanRoot(root2.agent, canonicalAgentPath(root2.path, aliases), true);
|
|
41335
|
+
if (options.reviewedCacheAlias !== undefined && !reviewedCacheAliasFound)
|
|
41336
|
+
throw new Error("Reviewed cache alias was not an inventoried skill-containing sibling alias");
|
|
41325
41337
|
recheckRootAliases(aliases);
|
|
41326
41338
|
return entries;
|
|
41327
41339
|
}
|
|
@@ -41984,12 +41996,17 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
41984
41996
|
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join21(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
41985
41997
|
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))
|
|
41986
41998
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
41987
|
-
const
|
|
41988
|
-
if (agent !== "codex" || !entry.vendor
|
|
41999
|
+
const disabledVendorSkill = (entry) => {
|
|
42000
|
+
if (agent !== "codex" || !entry.vendor)
|
|
41989
42001
|
return false;
|
|
41990
|
-
const matched = Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
41991
42002
|
const settings = setting(entry.path);
|
|
41992
|
-
|
|
42003
|
+
if (settings.length !== 1 || settings[0]?.enabled !== false)
|
|
42004
|
+
return false;
|
|
42005
|
+
if (entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "plugins", "cache"), aliases) + sep4))
|
|
42006
|
+
return true;
|
|
42007
|
+
if (!entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep4))
|
|
42008
|
+
return false;
|
|
42009
|
+
return Array.isArray(binding.disabledBuiltins) && binding.disabledBuiltins.some((item) => item.path === entry.path && item.hash === entry.hash);
|
|
41993
42010
|
};
|
|
41994
42011
|
if (!discovery || discovery.agent !== agent)
|
|
41995
42012
|
throw new Error("NATIVE_SKILL_DRIFT: native discovery coverage is missing; run skills hook install");
|
|
@@ -42006,7 +42023,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
42006
42023
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
42007
42024
|
}
|
|
42008
42025
|
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
|
|
42009
|
-
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !
|
|
42026
|
+
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
|
|
42010
42027
|
if (unexpected.length) {
|
|
42011
42028
|
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
42012
42029
|
const escaped = JSON.stringify(entry.path).replace(/[\u007f-\u009f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|