Metadata-Version: 2.5
Name: sidekick-agent
Version: 0.29.0
Summary: Local-first terminal companion agent (Ollama-powered)
Project-URL: Homepage, https://github.com/Faisal-Fayaz/sidekick
Project-URL: Repository, https://github.com/Faisal-Fayaz/sidekick
Project-URL: Issues, https://github.com/Faisal-Fayaz/sidekick/issues
Project-URL: Documentation, https://github.com/Faisal-Fayaz/sidekick/tree/main/README.md
Project-URL: Changelog, https://github.com/Faisal-Fayaz/sidekick/releases
Author-email: Faisal-Fayaz <fai504438@gmail.com>
Maintainer-email: Irfanwani <irfanwani347@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Irfanwani and Sidekick contributors
        
        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.
License-File: LICENSE
Keywords: agent,cli,llm,local-first,ollama,terminal,tui,voice
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Shells
Classifier: Topic :: Terminals
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: openai>=1.0
Requires-Dist: python-dotenv>=1.0
Requires-Dist: rich>=13.0
Requires-Dist: textual>=8.0
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: test
Requires-Dist: hypothesis>=6.0; extra == 'test'
Requires-Dist: pytest-cov>=5.0; extra == 'test'
Requires-Dist: pytest>=8.0; extra == 'test'
Provides-Extra: voice
Requires-Dist: faster-whisper; extra == 'voice'
Description-Content-Type: text/markdown

<div align="center">

# Sidekick

**A local-first terminal companion you can talk to — chat, voice, and 19 tools, on your hardware.**

