dantelabs-agentic-school 1.0.0 → 1.1.1

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 (42) hide show
  1. package/.claude-plugin/marketplace.json +4 -5
  2. package/cli/bin/cli.js +31 -8
  3. package/cli/src/commands/info.js +10 -9
  4. package/cli/src/commands/install.js +53 -29
  5. package/cli/src/commands/list.js +17 -16
  6. package/cli/src/commands/uninstall.js +23 -16
  7. package/cli/src/i18n/index.js +96 -0
  8. package/cli/src/i18n/locales/en.js +107 -0
  9. package/cli/src/i18n/locales/ko.js +107 -0
  10. package/package.json +1 -1
  11. package/plugins/common/skills/kie-image-generator/.env.example +4 -0
  12. package/plugins/common/skills/kie-image-generator/SKILL.md +281 -0
  13. package/plugins/common/skills/kie-image-generator/references/api_docs.md +358 -0
  14. package/plugins/common/skills/kie-image-generator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  15. package/plugins/common/skills/kie-image-generator/scripts/generate_image.py +285 -0
  16. package/plugins/common/skills/kie-image-generator/scripts/models/__init__.py +19 -0
  17. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/__init__.cpython-313.pyc +0 -0
  18. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/flux_kontext.cpython-313.pyc +0 -0
  19. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/gpt4o.cpython-313.pyc +0 -0
  20. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/ideogram.cpython-313.pyc +0 -0
  21. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/imagen.cpython-313.pyc +0 -0
  22. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/nano_banana.cpython-313.pyc +0 -0
  23. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/nano_banana_edit.cpython-313.pyc +0 -0
  24. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/nano_banana_pro.cpython-313.pyc +0 -0
  25. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/seedream.cpython-313.pyc +0 -0
  26. package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/seedream_edit.cpython-313.pyc +0 -0
  27. package/plugins/common/skills/kie-image-generator/scripts/models/flux_kontext.py +36 -0
  28. package/plugins/common/skills/kie-image-generator/scripts/models/gpt4o.py +36 -0
  29. package/plugins/common/skills/kie-image-generator/scripts/models/ideogram.py +85 -0
  30. package/plugins/common/skills/kie-image-generator/scripts/models/imagen.py +48 -0
  31. package/plugins/common/skills/kie-image-generator/scripts/models/nano_banana.py +40 -0
  32. package/plugins/common/skills/kie-image-generator/scripts/models/nano_banana_edit.py +55 -0
  33. package/plugins/common/skills/kie-image-generator/scripts/models/nano_banana_pro.py +47 -0
  34. package/plugins/common/skills/kie-image-generator/scripts/models/seedream.py +51 -0
  35. package/plugins/common/skills/kie-image-generator/scripts/models/seedream_edit.py +66 -0
  36. package/plugins/common/skills/kie-image-generator/scripts/utils.py +706 -0
  37. package/plugins/common/skills/kie-video-generator/SKILL.md +258 -0
  38. package/plugins/common/skills/kie-video-generator/references/api_docs.md +202 -0
  39. package/plugins/common/skills/kie-video-generator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  40. package/plugins/common/skills/kie-video-generator/scripts/generate_video.py +356 -0
  41. package/plugins/common/skills/kie-video-generator/scripts/models/__init__.py +4 -0
  42. package/plugins/common/skills/kie-video-generator/scripts/utils.py +617 -0
