harness-evolver 0.6.0 → 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.
Files changed (2) hide show
  1. package/bin/install.js +59 -71
  2. package/package.json +1 -1
package/bin/install.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Harness Evolver installer.
4
- * Copies plugin to Claude Code plugin cache and registers it.
4
+ * Copies skills/agents/tools directly to runtime directories (GSD pattern).
5
5
  *
6
6
  * Usage: npx harness-evolver@latest
7
7
  */
@@ -49,7 +49,6 @@ function copyDir(src, dest) {
49
49
  const srcPath = path.join(src, entry.name);
50
50
  const destPath = path.join(dest, entry.name);
51
51
  if (entry.isDirectory()) {
52
- if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "__pycache__" || entry.name === "tests" || entry.name === "docs") continue;
53
52
  copyDir(srcPath, destPath);
54
53
  } else {
55
54
  fs.copyFileSync(srcPath, destPath);
@@ -57,6 +56,11 @@ function copyDir(src, dest) {
57
56
  }
58
57
  }
59
58
 
59
+ function copyFile(src, dest) {
60
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
61
+ fs.copyFileSync(src, dest);
62
+ }
63
+
60
64
  function checkPython() {
61
65
  try {
62
66
  execSync("python3 --version", { stdio: "pipe" });
@@ -66,80 +70,47 @@ function checkPython() {
66
70
  }
67
71
  }
68
72
 
69
- function readJSON(filepath) {
70
- try {
71
- return JSON.parse(fs.readFileSync(filepath, "utf8"));
72
- } catch {
73
- return null;
74
- }
75
- }
76
-
77
- function writeJSON(filepath, data) {
78
- fs.mkdirSync(path.dirname(filepath), { recursive: true });
79
- fs.writeFileSync(filepath, JSON.stringify(data, null, 2) + "\n");
80
- }
81
-
82
- function installPlugin(runtimeDir, scope) {
73
+ function installForRuntime(runtimeDir, scope) {
83
74
  const baseDir = scope === "local"
84
75
  ? path.join(process.cwd(), runtimeDir)
85
76
  : path.join(HOME, runtimeDir);
86
77
 
87
- // 1. Copy plugin to cache
88
- const cacheDir = path.join(baseDir, "plugins", "cache", "local", "harness-evolver", VERSION);
89
- console.log(` Copying plugin to ${scope === "local" ? "." : "~"}/${runtimeDir}/plugins/cache/...`);
90
- copyDir(PLUGIN_ROOT, cacheDir);
91
- console.log(` ${GREEN}✓${RESET} Plugin files copied`);
92
-
93
- // 2. Register in installed_plugins.json
94
- const installedPath = path.join(baseDir, "plugins", "installed_plugins.json");
95
- let installed = readJSON(installedPath) || { version: 2, plugins: {} };
96
- if (!installed.plugins) installed.plugins = {};
97
-
98
- installed.plugins["harness-evolver@local"] = [{
99
- scope: "user",
100
- installPath: cacheDir,
101
- version: VERSION,
102
- installedAt: new Date().toISOString(),
103
- lastUpdated: new Date().toISOString(),
104
- }];
105
- writeJSON(installedPath, installed);
106
- console.log(` ${GREEN}✓${RESET} Registered in installed_plugins.json`);
107
-
108
- // 3. Enable in settings.json
109
- const settingsPath = path.join(baseDir, "settings.json");
110
- let settings = readJSON(settingsPath) || {};
111
- if (!settings.enabledPlugins) settings.enabledPlugins = {};
112
- settings.enabledPlugins["harness-evolver@local"] = true;
113
- writeJSON(settingsPath, settings);
114
- console.log(` ${GREEN}✓${RESET} Enabled in settings.json`);
115
-
116
- // Count installed items
117
- const skillCount = fs.existsSync(path.join(cacheDir, "skills"))
118
- ? fs.readdirSync(path.join(cacheDir, "skills")).filter(f =>
119
- fs.statSync(path.join(cacheDir, "skills", f)).isDirectory()
120
- ).length
121
- : 0;
122
- const agentCount = fs.existsSync(path.join(cacheDir, "agents"))
123
- ? fs.readdirSync(path.join(cacheDir, "agents")).length
124
- : 0;
125
- const toolCount = fs.existsSync(path.join(cacheDir, "tools"))
126
- ? fs.readdirSync(path.join(cacheDir, "tools")).filter(f => f.endsWith(".py")).length
127
- : 0;
128
-
129
- console.log(` ${GREEN}✓${RESET} ${skillCount} skills, ${agentCount} agent, ${toolCount} tools`);
78
+ const commandsDir = path.join(baseDir, "commands", "harness-evolver");
79
+ const agentsDir = path.join(baseDir, "agents");
80
+
81
+ // Skills → commands/harness-evolver/
82
+ const skillsSource = path.join(PLUGIN_ROOT, "skills");
83
+ if (fs.existsSync(skillsSource)) {
84
+ for (const skill of fs.readdirSync(skillsSource, { withFileTypes: true })) {
85
+ if (skill.isDirectory()) {
86
+ copyDir(path.join(skillsSource, skill.name), path.join(commandsDir, skill.name));
87
+ console.log(` ${GREEN}✓${RESET} Installed command: harness-evolver:${skill.name}`);
88
+ }
89
+ }
90
+ }
91
+
92
+ // Agents → agents/
93
+ const agentsSource = path.join(PLUGIN_ROOT, "agents");
94
+ if (fs.existsSync(agentsSource)) {
95
+ fs.mkdirSync(agentsDir, { recursive: true });
96
+ for (const agent of fs.readdirSync(agentsSource)) {
97
+ copyFile(path.join(agentsSource, agent), path.join(agentsDir, agent));
98
+ console.log(` ${GREEN}✓${RESET} Installed agent: ${agent}`);
99
+ }
100
+ }
130
101
  }
131
102
 
132
- function installToolsGlobal() {
103
+ function installTools() {
133
104
  const toolsDir = path.join(HOME, ".harness-evolver", "tools");
134
105
  const toolsSource = path.join(PLUGIN_ROOT, "tools");
135
106
  if (fs.existsSync(toolsSource)) {
136
107
  fs.mkdirSync(toolsDir, { recursive: true });
137
108
  for (const tool of fs.readdirSync(toolsSource)) {
138
109
  if (tool.endsWith(".py")) {
139
- fs.copyFileSync(path.join(toolsSource, tool), path.join(toolsDir, tool));
110
+ copyFile(path.join(toolsSource, tool), path.join(toolsDir, tool));
140
111
  }
141
112
  }
142
- console.log(` ${GREEN}✓${RESET} Tools copied to ~/.harness-evolver/tools/`);
113
+ console.log(` ${GREEN}✓${RESET} Installed tools to ~/.harness-evolver/tools/`);
143
114
  }
144
115
  }
145
116
 
@@ -148,10 +119,31 @@ function installExamples() {
148
119
  const examplesSource = path.join(PLUGIN_ROOT, "examples");
149
120
  if (fs.existsSync(examplesSource)) {
150
121
  copyDir(examplesSource, examplesDir);
151
- console.log(` ${GREEN}✓${RESET} Examples copied to ~/.harness-evolver/examples/`);
122
+ console.log(` ${GREEN}✓${RESET} Installed examples to ~/.harness-evolver/examples/`);
152
123
  }
153
124
  }
154
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
+
155
147
  async function main() {
156
148
  console.log(LOGO);
157
149
  console.log(` ${DIM}Harness Evolver v${VERSION}${RESET}`);
@@ -164,7 +156,6 @@ async function main() {
164
156
  }
165
157
  console.log(` ${GREEN}✓${RESET} python3 found`);
166
158
 
167
- // Detect runtimes
168
159
  const RUNTIMES = [
169
160
  { name: "Claude Code", dir: ".claude" },
170
161
  { name: "Cursor", dir: ".cursor" },
@@ -180,7 +171,6 @@ async function main() {
180
171
 
181
172
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
182
173
 
183
- // Runtime selection
184
174
  console.log(`\n ${YELLOW}Which runtime(s) would you like to install for?${RESET}\n`);
185
175
  RUNTIMES.forEach((r, i) => console.log(` ${i + 1}) ${r.name.padEnd(14)} (~/${r.dir})`));
186
176
  if (RUNTIMES.length > 1) {
@@ -200,7 +190,6 @@ async function main() {
200
190
  }
201
191
  if (selected.length === 0) selected = [RUNTIMES[0]];
202
192
 
203
- // Scope selection
204
193
  console.log(`\n ${YELLOW}Where would you like to install?${RESET}\n`);
205
194
  console.log(` 1) Global (~/${selected[0].dir}) - available in all projects`);
206
195
  console.log(` 2) Local (./${selected[0].dir}) - this project only`);
@@ -210,23 +199,22 @@ async function main() {
210
199
 
211
200
  console.log();
212
201
 
213
- // Install
214
202
  for (const runtime of selected) {
215
203
  console.log(` Installing for ${BRIGHT_MAGENTA}${runtime.name}${RESET}\n`);
216
- installPlugin(runtime.dir, scope);
204
+ cleanupBrokenPluginEntry(runtime.dir);
205
+ installForRuntime(runtime.dir, scope);
217
206
  console.log();
218
207
  }
219
208
 
220
- installToolsGlobal();
209
+ installTools();
221
210
  installExamples();
222
211
 
223
- // Version marker
224
212
  const versionPath = path.join(HOME, ".harness-evolver", "VERSION");
225
213
  fs.mkdirSync(path.dirname(versionPath), { recursive: true });
226
214
  fs.writeFileSync(versionPath, VERSION);
227
215
  console.log(` ${GREEN}✓${RESET} VERSION ${VERSION}`);
228
216
 
229
- 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}`);
230
218
  console.log(`\n ${DIM}Quick start with example:${RESET}`);
231
219
  console.log(` cp -r ~/.harness-evolver/examples/classifier ./my-project`);
232
220
  console.log(` cd my-project && claude`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-evolver",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Meta-Harness-style autonomous harness optimization for Claude Code",
5
5
  "author": "Raphael Valdetaro",
6
6
  "license": "MIT",