@ariasbruno/skillbase 0.1.1 → 1.1.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/src/cli.js CHANGED
@@ -3,84 +3,129 @@ import {
3
3
  addSkill,
4
4
  checkUpdates,
5
5
  getGlobalSkillsDir,
6
+ getProjectSkillsDir,
6
7
  initProject,
7
8
  installFromManifest,
8
9
  installRemoteSkillRef,
9
10
  listGlobalSkills,
11
+ listProjectSkills,
10
12
  migrateAgentsSkillsToSkillbase,
11
13
  readManifest,
12
14
  removeSkill,
13
15
  updateSkills
14
16
  } from './core.js';
17
+ import {
18
+ bold,
19
+ dim,
20
+ dim2,
21
+ text,
22
+ grayLogo,
23
+ cyan,
24
+ yellow,
25
+ green,
26
+ red,
27
+ S_DIAMOND,
28
+ S_BULLET,
29
+ S_CHECK,
30
+ S_POINTER,
31
+ S_WARNING,
32
+ S_BAR,
33
+ S_BAR_START,
34
+ S_BAR_END,
35
+ S_STEP
36
+ } from './styles.js';
37
+ import { createRequire } from 'node:module';
38
+ import { t, setLanguage } from './i18n.js';
39
+ import { getConfig, saveConfig } from './config.js';
40
+
41
+ const require = createRequire(import.meta.url);
42
+ const pkg = require('../package.json');
43
+
44
+ function printLogo() {
45
+ const LOGO_LINES = [
46
+ ' ███████╗██╗ ██╗██╗██╗ ██╗ ██████╗ █████╗ ███████╗███████╗',
47
+ ' ██╔════╝██║ ██╔╝██║██║ ██║ ██╔══██╗██╔══██╗██╔════╝██╔════╝',
48
+ ' ███████╗█████╔╝ ██║██║ ██║ ██████╔╝███████║███████╗█████╗ ',
49
+ ' ╚════██║██╔═██╗ ██║██║ ██║ ██╔══██╗██╔══██║╚════██║██╔══╝ ',
50
+ ' ███████║██║ ██╗██║███████╗███████╗██████╔╝██║ ██║███████║███████╗',
51
+ ' ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝',
52
+ ];
53
+
54
+ console.log();
55
+ LOGO_LINES.forEach((line, i) => {
56
+ console.log(grayLogo(line, i));
57
+ });
58
+ console.log(` ${dim2(pkg.description)} ${dim2(`v${pkg.version}`)}`);
59
+ console.log();
60
+ }
15
61
 
