Coding assistants that learn your repository’s conventions
Tell the assistant once that tests run with vitest or that dates use date-fns. It recalls the conventions that matter for each task, inside the token budget you set. With the defaults, the memory runs in your process and sends nothing anywhere.
npm install actrone-memory · pip install actrone-memory
The same rules, pasted into every prompt
Rules are repeated on every request
Developers paste the same instructions again, or keep a rules file that the assistant reads in full every time, relevant or not.
Long sessions crowd out what matters
After an hour of back and forth, the convention that matters for this change is buried under turns that do not.
One repository’s rules leak into another
A single shared memory suggests the Python service’s conventions while you work in the TypeScript app.
Four steps, one scope per repository
repo:web, so conventions are recalled for the codebase they belong to and nowhere else.Record a convention once
injectMemory, in Python inject_memory
When a developer or a code review states a rule, store it under the repository’s scope.
Recall for each task
retrieveContext, in Python retrieve_context
Ranks the repository’s conventions against the task description and returns the most relevant ones.
Stay inside the budget
tokenBudget, in Python token_budget
By default a quarter of the budget goes to long-term memory and about a third to recent turns. Both are pruned to fit, so the context never exceeds what you allowed.
Keep the session
storeTurn, in Python store_turn
Each request and answer is recorded, so a follow-up request in the same session sees the previous exchange.
The whole integration, in both languages
import { MemoryManager } from "actrone-memory";
// One scope per repository, so one codebase's conventions never leak into another's.
const scopeFor = (repo: string) => `repo:${repo}`;
// Record a convention once, when the developer or a code review states it.
export async function learnConvention(memory: MemoryManager, repo: string, convention: string) {
return memory.injectMemory(scopeFor(repo), convention, 0.9, "conventions", ["convention"], "user");
}
// Before each request, recall this task's conventions within a token budget.
export async function conventionsFor(
memory: MemoryManager,
repo: string,
sessionId: string,
task: string,
) {
const context = await memory.retrieveContext(scopeFor(repo), sessionId, task, 800);
return {
conventions: context.episodicMemories.map((m) => m.content),
tokensUsed: context.totalTokensUsed,
};
}
// Keep the exchange, so the next request in this session sees it.
export async function recordExchange(
memory: MemoryManager,
repo: string,
sessionId: string,
request: string,
answer: string,
) {
await memory.storeTurn(scopeFor(repo), sessionId, request, answer);
}from dataclasses import dataclass
from actrone_memory import MemoryManager
def scope_for(repo: str) -> str:
"""One scope per repository, so one codebase's conventions never leak into another's."""
return f"repo:{repo}"
async def learn_convention(memory: MemoryManager, repo: str, convention: str) -> str:
"""Record a convention once, when the developer or a code review states it."""
return await memory.inject_memory(
agent_id=scope_for(repo),
content=convention,
importance=0.9,
session_id="conventions",
topic_tags=["convention"],
source="user",
)
@dataclass
class TaskContext:
conventions: list[str]
tokens_used: int
async def conventions_for(
memory: MemoryManager, repo: str, session_id: str, task: str
) -> TaskContext:
"""Before each request, recall this task's conventions within a token budget."""
context = await memory.retrieve_context(
agent_id=scope_for(repo), session_id=session_id, query=task, token_budget=800
)
return TaskContext(
conventions=[m.content for m in context.episodic_memories],
tokens_used=context.total_tokens_used,
)
async def record_exchange(
memory: MemoryManager, repo: str, session_id: str, request: str, answer: str
) -> None:
"""Keep the exchange, so the next request in this session sees it."""
await memory.store_turn(
agent_id=scope_for(repo),
session_id=session_id,
user_message=request,
assistant_message=answer,
)Create the manager once and pass it in: await MemoryManager.create() in TypeScript, or async with create_memory_manager() as memory: in Python.
What this does not do for a coding assistant
It does not read your code
It remembers what you tell it. It does not index the repository, parse files or build a code graph.
It recalls conventions. It does not enforce them
Memory puts the right rules in front of the model. Following them is still the model’s job, and your review’s.
Recall matches shared words until you add a model
With no extra packages the embedder compares words, so “Add a route handler” finds “Route handlers live in src/routes” through the words they share. Install
fastembedin TypeScript or theonnxextra in Python for recall by meaning, still on your machine.In-process until you add a store
The default store lives in your process, so a restart forgets everything. Pass the Redis, Postgres or Qdrant adapters when memory has to outlive a deploy. The calls in the code do not change.
Token counts are estimates
Both libraries count about four characters per token by default. Python counts exactly with the
tiktokenextra, and TypeScript accepts your own counter.