v0.9.1
Changelog
All notable changes to Chronicle are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
[0.9.0] — 2026-04-11
Added
Decision relationship DAG (chronicle relate) (I1)
chronicle relate "<title>" --depends-on "<title>"— record that a decision builds on anotherchronicle relate "<title>" --supersedes "<title>"— mark an old decision as replacedchronicle relate "<title>" --related-to "<title>"— soft cross-reference between decisionschronicle relate --list— print the full relation graph across all decisions- Relations stored as
<!-- relations:{...} -->inline HTML comments indecisions.mdrows (backward-compatible with existing stores) applyRelationToContent,buildRelationGraph,getRelatedRows,parseRelations,serializeRelations,addRelationToRow,removeRelationFromRow,extractTitleFromRowexported from@chronicle/core
Business/product context layer (chronicle context) (I2)
chronicle context add --goal|--constraint|--team|--stack|--non-goal <text>— add a fact to.lore/context.mdchronicle context remove ...— remove a context factchronicle context show— print current project contextchronicle context edit— opencontext.mdin$EDITORchronicle injectnow prepends the project context block at the top of every outputreadContext,writeContext,addContextFact,removeContextFact,formatContextForInjectexported from@chronicle/core
Ownership tracking (chronicle who) (I3)
chronicle who <file>— show owner(s) and all recorded decisions + risks for a file- Reads CODEOWNERS automatically (checks
CODEOWNERS,.github/CODEOWNERS,.gitlab/CODEOWNERS) - Falls back to
.lore/ownership.md(format:- \pattern`: @owner`) chronicle capturenow stamps<!-- author:email -->on each captured decision rowchronicle inject --filesnow includes a## File Ownershipsection when ownership is definedloadOwnership,getOwnersForFile,parseAuthorFromRow,setAuthorOnRow,buildOwnershipSection,writeLoreOwnershipexported from@chronicle/core
CI / Server-side automation
chronicle verify— CI gate: exits 1 when.lore/lags by more than--max-lagcommits (default 5);--jsonfor machine-readable outputchronicle process— batch processor for GitHub Actions: processes all uncached commits in one pass, writes.lore/process.log, exits 1 on LLM errors.github/workflows/chronicle.yml— official GitHub Actions workflow that triggers on push to main, runschronicle process, and commits updated.lore/back with[skip ci]— closes the "repo maintains itself" loop
Comprehensive test suite (149 tests across 9 suites)
extraction-parsing.test.ts— 21 tests covering malformed JSON, HTML-wrapped responses, null fields, truncated output, code block variations, and prompt completenessranker.test.ts— 26 tests forparseDecisionsTable,scoreRow(file match, age decay, risk/confidence bonus),rankDecisions(sort order, semantic blend, topN),estimateTokens,trimToTokenBudgetrag-quality.test.ts— 28 behavioral tests: "does the store contain the right knowledge?" — decisions completeness, rejections format, risks content, evolution eras, deep ADR structure, inject output ranking, staleness, token budget, relations DAG, business contextpipeline.test.ts— 10 end-to-end integration tests using real git repos (temp fixture) + mock LLM: feature commit → decisions.md, security → high-risk, rejection → rejected.md, noise filtering, store write correctness, deep ADR creation, cache prevents re-processingfixtures.ts— shared fixture factory:buildProjectRepo()(7-commit git repo covering all change types),buildPopulatedLore()(pre-populated.lore/for inject tests),buildMockLLM()(keyword-aware mock returning realistic ExtractionResults)
Fixed
parseExtractionResponsenow filters non-object elements from arrays (LLM hallucination of[1, 2, 3]no longer passes through as decisions)scoreRowrecentFiles matching now uses prefix matching: a row affectingsrc/auth/correctly matches a recent filesrc/auth/jwt.ts- Extraction cache now marks noise commits (zero results) as processed, preventing re-querying on the second pass
buildExtractionPromptnow includes the commit hash in the prompt so the LLM can return it in each result object (enabling accurate cache keying)- Evolution test updated:
buildEvolutioncorrectly synthesizes a singlev0.1 (initial)era for repos with no git tags (time-based era synthesis was added in v0.3.0 but the test expectation was not updated)
[0.8.0] — 2026-04-11
Added
Multi-source knowledge ingestion
chronicle add — register additional knowledge sources:
--repo <path|url>— secondary git repo (clones remotes to~/.chronicle/repos/); decisions extracted immediately--dir <path>— local directory (.md,.ts,.py,.go, etc.)--url <url>— web page (HTML stripped to text)--pdf <path>— PDF file (text PDFs; requires optionalpdf-parse)--list/--remove <id>— manage registered sources- Source registry persisted at
.lore/sources.json
chronicle ingest — index dir/url/pdf sources into .lore/chunks/{sourceId}/:
- Chunks text at ~500-token boundaries (paragraph-aware)
- Skips already-ingested sources unless
--force --id <id>to re-ingest a single source
Unified search (M4)
chronicle searchnow also scans.lore/chunks/in keyword mode- Semantic/hybrid modes already work across all embedded content
Git merge driver for decisions.md (M5)
chronicle hooks installnow registersmerge.chronicle-decisionsin.git/config- Adds
.lore/decisions*.md merge=chronicle-decisionsto.gitattributes chronicle merge-driver <base> <ours> <theirs>(internal, called by git):- Union-merges table rows from both branches
- Deduplicates by title (keeps newest date)
- Exits 0 → conflict-free merge; exits 1 → unresolvable
Source abstraction layer (@chronicle/core)
SourceConfig,SourceType,SourceRegistrytypesloadSourceRegistry,saveSourceRegistry,addSource,removeSource,listSources,getSource,markIngested,deriveSourceIdchunkText,ingestDir,ingestUrl,ingestPdffromingestor.ts
[0.7.0] — 2026-04-11
Added
Local embedding engine (@chronicle/core)
embed(text)/embedBatch(texts)— MiniLM-L6-v2 via@huggingface/transformers(22MB, fully offline)cosineSimilarity(a, b)— dot product on normalized vectorsloadEmbeddingCache/saveEmbeddingCache— SHA-256 content-keyed JSON cache at.lore/embeddings.jsongetEmbeddings(texts, cache)— batch embedding with cache deduplication (only new content is embedded)@huggingface/transformersis anoptionalDependency— Chronicle works without it, embedding features degrade gracefully to heuristic mode
Semantic search (chronicle search)
--semantic— pure vector similarity search using MiniLM embeddings--hybrid— linear blend: 0.7 × semantic + 0.3 × keyword score- Visual score bar output (█░░░ style) with source attribution
- Graceful fallback to keyword search with install hint if transformers not available
Semantic inject ranking (chronicle inject)
--query <text>— natural language query that re-ranks decision rows by semantic similarity- Phase 2 ranker:
0.6 × semantic + 0.4 × heuristicblend when--queryprovided buildSemanticScores(rows, query)exported from@chronicle/core
Incremental vector index (post-commit hook)
buildEmbeddingIndex(root)— indexes all decisions, rejects, risks; skips cached content- Called automatically by
chronicle captureafter each commit (only new decisions are embedded)
RAG quality harness (chronicle eval)
chronicle eval— runs 4 KPI checks: Decision Recall, Rejection Hit Rate, Semantic MRR@5, False Confidence Ratechronicle eval --init— bootstraps.lore/.eval.jsonfrom existing decisions (ready to run immediately)--jsonfor machine-readable output;--verbosefor per-case details- Exits 1 if any KPI is below target (suitable for CI)
- KPI targets: Recall ≥ 80%, Rejection Hit ≥ 90%, MRR@5 ≥ 0.70, False Confidence ≤ 10%
Changed
rankDecisions()now acceptssemanticScores?: Map<string, number>for hybrid scoring
[0.6.0] — 2026-04-11
Added
Relevance-ranked inject (chronicle inject)
--top <n>— return only the N most relevant decisions (ranked by heuristic score)--tokens <n>— auto-trim output to fit within N tokens (~4 chars/token)--min-confidence <n>— omit decisions below a confidence threshold (0.0–1.0)rankDecisions()in@chronicle/core— heuristic scoring: file match ×3, recent file ×1, risk bonus (high=+2, medium=+1), age decay, confidence bonustrimToTokenBudget()— greedy section trimmer with partial truncation fallback
Confidence scores on decisions
- LLM extraction now returns a
confidencefield (0.0–1.0) per decision - Stored as
<!-- confidence:0.72 -->inline HTML comments indecisions.mdrows - Backward-compatible: existing stores default to
confidence=1.0
Staleness detection (chronicle inject --no-stale)
- Automatically flags decisions whose affected files have been significantly modified since the decision was recorded
- One-shot
git log --name-onlyscan builds aFileModMap— no per-decision git calls - Stale decisions annotated with
<!-- stale -->and surfaced as a⚠️ Potentially Stale Decisionswarning block in inject output - Disable with
--no-stalefor faster runs without git access
Session history index (chronicle session save)
- Every
session savenow rebuilds.lore/sessions/_index.md— a compact markdown table of all sessions chronicle injectincludes the history index + most recent raw session (not just the last session)_index.mdis excluded fromsession listandsession showoutput
Graph monorepo awareness (chronicle graph)
--depth <n>— control how many path segments to group by (default: 2)--monorepo— force monorepo mode (auto-detected frompackages/,apps/,services/,libs/dirs)- Paths under monorepo root dirs always group at depth 2 regardless of
--depth - Monorepo badge displayed in graph top-bar when detected
@chronicle/core exports
rankDecisions,parseDecisionsTable,scoreRow,estimateTokens,trimToTokenBudget(fromranker.ts)buildFileModMap,annotateStaleDecisions,formatStaleWarning(fromstaleness.ts)
Changed
decisions.mdtable now includesDateas first column (added in G1)- Graph
buildGraphData()acceptsGraphOptionswithdepthandmonorepofields
[0.5.0] — 2026-04-10
Added
New Commands
chronicle doctor— validates.lore/health: checks for missing files, broken ADR links, cache integrity, and git hook installationchronicle search <query>— full-text search across all.lore/markdown files with highlighted matches and file:line context;--jsonflag for machine outputchronicle serve [--port]— zero-dependency local web viewer (dark theme, sidebar nav, live search); opens in browser automaticallychronicle session save|list|show— save and browse session notes in.lore/sessions/; supports piped input for auto-summaries
Parallel Extraction
extractFromCommits()now runs LLM batches concurrently (defaultconcurrency=4for API providers,1for Ollama)--concurrency <n>flag oninitanddeepenfor manual override- Benchmark: 4 batches in parallel → ~4× speedup vs sequential for Anthropic/Gemini/OpenAI
Progressive History (--limit)
chronicle init --limit <n>— cap initial scan to N most recent commitschronicle deepen --limit <n>— process additional batches incrementally- Enables fast first-run (20 commits → ~5s with API) then deepening as needed
LLM Provider Updates
- Gemini updated to
gemini-2.5-flashwiththinkingBudget: 0(thinking mode disabled for structured extraction — 8× faster) - All providers now defensively handle partial LLM responses (missing
title,affects,riskfields no longer crash)
Null-safety fixes
buildStore,formatDeepADR,formatDecisionEntry,formatRejectionEntryall handle LLM responses missing optional fields- Cache fallback: results matched to commits by
hashfield, with positional fallback
Fixed
deepennow accepts--llm,--limit,--concurrency(was hardcoded to anthropic, no limit)git logdelimiter changed from|to\x1f(ASCII unit separator) — fixes parsing for repos with|in commit subjects
[0.4.0] — 2026-04-10
Added
Semantic Clustering Extraction (strategy: 'clustered')
extractFilesFromDiff()— parsesdiff --git a/X b/Xheaders to extract the file set touched by a commitclusterCommitsByFileOverlap()— groups commits into cohesive clusters where all members share at least one touched file- Isolated commits (no file overlap with neighbours) are batched together up to 4 per group to avoid excessive LLM calls
- Clusters respect
MAX_CLUSTER_SIZE=8andMAX_CLUSTER_CHARS=8000to stay within LLM context limits strategyClustered()— runs one LLM call per semantic cluster instead of per fixed-size batch- Pass
{ strategy: 'clustered' }toextractFromCommits()to use the new strategy
Why it matters: The v1 simple strategy could batch "add JWT auth" with unrelated CSS tweaks. The clustered strategy ensures the LLM sees a coherent feature narrative — all commits that touched auth/ together — yielding richer rationale extraction.
Tests
- 8 new clustering tests: file parsing, overlap detection, singleton merging, MAX_CLUSTER_SIZE cap, LLM call count
- Total: 64 tests passing (58 TS + 6 Python)
[0.3.0] — 2025-04-10
Added
Evolution Records (chronicle evolution)
buildEvolution()— groups git history into eras (one per release tag + current untagged work)- Each era contains: decisions made, rejections logged, most-changed files, date range
renderEvolutionMarkdown()— renders eras to human+AI-readable.lore/evolution.mdmergeWithExisting()— preserves manually-written> summaryfields when regeneratingchronicle evolution --regen— force-rebuild;--view— print to stdout- Auto-generates
evolution.mdat the end ofchronicle init - Evolution included in
chronicle injectoutput (first era, compact)
Terminal Status Indicator
- Before every write command:
◆ chronicle │ N decisions · N rejected · N ADRs · last capture: Xm ago - After every write command:
◆ chronicle wrote +N decisions, +N rejections(only if something changed) - Written to stderr — doesn't pollute
chronicle injectstdout pipe - Active for:
init,deepen,setup,diagram,evolution,capture
Tests
- 12 evolution tests: tag detection, era chaining, HEAD detection, rendering, merge behavior
- Total: 62 tests passing (56 TS + 6 Python)
[0.2.0] — 2025-04-10
Added
Tool adapters (chronicle setup --tool=<name>)
claude-code/openclaw— MCP server config (.claude/mcp.json) + SessionStart/Stop hooks (.claude/settings.json); merges with existing configcursor— generates.cursorruleswith current.lore/contextaider— writes.aider.conf.ymlwith--readentries for.lore/files; idempotentgemini-cli— generatesGEMINI.mdwith compressed contextcopilot— generates.github/copilot-instructions.mdcodex— appends Chronicle context toAGENTS.mdopencode— writes.opencode.jsonwithcontextFilesentriestrae/factory— universal pipe instructions (chronicle inject | <tool>)chronicle setupwith no args lists all available integrationschronicle setup --allinstalls every adapter at once
ASCII Diagrams (chronicle diagram)
- All diagrams are plain
.txtfiles — render anywhere (terminal, GitHub, AI context windows) architecture.txt— module tree grouped by directory, relationships from decision logdependencies.txt— import graph from source files; highlights high-blast-radius files (≥3 dependents)evolution.txt— timeline from git tags + dated decision entries, grouped by year
Tests
- 11 adapter tests covering install, idempotency, merge behavior, pipe tools
- Total: 50 tests passing (44 TS + 6 Python)
Changed
- Diagram files use
.txtextension instead of.mmd— ASCII format, no renderer needed
Unreleased
Planned
- Phase 8 (v3): Two-pass extraction (cheap LLM filter → quality model for complex decisions)
0.1.0 — 2025-04-10
Initial release of Chronicle — AI-native development memory.
Added
Core (@chronicle/core)
store.ts— file-based markdown store withfindLoreRoot(walks up like git),readStore,writeStore,appendToStore,writeDeepDecision,initStorescanner.ts— git history scanner with noise filtering (chore/style/docs/test prefixes), diff size threshold (≥20 changed lines), diff capping (4000 chars), tag detectionextractor.ts— LLM extraction engine with pluggable strategy pattern; v1simplestrategy (batches of 6, ≤5000 chars);clusteredandtwo-passslots reserved for v2/v3cache.ts— SHA-keyed JSON file cache; prevents reprocessing commits across runsExtractionCacheinterface for swappable cache backends (in-memory, file, future SQLite)
CLI (chronicle-dev)
chronicle init [--depth]— bootstraps.lore/from git history; progressive scan defaults to 6 monthschronicle inject [--files] [--full] [--format]— outputs compressed context to stdout; scopes to relevant files; supportsmarkdown,xml,plainformatschronicle deepen [--depth]— extends scan further back without reprocessing cached commitschronicle hooks install/remove— installspost-commit(async decision capture) andprepare-commit-msg(risk annotation) git hooks- Internal
chronicle captureandchronicle enrich-commitcommands (invoked by hooks)
MCP Server (@chronicle/mcp)
chronicle_get_context— injects compressed project context; scopes to files if specifiedchronicle_log_decision— AI logs architectural choices mid-sessionchronicle_log_rejection— AI logs abandoned approaches (crown jewel: prevents future repetition)chronicle_get_risks— returns blast-radius info before touching a filechronicle_save_session— summarizes session to.lore/sessions/YYYY-MM-DD.md
Python wrapper (chronicle-dev on PyPI)
- Thin subprocess wrapper; delegates all logic to Node CLI
- Detects Node ≥ 20; falls back to
npx chronicle-devif not globally installed - Entry point:
chroniclecommand identical to npm version
Project
.lore/store structure:index.md,decisions.md,decisions/,rejected.md,risks.md,evolution.md,diagrams/,sessions/- Hub-and-spoke ADR model: shallow decisions inline, complex decisions in
decisions/<slug>.md ARCHITECTURE.md— full system design documentation- GitHub Actions CI (test on every push/PR) and Release (publish npm + PyPI on tag)
- Vitest test suite for core package (store, extractor, scanner)
- pytest suite for Python wrapper
Architecture decisions
- Markdown-only store: no vector DB, no embeddings — plain files readable by any LLM
- Commit SHA cache ensures bootstrap is idempotent and cost-efficient
- Strategy pattern in extractor: v1 ships today, v2/v3 are drop-in replacements
- Python package delegates to Node — single source of truth, no duplication