@walwal-harness/cli 3.2.0 → 3.3.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "harness": {
3
3
  "name": "6-Agent Production Harness",
4
- "version": "3.2.0",
4
+ "version": "3.3.0",
5
5
  "description": "Dispatcher + NestJS MSA + React/Next.js + Playwright 기반 실무 하네스",
6
6
  "source": "https://www.anthropic.com/engineering/harness-design-long-running-apps"
7
7
  },
@@ -10,7 +10,9 @@
10
10
  "role": "사용자 요청 분석 → 파이프라인 결정 (FE-ONLY / BE-ONLY / FULLSTACK)",
11
11
  "skill": "harness-dispatcher",
12
12
  "outputs": ["actions/pipeline.json"],
13
- "always_first": true
13
+ "always_first": true,
14
+ "model": "opus",
15
+ "thinking_mode": null
14
16
  },
15
17
  "brainstorming": {
16
18
  "role": "사용자의 러프한 요구사항을 대화형으로 구체화하여 Planner가 바로 쓸 수 있는 brainstorm-spec.md로 변환.",
@@ -33,6 +35,9 @@
33
35
  },
34
36
  "invoked_by": "dispatcher (user opt-in after suggestion)",
35
37
  "next_on_complete": "planner",
38
+ "model": "opus",
39
+ "thinking_mode": "plan",
40
+ "thinking_mode_description": "Claude Code plan 모드로 실행. 구조화된 계획 수립 후 실행.",
36
41
  "attribution": "Derived from obra/superpowers skills/brainstorming (MIT License)"
37
42
  },
38
43
  "planner": {
@@ -43,21 +48,30 @@
43
48
  "actions/plan.md",
44
49
  "actions/feature-list.json",
45
50
  "actions/api-contract.json"
46
- ]
51
+ ],
52
+ "model": "opus",
53
+ "thinking_mode": "ultraplan",
54
+ "thinking_mode_description": "최고 수준의 계획 모드. 아키텍처 결정, API 설계, 서비스 분할에 깊은 사고 필요."
47
55
  },
48
56
  "generator-backend": {
49
57
  "role": "NestJS MSA 서비스 구현, API 엔드포인트, DB 스키마",
50
58
  "skill": "harness-generator-backend",
51
59
  "inputs": ["actions/plan.md", "actions/feature-list.json", "actions/api-contract.json", "actions/sprint-contract.md"],
52
60
  "outputs": ["code:backend", "actions/sprint-contract.md"],
53
- "order": 1
61
+ "order": 1,
62
+ "model": "sonnet",
63
+ "thinking_mode": null,
64
+ "model_rationale": "코드 생성은 Sonnet이 비용 대비 효율적. 계획은 Planner가 이미 완료."
54
65
  },
55
66
  "generator-frontend": {
56
67
  "role": "React/Next.js UI 컴포넌트, 상태관리, API 연동",
57
68
  "skill": "harness-generator-frontend",
58
69
  "inputs": ["actions/plan.md", "actions/feature-list.json", "actions/api-contract.json", "actions/sprint-contract.md"],
59
70
  "outputs": ["code:frontend", "actions/sprint-contract.md"],
60
- "order": 2
71
+ "order": 2,
72
+ "model": "sonnet",
73
+ "thinking_mode": null,
74
+ "model_rationale": "코드 생성은 Sonnet이 비용 대비 효율적. 계획은 Planner가 이미 완료."
61
75
  },
