Skill: fahrulalwan/metaskill (version 1.3.0)

--- name: metaskill description: "Teaches AI agents how to learn better by enforcing deep correction, transfer learning, and proactive pattern recognition. Use when an error occurs and needs deep analysis (not surface patch), before starting a complex task to check past analogies, or after success to capture winning patterns. NOT for routine file reads or simple one-off commands." --- # Metaskill ## 3 Core Components 1. **Deep Self-Correction (`deep-correct.sh`)** — 3-level breakdown on errors: - **Surface**: What specifically failed - **Principle**: The underlying rule/constraint violated - **Habit**: Concrete behavioral change to prevent recurrence 2. **Transfer Learning (`transfer-check.sh`)** — Before a task, search past learnings for analogous patterns. Maps domains (e.g., "auth" → "security") to prevent siloed learning. 3. **Proactive Pattern Recognition (`success-capture.sh`)** — Log what worked and why, building a repository of successful patterns. ## Usage ```bash # When an error occurs bash skills/metaskill/scripts/deep-correct.sh "description of the error" # Before starting a complex task bash skills/metaskill/scripts/transfer-check.sh "description of the new task" # After successful execution bash skills/metaskill/scripts/success-capture.sh "what worked" "why it worked" # Monthly health eval bash skills/metaskill/scripts/eval.sh --save ``` ## Configuration (LLM Provider) Metaskill uses two provider tiers — **fast** (extraction) and **deep** (transfer/eval). Edit `config.yaml` to match your setup: ```yaml # config.yaml providers: fast: anthropic # change to: openai | ollama | gemini deep: anthropic ``` | Provider | Env Var | Notes | |---|---|---| | `anthropic` | `ANTHROPIC_API_KEY` | Default | | `openai` | `OPENAI_API_KEY` | | | `ollama` | *(none needed)* | Local, free | | `gemini` | `GOOGLE_API_KEY` | | **Ollama example** (fully local, no API key): ```yaml providers: fast: ollama deep: ollama models: ollama: fast: llama3.2 deep: llama3.1:70b ``` If no provider is available, metaskill falls back to manual/heuristic mode (still works, but less precise extraction). ## Integration with Self-Improving-Agent Writes to `skills/self-improving-agent/.learnings/` if present, otherwise falls back to its own `.learnings/` directory. No extra setup needed. ## AGENTS.md Wiring (Mandatory) Add to pre-task checklist: 1. Run `transfer-check.sh` before any major task 2. Run `deep-correct.sh` immediately after any error (not just LEARNINGS.md append) 3. Run `success-capture.sh` after complex task completes successfully

path: config.yaml
content: # Metaskill LLM Provider Configuration
# Edit this file to match your setup. No code changes needed.

providers:
  # "fast" provider: quick extraction tasks (haiku-level speed/cost)
  fast: anthropic
  # "deep" provider: transfer learning & semantic eval (sonnet-level)
  deep: anthropic

# Model per provider × tier
models:
  anthropic:
    fast: claude-haiku-4-5
    deep: claude-sonnet-4-6
  openai:
    fast: gpt-4o-mini
    deep: gpt-4o
  ollama:
    fast: llama3.2
    deep: llama3.1:70b
  gemini:
    fast: gemini-2.5-flash-lite
    deep: gemini-2.5-flash

# Env var name for each provider's API key ("" = no key needed)
env_vars:
  anthropic: ANTHROPIC_API_KEY
  openai: OPENAI_API_KEY
  ollama: ""
  gemini: GOOGLE_API_KEY


path: scripts/deep-correct.sh
content: #!/bin/bash
# deep-correct.sh
# Guided 3-level correction flow

if [ -z "$1" ]; then
  echo "Usage: $0 <error_description> [principle] [habit]"
  exit 1
fi

ERROR_DESC="$1"

# Dynamic workspace detection — works for any OpenClaw user
WORKSPACE=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null)
if [ -z "$WORKSPACE" ]; then
  WORKSPACE="${OPENCLAW_WORKSPACE:-$HOME/.openclaw/workspace}"
fi

# Try self-improving-agent first, fallback to metaskill's own learnings
if [ -f "$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md" ]; then
  LEARNINGS_FILE="$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md"