@@ -14,11 +14,11 @@
14
14
  "plugins": [
15
15
  {
16
16
  "name": "common",
17
- "description": "공통 유틸리티 스킬 모음. 인증 관리, 문서 제작 도구(PPTX, PDF, DOCX) 등 여러 플러그인에서 공통으로 사용하는 기능을 제공합니다.",
17
+ "description": "공통 유틸리티 스킬 모음. 인증 관리, 문서 제작 도구(PPTX, PDF, DOCX), 이미지/비디오 생성(Kie.ai) 등 여러 플러그인에서 공통으로 사용하는 기능을 제공합니다.",
18
18
  "version": "1.0.0",
19
19
  "path": "plugins/common",
20
20
  "components": {
21
- "skills": ["auth-manager", "pptx", "pdf", "docx"]
21
+ "skills": ["auth-manager", "pptx", "pdf", "docx", "kie-image-generator", "kie-video-generator"]
22
22
  }
23
23
  },
24
24
  {
@@ -78,15 +78,14 @@
78
78
  },
79
79
  {
80
80
  "name": "creative-production",
81
- "description": "실제 이미지와 비디오를 생성합니다. kie-image-generator, kie-video-generator 스킬과 연동됩니다.",
81
+ "description": "실제 이미지와 비디오를 생성합니다. common 플러그인의 kie-image-generator, kie-video-generator 스킬을 활용합니다.",
82
82
  "version": "1.0.0",
83
83
  "path": "plugins/creative-production",
84
84
  "components": {
85
85
  "agents": ["creative-director", "production-coordinator"],
86
86
  "commands": ["create-image", "create-video"],
87
87
  "skills": ["image-prompt-guide", "video-production"]
88
- },
89
- "externalSkills": ["kie-image-generator", "kie-video-generator"]
88
+ }
90
89
  },
