@agentskill.sh/cli 1.0.8 → 2.0.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.
@@ -0,0 +1,10 @@
1
+ import type { AgentConfig, AgentType } from './types.js';
2
+ export declare const agents: Record<AgentType, AgentConfig>;
3
+ /** Detect all agents installed on the machine */
4
+ export declare function detectInstalledAgents(): Promise<AgentType[]>;
5
+ /** Agents that use the universal .agents/skills directory */
6
+ export declare function getUniversalAgents(): AgentType[];
7
+ /** Agents that need their own skill directory */
8
+ export declare function getNonUniversalAgents(): AgentType[];
9
+ export declare function isUniversalAgent(type: AgentType): boolean;
10
+ export declare function getAgentDisplayName(type: AgentType): string;
package/dist/agents.js ADDED
@@ -0,0 +1,347 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ import { existsSync } from 'fs';
4
+ const home = homedir();
5
+ const configHome = process.env.XDG_CONFIG_HOME || join(home, '.config');
6
+ const codexHome = process.env.CODEX_HOME?.trim() || join(home, '.codex');
7
+ const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join(home, '.claude');
8
+ export const agents = {
9
+ // Universal agents (use .agents/skills)
10
+ amp: {
11
+ name: 'amp',
12
+ displayName: 'Amp',
13
+ skillsDir: '.agents/skills',
14
+ globalSkillsDir: join(configHome, 'agents/skills'),
15
+ detectInstalled: async () => existsSync(join(configHome, 'amp')),
16
+ },
17
+ antigravity: {
18
+ name: 'antigravity',
19
+ displayName: 'Antigravity',
20
+ skillsDir: '.agents/skills',
21
+ globalSkillsDir: join(home, '.gemini/antigravity/skills'),
22
+ detectInstalled: async () => existsSync(join(home, '.gemini/antigravity')),
23
+ },
24
+ cline: {
25
+ name: 'cline',
26
+ displayName: 'Cline',
27
+ skillsDir: '.agents/skills',
28
+ globalSkillsDir: join(home, '.agents/skills'),
29
+ detectInstalled: async () => existsSync(join(home, '.cline')),
30
+ },
31
+ codex: {
32
+ name: 'codex',
33
+ displayName: 'Codex',
34
+ skillsDir: '.agents/skills',
35
+ globalSkillsDir: join(codexHome, 'skills'),
36
+ detectInstalled: async () => existsSync(codexHome) || existsSync('/etc/codex'),
37
+ },
38
+ cursor: {
39
+ name: 'cursor',
40
+ displayName: 'Cursor',
41
+ skillsDir: '.agents/skills',
42
+ globalSkillsDir: join(home, '.cursor/skills'),
43
+ detectInstalled: async () => existsSync(join(home, '.cursor')),
44
+ },
45
+ deepagents: {
46
+ name: 'deepagents',
47
+ displayName: 'Deep Agents',
48
+ skillsDir: '.agents/skills',
49
+ globalSkillsDir: join(home, '.deepagents/agent/skills'),
50
+ detectInstalled: async () => existsSync(join(home, '.deepagents')),
51
+ },
52
+ firebender: {
53
+ name: 'firebender',
54
+ displayName: 'Firebender',
55
+ skillsDir: '.agents/skills',
56
+ globalSkillsDir: join(home, '.firebender/skills'),
57
+ detectInstalled: async () => existsSync(join(home, '.firebender')),
58
+ },
59
+ 'gemini-cli': {
60
+ name: 'gemini-cli',
61
+ displayName: 'Gemini CLI',
62
+ skillsDir: '.agents/skills',
63
+ globalSkillsDir: join(home, '.gemini/skills'),
64
+ detectInstalled: async () => existsSync(join(home, '.gemini')),
65
+ },
66
+ 'github-copilot': {
67
+ name: 'github-copilot',
68
+ displayName: 'GitHub Copilot',
69
+ skillsDir: '.agents/skills',
70
+ globalSkillsDir: join(home, '.copilot/skills'),
71
+ detectInstalled: async () => existsSync(join(home, '.copilot')),
72
+ },
73
+ 'kimi-cli': {
74
+ name: 'kimi-cli',
75
+ displayName: 'Kimi Code CLI',
76
+ skillsDir: '.agents/skills',
77
+ globalSkillsDir: join(configHome, 'agents/skills'),
78
+ detectInstalled: async () => existsSync(join(home, '.kimi')),
79
+ },
80
+ opencode: {
81
+ name: 'opencode',
82
+ displayName: 'OpenCode',
83
+ skillsDir: '.agents/skills',
84
+ globalSkillsDir: join(configHome, 'opencode/skills'),
85
+ detectInstalled: async () => existsSync(join(configHome, 'opencode')),
86
+ },
87
+ warp: {
88
+ name: 'warp',
89
+ displayName: 'Warp',
90
+ skillsDir: '.agents/skills',
91
+ globalSkillsDir: join(home, '.agents/skills'),
92
+ detectInstalled: async () => existsSync(join(home, '.warp')),
93
+ },
94
+ // Non-universal agents (agent-specific skill dirs)
95
+ augment: {
96
+ name: 'augment',
97
+ displayName: 'Augment',
98
+ skillsDir: '.augment/skills',
99
+ globalSkillsDir: join(home, '.augment/skills'),
100
+ detectInstalled: async () => existsSync(join(home, '.augment')),
101
+ },
102
+ bob: {
103
+ name: 'bob',
104
+ displayName: 'IBM Bob',
105
+ skillsDir: '.bob/skills',
106
+ globalSkillsDir: join(home, '.bob/skills'),
107
+ detectInstalled: async () => existsSync(join(home, '.bob')),
108
+ },
109
+ 'claude-code': {
110
+ name: 'claude-code',
111
+ displayName: 'Claude Code',
112
+ skillsDir: '.claude/skills',
113
+ globalSkillsDir: join(claudeHome, 'skills'),
114
+ detectInstalled: async () => existsSync(claudeHome),
115
+ },
116
+ openclaw: {
117
+ name: 'openclaw',
118
+ displayName: 'OpenClaw',
119
+ skillsDir: 'skills',
120
+ globalSkillsDir: join(home, '.openclaw/skills'),
121
+ detectInstalled: async () => existsSync(join(home, '.openclaw')) ||
122
+ existsSync(join(home, '.clawdbot')) ||
123
+ existsSync(join(home, '.moltbot')),
124
+ },
125
+ codebuddy: {
126
+ name: 'codebuddy',
127
+ displayName: 'CodeBuddy',
128
+ skillsDir: '.codebuddy/skills',
129
+ globalSkillsDir: join(home, '.codebuddy/skills'),
130
+ detectInstalled: async () => existsSync(join(home, '.codebuddy')),
131
+ },
132
+ 'command-code': {
133
+ name: 'command-code',
134
+ displayName: 'Command Code',
135
+ skillsDir: '.commandcode/skills',
136
+ globalSkillsDir: join(home, '.commandcode/skills'),
137
+ detectInstalled: async () => existsSync(join(home, '.commandcode')),
138
+ },
139
+ continue: {
140
+ name: 'continue',
141
+ displayName: 'Continue',
142
+ skillsDir: '.continue/skills',
143
+ globalSkillsDir: join(home, '.continue/skills'),
144
+ detectInstalled: async () => existsSync(join(home, '.continue')),
145
+ },
146
+ cortex: {
147
+ name: 'cortex',
148
+ displayName: 'Cortex Code',
149
+ skillsDir: '.cortex/skills',
150
+ globalSkillsDir: join(home, '.snowflake/cortex/skills'),
151
+ detectInstalled: async () => existsSync(join(home, '.snowflake/cortex')),
152
+ },
153
+ crush: {
154
+ name: 'crush',
155
+ displayName: 'Crush',
156
+ skillsDir: '.crush/skills',
157
+ globalSkillsDir: join(configHome, 'crush/skills'),
158
+ detectInstalled: async () => existsSync(join(configHome, 'crush')),
159
+ },
160
+ droid: {
161
+ name: 'droid',
162
+ displayName: 'Droid',
163
+ skillsDir: '.factory/skills',
164
+ globalSkillsDir: join(home, '.factory/skills'),
165
+ detectInstalled: async () => existsSync(join(home, '.factory')),
166
+ },
167
+ goose: {
168
+ name: 'goose',
169
+ displayName: 'Goose',
170
+ skillsDir: '.goose/skills',
171
+ globalSkillsDir: join(configHome, 'goose/skills'),
172
+ detectInstalled: async () => existsSync(join(configHome, 'goose')),
173
+ },
174
+ junie: {
175
+ name: 'junie',
176
+ displayName: 'Junie',
177
+ skillsDir: '.junie/skills',
178
+ globalSkillsDir: join(home, '.junie/skills'),
179
+ detectInstalled: async () => existsSync(join(home, '.junie')),
180
+ },
181
+ 'iflow-cli': {
182
+ name: 'iflow-cli',
183
+ displayName: 'iFlow CLI',
184
+ skillsDir: '.iflow/skills',
185
+ globalSkillsDir: join(home, '.iflow/skills'),
186
+ detectInstalled: async () => existsSync(join(home, '.iflow')),
187
+ },
188
+ kilo: {
189
+ name: 'kilo',
190
+ displayName: 'Kilo Code',
191
+ skillsDir: '.kilocode/skills',
192
+ globalSkillsDir: join(home, '.kilocode/skills'),
193
+ detectInstalled: async () => existsSync(join(home, '.kilocode')),
194
+ },
195
+ 'kiro-cli': {
196
+ name: 'kiro-cli',
197
+ displayName: 'Kiro CLI',
198
+ skillsDir: '.kiro/skills',
199
+ globalSkillsDir: join(home, '.kiro/skills'),
200
+ detectInstalled: async () => existsSync(join(home, '.kiro')),
201
+ },
202
+ kode: {
203
+ name: 'kode',
204
+ displayName: 'Kode',
205
+ skillsDir: '.kode/skills',
206
+ globalSkillsDir: join(home, '.kode/skills'),
207
+ detectInstalled: async () => existsSync(join(home, '.kode')),
208
+ },
209
+ mcpjam: {
210
+ name: 'mcpjam',
211
+ displayName: 'MCPJam',
212
+ skillsDir: '.mcpjam/skills',
213
+ globalSkillsDir: join(home, '.mcpjam/skills'),
214
+ detectInstalled: async () => existsSync(join(home, '.mcpjam')),
215
+ },
216
+ 'mistral-vibe': {
217
+ name: 'mistral-vibe',
218
+ displayName: 'Mistral Vibe',
219
+ skillsDir: '.vibe/skills',
220
+ globalSkillsDir: join(home, '.vibe/skills'),
221
+ detectInstalled: async () => existsSync(join(home, '.vibe')),
222
+ },
223
+ mux: {
224
+ name: 'mux',
225
+ displayName: 'Mux',
226
+ skillsDir: '.mux/skills',
227
+ globalSkillsDir: join(home, '.mux/skills'),
228
+ detectInstalled: async () => existsSync(join(home, '.mux')),
229
+ },
230
+ openhands: {
231
+ name: 'openhands',
232
+ displayName: 'OpenHands',
233
+ skillsDir: '.openhands/skills',
234
+ globalSkillsDir: join(home, '.openhands/skills'),
235
+ detectInstalled: async () => existsSync(join(home, '.openhands')),
236
+ },
237
+ pi: {
238
+ name: 'pi',
239
+ displayName: 'Pi',
240
+ skillsDir: '.pi/skills',
241
+ globalSkillsDir: join(home, '.pi/agent/skills'),
242
+ detectInstalled: async () => existsSync(join(home, '.pi/agent')),
243
+ },
244
+ qoder: {
245
+ name: 'qoder',
246
+ displayName: 'Qoder',
247
+ skillsDir: '.qoder/skills',
248
+ globalSkillsDir: join(home, '.qoder/skills'),
249
+ detectInstalled: async () => existsSync(join(home, '.qoder')),
250
+ },
251
+ 'qwen-code': {
252
+ name: 'qwen-code',
253
+ displayName: 'Qwen Code',
254
+ skillsDir: '.qwen/skills',
255
+ globalSkillsDir: join(home, '.qwen/skills'),
256
+ detectInstalled: async () => existsSync(join(home, '.qwen')),
257
+ },
258
+ roo: {
259
+ name: 'roo',
260
+ displayName: 'Roo Code',
261
+ skillsDir: '.roo/skills',
262
+ globalSkillsDir: join(home, '.roo/skills'),
263
+ detectInstalled: async () => existsSync(join(home, '.roo')),
264
+ },
265
+ trae: {
266
+ name: 'trae',
267
+ displayName: 'Trae',
268
+ skillsDir: '.trae/skills',
269
+ globalSkillsDir: join(home, '.trae/skills'),
270
+ detectInstalled: async () => existsSync(join(home, '.trae')),
271
+ },
272
+ 'trae-cn': {
273
+ name: 'trae-cn',
274
+ displayName: 'Trae CN',
275
+ skillsDir: '.trae/skills',
276
+ globalSkillsDir: join(home, '.trae-cn/skills'),
277
+ detectInstalled: async () => existsSync(join(home, '.trae-cn')),
278
+ },
279
+ windsurf: {
280
+ name: 'windsurf',
281
+ displayName: 'Windsurf',
282
+ skillsDir: '.windsurf/skills',
283
+ globalSkillsDir: join(home, '.codeium/windsurf/skills'),
284
+ detectInstalled: async () => existsSync(join(home, '.codeium/windsurf')),
285
+ },
286
+ zencoder: {
287
+ name: 'zencoder',
288
+ displayName: 'Zencoder',
289
+ skillsDir: '.zencoder/skills',
290
+ globalSkillsDir: join(home, '.zencoder/skills'),
291
+ detectInstalled: async () => existsSync(join(home, '.zencoder')),
292
+ },
293
+ neovate: {
294
+ name: 'neovate',
295
+ displayName: 'Neovate',
296
+ skillsDir: '.neovate/skills',
297
+ globalSkillsDir: join(home, '.neovate/skills'),
298
+ detectInstalled: async () => existsSync(join(home, '.neovate')),
299
+ },
300
+ pochi: {
301
+ name: 'pochi',
302
+ displayName: 'Pochi',
303
+ skillsDir: '.pochi/skills',
304
+ globalSkillsDir: join(home, '.pochi/skills'),
305
+ detectInstalled: async () => existsSync(join(home, '.pochi')),
306
+ },
307
+ adal: {
308
+ name: 'adal',
309
+ displayName: 'AdaL',
310
+ skillsDir: '.adal/skills',
311
+ globalSkillsDir: join(home, '.adal/skills'),
312
+ detectInstalled: async () => existsSync(join(home, '.adal')),
313
+ },
314
+ hermes: {
315
+ name: 'hermes',
316
+ displayName: 'Hermes',
317
+ skillsDir: '.hermes/skills',
318
+ globalSkillsDir: join(home, '.hermes/skills'),
319
+ detectInstalled: async () => existsSync(join(home, '.hermes')),
320
+ },
321
+ };
322
+ /** Detect all agents installed on the machine */
323
+ export async function detectInstalledAgents() {
324
+ const results = await Promise.all(Object.entries(agents).map(async ([type, config]) => ({
325
+ type,
326
+ installed: await config.detectInstalled(),
327
+ })));
328
+ return results.filter((r) => r.installed).map((r) => r.type);
329
+ }
330
+ /** Agents that use the universal .agents/skills directory */
331
+ export function getUniversalAgents() {
332
+ return Object.entries(agents)
333
+ .filter(([, config]) => config.skillsDir === '.agents/skills' && config.showInUniversalList !== false)
334
+ .map(([type]) => type);
335
+ }
336
+ /** Agents that need their own skill directory */
337
+ export function getNonUniversalAgents() {
338
+ return Object.entries(agents)
339
+ .filter(([, config]) => config.skillsDir !== '.agents/skills')
340
+ .map(([type]) => type);
341
+ }
342
+ export function isUniversalAgent(type) {
343
+ return agents[type]?.skillsDir === '.agents/skills';
344
+ }
345
+ export function getAgentDisplayName(type) {
346
+ return agents[type]?.displayName || type;
347
+ }
package/dist/api.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const API_BASE = 'https://agentskill.sh/api';
2
- const VERSION = '1.0.8';
2
+ const VERSION = '2.0.0';
3
3
  export async function apiFetch(path, options) {
4
4
  const res = await fetch(`${API_BASE}${path}`, {
5
5
  ...options,
@@ -1,5 +1,6 @@
1
+ import pc from 'picocolors';
1
2
  import { apiFetch } from '../api.js';
2
- import { detectPlatform } from '../platform.js';
3
+ import { ORANGE } from '../ui.js';
3
4
  export async function feedbackCommand(args) {
4
5
  const jsonFlag = args.includes('--json');
5
6
  const filteredArgs = args.filter((a) => !a.startsWith('--'));
@@ -19,19 +20,16 @@ export async function feedbackCommand(args) {
19
20
  console.error('Score must be an integer between 1 and 5.');
20
21
  process.exit(1);
21
22
  }
22
- // Normalize slug: strip leading @ if present
23
23
  const cleanSlug = slug.startsWith('@') ? slug.slice(1) : slug;
24
- const platform = detectPlatform();
25
24
  const body = {
26
25
  score,
27
- platform,
28
- agentName: 'ags',
26
+ platform: 'claude-code',
27
+ agentName: 'agentskill-sh-cli',
29
28
  sessionId: `cli-${Date.now()}`,
30
29
  autoRated: false,
31
30
  };
32
31
  if (comment)
33
32
  body.comment = comment;
34
- // If slug contains owner prefix, split it for the API
35
33
  const apiSlug = cleanSlug.includes('/') ? cleanSlug.split('/').pop() : cleanSlug;
36
34
  const owner = cleanSlug.includes('/') ? cleanSlug.split('/')[0] : undefined;
37
35
  if (owner)
@@ -50,8 +48,10 @@ export async function feedbackCommand(args) {
50
48
  }, null, 2));
51
49
  return;
52
50
  }
53
- console.log(`\nFeedback submitted for "${cleanSlug}": ${score}/5`);
51
+ console.log();
52
+ console.log(pc.green('\u2713') + ` Feedback submitted for ${ORANGE(cleanSlug)}: ${pc.bold(`${score}/5`)}`);
54
53
  if (comment)
55
- console.log(`Comment: ${comment}`);
56
- console.log(`Community average: ${data.averageScore}/5 (${data.ratingCount} ratings)`);
54
+ console.log(` Comment: ${comment}`);
55
+ console.log(` Community average: ${pc.bold(`${data.averageScore}/5`)} (${data.ratingCount} ratings)`);
56
+ console.log();
57
57
  }
