| name | syncing-memory-filesystem |
| description | Manage git-backed memory repos. Load this skill when working with git-backed agent memory, setting up remote memory repos, resolving sync conflicts, or managing memory via git workflows. |
Git-Backed Memory Repos
Agents with the git-memory-enabled tag have their memory blocks stored in git repositories accessible via the Letta API. This enables version control, collaboration, and external editing of agent memory.
Features:
- Stored in cloud (GCS)
- Accessible via
$LETTA_BASE_URL/v1/git/<agent-id>/state.git
- Bidirectional sync: API <-> Git (webhook-triggered, ~2-3s delay)
- Structure:
memory/system/*.md for system blocks
What the CLI Harness Does Automatically
When memfs is enabled, the Letta Code CLI automatically:
- Adds the
git-memory-enabled tag to the agent (triggers backend to create the git repo)
- Clones the repo into
~/.letta/agents/<agent-id>/memory/ (git root is the memory directory)
- Configures a local credential helper in
memory/.git/config (so git push/git pull work without auth ceremony)
- Installs a pre-commit hook that validates frontmatter before each commit (see below)
- Installs a post-commit hook that pushes commits to an optional additional remote (see "Additional memory-repository remote" below)
- Sets canonical local git identity (
letta.agentId, user.name, user.email) so direct git commit from the agent's shell attributes correctly to the agent — not the operator's global git identity
- On subsequent startups: pulls latest changes, reconfigures credentials, hooks, and identity (self-healing)
- During sessions: periodically checks
git status and reminds you (the agent) to commit/push if dirty
If any of these steps fail, you can replicate them manually using the sections below.
Authentication (Preferred: Repo-Local)
The harness configures a per-repo credential helper during clone and refreshes it on pull/startup.
This local setup is the default and recommended approach.
Why this matters: host-level global credential helpers (e.g. installed by other tooling) can conflict with memfs auth and cause confusing failures.
Important: Always use single-line format for credential helpers. Multi-line helpers can break tools that parse git config --list line-by-line.
cd ~/.letta/agents/<agent-id>/memory
git config --local --get-regexp '^credential\..*\.helper$'
git config --local credential.$LETTA_BASE_URL.helper '!f() { echo "username=letta"; echo "password=$LETTA_API_KEY"; }; f'
If you suspect global helper conflicts, inspect and clear host-specific global entries:
git config --global --get-regexp '^credential\..*letta\.com.*\.helper$'
git config --global --unset-all credential.https://api.letta.com.helper
For cloning a different agent's repo, prefer a one-off auth header over global credential changes:
AUTH_HEADER="Authorization: Basic $(printf 'letta:%s' "$LETTA_API_KEY" | base64 | tr -d '\n')"
git -c "http.extraHeader=$AUTH_HEADER" clone "$LETTA_BASE_URL/v1/git/<agent-id>/state.git" ~/my-agent-memory
Pre-Commit Hook (Frontmatter Validation)
The harness installs a git pre-commit hook that validates .md files under memory/ before each commit. This prevents pushes that the server would reject.
Rules:
- Every
.md file must have YAML frontmatter (--- header and closing ---)
- Required fields:
description (non-empty string)
read_only is a protected field: you (the agent) cannot add, remove, or change it. Files with read_only: true cannot be modified at all. Only the server/user sets this field.
- Unknown frontmatter keys are rejected
Valid file format:
---
description: What this block contains
---
Block content goes here.
If the hook rejects a commit, read the error message — it tells you exactly which file and which rule was violated. Fix the file and retry.
Additional Memory-Repository Remote
In addition to pushing to the Letta server, you can push every commit to a second git remote — e.g. a private GitHub repo — so you have a backup or a copy you can browse with regular tools.
Via the slash command (recommended):
/memory-repository set git@github.com:you/my-memory.git
/memory-repository status
/memory-repository push # force a push now, e.g. after a network failure
/memory-repository unset # stop pushing
How it works:
/memory-repository set <url> writes the URL to letta.memoryRepository.url in the memfs repo's local .git/config and installs a post-commit hook.
- After every commit, the hook reads
letta.memoryRepository.url and asynchronously pushes to it in the background. Commits are never blocked by push failures.
- Push output and exit codes are appended to
.git/memory-repository-push.log — visible via /memory-repository status.
- The setting is per-repo, so each agent on a machine has its own independent configuration.
Auth: uses your existing git credentials — SSH keys, credential helpers, or tokens in the URL. Letta does not store tokens for this feature. If you're pushing to GitHub, SSH is easiest.
Manual equivalent (without the slash command):
cd ~/.letta/agents/<agent-id>/memory
git config --local letta.memoryRepository.url git@github.com:you/my-memory.git
Clone Agent Memory
git clone "$LETTA_BASE_URL/v1/git/<agent-id>/state.git" ~/my-agent-memory
ls ~/my-agent-memory/memory/system/
cat ~/my-agent-memory/memory/system/human.md
Enabling Git Memory (Manual)
If the harness /memfs enable failed, you can replicate it:
AGENT_ID="<your-agent-id>"
AGENT_DIR=~/.letta/agents/$AGENT_ID
MEMORY_REPO_DIR="$AGENT_DIR/memory"
curl -X PATCH "$LETTA_BASE_URL/v1/agents/$AGENT_ID" \
-H "Authorization: Bearer $LETTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tags": ["origin:letta-code", "git-memory-enabled"]}'
mkdir -p "$MEMORY_REPO_DIR"
git clone "$LETTA_BASE_URL/v1/git/$AGENT_ID/state.git" "$MEMORY_REPO_DIR"
cd "$MEMORY_REPO_DIR"
git config --local credential.$LETTA_BASE_URL.helper '!f() { echo "username=letta"; echo "password=$LETTA_API_KEY"; }; f'
Bidirectional Sync
API Edit -> Git Pull
cd ~/.letta/agents/<agent-id>/memory
git pull
Changes made via the API are automatically committed to git within 2-3 seconds.
Git Push -> API Update
cd ~/.letta/agents/<agent-id>/memory
echo "Updated info" > system/human.md
git add system/human.md
git commit -m "fix: update human block"
git push
Conflict Resolution
When both API and git have diverged:
cd ~/.letta/agents/<agent-id>/memory
git push
git pull --no-rebase
cat system/human.md
echo "final resolved content" > system/human.md
git add system/human.md
git commit -m "fix: resolved conflict in human block"
git push
Block Management
Create New Block
echo "My new block content" > system/new-block.md
git add system/new-block.md
git commit -m "feat: add new block"
git push
Delete/Detach Block
git rm system/persona.md
git commit -m "chore: remove persona block"
git push
Directory Structure
~/.letta/agents/<agent-id>/
├── .letta/
│ └── config.json # Agent metadata
└── memory/ # Git repo root
├── .git/ # Git repo data
└── system/ # System blocks (attached to agent)
├── human.md
└── persona.md
System blocks (memory/system/) are attached to the agent and appear in the agent's system prompt.
Requirements
- Agent must have
git-memory-enabled tag
- Valid API key with agent access
- Git installed locally
Troubleshooting
Clone fails with "Authentication failed":
- Check local helper(s):
git -C ~/.letta/agents/<agent-id>/memory config --local --get-regexp '^credential\..*\.helper$'
- Check for conflicting global helper(s):
git config --global --get-regexp '^credential\..*letta\.com.*\.helper$'
- Reconfigure local helper: see Authentication section above
- Verify the endpoint is reachable:
curl -u letta:$LETTA_API_KEY $LETTA_BASE_URL/v1/git/<agent-id>/state.git/info/refs?service=git-upload-pack
Push/pull doesn't update API:
- Wait 2-3 seconds for webhook processing
- Verify agent has
git-memory-enabled tag
- Check if you have write access to the agent
Harness setup failed (no .git/ after /memfs enable):
- Check debug logs (
LETTA_DEBUG=1)
- Follow "Enabling Git Memory (Manual)" steps above
Can't see changes immediately:
- Bidirectional sync has a 2-3 second delay for webhook processing
- Use
git pull to get latest API changes
- Use
git fetch to check remote without merging