@walwal-harness/cli 3.3.0 → 3.3.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.
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
├── config.json # 하네스 설정
|
|
13
13
|
├── progress.json # 기계 판독 상태 (세션 오케스트레이션)
|
|
14
14
|
├── progress.log # 사람 판독 히스토리 (append-only)
|
|
15
|
-
├──
|
|
15
|
+
├── handoff.json # 세션 전환 문서 (prompt, model, artifacts, regression 등)
|
|
16
16
|
├── actions/ # 현재 활성 문서
|
|
17
17
|
│ ├── pipeline.json # Dispatcher 결정 (어떤 파이프라인인지)
|
|
18
18
|
│ ├── plan.md # 제품 사양
|
|
@@ -143,7 +143,7 @@ pending → draft → reviewed → approved
|
|
|
143
143
|
|------|------|
|
|
144
144
|
| `.harness/progress.json` | 기계 판독 상태 (현재 에이전트, 파이프라인, 실패 정보) |
|
|
145
145
|
| `.harness/progress.log` | 사람 판독 히스토리 (append-only) |
|
|
146
|
-
| `.harness/
|
|
146
|
+
| `.harness/handoff.json` | 세션 전환 문서 (prompt, model, thinking_mode, artifacts, regression) |
|
|
147
147
|
|
|
148
148
|
### 실행 방법
|
|
149
149
|
|
|
@@ -164,7 +164,7 @@ Feature-level 프로그래스를 출력하고 다음 에이전트를 안내합
|
|
|
164
164
|
/harness-generator-backend
|
|
165
165
|
|
|
166
166
|
# 방법 B: claude CLI 자동 실행
|
|
167
|
-
claude --
|
|
167
|
+
claude --model $(jq -r .model .harness/handoff.json) --prompt "$(jq -r .prompt .harness/handoff.json)"
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
#### 4. SessionStart 훅 (자동)
|
|
@@ -312,7 +312,6 @@
|
|
|
312
312
|
"isolation": true,
|
|
313
313
|
"state_file": ".harness/progress.json",
|
|
314
314
|
"state_log": ".harness/progress.log",
|
|
315
|
-
"next_prompt_file": ".harness/next-prompt.txt",
|
|
316
315
|
"handoff_file": ".harness/handoff.json",
|
|
317
316
|
"context_guard": {
|
|
318
317
|
"comment": "컨텍스트 분리 하드 가드레일. UserPromptSubmit 훅이 위반을 감지하면 경고를 주입한다.",
|
package/bin/init.js
CHANGED
|
@@ -155,10 +155,10 @@ function scaffoldHarness() {
|
|
|
155
155
|
fs.writeFileSync(progressLog, `# Harness Progress Log\n# ${date} — Initialized\n`);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// Create
|
|
159
|
-
const
|
|
160
|
-
if (!fileExists(
|
|
161
|
-
fs.writeFileSync(
|
|
158
|
+
// Create handoff.json placeholder
|
|
159
|
+
const handoff = path.join(HARNESS_DIR, 'handoff.json');
|
|
160
|
+
if (!fileExists(handoff) || isForce) {
|
|
161
|
+
fs.writeFileSync(handoff, '{}');
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
log('.harness/ scaffolding complete');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.1",
|
|
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"
|
package/scripts/harness-next.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# harness-next.sh — 세션 오케스트레이터
|
|
3
3
|
# 현재 progress.json 상태를 읽고 다음 에이전트를 결정한다.
|
|
4
|
-
# Feature-level 프로그래스를 출력하고
|
|
4
|
+
# Feature-level 프로그래스를 출력하고 handoff.json을 생성한다.
|
|
5
5
|
set -e
|
|
6
6
|
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
@@ -20,7 +20,7 @@ PROJECT_ROOT="$(resolve_harness_root "${1:-.}")" || {
|
|
|
20
20
|
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
21
21
|
CONFIG="$PROJECT_ROOT/.harness/config.json"
|
|
22
22
|
PIPELINE_JSON="$PROJECT_ROOT/.harness/actions/pipeline.json"
|
|
23
|
-
|
|
23
|
+
HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
|
|
24
24
|
|
|
25
25
|
check_jq || exit 1
|
|
26
26
|
|
|
@@ -296,7 +296,7 @@ render_agent_bar "$PROJECT_ROOT"
|
|
|
296
296
|
echo ""
|
|
297
297
|
|
|
298
298
|
# ─────────────────────────────────────────
|
|
299
|
-
# Generate
|
|
299
|
+
# Generate handoff.json (unified session transition document)
|
|
300
300
|
# ─────────────────────────────────────────
|
|
301
301
|
if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_status" != "blocked" ]; then
|
|
302
302
|
# ── Escalation check: 3회 실패 시 Planner에게 scope 축소 요청 ──
|
|
@@ -304,7 +304,6 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
304
304
|
if [ "$retry_count" -ge "$escalate_after" ] && [ "$sprint_status" = "failed" ] && [ "$next_agent" != "planner" ]; then
|
|
305
305
|
echo " ⚠ Escalation: ${retry_count}회 실패 — Planner에게 scope 축소/접근 변경 요청"
|
|
306
306
|
next_agent="planner"
|
|
307
|
-
# progress.json에 에스컬레이션 기록
|
|
308
307
|
jq --arg msg "Escalated after ${retry_count} failures. Planner must review scope or approach." \
|
|
309
308
|
'.next_agent = "planner" |
|
|
310
309
|
.failure.message = $msg |
|
|
@@ -315,7 +314,7 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
315
314
|
agent_model=$(jq -r ".agents[\"${next_agent}\"].model // \"opus\"" "$CONFIG" 2>/dev/null || echo "opus")
|
|
316
315
|
agent_thinking=$(jq -r ".agents[\"${next_agent}\"].thinking_mode // \"null\"" "$CONFIG" 2>/dev/null || echo "null")
|
|
317
316
|
|
|
318
|
-
# Build prompt
|
|
317
|
+
# ── Build prompt text (embedded in handoff.json) ──
|
|
319
318
|
prompt="/harness-${next_agent} 를 실행하세요."
|
|
320
319
|
|
|
321
320
|
if [ "$sprint_num" -gt 0 ]; then
|
|
@@ -326,37 +325,33 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
326
325
|
prompt+="을 진행합니다."
|
|
327
326
|
fi
|
|
328
327
|
|
|
329
|
-
prompt+=$'\n'".harness/
|
|
328
|
+
prompt+=$'\n'".harness/handoff.json을 읽고 컨텍스트를 확인하세요."
|
|
330
329
|
|
|
331
|
-
# Inject thinking mode instruction
|
|
330
|
+
# Inject thinking mode instruction
|
|
332
331
|
if [ "$agent_thinking" != "null" ]; then
|
|
333
332
|
case "$agent_thinking" in
|
|
334
333
|
ultraplan)
|
|
335
|
-
prompt+=$'\n\n'"[Thinking Mode: ultraplan]
|
|
334
|
+
prompt+=$'\n\n'"[Thinking Mode: ultraplan] /${agent_thinking} 모드를 사용하세요. 깊은 사고로 아키텍처와 설계를 수행합니다."
|
|
336
335
|
;;
|
|
337
336
|
ultrathink)
|
|
338
|
-
prompt+=$'\n\n'"[Thinking Mode: ultrathink]
|
|
337
|
+
prompt+=$'\n\n'"[Thinking Mode: ultrathink] /${agent_thinking} 모드를 사용하세요. 최대 추론 깊이로 비판적 검증을 수행합니다."
|
|
339
338
|
;;
|
|
340
339
|
plan)
|
|
341
|
-
prompt+=$'\n\n'"[Thinking Mode: plan]
|
|
340
|
+
prompt+=$'\n\n'"[Thinking Mode: plan] /${agent_thinking} 모드를 사용하세요. 구조화된 계획을 수립한 후 실행합니다."
|
|
342
341
|
;;
|
|
343
342
|
esac
|
|
344
343
|
fi
|
|
345
344
|
|
|
346
|
-
# Add failure context if retrying
|
|
345
|
+
# Add failure context if retrying
|
|
347
346
|
failure_msg=$(jq -r '.failure.message // empty' "$PROGRESS")
|
|
348
347
|
if [ -n "$failure_msg" ] && [ "$failure_msg" != "null" ]; then
|
|
349
348
|
prompt+=$'\n\n'"이전 실패 사유: ${failure_msg}"
|
|
350
349
|
prompt+=$'\n'"같은 접근을 반복하지 말고, 실패 원인을 분석한 후 다른 전략으로 시도하세요."
|
|
351
350
|
fi
|
|
352
351
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
# ── Generate structured handoff.json ──
|
|
356
|
-
HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
|
|
352
|
+
# ── Collect artifacts ──
|
|
357
353
|
FEATURE_LIST="$PROJECT_ROOT/.harness/actions/feature-list.json"
|
|
358
354
|
|
|
359
|
-
# Collect available artifacts
|
|
360
355
|
local -a artifacts_ready=()
|
|
361
356
|
for f in plan.md feature-list.json api-contract.json sprint-contract.md evaluation-functional.md evaluation-visual.md; do
|
|
362
357
|
if [ -f "$PROJECT_ROOT/.harness/actions/$f" ]; then
|
|
@@ -372,7 +367,7 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
372
367
|
focus_features=$(jq '[.features[]? | select(.passes == null or (.passes | length) == 0 or ((.passes // []) | map(select(. == "evaluator-functional")) | length == 0)) | .id] | .[0:5]' "$FEATURE_LIST" 2>/dev/null || echo "[]")
|
|
373
368
|
fi
|
|
374
369
|
|
|
375
|
-
# ── Regression data
|
|
370
|
+
# ── Regression data ──
|
|
376
371
|
local regression_source="null"
|
|
377
372
|
local prev_sprint=$((sprint_num - 1))
|
|
378
373
|
local prev_archive="$PROJECT_ROOT/.harness/archive/sprint-$(printf '%03d' $prev_sprint)"
|
|
@@ -386,7 +381,7 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
386
381
|
fi
|
|
387
382
|
fi
|
|
388
383
|
|
|
389
|
-
# ── Eval-specific
|
|
384
|
+
# ── Eval-specific config ──
|
|
390
385
|
local eval_config="null"
|
|
391
386
|
local cross_validation_data="null"
|
|
392
387
|
case "$next_agent" in
|
|
@@ -403,34 +398,35 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
403
398
|
;;
|
|
404
399
|
esac
|
|
405
400
|
|
|
406
|
-
# ── Cross-Validation
|
|
401
|
+
# ── Cross-Validation ──
|
|
407
402
|
if [ "$next_agent" = "evaluator-visual" ]; then
|
|
408
403
|
local func_eval="$PROJECT_ROOT/.harness/actions/evaluation-functional.md"
|
|
409
404
|
if [ -f "$func_eval" ]; then
|
|
410
|
-
# evaluation-functional.md 내 JSON 코드블록에서 Cross-Validation Data 추출
|
|
411
405
|
cross_validation_data=$(sed -n '/```json/,/```/p' "$func_eval" | tail -n +2 | head -n -1 | jq 'select(.evaluator == "functional")' 2>/dev/null || echo "null")
|
|
412
406
|
fi
|
|
413
407
|
fi
|
|
414
408
|
|
|
415
|
-
# Build handoff.json
|
|
409
|
+
# ── Build handoff.json (single source of truth for session transition) ──
|
|
416
410
|
jq -n \
|
|
417
411
|
--arg from "${current_agent:-dispatcher}" \
|
|
418
412
|
--arg to "$next_agent" \
|
|
413
|
+
--arg prompt "$prompt" \
|
|
419
414
|
--argjson sprint "$sprint_num" \
|
|
420
415
|
--argjson retry "$retry_count" \
|
|
421
416
|
--arg status "$sprint_status" \
|
|
417
|
+
--arg agent_model "$agent_model" \
|
|
418
|
+
--arg agent_thinking "$agent_thinking" \
|
|
422
419
|
--arg failure_msg "${failure_msg:-}" \
|
|
423
420
|
--argjson artifacts "$artifacts_json" \
|
|
424
421
|
--argjson focus "$focus_features" \
|
|
425
422
|
--argjson regression "$regression_source" \
|
|
426
423
|
--argjson eval_config "$eval_config" \
|
|
427
424
|
--argjson cross_val "$cross_validation_data" \
|
|
428
|
-
--arg agent_model "$agent_model" \
|
|
429
|
-
--arg agent_thinking "$agent_thinking" \
|
|
430
425
|
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
431
426
|
'{
|
|
432
427
|
from: $from,
|
|
433
428
|
to: $to,
|
|
429
|
+
prompt: $prompt,
|
|
434
430
|
sprint: $sprint,
|
|
435
431
|
retry_count: $retry,
|
|
436
432
|
sprint_status: $status,
|
|
@@ -447,12 +443,20 @@ if [ "$next_agent" != "null" ] && [ "$next_agent" != "archive" ] && [ "$agent_st
|
|
|
447
443
|
}' > "$HANDOFF"
|
|
448
444
|
|
|
449
445
|
elif [ "$next_agent" = "archive" ]; then
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
446
|
+
jq -n \
|
|
447
|
+
--arg from "${current_agent:-evaluator}" \
|
|
448
|
+
--argjson sprint "$sprint_num" \
|
|
449
|
+
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
450
|
+
'{
|
|
451
|
+
from: $from,
|
|
452
|
+
to: "archive",
|
|
453
|
+
prompt: "Sprint 문서를 아카이브하세요.\n.harness/actions/의 스프린트 문서를 .harness/archive/sprint-NNN/으로 이동합니다.\n.harness/handoff.json을 읽고 sprint 번호를 확인하세요.",
|
|
454
|
+
sprint: $sprint,
|
|
455
|
+
model: "opus",
|
|
456
|
+
thinking_mode: null,
|
|
457
|
+
timestamp: $timestamp
|
|
458
|
+
}' > "$HANDOFF"
|
|
455
459
|
|
|
456
460
|
else
|
|
457
|
-
echo
|
|
461
|
+
echo '{}' > "$HANDOFF"
|
|
458
462
|
fi
|
|
@@ -284,12 +284,12 @@ render_progress() {
|
|
|
284
284
|
echo ""
|
|
285
285
|
echo " Next → /harness-${next_agent} (model: ${next_model}${mode_str})"
|
|
286
286
|
|
|
287
|
-
# Build auto CLI command with model flag
|
|
287
|
+
# Build auto CLI command with model flag, reading prompt from handoff.json
|
|
288
288
|
local model_flag=""
|
|
289
289
|
if [ "$next_model" != "opus" ]; then
|
|
290
290
|
model_flag=" --model ${next_model}"
|
|
291
291
|
fi
|
|
292
|
-
echo " Auto → claude${model_flag} --prompt \"\$(
|
|
292
|
+
echo " Auto → claude${model_flag} --prompt \"\$(jq -r .prompt .harness/handoff.json)\""
|
|
293
293
|
fi
|
|
294
294
|
|
|
295
295
|
echo ""
|