From c6a645197823bad68698559136feadef18a55cd8 Mon Sep 17 00:00:00 2001 From: Christopher Date: Tue, 14 Apr 2026 04:52:12 +0000 Subject: [PATCH 1/2] fix(paths): separate config dir from AGENTV_HOME data dir Lightweight config/cache files (version-check.json, last-config.json, projects.yaml) now always resolve to ~/.agentv regardless of AGENTV_HOME. Previously these files would silently move when AGENTV_HOME was set to relocate heavy data to another drive, causing inconsistent behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/cli/src/commands/eval/last-config.ts | 4 ++-- apps/cli/src/update-check.ts | 4 ++-- .../content/docs/docs/evaluation/running-evals.mdx | 2 +- packages/core/src/benchmarks.ts | 4 ++-- packages/core/src/index.ts | 1 + packages/core/src/paths.ts | 14 ++++++++++++++ packages/core/test/paths.test.ts | 10 ++++++++++ 7 files changed, 32 insertions(+), 7 deletions(-) diff --git a/apps/cli/src/commands/eval/last-config.ts b/apps/cli/src/commands/eval/last-config.ts index 37dd7cf44..978fb1bca 100644 --- a/apps/cli/src/commands/eval/last-config.ts +++ b/apps/cli/src/commands/eval/last-config.ts @@ -1,8 +1,8 @@ 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 AGENTV_DIR = getAgentvConfigDir(); const LAST_CONFIG_PATH = path.join(AGENTV_DIR, 'last-config.json'); export interface LastConfig { diff --git a/apps/cli/src/update-check.ts b/apps/cli/src/update-check.ts index cae54fdb6..f46798a1f 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 AGENTV_DIR = getAgentvConfigDir(); const CACHE_FILE = 'version-check.json'; const NPM_REGISTRY_URL = 'https://registry.npmjs.org/agentv/latest'; 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..7214dc405 100644 --- a/packages/core/src/benchmarks.ts +++ b/packages/core/src/benchmarks.ts @@ -21,7 +21,7 @@ import path from 'node:path'; import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'; -import { getAgentvHome } from './paths.js'; +import { getAgentvConfigDir } from './paths.js'; // ── Types ─────────────────────────────────────────────────────────────── @@ -40,7 +40,7 @@ export interface BenchmarkRegistry { // ── Registry path ─────────────────────────────────────────────────────── export function getBenchmarksRegistryPath(): string { - return path.join(getAgentvHome(), 'projects.yaml'); + return path.join(getAgentvConfigDir(), 'projects.yaml'); } // ── Load / Save ───────────────────────────────────────────────────────── 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')); + }); }); From b04f1819cc71d0d9dda9229d9fa2243a6772de08 Mon Sep 17 00:00:00 2001 From: Christopher Date: Tue, 14 Apr 2026 05:52:22 +0000 Subject: [PATCH 2/2] fix(paths): add projects.yaml migration and rename AGENTV_DIR to CONFIG_DIR Addresses code review feedback: - One-time migration copies projects.yaml from AGENTV_HOME to ~/.agentv so existing users don't lose their Studio benchmark registry - Renames AGENTV_DIR to CONFIG_DIR in switched files for clarity Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/cli/src/commands/eval/last-config.ts | 6 ++--- apps/cli/src/update-check.ts | 6 ++--- packages/core/src/benchmarks.ts | 30 +++++++++++++++++++++-- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/apps/cli/src/commands/eval/last-config.ts b/apps/cli/src/commands/eval/last-config.ts index 978fb1bca..44402a4e3 100644 --- a/apps/cli/src/commands/eval/last-config.ts +++ b/apps/cli/src/commands/eval/last-config.ts @@ -2,8 +2,8 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { getAgentvConfigDir } from '@agentv/core'; -const AGENTV_DIR = getAgentvConfigDir(); -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 f46798a1f..d50ad7751 100644 --- a/apps/cli/src/update-check.ts +++ b/apps/cli/src/update-check.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { getAgentvConfigDir } from '@agentv/core'; const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours -const AGENTV_DIR = getAgentvConfigDir(); +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/packages/core/src/benchmarks.ts b/packages/core/src/benchmarks.ts index 7214dc405..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 { getAgentvConfigDir } from './paths.js'; +import { getAgentvConfigDir, getAgentvHome } from './paths.js'; // ── Types ─────────────────────────────────────────────────────────────── @@ -43,10 +51,28 @@ export function getBenchmarksRegistryPath(): string { 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: [] }; }