ID@agntn/harnessesv0.4.2

Thirteen agents. One map.

Where each coding agent keeps its config, sessions, instructions, skills and hooks, per platform, with an evidence level on every path. Detect the one you're inside, run a prompt through another, keep every MCP list and AGENTS.md in sync from one file.

Harnesses
13
Linux · macOS · Windows
Paths
208
each with its evidence
Headless
11 of 13
invoke() spawns them

207 checked against the CLI1 inferred

Install$ pnpm add @agntn/harnesses
CallgetHarness("claude").resolve(…)02 / 13linux · 23 paths
Harness / claude

Anthropic Claude Code

Headless -p mode, an advisor without tools, JSONL transcripts per project.

Map

  • config~/.claude/settings.json+5
  • sessions~/.claude/projects/<dash-encoded-cwd>/*.jsonl+1
  • instructionsCLAUDE.md+2
  • skills.claude/skills/+1
  • commands.claude/commands/+1
  • promptTemplates.claude/commands/+1
  • hooks.claude/hooks/+1
  • temp/tmp/claude-<uid>/
Binary
claude
Invoke
6 of 6 modes
Models
no listing
MCP
json · standard
Agents file
~/.claude/CLAUDE.md
Inferred 0 / 23

Every path, with its evidence

getHarness("codex") is one object with config, sessions, instructions, skills, commands, prompt templates and hooks. Nothing is fetched or scanned; it's a table someone checked against the CLI's own docs, source, or a local probe, and wrote the level down. inferred means exactly that. This panel walks through 13 harnesses, values expanded the way the library expands them.

  • Each entry carries scope (user, project, system, data) and level (official, community, inferred)
  • resolve({ platform, homeDir, projectRoot }) expands ~, ${HOME} and %VAR% and drops paths for other platforms
  • A note where the path needs one: dash-encoded cwd, XDG on every platform, deprecated location
Read Registry and paths
Fileclaude.tssame calls · 13 harnesses

Resolve

import { getHarness } from "@agntn/harnesses";// Anthropic Claude Code, binary claudeconst harness = getHarness("claude");const paths = harness.resolve({ platform: "linux", homeDir: "/home/dev" });paths.config[0]?.path;    // "/home/dev/.claude/settings.json"paths.sessions[0]?.path;  // "/home/dev/.claude/projects/<dash-encoded-cwd>/*.jsonl"paths.instructions[0]?.path; // "CLAUDE.md"paths.skills[0]?.path;    // ".claude/skills/"paths.hooks[0]?.path;     // ".claude/hooks/"harness.detection.envVars; // ["CLAUDE_CODE","CLAUDECODE"]

Six modes, none of them a fallback

invoke(prompt, options) expands one argument template per mode and spawns the binary. A harness whose CLI cannot switch tools off rejects advisor mode instead of pretending. The panel shows what would run for the current harness, and the error you get for a mode it doesn't have.

  • tools defaults to false: an advisor with tools removed by a native flag, not by prompt wording
  • readOnly needs a sandbox the CLI enforces itself. No recipe, no run, and an error that says which retry would work
  • timeoutMs and signal stop the whole process group; stopped results keep their output
Read Running another harness
Callinvoke("Review this patch", options)claude · 6 of 6

Modes

  • advisor{}
  • advisor · json{ structured: true }
  • read-only{ readOnly: true }
  • read-only · json{ readOnly: true, structured: true }
  • agent{ tools: true }
  • agent · json{ tools: true, structured: true }

Spawned

claude -p 'Review this patch' --append-system-prompt 'This run has no tools: nothing can be called, run or read, and no tool output will come back. Never write a tool call as text, such as an <invoke> or <function_calls> block. Answer from reasoning alone, and mark any value you did not derive in the answer itself as unverified.' --strict-mcp-config --tools '' --output-format stream-json --verbose --include-partial-messages

Rejected

Noneevery mode has a recipe

One list, every dialect

~/.config/agntn/mcp.jsonc is the only place a server is declared. ~ and ${HOME} expand at sync time because harnesses spawn MCP servers without a shell, and a tilde in a config is a server that silently never starts. TOML files get a surgical edit that keeps their comments. JSON files are rewritten.

  • listMcpServers reads JSON and TOML, standard, OpenCode, VS Code and Antigravity shapes, into one McpServerConfig
  • sync makes each user scope config exactly the master list; extras are removed, not merged around
  • excludes keeps a harness's own servers and pulls master names out of it, unless a server opts back in with its own
Read Syncing MCP servers
File~/.config/agntn/mcp.jsonc
{  "excludes": ["codex"],  "mcpServers": {    "harnesses": { "command": "npx", "args": [/* … */] }  }}

Targets

Claude Code JSON rewritten under mcpServers

Thirteen harnesses, three platforms

Each page lists the binaries, capabilities, invocation templates, every path with its scope and level, the MCP config dialect and what the harness reads from other harnesses' folders. Cursor and Copilot scan .claude/skills/, Grok loads CLAUDE.md. That's written down too, because it decides where your file has to live.

  • Linux, macOS and Windows: Linux, macOS, Windows paths tagged where they differ
  • Detection by environment variable first, then by project markers; two matches is null, not a guess
  • Audio and video mean a verified native route into model context, not a conversion or an MCP tool
Read All harnesses

Eleven tools, three hosts

harnesses mcp serves the tools over stdio, the Pi and OMP extensions render the same executors in the terminal. So Claude can ask Codex for a second opinion on a patch, read-only, and get the answer back as one tool result. That's what the tools are for; the table of paths alone wouldn't need a server.

  • harnesses_detect, harnesses_info, harnesses_models, harnesses_run, and seven for MCP configuration, AGENTS.md, prompts and skills
  • harnesses_run makes the model choose tools explicitly; unsupported modes never widen access
  • The host's own request signal cancels a run, not a JSON argument the model could forget
Read MCP, Pi and OMP
Callharnesses_info("claude")11 tools
Tool / read-only

Anthropic Claude Code

The agent gets the metadata with every path resolved for the machine it runs on.

binaries
claude
invocationModes
6 of 6 true
capabilities
mcp, vision, tools, streaming
modelListing
false
detection
env CLAUDE_CODE, then 4 markers
MCP · Pi · OMP23 paths resolved

Extend Harness, call registerHarness

Every built-in is a concrete class extending the exported abstract Harness. Yours is the same shape, one file. Put level: "inferred" on a path you haven't checked and change it when you have. The type won't let you skip the field, which is the point.

  • id, name, binaries, the six path groups, capabilities, detection and invocation. Same fields the built-ins fill
  • registerHarness(Class) makes it visible to getHarness, detectHarness and the CLI in your process
  • invocation: null is honest for a CLI without a headless mode. invoke() then throws before spawning
Read Custom harnesses
Fileaider.ts
import { Harness, registerHarness, type HarnessId } from "@agntn/harnesses";class Aider extends Harness {  readonly id = "aider" as HarnessId;  readonly config = [    { path: "~/.aider.conf.yml", scope: "user", level: "official" },  ];  /* name, binaries, capabilities, sessions, detection, … */  readonly invocation = { /* args, modelArgs */ level: "inferred" };}registerHarness(Aider);
Startpnpm add @agntn/harnessesNode.js 26 or newer

Start with one command

One install gives you the library, the harnesses CLI and the MCP server. Ask it where an agent keeps something, then go and look.

  • PinPre-1.0, so pin exact versions.
  • EvidenceA path marked inferred is a guess someone wrote down. Check it before you rely on it.
  • LocalNothing is fetched. Detection reads env vars and files, invoke spawns a CLI you installed.

First lookup

$ pnpm add @agntn/harnessesimport { detectHarness, getHarness } from "@agntn/harnesses";detectHarness()?.id;  // "claude" inside Claude Code, else undefinedconst claude = getHarness("claude");claude.config[0]?.path;  // "~/.claude/settings.json", as the registry stores itclaude.invocationModes;  // 6 of 6 true