fix(#384): make getAgentsDir runtime-aware so non-Claude installs find agents (#617)

* fix(#384): make getAgentsDir runtime-aware so non-Claude installs find agents

getAgentsDir() ignored the active runtime and always returned the
claude-family __dirname-relative agents path, so on OpenCode (and other
non-Claude runtimes) checkAgentsInstalled() looked in the wrong directory
and reported agents missing even when installed. Resolve the per-runtime
global config dir via getGlobalConfigDir(runtime) (GSD_AGENTS_DIR env >
runtime arg > GSD_RUNTIME env > 'claude'), and surface agent_runtime and
agents_dir through withProjectRoot() so init diagnostics show which
directory was checked. Updates the stale W010 health-check test that
relied on the old __dirname path.

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

* chore(#384): set changeset pr to 617

* fix(#384): keep claude on __dirname agents path; fix test lint + leak

Address CI failures on the first cut:
- getAgentsDir now only routes NON-claude runtimes through
  getGlobalConfigDir(runtime); claude retains the original __dirname-
  relative agents path, which correctly resolves to <repo>/agents for
  repo runs and the runtime config agents dir for real installs. This
  restores validate-health / W010 checks that were regressing because the
  claude default had moved off the repo-relative path.
- Revert the now-unneeded GSD_AGENTS_DIR workaround in
  agent-install-validation.test.cjs (back to its next state).
- bug-384 test: use helpers.cleanup() instead of raw fs.rmSync()
  (local/no-raw-rmsync-in-tests) and drop an unused var.
- Reword a doc comment that contained a literal ~/.claude/agents path,
  which tripped the cline-install no-leaked-paths scanner.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-02 16:14:33 -04:00
committed by GitHub
parent 2726af1246
commit 8c79044298
4 changed files with 185 additions and 6 deletions

View File

@@ -36,6 +36,7 @@ const {
findContextMdIn,
} = planningWorkspace;
import { findProjectRoot } from './project-root.cjs';
import { getGlobalConfigDir } from './runtime-homes.cjs';
// ─── Configuration Module (generated CJS mirror) ────────────────────────────
import { CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS, normalizeLegacyKeys } from './configuration.cjs';
@@ -1198,13 +1199,26 @@ function getRoadmapPhaseInternal(cwd: string, phaseNum: unknown): RoadmapPhaseRe
// ─── Agent installation validation (#1371) ───────────────────────────────────
/**
* Resolve the agents directory from the GSD install location.
* Resolve the agents directory for the given runtime.
*
* Priority:
* 1. GSD_AGENTS_DIR env var (explicit override, any runtime)
* 2. For claude runtime: __dirname-relative path (agents/ sibling of get-shit-done/)
* This is correct for both repo runs and real installs (the runtime config dir's
* agents/ folder) because gsd-tools.cjs lives inside get-shit-done/bin/ in both cases.
* 3. For non-claude runtimes: getGlobalConfigDir(runtime)/agents
*
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
*/
function getAgentsDir(): string {
function getAgentsDir(runtime?: string): string {
if (process.env['GSD_AGENTS_DIR']) {
return process.env['GSD_AGENTS_DIR'];
}
return path.join(__dirname, '..', '..', '..', 'agents');
const resolved = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
if (resolved === 'claude') {
return path.join(__dirname, '..', '..', '..', 'agents');
}
return path.join(getGlobalConfigDir(resolved), 'agents');
}
interface AgentsInstalledResult {
@@ -1212,13 +1226,17 @@ interface AgentsInstalledResult {
missing_agents: string[];
installed_agents: string[];
agents_dir: string;
agent_runtime: string;
}
/**
* Check which GSD agents are installed on disk.
*
* @param runtime - the active runtime name; defaults to GSD_RUNTIME env, then 'claude'
*/
function checkAgentsInstalled(): AgentsInstalledResult {
const agentsDir = getAgentsDir();
function checkAgentsInstalled(runtime?: string): AgentsInstalledResult {
const resolvedRuntime = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
const agentsDir = getAgentsDir(resolvedRuntime);
const expectedAgents = Object.keys(MODEL_PROFILES);
const installed: string[] = [];
const missing: string[] = [];
@@ -1229,6 +1247,7 @@ function checkAgentsInstalled(): AgentsInstalledResult {
missing_agents: expectedAgents,
installed_agents: [],
agents_dir: agentsDir,
agent_runtime: resolvedRuntime,
};
}
@@ -1248,6 +1267,7 @@ function checkAgentsInstalled(): AgentsInstalledResult {
missing_agents: missing,
installed_agents: installed,
agents_dir: agentsDir,
agent_runtime: resolvedRuntime,
};
}

View File

@@ -85,9 +85,12 @@ function getLatestCompletedMilestone(cwd: string): { version: string; name: stri
function withProjectRoot(cwd: string, result: Record<string, unknown>): Record<string, unknown> {
result['project_root'] = cwd;
const agentStatus = checkAgentsInstalled();
const activeRuntime = resolveRuntime(cwd);
const agentStatus = checkAgentsInstalled(activeRuntime);
result['agents_installed'] = agentStatus.agents_installed;
result['missing_agents'] = agentStatus.missing_agents;
result['agents_dir'] = agentStatus.agents_dir;
result['agent_runtime'] = agentStatus.agent_runtime;
const config = loadConfig(cwd);
if (config.response_language) {
result['response_language'] = config.response_language;