Metadata-Version: 2.4
Name: scopedocs-cli
Version: 0.1.3
Summary: ScopeDocs CLI — explain services, map impact, ask questions across your engineering knowledge graph.
Project-URL: Homepage, https://scopedocs.ai
Project-URL: Documentation, https://guide.scopedocs.app
Project-URL: Source, https://github.com/scopedocs/scopedocs-ai
Project-URL: Issues, https://github.com/scopedocs/scopedocs-ai/issues
Project-URL: Changelog, https://github.com/scopedocs/scopedocs-ai/blob/main/cli/CHANGELOG.md
Author-email: ScopeDocs <support@scopedocs.ai>
Maintainer-email: Long Nguyen <long@scopedocs.ai>
Keywords: agent,cli,developer-tools,documentation,knowledge-graph,mcp,rag,scopedocs
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: packaging>=23.0
Requires-Dist: rich>=13.7
Requires-Dist: textual>=0.79
Requires-Dist: typer>=0.12
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
Requires-Dist: pytest>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# ScopeDocs CLI

Stateless HTTP client for the ScopeDocs knowledge graph. Three core verbs — `ask`, `impact`, `why` — plus auth and feedback. Designed for humans (TUI) and agents (`--json`) with the same code path.

```
$ scopedocs why payment-service

  ᴡʜʏ — payment-service
  ════════════════════════════════════════════════════
  Stripe was chosen over Adyen in 2024-Q3 to keep the
  refund SLA under 5 minutes [1]. Idempotency keys are
  enforced at the edge by checkout-web [2].

  ɢ ᴏ ᴠ ᴇ ʀ ɴ ɪ ɴ ɢ   ᴅ ᴇ ᴄ ɪ ꜱ ɪ ᴏ ɴ ꜱ
    ADR-042  Refund SLA + provider choice    long@
    ADR-051  Idempotency at edge             radha@

  [1] notion://adr-042
  [2] github://checkout-web/pull/1834
```

---

## Install

Requires Python 3.11+.

**macOS / Linux / WSL2:**

```bash
curl -fsSL https://api.scopedocs.ai/install.sh | sh
```

**Windows (PowerShell):**

```powershell
irm https://api.scopedocs.ai/install.ps1 | iex
```

**Manual (any platform):**

```bash
pipx install scopedocs-cli                       # recommended
python3 -m pip install --user scopedocs-cli      # plain pip
```

**Local dev install:**

```bash
cd cli
python3 -m venv .venv
.venv/bin/pip install -e .
```

## Authenticate

Get an API key from the ScopeDocs web UI (Settings → API keys), then:

```bash
scopedocs auth login                  # prompts for the key
scopedocs auth status                 # who am I — key, org, scopes, rate-limit
scopedocs auth status --json          # same, machine-readable
scopedocs auth logout                 # clears saved credentials
```

`auth status` hits `/api/v1/me` and shows your CLI version, key name, org, scopes, rate-limit budget, and last-used timestamp. Use it as a "whoami" probe in CI to verify a key is still valid.

Credentials are saved to `~/.config/scopedocs/config.toml` with `0600` perms. You can also bypass the file with the `SCOPEDOCS_API_KEY` env var or `--api-key` flag.

## Core commands

### `scopedocs ask "<question>"`

Free-form Q&A across all sources (GitHub, Linear, Slack, Notion, code). Streams tokens by default.

```bash
scopedocs ask "why is the refund flow flaky right now?"
scopedocs ask "who owns auth-service on-call?" --json
```

Backend: `POST /api/v1/chat`.

### `scopedocs impact <target>`

Blast-radius map for a service, file, or symbol — direct consumers, pipelines, dashboards, recent incidents (≤90 days), live workarounds.

```bash
scopedocs impact payment-service
scopedocs impact backend/payment/stripe.py
scopedocs impact RefundProcessor.refund
```

Backend: `GET /api/v1/impact/{target}`.

> **Status:** the backend endpoint is not deployed yet. The CLI calls it and exits cleanly with code `4` (not-found) until backend ships it. No canned data fallback.

### `scopedocs why <target>`

Why is this designed this way? Returns governing ADRs, chosen path, rejected alternatives, owners — **decisions only**, not "what does this code do" (that's your coding agent's job).

```bash
scopedocs why payment-service
scopedocs why backend/auth/jwt_auth.py
```

Backend: `GET /api/v1/docs/search?query=...&category=adr` + `GET /api/v1/graph/node/{id}`.

### `scopedocs update`

Check PyPI for a newer release. Detects how the CLI was installed (pipx / pip --user / venv) and prints the matching upgrade command. Pass `--apply` to run it automatically (only safe for pipx — other install methods print the command for you to run manually).

