Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .pi/skills/memory/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
# Memory Skill

Project-local memory workflow for Set Kyar's workspace.
Project-local memory workflow for pi-web users.

## Files
- `SKILL.md` — skill instructions
- `scripts/memory.py` — CLI implementation
- `../../../data/memory.sqlite` — database
- `../../../data/schema.sql` — schema
- `$PI_CODING_AGENT_DIR/pi-web-memory.sqlite` (default `~/.pi/agent/`) — database, auto-initialized on first use
- `data/schema.sql` — schema (shipped with the skill)

## Use from repo root
```bash
python3 .pi/skills/memory/scripts/memory.py search "rent"
python3 .pi/skills/memory/scripts/memory.py add-memory "Prefers concise answers" --category preference --importance 4
python3 .pi/skills/memory/scripts/memory.py add-reminder "Pay rental" --due-at "2026-05-21 09:00" --timezone Asia/Singapore
# Optional: initialize explicitly (normal commands also auto-initialize)
python3 .pi/skills/memory/scripts/memory.py init

# Remember a project fact (always scoped to cwd/project)
python3 .pi/skills/memory/scripts/memory.py add-memory "Prefers tabs over spaces" \
--category preference --importance 4 --cwd /path/to/project --project my-project

# Search memories for the current project
python3 .pi/skills/memory/scripts/memory.py search "tabs" --project my-project
```
64 changes: 52 additions & 12 deletions .pi/skills/memory/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,75 @@
---
name: memory
description: Manages project-local long-term memory in SQLite for Set Kyar's workspace. Use when storing, searching, correcting, or listing memories, reminders, accounts, automations, or schema changes.
description: Manages project-local long-term memory in SQLite for pi-web users. Use when storing, searching, correcting, or listing memories.
---

# Memory

Use this skill for project memory tasks.
Use this skill for project memory tasks. Every memory is scoped to a specific project (working directory) so facts stay relevant to the right context.

## Core files
- `data/memory.sqlite` — primary memory database
- `data/schema.sql` — schema definition
- `$PI_CODING_AGENT_DIR/pi-web-memory.sqlite` (default `~/.pi/agent/`) — primary memory database, auto-initialized on first use
- `.pi/skills/memory/data/schema.sql` — schema definition (shipped with the skill)
- `.pi/skills/memory/scripts/memory.py` — CLI implementation

## Project context

Memories are associated with a project via `--cwd` and `--project`. This allows the same database to serve multiple projects without cross-polluting recall. When a pi session is active, also capture session info so you can trace which conversation produced a memory.

| Context field | Source | Example |
|---|---|---|
| `--cwd` | `pwd` / pi session header `cwd` | `/Users/setkyar/pi-web` |
| `--project` | basename of cwd | `pi-web` |
| `--session-id` | pi session header `id` (8-char UUID) | `a1b2c3d4` |
| `--session-name` | `/name` or `session_info` entry | `Refactor auth module` |

Pi sessions are stored as JSONL trees in `getSessionsDir()` (default `~/.pi/agent/sessions/<encoded-cwd>/`). Each session file has a header with `id`, `cwd`, `timestamp`, and optional `parentSession`. Entries form a tree via `id`/`parentId`; use `/tree` to navigate branches, `/fork` to split into a new session, and `/name` to set a display name.

When the user says "remember that" or "save this for later", record:
- The fact itself (`add-memory`)
- `--cwd` of the current project
- `--project` (derived from cwd basename)
- Current pi session ID and name if a pi session is active

## Common commands
```bash
python3 .pi/skills/memory/scripts/memory.py search "rent"
python3 .pi/skills/memory/scripts/memory.py add-memory "Prefers concise answers" --category preference --importance 4
python3 .pi/skills/memory/scripts/memory.py add-reminder "Pay rental" --due-at "2026-05-21 09:00" --timezone Asia/Singapore
python3 .pi/skills/memory/scripts/memory.py add-account "Syfe" --account-name "Brokerage" --currency SGD --balance 100000
python3 .pi/skills/memory/scripts/memory.py schema-changes
# Optional: initialize explicitly (normal commands also auto-initialize)
python3 .pi/skills/memory/scripts/memory.py init

# Remember a project fact
python3 .pi/skills/memory/scripts/memory.py add-memory "Prefers tabs over spaces" \
--category preference --importance 4 --cwd /Users/setkyar/pi-web --project pi-web

# Remember with session context
python3 .pi/skills/memory/scripts/memory.py add-memory "Decided to use Go 1.25 for the backend" \
--category decision --importance 5 --cwd /Users/setkyar/pi-web --project pi-web \
--session-id a1b2c3d4 --session-name "pi-web backend refactor"

# Search across all projects or filter by project
python3 .pi/skills/memory/scripts/memory.py search "tabs" --project pi-web
```

