@walwal-harness/cli 4.0.0-beta.2 → 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.
Files changed (2) hide show
  1. package/bin/init.js +95 -11
  2. package/package.json +1 -1
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.2",
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"