@hasna/skills 0.8.5 → 0.8.7
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 +5 -2
- package/bin/index.js +93 -26
- package/bin/mcp.js +47 -2
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +47 -2
- package/dist/sdk/index.js +47 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -246,8 +246,11 @@ select complete skill instructions from that verified cache using explicit
|
|
|
246
246
|
A session retains its selected versions; compaction restores loaded instructions,
|
|
247
247
|
and subagents inherit the parent's selection. Instructions that exceed the
|
|
248
248
|
context budget produce an explicit `skills load` command. A hook never executes
|
|
249
|
-
a skill. Cached use is explicit and expires after 24 hours
|
|
250
|
-
|
|
249
|
+
a skill. Cached use is explicit and expires after 24 hours. When cached context
|
|
250
|
+
expires or is missing, the hook makes one authenticated API attempt for the same
|
|
251
|
+
pinned session. It preserves exact versions and does not extend an expired
|
|
252
|
+
receipt's lifetime. Authentication, integrity and native discovery failures still
|
|
253
|
+
block; they do not silently switch to a local catalog.
|
|
251
254
|
|
|
252
255
|
## Profiles, station sync and rollback
|
|
253
256
|
|
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.7",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -38212,6 +38212,35 @@ function isSafeRelativePath(value) {
|
|
|
38212
38212
|
const normalized = normalize(value).replace(/\\/g, "/");
|
|
38213
38213
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
38214
38214
|
}
|
|
38215
|
+
function validateRuntimeEntry(skillPath, entrypoint, issues, warnings) {
|
|
38216
|
+
if (typeof entrypoint !== "string" || !isSafeRelativePath(entrypoint) || /^[a-zA-Z]:/.test(entrypoint) || entrypoint.includes("\\") || entrypoint.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
38217
|
+
add2(issues, "skill.runtime_entrypoint_unsafe", "runtime.entrypoint must be a canonical relative file path inside the skill directory");
|
|
38218
|
+
return;
|
|
38219
|
+
}
|
|
38220
|
+
const parts = entrypoint.split("/");
|
|
38221
|
+
let current = skillPath;
|
|
38222
|
+
for (let index = 0;index < parts.length; index++) {
|
|
38223
|
+
current = join8(current, parts[index]);
|
|
38224
|
+
let info;
|
|
38225
|
+
try {
|
|
38226
|
+
info = lstatSync(current);
|
|
38227
|
+
} catch {
|
|
38228
|
+
add2(issues, "skill.runtime_entrypoint_missing", "The declared runtime.entrypoint is missing or inaccessible");
|
|
38229
|
+
return;
|
|
38230
|
+
}
|
|
38231
|
+
if (info.isSymbolicLink()) {
|
|
38232
|
+
add2(issues, "skill.runtime_entrypoint_symlink", "runtime.entrypoint must not traverse symbolic links");
|
|
38233
|
+
return;
|
|
38234
|
+
}
|
|
38235
|
+
if (index < parts.length - 1 ? !info.isDirectory() : !info.isFile()) {
|
|
38236
|
+
add2(issues, "skill.runtime_entrypoint_not_file", "runtime.entrypoint must resolve to a regular file");
|
|
38237
|
+
return;
|
|
38238
|
+
}
|
|
38239
|
+
if (index === parts.length - 1 && info.size < 50) {
|
|
38240
|
+
add2(warnings, "skill.runtime_entrypoint_minimal", `Source entry point is very small (${info.size}B)`);
|
|
38241
|
+
}
|
|
38242
|
+
}
|
|
38243
|
+
}
|
|
38215
38244
|
function isHostedPackageMetadata(pkg) {
|
|
38216
38245
|
return isHostedMetadataPackage(pkg);
|
|
38217
38246
|
}
|
|
@@ -38447,10 +38476,22 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
38447
38476
|
} else {
|
|
38448
38477
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
38449
38478
|
const srcDir = join8(skillPath, "src");
|
|
38479
|
+
let declaredEntrypoint;
|
|
38480
|
+
const manifestPath = join8(skillPath, "skill.json");
|
|
38481
|
+
if (!hostedMetadata && existsSync8(manifestPath)) {
|
|
38482
|
+
try {
|
|
38483
|
+
const manifest = asRecord(readJsonFile(manifestPath));
|
|
38484
|
+
declaredEntrypoint = asRecord(manifest?.runtime)?.entrypoint;
|
|
38485
|
+
} catch {
|
|
38486
|
+
add2(issues, "skill.manifest_invalid", "skill.json must contain valid JSON");
|
|
38487
|
+
}
|
|
38488
|
+
}
|
|
38450
38489
|
if (hostedMetadata) {
|
|
38451
38490
|
if (existsSync8(srcDir)) {
|
|
38452
38491
|
add2(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
38453
38492
|
}
|
|
38493
|
+
} else if (declaredEntrypoint !== undefined && declaredEntrypoint !== null) {
|
|
38494
|
+
validateRuntimeEntry(skillPath, declaredEntrypoint, issues, warnings);
|
|
38454
38495
|
} else if (!existsSync8(srcDir)) {
|
|
38455
38496
|
add2(issues, "skill.src_missing", "Missing src/ directory");
|
|
38456
38497
|
} else if (!existsSync8(join8(srcDir, "index.ts")) && !existsSync8(join8(srcDir, "index.js"))) {
|
|
@@ -40028,7 +40069,11 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
40028
40069
|
"package.missing",
|
|
40029
40070
|
"package.bin_missing",
|
|
40030
40071
|
"skill.src_missing",
|
|
40031
|
-
"skill.src_index_missing"
|
|
40072
|
+
"skill.src_index_missing",
|
|
40073
|
+
"skill.runtime_entrypoint_unsafe",
|
|
40074
|
+
"skill.runtime_entrypoint_missing",
|
|
40075
|
+
"skill.runtime_entrypoint_symlink",
|
|
40076
|
+
"skill.runtime_entrypoint_not_file"
|
|
40032
40077
|
]);
|
|
40033
40078
|
for (let i = issues.length - 1;i >= 0; i--) {
|
|
40034
40079
|
if (executableOnlyCodes.has(issues[i].code))
|
|
@@ -105416,6 +105461,34 @@ __export(exports_agent_integration, {
|
|
|
105416
105461
|
});
|
|
105417
105462
|
import { readFileSync as readFileSync37 } from "fs";
|
|
105418
105463
|
import { isAbsolute as isAbsolute17 } from "path";
|
|
105464
|
+
async function contextForHook(input3, profileId, cached2, deadline) {
|
|
105465
|
+
const remaining = deadline - Date.now();
|
|
105466
|
+
if (remaining <= 0)
|
|
105467
|
+
throw new Error("Skills hook deadline exceeded");
|
|
105468
|
+
const args2 = [process.execPath, process.argv[1], "context", "--stdin", "--json", "--selection-profile", profileId];
|
|
105469
|
+
if (cached2)
|
|
105470
|
+
args2.push("--cached");
|
|
105471
|
+
const child = Bun.spawn(args2, { stdin: new Blob([JSON.stringify(input3)]), stdout: "pipe", stderr: "pipe", env: { ...process.env, NO_COLOR: "1" } });
|
|
105472
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), Math.min(6500, remaining));
|
|
105473
|
+
let result2;
|
|
105474
|
+
let status;
|
|
105475
|
+
try {
|
|
105476
|
+
const [stdout, , exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
|
|
105477
|
+
status = exitCode;
|
|
105478
|
+
result2 = JSON.parse(stdout);
|
|
105479
|
+
} finally {
|
|
105480
|
+
clearTimeout(timer);
|
|
105481
|
+
}
|
|
105482
|
+
if (status !== 0) {
|
|
105483
|
+
if (cached2 && RECOVERABLE_CONTEXT_CACHE_ERRORS.has(result2?.error?.code)) {
|
|
105484
|
+
return contextForHook(input3, profileId, false, deadline);
|
|
105485
|
+
}
|
|
105486
|
+
throw new Error("Skills context could not be resolved");
|
|
105487
|
+
}
|
|
105488
|
+
if (typeof result2?.context !== "string")
|
|
105489
|
+
throw new Error("Invalid Skills context response");
|
|
105490
|
+
return result2;
|
|
105491
|
+
}
|
|
105419
105492
|
function agents(value) {
|
|
105420
105493
|
if (value === "all")
|
|
105421
105494
|
return [...INTEGRATION_AGENTS];
|
|
@@ -105444,6 +105517,7 @@ function registerAgentIntegration(parent) {
|
|
|
105444
105517
|
}
|
|
105445
105518
|
});
|
|
105446
105519
|
hook.command("user-prompt").requiredOption("--agent <agent>", `Native payload/output adapter: ${INTEGRATION_AGENTS.join(", ")}`).option("--event <event>", "Native lifecycle event supplied by the installed adapter").option("--selection-profile <id>", "Selection profile to load").description("Read native lifecycle JSON on stdin and return selected context").action(async (options) => {
|
|
105520
|
+
const deadline = Date.now() + 12000;
|
|
105447
105521
|
let event = options.agent === "hermes" && options.event === "pre_tool_call" ? "pre_tool_call" : "UserPromptSubmit";
|
|
105448
105522
|
try {
|
|
105449
105523
|
if (agents(options.agent).length !== 1)
|
|
@@ -105492,38 +105566,29 @@ function registerAgentIntegration(parent) {
|
|
|
105492
105566
|
if (typeof input3.prompt === "string")
|
|
105493
105567
|
input3.prompt = normalizeAgentHookPrompt(options.agent, nativeEvent, input3.prompt);
|
|
105494
105568
|
if (event === "SessionStart") {
|
|
105569
|
+
const remaining = deadline - Date.now();
|
|
105570
|
+
if (remaining <= 0)
|
|
105571
|
+
throw new Error("Skills hook deadline exceeded");
|
|
105495
105572
|
const refresh = Bun.spawn([process.execPath, process.argv[1], "sync", "--selection-profile", selectionProfile, "--json"], { stdin: "ignore", stdout: "pipe", stderr: "pipe", env: { ...process.env, NO_COLOR: "1" } });
|
|
105496
|
-
const
|
|
105573
|
+
const timer = setTimeout(() => refresh.kill("SIGKILL"), Math.min(6500, remaining));
|
|
105497
105574
|
try {
|
|
105498
105575
|
const [, , status] = await Promise.all([new Response(refresh.stdout).text(), new Response(refresh.stderr).text(), refresh.exited]);
|
|
105499
105576
|
if (status !== 0)
|
|
105500
105577
|
throw new Error("Session profile refresh failed");
|
|
105501
105578
|
} finally {
|
|
105502
|
-
clearTimeout(
|
|
105579
|
+
clearTimeout(timer);
|
|
105503
105580
|
}
|
|
105504
105581
|
}
|
|
105505
|
-
const
|
|
105506
|
-
const
|
|
105507
|
-
|
|
105508
|
-
|
|
105509
|
-
|
|
105510
|
-
|
|
105511
|
-
|
|
105512
|
-
|
|
105513
|
-
|
|
105514
|
-
|
|
105515
|
-
const output2 = hookContextOutput(event, result2);
|
|
105516
|
-
if (options.agent === "hermes") {
|
|
105517
|
-
await writeCliOutput(JSON.stringify({ context: output2.hookSpecificOutput?.additionalContext ?? "" }));
|
|
105518
|
-
} else if (options.agent === "cursor") {
|
|
105519
|
-
await writeCliOutput(JSON.stringify(event === "SessionStart" ? { additional_context: output2.hookSpecificOutput?.additionalContext ?? "" } : { continue: true }));
|
|
105520
|
-
} else {
|
|
105521
|
-
if (options.agent === "gemini" && output2.hookSpecificOutput)
|
|
105522
|
-
output2.hookSpecificOutput.hookEventName = nativeEvent;
|
|
105523
|
-
await writeCliOutput(JSON.stringify(output2));
|
|
105524
|
-
}
|
|
105525
|
-
} finally {
|
|
105526
|
-
clearTimeout(timer);
|
|
105582
|
+
const result2 = await contextForHook(input3, selectionProfile, true, deadline);
|
|
105583
|
+
const output2 = hookContextOutput(event, result2);
|
|
105584
|
+
if (options.agent === "hermes") {
|
|
105585
|
+
await writeCliOutput(JSON.stringify({ context: output2.hookSpecificOutput?.additionalContext ?? "" }));
|
|
105586
|
+
} else if (options.agent === "cursor") {
|
|
105587
|
+
await writeCliOutput(JSON.stringify(event === "SessionStart" ? { additional_context: output2.hookSpecificOutput?.additionalContext ?? "" } : { continue: true }));
|
|
105588
|
+
} else {
|
|
105589
|
+
if (options.agent === "gemini" && output2.hookSpecificOutput)
|
|
105590
|
+
output2.hookSpecificOutput.hookEventName = nativeEvent;
|
|
105591
|
+
await writeCliOutput(JSON.stringify(output2));
|
|
105527
105592
|
}
|
|
105528
105593
|
} catch (error61) {
|
|
105529
105594
|
const reason = error61 instanceof Error && error61.message.startsWith("NATIVE_SKILL_DRIFT:") ? error61.message : "Skills context is unavailable. Run skills sync --selection-profile <id> and skills context --stdin --json to diagnose the selected profile.";
|
|
@@ -105559,11 +105624,13 @@ function registerAgentIntegration(parent) {
|
|
|
105559
105624
|
}
|
|
105560
105625
|
});
|
|
105561
105626
|
}
|
|
105627
|
+
var RECOVERABLE_CONTEXT_CACHE_ERRORS;
|
|
105562
105628
|
var init_agent_integration2 = __esm(() => {
|
|
105563
105629
|
init_agent_hermes();
|
|
105564
105630
|
init_context();
|
|
105565
105631
|
init_agent_adapters();
|
|
105566
105632
|
init_agent_integration();
|
|
105633
|
+
RECOVERABLE_CONTEXT_CACHE_ERRORS = new Set(["CACHED_PROFILE_EXPIRED", "CACHED_PROFILE_MISSING", "CACHED_BUNDLE_MISSING"]);
|
|
105567
105634
|
});
|
|
105568
105635
|
|
|
105569
105636
|
// src/lib/profile-admin.ts
|
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.7",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -36478,6 +36478,35 @@ function isSafeRelativePath(value) {
|
|
|
36478
36478
|
const normalized = normalize(value).replace(/\\/g, "/");
|
|
36479
36479
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
36480
36480
|
}
|
|
36481
|
+
function validateRuntimeEntry(skillPath, entrypoint, issues, warnings) {
|
|
36482
|
+
if (typeof entrypoint !== "string" || !isSafeRelativePath(entrypoint) || /^[a-zA-Z]:/.test(entrypoint) || entrypoint.includes("\\") || entrypoint.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
36483
|
+
add(issues, "skill.runtime_entrypoint_unsafe", "runtime.entrypoint must be a canonical relative file path inside the skill directory");
|
|
36484
|
+
return;
|
|
36485
|
+
}
|
|
36486
|
+
const parts = entrypoint.split("/");
|
|
36487
|
+
let current = skillPath;
|
|
36488
|
+
for (let index = 0;index < parts.length; index++) {
|
|
36489
|
+
current = join7(current, parts[index]);
|
|
36490
|
+
let info;
|
|
36491
|
+
try {
|
|
36492
|
+
info = lstatSync3(current);
|
|
36493
|
+
} catch {
|
|
36494
|
+
add(issues, "skill.runtime_entrypoint_missing", "The declared runtime.entrypoint is missing or inaccessible");
|
|
36495
|
+
return;
|
|
36496
|
+
}
|
|
36497
|
+
if (info.isSymbolicLink()) {
|
|
36498
|
+
add(issues, "skill.runtime_entrypoint_symlink", "runtime.entrypoint must not traverse symbolic links");
|
|
36499
|
+
return;
|
|
36500
|
+
}
|
|
36501
|
+
if (index < parts.length - 1 ? !info.isDirectory() : !info.isFile()) {
|
|
36502
|
+
add(issues, "skill.runtime_entrypoint_not_file", "runtime.entrypoint must resolve to a regular file");
|
|
36503
|
+
return;
|
|
36504
|
+
}
|
|
36505
|
+
if (index === parts.length - 1 && info.size < 50) {
|
|
36506
|
+
add(warnings, "skill.runtime_entrypoint_minimal", `Source entry point is very small (${info.size}B)`);
|
|
36507
|
+
}
|
|
36508
|
+
}
|
|
36509
|
+
}
|
|
36481
36510
|
function isHostedPackageMetadata(pkg) {
|
|
36482
36511
|
return isHostedMetadataPackage(pkg);
|
|
36483
36512
|
}
|
|
@@ -36713,10 +36742,22 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
36713
36742
|
} else {
|
|
36714
36743
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
36715
36744
|
const srcDir = join7(skillPath, "src");
|
|
36745
|
+
let declaredEntrypoint;
|
|
36746
|
+
const manifestPath = join7(skillPath, "skill.json");
|
|
36747
|
+
if (!hostedMetadata && existsSync4(manifestPath)) {
|
|
36748
|
+
try {
|
|
36749
|
+
const manifest = asRecord(readJsonFile(manifestPath));
|
|
36750
|
+
declaredEntrypoint = asRecord(manifest?.runtime)?.entrypoint;
|
|
36751
|
+
} catch {
|
|
36752
|
+
add(issues, "skill.manifest_invalid", "skill.json must contain valid JSON");
|
|
36753
|
+
}
|
|
36754
|
+
}
|
|
36716
36755
|
if (hostedMetadata) {
|
|
36717
36756
|
if (existsSync4(srcDir)) {
|
|
36718
36757
|
add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
36719
36758
|
}
|
|
36759
|
+
} else if (declaredEntrypoint !== undefined && declaredEntrypoint !== null) {
|
|
36760
|
+
validateRuntimeEntry(skillPath, declaredEntrypoint, issues, warnings);
|
|
36720
36761
|
} else if (!existsSync4(srcDir)) {
|
|
36721
36762
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
36722
36763
|
} else if (!existsSync4(join7(srcDir, "index.ts")) && !existsSync4(join7(srcDir, "index.js"))) {
|
|
@@ -38176,7 +38217,11 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
38176
38217
|
"package.missing",
|
|
38177
38218
|
"package.bin_missing",
|
|
38178
38219
|
"skill.src_missing",
|
|
38179
|
-
"skill.src_index_missing"
|
|
38220
|
+
"skill.src_index_missing",
|
|
38221
|
+
"skill.runtime_entrypoint_unsafe",
|
|
38222
|
+
"skill.runtime_entrypoint_missing",
|
|
38223
|
+
"skill.runtime_entrypoint_symlink",
|
|
38224
|
+
"skill.runtime_entrypoint_not_file"
|
|
38180
38225
|
]);
|
|
38181
38226
|
for (let i = issues.length - 1;i >= 0; i--) {
|
|
38182
38227
|
if (executableOnlyCodes.has(issues[i].code))
|
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.7",
|
|
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.7",
|
|
76172
76172
|
description: "Skills library for AI coding agents",
|
|
76173
76173
|
type: "module",
|
|
76174
76174
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -7376,6 +7376,35 @@ function isSafeRelativePath(value) {
|
|
|
7376
7376
|
const normalized = normalize(value).replace(/\\/g, "/");
|
|
7377
7377
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
7378
7378
|
}
|
|
7379
|
+
function validateRuntimeEntry(skillPath, entrypoint, issues, warnings) {
|
|
7380
|
+
if (typeof entrypoint !== "string" || !isSafeRelativePath(entrypoint) || /^[a-zA-Z]:/.test(entrypoint) || entrypoint.includes("\\") || entrypoint.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
7381
|
+
add(issues, "skill.runtime_entrypoint_unsafe", "runtime.entrypoint must be a canonical relative file path inside the skill directory");
|
|
7382
|
+
return;
|
|
7383
|
+
}
|
|
7384
|
+
const parts = entrypoint.split("/");
|
|
7385
|
+
let current = skillPath;
|
|
7386
|
+
for (let index = 0;index < parts.length; index++) {
|
|
7387
|
+
current = join4(current, parts[index]);
|
|
7388
|
+
let info;
|
|
7389
|
+
try {
|
|
7390
|
+
info = lstatSync(current);
|
|
7391
|
+
} catch {
|
|
7392
|
+
add(issues, "skill.runtime_entrypoint_missing", "The declared runtime.entrypoint is missing or inaccessible");
|
|
7393
|
+
return;
|
|
7394
|
+
}
|
|
7395
|
+
if (info.isSymbolicLink()) {
|
|
7396
|
+
add(issues, "skill.runtime_entrypoint_symlink", "runtime.entrypoint must not traverse symbolic links");
|
|
7397
|
+
return;
|
|
7398
|
+
}
|
|
7399
|
+
if (index < parts.length - 1 ? !info.isDirectory() : !info.isFile()) {
|
|
7400
|
+
add(issues, "skill.runtime_entrypoint_not_file", "runtime.entrypoint must resolve to a regular file");
|
|
7401
|
+
return;
|
|
7402
|
+
}
|
|
7403
|
+
if (index === parts.length - 1 && info.size < 50) {
|
|
7404
|
+
add(warnings, "skill.runtime_entrypoint_minimal", `Source entry point is very small (${info.size}B)`);
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
}
|
|
7379
7408
|
function isHostedPackageMetadata(pkg) {
|
|
7380
7409
|
return isHostedMetadataPackage(pkg);
|
|
7381
7410
|
}
|
|
@@ -7611,10 +7640,22 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
7611
7640
|
} else {
|
|
7612
7641
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
7613
7642
|
const srcDir = join4(skillPath, "src");
|
|
7643
|
+
let declaredEntrypoint;
|
|
7644
|
+
const manifestPath = join4(skillPath, "skill.json");
|
|
7645
|
+
if (!hostedMetadata && existsSync4(manifestPath)) {
|
|
7646
|
+
try {
|
|
7647
|
+
const manifest = asRecord(readJsonFile(manifestPath));
|
|
7648
|
+
declaredEntrypoint = asRecord(manifest?.runtime)?.entrypoint;
|
|
7649
|
+
} catch {
|
|
7650
|
+
add(issues, "skill.manifest_invalid", "skill.json must contain valid JSON");
|
|
7651
|
+
}
|
|
7652
|
+
}
|
|
7614
7653
|
if (hostedMetadata) {
|
|
7615
7654
|
if (existsSync4(srcDir)) {
|
|
7616
7655
|
add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
7617
7656
|
}
|
|
7657
|
+
} else if (declaredEntrypoint !== undefined && declaredEntrypoint !== null) {
|
|
7658
|
+
validateRuntimeEntry(skillPath, declaredEntrypoint, issues, warnings);
|
|
7618
7659
|
} else if (!existsSync4(srcDir)) {
|
|
7619
7660
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
7620
7661
|
} else if (!existsSync4(join4(srcDir, "index.ts")) && !existsSync4(join4(srcDir, "index.js"))) {
|
|
@@ -9146,7 +9187,11 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
9146
9187
|
"package.missing",
|
|
9147
9188
|
"package.bin_missing",
|
|
9148
9189
|
"skill.src_missing",
|
|
9149
|
-
"skill.src_index_missing"
|
|
9190
|
+
"skill.src_index_missing",
|
|
9191
|
+
"skill.runtime_entrypoint_unsafe",
|
|
9192
|
+
"skill.runtime_entrypoint_missing",
|
|
9193
|
+
"skill.runtime_entrypoint_symlink",
|
|
9194
|
+
"skill.runtime_entrypoint_not_file"
|
|
9150
9195
|
]);
|
|
9151
9196
|
for (let i = issues.length - 1;i >= 0; i--) {
|
|
9152
9197
|
if (executableOnlyCodes.has(issues[i].code))
|
|
@@ -37660,7 +37705,7 @@ import { dirname as dirname14, relative as relative7 } from "path";
|
|
|
37660
37705
|
// package.json
|
|
37661
37706
|
var package_default = {
|
|
37662
37707
|
name: "@hasna/skills",
|
|
37663
|
-
version: "0.8.
|
|
37708
|
+
version: "0.8.7",
|
|
37664
37709
|
description: "Skills library for AI coding agents",
|
|
37665
37710
|
type: "module",
|
|
37666
37711
|
bin: {
|
package/dist/sdk/index.js
CHANGED
|
@@ -96141,7 +96141,7 @@ async function resolveExecutionGrant(input) {
|
|
|
96141
96141
|
// package.json
|
|
96142
96142
|
var package_default = {
|
|
96143
96143
|
name: "@hasna/skills",
|
|
96144
|
-
version: "0.8.
|
|
96144
|
+
version: "0.8.7",
|
|
96145
96145
|
description: "Skills library for AI coding agents",
|
|
96146
96146
|
type: "module",
|
|
96147
96147
|
bin: {
|
|
@@ -98480,6 +98480,35 @@ function isSafeRelativePath(value) {
|
|
|
98480
98480
|
const normalized = normalize(value).replace(/\\/g, "/");
|
|
98481
98481
|
return normalized !== ".." && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
98482
98482
|
}
|
|
98483
|
+
function validateRuntimeEntry(skillPath, entrypoint, issues, warnings) {
|
|
98484
|
+
if (typeof entrypoint !== "string" || !isSafeRelativePath(entrypoint) || /^[a-zA-Z]:/.test(entrypoint) || entrypoint.includes("\\") || entrypoint.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
98485
|
+
add(issues, "skill.runtime_entrypoint_unsafe", "runtime.entrypoint must be a canonical relative file path inside the skill directory");
|
|
98486
|
+
return;
|
|
98487
|
+
}
|
|
98488
|
+
const parts = entrypoint.split("/");
|
|
98489
|
+
let current = skillPath;
|
|
98490
|
+
for (let index = 0;index < parts.length; index++) {
|
|
98491
|
+
current = join18(current, parts[index]);
|
|
98492
|
+
let info;
|
|
98493
|
+
try {
|
|
98494
|
+
info = lstatSync13(current);
|
|
98495
|
+
} catch {
|
|
98496
|
+
add(issues, "skill.runtime_entrypoint_missing", "The declared runtime.entrypoint is missing or inaccessible");
|
|
98497
|
+
return;
|
|
98498
|
+
}
|
|
98499
|
+
if (info.isSymbolicLink()) {
|
|
98500
|
+
add(issues, "skill.runtime_entrypoint_symlink", "runtime.entrypoint must not traverse symbolic links");
|
|
98501
|
+
return;
|
|
98502
|
+
}
|
|
98503
|
+
if (index < parts.length - 1 ? !info.isDirectory() : !info.isFile()) {
|
|
98504
|
+
add(issues, "skill.runtime_entrypoint_not_file", "runtime.entrypoint must resolve to a regular file");
|
|
98505
|
+
return;
|
|
98506
|
+
}
|
|
98507
|
+
if (index === parts.length - 1 && info.size < 50) {
|
|
98508
|
+
add(warnings, "skill.runtime_entrypoint_minimal", `Source entry point is very small (${info.size}B)`);
|
|
98509
|
+
}
|
|
98510
|
+
}
|
|
98511
|
+
}
|
|
98483
98512
|
function isHostedPackageMetadata(pkg) {
|
|
98484
98513
|
return isHostedMetadataPackage(pkg);
|
|
98485
98514
|
}
|
|
@@ -98715,10 +98744,22 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
98715
98744
|
} else {
|
|
98716
98745
|
metadata.runtime = hostedMetadata ? "hosted" : "local";
|
|
98717
98746
|
const srcDir = join18(skillPath, "src");
|
|
98747
|
+
let declaredEntrypoint;
|
|
98748
|
+
const manifestPath = join18(skillPath, "skill.json");
|
|
98749
|
+
if (!hostedMetadata && existsSync6(manifestPath)) {
|
|
98750
|
+
try {
|
|
98751
|
+
const manifest = asRecord(readJsonFile(manifestPath));
|
|
98752
|
+
declaredEntrypoint = asRecord(manifest?.runtime)?.entrypoint;
|
|
98753
|
+
} catch {
|
|
98754
|
+
add(issues, "skill.manifest_invalid", "skill.json must contain valid JSON");
|
|
98755
|
+
}
|
|
98756
|
+
}
|
|
98718
98757
|
if (hostedMetadata) {
|
|
98719
98758
|
if (existsSync6(srcDir)) {
|
|
98720
98759
|
add(issues, "skill.hosted_source_forbidden", "Hosted metadata skills must not include local implementation source");
|
|
98721
98760
|
}
|
|
98761
|
+
} else if (declaredEntrypoint !== undefined && declaredEntrypoint !== null) {
|
|
98762
|
+
validateRuntimeEntry(skillPath, declaredEntrypoint, issues, warnings);
|
|
98722
98763
|
} else if (!existsSync6(srcDir)) {
|
|
98723
98764
|
add(issues, "skill.src_missing", "Missing src/ directory");
|
|
98724
98765
|
} else if (!existsSync6(join18(srcDir, "index.ts")) && !existsSync6(join18(srcDir, "index.js"))) {
|
|
@@ -100216,7 +100257,11 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
100216
100257
|
"package.missing",
|
|
100217
100258
|
"package.bin_missing",
|
|
100218
100259
|
"skill.src_missing",
|
|
100219
|
-
"skill.src_index_missing"
|
|
100260
|
+
"skill.src_index_missing",
|
|
100261
|
+
"skill.runtime_entrypoint_unsafe",
|
|
100262
|
+
"skill.runtime_entrypoint_missing",
|
|
100263
|
+
"skill.runtime_entrypoint_symlink",
|
|
100264
|
+
"skill.runtime_entrypoint_not_file"
|
|
100220
100265
|
]);
|
|
100221
100266
|
for (let i = issues.length - 1;i >= 0; i--) {
|
|
100222
100267
|
if (executableOnlyCodes.has(issues[i].code))
|