dantelabs-agentic-school 1.4.0 → 1.5.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.
@@ -137,12 +137,20 @@
137
137
  "version": "1.0.0",
138
138
  "source": "./plugins/data-science/model-deployment",
139
139
  "category": "data-science"
140
+ },
141
+ {
142
+ "name": "gcp-openclaw",
143
+ "description": "GCP VM 인스턴스에 OpenClaw를 배포하는 가이드 및 자동화 도구",
144
+ "version": "1.0.0",
145
+ "source": "./plugins/gcp/gcp-openclaw",
146
+ "category": "gcp"
140
147
  }
141
148
  ],
142
149
  "categories": [
143
150
  "common",
144
151
  "marketing",
145
- "data-science"
152
+ "data-science",
153
+ "gcp"
146
154
  ],
147
155
  "tags": [
148
156
  "agentic-business",
@@ -165,6 +173,9 @@
165
173
  "model-monitoring",
166
174
  "model-deployment",
167
175
  "imbalance-handling",
168
- "workflow-automation"
176
+ "workflow-automation",
177
+ "gcp",
178
+ "openclaw",
179
+ "deployment"
169
180
  ]
170
181
  }
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  이 프로젝트는 **GTM Agents 스타일의 전략 에이전트**와 **실제 생성/분석 도구**를 통합하여, **엔드투엔드 비즈니스 자동화**를 제공합니다.
8
8
 
9
- 현재 **마케팅 자동화**(8개) **데이터 사이언스**(9개) 플러그인이 구현되어 있으며, 향후 **일반 비즈니스** 등으로 확장될 예정입니다.
9
+ 현재 **마케팅 자동화**(8개), **데이터 사이언스**(9개), **GCP 클라우드**(1개) 플러그인이 구현되어 있으며, 향후 **일반 비즈니스** 등으로 확장될 예정입니다.
10
10
 
11
11
  ## 지원 영역
12
12
 
@@ -14,6 +14,7 @@
14
14
  | --- | --- | --- | --- |
15
15
  | 🎯 Marketing | ✅ 구현완료 | 브랜드 분석 → 크리에이티브 제작 | 8개 |
16
16
  | 📊 Data Science | ✅ 구현완료 | 데이터 분석 → 모델 학습 → 배포 | 9개 |
17
+ | ☁️ GCP | ✅ 구현완료 | GCP VM 배포 가이드 및 자동화 | 1개 |
17
18
  | 💼 Business Ops | 🔜 예정 | 워크플로우 → 자동화 → 리포팅 | - |
18
19
 
19
20
  ## 특징
@@ -54,6 +55,7 @@ npx dantelabs-agentic-school uninstall brand-analytics
54
55
  | 옵션 | 설명 |
55
56
  | --- | --- |
56
57
  | `--path, -p` | 설치 경로 (기본: 현재 디렉토리) |
58
+ | `--target, -t` | 타겟 플랫폼 (claude, gemini, antigravity, codex, opencode, agents) |
57
59
  | `--force, -f` | 기존 파일 덮어쓰기 |
58
60
  | `--no-common` | common 유틸리티 제외 |
59
61
  | `--dry-run` | 미리보기 (실제 설치 안함) |
@@ -61,6 +63,40 @@ npx dantelabs-agentic-school uninstall brand-analytics
61
63
  | `-v, --verbose` | 상세 정보 표시 |
62
64
  | `-l, --lang` | 언어 설정 (en, ko) - 기본: en |
63
65
 
66
+ ### 멀티플랫폼 설치
67
+
68
+ `--target` 옵션으로 다양한 AI 코딩 도구에 플러그인을 설치할 수 있습니다.
69
+
70
+ ```bash
71
+ # Claude Code (기본값, --target 생략 가능)
72
+ npx dantelabs-agentic-school install gcp-openclaw
73
+
74
+ # Gemini CLI
75
+ npx dantelabs-agentic-school install gcp-openclaw --target gemini
76
+
77
+ # Google Antigravity IDE
78
+ npx dantelabs-agentic-school install gcp-openclaw --target antigravity
79
+
80
+ # OpenAI Codex CLI
81
+ npx dantelabs-agentic-school install gcp-openclaw --target codex
82
+
83
+ # OpenCode
84
+ npx dantelabs-agentic-school install gcp-openclaw --target opencode
85
+ ```
86
+
87
+ #### 플랫폼별 호환성
88
+
89
+ | 플랫폼 | 디렉토리 | Skills | Agents | Commands |
90
+ | --- | --- | --- | --- | --- |
91
+ | `claude` (기본) | `.claude/` | ✅ | ✅ | ✅ |
92
+ | `gemini` | `.gemini/` | ✅ | ✅ | ❌ skip |
93
+ | `antigravity` | `.agent/` | ✅ | ❌ skip | ❌ skip |
94
+ | `codex` | `.agents/` | ✅ | ❌ skip | ❌ skip |
95
+ | `opencode` | `.opencode/` | ✅ | ✅ | ✅ |
96
+ | `agents` | `.agents/` | ✅ | ❌ skip | ❌ skip |
97
+
98
+ > **Note**: 모든 플러그인의 Skills는 모든 플랫폼에서 설치됩니다. Agents/Commands는 해당 플랫폼이 지원하지 않으면 자동으로 skip됩니다.
99
+
64
100
  ### 샘플 다운로드
65
101
 
66
102
  학습용 샘플 파일을 다운로드하여 마케팅 자동화 파이프라인을 직접 체험해볼 수 있습니다.
@@ -309,10 +345,10 @@ python ~/.claude/skills/kie-video-generator/scripts/generate_video.py --credits
309
345
 
310
346
  | 항목 | 개수 |
311
347
  | --- | --- |
312
- | 플러그인 | 18개 (common 1 + marketing 8 + data-science 9) |
348
+ | 플러그인 | 19개 (common 1 + marketing 8 + data-science 9 + gcp 1) |
313
349
  | 에이전트 | 15개 |
