makecc 0.2.16 → 0.2.18
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/dist/client/assets/{index-Dlx6ST3a.css → index-1V3GQ_8q.css} +1 -1
- package/dist/client/assets/index-j_MJWOjN.js +67 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +11 -6
- package/dist/server/services/claudeCliService.js +1 -1
- package/dist/server/services/claudeService.js +1 -1
- package/dist/server/services/configLoaderService.js +10 -46
- package/dist/server/services/nodeSyncService.js +18 -41
- package/dist/server/services/terminalService.js +1 -1
- package/dist/server/services/workflowAIService.js +169 -25
- package/dist/server/services/workflowExecutionService.js +2 -2
- package/package.json +1 -1
- package/server/index.ts +13 -6
- package/server/services/claudeCliService.ts +1 -1
- package/server/services/claudeService.ts +1 -1
- package/server/services/configLoaderService.ts +13 -62
- package/server/services/nodeSyncService.ts +20 -50
- package/server/services/terminalService.ts +1 -1
- package/server/services/workflowAIService.ts +213 -27
- package/server/services/workflowExecutionService.ts +2 -2
- package/server/types.ts +1 -1
- package/dist/client/assets/index-Bir6nP2a.js +0 -150
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { skillGeneratorService } from './skillGeneratorService';
|
|
2
3
|
|
|
3
4
|
// API 설정 타입
|
|
4
5
|
export interface ApiSettings {
|
|
@@ -7,6 +8,14 @@ export interface ApiSettings {
|
|
|
7
8
|
proxyUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
// 진행 상황 콜백
|
|
12
|
+
export type WorkflowProgressCallback = (event: {
|
|
13
|
+
step: 'workflow' | 'skill' | 'agent' | 'completed';
|
|
14
|
+
message: string;
|
|
15
|
+
current?: number;
|
|
16
|
+
total?: number;
|
|
17
|
+
}) => void;
|
|
18
|
+
|
|
10
19
|
// AI가 생성하는 워크플로우 결과 타입
|
|
11
20
|
export interface AIWorkflowResult {
|
|
12
21
|
workflowName: string;
|
|
@@ -16,14 +25,14 @@ export interface AIWorkflowResult {
|
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
export interface AIGeneratedNode {
|
|
19
|
-
type: 'input' | '
|
|
28
|
+
type: 'input' | 'agent' | 'skill' | 'mcp' | 'output';
|
|
20
29
|
label: string;
|
|
21
30
|
description: string;
|
|
22
31
|
config: {
|
|
23
32
|
// input
|
|
24
33
|
inputType?: 'text' | 'file' | 'select';
|
|
25
34
|
placeholder?: string;
|
|
26
|
-
//
|
|
35
|
+
// agent
|
|
27
36
|
role?: string;
|
|
28
37
|
tools?: string[];
|
|
29
38
|
model?: string;
|
|
@@ -62,29 +71,29 @@ const SYSTEM_PROMPT = `당신은 Claude Code 워크플로우 설계 전문가입
|
|
|
62
71
|
## 사용 가능한 공식 스킬
|
|
63
72
|
${AVAILABLE_SKILLS.map(s => `- ${s.id}: ${s.description}`).join('\n')}
|
|
64
73
|
|
|
65
|
-
## 사용 가능한 도구 (
|
|
74
|
+
## 사용 가능한 도구 (agent의 tools에 사용)
|
|
66
75
|
${AVAILABLE_TOOLS.join(', ')}
|
|
67
76
|
|
|
68
77
|
## 응답 형식 (JSON)
|
|
69
78
|
반드시 아래 형식의 유효한 JSON으로 응답하세요. 다른 텍스트 없이 JSON만 반환하세요.
|
|
70
79
|
|
|
71
80
|
{
|
|
72
|
-
"workflowName": "
|
|
73
|
-
"description": "워크플로우 설명",
|
|
81
|
+
"workflowName": "Workflow Name in English",
|
|
82
|
+
"description": "워크플로우 설명 (한글 가능)",
|
|
74
83
|
"nodes": [
|
|
75
84
|
{
|
|
76
85
|
"type": "input",
|
|
77
|
-
"label": "
|
|
78
|
-
"description": "입력 설명",
|
|
86
|
+
"label": "input-name-in-english",
|
|
87
|
+
"description": "입력 설명 (한글 가능)",
|
|
79
88
|
"config": {
|
|
80
89
|
"inputType": "text",
|
|
81
90
|
"placeholder": "입력 안내"
|
|
82
91
|
}
|
|
83
92
|
},
|
|
84
93
|
{
|
|
85
|
-
"type": "
|
|
86
|
-
"label": "
|
|
87
|
-
"description": "에이전트 역할 설명",
|
|
94
|
+
"type": "agent",
|
|
95
|
+
"label": "agent-name-in-english",
|
|
96
|
+
"description": "에이전트 역할 설명 (한글 가능)",
|
|
88
97
|
"config": {
|
|
89
98
|
"role": "researcher|writer|analyst|coder|custom",
|
|
90
99
|
"tools": ["Read", "Write"],
|
|
@@ -94,8 +103,8 @@ ${AVAILABLE_TOOLS.join(', ')}
|
|
|
94
103
|
},
|
|
95
104
|
{
|
|
96
105
|
"type": "skill",
|
|
97
|
-
"label": "
|
|
98
|
-
"description": "스킬 설명",
|
|
106
|
+
"label": "skill-name-in-english",
|
|
107
|
+
"description": "스킬 설명 (한글 가능)",
|
|
99
108
|
"config": {
|
|
100
109
|
"skillType": "official",
|
|
101
110
|
"skillId": "image-gen-nanobanana"
|
|
@@ -103,18 +112,18 @@ ${AVAILABLE_TOOLS.join(', ')}
|
|
|
103
112
|
},
|
|
104
113
|
{
|
|
105
114
|
"type": "skill",
|
|
106
|
-
"label": "
|
|
107
|
-
"description": "커스텀 스킬 설명",
|
|
115
|
+
"label": "custom-skill-name",
|
|
116
|
+
"description": "커스텀 스킬 설명 (한글 가능)",
|
|
108
117
|
"config": {
|
|
109
118
|
"skillType": "custom",
|
|
110
119
|
"skillId": "my-custom-skill",
|
|
111
|
-
"skillContent": "---\\nname: my-custom-skill\\ndescription:
|
|
120
|
+
"skillContent": "---\\nname: my-custom-skill\\ndescription: Custom skill description\\n---\\n\\n# Skill Instructions\\n\\nSpecific instructions..."
|
|
112
121
|
}
|
|
113
122
|
},
|
|
114
123
|
{
|
|
115
124
|
"type": "output",
|
|
116
|
-
"label": "
|
|
117
|
-
"description": "출력 설명",
|
|
125
|
+
"label": "output-name-in-english",
|
|
126
|
+
"description": "출력 설명 (한글 가능)",
|
|
118
127
|
"config": {
|
|
119
128
|
"outputType": "auto|markdown|document|image"
|
|
120
129
|
}
|
|
@@ -130,34 +139,36 @@ ${AVAILABLE_TOOLS.join(', ')}
|
|
|
130
139
|
1. 항상 input 노드로 시작하고 output 노드로 종료
|
|
131
140
|
2. 기존 공식 스킬로 가능하면 official 스킬 사용
|
|
132
141
|
3. 새로운 기능이 필요하면 custom 스킬 생성 (skillContent에 SKILL.md 형식)
|
|
133
|
-
4.
|
|
142
|
+
4. agent는 복잡한 추론이나 다단계 작업에 사용
|
|
134
143
|
5. edges의 from/to는 nodes 배열의 인덱스 (0부터 시작)
|
|
135
144
|
6. 순차적으로 연결되지 않아도 됨 (병렬 처리, 합류 가능)
|
|
136
145
|
7. systemPrompt는 구체적이고 실행 가능한 지시사항으로 작성
|
|
146
|
+
8. **중요: label은 반드시 영어로, kebab-case 형식으로 작성 (예: blog-writer, data-analyzer)**
|
|
147
|
+
9. workflowName도 영어로 작성
|
|
137
148
|
|
|
138
149
|
## 예시
|
|
139
150
|
|
|
140
151
|
### 예시 1: "이미지 3개 만들어줘"
|
|
141
152
|
{
|
|
142
|
-
"workflowName": "
|
|
153
|
+
"workflowName": "Image Generation",
|
|
143
154
|
"description": "3개의 이미지를 생성하는 워크플로우",
|
|
144
155
|
"nodes": [
|
|
145
|
-
{ "type": "input", "label": "
|
|
146
|
-
{ "type": "skill", "label": "
|
|
147
|
-
{ "type": "output", "label": "
|
|
156
|
+
{ "type": "input", "label": "image-prompt", "description": "생성할 이미지에 대한 설명", "config": { "inputType": "text", "placeholder": "이미지 프롬프트 입력" } },
|
|
157
|
+
{ "type": "skill", "label": "image-generator", "description": "AI로 이미지 생성", "config": { "skillType": "official", "skillId": "image-gen-nanobanana" } },
|
|
158
|
+
{ "type": "output", "label": "generated-images", "description": "생성된 이미지 결과", "config": { "outputType": "image" } }
|
|
148
159
|
],
|
|
149
160
|
"edges": [{ "from": 0, "to": 1 }, { "from": 1, "to": 2 }]
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
### 예시 2: "데이터 분석해서 보고서 만들어줘"
|
|
153
164
|
{
|
|
154
|
-
"workflowName": "
|
|
165
|
+
"workflowName": "Data Analysis Report",
|
|
155
166
|
"description": "데이터를 분석하고 보고서를 작성하는 워크플로우",
|
|
156
167
|
"nodes": [
|
|
157
|
-
{ "type": "input", "label": "
|
|
158
|
-
{ "type": "
|
|
159
|
-
{ "type": "
|
|
160
|
-
{ "type": "output", "label": "
|
|
168
|
+
{ "type": "input", "label": "data-file", "description": "분석할 데이터 파일", "config": { "inputType": "file" } },
|
|
169
|
+
{ "type": "agent", "label": "data-analyzer", "description": "데이터를 분석하고 인사이트 도출", "config": { "role": "analyst", "tools": ["Read", "Grep", "Glob"], "model": "sonnet", "systemPrompt": "주어진 데이터를 분석하여 핵심 인사이트를 도출하세요. 통계적 요약, 트렌드, 이상치를 파악하세요." } },
|
|
170
|
+
{ "type": "agent", "label": "report-writer", "description": "분석 결과로 보고서 작성", "config": { "role": "writer", "tools": ["Read", "Write"], "model": "opus", "systemPrompt": "분석 결과를 바탕으로 경영진을 위한 간결하고 명확한 보고서를 작성하세요." } },
|
|
171
|
+
{ "type": "output", "label": "analysis-report", "description": "최종 분석 보고서", "config": { "outputType": "document" } }
|
|
161
172
|
],
|
|
162
173
|
"edges": [{ "from": 0, "to": 1 }, { "from": 1, "to": 2 }, { "from": 2, "to": 3 }]
|
|
163
174
|
}
|
|
@@ -272,6 +283,181 @@ export class WorkflowAIService {
|
|
|
272
283
|
|
|
273
284
|
return result;
|
|
274
285
|
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 워크플로우 생성 + 재귀적 노드 확장
|
|
289
|
+
* 각 custom 스킬과 에이전트에 대해 상세 내용 생성
|
|
290
|
+
*/
|
|
291
|
+
async generateWithExpansion(
|
|
292
|
+
prompt: string,
|
|
293
|
+
settings?: ApiSettings,
|
|
294
|
+
onProgress?: WorkflowProgressCallback
|
|
295
|
+
): Promise<AIWorkflowResult> {
|
|
296
|
+
// 1. 워크플로우 구조 생성
|
|
297
|
+
onProgress?.({ step: 'workflow', message: '워크플로우 구조를 생성하고 있습니다...' });
|
|
298
|
+
const result = await this.generate(prompt, settings);
|
|
299
|
+
|
|
300
|
+
// 2. 확장이 필요한 노드 식별
|
|
301
|
+
const customSkills = result.nodes.filter(
|
|
302
|
+
(n) => n.type === 'skill' && n.config.skillType === 'custom'
|
|
303
|
+
);
|
|
304
|
+
const agents = result.nodes.filter((n) => n.type === 'agent');
|
|
305
|
+
|
|
306
|
+
const totalExpansions = customSkills.length + agents.length;
|
|
307
|
+
let current = 0;
|
|
308
|
+
|
|
309
|
+
// 3. 각 custom 스킬 확장
|
|
310
|
+
for (const skill of customSkills) {
|
|
311
|
+
current++;
|
|
312
|
+
onProgress?.({
|
|
313
|
+
step: 'skill',
|
|
314
|
+
message: `스킬 "${skill.label}" 상세 생성 중...`,
|
|
315
|
+
current,
|
|
316
|
+
total: totalExpansions,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
// skillGeneratorService를 사용하여 완전한 스킬 생성
|
|
321
|
+
const skillPrompt = this.buildSkillPrompt(skill, result);
|
|
322
|
+
const skillResult = await skillGeneratorService.generate(skillPrompt, settings);
|
|
323
|
+
|
|
324
|
+
if (skillResult.success && skillResult.skill) {
|
|
325
|
+
// 생성된 스킬 정보로 노드 업데이트
|
|
326
|
+
skill.config.skillId = skillResult.skill.skillId;
|
|
327
|
+
skill.config.skillContent = undefined; // 파일로 저장되었으므로 제거
|
|
328
|
+
// savedPath는 로그로만 출력 (타입에 없음)
|
|
329
|
+
console.log(`Skill saved to: ${skillResult.savedPath}`);
|
|
330
|
+
}
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error(`Failed to expand skill ${skill.label}:`, error);
|
|
333
|
+
// 실패해도 계속 진행
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 4. 각 에이전트 확장 (상세 systemPrompt 생성)
|
|
338
|
+
for (const agent of agents) {
|
|
339
|
+
current++;
|
|
340
|
+
onProgress?.({
|
|
341
|
+
step: 'agent',
|
|
342
|
+
message: `에이전트 "${agent.label}" 상세 생성 중...`,
|
|
343
|
+
current,
|
|
344
|
+
total: totalExpansions,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const expandedPrompt = await this.expandAgentPrompt(agent, result, settings);
|
|
349
|
+
agent.config.systemPrompt = expandedPrompt;
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error(`Failed to expand agent ${agent.label}:`, error);
|
|
352
|
+
// 실패해도 계속 진행
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
onProgress?.({ step: 'completed', message: '워크플로우 생성 완료!' });
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 스킬 생성을 위한 상세 프롬프트 빌드
|
|
362
|
+
*/
|
|
363
|
+
private buildSkillPrompt(skill: AIGeneratedNode, workflow: AIWorkflowResult): string {
|
|
364
|
+
// 워크플로우에서 이 스킬의 연결 관계 파악
|
|
365
|
+
const skillIndex = workflow.nodes.indexOf(skill);
|
|
366
|
+
const upstreamNodes = workflow.edges
|
|
367
|
+
.filter((e) => e.to === skillIndex)
|
|
368
|
+
.map((e) => workflow.nodes[e.from]);
|
|
369
|
+
const downstreamNodes = workflow.edges
|
|
370
|
+
.filter((e) => e.from === skillIndex)
|
|
371
|
+
.map((e) => workflow.nodes[e.to]);
|
|
372
|
+
|
|
373
|
+
const contextParts = [
|
|
374
|
+
`스킬 이름: ${skill.label}`,
|
|
375
|
+
`설명: ${skill.description}`,
|
|
376
|
+
`워크플로우: ${workflow.workflowName}`,
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
if (upstreamNodes.length > 0) {
|
|
380
|
+
contextParts.push(
|
|
381
|
+
`이전 단계: ${upstreamNodes.map((n) => `${n.label} (${n.type})`).join(', ')}`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (downstreamNodes.length > 0) {
|
|
386
|
+
contextParts.push(
|
|
387
|
+
`다음 단계: ${downstreamNodes.map((n) => `${n.label} (${n.type})`).join(', ')}`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (skill.config.skillContent) {
|
|
392
|
+
contextParts.push(`기본 내용:\n${skill.config.skillContent}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return `다음 스킬을 생성해주세요:\n\n${contextParts.join('\n')}`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* 에이전트의 systemPrompt를 상세하게 확장
|
|
400
|
+
*/
|
|
401
|
+
private async expandAgentPrompt(
|
|
402
|
+
agent: AIGeneratedNode,
|
|
403
|
+
workflow: AIWorkflowResult,
|
|
404
|
+
settings?: ApiSettings
|
|
405
|
+
): Promise<string> {
|
|
406
|
+
const client = this.getClient(settings);
|
|
407
|
+
|
|
408
|
+
// 워크플로우에서 이 에이전트의 연결 관계 파악
|
|
409
|
+
const agentIndex = workflow.nodes.indexOf(agent);
|
|
410
|
+
const upstreamNodes = workflow.edges
|
|
411
|
+
.filter((e) => e.to === agentIndex)
|
|
412
|
+
.map((e) => workflow.nodes[e.from]);
|
|
413
|
+
const downstreamNodes = workflow.edges
|
|
414
|
+
.filter((e) => e.from === agentIndex)
|
|
415
|
+
.map((e) => workflow.nodes[e.to]);
|
|
416
|
+
|
|
417
|
+
const systemPrompt = `You are an expert at writing detailed system prompts for AI agents.
|
|
418
|
+
Given an agent's context, generate a comprehensive system prompt that:
|
|
419
|
+
1. Clearly defines the agent's role and responsibilities
|
|
420
|
+
2. Specifies input/output expectations
|
|
421
|
+
3. Provides step-by-step instructions
|
|
422
|
+
4. Includes best practices and constraints
|
|
423
|
+
5. Is written in Korean for user-facing parts
|
|
424
|
+
|
|
425
|
+
Respond with ONLY the system prompt text, no explanations or formatting.`;
|
|
426
|
+
|
|
427
|
+
const userPrompt = `워크플로우: ${workflow.workflowName}
|
|
428
|
+
워크플로우 설명: ${workflow.description}
|
|
429
|
+
|
|
430
|
+
에이전트 정보:
|
|
431
|
+
- 이름: ${agent.label}
|
|
432
|
+
- 설명: ${agent.description}
|
|
433
|
+
- 역할: ${agent.config.role || 'custom'}
|
|
434
|
+
- 도구: ${(agent.config.tools || []).join(', ')}
|
|
435
|
+
- 모델: ${agent.config.model || 'sonnet'}
|
|
436
|
+
|
|
437
|
+
${upstreamNodes.length > 0 ? `이전 단계에서 받는 입력:\n${upstreamNodes.map((n) => `- ${n.label}: ${n.description}`).join('\n')}` : ''}
|
|
438
|
+
|
|
439
|
+
${downstreamNodes.length > 0 ? `다음 단계로 전달할 출력:\n${downstreamNodes.map((n) => `- ${n.label}: ${n.description}`).join('\n')}` : ''}
|
|
440
|
+
|
|
441
|
+
${agent.config.systemPrompt ? `기존 프롬프트 (확장 필요):\n${agent.config.systemPrompt}` : ''}
|
|
442
|
+
|
|
443
|
+
이 에이전트를 위한 상세하고 실행 가능한 system prompt를 작성해주세요.`;
|
|
444
|
+
|
|
445
|
+
const response = await client.messages.create({
|
|
446
|
+
model: 'claude-sonnet-4-20250514',
|
|
447
|
+
max_tokens: 2048,
|
|
448
|
+
system: systemPrompt,
|
|
449
|
+
messages: [{ role: 'user', content: userPrompt }],
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
let result = '';
|
|
453
|
+
for (const block of response.content) {
|
|
454
|
+
if (block.type === 'text') {
|
|
455
|
+
result += block.text;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return result.trim() || agent.config.systemPrompt || agent.description;
|
|
460
|
+
}
|
|
275
461
|
}
|
|
276
462
|
|
|
277
463
|
export const workflowAIService = new WorkflowAIService();
|
|
@@ -112,7 +112,7 @@ export class WorkflowExecutionService {
|
|
|
112
112
|
case 'input':
|
|
113
113
|
return this.executeInputNode(node, context.inputs);
|
|
114
114
|
|
|
115
|
-
case '
|
|
115
|
+
case 'agent':
|
|
116
116
|
return this.executeSubagentNode(node, previousResults, onProgress, onLog);
|
|
117
117
|
|
|
118
118
|
case 'skill':
|
|
@@ -161,7 +161,7 @@ export class WorkflowExecutionService {
|
|
|
161
161
|
onLog?.('info', `claude -c 실행 중: ${data.label} (${data.role})`);
|
|
162
162
|
|
|
163
163
|
// 프롬프트 생성
|
|
164
|
-
const prompt = buildNodePrompt('
|
|
164
|
+
const prompt = buildNodePrompt('agent', data as unknown as Record<string, unknown>, previousResults);
|
|
165
165
|
|
|
166
166
|
try {
|
|
167
167
|
onProgress?.({ nodeId: node.id, status: 'running', progress: 40 });
|
package/server/types.ts
CHANGED
|
@@ -74,7 +74,7 @@ export type WorkflowNodeData =
|
|
|
74
74
|
// Node structure for execution
|
|
75
75
|
export interface ExecutionNode {
|
|
76
76
|
id: string;
|
|
77
|
-
type: 'input' | '
|
|
77
|
+
type: 'input' | 'agent' | 'skill' | 'mcp' | 'output';
|
|
78
78
|
data: WorkflowNodeData;
|
|
79
79
|
position: { x: number; y: number };
|
|
80
80
|
}
|