feat: add project_code config for phase directory prefixing
When project_code is set (e.g., "CK"), phase directories are prefixed
with the code: CK-01-foundation, CK-02-api, etc. This disambiguates
phases across multiple GSD projects in the same session.
Changes:
- Add project_code to VALID_CONFIG_KEYS and buildNewProjectConfig defaults
- Add project_code to loadConfig in core.cjs
- Prepend prefix in cmdPhaseAdd and cmdPhaseInsert
- Update searchPhaseInDir, cmdFindPhase, comparePhaseNum, and
normalizePhaseName to strip prefix before matching/sorting
- Support {project} placeholder in git.phase_branch_template
- Add 4 tests covering prefixed add, null code, find, and sort
Closes #1019
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -657,6 +657,92 @@ describe('phase add command', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// phase add with project_code prefix
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
describe('phase add with project_code', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('prefixes phase directory with project_code', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({ project_code: 'CK' })
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
'# Roadmap v1.0\n\n### Phase 1: Foundation\n**Goal:** Setup\n\n---\n'
|
||||
);
|
||||
|
||||
const result = runGsdTools('phase add User Dashboard', tmpDir, { HOME: tmpDir });
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.phase_number, 2, 'should be phase 2');
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(tmpDir, '.planning', 'phases', 'CK-02-user-dashboard')),
|
||||
'directory should have CK- prefix'
|
||||
);
|
||||
});
|
||||
|
||||
test('no prefix when project_code is null', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'config.json'),
|
||||
JSON.stringify({ project_code: null })
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
'# Roadmap v1.0\n\n### Phase 1: Foundation\n**Goal:** Setup\n\n---\n'
|
||||
);
|
||||
|
||||
const result = runGsdTools('phase add User Dashboard', tmpDir, { HOME: tmpDir });
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(tmpDir, '.planning', 'phases', '02-user-dashboard')),
|
||||
'directory should have no prefix'
|
||||
);
|
||||
});
|
||||
|
||||
test('find-phase resolves prefixed directories', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', 'CK-01-foundation');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(phaseDir, '01-01-PLAN.md'), '# Plan');
|
||||
|
||||
const result = runGsdTools('find-phase 01', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.found, true, 'should find prefixed phase');
|
||||
assert.strictEqual(output.phase_number, '01', 'should extract numeric phase number');
|
||||
});
|
||||
|
||||
test('phases list sorts prefixed directories correctly', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', 'CK-02-api'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', 'CK-01-foundation'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', 'CK-03-ui'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phases list', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(
|
||||
output.directories,
|
||||
['CK-01-foundation', 'CK-02-api', 'CK-03-ui'],
|
||||
'prefixed phases should sort numerically'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// phase insert command
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user