Skip to content

Latest commit

 

History

History
353 lines (257 loc) · 11.6 KB

File metadata and controls

353 lines (257 loc) · 11.6 KB

karma

Install

pip install karma

Five Memory Stores

Store Scope Backed by Decay
TemplateMemory templates template.db (ghapi-indexed GitHub repos) permanent
CodeMemory repo + env packages codesigs + litesearch FTS5+vector permanent
PracticeMemory repo-scoped idioms litesearch FTS5+vector (SHA1 dedup) permanent
DecisionLog global litesearch FTS5+vector (append-only) permanent
ConversationLog session-scoped (git branch) litesearch FTS5+vector permanent

Quick Start

#| eval: false
from karma import index_repo, search_code

# Index your repo
result = index_repo('.')
print(result)
#| eval: false
from karma.memory import DecisionLog

dl = DecisionLog()
dl.log('Chose RRF k=60 for hybrid search — standard constant')
results = dl.search('RRF')
print(results)
#| eval: false
from karma import arun, build_dev_context

ctx = arun(build_dev_context('how to extend a class', repo_root='.'))
print(ctx)
#| eval: false
from karma import arun, PracticeMemory

pm = PracticeMemory(repo_root='.')
arun(pm.add('Use L.map not list comprehension', tags='fastcore', severity='must'))
results = arun(pm.query('how to map?'))
print(results)

Scaffolding — build from templates

Index a reference repo as a scaffold template, then get exemplar files and package status before building a new module.

#| eval: false
from karma.skill import index_template, scaffold_context

# Index a template repo once (cached by SHA — fast on repeat)
index_template('https://github.com/Karthik777/lego')

# Get full scaffold context before implementing
ctx = scaffold_context(
    'stripe integration with webhook handling',
    template='lego',
    packages=['faststripe'],
    wrap_for_llm=True
)
print(ctx)

Hermes Agent integration

karma provides first-class support for Hermes Agent as a memory provider. A single call copies the plugin into Hermes's plugin directory.

#| eval: false
from karma.skill import install_hermes_plugin

# One call — copies plugin to ~/.hermes/hermes-agent/plugins/memory/karma/
result = install_hermes_plugin()
print(result)
# {'installed': True, 'path': '~/.hermes/hermes-agent/plugins/memory/karma/__init__.py', 'message': '...'}

# Then in hermes config.yaml set: memory.provider: karma

Why karma?

Every repo develops a memory — the decisions you made, the packages you chose, the patterns that work and the anti-patterns you burned your hands on. Without karma that memory lives only in your head (or gets lost in a wall of commit history).

karma gives that memory a home:

  • CodeMemory — permanent FTS5+vector index of your repo and installed packages. Ask "does a hybrid search helper already exist?" before writing one.
  • DecisionLog — append-only log of architectural decisions, searchable by meaning. "Why did we choose RRF k=60?" → answered in milliseconds.
  • PracticeMemory — repo-scoped idioms and anti-patterns seeded from CLAUDE.md/AGENTS.md. Copilot (or any agent) sees them in every context window.
  • ConversationLog — session-scoped turn log, defaults to git branch. For LLM-only users without a harness. Supports cross-session search via search_all_conversations(). Harnesses like Lisette manage their own state — use this when you need it.
  • Upgrade hints — when your search hits code from an older installed version, karma surfaces the newer API automatically.

Lisette + Copilot Agent Demo

karma ships async tool functions that drop straight into Lisette's Chat(tools=[...]) interface — the same pattern used by GitHub Copilot agents.

The system prompt orders the agent: dev_context_toolsearch_code_tool → implement → log_decision_tool. That order ensures karma is consulted before any code is written.

#| eval: false
from lisette import Chat, bind
from karma.context import dev_context_tool, search_code_tool
from karma.memory import (add_practice_tool, log_decision_tool, query_practices_tool,
                          add_conversation_tool, search_conversation_tool, search_all_conversations_tool)
from karma.code import index_repo

index_repo('.')  # incremental — skips unchanged files on subsequent runs

xtra = {'editor-version': 'vscode/1.85.1', 'Copilot-Integration-Id': 'vscode-chat'}
sp = """You are a coding assistant with full memory of this repository.
ALWAYS call dev_context_tool first — surfaces existing code, past decisions, and practices.
Order: dev_context_tool → search_code_tool → implement → log_decision_tool."""

chat = Chat('github_copilot/claude-sonnet-4', sp,
            tools=[dev_context_tool, search_code_tool, add_practice_tool,
                   log_decision_tool, query_practices_tool,
                   add_conversation_tool, search_conversation_tool, search_all_conversations_tool])
c = bind(chat, max_steps=6, return_all=True, max_tokens=5000, extra_headers=xtra)

r = c('What should I know before adding a batch_search feature?')
print(chat.hist[-1].content)

What the agent does

  1. dev_context_tool — fetches code search results, repo practices, and past decisions for the query in parallel. Returns one formatted context block.
  2. search_code_tool — targeted FTS5+vector search for specific symbols.
  3. log_decision_tool — stores the decision made (e.g. "used RRF k=60 — standard constant") as a permanent entry in the DecisionLog.
  4. add_practice_tool — saves a new repo idiom (e.g. "always call pre(query) before FTS5").
  5. add_conversation_tool / search_conversation_tool / search_all_conversations_tool — session-scoped turn log. Useful for LLM-only users without a harness; harnesses like Lisette manage their own state.

