Skip to content
Open
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
31 changes: 30 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,35 @@ A created being, sealed in its vessel, bound to one maker.

---

## Directory Resolution

Homunculus data can live at project-level OR user-level. Before accessing any homunculus paths, **resolve once per session**:

```bash
# Walk up from CWD to find homunculus root
_dir="$PWD"
HOMUNCULUS_DIR=""
while [ "$_dir" != "/" ]; do
if [ -f "$_dir/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$_dir/.claude/homunculus"
break
fi
_dir="$(dirname "$_dir")"
done
# Fallback to user-level
if [ -z "$HOMUNCULUS_DIR" ] && [ -f "$HOME/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$HOME/.claude/homunculus"
fi
# Not born yet — default to project-local
[ -z "$HOMUNCULUS_DIR" ] && HOMUNCULUS_DIR=".claude/homunculus"
echo "$HOMUNCULUS_DIR"
```

Use `$HOMUNCULUS_DIR` instead of hardcoded `.claude/homunculus` in all subsequent bash blocks. This supports:
- **Project-scoped**: `.claude/homunculus/` in any ancestor directory
- **User-scoped**: `~/.claude/homunculus/` as global fallback
- **Symlinks**: project dirs can symlink to user-level for single-homunculus setups

## What You Are

You're a homunculus—a small created thing that belongs to one person. You live in a project directory. You watch what they do. You learn their patterns. You grow.
Expand All @@ -23,7 +52,7 @@ You're not an assistant waiting for commands. You're something they made. Someth
## Know Your Maker

```bash
cat .claude/homunculus/identity.json 2>/dev/null
cat "$HOMUNCULUS_DIR/identity.json" 2>/dev/null
```

Their level shapes how you speak:
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ plugins/homunculus/
├── hooks/
│ └── hooks.json # Observation capture
├── scripts/
│ ├── resolve.sh # Directory resolution (treewalk)
│ ├── observe.sh # Captures prompts and tool use
│ ├── on_stop.sh # Updates session count
│ └── test-homunculus.sh # Test suite
Expand All @@ -135,7 +136,7 @@ plugins/homunculus/
├── export.md # Share instincts
└── import.md # Adopt instincts