```bash
scopedocs update                      # check + print upgrade command
scopedocs update --apply              # auto-upgrade (pipx only)
scopedocs update --json               # machine-readable
scopedocs update --channel pre        # opt into pre-releases
```

The default `--channel auto` mirrors the channel of the installed version: stable on `0.x.y`, pre-release on `0.x.ybN` / `0.x.yaN`. Pass `--channel pre` to track betas, `--channel stable` to ignore them.

### `scopedocs feedback --good | --bad "<note>"`

Rate the most recent answer. Reads the latest entry from the local trace log, posts it to `/api/v1/feedback` with the original `client_trace_id` so the backend can join it back to the original query for DSPy training.

```bash
scopedocs feedback --good "matched the right ADR"
scopedocs feedback --bad  "missed the live workaround for INC-204"
```

## Interactive TUI

Run with no arguments to open a chat-style Textual app:

```bash
scopedocs
```

Slash commands: `/ask`, `/impact`, `/why`, `/copy` (Ctrl+Y), `/clear` (Ctrl+L), `/help`, `/exit`. Bare input (no leading slash) is treated as `/ask`. `↑`/`↓` walks prompt history.

Copy uses OSC 52 — works over SSH and tmux on iTerm2, Kitty, Alacritty, WezTerm, Ghostty, Windows Terminal. Silently no-ops on macOS Terminal.app and GNOME Terminal.

## Global flags

Apply to every command:

| Flag | Default | Purpose |
|---|---|---|
| `--format text\|json\|markdown` | `text` | Output format. Markdown deferred. |
| `--json` | off | Shortcut for `--format json` |
| `--budget <n>` | `3000` | Token budget hint sent to backend |
| `--workspace <id>` | from config | Override workspace |
| `--api-key <key>` | from env / config | Override auth |
| `--backend <url>` | from config | Override backend URL |
| `--no-color` | auto | Disable ANSI (auto-detected on non-tty) |
| `--quiet` / `-q` | off | Suppress logs |
| `--verbose` / `-v` | off | Print HTTP request/response trace to stderr |
| `--no-context` | off | Skip the local query-enrichment layer (privacy) |
| `--no-trace` | off | Don't append to the local trace log |
| `--session` | off | Attach the last few Q/A turns from the same cwd as conversation history |
| `--no-stream` | off | Force single-shot response (disable SSE streaming) |
| `--version`, `--help` | — | Standard |

## Config

Precedence (high → low): **CLI flag > env var > user config > repo config > built-in default**.

User config (path varies by platform):
- POSIX: `$XDG_CONFIG_HOME/scopedocs/config.toml`, default `~/.config/scopedocs/config.toml`
- Windows: `%APPDATA%/scopedocs/config.toml`

```toml
[default]
workspace = "ws_xxx"
api_key = "sk-sd_xxx"
budget = 3000

[backend]
url = "https://api.scopedocs.ai"  # default — override only for self-hosted / staging
```

Repo override: `.scopedocs/config.toml` at the repo root (commit this for team-wide defaults). Currently honors `workspace`, `repo_alias`, and `privacy.deny`.

Environment variables: `SCOPEDOCS_API_KEY`, `SCOPEDOCS_WORKSPACE`, `SCOPEDOCS_BACKEND_URL`, `SCOPEDOCS_BUDGET`, `SCOPEDOCS_NO_TRACE`.

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Success |
| `1` | Auth required / invalid key, or generic backend error |
| `2` | Usage error (handled by Typer) |
| `3` | Backend unreachable (network / timeout) |
| `4` | Not found (404 — endpoint or target missing) |

All error paths print a clean one-line message; no Python tracebacks reach the user.

## Query enrichment layer

Before every `/api/v1/chat` call, the CLI attaches a small `query_context` payload that the backend cannot otherwise see. This is **query refinement only** — it is *not* the DSPy context distillation, which lives in the backend.

| Provider | Collects |
|---|---|
| `env` | cwd, CLI version, terminal, IDE hint |
| `git` | branch, head sha (12 chars), uncommitted dirty files (cap 20) |
| `ide_selection` | file, line range, highlighted text (cap 1200 chars) |
| `workspace_config` | `.scopedocs/config.toml` workspace, repo alias, privacy denylist |

Total payload is capped at ~2000 chars (≈500 tokens). Eviction order when over: `workspace_config` → `ide_selection.text` → `git.dirty_files[5:]` → drop entire provider. `env` always survives.

