# Incubate

> [core] v26.5.16 L-SKLL | Clone or create repos for active development — the right hand of /learn. Workflow modes — default (long-term dev), --flash (issue → PR → offload), --contribute (fork + multi-PR). Use when user says "incubate [repo]", "work on [repo]", "clone for dev", or wants to set up a dev workflow. Do NOT trigger for study/exploration (use /learn), finding projects (use /trace), or session mining (use /dig).

- Skill: `l3rowniecakez/incubate` (Agent Skill)
- Install (CLI): `npx skillmds@latest add l3rowniecakez/incubate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/l3rowniecakez/incubate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: l3rowniecakez (https://skillmd.com/u/l3rowniecakez)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/l3rowniecakez/incubate

---


# /incubate — Active Development Workflow

Clone or create repos for active development → set up branches, make changes, push PRs.

> "/learn reads the book. /incubate writes the next chapter."

## Usage

```
/incubate [url]                          # Clone via ghq, symlink, ready for dev
/incubate [slug]                         # Use slug from ψ/memory/slugs.yaml
/incubate [repo-name]                    # Finds in ghq or creates with default org
/incubate [url] --flash "fix desc"       # Issue → branch → fix → PR → offload
/incubate [url] --contribute             # Fork if needed → branch per feature → PRs
/incubate --status                       # List all active ψ/incubate/ with git status
/incubate --status --include-offloaded   # Also list offloaded entries from .origins (#280)
/incubate --offload [slug]               # Remove symlink, keep ghq clone
/incubate --offload [slug] --purge       # Also drop entry from .origins manifest (#280)
/incubate --init                         # Restore all origins after git clone
```

---

## Workflow Modes

| Flag | Scope | Duration | Cleanup |
|------|-------|----------|---------|
| (default) | Long-term dev | Weeks/months | Manual offload |
| `--flash` | Single fix | Minutes | Issue → PR → auto-offload + purge |
| `--contribute` | Multi-feature | Days/weeks | Offload when all PRs done |
| `--status` | Query | — | Read-only listing |
| `--offload` | Cleanup | — | Remove symlink (keep ghq) |

```
incubate        → Long-term dev (manual cleanup)
    ↓
--contribute    → Push → offload (keep ghq)
    ↓
--flash         → Issue → Branch → PR → offload → purge (complete cycle)
```

---

## Directory Structure

```
ψ/incubate/
├── .origins                          # Manifest of incubated repos (committed)
└── OWNER/
    └── REPO/
        ├── origin                    # Symlink to ghq source (gitignored)
        └── REPO.md                   # Hub file — tracks incubation sessions (committed)
```

**Offload source, keep hub:**
```bash
unlink ψ/incubate/OWNER/REPO/origin   # Remove symlink
# ghq clone preserved for future use
# Hub file (REPO.md) remains in ψ/incubate/OWNER/REPO/
```

---

## /incubate --init

Restore all origins after cloning (like `git submodule init`):

```bash
ROOT="$(pwd)"
while read repo; do
  OWNER=$(dirname "$repo")
  REPO=$(basename "$repo")
  ghq get -u "https://github.com/$repo"
  mkdir -p "$ROOT/ψ/incubate/$OWNER/$REPO"
  ln -sf "$(ghq root)/github.com/$repo" "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
  echo "✓ Restored: $repo"
done < "$ROOT/ψ/incubate/.origins"
```

---

## Step 0: Detect Input Type + Resolve Path

**CRITICAL: Capture ABSOLUTE paths first:**
```bash
date "+🕐 %H:%M %Z (%A %d %B %Y)" && ROOT="$(pwd)"
echo "Incubating from: $ROOT"
```

### If URL (http* or owner/repo format)

Clone or create, symlink origin, update manifest:

```bash
# Replace [URL] with actual URL
URL="[URL]"
ROOT="$(pwd)"
OWNER=$(echo "$URL" | sed -E 's|.*github.com/([^/]+)/.*|\1|')
REPO=$(echo "$URL" | sed -E 's|.*/([^/]+)(\.git)?$|\1|')
SLUG="$OWNER/$REPO"

