Loki CLI - Complete Project Documentation

AI-Powered Code Analysis CLI with Error Detection, RAG Chat, and Web UI

35Python Files
11CLI Commands
18+Languages
5Security Layers
4AI Providers
124RAG Chunks

What is Loki?

Loki CLI is a zero-cost, local-only AI code analysis tool. It scans entire multi-language codebases, detects errors using language-specific linters and AST parsing, builds a FAISS vector index for RAG-powered chat, captures runtime errors from any process, and provides AI-powered explanations and auto-fixes. Everything runs on the user's machine with encrypted cache and OS keychain API key storage.

Quick Start

# Install
pip install -e D:\pro-2

# Set up AI provider (Groq - free)
loki models groq
# Paste your API key when prompted

# Scan your project
cd my-project
loki init

# See errors
loki errors

# Fix errors with AI
loki fix

# Chat with AI about your code
loki ai

# Capture runtime errors from any process
loki capture python my_script.py

# Open web UI
loki show

# Clean up when done
loki exit

System Architecture

Module Diagram

loki-cli/
├── loki/
│   ├── cli.py                    # Click CLI entry point
│   ├── core/
│   │   ├── types.py              # Data structures (enums, dataclasses)
│   │   ├── scanner.py            # File scanner for ALL languages
│   │   ├── errors.py             # Multi-language error detection
│   │   ├── cache.py              # Encrypted cache manager
│   │   ├── config.py             # User config (~/.loki/config.json)
│   │   ├── runtime_capture.py    # Runtime error capture hooks
│   │   └── global_hook.py        # Global exception hook
│   ├── security/
│   │   ├── secret_manager.py     # OS keychain API key storage
│   │   ├── cache_security.py     # Fernet encryption for cache
│   │   ├── secure_delete.py      # 3-pass secure file deletion
│   │   ├── leak_prevention.py    # API key/token redaction
│   │   └── integrity.py          # Package integrity verification
│   ├── ai/
│   │   ├── rag.py                # FAISS + sentence-transformers RAG
│   │   ├── chat.py               # Chat session with context injection
│   │   ├── guardrails.py         # Prompt injection + leak prevention
│   │   └── providers/
│   │       ├── base.py           # Abstract AI provider interface
│   │       ├── groq.py           # Groq API (free tier)
│   │       ├── openai_provider.py # OpenAI API
│   │       ├── anthropic.py      # Anthropic API
│   │       └── openrouter.py     # OpenRouter API
│   ├── commands/
│   │   ├── init_cmd.py           # loki init
│   │   ├── errors_cmd.py         # loki errors
│   │   ├── describe_cmd.py       # loki describe
│   │   ├── ai_cmd.py             # loki ai
│   │   ├── exit_cmd.py           # loki exit
│   │   ├── fix_cmd.py            # loki fix
│   │   ├── watch_cmd.py          # loki watch
│   │   ├── report_cmd.py         # loki report
│   │   ├── models_cmd.py         # loki models
│   │   ├── capture_cmd.py        # loki capture
│   │   └── inject_cmd.py         # loki inject
│   └── ui/
│       ├── server.py             # FastAPI web server
│       ├── routes.py             # API endpoints
│       ├── security.py           # Rate limiter + input sanitizer
│       └── static/
│           ├── index.html        # Web UI HTML
│           ├── style.css         # Dark theme CSS
│           └── app.js            # Frontend JavaScript
├── tests/                        # Test suite
├── .github/workflows/            # CI/CD pipelines
├── pyproject.toml                # Package config
└── res.index.html                # This documentation

Data Flow

User runs `loki init`
        │
        ▼
┌─────────────────┐
│  FileScanner     │  Walks directory, identifies 18+ languages
│  (scanner.py)    │  Computes SHA256 hashes per file
└────────┬────────┘
         │ ScanResult (files[], structure{})
         ▼
┌─────────────────┐
│  ErrorDetector   │  AST parsing (Python), subprocess calls
│  (errors.py)     │  to node/rustc/gcc/javac/go + linters
└────────┬────────┘
         │ Error[] (file, line, severity, code, message)
         ▼
┌─────────────────┐
│  RAGEngine       │  Chunks code → sentence-transformers → FAISS
│  (rag.py)        │  Saves index.faiss + chunks.json to cache
└────────┬────────┘
         │ Vector index built
         ▼
┌─────────────────┐
│  CacheManager    │  Encrypts with Fernet → ~/.loki/{hash}/
│  (cache.py)      │  API keys in OS keychain via keyring
└─────────────────┘
         │
         ▼
   User runs `loki ai` or opens `loki show`
         │
         ▼
