# pi-gortex

A [Pi](https://github.com/earendil-works/pi) extension that gives the agent
[Gortex](https://github.com/zzet/gortex)'s code-graph tools, and applies
Gortex's read-discipline decisions to Pi's own tools.

### What is Gortex?

A code-intelligence engine that indexes your repositories into a queryable
knowledge graph of functions, classes, call chains, routes and cross-service
contracts, and serves it over MCP. Agents look up exactly the symbol they
need instead of reading whole files around it, which is where the token
savings come from. It is a single static binary with no dependency chain,
and it covers 257 languages through tree-sitter.

This extension connects the two. It hands `gortex mcp` to Pi's built-in MCP,
and read discipline goes through `gortex hook`.

## What it does

- **Hands Gortex's graph tools to Pi's built-in MCP**, which offers them to
  the model as `mcp__gortex__<tool>`. The rest of the catalogue is discovered
  through the daemon's own `tools_search`.
- **Applies read discipline.** Pi's tool calls are forwarded to
  `gortex hook --agent=pi`, which answers block / soft guidance / nothing
  according to the posture you configured. Gortex owns that logic, so the
  behaviour matches every other agent it integrates with.
- **Briefs the model once per session** with Gortex's orientation for the
  current repository.

See [docs/architecture.md](docs/architecture.md) for how it works.

## Requirements

- **Node 24+**, **Pi 1.0+** with its built-in MCP loaded (not `--no-mcp`)
- **The `gortex` binary** on `PATH`, **v0.61.2 or newer**. See
  [Gortex installation](https://github.com/zzet/gortex/blob/main/docs/installation.md).
  `gortex mcp` starts the shared daemon itself, and the extension never blocks
  Pi's startup on it. It warns you on the first turn when the daemon is older
  than that.
  Run `gortex upgrade` to fix it.

## Install

Add it to Pi's settings, `~/.pi/agent/settings.json` for every project or
`<project>/.pi/settings.json` for one:

```json
{
  "packages": ["npm:pi-gortex"]
}
```

Leave the spec unpinned: Pi skips pinned specs during `pi update`.

For local development, point Pi at a checkout instead: a `packages` entry with
the absolute path, or a single run with `pi -e /path/to/pi-gortex`.

## Configure

Zero configuration required. Every value resolves at runtime, first hit wins:
environment -> `<session cwd>/.pi/gortex.json` -> `<agent dir>/extensions/gortex.json`
(agent dir is `$PI_CODING_AGENT_DIR`, else `~/.pi/agent`) -> defaults.

| Sidecar key    | Environment        | Default            | Meaning |
|----------------|--------------------|--------------------|---------|
| `bin`          | `GORTEX_BIN`       | `gortex` on `PATH` | The gortex binary. Read from the global sidecar only; a project file cannot set it. `~` expands, and a relative path resolves against the sidecar's directory (the launch directory for `GORTEX_BIN`). |
| `hook_mode`    | `GORTEX_HOOK_MODE` | `deny`             | Posture: `deny`, `enrich`, `consult-unlock`, `nudge` (`adaptive-nudge` is an alias). |
| `enforce`      | `GORTEX_ENFORCE`   | `true`             | `false` keeps the graph tools and the briefing, and wires no enforcement. |
| `tools_preset` | `GORTEX_TOOLS`     | daemon's default   | Eager MCP surface: a Gortex preset (`core`, `full`, `agent`, `edit`, `nav`, `readonly`, `localization`), optionally followed by comma-separated tool names and `+tool`/`-tool` changes, e.g. `nav,+edit_file`. A value that does not open with a preset is ignored. |

```json
{
  "bin": "/usr/local/bin/gortex",
  "hook_mode": "enrich",
  "enforce": true
}
```

The extension reads its configuration at every session start, from the
directory the session runs in, so a change takes effect on `/reload` or `/new`.

**Pi's own `mcp.json` wins.** The extension registers a server named `gortex`. A
`gortex` entry in `~/.pi/agent/mcp.json` or `.pi/mcp.json` replaces it,
including its exposure, and Pi's default exposure there is `codemode`, which
keeps the tools out of the model's tool list. Leave Gortex out of `mcp.json`, or
set `"exposure": "direct"` on that entry. A Gortex server configured under
another name runs as a second connection, and read discipline treats its tools
as non-graph calls.

**If the graph tools go missing**, the first turn of the session says so and
why: Pi's MCP support is not loaded, the gortex binary was not found, or Pi
refused the registration. Fix the cause, then `/reload`, or restart Pi with MCP
enabled when it was turned off. The extension fails open, always: an absent
binary or a daemon that is down costs the session its graph tools, never the
session. When the first turn finds no graph tools, Pi's own tools are not held
to read discipline until some appear.

## Develop

```sh
npm install
npm test         # node --test
npm run typecheck
npm run check    # both
```

Zero runtime dependencies. Pi's extension loader resolves Pi's own packages to
the running Pi, so they are imported freely and kept in `devDependencies`, and
the package also loads as a bare directory extension with no `node_modules`
beside it.

The suites drive the extension through Pi's own `ExtensionRunner`;
[docs/tests.md](docs/tests.md) explains the approach and the rules a suite
follows.

## License

Apache License 2.0, the same license as
[gortex](https://github.com/zzet/gortex) itself.