else
  # Fallback: use metaskill's own learnings dir
  mkdir -p "$WORKSPACE/skills/metaskill/.learnings"
  LEARNINGS_FILE="$WORKSPACE/skills/metaskill/.learnings/LEARNINGS.md"
  # Create file if not exists
  [ -f "$LEARNINGS_FILE" ] || echo -e "# Learnings Log\n\n---\n" > "$LEARNINGS_FILE"
fi

echo "=== Deep Correction Flow ==="
echo "Surface Error: $ERROR_DESC"

SCRIPT_DIR=$(dirname "$0")

if [ -n "$2" ] && [ -n "$3" ]; then
  PRINCIPLE="$2"
  HABIT="$3"
else
  # Try LLM extraction (v1.1)
  echo "[LLM mode] Attempting extraction..."
  LLM_RESULT=$(python3 "$SCRIPT_DIR/llm_extract.py" "$ERROR_DESC" 2>/dev/null)
  
  if [ "$LLM_RESULT" = "FALLBACK" ] || [ -z "$LLM_RESULT" ]; then
    echo "[offline mode] LLM extraction unavailable or failed. Falling back to manual input."
    echo "Enter Principle (what underlying rule was violated): "
    read -r PRINCIPLE
    echo "Enter Habit (what behavioral change prevents recurrence): "
    read -r HABIT
  else
    # Parse JSON
    ERROR_DESC=$(echo "$LLM_RESULT" | python3 -c "import sys, json; print(json.load(sys.stdin).get(\"surface\", \"\"))")
    PRINCIPLE=$(echo "$LLM_RESULT" | python3 -c "import sys, json; print(json.load(sys.stdin).get(\"principle\", \"\"))")
    HABIT=$(echo "$LLM_RESULT" | python3 -c "import sys, json; print(json.load(sys.stdin).get(\"habit\", \"\"))")
    
    echo ""
    echo "LLM Extracted Levels:"
    echo "Surface: $ERROR_DESC"
    echo "Principle: $PRINCIPLE"
    echo "Habit: $HABIT"
    echo ""
    echo "Press Enter to confirm and save, or Ctrl+C to abort."
    read -r _
  fi
fi

DATE=$(date +%Y-%m-%d)

cat <<INNEREOF >> "$LEARNINGS_FILE"

## [$DATE] Deep Correction
**Surface Error:** $ERROR_DESC
**Principle:** $PRINCIPLE
**Habit:** $HABIT

INNEREOF

echo "✅ Saved to $LEARNINGS_FILE"
echo "Summary:"
echo "- Surface: $ERROR_DESC"
echo "- Principle: $PRINCIPLE"
echo "- Habit: $HABIT"


path: scripts/eval.sh
content: #!/bin/bash
# metaskill/eval.sh — Evaluate how well the metaskill system is working
# Usage: ./eval.sh [--report] [--save]
# Output: terminal summary + optional save to memory/metaskill-eval-YYYY-MM-DD.md

# Dynamic workspace detection — works for any OpenClaw user
WORKSPACE=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null)
if [ -z "$WORKSPACE" ]; then
  WORKSPACE="${OPENCLAW_WORKSPACE:-$HOME/.openclaw/workspace}"
fi

# Try self-improving-agent first, fallback to metaskill's own learnings
if [ -f "$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md" ]; then
  LEARNINGS="$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md"
else
  # Fallback: use metaskill's own learnings dir
  mkdir -p "$WORKSPACE/skills/metaskill/.learnings"
  LEARNINGS="$WORKSPACE/skills/metaskill/.learnings/LEARNINGS.md"
  # Create file if not exists
  [ -f "$LEARNINGS" ] || echo -e "# Learnings Log\n\n---\n" > "$LEARNINGS"
fi

if [ -f "$WORKSPACE/skills/self-improving-agent/.learnings/ERRORS.md" ]; then
  ERRORS_FILE="$WORKSPACE/skills/self-improving-agent/.learnings/ERRORS.md"