[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/)
[![Textual TUI](https://img.shields.io/badge/TUI-textual-green.svg)](https://textual.textualize.io/)
[![Ollama](https://img.shields.io/badge/LLM-ollama%20%2B%20any%20OpenAI--compatible-orange.svg)](https://ollama.com/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
[![CI](https://github.com/Faisal-Fayaz/sidekick/actions/workflows/ci.yml/badge.svg)](https://github.com/Faisal-Fayaz/sidekick/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/sidekick-agent.svg)](https://pypi.org/project/sidekick-agent/)

*No cloud account required. No API bill by default. Your files, memory, and voice never leave your machine unless you hand it a key.*

</div>

## See it

Slash autocomplete with fuzzy filtering, right in the prompt:

![Slash autocomplete](docs/tui-complete.svg)

A grounded answer — real tools, real system data, streamed live:

![Grounded answer](docs/tui-chat.svg)

*Screenshots are real SVG captures of the app running headless (`docs/shot.py`), not mockups. A marketing/docs site lives in [`website/`](website/) (Vite + React + TS).*

```console
$ sk brief
╭─ sidekick brief  Sat 2026-09-19 11:58 ─╮
│ CPU: AMD Ryzen 7 4800H (16 threads)     │
│ Mem: 7.2Gi · GPU: GTX 1650 4GB          │
│ /dev/nvme0n1p8  133G  117G  8.5G  94% / │
╰─────────────────────────────────────────╯
│ ! disk 94% full — clean ~/Downloads…    │

$ sk run "what is the ideal llm i can run on my device"
• qwen3:4b (2.5 GB): fits comfortably in your 4096 MiB VRAM.
• llama3.2:3b (2.0 GB): another good option.

$ sk talk
[Enter] to record, [Enter] to stop. /quit exits.
heard> what files are in the sidekick repo
```

## Why sidekick

| | Sidekick | Typical cloud agent |
|---|---|---|
| Runs fully offline (Ollama) | ✅ | ❌ |
| Voice input, transcribed on your CPU | ✅ | ❌ |
| Copy/paste that works in-terminal | ✅ drag-select, `ctrl+y`, `/copy` | varies |
| Answers grounded in *your* system, not guessed | ✅ deterministic grounding | prompt-only |
| Skills you can read (`SKILL.md`, incl. superpowers) | ✅ | varies |
| Offline test suite incl. prompt-regression evals | ✅ | rare |
| Deny-by-default egress for what the model fetches (`sk egress`, logged) | ✅ | ❌ |
| Audit ledger you can hand to an auditor (`sk audit`, credentials redacted) | ✅ | ❌ (their product *is* your code) |
| Costs $0 by default, spend caps when you bring keys | ✅ | metered |

## Quickstart

```bash
uv tool install sidekick-agent[voice]   # global `sk`, STT included
sk init                                  # guided first-run: hardware → model → verify
sk                                       # fullscreen chat — start here (`sk tui` works too)
```

No clone, no build — installs straight from PyPI. Requires Python 3.12+.
Without `[voice]` you get everything except Talk/mic (installs on first use instead). Local path needs Ollama (`ollama serve`, pull `qwen2.5-coder:7b` for smarts or `llama3.2:3b` for speed).

## Install

| Channel | Command |
|---|---|
| PyPI / uv | `uv tool install sidekick-agent[voice]` |
| PyPI / pipx | `pipx install sidekick-agent[voice]` |
| PyPI / pip | `pip install sidekick-agent[voice]` |
| AUR (Arch) | `yay -S python-sidekick-agent` |
| conda-forge | `conda install -c conda-forge sidekick-agent` *(feedstock lives in a separate repo)* |

The published name is **`sidekick-agent`** (the `sidekick` name is taken on
PyPI); the command stays `sk`. Version is a single source of truth in
`src/sk/__init__.py`. Publishing is automatic and credential-free: when a PR
is merged to `main` of the canonical repo
[`Faisal-Fayaz/sidekick`](https://github.com/Faisal-Fayaz/sidekick) with a bumped
`__version__`, GitHub Actions trusted-publishes to PyPI and opens a GitHub
Release (forks can never publish) — details in [`packaging/README.md`](packaging/README.md).

**From source (dev):**

```bash
git clone https://github.com/Faisal-Fayaz/sidekick && cd sidekick
uv tool install -e ".[voice]"   # editable dev install; STT included
sk doctor
```

## Chat

One input, two surfaces — fullscreen TUI and plain-text REPL share every command:

```bash
sk                   # fullscreen chat with streaming + themes — start here
sk tui --model fast  # same, explicit form
sk chat              # fallback REPL: dumb terminals, screen readers, broken TUIs
```

Type `/` and an autocomplete popup filters all 20+ commands — Enter completes, Tab too, Esc dismisses, ↑/↓ navigates. `F1` opens a generated cheatsheet (keys + commands, built from the same tables as the dispatcher, so it can't rot).

TUI keys: **Enter** sends · **ctrl+j**/**alt+enter** newline · **↑/↓** history · **ctrl+y** copies · **ctrl+g** push-to-talk · **pgup/pgdn** scroll · **F1** help · **F2** dark/light theme · **F3** sessions drawer · **F4** plan/build toggle · **F5** model picker (filter + live provider list). Answers stream live as Markdown with role colors; approvals arrive as cards with timeout; the status bar shows model · session · last-turn time/tokens.

Plan mode proposes without writing: `/plan` blocks file writes in chat (`/build` reverts), `F4` toggles the same in the TUI, and `sk run --plan` does it single-shot. Sessions carry `/compact [hint]` (summarize history on demand), `/diff` and `/review` (inspect the working tree), plus `/rewind [n]` to undo agent file edits.

## Voice

```bash
sk talk [-d SECS] [--stt-model base] [--device hw:2,0]  # Enter records, Enter stops
sk mic-test                                             # peak dB + silent/quiet/good verdict
```

Capture via the OS-native recorder (arecord/ALSA on Linux, sox/ffmpeg on macOS), transcription via local faster-whisper int8, transcript lands editable in the prompt. In the TUI, `ctrl+g` (or the mic pill) does the same. Voice never leaves your machine; recordings are temp files, deleted after each take.

## MCP server

```bash
sk mcp [--allow-writes]   # JSON-RPC 2.0 over stdio, zero new deps
```

All 19 tools, same safety policy (SSRF guards, write blocklists, hard-refusals). Reads auto-run; shell/writes/delete need `--allow-writes`, else a clean denied error. Stdout carries protocol only. Claude Desktop snippet:

```json
{ "mcpServers": { "sidekick": { "command": "sk", "args": ["mcp"] } } }
```

## Providers (BYO key)

```bash
sk connect     # pick provider → paste key (hidden) → pick model → ping. Done.
```

One guided flow: numbered provider list (local ones skip keys), live validation *before* anything saves, curated model list (TTS/image junk filtered, recommended pre-highlighted, Enter accepts), and a 5-token ping instead of a full agent turn. Advanced paths still work: `sk auth add/list/status/remove`, `sk model`, `sk setup` (connect + hook), `sk config --provider openai --api-key sk-...`, `/provider groq` inside chat.

Presets: `ollama|openai|groq|together|deepseek|openrouter|google|lmstudio|anthropic|opencode|custom` (`anthropic` speaks the native Messages API; the rest are OpenAI-compatible). Any OpenAI-compatible endpoint works via `--provider custom --base-url https://... --api-key ...`. Preferred: `SIDEKICK_API_KEY` env (never touches disk); file keys are chmod 600 and masked in `--show`. The Anthropic backend marks the static system prompt + tool definitions cacheable (repeat turns up to 10x cheaper); OpenAI-compatible providers cache matching prefixes automatically server-side. Reasoning effort: `sk config --reasoning-effort low` (default; `off|minimal|low|medium|high|max`), escalated to `high` in plan mode. Honored on OpenRouter (`reasoning.effort`), OpenAI (`reasoning_effort`), Anthropic (thinking budget sized to fit `max_tokens`), and Ollama (`think` toggle — `off` disables thinking, `high`/`max` enables it).

The `opencode` preset points at OpenCode Zen, opencode's gateway with a set of free, tools-capable models (`config.OPENCODE_FREE_MODELS`) plus paid tiers. The anonymous free tier is restricted by opencode to its own app, so add a free OpenCode account key first — `OPENCODE_API_KEY` env or `sk auth add opencode` (get it at opencode.ai/auth). Fast/smart resolve to `nemotron-3.5-lightning-free` / `muse-spark-1.3-contributor-free`; run `sk models opencode` for the live list.

## Command reference

| Command | What |
|---|---|
| `sk` / `sk tui [--continue]` | Fullscreen chat, fresh session each launch |
| `sk chat [--continue]` | Fallback plain-text REPL (dumb terminals, screen readers, TUI issues) |
| `sk talk [-d SECS] [--stt-model base] [--device hw:2,0]` | Push-to-talk voice chat (CPU transcription, Enter to record/stop) |
| `sk mic-test [-d SECS] [--device hw:2,0]` | Mic level check: peak dB + silent/quiet/good verdict |
| `/sessions`, `/resume <n>`, `/sessions delete <n>`, `/fork [n]` | List, switch, delete, branch past sessions |
| `/yolo`, `/confirm`, `/readonly` | Approval modes: auto-approve writes / ask every time / block all file writes (research mode) |
| `/compact [hint]`, `/diff`, `/review` | Summarize history on demand / inspect working-tree diff / review it from inside a session |
| `/plan`, `/build` | Plan mode: propose without writing (blocked writes) / back to build mode |
| `/rewind [n]` | Undo an agent file edit — snapshots write/edit/delete targets (`shell` mutations are not tracked, use git for those) |
| `sk run "task" [--yes] [--plan] [--read-only] [--deny LIST] [--model auto\|fast\|smart\|name] [--json] [--bg] [--allow LIST]` | Single-shot agent run (auto-router picks the model; `--json` emits one machine-readable document + exit codes, use with `--yes` unattended; `--bg` detaches, returns a job id, notifies on completion; `--allow shell:pytest,write_file` skips prompts for listed tools; `--plan` proposes without writing) |
| `sk jobs [-n N]` | List background jobs from `sk run --bg` |
| `sk brief [-p PATH] [--smart]` | Morning digest: system + git + todos + memories, instant without LLM |
| `sk digest [--force]` | Teammate pilot: brief + overnight failures, desktop nudge or log |
| `sk remember/recall/memories/forget` | Long-term memory (FTS5 search, auto-injected) |
| `sk search QUERY [--session S]` | Full-text search across past transcripts |
| `sk todo add/list/done/clear` | Todos |
| `sk history` / `sk oops` | Shell log / explain last failure |
| `sk imagine "prompt" [--out f.png]` | Generate an image via the provider images endpoint |
| `sk export [SESSION] [--out f.md]` | Session transcript as Markdown (turns + tool calls) |
| `sk egress [list\|allow HOST\|deny HOST\|test URL]` | Egress policy: what the model may fetch, and what it was blocked from ([docs](docs/egress.md)) |
| `sk audit [--session S] [--format md\|json]` | Compliance log: tool runs, approve/deny, local-vs-egress |
| `sk stats [--session S] [--format md\|json]` | Usage + cost estimates from audit rows (turns, tools, tokens); pair with spend caps for BYO-key budgets |
| `sk hook-install [--write]` | Bash/zsh logging hook |
| `sk hooks [--check]` | List event hooks + live dry-run (see `docs/hooks.md`) |
| `sk skills` / `sk skills-search` / `sk skills-registry [QUERY]` / `sk skills-install NAME` / `sk plugins` / `sk daemon [--once]` / `sk daemon-install [--schedule TXT]` / `sk daemon-install-macos [--schedule TXT]` / `sk daemon-schedule [--set TXT]` | Skill packs (registry + superpowers) / user-defined tools (`TOOLS.md`, see `docs/plugins.md`) / background watcher (systemd/launchd, calendar schedules) |
| `sk mcp [--allow-writes]` | MCP server over stdio (19 tools, safe defaults) |
| `sk mcp-servers` | List configured MCP client servers + live tool check (see `docs/mcp-client.md`) |
| `sk doctor [--fix]` / `sk report` / `sk models [list \| pull <id> \| prune <id>]` / `sk config` / `sk version` / `sk upgrade [--check]` | Health (+auto-remediation) / redacted diagnostics bundle / models (list, download, remove) / settings / build / self-update |
| `sk init` / `sk setup` / `sk connect` | Guided first-run / full setup / provider key flow |
| `sk model` / `sk auth add/list/status/remove` | Ask the provider for its live model list and set the default / manage provider keys (masked, validated live) |

Packs use the `SKILL.md` frontmatter format. The prompt carries a relevance-ranked index; the agent loads full instructions on demand via the `skill` tool. `fast`/`smart` resolve per provider (Ollama: llama3.2:3b/qwen2.5-coder:7b, Groq: gpt-oss-20b/120b).

## Architecture

```mermaid
flowchart TB
    U([you]) --> CLI[sk / sk run]
    U --> TUI[sk tui: autocomplete, streaming, mic pill]
    U --> VOICE[sk talk: arecord + faster-whisper]
    CLI --> SLASH[slash.py: /commands, no LLM]
    TUI --> SLASH
    VOICE --> AGENT
    CLI --> AGENT[agent.py: stream → tools → synthesize]
    TUI --> AGENT
    AGENT --> GROUND[deterministic grounding: ~/paths, URLs,\nsysinfo — injected before the model sees the prompt]
    AGENT --> TOOLS[tools/: 19 tools, allowlists,\nhard-blocks, SSRF guard]
    AGENT --> MEM[(store.py: history, memories FTS5,\ntodos, shell log)]
    AGENT --> SKILLS[skills: relevance-ranked SKILL.md index]
```

Design bets that paid off: **deterministic grounding beats prompt instructions** (small models ignore rules but can't argue with injected facts), **text-JSON fallback** (coders emit tools as text over the OpenAI endpoint), **FTS5 over vectors** (zero deps, instant, no embedding server on a 4GB box), **parallel reads** (approval-gated tools stay serial; independent reads run concurrently with failures isolated), **capability profiles over hope** (known models declare their tool protocol; the router reads them instead of paying a probing 400).

## Safety

Reads auto-run. Writes, deletes, and general shell need approval (inline `[y/N]` in TUI, prompt in CLI), HOME/`/tmp` only, ≤100KB, never `~/.ssh`, `~/.gnupg`, `/etc`, `/usr`. Multi-tool turns with destructive actions get **one plan review** up front instead of per-tool prompts (silent in `--yes`/`/yolo`; denials execute nothing). `shell` hard-refuses `rm -rf /`, `mkfs`, `dd` to devices, fork bombs even with approval. `read_url`/`web_search` block localhost/private IPs.

**Egress is deny-by-default.** Sidekick will not fetch a destination the model
asked for until you allow that host:

```bash
sk egress                              # what is allowed, and what was blocked
sk egress allow example.com            # allow one host
sk egress allow '*.example.com'        # or a whole domain
sk egress deny example.com             # revoke
sk egress test https://example.com/x   # check a URL without fetching it
```

This covers every destination the *model* can cause a fetch of: `read_url`,
`web_search` (including each search hit), and provider-supplied image URLs. With
an empty allowlist, all three are refused. Your model API endpoint and the MCP
servers you configured are unaffected — you chose those, they are not something
a prompt can talk the agent into adding. Every allow and deny is written to the
ledger with the host and reason, visible via `sk egress` and `sk audit`.

**The ceiling, stated plainly:** this governs fetches made *through the agent's
tools*. A `shell` command running `curl` is not covered — no string denylist can
be a network boundary. That is why shell still needs approval, and why
[issue #329](https://github.com/Faisal-Fayaz/sidekick/issues/329) (OS-level
sandboxing) is the real answer rather than another pattern list. See
[`docs/egress.md`](docs/egress.md). API keys chmod 600, masked in output.

## Tests

```bash
uv run --python 3.12 --with ".[test]" pytest tests -q   # unit + regression + Textual pilot, no Ollama needed
```

The eval harness (`tests/test_eval.py`) locks in every past quality bug as an offline regression test. A suite-wide fixture guarantees tests never touch your live `~/.sidekick/`.

## Config

`~/.sidekick/config.toml` (`provider`, `model`, `base_url` override, `api_key`, …). Env overrides: `SIDEKICK_PROVIDER`, `SIDEKICK_MODEL`, `SIDEKICK_BASE_URL`, `SIDEKICK_API_KEY`, `SIDEKICK_SPEND_CAP` (per-session USD cap, `0` = unlimited). Data stays home: `history.db`, `skills/`, `nudges.log`, `input_history`, `tui-errors.log`.

**History budget:** `history_budget_tokens` (default 3000) caps per-turn history; over-budget sessions compact to a rolling summary via the current model (DB history stays complete). Lower it for small-context models.

**Per-project config:** a `.sidekick.toml` in any repo layers over the global file (nearest one walking up from cwd). It may carry a `[project]` table only:

```toml
[project]
docs = ["CONTRIBUTING.md", "docs/architecture.md"]  # injected into the prompt
memory_namespace = "my-repo"                        # scopes this repo's memory
```

**Nothing at the top level of a project file is honoured** — not `provider`, `model`, `max_steps`, `temperature`, `egress_allow`, or anything else. That is deliberate, not a gap: a repository you just cloned must not be able to steer the agent's model, budget, approvals, or network, so those keys live in `~/.sidekick/config.toml` or the environment only. The same applies to `[project].approved_commands` (shell approvals stay global).

Every ignored key is reported rather than dropped quietly — a reduced `max_steps` in a project file is a *refusal*, not tuning, and it should not look otherwise:

```console
$ sk config --show
provider=ollama
project: /home/me/repo/.sidekick.toml
  ignoring max_steps in .sidekick.toml (global config or env only)
  ignoring max_step in .sidekick.toml (not a project-file setting; …)
```

`sk --cwd PATH` runs any command as if in that directory.

**Project memory auto-discovery:** from the cwd upward, the first `SIDEKICK.md` / `AGENTS.md` / `CLAUDE.md` / `GEMINI.md` found (root-down) is injected into the prompt automatically — no config needed for repos that already document themselves.

**Config profiles:** named `~/.sidekick/profiles/<name>.toml` files (e.g. work, personal, low-VRAM) replace `config.toml` when selected via `sk --profile NAME …` or `SIDEKICK_PROFILE=NAME` (env still wins, project files still layer). Manage with `sk config --profiles` (list), `sk config --save-profile NAME` (snapshot current non-secret settings — keys stay in keyring/env). Migrating: `cp ~/.sidekick/config.toml ~/.sidekick/profiles/work.toml`, trim it, and switch with the flag. `sk config --show` prints the active profile.

## Roadmap

See [`ROADMAP.md`](ROADMAP.md) — the shared plan (vision, next milestone, done list). It changes by pull request only.

## License

MIT — do what you want, shout-outs appreciated.
