harness-evolver 4.2.5 → 4.2.6

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,7 +1,7 @@
1
1
  {
2
2
  "name": "harness-evolver",
3
3
  "description": "LangSmith-native autonomous agent optimization — evolves LLM agent code using multi-agent proposers, LangSmith experiments, and git worktrees",
4
- "version": "4.2.5",
4
+ "version": "4.2.6",
5
5
  "author": {
6
6
  "name": "Raphael Valdetaro"
7
7
  },
package/bin/install.js CHANGED
@@ -374,7 +374,7 @@ function installPythonDeps() {
374
374
  return false;
375
375
  }
376
376
 
377
- async function configureLangSmith(rl) {
377
+ async function configureLangSmith(rl, nonInteractive) {
378
378
  const langsmithCredsDir = process.platform === "darwin"
379
379
  ? path.join(HOME, "Library", "Application Support", "langsmith-cli")
380
380
  : path.join(HOME, ".config", "langsmith-cli");
@@ -393,13 +393,28 @@ async function configureLangSmith(rl) {
393
393
  try {
394
394
  const content = fs.readFileSync(langsmithCredsFile, "utf8");
395
395
  if (content.includes("LANGSMITH_API_KEY=lsv2_")) {
396
- stepDone("API key found in credentials file");
397
- hasKey = true;
396
+ // Validate existing key with a real request
397
+ const existingKey = content.match(/LANGSMITH_API_KEY=(lsv2_[^\s\n]+)/)?.[1];
398
+ if (existingKey) {
399
+ try {
400
+ execSync(`curl -sf -o /dev/null -w "%{http_code}" -H "x-api-key: ${existingKey}" https://api.smith.langchain.com/info`, { stdio: "pipe", timeout: 10000 });
401
+ stepDone("API key found and validated");
402
+ hasKey = true;
403
+ } catch {
404
+ barLine(c.yellow("API key found but could not be validated — LangSmith may be unreachable"));
405
+ barLine(c.dim("Will ask for a new key just in case."));
406
+ }
407
+ }
398
408
  }
399
409
  } catch {}
400
410
  }
401
411
 
402
412
  if (!hasKey) {
413
+ if (nonInteractive) {
414
+ stepError("No API key found — set LANGSMITH_API_KEY in environment and re-run");
415
+ barLine(c.dim("Run: export LANGSMITH_API_KEY=lsv2_pt_your_key"));
416
+ return;
417
+ }
403
418
  barLine(c.dim("Get yours at https://smith.langchain.com/settings"));
404
419
  barLine(c.dim("LangSmith is required. The evolver won't work without it."));
405
420
  barEmpty();
@@ -410,6 +425,13 @@ async function configureLangSmith(rl) {
410
425
  const key = apiKey.trim();
411
426
 
412
427
  if (key && key.startsWith("lsv2_")) {
428
+ // Validate key with a real request before saving
429
+ try {
430
+ execSync(`curl -sf -o /dev/null -w "%{http_code}" -H "x-api-key: ${key}" https://api.smith.langchain.com/info`, { stdio: "pipe", timeout: 10000 });
431
+ } catch {
432
+ barLine(c.yellow("Key could not be validated — LangSmith may be unreachable"));
433
+ barLine(c.dim("Saving anyway. If it doesn't work, re-run the installer."));
434
+ }
413
435
  try {
414
436
  fs.mkdirSync(langsmithCredsDir, { recursive: true });
415
437
  fs.writeFileSync(langsmithCredsFile, `LANGSMITH_API_KEY=${key}\n`);
@@ -454,7 +476,7 @@ async function configureLangSmith(rl) {
454
476
  }
455
477
  }
456
478
 
457
- async function configureOptionalIntegrations(rl) {
479
+ async function configureOptionalIntegrations(rl, nonInteractive) {
458
480
  barEmpty();
459
481
  step(c.bold("Optional Integrations"));
460
482
  barEmpty();
@@ -474,7 +496,7 @@ async function configureOptionalIntegrations(rl) {
474
496
 
475
497
  if (hasContext7) {
476
498
  stepDone("Context7 MCP already configured");
477
- } else {
499
+ } else if (!nonInteractive) {
478
500
  barLine(c.bold("Context7 MCP") + " \u2014 " + c.dim("up-to-date library documentation"));
479
501
  const c7Answer = await ask(rl, `${c.cyan(S.stepActive)} Install Context7 MCP? [y/N]: `);
480
502
  if (c7Answer.trim().toLowerCase() === "y") {
@@ -506,7 +528,7 @@ async function configureOptionalIntegrations(rl) {
506
528
 
507
529
  if (hasLcDocs) {
508
530
  stepDone("LangChain Docs MCP already configured");
509
- } else {
531
+ } else if (!nonInteractive) {
510
532
  barLine(c.bold("LangChain Docs MCP") + " \u2014 " + c.dim("LangChain/LangGraph/LangSmith docs"));
511
533
  const lcAnswer = await ask(rl, `${c.cyan(S.stepActive)} Install LangChain Docs MCP? [y/N]: `);
512
534
  if (lcAnswer.trim().toLowerCase() === "y") {
@@ -525,6 +547,8 @@ async function configureOptionalIntegrations(rl) {
525
547
  // ─── Main ───────────────────────────────────────────────────────────────────
526
548
 
527
549
  async function main() {
550
+ const nonInteractive = process.argv.includes("--yes") || process.argv.includes("-y");
551
+
528
552
  banner();
529
553
 
530
554
  header("harness-evolver");
@@ -540,6 +564,21 @@ async function main() {
540
564
  }
541
565
  } catch {}
542
566
 
567
+ // Check installed version
568
+ const versionPath = path.join(HOME, ".evolver", "VERSION");
569
+ let installedVersion = null;
570
+ if (fs.existsSync(versionPath)) {
571
+ installedVersion = fs.readFileSync(versionPath, "utf8").trim();
572
+ }
573
+
574
+ if (installedVersion && installedVersion !== VERSION) {
575
+ step(`Upgrading ${c.dim(installedVersion)} → ${c.cyan(VERSION)}`);
576
+ } else if (installedVersion === VERSION) {
577
+ step(`Reinstalling ${c.cyan(VERSION)}`);
578
+ } else {
579
+ step(`Fresh install ${c.cyan(VERSION)}`);
580
+ }
581
+
543
582
  barEmpty();
544
583
 
545
584
  // Python check
@@ -567,6 +606,11 @@ async function main() {
567
606
 
568
607
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
569
608
 
609
+ function askOrDefault(question, defaultValue) {
610
+ if (nonInteractive) return Promise.resolve(defaultValue);
611
+ return ask(rl, question);
612
+ }
613
+
570
614
  // Runtime selection
571
615
  barEmpty();
572
616
  stepPrompt("Which runtime(s) to install for?");
@@ -577,7 +621,7 @@ async function main() {
577
621
  barLine(c.dim("Select multiple: 1,2 or 1 2"));
578
622
  }
579
623
 
580
- const runtimeAnswer = await ask(rl, `${c.cyan(S.stepActive)} Choice [1]: `);
624
+ const runtimeAnswer = await askOrDefault(`${c.cyan(S.stepActive)} Choice [1]: `, "1");
581
625
  const runtimeInput = (runtimeAnswer.trim() || "1");
582
626
 
583
627
  let selected;
@@ -598,7 +642,7 @@ async function main() {
598
642
  barLine(` ${c.bold("1")} Global ${c.dim(`(~/${selected[0].dir})`)}`);
599
643
  barLine(` ${c.bold("2")} Local ${c.dim(`(./${selected[0].dir})`)}`);
600
644
 
601
- const scopeAnswer = await ask(rl, `${c.cyan(S.stepActive)} Choice [1]: `);
645
+ const scopeAnswer = await askOrDefault(`${c.cyan(S.stepActive)} Choice [1]: `, "1");
602
646
  const scope = (scopeAnswer.trim() === "2") ? "local" : "global";
603
647
 
604
648
  stepDone(`Scope: ${c.cyan(scope)}`);
@@ -632,8 +676,20 @@ async function main() {
632
676
  const toolCount = installTools();
633
677
  stepDone(`${toolCount} tools installed to ~/.evolver/tools/`);
634
678
 
635
- // Version marker
636
- const versionPath = path.join(HOME, ".evolver", "VERSION");
679
+ // Suggest .worktreeinclude for worktree support
680
+ barEmpty();
681
+ const cwdGit = fs.existsSync(path.join(process.cwd(), ".git"));
682
+ const cwdWorktreeInclude = fs.existsSync(path.join(process.cwd(), ".worktreeinclude"));
683
+ if (cwdGit && !cwdWorktreeInclude) {
684
+ step("Worktree support");
685
+ barLine(c.dim("For /evolver:evolve to work, .evolver.json needs to be in worktrees."));
686
+ barLine(c.dim("Create .worktreeinclude in your project root with:"));
687
+ barLine(c.dim(" .evolver.json"));
688
+ barLine(c.dim(" .env"));
689
+ stepDone("Tip shown");
690
+ }
691
+
692
+ // Version marker (versionPath declared earlier for upgrade check)
637
693
  fs.mkdirSync(path.dirname(versionPath), { recursive: true });
638
694
  fs.writeFileSync(versionPath, VERSION);
639
695
 
@@ -642,10 +698,10 @@ async function main() {
642
698
  installPythonDeps();
643
699
 
644
700
  // Configure LangSmith
645
- await configureLangSmith(rl);
701
+ await configureLangSmith(rl, nonInteractive);
646
702
 
647
703
  // Optional integrations
648
- await configureOptionalIntegrations(rl);
704
+ await configureOptionalIntegrations(rl, nonInteractive);
649
705
 
650
706
  // Done
651
707
  barEmpty();
@@ -657,6 +713,9 @@ async function main() {
657
713
  barLine(` ${c.cyan("/evolver:status")} \u2014 check progress`);
658
714
  barLine(` ${c.cyan("/evolver:deploy")} \u2014 finalize and push`);
659
715
  barEmpty();
716
+ barLine(c.dim("Plugin marketplace (auto-updates):"));
717
+ barLine(` ${c.cyan("/plugin install harness-evolver")} ${c.dim("— from Claude Code marketplace")}`);
718
+ barEmpty();
660
719
  barLine(c.dim("GitHub: https://github.com/raphaelchristi/harness-evolver"));
661
720
  footer();
662
721
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-evolver",
3
- "version": "4.2.5",
3
+ "version": "4.2.6",
4
4
  "description": "LangSmith-native autonomous agent optimization for Claude Code",
5
5
  "author": "Raphael Valdetaro",
6
6
  "license": "MIT",
@@ -23,6 +23,8 @@ EVOLVER_PY="${EVOLVER_PY:-$([ -f "$HOME/.evolver/venv/bin/python" ] && echo "$HO
23
23
 
24
24
  Use `$EVOLVER_PY` instead of `python3` for ALL tool invocations.
25
25
 
26
+ **IMPORTANT: Never pass `LANGSMITH_API_KEY` inline in Bash commands.** The key is loaded automatically by the SessionStart hook and by each tool's `ensure_langsmith_api_key()`. Passing it inline exposes it in the output.
27
+
26
28
  ## Parse Arguments
27
29
 
28
30
  - `--iterations N` (default: from interactive question or 5)
@@ -45,6 +45,8 @@ EVOLVER_PY="${EVOLVER_PY:-$([ -f "$HOME/.evolver/venv/bin/python" ] && echo "$HO
45
45
 
46
46
  Use `$EVOLVER_PY` instead of `python3` for ALL tool invocations. This ensures the venv with langsmith is used.
47
47
 
48
+ **IMPORTANT: Never pass `LANGSMITH_API_KEY` inline in Bash commands.** The key is loaded automatically by the SessionStart hook (from credentials file or environment) and by each Python tool's `ensure_langsmith_api_key()`. Passing it inline exposes it in the output. If the key is missing, tell the user to run `export LANGSMITH_API_KEY=lsv2_pt_...` instead.
49
+
48
50
  ## Phase 1: Explore Project (automatic)
49
51
 
50
52
  ```bash