else
  mkdir -p "$WORKSPACE/skills/metaskill/.learnings"
  ERRORS_FILE="$WORKSPACE/skills/metaskill/.learnings/ERRORS.md"
  [ -f "$ERRORS_FILE" ] || echo -e "# Errors Log\n\n---\n" > "$ERRORS_FILE"
fi

WINS="$WORKSPACE/skills/metaskill/.learnings/WINS.md"
EVAL_DIR="$WORKSPACE/memory/metaskill-evals"
DATE=$(date +%Y-%m-%d)
SAVE=false
[[ "$*" == *--save* ]] && SAVE=true

mkdir -p "$EVAL_DIR"

echo ""
echo "🧠 METASKILL EVAL — $DATE"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

echo ""
echo "⚙️  SYSTEM STATUS"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROVIDER_STATUS=$(python3 -c "
import sys; sys.path.insert(0, '$SCRIPT_DIR')
from llm_provider import get_status
import json; print(json.dumps(get_status()))
" 2>/dev/null)

if [ -n "$PROVIDER_STATUS" ]; then
  FAST_PROVIDER=$(echo "$PROVIDER_STATUS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('fast',{}).get('provider','?'))")
  FAST_MODEL=$(echo "$PROVIDER_STATUS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('fast',{}).get('model','?'))")
  FAST_READY=$(echo "$PROVIDER_STATUS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('fast',{}).get('ready',False))")
  DEEP_PROVIDER=$(echo "$PROVIDER_STATUS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('deep',{}).get('provider','?'))")
  DEEP_MODEL=$(echo "$PROVIDER_STATUS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('deep',{}).get('model','?'))")
  DEEP_READY=$(echo "$PROVIDER_STATUS" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('deep',{}).get('ready',False))")

  if [ "$FAST_READY" = "True" ] || [ "$DEEP_READY" = "True" ]; then
    echo "   Version: v1.2 (LLM-powered extraction active)"
    echo "   Fast provider : $FAST_PROVIDER / $FAST_MODEL $([ "$FAST_READY" = "True" ] && echo "✅" || echo "❌ missing key")"
    echo "   Deep provider : $DEEP_PROVIDER / $DEEP_MODEL $([ "$DEEP_READY" = "True" ] && echo "✅" || echo "❌ missing key")"
  else
    echo "   Version: v1.0 fallback (Manual extraction mode)"
    echo "   No LLM provider ready. Edit skills/metaskill/config.yaml and set the required env var."
    echo "   Fast: $FAST_PROVIDER → needs \$$(python3 -c "import sys; sys.path.insert(0,'$SCRIPT_DIR'); from llm_provider import _load_config; c=_load_config(); print(c['env_vars'].get(c['providers']['fast'],'?'))" 2>/dev/null)"
  fi
else
  echo "   Version: v1.0 fallback (llm_provider.py unavailable)"
fi

# ── GAP 1: Self-correction depth ────────────────────────────────────────────
echo ""
echo "📊 GAP 1: Self-Correction Depth"
echo "   (Are we extracting 3 levels, or just surface?)"
echo ""

# Count total learnings (## headers)
TOTAL_LEARNINGS=$(grep -c "^## " "$LEARNINGS" 2>/dev/null); TOTAL_LEARNINGS=${TOTAL_LEARNINGS:-0}

# Count deep corrections (entries that have Surface + Principle + Habit keywords)
DEEP=$(grep -c -i "habit:\|habit —\|\*\*habit\*\*" "$LEARNINGS" 2>/dev/null); DEEP=${DEEP:-0}
SURFACE_ONLY=$((TOTAL_LEARNINGS - DEEP))

if [ "$TOTAL_LEARNINGS" -gt 0 ]; then
  DEPTH_PCT=$((DEEP * 100 / TOTAL_LEARNINGS))
else
  DEPTH_PCT=0
fi

echo "   Total learnings:    $TOTAL_LEARNINGS"
echo "   3-level (deep):     $DEEP ($DEPTH_PCT%)"
echo "   Surface-only:       $SURFACE_ONLY"

if [ "$DEPTH_PCT" -lt 20 ]; then
  DEPTH_GRADE="🔴 Critical — most corrections are surface-only"
elif [ "$DEPTH_PCT" -lt 50 ]; then
  DEPTH_GRADE="🟡 Developing — some depth, but inconsistent"
elif [ "$DEPTH_PCT" -lt 80 ]; then
  DEPTH_GRADE="🟢 Good — majority are deep corrections"
else
  DEPTH_GRADE="✅ Excellent"
fi
echo "   Grade: $DEPTH_GRADE"

# Identify repeat offenders (same category/keyword appearing 3+ times)
echo ""
echo "   ⚠️  Repeat error patterns (category appearing 3x+):"
grep -i "category:\|**category**" "$LEARNINGS" 2>/dev/null | \
  sed 's/.*category.*: *//i' | tr '[:upper:]' '[:lower:]' | tr -d '**' | \
  sort | uniq -c | sort -rn | \
  awk '$1 >= 3 {printf "      %dx: %s\n", $1, $2}' | head -5
echo ""

# ── GAP 2: Transfer learning ─────────────────────────────────────────────────
echo "📊 GAP 2: Transfer Learning"
echo "   (Are principles crossing domains?)"
echo ""

# Heuristic: count learnings with cross-domain language ("also applies", "same pattern", "analogous")
TRANSFERS=$(grep -ci "also applies\|same pattern\|analogous\|similar to\|cross-domain\|generalize\|applies to all\|pattern:" "$LEARNINGS" 2>/dev/null); TRANSFERS=${TRANSFERS:-0}

# Check if transfer-check.sh has been used (look for invocations in daily notes)
TRANSFER_USES=$(grep -rl "transfer-check" "$WORKSPACE/memory/" 2>/dev/null | wc -l | tr -d ' '); TRANSFER_USES=${TRANSFER_USES:-0}

echo "   Cross-domain principles:  $TRANSFERS"
echo "   transfer-check.sh uses:   $TRANSFER_USES (in daily notes)"

if [ "$TRANSFERS" -lt 3 ]; then
  TRANSFER_GRADE="🔴 Critical — learnings are siloed, not transferring"
elif [ "$TRANSFERS" -lt 8 ]; then
  TRANSFER_GRADE="🟡 Developing — some transfer, mostly accidental"
else
  TRANSFER_GRADE="🟢 Good — principles are actively crossing domains"
fi
echo "   Grade: $TRANSFER_GRADE"
echo ""

# ── GAP 3: Proactive pattern recognition (win/error ratio) ───────────────────
echo "📊 GAP 3: Proactive Pattern Recognition"
echo "   (Win:Error ratio — are we learning from what works?)"
echo ""

WINS_COUNT=$(grep -c "^## " "$WINS" 2>/dev/null); WINS_COUNT=${WINS_COUNT:-0}
ERRORS_COUNT=$(grep -c "^## " "$ERRORS_FILE" 2>/dev/null); ERRORS_COUNT=${ERRORS_COUNT:-0}

echo "   Wins logged:    $WINS_COUNT"
echo "   Errors logged:  $ERRORS_COUNT"

if [ "$((WINS_COUNT + ERRORS_COUNT))" -gt 0 ]; then
  WIN_PCT=$((WINS_COUNT * 100 / (WINS_COUNT + ERRORS_COUNT)))
else
  WIN_PCT=0
fi
echo "   Win ratio:      $WIN_PCT%  (target: ≥40%)"

if [ "$WIN_PCT" -lt 10 ]; then
  WIN_GRADE="🔴 Critical — almost entirely reactive, no wins captured"
elif [ "$WIN_PCT" -lt 30 ]; then
  WIN_GRADE="🟡 Developing — starting to capture wins"
elif [ "$WIN_PCT" -lt 50 ]; then
  WIN_GRADE="🟢 Good — balanced learning"
else
  WIN_GRADE="✅ Excellent — proactive learner"
fi
echo "   Grade: $WIN_GRADE"
echo ""

# ── OVERALL SCORE ─────────────────────────────────────────────────────────────
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📈 OVERALL METASKILL SCORE"
echo ""

SCORE=$((DEPTH_PCT / 3 + TRANSFERS * 5 + WIN_PCT / 3))
# cap at 100
[ $SCORE -gt 100 ] && SCORE=100

echo "   Score: $SCORE / 100"

if [ $SCORE -lt 25 ]; then
  echo "   Status: 🔴 Early stage — foundational work needed"
elif [ $SCORE -lt 50 ]; then
  echo "   Status: 🟡 Developing — patterns emerging but inconsistent"
elif [ $SCORE -lt 75 ]; then
  echo "   Status: 🟢 Functional — metaskill working, room to grow"
else
  echo "   Status: ✅ Advanced — strong learning system"
fi

# ── RECOMMENDATIONS ───────────────────────────────────────────────────────────
echo ""
echo "💡 TOP RECOMMENDATIONS:"
echo ""

REC_COUNT=0

if [ "$DEPTH_PCT" -lt 50 ]; then
  echo "   1. Run deep-correct.sh on the last 5 surface-only learnings"
  echo "      → Convert them to 3-level (surface/principle/habit)"
  REC_COUNT=$((REC_COUNT+1))
fi

if [ "$TRANSFER_USES" -lt 3 ]; then
  echo "   2. Wire transfer-check.sh into pre-task routine"
  echo "      → Add to AGENTS.md: before any non-trivial task, run it"
  REC_COUNT=$((REC_COUNT+1))
fi

if [ "$WIN_PCT" -lt 20 ]; then
  echo "   3. Log at least 1 win this week via success-capture.sh"
  echo "      → Start small: what worked today?"
  REC_COUNT=$((REC_COUNT+1))
fi

if [ $SCORE -gt 60 ]; then
  echo "   ✅ System healthy — keep the cadence"
fi

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# ── SAVE TO FILE ──────────────────────────────────────────────────────────────
if [ "$SAVE" = true ]; then
  OUTFILE="$EVAL_DIR/$DATE.md"
  cat > "$OUTFILE" << INEOF
# Metaskill Eval — $DATE

## Scores
| Gap | Metric | Grade |
|-----|--------|-------|
| Self-correction depth | $DEPTH_PCT% deep (${DEEP}/${TOTAL_LEARNINGS}) | $DEPTH_GRADE |
| Transfer learning | $TRANSFERS cross-domain principles | $TRANSFER_GRADE |
| Proactive recognition | $WIN_PCT% win ratio (${WINS_COUNT} wins / ${ERRORS_COUNT} errors) | $WIN_GRADE |

**Overall: $SCORE/100**

## Recommendations
$([ "$DEPTH_PCT" -lt 50 ] && echo "- Run deep-correct.sh on last 5 surface-only learnings")
$([ "$TRANSFER_USES" -lt 3 ] && echo "- Wire transfer-check.sh into pre-task routine in AGENTS.md")
$([ "$WIN_PCT" -lt 20 ] && echo "- Log at least 1 win this week via success-capture.sh")

## Raw counts
- Total learnings: $TOTAL_LEARNINGS
- Deep (3-level): $DEEP
- Surface-only: $SURFACE_ONLY
- Wins: $WINS_COUNT
- Errors: $ERRORS_COUNT
- Transfer uses: $TRANSFER_USES
INEOF
  echo "   Saved → $OUTFILE"
fi


path: scripts/llm_extract.py
content: # scripts/llm_extract.py
import sys, json, os
sys.path.insert(0, os.path.dirname(__file__))
from llm_provider import call_llm

def extract_levels(error_desc):
    prompt = f"""Extract 3 correction levels from this error/mistake:

"{error_desc}"

Return ONLY valid JSON:
{{"surface": "what specifically went wrong in this instance", "principle": "the underlying rule or mental model that was violated", "habit": "the concrete behavioral change that prevents recurrence"}}"""

    result = call_llm(prompt, provider_type="fast", max_tokens=300)
    if result:
        try:
            return json.loads(result)
        except json.JSONDecodeError:
            pass
    return None

if __name__ == "__main__":
    error = " ".join(sys.argv[1:])
    result = extract_levels(error)
    if result:
        print(json.dumps(result))
    else:
        print("FALLBACK")


path: scripts/llm_provider.py
content: """
llm_provider.py — Abstract LLM provider for metaskill.
Reads config.yaml, routes to the right provider, falls back gracefully.
"""
import os, sys
from pathlib import Path

def _load_config():
    config_path = Path(__file__).parent.parent / "config.yaml"
    try:
        import yaml
        with open(config_path) as f:
            return yaml.safe_load(f)
    except ImportError:
        # yaml not available — parse manually (simple key: value)
        cfg = {"providers": {"fast": "anthropic", "deep": "anthropic"},
               "models": {"anthropic": {"fast": "claude-haiku-4-5", "deep": "claude-sonnet-4-6"},
                          "openai": {"fast": "gpt-4o-mini", "deep": "gpt-4o"},
                          "ollama": {"fast": "llama3.2", "deep": "llama3.1:70b"},
                          "gemini": {"fast": "gemini-2.5-flash-lite", "deep": "gemini-2.5-flash"}},
               "env_vars": {"anthropic": "ANTHROPIC_API_KEY", "openai": "OPENAI_API_KEY",
                            "ollama": "", "gemini": "GOOGLE_API_KEY"}}
        try:
            with open(config_path) as f:
                for line in f:
                    line = line.strip()
                    if line.startswith("fast:") and "providers" in str(cfg):
                        pass  # simplified fallback — defaults are fine
        except Exception:
            pass
        return cfg
    except Exception:
        return None

def call_llm(prompt: str, provider_type: str = "fast", max_tokens: int = 500) -> str | None:
    """
    Call LLM using configured provider.
    provider_type: "fast" (haiku-level) or "deep" (sonnet-level)
    Returns text string or None on failure.
    """
    cfg = _load_config()
    if not cfg:
        return _fallback(prompt, max_tokens)

    provider = cfg.get("providers", {}).get(provider_type, "anthropic")
    model = cfg.get("models", {}).get(provider, {}).get(provider_type, None)
    env_var = cfg.get("env_vars", {}).get(provider, "")

    result = _call_provider(provider, model, prompt, max_tokens, env_var)
    if result is not None:
        return result

    # Fallback: try the other provider
    other_provider = "openai" if provider != "openai" else "anthropic"
    other_model = cfg.get("models", {}).get(other_provider, {}).get(provider_type)
    other_env = cfg.get("env_vars", {}).get(other_provider, "")
    return _call_provider(other_provider, other_model, prompt, max_tokens, other_env)


def _call_provider(provider: str, model: str, prompt: str, max_tokens: int, env_var: str) -> str | None:
    if not model:
        return None

    if provider == "anthropic":
        [REDACTED_SECRET] or "ANTHROPIC_API_KEY")
        if not [REDACTED_SECRET] None
        try:
            import anthropic
            client = anthropic.Anthropic([REDACTED_SECRET])
            msg = client.messages.create(
                model=model, max_tokens=max_tokens,
                messages=[{"role": "user", "content": prompt}]
            )
            return msg.content[0].text
        except Exception as e:
            print(f"[llm_provider] anthropic error: {e}", file=sys.stderr)
            return None

    elif provider == "openai":
        [REDACTED_SECRET] or "OPENAI_API_KEY")
        if not [REDACTED_SECRET] None
        try:
            from openai import OpenAI
            client = OpenAI([REDACTED_SECRET])
            resp = client.chat.completions.create(
                model=model, max_tokens=max_tokens,
                messages=[{"role": "user", "content": prompt}]
            )
            return resp.choices[0].message.content
        except Exception as e:
            print(f"[llm_provider] openai error: {e}", file=sys.stderr)
            return None

    elif provider == "ollama":
        try:
            import urllib.request, json
            data = json.dumps({"model": model, "prompt": prompt, "stream": False}).encode()
            req = urllib.request.Request("http://localhost:11434/api/generate",
                                         data=data, headers={"Content-Type": "application/json"})
            with urllib.request.urlopen(req, timeout=30) as r:
                return json.loads(r.read())["response"]
        except Exception as e:
            print(f"[llm_provider] ollama error: {e}", file=sys.stderr)
            return None

    elif provider == "gemini":
        [REDACTED_SECRET] or "GOOGLE_API_KEY")
        if not [REDACTED_SECRET] None
        try:
            import urllib.request, json
            url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={api_key}"
            body = json.dumps({"contents": [{"parts": [{"text": prompt}]}]}).encode()
            req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
            with urllib.request.urlopen(req, timeout=30) as r:
                resp = json.loads(r.read())
                return resp["candidates"][0]["content"]["parts"][0]["text"]
        except Exception as e:
            print(f"[llm_provider] gemini error: {e}", file=sys.stderr)
            return None

    return None


