@evomap/evolver 1.89.20 → 2.0.0-beta.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 +27 -564
- package/bin/evolver-llm-proxy.js +3 -0
- package/bin/evolver-mcp.js +2 -0
- package/bin/evolver-proxy.js +3 -0
- package/bin/evolver.js +4 -0
- package/index.js +1 -3586
- package/package.json +32 -62
- package/CONTRIBUTING.md +0 -19
- package/LICENSE +0 -641
- package/README.ja-JP.md +0 -521
- package/README.ko-KR.md +0 -520
- package/README.zh-CN.md +0 -531
- package/SKILL.md +0 -365
- package/assets/cover.png +0 -0
- package/assets/gep/genes.seed.json +0 -496
- package/conformance/savings-core/constants.json +0 -30
- package/conformance/savings-core/golden-vectors.json +0 -333
- package/scripts/a2a_export.js +0 -63
- package/scripts/a2a_ingest.js +0 -79
- package/scripts/a2a_promote.js +0 -118
- package/scripts/analyze_by_skill.js +0 -121
- package/scripts/build_binaries.js +0 -479
- package/scripts/check-changelog.js +0 -166
- package/scripts/extract_log.js +0 -85
- package/scripts/generate_history.js +0 -75
- package/scripts/gep_append_event.js +0 -96
- package/scripts/gep_personality_report.js +0 -234
- package/scripts/human_report.js +0 -147
- package/scripts/recall-verify-report.js +0 -234
- package/scripts/recover_loop.js +0 -61
- package/scripts/refresh_stars_badge.js +0 -168
- package/scripts/seed-merchants.js +0 -91
- package/scripts/skill2recipes.js +0 -118
- package/scripts/suggest_version.js +0 -89
- package/scripts/validate-modules.js +0 -38
- package/scripts/validate-suite.js +0 -78
- package/skills/_meta/SKILL.md +0 -41
- package/skills/index.json +0 -14
- package/src/adapters/claudeCode.js +0 -194
- package/src/adapters/codex.js +0 -216
- package/src/adapters/cursor.js +0 -91
- package/src/adapters/hookAdapter.js +0 -469
- package/src/adapters/kiro.js +0 -195
- package/src/adapters/opencode.js +0 -326
- package/src/adapters/scripts/_lockPaths.js +0 -74
- package/src/adapters/scripts/_memoryFiltering.js +0 -35
- package/src/adapters/scripts/_runtimePaths.js +0 -440
- package/src/adapters/scripts/evolver-session-end.js +0 -321
- package/src/adapters/scripts/evolver-session-start.js +0 -587
- package/src/adapters/scripts/evolver-signal-detect.js +0 -98
- package/src/adapters/scripts/evolver-task-recall.js +0 -173
- package/src/atp/atpExecute.js +0 -283
- package/src/atp/atpTaskPickup.js +0 -233
- package/src/atp/autoBuyer.js +0 -382
- package/src/atp/autoDeliver.js +0 -215
- package/src/atp/cli.js +0 -354
- package/src/atp/cliAutobuyPrompt.js +0 -154
- package/src/atp/consumerAgent.js +0 -157
- package/src/atp/defaultHandler.js +0 -69
- package/src/atp/heartbeatSignalsHandler.js +0 -254
- package/src/atp/hubClient.js +0 -317
- package/src/atp/index.js +0 -38
- package/src/atp/merchantAgent.js +0 -118
- package/src/atp/protocol.js +0 -41
- package/src/atp/questionComposer.js +0 -133
- package/src/atp/serviceHelper.js +0 -92
- package/src/canary.js +0 -13
- package/src/config.js +0 -351
- package/src/evolve/guards.js +0 -1
- package/src/evolve/pipeline/collect.js +0 -1
- package/src/evolve/pipeline/dispatch.js +0 -1
- package/src/evolve/pipeline/enrich.js +0 -1
- package/src/evolve/pipeline/hub.js +0 -1
- package/src/evolve/pipeline/select.js +0 -1
- package/src/evolve/pipeline/signals.js +0 -1
- package/src/evolve/utils.js +0 -1
- package/src/evolve.js +0 -1
- package/src/experiment/agentRunner.js +0 -229
- package/src/experiment/cli.js +0 -159
- package/src/experiment/comparison.js +0 -233
- package/src/experiment/metrics.js +0 -75
- package/src/forceUpdate.js +0 -991
- package/src/gep/a2a.js +0 -173
- package/src/gep/a2aProtocol.js +0 -1
- package/src/gep/analyzer.js +0 -35
- package/src/gep/antiAbuseTelemetry.js +0 -1
- package/src/gep/assetCallLog.js +0 -197
- package/src/gep/assetStore.js +0 -723
- package/src/gep/assets.js +0 -36
- package/src/gep/autoDistillConv.js +0 -1
- package/src/gep/autoDistillLlm.js +0 -1
- package/src/gep/bridge.js +0 -138
- package/src/gep/candidateEval.js +0 -1
- package/src/gep/candidates.js +0 -1
- package/src/gep/claimNudge.js +0 -121
- package/src/gep/cliContracts.js +0 -1190
- package/src/gep/contentHash.js +0 -1
- package/src/gep/conversationDistiller.js +0 -1
- package/src/gep/conversationSniffer.js +0 -1
- package/src/gep/crypto.js +0 -1
- package/src/gep/curriculum.js +0 -1
- package/src/gep/deviceId.js +0 -1
- package/src/gep/directoryClient.js +0 -115
- package/src/gep/envFingerprint.js +0 -1
- package/src/gep/epigenetics.js +0 -1
- package/src/gep/execBridge.js +0 -1
- package/src/gep/executionTrace.js +0 -291
- package/src/gep/explore.js +0 -1
- package/src/gep/featureFlags.js +0 -121
- package/src/gep/gitOps.js +0 -265
- package/src/gep/hash.js +0 -1
- package/src/gep/hostErrorClassifier.js +0 -34
- package/src/gep/hubFetch.js +0 -1
- package/src/gep/hubReview.js +0 -1
- package/src/gep/hubSearch.js +0 -1
- package/src/gep/hubVerify.js +0 -1
- package/src/gep/idleScheduler.js +0 -400
- package/src/gep/issueReporter.js +0 -416
- package/src/gep/learningSignals.js +0 -1
- package/src/gep/llmReview.js +0 -92
- package/src/gep/localStateAwareness.js +0 -243
- package/src/gep/mailboxTransport.js +0 -119
- package/src/gep/memoryGraph.js +0 -1
- package/src/gep/memoryGraphAdapter.js +0 -1
- package/src/gep/mutation.js +0 -1
- package/src/gep/narrativeMemory.js +0 -1
- package/src/gep/oauthLogin.js +0 -181
- package/src/gep/openPRRegistry.js +0 -1
- package/src/gep/paths.js +0 -522
- package/src/gep/personality.js +0 -1
- package/src/gep/policyCheck.js +0 -1
- package/src/gep/portable.js +0 -103
- package/src/gep/privacyClient.js +0 -235
- package/src/gep/prompt.js +0 -1
- package/src/gep/questionGenerator.js +0 -518
- package/src/gep/recallInject.js +0 -1
- package/src/gep/recallVerifier.js +0 -1
- package/src/gep/reflection.js +0 -1
- package/src/gep/sanitize.js +0 -264
- package/src/gep/savingsCore.js +0 -1
- package/src/gep/schemas/capsule.js +0 -170
- package/src/gep/schemas/gene.js +0 -154
- package/src/gep/schemas/index.js +0 -8
- package/src/gep/schemas/protocol.js +0 -51
- package/src/gep/schemas/task.js +0 -74
- package/src/gep/selector.js +0 -1
- package/src/gep/selfPR.js +0 -469
- package/src/gep/signals.js +0 -776
- package/src/gep/skill2gep.js +0 -1056
- package/src/gep/skill2gepAudit.js +0 -303
- package/src/gep/skill2recipes.js +0 -511
- package/src/gep/skillDistiller.js +0 -1
- package/src/gep/skillPublisher.js +0 -358
- package/src/gep/solidify.js +0 -1
- package/src/gep/strategy.js +0 -1
- package/src/gep/taskReceiver.js +0 -575
- package/src/gep/tokenSavings.js +0 -1
- package/src/gep/trajectoryExport.js +0 -1
- package/src/gep/validationReport.js +0 -55
- package/src/gep/validator/index.js +0 -411
- package/src/gep/validator/reporter.js +0 -210
- package/src/gep/validator/sandboxExecutor.js +0 -480
- package/src/gep/validator/stakeBootstrap.js +0 -357
- package/src/gep/workspaceKeychain.js +0 -1
- package/src/ops/cleanup.js +0 -80
- package/src/ops/commentary.js +0 -60
- package/src/ops/health_check.js +0 -104
- package/src/ops/index.js +0 -11
- package/src/ops/innovation.js +0 -67
- package/src/ops/lifecycle.js +0 -798
- package/src/ops/self_repair.js +0 -76
- package/src/ops/skills_monitor.js +0 -147
- package/src/ops/trigger.js +0 -33
- package/src/proxy/clientSettings.js +0 -405
- package/src/proxy/envelope.js +0 -59
- package/src/proxy/extensions/dmHandler.js +0 -45
- package/src/proxy/extensions/sessionHandler.js +0 -141
- package/src/proxy/extensions/skillUpdater.js +0 -64
- package/src/proxy/extensions/traceControl.js +0 -1
- package/src/proxy/index.js +0 -1395
- package/src/proxy/inject.js +0 -1
- package/src/proxy/lifecycle/manager.js +0 -1568
- package/src/proxy/mailbox/state.js +0 -207
- package/src/proxy/mailbox/store.js +0 -590
- package/src/proxy/router/cache_passthrough.js +0 -26
- package/src/proxy/router/features.js +0 -84
- package/src/proxy/router/gemini_route.js +0 -154
- package/src/proxy/router/messages_route.js +0 -535
- package/src/proxy/router/model_router.js +0 -113
- package/src/proxy/router/models_route.js +0 -52
- package/src/proxy/router/ollama_route.js +0 -103
- package/src/proxy/router/responses_route.js +0 -170
- package/src/proxy/router/vertex_route.js +0 -110
- package/src/proxy/server/http.js +0 -363
- package/src/proxy/server/routes.js +0 -558
- package/src/proxy/server/settings.js +0 -115
- package/src/proxy/sync/engine.js +0 -179
- package/src/proxy/sync/inbound.js +0 -211
- package/src/proxy/sync/outbound.js +0 -320
- package/src/proxy/task/monitor.js +0 -131
- package/src/proxy/trace/extractor.js +0 -1
- package/src/proxy/trace/usage.js +0 -1
- package/src/webui/client/clientJs/assets.js +0 -111
- package/src/webui/client/clientJs/bootstrap.js +0 -92
- package/src/webui/client/clientJs/common.js +0 -77
- package/src/webui/client/clientJs/i18n.js +0 -366
- package/src/webui/client/clientJs/index.js +0 -35
- package/src/webui/client/clientJs/interactions.js +0 -351
- package/src/webui/client/clientJs/overview.js +0 -152
- package/src/webui/client/clientJs/personality.js +0 -285
- package/src/webui/client/clientJs/pipelines.js +0 -330
- package/src/webui/client/indexHtml.js +0 -221
- package/src/webui/client/static.js +0 -23
- package/src/webui/client/stylesCss.js +0 -639
- package/src/webui/client/vendor/README.md +0 -15
- package/src/webui/client/vendor/echarts.min.js +0 -45
- package/src/webui/index.js +0 -14
- package/src/webui/observer/assets.js +0 -146
- package/src/webui/observer/index.js +0 -37
- package/src/webui/observer/interactions.js +0 -127
- package/src/webui/observer/jsonl.js +0 -75
- package/src/webui/observer/paths.js +0 -46
- package/src/webui/observer/personality.js +0 -43
- package/src/webui/observer/pipelineEvents.js +0 -58
- package/src/webui/observer/redact.js +0 -63
- package/src/webui/observer/runs.js +0 -356
- package/src/webui/observer/safety.js +0 -57
- package/src/webui/observer/skills.js +0 -70
- package/src/webui/observer/status.js +0 -71
- package/src/webui/server/http.js +0 -138
- package/src/webui/server/routes.js +0 -41
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
|
|
5
|
-
const PLATFORMS = {
|
|
6
|
-
cursor: { name: 'Cursor', configDir: '.cursor', detector: '.cursor' },
|
|
7
|
-
'claude-code': { name: 'Claude Code', configDir: '.claude', detector: '.claude' },
|
|
8
|
-
codex: { name: 'Codex', configDir: '.codex', detector: '.codex' },
|
|
9
|
-
kiro: { name: 'Kiro', configDir: '.kiro', detector: '.kiro' },
|
|
10
|
-
opencode: { name: 'opencode', configDir: '.opencode', detector: '.opencode' },
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// Detect the host agent from runtime environment signals, which are far more
|
|
14
|
-
// reliable than scanning for `.claude` / `.cursor` directories when both exist
|
|
15
|
-
// on disk (e.g. under $HOME). Precedence when signals conflict (#590):
|
|
16
|
-
// 1. strong Claude Code signals (set only by the Claude Code runtime)
|
|
17
|
-
// 2. Cursor signals
|
|
18
|
-
// 3. Codex signals
|
|
19
|
-
// 4. CLAUDE_PROJECT_DIR — a weak Claude alias some hosts (incl. Cursor's
|
|
20
|
-
// Claude integration) export, so it loses to explicit Cursor signals.
|
|
21
|
-
function detectPlatformFromEnv(env = process.env) {
|
|
22
|
-
const hasStrongClaudeSignal = env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT;
|
|
23
|
-
if (hasStrongClaudeSignal) {
|
|
24
|
-
return 'claude-code';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const hasCursorSignal =
|
|
28
|
-
env.CURSOR_TRACE_ID ||
|
|
29
|
-
env.CURSOR_SESSION_ID ||
|
|
30
|
-
env.CURSOR_PROJECT_DIR ||
|
|
31
|
-
env.CURSOR_AGENT ||
|
|
32
|
-
String(env.TERM_PROGRAM || '').toLowerCase() === 'cursor';
|
|
33
|
-
if (hasCursorSignal) {
|
|
34
|
-
return 'cursor';
|
|
35
|
-
}
|
|
36
|
-
const hasCodexSignal =
|
|
37
|
-
env.CODEX_THREAD_ID ||
|
|
38
|
-
env.CODEX_SHELL ||
|
|
39
|
-
env.CODEX_CI ||
|
|
40
|
-
env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
|
|
41
|
-
if (hasCodexSignal) {
|
|
42
|
-
return 'codex';
|
|
43
|
-
}
|
|
44
|
-
if (env.CLAUDE_PROJECT_DIR) {
|
|
45
|
-
return 'claude-code';
|
|
46
|
-
}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function detectPlatform(cwd) {
|
|
51
|
-
const envPlatform = detectPlatformFromEnv();
|
|
52
|
-
if (envPlatform) return envPlatform;
|
|
53
|
-
|
|
54
|
-
const root = cwd || process.cwd();
|
|
55
|
-
const home = os.homedir();
|
|
56
|
-
for (const [id, meta] of Object.entries(PLATFORMS)) {
|
|
57
|
-
if (fs.existsSync(path.join(root, meta.detector))) return id;
|
|
58
|
-
}
|
|
59
|
-
for (const [id, meta] of Object.entries(PLATFORMS)) {
|
|
60
|
-
if (fs.existsSync(path.join(home, meta.detector))) return id;
|
|
61
|
-
}
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Quote `value` as a single shell argument for the host that runs hook
|
|
66
|
-
// `command` strings (POSIX sh, or cmd.exe on Windows).
|
|
67
|
-
function shellQuoteForHost(value) {
|
|
68
|
-
const raw = String(value);
|
|
69
|
-
if (process.platform === 'win32') {
|
|
70
|
-
return `"${raw.replace(/"/g, '\\"')}"`;
|
|
71
|
-
}
|
|
72
|
-
return `'${raw.replace(/'/g, "'\\''")}'`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Build a hook `command` that runs `scriptPath` with node WITHOUT exposing the
|
|
76
|
-
// path bytes to the shell. The absolute path is base64-encoded into a `node -e`
|
|
77
|
-
// wrapper that decodes it and spawns the real script with `shell:false`, so
|
|
78
|
-
// spaces / `$()` / backticks / `%VAR%` in the path can never be expanded by sh
|
|
79
|
-
// or cmd.exe (#590). The plaintext basename is appended as a final, inert arg
|
|
80
|
-
// so uninstall/reinstall can still match evolver-owned hooks by command
|
|
81
|
-
// substring (see isEvolverHookCommand) even though the path itself is encoded.
|
|
82
|
-
//
|
|
83
|
-
// Shared across adapters so there is exactly one implementation to audit.
|
|
84
|
-
function buildSafeNodeHookCommand(scriptPath) {
|
|
85
|
-
const encodedPath = Buffer.from(String(scriptPath), 'utf8').toString('base64');
|
|
86
|
-
const js = [
|
|
87
|
-
"const{spawnSync}=require('child_process')",
|
|
88
|
-
`const p=Buffer.from('${encodedPath}','base64').toString('utf8')`,
|
|
89
|
-
"const r=spawnSync(process.execPath,[p],{stdio:'inherit',shell:false})",
|
|
90
|
-
"if(r.error){console.error(r.error.message||String(r.error));process.exit(1)}",
|
|
91
|
-
"process.exit(r.status==null?1:r.status)",
|
|
92
|
-
].join(';');
|
|
93
|
-
return `node -e ${shellQuoteForHost(js)} ${path.basename(scriptPath)}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function resolveConfigRoot(platformId, cwd) {
|
|
97
|
-
const root = cwd || process.cwd();
|
|
98
|
-
const home = os.homedir();
|
|
99
|
-
const meta = PLATFORMS[platformId];
|
|
100
|
-
if (!meta) return null;
|
|
101
|
-
if (fs.existsSync(path.join(root, meta.detector))) return root;
|
|
102
|
-
if (fs.existsSync(path.join(home, meta.detector))) return home;
|
|
103
|
-
return root;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function loadAdapter(platformId) {
|
|
107
|
-
switch (platformId) {
|
|
108
|
-
case 'cursor': return require('./cursor');
|
|
109
|
-
case 'claude-code': return require('./claudeCode');
|
|
110
|
-
case 'codex': return require('./codex');
|
|
111
|
-
case 'kiro': return require('./kiro');
|
|
112
|
-
case 'opencode': return require('./opencode');
|
|
113
|
-
default: return null;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function mergeJsonFile(filePath, patch, { markerKey = '_evolver_managed' } = {}) {
|
|
118
|
-
let existing = {};
|
|
119
|
-
try {
|
|
120
|
-
if (fs.existsSync(filePath)) {
|
|
121
|
-
const raw = fs.readFileSync(filePath, 'utf8').trim();
|
|
122
|
-
if (raw) existing = JSON.parse(raw);
|
|
123
|
-
}
|
|
124
|
-
} catch { /* start fresh */ }
|
|
125
|
-
const merged = mergeWithHooksUnion(existing, patch);
|
|
126
|
-
merged[markerKey] = true;
|
|
127
|
-
const tmp = filePath + '.tmp';
|
|
128
|
-
fs.writeFileSync(tmp, JSON.stringify(merged, null, 2) + '\n', 'utf8');
|
|
129
|
-
fs.renameSync(tmp, filePath);
|
|
130
|
-
return merged;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Like deepMerge, but for `hooks.<event>` arrays specifically: instead of
|
|
134
|
-
// replacing the user's existing entries, keep them and append/refresh evolver-
|
|
135
|
-
// owned entries (matched by command containing `evolver-session/-signal`).
|
|
136
|
-
// This preserves user-installed Stop/SessionStart hooks (#539) while still
|
|
137
|
-
// updating evolver hooks across reinstalls.
|
|
138
|
-
function mergeWithHooksUnion(target, source) {
|
|
139
|
-
const result = deepMerge(target, source);
|
|
140
|
-
if (
|
|
141
|
-
target && target.hooks && typeof target.hooks === 'object' &&
|
|
142
|
-
source && source.hooks && typeof source.hooks === 'object'
|
|
143
|
-
) {
|
|
144
|
-
for (const event of Object.keys(source.hooks)) {
|
|
145
|
-
const tArr = Array.isArray(target.hooks[event]) ? target.hooks[event] : null;
|
|
146
|
-
const sArr = Array.isArray(source.hooks[event]) ? source.hooks[event] : null;
|
|
147
|
-
if (tArr && sArr) {
|
|
148
|
-
const userEntries = tArr
|
|
149
|
-
.map(stripEvolverCommands)
|
|
150
|
-
.filter(Boolean);
|
|
151
|
-
result.hooks[event] = [...userEntries, ...sArr];
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return result;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function stripEvolverCommands(entry) {
|
|
159
|
-
if (!entry || typeof entry !== 'object') return entry;
|
|
160
|
-
if (typeof entry.command === 'string' && isEvolverHookCommand(entry.command)) {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
if (!Array.isArray(entry.hooks)) return entry;
|
|
164
|
-
|
|
165
|
-
const hooks = entry.hooks.filter(h => {
|
|
166
|
-
const cmd = h && h.command;
|
|
167
|
-
return !(typeof cmd === 'string' && isEvolverHookCommand(cmd));
|
|
168
|
-
});
|
|
169
|
-
if (hooks.length === 0) return null;
|
|
170
|
-
if (hooks.length === entry.hooks.length) return entry;
|
|
171
|
-
return { ...entry, hooks };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Pull all `command` strings out of an event entry, supporting both flat
|
|
175
|
-
// shape (Codex: `{type, command}`) and Claude Code matcher shape
|
|
176
|
-
// (`{matcher, hooks: [{type, command}]}`). Returns [] when neither applies.
|
|
177
|
-
function collectCommands(entry) {
|
|
178
|
-
if (!entry || typeof entry !== 'object') return [];
|
|
179
|
-
const out = [];
|
|
180
|
-
if (typeof entry.command === 'string') out.push(entry.command);
|
|
181
|
-
if (Array.isArray(entry.hooks)) {
|
|
182
|
-
for (const h of entry.hooks) {
|
|
183
|
-
if (h && typeof h.command === 'string') out.push(h.command);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return out;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function isEvolverHookCommand(command) {
|
|
190
|
-
if (typeof command !== 'string') return false;
|
|
191
|
-
return command.includes('evolver-session') ||
|
|
192
|
-
command.includes('evolver-signal') ||
|
|
193
|
-
command.includes('evolver-task-recall') ||
|
|
194
|
-
// Legacy installs briefly shipped this companion daemon hook. Treat it
|
|
195
|
-
// as evolver-owned so reinstall/merge can remove it instead of preserving
|
|
196
|
-
// a stale supervisor that may point clients at a dead proxy.
|
|
197
|
-
command.includes('evolver-daemon-start');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function deepMerge(target, source) {
|
|
201
|
-
const result = { ...target };
|
|
202
|
-
for (const key of Object.keys(source)) {
|
|
203
|
-
if (
|
|
204
|
-
source[key] && typeof source[key] === 'object' && !Array.isArray(source[key]) &&
|
|
205
|
-
result[key] && typeof result[key] === 'object' && !Array.isArray(result[key])
|
|
206
|
-
) {
|
|
207
|
-
result[key] = deepMerge(result[key], source[key]);
|
|
208
|
-
} else {
|
|
209
|
-
result[key] = source[key];
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return result;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Refuse to write/read through a symbolic link at the adapter's
|
|
216
|
-
// platform config dir (`<root>/.codex`, `<root>/.claude`, …) or any
|
|
217
|
-
// nested adapter-owned subdir (`hooks/`, `plugins/`, …). A
|
|
218
|
-
// repository-controlled symlink at any of these paths would let
|
|
219
|
-
// install/uninstall writes land on attacker-chosen files outside the
|
|
220
|
-
// workspace (PR #94 round-4 surfaced the top-level case; round-5
|
|
221
|
-
// surfaced that a hostile repo can keep `.codex` real and only
|
|
222
|
-
// symlink `.codex/hooks`). Missing dirs are fine — install will
|
|
223
|
-
// create them.
|
|
224
|
-
function assertSafeConfigDir(dir, label, { subdirs = [] } = {}) {
|
|
225
|
-
assertNotSymlink(dir, label || 'config dir');
|
|
226
|
-
for (const sub of subdirs) {
|
|
227
|
-
assertNotSymlink(path.join(dir, sub), `${label || 'config dir'}/${sub}`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function assertNotSymlink(p, label) {
|
|
232
|
-
let st;
|
|
233
|
-
try {
|
|
234
|
-
st = fs.lstatSync(p);
|
|
235
|
-
} catch (e) {
|
|
236
|
-
if (e && e.code === 'ENOENT') return;
|
|
237
|
-
throw e;
|
|
238
|
-
}
|
|
239
|
-
if (st.isSymbolicLink()) {
|
|
240
|
-
throw new Error(
|
|
241
|
-
`[setup-hooks] Refusing to operate: ${label} ${p} is a ` +
|
|
242
|
-
`symbolic link. evolver will not follow symlinks for ` +
|
|
243
|
-
`adapter-owned dirs — a hostile workspace could redirect ` +
|
|
244
|
-
`writes/unlinks outside the project root. Replace it with a ` +
|
|
245
|
-
`real directory and rerun.`
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function copyHookScripts(destDir, evolverRoot) {
|
|
251
|
-
const scriptsDir = path.join(evolverRoot || __dirname, 'scripts');
|
|
252
|
-
// Every helper required by the entry-point hooks via `require('./_xxx')`
|
|
253
|
-
// resolves relative to the *destination* (`__dirname` after copy), so
|
|
254
|
-
// every such helper MUST appear here or the hook crashes with
|
|
255
|
-
// MODULE_NOT_FOUND at runtime. Two regressions of this shape have shipped
|
|
256
|
-
// already:
|
|
257
|
-
// - PR #94 review caught `_runtimePaths.js` missing from this list.
|
|
258
|
-
// - Issue #547 (rendigua, v1.87.0): `_memoryFiltering.js` was added to
|
|
259
|
-
// evolver-session-start.js but not here, so fresh installs failed
|
|
260
|
-
// immediately on `node .codex/hooks/evolver-session-start.js`.
|
|
261
|
-
// To keep future helpers from re-living this, the regression test in
|
|
262
|
-
// test/adapters.test.js scans every `require('./_*')` in the source
|
|
263
|
-
// adapter scripts and asserts the target file is in this list.
|
|
264
|
-
const scripts = [
|
|
265
|
-
'_runtimePaths.js',
|
|
266
|
-
'_memoryFiltering.js',
|
|
267
|
-
'_lockPaths.js',
|
|
268
|
-
'evolver-session-start.js',
|
|
269
|
-
'evolver-signal-detect.js',
|
|
270
|
-
'evolver-session-end.js',
|
|
271
|
-
'evolver-task-recall.js',
|
|
272
|
-
];
|
|
273
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
274
|
-
const copied = [];
|
|
275
|
-
for (const name of scripts) {
|
|
276
|
-
const src = path.join(scriptsDir, name);
|
|
277
|
-
const dest = path.join(destDir, name);
|
|
278
|
-
if (!fs.existsSync(src)) {
|
|
279
|
-
console.warn(`[setup-hooks] Warning: script not found: ${src}`);
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
// PR #94 round-6 HIGH: reject if the destination is a pre-planted
|
|
283
|
-
// symlink. fs.copyFileSync follows symlinks at the destination, so
|
|
284
|
-
// a hostile repo that pre-creates `.codex/hooks/evolver-session-end.js`
|
|
285
|
-
// pointing at e.g. `~/.bashrc` would have its target overwritten with
|
|
286
|
-
// evolver script content. Round-5 closed the directory hole; this
|
|
287
|
-
// closes the per-file hole.
|
|
288
|
-
assertNotSymlink(dest, `hook destination ${name}`);
|
|
289
|
-
fs.copyFileSync(src, dest);
|
|
290
|
-
// NOTE(windows): fs.chmodSync is a no-op on Windows; hook scripts remain
|
|
291
|
-
// executable via file extension association (.js), not Unix mode bits.
|
|
292
|
-
try { fs.chmodSync(dest, 0o755); } catch { /* best-effort; no-op on Windows */ }
|
|
293
|
-
copied.push(dest);
|
|
294
|
-
}
|
|
295
|
-
return copied;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function appendSectionToFile(filePath, marker, content) {
|
|
299
|
-
let existing = '';
|
|
300
|
-
try { existing = fs.readFileSync(filePath, 'utf8'); } catch { /* new file */ }
|
|
301
|
-
if (existing.includes(marker)) {
|
|
302
|
-
console.log(`[setup-hooks] Section already present in ${filePath}, skipping.`);
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n\n' : '\n';
|
|
306
|
-
fs.writeFileSync(filePath, existing + separator + content + '\n', 'utf8');
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function removeEvolverHooks(filePath, { markerKey = '_evolver_managed' } = {}) {
|
|
311
|
-
try {
|
|
312
|
-
if (!fs.existsSync(filePath)) return false;
|
|
313
|
-
const raw = fs.readFileSync(filePath, 'utf8').trim();
|
|
314
|
-
if (!raw) return false;
|
|
315
|
-
const data = JSON.parse(raw);
|
|
316
|
-
if (!data[markerKey]) return false;
|
|
317
|
-
|
|
318
|
-
let changed = false;
|
|
319
|
-
if (data.hooks) {
|
|
320
|
-
for (const event of Object.keys(data.hooks)) {
|
|
321
|
-
if (Array.isArray(data.hooks[event])) {
|
|
322
|
-
const before = data.hooks[event].length;
|
|
323
|
-
data.hooks[event] = data.hooks[event].filter(h => {
|
|
324
|
-
const cmd = h.command || '';
|
|
325
|
-
return !isEvolverHookCommand(cmd);
|
|
326
|
-
});
|
|
327
|
-
if (data.hooks[event].length !== before) changed = true;
|
|
328
|
-
if (data.hooks[event].length === 0) delete data.hooks[event];
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
if (Object.keys(data.hooks).length === 0) delete data.hooks;
|
|
332
|
-
}
|
|
333
|
-
if (data.mcpServers) {
|
|
334
|
-
// Claude Code / Codex: hooks in mcpServers sub-key -- not relevant, skip
|
|
335
|
-
}
|
|
336
|
-
delete data[markerKey];
|
|
337
|
-
const tmp = filePath + '.tmp';
|
|
338
|
-
fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
339
|
-
fs.renameSync(tmp, filePath);
|
|
340
|
-
return changed;
|
|
341
|
-
} catch {
|
|
342
|
-
return false;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function removeHookScripts(hooksDir) {
|
|
347
|
-
// Must mirror the install list above. If install copies a helper but
|
|
348
|
-
// uninstall doesn't remove it, `setup-hooks --uninstall` leaves orphan
|
|
349
|
-
// files behind that the user then has to clean up by hand (#547 fix
|
|
350
|
-
// would have introduced exactly this gap if only the install side
|
|
351
|
-
// had been updated).
|
|
352
|
-
const scripts = [
|
|
353
|
-
'_runtimePaths.js',
|
|
354
|
-
'_memoryFiltering.js',
|
|
355
|
-
'_lockPaths.js',
|
|
356
|
-
'evolver-session-start.js',
|
|
357
|
-
'evolver-signal-detect.js',
|
|
358
|
-
'evolver-session-end.js',
|
|
359
|
-
'evolver-task-recall.js',
|
|
360
|
-
];
|
|
361
|
-
let removed = 0;
|
|
362
|
-
for (const name of scripts) {
|
|
363
|
-
const p = path.join(hooksDir, name);
|
|
364
|
-
try {
|
|
365
|
-
if (fs.existsSync(p)) { fs.unlinkSync(p); removed++; }
|
|
366
|
-
} catch (e) {
|
|
367
|
-
// Surface unlink failures so users can see why a "successful"
|
|
368
|
-
// uninstall left files behind (Windows file-locking, perms, …).
|
|
369
|
-
console.warn(`[setup-hooks] Failed to remove ${p}: ${e.message || e}`);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
return removed;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Remove a marker-bracketed section from a markdown file. Used by adapter
|
|
376
|
-
// uninstall to clean up CLAUDE.md / AGENTS.md without nuking surrounding
|
|
377
|
-
// user content.
|
|
378
|
-
//
|
|
379
|
-
// The previous inline implementations (codex/claude/kiro/opencode) searched
|
|
380
|
-
// for the *next* `\n## ` after the marker, which matched evolver's own
|
|
381
|
-
// `## Evolution Memory` heading and left the entire injected section in
|
|
382
|
-
// place (#538). This helper skips any `## ` heading on the same line as the
|
|
383
|
-
// marker, then looks for the next H2 to know where the user's content
|
|
384
|
-
// resumes.
|
|
385
|
-
function removeMarkedSection(filePath, marker) {
|
|
386
|
-
try {
|
|
387
|
-
if (!fs.existsSync(filePath)) return false;
|
|
388
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
389
|
-
const idx = raw.indexOf(marker);
|
|
390
|
-
if (idx === -1) return false;
|
|
391
|
-
|
|
392
|
-
// Skip past the marker line (and any heading on the same line).
|
|
393
|
-
let scanFrom = idx + marker.length;
|
|
394
|
-
const eol = raw.indexOf('\n', scanFrom);
|
|
395
|
-
if (eol !== -1) scanFrom = eol + 1;
|
|
396
|
-
|
|
397
|
-
// Skip past evolver's own `## ...` heading line if present.
|
|
398
|
-
if (raw.startsWith('## ', scanFrom)) {
|
|
399
|
-
const eol2 = raw.indexOf('\n', scanFrom);
|
|
400
|
-
scanFrom = eol2 !== -1 ? eol2 + 1 : raw.length;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const nextSection = raw.indexOf('\n## ', scanFrom);
|
|
404
|
-
const endIdx = nextSection !== -1 ? nextSection : raw.length;
|
|
405
|
-
const before = raw.slice(0, idx).trimEnd();
|
|
406
|
-
const after = nextSection !== -1 ? raw.slice(endIdx) : '';
|
|
407
|
-
const next = (before ? before + (after.startsWith('\n') ? '' : '\n') : '') + after;
|
|
408
|
-
fs.writeFileSync(filePath, next.trimEnd() + '\n', 'utf8');
|
|
409
|
-
return true;
|
|
410
|
-
} catch (e) {
|
|
411
|
-
console.warn(`[setup-hooks] Failed to clean section in ${filePath}: ${e.message || e}`);
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
async function setupHooks({ platform, cwd, force, uninstall, evolverRoot } = {}) {
|
|
417
|
-
const effectiveCwd = cwd || process.cwd();
|
|
418
|
-
const effectiveEvolverRoot = evolverRoot || path.resolve(__dirname, '..');
|
|
419
|
-
const platformId = platform || detectPlatform(effectiveCwd);
|
|
420
|
-
|
|
421
|
-
if (!platformId) {
|
|
422
|
-
console.error('[setup-hooks] Could not detect platform. Use --platform=cursor|claude-code|codex|kiro|opencode');
|
|
423
|
-
return { ok: false, error: 'platform_not_detected' };
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const meta = PLATFORMS[platformId];
|
|
427
|
-
if (!meta) {
|
|
428
|
-
console.error(`[setup-hooks] Unknown platform: ${platformId}`);
|
|
429
|
-
return { ok: false, error: 'unknown_platform' };
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const configRoot = resolveConfigRoot(platformId, effectiveCwd);
|
|
433
|
-
const adapter = loadAdapter(platformId);
|
|
434
|
-
if (!adapter) {
|
|
435
|
-
console.error(`[setup-hooks] No adapter found for ${platformId}`);
|
|
436
|
-
return { ok: false, error: 'no_adapter' };
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
console.log(`[setup-hooks] Platform: ${meta.name}`);
|
|
440
|
-
console.log(`[setup-hooks] Config root: ${configRoot}`);
|
|
441
|
-
|
|
442
|
-
if (uninstall) {
|
|
443
|
-
return adapter.uninstall({ configRoot, evolverRoot: effectiveEvolverRoot });
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return adapter.install({ configRoot, evolverRoot: effectiveEvolverRoot, force });
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
module.exports = {
|
|
450
|
-
detectPlatformFromEnv,
|
|
451
|
-
detectPlatform,
|
|
452
|
-
buildSafeNodeHookCommand,
|
|
453
|
-
resolveConfigRoot,
|
|
454
|
-
loadAdapter,
|
|
455
|
-
mergeJsonFile,
|
|
456
|
-
deepMerge,
|
|
457
|
-
mergeWithHooksUnion,
|
|
458
|
-
collectCommands,
|
|
459
|
-
isEvolverHookCommand,
|
|
460
|
-
copyHookScripts,
|
|
461
|
-
appendSectionToFile,
|
|
462
|
-
assertSafeConfigDir,
|
|
463
|
-
assertNotSymlink,
|
|
464
|
-
removeEvolverHooks,
|
|
465
|
-
removeHookScripts,
|
|
466
|
-
removeMarkedSection,
|
|
467
|
-
setupHooks,
|
|
468
|
-
PLATFORMS,
|
|
469
|
-
};
|
package/src/adapters/kiro.js
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { copyHookScripts, removeHookScripts, removeMarkedSection, assertSafeConfigDir } = require('./hookAdapter');
|
|
4
|
-
|
|
5
|
-
const HOOK_SCRIPTS_DIR_NAME = 'hooks';
|
|
6
|
-
const EVOLVER_MARKER = '<!-- evolver-evolution-memory -->';
|
|
7
|
-
const HOOK_FILE_SUFFIX = '.kiro.hook';
|
|
8
|
-
const HOOK_FILES = {
|
|
9
|
-
sessionStart: 'evolver-session-start.kiro.hook',
|
|
10
|
-
signalDetect: 'evolver-signal-detect.kiro.hook',
|
|
11
|
-
sessionEnd: 'evolver-session-end.kiro.hook',
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function buildHookConfig(kind, scriptsBase) {
|
|
15
|
-
// Kiro has no dedicated sessionStart event; `promptSubmit` is the closest
|
|
16
|
-
// analogue. The session-start JS script itself guards against per-prompt
|
|
17
|
-
// re-injection via a session-scoped state file when
|
|
18
|
-
// EVOLVER_SESSION_START_DEDUP=1 is set (injected inline into the command
|
|
19
|
-
// because Kiro runCommand has no env field).
|
|
20
|
-
const sessionStartCmd = `EVOLVER_SESSION_START_DEDUP=1 node ${scriptsBase}/evolver-session-start.js`;
|
|
21
|
-
const hookTemplates = {
|
|
22
|
-
sessionStart: {
|
|
23
|
-
name: 'Evolver Session Start',
|
|
24
|
-
version: '1',
|
|
25
|
-
description:
|
|
26
|
-
'Reads recent evolution memory from the local memory graph and injects it as context when a prompt is submitted.',
|
|
27
|
-
when: { type: 'promptSubmit' },
|
|
28
|
-
then: {
|
|
29
|
-
type: 'runCommand',
|
|
30
|
-
command: sessionStartCmd,
|
|
31
|
-
timeout: 3,
|
|
32
|
-
},
|
|
33
|
-
_evolver_managed: true,
|
|
34
|
-
},
|
|
35
|
-
signalDetect: {
|
|
36
|
-
name: 'Evolver Signal Detect',
|
|
37
|
-
version: '1',
|
|
38
|
-
description:
|
|
39
|
-
'Detects evolution signals (errors, perf bottlenecks, capability gaps, test failures) in file content after write operations.',
|
|
40
|
-
when: { type: 'postToolUse', toolTypes: ['write'] },
|
|
41
|
-
then: {
|
|
42
|
-
type: 'runCommand',
|
|
43
|
-
command: `node ${scriptsBase}/evolver-signal-detect.js`,
|
|
44
|
-
timeout: 2,
|
|
45
|
-
},
|
|
46
|
-
_evolver_managed: true,
|
|
47
|
-
},
|
|
48
|
-
sessionEnd: {
|
|
49
|
-
name: 'Evolver Session End',
|
|
50
|
-
version: '1',
|
|
51
|
-
description:
|
|
52
|
-
'Records evolution outcome at session end by analyzing git diff stats and writing to the local memory graph.',
|
|
53
|
-
when: { type: 'agentStop' },
|
|
54
|
-
then: {
|
|
55
|
-
type: 'runCommand',
|
|
56
|
-
command: `node ${scriptsBase}/evolver-session-end.js`,
|
|
57
|
-
timeout: 8,
|
|
58
|
-
},
|
|
59
|
-
_evolver_managed: true,
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
return hookTemplates[kind];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function buildAgentsMdSection() {
|
|
66
|
-
return `${EVOLVER_MARKER}
|
|
67
|
-
## Evolution Memory (Evolver)
|
|
68
|
-
|
|
69
|
-
This project uses evolver for self-evolution. Hooks automatically:
|
|
70
|
-
1. Inject recent evolution memory on prompt submit
|
|
71
|
-
2. Detect evolution signals during file edits
|
|
72
|
-
3. Record outcomes at session end
|
|
73
|
-
|
|
74
|
-
For substantive tasks, call \`gep_recall\` before work and \`gep_record_outcome\` after.
|
|
75
|
-
Signals: log_error, perf_bottleneck, user_feature_request, capability_gap, deployment_issue, test_failure.`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function appendSectionToFile(filePath, marker, content) {
|
|
79
|
-
let existing = '';
|
|
80
|
-
try { existing = fs.readFileSync(filePath, 'utf8'); } catch { /* new file */ }
|
|
81
|
-
if (existing.includes(marker)) return false;
|
|
82
|
-
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n\n' : '\n';
|
|
83
|
-
fs.writeFileSync(filePath, existing + separator + content + '\n', 'utf8');
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function writeHookFile(hooksDir, fileName, config) {
|
|
88
|
-
const tmp = path.join(hooksDir, fileName + '.tmp');
|
|
89
|
-
const dest = path.join(hooksDir, fileName);
|
|
90
|
-
fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
91
|
-
fs.renameSync(tmp, dest);
|
|
92
|
-
return dest;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function isEvolverManagedHookFile(filePath) {
|
|
96
|
-
try {
|
|
97
|
-
const raw = fs.readFileSync(filePath, 'utf8').trim();
|
|
98
|
-
if (!raw) return false;
|
|
99
|
-
const data = JSON.parse(raw);
|
|
100
|
-
if (data && data._evolver_managed === true) return true;
|
|
101
|
-
if (typeof data.name === 'string' && /^evolver\b/i.test(data.name)) return true;
|
|
102
|
-
if (data.then && typeof data.then.command === 'string' &&
|
|
103
|
-
/evolver-(session|signal)/.test(data.then.command)) return true;
|
|
104
|
-
} catch { /* treat as non-evolver */ }
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function install({ configRoot, evolverRoot, force }) {
|
|
109
|
-
const kiroDir = path.join(configRoot, '.kiro');
|
|
110
|
-
const hooksDir = path.join(kiroDir, HOOK_SCRIPTS_DIR_NAME);
|
|
111
|
-
const agentsMdPath = path.join(configRoot, 'AGENTS.md');
|
|
112
|
-
const scriptsBase = '.kiro/hooks';
|
|
113
|
-
assertSafeConfigDir(kiroDir, '.kiro', { subdirs: [HOOK_SCRIPTS_DIR_NAME] });
|
|
114
|
-
|
|
115
|
-
const hookPaths = Object.values(HOOK_FILES).map(name => path.join(hooksDir, name));
|
|
116
|
-
|
|
117
|
-
if (!force) {
|
|
118
|
-
const existingEvolverHook = hookPaths.find(p => fs.existsSync(p) && isEvolverManagedHookFile(p));
|
|
119
|
-
if (existingEvolverHook) {
|
|
120
|
-
console.log('[kiro] Evolver hooks already installed. Use --force to overwrite.');
|
|
121
|
-
return { ok: true, skipped: true };
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
fs.mkdirSync(hooksDir, { recursive: true });
|
|
126
|
-
|
|
127
|
-
const written = [];
|
|
128
|
-
for (const [kind, fileName] of Object.entries(HOOK_FILES)) {
|
|
129
|
-
const cfg = buildHookConfig(kind, scriptsBase);
|
|
130
|
-
const dest = writeHookFile(hooksDir, fileName, cfg);
|
|
131
|
-
written.push(dest);
|
|
132
|
-
console.log('[kiro] Wrote ' + dest);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const copied = copyHookScripts(hooksDir, path.join(evolverRoot, 'src', 'adapters'));
|
|
136
|
-
console.log('[kiro] Copied ' + copied.length + ' hook scripts to ' + hooksDir);
|
|
137
|
-
|
|
138
|
-
const injected = appendSectionToFile(agentsMdPath, EVOLVER_MARKER, buildAgentsMdSection());
|
|
139
|
-
if (injected) {
|
|
140
|
-
console.log('[kiro] Injected evolution section into ' + agentsMdPath);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
console.log('[kiro] Installation complete.');
|
|
144
|
-
console.log('[kiro] Kiro auto-discovers *.kiro.hook files in .kiro/hooks/ -- no restart needed.');
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
ok: true,
|
|
148
|
-
platform: 'kiro',
|
|
149
|
-
files: [...written, agentsMdPath, ...copied],
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function uninstall({ configRoot }) {
|
|
154
|
-
const kiroDir = path.join(configRoot, '.kiro');
|
|
155
|
-
const hooksDir = path.join(kiroDir, HOOK_SCRIPTS_DIR_NAME);
|
|
156
|
-
const agentsMdPath = path.join(configRoot, 'AGENTS.md');
|
|
157
|
-
assertSafeConfigDir(kiroDir, '.kiro', { subdirs: [HOOK_SCRIPTS_DIR_NAME] });
|
|
158
|
-
|
|
159
|
-
let changed = false;
|
|
160
|
-
let removedCount = 0;
|
|
161
|
-
|
|
162
|
-
if (fs.existsSync(hooksDir)) {
|
|
163
|
-
try {
|
|
164
|
-
const entries = fs.readdirSync(hooksDir);
|
|
165
|
-
for (const entry of entries) {
|
|
166
|
-
if (!entry.endsWith(HOOK_FILE_SUFFIX)) continue;
|
|
167
|
-
const full = path.join(hooksDir, entry);
|
|
168
|
-
if (isEvolverManagedHookFile(full)) {
|
|
169
|
-
try { fs.unlinkSync(full); removedCount++; changed = true; } catch { /* ignore */ }
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
} catch { /* ignore */ }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const scripts = removeHookScripts(hooksDir);
|
|
176
|
-
if (scripts > 0) changed = true;
|
|
177
|
-
|
|
178
|
-
if (removeMarkedSection(agentsMdPath, EVOLVER_MARKER)) {
|
|
179
|
-
changed = true;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
console.log(changed
|
|
183
|
-
? `[kiro] Uninstalled evolver hooks (${removedCount} hook files + ${scripts} scripts removed).`
|
|
184
|
-
: '[kiro] No evolver hooks found to uninstall.');
|
|
185
|
-
|
|
186
|
-
return { ok: true, removed: changed };
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
module.exports = {
|
|
190
|
-
install,
|
|
191
|
-
uninstall,
|
|
192
|
-
buildHookConfig,
|
|
193
|
-
isEvolverManagedHookFile,
|
|
194
|
-
HOOK_FILES,
|
|
195
|
-
};
|