@hasna/skills 0.1.46 → 0.1.48
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 +32 -1
- package/bin/index.js +486 -182
- package/bin/mcp.js +311 -94
- package/dist/index.js +122 -59
- package/dist/lib/compact-output.d.ts +32 -0
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/skill-aliases.d.ts +1 -0
- package/dist/storage.js +28 -7
- package/package.json +2 -2
- package/skills/transcript/SKILL.md +53 -73
- package/skills/transcript/package.json +1 -1
- package/skills/apidocs/.claude/settings.json +0 -5
- package/skills/hook/bunfig.toml +0 -5
- package/skills/implementation/bunfig.toml +0 -5
package/dist/index.js
CHANGED
|
@@ -15,11 +15,11 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/lib/registry.ts
|
|
18
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as
|
|
18
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync4 } from "fs";
|
|
19
19
|
import { join as join4 } from "path";
|
|
20
20
|
|
|
21
21
|
// src/lib/config.ts
|
|
22
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "fs";
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
23
23
|
import { join, dirname } from "path";
|
|
24
24
|
import { homedir } from "os";
|
|
25
25
|
var ENUM_KEYS = {
|
|
@@ -45,6 +45,24 @@ function allowedValues(key) {
|
|
|
45
45
|
return MODE_VALUES;
|
|
46
46
|
return ENUM_KEYS[key];
|
|
47
47
|
}
|
|
48
|
+
function mergeDirectoryContents(sourceDir, targetDir) {
|
|
49
|
+
if (!existsSync(sourceDir))
|
|
50
|
+
return;
|
|
51
|
+
mkdirSync(targetDir, { recursive: true });
|
|
52
|
+
for (const entry of readdirSync(sourceDir)) {
|
|
53
|
+
const sourcePath = join(sourceDir, entry);
|
|
54
|
+
const targetPath = join(targetDir, entry);
|
|
55
|
+
try {
|
|
56
|
+
const sourceStat = statSync(sourcePath);
|
|
57
|
+
if (sourceStat.isDirectory()) {
|
|
58
|
+
mergeDirectoryContents(sourcePath, targetPath);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (!existsSync(targetPath))
|
|
62
|
+
copyFileSync(sourcePath, targetPath);
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
48
66
|
function normalizeConfigValue(key, value) {
|
|
49
67
|
if (typeof value !== "string")
|
|
50
68
|
return;
|
|
@@ -68,14 +86,17 @@ function normalizeConfigValue(key, value) {
|
|
|
68
86
|
function getDataDir() {
|
|
69
87
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
70
88
|
const newDir = join(home, ".hasna", "skills");
|
|
89
|
+
const oldDir = join(home, ".skills");
|
|
71
90
|
const oldConfigFile = join(home, ".skillsrc");
|
|
91
|
+
mkdirSync(newDir, { recursive: true });
|
|
92
|
+
try {
|
|
93
|
+
mergeDirectoryContents(oldDir, newDir);
|
|
94
|
+
} catch {}
|
|
72
95
|
if (existsSync(oldConfigFile) && !existsSync(join(newDir, "config.json"))) {
|
|
73
|
-
mkdirSync(newDir, { recursive: true });
|
|
74
96
|
try {
|
|
75
97
|
copyFileSync(oldConfigFile, join(newDir, "config.json"));
|
|
76
98
|
} catch {}
|
|
77
99
|
}
|
|
78
|
-
mkdirSync(newDir, { recursive: true });
|
|
79
100
|
return newDir;
|
|
80
101
|
}
|
|
81
102
|
function getConfigPath(scope) {
|
|
@@ -146,15 +167,15 @@ import {
|
|
|
146
167
|
lstatSync as lstatSync2,
|
|
147
168
|
mkdirSync as mkdirSync2,
|
|
148
169
|
readFileSync as readFileSync3,
|
|
149
|
-
readdirSync as
|
|
170
|
+
readdirSync as readdirSync3,
|
|
150
171
|
rmSync,
|
|
151
|
-
statSync as
|
|
172
|
+
statSync as statSync3,
|
|
152
173
|
writeFileSync as writeFileSync2
|
|
153
174
|
} from "fs";
|
|
154
175
|
import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join as join3, normalize as normalize2, relative } from "path";
|
|
155
176
|
|
|
156
177
|
// src/lib/skill-validation.ts
|
|
157
|
-
import { existsSync as existsSync2, lstatSync, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
178
|
+
import { existsSync as existsSync2, lstatSync, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
158
179
|
import { isAbsolute, join as join2, normalize } from "path";
|
|
159
180
|
|
|
160
181
|
// src/lib/skill-aliases.ts
|
|
@@ -170,7 +191,8 @@ var SKILL_ALIASES = {
|
|
|
170
191
|
"pdf-reader": "read-pdf",
|
|
171
192
|
"generate-image": "image",
|
|
172
193
|
"image-generator": "image",
|
|
173
|
-
"create-blog-article": "blog-article"
|
|
194
|
+
"create-blog-article": "blog-article",
|
|
195
|
+
"skill-diff": "diff-viewer"
|
|
174
196
|
};
|
|
175
197
|
function normalizeSkillSlug(name) {
|
|
176
198
|
return name.trim();
|
|
@@ -199,7 +221,7 @@ var PREMIUM_SKILLS = [
|
|
|
199
221
|
{ slug: "brand-kit", displayName: "Brand Kit", tier: "premium", costCents: 400, providers: ["hosted"], description: "Hosted brand kit with logo usage, palette, typography, brand voice, sample applications, Markdown guide, PDF guide, and SVG assets" },
|
|
200
222
|
{ slug: "generate-book-cover", displayName: "Book Cover", tier: "premium", costCents: 20, providers: ["gpt-image-2"], description: "Professional book cover design from title and genre" },
|
|
201
223
|
{ slug: "remove-background", displayName: "Remove Background", tier: "premium", costCents: 10, providers: ["gemini-3-pro"], description: "AI-powered background removal from images" },
|
|
202
|
-
{ slug: "transcript", displayName: "Transcript", tier: "premium", costCents: 10, providers: ["
|
|
224
|
+
{ slug: "transcript", displayName: "Transcript", tier: "premium", costCents: 10, providers: ["openai", "elevenlabs", "deepgram", "hosted"], description: "Audio/video transcription with timestamps, diarization, and URL support" },
|
|
203
225
|
{ slug: "webcrawling", displayName: "Web Crawling", tier: "premium", costCents: 5, providers: ["firecrawl"], description: "Structured web page crawling and extraction" },
|
|
204
226
|
{ slug: "browse", displayName: "Browse", tier: "premium", costCents: 5, providers: ["browser"], description: "Web browsing and page interaction" },
|
|
205
227
|
{ slug: "read-pdf", displayName: "Read PDF", tier: "premium", costCents: 5, providers: ["cerebras"], description: "Hosted PDF extraction and structured content analysis" },
|
|
@@ -787,7 +809,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
787
809
|
if (!VALID_BIN_COMMAND.test(bareName)) {
|
|
788
810
|
add(issues, "skill.name_invalid", `Skill name '${bareName}' must use lowercase letters, numbers, dots, underscores, or hyphens`);
|
|
789
811
|
}
|
|
790
|
-
for (const entry of
|
|
812
|
+
for (const entry of readdirSync2(skillPath).sort()) {
|
|
791
813
|
const entryPath = join2(skillPath, entry);
|
|
792
814
|
if (RESERVED_SKILL_ENTRIES.has(entry)) {
|
|
793
815
|
add(issues, "skill.reserved_file", `Reserved file '${entry}' is not allowed in skill packages`);
|
|
@@ -902,7 +924,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
902
924
|
const targetPath = join2(skillPath, target);
|
|
903
925
|
if (!existsSync2(targetPath)) {
|
|
904
926
|
add(warnings, "package.bin_target_missing", `package.json bin '${command}' target '${target}' is not present before build`);
|
|
905
|
-
} else if (
|
|
927
|
+
} else if (statSync2(targetPath).isDirectory()) {
|
|
906
928
|
add(issues, "package.bin_target_directory", `package.json bin '${command}' target '${target}' must point to a file, not a directory`);
|
|
907
929
|
}
|
|
908
930
|
}
|
|
@@ -925,7 +947,7 @@ function validateSkillDirectory(name, skillPath, registryMeta) {
|
|
|
925
947
|
add(issues, "skill.src_index_missing", "Missing src/index.ts or src/index.js");
|
|
926
948
|
} else {
|
|
927
949
|
const indexPath = existsSync2(join2(srcDir, "index.ts")) ? join2(srcDir, "index.ts") : join2(srcDir, "index.js");
|
|
928
|
-
const size =
|
|
950
|
+
const size = statSync2(indexPath).size;
|
|
929
951
|
if (size < 50)
|
|
930
952
|
add(warnings, "skill.src_index_minimal", `Source entry point is very small (${size}B)`);
|
|
931
953
|
}
|
|
@@ -947,9 +969,9 @@ function validateRegistryConsistency(registry, skillsDir) {
|
|
|
947
969
|
seen.add(name);
|
|
948
970
|
return false;
|
|
949
971
|
})));
|
|
950
|
-
const skillDirs = existsSync2(skillsDir) ?
|
|
972
|
+
const skillDirs = existsSync2(skillsDir) ? readdirSync2(skillsDir).filter((entry) => {
|
|
951
973
|
const fullPath = join2(skillsDir, entry);
|
|
952
|
-
return !entry.startsWith(".") && entry !== "_common" &&
|
|
974
|
+
return !entry.startsWith(".") && entry !== "_common" && statSync2(fullPath).isDirectory();
|
|
953
975
|
}) : [];
|
|
954
976
|
const directoryNames = new Set(skillDirs);
|
|
955
977
|
const registryNameSet = new Set(registryNames);
|
|
@@ -1016,7 +1038,7 @@ function findPortableSkill(name, options = {}) {
|
|
|
1016
1038
|
return null;
|
|
1017
1039
|
}
|
|
1018
1040
|
const path = getPortableSkillPath(normalized, options);
|
|
1019
|
-
if (!existsSync3(path) || !
|
|
1041
|
+
if (!existsSync3(path) || !statSync3(path).isDirectory())
|
|
1020
1042
|
return null;
|
|
1021
1043
|
try {
|
|
1022
1044
|
return summarizePortableSkill(path, normalized);
|
|
@@ -1029,7 +1051,7 @@ function listPortableSkills(options = {}) {
|
|
|
1029
1051
|
if (!existsSync3(root))
|
|
1030
1052
|
return [];
|
|
1031
1053
|
const skills = [];
|
|
1032
|
-
for (const entry of
|
|
1054
|
+
for (const entry of readdirSync3(root).sort()) {
|
|
1033
1055
|
if (entry.startsWith(".") || DATA_DIR_NON_SKILL_ENTRIES.has(entry))
|
|
1034
1056
|
continue;
|
|
1035
1057
|
const path = join3(root, entry);
|
|
@@ -1105,7 +1127,7 @@ function scaffoldPortableSkill(name, options = {}) {
|
|
|
1105
1127
|
}
|
|
1106
1128
|
function portPortableSkill(sourcePath, options = {}) {
|
|
1107
1129
|
const absoluteSource = normalize2(sourcePath);
|
|
1108
|
-
if (!existsSync3(absoluteSource) || !
|
|
1130
|
+
if (!existsSync3(absoluteSource) || !statSync3(absoluteSource).isDirectory()) {
|
|
1109
1131
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
1110
1132
|
}
|
|
1111
1133
|
const inferred = readPortableSkillManifest(absoluteSource, basename(absoluteSource));
|
|
@@ -1174,7 +1196,7 @@ function validatePortableSkillDirectory(name, skillPath) {
|
|
|
1174
1196
|
const entryPath = join3(skillPath, command.entry);
|
|
1175
1197
|
if (!existsSync3(entryPath))
|
|
1176
1198
|
add2(issues, "portable.command_entry_missing", `Command '${command.name}' entry '${command.entry}' is missing`);
|
|
1177
|
-
else if (
|
|
1199
|
+
else if (statSync3(entryPath).isDirectory())
|
|
1178
1200
|
add2(issues, "portable.command_entry_directory", `Command '${command.name}' entry '${command.entry}' must be a file`);
|
|
1179
1201
|
}
|
|
1180
1202
|
}
|
|
@@ -1595,7 +1617,7 @@ function displayName(name) {
|
|
|
1595
1617
|
}
|
|
1596
1618
|
function safeIsDirectory(path) {
|
|
1597
1619
|
try {
|
|
1598
|
-
return
|
|
1620
|
+
return statSync3(path).isDirectory();
|
|
1599
1621
|
} catch {
|
|
1600
1622
|
return false;
|
|
1601
1623
|
}
|
|
@@ -2806,9 +2828,9 @@ var MEDIA_PROCESSING_SKILLS = [
|
|
|
2806
2828
|
{
|
|
2807
2829
|
name: "transcript",
|
|
2808
2830
|
displayName: "Transcript",
|
|
2809
|
-
description: "
|
|
2831
|
+
description: "Transcribe audio, video, and media URLs with OpenAI GPT-4o, ElevenLabs Scribe v2, DeepGram, or hosted runtime",
|
|
2810
2832
|
category: "Media Processing",
|
|
2811
|
-
tags: ["transcript", "audio", "video", "speech-to-text"]
|
|
2833
|
+
tags: ["transcript", "audio", "video", "speech-to-text", "diarization", "youtube"]
|
|
2812
2834
|
},
|
|
2813
2835
|
{
|
|
2814
2836
|
name: "video-cut-suggester",
|
|
@@ -3591,7 +3613,7 @@ function discoverSkillsInDir(dir) {
|
|
|
3591
3613
|
return [];
|
|
3592
3614
|
const result = [];
|
|
3593
3615
|
try {
|
|
3594
|
-
const entries =
|
|
3616
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
3595
3617
|
for (const entry of entries) {
|
|
3596
3618
|
if (!entry.isDirectory())
|
|
3597
3619
|
continue;
|
|
@@ -4062,7 +4084,7 @@ function loadProjectConfigCompat(targetDir) {
|
|
|
4062
4084
|
}
|
|
4063
4085
|
// src/lib/run-state.ts
|
|
4064
4086
|
import { createHash, randomBytes } from "crypto";
|
|
4065
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, readdirSync as
|
|
4087
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, readdirSync as readdirSync5, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
4066
4088
|
import { extname, join as join7, relative as relative2 } from "path";
|
|
4067
4089
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
4068
4090
|
const now = new Date;
|
|
@@ -4137,11 +4159,11 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
4137
4159
|
if (!existsSync7(runsRoot))
|
|
4138
4160
|
return [];
|
|
4139
4161
|
const records = [];
|
|
4140
|
-
for (const day of
|
|
4162
|
+
for (const day of readdirSync5(runsRoot).sort().reverse()) {
|
|
4141
4163
|
const dayDir = join7(runsRoot, day);
|
|
4142
|
-
if (!
|
|
4164
|
+
if (!statSync4(dayDir).isDirectory())
|
|
4143
4165
|
continue;
|
|
4144
|
-
for (const runId of
|
|
4166
|
+
for (const runId of readdirSync5(dayDir).sort().reverse()) {
|
|
4145
4167
|
const record = readRunRecord(join7(dayDir, runId));
|
|
4146
4168
|
if (record)
|
|
4147
4169
|
records.push(record);
|
|
@@ -4155,7 +4177,7 @@ function findSkillRun(runId, targetDir = process.cwd()) {
|
|
|
4155
4177
|
const runsRoot = join7(getProjectStateDir(targetDir), "runs");
|
|
4156
4178
|
if (!existsSync7(runsRoot))
|
|
4157
4179
|
return null;
|
|
4158
|
-
for (const day of
|
|
4180
|
+
for (const day of readdirSync5(runsRoot)) {
|
|
4159
4181
|
const record = readRunRecord(join7(runsRoot, day, runId));
|
|
4160
4182
|
if (record)
|
|
4161
4183
|
return record;
|
|
@@ -4178,7 +4200,7 @@ function collectRunArtifacts(context) {
|
|
|
4178
4200
|
return [];
|
|
4179
4201
|
const artifacts = [];
|
|
4180
4202
|
for (const path of walkFiles(context.exportDir)) {
|
|
4181
|
-
const stat =
|
|
4203
|
+
const stat = statSync4(path);
|
|
4182
4204
|
const bytes = readFileSync7(path);
|
|
4183
4205
|
artifacts.push({
|
|
4184
4206
|
path: toProjectRelative(context.targetDir, path),
|
|
@@ -4201,9 +4223,9 @@ function readRunRecord(runDir) {
|
|
|
4201
4223
|
}
|
|
4202
4224
|
function walkFiles(dir) {
|
|
4203
4225
|
const files = [];
|
|
4204
|
-
for (const entry of
|
|
4226
|
+
for (const entry of readdirSync5(dir)) {
|
|
4205
4227
|
const full = join7(dir, entry);
|
|
4206
|
-
if (
|
|
4228
|
+
if (statSync4(full).isDirectory())
|
|
4207
4229
|
files.push(...walkFiles(full));
|
|
4208
4230
|
else
|
|
4209
4231
|
files.push(full);
|
|
@@ -15482,7 +15504,7 @@ function finalize(ctx, schema) {
|
|
|
15482
15504
|
result.$schema = "http://json-schema.org/draft-07/schema#";
|
|
15483
15505
|
} else if (ctx.target === "draft-04") {
|
|
15484
15506
|
result.$schema = "http://json-schema.org/draft-04/schema#";
|
|
15485
|
-
} else if (ctx.target === "openapi-3.0") {}
|
|
15507
|
+
} else if (ctx.target === "openapi-3.0") {} else {}
|
|
15486
15508
|
if (ctx.external?.uri) {
|
|
15487
15509
|
const id = ctx.external.registry.get(schema)?.id;
|
|
15488
15510
|
if (!id)
|
|
@@ -15730,7 +15752,7 @@ var literalProcessor = (schema, ctx, json, _params) => {
|
|
|
15730
15752
|
if (val === undefined) {
|
|
15731
15753
|
if (ctx.unrepresentable === "throw") {
|
|
15732
15754
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
15733
|
-
}
|
|
15755
|
+
} else {}
|
|
15734
15756
|
} else if (typeof val === "bigint") {
|
|
15735
15757
|
if (ctx.unrepresentable === "throw") {
|
|
15736
15758
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -18811,7 +18833,7 @@ import { dirname as dirname4, relative as relative3 } from "path";
|
|
|
18811
18833
|
// package.json
|
|
18812
18834
|
var package_default = {
|
|
18813
18835
|
name: "@hasna/skills",
|
|
18814
|
-
version: "0.1.
|
|
18836
|
+
version: "0.1.48",
|
|
18815
18837
|
description: "Skills library for AI coding agents",
|
|
18816
18838
|
type: "module",
|
|
18817
18839
|
bin: {
|
|
@@ -18880,7 +18902,7 @@ var package_default = {
|
|
|
18880
18902
|
typescript: "^5"
|
|
18881
18903
|
},
|
|
18882
18904
|
dependencies: {
|
|
18883
|
-
"@hasna/events": "^0.1.
|
|
18905
|
+
"@hasna/events": "^0.1.7",
|
|
18884
18906
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
18885
18907
|
chalk: "^5.3.0",
|
|
18886
18908
|
commander: "^12.1.0",
|
|
@@ -19064,18 +19086,29 @@ var runOutputSchema = objectSchema({
|
|
|
19064
19086
|
exitCode: { type: "number", description: "Process exit code for local runs." },
|
|
19065
19087
|
skill: stringSchema("Canonical skill slug."),
|
|
19066
19088
|
remote: { type: "boolean", description: "Whether the skill was submitted to the hosted runtime." },
|
|
19067
|
-
|
|
19068
|
-
|
|
19089
|
+
stdoutPreview: objectSchema({
|
|
19090
|
+
text: stringSchema("Truncated stdout preview."),
|
|
19091
|
+
length: { type: "number" },
|
|
19092
|
+
truncated: { type: "boolean" }
|
|
19093
|
+
}, [], "Default compact stdout preview."),
|
|
19094
|
+
stderrPreview: objectSchema({
|
|
19095
|
+
text: stringSchema("Truncated stderr preview."),
|
|
19096
|
+
length: { type: "number" },
|
|
19097
|
+
truncated: { type: "boolean" }
|
|
19098
|
+
}, [], "Default compact stderr preview."),
|
|
19099
|
+
stdout: stringSchema("Captured stdout for local runs when detail:true is requested."),
|
|
19100
|
+
stderr: stringSchema("Captured stderr for local runs when detail:true is requested."),
|
|
19069
19101
|
id: stringSchema("Remote run id when submitted remotely."),
|
|
19070
19102
|
localRunId: stringSchema("Local run metadata id."),
|
|
19071
19103
|
status: stringSchema("Run lifecycle status."),
|
|
19072
19104
|
pricing: pricingSchema,
|
|
19073
|
-
remoteRun: objectSchema({}, [], "
|
|
19074
|
-
run: objectSchema({}, [], "
|
|
19105
|
+
remoteRun: objectSchema({}, [], "Compact hosted remote run summary by default; full contract when detail:true is requested.", true),
|
|
19106
|
+
run: objectSchema({}, [], "Compact local run metadata by default; full metadata when detail:true is requested.", true),
|
|
19075
19107
|
nextActions: objectSchema({
|
|
19076
19108
|
poll: stringSchema("Command to poll run status."),
|
|
19077
19109
|
download: stringSchema("Command to download artifacts.")
|
|
19078
|
-
})
|
|
19110
|
+
}),
|
|
19111
|
+
detailHint: stringSchema("How to request the complete payload.")
|
|
19079
19112
|
}, [], "Skill run result.");
|
|
19080
19113
|
var toolContracts = [
|
|
19081
19114
|
{
|
|
@@ -19123,7 +19156,7 @@ var toolContracts = [
|
|
|
19123
19156
|
{
|
|
19124
19157
|
name: "list_skills",
|
|
19125
19158
|
title: "List Skills",
|
|
19126
|
-
description: "List skills from the basic or full registry profile.",
|
|
19159
|
+
description: "List skills from the basic or full registry profile. Returns a compact paged envelope by default.",
|
|
19127
19160
|
params: ["category?", "profile?", "detail?", "limit?", "offset?"],
|
|
19128
19161
|
category: "discovery",
|
|
19129
19162
|
sideEffects: "none",
|
|
@@ -19139,7 +19172,11 @@ var toolContracts = [
|
|
|
19139
19172
|
skills: arraySchema(skillSummarySchema),
|
|
19140
19173
|
total: { type: "number" },
|
|
19141
19174
|
offset: { type: "number" },
|
|
19142
|
-
limit: { type: "number" }
|
|
19175
|
+
limit: { type: "number" },
|
|
19176
|
+
nextOffset: { type: "number" },
|
|
19177
|
+
hasMore: { type: "boolean" },
|
|
19178
|
+
nextArguments: objectSchema({}, [], "Arguments for the next page.", true),
|
|
19179
|
+
detailHint: stringSchema("How to request fuller skill objects.")
|
|
19143
19180
|
})
|
|
19144
19181
|
},
|
|
19145
19182
|
{
|
|
@@ -19160,7 +19197,7 @@ var toolContracts = [
|
|
|
19160
19197
|
{
|
|
19161
19198
|
name: "search_skills",
|
|
19162
19199
|
title: "Search Skills",
|
|
19163
|
-
description: "Search skills by name, description, or tags.",
|
|
19200
|
+
description: "Search skills by name, description, or tags. Returns a compact paged envelope by default.",
|
|
19164
19201
|
params: ["query", "profile?", "detail?", "limit?", "offset?"],
|
|
19165
19202
|
category: "discovery",
|
|
19166
19203
|
sideEffects: "none",
|
|
@@ -19172,7 +19209,16 @@ var toolContracts = [
|
|
|
19172
19209
|
limit: { type: "number", minimum: 0 },
|
|
19173
19210
|
offset: { type: "number", minimum: 0 }
|
|
19174
19211
|
}, ["query"]),
|
|
19175
|
-
outputSchema: objectSchema({
|
|
19212
|
+
outputSchema: objectSchema({
|
|
19213
|
+
skills: arraySchema(skillSummarySchema),
|
|
19214
|
+
total: { type: "number" },
|
|
19215
|
+
offset: { type: "number" },
|
|
19216
|
+
limit: { type: "number" },
|
|
19217
|
+
nextOffset: { type: "number" },
|
|
19218
|
+
hasMore: { type: "boolean" },
|
|
19219
|
+
nextArguments: objectSchema({}, [], "Arguments for the next page.", true),
|
|
19220
|
+
detailHint: stringSchema("How to request fuller skill objects.")
|
|
19221
|
+
})
|
|
19176
19222
|
},
|
|
19177
19223
|
{
|
|
19178
19224
|
name: "get_skill_info",
|
|
@@ -19289,8 +19335,8 @@ var toolContracts = [
|
|
|
19289
19335
|
{
|
|
19290
19336
|
name: "run_skill",
|
|
19291
19337
|
title: "Run Skill",
|
|
19292
|
-
description: "Run a skill locally or through a configured remote runner.",
|
|
19293
|
-
params: ["name", "input?", "args?", "approved?"],
|
|
19338
|
+
description: "Run a skill locally or through a configured remote runner. Returns compact stdout/stderr previews and run summaries by default; pass detail:true for full records.",
|
|
19339
|
+
params: ["name", "input?", "args?", "approved?", "detail?"],
|
|
19294
19340
|
category: "execution",
|
|
19295
19341
|
sideEffects: "local-process-or-remote-run",
|
|
19296
19342
|
stable: true,
|
|
@@ -19298,28 +19344,33 @@ var toolContracts = [
|
|
|
19298
19344
|
name: skillNameInput,
|
|
19299
19345
|
input: runInputSchema,
|
|
19300
19346
|
args: runArgsSchema,
|
|
19301
|
-
approved: paidRunApprovalSchema
|
|
19347
|
+
approved: paidRunApprovalSchema,
|
|
19348
|
+
detail: { type: "boolean", default: false, description: "Return full stdout/stderr, remote run, and local run metadata." }
|
|
19302
19349
|
}, ["name"]),
|
|
19303
19350
|
outputSchema: runOutputSchema
|
|
19304
19351
|
},
|
|
19305
19352
|
{
|
|
19306
19353
|
name: "get_run_status",
|
|
19307
19354
|
title: "Get Run Status",
|
|
19308
|
-
description: "Fetch remote run status and next actions.",
|
|
19309
|
-
params: ["run_id"],
|
|
19355
|
+
description: "Fetch remote run status and next actions. Returns a compact status summary by default; pass detail:true for the complete remote run payload.",
|
|
19356
|
+
params: ["run_id", "detail?"],
|
|
19310
19357
|
category: "execution",
|
|
19311
19358
|
sideEffects: "none",
|
|
19312
19359
|
stable: true,
|
|
19313
|
-
inputSchema: objectSchema({
|
|
19360
|
+
inputSchema: objectSchema({
|
|
19361
|
+
run_id: stringSchema("Remote or local run id."),
|
|
19362
|
+
detail: { type: "boolean", default: false, description: "Return the complete remote run payload." }
|
|
19363
|
+
}, ["run_id"]),
|
|
19314
19364
|
outputSchema: objectSchema({
|
|
19315
19365
|
contractVersion: { type: "number", description: "Remote run payload contract version." },
|
|
19316
19366
|
runId: stringSchema("Remote run id."),
|
|
19317
19367
|
localRunId: stringSchema("Local run id."),
|
|
19318
|
-
run: objectSchema({}, [], "
|
|
19368
|
+
run: objectSchema({}, [], "Compact remote run status by default; full status when detail:true is requested.", true),
|
|
19319
19369
|
nextActions: objectSchema({
|
|
19320
19370
|
poll: stringSchema("Command to poll run status."),
|
|
19321
19371
|
download: stringSchema("Command to download artifacts.")
|
|
19322
|
-
})
|
|
19372
|
+
}),
|
|
19373
|
+
detailHint: stringSchema("How to request the complete payload.")
|
|
19323
19374
|
})
|
|
19324
19375
|
},
|
|
19325
19376
|
{
|
|
@@ -19414,13 +19465,25 @@ var toolContracts = [
|
|
|
19414
19465
|
{
|
|
19415
19466
|
name: "list_schedules",
|
|
19416
19467
|
title: "List Schedules",
|
|
19417
|
-
description: "List scheduled skill runs.",
|
|
19418
|
-
params: [],
|
|
19468
|
+
description: "List scheduled skill runs as a compact paged envelope.",
|
|
19469
|
+
params: ["limit?", "offset?"],
|
|
19419
19470
|
category: "scheduling",
|
|
19420
19471
|
sideEffects: "none",
|
|
19421
19472
|
stable: true,
|
|
19422
|
-
inputSchema: objectSchema(
|
|
19423
|
-
|
|
19473
|
+
inputSchema: objectSchema({
|
|
19474
|
+
limit: { type: "number", minimum: 0 },
|
|
19475
|
+
offset: { type: "number", minimum: 0 }
|
|
19476
|
+
}),
|
|
19477
|
+
outputSchema: objectSchema({
|
|
19478
|
+
schedules: arraySchema(objectSchema({}, [], "Compact schedule record.", true)),
|
|
19479
|
+
total: { type: "number" },
|
|
19480
|
+
offset: { type: "number" },
|
|
19481
|
+
limit: { type: "number" },
|
|
19482
|
+
nextOffset: { type: "number" },
|
|
19483
|
+
hasMore: { type: "boolean" },
|
|
19484
|
+
nextArguments: objectSchema({}, [], "Arguments for the next page.", true),
|
|
19485
|
+
detailHint: stringSchema("How to request complete schedule details.")
|
|
19486
|
+
})
|
|
19424
19487
|
},
|
|
19425
19488
|
{
|
|
19426
19489
|
name: "remove_schedule",
|
|
@@ -19753,8 +19816,8 @@ import {
|
|
|
19753
19816
|
existsSync as existsSync12,
|
|
19754
19817
|
mkdirSync as mkdirSync9,
|
|
19755
19818
|
readFileSync as readFileSync11,
|
|
19756
|
-
readdirSync as
|
|
19757
|
-
statSync as
|
|
19819
|
+
readdirSync as readdirSync6,
|
|
19820
|
+
statSync as statSync5,
|
|
19758
19821
|
writeFileSync as writeFileSync8
|
|
19759
19822
|
} from "fs";
|
|
19760
19823
|
import { dirname as dirname6, join as join12, normalize as normalize3, relative as relative4, sep } from "path";
|
|
@@ -20216,9 +20279,9 @@ function parsePositiveInteger(value) {
|
|
|
20216
20279
|
}
|
|
20217
20280
|
function walkFiles2(dir) {
|
|
20218
20281
|
const files = [];
|
|
20219
|
-
for (const entry of
|
|
20282
|
+
for (const entry of readdirSync6(dir)) {
|
|
20220
20283
|
const full = join12(dir, entry);
|
|
20221
|
-
const stats =
|
|
20284
|
+
const stats = statSync5(full);
|
|
20222
20285
|
if (stats.isDirectory())
|
|
20223
20286
|
files.push(...walkFiles2(full));
|
|
20224
20287
|
else
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const DEFAULT_LIST_LIMIT = 30;
|
|
2
|
+
export declare const DEFAULT_SEARCH_LIMIT = 20;
|
|
3
|
+
export declare const DEFAULT_TAG_LIMIT = 80;
|
|
4
|
+
export declare const DEFAULT_MCP_LIMIT = 25;
|
|
5
|
+
export declare const MAX_PAGE_LIMIT = 200;
|
|
6
|
+
export declare const DEFAULT_PREVIEW_CHARS = 600;
|
|
7
|
+
export interface Page<T> {
|
|
8
|
+
items: T[];
|
|
9
|
+
total: number;
|
|
10
|
+
offset: number;
|
|
11
|
+
limit: number;
|
|
12
|
+
hasMore: boolean;
|
|
13
|
+
nextOffset: number | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function truncateText(value: unknown, maxChars?: number): string;
|
|
16
|
+
export declare function previewText(value: unknown, maxChars?: number): {
|
|
17
|
+
text: string;
|
|
18
|
+
length: number;
|
|
19
|
+
truncated: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function parsePageLimit(value: string | number | undefined, fallback: number, options?: {
|
|
22
|
+
max?: number;
|
|
23
|
+
allowAll?: boolean;
|
|
24
|
+
}): number;
|
|
25
|
+
export declare function parsePageOffset(value: string | number | undefined, fallback?: number): number;
|
|
26
|
+
export declare function paginate<T>(items: T[], options: {
|
|
27
|
+
limit: number;
|
|
28
|
+
offset?: number;
|
|
29
|
+
}): Page<T>;
|
|
30
|
+
export declare function showingLabel(total: number, shown: number, offset: number): string;
|
|
31
|
+
export declare function compactRunRecord(run: any): Record<string, unknown>;
|
|
32
|
+
export declare function compactRemoteRun(run: any): Record<string, unknown>;
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type ConfigScope = "global" | "project";
|
|
|
19
19
|
/**
|
|
20
20
|
* Get the data directory for skills global config/data.
|
|
21
21
|
* New default: ~/.hasna/skills/
|
|
22
|
-
* Auto-migrates from ~/.
|
|
22
|
+
* Auto-migrates from ~/.skills/ and ~/.skillsrc without deleting legacy data.
|
|
23
23
|
*/
|
|
24
24
|
export declare function getDataDir(): string;
|
|
25
25
|
/**
|
|
@@ -11,6 +11,7 @@ export declare const SKILL_ALIASES: {
|
|
|
11
11
|
readonly "generate-image": "image";
|
|
12
12
|
readonly "image-generator": "image";
|
|
13
13
|
readonly "create-blog-article": "blog-article";
|
|
14
|
+
readonly "skill-diff": "diff-viewer";
|
|
14
15
|
};
|
|
15
16
|
export type SkillAlias = keyof typeof SKILL_ALIASES;
|
|
16
17
|
export declare function normalizeSkillSlug(name: string): string;
|
package/dist/storage.js
CHANGED
|
@@ -20,14 +20,14 @@ import {
|
|
|
20
20
|
existsSync as existsSync3,
|
|
21
21
|
mkdirSync as mkdirSync3,
|
|
22
22
|
readFileSync as readFileSync3,
|
|
23
|
-
readdirSync,
|
|
24
|
-
statSync,
|
|
23
|
+
readdirSync as readdirSync2,
|
|
24
|
+
statSync as statSync2,
|
|
25
25
|
writeFileSync as writeFileSync3
|
|
26
26
|
} from "fs";
|
|
27
27
|
import { dirname as dirname2, join as join3, normalize, relative, sep } from "path";
|
|
28
28
|
|
|
29
29
|
// src/lib/config.ts
|
|
30
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from "fs";
|
|
30
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
31
31
|
import { join, dirname } from "path";
|
|
32
32
|
import { homedir } from "os";
|
|
33
33
|
var ENUM_KEYS = {
|
|
@@ -53,6 +53,24 @@ function allowedValues(key) {
|
|
|
53
53
|
return MODE_VALUES;
|
|
54
54
|
return ENUM_KEYS[key];
|
|
55
55
|
}
|
|
56
|
+
function mergeDirectoryContents(sourceDir, targetDir) {
|
|
57
|
+
if (!existsSync(sourceDir))
|
|
58
|
+
return;
|
|
59
|
+
mkdirSync(targetDir, { recursive: true });
|
|
60
|
+
for (const entry of readdirSync(sourceDir)) {
|
|
61
|
+
const sourcePath = join(sourceDir, entry);
|
|
62
|
+
const targetPath = join(targetDir, entry);
|
|
63
|
+
try {
|
|
64
|
+
const sourceStat = statSync(sourcePath);
|
|
65
|
+
if (sourceStat.isDirectory()) {
|
|
66
|
+
mergeDirectoryContents(sourcePath, targetPath);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!existsSync(targetPath))
|
|
70
|
+
copyFileSync(sourcePath, targetPath);
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
56
74
|
function normalizeConfigValue(key, value) {
|
|
57
75
|
if (typeof value !== "string")
|
|
58
76
|
return;
|
|
@@ -76,14 +94,17 @@ function normalizeConfigValue(key, value) {
|
|
|
76
94
|
function getDataDir() {
|
|
77
95
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
78
96
|
const newDir = join(home, ".hasna", "skills");
|
|
97
|
+
const oldDir = join(home, ".skills");
|
|
79
98
|
const oldConfigFile = join(home, ".skillsrc");
|
|
99
|
+
mkdirSync(newDir, { recursive: true });
|
|
100
|
+
try {
|
|
101
|
+
mergeDirectoryContents(oldDir, newDir);
|
|
102
|
+
} catch {}
|
|
80
103
|
if (existsSync(oldConfigFile) && !existsSync(join(newDir, "config.json"))) {
|
|
81
|
-
mkdirSync(newDir, { recursive: true });
|
|
82
104
|
try {
|
|
83
105
|
copyFileSync(oldConfigFile, join(newDir, "config.json"));
|
|
84
106
|
} catch {}
|
|
85
107
|
}
|
|
86
|
-
mkdirSync(newDir, { recursive: true });
|
|
87
108
|
return newDir;
|
|
88
109
|
}
|
|
89
110
|
function getConfigPath(scope) {
|
|
@@ -743,9 +764,9 @@ function parsePositiveInteger(value) {
|
|
|
743
764
|
}
|
|
744
765
|
function walkFiles(dir) {
|
|
745
766
|
const files = [];
|
|
746
|
-
for (const entry of
|
|
767
|
+
for (const entry of readdirSync2(dir)) {
|
|
747
768
|
const full = join3(dir, entry);
|
|
748
|
-
const stats =
|
|
769
|
+
const stats = statSync2(full);
|
|
749
770
|
if (stats.isDirectory())
|
|
750
771
|
files.push(...walkFiles(full));
|
|
751
772
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "Skills library for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"typescript": "^5"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@hasna/events": "^0.1.
|
|
72
|
+
"@hasna/events": "^0.1.7",
|
|
73
73
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
74
74
|
"chalk": "^5.3.0",
|
|
75
75
|
"commander": "^12.1.0",
|