feat(memory): add memory CLI command group and project memory ADRs
- Add docs/adr/ADR-001-workplan-convention.md (formalises existing convention) - Add docs/adr/ADR-002-project-memory-convention.md (file location, structure, session protocols, opt-out, CLI interface) - Implement `kaizen-agentic memory` command group: show, init, brief, clear - Memory stored at .kaizen/agents/<name>/memory.md in project root - `init` scaffolds the standard memory template with YAML frontmatter - `brief` lists all agent memories + note that coach synthesis is pending T13 - `clear` deletes with confirmation prompt WP-0002 T07 and T08 done. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
57
docs/adr/ADR-001-workplan-convention.md
Normal file
57
docs/adr/ADR-001-workplan-convention.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: ADR-001
|
||||
title: Workplan Convention
|
||||
status: accepted
|
||||
date: "2026-03-18"
|
||||
---
|
||||
|
||||
# ADR-001 — Workplan Convention
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
kaizen-agentic needs a way to track planned work that is version-controlled,
|
||||
visible to the state-hub, and authoritative when the two diverge.
|
||||
|
||||
## Decision
|
||||
|
||||
Work items originate as Markdown files in `workplans/` **before** being
|
||||
registered in the state-hub DB. The file is always authoritative; the DB is
|
||||
a read/query model derived from it.
|
||||
|
||||
**File naming:** `workplans/kaizen-agentic-WP-NNNN-<slug>.md`
|
||||
**ID prefix:** `KAIZEN-WP`
|
||||
|
||||
### Required YAML frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: KAIZEN-WP-NNNN
|
||||
type: workplan
|
||||
title: "..."
|
||||
domain: custodian
|
||||
repo: kaizen-agentic
|
||||
status: active | completed | archived
|
||||
owner: kaizen-agentic
|
||||
topic_slug: custodian
|
||||
state_hub_workstream_id: <uuid>
|
||||
created: "YYYY-MM-DD"
|
||||
updated: "YYYY-MM-DD"
|
||||
---
|
||||
```
|
||||
|
||||
### Task tracking
|
||||
|
||||
Tasks use `- [ ]` / `- [x]` checkboxes with a `T##` code prefix. A
|
||||
`## State Hub Task IDs` table at the end of each workplan maps codes to
|
||||
DB UUIDs so status can be synced without a list_tasks lookup.
|
||||
|
||||
## Consequences
|
||||
|
||||
- File is the source of truth; DB drift is auto-fixable via
|
||||
`check_repo_consistency(fix=True)`.
|
||||
- Tasks must be created in the file first, then registered in the hub.
|
||||
- C-12 warnings are expected when the DB host has not yet seen local changes.
|
||||
119
docs/adr/ADR-002-project-memory-convention.md
Normal file
119
docs/adr/ADR-002-project-memory-convention.md
Normal file
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: ADR-002
|
||||
title: Project Memory Convention
|
||||
status: accepted
|
||||
date: "2026-03-18"
|
||||
---
|
||||
|
||||
# ADR-002 — Project Memory Convention
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
kaizen-agentic agents are stateless by default — each session starts from
|
||||
scratch with no knowledge of what has been tried, what worked, or what the
|
||||
project's recurring patterns are. This makes agents less useful over time
|
||||
and forces the operator to re-supply context that the agent itself
|
||||
accumulated.
|
||||
|
||||
## Decision
|
||||
|
||||
Each agent deployed into a project may maintain a **project-scoped memory
|
||||
file**. Memory files are written at session close and read at session start.
|
||||
|
||||
### File location
|
||||
|
||||
```
|
||||
<project-root>/.kaizen/agents/<agent-name>/memory.md
|
||||
```
|
||||
|
||||
The `.kaizen/` directory is the kaizen-agentic ecosystem's project-level
|
||||
state directory, analogous to `.claude/` for Claude Code.
|
||||
|
||||
### Memory file structure
|
||||
|
||||
```markdown
|
||||
---
|
||||
agent: <agent-name>
|
||||
project: <project-root or slug>
|
||||
last_updated: <ISO date>
|
||||
session_count: <n>
|
||||
---
|
||||
|
||||
## Project Context
|
||||
<!-- What this agent knows about the project it works in -->
|
||||
|
||||
## Accumulated Findings
|
||||
<!-- Patterns, recurring issues, key decisions encountered -->
|
||||
|
||||
## What Worked
|
||||
<!-- Approaches that produced good results in this project -->
|
||||
|
||||
## Watch Points
|
||||
<!-- Recurring risks, traps, or areas requiring extra care -->
|
||||
|
||||
## Open Threads
|
||||
<!-- Things noticed but not yet acted on -->
|
||||
|
||||
## Session Log
|
||||
<!-- One-line entry per session: date · summary · outcome -->
|
||||
```
|
||||
|
||||
### Session-start protocol (all memory-enabled agents)
|
||||
|
||||
1. Check for `.kaizen/agents/<name>/memory.md` in the project root.
|
||||
2. If present, read it before beginning work.
|
||||
3. Acknowledge the memory in the opening brief.
|
||||
|
||||
### Session-close protocol (all memory-enabled agents)
|
||||
|
||||
1. Update `## Accumulated Findings`, `## What Worked`, `## Watch Points`
|
||||
as needed.
|
||||
2. Append one line to `## Session Log`.
|
||||
3. Bump `last_updated` and `session_count`.
|
||||
|
||||
### Agent opt-out
|
||||
|
||||
An agent may declare `memory: disabled` in its YAML frontmatter to opt out.
|
||||
Default is enabled. Stateless utility agents (e.g. `keepaTodofile`) should
|
||||
opt out.
|
||||
|
||||
### CLI interface
|
||||
|
||||
```
|
||||
kaizen-agentic memory show <agent> # Print agent memory for current project
|
||||
kaizen-agentic memory init <agent> # Scaffold empty memory file
|
||||
kaizen-agentic memory brief <agent> # Run coach, print orientation for agent
|
||||
kaizen-agentic memory clear <agent> # Wipe memory (with confirmation prompt)
|
||||
```
|
||||
|
||||
`memory init` creates the `.kaizen/agents/<name>/memory.md` file with the
|
||||
standard structure and populates the frontmatter.
|
||||
|
||||
### Coaching meta-agent
|
||||
|
||||
A dedicated `agent-coach.md` (category: `meta`) reads across all
|
||||
`.kaizen/agents/*/memory.md` files in a project and:
|
||||
|
||||
- Synthesises a cross-agent brief (shared patterns, cross-domain risks)
|
||||
- Produces a new-agent orientation targeted at a specific agent about to
|
||||
be deployed for the first time
|
||||
- Maintains its own memory covering meta-level fleet observations
|
||||
|
||||
`kaizen-agentic memory brief <agent>` invokes the coach to produce this
|
||||
orientation.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Agents accumulate project-specific knowledge and arrive in later sessions
|
||||
informed rather than blank.
|
||||
- The `.kaizen/` directory should be added to `.gitignore` by default;
|
||||
teams may choose to commit it for shared context.
|
||||
- Memory files are human-readable and can be manually edited or reviewed.
|
||||
- The coach agent provides a single synthesised view across all agent
|
||||
memories — reducing the operator's burden of re-supplying context.
|
||||
- Agents with `memory: disabled` remain fully stateless and require no
|
||||
`.kaizen/` setup.
|
||||
@@ -756,6 +756,140 @@ def remove(name: str, target: str):
|
||||
click.echo(f"❌ Extension not found: {name}")
|
||||
|
||||
|
||||
@cli.group()
|
||||
def memory():
|
||||
"""Manage project-scoped agent memory (.kaizen/agents/<name>/memory.md)."""
|
||||
pass
|
||||
|
||||
|
||||
@memory.command("show")
|
||||
@click.argument("agent_name")
|
||||
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||
def memory_show(agent_name: str, target: str):
|
||||
"""Print agent memory for the current project."""
|
||||
memory_path = _memory_path(target, agent_name)
|
||||
|
||||
if not memory_path.exists():
|
||||
click.echo(f"No memory found for agent '{agent_name}'.")
|
||||
click.echo(f" Expected: {memory_path}")
|
||||
click.echo(f" Run: kaizen-agentic memory init {agent_name}")
|
||||
return
|
||||
|
||||
click.echo(memory_path.read_text())
|
||||
|
||||
|
||||
@memory.command("init")
|
||||
@click.argument("agent_name")
|
||||
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||
def memory_init(agent_name: str, target: str):
|
||||
"""Scaffold an empty memory file for an agent."""
|
||||
memory_path = _memory_path(target, agent_name)
|
||||
|
||||
if memory_path.exists():
|
||||
click.echo(f"Memory file already exists: {memory_path}")
|
||||
return
|
||||
|
||||
memory_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
project_name = Path(target).resolve().name
|
||||
|
||||
content = f"""---
|
||||
agent: {agent_name}
|
||||
project: {project_name}
|
||||
last_updated: {_today()}
|
||||
session_count: 0
|
||||
---
|
||||
|
||||
## Project Context
|
||||
<!-- What this agent knows about the project it works in -->
|
||||
|
||||
## Accumulated Findings
|
||||
<!-- Patterns, recurring issues, key decisions encountered -->
|
||||
|
||||
## What Worked
|
||||
<!-- Approaches that produced good results in this project -->
|
||||
|
||||
## Watch Points
|
||||
<!-- Recurring risks, traps, or areas requiring extra care -->
|
||||
|
||||
## Open Threads
|
||||
<!-- Things noticed but not yet acted on -->
|
||||
|
||||
## Session Log
|
||||
<!-- One-line entry per session: date · summary · outcome -->
|
||||
"""
|
||||
memory_path.write_text(content)
|
||||
click.echo(f"Initialized memory for '{agent_name}': {memory_path}")
|
||||
|
||||
|
||||
@memory.command("brief")
|
||||
@click.argument("agent_name")
|
||||
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||
def memory_brief(agent_name: str, target: str):
|
||||
"""Print a coach-synthesised orientation for an agent.
|
||||
|
||||
Reads this agent's memory and all other agent memories in the project.
|
||||
Coach agent integration is pending (T13 — see KAIZEN-WP-0002).
|
||||
Currently prints the agent's own memory as a starting orientation.
|
||||
"""
|
||||
memory_path = _memory_path(target, agent_name)
|
||||
kaizen_dir = Path(target).resolve() / ".kaizen" / "agents"
|
||||
|
||||
click.echo(f"=== Agent Brief: {agent_name} ===\n")
|
||||
|
||||
# Show own memory
|
||||
if memory_path.exists():
|
||||
click.echo(f"--- Memory: {agent_name} ---")
|
||||
click.echo(memory_path.read_text())
|
||||
else:
|
||||
click.echo(f"No memory file found for '{agent_name}'. Run: memory init {agent_name}")
|
||||
|
||||
# List other agents with memory in this project
|
||||
other_agents = []
|
||||
if kaizen_dir.exists():
|
||||
for agent_dir in kaizen_dir.iterdir():
|
||||
if agent_dir.is_dir() and agent_dir.name != agent_name:
|
||||
mf = agent_dir / "memory.md"
|
||||
if mf.exists():
|
||||
other_agents.append(agent_dir.name)
|
||||
|
||||
if other_agents:
|
||||
click.echo(f"\n--- Other agents with memory in this project: {', '.join(other_agents)} ---")
|
||||
click.echo("(Coach synthesis not yet available — use 'memory show <agent>' to read each)")
|
||||
else:
|
||||
click.echo("\nNo other agent memories found in this project.")
|
||||
|
||||
click.echo("\nNote: Full coach synthesis will be available once agent-coach is implemented (KAIZEN-WP-0002 T12-T13).")
|
||||
|
||||
|
||||
@memory.command("clear")
|
||||
@click.argument("agent_name")
|
||||
@click.option("--target", "-t", default=".", help="Project root (default: current)")
|
||||
@click.confirmation_option(prompt="This will permanently delete the agent memory. Continue?")
|
||||
def memory_clear(agent_name: str, target: str):
|
||||
"""Wipe agent memory for the current project."""
|
||||
memory_path = _memory_path(target, agent_name)
|
||||
|
||||
if not memory_path.exists():
|
||||
click.echo(f"No memory found for agent '{agent_name}' — nothing to clear.")
|
||||
return
|
||||
|
||||
memory_path.unlink()
|
||||
click.echo(f"Cleared memory for '{agent_name}': {memory_path}")
|
||||
|
||||
# Remove empty parent directory
|
||||
if not any(memory_path.parent.iterdir()):
|
||||
memory_path.parent.rmdir()
|
||||
|
||||
|
||||
def _memory_path(target: str, agent_name: str) -> Path:
|
||||
return Path(target).resolve() / ".kaizen" / "agents" / agent_name / "memory.md"
|
||||
|
||||
|
||||
def _today() -> str:
|
||||
from datetime import date
|
||||
return date.today().isoformat()
|
||||
|
||||
|
||||
def _get_registry() -> AgentRegistry:
|
||||
"""Get the agent registry."""
|
||||
# Try to find agents directory
|
||||
|
||||
@@ -138,8 +138,8 @@ kaizen-agentic memory clear <agent> # Wipe memory (with confirmation)
|
||||
### Tasks
|
||||
|
||||
**Memory convention and tooling**
|
||||
- [ ] T07 — Write ADR: project memory convention (file location, structure, lifecycle)
|
||||
- [ ] T08 — Implement `memory` CLI command group (show, init, brief, clear)
|
||||
- [x] T07 — Write ADR: project memory convention (file location, structure, lifecycle)
|
||||
- [x] T08 — Implement `memory` CLI command group (show, init, brief, clear)
|
||||
- [ ] T09 — Add session-start and session-close protocol sections to agent template /
|
||||
contributor guide
|
||||
|
||||
|
||||
Reference in New Issue
Block a user