Local memory and continuity engine for AI-assisted development
Kindling captures what happens during AI-assisted development — tool calls, diffs, commands, errors — and makes it retrievable across sessions. All data stays local in embedded SQLite.
The fastest way to use Kindling — automatic memory for every Claude Code session.
# Install and set up in one step
curl -fsSL https://raw.githubusercontent.com/EddaCraft/kindling/main/install.sh | sh
# Or with npx (no global install)
npx @eddacraft/kindling-cli init --claude-codeThat's it. Kindling now captures your Claude Code sessions automatically — tool calls, file edits, commands, errors — all searchable across sessions.
Manual setup: If you prefer to configure hooks yourself, add to .claude/settings.json:
{
"hooks": {
"SessionStart": [{ "type": "command", "command": "kindling-hook session-start" }],
"PostToolUse": [{ "type": "command", "command": "kindling-hook post-tool-use" }],
"Stop": [{ "type": "command", "command": "kindling-hook stop" }]
}
}Node.js >= 20 required. Prebuilt binaries ship for Linux (glibc), macOS (Intel + Apple Silicon), and Windows (x64).
# Recommended: one-line installer (installs CLI + Claude Code plugin)
curl -fsSL https://raw.githubusercontent.com/EddaCraft/kindling/main/install.sh | sh
# Or install with your preferred package manager
npm install -g @eddacraft/kindling-cli # CLI (global)
npm install @eddacraft/kindling # Library (project-local)
pnpm add -g @eddacraft/kindling-cli
pnpm add @eddacraft/kindling
yarn global add @eddacraft/kindling-cli
yarn add @eddacraft/kindling
bun add -g @eddacraft/kindling-cli
bun add @eddacraft/kindlingIf prebuilt binaries aren't available for your platform
Kindling uses better-sqlite3 which needs a C++ compiler to build from source:
- Debian/Ubuntu:
sudo apt-get install build-essential python3 - Fedora/RHEL:
sudo dnf groupinstall "Development Tools" - Alpine (musl):
apk add build-base python3 - macOS:
xcode-select --install - Windows (Admin):
npm install -g windows-build-tools
The CLI is both a reader and a writer — you can capture observations manually, not just search for them.
# Initialize Kindling (creates database, optionally sets up Claude Code)
kindling init
kindling init --claude-code
# --- Write: capture context from the command line ---
# Log an observation directly
kindling log "JWT tokens expire after 15 minutes, not 1 hour"
kindling log --kind error "segfault in auth middleware after upgrade"
# Open/close capsules for manual sessions
kindling capsule open --intent "investigating memory leak" --repo ./my-project
kindling capsule close cap_abc123 --summary "root cause: unbounded cache in SessionStore"
# --- Read: search and inspect your memory ---
# Search across all captured context
kindling search "authentication error"
kindling search "auth" --session session-123 --repo ./my-project
# List entities
kindling list capsules
kindling list capsules --status open
kindling list observations --kind error
# Show database status
kindling status
# Pin important findings (always returned first in searches)
kindling pin observation obs_abc123 --note "Root cause identified"
kindling pin observation obs_abc123 --ttl 7d
# Inspect details
kindling inspect observation obs_abc123
kindling inspect capsule cap_xyz789
# Export / import
kindling export ./backup.json
kindling import ./backup.json
# Start API server (for multi-agent access)
kindling serve --port 3000Kindling organizes memory into two layers:
Observations — atomic units of captured context (tool calls, commands, file diffs, errors, messages). These flow in automatically from adapters or manually via the CLI.
Capsules — bounded groups of observations (a session, a workflow run). Each capsule has an intent, a lifecycle (open/close), and a summary.
When you search, Kindling returns results in three tiers:
- Pins — user-marked priority items (always first, non-evictable)
- Current Summary — active session context
- Provider Hits — ranked FTS results with provenance ("why was this returned?")
Kindling captures context automatically through adapters:
| Adapter | What it captures |
|---|---|
| Claude Code | Tool calls, file edits, commands, user messages, subagent completions |
| OpenCode | Session events and tool activity |
| PocketFlow | Workflow node lifecycle and outputs |
Or capture manually with the CLI (kindling log, kindling capsule open/close).
| Package | Description |
|---|---|
@eddacraft/kindling |
Main package: core + SQLite store + provider + API server |
@eddacraft/kindling-cli |
CLI for reading, writing, and managing memory |
@eddacraft/kindling-core |
Lightweight types + KindlingService (for adapter authors) |
@eddacraft/kindling-store-sqljs |
sql.js WASM store (browser environments) |
@eddacraft/kindling-adapter-claude-code |
Claude Code hooks integration |
@eddacraft/kindling-adapter-opencode |
OpenCode session integration |
@eddacraft/kindling-adapter-pocketflow |
PocketFlow workflow integration |
For building on Kindling as a library:
import { randomUUID } from 'node:crypto';
import {
KindlingService,
openDatabase,
SqliteKindlingStore,
LocalFtsProvider,
} from '@eddacraft/kindling';
const db = openDatabase({ path: './my-memory.db' });
const store = new SqliteKindlingStore(db);
const provider = new LocalFtsProvider(db);
const service = new KindlingService({ store, provider });
// Open a session capsule
const capsule = service.openCapsule({
type: 'session',
intent: 'debug authentication issue',
scopeIds: { sessionId: 'session-1', repoId: 'my-project' },
});
// Capture observations
service.appendObservation(
{
id: randomUUID(),
kind: 'error',
content: 'JWT validation failed: token expired',
provenance: { stack: 'Error: Token expired\n at validateToken.ts:42' },
scopeIds: { sessionId: 'session-1' },
ts: Date.now(),
redacted: false,
},
{ capsuleId: capsule.id },
);
// Search
const results = await service.retrieve({
query: 'authentication token',
scopeIds: { sessionId: 'session-1' },
});
// Close with summary
service.closeCapsule(capsule.id, {
generateSummary: true,
summaryContent: 'Fixed JWT expiration check in token validation middleware',
});
db.close();| Package | Description |
|---|---|
@eddacraft/kindling |
Main package: re-exports core + SQLite store + local FTS provider |
@eddacraft/kindling-core |
Domain types, KindlingService, validation (for adapter authors, browser) |
@eddacraft/kindling-store-sqlite |
SQLite persistence with FTS5 and WAL mode |
@eddacraft/kindling-store-sqljs |
sql.js WASM store for browser compatibility |
@eddacraft/kindling-provider-local |
Local FTS-based retrieval provider with deterministic ranking |
@eddacraft/kindling-server |
HTTP API server for multi-agent concurrency (Fastify) |
@eddacraft/kindling-cli |
CLI tools for inspection, search, and management |
@eddacraft/kindling-adapter-opencode |
OpenCode session integration |
@eddacraft/kindling-adapter-pocketflow |
PocketFlow workflow integration with intent and confidence tracking |
@eddacraft/kindling-adapter-claude-code |
Claude Code hooks integration |
Adapters
┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐
│ OpenCode │ │ Claude Code │ │ PocketFlow Nodes │
│ Sessions │ │ (Hooks) │ │ (Workflows) │
└──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘
│ │ │
└─────────────────┴─────────────────────┘
▼
┌──────────────────────────────┐
│ @eddacraft/kindling │ ← Main package
│ ┌────────────────────────┐ │
│ │ KindlingService │ │
│ │ (kindling-core) │ │
│ └──────────┬─────────────┘ │
│ │ │
│ ┌──────────┴────────────┐ │
│ ▼ ▼ │
│ SqliteStore LocalFts │
│ (persistence) Provider │
│ └──────┬───────┴──────┘ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ SQLite Database │ │
│ │ (WAL + FTS5) │ │
│ └─────────────────────┘ │
│ │
│ API Server (Fastify) │
└──────────────────────────────┘
# Show database status
kindling status
# Search for context
kindling search "authentication error"
kindling search --session session-123
# List entities
kindling list capsules
kindling list pins
kindling list observations
# Pin important findings
kindling pin observation obs_abc123 --note "Root cause identified"
# Remove a pin
kindling unpin pin_xyz789Atomic units of captured context:
| Kind | Description |
|---|---|
tool_call |
AI tool invocations (Read, Edit, Bash, etc.) |
command |
Shell commands with exit codes and output |
file_diff |
File changes with paths |
error |
Errors with stack traces |
message |
User/assistant messages |
node_start / node_end |
Workflow node lifecycle |
node_output / node_error |
Workflow node results |
Bounded units of meaning that group observations:
- Session - Interactive development session
- PocketFlowNode - Single workflow node execution
Each capsule has:
- Type and intent (debug, implement, test, etc.)
- Open/close lifecycle with automatic summary generation
- Scope (sessionId, repoId, agentId, userId)
Deterministic, explainable retrieval with 3 tiers:
- Pins - Non-evictable, user-controlled priority content
- Current Summary - Active session/capsule context
- Provider Hits - Ranked FTS results with explainability
Resume work without re-explaining context:
import { SessionManager } from '@eddacraft/kindling-adapter-opencode';
const manager = new SessionManager(store);
// Start session
manager.onSessionStart({
sessionId: 'session-1',
intent: 'Fix authentication bug',
repoId: '/home/user/my-project',
});
// Events flow in automatically...
// Later: retrieve session context
const context = service.retrieve({
scopeIds: { sessionId: 'session-1' },
});Capture high-signal workflow executions with PocketFlow nodes:
import { KindlingNode, KindlingFlow } from '@eddacraft/kindling-adapter-pocketflow';
import type { KindlingNodeContext } from '@eddacraft/kindling-adapter-pocketflow';
// Define a node that auto-captures its lifecycle as observations
class TestRunnerNode extends KindlingNode<KindlingNodeContext> {
constructor() {
super({ name: 'run-integration-tests', intent: 'test' });
}
async exec(): Promise<unknown> {
// Your node logic here — prep/exec/post are auto-instrumented
return { passed: 42, failed: 0 };
}
}
// Run inside a flow with a Kindling-aware shared store
const node = new TestRunnerNode();
const flow = new KindlingFlow(node);
await flow.run({ store, scopeIds: { repoId: 'my-app' } });Mark important discoveries for non-evictable retrieval:
service.pin({
targetType: 'observation',
targetId: errorObs.id,
note: 'Root cause of production outage',
ttlMs: 7 * 24 * 60 * 60 * 1000, // 1 week
});
// Pins always appear first in retrieval
const results = service.retrieve({ query: 'outage' });
console.log(results.pins); // Includes the pinned error- Capture, Don't Judge — preserves what happened without asserting truth
- Deterministic & Explainable — retrieval results include "why" explanations
- Local-First — no external services, embedded SQLite
- Privacy-Aware — automatic redaction of secrets, bounded output capture
- Provenance Always — every piece of context points to concrete evidence
Kindling captures what happened. Anvil enforces what should happen.
Request access to the Anvil closed beta → eddacraft.ai
git clone https://github.com/EddaCraft/kindling.git
cd kindling
pnpm install
pnpm run build
pnpm run test
pnpm run type-checkThis project uses Anvil Plan Spec (APS) for planning.
Full docs at docs.eddacraft.ai/kindling/overview:
See CONTRIBUTING.md for development guidelines and SECURITY.md for responsible disclosure.
Apache 2.0 — See LICENSE for details.
Built by the EddaCraft team