@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
- ├── 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
 
@@ -164,7 +164,7 @@ Feature-level 프로그래스를 출력하고 다음 에이전트를 안내합
164
164
  /harness-generator-backend
165
165
 
166
166
  # 방법 B: claude CLI 자동 실행
167
- claude --prompt "$(cat .harness/next-prompt.txt)"
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 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.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"
@@ -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
@@ -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 \"\$(cat .harness/next-prompt.txt)\""
292
+ echo " Auto → claude${model_flag} --prompt \"\$(jq -r .prompt .harness/handoff.json)\""
293
293
  fi
294
294
 
295
295
  echo ""