def _fallback(prompt: str, max_tokens: int) -> str | None:
    """Last resort: try any available provider."""
    for provider, env in [("anthropic", "ANTHROPIC_API_KEY"), ("openai", "OPENAI_API_KEY")]:
        if os.environ.get(env):
            models = {"anthropic": "claude-haiku-4-5", "openai": "gpt-4o-mini"}
            return _call_provider(provider, models[provider], prompt, max_tokens, env)
    return None


def get_status() -> dict:
    """Return provider config status for eval/diagnostics."""
    cfg = _load_config()
    if not cfg:
        return {"mode": "fallback", "providers": {}}
    result = {}
    for tier in ["fast", "deep"]:
        provider = cfg.get("providers", {}).get(tier, "anthropic")
        env_var = cfg.get("env_vars", {}).get(provider, "")
        has_key = bool(os.environ.get(env_var)) if env_var else True  # ollama needs no key
        model = cfg.get("models", {}).get(provider, {}).get(tier, "?")
        result[tier] = {"provider": provider, "model": model, "ready": has_key}
    return result


path: scripts/llm_transfer.py
content: # scripts/llm_transfer.py
import sys, json, os
sys.path.insert(0, os.path.dirname(__file__))
from llm_provider import call_llm

def get_analogous_principles(task_desc, learnings_content):
    prompt = f"""Given this task:
"{task_desc}"

Which of these past learnings are analogically relevant? Explain the connection. Return ONLY valid JSON in this format:
{{"principles": [{{"principle": "...", "reasoning": "..."}}]}}

Past learnings:
{learnings_content}"""

    result = call_llm(prompt, provider_type="deep", max_tokens=500)
    if result:
        try:
            return json.loads(result)
        except json.JSONDecodeError:
            pass
    return None

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("FALLBACK")
        sys.exit(0)

    task_desc = sys.argv[1]
    learnings_file = sys.argv[2]

    try:
        with open(learnings_file, "r") as f:
            content = f.read()

        # Extract just the headers + first 2 lines of each entry
        lines = content.split('\n')
        extracted_lines = []
        capture = 0
        for line in lines:
            if line.startswith('## '):
                capture = 3
            if capture > 0:
                extracted_lines.append(line)
                capture -= 1

        extracted_content = '\n'.join(extracted_lines)
    except Exception:
        print("FALLBACK")
        sys.exit(0)

    result = get_analogous_principles(task_desc, extracted_content)
    if result:
        print(json.dumps(result))
    else:
        print("FALLBACK")


