makecc 0.2.18 → 0.2.19

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.
@@ -0,0 +1,424 @@
1
+ import { promises as fs, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import { nanoid } from 'nanoid';
5
+
6
+ export interface Project {
7
+ id: string;
8
+ name: string;
9
+ description: string;
10
+ thumbnail?: string;
11
+ skillCount: number;
12
+ agentCount: number;
13
+ path: string;
14
+ lastModified: string;
15
+ createdAt: string;
16
+ }
17
+
18
+ export interface GalleryItem {
19
+ id: string;
20
+ name: string;
21
+ description: string;
22
+ thumbnail: string;
23
+ author: string;
24
+ downloads: number;
25
+ tags: string[];
26
+ }
27
+
28
+ // Default makecc home directory
29
+ const MAKECC_HOME = process.env.MAKECC_HOME || join(homedir(), 'makecc');
30
+
31
+ // Gallery items (static for now, could be fetched from a registry later)
32
+ const GALLERY_ITEMS: GalleryItem[] = [
33
+ {
34
+ id: 'gmail-assistant',
35
+ name: 'Gmail Assistant',
36
+ description: 'Read, write and organize emails using Claude',
37
+ thumbnail: '/gallery/gmail.png',
38
+ author: 'makecc',
39
+ downloads: 1234,
40
+ tags: ['email', 'productivity'],
41
+ },
42
+ {
43
+ id: 'web-scraper',
44
+ name: 'Web Scraper',
45
+ description: 'Extract data from websites automatically',
46
+ thumbnail: '/gallery/scraper.png',
47
+ author: 'makecc',
48
+ downloads: 892,
49
+ tags: ['data', 'automation'],
50
+ },
51
+ {
52
+ id: 'document-analyzer',
53
+ name: 'Document Analyzer',
54
+ description: 'Analyze and summarize PDF documents',
55
+ thumbnail: '/gallery/docs.png',
56
+ author: 'makecc',
57
+ downloads: 567,
58
+ tags: ['pdf', 'analysis'],
59
+ },
60
+ ];
61
+
62
+ class ProjectService {
63
+ private makeccHome: string;
64
+
65
+ constructor() {
66
+ this.makeccHome = MAKECC_HOME;
67
+ }
68
+
69
+ getMakeccHome(): string {
70
+ return this.makeccHome;
71
+ }
72
+
73
+ async ensureMakeccHome(): Promise<void> {
74
+ if (!existsSync(this.makeccHome)) {
75
+ await fs.mkdir(this.makeccHome, { recursive: true });
76
+ console.log(`Created makecc home directory: ${this.makeccHome}`);
77
+ }
78
+ }
79
+
80
+ async listProjects(): Promise<Project[]> {
81
+ await this.ensureMakeccHome();
82
+
83
+ const entries = await fs.readdir(this.makeccHome, { withFileTypes: true });
84
+ const projects: Project[] = [];
85
+
86
+ for (const entry of entries) {
87
+ if (!entry.isDirectory()) continue;
88
+ if (entry.name.startsWith('.')) continue;
89
+
90
+ const projectPath = join(this.makeccHome, entry.name);
91
+ const project = await this.loadProjectInfo(projectPath, entry.name);
92
+ if (project) {
93
+ projects.push(project);
94
+ }
95
+ }
96
+
97
+ // Sort by lastModified (most recent first)
98
+ projects.sort((a, b) =>
99
+ new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
100
+ );
101
+
102
+ return projects;
103
+ }
104
+
105
+ private async loadProjectInfo(projectPath: string, name: string): Promise<Project | null> {
106
+ try {
107
+ const stat = await fs.stat(projectPath);
108
+
109
+ // Read project.json if exists
110
+ const configPath = join(projectPath, 'project.json');
111
+ let config: { description?: string; id?: string; createdAt?: string } = {};
112
+
113
+ if (existsSync(configPath)) {
114
+ const content = await fs.readFile(configPath, 'utf-8');
115
+ config = JSON.parse(content);
116
+ }
117
+
118
+ // Count skills and agents
119
+ const claudePath = join(projectPath, '.claude');
120
+ let skillCount = 0;
121
+ let agentCount = 0;
122
+
123
+ if (existsSync(claudePath)) {
124
+ const skillsPath = join(claudePath, 'skills');
125
+ const agentsPath = join(claudePath, 'agents');
126
+
127
+ if (existsSync(skillsPath)) {
128
+ const skills = await fs.readdir(skillsPath, { withFileTypes: true });
129
+ skillCount = skills.filter(s => s.isDirectory()).length;
130
+ }
131
+
132
+ if (existsSync(agentsPath)) {
133
+ const agents = await fs.readdir(agentsPath);
134
+ agentCount = agents.filter(a => a.endsWith('.md')).length;
135
+ }
136
+ }
137
+
138
+ return {
139
+ id: config.id || name,
140
+ name,
141
+ description: config.description || '',
142
+ skillCount,
143
+ agentCount,
144
+ path: projectPath,
145
+ lastModified: stat.mtime.toISOString(),
146
+ createdAt: config.createdAt || stat.birthtime.toISOString(),
147
+ };
148
+ } catch (error) {
149
+ console.error(`Failed to load project info for ${name}:`, error);
150
+ return null;
151
+ }
152
+ }
153
+
154
+ async createProject(name: string, description: string): Promise<Project> {
155
+ await this.ensureMakeccHome();
156
+
157
+ // Sanitize project name
158
+ const sanitizedName = name
159
+ .toLowerCase()
160
+ .replace(/[^a-z0-9-]/g, '-')
161
+ .replace(/-+/g, '-')
162
+ .replace(/^-|-$/g, '');
163
+
164
+ const projectPath = join(this.makeccHome, sanitizedName);
165
+
166
+ if (existsSync(projectPath)) {
167
+ throw new Error(`Project "${sanitizedName}" already exists`);
168
+ }
169
+
170
+ // Create project directory structure
171
+ await fs.mkdir(projectPath, { recursive: true });
172
+ await fs.mkdir(join(projectPath, '.claude', 'skills'), { recursive: true });
173
+ await fs.mkdir(join(projectPath, '.claude', 'agents'), { recursive: true });
174
+
175
+ // Create project.json
176
+ const projectId = nanoid(10);
177
+ const now = new Date().toISOString();
178
+ const config = {
179
+ id: projectId,
180
+ name: sanitizedName,
181
+ description,
182
+ createdAt: now,
183
+ };
184
+
185
+ await fs.writeFile(
186
+ join(projectPath, 'project.json'),
187
+ JSON.stringify(config, null, 2)
188
+ );
189
+
190
+ // Create CLAUDE.md
191
+ const claudeMd = `# ${name}
192
+
193
+ ${description}
194
+
195
+ ## Project Structure
196
+
197
+ \`\`\`
198
+ ${sanitizedName}/
199
+ ├── .claude/
200
+ │ ├── skills/ # Claude Code skills
201
+ │ └── agents/ # Claude Code agents
202
+ └── project.json # Project configuration
203
+ \`\`\`
204
+
205
+ ## Getting Started
206
+
207
+ 1. Open this project in makecc
208
+ 2. Create skills using natural language
209
+ 3. Build workflows to automate tasks
210
+ `;
211
+
212
+ await fs.writeFile(join(projectPath, 'CLAUDE.md'), claudeMd);
213
+
214
+ return {
215
+ id: projectId,
216
+ name: sanitizedName,
217
+ description,
218
+ skillCount: 0,
219
+ agentCount: 0,
220
+ path: projectPath,
221
+ lastModified: now,
222
+ createdAt: now,
223
+ };
224
+ }
225
+
226
+ async deleteProject(projectId: string): Promise<void> {
227
+ const projects = await this.listProjects();
228
+ const project = projects.find(p => p.id === projectId || p.name === projectId);
229
+
230
+ if (!project) {
231
+ throw new Error(`Project "${projectId}" not found`);
232
+ }
233
+
234
+ // Move to trash instead of permanent delete
235
+ const trashPath = join(this.makeccHome, '.trash');
236
+ await fs.mkdir(trashPath, { recursive: true });
237
+
238
+ const trashName = `${project.name}_${Date.now()}`;
239
+ await fs.rename(project.path, join(trashPath, trashName));
240
+ }
241
+
242
+ async getProject(projectId: string): Promise<Project | null> {
243
+ const projects = await this.listProjects();
244
+ return projects.find(p => p.id === projectId || p.name === projectId) || null;
245
+ }
246
+
247
+ getGalleryItems(): GalleryItem[] {
248
+ return GALLERY_ITEMS;
249
+ }
250
+
251
+ async createSampleProjects(): Promise<void> {
252
+ await this.ensureMakeccHome();
253
+
254
+ // Check if samples already exist
255
+ const projects = await this.listProjects();
256
+ const hasGmail = projects.some(p => p.name === 'gmail-assistant');
257
+ const hasScraper = projects.some(p => p.name === 'web-scraper');
258
+
259
+ if (!hasGmail) {
260
+ await this.createGmailSampleProject();
261
+ }
262
+
263
+ if (!hasScraper) {
264
+ await this.createScraperSampleProject();
265
+ }
266
+ }
267
+
268
+ private async createGmailSampleProject(): Promise<void> {
269
+ const projectPath = join(this.makeccHome, 'gmail-assistant');
270
+ await fs.mkdir(projectPath, { recursive: true });
271
+ await fs.mkdir(join(projectPath, '.claude', 'skills', 'gmail'), { recursive: true });
272
+ await fs.mkdir(join(projectPath, '.claude', 'agents'), { recursive: true });
273
+
274
+ // project.json
275
+ await fs.writeFile(
276
+ join(projectPath, 'project.json'),
277
+ JSON.stringify({
278
+ id: 'gmail-sample',
279
+ name: 'gmail-assistant',
280
+ description: 'Read, write and organize emails using Claude',
281
+ createdAt: new Date().toISOString(),
282
+ }, null, 2)
283
+ );
284
+
285
+ // Sample skill: SKILL.md
286
+ const skillMd = `---
287
+ name: gmail
288
+ description: Gmail 읽기/쓰기 스킬
289
+ ---
290
+
291
+ # Gmail Skill
292
+
293
+ 이 스킬은 Gmail API를 사용하여 이메일을 읽고 쓸 수 있게 합니다.
294
+
295
+ ## 사용법
296
+
297
+ \`\`\`bash
298
+ python scripts/main.py read --count 10
299
+ python scripts/main.py send --to "user@example.com" --subject "Hello" --body "Hi there!"
300
+ \`\`\`
301
+
302
+ ## 기능
303
+
304
+ - 받은편지함 읽기
305
+ - 이메일 검색
306
+ - 이메일 작성 및 전송
307
+ - 라벨 관리
308
+ `;
309
+
310
+ await fs.writeFile(
311
+ join(projectPath, '.claude', 'skills', 'gmail', 'SKILL.md'),
312
+ skillMd
313
+ );
314
+
315
+ // Sample Python script
316
+ const mainPy = `#!/usr/bin/env python3
317
+ """Gmail skill for reading and writing emails."""
318
+
319
+ import argparse
320
+ import json
321
+
322
+ def read_emails(count: int = 10):
323
+ """Read recent emails from inbox."""
324
+ print(json.dumps({
325
+ "status": "success",
326
+ "message": f"Would read {count} emails from Gmail",
327
+ "note": "This is a sample - configure Gmail API credentials to use"
328
+ }))
329
+
330
+ def send_email(to: str, subject: str, body: str):
331
+ """Send an email."""
332
+ print(json.dumps({
333
+ "status": "success",
334
+ "message": f"Would send email to {to}",
335
+ "subject": subject,
336
+ "note": "This is a sample - configure Gmail API credentials to use"
337
+ }))
338
+
339
+ def main():
340
+ parser = argparse.ArgumentParser(description='Gmail Skill')
341
+ subparsers = parser.add_subparsers(dest='command', help='Commands')
342
+
343
+ # Read command
344
+ read_parser = subparsers.add_parser('read', help='Read emails')
345
+ read_parser.add_argument('--count', type=int, default=10, help='Number of emails')
346
+
347
+ # Send command
348
+ send_parser = subparsers.add_parser('send', help='Send email')
349
+ send_parser.add_argument('--to', required=True, help='Recipient')
350
+ send_parser.add_argument('--subject', required=True, help='Subject')
351
+ send_parser.add_argument('--body', required=True, help='Body')
352
+
353
+ args = parser.parse_args()
354
+
355
+ if args.command == 'read':
356
+ read_emails(args.count)
357
+ elif args.command == 'send':
358
+ send_email(args.to, args.subject, args.body)
359
+ else:
360
+ parser.print_help()
361
+
362
+ if __name__ == '__main__':
363
+ main()
364
+ `;
365
+
366
+ await fs.mkdir(join(projectPath, '.claude', 'skills', 'gmail', 'scripts'), { recursive: true });
367
+ await fs.writeFile(
368
+ join(projectPath, '.claude', 'skills', 'gmail', 'scripts', 'main.py'),
369
+ mainPy
370
+ );
371
+
372
+ console.log('Created sample project: gmail-assistant');
373
+ }
374
+
375
+ private async createScraperSampleProject(): Promise<void> {
376
+ const projectPath = join(this.makeccHome, 'web-scraper');
377
+ await fs.mkdir(projectPath, { recursive: true });
378
+ await fs.mkdir(join(projectPath, '.claude', 'agents'), { recursive: true });
379
+
380
+ // project.json
381
+ await fs.writeFile(
382
+ join(projectPath, 'project.json'),
383
+ JSON.stringify({
384
+ id: 'scraper-sample',
385
+ name: 'web-scraper',
386
+ description: 'Extract data from websites automatically',
387
+ createdAt: new Date().toISOString(),
388
+ }, null, 2)
389
+ );
390
+
391
+ // Sample agent
392
+ const agentMd = `---
393
+ name: web-scraper
394
+ description: 웹 페이지에서 데이터를 추출하는 에이전트
395
+ tools: WebFetch, Read, Write
396
+ model: sonnet
397
+ ---
398
+
399
+ # Web Scraper Agent
400
+
401
+ 이 에이전트는 웹 페이지에서 데이터를 추출하고 구조화합니다.
402
+
403
+ ## 워크플로우
404
+
405
+ 1. URL을 입력받습니다
406
+ 2. WebFetch로 페이지 내용을 가져옵니다
407
+ 3. 원하는 데이터를 추출합니다
408
+ 4. JSON 또는 CSV로 결과를 저장합니다
409
+
410
+ ## 사용 예시
411
+
412
+ "https://example.com에서 모든 제품 이름과 가격을 추출해줘"
413
+ `;
414
+
415
+ await fs.writeFile(
416
+ join(projectPath, '.claude', 'agents', 'web-scraper.md'),
417
+ agentMd
418
+ );
419
+
420
+ console.log('Created sample project: web-scraper');
421
+ }
422
+ }
423
+
424
+ export const projectService = new ProjectService();
@@ -42,23 +42,25 @@ export interface SkillProgressEvent {
42
42
 
43
43
  export type SkillProgressCallback = (event: SkillProgressEvent) => void;
44
44
 
45
- const SYSTEM_PROMPT = `You are a Claude Code skill generator. You MUST respond with ONLY a valid JSON object. No markdown, no code blocks, no explanations - just pure JSON.
45
+ const SYSTEM_PROMPT = `You are an expert Claude Code skill generator. Generate production-quality skills with complete, working code.
46
46
 
47
- Your response must follow this exact JSON schema:
47
+ RESPOND WITH ONLY A VALID JSON OBJECT - NO MARKDOWN, NO CODE BLOCKS, NO EXPLANATIONS.
48
+
49
+ ## JSON Schema
48
50
 
49
51
  {
50
- "skillName": "Human readable skill name",
52
+ "skillName": "Human Readable Skill Name",
51
53
  "skillId": "skill-id-in-kebab-case",
52
- "description": "Brief description of what this skill does",
54
+ "description": "Comprehensive description including what it does AND when to use it. Include trigger phrases in Korean. Example: 'PDF 문서에서 텍스트 추출 및 분석. \"PDF 읽어줘\", \"PDF 분석해줘\", \"PDF에서 텍스트 추출\" 등의 요청 시 사용.'",
53
55
  "files": [
54
56
  {
55
57
  "path": "SKILL.md",
56
- "content": "Full SKILL.md content here",
58
+ "content": "Full SKILL.md content (see format below)",
57
59
  "language": "markdown"
58
60
  },
59
61
  {
60
62
  "path": "scripts/main.py",
61
- "content": "Full Python script content here",
63
+ "content": "Full Python script (150-300 lines, production quality)",
62
64
  "language": "python"
63
65
  },
64
66
  {
@@ -69,36 +71,155 @@ Your response must follow this exact JSON schema:
69
71
  ]
70
72
  }
71
73
 
72
- SKILL.md must follow this format:
74
+ ## SKILL.md Format (MUST follow exactly)
75
+
73
76
  ---
74
77
  name: skill-id
75
- description: One line description
78
+ description: Detailed description with trigger phrases. Include what it does AND specific Korean trigger phrases like "~해줘", "~만들어줘". This is the PRIMARY mechanism for skill activation.
76
79
  ---
77
80
 
78
81
  # Skill Name
79
82
 
80
- ## When to use
81
- - Use case 1
82
- - Use case 2
83
+ Brief description of what this skill does.
84
+
85
+ ## 실행 요구사항 (필수)
86
+
87
+ List any prerequisites:
88
+ - API keys needed (with instructions to check/request)
89
+ - Environment setup
90
+ - Dependencies
91
+
92
+ ## 빠른 시작
93
+
94
+ \\\`\\\`\\\`bash
95
+ .venv/bin/python .claude/skills/skill-id/scripts/main.py \\\\
96
+ --required-arg "value" \\\\
97
+ --output output.ext
98
+ \\\`\\\`\\\`
99
+
100
+ ## 스크립트 옵션
101
+
102
+ | 옵션 | 설명 | 기본값 |
103
+ |------|------|--------|
104
+ | \\\`--arg1\\\`, \\\`-a\\\` | Description | default |
105
+ | \\\`--output\\\`, \\\`-o\\\` | 출력 경로 (필수) | - |
83
106
 
84
- ## Usage
85
- \`\`\`bash
86
- .venv/bin/python .claude/skills/skill-id/scripts/main.py [args]
87
- \`\`\`
107
+ ## 사용 예시
88
108
 
89
- ## Parameters
90
- - \`--param1\`: Description
109
+ ### 예시 1: Basic Usage
110
+ \\\`\\\`\\\`bash
111
+ .venv/bin/python .claude/skills/skill-id/scripts/main.py \\\\
112
+ --arg "value" --output result.ext
113
+ \\\`\\\`\\\`
91
114
 
92
- ## Example
93
- [Usage example]
115
+ ### 예시 2: Advanced Usage
116
+ \\\`\\\`\\\`bash
117
+ .venv/bin/python .claude/skills/skill-id/scripts/main.py \\\\
118
+ --arg "value" --advanced-option
119
+ \\\`\\\`\\\`
94
120
 
95
- RULES:
96
- 1. Generate COMPLETE, WORKING code - no placeholders
97
- 2. Include proper error handling with try-except
98
- 3. Use Korean for user-facing messages
99
- 4. Scripts run with .venv/bin/python (project local virtual environment)
100
- 5. List all required packages in requirements.txt
101
- 6. RESPOND WITH JSON ONLY - NO OTHER TEXT`;
121
+ ## 제한사항
122
+
123
+ - List any limitations or constraints
124
+
125
+ ## Python Script Requirements (scripts/main.py)
126
+
127
+ The script MUST:
128
+ 1. Be 150-300 lines of COMPLETE, WORKING code
129
+ 2. Use argparse with --help support
130
+ 3. Include comprehensive error handling (try-except)
131
+ 4. Print Korean status messages with emoji (✅ 완료, ❌ 오류, ⏳ 처리 중)
132
+ 5. Check dependencies at startup with helpful install instructions
133
+ 6. Support common use cases with sensible defaults
134
+ 7. Include docstring with usage examples
135
+
136
+ ## Script Template Structure
137
+
138
+ \\\`\\\`\\\`python
139
+ #!/usr/bin/env python3
140
+ """
141
+ Skill Name - Brief description
142
+
143
+ Usage:
144
+ python main.py --arg "value" --output output.ext
145
+
146
+ Examples:
147
+ python main.py --input data.txt --output result.txt
148
+ """
149
+
150
+ import argparse
151
+ import sys
152
+ from pathlib import Path
153
+
154
+ def check_dependencies():
155
+ """Check required packages"""
156
+ try:
157
+ import required_package
158
+ return True
159
+ except ImportError:
160
+ print("❌ required_package가 설치되어 있지 않습니다.")
161
+ print(" 설치: uv pip install --python .venv/bin/python required_package")
162
+ return False
163
+
164
+ def main_function(arg1, arg2, output_path):
165
+ """Main processing logic with error handling"""
166
+ print(f"⏳ 처리 중...")
167
+
168
+ try:
169
+ # Processing logic here
170
+ result = process(arg1, arg2)
171
+
172
+ # Save output
173
+ output_file = Path(output_path)
174
+ output_file.parent.mkdir(parents=True, exist_ok=True)
175
+
176
+ with open(output_file, 'w') as f:
177
+ f.write(result)
178
+
179
+ print(f"✅ 완료!")
180
+ print(f" 파일: {output_file}")
181
+ return str(output_file)
182
+
183
+ except Exception as e:
184
+ print(f"❌ 오류 발생: {e}")
185
+ sys.exit(1)
186
+
187
+ def main():
188
+ parser = argparse.ArgumentParser(
189
+ description="Skill description",
190
+ formatter_class=argparse.RawDescriptionHelpFormatter,
191
+ epilog=\"\"\"
192
+ 예시:
193
+ python main.py --input data.txt --output result.txt
194
+ python main.py --input data.txt --output result.txt --option
195
+ \"\"\"
196
+ )
197
+
198
+ parser.add_argument("--input", "-i", required=True, help="입력 파일")
199
+ parser.add_argument("--output", "-o", required=True, help="출력 경로")
200
+ parser.add_argument("--option", action="store_true", help="옵션 설명")
201
+
202
+ args = parser.parse_args()
203
+
204
+ if not check_dependencies():
205
+ sys.exit(1)
206
+
207
+ main_function(args.input, args.option, args.output)
208
+
209
+ if __name__ == "__main__":
210
+ main()
211
+ \\\`\\\`\\\`
212
+
213
+ ## Critical Rules
214
+
215
+ 1. GENERATE COMPLETE, WORKING CODE - NO PLACEHOLDERS, NO "# TODO", NO "pass"
216
+ 2. Scripts must be 150-300 lines with real implementation
217
+ 3. Include ALL necessary imports and helper functions
218
+ 4. Use Korean for user-facing messages, English for code/logs
219
+ 5. description field MUST include Korean trigger phrases
220
+ 6. SKILL.md MUST have complete usage examples with actual commands
221
+ 7. Always include requirements.txt with specific packages needed
222
+ 8. RESPOND WITH JSON ONLY - NO OTHER TEXT`;
102
223
 
103
224
  export class SkillGeneratorService {
104
225
  private projectRoot: string;
@@ -32,6 +32,7 @@ export interface AIGeneratedNode {
32
32
  // input
33
33
  inputType?: 'text' | 'file' | 'select';
34
34
  placeholder?: string;
35
+ defaultValue?: string; // 기본 입력값
35
36
  // agent
36
37
  role?: string;
37
38
  tools?: string[];
@@ -87,7 +88,8 @@ ${AVAILABLE_TOOLS.join(', ')}
87
88
  "description": "입력 설명 (한글 가능)",
88
89
  "config": {
89
90
  "inputType": "text",
90
- "placeholder": "입력 안내"
91
+ "placeholder": "입력 안내",
92
+ "defaultValue": "워크플로우에 적합한 예시 입력값"
91
93
  }
92
94
  },
93
95
  {
@@ -145,6 +147,7 @@ ${AVAILABLE_TOOLS.join(', ')}
145
147
  7. systemPrompt는 구체적이고 실행 가능한 지시사항으로 작성
146
148
  8. **중요: label은 반드시 영어로, kebab-case 형식으로 작성 (예: blog-writer, data-analyzer)**
147
149
  9. workflowName도 영어로 작성
150
+ 10. **필수: input 노드의 config에 반드시 defaultValue 포함 - 워크플로우 목적에 맞는 구체적인 예시 값을 한글로 제공**
148
151
 
149
152
  ## 예시
150
153
 
@@ -153,7 +156,7 @@ ${AVAILABLE_TOOLS.join(', ')}
153
156
  "workflowName": "Image Generation",
154
157
  "description": "3개의 이미지를 생성하는 워크플로우",
155
158
  "nodes": [
156
- { "type": "input", "label": "image-prompt", "description": "생성할 이미지에 대한 설명", "config": { "inputType": "text", "placeholder": "이미지 프롬프트 입력" } },
159
+ { "type": "input", "label": "image-prompt", "description": "생성할 이미지에 대한 설명", "config": { "inputType": "text", "placeholder": "이미지 프롬프트 입력", "defaultValue": "미래적인 도시 야경, 네온 사인이 빛나는 사이버펑크 스타일" } },
157
160
  { "type": "skill", "label": "image-generator", "description": "AI로 이미지 생성", "config": { "skillType": "official", "skillId": "image-gen-nanobanana" } },
158
161
  { "type": "output", "label": "generated-images", "description": "생성된 이미지 결과", "config": { "outputType": "image" } }
159
162
  ],
@@ -165,7 +168,7 @@ ${AVAILABLE_TOOLS.join(', ')}
165
168
  "workflowName": "Data Analysis Report",
166
169
  "description": "데이터를 분석하고 보고서를 작성하는 워크플로우",
167
170
  "nodes": [
168
- { "type": "input", "label": "data-file", "description": "분석할 데이터 파일", "config": { "inputType": "file" } },
171
+ { "type": "input", "label": "data-file", "description": "분석할 데이터 파일", "config": { "inputType": "file", "defaultValue": "2024년 1~3분기 매출 데이터: 1월 1200만, 2월 1350만, 3월 1100만, 4월 1500만, 5월 1650만, 6월 1400만, 7월 1800만, 8월 1750만, 9월 1900만" } },
169
172
  { "type": "agent", "label": "data-analyzer", "description": "데이터를 분석하고 인사이트 도출", "config": { "role": "analyst", "tools": ["Read", "Grep", "Glob"], "model": "sonnet", "systemPrompt": "주어진 데이터를 분석하여 핵심 인사이트를 도출하세요. 통계적 요약, 트렌드, 이상치를 파악하세요." } },
170
173
  { "type": "agent", "label": "report-writer", "description": "분석 결과로 보고서 작성", "config": { "role": "writer", "tools": ["Read", "Write"], "model": "opus", "systemPrompt": "분석 결과를 바탕으로 경영진을 위한 간결하고 명확한 보고서를 작성하세요." } },
171
174
  { "type": "output", "label": "analysis-report", "description": "최종 분석 보고서", "config": { "outputType": "document" } }