harness-evolver 0.6.0 → 0.7.1

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 +64 -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,52 @@ 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/ as flat .md files
82
+ // Claude Code expects commands/name.md, not commands/name/SKILL.md
83
+ const skillsSource = path.join(PLUGIN_ROOT, "skills");
84
+ if (fs.existsSync(skillsSource)) {
85
+ fs.mkdirSync(commandsDir, { recursive: true });
86
+ for (const skill of fs.readdirSync(skillsSource, { withFileTypes: true })) {
87
+ if (skill.isDirectory()) {
88
+ const skillMd = path.join(skillsSource, skill.name, "SKILL.md");
89
+ if (fs.existsSync(skillMd)) {
90
+ fs.copyFileSync(skillMd, path.join(commandsDir, skill.name + ".md"));
91
+ console.log(` ${GREEN}✓${RESET} Installed command: harness-evolver:${skill.name}`);
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ // Agents agents/
98
+ const agentsSource = path.join(PLUGIN_ROOT, "agents");
99
+ if (fs.existsSync(agentsSource)) {
100
+ fs.mkdirSync(agentsDir, { recursive: true });
101
+ for (const agent of fs.readdirSync(agentsSource)) {
102
+ copyFile(path.join(agentsSource, agent), path.join(agentsDir, agent));
103
+ console.log(` ${GREEN}✓${RESET} Installed agent: ${agent}`);
104
+ }
105
+ }
130
106
  }
131
107
 
132
- function installToolsGlobal() {
108
+ function installTools() {
133
109
  const toolsDir = path.join(HOME, ".harness-evolver", "tools");
134
110
  const toolsSource = path.join(PLUGIN_ROOT, "tools");
135
111
  if (fs.existsSync(toolsSource)) {
136
112
  fs.mkdirSync(toolsDir, { recursive: true });
137
113
  for (const tool of fs.readdirSync(toolsSource)) {
138
114
  if (tool.endsWith(".py")) {
139
- fs.copyFileSync(path.join(toolsSource, tool), path.join(toolsDir, tool));
115
+ copyFile(path.join(toolsSource, tool), path.join(toolsDir, tool));
140
116
  }
141
117
  }
142
- console.log(` ${GREEN}✓${RESET} Tools copied to ~/.harness-evolver/tools/`);
118
+ console.log(` ${GREEN}✓${RESET} Installed tools to ~/.harness-evolver/tools/`);
143
119
  }
144
120
  }
145
121
 
@@ -148,10 +124,31 @@ function installExamples() {
148
124
  const examplesSource = path.join(PLUGIN_ROOT, "examples");
149
125
  if (fs.existsSync(examplesSource)) {
150
126
  copyDir(examplesSource, examplesDir);
151
- console.log(` ${GREEN}✓${RESET} Examples copied to ~/.harness-evolver/examples/`);
127
+ console.log(` ${GREEN}✓${RESET} Installed examples to ~/.harness-evolver/examples/`);
152
128
  }
153
129
  }
154
130
 
131
+ function cleanupBrokenPluginEntry(runtimeDir) {
132
+ // Remove the harness-evolver@local entry that doesn't work
133
+ const installedPath = path.join(HOME, runtimeDir, "plugins", "installed_plugins.json");
134
+ try {
135
+ const data = JSON.parse(fs.readFileSync(installedPath, "utf8"));
136
+ if (data.plugins && data.plugins["harness-evolver@local"]) {
137
+ delete data.plugins["harness-evolver@local"];
138
+ fs.writeFileSync(installedPath, JSON.stringify(data, null, 2) + "\n");
139
+ }
140
+ } catch {}
141
+
142
+ const settingsPath = path.join(HOME, runtimeDir, "settings.json");
143
+ try {
144
+ const data = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
145
+ if (data.enabledPlugins && data.enabledPlugins["harness-evolver@local"] !== undefined) {
146
+ delete data.enabledPlugins["harness-evolver@local"];
147
+ fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
148
+ }
149
+ } catch {}
150
+ }
151
+
155
152
  async function main() {
156
153
  console.log(LOGO);
157
154
  console.log(` ${DIM}Harness Evolver v${VERSION}${RESET}`);
@@ -164,7 +161,6 @@ async function main() {
164
161
  }
165
162
  console.log(` ${GREEN}✓${RESET} python3 found`);
166
163
 
167
- // Detect runtimes
168
164
  const RUNTIMES = [
169
165
  { name: "Claude Code", dir: ".claude" },
170
166
  { name: "Cursor", dir: ".cursor" },
@@ -180,7 +176,6 @@ async function main() {
180
176
 
181
177
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
182
178
 
183
- // Runtime selection
184
179
  console.log(`\n ${YELLOW}Which runtime(s) would you like to install for?${RESET}\n`);
185
180
  RUNTIMES.forEach((r, i) => console.log(` ${i + 1}) ${r.name.padEnd(14)} (~/${r.dir})`));
186
181
  if (RUNTIMES.length > 1) {
@@ -200,7 +195,6 @@ async function main() {
200
195
  }
201
196
  if (selected.length === 0) selected = [RUNTIMES[0]];
202
197
 
203
- // Scope selection
204
198
  console.log(`\n ${YELLOW}Where would you like to install?${RESET}\n`);
205
199
  console.log(` 1) Global (~/${selected[0].dir}) - available in all projects`);
206
200
  console.log(` 2) Local (./${selected[0].dir}) - this project only`);
@@ -210,23 +204,22 @@ async function main() {
210
204
 
211
205
  console.log();
212
206
 
213
- // Install
214
207
  for (const runtime of selected) {
215
208
  console.log(` Installing for ${BRIGHT_MAGENTA}${runtime.name}${RESET}\n`);
216
- installPlugin(runtime.dir, scope);
209
+ cleanupBrokenPluginEntry(runtime.dir);
210
+ installForRuntime(runtime.dir, scope);
217
211
  console.log();
218
212
  }
219
213
 
220
- installToolsGlobal();
214
+ installTools();
221
215
  installExamples();
222
216
 
223
- // Version marker
224
217
  const versionPath = path.join(HOME, ".harness-evolver", "VERSION");
225
218
  fs.mkdirSync(path.dirname(versionPath), { recursive: true });
226
219
  fs.writeFileSync(versionPath, VERSION);
227
220
  console.log(` ${GREEN}✓${RESET} VERSION ${VERSION}`);
228
221
 
229
- console.log(`\n ${GREEN}Done!${RESET} Open a project in Claude Code and run ${BRIGHT_MAGENTA}/harness-evolver:init${RESET}`);
222
+ console.log(`\n ${GREEN}Done!${RESET} Run ${BRIGHT_MAGENTA}/reload-plugins${RESET} in Claude Code, then ${BRIGHT_MAGENTA}/harness-evolver:init${RESET}`);
230
223
  console.log(`\n ${DIM}Quick start with example:${RESET}`);
231
224
  console.log(` cp -r ~/.harness-evolver/examples/classifier ./my-project`);
232
225
  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.1",
4
4
  "description": "Meta-Harness-style autonomous harness optimization for Claude Code",
5
5
  "author": "Raphael Valdetaro",
6
6
  "license": "MIT",