91
90
  {
92
91
  "name": "campaign-orchestration",
package/cli/bin/cli.js CHANGED
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';
6
6
  import { dirname, join } from 'path';
7
7
  import { readFileSync } from 'fs';
8
8
 
9
+ import { setLocale, t, getAvailableLocales } from '../src/i18n/index.js';
9
10
  import installCommand from '../src/commands/install.js';
10
11
  import listCommand from '../src/commands/list.js';
11
12
  import infoCommand from '../src/commands/info.js';
@@ -19,30 +20,52 @@ const pkg = JSON.parse(
19
20
  readFileSync(join(__dirname, '../../package.json'), 'utf8')
20
21
  );
21
22
 
23
+ // Pre-parse --lang option before commander
24
+ const langIndex = process.argv.findIndex(
25
+ (arg) => arg === '--lang' || arg === '-l'
26
+ );
27
+ if (langIndex !== -1 && process.argv[langIndex + 1]) {
28
+ setLocale(process.argv[langIndex + 1]);
29
+ }
30
+
22
31
  const program = new Command();
23
32
 
24
33
  program
25
34
  .name('dantelabs')
26
- .description('Dante Labs Agentic School - Claude Code Plugin Installer')
35
+ .description(t('cli.description'))
27
36
  .version(pkg.version)
37
+ .option(
38
+ '-l, --lang <locale>',
39
+ `Language (${getAvailableLocales().join(', ')})`,
40
+ 'en'
41
+ )
42
+ .hook('preAction', (thisCommand) => {
43
+ const opts = thisCommand.opts();
44
+ if (opts.lang) {
45
+ setLocale(opts.lang);
46
+ }
47
+ })
28
48
  .addHelpText('after', `
29
- ${chalk.bold('Examples:')}
30
- ${chalk.gray('# Install all plugins')}
49
+ ${chalk.bold(t('cli.examples'))}
50
+ ${chalk.gray(t('cli.installAllPlugins'))}
31
51
  $ npx dantelabs-agentic-school install
32
52
 
33
- ${chalk.gray('# Install specific plugin')}
53
+ ${chalk.gray(t('cli.installSpecificPlugin'))}
34
54
  $ npx dantelabs-agentic-school install brand-analytics
35
55
 
36
- ${chalk.gray('# Install to custom path')}
56
+ ${chalk.gray(t('cli.installCustomPath'))}
37
57
  $ npx dantelabs-agentic-school install --path ./my-project
38
58
 
39
- ${chalk.gray('# List available plugins')}
59
+ ${chalk.gray(t('cli.listPlugins'))}
40
60
  $ npx dantelabs-agentic-school list
41
61
 
42
- ${chalk.gray('# Show plugin info')}
62
+ ${chalk.gray(t('cli.showPluginInfo'))}
43
63
  $ npx dantelabs-agentic-school info brand-analytics
44
64
 
45
- ${chalk.bold('More info:')} https://github.com/dandacompany/dantelabs-agentic-school
65
+ ${chalk.gray('# Korean language')}
66
+ $ npx dantelabs-agentic-school --lang ko list
67
+
68
+ ${chalk.bold(t('cli.moreInfo'))}: https://github.com/dandacompany/dantelabs-agentic-school
46
69
  `);
47
70
 
48
71
  // Register commands
@@ -1,21 +1,22 @@
1
1
  import chalk from 'chalk';
2
2
  import { getMarketplaceConfig } from '../lib/config.js';
3
3
  import logger from '../utils/logger.js';
4
+ import { t } from '../i18n/index.js';
4
5
 
5
6
  export default function infoCommand(program) {
6
7
  program
7
8
  .command('info <plugin>')
8
- .description('Show detailed information about a plugin')
9
- .option('--json', 'Output as JSON')
9
+ .description(t('info.description'))
10
+ .option('--json', t('info.optionJson'))
10
11
  .action(async (pluginName, options) => {
11
12
  try {
12
13
  const config = await getMarketplaceConfig();
13
14
  const plugin = config.plugins.find((p) => p.name === pluginName);
14
15
 
15
16
  if (!plugin) {
16
- logger.error(`Plugin '${pluginName}' not found`);
17
+ logger.error(t('info.pluginNotFound', { name: pluginName }));
17
18
  console.log();
18
- console.log('Available plugins:');
19
+ console.log(`${t('common.availablePlugins')}:`);
19
20
  config.plugins.forEach((p) => {
20
21
  console.log(` - ${chalk.cyan(p.name)}`);
21
22
  });
@@ -40,31 +41,31 @@ export default function infoCommand(program) {
40
41
  const components = plugin.components || {};
41
42
 
42
43
  if (components.agents?.length) {
43
- console.log(chalk.bold('Agents:'));
44
+ console.log(chalk.bold(`${t('info.agents')}:`));
44
45
  components.agents.forEach((a) => console.log(` - ${a}`));
45
46
  console.log();
46
47
  }
47
48
 
48
49
  if (components.commands?.length) {
49
- console.log(chalk.bold('Commands:'));
50
+ console.log(chalk.bold(`${t('info.commands')}:`));
50
51
  components.commands.forEach((c) => console.log(` - /${c}`));
51
52
  console.log();
52
53
  }
53
54
 
54
55
  if (components.skills?.length) {
55
- console.log(chalk.bold('Skills:'));
56
+ console.log(chalk.bold(`${t('info.skills')}:`));
56
57
  components.skills.forEach((s) => console.log(` - ${s}`));
57
58
  console.log();
58
59
  }
59
60
 
60
61
  if (plugin.externalSkills?.length) {
61
- console.log(chalk.bold.yellow('External Skills Required:'));
62
+ console.log(chalk.bold.yellow(`${t('info.externalSkillsRequired')}:`));
62
63
  plugin.externalSkills.forEach((s) => console.log(` - ${s}`));
63
64
  console.log();
64
65
  }
65
66
 
66
67
  console.log(
67
- chalk.gray('Install:'),
68
+ chalk.gray(`${t('info.installHint')}:`),
68
69
  `npx dantelabs-agentic-school install ${pluginName}`
69
70
  );
70
71
  } catch (error) {
@@ -9,17 +9,18 @@ import { getMarketplaceConfig, getPluginDependencies } from '../lib/config.js';
9
9
  import { installPlugin } from '../lib/installer.js';
10
10
  import logger from '../utils/logger.js';
11
11
  import { resolvePath } from '../utils/fs-utils.js';
12
+ import { t } from '../i18n/index.js';
12
13
 
13
14
  export default function installCommand(program) {
14
15
  program
15
16
  .command('install [plugin]')
16
17
  .alias('i')
17
- .description('Install plugins to your project')
18
- .option('-p, --path <path>', 'Installation path (default: current directory)')
19
- .option('-f, --force', 'Force overwrite existing files')
20
- .option('--all', 'Install all available plugins')
21
- .option('--no-common', 'Skip common utilities')
22
- .option('--dry-run', 'Show what would be installed without making changes')
18
+ .description(t('install.description'))
19
+ .option('-p, --path <path>', t('install.optionPath'))
20
+ .option('-f, --force', t('install.optionForce'))
21
+ .option('--all', t('install.optionAll'))
22
+ .option('--no-common', t('install.optionNoCommon'))
23
+ .option('--dry-run', t('install.optionDryRun'))
23
24
  .action(async (pluginName, options) => {
24
25
  const spinner = ora();
25
26
 
@@ -30,12 +31,12 @@ export default function installCommand(program) {
30
31
  : process.cwd();
31
32
  const claudeDir = join(targetPath, '.claude');
32
33
 
33
- logger.info(`Installation target: ${chalk.cyan(claudeDir)}`);
34
+ logger.info(`${t('install.installTarget')}: ${chalk.cyan(claudeDir)}`);
34
35
 
35
36
  // Load marketplace config
36
- spinner.start('Loading plugin registry...');
37
+ spinner.start(t('install.loadingRegistry'));
37
38
  const config = await getMarketplaceConfig();
38
- spinner.succeed('Plugin registry loaded');
39
+ spinner.succeed(t('install.registryLoaded'));
39
40
 
40
41
  // Determine which plugins to install
41
42
  let pluginsToInstall = [];
@@ -46,13 +47,15 @@ export default function installCommand(program) {
46
47
  {
47
48
  type: 'confirm',
48
49
  name: 'confirm',
49
- message: `Install all ${config.plugins.length} plugins?`,
50
+ message: t('install.confirmInstallAll', {
51
+ count: config.plugins.length
52
+ }),
50
53
  default: true
51
54
  }
52
55
  ]);
53
56
 
54
57
  if (!confirm) {
55
- logger.info('Installation cancelled');
58
+ logger.info(t('install.installCancelled'));
56
59
  return;
57
60
  }
58
61
 
@@ -62,9 +65,9 @@ export default function installCommand(program) {
62
65
  const plugin = config.plugins.find((p) => p.name === pluginName);
63
66
 
64
67
  if (!plugin) {
65
- logger.error(`Plugin '${pluginName}' not found`);
68
+ logger.error(t('install.pluginNotFound', { name: pluginName }));
66
69
  console.log();
67
- console.log('Available plugins:');
70
+ console.log(`${t('common.availablePlugins')}:`);
68
71
  config.plugins.forEach((p) => {
69
72
  console.log(` - ${chalk.cyan(p.name)}: ${p.description}`);
70
73
  });
@@ -89,7 +92,7 @@ export default function installCommand(program) {
89
92
  // Dry run mode
90
93
  if (options.dryRun) {
91
94
  console.log();
92
- logger.info('Dry run - would install:');
95
+ logger.info(t('install.dryRunTitle'));
93
96
  console.log();
94
97
 
95
98
  for (const plugin of pluginsToInstall) {
@@ -98,23 +101,29 @@ export default function installCommand(program) {
98
101
 
99
102
  if (components.agents?.length) {
100
103
  console.log(
101
- chalk.gray(` Agents: ${components.agents.join(', ')}`)
104
+ chalk.gray(
105
+ ` ${t('common.agents')}: ${components.agents.join(', ')}`
106
+ )
102
107
  );
103
108
  }
104
109
  if (components.commands?.length) {
105
110
  console.log(
106
- chalk.gray(` Commands: /${components.commands.join(', /')}`)
111
+ chalk.gray(
112
+ ` ${t('common.commands')}: /${components.commands.join(', /')}`
113
+ )
107
114
  );
108
115
  }
109
116
  if (components.skills?.length) {
110
117
  console.log(
111
- chalk.gray(` Skills: ${components.skills.join(', ')}`)
118
+ chalk.gray(
119
+ ` ${t('common.skills')}: ${components.skills.join(', ')}`
120
+ )
112
121
  );
113
122
  }
114
123
  console.log();
115
124
  }
116
125
 
117
- console.log(chalk.gray('Run without --dry-run to install.'));
126
+ console.log(chalk.gray(t('install.dryRunFooter')));
118
127
  return;
119
128
  }
120
129
 
@@ -129,13 +138,17 @@ export default function installCommand(program) {
129
138
  let totalSkills = 0;
130
139
 
131
140
  for (const plugin of pluginsToInstall) {
132
- spinner.start(`Installing ${chalk.cyan(plugin.name)}...`);
141
+ spinner.start(t('install.installing', { name: chalk.cyan(plugin.name) }));
133
142
 
134
143
  try {
135
144
  const results = await installPlugin(plugin, claudeDir, {
136
145
  force: options.force,
137
146
  onProgress: (type, name) => {
138
- spinner.text = `Installing ${chalk.cyan(plugin.name)}: ${type} ${name}`;
147
+ spinner.text = t('install.installingComponent', {
148
+ plugin: chalk.cyan(plugin.name),
149
+ type,
150
+ name
151
+ });
139
152
  }
140
153
  });
141
154
 
@@ -143,9 +156,14 @@ export default function installCommand(program) {
143
156
  totalCommands += results.commands;
144
157
  totalSkills += results.skills;
145
158
 
146
- spinner.succeed(`Installed ${chalk.cyan(plugin.name)}`);
159
+ spinner.succeed(t('install.installed', { name: chalk.cyan(plugin.name) }));
147
160
  } catch (err) {
148
- spinner.fail(`Failed to install ${plugin.name}: ${err.message}`);
161
+ spinner.fail(
162
+ t('install.failedToInstall', {
163
+ name: plugin.name,
164
+ error: err.message
165
+ })
166
+ );
149
167
  if (!options.force) {
150
168
  throw err;
151
169
  }
@@ -155,21 +173,25 @@ export default function installCommand(program) {
155
173
  // Summary
156
174
  console.log();
157
175
  logger.success(
158
- `Successfully installed ${pluginsToInstall.length} plugin(s)`
176
+ t('install.successMessage', { count: pluginsToInstall.length })
159
177
  );
160
178
  console.log(
161
179
  chalk.gray(
162
- ` ${totalAgents} agents, ${totalCommands} commands, ${totalSkills} skills`
180
+ ` ${t('install.componentSummary', {
181
+ agents: totalAgents,
182
+ commands: totalCommands,
183
+ skills: totalSkills
184
+ })}`
163
185
  )
164
186
  );
165
187
  console.log();
166
- console.log(`Location: ${chalk.cyan(claudeDir)}`);
188
+ console.log(`${t('common.location')}: ${chalk.cyan(claudeDir)}`);
167
189
 
168
190
  // Show next steps
169
191
  console.log();
170
- console.log(chalk.bold('Next steps:'));
192
+ console.log(chalk.bold(`${t('install.nextSteps')}:`));
171
193
  console.log(
172
- ` 1. Run ${chalk.cyan('claude --help')} to see available commands`
194
+ ` 1. ${t('install.nextStep1', { command: chalk.cyan('claude --help') })}`
173
195
  );
174
196
 
175
197
  // Show example command based on installed plugins
@@ -178,7 +200,9 @@ export default function installCommand(program) {
178
200
  );
179
201
  if (hasAnalyzeBrand) {
180
202
  console.log(
181
- ` 2. Try ${chalk.cyan('/analyze-brand --brand-doc ./your-brand.md')}`
203
+ ` 2. ${t('install.nextStep2', {
204
+ command: chalk.cyan('/analyze-brand --brand-doc ./your-brand.md')
205
+ })}`
182
206
  );
183
207
  }
184
208
 
@@ -189,7 +213,7 @@ export default function installCommand(program) {
189
213
 
190
214
  if (externalSkills.length > 0) {
191
215
  console.log();
192
- console.log(chalk.yellow('External skills required:'));
216
+ console.log(chalk.yellow(`${t('install.externalSkillsRequired')}:`));
193
217
  [...new Set(externalSkills)].forEach((skill) => {
194
218
  console.log(` - ${skill}`);
195
219
  });
@@ -1,14 +1,15 @@
1
1
  import chalk from 'chalk';
2
2
  import { getMarketplaceConfig } from '../lib/config.js';
3
3
  import logger from '../utils/logger.js';
4
+ import { t } from '../i18n/index.js';
4
5
 
5
6
  export default function listCommand(program) {
6
7
  program
7
8
  .command('list')
8
9
  .alias('ls')
9
- .description('List all available plugins')
10
- .option('--json', 'Output as JSON')
11
- .option('-v, --verbose', 'Show detailed information')
10
+ .description(t('list.description'))
11
+ .option('--json', t('list.optionJson'))
12
+ .option('-v, --verbose', t('list.optionVerbose'))
12
13
  .action(async (options) => {
13
14
  try {
14
15
  const config = await getMarketplaceConfig();
@@ -19,7 +20,7 @@ export default function listCommand(program) {
19
20
  }
20
21
 
21
22
  console.log();
22
- console.log(chalk.bold.blue('Dante Labs Agentic School - Available Plugins'));
23
+ console.log(chalk.bold.blue(t('list.title')));
23
24
  console.log(chalk.gray('━'.repeat(60)));
24
25
  console.log();
25
26
 
@@ -34,22 +35,22 @@ export default function listCommand(program) {
34
35
  const components = plugin.components || {};
35
36
  if (components.agents?.length) {
36
37
  console.log(
37
- chalk.gray(` Agents: ${components.agents.join(', ')}`)
38
+ chalk.gray(` ${t('common.agents')}: ${components.agents.join(', ')}`)
38
39
  );
39
40
  }
40
41
  if (components.commands?.length) {
41
42
  console.log(
42
- chalk.gray(` Commands: /${components.commands.join(', /')}`)
43
+ chalk.gray(` ${t('common.commands')}: /${components.commands.join(', /')}`)
43
44
  );
44
45
  }
45
46
  if (components.skills?.length) {
46
47
  console.log(
47
- chalk.gray(` Skills: ${components.skills.join(', ')}`)
48
+ chalk.gray(` ${t('common.skills')}: ${components.skills.join(', ')}`)
48
49
  );
49
50
  }
50
51
  if (plugin.externalSkills?.length) {
51
52
  console.log(
52
- chalk.yellow(` External: ${plugin.externalSkills.join(', ')}`)
53
+ chalk.yellow(` ${t('common.external')}: ${plugin.externalSkills.join(', ')}`)
53
54
  );
54
55
  }
55
56
  }
@@ -73,16 +74,16 @@ export default function listCommand(program) {
73
74
 
74
75
  console.log(chalk.gray('━'.repeat(60)));
75
76
  console.log(
76
- chalk.bold('Summary:'),
77
- `${config.plugins.length} plugins,`,
78
- `${totalAgents} agents,`,
79
- `${totalCommands} commands,`,
80
- `${totalSkills} skills`
77
+ chalk.bold(`${t('common.summary')}:`),
78
+ t('list.summaryText', {
79
+ plugins: config.plugins.length,
80
+ agents: totalAgents,
81
+ commands: totalCommands,
82
+ skills: totalSkills
83
+ })
81
84
  );
82
85
  console.log();
83
- console.log(
84
- chalk.gray(`Install: npx dantelabs-agentic-school install [plugin-name]`)
85
- );
86
+ console.log(chalk.gray(t('list.installHint')));
86
87
  } catch (error) {
87
88
  logger.error(error.message);
88
89
  process.exit(1);
@@ -8,14 +8,15 @@ import { getMarketplaceConfig } from '../lib/config.js';
8
8
  import { uninstallPlugin } from '../lib/installer.js';
9
9
  import logger from '../utils/logger.js';
10
10
  import { resolvePath } from '../utils/fs-utils.js';
11
+ import { t } from '../i18n/index.js';
11
12
 
12
13
  export default function uninstallCommand(program) {
13
14
  program
14
15
  .command('uninstall <plugin>')
15
16
  .alias('rm')
16
- .description('Uninstall a plugin from your project')
17
- .option('-p, --path <path>', 'Project path (default: current directory)')
18
- .option('-y, --yes', 'Skip confirmation prompt')
17
+ .description(t('uninstall.description'))
18
+ .option('-p, --path <path>', t('uninstall.optionPath'))
19
+ .option('-y, --yes', t('uninstall.optionYes'))
19
20
  .action(async (pluginName, options) => {
20
21
  const spinner = ora();
21
22
 
@@ -28,12 +29,12 @@ export default function uninstallCommand(program) {
28
29
 
29
30
  // Check if .claude directory exists
30
31
  if (!existsSync(claudeDir)) {
31
- logger.error(`No .claude directory found at ${targetPath}`);
32
+ logger.error(t('uninstall.noClaudeDir', { path: targetPath }));
32
33
  process.exit(1);
33
34
  }
34
35
 
35
36
  // Load marketplace config to get plugin info
36
- spinner.start('Loading plugin registry...');
37
+ spinner.start(t('uninstall.loadingRegistry'));
37
38
  const config = await getMarketplaceConfig();
38
39
  spinner.stop();
39
40
 
@@ -41,9 +42,9 @@ export default function uninstallCommand(program) {
41
42
  const plugin = config.plugins.find((p) => p.name === pluginName);
42
43
 
43
44
  if (!plugin) {
44
- logger.error(`Plugin '${pluginName}' not found in registry`);
45
+ logger.error(t('uninstall.pluginNotFound', { name: pluginName }));
45
46
  console.log();
46
- console.log('Available plugins:');
47
+ console.log(`${t('common.availablePlugins')}:`);
47
48
  config.plugins.forEach((p) => {
48
49
  console.log(` - ${chalk.cyan(p.name)}`);
49
50
  });
@@ -52,22 +53,24 @@ export default function uninstallCommand(program) {
52
53
 
53
54
  // Show what will be removed
54
55
  console.log();
55
- console.log(chalk.bold(`Will remove ${chalk.cyan(plugin.name)}:`));
56
+ console.log(
57
+ chalk.bold(t('uninstall.willRemove', { name: chalk.cyan(plugin.name) }))
58
+ );
56
59
  const components = plugin.components || {};
57
60
 
58
61
  if (components.agents?.length) {
59
62
  console.log(
60
- chalk.gray(` Agents: ${components.agents.join(', ')}`)
63
+ chalk.gray(` ${t('common.agents')}: ${components.agents.join(', ')}`)
61
64
  );
62
65
  }
63
66
  if (components.commands?.length) {
64
67
  console.log(
65
- chalk.gray(` Commands: /${components.commands.join(', /')}`)
68
+ chalk.gray(` ${t('common.commands')}: /${components.commands.join(', /')}`)
66
69
  );
67
70
  }
68
71
  if (components.skills?.length) {
69
72
  console.log(
70
- chalk.gray(` Skills: ${components.skills.join(', ')}`)
73
+ chalk.gray(` ${t('common.skills')}: ${components.skills.join(', ')}`)
71
74
  );
72
75
  }
73
76
  console.log();
@@ -78,29 +81,33 @@ export default function uninstallCommand(program) {
78
81
  {
79
82
  type: 'confirm',
80
83
  name: 'confirm',
81
- message: `Are you sure you want to uninstall ${plugin.name}?`,
84
+ message: t('uninstall.confirmUninstall', { name: plugin.name }),
82
85
  default: false
83
86
  }
84
87
  ]);
85
88
 
86
89
  if (!confirm) {
87
- logger.info('Uninstall cancelled');
90
+ logger.info(t('uninstall.uninstallCancelled'));
88
91
  return;
89
92
  }
90
93
  }
91
94
 
92
95
  // Uninstall
93
- spinner.start(`Uninstalling ${chalk.cyan(plugin.name)}...`);
96
+ spinner.start(t('uninstall.uninstalling', { name: chalk.cyan(plugin.name) }));
94
97
 
95
98
  const results = await uninstallPlugin(plugin, claudeDir);
96
99
 
97
- spinner.succeed(`Uninstalled ${chalk.cyan(plugin.name)}`);
100
+ spinner.succeed(t('uninstall.uninstalled', { name: chalk.cyan(plugin.name) }));
98
101
 
99
102
  // Summary
100
103
  console.log();
101
104
  console.log(
102
105
  chalk.gray(
103
- `Removed: ${results.agents} agents, ${results.commands} commands, ${results.skills} skills`
106
+ t('uninstall.removedSummary', {
107
+ agents: results.agents,
108
+ commands: results.commands,
109
+ skills: results.skills
110
+ })
104
111
  )
105
112
  );
106
113
  } catch (error) {
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Internationalization (i18n) module
3
+ * Default language: English (en)
4
+ * Supported languages: en, ko
5
+ */
6
+
7
+ import en from './locales/en.js';
8
+ import ko from './locales/ko.js';
9
+
10
+ const locales = { en, ko };
11
+
12
+ // Default language
13
+ let currentLocale = 'en';
14
+
15
+ /**
16
+ * Set the current locale
17
+ * @param {string} locale - Locale code (en, ko)
18
+ */
19
+ export function setLocale(locale) {
20
+ if (locales[locale]) {
21
+ currentLocale = locale;
22
+ } else {
23
+ console.warn(`Locale '${locale}' not supported. Using 'en' as default.`);
24
+ currentLocale = 'en';
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Get the current locale
30
+ * @returns {string} Current locale code
31
+ */
32
+ export function getLocale() {
33
+ return currentLocale;
34
+ }
35
+
36
+ /**
37
+ * Get available locales
38
+ * @returns {string[]} Array of locale codes
39
+ */
40
+ export function getAvailableLocales() {
41
+ return Object.keys(locales);
42
+ }
43
+
44
+ /**
45
+ * Get translation for a key path
46
+ * @param {string} keyPath - Dot-separated key path (e.g., 'install.description')
47
+ * @param {object} params - Parameters to interpolate
48
+ * @returns {string} Translated string
49
+ */
50
+ export function t(keyPath, params = {}) {
51
+ const keys = keyPath.split('.');
52
+ let value = locales[currentLocale];
53
+
54
+ for (const key of keys) {
55
+ if (value && typeof value === 'object' && key in value) {
56
+ value = value[key];
57
+ } else {
58
+ // Fallback to English
59
+ value = locales.en;
60
+ for (const k of keys) {
61
+ if (value && typeof value === 'object' && k in value) {
62
+ value = value[k];
63
+ } else {
64
+ return keyPath; // Return key if not found
65
+ }
66
+ }
67
+ break;
68
+ }
69
+ }
70
+
71
+ if (typeof value !== 'string') {
72
+ return keyPath;
73
+ }
74
+
75
+ // Interpolate parameters: {name} -> value
76
+ return value.replace(/\{(\w+)\}/g, (_, key) => {
77
+ return params[key] !== undefined ? params[key] : `{${key}}`;
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Get all translations for a section
83
+ * @param {string} section - Section name (e.g., 'install', 'list')
84
+ * @returns {object} Section translations
85
+ */
86
+ export function getSection(section) {
87
+ return locales[currentLocale][section] || locales.en[section] || {};
88
+ }
89
+
90
+ export default {
91
+ setLocale,
92
+ getLocale,
93
+ getAvailableLocales,
94
+ t,
95
+ getSection
96
+ };