@hasna/skills 0.8.8 → 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 +29 -4
- package/bin/index.js +205 -43
- 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 +126 -8
- 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 +124 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,9 +194,22 @@ Reviewed Hermes installations need fresh source and directory coverage in their
|
|
|
194
194
|
reviewed bindings without changing existing source-only reviews.
|
|
195
195
|
|
|
196
196
|
Hook installation preserves unrelated configuration, hooks, and plugin assets.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
|
200
213
|
refused while managed CLI loading is active. Migration preserves ordinary skill
|
|
201
214
|
directories in private archives; `--include-unmanaged` includes user-authored
|
|
202
215
|
copies, and `--include-vendor` retires vendor `SKILL.md` discovery files while
|
|
@@ -381,6 +394,18 @@ skills grants set default --file ./reviewed-policy.json --if-match <policy-revis
|
|
|
381
394
|
skills grants show default --revision <prior-policy-revision> --json
|
|
382
395
|
```
|
|
383
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
|
+
|
|
384
409
|
Updating a policy appends a revision and atomically changes its current pointer.
|
|
385
410
|
An empty `grants` array revokes shared execution access. Historical policies remain
|
|
386
411
|
readable and can be submitted as a new reviewed revision for rollback; they cannot
|
|
@@ -619,7 +644,7 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
|
|
|
619
644
|
| `skills search <query>` | `s` | Search by name, description, or tags |
|
|
620
645
|
| `skills info <name>` | | Show metadata, env vars, and system dependencies |
|
|
621
646
|
| `skills show <name>` | | Show account or owned portable skill details |
|
|
622
|
-
| `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 |
|
|
623
648
|
| `skills requires <name>` | | Show env vars, system deps, and npm dependencies |
|
|
624
649
|
| `skills profiles show <id>` / `skills profiles set <id> --file <json>` | | Read an exact shared selection or update it with writer authorization |
|
|
625
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";
|
|
@@ -51446,16 +51523,21 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
51446
51523
|
}
|
|
51447
51524
|
}
|
|
51448
51525
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
51449
|
-
|
|
51450
|
-
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));
|
|
51451
51533
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
51452
51534
|
for (const skill of selections) {
|
|
51453
51535
|
const path = join21(skill.path, "SKILL.md");
|
|
51454
51536
|
let found = false;
|
|
51455
|
-
result2 = result2.replace(
|
|
51537
|
+
result2 = result2.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
51456
51538
|
const parsed = Bun.TOML.parse(section);
|
|
51457
|
-
const declared = parsed.skills?.config?.[0]
|
|
51458
|
-
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))))
|
|
51459
51541
|
return section;
|
|
51460
51542
|
found = true;
|
|
51461
51543
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -51470,6 +51552,8 @@ path = ${JSON.stringify(path)}
|
|
|
51470
51552
|
enabled = false
|
|
51471
51553
|
`;
|
|
51472
51554
|
}
|
|
51555
|
+
if (result2 !== bundled || bridgeNeedsRepair)
|
|
51556
|
+
assertCodexPathConfigEditable(bundled);
|
|
51473
51557
|
Bun.TOML.parse(result2);
|
|
51474
51558
|
return result2;
|
|
51475
51559
|
}
|
|
@@ -51862,8 +51946,10 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
51862
51946
|
}
|
|
51863
51947
|
const codexPath = canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases);
|
|
51864
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");
|
|
51865
51951
|
const setting = (path) => (codexConfig.skills?.config ?? []).filter((entry) => typeof entry.path === "string" && [path, join21(path, "SKILL.md")].includes(canonicalAgentPath(entry.path, aliases)));
|
|
51866
|
-
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))
|
|
51867
51953
|
throw new Error("NATIVE_SKILL_DRIFT: the Codex CLI bridge is disabled; run skills hook install");
|
|
51868
51954
|
const disabledBuiltin = (entry) => {
|
|
51869
51955
|
if (agent !== "codex" || !entry.vendor || !entry.path.startsWith(canonicalAgentPath(join21(home, ".codex", "skills", ".system"), aliases) + sep5))
|
|
@@ -51941,6 +52027,7 @@ var init_agent_integration = __esm(() => {
|
|
|
51941
52027
|
init_agent_bridge();
|
|
51942
52028
|
init_agent_discovery();
|
|
51943
52029
|
init_agent_adapters();
|
|
52030
|
+
init_agent_codex();
|
|
51944
52031
|
init_agent_hermes();
|
|
51945
52032
|
HOOK_EVENTS = ["UserPromptSubmit", "SessionStart", "SubagentStart"];
|
|
51946
52033
|
ROOTS = [
|
|
@@ -73474,6 +73561,46 @@ var init_read_access = __esm(() => {
|
|
|
73474
73561
|
init_remote_registry();
|
|
73475
73562
|
});
|
|
73476
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
|
+
|
|
73477
73604
|
// node_modules/zod/v4/classic/index.js
|
|
73478
73605
|
var init_classic = __esm(() => {
|
|
73479
73606
|
init_external();
|
|
@@ -76469,6 +76596,43 @@ async function prepareSkillDependencies(skillPath, env3, timeoutMs = DEFAULT_PRE
|
|
|
76469
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;
|
|
76470
76597
|
var init_dependency_preparation = () => {};
|
|
76471
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
|
+
|
|
76472
76636
|
// src/lib/skillinfo.ts
|
|
76473
76637
|
var exports_skillinfo = {};
|
|
76474
76638
|
__export(exports_skillinfo, {
|
|
@@ -76506,11 +76670,12 @@ function getSkillDocs(name) {
|
|
|
76506
76670
|
claudeMd: readIfExists(join32(skillPath, "CLAUDE.md"))
|
|
76507
76671
|
};
|
|
76508
76672
|
}
|
|
76509
|
-
function getSkillBestDoc(name) {
|
|
76673
|
+
function getSkillBestDoc(name, file2) {
|
|
76674
|
+
resolveSkillDocFile(file2);
|
|
76510
76675
|
const docs = getSkillDocs(name);
|
|
76511
76676
|
if (!docs)
|
|
76512
76677
|
return null;
|
|
76513
|
-
return docs
|
|
76678
|
+
return selectSkillDoc(docs, file2);
|
|
76514
76679
|
}
|
|
76515
76680
|
function getSkillRequirements(name) {
|
|
76516
76681
|
const skillPath = getSkillPath(name);
|
|
@@ -76830,6 +76995,7 @@ var init_skillinfo = __esm(() => {
|
|
|
76830
76995
|
init_installer();
|
|
76831
76996
|
init_registry();
|
|
76832
76997
|
init_skill_validation();
|
|
76998
|
+
init_skill_doc_file();
|
|
76833
76999
|
HOSTED_PROVIDER_ENV_PREFIXES = [
|
|
76834
77000
|
"OPENAI_",
|
|
76835
77001
|
"ANTHROPIC_",
|
|
@@ -78009,7 +78175,7 @@ function skillNotFound(name, similar = findSimilarSkills(name)) {
|
|
|
78009
78175
|
console.error(source_default.dim(`Did you mean: ${similar.join(", ")}?`));
|
|
78010
78176
|
}
|
|
78011
78177
|
function handleReadRefusal(error61) {
|
|
78012
|
-
if (!isSkillsFleetCredentialError(error61))
|
|
78178
|
+
if (!isSkillsFleetCredentialError(error61) && !(error61 instanceof SkillDocFileError))
|
|
78013
78179
|
throw error61;
|
|
78014
78180
|
console.error(source_default.red(error61.message));
|
|
78015
78181
|
process.exitCode = 1;
|
|
@@ -78099,9 +78265,9 @@ async function resolveRemoteNotFound(name, remote, message) {
|
|
|
78099
78265
|
}
|
|
78100
78266
|
}
|
|
78101
78267
|
async function handleDocs(name, options) {
|
|
78268
|
+
const file2 = resolveSkillDocFile(options.file);
|
|
78102
78269
|
const access = await requireSkillsReadAccess();
|
|
78103
78270
|
if (access.mode === "hosted") {
|
|
78104
|
-
const file2 = options.file === "readme" ? "README.md" : options.file === "claude" ? "CLAUDE.md" : options.file ? "SKILL.md" : undefined;
|
|
78105
78271
|
const result2 = await loadSelectedSkill(name, selectedProfileId(options.selectionProfile), { projectDir: process.cwd(), file: file2 });
|
|
78106
78272
|
console.log(options.json ? JSON.stringify({ skill: name, ...result2 }) : result2.content);
|
|
78107
78273
|
return;
|
|
@@ -78115,37 +78281,19 @@ async function handleDocs(name, options) {
|
|
|
78115
78281
|
process.exitCode = 1;
|
|
78116
78282
|
return;
|
|
78117
78283
|
}
|
|
78284
|
+
const content = selectSkillDoc(docs, options.file);
|
|
78118
78285
|
if (options.json) {
|
|
78119
78286
|
console.log(JSON.stringify({
|
|
78120
78287
|
skill: name,
|
|
78121
78288
|
hasSkillMd: docs.skillMd !== null,
|
|
78122
78289
|
hasReadme: docs.readme !== null,
|
|
78123
78290
|
hasClaudeMd: docs.claudeMd !== null,
|
|
78124
|
-
content
|
|
78291
|
+
content
|
|
78125
78292
|
}, null, 2));
|
|
78126
78293
|
return;
|
|
78127
78294
|
}
|
|
78128
|
-
|
|
78129
|
-
|
|
78130
|
-
content = docs.skillMd;
|
|
78131
|
-
else if (options.file === "readme")
|
|
78132
|
-
content = docs.readme;
|
|
78133
|
-
else if (options.file === "claude")
|
|
78134
|
-
content = docs.claudeMd;
|
|
78135
|
-
else
|
|
78136
|
-
content = docs.skillMd || docs.readme || docs.claudeMd;
|
|
78137
|
-
if (!content) {
|
|
78138
|
-
const available = [];
|
|
78139
|
-
if (docs.skillMd)
|
|
78140
|
-
available.push("skill");
|
|
78141
|
-
if (docs.readme)
|
|
78142
|
-
available.push("readme");
|
|
78143
|
-
if (docs.claudeMd)
|
|
78144
|
-
available.push("claude");
|
|
78145
|
-
if (!available.length)
|
|
78146
|
-
console.log(source_default.dim(`No documentation found for '${name}'`));
|
|
78147
|
-
else
|
|
78148
|
-
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}'`));
|
|
78149
78297
|
return;
|
|
78150
78298
|
}
|
|
78151
78299
|
console.log(content);
|
|
@@ -78310,6 +78458,7 @@ var init_introspect = __esm(() => {
|
|
|
78310
78458
|
init_fleet_credentials();
|
|
78311
78459
|
init_read_access();
|
|
78312
78460
|
init_skillinfo();
|
|
78461
|
+
init_skill_doc_file();
|
|
78313
78462
|
init_installer();
|
|
78314
78463
|
init_skill_validation();
|
|
78315
78464
|
init_portable_skills();
|
|
@@ -81596,7 +81745,7 @@ var init_execution_grants = __esm(() => {
|
|
|
81596
81745
|
});
|
|
81597
81746
|
|
|
81598
81747
|
// src/lib/execution-grant-client.ts
|
|
81599
|
-
import { isDeepStrictEqual as
|
|
81748
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
81600
81749
|
function id(value) {
|
|
81601
81750
|
if (!grantIdentifier(value))
|
|
81602
81751
|
throw new Error("Invalid execution grant profile or revision identifier.");
|
|
@@ -81648,7 +81797,7 @@ async function saveExecutionGrantPolicy(profileId, grants, revision) {
|
|
|
81648
81797
|
headers: revision === undefined ? { "If-None-Match": "*" } : { "If-Match": `"${revision}"` },
|
|
81649
81798
|
body: JSON.stringify({ grants: snapshot })
|
|
81650
81799
|
}));
|
|
81651
|
-
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !
|
|
81800
|
+
if (policy.profileId !== profileId || policy.previousRevision !== (revision ?? null) || !isDeepStrictEqual3(policy.grants, snapshot)) {
|
|
81652
81801
|
throw new Error("The API did not preserve the reviewed execution grant policy; read it before retrying.");
|
|
81653
81802
|
}
|
|
81654
81803
|
return policy;
|
|
@@ -81659,7 +81808,7 @@ async function resolveExecutionGrant(input2) {
|
|
|
81659
81808
|
if (!grantRecord(value) || Object.keys(value).length !== 3 || !grantIdentifier(value.policyRevision) || !grantIdentifier(value.grantId) || !grantRecord(value.bindings)) {
|
|
81660
81809
|
throw new SkillSelectionError("INVALID_EXECUTION_GRANT", "The Skills API returned an invalid execution grant.");
|
|
81661
81810
|
}
|
|
81662
|
-
if (!
|
|
81811
|
+
if (!isDeepStrictEqual3(value.bindings.selection, snapshot.selection) || !isDeepStrictEqual3(value.bindings.consumer, snapshot.consumer)) {
|
|
81663
81812
|
throw new SkillSelectionError("EXECUTION_GRANT_SCOPE_MISMATCH", "The Skills API returned a grant for a different selection or consumer.");
|
|
81664
81813
|
}
|
|
81665
81814
|
return value;
|
|
@@ -103289,7 +103438,12 @@ import { lstatSync as lstatSync21, mkdirSync as mkdirSync22, readFileSync as rea
|
|
|
103289
103438
|
import { basename as basename6, isAbsolute as isAbsolute15, join as join49 } from "path";
|
|
103290
103439
|
import { createInterface } from "readline";
|
|
103291
103440
|
function registerRuntime(parent) {
|
|
103292
|
-
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
|
+
});
|
|
103293
103447
|
const executions = parent.command("executions").description("Inspect isolated cloud executions");
|
|
103294
103448
|
for (const operation of ["show", "logs", "artifacts", "cancel"]) {
|
|
103295
103449
|
executions.command(operation).argument("<execution-id>").option("--json", "Output as JSON", false).action(async (id2) => {
|
|
@@ -103551,7 +103705,7 @@ function promptLine(question) {
|
|
|
103551
103705
|
});
|
|
103552
103706
|
});
|
|
103553
103707
|
}
|
|
103554
|
-
async function handleRun(name, args2, options) {
|
|
103708
|
+
async function handleRun(name, args2, options, separatorBeforeSkill) {
|
|
103555
103709
|
let managed;
|
|
103556
103710
|
try {
|
|
103557
103711
|
managed = requiresCliSkillLoading();
|
|
@@ -103560,6 +103714,12 @@ async function handleRun(name, args2, options) {
|
|
|
103560
103714
|
process.exitCode = 1;
|
|
103561
103715
|
return;
|
|
103562
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);
|
|
103563
103723
|
const targetIndex = args2.indexOf("--target");
|
|
103564
103724
|
if (managed && !options.remote || options.target || targetIndex >= 0 || options.secretBindings || options.secretBindingsTemplate || args2.includes("--secret-bindings") || args2.includes("--secret-bindings-template")) {
|
|
103565
103725
|
options = { ...options };
|
|
@@ -103585,6 +103745,7 @@ async function handleRun(name, args2, options) {
|
|
|
103585
103745
|
}
|
|
103586
103746
|
}
|
|
103587
103747
|
}
|
|
103748
|
+
args2 = [...args2, ...childArgs];
|
|
103588
103749
|
if (options.remote && options.target) {
|
|
103589
103750
|
console.error("Conflicting --remote and --target execution modes");
|
|
103590
103751
|
process.exitCode = 1;
|
|
@@ -116930,11 +117091,6 @@ init_fleet_credentials();
|
|
|
116930
117091
|
init_read_access();
|
|
116931
117092
|
var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
|
|
116932
117093
|
var isTTY2 = (process.stdout.isTTY ?? false) && (process.stdin.isTTY ?? false);
|
|
116933
|
-
if (process.argv.includes("--no-color")) {
|
|
116934
|
-
source_default.level = 0;
|
|
116935
|
-
const idx = process.argv.indexOf("--no-color");
|
|
116936
|
-
process.argv.splice(idx, 1);
|
|
116937
|
-
}
|
|
116938
117094
|
var program2 = new Command;
|
|
116939
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();
|
|
116940
117096
|
program2.hook("preAction", () => {
|
|
@@ -117018,6 +117174,12 @@ registerStorage2(program2);
|
|
|
117018
117174
|
var { registerRegistryReconcile: registerRegistryReconcile2 } = await Promise.resolve().then(() => (init_registry_reconcile2(), exports_registry_reconcile));
|
|
117019
117175
|
registerRegistryReconcile2(program2);
|
|
117020
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
|
+
}
|
|
117021
117183
|
try {
|
|
117022
117184
|
await program2.parseAsync();
|
|
117023
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 = [
|
|
@@ -11911,16 +11985,21 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
11911
11985
|
}
|
|
11912
11986
|
}
|
|
11913
11987
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
11914
|
-
|
|
11915
|
-
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));
|
|
11916
11995
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
11917
11996
|
for (const skill of selections) {
|
|
11918
11997
|
const path = join17(skill.path, "SKILL.md");
|
|
11919
11998
|
let found = false;
|
|
11920
|
-
result = result.replace(
|
|
11999
|
+
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
11921
12000
|
const parsed = Bun.TOML.parse(section);
|
|
11922
|
-
const declared = parsed.skills?.config?.[0]
|
|
11923
|
-
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))))
|
|
11924
12003
|
return section;
|
|
11925
12004
|
found = true;
|
|
11926
12005
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -11935,6 +12014,8 @@ path = ${JSON.stringify(path)}
|
|
|
11935
12014
|
enabled = false
|
|
11936
12015
|
`;
|
|
11937
12016
|
}
|
|
12017
|
+
if (result !== bundled || bridgeNeedsRepair)
|
|
12018
|
+
assertCodexPathConfigEditable(bundled);
|
|
11938
12019
|
Bun.TOML.parse(result);
|
|
11939
12020
|
return result;
|
|
11940
12021
|
}
|
|
@@ -13420,6 +13501,42 @@ async function prepareSkillDependencies(skillPath, env, timeoutMs = DEFAULT_PREP
|
|
|
13420
13501
|
// src/lib/skillinfo.ts
|
|
13421
13502
|
import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
|
|
13422
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
|
|
13423
13540
|
function isInstructionSkillDir(skillPath, meta) {
|
|
13424
13541
|
if (meta?.kind === "instruction")
|
|
13425
13542
|
return true;
|
|
@@ -13458,11 +13575,12 @@ function getSkillDocs(name) {
|
|
|
13458
13575
|
claudeMd: readIfExists(join23(skillPath, "CLAUDE.md"))
|
|
13459
13576
|
};
|
|
13460
13577
|
}
|
|
13461
|
-
function getSkillBestDoc(name) {
|
|
13578
|
+
function getSkillBestDoc(name, file) {
|
|
13579
|
+
resolveSkillDocFile(file);
|
|
13462
13580
|
const docs = getSkillDocs(name);
|
|
13463
13581
|
if (!docs)
|
|
13464
13582
|
return null;
|
|
13465
|
-
return docs
|
|
13583
|
+
return selectSkillDoc(docs, file);
|
|
13466
13584
|
}
|
|
13467
13585
|
function getSkillRequirements(name) {
|
|
13468
13586
|
const skillPath = getSkillPath(name);
|
|
@@ -37722,7 +37840,7 @@ import { dirname as dirname14, relative as relative7 } from "path";
|
|
|
37722
37840
|
// package.json
|
|
37723
37841
|
var package_default = {
|
|
37724
37842
|
name: "@hasna/skills",
|
|
37725
|
-
version: "0.8.
|
|
37843
|
+
version: "0.8.9",
|
|
37726
37844
|
description: "Skills library for AI coding agents",
|
|
37727
37845
|
type: "module",
|
|
37728
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 = [
|
|
@@ -98000,16 +98074,21 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
98000
98074
|
}
|
|
98001
98075
|
}
|
|
98002
98076
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
98003
|
-
|
|
98004
|
-
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));
|
|
98005
98084
|
const selections = [...skills.filter((entry) => entry.agent === "codex" && !entry.bridge).map((skill) => ({ path: skill.path, enabled: false })), { path: bridgePath, enabled: true }];
|
|
98006
98085
|
for (const skill of selections) {
|
|
98007
98086
|
const path = join16(skill.path, "SKILL.md");
|
|
98008
98087
|
let found = false;
|
|
98009
|
-
result = result.replace(
|
|
98088
|
+
result = result.replace(CODEX_SKILL_CONFIG_SECTIONS, (section) => {
|
|
98010
98089
|
const parsed = Bun.TOML.parse(section);
|
|
98011
|
-
const declared = parsed.skills?.config?.[0]
|
|
98012
|
-
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))))
|
|
98013
98092
|
return section;
|
|
98014
98093
|
found = true;
|
|
98015
98094
|
return /^\s*enabled\s*=/m.test(section) ? section.replace(/^\s*enabled\s*=.*$/m, `enabled = ${skill.enabled}`) : `${section.trimEnd()}
|
|
@@ -98024,6 +98103,8 @@ path = ${JSON.stringify(path)}
|
|
|
98024
98103
|
enabled = false
|
|
98025
98104
|
`;
|
|
98026
98105
|
}
|
|
98106
|
+
if (result !== bundled || bridgeNeedsRepair)
|
|
98107
|
+
assertCodexPathConfigEditable(bundled);
|
|
98027
98108
|
Bun.TOML.parse(result);
|
|
98028
98109
|
return result;
|
|
98029
98110
|
}
|
|
@@ -101470,6 +101551,40 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
101470
101551
|
return loadProjectConfig(targetDir);
|
|
101471
101552
|
}
|
|
101472
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
|
+
|
|
101473
101588
|
// src/lib/skillinfo.ts
|
|
101474
101589
|
function isInstructionSkillDir(skillPath, meta) {
|
|
101475
101590
|
if (meta?.kind === "instruction")
|
|
@@ -101509,11 +101624,12 @@ function getSkillDocs(name) {
|
|
|
101509
101624
|
claudeMd: readIfExists(join26(skillPath, "CLAUDE.md"))
|
|
101510
101625
|
};
|
|
101511
101626
|
}
|
|
101512
|
-
function getSkillBestDoc(name) {
|
|
101627
|
+
function getSkillBestDoc(name, file) {
|
|
101628
|
+
resolveSkillDocFile(file);
|
|
101513
101629
|
const docs = getSkillDocs(name);
|
|
101514
101630
|
if (!docs)
|
|
101515
101631
|
return null;
|
|
101516
|
-
return docs
|
|
101632
|
+
return selectSkillDoc(docs, file);
|
|
101517
101633
|
}
|
|
101518
101634
|
function getSkillRequirements(name) {
|
|
101519
101635
|
const skillPath = getSkillPath(name);
|