┌─────────────────┐
│  ChatSession     │  Query → FAISS search → relevant chunks
│  (chat.py)       │  + error context → AI provider → response
└─────────────────┘

CLI Commands - Detailed

loki init init_cmd.py

What it does: Scans the entire codebase, detects errors across all languages, loads runtime errors, builds the FAISS RAG index, and saves everything to encrypted cache.

Functions used:

  • FileScanner.scan() - Walks directory tree, identifies files by extension (35 extensions across 18 languages), computes SHA256 hashes, returns ScanResult
  • ErrorDetector.detect_all() - Runs AST parsing + subprocess calls to linters (pylint, mypy, eslint, flake8) and compilers (node, rustc, gcc, javac, go vet)
  • get_runtime_errors() - Loads previously captured runtime errors from runtime_errors.json
  • RAGEngine.chunk_code() - Splits code into 50-line chunks with 10-line overlap
  • RAGEngine.build_index() - Encodes chunks with sentence-transformers, builds FAISS IndexFlatL2, saves to disk
  • CacheManager.save_scan() - Serializes ScanResult to JSON, encrypts with Fernet, saves to ~/.loki/{hash}/
  • CacheManager.save_errors() - Serializes Error list to JSON, encrypts, saves

Why each step: Scanning identifies what code exists. Error detection finds bugs. RAG indexing enables AI to understand the code. Encryption protects the cache.

loki errors errors_cmd.py

What it does: Displays all cached errors in a formatted Rich table with severity, file location, line number, error code, message, and source linter.

Functions used:

  • CacheManager.load_errors() - Loads and decrypts errors.json, deserializes into Error objects
  • _get_severity() - Extracts severity string from both dict and enum objects (handles cache serialization)
  • _get_attr() - Safely gets attributes from both dict and dataclass objects

Bug fix: Rich markup error was caused by f"[{style}]" when style was empty string. Fixed by defaulting to "white" when severity is unrecognized.

loki describe describe_cmd.py

What it does: For each error, sends it to the AI provider for detailed explanation including: what the error means, why it occurs, and how to fix it. Renders output as Markdown.

Functions used:

  • AIProvider.chat() - Sends error details to AI with system prompt
  • Rich Markdown() - Renders AI response as formatted Markdown

loki ai ai_cmd.py

What it does: Interactive chat session with the AI. AI has full context of your code (via RAG) and all detected errors.

Functions used:

  • RAGEngine.query() - FAISS vector search for relevant code chunks
  • ChatSession.send() - Validates input → sanitizes → retrieves RAG context → adds error context → sends to AI → validates output
  • AIGuardrails.validate_input() - Blocks prompt injection and forbidden topics
  • AIGuardrails.validate_output() - Prevents training data leaks and system prompt exposure
  • LeakPrevention.sanitize_for_ai() - Strips env vars and sensitive data before sending to AI

loki fix fix_cmd.py

What it does: AI-powered auto-fix. Sends each fixable error to AI, extracts the suggested code fix, and optionally applies it to the file with user confirmation.

Functions used:

  • _extract_code_from_response() - Uses regex r'```(?:\w+)?\n(.*?)```' to extract code blocks from AI response
  • _apply_fix_to_file() - Reads file, replaces the error line with the AI suggestion, writes back
  • _is_fixable() - Checks if error's fixable flag is True (handles both bool and string)

Bug fix: Code extraction originally only matched Python code blocks. Fixed regex to match any language: r'```(?:\w+)?\n(.*?)```'

loki watch watch_cmd.py

What it does: Monitors filesystem for changes using watchdog. When tracked files are modified, automatically re-runs loki init.

Functions used:

  • watchdog.Observer() - OS-level filesystem watcher
  • ChangeHandler.on_modified() - Callback that triggers re-scan when files change

loki report report_cmd.py

What it does: Generates a Markdown report with summary stats, errors grouped by severity, errors grouped by file, and full error details.

Functions used:

  • collections.Counter - Groups errors by severity and file
  • _get_severity() / _get_attr() - Handles dict/enum data from cache

Bug fix: Original code used e.severity.value which fails when severity is a string. Fixed with _get_severity() helper.

loki models models_cmd.py

What it does: Lists all available AI providers (Groq, OpenAI, Anthropic, OpenRouter) with their models and configuration status. Can set the active provider and store API key in OS keychain.

Functions used:

  • SecretManager.store_key() - Stores API key in OS keychain via keyring
  • SecretManager.retrieve_key() - Retrieves API key from keychain
  • ConfigManager.set_provider() / set_model() - Saves preferences to config.json

