diff --git a/get-shit-done/bin/lib/config.cjs b/get-shit-done/bin/lib/config.cjs index 079ba6463..c7c678bce 100644 --- a/get-shit-done/bin/lib/config.cjs +++ b/get-shit-done/bin/lib/config.cjs @@ -4,7 +4,7 @@ const fs = require('fs'); const path = require('path'); -const { output, error, planningRoot, withPlanningLock, CONFIG_DEFAULTS, atomicWriteFileSync } = require('./core.cjs'); +const { output, error, planningDir, withPlanningLock, CONFIG_DEFAULTS, atomicWriteFileSync } = require('./core.cjs'); const { VALID_PROFILES, getAgentToModelMapForProfile, @@ -200,7 +200,7 @@ function buildNewProjectConfig(userChoices) { * Idempotent: if config.json already exists, returns { created: false }. */ function cmdConfigNewProject(cwd, choicesJson, raw) { - const planningBase = planningRoot(cwd); + const planningBase = planningDir(cwd); const configPath = path.join(planningBase, 'config.json'); // Idempotent: don't overwrite existing config @@ -245,7 +245,7 @@ function cmdConfigNewProject(cwd, choicesJson, raw) { * the happy path. But note that `error()` will still `exit(1)` out of the process. */ function ensureConfigFile(cwd) { - const planningBase = planningRoot(cwd); + const planningBase = planningDir(cwd); const configPath = path.join(planningBase, 'config.json'); // Ensure .planning directory exists @@ -295,7 +295,7 @@ function cmdConfigEnsureSection(cwd, raw) { * the happy path. But note that `error()` will still `exit(1)` out of the process. */ function setConfigValue(cwd, keyPath, parsedValue) { - const configPath = path.join(planningRoot(cwd), 'config.json'); + const configPath = path.join(planningDir(cwd), 'config.json'); return withPlanningLock(cwd, () => { // Load existing config or start with empty object @@ -368,7 +368,7 @@ function cmdConfigSet(cwd, keyPath, value, raw) { } function cmdConfigGet(cwd, keyPath, raw, defaultValue) { - const configPath = path.join(planningRoot(cwd), 'config.json'); + const configPath = path.join(planningDir(cwd), 'config.json'); const hasDefault = defaultValue !== undefined; if (!keyPath) { diff --git a/get-shit-done/bin/lib/core.cjs b/get-shit-done/bin/lib/core.cjs index d29eedc8b..92280e59b 100644 --- a/get-shit-done/bin/lib/core.cjs +++ b/get-shit-done/bin/lib/core.cjs @@ -692,19 +692,23 @@ function planningRoot(cwd) { } /** - * Get common .planning file paths, workstream-aware. - * Scoped paths (state, roadmap, phases, requirements) resolve to the active workstream. - * Shared paths (project, config) always resolve to the root .planning/. + * Get common .planning file paths, project-and-workstream-aware. + * + * All paths route through planningDir(cwd, ws), which honors the GSD_PROJECT + * env var and active workstream. This matches loadConfig() above (line 256), + * which has always read config.json via planningDir(cwd). Previously project + * and config were resolved against the unrouted .planning/ root, which broke + * `gsd-tools config-get` in multi-project layouts (the CRUD writers and the + * reader pointed at different files). */ function planningPaths(cwd, ws) { const base = planningDir(cwd, ws); - const root = path.join(cwd, '.planning'); return { planning: base, state: path.join(base, 'STATE.md'), roadmap: path.join(base, 'ROADMAP.md'), - project: path.join(root, 'PROJECT.md'), - config: path.join(root, 'config.json'), + project: path.join(base, 'PROJECT.md'), + config: path.join(base, 'config.json'), phases: path.join(base, 'phases'), requirements: path.join(base, 'REQUIREMENTS.md'), }; diff --git a/get-shit-done/bin/lib/template.cjs b/get-shit-done/bin/lib/template.cjs index cd6a47785..95b692cb0 100644 --- a/get-shit-done/bin/lib/template.cjs +++ b/get-shit-done/bin/lib/template.cjs @@ -4,7 +4,7 @@ const fs = require('fs'); const path = require('path'); -const { normalizePhaseName, findPhaseInternal, generateSlugInternal, normalizeMd, toPosixPath, output, error } = require('./core.cjs'); +const { normalizePhaseName, findPhaseInternal, generateSlugInternal, normalizeMd, toPosixPath, planningDir, output, error } = require('./core.cjs'); const { reconstructFrontmatter } = require('./frontmatter.cjs'); function cmdTemplateSelect(cwd, planPath, raw) { @@ -131,6 +131,10 @@ function cmdTemplateFill(cwd, templateType, options, raw) { must_haves: { truths: [], artifacts: [], key_links: [] }, ...fields, }; + const planBase = planningDir(cwd); + const projectRef = toPosixPath(path.relative(cwd, path.join(planBase, 'PROJECT.md'))); + const roadmapRef = toPosixPath(path.relative(cwd, path.join(planBase, 'ROADMAP.md'))); + const stateRef = toPosixPath(path.relative(cwd, path.join(planBase, 'STATE.md'))); body = [ `# Phase ${options.phase} Plan ${planNum}: [Title]`, '', @@ -140,9 +144,9 @@ function cmdTemplateFill(cwd, templateType, options, raw) { '- **Output:** [Concrete deliverable]', '', '## Context', - '@.planning/PROJECT.md', - '@.planning/ROADMAP.md', - '@.planning/STATE.md', + `@${projectRef}`, + `@${roadmapRef}`, + `@${stateRef}`, '', '## Tasks', '', diff --git a/get-shit-done/bin/lib/verify.cjs b/get-shit-done/bin/lib/verify.cjs index 396888961..749753e41 100644 --- a/get-shit-done/bin/lib/verify.cjs +++ b/get-shit-done/bin/lib/verify.cjs @@ -5,7 +5,7 @@ const fs = require('fs'); const path = require('path'); const os = require('os'); -const { safeReadFile, loadConfig, normalizePhaseName, escapeRegex, execGit, findPhaseInternal, getMilestoneInfo, stripShippedMilestones, extractCurrentMilestone, planningDir, planningRoot, output, error, checkAgentsInstalled, CONFIG_DEFAULTS } = require('./core.cjs'); +const { safeReadFile, loadConfig, normalizePhaseName, escapeRegex, execGit, findPhaseInternal, getMilestoneInfo, stripShippedMilestones, extractCurrentMilestone, planningDir, output, error, checkAgentsInstalled, CONFIG_DEFAULTS } = require('./core.cjs'); const { extractFrontmatter, parseMustHavesBlock } = require('./frontmatter.cjs'); const { writeStateMd } = require('./state.cjs'); @@ -534,11 +534,10 @@ function cmdValidateHealth(cwd, options, raw) { } const planBase = planningDir(cwd); - const planRoot = planningRoot(cwd); - const projectPath = path.join(planRoot, 'PROJECT.md'); + const projectPath = path.join(planBase, 'PROJECT.md'); const roadmapPath = path.join(planBase, 'ROADMAP.md'); const statePath = path.join(planBase, 'STATE.md'); - const configPath = path.join(planRoot, 'config.json'); + const configPath = path.join(planBase, 'config.json'); const phasesDir = path.join(planBase, 'phases'); const errors = []; @@ -865,9 +864,12 @@ function cmdValidateHealth(cwd, options, raw) { } // Generate minimal STATE.md from ROADMAP.md structure const milestone = getMilestoneInfo(cwd); + const projectRef = path + .relative(cwd, path.join(planningDir(cwd), 'PROJECT.md')) + .split(path.sep).join('/'); let stateContent = `# Session State\n\n`; stateContent += `## Project Reference\n\n`; - stateContent += `See: .planning/PROJECT.md\n\n`; + stateContent += `See: ${projectRef}\n\n`; stateContent += `## Position\n\n`; stateContent += `**Milestone:** ${milestone.version} ${milestone.name}\n`; stateContent += `**Current phase:** (determining...)\n`;