# Auto-stash unstaged changes in source clone before pulling (#279).
# `ghq get -u` runs `git pull` under the hood and aborts on dirty trees,
# stranding the ritual. Detect + stash with a clear log + restore hint.
GHQ_ROOT_PRECHECK=$(ghq root 2>/dev/null)
SOURCE_PRECHECK="$GHQ_ROOT_PRECHECK/github.com/$SLUG"
if [ -d "$SOURCE_PRECHECK/.git" ]; then
  if [ -n "$(git -C "$SOURCE_PRECHECK" status --porcelain 2>/dev/null)" ]; then
    STASH_NAME="pre-incubate-$(date +%Y-%m-%d)"
    echo "⚠️  Source clone has uncommitted changes — auto-stashing as '$STASH_NAME'"
    git -C "$SOURCE_PRECHECK" stash push -u -m "$STASH_NAME"
    echo "    (run \`git -C $SOURCE_PRECHECK stash pop\` to restore)"
  fi
fi

# Check if repo exists on GitHub
if gh repo view "$SLUG" --json name &>/dev/null; then
  ghq get -u "https://github.com/$SLUG"
else
  echo "Repo not found — creating private repo..."
  gh repo create "$SLUG" --private --clone=false
  ghq get "https://github.com/$SLUG"
  GHQ_ROOT=$(ghq root)
  LOCAL="$GHQ_ROOT/github.com/$SLUG"
  if [ ! -f "$LOCAL/README.md" ]; then
    echo "# $REPO" > "$LOCAL/README.md"
    git -C "$LOCAL" add README.md
    git -C "$LOCAL" commit -m "Initial commit"
    git -C "$LOCAL" push origin main 2>/dev/null || git -C "$LOCAL" push origin master
  fi
fi

GHQ_ROOT=$(ghq root)
mkdir -p "$ROOT/ψ/incubate/$OWNER/$REPO"
ln -sf "$GHQ_ROOT/github.com/$OWNER/$REPO" "$ROOT/ψ/incubate/$OWNER/$REPO/origin"

# Auto-add gitignore pattern if missing (#250)
GITIGNORE="$ROOT/.gitignore"
if [ -f "$GITIGNORE" ]; then
  if ! grep -q 'ψ/incubate/\*\*/origin' "$GITIGNORE" 2>/dev/null; then
    echo 'ψ/incubate/**/origin' >> "$GITIGNORE"
    echo "✓ Added ψ/incubate/**/origin to .gitignore"
  fi
else
  # Also check ψ/.gitignore as fallback
  PSI_GITIGNORE="$ROOT/ψ/.gitignore"
  if [ -f "$PSI_GITIGNORE" ] && ! grep -q 'incubate/\*\*/origin' "$PSI_GITIGNORE" 2>/dev/null; then
    echo 'incubate/**/origin' >> "$PSI_GITIGNORE"
    echo "✓ Added incubate/**/origin to ψ/.gitignore"
  fi
fi

# Update manifest
echo "$OWNER/$REPO" >> "$ROOT/ψ/incubate/.origins"
sort -u -o "$ROOT/ψ/incubate/.origins" "$ROOT/ψ/incubate/.origins"

echo "✓ Ready: $ROOT/ψ/incubate/$OWNER/$REPO/origin → source"
```

### Step 0.5: Drop INCUBATED_BY Breadcrumb (#226, #228)

After clone/symlink, write `.claude/INCUBATED_BY` in the **target repo** (not the oracle repo):

```bash
TARGET_REPO="$GHQ_ROOT/github.com/$OWNER/$REPO"
mkdir -p "$TARGET_REPO/.claude"

# Check if this repo was previously /learn'd
LEARNED_FROM=""
if [ -d "$ROOT/ψ/learn/$OWNER/$REPO" ]; then
  LEARNED_FROM="learned-from: ψ/learn/$OWNER/$REPO/"
fi

cat > "$TARGET_REPO/.claude/INCUBATED_BY" << BREADCRUMB
oracle: $(basename "$ROOT")
oracle-repo: $(git -C "$ROOT" remote get-url origin 2>/dev/null || echo "local")
date: $(date +%Y-%m-%d)
mode: ${MODE:-default}
source: https://github.com/$OWNER/$REPO
${LEARNED_FROM}
BREADCRUMB