loki capture capture_cmd.py

What it does: Runs any command and monitors its stdout/stderr for error patterns. Works with ANY programming language. Uses 65+ regex patterns to detect errors from Python, JS, Rust, C, Java, Go, etc.

Functions used:

  • subprocess.Popen() - Spawns the target process
  • _monitor_stream() - Reads stdout/stderr in separate threads
  • _is_error_line() - Tests each line against 65+ regex patterns in ERROR_PATTERNS dict
  • _monitor_log_files() - Watches log files for error patterns using watchdog

How error patterns work: Each pattern maps a language to regex: {"python": ["Traceback", "Error:", ...], "javascript": ["TypeError:", "ReferenceError:", ...]}

loki inject inject_cmd.py

What it does: Prepends a Python exception hook into all Python files in a directory. When those files run and crash, the hook captures the error to the Loki cache.

Functions used:

  • HOOK_CODE - The injected Python snippet that sets up sys.excepthook
  • File I/O to prepend hook code to each .py file

loki exit exit_cmd.py

What it does: Securely deletes the project cache. Uses 3-pass overwriting with random data before unlinking files.

Functions used:

  • SecureDeleter.secure_delete_dir() - 3-pass secure deletion: overwrites with random data, then unlinks
  • CacheManager.clear() - Deletes entire ~/.loki/{hash}/ directory

Core Module - File by File

core/types.py

Purpose: Defines ALL data structures used across the entire application. This is the foundation - every module imports from here.

Classes:

  • Severity(Enum) - ERROR, WARNING, INFO
  • Language(Enum) - 18 languages: PYTHON, JAVASCRIPT, TYPESCRIPT, GO, RUST, C, CPP, JAVA, RUBY, PHP, SWIFT, KOTLIN, SCALA, ERLANG, HASKELL, LUA, R, OBJECTIVE_C, OBJECTIVE_CPP, UNKNOWN
  • FileMetadata - path, hash, size, language, lines, last_modified
  • ScanResult - project_hash, created_at, files[], structure{}
  • Error - file, line, column, severity, code, message, source, fixable, suggestion
  • ErrorSummary - total, errors, warnings, info
  • CodeChunk - file_path, start_line, end_line, content, language
  • ChatMessage - role, content, timestamp, context[]
  • CommandResult - success, message, data{}, error

Why: Centralized types prevent import cycles and ensure consistency. Every module uses these same structures.

core/scanner.py

Purpose: Recursively walks a project directory, identifies files by extension across 18+ languages, computes SHA256 hashes, and produces a complete scan result.

Key functions:

  • scan() - Main entry point. Walks files, parses metadata, returns ScanResult
  • _walk_files() - os.walk with ignore pattern filtering. Uses fnmatch.fnmatch() to check patterns
  • _parse_file() - Reads file, counts lines, computes SHA256 hash with hashlib.sha256()
  • _should_ignore() - Checks against ignore patterns (node_modules, .git, __pycache__, etc.)
  • _find_entry_points() - Identifies likely entry points (main.py, index.js, Cargo.toml, etc.)
  • _compute_project_hash() - SHA256 hash of project path for cache directory naming

EXTENSION_MAP: Maps 35 file extensions to Language enum values. Example: {".py": Language.PYTHON, ".js": Language.JAVASCRIPT, ".rs": Language.RUST, ...}

ENTRY_POINTS: List of 27 common entry point filenames (main.py, index.js, Cargo.toml, etc.)

core/errors.py

Purpose: Multi-language error detection engine. Uses AST parsing for syntax errors and subprocess calls to linters/compilers for deeper analysis.

Detection methods:

  • Python: ast.parse() catches SyntaxError. pylint --output-format=json for style/errors. mypy --output-json for type errors. flake8 --format=json for linting.
  • JavaScript/TypeScript: node --check for syntax. npx eslint --format=json for linting.
  • Go: go vet for static analysis
  • Rust: rustc --edition 2021 --crate-type lib for syntax checking
  • C/C++: gcc -fsyntax-only / g++ -fsyntax-only
  • Java: javac -version for compilation errors
  • Build systems: make -n for dry-run build errors

Error format: Each error becomes Error(file, line, column, severity, code, message, source, fixable)

Timeouts: Pylint: 60s, Mypy: 60s, ESLint: 30s, Flake8: 30s. Limited to 5 files per linter to avoid slowdowns.

core/cache.py

