create-claude-pipeline 0.3.1 → 0.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-pipeline",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Claude Code 파이프라인 시스템을 프로젝트에 설치하고 대시보드를 실행합니다",
5
5
  "bin": {
6
6
  "create-claude-pipeline": "./bin/cli.js"
@@ -1,7 +1,7 @@
1
1
  import { spawn, execSync } from "child_process";
2
2
  import path from "path";
3
+ import fs from "fs";
3
4
  import { StateManager } from "./state-manager.js";
4
- import { SignalWatcher } from "./signal-watcher.js";
5
5
  import { ContextWatcher } from "./context-watcher.js";
6
6
  import { waitForCheckpoint } from "./checkpoint-waiter.js";
7
7
  const PIPELINE_ID = process.env.PIPELINE_ID;
@@ -16,8 +16,8 @@ if (!REQUIREMENTS) {
16
16
  process.exit(1);
17
17
  }
18
18
  const projectRoot = path.resolve(PIPELINES_DIR, "..");
19
- const abortController = new AbortController();
20
- // ── Pre-check: Claude CLI availability ──────────────────────────────
19
+ const contextDir = path.join(PIPELINES_DIR, PIPELINE_ID, "context");
20
+ // ── Pre-check ───────────────────────────────────────────────────────
21
21
  function checkClaudeCLI() {
22
22
  try {
23
23
  execSync("claude --version", { timeout: 10000, stdio: "pipe" });
@@ -27,206 +27,315 @@ function checkClaudeCLI() {
27
27
  return false;
28
28
  }
29
29
  }
30
- // ── Build the prompt for Claude ─────────────────────────────────────
31
- function buildPrompt(requirements, pipelineId) {
30
+ // ── Run a single Claude -p call and return stdout ───────────────────
31
+ function runClaude(prompt) {
32
+ return new Promise((resolve) => {
33
+ const child = spawn("claude", ["-p", prompt], {
34
+ cwd: projectRoot,
35
+ stdio: ["ignore", "pipe", "pipe"],
36
+ env: { ...process.env, PIPELINE_ID: PIPELINE_ID },
37
+ });
38
+ let stdout = "";
39
+ let stderr = "";
40
+ child.stdout.on("data", (data) => {
41
+ const text = data.toString();
42
+ stdout += text;
43
+ // Print lines as they come
44
+ for (const line of text.split("\n")) {
45
+ if (line.trim())
46
+ console.log(`[Claude] ${line}`);
47
+ }
48
+ });
49
+ child.stderr.on("data", (data) => {
50
+ stderr += data.toString();
51
+ });
52
+ child.on("close", (code) => {
53
+ if (stderr.trim())
54
+ console.error(`[Claude:err] ${stderr.trim()}`);
55
+ resolve({ stdout, code: code ?? 1 });
56
+ });
57
+ child.on("error", () => {
58
+ resolve({ stdout, code: 1 });
59
+ });
60
+ });
61
+ }
62
+ // ── Read context file if it exists ──────────────────────────────────
63
+ function readContextFile(filename) {
64
+ const filePath = path.join(contextDir, filename);
65
+ try {
66
+ return fs.readFileSync(filePath, "utf-8");
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ // ── List existing context files ─────────────────────────────────────
73
+ function listContextFiles() {
74
+ try {
75
+ return fs.readdirSync(contextDir).sort();
76
+ }
77
+ catch {
78
+ return [];
79
+ }
80
+ }
81
+ // ── Build phase-specific prompts ────────────────────────────────────
82
+ const COMMON_INSTRUCTIONS = [
83
+ `PIPELINE_ID: ${PIPELINE_ID}`,
84
+ "",
85
+ "모든 산출물은 반드시 아래 경로에 저장:",
86
+ ` pipelines/${PIPELINE_ID}/context/`,
87
+ "",
88
+ "예시:",
89
+ ` pipelines/${PIPELINE_ID}/context/00_requirements.md`,
90
+ ` pipelines/${PIPELINE_ID}/context/01_plan.md`,
91
+ "",
92
+ "절대 프로젝트 루트의 context/ 폴더에 파일을 만들지 마세요.",
93
+ 'CLAUDE.md의 파이프라인 가이드를 따르세요.',
94
+ ].join("\n");
95
+ function buildPhase0Prompt() {
32
96
  return [
33
- "다음 요구사항을 파이프라인으로 처리해주세요.",
97
+ "## PHASE 0: 인풋 수신",
34
98
  "",
35
- `PIPELINE_ID: ${pipelineId}`,
99
+ "아래 요구사항을 분석해서 PM(Alex)으로서 작업 범위를 파악해주세요.",
36
100
  "",
37
101
  "요구사항:",
102
+ REQUIREMENTS,
103
+ "",
104
+ "수행할 작업:",
105
+ "1. 신규 기능인지 기존 기능 수정인지 판단",
106
+ "2. 영향 범위 파악 (FE / BE / Infra / 전체)",
107
+ "3. 필요한 Agent 역할 목록 결정",
108
+ "4. 예상 작업 순서 설계",
109
+ "",
110
+ `결과를 pipelines/${PIPELINE_ID}/context/00_requirements.md 에 저장해주세요.`,
111
+ "",
112
+ COMMON_INSTRUCTIONS,
113
+ ].join("\n");
114
+ }
115
+ function buildPhase1Prompt() {
116
+ const requirements = readContextFile("00_requirements.md") || REQUIREMENTS;
117
+ return [
118
+ "## PHASE 1: 기획",
119
+ "",
120
+ "기획자(Mina)로서 아래 요구사항을 바탕으로 기획안을 작성해주세요.",
121
+ "",
122
+ "=== 요구사항 ===",
123
+ requirements,
124
+ "=== 끝 ===",
125
+ "",
126
+ "기획안에 포함할 내용:",
127
+ "1. 개요 (목적, 핵심 가치, 작업 범위)",
128
+ "2. 유저 스토리",
129
+ "3. 기능 명세 (표 형식)",
130
+ "4. 화면 목록 (표 형식)",
131
+ "5. API 초안 (Method / Path / 설명 / 인증 여부)",
132
+ "6. 엣지케이스 & 예외 처리",
133
+ "7. 비기능 요구사항",
134
+ "",
135
+ `결과를 pipelines/${PIPELINE_ID}/context/01_plan.md 에 저장해주세요.`,
136
+ "",
137
+ COMMON_INSTRUCTIONS,
138
+ ].join("\n");
139
+ }
140
+ function buildPhase2Prompt() {
141
+ const requirements = readContextFile("00_requirements.md") || REQUIREMENTS;
142
+ const plan = readContextFile("01_plan.md") || "";
143
+ return [
144
+ "## PHASE 2: 설계",
145
+ "",
146
+ "디자이너(Lena)와 BE 설계자(Sam)로서 설계를 수행해주세요.",
147
+ "",
148
+ "=== 요구사항 ===",
38
149
  requirements,
150
+ "=== 기획안 ===",
151
+ plan,
152
+ "=== 끝 ===",
153
+ "",
154
+ "디자이너 산출물 (02_design_spec.md):",
155
+ "- 디자인 토큰, 공통 컴포넌트, 화면별 레이아웃, 인터랙션, 접근성",
156
+ "",
157
+ "BE 설계 산출물 (03_api_spec.md + 03_erd.md):",
158
+ "- ERD, API 명세 상세, 인증/권한 설계",
159
+ "",
160
+ `모든 파일을 pipelines/${PIPELINE_ID}/context/ 에 저장해주세요.`,
161
+ "",
162
+ COMMON_INSTRUCTIONS,
163
+ ].join("\n");
164
+ }
165
+ function buildPhase3Prompt() {
166
+ const contextFiles = listContextFiles();
167
+ let contextSummary = "";
168
+ for (const file of contextFiles) {
169
+ const content = readContextFile(file);
170
+ if (content) {
171
+ contextSummary += `\n=== ${file} ===\n${content}\n`;
172
+ }
173
+ }
174
+ return [
175
+ "## PHASE 3: 구현",
39
176
  "",
40
- "중요: 세션은 파이프라인 대시보드에서 실행됩니다.",
41
- 'CLAUDE.md의 "Pipeline Dashboard Integration" 섹션을 반드시 따르세요.',
177
+ "FE(Jay), BE(Sam), Infra(Dex)로서 구현을 수행해주세요.",
42
178
  "",
43
- "특히 context 파일 경로에 주의하세요:",
44
- `- 모든 산출물(context 파일)은 pipelines/${pipelineId}/context/ 에 생성`,
45
- `- 시그널 파일은 pipelines/${pipelineId}/signals/ 에 생성`,
46
- `- 예: pipelines/${pipelineId}/context/00_requirements.md`,
47
- `- 예: pipelines/${pipelineId}/context/01_plan.md`,
179
+ "지금까지의 산출물:",
180
+ contextSummary,
181
+ "=== ===",
48
182
  "",
49
- "절대 프로젝트 루트의 context/ 폴더에 파일을 만들지 마세요.",
183
+ "기획안과 설계 명세를 바탕으로 코드를 구현해주세요.",
184
+ "각 Agent는 자신의 담당 파일만 수정합니다.",
185
+ "",
186
+ COMMON_INSTRUCTIONS,
187
+ ].join("\n");
188
+ }
189
+ function buildPhase4Prompt() {
190
+ const contextFiles = listContextFiles();
191
+ let contextSummary = "";
192
+ for (const file of contextFiles) {
193
+ const content = readContextFile(file);
194
+ if (content) {
195
+ contextSummary += `\n=== ${file} ===\n${content}\n`;
196
+ }
197
+ }
198
+ return [
199
+ "## PHASE 4: QA + 통합",
200
+ "",
201
+ "QA(Eva), 보안 리뷰어(Rex), 코드 리뷰어(Nora)로서 검증을 수행해주세요.",
202
+ "",
203
+ "지금까지의 산출물:",
204
+ contextSummary,
205
+ "=== 끝 ===",
206
+ "",
207
+ "산출물:",
208
+ `- pipelines/${PIPELINE_ID}/context/qa_report.md`,
209
+ `- pipelines/${PIPELINE_ID}/context/security_report.md`,
210
+ "",
211
+ COMMON_INSTRUCTIONS,
50
212
  ].join("\n");
51
213
  }
214
+ const PHASES = [
215
+ {
216
+ phase: 0,
217
+ name: "인풋 수신",
218
+ buildPrompt: buildPhase0Prompt,
219
+ expectedFiles: ["00_requirements.md"],
220
+ checkpoint: "요구사항 분석 결과를 확인해주세요.",
221
+ },
222
+ {
223
+ phase: 1,
224
+ name: "기획",
225
+ buildPrompt: buildPhase1Prompt,
226
+ expectedFiles: ["01_plan.md"],
227
+ checkpoint: "기획안을 검토해주세요.",
228
+ },
229
+ {
230
+ phase: 2,
231
+ name: "설계",
232
+ buildPrompt: buildPhase2Prompt,
233
+ expectedFiles: ["02_design_spec.md", "03_api_spec.md"],
234
+ checkpoint: "디자인 명세 + API 명세를 검토해주세요.",
235
+ },
236
+ {
237
+ phase: 3,
238
+ name: "구현",
239
+ buildPrompt: buildPhase3Prompt,
240
+ expectedFiles: [],
241
+ checkpoint: "구현 결과를 확인해주세요.",
242
+ },
243
+ {
244
+ phase: 4,
245
+ name: "QA + 통합",
246
+ buildPrompt: buildPhase4Prompt,
247
+ expectedFiles: ["qa_report.md", "security_report.md"],
248
+ checkpoint: "QA 보고서 + 보안 보고서를 확인해주세요.",
249
+ },
250
+ ];
52
251
  // ── Main ────────────────────────────────────────────────────────────
53
252
  async function main() {
54
253
  const stateManager = new StateManager(PIPELINES_DIR, PIPELINE_ID);
55
- // Verify state.json exists (created by dashboard)
56
254
  const initialState = stateManager.read();
57
255
  if (!initialState) {
58
256
  console.error(`state.json not found for pipeline ${PIPELINE_ID}`);
59
257
  process.exit(1);
60
258
  }
61
- // Pre-check Claude CLI
62
259
  if (!checkClaudeCLI()) {
63
260
  stateManager.setStatus("failed");
64
- stateManager.addActivity("system", "error", "Claude CLI를 찾을 수 없거나 로그인되어 있지 않습니다. `claude --version`을 확인해주세요.");
261
+ stateManager.addActivity("system", "error", "Claude CLI를 찾을 수 없거나 로그인되어 있지 않습니다.");
65
262
  process.exit(1);
66
263
  }
67
- // Start watchers
68
- const signalWatcher = new SignalWatcher(stateManager, PIPELINES_DIR, PIPELINE_ID);
264
+ // Ensure context directory exists
265
+ fs.mkdirSync(contextDir, { recursive: true });
266
+ // Start context watcher (fallback: copies root context/ to pipeline context/)
69
267
  const contextWatcher = new ContextWatcher(stateManager, PIPELINES_DIR, PIPELINE_ID);
70
- // Wire up: notify contextWatcher when signals are processed
71
- signalWatcher.on("phase", () => contextWatcher.notifySignalProcessed());
72
- signalWatcher.on("checkpoint", () => contextWatcher.notifySignalProcessed());
73
- signalWatcher.start(500);
74
268
  contextWatcher.start();
75
269
  stateManager.setStatus("running");
76
270
  stateManager.addActivity("system", "info", "파이프라인 시작");
77
- // ── Spawn Claude CLI ──────────────────────────────────────────────
78
- const prompt = buildPrompt(REQUIREMENTS, PIPELINE_ID);
79
- const claude = spawn("claude", ["-p", prompt, "--verbose"], {
80
- cwd: projectRoot,
81
- stdio: ["pipe", "pipe", "pipe"],
82
- env: { ...process.env, PIPELINE_ID: PIPELINE_ID },
83
- });
84
- // ── Handle checkpoint events ──────────────────────────────────────
85
- signalWatcher.on("checkpoint", async (phase, description) => {
86
- console.log(`[Runner] Checkpoint Phase ${phase}: ${description}`);
271
+ // ── Run phases sequentially ─────────────────────────────────────
272
+ for (const phaseConfig of PHASES) {
273
+ const { phase, name, buildPrompt, expectedFiles, checkpoint } = phaseConfig;
274
+ console.log(`\n[Runner] ── Phase ${phase}: ${name} ──`);
275
+ stateManager.setPhase(phase);
276
+ stateManager.addActivity("system", "info", `Phase ${phase} 시작: ${name}`);
277
+ // Build and run prompt
278
+ const prompt = buildPrompt();
279
+ const result = await runClaude(prompt);
280
+ if (result.code !== 0) {
281
+ stateManager.setStatus("failed");
282
+ stateManager.addActivity("system", "error", `Phase ${phase} 실패 (exit code: ${result.code})`);
283
+ break;
284
+ }
285
+ // Log Claude's output as activity (truncated)
286
+ const summary = result.stdout.trim().slice(0, 200);
287
+ if (summary) {
288
+ stateManager.addActivity("system", "progress", summary + (result.stdout.length > 200 ? "..." : ""));
289
+ }
290
+ // Register output files
291
+ for (const file of expectedFiles) {
292
+ if (fs.existsSync(path.join(contextDir, file))) {
293
+ stateManager.addOutput(`context/${file}`, phase);
294
+ stateManager.addActivity("system", "success", `산출물 생성: ${file}`);
295
+ }
296
+ }
297
+ // Also register any unexpected context files
298
+ for (const file of listContextFiles()) {
299
+ const existing = stateManager.read();
300
+ if (existing && !existing.outputs.some((o) => o.filename === `context/${file}`)) {
301
+ const filePhase = phase;
302
+ stateManager.addOutput(`context/${file}`, filePhase);
303
+ }
304
+ }
305
+ // ── Checkpoint: wait for user approval ──────────────────────
306
+ stateManager.addActivity("system", "info", `Checkpoint Phase ${phase}: ${checkpoint}`);
87
307
  stateManager.setStatus("paused");
308
+ console.log(`[Runner] Checkpoint Phase ${phase}: waiting for approval...`);
88
309
  try {
89
- const response = await waitForCheckpoint(PIPELINES_DIR, PIPELINE_ID, abortController.signal);
310
+ const response = await waitForCheckpoint(PIPELINES_DIR, PIPELINE_ID);
90
311
  if (response.action === "approve") {
91
312
  stateManager.addActivity("system", "success", `Checkpoint Phase ${phase} approved`);
92
- // Send approval to Claude via stdin
93
- if (claude.stdin.writable) {
94
- claude.stdin.write(`체크포인트 승인됨. 다음 Phase로 진행하세요.\n`);
95
- }
313
+ stateManager.setStatus("running");
314
+ console.log(`[Runner] Phase ${phase} approved`);
96
315
  }
97
316
  else {
98
- const feedback = response.message || "사용자가 수정을 요청했습니다.";
317
+ const feedback = response.message || "수정 요청";
99
318
  stateManager.addActivity("system", "info", `Checkpoint Phase ${phase} rejected: ${feedback}`);
100
- if (claude.stdin.writable) {
101
- claude.stdin.write(`체크포인트 거절됨. 피드백: ${feedback}\n수정 다시 진행해주세요.\n`);
102
- }
319
+ stateManager.setStatus("failed");
320
+ stateManager.addActivity("system", "error", `Phase ${phase}에서 사용자가 거절함`);
321
+ console.log(`[Runner] Phase ${phase} rejected: ${feedback}`);
322
+ break;
103
323
  }
104
- stateManager.setStatus("running");
105
324
  }
106
325
  catch (err) {
107
- if (err.message !== "Aborted") {
108
- console.error("[Runner] Checkpoint wait error:", err);
109
- }
110
- }
111
- });
112
- // ── Stream stdout/stderr → parse for activity logging ─────────────
113
- // Even if Claude doesn't write signal files, we extract progress
114
- // from stdout to keep the dashboard alive with activity updates.
115
- const AGENT_KEYWORDS = {
116
- "PM": "alex", "Alex": "alex",
117
- "기획": "mina", "Mina": "mina",
118
- "디자이너": "lena", "Lena": "lena", "디자인": "lena",
119
- "FE": "jay", "Jay": "jay", "프론트": "jay",
120
- "BE": "sam", "Sam": "sam", "백엔드": "sam",
121
- "인프라": "dex", "Dex": "dex", "Infra": "dex", "Docker": "dex",
122
- "QA": "eva", "Eva": "eva", "테스트": "eva",
123
- "보안": "rex", "Rex": "rex", "Security": "rex",
124
- "리뷰": "nora", "Nora": "nora", "코드 리뷰": "nora",
125
- };
126
- const PHASE_PATTERNS = [
127
- { pattern: /PHASE\s*0|인풋\s*수신|요구사항\s*분석/i, phase: 0 },
128
- { pattern: /PHASE\s*1|기획|plan/i, phase: 1 },
129
- { pattern: /PHASE\s*2|설계|design|API\s*명세/i, phase: 2 },
130
- { pattern: /PHASE\s*3|구현|implement/i, phase: 3 },
131
- { pattern: /PHASE\s*4|QA|통합|보안\s*리뷰/i, phase: 4 },
132
- ];
133
- let lastActivityTime = 0;
134
- const ACTIVITY_THROTTLE_MS = 3000; // Don't spam: max 1 activity per 3s from stdout
135
- function detectAgentFromLine(line) {
136
- for (const [keyword, agentId] of Object.entries(AGENT_KEYWORDS)) {
137
- if (line.includes(keyword))
138
- return agentId;
326
+ console.error("[Runner] Checkpoint error:", err);
327
+ stateManager.setStatus("failed");
328
+ break;
139
329
  }
140
- return "system";
141
330
  }
142
- function processStdoutLine(line) {
143
- const trimmed = line.trim();
144
- if (!trimmed || trimmed.length < 5)
145
- return;
146
- // Skip noisy lines (tool execution details, JSON, whitespace-heavy)
147
- if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith("```"))
148
- return;
149
- if (/^[-=_]{3,}$/.test(trimmed))
150
- return;
151
- const now = Date.now();
152
- // Phase detection (always process, no throttle)
153
- for (const { pattern, phase } of PHASE_PATTERNS) {
154
- if (pattern.test(trimmed)) {
155
- const currentState = stateManager.read();
156
- if (currentState && currentState.currentPhase < phase) {
157
- stateManager.setPhase(phase);
158
- stateManager.addActivity("system", "info", `Phase ${phase} 시작`);
159
- contextWatcher.notifySignalProcessed();
160
- }
161
- break;
162
- }
163
- }
164
- // Checkpoint detection from stdout
165
- if (/체크포인트|checkpoint/i.test(trimmed) && /승인|확인|검토|approve|review/i.test(trimmed)) {
166
- // Don't duplicate if signal-watcher already caught it
167
- return;
168
- }
169
- // Throttled activity logging
170
- if (now - lastActivityTime < ACTIVITY_THROTTLE_MS)
171
- return;
172
- lastActivityTime = now;
173
- // Log meaningful lines as activities
174
- const agentId = detectAgentFromLine(trimmed);
175
- // Truncate long lines
176
- const message = trimmed.length > 120 ? trimmed.slice(0, 117) + "..." : trimmed;
177
- stateManager.addActivity(agentId, "progress", message);
331
+ // ── Finalize ──────────────────────────────────────────────────
332
+ const finalState = stateManager.read();
333
+ if (finalState && finalState.status === "running") {
334
+ stateManager.setStatus("completed");
335
+ stateManager.addActivity("system", "success", "파이프라인 완료");
178
336
  }
179
- let stdoutBuffer = "";
180
- claude.stdout.on("data", (data) => {
181
- stdoutBuffer += data.toString();
182
- const lines = stdoutBuffer.split("\n");
183
- stdoutBuffer = lines.pop() || "";
184
- for (const line of lines) {
185
- if (line.trim()) {
186
- console.log(`[Claude] ${line}`);
187
- processStdoutLine(line);
188
- }
189
- }
190
- });
191
- claude.stderr.on("data", (data) => {
192
- const text = data.toString().trim();
193
- if (text) {
194
- console.error(`[Claude:err] ${text}`);
195
- }
196
- });
197
- // ── Handle process exit ───────────────────────────────────────────
198
- claude.on("close", (code) => {
199
- console.log(`[Runner] Claude process exited with code ${code}`);
200
- abortController.abort();
201
- if (code === 0) {
202
- stateManager.setStatus("completed");
203
- stateManager.addActivity("system", "success", "파이프라인 완료");
204
- }
205
- else {
206
- stateManager.setStatus("failed");
207
- stateManager.addActivity("system", "error", `Claude 프로세스가 비정상 종료되었습니다 (exit code: ${code})`);
208
- }
209
- signalWatcher.stop();
210
- contextWatcher.stop();
211
- });
212
- claude.on("error", (err) => {
213
- console.error("[Runner] Failed to spawn Claude:", err);
214
- stateManager.setStatus("failed");
215
- stateManager.addActivity("system", "error", `Claude 실행 실패: ${err.message}`);
216
- signalWatcher.stop();
217
- contextWatcher.stop();
218
- });
219
- // ── Graceful shutdown ─────────────────────────────────────────────
220
- const cleanup = () => {
221
- abortController.abort();
222
- signalWatcher.stop();
223
- contextWatcher.stop();
224
- if (!claude.killed) {
225
- claude.kill("SIGTERM");
226
- }
227
- };
228
- process.on("SIGINT", cleanup);
229
- process.on("SIGTERM", cleanup);
337
+ contextWatcher.stop();
338
+ console.log("[Runner] Pipeline finished");
230
339
  }
231
340
  main().catch((err) => {
232
341
  console.error("[Runner] Fatal error:", err);
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-runner.js","sourceRoot":"","sources":["../src/pipeline-runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline-runner.js","sourceRoot":"","sources":["../src/pipeline-runner.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,7 @@
1
- import { spawn, execSync } from "child_process";
1
+ import { spawn, execSync, ChildProcess } from "child_process";
2
2
  import path from "path";
3
3
  import fs from "fs";
4
4
  import { StateManager } from "./state-manager.js";
5
- import { SignalWatcher } from "./signal-watcher.js";
6
5
  import { ContextWatcher } from "./context-watcher.js";
7
6
  import { waitForCheckpoint } from "./checkpoint-waiter.js";
8
7
 
@@ -21,9 +20,9 @@ if (!REQUIREMENTS) {
21
20
  }
22
21
 
23
22
  const projectRoot = path.resolve(PIPELINES_DIR, "..");
24
- const abortController = new AbortController();
23
+ const contextDir = path.join(PIPELINES_DIR, PIPELINE_ID, "context");
25
24
 
26
- // ── Pre-check: Claude CLI availability ──────────────────────────────
25
+ // ── Pre-check ───────────────────────────────────────────────────────
27
26
  function checkClaudeCLI(): boolean {
28
27
  try {
29
28
  execSync("claude --version", { timeout: 10000, stdio: "pipe" });
@@ -33,247 +32,369 @@ function checkClaudeCLI(): boolean {
33
32
  }
34
33
  }
35
34
 
36
- // ── Build the prompt for Claude ─────────────────────────────────────
37
- function buildPrompt(requirements: string, pipelineId: string): string {
35
+ // ── Run a single Claude -p call and return stdout ───────────────────
36
+ function runClaude(prompt: string): Promise<{ stdout: string; code: number }> {
37
+ return new Promise((resolve) => {
38
+ const child = spawn("claude", ["-p", prompt], {
39
+ cwd: projectRoot,
40
+ stdio: ["ignore", "pipe", "pipe"],
41
+ env: { ...process.env, PIPELINE_ID: PIPELINE_ID! },
42
+ });
43
+
44
+ let stdout = "";
45
+ let stderr = "";
46
+
47
+ child.stdout.on("data", (data: Buffer) => {
48
+ const text = data.toString();
49
+ stdout += text;
50
+ // Print lines as they come
51
+ for (const line of text.split("\n")) {
52
+ if (line.trim()) console.log(`[Claude] ${line}`);
53
+ }
54
+ });
55
+
56
+ child.stderr.on("data", (data: Buffer) => {
57
+ stderr += data.toString();
58
+ });
59
+
60
+ child.on("close", (code) => {
61
+ if (stderr.trim()) console.error(`[Claude:err] ${stderr.trim()}`);
62
+ resolve({ stdout, code: code ?? 1 });
63
+ });
64
+
65
+ child.on("error", () => {
66
+ resolve({ stdout, code: 1 });
67
+ });
68
+ });
69
+ }
70
+
71
+ // ── Read context file if it exists ──────────────────────────────────
72
+ function readContextFile(filename: string): string | null {
73
+ const filePath = path.join(contextDir, filename);
74
+ try {
75
+ return fs.readFileSync(filePath, "utf-8");
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ // ── List existing context files ─────────────────────────────────────
82
+ function listContextFiles(): string[] {
83
+ try {
84
+ return fs.readdirSync(contextDir).sort();
85
+ } catch {
86
+ return [];
87
+ }
88
+ }
89
+
90
+ // ── Build phase-specific prompts ────────────────────────────────────
91
+
92
+ const COMMON_INSTRUCTIONS = [
93
+ `PIPELINE_ID: ${PIPELINE_ID}`,
94
+ "",
95
+ "모든 산출물은 반드시 아래 경로에 저장:",
96
+ ` pipelines/${PIPELINE_ID}/context/`,
97
+ "",
98
+ "예시:",
99
+ ` pipelines/${PIPELINE_ID}/context/00_requirements.md`,
100
+ ` pipelines/${PIPELINE_ID}/context/01_plan.md`,
101
+ "",
102
+ "절대 프로젝트 루트의 context/ 폴더에 파일을 만들지 마세요.",
103
+ 'CLAUDE.md의 파이프라인 가이드를 따르세요.',
104
+ ].join("\n");
105
+
106
+ function buildPhase0Prompt(): string {
38
107
  return [
39
- "다음 요구사항을 파이프라인으로 처리해주세요.",
108
+ "## PHASE 0: 인풋 수신",
40
109
  "",
41
- `PIPELINE_ID: ${pipelineId}`,
110
+ "아래 요구사항을 분석해서 PM(Alex)으로서 작업 범위를 파악해주세요.",
42
111
  "",
43
112
  "요구사항:",
113
+ REQUIREMENTS,
114
+ "",
115
+ "수행할 작업:",
116
+ "1. 신규 기능인지 기존 기능 수정인지 판단",
117
+ "2. 영향 범위 파악 (FE / BE / Infra / 전체)",
118
+ "3. 필요한 Agent 역할 목록 결정",
119
+ "4. 예상 작업 순서 설계",
120
+ "",
121
+ `결과를 pipelines/${PIPELINE_ID}/context/00_requirements.md 에 저장해주세요.`,
122
+ "",
123
+ COMMON_INSTRUCTIONS,
124
+ ].join("\n");
125
+ }
126
+
127
+ function buildPhase1Prompt(): string {
128
+ const requirements = readContextFile("00_requirements.md") || REQUIREMENTS;
129
+ return [
130
+ "## PHASE 1: 기획",
131
+ "",
132
+ "기획자(Mina)로서 아래 요구사항을 바탕으로 기획안을 작성해주세요.",
133
+ "",
134
+ "=== 요구사항 ===",
44
135
  requirements,
136
+ "=== 끝 ===",
45
137
  "",
46
- "중요: 세션은 파이프라인 대시보드에서 실행됩니다.",
47
- 'CLAUDE.md의 "Pipeline Dashboard Integration" 섹션을 반드시 따르세요.',
138
+ "기획안에 포함할 내용:",
139
+ "1. 개요 (목적, 핵심 가치, 작업 범위)",
140
+ "2. 유저 스토리",
141
+ "3. 기능 명세 (표 형식)",
142
+ "4. 화면 목록 (표 형식)",
143
+ "5. API 초안 (Method / Path / 설명 / 인증 여부)",
144
+ "6. 엣지케이스 & 예외 처리",
145
+ "7. 비기능 요구사항",
48
146
  "",
49
- "특히 context 파일 경로에 주의하세요:",
50
- `- 모든 산출물(context 파일)은 pipelines/${pipelineId}/context/ 에 생성`,
51
- `- 시그널 파일은 pipelines/${pipelineId}/signals/ 에 생성`,
52
- `- 예: pipelines/${pipelineId}/context/00_requirements.md`,
53
- `- 예: pipelines/${pipelineId}/context/01_plan.md`,
147
+ `결과를 pipelines/${PIPELINE_ID}/context/01_plan.md 저장해주세요.`,
54
148
  "",
55
- "절대 프로젝트 루트의 context/ 폴더에 파일을 만들지 마세요.",
149
+ COMMON_INSTRUCTIONS,
56
150
  ].join("\n");
57
151
  }
58
152
 
153
+ function buildPhase2Prompt(): string {
154
+ const requirements = readContextFile("00_requirements.md") || REQUIREMENTS;
155
+ const plan = readContextFile("01_plan.md") || "";
156
+ return [
157
+ "## PHASE 2: 설계",
158
+ "",
159
+ "디자이너(Lena)와 BE 설계자(Sam)로서 설계를 수행해주세요.",
160
+ "",
161
+ "=== 요구사항 ===",
162
+ requirements,
163
+ "=== 기획안 ===",
164
+ plan,
165
+ "=== 끝 ===",
166
+ "",
167
+ "디자이너 산출물 (02_design_spec.md):",
168
+ "- 디자인 토큰, 공통 컴포넌트, 화면별 레이아웃, 인터랙션, 접근성",
169
+ "",
170
+ "BE 설계 산출물 (03_api_spec.md + 03_erd.md):",
171
+ "- ERD, API 명세 상세, 인증/권한 설계",
172
+ "",
173
+ `모든 파일을 pipelines/${PIPELINE_ID}/context/ 에 저장해주세요.`,
174
+ "",
175
+ COMMON_INSTRUCTIONS,
176
+ ].join("\n");
177
+ }
178
+
179
+ function buildPhase3Prompt(): string {
180
+ const contextFiles = listContextFiles();
181
+ let contextSummary = "";
182
+ for (const file of contextFiles) {
183
+ const content = readContextFile(file);
184
+ if (content) {
185
+ contextSummary += `\n=== ${file} ===\n${content}\n`;
186
+ }
187
+ }
188
+ return [
189
+ "## PHASE 3: 구현",
190
+ "",
191
+ "FE(Jay), BE(Sam), Infra(Dex)로서 구현을 수행해주세요.",
192
+ "",
193
+ "지금까지의 산출물:",
194
+ contextSummary,
195
+ "=== 끝 ===",
196
+ "",
197
+ "기획안과 설계 명세를 바탕으로 코드를 구현해주세요.",
198
+ "각 Agent는 자신의 담당 파일만 수정합니다.",
199
+ "",
200
+ COMMON_INSTRUCTIONS,
201
+ ].join("\n");
202
+ }
203
+
204
+ function buildPhase4Prompt(): string {
205
+ const contextFiles = listContextFiles();
206
+ let contextSummary = "";
207
+ for (const file of contextFiles) {
208
+ const content = readContextFile(file);
209
+ if (content) {
210
+ contextSummary += `\n=== ${file} ===\n${content}\n`;
211
+ }
212
+ }
213
+ return [
214
+ "## PHASE 4: QA + 통합",
215
+ "",
216
+ "QA(Eva), 보안 리뷰어(Rex), 코드 리뷰어(Nora)로서 검증을 수행해주세요.",
217
+ "",
218
+ "지금까지의 산출물:",
219
+ contextSummary,
220
+ "=== 끝 ===",
221
+ "",
222
+ "산출물:",
223
+ `- pipelines/${PIPELINE_ID}/context/qa_report.md`,
224
+ `- pipelines/${PIPELINE_ID}/context/security_report.md`,
225
+ "",
226
+ COMMON_INSTRUCTIONS,
227
+ ].join("\n");
228
+ }
229
+
230
+ // ── Phase definitions ───────────────────────────────────────────────
231
+
232
+ interface PhaseConfig {
233
+ phase: number;
234
+ name: string;
235
+ buildPrompt: () => string;
236
+ expectedFiles: string[];
237
+ checkpoint: string;
238
+ }
239
+
240
+ const PHASES: PhaseConfig[] = [
241
+ {
242
+ phase: 0,
243
+ name: "인풋 수신",
244
+ buildPrompt: buildPhase0Prompt,
245
+ expectedFiles: ["00_requirements.md"],
246
+ checkpoint: "요구사항 분석 결과를 확인해주세요.",
247
+ },
248
+ {
249
+ phase: 1,
250
+ name: "기획",
251
+ buildPrompt: buildPhase1Prompt,
252
+ expectedFiles: ["01_plan.md"],
253
+ checkpoint: "기획안을 검토해주세요.",
254
+ },
255
+ {
256
+ phase: 2,
257
+ name: "설계",
258
+ buildPrompt: buildPhase2Prompt,
259
+ expectedFiles: ["02_design_spec.md", "03_api_spec.md"],
260
+ checkpoint: "디자인 명세 + API 명세를 검토해주세요.",
261
+ },
262
+ {
263
+ phase: 3,
264
+ name: "구현",
265
+ buildPrompt: buildPhase3Prompt,
266
+ expectedFiles: [],
267
+ checkpoint: "구현 결과를 확인해주세요.",
268
+ },
269
+ {
270
+ phase: 4,
271
+ name: "QA + 통합",
272
+ buildPrompt: buildPhase4Prompt,
273
+ expectedFiles: ["qa_report.md", "security_report.md"],
274
+ checkpoint: "QA 보고서 + 보안 보고서를 확인해주세요.",
275
+ },
276
+ ];
277
+
59
278
  // ── Main ────────────────────────────────────────────────────────────
60
279
  async function main(): Promise<void> {
61
280
  const stateManager = new StateManager(PIPELINES_DIR!, PIPELINE_ID!);
62
281
 
63
- // Verify state.json exists (created by dashboard)
64
282
  const initialState = stateManager.read();
65
283
  if (!initialState) {
66
284
  console.error(`state.json not found for pipeline ${PIPELINE_ID}`);
67
285
  process.exit(1);
68
286
  }
69
287
 
70
- // Pre-check Claude CLI
71
288
  if (!checkClaudeCLI()) {
72
289
  stateManager.setStatus("failed");
73
290
  stateManager.addActivity(
74
291
  "system",
75
292
  "error",
76
- "Claude CLI를 찾을 수 없거나 로그인되어 있지 않습니다. `claude --version`을 확인해주세요.",
293
+ "Claude CLI를 찾을 수 없거나 로그인되어 있지 않습니다.",
77
294
  );
78
295
  process.exit(1);
79
296
  }
80
297
 
81
- // Start watchers
82
- const signalWatcher = new SignalWatcher(stateManager, PIPELINES_DIR!, PIPELINE_ID!);
83
- const contextWatcher = new ContextWatcher(stateManager, PIPELINES_DIR!, PIPELINE_ID!);
84
-
85
- // Wire up: notify contextWatcher when signals are processed
86
- signalWatcher.on("phase", () => contextWatcher.notifySignalProcessed());
87
- signalWatcher.on("checkpoint", () => contextWatcher.notifySignalProcessed());
298
+ // Ensure context directory exists
299
+ fs.mkdirSync(contextDir, { recursive: true });
88
300
 
89
- signalWatcher.start(500);
301
+ // Start context watcher (fallback: copies root context/ to pipeline context/)
302
+ const contextWatcher = new ContextWatcher(stateManager, PIPELINES_DIR!, PIPELINE_ID!);
90
303
  contextWatcher.start();
91
304
 
92
305
  stateManager.setStatus("running");
93
306
  stateManager.addActivity("system", "info", "파이프라인 시작");
94
307
 
95
- // ── Spawn Claude CLI ──────────────────────────────────────────────
96
- const prompt = buildPrompt(REQUIREMENTS, PIPELINE_ID!);
308
+ // ── Run phases sequentially ─────────────────────────────────────
309
+ for (const phaseConfig of PHASES) {
310
+ const { phase, name, buildPrompt, expectedFiles, checkpoint } = phaseConfig;
97
311
 
98
- const claude = spawn("claude", ["-p", prompt, "--verbose"], {
99
- cwd: projectRoot,
100
- stdio: ["pipe", "pipe", "pipe"],
101
- env: { ...process.env, PIPELINE_ID: PIPELINE_ID! },
102
- });
312
+ console.log(`\n[Runner] ── Phase ${phase}: ${name} ──`);
313
+ stateManager.setPhase(phase);
314
+ stateManager.addActivity("system", "info", `Phase ${phase} 시작: ${name}`);
315
+
316
+ // Build and run prompt
317
+ const prompt = buildPrompt();
318
+ const result = await runClaude(prompt);
319
+
320
+ if (result.code !== 0) {
321
+ stateManager.setStatus("failed");
322
+ stateManager.addActivity(
323
+ "system",
324
+ "error",
325
+ `Phase ${phase} 실패 (exit code: ${result.code})`,
326
+ );
327
+ break;
328
+ }
103
329
 
104
- // ── Handle checkpoint events ──────────────────────────────────────
105
- signalWatcher.on("checkpoint", async (phase, description) => {
106
- console.log(`[Runner] Checkpoint Phase ${phase}: ${description}`);
330
+ // Log Claude's output as activity (truncated)
331
+ const summary = result.stdout.trim().slice(0, 200);
332
+ if (summary) {
333
+ stateManager.addActivity("system", "progress", summary + (result.stdout.length > 200 ? "..." : ""));
334
+ }
335
+
336
+ // Register output files
337
+ for (const file of expectedFiles) {
338
+ if (fs.existsSync(path.join(contextDir, file))) {
339
+ stateManager.addOutput(`context/${file}`, phase);
340
+ stateManager.addActivity("system", "success", `산출물 생성: ${file}`);
341
+ }
342
+ }
343
+
344
+ // Also register any unexpected context files
345
+ for (const file of listContextFiles()) {
346
+ const existing = stateManager.read();
347
+ if (existing && !existing.outputs.some((o) => o.filename === `context/${file}`)) {
348
+ const filePhase = phase;
349
+ stateManager.addOutput(`context/${file}`, filePhase);
350
+ }
351
+ }
352
+
353
+ // ── Checkpoint: wait for user approval ──────────────────────
354
+ stateManager.addActivity(
355
+ "system",
356
+ "info",
357
+ `Checkpoint Phase ${phase}: ${checkpoint}`,
358
+ );
107
359
  stateManager.setStatus("paused");
108
360
 
361
+ console.log(`[Runner] Checkpoint Phase ${phase}: waiting for approval...`);
362
+
109
363
  try {
110
- const response = await waitForCheckpoint(
111
- PIPELINES_DIR!,
112
- PIPELINE_ID!,
113
- abortController.signal,
114
- );
364
+ const response = await waitForCheckpoint(PIPELINES_DIR!, PIPELINE_ID!);
115
365
 
116
366
  if (response.action === "approve") {
117
367
  stateManager.addActivity("system", "success", `Checkpoint Phase ${phase} approved`);
118
- // Send approval to Claude via stdin
119
- if (claude.stdin.writable) {
120
- claude.stdin.write(`체크포인트 승인됨. 다음 Phase로 진행하세요.\n`);
121
- }
368
+ stateManager.setStatus("running");
369
+ console.log(`[Runner] Phase ${phase} approved`);
122
370
  } else {
123
- const feedback = response.message || "사용자가 수정을 요청했습니다.";
371
+ const feedback = response.message || "수정 요청";
124
372
  stateManager.addActivity(
125
373
  "system",
126
374
  "info",
127
375
  `Checkpoint Phase ${phase} rejected: ${feedback}`,
128
376
  );
129
- if (claude.stdin.writable) {
130
- claude.stdin.write(`체크포인트 거절됨. 피드백: ${feedback}\n수정 다시 진행해주세요.\n`);
131
- }
377
+ stateManager.setStatus("failed");
378
+ stateManager.addActivity("system", "error", `Phase ${phase}에서 사용자가 거절함`);
379
+ console.log(`[Runner] Phase ${phase} rejected: ${feedback}`);
380
+ break;
132
381
  }
133
-
134
- stateManager.setStatus("running");
135
382
  } catch (err) {
136
- if ((err as Error).message !== "Aborted") {
137
- console.error("[Runner] Checkpoint wait error:", err);
138
- }
139
- }
140
- });
141
-
142
- // ── Stream stdout/stderr → parse for activity logging ─────────────
143
- // Even if Claude doesn't write signal files, we extract progress
144
- // from stdout to keep the dashboard alive with activity updates.
145
-
146
- const AGENT_KEYWORDS: Record<string, string> = {
147
- "PM": "alex", "Alex": "alex",
148
- "기획": "mina", "Mina": "mina",
149
- "디자이너": "lena", "Lena": "lena", "디자인": "lena",
150
- "FE": "jay", "Jay": "jay", "프론트": "jay",
151
- "BE": "sam", "Sam": "sam", "백엔드": "sam",
152
- "인프라": "dex", "Dex": "dex", "Infra": "dex", "Docker": "dex",
153
- "QA": "eva", "Eva": "eva", "테스트": "eva",
154
- "보안": "rex", "Rex": "rex", "Security": "rex",
155
- "리뷰": "nora", "Nora": "nora", "코드 리뷰": "nora",
156
- };
157
-
158
- const PHASE_PATTERNS = [
159
- { pattern: /PHASE\s*0|인풋\s*수신|요구사항\s*분석/i, phase: 0 },
160
- { pattern: /PHASE\s*1|기획|plan/i, phase: 1 },
161
- { pattern: /PHASE\s*2|설계|design|API\s*명세/i, phase: 2 },
162
- { pattern: /PHASE\s*3|구현|implement/i, phase: 3 },
163
- { pattern: /PHASE\s*4|QA|통합|보안\s*리뷰/i, phase: 4 },
164
- ];
165
-
166
- let lastActivityTime = 0;
167
- const ACTIVITY_THROTTLE_MS = 3000; // Don't spam: max 1 activity per 3s from stdout
168
-
169
- function detectAgentFromLine(line: string): string {
170
- for (const [keyword, agentId] of Object.entries(AGENT_KEYWORDS)) {
171
- if (line.includes(keyword)) return agentId;
383
+ console.error("[Runner] Checkpoint error:", err);
384
+ stateManager.setStatus("failed");
385
+ break;
172
386
  }
173
- return "system";
174
387
  }
175
388
 
176
- function processStdoutLine(line: string): void {
177
- const trimmed = line.trim();
178
- if (!trimmed || trimmed.length < 5) return;
179
-
180
- // Skip noisy lines (tool execution details, JSON, whitespace-heavy)
181
- if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.startsWith("```")) return;
182
- if (/^[-=_]{3,}$/.test(trimmed)) return;
183
-
184
- const now = Date.now();
185
-
186
- // Phase detection (always process, no throttle)
187
- for (const { pattern, phase } of PHASE_PATTERNS) {
188
- if (pattern.test(trimmed)) {
189
- const currentState = stateManager.read();
190
- if (currentState && currentState.currentPhase < phase) {
191
- stateManager.setPhase(phase);
192
- stateManager.addActivity("system", "info", `Phase ${phase} 시작`);
193
- contextWatcher.notifySignalProcessed();
194
- }
195
- break;
196
- }
197
- }
198
-
199
- // Checkpoint detection from stdout
200
- if (/체크포인트|checkpoint/i.test(trimmed) && /승인|확인|검토|approve|review/i.test(trimmed)) {
201
- // Don't duplicate if signal-watcher already caught it
202
- return;
203
- }
204
-
205
- // Throttled activity logging
206
- if (now - lastActivityTime < ACTIVITY_THROTTLE_MS) return;
207
- lastActivityTime = now;
208
-
209
- // Log meaningful lines as activities
210
- const agentId = detectAgentFromLine(trimmed);
211
- // Truncate long lines
212
- const message = trimmed.length > 120 ? trimmed.slice(0, 117) + "..." : trimmed;
213
- stateManager.addActivity(agentId, "progress", message);
389
+ // ── Finalize ──────────────────────────────────────────────────
390
+ const finalState = stateManager.read();
391
+ if (finalState && finalState.status === "running") {
392
+ stateManager.setStatus("completed");
393
+ stateManager.addActivity("system", "success", "파이프라인 완료");
214
394
  }
215
395
 
216
- let stdoutBuffer = "";
217
- claude.stdout.on("data", (data: Buffer) => {
218
- stdoutBuffer += data.toString();
219
- const lines = stdoutBuffer.split("\n");
220
- stdoutBuffer = lines.pop() || "";
221
- for (const line of lines) {
222
- if (line.trim()) {
223
- console.log(`[Claude] ${line}`);
224
- processStdoutLine(line);
225
- }
226
- }
227
- });
228
-
229
- claude.stderr.on("data", (data: Buffer) => {
230
- const text = data.toString().trim();
231
- if (text) {
232
- console.error(`[Claude:err] ${text}`);
233
- }
234
- });
235
-
236
- // ── Handle process exit ───────────────────────────────────────────
237
- claude.on("close", (code) => {
238
- console.log(`[Runner] Claude process exited with code ${code}`);
239
- abortController.abort();
240
-
241
- if (code === 0) {
242
- stateManager.setStatus("completed");
243
- stateManager.addActivity("system", "success", "파이프라인 완료");
244
- } else {
245
- stateManager.setStatus("failed");
246
- stateManager.addActivity(
247
- "system",
248
- "error",
249
- `Claude 프로세스가 비정상 종료되었습니다 (exit code: ${code})`,
250
- );
251
- }
252
-
253
- signalWatcher.stop();
254
- contextWatcher.stop();
255
- });
256
-
257
- claude.on("error", (err) => {
258
- console.error("[Runner] Failed to spawn Claude:", err);
259
- stateManager.setStatus("failed");
260
- stateManager.addActivity("system", "error", `Claude 실행 실패: ${err.message}`);
261
- signalWatcher.stop();
262
- contextWatcher.stop();
263
- });
264
-
265
- // ── Graceful shutdown ─────────────────────────────────────────────
266
- const cleanup = () => {
267
- abortController.abort();
268
- signalWatcher.stop();
269
- contextWatcher.stop();
270
- if (!claude.killed) {
271
- claude.kill("SIGTERM");
272
- }
273
- };
274
-
275
- process.on("SIGINT", cleanup);
276
- process.on("SIGTERM", cleanup);
396
+ contextWatcher.stop();
397
+ console.log("[Runner] Pipeline finished");
277
398
  }
278
399
 
279
400
  main().catch((err) => {