62
76
  "evaluator-functional": {
63
77
  "role": "Playwright로 E2E 기능 검증, API 응답/DB 상태 확인",
@@ -65,6 +79,9 @@
65
79
  "tools": ["playwright:browser_*"],
66
80
  "inputs": ["actions/sprint-contract.md", "actions/feature-list.json", "actions/api-contract.json"],
67
81
  "outputs": ["actions/evaluation-functional.md"],
82
+ "model": "opus",
83
+ "thinking_mode": "ultrathink",
84
+ "thinking_mode_description": "최고 수준의 사고 모드. 적대적 검증에 깊은 추론이 필요. 각 항목을 비판적으로 분석.",
68
85
  "evaluation_template": "assets/templates/evaluation-functional.md.template",
69
86
  "adversarial_rules": {
70
87
  "comment": "Evaluator 적대적 행동 규칙 — 통과 편향 제거를 위한 강제 규칙",
@@ -92,6 +109,9 @@
92
109
  "tools": ["playwright:browser_take_screenshot", "playwright:browser_resize", "playwright:browser_snapshot"],
93
110
  "inputs": ["actions/sprint-contract.md", "actions/evaluation-functional.md", "actions/feature-list.json"],
94
111
  "outputs": ["actions/evaluation-visual.md"],
112
+ "model": "opus",
113
+ "thinking_mode": "ultrathink",
114
+ "thinking_mode_description": "최고 수준의 사고 모드. 시각적 결함, 접근성, AI슬롭을 비판적으로 탐지.",
95
115
  "evaluation_template": "assets/templates/evaluation-visual.md.template",
96
116
  "adversarial_rules": {
97
117
  "comment": "Visual Evaluator 적대적 행동 규칙",
@@ -119,7 +139,9 @@
119
139
  "inputs": ["actions/plan.md", "actions/feature-list.json", "actions/api-contract.json", "actions/sprint-contract.md"],
120
140
  "outputs": ["code:flutter", "actions/sprint-contract.md"],
121
141
  "order": 2,
122
- "fe_stack": "flutter"
142
+ "fe_stack": "flutter",
143
+ "model": "sonnet",
144
+ "thinking_mode": null
123
145
  },
124
146
  "evaluator-functional-flutter": {
125
147
  "role": "Flutter 앱 검증 — flutter analyze, flutter test, build_runner 일관성, 안티패턴 정적 검증",
@@ -127,7 +149,9 @@
127
149
  "tools": ["bash:flutter", "bash:dart"],
128
150
  "inputs": ["actions/sprint-contract.md"],
129
151
  "outputs": ["actions/evaluation-functional.md"],
130
- "fe_stack": "flutter"
152
+ "fe_stack": "flutter",
153
+ "model": "opus",
154
+ "thinking_mode": "ultrathink"
131
155
  }
132
156
  },
133
157
  "flow": {
package/bin/init.js CHANGED
@@ -303,7 +303,44 @@ function installSessionHook() {
303
303
  }
304
304
 
305
305
  // ─────────────────────────────────────────
306
- // 3c. UserPromptSubmit hook (auto dispatcher routing)
306
+ // 3c. Statusline (persistent status bar)
307
+ // ─────────────────────────────────────────
308
+ function installStatusline() {
309
+ log('Installing statusline...');
310
+
311
+ const settingsDir = path.join(PROJECT_ROOT, '.claude');
312
+ const settingsFile = path.join(settingsDir, 'settings.json');
313
+
314
+ ensureDir(settingsDir);
315
+
316
+ let settings = {};
317
+ if (fileExists(settingsFile)) {
318
+ try {
319
+ settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
320
+ } catch (e) {
321
+ log('WARNING: Could not parse existing .claude/settings.json, creating new');
322
+ }
323
+ }
324
+
325
+ // Check if statusLine is already configured
326
+ if (settings.statusLine && settings.statusLine.command &&
327
+ settings.statusLine.command.includes('harness-statusline')) {
328
+ log('Statusline already installed');
329
+ return;
330
+ }
331
+
332
+ settings.statusLine = {
333
+ type: 'command',
334
+ command: 'bash scripts/harness-statusline.sh',
335
+ refreshInterval: 3
336
+ };
337
+
338
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
339
+ log('Statusline installed — persistent status bar at terminal bottom');
340
+ }
341
+
342
+ // ─────────────────────────────────────────
343
+ // 3d. UserPromptSubmit hook (auto dispatcher routing)
307
344
  // ─────────────────────────────────────────
308
345
  function installUserPromptSubmitHook() {
309
346
  log('Installing UserPromptSubmit hook (auto dispatcher routing)...');
@@ -514,11 +551,12 @@ What it does:
514
551
  1. Scaffolds .harness/ directory (actions, archive, gotchas, config)
515
552
  2. Installs skills to .claude/skills/ (dispatcher, planner, generators, evaluators)
516
553
  3. Copies helper scripts to scripts/
517
- 4. Registers SessionStart hook (boot-time progress render)
518
- 5. Registers UserPromptSubmit hook (auto-route every prompt through harness-dispatcher)
519
- 6. Creates AGENTS.md + CLAUDE.md symlink
520
- 7. Checks Playwright MCP configuration
521
- 8. Checks recommended external skills (Vercel, design skills)
554
+ 4. Registers SessionStart hook (compact boot-time status)
555
+ 5. Installs statusline (persistent 1-line status bar at terminal bottom)
556
+ 6. Registers UserPromptSubmit hook (auto-route every prompt through harness-dispatcher)
557
+ 7. Creates AGENTS.md + CLAUDE.md symlink
558
+ 8. Checks Playwright MCP configuration
559
+ 9. Checks recommended external skills (Vercel, design skills)
522
560
 
523
561
  Auto routing:
524
562
  Every user prompt is routed through harness-dispatcher.
@@ -555,6 +593,7 @@ function main() {
555
593
  installSkills();
556
594
  installScripts();
557
595
  installSessionHook();
596
+ installStatusline();
558
597
  installUserPromptSubmitHook();
559
598
  setupAgentsMd();
560
599
  checkPlaywrightMcp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
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.",
5
5
  "bin": {
6
6
  "walwal-harness": "bin/init.js"
@@ -36,6 +36,6 @@
36
36
  "gotchas/"
37
37
  ],
38
38
  "dependencies": {
39
- "@walwal-harness/cli": "^3.2.0"
39
+ "@walwal-harness/cli": "^3.3.0"
40
40
  }
41
41
  }
@@ -311,6 +311,10 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
311
311
  .failure.retry_target = "planner"' "$PROGRESS" > "${PROGRESS}.tmp" && mv "${PROGRESS}.tmp" "$PROGRESS"
312
312
  fi
313
313
 
314
+ # ── Read model & thinking mode for next agent ──
315
+ agent_model=$(jq -r ".agents[\"${next_agent}\"].model // \"opus\"" "$CONFIG" 2>/dev/null || echo "opus")
316
+ agent_thinking=$(jq -r ".agents[\"${next_agent}\"].thinking_mode // \"null\"" "$CONFIG" 2>/dev/null || echo "null")
317
+
314
318
  # Build prompt
315
319
  prompt="/harness-${next_agent} 를 실행하세요."
316
320
 
@@ -324,6 +328,21 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
324
328
 
325
329
  prompt+=$'\n'".harness/progress.json을 읽고 현재 상태를 확인하세요."
326
330
 
331
+ # Inject thinking mode instruction into prompt
332
+ if [ "$agent_thinking" != "null" ]; then
333
+ case "$agent_thinking" in
334
+ ultraplan)
335
+ prompt+=$'\n\n'"[Thinking Mode: ultraplan] 이 세션에서는 /ultraplan 모드를 사용하세요. 깊은 사고로 아키텍처와 설계를 수행합니다."
336
+ ;;
337
+ ultrathink)
338
+ prompt+=$'\n\n'"[Thinking Mode: ultrathink] 이 세션에서는 /ultrathink 모드를 사용하세요. 최대 추론 깊이로 비판적 검증을 수행합니다."
339
+ ;;
340
+ plan)
341
+ prompt+=$'\n\n'"[Thinking Mode: plan] 이 세션에서는 /plan 모드를 사용하세요. 구조화된 계획을 수립한 후 실행합니다."
342
+ ;;
343
+ esac
344
+ fi
345
+
327
346
  # Add failure context if retrying (include previous failure summary)