**Deliberately not collected:** recent commits, PR descriptions, file content beyond uncommitted changes — those already exist in Layer 2 via the GitHub integration.

Disable with `--no-context` (one-shot) or remove the relevant provider via `.scopedocs/config.toml`.

## Trace log

Every invocation appends one JSONL line to a per-user state file:
- POSIX: `$XDG_STATE_HOME/scopedocs/traces.jsonl`, default `~/.local/state/scopedocs/traces.jsonl`
- Windows: `%LOCALAPPDATA%/scopedocs/traces.jsonl`

```json
{"trace_id": "...", "ts": "...", "command": "ask", "question": "...", "query_context": {...}, "answer_summary": "...", "source_refs": [...], "tokens_used": 0, "backend_request_id": "..."}
```

Perms `0600`. Auto-rotates at 5000 lines. The `feedback` command reads the most recent entry to wire the rating back to the original query.

Disable with `--no-trace` or `SCOPEDOCS_NO_TRACE=1`.

## JSON output

Every command supports `--json` for agent consumption. Stable schemas — see [scopedocs_cli/renderers/json.py](scopedocs_cli/renderers/json.py). Example:

```bash
scopedocs ask "who owns billing?" --json | jq '.sources[].url'
```

`ask` shape:

```json
{
  "command": "ask",
  "question": "string",
  "summary": "string with [1] [2] citations",
  "evidence": [{"source": "...", "system": "github|slack|linear|notion|...", "signal": "...", "ref": "1"}],
  "recommendation": "string",
  "sources": [{"ref": "1", "url": "...", "type": "pr|slack_thread|notion_page|adr|incident", "title": "..."}],
  "tokens_used": 0,
  "tokens_budget": 3000
}
```

Schemas for `impact` and `why` are documented inline in [scopedocs_cli/renderers/json.py](scopedocs_cli/renderers/json.py).

## Verbose mode

`--verbose` enables Warp-style per-request tracing on stderr:

```
→ POST https://api.scopedocs.ai/api/v1/chat
← 200 https://api.scopedocs.ai/api/v1/chat in 412ms
```

Useful when debugging auth/backend issues without enabling Python logging.

## Structure

```
cli/
├── pyproject.toml
├── README.md
└── scopedocs_cli/
    ├── main.py                 # Typer entry, global flags, lazy-imported subcommands
    ├── client.py               # httpx wrapper (4 exception types, streaming SSE)
    ├── config.py               # XDG config loader, save/clear creds
    ├── theme.py                # Terminal Cartography palette
    ├── tui.py                  # Textual chat TUI
    ├── ui.py                   # Rich helpers (shared TUI + one-shot)
    ├── commands/               # ask · impact · why · feedback · auth
    ├── orchestrator/           # query enrichment layer + budget + trace
    │   ├── budget.py
    │   ├── trace.py
    │   └── providers/          # env · git · ide_selection · workspace_config
    └── renderers/              # tui.py + json.py
```

## Tests

```bash
cd cli && python -m pytest tests/ -v
```

73 tests covering providers, budget, trace, client, config, ask command, `auth status` / `me` plumbing, and `update` (version compare, channel filtering, install-method detection). Mocked with `httpx.MockTransport`; no live network. CI runs the matrix on Python 3.11/3.12/3.13 across Ubuntu, macOS, and Windows — see [.github/workflows/cli-tests.yml](../.github/workflows/cli-tests.yml).

## Releasing

Releases are automatic. Merge a PR that bumps the version in `cli/pyproject.toml` (and the matching `cli/scopedocs_cli/__init__.py`) to `main`, and `.github/workflows/cli-release.yml` will:

1. Read the version from `cli/pyproject.toml`.
2. Verify `__init__.py` matches (catch desync mistakes).
3. Skip cleanly if that version already exists on PyPI (idempotent).
4. Run the test suite, build sdist + wheel, validate with `twine check --strict`.
5. Publish to PyPI via Trusted Publishing (OIDC — no API token in secrets).
6. Tag the commit `cli-v<version>` and create a GitHub Release with the artifacts attached.

To force a re-publish (rare — only if you yanked and want to re-upload), use the workflow's `workflow_dispatch` trigger with `force=true`.

**One-time setup** (already done if you can see releases): on PyPI go to project → Settings → Publishing → "Add a new pending publisher" with `owner=scopedocs`, `repo=scopedocs-ai`, `workflow=cli-release.yml`, `environment=pypi-cli-release-d9421f10`. Then create the `pypi-cli-release-d9421f10` environment in GitHub repo settings (no secrets needed; restrict deployment branches to `main`).

See [CHANGELOG.md](CHANGELOG.md) for release history.
