autohand-cli 0.5.0 → 0.6.0
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/LICENSE +233 -0
- package/README.md +257 -68
- package/dist/{agents-RB34F4XE.js → agents-7GZ2VBYU.js} +2 -2
- package/dist/{agents-new-5I3B2W2I.js → agents-new-VALZG4B5.js} +2 -2
- package/dist/chunk-2TPGTNNY.js +252 -0
- package/dist/{chunk-A7HRTONQ.js → chunk-3OD5MR23.js} +9 -7
- package/dist/{chunk-PX5AGAEX.js → chunk-4H3B46YX.js} +1 -1
- package/dist/{chunk-ALMJANSA.js → chunk-6FEZ6JAQ.js} +1 -1
- package/dist/{chunk-KCMWJB53.js → chunk-6PYYLBNT.js} +1 -1
- package/dist/{chunk-2EPIFDFM.js → chunk-737A24RB.js} +10 -1
- package/dist/{chunk-PQJIQBQ5.js → chunk-CYY7D5TQ.js} +1 -1
- package/dist/chunk-NEAJ2UWG.js +191 -0
- package/dist/{chunk-2QAL3HH4.js → chunk-PRZTK2FX.js} +1 -1
- package/dist/{chunk-UW2LYWIM.js → chunk-WMC556GK.js} +2 -2
- package/dist/{feedback-NEDFOKMA.js → feedback-RJNBUBDQ.js} +2 -2
- package/dist/index.cjs +5988 -4019
- package/dist/index.js +8387 -6889
- package/dist/login-HXQ3FDA7.js +10 -0
- package/dist/logout-6KCD4WA3.js +10 -0
- package/dist/skills-POKNCIQV.js +10 -0
- package/dist/skills-new-5VCNKQDP.js +9 -0
- package/dist/{status-IT5CYW37.js → status-RNUAXIRO.js} +1 -1
- package/package.json +1 -1
- package/dist/login-GPXDNB2F.js +0 -10
- package/dist/logout-43W7N6JU.js +0 -10
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AUTOHAND_PATHS
|
|
3
|
+
} from "./chunk-737A24RB.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/skills-new.ts
|
|
6
|
+
import path from "path";
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import enquirer from "enquirer";
|
|
10
|
+
|
|
11
|
+
// src/skills/types.ts
|
|
12
|
+
var SKILL_CONSTRAINTS = {
|
|
13
|
+
NAME_MAX_LENGTH: 64,
|
|
14
|
+
NAME_PATTERN: /^[a-z0-9-]+$/,
|
|
15
|
+
DESCRIPTION_MAX_LENGTH: 1024,
|
|
16
|
+
COMPATIBILITY_MAX_LENGTH: 500
|
|
17
|
+
};
|
|
18
|
+
function isValidSkillName(name) {
|
|
19
|
+
if (!name || typeof name !== "string") return false;
|
|
20
|
+
if (name.length > SKILL_CONSTRAINTS.NAME_MAX_LENGTH) return false;
|
|
21
|
+
return SKILL_CONSTRAINTS.NAME_PATTERN.test(name);
|
|
22
|
+
}
|
|
23
|
+
function validateSkillFrontmatter(frontmatter) {
|
|
24
|
+
const errors = [];
|
|
25
|
+
if (!frontmatter.name) {
|
|
26
|
+
errors.push("Missing required field: name");
|
|
27
|
+
} else if (!isValidSkillName(frontmatter.name)) {
|
|
28
|
+
errors.push(`Invalid name: must be 1-${SKILL_CONSTRAINTS.NAME_MAX_LENGTH} chars, lowercase alphanumeric with hyphens only`);
|
|
29
|
+
}
|
|
30
|
+
if (!frontmatter.description) {
|
|
31
|
+
errors.push("Missing required field: description");
|
|
32
|
+
} else if (frontmatter.description.length > SKILL_CONSTRAINTS.DESCRIPTION_MAX_LENGTH) {
|
|
33
|
+
errors.push(`Description exceeds ${SKILL_CONSTRAINTS.DESCRIPTION_MAX_LENGTH} characters`);
|
|
34
|
+
}
|
|
35
|
+
if (frontmatter.compatibility && frontmatter.compatibility.length > SKILL_CONSTRAINTS.COMPATIBILITY_MAX_LENGTH) {
|
|
36
|
+
errors.push(`Compatibility exceeds ${SKILL_CONSTRAINTS.COMPATIBILITY_MAX_LENGTH} characters`);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
valid: errors.length === 0,
|
|
40
|
+
errors
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/commands/skills-new.ts
|
|
45
|
+
var metadata = {
|
|
46
|
+
command: "/skills new",
|
|
47
|
+
description: "create a new skill from a description",
|
|
48
|
+
implemented: true,
|
|
49
|
+
prd: "prd/skills_feature.md"
|
|
50
|
+
};
|
|
51
|
+
var SIMILARITY_THRESHOLD = 0.9;
|
|
52
|
+
async function createSkill(ctx) {
|
|
53
|
+
const { llm, skillsRegistry } = ctx;
|
|
54
|
+
if (!skillsRegistry) {
|
|
55
|
+
console.log(chalk.red("Skills registry not available."));
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const answers = await enquirer.prompt([
|
|
59
|
+
{
|
|
60
|
+
type: "input",
|
|
61
|
+
name: "name",
|
|
62
|
+
message: "Skill name (lowercase, hyphens allowed)",
|
|
63
|
+
validate: (val) => {
|
|
64
|
+
if (typeof val !== "string" || !val.trim()) {
|
|
65
|
+
return "Name is required";
|
|
66
|
+
}
|
|
67
|
+
if (!isValidSkillName(val.trim())) {
|
|
68
|
+
return "Name must be lowercase alphanumeric with hyphens only (max 64 chars)";
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: "input",
|
|
75
|
+
name: "description",
|
|
76
|
+
message: "Describe what this skill should help with",
|
|
77
|
+
validate: (val) => {
|
|
78
|
+
if (typeof val !== "string" || !val.trim()) {
|
|
79
|
+
return "Description is required";
|
|
80
|
+
}
|
|
81
|
+
if (val.length > 1024) {
|
|
82
|
+
return "Description must be 1024 characters or less";
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
]);
|
|
88
|
+
const name = answers.name.trim().toLowerCase();
|
|
89
|
+
const description = answers.description.trim();
|
|
90
|
+
if (!name) {
|
|
91
|
+
console.log(chalk.gray("Canceled: no name provided."));
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (skillsRegistry.hasSkill(name)) {
|
|
95
|
+
console.log(chalk.yellow(`A skill named "${name}" already exists.`));
|
|
96
|
+
const { overwrite } = await enquirer.prompt({
|
|
97
|
+
type: "confirm",
|
|
98
|
+
name: "overwrite",
|
|
99
|
+
message: "Do you want to overwrite it?",
|
|
100
|
+
initial: false
|
|
101
|
+
});
|
|
102
|
+
if (!overwrite) {
|
|
103
|
+
console.log(chalk.gray("Canceled."));
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const similar = skillsRegistry.findSimilar(description, SIMILARITY_THRESHOLD);
|
|
108
|
+
if (similar.length > 0) {
|
|
109
|
+
console.log(chalk.yellow("\nFound similar existing skill(s):"));
|
|
110
|
+
for (const match of similar.slice(0, 3)) {
|
|
111
|
+
console.log(chalk.gray(` - ${match.skill.name}: ${match.skill.description}`));
|
|
112
|
+
console.log(chalk.gray(` Similarity: ${(match.score * 100).toFixed(0)}%`));
|
|
113
|
+
}
|
|
114
|
+
console.log();
|
|
115
|
+
const { proceed } = await enquirer.prompt({
|
|
116
|
+
type: "select",
|
|
117
|
+
name: "proceed",
|
|
118
|
+
message: "How would you like to proceed?",
|
|
119
|
+
choices: [
|
|
120
|
+
{ name: "continue", message: "Continue creating new skill" },
|
|
121
|
+
{ name: "use", message: `Use existing skill "${similar[0].skill.name}" instead` },
|
|
122
|
+
{ name: "cancel", message: "Cancel" }
|
|
123
|
+
]
|
|
124
|
+
});
|
|
125
|
+
if (proceed === "cancel") {
|
|
126
|
+
console.log(chalk.gray("Canceled."));
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (proceed === "use") {
|
|
130
|
+
const existingSkill = similar[0].skill;
|
|
131
|
+
skillsRegistry.activateSkill(existingSkill.name);
|
|
132
|
+
console.log(chalk.green(`\u2713 Activated existing skill: ${existingSkill.name}`));
|
|
133
|
+
return `Activated existing skill "${existingSkill.name}".`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
console.log(chalk.cyan("\nGenerating skill content..."));
|
|
137
|
+
const prompt = buildPrompt(name, description);
|
|
138
|
+
let content;
|
|
139
|
+
try {
|
|
140
|
+
const completion = await llm.complete({
|
|
141
|
+
messages: [
|
|
142
|
+
{
|
|
143
|
+
role: "system",
|
|
144
|
+
content: `You are an expert at creating Agent Skills (SKILL.md files).
|
|
145
|
+
Generate a complete SKILL.md file with proper YAML frontmatter and markdown body.
|
|
146
|
+
The skill should provide clear, actionable instructions that an AI agent can follow.
|
|
147
|
+
Focus on practical workflows and step-by-step guidance.
|
|
148
|
+
Output only the raw markdown content, no code fences.`
|
|
149
|
+
},
|
|
150
|
+
{ role: "user", content: prompt }
|
|
151
|
+
],
|
|
152
|
+
maxTokens: 1500
|
|
153
|
+
});
|
|
154
|
+
content = completion.content.trim();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error(chalk.red(`Failed to generate skill: ${error.message}`));
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
console.log();
|
|
160
|
+
console.log(chalk.bold("Preview:"));
|
|
161
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
162
|
+
console.log(content.length > 800 ? content.slice(0, 800) + "\n...(truncated)" : content);
|
|
163
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
164
|
+
console.log();
|
|
165
|
+
const { confirm } = await enquirer.prompt({
|
|
166
|
+
type: "confirm",
|
|
167
|
+
name: "confirm",
|
|
168
|
+
message: "Save this skill?",
|
|
169
|
+
initial: true
|
|
170
|
+
});
|
|
171
|
+
if (!confirm) {
|
|
172
|
+
console.log(chalk.gray("Canceled."));
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const skillDir = path.join(AUTOHAND_PATHS.skills, name);
|
|
176
|
+
const skillPath = path.join(skillDir, "SKILL.md");
|
|
177
|
+
try {
|
|
178
|
+
await fs.ensureDir(skillDir);
|
|
179
|
+
await fs.writeFile(skillPath, content + "\n", "utf8");
|
|
180
|
+
console.log(chalk.green(`\u2713 Saved new skill to ${skillPath}`));
|
|
181
|
+
const success = await skillsRegistry.saveSkill(name, content);
|
|
182
|
+
if (success) {
|
|
183
|
+
const { activate } = await enquirer.prompt({
|
|
184
|
+
type: "confirm",
|
|
185
|
+
name: "activate",
|
|
186
|
+
message: "Activate this skill now?",
|
|
187
|
+
initial: true
|
|
188
|
+
});
|
|
189
|
+
if (activate) {
|
|
190
|
+
skillsRegistry.activateSkill(name);
|
|
191
|
+
console.log(chalk.green(`\u2713 Skill "${name}" is now active.`));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(chalk.red(`Failed to save skill: ${error.message}`));
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return `Created new skill: ${name}`;
|
|
199
|
+
}
|
|
200
|
+
function buildPrompt(name, description) {
|
|
201
|
+
return `Create a SKILL.md file for a skill named "${name}".
|
|
202
|
+
|
|
203
|
+
Description: ${description}
|
|
204
|
+
|
|
205
|
+
The file must have:
|
|
206
|
+
1. YAML frontmatter with:
|
|
207
|
+
- name: ${name}
|
|
208
|
+
- description: A clear one-line description (max 100 chars)
|
|
209
|
+
- Optional: license, compatibility, allowed-tools
|
|
210
|
+
|
|
211
|
+
2. Markdown body with:
|
|
212
|
+
- A heading with the skill name
|
|
213
|
+
- Clear purpose statement
|
|
214
|
+
- Step-by-step workflow or instructions
|
|
215
|
+
- Best practices or tips
|
|
216
|
+
- Example usage if applicable
|
|
217
|
+
|
|
218
|
+
Make the instructions practical and actionable for an AI coding agent.
|
|
219
|
+
Keep the total content concise but complete (200-400 words in body).
|
|
220
|
+
|
|
221
|
+
Output format:
|
|
222
|
+
---
|
|
223
|
+
name: ${name}
|
|
224
|
+
description: <description here>
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# <Title>
|
|
228
|
+
|
|
229
|
+
<body content here>`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export {
|
|
233
|
+
validateSkillFrontmatter,
|
|
234
|
+
metadata,
|
|
235
|
+
createSkill
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* @license
|
|
239
|
+
* Copyright 2025 Autohand AI LLC
|
|
240
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
241
|
+
*
|
|
242
|
+
* Skills Types - Agent Skills standard implementation
|
|
243
|
+
* Skills are instruction packages (workflows, guides) that provide specialized
|
|
244
|
+
* instructions to the agent, similar to on-demand AGENTS.md files.
|
|
245
|
+
*/
|
|
246
|
+
/**
|
|
247
|
+
* @license
|
|
248
|
+
* Copyright 2025 Autohand AI LLC
|
|
249
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
250
|
+
*
|
|
251
|
+
* Skills new command - Generate a new skill from description using LLM
|
|
252
|
+
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AUTH_CONFIG,
|
|
3
3
|
AUTOHAND_FILES
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-737A24RB.js";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
7
|
import fs from "fs-extra";
|
|
@@ -44,13 +44,14 @@ async function parseConfigFile(configPath) {
|
|
|
44
44
|
async function loadConfig(customPath) {
|
|
45
45
|
const configPath = await detectConfigPath(customPath);
|
|
46
46
|
await fs.ensureDir(path.dirname(configPath));
|
|
47
|
+
let isNewConfig = false;
|
|
47
48
|
if (!await fs.pathExists(configPath)) {
|
|
48
49
|
const defaultConfig = {
|
|
49
50
|
provider: "openrouter",
|
|
50
51
|
openrouter: {
|
|
51
|
-
apiKey: "
|
|
52
|
+
apiKey: "",
|
|
52
53
|
baseUrl: "https://openrouter.ai/api/v1",
|
|
53
|
-
model: "anthropic/claude-
|
|
54
|
+
model: "anthropic/claude-sonnet-4-20250514"
|
|
54
55
|
},
|
|
55
56
|
workspace: {
|
|
56
57
|
defaultRoot: process.cwd(),
|
|
@@ -59,12 +60,13 @@ async function loadConfig(customPath) {
|
|
|
59
60
|
ui: {
|
|
60
61
|
theme: "dark",
|
|
61
62
|
autoConfirm: false
|
|
63
|
+
},
|
|
64
|
+
telemetry: {
|
|
65
|
+
enabled: true
|
|
62
66
|
}
|
|
63
67
|
};
|
|
64
68
|
await fs.writeJson(configPath, defaultConfig, { spaces: 2 });
|
|
65
|
-
|
|
66
|
-
`Created default config at ${configPath}. Please update it with your OpenRouter credentials before rerunning.`
|
|
67
|
-
);
|
|
69
|
+
isNewConfig = true;
|
|
68
70
|
}
|
|
69
71
|
let parsed;
|
|
70
72
|
try {
|
|
@@ -75,7 +77,7 @@ async function loadConfig(customPath) {
|
|
|
75
77
|
const normalized = normalizeConfig(parsed);
|
|
76
78
|
const withEnv = mergeEnvVariables(normalized);
|
|
77
79
|
validateConfig(withEnv, configPath);
|
|
78
|
-
return { ...withEnv, configPath };
|
|
80
|
+
return { ...withEnv, configPath, isNewConfig };
|
|
79
81
|
}
|
|
80
82
|
function mergeEnvVariables(config) {
|
|
81
83
|
return {
|
|
@@ -20,7 +20,9 @@ var AUTOHAND_PATHS = {
|
|
|
20
20
|
/** Agent definitions */
|
|
21
21
|
agents: path.join(AUTOHAND_HOME, "agents"),
|
|
22
22
|
/** Custom tools */
|
|
23
|
-
tools: path.join(AUTOHAND_HOME, "tools")
|
|
23
|
+
tools: path.join(AUTOHAND_HOME, "tools"),
|
|
24
|
+
/** Skills (instruction packages) */
|
|
25
|
+
skills: path.join(AUTOHAND_HOME, "skills")
|
|
24
26
|
};
|
|
25
27
|
var AUTOHAND_FILES = {
|
|
26
28
|
/** Main config file */
|
|
@@ -51,6 +53,13 @@ var AUTH_CONFIG = {
|
|
|
51
53
|
authTimeout: 5 * 60 * 1e3,
|
|
52
54
|
sessionExpiryDays: 30
|
|
53
55
|
};
|
|
56
|
+
var SKILL_LOCATIONS = [
|
|
57
|
+
{ basePath: path.join(os.homedir(), ".codex", "skills"), source: "codex-user", recursive: true },
|
|
58
|
+
{ basePath: path.join(os.homedir(), ".claude", "skills"), source: "claude-user", recursive: false },
|
|
59
|
+
// Project-level Claude skills are resolved at runtime with workspaceRoot
|
|
60
|
+
{ basePath: AUTOHAND_PATHS.skills, source: "autohand-user", recursive: true }
|
|
61
|
+
// Project-level Autohand skills are resolved at runtime with workspaceRoot
|
|
62
|
+
];
|
|
54
63
|
|
|
55
64
|
export {
|
|
56
65
|
AUTOHAND_HOME,
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// src/commands/skills.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
async function skills(ctx, args = []) {
|
|
4
|
+
const { skillsRegistry } = ctx;
|
|
5
|
+
if (!skillsRegistry) {
|
|
6
|
+
console.log(chalk.red("Skills registry not available."));
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const subcommand = args[0]?.toLowerCase();
|
|
10
|
+
const skillName = args.slice(1).join(" ").trim() || args[1];
|
|
11
|
+
switch (subcommand) {
|
|
12
|
+
case "use":
|
|
13
|
+
case "activate":
|
|
14
|
+
return activateSkill(skillsRegistry, skillName);
|
|
15
|
+
case "deactivate":
|
|
16
|
+
case "off":
|
|
17
|
+
return deactivateSkill(skillsRegistry, skillName);
|
|
18
|
+
case "info":
|
|
19
|
+
case "show":
|
|
20
|
+
return showSkillInfo(skillsRegistry, skillName);
|
|
21
|
+
default:
|
|
22
|
+
return listSkills(skillsRegistry);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function listSkills(registry) {
|
|
26
|
+
const allSkills = registry.listSkills();
|
|
27
|
+
const activeSkills = registry.getActiveSkills();
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(chalk.bold.cyan("Available Skills"));
|
|
30
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
31
|
+
if (allSkills.length === 0) {
|
|
32
|
+
console.log(chalk.gray("No skills found."));
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(chalk.gray("Tip: Skills can be added in:"));
|
|
35
|
+
console.log(chalk.gray(" - ~/.autohand/skills/<skill-name>/SKILL.md"));
|
|
36
|
+
console.log(chalk.gray(" - <project>/.autohand/skills/<skill-name>/SKILL.md"));
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const skill of allSkills) {
|
|
41
|
+
const existing = bySource.get(skill.source) ?? [];
|
|
42
|
+
existing.push(skill);
|
|
43
|
+
bySource.set(skill.source, existing);
|
|
44
|
+
}
|
|
45
|
+
const sourceLabels = {
|
|
46
|
+
"codex-user": "Codex User Skills (~/.codex/skills/)",
|
|
47
|
+
"claude-user": "Claude User Skills (~/.claude/skills/)",
|
|
48
|
+
"claude-project": "Claude Project Skills (.claude/skills/)",
|
|
49
|
+
"autohand-user": "Autohand User Skills (~/.autohand/skills/)",
|
|
50
|
+
"autohand-project": "Autohand Project Skills (.autohand/skills/)"
|
|
51
|
+
};
|
|
52
|
+
for (const [source, skills2] of bySource) {
|
|
53
|
+
console.log();
|
|
54
|
+
console.log(chalk.bold.yellow(sourceLabels[source] || source));
|
|
55
|
+
console.log();
|
|
56
|
+
for (const skill of skills2) {
|
|
57
|
+
const isActive = skill.isActive;
|
|
58
|
+
const statusIcon = isActive ? chalk.green("\u25CF") : chalk.gray("\u25CB");
|
|
59
|
+
const nameColor = isActive ? chalk.green : chalk.white;
|
|
60
|
+
console.log(` ${statusIcon} ${nameColor(skill.name)}`);
|
|
61
|
+
console.log(chalk.gray(` ${skill.description}`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
console.log();
|
|
65
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
66
|
+
console.log(chalk.gray(`Total: ${allSkills.length} skills, ${activeSkills.length} active`));
|
|
67
|
+
console.log();
|
|
68
|
+
console.log(chalk.gray("Commands:"));
|
|
69
|
+
console.log(chalk.gray(" /skills use <name> Activate a skill"));
|
|
70
|
+
console.log(chalk.gray(" /skills deactivate <name> Deactivate a skill"));
|
|
71
|
+
console.log(chalk.gray(" /skills info <name> Show skill details"));
|
|
72
|
+
console.log(chalk.gray(" /skills new Create a new skill"));
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function activateSkill(registry, name) {
|
|
76
|
+
if (!name) {
|
|
77
|
+
console.log(chalk.red("Usage: /skills use <skill-name>"));
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const skill = registry.getSkill(name);
|
|
81
|
+
if (!skill) {
|
|
82
|
+
console.log(chalk.red(`Skill not found: ${name}`));
|
|
83
|
+
const similar = registry.findSimilar(name, 0.2);
|
|
84
|
+
if (similar.length > 0) {
|
|
85
|
+
console.log(chalk.gray("Did you mean:"));
|
|
86
|
+
for (const match of similar.slice(0, 3)) {
|
|
87
|
+
console.log(chalk.gray(` - ${match.skill.name}`));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (skill.isActive) {
|
|
93
|
+
console.log(chalk.yellow(`Skill "${name}" is already active.`));
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const success = registry.activateSkill(name);
|
|
97
|
+
if (success) {
|
|
98
|
+
console.log(chalk.green(`\u2713 Activated skill: ${name}`));
|
|
99
|
+
console.log(chalk.gray(` ${skill.description}`));
|
|
100
|
+
return `Skill "${name}" is now active.`;
|
|
101
|
+
} else {
|
|
102
|
+
console.log(chalk.red(`Failed to activate skill: ${name}`));
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function deactivateSkill(registry, name) {
|
|
107
|
+
if (!name) {
|
|
108
|
+
console.log(chalk.red("Usage: /skills deactivate <skill-name>"));
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const skill = registry.getSkill(name);
|
|
112
|
+
if (!skill) {
|
|
113
|
+
console.log(chalk.red(`Skill not found: ${name}`));
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
if (!skill.isActive) {
|
|
117
|
+
console.log(chalk.yellow(`Skill "${name}" is not active.`));
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const success = registry.deactivateSkill(name);
|
|
121
|
+
if (success) {
|
|
122
|
+
console.log(chalk.green(`\u2713 Deactivated skill: ${name}`));
|
|
123
|
+
return `Skill "${name}" is now inactive.`;
|
|
124
|
+
} else {
|
|
125
|
+
console.log(chalk.red(`Failed to deactivate skill: ${name}`));
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function showSkillInfo(registry, name) {
|
|
130
|
+
if (!name) {
|
|
131
|
+
console.log(chalk.red("Usage: /skills info <skill-name>"));
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const skill = registry.getSkill(name);
|
|
135
|
+
if (!skill) {
|
|
136
|
+
console.log(chalk.red(`Skill not found: ${name}`));
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
console.log();
|
|
140
|
+
console.log(chalk.bold.cyan(`Skill: ${skill.name}`));
|
|
141
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(chalk.white("Description: ") + skill.description);
|
|
144
|
+
console.log(chalk.white("Status: ") + (skill.isActive ? chalk.green("Active") : chalk.gray("Inactive")));
|
|
145
|
+
console.log(chalk.white("Source: ") + skill.source);
|
|
146
|
+
console.log(chalk.white("Path: ") + chalk.gray(skill.path));
|
|
147
|
+
if (skill.license) {
|
|
148
|
+
console.log(chalk.white("License: ") + skill.license);
|
|
149
|
+
}
|
|
150
|
+
if (skill.compatibility) {
|
|
151
|
+
console.log(chalk.white("Compatibility: ") + skill.compatibility);
|
|
152
|
+
}
|
|
153
|
+
if (skill["allowed-tools"]) {
|
|
154
|
+
console.log(chalk.white("Allowed Tools: ") + skill["allowed-tools"]);
|
|
155
|
+
}
|
|
156
|
+
if (skill.metadata && Object.keys(skill.metadata).length > 0) {
|
|
157
|
+
console.log(chalk.white("Metadata:"));
|
|
158
|
+
for (const [key, value] of Object.entries(skill.metadata)) {
|
|
159
|
+
console.log(chalk.gray(` ${key}: ${value}`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
console.log();
|
|
163
|
+
console.log(chalk.bold("Content:"));
|
|
164
|
+
console.log(chalk.gray("\u2500".repeat(50)));
|
|
165
|
+
const bodyPreview = skill.body.length > 500 ? skill.body.slice(0, 500) + chalk.gray("\n... (truncated)") : skill.body;
|
|
166
|
+
console.log(bodyPreview || chalk.gray("(no body content)"));
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
var metadata = {
|
|
170
|
+
command: "/skills",
|
|
171
|
+
description: "list and manage available skills",
|
|
172
|
+
implemented: true
|
|
173
|
+
};
|
|
174
|
+
var useMetadata = {
|
|
175
|
+
command: "/skills use",
|
|
176
|
+
description: "activate a skill",
|
|
177
|
+
implemented: true
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export {
|
|
181
|
+
skills,
|
|
182
|
+
metadata,
|
|
183
|
+
useMetadata
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* @license
|
|
187
|
+
* Copyright 2025 Autohand AI LLC
|
|
188
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
189
|
+
*
|
|
190
|
+
* Skills command - List and manage available skills
|
|
191
|
+
*/
|