Metadata-Version: 2.4
Name: jarn
Version: 0.9.2
Summary: J.A.R.N. — Just A Reliable Nerd. A TUI-first coding agent harness built on DeepAgents.
Project-URL: Homepage, https://github.com/chayapats/jarn
Project-URL: Repository, https://github.com/chayapats/jarn
Project-URL: Documentation, https://github.com/chayapats/jarn/tree/main/docs
Author: Chayapat
License: Apache-2.0
License-File: LICENSE
Keywords: agent,coding-agent,deepagents,harness,llm,tui
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development
Requires-Python: >=3.12
Requires-Dist: aiosqlite>=0.20
Requires-Dist: deepagents>=0.6.7
Requires-Dist: httpx>=0.27
Requires-Dist: keyring>=25.0
Requires-Dist: langchain-anthropic>=0.2
Requires-Dist: langchain-google-genai>=2.0
Requires-Dist: langchain-mcp-adapters>=0.1
Requires-Dist: langchain-mistralai>=0.2
Requires-Dist: langchain-ollama>=0.2
Requires-Dist: langchain-openai>=0.2
Requires-Dist: langchain>=0.3
Requires-Dist: langgraph-checkpoint-sqlite>=2.0
Requires-Dist: langgraph>=0.2
Requires-Dist: platformdirs>=4.2
Requires-Dist: prompt-toolkit>=3.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.7
Requires-Dist: ruamel-yaml>=0.19.1
Requires-Dist: textual>=0.80
Requires-Dist: tiktoken>=0.7
Provides-Extra: build
Requires-Dist: pyinstaller==6.21.0; extra == 'build'
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27; extra == 'dev'
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: tzdata>=2024.1; (sys_platform == 'win32') and extra == 'dev'
Provides-Extra: otel
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'otel'
Description-Content-Type: text/markdown

<div align="center">

```
     ██╗  █████╗  ██████╗  ███╗   ██╗
     ██║ ██╔══██╗ ██╔══██╗ ████╗  ██║
     ██║ ███████║ ██████╔╝ ██╔██╗ ██║
██   ██║ ██╔══██║ ██╔══██╗ ██║╚██╗██║
╚█████╔╝ ██║  ██║ ██║  ██║ ██║ ╚████║
 ╚════╝  ╚═╝  ╚═╝ ╚═╝  ╚═╝ ╚═╝  ╚═══╝
```

**J.A.R.N. — Just A Reliable Nerd**