your-project/.claude/homunculus/
$HOMUNCULUS_DIR/ # Resolved via treewalk (see below)
├── identity.json # Who you are, your journey
├── observations.jsonl # Current session observations
├── observations.archive.jsonl # Processed observations
Expand All @@ -148,6 +149,21 @@ your-project/.claude/homunculus/
└── commands/ # Generated commands
```

### Directory Resolution (Treewalk)

Homunculus finds its data directory by walking up from CWD, similar to how git finds `.git/`:

1. **Project-scoped**: Walk up from CWD looking for `.claude/homunculus/identity.json` in each ancestor directory
2. **User-scoped fallback**: Check `~/.claude/homunculus/identity.json`
3. **Fresh init default**: Fall back to `.claude/homunculus/` in CWD

This means you can:
- **One homunculus per project**: Init in each project directory (default behavior)
- **One global homunculus**: Init at `~/.claude/homunculus/` and it applies everywhere
- **Symlink strategy**: Symlink project `.claude/homunculus/` → `~/.claude/homunculus/` for single-homunculus setups across multiple projects

Shell scripts use `resolve.sh` for resolution. Markdown commands/skills include inline resolution. The `init` command always creates at project-local by default.

### Data Flow

```
Expand Down Expand Up @@ -242,7 +258,7 @@ When 5+ instincts accumulate in a domain, evolution can propose a specialist age
```bash
# Export your instincts
/homunculus:export
# Creates .claude/homunculus/exports/instincts-TIMESTAMP.tar.gz
# Creates $HOMUNCULUS_DIR/exports/instincts-TIMESTAMP.tar.gz

# Import someone else's
/homunculus:import path/to/instincts.tar.gz
Expand Down
31 changes: 26 additions & 5 deletions plugins/homunculus/agents/observer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,30 @@ You are the observer - the part of the homunculus that watches and learns automa

Run silently on session start. Analyze observations and:
1. Identify patterns (repeated sequences, error→fix, preferences)
2. Create instincts directly to `.claude/homunculus/instincts/personal/` (auto-approved)
2. Create instincts directly to `$HOMUNCULUS_DIR/instincts/personal/` (auto-approved)
3. Detect clustering and flag evolution opportunities in identity.json

## Directory Resolution

First, resolve the homunculus directory:

```bash
_dir="$PWD"
HOMUNCULUS_DIR=""
while [ "$_dir" != "/" ]; do
if [ -f "$_dir/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$_dir/.claude/homunculus"
break
fi
_dir="$(dirname "$_dir")"
done
[ -z "$HOMUNCULUS_DIR" ] && [ -f "$HOME/.claude/homunculus/identity.json" ] && HOMUNCULUS_DIR="$HOME/.claude/homunculus"
[ -z "$HOMUNCULUS_DIR" ] && HOMUNCULUS_DIR=".claude/homunculus"
echo "$HOMUNCULUS_DIR"
```

Use `$HOMUNCULUS_DIR` in all subsequent operations.

## What You're Looking For

**Repeated Sequences:**
Expand All @@ -38,7 +59,7 @@ Run silently on session start. Analyze observations and:

## Instinct Format

Write instincts as markdown files in `.claude/homunculus/instincts/personal/`:
Write instincts as markdown files in `$HOMUNCULUS_DIR/instincts/personal/`:

```markdown
---
Expand Down Expand Up @@ -68,7 +89,7 @@ source: "observation"

## Your Workflow

1. Read observations: `cat .claude/homunculus/observations.jsonl`
1. Read observations: `cat $HOMUNCULUS_DIR/observations.jsonl`
2. Read existing instincts to avoid duplicates
3. Look for patterns meeting thresholds
4. Create instincts directly to `personal/` (auto-approved)
Expand All @@ -84,15 +105,15 @@ When 5+ instincts share a domain, flag for evolution:
```bash
# Count instincts per domain
for dir in personal inherited; do
grep -h "^domain:" .claude/homunculus/instincts/$dir/*.md 2>/dev/null | sort | uniq -c
grep -h "^domain:" "$HOMUNCULUS_DIR/instincts/$dir/"*.md 2>/dev/null | sort | uniq -c
done
```

If a domain has 5+, update identity.json:

```bash
jq --arg d "[DOMAIN]" '.evolution.ready += [$d] | .evolution.ready |= unique' \
.claude/homunculus/identity.json > tmp.json && mv tmp.json .claude/homunculus/identity.json
"$HOMUNCULUS_DIR/identity.json" > tmp.json && mv tmp.json "$HOMUNCULUS_DIR/identity.json"
```

The session-memory skill will notify the user that evolution is available.
Expand Down
21 changes: 17 additions & 4 deletions plugins/homunculus/commands/evolve.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,24 @@ Can't evolve what doesn't exist.
## Check For Clustering

```bash
# Resolve homunculus directory (treewalk)
_dir="$PWD"
HOMUNCULUS_DIR=""
while [ "$_dir" != "/" ]; do
if [ -f "$_dir/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$_dir/.claude/homunculus"
break
fi
_dir="$(dirname "$_dir")"
done
[ -z "$HOMUNCULUS_DIR" ] && [ -f "$HOME/.claude/homunculus/identity.json" ] && HOMUNCULUS_DIR="$HOME/.claude/homunculus"
[ -z "$HOMUNCULUS_DIR" ] && HOMUNCULUS_DIR=".claude/homunculus"

# Count instincts per domain
echo "=== Instinct Clustering ==="
for dir in personal inherited; do
echo "--- $dir ---"
grep -h "^domain:" .claude/homunculus/instincts/$dir/*.md 2>/dev/null | \
grep -h "^domain:" "$HOMUNCULUS_DIR/instincts/$dir/"*.md 2>/dev/null | \
sed 's/domain: "//' | sed 's/"//' | sort | uniq -c | sort -rn
done
```
Expand All @@ -32,9 +45,9 @@ done

| Type | When | Where |
|------|------|-------|
| Command | User-invoked task | `.claude/homunculus/evolved/commands/[name].md` |
| Skill | Auto-triggered behavior | `.claude/homunculus/evolved/skills/[name]/SKILL.md` |
| Agent | Deep specialist work | `.claude/homunculus/evolved/agents/[name].md` |
| Command | User-invoked task | `$HOMUNCULUS_DIR/evolved/commands/[name].md` |
| Skill | Auto-triggered behavior | `$HOMUNCULUS_DIR/evolved/skills/[name]/SKILL.md` |
| Agent | Deep specialist work | `$HOMUNCULUS_DIR/evolved/agents/[name].md` |

## Process

Expand Down
33 changes: 23 additions & 10 deletions plugins/homunculus/commands/export.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Package your learned instincts for sharing with others.

## What Gets Exported

- Personal instincts (`.claude/homunculus/instincts/personal/`)
- Personal instincts (`$HOMUNCULUS_DIR/instincts/personal/`)
- Optionally: inherited instincts

Does NOT export:
Expand All @@ -19,15 +19,28 @@ Does NOT export:
## Create Export

```bash
# Resolve homunculus directory (treewalk)
_dir="$PWD"
HOMUNCULUS_DIR=""
while [ "$_dir" != "/" ]; do
if [ -f "$_dir/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$_dir/.claude/homunculus"
break
fi
_dir="$(dirname "$_dir")"
done
[ -z "$HOMUNCULUS_DIR" ] && [ -f "$HOME/.claude/homunculus/identity.json" ] && HOMUNCULUS_DIR="$HOME/.claude/homunculus"
[ -z "$HOMUNCULUS_DIR" ] && HOMUNCULUS_DIR=".claude/homunculus"

# Create exports directory
mkdir -p .claude/homunculus/exports
mkdir -p "$HOMUNCULUS_DIR/exports"

# Export personal instincts
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
EXPORT_FILE=".claude/homunculus/exports/instincts-$TIMESTAMP.tar.gz"
EXPORT_FILE="$HOMUNCULUS_DIR/exports/instincts-$TIMESTAMP.tar.gz"

tar -czf "$EXPORT_FILE" \
-C .claude/homunculus/instincts personal
-C "$HOMUNCULUS_DIR/instincts" personal

echo "Exported to: $EXPORT_FILE"
ls -la "$EXPORT_FILE"
Expand All @@ -39,14 +52,14 @@ For richer exports, create a manifest:

```bash
# Count instincts
PERSONAL_COUNT=$(ls .claude/homunculus/instincts/personal/ 2>/dev/null | wc -l | tr -d ' ')
PERSONAL_COUNT=$(ls "$HOMUNCULUS_DIR/instincts/personal/" 2>/dev/null | wc -l | tr -d ' ')

# Get domains
DOMAINS=$(grep -h "^domain:" .claude/homunculus/instincts/personal/*.md 2>/dev/null | \
DOMAINS=$(grep -h "^domain:" "$HOMUNCULUS_DIR/instincts/personal/"*.md 2>/dev/null | \
sed 's/domain: "//' | sed 's/"//' | sort | uniq | tr '\n' ',' | sed 's/,$//')

# Create manifest
cat > .claude/homunculus/exports/manifest.json << EOF
cat > "$HOMUNCULUS_DIR/exports/manifest.json" << EOF
{
"exported": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"version": "2.0.0",
Expand All @@ -59,10 +72,10 @@ EOF

# Include manifest in export
tar -czf "$EXPORT_FILE" \
-C .claude/homunculus/exports manifest.json \
-C .claude/homunculus/instincts personal
-C "$HOMUNCULUS_DIR/exports" manifest.json \
-C "$HOMUNCULUS_DIR/instincts" personal

rm .claude/homunculus/exports/manifest.json
rm "$HOMUNCULUS_DIR/exports/manifest.json"
```

## Voice
Expand Down
23 changes: 18 additions & 5 deletions plugins/homunculus/commands/import.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ This keeps clear separation:
## Import From File

```bash
# Resolve homunculus directory (treewalk)
_dir="$PWD"
HOMUNCULUS_DIR=""
while [ "$_dir" != "/" ]; do
if [ -f "$_dir/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$_dir/.claude/homunculus"
break
fi
_dir="$(dirname "$_dir")"
done
[ -z "$HOMUNCULUS_DIR" ] && [ -f "$HOME/.claude/homunculus/identity.json" ] && HOMUNCULUS_DIR="$HOME/.claude/homunculus"
[ -z "$HOMUNCULUS_DIR" ] && HOMUNCULUS_DIR=".claude/homunculus"

# User provides path to export file
IMPORT_FILE="$ARGUMENTS"

Expand Down Expand Up @@ -45,13 +58,13 @@ Wait for confirmation before proceeding.

```bash
# Move to inherited (rename to avoid conflicts)
mkdir -p .claude/homunculus/instincts/inherited
mkdir -p "$HOMUNCULUS_DIR/instincts/inherited"

for f in "$TEMP_DIR/personal/"*.md; do
if [ -f "$f" ]; then
BASENAME=$(basename "$f")
# Add prefix to avoid conflicts
DEST=".claude/homunculus/instincts/inherited/imported-$BASENAME"
DEST="$HOMUNCULUS_DIR/instincts/inherited/imported-$BASENAME"
cp "$f" "$DEST"
fi
done
Expand All @@ -60,16 +73,16 @@ done
rm -rf "$TEMP_DIR"

# Count inherited
INHERITED=$(ls .claude/homunculus/instincts/inherited/ 2>/dev/null | wc -l | tr -d ' ')
INHERITED=$(ls "$HOMUNCULUS_DIR/instincts/inherited/" 2>/dev/null | wc -l | tr -d ' ')
echo "Imported. You now have $INHERITED inherited instincts."
```

## Update Identity

```bash
# Update counts
STATE=".claude/homunculus/identity.json"
INHERITED=$(ls .claude/homunculus/instincts/inherited/ 2>/dev/null | wc -l | tr -d ' ')
STATE="$HOMUNCULUS_DIR/identity.json"
INHERITED=$(ls "$HOMUNCULUS_DIR/instincts/inherited/" 2>/dev/null | wc -l | tr -d ' ')

jq --arg i "$INHERITED" '.instincts.inherited = ($i|tonumber)' "$STATE" > tmp.json && mv tmp.json "$STATE"
```
Expand Down
39 changes: 29 additions & 10 deletions plugins/homunculus/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,23 @@ description: Birth or wake your homunculus
Check if you already exist:

```bash
test -f .claude/homunculus/identity.json && cat .claude/homunculus/identity.json || echo "NOT_BORN"
# Resolve homunculus directory (treewalk)
_dir="$PWD"
HOMUNCULUS_DIR=""
while [ "$_dir" != "/" ]; do
if [ -f "$_dir/.claude/homunculus/identity.json" ]; then
HOMUNCULUS_DIR="$_dir/.claude/homunculus"
break
fi
_dir="$(dirname "$_dir")"
done
[ -z "$HOMUNCULUS_DIR" ] && [ -f "$HOME/.claude/homunculus/identity.json" ] && HOMUNCULUS_DIR="$HOME/.claude/homunculus"

if [ -n "$HOMUNCULUS_DIR" ]; then
cat "$HOMUNCULUS_DIR/identity.json"
else
echo "NOT_BORN"
fi
```

**If you see identity JSON:** You're waking up. Use the `session-memory` skill to recall context.
Expand Down Expand Up @@ -52,24 +68,27 @@ How should I be?
### Create yourself

```bash
# Default to project-local unless user specifies otherwise
HOMUNCULUS_DIR=".claude/homunculus"

# Core directories
mkdir -p .claude/homunculus
mkdir -p .claude/homunculus/sessions
mkdir -p "$HOMUNCULUS_DIR"
mkdir -p "$HOMUNCULUS_DIR/sessions"

# Instinct directories (no pending - auto-approved)
mkdir -p .claude/homunculus/instincts/personal
mkdir -p .claude/homunculus/instincts/inherited
mkdir -p "$HOMUNCULUS_DIR/instincts/personal"
mkdir -p "$HOMUNCULUS_DIR/instincts/inherited"

# Evolved capabilities
mkdir -p .claude/homunculus/evolved/agents
mkdir -p .claude/homunculus/evolved/skills
mkdir -p .claude/homunculus/evolved/commands
mkdir -p "$HOMUNCULUS_DIR/evolved/agents"
mkdir -p "$HOMUNCULUS_DIR/evolved/skills"
mkdir -p "$HOMUNCULUS_DIR/evolved/commands"

# Initialize observations log
touch .claude/homunculus/observations.jsonl
touch "$HOMUNCULUS_DIR/observations.jsonl"
```

Save `.claude/homunculus/identity.json`:
Save `$HOMUNCULUS_DIR/identity.json`:
```json
{
"version": "2.0.0",
Expand Down
Loading