diff --git a/src/commands/init-hooks.ts b/src/commands/init-hooks.ts index 6a61f993..8897e412 100644 --- a/src/commands/init-hooks.ts +++ b/src/commands/init-hooks.ts @@ -3,16 +3,18 @@ * * Generates Claude Code hook configuration files: * - .claude/settings.json (or ~/.claude/settings.json for user scope) - * - .git-mem.json (hook-specific config) + * - .git-mem/.git-mem.yaml (hook-specific config) * * Preserves existing non-git-mem hooks in settings.json and - * user customizations in .git-mem.json on re-runs. + * user customizations in .git-mem/.git-mem.yaml on re-runs. */ -import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs'; +import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, rmSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; +import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'; import type { ILogger } from '../domain/interfaces/ILogger'; +import { getConfigPath, getConfigDir } from '../hooks/utils/config'; interface IInitHooksOptions { yes?: boolean; @@ -95,7 +97,7 @@ export function removeGitMemHooks(hooks: Record): Record { try { return JSON.parse(readFileSync(gitMemConfigPath, 'utf8')) as Record; } catch { return {}; } })() - : {}; + // Deep-merge into existing .git-mem/.git-mem.yaml (preserves user customizations) + if (!existsSync(configDir)) { + mkdirSync(configDir, { recursive: true }); + } + const existingGitMemConfig = (() => { + if (!existsSync(configPath)) return {}; + try { + const parsed = parseYaml(readFileSync(configPath, 'utf8')); + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) + ? (parsed as Record) + : {}; + } catch { + return {}; + } + })(); const newGitMemConfig = buildGitMemConfig(); const mergedGitMemConfig = deepMergeGitMemConfig(existingGitMemConfig, newGitMemConfig); - writeFileSync(gitMemConfigPath, JSON.stringify(mergedGitMemConfig, null, 2) + '\n'); - console.log('Written .git-mem.json'); + writeFileSync(configPath, stringifyYaml(mergedGitMemConfig)); + console.log('Written .git-mem/.git-mem.yaml'); console.log('\nHooks configured:'); console.log(' SessionStart — Load memories into Claude context on startup'); @@ -283,5 +298,5 @@ export async function initHooksCommand(options: IInitHooksOptions, logger?: ILog console.log('\nNext steps:'); console.log(' 1. Start Claude Code in this repo: claude'); console.log(' 2. Memories will load automatically on session start'); - console.log(' 3. Adjust settings in .git-mem.json'); + console.log(' 3. Adjust settings in .git-mem/.git-mem.yaml'); } diff --git a/src/domain/interfaces/IHookConfig.ts b/src/domain/interfaces/IHookConfig.ts index a8f31ce2..c382b272 100644 --- a/src/domain/interfaces/IHookConfig.ts +++ b/src/domain/interfaces/IHookConfig.ts @@ -1,7 +1,7 @@ /** * IHookConfig * - * Type definitions for .git-mem.json hook configuration. + * Type definitions for .git-mem/.git-mem.yaml hook configuration. * Read by hook entry points to control per-hook behaviour. */ diff --git a/src/infrastructure/services/HookConfigLoader.ts b/src/infrastructure/services/HookConfigLoader.ts index 1e900eae..1b59d53e 100644 --- a/src/infrastructure/services/HookConfigLoader.ts +++ b/src/infrastructure/services/HookConfigLoader.ts @@ -2,7 +2,7 @@ * HookConfigLoader * * Infrastructure implementation of IHookConfigLoader. - * Loads and merges hook configuration from .git-mem.json. + * Loads and merges hook configuration from .git-mem/.git-mem.yaml. */ import type { IHookConfigLoader } from '../../domain/interfaces/IHookConfigLoader'; @@ -10,7 +10,8 @@ import type { IHookConfig } from '../../domain/interfaces/IHookConfig'; import { loadHookConfig } from '../../hooks/utils/config'; /** - * Implementation of IHookConfigLoader that reads from .git-mem.json. + * Implementation of IHookConfigLoader that reads from .git-mem/.git-mem.yaml. + * Returns defaults on missing file or parse errors (never throws). */ export class HookConfigLoader implements IHookConfigLoader { loadConfig(cwd: string): IHookConfig {