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.
@@ -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: '정보',
@@ -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: 'main',
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.2.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): 개별 브랜드 관점의 경쟁사 분석