Non-blocking, zero-friction capture of Claude Code sessions with semantic search via QMD. Sessions are logged as markdown, attached to commits via git notes, and indexed locally so the agent can query its own history.
- Never block the agent. Session commits happen asynchronously in the background — no user confirmation, no pauses.
- Everything is local. No SaaS, no orphan branches, no external services. Git notes + QMD.
- Strictly per-project. All session data, knowledge bases, mistake ledgers, and QMD indexes are scoped to the individual repo. No cross-project leakage. Each project gets its own QMD index derived from the repo name.
- Markdown is the format. Sessions are human-readable markdown files. No proprietary formats.
- The agent searches itself. QMD exposes session history as an MCP server so Claude Code can query past reasoning within the current project.
ghost/
├── ghost # Shell wrapper (like qmd's — do NOT replace with compiled binary)
├── src/
│ ├── index.ts # CLI entrypoint — uses util.parseArgs for argument parsing
│ ├── hooks.ts # All hook handlers (session-start, session-end, prompt, stop, post-write, post-task)
│ ├── hooks.test.ts # Hook handler tests (colocated)
│ ├── session.ts # Create, append, finalize session files + git notes checkpoint
│ ├── session.test.ts # Session manager tests (colocated)
│ ├── summarize.ts # AI summarization on session end
│ ├── knowledge.ts # Knowledge base consolidation + mistake ledger + decision log + briefing
│ ├── knowledge.test.ts # Knowledge tests (colocated)
│ ├── search.ts # QMD indexing of session files (project-scoped)
│ ├── setup.ts # Writes Claude Code hooks config + git notes ref setup
│ ├── setup.test.ts # Setup tests (colocated)
│ ├── git.ts # Git operations (notes, rev-parse, diff)
│ ├── env.ts # Read Claude hook env vars
│ ├── paths.ts # Session file paths, XDG compliance
│ └── qmd.ts # Project-scoped QMD wrapper (--index per repo)
├── package.json
├── tsconfig.json
├── bun.lock
├── .gitignore
└── CLAUDE.md
Tests are colocated alongside source files (*.test.ts) and run with Bun's built-in test framework (bun test). Flat src/ directory — no nested subdirectories unless complexity demands it.
Use Bun as the runtime (bun not node, bun install not npm install). It starts in ~6ms vs ~30ms for Node, which matters when every hook invocation adds latency to the agent loop.
package.json:
{
"name": "ghost",
"version": "1.0.0",
"description": "Local AI session capture & search for Claude Code",
"type": "module",
"bin": {
"ghost": "./ghost"
},
"scripts": {
"test": "bun test",
"ghost": "bun src/index.ts",
"link": "bun link"
},
"dependencies": {
"yaml": "^2.8.2",
"zod": "^4.2.1"
},
"devDependencies": {
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "^5.9.3"
},
"engines": {
"bun": ">=1.0.0"
},
"license": "MIT"
}Minimal dependencies. Zod for CLI arg and config validation. YAML for any structured config files. Everything else is Bun builtins, git, and QMD.
Session IDs use crypto.randomBytes — no nanoid dependency needed.
tsconfig.json (identical to qmd):
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}Never run bun build --compile — it creates a standalone binary but breaks the ability to find bun dynamically across environments. The ghost file is a shell wrapper script that runs bun src/index.ts — do not replace it.
The ghost executable is a bash shell wrapper (same pattern as qmd):
#!/usr/bin/env bash
# ghost - Local AI Session Capture & Search
set -euo pipefail
# Find bun - prefer PATH, fallback to known locations
find_bun() {
if command -v bun &>/dev/null; then
local ver=$(bun --version 2>/dev/null || echo "0")
if [[ "$ver" =~ ^1\. ]]; then
command -v bun
return 0
fi
fi
: "${HOME:=$(eval echo ~)}"
if [[ "${BASH_SOURCE[0]}" == */.bun/* ]]; then
local bun_home="${BASH_SOURCE[0]%%/.bun/*}/.bun"
if [[ -x "$bun_home/bin/bun" ]]; then
echo "$bun_home/bin/bun"
return 0
fi
fi
local candidates=(
"$HOME/.local/share/mise/installs/bun/latest/bin/bun"
"$HOME/.local/share/mise/shims/bun"
"$HOME/.asdf/shims/bun"
"/opt/homebrew/bin/bun"
"/usr/local/bin/bun"
"$HOME/.bun/bin/bun"
)
for c in "${candidates[@]}"; do
[[ -x "$c" ]] && { echo "$c"; return 0; }
done
return 1
}
BUN=$(find_bun) || { echo "Error: bun not found. Install from https://bun.sh" >&2; exit 1; }
SOURCE="${BASH_SOURCE[0]}"
while [[ -L "$SOURCE" ]]; do
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
exec "$BUN" "$SCRIPT_DIR/src/index.ts" "$@"Install globally:
bun link # Makes `ghost` available system-wideFollow the same patterns as qmd:
- camelCase for functions and variables:
createSession(),projectIndex() - PascalCase for types and interfaces:
type SessionConfig,interface HookEvent - UPPER_SNAKE_CASE for constants:
SESSION_DIR,HOOK_TIMEOUT_MS,DEFAULT_SUMMARY_PROMPT - Section separators between logical sections in files:
// ============================================================================= // Session Management // =============================================================================
- JSDoc comments for exported functions explaining purpose
- Strict TypeScript — all exports explicitly typed,
noUncheckedIndexedAccessenabled - async/await throughout — no callbacks, no
.then()chains - CLI argument parsing via
util.parseArgs(built into Bun/Node) — no third-party CLI framework - Error handling via try/catch in CLI commands, silent failures in hooks
- No ESLint or Prettier — keep it simple, match the existing style by eye
Tests use Bun's built-in test framework (describe(), test(), expect()):
bun test # Run all tests
bun test src/session.test.ts # Specific test fileTest patterns:
- Each test gets a fresh temp directory for isolation
- Use environment variable overrides for paths (like qmd's
INDEX_PATHpattern) - CLI integration tests spawn actual
ghostprocesses - Tests must clean up after themselves
Running ghost enable inside a git repo does the following:
.ai-sessions/
├── active/ # Current in-progress session file
├── completed/ # Finalized session markdown files
├── knowledge.md # Auto-generated project knowledge base
├── mistakes.md # Mistake ledger
├── decisions.md # Decision log
├── tags.json # Tag → session ID index
└── .gitignore # Ignore active/, keep completed/
git config notes.displayRef refs/notes/ai-sessionsThis makes git log automatically show session notes inline. No orphan branches.
Generate .claude/settings.json (or merge into existing). All hooks call ghost <event> which dispatches to the appropriate handler.
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "ghost session-start" }
]
}
],
"SessionEnd": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "ghost session-end" }
]
}
],
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "ghost prompt" }
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "ghost stop" }
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "ghost post-write" }
]
},
{
"matcher": "Task",
"hooks": [
{ "type": "command", "command": "ghost post-task" }
]
}
]
}
}Write to the project-level .claude/settings.json (not global) so the MCP server exposes session search to Claude Code:
{
"mcpServers": {
"ghost-sessions": {
"command": "qmd",
"args": ["-c", "ghost-<project-name>", "mcp"]
}
}
}Where <project-name> is derived from basename $(git rev-parse --show-toplevel). The -c flag restricts search to the ghost collection for this project.
Install a standard git post-commit hook that runs ghost checkpoint asynchronously. This is the key non-blocking piece — it fires after every commit and attaches session context as a git note in the background:
#!/bin/sh
# .git/hooks/post-commit
ghost checkpoint &The & is critical. The commit completes immediately. The note attachment happens after.
qmd collection add .ai-sessions/completed/ --name ghost-$(basename $(git rev-parse --show-toplevel))
qmd context add .ai-sessions/completed "AI coding session transcripts, decisions, and reasoning"
qmd embed1. Generate session ID: YYYY-MM-DD-{crypto.randomBytes(4).toString('hex')}
2. Write session ID to .ai-sessions/active/current-id
3. Create session file: .ai-sessions/active/{session-id}.md
4. Write frontmatter:
---
session: {session-id}
branch: {current branch}
base_commit: {HEAD sha}
started: {ISO timestamp}
tags: []
---
5. Check for recent session on same branch (<24h with open items)
→ If found, inject warm resume context (see Session Continuity below)
6. Inject condensed mistake ledger if .ai-sessions/mistakes.md exists
7. Exit 0 immediately
Execution budget: <50ms.
1. Read $CLAUDE_USER_PROMPT from env
2. Append to active session file:
## Prompt {n}
> {user prompt text}
3. Exit 0
1. Read $CLAUDE_TOOL_ARG_FILE_PATH from env
2. Append to active session file:
- Modified: {filepath}
3. Increment file modification count in memory (for heatmap)
4. Exit 0
1. Read task output from env if available
2. Append brief task completion note to session file
3. Exit 0
This fires after each agent turn completes.
1. Append turn delimiter to session file:
---
_turn completed: {timestamp}_
2. Snapshot current diff stat:
git diff --stat >> session file (abbreviated)
3. Exit 0
This is the only hook that does real work, and it still must not block.
1. Finalize the session file (close any open sections, write file heatmap data)
2. Fork a background process that:
a. Generates an AI summary with tags, decisions, and mistakes (see below)
b. Appends summary to the session file
c. Auto-tags the session and updates tags.json
d. Extracts decisions → appends to decisions.md
e. Extracts mistakes → appends to mistakes.md
f. Moves session file from active/ to completed/
g. Attaches as git note to HEAD
h. Indexes into project-scoped QMD
i. Increments session counter — if threshold hit, triggers knowledge base rebuild
3. Exit 0 immediately (don't wait for background process)
The background process writes a PID file so ghost status can report if summarization is still running.
Every hook handler MUST exit in under 100ms. The rules:
- SessionStart, Prompt, PostWrite, PostTask, Stop: File appends only. No network, no subprocess spawning, no git operations beyond
rev-parse. - SessionEnd: Forks background process immediately, exits 0. All heavy work (summarize, git notes, qmd index) happens in the detached process.
- Post-commit hook: Runs
ghost checkpoint &— the ampersand ensures the commit returns instantly. - If anything fails, fail silently. A broken session log should never break the developer's workflow or block the agent.
On session end, the background process runs:
// summarize.ts
async function summarize(sessionPath: string): Promise<string> {
const result = await $`claude -p ${SUMMARY_PROMPT} < ${sessionPath}`;
return result.stdout;
}The prompt asks for a structured summary that also extracts tags, decisions, and mistakes in one pass:
Summarize this AI coding session. Return markdown with these sections:
## Intent
What was the developer trying to accomplish (1-2 sentences)
## Changes
Files modified and why (bullet list)
## Decisions
Key technical decisions made and reasoning. Only include significant decisions
involving architecture, technology choice, or approach selection.
Format each as:
**{short title}**: {context} → {decision} ({reasoning})
## Mistakes
Anything that went wrong, was reverted, or required multiple attempts.
Format each as:
**{short description}**: What happened → Why it failed → Correct approach
## Open Items
Anything left unfinished or flagged for follow-up
## Tags
Comma-separated topic tags inferred from the session content.
Use namespace:value format where appropriate (e.g. area:cart, type:bug-fix).
If claude CLI isn't available or the call fails, skip summarization silently. The raw session log is still valuable without it.
The checkpoint logic attaches session data to the relevant commit:
// checkpoint.ts
async function checkpoint() {
const sessionId = await readFile('.ai-sessions/active/current-id', 'utf8');
const sessionPath = `.ai-sessions/completed/${sessionId.trim()}.md`;
if (!existsSync(sessionPath)) return;
await $`git notes --ref=ai-sessions add -f -F ${sessionPath} HEAD`;
}Why git notes:
- No orphan branch pollution
- Attached directly to the commit they describe
- Visible in
git logwhennotes.displayRefis configured - Push with
git push origin refs/notes/ai-sessions - The semantically correct git primitive for commit metadata
Reading notes later:
git notes --ref=ai-sessions show abc123f # View session for a specific commit
git log --show-notes=ai-sessions # View all commits with session contextEvery project gets its own isolated QMD index. No leakage between projects.
Collection naming: Ghost creates a QMD collection per project, named ghost-<repo>:
// qmd.ts
import { $ } from "bun";
import path from "path";
/**
* Derive the QMD collection name from the git repo root.
* Gives you collections like ghost-10sq, ghost-wine-direct, etc.
*/
async function collectionName(): Promise<string> {
const root = (await $`git rev-parse --show-toplevel`.text()).trim();
return `ghost-${path.basename(root)}`;
}Indexing on session end:
async function indexSession(sessionPath: string) {
const name = await collectionName();
await $`qmd collection add .ai-sessions/completed/ --name ${name}`;
await $`qmd context add .ai-sessions/completed "AI coding session transcripts and reasoning"`;
await $`qmd embed`;
}What this enables:
Claude Code, via the QMD MCP server, can query past reasoning scoped to the current project:
- "Why did we choose that approach for fee calculation?"
- "What files did we touch when working on INP optimization?"
- "What decisions were made about the cart refactor?"
- "What's still open from last week's sessions?"
Tags are the filtering layer that makes everything else useful at scale.
The summarization prompt already extracts tags. These get written into the session frontmatter:
---
session: 2026-02-13-k8f2m9x1
branch: feature/fulfillment-retry
tags: [fulfillment, webhooks, area:api, type:bug-fix]
---ghost tag k8f2m9x1 "client:10sq" "migration"
ghost tag --last "performance" "inp"Ghost maintains .ai-sessions/tags.json for fast lookup without hitting QMD:
{
"fulfillment": ["2026-02-13-k8f2m9x1", "2026-02-10-m3n4o5p6"],
"client:10sq": ["2026-02-12-a1b2c3d4"],
"area:cart": ["2026-02-13-k8f2m9x1", "2026-02-11-q7r8s9t0"]
}ghost search --tag "fulfillment" "retry logic"
ghost search --tag "client:10sq" "metafields"Under the hood this runs qmd query -c ghost-<project-name> and filters results by checking frontmatter. Tags use a namespace:value convention for structured categorization — client:x, area:cart, type:bug-fix.
Claude starts every session cold. On a large project it spends the first 5-10 minutes re-reading files, re-discovering architecture, re-learning conventions. CLAUDE.md helps but it's static and manually maintained. The real knowledge lives in past sessions — what was tried, what failed, what patterns emerged — and Claude can't access any of it.
A persistent, auto-maintained knowledge base per project that distills session history into structured reference material. CLAUDE.md that writes itself.
After every N sessions (configurable, default 5) or on demand via ghost knowledge build, Ghost runs a consolidation pass:
1. Read all completed session summaries
2. Read current .ai-sessions/knowledge.md (if exists)
3. Prompt Claude to merge new session learnings into the knowledge base
4. Write updated knowledge.md
5. Index into project-scoped QMD
The knowledge base has fixed sections:
# Project Knowledge Base
_Auto-generated by Ghost. Last updated: 2026-02-13_
## Architecture
- Shopify Plus store with custom checkout extensions
- Cart logic in src/cart/, uses strategy pattern for fee calculation
- Fulfillment handled by custom app at src/fulfillment-app/
## Conventions
- All monetary values stored as integers (cents)
- Shopify metafields prefixed with `app--custom.`
- Tests colocated in __tests__/ directories
## Key Decisions
- Chose percentage-based fees over tiered (2026-02-13, session k8f2m9x1)
- Kept Liquid for PDP, moved collection pages to sections (2026-02-10)
- Rejected headless approach for checkout — native performs better (2026-02-08)
## Gotchas
- API rate limits hit at ~40 req/s during bulk metafield updates
- Fulfillment webhook fires twice on partial fulfillment — dedupe by order ID
- Don't use cart.js API for bundles, use AJAX API with sections
## Patterns That Work
- For complex Liquid: break into snippets <50 lines, test with theme check
- For metafield migrations: batch via GraphQL admin, not REST
- For INP: defer all non-critical JS to requestIdleCallback
## Open Threads
- Tax interaction with percentage fees still untested
- Need to verify Flow triggers on custom fulfillment eventsghost knowledge build # Rebuild now
ghost knowledge inject # Symlink/append to CLAUDE.md
ghost knowledge show # Print current knowledge base
ghost knowledge diff # Show what changed since last buildghost knowledge inject appends an include reference to CLAUDE.md or symlinks the knowledge file so the agent picks it up automatically. Every new session starts warm.
The knowledge base gets indexed into the project's ghost collection:
qmd context add .ai-sessions/knowledge.md "Project knowledge base — architecture, conventions, decisions, gotchas"
qmd update
qmd embedClaude makes the same mistakes repeatedly across sessions. It'll try an approach that was already tried and rejected, or hit a known API quirk that was debugged two weeks ago. There's no negative feedback loop.
A dedicated file .ai-sessions/mistakes.md that captures things that went wrong and shouldn't be repeated.
Auto-captured: The summarization prompt specifically extracts mistakes. Ghost appends new entries to the ledger.
Manual capture:
ghost mistake "Don't use cart.js API for bundles — it silently drops line properties over 250 chars. Use AJAX sections API instead."Injection on session start: When SessionStart fires, Ghost checks if mistakes.md exists and injects a condensed version into the session file header:
---
session: 2026-02-14-x9y8z7w6
---
> ⚠️ Known project pitfalls (12 entries):
> - cart.js API drops line properties >250 chars — use AJAX sections API
> - Fulfillment webhook fires twice on partial — dedupe by order ID
> - Flow custom triggers require app proxy, not direct APIThis is the negative knowledge base. Equally valuable to knowing what works.
On long-running projects, "why did we do it this way?" becomes unanswerable. The reasoning is buried in session transcripts nobody will re-read.
A standalone decision log at .ai-sessions/decisions.md capturing architectural and technical decisions in ADR-lite format:
## 2026-02-13: Percentage fees with hard cap
**Context:** Client wanted flexible fee structure.
**Decision:** Percentage with $50 hard cap for orders >$500.
**Reasoning:** Simpler, client preferred it, tiered adds complexity with no UX benefit.
**Session:** k8f2m9x1
## 2026-02-10: Keep Liquid for PDP
**Context:** Considered moving PDP to headless React.
**Decision:** Stay on Liquid with section-based architecture.
**Reasoning:** PDP performance is already good. Migration cost not justified.
**Session:** m3n4o5p6Auto-captured: The summarization prompt extracts significant decisions (architecture, technology choice, approach selection). Ghost appends them to the log.
CLI access:
ghost decisions # Show all
ghost decisions --tag "area:checkout" # Filter by tag
ghost search --decisions "fee structure" # QMD search scoped to decisionsStarting a new Claude Code session means losing all conversational context. The agent rebuilds its mental model from scratch.
On SessionStart, Ghost checks if there's a recent session on the same branch (within 24 hours, with open items) and auto-generates a continuity block injected at the top of the new session file:
## Context from Previous Session (2026-02-13-k8f2m9x1)
**What we were doing:** Migrating cart fee calculation from fixed to percentage-based.
**Where we left off:** Fee calculation works. Tests pass. Still need to:
- Update metafield sync to include fee strategy
- Test tax interaction with percentage fees
- Update checkout extension to read new fee format
**Files we were working in:**
- src/cart/fees.ts (main changes)
- src/cart/types.ts (new FeeStrategy type)
- src/cart/__tests__/fees.test.ts
**Key decisions made:**
- Percentage with hard cap over tiered (simpler, client preferred)
- Backward compat: default strategy to 'percentage' if unset
**Watch out for:**
- Fee cap logic has an edge case at exactly $500 — boundary test is fragileThis also gets optionally auto-appended to CLAUDE.md so the agent picks it up immediately. No manual "continue where we left off" prompting needed.
Manual resume:
ghost resume # Generate context handoff from last session on this branch
ghost resume k8f2m9x1 # Resume from a specific sessionThe biggest accelerator for Claude is starting a session with the right context already loaded. On a project with 500+ files, the agent reads the wrong things first.
Before starting work, generate a scoped context brief:
ghost brief "I need to add a new payment method to checkout"Ghost does the following:
- Searches QMD for relevant past sessions
- Pulls relevant decisions from the decision log
- Checks the mistake ledger for related gotchas
- Runs the file heatmap for related tags
- Synthesizes a brief:
## Brief: Adding a New Payment Method
### Relevant Past Work
- Session k8f2m9x1 (2026-02-10): Integrated Afterpay via checkout extension
- Session m3n4o5p6 (2026-01-28): Fixed payment method display ordering bug
### Key Files
- src/checkout/extensions/payment-methods.ts (31 changes across sessions)
- src/checkout/extensions/payment-config.json
- src/shopify-app/api/payment-customize.ts
### Relevant Decisions
- Payment methods configured via metafields, not hardcoded (2026-01-15)
- Custom payment apps must use Payment Customization API, not Functions (2026-01-20)
### Watch Out For
- Payment method ordering is fragile — test with 3+ methods enabled
- Checkout extension sandbox doesn't support all Payment API fields in dev mode
### Suggested Starting Point
Start with payment-methods.ts and the Payment Customization API docs.This is the "you are an expert on this project" context packet. Paste it as your first prompt or have Ghost auto-inject it.
Ghost tracks file modification frequency across all sessions from the PostToolUse(Write|Edit) data:
ghost heatmap 42 changes │ src/cart/fees.ts
38 changes │ src/cart/types.ts
31 changes │ src/checkout/extensions/fee-display.ts
28 changes │ src/fulfillment-app/webhooks/order.ts
15 changes │ src/theme/sections/product.liquid
9 changes │ src/utils/shopify-api.ts
Per-tag heatmaps:
ghost heatmap --tag "fulfillment" # Which files matter for fulfillment work?
ghost heatmap --json --top 20 # Structured output for context injectionThe heatmap feeds into both the knowledge base build and scope briefings. The "Architecture" section of the knowledge base auto-references which files are central vs peripheral.
Ghost tracks prompt → file change mappings in order during the session:
## Prompt 1 → Changes
> Refactor fee calculation to percentage-based
- src/cart/fees.ts (lines 42-89)
- src/cart/types.ts (lines 1-15)
## Prompt 2 → Changes
> Fix the edge case for orders over $500
- src/cart/fees.ts (lines 67-72)
- src/cart/__tests__/fees.test.ts (lines 30-45)On ghost show <commit>, this renders as an annotated diff — each hunk attributed to the prompt that caused it.
---
session: 2026-02-13-k8f2m9x1
branch: feature/cart-fees
base_commit: a1b2c3d4e5f6
started: 2026-02-13T09:30:00+11:00
ended: 2026-02-13T10:15:00+11:00
tags: [area:cart, fees, type:refactor]
---
> ⚠️ Known project pitfalls (8 entries):
> - cart.js API drops line properties >250 chars — use AJAX sections API
> - Fulfillment webhook fires twice on partial — dedupe by order ID
## Context from Previous Session (2026-02-12-p4q5r6s7)
**Where we left off:** Scoped out the fee migration, decided on percentage-based approach.
**Open items:** Implementation not started.
## Prompt 1
> Refactor the cart to use percentage-based fees instead of fixed amounts
- Modified: src/cart/fees.ts
- Modified: src/cart/types.ts
- Modified: src/cart/__tests__/fees.test.ts
---
_turn completed: 2026-02-13T09:35:12+11:00_
## Prompt 2
> The fee calculation is wrong for orders over $500, there should be a cap
- Modified: src/cart/fees.ts
- Modified: src/cart/__tests__/fees.test.ts
---
_turn completed: 2026-02-13T09:41:30+11:00_
## Summary
### Intent
Migrate cart fee system from fixed dollar amounts to percentage-based with a cap for high-value orders.
### Changes
- `src/cart/fees.ts` — Replaced fixed fee lookup with percentage calc, added $50 cap for orders >$500
- `src/cart/types.ts` — Added `FeeStrategy` type union
- `src/cart/__tests__/fees.test.ts` — Added edge case tests for cap boundary
### Decisions
**Percentage with hard cap over tiered:** Client wanted flexible fees. Chose percentage with $50 cap — simpler, client preferred, tiered adds complexity with no UX benefit.
### Mistakes
_None this session._
### Open Items
- Need to update metafield sync to include fee strategy
- Tax interaction with percentage fees untested
### Tags
area:cart, fees, type:refactor, percentage-fees| Command | Description |
|---|---|
ghost enable |
Run setup phase. Idempotent — safe to run multiple times. |
ghost disable |
Remove hooks from .claude/settings.json. Leave session files intact. |
ghost status |
Current session ID, completed count, QMD index status, background process state. |
ghost search <query> |
Shortcut for qmd query -c ghost-<project-name> <query>. Project-scoped. |
ghost search --tag <tag> <query> |
Tag-filtered search. |
ghost search --decisions <query> |
Search scoped to decision log. |
ghost log |
Pretty-print recent sessions with summaries. |
ghost show <commit-sha> |
Display session note attached to a specific commit. |
ghost tag <session-id> <tags...> |
Add tags to a session. |
ghost tag --last <tags...> |
Tag the most recent session. |
ghost knowledge build |
Rebuild project knowledge base from all sessions. |
ghost knowledge inject |
Symlink/append knowledge base to CLAUDE.md. |
ghost knowledge show |
Print current knowledge base. |
ghost knowledge diff |
Show what changed since last build. |
ghost mistake "<description>" |
Manually add entry to mistake ledger. |
ghost decisions |
Show all decisions. Supports --tag filter. |
ghost resume |
Generate context handoff from last session on this branch. |
ghost resume <session-id> |
Resume from a specific session. |
ghost brief "<description>" |
Generate scoped context brief for upcoming work. |
ghost heatmap |
Show file modification frequency across sessions. |
ghost stats |
Session metrics and trends. |
ghost reindex |
Rebuild the project's QMD collection from all completed sessions. |
ghost statsSessions (last 30 days): 47
Avg session duration: 23 min
Avg prompts per session: 6.2
Files modified: 142 unique
Top areas: cart (34%), checkout (28%), fulfillment (18%)
Mistakes logged: 8
Decisions recorded: 12
Knowledge base entries: 45
Trend: Session duration ↓12% vs previous 30 days
Trend: Prompts per session ↓8% (agent needs less guidance)
ghost stats --json # For dashboards
ghost stats --tag "area:cart" # Per-area stats
ghost stats --since 2026-01-01 # Date range| Phase | Feature | Why |
|---|---|---|
| Core | Session capture + QMD search (project-scoped) | Foundation — everything else builds on this |
| P1 | Knowledge base auto-generation | Biggest impact on context loss problem |
| P1 | Session tagging (auto + manual) | Makes everything else filterable |
| P1 | Warm resume / continuity | Immediate quality of life improvement |
| P2 | Mistake ledger | Prevents repeated failures |
| P2 | Decision log | Long-term project memory |
| P2 | Scope briefing | The "10x accelerator" feature |
| P3 | File heat map | Nice to have, improves briefings |
| P3 | Diff attribution | Useful for code review |
| P3 | Metrics | For proving ROI and refining workflows |
- Multi-worktree support: Each worktree gets its own active session directory. Use
git rev-parse --git-dirto scope correctly. The QMD index is still per-project (shared across worktrees of the same repo). - Session linking: If a session spans multiple commits, link the notes together via a shared session ID in the frontmatter.
- CLAUDE.md injection: On session start, optionally prepend a "recent context" section to CLAUDE.md with the last session's summary, giving the agent warm-start context.
- Pruning:
ghost prune --older-than 30dto clean up old session files and re-index the project's QMD index.