autohand-cli 0.6.4 → 0.6.7
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/dist/SkillsRegistry-LXDK73BL.cjs +9 -0
- package/dist/SkillsRegistry-SP5MX7OA.js +9 -0
- package/dist/agents-FH47ZMOI.cjs +10 -0
- package/dist/{agents-OJWYZN6X.js → agents-NB5VQN6H.js} +2 -2
- package/dist/{agents-new-WQLJOXSS.js → agents-new-M325HGWT.js} +3 -2
- package/dist/agents-new-XLEU26YI.cjs +11 -0
- package/dist/{chunk-DD2YPHP5.cjs → chunk-2OBNJCG6.cjs} +15 -13
- package/dist/{chunk-NWXYG5PQ.js → chunk-37NUB5KX.js} +1 -1
- package/dist/{chunk-AL4Z4WKG.cjs → chunk-3DPDLZYY.cjs} +9 -7
- package/dist/chunk-3HPUOQJN.cjs +23 -0
- package/dist/{chunk-G7SYGATA.cjs → chunk-3ZUWWML7.cjs} +2 -2
- package/dist/{chunk-3Y6G5DUX.cjs → chunk-53YDUYNS.cjs} +10 -4
- package/dist/{chunk-PRZTK2FX.js → chunk-5WKR4HIB.js} +7 -5
- package/dist/{chunk-ZTA2ASFW.cjs → chunk-6ZGNSZRG.cjs} +1 -1
- package/dist/chunk-7BYSXAKS.js +23 -0
- package/dist/{chunk-6FEZ6JAQ.js → chunk-7HB7GSQF.js} +1 -1
- package/dist/{chunk-UBGEAEKS.js → chunk-AY2XV7TH.js} +1 -1
- package/dist/{chunk-7RRX7H2X.cjs → chunk-B5N5UAMO.cjs} +20 -12
- package/dist/{chunk-KZ2UXXLH.js → chunk-BAHUKJJR.js} +7 -5
- package/dist/{chunk-KT55HW6V.js → chunk-CHQMK2ZG.js} +1 -1
- package/dist/chunk-CVYEUA3D.cjs +528 -0
- package/dist/{chunk-MRQV5HMC.js → chunk-DE7YC5MB.js} +8 -6
- package/dist/{chunk-AD4O67ZA.cjs → chunk-EJ77L3KT.cjs} +10 -10
- package/dist/{chunk-737A24RB.js → chunk-FUEL6BK7.js} +9 -1
- package/dist/{chunk-6MCXWSR3.js → chunk-H4RPZD6H.js} +1 -1
- package/dist/chunk-JHGIWNHL.cjs +46 -0
- package/dist/{chunk-27JNK5TE.cjs → chunk-LUKMRIKJ.cjs} +40 -61
- package/dist/{chunk-4H3B46YX.js → chunk-MWLAHCU7.js} +9 -3
- package/dist/{chunk-QCMC2WOC.cjs → chunk-N6ZOJI2M.cjs} +4 -4
- package/dist/{chunk-2TPGTNNY.js → chunk-NGSLABLS.js} +37 -58
- package/dist/chunk-QHPFA6OE.js +46 -0
- package/dist/{chunk-M4LKQQHU.cjs → chunk-REPKBECD.cjs} +2 -2
- package/dist/chunk-SKU4M27Z.js +528 -0
- package/dist/chunk-W4XTDUGT.js +267 -0
- package/dist/{chunk-RDEROLKA.cjs → chunk-XAM7SFVB.cjs} +8 -6
- package/dist/chunk-XF4EQ3IV.cjs +267 -0
- package/dist/{chunk-IHJDYAYJ.cjs → chunk-XTHHDIBG.cjs} +9 -1
- package/dist/{chunk-5N3QP5LJ.js → chunk-YDH2BMEN.js} +19 -11
- package/dist/constants-ICQLSGZN.cjs +19 -0
- package/dist/constants-N3I2FHCM.js +19 -0
- package/dist/feedback-TOGESBX7.cjs +11 -0
- package/dist/{feedback-3THCLEBE.js → feedback-YGSYBQEW.js} +3 -2
- package/dist/index.cjs +1463 -1146
- package/dist/index.js +987 -670
- package/dist/login-QVBS7KBK.cjs +13 -0
- package/dist/login-XUVEFKCR.js +13 -0
- package/dist/logout-75YLPOBK.js +13 -0
- package/dist/logout-FYYR5KCP.cjs +13 -0
- package/dist/{permissions-CYW62ZK3.js → permissions-3GS4ZWVA.js} +2 -1
- package/dist/permissions-E3MTIE7D.cjs +10 -0
- package/dist/{skills-HF4SAF5O.js → skills-3M26KASS.js} +3 -1
- package/dist/skills-BOFY5RQN.cjs +13 -0
- package/dist/skills-install-WJ2LDRQG.js +680 -0
- package/dist/skills-install-Z3W5PQWQ.cjs +680 -0
- package/dist/skills-new-KIBUN63X.js +12 -0
- package/dist/skills-new-XDYS24XW.cjs +12 -0
- package/dist/{status-SLYYTKXD.js → status-6FY6RKIS.js} +1 -1
- package/dist/status-DJHDT6QH.cjs +9 -0
- package/dist/theme-2UK74UWR.cjs +13 -0
- package/dist/{theme-THMQ5AIN.js → theme-XNZ2X6HE.js} +3 -3
- package/package.json +1 -1
- package/dist/agents-EHLYBJLK.cjs +0 -10
- package/dist/agents-new-7VPASCBV.cjs +0 -10
- package/dist/chunk-NEAJ2UWG.js +0 -191
- package/dist/chunk-Z6SGIQWH.cjs +0 -191
- package/dist/feedback-PATTKRH5.cjs +0 -10
- package/dist/login-QJROML5I.js +0 -12
- package/dist/login-X66DSV75.cjs +0 -12
- package/dist/logout-3Z7R3F7J.cjs +0 -12
- package/dist/logout-RJ5OAXRI.js +0 -12
- package/dist/permissions-NOC5DMOH.cjs +0 -9
- package/dist/skills-U6J6DFLK.cjs +0 -11
- package/dist/skills-new-QDTNEG3R.js +0 -10
- package/dist/skills-new-UPVBHIF2.cjs +0 -10
- package/dist/status-GR73LEEN.cjs +0 -9
- package/dist/theme-YDANJLZR.cjs +0 -13
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
2
|
+
|
|
3
|
+
var _chunkJHGIWNHLcjs = require('./chunk-JHGIWNHL.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
var _chunkXTHHDIBGcjs = require('./chunk-XTHHDIBG.cjs');
|
|
7
|
+
|
|
8
|
+
// src/skills/SkillsRegistry.ts
|
|
9
|
+
var _fsextra = require('fs-extra'); var _fsextra2 = _interopRequireDefault(_fsextra);
|
|
10
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
11
|
+
|
|
12
|
+
// src/skills/SkillParser.ts
|
|
13
|
+
|
|
14
|
+
var _yaml = require('yaml'); var _yaml2 = _interopRequireDefault(_yaml);
|
|
15
|
+
var SkillParser = class {
|
|
16
|
+
/**
|
|
17
|
+
* Parse a SKILL.md file from disk
|
|
18
|
+
*/
|
|
19
|
+
async parseFile(filePath, source) {
|
|
20
|
+
try {
|
|
21
|
+
const content = await _fsextra2.default.readFile(filePath, "utf-8");
|
|
22
|
+
return this.parseContent(content, filePath, source);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse SKILL.md content directly from a string
|
|
32
|
+
*/
|
|
33
|
+
parseContent(content, filePath, source) {
|
|
34
|
+
const extraction = this.extractFrontmatter(content);
|
|
35
|
+
if (!extraction) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: "No valid YAML frontmatter found. SKILL.md must start with --- delimited frontmatter."
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const parsed = _yaml2.default.parse(extraction.frontmatter);
|
|
43
|
+
const validation = _chunkJHGIWNHLcjs.validateSkillFrontmatter.call(void 0, parsed);
|
|
44
|
+
if (!validation.valid) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: `Invalid skill frontmatter: ${validation.errors.join("; ")}`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const skill = {
|
|
51
|
+
name: parsed.name,
|
|
52
|
+
description: parsed.description,
|
|
53
|
+
license: parsed.license,
|
|
54
|
+
compatibility: parsed.compatibility,
|
|
55
|
+
metadata: parsed.metadata,
|
|
56
|
+
"allowed-tools": parsed["allowed-tools"],
|
|
57
|
+
body: extraction.body,
|
|
58
|
+
path: filePath,
|
|
59
|
+
source,
|
|
60
|
+
isActive: false
|
|
61
|
+
};
|
|
62
|
+
return { success: true, skill };
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: `Failed to parse YAML frontmatter: ${error instanceof Error ? error.message : String(error)}`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Extract YAML frontmatter and body from content
|
|
72
|
+
* Frontmatter must be delimited by --- at start and end
|
|
73
|
+
*/
|
|
74
|
+
extractFrontmatter(content) {
|
|
75
|
+
if (!content.startsWith("---")) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const lines = content.split("\n");
|
|
79
|
+
let closingIndex = -1;
|
|
80
|
+
for (let i = 1; i < lines.length; i++) {
|
|
81
|
+
if (lines[i].trim() === "---") {
|
|
82
|
+
closingIndex = i;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (closingIndex === -1) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const frontmatter = lines.slice(1, closingIndex).join("\n");
|
|
90
|
+
const body = lines.slice(closingIndex + 1).join("\n").trim();
|
|
91
|
+
return { frontmatter, body };
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/skills/SkillsRegistry.ts
|
|
96
|
+
var SIMILARITY_THRESHOLD = 0.3;
|
|
97
|
+
var VENDOR_SOURCES = ["codex-user", "claude-user", "codex-project", "claude-project"];
|
|
98
|
+
var SkillsRegistry = class {
|
|
99
|
+
constructor(userSkillsDir, defaultSource = "autohand-user") {
|
|
100
|
+
this.userSkillsDir = userSkillsDir;
|
|
101
|
+
this.skills = /* @__PURE__ */ new Map();
|
|
102
|
+
this.parser = new SkillParser();
|
|
103
|
+
this.workspaceRoot = null;
|
|
104
|
+
this.telemetryManager = null;
|
|
105
|
+
this.communityClient = null;
|
|
106
|
+
this.defaultSource = defaultSource;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Set the telemetry manager for tracking skill events
|
|
110
|
+
*/
|
|
111
|
+
setTelemetryManager(telemetryManager) {
|
|
112
|
+
this.telemetryManager = telemetryManager;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Set the community skills client for backup/sync operations
|
|
116
|
+
*/
|
|
117
|
+
setCommunityClient(client) {
|
|
118
|
+
this.communityClient = client;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get the community skills client
|
|
122
|
+
*/
|
|
123
|
+
getCommunityClient() {
|
|
124
|
+
return this.communityClient;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if any vendor skills (codex/claude) are loaded
|
|
128
|
+
*/
|
|
129
|
+
hasVendorSkills() {
|
|
130
|
+
for (const skill of this.skills.values()) {
|
|
131
|
+
if (VENDOR_SOURCES.includes(skill.source)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get all vendor skills (from codex/claude sources)
|
|
139
|
+
*/
|
|
140
|
+
getVendorSkills() {
|
|
141
|
+
return Array.from(this.skills.values()).filter(
|
|
142
|
+
(skill) => VENDOR_SOURCES.includes(skill.source)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Import a community skill package and save to disk
|
|
147
|
+
*/
|
|
148
|
+
async importCommunitySkill(pkg, targetDir) {
|
|
149
|
+
if (!pkg.name || !pkg.body) {
|
|
150
|
+
return { success: false, error: "Invalid skill package: missing name or body" };
|
|
151
|
+
}
|
|
152
|
+
const skillDir = _path2.default.join(targetDir, pkg.name);
|
|
153
|
+
const skillPath = _path2.default.join(skillDir, "SKILL.md");
|
|
154
|
+
if (await _fsextra2.default.pathExists(skillPath)) {
|
|
155
|
+
return { success: false, skipped: true, error: "Skill already exists" };
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
await _fsextra2.default.ensureDir(skillDir);
|
|
159
|
+
await _fsextra2.default.writeFile(skillPath, pkg.body, "utf-8");
|
|
160
|
+
const result = await this.parser.parseFile(skillPath, "community");
|
|
161
|
+
if (result.success && result.skill) {
|
|
162
|
+
this.skills.set(result.skill.name, result.skill);
|
|
163
|
+
return { success: true, path: skillPath };
|
|
164
|
+
}
|
|
165
|
+
return { success: false, error: "Failed to parse imported skill" };
|
|
166
|
+
} catch (err) {
|
|
167
|
+
const error = err instanceof Error ? err.message : "Unknown error";
|
|
168
|
+
return { success: false, error };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Import a community skill directory with multiple files
|
|
173
|
+
* Used for skills from GitHub that include templates, examples, etc.
|
|
174
|
+
*
|
|
175
|
+
* @param skillName - Name of the skill (used as directory name)
|
|
176
|
+
* @param files - Map of relative file paths to their contents
|
|
177
|
+
* @param targetDir - Target directory (user or project skills dir)
|
|
178
|
+
* @param force - Overwrite if skill already exists
|
|
179
|
+
*/
|
|
180
|
+
async importCommunitySkillDirectory(skillName, files, targetDir, force = false) {
|
|
181
|
+
if (!files.has("SKILL.md")) {
|
|
182
|
+
return { success: false, error: "Missing required SKILL.md file" };
|
|
183
|
+
}
|
|
184
|
+
const skillDir = _path2.default.join(targetDir, skillName);
|
|
185
|
+
const skillPath = _path2.default.join(skillDir, "SKILL.md");
|
|
186
|
+
if (!force && await _fsextra2.default.pathExists(skillPath)) {
|
|
187
|
+
return { success: false, skipped: true, error: "Skill already exists" };
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
if (force && await _fsextra2.default.pathExists(skillDir)) {
|
|
191
|
+
await _fsextra2.default.remove(skillDir);
|
|
192
|
+
}
|
|
193
|
+
for (const [relativePath, content] of files) {
|
|
194
|
+
const fullPath = _path2.default.join(skillDir, relativePath);
|
|
195
|
+
await _fsextra2.default.ensureDir(_path2.default.dirname(fullPath));
|
|
196
|
+
await _fsextra2.default.writeFile(fullPath, content, "utf-8");
|
|
197
|
+
}
|
|
198
|
+
const result = await this.parser.parseFile(skillPath, "community");
|
|
199
|
+
if (result.success && result.skill) {
|
|
200
|
+
this.skills.set(result.skill.name, result.skill);
|
|
201
|
+
return { success: true, path: skillDir };
|
|
202
|
+
}
|
|
203
|
+
return { success: false, error: "Failed to parse imported skill" };
|
|
204
|
+
} catch (err) {
|
|
205
|
+
const error = err instanceof Error ? err.message : "Unknown error";
|
|
206
|
+
return { success: false, error };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Check if a skill is already installed
|
|
211
|
+
*/
|
|
212
|
+
isSkillInstalled(skillName, targetDir) {
|
|
213
|
+
const skillPath = _path2.default.join(targetDir, skillName, "SKILL.md");
|
|
214
|
+
return _fsextra2.default.pathExists(skillPath);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get the user skills directory path
|
|
218
|
+
*/
|
|
219
|
+
getUserSkillsDir() {
|
|
220
|
+
return this.userSkillsDir;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Initialize the registry by loading skills from the user directory
|
|
224
|
+
*/
|
|
225
|
+
async initialize() {
|
|
226
|
+
await this.loadFromDirectory(this.userSkillsDir, this.defaultSource, true);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Set the workspace root and load project-level skills
|
|
230
|
+
*/
|
|
231
|
+
async setWorkspace(workspaceRoot) {
|
|
232
|
+
this.workspaceRoot = workspaceRoot;
|
|
233
|
+
const claudeProjectSkillsDir = _path2.default.join(workspaceRoot, ".claude", "skills");
|
|
234
|
+
await this.loadFromDirectory(claudeProjectSkillsDir, "claude-project", false);
|
|
235
|
+
const autohandProjectSkillsDir = _path2.default.join(workspaceRoot, _chunkXTHHDIBGcjs.PROJECT_DIR_NAME, "skills");
|
|
236
|
+
await this.loadFromDirectory(autohandProjectSkillsDir, "autohand-project", true);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Add an additional skill location to search
|
|
240
|
+
*/
|
|
241
|
+
async addLocation(directory, source, recursive = true) {
|
|
242
|
+
await this.loadFromDirectory(directory, source, recursive);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Add a skill location with auto-copy to autohand location
|
|
246
|
+
* Copies discovered skills to the target autohand directory, preserving structure
|
|
247
|
+
*/
|
|
248
|
+
async addLocationWithAutoCopy(sourceDirectory, source, targetAutohandDir, recursive = true) {
|
|
249
|
+
const result = {
|
|
250
|
+
copiedCount: 0,
|
|
251
|
+
skippedCount: 0,
|
|
252
|
+
errorCount: 0,
|
|
253
|
+
copiedSkills: [],
|
|
254
|
+
skippedSkills: []
|
|
255
|
+
};
|
|
256
|
+
if (!await _fsextra2.default.pathExists(sourceDirectory)) {
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
const skillFiles = await this.findSkillFiles(sourceDirectory, recursive);
|
|
260
|
+
for (const skillPath of skillFiles) {
|
|
261
|
+
const parseResult = await this.parser.parseFile(skillPath, source);
|
|
262
|
+
if (!parseResult.success || !parseResult.skill) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const skill = parseResult.skill;
|
|
266
|
+
const skillName = skill.name;
|
|
267
|
+
const skillDir = _path2.default.dirname(skillPath);
|
|
268
|
+
const relativePath = _path2.default.relative(sourceDirectory, skillDir);
|
|
269
|
+
const targetSkillDir = _path2.default.join(targetAutohandDir, relativePath);
|
|
270
|
+
const targetSkillPath = _path2.default.join(targetSkillDir, "SKILL.md");
|
|
271
|
+
if (await _fsextra2.default.pathExists(targetSkillPath)) {
|
|
272
|
+
result.skippedCount++;
|
|
273
|
+
result.skippedSkills.push(skillName);
|
|
274
|
+
} else {
|
|
275
|
+
try {
|
|
276
|
+
await _fsextra2.default.ensureDir(targetSkillDir);
|
|
277
|
+
await _fsextra2.default.copyFile(skillPath, targetSkillPath);
|
|
278
|
+
result.copiedCount++;
|
|
279
|
+
result.copiedSkills.push(skillName);
|
|
280
|
+
} catch (e) {
|
|
281
|
+
result.errorCount++;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
this.skills.set(skill.name, skill);
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Set workspace with auto-copy from claude-project to autohand-project
|
|
290
|
+
*/
|
|
291
|
+
async setWorkspaceWithAutoCopy(workspaceRoot) {
|
|
292
|
+
this.workspaceRoot = workspaceRoot;
|
|
293
|
+
const claudeProjectSkillsDir = _path2.default.join(workspaceRoot, ".claude", "skills");
|
|
294
|
+
const autohandProjectSkillsDir = _path2.default.join(workspaceRoot, _chunkXTHHDIBGcjs.PROJECT_DIR_NAME, "skills");
|
|
295
|
+
await this.addLocationWithAutoCopy(
|
|
296
|
+
claudeProjectSkillsDir,
|
|
297
|
+
"claude-project",
|
|
298
|
+
autohandProjectSkillsDir,
|
|
299
|
+
false
|
|
300
|
+
// Claude project is not recursive
|
|
301
|
+
);
|
|
302
|
+
await this.loadFromDirectory(autohandProjectSkillsDir, "autohand-project", true);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Add a skill location with auto-copy AND backup to community API
|
|
306
|
+
* Enhanced version that also backs up vendor skills to the API
|
|
307
|
+
*/
|
|
308
|
+
async addLocationWithAutoCopyAndBackup(sourceDirectory, source, targetAutohandDir, recursive = true) {
|
|
309
|
+
const result = await this.addLocationWithAutoCopy(
|
|
310
|
+
sourceDirectory,
|
|
311
|
+
source,
|
|
312
|
+
targetAutohandDir,
|
|
313
|
+
recursive
|
|
314
|
+
);
|
|
315
|
+
if (this.communityClient && result.copiedCount > 0) {
|
|
316
|
+
const backupPayloads = [];
|
|
317
|
+
for (const skillName of result.copiedSkills) {
|
|
318
|
+
const skill = this.skills.get(skillName);
|
|
319
|
+
if (skill) {
|
|
320
|
+
backupPayloads.push({
|
|
321
|
+
name: skill.name,
|
|
322
|
+
description: skill.description,
|
|
323
|
+
body: skill.body,
|
|
324
|
+
allowedTools: skill["allowed-tools"],
|
|
325
|
+
originalSource: source,
|
|
326
|
+
originalPath: skill.path
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (backupPayloads.length > 0) {
|
|
331
|
+
await this.communityClient.backupAllSkills(backupPayloads);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Backup all vendor skills to community API
|
|
338
|
+
*/
|
|
339
|
+
async backupAllVendorSkills() {
|
|
340
|
+
if (!this.communityClient) {
|
|
341
|
+
return { backed: 0, failed: 0 };
|
|
342
|
+
}
|
|
343
|
+
const vendorSkills = this.getVendorSkills();
|
|
344
|
+
if (vendorSkills.length === 0) {
|
|
345
|
+
return { backed: 0, failed: 0 };
|
|
346
|
+
}
|
|
347
|
+
const payloads = vendorSkills.map((skill) => ({
|
|
348
|
+
name: skill.name,
|
|
349
|
+
description: skill.description,
|
|
350
|
+
body: skill.body,
|
|
351
|
+
allowedTools: skill["allowed-tools"],
|
|
352
|
+
originalSource: skill.source,
|
|
353
|
+
originalPath: skill.path
|
|
354
|
+
}));
|
|
355
|
+
return this.communityClient.backupAllSkills(payloads);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Load skills from a directory
|
|
359
|
+
*/
|
|
360
|
+
async loadFromDirectory(directory, source, recursive) {
|
|
361
|
+
if (!await _fsextra2.default.pathExists(directory)) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const skillFiles = await this.findSkillFiles(directory, recursive);
|
|
365
|
+
for (const skillPath of skillFiles) {
|
|
366
|
+
const result = await this.parser.parseFile(skillPath, source);
|
|
367
|
+
if (result.success && result.skill) {
|
|
368
|
+
this.skills.set(result.skill.name, result.skill);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Find all SKILL.md files in a directory
|
|
374
|
+
*/
|
|
375
|
+
async findSkillFiles(directory, recursive) {
|
|
376
|
+
const results = [];
|
|
377
|
+
if (!await _fsextra2.default.pathExists(directory)) {
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
const entries = await _fsextra2.default.readdir(directory, { withFileTypes: true });
|
|
381
|
+
for (const entry of entries) {
|
|
382
|
+
const fullPath = _path2.default.join(directory, entry.name);
|
|
383
|
+
if (entry.isDirectory()) {
|
|
384
|
+
const skillPath = _path2.default.join(fullPath, "SKILL.md");
|
|
385
|
+
if (await _fsextra2.default.pathExists(skillPath)) {
|
|
386
|
+
results.push(skillPath);
|
|
387
|
+
}
|
|
388
|
+
if (recursive) {
|
|
389
|
+
const nested = await this.findSkillFiles(fullPath, true);
|
|
390
|
+
results.push(...nested);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return results;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* List all available skills
|
|
398
|
+
*/
|
|
399
|
+
listSkills() {
|
|
400
|
+
return Array.from(this.skills.values());
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get a specific skill by name
|
|
404
|
+
*/
|
|
405
|
+
getSkill(name) {
|
|
406
|
+
return _nullishCoalesce(this.skills.get(name), () => ( null));
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Activate a skill by name
|
|
410
|
+
*/
|
|
411
|
+
activateSkill(name) {
|
|
412
|
+
const skill = this.skills.get(name);
|
|
413
|
+
if (!skill) {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
skill.isActive = true;
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Deactivate a skill by name
|
|
421
|
+
*/
|
|
422
|
+
deactivateSkill(name) {
|
|
423
|
+
const skill = this.skills.get(name);
|
|
424
|
+
if (!skill) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
skill.isActive = false;
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Deactivate all active skills
|
|
432
|
+
*/
|
|
433
|
+
deactivateAll() {
|
|
434
|
+
for (const skill of this.skills.values()) {
|
|
435
|
+
skill.isActive = false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Get all currently active skills
|
|
440
|
+
*/
|
|
441
|
+
getActiveSkills() {
|
|
442
|
+
return Array.from(this.skills.values()).filter((s) => s.isActive);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Find skills similar to a given query using Jaccard similarity
|
|
446
|
+
*/
|
|
447
|
+
findSimilar(query, threshold = SIMILARITY_THRESHOLD) {
|
|
448
|
+
const queryTokens = this.tokenize(query);
|
|
449
|
+
const matches = [];
|
|
450
|
+
for (const skill of this.skills.values()) {
|
|
451
|
+
const skillText = `${skill.name} ${skill.description}`;
|
|
452
|
+
const skillTokens = this.tokenize(skillText);
|
|
453
|
+
const score = this.calculateJaccard(queryTokens, skillTokens);
|
|
454
|
+
if (score >= threshold) {
|
|
455
|
+
matches.push({ skill, score });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return matches.sort((a, b) => b.score - a.score);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Calculate Jaccard similarity between two token sets
|
|
462
|
+
*/
|
|
463
|
+
calculateJaccard(a, b) {
|
|
464
|
+
if (a.size === 0 || b.size === 0) {
|
|
465
|
+
return 0;
|
|
466
|
+
}
|
|
467
|
+
const intersection = new Set([...a].filter((x) => b.has(x)));
|
|
468
|
+
const union = /* @__PURE__ */ new Set([...a, ...b]);
|
|
469
|
+
return intersection.size / union.size;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Tokenize text into a set of lowercase words
|
|
473
|
+
*/
|
|
474
|
+
tokenize(text) {
|
|
475
|
+
return new Set(
|
|
476
|
+
text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 2)
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get the number of loaded skills
|
|
481
|
+
*/
|
|
482
|
+
get size() {
|
|
483
|
+
return this.skills.size;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Check if a skill exists by name
|
|
487
|
+
*/
|
|
488
|
+
hasSkill(name) {
|
|
489
|
+
return this.skills.has(name);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Save a new skill to the user skills directory
|
|
493
|
+
*/
|
|
494
|
+
async saveSkill(name, content) {
|
|
495
|
+
const skillDir = _path2.default.join(this.userSkillsDir, name);
|
|
496
|
+
const skillPath = _path2.default.join(skillDir, "SKILL.md");
|
|
497
|
+
try {
|
|
498
|
+
await _fsextra2.default.ensureDir(skillDir);
|
|
499
|
+
await _fsextra2.default.writeFile(skillPath, content, "utf-8");
|
|
500
|
+
const result = await this.parser.parseFile(skillPath, "autohand-user");
|
|
501
|
+
if (result.success && result.skill) {
|
|
502
|
+
this.skills.set(result.skill.name, result.skill);
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
return false;
|
|
506
|
+
} catch (e2) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
exports.SkillsRegistry = SkillsRegistry;
|
|
515
|
+
/**
|
|
516
|
+
* @license
|
|
517
|
+
* Copyright 2025 Autohand AI LLC
|
|
518
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
519
|
+
*
|
|
520
|
+
* SkillParser - Parses SKILL.md files with YAML frontmatter
|
|
521
|
+
*/
|
|
522
|
+
/**
|
|
523
|
+
* @license
|
|
524
|
+
* Copyright 2025 Autohand AI LLC
|
|
525
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
526
|
+
*
|
|
527
|
+
* SkillsRegistry - Manages skill discovery, loading, and activation
|
|
528
|
+
*/
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAuthClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CHQMK2ZG.js";
|
|
4
4
|
import {
|
|
5
5
|
saveConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AY2XV7TH.js";
|
|
7
|
+
import {
|
|
8
|
+
safePrompt
|
|
9
|
+
} from "./chunk-7BYSXAKS.js";
|
|
7
10
|
import {
|
|
8
11
|
AUTH_CONFIG
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FUEL6BK7.js";
|
|
10
13
|
|
|
11
14
|
// src/commands/login.ts
|
|
12
15
|
import chalk from "chalk";
|
|
13
|
-
import enquirer from "enquirer";
|
|
14
16
|
var metadata = {
|
|
15
17
|
command: "/login",
|
|
16
18
|
description: "sign in to your Autohand account",
|
|
@@ -47,13 +49,13 @@ function sleep(ms) {
|
|
|
47
49
|
async function login(ctx) {
|
|
48
50
|
const config = ctx.config;
|
|
49
51
|
if (config?.auth?.token && config?.auth?.user) {
|
|
50
|
-
const
|
|
52
|
+
const result = await safePrompt({
|
|
51
53
|
type: "confirm",
|
|
52
54
|
name: "continueLogin",
|
|
53
55
|
message: `Already logged in as ${chalk.cyan(config.auth.user.email)}. Log in with a different account?`,
|
|
54
56
|
initial: false
|
|
55
57
|
});
|
|
56
|
-
if (!continueLogin) {
|
|
58
|
+
if (!result || !result.continueLogin) {
|
|
57
59
|
console.log(chalk.gray("Login cancelled."));
|
|
58
60
|
return null;
|
|
59
61
|
}
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _chunkN6ZOJI2Mcjs = require('./chunk-N6ZOJI2M.cjs');
|
|
9
9
|
|
|
10
10
|
// src/commands/theme.ts
|
|
11
11
|
var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
|
|
12
12
|
var _enquirer = require('enquirer'); var _enquirer2 = _interopRequireDefault(_enquirer);
|
|
13
13
|
async function theme(ctx) {
|
|
14
14
|
const { Select } = _enquirer2.default;
|
|
15
|
-
const themes =
|
|
16
|
-
const currentTheme =
|
|
15
|
+
const themes = _chunkN6ZOJI2Mcjs.listAvailableThemes.call(void 0, );
|
|
16
|
+
const currentTheme = _chunkN6ZOJI2Mcjs.isThemeInitialized.call(void 0, ) ? _chunkN6ZOJI2Mcjs.getTheme.call(void 0, ).name : _optionalChain([ctx, 'access', _ => _.config, 'access', _2 => _2.ui, 'optionalAccess', _3 => _3.theme]) || "dark";
|
|
17
17
|
console.log(_chalk2.default.cyan("\n\u{1F3A8} Theme Selection\n"));
|
|
18
18
|
console.log(_chalk2.default.gray(`Current theme: ${_chalk2.default.white(currentTheme)}`));
|
|
19
|
-
console.log(_chalk2.default.gray(`Custom themes location: ${
|
|
19
|
+
console.log(_chalk2.default.gray(`Custom themes location: ${_chunkN6ZOJI2Mcjs.CUSTOM_THEMES_DIR}
|
|
20
20
|
`));
|
|
21
21
|
const choices = themes.map((name) => ({
|
|
22
22
|
name,
|
|
@@ -35,12 +35,12 @@ async function theme(ctx) {
|
|
|
35
35
|
console.log(_chalk2.default.gray("\nNo change made."));
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
_chunkN6ZOJI2Mcjs.initTheme.call(void 0, selected);
|
|
39
39
|
ctx.config.ui = { ...ctx.config.ui, theme: selected };
|
|
40
|
-
await
|
|
40
|
+
await _chunkN6ZOJI2Mcjs.saveConfig.call(void 0, ctx.config);
|
|
41
41
|
console.log(_chalk2.default.green(`
|
|
42
42
|
\u2713 Theme changed to '${selected}'`));
|
|
43
|
-
const newTheme =
|
|
43
|
+
const newTheme = _chunkN6ZOJI2Mcjs.getTheme.call(void 0, );
|
|
44
44
|
console.log("\nTheme preview:");
|
|
45
45
|
console.log(` ${newTheme.fg("accent", "\u25CF accent")} ${newTheme.fg("success", "\u25CF success")} ${newTheme.fg("error", "\u25CF error")} ${newTheme.fg("warning", "\u25CF warning")}`);
|
|
46
46
|
console.log(` ${newTheme.fg("muted", "\u25CF muted")} ${newTheme.fg("dim", "\u25CF dim")} ${newTheme.fg("text", "\u25CF text")}`);
|
|
@@ -52,15 +52,15 @@ async function theme(ctx) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
async function themeInfo() {
|
|
55
|
-
if (!
|
|
55
|
+
if (!_chunkN6ZOJI2Mcjs.isThemeInitialized.call(void 0, )) {
|
|
56
56
|
console.log(_chalk2.default.yellow("Theme not initialized."));
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
|
-
const currentTheme =
|
|
59
|
+
const currentTheme = _chunkN6ZOJI2Mcjs.getTheme.call(void 0, );
|
|
60
60
|
console.log(_chalk2.default.cyan("\n\u{1F3A8} Current Theme Info\n"));
|
|
61
61
|
console.log(_chalk2.default.gray(`Name: ${_chalk2.default.white(currentTheme.name)}`));
|
|
62
62
|
console.log(_chalk2.default.gray(`Color mode: ${_chalk2.default.white(currentTheme.getColorMode())}`));
|
|
63
|
-
console.log(_chalk2.default.gray(`Custom themes dir: ${
|
|
63
|
+
console.log(_chalk2.default.gray(`Custom themes dir: ${_chunkN6ZOJI2Mcjs.CUSTOM_THEMES_DIR}`));
|
|
64
64
|
console.log();
|
|
65
65
|
console.log("Color preview:");
|
|
66
66
|
console.log(` ${currentTheme.fg("accent", "\u25CF accent")} ${currentTheme.fg("success", "\u25CF success")} ${currentTheme.fg("error", "\u25CF error")} ${currentTheme.fg("warning", "\u25CF warning")}`);
|
|
@@ -60,13 +60,21 @@ var SKILL_LOCATIONS = [
|
|
|
60
60
|
{ basePath: AUTOHAND_PATHS.skills, source: "autohand-user", recursive: true }
|
|
61
61
|
// Project-level Autohand skills are resolved at runtime with workspaceRoot
|
|
62
62
|
];
|
|
63
|
+
function getProjectSkillLocations(workspaceRoot) {
|
|
64
|
+
return [
|
|
65
|
+
{ basePath: path.join(workspaceRoot, ".claude", "skills"), source: "claude-project", recursive: false },
|
|
66
|
+
{ basePath: path.join(workspaceRoot, PROJECT_DIR_NAME, "skills"), source: "autohand-project", recursive: true }
|
|
67
|
+
];
|
|
68
|
+
}
|
|
63
69
|
|
|
64
70
|
export {
|
|
65
71
|
AUTOHAND_HOME,
|
|
66
72
|
AUTOHAND_PATHS,
|
|
67
73
|
AUTOHAND_FILES,
|
|
68
74
|
PROJECT_DIR_NAME,
|
|
69
|
-
AUTH_CONFIG
|
|
75
|
+
AUTH_CONFIG,
|
|
76
|
+
SKILL_LOCATIONS,
|
|
77
|
+
getProjectSkillLocations
|
|
70
78
|
};
|
|
71
79
|
/**
|
|
72
80
|
* @license
|
|
@@ -5,7 +5,7 @@ import readline from "readline";
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "autohand-cli",
|
|
8
|
-
version: "0.6.
|
|
8
|
+
version: "0.6.7",
|
|
9
9
|
licenses: "SEE LICENSE IN LICENSE",
|
|
10
10
|
description: "Autohand interactive coding agent CLI powered by LLMs.",
|
|
11
11
|
type: "module",
|