Agents: SQLite store + type-defaults config refactor #48
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks#48
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
User story
As a maintainer, I want
config/agents.jsonto describe agent types (Forgejo user, token file, git identity, GPG key, default model, default container image) and a SQLite-backedagentstable to describe instances (name, type, per-instance overrides), so that adding a seconddevorrevieweris a row insert rather than a JSON edit + restart.Context
Today
config/agents.jsonhas one top-level object per hardcoded name (boss,dev,reviewer), each with its complete config. The system has no concept of "agent type" vs. "agent instance" — there's exactly one of each.This story introduces that split and adds the SQLite layer for instance overrides. First boot after this lands must be behaviour-identical to today: the migration seeds one
<type>-defaultinstance per type, each with no overrides, and routing falls through to type defaults.Acceptance criteria
Config shape
config/agents.jsonrewritten as{ types: { boss: { forgejo_user, token_file, git_name, git_email, branch_prefix, default_model, default_container_image, default_system_prompt, gpg_key_file? }, dev: {...}, reviewer: {...} }, webhook_secret_file, forgejo_url, forgejo_mcp_command }. No top-levelagentsobject.SQLite layer
src/db.ts— thin wrapper aroundbun:sqlite, opens a file at~/.local/state/claude-hooks/agents.db(orstateRoot()/agents.dbto matchsessions.json). Creates theagentstable on first open.listAgents(),getAgent(name),listAgentsByType(type),createAgent({name, type, ...}),updateAgent(name, patch),deleteAgent(name).Merged instance resolver
resolveAgent(name) -> Agent | nullthat merges the type defaults with the SQLite row. Precedence: SQLite row field wins when non-null; type defaults fill the rest. Returns a fully-populatedAgentobject (shape compatible with today'sAgentConfig).match_labelsis parsed JSON →string[], defaults to[]when null.Seed migration
agentstable is empty), insert one row per type:{ name: "<type>-default", type: "<type>", model: null, prompt_appendix: null, match_labels: null }. No overrides; the type default wins.config/agents.jsonis still in the old shape on upgrade, log a clear migration error pointing at a one-shotjust migrate-agents-v2recipe (or embed the rewrite in startup — pick one, document it).Session key
sessionKeybecomes<type>:<repo>:<issueOrPr>(was<name>:<repo>:<issueOrPr>). Pool members of the same type can resume each other's sessions.Tests
db.test.ts— CRUD round-trip, type lookup, JSONmatch_labelsparse/serialize, default seed.resolveAgent— type-default-only, override partial, override complete.Out of scope
References
config/agents.json.src/sessions.ts:sessionKey.Dependencies
main.