()
| 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 |
no test coverage detected