@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.
|
|
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.
|
|
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
|
|
518
|
-
5.
|
|
519
|
-
6.
|
|
520
|
-
7.
|
|
521
|
-
8. Checks
|
|
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.
|
|
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.
|
|
39
|
+
"@walwal-harness/cli": "^3.3.0"
|
|
40
40
|
}
|
|
41
41
|
}
|
package/scripts/harness-next.sh
CHANGED
|
@@ -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 |
|
|
93
|
-
|
|
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
|
-
|
|
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 ""
|