echo "✓ Breadcrumb dropped: $TARGET_REPO/.claude/INCUBATED_BY"
```

The breadcrumb enables:
- **Orphan detection**: Any Claude session can check who tracks this repo
- **Provenance chain**: `learned-from` links /learn → /incubate (#232)
- **/recap awareness**: /recap shows a warning when INCUBATED_BY exists (#229)

### If just a name (no slash, no URL)

Try ghq first, then create with default org:

```bash
NAME="[NAME]"
ROOT="$(pwd)"
DEFAULT_ORG="laris-co"  # Configurable via --org flag

MATCH=$(ghq list | grep -i "/$NAME$" | head -1)
if [ -n "$MATCH" ]; then
  OWNER=$(echo "$MATCH" | cut -d'/' -f2)
  REPO=$(echo "$MATCH" | cut -d'/' -f3)
else
  OWNER="$DEFAULT_ORG"
  REPO="$NAME"
fi
# Then proceed with URL flow using OWNER/REPO
```

### Verify

```bash
ls -la "$ROOT/ψ/incubate/$OWNER/$REPO/"
```

---

## Step 1: Detect Workflow Mode

Check arguments for workflow flags:

| Argument | Mode | Action |
|----------|------|--------|
| (none) | Default | Clone + symlink + show status |
| `--flash` | Flash | Issue → branch → fix → PR → offload |
| `--contribute` | Contribute | Fork if needed → multi-feature PRs |
| `--status` | Status | List all incubations (skip clone) |
| `--offload` | Offload | Remove symlink (skip clone) |

**Calculate ACTUAL paths (replace variables with real values):**
```
REPO_DIR   = [ROOT]/ψ/incubate/[OWNER]/[REPO]/
SOURCE_DIR = [ROOT]/ψ/incubate/[OWNER]/[REPO]/origin/   ← symlink to ghq
WORK_DIR   = [GHQ_ROOT]/github.com/[OWNER]/[REPO]/      ← actual working directory
```

⚠️ IMPORTANT: Always use literal paths. Never pass shell variables to subagents.

---

## Mode: Default (long-term dev)

After Step 0 (clone + symlink), the repo is ready for development.

**Verify working state:**
```bash
WORK_DIR="$ROOT/ψ/incubate/$OWNER/$REPO/origin"
echo "Branch: $(git -C "$WORK_DIR" branch --show-current)"
echo "Status: $(git -C "$WORK_DIR" status --short | wc -l) changed files"
echo "Remote: $(git -C "$WORK_DIR" remote get-url origin)"
echo "Last commit: $(git -C "$WORK_DIR" log --oneline -1)"
```

**Skip to Step 2** (create/update hub file).

---

## Mode: --flash (single-fix cycle)

Complete contribution cycle: Issue → Branch → Fix → PR → Offload.

### Step F1: Create Issue (document intent)
```bash
WORK_DIR="$ROOT/ψ/incubate/$OWNER/$REPO/origin"
# Compose issue title and description from user's intent
ISSUE_URL=$(gh issue create --repo "$OWNER/$REPO" --title "[TITLE]" --body "[DESCRIPTION]")
ISSUE_NUM=$(echo "$ISSUE_URL" | grep -oP '\d+$')
echo "Created: #$ISSUE_NUM"
```

### Step F2: Create Branch
```bash
BRANCH="issue-${ISSUE_NUM}-[short-description]"
git -C "$WORK_DIR" checkout -b "$BRANCH"
echo "Branch: $BRANCH"
```

### Step F3: Make Changes
Let the user describe what to fix. Make changes, then:
```bash
git -C "$WORK_DIR" add -A
git -C "$WORK_DIR" commit -m "[commit message]

