Agents · 2026-05-24 · By RedDB team · 5 min read

Building a "remember this" skill end-to-end

A 30-minute walkthrough — turn a Claude Code skill into a durable note-taker. The skill captures the current conversation slice, embeds it, writes it to RedDB, and surfaces it on the next SessionStart. Every file you need, in order.

What we’re building

Three earlier posts in this pillar set the table:

A slash command needs a human to type it. A SessionStart hook needs a session boundary. Neither catches the moment in the middle of a turn where a fact deserves to be remembered — a correction, a constraint, a piece of domain language the agent just learned. That is what a skill is for: the harness picks it up by description, the agent invokes it without ceremony, the side effect lands in the database.

By the end of this post you have a skill called remember-this:

  1. Reader installs three files (SKILL.md, capture.sh, an embedder).
  2. Mid-turn, agent decides “this is worth keeping” and invokes the skill with a short body.
  3. Skill embeds the body, writes it to the memory table from D1, returns a confirmation.
  4. Next session, the SessionStart hook surfaces the row in context if it scores against the active task.

Total moving parts: one markdown file, one shell script, one HTTP call. No new infrastructure beyond the RedDB instance from D1.

Prerequisites

  • A RedDB instance reachable from your workstation. The examples assume http://localhost:7878 and a bearer token in $REDDB_TOKEN.
  • The memory table from the D1 post. Repeated here so this post stands alone:
CREATE TABLE memory (
  id          TEXT PRIMARY KEY,
  kind        TEXT NOT NULL,
  scope       TEXT NOT NULL,
  body        TEXT NOT NULL,
  embedding   VECTOR(1024) NOT NULL,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  deleted_at  TIMESTAMPTZ
);

CREATE INDEX memory_scope_idx     ON memory (scope, created_at DESC) WHERE deleted_at IS NULL;
CREATE INDEX memory_embedding_idx ON memory USING hnsw (embedding vector_cosine_ops) WHERE deleted_at IS NULL;
  • An embedding endpoint. The examples call voyage-3 via the Voyage HTTP API; swap in any 1024-dim model and the code is the same. Set $VOYAGE_KEY.
  • Claude Code installed with skills enabled (any recent release).

File 1 — SKILL.md

Drop at ~/.claude/skills/remember-this/SKILL.md:

---
name: remember-this
description: Persist a fact, correction, or constraint into long-term memory mid-turn. Use when the user states a preference, corrects an approach, names a non-obvious constraint, or shares a piece of domain knowledge that should outlive the current session. Do not invoke for ephemeral task state, code changes, or anything already obvious from the repo. Invoke at most once per logical fact.
---

# remember-this

When this skill matches, capture the fact you just learned.

## Steps

1. Decide what to save. One memory equals one self-contained fact. Strip surrounding chat. Rephrase in third person if the user said "I" — future-you will not know who "I" was.
2. Pick a `kind`:
   - `user` — durable facts about the user (role, expertise, tools they own)
   - `feedback` — corrections to your approach, with the *why*
   - `project` — facts about the current project that are not derivable from the code
   - `reference` — pointers to external systems (Linear projects, Grafana boards, Slack channels)
3. Pick a `scope`:
   - `global` — true regardless of project
   - the current working directory — true here only
4. Run the capture:

   ```bash
   ~/.claude/skills/remember-this/capture.sh \
     --kind=<kind> \
     --scope=<scope> \
     --body="<the fact, one sentence>"
  1. Report back to the user in one line: Remembered (<kind>, <scope>): <body>.

Do not

  • Save the verbatim user message. Save the fact extracted from it.
  • Save anything that would be obvious from reading git log or CLAUDE.md.
  • Save more than one row per turn. If two facts appeared, invoke twice with two bodies.
  • Invoke when the user is mid-task and just venting. Memory is for things that change future behavior.

Two things the description does, that matter for trigger quality:

- It tells the harness *when not* to fire. Skills with vague descriptions fire on every turn and the user disables them within a day.
- It encodes the unit of work: one fact per invocation. The agent will otherwise dump entire chat windows into a single row and the embedding becomes useless.

## File 2 — `capture.sh`

Drop at `~/.claude/skills/remember-this/capture.sh`. `chmod +x` it.

```bash
#!/usr/bin/env bash
set -euo pipefail

KIND=user
SCOPE=$(pwd)
BODY=

for arg in "$@"; do
  case "$arg" in
    --kind=*)  KIND="${arg#--kind=}"  ;;
    --scope=*) SCOPE="${arg#--scope=}" ;;
    --body=*)  BODY="${arg#--body=}"  ;;
    *) echo "unknown arg: $arg" >&2; exit 2 ;;
  esac
done

if [[ -z "$BODY" ]]; then
  echo "--body is required" >&2
  exit 2
fi

case "$KIND" in
  user|feedback|project|reference) ;;
  *) echo "invalid --kind: $KIND" >&2; exit 2 ;;
esac

ID=$(python3 -c 'import secrets,time;print(f"{int(time.time()*1000):x}{secrets.token_hex(6)}")')