Purpose: Manages encrypted cache storage in ~/.loki/{project_hash}/. All data is encrypted with Fernet before writing to disk.

Functions:

  • save_scan(result) - Uses dataclasses.asdict() to serialize, then SecureCache.save_encrypted()
  • load_scan() - Loads and decrypts, then reconstructs ScanResult from dict
  • save_errors(errors) - Serializes Error list with asdict()
  • load_errors() - Deserializes back to Error objects
  • is_stale() - Checks if cache is older than SCAN_TTL (300 seconds)
  • exists() - Checks if scan.json exists in cache
  • clear() - Delegates to SecureDeleter for 3-pass secure deletion

core/config.py

Purpose: Reads/writes ~/.loki/config.json to persist user preferences.

Stored settings: AI provider (groq/openai/anthropic/openrouter), model name, file extensions to scan, ignore patterns.

Default config: {"provider": "groq", "model": "llama-3.3-70b-versatile", "extensions": [".py", ".js", ...], "ignore": ["node_modules", ".git", ...]}

core/runtime_capture.py

Purpose: Captures runtime errors from Python processes by installing global exception hooks.

Functions:

  • setup_runtime_capture(cache_dir) - Replaces sys.excepthook and threading.excepthook with custom handlers
  • capture_exception(type, value, tb) - Formats traceback, saves to runtime_errors.json
  • capture_console_error(msg) - Captures console.print errors
  • get_runtime_errors() - Loads all captured runtime errors
  • _save_runtime_error(error_data) - Appends error to JSON file with timestamp

How it works: When Python raises an uncaught exception, sys.excepthook is called. Our hook intercepts it, formats the traceback with traceback.format_exception(), and saves it to disk.

core/global_hook.py

Purpose: Extended global exception hook with decorators and context managers for catching exceptions in any code.

Key features:

  • init_hook(cache_dir) - Installs the global exception hook
  • @catch_all - Decorator that catches any exception in a function and logs it
  • @catch_all_async - Same for async functions
  • ExceptionLogger() - Context manager: with ExceptionLogger(): ...
  • _patch_except() - Patches sys.excepthook at a lower level than runtime_capture

Security Layer - 5 Layers

Layer 1: secret_manager.py - API Key Storage

Purpose: Stores API keys in the OS keychain (Windows Credential Manager, macOS Keychain, Linux Secret Service). Keys are NEVER stored in files.

Functions:

  • store_key(provider, key) - Validates key format, stores via keyring.set_password("loki", provider, key)
  • retrieve_key(provider) - keyring.get_password("loki", provider)
  • delete_key(provider) - keyring.delete_password("loki", provider)
  • _validate_key_format(key) - Regex patterns per provider: Groq (gsk_), OpenAI (sk-), Anthropic (sk-ant-), OpenRouter (sk-or-)
  • _update_config_hash() - Stores SHA256 hash of API key in config for tamper detection
  • _verify_config_hash() - Verifies stored hash matches actual key

Why keyring: OS keychain is encrypted at rest, protected by user login, and never exposed as plain text files.

Layer 2: cache_security.py - Encrypted Cache

Purpose: Encrypts all cache files on disk using Fernet symmetric encryption (AES-128-CBC with HMAC-SHA256).

Functions:

  • save_encrypted(filename, data) - Serializes to JSON, encrypts with Fernet(key).encrypt(), writes to disk
  • load_encrypted(filename) - Reads file, decrypts with Fernet(key).decrypt(), parses JSON
  • _get_or_create_key() - Retrieves encryption key from OS keychain, or generates new one and stores it
  • _set_restrictive_permissions() - Sets file permissions to owner-only (0o600 on Unix)

How Fernet works: Each encrypted token contains: version (1 byte) + timestamp (8 bytes) + IV (16 bytes) + ciphertext + HMAC (32 bytes). Decryption requires the same key.

Layer 3: secure_delete.py - Secure Deletion

Purpose: Prevents data recovery by overwriting files with random data before deletion.

Algorithm (3-pass):

  • Pass 1: Overwrite entire file with random bytes (os.urandom())
  • Pass 2: Overwrite with different random bytes
  • Pass 3: Overwrite with zeros
  • Then os.unlink() to remove the file entry

secure_delete_dir() - Recursively walks directory, secure-deletes all files, then removes empty directories.

Layer 4: leak_prevention.py - Data Sanitization

Purpose: Prevents API keys, passwords, tokens, and secrets from being sent to AI providers.

