Metadata-Version: 2.4
Name: tythan-code
Version: 1.0.0
Summary: Tythan Code: a terminal AI coding assistant that chats with any model (Anthropic, OpenAI-compatible, local), reads/edits files, searches the project and runs commands — with your confirmation.
Author: TythanAI
License: MIT
Project-URL: Homepage, https://github.com/TythanAI/Tythan-Code
Project-URL: Repository, https://github.com/TythanAI/Tythan-Code
Project-URL: Issues, https://github.com/TythanAI/Tythan-Code/issues
Keywords: ai,llm,coding-agent,cli,terminal,anthropic,claude,openai,security,sast,mcp,model-context-protocol
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: anthropic>=0.90.0
Requires-Dist: openai>=1.30.0
Requires-Dist: rich>=13.0.0
Requires-Dist: prompt_toolkit>=3.0.0
Requires-Dist: mcp>=1.0.0
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# Tythan Code

**The AI coding agent that won't ship vulnerabilities.**

A Cursor-style AI coding assistant that lives in your terminal — with **any
model you want** and a **built-in security auditor**. Chat about your project;
the assistant reads and edits files, searches the codebase, runs shell
commands, and scans its own output for security issues before calling a task
done. Every file change is shown as a diff and every command waits for your
confirmation.

```
 _        _   _                           _
| |_ _  _| |_| |_  __ _ _ _    __ ___  __| |___
|  _| || |  _| ' \/ _` | ' \  / _/ _ \/ _` / -_)
 \__|\_, |\__|_||_\__,_|_||_| \__\___/\__,_\___|
     |__/