328
347
  failure_msg=$(jq -r '.failure.message // empty' "$PROGRESS")
329
348
  if [ -n "$failure_msg" ] && [ "$failure_msg" != "null" ]; then
@@ -406,6 +425,8 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
406
425
  --argjson regression "$regression_source" \
407
426
  --argjson eval_config "$eval_config" \
408
427
  --argjson cross_val "$cross_validation_data" \
428
+ --arg agent_model "$agent_model" \
429
+ --arg agent_thinking "$agent_thinking" \
409
430
  --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
410
431
  '{
411
432
  from: $from,
@@ -413,6 +434,8 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
413
434
  sprint: $sprint,
414
435
  retry_count: $retry,
415
436
  sprint_status: $status,
437
+ model: $agent_model,
438
+ thinking_mode: (if $agent_thinking == "null" then null else $agent_thinking end),
416
439
  failure_context: (if $failure_msg != "" then $failure_msg else null end),
417
440
  artifacts_ready: $artifacts,
418
441
  focus_features: $focus,
@@ -88,6 +88,28 @@ if [ "$retry_count" -gt 0 ]; then
88
88
  retry_str=" R${retry_count}"
89
89
  fi
90
90
 
91
+ # Model/mode for current or next agent
92
+ CONFIG="$PROJECT_ROOT/.harness/config.json"
93
+ active_agent="$current_agent"
94
+ if [ "$active_agent" = "none" ] || [ "$active_agent" = "null" ]; then
95
+ active_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
96
+ fi
97
+ model_short=""
98
+ if [ -f "$CONFIG" ] && [ "$active_agent" != "none" ] && [ "$active_agent" != "null" ]; then
99
+ am=$(jq -r ".agents[\"${active_agent}\"].model // empty" "$CONFIG" 2>/dev/null)
100
+ at=$(jq -r ".agents[\"${active_agent}\"].thinking_mode // empty" "$CONFIG" 2>/dev/null)
101
+ if [ -n "$am" ]; then
102
+ model_short="${am}"
103
+ if [ -n "$at" ] && [ "$at" != "null" ]; then
104
+ model_short+="/${at}"
105
+ fi
106
+ fi
107
+ fi
108
+
91
109
  # Build compact status line
