@agntn/harnesses 0.1.1 → 0.2.1

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.
Files changed (47) hide show
  1. package/README.md +152 -117
  2. package/dist/_chunks/agents.mjs +2 -30
  3. package/dist/_chunks/agents.mjs.map +1 -1
  4. package/dist/_chunks/libs/typebox.mjs +535 -608
  5. package/dist/_chunks/libs/typebox.mjs.map +1 -1
  6. package/dist/_chunks/mcp-servers.mjs +5 -34
  7. package/dist/_chunks/mcp-servers.mjs.map +1 -1
  8. package/dist/_chunks/mcp.mjs +13 -163
  9. package/dist/_chunks/mcp.mjs.map +1 -1
  10. package/dist/_chunks/output.mjs +28 -0
  11. package/dist/_chunks/output.mjs.map +1 -0
  12. package/dist/_chunks/{agents-sync.d.mts → prompt-sync.d.mts} +91 -44
  13. package/dist/_chunks/prompt-sync.d.mts.map +1 -0
  14. package/dist/_chunks/{agents-sync.mjs → prompt-sync.mjs} +565 -26
  15. package/dist/_chunks/prompt-sync.mjs.map +1 -0
  16. package/dist/_chunks/prompts.mjs +28 -0
  17. package/dist/_chunks/prompts.mjs.map +1 -0
  18. package/dist/_chunks/tool-operations.mjs +43 -10
  19. package/dist/_chunks/tool-operations.mjs.map +1 -1
  20. package/dist/_chunks/tool-schemas.d.mts +17 -13
  21. package/dist/_chunks/tool-schemas.d.mts.map +1 -1
  22. package/dist/_chunks/tool-schemas.mjs +4 -0
  23. package/dist/_chunks/tool-schemas.mjs.map +1 -1
  24. package/dist/_chunks/types.mjs +1 -1
  25. package/dist/_chunks/types.mjs.map +1 -1
  26. package/dist/cli.mjs +6 -2
  27. package/dist/cli.mjs.map +1 -1
  28. package/dist/index.d.mts +26 -12
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.mjs +2 -2
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/mcp.d.mts +1 -2
  33. package/dist/mcp.d.mts.map +1 -1
  34. package/dist/mcp.mjs +176 -1
  35. package/dist/mcp.mjs.map +1 -0
  36. package/dist/tool-operations.d.mts +42 -32
  37. package/dist/tool-operations.d.mts.map +1 -1
  38. package/dist/tool-operations.mjs +2 -2
  39. package/dist/tool-schemas.d.mts +2 -2
  40. package/package.json +24 -21
  41. package/packages/omp/extensions/harnesses.ts +19 -1
  42. package/packages/pi/extensions/harnesses.ts +26 -2
  43. package/packages/shared/tui.ts +22 -1
  44. package/dist/_chunks/agents-sync.d.mts.map +0 -1
  45. package/dist/_chunks/agents-sync.mjs.map +0 -1
  46. package/dist/_chunks/mcp2.mjs +0 -17
  47. package/dist/_chunks/mcp2.mjs.map +0 -1
package/README.md CHANGED
@@ -5,160 +5,195 @@
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
+ - 🗂️ **One prompt folder, supported harnesses.** Markdown lives under XDG data; Gemini TOML is generated from it.
28
+ - 📜 **Session types when the format is stable.** JSONL, SQLite, JSON. Unstable shapes stay `unknown`.
29
+ - 🤖 **Ten tools, three doors.** MCP, Pi and OMP call the same executors.
30
+
31
+ ## 📦 Install
13
32
 
14
33
  ```bash
15
34
  pnpm add @agntn/harnesses
16
35
  ```
17
36
 
18
- ## Usage
37
+ Node.js 24 or newer.
19
38
 