Closes #$ISSUE_NUM"
git -C "$WORK_DIR" push -u origin "$BRANCH"
```

### Step F4: Create PR
```bash
PR_URL=$(gh pr create --repo "$OWNER/$REPO" \
  --title "[PR title]" \
  --body "$(cat <<'EOF'
## Summary
[what was fixed]

Closes #$ISSUE_NUM

---
**From**: [Oracle Name]
Rule 6: "Oracle Never Pretends to Be Human"
Written by an Oracle — AI speaking as itself.
EOF
)" --head "$BRANCH")
PR_NUM=$(echo "$PR_URL" | grep -oP '\d+$')
echo "PR: #$PR_NUM (closes #$ISSUE_NUM)"
```

### Step F5: Auto-offload + purge
```bash
cd "$ROOT"
unlink "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
rmdir "$ROOT/ψ/incubate/$OWNER" 2>/dev/null
rm -rf "$(ghq root)/github.com/$OWNER/$REPO"
echo "✓ Issue #$ISSUE_NUM → PR #$PR_NUM → Offloaded & Purged"
```

**Update hub file before offload** (Step 2), then offload.

---

## Mode: --contribute (multi-feature contribution)

For extended contribution over days/weeks. Forks if needed.

### Step C1: Fork if not your repo
```bash
WORK_DIR="$ROOT/ψ/incubate/$OWNER/$REPO/origin"
ME=$(gh api user --jq '.login')
if ! gh repo view "$OWNER/$REPO" --json viewerPermission --jq '.viewerPermission' | grep -qE 'ADMIN|MAINTAIN|WRITE'; then
  echo "No push access — forking..."
  gh repo fork "$OWNER/$REPO" --clone=false
  git -C "$WORK_DIR" remote add fork "https://github.com/$ME/$REPO.git"
  echo "Fork remote added. Push to 'fork' instead of 'origin'."
fi
```

### Step C2: Create feature branch
```bash
BRANCH="feat/[feature-name]"
git -C "$WORK_DIR" checkout -b "$BRANCH"
```

### Step C3: Work cycle (repeat per feature)
```bash
# ... make changes ...
git -C "$WORK_DIR" add -A
git -C "$WORK_DIR" commit -m "[commit message]"
REMOTE=$(git -C "$WORK_DIR" remote | grep fork || echo origin)
git -C "$WORK_DIR" push -u "$REMOTE" "$BRANCH"
gh pr create --repo "$OWNER/$REPO" \
  --title "[PR title]" \
  --body "[description]" \
  --head "$ME:$BRANCH"
```

### Step C4: Offload when all PRs done
```bash
unlink "$ROOT/ψ/incubate/$OWNER/$REPO/origin"
rmdir "$ROOT/ψ/incubate/$OWNER" 2>/dev/null
echo "✓ Offloaded (ghq kept for PR feedback)"
```

---

## Mode: --status (list incubations)

No clone needed. List all active incubations with git status.

Add `--include-offloaded` to also list entries in `.origins` whose symlinks
have been removed (#280) — surfaces the historical record without losing it.

```bash
ROOT="$(pwd)"
INCLUDE_OFFLOADED="${1:-}"   # pass "--include-offloaded" to enable
echo "🌱 Active Incubations"
echo ""

# Collect active slugs (those with live symlinks)
declare -A ACTIVE_SLUGS=()
for link in $(find "$ROOT/ψ/incubate" -name "origin" -type l 2>/dev/null); do
  REPO_DIR=$(dirname "$link")
  SLUG=$(echo "$REPO_DIR" | sed "s|$ROOT/ψ/incubate/||")
  ACTIVE_SLUGS["$SLUG"]=1
  TARGET=$(readlink "$link")
  if [ -d "$TARGET" ]; then
    BRANCH=$(git -C "$TARGET" branch --show-current 2>/dev/null)
    CHANGES=$(git -C "$TARGET" status --short 2>/dev/null | wc -l)
    echo "  $SLUG"
    echo "    Branch: $BRANCH | Changes: $CHANGES"
    echo "    Path:   $TARGET"
  else
    echo "  $SLUG (broken symlink → $TARGET)"
  fi
  echo ""
done

