diff --git a/apps/cli/src/commands/eval/last-config.ts b/apps/cli/src/commands/eval/last-config.ts index 37dd7cf44..44402a4e3 100644 --- a/apps/cli/src/commands/eval/last-config.ts +++ b/apps/cli/src/commands/eval/last-config.ts @@ -1,9 +1,9 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; -import { getAgentvHome } from '@agentv/core'; +import { getAgentvConfigDir } from '@agentv/core'; -const AGENTV_DIR = getAgentvHome(); -const LAST_CONFIG_PATH = path.join(AGENTV_DIR, 'last-config.json'); +const CONFIG_DIR = getAgentvConfigDir(); +const LAST_CONFIG_PATH = path.join(CONFIG_DIR, 'last-config.json'); export interface LastConfig { readonly timestamp: string; @@ -25,6 +25,6 @@ export async function loadLastConfig(): Promise { } export async function saveLastConfig(config: LastConfig): Promise { - await mkdir(AGENTV_DIR, { recursive: true }); + await mkdir(CONFIG_DIR, { recursive: true }); await writeFile(LAST_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8'); } diff --git a/apps/cli/src/update-check.ts b/apps/cli/src/update-check.ts index cae54fdb6..d50ad7751 100644 --- a/apps/cli/src/update-check.ts +++ b/apps/cli/src/update-check.ts @@ -1,10 +1,10 @@ import { spawn } from 'node:child_process'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { getAgentvHome } from '@agentv/core'; +import { getAgentvConfigDir } from '@agentv/core'; const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours -const AGENTV_DIR = getAgentvHome(); +const CONFIG_DIR = getAgentvConfigDir(); const CACHE_FILE = 'version-check.json'; const NPM_REGISTRY_URL = 'https://registry.npmjs.org/agentv/latest'; @@ -17,7 +17,7 @@ export interface UpdateCache { * Read the cached update info from disk. Returns null if missing or malformed. */ export async function getCachedUpdateInfo(path?: string): Promise { - const filePath = path ?? join(AGENTV_DIR, CACHE_FILE); + const filePath = path ?? join(CONFIG_DIR, CACHE_FILE); try { const raw = await readFile(filePath, 'utf-8'); const data = JSON.parse(raw); @@ -67,7 +67,7 @@ export function buildNotice(currentVersion: string, latestVersion: string | null * survives even if the parent calls process.exit(). */ export function backgroundUpdateCheck(): void { - const dir = AGENTV_DIR; + const dir = CONFIG_DIR; const filePath = join(dir, CACHE_FILE); const script = ` diff --git a/apps/web/src/content/docs/docs/evaluation/running-evals.mdx b/apps/web/src/content/docs/docs/evaluation/running-evals.mdx index efa3ddc8f..b0e46c911 100644 --- a/apps/web/src/content/docs/docs/evaluation/running-evals.mdx +++ b/apps/web/src/content/docs/docs/evaluation/running-evals.mdx @@ -409,7 +409,7 @@ The `{timestamp}` placeholder is replaced with an ISO-like timestamp (e.g., `202 ### AGENTV_HOME -Override the default `~/.agentv` directory for all global runtime data (workspaces, git cache, subagents, trace state, version check cache): +Override the data directory for heavy runtime artifacts — workspaces, workspace pool, subagents, trace state, git cache, and downloaded dependencies. Lightweight config and cache files (`version-check.json`, `last-config.json`, `projects.yaml`) always stay in `~/.agentv` regardless of this setting. ```bash # Linux/macOS diff --git a/packages/core/src/benchmarks.ts b/packages/core/src/benchmarks.ts index 2fb603b12..53a8a2ed2 100644 --- a/packages/core/src/benchmarks.ts +++ b/packages/core/src/benchmarks.ts @@ -16,12 +16,20 @@ * discoverBenchmarks() to scan a directory tree for `.agentv/` directories. */ -import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'; +import { + copyFileSync, + existsSync, + mkdirSync, + readFileSync, + readdirSync, + statSync, + writeFileSync, +} from 'node:fs'; import path from 'node:path'; import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'; -import { getAgentvHome } from './paths.js'; +import { getAgentvConfigDir, getAgentvHome } from './paths.js'; // ── Types ─────────────────────────────────────────────────────────────── @@ -40,13 +48,31 @@ export interface BenchmarkRegistry { // ── Registry path ─────────────────────────────────────────────────────── export function getBenchmarksRegistryPath(): string { - return path.join(getAgentvHome(), 'projects.yaml'); + return path.join(getAgentvConfigDir(), 'projects.yaml'); +} + +/** + * One-time migration: if projects.yaml exists at the old AGENTV_HOME location + * but not in ~/.agentv, copy it over. This handles the case where users had + * AGENTV_HOME set and projects.yaml was created there before the config/data split. + */ +function migrateProjectsYaml(targetPath: string): void { + const dataHome = getAgentvHome(); + const configDir = getAgentvConfigDir(); + if (dataHome === configDir) return; + const legacyPath = path.join(dataHome, 'projects.yaml'); + if (!existsSync(legacyPath)) return; + mkdirSync(path.dirname(targetPath), { recursive: true }); + copyFileSync(legacyPath, targetPath); } // ── Load / Save ───────────────────────────────────────────────────────── export function loadBenchmarkRegistry(): BenchmarkRegistry { const registryPath = getBenchmarksRegistryPath(); + if (!existsSync(registryPath)) { + migrateProjectsYaml(registryPath); + } if (!existsSync(registryPath)) { return { benchmarks: [] }; } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 79bf7066e..656fd4c26 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -73,6 +73,7 @@ export { type ResultsRepoStatus, } from './evaluation/results-repo.js'; export { + getAgentvConfigDir, getAgentvHome, getWorkspacesRoot, getSubagentsRoot, diff --git a/packages/core/src/paths.ts b/packages/core/src/paths.ts index d23f41d65..e864319ca 100644 --- a/packages/core/src/paths.ts +++ b/packages/core/src/paths.ts @@ -3,6 +3,20 @@ import path from 'node:path'; let logged = false; +/** + * The default config directory (~/.agentv). Always resolves to the user's home + * directory regardless of AGENTV_HOME. Used for lightweight, machine-local files + * like version-check.json, last-config.json, and projects.yaml. + */ +export function getAgentvConfigDir(): string { + return path.join(os.homedir(), '.agentv'); +} + +/** + * The data root for heavy/large artifacts (workspaces, workspace-pool, subagents, + * trace-state, cache, deps). Respects AGENTV_HOME override so users can relocate + * bulky data to a different drive. Falls back to ~/.agentv when unset. + */ export function getAgentvHome(): string { const envHome = process.env.AGENTV_HOME; if (envHome && envHome !== 'undefined') { diff --git a/packages/core/test/paths.test.ts b/packages/core/test/paths.test.ts index 484aac0ba..8de99b3c8 100644 --- a/packages/core/test/paths.test.ts +++ b/packages/core/test/paths.test.ts @@ -4,6 +4,7 @@ import path from 'node:path'; import { _resetLoggedForTesting, + getAgentvConfigDir, getAgentvHome, getSubagentsRoot, getTraceStateRoot, @@ -75,4 +76,13 @@ describe('paths', () => { expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); + + it('getAgentvConfigDir always returns ~/.agentv regardless of AGENTV_HOME', () => { + process.env.AGENTV_HOME = '/data/agentv'; + expect(getAgentvConfigDir()).toBe(path.join(os.homedir(), '.agentv')); + }); + + it('getAgentvConfigDir returns ~/.agentv when AGENTV_HOME is not set', () => { + expect(getAgentvConfigDir()).toBe(path.join(os.homedir(), '.agentv')); + }); });