dantelabs-agentic-school 1.2.1 → 1.3.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 +1 -1
- package/README.md +62 -0
- package/cli/bin/cli.js +6 -0
- package/cli/src/commands/sample.js +259 -0
- package/cli/src/i18n/locales/en.js +26 -0
- package/cli/src/i18n/locales/ko.js +26 -0
- package/cli/src/lib/config.js +1 -1
- package/package.json +2 -1
- package/plugins/market-research/agents/competitive-intelligence.md +171 -0
- package/plugins/market-research/agents/market-analyst.md +141 -0
- package/plugins/market-research/commands/analyze-market.md +129 -0
- package/plugins/market-research/commands/competitive-landscape.md +144 -0
- package/samples/marketing/dante-coffee-agentic-marketing-scenario.md +872 -0
- package/samples/marketing/dante-coffee-brand-brief.md +133 -0
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
64
|
"name": "market-research",
|
|
65
|
-
"description": "시장 분석
|
|
65
|
+
"description": "시장 규모/성장률 분석, Porter's 5 Forces, 경쟁 환경 분석 등 종합적인 시장 조사를 수행합니다. TAM/SAM/SOM 모델링, PESTLE 분석, 경쟁사 포지셔닝 맵 등을 포함합니다.",
|
|
66
66
|
"version": "1.0.0",
|
|
67
67
|
"source": "./plugins/market-research"
|
|
68
68
|
}
|
package/README.md
CHANGED
|
@@ -61,6 +61,38 @@ npx dantelabs-agentic-school uninstall brand-analytics
|
|
|
61
61
|
| `-v, --verbose` | 상세 정보 표시 |
|
|
62
62
|
| `-l, --lang` | 언어 설정 (en, ko) - 기본: en |
|
|
63
63
|
|
|
64
|
+
### 샘플 다운로드
|
|
65
|
+
|
|
66
|
+
학습용 샘플 파일을 다운로드하여 마케팅 자동화 파이프라인을 직접 체험해볼 수 있습니다.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# 사용 가능한 샘플 목록 보기
|
|
70
|
+
npx dantelabs-agentic-school sample --list
|
|
71
|
+
|
|
72
|
+
# 특정 샘플 다운로드
|
|
73
|
+
npx dantelabs-agentic-school sample marketing
|
|
74
|
+
|
|
75
|
+
# 전체 샘플 다운로드
|
|
76
|
+
npx dantelabs-agentic-school sample --all
|
|
77
|
+
|
|
78
|
+
# 다운로드 경로 지정
|
|
79
|
+
npx dantelabs-agentic-school sample marketing --path ./my-project
|
|
80
|
+
|
|
81
|
+
# 기존 파일 덮어쓰기
|
|
82
|
+
npx dantelabs-agentic-school sample marketing --force
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
샘플을 다운로드하면 `samples/` 폴더에 학습 자료가 저장됩니다:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
samples/
|
|
89
|
+
└── marketing/
|
|
90
|
+
├── dante-coffee-agentic-marketing-scenario.md # 마케팅 시나리오 가이드
|
|
91
|
+
└── dante-coffee-brand-brief.md # 브랜드 브리프 예시
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> **Tip**: 다운로드한 브랜드 브리프를 사용하여 `/analyze-brand --brand-doc ./samples/marketing/dante-coffee-brand-brief.md` 명령어로 전체 파이프라인을 실행해볼 수 있습니다.
|
|
95
|
+
|
|
64
96
|
### Claude Code에서 플러그인 사용
|
|
65
97
|
|
|
66
98
|
#### 방법 1: 프로젝트별 설치 (권장)
|
|
@@ -117,6 +149,36 @@ npx dantelabs-agentic-school install --path ~
|
|
|
117
149
|
claude
|
|
118
150
|
```
|
|
119
151
|
|
|
152
|
+
#### 방법 4: Claude Code 세션 내 마켓플레이스 설치
|
|
153
|
+
|
|
154
|
+
Claude Code 실행 중 슬래시 명령어로 직접 마켓플레이스를 등록하고 플러그인을 설치할 수 있습니다.
|
|
155
|
+
|
|
156
|
+
**1. 마켓플레이스 추가**
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# GitHub 저장소로 추가
|
|
160
|
+
/plugin marketplace add dandacompany/dantelabs-agentic-school
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**2. 플러그인 설치**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# 특정 플러그인 설치
|
|
167
|
+
/plugin install brand-analytics@dantelabs-agentic-school
|
|
168
|
+
|
|
169
|
+
# 또는 /plugin 실행 후 Discover 탭에서 검색하여 설치
|
|
170
|
+
/plugin
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**3. 설치된 플러그인 확인**
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# 플러그인 목록 확인
|
|
177
|
+
/plugin
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
> **Tip**: `/plugin` 명령어 실행 후 **Discover** 탭에서 마켓플레이스의 모든 플러그인을 검색하고 설치할 수 있습니다.
|
|
181
|
+
|
|
120
182
|
### 플러그인 사용 확인
|
|
121
183
|
|
|
122
184
|
Claude Code 실행 후 `/help` 명령어로 설치된 커맨드를 확인할 수 있습니다:
|
package/cli/bin/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import installCommand from '../src/commands/install.js';
|
|
|
11
11
|
import listCommand from '../src/commands/list.js';
|
|
12
12
|
import infoCommand from '../src/commands/info.js';
|
|
13
13
|
import uninstallCommand from '../src/commands/uninstall.js';
|
|
14
|
+
import sampleCommand from '../src/commands/sample.js';
|
|
14
15
|
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = dirname(__filename);
|
|
@@ -62,6 +63,10 @@ ${chalk.bold(t('cli.examples'))}
|
|
|
62
63
|
${chalk.gray(t('cli.showPluginInfo'))}
|
|
63
64
|
$ npx dantelabs-agentic-school info brand-analytics
|
|
64
65
|
|
|
66
|
+
${chalk.gray('# Download samples')}
|
|
67
|
+
$ npx dantelabs-agentic-school sample marketing
|
|
68
|
+
$ npx dantelabs-agentic-school sample --all
|
|
69
|
+
|
|
65
70
|
${chalk.gray('# Korean language')}
|
|
66
71
|
$ npx dantelabs-agentic-school --lang ko list
|
|
67
72
|
|
|
@@ -73,6 +78,7 @@ installCommand(program);
|
|
|
73
78
|
listCommand(program);
|
|
74
79
|
infoCommand(program);
|
|
75
80
|
uninstallCommand(program);
|
|
81
|
+
sampleCommand(program);
|
|
76
82
|
|
|
77
83
|
// Parse arguments
|
|
78
84
|
program.parse();
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { mkdir, writeFile, readFile, readdir, cp } from 'fs/promises';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { GITHUB_CONFIG } from '../lib/config.js';
|
|
8
|
+
import { fetchFile, getDirectoryContents } from '../lib/downloader.js';
|
|
9
|
+
import logger from '../utils/logger.js';
|
|
10
|
+
import { t } from '../i18n/index.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const PACKAGE_ROOT = join(__dirname, '../../..');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if local samples directory exists
|
|
18
|
+
*/
|
|
19
|
+
function hasLocalSamples() {
|
|
20
|
+
return existsSync(join(PACKAGE_ROOT, 'samples'));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get list of available samples (local or remote)
|
|
25
|
+
*/
|
|
26
|
+
async function getAvailableSamples() {
|
|
27
|
+
if (hasLocalSamples()) {
|
|
28
|
+
const samplesDir = join(PACKAGE_ROOT, 'samples');
|
|
29
|
+
const entries = await readdir(samplesDir, { withFileTypes: true });
|
|
30
|
+
return entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fallback to remote
|
|
34
|
+
const contents = await getDirectoryContents('samples');
|
|
35
|
+
return contents
|
|
36
|
+
.filter(item => item.type === 'dir')
|
|
37
|
+
.map(item => item.name);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Copy local sample folder recursively
|
|
42
|
+
*/
|
|
43
|
+
async function copyLocalSampleFolder(sampleName, targetDir, options = {}) {
|
|
44
|
+
const { onFile } = options;
|
|
45
|
+
const localPath = join(PACKAGE_ROOT, 'samples', sampleName);
|
|
46
|
+
|
|
47
|
+
if (!existsSync(localPath)) {
|
|
48
|
+
throw new Error(t('sample.notFound', { name: sampleName }));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Create target directory
|
|
52
|
+
await mkdir(targetDir, { recursive: true });
|
|
53
|
+
|
|
54
|
+
let fileCount = 0;
|
|
55
|
+
|
|
56
|
+
async function copyRecursive(srcDir, destDir) {
|
|
57
|
+
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
58
|
+
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
const srcPath = join(srcDir, entry.name);
|
|
61
|
+
const destPath = join(destDir, entry.name);
|
|
62
|
+
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
await mkdir(destPath, { recursive: true });
|
|
65
|
+
await copyRecursive(srcPath, destPath);
|
|
66
|
+
} else {
|
|
67
|
+
const content = await readFile(srcPath);
|
|
68
|
+
await writeFile(destPath, content);
|
|
69
|
+
fileCount++;
|
|
70
|
+
|
|
71
|
+
if (onFile) {
|
|
72
|
+
onFile(entry.name);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await copyRecursive(localPath, targetDir);
|
|
79
|
+
return fileCount;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Download a sample folder from remote
|
|
84
|
+
*/
|
|
85
|
+
async function downloadRemoteSampleFolder(sampleName, targetDir, options = {}) {
|
|
86
|
+
const { onFile } = options;
|
|
87
|
+
const remotePath = `samples/${sampleName}`;
|
|
88
|
+
const contents = await getDirectoryContents(remotePath);
|
|
89
|
+
|
|
90
|
+
if (contents.length === 0) {
|
|
91
|
+
throw new Error(t('sample.notFound', { name: sampleName }));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Create target directory
|
|
95
|
+
await mkdir(targetDir, { recursive: true });
|
|
96
|
+
|
|
97
|
+
let fileCount = 0;
|
|
98
|
+
|
|
99
|
+
for (const item of contents) {
|
|
100
|
+
if (item.type === 'file') {
|
|
101
|
+
const content = await fetchFile(item.path);
|
|
102
|
+
const filePath = join(targetDir, item.name);
|
|
103
|
+
await writeFile(filePath, content);
|
|
104
|
+
fileCount++;
|
|
105
|
+
|
|
106
|
+
if (onFile) {
|
|
107
|
+
onFile(item.name);
|
|
108
|
+
}
|
|
109
|
+
} else if (item.type === 'dir') {
|
|
110
|
+
// Recursively download subdirectories
|
|
111
|
+
const subDir = join(targetDir, item.name);
|
|
112
|
+
const subResult = await downloadRemoteSampleFolder(
|
|
113
|
+
`${sampleName}/${item.name}`,
|
|
114
|
+
subDir,
|
|
115
|
+
options
|
|
116
|
+
);
|
|
117
|
+
fileCount += subResult;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return fileCount;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Download or copy a sample folder (local or remote)
|
|
126
|
+
*/
|
|
127
|
+
async function downloadSampleFolder(sampleName, targetDir, options = {}) {
|
|
128
|
+
if (hasLocalSamples()) {
|
|
129
|
+
return copyLocalSampleFolder(sampleName, targetDir, options);
|
|
130
|
+
}
|
|
131
|
+
return downloadRemoteSampleFolder(sampleName, targetDir, options);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default function sampleCommand(program) {
|
|
135
|
+
program
|
|
136
|
+
.command('sample [name]')
|
|
137
|
+
.description(t('sample.description'))
|
|
138
|
+
.option('-a, --all', t('sample.optionAll'))
|
|
139
|
+
.option('-l, --list', t('sample.optionList'))
|
|
140
|
+
.option('-p, --path <path>', t('sample.optionPath'), '.')
|
|
141
|
+
.option('-f, --force', t('sample.optionForce'))
|
|
142
|
+
.action(async (name, options) => {
|
|
143
|
+
try {
|
|
144
|
+
const targetBase = options.path;
|
|
145
|
+
|
|
146
|
+
// List available samples
|
|
147
|
+
if (options.list || (!name && !options.all)) {
|
|
148
|
+
const spinner = ora(t('sample.fetchingList')).start();
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const samples = await getAvailableSamples();
|
|
152
|
+
spinner.stop();
|
|
153
|
+
|
|
154
|
+
console.log();
|
|
155
|
+
console.log(chalk.bold.blue(t('sample.availableTitle')));
|
|
156
|
+
console.log(chalk.gray('━'.repeat(50)));
|
|
157
|
+
console.log();
|
|
158
|
+
|
|
159
|
+
if (samples.length === 0) {
|
|
160
|
+
console.log(chalk.yellow(t('sample.noSamples')));
|
|
161
|
+
} else {
|
|
162
|
+
for (const sample of samples) {
|
|
163
|
+
console.log(` ${chalk.cyan('•')} ${sample}`);
|
|
164
|
+
}
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(chalk.gray(t('sample.downloadHint')));
|
|
167
|
+
console.log(chalk.gray(t('sample.downloadAllHint')));
|
|
168
|
+
}
|
|
169
|
+
console.log();
|
|
170
|
+
} catch (error) {
|
|
171
|
+
spinner.fail(t('sample.fetchError'));
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Download all samples
|
|
178
|
+
if (options.all) {
|
|
179
|
+
const spinner = ora(t('sample.fetchingList')).start();
|
|
180
|
+
const samples = await getAvailableSamples();
|
|
181
|
+
spinner.stop();
|
|
182
|
+
|
|
183
|
+
if (samples.length === 0) {
|
|
184
|
+
logger.warn(t('sample.noSamples'));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log();
|
|
189
|
+
console.log(chalk.bold.blue(t('sample.downloadingAll', { count: samples.length })));
|
|
190
|
+
console.log();
|
|
191
|
+
|
|
192
|
+
let totalFiles = 0;
|
|
193
|
+
|
|
194
|
+
for (const sampleName of samples) {
|
|
195
|
+
const targetDir = join(targetBase, 'samples', sampleName);
|
|
196
|
+
|
|
197
|
+
if (!options.force && existsSync(targetDir)) {
|
|
198
|
+
logger.warn(t('sample.alreadyExists', { name: sampleName }));
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const downloadSpinner = ora(t('sample.downloading', { name: sampleName })).start();
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const fileCount = await downloadSampleFolder(sampleName, targetDir, {
|
|
206
|
+
onFile: (fileName) => {
|
|
207
|
+
downloadSpinner.text = t('sample.downloadingFile', { name: sampleName, file: fileName });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
downloadSpinner.succeed(
|
|
212
|
+
t('sample.downloadedFiles', { name: sampleName, count: fileCount })
|
|
213
|
+
);
|
|
214
|
+
totalFiles += fileCount;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
downloadSpinner.fail(t('sample.downloadFailed', { name: sampleName }));
|
|
217
|
+
logger.error(error.message);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log();
|
|
222
|
+
console.log(chalk.green(t('sample.allComplete', { count: totalFiles })));
|
|
223
|
+
console.log(chalk.gray(t('sample.savedTo', { path: join(targetBase, 'samples') })));
|
|
224
|
+
console.log();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Download specific sample
|
|
229
|
+
const targetDir = join(targetBase, 'samples', name);
|
|
230
|
+
|
|
231
|
+
if (!options.force && existsSync(targetDir)) {
|
|
232
|
+
logger.error(t('sample.alreadyExistsError', { path: targetDir }));
|
|
233
|
+
console.log(chalk.gray(t('sample.useForceHint')));
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const spinner = ora(t('sample.downloading', { name })).start();
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const fileCount = await downloadSampleFolder(name, targetDir, {
|
|
241
|
+
onFile: (fileName) => {
|
|
242
|
+
spinner.text = t('sample.downloadingFile', { name, file: fileName });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
spinner.succeed(t('sample.downloadedFiles', { name, count: fileCount }));
|
|
247
|
+
console.log();
|
|
248
|
+
console.log(chalk.gray(t('sample.savedTo', { path: targetDir })));
|
|
249
|
+
console.log();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
spinner.fail(t('sample.downloadFailed', { name }));
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.error(error.message);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
@@ -96,6 +96,32 @@ export default {
|
|
|
96
96
|
removedSummary: 'Removed: {agents} agents, {commands} commands, {skills} skills'
|
|
97
97
|
},
|
|
98
98
|
|
|
99
|
+
// Sample command
|
|
100
|
+
sample: {
|
|
101
|
+
description: 'Download sample files for learning',
|
|
102
|
+
optionAll: 'Download all samples',
|
|
103
|
+
optionList: 'List available samples',
|
|
104
|
+
optionPath: 'Download path (default: current directory)',
|
|
105
|
+
optionForce: 'Force overwrite existing files',
|
|
106
|
+
fetchingList: 'Fetching sample list...',
|
|
107
|
+
availableTitle: 'Available Samples',
|
|
108
|
+
noSamples: 'No samples available',
|
|
109
|
+
downloadHint: 'Download: npx dantelabs-agentic-school sample <name>',
|
|
110
|
+
downloadAllHint: 'Download all: npx dantelabs-agentic-school sample --all',
|
|
111
|
+
downloadingAll: 'Downloading all {count} samples...',
|
|
112
|
+
downloading: 'Downloading {name}...',
|
|
113
|
+
downloadingFile: 'Downloading {name}: {file}',
|
|
114
|
+
downloadedFiles: 'Downloaded {name} ({count} files)',
|
|
115
|
+
downloadFailed: 'Failed to download {name}',
|
|
116
|
+
allComplete: 'All samples downloaded ({count} files total)',
|
|
117
|
+
savedTo: 'Saved to: {path}',
|
|
118
|
+
alreadyExists: 'Skipping {name} (already exists, use --force to overwrite)',
|
|
119
|
+
alreadyExistsError: 'Directory already exists: {path}',
|
|
120
|
+
useForceHint: 'Use --force to overwrite existing files',
|
|
121
|
+
notFound: "Sample '{name}' not found",
|
|
122
|
+
fetchError: 'Failed to fetch sample list'
|
|
123
|
+
},
|
|
124
|
+
|
|
99
125
|
// Logger
|
|
100
126
|
logger: {
|
|
101
127
|
info: 'info',
|
|
@@ -96,6 +96,32 @@ export default {
|
|
|
96
96
|
removedSummary: '삭제됨: 에이전트 {agents}개, 명령어 {commands}개, 스킬 {skills}개'
|
|
97
97
|
},
|
|
98
98
|
|
|
99
|
+
// Sample command
|
|
100
|
+
sample: {
|
|
101
|
+
description: '학습용 샘플 파일 다운로드',
|
|
102
|
+
optionAll: '모든 샘플 다운로드',
|
|
103
|
+
optionList: '사용 가능한 샘플 목록',
|
|
104
|
+
optionPath: '다운로드 경로 (기본: 현재 디렉토리)',
|
|
105
|
+
optionForce: '기존 파일 강제 덮어쓰기',
|
|
106
|
+
fetchingList: '샘플 목록 불러오는 중...',
|
|
107
|
+
availableTitle: '사용 가능한 샘플',
|
|
108
|
+
noSamples: '사용 가능한 샘플이 없습니다',
|
|
109
|
+
downloadHint: '다운로드: npx dantelabs-agentic-school sample <이름>',
|
|
110
|
+
downloadAllHint: '전체 다운로드: npx dantelabs-agentic-school sample --all',
|
|
111
|
+
downloadingAll: '전체 {count}개 샘플 다운로드 중...',
|
|
112
|
+
downloading: '{name} 다운로드 중...',
|
|
113
|
+
downloadingFile: '{name} 다운로드 중: {file}',
|
|
114
|
+
downloadedFiles: '{name} 다운로드 완료 ({count}개 파일)',
|
|
115
|
+
downloadFailed: '{name} 다운로드 실패',
|
|
116
|
+
allComplete: '전체 샘플 다운로드 완료 (총 {count}개 파일)',
|
|
117
|
+
savedTo: '저장 위치: {path}',
|
|
118
|
+
alreadyExists: '{name} 건너뜀 (이미 존재함, --force로 덮어쓰기 가능)',
|
|
119
|
+
alreadyExistsError: '디렉토리가 이미 존재합니다: {path}',
|
|
120
|
+
useForceHint: '기존 파일을 덮어쓰려면 --force 옵션을 사용하세요',
|
|
121
|
+
notFound: "샘플 '{name}'을(를) 찾을 수 없습니다",
|
|
122
|
+
fetchError: '샘플 목록을 불러오는 데 실패했습니다'
|
|
123
|
+
},
|
|
124
|
+
|
|
99
125
|
// Logger
|
|
100
126
|
logger: {
|
|
101
127
|
info: '정보',
|
package/cli/src/lib/config.js
CHANGED
|
@@ -17,7 +17,7 @@ const CACHE_TTL = 1000 * 60 * 60; // 1 hour
|
|
|
17
17
|
export const GITHUB_CONFIG = {
|
|
18
18
|
owner: 'dandacompany',
|
|
19
19
|
repo: 'dantelabs-agentic-school',
|
|
20
|
-
branch: '
|
|
20
|
+
branch: 'master',
|
|
21
21
|
get rawBase() {
|
|
22
22
|
return `https://raw.githubusercontent.com/${this.owner}/${this.repo}/${this.branch}`;
|
|
23
23
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dantelabs-agentic-school",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"files": [
|
|
43
43
|
"cli/",
|
|
44
44
|
"plugins/",
|
|
45
|
+
"samples/",
|
|
45
46
|
".claude-plugin/"
|
|
46
47
|
],
|
|
47
48
|
"dependencies": {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: competitive-intelligence
|
|
3
|
+
description: 경쟁 환경 및 산업 구조를 분석하는 경쟁 인텔리전스 에이전트
|
|
4
|
+
when_to_use: |
|
|
5
|
+
이 에이전트는 다음 상황에서 사용합니다:
|
|
6
|
+
- "경쟁 환경 분석해줘", "경쟁사 조사해줘"
|
|
7
|
+
- "Porter's 5 Forces 분석", "산업 구조 분석"
|
|
8
|
+
- "경쟁사 포지셔닝 맵", "경쟁 강도 분석"
|
|
9
|
+
- 산업 수준의 경쟁 환경 파악이 필요할 때
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Competitive Intelligence Agent
|
|
13
|
+
|
|
14
|
+
산업 수준의 경쟁 환경과 구조를 분석하는 전문 에이전트입니다.
|
|
15
|
+
|
|
16
|
+
## Role & Expertise
|
|
17
|
+
|
|
18
|
+
### Primary Role
|
|
19
|
+
- Porter's Five Forces 분석
|
|
20
|
+
- 경쟁사 포지셔닝 분석
|
|
21
|
+
- 산업 구조 분석
|
|
22
|
+
- 경쟁 강도 평가
|
|
23
|
+
|
|
24
|
+
### Core Skills
|
|
25
|
+
- 산업 구조 분석
|
|
26
|
+
- 경쟁사 벤치마킹
|
|
27
|
+
- 포지셔닝 맵 작성
|
|
28
|
+
- 진입 장벽 평가
|
|
29
|
+
|
|
30
|
+
## Analysis Framework
|
|
31
|
+
|
|
32
|
+
### 1. Porter's Five Forces
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
┌─────────────────┐
|
|
36
|
+
│ 신규 진입자 │
|
|
37
|
+
│ 위협 │
|
|
38
|
+
└────────┬────────┘
|
|
39
|
+
│
|
|
40
|
+
▼
|
|
41
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
42
|
+
│ 공급자 교섭력 │◀─│ 산업 내 │─▶│ 구매자 교섭력 │
|
|
43
|
+
│ │ │ 경쟁 │ │ │
|
|
44
|
+
└──────────────┘ └───────┬──────┘ └──────────────┘
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
┌─────────────────┐
|
|
48
|
+
│ 대체재 위협 │
|
|
49
|
+
└─────────────────┘
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
각 요인별 평가 기준:
|
|
53
|
+
- **HIGH**: 강한 위협/압력
|
|
54
|
+
- **MEDIUM**: 중간 수준
|
|
55
|
+
- **LOW**: 낮은 위협/압력
|
|
56
|
+
|
|
57
|
+
### 2. 경쟁사 포지셔닝 매트릭스
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
High
|
|
61
|
+
가격 │ ● Premium
|
|
62
|
+
│ Leaders
|
|
63
|
+
│
|
|
64
|
+
│ ★ Target
|
|
65
|
+
│ Position
|
|
66
|
+
│
|
|
67
|
+
Low │ ● Low-cost
|
|
68
|
+
│ Players
|
|
69
|
+
└──────────────────
|
|
70
|
+
Low 품질/가치 High
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. 경쟁 강도 분석
|
|
74
|
+
|
|
75
|
+
| 요소 | 평가 항목 |
|
|
76
|
+
|------|----------|
|
|
77
|
+
| 경쟁자 수 | 시장 내 경쟁사 수 |
|
|
78
|
+
| 성장률 | 시장 성장 속도 |
|
|
79
|
+
| 차별화 | 제품/서비스 차별화 정도 |
|
|
80
|
+
| 전환 비용 | 고객 전환 비용 |
|
|
81
|
+
| 퇴출 장벽 | 시장 퇴출의 어려움 |
|
|
82
|
+
|
|
83
|
+
## Output Format
|
|
84
|
+
|
|
85
|
+
### 경쟁 환경 분석 리포트
|
|
86
|
+
|
|
87
|
+
```markdown
|
|
88
|
+
# 경쟁 환경 분석: [산업명]
|
|
89
|
+
|
|
90
|
+
## Executive Summary
|
|
91
|
+
> [핵심 발견 사항]
|
|
92
|
+
|
|
93
|
+
## 1. Porter's Five Forces 분석
|
|
94
|
+
|
|
95
|
+
### 1.1 산업 내 경쟁 (Industry Rivalry): [HIGH/MEDIUM/LOW]
|
|
96
|
+
- 현황: [분석]
|
|
97
|
+
- 주요 경쟁사: [리스트]
|
|
98
|
+
- 경쟁 요인: [요인들]
|
|
99
|
+
|
|
100
|
+
### 1.2 신규 진입자 위협: [HIGH/MEDIUM/LOW]
|
|
101
|
+
- 진입 장벽: [분석]
|
|
102
|
+
- 필요 자본: [수준]
|
|
103
|
+
- 규제 환경: [분석]
|
|
104
|
+
|
|
105
|
+
### 1.3 대체재 위협: [HIGH/MEDIUM/LOW]
|
|
106
|
+
- 대체재 유형: [리스트]
|
|
107
|
+
- 가격/성능 비교: [분석]
|
|
108
|
+
|
|
109
|
+
### 1.4 공급자 교섭력: [HIGH/MEDIUM/LOW]
|
|
110
|
+
- 공급자 집중도: [분석]
|
|
111
|
+
- 전환 비용: [분석]
|
|
112
|
+
|
|
113
|
+
### 1.5 구매자 교섭력: [HIGH/MEDIUM/LOW]
|
|
114
|
+
- 구매자 특성: [분석]
|
|
115
|
+
- 가격 민감도: [분석]
|
|
116
|
+
|
|
117
|
+
## 2. 경쟁사 포지셔닝
|
|
118
|
+
|
|
119
|
+
| 경쟁사 | 포지션 | 가격대 | 주요 강점 | 시장 점유율 |
|
|
120
|
+
|--------|--------|--------|----------|------------|
|
|
121
|
+
| A사 | ... | ... | ... | ...% |
|
|
122
|
+
| B사 | ... | ... | ... | ...% |
|
|
123
|
+
|
|
124
|
+
### 포지셔닝 맵
|
|
125
|
+
[2x2 매트릭스 설명]
|
|
126
|
+
|
|
127
|
+
## 3. 전략적 기회
|
|
128
|
+
|
|
129
|
+
### 빈 공간 (White Space)
|
|
130
|
+
- [기회 영역 1]
|
|
131
|
+
- [기회 영역 2]
|
|
132
|
+
|
|
133
|
+
### 차별화 기회
|
|
134
|
+
- [차별화 포인트 1]
|
|
135
|
+
- [차별화 포인트 2]
|
|
136
|
+
|
|
137
|
+
## 4. 핵심 시사점
|
|
138
|
+
- [시사점 1]
|
|
139
|
+
- [시사점 2]
|
|
140
|
+
- [시사점 3]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Usage
|
|
144
|
+
|
|
145
|
+
### 직접 호출
|
|
146
|
+
```
|
|
147
|
+
"한국 커피 시장의 경쟁 환경을 분석해줘"
|
|
148
|
+
"Porter's 5 Forces로 산업 구조를 분석해줘"
|
|
149
|
+
"경쟁사 포지셔닝 맵을 만들어줘"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 커맨드 연동
|
|
153
|
+
```bash
|
|
154
|
+
/competitive-landscape --market "Korean coffee retail"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Related Skills
|
|
158
|
+
|
|
159
|
+
- `analysis-reports`: 분석 리포트 템플릿
|
|
160
|
+
- `diagram-generator`: Porter's 5 Forces, 포지셔닝 맵 시각화
|
|
161
|
+
|
|
162
|
+
## Related Agents
|
|
163
|
+
|
|
164
|
+
- `market-analyst`: 시장 규모 및 성장 분석
|
|
165
|
+
- `competitive-analyst`: 개별 경쟁사 심층 분석 (brand-analytics)
|
|
166
|
+
|
|
167
|
+
## Notes
|
|
168
|
+
|
|
169
|
+
> **competitive-intelligence vs competitive-analyst**
|
|
170
|
+
> - `competitive-intelligence` (market-research): 산업 수준의 경쟁 구조 분석
|
|
171
|
+
> - `competitive-analyst` (brand-analytics): 개별 브랜드 관점의 경쟁사 분석
|