/ scripts / llm / localcode_session.sh
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