6 sensitive patterns:

  • API keys: (?:api[_-]?key|apikey)\s*[:=]\s*['"]?([a-zA-Z0-9]{20,})['"]?
  • Passwords: (?:password|passwd|pwd)\s*[:=]\code>
  • Tokens: (?:token|secret|bearer)\s*[:=]
  • AWS keys: AKIA[0-9A-Z]{16}
  • Generic secrets: (?:sk|pk|key)[_-][a-zA-Z0-9]{20,}
  • Connection strings: mongodb(\+srv)?://

sanitize_for_ai() additionally removes os.environ and os.getenv calls.

Layer 5: integrity.py - Package Integrity

Purpose: Detects tampering with the installed package files.

Functions:

  • generate_manifest() - Walks package directory, computes SHA256 hash of every file, saves to MANIFEST_FILE
  • verify_manifest() - Recomputes hashes and compares against stored manifest. Returns list of modified/added/deleted files.

guardrails.py - AI Safety

Purpose: Prevents prompt injection, training data leaks, and system prompt exposure.

Input validation:

  • 12 injection patterns: Detects "ignore previous instructions", "you are now", "system:", "ADMIN:", etc.
  • 10 forbidden topics: Blocks questions about system prompt, training data, other users, etc.
  • 8 training data patterns: Blocks probing for "as an AI", "you were trained", "your training data", etc.

Output validation:

  • Strips any leaked system prompt text
  • Removes shell command blocks (```bash, ```sh)
  • Filters training data leak patterns from response

AI Engine

ai/chat.py - ChatSession

Purpose: Manages the complete chat lifecycle: input validation → sanitization → context retrieval → AI call → output filtering → history storage.

Flow of send(message):

  1. AIGuardrails.validate_input(message) - Block injection/forbidden topics
  2. LeakPrevention.sanitize_for_ai(message) - Strip sensitive data
  3. get_context(sanitized) - RAG query for relevant code chunks
  4. get_error_context() - Format detected errors as context
  5. provider.chat(sanitized, full_context) - Send to AI with combined context
  6. AIGuardrails.validate_output(response) - Filter leaks from AI response
  7. Append both messages to self.history

Context format: Each RAG chunk becomes "file_path:start_line-end_line\n{code_content}". Errors become "- file:line [severity] message".

ai/system_prompt.py

Purpose: Hardcoded system prompt with 10 strict behavioral rules for the AI.

Rules: Code-only topics, no command execution, no prompt leakage, no personal opinions, no harmful code, always explain reasoning, ask for clarification when needed, format code properly, no speculation about users, stay in character as Loki.

AI Providers

providers/base.py - AIProvider (ABC)

Purpose: Abstract base class defining the interface all AI providers must implement.

Abstract methods: chat(prompt, context) -> str, embed(text) -> list[float], validate_key(key) -> bool

providers/groq.py - GroqProvider

Purpose: Default free AI provider. Uses Groq's fast inference API.

Implementation: groq.Groq(api_key=key).chat.completions.create(model=model, messages=[{system}, {user}])

Key validation: Regex r'^gsk_[a-zA-Z0-9]{48}$'

Default model: llama-3.3-70b-versatile

System prompt: Built from system_prompt.py with additional codebase context injected.

providers/openai_provider.py - OpenAIProvider

Purpose: OpenAI API integration (optional dependency).

Implementation: openai.OpenAI(api_key=key).chat.completions.create(model=model, messages=[...])

Also supports: embeddings.create() for vector embeddings.

Key validation: Regex r'^sk-[a-zA-Z0-9]{48}$'

providers/anthropic.py - AnthropicProvider

Purpose: Anthropic Claude API integration (optional dependency).

Implementation: anthropic.Anthropic(api_key=key).messages.create(model=model, max_tokens=4096, system=system, messages=[...])

Key validation: Regex r'^sk-ant-[a-zA-Z0-9]{93}$'

providers/openrouter.py - OpenRouterProvider

Purpose: OpenRouter API integration - access to multiple models via HTTP.

Implementation: Raw HTTP POST to https://openrouter.ai/api/v1/chat/completions using httpx.

Key validation: Regex r'^sk-or-[a-zA-Z0-9]{64}$'

RAG Engine (Retrieval-Augmented Generation)

ai/rag.py - RAGEngine

Purpose: Builds a vector search index over your codebase so the AI can find and understand relevant code when answering questions.

How it works:

  1. Chunking: chunk_code() splits each file into 50-line chunks with 10-line overlap. Each chunk becomes a CodeChunk(file_path, start_line, end_line, content, language)
  2. Embedding: SentenceTransformer("all-MiniLM-L6-v2") converts each chunk's text into a 384-dimensional vector
  3. Indexing: faiss.IndexFlatL2(384) stores all vectors. L2 distance for similarity search.
  4. Saving: faiss.write_index() saves the index. chunks.json stores chunk metadata.
  5. Querying: User question → embed → index.search(embedding, top_k=5) → return 5 most relevant chunks

Libraries:

  • faiss-cpu - Facebook's vector similarity search library
  • sentence-transformers - Hugging Face model for text embeddings
  • numpy - Array operations for FAISS

Storage: ~/.loki/{project_hash}/embeddings/index.faiss + chunks.json

Current state: 124 chunks indexed, 190KB index file, 162KB chunks.json

Web UI Backend

ui/server.py - UIServer

Purpose: FastAPI web server that serves the web UI and API endpoints.

Features:

  • CORS configured for localhost only (127.0.0.1:8080)
  • Serves static files from ui/static/
  • Auto-opens browser on start with webbrowser.open()
  • Uses uvicorn.run() for ASGI server

ui/routes.py - API Endpoints

Endpoints:

  • GET /api/files - Returns file tree with error counts per file
  • GET /api/errors?file= - Returns errors, optionally filtered by file path
  • GET /api/content?file= - Returns raw file content for code viewer
  • POST /api/chat - Sends message to AI, returns response with RAG context
  • WS /ws/chat - WebSocket for real-time chat
  • GET /api/runtime-errors - Returns captured runtime errors

Key pattern: All functions handle both dict and dataclass objects from cache using _get_attr() helper.

ui/security.py - Rate Limiter & Sanitizer

Functions:

  • RateLimiter.is_allowed(ip) - In-memory per-IP rate limiting (prevents abuse)
  • InputSanitizer.sanitize_file_path(path) - Prevents directory traversal (removes ../, ..\\)
  • InputSanitizer.sanitize_message(msg) - HTML escapes, limits length to 1000 chars, removes control characters

Frontend (HTML/CSS/JS)

ui/static/index.html

Purpose: Single-page application with three-panel layout.

Layout:

  • Left panel (280px): File tree sidebar + stats (files, errors, warnings)
  • Center panel (1fr): Code viewer with Prism.js syntax highlighting + error list
  • Right panel (360px): AI chat sidebar

External libraries (CDN):

  • Three.js r128: Animated neural-graph background with 95 particles and dynamic links
  • Prism.js 1.29.0: Syntax highlighting for code blocks
  • Google Fonts: Space Grotesk (headings), Inter (body), JetBrains Mono (code)

Mobile support: Slide-over drawers with hamburger menu at 900px breakpoint

ui/static/style.css

Purpose: Complete dark-theme CSS with "developer HUD" aesthetic.

Key features:

  • 30+ CSS custom properties for theming
  • Glass-panel effects with backdrop-filter: blur
  • Neon cyan (#00f0ff) and violet (#b84dff) accent colors
  • Animated corner brackets (corner-breathe animation)
  • Scan-sweep gradient animation on active elements
  • Holographic tilt effects on hover
  • Custom scrollbar styling
  • Responsive breakpoints: 1180px, 900px, 600px

ui/static/app.js

Purpose: Frontend JavaScript - fetches data from API, renders UI, handles chat.

Key functions:

  • loadFiles() - Fetches /api/files, renders file tree with icons and error badges
  • loadContent(filePath, ext) - Fetches /api/content, applies Prism.js highlighting
  • loadAllErrors() - Fetches /api/errors, updates stats and error list
  • sendChat() - POST to /api/chat, appends response to chat panel
  • getLanguageClass(ext) - Maps file extensions to Prism.js language classes
  • escapeHtml(text) - Prevents XSS via DOM textContent
  • Prism.highlightElement(codeElement) - Applies syntax highlighting to code blocks

CI/CD Pipelines

.github/workflows/test.yml

Purpose: Runs tests and linting on every push/PR to main.

Steps: Checkout → Setup Python (3.10/3.11/3.12 matrix) → Install deps → pytest tests/ -v --cov=lokiruff check .

.github/workflows/publish.yml

Purpose: Auto-publishes to PyPI when a GitHub release is created.

Steps: Checkout → Setup Python → Build (python -m build) → Publish (pypa/gh-action-pypi-publish@release/v1 with trusted publishing)

Test Suite

tests/test_cache.py

Tests: test_cache_save_and_load() - Creates temp directory, saves ScanResult, loads it back, verifies data matches. test_cache_exists() - Verifies exists() returns False when no cache, True after save.

tests/test_commands.py

Tests: test_init_creates_cache() - Creates temp project with a Python file, runs execute_init(), verifies cache directory exists.

tests/test_errors.py

Tests: test_detect_syntax_errors() - Creates file with syntax error (unclosed bracket), runs ErrorDetector, verifies error is found. Creates valid file, verifies no errors.

tests/test_providers.py

Tests: test_groq_validate_key() - Verifies valid gsk_ key is accepted, invalid keys rejected.

tests/test_scanner.py

Tests: test_scanner_finds_python_files() - Creates temp dir with .py file, runs scanner, verifies file is found. test_scanner_respects_ignore_patterns() - Creates __pycache__ directory, verifies it's skipped.

tests/test_security.py

Tests: test_sanitize_api_key() - Passes text with API key, verifies it's redacted to [REDACTED]. test_sanitize_for_ai() - Verifies os.environ calls are stripped.

Bugs Found & Fixed

Bug 1: Rich Markup Error in errors_cmd.py

Error: rich.errors.MarkupError: invalid markup when displaying errors with empty severity.

Cause: Code used f"[{style}]" where style could be empty string, creating invalid Rich markup like "".

Fix: Default to "white" when severity is unrecognized: style = _get_severity(e) or "white"

Bug 2: Severity.value Error in report_cmd.py

Error: AttributeError: 'str' object has no attribute 'value' when generating reports.

Cause: Cache deserializes severity as a string (e.g., "error"), not as Severity enum. Code used e.severity.value.

Fix: Created _get_severity() helper that handles both dict and enum: if isinstance(sev, str): return sev; if hasattr(sev, 'value'): return sev.value

Bug 3: Dict vs Object Access in All Commands

Error: Commands failed with AttributeError or KeyError when accessing error/file properties.

Cause: CacheManager.load_errors() returns deserialized data. Depending on how it's loaded, objects could be dicts OR dataclass instances.

Fix: Added _get_attr(obj, attr) helper to every command: if hasattr(obj, attr): return getattr(obj, attr); if isinstance(obj, dict): return obj.get(attr, '')

Bug 4: Python-Only Code Extraction in fix_cmd.py

Error: AI responses with non-Python code blocks weren't extracted.

Cause: Regex was r'```(?:python|py)?\n(.*?)```' - only matched Python.

Fix: Changed to r'```(?:\w+)?\n(.*?)```' - matches any language code block.

Bug 5: Linter Timeouts on Large Projects

Error: loki init would hang for minutes on projects with many files.

Cause: Pylint (120s timeout, 10 files), Mypy (120s, 10 files), ESLint (120s, 10 files) were too slow.

Fix: Reduced timeouts (60s/30s), limited to 5 files per linter, added --disable=C,R to pylint to skip convention/refactor checks.

Bug 6: RAG Only Indexed Python Files

Error: AI chat had no context about JS/TS/Rust/Go files.

Cause: init_cmd.py had if file_meta.language.value == "python": filter.

Fix: Changed to if file_meta.language.value != "unknown": to index ALL languages.

Dependencies

PackageVersionUsed InPurpose
click>=8.0cli.pyCLI framework - command parsing, help text, options
rich>=13.0All commandsTerminal formatting - tables, colors, markdown, progress bars
groq>=0.4providers/groq.pyGroq API client (free tier)
openai>=1.0providers/openai_provider.pyOpenAI API client (optional)
anthropic>=0.20providers/anthropic.pyAnthropic API client (optional)
httpx>=0.25providers/openrouter.pyHTTP client for OpenRouter API
faiss-cpu>=1.7ai/rag.pyVector similarity search (Facebook AI)
sentence-transformers>=2.2ai/rag.pyText embeddings (Hugging Face)
numpy>=1.24ai/rag.pyArray operations for FAISS
cryptography>=41.0security/cache_security.pyFernet encryption (AES-128-CBC)
keyring>=24.0security/secret_manager.pyOS keychain access
fastapi>=0.104ui/server.pyWeb API framework
uvicorn>=0.24ui/server.pyASGI server
websockets>=12.0ui/routes.pyWebSocket support for real-time chat
watchdog>=3.0commands/watch_cmd.pyFilesystem monitoring
hatchlingbuildpyproject.tomlPackage build backend

All Files Reference

FileLinesPurposeKey Functions
pyproject.toml45Package config, deps, entry pointloki entry point, hatchling build
loki/__init__.py3Package version__version__, __author__
loki/cli.py120CLI entry point, 11 commandsmain(), init(), errors(), show(), ai(), fix(), etc.
loki/core/types.py83All data structuresSeverity, Language, FileMetadata, ScanResult, Error, CodeChunk
loki/core/scanner.py130Multi-language file scannerscan(), _walk_files(), _parse_file(), EXTENSION_MAP
loki/core/errors.py350Error detection for all languagesdetect_all(), _check_*_syntax(), _run_*linters()
loki/core/cache.py77Encrypted cache managersave_scan(), load_scan(), save_errors(), load_errors()
loki/core/config.py65User config persistenceget_provider(), set_provider(), get_ignore_patterns()
loki/core/runtime_capture.py90Runtime error capture hookssetup_runtime_capture(), capture_exception(), get_runtime_errors()
loki/core/global_hook.py120Extended exception hookinit_hook(), @catch_all, ExceptionLogger
loki/security/secret_manager.py110OS keychain API key storagestore_key(), retrieve_key(), _validate_key_format()
loki/security/cache_security.py80Fernet encryption for cachesave_encrypted(), load_encrypted(), _get_or_create_key()
loki/security/secure_delete.py553-pass secure file deletionsecure_delete(), secure_delete_dir()
loki/security/leak_prevention.py70Secret redactionsanitize(), sanitize_for_ai(), SENSITIVE_PATTERNS
loki/security/integrity.py50Package integrity verificationgenerate_manifest(), verify_manifest()
loki/ai/rag.py124FAISS vector search indexbuild_index(), query(), chunk_code(), _save_index()
loki/ai/chat.py87Chat session managementsend(), get_context(), get_error_context()
loki/ai/guardrails.py150AI safety (injection/leak prevention)validate_input(), validate_output(), INJECTION_PATTERNS
loki/ai/system_prompt.py30AI behavioral rulesSYSTEM_PROMPT constant
loki/ai/providers/base.py25Abstract provider interfaceAIProvider(ABC), chat(), embed(), validate_key()
loki/ai/providers/groq.py60Groq API providerGroqProvider, chat(), validate_key()
loki/ai/providers/openai_provider.py70OpenAI API providerOpenAIProvider, chat(), embed(), validate_key()
loki/ai/providers/anthropic.py65Anthropic API providerAnthropicProvider, chat(), validate_key()
loki/ai/providers/openrouter.py70OpenRouter API providerOpenRouterProvider, chat(), validate_key()
loki/commands/init_cmd.py75loki init implementationexecute_init()
loki/commands/errors_cmd.py65loki errors implementationexecute_errors(), _get_severity(), _get_attr()
loki/commands/describe_cmd.py55loki describe implementationexecute_describe()
loki/commands/ai_cmd.py59loki ai implementationexecute_ai()
loki/commands/exit_cmd.py35loki exit implementationexecute_exit()
loki/commands/fix_cmd.py156loki fix implementationexecute_fix(), _extract_code_from_response(), _apply_fix_to_file()
loki/commands/watch_cmd.py50loki watch implementationexecute_watch(), ChangeHandler
loki/commands/report_cmd.py80loki report implementationexecute_report(), _get_severity(), _get_attr()
loki/commands/models_cmd.py70loki models implementationexecute_models(), PROVIDERS dict
loki/commands/capture_cmd.py120loki capture implementationexecute_capture(), _is_error_line(), ERROR_PATTERNS
loki/commands/inject_cmd.py60loki inject implementationexecute_inject(), HOOK_CODE
loki/ui/server.py55FastAPI web serverUIServer, start(), _setup_middleware()
loki/ui/routes.py132API endpointscreate_router(), /api/files, /api/errors, /api/chat
loki/ui/security.py60Rate limiter + input sanitizerRateLimiter, InputSanitizer
loki/ui/static/index.html200Web UI HTMLThree.js background, three-panel layout
loki/ui/static/style.css400Dark theme CSS30+ CSS variables, animations, responsive
loki/ui/static/app.js228Frontend JavaScriptloadFiles(), loadContent(), sendChat()
.github/workflows/test.yml35CI test pipelinepytest + ruff on Python 3.10-3.12
.github/workflows/publish.yml30PyPI publish pipelineBuild + publish on release
tests/test_cache.py30Cache teststest_cache_save_and_load(), test_cache_exists()
tests/test_commands.py25Command teststest_init_creates_cache()
tests/test_errors.py30Error detection teststest_detect_syntax_errors()
tests/test_providers.py20Provider teststest_groq_validate_key()
tests/test_scanner.py30Scanner teststest_scanner_finds_python_files()
tests/test_security.py25Security teststest_sanitize_api_key()