@hasna/skills 0.8.7 → 0.8.9
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 +45 -4
- package/bin/index.js +237 -60
- package/bin/mcp.js +46 -3
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/option-boundary.d.ts +6 -0
- package/dist/index.js +144 -9
- package/dist/lib/agent-codex.d.ts +10 -0
- package/dist/lib/skill-doc-file.d.ts +9 -0
- package/dist/lib/skillinfo.d.ts +3 -2
- package/dist/sdk/index.js +142 -9
- 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
|
|
@@ -178,9 +194,22 @@ Reviewed Hermes installations need fresh source and directory coverage in their
|
|
|
178
194
|
reviewed bindings without changing existing source-only reviews.
|
|
179
195
|
|
|
180
196
|
Hook installation preserves unrelated configuration, hooks, and plugin assets.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
For Codex it sets `skills.bundled.enabled = false` in user configuration, preserving
|
|
198
|
+
the `skills-cli` bridge and disabling discovered native skill paths. This prevents
|
|
199
|
+
supported Codex clients from installing or loading their bundled `.system` skills.
|
|
200
|
+
The setting and its discovery witness are enrolled in the same configuration
|
|
201
|
+
transaction; existing installations must rerun `skills hook install`. Missing or
|
|
202
|
+
re-enabled bundled protection fails `skills hook check` before context loads.
|
|
203
|
+
Ordinary `[skills.bundled]` and legacy `[[skills.config]]` tables are supported;
|
|
204
|
+
inline or dotted target definitions that require rewriting refuse before changes.
|
|
205
|
+
Convert those definitions to ordinary tables while preserving their values, then
|
|
206
|
+
rerun installation. The control has been verified with Codex 0.153.0, 0.153.4 and
|
|
207
|
+
0.154.0; this is not a claim that every older build supports it. Use a client that
|
|
208
|
+
supports `skills.bundled.enabled`; Skills does not infer support from version text
|
|
209
|
+
or silently substitute path-only protection. Existing exact system-skill trees can
|
|
210
|
+
remain only with their hash-bound disabled paths; migration preserves these package
|
|
211
|
+
files. A client that restores or changes packaged skills requires a fresh inventory
|
|
212
|
+
and disable plan. Native exports are
|
|
184
213
|
refused while managed CLI loading is active. Migration preserves ordinary skill
|
|
185
214
|
directories in private archives; `--include-unmanaged` includes user-authored
|
|
186
215
|
copies, and `--include-vendor` retires vendor `SKILL.md` discovery files while
|
|
@@ -365,6 +394,18 @@ skills grants set default --file ./reviewed-policy.json --if-match <policy-revis
|
|
|
365
394
|
skills grants show default --revision <prior-policy-revision> --json
|
|
366
395
|
```
|
|
367
396
|
|
|
397
|
+
For executable arguments, use `--` after the skill and Skills options:
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
skills run --target local --selection-profile default --json \
|
|
401
|
+
your-skill@1.0.0 -- prepare --input child-input.json --json
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Everything after that separator belongs to the executable, including flags such
|
|
405
|
+
as `--input`, `--json` and any further `--`. Cloud execution accepts structured
|
|
406
|
+
Skills `--input` only and rejects executable arguments. Existing calls without a
|
|
407
|
+
separator retain their option parsing behavior.
|
|
408
|
+
|
|
368
409
|
Updating a policy appends a revision and atomically changes its current pointer.
|
|
369
410
|
An empty `grants` array revokes shared execution access. Historical policies remain
|
|
370
411
|
readable and can be submitted as a new reviewed revision for rollback; they cannot
|
|
@@ -603,7 +644,7 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
|
|
|
603
644
|
| `skills search <query>` | `s` | Search by name, description, or tags |
|
|
604
645
|
| `skills info <name>` | | Show metadata, env vars, and system dependencies |
|
|
605
646
|
| `skills show <name>` | | Show account or owned portable skill details |
|
|
606
|
-
| `skills docs <name>` | | Show documentation
|
|
647
|
+
| `skills docs <name>` | `--file skill\|readme\|claude` | Show preferred documentation; an explicit file must exist and unknown aliases fail |
|
|
607
648
|
| `skills requires <name>` | | Show env vars, system deps, and npm dependencies |
|
|
608
649
|
| `skills profiles show <id>` / `skills profiles set <id> --file <json>` | | Read an exact shared selection or update it with writer authorization |
|
|
609
650
|
| `skills install [name@version] --selection-profile <id>` | | Cache selected immutable bundles; without names, sync the profile |
|
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.9",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -51119,6 +51119,83 @@ var init_agent_adapters = __esm(() => {
|
|
|
51119
51119
|
};
|
|
51120
51120
|
});
|
|
51121
51121
|
|
|
51122
|
+
// src/lib/agent-codex.ts
|
|
51123
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
51124
|
+
function assertCodexPathConfigEditable(text2) {
|
|
51125
|
+
const before2 = Bun.TOML.parse(text2);
|
|
51126
|
+
if (before2.skills?.config === undefined)
|
|
51127
|
+
return;
|
|
51128
|
+
const expected = structuredClone(before2);
|
|
51129
|
+
delete expected.skills.config;
|
|
51130
|
+
try {
|
|
51131
|
+
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
51132
|
+
if (isDeepStrictEqual2(withoutSections, expected))
|
|
51133
|
+
return;
|
|
51134
|
+
} catch {}
|
|
51135
|
+
throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
|
|
51136
|
+
}
|
|
51137
|
+
function disableCodexBundledSkills(text2) {
|
|
51138
|
+
const before2 = Bun.TOML.parse(text2);
|
|
51139
|
+
const table = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
51140
|
+
const refuse = () => new Error("Codex bundled skills configuration requires ordinary [skills.bundled] TOML with a boolean enabled setting; preserve and convert inline or dotted definitions before running skills hook install");
|
|
51141
|
+
if (before2.skills !== undefined && !table(before2.skills))
|
|
51142
|
+
throw refuse();
|
|
51143
|
+
if (before2.skills?.bundled !== undefined && !table(before2.skills.bundled))
|
|
51144
|
+
throw refuse();
|
|
51145
|
+
if (before2.skills !== undefined) {
|
|
51146
|
+
let root = before2, attempts = 0;
|
|
51147
|
+
for (const header of text2.matchAll(/^[ \t]*\[/gm)) {
|
|
51148
|
+
if (++attempts > 256)
|
|
51149
|
+
throw refuse();
|
|
51150
|
+
try {
|
|
51151
|
+
root = Bun.TOML.parse(text2.slice(0, header.index));
|
|
51152
|
+
break;
|
|
51153
|
+
} catch {}
|
|
51154
|
+
}
|
|
51155
|
+
if (root.skills !== undefined)
|
|
51156
|
+
throw refuse();
|
|
51157
|
+
}
|
|
51158
|
+
const enabled = before2.skills?.bundled?.enabled;
|
|
51159
|
+
if (enabled !== undefined && typeof enabled !== "boolean")
|
|
51160
|
+
throw refuse();
|
|
51161
|
+
if (enabled === false)
|
|
51162
|
+
return text2;
|
|
51163
|
+
const expected = structuredClone(before2);
|
|
51164
|
+
expected.skills ??= {};
|
|
51165
|
+
expected.skills.bundled ??= {};
|
|
51166
|
+
expected.skills.bundled.enabled = false;
|
|
51167
|
+
const candidates = new Set;
|
|
51168
|
+
const consider = (candidate) => {
|
|
51169
|
+
try {
|
|
51170
|
+
if (isDeepStrictEqual2(Bun.TOML.parse(candidate), expected))
|
|
51171
|
+
candidates.add(candidate);
|
|
51172
|
+
} catch {}
|
|
51173
|
+
};
|
|
51174
|
+
const newline = text2.includes(`\r
|
|
51175
|
+
`) ? `\r
|
|
51176
|
+
` : `
|
|
51177
|
+
`;
|
|
51178
|
+
if (before2.skills?.bundled !== undefined) {
|
|
51179
|
+
for (const section of text2.matchAll(/^[ \t]*\[skills\.bundled\][ \t]*(?:#[^\r\n]*)?(?:\r?\n|$)(?:(?![ \t]*\[)[^\n]*(?:\n|$))*/gm)) {
|
|
51180
|
+
const headerEnd = section[0].indexOf(`
|
|
51181
|
+
`) + 1 || section[0].length;
|
|
51182
|
+
const replacement = enabled === true ? section[0].replace(/^([ \t]*enabled[ \t]*=[ \t]*)true([ \t]*(?:#[^\r\n]*)?)(\r?)$/m, "$1false$2$3") : section[0].slice(0, headerEnd) + (section[0].slice(0, headerEnd).endsWith(`
|
|
51183
|
+
`) ? "" : newline) + `enabled = false${newline}` + section[0].slice(headerEnd);
|
|
51184
|
+
consider(text2.slice(0, section.index) + replacement + text2.slice(section.index + section[0].length));
|
|
51185
|
+
}
|
|
51186
|
+
} else {
|
|
51187
|
+
consider(`${text2}${text2.endsWith(`
|
|
51188
|
+
`) || !text2 ? "" : newline}${newline}[skills.bundled]${newline}enabled = false${newline}`);
|
|
51189
|
+
}
|
|
51190
|
+
if (candidates.size !== 1)
|
|
51191
|
+
throw refuse();
|
|
51192
|
+
return [...candidates][0];
|
|
51193
|
+
}
|
|
51194
|
+
var CODEX_SKILL_CONFIG_SECTIONS;
|
|
51195
|
+
var init_agent_codex = __esm(() => {
|
|
51196
|
+
CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
51197
|
+
});
|
|
51198
|
+
|
|
51122
51199
|
// src/lib/agent-integration.ts
|
|
51123
51200
|
import { createHash as createHash9, randomUUID as randomUUID5 } from "crypto";
|
|
51124
51201
|
import { existsSync as existsSync14, lstatSync as lstatSync11, statSync as statSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync12, readdirSync as readdirSync9, opendirSync as opendirSync3, readlinkSync as readlinkSync2, realpathSync as realpathSync4, renameSync as renameSync3, rmdirSync, writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, chmodSync, openSync as openSync4, closeSync as closeSync4, fsyncSync, fstatSync as fstatSync4, readSync as readSync4, constants as constants5, linkSync } from "fs";
|
|
@@ -51228,11 +51305,26 @@ function treeHash(root) {
|
|
|
51228
51305
|
visit2(root);
|
|
51229
51306
|
return hash2.digest("hex");
|
|
51230
51307
|
}
|
|
51308
|
+
function projectAncestorDirectories(projects) {
|
|
51309
|
+
const directories = new Set;
|
|
51310
|
+
for (const project of projects) {
|
|
51311
|
+
for (let path = resolve10(project), depth = 0;; depth++) {
|
|
51312
|
+
if (depth >= 100)
|
|
51313
|
+
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
51314
|
+
directories.add(path);
|
|
51315
|
+
const parent = dirname10(path);
|
|
51316
|
+
if (parent === path)
|
|
51317
|
+
break;
|
|
51318
|
+
path = parent;
|
|
51319
|
+
}
|
|
51320
|
+
}
|
|
51321
|
+
return [...directories];
|
|
51322
|
+
}
|
|
51231
51323
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
51232
51324
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
51233
51325
|
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join21(home, path), aliases)]);
|
|
51234
51326
|
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join21(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
51235
|
-
for (const project of
|
|
51327
|
+
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
51236
51328
|
for (const [agent, path] of ROOTS)
|
|
51237
51329
|
roots.push([agent, canonicalAgentPath(join21(project, path), aliases)]);
|
|
51238
51330
|
}
|
|
@@ -51335,6 +51427,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
51335
51427
|
if (options.includeVendor) {
|
|
51336
51428
|
for (const agent of ["codex", "claude"])
|
|
51337
51429
|
scanRoot(agent, canonicalAgentPath(join21(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
51430
|
+
scanRoot("claude", canonicalAgentPath(join21(home, ".claude", "plugins", "synced"), aliases), true);
|
|
51338
51431
|
scanRoot("gemini", join21(home, ".gemini", "extensions"), true);
|
|
51339
51432
|
}
|
|
51340
51433
|
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 +51492,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
51399
51492
|
config.permissions.deny = nativeReady ? denied.filter((rule) => rule !== "Skill") : [...new Set([...denied, "Skill"])];
|
|
51400
51493
|
config.permissions.allow = nativeReady ? [...new Set([...allowed, `Skill(${CLI_BRIDGE_NAME})`])] : allowed;
|
|
51401
51494
|
config.disableBundledSkills = true;
|
|
51495
|
+
config.syncClaudeAiSkills = false;
|
|
51402
51496
|
}
|
|
51403
51497
|
if (agent === "gemini") {
|
|
51404
51498
|
config.skills ??= {};
|
|
@@ -51429,16 +51523,21 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
51429
51523
|
}
|
|
51430
51524
|
}
|
|
51431
51525
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
51432
|
-
|
|
51433
|
-
let result2 =
|
|
51526
|
+
const bundled = disableCodexBundledSkills(text2);
|
|
51527
|
+
let result2 = bundled;
|
|
51528
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve10(bridgePath), resolve10(join21(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
51529
|
+
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
51530
|
+
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
51531
|
+
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
51532
|
+
const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
|
|
51434
51533
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
51435
51534
|
for (const skill of selections) {
|
|
51436
51535
|
const path = join21(skill.path, "SKILL.md");
|
|
51437
51536
|
let found = false;
|
|
51438
|
-
result2 = result2.replace(
|
|
51537
|
+
result2 = result2.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
51439
51538
|
const parsed = Bun.TOML.parse(section);
|
|
51440
|
-
const declared = parsed.skills?.config?.[0]
|
|
51441
|
-
if (!declared || ![resolve10(path), resolve10(skill.path)].includes(canonicalAgentPath(declared, aliases)))
|
|
51539
|
+
const declared = parsed.skills?.config?.[0];
|
|
51540
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve10(path), resolve10(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
51442
51541
|
return section;
|
|
51443
51542
|
found = true;
|
|
51444
51543
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -51453,6 +51552,8 @@ path = ${JSON.stringify(path)}
|
|
|
51453
51552
|
enabled = false
|
|
51454
51553
|
`;
|
|
51455
51554
|
}
|
|
51555
|
+
if (result2 !== bundled || bridgeNeedsRepair)
|
|
51556
|
+
assertCodexPathConfigEditable(bundled);
|
|
51456
51557
|
Bun.TOML.parse(result2);
|
|
51457
51558
|
return result2;
|
|
51458
51559
|
}
|
|
@@ -51811,18 +51912,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51811
51912
|
const expected = canonicalAgentPath(join21(home, AGENT_ADAPTERS[agent].root, CLI_BRIDGE_NAME), aliases);
|
|
51812
51913
|
if (!isOwnedCliBridge(expected, [expected]))
|
|
51813
51914
|
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
|
-
}
|
|
51915
|
+
const roots = projectAncestorDirectories([options.projectDir ?? process.cwd(), ...options.projectDirs ?? [], ...agent === "hermes" && process.env.TERMINAL_CWD ? [process.cwd()] : []]);
|
|
51826
51916
|
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
51917
|
assertProjectDiscovery(agent, [...roots], home, (path) => canonicalAgentPath(path, aliases));
|
|
51828
51918
|
const configPath = canonicalAgentPath(join21(home, AGENT_ADAPTERS[agent].config), aliases), config = agent === "hermes" ? parseHermesConfig(readOptional(configPath)) : jsonObject(readOptional(configPath), configPath);
|
|
@@ -51849,13 +51939,17 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51849
51939
|
}
|
|
51850
51940
|
if (agent === "claude" && (config.disableBundledSkills !== true || config.disableAllHooks === true || !config.permissions?.allow?.includes(`Skill(${CLI_BRIDGE_NAME})`) || config.permissions?.deny?.includes("Skill")))
|
|
51851
51941
|
throw new Error("NATIVE_SKILL_DRIFT: Claude bridge or bundled-skill protection changed; retire copies and run skills hook install");
|
|
51942
|
+
if (agent === "claude" && config.syncClaudeAiSkills !== false)
|
|
51943
|
+
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
51944
|
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
51945
|
throw new Error("NATIVE_SKILL_DRIFT: Gemini bridge or bundled-skill protection changed; run skills hook install");
|
|
51854
51946
|
}
|
|
51855
51947
|
const codexPath = canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases);
|
|
51856
51948
|
const codexConfig = agent === "codex" ? Bun.TOML.parse(readOptional(codexPath) ?? "") : {};
|
|
51949
|
+
if (agent === "codex" && codexConfig.skills?.bundled?.enabled !== false)
|
|
51950
|
+
throw new Error("NATIVE_SKILL_DRIFT: Codex bundled skill reseeding is not disabled (skills.bundled.enabled); run skills hook install");
|
|
51857
51951
|
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join21(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
51858
|
-
if (agent === "codex" && setting(expected).some((entry) => entry.enabled === false))
|
|
51952
|
+
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))
|
|
51859
51953
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
51860
51954
|
const disabledBuiltin = (entry) => {
|
|
51861
51955
|
if (agent !== "codex" || !entry.vendor || !entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep5))
|
|
@@ -51880,8 +51974,15 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51880
51974
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
51881
51975
|
}
|
|
51882
51976
|
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
|
|
51883
|
-
|
|
51884
|
-
|
|
51977
|
+
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledBuiltin(entry));
|
|
51978
|
+
if (unexpected.length) {
|
|
51979
|
+
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
51980
|
+
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")}`);
|
|
51981
|
+
return escaped.length > 768 ? `${escaped.slice(0, 384)}...${escaped.slice(-381)}` : escaped;
|
|
51982
|
+
});
|
|
51983
|
+
const remaining = unexpected.length - paths.length;
|
|
51984
|
+
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.`);
|
|
51985
|
+
}
|
|
51885
51986
|
recheckRootAliases(aliases);
|
|
51886
51987
|
}
|
|
51887
51988
|
function normalizeAgentHookPrompt(agent, event, prompt) {
|
|
@@ -51926,6 +52027,7 @@ var init_agent_integration = __esm(() => {
|
|
|
51926
52027
|
init_agent_bridge();
|
|
51927
52028
|
init_agent_discovery();
|
|
51928
52029
|
init_agent_adapters();
|
|
52030
|
+
init_agent_codex();
|
|
51929
52031
|
init_agent_hermes();
|
|
51930
52032
|
HOOK_EVENTS = ["UserPromptSubmit", "SessionStart", "SubagentStart"];
|
|
51931
52033
|
ROOTS = [
|
|
@@ -73459,6 +73561,46 @@ var init_read_access = __esm(() => {
|
|
|
73459
73561
|
init_remote_registry();
|
|
73460
73562
|
});
|
|
73461
73563
|
|
|
73564
|
+
// src/cli/option-boundary.ts
|
|
73565
|
+
function optionPrefix(program2, args) {
|
|
73566
|
+
const indices = [];
|
|
73567
|
+
let command = program2, runArguments = false;
|
|
73568
|
+
for (let index = 0;index < args.length; index++) {
|
|
73569
|
+
const value = args[index];
|
|
73570
|
+
if (value === "--")
|
|
73571
|
+
return { indices, separator: index };
|
|
73572
|
+
indices.push(index);
|
|
73573
|
+
if (runArguments)
|
|
73574
|
+
continue;
|
|
73575
|
+
const options = [...command.options, ...program2.options];
|
|
73576
|
+
const option = options.find((option2) => option2.long === value || option2.short === value);
|
|
73577
|
+
if (option?.required || option?.optional && args[index + 1] !== undefined && !args[index + 1].startsWith("-")) {
|
|
73578
|
+
index++;
|
|
73579
|
+
continue;
|
|
73580
|
+
}
|
|
73581
|
+
if (!value.startsWith("--") && value.startsWith("-") && value.length > 2) {
|
|
73582
|
+
for (let offset = 1;offset < value.length; offset++) {
|
|
73583
|
+
const short = options.find((option2) => option2.short === `-${value[offset]}`);
|
|
73584
|
+
if (!short)
|
|
73585
|
+
break;
|
|
73586
|
+
if (short.required || short.optional) {
|
|
73587
|
+
if (offset === value.length - 1 && (short.required || args[index + 1] !== undefined && !args[index + 1].startsWith("-")))
|
|
73588
|
+
index++;
|
|
73589
|
+
break;
|
|
73590
|
+
}
|
|
73591
|
+
}
|
|
73592
|
+
}
|
|
73593
|
+
if (value.startsWith("-"))
|
|
73594
|
+
continue;
|
|
73595
|
+
const child = command.commands.find((child2) => child2.name() === value || child2.aliases().includes(value));
|
|
73596
|
+
if (child)
|
|
73597
|
+
command = child;
|
|
73598
|
+
else if (command.name() === "run" && command.parent === program2)
|
|
73599
|
+
runArguments = true;
|
|
73600
|
+
}
|
|
73601
|
+
return { indices, separator: -1 };
|
|
73602
|
+
}
|
|
73603
|
+
|
|
73462
73604
|
// node_modules/zod/v4/classic/index.js
|
|
73463
73605
|
var init_classic = __esm(() => {
|
|
73464
73606
|
init_external();
|
|
@@ -76454,6 +76596,43 @@ async function prepareSkillDependencies(skillPath, env3, timeoutMs = DEFAULT_PRE
|
|
|
76454
76596
|
var DEPENDENCY_PREPARATION_MARKER = ".skills-dependency-preparation", RECOVERY = "Skill dependency preparation is incomplete. Confirm no installer is running, successfully run bun install --no-save in the skill directory, then remove only .skills-dependency-preparation. See docs/skill-standard.md#recovering-interrupted-dependency-preparation.", DEFAULT_PREPARATION_TIMEOUT_MS = 60000;
|
|
76455
76597
|
var init_dependency_preparation = () => {};
|
|
76456
76598
|
|
|
76599
|
+
// src/lib/skill-doc-file.ts
|
|
76600
|
+
function resolveSkillDocFile(file2) {
|
|
76601
|
+
switch (file2) {
|
|
76602
|
+
case undefined:
|
|
76603
|
+
case "":
|
|
76604
|
+
return;
|
|
76605
|
+
case "skill":
|
|
76606
|
+
return "SKILL.md";
|
|
76607
|
+
case "readme":
|
|
76608
|
+
return "README.md";
|
|
76609
|
+
case "claude":
|
|
76610
|
+
return "CLAUDE.md";
|
|
76611
|
+
default:
|
|
76612
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_INVALID", "Unsupported documentation file. Choose skill, readme, or claude.");
|
|
76613
|
+
}
|
|
76614
|
+
}
|
|
76615
|
+
function selectSkillDoc(docs, file2) {
|
|
76616
|
+
const path = resolveSkillDocFile(file2);
|
|
76617
|
+
if (path === undefined)
|
|
76618
|
+
return docs.skillMd || docs.readme || docs.claudeMd || null;
|
|
76619
|
+
const content = path === "SKILL.md" ? docs.skillMd : path === "README.md" ? docs.readme : docs.claudeMd;
|
|
76620
|
+
if (content === null)
|
|
76621
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_MISSING", `Documentation file ${path} was not found.`);
|
|
76622
|
+
return content;
|
|
76623
|
+
}
|
|
76624
|
+
var SkillDocFileError;
|
|
76625
|
+
var init_skill_doc_file = __esm(() => {
|
|
76626
|
+
SkillDocFileError = class SkillDocFileError extends Error {
|
|
76627
|
+
code;
|
|
76628
|
+
constructor(code, message) {
|
|
76629
|
+
super(message);
|
|
76630
|
+
this.code = code;
|
|
76631
|
+
this.name = "SkillDocFileError";
|
|
76632
|
+
}
|
|
76633
|
+
};
|
|
76634
|
+
});
|
|
76635
|
+
|
|
76457
76636
|
// src/lib/skillinfo.ts
|
|
76458
76637
|
var exports_skillinfo = {};
|
|
76459
76638
|
__export(exports_skillinfo, {
|
|
@@ -76491,11 +76670,12 @@ function getSkillDocs(name) {
|
|
|
76491
76670
|
claudeMd: readIfExists(join32(skillPath, "CLAUDE.md"))
|
|
76492
76671
|
};
|
|
76493
76672
|
}
|
|
76494
|
-
function getSkillBestDoc(name) {
|
|
76673
|
+
function getSkillBestDoc(name, file2) {
|
|
76674
|
+
resolveSkillDocFile(file2);
|
|
76495
76675
|
const docs = getSkillDocs(name);
|
|
76496
76676
|
if (!docs)
|
|
76497
76677
|
return null;
|
|
76498
|
-
return docs
|
|
76678
|
+
return selectSkillDoc(docs, file2);
|
|
76499
76679
|
}
|
|
76500
76680
|
function getSkillRequirements(name) {
|
|
76501
76681
|
const skillPath = getSkillPath(name);
|
|
@@ -76815,6 +76995,7 @@ var init_skillinfo = __esm(() => {
|
|
|
76815
76995
|
init_installer();
|
|
76816
76996
|
init_registry();
|
|
76817
76997
|
init_skill_validation();
|
|
76998
|
+
init_skill_doc_file();
|
|
76818
76999
|
HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
76819
77000
|
"OPENAI_",
|
|
76820
77001
|
"ANTHROPIC_",
|
|
@@ -77994,7 +78175,7 @@ function skillNotFound(name, similar = findSimilarSkills(name)) {
|
|
|
77994
78175
|
console.error(source_default.dim(`Did you mean: ${similar.join(", ")}?`));
|
|
77995
78176
|
}
|
|
77996
78177
|
function handleReadRefusal(error61) {
|
|
77997
|
-
if (!isSkillsFleetCredentialError(error61))
|
|
78178
|
+
if (!isSkillsFleetCredentialError(error61) && !(error61 instanceof SkillDocFileError))
|
|
77998
78179
|
throw error61;
|
|
77999
78180
|
console.error(source_default.red(error61.message));
|
|
78000
78181
|
process.exitCode = 1;
|
|
@@ -78084,9 +78265,9 @@ async function resolveRemoteNotFound(name, remote, message) {
|
|
|
78084
78265
|
}
|
|
78085
78266
|
}
|
|
78086
78267
|
async function handleDocs(name, options) {
|
|
78268
|
+
const file2 = resolveSkillDocFile(options.file);
|
|
78087
78269
|
const access = await requireSkillsReadAccess();
|
|
78088
78270
|
if (access.mode === "hosted") {
|
|
78089
|
-
const file2 = options.file === "readme" ? "README.md" : options.file === "claude" ? "CLAUDE.md" : options.file ? "SKILL.md" : undefined;
|
|
78090
78271
|
const result2 = await loadSelectedSkill(name, selectedProfileId(options.selectionProfile), { projectDir: process.cwd(), file: file2 });
|
|
78091
78272
|
console.log(options.json ? JSON.stringify({ skill: name, ...result2 }) : result2.content);
|
|
78092
78273
|
return;
|
|
@@ -78100,37 +78281,19 @@ async function handleDocs(name, options) {
|
|
|
78100
78281
|
process.exitCode = 1;
|
|
78101
78282
|
return;
|
|
78102
78283
|
}
|
|
78284
|
+
const content = selectSkillDoc(docs, options.file);
|
|
78103
78285
|
if (options.json) {
|
|
78104
78286
|
console.log(JSON.stringify({
|
|
78105
78287
|
skill: name,
|
|
78106
78288
|
hasSkillMd: docs.skillMd !== null,
|
|
78107
78289
|
hasReadme: docs.readme !== null,
|
|
78108
78290
|
hasClaudeMd: docs.claudeMd !== null,
|
|
78109
|
-
content
|
|
78291
|
+
content
|
|
78110
78292
|
}, null, 2));
|
|
78111
78293
|
return;
|
|
78112
78294
|
}
|
|
78113
|
-
|
|
78114
|
-
|
|
78115
|
-
content = docs.skillMd;
|
|
78116
|
-
else if (options.file === "readme")
|
|
78117
|
-
content = docs.readme;
|
|
78118
|
-
else if (options.file === "claude")
|
|
78119
|
-
content = docs.claudeMd;
|
|
78120
|
-
else
|
|
78121
|
-
content = docs.skillMd || docs.readme || docs.claudeMd;
|
|
78122
|
-
if (!content) {
|
|
78123
|
-
const available = [];
|
|
78124
|
-
if (docs.skillMd)
|
|
78125
|
-
available.push("skill");
|
|
78126
|
-
if (docs.readme)
|
|
78127
|
-
available.push("readme");
|
|
78128
|
-
if (docs.claudeMd)
|
|
78129
|
-
available.push("claude");
|
|
78130
|
-
if (!available.length)
|
|
78131
|
-
console.log(source_default.dim(`No documentation found for '${name}'`));
|
|
78132
|
-
else
|
|
78133
|
-
console.log(source_default.dim(`File '${options.file}' not found. Available: ${available.join(", ")}`));
|
|
78295
|
+
if (content === null) {
|
|
78296
|
+
console.log(source_default.dim(`No documentation found for '${name}'`));
|
|
78134
78297
|
return;
|
|
78135
78298
|
}
|
|
78136
78299
|
console.log(content);
|
|
@@ -78295,6 +78458,7 @@ var init_introspect = __esm(() => {
|
|
|
78295
78458
|
init_fleet_credentials();
|
|
78296
78459
|
init_read_access();
|
|
78297
78460
|
init_skillinfo();
|
|
78461
|
+
init_skill_doc_file();
|
|
78298
78462
|
init_installer();
|
|
78299
78463
|
init_skill_validation();
|
|
78300
78464
|
init_portable_skills();
|
|
@@ -81581,7 +81745,7 @@ var init_execution_grants = __esm(() => {
|
|
|
81581
81745
|
});
|
|
81582
81746
|
|
|
81583
81747
|
// src/lib/execution-grant-client.ts
|
|
81584
|
-
import { isDeepStrictEqual as
|
|
81748
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
81585
81749
|
function id(value) {
|
|
81586
81750
|
if (!grantIdentifier(value))
|
|
81587
81751
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -81633,7 +81797,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
81633
81797
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
81634
81798
|
body: JSON.stringify({ grants: snapshot })
|
|
81635
81799
|
}));
|
|
81636
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
81800
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual3(policy.grants, snapshot)) {
|
|
81637
81801
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
81638
81802
|
}
|
|
81639
81803
|
return policy;
|
|
@@ -81644,7 +81808,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
81644
81808
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
81645
81809
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
81646
81810
|
}
|
|
81647
|
-
if (!
|
|
81811
|
+
if (!isDeepStrictEqual3(value.bindings.selection, snapshot.selection) || !isDeepStrictEqual3(value.bindings.consumer, snapshot.consumer)) {
|
|
81648
81812
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
81649
81813
|
}
|
|
81650
81814
|
return value;
|
|
@@ -103274,7 +103438,12 @@ import { lstatSync as lstatSync21, mkdirSync as mkdirSync22, readFileSync as rea
|
|
|
103274
103438
|
import { basename as basename6, isAbsolute as isAbsolute15, join as join49 } from "path";
|
|
103275
103439
|
import { createInterface } from "readline";
|
|
103276
103440
|
function registerRuntime(parent) {
|
|
103277
|
-
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options) =>
|
|
103441
|
+
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill; use -- to end Skills options").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--remote", "Run on the configured server, using its catalog and quote", false).option("--target <target>", "Execution target: local or cloud").option("--secret-bindings <path>", "Reviewed, exact-selection local execution vault bindings (JSON file)").option("--secret-bindings-template", "Print a scoped bindings template without resolving secrets or running the skill", false).option("--input <json>", "Structured cloud execution input").option("--skill-version <version>", "Exact selected skill version").option("--selection-profile <id>", "Selected Skills profile").option("--session <id>", "Pinned Skills session").option("--cached", "Use the explicit verified selected cache", false).option("--yes", "Approve the server's quoted credit cost for this run", false).option("--idempotency-key <key>", "Reuse this key when retrying the same remote submission").option("--file <path>", "Attach a local input file to a remote run (repeatable)", (value, prior) => [...prior, value], []).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options, command) => {
|
|
103442
|
+
const parentArgs = command.parent.args;
|
|
103443
|
+
const prefix = parentArgs.slice(0, parentArgs.length - command.args.length);
|
|
103444
|
+
const separatorBeforeSkill = optionPrefix(command.parent, prefix).separator >= 0;
|
|
103445
|
+
await handleRun(name, args2, options, separatorBeforeSkill);
|
|
103446
|
+
});
|
|
103278
103447
|
const executions = parent.command("executions").description("Inspect isolated cloud executions");
|
|
103279
103448
|
for (const operation of ["show", "logs", "artifacts", "cancel"]) {
|
|
103280
103449
|
executions.command(operation).argument("<execution-id>").option("--json", "Output as JSON", false).action(async (id2) => {
|
|
@@ -103536,7 +103705,7 @@ function promptLine(question) {
|
|
|
103536
103705
|
});
|
|
103537
103706
|
});
|
|
103538
103707
|
}
|
|
103539
|
-
async function handleRun(name, args2, options) {
|
|
103708
|
+
async function handleRun(name, args2, options, separatorBeforeSkill) {
|
|
103540
103709
|
let managed;
|
|
103541
103710
|
try {
|
|
103542
103711
|
managed = requiresCliSkillLoading();
|
|
@@ -103545,6 +103714,12 @@ async function handleRun(name, args2, options) {
|
|
|
103545
103714
|
process.exitCode = 1;
|
|
103546
103715
|
return;
|
|
103547
103716
|
}
|
|
103717
|
+
const separator = args2.indexOf("--");
|
|
103718
|
+
const childArgs = separatorBeforeSkill ? args2 : separator < 0 ? [] : args2.slice(separator + 1);
|
|
103719
|
+
if (separatorBeforeSkill)
|
|
103720
|
+
args2 = [];
|
|
103721
|
+
else if (separator >= 0)
|
|
103722
|
+
args2 = args2.slice(0, separator);
|
|
103548
103723
|
const targetIndex = args2.indexOf("--target");
|
|
103549
103724
|
if (managed && !options.remote || options.target || targetIndex >= 0 || options.secretBindings || options.secretBindingsTemplate || args2.includes("--secret-bindings") || args2.includes("--secret-bindings-template")) {
|
|
103550
103725
|
options = { ...options };
|
|
@@ -103570,6 +103745,7 @@ async function handleRun(name, args2, options) {
|
|
|
103570
103745
|
}
|
|
103571
103746
|
}
|
|
103572
103747
|
}
|
|
103748
|
+
args2 = [...args2, ...childArgs];
|
|
103573
103749
|
if (options.remote && options.target) {
|
|
103574
103750
|
console.error("Conflicting --remote and --target execution modes");
|
|
103575
103751
|
process.exitCode = 1;
|
|
@@ -105609,10 +105785,10 @@ function registerAgentIntegration(parent) {
|
|
|
105609
105785
|
}
|
|
105610
105786
|
});
|
|
105611
105787
|
const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
|
|
105612
|
-
migrate.command("native").option("--project <directory>", "Also inventory
|
|
105788
|
+
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
105789
|
try {
|
|
105614
105790
|
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync37(options.discoveryInputs, "utf8")) : undefined;
|
|
105615
|
-
const inventory = inventoryNativeSkills(undefined, {
|
|
105791
|
+
const inventory = inventoryNativeSkills(undefined, { projectDirs: [process.cwd(), ...options.project ? [options.project] : []], includeVendor: options.includeVendor, configured: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
|
|
105616
105792
|
const result2 = options.apply ? archiveNativeSkills(inventory, { includeUnmanaged: options.includeUnmanaged, includeVendor: options.includeVendor, allowRootAliases: options.allowRootAliases }) : { entries: [] };
|
|
105617
105793
|
if (options.json)
|
|
105618
105794
|
await writeCliOutput(JSON.stringify({ applied: options.apply, inventory, ...result2 }));
|
|
@@ -116915,11 +117091,6 @@ init_fleet_credentials();
|
|
|
116915
117091
|
init_read_access();
|
|
116916
117092
|
var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
|
|
116917
117093
|
var isTTY2 = (process.stdout.isTTY ?? false) && (process.stdin.isTTY ?? false);
|
|
116918
|
-
if (process.argv.includes("--no-color")) {
|
|
116919
|
-
source_default.level = 0;
|
|
116920
|
-
const idx = process.argv.indexOf("--no-color");
|
|
116921
|
-
process.argv.splice(idx, 1);
|
|
116922
|
-
}
|
|
116923
117094
|
var program2 = new Command;
|
|
116924
117095
|
program2.name("skills").description("Discover and run AI agent skills through the Skills CLI/MCP").version(package_default.version).option("--verbose", "Enable verbose logging", false).option("--profile <name>", "Use an isolated Skills instance credential profile").option("--no-color", "Disable colored output (also respects NO_COLOR env var)").enablePositionalOptions();
|
|
116925
117096
|
program2.hook("preAction", () => {
|
|
@@ -117003,6 +117174,12 @@ registerStorage2(program2);
|
|
|
117003
117174
|
var { registerRegistryReconcile: registerRegistryReconcile2 } = await Promise.resolve().then(() => (init_registry_reconcile2(), exports_registry_reconcile));
|
|
117004
117175
|
registerRegistryReconcile2(program2);
|
|
117005
117176
|
registerEventsCommands(program2, { source: "skills" });
|
|
117177
|
+
var cliArgs = process.argv.slice(2);
|
|
117178
|
+
var colorFlag = optionPrefix(program2, cliArgs).indices.find((index) => cliArgs[index] === "--no-color");
|
|
117179
|
+
if (colorFlag !== undefined) {
|
|
117180
|
+
source_default.level = 0;
|
|
117181
|
+
process.argv.splice(colorFlag + 2, 1);
|
|
117182
|
+
}
|
|
117006
117183
|
try {
|
|
117007
117184
|
await program2.parseAsync();
|
|
117008
117185
|
} catch (err) {
|
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.9",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -47384,6 +47384,9 @@ var init_agent_discovery = __esm(() => {
|
|
|
47384
47384
|
// src/lib/agent-adapters.ts
|
|
47385
47385
|
var init_agent_adapters = () => {};
|
|
47386
47386
|
|
|
47387
|
+
// src/lib/agent-codex.ts
|
|
47388
|
+
var init_agent_codex = () => {};
|
|
47389
|
+
|
|
47387
47390
|
// src/lib/agent-integration.ts
|
|
47388
47391
|
var init_agent_integration = __esm(() => {
|
|
47389
47392
|
init_config();
|
|
@@ -47391,6 +47394,7 @@ var init_agent_integration = __esm(() => {
|
|
|
47391
47394
|
init_agent_bridge();
|
|
47392
47395
|
init_agent_discovery();
|
|
47393
47396
|
init_agent_adapters();
|
|
47397
|
+
init_agent_codex();
|
|
47394
47398
|
init_agent_hermes();
|
|
47395
47399
|
});
|
|
47396
47400
|
|
|
@@ -47649,6 +47653,43 @@ var init_installer = __esm(() => {
|
|
|
47649
47653
|
};
|
|
47650
47654
|
});
|
|
47651
47655
|
|
|
47656
|
+
// src/lib/skill-doc-file.ts
|
|
47657
|
+
function resolveSkillDocFile(file2) {
|
|
47658
|
+
switch (file2) {
|
|
47659
|
+
case undefined:
|
|
47660
|
+
case "":
|
|
47661
|
+
return;
|
|
47662
|
+
case "skill":
|
|
47663
|
+
return "SKILL.md";
|
|
47664
|
+
case "readme":
|
|
47665
|
+
return "README.md";
|
|
47666
|
+
case "claude":
|
|
47667
|
+
return "CLAUDE.md";
|
|
47668
|
+
default:
|
|
47669
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_INVALID", "Unsupported documentation file. Choose skill, readme, or claude.");
|
|
47670
|
+
}
|
|
47671
|
+
}
|
|
47672
|
+
function selectSkillDoc(docs, file2) {
|
|
47673
|
+
const path = resolveSkillDocFile(file2);
|
|
47674
|
+
if (path === undefined)
|
|
47675
|
+
return docs.skillMd || docs.readme || docs.claudeMd || null;
|
|
47676
|
+
const content = path === "SKILL.md" ? docs.skillMd : path === "README.md" ? docs.readme : docs.claudeMd;
|
|
47677
|
+
if (content === null)
|
|
47678
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_MISSING", `Documentation file ${path} was not found.`);
|
|
47679
|
+
return content;
|
|
47680
|
+
}
|
|
47681
|
+
var SkillDocFileError;
|
|
47682
|
+
var init_skill_doc_file = __esm(() => {
|
|
47683
|
+
SkillDocFileError = class SkillDocFileError extends Error {
|
|
47684
|
+
code;
|
|
47685
|
+
constructor(code, message) {
|
|
47686
|
+
super(message);
|
|
47687
|
+
this.code = code;
|
|
47688
|
+
this.name = "SkillDocFileError";
|
|
47689
|
+
}
|
|
47690
|
+
};
|
|
47691
|
+
});
|
|
47692
|
+
|
|
47652
47693
|
// src/lib/skillinfo.ts
|
|
47653
47694
|
import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
|
|
47654
47695
|
import { join as join18 } from "path";
|
|
@@ -47674,11 +47715,12 @@ function getSkillDocs(name) {
|
|
|
47674
47715
|
claudeMd: readIfExists(join18(skillPath, "CLAUDE.md"))
|
|
47675
47716
|
};
|
|
47676
47717
|
}
|
|
47677
|
-
function getSkillBestDoc(name) {
|
|
47718
|
+
function getSkillBestDoc(name, file2) {
|
|
47719
|
+
resolveSkillDocFile(file2);
|
|
47678
47720
|
const docs = getSkillDocs(name);
|
|
47679
47721
|
if (!docs)
|
|
47680
47722
|
return null;
|
|
47681
|
-
return docs
|
|
47723
|
+
return selectSkillDoc(docs, file2);
|
|
47682
47724
|
}
|
|
47683
47725
|
function getSkillRequirements(name) {
|
|
47684
47726
|
const skillPath = getSkillPath(name);
|
|
@@ -47856,6 +47898,7 @@ var init_skillinfo = __esm(() => {
|
|
|
47856
47898
|
init_installer();
|
|
47857
47899
|
init_registry();
|
|
47858
47900
|
init_skill_validation();
|
|
47901
|
+
init_skill_doc_file();
|
|
47859
47902
|
HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
47860
47903
|
"OPENAI_",
|
|
47861
47904
|
"ANTHROPIC_",
|
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.9",
|
|
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.9",
|
|
76172
76172
|
description: "Skills library for AI coding agents",
|
|
76173
76173
|
type: "module",
|
|
76174
76174
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -11570,6 +11570,80 @@ export default async ({ directory }) => {
|
|
|
11570
11570
|
`;
|
|
11571
11571
|
}
|
|
11572
11572
|
|
|
11573
|
+
// src/lib/agent-codex.ts
|
|
11574
|
+
import { isDeepStrictEqual } from "util";
|
|
11575
|
+
var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
11576
|
+
function assertCodexPathConfigEditable(text2) {
|
|
11577
|
+
const before = Bun.TOML.parse(text2);
|
|
11578
|
+
if (before.skills?.config === undefined)
|
|
11579
|
+
return;
|
|
11580
|
+
const expected = structuredClone(before);
|
|
11581
|
+
delete expected.skills.config;
|
|
11582
|
+
try {
|
|
11583
|
+
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
11584
|
+
if (isDeepStrictEqual(withoutSections, expected))
|
|
11585
|
+
return;
|
|
11586
|
+
} catch {}
|
|
11587
|
+
throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
|
|
11588
|
+
}
|
|
11589
|
+
function disableCodexBundledSkills(text2) {
|
|
11590
|
+
const before = Bun.TOML.parse(text2);
|
|
11591
|
+
const table = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
11592
|
+
const refuse = () => new Error("Codex bundled skills configuration requires ordinary [skills.bundled] TOML with a boolean enabled setting; preserve and convert inline or dotted definitions before running skills hook install");
|
|
11593
|
+
if (before.skills !== undefined && !table(before.skills))
|
|
11594
|
+
throw refuse();
|
|
11595
|
+
if (before.skills?.bundled !== undefined && !table(before.skills.bundled))
|
|
11596
|
+
throw refuse();
|
|
11597
|
+
if (before.skills !== undefined) {
|
|
11598
|
+
let root = before, attempts = 0;
|
|
11599
|
+
for (const header of text2.matchAll(/^[ \t]*\[/gm)) {
|
|
11600
|
+
if (++attempts > 256)
|
|
11601
|
+
throw refuse();
|
|
11602
|
+
try {
|
|
11603
|
+
root = Bun.TOML.parse(text2.slice(0, header.index));
|
|
11604
|
+
break;
|
|
11605
|
+
} catch {}
|
|
11606
|
+
}
|
|
11607
|
+
if (root.skills !== undefined)
|
|
11608
|
+
throw refuse();
|
|
11609
|
+
}
|
|
11610
|
+
const enabled = before.skills?.bundled?.enabled;
|
|
11611
|
+
if (enabled !== undefined && typeof enabled !== "boolean")
|
|
11612
|
+
throw refuse();
|
|
11613
|
+
if (enabled === false)
|
|
11614
|
+
return text2;
|
|
11615
|
+
const expected = structuredClone(before);
|
|
11616
|
+
expected.skills ??= {};
|
|
11617
|
+
expected.skills.bundled ??= {};
|
|
11618
|
+
expected.skills.bundled.enabled = false;
|
|
11619
|
+
const candidates = new Set;
|
|
11620
|
+
const consider = (candidate) => {
|
|
11621
|
+
try {
|
|
11622
|
+
if (isDeepStrictEqual(Bun.TOML.parse(candidate), expected))
|
|
11623
|
+
candidates.add(candidate);
|
|
11624
|
+
} catch {}
|
|
11625
|
+
};
|
|
11626
|
+
const newline = text2.includes(`\r
|
|
11627
|
+
`) ? `\r
|
|
11628
|
+
` : `
|
|
11629
|
+
`;
|
|
11630
|
+
if (before.skills?.bundled !== undefined) {
|
|
11631
|
+
for (const section of text2.matchAll(/^[ \t]*\[skills\.bundled\][ \t]*(?:#[^\r\n]*)?(?:\r?\n|$)(?:(?![ \t]*\[)[^\n]*(?:\n|$))*/gm)) {
|
|
11632
|
+
const headerEnd = section[0].indexOf(`
|
|
11633
|
+
`) + 1 || section[0].length;
|
|
11634
|
+
const replacement = enabled === true ? section[0].replace(/^([ \t]*enabled[ \t]*=[ \t]*)true([ \t]*(?:#[^\r\n]*)?)(\r?)$/m, "$1false$2$3") : section[0].slice(0, headerEnd) + (section[0].slice(0, headerEnd).endsWith(`
|
|
11635
|
+
`) ? "" : newline) + `enabled = false${newline}` + section[0].slice(headerEnd);
|
|
11636
|
+
consider(text2.slice(0, section.index) + replacement + text2.slice(section.index + section[0].length));
|
|
11637
|
+
}
|
|
11638
|
+
} else {
|
|
11639
|
+
consider(`${text2}${text2.endsWith(`
|
|
11640
|
+
`) || !text2 ? "" : newline}${newline}[skills.bundled]${newline}enabled = false${newline}`);
|
|
11641
|
+
}
|
|
11642
|
+
if (candidates.size !== 1)
|
|
11643
|
+
throw refuse();
|
|
11644
|
+
return [...candidates][0];
|
|
11645
|
+
}
|
|
11646
|
+
|
|
11573
11647
|
// src/lib/agent-integration.ts
|
|
11574
11648
|
var sha = (data) => createHash9("sha256").update(data).digest("hex");
|
|
11575
11649
|
var ROOTS = [
|
|
@@ -11693,11 +11767,26 @@ function treeHash(root) {
|
|
|
11693
11767
|
visit2(root);
|
|
11694
11768
|
return hash2.digest("hex");
|
|
11695
11769
|
}
|
|
11770
|
+
function projectAncestorDirectories(projects) {
|
|
11771
|
+
const directories = new Set;
|
|
11772
|
+
for (const project of projects) {
|
|
11773
|
+
for (let path = resolve9(project), depth = 0;; depth++) {
|
|
11774
|
+
if (depth >= 100)
|
|
11775
|
+
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
11776
|
+
directories.add(path);
|
|
11777
|
+
const parent = dirname10(path);
|
|
11778
|
+
if (parent === path)
|
|
11779
|
+
break;
|
|
11780
|
+
path = parent;
|
|
11781
|
+
}
|
|
11782
|
+
}
|
|
11783
|
+
return [...directories];
|
|
11784
|
+
}
|
|
11696
11785
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
11697
11786
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
11698
11787
|
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join17(home, path), aliases)]);
|
|
11699
11788
|
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join17(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
11700
|
-
for (const project of
|
|
11789
|
+
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
11701
11790
|
for (const [agent, path] of ROOTS)
|
|
11702
11791
|
roots.push([agent, canonicalAgentPath(join17(project, path), aliases)]);
|
|
11703
11792
|
}
|
|
@@ -11800,6 +11889,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
11800
11889
|
if (options.includeVendor) {
|
|
11801
11890
|
for (const agent of ["codex", "claude"])
|
|
11802
11891
|
scanRoot(agent, canonicalAgentPath(join17(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
11892
|
+
scanRoot("claude", canonicalAgentPath(join17(home, ".claude", "plugins", "synced"), aliases), true);
|
|
11803
11893
|
scanRoot("gemini", join17(home, ".gemini", "extensions"), true);
|
|
11804
11894
|
}
|
|
11805
11895
|
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 +11954,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
11864
11954
|
config.permissions.deny = nativeReady ? denied.filter((rule) => rule !== "Skill") : [...new Set([...denied, "Skill"])];
|
|
11865
11955
|
config.permissions.allow = nativeReady ? [...new Set([...allowed, `Skill(${CLI_BRIDGE_NAME})`])] : allowed;
|
|
11866
11956
|
config.disableBundledSkills = true;
|
|
11957
|
+
config.syncClaudeAiSkills = false;
|
|
11867
11958
|
}
|
|
11868
11959
|
if (agent === "gemini") {
|
|
11869
11960
|
config.skills ??= {};
|
|
@@ -11894,16 +11985,21 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
11894
11985
|
}
|
|
11895
11986
|
}
|
|
11896
11987
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
11897
|
-
|
|
11898
|
-
let result =
|
|
11988
|
+
const bundled = disableCodexBundledSkills(text2);
|
|
11989
|
+
let result = bundled;
|
|
11990
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve9(bridgePath), resolve9(join17(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
11991
|
+
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
11992
|
+
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
11993
|
+
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
11994
|
+
const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
|
|
11899
11995
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
11900
11996
|
for (const skill of selections) {
|
|
11901
11997
|
const path = join17(skill.path, "SKILL.md");
|
|
11902
11998
|
let found = false;
|
|
11903
|
-
result = result.replace(
|
|
11999
|
+
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
11904
12000
|
const parsed = Bun.TOML.parse(section);
|
|
11905
|
-
const declared = parsed.skills?.config?.[0]
|
|
11906
|
-
if (!declared || ![resolve9(path), resolve9(skill.path)].includes(canonicalAgentPath(declared, aliases)))
|
|
12001
|
+
const declared = parsed.skills?.config?.[0];
|
|
12002
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve9(path), resolve9(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
11907
12003
|
return section;
|
|
11908
12004
|
found = true;
|
|
11909
12005
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -11918,6 +12014,8 @@ path = ${JSON.stringify(path)}
|
|
|
11918
12014
|
enabled = false
|
|
11919
12015
|
`;
|
|
11920
12016
|
}
|
|
12017
|
+
if (result !== bundled || bridgeNeedsRepair)
|
|
12018
|
+
assertCodexPathConfigEditable(bundled);
|
|
11921
12019
|
Bun.TOML.parse(result);
|
|
11922
12020
|
return result;
|
|
11923
12021
|
}
|
|
@@ -13403,6 +13501,42 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
13403
13501
|
// src/lib/skillinfo.ts
|
|
13404
13502
|
import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
|
|
13405
13503
|
import { join as join23 } from "path";
|
|
13504
|
+
|
|
13505
|
+
// src/lib/skill-doc-file.ts
|
|
13506
|
+
class SkillDocFileError extends Error {
|
|
13507
|
+
code;
|
|
13508
|
+
constructor(code, message) {
|
|
13509
|
+
super(message);
|
|
13510
|
+
this.code = code;
|
|
13511
|
+
this.name = "SkillDocFileError";
|
|
13512
|
+
}
|
|
13513
|
+
}
|
|
13514
|
+
function resolveSkillDocFile(file) {
|
|
13515
|
+
switch (file) {
|
|
13516
|
+
case undefined:
|
|
13517
|
+
case "":
|
|
13518
|
+
return;
|
|
13519
|
+
case "skill":
|
|
13520
|
+
return "SKILL.md";
|
|
13521
|
+
case "readme":
|
|
13522
|
+
return "README.md";
|
|
13523
|
+
case "claude":
|
|
13524
|
+
return "CLAUDE.md";
|
|
13525
|
+
default:
|
|
13526
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_INVALID", "Unsupported documentation file. Choose skill, readme, or claude.");
|
|
13527
|
+
}
|
|
13528
|
+
}
|
|
13529
|
+
function selectSkillDoc(docs, file) {
|
|
13530
|
+
const path = resolveSkillDocFile(file);
|
|
13531
|
+
if (path === undefined)
|
|
13532
|
+
return docs.skillMd || docs.readme || docs.claudeMd || null;
|
|
13533
|
+
const content = path === "SKILL.md" ? docs.skillMd : path === "README.md" ? docs.readme : docs.claudeMd;
|
|
13534
|
+
if (content === null)
|
|
13535
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_MISSING", `Documentation file ${path} was not found.`);
|
|
13536
|
+
return content;
|
|
13537
|
+
}
|
|
13538
|
+
|
|
13539
|
+
// src/lib/skillinfo.ts
|
|
13406
13540
|
function isInstructionSkillDir(skillPath, meta) {
|
|
13407
13541
|
if (meta?.kind === "instruction")
|
|
13408
13542
|
return true;
|
|
@@ -13441,11 +13575,12 @@ function getSkillDocs(name) {
|
|
|
13441
13575
|
claudeMd: readIfExists(join23(skillPath, "CLAUDE.md"))
|
|
13442
13576
|
};
|
|
13443
13577
|
}
|
|
13444
|
-
function getSkillBestDoc(name) {
|
|
13578
|
+
function getSkillBestDoc(name, file) {
|
|
13579
|
+
resolveSkillDocFile(file);
|
|
13445
13580
|
const docs = getSkillDocs(name);
|
|
13446
13581
|
if (!docs)
|
|
13447
13582
|
return null;
|
|
13448
|
-
return docs
|
|
13583
|
+
return selectSkillDoc(docs, file);
|
|
13449
13584
|
}
|
|
13450
13585
|
function getSkillRequirements(name) {
|
|
13451
13586
|
const skillPath = getSkillPath(name);
|
|
@@ -37705,7 +37840,7 @@ import { dirname as dirname14, relative as relative7 } from "path";
|
|
|
37705
37840
|
// package.json
|
|
37706
37841
|
var package_default = {
|
|
37707
37842
|
name: "@hasna/skills",
|
|
37708
|
-
version: "0.8.
|
|
37843
|
+
version: "0.8.9",
|
|
37709
37844
|
description: "Skills library for AI coding agents",
|
|
37710
37845
|
type: "module",
|
|
37711
37846
|
bin: {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const CODEX_SKILL_CONFIG_SECTIONS: RegExp;
|
|
2
|
+
/** The path editor supports ordinary array-of-table sections only. Removing
|
|
3
|
+
* those sections must remove exactly the parsed path array, with all other
|
|
4
|
+
* values unchanged. This also excludes header-like text inside user strings. */
|
|
5
|
+
export declare function assertCodexPathConfigEditable(text: string): void;
|
|
6
|
+
/** Preserve user TOML verbatim except for the supported bundled-skill control.
|
|
7
|
+
* Bun's parser accepts some illegal table reopenings, so never append a table
|
|
8
|
+
* over an existing inline/dotted skills definition. Refuse unfamiliar layouts
|
|
9
|
+
* before the integration transaction writes any files. */
|
|
10
|
+
export declare function disableCodexBundledSkills(text: string): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SkillDocs } from "./skillinfo.js";
|
|
2
|
+
export declare class SkillDocFileError extends Error {
|
|
3
|
+
readonly code: "SKILL_DOC_FILE_INVALID" | "SKILL_DOC_FILE_MISSING";
|
|
4
|
+
constructor(code: "SKILL_DOC_FILE_INVALID" | "SKILL_DOC_FILE_MISSING", message: string);
|
|
5
|
+
}
|
|
6
|
+
/** The docs command's aliases are not arbitrary bundle paths (skills load supports those). */
|
|
7
|
+
export declare function resolveSkillDocFile(file?: string): "SKILL.md" | "README.md" | "CLAUDE.md" | undefined;
|
|
8
|
+
/** Fall back only when no file was requested, identically for text, JSON and SDK reads. */
|
|
9
|
+
export declare function selectSkillDoc(docs: SkillDocs, file?: string): string | null;
|
package/dist/lib/skillinfo.d.ts
CHANGED
|
@@ -15,9 +15,10 @@ export interface SkillRequirements {
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function getSkillDocs(name: string): SkillDocs | null;
|
|
17
17
|
/**
|
|
18
|
-
* Get
|
|
18
|
+
* Get preferred documentation, or an exact docs alias (skill, readme, claude).
|
|
19
|
+
* Unknown aliases and missing explicitly requested files refuse without fallback.
|
|
19
20
|
*/
|
|
20
|
-
export declare function getSkillBestDoc(name: string): string | null;
|
|
21
|
+
export declare function getSkillBestDoc(name: string, file?: string): string | null;
|
|
21
22
|
/**
|
|
22
23
|
* Extract requirements from a skill's source files
|
|
23
24
|
*/
|
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.9",
|
|
96145
96145
|
description: "Skills library for AI coding agents",
|
|
96146
96146
|
type: "module",
|
|
96147
96147
|
bin: {
|
|
@@ -97659,6 +97659,80 @@ export default async ({ directory }) => {
|
|
|
97659
97659
|
`;
|
|
97660
97660
|
}
|
|
97661
97661
|
|
|
97662
|
+
// src/lib/agent-codex.ts
|
|
97663
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
97664
|
+
var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
97665
|
+
function assertCodexPathConfigEditable(text2) {
|
|
97666
|
+
const before = Bun.TOML.parse(text2);
|
|
97667
|
+
if (before.skills?.config === undefined)
|
|
97668
|
+
return;
|
|
97669
|
+
const expected = structuredClone(before);
|
|
97670
|
+
delete expected.skills.config;
|
|
97671
|
+
try {
|
|
97672
|
+
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
97673
|
+
if (isDeepStrictEqual2(withoutSections, expected))
|
|
97674
|
+
return;
|
|
97675
|
+
} catch {}
|
|
97676
|
+
throw new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables; preserve and convert inline or dotted config arrays before running skills hook install");
|
|
97677
|
+
}
|
|
97678
|
+
function disableCodexBundledSkills(text2) {
|
|
97679
|
+
const before = Bun.TOML.parse(text2);
|
|
97680
|
+
const table = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
97681
|
+
const refuse2 = () => new Error("Codex bundled skills configuration requires ordinary [skills.bundled] TOML with a boolean enabled setting; preserve and convert inline or dotted definitions before running skills hook install");
|
|
97682
|
+
if (before.skills !== undefined && !table(before.skills))
|
|
97683
|
+
throw refuse2();
|
|
97684
|
+
if (before.skills?.bundled !== undefined && !table(before.skills.bundled))
|
|
97685
|
+
throw refuse2();
|
|
97686
|
+
if (before.skills !== undefined) {
|
|
97687
|
+
let root = before, attempts = 0;
|
|
97688
|
+
for (const header of text2.matchAll(/^[ \t]*\[/gm)) {
|
|
97689
|
+
if (++attempts > 256)
|
|
97690
|
+
throw refuse2();
|
|
97691
|
+
try {
|
|
97692
|
+
root = Bun.TOML.parse(text2.slice(0, header.index));
|
|
97693
|
+
break;
|
|
97694
|
+
} catch {}
|
|
97695
|
+
}
|
|
97696
|
+
if (root.skills !== undefined)
|
|
97697
|
+
throw refuse2();
|
|
97698
|
+
}
|
|
97699
|
+
const enabled = before.skills?.bundled?.enabled;
|
|
97700
|
+
if (enabled !== undefined && typeof enabled !== "boolean")
|
|
97701
|
+
throw refuse2();
|
|
97702
|
+
if (enabled === false)
|
|
97703
|
+
return text2;
|
|
97704
|
+
const expected = structuredClone(before);
|
|
97705
|
+
expected.skills ??= {};
|
|
97706
|
+
expected.skills.bundled ??= {};
|
|
97707
|
+
expected.skills.bundled.enabled = false;
|
|
97708
|
+
const candidates = new Set;
|
|
97709
|
+
const consider = (candidate) => {
|
|
97710
|
+
try {
|
|
97711
|
+
if (isDeepStrictEqual2(Bun.TOML.parse(candidate), expected))
|
|
97712
|
+
candidates.add(candidate);
|
|
97713
|
+
} catch {}
|
|
97714
|
+
};
|
|
97715
|
+
const newline = text2.includes(`\r
|
|
97716
|
+
`) ? `\r
|
|
97717
|
+
` : `
|
|
97718
|
+
`;
|
|
97719
|
+
if (before.skills?.bundled !== undefined) {
|
|
97720
|
+
for (const section of text2.matchAll(/^[ \t]*\[skills\.bundled\][ \t]*(?:#[^\r\n]*)?(?:\r?\n|$)(?:(?![ \t]*\[)[^\n]*(?:\n|$))*/gm)) {
|
|
97721
|
+
const headerEnd = section[0].indexOf(`
|
|
97722
|
+
`) + 1 || section[0].length;
|
|
97723
|
+
const replacement = enabled === true ? section[0].replace(/^([ \t]*enabled[ \t]*=[ \t]*)true([ \t]*(?:#[^\r\n]*)?)(\r?)$/m, "$1false$2$3") : section[0].slice(0, headerEnd) + (section[0].slice(0, headerEnd).endsWith(`
|
|
97724
|
+
`) ? "" : newline) + `enabled = false${newline}` + section[0].slice(headerEnd);
|
|
97725
|
+
consider(text2.slice(0, section.index) + replacement + text2.slice(section.index + section[0].length));
|
|
97726
|
+
}
|
|
97727
|
+
} else {
|
|
97728
|
+
consider(`${text2}${text2.endsWith(`
|
|
97729
|
+
`) || !text2 ? "" : newline}${newline}[skills.bundled]${newline}enabled = false${newline}`);
|
|
97730
|
+
}
|
|
97731
|
+
if (candidates.size !== 1)
|
|
97732
|
+
throw refuse2();
|
|
97733
|
+
return [...candidates][0];
|
|
97734
|
+
}
|
|
97735
|
+
|
|
97662
97736
|
// src/lib/agent-integration.ts
|
|
97663
97737
|
var sha = (data) => createHash9("sha256").update(data).digest("hex");
|
|
97664
97738
|
var ROOTS = [
|
|
@@ -97782,11 +97856,26 @@ function treeHash(root) {
|
|
|
97782
97856
|
visit2(root);
|
|
97783
97857
|
return hash2.digest("hex");
|
|
97784
97858
|
}
|
|
97859
|
+
function projectAncestorDirectories(projects) {
|
|
97860
|
+
const directories = new Set;
|
|
97861
|
+
for (const project of projects) {
|
|
97862
|
+
for (let path = resolve8(project), depth = 0;; depth++) {
|
|
97863
|
+
if (depth >= 100)
|
|
97864
|
+
throw new Error("NATIVE_SKILL_DRIFT: project ancestor discovery limit exceeded");
|
|
97865
|
+
directories.add(path);
|
|
97866
|
+
const parent = dirname9(path);
|
|
97867
|
+
if (parent === path)
|
|
97868
|
+
break;
|
|
97869
|
+
path = parent;
|
|
97870
|
+
}
|
|
97871
|
+
}
|
|
97872
|
+
return [...directories];
|
|
97873
|
+
}
|
|
97785
97874
|
function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
97786
97875
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
97787
97876
|
const roots = ROOTS.map(([agent, path]) => [agent, canonicalAgentPath(join16(home, path), aliases)]);
|
|
97788
97877
|
const bridgePaths = Object.values(AGENT_ADAPTERS).map((adapter) => canonicalAgentPath(join16(home, adapter.root, CLI_BRIDGE_NAME), aliases));
|
|
97789
|
-
for (const project of
|
|
97878
|
+
for (const project of projectAncestorDirectories([...options.projectDirs ?? [], ...options.projectDir ? [options.projectDir] : []])) {
|
|
97790
97879
|
for (const [agent, path] of ROOTS)
|
|
97791
97880
|
roots.push([agent, canonicalAgentPath(join16(project, path), aliases)]);
|
|
97792
97881
|
}
|
|
@@ -97889,6 +97978,7 @@ function inventoryNativeSkills(home = homedir2(), options = {}) {
|
|
|
97889
97978
|
if (options.includeVendor) {
|
|
97890
97979
|
for (const agent of ["codex", "claude"])
|
|
97891
97980
|
scanRoot(agent, canonicalAgentPath(join16(home, `.${agent}`, "plugins", "cache"), aliases), true, true);
|
|
97981
|
+
scanRoot("claude", canonicalAgentPath(join16(home, ".claude", "plugins", "synced"), aliases), true);
|
|
97892
97982
|
scanRoot("gemini", join16(home, ".gemini", "extensions"), true);
|
|
97893
97983
|
}
|
|
97894
97984
|
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 +98043,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
97953
98043
|
config.permissions.deny = nativeReady ? denied.filter((rule) => rule !== "Skill") : [...new Set([...denied, "Skill"])];
|
|
97954
98044
|
config.permissions.allow = nativeReady ? [...new Set([...allowed, `Skill(${CLI_BRIDGE_NAME})`])] : allowed;
|
|
97955
98045
|
config.disableBundledSkills = true;
|
|
98046
|
+
config.syncClaudeAiSkills = false;
|
|
97956
98047
|
}
|
|
97957
98048
|
if (agent === "gemini") {
|
|
97958
98049
|
config.skills ??= {};
|
|
@@ -97983,16 +98074,21 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
97983
98074
|
}
|
|
97984
98075
|
}
|
|
97985
98076
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
97986
|
-
|
|
97987
|
-
let result =
|
|
98077
|
+
const bundled = disableCodexBundledSkills(text2);
|
|
98078
|
+
let result = bundled;
|
|
98079
|
+
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve8(bridgePath), resolve8(join16(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
98080
|
+
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
98081
|
+
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
98082
|
+
throw new Error("Codex skill controls require one path or name selector per [[skills.config]] entry; review ambiguous entries before running skills hook install");
|
|
98083
|
+
const bridgeNeedsRepair = previousConfig?.some((entry) => entry.enabled === false && bridgeSelector(entry));
|
|
97988
98084
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
97989
98085
|
for (const skill of selections) {
|
|
97990
98086
|
const path = join16(skill.path, "SKILL.md");
|
|
97991
98087
|
let found = false;
|
|
97992
|
-
result = result.replace(
|
|
98088
|
+
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
97993
98089
|
const parsed = Bun.TOML.parse(section);
|
|
97994
|
-
const declared = parsed.skills?.config?.[0]
|
|
97995
|
-
if (!declared || ![resolve8(path), resolve8(skill.path)].includes(canonicalAgentPath(declared, aliases)))
|
|
98090
|
+
const declared = parsed.skills?.config?.[0];
|
|
98091
|
+
if (!declared || !(skill.enabled && bridgeSelector(declared)) && !(typeof declared.path === "string" && [resolve8(path), resolve8(skill.path)].includes(canonicalAgentPath(declared.path, aliases))))
|
|
97996
98092
|
return section;
|
|
97997
98093
|
found = true;
|
|
97998
98094
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -98007,6 +98103,8 @@ path = ${JSON.stringify(path)}
|
|
|
98007
98103
|
enabled = false
|
|
98008
98104
|
`;
|
|
98009
98105
|
}
|
|
98106
|
+
if (result !== bundled || bridgeNeedsRepair)
|
|
98107
|
+
assertCodexPathConfigEditable(bundled);
|
|
98010
98108
|
Bun.TOML.parse(result);
|
|
98011
98109
|
return result;
|
|
98012
98110
|
}
|
|
@@ -101453,6 +101551,40 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
101453
101551
|
return loadProjectConfig(targetDir);
|
|
101454
101552
|
}
|
|
101455
101553
|
|
|
101554
|
+
// src/lib/skill-doc-file.ts
|
|
101555
|
+
class SkillDocFileError extends Error {
|
|
101556
|
+
code;
|
|
101557
|
+
constructor(code, message) {
|
|
101558
|
+
super(message);
|
|
101559
|
+
this.code = code;
|
|
101560
|
+
this.name = "SkillDocFileError";
|
|
101561
|
+
}
|
|
101562
|
+
}
|
|
101563
|
+
function resolveSkillDocFile(file) {
|
|
101564
|
+
switch (file) {
|
|
101565
|
+
case undefined:
|
|
101566
|
+
case "":
|
|
101567
|
+
return;
|
|
101568
|
+
case "skill":
|
|
101569
|
+
return "SKILL.md";
|
|
101570
|
+
case "readme":
|
|
101571
|
+
return "README.md";
|
|
101572
|
+
case "claude":
|
|
101573
|
+
return "CLAUDE.md";
|
|
101574
|
+
default:
|
|
101575
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_INVALID", "Unsupported documentation file. Choose skill, readme, or claude.");
|
|
101576
|
+
}
|
|
101577
|
+
}
|
|
101578
|
+
function selectSkillDoc(docs, file) {
|
|
101579
|
+
const path = resolveSkillDocFile(file);
|
|
101580
|
+
if (path === undefined)
|
|
101581
|
+
return docs.skillMd || docs.readme || docs.claudeMd || null;
|
|
101582
|
+
const content = path === "SKILL.md" ? docs.skillMd : path === "README.md" ? docs.readme : docs.claudeMd;
|
|
101583
|
+
if (content === null)
|
|
101584
|
+
throw new SkillDocFileError("SKILL_DOC_FILE_MISSING", `Documentation file ${path} was not found.`);
|
|
101585
|
+
return content;
|
|
101586
|
+
}
|
|
101587
|
+
|
|
101456
101588
|
// src/lib/skillinfo.ts
|
|
101457
101589
|
function isInstructionSkillDir(skillPath, meta) {
|
|
101458
101590
|
if (meta?.kind === "instruction")
|
|
@@ -101492,11 +101624,12 @@ function getSkillDocs(name) {
|
|
|
101492
101624
|
claudeMd: readIfExists(join26(skillPath, "CLAUDE.md"))
|
|
101493
101625
|
};
|
|
101494
101626
|
}
|
|
101495
|
-
function getSkillBestDoc(name) {
|
|
101627
|
+
function getSkillBestDoc(name, file) {
|
|
101628
|
+
resolveSkillDocFile(file);
|
|
101496
101629
|
const docs = getSkillDocs(name);
|
|
101497
101630
|
if (!docs)
|
|
101498
101631
|
return null;
|
|
101499
|
-
return docs
|
|
101632
|
+
return selectSkillDoc(docs, file);
|
|
101500
101633
|
}
|
|
101501
101634
|
function getSkillRequirements(name) {
|
|
101502
101635
|
const skillPath = getSkillPath(name);
|