@walwal-harness/cli 6.0.4 → 6.0.5

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,105 +0,0 @@
1
- #!/bin/bash
2
- # harness-user-prompt-submit.sh — UserPromptSubmit hook (v5 unified)
3
- # 핵심 상태 + 라우팅 지시만 주입. Solo/Team 통합.
4
- set -e
5
-
6
- INPUT=$(cat)
7
- CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)
8
- if [ -z "$CWD" ]; then CWD="$PWD"; fi
9
-
10
- # 조건 1: 하네스 초기화 확인
11
- if [ ! -f "$CWD/.harness/config.json" ]; then exit 0; fi
12
-
13
- # 조건 2: opt-out 플래그 확인
14
- AUTO_ROUTE="true"
15
- if command -v jq >/dev/null 2>&1; then
16
- AUTO_ROUTE=$(jq -r '.behavior.auto_route_dispatcher // true' "$CWD/.harness/config.json" 2>/dev/null || echo "true")
17
- fi
18
- if [ "$AUTO_ROUTE" != "true" ]; then exit 0; fi
19
-
20
- # 사용자 skip 감지
21
- PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty' 2>/dev/null || true)
22
- if echo "$PROMPT" | grep -qiE "harness\s*(skip|off|bypass|없이)|without\s*harness|just\s*(answer|chat|reply)"; then
23
- exit 0
24
- fi
25
-
26
- # 현재 세션 상태 읽기
27
- PIPELINE="none"; CURRENT_AGENT="none"; NEXT_AGENT="none"
28
- SPRINT_NUM="0"; SPRINT_STATUS="init"; AGENT_STATUS="pending"
29
-
30
- if [ -f "$CWD/.harness/progress.json" ] && command -v jq >/dev/null 2>&1; then
31
- PIPELINE=$(jq -r '.pipeline // "none"' "$CWD/.harness/progress.json" 2>/dev/null || echo "none")
32
- CURRENT_AGENT=$(jq -r '.current_agent // "none"' "$CWD/.harness/progress.json" 2>/dev/null || echo "none")
33
- NEXT_AGENT=$(jq -r '.next_agent // "none"' "$CWD/.harness/progress.json" 2>/dev/null || echo "none")
34
- SPRINT_NUM=$(jq -r '.sprint.number // 0' "$CWD/.harness/progress.json" 2>/dev/null || echo "0")
35
- SPRINT_STATUS=$(jq -r '.sprint.status // "init"' "$CWD/.harness/progress.json" 2>/dev/null || echo "init")
36
- AGENT_STATUS=$(jq -r '.agent_status // "pending"' "$CWD/.harness/progress.json" 2>/dev/null || echo "pending")
37
- fi
38
-
39
- # ── 명령 히스토리 기록 (모든 모드 공통) ──
40
- PROGRESS_LOG="$CWD/.harness/progress.log"
41
- if [ -n "$PROMPT" ] && [ -d "$CWD/.harness" ]; then
42
- # progress.log가 없으면 생성
43
- if [ ! -f "$PROGRESS_LOG" ]; then
44
- echo "# Harness Command History — $(date +%Y-%m-%d)" > "$PROGRESS_LOG"
45
- fi
46
- PROMPT_SHORT=$(echo "$PROMPT" | tr '\n' ' ' | sed 's/ */ /g' | cut -c1-80)
47
- if [ ${#PROMPT_SHORT} -gt 2 ]; then
48
- echo "$(date +"%Y-%m-%d %H:%M") | user-prompt | input | ${PROMPT_SHORT}" >> "$PROGRESS_LOG"
49
- fi
50
- fi
51
-
52
- # ── 컨텍스트 분리 가드레일 ──
53
- # 현재 에이전트가 활성인데 다른 에이전트 스킬을 호출하려는 경우 경고
54
- CONTEXT_WARNING=""
55
- if [ "$CURRENT_AGENT" != "none" ] && [ "$CURRENT_AGENT" != "null" ] && [ "$AGENT_STATUS" = "running" ]; then
56
- # 프롬프트에서 /harness-* 패턴 추출
57
- REQUESTED_SKILL=$(echo "$PROMPT" | grep -oE '/harness-[a-z-]+' | head -1 | sed 's|/harness-||')
58
- if [ -n "$REQUESTED_SKILL" ] && [ "$REQUESTED_SKILL" != "$CURRENT_AGENT" ]; then
59
- CONTEXT_WARNING="
60
- ## !! Context Isolation Warning !!
61
- current_agent=${CURRENT_AGENT} (running) 인데 /harness-${REQUESTED_SKILL} 호출 감지.
62
- 한 세션에서 다른 에이전트를 실행하면 컨텍스트가 오염됩니다.
63
- 현재 에이전트를 먼저 완료(completed)하거나, 새 세션을 시작하세요."
64
- fi
65
- fi
66
-
67
- # ── Mode 기반 분기 ──
68
- MODE="solo"
69
- if [ -f "$CWD/.harness/progress.json" ] && command -v jq >/dev/null 2>&1; then
70
- MODE=$(jq -r '.mode // "solo"' "$CWD/.harness/progress.json" 2>/dev/null || echo "solo")
71
- fi
72
-
73
- if [ "$MODE" = "team" ]; then
74
- FEATURE_QUEUE="$CWD/.harness/actions/feature-queue.json"
75
- T_PASSED=0; T_TOTAL=0; T_FAILED=0
76
- if [ -f "$FEATURE_QUEUE" ]; then
77
- T_PASSED=$(jq '.queue.passed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
78
- T_TOTAL=$(jq '[.queue.ready, (.queue.blocked | keys), (.queue.in_progress | keys), .queue.passed, .queue.failed] | flatten | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
79
- T_FAILED=$(jq '.queue.failed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
80
- fi
81
-
82
- cat <<EOF
83
- [harness] team | ${T_PASSED}/${T_TOTAL} passed | ${T_FAILED} failed
84
-
85
- ## Team Mode Active
86
- - 3 Agent Teams이 자율적으로 Gen→Eval 루프 실행 중 (max 5 retries)
87
- - 역할: 모니터링, 실패 대응, 수동 개입
88
- - /harness-stop → 중단 | /harness-solo → Solo 전환
89
- - skip: "harness skip" 시 일반 대화
90
- EOF
91
- exit 0
92
- fi
93
-
94
- # ── Compact context 주입 (solo/paused mode) ──
95
- cat <<EOF
96
- [harness] S${SPRINT_NUM} | ${PIPELINE} | agent=${CURRENT_AGENT} (${AGENT_STATUS}) | next=${NEXT_AGENT}
97
- ${CONTEXT_WARNING}
98
- ## Route
99
- - pipeline=none/dispatcher 미실행 → harness-dispatcher 스킬 호출
100
- - 기능 요청 → pipeline flow | 실수 지적 → gotcha flow | 메타 질문 → 짧게 응답 (skip)
101
- - 활성 pipeline → next_agent/current_agent 컨텍스트로 계속
102
- - skip: "harness skip", "just answer" 등 명시 시 단일 메시지 건너뜀
103
- EOF
104
-
105
- exit 0
@@ -1,337 +0,0 @@
1
- #!/bin/bash
2
- # init-agents-md.sh — scan-result.json을 기반으로 AGENTS.md를 생성/리빌드
3
- # 기존 CLAUDE.md의 사용자 규칙을 보존하면서 하네스 포맷으로 변환
4
- set -e
5
-
6
- PROJECT_ROOT="${1:-.}"
7
- SCAN_RESULT="${PROJECT_ROOT}/.harness/actions/scan-result.json"
8
- export SCAN_RESULT PROJECT_ROOT
9
- AGENTS_FILE="${PROJECT_ROOT}/AGENTS.md"
10
- CLAUDE_FILE="${PROJECT_ROOT}/CLAUDE.md"
11
- BACKUP_DIR="${PROJECT_ROOT}/.harness/archive/pre-harness-backup"
12
-
13
- if [ ! -f "$SCAN_RESULT" ]; then
14
- echo "ERROR: scan-result.json not found. Run scan-project.sh first."
15
- exit 1
16
- fi
17
-
18
- # ─────────────────────────────────────────
19
- # 1. Python으로 scan-result.json 파싱
20
- # ─────────────────────────────────────────
21
- eval "$(python3 << 'PYEOF'
22
- import json, sys, os
23
-
24
- with open(os.environ.get("SCAN_RESULT", ".harness/actions/scan-result.json")) as f:
25
- scan = json.load(f)
26
-
27
- docs = scan["existing_docs"]
28
- tech = scan["tech_stack"]
29
- rec = scan["recommendation"]
30
- dirs = scan["structure"]["directories"]
31
- configs = scan["structure"]["config_files"]
32
-
33
- print(f'ACTION="{rec["agents_md_action"]}"')
34
- print(f'PROJECT_TYPE="{rec["project_type"]}"')
35
- print(f'TECH_BE="{tech["backend"]}"')
36
- print(f'TECH_FE="{tech["frontend"]}"')
37
- print(f'TECH_DB="{tech["database"]}"')
38
- print(f'TECH_MONO="{tech["monorepo"]}"')
39
- print(f'OPENAPI="{scan["api"]["openapi_spec"]}"')
40
- print(f'HAS_EXISTING_CLAUDE="{docs["claude_md"]}"')
41
- print(f'EXISTING_CONTENT={json.dumps(docs["existing_claude_md_content"])}')
42
- PYEOF
43
- )"
44
-
45
- echo "=== AGENTS.md Initialization ==="
46
- echo "Action: ${ACTION}"
47
- echo "Project Type: ${PROJECT_TYPE}"
48
- echo ""
49
-
50
- # ─────────────────────────────────────────
51
- # 2. 기존 문서 백업
52
- # ─────────────────────────────────────────
53
- if [ "$ACTION" = "migrate-claude-to-agents" ] || [ "$ACTION" = "rebuild-with-harness" ]; then
54
- mkdir -p "$BACKUP_DIR"
55
- TIMESTAMP=$(date +%Y%m%d_%H%M%S)
56
-
57
- if [ -f "$AGENTS_FILE" ] && [ ! -L "$AGENTS_FILE" ]; then
58
- cp "$AGENTS_FILE" "${BACKUP_DIR}/AGENTS.md.${TIMESTAMP}.bak"
59
- echo "Backed up: AGENTS.md → ${BACKUP_DIR}/AGENTS.md.${TIMESTAMP}.bak"
60
- fi
61
-
62
- if [ -f "$CLAUDE_FILE" ] && [ ! -L "$CLAUDE_FILE" ]; then
63
- cp "$CLAUDE_FILE" "${BACKUP_DIR}/CLAUDE.md.${TIMESTAMP}.bak"
64
- echo "Backed up: CLAUDE.md → ${BACKUP_DIR}/CLAUDE.md.${TIMESTAMP}.bak"
65
- fi
66
- fi
67
-
68
- # ─────────────────────────────────────────
69
- # 3. IA-MAP 자동 생성
70
- # ─────────────────────────────────────────
71
- generate_ia_map() {
72
- python3 << 'PYEOF'
73
- import json, os
74
-
75
- with open(os.environ.get("SCAN_RESULT", ".harness/actions/scan-result.json")) as f:
76
- scan = json.load(f)
77
-
78
- dirs = scan["structure"]["directories"]
79
- tech = scan["tech_stack"]
80
-
81
- # 경로별 태그/설명 추론 규칙
82
- tag_rules = {
83
- # Backend patterns
84
- "apps/gateway": ("[BE]", "API Gateway", "Generator-Backend"),
85
- "apps/service": ("[BE]", "Microservice", "Generator-Backend"),
86
- "src/controllers": ("[BE]", "API Controllers", "Generator-Backend"),
87
- "src/services": ("[BE]", "Business Logic", "Generator-Backend"),
88
- "src/models": ("[BE]", "Data Models", "Generator-Backend"),
89
- "src/entities": ("[BE]", "DB Entities", "Generator-Backend"),
90
- "src/modules": ("[BE]", "NestJS Modules", "Generator-Backend"),
91
- "src/guards": ("[BE]", "Auth Guards", "Generator-Backend"),
92
- "src/pipes": ("[BE]", "Validation Pipes", "Generator-Backend"),
93
- "src/interceptors": ("[BE]", "Interceptors", "Generator-Backend"),
94
- "src/filters": ("[BE]", "Exception Filters", "Generator-Backend"),
95
- "routers": ("[BE]", "API Routers", "Generator-Backend"),
96
- "routes": ("[BE]", "API Routes", "Generator-Backend"),
97
- "middleware": ("[BE]", "Middleware", "Generator-Backend"),
98
- "migrations": ("[BE]", "DB Migrations", "Generator-Backend"),
99
-
100
- # Frontend patterns
101
- "apps/web": ("[FE]", "Frontend App", "Generator-Frontend"),
102
- "src/components": ("[FE]", "UI Components", "Generator-Frontend"),
103
- "src/pages": ("[FE]", "Page Routes", "Generator-Frontend"),
104
- "src/app": ("[FE]", "App Router", "Generator-Frontend"),
105
- "src/hooks": ("[FE]", "Custom Hooks", "Generator-Frontend"),
106
- "src/stores": ("[FE]", "State Management", "Generator-Frontend"),
107
- "src/styles": ("[FE]", "Stylesheets", "Generator-Frontend"),
108
- "src/api": ("[FE]", "API Client Layer", "Generator-Frontend"),
109
- "src/lib": ("[FE]", "Utility Library", "Generator-Frontend"),
110
- "public": ("[FE]", "Static Assets", "Generator-Frontend"),
111
-
112
- # Shared / Libs
113
- "libs/shared": ("[BE]", "Shared Libraries", "Generator-Backend"),
114
- "libs/database": ("[BE]", "Database Module", "Generator-Backend"),
115
- "libs/common": ("[BE]", "Common Utilities", "Generator-Backend"),
116
-
117
- # Infra
118
- "docker": ("[INFRA]", "Docker Configuration", "Planner"),
119
- ".github": ("[INFRA]", "CI/CD Workflows", "Planner"),
120
- "deploy": ("[INFRA]", "Deployment Scripts", "Planner"),
121
- "terraform": ("[INFRA]", "Infrastructure as Code", "Planner"),
122
- "k8s": ("[INFRA]", "Kubernetes Manifests", "Planner"),
123
-
124
- # Harness
125
- ".harness": ("[HARNESS]", "Harness System", "Planner"),
126
-
127
- # Tests
128
- "test": ("[TEST]", "Test Suite", "Evaluator"),
129
- "tests": ("[TEST]", "Test Suite", "Evaluator"),
130
- "e2e": ("[TEST]", "E2E Tests", "Evaluator"),
131
- "__tests__": ("[TEST]", "Unit Tests", "Evaluator"),
132
-
133
- # Native app patterns (Swift / Kotlin / Rust 등)
134
- "Sources": ("[FE]", "Native Source Root", "Generator-Frontend"),
135
- "Tests": ("[TEST]", "Native Test Suite", "Evaluator"),
136
- "Resources": ("[FE]", "Native Resources", "Generator-Frontend"),
137
- "android": ("[FE]", "Android Host", "Generator-Frontend"),
138
- "ios": ("[FE]", "iOS Host", "Generator-Frontend"),
139
- "macos": ("[FE]", "macOS Host", "Generator-Frontend"),
140
- "Shared": ("[FE]", "Cross-platform Shared", "Generator-Frontend"),
141
- }
142
-
143
- # 디렉토리를 분석
144
- ia_lines = []
145
- for d in sorted(dirs):
146
- if d == "." or not d:
147
- continue
148
-
149
- matched = False
150
- for pattern, (tag, desc, owner) in tag_rules.items():
151
- if pattern in d:
152
- depth = d.count("/")
153
- indent = "│ " * depth
154
- name = d.split("/")[-1] + "/"
155
- ia_lines.append(f"{indent}├── {name:<25} # {tag} {desc:<30} → {owner}")
156
- matched = True
157
- break
158
-
159
- if not matched:
160
- depth = d.count("/")
161
- if depth <= 1: # 1depth만 미분류 표시
162
- indent = "│ " * depth
163
- name = d.split("/")[-1] + "/"
164
- ia_lines.append(f"{indent}├── {name:<25} # [?] (Planner가 분류 필요) → TBD")
165
-
166
- for line in ia_lines:
167
- print(line)
168
- PYEOF
169
- }
170
-
171
- IA_MAP=$(generate_ia_map)
172
-
173
- # ─────────────────────────────────────────
174
- # 4. 기존 사용자 규칙 추출 (CLAUDE.md에서)
175
- # ─────────────────────────────────────────
176
- extract_user_rules() {
177
- if [ "$HAS_EXISTING_CLAUDE" = "exists" ]; then
178
- python3 << 'PYEOF'
179
- import json, os
180
-
181
- with open(os.environ.get("SCAN_RESULT", ".harness/actions/scan-result.json")) as f:
182
- scan = json.load(f)
183
-
184
- content = scan["existing_docs"]["existing_claude_md_content"]
185
-
186
- if not content or content.strip() == "":
187
- print("(기존 규칙 없음)")
188
- else:
189
- # 사용자 정의 규칙 영역만 추출 (harness 관련 제외)
190
- lines = content.split("\n")
191
- user_section = []
192
- skip = False
193
- for line in lines:
194
- # harness가 생성한 섹션은 제외
195
- if any(kw in line.lower() for kw in ["ia-map", "ia map", "harness", "## tech stack"]):
196
- skip = True
197
- elif line.startswith("## ") and skip:
198
- skip = False
199
-
200
- if not skip:
201
- user_section.append(line)
202
-
203
- result = "\n".join(user_section).strip()
204
- if result:
205
- print(result)
206
- else:
207
- print("(기존 규칙 없음)")
208
- PYEOF
209
- else
210
- echo "(기존 규칙 없음)"
211
- fi
212
- }
213
-
214
- USER_RULES=$(extract_user_rules)
215
-
216
- # ─────────────────────────────────────────
217
- # 5. AGENTS.md 생성
218
- # ─────────────────────────────────────────
219
- cat > "$AGENTS_FILE" << AGENTSEOF
220
- # AGENTS.md — Project Context for AI Agents
221
-
222
- > 이 파일은 모든 AI 에이전트(Claude, Cursor, Copilot, Windsurf 등)의 공통 진입점입니다.
223
- > CLAUDE.md는 이 파일의 심볼릭 링크입니다.
224
- > Generated by walwal-harness ($(date +%Y-%m-%d))
225
-
226
- ## Project
227
-
228
- - **Name**: (Planner가 설정)
229
- - **Description**: (Planner가 설정)
230
- - **Phase**: INIT
231
- - **Type**: ${PROJECT_TYPE}
232
- - **Harness**: \`.harness/HARNESS.md\` 참조
233
-
234
- ## Tech Stack (Auto-detected)
235
-
236
- - Backend: ${TECH_BE}
237
- - Frontend: ${TECH_FE}
238
- - Database: ${TECH_DB}
239
- - Monorepo: ${TECH_MONO}
240
- - OpenAPI: ${OPENAPI}
241
-
242
- ## IA-MAP (Information Architecture)
243
-
244
- > 자동 스캔으로 생성됨. Planner가 검토 후 확정해야 합니다.
245
- > \`[?]\` 태그는 Planner가 분류해야 하는 미확인 경로입니다.
246
-
247
- \`\`\`
248
- /
249
- ${IA_MAP}
250
- ├── AGENTS.md # [META] 프로젝트 컨텍스트 → Planner
251
- ├── CLAUDE.md # [META] → AGENTS.md 심볼릭 링크
252
- └── .harness/ # [HARNESS] 하네스 시스템 → Planner
253
- \`\`\`
254
-
255
- ### IA-MAP 범례
256
-
257
- | 태그 | 의미 | 소유 에이전트 |
258
- |------|------|--------------|
259
- | \`[BE]\` | Backend 영역 | Generator-Backend |
260
- | \`[FE]\` | Frontend 영역 | Generator-Frontend |
261
- | \`[HARNESS]\` | 하네스 시스템 | Planner / Evaluator |
262
- | \`[META]\` | 프로젝트 메타 문서 | Planner |
263
- | \`[INFRA]\` | 인프라/배포 설정 | Planner |
264
- | \`[TEST]\` | 테스트 코드 | Evaluator / Generator |
265
- | \`[?]\` | 미분류 (Planner 확인 필요) | TBD |
266
-
267
- ## Preserved Rules (기존 프로젝트에서 이관)
268
-
269
- ${USER_RULES}
270
-
271
- ## Rules (모든 에이전트 공통)
272
-
273
- ### 읽기/쓰기 권한
274
-
275
- | 파일 | 읽기 | 쓰기 |
276
- |------|------|------|
277
- | AGENTS.md | 전체 | Planner만 |
278
- | .harness/actions/api-contract.json | 전체 | Planner만 |
279
- | .harness/actions/feature-list.json | 전체 | passes: Generator, 나머지: Planner |
280
- | .harness/progress.json | 전체 | 전체 (Session Boundary Protocol에 따라 업데이트) |
281
- | \`[BE]\` 소유 경로 | 전체 | Generator-Backend만 |
282
- | \`[FE]\` 소유 경로 | 전체 | Generator-Frontend만 |
283
- | .harness/archive/ | 전체 | 쓰기 금지 (불변) |
284
-
285
- ### 변경 요청 프로토콜
286
-
287
- AGENTS.md 또는 api-contract.json 변경이 필요할 때:
288
- 1. sprint-contract.md에 \`## Change Request\` 섹션 추가
289
- 2. Planner가 다음 스프린트 전환 시 반영 여부 결정
290
-
291
- ### 금지 사항
292
-
293
- - AGENTS.md를 Planner 외 에이전트가 수정
294
- - 서비스 간 직접 DB 접근
295
- - 테스트 삭제/약화
296
- - archive/ 내 파일 수정
297
- - 프로젝트를 조기 "완료" 선언
298
-
299
- ## Harness Quick Reference
300
-
301
- | 명령 | 설명 |
302
- |------|------|
303
- | \`bash scripts/scan-project.sh\` | 프로젝트 구조 재스캔 |
304
- | \`.harness/progress.json\` | 현재 진행 상태 (기계 판독) |
305
- | \`.harness/actions/\` | 활성 스프린트 문서 |
306
- | \`.harness/HARNESS.md\` | 하네스 상세 가이드 |
307
- AGENTSEOF
308
-
309
- echo ""
310
- echo "=== AGENTS.md Generated ==="
311
- echo "Path: ${AGENTS_FILE}"
312
-
313
- # ─────────────────────────────────────────
314
- # 6. CLAUDE.md 심볼릭 링크
315
- # ─────────────────────────────────────────
316
- if [ -f "$CLAUDE_FILE" ] && [ ! -L "$CLAUDE_FILE" ]; then
317
- echo ""
318
- echo "WARNING: CLAUDE.md가 일반 파일로 존재합니다."
319
- echo "기존 내용은 AGENTS.md의 'Preserved Rules' 섹션에 이관되었습니다."
320
- echo "백업: ${BACKUP_DIR}/"
321
- rm "$CLAUDE_FILE"
322
- fi
323
-
324
- if [ ! -L "$CLAUDE_FILE" ]; then
325
- ln -sf AGENTS.md "$CLAUDE_FILE"
326
- echo "Created symlink: CLAUDE.md → AGENTS.md"
327
- else
328
- echo "Symlink already exists: CLAUDE.md → AGENTS.md"
329
- fi
330
-
331
- echo ""
332
- echo "=== Initialization Complete ==="
333
- echo ""
334
- echo "Next steps:"
335
- echo " 1. AGENTS.md의 [?] 태그를 확인하고 Planner에게 분류를 요청하세요"
336
- echo " 2. 'Preserved Rules' 섹션에서 불필요한 규칙을 정리하세요"
337
- echo " 3. '하네스 엔지니어링 시작'으로 Dispatcher를 실행하세요"