diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..33dd9cd4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.3.0] - 2026-02-12 + +### Added + +- **Claude Code hooks integration** — three lifecycle hooks that give Claude persistent project memory: + - `session-start` — loads stored memories into Claude's context at session start + - `session-stop` — captures new memories from commits made during the session + - `prompt-submit` — surfaces relevant memories per prompt (disabled by default) +- **`git-mem init-hooks`** command — one-command setup for Claude Code hooks: + - Creates `.claude/settings.json` with hook registrations + - Creates `.git-mem.json` with default configuration + - Safe merge preserves other tools' hooks in `settings.json` + - `--remove` flag cleanly uninstalls git-mem hooks only + - `--scope user` for `~/.claude/settings.json` (user-wide) +- **Unified `git-mem hook ` command** — single entry point for all hook events, reads JSON from stdin, routes to handlers via EventBus +- **DI container** (awilix) — replaces manual service instantiation across CLI, MCP, and hooks with centralized dependency injection +- **EventBus** — pub/sub event dispatch with error isolation; failing handlers don't block other handlers or crash the hook +- **`.git-mem.json` configuration** — per-project hook settings (enable/disable events, memory limits, auto-liberate threshold) +- **Hook handlers:** + - `SessionStartHandler` — loads and formats memories as markdown context + - `SessionStopHandler` — delegates to `SessionCaptureService` for 24h rolling commit scan + - `PromptSubmitHandler` — loads relevant memories for prompt context +- **Hook services:** + - `MemoryContextLoader` — loads memories with configurable limits + - `ContextFormatter` — formats memories as structured markdown + - `SessionCaptureService` — scans recent commits via `LiberateService`, extracts memories + +### Changed + +- CLI commands now resolve services from DI container instead of manual instantiation +- MCP tools now resolve services from DI container + +## [0.2.1] - 2026-02-10 + +### Fixed + +- MCP server ESM interop with `@modelcontextprotocol/sdk` + +## [0.2.0] - 2026-02-09 + +### Added + +- MCP server (`git-mem-mcp`) with 4 tools: remember, recall, context, liberate +- `git-mem init-mcp` command for MCP server configuration +- `git-mem sync` command for syncing memories across remotes + +## [0.1.0] - 2026-02-05 + +### Added + +- Initial release +- `git-mem remember` — store memories as git notes +- `git-mem recall` — search and retrieve memories +- `git-mem context` — match staged changes against stored memories +- `git-mem liberate` — scan git history, score commits, extract memories +- Clean architecture (Domain → Application → Infrastructure) +- Git notes storage on `refs/notes/mem` +- Heuristic pattern extraction from conventional commits +- Optional LLM enrichment via `--enrich` flag diff --git a/CLAUDE.md b/CLAUDE.md index c53b733b..82652243 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,32 +32,36 @@ Clean architecture with three layers. Dependencies point inward only: Infrastruc **Domain** (`src/domain/`) — Zero dependencies. Entities (`IMemoryEntity`), interfaces (`IMemoryRepository`, `INotesService`, `ITrailerService`, `IGitClient`), types (quality, lifecycle), errors (`GitMemError` hierarchy), and pure utils (deduplication). -**Application** (`src/application/`) — Depends on domain only. Three services: `MemoryService` (remember/recall CRUD), `ExtractService` (scan git history, score commits, extract patterns), `ContextService` (match staged changes against stored memories). +**Application** (`src/application/`) — Depends on domain only. Core services: `MemoryService` (remember/recall CRUD), `ExtractService` (scan git history, score commits, extract patterns), `ContextService` (match staged changes against stored memories). Hook services: `MemoryContextLoader` (loads memories with limits), `ContextFormatter` (formats memories as markdown), `SessionCaptureService` (24h rolling commit scan via ExtractService). Three event handlers: `SessionStartHandler`, `SessionStopHandler`, `PromptSubmitHandler`. -**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. +**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/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 - `src/commands/` — CLI command handlers - `src/mcp/tools/` — MCP tool handlers (remember, recall, context, extract) -**Bootstrapping pattern** — Awilix DI container (`src/infrastructure/di/`). `createContainer(options?)` wires all services; CLI commands and MCP tools resolve from `container.cradle`: +**Bootstrapping pattern** — Awilix DI container (`src/infrastructure/di/`). `createContainer(options?)` wires all services; CLI commands, MCP tools, and hooks resolve from `container.cradle`: ```typescript 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. +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. + +**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. ## Testing Uses **`node:test`** (native Node.js test runner) with **`tsx`** for TypeScript, not Jest. Tests import `describe`, `it`, `before`, `after` from `node:test` and assertions from `node:assert/strict`. -**Unit tests** (`tests/unit/`) — Mock dependencies manually (no framework). 57 tests. +**Unit tests** (`tests/unit/`) — Mock dependencies manually (no framework). 265 tests. -**Integration tests** (`tests/integration/`) — Create real temporary git repos in `os.tmpdir()`, run actual git commands, clean up in `after()`. All services instantiated against real repos. 32 tests. +**Integration tests** (`tests/integration/`) — Create real temporary git repos in `os.tmpdir()`, run actual git commands, clean up in `after()`. Hook integration tests (`tests/integration/hooks/`) spawn `git-mem hook ` as child processes via `tsx` binary. 46 tests. ## Environment Variables @@ -71,6 +75,10 @@ Uses **`node:test`** (native Node.js test runner) with **`tsx`** for TypeScript, - **`cwd` parameter**: Must be threaded through all service calls when operating on a repo that isn't the current working directory - **Commit triage**: Weighted scoring based on conventional prefixes, decision keywords, diff size, PR merges - **TypeScript config**: Relaxed strict mode for development (`strict: false` in tsconfig.json) +- **Hook config**: `.git-mem.json` at project root, loaded by `src/hooks/utils/config.ts`. Never throws — returns defaults on error +- **Hook stdin**: `src/hooks/utils/stdin.ts` reads JSON from stdin. Returns `{}` on TTY or parse error — hooks must never crash +- **EventBus error isolation**: Handler exceptions are caught and returned as failed `IEventResult[]` — one failing handler doesn't block others +- **Hook timeout**: 10s hard limit via `setupShutdown()` — hooks must never hang Claude Code - Interfaces prefixed with `I` (enforced by ESLint) - No `any` in production code (ESLint error); relaxed to warn in test files diff --git a/README.md b/README.md index f8958699..a4558291 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Git Mem +# git-mem Your git history is the perfect context for AI. It just needs to be extracted for agents.... ## Why? -Your git history already contains the decisions, conventions, and gotchas that matter — git-mem extracts them and makes them available to Claude automatically. +Your git history already contains the decisions, conventions, and gotchas that matter — git-mem extracts them and makes them available to Claude automatically. ## How? @@ -14,7 +14,7 @@ Each commit is updated with background AI metadata, which Claude can use to impr Every **new** commit is automatically populated with the folowing metadata.. -- **AI-Agent:** Claude/Opus-4.5 +- **AI-Agent:** Claude/Opus-4.5 - **AI-Decision:** JWT over sessions — stateless API, scales horizontally - **AI-Context:** [pattern/middleware, entity/auth-module] - **AI-Confidence:** 0.95 @@ -34,6 +34,149 @@ git-mem init `init` sets up hooks, MCP config, .gitignore entries, and runs an initial extract from your history. +## Quick Start + +### Store a memory + +```bash +git-mem remember "Use JWT for auth — decided in sprint 3" --type decision --tags auth,security +``` + +### Recall memories + +```bash +git-mem recall --query "authentication" +``` + +### Scan git history for knowledge + +```bash +git-mem extract --since 2026-01-01 +``` + +### Surface context for staged changes + +```bash +git-mem context +``` + +## Claude Code Integration + +git-mem integrates with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) via lifecycle hooks to give Claude persistent project memory across sessions. + +### How It Works + +```text +Claude Code starts session + → git-mem hook session-start + → Loads stored memories into Claude's context + +User submits prompt (optional) + → git-mem hook prompt-submit + → Surfaces relevant memories for the prompt + +Claude Code session ends + → git-mem hook session-stop + → Scans commits made during session, extracts new memories +``` + +### Hooks + +| Hook | Event | What it does | +|------|-------|-------------| +| `session-start` | `SessionStart` | Loads stored memories as markdown context | +| `session-stop` | `SessionStop` | Captures memories from recent commits via `extract` | +| `prompt-submit` | `UserPromptSubmit` | Surfaces relevant memories per prompt (disabled by default) | + +### Configuration + +Edit `.git-mem.json` to customize hook behavior: + +```json +{ + "hooks": { + "enabled": true, + "sessionStart": { + "enabled": true, + "memoryLimit": 20 + }, + "sessionStop": { + "enabled": true, + "autoExtract": true, + "threshold": 3 + }, + "promptSubmit": { + "enabled": false, + "recordPrompts": false, + "surfaceContext": true + } + } +} +``` + +| Option | Default | Description | +|--------|---------|-------------| +| `hooks.enabled` | `true` | Master switch for all hooks | +| `sessionStart.enabled` | `true` | Load memories on session start | +| `sessionStart.memoryLimit` | `20` | Max memories to load | +| `sessionStop.enabled` | `true` | Capture memories on session end | +| `sessionStop.autoExtract` | `true` | Auto-scan commits for memories | +| `sessionStop.threshold` | `3` | Minimum commit triage score | +| `promptSubmit.enabled` | `false` | Surface context per prompt | +| `promptSubmit.surfaceContext` | `true` | Include memories in prompt context | + +## MCP Server + +git-mem also ships as an MCP server for tools that support the Model Context Protocol: + +```bash +git-mem init # includes MCP setup +``` + +This registers `git-mem-mcp` with 4 tools: `remember`, `recall`, `context`, `extract`. + +## Architecture + +```text +┌─────────────────────────────────────────────┐ +│ Entry Points │ +│ ┌─────┐ ┌─────────┐ ┌────────────────┐ │ +│ │ CLI │ │ MCP Srv │ │ Claude Hooks │ │ +│ └──┬──┘ └────┬────┘ └───────┬────────┘ │ +│ │ │ │ │ +│ └──────────┴───────┬───────┘ │ +│ │ │ +│ ┌─────────┴─────────┐ │ +│ │ DI Container │ │ +│ │ (awilix) │ │ +│ └─────────┬─────────┘ │ +│ │ │ +│ ┌─────────────────────┴──────────────────┐ │ +│ │ Application Layer │ │ +│ │ MemoryService ExtractService │ │ +│ │ ContextService SessionCaptureService │ │ +│ │ SessionStartHandler PromptSubmitHandler│ │ +│ └─────────────────────┬──────────────────┘ │ +│ │ │ +│ ┌─────────────────────┴──────────────────┐ │ +│ │ Domain Layer │ │ +│ │ IMemoryEntity IMemoryRepository │ │ +│ │ IEventBus HookEvents IHookConfig │ │ +│ └────────────────────────────────────────┘ │ +│ │ │ +│ ┌─────────────────────┴──────────────────┐ │ +│ │ Infrastructure Layer │ │ +│ │ GitClient NotesService EventBus │ │ +│ │ MemoryRepository HeuristicPatterns │ │ +│ └────────────────────────────────────────┘ │ +│ │ │ +│ ┌─────────┴─────────┐ │ +│ │ Git Notes │ │ +│ │ refs/notes/mem │ │ +│ └───────────────────┘ │ +└─────────────────────────────────────────────┘ +``` + See the [Getting Started Guide](./docs/getting-started.md) for full CLI and MCP setup docs. ## License diff --git a/package.json b/package.json index 67b4ff03..d3d4743c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "git-mem", - "version": "0.2.1", + "version": "0.3.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.", "bin": { "git-mem": "dist/cli.js",