Any LLM that speaks the OpenAI tool-call protocol works: Copilot, claude-3-5, GPT-4o, etc.

Storing and recalling decisions

from karma.memory import DecisionLog

dl = DecisionLog()

# Store an architectural decision (append-only, permanent)
dl.log('Chose RRF k=60 for hybrid search — standard constant, insensitive to corpus size')

# Later — in any session, any agent
results = dl.search('why RRF k=60')
print(results[0]['content'])  # → 'Chose RRF k=60 for hybrid search…'

# Recent decisions as a formatted context string
print(dl.as_context_str())

Seeding repo practices from CLAUDE.md

from karma import arun, PracticeMemory

pm = PracticeMemory(repo_root='.')
# Reads CLAUDE.md, AGENTS.md, CONVENTIONS.md, CONTRIBUTING.md, README.md
result = arun(pm.seed_from_dir('.'))
print(result)  # {'added': 12, 'skipped': 0}

# Query a practice
r = arun(pm.query('how should I handle list operations?'))
print(r[0]['content'])  # → 'Use L.map not list comprehension'  [severity: must]

Conversation Log

Session-scoped turn log for LLM-only users without a harness. If you use Lisette or another agent harness, skip this — the harness manages its own state.

from karma.memory import ConversationLog

log = ConversationLog()  # session_id defaults to current git branch
log.add('user', 'how do I batch search?')
log.add('assistant', 'Use search_many() — handles batch queries efficiently')

# Retrieve session turns
turns = log.get_session()  # oldest first

# Semantic search within this session
results = log.search('batch search')

# Cross-session search — finds relevant turns from any session
all_results = log.search_all('batch search')

# Formatted context string (respects token budget)
print(log.as_context_str(max_turns=10, max_chars=3000))

Via karma.skill (sync, pyskills-compatible):

from karma.skill import add_conversation, get_session, search_conversation, search_all_conversations

add_conversation('user', 'how do I batch search?')
add_conversation('assistant', 'Use search_many()...')
turns = get_session()  # JSON string
results = search_all_conversations('batch search')  # cross-session

Agent integrations

All LLM coding hosts — .agents/skills/

On import, karma copies its bundled SKILL.md into .agents/skills/karma/SKILL.md at the project root. This is automatically discovered by Claude Code, Cursor, GitHub Copilot, Continue.dev, and OpenCode.

pyskills-compatible hosts (Open Code, custom agents)

from pyskills import list_pyskills, doc
import karma.skill

# Discovery: host sees karma without importing the full package
print(list_pyskills())
# → [('karma', 'Karma — repo memory for coding agents.'), ...]

# Documentation: all function signatures + docstrings
print(doc(karma.skill))

# Direct sync calls — works from any context
print(karma.skill.dev_context('retry logic', repo_root='.'))
print(karma.skill.search_code('async wrapper', repo_root='.'))

Direct Python (scripts, notebooks, any event-loop context)

import karma.skill as ks
ks.index_repo('.')
print(ks.dev_context('implement a cache', repo_root='.'))

pyskills Integration

karma registers itself as a pyskills skill via an entry point in pyproject.toml, so any compatible harness discovers it without importing the full package.

Discovery

#| eval: false
from pyskills import list_pyskills, doc
import karma.skill

skills = list_pyskills()
print(skills)  # → {'karma': 'Karma — repo memory for coding agents.', ...}

# LLM-friendly docs — full signatures + docstrings, no heavy imports needed
print(doc(karma.skill))

Local / XDG skills

pyskills also supports local skills placed outside any pip package, in ~/.local/share/pyskills/ (or $XDG_DATA_HOME/pyskills). Use register_pyskill to create one alongside karma:

#| eval: false
from pyskills import register_pyskill, disable_pyskill, enable_pyskill

# Create a local extension skill
register_pyskill(
    name='myproject.karma_ext',
    docstr='Project-specific karma helpers',
    code='from karma.skill import dev_context\n# your extensions here\n',
)

# Toggle any skill without uninstalling
disable_pyskill('karma')   # hides karma from list_pyskills()
enable_pyskill('karma')    # restores it

Database locations

karma stores all data in $XDG_DATA_HOME/karma/ (default ~/.local/share/karma/):

File Contents Scope
karma.db Practices, decisions, conversations global (all projects)
codesearch.db Repo code sigs + chunks global (all repos)
env.db Installed package index + upgrade hints global (all projects)
template.db Scaffold template files from GitHub repos global (all projects)

Databases are global, not per-repo because installed packages and past decisions are shared across projects. Only PracticeMemory is repo-scoped (stored in karma.db under a per-repo agent_id).

Build / dev environment

pip install -e ".[dev]"     # installs karma + dev deps (includes pyskills)
nbdev_export                # regenerate karma/*.py from nbs/ — do this after any notebook edit
nbdev_preview               # rebuild docs / README

The [project.entry-points.pyskills] entry in pyproject.toml makes list_pyskills() return karma automatically after pip install -e ..

#| eval: false
from karma.core import _xdg_karma_dir

db_dir = _xdg_karma_dir()
print(f'karma data dir: {db_dir}')
# karma.db      — practices, decisions, conversations
# codesearch.db — repo code sigs + chunks (all repos)
# env.db        — installed package index + upgrade hints
# template.db   — scaffold template files from GitHub repos