@agntn/harnesses 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,158 +5,172 @@
5
5
  [![license](https://npmx.dev/api/registry/badge/license/@agntn/harnesses)](https://npmx.dev/package/@agntn/harnesses)
6
6
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/agntn/harnesses)
7
7
 
8
- Metadata toolkit for AI coding harnesses. One registry of paths, formats, and detection rules for every major CLI.
8
+ 🧭 Thirteen coding CLIs, one map. You ask where Claude keeps skills, you get the path.
9
9
 
10
- Docs: [harnesses.agntn.dev](https://harnesses.agntn.dev)
10
+ > [!WARNING]
11
+ > **@agntn/harnesses is pre-1.0.** Paths follow upstream CLIs that still move. Building on it now means pinning the version.
11
12
 
12
- ## Install
13
+ ## Why?
14
+
15
+ Claude stores transcripts under a mangled copy of your cwd. Codex keeps TOML with comments you wanted to keep. Ask a model where Codex lives and it invents `.claude/`. So this is one registry: thirteen harnesses, same object, paths for your machine.
16
+
17
+ The rest of it sits on [harnesses.agntn.dev](https://harnesses.agntn.dev).
18
+
19
+ ## ✨ Features
20
+
21
+ - 🧩 **Thirteen harnesses, one class.** Same fields on Claude, Codex, Pi and the rest.
22
+ - 📂 **Every path has a receipt.** Scope, evidence level, and a platform tag when the OS actually differs.
23
+ - 🔎 **Detects which CLI you're inside.** Environment variables first. Two project markers in one directory is `null`, not a guess.
24
+ - ▶️ **Headless runs with real modes.** Advisor without tools, full agent, or a native read-only sandbox. A mode the CLI cannot enforce is rejected.
25
+ - 🔌 **MCP across the dialects.** One master list at `~/.config/agntn/mcp.jsonc`. TOML edits keep the comments.
26
+ - 🔗 **One AGENTS.md behind the global files.** Symlinks, so an edit through Claude or Gemini is the same bytes.
27
+ - 📜 **Session types when the format is stable.** JSONL, SQLite, JSON. Unstable shapes stay `unknown`.
28
+ - 🤖 **Nine tools, three doors.** MCP, Pi and OMP call the same executors.
29
+
30
+ ## 📦 Install
13
31
 
14
32
  ```bash
15
33
  pnpm add @agntn/harnesses
16
34
  ```
17
35
 
18
- ## Usage
36
+ Node.js 24 or newer.
19
37
 
20
- ```ts
21
- import { getHarness, detectHarness, detectProjectHarnesses } from "@agntn/harnesses";
38
+ ## 🚀 First call
22
39
 
23
- const claude = getHarness("claude");
24
- console.log(claude.skills); // [{ path: ".claude/skills/", scope: "project", ... }, ...]
25
- console.log(claude.hooks); // [{ path: ".claude/hooks/", scope: "project", ... }, ...]
26
- console.log(claude.invocationModes); // advisor and full agent modes, no read-only mode
40
+ ```bash
41
+ npx @agntn/harnesses detect
42
+ ```
43
+
44
+ ```
45
+ System Scan
46
+
47
+ ● antigravity Google Antigravity CLI v1.2.5
48
+ ● claude Anthropic Claude Code v2.1.276
49
+ ● codex OpenAI Codex CLI v0.154.0
50
+ ○ cursor Cursor
51
+ ● freebuff Freebuff
52
+ ○ gemini Google Gemini CLI
53
+ ○ github-copilot GitHub Copilot
54
+ ● grok xAI Grok CLI v1.0.34
55
+ ● mastracode Mastra Code
56
+ ● omp OMP (oh-my-pi) v18.2.4
57
+ ● opencode OpenCode CLI v2.0.5
58
+ ● prime-agent Prime Agent v0.9.5
59
+ ● pi Pi Coding Agent v0.85.1
60
+ ```
61
+
62
+ No key, no config. No network either. `detect` looks at `PATH`. Filled dot is installed, hollow is not. After `pnpm add`, the same command is `pnpm exec harnesses`, or install it once with `pnpm add -g @agntn/harnesses`.
63
+
64
+ Same binary, more commands:
65
+
66
+ ```bash
67
+ harnesses list
68
+ harnesses info claude
69
+ harnesses paths pi
70
+ harnesses models pi gpt-5.4 --json
71
+ harnesses run claude "review this design"
72
+ harnesses run codex --read-only "review this"
73
+ harnesses mcp-servers list
74
+ harnesses agents sync --check
75
+ ```
76
+
77
+ `run` without `--tools` is the advisor. `--tools` is the full agent. `--read-only` asks the CLI for a sandbox and implies tools. Timeouts, `--cwd` and `--model` sit in the [CLI guide](https://harnesses.agntn.dev/guide/cli).
27
78
 
28
- const codex = getHarness("codex");
29
- await codex.invoke("Review this patch", { readOnly: true, timeoutMs: 60_000 });
79
+ ### Commands
30
80
 
31
- const pi = getHarness("pi");
32
- const { models } = await pi.listModels({ search: "gpt-5.4" });
33
- console.log(models); // [{ provider: "openai-codex", id: "gpt-5.4", ... }]
34
- await pi.invoke("Review this patch", { model: "openai-codex/gpt-5.4", readOnly: true });
81
+ | Command | What it does | Example |
82
+ | ------------- | ------------------------------------------------------ | ------------------------------------------- |
83
+ | `list` | Every known harness, id and name | `harnesses list` |
84
+ | `detect` | Which ones are installed, with versions | `harnesses detect` |
85
+ | `info` | Registry entry: modes, capabilities, path templates | `harnesses info claude` |
86
+ | `paths` | Those templates expanded for this machine | `harnesses paths pi` |
87
+ | `models` | Models the harness can use, through its native listing | `harnesses models pi` |
88
+ | `run` | One prompt through headless mode | `harnesses run claude "review this design"` |
89
+ | `mcp-servers` | MCP servers across the config dialects | `harnesses mcp-servers list` |
90
+ | `agents sync` | Link global instructions files to one master | `harnesses agents sync --check` |
91
+ | `mcp` | The MCP server on stdio | `harnesses mcp` |
35
92
 
36
- // Resolve to absolute paths for current platform
93
+ `list`, `detect`, `info`, `paths` and `models` take `--json` or `--toon`. `run --json` is different: that one is the harness's own structured output.
94
+
95
+ ## 🧠 Library
96
+
97
+ ```ts
98
+ import { getHarness, detectHarness } from "@agntn/harnesses";
99
+
100
+ const claude = getHarness("claude");
37
101
  const paths = claude.resolve({ platform: "linux", homeDir: "/home/dev" });
38
- console.log(paths.config); // [{ path: "/home/dev/.claude/settings.json", ... }, ...]
102
+ console.log(paths.skills);
39
103
 
40
- // Detect which agent is running (env vars first, then project markers)
41
104
  const active = detectHarness();
42
- if (active) {
43
- console.log(`Running inside ${active.name}`);
44
- }
105
+ if (active) console.log(active.id);
45
106
 
46
- // Find all agents configured in a project directory
47
- const harnesses = detectProjectHarnesses("/path/to/project");
107
+ await getHarness("codex").invoke("Review this patch", { readOnly: true });
48
108
  ```
49
109
 
50
- Session schemas are typed per agent, so you get structure when parsing JSONL/SQLite/JSON files:
110
+ That's most of it, really. `getHarness` wants an exact id. `detectHarness` uses env vars first, then a single project marker. `invoke()` talks to the CLI. A mode the CLI cannot run comes back as an error, not a quieter one. The rest: [Registry](https://harnesses.agntn.dev/guide/registry), [Invoke](https://harnesses.agntn.dev/guide/invoke), [MCP servers](https://harnesses.agntn.dev/guide/mcp-servers), [Instructions files](https://harnesses.agntn.dev/guide/agents-sync).
51
111
 
52
- ```ts
53
- import type { ClaudeSessionEntry, CodexThread, GeminiConversationRecord } from "@agntn/harnesses";
54
- ```
112
+ ## 🗺️ Harnesses
55
113
 
56
- ## Supported agents
57
-
58
- | Agent | ID | Detection | Skills | Hooks | Sessions |
59
- | --------------- | ---------------- | ------------- | --------------------- | ------------------------ | -------------- |
60
- | Antigravity CLI | `antigravity` | project | `.agents/skills/` | - | JSONL + SQLite |
61
- | Claude Code | `claude` | env + project | `.claude/skills/` | `.claude/hooks/` | JSONL |
62
- | Codex CLI | `codex` | project | `.agents/skills/` | - | SQLite + JSONL |
63
- | Gemini CLI | `gemini` | env + project | `.gemini/skills/` | - | JSON |
64
- | Grok CLI | `grok` | env + project | `.grok/skills/` | `.grok/hooks/` | TOML + JSONL |
65
- | OpenCode | `opencode` | project | `.opencode/skills/` | - | SQLite |
66
- | Cursor | `cursor` | env + project | `.cursor/skills/` | - | - |
67
- | GitHub Copilot | `github-copilot` | env + project | `.github/skills/` | - | - |
68
- | Mastra Code | `mastracode` | project | `.mastracode/skills/` | `.mastracode/hooks.json` | SQLite |
69
- | OMP (oh-my-pi) | `omp` | env + project | `.omp/skills/` | - | JSONL + SQLite |
70
- | Pi Coding Agent | `pi` | env + project | `.pi/skills/` | - | JSON + JSONL |
71
- | Freebuff | `freebuff` | project | `.agents/skills/` | - | JSON + JSONL |
72
-
73
- ### Native audio and video input
74
-
75
- `audio` and `video` report whether the harness has a verified native route that puts that medium into model context. External conversion, MCP tools, and voice dictation that becomes text do not count. `false` means no native route was verified, not that every possible provider or extension was disproved.
76
-
77
- | Agent | Audio | Video | Evidence boundary |
78
- | --------------- | :---: | :---: | --------------------------------------------------------------------------- |
79
- | Antigravity CLI | Yes | Yes | Native attachments; documented audio formats and direct video pasting |
80
- | Gemini CLI | Yes | No | The `read_file` tool supports audio; native video support is not documented |
81
- | Claude Code | No | No | No verified native route |
82
- | Codex CLI | No | No | No verified native route |
83
- | Grok CLI | No | No | Its ACP parser recognizes audio blocks, but the runtime rejects them |
84
- | OpenCode | No | No | Its attachment documentation explicitly excludes audio and video |
85
- | Cursor | No | No | Voice input is transcribed to text |
86
- | GitHub Copilot | No | No | Voice input is transcribed locally to text |
87
- | Mastra Code | No | No | No verified native route |
88
- | OMP (oh-my-pi) | No | No | No verified native route |
89
- | Pi Coding Agent | No | No | No verified native route |
90
- | Freebuff | No | No | No verified native route |
91
-
92
- Primary references: [Antigravity prompting](https://antigravity.google/docs/cli/prompting/), [Antigravity changelog](https://github.com/google-antigravity/antigravity-cli/blob/main/CHANGELOG.md), [Gemini CLI tools](https://github.com/google-gemini/gemini-cli/blob/main/docs/reference/tools.md), [Gemini CLI video request](https://github.com/google-gemini/gemini-cli/issues/27194), [OpenCode attachments](https://opencode.ai/v2/docs/attachments/), [Cursor prompting](https://cursor.com/docs/agent/prompting), and [Copilot CLI voice input](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli/voice-input).
93
-
94
- `invoke()` and `listModels()` accept `timeoutMs` and `signal?: AbortSignal`. Unset or `0` means no deadline. An already aborted signal skips spawning. Otherwise, the first cancellation or deadline starts cleanup: Linux and macOS use a dedicated process group, with `SIGTERM` followed by `SIGKILL` after 500 ms even if the root has exited. Windows uses `taskkill /T /F` immediately, with a 2 s budget for that command. Cleanup failures reject the call.
95
-
96
- Stopped results retain captured output and set `exitCode: null`. Caller cancellation sets `aborted: true`, a deadline sets `timedOut: true`, and the first reason wins. Both flags are false on normal completion. Later aborts do nothing, and cancelled model listings return no parsed models. Pi, OMP, and MCP tools forward their host request signal, not a JSON argument supplied by the model.
114
+ | ID | Name | Project skills |
115
+ | ---------------- | ---------------------- | ---------------------- |
116
+ | `antigravity` | Google Antigravity CLI | `.agents/skills/` |
117
+ | `claude` | Anthropic Claude Code | `.claude/skills/` |
118
+ | `codex` | OpenAI Codex CLI | `.codex/skills/` |
119
+ | `cursor` | Cursor | `.cursor/skills/` |
120
+ | `freebuff` | Freebuff | `.agents/skills/` |
121
+ | `gemini` | Google Gemini CLI | `.gemini/skills/` |
122
+ | `github-copilot` | GitHub Copilot | `.github/skills/` |
123
+ | `grok` | xAI Grok CLI | `.grok/skills/` |
124
+ | `mastracode` | Mastra Code | `.mastracode/skills/` |
125
+ | `omp` | OMP (oh-my-pi) | `.omp/skills/` |
126
+ | `opencode` | OpenCode CLI | `.opencode/skills/` |
127
+ | `prime-agent` | Prime Agent | `.prime/agent/skills/` |
128
+ | `pi` | Pi Coding Agent | `.pi/skills/` |
97
129
 
98
- ```ts
99
- const controller = new AbortController();
100
- const pending = getHarness("pi").invoke("Review this change", {
101
- tools: true,
102
- readOnly: true,
103
- signal: controller.signal,
104
- });
105
- controller.abort();
106
- const result = await pending;
107
- console.log(result.aborted);
108
- ```
130
+ That's the project directory. Most of them also keep a copy under your home directory, and a few read someone else's skills folder on purpose. Sessions, hooks, audio, video, the whole sheet: [Harnesses](https://harnesses.agntn.dev/harnesses).
109
131
 
110
- This is command cleanup, not a sandbox. Descendants that leave the POSIX process group, or outlive an already exited root on Windows, cannot be reliably reached by these mechanisms. Inherited output pipes do not extend the wait after cleanup. Scheduling and OS delays can exceed the stated budgets.
132
+ ## 🤖 Agents
111
133
 
112
- Each agent is a concrete subclass of the abstract `Harness` class. Custom subclasses can be added with `registerHarness`. Every harness exposes config paths, session locations, instruction files, skills dirs, hooks, commands, persistence formats, capabilities (MCP, vision, audio, video, tools, streaming), detection rules, a normalized non-interactive invocation (`harness.invoke(prompt, { model })`) where the CLI has a headless mode, native model listing (`harness.listModels()`) where the CLI supports it, and its MCP server config files (`listMcpServers`/`addMcpServer`/`removeMcpServer` normalize the dialects; writes rewrite JSON and surgically edit TOML with comments preserved). `syncMcpServers` treats `~/.config/agntn/mcp.jsonc` (JSONC, XDG-aware) as the single source of truth and resets every harness's user-scope MCP config to exactly that list; a top-level `"excludes": ["codex"]` array opts individual harnesses out of the sync (their own servers stay, master-listed names are withdrawn), and `~`/`${HOME}` in commands, args, and env values expand to absolute paths at sync time (harnesses spawn MCP servers without a shell). `syncAgentsFiles` links every harness's global instructions file (CLAUDE.md/AGENTS.md/GEMINI.md) to one master file as symlinks, so an edit made through any harness lands in the single physical copy; `~/.config/agntn/agents.jsonc` sets the `source`, `companions`, and `excludes`, diverged regular files are backed up and relinked, and check mode reports without writing. Companion paths are relative to the source directory and are linked at the same relative path beside each harness target.
134
+ ```bash
135
+ harnesses mcp
136
+ pi install npm:@agntn/harnesses
137
+ omp install @agntn/harnesses
138
+ ```
113
139
 
114
- ```jsonc
140
+ ```json
115
141
  {
116
- "source": "bundle/AGENTS.md",
117
- "companions": ["RULES.md"],
118
- "excludes": ["codex"],
142
+ "mcpServers": {
143
+ "harnesses": { "command": "npx", "args": ["-y", "@agntn/harnesses", "mcp"] }
144
+ }
119
145
  }
120
146
  ```
121
147
 
122
- All paths carry `scope` (user/project/system/data), `level` (official/community/inferred), and optional `platforms` tags.
148
+ Nine tools, the same nine on MCP, Pi and OMP. `harnesses_detect`, `harnesses_info` and `harnesses_mcp_list` only read. `harnesses_run` is the one that can spend tokens. `tools` is required, so the model has to pick advisor or agent. What each call returns is on the [Agents page](https://harnesses.agntn.dev/guide/agents).
123
149
 
124
- The `harnesses_info` agent tool accepts one harness id or a batch of up to 20 ids. Batch results keep the input order and include errors for unknown ids beside successful metadata.
150
+ ## 🚫 What this does not do
125
151
 
126
- ## CLI
152
+ It does not install skills, drive a browser, or run a sandbox of its own. `invoke()` is the harness CLI plus process cleanup. The wide, thin agent list is [unagent](https://github.com/onmax/unagent).
127
153
 
128
- ```bash
129
- harnesses list # all known harnesses
130
- harnesses detect # which ones are installed + versions
131
- harnesses info claude # metadata, including supported invocation modes
132
- harnesses paths claude # resolved paths for current platform
133
- harnesses info codex --json # machine-readable output
134
- harnesses models pi # models available to Pi
135
- harnesses models pi gpt-5.4 --json
136
- harnesses run claude "review this design" # advisor without tools mode (default)
137
- harnesses run pi --model openai-codex/gpt-5.4 "review this design"
138
- harnesses run claude --tools "fix lint" # full agent with tools enabled
139
- harnesses run codex --read-only "review this" # tools inside a native read-only sandbox
140
- harnesses mcp-servers list # MCP servers configured across all harnesses
141
- harnesses mcp-servers add omp probe --command node --args "srv.mjs mcp"
142
- harnesses mcp-servers remove omp probe
143
- harnesses mcp-servers sync # reset all harnesses to ~/.config/agntn/mcp.jsonc
144
- harnesses agents sync --check # doctor: link global AGENTS.md files to one master
145
- harnesses mcp # run the MCP server over stdio
146
- ```
147
-
148
- `tools` defaults to `false` in the library and CLI. The MCP, Pi, and OMP tools require agents to choose it explicitly. `false` must use a native CLI flag that removes tools from the model context; it is a lightweight advisor, not an agent constrained only by prompt wording. Set `tools: true` (or CLI `--tools`) whenever the task needs harness tools, including Grok's native X search. Add `readOnly: true` when those tools must stay inside a sandbox enforced by the harness CLI; the agent tools pass it beside `tools: true`, while the library and CLI let it imply tools. Read-only mode is rejected when a harness has no verified native recipe, so it never falls back to broader access. A recipe can also carry the lowest CLI version whose enforcement was verified, and `invoke()` rejects read-only runs on older or unknown versions: Grok runs `--sandbox read-only` from 1.0.13, and Claude Code keeps read-only runs on `Read`, `Glob` and `Grep` with `--strict-mcp-config` from 2.1.175. Harnesses whose CLI cannot disable tools reject advisor mode instead of silently running an agent and return an explicit `tools` retry when their full agent mode can handle the request.
154
+ ## 🧩 Adding a harness
149
155
 
150
- ## How harnesses compares to unagent
156
+ Want a fourteenth? One class extending `Harness`, then `registerHarness`. `getHarness`, the CLI and the tools pick it up. How to write that class: [Custom harnesses](https://harnesses.agntn.dev/guide/custom).
151
157
 
152
- [unagent](https://github.com/onmax/unagent) covers similar ground but makes different tradeoffs.
158
+ ## 🛠️ Development
153
159
 
154
- **harnesses is deep and narrow.** Each harness gets verified, platform-specific paths with scope, evidence level, and platform tags. Session formats are typed per harness. Twelve harnesses, each fully mapped.
160
+ ```bash
161
+ pnpm install
162
+ pnpm lint # builds first, then oxlint and oxfmt --check
163
+ pnpm lint:fix
164
+ pnpm typecheck
165
+ pnpm test:run
166
+ pnpm build # obuild
167
+ pnpm docs # the Docus site, bundles src/ itself
168
+ ```
155
169
 
156
- **unagent is wide and shallow.** 40+ agents detected by env vars, but each definition is just `configDir` + `rulesFile` + `skillsDir`. No platform-specific paths, no session schemas. In exchange, it ships runtime primitives harnesses doesn't touch yet: skill install/uninstall, vector stores, browser automation, sandboxes, queues, workflows.
170
+ ## 💛 Thanks
157
171
 
158
- harnesses tells you _where coding harnesses live and what format their data uses_. unagent tells you _which agent is running_ and gives you tools to _do things_ with skills. They could use each other.
172
+ Anthropic and OpenAI both run programs this package was built with. [Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) and [Codex for Open Source](https://developers.openai.com/community/codex-for-oss). Thank you <3
159
173
 
160
- ## License
174
+ ## 📄 License
161
175
 
162
176
  [MIT](./LICENSE)
@@ -1,20 +1,20 @@
1
- declare const version: string;
2
- type HarnessId = "antigravity" | "codex" | "gemini" | "grok" | "claude" | "opencode" | "cursor" | "freebuff" | "github-copilot" | "mastracode" | "omp" | "pi";
3
- type EvidenceLevel = "official" | "community" | "inferred";
4
- type Platform = "linux" | "darwin" | "win32";
5
- interface PathCandidate {
1
+ export declare const version: string;
2
+ export type HarnessId = "antigravity" | "codex" | "gemini" | "grok" | "claude" | "opencode" | "cursor" | "freebuff" | "github-copilot" | "mastracode" | "omp" | "pi" | "prime-agent";
3
+ export type EvidenceLevel = "official" | "community" | "inferred";
4
+ export type Platform = "linux" | "darwin" | "win32";
5
+ export interface PathCandidate {
6
6
  path: string;
7
7
  scope: "user" | "project" | "system" | "data";
8
8
  level: EvidenceLevel;
9
9
  platforms?: Platform[];
10
10
  note?: string;
11
11
  }
12
- interface StorageDescriptor {
12
+ export interface StorageDescriptor {
13
13
  format: string;
14
14
  level: EvidenceLevel;
15
15
  note?: string;
16
16
  }
17
- interface HarnessCapabilities {
17
+ export interface HarnessCapabilities {
18
18
  mcp: boolean;
19
19
  vision: boolean;
20
20
  /** Audio reaches the model without conversion or MCP. */
@@ -25,7 +25,7 @@ interface HarnessCapabilities {
25
25
  streaming: boolean;
26
26
  }
27
27
  /** How to run one prompt through the harness non-interactively. */
28
- interface HarnessInvocation {
28
+ export interface HarnessInvocation {
29
29
  /** Binary to spawn; defaults to the harness's first `binaries` entry. */
30
30
  binary?: string;
31
31
  /** Full agent argument template; every "{prompt}" is replaced with the prompt text. */
@@ -48,7 +48,7 @@ interface HarnessInvocation {
48
48
  note?: string;
49
49
  }
50
50
  /** Invocation modes a harness supports without fallback or prompt-only restrictions. */
51
- interface HarnessInvocationModes {
51
+ export interface HarnessInvocationModes {
52
52
  advisor: boolean;
53
53
  advisorStructured: boolean;
54
54
  readOnly: boolean;
@@ -57,7 +57,7 @@ interface HarnessInvocationModes {
57
57
  agentStructured: boolean;
58
58
  }
59
59
  /** How to ask one harness CLI for the models currently available to it. */
60
- interface HarnessModelListing {
60
+ export interface HarnessModelListing {
61
61
  /** Arguments used when no search filter is supplied. */
62
62
  args: string[];
63
63
  /** Optional argument template for a search filter; every "{search}" is replaced. */
@@ -66,7 +66,7 @@ interface HarnessModelListing {
66
66
  note?: string;
67
67
  }
68
68
  /** One model normalized from a harness's native model-listing output. */
69
- interface AvailableModel {
69
+ export interface AvailableModel {
70
70
  provider: string;
71
71
  id: string;
72
72
  contextWindow: number;
@@ -75,7 +75,7 @@ interface AvailableModel {
75
75
  images: boolean;
76
76
  }
77
77
  /** Options for querying the models available to one harness. */
78
- interface ListModelsOptions {
78
+ export interface ListModelsOptions {
79
79
  search?: string;
80
80
  cwd?: string;
81
81
  env?: Record<string, string>;
@@ -84,7 +84,7 @@ interface ListModelsOptions {
84
84
  /** Cancel with the same process cleanup as a timeout. */
85
85
  signal?: AbortSignal;
86
86
  }
87
- interface InvokeOptions {
87
+ export interface InvokeOptions {
88
88
  cwd?: string;
89
89
  env?: Record<string, string>;
90
90
  /** Harness-native model id or selector. */
@@ -100,7 +100,7 @@ interface InvokeOptions {
100
100
  /** Use the harness's structured (JSON) output mode instead of plain text. */
101
101
  structured?: boolean;
102
102
  }
103
- interface InvokeResult {
103
+ export interface InvokeResult {
104
104
  command: string;
105
105
  args: string[];
106
106
  stdout: string;
@@ -112,16 +112,21 @@ interface InvokeResult {
112
112
  aborted: boolean;
113
113
  }
114
114
  /** Result of one native model-listing command. */
115
- interface ListModelsResult extends InvokeResult {
115
+ export interface ListModelsResult extends InvokeResult {
116
116
  /** Empty on a successful no-match response or when the command itself failed. */
117
117
  models: AvailableModel[];
118
118
  }
119
119
  /** Normalized MCP server entry, shared across every harness dialect. */
120
- interface McpServerConfig {
120
+ export interface McpServerConfig {
121
121
  name: string;
122
122
  transport: "stdio" | "http" | "sse";
123
123
  command?: string;
124
124
  args?: string[];
125
+ /**
126
+ * Environment for stdio servers. A value that is exactly "${NAME}" refers to
127
+ * the variable NAME of the harness environment: dialects with native
128
+ * references keep it, the others resolve it when the entry is written.
129
+ */
125
130
  env?: Record<string, string>;
126
131
  url?: string;
127
132
  headers?: Record<string, string>;
@@ -129,25 +134,28 @@ interface McpServerConfig {
129
134
  enabled?: boolean;
130
135
  }
131
136
  /** How one harness config file stores its MCP servers. */
132
- interface McpConfigFile extends PathCandidate {
137
+ export interface McpConfigFile extends PathCandidate {
133
138
  format: "json" | "toml";
134
139
  /** Object path to the server map inside the file, e.g. ["mcpServers"]. */
135
140
  key: string[];
136
- /** Shape of individual entries; "standard" is the {command, args, env, url} family. */
137
- dialect: "standard" | "antigravity" | "opencode" | "vscode";
141
+ /**
142
+ * Shape of individual entries; "standard" is the {command, args, env, url}
143
+ * family and "prime" is that family with env values as {"env": "NAME"} references.
144
+ */
145
+ dialect: "standard" | "antigravity" | "opencode" | "prime" | "vscode";
138
146
  }
139
- interface HarnessDetection {
147
+ export interface HarnessDetection {
140
148
  /** Environment variables that indicate running inside this agent. */
141
149
  envVars: string[];
142
150
  /** Project-level files or directories whose presence indicates this agent. */
143
151
  projectMarkers: string[];
144
152
  }
145
- interface ResolveOptions {
153
+ export interface ResolveOptions {
146
154
  homeDir?: string;
147
155
  projectRoot?: string;
148
156
  platform?: Platform;
149
157
  }
150
- interface ResolvedPaths {
158
+ export interface ResolvedPaths {
151
159
  config: PathCandidate[];
152
160
  sessions: PathCandidate[];
153
161
  instructions: PathCandidate[];
@@ -161,7 +169,7 @@ type InvocationOptions = Readonly<{
161
169
  tools?: boolean;
162
170
  readOnly?: boolean;
163
171
  }>;
164
- declare abstract class Harness {
172
+ export declare abstract class Harness {
165
173
  abstract readonly id: HarnessId;
166
174
  abstract readonly name: string;
167
175
  abstract readonly binaries: string[];
@@ -271,9 +279,9 @@ declare abstract class Harness {
271
279
  resolveCandidates<T extends PathCandidate>(entries: readonly T[], options?: ResolveOptions): T[];
272
280
  resolve(options?: ResolveOptions): ResolvedPaths;
273
281
  }
274
- type HarnessConstructor = new () => Harness;
282
+ export type HarnessConstructor = new () => Harness;
275
283
  /** One resolved config file together with the servers it declares. */
276
- interface McpConfigListing {
284
+ export interface McpConfigListing {
277
285
  path: string;
278
286
  scope: McpConfigFile["scope"];
279
287
  format: McpConfigFile["format"];
@@ -284,6 +292,10 @@ interface McpConfigListing {
284
292
  /** Set when the file exists but could not be parsed. */
285
293
  error?: string;
286
294
  }
295
+ /** Thrown when a server's env cannot be written the way the target dialect requires. */
296
+ export declare class McpEnvError extends Error {
297
+ readonly name = "McpEnvError";
298
+ }
287
299
  /**
288
300
  * Lists the MCP servers a harness has configured, per declared config file.
289
301
  * Missing files come back with `exists: false`; unparsable ones carry `error`.
@@ -292,7 +304,7 @@ interface McpConfigListing {
292
304
  * @param options - Platform and path-resolution overrides.
293
305
  * @returns {McpConfigListing[]} One listing per declared config file.
294
306
  */
295
- declare function listMcpServers(harness: Harness, options?: ResolveOptions): McpConfigListing[];
307
+ export declare function listMcpServers(harness: Harness, options?: ResolveOptions): McpConfigListing[];
296
308
  /**
297
309
  * Adds (or replaces) one MCP server in a harness's config. The rest of the
298
310
  * file is preserved: JSON through a parse/serialize round trip (formatting
@@ -304,7 +316,7 @@ declare function listMcpServers(harness: Harness, options?: ResolveOptions): Mcp
304
316
  * @param options - Platform and path-resolution overrides.
305
317
  * @returns {{ path: string, replaced: boolean }} The written path and replacement status.
306
318
  */
307
- declare function addMcpServer(harness: Harness, server: McpServerConfig, scope?: "user" | "project", options?: ResolveOptions): {
319
+ export declare function addMcpServer(harness: Harness, server: McpServerConfig, scope?: "user" | "project", options?: ResolveOptions): {
308
320
  path: string;
309
321
  replaced: boolean;
310
322
  };
@@ -317,7 +329,7 @@ declare function addMcpServer(harness: Harness, server: McpServerConfig, scope?:
317
329
  * @param options - Platform and path-resolution overrides.
318
330
  * @returns {{ path: string, removed: boolean }} The targeted path and removal status.
319
331
  */
320
- declare function removeMcpServer(harness: Harness, name: string, scope?: "user" | "project", options?: ResolveOptions): {
332
+ export declare function removeMcpServer(harness: Harness, name: string, scope?: "user" | "project", options?: ResolveOptions): {
321
333
  path: string;
322
334
  removed: boolean;
323
335
  };
@@ -328,16 +340,16 @@ declare function removeMcpServer(harness: Harness, name: string, scope?: "user"
328
340
  * @param text - JSONC source text.
329
341
  * @returns {unknown} The parsed JSON value.
330
342
  */
331
- declare function parseJsonc(text: string): unknown;
343
+ export declare function parseJsonc(text: string): unknown;
332
344
  /**
333
345
  * Resolves the master sync file path: $XDG_CONFIG_HOME or ~/.config.
334
346
  *
335
347
  * @param options - Path-resolution overrides.
336
348
  * @returns {string} The resolved master MCP config path.
337
349
  */
338
- declare function masterMcpPath(options?: ResolveOptions): string;
350
+ export declare function masterMcpPath(options?: ResolveOptions): string;
339
351
  /** One harness's outcome of a sync run. */
340
- interface SyncTargetResult {
352
+ export interface SyncTargetResult {
341
353
  id: string;
342
354
  path?: string;
343
355
  /** Reason this harness could not be targeted; `results` is empty then. */
@@ -346,11 +358,13 @@ interface SyncTargetResult {
346
358
  excluded?: true;
347
359
  results: Array<{
348
360
  name: string;
349
- action: "added" | "replaced" | "removed" | "unchanged";
361
+ action: "added" | "replaced" | "removed" | "unchanged" | "skipped";
362
+ /** Why a master server could not be written to this harness. */
363
+ reason?: string;
350
364
  }>;
351
365
  }
352
366
  /** Outcome of resetting the harness configs to the master list. */
353
- interface SyncReport {
367
+ export interface SyncReport {
354
368
  source: string;
355
369
  servers: string[];
356
370
  targets: SyncTargetResult[];
@@ -361,7 +375,7 @@ interface SyncReport {
361
375
  * @param options - Path-resolution overrides.
362
376
  * @returns {{ path: string, servers: McpServerConfig[], excludes: string[] }} The master list.
363
377
  */
364
- declare function readMasterMcpServers(options?: ResolveOptions): {
378
+ export declare function readMasterMcpServers(options?: ResolveOptions): {
365
379
  path: string;
366
380
  servers: McpServerConfig[];
367
381
  /** Harness ids the master list opts out of syncing. */
@@ -376,9 +390,9 @@ declare function readMasterMcpServers(options?: ResolveOptions): {
376
390
  * @param options - Platform and path-resolution overrides.
377
391
  * @returns {SyncReport} Per-harness synchronization outcomes.
378
392
  */
379
- declare function syncMcpServers(harnesses: readonly Harness[], options?: ResolveOptions): SyncReport;
393
+ export declare function syncMcpServers(harnesses: readonly Harness[], options?: ResolveOptions): SyncReport;
380
394
  /** Configuration read from agents.jsonc. */
381
- interface AgentsConfig {
395
+ export interface AgentsConfig {
382
396
  /** The master instructions file every harness links to. */
383
397
  source: string;
384
398
  /** Relative files to link beside every harness instructions target. */
@@ -388,9 +402,9 @@ interface AgentsConfig {
388
402
  /** Path the config was read from; absent when defaults were used. */
389
403
  configPath?: string;
390
404
  }
391
- type AgentsSyncAction = "linked" | "relinked" | "adopted" | "unchanged" | "skipped";
405
+ export type AgentsSyncAction = "linked" | "relinked" | "adopted" | "unchanged" | "skipped";
392
406
  /** One companion file's outcome for a harness target. */
393
- interface AgentsCompanionTargetResult {
407
+ export interface AgentsCompanionTargetResult {
394
408
  readonly source: string;
395
409
  readonly path: string;
396
410
  readonly action: Exclude<AgentsSyncAction, "skipped">;
@@ -398,7 +412,7 @@ interface AgentsCompanionTargetResult {
398
412
  readonly detail?: string;
399
413
  }
400
414
  /** One harness's outcome of an agents sync run. */
401
- interface AgentsTargetResult {
415
+ export interface AgentsTargetResult {
402
416
  id: string;
403
417
  path?: string;
404
418
  action: AgentsSyncAction;
@@ -408,7 +422,7 @@ interface AgentsTargetResult {
408
422
  companions?: readonly AgentsCompanionTargetResult[];
409
423
  }
410
424
  /** Outcome of one agents sync/doctor run. */
411
- interface AgentsSyncReport {
425
+ export interface AgentsSyncReport {
412
426
  source: string;
413
427
  check: boolean;
414
428
  targets: AgentsTargetResult[];
@@ -419,7 +433,7 @@ interface AgentsSyncReport {
419
433
  * @param options - Path-resolution overrides.
420
434
  * @returns {AgentsConfig} The normalized sync configuration.
421
435
  */
422
- declare function readAgentsConfig(options?: ResolveOptions): AgentsConfig;
436
+ export declare function readAgentsConfig(options?: ResolveOptions): AgentsConfig;
423
437
  /**
424
438
  * Links every harness's user-scope instructions file to the master. In check
425
439
  * mode nothing is written; the report shows what a real run would do.
@@ -429,6 +443,5 @@ declare function readAgentsConfig(options?: ResolveOptions): AgentsConfig;
429
443
  * @param options - Path-resolution overrides.
430
444
  * @returns {AgentsSyncReport} Per-harness synchronization outcomes.
431
445
  */
432
- declare function syncAgentsFiles(harnesses: readonly Harness[], check?: boolean, options?: ResolveOptions): AgentsSyncReport;
433
- export { AgentsCompanionTargetResult, AgentsConfig, AgentsSyncAction, AgentsSyncReport, AgentsTargetResult, AvailableModel, EvidenceLevel, Harness, HarnessCapabilities, HarnessConstructor, HarnessDetection, HarnessId, HarnessInvocation, HarnessInvocationModes, HarnessModelListing, InvokeOptions, InvokeResult, ListModelsOptions, ListModelsResult, McpConfigFile, McpConfigListing, McpServerConfig, PathCandidate, Platform, ResolveOptions, ResolvedPaths, StorageDescriptor, SyncReport, SyncTargetResult, addMcpServer, listMcpServers, masterMcpPath, parseJsonc, readAgentsConfig, readMasterMcpServers, removeMcpServer, syncAgentsFiles, syncMcpServers, version };
446
+ export declare function syncAgentsFiles(harnesses: readonly Harness[], check?: boolean, options?: ResolveOptions): AgentsSyncReport;
434
447
  //# sourceMappingURL=agents-sync.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents-sync.d.mts","names":[],"sources":["../../src/types.ts","../../src/harness.ts","../../src/mcp-servers.ts","../../src/agents-sync.ts"],"mappings":"AAEA,cAAa;KAED;KAaA;KACA;UAEK;EACf;EACA;EACA,OAAO;EACP,YAAY;EACZ;;UAGe;EACf;EACA,OAAO;EACP;;UAGe;EACf;EACA;;EAEA;;EAEA;EACA;EACA;;;UAIe;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA,OAAO;EACP;;;UAIe;EACf;EACA;EACA;EACA;EACA;EACA;;;UAIe;;EAEf;;EAEA;EACA,OAAO;EACP;;;UAIe;EACf;EACA;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA,MAAM;;EAEN;;EAEA,SAAS;;UAGM;EACf;EACA,MAAM;;EAEN;;EAEA;;EAEA;;EAEA;;EAEA,SAAS;;EAET;;UAGe;EACf;EACA;EACA;EACA;;EAEA;EACA;;EAEA;;;UAIe,yBAAyB;;EAExC,QAAQ;;;UAIO;EACf;EACA;EACA;EACA;EACA,MAAM;EACN;EACA,UAAU;;EAEV;;;UAIe,sBAAsB;EACrC;;EAEA;;EAEA;;UAGe;;EAEf;;EAEA;;UAGe;EACf;EACA;EACA,WAAW;;UAGI;EACf,QAAQ;EACR,UAAU;EACV,cAAc;EACd,QAAQ;EACR,UAAU;EACV,OAAO;;KCnJJ,oBAAoB;EACvB;EACA;EACA;EACA;;uBA2OoB;oBACF,IAAI;oBACJ;oBACA;oBACA,QAAQ;oBACR,UAAU;oBACV,aAAa;oBACb,cAAc;oBACd,QAAQ;oBACR,UAAU;oBACV,OAAO;oBACP,cAAc;oBACd,WAAW;;oBAEX,YAAY;;WAErB,cAAc;;WAEd,YAAY;;;;;;WAMZ;EAET;EAYA;EAIA,cAAc;MAKV;;;;;;MAsBA,mBAAmB;;;;;;;;;;EAoBvB,gBACE,gBACA,UAAS;IACN;IAAiB;;;;;;;;EAmBtB,gBAAgB,UAAS;;;;;;;;;;EAqBzB,OAAO,gBAAgB,UAAS,gBAAqB,QAAQ;;;;;;;;;UA2BrD;;;;;;;EAgBR,yBAAyB;IAAoB;IAAiB;;;;;;;;;EAmBxD,WAAW,UAAS,oBAAyB,QAAQ;;;;;;YAyBjD,wBAAwB,kBAAkB;;;;;;;;;;EAapD,kBAAkB,UAAU,eAC1B,kBAAkB,KAClB,UAAS,iBACR;EAcH,QAAQ,UAAS,iBAAsB;;KAY7B,+BAA+B;;UCxgB1B;EACf;EACA,OAAO;EACP,QAAQ;EACR,OAAO;EACP;EACA;EACA,SAAS;;EAET;;;;;;;;;;iBAmRc,eAAe,SAAS,SAAS,UAAS,iBAAsB;;;;;;;;;;;;iBAiLhE,aACd,SAAS,SACT,QAAQ,iBACR,4BACA,UAAS;EACN;EAAc;;;;;;;;;;;iBA0BH,gBACd,SAAS,SACT,cACA,4BACA,UAAS;EACN;EAAc;;;;;;;;;iBA8FH,WAAW;;;;;;;iBAwBX,cAAc,UAAS;;UAKtB;EACf;EACA;;EAEA;;EAEA;EACA,SAAS;IAAQ;IAAc;;;;UAIhB;EACf;EACA;EACA,SAAS;;;;;;;;iBAuEK,qBAAqB,UAAS;EAC5C;EACA,SAAS;;EAET;;;;;;;;;;;iBA2Hc,eACd,oBAAoB,WACpB,UAAS,iBACR;;UC1zBc;;EAEf;;EAEA;;EAEA;;EAEA;;KAGU;;UAGK;WACN;WACA;WACA,QAAQ,QAAQ;;WAEhB;;;UAIM;EACf;EACA;EACA,QAAQ;;EAER;;EAEA,sBAAsB;;;UAIP;EACf;EACA;EACA,SAAS;;;;;;;;iBA2FK,iBAAiB,UAAS,iBAAsB;;;;;;;;;;iBAiMhD,gBACd,oBAAoB,WACpB,iBACA,UAAS,iBACR"}
1
+ {"version":3,"file":"agents-sync.d.mts","names":[],"sources":["../../src/types.ts","../../src/harness.ts","../../src/mcp-servers.ts","../../src/agents-sync.ts"],"mappings":"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"}