feat: import Layered Soul skills into Colibri (+ integration doc)
Wires clawdie/layered-soul (the portable identity/context source) into Colibri. scripts/import-layered-soul.sh reads its reviewed skills/**/*.md into the existing `skills` catalog (mirrors import-clawdie-skills.sh; idempotent, frontmatter name/description, category from the skill's parent dir). Honest scope: only skills are wired. The adapter's "Layered Memory Fabric" (system_brain / system_ops + a richer system_skills) is design-only (COLIBRI-SKILLS-PLAN.md), so curated memory is reported-but-not-imported and the gap is documented in docs/INTEGRATION-LAYERED-SOUL.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6a72befbb4
commit
252c282298
2 changed files with 119 additions and 0 deletions
43
docs/INTEGRATION-LAYERED-SOUL.md
Normal file
43
docs/INTEGRATION-LAYERED-SOUL.md
Normal file
|
|
@ -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 <layered-soul-dir> <colibri.sqlite>
|
||||
|
||||
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.
|
||||
76
scripts/import-layered-soul.sh
Executable file
76
scripts/import-layered-soul.sh
Executable file
|
|
@ -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 <layered-soul-dir> <colibri-sqlite-db>
|
||||
|
||||
set -eu
|
||||
|
||||
SOUL="${1:?usage: $0 <layered-soul-dir> <colibri-sqlite-db>}"
|
||||
DB="${2:?usage: $0 <layered-soul-dir> <colibri-sqlite-db>}"
|
||||
|
||||
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
|
||||
Loading…
Add table
Reference in a new issue