colibri/scripts/import-layered-soul.sh
Sam & Claude a56c33c30d
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
fix(import): treat each SKILL.md as one skill (not every .md)
The layered-soul skills importer globbed skills/**/*.md, pulling references/ and
templates/ in as separate skills. Import each skills/**/SKILL.md instead
(frontmatter name/description, category 'soul'); supporting files are not skills.
Verified against the populated layered-soul: 9 skills imported, idempotent,
curated memory deferred. Doc updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:08:12 +02:00

72 lines
3.4 KiB
Bash
Executable file

#!/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: each skills/**/SKILL.md is one skill. Its references/ and
# templates/ are supporting material, not separate skills. find|while runs in a
# subshell, so keep the counters + summary together inside one { } group.
find "$SOUL/skills" -type f -name 'SKILL.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 skill directory name when there is no frontmatter name.
[ -n "$name" ] || name=$(basename "$(dirname "$skill_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="soul"
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