@walwal-harness/cli 3.3.0 → 3.3.2

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
- ├── next-prompt.txt # 다음 세션용 프롬프트 (claude CLI 파이프용)
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/next-prompt.txt` | 다음 세션용 프롬프트 (claude CLI 파이프용) |
146
+ | `.harness/handoff.json` | 세션 전환 문서 (prompt, model, thinking_mode, artifacts, regression) |
147
147
 
148
148
  ### 실행 방법
149
149
 
@@ -152,23 +152,39 @@ pending → draft → reviewed → approved
152
152
  "하네스 엔지니어링 시작" 또는 /harness-dispatcher
153
153
  ```
154
154
 
155
- #### 2. 이후 세션: harness-next.sh로 안내받기
156
- ```bash
157
- bash scripts/harness-next.sh
155
+ #### 2. 이후 세션: 새 세션만 열면 자동 진행
156
+
157
+ 에이전트가 완료 후 STOP하면, **새 세션을 시작하기만 하면 됩니다**.
158
+ SessionStart 훅이 자동으로:
159
+ 1. 이전 에이전트의 완료 상태 감지
160
+ 2. 게이트 체크 실행 (Pre-Eval Gate, 파일 소유권, 아티팩트 선행조건)
161
+ 3. `handoff.json` 생성 (prompt, model, thinking_mode, regression 등)
162
+ 4. 다음 에이전트 안내 출력
163
+
164
+ ```
165
+ # 새 세션 시작 시 자동 출력 예시:
166
+ # Harness: next → /harness-generator-backend (sonnet)
158
167
  ```
159
- Feature-level 프로그래스를 출력하고 다음 에이전트를 안내합니다.
160
168
 
161
- #### 3. 다음 세션 시작
162
- ```bash
163
- # 방법 A: 새 세션에서 스킬 직접 호출
164
- /harness-generator-backend
169
+ 사용자는 안내에 따라 스킬을 호출하면 됩니다.
170
+
171
+ #### 자동 CLI 실행 (옵션)
165
172
 
166
- # 방법 B: claude CLI 자동 실행
167
- claude --prompt "$(cat .harness/next-prompt.txt)"
173
+ 완전 자동화를 원하면 아래 명령으로 다음 에이전트를 즉시 시작할 수 있습니다:
174
+
175
+ ```bash
176
+ claude --model $(jq -r .model .harness/handoff.json) --prompt "$(jq -r .prompt .harness/handoff.json)"
168
177
  ```
169
178
 
170
- #### 4. SessionStart 훅 (자동)
171
- 세션 시작 시 `.claude/settings.json` 훅이 자동으로 현재 프로그래스를 출력합니다.
179
+ #### 디버깅 (수동)
180
+
181
+ 문제가 생겼을 때만 수동으로 상태를 확인합니다:
182
+
183
+ ```bash
184
+ bash scripts/harness-next.sh # 게이트 체크 + 프로그래스 출력
185
+ jq . .harness/handoff.json # handoff 내용 확인
186
+ jq . .harness/progress.json # 현재 상태 확인
187
+ ```
172
188
 
173
189
  ### Session Boundary Protocol
174
190
 
@@ -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 next-prompt.txt placeholder
159
- const nextPrompt = path.join(HARNESS_DIR, 'next-prompt.txt');
160
- if (!fileExists(nextPrompt) || isForce) {
161
- fs.writeFileSync(nextPrompt, '');
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.0",
3
+ "version": "3.3.2",
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"
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  # harness-next.sh — 세션 오케스트레이터
3
3
  # 현재 progress.json 상태를 읽고 다음 에이전트를 결정한다.
4
- # Feature-level 프로그래스를 출력하고 next-prompt.txt를 생성한다.
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
- NEXT_PROMPT="$PROJECT_ROOT/.harness/next-prompt.txt"
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 next-prompt.txt
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/progress.json을 읽고 현재 상태를 확인하세요."
328
+ prompt+=$'\n'".harness/handoff.json을 읽고 컨텍스트를 확인하세요."
330
329
 
331
- # Inject thinking mode instruction into prompt
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] 이 세션에서는 /ultraplan 모드를 사용하세요. 깊은 사고로 아키텍처와 설계를 수행합니다."
334
+ prompt+=$'\n\n'"[Thinking Mode: ultraplan] /${agent_thinking} 모드를 사용하세요. 깊은 사고로 아키텍처와 설계를 수행합니다."
336
335
  ;;
337
336
  ultrathink)
338
- prompt+=$'\n\n'"[Thinking Mode: ultrathink] 이 세션에서는 /ultrathink 모드를 사용하세요. 최대 추론 깊이로 비판적 검증을 수행합니다."
337
+ prompt+=$'\n\n'"[Thinking Mode: ultrathink] /${agent_thinking} 모드를 사용하세요. 최대 추론 깊이로 비판적 검증을 수행합니다."
339
338
  ;;
340
339
  plan)
