Guide

Registry and paths

How resolve expands a template, what a platform tag hides, and why some paths carry a note.

Templates

Paths in the registry are templates. ~ at the start and ${HOME} anywhere become the home directory, ${PROJECT_ROOT} the project root, %VAR% the environment variable of that name. resolve expands them and filters by platform in one pass:

import { getHarness } from "@agntn/harnesses";

const gemini = getHarness("gemini");

gemini.resolve({ platform: "win32", homeDir: "C:\\Users\\dev" }).config.map((p) => p.path);
// [
//   "C:\\Users\\dev/.gemini/settings.json",
//   ".gemini/settings.json",
//   "C:\\ProgramData/gemini-cli/settings.json"   // %PROGRAMDATA% from the environment
// ]

Without options, platform is process.platform, homeDir is os.homedir(), projectRoot is process.cwd(). An unsupported platform string throws; the three that work are linux, darwin and win32. An unset %VAR% stays as written, which is easy to spot and better than an empty string in the middle of a path.

Relative templates stay relative. .claude/skills/ is meant to be joined with whatever project you are looking at, and resolve does not pretend to know which one.

resolvePathTemplate(template, options) is exported on its own for a path you got from somewhere else.

Platforms

A PathCandidate without platforms applies everywhere. One with platforms: ["darwin"] is dropped by resolve on Linux and Windows. The raw arrays on the harness keep every entry, so a tool that wants to show all of them can.

The tags are only set where paths really differ. OpenCode and Freebuff use ~/.config/... on every platform including Windows, and the registry says so in a note instead of inventing an %APPDATA% variant nobody verified.

Notes

A note is there when the path alone would mislead:

  • ~/.claude/projects/<dash-encoded-cwd>/*.jsonl - the placeholder tells you the directory name is derived, not literal.
  • ~/.codex/skills/ - deprecated in favour of ~/.agents/skills/, kept because installs still have it.
  • .claude/skills/ under Cursor, Copilot, OpenCode and Mastra Code - those harnesses read Claude's directory natively, which decides where a shared skill has to live.

Read them. A path without its note is how a shared skill ends up in a directory nobody scans.

Scopes

ScopeMeans
userSettings of one user, under the home directory
projectInside a repository, relative to its root
systemFor the whole machine, usually managed by an administrator
dataTranscripts, histories and databases the harness writes for itself

sessions entries are data. Config files under ~/.config are user, even on the harnesses that keep their database there too.

Detection

harness.detectEnv(); // any of detection.envVars set to something
harness.detectProject(cwd); // any of detection.projectMarkers exists under cwd
harness.isInstalled(); // any of binaries found on PATH
harness.version; // first semver in `<binary> --version`, or null

detectHarness(cwd) combines them: env first, then a single project match, else null. detectHarnessFromEnv() and detectProjectHarnesses(cwd) are the two halves, exported separately.

In the CLI

harnesses info claude          # the raw registry entries
harnesses paths claude         # resolved for this machine
harnesses paths codex --json   # same, as JSON

Or in the explorer, for a platform and home directory that isn't yours.

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