@hasna/skills 0.1.24 → 0.1.25
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/bin/index.js +153 -70
- package/bin/mcp.js +65 -34
- package/dist/cli/commands/feedback.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/lib/feedback.d.ts +15 -0
- package/package.json +3 -3
- package/skills/_common/http-client.ts +1 -1
- package/skills/_common/installer.ts +1 -1
- package/skills/deploy/src/http-client.ts +1 -1
- package/skills/e2bswarm/src/lib/paths.ts +1 -1
package/bin/index.js
CHANGED
|
@@ -1891,12 +1891,12 @@ var package_default;
|
|
|
1891
1891
|
var init_package = __esm(() => {
|
|
1892
1892
|
package_default = {
|
|
1893
1893
|
name: "@hasna/skills",
|
|
1894
|
-
version: "0.1.
|
|
1894
|
+
version: "0.1.25",
|
|
1895
1895
|
description: "Skills library for AI coding agents",
|
|
1896
1896
|
type: "module",
|
|
1897
1897
|
bin: {
|
|
1898
|
-
skills: "
|
|
1899
|
-
"skills-mcp": "
|
|
1898
|
+
skills: "bin/index.js",
|
|
1899
|
+
"skills-mcp": "bin/mcp.js"
|
|
1900
1900
|
},
|
|
1901
1901
|
exports: {
|
|
1902
1902
|
".": {
|
|
@@ -2122,7 +2122,7 @@ function discoverSkillsInDir(dir) {
|
|
|
2122
2122
|
const fm = parseSkillMdFrontmatter(content);
|
|
2123
2123
|
if (!fm?.name)
|
|
2124
2124
|
continue;
|
|
2125
|
-
const name = fm.name
|
|
2125
|
+
const name = fm.name;
|
|
2126
2126
|
result.push({
|
|
2127
2127
|
name,
|
|
2128
2128
|
displayName: fm.displayName || name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -7216,7 +7216,7 @@ Skills Doctor \u2014 ${installed.length} installed, ${issues.length} with issues
|
|
|
7216
7216
|
async function handleTest(skillArg, options) {
|
|
7217
7217
|
let skillNames;
|
|
7218
7218
|
if (skillArg) {
|
|
7219
|
-
const registryName = skillArg
|
|
7219
|
+
const registryName = skillArg;
|
|
7220
7220
|
if (!getSkill(registryName)) {
|
|
7221
7221
|
if (options.json) {
|
|
7222
7222
|
console.log(JSON.stringify({ error: `Skill '${skillArg}' not found` }));
|
|
@@ -7354,7 +7354,7 @@ function handleWhoami(options) {
|
|
|
7354
7354
|
let skillCount = 0;
|
|
7355
7355
|
if (exists)
|
|
7356
7356
|
try {
|
|
7357
|
-
skillCount = readdirSync6(agentSkillsPath).filter((f) => f.startsWith("
|
|
7357
|
+
skillCount = readdirSync6(agentSkillsPath).filter((f) => !f.startsWith(".") && statSync5(join8(agentSkillsPath, f)).isDirectory()).length;
|
|
7358
7358
|
} catch {}
|
|
7359
7359
|
agentConfigs.push({ agent, label: AGENT_LABELS[agent], path: agentSkillsPath, exists, skillCount });
|
|
7360
7360
|
}
|
|
@@ -47223,11 +47223,58 @@ function recordScheduleRun(id, status, targetDir) {
|
|
|
47223
47223
|
}
|
|
47224
47224
|
var init_scheduler = () => {};
|
|
47225
47225
|
|
|
47226
|
+
// src/lib/feedback.ts
|
|
47227
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
|
|
47228
|
+
import { homedir as homedir6 } from "os";
|
|
47229
|
+
import { dirname as dirname5, join as join12 } from "path";
|
|
47230
|
+
function getFeedbackDbPath() {
|
|
47231
|
+
return process.env.SKILLS_FEEDBACK_DB_PATH || join12(homedir6(), ".hasna", "skills", "skills.db");
|
|
47232
|
+
}
|
|
47233
|
+
function getFeedbackDb() {
|
|
47234
|
+
const dbPath = getFeedbackDbPath();
|
|
47235
|
+
const dir = dirname5(dbPath);
|
|
47236
|
+
if (!existsSync12(dir))
|
|
47237
|
+
mkdirSync5(dir, { recursive: true });
|
|
47238
|
+
const db = new SqliteAdapter(dbPath);
|
|
47239
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
47240
|
+
db.exec([
|
|
47241
|
+
"CREATE TABLE IF NOT EXISTS feedback (",
|
|
47242
|
+
"id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),",
|
|
47243
|
+
"message TEXT NOT NULL,",
|
|
47244
|
+
"email TEXT,",
|
|
47245
|
+
"category TEXT DEFAULT 'general',",
|
|
47246
|
+
"agent TEXT,",
|
|
47247
|
+
"version TEXT,",
|
|
47248
|
+
"machine_id TEXT,",
|
|
47249
|
+
"created_at TEXT NOT NULL DEFAULT (datetime('now'))",
|
|
47250
|
+
")"
|
|
47251
|
+
].join(" "));
|
|
47252
|
+
try {
|
|
47253
|
+
db.exec("ALTER TABLE feedback ADD COLUMN agent TEXT");
|
|
47254
|
+
} catch {}
|
|
47255
|
+
return db;
|
|
47256
|
+
}
|
|
47257
|
+
function saveFeedback2(input) {
|
|
47258
|
+
const message = input.message.trim();
|
|
47259
|
+
if (!message)
|
|
47260
|
+
throw new Error("Feedback message is required");
|
|
47261
|
+
const category = input.category ?? "general";
|
|
47262
|
+
const db = getFeedbackDb();
|
|
47263
|
+
try {
|
|
47264
|
+
db.run("INSERT INTO feedback (message, email, category, agent, version) VALUES (?, ?, ?, ?, ?)", [message, input.email || null, category, input.agent || null, input.version || null]);
|
|
47265
|
+
} finally {
|
|
47266
|
+
db.close();
|
|
47267
|
+
}
|
|
47268
|
+
return { saved: true, category, path: getFeedbackDbPath() };
|
|
47269
|
+
}
|
|
47270
|
+
var init_feedback = __esm(() => {
|
|
47271
|
+
init_dist();
|
|
47272
|
+
});
|
|
47273
|
+
|
|
47226
47274
|
// src/mcp/index.ts
|
|
47227
47275
|
var exports_mcp = {};
|
|
47228
|
-
import { existsSync as
|
|
47229
|
-
import { join as
|
|
47230
|
-
import { homedir as homedir6 } from "os";
|
|
47276
|
+
import { existsSync as existsSync13, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
|
|
47277
|
+
import { join as join13 } from "path";
|
|
47231
47278
|
function stripNulls(obj) {
|
|
47232
47279
|
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined && !(Array.isArray(v) && v.length === 0)));
|
|
47233
47280
|
}
|
|
@@ -47254,17 +47301,6 @@ function mcpError(code, message, suggestions) {
|
|
|
47254
47301
|
isError: true
|
|
47255
47302
|
};
|
|
47256
47303
|
}
|
|
47257
|
-
function getFeedbackDb() {
|
|
47258
|
-
const home = homedir6();
|
|
47259
|
-
const dbPath = join12(home, ".hasna", "skills", "skills.db");
|
|
47260
|
-
const dir = dirname5(dbPath);
|
|
47261
|
-
if (!existsSync12(dir))
|
|
47262
|
-
mkdirSync5(dir, { recursive: true });
|
|
47263
|
-
const db = new SqliteAdapter(dbPath);
|
|
47264
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
47265
|
-
db.exec("CREATE TABLE IF NOT EXISTS feedback (id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), message TEXT NOT NULL, email TEXT, category TEXT DEFAULT 'general', version TEXT, machine_id TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')))");
|
|
47266
|
-
return db;
|
|
47267
|
-
}
|
|
47268
47304
|
async function main() {
|
|
47269
47305
|
const transport = new StdioServerTransport;
|
|
47270
47306
|
registerCloudTools(server, "skills");
|
|
@@ -47281,6 +47317,7 @@ var init_mcp2 = __esm(() => {
|
|
|
47281
47317
|
init_installer();
|
|
47282
47318
|
init_skillinfo();
|
|
47283
47319
|
init_scheduler();
|
|
47320
|
+
init_feedback();
|
|
47284
47321
|
server = new McpServer({
|
|
47285
47322
|
name: "skills",
|
|
47286
47323
|
version: package_default.version
|
|
@@ -47607,13 +47644,13 @@ var init_mcp2 = __esm(() => {
|
|
|
47607
47644
|
const agents = [];
|
|
47608
47645
|
for (const agent of AGENT_TARGETS) {
|
|
47609
47646
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
47610
|
-
const exists =
|
|
47647
|
+
const exists = existsSync13(agentSkillsPath);
|
|
47611
47648
|
let skillCount = 0;
|
|
47612
47649
|
if (exists) {
|
|
47613
47650
|
try {
|
|
47614
47651
|
skillCount = readdirSync8(agentSkillsPath).filter((f) => {
|
|
47615
|
-
const full =
|
|
47616
|
-
return f.startsWith("
|
|
47652
|
+
const full = join13(agentSkillsPath, f);
|
|
47653
|
+
return !f.startsWith(".") && statSync6(full).isDirectory();
|
|
47617
47654
|
}).length;
|
|
47618
47655
|
} catch {}
|
|
47619
47656
|
}
|
|
@@ -47693,17 +47730,17 @@ var init_mcp2 = __esm(() => {
|
|
|
47693
47730
|
}, async ({ name }) => {
|
|
47694
47731
|
const skillPath = getSkillPath(name);
|
|
47695
47732
|
const issues = [];
|
|
47696
|
-
if (!
|
|
47733
|
+
if (!existsSync13(skillPath)) {
|
|
47697
47734
|
return {
|
|
47698
47735
|
content: [{ type: "text", text: JSON.stringify({ name, valid: false, issues: [`Skill directory not found: ${skillPath}`] }) }]
|
|
47699
47736
|
};
|
|
47700
47737
|
}
|
|
47701
|
-
if (!
|
|
47738
|
+
if (!existsSync13(join13(skillPath, "SKILL.md")))
|
|
47702
47739
|
issues.push("Missing SKILL.md");
|
|
47703
|
-
if (!
|
|
47740
|
+
if (!existsSync13(join13(skillPath, "tsconfig.json")))
|
|
47704
47741
|
issues.push("Missing tsconfig.json");
|
|
47705
|
-
const pkgPath =
|
|
47706
|
-
if (!
|
|
47742
|
+
const pkgPath = join13(skillPath, "package.json");
|
|
47743
|
+
if (!existsSync13(pkgPath)) {
|
|
47707
47744
|
issues.push("Missing package.json");
|
|
47708
47745
|
} else {
|
|
47709
47746
|
try {
|
|
@@ -47714,10 +47751,10 @@ var init_mcp2 = __esm(() => {
|
|
|
47714
47751
|
issues.push("package.json is invalid JSON");
|
|
47715
47752
|
}
|
|
47716
47753
|
}
|
|
47717
|
-
const srcDir =
|
|
47718
|
-
if (!
|
|
47754
|
+
const srcDir = join13(skillPath, "src");
|
|
47755
|
+
if (!existsSync13(srcDir)) {
|
|
47719
47756
|
issues.push("Missing src/ directory");
|
|
47720
|
-
} else if (!
|
|
47757
|
+
} else if (!existsSync13(join13(srcDir, "index.ts"))) {
|
|
47721
47758
|
issues.push("Missing src/index.ts");
|
|
47722
47759
|
}
|
|
47723
47760
|
const valid = issues.length === 0;
|
|
@@ -47841,12 +47878,10 @@ var init_mcp2 = __esm(() => {
|
|
|
47841
47878
|
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
47842
47879
|
return { content: [{ type: "text", text: JSON.stringify(agents, null, 2) }] };
|
|
47843
47880
|
});
|
|
47844
|
-
server.tool("send_feedback", "Send feedback about this service", { message: exports_external.string(), email: exports_external.string().optional(), category: exports_external.enum(["bug", "feature", "general"]).optional() }, async (params) => {
|
|
47881
|
+
server.tool("send_feedback", "Send feedback about this service", { message: exports_external.string(), email: exports_external.string().optional(), agent: exports_external.string().optional(), category: exports_external.enum(["bug", "feature", "general"]).optional() }, async (params) => {
|
|
47845
47882
|
try {
|
|
47846
|
-
const
|
|
47847
|
-
|
|
47848
|
-
db.close();
|
|
47849
|
-
return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
|
|
47883
|
+
const result = saveFeedback2({ ...params, version: package_default.version });
|
|
47884
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
47850
47885
|
} catch (e) {
|
|
47851
47886
|
return { content: [{ type: "text", text: String(e) }], isError: true };
|
|
47852
47887
|
}
|
|
@@ -47863,7 +47898,7 @@ __export(exports_runtime, {
|
|
|
47863
47898
|
registerRuntime: () => registerRuntime
|
|
47864
47899
|
});
|
|
47865
47900
|
import chalk7 from "chalk";
|
|
47866
|
-
import { join as
|
|
47901
|
+
import { join as join14 } from "path";
|
|
47867
47902
|
import { homedir as homedir8 } from "os";
|
|
47868
47903
|
function registerRuntime(parent) {
|
|
47869
47904
|
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill").allowUnknownOption(true).passThroughOptions(true).description("Run a skill directly").action(async (name, args) => handleRun(name, args));
|
|
@@ -47914,7 +47949,7 @@ async function handleRun(name, args) {
|
|
|
47914
47949
|
async function handleMcp(options) {
|
|
47915
47950
|
if (options.register) {
|
|
47916
47951
|
const agents = options.register === "all" ? [...AGENT_TARGETS] : [options.register];
|
|
47917
|
-
const binPath =
|
|
47952
|
+
const binPath = join14(import.meta.dir, "..", "mcp", "index.ts");
|
|
47918
47953
|
for (const agent of agents) {
|
|
47919
47954
|
if (agent === "claude") {
|
|
47920
47955
|
try {
|
|
@@ -47926,10 +47961,10 @@ async function handleMcp(options) {
|
|
|
47926
47961
|
}
|
|
47927
47962
|
} else {
|
|
47928
47963
|
const dirs = {
|
|
47929
|
-
codex:
|
|
47930
|
-
gemini:
|
|
47931
|
-
pi:
|
|
47932
|
-
opencode:
|
|
47964
|
+
codex: join14(homedir8(), ".codex", "config.toml"),
|
|
47965
|
+
gemini: join14(homedir8(), ".gemini", "settings.json"),
|
|
47966
|
+
pi: join14(homedir8(), ".pi", "agent", "mcp.json"),
|
|
47967
|
+
opencode: join14(homedir8(), ".opencode", "config.json")
|
|
47933
47968
|
};
|
|
47934
47969
|
const cfg = {
|
|
47935
47970
|
codex: `[mcp_servers.skills]
|
|
@@ -48119,7 +48154,8 @@ var init_completion = __esm(() => {
|
|
|
48119
48154
|
"sync",
|
|
48120
48155
|
"validate",
|
|
48121
48156
|
"diff",
|
|
48122
|
-
"schedule"
|
|
48157
|
+
"schedule",
|
|
48158
|
+
"feedback"
|
|
48123
48159
|
];
|
|
48124
48160
|
skillCmds = ["install", "info", "docs", "requires", "run", "remove"];
|
|
48125
48161
|
skillNames = SKILLS.map((s) => s.name);
|
|
@@ -48132,8 +48168,8 @@ __export(exports_create_sync_config, {
|
|
|
48132
48168
|
registerCreateSync: () => registerCreateSync
|
|
48133
48169
|
});
|
|
48134
48170
|
import chalk8 from "chalk";
|
|
48135
|
-
import { existsSync as
|
|
48136
|
-
import { join as
|
|
48171
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, readdirSync as readdirSync9 } from "fs";
|
|
48172
|
+
import { join as join15 } from "path";
|
|
48137
48173
|
import { homedir as homedir9 } from "os";
|
|
48138
48174
|
function registerCreateSync(parent) {
|
|
48139
48175
|
const configCmd = parent.command("config").description("Manage skills configuration");
|
|
@@ -48164,18 +48200,18 @@ function registerCreateSync(parent) {
|
|
|
48164
48200
|
configCmd.command("path").description("Show configuration file paths").action(() => {
|
|
48165
48201
|
const gp = getConfigPath("global");
|
|
48166
48202
|
const pp = getConfigPath("project");
|
|
48167
|
-
console.log(`${chalk8.cyan("global")}: ${gp}${
|
|
48168
|
-
console.log(`${chalk8.cyan("project")}: ${pp}${
|
|
48203
|
+
console.log(`${chalk8.cyan("global")}: ${gp}${existsSync14(gp) ? chalk8.green(" (exists)") : chalk8.dim(" (not found)")}`);
|
|
48204
|
+
console.log(`${chalk8.cyan("project")}: ${pp}${existsSync14(pp) ? chalk8.green(" (exists)") : chalk8.dim(" (not found)")}`);
|
|
48169
48205
|
});
|
|
48170
48206
|
parent.command("create").argument("<name>", "Skill name (e.g. my-tool)").option("--category <category>", "Skill category", "Development Tools").option("--description <description>", "Short description of what the skill does").option("--tags <tags>", "Comma-separated tags (e.g. api,testing,automation)").option("--global", "Create in ~/.hasna/skills/custom/ instead of .skills/custom-skills/", false).option("--json", "Output result as JSON", false).description("Scaffold a new custom skill directory").action((name, options) => handleCreate(name, options));
|
|
48171
48207
|
parent.command("sync").option("--to <agent>", "Push custom skills to agent").option("--from <agent>", "List agent skills and show which are unknown").option("--register", "Copy unknown agent skills into ~/.hasna/skills/custom/", false).option("--scope <scope>", "Agent install scope: global or project", "global").option("--json", "Output as JSON", false).description("Sync custom skills with agent directories (--to or --from)").action((options) => handleSync(options));
|
|
48172
48208
|
}
|
|
48173
48209
|
function handleCreate(name, options) {
|
|
48174
|
-
const bare = name.
|
|
48175
|
-
const dirName =
|
|
48176
|
-
const baseDir = options.global ?
|
|
48177
|
-
const skillDir =
|
|
48178
|
-
if (
|
|
48210
|
+
const bare = name.trim();
|
|
48211
|
+
const dirName = bare;
|
|
48212
|
+
const baseDir = options.global ? join15(homedir9(), ".hasna", "skills", "custom") : join15(process.cwd(), ".skills", "custom-skills");
|
|
48213
|
+
const skillDir = join15(baseDir, dirName);
|
|
48214
|
+
if (existsSync14(skillDir)) {
|
|
48179
48215
|
console.log(options.json ? JSON.stringify({ error: `Skill '${bare}' already exists at ${skillDir}` }) : chalk8.red(`Skill '${bare}' already exists at ${skillDir}`));
|
|
48180
48216
|
process.exitCode = 1;
|
|
48181
48217
|
return;
|
|
@@ -48183,8 +48219,8 @@ function handleCreate(name, options) {
|
|
|
48183
48219
|
const description = options.description || `${bare} skill`;
|
|
48184
48220
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()).filter(Boolean) : [bare];
|
|
48185
48221
|
const displayName = bare.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
48186
|
-
mkdirSync6(
|
|
48187
|
-
writeFileSync7(
|
|
48222
|
+
mkdirSync6(join15(skillDir, "src"), { recursive: true });
|
|
48223
|
+
writeFileSync7(join15(skillDir, "SKILL.md"), [
|
|
48188
48224
|
"---",
|
|
48189
48225
|
`name: ${bare}`,
|
|
48190
48226
|
`description: ${description}`,
|
|
@@ -48204,11 +48240,11 @@ function handleCreate(name, options) {
|
|
|
48204
48240
|
""
|
|
48205
48241
|
].join(`
|
|
48206
48242
|
`));
|
|
48207
|
-
writeFileSync7(
|
|
48243
|
+
writeFileSync7(join15(skillDir, "src", "index.ts"), [`#!/usr/bin/env bun`, `/**`, ` * ${displayName} \u2014 ${description}`, ` */`, "", `console.log("${displayName}");`, ""].join(`
|
|
48208
48244
|
`));
|
|
48209
|
-
writeFileSync7(
|
|
48245
|
+
writeFileSync7(join15(skillDir, "package.json"), JSON.stringify({ name: bare, version: "0.1.0", description, bin: { [bare]: "./src/index.ts" }, scripts: { dev: `bun src/index.ts` }, dependencies: {} }, null, 2) + `
|
|
48210
48246
|
`);
|
|
48211
|
-
writeFileSync7(
|
|
48247
|
+
writeFileSync7(join15(skillDir, "tsconfig.json"), JSON.stringify({ compilerOptions: { target: "ES2022", module: "ESNext", moduleResolution: "bundler", strict: true, outDir: "dist" }, include: ["src/**/*.ts"] }, null, 2) + `
|
|
48212
48248
|
`);
|
|
48213
48249
|
clearRegistryCache();
|
|
48214
48250
|
if (options.json)
|
|
@@ -48217,8 +48253,8 @@ function handleCreate(name, options) {
|
|
|
48217
48253
|
console.log(chalk8.green(`\u2713 Created custom skill '${bare}' at ${skillDir}`));
|
|
48218
48254
|
console.log(chalk8.dim(` Category: ${options.category}`));
|
|
48219
48255
|
console.log(chalk8.dim(` Tags: ${tags.join(", ")}`));
|
|
48220
|
-
console.log(` ${chalk8.cyan("Edit:")} ${
|
|
48221
|
-
console.log(` ${chalk8.cyan("Run:")} bun ${
|
|
48256
|
+
console.log(` ${chalk8.cyan("Edit:")} ${join15(skillDir, "src", "index.ts")}`);
|
|
48257
|
+
console.log(` ${chalk8.cyan("Run:")} bun ${join15(skillDir, "src", "index.ts")}`);
|
|
48222
48258
|
}
|
|
48223
48259
|
}
|
|
48224
48260
|
function handleSync(options) {
|
|
@@ -48235,7 +48271,7 @@ function handleSync(options) {
|
|
|
48235
48271
|
return;
|
|
48236
48272
|
}
|
|
48237
48273
|
const agentDir = getAgentSkillsDir(agentName, options.scope);
|
|
48238
|
-
if (!
|
|
48274
|
+
if (!existsSync14(agentDir)) {
|
|
48239
48275
|
console.log(options.json ? JSON.stringify({ agentDir, skills: [], message: "Directory not found" }) : chalk8.dim(`No skills directory found at ${agentDir}`));
|
|
48240
48276
|
return;
|
|
48241
48277
|
}
|
|
@@ -48245,21 +48281,21 @@ function handleSync(options) {
|
|
|
48245
48281
|
for (const entry of readdirSync9(agentDir, { withFileTypes: true })) {
|
|
48246
48282
|
if (!entry.isDirectory())
|
|
48247
48283
|
continue;
|
|
48248
|
-
const bare = entry.name
|
|
48249
|
-
found.push({ name: bare, path:
|
|
48284
|
+
const bare = entry.name;
|
|
48285
|
+
found.push({ name: bare, path: join15(agentDir, entry.name), inRegistry: registryNames.has(bare) });
|
|
48250
48286
|
}
|
|
48251
48287
|
const unknown3 = found.filter((s) => !s.inRegistry);
|
|
48252
48288
|
if (options.register && unknown3.length > 0) {
|
|
48253
|
-
const globalSkillsDir =
|
|
48289
|
+
const globalSkillsDir = join15(homedir9(), ".hasna", "skills", "custom");
|
|
48254
48290
|
const registered = [];
|
|
48255
48291
|
for (const s of unknown3) {
|
|
48256
|
-
const srcSkillMd =
|
|
48257
|
-
if (!
|
|
48292
|
+
const srcSkillMd = join15(s.path, "SKILL.md");
|
|
48293
|
+
if (!existsSync14(srcSkillMd))
|
|
48258
48294
|
continue;
|
|
48259
|
-
const destDir =
|
|
48260
|
-
if (!
|
|
48295
|
+
const destDir = join15(globalSkillsDir, s.name);
|
|
48296
|
+
if (!existsSync14(destDir))
|
|
48261
48297
|
mkdirSync6(destDir, { recursive: true });
|
|
48262
|
-
writeFileSync7(
|
|
48298
|
+
writeFileSync7(join15(destDir, "SKILL.md"), readFileSync12(srcSkillMd, "utf-8"));
|
|
48263
48299
|
registered.push(s.name);
|
|
48264
48300
|
}
|
|
48265
48301
|
clearRegistryCache();
|
|
@@ -48450,6 +48486,51 @@ var init_schedule = __esm(() => {
|
|
|
48450
48486
|
init_scheduler();
|
|
48451
48487
|
});
|
|
48452
48488
|
|
|
48489
|
+
// src/cli/commands/feedback.ts
|
|
48490
|
+
var exports_feedback = {};
|
|
48491
|
+
__export(exports_feedback, {
|
|
48492
|
+
registerFeedback: () => registerFeedback
|
|
48493
|
+
});
|
|
48494
|
+
import chalk10 from "chalk";
|
|
48495
|
+
function registerFeedback(parent) {
|
|
48496
|
+
parent.command("feedback").argument("<message...>", "Feedback message").option("--category <category>", "Feedback category: bug, feature, or general", "general").option("--email <email>", "Contact email for follow-up").option("--agent <name>", "Agent name sending feedback").option("--json", "Output as JSON", false).description("Send feedback from an agent or local CLI session").action((messageParts, options) => {
|
|
48497
|
+
const category = options.category;
|
|
48498
|
+
if (!["bug", "feature", "general"].includes(category)) {
|
|
48499
|
+
const error48 = `Invalid category: ${options.category}. Use bug, feature, or general.`;
|
|
48500
|
+
if (options.json)
|
|
48501
|
+
console.log(JSON.stringify({ saved: false, error: error48 }));
|
|
48502
|
+
else
|
|
48503
|
+
console.error(chalk10.red(error48));
|
|
48504
|
+
process.exitCode = 1;
|
|
48505
|
+
return;
|
|
48506
|
+
}
|
|
48507
|
+
try {
|
|
48508
|
+
const result = saveFeedback2({
|
|
48509
|
+
message: messageParts.join(" "),
|
|
48510
|
+
category,
|
|
48511
|
+
email: options.email,
|
|
48512
|
+
agent: options.agent,
|
|
48513
|
+
version: package_default.version
|
|
48514
|
+
});
|
|
48515
|
+
if (options.json)
|
|
48516
|
+
console.log(JSON.stringify(result, null, 2));
|
|
48517
|
+
else
|
|
48518
|
+
console.log(chalk10.green(`Feedback saved (${result.category})`));
|
|
48519
|
+
} catch (error48) {
|
|
48520
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
48521
|
+
if (options.json)
|
|
48522
|
+
console.log(JSON.stringify({ saved: false, error: message }));
|
|
48523
|
+
else
|
|
48524
|
+
console.error(chalk10.red(message));
|
|
48525
|
+
process.exitCode = 1;
|
|
48526
|
+
}
|
|
48527
|
+
});
|
|
48528
|
+
}
|
|
48529
|
+
var init_feedback2 = __esm(() => {
|
|
48530
|
+
init_package();
|
|
48531
|
+
init_feedback();
|
|
48532
|
+
});
|
|
48533
|
+
|
|
48453
48534
|
// src/cli/index.tsx
|
|
48454
48535
|
import { render } from "ink";
|
|
48455
48536
|
|
|
@@ -48471,7 +48552,7 @@ var {
|
|
|
48471
48552
|
|
|
48472
48553
|
// src/cli/index.tsx
|
|
48473
48554
|
init_package();
|
|
48474
|
-
import
|
|
48555
|
+
import chalk11 from "chalk";
|
|
48475
48556
|
|
|
48476
48557
|
// src/cli/components/App.tsx
|
|
48477
48558
|
import { useState as useState7 } from "react";
|
|
@@ -49596,7 +49677,7 @@ init_registry();
|
|
|
49596
49677
|
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
49597
49678
|
var isTTY = (process.stdout.isTTY ?? false) && (process.stdin.isTTY ?? false);
|
|
49598
49679
|
if (process.argv.includes("--no-color")) {
|
|
49599
|
-
|
|
49680
|
+
chalk11.level = 0;
|
|
49600
49681
|
const idx = process.argv.indexOf("--no-color");
|
|
49601
49682
|
process.argv.splice(idx, 1);
|
|
49602
49683
|
}
|
|
@@ -49627,4 +49708,6 @@ var { registerCreateSync: registerCreateSync2 } = await Promise.resolve().then((
|
|
|
49627
49708
|
registerCreateSync2(program2);
|
|
49628
49709
|
var { registerSchedule: registerSchedule2 } = await Promise.resolve().then(() => (init_schedule(), exports_schedule));
|
|
49629
49710
|
registerSchedule2(program2);
|
|
49711
|
+
var { registerFeedback: registerFeedback2 } = await Promise.resolve().then(() => (init_feedback2(), exports_feedback));
|
|
49712
|
+
registerFeedback2(program2);
|
|
49630
49713
|
program2.parse();
|
package/bin/mcp.js
CHANGED
|
@@ -28561,9 +28561,8 @@ class StdioServerTransport {
|
|
|
28561
28561
|
}
|
|
28562
28562
|
|
|
28563
28563
|
// src/mcp/index.ts
|
|
28564
|
-
import { existsSync as
|
|
28565
|
-
import { join as
|
|
28566
|
-
import { homedir as homedir9 } from "os";
|
|
28564
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, statSync as statSync3 } from "fs";
|
|
28565
|
+
import { join as join11 } from "path";
|
|
28567
28566
|
|
|
28568
28567
|
// node_modules/@hasna/cloud/dist/index.js
|
|
28569
28568
|
import { createRequire } from "module";
|
|
@@ -38746,12 +38745,12 @@ init_adapter();
|
|
|
38746
38745
|
// package.json
|
|
38747
38746
|
var package_default = {
|
|
38748
38747
|
name: "@hasna/skills",
|
|
38749
|
-
version: "0.1.
|
|
38748
|
+
version: "0.1.25",
|
|
38750
38749
|
description: "Skills library for AI coding agents",
|
|
38751
38750
|
type: "module",
|
|
38752
38751
|
bin: {
|
|
38753
|
-
skills: "
|
|
38754
|
-
"skills-mcp": "
|
|
38752
|
+
skills: "bin/index.js",
|
|
38753
|
+
"skills-mcp": "bin/mcp.js"
|
|
38755
38754
|
},
|
|
38756
38755
|
exports: {
|
|
38757
38756
|
".": {
|
|
@@ -40529,7 +40528,7 @@ function discoverSkillsInDir(dir) {
|
|
|
40529
40528
|
const fm = parseSkillMdFrontmatter(content);
|
|
40530
40529
|
if (!fm?.name)
|
|
40531
40530
|
continue;
|
|
40532
|
-
const name = fm.name
|
|
40531
|
+
const name = fm.name;
|
|
40533
40532
|
result.push({
|
|
40534
40533
|
name,
|
|
40535
40534
|
displayName: fm.displayName || name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -41342,6 +41341,51 @@ function removeSchedule(idOrName, targetDir) {
|
|
|
41342
41341
|
return true;
|
|
41343
41342
|
}
|
|
41344
41343
|
|
|
41344
|
+
// src/lib/feedback.ts
|
|
41345
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
41346
|
+
import { homedir as homedir9 } from "os";
|
|
41347
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
41348
|
+
function getFeedbackDbPath() {
|
|
41349
|
+
return process.env.SKILLS_FEEDBACK_DB_PATH || join10(homedir9(), ".hasna", "skills", "skills.db");
|
|
41350
|
+
}
|
|
41351
|
+
function getFeedbackDb() {
|
|
41352
|
+
const dbPath = getFeedbackDbPath();
|
|
41353
|
+
const dir = dirname3(dbPath);
|
|
41354
|
+
if (!existsSync9(dir))
|
|
41355
|
+
mkdirSync6(dir, { recursive: true });
|
|
41356
|
+
const db = new SqliteAdapter(dbPath);
|
|
41357
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
41358
|
+
db.exec([
|
|
41359
|
+
"CREATE TABLE IF NOT EXISTS feedback (",
|
|
41360
|
+
"id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),",
|
|
41361
|
+
"message TEXT NOT NULL,",
|
|
41362
|
+
"email TEXT,",
|
|
41363
|
+
"category TEXT DEFAULT 'general',",
|
|
41364
|
+
"agent TEXT,",
|
|
41365
|
+
"version TEXT,",
|
|
41366
|
+
"machine_id TEXT,",
|
|
41367
|
+
"created_at TEXT NOT NULL DEFAULT (datetime('now'))",
|
|
41368
|
+
")"
|
|
41369
|
+
].join(" "));
|
|
41370
|
+
try {
|
|
41371
|
+
db.exec("ALTER TABLE feedback ADD COLUMN agent TEXT");
|
|
41372
|
+
} catch {}
|
|
41373
|
+
return db;
|
|
41374
|
+
}
|
|
41375
|
+
function saveFeedback2(input) {
|
|
41376
|
+
const message = input.message.trim();
|
|
41377
|
+
if (!message)
|
|
41378
|
+
throw new Error("Feedback message is required");
|
|
41379
|
+
const category = input.category ?? "general";
|
|
41380
|
+
const db = getFeedbackDb();
|
|
41381
|
+
try {
|
|
41382
|
+
db.run("INSERT INTO feedback (message, email, category, agent, version) VALUES (?, ?, ?, ?, ?)", [message, input.email || null, category, input.agent || null, input.version || null]);
|
|
41383
|
+
} finally {
|
|
41384
|
+
db.close();
|
|
41385
|
+
}
|
|
41386
|
+
return { saved: true, category, path: getFeedbackDbPath() };
|
|
41387
|
+
}
|
|
41388
|
+
|
|
41345
41389
|
// src/mcp/index.ts
|
|
41346
41390
|
var server = new McpServer({
|
|
41347
41391
|
name: "skills",
|
|
@@ -41696,13 +41740,13 @@ server.registerTool("whoami", {
|
|
|
41696
41740
|
const agents = [];
|
|
41697
41741
|
for (const agent of AGENT_TARGETS) {
|
|
41698
41742
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
41699
|
-
const exists =
|
|
41743
|
+
const exists = existsSync10(agentSkillsPath);
|
|
41700
41744
|
let skillCount = 0;
|
|
41701
41745
|
if (exists) {
|
|
41702
41746
|
try {
|
|
41703
41747
|
skillCount = readdirSync7(agentSkillsPath).filter((f) => {
|
|
41704
|
-
const full =
|
|
41705
|
-
return f.startsWith("
|
|
41748
|
+
const full = join11(agentSkillsPath, f);
|
|
41749
|
+
return !f.startsWith(".") && statSync3(full).isDirectory();
|
|
41706
41750
|
}).length;
|
|
41707
41751
|
} catch {}
|
|
41708
41752
|
}
|
|
@@ -41782,17 +41826,17 @@ server.registerTool("validate_skill", {
|
|
|
41782
41826
|
}, async ({ name }) => {
|
|
41783
41827
|
const skillPath = getSkillPath(name);
|
|
41784
41828
|
const issues = [];
|
|
41785
|
-
if (!
|
|
41829
|
+
if (!existsSync10(skillPath)) {
|
|
41786
41830
|
return {
|
|
41787
41831
|
content: [{ type: "text", text: JSON.stringify({ name, valid: false, issues: [`Skill directory not found: ${skillPath}`] }) }]
|
|
41788
41832
|
};
|
|
41789
41833
|
}
|
|
41790
|
-
if (!
|
|
41834
|
+
if (!existsSync10(join11(skillPath, "SKILL.md")))
|
|
41791
41835
|
issues.push("Missing SKILL.md");
|
|
41792
|
-
if (!
|
|
41836
|
+
if (!existsSync10(join11(skillPath, "tsconfig.json")))
|
|
41793
41837
|
issues.push("Missing tsconfig.json");
|
|
41794
|
-
const pkgPath =
|
|
41795
|
-
if (!
|
|
41838
|
+
const pkgPath = join11(skillPath, "package.json");
|
|
41839
|
+
if (!existsSync10(pkgPath)) {
|
|
41796
41840
|
issues.push("Missing package.json");
|
|
41797
41841
|
} else {
|
|
41798
41842
|
try {
|
|
@@ -41803,10 +41847,10 @@ server.registerTool("validate_skill", {
|
|
|
41803
41847
|
issues.push("package.json is invalid JSON");
|
|
41804
41848
|
}
|
|
41805
41849
|
}
|
|
41806
|
-
const srcDir =
|
|
41807
|
-
if (!
|
|
41850
|
+
const srcDir = join11(skillPath, "src");
|
|
41851
|
+
if (!existsSync10(srcDir)) {
|
|
41808
41852
|
issues.push("Missing src/ directory");
|
|
41809
|
-
} else if (!
|
|
41853
|
+
} else if (!existsSync10(join11(srcDir, "index.ts"))) {
|
|
41810
41854
|
issues.push("Missing src/index.ts");
|
|
41811
41855
|
}
|
|
41812
41856
|
const valid = issues.length === 0;
|
|
@@ -41930,23 +41974,10 @@ server.tool("list_agents", "List all registered agents.", {}, async () => {
|
|
|
41930
41974
|
return { content: [{ type: "text", text: "No agents registered." }] };
|
|
41931
41975
|
return { content: [{ type: "text", text: JSON.stringify(agents, null, 2) }] };
|
|
41932
41976
|
});
|
|
41933
|
-
|
|
41934
|
-
const home = homedir9();
|
|
41935
|
-
const dbPath = join10(home, ".hasna", "skills", "skills.db");
|
|
41936
|
-
const dir = dirname3(dbPath);
|
|
41937
|
-
if (!existsSync9(dir))
|
|
41938
|
-
mkdirSync6(dir, { recursive: true });
|
|
41939
|
-
const db = new SqliteAdapter(dbPath);
|
|
41940
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
41941
|
-
db.exec("CREATE TABLE IF NOT EXISTS feedback (id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), message TEXT NOT NULL, email TEXT, category TEXT DEFAULT 'general', version TEXT, machine_id TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')))");
|
|
41942
|
-
return db;
|
|
41943
|
-
}
|
|
41944
|
-
server.tool("send_feedback", "Send feedback about this service", { message: exports_external.string(), email: exports_external.string().optional(), category: exports_external.enum(["bug", "feature", "general"]).optional() }, async (params) => {
|
|
41977
|
+
server.tool("send_feedback", "Send feedback about this service", { message: exports_external.string(), email: exports_external.string().optional(), agent: exports_external.string().optional(), category: exports_external.enum(["bug", "feature", "general"]).optional() }, async (params) => {
|
|
41945
41978
|
try {
|
|
41946
|
-
const
|
|
41947
|
-
|
|
41948
|
-
db.close();
|
|
41949
|
-
return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
|
|
41979
|
+
const result = saveFeedback2({ ...params, version: package_default.version });
|
|
41980
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
41950
41981
|
} catch (e) {
|
|
41951
41982
|
return { content: [{ type: "text", text: String(e) }], isError: true };
|
|
41952
41983
|
}
|
package/dist/index.js
CHANGED
|
@@ -1697,7 +1697,7 @@ function discoverSkillsInDir(dir) {
|
|
|
1697
1697
|
const fm = parseSkillMdFrontmatter(content);
|
|
1698
1698
|
if (!fm?.name)
|
|
1699
1699
|
continue;
|
|
1700
|
-
const name = fm.name
|
|
1700
|
+
const name = fm.name;
|
|
1701
1701
|
result.push({
|
|
1702
1702
|
name,
|
|
1703
1703
|
displayName: fm.displayName || name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type FeedbackCategory = "bug" | "feature" | "general";
|
|
2
|
+
export interface FeedbackInput {
|
|
3
|
+
message: string;
|
|
4
|
+
category?: FeedbackCategory;
|
|
5
|
+
email?: string;
|
|
6
|
+
agent?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface FeedbackResult {
|
|
10
|
+
saved: true;
|
|
11
|
+
category: FeedbackCategory;
|
|
12
|
+
path: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function getFeedbackDbPath(): string;
|
|
15
|
+
export declare function saveFeedback(input: FeedbackInput): FeedbackResult;
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "Skills library for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"skills": "
|
|
8
|
-
"skills-mcp": "
|
|
7
|
+
"skills": "bin/index.js",
|
|
8
|
+
"skills-mcp": "bin/mcp.js"
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
@@ -47,7 +47,7 @@ export async function executeSkill(request: SkillRequest): Promise<SkillResponse
|
|
|
47
47
|
method: "POST",
|
|
48
48
|
headers: {
|
|
49
49
|
"Content-Type": "application/json",
|
|
50
|
-
"User-Agent":
|
|
50
|
+
"User-Agent": `${skill}-cli/1.0`,
|
|
51
51
|
"X-API-Key": apiKey
|
|
52
52
|
},
|
|
53
53
|
body: JSON.stringify(params)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Skill Installer Module
|
|
5
5
|
*
|
|
6
6
|
* Universal installer for skills to integrate with AI code assistants
|
|
7
|
-
* Usage:
|
|
7
|
+
* Usage: skills run [name] -- install [claude|codex|windsurf|cursor]
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
|
|
@@ -47,7 +47,7 @@ export async function executeSkill(request: SkillRequest): Promise<SkillResponse
|
|
|
47
47
|
method: "POST",
|
|
48
48
|
headers: {
|
|
49
49
|
"Content-Type": "application/json",
|
|
50
|
-
"User-Agent":
|
|
50
|
+
"User-Agent": `${skill}-cli/1.0`,
|
|
51
51
|
"X-API-Key": apiKey
|
|
52
52
|
},
|
|
53
53
|
body: JSON.stringify(params)
|
|
@@ -5,7 +5,7 @@ import { join } from 'node:path';
|
|
|
5
5
|
* Standard skill data directory structure
|
|
6
6
|
*
|
|
7
7
|
* ~/.skills/
|
|
8
|
-
* └──
|
|
8
|
+
* └── [name]/
|
|
9
9
|
* ├── exports/ # Output data, results, artifacts
|
|
10
10
|
* ├── logs/ # Execution logs, debug info
|
|
11
11
|
* ├── cache/ # Temporary cached data
|