20
- ```ts
21
- import { getHarness, detectHarness, detectProjectHarnesses } from "@agntn/harnesses";
39
+ ## 🚀 First call
22
40
 
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
41
+ ```bash
42
+ npx @agntn/harnesses detect
43
+ ```
44
+
45
+ ```
46
+ System Scan
47
+
48
+ ● antigravity Google Antigravity CLI v1.2.5
49
+ ● claude Anthropic Claude Code v2.1.276
50
+ ● codex OpenAI Codex CLI v0.154.0
51
+ ○ cursor Cursor
52
+ ● freebuff Freebuff
53
+ ○ gemini Google Gemini CLI
54
+ ○ github-copilot GitHub Copilot
55
+ ● grok xAI Grok CLI v1.0.34
56
+ ● mastracode Mastra Code
57
+ ● omp OMP (oh-my-pi) v18.2.4
58
+ ● opencode OpenCode CLI v2.0.5
59
+ ● prime-agent Prime Agent v0.9.5
60
+ ● pi Pi Coding Agent v0.85.1
61
+ ```
62
+
63
+ 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`.
64
+
65
+ Same binary, more commands:
66
+
67
+ ```bash
68
+ harnesses list
69
+ harnesses info claude
70
+ harnesses paths pi
71
+ harnesses models pi gpt-5.4 --json
72
+ harnesses run claude "review this design"
73
+ harnesses run codex --read-only "review this"
74
+ harnesses mcp-servers list
75
+ harnesses agents sync --check
76
+ harnesses prompts sync --check
77
+ ```
78
+
79
+ `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
80
 
28
- const codex = getHarness("codex");
29
- await codex.invoke("Review this patch", { readOnly: true, timeoutMs: 60_000 });
81
+ ### Commands
30
82
 
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 });
83
+ | Command | What it does | Example |
84
+ | -------------- | ------------------------------------------------------ | ------------------------------------------- |
85
+ | `list` | Every known harness, id and name | `harnesses list` |
86
+ | `detect` | Which ones are installed, with versions | `harnesses detect` |
87
+ | `info` | Registry entry: modes, capabilities, path templates | `harnesses info claude` |
88
+ | `paths` | Those templates expanded for this machine | `harnesses paths pi` |
89
+ | `models` | Models the harness can use, through its native listing | `harnesses models pi` |
90
+ | `run` | One prompt through headless mode | `harnesses run claude "review this design"` |
91
+ | `mcp-servers` | MCP servers across the config dialects | `harnesses mcp-servers list` |
92
+ | `agents sync` | Link global instructions files to one master | `harnesses agents sync --check` |
93
+ | `prompts sync` | Sync one Markdown prompt directory across harnesses | `harnesses prompts sync --check` |
94
+ | `mcp` | The MCP server on stdio | `harnesses mcp` |
35
95
 
36
- // Resolve to absolute paths for current platform
96
+ `list`, `detect`, `info`, `paths` and `models` take `--json` or `--toon`. `run --json` is different: that one is the harness's own structured output.
97
+
98
+ ## 🧠 Library
99
+
100
+ ```ts
101
+ import { getHarness, detectHarness } from "@agntn/harnesses";
102
+
103
+ const claude = getHarness("claude");
37
104
  const paths = claude.resolve({ platform: "linux", homeDir: "/home/dev" });
38
- console.log(paths.config); // [{ path: "/home/dev/.claude/settings.json", ... }, ...]
105
+ console.log(paths.skills);
39
106
 
40
- // Detect which agent is running (env vars first, then project markers)
41
107
  const active = detectHarness();
42
- if (active) {
43
- console.log(`Running inside ${active.name}`);
44
- }
108
+ if (active) console.log(active.id);
45
109
 
