# SneakyCode A privacy-first, locally-running Python coding agent that uses a local LLM (via Ollama) to perform autonomous coding tasks inside a project directory. SneakyCode accepts natural language tasks and executes them using a defined toolset for filesystem operations, shell execution, code search, and file manipulation. It runs a ReAct-style tool-call loop: send conversation history to the LLM, receive tool calls, execute them with permission checks, and feed results back until the task is complete. ## Prerequisites - Python 3.11+ - [Ollama](https://ollama.ai/) running locally with a model that supports function calling (e.g., `qwen3.5`, `llama3.1`, `mistral-nemo`) - [uv](https://docs.astral.sh/uv/) (recommended) or pip ## Installation ```bash # Clone the repository git clone cd SneakyCode # Install dependencies uv sync --dev # Or with pip pip install -e ".[dev]" ``` ## Configuration Edit `config/config.yaml` to configure the agent. The full configuration reference: ```yaml llm: model: "qwen3.5:latest" # Ollama model name endpoint: "http://localhost:11434" # Ollama endpoint api_path: "/v1/chat/completions" # API endpoint path temperature: 0.1 # Sampling temperature max_tokens: 4096 # Maximum tokens in LLM response timeout: 120 # Request timeout in seconds max_retries: 3 # Retry attempts on transient errors retry_backoff_base: 1.0 # Exponential backoff base (seconds) retry_backoff_max: 30.0 # Maximum backoff seconds agent: max_iterations: 25 # Max tool-call iterations per turn max_conversation_tokens: 32000 # Token budget for conversation workspace_root: "." # Project directory for file operations truncation_keep_recent: 10 # Messages preserved during truncation truncation_threshold: 0.85 # Budget fraction that triggers truncation session: session_dir: ".sneakycode/sessions" # Directory for session files auto_save: true # Save session after each turn max_session_age_hours: 72 # Auto-cleanup old sessions offer_resume: true # Offer to resume on startup permissions: auto_approve: [read_file, list_dir, grep_files, find_files, finish] prompt_user: [write_file, delete_file, run_command, str_replace, patch_apply, make_dir] deny: [] tools: shell: allowed_commands: # Commands the LLM may run - git - python - pip - pytest - ruff - ls - cat - head - tail - wc - diff - grep - find - echo denied_commands: # Blocked commands - rm -rf / - sudo - curl - wget max_output_bytes: 65536 # Max captured output size (bytes) filesystem: max_file_size_bytes: 1048576 # 1 MB — max file size for read/write binary_detection: true # Detect and reject binary files display: show_tool_calls: true # Show tool call details in output show_token_usage: true # Show token usage stats stream_output: true # Stream LLM output to terminal skills: enabled: true # Enable the skills system directories: # Directories to scan for skill files - ".sneakycode/skills" debug: enabled: false # Enable debug logging log_dir: ".sneakycode/logs" # Debug log directory max_files: 10 # Max debug log files to retain ``` Environment variable `SNEAKYCODE_CONFIG` can override the config file path. ## Usage ```bash # Start the interactive TUI sneakycode # Open a specific project directory sneakycode /path/to/project # Or run directly python -m app.main # With options sneakycode --config path/to/config.yaml --verbose --log-file sneakycode.log ``` ### CLI Options | Option | Description | |--------------------------|--------------------------------------------------| | `DIRECTORY` | Project directory to use as workspace root | | `--config PATH` | Path to config YAML file (default: `config/config.yaml`) | | `-v`, `--verbose` | Enable verbose (DEBUG) logging | | `--log-file PATH` | Path to log file for persistent logging | ### REPL Commands | Command | Description | |-------------------|----------------------------------------------------| | `/help` | Show available commands | | `/quit` | Save session and exit (also `/exit`, `/bye`) | | `/history` | Show conversation history | | `/clear` | Clear conversation history | | `/save` | Manually save session | | `/session` | Show session info (messages, tokens, start time) | | `/models` | List available Ollama models | | `/models ` | Switch to a different model | | `/skills` | List available skills | ### Session Persistence Sessions are automatically saved after each agent turn and on exit. On startup, SneakyCode offers to resume the most recent session for the current workspace. Session files are stored in `.sneakycode/sessions/` within the workspace root (configurable via `session.session_dir`). ## Available Tools SneakyCode provides tools across 6 categories. See [docs/tools.md](docs/tools.md) for the full reference. | Category | Tools | Permission | |------------|-------------------------------------------------|---------------| | Read | `read_file`, `list_dir` | Auto-approved | | Search | `grep_files`, `find_files` | Auto-approved | | Write | `write_file`, `make_dir`, `delete_file` | User confirm | | Edit | `str_replace`, `patch_apply` | User confirm | | Shell | `run_command` | User confirm | | Control | `finish` | Auto-approved | | Skills | `load_skill` | Auto-approved | The `load_skill` tool is available when `skills.enabled` is `true` in the config. It allows the LLM to load skill instructions from the configured skill directories. ## Skills SneakyCode includes a skills system that lets you provide reusable instruction sets to the LLM. Skills are markdown files placed in `.sneakycode/skills/` (or any directory listed in `skills.directories`). Skills are auto-discovered on startup. The LLM can load them via the `load_skill` tool, and you can list available skills with the `/skills` command. To create a skill, add a `.md` file to your skills directory with a descriptive filename (e.g., `refactoring.md`). The file content is injected into the conversation when the skill is loaded. ## Development ```bash # Run tests .venv/bin/python -m pytest tests/ -v # Run with coverage .venv/bin/python -m pytest tests/ --cov=app # Lint .venv/bin/ruff check app/ tests/ # Format .venv/bin/ruff format app/ tests/ ``` ### Project Structure ``` app/ ├── agent/ # Agent loop and session context ├── models/ # Pydantic config and message schemas ├── services/ # LLM client, streaming, permissions, session persistence ├── tools/ # Tool implementations (one file per group) ├── ui/ # Textual TUI application and widgets └── utils/ # Logging, display, file helpers, token counter config/ └── config.yaml # Application configuration tests/ ├── unit/ # Unit tests for individual components └── integration/ # End-to-end workflow tests with mocked LLM ``` ## Architecture SneakyCode follows a **ReAct-style** agent pattern: 1. User provides a task in natural language 2. Agent sends conversation history + tool schemas to the LLM 3. LLM responds with either text (task complete) or tool calls 4. Agent executes tool calls with permission checks 5. Results are fed back to the LLM for the next iteration 6. Loop continues until the LLM produces a plain-text response or calls `finish` The LLM client is abstracted behind an OpenAI-compatible interface, so any endpoint implementing the `/v1/chat/completions` SSE streaming protocol works as a backend. ## License MIT