@hasna/skills 0.8.7 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/bin/index.js +33 -18
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +19 -2
- package/dist/sdk/index.js +19 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,22 @@ skills hook install --agent all --selection-profile default --json
|
|
|
70
70
|
skills hook install --agent all --selection-profile default --apply --json
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
Native migration includes the current directory, every ancestor, and the global
|
|
74
|
+
agent directories. Use `--project /path/to/project` to include another project
|
|
75
|
+
and its ancestors. This matches the prompt hook's project discovery, including
|
|
76
|
+
skills inherited from a parent workspace. Preview the JSON inventory before
|
|
77
|
+
applying it; archived copies retain recovery receipts. A drift refusal reports
|
|
78
|
+
the affected paths without printing their skill documents.
|
|
79
|
+
|
|
80
|
+
Claude hook installation sets `syncClaudeAiSkills: false` in user settings to
|
|
81
|
+
stop [account skill synchronization](https://code.claude.com/docs/en/skills#skills-synced-from-claudeai)
|
|
82
|
+
from recreating native copies. The prompt hook checks that setting. Vendor
|
|
83
|
+
migration also inventories `~/.claude/plugins/synced/` and archives its skill
|
|
84
|
+
documents while preserving other plugin files. Whole-plugin synchronization
|
|
85
|
+
remains unchanged because synced plugins can also provide hooks, MCP servers,
|
|
86
|
+
and language servers. A later plugin download that restores a skill document
|
|
87
|
+
will trigger another drift refusal and require review.
|
|
88
|
+
|
|
73
89
|
Native hook invocations must use their installed adapter's selection profile.
|
|
74
90
|
An old client command or environment override naming another profile refuses
|
|
75
91
|
before synchronization or context loading. Review the hook installation and
|
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.8.
|
|
36871
|
+
version: "0.8.8",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51228,11 +51228,26 @@ function treeHash(root) {
|
|
|
51228
51228
|
visit2(root);
|
|
51229
51229
|
return hash2.digest("hex");
|
|
51230
51230
|
}
|
|
51231
|
+
function projectAncestorDirectories(projects) {
|
|
51232
|
+
const directories = new Set;
|
|
51233
|
+
for (const project of projects) {
|
|
51234
|
+
for (let path = resolve10(project), depth = 0;; depth++) {
|
|
51235
|
+
if (depth >= 100)
|
|
51236
|
+
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
51237
|
+
directories.add(path);
|
|
51238
|
+
const parent = dirname10(path);
|
|
51239
|
+
if (parent === path)
|
|
51240
|
+
break;
|
|
51241
|
+
path = parent;
|
|
51242
|
+
}
|
|
51243
|
+
}
|
|
51244
|
+
return [...directories];
|
|
51245
|
+
}
|
|
51231
51246
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
51232
51247
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
51233
51248
|
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join21(home, path), aliases)]);
|
|
51234
51249
|
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join21(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
51235
|
-
for (const project of
|
|
51250
|
+
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
51236
51251
|
for (const [agent, path] of ROOTS)
|
|
51237
51252
|
roots.push([agent, canonicalAgentPath(join21(project, path), aliases)]);
|
|
51238
51253
|
}
|
|
@@ -51335,6 +51350,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
51335
51350
|
if (options.includeVendor) {
|
|
51336
51351
|
for (const agent of ["codex", "claude"])
|
|
51337
51352
|
scanRoot(agent, canonicalAgentPath(join21(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
51353
|
+
scanRoot("claude", canonicalAgentPath(join21(home, ".claude", "plugins", "synced"), aliases), true);
|
|
51338
51354
|
scanRoot("gemini", join21(home, ".gemini", "extensions"), true);
|
|
51339
51355
|
}
|
|
51340
51356
|
const configured = options.configured ? INTEGRATION_AGENTS.flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
@@ -51399,6 +51415,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
51399
51415
|
config.permissions.deny = nativeReady ? denied.filter((rule) => rule !== "Skill") : [...new Set([...denied, "Skill"])];
|
|
51400
51416
|
config.permissions.allow = nativeReady ? [...new Set([...allowed, `Skill(${CLI_BRIDGE_NAME})`])] : allowed;
|
|
51401
51417
|
config.disableBundledSkills = true;
|
|
51418
|
+
config.syncClaudeAiSkills = false;
|
|
51402
51419
|
}
|
|
51403
51420
|
if (agent === "gemini") {
|
|
51404
51421
|
config.skills ??= {};
|
|
@@ -51811,18 +51828,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51811
51828
|
const expected = canonicalAgentPath(join21(home, AGENT_ADAPTERS[agent].root, CLI_BRIDGE_NAME), aliases);
|
|
51812
51829
|
if (!isOwnedCliBridge(expected, [expected]))
|
|
51813
51830
|
throw new Error("NATIVE_SKILL_DRIFT: the native Skills bridge is missing or modified; repair it before continuing");
|
|
51814
|
-
const roots =
|
|
51815
|
-
for (const project of [options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]) {
|
|
51816
|
-
for (let path = resolve10(project), depth = 0;; depth++) {
|
|
51817
|
-
if (depth >= 100)
|
|
51818
|
-
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
51819
|
-
roots.add(path);
|
|
51820
|
-
const parent = dirname10(path);
|
|
51821
|
-
if (parent === path)
|
|
51822
|
-
break;
|
|
51823
|
-
path = parent;
|
|
51824
|
-
}
|
|
51825
|
-
}
|
|
51831
|
+
const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
|
|
51826
51832
|
const visible = (entry) => entry.agent === agent || ["codex", "gemini", "opencode", "hermes"].includes(agent) && entry.path.includes(`${sep5}.agents${sep5}skills${sep5}`) || agent === "opencode" && entry.agent === "claude";
|
|
51827
51833
|
assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases));
|
|
51828
51834
|
const configPath = canonicalAgentPath(join21(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
|
|
@@ -51849,6 +51855,8 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51849
51855
|
}
|
|
51850
51856
|
if (agent === "claude" && (config.disableBundledSkills !== true || config.disableAllHooks === true || !config.permissions?.allow?.includes(`Skill(${CLI_BRIDGE_NAME})`) || config.permissions?.deny?.includes("Skill")))
|
|
51851
51857
|
throw new Error("NATIVE_SKILL_DRIFT: Claude bridge or bundled-skill protection changed; retire copies and run skills hook install");
|
|
51858
|
+
if (agent === "claude" && config.syncClaudeAiSkills !== false)
|
|
51859
|
+
throw new Error("NATIVE_SKILL_DRIFT: Claude account skill synchronization is not disabled (syncClaudeAiSkills); run skills hook install to prevent native copies from returning");
|
|
51852
51860
|
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)))
|
|
51853
51861
|
throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
|
|
51854
51862
|
}
|
|
@@ -51880,8 +51888,15 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51880
51888
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
51881
51889
|
}
|
|
51882
51890
|
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
|
|
51883
|
-
|
|
51884
|
-
|
|
51891
|
+
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledBuiltin(entry));
|
|
51892
|
+
if (unexpected.length) {
|
|
51893
|
+
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
51894
|
+
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")}`);
|
|
51895
|
+
return escaped.length > 768 ? `${escaped.slice(0, 384)}...${escaped.slice(-381)}` : escaped;
|
|
51896
|
+
});
|
|
51897
|
+
const remaining = unexpected.length - paths.length;
|
|
51898
|
+
throw new Error(`NATIVE_SKILL_DRIFT: ${unexpected.length} unexpected native skill copies were found: ${paths.join(", ")}${remaining ? `; ${remaining} more` : ""}. Review skills migrate native --project <working-directory> --include-unmanaged --include-vendor --json before continuing; it inventories that directory and its ancestors. Use --apply after reviewing the archive plan.`);
|
|
51899
|
+
}
|
|
51885
51900
|
recheckRootAliases(aliases);
|
|
51886
51901
|
}
|
|
51887
51902
|
function normalizeAgentHookPrompt(agent, event, prompt) {
|
|
@@ -105609,10 +105624,10 @@ function registerAgentIntegration(parent) {
|
|
|
105609
105624
|
}
|
|
105610
105625
|
});
|
|
105611
105626
|
const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
|
|
105612
|
-
migrate.command("native").option("--project <directory>", "Also inventory
|
|
105627
|
+
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("--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) => {
|
|
105613
105628
|
try {
|
|
105614
105629
|
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync37(options.discoveryInputs, "utf8")) : undefined;
|
|
105615
|
-
const inventory = inventoryNativeSkills(undefined, {
|
|
105630
|
+
const inventory = inventoryNativeSkills(undefined, { projectDirs: [process.cwd(), ...options.project ? [options.project] : []], includeVendor: options.includeVendor, configured: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
|
|
105616
105631
|
const result2 = options.apply ? archiveNativeSkills(inventory, { includeUnmanaged: options.includeUnmanaged, includeVendor: options.includeVendor, allowRootAliases: options.allowRootAliases }) : { entries: [] };
|
|
105617
105632
|
if (options.json)
|
|
105618
105633
|
await writeCliOutput(JSON.stringify({ applied: options.apply, inventory, ...result2 }));
|
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.8.
|
|
20022
|
+
version: "0.8.8",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -83749,7 +83749,7 @@ var require_protocols9 = __commonJS((exports) => {
|
|
|
83749
83749
|
// package.json
|
|
83750
83750
|
var package_default = {
|
|
83751
83751
|
name: "@hasna/skills",
|
|
83752
|
-
version: "0.8.
|
|
83752
|
+
version: "0.8.8",
|
|
83753
83753
|
description: "Skills library for AI coding agents",
|
|
83754
83754
|
type: "module",
|
|
83755
83755
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -76168,7 +76168,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
76168
76168
|
// package.json
|
|
76169
76169
|
var package_default = {
|
|
76170
76170
|
name: "@hasna/skills",
|
|
76171
|
-
version: "0.8.
|
|
76171
|
+
version: "0.8.8",
|
|
76172
76172
|
description: "Skills library for AI coding agents",
|
|
76173
76173
|
type: "module",
|
|
76174
76174
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -11693,11 +11693,26 @@ function treeHash(root) {
|
|
|
11693
11693
|
visit2(root);
|
|
11694
11694
|
return hash2.digest("hex");
|
|
11695
11695
|
}
|
|
11696
|
+
function projectAncestorDirectories(projects) {
|
|
11697
|
+
const directories = new Set;
|
|
11698
|
+
for (const project of projects) {
|
|
11699
|
+
for (let path = resolve9(project), depth = 0;; depth++) {
|
|
11700
|
+
if (depth >= 100)
|
|
11701
|
+
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
11702
|
+
directories.add(path);
|
|
11703
|
+
const parent = dirname10(path);
|
|
11704
|
+
if (parent === path)
|
|
11705
|
+
break;
|
|
11706
|
+
path = parent;
|
|
11707
|
+
}
|
|
11708
|
+
}
|
|
11709
|
+
return [...directories];
|
|
11710
|
+
}
|
|
11696
11711
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
11697
11712
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
11698
11713
|
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join17(home, path), aliases)]);
|
|
11699
11714
|
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join17(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
11700
|
-
for (const project of
|
|
11715
|
+
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
11701
11716
|
for (const [agent, path] of ROOTS)
|
|
11702
11717
|
roots.push([agent, canonicalAgentPath(join17(project, path), aliases)]);
|
|
11703
11718
|
}
|
|
@@ -11800,6 +11815,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
11800
11815
|
if (options.includeVendor) {
|
|
11801
11816
|
for (const agent of ["codex", "claude"])
|
|
11802
11817
|
scanRoot(agent, canonicalAgentPath(join17(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
11818
|
+
scanRoot("claude", canonicalAgentPath(join17(home, ".claude", "plugins", "synced"), aliases), true);
|
|
11803
11819
|
scanRoot("gemini", join17(home, ".gemini", "extensions"), true);
|
|
11804
11820
|
}
|
|
11805
11821
|
const configured = options.configured ? INTEGRATION_AGENTS.flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
@@ -11864,6 +11880,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
11864
11880
|
config.permissions.deny = nativeReady ? denied.filter((rule) => rule !== "Skill") : [...new Set([...denied, "Skill"])];
|
|
11865
11881
|
config.permissions.allow = nativeReady ? [...new Set([...allowed, `Skill(${CLI_BRIDGE_NAME})`])] : allowed;
|
|
11866
11882
|
config.disableBundledSkills = true;
|
|
11883
|
+
config.syncClaudeAiSkills = false;
|
|
11867
11884
|
}
|
|
11868
11885
|
if (agent === "gemini") {
|
|
11869
11886
|
config.skills ??= {};
|
|
@@ -37705,7 +37722,7 @@ import { dirname as dirname14, relative as relative7 } from "path";
|
|
|
37705
37722
|
// package.json
|
|
37706
37723
|
var package_default = {
|
|
37707
37724
|
name: "@hasna/skills",
|
|
37708
|
-
version: "0.8.
|
|
37725
|
+
version: "0.8.8",
|
|
37709
37726
|
description: "Skills library for AI coding agents",
|
|
37710
37727
|
type: "module",
|
|
37711
37728
|
bin: {
|
package/dist/sdk/index.js
CHANGED
|
@@ -96141,7 +96141,7 @@ async function resolveExecutionGrant(input) {
|
|
|
96141
96141
|
// package.json
|
|
96142
96142
|
var package_default = {
|
|
96143
96143
|
name: "@hasna/skills",
|
|
96144
|
-
version: "0.8.
|
|
96144
|
+
version: "0.8.8",
|
|
96145
96145
|
description: "Skills library for AI coding agents",
|
|
96146
96146
|
type: "module",
|
|
96147
96147
|
bin: {
|
|
@@ -97782,11 +97782,26 @@ function treeHash(root) {
|
|
|
97782
97782
|
visit2(root);
|
|
97783
97783
|
return hash2.digest("hex");
|
|
97784
97784
|
}
|
|
97785
|
+
function projectAncestorDirectories(projects) {
|
|
97786
|
+
const directories = new Set;
|
|
97787
|
+
for (const project of projects) {
|
|
97788
|
+
for (let path = resolve8(project), depth = 0;; depth++) {
|
|
97789
|
+
if (depth >= 100)
|
|
97790
|
+
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
97791
|
+
directories.add(path);
|
|
97792
|
+
const parent = dirname9(path);
|
|
97793
|
+
if (parent === path)
|
|
97794
|
+
break;
|
|
97795
|
+
path = parent;
|
|
97796
|
+
}
|
|
97797
|
+
}
|
|
97798
|
+
return [...directories];
|
|
97799
|
+
}
|
|
97785
97800
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
97786
97801
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
97787
97802
|
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join16(home, path), aliases)]);
|
|
97788
97803
|
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join16(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
97789
|
-
for (const project of
|
|
97804
|
+
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
97790
97805
|
for (const [agent, path] of ROOTS)
|
|
97791
97806
|
roots.push([agent, canonicalAgentPath(join16(project, path), aliases)]);
|
|
97792
97807
|
}
|
|
@@ -97889,6 +97904,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
97889
97904
|
if (options.includeVendor) {
|
|
97890
97905
|
for (const agent of ["codex", "claude"])
|
|
97891
97906
|
scanRoot(agent, canonicalAgentPath(join16(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
97907
|
+
scanRoot("claude", canonicalAgentPath(join16(home, ".claude", "plugins", "synced"), aliases), true);
|
|
97892
97908
|
scanRoot("gemini", join16(home, ".gemini", "extensions"), true);
|
|
97893
97909
|
}
|
|
97894
97910
|
const configured = options.configured ? INTEGRATION_AGENTS.flatMap((agent) => resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, canonical: (path) => canonicalAgentPath(path, aliases) }).roots.map((path) => ({ agent, path }))) : [];
|
|
@@ -97953,6 +97969,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
97953
97969
|
config.permissions.deny = nativeReady ? denied.filter((rule) => rule !== "Skill") : [...new Set([...denied, "Skill"])];
|
|
97954
97970
|
config.permissions.allow = nativeReady ? [...new Set([...allowed, `Skill(${CLI_BRIDGE_NAME})`])] : allowed;
|
|
97955
97971
|
config.disableBundledSkills = true;
|
|
97972
|
+
config.syncClaudeAiSkills = false;
|
|
97956
97973
|
}
|
|
97957
97974
|
if (agent === "gemini") {
|
|
97958
97975
|
config.skills ??= {};
|