path: scripts/success-capture.sh
content: #!/bin/bash
# success-capture.sh
# Capture what worked + why

# Dynamic workspace detection — works for any OpenClaw user
WORKSPACE=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null)
if [ -z "$WORKSPACE" ]; then
  WORKSPACE="${OPENCLAW_WORKSPACE:-$HOME/.openclaw/workspace}"
fi

# Try self-improving-agent first, fallback to metaskill's own learnings
if [ -f "$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md" ]; then
  LEARNINGS_FILE="$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md"
else
  # Fallback: use metaskill's own learnings dir
  mkdir -p "$WORKSPACE/skills/metaskill/.learnings"
  LEARNINGS_FILE="$WORKSPACE/skills/metaskill/.learnings/LEARNINGS.md"
  # Create file if not exists
  [ -f "$LEARNINGS_FILE" ] || echo -e "# Learnings Log\n\n---\n" > "$LEARNINGS_FILE"
fi

WINS_FILE="$WORKSPACE/skills/metaskill/.learnings/WINS.md"
mkdir -p "$(dirname "$WINS_FILE")"

if [ -z "$1" ]; then
  echo "Usage: $0 <what_worked> [why_it_worked] or $0 --summary"
  exit 1
fi

# If running in summary mode:
if [ "$1" == "--summary" ]; then
    WINS=$(grep -c "^##" "$WINS_FILE" 2>/dev/null || echo 0)
    ERRORS=$(grep -c "^##" "$LEARNINGS_FILE" 2>/dev/null || echo 0)
    echo "=== Weekly Summary ==="
    echo "Wins logged: $WINS"
    echo "Deep Corrections (Errors): $ERRORS"
    exit 0
