* fix(#3203): stop npm-global installs validating bundled agents against themselves getAgentsDir's claude branch derived the agents directory from __dirname, which is correct for repo runs and runtime-config-dir installs (where <root>/../agents IS the user's agents dir) but on an npm-global install resolves to the package's own bundled agents/, so checkAgentsInstalled validated the package against itself and agents_installed could never be false. The new-project and new-milestone halt/warn gates were silently dead for npm-global users. Keep the install-relative path for the shapes where it is correct, but when it lies inside a node_modules tree — the provably self-validating case — resolve getGlobalConfigDir('claude')/agents like every other runtime, honouring CLAUDE_CONFIG_DIR. GSD_AGENTS_DIR stays priority 1. Repair the doc comment that asserted the __dirname form was correct for both install shapes. Regression test mirrors the published npm-global layout (package under node_modules with a complete bundled agents/) and pins the resolved directory plus the issue's negative control (one agent missing from the config dir → agents_installed:false). Verified red against pre-fix code, green post-fix; the repo-layout W010 health test stays green. * chore(#3203): set changeset fragment pr to 3229 * docs(#3203): describe the node_modules guard as lexical, in CONTEXT.md and at the call site The Agent Install Check Module glossary entry asserted that Claude resolves the agents directory `__dirname`-relative unconditionally. That is the premise this PR falsified: on an npm-global install the install-relative path resolves to the package's own bundled `agents/`, so the check validated the package against itself and `agents_installed` could never be false. The inline doc comment above `getAgentsDir` was repaired with the fix; this external predicate was left behind and has been false since. CONTRIBUTING.md's `Fixed`-fragment docs exemption names this case explicitly — "Edit the docs anyway if a fix corrects something the docs got wrong." Both surfaces now describe the guard as what it is: an exact, case-sensitive path-segment test that TARGETS those layouts rather than detecting them, so neither claims more certainty than the predicate has. The call-site comment carried the same conflation the glossary did. A path merely carrying a directory of that name resolves the same way — the edge already disclosed on this PR — and a non-empty GSD_AGENTS_DIR overrides it. Comment-only in `src/`; no behaviour change. `CONFIG.LOCATION.SEAM.two-families` needs no change: `GSD_AGENTS_DIR -> getAgentsDir priority 1` is still accurate. --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
@@ -81,9 +81,17 @@ function truncatePostureValue(value: string): string {
|
||||
*
|
||||
* Priority:
|
||||
* 1. GSD_AGENTS_DIR env var (explicit override, any runtime)
|
||||
* 2. For claude runtime: __dirname-relative path (agents/ sibling of gsd-core/)
|
||||
* This is correct for both repo runs and real installs (the runtime config dir's
|
||||
* agents/ folder) because gsd-tools.cjs lives inside gsd-core/bin/ in both cases.
|
||||
* 2. For claude runtime: __dirname-relative path (agents/ sibling of
|
||||
* gsd-core/) — correct for repo runs and runtime-config-dir installs,
|
||||
* where the sibling agents/ IS the user's agents dir — UNLESS that path
|
||||
* carries an exact node_modules segment. gsd-tools.cjs lives inside
|
||||
* gsd-core/bin/ in every install shape, but on an npm-global install
|
||||
* gsd-core/ sits inside the package (not the runtime config dir) and the
|
||||
* package ships its own agents/, so the install-relative path resolves
|
||||
* to the bundled copy and the check validates the package against
|
||||
* itself — agents_installed can never be false. In that case resolve
|
||||
* getGlobalConfigDir('claude')/agents (honours CLAUDE_CONFIG_DIR) like
|
||||
* every other runtime (#3203).
|
||||
* 3. For non-claude runtimes with a manifest-backed project-local install:
|
||||
* <projectRoot>/<localConfigDir>/agents (or <projectRoot>/agents when
|
||||
* the runtime's local install targets the project root). Requiring the
|
||||
@@ -100,7 +108,16 @@ function getAgentsDir(runtime?: string, projectRoot?: string): string {
|
||||
}
|
||||
const resolved = runtime ?? (process.env['GSD_RUNTIME'] || 'claude');
|
||||
if (resolved === 'claude') {
|
||||
return path.join(__dirname, '..', '..', '..', 'agents');
|
||||
const installRelative = path.join(__dirname, '..', '..', '..', 'agents');
|
||||
// #3203: a lexical guard, not an install-shape test. It targets the
|
||||
// layouts where the sibling agents/ is the package's own bundled copy and
|
||||
// the check would otherwise validate the package against itself; a path
|
||||
// merely carrying a directory of that name resolves the same way, and a
|
||||
// non-empty GSD_AGENTS_DIR overrides both.
|
||||
if (installRelative.split(path.sep).includes('node_modules')) {
|
||||
return path.join(getGlobalConfigDir('claude'), 'agents');
|
||||
}
|
||||
return installRelative;
|
||||
}
|
||||
if (projectRoot) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
|
||||
Reference in New Issue
Block a user