localcode_session.sh
1 #!/usr/bin/env bash 2 # LocalCode Session Manager 3 # Replicates Claude Code startup flow for local LLM 4 # Usage: ./localcode_session.sh start <project_path> 5 6 set -euo pipefail 7 8 # Configuration 9 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 SESSIONS_DIR="${HOME}/.localcode/sessions" 11 OLLAMA_MODEL="${LLM_MODEL:-deepseek-coder:6.7b}" 12 OLLAMA_ENDPOINT="${OLLAMA_ENDPOINT:-http://localhost:11434}" 13 14 # Ensure sessions directory exists 15 mkdir -p "$SESSIONS_DIR" 16 17 # Color output 18 RED='\033[0;31m' 19 GREEN='\033[0;32m' 20 YELLOW='\033[1;33m' 21 BLUE='\033[0;34m' 22 CYAN='\033[0;36m' 23 NC='\033[0m' 24 25 # Helper functions 26 info() { echo -e "${BLUE}ℹ${NC} $1"; } 27 success() { echo -e "${GREEN}✓${NC} $1"; } 28 error() { echo -e "${RED}✗${NC} $1" >&2; } 29 warn() { echo -e "${YELLOW}⚠${NC} $1"; } 30 31 # Generate session ID 32 generate_session_id() { 33 echo "session_$(date +%Y%m%d_%H%M%S)_$$" 34 } 35 36 # Build startup context from project path 37 build_startup_context() { 38 local project_path="$1" 39 local session_dir="$2" 40 41 info "Building startup context for: $project_path" 42 43 cd "$project_path" || { 44 error "Cannot access project directory: $project_path" 45 return 1 46 } 47 48 local context_file="$session_dir/startup_context.txt" 49 50 cat > "$context_file" <<'CONTEXT_START' 51 # LOCALCODE SESSION STARTUP CONTEXT 52 # This context is injected at the beginning of every query 53 54 ## PROJECT OVERVIEW 55 56 CONTEXT_START 57 58 # 1. Read CLAUDE.md if exists 59 if [[ -f "CLAUDE.md" ]]; then 60 echo "### Project Rules (from CLAUDE.md)" >> "$context_file" 61 echo '```' >> "$context_file" 62 head -200 CLAUDE.md >> "$context_file" 63 echo '```' >> "$context_file" 64 echo "" >> "$context_file" 65 success "Loaded CLAUDE.md (200 lines)" 66 fi 67 68 # 2. Run session-start hook if exists 69 if [[ -f ".claude/hooks/session-start.sh" ]]; then 70 echo "### System Status (from session-start.sh)" >> "$context_file" 71 echo '```json' >> "$context_file" 72 bash .claude/hooks/session-start.sh 2>/dev/null || echo '{"status": "hook_failed"}' >> "$context_file" 73 echo '```' >> "$context_file" 74 echo "" >> "$context_file" 75 success "Executed session-start hook" 76 fi 77 78 # 3. Git context 79 if [[ -d ".git" ]]; then 80 echo "### Git Context" >> "$context_file" 81 echo "Branch: $(git branch --show-current 2>/dev/null || echo 'unknown')" >> "$context_file" 82 echo "Last Commit: $(git log -1 --oneline 2>/dev/null || echo 'No commits')" >> "$context_file" 83 echo "Status: $(git status --short | wc -l) changed files" >> "$context_file" 84 echo "" >> "$context_file" 85 success "Loaded git context" 86 fi 87 88 # 4. Directory structure (top-level only) 89 echo "### Directory Structure" >> "$context_file" 90 echo '```' >> "$context_file" 91 ls -1 | head -20 >> "$context_file" 92 echo '```' >> "$context_file" 93 echo "" >> "$context_file" 94 success "Loaded directory structure" 95 96 # 5. Key file detection 97 echo "### Key Files Detected" >> "$context_file" 98 for pattern in "mix.exs" "package.json" "requirements.txt" "Cargo.toml" "go.mod" "Makefile" "docker-compose.yml"; do 99 if [[ -f "$pattern" ]]; then 100 echo "- $pattern" >> "$context_file" 101 fi 102 done 103 echo "" >> "$context_file" 104 105 success "Startup context built: $context_file" 106 echo "$context_file" 107 } 108 109 # Initialize session 110 start_session() { 111 local project_path="${1:-$(pwd)}" 112 113 # Resolve absolute path 114 project_path="$(cd "$project_path" && pwd)" 115 116 local session_id=$(generate_session_id) 117 local session_dir="$SESSIONS_DIR/$session_id" 118 119 mkdir -p "$session_dir" 120 121 info "Starting LocalCode session: $session_id" 122 info "Project: $project_path" 123 124 # Build startup context 125 local context_file=$(build_startup_context "$project_path" "$session_dir") 126 127 # Create session metadata 128 cat > "$session_dir/session.json" <<EOF 129 { 130 "session_id": "$session_id", 131 "project_path": "$project_path", 132 "model": "$OLLAMA_MODEL", 133 "started_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", 134 "conversation_turn": 0 135 } 136 EOF 137 138 # Create empty conversation history 139 echo "[]" > "$session_dir/conversation.json" 140 141 # Create tool results log 142 echo "[]" > "$session_dir/tool_results.json" 143 144 success "Session started: $session_id" 145 echo "" >&2 146 echo -e "${CYAN}Session Directory:${NC} $session_dir" >&2 147 echo -e "${CYAN}To query:${NC} $SCRIPT_DIR/localcode_query.sh $session_id \"your question\"" >&2 148 echo "" >&2 149 # Only session ID goes to stdout (for variable capture) 150 echo "$session_id" 151 } 152 153 # List active sessions 154 list_sessions() { 155 info "Active LocalCode sessions:" 156 echo "" 157 158 if [[ ! -d "$SESSIONS_DIR" ]] || [[ -z "$(ls -A "$SESSIONS_DIR" 2>/dev/null)" ]]; then 159 warn "No active sessions found" 160 return 0 161 fi 162 163 for session_dir in "$SESSIONS_DIR"/session_*; do 164 if [[ -f "$session_dir/session.json" ]]; then 165 local session_id=$(basename "$session_dir") 166 local project_path=$(jq -r '.project_path' "$session_dir/session.json") 167 local started_at=$(jq -r '.started_at' "$session_dir/session.json") 168 local turn=$(jq -r '.conversation_turn' "$session_dir/session.json") 169 170 echo -e "${GREEN}$session_id${NC}" 171 echo " Project: $project_path" 172 echo " Started: $started_at" 173 echo " Turns: $turn" 174 echo "" 175 fi 176 done 177 } 178 179 # End session 180 end_session() { 181 local session_id="$1" 182 local session_dir="$SESSIONS_DIR/$session_id" 183 184 if [[ ! -d "$session_dir" ]]; then 185 error "Session not found: $session_id" 186 return 1 187 fi 188 189 info "Ending session: $session_id" 190 191 # Archive session 192 local archive_dir="$SESSIONS_DIR/archive" 193 mkdir -p "$archive_dir" 194 195 tar -czf "$archive_dir/${session_id}.tar.gz" -C "$SESSIONS_DIR" "$session_id" 2>/dev/null 196 rm -rf "$session_dir" 197 198 success "Session archived to: $archive_dir/${session_id}.tar.gz" 199 } 200 201 # Show session info 202 show_session() { 203 local session_id="$1" 204 local session_dir="$SESSIONS_DIR/$session_id" 205 206 if [[ ! -d "$session_dir" ]]; then 207 error "Session not found: $session_id" 208 return 1 209 fi 210 211 echo -e "${CYAN}Session: $session_id${NC}" 212 echo "" 213 cat "$session_dir/session.json" | jq '.' 214 echo "" 215 216 local turn_count=$(jq '. | length' "$session_dir/conversation.json") 217 echo -e "${CYAN}Conversation History:${NC} $turn_count turns" 218 219 if [[ $turn_count -gt 0 ]]; then 220 echo "" 221 jq -r '.[] | "[\(.role)] \(.content | .[0:100])..."' "$session_dir/conversation.json" 222 fi 223 } 224 225 # Main CLI 226 main() { 227 local command="${1:-help}" 228 229 case "$command" in 230 start) 231 shift 232 start_session "$@" 233 ;; 234 list) 235 list_sessions 236 ;; 237 end) 238 shift 239 end_session "$@" 240 ;; 241 show) 242 shift 243 show_session "$@" 244 ;; 245 help|--help|-h) 246 cat <<EOF 247 LocalCode Session Manager - Replicate Claude Code flow for local LLM 248 249 Usage: 250 $0 start [project_path] Start new session (default: current directory) 251 $0 list List active sessions 252 $0 show <session_id> Show session details 253 $0 end <session_id> End and archive session 254 $0 help Show this help 255 256 Examples: 257 # Start session in current directory 258 $0 start 259 260 # Start session in specific project 261 $0 start /path/to/project 262 263 # List all sessions 264 $0 list 265 266 # Show session details 267 $0 show session_20250111_123456_12345 268 269 # End session 270 $0 end session_20250111_123456_12345 271 272 Session files stored in: $SESSIONS_DIR 273 EOF 274 ;; 275 *) 276 error "Unknown command: $command" 277 echo "Run '$0 help' for usage" 278 return 1 279 ;; 280 esac 281 } 282 283 # Run if called directly 284 if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 285 main "$@" 286 fi