@jjlabsio/claude-crew 0.1.21 → 0.1.23

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.
@@ -11,7 +11,7 @@
11
11
  "name": "claude-crew",
12
12
  "source": "./",
13
13
  "description": "오케스트레이터 + PM, 플래너, 개발, QA, 마케팅 에이전트 팀으로 단일 제품의 개발과 마케팅을 통합 관리",
14
- "version": "0.1.21",
14
+ "version": "0.1.23",
15
15
  "author": {
16
16
  "name": "Jaejin Song",
17
17
  "email": "wowlxx28@gmail.com"
@@ -28,5 +28,5 @@
28
28
  "category": "workflow"
29
29
  }
30
30
  ],
31
- "version": "0.1.21"
31
+ "version": "0.1.23"
32
32
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-crew",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "1인 SaaS 개발자를 위한 멀티 에이전트 오케스트레이션 — 개발, 마케팅, 일정을 한 대화에서 통합 관리",
5
5
  "author": {
6
6
  "name": "Jaejin Song",
@@ -0,0 +1,24 @@
1
+ {
2
+ "claude": {
3
+ "models": [
4
+ { "id": "opus", "label": "Opus 4.6 — 최고 품질, 복잡한 구현", "default_for": ["dev", "code-reviewer"] },
5
+ { "id": "sonnet", "label": "Sonnet 4.6 — 빠르고 저렴, Opus급 성능", "default_for": ["qa"] },
6
+ { "id": "haiku", "label": "Haiku 4.5 — 최저 비용, 단순 태스크", "default_for": [] }
7
+ ]
8
+ },
9
+ "codex": {
10
+ "models": [
11
+ { "id": "gpt-5.4", "reasoning": "xhigh", "label": "GPT-5.4 xhigh (추천) — 최고 성능, 토큰 多", "default_for": ["dev", "code-reviewer"] },
12
+ { "id": "gpt-5.4", "reasoning": "high", "label": "GPT-5.4 high — 고성능, 균형잡힌 비용", "default_for": [] },
13
+ { "id": "gpt-5.4", "reasoning": "medium", "label": "GPT-5.4 medium — 빠르고 저렴", "default_for": [] },
14
+ { "id": "o3", "reasoning": "high", "label": "o3 high — 추론 특화", "default_for": [] },
15
+ { "id": "o3", "reasoning": "medium", "label": "o3 medium — 추론 특화, 저비용", "default_for": [] },
16
+ { "id": "gpt-5.4-mini", "reasoning": null, "label": "GPT-5.4 Mini — 최저 비용", "default_for": ["qa"] }
17
+ ]
18
+ },
19
+ "agent_defaults": {
20
+ "dev": { "provider": "claude", "model": "opus" },
21
+ "code-reviewer": { "provider": "claude", "model": "opus" },
22
+ "qa": { "provider": "claude", "model": "sonnet" }
23
+ }
24
+ }
package/hud/index.mjs CHANGED
@@ -21,6 +21,7 @@ import { homedir } from 'node:os';
21
21
  const RESET = '\x1b[0m';
22
22
  const bold = (s) => `\x1b[1m${s}\x1b[22m`;
23
23
  const dim = (s) => `\x1b[2m${s}\x1b[22m`;
24
+ const subdued = (s) => `\x1b[38;5;248m${s}${RESET}`;
24
25
  const yellow = (s) => `\x1b[33m${s}\x1b[39m`;
25
26
  const red = (s) => `\x1b[31m${s}\x1b[39m`;
26
27
  const green = (s) => `\x1b[32m${s}\x1b[39m`;
@@ -237,7 +238,7 @@ function colorizeRateLimits(limits) {
237
238
  };
238
239
  const formatWindow = (label, pct, resetAt) => {
239
240
  const reset = formatResetTime(resetAt);
240
- const resetStr = reset ? ` ${dim(`(${reset})`)}` : '';
241
+ const resetStr = reset ? ` ${subdued(`(${reset})`)}` : '';
241
242
  return `${label}:${colorize(pct)}${resetStr}`;
242
243
  };
243
244
  const parts = [];
@@ -246,6 +247,41 @@ function colorizeRateLimits(limits) {
246
247
  return parts.join(' ');
247
248
  }
248
249
 
250
+ // ---------------------------------------------------------------------------
251
+ // Worktree-aware transcript path resolution
252
+ // ---------------------------------------------------------------------------
253
+ // When Claude Code enters a worktree, stdin.transcript_path points to a
254
+ // worktree-specific project directory whose transcript only contains events
255
+ // after worktree entry — todos created before are lost.
256
+ //
257
+ // Fix: detect worktree, locate the original project transcript with the same
258
+ // session ID, and merge todos from the original into the worktree result.
259
+ //
260
+ // Parallel worktrees: each gets its own transcript_path but the original
261
+ // project transcript is shared. Each worktree merges from the same original,
262
+ // so all see the full todo list without interfering with each other.
263
+ function resolveOriginalTranscriptPath(transcriptPath, cwd) {
264
+ if (!transcriptPath || !cwd) return null;
265
+
266
+ const commonDir = gitExec('git rev-parse --git-common-dir', cwd);
267
+ if (!commonDir || commonDir === '.git' || commonDir.startsWith('.')) return null;
268
+
269
+ // commonDir is absolute: /path/to/main-repo/.git
270
+ const mainRepoPath = dirname(commonDir);
271
+ const transcriptFilename = basename(transcriptPath);
272
+ const transcriptDir = dirname(transcriptPath);
273
+ const projectsBase = dirname(transcriptDir);
274
+
275
+ // Convert main repo path to Claude Code project dir format:
276
+ // /Users/foo/myproject → -Users-foo-myproject
277
+ const mainProjectDirName = mainRepoPath.replace(/\//g, '-');
278
+ const originalPath = join(projectsBase, mainProjectDirName, transcriptFilename);
279
+
280
+ if (originalPath === transcriptPath) return null;
281
+ if (existsSync(originalPath)) return originalPath;
282
+ return null;
283
+ }
284
+
249
285
  // ---------------------------------------------------------------------------
250
286
  // Transcript parsing (agents + skills)
251
287
  // ---------------------------------------------------------------------------
@@ -510,7 +546,7 @@ function renderAgentsMultiLine(agents, maxLines = 5) {
510
546
  const desc = a.description.length > 40 ? a.description.slice(0, 37) + '...' : a.description;
511
547
 
512
548
  detailLines.push(
513
- `${dim(prefix)} ${cyan(name)} ${dim(model)} : ${desc} ${dim(`(${duration})`)}`
549
+ `${dim(prefix)} ${cyan(name)} ${subdued(model)} : ${desc} ${subdued(`(${duration})`)}`
514
550
  );
515
551
  });
516
552
 
@@ -534,7 +570,7 @@ function renderTodosLine(todos) {
534
570
 
535
571
  if (!inProgress) {
536
572
  if (completed === total && total > 0) {
537
- return `${green('\u2713')} all done ${dim(`(${completed}/${total})`)}`;
573
+ return `${green('\u2713')} all done ${subdued(`(${completed}/${total})`)}`;
538
574
  }
539
575
  return null;
540
576
  }
@@ -542,7 +578,7 @@ function renderTodosLine(todos) {
542
578
  const content = inProgress.content.length > 50
543
579
  ? inProgress.content.slice(0, 47) + '...'
544
580
  : inProgress.content;
545
- return `${yellow('\u25b8')} ${content} ${dim(`(${completed}/${total})`)}`;
581
+ return `${yellow('\u25b8')} ${content} ${subdued(`(${completed}/${total})`)}`;
546
582
  }
547
583
 
548
584
  // ---------------------------------------------------------------------------
@@ -628,6 +664,21 @@ async function main() {
628
664
 
629
665
  const transcript = parseTranscript(stdin.transcript_path);
630
666
 
667
+ // In worktrees, merge todos from the original project transcript.
668
+ // The worktree transcript only has events after EnterWorktree, so todos
669
+ // created before are missing. The original transcript has the full history.
670
+ // If the worktree transcript also has todos (created after worktree entry),
671
+ // use the worktree's todos since they are more up-to-date for that session.
672
+ if (transcript.todos.length === 0) {
673
+ const originalPath = resolveOriginalTranscriptPath(stdin.transcript_path, cwd);
674
+ if (originalPath) {
675
+ const originalTranscript = parseTranscript(originalPath);
676
+ if (originalTranscript.todos.length > 0) {
677
+ transcript.todos = originalTranscript.todos;
678
+ }
679
+ }
680
+ }
681
+
631
682
  const { headerPart, detailLines } = renderAgentsMultiLine(transcript.agents);
632
683
  if (headerPart) {
633
684
  midElements.push(headerPart);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlabsio/claude-crew",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "1인 SaaS 개발자를 위한 멀티 에이전트 오케스트레이션 — 개발, 마케팅, 일정을 한 대화에서 통합 관리",
5
5
  "author": "Jaejin Song <wowlxx28@gmail.com>",
6
6
  "license": "MIT",
@@ -18,6 +18,7 @@
18
18
  "files": [
19
19
  ".claude-plugin/",
20
20
  "agents/",
21
+ "data/",
21
22
  "skills/",
22
23
  "hooks/",
23
24
  "hud/",
@@ -14,6 +14,47 @@ description: contract.md를 입력으로 받아 Dev + CodeReviewer + QA 파이
14
14
 
15
15
  ---
16
16
 
17
+ ## Multi-Provider 지원
18
+
19
+ 에이전트별로 claude 또는 codex provider를 사용할 수 있다. `/crew-setup`에서 설정한 `.crew/config.json`을 기반으로 디스패치한다.
20
+
21
+ ### Provider 설정 해석
22
+
23
+ 오케스트레이터는 Phase 1에서 `.crew/config.json`과 `data/provider-catalog.json`을 읽어 각 에이전트의 provider 설정을 결정한다.
24
+
25
+ **우선순위:**
26
+ 1. `.crew/config.json`의 `providers.{role}` — 있으면 사용
27
+ 2. `data/provider-catalog.json`의 `agent_defaults.{role}` — 폴백
28
+
29
+ **해석된 설정 예시:**
30
+ ```json
31
+ { "provider": "codex", "model": "gpt-5.4", "reasoning": "xhigh" }
32
+ { "provider": "claude", "model": "opus" }
33
+ ```
34
+
35
+ ### 디스패치 규칙
36
+
37
+ **claude provider:**
38
+ ```
39
+ Agent(subagent_type="{role}", model="{model}", description="...", prompt="...")
40
+ ```
41
+ 기존과 동일. `model` 파라미터를 설정값으로 전달한다.
42
+
43
+ **codex provider:**
44
+ ```
45
+ Bash("codex exec --model {model} -c model_reasoning_effort=\"{reasoning}\" --dangerously-bypass-approvals-and-sandbox \"{prompt}\"")
46
+ ```
47
+ - 프롬프트가 길면 임시 파일에 저장 후 `cat`으로 전달한다.
48
+ - Codex는 CWD 기준으로 작업하므로 워크트리 안에서 실행한다.
49
+ - Codex의 stdout에서 결과를 캡처한다.
50
+
51
+ **codex provider 제약:**
52
+ - Codex는 Claude Code의 Read/Edit/Glob 도구가 아닌 자체 도구를 사용한다.
53
+ - Codex 에이전트는 `.crew/` 파일에 직접 접근할 수 없으므로, 프롬프트에 필요한 내용을 인라인으로 주입해야 한다.
54
+ - Codex dev-log.md 작성: Codex stdout을 오케스트레이터가 파싱하여 dev-log.md를 생성한다.
55
+
56
+ ---
57
+
17
58
  ## 절대 금지
18
59
 
19
60
  - 오케스트레이터가 코드를 직접 작성하지 않는다.
@@ -64,7 +105,16 @@ description: contract.md를 입력으로 받아 Dev + CodeReviewer + QA 파이
64
105
 
65
106
  ### Phase 1 — 환경 준비 (오케스트레이터 직접)
66
107
 
67
- **1a. contract.md 유효성 검사**
108
+ **1a. Provider 설정 로드**
109
+
110
+ 1. `data/provider-catalog.json`을 읽어 `agent_defaults`를 로드한다.
111
+ 2. `.crew/config.json`이 있으면 `providers` 필드를 읽어 `agent_defaults`를 오버라이드한다.
112
+ 3. codex provider가 하나라도 설정되어 있으면 `which codex`로 가용성을 확인한다.
113
+ - codex가 없으면 경고를 출력하고 해당 에이전트를 기본값(claude)으로 폴백한다.
114
+
115
+ 해석된 설정을 Phase 2, 3에서 ��용한다.
116
+
117
+ **1b. contract.md 유효성 검사**
68
118
 
69
119
  `.crew/plans/{task-id}/contract.md`를 읽는다.
70
120
  - 파일이 존재하는가?
@@ -82,17 +132,25 @@ contract.md 유효성 검사에 실패했습니다.
82
132
  [3] 이 태스크를 보류
83
133
  ```
84
134
 
85
- **1b. 워크트리 생성**
135
+ **1c. 워크트리 생성**
136
+
137
+ Claude Code의 `EnterWorktree` 도구를 사용한다:
138
+
139
+ ```
140
+ EnterWorktree(name="feat/{task-id}")
141
+ ```
142
+
143
+ 워크트리 진입 후 브랜치를 `origin/main` 기준으로 리셋한다:
86
144
 
87
145
  ```bash
88
146
  git fetch origin main
89
- git worktree add ../{project}-worktree-feat-{task-id} -b feat/{task-id} origin/main
147
+ git reset --hard origin/main
90
148
  ```
91
149
 
92
- 워크트리 디렉토리로 이동한다. 이후 모든 작업은 워크트리에서 수행한다.
93
- 환경 파일(`.env*` 등)이 있으면 복사한다.
150
+ 이후 모든 작업은 워크트리에서 수행한다.
151
+ 환경 파일(`.env*` 등)이 원본 프로젝트에 있으면 복사한다.
94
152
 
95
- **1c. 상태 갱신**
153
+ **1d. 상태 갱신**
96
154
 
97
155
  contract.md의 `## 상태` 섹션을 갱신한다:
98
156
 
@@ -105,12 +163,14 @@ IN_PROGRESS — Dev 에이전트가 구현 중이다.
105
163
 
106
164
  ### Phase 2 — 구현 (Dev 에이전트)
107
165
 
108
- **모델**: opus
166
+ Phase 1a에서 해석된 dev 설정에 따라 디스패치한다.
167
+
168
+ #### Phase 2 — claude provider인 경우
109
169
 
110
170
  호출:
111
171
 
112
172
  ```
113
- Agent(subagent_type="dev", description="Dev: {task-id} 구현", prompt="...")
173
+ Agent(subagent_type="dev", model="{설정된 모델}", description="Dev: {task-id} 구현", prompt="...")
114
174
  ```
115
175
 
116
176
  **첫 번째 실행 시 에이전트 프롬프트**:
@@ -180,6 +240,53 @@ brief.md, spec.md, analysis.md는 읽지 않는다.
180
240
  - 자체 검증 5개 모두 PASS 해야 완료를 선언할 수 있다.
181
241
  ```
182
242
 
243
+ #### Phase 2 — codex provider인 경우
244
+
245
+ 오케스트레이터가 다음을 수행한다:
246
+
247
+ 1. plan.md와 contract.md의 내용을 읽어 프롬프트에 인라인으로 주입한다.
248
+ 2. Codex를 실행한다:
249
+
250
+ ```bash
251
+ codex exec --model {model} -c model_reasoning_effort="{reasoning}" --dangerously-bypass-approvals-and-sandbox "$(cat <<'PROMPT'
252
+ 당신은 Dev 에이전트다. 아래 plan.md를 기반으로 코드를 구현한다.
253
+
254
+ ## plan.md
255
+ {오케스트레이터가 plan.md 내용을 여기에 인라인 삽입}
256
+
257
+ ## contract.md (수용 기준)
258
+ {오케스트레이터가 contract.md의 수용 기준 섹션을 여기에 인라인 삽입}
259
+
260
+ ## 작업 순서
261
+ 1. 코드베이스를 탐색하여 관련 파일을 파악한다.
262
+ 2. 유저 스토리 단위로 순차 구현한다.
263
+ 3. 모든 구현 완료 후 자체 검증을 실행한다:
264
+ - 빌드 성공 확인
265
+ - 린트 통과 확인
266
+ - 타입 체크 통과 확인
267
+ - 기존 테스트 스위트 통과 확인
268
+ 4. 자체 검증이 실패하면 직접 수정하여 통과시킨다.
269
+
270
+ ## 규칙
271
+ - plan.md에 없는 것을 구현하지 않는다.
272
+ - 자체 검증 모두 PASS 해야 완료를 선언할 수 있다.
273
+ - 기존 코드베이스의 컨벤션을 따른다.
274
+
275
+ ## 완료 시 출력
276
+ 구현 요약을 마지막에 출력하라:
277
+ - 변경한 파일 목록
278
+ - 각 유저 스토리별 구현 내용 1줄 요약
279
+ - 자체 검증 결과 (각 항목별 PASS/FAIL + 명령어 + 출력)
280
+ PROMPT
281
+ )"
282
+ ```
283
+
284
+ 3. Codex stdout을 캡처하여 `.crew/plans/{task-id}/dev-log.md`를 생성한다.
285
+
286
+ **retry 시 (codex provider)**:
287
+
288
+ 동일한 패턴으로, 프롬프트에 review-report-{n}.md와 qa-report-{n}.md 내용을 인라인 삽입한다.
289
+
183
290
  **Phase 2 실패 조건**: Dev 에이전트가 자체 검증을 통과하지 못한 채 완료를 선언하면 에스컬레이션.
184
291
 
185
292
  ---
@@ -190,14 +297,41 @@ CodeReviewer와 QA를 **동시에** Agent tool 2개로 호출한다.
190
297
 
191
298
  #### Phase 3a — CodeReviewer
192
299
 
193
- **모델**: opus
300
+ Phase 1a에서 해석된 code-reviewer 설정에 따라 디스패치한다.
194
301
 
195
- 호출:
302
+ **claude provider 호출:**
196
303
 
197
304
  ```
198
- Agent(subagent_type="code-reviewer", description="CodeReviewer: {task-id} 코드 리뷰", prompt="...")
305
+ Agent(subagent_type="code-reviewer", model="{설정된 모델}", description="CodeReviewer: {task-id} 코드 리뷰", prompt="...")
306
+ ```
307
+
308
+ **codex provider 호출:**
309
+
310
+ 오케스트레이터가 `git diff main...HEAD`를 실행하여 diff를 캡처한 뒤, diff와 가드레일을 프롬프트에 인라인 삽입하여 Codex를 실행한다.
311
+
312
+ ```bash
313
+ codex exec --model {model} -c model_reasoning_effort="{reasoning}" --dangerously-bypass-approvals-and-sandbox "$(cat <<'PROMPT'
314
+ 당신은 CodeReviewer다. 아래 코드 변경 사항의 품질을 판단하라.
315
+
316
+ ## 변경 사항 (git diff)
317
+ {오케스트레이터가 git diff 결과를 인라인 삽입}
318
+
319
+ ## 가드레일
320
+ ### Must
321
+ {contract.md에서 추출한 Must 항목}
322
+ ### Must NOT
323
+ {contract.md에서 추출한 Must NOT 항목}
324
+
325
+ ## 검토 항목 / 판정 규칙 / 출력 형식
326
+ (claude provider 프롬프트와 동일)
327
+ PROMPT
328
+ )"
199
329
  ```
200
330
 
331
+ Codex stdout을 캡처하여 review-report.md 내용으로 사용한다.
332
+
333
+ **공통 사전 작업 (provider 무관):**
334
+
201
335
  오케스트레이터가 해야 할 사전 작업:
202
336
  1. contract.md에서 가드레일 섹션(Must/Must NOT)만 추출한다.
203
337
  2. 가드레일을 CodeReviewer 프롬프트에 인라인으로 주입한다.
@@ -239,12 +373,20 @@ contract.md, plan.md, brief.md, spec.md, dev-log.md는 읽지 않는다.
239
373
 
240
374
  #### Phase 3b — QA
241
375
 
242
- **모델**: sonnet
376
+ Phase 1a에서 해석된 qa 설정에 따라 디스패치한다.
243
377
 
244
- 호출:
378
+ **claude provider 호출:**
245
379
 
246
380
  ```
247
- Agent(subagent_type="qa", description="QA: {task-id} 검증", prompt="...")
381
+ Agent(subagent_type="qa", model="{설정된 모델}", description="QA: {task-id} 검증", prompt="...")
382
+ ```
383
+
384
+ **codex provider 호출:**
385
+
386
+ 오케스트레이터가 plan.md 내용을 읽어 프롬프트에 인라인 삽입하여 Codex를 실행한다. Codex stdout을 캡처하여 qa-report.md 내용으로 사용한다.
387
+
388
+ ```bash
389
+ codex exec --model {model} -c model_reasoning_effort="{reasoning}" --dangerously-bypass-approvals-and-sandbox "{QA 프롬프트 + plan.md 인라인}"
248
390
  ```
249
391
 
250
392
  에이전트 프롬프트:
@@ -290,11 +432,16 @@ CodeReviewer와 QA 에이전트는 read-only이므로 파일을 직접 작성하
290
432
 
291
433
  **Phase 3 병렬 실행 방법**:
292
434
 
293
- 오케스트레이터는 번의 메시지에서 개의 Agent tool 호출을 동시에 수행한다:
435
+ 오케스트레이터는 CodeReviewer와 QA를 **동시에** 호출한다. provider 조합에 따라:
436
+
437
+ - 둘 다 claude → Agent tool 2개를 한 번에 호출
438
+ - 둘 다 codex → Bash tool 2개를 한 번에 호출
439
+ - 혼합 → Agent + Bash를 한 번에 호출
294
440
 
295
441
  ```
296
- Agent(subagent_type="code-reviewer", description="CodeReviewer: {task-id} 코드 리뷰", prompt="...")
297
- Agent(subagent_type="qa", description="QA: {task-id} 검증", prompt="...")
442
+ # 예: code-reviewer=claude, qa=codex
443
+ Agent(subagent_type="code-reviewer", model="opus", description="CodeReviewer: {task-id}", prompt="...")
444
+ Bash("codex exec --model gpt-5.4 ... '{QA 프롬프트}'")
298
445
  ```
299
446
 
300
447
  ---
@@ -339,7 +486,15 @@ PR: {PR URL}
339
486
 
340
487
  `.dev_loop_count` 파일이 존재하면 삭제한다.
341
488
 
342
- **5d. 완료 반환**
489
+ **5d. 워크트리 종료**
490
+
491
+ ```
492
+ ExitWorktree(action="remove")
493
+ ```
494
+
495
+ PR push가 완료되었으므로 로컬 워크트리를 제거하고 원본 프로젝트 디렉토리로 복귀한다.
496
+
497
+ **5e. 완료 반환**
343
498
 
344
499
  ```
345
500
  상태: COMPLETE
@@ -378,6 +533,7 @@ Phase 4에서 FAIL이면:
378
533
  에스컬레이션 시:
379
534
  - `.dev_loop_count` 파일을 삭제한다.
380
535
  - contract.md 상태를 `BLOCKED`으로 갱신한다.
536
+ - `ExitWorktree(action="keep")`으로 원본 프로젝트 디렉토리로 복귀한다.
381
537
 
382
538
  **조건 2 — 같은 기준 3회 연속 실패**:
383
539
 
@@ -391,6 +547,8 @@ review-report.md와 qa-report.md에서 FAIL 항목을 확인한다.
391
547
  [3] 이 태스크를 보류
392
548
  ```
393
549
 
550
+ 에스컬레이션 시 `ExitWorktree(action="keep")`으로 원본 프로젝트 디렉토리로 복귀한다.
551
+
394
552
  **6c. 피드백 아카이브**
395
553
 
396
554
  `n = 카운터 + 1`
@@ -1,13 +1,17 @@
1
1
  ---
2
2
  name: crew-setup
3
- description: claude-crew 플러그인 초기 설정 — HUD statusline 설치
3
+ description: claude-crew 플러그인 초기 설정 — HUD statusline 설치 + provider 설정
4
4
  ---
5
5
 
6
6
  ## 역할
7
7
 
8
- claude-crew 플러그인의 HUD statusline을 사용자 환경에 설치한다.
8
+ claude-crew 플러그인의 초기 설정을 수행한다:
9
+ 1. HUD statusline 설치
10
+ 2. 에이전트별 provider/model 설정
9
11
 
10
- ## 절차
12
+ ---
13
+
14
+ ## Step 1 — HUD statusline 설치
11
15
 
12
16
  1. `~/.claude/settings.json`을 읽는다.
13
17
  2. `statusLine` 필드를 crew HUD 스크립트로 설정한다:
@@ -23,7 +27,115 @@ claude-crew 플러그인의 HUD statusline을 사용자 환경에 설치한다.
23
27
  - 성공 시: "CREW HUD가 설치되었습니다. 다음 세션부터 statusline에 표시됩니다."
24
28
  - 실패 시: 에러 내용을 알린다.
25
29
 
26
- ## 주의
30
+ **주의**: `settings.json`의 다른 필드는 절대 수정하지 않는다. statusLine만 교체한다.
31
+
32
+ ---
33
+
34
+ ## Step 2 — Provider 설정
35
+
36
+ 에이전트별로 어떤 provider(claude/codex)와 model을 사용할지 설정한다.
37
+
38
+ ### 2a. 카탈로그 로드
39
+
40
+ `data/provider-catalog.json`을 읽어 사용 가능한 provider와 model 목록을 로드한다.
41
+
42
+ ### 2b. Codex CLI 가용성 확인
43
+
44
+ ```bash
45
+ which codex
46
+ ```
47
+
48
+ - codex가 없으면: "Codex CLI가 설치되어 있지 않습니다. 모든 에이전트가 Claude를 사용합니다." 안내 후 Step 2를 스킵한다.
49
+ - codex가 있으면: 계속 진행한다.
50
+
51
+ ### 2c. 기존 설정 표시
52
+
53
+ `.crew/config.json`이 있으면 현재 설정을 표시한다. 없으면 기본값을 표시한다.
54
+
55
+ ```
56
+ 현재 에이전트 설정:
57
+ - dev: claude / opus (기본값)
58
+ - code-reviewer: claude / opus (기본값)
59
+ - qa: claude / sonnet (기본값)
60
+ ```
61
+
62
+ ### 2d. 설정할 에이전트 선택
63
+
64
+ 사용자에게 설정을 변경할 에이전트를 선택하게 한다.
65
+
66
+ ```
67
+ 설정을 변경할 에이전트를 선택하세요 (쉼표 구분, 엔터 = 스킵):
68
+ dev, code-reviewer, qa
69
+ ```
70
+
71
+ - 사용자가 엔터만 누르거나 "없음"/"스킵"을 입력하면 Step 2를 종료한다.
72
+ - 예시 입력: `dev`, `dev, code-reviewer`
73
+
74
+ ### 2e. 선택된 에이전트별 설정
75
+
76
+ 선택된 각 에이전트에 대해 순차적으로:
77
+
78
+ **Provider 선택:**
79
+ ```
80
+ ── {agent} ──
81
+ Provider:
82
+ [1] claude
83
+ [2] codex
84
+ ```
85
+
86
+ **Model 선택 (provider에 따라 목록이 다름):**
87
+
88
+ 카탈로그에서 해당 provider의 models 배열을 번호 목록으로 표시한다. 1번이 추천.
89
+
90
+ claude 선택 시:
91
+ ```
92
+ Model:
93
+ [1] Opus 4.6 — 최고 품질, 복잡한 구현 (추천)
94
+ [2] Sonnet 4.6 — 빠르고 저렴, Opus급 성능
95
+ [3] Haiku 4.5 — 최저 비용, 단순 태스크
96
+ ```
97
+
98
+ codex 선택 시:
99
+ ```
100
+ Model:
101
+ [1] GPT-5.4 xhigh (추천) — 최고 성능, 토큰 多
102
+ [2] GPT-5.4 high — 고성능, 균형잡힌 비용
103
+ [3] GPT-5.4 medium — 빠르고 저렴
104
+ [4] o3 high — 추론 특화
105
+ [5] o3 medium — 추론 특화, 저비용
106
+ [6] GPT-5.4 Mini — 최저 비용
107
+ ```
108
+
109
+ ### 2f. 설정 저장
110
+
111
+ 선택 결과를 `.crew/config.json`에 저장한다.
112
+
113
+ **규칙:**
114
+ - 기본값과 동일한 설정은 저장하지 않는다 (기본값은 `agent_defaults` 참조).
115
+ - 기본값과 다른 설정만 `providers` 객체에 기록한다.
116
+ - 기존 `.crew/config.json`이 있으면 `providers` 필드만 머지한다 (다른 필드 보존).
117
+ - `.crew/` 디렉토리가 없으면 생성한다.
118
+
119
+ **저장 형식:**
120
+
121
+ ```json
122
+ {
123
+ "providers": {
124
+ "dev": { "provider": "codex", "model": "gpt-5.4", "reasoning": "xhigh" }
125
+ }
126
+ }
127
+ ```
128
+
129
+ - claude provider일 때: `reasoning` 필드 생략
130
+ - codex provider일 때: 카탈로그의 `reasoning` 값 포함 (`null`이면 생략)
131
+
132
+ ### 2g. 확인 메시지
133
+
134
+ ```
135
+ ✓ Provider 설정 완료:
136
+ - dev: codex / gpt-5.4 xhigh
137
+ - code-reviewer: claude / opus (기본값)
138
+ - qa: claude / sonnet (기본값)
27
139
 
28
- - `settings.json`의 다른 필드는 절대 수정하지 않는다.
29
- - statusLine만 교체한다.
140
+ 설정 파일: .crew/config.json
141
+ ```