fi

WHAT="$1"
WHY="$2"

DATE=$(date +%Y-%m-%d)

cat <<INNEREOF >> "$WINS_FILE"

## [$DATE] Win
**What worked:** $WHAT
**Why it worked:** ${WHY:-"Not specified"}

INNEREOF

echo "✅ Success captured to $WINS_FILE"


path: scripts/transfer-check.sh
content: #!/bin/bash
# transfer-check.sh
# Pre-task analogy lookup

if [ -z "$1" ]; then
  echo "Usage: $0 <task_description>"
  exit 1
fi

TASK="$1"

# Dynamic workspace detection — works for any OpenClaw user
WORKSPACE=$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null)
if [ -z "$WORKSPACE" ]; then
  WORKSPACE="${OPENCLAW_WORKSPACE:-$HOME/.openclaw/workspace}"
fi

# Try self-improving-agent first, fallback to metaskill's own learnings
if [ -f "$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md" ]; then
  LEARNINGS_FILE="$WORKSPACE/skills/self-improving-agent/.learnings/LEARNINGS.md"
else
  # Fallback: use metaskill's own learnings dir
  mkdir -p "$WORKSPACE/skills/metaskill/.learnings"
  LEARNINGS_FILE="$WORKSPACE/skills/metaskill/.learnings/LEARNINGS.md"
  # Create file if not exists
  [ -f "$LEARNINGS_FILE" ] || echo -e "# Learnings Log\n\n---\n" > "$LEARNINGS_FILE"
