Repository navigation
feat(hooks): run Evolver from Cursor hooks, with nothing in the model context but the recalled steps (2.2.0) - #10
Conversation
… context but the recalled steps (2.2.0) Aligns the Cursor plugin with dsh-evolver 0.3.0 (#51/#52, check readers from #53): - beforeSubmitPrompt recalls one strategy by the prompt text and adds it as additional_context, ending with "Apply them where they fit."; inline edits (no composer_mode) are left alone. - Tool, edit and response hooks record the turn per generation; stop reports every injected strategy with the turn's evidence and its own end status; aborted, correction and retention follow-ups go through the Proxy outbox. - A verified turn becomes the chat's candidate, dropped by a correction, and is distilled at session end in a detached worker through cursor-agent, headless (-p --output-format json --mode ask, empty workspace), with an env marker that turns this plugin's own hooks off inside that run. - /evolver-* commands are answered by the beforeSubmitPrompt hook itself (continue:false), before any model sees them; /evolver-evolve stays the dsh prompt. - Removes the MCP server, skill, rule, Configure variables, the session-start memory summary, the [Evolution Signal] notice and the git-diff outcome posted to /a2a/evolution/record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sentinel 已审核此变更,并发现 1 个已核验问题。 另有 35 个需要人工确认的风险;黄色 advisory 不阻塞合并,建议开发 agent review advisory 评论并逐条确认。 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 db810724beb0 |
|
Sentinel 保留了 35 个尚未确认的审核候选。这些黄色条目不阻塞合并;红色 P0/P1 与其他独立硬门禁不受影响。 单一猜测、缺少变更行证据或纯 checklist 的候选会统一降为 P3。
另有 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 db810724beb0 |
| } finally { | ||
| if (token) release(lockPath, token); | ||
| } | ||
| } |
There was a problem hiding this comment.
锁等待超时后无锁写入会覆盖并发状态更新
🔴 严重程度:高(P1)
acquire() 在超时时返回 null,但 updateState() 仍继续执行“读旧状态 -> mutate -> 写回”流程,失去互斥保护。两个并发写入可基于同一旧快照写回不同结果,后写者覆盖前写者。
Finding:finding_43abb78af593ad0b14931fcf
…ed one Port of EvoMap/evolver-dsh-plugin#55. Every reuse report (turn end, correction, retention, aborted) now says `applied: used`: the model follows what is injected into its turn, and whether the strategy helped is left to the outcome (last check, or how the turn ended), the next prompt and kept edits. `tier` is L1 whenever a check ran, and the reason no longer names a verdict. A turn that reused an injected strategy is a distillation candidate like any verified turn; the engine's Gene intake drops a repeat of a Gene it has. applied.mjs and the edited-text capture existed only for the verdict and are removed; a turn records the files it edited (afterFileEdit writes, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sentinel 正在审核此变更,尚未形成最终结论。 大 PR 拆片审核中
开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 b783935d765b |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sentinel 已审核此变更,并发现 2 个已核验问题。 另有 42 个需要人工确认的风险;黄色 advisory 不阻塞合并,建议开发 agent review advisory 评论并逐条确认。 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 2ffa85698c67 |
|
Sentinel 保留了 42 个尚未确认的审核候选。这些黄色条目不阻塞合并;红色 P0/P1 与其他独立硬门禁不受影响。 单一猜测、缺少变更行证据或纯 checklist 的候选会统一降为 P3。
另有 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 2ffa85698c67 |
| */ | ||
| export function updateState(target, fallback, mutate) { | ||
| const lockPath = `${target}.lock`; | ||
| const token = acquire(lockPath, Date.now() + LOCK_WAIT_MS); |
There was a problem hiding this comment.
锁等待超时后无锁读改写会覆盖并发状态
🟠 严重程度:中(P2)
获取锁超过两秒后,acquire() 返回 null,但 updateState() 仍执行读、变更和写入;两个并发调用可基于不同的旧快照覆盖彼此更新。
Finding:finding_89d4e570135304d7609d18d6
…saved A turn that reused an injected strategy and whose last check passed adds its saving to the chat's and to the running total in ~/.evolver/state/cursor-savings.json (chats unseen for 8 days drop out of the per-chat map, not out of the total). The saving is savingsCore's reference-reuse estimate, as the Claude Code and Codex plugins show it, from the lines the turn's edits changed, capped at the turn's own tokens from stop (input + output + cache writes). Cursor hooks have no message for the person only, so the figures are shown by /evolver-status, which the plugin answers without the model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sentinel 正在审核此变更,尚未形成最终结论。 大 PR 拆片审核中
开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 bb87a17eb968 |
…s included Undelivered reports, unreported injections, retention checks, chat files and per-chat savings now live 3 days instead of 7-8 (one STATE_TTL_MS), and the background worker drops distillation candidates older than that whoever owns them, so a chat that never closes on a host without a process table no longer keeps its candidate for good. README: Cursor 3.27 loads plugin hooks only while "Include Third-Party Plugins, Skills, and Other Configs" is on; with it off none of this plugin's hooks run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sentinel 已审核此变更,未发现已核验问题,但发现需要人工确认的风险。 另有 42 个需要人工确认的风险;黄色 advisory 不阻塞合并,建议开发 agent review advisory 评论并逐条确认。 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 f5b3ef9c02ad |
|
Sentinel 保留了 42 个尚未确认的审核候选。这些黄色条目不阻塞合并;红色 P0/P1 与其他独立硬门禁不受影响。 单一猜测、缺少变更行证据或纯 checklist 的候选会统一降为 P3。
另有 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 f5b3ef9c02ad |
Each turn that reused an injected strategy is recorded with its turn number, the asset's name and id, the turn's last check and its saving (or none counted), and /evolver-status lists a chat's latest 20 below the totals. The injection ledger keeps the recalled asset's name so the turn end can show it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- sessionEnd ends only the turn it saw settle: a prompt that opened a new turn in the same chat meanwhile keeps its session, injections and candidate (endSession returns false and nothing is cleaned up). - Tool, edit and response hooks record only under their own generation_id; one without it no longer lands in the current turn. - A distillation claim counts only once it is on disk, and candidates are told apart by turn as well as timestamp. - The background worker runs engine setup beside report delivery and distillation, so a slow install no longer holds them back. - Stripe-style keys (sk_live_…, rk_test_…) are redacted. - The global Evolver core is also looked for under %APPDATA%\npm on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sentinel 已审核此变更,未发现已核验问题,但发现需要人工确认的风险。 另有 47 个需要人工确认的风险;黄色 advisory 不阻塞合并,建议开发 agent review advisory 评论并逐条确认。 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 52d04ccac657 |
|
Sentinel 保留了 47 个尚未确认的审核候选。这些黄色条目不阻塞合并;红色 P0/P1 与其他独立硬门禁不受影响。 单一猜测、缺少变更行证据或纯 checklist 的候选会统一降为 P3。
另有 开发 agent 查询详情: 调用 MCP 工具 由 Sentinel 审核 · 目标提交 52d04ccac657 |
Touches hot-path: yes — security-sensitive model/tool execution (hooks spawn a headless model run and the
evolverCLI)Phase: feature
Risk vector: forward
Summary
Ports the Cursor plugin to the hook-only design of dsh-evolver 0.3.0 (EvoMap/evolver-dsh-plugin#51, #52, check/correction readers from #53), matching the Claude Code (EvoMap/evolver-claude-code-mods#8), WorkBuddy (EvoMap/evolver-workbuddy-plugin#10) and Codex (EvoMap/evolver-codex-plugin#10) ports. Version 2.1.0 → 2.2.0 in
plugin.json,marketplace.json(both fields) and the newpackage.json.What changed:
evolver-proxyMCP server (9 tools), theevolverskill,rules/evolution-memory.mdc, the 8 tool-calling commands, the sessionStart memory-graph summary and claim nudge, theafterFileEdit[Evolution Signal]notice, and the Configure variables (they only fed the MCP server).beforeSubmitPromptcallsPOST /asset/fetch {text}and returns the best strategy asadditional_context, ending exactlyApply them where they fit.preToolUse(Write/Delete before-hashes),postToolUse/postToolUseFailure(commands, checks, paths),afterFileEdit(written text),afterAgentResponse(answer);stopreports every injected strategy through the Proxy's/asset/reuse-resultwith graded evidence, task idcursor:<conversation>:<turn>.:correction), retention (:retention), all through the outbox with retries.platform: 'cursor',trigger: 'session_end'; leftovers claimed at the next session start; 3 attempts.cursor--prefixed and written under the shared state lock; the turn counter survives session end (TTL-pruned).POST /a2a/evolution/record(dsh keeps no equivalent); the hooks no longer rungitat all.Follow-up commits on this branch:
b783935,2ffa856): every reuse report saysapplied: used; a verified turn that reused a strategy is distilled too; the applied verdict and edited-text buffer are gone.bb87a17,e724b2d):/evolver-statusopens with what reused strategies saved in this chat and in all chats (savingsCore reference-reuse estimate, only after a passing check, capped at the turn's ownstoptoken counts), then lists this chat's reuses (turn, asset name and id, last check, saving). Cursor hooks have no user-only message, so it is shown on demand by the plugin-run command; a desktop notification and astopfollow-up were rejected.f5b3ef9): reports, injections, retention checks, chat files, per-chat savings and distillation candidates all expire after 3 days (oneSTATE_TTL_MS); stuck candidates included. README: Cursor 3.27 loads plugin hooks only while Include Third-Party Plugins, Skills, and Other Configs is on (loadPluginHooksreturns early otherwise).52d04cc): a latesessionEndno longer cleans up a turn a new prompt opened; hooks record only under their owngeneration_id; a distillation claim counts only once on disk and candidates are matched by turn as well as time; engine setup no longer delays delivery/distillation; Stripe-style keys redacted; Windows%APPDATA%\npmglobal root searched. Left as designed, with reasons: the unguarded update after a 2 s lock wait (spec item 9), the longbeforeSubmitPrompttimeout for the 10-minute CLI commands, and dsh's correction reader.Libraries are the reviewed Codex port's (
evolver-codex-plugin@ 8ffb016), renamed tocursor-; Cursor-specific code islib/hook-io,turn-record,verification(exit-code reader),cursor-agent,process-owner,project-dir,commands(typed-command parser),plugin-commands,recording-hook,session-state(settling) andturn-end(endOpenTurn), plus the hooks.Host adaptations, with evidence
All "source" citations are the local Cursor 3.27.6 bundle (
WB=out/vs/workbench/workbench.desktop.main.js,EXH=out/vs/workbench/api/node/extensionHostProcess.js,AX=extensions/cursor-agent-exec/dist/main.js); the full table is in the shared host-facts note used for the planned monorepo.beforeSubmitPrompt.additional_context, sent as a<system_reminder>with that same requestMr=ny?.additional_context…hookEvent:"beforeSubmitPrompt"; AXbeforeSubmitPromptresponseadditionalContext. Not in Cursor's hook docs (docs list onlycontinue/user_message)composer_modeand ignore added contextexecuteBeforeSubmitPromptHookpayload has nocomposer_modeand reads onlycontinuestop.status(completed/aborted/error)afterAgentResponse(stop carries none)triggerWakeupTurnEndHooksawaits it before stop, completed turns onlypostToolUse.tool_output={"output","exitCode"}— non-zero exit fails, else output is read;postToolUseFailureerror/timeout fails, interrupt orUnknown error(backgrounded) = no verdict,permission_denied= not runjD/WDcreateSuccessOutput,getErrorMessage,isInterruptafterFileEditnew_string, before-hashes frompreToolUse`WriteKD({file_path, content}), docs hooks "afterFileEdit"stopis not awaited before the next prompt (a prompt typed mid-turn aborts it first), so the prompt waits ≤3 s for the turn to settle (closed+settlingstate); a turn with no stop is closed aserrorER=!0,this.triggerStopHook(…)without awaitsessionEndonly writes state, waits ≤2 s for a settling stop, ends a running turn asaborted, starts the detached workeruser_close/window_close, awaited at window shutdowncursor-agent -p --output-format json --mode ask --trust --workspace <empty tmp> [--model m] <prompt>; no--forceso nothing is applied; prompt as argument; nodistill_usagecli/reference/parameters,cli/headless,cli/reference/output-format({type,result,is_error…}, no usage)EVOLVER_HEADLESS=1in the worker's env makes every hook a no-op/cursor/i(ps;/proc/<pid>/exeon Linux)$executeHookDirectspawns hooks from the extension hosthooks.jsonhas"version": 1and only known event names (one unknown name rejects the whole file)Unknown hook type: …; plugin loader runs the same checkcursor-agent.exe/agent.exe(no shell);.cmdshims skippedScope and cuts
Outcome: a Cursor user with the plugin gets one recalled strategy behind each prompt and nothing else of Evolver in the model's context; every reuse is reported with graded evidence at turn end (plus aborted, correction and retention follow-ups); the chat's latest verified turn is distilled into a Gene when the chat closes;
/evolver-*commands run in the plugin.Not cut, but not verified end to end (decided by the reviewer / requester):
beforeSubmitPromptreturned a 10-step Xiaohongshu strategy, the Hooks log shows it merged into the request, and the model followed it;postToolUserecorded the turn. Seen there too:sessionEndat window reload failed inside Cursor (MainThreadShellExec not initialized), which the next session start's leftover claim covers.cursor-agent(not installed here): distillation is exercised with a fake CLI only..exeCLI (above).Module boundary
Plugin-only:
hooks/,lib/,scripts/background-worker.mjs,commands/, manifests, README/CHANGELOG, tests and the CI test glob (test/*.test.mjs). Talks only to the local Proxy (/asset/fetch,/asset/search,/asset/reuse-result,/conversation/distill,/proxy/status), theevolverCLI andcursor-agent. State under~/.evolver/state/cursor-*.Alternative considered
sessionStart.additional_contextfor recall (documented): rejected — it fires once per chat, not per prompt.cursor-agent; the documented positional argument is used.Validation
npm test(node --test test/*.test.mjs): 138 pass on the final head. Covers recall text, turn-end reasons (completed/aborted/error), aborted reports (chat closed mid-turn, killed owner), candidate create/skip/replace/drop and ordering (including concurrent stop + correction), leftover claim and other-host isolation, distillation via a fakecursor-agent(args,platform: 'cursor', default model), headless self-hook guard, turn counter across a reopened chat, plugin commands, inline-edit skip, manifest/hooks.json validity.Rollback / recovery
git revertthis commit restores 2.1.0 (MCP server, skill, rule, old hooks). State written by this version lives only in~/.evolver/state/cursor-*and is ignored by 2.1.0; deleting those files is safe. No schema or Hub contract changes.🤖 Generated with Claude Code