62
+ /**
63
+ * Muestra la ayuda principal de la CLI con el estilo Clack.
64
+ */
16
65
  function printHelp() {
17
- console.log(`skillbase - Gestor de skills locales/remotas
18
-
19
- Uso:
20
- skillbase -h
21
- skillbase h
22
- skillbase ls
23
- skillbase l
24
- skillbase init [--hard]
25
- skillbase add <skill> [--sym]
26
- skillbase add
27
- skillbase a <skill> [-s]
28
- skillbase install
29
- skillbase i
30
- skillbase install <skill|repo-url> --remote [--skill <name>] [--force]
31
- skillbase remove <skill> [--g]
32
- skillbase rm <skill> [--g]
33
- skillbase check [--remote]
34
- skillbase c [-r]
35
- skillbase update [<skill>] [--remote] [--force]
36
- skillbase up [<skill>] [-r] [-f]
37
- skillbase migrate [--project] [--force]
38
- skillbase m [--project] [-f]
39
-
40
- Descripción breve de comandos:
41
- ls Lista skills instaladas globalmente en ~/.skillbase/skills.
42
- init Sugiere skills globales según tecnologías del proyecto.
43
- add Instala una skill global (o abre selector interactivo sin argumentos).
44
- install Instala desde skillbase.json o una skill remota con --remote.
45
- remove Elimina una skill del proyecto o global con --g.
46
- check Revisa si hay versiones más nuevas disponibles.
47
- update Actualiza skills del manifiesto (todas o una puntual).
48
- migrate Migra skills desde .agents hacia ~/skillbase/skills.
49
-
50
- Aliases:
51
- l=ls, h=-h, a=add, i=install, rm=remove, c=check, up=update, m=migrate
52
-
53
- Short flags:
54
- -s=--sym, -r=--remote, -f=--force, -k=--skill, -p=--project
55
-
56
- Notas:
57
- - Carpeta global por defecto: ~/.skillbase/skills
58
- - Puedes cambiarla con SKILLBASE_HOME
59
- - Las skills de proyecto viven en .agents/skills
60
- - El manifiesto del proyecto es skillbase.json
61
- `);
66
+ printLogo();
67
+ console.log(`${cyan(S_BAR_START)} ${bold(t('USAGE'))}`);
68
+ console.log(`${cyan(S_BAR)} ${green('skillbase')} ${text('<command> [args] [options]')}`);
69
+ console.log(`${cyan(S_BAR)}`);
70
+
71
+ console.log(`${cyan(S_BAR)} ${bold(t('COMMANDS'))}`);
72
+ const commands = [
73
+ ['ls', `[args]`, t('DESC_LS')],
74
+ ['init', ``, t('DESC_INIT')],
75
+ ['add', `<skill>`, t('DESC_ADD')],
76
+ ['install', `[url]`, t('DESC_INSTALL')],
77
+ ['remove', `<skill>`, t('DESC_REMOVE')],
78
+ ['check', ``, t('DESC_CHECK')],
79
+ ['update', `[skill]`, t('DESC_UPDATE')],
80
+ ['migrate', ``, t('DESC_MIGRATE')],
81
+ ['lang', `<lang>`, t('DESC_LANG')],
82
+ ];
83
+
84
+ commands.forEach(([cmd, args, desc]) => {
85
+ const fullCmd = `${bold(cmd.padEnd(8))} ${dim2(args.padEnd(8))}`;
86
+ console.log(`${cyan(S_BAR)} ${fullCmd} ${text(desc)}`);
87
+ });
88
+
89
+ console.log(`${cyan(S_BAR)}`);
90
+ console.log(`${cyan(S_BAR)} ${bold(t('OPTIONS'))}`);
91
+ const options = [
92
+ ['-r, --remote ', t('OPT_REMOTE')],
93
+ ['-f, --force ', t('OPT_FORCE')],
94
+ ['-s, --sym ', t('OPT_SYM')],
95
+ ['-g, --global ', t('OPT_GLOBAL')],
96
+ ['-h, --help ', t('OPT_HELP')],
97
+ ];
98
+
99
+ options.forEach(([opt, desc]) => {
100
+ console.log(`${cyan(S_BAR)} ${dim2(opt.padEnd(14))} ${dim2(desc)}`);
101
+ });
102
+
103
+ console.log(`${cyan(S_BAR)}`);
104
+ console.log(`${cyan(S_BAR_END)} ${dim2(t('SHORTCUTS'))}\n`);
62
105
  }
63
106
 
107
+ /**
108
+ * Muestra una lista resumida de comandos cuando no se indica ninguno.
109
+ */
64
110
  function printCommandList() {
65
- console.log(`Comandos disponibles:
66
- - skillbase ls
67
- - skillbase l
68
- - skillbase init [--hard]
69
- - skillbase add
70
- - skillbase add <skill> [--sym]
71
- - skillbase a <skill> [-s]
72
- - skillbase install | skillbase i
73
- - skillbase install <skill|repo-url> --remote [--skill <name>] [--force]
74
- - skillbase remove <skill> [--g]
75
- - skillbase rm <skill> [--g]
76
- - skillbase check [--remote]
77
- - skillbase c [-r]
78
- - skillbase update [<skill>] [--remote] [--force]
79
- - skillbase up [<skill>] [-r] [-f]
80
- - skillbase migrate [--project] [--force]
81
- - skillbase m [--project] [-f]
82
-
83
- Usa "skillbase -h" para ver detalles.`);
111
+ printLogo();
112
+ console.log(`${cyan(S_BAR_START)} ${bold(t('COMMANDS_AVAILABLE'))}`);
113
+
114
+ const quickList = [
115
+ ['ls', t('DESC_LS_SHORT')],
116
+ ['init', t('DESC_INIT_SHORT')],
117
+ ['add', t('DESC_ADD_SHORT')],
118
+ ['install', t('DESC_INSTALL_SHORT')],
119
+ ['migrate', t('DESC_MIGRATE_SHORT')],
120
+ ['lang', t('DESC_LANG_SHORT')],
121
+ ];
122
+
123
+ quickList.forEach(([cmd, desc]) => {
124
+ console.log(`${cyan(S_BAR)} ${bold(cmd.padEnd(12))} ${dim2(desc)}`);
125
+ });
126
+
127
+ console.log(`${cyan(S_BAR)}`);
128
+ console.log(`${cyan(S_BAR_END)} ${dim2(t('USE_HELP', { cmd: yellow('skillbase --help') }))}\n`);
84
129
  }
