Metadata-Version: 2.4
Name: sagex
Version: 0.2.4
Summary: Self-learning local code agent runtime
Author: Karlen Minasyan
License: MIT License
        
        Copyright (c) 2026 Karlen Minasyan
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Keywords: ai,agent,code,automation,llm
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: anthropic>=0.40.0
Requires-Dist: openai>=1.50.0
Requires-Dist: watchdog>=4.0.0
Requires-Dist: chromadb>=0.5.0
Requires-Dist: sentence-transformers>=3.0.0
Requires-Dist: fastapi>=0.111.0
Requires-Dist: uvicorn[standard]>=0.29.0
Requires-Dist: click>=8.1.0
Requires-Dist: pydantic>=2.7.0
Requires-Dist: pyyaml>=6.0.1
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: pytest>=8.2.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
Requires-Dist: types-PyYAML>=6.0.0; extra == "dev"
Dynamic: license-file

# sagex

Self-learning local code agent that watches your codebase, acts autonomously, and gets smarter with every cycle.

The agent that learns your codebase so you don't have to explain it twice.

---

## What it does

Sagex runs alongside your project. It watches for file changes, reasons about what matters, takes action via LLM agents, and reflects on outcomes — storing lessons in a local SQLite knowledge base that improves every cycle.

```
File change detected
       ↓
Triage agent  →  is this worth acting on?
       ↓
Task agent    →  plan and apply edits
       ↓
Meta agent    →  what did we learn?
       ↓
Rules DB      →  store lesson for next time
```

---

## Install

**Python (PyPI)**

```bash
pip install sagex
```

**Node (no Python setup needed — the wrapper installs sagex into a private venv during `npm install`, or on first use via `npx`)**

```bash
# Install globally so the `sagex` CLI is on your PATH:
npm i -g sagex-runtime
sagex --version
sagex serve --port 8765

# Or one-shot without installing (must repeat for every call):
npx -p sagex-runtime sagex --version
```

---

## Choose a provider (v0.2.0+)

Sagex talks to four LLM providers behind the same interface. Pick one in
`.agents/config.yaml` under `agent.provider:`:

| Provider | Auth | Cost | Best for |
|---|---|---|---|
| `anthropic_api` (default) | `ANTHROPIC_API_KEY` env | Pay-per-token (Anthropic) | Teams, predictable spend |
| `claude_code` | Local `claude login` | **$0** (Claude Pro/Max subscription) | Solo devs already paying for Claude Max |
| `openai_api` | `OPENAI_API_KEY` env | Pay-per-token (OpenAI) | OpenAI customers / GPT-5 preference |
| `ollama` | None | **$0** (local hardware) | Privacy, offline, hobbyists |

For a one-off run without editing the config, override with
`SAGEX_PROVIDER=ollama sagex serve ...`. See `docs/PROVIDERS.md` for
quality expectations, hardware guidance, and known caveats per provider.

---

## Quick start

Follow the steps **in order** — skipping step 2 or 4 leaves you with a
registered-but-silent runtime that looks healthy in `status` but never
runs a cycle.

### 1. Start the runtime

```bash
# Authenticate for your chosen provider (one of):
export ANTHROPIC_API_KEY=sk-ant-...   # anthropic_api (default)
export OPENAI_API_KEY=sk-...          # openai_api
claude login                          # claude_code (npm i -g @anthropic-ai/claude-code)
# ollama needs nothing — ensure `ollama serve` is running

# In terminal 1 (leave running):
sagex serve --port 8765
```

### 2. Scaffold the project config

In your project repo (separate terminal):

```bash
sagex init .
```

This writes `.agents/config.yaml` with a **placeholder name** `my-project`
and prints next-step instructions.

### 3. Edit `.agents/config.yaml` — **this is mandatory**

At minimum, set:

- `name:` — a real project name (the string you will use in every
  subsequent CLI command). `sagex register` will refuse to proceed while
  this is still `my-project`.
- `watch.paths:` — globs that match the source files you care about.
- `test.command:` — the command sagex will run before and after applying
  changes to detect regressions.

The file is heavily commented — read it top to bottom; every section has
inline notes.

### 4. Install the git hooks

`sagex init` creates hook scripts under `.agents/hooks/` but does **not**
install them into `.git/hooks/`. Without this step, commits and pushes
never fire agent cycles — you can still `sagex trigger` manually, but the
watchful-observer flow is inert.

