Getting Started
Why this exists
Every coding agent keeps its state somewhere slightly different. Claude Code writes JSONL transcripts under a dash-encoded copy of your working directory, Codex keeps TOML with comments you'd like to survive an edit, OpenCode puts one SQLite database under XDG on every platform, Gemini CLI has a %PROGRAMDATA% path on Windows and nowhere else. Every tool that reads a session, adds an MCP server or looks for the right AGENTS.md needs that table, and a table kept in thirteen heads is wrong in at least one of them.
So @agntn/harnesses writes it down once. Each harness is a class with the same fields, each path carries a scope and an evidence level, and the same object knows how to detect the harness, run a prompt through it headlessly, and edit its MCP list. Nothing is scanned or guessed at runtime. You get the table, expanded for your platform, and that's it.
Install
pnpm add @agntn/harnesses
Node 26 or newer. The package is ESM only.
First call
import { getHarness } from "@agntn/harnesses";
const claude = getHarness("claude");
claude.name; // "Anthropic Claude Code"
claude.skills; // [{ path: ".claude/skills/", scope: "project", level: "official" }, ...]
const paths = claude.resolve({ platform: "linux", homeDir: "/home/dev" });
paths.config[0]?.path; // "/home/dev/.claude/settings.json"
getHarness wants an exact id and throws on anything else. isHarnessId(value) narrows a string first, listHarnesses() gives you the ids, getAllHarnesses() the objects. Importing the package registers the thirteen built-ins; registerHarness adds yours.
Which one am I in
import { detectHarness, detectProjectHarnesses } from "@agntn/harnesses";
const active = detectHarness();
active?.id; // "claude" when CLAUDE_CODE is set, null when nothing is unambiguous
detectProjectHarnesses("/srv/app").map((h) => h.id); // every harness with a marker in that directory
Environment variables win, because being inside a harness is unambiguous. Without one, a single project marker decides; two harnesses with markers in the same directory give null, not the first one. A CLAUDE.md next to an AGENTS.md is the common case, and picking one there would be a lie.
The shape
Every harness has these:
| Field | Type | What it holds |
|---|---|---|
id, name, binaries | HarnessId, string, string[] | Registry id, display name, executables searched on PATH |
config, sessions, instructions, skills, commands, hooks | PathCandidate[] | Templates with scope, level, optional platforms and note |
promptTemplates | PathCandidate[] | Reusable prompt file locations, including custom command equivalents |
temp | PathCandidate[] | Where the harness writes its own temp files, when verified |
envOverrides | EnvOverride[] | Variables that move a root, with the default root and the categories under it |
persistence | StorageDescriptor[] | Formats on disk: JSON, JSONL, TOML, SQLite, YAML, Protocol Buffers |
capabilities | HarnessCapabilities | mcp, vision, audio, video, tools, streaming |
detection | HarnessDetection | envVars and projectMarkers |
invocation | HarnessInvocation | null | Argument templates per mode, or null without a headless CLI |
modelListing | HarnessModelListing | null | Native model listing command, when the CLI has one |
mcpConfigs | McpConfigFile[] | Config files that hold MCP servers, with format, key and dialect |
agentsFile | string | null | The one user scope instructions file that syncAgentsFiles links |
And these methods: resolve(options), detectEnv(), detectProject(cwd), isInstalled(), version, invocationModes, buildInvocation(prompt, options), invocationError(options), invoke(prompt, options), buildModelListInvocation(search), listModels(options), modelSelector(model).
Evidence
level is official when the path comes from the CLI's documentation or source, community when it comes from a maintained outside source, inferred when someone reasoned it out from the others and hasn't verified it. platforms is set only where a path differs by OS. scope is user, project, system or data, and data is where transcripts and databases live as opposed to settings you edit.
A false in capabilities.audio or video means no native route into model context was verified, not that every provider and extension was disproved. The harness pages record what was verified.
Next
- Registry and paths: resolve, platforms, what the notes are for.
- Running another harness: six modes, timeouts, cancellation, models.
- MCP servers: one master list, every dialect.
- Instructions files: one
AGENTS.mdbehind every harness's global file. - CLI: the
harnessescommand. - Agents: eleven tools over MCP, Pi and OMP.
- Sessions: typed schemas for the transcript formats.
- Custom harnesses: extend
Harness, register it. - Explorer: every path, expanded in the page.