85
130
 
86
131
  function hasFlag(args, ...flags) {
@@ -94,6 +139,9 @@ function getFlagValue(args, ...flags) {
94
139
  return null;
95
140
  }
96
141
 
142
+ /**
143
+ * Punto de entrada principal de la CLI.
144
+ */
97
145
  export async function runCLI(argv) {
98
146
  const args = argv.slice(2);
99
147
  const [rawCommand, maybeSkill] = args;
@@ -114,60 +162,89 @@ export async function runCLI(argv) {
114
162
  return;
115
163
  }
116
164
 
117
- if (command === '-h' || command === '--help') {
165
+ if (command === '-h' || command === '--help' || hasFlag(args, '-h', '--help')) {
118
166
  printHelp();
119
167
  return;
120
168
  }
121
169
 
122
170
  switch (command) {
123
171
  case 'ls': {
124
- const skills = await listGlobalSkills();
125
- if (!skills.length) {
126
- console.log(`No hay skills globales instaladas en ${getGlobalSkillsDir()}`);
172
+ const showGlobal = hasFlag(args, '--global', '-g');
173
+ if (showGlobal) {
174
+ const skills = await listGlobalSkills();
175
+ if (!skills.length) {
176
+ console.log(`${yellow(S_WARNING)} ${t('LS_GLOBAL_EMPTY', { dir: dim2(getGlobalSkillsDir()) })}`);
177
+ } else {
178
+ console.log(`${cyan(S_BAR_START)} ${bold(t('LS_GLOBAL_TITLE', { dir: dim2(getGlobalSkillsDir()) }))}`);
179
+ skills.forEach((skill) => console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${text(skill)}`));
180
+ console.log(`${cyan(S_BAR_END)}\n`);
181
+ }
127
182
  } else {
128
- console.log(`Skills globales (${getGlobalSkillsDir()}):`);
129
- skills.forEach((skill) => console.log(`- ${skill}`));
183
+ const skills = await listProjectSkills();
184
+ if (!skills.length) {
185
+ console.log(`${yellow(S_WARNING)} ${t('LS_PROJECT_EMPTY', { dir: dim2(getProjectSkillsDir()) })}`);
186
+ } else {
187
+ console.log(`${cyan(S_BAR_START)} ${bold(t('LS_PROJECT_TITLE', { dir: dim2(getProjectSkillsDir()) }))}`);
188
+ skills.forEach((skill) => console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${text(skill)}`));
189
+ console.log(`${cyan(S_BAR_END)}\n`);
190
+ }
130
191
  }
131
192
  return;
132
193
  }
133
194
 
134
195
  case 'init': {
196
+ printLogo();
135
197
  const result = await initProject({ hard: hasFlag(args, '--hard') });
136
- if (!result.technologies.length) {
137
- console.log('No se detectaron tecnologías en el proyecto.');
138
- return;
139
- }
140
- console.log(`Tecnologías detectadas: ${result.technologies.join(', ')}`);
141
- if (!result.suggested.length) {
142
- console.log('No se encontraron skills compatibles en ~/.skillbase/skills.');
198
+
199
+ if (result.cancelled) {
200
+ console.log(`${cyan(S_BAR_START)} ${dim2(t('CANCELLED'))}`);
201
+ console.log(`${cyan(S_BAR_END)}`);
143
202
  return;
144
203
  }
145
- console.log(`Skills sugeridas: ${result.suggested.join(', ')}`);
146
- if (result.cancelled) {
147
- console.log('Selección cancelada.');
204
+
205
+ if (!result.technologies.length) {
206
+ console.log(`${yellow(S_WARNING)} ${t('INIT_NO_STACK')}`);
148
207
  return;
149
208
  }
209
+
210
+ console.log(`${cyan(S_BAR_START)} ${bold(t('INIT_ANALYSIS'))}`);
211
+ result.technologies.forEach((tech) => {
212
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${text(tech)}`);
213
+ });
214
+ console.log(`${cyan(S_BAR)}`);
215
+
150
216
  if (result.installed.length) {
151
- console.log(`Skills instaladas: ${result.installed.join(', ')}`);
217
+ console.log(`${cyan(S_BAR_START)} ${bold(t('INIT_RESUMEN'))}`);
218
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('INIT_INSTALLED', { list: bold(result.installed.join(', ')) })}`);
219
+ console.log(`${cyan(S_BAR_END)}\n`);
152
220
  } else {
153
- console.log('No se seleccionaron skills para instalar.');
221
+ console.log(`${cyan(S_BAR_END)} ${dim2(t('INIT_NONE'))}\n`);
154
222
  }
155
223
  return;
156
224
  }
157
225
 
158
226
  case 'add': {
159
227
  if (!maybeSkill) {
228
+ printLogo();
160
229
  const result = await addSkillsInteractive({ sym: hasFlag(args, '--sym', '-s') });
161
- if (result.cancelled) return;
230
+ if (result.cancelled) {
231
+ console.log(`${cyan(S_BAR_START)} ${dim2(t('SELECTION_CANCELLED'))}`);
232
+ console.log(`${cyan(S_BAR_END)}`);
233
+ return;
234
+ }
162
235
  if (!result.selected.length) {
163
- console.log('No se seleccionaron skills para instalar.');
236
+ console.log(`${cyan(S_BAR_END)} ${dim2(t('ADD_NO_SELECTED'))}\n`);
164
237
  return;
165
238
  }
166
- console.log(`Skills instaladas: ${result.selected.join(', ')}`);
239
+ console.log(`${cyan(S_BAR_START)} ${bold(t('ADD_RESUMEN'))}`);
240
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('INIT_INSTALLED', { list: bold(result.selected.join(', ')) })}`);
241
+ console.log(`${cyan(S_BAR_END)}\n`);
167
242
  return;
168
243
  }
169
244
  await addSkill(maybeSkill, { sym: hasFlag(args, '--sym', '-s') });
170
- console.log(`Skill "${maybeSkill}" instalada en el proyecto.`);
245
+ console.log(`${cyan(S_BAR_START)} ${bold(t('ADD_RESUMEN'))}`);
246
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('ADD_SUCCESS', { skill: bold(maybeSkill) })}`);
247
+ console.log(`${cyan(S_BAR_END)}\n`);
171
248
  return;
172
249
  }
173
250
 
@@ -177,77 +254,117 @@ export async function runCLI(argv) {
177
254
  force: hasFlag(args, '--force', '-f'),
178
255
  skill: getFlagValue(args, '--skill', '-k')
179
256
  });
180
- console.log(`Skill remota "${maybeSkill}" instalada.`);
257
+ console.log(`${cyan(S_BAR_START)} ${bold(t('INIT_RESUMEN'))}`);
258
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('ADD_REMOTE_SUCCESS', { skill: bold(maybeSkill) })}`);
259
+ console.log(`${cyan(S_BAR_END)}\n`);
181
260
  return;
182
261
  }
183
262
  if (maybeSkill && !hasFlag(args, '--remote', '-r')) {
184
- throw new Error('Para instalar una skill puntual usa "skillbase add <skill>" (global) o "skillbase install <skill|repo-url> --remote".');
263
+ throw new Error(t('INSTALL_ERROR_SINGLE'));
185
264
  }
186
265
  await installFromManifest({ remote: hasFlag(args, '--remote', '-r'), force: hasFlag(args, '--force', '-f') });
187
266
  const manifest = await readManifest();
188
- console.log(`Instaladas ${manifest.skills.length} skills desde skillbase.json.`);
267
+ console.log(`${cyan(S_BAR_START)} ${bold(t('INIT_RESUMEN'))}`);
268
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('INSTALL_MANIFEST_SUCCESS', { count: bold(manifest.skills.length) })}`);
269
+ console.log(`${cyan(S_BAR_END)}\n`);
189
270
  return;
190
271
  }
191
272
 
192
273
  case 'remove': {
193
- if (!maybeSkill) throw new Error('Debes indicar una skill: skillbase remove <skill>');
194
- await removeSkill(maybeSkill, { global: hasFlag(args, '--g') });
195
- if (hasFlag(args, '--g')) {
196
- console.log(`Skill global "${maybeSkill}" eliminada.`);
274
+ if (!maybeSkill) throw new Error(t('REMOVE_REQUIRED'));
275
+ const isGlobal = hasFlag(args, '-g', '--global');
276
+ await removeSkill(maybeSkill, { global: isGlobal });
277
+ console.log(`${cyan(S_BAR_START)} ${bold(t('INIT_RESUMEN'))}`);
278
+ if (isGlobal) {
279
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('REMOVE_GLOBAL_SUCCESS', { skill: bold(maybeSkill) })}`);
197
280
  } else {
198
- console.log(`Skill "${maybeSkill}" eliminada del proyecto.`);
281
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('REMOVE_PROJECT_SUCCESS', { skill: bold(maybeSkill) })}`);
199
282
  }
283
+ console.log(`${cyan(S_BAR_END)}\n`);
200
284
  return;
201
285
  }