fi

echo "=== Transfer Learning Check ==="
echo "Task: $TASK"

if [ ! -f "$LEARNINGS_FILE" ]; then
  echo "No analogous patterns — proceed, but log what works"
  exit 0
fi

SCRIPT_DIR=$(dirname "$0")
echo "Searching for analogous principles..."

# Try LLM extraction (v1.1)
echo "[LLM mode] Searching for analogies..."
LLM_RESULT=$(python3 "$SCRIPT_DIR/llm_transfer.py" "$TASK" "$LEARNINGS_FILE" 2>/dev/null)

if [ "$LLM_RESULT" = "FALLBACK" ] || [ -z "$LLM_RESULT" ]; then
  echo "[offline mode] LLM search unavailable or failed. Falling back to naive keyword search."
  
  # Extract naive keywords (simple lowercase, remove common words)
  KEYWORDS=$(echo "$TASK" | tr "[:upper:]" "[:lower:]" | sed -E "s/( a | the | to | and | for | in | of | on | with )/ /g" | tr -s " ")

  MATCHES=""
  for word in $KEYWORDS; do
    if [ ${#word} -gt 3 ]; then
      FOUND=$(grep -i -C 2 "$word" "$LEARNINGS_FILE" 2>/dev/null)
      if [ ! -z "$FOUND" ]; then
        MATCHES="$MATCHES\n$FOUND"
      fi
    fi
  done

  if [ -z "$MATCHES" ] || [ "$MATCHES" = "\n" ]; then
    echo "No analogous patterns — proceed, but log what works"
  else
    echo "Relevant principles found:"
    echo -e "$MATCHES" | sort -u | head -n 15
  fi
else
  # Parse JSON
  # Check if "principles" key exists
  HAS_PRINCIPLES=$(echo "$LLM_RESULT" | python3 -c "import sys, json; print('1' if json.load(sys.stdin).get('principles', []) else '0')")
  
  if [ "$HAS_PRINCIPLES" = "1" ]; then
    echo "Relevant principles found (LLM Analogy):"
    echo "$LLM_RESULT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for p in data.get('principles', []):
    print(f'- Principle: {p.get(\"principle\", \"\")}')
    print(f'  Reasoning: {p.get(\"reasoning\", \"\")}')
    print()
"
  else
    echo "No analogous patterns found by LLM — proceed, but log what works"
  fi
fi


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)