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 +1 -1
- package/bin/install.js +56 -54
- package/package.json +2 -2
- package/skills/compare/SKILL.md +1 -1
- package/skills/deploy/SKILL.md +1 -1
- package/skills/diagnose/SKILL.md +1 -1
- package/skills/evolve/SKILL.md +1 -1
- package/skills/init/SKILL.md +1 -1
- package/skills/status/SKILL.md +1 -1
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Raphael Valdetaro
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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
|
|
168
|
-
const
|
|
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
|
|
172
|
-
if (runtimeInput === String(
|
|
173
|
-
|
|
184
|
+
let selected;
|
|
185
|
+
if (runtimeInput === String(RUNTIMES.length + 1)) {
|
|
186
|
+
selected = RUNTIMES;
|
|
174
187
|
} else {
|
|
175
|
-
const indices = runtimeInput.split(/[,\s]+/).map(
|
|
176
|
-
|
|
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 (~/${
|
|
188
|
-
console.log(` 2) Local (./${
|
|
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
|
-
|
|
196
|
-
for
|
|
197
|
-
|
|
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}
|
|
215
|
+
console.log(` ${GREEN}✓${RESET} VERSION ${VERSION}`);
|
|
212
216
|
|
|
213
|
-
console.log(`\n ${GREEN}Done!${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(
|
|
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.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Meta-Harness-style autonomous harness optimization for Claude Code",
|
|
5
|
-
"author": "Raphael Valdetaro
|
|
5
|
+
"author": "Raphael Valdetaro",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
package/skills/compare/SKILL.md
CHANGED
|
@@ -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]
|
package/skills/deploy/SKILL.md
CHANGED
|
@@ -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]
|
package/skills/diagnose/SKILL.md
CHANGED
|
@@ -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]
|
package/skills/evolve/SKILL.md
CHANGED
|
@@ -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]
|
package/skills/init/SKILL.md
CHANGED
|
@@ -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]
|
package/skills/status/SKILL.md
CHANGED
|
@@ -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
|
---
|