202
286
 
203
287
  case 'check': {
288
+ console.log(`${cyan(S_BAR_START)} ${dim2(t('CHECK_SEARCHING'))}`);
204
289
  const updates = await checkUpdates({ remoteOnly: hasFlag(args, '--remote', '-r') });
205
290
  if (!updates.length) {
206
- console.log('No hay actualizaciones disponibles.');
291
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('CHECK_UP_TO_DATE')}`);
292
+ console.log(`${cyan(S_BAR_END)}\n`);
207
293
  return;
208
294
  }
209
- console.log('Actualizaciones disponibles:');
295
+ console.log(`${cyan(S_BAR)} ${bold(t('CHECK_UPDATES_FOUND'))}`);
210
296
  updates.forEach((item) => {
211
- console.log(`- ${item.name}: ${item.current ?? 'desconocida'} -> ${item.latest} (${item.source})`);
297
+ console.log(`${cyan(S_BAR)} ${yellow(S_POINTER)} ${item.name}: ${dim2(item.current ?? '?')} ${dim2(S_STEP)} ${green(item.latest)} ${dim2(`(${item.source})`)}`);
212
298
  });
299
+ console.log(`${cyan(S_BAR)}`);
300
+ console.log(`${cyan(S_BAR_END)} ${dim2(t('CHECK_UPDATE_HINT', { cmd: yellow('skillbase update') }))}\n`);
213
301
  return;
214
302
  }
215
303
 
216
304
  case 'update': {
217
305
  const skill = maybeSkill && !maybeSkill.startsWith('-') ? maybeSkill : null;
306
+ console.log(`${cyan(S_BAR_START)} ${dim2(t('UPDATE_START'))}`);
218
307
  await updateSkills({
219
308
  skillName: skill,
220
309
  remoteOnly: hasFlag(args, '--remote', '-remote', '-r'),
221
310
  force: hasFlag(args, '--force', '-f')
222
311
  });
312
+ console.log(`${cyan(S_BAR)} ${bold(t('INIT_RESUMEN'))}`);
223
313
  if (skill) {
224
- console.log(`Skill "${skill}" actualizada.`);
314
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('UPDATE_SINGLE_SUCCESS', { skill: bold(skill) })}`);
225
315
  } else {
226
- console.log('Skills actualizadas.');
316
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('UPDATE_SUCCESS')}`);
227
317
  }
318
+ console.log(`${cyan(S_BAR_END)}\n`);
228
319
  return;
229
320
  }
230
321
 
231
322
  case 'migrate': {
232
- const fromProject = hasFlag(args, '--project', '-p');
323
+ const fromProject = hasFlag(args, '--promote', '-p');
233
324
  const result = await migrateAgentsSkillsToSkillbase({
234
325
  force: hasFlag(args, '--force', '-f'),
235
326
  fromProject
236
327
  });
237
- console.log(`Origen de migración: ${result.sourceRoot}/skills`);
238
- console.log(`Skills encontradas: ${result.totalFound}`);
328
+ console.log(`${cyan(S_BAR_START)} ${bold(t('MIGRATE_TITLE'))}`);
329
+ console.log(`${cyan(S_BAR)} ${dim2(S_POINTER)} ${t('MIGRATE_SOURCE', { dir: dim2(`${result.sourceRoot}/skills`) })}`);
330
+ console.log(`${cyan(S_BAR)} ${dim2(S_POINTER)} ${t('MIGRATE_FOUND', { count: bold(result.totalFound) })}`);
331
+
239
332
  if (result.migrated.length) {
240
- console.log(`Migradas a ${getGlobalSkillsDir()}:`);
241
- result.migrated.forEach((skill) => console.log(`- ${skill}`));
333
+ console.log(`${cyan(S_BAR)}`);
334
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('MIGRATE_SUCCESS_LIST', { dir: dim2(getGlobalSkillsDir()) })}`);
335
+ result.migrated.forEach((skill) => console.log(`${cyan(S_BAR)} ${dim2('·')} ${text(skill)}`));
242
336
  }
