103 Commits

Author SHA1 Message Date
evolver-publish 10212dcfa2 Release v1.89.13 2026-06-16 11:56:39 +08:00
evolver-publish f9b6f0dea5 Release v1.89.11 2026-06-15 11:04:27 +08:00
evolver-publish 51a7464560 Release v1.89.10 2026-06-13 09:34:19 +00:00
evolver-publish 4545bf3513 Release v1.89.9 2026-06-13 05:49:21 +00:00
evolver-publish f7a6de7640 Release v1.89.8 2026-06-13 02:47:38 +00:00
evolver-publish 95e00bd767 Release v1.89.7 2026-06-13 02:18:30 +00:00
OwenShen de547d514a docs(codex): clarify Evolver plugin tool setup (#566) 2026-06-13 01:34:34 +08:00
evolver-publish 504df36105 Release v1.89.6
Co-authored-by: autogame-17 <ram@evomap.work>
2026-06-12 17:08:28 +00:00
evolver-publish db51019f52 Release v1.89.5 2026-06-12 08:17:49 +08:00
autogame-17 83553705d7 fix(sanitize): skip path/URL-shaped env values in reverse leak scan (#568)
detectEnvValueLeaks reverse-scans every process.env value and flags any that
appears verbatim in content. CI tooling exports many env vars whose value is the
repo checkout path: the runner sets GITHUB_WORKSPACE / RUNNER_WORKSPACE, and
`npm test` additionally sets INIT_CWD / npm_config_local_prefix /
npm_package_json / PWD — all = /home/runner/work/evolver/evolver. Each is a
substring of capsule content that legitimately references the build path, so the
reverse scan reported a false-positive leak. This (a) failed
test/sanitize.test.js:280 on every CI run while passing locally, and (b) would
block every self-PR created from CI over its own runner path.

Filesystem paths and URLs are not secrets, so skip path/URL-shaped env values in
the reverse scan. Genuine sensitive paths in content are still caught by the
local_path pattern scanner and credentialed URLs by db_url / basic_auth — the
reverse scan exists for non-pattern-matchable hardcoded secret values, which are
never paths/URLs. Regression test sets the runner/npm checkout-path vars and
asserts fullLeakCheck stays clean, plus asserts a non-path secret value is still
reverse-detected so the security guarantee is locked in.

main CI had been red since at least v1.88.3 (2026-06-06), including on docs-only
commits that never touched sanitize — confirming an environmental (env-dependent)
failure, not a code regression.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 10:08:34 +08:00
evolver-publish 614892cd76 Release v1.89.3 2026-06-10 16:47:05 +08:00
evolver-publish 95c26f5b8f Release v1.89.2 2026-06-09 04:03:05 +00:00
evolver-publish 770d59ae51 Release v1.89.1 2026-06-09 03:44:24 +00:00
seikiko c3a0098cd5 docs: license to GPL-3.0-or-later; drop internal Public Release section; fix issue-repo/runtime refs
- License section now states GPL-3.0-or-later (was stale MIT) in README / README.zh-CN / SKILL.
- Remove the maintainer-only "Public Release" section (non-existent npm scripts + internal publish flow).
- EVOLVER_ISSUE_REPO documented default -> EvoMap/evolver (matches code default).
- Point auto-issue hint (index.js) and skill attribution footer (skillPublisher.js) at EvoMap/evolver.
- Normalize `evolver run` -> `evolver`; rewrite Roadmap into directional items.
- Drop .github/CODEOWNERS from the public mirror.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 02:33:38 +00:00
evolver-publish 1c7fa4cfcb Release v1.88.4 2026-06-08 00:06:58 +08:00
evolver-publish 8e0cbbc97f Release v1.88.3 2026-06-06 18:08:01 +00:00
evolver-publish 534ede263b Release v1.88.2
Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
Co-authored-by: bjw9808 <bjw9808@gmail.com>
Co-authored-by: autogame-17 <166480271+autogame-17@users.noreply.github.com>
2026-06-05 09:29:13 +00:00
evolver-publish a1d22a3d5f Release v1.88.1 2026-06-03 18:04:02 +00:00
evolver-publish f5dbde2836 Release v1.88.0 2026-06-03 08:36:30 +00:00
evolver-publish 12c2992346 Release v1.87.4 2026-06-02 03:52:40 +00:00
autogame-17 63631c1a86 feat(hooks): tell the user when evolution memory is inactive in a non-git folder (#558)
Found via real-Cursor end-to-end testing: in a non-git workspace, session-end
records nothing (every outcome is derived from the git diff) and the only trace
was a line in ~/.evolver/logs/evolution.log the user never sees. So evolver
silently does nothing and the user has no idea why.

session-start now surfaces a one-line notice via additionalContext when the
workspace is not a git repo: "This folder is not a git repository, so evolution
memory is inactive ... run `git init` or open a git project." additionalContext
is injected as opening context and does NOT trigger an extra inference round
(unlike a stop-hook systemMessage, which Cursor mishandles).

The notice is throttled per-folder (30 min) by reusing the session-start dedup
state file; the throttle logic is factored into a shared throttled(key, ttlMs)
helper used by both the Kiro per-prompt dedup and the notice. A new shared
isGitWorkspace() lives in _runtimePaths.js. When a non-git folder DOES have
cwd-tagged memory, the notice and the memory are both shown.

Tests: +4 (notice shown / throttled / not in a git repo / shown alongside
memory). All touched test files pass.

Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 09:51:13 +08:00
autogame-17 fdebedf3d4 fix(hooks): resolve workspace_id via FS fallback when evolver package is absent (#557)
* fix(hooks): resolve workspace_id via FS fallback when evolver package is absent

Found by real-Cursor end-to-end testing: on plugin-only installs (no
@evomap/evolver package on the machine), resolveWorkspaceId could not reach
paths.getWorkspaceId() and returned null. Every session-end entry was then
stamped workspace_id=null, so the forge-resistant workspace scoping silently
degraded to plain cwd-tag matching — isolation still held (cwd was fixed to
the project dir in #554/#555), but the secret-backed tag the docs promise was
never created.

Add a self-contained FS-only fallback in _runtimePaths.js that reads — and
lazily, atomically creates — the per-workspace secret at
<workspaceRoot>/.evolver/workspace-id. It mirrors src/gep/paths.js exactly:
the workspace root (OPENCLAW_WORKSPACE, else the git repo root at/above the
project dir, else the project dir), the 16-byte hex format, 0600 mode,
O_EXCL|O_NOFOLLOW atomic create, and symlink rejection on both the dir and
file. Because the path and format match, a workspace seeded by the fallback
is read back identically by paths.getWorkspaceId() once the package is
installed — verified: both resolve the same id from the same file.

resolveWorkspaceId now takes an optional projectDir so reader and writer
resolve the secret against the same root; both call sites pass the
resolveProjectDir() value they already compute.

Test: test/resolveWorkspaceId.test.js covers env override, stable hex
generation + persistence + 0600, cross-project isolation, git-repo-root
resolution, OPENCLAW_WORKSPACE override, and symlinked-.evolver refusal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(hooks): close two paths.js-parity gaps in the workspace-id FS fallback

Bugbot (PR #557 round-1) found two ways the FS fallback diverged from
src/gep/paths.js, breaking either the read-back-identically guarantee or
its symlink hardening:

1. (Low, security) The EEXIST race re-read used a bare readFileSync with no
   symlink guards, unlike paths.js which re-reads via _readWorkspaceIdFromFs.
   An attacker swapping .evolver for a symlink between the dir lstat and
   openSync could then have the re-read follow it. Factor the guarded read
   into _readWsIdGuarded() and use it for BOTH the initial read and the
   EEXIST re-read; also refuse a pre-existing non-regular/symlinked id file
   instead of creating over it.

2. paths.js getWorkspaceRoot() returns repoRoot/workspace when that subdir
   exists; _fsWorkspaceRoot omitted that step, so projects with a workspace/
   dir wrote the secret to repoRoot/.evolver while the package reads
   repoRoot/workspace/.evolver -- they would never find each other. Mirror
   the workspace/ step.

Verified: a project with a workspace/ subdir now has the fallback and
paths.getWorkspaceId() resolve the same id from the same file.

Tests: +2 (workspace/ subdir parity; symlinked id-file refusal) -> 8 total.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(hooks): guard the whole FS workspace-id resolver against fs throws

Bugbot (PR #557 round-2) found that the bare lstatSync I added in round-1
(to refuse overwriting a guard-rejected id file) sat outside any try-catch.
throwIfNoEntry:false only suppresses ENOENT; EACCES/EIO still throw. Since
evolver-session-start.js main() calls resolveWorkspaceId without a try-catch,
such an error would crash the hook instead of degrading to null -- violating
the documented "returns null on any read/write error" contract.

Wrap the entire _fsWorkspaceId body in a single try-catch so any unexpected
fs throw returns null. The hook then falls back to legacy cwd-tag matching
rather than dying.

Test: regression provoking EACCES via a chmod 000 .evolver dir, asserting
resolveWorkspaceId returns null and does not throw (skipped as root, which
ignores file permissions). 9 tests total.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 09:01:04 +08:00
autogame-17 f225e30368 fix(hooks): workspace-scope memory recall + log a no-changes breadcrumb (#555)
* fix(hooks): scope session-start memory recall to the current workspace

The session-start hook injected the last 5 memory-graph outcomes with no
workspace filter, while the session-end writer already tags every entry
with workspace_id (forge-resistant) and cwd (backward-compat). On
npm-global installs every project shares the user-level fallback graph
(~/.evolver/memory/evolution/memory_graph.jsonl), so project A's session
start would surface project B's outcomes — the cross-project disclosure /
prompt-injection surface Bugbot flagged on the writer side (PR #105
round-2), which the reader never enforced.

Add resolveWorkspaceId() to _runtimePaths.js mirroring the writer's
resolution (EVOLVER_WORKSPACE_ID, then paths.getWorkspaceId() from the
resolved evolver root). session-start now scopes entries to the current
workspace BEFORE taking the most-recent window — filtering after a tail-N
read would let other projects crowd this workspace out of the window
entirely. Untagged legacy/Hub entries and the can't-resolve-id case both
fall through to 'show it', so there is no regression vs. the old unscoped
behavior; only cross-project leakage is removed.

Tests: test/sessionStartScope.test.js covers isolation, the empty case,
legacy pass-through, cwd fallback, and the belongsToWorkspace predicate
branches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(hooks): log a breadcrumb when session-end records nothing

session-end derives the outcome (status/score/signals/summary) entirely
from the git diff. In a non-git workspace, or a repo with no changes this
session, there is no signal source, so the hook correctly records nothing
rather than fabricating an empty outcome that would pollute the memory
graph. But the no-changes branch was fully silent, so a user could not
tell 'evolver ran and had nothing to record' from 'evolver never fired'.

Emit a one-line breadcrumb to evolution.log on that branch, distinguishing
'not a git workspace' from 'no changes detected this session' (a single
cheap rev-parse settles which, since the diff commands can't). No memory
-graph entry is written. Factor the existing inline log-append into a
shared appendEvolutionLog() helper used by both the recorded-outcome and
skip paths.

Tests: two regressions in sessionEndHook.test.js asserting the breadcrumb
is logged and no graph entry is written, for both the non-git and
clean-repo cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(hooks): share one workspace-id resolver between reader and writer

Bugbot (PR #555) flagged that the new _runtimePaths.resolveWorkspaceId
duplicated evolver-session-end.js#resolveWorkspaceIdForWriter. The two
copies were equivalent now, but if either drifted the reader and writer
would resolve different ids — and since the reader filters memory-graph
entries by exact workspace_id match, a drift would silently match nothing
and disable workspace scoping entirely.

Delete resolveWorkspaceIdForWriter and have the writer call the shared
resolveWorkspaceId() (no-arg form is behavior-identical: EVOLVER_WORKSPACE_ID
then paths.getWorkspaceId() from findEvolverRoot()). Single source of truth;
drift is now impossible by construction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(hooks): tag entry.cwd with resolveProjectDir(), not process.cwd()

Bugbot (PR #555 round-2) flagged a reader/writer cwd mismatch: the
session-start reader derives its cwd fallback from resolveProjectDir()
(CURSOR_PROJECT_DIR under Cursor), but the writer still stamped
cwd: process.cwd() — the plugin install dir under Cursor. When an entry
has no workspace_id and belongsToWorkspace() falls to the cwd compare,
the writer's plugin-dir cwd could never equal the reader's project-dir
currentDir, silently hiding every cwd-only entry. The writer also already
used resolveProjectDir() for git-diff collection (#554), making the raw
process.cwd() tag internally inconsistent.

Stamp cwd with resolveProjectDir() so the diff source, the cwd tag, and
the reader's fallback all agree. collect.js uses cwd only as a legacy
fallback (disabled once a workspace_id secret exists), and the tag is
still a directory path, so its scoping contract is unchanged.

Test: regression asserting entry.cwd == CURSOR_PROJECT_DIR (not the hook
process cwd) under a simulated Cursor host.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* perf(hooks): bound session-start memory parse instead of parsing the whole file

Bugbot (PR #555 round-3) flagged that the workspace-scoping fix replaced
readLastN (which read the whole file but parsed only the last 5 lines)
with readAllEntries, which JSON-parses every line. The memory graph can
reach ~100 MB before rotation, so parsing hundreds of thousands of
entries on every session start is real CPU/memory cost.

Replace readAllEntries with readRecentWorkspaceEntries: it still reads the
file (cheap, as readLastN did) but parses lines lazily from the newest end,
keeps only workspace matches, and stops once it has N. Parse count is
bounded by where this workspace's N-th-most-recent entry sits, not by total
file size — while preserving scope-before-trim correctness (other projects
can't crowd this workspace out of the window).

Test: a 200-other-entry graph with this workspace's entries behind them,
asserting the recent own entry still surfaces with no cross-project leak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 03:40:45 +08:00
autogame-17 39c0d3b8a4 fix(hooks): resolve project dir from host env, not process.cwd() (#554)
Cursor invokes hook events (e.g. afterFileEdit) with the working
directory set to the plugin install dir, not the opened workspace.
evolver-session-end.js ran git diff against process.cwd() and therefore
found no changes inside the plugin dir, silently recording nothing for
every Cursor task.

Add resolveProjectDir() to _runtimePaths.js: prefer CURSOR_PROJECT_DIR,
then CLAUDE_PROJECT_DIR (Claude Code, and Cursor compat alias), then fall
back to process.cwd(). Only honor an env value that points at an existing
directory so a stale value cannot redirect git collection. Codex,
opencode, Kiro and direct CLI usage leave both env vars unset, so cwd
remains the source there — a no-op on those platforms.

getGitDiffStats() now uses resolveProjectDir(). The workspace-tag cwd in
recordToLocal() is intentionally left on process.cwd(): the review-time
reader uses it for backward-compat scoping while the forge-resistant tag
is workspace_id, so changing the writer there would risk a silent
reader/writer mismatch.

Tests: new test/resolveProjectDir.test.js (priority order, stale/empty/
file env values) plus two session-end regressions proving a diff is
recorded via CURSOR_PROJECT_DIR / CLAUDE_PROJECT_DIR when cwd is wrong.

Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:50:30 +08:00
evolver-publish 4854a3b9b9 Release v1.87.3 2026-06-01 15:20:47 +00:00
evolver-publish 094989c41b Release v1.87.2 2026-05-27 12:50:00 +00:00
evolver-publish 6a7a312a39 Release v1.87.1 2026-05-27 09:01:45 +00:00
evolver-publish 687aebd809 Release v1.87.0 2026-05-26 05:32:31 +00:00
autogame-17 4227d26b7f fix: harden setup-hooks clean install path (#545)
Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
2026-05-26 00:05:19 +08:00
autogame-17 3d7804f949 refactor: optimize signal detection with context-aware stratification
- Add stratifyContent() to separate code/comments from document text
- Reduce false positives when detecting signals on file edits
- Filter out JSON fields and code comments that contain keywords like 'error'
- Only check document text for signal keywords

Expected impact: 10-15% reduction in spurious signal detection

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 13:10:18 +00:00
autogame-17 44ee85bb0c refactor: optimize evolution memory filtering for session context
- Extract filterRelevantOutcomes logic to shared _memoryFiltering.js module
- Filter outcomes by: success status + score >= 0.5 + within 7 days
- Reduce injected memory from 5 to ~2-3 relevant outcomes
- Support configuration via opts parameter (minScore, maxAgeMs, maxOutcomes)
- Enable cross-platform reuse (Claude Code, Codex, etc.)

Expected impact: 30-50% reduction in evolution memory noise

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 12:57:29 +00:00
evolver-publish 3d5386cfe1 Release v1.86.0 2026-05-23 08:30:01 +00:00
evolver-publish 0d62784919 Release v1.85.3 2026-05-23 04:30:17 +00:00
evolver-publish 3ec75ca16d Release v1.85.2 2026-05-22 14:50:45 +00:00
evolver-publish 2eadd2b618 Release v1.85.1 2026-05-22 11:04:53 +00:00
evolver-publish 85ac5b6107 Release v1.85.0 2026-05-22 04:07:55 +00:00
evolver-publish 211c2f6f87 Release v1.84.2 2026-05-22 03:15:04 +00:00
evolver-publish 4928f05d1d Release v1.84.1 2026-05-18 20:09:38 +00:00
evolver-publish cde44d0ce0 Release v1.84.0
Co-authored-by: autogame-17 <autogame-17@users.noreply.github.com>
2026-05-17 03:24:53 +00:00
evolver-publish 79aac75343 Release v1.83.0 2026-05-16 13:30:17 +00:00
evolver-publish f08beccc1d Release v1.82.1 2026-05-16 11:38:36 +00:00
evolver-publish 3dfb136b22 Release v1.82.0 2026-05-16 10:21:43 +00:00
evolver-publish 7c3f103093 Release v1.81.0 2026-05-16 09:19:59 +00:00
evolver-publish 0bca687d5e Release v1.80.9 2026-05-16 07:14:57 +00:00
evolver-publish 65269ec7a0 Release v1.80.8 2026-05-16 05:36:11 +00:00
evolver-publish 17e1c79bbb Release v1.80.7 2026-05-09 10:55:54 +00:00
evolver-publish 9f7b0c5249 Release v1.80.6 2026-05-09 09:04:21 +00:00
evolver-publish eac8f01c3b Release v1.80.5 2026-05-09 05:32:55 +00:00
evolver-publish 1cee702ee9 Release v1.80.4 2026-05-08 12:50:51 +00:00
evolver-publish 147d428cf3 Release v1.80.3 2026-05-07 13:19:12 +00:00