@hasna/skills 0.8.4 → 0.8.6
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 +56 -32
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/index.js +10 -8
- package/dist/sdk/index.js +1 -1
- 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.6",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -76989,28 +76989,30 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
76989
76989
|
explicit.add(selectionKey(exactProfileSelection(spec, profile)));
|
|
76990
76990
|
}
|
|
76991
76991
|
const promptWords = words2(prompt);
|
|
76992
|
+
const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
|
|
76992
76993
|
const loaded = new Set(resolved.session?.loaded ?? []);
|
|
76993
76994
|
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve11(input2.cwd)] : []];
|
|
76994
76995
|
const candidates = profile.selections.map((selection) => {
|
|
76995
76996
|
const key = selectionKey(selection);
|
|
76996
76997
|
if (input2.restore && loaded.has(key))
|
|
76997
|
-
return { selection, reason: "session-restore", score: 2000 };
|
|
76998
|
+
return { selection, reason: "session-restore", priority: 6, score: 2000 };
|
|
76998
76999
|
if (resolved.inheritedLoaded?.includes(key))
|
|
76999
|
-
return { selection, reason: "subagent-inherit", score: 2000 };
|
|
77000
|
+
return { selection, reason: "subagent-inherit", priority: 6, score: 2000 };
|
|
77000
77001
|
if (explicit.has(key))
|
|
77001
|
-
return { selection, reason: "explicit", score: 1000 };
|
|
77002
|
+
return { selection, reason: "explicit", priority: 5, score: 1000 };
|
|
77002
77003
|
if (selection.triggers?.always)
|
|
77003
|
-
return { selection, reason: "profile-required", score: 900 };
|
|
77004
|
+
return { selection, reason: "profile-required", priority: 4, score: 900 };
|
|
77004
77005
|
const matchedPath = selection.triggers?.paths?.some((pattern) => paths.some((path) => pathMatch(pattern, path)));
|
|
77005
77006
|
if (matchedPath)
|
|
77006
|
-
return { selection, reason: "path", score: 500 };
|
|
77007
|
+
return { selection, reason: "path", priority: 3, score: 500 };
|
|
77007
77008
|
const keywords = selection.triggers?.keywords ?? selection.slug.split("-").filter((word) => word.length >= 4);
|
|
77008
77009
|
const matches2 = keywords.filter((keyword) => {
|
|
77009
77010
|
const tokens = [...words2(keyword)];
|
|
77010
77011
|
return tokens.length > 0 && tokens.every((token) => promptWords.has(token));
|
|
77011
77012
|
}).length;
|
|
77012
|
-
|
|
77013
|
-
|
|
77013
|
+
const named = promptNames.has(selection.slug) || selection.aliases?.some((alias) => promptNames.has(alias));
|
|
77014
|
+
return { selection, reason: "prompt", priority: named ? 2 : 1, score: matches2 * 100 };
|
|
77015
|
+
}).filter((candidate) => candidate.score > 0).sort((a, b) => b.priority - a.priority || b.score - a.score || a.selection.slug.localeCompare(b.selection.slug));
|
|
77014
77016
|
const sections = [];
|
|
77015
77017
|
const selections = [];
|
|
77016
77018
|
const omitted = [];
|
|
@@ -105414,6 +105416,34 @@ __export(exports_agent_integration, {
|
|
|
105414
105416
|
});
|
|
105415
105417
|
import { readFileSync as readFileSync37 } from "fs";
|
|
105416
105418
|
import { isAbsolute as isAbsolute17 } from "path";
|
|
105419
|
+
async function contextForHook(input3, profileId, cached2, deadline) {
|
|
105420
|
+
const remaining = deadline - Date.now();
|
|
105421
|
+
if (remaining <= 0)
|
|
105422
|
+
throw new Error("Skills hook deadline exceeded");
|
|
105423
|
+
const args2 = [process.execPath, process.argv[1], "context", "--stdin", "--json", "--selection-profile", profileId];
|
|
105424
|
+
if (cached2)
|
|
105425
|
+
args2.push("--cached");
|
|
105426
|
+
const child = Bun.spawn(args2, { stdin: new Blob([JSON.stringify(input3)]), stdout: "pipe", stderr: "pipe", env: { ...process.env, NO_COLOR: "1" } });
|
|
105427
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), Math.min(6500, remaining));
|
|
105428
|
+
let result2;
|
|
105429
|
+
let status;
|
|
105430
|
+
try {
|
|
105431
|
+
const [stdout, , exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
|
|
105432
|
+
status = exitCode;
|
|
105433
|
+
result2 = JSON.parse(stdout);
|
|
105434
|
+
} finally {
|
|
105435
|
+
clearTimeout(timer);
|
|
105436
|
+
}
|
|
105437
|
+
if (status !== 0) {
|
|
105438
|
+
if (cached2 && RECOVERABLE_CONTEXT_CACHE_ERRORS.has(result2?.error?.code)) {
|
|
105439
|
+
return contextForHook(input3, profileId, false, deadline);
|
|
105440
|
+
}
|
|
105441
|
+
throw new Error("Skills context could not be resolved");
|
|
105442
|
+
}
|
|
105443
|
+
if (typeof result2?.context !== "string")
|
|
105444
|
+
throw new Error("Invalid Skills context response");
|
|
105445
|
+
return result2;
|
|
105446
|
+
}
|
|
105417
105447
|
function agents(value) {
|
|
105418
105448
|
if (value === "all")
|
|
105419
105449
|
return [...INTEGRATION_AGENTS];
|
|
@@ -105442,6 +105472,7 @@ function registerAgentIntegration(parent) {
|
|
|
105442
105472
|
}
|
|
105443
105473
|
});
|
|
105444
105474
|
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) => {
|
|
105475
|
+
const deadline = Date.now() + 12000;
|
|
105445
105476
|
let event = options.agent === "hermes" && options.event === "pre_tool_call" ? "pre_tool_call" : "UserPromptSubmit";
|
|
105446
105477
|
try {
|
|
105447
105478
|
if (agents(options.agent).length !== 1)
|
|
@@ -105490,38 +105521,29 @@ function registerAgentIntegration(parent) {
|
|
|
105490
105521
|
if (typeof input3.prompt === "string")
|
|
105491
105522
|
input3.prompt = normalizeAgentHookPrompt(options.agent, nativeEvent, input3.prompt);
|
|
105492
105523
|
if (event === "SessionStart") {
|
|
105524
|
+
const remaining = deadline - Date.now();
|
|
105525
|
+
if (remaining <= 0)
|
|
105526
|
+
throw new Error("Skills hook deadline exceeded");
|
|
105493
105527
|
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" } });
|
|
105494
|
-
const
|
|
105528
|
+
const timer = setTimeout(() => refresh.kill("SIGKILL"), Math.min(6500, remaining));
|
|
105495
105529
|
try {
|
|
105496
105530
|
const [, , status] = await Promise.all([new Response(refresh.stdout).text(), new Response(refresh.stderr).text(), refresh.exited]);
|
|
105497
105531
|
if (status !== 0)
|
|
105498
105532
|
throw new Error("Session profile refresh failed");
|
|
105499
105533
|
} finally {
|
|
105500
|
-
clearTimeout(
|
|
105534
|
+
clearTimeout(timer);
|
|
105501
105535
|
}
|
|
105502
105536
|
}
|
|
105503
|
-
const
|
|
105504
|
-
const
|
|
105505
|
-
|
|
105506
|
-
|
|
105507
|
-
|
|
105508
|
-
|
|
105509
|
-
|
|
105510
|
-
|
|
105511
|
-
|
|
105512
|
-
|
|
105513
|
-
const output2 = hookContextOutput(event, result2);
|
|
105514
|
-
if (options.agent === "hermes") {
|
|
105515
|
-
await writeCliOutput(JSON.stringify({ context: output2.hookSpecificOutput?.additionalContext ?? "" }));
|
|
105516
|
-
} else if (options.agent === "cursor") {
|
|
105517
|
-
await writeCliOutput(JSON.stringify(event === "SessionStart" ? { additional_context: output2.hookSpecificOutput?.additionalContext ?? "" } : { continue: true }));
|
|
105518
|
-
} else {
|
|
105519
|
-
if (options.agent === "gemini" && output2.hookSpecificOutput)
|
|
105520
|
-
output2.hookSpecificOutput.hookEventName = nativeEvent;
|
|
105521
|
-
await writeCliOutput(JSON.stringify(output2));
|
|
105522
|
-
}
|
|
105523
|
-
} finally {
|
|
105524
|
-
clearTimeout(timer);
|
|
105537
|
+
const result2 = await contextForHook(input3, selectionProfile, true, deadline);
|
|
105538
|
+
const output2 = hookContextOutput(event, result2);
|
|
105539
|
+
if (options.agent === "hermes") {
|
|
105540
|
+
await writeCliOutput(JSON.stringify({ context: output2.hookSpecificOutput?.additionalContext ?? "" }));
|
|
105541
|
+
} else if (options.agent === "cursor") {
|
|
105542
|
+
await writeCliOutput(JSON.stringify(event === "SessionStart" ? { additional_context: output2.hookSpecificOutput?.additionalContext ?? "" } : { continue: true }));
|
|
105543
|
+
} else {
|
|
105544
|
+
if (options.agent === "gemini" && output2.hookSpecificOutput)
|
|
105545
|
+
output2.hookSpecificOutput.hookEventName = nativeEvent;
|
|
105546
|
+
await writeCliOutput(JSON.stringify(output2));
|
|
105525
105547
|
}
|
|
105526
105548
|
} catch (error61) {
|
|
105527
105549
|
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.";
|
|
@@ -105557,11 +105579,13 @@ function registerAgentIntegration(parent) {
|
|
|
105557
105579
|
}
|
|
105558
105580
|
});
|
|
105559
105581
|
}
|
|
105582
|
+
var RECOVERABLE_CONTEXT_CACHE_ERRORS;
|
|
105560
105583
|
var init_agent_integration2 = __esm(() => {
|
|
105561
105584
|
init_agent_hermes();
|
|
105562
105585
|
init_context();
|
|
105563
105586
|
init_agent_adapters();
|
|
105564
105587
|
init_agent_integration();
|
|
105588
|
+
RECOVERABLE_CONTEXT_CACHE_ERRORS = new Set(["CACHED_PROFILE_EXPIRED", "CACHED_PROFILE_MISSING", "CACHED_BUNDLE_MISSING"]);
|
|
105565
105589
|
});
|
|
105566
105590
|
|
|
105567
105591
|
// 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.6",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -83749,7 +83749,7 @@ var require_protocols9 = __commonJS((exports) => {
|
|
|
83749
83749
|
// package.json
|
|
83750
83750
|
var package_default = {
|
|
83751
83751
|
name: "@hasna/skills",
|
|
83752
|
-
version: "0.8.
|
|
83752
|
+
version: "0.8.6",
|
|
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.6",
|
|
76172
76172
|
description: "Skills library for AI coding agents",
|
|
76173
76173
|
type: "module",
|
|
76174
76174
|
bin: {
|
package/dist/index.js
CHANGED
|
@@ -37660,7 +37660,7 @@ import { dirname as dirname14, relative as relative7 } from "path";
|
|
|
37660
37660
|
// package.json
|
|
37661
37661
|
var package_default = {
|
|
37662
37662
|
name: "@hasna/skills",
|
|
37663
|
-
version: "0.8.
|
|
37663
|
+
version: "0.8.6",
|
|
37664
37664
|
description: "Skills library for AI coding agents",
|
|
37665
37665
|
type: "module",
|
|
37666
37666
|
bin: {
|
|
@@ -40707,28 +40707,30 @@ async function buildSkillContext(input2, options = {}) {
|
|
|
40707
40707
|
explicit.add(selectionKey(exactProfileSelection(spec, profile)));
|
|
40708
40708
|
}
|
|
40709
40709
|
const promptWords = words(prompt);
|
|
40710
|
+
const promptNames = new Set(prompt.toLowerCase().match(/[a-z0-9]+(?:-[a-z0-9]+)*/g) ?? []);
|
|
40710
40711
|
const loaded = new Set(resolved.session?.loaded ?? []);
|
|
40711
40712
|
const paths = [...input2.paths ?? [], ...input2.cwd ? [input2.cwd, resolve13(input2.cwd)] : []];
|
|
40712
40713
|
const candidates = profile.selections.map((selection) => {
|
|
40713
40714
|
const key = selectionKey(selection);
|
|
40714
40715
|
if (input2.restore && loaded.has(key))
|
|
40715
|
-
return { selection, reason: "session-restore", score: 2000 };
|
|
40716
|
+
return { selection, reason: "session-restore", priority: 6, score: 2000 };
|
|
40716
40717
|
if (resolved.inheritedLoaded?.includes(key))
|
|
40717
|
-
return { selection, reason: "subagent-inherit", score: 2000 };
|
|
40718
|
+
return { selection, reason: "subagent-inherit", priority: 6, score: 2000 };
|
|
40718
40719
|
if (explicit.has(key))
|
|
40719
|
-
return { selection, reason: "explicit", score: 1000 };
|
|
40720
|
+
return { selection, reason: "explicit", priority: 5, score: 1000 };
|
|
40720
40721
|
if (selection.triggers?.always)
|
|
40721
|
-
return { selection, reason: "profile-required", score: 900 };
|
|
40722
|
+
return { selection, reason: "profile-required", priority: 4, score: 900 };
|
|
40722
40723
|
const matchedPath = selection.triggers?.paths?.some((pattern) => paths.some((path) => pathMatch(pattern, path)));
|
|
40723
40724
|
if (matchedPath)
|
|
40724
|
-
return { selection, reason: "path", score: 500 };
|
|
40725
|
+
return { selection, reason: "path", priority: 3, score: 500 };
|
|
40725
40726
|
const keywords = selection.triggers?.keywords ?? selection.slug.split("-").filter((word) => word.length >= 4);
|
|
40726
40727
|
const matches = keywords.filter((keyword) => {
|
|
40727
40728
|
const tokens = [...words(keyword)];
|
|
40728
40729
|
return tokens.length > 0 && tokens.every((token) => promptWords.has(token));
|
|
40729
40730
|
}).length;
|
|
40730
|
-
|
|
40731
|
-
|
|
40731
|
+
const named = promptNames.has(selection.slug) || selection.aliases?.some((alias) => promptNames.has(alias));
|
|
40732
|
+
return { selection, reason: "prompt", priority: named ? 2 : 1, score: matches * 100 };
|
|
40733
|
+
}).filter((candidate) => candidate.score > 0).sort((a, b) => b.priority - a.priority || b.score - a.score || a.selection.slug.localeCompare(b.selection.slug));
|
|
40732
40734
|
const sections = [];
|
|
40733
40735
|
const selections = [];
|
|
40734
40736
|
const omitted = [];
|
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.6",
|
|
96145
96145
|
description: "Skills library for AI coding agents",
|
|
96146
96146
|
type: "module",
|
|
96147
96147
|
bin: {
|