Skip to content

Commit 72d423b

Browse files
authored
Refactor config file handling in InitCommand
1 parent a186481 commit 72d423b

1 file changed

Lines changed: 39 additions & 32 deletions

File tree

git-ai/src/commands/InitCommand.ts

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { logger } from '../utils/logger.js';
1212
async function readSecretInput(rl: readline.Interface, prompt: string): Promise<string> {
1313
const rlAny = rl as any;
1414
const originalWrite = rlAny._writeToOutput;
15-
// Suppress character echoing: only allow the initial prompt to be written
1615
let promptWritten = false;
1716
rlAny._writeToOutput = function _writeToOutput(str: string) {
1817
if (!promptWritten) {
@@ -38,6 +37,7 @@ export async function initCommand() {
3837
console.log('🚀 Welcome to AI-Git-Terminal Setup\n');
3938

4039
try {
40+
// --- Step 1: Read API Key ---
4141
let apiKey = '';
4242
while (!apiKey) {
4343
const apiKeyInput = await readSecretInput(rl, '🔑 Enter your Gemini API Key: ');
@@ -47,56 +47,63 @@ export async function initCommand() {
4747
}
4848
}
4949

50+
// --- Step 2: Read model name ---
5051
const modelInput = await rl.question('🤖 Enter model name (default: gemini-1.5-flash): ');
5152
const model = modelInput.trim() || 'gemini-1.5-flash';
5253

54+
// --- Step 3: Build config object ---
5355
const newConfig: Config = {
54-
ai: {
55-
provider: 'gemini',
56-
apiKey,
57-
model: model,
58-
},
59-
git: {
60-
autoStage: false,
61-
},
62-
ui: {
63-
theme: 'dark',
64-
showIcons: true,
65-
},
56+
ai: { provider: 'gemini', apiKey, model },
57+
git: { autoStage: false },
58+
ui: { theme: 'dark', showIcons: true },
6659
};
6760

68-
// Validate with Zod and persist with restricted permissions (mode 0o600)
61+
// Validate with Zod
6962
ConfigSchema.parse(newConfig);
7063

7164
const configPath = path.join(os.homedir(), '.aigitrc');
7265

73-
if (fs.existsSync(configPath)) {
74-
const overwriteChoice = (await rl.question(
75-
'⚠️ Existing config found. Choose [o]verwrite, [b]ackup then replace, or [c]ancel: '
76-
)).trim().toLowerCase();
66+
// --- Step 4: Attempt atomic creation ---
67+
try {
68+
const fd = fs.openSync(configPath, fs.O_CREAT | fs.O_EXCL | fs.O_RDWR, 0o600);
69+
fs.writeFileSync(fd, JSON.stringify(newConfig, null, 2));
70+
fs.closeSync(fd);
71+
console.log(`\n✅ Configuration saved to ${configPath}`);
72+
console.log('Try running: ai-git commit');
73+
return;
74+
} catch (err: any) {
75+
if (err.code !== 'EEXIST') throw err;
76+
// File already exists, proceed to backup/overwrite prompt
77+
}
7778

78-
if (overwriteChoice === 'b' || overwriteChoice === 'backup') {
79-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
80-
const backupPath = `${configPath}.bak-${timestamp}`;
81-
fs.renameSync(configPath, backupPath);
82-
console.log(`📦 Existing config backed up to ${backupPath}`);
83-
} else if (overwriteChoice === 'o' || overwriteChoice === 'overwrite') {
84-
console.log('📝 Overwriting existing config file.');
85-
} else {
86-
console.log('🚫 Initialization canceled. Existing config left unchanged.');
87-
return;
88-
}
79+
// --- Step 5: Handle existing file ---
80+
const overwriteChoice = (await rl.question(
81+
'⚠️ Existing config found. Choose [o]verwrite, [b]ackup then replace, or [c]ancel: '
82+
)).trim().toLowerCase();
83+
84+
if (overwriteChoice === 'b' || overwriteChoice === 'backup') {
85+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
86+
const backupPath = `${configPath}.bak-${timestamp}`;
87+
fs.renameSync(configPath, backupPath);
88+
console.log(`📦 Existing config backed up to ${backupPath}`);
89+
// Now write new config
90+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
91+
} else if (overwriteChoice === 'o' || overwriteChoice === 'overwrite') {
92+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
93+
console.log('📝 Overwriting existing config file.');
94+
} else {
95+
console.log('🚫 Initialization canceled. Existing config left unchanged.');
96+
return;
8997
}
9098

91-
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
9299
fs.chmodSync(configPath, 0o600);
93-
94100
console.log(`\n✅ Configuration saved to ${configPath}`);
95101
console.log('Try running: ai-git commit');
102+
96103
} catch (error) {
97104
logger.error('Failed to save configuration: ' + (error instanceof Error ? error.message : String(error)));
98105
console.error('\n❌ Invalid input or failed to write config file.');
99106
} finally {
100107
rl.close();
101108
}
102-
}
109+
}

0 commit comments

Comments
 (0)