314
- | 커맨드 | 19개 |
315
- | 스킬 | 28개 (common 6 + marketing 13 + data-science 9) |
350
+ | 커맨드 | 20개 |
351
+ | 스킬 | 29개 (common 6 + marketing 13 + data-science 9 + gcp 1) |
316
352
 
317
353
  ## 플러그인 목록
318
354
 
@@ -653,6 +689,49 @@ projects/{project-name}/
653
689
 
654
690
  ---
655
691
 
692
+ ## ☁️ GCP Plugins
693
+
694
+ Google Cloud Platform 배포 가이드 및 자동화 도구를 제공합니다.
695
+
696
+ ### gcp-openclaw
697
+
698
+ GCP VM 인스턴스에 OpenClaw를 배포하는 포괄적인 가이드입니다.
699
+
700
+ | 컴포넌트 | 이름 | 설명 |
701
+ | --- | --- | --- |
702
+ | Command | /deploy-openclaw | GCP VM 생성 및 OpenClaw 배포 가이드 실행 |
703
+ | Skill | gcp-openclaw | GCP 배포 전체 워크플로 (VM, SSH, OpenClaw, GOG CLI) |
704
+
705
+ #### 사용 예시
706
+
707
+ ```bash
708
+ # 기본 설정으로 배포 가이드 실행
709
+ /deploy-openclaw
710
+
711
+ # 서울 리전에 커스텀 설정으로 배포
712
+ /deploy-openclaw --zone asia-northeast3-a --machine-type e2-medium --instance-name openclaw-kr
713
+ ```
714
+
715
+ #### 배포 파이프라인
716
+
717
+ ```text
718
+ 1. GCP 인증 및 프로젝트 설정
719
+
720
+ 2. VM 인스턴스 생성 (Ubuntu 22.04 LTS)
721
+
722
+ 3. SSH 구성 및 접속
723
+
724
+ 4. 의존성 설치 (Node.js v22+, Go 1.23+)
725
+
726
+ 5. OpenClaw 설치 및 초기화
727
+
728
+ 6. GOG CLI 설치 및 인증
729
+
730
+ 7. SSH 터널링으로 대시보드 접속
731
+ ```
732
+
733
+ ---
734
+
656
735
  ## 🔜 Coming Soon
657
736
 
658
737
  ### 💼 Business Ops Plugins (예정)
@@ -7,6 +7,7 @@ import { mkdir } from 'fs/promises';
7
7
 
8
8
  import { getMarketplaceConfig, getPluginDependencies } from '../lib/config.js';
9
9
  import { installPlugin } from '../lib/installer.js';
10
+ import { getPlatformConfig, PLATFORM_NAMES, DEFAULT_PLATFORM } from '../lib/platforms.js';
10
11
  import logger from '../utils/logger.js';
11
12
  import { resolvePath } from '../utils/fs-utils.js';
12
13
  import { t } from '../i18n/index.js';
