From 68cb64b780fbcc2a22753cb9020f4103e5472020 Mon Sep 17 00:00:00 2001 From: Tony Casey Date: Sat, 14 Feb 2026 18:50:58 +0000 Subject: [PATCH 1/3] feat: load .env in hooks for API keys AI-Agent: Claude-Code/2.1.42 AI-Model: claude-opus-4-5-20251101 AI-Gotcha: Git hooks require explicit loading of .env files to access API keys and other environment variables that are normally available in the main application context. AI-Confidence: high AI-Tags: git-hooks, environment-variables, dotenv, api-keys, configuration, execution-context AI-Lifecycle: project AI-Memory-Id: a4807174 AI-Source: llm-enrichment --- src/commands/hook.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/commands/hook.ts b/src/commands/hook.ts index e5cf2c4e..41596c9d 100644 --- a/src/commands/hook.ts +++ b/src/commands/hook.ts @@ -10,6 +10,8 @@ * git-mem hook prompt-submit */ +import { join } from 'path'; +import { config as loadEnv } from 'dotenv'; import { createContainer } from '../infrastructure/di'; import { readStdin } from '../hooks/utils/stdin'; import { setupShutdown } from '../hooks/utils/shutdown'; @@ -120,7 +122,12 @@ export async function hookCommand(eventName: string, _logger?: ILogger): Promise try { const input = await readStdin(); - const config = loadHookConfig(input.cwd); + const cwd = input.cwd ?? process.cwd(); + + // Load .env from repository root for API keys (e.g., ANTHROPIC_API_KEY) + loadEnv({ path: join(cwd, '.env'), quiet: true }); + + const config = loadHookConfig(cwd); if (!isEventEnabled(config.hooks, eventName)) { clearTimeout(timer); From 0ed28acd457a3e337ebc5475a3a35266e0bb53bf Mon Sep 17 00:00:00 2001 From: Tony Casey Date: Sat, 14 Feb 2026 18:52:51 +0000 Subject: [PATCH 2/3] chore: add dotenv dependency for .env loading in hooks AI-Agent: Claude-Code/2.1.42 AI-Model: claude-opus-4-5-20251101 AI-Decision: The project uses dotenv for loading environment variables from .env files, specifically for hooks functionality. AI-Confidence: verified AI-Tags: dotenv, environment-variables, hooks, configuration, cli-tool, package-structure, binary AI-Lifecycle: project AI-Memory-Id: 5e159d36 AI-Source: llm-enrichment --- package-lock.json | 13 +++++++++++++ package.json | 1 + 2 files changed, 14 insertions(+) diff --git a/package-lock.json b/package-lock.json index 828f7b2f..fda883b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "awilix": "^12.1.0", "chalk": "^5.3.0", "commander": "^11.1.0", + "dotenv": "^17.3.1", "prompts": "^2.4.2" }, "bin": { @@ -2960,6 +2961,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index 4e662c4d..e15d736f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "awilix": "^12.1.0", "chalk": "^5.3.0", "commander": "^11.1.0", + "dotenv": "^17.3.1", "prompts": "^2.4.2" }, "devDependencies": { From 6abf8ea653930b14df82f97e39b5b186f3cc6ef5 Mon Sep 17 00:00:00 2001 From: Tony Casey Date: Sat, 14 Feb 2026 19:01:32 +0000 Subject: [PATCH 3/3] fix: use git root for .env loading, not cwd (GIT-94) AI-Agent: Claude-Code/2.1.42 AI-Model: claude-opus-4-5-20251101 AI-Gotcha: use git root for .env loading, not cwd (GIT-94). AI-Agent: Claude-Code/2.1.42 AI-Confidence: medium AI-Tags: commands, typescript AI-Lifecycle: project AI-Memory-Id: 48348a38 AI-Source: heuristic --- src/commands/hook.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/commands/hook.ts b/src/commands/hook.ts index 41596c9d..25d3e2f4 100644 --- a/src/commands/hook.ts +++ b/src/commands/hook.ts @@ -11,6 +11,7 @@ */ import { join } from 'path'; +import { execFileSync } from 'child_process'; import { config as loadEnv } from 'dotenv'; import { createContainer } from '../infrastructure/di'; import { readStdin } from '../hooks/utils/stdin'; @@ -102,6 +103,22 @@ export function buildEvent(eventType: HookEventType, input: IHookInput): HookEve } } +/** + * Find git repository root from a working directory. + * Returns cwd if git command fails (graceful fallback). + */ +function findGitRoot(cwd: string): string { + try { + return execFileSync('git', ['rev-parse', '--show-toplevel'], { + cwd, + encoding: 'utf8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + } catch { + return cwd; + } +} + /** Stderr labels per event for user-facing messages. */ const STDERR_LABELS: Record = { 'session:start': { success: 'Memory loaded.', prefix: 'Memory loaded' }, @@ -123,11 +140,12 @@ export async function hookCommand(eventName: string, _logger?: ILogger): Promise try { const input = await readStdin(); const cwd = input.cwd ?? process.cwd(); + const repoRoot = findGitRoot(cwd); // Load .env from repository root for API keys (e.g., ANTHROPIC_API_KEY) - loadEnv({ path: join(cwd, '.env'), quiet: true }); + loadEnv({ path: join(repoRoot, '.env'), quiet: true }); - const config = loadHookConfig(cwd); + const config = loadHookConfig(repoRoot); if (!isEventEnabled(config.hooks, eventName)) { clearTimeout(timer);