The bootstrap and memory system is fully connected to the system prompt flow. Files are automatically loaded and injected into all interactions — in chat mode as well as in
/agent and /coder modes.Bootstrap Files
Bootstrap files are Markdown documents automatically loaded into the agent’s system prompt. They define who the assistant is, how it behaves, and which rules it should follow.Supported Files
ChatCLI loads exactly 5 bootstrap files, in this order. All are optional — if they don’t exist, they are simply skipped:Loading Priority
Files are searched at two levels, with the workspace taking priority:1
Workspace (project root)
Project-specific configurations. Takes priority over global. The project root is detected automatically (see below).
2
Global (~/.chatcli/)
User default configurations. Serves as a fallback when the file does not exist in the workspace.
Automatic Workspace Detection
ChatCLI usesdetectProjectDir() to find the real project root. Instead of simply using the current directory (CWD), it walks up the directory tree looking for project markers:
- Checks if the current directory contains
.git/or.agent/ - If not found, moves to the parent directory and repeats
- Continues until a marker is found or it reaches the filesystem root
- If no marker is found, falls back to the CWD
Recognized markers:
.git (Git repository) and .agent (explicit ChatCLI marker). Only one needs to exist to define the workspace root.Example Scenarios
Detailed Examples
- SOUL.md
- USER.md
- IDENTITY.md
- RULES.md
- AGENTS.md
Defines personality and tone. Place at
~/.chatcli/SOUL.md (global) or ./SOUL.md (project):Where to Place Files
“Project root” means the directory containing.git/ or .agent/ — automatically detected by detectProjectDir(), not necessarily the CWD.
Smart Cache
Bootstrap files use mtime-based (modification time) caching:- On the first read, the content is cached in memory
- Subsequent reads check if the mtime has changed
- If the file was modified, the cache is automatically invalidated
IsStale()checks if any file has changed since the last load
Persistent Memory
The memory system maintains context across ChatCLI sessions using structured storage with multiple components that learn about you and your work over time.System Architecture
Resilient extraction — nothing is lost silently
Extraction depends on a background LLM call — and a provider outage must not cost the conversation its memory. Three layered defenses:- Fallback chain: extraction tries the session’s active client and, on failure, walks
CHATCLI_MEMORY_FALLBACK_PROVIDERS(orCHATCLI_FALLBACK_PROVIDERS), with a per-attempt timeout. - Durable on-disk queue: a segment that fails on every provider is written to
~/.chatcli/memory/pending/(atomic writes) and retried on later runs, oldest first — it survives restarts. The queue is capped (100 segments) and corrupt files are dropped without wedging the rest. - A visible notice: two consecutive failures print a one-liner in the terminal (
memory: extraction failing…) — days of silent fact loss can no longer happen.
The gateway consults this memory too: the daemon’s persona calls
@memory recall before answering “I don’t know” to personal questions. See Chat Gateway.Storage Structure
All memory lives in~/.chatcli/memory/:
Components
FactIndex -- Long-Term Memory
FactIndex -- Long-Term Memory
Replaces the old append-only MEMORY.md. Each fact has:
- Unique ID via SHA-256 content hash (automatic deduplication)
- Category: architecture, pattern, preference, gotcha, project, personal
- Temporal score:
(1 + log(accessCount)) * exp(-days * ln2 / halfLife) - Tags for keyword search
UserProfile -- User Profile
UserProfile -- User Profile
Automatically detected by the AI during extraction and editable by you/the model in any mode:
- Name, role, expertise level, company, location
- Preferred language and communication style
- Lists with lifecycle: certifications, skills, goals, interests and directives (restating an item supersedes the old one instead of duplicating;
_replace/_done/_removesuffixes rewrite) - Directives with severity (hard rules vs preferences) and per-project scope (
[scope:<project>] ruleis only injected when that workspace is active) - Stances — technical positions recorded with the why (
"position :: reason") - Milestones — dated timeline of what happened
- Structured Environment (
env_os,env_shell, …) — auto-migrated from legacy preferences - Per-field provenance and freshness: each attribute knows whether it came from you or from extraction and when it was last confirmed; fields unconfirmed for 120+ days are flagged as possibly stale in the prompt
- Privacy tier: finance/health/family/document keys are auto-tagged
[sensitive]— they personalize answers but never enter code, examples or generated artifacts (sensitive_mark/sensitive_unmarkfor manual control) - Most used commands (top 10) and general preferences
/memory profile. Old profiles polluted by append-only versions self-heal on the next load (fragments, progress duplicates and completed goals leave on their own).TopicTracker -- Recurring Topics
TopicTracker -- Recurring Topics
Tracks technical topics discussed:
- Mention frequency
- Recency (recent topics weigh more)
- Links to related facts
/memory topics.ProjectTracker -- Projects
ProjectTracker -- Projects
Tracks projects you work on:
- Name, path, description
- Technologies used
- Status (active, paused, completed)
- Last activity
/memory projects.PatternDetector -- Usage Patterns
PatternDetector -- Usage Patterns
Analyzes how you use ChatCLI:
- Total sessions and average duration
- Peak activity hours
- Preferred features (chat, agent, coder)
- Common errors and resolutions
/memory stats.Smart Retrieval
Instead of dumping all memory into the system prompt, ChatCLI uses intelligent retrieval:- Extracts keywords from the last few conversation messages
- Searches relevant facts in FactIndex by keyword match + temporal score
- Respects a configurable budget (default: 4000 characters)
- Prioritizes: Profile > Projects > Topics > Relevant facts > Recent notes > Trajectory (digests) > Usage patterns
Facts accessed by the retriever automatically get their scores bumped, creating a virtuous cycle: the more useful a fact is, the more it appears.
Injection mode: push vs pull
How memory reaches the model in/agent and /coder is controlled by CHATCLI_MEMORY_MODE:
Proactive auto-recall closes the pull model’s gap: models sometimes skip the
@memory recall call even when a stored gotcha is exactly what they need. In index mode, each agent/coder turn also ranks the fact index against keywords from the recent messages and rides the top 3 matches into the prompt as a tiny [MEMORY AUTO-RECALL] block (700-byte cap). The block lives in the uncached trailing region next to the wall-clock context, so the stable digest stays byte-identical and prompt caching is unaffected. Gated by CHATCLI_MEMORY_AUTORECALL (on by default).
The pull mode (index) shrinks the per-turn memory block by ~88% on a 500-fact store without losing access to detail — see Token Efficiency › Pull-first memory for the full measurement. Chat is tool-less and cannot pull on demand: there index degrades to full. The @memory recall tool uses HyDE + vector search, so pulled detail matches push quality. Check the active mode with /config memory.
Memory Configuration
The memory system has tunable parameters via environment variables:
In addition to environment variables, the internal
Config struct defines additional defaults:
How Memories Are Created
The background worker now extracts 6 types of information (previously only 2):- DAILY — What was done (files, commands, errors, tasks)
- LONGTERM — New facts to remember permanently
- PROFILE_UPDATE — Information about the user (name, role, expertise)
- TOPICS — Technical topics discussed
- PROJECTS — Projects worked on
- EPISODES — Dated, durable units of completed work (see Episodic memory)
Extraction Process
The Memory Worker follows this internal flow:- EnhancedExtractionPrompt: Sends the recent conversation history to the LLM with a structured prompt requesting information extraction
- Expected output: The LLM returns text with well-defined section headers:
## DAILY— Summary of what was done in the session## LONGTERM— New facts for long-term memory## PROFILE_UPDATE— User profile updates## TOPICS— Technical topics identified## PROJECTS— Projects mentioned or worked on
- ParseEnhancedResponse(): Parses the response and extracts each section individually
- Deduplication: Each fact receives a unique ID via SHA-256 content hash. Facts with a hash matching an existing one are automatically discarded
- Profile merging with lifecycle:
PROFILE_UPDATEchanges are merged with the existing profile. List fields upsert (restating an item supersedes the old one instead of duplicating), and extraction may emit rewrite operations:goals_done=/goals_remove=remove finished goals (moving them tomilestone=/certifications=),goals_replace=replaces the whole list — the same suffixes work for certifications, skills, interests and directives. Your instructions about the profile (“remove X from my goals”) are applied, never recorded as if they were facts
Automatic Compaction
The system runs periodic compaction to prevent uncontrolled growth:1
Check (every 6 hours)
Checks if the fact count exceeds 80% of the limit or if 24h have passed since the last compaction.
2
LLM Compaction (preferred)
Sends all facts to the AI with instructions to: merge duplicates, remove obsolete, consolidate related. Preserves original metadata.
3
Score-based Fallback
If the LLM call fails, archives facts with scores below 0.1 to
memory_archive.json.4
Daily Note Cleanup
Removes notes older than the retention period (default: 30 days). Empty directories are cleaned up.
5
MEMORY.md Regeneration
Rewrites MEMORY.md from the FactIndex — always up-to-date, never the source of truth.
Weekly and Monthly Digests (Trajectory)
Daily notes expire after ~30 days — rollups preserve the long-range narrative before that:- When an ISO week ends, that week’s daily notes consolidate into
weekly/2026-W27.md(LLM summary with a deterministic fallback — never depends on the provider being up). - When a month ends, its weekly digests consolidate into
monthly/2026-06.md. - Retention: weeklies keep ~26 weeks; monthlies are kept forever (minimal cost).
- A bounded Trajectory section enters the memory context with the latest monthly + recent weeklies — that is how “what you have been doing these past months” survives daily-note cleanup.
Automatic Migration
When starting for the first time with the new system, ChatCLI detects if a legacyMEMORY.md exists (without memory_index.json) and migrates automatically:
- Each line/bullet is converted to an individual fact
- Categories are detected from markdown headers
- Tags are extracted by technical keywords
- The original file is saved as
MEMORY.md.bak
/memory Command
Manual editing and extended profile
Automatic detection doesn’t always catch everything, so you can edit memory explicitly. Beyond name/role/expertise/company/location, the profile covers certifications, skills, goals, interests, directives, stances, milestones and environment (env_*). List fields have a lifecycle, they are not append-only: new items enter, a restated item (same text apart from status/parenthetical) supersedes the old one in place, and operation suffixes rewrite —_replace overwrites the whole list (empty value clears it), _done/_remove delete matching items. Commas inside parentheses are safe ("Quiz X (Provider, 60 questões)" stays whole).
forget only acts on facts; to remove profile items use the suffixes (goals_remove=...). Directives with [scope:<project>] are only injected when that workspace is active; without the tag they apply globally.@memory tool (agent, coder and chat)
Inside /agent and /coder, the model can persist and explore memory on its own via the @memory tool (cmds remember, profile, forget, recall, timeline, neighbors, map):
/memory manually. The profile subcommand accepts every lifecycle operation (goals_done, goals_replace, stance, milestone, env_*, sensitive_mark, …).
In chat too: memory/profile updates are the fourth sanctioned exception of tool-less chat (alongside ask_user, read-only knowledge and @graphview). When you reveal or correct a durable fact about yourself, the model persists it in the same turn — no more “I’ll keep that in mind” without writing anything. Gated by CHATCLI_CHAT_MEMORY (on by default), flippable with /config chat memory on|off.
Episodic memory: the work timeline
Facts capture what is true; episodes capture what happened. An episode is a dated, durable unit of completed work — summary, outcome and refs (files, PRs, commands) — extracted automatically by the background worker (## EPISODES section) and stored forever in ~/.chatcli/memory/episodes.json. Unlike daily notes (30-day retention) and their digests, episodes never expire, so “what did we do three months ago?” finally has a structured answer.
- Storage follows the fact-index disciplines: atomic writes, quarantine on corruption, and reconciling multi-process rewrites (REPL, gateway daemon and MCP server never erase each other). The same work item re-extracted on the same day dedups and enriches outcome/refs instead of duplicating. Capped at 2000 episodes (oldest dropped first).
@memory timelineis the model-facing chronological view:{project?, from?, to?, query?, limit?}.from/toaccept ISO dates (2026-04,2026-04-12) or natural expressions in English and Portuguese (“3 months ago”, “há 3 meses”, “abril”, “last week”) — a time expression insidequeryworks too.- Temporal recall: a
@memory recallquery carrying a time expression (“o que fizemos em abril?”) automatically prepends the matching timeline slice to the result — relevance ranking alone cannot answer “when”. When the window came from natural language and the leftover words match nothing, the window wins and returns unfiltered (the date was the real signal). /memory timeline [q]is the human view — same temporal parsing, rendered in the terminal.- Rollups are grounded in episodes: weekly/monthly digests now lead with the period’s episodes and use daily notes as color, so the long-range narrative stops decaying — and a week whose notes already expired still gets its digest from episodes alone.
Knowledge graph (@memory neighbors / map)
What ChatCLI knows about you is not a flat list: facts, topics, projects, skills and tags form an on-demand graph (Obsidian-style, in the core) derived from the relationships the stores already record (topic↔fact, fact→project, tags, skill triggers) plus [[wikilinks]] in note text. Access is through @memory itself:
recall→ search by content (“which facts match these words?”).neighbors <subject>→ local graph: backlinks + related notes for a subject (“what connects to this?”).map→ overview (counts by kind + hubs).
/graph (renders the graph to an image via embedded go-graphviz).
Fact quality (confidence, provenance, reconciliation)
Each fact carries confidence and provenance: what you state directly outranks a background-extraction guess, and a re-observed fact climbs — confidence scales the score (ranking and survival against decay/pruning). On write, ChatCLI reconciles: a rephrasing reinforces the existing fact instead of duplicating, and a same-subject update of equal-or-higher confidence supersedes the stale one (conservatively — a weak guess never wipes a strong fact). Topics gain a rolling summary of what was discussed, becoming real knowledge nodes. Legacy memory indexes are enriched once on startup, without losing anything.What goes in each component?
What goes in each component?
- FactIndex: Stable, long-lasting facts — decisions, patterns, gotchas, preferences
- UserProfile: Who you are — name, role, expertise, language
- TopicTracker: What you talk about — Go, Docker, K8s, etc.
- ProjectTracker: What you work on — chatcli, my-app, etc.
- PatternDetector: How you work — schedules, features, common errors
- Daily notes: What happened today — temporal and specific
Can I edit memories manually?
Can I edit memories manually?
Yes! All files are plain JSON or Markdown:JSON changes are loaded on next startup. MEMORY.md is regenerated and should not be edited directly.
How does fact scoring work?
How does fact scoring work?
Each fact has a score calculated by:
- accessCount: How many times the fact was used by the retriever
- daysSinceAccess: Days since last access
- halfLifeDays: Decay half-life (default: 30 days)
What Gets Injected into the Prompt
The ContextBuilder assembles the following block and injects it as a system prompt prefix:Configuration
- Environment Variables
- Via Helm Chart
CHATCLI_BOOTSTRAP_DIR only overrides the global directory (~/.chatcli/). Project-level files (detected via .git or .agent markers) still take priority over global ones.Context Injection Optimization (Prompt Caching)
ChatCLI optimizes token costs when contexts are attached using three complementary strategies:Unified System Prompt with Cache Hints
Contexts attached via/context attach are injected as system prompt, not as user messages. This enables provider-level prompt caching:
The system prompt block contains:
- Bootstrap (SOUL.md, USER.md, etc.)
- Memory (MEMORY.md + daily notes)
- Attached Contexts (new — previously injected as user messages)
- K8s Watcher (if active)
Smart Compaction
Injected context messages (/memory load, summarized contexts) are automatically truncated during compaction (Level 1 — trimming). This prevents old reference context from consuming valuable token budget.
Token Visibility
The/context attached command now shows:
- Estimated tokens per context
- Total tokens per turn
- Cache hints per provider
- Warnings for oversized contexts
/context attach, the feedback includes the estimated cost per turn.
Best Practices
Global SOUL.md, per-project USER.md
Keep your preferred personality globally and technical context per project.
Keep MEMORY.md concise
Keep only stable and confirmed facts — not session-specific ones.
Daily notes for journaling
Use them to record decisions, solved problems, and temporal context.
Don't duplicate CLAUDE.md
If you already use CLAUDE.md or project instructions, avoid duplicating them in bootstrap.
Next Steps
Conversation Control
Use /compact and /rewind to manage conversation size and state.
Sessions
Save and reuse conversations across projects.