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.
Files changed (76) hide show
  1. package/dist/SkillsRegistry-LXDK73BL.cjs +9 -0
  2. package/dist/SkillsRegistry-SP5MX7OA.js +9 -0
  3. package/dist/agents-FH47ZMOI.cjs +10 -0
  4. package/dist/{agents-OJWYZN6X.js → agents-NB5VQN6H.js} +2 -2
  5. package/dist/{agents-new-WQLJOXSS.js → agents-new-M325HGWT.js} +3 -2
  6. package/dist/agents-new-XLEU26YI.cjs +11 -0
  7. package/dist/{chunk-DD2YPHP5.cjs → chunk-2OBNJCG6.cjs} +15 -13
  8. package/dist/{chunk-NWXYG5PQ.js → chunk-37NUB5KX.js} +1 -1
  9. package/dist/{chunk-AL4Z4WKG.cjs → chunk-3DPDLZYY.cjs} +9 -7
  10. package/dist/chunk-3HPUOQJN.cjs +23 -0
  11. package/dist/{chunk-G7SYGATA.cjs → chunk-3ZUWWML7.cjs} +2 -2
  12. package/dist/{chunk-3Y6G5DUX.cjs → chunk-53YDUYNS.cjs} +10 -4
  13. package/dist/{chunk-PRZTK2FX.js → chunk-5WKR4HIB.js} +7 -5
  14. package/dist/{chunk-ZTA2ASFW.cjs → chunk-6ZGNSZRG.cjs} +1 -1
  15. package/dist/chunk-7BYSXAKS.js +23 -0
  16. package/dist/{chunk-6FEZ6JAQ.js → chunk-7HB7GSQF.js} +1 -1
  17. package/dist/{chunk-UBGEAEKS.js → chunk-AY2XV7TH.js} +1 -1
  18. package/dist/{chunk-7RRX7H2X.cjs → chunk-B5N5UAMO.cjs} +20 -12
  19. package/dist/{chunk-KZ2UXXLH.js → chunk-BAHUKJJR.js} +7 -5
  20. package/dist/{chunk-KT55HW6V.js → chunk-CHQMK2ZG.js} +1 -1
  21. package/dist/chunk-CVYEUA3D.cjs +528 -0
  22. package/dist/{chunk-MRQV5HMC.js → chunk-DE7YC5MB.js} +8 -6
  23. package/dist/{chunk-AD4O67ZA.cjs → chunk-EJ77L3KT.cjs} +10 -10
  24. package/dist/{chunk-737A24RB.js → chunk-FUEL6BK7.js} +9 -1
  25. package/dist/{chunk-6MCXWSR3.js → chunk-H4RPZD6H.js} +1 -1
  26. package/dist/chunk-JHGIWNHL.cjs +46 -0
  27. package/dist/{chunk-27JNK5TE.cjs → chunk-LUKMRIKJ.cjs} +40 -61
  28. package/dist/{chunk-4H3B46YX.js → chunk-MWLAHCU7.js} +9 -3
  29. package/dist/{chunk-QCMC2WOC.cjs → chunk-N6ZOJI2M.cjs} +4 -4
  30. package/dist/{chunk-2TPGTNNY.js → chunk-NGSLABLS.js} +37 -58
  31. package/dist/chunk-QHPFA6OE.js +46 -0
  32. package/dist/{chunk-M4LKQQHU.cjs → chunk-REPKBECD.cjs} +2 -2
  33. package/dist/chunk-SKU4M27Z.js +528 -0
  34. package/dist/chunk-W4XTDUGT.js +267 -0
  35. package/dist/{chunk-RDEROLKA.cjs → chunk-XAM7SFVB.cjs} +8 -6
  36. package/dist/chunk-XF4EQ3IV.cjs +267 -0
  37. package/dist/{chunk-IHJDYAYJ.cjs → chunk-XTHHDIBG.cjs} +9 -1
  38. package/dist/{chunk-5N3QP5LJ.js → chunk-YDH2BMEN.js} +19 -11
  39. package/dist/constants-ICQLSGZN.cjs +19 -0
  40. package/dist/constants-N3I2FHCM.js +19 -0
  41. package/dist/feedback-TOGESBX7.cjs +11 -0
  42. package/dist/{feedback-3THCLEBE.js → feedback-YGSYBQEW.js} +3 -2
  43. package/dist/index.cjs +1463 -1146
  44. package/dist/index.js +987 -670
  45. package/dist/login-QVBS7KBK.cjs +13 -0
  46. package/dist/login-XUVEFKCR.js +13 -0
  47. package/dist/logout-75YLPOBK.js +13 -0
  48. package/dist/logout-FYYR5KCP.cjs +13 -0
  49. package/dist/{permissions-CYW62ZK3.js → permissions-3GS4ZWVA.js} +2 -1
  50. package/dist/permissions-E3MTIE7D.cjs +10 -0
  51. package/dist/{skills-HF4SAF5O.js → skills-3M26KASS.js} +3 -1
  52. package/dist/skills-BOFY5RQN.cjs +13 -0
  53. package/dist/skills-install-WJ2LDRQG.js +680 -0
  54. package/dist/skills-install-Z3W5PQWQ.cjs +680 -0
  55. package/dist/skills-new-KIBUN63X.js +12 -0
  56. package/dist/skills-new-XDYS24XW.cjs +12 -0
  57. package/dist/{status-SLYYTKXD.js → status-6FY6RKIS.js} +1 -1
  58. package/dist/status-DJHDT6QH.cjs +9 -0
  59. package/dist/theme-2UK74UWR.cjs +13 -0
  60. package/dist/{theme-THMQ5AIN.js → theme-XNZ2X6HE.js} +3 -3
  61. package/package.json +1 -1
  62. package/dist/agents-EHLYBJLK.cjs +0 -10
  63. package/dist/agents-new-7VPASCBV.cjs +0 -10
  64. package/dist/chunk-NEAJ2UWG.js +0 -191
  65. package/dist/chunk-Z6SGIQWH.cjs +0 -191
  66. package/dist/feedback-PATTKRH5.cjs +0 -10
  67. package/dist/login-QJROML5I.js +0 -12
  68. package/dist/login-X66DSV75.cjs +0 -12
  69. package/dist/logout-3Z7R3F7J.cjs +0 -12
  70. package/dist/logout-RJ5OAXRI.js +0 -12
  71. package/dist/permissions-NOC5DMOH.cjs +0 -9
  72. package/dist/skills-U6J6DFLK.cjs +0 -11
  73. package/dist/skills-new-QDTNEG3R.js +0 -10
  74. package/dist/skills-new-UPVBHIF2.cjs +0 -10
  75. package/dist/status-GR73LEEN.cjs +0 -9
  76. package/dist/theme-YDANJLZR.cjs +0 -13