341
- prompt+=$'\n\n'"[Thinking Mode: plan] 이 세션에서는 /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 (include previous failure summary)
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
- echo "$prompt" > "$NEXT_PROMPT"
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: collect previous sprint's passed AC from archive ──
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 scoring config ──
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: evaluator-visual이면 functional 결과 파싱 ──
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
- cat > "$NEXT_PROMPT" <<'PROMPT'
451
- Sprint 문서를 아카이브 하세요.
452
- .harness/actions/의 스프린트 문서를 .harness/archive/sprint-NNN/으로 이동합니다.
453
- .harness/progress.json을 읽고 sprint 번호를 확인하세요.
454
- PROMPT
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 "" > "$NEXT_PROMPT"
461
+ echo '{}' > "$HANDOFF"
458
462
  fi
@@ -1,6 +1,11 @@
1
1
  #!/bin/bash
2
- # harness-session-start.sh — SessionStart 훅 (compact)
3
- # statusline이 상시 상태를 표시하므로, 여기서는 핵심 안내만 출력.
2
+ # harness-session-start.sh — SessionStart 훅
3
+ # 새 세션 시작 시 자동으로:
4
+ # 1) 이전 에이전트가 completed이면 harness-next.sh를 실행하여 게이트 체크 + handoff 생성
5
+ # 2) handoff.json이 있으면 다음 에이전트 안내
6
+ # 3) statusline이 상시 상태를 표시하므로 여기서는 핵심 안내만 출력
7
+ #
8
+ # 사용자가 수동으로 harness-next.sh를 실행할 필요가 없도록 자동화.
4
9
 
5
10
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
11
  LIB="$SCRIPT_DIR/lib/harness-render-progress.sh"
@@ -11,6 +16,8 @@ command -v jq &>/dev/null || exit 0
11
16
 
12
17
  PROJECT_ROOT="$(resolve_harness_root "." 2>/dev/null)" || exit 0
13
18
  PROGRESS="$PROJECT_ROOT/.harness/progress.json"
19
+ CONFIG="$PROJECT_ROOT/.harness/config.json"
20
+ HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
14
21
  [ -f "$PROGRESS" ] || exit 0
15
22
 
16
23
  sprint_status=$(jq -r '.sprint.status // "init"' "$PROGRESS" 2>/dev/null)
@@ -18,17 +25,45 @@ current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
18
25
  next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
19
26
  agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
20
27
 
21
- # init 상태: 간단 안내
28
+ # ─────────────────────────────────────────
29
+ # init 상태: 첫 안내
30
+ # ─────────────────────────────────────────
22
31
  if [ "$sprint_status" = "init" ]; then
23
32
  echo "# Harness ready — say \"하네스 엔지니어링 시작\" or /harness-dispatcher"
24
33
  exit 0
25
34
  fi
26
35
 
27
- # 활성 세션: 다음 액션만 안내 (상세 프로그래스는 statusline에서 상시 표시)
36
+ # ─────────────────────────────────────────
37
+ # 이전 에이전트가 completed → 자동으로 harness-next 실행
38
+ # (게이트 체크, 아티팩트 검증, handoff.json 생성)
39
+ # ─────────────────────────────────────────
40
+ if [ "$agent_status" = "completed" ] || [ "$agent_status" = "failed" ]; then
41
+ # harness-next.sh를 백그라운드로 실행하면 안 됨 — 결과가 필요
42
+ bash "$SCRIPT_DIR/harness-next.sh" "$PROJECT_ROOT" 2>/dev/null
43
+
44
+ # harness-next.sh가 progress.json과 handoff.json을 업데이트했으므로 다시 읽기
45
+ next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
46
+ agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
47
+ fi
48
+
49
+ # ─────────────────────────────────────────
50
+ # 상태별 안내 출력
51
+ # ─────────────────────────────────────────
28
52
  if [ "$agent_status" = "blocked" ]; then
29
- echo "# Harness BLOCKED — user intervention required. Run: bash scripts/harness-next.sh"
30
- elif [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ]; then
31
- echo "# Harness: next → /harness-${next_agent}"
53
+ echo "# Harness BLOCKED — retry limit reached, user intervention required"
54
+
55
+ elif [ -f "$HANDOFF" ] && [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ]; then
56
+ # handoff.json에서 모델/모드 정보 읽기
57
+ handoff_model=$(jq -r '.model // "opus"' "$HANDOFF" 2>/dev/null)
58
+ handoff_thinking=$(jq -r '.thinking_mode // empty' "$HANDOFF" 2>/dev/null)
59
+
60
+ mode_str=""
61
+ if [ -n "$handoff_thinking" ] && [ "$handoff_thinking" != "null" ]; then
62
+ mode_str=" /${handoff_thinking}"
63
+ fi
64
+
65
+ echo "# Harness: next → /harness-${next_agent} (${handoff_model}${mode_str})"
66
+
32
67
  elif [ "$current_agent" != "none" ] && [ "$current_agent" != "null" ]; then
33
68
  echo "# Harness: ${current_agent} [${agent_status}]"
34
69
  fi
@@ -282,14 +282,8 @@ render_progress() {
282
282
  fi
283
283
 
284
284
  echo ""
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)\""
285
+ echo " Next → /harness-${next_agent} (${next_model}${mode_str})"
286
+ echo " Tip → 새 세션을 시작하면 자동으로 안내됩니다"
293
287
  fi
294
288
 
295
289
  echo ""