46
- // Find all agents configured in a project directory
47
- const harnesses = detectProjectHarnesses("/path/to/project");
110
+ await getHarness("codex").invoke("Review this patch", { readOnly: true });
48
111
  ```
49
112
 
50
- Session schemas are typed per agent, so you get structure when parsing JSONL/SQLite/JSON files:
113
+ 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
114
 
52
- ```ts
53
- import type { ClaudeSessionEntry, CodexThread, GeminiConversationRecord } from "@agntn/harnesses";
54
- ```
115
+ ### Prompt templates
55
116
 
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
- | Prime Agent | `prime-agent` | env + project | `.prime/agent/skills/` | - | JSONL + JSON |
72
- | Freebuff | `freebuff` | project | `.agents/skills/` | - | JSON + JSONL |
73
-
74
- ### Native audio and video input
75
-
76
- `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.
77
-
78
- | Agent | Audio | Video | Evidence boundary |
79
- | --------------- | :---: | :---: | --------------------------------------------------------------------------- |
80
- | Antigravity CLI | Yes | Yes | Native attachments; documented audio formats and direct video pasting |
81
- | Gemini CLI | Yes | No | The `read_file` tool supports audio; native video support is not documented |
82
- | Claude Code | No | No | No verified native route |
83
- | Codex CLI | No | No | No verified native route |
84
- | Grok CLI | No | No | Its ACP parser recognizes audio blocks, but the runtime rejects them |
85
- | OpenCode | No | No | Its attachment documentation explicitly excludes audio and video |
86
- | Cursor | No | No | Voice input is transcribed to text |
87
- | GitHub Copilot | No | No | Voice input is transcribed locally to text |
88
- | Mastra Code | No | No | No verified native route |
89
- | OMP (oh-my-pi) | No | No | No verified native route |
90
- | Pi Coding Agent | No | No | No verified native route |
91
- | Prime Agent | No | No | No verified native route |
92
- | Freebuff | No | No | No verified native route |
93
-
94
- 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).
95
-
96
- `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.
97
-
98
- 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.
117
+ Put canonical Markdown templates in `$XDG_DATA_HOME/agntn/prompts/`, or
118
+ `~/.local/share/agntn/prompts/` when `XDG_DATA_HOME` is unset. Then sync every
119
+ supported harness:
99
120
 
100
- ```ts
101
- const controller = new AbortController();
102
- const pending = getHarness("pi").invoke("Review this change", {
103
- tools: true,
104
- readOnly: true,
105
- signal: controller.signal,
106
- });
107
- controller.abort();
108
- const result = await pending;
109
- console.log(result.aborted);
121
+ ```bash
122
+ harnesses prompts sync --check
123
+ harnesses prompts sync
110
124
  ```
111
125
 
112
- 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.
126
+ Markdown harnesses receive symlinks to those files. Gemini receives generated
127
+ TOML commands, with `$ARGUMENTS` translated to `{{args}}`. Unmanaged files at a
128
+ managed name are backed up under the same XDG data tree before replacement.
129
+
130
+ `getHarness("pi").promptTemplates` still lists every verified reusable prompt
131
+ location. `promptTemplateSyncTarget` is the single stable user destination the
132
+ sync owns for that harness. Formats, frontmatter and CLI limits are in the
133
+ [registry guide](https://harnesses.agntn.dev/guide/registry#prompt-templates).
134
+
135
+ ## 🗺️ Harnesses
136
+
137
+ | ID | Name | Project skills |
138
+ | ---------------- | ---------------------- | ---------------------- |
139
+ | `antigravity` | Google Antigravity CLI | `.agents/skills/` |
140
+ | `claude` | Anthropic Claude Code | `.claude/skills/` |
141
+ | `codex` | OpenAI Codex CLI | `.codex/skills/` |
142
+ | `cursor` | Cursor | `.cursor/skills/` |
143
+ | `freebuff` | Freebuff | `.agents/skills/` |
144
+ | `gemini` | Google Gemini CLI | `.gemini/skills/` |
145
+ | `github-copilot` | GitHub Copilot | `.github/skills/` |
146
+ | `grok` | xAI Grok CLI | `.grok/skills/` |
147
+ | `mastracode` | Mastra Code | `.mastracode/skills/` |
148
+ | `omp` | OMP (oh-my-pi) | `.omp/skills/` |
149
+ | `opencode` | OpenCode CLI | `.opencode/skills/` |
150
+ | `prime-agent` | Prime Agent | `.prime/agent/skills/` |
151
+ | `pi` | Pi Coding Agent | `.pi/skills/` |
152
+
153
+ 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).
154
+
155
+ ## 🤖 Agents
113
156
 
