Skip to content

Instantly share code, notes, and snippets.

@twiecki
Created January 21, 2026 04:42
Show Gist options
  • Select an option

  • Save twiecki/26f2423e69cb6ee6372d49938cb38842 to your computer and use it in GitHub Desktop.

Select an option

Save twiecki/26f2423e69cb6ee6372d49938cb38842 to your computer and use it in GitHub Desktop.
# Personal Knowledge Daemon - Fresh Template Setup
You are setting up a fresh personal knowledge management system following the "daemon" pattern - a persistent memory layer for Claude Code stored as markdown files in Git.
## Core Concept
**GitHub = Memory | Markdown = Thoughts | PRs = Memory Updates**
Each Claude Code session:
1. Reads context from markdown files
2. Works on tasks, updates knowledge
3. Commits changes via PR
4. Next session picks up where you left off
## Directory Structure to Create
```
daemon/
├── README.md # Repository overview and orientation guide
├── AGENTS.md # Agent workflow documentation
├── pyproject.toml # Python dependencies (use uv)
├── .gitignore # Standard Python + secrets
├── .env.example # Template for API keys
├── skills/ # Composio integrations and tools
│ ├── composio-overview.md # Quick start guide for Composio
│ ├── composio-calendar.md # Google Calendar integration
│ ├── composio-gmail.md # Gmail integration
│ ├── composio-drive.md # Google Drive integration
│ └── ... (add more as needed)
├── hooks/ # Claude Code lifecycle hooks
│ ├── session-start.sh # SessionStart hook (install tools, clone repos)
│ └── subagent-pr.sh # SubagentStop hook (auto PR creation)
├── settings.json # Claude Code settings (hooks config)
├── tasks/
│ ├── active.md # Current priorities and TODOs
│ └── archive/ # Completed tasks
├── people/ # Relationship profiles (optional)
├── work/ # Professional context (optional)
│ └── buckets/ # Open work items
├── personal/ # Personal context (optional)
└── projects/ # Git submodules or cloned repos (optional)
```
## Setup Steps
### 1. Create README.md
```markdown
# Daemon - Personal Knowledge System
A persistent memory layer for Claude, stored as markdown files in Git.
## How It Works
1. **Spawn**: Start a Claude Code session with this repo
2. **Context**: Claude reads relevant files to understand the current situation
3. **Work**: Discuss, explore, plan - Claude updates markdown files with new insights
4. **Commit**: Create a PR, merge it, memory is updated
5. **Persist**: Next session picks up where you left off
## Directory Structure
[Copy the tree structure from above]
## Getting Started
1. Set up `.env` file with API keys (see `.env.example`)
2. Start a Claude Code session
3. The SessionStart hook will install tools and clone any configured repos
4. Start working - all context is in markdown files
## Language & Communication
- Files: English (for portability and tooling)
- Conversation: [Your preference - German/English/etc]
## Skills
Available integrations via `skills/`:
- Calendar (Google Calendar via Composio)
- Email (Gmail via Composio)
- [Add your integrations]
See `skills/composio-overview.md` for setup instructions.
```
### 2. Create AGENTS.md
```markdown
# Agent Workflow & Orchestration
## Overview
This repo supports a hierarchical agent workflow:
```
Main Branch: claude/main-session-xxxxx
└── Orchestrator-Branch (session with full context)
└── Sub-Agent-Branch (specialized task)
→ PR back to Orchestrator-Branch
```
**Orchestrator**: Main session with full context, starts specialized sub-agents
**Sub-Agent**: Focused task, reports back to orchestrator
## Branch Naming Convention
All branches must follow: `claude/<description>-<session-id>`
Examples:
- `claude/main-session-HOaw1`
- `claude/email-summary-task-xY3k9`
## Environment Variables for Sub-Agents
When starting a sub-agent from an orchestrator session:
```bash
export PARENT_BRANCH=$(git branch --show-current)
# Then spawn sub-agent
```
The `PARENT_BRANCH` env var tells the SubagentStop hook to create a PR against the orchestrator branch instead of main.
## Git Operations
### Push Requirements
- Always use: `git push -u origin <branch-name>`
- Branch must match pattern: `claude/*-<session-id>`
- Retry on network errors: up to 4 times with exponential backoff (2s, 4s, 8s, 16s)
### Fetch/Pull
- Prefer specific branches: `git fetch origin <branch-name>`
- Retry on network errors: same exponential backoff
## Workflow Hooks
### SessionStart Hook
- Installs required tools (gh CLI, etc.)
- Loads environment variables from `.env`
- Clones configured repos to `projects/`
- Sets up authentication
### SubagentStop Hook
- Automatically creates PR when sub-agent completes
- PR target: `$PARENT_BRANCH` (if set) or main branch
- Enables auto-merge if repository supports it
- Waits for CI checks
- Merges if all checks pass
## Python Dependency Management
Use `uv` (not `pip`):
```bash
# Install/sync dependencies
uv sync
# Add new package
uv add <package-name>
# Run script (uses .venv automatically)
uv run python script.py
```
Dependencies defined in `pyproject.toml`.
## GitHub CLI
Always load environment variables first:
```bash
source /home/user/daemon/.env && export GH_TOKEN="$GITHUB_TOKEN" && gh <command>
```
This is required because hooks cannot pass environment variables to main session.
```
### 3. Create settings.json
```json
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "hooks/session-start.sh"
}
]
}
],
"SubagentStop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "hooks/subagent-pr.sh"
}
]
}
]
}
}
```
### 4. Create pyproject.toml
```toml
[project]
name = "daemon"
version = "0.1.0"
description = "Personal assistant daemon"
requires-python = ">=3.11"
dependencies = [
"composio",
"python-dotenv",
]
[tool.uv]
dev-dependencies = []
```
### 5. Create .gitignore
```
# Python
__pycache__/
*.py[cod]
*$py.class
.venv/
venv/
ENV/
env/
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Environment
.env
.env.local
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Claude Code
.claude/cache/
```
### 6. Create .env.example
```bash
# Composio API Key (get from https://app.composio.dev)
COMPOSIO_API_KEY=your_composio_api_key_here
# GitHub Token (for gh CLI authentication)
GITHUB_TOKEN=your_github_token_here
# Optional: Claude Instance ID (for multi-instance coordination)
CLAUDE_INSTANCE=A
```
### 7. Create hooks/session-start.sh
```bash
#!/bin/bash
# SessionStart hook: setup environment and tools
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$SCRIPT_DIR/.."
ENV_FILE="$REPO_DIR/.env"
# Load environment variables
if [ -f "$ENV_FILE" ]; then
set -a
source "$ENV_FILE"
set +a
fi
# Set Claude instance ID
export CLAUDE_INSTANCE="${CLAUDE_INSTANCE:-A}"
# Install GitHub CLI if not present
if ! command -v gh &> /dev/null; then
echo "Installing GitHub CLI..."
GH_VERSION="2.63.2"
if curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o /tmp/gh.tar.gz && \
tar -xzf /tmp/gh.tar.gz -C /tmp && \
sudo mv "/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/ && \
rm -rf /tmp/gh.tar.gz /tmp/gh_${GH_VERSION}_linux_amd64; then
echo "GitHub CLI installed."
else
echo "GitHub CLI installation failed."
fi
fi
# Set GH_TOKEN for gh CLI authentication
if [ -n "$GITHUB_TOKEN" ]; then
export GH_TOKEN="$GITHUB_TOKEN"
fi
# Optional: Clone project repos
# PROJECTS_DIR="$REPO_DIR/projects"
# if [ -d "$PROJECTS_DIR" ]; then
# echo "Checking project repos..."
# REPOS="your-org/repo1 your-org/repo2"
# for repo in $REPOS; do
# repo_name=$(basename $repo)
# if [ ! -d "$PROJECTS_DIR/$repo_name/.git" ]; then
# echo "Cloning $repo..."
# gh repo clone "$repo" "$PROJECTS_DIR/$repo_name" 2>/dev/null || \
# echo "Failed to clone $repo"
# fi
# done
# fi
echo "Session ready!"
```
### 8. Create hooks/subagent-pr.sh
```bash
#!/bin/bash
# SubagentStop hook: automatic PR creation and merge
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$SCRIPT_DIR/.."
cd "$REPO_DIR"
# Load environment for gh CLI
if [ -f "$REPO_DIR/.env" ]; then
set -a
source "$REPO_DIR/.env"
set +a
fi
if [ -n "$GITHUB_TOKEN" ]; then
export GH_TOKEN="$GITHUB_TOKEN"
fi
log() {
echo "[PR-Workflow] $1"
}
# Current branch
CURRENT_BRANCH=$(git branch --show-current)
# Main branch (customize for your repo)
MAIN_BRANCH="main" # Or "claude/main-session-xxxxx" if you have a long-lived orchestrator
# Base branch for PR: PARENT_BRANCH (if set by orchestrator) or MAIN_BRANCH
if [ -n "$PARENT_BRANCH" ]; then
BASE_BRANCH="$PARENT_BRANCH"
log "Sub-Agent Mode: PR against orchestrator branch $BASE_BRANCH"
else
BASE_BRANCH="$MAIN_BRANCH"
log "Orchestrator Mode: PR against main branch $BASE_BRANCH"
fi
# Don't run on main branch
if [[ "$CURRENT_BRANCH" == "$MAIN_BRANCH" ]]; then
log "On main branch - skipping PR workflow"
exit 0
fi
# Don't run on base branch
if [[ "$CURRENT_BRANCH" == "$BASE_BRANCH" ]]; then
log "On base branch - skipping PR workflow"
exit 0
fi
# Check for uncommitted changes
if ! git diff --quiet || ! git diff --cached --quiet; then
log "WARNING: Uncommitted changes detected - these will NOT be in the PR"
git status --short
fi
# Check if remote branch exists
if ! git rev-parse --verify "origin/$CURRENT_BRANCH" >/dev/null 2>&1; then
log "Branch $CURRENT_BRANCH doesn't exist on remote - pushing"
HAS_NEW_COMMITS=true
else
# Check for unpushed commits
LOCAL_COMMITS=$(git rev-list "origin/$CURRENT_BRANCH..$CURRENT_BRANCH" --count 2>/dev/null || echo "0")
if [ "$LOCAL_COMMITS" -eq 0 ]; then
log "No new commits to push"
exit 0
fi
HAS_NEW_COMMITS=true
fi
# Push to remote
log "Pushing branch $CURRENT_BRANCH..."
if ! git push -u origin "$CURRENT_BRANCH" 2>/dev/null; then
log "Push failed - may already be pushed or insufficient permissions"
fi
# Check if PR already exists
EXISTING_PR=$(gh pr list --head "$CURRENT_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
log "PR #$EXISTING_PR already exists for branch $CURRENT_BRANCH"
PR_NUMBER="$EXISTING_PR"
else
# Create new PR
log "Creating new PR..."
COMMIT_MSG=$(git log -1 --pretty=%s)
COMMIT_BODY=$(git log -1 --pretty=%b)
PR_URL=$(gh pr create \
--title "$COMMIT_MSG" \
--body "${COMMIT_BODY:-Auto-created by SubagentStop workflow}" \
--base "$BASE_BRANCH" \
--head "$CURRENT_BRANCH" \
2>/dev/null) || {
log "PR creation failed"
exit 1
}
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
log "PR #$PR_NUMBER created: $PR_URL"
fi
# Enable auto-merge (if supported)
log "Enabling auto-merge for PR #$PR_NUMBER..."
gh pr merge "$PR_NUMBER" --auto --squash 2>/dev/null || {
log "Auto-merge not available - manual merge required"
}
# Wait for CI checks
log "Waiting for CI checks (max 5 minutes)..."
if timeout 300 gh pr checks "$PR_NUMBER" --watch 2>/dev/null; then
log "All checks passed!"
# Manual merge if auto-merge wasn't enabled
if ! gh pr view "$PR_NUMBER" --json autoMergeRequest --jq '.autoMergeRequest' | grep -q "SQUASH"; then
log "Merging PR #$PR_NUMBER..."
gh pr merge "$PR_NUMBER" --squash --delete-branch 2>/dev/null || {
log "Merge failed - may need review"
}
else
log "Auto-merge enabled - PR will merge automatically when ready"
fi
else
log "CI checks failed or timeout - PR remains open"
exit 1
fi
log "Workflow complete!"
```
### 9. Create tasks/active.md
```markdown
# Active Tasks
## Current Priorities
### [Task Category]
- [ ] Task 1
- [ ] Task 2
## Waiting On / Blocked
## Notes
```
### 10. Create skills/composio-overview.md
```markdown
# Composio Integration Overview
## Quick Start
```python
from composio import Composio
from dotenv import load_dotenv
import os
load_dotenv('/home/user/daemon/.env')
client = Composio(api_key=os.getenv('COMPOSIO_API_KEY'))
```
## Setup Instructions
1. Get Composio API key from https://app.composio.dev
2. Add to `.env`: `COMPOSIO_API_KEY=your_key_here`
3. Connect services you need (Calendar, Gmail, Drive, etc.)
4. Note the `connected_account_id` for each service
## General Pattern
```python
result = client.tools.execute(
slug="TOOLNAME_ACTION",
arguments={...},
connected_account_id="ca_XXX", # Use this, not user_id
dangerously_skip_version_check=True # Required
)
if result.get('successful'):
data = result['data']['response_data']
else:
error = result.get('error')
```
## List Available Tools for a Service
```python
tools = client.tools.get_raw_composio_tools(toolkits=["googlecalendar"])
for tool in tools:
print(f"- {tool.slug}: {tool.name}")
```
## List Connected Accounts
```python
accounts = client.connected_accounts.list()
for item in accounts.items:
print(f"- {item.id}: {item.toolkit.slug} ({item.status})")
```
## Common Issues
### SSL Certificate Errors
Transient issue. Solution: Add `time.sleep(2)` and retry.
### Version Check Errors
Always add `dangerously_skip_version_check=True` to execute() calls.
### User ID vs Connected Account ID
- `user_id`: Links to a user, may have multiple connections
- `connected_account_id`: Links to specific OAuth connection
**Always prefer `connected_account_id` for reliability!**
```
### 11. Make hooks executable
```bash
chmod +x hooks/session-start.sh hooks/subagent-pr.sh
```
### 12. Initialize git and create first commit
```bash
git init
git add .
git commit -m "Initial daemon template setup"
git branch -M main
git remote add origin <your-repo-url>
git push -u origin main
```
## Skills to Port from Original Daemon
The following skills are worth porting:
1. **composio-calendar.md** - Google Calendar integration
2. **composio-gmail.md** - Gmail integration
3. **composio-drive.md** - Google Drive integration
4. **composio-notion.md** - Notion integration (if you use Notion)
5. **morning-briefing.md** - Combined email + calendar summary
6. **content-writing-authority.md** - Writing style guide
## Customization
1. **Main branch**: Update `MAIN_BRANCH` in `hooks/subagent-pr.sh` to match your workflow
2. **Repos to clone**: Edit `hooks/session-start.sh` to clone your projects
3. **Skills**: Add service-specific integration guides in `skills/`
4. **Context**: Add your personal/work context in respective directories
5. **Language**: Set your preferred language in README.md
## Standards Compliance
✅ Uses `skills/` instead of `.claude/skills/`
✅ Uses `AGENTS.md` for workflow documentation
✅ Hooks in dedicated `hooks/` directory
✅ Clear separation: config in root, logic in subdirectories
✅ Standard Python tooling (uv, pyproject.toml)
✅ Markdown-first for all documentation
## Next Steps After Setup
1. Copy your API keys to `.env`
2. Run: `uv sync` to install dependencies
3. Start a Claude Code session
4. The SessionStart hook will prepare your environment
5. Start adding your context to markdown files
6. Work with Claude, commit via PRs
7. Your knowledge persists across sessions!
---
**Remember**: GitHub is your memory. Markdown files are your thoughts. PRs are memory updates. Let the system work for you.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment