Guide

Getting Started

Install, read one harness's paths, detect which agent you're inside. The shape every harness shares.
Pre-1.0. The API and the tool list can still move, and so do the upstream CLIs the paths follow. Pin exact versions if you build on it now.

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 twelve 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 22 or newer. The package is ESM only.

First call

paths.ts
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 twelve 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:

FieldTypeWhat it holds
id, name, binariesHarnessId, string, string[]Registry id, display name, executables searched on PATH
config, sessions, instructions, skills, commands, hooksPathCandidate[]Templates with scope, level, optional platforms and note
persistenceStorageDescriptor[]Formats on disk: JSON, JSONL, TOML, SQLite, YAML, Protocol Buffers
capabilitiesHarnessCapabilitiesmcp, vision, audio, video, tools, streaming
detectionHarnessDetectionenvVars and projectMarkers
invocationHarnessInvocation | nullArgument templates per mode, or null without a headless CLI
modelListingHarnessModelListing | nullNative model listing command, when the CLI has one
mcpConfigsMcpConfigFile[]Config files that hold MCP servers, with format, key and dialect
agentsFilestring | nullThe 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).

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 README lists the evidence for those two, harness by harness.

Next

@agntn/harnesses·MIT license· Paths follow the upstream CLIs and carry their evidence level. This site reads nothing from your machine.