243
337
  if (result.skipped.length) {
244
- console.log('Omitidas (ya existen globalmente, usa --force para sobrescribir):');
245
- result.skipped.forEach((skill) => console.log(`- ${skill}`));
338
+ console.log(`${cyan(S_BAR)}`);
339
+ console.log(`${cyan(S_BAR)} ${yellow(S_POINTER)} ${t('MIGRATE_SKIPPED_LIST')}`);
340
+ result.skipped.forEach((skill) => console.log(`${cyan(S_BAR)} ${dim2('·')} ${text(skill)}`));
341
+ }
342
+
343
+ if (result.migrated.length) {
344
+ console.log(`${cyan(S_BAR)}`);
345
+ console.log(`${cyan(S_BAR)} ${bold(t('INIT_RESUMEN'))}`);
346
+ console.log(`${cyan(S_BAR)} ${green(S_CHECK)} ${t('MIGRATE_RESUMEN_SUCCESS', { count: bold(result.migrated.length) })}`);
347
+ console.log(`${cyan(S_BAR)} ${dim2(S_POINTER)} ${t('MIGRATE_RESUMEN_DEST', { dir: bold(getGlobalSkillsDir()) })}`);
348
+ console.log(`${cyan(S_BAR_END)}\n`);
349
+ } else {
350
+ console.log(`${cyan(S_BAR_END)} ${dim2(t('MIGRATE_NONE'))}\n`);
351
+ }
352
+ return;
353
+ }
354
+
355
+ case 'lang': {
356
+ if (!maybeSkill || !['en', 'es'].includes(maybeSkill)) {
357
+ throw new Error(t('LANG_INVALID', { lang: maybeSkill || '??' }));
246
358
  }
359
+ const config = await getConfig();
360
+ config.lang = maybeSkill;
361
+ saveConfig(config);
362
+ console.log(`${cyan(S_BAR_START)} ${bold(t('LANG_SUCCESS', { lang: maybeSkill === 'es' ? 'Español' : 'English' }))}`);
363
+ console.log(`${cyan(S_BAR_END)}\n`);
247
364
  return;
248
365
  }
