@hasna/skills 0.10.74 → 0.10.76
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 +18 -1
- package/bin/index.js +701 -341
- package/bin/mcp.js +142 -93
- package/bin/migrate.js +1 -1
- package/bin/server.js +1 -1
- package/bin/worker.js +1 -1
- package/dist/cli/stray-arguments.d.ts +29 -0
- package/dist/index.js +572 -243
- package/dist/lib/agent-codex-trust.d.ts +4 -0
- package/dist/lib/agent-integration.d.ts +3 -3
- package/dist/lib/agent-sync.d.ts +6 -4
- package/dist/lib/codex-corpus-write.d.ts +47 -6
- package/dist/lib/codex-hook-rpc.d.ts +3 -0
- package/dist/lib/home-adoption.d.ts +5 -5
- package/dist/lib/installer.d.ts +5 -3
- package/dist/lib/search.d.ts +18 -0
- package/dist/runtime-prerequisites.js +1 -1
- package/dist/sdk/index.js +630 -301
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -23,7 +23,7 @@ var package_default;
|
|
|
23
23
|
var init_package = __esm(() => {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "@hasna/skills",
|
|
26
|
-
version: "0.10.
|
|
26
|
+
version: "0.10.76",
|
|
27
27
|
description: "Skills library for AI coding agents",
|
|
28
28
|
type: "module",
|
|
29
29
|
bin: {
|
|
@@ -33567,8 +33567,13 @@ var init_kernel_lock = __esm(() => {
|
|
|
33567
33567
|
});
|
|
33568
33568
|
|
|
33569
33569
|
// src/lib/codex-corpus-write.ts
|
|
33570
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
33571
|
+
var enclosing, leaseHolds;
|
|
33570
33572
|
var init_codex_corpus_write = __esm(() => {
|
|
33571
33573
|
init_kernel_lock();
|
|
33574
|
+
init_config();
|
|
33575
|
+
enclosing = new AsyncLocalStorage;
|
|
33576
|
+
leaseHolds = new WeakMap;
|
|
33572
33577
|
});
|
|
33573
33578
|
|
|
33574
33579
|
// src/lib/codex-hook-rpc.ts
|
|
@@ -36452,6 +36457,118 @@ var init_skillinfo = __esm(() => {
|
|
|
36452
36457
|
GENERIC_ENV_PATTERN = /\b((?:OPENAI|ANTHROPIC|GEMINI|XAI|ELEVENLABS|DEEPGRAM|REPLICATE|FAL|STABILITY|EXA|FIRECRAWL|TWILIO|SENDGRID|RESEND|SLACK|DISCORD|NOTION|LINEAR|GITHUB|AWS|GOOGLE|CLOUDFLARE|VERCEL|SUPABASE|STRIPE)_[A-Z_]+)\b/g;
|
|
36453
36458
|
});
|
|
36454
36459
|
|
|
36460
|
+
// src/lib/search.ts
|
|
36461
|
+
function editDistance(a, b) {
|
|
36462
|
+
if (a === b)
|
|
36463
|
+
return 0;
|
|
36464
|
+
if (a.length === 0)
|
|
36465
|
+
return b.length;
|
|
36466
|
+
if (b.length === 0)
|
|
36467
|
+
return a.length;
|
|
36468
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
36469
|
+
const curr = new Array(b.length + 1);
|
|
36470
|
+
for (let i = 1;i <= a.length; i++) {
|
|
36471
|
+
curr[0] = i;
|
|
36472
|
+
for (let j = 1;j <= b.length; j++) {
|
|
36473
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
36474
|
+
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
36475
|
+
}
|
|
36476
|
+
prev.splice(0, prev.length, ...curr);
|
|
36477
|
+
}
|
|
36478
|
+
return prev[b.length];
|
|
36479
|
+
}
|
|
36480
|
+
function fuzzyMatchScore(word, target) {
|
|
36481
|
+
if (target.includes(word))
|
|
36482
|
+
return 1;
|
|
36483
|
+
const tokens = target.split(/[\s\-_]+/).filter(Boolean);
|
|
36484
|
+
for (const token of tokens) {
|
|
36485
|
+
if (token.startsWith(word))
|
|
36486
|
+
return 0.6;
|
|
36487
|
+
}
|
|
36488
|
+
if (word.length >= 3) {
|
|
36489
|
+
const maxDist = word.length <= 3 ? 1 : 2;
|
|
36490
|
+
for (const token of tokens) {
|
|
36491
|
+
if (Math.abs(token.length - word.length) <= maxDist) {
|
|
36492
|
+
const dist = editDistance(word, token);
|
|
36493
|
+
if (dist <= maxDist)
|
|
36494
|
+
return 0.4;
|
|
36495
|
+
}
|
|
36496
|
+
}
|
|
36497
|
+
}
|
|
36498
|
+
return 0;
|
|
36499
|
+
}
|
|
36500
|
+
function searchSkills(query, registry2 = loadRegistry()) {
|
|
36501
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
36502
|
+
if (words.length === 0)
|
|
36503
|
+
return [];
|
|
36504
|
+
const scored = [];
|
|
36505
|
+
for (const skill of registry2) {
|
|
36506
|
+
const nameLower = skill.name.toLowerCase();
|
|
36507
|
+
const displayNameLower = skill.displayName.toLowerCase();
|
|
36508
|
+
const descriptionLower = skill.description.toLowerCase();
|
|
36509
|
+
const tagsLower = skill.tags.map((t2) => t2.toLowerCase());
|
|
36510
|
+
const tagsCombined = tagsLower.join(" ");
|
|
36511
|
+
let score = 0;
|
|
36512
|
+
let allWordsMatch = true;
|
|
36513
|
+
for (const word of words) {
|
|
36514
|
+
let wordScore = 0;
|
|
36515
|
+
const nameMatch = fuzzyMatchScore(word, nameLower);
|
|
36516
|
+
if (nameMatch > 0)
|
|
36517
|
+
wordScore += 10 * nameMatch;
|
|
36518
|
+
const displayMatch = fuzzyMatchScore(word, displayNameLower);
|
|
36519
|
+
if (displayMatch > 0)
|
|
36520
|
+
wordScore += 7 * displayMatch;
|
|
36521
|
+
const tagMatch = Math.max(...tagsLower.map((t2) => fuzzyMatchScore(word, t2)), fuzzyMatchScore(word, tagsCombined));
|
|
36522
|
+
if (tagMatch > 0)
|
|
36523
|
+
wordScore += 5 * tagMatch;
|
|
36524
|
+
const descMatch = fuzzyMatchScore(word, descriptionLower);
|
|
36525
|
+
if (descMatch > 0)
|
|
36526
|
+
wordScore += 2 * descMatch;
|
|
36527
|
+
if (wordScore === 0) {
|
|
36528
|
+
allWordsMatch = false;
|
|
36529
|
+
break;
|
|
36530
|
+
}
|
|
36531
|
+
score += wordScore;
|
|
36532
|
+
}
|
|
36533
|
+
if (allWordsMatch && score > 0) {
|
|
36534
|
+
scored.push({ skill, score });
|
|
36535
|
+
}
|
|
36536
|
+
}
|
|
36537
|
+
scored.sort((a, b) => b.score - a.score);
|
|
36538
|
+
return scored.map((s) => s.skill);
|
|
36539
|
+
}
|
|
36540
|
+
function findCloseSkillNames(query, names, maxResults = 3) {
|
|
36541
|
+
if (maxResults <= 0)
|
|
36542
|
+
return [];
|
|
36543
|
+
const q = query.trim().toLowerCase();
|
|
36544
|
+
if (q.length === 0)
|
|
36545
|
+
return [];
|
|
36546
|
+
const tokens = q.split("-").filter(Boolean);
|
|
36547
|
+
const ranked = [];
|
|
36548
|
+
if (q.length >= 3) {
|
|
36549
|
+
for (const name of names) {
|
|
36550
|
+
const candidate = name.toLowerCase();
|
|
36551
|
+
const rank = candidate === q ? 0 : candidate.endsWith(`-${q}`) || candidate.startsWith(`${q}-`) ? 1 : tokens.length > 1 && tokens.every((token) => candidate.split("-").includes(token)) ? 2 : undefined;
|
|
36552
|
+
if (rank !== undefined)
|
|
36553
|
+
ranked.push([name, rank]);
|
|
36554
|
+
}
|
|
36555
|
+
}
|
|
36556
|
+
ranked.sort((a, b) => a[1] - b[1] || a[0].length - b[0].length || a[0].localeCompare(b[0]));
|
|
36557
|
+
const found = ranked.slice(0, maxResults).map(([name]) => name);
|
|
36558
|
+
if (found.length === maxResults)
|
|
36559
|
+
return found;
|
|
36560
|
+
const taken = new Set(found);
|
|
36561
|
+
const threshold = Math.max(3, Math.floor(q.length / 2));
|
|
36562
|
+
const byDistance = names.filter((name) => !taken.has(name)).map((name) => ({ name, distance: editDistance(q, name.toLowerCase()) })).filter((entry) => entry.distance <= threshold).sort((a, b) => a.distance - b.distance || a.name.localeCompare(b.name));
|
|
36563
|
+
return [...found, ...byDistance.slice(0, maxResults - found.length).map((entry) => entry.name)];
|
|
36564
|
+
}
|
|
36565
|
+
function findSimilarSkills(query, maxResults = 3, registry2 = loadRegistry()) {
|
|
36566
|
+
return findCloseSkillNames(query, registry2.map((skill) => skill.name), maxResults);
|
|
36567
|
+
}
|
|
36568
|
+
var init_search = __esm(() => {
|
|
36569
|
+
init_registry();
|
|
36570
|
+
});
|
|
36571
|
+
|
|
36455
36572
|
// src/lib/selected-document.ts
|
|
36456
36573
|
function readSelectedDocument(entries, file2) {
|
|
36457
36574
|
const entry = file2 !== undefined ? entries.find((candidate) => candidate.path === file2) : DOCUMENT_PATHS.map((path) => entries.find((candidate) => candidate.path === path && candidate.bytes.length > 0)).find(Boolean);
|
|
@@ -36532,11 +36649,30 @@ function exactProfileSelection(spec, profile) {
|
|
|
36532
36649
|
const at = spec.lastIndexOf("@");
|
|
36533
36650
|
const slug = at > 0 ? spec.slice(0, at) : spec;
|
|
36534
36651
|
const version2 = at > 0 ? spec.slice(at + 1) : undefined;
|
|
36535
|
-
const
|
|
36536
|
-
if (
|
|
36537
|
-
throw new SkillSelectionError("SKILL_NOT_SELECTED",
|
|
36652
|
+
const named = profile.selections.filter((entry) => entry.slug === slug || entry.aliases?.includes(slug));
|
|
36653
|
+
if (version2 === "")
|
|
36654
|
+
throw new SkillSelectionError("SKILL_NOT_SELECTED", emptyVersionRefusal(spec));
|
|
36655
|
+
if (named.length === 0)
|
|
36656
|
+
throw new SkillSelectionError("SKILL_NOT_SELECTED", unknownNameRefusal(slug, profile));
|
|
36657
|
+
const selection = version2 === undefined ? named[0] : named.find((entry) => entry.version === version2);
|
|
36658
|
+
if (!selection)
|
|
36659
|
+
throw new SkillSelectionError("SKILL_NOT_SELECTED", wrongVersionRefusal(slug, version2, named));
|
|
36538
36660
|
return selection;
|
|
36539
36661
|
}
|
|
36662
|
+
function emptyVersionRefusal(spec) {
|
|
36663
|
+
return `The requested name '${spec}' has an empty version. ${ACCEPTED_FORM}`;
|
|
36664
|
+
}
|
|
36665
|
+
function wrongVersionRefusal(slug, version2, named) {
|
|
36666
|
+
const versions2 = [...new Set(named.map((entry) => entry.version))].sort();
|
|
36667
|
+
const canonical9 = named[0].slug;
|
|
36668
|
+
return `Skill '${slug}' is selected at version '${versions2.join("', '")}', but '${version2}' was requested. ` + `Accepted form: '${canonical9}@${versions2[0]}'.`;
|
|
36669
|
+
}
|
|
36670
|
+
function unknownNameRefusal(slug, profile) {
|
|
36671
|
+
const names = profile.selections.flatMap((entry) => [entry.slug, ...entry.aliases ?? []]);
|
|
36672
|
+
const close = findCloseSkillNames(slug, names);
|
|
36673
|
+
const hint = close.length > 0 ? ` Did you mean: ${close.join(", ")}?` : "";
|
|
36674
|
+
return `Skill '${slug}' is not selected by this Skills profile or project lock.${hint} ` + `Read the accepted names with 'skills search <term>' or 'skills list --all --json'; accept one with 'skills pin <name>'. ${ACCEPTED_FORM}`;
|
|
36675
|
+
}
|
|
36540
36676
|
async function loadSelectedSkill(spec, profileId, options = {}) {
|
|
36541
36677
|
const context = await resolveSelectionContext(profileId, options);
|
|
36542
36678
|
const selection = exactProfileSelection(spec, context.receipt.profile);
|
|
@@ -36581,9 +36717,11 @@ async function selectedSkillRequirements(spec, profileId, options = {}) {
|
|
|
36581
36717
|
}
|
|
36582
36718
|
return { ...getSkillRequirementsFromContent(selection.slug, texts, dependencies), selection };
|
|
36583
36719
|
}
|
|
36720
|
+
var ACCEPTED_FORM = "Accepted form: '<slug>' or '<slug>@<version>'.";
|
|
36584
36721
|
var init_selection_resolver = __esm(() => {
|
|
36585
36722
|
init_profile_client();
|
|
36586
36723
|
init_skillinfo();
|
|
36724
|
+
init_search();
|
|
36587
36725
|
init_selected_document();
|
|
36588
36726
|
init_selection_cache();
|
|
36589
36727
|
});
|
|
@@ -38615,95 +38753,6 @@ var init_registry_types = __esm(() => {
|
|
|
38615
38753
|
];
|
|
38616
38754
|
});
|
|
38617
38755
|
|
|
38618
|
-
// src/lib/search.ts
|
|
38619
|
-
function editDistance(a, b) {
|
|
38620
|
-
if (a === b)
|
|
38621
|
-
return 0;
|
|
38622
|
-
if (a.length === 0)
|
|
38623
|
-
return b.length;
|
|
38624
|
-
if (b.length === 0)
|
|
38625
|
-
return a.length;
|
|
38626
|
-
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
38627
|
-
const curr = new Array(b.length + 1);
|
|
38628
|
-
for (let i = 1;i <= a.length; i++) {
|
|
38629
|
-
curr[0] = i;
|
|
38630
|
-
for (let j = 1;j <= b.length; j++) {
|
|
38631
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
38632
|
-
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
38633
|
-
}
|
|
38634
|
-
prev.splice(0, prev.length, ...curr);
|
|
38635
|
-
}
|
|
38636
|
-
return prev[b.length];
|
|
38637
|
-
}
|
|
38638
|
-
function fuzzyMatchScore(word, target) {
|
|
38639
|
-
if (target.includes(word))
|
|
38640
|
-
return 1;
|
|
38641
|
-
const tokens = target.split(/[\s\-_]+/).filter(Boolean);
|
|
38642
|
-
for (const token of tokens) {
|
|
38643
|
-
if (token.startsWith(word))
|
|
38644
|
-
return 0.6;
|
|
38645
|
-
}
|
|
38646
|
-
if (word.length >= 3) {
|
|
38647
|
-
const maxDist = word.length <= 3 ? 1 : 2;
|
|
38648
|
-
for (const token of tokens) {
|
|
38649
|
-
if (Math.abs(token.length - word.length) <= maxDist) {
|
|
38650
|
-
const dist = editDistance(word, token);
|
|
38651
|
-
if (dist <= maxDist)
|
|
38652
|
-
return 0.4;
|
|
38653
|
-
}
|
|
38654
|
-
}
|
|
38655
|
-
}
|
|
38656
|
-
return 0;
|
|
38657
|
-
}
|
|
38658
|
-
function searchSkills(query, registry2 = loadRegistry()) {
|
|
38659
|
-
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
38660
|
-
if (words.length === 0)
|
|
38661
|
-
return [];
|
|
38662
|
-
const scored = [];
|
|
38663
|
-
for (const skill of registry2) {
|
|
38664
|
-
const nameLower = skill.name.toLowerCase();
|
|
38665
|
-
const displayNameLower = skill.displayName.toLowerCase();
|
|
38666
|
-
const descriptionLower = skill.description.toLowerCase();
|
|
38667
|
-
const tagsLower = skill.tags.map((t2) => t2.toLowerCase());
|
|
38668
|
-
const tagsCombined = tagsLower.join(" ");
|
|
38669
|
-
let score = 0;
|
|
38670
|
-
let allWordsMatch = true;
|
|
38671
|
-
for (const word of words) {
|
|
38672
|
-
let wordScore = 0;
|
|
38673
|
-
const nameMatch = fuzzyMatchScore(word, nameLower);
|
|
38674
|
-
if (nameMatch > 0)
|
|
38675
|
-
wordScore += 10 * nameMatch;
|
|
38676
|
-
const displayMatch = fuzzyMatchScore(word, displayNameLower);
|
|
38677
|
-
if (displayMatch > 0)
|
|
38678
|
-
wordScore += 7 * displayMatch;
|
|
38679
|
-
const tagMatch = Math.max(...tagsLower.map((t2) => fuzzyMatchScore(word, t2)), fuzzyMatchScore(word, tagsCombined));
|
|
38680
|
-
if (tagMatch > 0)
|
|
38681
|
-
wordScore += 5 * tagMatch;
|
|
38682
|
-
const descMatch = fuzzyMatchScore(word, descriptionLower);
|
|
38683
|
-
if (descMatch > 0)
|
|
38684
|
-
wordScore += 2 * descMatch;
|
|
38685
|
-
if (wordScore === 0) {
|
|
38686
|
-
allWordsMatch = false;
|
|
38687
|
-
break;
|
|
38688
|
-
}
|
|
38689
|
-
score += wordScore;
|
|
38690
|
-
}
|
|
38691
|
-
if (allWordsMatch && score > 0) {
|
|
38692
|
-
scored.push({ skill, score });
|
|
38693
|
-
}
|
|
38694
|
-
}
|
|
38695
|
-
scored.sort((a, b) => b.score - a.score);
|
|
38696
|
-
return scored.map((s) => s.skill);
|
|
38697
|
-
}
|
|
38698
|
-
function findSimilarSkills(query, maxResults = 3, registry2 = loadRegistry()) {
|
|
38699
|
-
const q = query.toLowerCase();
|
|
38700
|
-
const scored = registry2.map((s) => ({ name: s.name, dist: editDistance(q, s.name.toLowerCase()) })).filter((s) => s.dist <= Math.max(3, Math.floor(q.length / 2))).sort((a, b) => a.dist - b.dist);
|
|
38701
|
-
return scored.slice(0, maxResults).map((s) => s.name);
|
|
38702
|
-
}
|
|
38703
|
-
var init_search = __esm(() => {
|
|
38704
|
-
init_registry();
|
|
38705
|
-
});
|
|
38706
|
-
|
|
38707
38756
|
// src/lib/registry.ts
|
|
38708
38757
|
import { existsSync as existsSync13, readFileSync as readFileSync18, readdirSync as readdirSync10 } from "fs";
|
|
38709
38758
|
import { join as join33 } from "path";
|
package/bin/migrate.js
CHANGED
package/bin/server.js
CHANGED
|
@@ -24140,7 +24140,7 @@ restoreLaunchCwd();
|
|
|
24140
24140
|
// package.json
|
|
24141
24141
|
var package_default = {
|
|
24142
24142
|
name: "@hasna/skills",
|
|
24143
|
-
version: "0.10.
|
|
24143
|
+
version: "0.10.76",
|
|
24144
24144
|
description: "Skills library for AI coding agents",
|
|
24145
24145
|
type: "module",
|
|
24146
24146
|
bin: {
|
package/bin/worker.js
CHANGED
|
@@ -24143,7 +24143,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
24143
24143
|
// package.json
|
|
24144
24144
|
var package_default = {
|
|
24145
24145
|
name: "@hasna/skills",
|
|
24146
|
-
version: "0.10.
|
|
24146
|
+
version: "0.10.76",
|
|
24147
24147
|
description: "Skills library for AI coding agents",
|
|
24148
24148
|
type: "module",
|
|
24149
24149
|
bin: {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stray positionals on a verb that declares none are a phantom verb, not a
|
|
3
|
+
* no-op. Commander's `allowExcessArguments` defaults to true (12.1.0,
|
|
4
|
+
* `lib/command.js:28`), so `skills categories typo` exited 0 with the ordinary
|
|
5
|
+
* output and `skills list typo` printed the whole catalogue: a caller reading
|
|
6
|
+
* the exit code is told the word was understood (BUG e3997558).
|
|
7
|
+
*
|
|
8
|
+
* The default command refuses by hand so it can name the verbs that exist, and
|
|
9
|
+
* `skills mcp` refuses by hand so it can name its three forms. Those two keep
|
|
10
|
+
* their own, better sentences. Every other command that declares no argument of
|
|
11
|
+
* its own is closed here in one pass, so the next command added gets the
|
|
12
|
+
* refusal without its author having to remember the hole exists.
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from "commander";
|
|
15
|
+
/**
|
|
16
|
+
* Opt a command out of the pass because it owns a more precise refusal than
|
|
17
|
+
* commander's. Chainable, and it keeps `allowExcessArguments(true)` so the
|
|
18
|
+
* command still receives the stray word it wants to report.
|
|
19
|
+
*/
|
|
20
|
+
export declare function permitStrayArguments(command: Command): Command;
|
|
21
|
+
/**
|
|
22
|
+
* Refuse positionals on every command that declares no argument of its own.
|
|
23
|
+
*
|
|
24
|
+
* A command that declares arguments keeps commander's own arity check, and a
|
|
25
|
+
* command with subcommands never reaches the excess check for a word that names
|
|
26
|
+
* a child (commander dispatches the subcommand first). Commands without an
|
|
27
|
+
* action handler reach it only with zero operands, so the flag is inert there.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rejectStrayArguments(command: Command): void;
|