diff --git a/CHANGELOG.md b/CHANGELOG.md index b70d05dc..f556d05d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Documentation repositioning** — README and getting-started now lead with AI metadata tracking, memory as "level up" -## [0.4.0] - 2026-02-15 +## [0.5.0] - 2026-02-16 ### Added @@ -31,12 +31,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Detects memory type (decision, gotcha, convention, fact) from patterns - Infers tags from conventional commit scope and file paths - Adds AI-Agent, AI-Model, AI-Confidence, AI-Lifecycle, AI-Memory-Id trailers +- **Multi-provider LLM enrichment** (GIT-110) — OpenAI, Gemini, Ollama alongside Anthropic; auto-detected from env vars or configured in `.git-mem.yaml` +- **Multi-agent detection** (GIT-121) — auto-detects Claude Code and Codex from env vars, resolves model from config files (session JSONL, config.toml) +- **`git mem trailers` command** (GIT-71) — inspect AI-* trailers on commits, query across history, list distinct keys ### Fixed - Edge cases in commit-msg hook (empty messages, missing env vars) - Duplicate Agent/Model trailers prevention - Hook chaining — all hooks now install by default +- **Hook LLM config passthrough** (GIT-122) — hook command now forwards `llm` config from `.git-mem.yaml` to DI container +- **Init MCP** — support source-checkout local server resolution (GIT-80) +- **Init push semantics** — preserve default push semantics for notes refs (GIT-117) +- **Circular error references** — in logger and handlers (GIT-109) +- **Git root resolution** — for init paths + Windows test compat (GIT-108) +- **Undefined content** — handle undefined content in memory query (GIT-96) +- **Windows compat** — PowerShell-safe lint globs (GIT-118), cross-platform test commands (GIT-119) ## [0.3.0] - 2026-02-12 diff --git a/CLAUDE.md b/CLAUDE.md index e00b6a55..79678b75 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ Clean architecture with three layers. Dependencies point inward only: Infrastruc **Infrastructure** (`src/infrastructure/`) — Implements domain interfaces. `GitClient` wraps git CLI. `NotesService` reads/writes `refs/notes/mem`. `TrailerService` queries commit trailers. `MemoryRepository` persists `IMemoryEntity[]` as JSON in git notes. `HeuristicPatterns` provides regex-based extraction rules. `EventBus` provides pub/sub event dispatch with error isolation (failing handlers don't crash the hook). **Entry points:** -- `src/cli.ts` — Commander.js CLI with 6 commands (remember, recall, context, extract, sync, init) +- `src/cli.ts` — Commander.js CLI with 8 commands (remember, recall, context, extract, sync, init, trailers, hook) - `src/mcp-server.ts` — MCP server over stdio; `src/mcp/server.ts` creates the server and registers 4 tools - `src/commands/hook.ts` — Unified hook entry point: reads stdin JSON, loads config, emits typed event via EventBus - `src/commands/init.ts` — Interactive setup: hooks, MCP config, .gitignore, initial extract @@ -51,7 +51,7 @@ const container = createContainer({ logger, scope: 'remember' }); const { memoryService } = container.cradle; ``` -Uses `InjectionMode.CLASSIC` (matches constructor parameter names to registration names). `ICradle` in `types.ts` defines the typed container shape with all interface references. The container also registers the `EventBus` with three hook handlers (`session:start`, `session:stop`, `prompt:submit`) wired during creation. +Uses `InjectionMode.CLASSIC` (matches constructor parameter names to registration names). `ICradle` in `types.ts` defines the typed container shape with all interface references. The container also registers the `EventBus` with five hook handlers (`session:start`, `session:stop`, `prompt:submit`, `commit:msg`, `post:commit`) wired during creation. **Hook event flow:** `git-mem hook ` → `readStdin()` → `loadHookConfig()` → `createContainer()` → `eventBus.emit(typedEvent)` → handlers return `IEventResult[]` → output to stdout (context), summary to stderr. Hooks have a 10s hard timeout and never throw — failures are caught and reported silently. diff --git a/README.md b/README.md index d8ba0ad1..3ab0009f 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Track AI contributions in your git history. +![AI commit metadata](git-mem.png) + ## Why? AI writes code now. Your commits should show when, which agent, and which model. diff --git a/docs/getting-started.md b/docs/getting-started.md index ecc8f562..c87d8f01 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -138,7 +138,7 @@ Options: - `--commit-count ` — Max commits to process - `--dry-run` — Preview without writing - `--threshold ` — Interest score threshold (default: 3) -- `--enrich` — Enable LLM enrichment (requires `ANTHROPIC_API_KEY`) +- `--enrich` — Enable LLM enrichment (requires an LLM provider API key) ### context @@ -164,6 +164,24 @@ git mem sync --push # push only git mem sync --pull # pull only ``` +### trailers + +Inspect AI-* trailers on commits. + +```bash +git mem trailers # show trailers on HEAD +git mem trailers abc123 # show trailers on specific commit +git mem trailers --query AI-Decision # search across history +git mem trailers --keys # list all distinct AI-* trailer keys +``` + +Options: +- `--query ` — Search for trailer key across history (e.g. `AI-Decision`) +- `--since ` — Filter commits after date +- `--keys` — List all distinct AI-* trailer keys in the repo +- `-n, --limit ` — Max commits to search +- `--json` — Output as JSON + --- ## Using Claude Code? @@ -177,6 +195,8 @@ If you use Claude Code, git-mem can do more than just add trailers & notes — i | **SessionStart** | Loads stored memories into Claude's context on startup | | **Stop** | Extracts knowledge from commits made during the session | | **UserPromptSubmit** | Surfaces relevant memories per prompt (disabled by default) | +| **PostCommit** | Records AI agent and model metadata on each commit | +| **CommitMsg** | Analyzes commit message and adds AI-* trailers (with optional LLM enrichment) | These hooks read the AI trailers from your commits and build a searchable memory layer that Claude can query. @@ -195,6 +215,12 @@ hooks: promptSubmit: enabled: false surfaceContext: true + commitMsg: + enabled: true + enrich: false # set true + provide LLM API key for enriched extraction + enrichTimeout: 8000 + postCommit: + enabled: true ``` @@ -202,6 +228,22 @@ hooks: | Variable | Required | Description | |----------|----------|-------------| -| `ANTHROPIC_API_KEY` | Only for `--enrich` | Anthropic API key for LLM enrichment during extract. Get one at [console.anthropic.com](https://console.anthropic.com/). | +| `ANTHROPIC_API_KEY` | For Anthropic LLM | Anthropic (Claude) API key | +| `OPENAI_API_KEY` | For OpenAI LLM | OpenAI API key (requires `npm install openai`) | +| `GOOGLE_API_KEY` / `GEMINI_API_KEY` | For Gemini LLM | Google Gemini API key (requires `npm install @google/generative-ai`) | +| `OLLAMA_HOST` | For Ollama | Ollama server URL (default: `http://localhost:11434`) | +| `GIT_MEM_LLM_PROVIDER` | No | Force provider: `anthropic`, `openai`, `gemini`, `ollama` | + +Only one provider is needed. If `--enrich` is used without any LLM API key, git-mem falls back to heuristic extraction with a warning. + +### LLM Configuration -If `--enrich` is used without an API key, git-mem falls back to heuristic extraction only. +You can also configure the LLM provider in `.git-mem/.git-mem.yaml`: + +```yaml +llm: + provider: openai # auto-detected from env vars if omitted + model: gpt-4o # uses provider default if omitted + intentModel: gpt-4o-mini + baseUrl: http://localhost:11434 # for ollama +``` diff --git a/git-mem.png b/git-mem.png new file mode 100644 index 00000000..38c7607b Binary files /dev/null and b/git-mem.png differ diff --git a/package.json b/package.json index 436664ae..16837ab7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "git-mem", - "version": "0.4.0", - "description": "Git-native memory layer for AI coding tools. Store decisions, context, and knowledge directly in your git repository using commit trailers and git notes.", + "version": "0.5.0", + "description": "Add AI metadata to git commits automatically. Tracks agent, model, and decisions via commit trailers and git notes.", "bin": { "git-mem": "dist/cli.js", "git-mem-mcp": "dist/mcp-server.js" diff --git a/tests/integration/hooks/hook-commit-msg.test.ts b/tests/integration/hooks/hook-commit-msg.test.ts index b335200d..d79cd290 100644 --- a/tests/integration/hooks/hook-commit-msg.test.ts +++ b/tests/integration/hooks/hook-commit-msg.test.ts @@ -439,7 +439,10 @@ describe('Integration: hook commit-msg', () => { it('should detect Claude Code agent from CLAUDECODE env var', () => { delete process.env.GIT_MEM_AGENT; process.env.CLAUDECODE = '1'; - process.env.ANTHROPIC_MODEL = 'claude-opus-4-5-20251101'; + // Use GIT_MEM_MODEL (highest priority) rather than ANTHROPIC_MODEL, + // because detectClaudeModel() reads session JSONL files when CLAUDECODE + // is set, which can find real sessions in the dev environment. + process.env.GIT_MEM_MODEL = 'claude-opus-4-5-20251101'; const commitMsg = 'feat: claude code commit'; const msgPath = createCommitMsgFile(repoDir, commitMsg); @@ -457,7 +460,7 @@ describe('Integration: hook commit-msg', () => { modifiedMsg.includes('Claude-Code') || modifiedMsg.includes('claude-code'), 'Agent should be Claude Code variant', ); - assert.ok(modifiedMsg.includes('AI-Model: claude-opus-4-5-20251101'), 'Should use ANTHROPIC_MODEL'); + assert.ok(modifiedMsg.includes('AI-Model: claude-opus-4-5-20251101'), 'Should use GIT_MEM_MODEL'); }); });