harness-evolver 0.5.1 → 0.7.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Raphael Valdetaro Christi Cordeiro
3
+ Copyright (c) 2026 Raphael Valdetaro
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/bin/install.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Harness Evolver installer.
4
- * Interactive setup with runtime selection, global/local choice.
4
+ * Copies skills/agents/tools directly to runtime directories (GSD pattern).
5
5
  *
6
6
  * Usage: npx harness-evolver@latest
7
7
  */
@@ -15,7 +15,6 @@ const VERSION = require("../package.json").version;
15
15
  const PLUGIN_ROOT = path.resolve(__dirname, "..");
16
16
  const HOME = process.env.HOME || process.env.USERPROFILE;
17
17
 
18
- // ANSI colors
19
18
  const MAGENTA = "\x1b[35m";
20
19
  const BRIGHT_MAGENTA = "\x1b[95m";
21
20
  const GREEN = "\x1b[32m";
@@ -40,13 +39,6 @@ ${BRIGHT_MAGENTA} ██╗ ██╗ █████╗ ██████╗
40
39
  ╚══════╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝${RESET}
41
40
  `;
42
41
 
43
- const RUNTIMES = [
44
- { name: "Claude Code", dir: ".claude", detected: () => fs.existsSync(path.join(HOME, ".claude")) },
45
- { name: "Cursor", dir: ".cursor", detected: () => fs.existsSync(path.join(HOME, ".cursor")) },
46
- { name: "Codex", dir: ".codex", detected: () => fs.existsSync(path.join(HOME, ".codex")) },
47
- { name: "Windsurf", dir: ".windsurf", detected: () => fs.existsSync(path.join(HOME, ".windsurf")) },
48
- ];
49
-
50
42
  function ask(rl, question) {
51
43
  return new Promise((resolve) => rl.question(question, resolve));
52
44
  }
@@ -86,18 +78,18 @@ function installForRuntime(runtimeDir, scope) {
86
78
  const commandsDir = path.join(baseDir, "commands", "harness-evolver");
87
79
  const agentsDir = path.join(baseDir, "agents");
88
80
 
89
- // Skills
81
+ // Skills → commands/harness-evolver/
90
82
  const skillsSource = path.join(PLUGIN_ROOT, "skills");
91
83
  if (fs.existsSync(skillsSource)) {
92
84
  for (const skill of fs.readdirSync(skillsSource, { withFileTypes: true })) {
93
85
  if (skill.isDirectory()) {
94
86
  copyDir(path.join(skillsSource, skill.name), path.join(commandsDir, skill.name));
95
- console.log(` ${GREEN}✓${RESET} Installed skill: ${skill.name}`);
87
+ console.log(` ${GREEN}✓${RESET} Installed command: harness-evolver:${skill.name}`);
96
88
  }
97
89
  }
98
90
  }
99
91
 
100
- // Agents
92
+ // Agents → agents/
101
93
  const agentsSource = path.join(PLUGIN_ROOT, "agents");
102
94
  if (fs.existsSync(agentsSource)) {
103
95
  fs.mkdirSync(agentsDir, { recursive: true });
@@ -116,9 +108,9 @@ function installTools() {
116
108
  for (const tool of fs.readdirSync(toolsSource)) {
117
109
  if (tool.endsWith(".py")) {
118
110
  copyFile(path.join(toolsSource, tool), path.join(toolsDir, tool));
119
- console.log(` ${GREEN}✓${RESET} Installed tool: ${tool}`);
120
111
  }
121
112
  }
113
+ console.log(` ${GREEN}✓${RESET} Installed tools to ~/.harness-evolver/tools/`);
122
114
  }
123
115
  }
124
116
 
@@ -127,26 +119,51 @@ function installExamples() {
127
119
  const examplesSource = path.join(PLUGIN_ROOT, "examples");
128
120
  if (fs.existsSync(examplesSource)) {
129
121
  copyDir(examplesSource, examplesDir);
130
- console.log(` ${GREEN}✓${RESET} Installed examples: classifier`);
122
+ console.log(` ${GREEN}✓${RESET} Installed examples to ~/.harness-evolver/examples/`);
131
123
  }
132
124
  }
133
125
 
126
+ function cleanupBrokenPluginEntry(runtimeDir) {
127
+ // Remove the harness-evolver@local entry that doesn't work
128
+ const installedPath = path.join(HOME, runtimeDir, "plugins", "installed_plugins.json");
129
+ try {
130
+ const data = JSON.parse(fs.readFileSync(installedPath, "utf8"));
131
+ if (data.plugins && data.plugins["harness-evolver@local"]) {
132
+ delete data.plugins["harness-evolver@local"];
133
+ fs.writeFileSync(installedPath, JSON.stringify(data, null, 2) + "\n");
134
+ }
135
+ } catch {}
136
+
137
+ const settingsPath = path.join(HOME, runtimeDir, "settings.json");
138
+ try {
139
+ const data = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
140
+ if (data.enabledPlugins && data.enabledPlugins["harness-evolver@local"] !== undefined) {
141
+ delete data.enabledPlugins["harness-evolver@local"];
142
+ fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
143
+ }
144
+ } catch {}
145
+ }
146
+
134
147
  async function main() {
135
148
  console.log(LOGO);
136
149
  console.log(` ${DIM}Harness Evolver v${VERSION}${RESET}`);
137
150
  console.log(` ${DIM}Meta-Harness-style autonomous harness optimization${RESET}`);
138
151
  console.log();
139
152
 
140
- // Check python
141
153
  if (!checkPython()) {
142
154
  console.error(` ${RED}ERROR:${RESET} python3 not found in PATH. Install Python 3.8+ first.`);
143
155
  process.exit(1);
144
156
  }
145
157
  console.log(` ${GREEN}✓${RESET} python3 found`);
146
158
 
147
- // Detect runtimes
148
- const available = RUNTIMES.filter((r) => r.detected());
149
- if (available.length === 0) {
159
+ const RUNTIMES = [
160
+ { name: "Claude Code", dir: ".claude" },
161
+ { name: "Cursor", dir: ".cursor" },
162
+ { name: "Codex", dir: ".codex" },
163
+ { name: "Windsurf", dir: ".windsurf" },
164
+ ].filter(r => fs.existsSync(path.join(HOME, r.dir)));
165
+
166
+ if (RUNTIMES.length === 0) {
150
167
  console.error(`\n ${RED}ERROR:${RESET} No supported runtime detected.`);
151
168
  console.error(` Install Claude Code, Cursor, Codex, or Windsurf first.`);
152
169
  process.exit(1);
@@ -154,76 +171,61 @@ async function main() {
154
171
 
155
172
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
156
173
 
157
- // Runtime selection
158
174
  console.log(`\n ${YELLOW}Which runtime(s) would you like to install for?${RESET}\n`);
159
- available.forEach((r, i) => {
160
- console.log(` ${i + 1}) ${r.name.padEnd(14)} (~/${r.dir})`);
161
- });
162
- if (available.length > 1) {
163
- console.log(` ${available.length + 1}) All`);
175
+ RUNTIMES.forEach((r, i) => console.log(` ${i + 1}) ${r.name.padEnd(14)} (~/${r.dir})`));
176
+ if (RUNTIMES.length > 1) {
177
+ console.log(` ${RUNTIMES.length + 1}) All`);
164
178
  console.log(`\n ${DIM}Select multiple: 1,2 or 1 2${RESET}`);
165
179
  }
166
180
 
167
- const defaultChoice = "1";
168
- const runtimeAnswer = await ask(rl, `\n ${YELLOW}Choice [${defaultChoice}]:${RESET} `);
169
- const runtimeInput = (runtimeAnswer.trim() || defaultChoice);
181
+ const runtimeAnswer = await ask(rl, `\n ${YELLOW}Choice [1]:${RESET} `);
182
+ const runtimeInput = (runtimeAnswer.trim() || "1");
170
183
 
171
- let selectedRuntimes;
172
- if (runtimeInput === String(available.length + 1)) {
173
- selectedRuntimes = available;
184
+ let selected;
185
+ if (runtimeInput === String(RUNTIMES.length + 1)) {
186
+ selected = RUNTIMES;
174
187
  } else {
175
- const indices = runtimeInput.split(/[,\s]+/).map((s) => parseInt(s, 10) - 1);
176
- selectedRuntimes = indices
177
- .filter((i) => i >= 0 && i < available.length)
178
- .map((i) => available[i]);
179
- }
180
-
181
- if (selectedRuntimes.length === 0) {
182
- selectedRuntimes = [available[0]];
188
+ const indices = runtimeInput.split(/[,\s]+/).map(s => parseInt(s, 10) - 1);
189
+ selected = indices.filter(i => i >= 0 && i < RUNTIMES.length).map(i => RUNTIMES[i]);
183
190
  }
191
+ if (selected.length === 0) selected = [RUNTIMES[0]];
184
192
 
185
- // Scope selection
186
193
  console.log(`\n ${YELLOW}Where would you like to install?${RESET}\n`);
187
- console.log(` 1) Global (~/${selectedRuntimes[0].dir}) - available in all projects`);
188
- console.log(` 2) Local (./${selectedRuntimes[0].dir}) - this project only`);
194
+ console.log(` 1) Global (~/${selected[0].dir}) - available in all projects`);
195
+ console.log(` 2) Local (./${selected[0].dir}) - this project only`);
189
196
 
190
197
  const scopeAnswer = await ask(rl, `\n ${YELLOW}Choice [1]:${RESET} `);
191
198
  const scope = (scopeAnswer.trim() === "2") ? "local" : "global";
192
199
 
193
200
  console.log();
194
201
 
195
- // Install for each selected runtime
196
- for (const runtime of selectedRuntimes) {
197
- const target = scope === "local" ? `./${runtime.dir}` : `~/${runtime.dir}`;
198
- console.log(` Installing for ${BRIGHT_MAGENTA}${runtime.name}${RESET} to ${target}`);
199
- console.log();
202
+ for (const runtime of selected) {
203
+ console.log(` Installing for ${BRIGHT_MAGENTA}${runtime.name}${RESET}\n`);
204
+ cleanupBrokenPluginEntry(runtime.dir);
200
205
  installForRuntime(runtime.dir, scope);
206
+ console.log();
201
207
  }
202
208
 
203
- // Tools and examples are always global
204
209
  installTools();
205
210
  installExamples();
206
211
 
207
- // Write version file
208
212
  const versionPath = path.join(HOME, ".harness-evolver", "VERSION");
209
213
  fs.mkdirSync(path.dirname(versionPath), { recursive: true });
210
214
  fs.writeFileSync(versionPath, VERSION);
211
- console.log(` ${GREEN}✓${RESET} Wrote VERSION (${VERSION})`);
215
+ console.log(` ${GREEN}✓${RESET} VERSION ${VERSION}`);
212
216
 
213
- console.log(`\n ${GREEN}Done!${RESET} Open a project in Claude Code and run ${BRIGHT_MAGENTA}/harness-evolver:init${RESET}`);
217
+ console.log(`\n ${GREEN}Done!${RESET} Run ${BRIGHT_MAGENTA}/reload-plugins${RESET} in Claude Code, then ${BRIGHT_MAGENTA}/harness-evolver:init${RESET}`);
214
218
  console.log(`\n ${DIM}Quick start with example:${RESET}`);
215
219
  console.log(` cp -r ~/.harness-evolver/examples/classifier ./my-project`);
216
220
  console.log(` cd my-project && claude`);
217
221
  console.log(` /harness-evolver:init`);
218
222
  console.log(` /harness-evolver:evolve`);
219
-
220
- console.log(`\n ${DIM}GitHub: https://github.com/raphaelchristi/harness-evolver${RESET}`);
221
- console.log();
223
+ console.log(`\n ${DIM}GitHub: https://github.com/raphaelchristi/harness-evolver${RESET}\n`);
222
224
 