EMBEDDING=$(
  curl -sS https://api.voyageai.com/v1/embeddings \
    -H "Authorization: Bearer ${VOYAGE_KEY}" \
    -H 'Content-Type: application/json' \
    -d "$(jq -nc --arg input "$BODY" '{model:"voyage-3", input:[$input]}')" \
  | jq -c '.data[0].embedding'
)

curl -sS -X POST "${REDDB_URL:-http://localhost:7878}/sql" \
  -H "Authorization: Bearer ${REDDB_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d "$(jq -nc \
        --arg id "$ID" \
        --arg kind "$KIND" \
        --arg scope "$SCOPE" \
        --arg body "$BODY" \
        --argjson emb "$EMBEDDING" \
        '{sql:"INSERT INTO memory (id,kind,scope,body,embedding) VALUES ($1,$2,$3,$4,$5)", params:[$id,$kind,$scope,$body,$emb]}')" \
  >/dev/null

echo "ok $ID"

The script is intentionally small. The contract with the model is “exit zero on success, non-zero with a message on failure”, so any breakage shows up in the agent’s turn as an error it can recover from (re-invoke, or apologise and continue). Three things to notice:

  • set -euo pipefail makes silent failure impossible. A skill that pretends to save and doesn’t is the worst class of bug — the user trusts the memory exists and finds out three weeks later it never did.
  • ID is a millisecond-precision timestamp prefix plus six random bytes. Sortable, unique, no extra ULID dependency.
  • The INSERT is parameterised. The body comes from a language model; treat it like user input from the open internet.

File 3 — nothing (read-back already exists)

The SessionStart hook from D1 already reads top-K rows from memory scoped to the project. Once capture.sh writes a row, the next session sees it without any change to the read path.

If the D1 hook is not in place, the minimal version:

#!/usr/bin/env bash
# ~/.claude/hooks/session-start.sh
EMBED=$(printf '%s' "$CLAUDE_PROJECT_DIR" | embed-cli)
curl -sS "${REDDB_URL}/sql" \
  -H "Authorization: Bearer ${REDDB_TOKEN}" \
  -d "$(jq -nc --arg s "$CLAUDE_PROJECT_DIR" --argjson e "$EMBED" \
        '{sql:"SELECT body FROM memory WHERE scope IN ($1,$'global') AND deleted_at IS NULL ORDER BY embedding <=> $2 LIMIT 8", params:[$s,$e]}')" \
  | jq -r '.rows[] | "- " + .body'

That output is appended to the session’s initial system message. The agent now opens every turn with the most relevant rows from prior conversations.

Testing it

Start a fresh session in any project. Steer the agent into territory worth remembering:

“I’m a Go engineer working on the React side of this repo for the first time. Frame frontend explanations in terms of backend analogues.”

A correctly-tuned remember-this should fire once and produce something like:

Remembered (user, /home/you/work/this-repo):
  User is a Go engineer new to React in this repo;
  prefers frontend concepts framed as backend analogues.

Verify directly against the database:

SELECT id, kind, scope, body
FROM memory
WHERE scope = '/home/you/work/this-repo'
ORDER BY created_at DESC
LIMIT 5;

Then start a new session in the same directory. The SessionStart hook should pull the row, and a question like “how should I structure this component?” should get answered in backend-analogue terms without you re-stating the preference.

What goes wrong in week one

Three failure modes, observed across the first dozen installs:

Over-firing. The skill description is the only thing the harness sees. If it reads as “save anything interesting”, the agent saves chat noise. Tighten the description until the skill fires roughly once per session of real work. Re-read the Do not block above — most of it exists because of an earlier over-firing rev.

Embedding cost surprise. Every /remember is one embedding call. At Voyage’s price it is fractions of a cent, but it is non-zero and it lives on the read path of every future session. If memory grows past ~10k rows for a single scope, prune. A /forget --before=30d --kind=feedback cleanup pass once a quarter keeps the index hot.

Scope confusion. scope=global looks tempting and is almost always wrong. A preference that holds for one project rarely holds for the next. Default to the project path; promote to global only after the same fact has been re-saved across three projects.

Why this shape

The skill is twenty-odd lines of markdown plus a thirty-line shell script. It deliberately does not include:

  • A daemon. The skill runs synchronously on the agent’s turn. Latency is one HTTP roundtrip to the embedder and one to RedDB, well under a second.
  • A queue. If the write fails, the agent sees the non-zero exit and tells the user. No silent retry, no dropped rows.
  • An ORM. The SQL is short enough to read; the parameter binding is the only safety bit that matters.

The whole pillar’s argument is that a relational + vector database is a sufficient memory substrate for agent CLIs. This skill is the smallest end-to-end demonstration of that claim: one table, three files, real recall on the next session.

Next

The next post in this pillar takes the same memory table and adds the observability layer — every tool call, token, and cost landing in RedDB via a PostToolUse hook, queryable as “which skill cost me the most last sprint” and “p99 latency by tool”. The remember-this skill becomes one row among thousands in that view; the database stops being a memory store and starts being the agent’s substrate.

© 2026 RedDB.io. AGPL-3.0 self-host · Managed Cloud.