```bash
ln -sf "$(pwd)/.agents/hooks/post-commit" .git/hooks/post-commit
ln -sf "$(pwd)/.agents/hooks/pre-push"    .git/hooks/pre-push
```

### 5. Register with the runtime

```bash
sagex register .agents/config.yaml
# → Registered: <your-project-name>
```

### 6. Verify and exercise

Replace `<your-project-name>` below with the value you set in step 3:

```bash
# Does the runtime know about us?
sagex status <your-project-name>

# Fire a dry-run cycle without needing a real commit:
sagex trigger <your-project-name> --dry-run

# After a real commit has fired the post-commit hook:
sagex usage <your-project-name> --last 1h
```

---

## CLI reference

`sagex --help` / `sagex <command> --help` is the authoritative source.
This table is a quick index.

### Top-level

| Flag | Description |
|---|---|
| `--version` | Print the installed sagex version and exit. |
| `--help` | Print command list and exit. |

### `sagex init REPO_PATH`

Scaffold `.agents/config.yaml`, `.agents/.gitignore`, and the hook scripts
under `.agents/hooks/`. Does **not** install the hooks into `.git/hooks/`
— that is a separate, manual step (see Quick Start step 4).

| Argument | Description |
|---|---|
| `REPO_PATH` | Path to the project repo root. Use `.` when run from the repo. |

### `sagex register CONFIG_PATH`

Register a project config with a running `sagex serve` runtime.

| Argument / Flag | Description |
|---|---|
| `CONFIG_PATH` | Path to `.agents/config.yaml`. |
| `--force` | Allow registering even if `name:` is still the template default (`my-project`). Intended for tests and scripted setups. |

### `sagex serve`

Start the agent runtime server. Long-running process; runs event handling
and the FastAPI control API.

| Flag | Default | Description |
|---|---|---|
| `--host` | `0.0.0.0` | Interface the HTTP server binds to. `127.0.0.1` for local-only. |
| `--port` | `8765` | TCP port. |
| `--max-concurrent` | `3` | Ceiling on concurrent *active* agent tasks (the `asyncio.Semaphore`). Approval waits run outside this cap. |

### `sagex status PROJECT_NAME`

Print rules count, today's token spend, and recent episodes.

| Argument / Flag | Default | Description |
|---|---|---|
| `PROJECT_NAME` | — | Must match `name:` in the registered `config.yaml`. |
| `--runtime-url` | `http://localhost:8765` | Override when `serve` runs on a non-default host/port. |

### `sagex usage PROJECT_NAME`

Token spend and cost breakdown over a time window.

| Argument / Flag | Default | Description |
|---|---|---|
| `PROJECT_NAME` | — | Registered project name. |
| `--last` | `24h` | Window. Accepts `1h`, `24h`, `7d`. |
| `--runtime-url` | `http://localhost:8765` | Runtime HTTP URL. |

### `sagex trigger PROJECT_NAME`

Manually drive one agent cycle — useful for testing without waiting for
a real git commit.

| Argument / Flag | Default | Description |
|---|---|---|
| `PROJECT_NAME` | — | Registered project name. |
| `--dry-run` | off | Generate the plan without applying any file changes. |
| `--runtime-url` | `http://localhost:8765` | Runtime HTTP URL. |

### `sagex notify`

Called automatically by the installed git hooks. Rarely invoked directly.

| Flag | Required | Description |
|---|---|---|
| `--event` | yes | Event type — `commit` or `push`. |
| `--repo` | yes | Absolute path to the project repo root. |
| `--runtime-url` | no (default `http://localhost:8765`) | Runtime HTTP URL. |

---

## Environment variables

| Variable | Required when | Description |
|---|---|---|
| `ANTHROPIC_API_KEY` | `provider: anthropic_api` | Anthropic API key. |
| `OPENAI_API_KEY` | `provider: openai_api` | OpenAI API key. |
| `SAGEX_PROVIDER` | optional | Override `agent.provider` in the config for this process (values: `anthropic_api`, `claude_code`, `openai_api`, `ollama`). |
| `AGENTIS_LOG_LEVEL` | optional | `DEBUG`, `INFO` (default), `WARNING`, `ERROR`. |
| `AGENTIS_DB_PATH` | optional | Override the default global usage DB location (`~/.agentis/usage.db`). |

For local development, drop these into a `.env` file at the repo root —
`sagex serve` loads `.env` via `python-dotenv` on startup. `.env` is
gitignored by default.

---

## What triggers an agent cycle

Four paths — in decreasing order of "actually what you want in daily use":