@@ -0,0 +1 @@
1
+ export declare function findCommand(args: string[]): Promise<void>;
@@ -0,0 +1,183 @@
1
+ import * as readline from 'readline';
2
+ import pc from 'picocolors';
3
+ import { apiFetch } from '../api.js';
4
+ import { truncate, ORANGE, DIM } from '../ui.js';
5
+ const DEBOUNCE_MS = 300;
6
+ const MAX_RESULTS = 10;
7
+ function scoreLabel(score) {
8
+ if (score == null)
9
+ return pc.dim('n/a');
10
+ if (score >= 70)
11
+ return pc.green(String(score));
12
+ if (score >= 30)
13
+ return pc.yellow(String(score));
14
+ return pc.red(String(score));
15
+ }
16
+ function renderResults(results, selectedIndex, query) {
17
+ const lines = [];
18
+ if (!results.length && query.length > 0) {
19
+ lines.push(pc.dim(' No results'));
20
+ return lines;
21
+ }
22
+ for (let i = 0; i < results.length; i++) {
23
+ const r = results[i];
24
+ const isSelected = i === selectedIndex;
25
+ const prefix = isSelected ? ORANGE('\u276f ') : ' ';
26
+ const name = isSelected ? pc.bold(ORANGE(r.name)) : r.name;
27
+ const owner = DIM(`@${r.owner}`);
28
+ const security = scoreLabel(r.securityScore);
29
+ const desc = truncate(r.description || '', 40);
30
+ lines.push(`${prefix}${name} ${owner} ${DIM('sec:')}${security} ${DIM(desc)}`);
31
+ }
32
+ return lines;
33
+ }
34
+ function clearLines(count) {
35
+ for (let i = 0; i < count; i++) {
36
+ process.stdout.write('\x1b[1A\x1b[2K');
37
+ }
38
+ }
39
+ export async function findCommand(args) {
40
+ // Non-interactive mode: just search and print
41
+ if (!process.stdin.isTTY) {
42
+ const query = args.filter((a) => !a.startsWith('--')).join(' ');
43
+ if (!query) {
44
+ console.error('Usage: ags find <query>');
45
+ process.exit(1);
46
+ }
47
+ const params = new URLSearchParams({ q: query, limit: String(MAX_RESULTS) });
48
+ const data = await apiFetch(`/agent/search?${params}`);
49
+ for (const r of data.results) {
50
+ console.log(`${r.slug}\t${r.owner}\t${r.securityScore ?? 'n/a'}\t${r.description || ''}`);
51
+ }
52
+ return;
53
+ }
54
+ // Interactive fzf-style search
55
+ const rl = readline.createInterface({
56
+ input: process.stdin,
57
+ output: process.stdout,
58
+ terminal: true,
59
+ });
60
+ // Enable raw mode for key-by-key input
61
+ if (process.stdin.setRawMode) {
62
+ process.stdin.setRawMode(true);
63
+ }
64
+ process.stdin.resume();
65
+ let query = args.filter((a) => !a.startsWith('--')).join(' ');
66
+ let results = [];
67
+ let selectedIndex = 0;
68
+ let lastRenderedLines = 0;
69
+ let debounceTimer = null;
70
+ let fetching = false;
71
+ function render() {
72
+ if (lastRenderedLines > 0) {
73
+ clearLines(lastRenderedLines);
74
+ }
75
+ const lines = [];
76
+ lines.push(`${ORANGE('\u276f')} ${pc.bold('Find a skill:')} ${query}${fetching ? DIM(' ...') : ''}`);
77
+ const resultLines = renderResults(results, selectedIndex, query);
78
+ lines.push(...resultLines);
79
+ if (results.length > 0) {
80
+ lines.push(DIM(' \u2191\u2193 navigate \u21b5 install esc quit'));
81
+ }
82
+ process.stdout.write(lines.join('\n') + '\n');
83
+ lastRenderedLines = lines.length;
84
+ }
85
+ async function search() {
86
+ if (!query.trim()) {
87
+ results = [];
88
+ selectedIndex = 0;
89
+ render();
90
+ return;
91
+ }
92
+ fetching = true;
93
+ render();
94
+ try {
95
+ const params = new URLSearchParams({ q: query, limit: String(MAX_RESULTS) });
96
+ const data = await apiFetch(`/agent/search?${params}`);
97
+ results = data.results;
98
+ selectedIndex = 0;
99
+ }
100
+ catch {
101
+ results = [];
102
+ }
103
+ fetching = false;
104
+ render();
105
+ }
106
+ function scheduleSearch() {
107
+ if (debounceTimer)
108
+ clearTimeout(debounceTimer);
109
+ debounceTimer = setTimeout(search, DEBOUNCE_MS);
110
+ }
111
+ // Initial render
112
+ render();
113
+ // If there's an initial query, search immediately
114
+ if (query) {
115
+ await search();
116
+ }
117
+ return new Promise((resolve) => {
118
+ process.stdin.on('data', async (data) => {
119
+ const key = data.toString();
120
+ // Escape or Ctrl+C: quit
121
+ if (key === '\x1b' || key === '\x03') {
122
+ if (lastRenderedLines > 0) {
123
+ clearLines(lastRenderedLines);
124
+ }
125
+ if (process.stdin.setRawMode) {
126
+ process.stdin.setRawMode(false);
127
+ }
128
+ rl.close();
129
+ resolve();
130
+ return;
131
+ }
132
+ // Enter: install selected
133
+ if (key === '\r' || key === '\n') {
134
+ if (results.length > 0 && results[selectedIndex]) {
135
+ const selected = results[selectedIndex];
136
+ if (lastRenderedLines > 0) {
137
+ clearLines(lastRenderedLines);
138
+ }
139
+ if (process.stdin.setRawMode) {
140
+ process.stdin.setRawMode(false);
141
+ }
142
+ rl.close();
143
+ console.log(`\nInstalling ${ORANGE(selected.name)}...\n`);
144
+ const { installCommand } = await import('./install.js');
145
+ await installCommand([selected.slug]);
146
+ resolve();
147
+ }
148
+ return;
149
+ }
150
+ // Up arrow
151
+ if (key === '\x1b[A') {
152
+ if (selectedIndex > 0) {
153
+ selectedIndex--;
154
+ render();
155
+ }
156
+ return;
157
+ }
158
+ // Down arrow
159
+ if (key === '\x1b[B') {
160
+ if (selectedIndex < results.length - 1) {
161
+ selectedIndex++;
162
+ render();
163
+ }
164
+ return;
165
+ }
166
+ // Backspace
167
+ if (key === '\x7f' || key === '\b') {
168
+ if (query.length > 0) {
169
+ query = query.slice(0, -1);
170
+ scheduleSearch();
171
+ render();
172
+ }
173
+ return;
174
+ }
175
+ // Regular character input
176
+ if (key.length === 1 && key >= ' ') {
177
+ query += key;
178
+ scheduleSearch();
179
+ render();
180
+ }
181
+ });
182
+ });
183
+ }
@@ -0,0 +1 @@
1
+ export declare function initCommand(args: string[]): Promise<void>;
@@ -0,0 +1,82 @@
1
+ import * as p from '@clack/prompts';
2
+ import pc from 'picocolors';
3
+ import { writeFileSync, existsSync } from 'fs';
4
+ import { join, basename } from 'path';
5
+ import { ORANGE } from '../ui.js';
6
+ export async function initCommand(args) {
7
+ const cwd = process.cwd();
8
+ const defaultName = basename(cwd);
9
+ // Get skill name from args or use directory name
10
+ let name = args.find((a) => !a.startsWith('--')) || '';
11
+ if (!name) {
12
+ const input = await p.text({
13
+ message: 'Skill name:',
14
+ placeholder: defaultName,
15
+ defaultValue: defaultName,
16
+ validate: (val) => {
17
+ if (!val || !val.trim())
18
+ return 'Skill name is required';
19
+ if (!/^[a-z0-9-]+$/.test(val.trim())) {
20
+ return 'Use lowercase letters, numbers, and hyphens only';
21
+ }
22
+ return undefined;
23
+ },
24
+ });
25
+ if (p.isCancel(input)) {
26
+ p.cancel('Cancelled.');
27
+ return;
28
+ }
29
+ name = input;
30
+ }
31
+ // Get description
32
+ const description = await p.text({
33
+ message: 'Description:',
34
+ placeholder: 'What does this skill do?',
35
+ validate: (val) => {
36
+ if (!val || !val.trim())
37
+ return 'Description is required';
38
+ return undefined;
39
+ },
40
+ });
41
+ if (p.isCancel(description)) {
42
+ p.cancel('Cancelled.');
43
+ return;
44
+ }
45
+ const skillMdPath = join(cwd, 'SKILL.md');
46
+ if (existsSync(skillMdPath)) {
47
+ const overwrite = await p.confirm({
48
+ message: 'SKILL.md already exists. Overwrite?',
49
+ initialValue: false,
50
+ });
51
+ if (p.isCancel(overwrite) || !overwrite) {
52
+ p.cancel('Cancelled.');
53
+ return;
54
+ }
55
+ }
56
+ const content = `---
57
+ name: ${name}
58
+ description: >
59
+ ${description.trim()}
60
+ metadata:
61
+ author: ""
62
+ version: "1.0"
63
+ compatibility: Requires Node.js 18+
64
+ ---
65
+
66
+ # ${name}
67
+
68
+ ${description.trim()}
69
+
70
+ ## Usage
71
+
72
+ <!-- Describe how an AI agent should use this skill -->
73
+
74
+ ## Examples
75
+
76
+ <!-- Provide examples of when and how to use this skill -->
77
+ `;
78
+ writeFileSync(skillMdPath, content, 'utf-8');
79
+ p.log.success(`Created ${ORANGE('SKILL.md')} for ${pc.bold(name)}`);
80
+ p.log.info(`Edit ${pc.dim(skillMdPath)} to customize your skill.`);
81
+ p.log.info(`Publish: ${pc.dim('Push to GitHub and submit at https://agentskill.sh/submit')}`);
82
+ }