@evomap/evolver 1.79.1 → 1.80.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/README.md CHANGED
@@ -136,6 +136,9 @@ Evolver integrates with major agent runtimes through `setup-hooks`. Run it once
136
136
  |---|---|---|
137
137
  | [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`. |
138
138
  | [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. |
139
142
  | [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. |
140
143
 
141
144
  ## Run from Source (Contributors Only)
@@ -1,2 +1 @@
1
- {"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-05-06T01:46:54.543Z","signals":["memory_missing","user_missing","session_logs_missing"],"tags":["memory_missing","user_missing","session_logs_missing","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
2
- {"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-05-06T01:47:23.332Z","signals":["memory_missing","user_missing","session_logs_missing"],"tags":["memory_missing","user_missing","session_logs_missing","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
1
+ {"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-05-07T04:15:16.667Z","signals":["memory_missing","user_missing","session_logs_missing"],"tags":["memory_missing","user_missing","session_logs_missing","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
package/index.js CHANGED
@@ -1682,7 +1682,7 @@ async function main() {
1682
1682
  - distill flags:
1683
1683
  - --response-file=<path> (LLM response file for skill distillation)
1684
1684
  - 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)
1686
1686
  - --force (overwrite existing config)
1687
1687
  - --uninstall (remove evolver hooks)
1688
1688
  - asset-log flags:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver",
3
- "version": "1.79.1",
3
+ "version": "1.80.0",
4
4
  "description": "A GEP-powered self-evolution engine for AI agents. Features automated log analysis and Genome Evolution Protocol (GEP) for auditable, reusable evolution assets.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -7,6 +7,7 @@ const PLATFORMS = {
7
7
  'claude-code': { name: 'Claude Code', configDir: '.claude', detector: '.claude' },
8
8
  codex: { name: 'Codex', configDir: '.codex', detector: '.codex' },
9
9
  kiro: { name: 'Kiro', configDir: '.kiro', detector: '.kiro' },
10
+ opencode: { name: 'opencode', configDir: '.opencode', detector: '.opencode' },
10
11
  };
11
12
 
12
13
  function detectPlatform(cwd) {
@@ -37,6 +38,7 @@ function loadAdapter(platformId) {
37
38
  case 'claude-code': return require('./claudeCode');
38
39
  case 'codex': return require('./codex');
39
40
  case 'kiro': return require('./kiro');
41
+ case 'opencode': return require('./opencode');
40
42
  default: return null;
41
43
  }
42
44
  }
@@ -165,7 +167,7 @@ async function setupHooks({ platform, cwd, force, uninstall, evolverRoot } = {})
165
167
  const platformId = platform || detectPlatform(effectiveCwd);
166
168
 
167
169
  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');
169
171
  return { ok: false, error: 'platform_not_detected' };
170
172
  }
171
173
 
@@ -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
+ };