makecc 0.2.15 → 0.2.17

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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>makecc</title>
8
- <script type="module" crossorigin src="/assets/index-PJfSWqWr.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-CXXgu628.css">
8
+ <script type="module" crossorigin src="/assets/index-CzDuVmB7.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-1V3GQ_8q.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -128,11 +128,11 @@ app.post('/api/sync/node', async (req, res) => {
128
128
  // Delete node from file system
129
129
  app.delete('/api/sync/node', async (req, res) => {
130
130
  try {
131
- const { node } = req.body;
131
+ const { node, nodes } = req.body;
132
132
  if (!node) {
133
133
  return res.status(400).json({ message: 'Node data is required' });
134
134
  }
135
- const result = await nodeSyncService.deleteNode(node);
135
+ const result = await nodeSyncService.deleteNode(node, nodes);
136
136
  if (result.success) {
137
137
  res.json(result);
138
138
  }
@@ -219,7 +219,7 @@ app.post('/api/generate/skill', async (req, res) => {
219
219
  app.get('/api/load/claude-config', async (req, res) => {
220
220
  try {
221
221
  const config = await configLoaderService.loadAll();
222
- console.log(`Loaded config: ${config.skills.length} skills, ${config.subagents.length} agents, ` +
222
+ console.log(`Loaded config: ${config.skills.length} skills, ${config.agents.length} agents, ` +
223
223
  `${config.commands.length} commands, ${config.hooks.length} hooks`);
224
224
  res.json(config);
225
225
  }
@@ -161,7 +161,7 @@ function findNewFiles(before, after) {
161
161
  */
162
162
  export function buildNodePrompt(nodeType, nodeData, previousResults) {
163
163
  const lines = [];
164
- if (nodeType === 'subagent') {
164
+ if (nodeType === 'agent') {
165
165
  const role = nodeData.role || 'assistant';
166
166
  const description = nodeData.description || '';
167
167
  const systemPrompt = nodeData.systemPrompt || '';
@@ -21,7 +21,7 @@ export class ClaudeService {
21
21
  switch (node.type) {
22
22
  case 'input':
23
23
  return this.executeInputNode(node.id, node.data);
24
- case 'subagent':
24
+ case 'agent':
25
25
  return this.executeSubagentNode(node.id, node.data, onProgress);
26
26
  case 'skill':
27
27
  return this.executeSkillNode(node.id, node.data);
@@ -9,13 +9,13 @@ export class ConfigLoaderService {
9
9
  * .claude/ 디렉토리에서 모든 설정 로드
10
10
  */
11
11
  async loadAll() {
12
- const [skills, subagents, commands, hooks] = await Promise.all([
12
+ const [skills, agents, commands, hooks] = await Promise.all([
13
13
  this.loadSkills(),
14
14
  this.loadSubagents(),
15
15
  this.loadCommands(),
16
16
  this.loadHooks(),
17
17
  ]);
18
- return { skills, subagents, commands, hooks };
18
+ return { skills, agents, commands, hooks };
19
19
  }
20
20
  /**
21
21
  * .claude/skills/ 에서 스킬 로드
@@ -56,7 +56,7 @@ export class ConfigLoaderService {
56
56
  */
57
57
  async loadSubagents() {
58
58
  const agentsDir = path.join(this.projectRoot, '.claude', 'agents');
59
- const subagents = [];
59
+ const agents = [];
60
60
  try {
61
61
  const entries = await fs.readdir(agentsDir, { withFileTypes: true });
62
62
  for (const entry of entries) {
@@ -66,9 +66,9 @@ export class ConfigLoaderService {
66
66
  const content = await fs.readFile(agentPath, 'utf-8');
67
67
  const parsed = this.parseFrontmatter(content);
68
68
  const agentName = entry.name.replace('.md', '');
69
- subagents.push({
70
- id: `subagent-${agentName}`,
71
- type: 'subagent',
69
+ agents.push({
70
+ id: `agent-${agentName}`,
71
+ type: 'agent',
72
72
  label: parsed.frontmatter.name || agentName,
73
73
  description: parsed.frontmatter.description || '',
74
74
  tools: this.parseList(parsed.frontmatter.tools),
@@ -86,7 +86,7 @@ export class ConfigLoaderService {
86
86
  catch {
87
87
  // agents 디렉토리가 없으면 빈 배열 반환
88
88
  }
89
- return subagents;
89
+ return agents;
90
90
  }
91
91
  /**
92
92
  * .claude/commands/ 에서 커맨드 로드
@@ -13,10 +13,8 @@ export class NodeSyncService {
13
13
  switch (node.type) {
14
14
  case 'skill':
15
15
  return await this.syncSkillNode(node);
16
- case 'subagent':
17
- return await this.syncSubagentNode(node);
18
- case 'command':
19
- return await this.syncCommandNode(node);
16
+ case 'agent':
17
+ return await this.syncAgentNode(node);
20
18
  case 'hook':
21
19
  return await this.syncHookNode(node);
22
20
  case 'input':
@@ -33,10 +31,16 @@ export class NodeSyncService {
33
31
  }
34
32
  }
35
33
  /**
36
- * 노드 삭제 시 파일 삭제
34
+ * 노드 삭제 시 파일 삭제 및 관련 참조 정리
37
35
  */
38
- async deleteNode(node) {
36
+ async deleteNode(node, allNodes) {
39
37
  try {
38
+ const nodeId = this.getNodeIdentifier(node);
39
+ // 먼저 관련 노드들에서 참조 제거
40
+ if (allNodes) {
41
+ await this.removeReferencesToNode(nodeId, node.type, allNodes);
42
+ }
43
+ // 파일 삭제
40
44
  switch (node.type) {
41
45
  case 'skill':
42
46
  if (node.skillId) {
@@ -44,16 +48,11 @@ export class NodeSyncService {
44
48
  await fs.rm(skillPath, { recursive: true, force: true });
45
49
  }
46
50
  break;
47
- case 'subagent':
51
+ case 'agent':
48
52
  const agentName = this.toKebabCase(node.label);
49
53
  const agentPath = path.join(this.projectRoot, '.claude', 'agents', `${agentName}.md`);
50
54
  await fs.unlink(agentPath).catch(() => { });
51
55
  break;
52
- case 'command':
53
- const cmdName = node.commandName || this.toKebabCase(node.label);
54
- const cmdPath = path.join(this.projectRoot, '.claude', 'commands', `${cmdName}.md`);
55
- await fs.unlink(cmdPath).catch(() => { });
56
- break;
57
56
  case 'hook':
58
57
  await this.removeHookFromSettings(node);
59
58
  break;
@@ -65,6 +64,36 @@ export class NodeSyncService {
65
64
  return { success: false, error: errorMessage };
66
65
  }
67
66
  }
67
+ /**
68
+ * 삭제되는 노드에 대한 모든 참조를 관련 노드에서 제거
69
+ */
70
+ async removeReferencesToNode(nodeId, nodeType, allNodes) {
71
+ for (const relatedNode of allNodes) {
72
+ if (relatedNode.type === 'agent') {
73
+ // 서브에이전트의 skills 배열에서 삭제된 노드 제거
74
+ if (relatedNode.skills?.includes(nodeId)) {
75
+ relatedNode.skills = relatedNode.skills.filter(s => s !== nodeId);
76
+ await this.syncAgentNode(relatedNode);
77
+ }
78
+ }
79
+ else if (relatedNode.type === 'skill') {
80
+ let updated = false;
81
+ // 스킬의 upstream에서 삭제된 노드 제거
82
+ if (relatedNode.upstream?.includes(nodeId)) {
83
+ relatedNode.upstream = relatedNode.upstream.filter(s => s !== nodeId);
84
+ updated = true;
85
+ }
86
+ // 스킬의 downstream에서 삭제된 노드 제거
87
+ if (relatedNode.downstream?.includes(nodeId)) {
88
+ relatedNode.downstream = relatedNode.downstream.filter(s => s !== nodeId);
89
+ updated = true;
90
+ }
91
+ if (updated) {
92
+ await this.syncSkillNode(relatedNode);
93
+ }
94
+ }
95
+ }
96
+ }
68
97
  /**
69
98
  * 엣지 연결 시 관계 업데이트
70
99
  * - source의 downstream에 target 추가
@@ -80,13 +109,13 @@ export class NodeSyncService {
80
109
  const sourceId = this.getNodeIdentifier(sourceNode);
81
110
  const targetId = this.getNodeIdentifier(targetNode);
82
111
  // 서브에이전트 → 스킬 연결
83
- if (sourceNode.type === 'subagent' && targetNode.type === 'skill') {
112
+ if (sourceNode.type === 'agent' && targetNode.type === 'skill') {
84
113
  // 에이전트의 skills 필드 업데이트
85
114
  const skills = sourceNode.skills || [];
86
115
  if (!skills.includes(targetId)) {
87
116
  skills.push(targetId);
88
117
  sourceNode.skills = skills;
89
- await this.syncSubagentNode(sourceNode);
118
+ await this.syncAgentNode(sourceNode);
90
119
  }
91
120
  // 스킬의 upstream 필드 업데이트
92
121
  const upstream = targetNode.upstream || [];
@@ -114,13 +143,13 @@ export class NodeSyncService {
114
143
  }
115
144
  }
116
145
  // 스킬 → 서브에이전트 연결
117
- if (sourceNode.type === 'skill' && targetNode.type === 'subagent') {
146
+ if (sourceNode.type === 'skill' && targetNode.type === 'agent') {
118
147
  // 에이전트의 upstream skills 필드 업데이트
119
148
  const skills = targetNode.skills || [];
120
149
  if (!skills.includes(sourceId)) {
121
150
  skills.push(sourceId);
122
151
  targetNode.skills = skills;
123
- await this.syncSubagentNode(targetNode);
152
+ await this.syncAgentNode(targetNode);
124
153
  }
125
154
  // 스킬의 downstream 필드 업데이트
126
155
  const downstream = sourceNode.downstream || [];
@@ -131,13 +160,13 @@ export class NodeSyncService {
131
160
  }
132
161
  }
133
162
  // 서브에이전트 → 서브에이전트 연결
134
- if (sourceNode.type === 'subagent' && targetNode.type === 'subagent') {
163
+ if (sourceNode.type === 'agent' && targetNode.type === 'agent') {
135
164
  // source의 downstream agents
136
165
  const sourceDownstream = sourceNode.skills || [];
137
166
  if (!sourceDownstream.includes(targetId)) {
138
167
  sourceDownstream.push(targetId);
139
168
  sourceNode.skills = sourceDownstream;
140
- await this.syncSubagentNode(sourceNode);
169
+ await this.syncAgentNode(sourceNode);
141
170
  }
142
171
  }
143
172
  return { success: true };
@@ -169,10 +198,10 @@ export class NodeSyncService {
169
198
  const sourceId = this.getNodeIdentifier(sourceNode);
170
199
  const targetId = this.getNodeIdentifier(targetNode);
171
200
  // 서브에이전트 → 스킬 연결 해제
172
- if (sourceNode.type === 'subagent' && targetNode.type === 'skill') {
201
+ if (sourceNode.type === 'agent' && targetNode.type === 'skill') {
173
202
  // 에이전트에서 스킬 제거
174
203
  sourceNode.skills = (sourceNode.skills || []).filter(s => s !== targetId);
175
- await this.syncSubagentNode(sourceNode);
204
+ await this.syncAgentNode(sourceNode);
176
205
  // 스킬에서 upstream 제거
177
206
  targetNode.upstream = (targetNode.upstream || []).filter(s => s !== sourceId);
178
207
  await this.syncSkillNode(targetNode);
@@ -185,16 +214,16 @@ export class NodeSyncService {
185
214
  await this.syncSkillNode(targetNode);
186
215
  }
187
216
  // 스킬 → 서브에이전트 연결 해제
188
- if (sourceNode.type === 'skill' && targetNode.type === 'subagent') {
217
+ if (sourceNode.type === 'skill' && targetNode.type === 'agent') {
189
218
  targetNode.skills = (targetNode.skills || []).filter(s => s !== sourceId);
190
- await this.syncSubagentNode(targetNode);
219
+ await this.syncAgentNode(targetNode);
191
220
  sourceNode.downstream = (sourceNode.downstream || []).filter(s => s !== targetId);
192
221
  await this.syncSkillNode(sourceNode);
193
222
  }
194
223
  // 서브에이전트 → 서브에이전트 연결 해제
195
- if (sourceNode.type === 'subagent' && targetNode.type === 'subagent') {
224
+ if (sourceNode.type === 'agent' && targetNode.type === 'agent') {
196
225
  sourceNode.skills = (sourceNode.skills || []).filter(s => s !== targetId);
197
- await this.syncSubagentNode(sourceNode);
226
+ await this.syncAgentNode(sourceNode);
198
227
  }
199
228
  return { success: true };
200
229
  }
@@ -256,15 +285,29 @@ ${frontmatterStr}
256
285
  await fs.writeFile(skillMdPath, content, 'utf-8');
257
286
  return { success: true, path: skillPath };
258
287
  }
259
- async syncSubagentNode(node) {
288
+ async syncAgentNode(node) {
260
289
  const agentName = this.toKebabCase(node.label);
261
290
  const agentsDir = path.join(this.projectRoot, '.claude', 'agents');
262
291
  const agentPath = path.join(agentsDir, `${agentName}.md`);
263
292
  await fs.mkdir(agentsDir, { recursive: true });
264
- // Frontmatter 구성
293
+ // 기존 파일 읽기 시도
294
+ let existingContent = '';
295
+ let existingFrontmatter = {};
296
+ let existingBody = '';
297
+ try {
298
+ existingContent = await fs.readFile(agentPath, 'utf-8');
299
+ const parsed = this.parseFrontmatter(existingContent);
300
+ existingFrontmatter = parsed.frontmatter;
301
+ existingBody = parsed.body;
302
+ }
303
+ catch {
304
+ // 파일 없음 - 새로 생성
305
+ }
306
+ // Frontmatter 구성 - 기존 값 유지하면서 업데이트
265
307
  const frontmatter = {
308
+ ...existingFrontmatter,
266
309
  name: agentName,
267
- description: node.description || node.label,
310
+ description: node.description || existingFrontmatter.description || node.label,
268
311
  };
269
312
  if (node.tools && node.tools.length > 0) {
270
313
  frontmatter.tools = node.tools.join(', ');
@@ -272,39 +315,51 @@ ${frontmatterStr}
272
315
  if (node.model) {
273
316
  frontmatter.model = node.model;
274
317
  }
318
+ // skills 필드 처리 - 기존 값과 새 값 병합
275
319
  if (node.skills && node.skills.length > 0) {
276
320
  frontmatter.skills = node.skills.join(', ');
277
321
  }
322
+ else if (node.skills && node.skills.length === 0) {
323
+ // 명시적으로 빈 배열이면 skills 제거
324
+ delete frontmatter.skills;
325
+ }
278
326
  const frontmatterStr = Object.entries(frontmatter)
279
327
  .map(([key, value]) => `${key}: ${value}`)
280
328
  .join('\n');
329
+ // body 처리 - systemPrompt가 명시되면 교체, 아니면 기존 유지
330
+ const body = node.systemPrompt || existingBody || `You are ${node.label}.
331
+
332
+ ${node.description || ''}
333
+ `;
281
334
  const content = `---
282
335
  ${frontmatterStr}
283
336
  ---
284
337
 
285
- ${node.systemPrompt || `You are ${node.label}.
286
-
287
- ${node.description || ''}
288
- `}
338
+ ${body}
289
339
  `;
290
340
  await fs.writeFile(agentPath, content, 'utf-8');
291
341
  return { success: true, path: agentPath };
292
342
  }
293
- async syncCommandNode(node) {
294
- const cmdName = node.commandName || this.toKebabCase(node.label);
295
- const commandsDir = path.join(this.projectRoot, '.claude', 'commands');
296
- const cmdPath = path.join(commandsDir, `${cmdName}.md`);
297
- await fs.mkdir(commandsDir, { recursive: true });
298
- const content = node.commandContent || `---
299
- description: ${node.description || node.label}
300
- ---
301
-
302
- ${node.description || '커맨드 내용을 여기에 작성하세요'}
303
-
304
- $ARGUMENTS
305
- `;
306
- await fs.writeFile(cmdPath, content, 'utf-8');
307
- return { success: true, path: cmdPath };
343
+ /**
344
+ * Frontmatter 파싱
345
+ */
346
+ parseFrontmatter(content) {
347
+ const frontmatter = {};
348
+ let body = content;
349
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
350
+ if (match) {
351
+ const fmContent = match[1];
352
+ body = match[2].trim();
353
+ for (const line of fmContent.split('\n')) {
354
+ const colonIndex = line.indexOf(':');
355
+ if (colonIndex > 0) {
356
+ const key = line.slice(0, colonIndex).trim();
357
+ const value = line.slice(colonIndex + 1).trim();
358
+ frontmatter[key] = value;
359
+ }
360
+ }
361
+ }
362
+ return { frontmatter, body };
308
363
  }
309
364
  async syncHookNode(node) {
310
365
  const settingsPath = path.join(this.projectRoot, '.claude', 'settings.json');
@@ -28,7 +28,7 @@ function generateClaudePrompt(options) {
28
28
  executionOrder.forEach((node) => {
29
29
  if (node.type === 'input')
30
30
  return;
31
- if (node.type === 'subagent') {
31
+ if (node.type === 'agent') {
32
32
  const data = node.data;
33
33
  lines.push(`${stepNum}. **${data.label}** (${data.role})`);
34
34
  if (data.description) {
@@ -22,7 +22,7 @@ const SYSTEM_PROMPT = `당신은 Claude Code 워크플로우 설계 전문가입
22
22
  ## 사용 가능한 공식 스킬
23
23
  ${AVAILABLE_SKILLS.map(s => `- ${s.id}: ${s.description}`).join('\n')}
24
24
 
25
- ## 사용 가능한 도구 (subagent의 tools에 사용)
25
+ ## 사용 가능한 도구 (agent의 tools에 사용)
26
26
  ${AVAILABLE_TOOLS.join(', ')}
27
27
 
28
28
  ## 응답 형식 (JSON)
@@ -42,7 +42,7 @@ ${AVAILABLE_TOOLS.join(', ')}
42
42
  }
43
43
  },
44
44
  {
45
- "type": "subagent",
45
+ "type": "agent",
46
46
  "label": "에이전트 이름",
47
47
  "description": "에이전트 역할 설명",
48
48
  "config": {
@@ -90,7 +90,7 @@ ${AVAILABLE_TOOLS.join(', ')}
90
90
  1. 항상 input 노드로 시작하고 output 노드로 종료
91
91
  2. 기존 공식 스킬로 가능하면 official 스킬 사용
92
92
  3. 새로운 기능이 필요하면 custom 스킬 생성 (skillContent에 SKILL.md 형식)
93
- 4. subagent는 복잡한 추론이나 다단계 작업에 사용
93
+ 4. agent는 복잡한 추론이나 다단계 작업에 사용
94
94
  5. edges의 from/to는 nodes 배열의 인덱스 (0부터 시작)
95
95
  6. 순차적으로 연결되지 않아도 됨 (병렬 처리, 합류 가능)
96
96
  7. systemPrompt는 구체적이고 실행 가능한 지시사항으로 작성
@@ -115,8 +115,8 @@ ${AVAILABLE_TOOLS.join(', ')}
115
115
  "description": "데이터를 분석하고 보고서를 작성하는 워크플로우",
116
116
  "nodes": [
117
117
  { "type": "input", "label": "데이터 파일", "description": "분석할 데이터 파일", "config": { "inputType": "file" } },
118
- { "type": "subagent", "label": "데이터 분석가", "description": "데이터를 분석하고 인사이트 도출", "config": { "role": "analyst", "tools": ["Read", "Grep", "Glob"], "model": "sonnet", "systemPrompt": "주어진 데이터를 분석하여 핵심 인사이트를 도출하세요. 통계적 요약, 트렌드, 이상치를 파악하세요." } },
119
- { "type": "subagent", "label": "보고서 작성자", "description": "분석 결과로 보고서 작성", "config": { "role": "writer", "tools": ["Read", "Write"], "model": "opus", "systemPrompt": "분석 결과를 바탕으로 경영진을 위한 간결하고 명확한 보고서를 작성하세요." } },
118
+ { "type": "agent", "label": "데이터 분석가", "description": "데이터를 분석하고 인사이트 도출", "config": { "role": "analyst", "tools": ["Read", "Grep", "Glob"], "model": "sonnet", "systemPrompt": "주어진 데이터를 분석하여 핵심 인사이트를 도출하세요. 통계적 요약, 트렌드, 이상치를 파악하세요." } },
119
+ { "type": "agent", "label": "보고서 작성자", "description": "분석 결과로 보고서 작성", "config": { "role": "writer", "tools": ["Read", "Write"], "model": "opus", "systemPrompt": "분석 결과를 바탕으로 경영진을 위한 간결하고 명확한 보고서를 작성하세요." } },
120
120
  { "type": "output", "label": "분석 보고서", "description": "최종 분석 보고서", "config": { "outputType": "document" } }
121
121
  ],
122
122
  "edges": [{ "from": 0, "to": 1 }, { "from": 1, "to": 2 }, { "from": 2, "to": 3 }]
@@ -57,7 +57,7 @@ export class WorkflowExecutionService {
57
57
  switch (node.type) {
58
58
  case 'input':
59
59
  return this.executeInputNode(node, context.inputs);
60
- case 'subagent':
60
+ case 'agent':
61
61
  return this.executeSubagentNode(node, previousResults, onProgress, onLog);
62
62
  case 'skill':
63
63
  return this.executeSkillNode(node, previousResults, onProgress, onLog);
@@ -89,7 +89,7 @@ export class WorkflowExecutionService {
89
89
  onProgress?.({ nodeId: node.id, status: 'running', progress: 20 });
90
90
  onLog?.('info', `claude -c 실행 중: ${data.label} (${data.role})`);
91
91
  // 프롬프트 생성
92
- const prompt = buildNodePrompt('subagent', data, previousResults);
92
+ const prompt = buildNodePrompt('agent', data, previousResults);
93
93
  try {
94
94
  onProgress?.({ nodeId: node.id, status: 'running', progress: 40 });
95
95
  const result = await executeClaudeCli({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makecc",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "type": "module",
5
5
  "description": "Visual workflow builder for Claude Code agents and skills",
6
6
  "keywords": [
package/server/index.ts CHANGED
@@ -146,12 +146,12 @@ app.post('/api/sync/node', async (req, res) => {
146
146
  // Delete node from file system
147
147
  app.delete('/api/sync/node', async (req, res) => {
148
148
  try {
149
- const { node } = req.body;
149
+ const { node, nodes } = req.body;
150
150
  if (!node) {
151
151
  return res.status(400).json({ message: 'Node data is required' });
152
152
  }
153
153
 
154
- const result = await nodeSyncService.deleteNode(node);
154
+ const result = await nodeSyncService.deleteNode(node, nodes);
155
155
  if (result.success) {
156
156
  res.json(result);
157
157
  } else {
@@ -242,7 +242,7 @@ app.get('/api/load/claude-config', async (req, res) => {
242
242
  try {
243
243
  const config = await configLoaderService.loadAll();
244
244
  console.log(
245
- `Loaded config: ${config.skills.length} skills, ${config.subagents.length} agents, ` +
245
+ `Loaded config: ${config.skills.length} skills, ${config.agents.length} agents, ` +
246
246
  `${config.commands.length} commands, ${config.hooks.length} hooks`
247
247
  );
248
248
  res.json(config);
@@ -208,7 +208,7 @@ export function buildNodePrompt(
208
208
  ): string {
209
209
  const lines: string[] = [];
210
210
 
211
- if (nodeType === 'subagent') {
211
+ if (nodeType === 'agent') {
212
212
  const role = nodeData.role as string || 'assistant';
213
213
  const description = nodeData.description as string || '';
214
214
  const systemPrompt = nodeData.systemPrompt as string || '';
@@ -40,7 +40,7 @@ export class ClaudeService {
40
40
  case 'input':
41
41
  return this.executeInputNode(node.id, node.data as InputNodeData);
42
42
 
43
- case 'subagent':
43
+ case 'agent':
44
44
  return this.executeSubagentNode(
45
45
  node.id,
46
46
  node.data as SubagentNodeData,
@@ -12,7 +12,7 @@ interface LoadedSkill {
12
12
 
13
13
  interface LoadedSubagent {
14
14
  id: string;
15
- type: 'subagent';
15
+ type: 'agent';
16
16
  label: string;
17
17
  description: string;
18
18
  tools: string[];
@@ -44,7 +44,7 @@ export type LoadedNode = LoadedSkill | LoadedSubagent | LoadedCommand | LoadedHo
44
44
 
45
45
  export interface ClaudeConfig {
46
46
  skills: LoadedSkill[];
47
- subagents: LoadedSubagent[];
47
+ agents: LoadedSubagent[];
48
48
  commands: LoadedCommand[];
49
49
  hooks: LoadedHook[];
50
50
  }
@@ -60,14 +60,14 @@ export class ConfigLoaderService {
60
60
  * .claude/ 디렉토리에서 모든 설정 로드
61
61
  */
62
62
  async loadAll(): Promise<ClaudeConfig> {
63
- const [skills, subagents, commands, hooks] = await Promise.all([
63
+ const [skills, agents, commands, hooks] = await Promise.all([
64
64
  this.loadSkills(),
65
65
  this.loadSubagents(),
66
66
  this.loadCommands(),
67
67
  this.loadHooks(),
68
68
  ]);
69
69
 
70
- return { skills, subagents, commands, hooks };
70
+ return { skills, agents, commands, hooks };
71
71
  }
72
72
 
73
73
  /**
@@ -112,7 +112,7 @@ export class ConfigLoaderService {
112
112
  */
113
113
  async loadSubagents(): Promise<LoadedSubagent[]> {
114
114
  const agentsDir = path.join(this.projectRoot, '.claude', 'agents');
115
- const subagents: LoadedSubagent[] = [];
115
+ const agents: LoadedSubagent[] = [];
116
116
 
117
117
  try {
118
118
  const entries = await fs.readdir(agentsDir, { withFileTypes: true });
@@ -125,9 +125,9 @@ export class ConfigLoaderService {
125
125
  const parsed = this.parseFrontmatter(content);
126
126
  const agentName = entry.name.replace('.md', '');
127
127
 
128
- subagents.push({
129
- id: `subagent-${agentName}`,
130
- type: 'subagent',
128
+ agents.push({
129
+ id: `agent-${agentName}`,
130
+ type: 'agent',
131
131
  label: parsed.frontmatter.name || agentName,
132
132
  description: parsed.frontmatter.description || '',
133
133
  tools: this.parseList(parsed.frontmatter.tools),
@@ -144,7 +144,7 @@ export class ConfigLoaderService {
144
144
  // agents 디렉토리가 없으면 빈 배열 반환
145
145
  }
146
146
 
147
- return subagents;
147
+ return agents;
148
148
  }
149
149
 
150
150
  /**