249
366
 
250
367
  default:
251
- throw new Error(`Comando desconocido: ${command}. Usa skillbase -h`);
368
+ throw new Error(t('UNKNOWN_COMMAND', { cmd: command }));
252
369
  }
253
370
  }
package/src/config.js CHANGED
@@ -1,3 +1,5 @@
1
+ import fs from 'node:fs';
2
+ import fsp from 'node:fs/promises';
1
3
  import os from 'node:os';
2
4
  import path from 'node:path';
3
5
 
@@ -5,6 +7,16 @@ export const MANIFEST_FILE = 'skillbase.json';
5
7
  export const PROJECT_AGENTS_DIR = '.agents';
6
8
  export const PROJECT_SKILLS_DIR = 'skills';
7
9
  export const DEFAULT_GLOBAL_ROOTNAME = '.skillbase';
10
+ export const CONFIG_FILE = 'config.json';
11
+
12
+ export async function exists(target) {
13
+ try {
14
+ await fsp.access(target);
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
8
20
 
9
21
  export function getGlobalRootDir() {
10
22
  const fromEnv = process.env.SKILLBASE_HOME;
@@ -16,6 +28,31 @@ export function getGlobalSkillsDir() {
16
28
  return path.join(getGlobalRootDir(), PROJECT_SKILLS_DIR);
17
29
  }
18
30
 
31
+ export function getGlobalConfigPath() {
32
+ return path.join(getGlobalRootDir(), CONFIG_FILE);
33
+ }
34
+
35
+ export function getConfig() {
36
+ const configPath = getGlobalConfigPath();
37
+ try {
38
+ if (fs.existsSync(configPath)) {
39
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
40
+ }
41
+ } catch {
42
+ // Omitir errores de lectura
43
+ }
44
+ return { lang: 'en' };
45
+ }
46
+
47
+ export function saveConfig(config) {
48
+ const configPath = getGlobalConfigPath();
49
+ const rootDir = getGlobalRootDir();
50
+ if (!fs.existsSync(rootDir)) {
51
+ fs.mkdirSync(rootDir, { recursive: true });
52
+ }
53
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
54
+ }
55
+
19
56
  export function getProjectRoot(cwd = process.cwd()) {
20
57
  return cwd;
21
58
  }