harness-evolver 0.4.0 → 0.5.0

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/install.js +155 -52
  2. package/package.json +1 -1
package/bin/install.js CHANGED
@@ -1,26 +1,53 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Harness Evolver installer.
4
- * Detects Claude Code, copies skills/agents/tools to the right locations.
4
+ * Interactive setup with runtime selection, global/local choice.
5
5
  *
6
6
  * Usage: npx harness-evolver@latest
7
7
  */
8
8
 
9
9
  const fs = require("fs");
10
10
  const path = require("path");
11
+ const readline = require("readline");
11
12
  const { execSync } = require("child_process");
12
13
 
14
+ const VERSION = require("../package.json").version;
13
15
  const PLUGIN_ROOT = path.resolve(__dirname, "..");
14
16
  const HOME = process.env.HOME || process.env.USERPROFILE;
15
17
 
16
- const CLAUDE_DIR = path.join(HOME, ".claude");
17
- const COMMANDS_DIR = path.join(CLAUDE_DIR, "commands", "harness-evolver");
18
- const AGENTS_DIR = path.join(CLAUDE_DIR, "agents");
19
- const TOOLS_DIR = path.join(HOME, ".harness-evolver", "tools");
20
- const EXAMPLES_DIR = path.join(HOME, ".harness-evolver", "examples");
21
-
22
- function log(msg) {
23
- console.log(` ${msg}`);
18
+ // ANSI colors
19
+ const CYAN = "\x1b[36m";
20
+ const GREEN = "\x1b[32m";
21
+ const YELLOW = "\x1b[33m";
22
+ const RED = "\x1b[31m";
23
+ const DIM = "\x1b[2m";
24
+ const BOLD = "\x1b[1m";
25
+ const RESET = "\x1b[0m";
26
+
27
+ const LOGO = `
28
+ ${CYAN} ██╗ ██╗ █████╗ ██████╗ ███╗ ██╗███████╗███████╗███████╗
29
+ ██║ ██║██╔══██╗██╔══██╗████╗ ██║██╔════╝██╔════╝██╔════╝
30
+ ███████║███████║██████╔╝██╔██╗ ██║█████╗ ███████╗███████╗
31
+ ██╔══██║██╔══██║██╔══██╗██║╚██╗██║██╔══╝ ╚════██║╚════██║
32
+ ██║ ██║██║ ██║██║ ██║██║ ╚████║███████╗███████║███████║
33
+ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝╚══════╝
34
+ ${BOLD}███████╗██╗ ██╗ ██████╗ ██╗ ██╗ ██╗███████╗██████╗
35
+ ██╔════╝██║ ██║██╔═══██╗██║ ██║ ██║██╔════╝██╔══██╗
36
+ █████╗ ██║ ██║██║ ██║██║ ██║ ██║█████╗ ██████╔╝
37
+ ██╔══╝ ╚██╗ ██╔╝██║ ██║██║ ╚██╗ ██╔╝██╔══╝ ██╔══██╗
38
+ ███████╗ ╚████╔╝ ╚██████╔╝███████╗╚████╔╝ ███████╗██║ ██║
39
+ ╚══════╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝${RESET}
40
+ `;
41
+
42
+ const RUNTIMES = [
43
+ { name: "Claude Code", dir: ".claude", detected: () => fs.existsSync(path.join(HOME, ".claude")) },
44
+ { name: "Cursor", dir: ".cursor", detected: () => fs.existsSync(path.join(HOME, ".cursor")) },
45
+ { name: "Codex", dir: ".codex", detected: () => fs.existsSync(path.join(HOME, ".codex")) },
46
+ { name: "Windsurf", dir: ".windsurf", detected: () => fs.existsSync(path.join(HOME, ".windsurf")) },
47
+ ];
48
+
49
+ function ask(rl, question) {
50
+ return new Promise((resolve) => rl.question(question, resolve));
24
51
  }
25
52
 
26
53
  function copyDir(src, dest) {
@@ -50,76 +77,152 @@ function checkPython() {
50
77
  }
51
78
  }
52
79
 
53
- function main() {
54
- console.log("\n Harness Evolver v0.1.0\n");
80
+ function installForRuntime(runtimeDir, scope) {
81
+ const baseDir = scope === "local"
82
+ ? path.join(process.cwd(), runtimeDir)
83
+ : path.join(HOME, runtimeDir);
55
84
 
56
- if (!checkPython()) {
57
- console.error(" ERROR: python3 not found in PATH. Install Python 3.8+ first.");
58
- process.exit(1);
59
- }
60
- log("\u2713 python3 found");
85
+ const commandsDir = path.join(baseDir, "commands", "harness-evolver");
86
+ const agentsDir = path.join(baseDir, "agents");
61
87
 
62
- if (!fs.existsSync(CLAUDE_DIR)) {
63
- console.error(` ERROR: Claude Code directory not found at ${CLAUDE_DIR}`);
64
- console.error(" Install Claude Code first: https://claude.ai/code");
65
- process.exit(1);
66
- }
67
- log("\u2713 Claude Code detected");
68
-
69
- // Copy skills
88
+ // Skills
70
89
  const skillsSource = path.join(PLUGIN_ROOT, "skills");
71
90
  if (fs.existsSync(skillsSource)) {
72
91
  for (const skill of fs.readdirSync(skillsSource, { withFileTypes: true })) {
73
92
  if (skill.isDirectory()) {
74
- const src = path.join(skillsSource, skill.name);
75
- const dest = path.join(COMMANDS_DIR, skill.name);
76
- copyDir(src, dest);
77
- log(` skill: ${skill.name}`);
93
+ copyDir(path.join(skillsSource, skill.name), path.join(commandsDir, skill.name));
94
+ console.log(` ${GREEN}✓${RESET} Installed skill: ${skill.name}`);
78
95
  }
79
96
  }
80
97
  }
81
98
 
82
- // Copy agents
99
+ // Agents
83
100
  const agentsSource = path.join(PLUGIN_ROOT, "agents");
84
101
  if (fs.existsSync(agentsSource)) {
85
- fs.mkdirSync(AGENTS_DIR, { recursive: true });
102
+ fs.mkdirSync(agentsDir, { recursive: true });
86
103
  for (const agent of fs.readdirSync(agentsSource)) {
87
- copyFile(
88
- path.join(agentsSource, agent),
89
- path.join(AGENTS_DIR, agent)
90
- );
91
- log(` agent: ${agent}`);
104
+ copyFile(path.join(agentsSource, agent), path.join(agentsDir, agent));
105
+ console.log(` ${GREEN}✓${RESET} Installed agent: ${agent}`);
92
106
  }
93
107
  }
108
+ }
94
109
 
95
- // Copy tools
110
+ function installTools() {
111
+ const toolsDir = path.join(HOME, ".harness-evolver", "tools");
96
112
  const toolsSource = path.join(PLUGIN_ROOT, "tools");
97
113
  if (fs.existsSync(toolsSource)) {
98
- fs.mkdirSync(TOOLS_DIR, { recursive: true });
114
+ fs.mkdirSync(toolsDir, { recursive: true });
99
115
  for (const tool of fs.readdirSync(toolsSource)) {
100
116
  if (tool.endsWith(".py")) {
101
- copyFile(
102
- path.join(toolsSource, tool),
103
- path.join(TOOLS_DIR, tool)
104
- );
105
- log(` tool: ${tool}`);
117
+ copyFile(path.join(toolsSource, tool), path.join(toolsDir, tool));
118
+ console.log(` ${GREEN}✓${RESET} Installed tool: ${tool}`);
106
119
  }
107
120
  }
108
121
  }
122
+ }
109
123
 
110
- // Copy examples
124
+ function installExamples() {
125
+ const examplesDir = path.join(HOME, ".harness-evolver", "examples");
111
126
  const examplesSource = path.join(PLUGIN_ROOT, "examples");
112
127
  if (fs.existsSync(examplesSource)) {
113
- copyDir(examplesSource, EXAMPLES_DIR);
114
- log(" examples: classifier");
128
+ copyDir(examplesSource, examplesDir);
129
+ console.log(` ${GREEN}✓${RESET} Installed examples: classifier`);
130
+ }
131
+ }
132
+
133
+ async function main() {
134
+ console.log(LOGO);
135
+ console.log(` ${DIM}Harness Evolver v${VERSION}${RESET}`);
136
+ console.log(` ${DIM}Meta-Harness-style autonomous harness optimization${RESET}`);
137
+ console.log();
138
+
139
+ // Check python
140
+ if (!checkPython()) {
141
+ console.error(` ${RED}ERROR:${RESET} python3 not found in PATH. Install Python 3.8+ first.`);
142
+ process.exit(1);
143
+ }
144
+ console.log(` ${GREEN}✓${RESET} python3 found`);
145
+
146
+ // Detect runtimes
147
+ const available = RUNTIMES.filter((r) => r.detected());
148
+ if (available.length === 0) {
149
+ console.error(`\n ${RED}ERROR:${RESET} No supported runtime detected.`);
150
+ console.error(` Install Claude Code, Cursor, Codex, or Windsurf first.`);
151
+ process.exit(1);
115
152
  }
116
153
 
117
- console.log("\n \u2713 Installed successfully!\n");
118
- console.log(" Next steps:");
119
- console.log(" 1. Copy an example: cp -r ~/.harness-evolver/examples/classifier ./my-project");
120
- console.log(" 2. cd my-project");
121
- console.log(" 3. /harness-evolve-init --harness harness.py --eval eval.py --tasks tasks/");
122
- console.log(" 4. /harness-evolve --iterations 5\n");
154
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
155
+
156
+ // Runtime selection
157
+ console.log(`\n ${YELLOW}Which runtime(s) would you like to install for?${RESET}\n`);
158
+ available.forEach((r, i) => {
159
+ console.log(` ${i + 1}) ${r.name.padEnd(14)} (~/${r.dir})`);
160
+ });
161
+ if (available.length > 1) {
162
+ console.log(` ${available.length + 1}) All`);
163
+ console.log(`\n ${DIM}Select multiple: 1,2 or 1 2${RESET}`);
164
+ }
165
+
166
+ const defaultChoice = "1";
167
+ const runtimeAnswer = await ask(rl, `\n ${YELLOW}Choice [${defaultChoice}]:${RESET} `);
168
+ const runtimeInput = (runtimeAnswer.trim() || defaultChoice);
169
+
170
+ let selectedRuntimes;
171
+ if (runtimeInput === String(available.length + 1)) {
172
+ selectedRuntimes = available;
173
+ } else {
174
+ const indices = runtimeInput.split(/[,\s]+/).map((s) => parseInt(s, 10) - 1);
175
+ selectedRuntimes = indices
176
+ .filter((i) => i >= 0 && i < available.length)
177
+ .map((i) => available[i]);
178
+ }
179
+
180
+ if (selectedRuntimes.length === 0) {
181
+ selectedRuntimes = [available[0]];
182
+ }
183
+
184
+ // Scope selection
185
+ console.log(`\n ${YELLOW}Where would you like to install?${RESET}\n`);
186
+ console.log(` 1) Global (~/${selectedRuntimes[0].dir}) - available in all projects`);
187
+ console.log(` 2) Local (./${selectedRuntimes[0].dir}) - this project only`);
188
+
189
+ const scopeAnswer = await ask(rl, `\n ${YELLOW}Choice [1]:${RESET} `);
190
+ const scope = (scopeAnswer.trim() === "2") ? "local" : "global";
191
+
192
+ console.log();
193
+
194
+ // Install for each selected runtime
195
+ for (const runtime of selectedRuntimes) {
196
+ const target = scope === "local" ? `./${runtime.dir}` : `~/${runtime.dir}`;
197
+ console.log(` Installing for ${CYAN}${runtime.name}${RESET} to ${target}`);
198
+ console.log();
199
+ installForRuntime(runtime.dir, scope);
200
+ }
201
+
202
+ // Tools and examples are always global
203
+ installTools();
204
+ installExamples();
205
+
206
+ // Write version file
207
+ const versionPath = path.join(HOME, ".harness-evolver", "VERSION");
208
+ fs.mkdirSync(path.dirname(versionPath), { recursive: true });
209
+ fs.writeFileSync(versionPath, VERSION);
210
+ console.log(` ${GREEN}✓${RESET} Wrote VERSION (${VERSION})`);
211
+
212
+ console.log(`\n ${GREEN}Done!${RESET} Open a project in Claude Code and run ${CYAN}/harness-evolver:init${RESET}`);
213
+ console.log(`\n ${DIM}Quick start with example:${RESET}`);
214
+ console.log(` cp -r ~/.harness-evolver/examples/classifier ./my-project`);
215
+ console.log(` cd my-project && claude`);
216
+ console.log(` /harness-evolver:init`);
217
+ console.log(` /harness-evolver:evolve`);
218
+
219
+ console.log(`\n ${DIM}GitHub: https://github.com/raphaelchristi/harness-evolver${RESET}`);
220
+ console.log();
221
+
222
+ rl.close();
123
223
  }
124
224
 
125
- main();
225
+ main().catch((err) => {
226
+ console.error(` ${RED}ERROR:${RESET} ${err.message}`);
227
+ process.exit(1);
228
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-evolver",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Meta-Harness-style autonomous harness optimization for Claude Code",
5
5
  "author": "Raphael Valdetaro Christi Cordeiro",
6
6
  "license": "MIT",