Skip to content

Commit 52b2a9b

Browse files
committed
Fix #200: Store project config in local directory
1 parent 77705bc commit 52b2a9b

File tree

4 files changed

+147
-1
lines changed

4 files changed

+147
-1
lines changed

packages/cli/src/commands/config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,18 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
114114

115115
// Handle 'list' command
116116
if (argv.command === 'list') {
117+
// Import directly to avoid circular dependency
118+
const { getSettingsDir } = await import('../settings/settings.js');
119+
const { getProjectConfigFile } = await import('../settings/config.js');
120+
121+
const globalConfigFile = path.join(getSettingsDir(), 'config.json');
122+
const projectConfigFile = getProjectConfigFile();
123+
117124
logger.info('Current configuration:');
125+
logger.info(`Global config file: ${globalConfigFile}`);
126+
logger.info(`Project config file: ${projectConfigFile}`);
127+
logger.info('');
128+
118129
const defaultConfig = getDefaultConfig();
119130

120131
// Get all valid config keys

packages/cli/src/settings/config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ const globalConfigFile = path.join(getSettingsDir(), 'config.json');
2323
// Export for testing
2424
export const getProjectConfigFile = (): string => {
2525
const projectDir = getProjectSettingsDir();
26+
27+
// Ensure the project directory exists
28+
if (projectDir && !fs.existsSync(projectDir)) {
29+
try {
30+
fs.mkdirSync(projectDir, { recursive: true });
31+
} catch (error) {
32+
console.error(`Error creating project settings directory: ${error}`);
33+
return '';
34+
}
35+
}
36+
2637
return projectDir ? path.join(projectDir, 'config.json') : '';
2738
};
2839

packages/cli/src/settings/settings.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,20 @@ export const getProjectSettingsDir = (): string => {
3333
}
3434

3535
// If we're creating a new project config, use the current directory
36-
return path.join(process.cwd(), '.mycoder');
36+
const projectDir = path.join(process.cwd(), '.mycoder');
37+
38+
// Ensure directory exists when it's requested
39+
if (!fs.existsSync(projectDir)) {
40+
try {
41+
fs.mkdirSync(projectDir, { recursive: true });
42+
} catch (error) {
43+
console.error(`Error creating project settings directory: ${error}`);
44+
// Still return the path even if we couldn't create it,
45+
// as other code will handle the error when trying to use it
46+
}
47+
}
48+
49+
return projectDir;
3750
};
3851

3952
/**
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
4+
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest';
5+
6+
import { getProjectConfigFile } from '../../src/settings/config.js';
7+
import { getProjectSettingsDir } from '../../src/settings/settings.js';
8+
9+
// Mock fs module
10+
vi.mock('fs', () => ({
11+
existsSync: vi.fn(),
12+
mkdirSync: vi.fn(),
13+
statSync: vi.fn(),
14+
}));
15+
16+
// Mock path module
17+
vi.mock('path', async (importOriginal) => {
18+
const actual = await importOriginal();
19+
return {
20+
...actual,
21+
parse: vi.fn(),
22+
};
23+
});
24+
25+
// Only mock specific functions from settings.js
26+
vi.mock('../../src/settings/settings.js', async (importOriginal) => {
27+
const actual = await importOriginal();
28+
return {
29+
...actual,
30+
getProjectSettingsDir: vi.fn(),
31+
};
32+
});
33+
34+
describe('Project Config File', () => {
35+
const mockCwd = '/mock/project/dir';
36+
const mockProjectDir = path.join(mockCwd, '.mycoder');
37+
const expectedConfigFile = path.join(mockProjectDir, 'config.json');
38+
39+
beforeEach(() => {
40+
// Reset mocks
41+
vi.resetAllMocks();
42+
43+
// Mock process.cwd()
44+
vi.spyOn(process, 'cwd').mockReturnValue(mockCwd);
45+
46+
// Mock path.parse
47+
vi.mocked(path.parse).mockReturnValue({
48+
root: '/',
49+
dir: '/mock',
50+
base: 'dir',
51+
name: 'dir',
52+
ext: '',
53+
});
54+
55+
// Default mock for existsSync
56+
vi.mocked(fs.existsSync).mockReturnValue(false);
57+
58+
// Default mock for statSync
59+
vi.mocked(fs.statSync).mockReturnValue({
60+
isDirectory: () => true,
61+
} as unknown as fs.Stats);
62+
});
63+
64+
afterEach(() => {
65+
vi.resetAllMocks();
66+
});
67+
68+
it('should return project config file path in current directory', () => {
69+
// Mock getProjectSettingsDir to return the project dir
70+
vi.mocked(getProjectSettingsDir).mockReturnValue(mockProjectDir);
71+
72+
const result = getProjectConfigFile();
73+
74+
expect(result).toBe(expectedConfigFile);
75+
});
76+
77+
it('should create project directory if it does not exist', () => {
78+
// Mock getProjectSettingsDir to return the project dir
79+
vi.mocked(getProjectSettingsDir).mockReturnValue(mockProjectDir);
80+
81+
// Mock directory does not exist
82+
vi.mocked(fs.existsSync).mockReturnValue(false);
83+
84+
getProjectConfigFile();
85+
86+
// Verify directory creation was attempted
87+
expect(fs.mkdirSync).toHaveBeenCalledWith(mockProjectDir, { recursive: true });
88+
});
89+
90+
it('should not create project directory if it already exists', () => {
91+
// Mock getProjectSettingsDir to return the project dir
92+
vi.mocked(getProjectSettingsDir).mockReturnValue(mockProjectDir);
93+
94+
// Mock directory already exists
95+
vi.mocked(fs.existsSync).mockReturnValue(true);
96+
97+
getProjectConfigFile();
98+
99+
// Verify directory creation was not attempted
100+
expect(fs.mkdirSync).not.toHaveBeenCalled();
101+
});
102+
103+
it('should return empty string if project directory cannot be determined', () => {
104+
// Mock getProjectSettingsDir to return empty string (error case)
105+
vi.mocked(getProjectSettingsDir).mockReturnValue('');
106+
107+
const result = getProjectConfigFile();
108+
109+
expect(result).toBe('');
110+
});
111+
});

0 commit comments

Comments
 (0)