()
| 163 | } |
| 164 | |
| 165 | function findEvolverRoot() { |
| 166 | if (process.env.EVOLVER_ROOT) { |
| 167 | const explicit = process.env.EVOLVER_ROOT; |
| 168 | if (fs.existsSync(path.join(explicit, 'package.json')) && |
| 169 | isEvolverPackageJson(path.join(explicit, 'package.json'))) { |
| 170 | return explicit; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // Dev/repo layout: this file lives at src/adapters/scripts/_runtimePaths.js, |
| 175 | // so `../../..` is the package root. |
| 176 | const repoRoot = path.resolve(__dirname, '..', '..', '..'); |
| 177 | if (fs.existsSync(path.join(repoRoot, 'package.json')) && |
| 178 | isEvolverPackageJson(path.join(repoRoot, 'package.json'))) { |
| 179 | return repoRoot; |
| 180 | } |
| 181 | |
| 182 | // npm-global / npm-local install layout. The hook script may have been |
| 183 | // copied out of the package into `.claude/hooks/` etc., breaking relative |
| 184 | // walks. Use require.resolve to find the installed package authoritatively. |
| 185 | // |
| 186 | // SECURITY: do NOT include `process.cwd()` here. A hostile workspace can |
| 187 | // place its own `node_modules/@evomap/evolver/package.json`, which would |
| 188 | // be selected here and control `findMemoryGraph()` -> the memory graph |
| 189 | // contents become attacker-controlled prompt-injection material in |
| 190 | // `evolver-session-start.js`'s `additionalContext`. Restrict to trusted, |
| 191 | // user/system-scoped install roots (built in `_buildInstallSearchPaths`). |
| 192 | try { |
| 193 | // Allowlist of trusted user/system-scoped install roots. Built by |
| 194 | // _buildInstallSearchPaths() above so the list is one source of truth |
| 195 | // (Apple Silicon Homebrew, Linuxbrew, NVM / fnm / Volta / asdf, |
| 196 | // and Windows %APPDATA%\npm + %ProgramFiles%\nodejs install layouts). |
| 197 | // process.cwd() is intentionally excluded: a hostile workspace can plant |
| 198 | // its own node_modules/@evomap/evolver/package.json which would then |
| 199 | // control findMemoryGraph() and feed attacker-controlled content into |
| 200 | // evolver-session-start.js's additionalContext. |
| 201 | const pkgJson = require.resolve('@evomap/evolver/package.json', { |
| 202 | paths: _buildInstallSearchPaths(), |
| 203 | }); |
| 204 | if (pkgJson && isEvolverPackageJson(pkgJson)) { |
| 205 | return path.dirname(pkgJson); |
| 206 | } |
| 207 | } catch { /* not installed via npm */ } |
| 208 | |
| 209 | const homeSkills = path.join(os.homedir(), 'skills', 'evolver'); |
| 210 | if (fs.existsSync(path.join(homeSkills, 'package.json')) && |
| 211 | isEvolverPackageJson(path.join(homeSkills, 'package.json'))) { |
| 212 | return homeSkills; |
| 213 | } |
| 214 | |
| 215 | return null; |
| 216 | } |
| 217 | |
| 218 | // Resolve the user's PROJECT directory — the workspace the agent is actually |
| 219 | // working in — for git-diff collection and workspace tagging. |
no test coverage detected