@evomap/evolver 1.90.0 → 2.0.0-beta.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.
- 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 -3670
- 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/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 -602
- 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 -361
- 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/solo/breaker.js +0 -25
- package/src/solo/gitGuard.js +0 -65
- 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,321 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// evolver-session-end.js
|
|
3
|
-
// Records evolution outcome at session end.
|
|
4
|
-
// Collects git diff stats, extracts signals, records via Hub API or local memory.
|
|
5
|
-
// Input: stdin JSON. Output: stdout JSON with `systemMessage` (Claude Code Stop
|
|
6
|
-
// hook notification) — or empty `{}` on Cursor where systemMessage is mishandled.
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const os = require('os');
|
|
11
|
-
const { spawnSync } = require('child_process');
|
|
12
|
-
// 10 MB — prevents RangeError on large child process output (e.g. git log/diff
|
|
13
|
-
// on large repos). See GHSA reports / issue #451.
|
|
14
|
-
const MAX_EXEC_BUFFER = 10 * 1024 * 1024;
|
|
15
|
-
|
|
16
|
-
const { findEvolverRoot, findMemoryGraph, resolveProjectDir, resolveWorkspaceId } = require('./_runtimePaths');
|
|
17
|
-
|
|
18
|
-
// Workspace-id resolution is shared with the session-start reader via
|
|
19
|
-
// _runtimePaths.resolveWorkspaceId(). Reader and writer MUST resolve the SAME
|
|
20
|
-
// id or workspace scoping silently breaks (no entry would ever match the
|
|
21
|
-
// reader's filter), so this logic lives in exactly one place instead of being
|
|
22
|
-
// duplicated here. The shared resolver mirrors src/gep/paths.js#getWorkspaceId()
|
|
23
|
-
// loaded from the evolver root, with an EVOLVER_WORKSPACE_ID env override —
|
|
24
|
-
// consistent with the review-time reader in src/evolve/pipeline/collect.js
|
|
25
|
-
// (Bugbot PR #109 round-1 MEDIUM; reader/writer drift flagged on PR #555).
|
|
26
|
-
|
|
27
|
-
function runGit(args, cwd) {
|
|
28
|
-
// Argv-array form, no shell. Avoids POSIX `2>/dev/null` redirects that
|
|
29
|
-
// break on Windows cmd.exe (#537). Failures (e.g. no HEAD~1 in a fresh
|
|
30
|
-
// repo) are surfaced as a non-zero status; callers distinguish them
|
|
31
|
-
// from successful empty output via the `ok` flag (PR #94 round-6 LOW).
|
|
32
|
-
const res = spawnSync('git', args, {
|
|
33
|
-
cwd,
|
|
34
|
-
encoding: 'utf8',
|
|
35
|
-
timeout: 5000,
|
|
36
|
-
maxBuffer: MAX_EXEC_BUFFER,
|
|
37
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
38
|
-
shell: false,
|
|
39
|
-
windowsHide: true,
|
|
40
|
-
});
|
|
41
|
-
if (res.status === 0 && typeof res.stdout === 'string') {
|
|
42
|
-
return { ok: true, out: res.stdout.trim() };
|
|
43
|
-
}
|
|
44
|
-
return { ok: false, out: '' };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getGitDiffStats() {
|
|
48
|
-
// Use the host-provided workspace root, not process.cwd(): Cursor runs some
|
|
49
|
-
// hook events with cwd set to the plugin dir, where `git diff` finds nothing.
|
|
50
|
-
const cwd = resolveProjectDir();
|
|
51
|
-
// Distinguish "git failed (no HEAD~1, etc.)" from "git succeeded with
|
|
52
|
-
// empty output (e.g. empty merge)". The previous `||` chain treated
|
|
53
|
-
// both as falsy and fell through to the working-tree diff, which can
|
|
54
|
-
// surface unrelated unstaged changes as the session outcome.
|
|
55
|
-
const statHead1 = runGit(['diff', '--stat', 'HEAD~1'], cwd);
|
|
56
|
-
const stat = statHead1.ok ? statHead1.out : runGit(['diff', '--stat'], cwd).out;
|
|
57
|
-
const diffHead1 = runGit(['diff', '--no-color', 'HEAD~1'], cwd);
|
|
58
|
-
const diffContent = diffHead1.ok ? diffHead1.out : runGit(['diff', '--no-color'], cwd).out;
|
|
59
|
-
const filesChanged = (stat.match(/\d+ files? changed/) || ['0'])[0];
|
|
60
|
-
const insertions = (stat.match(/(\d+) insertions?/) || [null, '0'])[1];
|
|
61
|
-
const deletions = (stat.match(/(\d+) deletions?/) || [null, '0'])[1];
|
|
62
|
-
// Distinguish "no git repo here" from "repo with no changes" purely for the
|
|
63
|
-
// skip-log message — the diff commands above can't tell the two apart (both
|
|
64
|
-
// yield empty output). A single cheap rev-parse settles it.
|
|
65
|
-
const isRepo = runGit(['rev-parse', '--is-inside-work-tree'], cwd).out === 'true';
|
|
66
|
-
return {
|
|
67
|
-
stat,
|
|
68
|
-
summary: `${filesChanged}, +${insertions}/-${deletions}`,
|
|
69
|
-
diffSnippet: diffContent.slice(0, 2000),
|
|
70
|
-
hasChanges: stat.length > 0,
|
|
71
|
-
isRepo,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Detect whether the hook is running inside Cursor.
|
|
76
|
-
//
|
|
77
|
-
// Why: Claude Code's Stop hook spec says `systemMessage` is a notification
|
|
78
|
-
// shown to the user and is NOT fed back into Claude's context. Cursor's
|
|
79
|
-
// Claude Code-compatible runtime currently splices it into the next
|
|
80
|
-
// inference round as if it were a user prompt, so Claude "responds" to the
|
|
81
|
-
// evolution receipt — visible to users as an unexplained extra reasoning
|
|
82
|
-
// turn after every task. Until Cursor fixes this, suppress systemMessage
|
|
83
|
-
// on Cursor while keeping the local-memory append intact.
|
|
84
|
-
//
|
|
85
|
-
// Detection (any of):
|
|
86
|
-
// - TERM_PROGRAM=cursor
|
|
87
|
-
// - CURSOR_TRACE_ID / CURSOR_SESSION_ID set
|
|
88
|
-
// - EVOLVER_HOOK_HOST=cursor (manual override)
|
|
89
|
-
// Escape hatch: EVOLVER_HOOK_VERBOSE=1 forces the message on regardless.
|
|
90
|
-
function isCursorHost() {
|
|
91
|
-
const verbose = String(process.env.EVOLVER_HOOK_VERBOSE || '').toLowerCase();
|
|
92
|
-
if (verbose === '1' || verbose === 'true') return false;
|
|
93
|
-
if (String(process.env.EVOLVER_HOOK_HOST || '').toLowerCase() === 'cursor') return true;
|
|
94
|
-
if (String(process.env.TERM_PROGRAM || '').toLowerCase() === 'cursor') return true;
|
|
95
|
-
if (process.env.CURSOR_TRACE_ID) return true;
|
|
96
|
-
if (process.env.CURSOR_SESSION_ID) return true;
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function detectSignals(text) {
|
|
101
|
-
if (!text) return [];
|
|
102
|
-
const lower = text.toLowerCase();
|
|
103
|
-
const signals = [];
|
|
104
|
-
if (/error:|exception:|failed/i.test(lower)) signals.push('log_error');
|
|
105
|
-
if (/timeout|slow|latency|bottleneck/i.test(lower)) signals.push('perf_bottleneck');
|
|
106
|
-
if (/add|implement|feature|new function|new module/i.test(lower)) signals.push('user_feature_request');
|
|
107
|
-
if (/improve|enhance|refactor|optimize/i.test(lower)) signals.push('user_improvement_suggestion');
|
|
108
|
-
if (/not supported|unsupported|not implemented/i.test(lower)) signals.push('capability_gap');
|
|
109
|
-
if (/deploy|ci|pipeline|build failed/i.test(lower)) signals.push('deployment_issue');
|
|
110
|
-
if (/test fail|assertion|expect\(/i.test(lower)) signals.push('test_failure');
|
|
111
|
-
return [...new Set(signals)];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function loadHubFetch() {
|
|
115
|
-
const evolverRoot = findEvolverRoot();
|
|
116
|
-
return require(path.join(evolverRoot, 'src', 'gep', 'hubFetch')).hubFetch;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async function recordToHub(outcome) {
|
|
120
|
-
const hubUrl = process.env.EVOMAP_HUB_URL || process.env.A2A_HUB_URL;
|
|
121
|
-
const apiKey = process.env.EVOMAP_API_KEY || process.env.A2A_NODE_SECRET;
|
|
122
|
-
const nodeId = process.env.EVOMAP_NODE_ID || process.env.A2A_NODE_ID;
|
|
123
|
-
if (!hubUrl || !apiKey) return false;
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const payload = JSON.stringify({
|
|
127
|
-
gene_id: outcome.geneId || 'ad_hoc',
|
|
128
|
-
signals: outcome.signals,
|
|
129
|
-
status: outcome.status,
|
|
130
|
-
score: outcome.score,
|
|
131
|
-
summary: outcome.summary,
|
|
132
|
-
sender_id: nodeId || undefined,
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/evolution/record';
|
|
136
|
-
const hubFetch = loadHubFetch();
|
|
137
|
-
const res = await hubFetch(endpoint, {
|
|
138
|
-
method: 'POST',
|
|
139
|
-
headers: {
|
|
140
|
-
'Content-Type': 'application/json',
|
|
141
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
142
|
-
},
|
|
143
|
-
body: payload,
|
|
144
|
-
signal: AbortSignal.timeout(8000),
|
|
145
|
-
});
|
|
146
|
-
try { await res.text(); } catch (_) {}
|
|
147
|
-
return res.ok;
|
|
148
|
-
} catch {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function recordToLocal(graphPath, outcome) {
|
|
154
|
-
try {
|
|
155
|
-
// Resolve the project dir once so the cwd tag and the workspace_id secret
|
|
156
|
-
// share a single, consistent source (both must agree with the session-start
|
|
157
|
-
// reader's resolveProjectDir()-based scoping).
|
|
158
|
-
const projectDir = resolveProjectDir();
|
|
159
|
-
const entry = {
|
|
160
|
-
timestamp: new Date().toISOString(),
|
|
161
|
-
gene_id: outcome.geneId || 'ad_hoc',
|
|
162
|
-
signals: outcome.signals,
|
|
163
|
-
outcome: {
|
|
164
|
-
status: outcome.status,
|
|
165
|
-
score: outcome.score,
|
|
166
|
-
note: outcome.summary,
|
|
167
|
-
},
|
|
168
|
-
// Tag the originating workspace so the review-time reader in
|
|
169
|
-
// collect.js can scope user-level fallback entries to the current
|
|
170
|
-
// cwd. Without this, two unrelated projects sharing the user-level
|
|
171
|
-
// fallback file (~/.evolver/memory/evolution/memory_graph.jsonl,
|
|
172
|
-
// used by npm-global installs) would cross-pollinate each other's
|
|
173
|
-
// review context — a prompt-injection / disclosure surface flagged
|
|
174
|
-
// by Bugbot on PR #105 round-2.
|
|
175
|
-
//
|
|
176
|
-
// workspace_id is the forge-resistant tag (PR #108 round-3): the
|
|
177
|
-
// reader compares it against the secret in the workspace's own
|
|
178
|
-
// .evolver/workspace-id file. cwd is retained as a backward-compat
|
|
179
|
-
// tag so older entries written before this hardening still pass
|
|
180
|
-
// the cwd check.
|
|
181
|
-
//
|
|
182
|
-
// Use resolveProjectDir() (NOT process.cwd()) so the cwd tag records the
|
|
183
|
-
// user's project, consistent with how the diff above is collected and
|
|
184
|
-
// with the session-start reader's cwd fallback. Under Cursor, cwd is the
|
|
185
|
-
// plugin install dir, so a raw process.cwd() tag would never match the
|
|
186
|
-
// reader's resolveProjectDir()-derived currentDir — silently hiding every
|
|
187
|
-
// cwd-only entry (Bugbot PR #555). collect.js only uses cwd as a legacy
|
|
188
|
-
// fallback (disabled once a workspace_id secret exists), so changing the
|
|
189
|
-
// tag's source — still a directory path — does not affect its scoping.
|
|
190
|
-
cwd: projectDir,
|
|
191
|
-
workspace_id: resolveWorkspaceId(undefined, projectDir),
|
|
192
|
-
source: 'hook:session-end',
|
|
193
|
-
};
|
|
194
|
-
fs.appendFileSync(graphPath, JSON.stringify(entry) + '\n', 'utf8');
|
|
195
|
-
return true;
|
|
196
|
-
} catch {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Append a single timestamped line to ~/.evolver/logs/evolution.log (or
|
|
202
|
-
// EVOLVER_HOOK_LOG_DIR). Best-effort: a log-write failure must never break the
|
|
203
|
-
// hook. Used both for recorded outcomes and for the "skipped, nothing to
|
|
204
|
-
// record" notices so a user can always see why a session did or did not
|
|
205
|
-
// produce an entry.
|
|
206
|
-
function appendEvolutionLog(line) {
|
|
207
|
-
try {
|
|
208
|
-
const logDir = process.env.EVOLVER_HOOK_LOG_DIR
|
|
209
|
-
|| path.join(os.homedir(), '.evolver', 'logs');
|
|
210
|
-
fs.mkdirSync(logDir, { recursive: true });
|
|
211
|
-
fs.appendFileSync(
|
|
212
|
-
path.join(logDir, 'evolution.log'),
|
|
213
|
-
`${new Date().toISOString()} ${line}\n`,
|
|
214
|
-
'utf8'
|
|
215
|
-
);
|
|
216
|
-
} catch { /* best-effort, never break the hook on log write */ }
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function main() {
|
|
220
|
-
let inputData = '';
|
|
221
|
-
let handled = false;
|
|
222
|
-
let watchdog = null;
|
|
223
|
-
const finish = (payload) => {
|
|
224
|
-
if (handled) return;
|
|
225
|
-
handled = true;
|
|
226
|
-
if (watchdog) clearTimeout(watchdog);
|
|
227
|
-
process.stdout.write(JSON.stringify(payload || {}));
|
|
228
|
-
process.exit(0);
|
|
229
|
-
};
|
|
230
|
-
process.stdin.setEncoding('utf8');
|
|
231
|
-
process.stdin.on('data', chunk => { inputData += chunk; });
|
|
232
|
-
process.stdin.on('end', () => {
|
|
233
|
-
if (handled) return;
|
|
234
|
-
// recordToHub is async (uses the shared Hub fetch transport); wrap the rest of the
|
|
235
|
-
// handler in an immediately-invoked async function so we can await it
|
|
236
|
-
// while still honouring the watchdog timeout and the `handled` guard.
|
|
237
|
-
(async () => {
|
|
238
|
-
try {
|
|
239
|
-
const diffInfo = getGitDiffStats();
|
|
240
|
-
|
|
241
|
-
if (!diffInfo.hasChanges) {
|
|
242
|
-
// No git diff means no signal source — session-end derives the
|
|
243
|
-
// outcome (status/score/signals/summary) entirely from the diff, so
|
|
244
|
-
// there is nothing meaningful to record. This is expected in a
|
|
245
|
-
// non-git workspace or a repo with no changes this session. Rather
|
|
246
|
-
// than fabricate an empty outcome (which would pollute the memory
|
|
247
|
-
// graph), record nothing — but leave a log breadcrumb so the user
|
|
248
|
-
// can tell "evolver ran but had nothing to record" apart from
|
|
249
|
-
// "evolver never fired".
|
|
250
|
-
const reason = diffInfo.isRepo
|
|
251
|
-
? 'no changes detected this session'
|
|
252
|
-
: 'not a git workspace';
|
|
253
|
-
appendEvolutionLog(`[Evolution] Session end: nothing recorded (${reason}).`);
|
|
254
|
-
finish({});
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const signals = detectSignals(diffInfo.diffSnippet);
|
|
259
|
-
if (signals.length === 0) signals.push('stable_success_plateau');
|
|
260
|
-
|
|
261
|
-
const hasErrors = signals.includes('log_error') || signals.includes('test_failure');
|
|
262
|
-
const status = hasErrors ? 'failed' : 'success';
|
|
263
|
-
const score = hasErrors ? 0.3 : 0.8;
|
|
264
|
-
|
|
265
|
-
const outcome = {
|
|
266
|
-
geneId: 'ad_hoc',
|
|
267
|
-
signals,
|
|
268
|
-
status,
|
|
269
|
-
score,
|
|
270
|
-
summary: `Session end: ${diffInfo.summary}. Signals: [${signals.join(', ')}]`,
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const evolverRoot = findEvolverRoot();
|
|
274
|
-
const graphPath = findMemoryGraph(evolverRoot);
|
|
275
|
-
|
|
276
|
-
// Local first: recordToHub is async with an 8s socket timeout, but
|
|
277
|
-
// the 7s watchdog (setTimeout below) will process.exit(0) before a
|
|
278
|
-
// slow hub returns — so anything sequenced after `await recordToHub`
|
|
279
|
-
// can be silently skipped. recordToLocal is the reliable offline
|
|
280
|
-
// fallback and must run regardless of hub latency.
|
|
281
|
-
const localOk = graphPath ? recordToLocal(graphPath, outcome) : false;
|
|
282
|
-
const hubOk = await recordToHub(outcome);
|
|
283
|
-
|
|
284
|
-
const target = hubOk ? 'Hub' : localOk ? 'local memory' : 'nowhere (no Hub or local path)';
|
|
285
|
-
const msg = `[Evolution] Session outcome recorded to ${target}: ${outcome.summary}`;
|
|
286
|
-
|
|
287
|
-
// Stop hook output schema (per Claude Code docs):
|
|
288
|
-
// - decision: "approve" | "block"
|
|
289
|
-
// - reason: string (shown when decision is set)
|
|
290
|
-
// - systemMessage: string (notification displayed to user)
|
|
291
|
-
// - continue: boolean
|
|
292
|
-
// - stopReason: string
|
|
293
|
-
//
|
|
294
|
-
// Earlier versions emitted `followup_message`, `stopMessage`, and
|
|
295
|
-
// `additionalContext` together. `followup_message` is the field that
|
|
296
|
-
// re-injects the receipt into Claude's next inference round, which
|
|
297
|
-
// caused the agent to "respond" to its own evolution log line —
|
|
298
|
-
// visible to users as an unexplained extra reasoning turn after
|
|
299
|
-
// every task. The evolver is supposed to be observational, so we
|
|
300
|
-
// now use `systemMessage` only — that surfaces the receipt to the
|
|
301
|
-
// user without forcing another inference round.
|
|
302
|
-
//
|
|
303
|
-
// Cursor compatibility: Cursor's Claude Code-compatible runtime
|
|
304
|
-
// currently treats `systemMessage` as a user prompt for the next
|
|
305
|
-
// inference round. When we detect Cursor, omit systemMessage too.
|
|
306
|
-
// The receipt is always appended to ~/.evolver/logs/evolution.log
|
|
307
|
-
// so it is never silently lost; users can opt back in to the inline
|
|
308
|
-
// notification with EVOLVER_HOOK_VERBOSE=1.
|
|
309
|
-
appendEvolutionLog(msg);
|
|
310
|
-
|
|
311
|
-
finish(isCursorHost() ? {} : { systemMessage: msg });
|
|
312
|
-
} catch (e) {
|
|
313
|
-
finish({});
|
|
314
|
-
}
|
|
315
|
-
})();
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
watchdog = setTimeout(() => finish({}), 7000);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
main();
|