223
225
  rl.close();
224
226
  }
225
227
 
226
- main().catch((err) => {
228
+ main().catch(err => {
227
229
  console.error(` ${RED}ERROR:${RESET} ${err.message}`);
228
230
  process.exit(1);
229
231
  });
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "harness-evolver",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "Meta-Harness-style autonomous harness optimization for Claude Code",
5
- "author": "Raphael Valdetaro Christi Cordeiro",
5
+ "author": "Raphael Valdetaro",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: compare
2
+ name: harness-evolver:compare
3
3
  description: "Use when the user wants to compare two harness versions, understand what changed between iterations, see why one version scored better than another, or debug a regression."
4
4
  argument-hint: "<vA> <vB>"
5
5
  allowed-tools: [Read, Bash, Glob, Grep]
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: deploy
2
+ name: harness-evolver:deploy
3
3
  description: "Use when the user wants to use the best evolved harness in their project, promote a version to production, copy the winning harness back, or is done evolving and wants to apply the result."
4
4
  argument-hint: "[version]"
5
5
  allowed-tools: [Read, Write, Bash, Glob]
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: diagnose
2
+ name: harness-evolver:diagnose
3
3
  description: "Use when the user wants to understand why a specific harness version failed, investigate a regression, analyze trace data, or debug a low score. Also use when the user says 'why did v003 fail' or 'what went wrong'."
4
4
  argument-hint: "[version]"
5
5
  allowed-tools: [Read, Bash, Glob, Grep]
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: evolve
2
+ name: harness-evolver:evolve
3
3
  description: "Use when the user wants to run the optimization loop, improve harness performance, evolve the harness, or iterate on harness quality. Requires .harness-evolver/ to exist (run harness-evolver:init first)."
4
4
  argument-hint: "[--iterations N]"
5
5
  allowed-tools: [Read, Write, Edit, Bash, Glob, Grep, Agent]
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: init
2
+ name: harness-evolver:init
3
3
  description: "Use when the user wants to set up harness optimization in their project, optimize an LLM agent, improve a harness, or mentions harness-evolver for the first time in a project without .harness-evolver/ directory."
4
4
  argument-hint: "[directory]"
5
5
  allowed-tools: [Read, Write, Edit, Bash, Glob, Grep, Agent]
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: status
2
+ name: harness-evolver:status
3
3
  description: "Use when the user asks about evolution progress, current scores, best harness version, how many iterations ran, or whether the loop is stagnating. Also use when the user says 'status', 'progress', or 'how is it going'."
4
4
  allowed-tools: [Read, Bash]
5
5
  ---