fix: preserve subdirectory CWD in monorepo worktrees (#1283)
When running GSD from a monorepo subdirectory inside a git worktree, resolveWorktreeRoot() resolved to the worktree root, discarding the subdirectory where .planning/ lives. All commands failed with "No ROADMAP.md found" even though the planning structure existed. Now check if CWD already contains .planning/ before worktree resolution. If it does, the CWD is already the correct project root and worktree resolution is skipped. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -174,11 +174,15 @@ async function main() {
|
||||
error(`Invalid --cwd: ${cwd}`);
|
||||
}
|
||||
|
||||
// Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree
|
||||
// Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
|
||||
// However, in monorepo worktrees where the subdirectory itself owns .planning/,
|
||||
// skip worktree resolution — the CWD is already the correct project root.
|
||||
const { resolveWorktreeRoot } = require('./lib/core.cjs');
|
||||
const worktreeRoot = resolveWorktreeRoot(cwd);
|
||||
if (worktreeRoot !== cwd) {
|
||||
cwd = worktreeRoot;
|
||||
if (!fs.existsSync(path.join(cwd, '.planning'))) {
|
||||
const worktreeRoot = resolveWorktreeRoot(cwd);
|
||||
if (worktreeRoot !== cwd) {
|
||||
cwd = worktreeRoot;
|
||||
}
|
||||
}
|
||||
|
||||
const rawIndex = args.indexOf('--raw');
|
||||
|
||||
@@ -1039,6 +1039,44 @@ describe('resolveWorktreeRoot', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── monorepo worktree CWD preservation (#1283) ─────────────────────────────
|
||||
|
||||
describe('monorepo worktree CWD preservation', () => {
|
||||
const { resolveWorktreeRoot } = require('../get-shit-done/bin/lib/core.cjs');
|
||||
|
||||
test('CWD with .planning/ skips worktree resolution (monorepo subdirectory)', () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-monorepo-wt-'));
|
||||
const subDir = path.join(tmpDir, 'service-alpha');
|
||||
fs.mkdirSync(path.join(subDir, '.planning'), { recursive: true });
|
||||
try {
|
||||
let cwd = subDir;
|
||||
if (!fs.existsSync(path.join(cwd, '.planning'))) {
|
||||
const worktreeRoot = resolveWorktreeRoot(cwd);
|
||||
if (worktreeRoot !== cwd) cwd = worktreeRoot;
|
||||
}
|
||||
assert.strictEqual(cwd, subDir, 'CWD with .planning/ must not be overridden by worktree resolution');
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('CWD without .planning/ still goes through worktree resolution', () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-monorepo-wt-'));
|
||||
try {
|
||||
let cwd = tmpDir;
|
||||
let worktreeResolutionCalled = false;
|
||||
if (!fs.existsSync(path.join(cwd, '.planning'))) {
|
||||
worktreeResolutionCalled = true;
|
||||
const worktreeRoot = resolveWorktreeRoot(cwd);
|
||||
if (worktreeRoot !== cwd) cwd = worktreeRoot;
|
||||
}
|
||||
assert.ok(worktreeResolutionCalled, 'worktree resolution must be called when .planning/ is absent');
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ─── withPlanningLock ────────────────────────────────────────────────────────
|
||||
|
||||
describe('withPlanningLock', () => {
|
||||
|
||||
Reference in New Issue
Block a user