Skip to content

Commit ab9d68e

Browse files
evolver-publishautogame-17
andcommitted
Release v1.80.0
Co-authored-by: seikiko <17@autogame.ai>
1 parent 93e44a3 commit ab9d68e

35 files changed

Lines changed: 459 additions & 31 deletions

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ Evolver integrates with major agent runtimes through `setup-hooks`. Run it once
136136
|---|---|---|
137137
| [Cursor](https://cursor.com) | `evolver setup-hooks --platform=cursor` | `~/.cursor/hooks.json` + scripts in `~/.cursor/hooks/`. Restart Cursor or open a new session. Fires on `sessionStart`, `afterFileEdit`, `stop`. |
138138
| [Claude Code](https://www.anthropic.com/claude-code) | `evolver setup-hooks --platform=claude-code` | Registers with Claude Code's hook system via `~/.claude/`. Restart the Claude Code CLI. |
139+
| [Codex](https://github.com/openai/codex) | `evolver setup-hooks --platform=codex` | `~/.codex/hooks.json` + scripts in `~/.codex/hooks/`, enables `codex_hooks` feature in `config.toml`. Restart the Codex CLI. |
140+
| [Kiro](https://kiro.dev) | `evolver setup-hooks --platform=kiro` | Three `*.kiro.hook` files + scripts in `~/.kiro/hooks/`. Auto-discovered, no restart needed. |
141+
| [opencode](https://opencode.ai) | `evolver setup-hooks --platform=opencode` | Plugin at `~/.opencode/plugins/evolver.js` + scripts in `~/.opencode/hooks/`. Restart opencode. |
139142
| [OpenClaw](https://openclaw.com) | No setup needed | OpenClaw natively interprets the `sessions_spawn(...)` stdout directives Evolver emits. Just run `evolver` from inside an OpenClaw session. |
140143

141144
## Run from Source (Contributors Only)

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1682,7 +1682,7 @@ async function main() {
16821682
- distill flags:
16831683
- --response-file=<path> (LLM response file for skill distillation)
16841684
- setup-hooks flags:
1685-
- --platform=cursor|claude-code|codex (auto-detect if omitted)
1685+
- --platform=cursor|claude-code|codex|kiro|opencode (auto-detect if omitted)
16861686
- --force (overwrite existing config)
16871687
- --uninstall (remove evolver hooks)
16881688
- asset-log flags:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@evomap/evolver",
3-
"version": "1.79.1",
3+
"version": "1.80.0",
44
"description": "A GEP-powered self-evolution engine for AI agents. Features automated log analysis and Genome Evolution Protocol (GEP) for auditable, reusable evolution assets.",
55
"main": "index.js",
66
"bin": {

src/adapters/hookAdapter.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const PLATFORMS = {
77
'claude-code': { name: 'Claude Code', configDir: '.claude', detector: '.claude' },
88
codex: { name: 'Codex', configDir: '.codex', detector: '.codex' },
99
kiro: { name: 'Kiro', configDir: '.kiro', detector: '.kiro' },
10+
opencode: { name: 'opencode', configDir: '.opencode', detector: '.opencode' },
1011
};
1112

1213
function detectPlatform(cwd) {
@@ -37,6 +38,7 @@ function loadAdapter(platformId) {
3738
case 'claude-code': return require('./claudeCode');
3839
case 'codex': return require('./codex');
3940
case 'kiro': return require('./kiro');
41+
case 'opencode': return require('./opencode');
4042
default: return null;
4143
}
4244
}
@@ -165,7 +167,7 @@ async function setupHooks({ platform, cwd, force, uninstall, evolverRoot } = {})
165167
const platformId = platform || detectPlatform(effectiveCwd);
166168

167169
if (!platformId) {
168-
console.error('[setup-hooks] Could not detect platform. Use --platform=cursor|claude-code|codex|kiro');
170+
console.error('[setup-hooks] Could not detect platform. Use --platform=cursor|claude-code|codex|kiro|opencode');
169171
return { ok: false, error: 'platform_not_detected' };
170172
}
171173

src/adapters/opencode.js

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const { copyHookScripts, removeHookScripts } = require('./hookAdapter');
4+
5+
const HOOK_SCRIPTS_DIR_NAME = 'hooks';
6+
const PLUGINS_DIR_NAME = 'plugins';
7+
const PLUGIN_FILE_NAME = 'evolver.js';
8+
const EVOLVER_MARKER = '<!-- evolver-evolution-memory -->';
9+
// First line of the plugin file. Used to (a) prove the plugin file is one we
10+
// wrote, so uninstall can safely delete it, and (b) skip overwriting if the
11+
// user replaced it with a custom plugin.
12+
const PLUGIN_HEADER = '// _evolver_managed: true (do not remove this line)';
13+
14+
function buildPluginSource(hooksDirAbsolute) {
15+
// The plugin shells out to the same three Node scripts that every other
16+
// evolver adapter uses, so the runtime behavior stays consistent across
17+
// hosts. opencode loads .js files synchronously at startup and invokes
18+
// the default-exported async factory with a context object.
19+
return `${PLUGIN_HEADER}
20+
// Auto-generated by \`evolver setup-hooks --platform=opencode\`.
21+
// See https://opencode.ai/docs/plugins/ for the plugin API.
22+
//
23+
// This plugin wires opencode events to the three evolver hook scripts:
24+
// session.created -> evolver-session-start.js (inject memory)
25+
// tool.execute.after -> evolver-signal-detect.js (detect signals on writes/edits)
26+
// session.idle -> evolver-session-end.js (record outcome)
27+
//
28+
// Each script is a stdin/stdout JSON filter -- nothing else is shared with
29+
// opencode, so removing this file fully detaches evolver from opencode.
30+
31+
const { spawnSync } = require('node:child_process');
32+
const path = require('node:path');
33+
34+
const HOOKS_DIR = ${JSON.stringify(hooksDirAbsolute)};
35+
36+
function runHook(scriptName, payload, timeoutMs) {
37+
try {
38+
const result = spawnSync('node', [path.join(HOOKS_DIR, scriptName)], {
39+
input: JSON.stringify(payload || {}),
40+
encoding: 'utf8',
41+
timeout: timeoutMs,
42+
});
43+
if (!result || !result.stdout) return {};
44+
try { return JSON.parse(result.stdout); } catch { return {}; }
45+
} catch {
46+
return {};
47+
}
48+
}
49+
50+
const Evolver = async () => ({
51+
event: async ({ event }) => {
52+
if (!event || typeof event.type !== 'string') return;
53+
if (event.type === 'session.created') {
54+
runHook('evolver-session-start.js', {
55+
session_id: event.properties && event.properties.info && event.properties.info.id,
56+
}, 3000);
57+
return;
58+
}
59+
if (event.type === 'session.idle') {
60+
runHook('evolver-session-end.js', {
61+
session_id: event.properties && event.properties.sessionID,
62+
}, 8000);
63+
}
64+
},
65+
'tool.execute.after': async (input, output) => {
66+
if (!input || typeof input.tool !== 'string') return;
67+
if (input.tool !== 'write' && input.tool !== 'edit') return;
68+
runHook('evolver-signal-detect.js', {
69+
tool_input: (output && output.args) || {},
70+
tool_response: (output && output.output) || (output && output.result) || {},
71+
}, 2000);
72+
},
73+
});
74+
75+
module.exports = { Evolver };
76+
module.exports.default = Evolver;
77+
`;
78+
}
79+
80+
function buildAgentsMdSection() {
81+
return `${EVOLVER_MARKER}
82+
## Evolution Memory (Evolver)
83+
84+
This project uses evolver for self-evolution. Hooks automatically:
85+
1. Inject recent evolution memory at session start
86+
2. Detect evolution signals during file edits
87+
3. Record outcomes at session end
88+
89+
For substantive tasks, call \`gep_recall\` before work and \`gep_record_outcome\` after.
90+
Signals: log_error, perf_bottleneck, user_feature_request, capability_gap, deployment_issue, test_failure.`;
91+
}
92+
93+
function appendSectionToFile(filePath, marker, content) {
94+
let existing = '';
95+
try { existing = fs.readFileSync(filePath, 'utf8'); } catch { /* new file */ }
96+
if (existing.includes(marker)) return false;
97+
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n\n' : '\n';
98+
fs.writeFileSync(filePath, existing + separator + content + '\n', 'utf8');
99+
return true;
100+
}
101+
102+
function isEvolverManagedPluginFile(filePath) {
103+
try {
104+
if (!fs.existsSync(filePath)) return false;
105+
const raw = fs.readFileSync(filePath, 'utf8');
106+
return raw.includes('_evolver_managed: true');
107+
} catch {
108+
return false;
109+
}
110+
}
111+
112+
function writePluginFile(pluginsDir, source) {
113+
fs.mkdirSync(pluginsDir, { recursive: true });
114+
const dest = path.join(pluginsDir, PLUGIN_FILE_NAME);
115+
const tmp = dest + '.tmp';
116+
fs.writeFileSync(tmp, source, 'utf8');
117+
fs.renameSync(tmp, dest);
118+
return dest;
119+
}
120+
121+
function install({ configRoot, evolverRoot, force }) {
122+
const opencodeDir = path.join(configRoot, '.opencode');
123+
const hooksDir = path.join(opencodeDir, HOOK_SCRIPTS_DIR_NAME);
124+
const pluginsDir = path.join(opencodeDir, PLUGINS_DIR_NAME);
125+
const pluginPath = path.join(pluginsDir, PLUGIN_FILE_NAME);
126+
const agentsMdPath = path.join(configRoot, 'AGENTS.md');
127+
128+
if (!force && isEvolverManagedPluginFile(pluginPath)) {
129+
console.log('[opencode] Evolver plugin already installed. Use --force to overwrite.');
130+
return { ok: true, skipped: true };
131+
}
132+
133+
fs.mkdirSync(opencodeDir, { recursive: true });
134+
135+
const written = writePluginFile(pluginsDir, buildPluginSource(hooksDir));
136+
console.log('[opencode] Wrote ' + written);
137+
138+
const copied = copyHookScripts(hooksDir, path.join(evolverRoot, 'src', 'adapters'));
139+
console.log('[opencode] Copied ' + copied.length + ' hook scripts to ' + hooksDir);
140+
141+
const injected = appendSectionToFile(agentsMdPath, EVOLVER_MARKER, buildAgentsMdSection());
142+
if (injected) {
143+
console.log('[opencode] Injected evolution section into ' + agentsMdPath);
144+
}
145+
146+
console.log('[opencode] Installation complete.');
147+
console.log('[opencode] opencode auto-loads plugins from .opencode/plugins/ -- restart opencode to activate.');
148+
149+
return {
150+
ok: true,
151+
platform: 'opencode',
152+
files: [written, agentsMdPath, ...copied],
153+
};
154+
}
155+
156+
function uninstall({ configRoot }) {
157+
const opencodeDir = path.join(configRoot, '.opencode');
158+
const hooksDir = path.join(opencodeDir, HOOK_SCRIPTS_DIR_NAME);
159+
const pluginsDir = path.join(opencodeDir, PLUGINS_DIR_NAME);
160+
const pluginPath = path.join(pluginsDir, PLUGIN_FILE_NAME);
161+
const agentsMdPath = path.join(configRoot, 'AGENTS.md');
162+
163+
let changed = false;
164+
165+
if (isEvolverManagedPluginFile(pluginPath)) {
166+
try { fs.unlinkSync(pluginPath); changed = true; } catch { /* ignore */ }
167+
}
168+
169+
const scripts = removeHookScripts(hooksDir);
170+
if (scripts > 0) changed = true;
171+
172+
try {
173+
if (fs.existsSync(agentsMdPath)) {
174+
let content = fs.readFileSync(agentsMdPath, 'utf8');
175+
if (content.includes(EVOLVER_MARKER)) {
176+
const idx = content.indexOf(EVOLVER_MARKER);
177+
const nextSection = content.indexOf('\n## ', idx + EVOLVER_MARKER.length);
178+
const endIdx = nextSection !== -1 ? nextSection : content.length;
179+
content = content.slice(0, idx).trimEnd() + (nextSection !== -1 ? content.slice(endIdx) : '');
180+
fs.writeFileSync(agentsMdPath, content.trimEnd() + '\n', 'utf8');
181+
changed = true;
182+
}
183+
}
184+
} catch { /* ignore */ }
185+
186+
console.log(changed
187+
? '[opencode] Uninstalled evolver plugin and hooks.'
188+
: '[opencode] No evolver plugin found to uninstall.');
189+
190+
return { ok: true, removed: changed };
191+
}
192+
193+
module.exports = {
194+
install,
195+
uninstall,
196+
buildPluginSource,
197+
isEvolverManagedPluginFile,
198+
PLUGIN_FILE_NAME,
199+
};

src/evolve.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/gep/.integrity

0 Bytes
Binary file not shown.

src/gep/a2aProtocol.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/gep/candidateEval.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/gep/candidates.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)