```

## Features

- **Security-first agent** — the `security_scan` tool detects leaked secrets
  and API keys (AWS, GitHub, Stripe, Google, Slack, Telegram, JWTs, Bearer
  tokens + a Shannon-entropy detector for everything else), dangerous code
  patterns (eval, pickle, SQL built from f-strings, `shell=True`,
  `verify=False`, weak ciphers, `random` used for secrets, ...) and insecure
  config (wildcard CORS, JWT `none`, debug mode, plain-http endpoints). The
  agent audits code it just wrote and fixes CRITICAL/HIGH findings before
  declaring a task done; run `/audit` any time for an instant offline report.
- **Dependency CVE check (SCA)** — pinned dependencies from
  `requirements.txt`, `pyproject.toml` and `package.json` are checked against
  the [OSV.dev](https://osv.dev) vulnerability database (free, no key).
  Included in `/audit`; the agent can request it via
  `security_scan(include_dependencies=true)`. Degrades gracefully offline.
- **Any model** — native Anthropic API plus *any* OpenAI-compatible endpoint:
  OpenAI, OpenRouter, Groq, DeepSeek, Mistral, xAI, and fully local models via
  Ollama / LM Studio / vLLM. Switch providers mid-session with `/provider`.
- **Agentic loop** — the model reads files, edits them, runs tests and
  iterates until the task is done, streaming its answer live.
- **Human in the loop, per hunk** — a new file gets a single whole-file diff
  and `[y/N]`; a change to an existing file is split into independent hunks
  (`git add -p` style) so you can accept some and reject others in one
  response instead of an all-or-nothing yes/no — `y`/`n` per hunk, `a` to
  accept it and everything remaining, `d` to reject it and everything
  remaining. Only the accepted hunks are written; the tool result tells the
  model how many of the total were actually applied. Shell commands still
  ask a plain yes/no before running. `--yolo` turns all of this off.
- **`@file` mentions** — type `@src/app.py` in your message to attach that
  file's contents.
- **Workspace-confined** — all file operations are locked inside the project
  directory; path traversal is rejected.
- **`.gitignore`-aware** — `list_files`, `search`, `code_search`, `/audit` and
  the dependency check all skip whatever the project's own `.gitignore`
  excludes, on top of the usual junk directories (`.git`, `node_modules`,
  ...). No more noise from build output or vendored code that happens to live
  outside those hardcoded names.
- **Project rules** — drop a `.tythancode/rules.md`, `.tythancoderules`,
  `AGENTS.md` or `.cursorrules` file (checked in that order, first one found
  wins) in the workspace root and Tythan Code loads it into every system
  prompt automatically: conventions, build/test commands, things it should
  never touch. No more repeating the same context every session; the banner
  tells you when one was picked up.
- **Task plan tracking** — for multi-step work the assistant maintains a
  visible todo list (`todo_write`), shown as a checklist as it works through
  each step. Check it any time with `/todos`.
- **Ranked codebase search (`code_search`)** — BM25 keyword ranking over
  chunked files, for when you don't know the exact string to grep for ("where
  is retry logic for HTTP calls?"). This is lexical ranking, not embeddings —
  fully offline, no model call, no vector database — but it recovers a lot of
  what naive grep misses, including matching `read_file`/`readFile` from a
  query like "read file". Built lazily on first use, then cached on disk
  under `~/.tythancode/index/` keyed by each file's modification time and
  size, so re-opening the same project later only re-tokenizes what actually
  changed instead of the whole codebase again. `/reindex` forces an eager
  rebuild (still fast, for the same reason).
- **MCP client** — connect any [Model Context Protocol](https://modelcontextprotocol.io)
  stdio server (filesystem, fetch, databases, your own internal tools, ...)
  by adding it under `"mcp_servers"` in `~/.tythancode/config.json`. Their
  tools show up to the model as `mcp__<server>__<tool>` alongside the
  built-ins. Tythan Code doesn't control what a third-party server's tool
  actually does, so every call is confirmed before running — unless the
  server itself marks the tool `readOnlyHint`, the same way `read_file`
  needs no confirmation. `/mcp` shows what's connected; a server that fails
  to start is reported and simply contributes no tools rather than blocking
  startup.
- **Web fetch (`fetch_url`)** — read documentation, changelogs or an API
  response straight into the conversation, the terminal equivalent of
  Cursor's `@web`. Confirmed before running, same as `run_command`, and
  every request — plus every individual redirect hop — is resolved and
  checked against private/loopback/link-local/reserved address ranges
  before connecting, so a prompt-injected "fetch http://169.254.169.254/..."
  can't be used to reach cloud metadata endpoints or internal services.
- **Tools** — `read_file`, `write_file`, `edit_file` (exact string replace),
  `list_files` (glob), `search` (regex), `code_search` (ranked), `todo_write`,
  `fetch_url`, `run_command`, plus whatever connected MCP servers add.
- **Undo (`/undo`)** — every `write_file`/`edit_file` is checkpointed before it
  runs. `/undo` reverts the whole last turn's file changes in one step,
  survives restarting Tythan Code, and stays out of the way otherwise: nothing
  is written to `~/.tythancode` until a file actually changes. Shell commands
  run via `run_command` aren't covered — there's no honest way to snapshot and
  revert arbitrary shell effects, so this is a safety net for agent-authored
  edits, not a full undo of everything the agent does. Files over 5MB or that
  aren't valid UTF-8 are skipped rather than checkpointed (so `/undo` never
  "restores" a lossy, corrupted copy) — Tythan Code tells you when this
  happens. `/checkpoints [n]` lists recent checkpoints (10 by default, up to
  the 50 retained per workspace).
- **Automatic context compaction** — long sessions don't hit a hard
  context-length error. When the conversation approaches the model's context
  window, Tythan Code summarizes the older turns into one message and keeps
  the most recent turns verbatim, so the assistant keeps working instead of
  failing outright. Trigger it manually with `/compact`, check usage with
  `/context`.

## Install

```bash
cd tythan-code
pip install .
```

Requires Python 3.10+. For the default Anthropic provider:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
# or: ant auth login   (the SDK picks the profile up automatically)
```

## Providers

On first run Tythan Code creates `~/.tythancode/config.json`:

```json
{
  "default_provider": "anthropic",
  "providers": {
    "anthropic":  {"type": "anthropic", "model": "claude-opus-4-8"},
    "openai":     {"type": "openai", "base_url": "https://api.openai.com/v1",
                   "api_key_env": "OPENAI_API_KEY", "model": "gpt-4o"},
    "openrouter": {"type": "openai", "base_url": "https://openrouter.ai/api/v1",
                   "api_key_env": "OPENROUTER_API_KEY", "model": "anthropic/claude-sonnet-4.5"},
    "ollama":     {"type": "openai", "base_url": "http://localhost:11434/v1",
                   "model": "qwen2.5-coder:14b"}
  }
}
```