A TUI-first coding agent harness built on [DeepAgents](https://github.com/langchain-ai/deepagents).

**English** · [ภาษาไทย](README-TH.md)

![jarn demo](docs/assets/demo.gif)

</div>

---

J.A.R.N. is a terminal coding agent in the spirit of Claude Code and Codex CLI, but
built as its own opinionated harness on top of the DeepAgents library. Its defining
trait is **reliability**: it plans before acting, can enforce project verification,
and asks before doing anything risky. With `verify.gate: auto`, a failed acceptance
command triggers one bounded repair attempt and blocks successful completion if it
still fails.

It runs entirely in your terminal (a Web UI is on the roadmap, post-launch). Notable
capabilities: **AGENTS.md / CLAUDE.md interop** (works out-of-the-box beside other
agents), **headless one-shot mode** (`jarn -p "..."`), **JSONL session transcripts**,
**`!` shell escape** (output fed into the next agent turn as context), **OS-level execution sandbox** (macOS `sandbox-exec` / Linux
`bwrap`) and **Docker container backend** (`execution.backend: docker`), **presets**
(`/preset`, `jarn --preset`) that set mode + sandbox at once, with an untrusted floor,
**auto-checkpoint + `/undo` / `/redo`**, **repo map** (`/map`), a **wiki knowledge
base** (`/wiki`), **`/config` settings panel** (interactive tabbed UI, persists to
`~/.jarn/config.yaml`), and per-server **MCP health** (`/mcp status`).

> **Status:** v0.9.2 (Alpha) — on PyPI (`pip install jarn`) and npm (`npm install -g
> jarn-cli` — a standalone binary, no Python). v0.9 is a **hardening release**: a
> full self-improvement audit plus adversarial multi-model review (47 repro-verified
> fixes across permissions, cost integrity, concurrency, compaction, and
> observability; MCP tools are now namespaced `mcp__<server>__<tool>` — see
> CHANGELOG for the breaking note). The three releases before it (v0.6.0–v0.8.0)
> landed four improvement waves: **engine reliability**; **UX parity with Claude Code** (live
> in-place streaming, `/theme`, `@git:`/`@url:` mentions, word-level diffs, ghost
> autosuggest, conversation `/rewind` with file restore); **differentiators**
> (`--add-dir` multi-root, inline images, headless `--output-schema`, labelled subagent
> streaming, pluggable web search, an LSP-lite diagnostics loop, a verified-completion
> badge); and **launch systems** (a reusable GitHub Action + a nightly eval harness). The
> architecture, configuration, permission engine, and terminal REPL are implemented and
> tested; live model calls require your own API key. See [CHANGELOG.md](CHANGELOG.md) and
> [SECURITY.md](SECURITY.md).

> **Security:** J.A.R.N. runs tools on your **host** by default (real filesystem +
> shell). A project's `.jarn/config.yaml` can declare hooks, MCP servers, and
> provider overrides — only trust repositories you would run code from. Untrusted
> projects are gated until you approve (`jarn trust`). Read [SECURITY.md](SECURITY.md)
> before use.

## Why J.A.R.N.?

- **Reliable by design** — plan → act → verify is baked into the system prompt. The
  default `verify.gate: suggest` shows the detected acceptance command; opt-in
  `verify.gate: auto` runs it before completion.
  The completion badge — `` ⎿ verified: pytest ✓ 214 passed · 3.2s `` — confirms the
  result. A failure is fed back for a bounded repair round and, if still failing,
  ends the turn/headless run as an error instead of success. A diagnostics
  feedback loop (LSP-lite) then lints/type-checks just the files each turn edited
  (ruff + pyright) and can queue one bounded auto-fix round, so the agent catches
  the type error it just introduced (`verify.diagnostics: auto`).
- **Safe by default** — a multi-layer permission system (coarse modes + fine-grained
  rules) sits in front of every file write and shell command, backed by a hard
  *danger-guard* that always confirms catastrophic actions — even in YOLO mode.
- **Bring your own model** — 13 providers (OpenRouter, Anthropic, OpenAI, Google,
  Mistral, Groq, DeepSeek, Together, Fireworks, xAI, Ollama, LM Studio, plus a generic
  OpenAI-compatible endpoint) with per-task routing so subagents can use cheaper models.
- **Labelled subagent streaming** — output from delegated `task` subagents is tagged
  with a dim `┊ <name> ` prefix and collapses to a single `└ <name>: working… (N tool
  calls)` status line (full text in the Ctrl+O pager), so parallel subagents no longer
  interleave anonymously.
- **Cost- & context-aware** — live token/cost tracking (with a per-tool breakdown)
  and a per-session budget that can warn or hard-stop; a context-% gauge and live
  generation throughput (tok/s) that work for local models (LM Studio / Ollama)
  too, not just priced cloud ones.
- **Date-aware** — the current local date/time is injected into the system prompt,
  so "today"-relative requests don't anchor to the model's training cutoff.
- **Pluggable web search** — `web_search` supports Tavily, Brave Search, and Exa in
  addition to the keyless DuckDuckGo fallback.  Set `search.provider: auto` (default)
  and export `TAVILY_API_KEY` / `BRAVE_API_KEY` / `EXA_API_KEY` — the first one set wins.
- **Extensible** — skills, slash commands, custom subagents, lifecycle hooks, and MCP
  servers, all configured through plain files in `~/.jarn` and `.jarn/`.

## Install

macOS (Apple Silicon) and Linux (x64 / arm64) are supported; on Windows use WSL.

**Via npm** — a self-contained binary, **no Python required**:

```bash
npm install -g jarn-cli     # installs the `jarn` command (also available as `jarn-cli`)
```

Intel macs install via pip/uv instead (no npm binary is published for them).

**Via pip / uv** — requires **Python 3.12+** and [uv](https://docs.astral.sh/uv/):

```bash
pip install jarn            # PyPI (alpha)
# or: uv tool install jarn
```

**From source:**

```bash
git clone https://github.com/chayapats/jarn && cd jarn
uv sync --extra dev
uv run jarn
```

`uv.lock` is tracked in the repo so every teammate gets the same dependency versions.

### Sharing with your team

```bash
git clone <repo-url> && cd jarn
uv sync --extra dev
uv run jarn setup          # once per machine — stores API key in ~/.jarn
cd your-project
jarn doctor                # config, providers, and loaded extensions
jarn                       # trust prompt appears if the project declares hooks/MCP
jarn trust .               # pre-approve a repo you control (optional)
```

If a cloned project ships `.jarn/config.yaml` with hooks, MCP servers, or provider
overrides, J.A.R.N. asks before honouring them. Decline to run safely with those
settings stripped, or run `jarn trust <path>` after reviewing the repo. Use
`jarn doctor` to see which skills, commands, subagents, hooks, and MCP servers
would load (including shadowed or skipped files).

## Uninstall

To fully remove all J.A.R.N. state (config, secrets, trust store, sessions) and
OS keychain entries:

```bash
jarn uninstall          # shows an itemized summary, then prompts for confirmation
jarn uninstall --yes    # skip the prompt
```

`jarn uninstall` removes **only** `~/.jarn` (global state) — it never touches
project-local `.jarn/` directories. After removal it prints the package-manager
uninstall line (`npm uninstall -g jarn-cli` or `pip uninstall jarn`).

## Quick start

**With OpenRouter (recommended — one browser click, no manual key handling):**

```bash
jarn login        # opens browser → authorize → key stored in OS keychain
cd your-project
jarn              # launch the TUI (runs setup if still needed)
```

**Or configure manually:**

```bash
jarn setup        # first-run wizard: pick a provider, store your API key, choose defaults
cd your-project
jarn init         # create a JARN.md project-context file (optional but recommended)
jarn              # launch the TUI
jarn doctor       # diagnose config / providers / keys / extensions at any time
jarn bug          # assemble a redacted report + open a prefilled GitHub issue
```

**Shell completions (tab-complete subcommands and flags):**

```bash
# zsh — run once, then restart your shell
jarn completions zsh > ~/.zfunc/_jarn
# add to ~/.zshrc if not already: fpath=(~/.zfunc $fpath) && autoload -Uz compinit && compinit

# bash — run once, then source or restart
jarn completions bash > ~/.bash_completions/jarn.bash
# add to ~/.bashrc: source ~/.bash_completions/jarn.bash

# fish — run once
jarn completions fish > ~/.config/fish/completions/jarn.fish
```

On first launch with no config, J.A.R.N. runs the setup wizard automatically.
The OpenRouter option in the wizard also offers a one-click browser login.

## Non-interactive / scripting

```bash
jarn -p "summarise the open TODOs"          # one-shot: print reply and exit
echo "what changed?" | jarn -p -            # read prompt from stdin
jarn -p "do X" --json                        # emit JSON: {result, tokens, cost, turns}
jarn -p "do X" --model anthropic/claude-opus-4-8  # override model for this run
jarn -p "do X" --permission-mode auto-edit  # allow file writes without prompting
jarn -p "do X" --cwd /path/to/project       # set working directory
jarn -p "extract the version" --output-schema schema.json --json  # structured output
```

**Structured output (`--output-schema`):** pass a JSON Schema file to constrain the
agent's final answer. The parsed object replaces the free-text `result` field in the
`--json` envelope, making CI parsing trivial:

```bash
jarn -p "list changed files as JSON" --output-schema files.schema.json --json \
  | jq '.result.files[]'
```

Exit codes when `--output-schema` is used: `0` success (structured object in `result`);
`1` with `error.kind: "schema"` if the agent fails to produce a conforming response;
`2` with `error.kind: "usage"` if the schema file can't be read or parsed.

**Fail-closed safety:** the default modes (`ask` / `plan`) refuse any tool that
would normally prompt for approval and exit non-zero. Pass `--permission-mode
auto-edit` or `yolo` to allow unattended tool use — the danger-guard still
blocks catastrophic commands in every mode.

## In CI

J.A.R.N. ships a [GitHub Actions composite action](action/action.yml) so you
can run it in any workflow — PR review, issue-fix bots, nightly audits.

```yaml
- uses: chayapats/jarn/action@main
  with:
    prompt: "Review this diff: …"
    preset: "review-only"     # read-only; use 'ci' for write-enabled runs
    max_turns: "5"
    api_key: ${{ secrets.OPENROUTER_API_KEY }}
```

**Outputs:** `result`, `cost_usd`, `turns`.

**Docker note:** the default `ci` preset requires Docker (ubuntu runners have
it). For docker-less runners (macOS/Windows) use `preset: trusted-repo` with
`permission_mode: auto-edit` — see [docs/GITHUB_ACTION.md](docs/GITHUB_ACTION.md).

Example workflows: [PR review bot](examples/github/pr-review.yml) ·
[Issue-fix bot](examples/github/issue-fix.yml).
Full docs: [docs/GITHUB_ACTION.md](docs/GITHUB_ACTION.md).

## The interface: native inline

```bash
jarn            # start a session
jarn --resume   # pick a previous session to resume on launch
jarn --add-dir ../shared-lib --add-dir ../sibling-repo  # extra writable roots (repeatable)
```

**Multi-root workspaces (`--add-dir`):** by default the agent's write scope is the
project root. Pass `--add-dir <dir>` (repeatable) to grant scoped write access to a
sibling directory too — useful for monorepo/sibling-repo work. Each dir must exist
and be a directory. The launch flag works for headless runs too (`jarn -p … --add-dir
<dir>`). You can also add one mid-session with `/add-dir <path>` (approval-gated in
`ask`/`plan` modes; refused on an untrusted project). Added roots widen
the **write scope only** — project context (JARN.md) is loaded from the primary root,
and checkpoint/undo (`/undo`, `/rewind`) snapshot the **primary root only**. See
[docs/PERMISSIONS.md](docs/PERMISSIONS.md) and [SECURITY.md](SECURITY.md).

J.A.R.N. renders the conversation straight to your terminal's normal buffer —
no alternate screen. The whole transcript lives in your terminal's **native
scrollback**: one scroll gesture scrolls everything and native selection/copy
works across the entire history, exactly like Claude Code. Assistant replies
stream live and render as Markdown; tool calls, approvals, and a per-turn diff
preview appear inline.

## Using J.A.R.N.

```
┌ toolbar: model · mode · queue · ctx · cost ─────────────────────────────┐
│                                                                            │
│   conversation stream (assistant output, tool calls, approvals)           │
│                                                                            │
├────────────────────────────────────────────────────────────────────────┤
│ › your message…                                                          │
└────────────────────────────────────────────────────────────────────────┘
```

- **Type** a message and press **Enter** to send (**Shift+Enter** / **Ctrl+J** for a newline).
- Start a line with **`/`** for a command (see below). **`@`** references a file path or a
  rich mention:
  - **`@<path>`** — file or directory (default; bare `@`).
  - **`@folder:<frag>`** — directories only.
  - **`@symbol:<name>`** — repo-map symbol (function / class).
  - **`@git:status|diff|staged|log`** — on submit, replaced by a fenced block of real
    read-only git output (`--porcelain=v1 -b`, `diff`, `diff --staged`,
    `log --oneline -15`). Fixed argv allowlist, no shell, 5 s timeout. Output is
    secret-redacted before injection.
  - **`@url:<url>`** — rewrites to `fetch <url> with web_fetch and use its content`
    at submit time; no pre-fetch (network stays agent-mediated and SSRF-guarded).
- **↑ / ↓** navigate input history.
- **Tab** accepts the highlighted completion (`/command` or `@file`). Completion uses a
  **two-tier fuzzy engine**: exact-prefix matches appear first (unchanged predictability),
  followed by subsequence fuzzy matches — so `/cmit` finds `/commit` and `@pyprjct`
  finds `pyproject.toml`.
- **Ghost autosuggest:** as you type, the most recent matching history entry appears
  as dim ghost text after the cursor (fish/zsh-style). Press **→ (Right arrow)** or
  **Ctrl+E** at the end of the line to accept the full suggestion. The ghost is hidden
  while the completion dropdown is open; Right arrow navigates normally mid-line.
- **Ctrl+R** opens a **reverse-history picker**: an arrow-key overlay over the 50 most
  recent unique history entries with live type-to-filter. Press **↑/↓** to navigate,
  **Enter** to prefill the input (does not submit), **Esc** to cancel. Works even
  while a turn is running.
- **Shift+Tab** cycles the permission mode (plan → ask → auto-edit → yolo); the new
  mode flashes on the input border and stays in the status bar.
- **Ctrl+O** (or **`/expand`**) opens the last turn's full tool output in the pager.
- **Ctrl+V** pastes an image/screenshot from the clipboard — it's saved under
  `.jarn/pastes/` and inserted as an `@path` the agent reads on send.
  Supported on **macOS** (PNG/TIFF/JPEG), **Linux** (Wayland `wl-paste` or X11
  `xclip`), and **Windows** (PowerShell); images over 10 MB are rejected.
  With `execution.inline_images: auto` (the default), an `@`-mentioned image
  (≤ 5 MB) is sent to the model as a **native image content block** in your message
  — so weak vision models see it directly instead of hoping they call `read_file`.
  Set `inline_images: off` for the old text-only `@path` behaviour. If a provider
  rejects images, JARN retries the turn **text-only** once and stops inlining for
  the rest of the session.
- **Esc Esc** (two Esc presses within 500 ms, idle, empty input) opens the **`/rewind`
  picker** — same chord as Claude Code. The first Esc still clears non-empty input;
  only the second Esc on an already-empty buffer fires the picker. After you pick a
  turn, a second arrow-key confirm offers **Restore files too** (revert the working
  tree to that turn's checkpoint, shown as a `git diff --stat` preview) or
  **Conversation only** (leave files as-is). Restoring is itself reversible with
  `/undo`; the file restore needs `git.autocheckpoint` on (otherwise the picker
  quietly rewinds the conversation only, exactly as before).
- **Esc** cancels the running turn. **Ctrl+C** cancels a turn / clears the input,
  and **twice in a row** exits (Claude Code-style). **Ctrl+Q** also quits.
- **Copy text:** the terminal owns selection — just **drag to select and ⌘C**
  (or your terminal's copy), and scroll with your terminal's native scrollback,
  exactly like Claude Code.
- **Notifications:** when a turn takes longer than `ui.notify_min_secs` (default 10 s),
  jarn emits a terminal **bell** (`\a`). Set `ui.notify: desktop` for a native OS
  notification (macOS / Linux), `both` for bell + desktop, or `off` to silence all
  notifications. Approval prompts always ring regardless of elapsed time.
- **Terminal tab title:** jarn sets the terminal-tab title via OSC 2 to show the current
  state — `jarn — <project>` (idle), `✳ jarn — <project>` (working), `⏸ jarn — <project>`
  (waiting for approval). Set `ui.terminal_title: false` to disable.
- **Live plan checklist:** when the agent plans, a `⏺ Todos` checklist appears above the
  input and updates **in place** as items flip (✔ done / ◐ in progress / ☐ pending),
  Claude Code-style, with the streaming reply below it. A long plan is capped (overflow
  collapses to `… +N more`); the full list is committed to scrollback at turn end.

Assistant replies render as **Markdown** (headings, lists, syntax-highlighted code).

`/model`, `/mode`, and `/resume` with no argument open an **arrow-key picker**
(↑/↓ + Enter; Esc cancel). `/model` also offers a custom ref prompt.

While a turn is running, submitted lines are **queued** (shown in the toolbar as
`queue N`); manage them with `/queue`, `/queue clear`, `/queue cancel <n>`, or
`/queue move <from> <to>`.

**Mid-turn steering.** Don't want to wait for the queued line to run next turn?
Steer it **into** the running turn: press **`[s]`** (steer now) on the freshly
queued line, or run `/queue steer <n>` to promote line _n_. The steer is appended
to the conversation as a new user message and the agent sees it **before its next
tool call** — great for course-correcting a long refactor ("actually, use
`pathlib`") without cancelling and re-prompting. Steering re-runs only the
in-flight model step with your guidance (one extra model call); completed tool
results are never re-run, so it never strands a tool call mid-flight. If the turn happens to finish first, the steer
runs as the next turn (never lost). Disable with `ui.steering: false` (hides the
`[s]` affordance; `/queue steer` then declines politely).

### Built-in commands

| Command | Description |
|---|---|
| `/help` | Show available commands and shortcuts. |
| `/init` | Create a JARN.md project context file. |
| `/config` | View or edit settings: /config, /config get <key>, /config set <key> <value> (persists). |
| `/model [/ref\|refresh]` | Show or switch the active model; /model refresh re-queries local endpoints. |
| `/mode [plan\|ask\|auto-edit\|yolo]` | Show or switch the permission mode (plan/ask/auto-edit/yolo). |
| `/theme [dark\|light\|high-contrast\|auto]` | Show or switch the color theme (dark/light/high-contrast/auto). |
| `/sandbox [on\|off]` | Show or toggle the execution backend (local/sandbox). |
| `/key [<key>]` | Set or replace the API key for the current provider (stored in the keychain). |
| `/preset [<preset-name>]` | Show or apply a preset — a shortcut that sets mode + sandbox at once. |
| `/cost` | Show session token usage and cost. |
| `/compact` | Summarize and compact the conversation context. |
| `/expand` | Open the last turn's full tool output in the pager (same as Ctrl+O). |
| `/clear` | Clear the conversation and start a fresh thread. |
| `/sessions` | List and resume previous sessions. |
| `/resume` | Pick a previous session to resume. |
| `/rewind` | Rewind to an earlier turn and continue (forks a new thread); optionally restore files to that turn too. A second arrow-key confirm reverts the working tree to that turn's checkpoint (shown as a `git diff --stat` preview), so conversation and files rewind together. |
| `/skill <name>` | Invoke a skill by name, injecting its instructions into the turn. |
| `/skills` | List available skills. |
| `/memory [search\|show\|add\|update\|delete\|dump] ...` | List, search, show, add, update, delete, or dump long-term memory. |
| `/permissions` | Show current permission rules and allowlist. |
| `/mcp [status\|refresh\|prompts\|prompt <server> <name>\|resources\|read <server> <uri>]` | Show MCP server health; list and invoke server prompts; list and read server resources. |
| `/trust` | Trust this project root and lift the untrusted review-only floor. |
| `/add-dir <path>` | Add a directory to this session's write scope (multi-root; approval-gated). |
| `/queue [clear\|cancel <n>\|move <from> <to>\|steer <n>]` | Show or manage queued input lines (while a turn is running). |
| `/undo` | Revert the last agent turn's file changes. |
| `/redo` | Re-apply the last undone agent turn's file changes. |
| `/abort` | Cancel the running turn and roll back its file changes. |
| `/commit` | Draft a commit message from the current diff and commit (with approval). |
| `/review` | Review the current working-tree diff for bugs and quality (read-only). |
| `/checkpoints` | List recent auto-checkpoints. |
| `/ps [kill <id>]` | List or kill background processes (from run_in_background). |
| `/quit` | Exit J.A.R.N. |
| `/map [focus] [--refresh]` | Show the ranked repo map (codebase overview). |
| `/wiki [search <q>\|list]` | Search or list wiki knowledge-base pages. |
| `/doctor` | Diagnose configuration, providers, and keys. |
| `/telemetry status` | Show telemetry opt-in status and local sink stats. |

## Permission modes

| Mode | File reads | File writes | Shell | Network |
|---|---|---|---|---|
| `plan` | ✅ | ❌ | ❌ | ❌ |
| `ask` (default) | ✅ | ask | ask | ask |
| `auto-edit` | ✅ | ✅ in-scope | ask | ✅ *(read-only)* |
| `yolo` | ✅ | ✅ | ✅ | ✅ |

In **`plan`** mode the agent researches read-only, then presents a concrete plan
(`exit_plan_mode`). Approve it and J.A.R.N. escalates the mode (default `auto-edit`,
configurable via `plan.exit_mode`; the picker also offers `ask`) and carries the plan
out in the same turn — no manual mode switch. Untrusted projects stay clamped to `plan`.

The **danger-guard** overrides all modes: `rm -rf` (incl. `rm -r -f` / `--recursive
--force`), force-push, `git reset --hard`, `mkfs`, fork bombs, out-of-scope writes, etc.
always require explicit confirmation (or are blocked outright). **Esc/Ctrl+C** cancels a
turn *and* kills any shell it spawned. See [docs/PERMISSIONS.md](docs/PERMISSIONS.md).

**Untrusted repos:** a project's `.jarn/config.yaml` can declare hooks, MCP servers, and
providers — capabilities that can run code or read secrets. J.A.R.N. asks you to **trust
a project** before honoring those keys (once per repo); decline and they're ignored while
the session continues safely.

## Configuration

Two tiers, both YAML, merged together (project overrides global):

```
~/.jarn/config.yaml      global: providers, keys (by reference), defaults, budget
.jarn/config.yaml        per-project: MCP servers, hooks, permission rules (committed)
JARN.md                  per-project context, auto-loaded into the system prompt
```

API keys are **referenced, never inlined** — `${ENV_VAR}` or `keychain:jarn/<provider>`.
Project config is gated by a **trust prompt** (see above). See
[docs/CONFIGURATION.md](docs/CONFIGURATION.md) for the full reference.

At startup jarn quietly checks PyPI for a newer release and prints one dim
line under the splash when an upgrade is available (cached 24 h; skipped under
the `offline` preset or when running headless). Disable with
`updates.check: false` in `~/.jarn/config.yaml`.

## Extending

Drop files into `~/.jarn/{skills,commands,agents}` (global) or `.jarn/{...}` (project):

- **Skills** (`skills/*.md`) — reusable knowledge/workflows, auto- or manually-triggered.
- **Commands** (`commands/*.md`) — custom `/slash` prompt templates.
- **Subagents** (`agents/*.md`) — specialist agents the main loop can delegate to.
- **Hooks** (config) — shell commands run on lifecycle events (lint after edit, test before commit).
- **MCP servers** (config) — connect external tool servers (stdio or HTTP).

See [docs/EXTENDING.md](docs/EXTENDING.md) ([quick start](docs/EXTENDING.md#quick-start-wire-skill--hook--mcp)) and [examples/](examples/).

## Documentation

- [Architecture](docs/ARCHITECTURE.md) — how the subsystems fit together
- [Configuration](docs/CONFIGURATION.md) — every config key explained
- [Permissions](docs/PERMISSIONS.md) — modes, rules, danger-guard, approvals
- [Extending](docs/EXTENDING.md) — skills, commands, subagents, hooks, MCP
- [Contributing](docs/CONTRIBUTING.md) — dev setup, tests, conventions
- [Roadmap](docs/ROADMAP.md) — what's in v1 / v1.x and what's next
- [Web UI](docs/WEB_UI.md) — planned, post-launch design
- [Open-core](docs/OPEN_CORE.md) — licensing & business model
- [SPEC.md](SPEC.md) — the original design specification

## Troubleshooting

### Esc Esc rewind feels slow or doesn't register

Terminals encode many keys as ESC-prefixed byte sequences (e.g. arrow keys start
with `\x1b[`). To tell a lone Esc from the start of a sequence, prompt_toolkit
waits a short time (~100 ms) after seeing `\x1b` before delivering it as a bare
Esc keystroke. This is inherent to how terminals work — not a jarn bug — and means
the Esc-Esc chord has a slight delay on the first press. The 500 ms window is
generous enough that a normal double-tap still registers.

If the chord never fires, check that neither the **terminal** nor **tmux/screen** is
eating the second `\x1b` (some multiplexers bind Esc for their own prefix key).

### Terminal ignores OSC 2 title updates

Some terminal emulators do not support OSC 2 (`\x1b]2;…\x07`) or suppress it by default.
jarn's tab-title feature is silently no-op in those environments — no visible side-effect
occurs. If you see stray escape characters in your output, set `ui.terminal_title: false`
in `~/.jarn/config.yaml` to disable the feature entirely.

### Caps Lock inserts a stray `a` (macOS)

On macOS, when Caps Lock is set to switch input source, some terminal apps that
enable the Kitty keyboard protocol's **report-all-keys** mode can leak a stray `a`
into the input. J.A.R.N. disables those flags for Textual (onboarding wizard,
`jarn keys`) and resets any leftover kitty flags before the main REPL starts
(prompt_toolkit does not enable report-all-keys itself).

- Run `jarn keys` (Textual) or `jarn keys --repl` (prompt_toolkit) to see exactly
  what your terminal sends for each key — share a line with a maintainer if you
  hit an unfiltered quirk.
- Set `JARN_KEEP_KITTY_ALL_KEYS=1` to opt out of the fix if you rely on full
  kitty key reporting (e.g. for a custom key-binding workflow).

## Development

```bash
uv sync --extra dev
uv run pytest                 # 2133 tests: logic + mocked-agent + packaging gate
uv run ruff check src tests scripts   # lint
uv run mypy src/              # type-check (CI-gated)
uv run jarn doctor            # sanity-check your environment (add --json for machine output)
uv run jarn bug --dry-run    # write redacted bug report to ~/.jarn/bug-report.md
```

## License

Apache-2.0. See [LICENSE](LICENSE).

Built on [DeepAgents](https://github.com/langchain-ai/deepagents),
[LangGraph](https://github.com/langchain-ai/langgraph), [prompt_toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit),
[Rich](https://github.com/Textualize/rich), and
[Textual](https://github.com/Textualize/textual) (onboarding wizard only).
