@walwal-harness/cli 4.0.0 → 5.0.1

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/bin/init.js CHANGED
@@ -16,6 +16,7 @@ const { execSync } = require('child_process');
16
16
  const PKG_ROOT = path.resolve(__dirname, '..');
17
17
  const args = process.argv.slice(2);
18
18
  const subcommand = args.find(a => !a.startsWith('-')) || null;
19
+ const subcommandArgs = args.filter(a => !a.startsWith('-') && a !== subcommand);
19
20
  const isAuto = args.includes('--auto');
20
21
  const isForce = args.includes('--force');
21
22
  const isHelp = args.includes('--help') || args.includes('-h');
@@ -100,14 +101,26 @@ function scaffoldHarness() {
100
101
  ensureDir(path.join(HARNESS_DIR, 'archive'));
101
102
  ensureDir(path.join(HARNESS_DIR, 'gotchas'));
102
103
 
103
- // Copy gotchas initial files
104
+ // Copy gotchas — ALWAYS overwrite system templates, preserve user custom entries
104
105
  const gotchasSrc = path.join(PKG_ROOT, 'gotchas');
105
106
  if (fs.existsSync(gotchasSrc)) {
107
+ const CUSTOM_MARKER = '## Custom Gotchas';
106
108
  const files = fs.readdirSync(gotchasSrc);
107
109
  for (const file of files) {
108
- const dest = path.join(HARNESS_DIR, 'gotchas', file);
109
- if (!fileExists(dest) || isForce) {
110
- copyFile(path.join(gotchasSrc, file), dest);
110
+ const destPath = path.join(HARNESS_DIR, 'gotchas', file);
111
+ const srcPath = path.join(gotchasSrc, file);
112
+ if (fileExists(destPath) && file.endsWith('.md')) {
113
+ // Preserve user-added custom section
114
+ const existing = fs.readFileSync(destPath, 'utf8');
115
+ const customIdx = existing.indexOf(CUSTOM_MARKER);
116
+ const userCustom = customIdx !== -1 ? existing.substring(customIdx) : '';
117
+ let newContent = fs.readFileSync(srcPath, 'utf8');
118
+ if (userCustom) {
119
+ newContent = newContent.trimEnd() + '\n\n' + userCustom;
120
+ }
121
+ fs.writeFileSync(destPath, newContent);
122
+ } else {
123
+ copyFile(srcPath, destPath);
111
124
  }
112
125
  }
113
126
  }
@@ -129,6 +142,25 @@ function scaffoldHarness() {
129
142
  }
130
143
  }
131
144
 
145
+ // Migrate progress.json v1 → v2 (add mode + team_state fields)
146
+ const progressPath = path.join(HARNESS_DIR, 'progress.json');
147
+ if (fileExists(progressPath)) {
148
+ try {
149
+ const progress = JSON.parse(fs.readFileSync(progressPath, 'utf8'));
150
+ if (!progress.version || progress.version < 2) {
151
+ progress.version = 2;
152
+ progress.mode = progress.mode || 'solo';
153
+ if (!progress.team_state) {
154
+ progress.team_state = { active_teams: 0, paused_at: null, resume_from: null };
155
+ }
156
+ fs.writeFileSync(progressPath, JSON.stringify(progress, null, 2) + '\n');
157
+ log('progress.json migrated to v2 (mode + team_state added)');
158
+ }
159
+ } catch (e) {
160
+ log('WARNING: Could not migrate progress.json');
161
+ }
162
+ }
163
+
132
164
  // config.json — ALWAYS update (harness system file, not user data)
133
165
  // But preserve user's custom settings (pre_eval_gate.frontend_cwd, behavior, etc.)
134
166
  const configSrc = path.join(PKG_ROOT, 'assets', 'templates', 'config.json');
@@ -286,6 +318,42 @@ function installScripts() {
286
318
  log('Scripts installation complete');
287
319
  }
288
320
 
321
+ // ─────────────────────────────────────────
322
+ // 3a. Commands → .claude/commands/
323
+ // ─────────────────────────────────────────
324
+ function installCommands() {
325
+ log('Installing commands to .claude/commands/...');
326
+
327
+ const commandsSrc = path.join(PKG_ROOT, 'commands');
328
+ if (!fs.existsSync(commandsSrc)) {
329
+ log('WARNING: commands/ directory not found in package');
330
+ return;
331
+ }
332
+
333
+ const commandsDest = path.join(PROJECT_ROOT, '.claude', 'commands');
334
+ ensureDir(commandsDest);
335
+
336
+ // Remove existing harness-* commands to prevent stale files
337
+ if (fs.existsSync(commandsDest)) {
338
+ const existing = fs.readdirSync(commandsDest);
339
+ for (const f of existing) {
340
+ if (f.startsWith('harness-')) {
341
+ fs.unlinkSync(path.join(commandsDest, f));
342
+ }
343
+ }
344
+ log(' Cleared existing harness-* commands');
345
+ }
346
+
347
+ // Copy all command files
348
+ const files = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
349
+ for (const file of files) {
350
+ copyFile(path.join(commandsSrc, file), path.join(commandsDest, file));
351
+ log(` Installed: /${file.replace('.md', '')}`);
352
+ }
353
+
354
+ log('Commands installation complete');
355
+ }
356
+
289
357
  // ─────────────────────────────────────────
290
358
  // 3b. SessionStart hook
291
359
  // ─────────────────────────────────────────
@@ -615,45 +683,43 @@ function showHelp() {
615
683
  ╚══════════════════════════════════════╝
616
684
 
617
685
  Usage:
618
- npx walwal-harness Initialize project for harness engineering
619
- npx walwal-harness --force Re-initialize (overwrites existing files)
620
- npx walwal-harness studio Launch Harness Studio v3 (tmux 5-pane)
621
- npx walwal-harness studio --ai Studio v3 + AI eval summary
622
- npx walwal-harness v4 Enable Agent Teams (set env var + init queue)
623
- npx walwal-harness --help Show this help
686
+ npx walwal-harness Initialize project for harness engineering
687
+ npx walwal-harness --force Re-initialize (overwrites existing files)
688
+ npx walwal-harness team Launch Team Mode (tmux studio + auto teams)
689
+ npx walwal-harness team --kill Kill Team Mode tmux session
690
+ npx walwal-harness --help Show this help
691
+
692
+ Modes (use inside claude/codex session):
693
+ /harness-solo Solo mode — prompting-based sequential pipeline
694
+ /harness-team Team mode — auto parallel Gen-Eval loop (3 teams)
695
+ /harness-stop Stop Team mode (preserves queue state)
624
696
 
625
697
  What it does:
626
698
  1. Scaffolds .harness/ directory (actions, archive, gotchas, config)
627
699
  2. Installs skills to .claude/skills/ (dispatcher, planner, generators, evaluators)
628
- 3. Copies helper scripts to scripts/
629
- 4. Registers SessionStart hook (compact boot-time status)
630
- 5. Installs statusline (persistent 1-line status bar at terminal bottom)
631
- 6. Registers UserPromptSubmit hook (auto-route every prompt through harness-dispatcher)
700
+ 3. Installs commands to .claude/commands/ (solo, team, stop)
701
+ 4. Copies helper scripts to scripts/
702
+ 5. Registers SessionStart + UserPromptSubmit hooks
703
+ 6. Installs statusline (persistent 1-line status bar)
632
704
  7. Creates AGENTS.md + CLAUDE.md symlink
633
- 8. Checks Playwright MCP configuration
634
- 9. Checks recommended external skills (Vercel, design skills)
635
-
636
- Auto routing:
637
- Every user prompt is routed through harness-dispatcher.
638
- Per-message opt-out: say "harness skip" or "harness 없이".
639
- Global disable: edit .harness/config.json → behavior.auto_route_dispatcher = false
640
705
 
641
706
  After init:
642
- 1. Restart Claude Code session (exit and re-enter) for skills to load
643
- 2. Say "하네스 엔지니어링 시작" or invoke /harness-dispatcher
707
+ 1. Restart Claude Code session (/exit → re-enter directory)
708
+ 2. Say "하네스 엔지니어링 시작" or /harness-dispatcher
709
+ 3. After Planner completes: /harness-team (team) or continue prompting (solo)
644
710
  `);
645
711
  }
646
712
 
647
713
  // ─────────────────────────────────────────
648
714
  // Main
649
715
  // ─────────────────────────────────────────
650
- function runStudio() {
651
- const useAi = args.includes('--ai') ? '--ai' : '';
716
+ function runTeamStudio() {
717
+ const killMode = args.includes('--kill');
652
718
  const scriptsDir = path.join(PKG_ROOT, 'scripts');
653
719
  const tmuxScript = path.join(scriptsDir, 'harness-tmux.sh');
654
720
 
655
721
  if (!fs.existsSync(tmuxScript)) {
656
- log('ERROR: harness-tmux.sh not found. Update @walwal-harness/cli to >= 3.6.0');
722
+ log('ERROR: harness-tmux.sh not found.');
657
723
  process.exit(1);
658
724
  }
659
725
 
@@ -664,57 +730,18 @@ function runStudio() {
664
730
  process.exit(1);
665
731
  }
666
732
 
667
- const cmd = `bash "${tmuxScript}" "${PROJECT_ROOT}" ${useAi}`.trim();
668
- log(`Launching Harness Studio...`);
669
- execSync(cmd, { stdio: 'inherit' });
670
- }
671
-
672
- function runStudioV4() {
673
- // Enable Agent Teams in project settings
674
- const settingsPath = path.join(PROJECT_ROOT, '.claude', 'settings.json');
675
- let settings = {};
676
- if (fileExists(settingsPath)) {
677
- try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (e) {}
733
+ if (killMode) {
734
+ const cmd = `bash "${tmuxScript}" --kill`;
735
+ execSync(cmd, { stdio: 'inherit' });
736
+ return;
678
737
  }
679
738
 
680
- if (!settings.env) settings.env = {};
681
- settings.env['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] = '1';
682
- ensureDir(path.dirname(settingsPath));
683
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
684
-
685
- // Initialize feature queue if feature-list.json exists
686
- const featureList = path.join(PROJECT_ROOT, '.harness', 'actions', 'feature-list.json');
687
- const featureQueue = path.join(PROJECT_ROOT, '.harness', 'actions', 'feature-queue.json');
688
- const queueMgr = path.join(PKG_ROOT, 'scripts', 'harness-queue-manager.sh');
689
-
690
- if (fs.existsSync(featureList) && fs.existsSync(queueMgr)) {
691
- if (!fs.existsSync(featureQueue)) {
692
- log('Initializing feature queue...');
693
- try { execSync(`bash "${queueMgr}" init "${PROJECT_ROOT}"`, { stdio: 'inherit' }); } catch (e) {}
694
- } else {
695
- log('Recovering feature queue...');
696
- try { execSync(`bash "${queueMgr}" recover "${PROJECT_ROOT}"`, { stdio: 'inherit' }); } catch (e) {}
697
- }
698
- }
739
+ // Enable Agent Teams in project settings
740
+ installAgentTeamsEnv();
699
741
 
700
- console.log('');
701
- log('╔═══════════════════════════════════════════════════════════╗');
702
- log('║ Agent Teams v4 ENABLED ║');
703
- log('╠═══════════════════════════════════════════════════════════╣');
704
- log('║ ║');
705
- log('║ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 set in ║');
706
- log('║ .claude/settings.json ║');
707
- log('║ ║');
708
- log('║ Next steps: ║');
709
- log('║ 1. Restart Claude Code (/exit → re-enter) ║');
710
- log('║ 2. Run Planner: "하네스 엔지니어링 시작" ║');
711
- log('║ 3. Start Teams: /harness-team-action ║');
712
- log('║ ║');
713
- log('║ Or use --teammate-mode tmux for split panes: ║');
714
- log('║ $ claude --teammate-mode tmux ║');
715
- log('║ ║');
716
- log('╚═══════════════════════════════════════════════════════════╝');
717
- console.log('');
742
+ const cmd = `bash "${tmuxScript}" "${PROJECT_ROOT}" --team`;
743
+ log('Launching Team Mode Studio...');
744
+ execSync(cmd, { stdio: 'inherit' });
718
745
  }
719
746
 
720
747
  function main() {
@@ -723,13 +750,15 @@ function main() {
723
750
  return;
724
751
  }
725
752
 
726
- if (subcommand === 'studio') {
727
- runStudio();
753
+ if (subcommand === 'team') {
754
+ runTeamStudio();
728
755
  return;
729
756
  }
730
757
 
731
- if (subcommand === 'studio-v4' || subcommand === 'v4') {
732
- runStudioV4();
758
+ // Legacy subcommands — redirect to new equivalents
759
+ if (subcommand === 'studio' || subcommand === 'studio-v4' || subcommand === 'v4') {
760
+ log('NOTE: "studio" and "v4" subcommands are replaced by "team".');
761
+ runTeamStudio();
733
762
  return;
734
763
  }
735
764
 
@@ -747,6 +776,7 @@ function main() {
747
776
  scaffoldHarness();
748
777
  installSkills();
749
778
  installScripts();
779
+ installCommands();
750
780
  installSessionHook();
751
781
  installStatusline();
752
782
  installUserPromptSubmitHook();
@@ -760,20 +790,21 @@ function main() {
760
790
  log('');
761
791
 
762
792
  if (isAuto) {
763
- // postinstall context — Claude Code is likely already running
764
793
  log('╔═══════════════════════════════════════════════════════════╗');
765
- log('║ IMPORTANT: Restart Claude Code for skills to activate! ║');
766
- log('║ ║');
767
- log('║ Claude Code discovers skills at session startup. ║');
768
- log('║ Type /exit, then re-enter this directory to begin. ║');
794
+ log('║ Restart Claude Code for skills & commands to activate! ║');
769
795
  log('║ ║');
770
796
  log('║ Then say: "하네스 엔지니어링 시작" ║');
771
797
  log('║ Or invoke: /harness-dispatcher ║');
798
+ log('║ ║');
799
+ log('║ After Planner completes: ║');
800
+ log('║ /harness-team → Team mode (auto parallel) ║');
801
+ log('║ /harness-solo → Solo mode (prompting sequential) ║');
772
802
  log('╚═══════════════════════════════════════════════════════════╝');
773
803
  } else {
774
804
  log('Next steps:');
775
805
  log(' 1. Restart Claude Code session (/exit → re-enter directory)');
776
- log(' 2. Say "하네스 엔지니어링 시작" or invoke /harness-dispatcher');
806
+ log(' 2. Say "하네스 엔지니어링 시작" or /harness-dispatcher');
807
+ log(' 3. After Planner: /harness-team (team) or keep prompting (solo)');
777
808
  }
778
809
  console.log('');
779
810
  }
@@ -0,0 +1,68 @@
1
+ # /harness-solo — Solo Mode 시작/전환
2
+
3
+ 프롬프트 기반으로 하네스 파이프라인을 순차 진행합니다.
4
+ Team 모드에서 전환 시, 진행 중이던 피처의 상태를 보존하고 Solo로 이어갑니다.
5
+
6
+ ## 실행 절차
7
+
8
+ ### Step 1: 현재 모드 확인 + 전환
9
+
10
+ ```bash
11
+ # 현재 mode 읽기
12
+ MODE=$(jq -r '.mode // "solo"' .harness/progress.json 2>/dev/null)
13
+ echo "현재 모드: $MODE"
14
+ ```
15
+
16
+ **team → solo 전환 시:**
17
+ ```bash
18
+ # in_progress features를 ready로 복구
19
+ bash scripts/harness-queue-manager.sh recover .
20
+
21
+ # mode를 solo로 설정
22
+ jq '.mode = "solo" | .team_state.active_teams = 0 | .team_state.paused_at = (now | todate)' .harness/progress.json > /tmp/progress_tmp.json && mv /tmp/progress_tmp.json .harness/progress.json
23
+ ```
24
+
25
+ **paused → solo 전환 시:**
26
+ ```bash
27
+ jq '.mode = "solo"' .harness/progress.json > /tmp/progress_tmp.json && mv /tmp/progress_tmp.json .harness/progress.json
28
+ ```
29
+
30
+ ### Step 2: 현재 진행 상태 표시
31
+
32
+ ```bash
33
+ # progress.json 상태 확인
34
+ cat .harness/progress.json | jq '{mode, sprint, current_agent, agent_status, next_agent}'
35
+
36
+ # feature-queue.json이 있으면 남은 피처 표시
37
+ if [ -f .harness/actions/feature-queue.json ]; then
38
+ echo "=== Feature Queue ==="
39
+ bash scripts/harness-queue-manager.sh status .
40
+ fi
41
+ ```
42
+
43
+ ### Step 3: 다음 에이전트 안내
44
+
45
+ progress.json의 `next_agent` 필드를 확인하여 다음 단계를 안내합니다:
46
+
47
+ - **next_agent = "dispatcher"** → "프롬프트로 요구사항을 입력하면 자동으로 dispatcher가 분석합니다."
48
+ - **next_agent = "planner"** → "/harness-planner 를 호출하거나, 계획을 요청하세요."
49
+ - **next_agent = "generator-backend"** → "/harness-generator-backend 를 호출하세요."
50
+ - **next_agent = "generator-frontend"** → "/harness-generator-frontend 를 호출하세요."
51
+ - **next_agent = "evaluator-functional"** → "/harness-evaluator-functional 를 호출하세요."
52
+ - **next_agent = "evaluator-visual"** → "/harness-evaluator-visual 를 호출하세요."
53
+
54
+ ### Step 4: Solo 모드 진행
55
+
56
+ 사용자가 프롬프트로 각 에이전트 스킬을 호출하여 순차 진행합니다.
57
+ 각 에이전트 완료 시 harness-next.sh가 자동으로 다음 에이전트를 결정합니다.
58
+
59
+ feature-queue.json이 존재하는 경우:
60
+ - 피처 완료(evaluator PASS) 시 `bash scripts/harness-queue-manager.sh pass {FEATURE_ID} .`를 호출하여 공유 상태 업데이트
61
+ - Team 모드로 전환해도 이미 완료된 피처는 skip됨
62
+
63
+ ## Solo 모드에서 가능한 작업
64
+
65
+ - 프롬프트로 각 에이전트 순차 실행
66
+ - 코드 직접 수정 및 디버깅
67
+ - 피처 단위 작업 선택
68
+ - Team 모드로 언제든 전환: `/harness-team`
@@ -0,0 +1,53 @@
1
+ # /harness-stop — Team Mode 중단
2
+
3
+ 실행 중인 Team 모드를 안전하게 중단합니다.
4
+ 진행 중인 피처는 ready 상태로 복구되어, 이후 Solo 또는 Team 모드에서 이어갈 수 있습니다.
5
+
6
+ ## 실행 절차
7
+
8
+ ### Step 1: 현재 모드 확인
9
+
10
+ ```bash
11
+ MODE=$(jq -r '.mode // "solo"' .harness/progress.json 2>/dev/null)
12
+ echo "현재 모드: $MODE"
13
+ ```
14
+
15
+ - **solo** → "이미 Solo 모드입니다. Team을 시작하려면 /harness-team 을 사용하세요." 안내 후 중단.
16
+ - **team** → Step 2로 진행.
17
+
18
+ ### Step 2: Queue 상태 보존
19
+
20
+ ```bash
21
+ # in_progress features를 ready로 복구
22
+ bash scripts/harness-queue-manager.sh recover .
23
+
24
+ # 현재 큐 상태 출력
25
+ bash scripts/harness-queue-manager.sh status .
26
+ ```
27
+
28
+ ### Step 3: 모드 전환
29
+
30
+ ```bash
31
+ # mode를 paused로 설정 (team에서 명시적으로 중단)
32
+ jq '.mode = "paused" | .team_state.active_teams = 0 | .team_state.paused_at = (now | todate)' .harness/progress.json > /tmp/progress_tmp.json && mv /tmp/progress_tmp.json .harness/progress.json
33
+ ```
34
+
35
+ ### Step 4: tmux 세션 정리 (선택)
36
+
37
+ ```bash
38
+ # harness-studio tmux 세션이 있으면 정리
39
+ tmux kill-session -t harness-studio 2>/dev/null && echo "tmux 세션 정리 완료" || echo "활성 tmux 세션 없음"
40
+ ```
41
+
42
+ ### Step 5: 재개 안내
43
+
44
+ 다음 안내를 사용자에게 전달합니다:
45
+
46
+ ```
47
+ Team 모드가 안전하게 중단되었습니다.
48
+ 진행 중이던 피처는 ready 상태로 복구되었습니다.
49
+
50
+ 재개 방법:
51
+ /harness-team → Team 모드 재개 (남은 피처 자동 할당)
52
+ /harness-solo → Solo 모드로 전환 (프롬프트 기반 순차 진행)
53
+ ```
@@ -1,35 +1,51 @@
1
- ---
2
- name: harness-team-action
3
- description: "v4 Agent Teams 가동. Studio 레이아웃 구축 → Queue 초기화 → Gen↔Eval 분리 사이클 팀 병렬 실행. 트리거: '/harness-team-action', 'team 시작', '팀 가동'"
4
- disable-model-invocation: false
5
- ---
1
+ # /harness-team — Team Mode 시작/재개
6
2
 
7
- # /harness-team-action — Agent Teams 가동
3
+ Planner가 완료한 feature-list.json의 피처들을 3개 팀이 병렬로 Gen→Eval 사이클을 수행합니다.
8
4
 
9
- ## Step 0: Studio 레이아웃 자동 구축
5
+ ## 실행 절차
10
6
 
11
- 3-column 대시보드 레이아웃을 자동 구축합니다:
7
+ ### Step 0: 선행 조건 확인
12
8
 
13
9
  ```bash
14
- bash scripts/harness-studio-setup.sh .
10
+ # Planner 아티팩트 확인
11
+ [ -f .harness/actions/feature-list.json ] && [ -f .harness/actions/api-contract.json ] && echo "READY" || echo "NOT_READY"
15
12
  ```
16
13
 
17
- 스크립트 출력을 확인합니다:
14
+ - **NOT_READY** → "Planner가 먼저 완료되어야 합니다. /harness-dispatcher 또는 프롬프트로 진행하세요." 안내 후 중단.
15
+ - **READY** → Step 1로 진행.
16
+
17
+ ### Step 1: 모드 전환 + Queue 초기화
18
18
 
19
- - **`Layout ready`** → tmux 안에서 split 완료. 바로 Step 1로.
20
- - **`OPENED_TERMINAL=true`** → 새 Terminal.app 창에 Studio가 자동으로 열림. 바로 Step 1로.
21
- - **`already set up`** → 이미 구축됨. 바로 Step 1로.
19
+ ```bash
20
+ # progress.json에 mode=team 설정
21
+ jq '.mode = "team" | .team_state.active_teams = 3 | .team_state.paused_at = null' .harness/progress.json > /tmp/progress_tmp.json && mv /tmp/progress_tmp.json .harness/progress.json
22
+
23
+ # Queue 초기화 또는 복구
24
+ if [ ! -f .harness/actions/feature-queue.json ]; then
25
+ bash scripts/harness-queue-manager.sh init .
26
+ else
27
+ bash scripts/harness-queue-manager.sh recover .
28
+ fi
29
+
30
+ # Queue 상태 확인
31
+ bash scripts/harness-queue-manager.sh status .
32
+ ```
22
33
 
23
- ## Step 1: Queue 초기화
34
+ ### Step 2: tmux Studio 레이아웃 구축
24
35
 
25
36
  ```bash
26
- if [ ! -f .harness/actions/feature-queue.json ]; then bash scripts/harness-queue-manager.sh init .; else bash scripts/harness-queue-manager.sh recover .; fi && bash scripts/harness-queue-manager.sh status .
37
+ bash scripts/harness-tmux.sh --team
27
38
  ```
28
39
 
29
- ## Step 2: Feature 할당 (dequeue)
40
+ 스크립트 출력을 확인합니다:
41
+ - **`Layout ready`** → tmux 레이아웃 구축 완료. Step 3로.
42
+ - **`OPENED_TERMINAL=true`** → 새 Terminal.app 창에 Studio 레이아웃이 자동 구축됨. Step 3로.
43
+ - **`already set up`** → 이미 구축됨. Step 3로.
44
+
45
+ ### Step 3: Feature 할당 (dequeue)
30
46
 
31
47
  Queue status 결과를 확인하여 `ready` 큐에 feature가 있는지 확인합니다.
32
- ready feature 수와 설정된 concurrency 중 작은 값만큼 팀을 생성합니다 (최대 3).
48
+ ready feature 수와 설정된 concurrency(기본 3) 중 작은 값만큼 팀을 생성합니다.
33
49
 
34
50
  **각 팀마다** dequeue 명령으로 feature를 원자적으로 할당합니다:
35
51
 
@@ -39,7 +55,7 @@ bash scripts/harness-queue-manager.sh dequeue {TEAM_NUMBER} .
39
55
 
40
56
  dequeue 결과로 feature ID가 반환됩니다. 빈 결과면 해당 팀은 생성하지 않습니다.
41
57
 
42
- ## Step 3: Agent 도구로 팀 생성 (Gen↔Eval 분리 사이클)
58
+ ### Step 4: Agent 도구로 팀 생성 (Gen↔Eval 분리 사이클)
43
59
 
44
60
  dequeue로 할당받은 feature마다 **Agent 도구**를 호출합니다.
45
61
  **반드시 `isolation: "worktree"`를 사용**하여 각 팀이 독립된 코드 복사본에서 작업합니다.
@@ -58,6 +74,7 @@ Agent({
58
74
 
59
75
  ```
60
76
  당신은 Harness Team-{N} 워커입니다. 하나의 Feature에 대해 Gen→Eval 사이클을 수행합니다.
77
+ 완료 후 자동으로 다음 Feature를 dequeue하여 연속 작업합니다.
61
78
 
62
79
  ## 할당된 Feature
63
80
  - Feature ID: {FEATURE_ID}
@@ -87,6 +104,7 @@ progress.log에 한 줄씩 남기세요. 대시보드는 3초마다 tail합니
87
104
  | `eval-done` | Eval 결과 수신 | `verdict=PASS score=2.95` |
88
105
  | `result` / `pass` | PASS 확정 | `F-001 PASS — queue.pass` |
89
106
  | `fail` | FAIL 확정(재시도/최종) | `FAIL #1 — AC-2 missing` |
107
+ | `escalate` | 5회 초과 실패 | `F-001 ESCALATED — user intervention required` |
90
108
 
91
109
  **progress.log 기록** (하네스 루트의 progress.log에 append):
92
110
  ```bash
@@ -145,14 +163,10 @@ logev gen-done "{FEATURE_ID} done — {변경파일수} files, {LOC} LOC"
145
163
 
146
164
  ## Phase 2: Evaluator (독립 평가 — Agent 도구 사용)
147
165
 
148
- **Eval 시작 로깅 (하위 단계 포함):**
166
+ **Eval 시작 로깅:**
149
167
  ```bash
150
168
  logev eval-start "{FEATURE_ID} spawning evaluator"
151
169
  bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" update_phase {FEATURE_ID} eval "$HARNESS_ROOT"
152
- # Evaluator가 AC를 하나씩 검증할 때마다:
153
- # logev eval-check "AC-3 — POST /users returns 201"
154
- # 최종:
155
- # logev eval-done "verdict=PASS score=2.95"
156
170
  ```
157
171
 
158
172
  코드 생성이 완료되면 **별도 Agent를 생성하여 평가**합니다.
@@ -199,7 +213,7 @@ FEEDBACK: (FAIL인 경우만) 구체적 수정 지시
199
213
 
200
214
  Evaluator Agent 결과를 확인합니다:
201
215
 
202
- ### PASS인 경우 (VERDICT: PASS, SCORE ≥ 2.80):
216
+ ### PASS인 경우 (VERDICT: PASS, SCORE >= 2.80):
203
217
  ```bash
204
218
  logev result "{FEATURE_ID} PASS score={SCORE} — merging"
205
219
  bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" pass {FEATURE_ID} "$HARNESS_ROOT"
@@ -212,20 +226,31 @@ logev fail "{FEATURE_ID} FAIL #{ATTEMPT} — {사유요약}"
212
226
  ```
213
227
  1. Evaluator의 FEEDBACK을 읽고 코드를 수정 (Phase 1로 돌아감)
214
228
  2. 수정 후 다시 Phase 2 (새 Evaluator Agent 생성 — 이전 Eval 컨텍스트 없음)
215
- 3. 최대 3회 시도. 3회 모두 FAIL이면:
229
+ 3. 최대 **5회** 시도. 5회 모두 FAIL이면:
216
230
  ```bash
217
- logev fail "{FEATURE_ID} FINAL FAIL after 3 attempts"
231
+ logev escalate "{FEATURE_ID} ESCALATED after 5 attempts — user intervention required"
218
232
  bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" fail {FEATURE_ID} "$HARNESS_ROOT"
219
233
  ```
220
- 실패 사유와 마지막 Eval 결과를 Lead에게 반환.
234
+ 실패 사유와 마지막 Eval 결과를 Lead에게 반환. **사용자 개입을 요청**합니다.
235
+
236
+ ## Phase 4: 자동 업무 획득
237
+
238
+ 피처 PASS 또는 최종 FAIL 처리 후:
239
+ 1. Queue 상태 확인: `bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" status "$HARNESS_ROOT"`
240
+ 2. `ready` 큐에 피처가 있으면 → dequeue → **새 Gen-Eval 루프 시작** (Phase 1부터 반복)
241
+ ```bash
242
+ NEW_FEATURE=$(bash "$HARNESS_ROOT/scripts/harness-queue-manager.sh" dequeue {N} "$HARNESS_ROOT")
243
+ logev gen-start "$NEW_FEATURE auto-acquired"
244
+ ```
245
+ 3. `ready=0`이면 → "Team-{N} 대기 중 — 모든 할당 피처 완료" 반환
221
246
  ```
222
247
 
223
- ## Step 4: 결과 수집 및 다음 라운드
248
+ ### Step 5: 결과 수집 및 다음 라운드
224
249
 
225
250
  모든 Team Agent가 완료되면:
226
251
  1. 각 Agent 반환 메시지에서 PASS/FAIL 확인
227
252
  2. Queue 상태 재확인: `bash scripts/harness-queue-manager.sh status .`
228
- 3. ready 큐에 새로 unblock된 feature가 있으면 → **Step 2로 돌아가서** 추가 팀 생성
253
+ 3. ready 큐에 새로 unblock된 feature가 있으면 → **Step 3으로 돌아가서** 추가 팀 생성
229
254
  4. ready=0, in_progress=0이면 → 최종 결과 보고
230
255
 
231
256
  ## 핵심 원칙
@@ -242,3 +267,8 @@ logev fail "{FEATURE_ID} FAIL #{ATTEMPT} — {사유요약}"
242
267
  ### 격리
243
268
  - 각 팀은 `isolation: "worktree"`로 독립 코드 복사본에서 작업
244
269
  - 팀 간 코드 충돌 없음
270
+
271
+ ### 에스컬레이션 (5회 초과)
272
+ - 5회 연속 FAIL 시 사용자 개입 요청
273
+ - escalate 로그 기록 → Dashboard에 즉시 표시
274
+ - 해당 팀은 다음 피처로 이동하지 않고 대기
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "4.0.0",
4
- "description": "Production harness for AI agent engineering — Planner, Generator(BE/FE), Evaluator(Func/Visual), optional Brainstormer (requirements refinement). Supports React and Flutter FE stacks.",
3
+ "version": "5.0.1",
4
+ "description": "Production harness for AI agent engineering — Solo/Team mode, Planner, Generator(BE/FE), Evaluator(Func/Visual), optional Brainstormer. Supports React, Next.js, and Flutter FE stacks.",
5
5
  "bin": {
6
6
  "walwal-harness": "bin/init.js"
7
7
  },
@@ -9,15 +9,17 @@
9
9
  "postinstall": "node bin/init.js --auto",
10
10
  "init": "node bin/init.js",
11
11
  "scan": "bash scripts/scan-project.sh .",
12
- "reset": "node bin/init.js --force",
13
- "studio": "bash scripts/harness-studio.sh",
14
- "studio:kill": "bash scripts/harness-studio.sh --kill"
12
+ "reset": "node bin/init.js --force"
15
13
  },
16
14
  "keywords": [
17
15
  "harness-engineering",
18
16
  "ai-agent",
19
17
  "claude-code",
18
+ "codex",
20
19
  "skill",
20
+ "command",
21
+ "solo-mode",
22
+ "team-mode",
21
23
  "brainstorming",
22
24
  "planner",
23
25
  "generator",
@@ -25,7 +27,6 @@
25
27
  "nestjs",
26
28
  "react",
27
29
  "flutter",
28
- "riverpod",
29
30
  "playwright"
30
31
  ],
31
32
  "author": "",
@@ -34,10 +35,8 @@
34
35
  "bin/",
35
36
  "skills/",
36
37
  "scripts/",
38
+ "commands/",
37
39
  "assets/",
38
40
  "gotchas/"
39
- ],
40
- "dependencies": {
41
- "@walwal-harness/cli": "^3.3.0"
42
- }
41
+ ]
43
42
  }