Repository navigation
Dev #2521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dev #2521
Changes from 7 commits
2141287
399ac1c
40a1e93
a8541ae
5c69a75
eee864b
6655415
1a140dc
d78f541
af2614d
b66d804
c5013f3
1ca6f3e
396db08
61c1024
3c4ab5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; | ||
| import { mkdtempSync, rmSync } from 'node:fs'; | ||
| import { homedir, tmpdir } from 'node:os'; | ||
| import { join } from 'node:path'; | ||
| import { | ||
| genieConfigExists, | ||
| getGenieConfigPath, | ||
| getGenieDir, | ||
| loadGenieConfig, | ||
| saveGenieConfig, | ||
| } from './genie-config.js'; | ||
|
|
||
| describe('genie-config GENIE_HOME resolution', () => { | ||
| let dir: string; | ||
| let prevGenieHome: string | undefined; | ||
|
|
||
| beforeEach(() => { | ||
| prevGenieHome = process.env.GENIE_HOME; | ||
| dir = mkdtempSync(join(tmpdir(), 'genie-config-')); | ||
| process.env.GENIE_HOME = dir; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| if (prevGenieHome === undefined) { | ||
| Reflect.deleteProperty(process.env, 'GENIE_HOME'); | ||
| } else { | ||
| process.env.GENIE_HOME = prevGenieHome; | ||
| } | ||
| rmSync(dir, { recursive: true, force: true }); | ||
| }); | ||
|
|
||
| test('config dir and path honor GENIE_HOME lazily (set after import)', () => { | ||
| expect(getGenieDir()).toBe(dir); | ||
| expect(getGenieConfigPath()).toBe(join(dir, 'config.json')); | ||
| }); | ||
|
|
||
| test('load returns isolated defaults, save/load round-trips inside GENIE_HOME', async () => { | ||
| expect(genieConfigExists()).toBe(false); | ||
| const config = await loadGenieConfig(); | ||
| config.setupComplete = true; | ||
| await saveGenieConfig(config); | ||
| expect(genieConfigExists()).toBe(true); | ||
| const reloaded = await loadGenieConfig(); | ||
| expect(reloaded.setupComplete).toBe(true); | ||
| }); | ||
|
|
||
| test('falls back to ~/.genie when GENIE_HOME is unset', () => { | ||
| Reflect.deleteProperty(process.env, 'GENIE_HOME'); | ||
| expect(getGenieDir()).toBe(join(homedir(), '.genie')); | ||
| expect(getGenieConfigPath()).toBe(join(homedir(), '.genie', 'config.json')); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,50 +2,52 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; | |||||||||||||||
| import { homedir } from 'node:os'; | ||||||||||||||||
| import { join } from 'node:path'; | ||||||||||||||||
| import { type GenieConfig, GenieConfigSchema, type ShortcutsConfig } from '../types/genie-config.js'; | ||||||||||||||||
|
|
||||||||||||||||
| const GENIE_DIR = join(homedir(), '.genie'); | ||||||||||||||||
| const GENIE_CONFIG_FILE = join(GENIE_DIR, 'config.json'); | ||||||||||||||||
| import { genieHome } from './workspace.js'; | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Get the path to the genie config directory | ||||||||||||||||
| * Get the path to the genie config directory. | ||||||||||||||||
| * Honors GENIE_HOME (which relocates ALL global state) and resolves lazily so | ||||||||||||||||
| * env overrides in tests and spawned subprocesses take effect. | ||||||||||||||||
| */ | ||||||||||||||||
| export function getGenieDir(): string { | ||||||||||||||||
| return GENIE_DIR; | ||||||||||||||||
| return genieHome(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Get the path to the genie config file | ||||||||||||||||
| */ | ||||||||||||||||
| export function getGenieConfigPath(): string { | ||||||||||||||||
| return GENIE_CONFIG_FILE; | ||||||||||||||||
| return join(genieHome(), 'config.json'); | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
19
to
21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value Reuse
♻️ Proposed refactor export function getGenieConfigPath(): string {
- return join(genieHome(), 'config.json');
+ return join(getGenieDir(), 'config.json');
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Check if genie config exists | ||||||||||||||||
| */ | ||||||||||||||||
| export function genieConfigExists(): boolean { | ||||||||||||||||
| return existsSync(GENIE_CONFIG_FILE); | ||||||||||||||||
| return existsSync(getGenieConfigPath()); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Ensure the genie config directory exists | ||||||||||||||||
| */ | ||||||||||||||||
| function ensureGenieDir(): void { | ||||||||||||||||
| if (!existsSync(GENIE_DIR)) { | ||||||||||||||||
| mkdirSync(GENIE_DIR, { recursive: true }); | ||||||||||||||||
| const dir = getGenieDir(); | ||||||||||||||||
| if (!existsSync(dir)) { | ||||||||||||||||
| mkdirSync(dir, { recursive: true }); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Load genie config, returning defaults if not found | ||||||||||||||||
| */ | ||||||||||||||||
| export async function loadGenieConfig(): Promise<GenieConfig> { | ||||||||||||||||
| if (!existsSync(GENIE_CONFIG_FILE)) { | ||||||||||||||||
| const configPath = getGenieConfigPath(); | ||||||||||||||||
| if (!existsSync(configPath)) { | ||||||||||||||||
| return GenieConfigSchema.parse({}); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| const content = readFileSync(GENIE_CONFIG_FILE, 'utf-8'); | ||||||||||||||||
| const content = readFileSync(configPath, 'utf-8'); | ||||||||||||||||
| const data = JSON.parse(content); | ||||||||||||||||
| return GenieConfigSchema.parse(data); | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
|
|
@@ -64,7 +66,7 @@ export async function saveGenieConfig(config: GenieConfig): Promise<void> { | |||||||||||||||
| try { | ||||||||||||||||
| const validated = GenieConfigSchema.parse(config); | ||||||||||||||||
| const content = JSON.stringify(validated, null, 2); | ||||||||||||||||
| writeFileSync(GENIE_CONFIG_FILE, content, 'utf-8'); | ||||||||||||||||
| writeFileSync(getGenieConfigPath(), content, 'utf-8'); | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| const message = error instanceof Error ? error.message : String(error); | ||||||||||||||||
| throw new Error(`Failed to save genie config: ${message}`); | ||||||||||||||||
|
|
||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: automagik-dev/genie
Length of output: 881
🏁 Script executed:
Repository: automagik-dev/genie
Length of output: 21916
Use a fresh GENIE_HOME per test.
ISOLATED_HOMEis shared across both cases and only cleaned up inafterAll, so any dispatch-side writes can leak state between tests. Match thebeforeEach/afterEachisolation pattern used in the other hook tests.🤖 Prompt for AI Agents
Source: Coding guidelines