Skip to content

Commit fe05f46

Browse files
committed
fix(init): add .gitkeep files to empty directories
After running openspec init, the specs/, changes/, and changes/archive/ directories are empty. Since git does not track empty directories, these folders are lost when the repository is cloned, causing openspec list to recommend re-initialization. Added .gitkeep file creation to createDirectoryStructure() for both normal and extend modes, ensuring empty directories are preserved in version control. Fixes #269
1 parent afdca0d commit fe05f46

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

src/core/init.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,17 @@ export class InitCommand {
465465
for (const dir of directories) {
466466
await FileSystemUtils.createDirectory(dir);
467467
}
468+
469+
// Add .gitkeep to empty directories so they are tracked by git
470+
const emptyDirs = [
471+
path.join(openspecPath, 'specs'),
472+
path.join(openspecPath, 'changes'),
473+
path.join(openspecPath, 'changes', 'archive'),
474+
];
475+
for (const dir of emptyDirs) {
476+
const gitkeepPath = path.join(dir, '.gitkeep');
477+
await FileSystemUtils.writeFile(gitkeepPath, '');
478+
}
468479
return;
469480
}
470481

@@ -481,6 +492,17 @@ export class InitCommand {
481492
await FileSystemUtils.createDirectory(dir);
482493
}
483494

495+
// Add .gitkeep to empty directories so they are tracked by git
496+
const emptyDirs = [
497+
path.join(openspecPath, 'specs'),
498+
path.join(openspecPath, 'changes'),
499+
path.join(openspecPath, 'changes', 'archive'),
500+
];
501+
for (const dir of emptyDirs) {
502+
const gitkeepPath = path.join(dir, '.gitkeep');
503+
await FileSystemUtils.writeFile(gitkeepPath, '');
504+
}
505+
484506
spinner.stopAndPersist({
485507
symbol: PALETTE.white('▌'),
486508
text: PALETTE.white('OpenSpec structure created'),

test/core/init.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ describe('InitCommand', () => {
6565
expect(await directoryExists(path.join(openspecPath, 'changes', 'archive'))).toBe(true);
6666
});
6767

68+
it('should create .gitkeep files in empty directories', async () => {
69+
const initCommand = new InitCommand({ tools: 'claude', force: true });
70+
71+
await initCommand.execute(testDir);
72+
73+
const openspecPath = path.join(testDir, 'openspec');
74+
expect(await fileExists(path.join(openspecPath, 'specs', '.gitkeep'))).toBe(true);
75+
expect(await fileExists(path.join(openspecPath, 'changes', '.gitkeep'))).toBe(true);
76+
expect(await fileExists(path.join(openspecPath, 'changes', 'archive', '.gitkeep'))).toBe(true);
77+
});
78+
6879
it('should create config.yaml with default schema', async () => {
6980
const initCommand = new InitCommand({ tools: 'claude', force: true });
7081

0 commit comments

Comments
 (0)