114
- 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). An env value that is exactly `${NAME}` references that variable of the harness environment: Prime Agent stores it natively as `{"env": "NAME"}`, the other harnesses receive the value from the syncing process, and a server one dialect cannot hold is reported as `skipped` with a reason while the rest of the list syncs. `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.
157
+ ```bash
158
+ harnesses mcp
159
+ pi install npm:@agntn/harnesses
160
+ omp install @agntn/harnesses
161
+ ```
115
162
 
116
- ```jsonc
163
+ ```json
117
164
  {
118
- "source": "bundle/AGENTS.md",
119
- "companions": ["RULES.md"],
120
- "excludes": ["codex"],
165
+ "mcpServers": {
166
+ "harnesses": { "command": "npx", "args": ["-y", "@agntn/harnesses", "mcp"] }
167
+ }
121
168
  }
122
169
  ```
123
170
 
124
- All paths carry `scope` (user/project/system/data), `level` (official/community/inferred), and optional `platforms` tags.
171
+ Ten tools, the same ten 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).
125
172
 
126
- 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. Paths come once, resolved for the current platform. The templates stay with `harnesses info`.
173
+ ## 🚫 What this does not do
127
174
 
128
- ## CLI
175
+ 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).
129
176
 
130
- ```bash
131
- harnesses list # all known harnesses
132
- harnesses detect # which ones are installed + versions
133
- harnesses info claude # metadata, including supported invocation modes
134
- harnesses paths claude # resolved paths for current platform
135
- harnesses info codex --json # machine-readable output
136
- harnesses models pi # models available to Pi
137
- harnesses models pi gpt-5.4 --json
138
- harnesses run claude "review this design" # advisor without tools mode (default)
139
- harnesses run pi --model openai-codex/gpt-5.4 "review this design"
140
- harnesses run claude --tools "fix lint" # full agent with tools enabled
141
- harnesses run codex --read-only "review this" # tools inside a native read-only sandbox
142
- harnesses mcp-servers list # MCP servers configured across all harnesses
143
- harnesses mcp-servers add omp probe --command node --args "srv.mjs mcp"
144
- harnesses mcp-servers remove omp probe
145
- harnesses mcp-servers sync # reset all harnesses to ~/.config/agntn/mcp.jsonc
146
- harnesses agents sync --check # doctor: link global AGENTS.md files to one master
147
- harnesses mcp # run the MCP server over stdio
148
- ```
149
-
150
- `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.
177
+ ## 🧩 Adding a harness
151
178
 
152
- ## How harnesses compares to unagent
179
+ 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).
153
180
 
154
- [unagent](https://github.com/onmax/unagent) covers similar ground but makes different tradeoffs.
181
+ ## 🛠️ Development
155
182
 
156
- **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. Thirteen harnesses, each fully mapped.
183
+ ```bash
184
+ pnpm install
185
+ pnpm lint # builds first, then oxlint and oxfmt --check
186
+ pnpm lint:fix
187
+ pnpm typecheck
188
+ pnpm test:run
189
+ pnpm build # obuild
190
+ pnpm docs # the Docus site, bundles src/ itself
191
+ ```
157
192
 
158
- **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.
193
+ ## 💛 Thanks
159
194
 
160
- 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.
195
+ 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
161
196
 
162
- ## License
197
+ ## 📄 License
163
198
 
164
199
  [MIT](./LICENSE)
@@ -1,36 +1,8 @@
1
- import { isHarnessId, listHarnesses } from "./agents-sync.mjs";
1
+ import { isHarnessId, listHarnesses } from "./prompt-sync.mjs";
2
2
  import { agentsSync } from "./tool-operations.mjs";
3
- import { stripVTControlCharacters } from "node:util";
3
+ import { formatArgs, report } from "./output.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { consola } from "consola";
6
- import { encode } from "@toon-format/toon";
7
- const formatArgs = {
8
- json: {
9
- type: "boolean",
10
- description: "Output as JSON"
11
- },
12
- toon: {
13
- type: "boolean",
14
- description: "Output as TOON"
15
- }
16
- };
17
- const UNSAFE_TERMINAL_CONTROLS = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
18
- function report(result, args) {
19
- const sanitize = (text) => stripVTControlCharacters(text ?? "").replace(UNSAFE_TERMINAL_CONTROLS, " ");
20
- if (result.isError) {
21
- consola.error(sanitize(result.content[0]?.text));
22
- process.exit(1);
23
- }
24
- if (args.json) {
25
- console.log(JSON.stringify(result.details, null, 2));
26
- return;
27
- }
28
- if (args.toon) {
29
- console.log(encode(result.details));
30
- return;
31
- }
32
- console.log(sanitize(result.content[0]?.text));
33
- }
34
6
  var agents_default = defineCommand({
35
7
  meta: { description: "Manage the shared global instructions file across harnesses" },
36
8
  subCommands: { sync: defineCommand({
@@ -1 +1 @@
1
- {"version":3,"file":"agents.mjs","names":["toToon"],"sources":["../../src/commands/agents.ts"],"sourcesContent":["import { stripVTControlCharacters } from \"node:util\";\nimport { defineCommand } from \"citty\";\nimport { consola } from \"consola\";\nimport { encode as toToon } from \"@toon-format/toon\";\nimport { isHarnessId, listHarnesses } from \"../registry.ts\";\nimport { agentsSync, type ToolResult } from \"../tool-operations.ts\";\n\nconst formatArgs = {\n json: { type: \"boolean\" as const, description: \"Output as JSON\" },\n toon: { type: \"boolean\" as const, description: \"Output as TOON\" },\n};\n\n// Sync reports echo backup paths and config values, so strip terminal controls.\n// oxlint-disable-next-line no-control-regex -- Terminal control bytes are precisely what this boundary removes.\nconst UNSAFE_TERMINAL_CONTROLS = /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F]/g;\n\nfunction report(\n result: ToolResult<unknown>,\n args: Readonly<{ json?: boolean; toon?: boolean }>,\n): void {\n const sanitize = (text: string | undefined) =>\n stripVTControlCharacters(text ?? \"\").replace(UNSAFE_TERMINAL_CONTROLS, \" \");\n if (result.isError) {\n consola.error(sanitize(result.content[0]?.text));\n process.exit(1);\n }\n if (args.json) {\n console.log(JSON.stringify(result.details, null, 2));\n return;\n }\n if (args.toon) {\n console.log(toToon(result.details));\n return;\n }\n console.log(sanitize(result.content[0]?.text));\n}\n\nexport default defineCommand({\n meta: { description: \"Manage the shared global instructions file across harnesses\" },\n subCommands: {\n sync: defineCommand({\n meta: {\n description:\n \"Link global instructions and declared companions (agents.jsonc: source, companions, excludes)\",\n },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: false },\n check: {\n type: \"boolean\" as const,\n description: \"Report what would change without writing anything\",\n },\n ...formatArgs,\n },\n run({ args }) {\n const id = args.id === undefined ? undefined : (args.id as string);\n if (id !== undefined && !isHarnessId(id)) {\n consola.error(`Unknown harness: ${id}\\nKnown: ${listHarnesses().join(\", \")}`);\n process.exit(1);\n }\n report(agentsSync(id, args.check === true), args);\n },\n }),\n },\n});\n"],"mappings":";;;;;;AAOA,MAAM,aAAa;CACjB,MAAM;EAAE,MAAM;EAAoB,aAAa;CAAiB;CAChE,MAAM;EAAE,MAAM;EAAoB,aAAa;CAAiB;AAClE;AAIA,MAAM,2BAA2B;AAEjC,SAAS,OACP,QACA,MACM;CACN,MAAM,YAAY,SAChB,yBAAyB,QAAQ,EAAE,CAAC,CAAC,QAAQ,0BAA0B,GAAG;CAC5E,IAAI,OAAO,SAAS;EAClB,QAAQ,MAAM,SAAS,OAAO,QAAQ,EAAE,EAAE,IAAI,CAAC;EAC/C,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI,KAAK,MAAM;EACb,QAAQ,IAAI,KAAK,UAAU,OAAO,SAAS,MAAM,CAAC,CAAC;EACnD;CACF;CACA,IAAI,KAAK,MAAM;EACb,QAAQ,IAAIA,OAAO,OAAO,OAAO,CAAC;EAClC;CACF;CACA,QAAQ,IAAI,SAAS,OAAO,QAAQ,EAAE,EAAE,IAAI,CAAC;AAC/C;AAEA,IAAA,iBAAe,cAAc;CAC3B,MAAM,EAAE,aAAa,8DAA8D;CACnF,aAAa,EACX,MAAM,cAAc;EAClB,MAAM,EACJ,aACE,gGACJ;EACA,MAAM;GACJ,IAAI;IAAE,MAAM;IAAuB,aAAa;IAAc,UAAU;GAAM;GAC9E,OAAO;IACL,MAAM;IACN,aAAa;GACf;GACA,GAAG;EACL;EACA,IAAI,EAAE,QAAQ;GACZ,MAAM,KAAK,KAAK,OAAO,KAAA,IAAY,KAAA,IAAa,KAAK;GACrD,IAAI,OAAO,KAAA,KAAa,CAAC,YAAY,EAAE,GAAG;IACxC,QAAQ,MAAM,oBAAoB,GAAG,WAAW,cAAc,CAAC,CAAC,KAAK,IAAI,GAAG;IAC5E,QAAQ,KAAK,CAAC;GAChB;GACA,OAAO,WAAW,IAAI,KAAK,UAAU,IAAI,GAAG,IAAI;EAClD;CACF,CAAC,EACH;AACF,CAAC"}
1
+ {"version":3,"file":"agents.mjs","names":[],"sources":["../../src/commands/agents.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { consola } from \"consola\";\nimport { isHarnessId, listHarnesses } from \"../registry.ts\";\nimport { agentsSync } from \"../tool-operations.ts\";\nimport { formatArgs, report } from \"./output.ts\";\n\nexport default defineCommand({\n meta: { description: \"Manage the shared global instructions file across harnesses\" },\n subCommands: {\n sync: defineCommand({\n meta: {\n description:\n \"Link global instructions and declared companions (agents.jsonc: source, companions, excludes)\",\n },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: false },\n check: {\n type: \"boolean\" as const,\n description: \"Report what would change without writing anything\",\n },\n ...formatArgs,\n },\n run({ args }) {\n const id = args.id === undefined ? undefined : (args.id as string);\n if (id !== undefined && !isHarnessId(id)) {\n consola.error(`Unknown harness: ${id}\\nKnown: ${listHarnesses().join(\", \")}`);\n process.exit(1);\n }\n report(agentsSync(id, args.check === true), args);\n },\n }),\n },\n});\n"],"mappings":";;;;;AAMA,IAAA,iBAAe,cAAc;CAC3B,MAAM,EAAE,aAAa,8DAA8D;CACnF,aAAa,EACX,MAAM,cAAc;EAClB,MAAM,EACJ,aACE,gGACJ;EACA,MAAM;GACJ,IAAI;IAAE,MAAM;IAAuB,aAAa;IAAc,UAAU;GAAM;GAC9E,OAAO;IACL,MAAM;IACN,aAAa;GACf;GACA,GAAG;EACL;EACA,IAAI,EAAE,QAAQ;GACZ,MAAM,KAAK,KAAK,OAAO,KAAA,IAAY,KAAA,IAAa,KAAK;GACrD,IAAI,OAAO,KAAA,KAAa,CAAC,YAAY,EAAE,GAAG;IACxC,QAAQ,MAAM,oBAAoB,GAAG,WAAW,cAAc,CAAC,CAAC,KAAK,IAAI,GAAG;IAC5E,QAAQ,KAAK,CAAC;GAChB;GACA,OAAO,WAAW,IAAI,KAAK,UAAU,IAAI,GAAG,IAAI;EAClD;CACF,CAAC,EACH;AACF,CAAC"}