1. **Post-commit hook** — every `git commit` fires `sagex notify
   --event=commit --repo=<pwd>`, which enqueues an event. Requires the
   symlink from Quick Start step 4.
2. **Pre-push hook** — same as above but on `git push`.
3. **Manual `sagex trigger <project>`** — best for testing. Works even
   without hooks installed.
4. **Direct HTTP POST** to `/notify` on the runtime. Used by CI systems
   and custom integrations.

If you install the hooks but still see `Total events: 0` after a commit,
check that `sagex serve` is running on the same URL the hooks point at
(default `http://localhost:8765`).

---

## Configuration (`.agents/config.yaml`)

`sagex init .` creates `.agents/config.yaml` in your project. The YAML
below is an illustrative excerpt of the default config — the
authoritative source is `src/sagex/cli.py::_CONFIG_TEMPLATE`, which is
what `sagex init` actually writes (with additional `knowledge:`,
`runner:`, and `notifications:` sections and slightly different
formatting). One `agent:` block drives all four sub-agents (triage,
task, meta, security); per-agent token ceilings live on the same block.

```yaml
name: my-project               # CHANGE ME — `sagex register` refuses the template default
repo_root: .

watch:
  paths:
    - "src/**/*.py"
    - "tests/**/*.py"
    - "*.yaml"
    - "*.toml"
  ignore:
    - "**/__pycache__/**"
    - "**/.git/**"
    - "**/*.log"
  debounce_seconds: 2.0

test:
  command: "pytest tests/ -x --tb=short"
  timeout_seconds: 120

agent:
  # Provider selection — override per-run with SAGEX_PROVIDER=<name>
  provider: "anthropic_api"    # anthropic_api | claude_code | openai_api | ollama
  # Leave empty to auto-resolve to the provider's default
  # (anthropic_api/claude_code → claude-sonnet-4-6 + claude-haiku-4-5;
  #  openai_api → gpt-5 + gpt-4o-mini;
  #  ollama → llama3.1:8b + llama3.2:1b).
  model: ""
  triage_model: ""
  max_tokens: 2048             # task agent output ceiling
  triage_max_tokens: 512       # haiku triage classification
  meta_max_tokens: 1024        # reflection JSON
  security_max_tokens: 1024    # security audit JSON
  summariser_max_tokens: 512   # diff pre-summariser (haiku)
  temperature: 0.2
  # triage_temperature: 0.0   # supported but not written by default; add if you want to override
  ollama_base_url: "http://localhost:11434"   # ollama only

budget:
  hourly_token_limit: 50000
  daily_token_limit: 500000
  alert_threshold: 0.8
  content_cache_ttl_minutes: 60
  enable_prompt_caching: true  # Anthropic cache_control, threshold-guarded

agents:
  security:
    enabled: false
    watch_extra:
      - ".env*"
      - "**/*secret*"
      - "**/requirements*.txt"
      - "**/Dockerfile*"
    auto_remediate: false
```

Cross-provider model settings (e.g. `provider: openai_api` +
`model: claude-sonnet-4-6`) fail at config-load with a clear error.

---

## Troubleshooting

**`sagex status <name>` returns zeros for everything.**
Either no events have fired yet, or you registered under a different
name. Run `curl -s http://localhost:8765/projects` — if your project
isn't listed, `sagex register .agents/config.yaml` again.

**`Connection refused` from `sagex register` or `sagex status`.**
`sagex serve` isn't running, or it's on a different port. Check with
`curl -s http://localhost:8765/projects` — if that errors too, the
runtime is down.

**Commits happen but `Total events` stays at 0.**
The git hooks weren't installed. Rerun Quick Start step 4. Confirm with
`ls -la .git/hooks/post-commit` — it must be a symlink into `.agents/`.

**`register` exits with "template default" error.**
Expected — edit `name:` in `.agents/config.yaml` to a real project name
before running register. See Quick Start step 3.

---

## Requirements

- Python 3.11+
- One provider's auth, depending on your `agent.provider` choice:
  - `anthropic_api` — `ANTHROPIC_API_KEY` env var
  - `claude_code` — local `claude` CLI (`npm i -g @anthropic-ai/claude-code`) + `claude login`
  - `openai_api` — `OPENAI_API_KEY` env var
  - `ollama` — nothing; `ollama serve` running locally
- SQLite (bundled with Python)

---

## Publishing

See [PUBLISHING.md](PUBLISHING.md) for instructions on releasing to PyPI and npm.

---

## License

MIT — see [LICENSE](LICENSE).