@@ -17,6 +18,7 @@ export default function installCommand(program) {
17
18
  .alias('i')
18
19
  .description(t('install.description'))
19
20
  .option('-p, --path <path>', t('install.optionPath'))
21
+ .option('-t, --target <platform>', t('install.optionTarget'), DEFAULT_PLATFORM)
20
22
  .option('-f, --force', t('install.optionForce'))
21
23
  .option('--all', t('install.optionAll'))
22
24
  .option('--no-common', t('install.optionNoCommon'))
@@ -25,13 +27,36 @@ export default function installCommand(program) {
25
27
  const spinner = ora();
26
28
 
27
29
  try {
30
+ // Validate platform
31
+ const platform = options.target;
32
+ if (!PLATFORM_NAMES.includes(platform)) {
33
+ logger.error(
34
+ t('install.invalidPlatform', {
35
+ name: platform,
36
+ valid: PLATFORM_NAMES.join(', ')
37
+ })
38
+ );
39
+ process.exit(1);
40
+ }
41
+
42
+ const platformConfig = getPlatformConfig(platform);
43
+
28
44
  // Determine installation path
29
45
  const targetPath = options.path
30
46
  ? resolvePath(options.path)
31
47
  : process.cwd();
32
- const claudeDir = join(targetPath, '.claude');
48
+ const baseDir = join(targetPath, platformConfig.dir);
33
49
 
34
- logger.info(`${t('install.installTarget')}: ${chalk.cyan(claudeDir)}`);
50
+ // Show platform info for non-default platforms
51
+ if (platform !== DEFAULT_PLATFORM) {
52
+ logger.info(
53
+ t('install.platformInfo', {
54
+ name: platformConfig.name,
55
+ description: platformConfig.description
56
+ })
57
+ );
58
+ }
59
+ logger.info(`${t('install.installTarget')}: ${chalk.cyan(baseDir)}`);
35
60
 
36
61
  // Load marketplace config
37
62
  spinner.start(t('install.loadingRegistry'));
@@ -100,18 +125,34 @@ export default function installCommand(program) {
100
125
  const components = plugin.components || {};
101
126
 
102
127
  if (components.agents?.length) {
103
- console.log(
104
- chalk.gray(
105
- ` ${t('common.agents')}: ${components.agents.join(', ')}`
106
- )
107
- );
128
+ if (platformConfig.agents) {
129
+ console.log(
130
+ chalk.gray(
131
+ ` ${t('common.agents')}: ${components.agents.join(', ')}`
132
+ )
133
+ );
134
+ } else {
135
+ console.log(
136
+ chalk.yellow(
137
+ ` ${t('common.agents')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.agents') })}`
138
+ )
139
+ );
140
+ }
108
141
  }
109
142
  if (components.commands?.length) {
110
- console.log(
111
- chalk.gray(
112
- ` ${t('common.commands')}: /${components.commands.join(', /')}`
113
- )
114
- );
143
+ if (platformConfig.commands) {
144
+ console.log(
145
+ chalk.gray(
146
+ ` ${t('common.commands')}: /${components.commands.join(', /')}`
147
+ )
148
+ );
149
+ } else {
150
+ console.log(
151
+ chalk.yellow(
152
+ ` ${t('common.commands')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.commands') })}`
153
+ )
154
+ );
155
+ }
115
156
  }
116
157
  if (components.skills?.length) {
117
158
  console.log(
@@ -127,22 +168,25 @@ export default function installCommand(program) {
127
168
  return;
128
169
  }
129
170
 
130
- // Create .claude directory
131
- if (!existsSync(claudeDir)) {
132
- await mkdir(claudeDir, { recursive: true });
171
+ // Create base directory
172
+ if (!existsSync(baseDir)) {
173
+ await mkdir(baseDir, { recursive: true });
133
174
  }
134
175
 
135
176
  // Install each plugin
136
177
  let totalAgents = 0;
137
178
  let totalCommands = 0;
138
179
  let totalSkills = 0;
180
+ let totalSkippedAgents = 0;
181
+ let totalSkippedCommands = 0;
139
182
 
140
183
  for (const plugin of pluginsToInstall) {
141
184
  spinner.start(t('install.installing', { name: chalk.cyan(plugin.name) }));
142
185
 
143
186
  try {
144
- const results = await installPlugin(plugin, claudeDir, {
187
+ const results = await installPlugin(plugin, baseDir, {
145
188
  force: options.force,
189
+ platform,
146
190
  onProgress: (type, name) => {
147
191
  spinner.text = t('install.installingComponent', {
148
192
  plugin: chalk.cyan(plugin.name),
@@ -155,6 +199,8 @@ export default function installCommand(program) {
155
199
  totalAgents += results.agents;
156
200
  totalCommands += results.commands;
157
201
  totalSkills += results.skills;
202
+ totalSkippedAgents += results.skippedAgents;
203
+ totalSkippedCommands += results.skippedCommands;
158
204
 
159
205
  spinner.succeed(t('install.installed', { name: chalk.cyan(plugin.name) }));
160
206
  } catch (err) {
@@ -184,21 +230,46 @@ export default function installCommand(program) {
184
230
  })}`
185
231
  )
186
232
  );
233
+
234
+ // Show skipped components warning
235
+ if (totalSkippedAgents > 0 || totalSkippedCommands > 0) {
236
+ console.log(
237
+ chalk.yellow(
238
+ ` ${t('install.skippedComponents', {
239
+ agents: totalSkippedAgents,
240
+ commands: totalSkippedCommands,
241
+ platform: platformConfig.name
242
+ })}`
243
+ )
244
+ );
245
+ }
246
+
187
247
  console.log();
188
- console.log(`${t('common.location')}: ${chalk.cyan(claudeDir)}`);
248
+ console.log(`${t('common.location')}: ${chalk.cyan(baseDir)}`);
189
249
 
190
250
  // Show next steps
191
251
  console.log();
192
252
  console.log(chalk.bold(`${t('install.nextSteps')}:`));
253
+
254
+ // Platform-specific next step hint
255
+ const cliCommands = {
256
+ claude: 'claude --help',
257
+ gemini: 'gemini',
258
+ antigravity: 'antigravity',
259
+ codex: 'codex --help',
260
+ opencode: 'opencode --help',
261
+ agents: 'claude --help',
262
+ };
263
+ const hintCmd = cliCommands[platform] || 'claude --help';
193
264
  console.log(
194
- ` 1. ${t('install.nextStep1', { command: chalk.cyan('claude --help') })}`
265
+ ` 1. ${t('install.nextStep1', { command: chalk.cyan(hintCmd) })}`
195
266
  );
196
267
 
197
268
  // Show example command based on installed plugins
198
269
  const hasAnalyzeBrand = pluginsToInstall.some(
199
270
  (p) => p.components?.commands?.includes('analyze-brand')
200
271
  );
201
- if (hasAnalyzeBrand) {
272
+ if (hasAnalyzeBrand && platformConfig.commands) {
202
273
  console.log(
203
274
  ` 2. ${t('install.nextStep2', {
204
275
  command: chalk.cyan('/analyze-brand --brand-doc ./your-brand.md')
@@ -6,6 +6,7 @@ import { existsSync } from 'fs';
6
6
 
7
7
  import { getMarketplaceConfig } from '../lib/config.js';
8
8
  import { uninstallPlugin } from '../lib/installer.js';
9
+ import { getPlatformConfig, PLATFORM_NAMES, DEFAULT_PLATFORM } from '../lib/platforms.js';
9
10
  import logger from '../utils/logger.js';
10
11
  import { resolvePath } from '../utils/fs-utils.js';
11
12
  import { t } from '../i18n/index.js';
@@ -16,20 +17,37 @@ export default function uninstallCommand(program) {
16
17
  .alias('rm')
17
18
  .description(t('uninstall.description'))
18
19
  .option('-p, --path <path>', t('uninstall.optionPath'))
20
+ .option('-t, --target <platform>', t('uninstall.optionTarget'), DEFAULT_PLATFORM)
19
21
  .option('-y, --yes', t('uninstall.optionYes'))
20
22
  .action(async (pluginName, options) => {
21
23
  const spinner = ora();
22
24
 
23
25
  try {
26
+ // Validate platform
27
+ const platform = options.target;
28
+ if (!PLATFORM_NAMES.includes(platform)) {
29
+ logger.error(
30
+ t('install.invalidPlatform', {
31
+ name: platform,
32
+ valid: PLATFORM_NAMES.join(', ')
33
+ })
34
+ );
35
+ process.exit(1);
36
+ }
37
+
38
+ const platformConfig = getPlatformConfig(platform);
39
+
24
40
  // Determine path
25
41
  const targetPath = options.path
26
42
  ? resolvePath(options.path)
27
43
  : process.cwd();
28
- const claudeDir = join(targetPath, '.claude');
44
+ const baseDir = join(targetPath, platformConfig.dir);
29
45
 
30
- // Check if .claude directory exists
31
- if (!existsSync(claudeDir)) {
32
- logger.error(t('uninstall.noClaudeDir', { path: targetPath }));
46
+ // Check if target directory exists
47
+ if (!existsSync(baseDir)) {
48
+ logger.error(
49
+ t('uninstall.noTargetDir', { dir: platformConfig.dir, path: targetPath })
50
+ );
33
51
  process.exit(1);
34
52
  }
35
53
 
@@ -59,14 +77,30 @@ export default function uninstallCommand(program) {
59
77
  const components = plugin.components || {};
60
78
 
61
79
  if (components.agents?.length) {
62
- console.log(
63
- chalk.gray(` ${t('common.agents')}: ${components.agents.join(', ')}`)
64
- );
80
+ if (platformConfig.agents) {
81
+ console.log(
82
+ chalk.gray(` ${t('common.agents')}: ${components.agents.join(', ')}`)
83
+ );
84
+ } else {
85
+ console.log(
86
+ chalk.yellow(
87
+ ` ${t('common.agents')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.agents') })}`
88
+ )
89
+ );
90
+ }
65
91
  }
66
92
  if (components.commands?.length) {
67
- console.log(
68
- chalk.gray(` ${t('common.commands')}: /${components.commands.join(', /')}`)
69
- );
93
+ if (platformConfig.commands) {
94
+ console.log(
95
+ chalk.gray(` ${t('common.commands')}: /${components.commands.join(', /')}`)
96
+ );
97
+ } else {
98
+ console.log(
99
+ chalk.yellow(
100
+ ` ${t('common.commands')}: ${t('install.platformNotSupported', { platform: platformConfig.name, component: t('common.commands') })}`
101
+ )
102
+ );
103
+ }
70
104
  }
71
105
  if (components.skills?.length) {
72
106
  console.log(
@@ -95,7 +129,7 @@ export default function uninstallCommand(program) {
95
129
  // Uninstall
96
130
  spinner.start(t('uninstall.uninstalling', { name: chalk.cyan(plugin.name) }));
97
131
 
98
- const results = await uninstallPlugin(plugin, claudeDir);
132
+ const results = await uninstallPlugin(plugin, baseDir, { platform });
99
133
 
100
134
  spinner.succeed(t('uninstall.uninstalled', { name: chalk.cyan(plugin.name) }));
101
135
 
@@ -110,6 +144,19 @@ export default function uninstallCommand(program) {
110
144
  })
111
145
  )
112
146
  );
147
+
148
+ // Show skipped info
149
+ if (results.skippedAgents > 0 || results.skippedCommands > 0) {
150
+ console.log(
151
+ chalk.yellow(
152
+ ` ${t('install.skippedComponents', {
153
+ agents: results.skippedAgents,
154
+ commands: results.skippedCommands,
155
+ platform: platformConfig.name
156
+ })}`
157
+ )
158
+ );
159
+ }
113
160
  } catch (error) {
114
161
  spinner.fail();
115
162
  logger.error(error.message);
@@ -38,6 +38,11 @@ export default {
38
38
  optionAll: 'Install all available plugins',
39
39
  optionNoCommon: 'Skip common utilities',
40
40
  optionDryRun: 'Show what would be installed without making changes',
41
+ optionTarget: 'Target platform (claude, gemini, antigravity, codex, opencode, agents)',
42
+ invalidPlatform: "Invalid platform '{name}'. Valid platforms: {valid}",
43
+ platformNotSupported: '{platform} does not support {component}. Skipping.',
44
+ skippedComponents: 'Skipped: {agents} agents, {commands} commands (not supported by {platform})',
45
+ platformInfo: 'Platform: {name} ({description})',
41
46
  installTarget: 'Installation target',
42
47
  loadingRegistry: 'Loading plugin registry...',
43
48
  registryLoaded: 'Plugin registry loaded',
@@ -85,6 +90,8 @@ export default {
85
90
  description: 'Uninstall a plugin from your project',
86
91
  optionPath: 'Project path (default: current directory)',
87
92
  optionYes: 'Skip confirmation prompt',
93
+ optionTarget: 'Target platform (claude, gemini, antigravity, codex, opencode, agents)',
94
+ noTargetDir: 'No {dir} directory found at {path}',
88
95
  noClaudeDir: 'No .claude directory found at {path}',
89
96
  loadingRegistry: 'Loading plugin registry...',
90
97
  pluginNotFound: "Plugin '{name}' not found in registry",
@@ -38,6 +38,11 @@ export default {
38
38
  optionAll: '모든 플러그인 설치',
39
39
  optionNoCommon: 'common 유틸리티 제외',
40
40
  optionDryRun: '실제 설치 없이 미리보기',
41
+ optionTarget: '대상 플랫폼 (claude, gemini, antigravity, codex, opencode, agents)',
42
+ invalidPlatform: "잘못된 플랫폼 '{name}'. 사용 가능: {valid}",
43
+ platformNotSupported: '{platform}은(는) {component}을(를) 지원하지 않습니다. 건너뜁니다.',
44
+ skippedComponents: '건너뜀: 에이전트 {agents}개, 명령어 {commands}개 ({platform} 미지원)',
45
+ platformInfo: '플랫폼: {name} ({description})',
41
46
  installTarget: '설치 대상 경로',
42
47
  loadingRegistry: '플러그인 목록 불러오는 중...',
43
48
  registryLoaded: '플러그인 목록 로드 완료',
@@ -85,6 +90,8 @@ export default {
85
90
  description: '프로젝트에서 플러그인 제거',
86
91
  optionPath: '프로젝트 경로 (기본: 현재 디렉토리)',
87
92
  optionYes: '확인 프롬프트 건너뛰기',
93
+ optionTarget: '대상 플랫폼 (claude, gemini, antigravity, codex, opencode, agents)',
94
+ noTargetDir: '{path}에 {dir} 디렉토리가 없습니다',
88
95
  noClaudeDir: '{path}에 .claude 디렉토리가 없습니다',
89
96
  loadingRegistry: '플러그인 목록 불러오는 중...',
90
97
  pluginNotFound: "플러그인 '{name}'이(가) 레지스트리에 없습니다",
@@ -3,6 +3,7 @@ import { join, dirname } from 'path';
3
3
  import { existsSync } from 'fs';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { fetchFile, downloadDirectory } from './downloader.js';
6
+ import { getPlatformConfig, DEFAULT_PLATFORM } from './platforms.js';
6
7
 
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = dirname(__filename);
@@ -143,9 +144,10 @@ function getSourcePath(plugin) {
143
144
  * └── skills/{skill-name}/
144
145
  */
145
146
  export async function installPlugin(plugin, claudeDir, options = {}) {
146
- const { force = false, onProgress } = options;
147
+ const { force = false, onProgress, platform = DEFAULT_PLATFORM } = options;
147
148
  const pluginName = plugin.name;
148
149
  const sourcePath = getSourcePath(plugin); // e.g., "plugins/brand-analytics"
150
+ const platformConfig = getPlatformConfig(platform);
149
151
 
150
152
  // Determine if we should use local or remote source
151
153
  const useLocalSource = hasLocalSource(sourcePath);
@@ -161,21 +163,29 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
161
163
  }
162
164
  }
163
165
 
164
- // Create base directories
165
- await ensureDir(join(claudeDir, 'agents'));
166
- await ensureDir(join(claudeDir, 'commands'));
167
- await ensureDir(join(claudeDir, 'skills'));
166
+ // Create base directories only for supported component types
167
+ if (platformConfig.agents) {
168
+ await ensureDir(join(claudeDir, platformConfig.agents));
169
+ }
170
+ if (platformConfig.commands) {
171
+ await ensureDir(join(claudeDir, platformConfig.commands));
172
+ }
173
+ await ensureDir(join(claudeDir, platformConfig.skills));
168
174
 
169
175
  const results = {
170
176
  plugin: pluginName,
171
177
  agents: 0,
172
178
  commands: 0,
173
- skills: 0
179
+ skills: 0,
180
+ skippedAgents: 0,
181
+ skippedCommands: 0,
174
182
  };
175
183
 
176
- // Install agents
177
- if (components.agents?.length) {
178
- const agentsTargetDir = join(claudeDir, 'agents', pluginName);
184
+ // Install agents (skip if platform does not support agents)
185
+ if (components.agents?.length && !platformConfig.agents) {
186
+ results.skippedAgents = components.agents.length;
187
+ } else if (components.agents?.length) {
188
+ const agentsTargetDir = join(claudeDir, platformConfig.agents, pluginName);
179
189
  await ensureDir(agentsTargetDir);
180
190
 
181
191
  for (const agentName of components.agents) {
@@ -207,9 +217,11 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
207
217
  }
208
218
  }
209
219
 
210
- // Install commands
211
- if (components.commands?.length) {
212
- const commandsTargetDir = join(claudeDir, 'commands', pluginName);
220
+ // Install commands (skip if platform does not support commands)
221
+ if (components.commands?.length && !platformConfig.commands) {
222
+ results.skippedCommands = components.commands.length;
223
+ } else if (components.commands?.length) {
224
+ const commandsTargetDir = join(claudeDir, platformConfig.commands, pluginName);
213
225
  await ensureDir(commandsTargetDir);
214
226
 
215
227
  for (const commandName of components.commands) {
@@ -244,7 +256,7 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
244
256
  // Install skills (preserve full directory structure)
245
257
  if (components.skills?.length) {
246
258
  for (const skillName of components.skills) {
247
- const skillTargetDir = join(claudeDir, 'skills', skillName);
259
+ const skillTargetDir = join(claudeDir, platformConfig.skills, skillName);
248
260
 
249
261
  if (!force && existsSync(skillTargetDir)) {
250
262
  throw new Error(`Directory exists: ${skillTargetDir}. Use --force to overwrite.`);
@@ -282,9 +294,11 @@ export async function installPlugin(plugin, claudeDir, options = {}) {
282
294
  /**
283
295
  * Uninstall a plugin from .claude directory
284
296
  */
285
- export async function uninstallPlugin(plugin, claudeDir) {
297
+ export async function uninstallPlugin(plugin, claudeDir, options = {}) {
298
+ const { platform = DEFAULT_PLATFORM } = options;
286
299
  const pluginName = plugin.name;
287
300
  const sourcePath = getSourcePath(plugin);
301
+ const platformConfig = getPlatformConfig(platform);
288
302
 
289
303
  // Discover components dynamically (or use provided components for backward compatibility)
290
304
  let components = plugin.components;
@@ -303,31 +317,37 @@ export async function uninstallPlugin(plugin, claudeDir) {
303
317
  plugin: pluginName,
304
318
  agents: 0,
305
319
  commands: 0,
306
- skills: 0
320
+ skills: 0,
321
+ skippedAgents: 0,
322
+ skippedCommands: 0,
307
323
  };
308
324
 
309
- // Remove agents
310
- if (components.agents?.length) {
311
- const agentsDir = join(claudeDir, 'agents', pluginName);
325
+ // Remove agents (skip if platform does not support agents)
326
+ if (components.agents?.length && platformConfig.agents) {
327
+ const agentsDir = join(claudeDir, platformConfig.agents, pluginName);
312
328
  if (existsSync(agentsDir)) {
313
329
  await rm(agentsDir, { recursive: true });
314
330
  results.agents = components.agents.length;
315
331
  }
332
+ } else if (components.agents?.length) {
333
+ results.skippedAgents = components.agents.length;
316
334
  }
317
335
 
318
- // Remove commands
319
- if (components.commands?.length) {
320
- const commandsDir = join(claudeDir, 'commands', pluginName);
336
+ // Remove commands (skip if platform does not support commands)
337
+ if (components.commands?.length && platformConfig.commands) {
338
+ const commandsDir = join(claudeDir, platformConfig.commands, pluginName);
321
339
  if (existsSync(commandsDir)) {
322
340
  await rm(commandsDir, { recursive: true });
323
341
  results.commands = components.commands.length;
324
342
  }
343
+ } else if (components.commands?.length) {
344
+ results.skippedCommands = components.commands.length;
325
345
  }
326
346
 
327
347
  // Remove skills
328
348
  if (components.skills?.length) {
329
349
  for (const skillName of components.skills) {
330
- const skillDir = join(claudeDir, 'skills', skillName);
350
+ const skillDir = join(claudeDir, platformConfig.skills, skillName);
331
351
  if (existsSync(skillDir)) {
332
352
  await rm(skillDir, { recursive: true });
333
353
  results.skills++;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Platform definitions for multi-platform skill installation.
3
+ *
4
+ * Each platform maps to a specific directory structure and
5
+ * declares which component types (skills, agents, commands) it supports.
6
+ * A `null` value means the platform does not support that component type.
7
+ */
8
+
9
+ export const PLATFORMS = {
10
+ claude: {
11
+ name: 'Claude Code',
12
+ dir: '.claude',
13
+ skills: 'skills',
14
+ agents: 'agents',
15
+ commands: 'commands',
16
+ description: 'Anthropic Claude Code',
17
+ },
18
+ gemini: {
19
+ name: 'Gemini CLI',
20
+ dir: '.gemini',
21
+ skills: 'skills',
22
+ agents: 'agents',
23
+ commands: null,
24
+ description: 'Google Gemini CLI',
25
+ },
26
+ antigravity: {
27
+ name: 'Antigravity',
28
+ dir: '.agent',
29
+ skills: 'skills',
30
+ agents: null,
31
+ commands: null,
32
+ description: 'Google Antigravity IDE',
33
+ },
34
+ codex: {
35
+ name: 'Codex CLI',
36
+ dir: '.agents',
37
+ skills: 'skills',
38
+ agents: null,
39
+ commands: null,
40
+ description: 'OpenAI Codex CLI',
41
+ },
42
+ opencode: {
43
+ name: 'OpenCode',
44
+ dir: '.opencode',
45
+ skills: 'skills',
46
+ agents: 'agents',
47
+ commands: 'commands',
48
+ description: 'OpenCode AI',
49
+ },
50
+ agents: {
51
+ name: 'Universal (.agents)',
52
+ dir: '.agents',
53
+ skills: 'skills',
54
+ agents: null,
55
+ commands: null,
56
+ description: 'Cross-platform .agents standard',
57
+ },
58
+ };
59
+
60
+ /** Valid platform name array */
61
+ export const PLATFORM_NAMES = Object.keys(PLATFORMS);
62
+
63
+ /** Default platform */
64
+ export const DEFAULT_PLATFORM = 'claude';
65
+
66
+ /**
67
+ * Get platform configuration by name.
68
+ * @param {string} name - Platform key (e.g. 'claude', 'gemini')
69
+ * @returns {object} Platform config object
70
+ * @throws {Error} If the platform name is invalid
71
+ */
72
+ export function getPlatformConfig(name) {
73
+ const config = PLATFORMS[name];
74
+ if (!config) {
75
+ throw new Error(
76
+ `Unknown platform '${name}'. Valid platforms: ${PLATFORM_NAMES.join(', ')}`
77
+ );
78
+ }
79
+ return config;
80
+ }
81
+
82
+ /**
83
+ * Compute the base installation directory for a given platform.
84
+ * @param {string} targetPath - Project root or custom path
85
+ * @param {string} platform - Platform key
86
+ * @returns {string} Absolute path to the platform directory (e.g. /project/.gemini)
87
+ */
88
+ export function getTargetDir(targetPath, platform) {
89
+ const config = getPlatformConfig(platform);
90
+ return `${targetPath}/${config.dir}`;
91
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dantelabs-agentic-school",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "CLI tool for installing Dante Labs agentic plugins for Claude Code",
5
5
  "main": "cli/src/index.js",
6
6
  "type": "module",
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: deploy-openclaw
3
+ description: GCP VM 인스턴스를 생성하고 OpenClaw + GOG CLI를 설치하는 단계별 가이드를 실행합니다.
4
+ arguments:
5
+ - name: zone
6
+ description: GCP 존 (예: us-central1-a, asia-northeast3-a)
7
+ required: false
8
+ default: "us-central1-a"
9
+ - name: machine-type
10
+ description: VM 머신 타입 (예: e2-small, e2-medium)
11
+ required: false
12
+ default: "e2-small"
13
+ - name: instance-name
14
+ description: VM 인스턴스 이름
15
+ required: false
16
+ default: "openclaw-instance"
17
+ ---
18
+
19
+ # /deploy-openclaw
20
+
21
+ GCP VM 인스턴스에 OpenClaw를 배포하는 전체 과정을 안내합니다.
22
+
23
+ ## Usage
24
+
25
+ ```bash
26
+ # 기본 설정으로 배포 가이드 실행
27
+ /deploy-openclaw
28
+
29
+ # 커스텀 설정
30
+ /deploy-openclaw --zone asia-northeast3-a --machine-type e2-medium --instance-name my-openclaw
31
+
32
+ # 서울 리전에 배포
33
+ /deploy-openclaw --zone asia-northeast3-a --instance-name openclaw-seoul
34
+ ```
35
+
36
+ ## What This Command Does
37
+
38
+ 이 명령어는 `gcp-openclaw` 스킬의 배포 가이드를 기반으로 다음 단계를 순차적으로 안내합니다:
39
+
40
+ ### 1. 전제 조건 확인
41
+ - Google Cloud CLI (`gcloud`) 설치 여부 확인
42
+ - 인증 및 프로젝트 설정 안내
43
+
44
+ ### 2. VM 인스턴스 생성
45
+ - 지정된 zone, machine-type, instance-name으로 VM 생성
46
+ - Ubuntu 22.04 LTS, 25GB 부트 디스크
47
+
48
+ ### 3. 서버 환경 설정
49
+ - Node.js v22+, Go 1.23+ 설치
50
+ - 기본 빌드 도구 설치
51
+
52
+ ### 4. OpenClaw 설치 및 초기화
53
+ - OpenClaw 전역 설치
54
+ - `openclaw onboard` 초기 설정
55
+
56
+ ### 5. GOG CLI 설치 및 인증
57
+ - GOG CLI 빌드 및 설치
58
+ - Keyring 백엔드 설정 (headless 모드)
59
+ - OAuth 인증 (client_secret.json)
60
+
61
+ ### 6. 접속 설정
62
+ - SSH 터널링으로 대시보드 접속
63
+
64
+ ## Examples
65
+
66
+ ### Example 1: 기본 배포 (미국 중부)
67
+ ```bash
68
+ /deploy-openclaw
69
+ ```
70
+ `us-central1-a` 존에 `e2-small` 인스턴스를 `openclaw-instance`라는 이름으로 생성합니다.
71
+
72
+ ### Example 2: 서울 리전 배포
73
+ ```bash
74
+ /deploy-openclaw --zone asia-northeast3-a --machine-type e2-medium --instance-name openclaw-kr
75
+ ```
76
+ 한국 사용자를 위해 서울 리전에 더 큰 머신 타입으로 배포합니다.
77
+
78
+ ## Related Skills
79
+
80
+ - `gcp-openclaw`: 전체 배포 가이드 참조 문서
81
+
82
+ ## Notes
83
+
84
+ - GCP 프로젝트에 결제가 활성화되어 있어야 합니다.
85
+ - `e2-small` (2 vCPU, 2GB RAM) 이상의 머신 타입을 권장합니다.
86
+ - SSH 터널링을 통해 대시보드에 접속할 수 있습니다.
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "gcp-openclaw",
3
+ "description": "GCP VM 인스턴스에 OpenClaw를 배포하는 가이드 및 자동화 도구",
4
+ "version": "1.0.0",
5
+ "author": {
6
+ "name": "Dante Labs",
7
+ "email": "datapod.k@gmail.com"
8
+ }
9
+ }
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: gcp-openclaw
3
+ description: VM 설정, SSH 구성, OpenClaw 설치 및 GOG CLI 설정을 포함하여 Google Cloud Platform에 OpenClaw를 배포하는 포괄적인 가이드입니다.
4
+ ---
5
+
6
+ # GCP OpenClaw 배포 가이드
7
+
8
+ 이 스킬은 Google Cloud Platform (GCP) VM 인스턴스에 OpenClaw를 배포하기 위한 단계별 워크플로를 제공합니다. 초기 GCP 설정부터 OpenClaw 및 GOG CLI 구성까지 모든 과정을 다룹니다.
9
+
10
+ ## 1. 전제 조건 (로컬 머신)
11
+
12
+ 로컬 컴퓨터에 Google Cloud CLI (`gcloud`)가 설치되어 있고 인증이 완료되었는지 확인하십시오.
13
+
14
+ ### 1.1 Google Cloud 인증
15
+ ```bash
16
+ gcloud auth login --no-launch-browser
17
+ ```
18
+ 출력된 URL을 브라우저에서 열어 인증하고, 확인 코드를 터미널에 붙여넣습니다.
19
+
20
+ ### 1.2 프로젝트 및 결제 설정
21
+ ```bash
22
+ # 프로젝트 ID 설정
23
+ gcloud config set project [YOUR_PROJECT_ID]
24
+
25
+ # 필요한 서비스 활성화
26
+ gcloud services enable compute.googleapis.com
27
+ gcloud services enable cloudresourcemanager.googleapis.com
28
+ ```
29
+
30
+ ## 2. VM 인스턴스 생성
31
+
32
+ 충분한 리소스를 가진 VM 인스턴스를 생성합니다.
33
+ **권장 사양:** `e2-small` (2 vCPU, 2GB RAM) 또는 `e2-medium` (2 vCPU, 4GB RAM).
34
+ **운영체제:** Ubuntu 22.04 LTS
35
+
36
+ ```bash
37
+ gcloud compute instances create openclaw-instance \
38
+ --zone=us-central1-a \
39
+ --machine-type=e2-small \
40
+ --image-family=ubuntu-2204-lts \
41
+ --image-project=ubuntu-os-cloud \
42
+ --boot-disk-size=25GB \
43
+ --boot-disk-type=pd-balanced \
44
+ --labels=env=dev,app=openclaw
45
+ ```
46
+
47
+ ## 3. SSH 구성
48
+
49
+ VM에 쉽게 접속할 수 있도록 SSH를 구성합니다.
50
+
51
+ ### 3.1 키 생성 및 접속
52
+ ```bash
53
+ # 키 자동 생성 및 접속
54
+ gcloud compute ssh openclaw-instance --zone=us-central1-a
55
+ ```
56
+
57
+ ### 3.2 표준 SSH 별칭 (선택 사항이지만 권장됨)
58
+ 로컬 `~/.ssh/config` 파일에 다음 내용을 추가합니다:
59
+ ```
60
+ Host openclaw
61
+ HostName [EXTERNAL_IP]
62
+ User [YOUR_USERNAME]
63
+ IdentityFile ~/.ssh/google_compute_engine
64
+ ```
65
+
66
+ ## 4. 의존성 및 도구 설치 (원격 서버)
67
+
68
+ 서버에 SSH로 접속하여 필요한 도구를 설치합니다.
69
+
70
+ ```bash
71
+ # 서버 접속
72
+ gcloud compute ssh openclaw-instance --zone=us-central1-a
73
+
74
+ # 업데이트 및 기본 도구 설치
75
+ sudo apt-get update
76
+ sudo apt-get install -y git curl make build-essential
77
+ ```
78
+
79
+ ### 4.1 Node.js 설치 (v22 이상)
80
+ OpenClaw는 Node.js v22 이상이 필요합니다.
81
+ ```bash
82
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
83
+ sudo apt-get install -y nodejs
84
+ ```
85
+
86
+ ### 4.2 Go 설치 (최신 버전 필수)
87
+ GOG CLI 빌드를 위해서는 **반드시 최신 버전의 Go (1.23 이상)**가 필요합니다. 운영체제 기본 패키지(예: `apt install golang`)는 버전이 낮아 빌드에 실패할 수 있으므로, 아래와 같이 수동 설치를 권장합니다.
88
+ ```bash
89
+ wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz
90
+ sudo rm -rf /usr/local/go
91
+ sudo tar -C /usr/local -xzf go1.23.6.linux-amd64.tar.gz
92
+ echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
93
+ source ~/.bashrc
94
+ ```
95
+
96
+ ## 5. OpenClaw 설치
97
+
98
+ npm을 사용하여 OpenClaw를 전역으로 설치합니다.
99
+
100
+ ```bash
101
+ # OpenClaw 설치
102
+ curl -fsSL https://openclaw.ai/install.sh | bash
103
+ # 또는 수동 설치:
104
+ # sudo npm install -g openclaw@latest
105
+ ```
106
+
107
+ ### 5.1 OpenClaw 초기화
108
+ ```bash
109
+ openclaw onboard
110
+ ```
111
+ 대화형 설정 마법사를 따라 에이전트를 구성합니다.
112
+
113
+ ## 6. GOG CLI 설치
114
+
115
+ Google 서비스 CLI 도구(`gogcli`)를 설치합니다.
116
+
117
+ ```bash
118
+ # 리포지토리 복제
119
+ git clone https://github.com/steipete/gogcli.git
120
+ cd gogcli
121
+
122
+ # 바이너리 빌드
123
+ make
124
+
125
+ # 전역 설치 (심볼릭 링크)
126
+ sudo ln -sf $(pwd)/bin/gog /usr/local/bin/gog
127
+
128
+ # 설치 확인
129
+ gog --help
130
+ ```
131
+
132
+ ### 6.2 보안 Keyring 구성 (중요)
133
+ 대화형 비밀번호 입력 없이 `gogcli`를 사용하려면(headless 모드), Keyring 백엔드를 **파일(file)**로 설정해야 합니다.
134
+
135
+ 1. **환경 변수 설정 (영구 적용)**
136
+ OpenClaw 편의 기능과 GOG CLI 보안 설정을 위해 `~/.bashrc` 파일 하단에 다음 내용을 추가합니다:
137
+ ```bash
138
+ # OpenClaw 자동 완성
139
+ source "$HOME/.openclaw/completions/openclaw.bash"
140
+
141
+ # Go 언어 경로 (앞서 추가하지 않았다면)
142
+ export PATH=$PATH:/usr/local/go/bin
143
+
144
+ # GOG CLI 설정 (File Backend & Headless)
145
+ export GOG_KEYRING_BACKEND=file
146
+ export GOG_KEYRING_PASSWORD='your_secure_password' # 강력한 비밀번호로 변경하세요
147
+ export GOG_ACCOUNT='your_email@gmail.com' # 이메일 반복 입력을 피하기 위해 설정
148
+ ```
149
+ 변경 사항 적용:
150
+ ```bash
151
+ source ~/.bashrc
152
+ ```
153
+
154
+ 2. **구성 확인**
155
+ 설정이 올바르게 적용되었는지 확인합니다:
156
+ ```bash
157
+ gog config list
158
+ ```
159
+
160
+ ## 7. 구성 및 인증
161
+
162
+ ### 7.1 Client Secret 설정
163
+ Google Cloud Console에서 다운로드한 `client_secret.json` 파일을 서버로 업로드합니다.
164
+ **로컬 머신에서 실행:**
165
+ ```bash
166
+ gcloud compute scp /local/path/to/client_secret.json openclaw-instance:~/client_secret.json --zone=us-central1-a
167
+ ```
168
+
169
+ ### 7.2 GOG CLI 인증 (Headless 모드)
170
+ **원격 서버에서 실행:**
171
+
172
+ 원격 서버에서는 브라우저를 실행할 수 없으므로 수동 인증 방식을 사용해야 합니다.
173
+
174
+ ```bash
175
+ # 업로드한 시크릿 파일을 사용하여 인증 (이메일 주소 지정 권장)
176
+ gog auth login [YOUR_EMAIL@gmail.com] --client-secret ~/client_secret.json
177
+ ```
178
+
179
+ 1. 명령어를 실행하면 터미널에 **인증 URL**이 출력됩니다.
180
+ 2. 해당 URL을 **로컬 컴퓨터의 브라우저**에 복사하여 붙여넣고 로그인합니다.
181
+ 3. 화면에 표시된 **인증 코드**를 복사하여 터미널에 붙여넣습니다.
182
+
183
+ > **참고:** 만약 브라우저가 열리려고 시도하다 실패한다면 `--manual` 플래그를 추가해 보십시오 (버전에 따라 다를 수 있음).
184
+
185
+
186
+ ### 7.3 텔레그램 봇 구성 (선택 사항)
187
+ 텔레그램을 통해 에이전트를 제어하려면 다음 단계를 따르세요:
188
+
189
+ 1. **봇 생성:**
190
+ - 텔레그램을 열고 **@BotFather**를 검색합니다.
191
+ - `/newbot`을 보내고 안내에 따라 진행하여 **HTTP API Token**을 발급받습니다.
192
+
193
+ 2. **OpenClaw 구성:**
194
+ - 구성 마법사를 실행합니다:
195
+ ```bash
196
+ openclaw configure --section telegram
197
+ ```
198
+ - 프롬프트에 **HTTP API Token**을 붙여넣습니다.
199
+ - 텔레그램 통합을 활성화합니다.
200
+
201
+ ## 8. 웹 액세스 (SSH 터널링)
202
+
203
+ 로컬 머신에서 OpenClaw 대시보드에 안전하게 접속하려면 다음 명령어를 사용하십시오:
204
+
205
+ **로컬 머신에서 실행:**
206
+ ```bash
207
+ # 원격 포트 18789를 로컬 포트 18790으로 포워딩
208
+ gcloud compute ssh openclaw-instance --zone=us-central1-a -- -L 18790:localhost:18789 -N -f
209
+ ```
210
+ 대시보드 접속 주소: http://localhost:18790
211
+
212
+ ## 9. 문제 해결 팁
213
+
214
+ - **메모리 문제:** `npm install` 실패 시 인스턴스 사양이 최소 `e2-small` (2GB RAM) 이상인지 확인하십시오. 스왑(swap) 파일을 추가하는 것도 방법입니다.
215
+ - **Go 버전:** `gogcli` 빌드 실패 시 `go version`을 확인하십시오. 반드시 1.22 버전 이상이어야 합니다.
216
+ - **포트 충돌:** 로컬에서 18789 포트가 이미 사용 중이라면 다른 포트를 사용하십시오 (예: `-L 18790:localhost:18789`).