92
- # Format: [S1] FULL | >backend | 2/5 feat | R0 | ctx 45% | $1.23
93
- echo "[S${sprint_num}] ${pl} | ${status_icon}${agent_short}${retry_str} | ${completed_features}/${total_features} feat | ctx ${context_pct}% | \$${cost}"
110
+ # Format: [S1] FULL | >backend | sonnet | 2/5 feat | ctx 45% | $1.23
111
+ if [ -n "$model_short" ]; then
112
+ echo "[S${sprint_num}] ${pl} | ${status_icon}${agent_short}${retry_str} | ${model_short} | ${completed_features}/${total_features} feat | ctx ${context_pct}% | \$${cost}"
113
+ else
114
+ echo "[S${sprint_num}] ${pl} | ${status_icon}${agent_short}${retry_str} | ${completed_features}/${total_features} feat | ctx ${context_pct}% | \$${cost}"
115
+ fi
@@ -268,9 +268,28 @@ render_progress() {
268
268
 
269
269
  # ── Next Action ──
270
270
  if [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ] && [ "$agent_status" != "blocked" ]; then
271
+ # Read model & thinking mode from config
272
+ local next_model="opus"
273
+ local next_thinking="null"
274
+ if [ -f "$CONFIG" ]; then
275
+ next_model=$(jq -r ".agents[\"${next_agent}\"].model // \"opus\"" "$CONFIG" 2>/dev/null || echo "opus")
276
+ next_thinking=$(jq -r ".agents[\"${next_agent}\"].thinking_mode // \"null\"" "$CONFIG" 2>/dev/null || echo "null")
277
+ fi
278
+
279
+ local mode_str=""
280
+ if [ "$next_thinking" != "null" ]; then
281
+ mode_str=" [/${next_thinking}]"
282
+ fi
283
+
271
284
  echo ""
272
- echo " Next → /harness-${next_agent}"
273
- echo " Auto → claude --prompt \"\$(cat .harness/next-prompt.txt)\""
285
+ echo " Next → /harness-${next_agent} (model: ${next_model}${mode_str})"
286
+
287
+ # Build auto CLI command with model flag
288
+ local model_flag=""
289
+ if [ "$next_model" != "opus" ]; then
290
+ model_flag=" --model ${next_model}"
291
+ fi
292
+ echo " Auto → claude${model_flag} --prompt \"\$(cat .harness/next-prompt.txt)\""
274
293
  fi
275
294
 
276
295
  echo ""