Context injection is Aiana's core intelligence feature. It automatically injects relevant memories and preferences at the start of each Claude Code session.
┌─────────────────────────────────────────────────────────────────┐
│ SessionStart Hook │
│ │
│ 1. User starts Claude Code session │
│ │ │
│ ▼ │
│ 2. Aiana hook triggers ──► ContextInjector │
│ │ │
│ ▼ │
│ 3. Load user profile (Redis) │
│ - Static preferences │
│ - Dynamic/recent context │
│ │ │
│ ▼ │
│ 4. Fetch project context │
│ - Recent sessions (SQLite) │
│ - Relevant memories (Qdrant) │
│ │ │
│ ▼ │
│ 5. Format and inject <aiana-context> │
│ │
└─────────────────────────────────────────────────────────────────┘
<aiana-context>
The following context is recalled from your previous sessions.
Use this to inform your responses but don't explicitly reference it.
## User Preferences (Persistent)
- Prefers TypeScript over JavaScript
- Uses ESLint flat config (v9+)
- Commits with conventional format
- Uses uv for Python dependencies
## Project: git-steer
### Recent Activity
- Fixed 9 security vulnerabilities
- Updated to vitest 4.x
- Created blog post series
## Recent Context
- Working on Aiana documentation
- Last session: security sweep across repos
</aiana-context>Long-term, persistent preferences that rarely change:
# Add static preference
injector.add_preference("Uses TypeScript over JavaScript", static=True)
injector.add_preference("Prefers conventional commits", static=True)These are stored in Redis with a 7-day TTL and typically represent:
- Language preferences
- Framework choices
- Code style conventions
- Workflow patterns
Recent, temporary context that changes frequently:
# Add dynamic context
injector.add_preference("Working on Aiana docs", static=False)
injector.add_preference("Debugging Redis connection", static=False)Dynamic context:
- Limited to 20 items (oldest removed when exceeded)
- Represents current work focus
- Automatically updated from sessions
# Recent sessions for this project
sessions = sqlite.list_sessions(project="git-steer", limit=5)
# Generates:
# ### Recent Sessions
# - 2026-02-01 10:00: 47 messages
# - 2026-01-31 14:30: 23 messages# Semantically relevant memories
memories = qdrant.get_recent(project="git-steer", limit=5)
# Generates:
# ### Recent Activity
# - Fixed 9 security vulnerabilities
# - Updated to vitest 4.xGenerated context is cached in Redis to avoid regeneration:
# Cache TTL: 4 hours
cache.cache_context(project, context, ttl=3600 * 4)
# Invalidation triggers:
# - New memory added
# - Preference changed
# - Session ended
cache.invalidate_context(project)from aiana.context import ContextInjector
from aiana.storage import AianaStorage
from aiana.storage.redis import RedisCache
from aiana.storage.qdrant import QdrantStorage
from aiana.embeddings import get_embedder
# Initialize backends
sqlite = AianaStorage()
redis = RedisCache()
embedder = get_embedder()
qdrant = QdrantStorage(embedder=embedder)
# Create injector
injector = ContextInjector(
redis_cache=redis,
qdrant_storage=qdrant,
sqlite_storage=sqlite,
)# Generate for a project
context = injector.generate_context(
cwd="/Users/ryan/Projects/git-steer",
session_id="abc123",
max_items=10,
)
print(context)
# <aiana-context>
# ...
# </aiana-context># In hooks.py or hook handler
def handle_session_start(session_id: str, cwd: str) -> dict:
injector = ContextInjector(...)
context = injector.generate_context(cwd, session_id)
return {
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": context,
}
}{
"hooks": {
"SessionStart": [{
"hooks": [{
"type": "command",
"command": "aiana hook session-start"
}]
}]
}
}When a session ends, save a summary for future context:
# In session-end hook
injector.save_session_summary(
session_id="abc123",
project="git-steer",
summary="Fixed 9 security vulnerabilities in dependencies"
)This:
- Adds to Qdrant for semantic retrieval
- Updates dynamic context
- Invalidates cached context
Good static preferences:
- "Uses TypeScript for new projects"
- "Prefers ESLint flat config"
- "Uses conventional commit messages"
Bad static preferences:
- "Working on feature X" (too temporary)
- "Had a bug in auth" (too specific)
Keep context concise and actionable:
<!-- Good -->
## User Preferences
- Uses uv for Python dependencies
- Prefers TypeScript
<!-- Bad (too verbose) -->
## User Preferences
- The user has indicated that they prefer to use the uv package manager
for managing Python dependencies instead of pip because they find it
faster and more reliable...The injector extracts project name from:
- Git root directory name
- Current directory name (fallback)
def get_project_name(self, cwd: str) -> str:
path = Path(cwd)
# Try to find git root
git_root = path
while git_root != git_root.parent:
if (git_root / ".git").exists():
return git_root.name
git_root = git_root.parent
return path.namecontext = redis.get_cached_context("git-steer")
if context:
print("Cached context found")
else:
print("No cached context")profile = redis.get_profile()
print(f"Static: {profile['static']}")
print(f"Dynamic: {profile['dynamic']}")# Invalidate cache
redis.invalidate_context("git-steer")
# Regenerate
context = injector.generate_context(cwd, max_items=10)When no memories exist:
<aiana-context>
No previous context found for project: new-project
Memories will be saved as you work.
</aiana-context>This informs Claude that:
- This is a new/unknown project
- Context will build over time
- No assumptions should be made