dantelabs-agentic-school 1.0.0 → 1.2.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/.claude-plugin/marketplace.json +11 -53
- package/cli/bin/cli.js +31 -8
- package/cli/src/commands/info.js +15 -11
- package/cli/src/commands/install.js +53 -29
- package/cli/src/commands/list.js +28 -22
- package/cli/src/commands/uninstall.js +23 -16
- package/cli/src/i18n/index.js +96 -0
- package/cli/src/i18n/locales/en.js +107 -0
- package/cli/src/i18n/locales/ko.js +107 -0
- package/cli/src/lib/config.js +116 -1
- package/cli/src/lib/installer.js +106 -3
- package/package.json +1 -1
- package/plugins/brand-analytics/plugin.json +9 -0
- package/plugins/campaign-orchestration/plugin.json +9 -0
- package/plugins/common/plugin.json +9 -0
- package/plugins/common/skills/kie-image-generator/.env.example +4 -0
- package/plugins/common/skills/kie-image-generator/SKILL.md +281 -0
- package/plugins/common/skills/kie-image-generator/references/api_docs.md +358 -0
- package/plugins/common/skills/kie-image-generator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/generate_image.py +285 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__init__.py +19 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/__init__.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/flux_kontext.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/gpt4o.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/ideogram.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/imagen.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/nano_banana.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/nano_banana_edit.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/nano_banana_pro.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/seedream.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/__pycache__/seedream_edit.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/flux_kontext.py +36 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/gpt4o.py +36 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/ideogram.py +85 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/imagen.py +48 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/nano_banana.py +40 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/nano_banana_edit.py +55 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/nano_banana_pro.py +47 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/seedream.py +51 -0
- package/plugins/common/skills/kie-image-generator/scripts/models/seedream_edit.py +66 -0
- package/plugins/common/skills/kie-image-generator/scripts/utils.py +706 -0
- package/plugins/common/skills/kie-video-generator/SKILL.md +258 -0
- package/plugins/common/skills/kie-video-generator/references/api_docs.md +202 -0
- package/plugins/common/skills/kie-video-generator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
- package/plugins/common/skills/kie-video-generator/scripts/generate_video.py +356 -0
- package/plugins/common/skills/kie-video-generator/scripts/models/__init__.py +4 -0
- package/plugins/common/skills/kie-video-generator/scripts/utils.py +617 -0
- package/plugins/content-creation/plugin.json +9 -0
- package/plugins/creative-production/plugin.json +9 -0
- package/plugins/customer-segmentation/plugin.json +9 -0
- package/plugins/market-research/plugin.json +9 -0
- package/plugins/persona-builder/plugin.json +9 -0
- package/plugins/social-strategy/plugin.json +9 -0
|
@@ -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('
|
|
17
|
-
.option('-p, --path <path>',
|
|
18
|
-
.option('-y, --yes', '
|
|
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(
|
|
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('
|
|
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(
|
|
45
|
+
logger.error(t('uninstall.pluginNotFound', { name: pluginName }));
|
|
45
46
|
console.log();
|
|
46
|
-
console.log('
|
|
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(
|
|
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(`
|
|
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(`
|
|
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(`
|
|
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:
|
|
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('
|
|
90
|
+
logger.info(t('uninstall.uninstallCancelled'));
|
|
88
91
|
return;
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
// Uninstall
|
|
93
|
-
spinner.start(
|
|
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(
|
|
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
|
-
|
|
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
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* English translations (default)
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
// Common
|
|
6
|
+
common: {
|
|
7
|
+
plugins: 'plugins',
|
|
8
|
+
agents: 'agents',
|
|
9
|
+
commands: 'commands',
|
|
10
|
+
skills: 'skills',
|
|
11
|
+
version: 'version',
|
|
12
|
+
install: 'Install',
|
|
13
|
+
summary: 'Summary',
|
|
14
|
+
location: 'Location',
|
|
15
|
+
external: 'External',
|
|
16
|
+
availablePlugins: 'Available plugins',
|
|
17
|
+
yes: 'yes',
|
|
18
|
+
no: 'no'
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// CLI descriptions
|
|
22
|
+
cli: {
|
|
23
|
+
description: 'Dante Labs Agentic School - Claude Code Plugin Installer',
|
|
24
|
+
examples: 'Examples',
|
|
25
|
+
moreInfo: 'More info',
|
|
26
|
+
installAllPlugins: '# Install all plugins',
|
|
27
|
+
installSpecificPlugin: '# Install specific plugin',
|
|
28
|
+
installCustomPath: '# Install to custom path',
|
|
29
|
+
listPlugins: '# List available plugins',
|
|
30
|
+
showPluginInfo: '# Show plugin info'
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Install command
|
|
34
|
+
install: {
|
|
35
|
+
description: 'Install plugins to your project',
|
|
36
|
+
optionPath: 'Installation path (default: current directory)',
|
|
37
|
+
optionForce: 'Force overwrite existing files',
|
|
38
|
+
optionAll: 'Install all available plugins',
|
|
39
|
+
optionNoCommon: 'Skip common utilities',
|
|
40
|
+
optionDryRun: 'Show what would be installed without making changes',
|
|
41
|
+
installTarget: 'Installation target',
|
|
42
|
+
loadingRegistry: 'Loading plugin registry...',
|
|
43
|
+
registryLoaded: 'Plugin registry loaded',
|
|
44
|
+
confirmInstallAll: 'Install all {count} plugins?',
|
|
45
|
+
installCancelled: 'Installation cancelled',
|
|
46
|
+
pluginNotFound: "Plugin '{name}' not found",
|
|
47
|
+
dryRunTitle: 'Dry run - would install:',
|
|
48
|
+
dryRunFooter: 'Run without --dry-run to install.',
|
|
49
|
+
installing: 'Installing {name}...',
|
|
50
|
+
installingComponent: 'Installing {plugin}: {type} {name}',
|
|
51
|
+
installed: 'Installed {name}',
|
|
52
|
+
failedToInstall: 'Failed to install {name}: {error}',
|
|
53
|
+
successMessage: 'Successfully installed {count} plugin(s)',
|
|
54
|
+
componentSummary: '{agents} agents, {commands} commands, {skills} skills',
|
|
55
|
+
nextSteps: 'Next steps',
|
|
56
|
+
nextStep1: 'Run {command} to see available commands',
|
|
57
|
+
nextStep2: 'Try {command}',
|
|
58
|
+
externalSkillsRequired: 'External skills required'
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// List command
|
|
62
|
+
list: {
|
|
63
|
+
description: 'List all available plugins',
|
|
64
|
+
optionJson: 'Output as JSON',
|
|
65
|
+
optionVerbose: 'Show detailed information',
|
|
66
|
+
title: 'Dante Labs Agentic School - Available Plugins',
|
|
67
|
+
summaryText: '{plugins} plugins, {agents} agents, {commands} commands, {skills} skills',
|
|
68
|
+
installHint: 'Install: npx dantelabs-agentic-school install [plugin-name]'
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Info command
|
|
72
|
+
info: {
|
|
73
|
+
description: 'Show detailed information about a plugin',
|
|
74
|
+
optionJson: 'Output as JSON',
|
|
75
|
+
pluginNotFound: "Plugin '{name}' not found",
|
|
76
|
+
agents: 'Agents',
|
|
77
|
+
commands: 'Commands',
|
|
78
|
+
skills: 'Skills',
|
|
79
|
+
externalSkillsRequired: 'External Skills Required',
|
|
80
|
+
installHint: 'Install'
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Uninstall command
|
|
84
|
+
uninstall: {
|
|
85
|
+
description: 'Uninstall a plugin from your project',
|
|
86
|
+
optionPath: 'Project path (default: current directory)',
|
|
87
|
+
optionYes: 'Skip confirmation prompt',
|
|
88
|
+
noClaudeDir: 'No .claude directory found at {path}',
|
|
89
|
+
loadingRegistry: 'Loading plugin registry...',
|
|
90
|
+
pluginNotFound: "Plugin '{name}' not found in registry",
|
|
91
|
+
willRemove: 'Will remove {name}:',
|
|
92
|
+
confirmUninstall: 'Are you sure you want to uninstall {name}?',
|
|
93
|
+
uninstallCancelled: 'Uninstall cancelled',
|
|
94
|
+
uninstalling: 'Uninstalling {name}...',
|
|
95
|
+
uninstalled: 'Uninstalled {name}',
|
|
96
|
+
removedSummary: 'Removed: {agents} agents, {commands} commands, {skills} skills'
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// Logger
|
|
100
|
+
logger: {
|
|
101
|
+
info: 'info',
|
|
102
|
+
success: 'success',
|
|
103
|
+
warn: 'warn',
|
|
104
|
+
error: 'error',
|
|
105
|
+
debug: 'debug'
|
|
106
|
+
}
|
|
107
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Korean translations
|
|
3
|
+
*/
|
|
4
|
+
export default {
|
|
5
|
+
// Common
|
|
6
|
+
common: {
|
|
7
|
+
plugins: '플러그인',
|
|
8
|
+
agents: '에이전트',
|
|
9
|
+
commands: '명령어',
|
|
10
|
+
skills: '스킬',
|
|
11
|
+
version: '버전',
|
|
12
|
+
install: '설치',
|
|
13
|
+
summary: '요약',
|
|
14
|
+
location: '위치',
|
|
15
|
+
external: '외부',
|
|
16
|
+
availablePlugins: '사용 가능한 플러그인',
|
|
17
|
+
yes: '예',
|
|
18
|
+
no: '아니오'
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// CLI descriptions
|
|
22
|
+
cli: {
|
|
23
|
+
description: 'Dante Labs Agentic School - Claude Code 플러그인 설치 도구',
|
|
24
|
+
examples: '사용 예시',
|
|
25
|
+
moreInfo: '자세한 정보',
|
|
26
|
+
installAllPlugins: '# 전체 플러그인 설치',
|
|
27
|
+
installSpecificPlugin: '# 특정 플러그인 설치',
|
|
28
|
+
installCustomPath: '# 경로 지정 설치',
|
|
29
|
+
listPlugins: '# 플러그인 목록 조회',
|
|
30
|
+
showPluginInfo: '# 플러그인 상세 정보'
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Install command
|
|
34
|
+
install: {
|
|
35
|
+
description: '프로젝트에 플러그인 설치',
|
|
36
|
+
optionPath: '설치 경로 (기본: 현재 디렉토리)',
|
|
37
|
+
optionForce: '기존 파일 강제 덮어쓰기',
|
|
38
|
+
optionAll: '모든 플러그인 설치',
|
|
39
|
+
optionNoCommon: 'common 유틸리티 제외',
|
|
40
|
+
optionDryRun: '실제 설치 없이 미리보기',
|
|
41
|
+
installTarget: '설치 대상 경로',
|
|
42
|
+
loadingRegistry: '플러그인 목록 불러오는 중...',
|
|
43
|
+
registryLoaded: '플러그인 목록 로드 완료',
|
|
44
|
+
confirmInstallAll: '전체 {count}개 플러그인을 설치할까요?',
|
|
45
|
+
installCancelled: '설치가 취소되었습니다',
|
|
46
|
+
pluginNotFound: "플러그인 '{name}'을(를) 찾을 수 없습니다",
|
|
47
|
+
dryRunTitle: '미리보기 - 설치 예정:',
|
|
48
|
+
dryRunFooter: '--dry-run 없이 실행하면 설치됩니다.',
|
|
49
|
+
installing: '{name} 설치 중...',
|
|
50
|
+
installingComponent: '{plugin} 설치 중: {type} {name}',
|
|
51
|
+
installed: '{name} 설치 완료',
|
|
52
|
+
failedToInstall: '{name} 설치 실패: {error}',
|
|
53
|
+
successMessage: '{count}개 플러그인 설치 완료',
|
|
54
|
+
componentSummary: '에이전트 {agents}개, 명령어 {commands}개, 스킬 {skills}개',
|
|
55
|
+
nextSteps: '다음 단계',
|
|
56
|
+
nextStep1: '{command} 실행하여 사용 가능한 명령어 확인',
|
|
57
|
+
nextStep2: '{command} 명령어 사용해보기',
|
|
58
|
+
externalSkillsRequired: '필요한 외부 스킬'
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// List command
|
|
62
|
+
list: {
|
|
63
|
+
description: '사용 가능한 플러그인 목록 조회',
|
|
64
|
+
optionJson: 'JSON 형식으로 출력',
|
|
65
|
+
optionVerbose: '상세 정보 표시',
|
|
66
|
+
title: 'Dante Labs Agentic School - 플러그인 목록',
|
|
67
|
+
summaryText: '플러그인 {plugins}개, 에이전트 {agents}개, 명령어 {commands}개, 스킬 {skills}개',
|
|
68
|
+
installHint: '설치: npx dantelabs-agentic-school install [플러그인명]'
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Info command
|
|
72
|
+
info: {
|
|
73
|
+
description: '플러그인 상세 정보 조회',
|
|
74
|
+
optionJson: 'JSON 형식으로 출력',
|
|
75
|
+
pluginNotFound: "플러그인 '{name}'을(를) 찾을 수 없습니다",
|
|
76
|
+
agents: '에이전트',
|
|
77
|
+
commands: '명령어',
|
|
78
|
+
skills: '스킬',
|
|
79
|
+
externalSkillsRequired: '필요한 외부 스킬',
|
|
80
|
+
installHint: '설치 명령어'
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Uninstall command
|
|
84
|
+
uninstall: {
|
|
85
|
+
description: '프로젝트에서 플러그인 제거',
|
|
86
|
+
optionPath: '프로젝트 경로 (기본: 현재 디렉토리)',
|
|
87
|
+
optionYes: '확인 프롬프트 건너뛰기',
|
|
88
|
+
noClaudeDir: '{path}에 .claude 디렉토리가 없습니다',
|
|
89
|
+
loadingRegistry: '플러그인 목록 불러오는 중...',
|
|
90
|
+
pluginNotFound: "플러그인 '{name}'이(가) 레지스트리에 없습니다",
|
|
91
|
+
willRemove: '{name} 제거 예정:',
|
|
92
|
+
confirmUninstall: '{name}을(를) 정말 삭제하시겠습니까?',
|
|
93
|
+
uninstallCancelled: '삭제가 취소되었습니다',
|
|
94
|
+
uninstalling: '{name} 삭제 중...',
|
|
95
|
+
uninstalled: '{name} 삭제 완료',
|
|
96
|
+
removedSummary: '삭제됨: 에이전트 {agents}개, 명령어 {commands}개, 스킬 {skills}개'
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// Logger
|
|
100
|
+
logger: {
|
|
101
|
+
info: '정보',
|
|
102
|
+
success: '성공',
|
|
103
|
+
warn: '경고',
|
|
104
|
+
error: '오류',
|
|
105
|
+
debug: '디버그'
|
|
106
|
+
}
|
|
107
|
+
};
|
package/cli/src/lib/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
1
|
+
import { readFile, writeFile, readdir } from 'fs/promises';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { existsSync } from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
@@ -7,6 +7,9 @@ import { dirname } from 'path';
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = dirname(__filename);
|
|
9
9
|
|
|
10
|
+
// Package root directory
|
|
11
|
+
const PACKAGE_ROOT = join(__dirname, '../../..');
|
|
12
|
+
|
|
10
13
|
const CONFIG_CACHE_FILE = '.dantelabs-cache.json';
|
|
11
14
|
const CACHE_TTL = 1000 * 60 * 60; // 1 hour
|
|
12
15
|
|
|
@@ -124,6 +127,118 @@ async function saveCachedConfig(config) {
|
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Normalize source path (handle both 'path' and 'source' fields)
|
|
132
|
+
*/
|
|
133
|
+
export function getSourcePath(plugin) {
|
|
134
|
+
const source = plugin.source || plugin.path;
|
|
135
|
+
if (!source) return `plugins/${plugin.name}`;
|
|
136
|
+
return source.replace(/^\.\//, '');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Discover components by scanning local plugin directory
|
|
141
|
+
*/
|
|
142
|
+
async function discoverLocalComponents(pluginPath) {
|
|
143
|
+
const components = { agents: [], commands: [], skills: [] };
|
|
144
|
+
|
|
145
|
+
// Scan agents directory
|
|
146
|
+
const agentsDir = join(pluginPath, 'agents');
|
|
147
|
+
if (existsSync(agentsDir)) {
|
|
148
|
+
const files = await readdir(agentsDir);
|
|
149
|
+
components.agents = files
|
|
150
|
+
.filter(f => f.endsWith('.md'))
|
|
151
|
+
.map(f => f.replace('.md', ''));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Scan commands directory
|
|
155
|
+
const commandsDir = join(pluginPath, 'commands');
|
|
156
|
+
if (existsSync(commandsDir)) {
|
|
157
|
+
const files = await readdir(commandsDir);
|
|
158
|
+
components.commands = files
|
|
159
|
+
.filter(f => f.endsWith('.md'))
|
|
160
|
+
.map(f => f.replace('.md', ''));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Scan skills directory
|
|
164
|
+
const skillsDir = join(pluginPath, 'skills');
|
|
165
|
+
if (existsSync(skillsDir)) {
|
|
166
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
167
|
+
components.skills = entries
|
|
168
|
+
.filter(e => e.isDirectory())
|
|
169
|
+
.map(e => e.name);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return components;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Discover components from remote GitHub directory
|
|
177
|
+
*/
|
|
178
|
+
async function discoverRemoteComponents(remotePath) {
|
|
179
|
+
const components = { agents: [], commands: [], skills: [] };
|
|
180
|
+
|
|
181
|
+
const fetchDir = async (path) => {
|
|
182
|
+
const url = `${GITHUB_CONFIG.apiBase}/contents/${path}?ref=${GITHUB_CONFIG.branch}`;
|
|
183
|
+
const response = await fetch(url, {
|
|
184
|
+
headers: {
|
|
185
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
186
|
+
'User-Agent': 'dantelabs-agentic-school-cli'
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
if (!response.ok) return [];
|
|
190
|
+
return response.json();
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Scan agents directory
|
|
194
|
+
try {
|
|
195
|
+
const agentsContents = await fetchDir(`${remotePath}/agents`);
|
|
196
|
+
components.agents = agentsContents
|
|
197
|
+
.filter(f => f.type === 'file' && f.name.endsWith('.md'))
|
|
198
|
+
.map(f => f.name.replace('.md', ''));
|
|
199
|
+
} catch (e) { /* no agents dir */ }
|
|
200
|
+
|
|
201
|
+
// Scan commands directory
|
|
202
|
+
try {
|
|
203
|
+
const commandsContents = await fetchDir(`${remotePath}/commands`);
|
|
204
|
+
components.commands = commandsContents
|
|
205
|
+
.filter(f => f.type === 'file' && f.name.endsWith('.md'))
|
|
206
|
+
.map(f => f.name.replace('.md', ''));
|
|
207
|
+
} catch (e) { /* no commands dir */ }
|
|
208
|
+
|
|
209
|
+
// Scan skills directory
|
|
210
|
+
try {
|
|
211
|
+
const skillsContents = await fetchDir(`${remotePath}/skills`);
|
|
212
|
+
components.skills = skillsContents
|
|
213
|
+
.filter(f => f.type === 'dir')
|
|
214
|
+
.map(f => f.name);
|
|
215
|
+
} catch (e) { /* no skills dir */ }
|
|
216
|
+
|
|
217
|
+
return components;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Enrich plugin with discovered components
|
|
222
|
+
*/
|
|
223
|
+
export async function enrichPluginWithComponents(plugin) {
|
|
224
|
+
// If components already exist, return as is
|
|
225
|
+
if (plugin.components && Object.keys(plugin.components).length > 0) {
|
|
226
|
+
return plugin;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const sourcePath = getSourcePath(plugin);
|
|
230
|
+
const localPath = join(PACKAGE_ROOT, sourcePath);
|
|
231
|
+
|
|
232
|
+
let components;
|
|
233
|
+
if (existsSync(localPath)) {
|
|
234
|
+
components = await discoverLocalComponents(localPath);
|
|
235
|
+
} else {
|
|
236
|
+
components = await discoverRemoteComponents(sourcePath);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { ...plugin, components };
|
|
240
|
+
}
|
|
241
|
+
|
|
127
242
|
/**
|
|
128
243
|
* Validate plugin name
|
|
129
244
|
*/
|
package/cli/src/lib/installer.js
CHANGED
|
@@ -46,6 +46,87 @@ async function copyDirectory(src, dest) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Discover components by scanning plugin directory
|
|
51
|
+
*/
|
|
52
|
+
async function discoverComponents(localPath) {
|
|
53
|
+
const components = { agents: [], commands: [], skills: [] };
|
|
54
|
+
|
|
55
|
+
// Scan agents directory
|
|
56
|
+
const agentsDir = join(localPath, 'agents');
|
|
57
|
+
if (existsSync(agentsDir)) {
|
|
58
|
+
const files = await readdir(agentsDir);
|
|
59
|
+
components.agents = files
|
|
60
|
+
.filter(f => f.endsWith('.md'))
|
|
61
|
+
.map(f => f.replace('.md', ''));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Scan commands directory
|
|
65
|
+
const commandsDir = join(localPath, 'commands');
|
|
66
|
+
if (existsSync(commandsDir)) {
|
|
67
|
+
const files = await readdir(commandsDir);
|
|
68
|
+
components.commands = files
|
|
69
|
+
.filter(f => f.endsWith('.md'))
|
|
70
|
+
.map(f => f.replace('.md', ''));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Scan skills directory
|
|
74
|
+
const skillsDir = join(localPath, 'skills');
|
|
75
|
+
if (existsSync(skillsDir)) {
|
|
76
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
77
|
+
components.skills = entries
|
|
78
|
+
.filter(e => e.isDirectory())
|
|
79
|
+
.map(e => e.name);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return components;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Discover components from remote GitHub directory
|
|
87
|
+
*/
|
|
88
|
+
async function discoverRemoteComponents(remotePath) {
|
|
89
|
+
const { getDirectoryContents } = await import('./downloader.js');
|
|
90
|
+
const components = { agents: [], commands: [], skills: [] };
|
|
91
|
+
|
|
92
|
+
// Scan agents directory
|
|
93
|
+
try {
|
|
94
|
+
const agentsContents = await getDirectoryContents(`${remotePath}/agents`);
|
|
95
|
+
components.agents = agentsContents
|
|
96
|
+
.filter(f => f.type === 'file' && f.name.endsWith('.md'))
|
|
97
|
+
.map(f => f.name.replace('.md', ''));
|
|
98
|
+
} catch (e) { /* no agents dir */ }
|
|
99
|
+
|
|
100
|
+
// Scan commands directory
|
|
101
|
+
try {
|
|
102
|
+
const commandsContents = await getDirectoryContents(`${remotePath}/commands`);
|
|
103
|
+
components.commands = commandsContents
|
|
104
|
+
.filter(f => f.type === 'file' && f.name.endsWith('.md'))
|
|
105
|
+
.map(f => f.name.replace('.md', ''));
|
|
106
|
+
} catch (e) { /* no commands dir */ }
|
|
107
|
+
|
|
108
|
+
// Scan skills directory
|
|
109
|
+
try {
|
|
110
|
+
const skillsContents = await getDirectoryContents(`${remotePath}/skills`);
|
|
111
|
+
components.skills = skillsContents
|
|
112
|
+
.filter(f => f.type === 'dir')
|
|
113
|
+
.map(f => f.name);
|
|
114
|
+
} catch (e) { /* no skills dir */ }
|
|
115
|
+
|
|
116
|
+
return components;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Normalize source path (handle both 'path' and 'source' fields)
|
|
121
|
+
*/
|
|
122
|
+
function getSourcePath(plugin) {
|
|
123
|
+
// Support both old 'path' format and new 'source' format
|
|
124
|
+
const source = plugin.source || plugin.path;
|
|
125
|
+
if (!source) return `plugins/${plugin.name}`;
|
|
126
|
+
// Remove leading './' if present
|
|
127
|
+
return source.replace(/^\.\//, '');
|
|
128
|
+
}
|
|
129
|
+
|
|
49
130
|
/**
|
|
50
131
|
* Install a single plugin to .claude directory
|
|
51
132
|
*
|
|
@@ -64,13 +145,22 @@ async function copyDirectory(src, dest) {
|
|
|
64
145
|
export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
65
146
|
const { force = false, onProgress } = options;
|
|
66
147
|
const pluginName = plugin.name;
|
|
67
|
-
const
|
|
68
|
-
const sourcePath = plugin.path; // e.g., "plugins/brand-analytics"
|
|
148
|
+
const sourcePath = getSourcePath(plugin); // e.g., "plugins/brand-analytics"
|
|
69
149
|
|
|
70
150
|
// Determine if we should use local or remote source
|
|
71
151
|
const useLocalSource = hasLocalSource(sourcePath);
|
|
72
152
|
const localSourcePath = join(PACKAGE_ROOT, sourcePath);
|
|
73
153
|
|
|
154
|
+
// Discover components dynamically (or use provided components for backward compatibility)
|
|
155
|
+
let components = plugin.components;
|
|
156
|
+
if (!components || Object.keys(components).length === 0) {
|
|
157
|
+
if (useLocalSource) {
|
|
158
|
+
components = await discoverComponents(localSourcePath);
|
|
159
|
+
} else {
|
|
160
|
+
components = await discoverRemoteComponents(sourcePath);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
74
164
|
// Create base directories
|
|
75
165
|
await ensureDir(join(claudeDir, 'agents'));
|
|
76
166
|
await ensureDir(join(claudeDir, 'commands'));
|
|
@@ -194,7 +284,20 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
|
|
|
194
284
|
*/
|
|
195
285
|
export async function uninstallPlugin(plugin, claudeDir) {
|
|
196
286
|
const pluginName = plugin.name;
|
|
197
|
-
const
|
|
287
|
+
const sourcePath = getSourcePath(plugin);
|
|
288
|
+
|
|
289
|
+
// Discover components dynamically (or use provided components for backward compatibility)
|
|
290
|
+
let components = plugin.components;
|
|
291
|
+
if (!components || Object.keys(components).length === 0) {
|
|
292
|
+
const useLocalSource = hasLocalSource(sourcePath);
|
|
293
|
+
const localSourcePath = join(PACKAGE_ROOT, sourcePath);
|
|
294
|
+
|
|
295
|
+
if (useLocalSource) {
|
|
296
|
+
components = await discoverComponents(localSourcePath);
|
|
297
|
+
} else {
|
|
298
|
+
components = await discoverRemoteComponents(sourcePath);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
198
301
|
|
|
199
302
|
const results = {
|
|
200
303
|
plugin: pluginName,
|