@@ -0,0 +1,680 @@
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(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+ var _chunk3HPUOQJNcjs = require('./chunk-3HPUOQJN.cjs');
4
+
5
+
6
+
7
+
8
+ var _chunkXTHHDIBGcjs = require('./chunk-XTHHDIBG.cjs');
9
+ require('./chunk-N254NRHT.cjs');
10
+
11
+ // src/commands/skills-install.ts
12
+ var _path = require('path'); var _path2 = _interopRequireDefault(_path);
13
+ var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
14
+
15
+ // src/skills/GitHubRegistryFetcher.ts
16
+ var DEFAULT_REPO = "autohandai/community-skills";
17
+ var DEFAULT_BRANCH = "main";
18
+ var GitHubRegistryFetcher = class {
19
+ constructor(config = {}) {
20
+ const repo = config.repo || DEFAULT_REPO;
21
+ const branch = config.branch || DEFAULT_BRANCH;
22
+ this.baseUrl = `https://raw.githubusercontent.com/${repo}/${branch}`;
23
+ this.timeout = config.timeout || 15e3;
24
+ }
25
+ /**
26
+ * Fetch the registry.json index file
27
+ */
28
+ async fetchRegistry() {
29
+ const url = `${this.baseUrl}/registry.json`;
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
32
+ try {
33
+ const response = await fetch(url, {
34
+ headers: {
35
+ Accept: "application/json",
36
+ "User-Agent": "autohand-cli"
37
+ },
38
+ signal: controller.signal
39
+ });
40
+ if (!response.ok) {
41
+ throw new Error(`Failed to fetch registry: HTTP ${response.status}`);
42
+ }
43
+ const data = await response.json();
44
+ return this.validateRegistry(data);
45
+ } finally {
46
+ clearTimeout(timeoutId);
47
+ }
48
+ }
49
+ /**
50
+ * Fetch a single file from a skill directory
51
+ */
52
+ async fetchSkillFile(skillDirectory, filePath) {
53
+ const url = `${this.baseUrl}/${skillDirectory}/${filePath}`;
54
+ const controller = new AbortController();
55
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
56
+ try {
57
+ const response = await fetch(url, {
58
+ headers: {
59
+ "User-Agent": "autohand-cli"
60
+ },
61
+ signal: controller.signal
62
+ });
63
+ if (!response.ok) {
64
+ throw new Error(`Failed to fetch ${filePath}: HTTP ${response.status}`);
65
+ }
66
+ return response.text();
67
+ } finally {
68
+ clearTimeout(timeoutId);
69
+ }
70
+ }
71
+ /**
72
+ * Fetch all files for a skill directory
73
+ * Returns a Map of relative file paths to their contents
74
+ */
75
+ async fetchSkillDirectory(skill) {
76
+ const contents = /* @__PURE__ */ new Map();
77
+ const errors = [];
78
+ const concurrencyLimit = 5;
79
+ const files = [...skill.files];
80
+ for (let i = 0; i < files.length; i += concurrencyLimit) {
81
+ const batch = files.slice(i, i + concurrencyLimit);
82
+ const results = await Promise.allSettled(
83
+ batch.map(async (file) => {
84
+ const content = await this.fetchSkillFile(skill.directory, file);
85
+ return { file, content };
86
+ })
87
+ );
88
+ for (const result of results) {
89
+ if (result.status === "fulfilled") {
90
+ contents.set(result.value.file, result.value.content);
91
+ } else {
92
+ errors.push(_optionalChain([result, 'access', _ => _.reason, 'optionalAccess', _2 => _2.message]) || "Unknown error");
93
+ }
94
+ }
95
+ }
96
+ if (!contents.has("SKILL.md")) {
97
+ throw new Error(
98
+ `Failed to fetch SKILL.md for ${skill.name}: ${errors.join(", ")}`
99
+ );
100
+ }
101
+ return contents;
102
+ }
103
+ /**
104
+ * Validate and normalize the registry data
105
+ */
106
+ validateRegistry(data) {
107
+ if (!data || typeof data !== "object") {
108
+ throw new Error("Invalid registry: expected object");
109
+ }
110
+ const registry = data;
111
+ if (!Array.isArray(registry.skills)) {
112
+ throw new Error("Invalid registry: missing skills array");
113
+ }
114
+ if (!Array.isArray(registry.categories)) {
115
+ throw new Error("Invalid registry: missing categories array");
116
+ }
117
+ const validatedSkills = [];
118
+ for (const skill of registry.skills) {
119
+ if (this.isValidSkill(skill)) {
120
+ validatedSkills.push(skill);
121
+ }
122
+ }
123
+ return {
124
+ version: String(registry.version || "1.0.0"),
125
+ updatedAt: String(registry.updatedAt || (/* @__PURE__ */ new Date()).toISOString()),
126
+ skills: validatedSkills,
127
+ categories: registry.categories
128
+ };
129
+ }
130
+ /**
131
+ * Type guard for valid skill objects
132
+ */
133
+ isValidSkill(skill) {
134
+ if (!skill || typeof skill !== "object") return false;
135
+ const s = skill;
136
+ return typeof s.id === "string" && typeof s.name === "string" && typeof s.description === "string" && typeof s.directory === "string" && Array.isArray(s.files) && s.files.includes("SKILL.md");
137
+ }
138
+ /**
139
+ * Search skills by query (client-side filtering)
140
+ */
141
+ filterSkills(skills, query) {
142
+ if (!query.trim()) return skills;
143
+ const lowerQuery = query.toLowerCase();
144
+ return skills.filter((skill) => {
145
+ const searchText = [
146
+ skill.name,
147
+ skill.description,
148
+ skill.category,
149
+ ...skill.tags || [],
150
+ ...skill.languages || [],
151
+ ...skill.frameworks || []
152
+ ].join(" ").toLowerCase();
153
+ return searchText.includes(lowerQuery);
154
+ });
155
+ }
156
+ /**
157
+ * Get skills by category
158
+ */
159
+ getSkillsByCategory(skills, categoryId) {
160
+ return skills.filter((skill) => skill.category === categoryId);
161
+ }
162
+ /**
163
+ * Get featured skills
164
+ */
165
+ getFeaturedSkills(skills) {
166
+ return skills.filter((skill) => skill.isFeatured);
167
+ }
168
+ /**
169
+ * Find a skill by name or ID
170
+ */
171
+ findSkill(skills, nameOrId) {
172
+ const lower = nameOrId.toLowerCase();
173
+ return skills.find(
174
+ (s) => s.id.toLowerCase() === lower || s.name.toLowerCase() === lower
175
+ ) || null;
176
+ }
177
+ /**
178
+ * Get similar skills based on simple string matching
179
+ */
180
+ findSimilarSkills(skills, query, limit = 5) {
181
+ const lower = query.toLowerCase();
182
+ const scored = skills.map((skill) => {
183
+ let score = 0;
184
+ if (skill.name.toLowerCase().includes(lower)) {
185
+ score += 10;
186
+ }
187
+ if (skill.description.toLowerCase().includes(lower)) {
188
+ score += 5;
189
+ }
190
+ if (_optionalChain([skill, 'access', _3 => _3.tags, 'optionalAccess', _4 => _4.some, 'call', _5 => _5((t) => t.toLowerCase().includes(lower))])) {
191
+ score += 3;
192
+ }
193
+ return { skill, score };
194
+ });
195
+ return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.skill);
196
+ }
197
+ };
198
+
199
+ // src/skills/CommunitySkillsCache.ts
200
+ var _fsextra = require('fs-extra'); var _fsextra2 = _interopRequireDefault(_fsextra);
201
+
202
+ var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
203
+ var DEFAULT_MAX_SKILLS_CACHE = 50;
204
+ var CommunitySkillsCache = class {
205
+ constructor(config = {}) {
206
+ this.cacheDir = config.cacheDir || _path2.default.join(_chunkXTHHDIBGcjs.AUTOHAND_HOME, "community-skills", "cache");
207
+ this.ttlMs = _nullishCoalesce(config.ttlMs, () => ( DEFAULT_TTL_MS));
208
+ this.maxSkillsCache = _nullishCoalesce(config.maxSkillsCache, () => ( DEFAULT_MAX_SKILLS_CACHE));
209
+ }
210
+ /**
211
+ * Get the cache directory path
212
+ */
213
+ getCacheDir() {
214
+ return this.cacheDir;
215
+ }
216
+ /**
217
+ * Get cached registry if it exists and is not expired
218
+ */
219
+ async getRegistry() {
220
+ const cached = await this.readCachedRegistry();
221
+ if (!cached) return null;
222
+ if (Date.now() - cached.fetchedAt >= this.ttlMs) {
223
+ return null;
224
+ }
225
+ return cached.registry;
226
+ }
227
+ /**
228
+ * Get cached registry ignoring TTL (for offline fallback)
229
+ */
230
+ async getRegistryIgnoreTTL() {
231
+ const cached = await this.readCachedRegistry();
232
+ return _optionalChain([cached, 'optionalAccess', _6 => _6.registry]) || null;
233
+ }
234
+ /**
235
+ * Get cache age in milliseconds, or null if no cache exists
236
+ */
237
+ async getCacheAge() {
238
+ const cached = await this.readCachedRegistry();
239
+ if (!cached) return null;
240
+ return Date.now() - cached.fetchedAt;
241
+ }
242
+ /**
243
+ * Check if cache is expired
244
+ */
245
+ async isExpired() {
246
+ const age = await this.getCacheAge();
247
+ if (age === null) return true;
248
+ return age >= this.ttlMs;
249
+ }
250
+ /**
251
+ * Save registry to cache
252
+ */
253
+ async setRegistry(registry, etag) {
254
+ const cached = {
255
+ registry,
256
+ fetchedAt: Date.now(),
257
+ etag
258
+ };
259
+ await _fsextra2.default.ensureDir(this.cacheDir);
260
+ await _fsextra2.default.writeJson(this.registryPath, cached, { spaces: 2 });
261
+ }
262
+ /**
263
+ * Get a cached skill body by skill ID
264
+ */
265
+ async getSkillBody(skillId) {
266
+ const skillPath = this.getSkillBodyPath(skillId);
267
+ if (await _fsextra2.default.pathExists(skillPath)) {
268
+ try {
269
+ return await _fsextra2.default.readFile(skillPath, "utf-8");
270
+ } catch (e) {
271
+ return null;
272
+ }
273
+ }
274
+ return null;
275
+ }
276
+ /**
277
+ * Cache a skill body
278
+ */
279
+ async setSkillBody(skillId, body) {
280
+ const skillsDir = _path2.default.join(this.cacheDir, "skills");
281
+ await _fsextra2.default.ensureDir(skillsDir);
282
+ await this.enforceMaxSkillsCache();
283
+ await _fsextra2.default.writeFile(this.getSkillBodyPath(skillId), body, "utf-8");
284
+ }
285
+ /**
286
+ * Get cached skill directory files
287
+ * Returns Map of relative paths to contents, or null if not cached
288
+ */
289
+ async getSkillDirectory(skillId) {
290
+ const skillCacheDir = _path2.default.join(this.cacheDir, "skills", skillId);
291
+ if (!await _fsextra2.default.pathExists(skillCacheDir)) {
292
+ return null;
293
+ }
294
+ try {
295
+ const files = /* @__PURE__ */ new Map();
296
+ await this.readDirRecursive(skillCacheDir, skillCacheDir, files);
297
+ return files.size > 0 ? files : null;
298
+ } catch (e2) {
299
+ return null;
300
+ }
301
+ }
302
+ /**
303
+ * Cache a skill directory with all its files
304
+ */
305
+ async setSkillDirectory(skillId, files) {
306
+ const skillCacheDir = _path2.default.join(this.cacheDir, "skills", skillId);
307
+ await this.enforceMaxSkillsCache();
308
+ await _fsextra2.default.remove(skillCacheDir);
309
+ for (const [relativePath, content] of files) {
310
+ const fullPath = _path2.default.join(skillCacheDir, relativePath);
311
+ await _fsextra2.default.ensureDir(_path2.default.dirname(fullPath));
312
+ await _fsextra2.default.writeFile(fullPath, content, "utf-8");
313
+ }
314
+ }
315
+ /**
316
+ * Clear all cached data
317
+ */
318
+ async clear() {
319
+ await _fsextra2.default.remove(this.cacheDir);
320
+ }
321
+ /**
322
+ * Clear only the registry cache (keep skill bodies)
323
+ */
324
+ async clearRegistry() {
325
+ await _fsextra2.default.remove(this.registryPath);
326
+ }
327
+ /**
328
+ * Get cache statistics
329
+ */
330
+ async getStats() {
331
+ const skillsDir = _path2.default.join(this.cacheDir, "skills");
332
+ let cachedSkillCount = 0;
333
+ if (await _fsextra2.default.pathExists(skillsDir)) {
334
+ const entries = await _fsextra2.default.readdir(skillsDir);
335
+ cachedSkillCount = entries.length;
336
+ }
337
+ const cached = await this.readCachedRegistry();
338
+ const registryAge = cached ? Date.now() - cached.fetchedAt : null;
339
+ return {
340
+ hasRegistry: cached !== null,
341
+ registryAge,
342
+ isExpired: registryAge !== null ? registryAge >= this.ttlMs : true,
343
+ cachedSkillCount
344
+ };
345
+ }
346
+ // ========== Private Methods ==========
347
+ get registryPath() {
348
+ return _path2.default.join(this.cacheDir, "registry.json");
349
+ }
350
+ getSkillBodyPath(skillId) {
351
+ return _path2.default.join(this.cacheDir, "skills", `${skillId}.md`);
352
+ }
353
+ async readCachedRegistry() {
354
+ if (!await _fsextra2.default.pathExists(this.registryPath)) {
355
+ return null;
356
+ }
357
+ try {
358
+ const data = await _fsextra2.default.readJson(this.registryPath);
359
+ if (typeof data === "object" && data !== null && typeof data.fetchedAt === "number" && data.registry && Array.isArray(data.registry.skills)) {
360
+ return data;
361
+ }
362
+ return null;
363
+ } catch (e3) {
364
+ return null;
365
+ }
366
+ }
367
+ /**
368
+ * Recursively read directory contents into a Map
369
+ */
370
+ async readDirRecursive(baseDir, currentDir, files) {
371
+ const entries = await _fsextra2.default.readdir(currentDir, { withFileTypes: true });
372
+ for (const entry of entries) {
373
+ const fullPath = _path2.default.join(currentDir, entry.name);
374
+ const relativePath = _path2.default.relative(baseDir, fullPath);
375
+ if (entry.isDirectory()) {
376
+ await this.readDirRecursive(baseDir, fullPath, files);
377
+ } else if (entry.isFile()) {
378
+ const content = await _fsextra2.default.readFile(fullPath, "utf-8");
379
+ files.set(relativePath, content);
380
+ }
381
+ }
382
+ }
383
+ /**
384
+ * Enforce maximum number of cached skills by removing oldest
385
+ */
386
+ async enforceMaxSkillsCache() {
387
+ const skillsDir = _path2.default.join(this.cacheDir, "skills");
388
+ if (!await _fsextra2.default.pathExists(skillsDir)) {
389
+ return;
390
+ }
391
+ const entries = await _fsextra2.default.readdir(skillsDir);
392
+ if (entries.length < this.maxSkillsCache) {
393
+ return;
394
+ }
395
+ const withStats = await Promise.all(
396
+ entries.map(async (name) => {
397
+ const entryPath = _path2.default.join(skillsDir, name);
398
+ try {
399
+ const stat = await _fsextra2.default.stat(entryPath);
400
+ return { name, path: entryPath, mtime: stat.mtime.getTime() };
401
+ } catch (e4) {
402
+ return { name, path: entryPath, mtime: 0 };
403
+ }
404
+ })
405
+ );
406
+ withStats.sort((a, b) => a.mtime - b.mtime);
407
+ const toRemove = withStats.slice(0, entries.length - this.maxSkillsCache + 1);
408
+ for (const entry of toRemove) {
409
+ await _fsextra2.default.remove(entry.path);
410
+ }
411
+ }
412
+ };
413
+
414
+ // src/commands/skills-install.ts
415
+ var metadata = {
416
+ command: "/skills install",
417
+ description: "browse and install community skills",
418
+ implemented: true
419
+ };
420
+ async function skillsInstall(ctx, skillName) {
421
+ const { skillsRegistry, workspaceRoot } = ctx;
422
+ if (!skillsRegistry) {
423
+ console.log(_chalk2.default.red("Skills registry not available."));
424
+ return null;
425
+ }
426
+ const cache = new CommunitySkillsCache();
427
+ const fetcher = new GitHubRegistryFetcher();
428
+ let registry;
429
+ try {
430
+ const cached = await cache.getRegistry();
431
+ if (cached) {
432
+ registry = cached;
433
+ } else {
434
+ console.log(_chalk2.default.cyan("Fetching community skills registry..."));
435
+ registry = await fetcher.fetchRegistry();
436
+ await cache.setRegistry(registry);
437
+ }
438
+ } catch (error) {
439
+ const stale = await cache.getRegistryIgnoreTTL();
440
+ if (stale) {
441
+ console.log(_chalk2.default.yellow("Using cached skills (offline mode)"));
442
+ registry = stale;
443
+ } else {
444
+ console.log(_chalk2.default.red("Failed to fetch community skills. Please check your internet connection."));
445
+ console.log(_chalk2.default.gray(error instanceof Error ? error.message : "Unknown error"));
446
+ return null;
447
+ }
448
+ }
449
+ if (skillName) {
450
+ return directInstall(ctx, registry, fetcher, cache, skillName);
451
+ }
452
+ return interactiveBrowser(ctx, registry, fetcher, cache);
453
+ }
454
+ async function directInstall(ctx, registry, fetcher, cache, skillName) {
455
+ const { skillsRegistry, workspaceRoot } = ctx;
456
+ const skill = fetcher.findSkill(registry.skills, skillName);
457
+ if (!skill) {
458
+ console.log(_chalk2.default.red(`Skill not found: ${skillName}`));
459
+ const similar = fetcher.findSimilarSkills(registry.skills, skillName, 3);
460
+ if (similar.length > 0) {
461
+ console.log(_chalk2.default.gray("Did you mean:"));
462
+ for (const s of similar) {
463
+ console.log(_chalk2.default.gray(` - ${s.name}: ${s.description}`));
464
+ }
465
+ }
466
+ return null;
467
+ }
468
+ const scope = await promptInstallScope();
469
+ if (!scope) {
470
+ console.log(_chalk2.default.gray("Installation cancelled."));
471
+ return null;
472
+ }
473
+ return installSkill(ctx, fetcher, cache, skill, scope);
474
+ }
475
+ async function interactiveBrowser(ctx, registry, fetcher, cache) {
476
+ console.log();
477
+ console.log(_chalk2.default.bold.cyan("Community Skills Marketplace"));
478
+ console.log(_chalk2.default.gray("\u2500".repeat(50)));
479
+ console.log(_chalk2.default.gray(`${registry.skills.length} skills available`));
480
+ console.log();
481
+ console.log(_chalk2.default.bold("Categories:"));
482
+ for (const cat of registry.categories) {
483
+ console.log(_chalk2.default.gray(` ${cat.name} (${cat.count})`));
484
+ }
485
+ console.log();
486
+ const featured = fetcher.getFeaturedSkills(registry.skills);
487
+ if (featured.length > 0) {
488
+ console.log(_chalk2.default.bold.yellow("Featured Skills:"));
489
+ for (const skill of featured.slice(0, 5)) {
490
+ const rating = skill.rating ? `\u2605 ${skill.rating.toFixed(1)}` : "";
491
+ const downloads = skill.downloadCount ? `\u2193${formatDownloads(skill.downloadCount)}` : "";
492
+ console.log(` ${_chalk2.default.green("\u25CF")} ${_chalk2.default.bold(skill.name)} ${_chalk2.default.gray(rating)} ${_chalk2.default.gray(downloads)}`);
493
+ console.log(_chalk2.default.gray(` ${skill.description}`));
494
+ }
495
+ console.log();
496
+ }
497
+ const choices = registry.skills.map((skill) => ({
498
+ name: skill.id,
499
+ message: formatSkillChoice(skill),
500
+ value: skill.id
501
+ }));
502
+ const answer = await _chunk3HPUOQJNcjs.safePrompt.call(void 0, [
503
+ {
504
+ type: "autocomplete",
505
+ name: "skill",
506
+ message: "Select a skill to install (type to search)",
507
+ choices
508
+ }
509
+ // Cast to any to allow autocomplete-specific options
510
+ ]);
511
+ if (!_optionalChain([answer, 'optionalAccess', _7 => _7.skill])) {
512
+ console.log(_chalk2.default.gray("No skill selected."));
513
+ return null;
514
+ }
515
+ const selectedSkill = registry.skills.find((s) => s.id === answer.skill);
516
+ if (!selectedSkill) {
517
+ console.log(_chalk2.default.red("Skill not found."));
518
+ return null;
519
+ }
520
+ console.log();
521
+ console.log(_chalk2.default.bold.cyan(`Skill: ${selectedSkill.name}`));
522
+ console.log(_chalk2.default.gray("\u2500".repeat(50)));
523
+ console.log(_chalk2.default.white("Description: ") + selectedSkill.description);
524
+ console.log(_chalk2.default.white("Category: ") + selectedSkill.category);
525
+ if (_optionalChain([selectedSkill, 'access', _8 => _8.tags, 'optionalAccess', _9 => _9.length])) {
526
+ console.log(_chalk2.default.white("Tags: ") + selectedSkill.tags.join(", "));
527
+ }
528
+ if (selectedSkill.rating) {
529
+ console.log(_chalk2.default.white("Rating: ") + `\u2605 ${selectedSkill.rating.toFixed(1)}`);
530
+ }
531
+ if (selectedSkill.downloadCount) {
532
+ console.log(_chalk2.default.white("Downloads: ") + formatDownloads(selectedSkill.downloadCount));
533
+ }
534
+ if (selectedSkill.files.length > 1) {
535
+ console.log(_chalk2.default.white("Files: ") + selectedSkill.files.length + " files");
536
+ for (const file of selectedSkill.files.slice(0, 5)) {
537
+ console.log(_chalk2.default.gray(` - ${file}`));
538
+ }
539
+ if (selectedSkill.files.length > 5) {
540
+ console.log(_chalk2.default.gray(` ... and ${selectedSkill.files.length - 5} more`));
541
+ }
542
+ }
543
+ console.log();
544
+ const scope = await promptInstallScope();
545
+ if (!scope) {
546
+ console.log(_chalk2.default.gray("Installation cancelled."));
547
+ return null;
548
+ }
549
+ return installSkill(ctx, fetcher, cache, selectedSkill, scope);
550
+ }
551
+ async function promptInstallScope() {
552
+ const answer = await _chunk3HPUOQJNcjs.safePrompt.call(void 0, [
553
+ {
554
+ type: "select",
555
+ name: "scope",
556
+ message: "Install location",
557
+ choices: [
558
+ {
559
+ name: "user",
560
+ message: "User (~/.autohand/skills/) - Available in all projects"
561
+ },
562
+ {
563
+ name: "project",
564
+ message: "Project (.autohand/skills/) - Only this project"
565
+ }
566
+ ]
567
+ }
568
+ ]);
569
+ return _optionalChain([answer, 'optionalAccess', _10 => _10.scope]) || null;
570
+ }
571
+ async function installSkill(ctx, fetcher, cache, skill, scope) {
572
+ const { skillsRegistry, workspaceRoot } = ctx;
573
+ const targetDir = scope === "project" ? _path2.default.join(workspaceRoot, _chunkXTHHDIBGcjs.PROJECT_DIR_NAME, "skills") : _chunkXTHHDIBGcjs.AUTOHAND_PATHS.skills;
574
+ const isInstalled = await skillsRegistry.isSkillInstalled(skill.name, targetDir);
575
+ if (isInstalled) {
576
+ const confirm = await _chunk3HPUOQJNcjs.safePrompt.call(void 0, [
577
+ {
578
+ type: "confirm",
579
+ name: "overwrite",
580
+ message: `Skill "${skill.name}" already exists. Overwrite?`,
581
+ initial: false
582
+ }
583
+ ]);
584
+ if (!_optionalChain([confirm, 'optionalAccess', _11 => _11.overwrite])) {
585
+ console.log(_chalk2.default.gray("Installation cancelled."));
586
+ return null;
587
+ }
588
+ }
589
+ console.log(_chalk2.default.cyan(`Installing ${skill.name}...`));
590
+ try {
591
+ let files = await cache.getSkillDirectory(skill.id);
592
+ if (!files) {
593
+ console.log(_chalk2.default.gray(`Fetching ${skill.files.length} files...`));
594
+ files = await fetcher.fetchSkillDirectory(skill);
595
+ await cache.setSkillDirectory(skill.id, files);
596
+ }
597
+ const result = await skillsRegistry.importCommunitySkillDirectory(
598
+ skill.name,
599
+ files,
600
+ targetDir,
601
+ isInstalled
602
+ // force if overwriting
603
+ );
604
+ if (result.success) {
605
+ console.log(_chalk2.default.green(`\u2713 Installed ${skill.name} to ${scope} skills`));
606
+ console.log(_chalk2.default.gray(` Path: ${result.path}`));
607
+ console.log();
608
+ console.log(_chalk2.default.gray("To activate this skill, run:"));
609
+ console.log(_chalk2.default.gray(` /skills use ${skill.name}`));
610
+ return `Skill "${skill.name}" installed successfully.`;
611
+ } else {
612
+ console.log(_chalk2.default.red(`Failed to install: ${result.error}`));
613
+ return null;
614
+ }
615
+ } catch (error) {
616
+ console.log(_chalk2.default.red("Installation failed."));
617
+ console.log(_chalk2.default.gray(error instanceof Error ? error.message : "Unknown error"));
618
+ return null;
619
+ }
620
+ }
621
+ function formatDownloads(count) {
622
+ if (count >= 1e6) return `${(count / 1e6).toFixed(1)}M`;
623
+ if (count >= 1e3) return `${(count / 1e3).toFixed(1)}K`;
624
+ return String(count);
625
+ }
626
+ function formatSkillChoice(skill) {
627
+ const parts = [];
628
+ if (skill.isFeatured) {
629
+ parts.push(_chalk2.default.yellow("\u2605"));
630
+ } else if (skill.isCurated) {
631
+ parts.push(_chalk2.default.green("\u2713"));
632
+ } else {
633
+ parts.push(" ");
634
+ }
635
+ parts.push(_chalk2.default.bold(skill.name.padEnd(30)));
636
+ if (skill.rating) {
637
+ parts.push(_chalk2.default.gray(`${skill.rating.toFixed(1)}`));
638
+ }
639
+ parts.push(_chalk2.default.gray(skill.description.slice(0, 40)));
640
+ return parts.join(" ");
641
+ }
642
+ async function refreshCache() {
643
+ const cache = new CommunitySkillsCache();
644
+ const fetcher = new GitHubRegistryFetcher();
645
+ console.log(_chalk2.default.cyan("Refreshing community skills cache..."));
646
+ try {
647
+ const registry = await fetcher.fetchRegistry();
648
+ await cache.setRegistry(registry);
649
+ console.log(_chalk2.default.green(`\u2713 Cached ${registry.skills.length} skills`));
650
+ } catch (error) {
651
+ console.log(_chalk2.default.red("Failed to refresh cache."));
652
+ console.log(_chalk2.default.gray(error instanceof Error ? error.message : "Unknown error"));
653
+ }
654
+ }
655
+
656
+
657
+
658
+
659
+ exports.metadata = metadata; exports.refreshCache = refreshCache; exports.skillsInstall = skillsInstall;
660
+ /**
661
+ * @license
662
+ * Copyright 2025 Autohand AI LLC
663
+ * SPDX-License-Identifier: Apache-2.0
664
+ *
665
+ * GitHubRegistryFetcher - Fetches community skills registry from GitHub
666
+ */
667
+ /**
668
+ * @license
669
+ * Copyright 2025 Autohand AI LLC
670
+ * SPDX-License-Identifier: Apache-2.0
671
+ *
672
+ * CommunitySkillsCache - TTL-based caching for community skills registry
673
+ */
674
+ /**
675
+ * @license
676
+ * Copyright 2025 Autohand AI LLC
677
+ * SPDX-License-Identifier: Apache-2.0
678
+ *
679
+ * Skills install command - Browse and install community skills from GitHub
680
+ */
@@ -0,0 +1,12 @@
1
+ import {
2
+ createSkill,
3
+ metadata
4
+ } from "./chunk-NGSLABLS.js";
5
+ import "./chunk-QHPFA6OE.js";
6
+ import "./chunk-7BYSXAKS.js";
7
+ import "./chunk-FUEL6BK7.js";
8
+ import "./chunk-JSBRDJBE.js";
9
+ export {
10
+ createSkill,
11
+ metadata
12
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+ var _chunkLUKMRIKJcjs = require('./chunk-LUKMRIKJ.cjs');
5
+ require('./chunk-JHGIWNHL.cjs');
6
+ require('./chunk-3HPUOQJN.cjs');
7
+ require('./chunk-XTHHDIBG.cjs');
8
+ require('./chunk-N254NRHT.cjs');
9
+
10
+
11
+
12
+ exports.createSkill = _chunkLUKMRIKJcjs.createSkill; exports.metadata = _chunkLUKMRIKJcjs.metadata;