makecc 0.2.13 → 0.2.15

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.
@@ -1,8 +1,7 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
1
  import { writeFile, mkdir } from 'fs/promises';
3
2
  import { join } from 'path';
4
3
  import { existsSync } from 'fs';
5
- import { skillExecutionService } from './skillExecutionService';
4
+ import { executeClaudeCli, buildNodePrompt } from './claudeCliService';
6
5
  import type {
7
6
  ExecutionNode,
8
7
  SubagentNodeData,
@@ -40,17 +39,15 @@ type ProgressCallback = (update: NodeExecutionUpdate) => void;
40
39
  type LogCallback = (type: 'info' | 'warn' | 'error' | 'debug', message: string) => void;
41
40
 
42
41
  /**
43
- * Anthropic API를 사용한 워크플로우 실행 서비스
42
+ * Claude CLI를 사용한 워크플로우 실행 서비스
44
43
  */
45
44
  export class WorkflowExecutionService {
46
- private client: Anthropic;
47
45
  private results: Map<string, ExecutionResult> = new Map();
48
46
  private outputDir: string = '';
47
+ private projectRoot: string = '';
49
48
 
50
49
  constructor() {
51
- this.client = new Anthropic({
52
- apiKey: process.env.ANTHROPIC_API_KEY,
53
- });
50
+ this.projectRoot = process.env.MAKECC_PROJECT_PATH || process.cwd();
54
51
  }
55
52
 
56
53
  /**
@@ -150,7 +147,7 @@ export class WorkflowExecutionService {
150
147
  }
151
148
 
152
149
  /**
153
- * Subagent 노드 실행 - Claude API로 작업 수행
150
+ * Subagent 노드 실행 - Claude CLI로 작업 수행
154
151
  */
155
152
  private async executeSubagentNode(
156
153
  node: ExecutionNode,
@@ -161,60 +158,39 @@ export class WorkflowExecutionService {
161
158
  const data = node.data as SubagentNodeData;
162
159
 
163
160
  onProgress?.({ nodeId: node.id, status: 'running', progress: 20 });
161
+ onLog?.('info', `claude -c 실행 중: ${data.label} (${data.role})`);
164
162
 
165
- // 역할별 시스템 프롬프트
166
- const rolePrompts: Record<string, string> = {
167
- researcher: `당신은 전문 리서처입니다. 주어진 주제에 대해 깊이 있는 조사를 수행하고, 핵심 정보를 정리하여 제공합니다.`,
168
- writer: `당신은 전문 작가입니다. 명확하고 매력적인 콘텐츠를 작성합니다. 사용자의 요구에 맞는 톤과 스타일로 글을 작성합니다.`,
169
- analyst: `당신은 데이터 분석가입니다. 정보를 분석하고 패턴을 파악하여 인사이트를 도출합니다.`,
170
- coder: `당신은 전문 개발자입니다. 깔끔하고 효율적인 코드를 작성하며, 모범 사례를 따릅니다.`,
171
- designer: `당신은 디자인 전문가입니다. 상세페이지, 배너, UI 등을 위한 디자인 가이드와 컨셉을 제안합니다.`,
172
- custom: `당신은 AI 어시스턴트입니다. 주어진 작업을 최선을 다해 수행합니다.`,
173
- };
174
-
175
- const systemPrompt = data.systemPrompt || rolePrompts[data.role] || rolePrompts.custom;
176
-
177
- const userMessage = `## 작업 설명
178
- ${data.description || '주어진 작업을 수행해주세요.'}
179
-
180
- ## 이전 단계 결과
181
- ${previousResults || '(없음)'}
182
-
183
- 위 내용을 바탕으로 작업을 수행하고 결과를 제공해주세요.`;
184
-
185
- onLog?.('debug', `Subagent "${data.label}" (${data.role}) 호출 중...`);
163
+ // 프롬프트 생성
164
+ const prompt = buildNodePrompt('subagent', data as unknown as Record<string, unknown>, previousResults);
186
165
 
187
166
  try {
188
167
  onProgress?.({ nodeId: node.id, status: 'running', progress: 40 });
189
168
 
190
- const modelId = this.getModelId(data.model);
191
-
192
- const response = await this.client.messages.create({
193
- model: modelId,
194
- max_tokens: 4096,
195
- system: systemPrompt,
196
- messages: [
197
- { role: 'user', content: userMessage }
198
- ],
169
+ const result = await executeClaudeCli({
170
+ prompt,
171
+ workingDirectory: this.projectRoot,
172
+ outputDirectory: this.outputDir,
173
+ timeoutMs: 300000, // 5분
199
174
  });
200
175
 
201
- onProgress?.({ nodeId: node.id, status: 'running', progress: 80 });
202
-
203
- // 응답 텍스트 추출
204
- const resultText = response.content
205
- .filter((block): block is Anthropic.TextBlock => block.type === 'text')
206
- .map((block) => block.text)
207
- .join('\n');
208
-
209
176
  onProgress?.({ nodeId: node.id, status: 'running', progress: 100 });
210
177
 
211
- return {
212
- nodeId: node.id,
213
- success: true,
214
- result: resultText,
215
- };
178
+ if (result.success) {
179
+ return {
180
+ nodeId: node.id,
181
+ success: true,
182
+ result: result.stdout,
183
+ files: result.generatedFiles,
184
+ };
185
+ } else {
186
+ return {
187
+ nodeId: node.id,
188
+ success: false,
189
+ error: result.stderr || 'Claude CLI 실행 실패',
190
+ };
191
+ }
216
192
  } catch (error) {
217
- const errorMsg = error instanceof Error ? error.message : 'Claude API 호출 실패';
193
+ const errorMsg = error instanceof Error ? error.message : 'Claude CLI 호출 실패';
218
194
  onLog?.('error', `Subagent 오류: ${errorMsg}`);
219
195
  return {
220
196
  nodeId: node.id,
@@ -225,7 +201,7 @@ ${previousResults || '(없음)'}
225
201
  }
226
202
 
227
203
  /**
228
- * Skill 노드 실행 - skillExecutionService 사용
204
+ * Skill 노드 실행 - Claude CLI로 스킬 실행
229
205
  */
230
206
  private async executeSkillNode(
231
207
  node: ExecutionNode,
@@ -237,25 +213,35 @@ ${previousResults || '(없음)'}
237
213
  const skillId = data.skillId || 'generic';
238
214
 
239
215
  onProgress?.({ nodeId: node.id, status: 'running', progress: 10 });
216
+ onLog?.('info', `claude -c 실행 중: /${skillId}`);
217
+
218
+ // 프롬프트 생성 - 스킬 호출 형태
219
+ const prompt = buildNodePrompt('skill', data as unknown as Record<string, unknown>, previousResults);
240
220
 
241
221
  try {
242
- // skillExecutionService를 사용하여 실제 파일 생성
243
- const result = await skillExecutionService.execute(
244
- skillId,
245
- previousResults,
246
- this.outputDir,
247
- onLog
248
- );
222
+ const result = await executeClaudeCli({
223
+ prompt,
224
+ workingDirectory: this.projectRoot,
225
+ outputDirectory: this.outputDir,
226
+ timeoutMs: 300000,
227
+ });
249
228
 
250
229
  onProgress?.({ nodeId: node.id, status: 'running', progress: 100 });
251
230
 
252
- return {
253
- nodeId: node.id,
254
- success: result.success,
255
- result: result.result,
256
- files: result.files,
257
- error: result.error,
258
- };
231
+ if (result.success) {
232
+ return {
233
+ nodeId: node.id,
234
+ success: true,
235
+ result: result.stdout,
236
+ files: result.generatedFiles,
237
+ };
238
+ } else {
239
+ return {
240
+ nodeId: node.id,
241
+ success: false,
242
+ error: result.stderr || '스킬 실행 실패',
243
+ };
244
+ }
259
245
  } catch (error) {
260
246
  const errorMsg = error instanceof Error ? error.message : '스킬 실행 실패';
261
247
  onLog?.('error', errorMsg);
@@ -268,7 +254,7 @@ ${previousResults || '(없음)'}
268
254
  }
269
255
 
270
256
  /**
271
- * MCP 노드 실행 - 외부 도구/서비스 연결
257
+ * MCP 노드 실행 - Claude CLI로 MCP 서버 연동
272
258
  */
273
259
  private async executeMcpNode(
274
260
  node: ExecutionNode,
@@ -279,51 +265,46 @@ ${previousResults || '(없음)'}
279
265
  const data = node.data as McpNodeData;
280
266
 
281
267
  onProgress?.({ nodeId: node.id, status: 'running', progress: 10 });
282
- onLog?.('info', `MCP 서버 "${data.serverName}" 연결 중...`);
268
+ onLog?.('info', `claude -c 실행 중: MCP 서버 "${data.serverName}"`);
283
269
 
284
- // MCP 서버 타입별 처리
285
- const mcpPrompt = `당신은 MCP (Model Context Protocol) 서버와 상호작용하는 전문가입니다.
270
+ const prompt = `MCP 서버를 사용하여 작업을 수행해주세요.
286
271
 
287
272
  ## MCP 서버 정보
288
273
  - 서버 이름: ${data.serverName}
289
274
  - 서버 타입: ${data.serverType}
290
- - 설정: ${JSON.stringify(data.serverConfig, null, 2)}
291
275
 
292
276
  ## 이전 단계 결과
293
- ${previousResults}
277
+ ${previousResults || '(없음)'}
294
278
 
295
279
  ## 작업
296
- 위 MCP 서버를 사용하여 이전 단계의 결과를 처리하세요.
297
-
298
- 다음 MCP 서버 유형에 따라 적절한 작업을 수행하세요:
299
- - PostgreSQL/데이터베이스: 데이터 조회 또는 저장
300
- - Notion/Google Drive: 문서 생성 또는 업데이트
301
- - Slack/Discord: 메시지 전송 시뮬레이션
302
- - GitHub/Jira: 이슈 또는 PR 관련 작업 시뮬레이션
303
-
304
- 작업 결과를 상세히 설명해주세요.`;
280
+ 위 MCP 서버를 사용하여 이전 단계의 결과를 처리하세요.`;
305
281
 
306
282
  try {
307
283
  onProgress?.({ nodeId: node.id, status: 'running', progress: 50 });
308
284
 
309
- const response = await this.client.messages.create({
310
- model: 'claude-sonnet-4-20250514',
311
- max_tokens: 4096,
312
- messages: [{ role: 'user', content: mcpPrompt }],
285
+ const result = await executeClaudeCli({
286
+ prompt,
287
+ workingDirectory: this.projectRoot,
288
+ outputDirectory: this.outputDir,
289
+ timeoutMs: 300000,
313
290
  });
314
291
 
315
- const result = response.content
316
- .filter((block): block is Anthropic.TextBlock => block.type === 'text')
317
- .map((block) => block.text)
318
- .join('\n');
319
-
320
292
  onProgress?.({ nodeId: node.id, status: 'running', progress: 100 });
321
293
 
322
- return {
323
- nodeId: node.id,
324
- success: true,
325
- result,
326
- };
294
+ if (result.success) {
295
+ return {
296
+ nodeId: node.id,
297
+ success: true,
298
+ result: result.stdout,
299
+ files: result.generatedFiles,
300
+ };
301
+ } else {
302
+ return {
303
+ nodeId: node.id,
304
+ success: false,
305
+ error: result.stderr || 'MCP 노드 실행 실패',
306
+ };
307
+ }
327
308
  } catch (error) {
328
309
  return {
329
310
  nodeId: node.id,
@@ -391,20 +372,6 @@ ${allFiles.map((f) => `- **${f.name}**: \`${f.path}\``).join('\n') || '없음'}
391
372
  }
392
373
  }
393
374
 
394
- /**
395
- * 모델 ID 변환
396
- */
397
- private getModelId(model?: 'sonnet' | 'opus' | 'haiku'): string {
398
- switch (model) {
399
- case 'opus':
400
- return 'claude-opus-4-20250514';
401
- case 'haiku':
402
- return 'claude-3-5-haiku-20241022';
403
- default:
404
- return 'claude-sonnet-4-20250514';
405
- }
406
- }
407
-
408
375
  /**
409
376
  * 이전 노드 결과 수집
410
377
  */