sonicoder / CLAUDE.md
R-Kentaren's picture
fix: agent_run param mismatch (send agent_name) + add GitHub push-update (3 inputs: repo name, token, username; --force-with-lease)
b87f702 verified
|
Raw
History Blame Contribute Delete
12.5 kB
# SoniCoder β€” Project Memory
> This file is the project's persistent memory. The agent reads it on every session.
> Edit it freely β€” it overrides defaults.
## What is SoniCoder?
SoniCoder is a local-first AI coding agent that can:
- Generate complete fullstack applications in any language/framework
- Read, write, and edit files in a sandboxed workspace
- Run shell commands (git, npm, pip, tests)
- Apply specialized skills (frontend-design, feature-dev, code-review, debugging, fullstack-scaffold, commit-workflow)
- Respond to slash commands (/commit, /review, /feature, /design, /explain, /test, /refactor, /skill, /help)
- Deploy to HuggingFace Spaces with one click
## Architecture
```
app.py ← Entry point: launches Gradio Server
code/
β”œβ”€β”€ config/constants.py ← App config, system prompt, language options
β”œβ”€β”€ model/
β”‚ β”œβ”€β”€ loader.py ← Dual model loading (text + VLM)
β”‚ └── inference.py ← Streaming inference (text + VLM)
β”œβ”€β”€ agent/__init__.py ← Agent loop (model ↔ tools, supports custom agents)
β”œβ”€β”€ tools/
β”‚ β”œβ”€β”€ fs.py ← read_file, write_file, edit_file, glob, grep, list_dir
β”‚ β”œβ”€β”€ bash.py ← Sandboxed shell execution
β”‚ β”œβ”€β”€ todos.py ← Todo list management
β”‚ └── github.py ← GitHub repo import (shallow clone + strip heavy dirs)
β”œβ”€β”€ skills/
β”‚ β”œβ”€β”€ __init__.py ← Skill discovery + loading
β”‚ └── builtins/ ← Built-in skills (markdown)
β”œβ”€β”€ agents/ ← Custom Agent system (AI-generated personas)
β”‚ β”œβ”€β”€ __init__.py ← Agent CRUD + system-prompt builder
β”‚ └── builtins/ ← Built-in agents (code-reviewer, test-writer)
β”œβ”€β”€ commands/
β”‚ β”œβ”€β”€ __init__.py ← Slash command parser + expander
β”‚ └── builtins/ ← Built-in commands (markdown, includes /agent and /github)
β”œβ”€β”€ hooks/
β”‚ β”œβ”€β”€ __init__.py ← Hook rule engine
β”‚ └── builtins/ ← Built-in hook rules (markdown)
β”œβ”€β”€ execution/
β”‚ β”œβ”€β”€ code_extractor.py ← Code extraction from model output
β”‚ β”œβ”€β”€ python_runner.py ← Sandboxed Python execution
β”‚ └── gradio_runner.py ← Gradio app subprocess runner
β”œβ”€β”€ huggingface/
β”‚ β”œβ”€β”€ dockerfile_gen.py ← Auto Dockerfile/package.json for JS
β”‚ └── push.py ← HF Hub push + ZIP packaging
β”œβ”€β”€ websearch/google_scraper.py ← DuckDuckGo + Google scraping (no API)
└── server/
β”œβ”€β”€ chat_helpers.py ← Chat history + prompt building
└── routes.py ← All HTTP + API endpoints
index.html ← Frontend (single-file SPA)
workspace/ ← Sandboxed agent workspace (auto-created)
```
## Conventions
- **Python**: 3.11+, type hints everywhere, `from __future__ import annotations`
- **Style**: Black formatting, 4-space indent, 100 char line limit
- **Docstrings**: Google style for modules, functions, classes
- **Error handling**: catch specific exceptions, never bare `except:`
- **Logging**: use `logging.getLogger(__name__)`, never `print()`
- **Tests**: pytest, in `tests/` directory, `test_*.py` naming
- **Frontend**: single-file HTML with inline CSS/JS, no build step
## Server rules
- All servers bind to `0.0.0.0` (never `localhost`)
- Default port: `7860` (HF Spaces convention)
- Sub-servers use `7861`, `7862`, etc.
## Model
- Default: `openbmb/MiniCPM5-1B` (text-only, 2.17 GB)
- Optional: `openbmb/MiniCPM-V-4.6` (vision + text, 2.8 GB)
- Loaded in background thread on startup
- Streaming inference via `TextIteratorStreamer`
## Tool call format
The model calls tools by emitting fenced code blocks with `tool` as the language:
```tool
read_file
path: src/app.py
```
Multi-line values use YAML block scalars:
```tool
write_file
path: src/new.py
content: |
import os
def main():
pass
```
## Slash commands
| Command | Description |
|---------|-------------|
| `/commit` | Create a git commit with a generated message |
| `/review` | Review current changes for bugs and quality |
| `/feature <desc>` | Guided feature development workflow |
| `/design <brief>` | Generate a distinctive frontend design |
| `/explain <target>` | Explain how code works |
| `/test <target>` | Generate tests |
| `/refactor <target>` | Refactor code for clarity |
| `/skill <name>` | Load and apply a skill |
| `/agent create <desc>` | AI generates a custom agent from natural-language description |
| `/agent use <name>` | Activate a saved agent |
| `/agent list` | List all saved agents |
| `/agent show <name>` | Show an agent's full definition |
| `/agent delete <name>` | Delete a user-defined agent |
| `/agent reset` | Reset to default SoniCoder persona |
| `/github <url> [subdir] [--branch <name>] [--into <path>]` | Import a GitHub repo into the workspace |
| `/help` | Show available commands and skills |
## Custom Agents
Custom agents are AI-generated personas layered on top of the base SoniCoder
system prompt. They can restrict the tool whitelist, auto-load skills, and
override temperature / max-iterations.
### Agent file format
Agents live in `workspace/.sonicoder/agents/<name>/AGENT.md` (built-ins in
`code/agents/builtins/<name>/AGENT.md`). Format:
```markdown
---
name: my-agent
description: One-line description
tools: read_file, list_dir, grep, bash
skills: code-review
temperature: 0.2
max_iterations: 12
tags: review, quality
author: AI-generated
created: 2026-06-20
---
# My Agent
Full system-prompt extension here. Define the persona, workflow, output format,
and any hard rules.
```
### How `/agent create` works
1. User runs `/agent create a security reviewer that flags hardcoded secrets`.
2. The slash-command expansion substitutes `AGENT_GENERATION_PROMPT` (defined
in `code/agents/__init__.py`) into the prompt.
3. The base SoniCoder model (NOT a custom agent) authors an `AGENT.md` file
via `write_file` and saves it under `.sonicoder/agents/<name>/`.
4. The user can then `/agent use <name>` or click the agent in the UI.
### Built-in agents
| Agent | Description |
|-------|-------------|
| `code-reviewer` | Read-only reviewer, structured issues table output |
| `test-writer` | Generates pytest/jest tests, runs them, iterates until green |
### API endpoints
| Endpoint | Description |
|----------|-------------|
| `list_agents` | List all agents (builtins + user) and the active one |
| `get_agent(name)` | Get a single agent's full definition |
| `save_agent(...)` | Create or overwrite a user agent (manual save) |
| `delete_agent(name)` | Delete a user agent (built-ins protected) |
| `set_active_agent(name)` | Set/clear the active agent for subsequent prompts |
| `import_github(url, branch, subdir, target_subdir, depth, timeout)` | Clone a GitHub repo into the workspace (shallow, heavy dirs stripped) |
| `github_url_examples()` | Return accepted GitHub URL formats |
| `push_github(repo_name, github_token, username, branch?, commit_message?, timeout?)` | Snapshot workspace β†’ commit β†’ push to a GitHub repo |
The `agent_run` endpoint also intercepts `/agent use|reset|delete|list` and
dispatches them directly to the agents module, bypassing the model entirely
for instant session-state updates.
## GitHub Import
SoniCoder can clone any public GitHub repo into the workspace, allowing the
agent to read, edit, extend, or refactor real-world code.
### How it works
1. User submits a GitHub URL via the Agent tab UI box (or via `/github <url>`
slash command in chat while Agent mode is ON).
2. The backend (`code/tools/github.py::import_github_repo`) parses the URL
(supports HTTPS, SSH, and `/tree/<branch>/<subdir>` forms) and validates
that the host is `github.com`.
3. The repo is shallow-cloned (`git clone --depth 1 --single-branch`) into a
temp directory.
4. Files are *copied* into the workspace (root or `target_subdir`) with these
directories stripped: `.git`, `.hg`, `.svn`, `node_modules`, `__pycache__`,
`.venv`, `venv`, `env`, `.tox`, `.mypy_cache`, `.pytest_cache`,
`.ruff_cache`, `dist`, `build`, `.next`, `.nuxt`, `.cache`, `.gradle`,
`target`, `Pods`. `.DS_Store` and `Thumbs.db` are also dropped.
5. The workspace tree refreshes; Agent mode auto-enables if it wasn't already.
### Security
- Only `github.com` URLs are accepted (HTTPS or SSH form).
- `target_subdir` is sanitized β€” no path escapes.
- The upstream repo is never modified (clone happens in temp dir, then
copied). The `.git` directory is stripped so the agent doesn't walk it.
- Default clone timeout: 120s (UI uses 180s). Max: 600s.
### Slash command
```
/github <url> [subdir] [--branch <name>] [--into <path>] [--depth <N>] [--timeout <s>]
```
The slash command (defined in `code/commands/builtins/github.md`) instructs
the agent to invoke `import_github_repo` via bash, then list the top-level
files and suggest next steps based on what was imported.
## GitHub Push
Push the current workspace back to a GitHub repo as a commit. Only 3 inputs
are required: repo name, GitHub token, username.
### How it works (`code/tools/github.py::push_to_github`)
1. Snapshot the workspace via `snapshot_workspace()` (returns a
`{relative_path: content}` dict).
2. Create a temp dir, `git init -b <branch>` inside it, write the snapshot
files in, `git add -A` + `git commit -m <message>`.
3. Build a push URL of the form
`https://<username>:<token>@github.com/<owner>/<repo>.git` and run
`git push --force-with-lease <url> <branch>`.
4. If `--force-with-lease` fails because the remote has no refs yet
(brand-new empty repo), retry with a plain `git push`.
5. Delete the temp dir. The token is never logged; error messages scrub it
before being returned.
### Why `--force-with-lease`
SoniCoder treats the workspace as the source of truth. `--force-with-lease`
replaces the remote tip with the workspace snapshot, but fails loudly (rather
than silently clobbering) if someone else pushed commits in the meantime β€”
because the temp repo has no reflog of the remote tip, the lease fails and
the user is told to pull first.
### UI
Located in the **Deploy** tab, in a "Push Update to GitHub" section below the
HuggingFace section. Only 3 fields are required: repo name, token, username.
An "Advanced" `<details>` exposes optional `branch` and `commit_message`
fields.
### Security
- Token is sent over HTTPS to the SoniCoder backend, used once for the push,
then dropped (not stored, not logged).
- Error messages are scrubbed to remove the token before being returned.
- The temp repo is deleted at the end of the call (context manager).
- The local SoniCoder workspace is never turned into a git repo; the
workspace's `.git` (if any, e.g. after an import β€” though imports strip
`.git`) is never read.
## Skills
| Skill | Description |
|-------|-------------|
| `frontend-design` | Distinctive visual design guidance |
| `feature-dev` | Guided feature implementation workflow |
| `code-review` | High-signal code review |
| `debugging` | Systematic debugging workflow |
| `fullstack-scaffold` | Project structure scaffolding rules |
| `commit-workflow` | Git commit best practices |
## Hooks
Hooks are markdown rules that fire on events (`bash`, `file`, `prompt`, `stop`).
They can `warn` (show a message) or `block` (prevent the action).
Built-in hooks:
- `block-dangerous-rm` β€” blocks `rm -rf /`, `~`, `$HOME`, `..`
- `warn-debug-code` β€” warns on `console.log`, `debugger`, `print`, `alert`
- `warn-secrets-in-code` β€” warns on hardcoded API_KEY/SECRET/TOKEN/PASSWORD
- `warn-eval-exec` β€” warns on `eval()` and `exec()`
Users can add custom hooks in `workspace/.sonicoder/hooks/*.local.md`.
## Workspace
The agent's sandboxed filesystem lives at `./workspace/` (configurable via
`SONICODER_WORKSPACE` env var). All file tools refuse paths that escape this root.
## Deploy
Generated projects can be pushed to HuggingFace Spaces via the Deploy tab.
Supported SDKs:
- `static` β€” HTML/CSS/JS
- `gradio` β€” Python Gradio apps
- `streamlit` β€” Python Streamlit apps
- `docker` β€” JS/TS frameworks (auto-generates Dockerfile + package.json)