diff --git a/docs/INTEGRATION-LAYERED-SOUL.md b/docs/INTEGRATION-LAYERED-SOUL.md new file mode 100644 index 0000000..a70dc4f --- /dev/null +++ b/docs/INTEGRATION-LAYERED-SOUL.md @@ -0,0 +1,43 @@ +# Integration: Layered Soul → Colibri + +[clawdie/layered-soul](https://code.smilepowered.org/clawdie/layered-soul) is the +portable, harness-agnostic source of durable identity + reviewed context. This doc +records how Colibri consumes it **today**, and what is still **planned** — so the +contract isn't mistaken for fully-built. + +## What's wired (works now) + +Reviewed **skills** import into Colibri's existing `skills` catalog: + + scripts/import-layered-soul.sh + +It reads `skills/**/*.md` (frontmatter `name` / `description`; category derived +from the skill's parent dir), and `INSERT OR IGNORE`s rows into the `skills` +table. Same mechanism and table as `clawdie-iso/scripts/import-clawdie-skills.sh`; +idempotent, safe to re-run. + +## What's deferred (planned, not built) + +The `adapters/colibri.md` in layered-soul names a "Layered Memory Fabric" with +three stores — `system_skills`, `system_brain`, `system_ops`. As of 2026-06-13 +only a single flat `skills` table exists; the rest is **design only** +(`docs/COLIBRI-SKILLS-PLAN.md`), so the importer intentionally does not target it. + +| Layered Soul source | Target (planned) | Status | +| ---------------------------- | ---------------- | -------------------------------------------------------- | +| `skills/**/*.md` | `system_skills` | imported into the flat `skills` table today | +| `memories/curated/**/*.md` | `system_brain` | NOT imported — no store yet (the importer reports a count) | +| converted task/job manifests | `system_ops` | NOT implemented | + +## Direction (one-way) + +`layered-soul` (git) is the **source of truth**; Colibri is a **consumer**. Import +flows one way (repo → Colibri). Don't hand-edit the imported runtime copy and +expect it to flow back — curate in the repo, then re-import. + +## Closing the gap (future work) + +1. Implement `system_brain` per `COLIBRI-SKILLS-PLAN.md`, then extend the importer + to load `memories/curated/`. +2. Migrate the flat `skills` table to the planned `system_skills` schema. +3. Define and import `system_ops` task/job manifests. diff --git a/scripts/import-layered-soul.sh b/scripts/import-layered-soul.sh new file mode 100755 index 0000000..22caa1c --- /dev/null +++ b/scripts/import-layered-soul.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# Import a Layered Soul repo's reviewed skills into the Colibri skills catalog. +# +# Layered Soul (clawdie/layered-soul) is the portable, harness-agnostic source of +# durable identity + curated context. This wires its reviewed `skills/**/*.md` +# into Colibri's existing `skills` SQLite table. +# +# Scope note (see docs/INTEGRATION-LAYERED-SOUL.md): only skills are imported. +# Curated memory (`memories/curated/**/*.md`) is reported but NOT imported yet — +# Colibri has no `system_brain` store; `system_brain`/`system_ops` and the +# "Layered Memory Fabric" are planned (docs/COLIBRI-SKILLS-PLAN.md), not built. +# +# Usage: +# scripts/import-layered-soul.sh + +set -eu + +SOUL="${1:?usage: $0 }" +DB="${2:?usage: $0 }" + +command -v sqlite3 >/dev/null 2>&1 || { echo "error: sqlite3 not available" >&2; exit 1; } + +# Sanity: looks like a Layered Soul repo? +if [ ! -f "$SOUL/manifest.json" ] || [ ! -f "$SOUL/SOUL.md" ]; then + echo "error: $SOUL does not look like a layered-soul repo (no manifest.json / SOUL.md)" >&2 + exit 1 +fi + +sqlite3 "$DB" "CREATE TABLE IF NOT EXISTS skills ( + id TEXT PRIMARY KEY NOT NULL, + name TEXT NOT NULL UNIQUE, + description TEXT, + category TEXT, + created_at TEXT NOT NULL +);" 2>/dev/null || true + +NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ) + +# Reviewed skills: skills/**/*.md (skip READMEs). find|while runs in a subshell, +# so keep the counters + summary together inside one { } group. +find "$SOUL/skills" -type f -name '*.md' ! -iname 'README.md' 2>/dev/null | sort | { + imported=0 + skipped=0 + while IFS= read -r skill_md; do + [ -f "$skill_md" ] || continue + frontmatter=$(sed -n '/^---$/,/^---$/p' "$skill_md" | sed '1d;$d') + name=$(printf '%s\n' "$frontmatter" | grep -m1 '^name:' | sed 's/^name: *//' | tr -d '"') + # Fall back to the file's basename when there is no frontmatter name. + [ -n "$name" ] || name=$(basename "$skill_md" .md) + [ -n "$name" ] || { skipped=$((skipped + 1)); continue; } + description=$(printf '%s\n' "$frontmatter" | grep -m1 '^description:' | sed 's/^description: *//' | tr -d '"') + [ -n "$description" ] || description="Layered Soul skill: $name" + # Category = the skill's parent dir under skills/, else "soul". + rel=${skill_md#"$SOUL"/skills/} + case "$rel" in + */*) category=$(printf '%s' "$rel" | cut -d/ -f1) ;; + *) category="soul" ;; + esac + id=$(uuidgen 2>/dev/null || python3 -c 'import uuid;print(uuid.uuid4())' 2>/dev/null || echo "soul-skill-$name") + if sqlite3 "$DB" "INSERT OR IGNORE INTO skills (id, name, description, category, created_at) + VALUES ('$id', '$(printf '%s' "$name" | sed "s/'/''/g")', '$(printf '%s' "$description" | sed "s/'/''/g")', '$(printf '%s' "$category" | sed "s/'/''/g")', '$NOW');" 2>/dev/null; then + imported=$((imported + 1)) + else + skipped=$((skipped + 1)) + fi + done + echo " layered-soul skills: $imported imported, $skipped skipped" +} + +# Curated memory: reported only (no destination store yet). +mem_count=$(find "$SOUL/memories/curated" -type f -name '*.md' ! -iname 'README.md' 2>/dev/null | wc -l | tr -d ' ') +if [ "${mem_count:-0}" -gt 0 ]; then + echo " NOTE: $mem_count curated memory file(s) found — NOT imported." + echo " Colibri has no system_brain store yet (planned: docs/COLIBRI-SKILLS-PLAN.md);" + echo " see docs/INTEGRATION-LAYERED-SOUL.md." +fi