SHA256
FIX SOME BUGS
This commit is contained in:
@@ -1,18 +1,52 @@
|
||||
# sjcode
|
||||
|
||||
ein terminal coding agent, der die ollama API nutzt
|
||||
a terminal coding agent, powered by the ollama API
|
||||
|
||||
## What makes it an agent
|
||||
|
||||
The client is not a chat front end: the model can **really work in your
|
||||
codebase**. It runs shell commands, reads and edits files — on your machine,
|
||||
in the directory you started `./coder` from. Every model reply can contain
|
||||
tool calls; the client executes them and feeds the real output back to the
|
||||
model until the task is done.
|
||||
|
||||
Tools the agent can use:
|
||||
|
||||
| tool | what it does | asks first |
|
||||
|---------|--------------------------------------------------|------------|
|
||||
| `run` | run one shell command in the project directory | yes |
|
||||
| `read` | read a file (numbered lines, `start`/`limit`) | no |
|
||||
| `files` | list project files (skips `.git`, caches, ...) | no |
|
||||
| `write` | create a file, or overwrite a small one | yes |
|
||||
| `edit` | exact-text replace in a file, shows a diff | yes |
|
||||
|
||||
Before every `run`/`write`/`edit` you see exactly what will happen (command
|
||||
line, or a unified diff for file changes) and press Enter to allow it, `n` to
|
||||
decline, `a` to stop the agent — or just type a short instruction ("use ripgrep
|
||||
instead") and the model adapts. `--yes` (or `/auto` in the session) runs
|
||||
everything without asking; the sandbox keeps all file tools inside the project
|
||||
directory.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
cp config.toml.example config.toml # then edit port / model / limits
|
||||
python3 server/server.py # starts the daemon + admin CLI
|
||||
cp server/config.toml.example server/config.toml # then edit port/model/limits
|
||||
python3 server/server.py # starts the daemon + admin CLI
|
||||
```
|
||||
|
||||
`config.toml` is machine-local and git-ignored; `config.toml.example` is the
|
||||
template. On first start the daemon creates the SQLite database (`sjcode.db`).
|
||||
|
||||
## Daemon (server.py)
|
||||
Pick a coder model in `[ollama] model` — one that follows structured output
|
||||
well, e.g. `qwen2.5-coder:7b` or bigger. The agent transcript is long, so the
|
||||
daemon requests a large context window from ollama:
|
||||
|
||||
```toml
|
||||
[ollama]
|
||||
num_ctx = 16384 # context window; do not go below 8192 for agent mode
|
||||
```
|
||||
|
||||
## Daemon (server/server.py)
|
||||
|
||||
On startup it prints its settings and drops into a minimal admin CLI
|
||||
(accounts are stored in SQLite):
|
||||
@@ -32,12 +66,18 @@ On startup it prints its settings and drops into a minimal admin CLI
|
||||
While the CLI runs, an HTTP API is served in the background:
|
||||
|
||||
- `POST /login` — `{"username", "password"}` → `{"token", ...}`
|
||||
- `POST /chat` — `{"token", "message", "model"?}` → `{"response", ...}`
|
||||
- `POST /chat` — `{"token", "message", "model"?, "stream"?}` → reply as JSON,
|
||||
or (with `"stream": true`) as NDJSON lines `{"chunk": ...}` ending with one
|
||||
final `{"response", "used_minutes", "remaining_minutes", ...}` line
|
||||
- `GET /models?token=...` — list ollama models
|
||||
- `GET /status` — health check
|
||||
|
||||
Every user row stores: `username`, `password_hash`, `last_logged_in`,
|
||||
`role`, `used_today` (minutes of AI usage; resets on a new day).
|
||||
|
||||
`SJCODE_CONFIG=/path/to/config.toml` lets you run a second daemon (tests,
|
||||
staging) next to a regular one.
|
||||
|
||||
## Roles and limits
|
||||
|
||||
In `config.toml` each role gets a daily limit in minutes:
|
||||
@@ -56,6 +96,7 @@ Login and every chat request are blocked once `used_today` reaches the limit.
|
||||
./coder # or: python3 client.py
|
||||
python3 client.py 10.0.0.5:8420 # explicit daemon address
|
||||
python3 client.py --model qwen3-coder # skip the model picker
|
||||
python3 client.py --yes # auto-approve every tool call (careful)
|
||||
python3 client.py --no-probe # skip the reachability check (use behind
|
||||
# HTTP proxies that answer on every port)
|
||||
```
|
||||
@@ -66,7 +107,10 @@ ones, or add a new one (`n`). Every server you use is saved in
|
||||
model, so the next start is just Enter-Enter. Addresses without a port use
|
||||
8420; `host:port`, `http(s)://...` URLs and IPv6 literals are accepted.
|
||||
|
||||
After login and model choice you get the agent banner and a chat REPL.
|
||||
After login and model choice you get the agent banner and a REPL. Type tasks,
|
||||
not chat messages — "rename the function foo to bar in all files" is a task,
|
||||
"what is a monad" is just a question the agent answers in place.
|
||||
|
||||
Commands:
|
||||
|
||||
| command | effect |
|
||||
@@ -76,14 +120,23 @@ Commands:
|
||||
| `/models` | list models on the server |
|
||||
| `/model <n>` | switch model (saved for this server) |
|
||||
| `/limit` | remaining minutes today (last known) |
|
||||
| `/auto` | toggle confirmations (auto-approve all) |
|
||||
| `/state` | auto mode, project dir, history size |
|
||||
| `/cwd` | show the project directory |
|
||||
| `/clear` | forget the conversation history |
|
||||
| `/quit` | exit |
|
||||
|
||||
Anything else is sent to the model. The client keeps the last few turns as
|
||||
context (the daemon API itself is stateless) and renders the reply in the
|
||||
agent style (`✓` lines for actions the model reports).
|
||||
The agent keeps the last few finished tasks as context. It stops after 30
|
||||
model round-trips per task; the daemon API itself is stateless.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.11+ (stdlib only; `tomllib` is included)
|
||||
- ollama listening on the address from `config.toml`
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
python3 -m unittest test_client.py -v # unit tests (no network)
|
||||
python3 test_e2e.py # fake ollama + daemon + client
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user