#!/bin/bash # Analyze each island with letta CLI and post summaries back to the API. # Usage: ./scripts/analyze-islands.sh # # Requires: letta CLI, CLOUDFLARE_API_TOKEN set set -euo pipefail API_BASE="https://stigmergic.latha.org" LETTA_BIN="${LETTA_BIN:-/home/nandi/.local/npm-global/bin/letta}" # Fetch all islands islands=$(curl -sf "$API_BASE/api/strata/islands" | python3 -c " import json, sys data = json.load(sys.stdin) for island in data['islands']: print(island['id']) ") total=$(echo "$islands" | wc -l) current=0 echo "Analyzing $total islands..." while IFS= read -r island_id; do current=$((current + 1)) echo "[$current/$total] Analyzing island: ${island_id:0:60}..." # Fetch this island's full data island_json=$(curl -sf "$API_BASE/api/strata/islands" | python3 -c " import json, sys data = json.load(sys.stdin) for island in data['islands']: if island['id'] == sys.argv[1]: # Build a concise context for the LLM vertices = island['vertices'] edges = island['edges'] meta = island.get('vertexMeta', {}) titles = [meta.get(v, {}).get('title', v) for v in vertices[:30]] domains = {} for v in vertices: if v.startswith('http'): try: from urllib.parse import urlparse h = urlparse(v).hostname.replace('www.', '') domains[h] = domains.get(h, 0) + 1 except: pass top_domains = sorted(domains.items(), key=lambda x: -x[1])[:5] edge_types = {} for e in edges: t = e.get('connectionType', 'unknown') edge_types[t] = edge_types.get(t, 0) + 1 notes = [e['note'] for e in edges if e.get('note')][:5] out = { 'vertex_count': len(vertices), 'edge_count': len(edges), 'top_domains': [d[0] + f' ({d[1]})' for d in top_domains], 'edge_types': edge_types, 'sample_titles': titles[:15], 'notes': notes, } print(json.dumps(out)) break " "$island_id") if [ -z "$island_json" ]; then echo " Skipping (no data)" continue fi # Ask letta for a one-sentence summary prompt="You are analyzing a knowledge graph island (connected component). Write a single concise sentence (max 200 chars) summarizing what this cluster of links is about. No preamble, just the summary. Island data: $island_json" summary=$($LETTA_BIN -p "$prompt" --output-format json --system letta --memfs 2>/dev/null | python3 -c " import json, sys try: data = json.load(sys.stdin) if data.get('type') == 'result': print(data['result'].strip().strip('\"').strip()) else: print(data.get('result', data.get('text', '')), end='') except: # Fallback: try reading as plain text sys.stdout.write(sys.stdin.read().strip()) " 2>/dev/null || echo "") if [ -z "$summary" ]; then echo " Skipping (no summary generated)" continue fi # Truncate to 300 chars summary=$(echo "$summary" | head -c 300) echo " Summary: $summary" # PUT the summary back encoded_id=$(python3 -c "import urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=''))" "$island_id") curl -sf -X PUT "$API_BASE/api/strata/islands/$encoded_id/summary" \ -H "Content-Type: application/json" \ -d "{\"summary\":$(python3 -c "import json; print(json.dumps(sys.argv[1]))" "$summary")}" \ > /dev/null echo " Saved." # Rate limit sleep 2 done <<< "$islands" echo "Done. $current islands analyzed."