diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..e69de29 diff --git a/claude/code/mcp.json b/claude/code/mcp.json new file mode 100644 index 0000000..7abf764 --- /dev/null +++ b/claude/code/mcp.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "core": { + "type": "http", + "url": "https://api.lthn.sh/mcp", + "headers": { + "Authorization": "Bearer $CORE_BRAIN_KEY" + }, + "allowedEnvVars": ["CORE_BRAIN_KEY"] + } + } +} diff --git a/claude/code/scripts/session-start.sh b/claude/code/scripts/session-start.sh index 10613b4..77221a0 100755 --- a/claude/code/scripts/session-start.sh +++ b/claude/code/scripts/session-start.sh @@ -1,7 +1,7 @@ #!/bin/bash # Session start: Load OpenBrain context + recent scratchpad # -# 1. Query OpenBrain for project-relevant memories +# 1. Query OpenBrain for session history + roadmap + project context # 2. Read local scratchpad if recent (<3h) # 3. Output to stdout → injected into Claude's context @@ -16,6 +16,45 @@ if [[ -z "$BRAIN_KEY" && -f "$BRAIN_KEY_FILE" ]]; then BRAIN_KEY=$(cat "$BRAIN_KEY_FILE" 2>/dev/null | tr -d '[:space:]') fi +# Helper: query OpenBrain and return JSON +recall() { + local query="$1" + local top_k="${2:-3}" + local extra="${3:-}" # optional extra JSON fields (project, type filter) + + local body="{\"query\": \"${query}\", \"top_k\": ${top_k}, \"agent_id\": \"cladius\"${extra}}" + + curl -s --max-time 8 "${BRAIN_URL}/v1/brain/recall" \ + -X POST \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H "Authorization: Bearer ${BRAIN_KEY}" \ + -d "$body" 2>/dev/null +} + +# Helper: format memories as markdown (truncate to N chars) +format_memories() { + local max_len="${1:-500}" + python3 -c " +import json, sys +data = json.load(sys.stdin) +for m in data.get('memories', []): + t = m.get('type', '?') + p = m.get('project', '?') + score = m.get('score', 0) + content = m.get('content', '')[:${max_len}] + created = m.get('created_at', '')[:10] + print(f'**[{t}]** ({p}, {created}, score:{score:.2f}):') + print(f'{content}') + print() +" 2>/dev/null +} + +# Helper: count memories in JSON +count_memories() { + python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('memories',[])))" 2>/dev/null || echo "0" +} + # --- OpenBrain Recall --- if [[ -n "$BRAIN_KEY" ]]; then # Detect project from CWD @@ -30,65 +69,52 @@ if [[ -n "$BRAIN_KEY" ]]; then */snider/*) PROJECT=$(basename "$CWD") ;; esac - echo "[SessionStart] OpenBrain: querying memories..." >&2 + echo "[SessionStart] OpenBrain: querying memories (project: ${PROJECT:-global})..." >&2 - # 1. Recent session summaries (what did we do recently?) - RECENT=$(curl -s --max-time 5 "${BRAIN_URL}/v1/brain/recall" \ - -X POST \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H "Authorization: Bearer ${BRAIN_KEY}" \ - -d "{\"query\": \"session summary milestone recent work completed\", \"top_k\": 3, \"agent_id\": \"cladius\"}" 2>/dev/null) + # 1. Session history — what happened recently? + # Session summaries are stored as type:decision with dated content + SESSIONS=$(recall "session summary day completed built implemented fixed pushed agentic dispatch IDE OpenBrain" 3 ", \"type\": \"decision\"") - # 2. Project-specific context (if we're in a project dir) + # 2. Design priorities + roadmap — what's planned next? + # Roadmap items are stored as type:plan with actionable next steps + ROADMAP=$(recall "next session priorities design plans wire dispatch conventions pending work roadmap" 3 ", \"type\": \"plan\"") + + # 3. Project-specific context (if we're in a project dir) PROJECT_CTX="" if [[ -n "$PROJECT" ]]; then - PROJECT_CTX=$(curl -s --max-time 5 "${BRAIN_URL}/v1/brain/recall" \ - -X POST \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H "Authorization: Bearer ${BRAIN_KEY}" \ - -d "{\"query\": \"architecture decisions conventions for ${PROJECT}\", \"top_k\": 3, \"agent_id\": \"cladius\", \"project\": \"${PROJECT}\"}" 2>/dev/null) + PROJECT_CTX=$(recall "architecture design decisions dependencies conventions implementation status for ${PROJECT}" 3 ", \"project\": \"${PROJECT}\", \"type\": \"convention\"") fi - # Output to stdout (injected into context) - RECENT_COUNT=$(echo "$RECENT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('memories',[])))" 2>/dev/null || echo "0") + # --- Output to stdout --- + SESSION_COUNT=$(echo "$SESSIONS" | count_memories) + ROADMAP_COUNT=$(echo "$ROADMAP" | count_memories) - if [[ "$RECENT_COUNT" -gt 0 ]]; then + if [[ "$SESSION_COUNT" -gt 0 ]]; then echo "" - echo "## OpenBrain — Recent Activity" + echo "## OpenBrain — Recent Sessions" echo "" - echo "$RECENT" | python3 -c " -import json, sys -data = json.load(sys.stdin) -for m in data.get('memories', []): - t = m.get('type', '?') - p = m.get('project', '?') - content = m.get('content', '')[:300] - print(f'**[{t}]** ({p}): {content}') - print() -" 2>/dev/null + echo "$SESSIONS" | format_memories 600 + fi + + if [[ "$ROADMAP_COUNT" -gt 0 ]]; then + echo "" + echo "## OpenBrain — Roadmap & Priorities" + echo "" + echo "$ROADMAP" | format_memories 600 fi if [[ -n "$PROJECT" && -n "$PROJECT_CTX" ]]; then - PROJECT_COUNT=$(echo "$PROJECT_CTX" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('memories',[])))" 2>/dev/null || echo "0") + PROJECT_COUNT=$(echo "$PROJECT_CTX" | count_memories) if [[ "$PROJECT_COUNT" -gt 0 ]]; then echo "" echo "## OpenBrain — ${PROJECT} Context" echo "" - echo "$PROJECT_CTX" | python3 -c " -import json, sys -data = json.load(sys.stdin) -for m in data.get('memories', []): - t = m.get('type', '?') - content = m.get('content', '')[:300] - print(f'**[{t}]**: {content}') - print() -" 2>/dev/null + echo "$PROJECT_CTX" | format_memories 400 fi fi - echo "[SessionStart] OpenBrain: ${RECENT_COUNT} recent + ${PROJECT_COUNT:-0} project memories loaded" >&2 + TOTAL=$((SESSION_COUNT + ROADMAP_COUNT + ${PROJECT_COUNT:-0})) + echo "[SessionStart] OpenBrain: ${TOTAL} memories loaded (${SESSION_COUNT} sessions, ${ROADMAP_COUNT} roadmap, ${PROJECT_COUNT:-0} project)" >&2 else echo "[SessionStart] OpenBrain: no API key (set CORE_BRAIN_KEY or create ~/.claude/brain.key)" >&2 fi