ACTIVE_COUNT=${#ACTIVE_SLUGS[@]}

# --include-offloaded: also list .origins entries with no live symlink (#280)
if [ "$INCLUDE_OFFLOADED" = "--include-offloaded" ] && [ -f "$ROOT/ψ/incubate/.origins" ]; then
  echo ""
  echo "📦 Offloaded (in .origins, no live symlink)"
  echo ""
  OFFLOADED_COUNT=0
  while IFS= read -r slug; do
    [ -z "$slug" ] && continue
    if [ -z "${ACTIVE_SLUGS[$slug]:-}" ]; then
      GHQ_ROOT=$(ghq root 2>/dev/null)
      GHQ_PATH="$GHQ_ROOT/github.com/$slug"
      if [ -d "$GHQ_PATH" ]; then
        echo "  $slug (ghq preserved at $GHQ_PATH)"
      else
        echo "  $slug (ghq absent — fully purged)"
      fi
      OFFLOADED_COUNT=$((OFFLOADED_COUNT + 1))
    fi
  done < "$ROOT/ψ/incubate/.origins"
  echo ""
  echo "Active: $ACTIVE_COUNT | Offloaded: $OFFLOADED_COUNT"
else
  echo "Total: $ACTIVE_COUNT active incubation(s)"
  if [ -f "$ROOT/ψ/incubate/.origins" ]; then
    TOTAL_RECORDED=$(grep -cv '^$' "$ROOT/ψ/incubate/.origins" 2>/dev/null || echo 0)
    OFFLOADED_HIDDEN=$((TOTAL_RECORDED - ACTIVE_COUNT))
    if [ "$OFFLOADED_HIDDEN" -gt 0 ]; then
      echo "  ($OFFLOADED_HIDDEN offloaded — use --include-offloaded to view)"
    fi
  fi
fi
```

**Done.** No hub file update needed.

---

## Mode: --offload (cleanup)

Remove symlink, keep ghq clone and hub file.

Add `--purge` to also remove the entry from `.origins` manifest (#280) —
useful when you want the offloaded slug to NOT count toward "Total" any more.

```bash
ROOT="$(pwd)"
SLUG="[OWNER/REPO or REPO]"
PURGE="${1:-}"   # pass "--purge" to also remove from .origins

# Find the symlink
LINK=$(find "$ROOT/ψ/incubate" -name "origin" -type l | xargs -I{} dirname {} | grep -i "$SLUG" | head -1)
if [ -z "$LINK" ]; then
  echo "Not found: $SLUG"
  echo "Active incubations:"
  find "$ROOT/ψ/incubate" -name "origin" -type l | xargs -I{} dirname {} | sed "s|$ROOT/ψ/incubate/||"
  exit 1
fi

REPO_NAME=$(basename "$LINK")
unlink "$LINK/origin"
OWNER_DIR=$(dirname "$LINK")
rmdir "$OWNER_DIR" 2>/dev/null

DEFLATED_SLUG=$(echo $LINK | sed "s|$ROOT/ψ/incubate/||")
echo "✓ Offloaded: $DEFLATED_SLUG"
echo "  Hub file remains: $LINK/$REPO_NAME.md"
echo "  ghq clone preserved for future use"

# --purge: remove from .origins manifest (#280)
if [ "$PURGE" = "--purge" ] && [ -f "$ROOT/ψ/incubate/.origins" ]; then
  TMPFILE=$(mktemp)
  grep -v "^${DEFLATED_SLUG}$" "$ROOT/ψ/incubate/.origins" > "$TMPFILE" || true
  mv "$TMPFILE" "$ROOT/ψ/incubate/.origins"
  echo "  ✂ Purged '$DEFLATED_SLUG' from .origins manifest"
  echo "  ⚠ This breadcrumb is gone — re-incubate to restore it (Principle 1: prefer --include-offloaded over --purge)"
fi
```

---

## Step 2: Create/Update Hub File (REPO.md)

After any mode except --status, create or update the hub file:

```markdown
# [REPO] Incubation Log

## Source
- **Origin**: ./origin/
- **GitHub**: https://github.com/OWNER/REPO

## Sessions

### [TODAY] — [mode]
- **Branch**: [branch-name]
- **Status**: active | offloaded | flash-completed
- **Changes**: [summary of what was done]
- **PRs**: #N, #M (if any)
```

Append new sessions. Never overwrite existing entries (Nothing is Deleted).

---

## Output Summary

### Default mode

announce-mode → bash substitutes $ROOT/$OWNER/$REPO/$WORK_DIR to absolute paths;
never print "Location: ψ/..." literally. See CONVENTIONS.md.

```bash
echo "🌱 Incubating: $REPO"
echo ""
echo "  Mode:     default (long-term dev)"
echo "  Location: $ROOT/ψ/incubate/$OWNER/$REPO/"
echo "  Working:  $WORK_DIR"
echo "  Branch:   $(git -C "$WORK_DIR" branch --show-current)"
echo "  Remote:   $(git -C "$WORK_DIR" remote get-url origin)"
echo "  Status:   $(git -C "$WORK_DIR" status --short | wc -l) changed files"
echo ""
echo "  Next: make changes, commit, push, create PR"
echo "  Done: /incubate --offload $OWNER/$REPO"
```

### --flash mode
```
⚡ Flash Complete: [REPO]

  Issue:  #N created
  Branch: issue-N-description
  PR:     #M (closes #N)
  Result: Offloaded & Purged

  ✓ Issue #N → PR #M → Done
```

### --contribute mode

announce-mode → bash substitutes; never print "Location: ψ/..." literally.

```bash
echo "🤝 Contributing: $REPO"
echo ""
echo "  Location: $ROOT/ψ/incubate/$OWNER/$REPO/"
echo "  Fork:     ${FORK_URL:-[none]}"
echo "  Branches: ${BRANCHES:-[list]}"
echo "  PRs:      ${PRS:-[list]}"
echo ""
echo "  Next: continue working, or /incubate --offload when done"
```

### --status mode

The actual emission lives in the bash block under `Mode: --status` above —
`echo "    Path:   $TARGET"` resolves `$TARGET` via `readlink`, so paths are
absolute. Example rendering:

```
🌱 Active Incubations

  OWNER/REPO
    Branch: feat/x | Changes: 3
    Path:   /Users/nat/ghq/github.com/OWNER/REPO

  Total: N active incubation(s)
```

---

## .gitignore Pattern

The pattern is auto-added to `.gitignore` on first `/incubate` run (#250). If you need to add it manually:

```gitignore
# Ignore origin symlinks only (source lives in ghq)
# Note: no trailing slash — origin is a symlink, not a directory
ψ/incubate/**/origin
```

---

## Trace Connection

After incubation work, log to Oracle so it's discoverable via `/trace`:

### Save the lesson (two-layer pattern)

1. Write to `ψ/memory/learnings/YYYY-MM-DD_incubate-<slug>.md` with frontmatter:
   ```yaml
   ---
   pattern: "Incubated [REPO]: [what was done — PR#, branch, outcome]"
   date: <today>
   source: incubate: OWNER/REPO
   concepts: ["incubate", "development", <relevant-tags>]
   ---

   # Incubated [REPO]
   <body: what was done, PR#, branch, outcome>
   ```

2. The Oracle's auto-memory layer picks up new files in `ψ/memory/learnings/` automatically — no separate API call needed.

This connects `/incubate` to the shared knowledge layer.

---

## Anti-Patterns

| Wrong | Right |
|-------|-------|
| `git clone` directly to ψ/ | `ghq get` then symlink |
| Flat: `ψ/incubate/repo-name` | Org structure: `ψ/incubate/owner/repo` |
| Copy files | Symlink always |
| Manual clone outside ghq | Everything through ghq |
| Delete ghq clone after work | Offload symlink only (Nothing is Deleted) |

---

## Notes

- Default: clone + symlink, ready for long-term development
- `--flash`: complete cycle (issue → branch → PR → offload + purge) for quick fixes
- `--contribute`: fork-aware multi-feature workflow for external repos
- `--status`: query all active incubations without cloning
- `--offload`: remove symlink, keep ghq and hub file
- Auto-creates private repos when target doesn't exist on GitHub
- `origin/` symlink structure allows easy offload without losing ghq clone
- `.origins` manifest enables `--init` restore after fresh git clone
- Mirror of `/learn`: learn = LEFT hand (study), incubate = RIGHT hand (work)

---

ARGUMENTS: $ARGUMENTS