## Rules
- Store explicit remember requests without asking again, unless sensitive.
- **Always capture `--cwd` and `--project`** when adding a memory. Derive `--project` from the basename of cwd.
- When a pi session is active, also capture `--session-id` and `--session-name` so memories are traceable to the conversation that produced them.
- Ask before storing sensitive domains like finance, health, legal, identity, addresses, or private documents.
- Never store passwords, API keys, seed phrases, OTPs, or full card numbers.
- Prefer additive schema changes only.
- Use `schema-change` for schema updates.

## Outdated memories

Memories are point-in-time snapshots. A memory recorded three months ago may no longer reflect reality — the codebase changed, the decision was reversed, or the preference shifted.

When recalling memories:
- **Check the timestamp** — `created_at` tells you when the memory was recorded. Older memories carry less weight.
- **Verify against code** — use `read`, `bash`, `rg` to confirm a memory still reflects the current codebase before acting on it.
- **Treat old memories as hints, not facts** — if a memory from March says "Auth uses JWT", but the current code has session cookies, the code wins.
- **Correct stale memories** — when you detect a memory is outdated, use `add-memory` with updated info (the new memory naturally supersedes the old one in search recency). Optionally archive the old entry if needed.
- **Surfacing staleness** — when presenting a recalled memory to the user, note its age: "I recall from [March 2026] that we decided X — let me verify that's still the case."

## When to use
- Remember project-specific facts, decisions, conventions, or patterns
- Save user preferences or personal facts
- Add reminders or recurring automations
- Update corrected memories
- Check existing stored context before answering
- Check existing stored context before answering a question about the current project
85 changes: 85 additions & 0 deletions .pi/skills/memory/data/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
PRAGMA foreign_keys = ON;

CREATE TABLE IF NOT EXISTS memories (
id INTEGER PRIMARY KEY,
content TEXT NOT NULL,
category TEXT NOT NULL DEFAULT 'general',
context TEXT,
importance INTEGER NOT NULL DEFAULT 3 CHECK (importance BETWEEN 1 AND 5),
sensitivity TEXT NOT NULL DEFAULT 'normal' CHECK (sensitivity IN ('low','normal','high','secret')),
source TEXT,
confidence REAL NOT NULL DEFAULT 1.0 CHECK (confidence >= 0 AND confidence <= 1),
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
archived INTEGER NOT NULL DEFAULT 0 CHECK (archived IN (0,1))
);

CREATE TABLE IF NOT EXISTS memory_events (
id INTEGER PRIMARY KEY,
event_type TEXT NOT NULL,
table_name TEXT,
row_id INTEGER,
summary TEXT,
raw_input TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS schema_changes (
id INTEGER PRIMARY KEY,
change_type TEXT NOT NULL CHECK (change_type IN ('create_table','create_index','alter_table_add_column','other_additive')),
object_name TEXT NOT NULL,
reason TEXT NOT NULL,
sql TEXT NOT NULL,
applied_by TEXT NOT NULL DEFAULT 'agent',
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(category);
CREATE INDEX IF NOT EXISTS idx_memories_context ON memories(context);
CREATE INDEX IF NOT EXISTS idx_memories_sensitivity ON memories(sensitivity);
CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);
CREATE INDEX IF NOT EXISTS idx_memories_archived_updated ON memories(archived, updated_at);
CREATE INDEX IF NOT EXISTS idx_memory_events_table_row ON memory_events(table_name, row_id);
CREATE INDEX IF NOT EXISTS idx_memory_events_created_at ON memory_events(created_at);
CREATE INDEX IF NOT EXISTS idx_schema_changes_object ON schema_changes(object_name);
CREATE INDEX IF NOT EXISTS idx_schema_changes_created_at ON schema_changes(created_at);

CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
content,
category,
context,
source,
content='memories',
content_rowid='id',
tokenize='unicode61 remove_diacritics 2'
);

CREATE TRIGGER IF NOT EXISTS memories_fts_ai
AFTER INSERT ON memories BEGIN
INSERT INTO memories_fts(rowid, content, category, context, source)
VALUES (new.id, new.content, new.category, new.context, new.source);
END;

CREATE TRIGGER IF NOT EXISTS memories_fts_ad
AFTER DELETE ON memories BEGIN
INSERT INTO memories_fts(memories_fts, rowid, content, category, context, source)
VALUES('delete', old.id, old.content, old.category, old.context, old.source);
END;

CREATE TRIGGER IF NOT EXISTS memories_fts_au
AFTER UPDATE ON memories BEGIN
INSERT INTO memories_fts(memories_fts, rowid, content, category, context, source)
VALUES('delete', old.id, old.content, old.category, old.context, old.source);
INSERT INTO memories_fts(rowid, content, category, context, source)
VALUES (new.id, new.content, new.category, new.context, new.source);
END;

CREATE TRIGGER IF NOT EXISTS memories_updated_at
AFTER UPDATE ON memories
FOR EACH ROW
WHEN NEW.updated_at = OLD.updated_at
BEGIN
UPDATE memories SET updated_at = CURRENT_TIMESTAMP WHERE id = OLD.id;
END;


Loading
Loading