Add any OpenAI-compatible service as a new entry (`type: "openai"` +
`base_url` + `api_key_env` + `model`). Local endpoints (localhost) don't need
a key. `type: "anthropic"` uses the native Anthropic API with adaptive
thinking, effort control and prompt caching.

Each provider entry can also set `"context_window": <tokens>` to override how
much context Tythan Code assumes that model has before it proactively
compacts history. Without it, Tythan Code guesses conservatively: 200k for
Anthropic, 128k for known hosted APIs (OpenAI, OpenRouter, Groq, ...), and a
cautious 8k for anything on localhost — local model servers commonly run with
a much smaller context than the underlying model supports unless configured
otherwise, so set this explicitly if you've raised `num_ctx` (Ollama) or
similar.

## MCP servers

Add stdio MCP servers under `"mcp_servers"` in the same config file:

```json
{
  "mcp_servers": {
    "fetch": {"command": "uvx", "args": ["mcp-server-fetch"]},
    "filesystem": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allow"]}
  }
}
```

`command`/`args` launch the server as a subprocess over stdio (the same
transport `npx`/`uvx`-based servers use); `env` (optional) adds extra
environment variables on top of a safe inherited default (`PATH`, `HOME`,
...) — it does not replace the environment outright. Tythan Code connects to
every configured server at startup; a server that fails to start is reported
once and simply has no tools, it doesn't stop Tythan Code from working.

## Usage

```bash
tythancode ~/my-project              # interactive chat in that workspace
tythancode                           # current directory
tythancode -p "fix the failing test" # one-shot, non-interactive
tythancode --provider ollama         # pick a provider for this session
tythancode --model claude-sonnet-5 --effort xhigh
tythancode --yolo                    # auto-approve everything (careful!)
tythancode --no-checkpoints          # don't record file checkpoints (disables /undo)
```

### In-chat commands

| Command | Effect |
|---|---|
| `/help` | show help |
| `/clear` | reset the conversation |
| `/provider [name]` | list providers / switch (resets the chat) |
| `/model <id>` | switch model within the current provider (context window stays as configured for the provider — see `/context`) |
| `/effort <lvl>` | `low` / `medium` / `high` / `xhigh` / `max` (Anthropic) |
| `/audit [path]` | offline security scan of the workspace (or a subpath) |
| `/yolo` | toggle confirmation prompts |
| `/undo` | revert the file changes from the last turn |
| `/checkpoints [n]` | list recent undo checkpoints (10 by default) |
| `/todos` | show the assistant's current task plan |
| `/reindex` | rebuild the `code_search` index now (otherwise built lazily on first use) |
| `/mcp` | show connected MCP servers and the tools they expose |
| `/compact` | summarize older history now to free up context |
| `/context` | show estimated context usage vs. the model's window |
| `/exit` or Ctrl+D | quit |

### Example session

```
you> add a --verbose flag to @cli.py
assistant
⚙ read_file {"path": "cli.py"}
⚙ edit_file {"path": "cli.py", "old_string": "...", "new_string": "..."}
╭─ cli.py — hunk 1/1 ───────────────────────────╮
│ @@ -12,1 +12,1 @@                             │
│ -    parser.add_argument("--quiet", ...)      │
│ +    parser.add_argument("--verbose", ...)    │
╰────────────────────────────────────────────────╯
apply this hunk? [y/n/a/d/?] y
Done — added the flag and wired it to the logger setup.
tokens: 8231 in / 412 out / 7100 cached
```

## Development

```bash
pip install -e ".[dev]"
pytest           # tools, agent loop and provider tests run offline
```

## Releasing to PyPI

The package builds and installs cleanly today (`python -m build && twine
check dist/*` both pass, and the built wheel installs into a clean venv with
a working `tythancode` command) — what's left is account setup, not code:

1. Fill in `authors` and `[project.urls]` in `pyproject.toml` — left out on
   purpose rather than guessed, since they'd otherwise ship placeholder
   name/repo info to the public index permanently.
2. Register the project on PyPI (the name `tythan-code` is free as of this
   writing) and configure a
   [trusted publisher](https://docs.pypi.org/trusted-publishers/) for this
   repo pointing at `.github/workflows/publish.yml` — no API token to
   generate or store as a secret.
3. Cut a GitHub Release; `publish.yml` builds and uploads automatically.
   `ci.yml` runs the test suite on every push/PR across Python 3.10–3.13.

To build and sanity-check locally without publishing:

```bash
pip install build twine
python -m build
twine check dist/*
```

## Architecture

```
tythancode/
├── cli.py                    # REPL, slash commands, @mentions
├── agent.py                  # provider-agnostic agent loop + confirmations
├── tools.py                  # tool schemas + sandboxed executors
├── ui.py                     # rich rendering: streams, diffs, prompts
├── security.py               # offline security scanner (/audit + agent tool)
├── sca.py                    # dependency CVE check via OSV.dev
├── config.py                 # ~/.tythancode/config.json provider registry
├── compaction.py             # round-splitting + token-estimate helpers for /compact
├── checkpoints.py            # file-level undo store behind /undo, /checkpoints
├── storage.py                # shared ~/.tythancode per-workspace directory hashing
├── ignore.py                 # .gitignore matching shared by tools/security/sca
├── rules.py                  # project rules file discovery (AGENTS.md, ...)
├── codeindex.py              # BM25 index + ranking behind the code_search tool
├── hunks.py                  # per-hunk diff split/reconstruct behind write_file/edit_file review
├── mcp_client.py             # MCP (Model Context Protocol) stdio client
├── webfetch.py               # fetch_url tool: SSRF-checked HTTP fetch + HTML->text
└── providers/
    ├── base.py               # Backend interface (owns native msg format)
    ├── anthropic_backend.py  # Messages API: streaming, thinking, caching
    └── openai_backend.py     # any /v1/chat/completions endpoint
```

The agent loop is provider-agnostic: one streaming call per round; when the
model returns tool calls, Tythan Code executes them locally (asking you first
for anything mutating), sends results back and repeats until the turn ends.
Refusals, `pause_turn` and token-limit stops are handled explicitly.

Two pieces of session-level bookkeeping wrap that loop:

- **Compaction** (`compaction.py` + `Agent.maybe_compact`) splits the message
  history into "rounds" (a user turn plus everything the agent did in
  response), and — once the estimated context in use crosses ~80% of the
  model's context window minus the reserved output budget — asks the backend
  to summarize every round except the most recent `compact_keep_rounds` into
  one message. Each `Backend` implements `render_round` (native messages →
  plain text) and `complete_text` (one-shot, tool-free completion) to make
  this provider-agnostic; if the summarization call itself fails, Tythan Code
  logs it once and keeps working with the full history rather than looping on
  a broken call.
- **Checkpoints** (`checkpoints.py` + `Agent._checkpoint_before`) record each
  touched file's pre-turn content the first time `write_file`/`edit_file`
  touches it in a turn, and persist the whole turn as one checkpoint under
  `~/.tythancode/checkpoints/<hash of the workspace path>/`. `/undo` pops the
  most recent one and restores every file it touched.
- **Code index** (`codeindex.py` + `Agent._get_code_index`) is built lazily on
  the first `code_search` call and kept in memory for the rest of the
  session. `Agent` drops the in-memory instance after `write_file`,
  `edit_file` or `run_command` (any tool that could have changed files on
  disk), so the next search always reflects current content instead of
  silently going stale. Rebuilding is also backed by a per-workspace JSON
  cache under `~/.tythancode/index/<hash of the workspace path>/index.json`
  keyed by each file's `(mtime, size)`: a file that hasn't changed reuses its
  cached chunks instead of being re-read and re-tokenized. The cache format
  is versioned (`codeindex.CACHE_VERSION`) so a future change to chunking or
  tokenization invalidates old caches automatically, and any read/parse
  failure just falls back to a full rebuild rather than serving corrupt data.
