@walwal-harness/cli 4.0.0-beta.1 → 4.0.0-beta.3

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.
package/bin/init.js CHANGED
@@ -129,17 +129,56 @@ function scaffoldHarness() {
129
129
  }
130
130
  }
131
131
 
132
- // Copy config.json
132
+ // config.json — ALWAYS update (harness system file, not user data)
133
+ // But preserve user's custom settings (pre_eval_gate.frontend_cwd, behavior, etc.)
133
134
  const configSrc = path.join(PKG_ROOT, 'assets', 'templates', 'config.json');
134
135
  const configDest = path.join(HARNESS_DIR, 'config.json');
135
- if (fs.existsSync(configSrc) && (!fileExists(configDest) || isForce)) {
136
- copyFile(configSrc, configDest);
136
+ if (fs.existsSync(configSrc)) {
137
+ if (fileExists(configDest) && !isForce) {
138
+ // Merge: keep user's customizations, update harness structure
139
+ try {
140
+ const existing = JSON.parse(fs.readFileSync(configDest, 'utf8'));
141
+ const template = JSON.parse(fs.readFileSync(configSrc, 'utf8'));
142
+ // Preserve user customizations
143
+ const userPreserve = {
144
+ behavior: existing.behavior,
145
+ 'flow.pre_eval_gate.frontend_cwd': existing?.flow?.pre_eval_gate?.frontend_cwd,
146
+ 'flow.pre_eval_gate.backend_cwd': existing?.flow?.pre_eval_gate?.backend_cwd,
147
+ 'flow.pre_eval_gate.frontend_checks': existing?.flow?.pre_eval_gate?.frontend_checks,
148
+ 'flow.pre_eval_gate.backend_checks': existing?.flow?.pre_eval_gate?.backend_checks,
149
+ };
150
+ // Write template, then re-apply user settings
151
+ fs.writeFileSync(configDest, JSON.stringify(template, null, 2) + '\n');
152
+ // Re-apply preserved user settings
153
+ const merged = JSON.parse(fs.readFileSync(configDest, 'utf8'));
154
+ if (userPreserve.behavior) merged.behavior = userPreserve.behavior;
155
+ if (userPreserve['flow.pre_eval_gate.frontend_cwd']) {
156
+ merged.flow.pre_eval_gate.frontend_cwd = userPreserve['flow.pre_eval_gate.frontend_cwd'];
157
+ }
158
+ if (userPreserve['flow.pre_eval_gate.backend_cwd']) {
159
+ merged.flow.pre_eval_gate.backend_cwd = userPreserve['flow.pre_eval_gate.backend_cwd'];
160
+ }
161
+ if (userPreserve['flow.pre_eval_gate.frontend_checks']) {
162
+ merged.flow.pre_eval_gate.frontend_checks = userPreserve['flow.pre_eval_gate.frontend_checks'];
163
+ }
164
+ if (userPreserve['flow.pre_eval_gate.backend_checks']) {
165
+ merged.flow.pre_eval_gate.backend_checks = userPreserve['flow.pre_eval_gate.backend_checks'];
166
+ }
167
+ fs.writeFileSync(configDest, JSON.stringify(merged, null, 2) + '\n');
168
+ log('config.json updated (user settings preserved)');
169
+ } catch (e) {
170
+ copyFile(configSrc, configDest);
171
+ log('config.json replaced (merge failed)');
172
+ }
173
+ } else {
174
+ copyFile(configSrc, configDest);
175
+ }
137
176
  }
138
177
 
139
- // Copy HARNESS.md
178
+ // HARNESS.md — ALWAYS update
140
179
  const harnessMdSrc = path.join(PKG_ROOT, 'assets', 'templates', 'HARNESS.md');
141
180
  const harnessMdDest = path.join(HARNESS_DIR, 'HARNESS.md');
142
- if (fs.existsSync(harnessMdSrc) && (!fileExists(harnessMdDest) || isForce)) {
181
+ if (fs.existsSync(harnessMdSrc)) {
143
182
  copyFile(harnessMdSrc, harnessMdDest);
144
183
  }
145
184
 
@@ -190,16 +229,23 @@ function installSkills() {
190
229
  .filter(d => d.isDirectory())
191
230
  .map(d => d.name);
192
231
 
232
+ // Remove obsolete skills (cleaned up in v4)
233
+ const obsoleteSkills = ['harness-generator-frontend-flutter', 'harness-evaluator-functional-flutter', 'harness-team'];
234
+ for (const obs of obsoleteSkills) {
235
+ const obsPath = path.join(CLAUDE_SKILLS_DIR, obs);
236
+ if (fs.existsSync(obsPath)) {
237
+ fs.rmSync(obsPath, { recursive: true, force: true });
238
+ log(` Removed obsolete: ${obs}`);
239
+ }
240
+ }
241
+
193
242
  for (const skill of skills) {
194
243
  const src = path.join(skillsSrc, skill);
195
244
  const dest = path.join(CLAUDE_SKILLS_DIR, `harness-${skill}`);
196
245
 
197
- if (!fileExists(dest) || isForce) {
198
- copyDir(src, dest);
199
- log(` Installed: harness-${skill}`);
200
- } else {
201
- log(` Skipped (exists): harness-${skill}`);
202
- }
246
+ // Skills are ALWAYS overwritten — they are harness-managed, not user-editable
247
+ copyDir(src, dest);
248
+ log(` Installed: harness-${skill}`);
203
249
  }
204
250
 
205
251
  log('Skills installation complete');
@@ -214,6 +260,21 @@ function installScripts() {
214
260
  const scriptsSrc = path.join(PKG_ROOT, 'scripts');
215
261
  const scriptsDest = path.join(PROJECT_ROOT, 'scripts');
216
262
 
263
+ // Remove obsolete scripts from previous versions
264
+ const obsoleteScripts = [
265
+ 'harness-studio-v4.sh',
266
+ 'harness-control-v4.sh',
267
+ 'harness-prompts-v4.sh',
268
+ 'harness-team-worker.sh',
269
+ ];
270
+ for (const obs of obsoleteScripts) {
271
+ const obsPath = path.join(scriptsDest, obs);
272
+ if (fs.existsSync(obsPath)) {
273
+ fs.unlinkSync(obsPath);
274
+ log(` Removed obsolete: ${obs}`);
275
+ }
276
+ }
277
+
217
278
  // Core scripts are ALWAYS overwritten on update (not user-editable)
218
279
  // These contain harness logic that must stay in sync with the CLI version
219
280
  const coreScripts = new Set([
@@ -421,6 +482,28 @@ function installUserPromptSubmitHook() {
421
482
  // ─────────────────────────────────────────
422
483
  // 4. AGENTS.md + CLAUDE.md
423
484
  // ─────────────────────────────────────────
485
+ // ─────────────────────────────────────────
486
+ // 3d. Agent Teams env var
487
+ // ─────────────────────────────────────────
488
+ function installAgentTeamsEnv() {
489
+ const settingsPath = path.join(PROJECT_ROOT, '.claude', 'settings.json');
490
+ let settings = {};
491
+ if (fileExists(settingsPath)) {
492
+ try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (e) {}
493
+ }
494
+
495
+ if (!settings.env) settings.env = {};
496
+
497
+ if (settings.env['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] !== '1') {
498
+ settings.env['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] = '1';
499
+ ensureDir(path.dirname(settingsPath));
500
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
501
+ log('Agent Teams enabled (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1)');
502
+ } else {
503
+ log('Agent Teams already enabled');
504
+ }
505
+ }
506
+
424
507
  function setupAgentsMd() {
425
508
  const agentsMd = path.join(PROJECT_ROOT, 'AGENTS.md');
426
509
  const claudeMd = path.join(PROJECT_ROOT, 'CLAUDE.md');
@@ -703,6 +786,7 @@ function main() {
703
786
  installSessionHook();
704
787
  installStatusline();
705
788
  installUserPromptSubmitHook();
789
+ installAgentTeamsEnv();
706
790
  setupAgentsMd();
707
791
  checkPlaywrightMcp();
708
792
  checkRecommendedSkills();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "4.0.0-beta.1",
3
+ "version": "4.0.0-beta.3",
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,64 +1,26 @@
1
1
  ---
2
2
  name: harness-team-action
3
- description: "v4 Agent Teams 가동. feature-queue를 초기화하고 3개 Teammate를 생성하여 Feature 단위 Gen→Eval 병렬 실행을 시작한다. 트리거: '/harness-team-action', 'team 시작', 'agent team 가동', '시작', '팀 가동'"
3
+ description: "v4 Agent Teams 가동. feature-queue를 초기화하고 3개 Teammate를 생성하여 Feature 병렬 실행. 트리거: '/harness-team-action', 'team 시작', '팀 가동'"
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
7
7
  # /harness-team-action — Agent Teams 가동
8
8
 
9
- ## 전제 조건
10
-
11
- - `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` 이 `.claude/settings.json`에 설정되어 있어야 함
12
- - `.harness/actions/feature-list.json`이 존재해야 함 (Planner가 생성)
13
-
14
- ## 실행 절차
15
-
16
- ### Step 1: Feature Queue 초기화
17
-
18
- 먼저 feature-queue.json을 확인/생성합니다:
9
+ ## Step 1: Queue 초기화
19
10
 
20
11
  ```bash
21
- if [ ! -f .harness/actions/feature-queue.json ]; then
22
- bash scripts/harness-queue-manager.sh init .
23
- else
24
- bash scripts/harness-queue-manager.sh recover .
25
- fi
26
- bash scripts/harness-queue-manager.sh status .
12
+ if [ ! -f .harness/actions/feature-queue.json ]; then bash scripts/harness-queue-manager.sh init .; else bash scripts/harness-queue-manager.sh recover .; fi && bash scripts/harness-queue-manager.sh status .
27
13
  ```
28
14
 
29
- ### Step 2: Teammate 생성
30
-
31
- **아래 내용을 그대로 실행합니다** — 3명의 Teammate를 생성하여 Feature Queue에서 작업을 분배합니다.
32
-
33
- `.harness/actions/feature-queue.json`의 `ready` 큐에서 최대 3개 Feature를 가져와 각 Teammate에게 할당합니다.
34
- Teammate가 Feature를 완료하면 queue에서 다음 ready Feature를 가져옵니다.
35
-
36
- **Teammate 생성 요청:**
37
-
38
- 3명의 Teammate를 생성하세요:
39
-
40
- 1. **team-1** (Generator + Evaluator):
41
- - `.harness/actions/feature-queue.json`의 `ready` 배열에서 첫 번째 Feature를 가져옴
42
- - 해당 Feature에 대해: 코드 생성 → `tsc --noEmit` + `eslint` 검증 → AC 기반 기능 평가
43
- - PASS 시: `bash scripts/harness-queue-manager.sh pass {FEATURE_ID} .` 실행 후 다음 Feature
44
- - FAIL 시: 최대 3회 재시도, 3회 실패 시 `bash scripts/harness-queue-manager.sh fail {FEATURE_ID} .`
45
- - AGENTS.md의 파일 소유권 규칙 준수
46
-
47
- 2. **team-2** (동일 역할): ready 배열에서 두 번째 Feature
48
-
49
- 3. **team-3** (동일 역할): ready 배열에서 세 번째 Feature
15
+ ## Step 2: Teammate 생성
50
16
 
51
- **각 Teammate 공통 규칙:**
52
- - `.harness/actions/feature-list.json`에서 자신의 Feature 정보(AC, depends_on) 읽기
53
- - `.harness/actions/api-contract.json`에서 관련 엔드포인트 확인
54
- - `CONVENTIONS.md` 존재 시 준수
55
- - Feature 완료 시 `feature-list.json`의 해당 Feature `passes`에 `["generator-frontend", "evaluator-functional"]` 추가
56
- - 다른 Teammate의 Feature 코드를 수정하지 않음
57
- - 완료/실패 시 Lead에게 `SendMessage`로 결과 보고
17
+ Queue의 `ready` 배열에서 Feature를 읽고, **3명의 Teammate 즉시 생성**하세요.
58
18
 
59
- ## 실행
19
+ Teammate에게 전달할 지시:
20
+ - `.harness/actions/feature-queue.json`의 ready에서 Feature 1개를 담당
21
+ - 해당 Feature의 코드를 생성하고, AC(Acceptance Criteria)를 기준으로 자체 검증
22
+ - PASS 시: `bash scripts/harness-queue-manager.sh pass {FEATURE_ID} .` 실행
23
+ - FAIL 시: 재시도 (최대 3회), 3회 실패 시 `bash scripts/harness-queue-manager.sh fail {FEATURE_ID} .`
24
+ - 완료 후 Lead에게 결과 보고
60
25
 
61
- - Lead(이 세션)는 **오케스트레이터** — Teammate 진행 모니터링
62
- - 상태 확인: `bash scripts/harness-queue-manager.sh status .`
63
- - 실패 Feature 재큐: `bash scripts/harness-queue-manager.sh requeue F-XXX .`
64
- - 중지: `/harness-team-stop`
26
+ **Teammate 이름**: team-1, team-2, team-3