@agntn/harnesses 0.2.1 → 0.3.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.
- package/README.md +48 -9
- package/dist/_chunks/agents.mjs +5 -3
- package/dist/_chunks/agents.mjs.map +1 -1
- package/dist/_chunks/mcp-servers.mjs +5 -3
- package/dist/_chunks/mcp-servers.mjs.map +1 -1
- package/dist/_chunks/mcp.mjs +10 -1
- package/dist/_chunks/mcp.mjs.map +1 -1
- package/dist/_chunks/output.mjs +7 -1
- package/dist/_chunks/output.mjs.map +1 -1
- package/dist/_chunks/prompts.mjs +4 -2
- package/dist/_chunks/prompts.mjs.map +1 -1
- package/dist/_chunks/{prompt-sync.d.mts → skills-sync.d.mts} +174 -57
- package/dist/_chunks/skills-sync.d.mts.map +1 -0
- package/dist/_chunks/{prompt-sync.mjs → skills-sync.mjs} +665 -148
- package/dist/_chunks/skills-sync.mjs.map +1 -0
- package/dist/_chunks/skills.mjs +30 -0
- package/dist/_chunks/skills.mjs.map +1 -0
- package/dist/_chunks/tool-operations.mjs +38 -13
- package/dist/_chunks/tool-operations.mjs.map +1 -1
- package/dist/_chunks/tool-schemas.d.mts +22 -13
- package/dist/_chunks/tool-schemas.d.mts.map +1 -1
- package/dist/_chunks/tool-schemas.mjs +18 -11
- package/dist/_chunks/tool-schemas.mjs.map +1 -1
- package/dist/_chunks/types.mjs +5 -2
- package/dist/_chunks/types.mjs.map +1 -1
- package/dist/cli.mjs +29 -9
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +16 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -3
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.d.mts +2 -0
- package/dist/mcp.d.mts.map +1 -1
- package/dist/mcp.mjs +6261 -5
- package/dist/mcp.mjs.map +1 -1
- package/dist/tool-operations.d.mts +21 -4
- package/dist/tool-operations.d.mts.map +1 -1
- package/dist/tool-operations.mjs +3 -3
- package/dist/tool-schemas.d.mts +2 -2
- package/dist/tool-schemas.mjs +1 -1
- package/dist/typebox.LICENSE +23 -0
- package/package.json +18 -18
- package/packages/omp/extensions/harnesses.ts +19 -1
- package/packages/pi/extensions/harnesses.ts +27 -46
- package/packages/shared/tui.ts +13 -0
- package/dist/THIRD-PARTY-LICENSES.md +0 -36
- package/dist/_chunks/libs/typebox.mjs +0 -5439
- package/dist/_chunks/libs/typebox.mjs.map +0 -1
- package/dist/_chunks/prompt-sync.d.mts.map +0 -1
- package/dist/_chunks/prompt-sync.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The rest of it sits on [harnesses.agntn.dev](https://harnesses.agntn.dev).
|
|
|
26
26
|
- 🔗 **One AGENTS.md behind the global files.** Symlinks, so an edit through Claude or Gemini is the same bytes.
|
|
27
27
|
- 🗂️ **One prompt folder, supported harnesses.** Markdown lives under XDG data; Gemini TOML is generated from it.
|
|
28
28
|
- 📜 **Session types when the format is stable.** JSONL, SQLite, JSON. Unstable shapes stay `unknown`.
|
|
29
|
-
- 🤖 **
|
|
29
|
+
- 🤖 **Eleven tools, three doors.** MCP, Pi and OMP call the same executors.
|
|
30
30
|
|
|
31
31
|
## 📦 Install
|
|
32
32
|
|
|
@@ -74,9 +74,10 @@ harnesses run codex --read-only "review this"
|
|
|
74
74
|
harnesses mcp-servers list
|
|
75
75
|
harnesses agents sync --check
|
|
76
76
|
harnesses prompts sync --check
|
|
77
|
+
harnesses skills sync --check
|
|
77
78
|
```
|
|
78
79
|
|
|
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).
|
|
80
|
+
`run` without `--tools` is the advisor. `--tools` is the full agent. `--read-only` asks the CLI for a sandbox and implies tools, so pairing it with `--no-tools` is an error. Timeouts, `--cwd` and `--model` sit in the [CLI guide](https://harnesses.agntn.dev/guide/cli).
|
|
80
81
|
|
|
81
82
|
### Commands
|
|
82
83
|
|
|
@@ -91,6 +92,7 @@ harnesses prompts sync --check
|
|
|
91
92
|
| `mcp-servers` | MCP servers across the config dialects | `harnesses mcp-servers list` |
|
|
92
93
|
| `agents sync` | Link global instructions files to one master | `harnesses agents sync --check` |
|
|
93
94
|
| `prompts sync` | Sync one Markdown prompt directory across harnesses | `harnesses prompts sync --check` |
|
|
95
|
+
| `skills sync` | Link every skills directory to one shared folder | `harnesses skills sync --check` |
|
|
94
96
|
| `mcp` | The MCP server on stdio | `harnesses mcp` |
|
|
95
97
|
|
|
96
98
|
`list`, `detect`, `info`, `paths` and `models` take `--json` or `--toon`. `run --json` is different: that one is the harness's own structured output.
|
|
@@ -103,14 +105,21 @@ import { getHarness, detectHarness } from "@agntn/harnesses";
|
|
|
103
105
|
const claude = getHarness("claude");
|
|
104
106
|
const paths = claude.resolve({ platform: "linux", homeDir: "/home/dev" });
|
|
105
107
|
console.log(paths.skills);
|
|
108
|
+
console.log(paths.temp, claude.envOverrides);
|
|
106
109
|
|
|
107
110
|
const active = detectHarness();
|
|
108
111
|
if (active) console.log(active.id);
|
|
109
112
|
|
|
110
113
|
await getHarness("codex").invoke("Review this patch", { readOnly: true });
|
|
114
|
+
|
|
115
|
+
const agy = getHarness("antigravity");
|
|
116
|
+
const [model] = (await agy.listModels({ search: "gemini" })).models;
|
|
117
|
+
if (model) await agy.invoke("Review this patch", { tools: true, model: agy.modelSelector(model) });
|
|
111
118
|
```
|
|
112
119
|
|
|
113
|
-
|
|
120
|
+
`temp` is where a harness drops its own temp files, and `envOverrides` names the variables that move its directories. `claude.resolve().temp` follows `CLAUDE_CODE_TMPDIR` when it's set, which is handy when `/tmp` is tmpfs and Claude's scratchpads are quietly eating RAM.
|
|
121
|
+
|
|
122
|
+
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. `listModels()` asks it what it can run, and `modelSelector()` turns one of those into what `model` takes, since Pi wants `provider/id` and Antigravity the bare id. 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).
|
|
114
123
|
|
|
115
124
|
### Prompt templates
|
|
116
125
|
|
|
@@ -123,15 +132,45 @@ harnesses prompts sync --check
|
|
|
123
132
|
harnesses prompts sync
|
|
124
133
|
```
|
|
125
134
|
|
|
126
|
-
Markdown
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
For a Markdown harness, the whole prompt directory becomes one symlink to that
|
|
136
|
+
directory, so a template added through any harness shows up in all of them.
|
|
137
|
+
Gemini gets generated TOML commands, with `$ARGUMENTS` translated to `{{args}}`,
|
|
138
|
+
and its commands directory keeps nothing else. Whatever was there before is
|
|
139
|
+
backed up under the same XDG data tree first.
|
|
129
140
|
|
|
130
141
|
`getHarness("pi").promptTemplates` still lists every verified reusable prompt
|
|
131
142
|
location. `promptTemplateSyncTarget` is the single stable user destination the
|
|
132
143
|
sync owns for that harness. Formats, frontmatter and CLI limits are in the
|
|
133
144
|
[registry guide](https://harnesses.agntn.dev/guide/registry#prompt-templates).
|
|
134
145
|
|
|
146
|
+
### Skills
|
|
147
|
+
|
|
148
|
+
Skills live in `$XDG_DATA_HOME/agntn/skills/`, next to the prompts, one folder
|
|
149
|
+
with a `SKILL.md` per skill:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
harnesses skills sync --check
|
|
153
|
+
harnesses skills sync
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { getAllHarnesses, syncSkills } from "@agntn/harnesses";
|
|
158
|
+
|
|
159
|
+
const report = syncSkills(getAllHarnesses(), true);
|
|
160
|
+
console.log(report.skills, report.targets);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Every harness's user skills directory, `~/.claude/skills/`, `~/.agents/skills/`
|
|
164
|
+
for Codex, `~/.pi/agent/skills/` and the rest, becomes one symlink to that
|
|
165
|
+
folder. A skill written through Claude is already there when Pi looks. Old
|
|
166
|
+
setups with a symlink per skill get replaced without ceremony, and a directory
|
|
167
|
+
with real skills in it is backed up to `diverged/skills/` first. Already keep
|
|
168
|
+
skills in a dotfiles repo? Make the XDG folder a symlink to it. The sync leaves
|
|
169
|
+
a harness directory alone if the source points into it, instead of moving your
|
|
170
|
+
only copy away. `skillsSyncTarget` names the directory it links for each
|
|
171
|
+
harness, `null` for Copilot and Freebuff, which have no user skills directory
|
|
172
|
+
in the registry.
|
|
173
|
+
|
|
135
174
|
## 🗺️ Harnesses
|
|
136
175
|
|
|
137
176
|
| ID | Name | Project skills |
|
|
@@ -168,7 +207,7 @@ omp install @agntn/harnesses
|
|
|
168
207
|
}
|
|
169
208
|
```
|
|
170
209
|
|
|
171
|
-
|
|
210
|
+
Eleven tools, the same eleven 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).
|
|
172
211
|
|
|
173
212
|
## 🚫 What this does not do
|
|
174
213
|
|
|
@@ -182,11 +221,11 @@ Want a fourteenth? One class extending `Harness`, then `registerHarness`. `getHa
|
|
|
182
221
|
|
|
183
222
|
```bash
|
|
184
223
|
pnpm install
|
|
185
|
-
pnpm lint # builds first, then
|
|
224
|
+
pnpm lint # builds first, then vp lint and vp fmt --check
|
|
186
225
|
pnpm lint:fix
|
|
187
226
|
pnpm typecheck
|
|
188
227
|
pnpm test:run
|
|
189
|
-
pnpm build #
|
|
228
|
+
pnpm build # vp pack
|
|
190
229
|
pnpm docs # the Docus site, bundles src/ itself
|
|
191
230
|
```
|
|
192
231
|
|
package/dist/_chunks/agents.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { isHarnessId, listHarnesses } from "./
|
|
2
|
-
import { agentsSync } from "./tool-operations.mjs";
|
|
3
|
-
import { formatArgs, report } from "./output.mjs";
|
|
1
|
+
import { v as isHarnessId, y as listHarnesses } from "./skills-sync.mjs";
|
|
2
|
+
import { r as agentsSync } from "./tool-operations.mjs";
|
|
3
|
+
import { n as formatArgs, r as report } from "./output.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import { consola } from "consola";
|
|
6
|
+
//#region src/commands/agents.ts
|
|
6
7
|
var agents_default = defineCommand({
|
|
7
8
|
meta: { description: "Manage the shared global instructions file across harnesses" },
|
|
8
9
|
subCommands: { sync: defineCommand({
|
|
@@ -29,6 +30,7 @@ var agents_default = defineCommand({
|
|
|
29
30
|
}
|
|
30
31
|
}) }
|
|
31
32
|
});
|
|
33
|
+
//#endregion
|
|
32
34
|
export { agents_default as default };
|
|
33
35
|
|
|
34
36
|
//# sourceMappingURL=agents.mjs.map
|
|
@@ -1 +1 @@
|
|
|
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":"
|
|
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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { isHarnessId, listHarnesses } from "./
|
|
2
|
-
import {
|
|
3
|
-
import { formatArgs, report } from "./output.mjs";
|
|
1
|
+
import { v as isHarnessId, y as listHarnesses } from "./skills-sync.mjs";
|
|
2
|
+
import { c as mcpList, l as mcpRemove, s as mcpAdd, u as mcpSync } from "./tool-operations.mjs";
|
|
3
|
+
import { n as formatArgs, r as report } from "./output.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import { consola } from "consola";
|
|
6
|
+
//#region src/commands/mcp-servers.ts
|
|
6
7
|
function requireHarnessId(id) {
|
|
7
8
|
if (!isHarnessId(id)) {
|
|
8
9
|
consola.error(`Unknown harness: ${id}\nKnown: ${listHarnesses().join(", ")}`);
|
|
@@ -115,6 +116,7 @@ var mcp_servers_default = defineCommand({
|
|
|
115
116
|
})
|
|
116
117
|
}
|
|
117
118
|
});
|
|
119
|
+
//#endregion
|
|
118
120
|
export { mcp_servers_default as default };
|
|
119
121
|
|
|
120
122
|
//# sourceMappingURL=mcp-servers.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-servers.mjs","names":[],"sources":["../../src/commands/mcp-servers.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { consola } from \"consola\";\nimport { isHarnessId, listHarnesses } from \"../registry.ts\";\nimport { mcpAdd, mcpList, mcpRemove, mcpSync } from \"../tool-operations.ts\";\nimport { formatArgs, report } from \"./output.ts\";\n\nfunction requireHarnessId(id: string): string {\n if (!isHarnessId(id)) {\n consola.error(`Unknown harness: ${id}\\nKnown: ${listHarnesses().join(\", \")}`);\n process.exit(1);\n }\n return id;\n}\n\nexport default defineCommand({\n meta: { description: \"Manage MCP servers across harness configs\" },\n subCommands: {\n list: defineCommand({\n meta: { description: \"List configured MCP servers, normalized\" },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: false },\n ...formatArgs,\n },\n run({ args }) {\n const id = args.id === undefined ? undefined : requireHarnessId(args.id as string);\n report(mcpList(id), args);\n },\n }),\n add: defineCommand({\n meta: { description: \"Add or replace one MCP server in a harness config\" },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: true },\n name: { type: \"positional\" as const, description: \"Server name\", required: true },\n command: { type: \"string\" as const, description: \"Binary for a stdio server\" },\n args: {\n type: \"string\" as const,\n description: \"Space-separated arguments for a stdio server\",\n },\n env: { type: \"string\" as const, description: \"Comma-separated K=V environment variables\" },\n url: { type: \"string\" as const, description: \"URL for an http/sse server\" },\n scope: { type: \"string\" as const, description: \"user (default) or project\" },\n ...formatArgs,\n },\n run({ args }) {\n const env = args.env\n ? Object.fromEntries(\n args.env\n .split(\",\")\n .map((pair) => pair.split(/=(.*)/s))\n .filter((pair) => pair[0])\n .map((pair) => [pair[0] as string, pair[1] ?? \"\"]),\n )\n : undefined;\n const result = mcpAdd(\n requireHarnessId(args.id as string),\n {\n name: args.name as string,\n ...(args.command ? { command: args.command } : {}),\n ...(args.args ? { args: args.args.split(\" \").filter(Boolean) } : {}),\n ...(env ? { env } : {}),\n ...(args.url ? { url: args.url } : {}),\n },\n args.scope === \"project\" ? \"project\" : \"user\",\n );\n report(result, args);\n },\n }),\n sync: defineCommand({\n meta: {\n description: \"Reset harness configs to the master list from ~/.config/agntn/mcp.jsonc\",\n },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: false },\n ...formatArgs,\n },\n run({ args }) {\n const id = args.id === undefined ? undefined : requireHarnessId(args.id as string);\n report(mcpSync(id), args);\n },\n }),\n remove: defineCommand({\n meta: { description: \"Remove one MCP server from a harness config\" },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: true },\n name: { type: \"positional\" as const, description: \"Server name\", required: true },\n scope: { type: \"string\" as const, description: \"user (default) or project\" },\n ...formatArgs,\n },\n run({ args }) {\n report(\n mcpRemove(\n requireHarnessId(args.id as string),\n args.name as string,\n args.scope === \"project\" ? \"project\" : \"user\",\n ),\n args,\n );\n },\n }),\n },\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp-servers.mjs","names":[],"sources":["../../src/commands/mcp-servers.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { consola } from \"consola\";\nimport { isHarnessId, listHarnesses } from \"../registry.ts\";\nimport { mcpAdd, mcpList, mcpRemove, mcpSync } from \"../tool-operations.ts\";\nimport { formatArgs, report } from \"./output.ts\";\n\nfunction requireHarnessId(id: string): string {\n if (!isHarnessId(id)) {\n consola.error(`Unknown harness: ${id}\\nKnown: ${listHarnesses().join(\", \")}`);\n process.exit(1);\n }\n return id;\n}\n\nexport default defineCommand({\n meta: { description: \"Manage MCP servers across harness configs\" },\n subCommands: {\n list: defineCommand({\n meta: { description: \"List configured MCP servers, normalized\" },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: false },\n ...formatArgs,\n },\n run({ args }) {\n const id = args.id === undefined ? undefined : requireHarnessId(args.id as string);\n report(mcpList(id), args);\n },\n }),\n add: defineCommand({\n meta: { description: \"Add or replace one MCP server in a harness config\" },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: true },\n name: { type: \"positional\" as const, description: \"Server name\", required: true },\n command: { type: \"string\" as const, description: \"Binary for a stdio server\" },\n args: {\n type: \"string\" as const,\n description: \"Space-separated arguments for a stdio server\",\n },\n env: { type: \"string\" as const, description: \"Comma-separated K=V environment variables\" },\n url: { type: \"string\" as const, description: \"URL for an http/sse server\" },\n scope: { type: \"string\" as const, description: \"user (default) or project\" },\n ...formatArgs,\n },\n run({ args }) {\n const env = args.env\n ? Object.fromEntries(\n args.env\n .split(\",\")\n .map((pair) => pair.split(/=(.*)/s))\n .filter((pair) => pair[0])\n .map((pair) => [pair[0] as string, pair[1] ?? \"\"]),\n )\n : undefined;\n const result = mcpAdd(\n requireHarnessId(args.id as string),\n {\n name: args.name as string,\n ...(args.command ? { command: args.command } : {}),\n ...(args.args ? { args: args.args.split(\" \").filter(Boolean) } : {}),\n ...(env ? { env } : {}),\n ...(args.url ? { url: args.url } : {}),\n },\n args.scope === \"project\" ? \"project\" : \"user\",\n );\n report(result, args);\n },\n }),\n sync: defineCommand({\n meta: {\n description: \"Reset harness configs to the master list from ~/.config/agntn/mcp.jsonc\",\n },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: false },\n ...formatArgs,\n },\n run({ args }) {\n const id = args.id === undefined ? undefined : requireHarnessId(args.id as string);\n report(mcpSync(id), args);\n },\n }),\n remove: defineCommand({\n meta: { description: \"Remove one MCP server from a harness config\" },\n args: {\n id: { type: \"positional\" as const, description: \"Harness id\", required: true },\n name: { type: \"positional\" as const, description: \"Server name\", required: true },\n scope: { type: \"string\" as const, description: \"user (default) or project\" },\n ...formatArgs,\n },\n run({ args }) {\n report(\n mcpRemove(\n requireHarnessId(args.id as string),\n args.name as string,\n args.scope === \"project\" ? \"project\" : \"user\",\n ),\n args,\n );\n },\n }),\n },\n});\n"],"mappings":";;;;;;AAMA,SAAS,iBAAiB,IAAoB;CAC5C,IAAI,CAAC,YAAY,EAAE,GAAG;EACpB,QAAQ,MAAM,oBAAoB,GAAG,WAAW,cAAc,CAAC,CAAC,KAAK,IAAI,GAAG;EAC5E,QAAQ,KAAK,CAAC;CAChB;CACA,OAAO;AACT;AAEA,IAAA,sBAAe,cAAc;CAC3B,MAAM,EAAE,aAAa,4CAA4C;CACjE,aAAa;EACX,MAAM,cAAc;GAClB,MAAM,EAAE,aAAa,0CAA0C;GAC/D,MAAM;IACJ,IAAI;KAAE,MAAM;KAAuB,aAAa;KAAc,UAAU;IAAM;IAC9E,GAAG;GACL;GACA,IAAI,EAAE,QAAQ;IACZ,MAAM,KAAK,KAAK,OAAO,KAAA,IAAY,KAAA,IAAY,iBAAiB,KAAK,EAAY;IACjF,OAAO,QAAQ,EAAE,GAAG,IAAI;GAC1B;EACF,CAAC;EACD,KAAK,cAAc;GACjB,MAAM,EAAE,aAAa,oDAAoD;GACzE,MAAM;IACJ,IAAI;KAAE,MAAM;KAAuB,aAAa;KAAc,UAAU;IAAK;IAC7E,MAAM;KAAE,MAAM;KAAuB,aAAa;KAAe,UAAU;IAAK;IAChF,SAAS;KAAE,MAAM;KAAmB,aAAa;IAA4B;IAC7E,MAAM;KACJ,MAAM;KACN,aAAa;IACf;IACA,KAAK;KAAE,MAAM;KAAmB,aAAa;IAA4C;IACzF,KAAK;KAAE,MAAM;KAAmB,aAAa;IAA6B;IAC1E,OAAO;KAAE,MAAM;KAAmB,aAAa;IAA4B;IAC3E,GAAG;GACL;GACA,IAAI,EAAE,QAAQ;IACZ,MAAM,MAAM,KAAK,MACb,OAAO,YACL,KAAK,IACF,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,MAAM,QAAQ,CAAC,CAAC,CACnC,QAAQ,SAAS,KAAK,EAAE,CAAC,CACzB,KAAK,SAAS,CAAC,KAAK,IAAc,KAAK,MAAM,EAAE,CAAC,CACrD,IACA,KAAA;IACJ,MAAM,SAAS,OACb,iBAAiB,KAAK,EAAY,GAClC;KACE,MAAM,KAAK;KACX,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;KAChD,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,EAAE,IAAI,CAAC;KAClE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;KACrB,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;IACtC,GACA,KAAK,UAAU,YAAY,YAAY,MACzC;IACA,OAAO,QAAQ,IAAI;GACrB;EACF,CAAC;EACD,MAAM,cAAc;GAClB,MAAM,EACJ,aAAa,0EACf;GACA,MAAM;IACJ,IAAI;KAAE,MAAM;KAAuB,aAAa;KAAc,UAAU;IAAM;IAC9E,GAAG;GACL;GACA,IAAI,EAAE,QAAQ;IACZ,MAAM,KAAK,KAAK,OAAO,KAAA,IAAY,KAAA,IAAY,iBAAiB,KAAK,EAAY;IACjF,OAAO,QAAQ,EAAE,GAAG,IAAI;GAC1B;EACF,CAAC;EACD,QAAQ,cAAc;GACpB,MAAM,EAAE,aAAa,8CAA8C;GACnE,MAAM;IACJ,IAAI;KAAE,MAAM;KAAuB,aAAa;KAAc,UAAU;IAAK;IAC7E,MAAM;KAAE,MAAM;KAAuB,aAAa;KAAe,UAAU;IAAK;IAChF,OAAO;KAAE,MAAM;KAAmB,aAAa;IAA4B;IAC3E,GAAG;GACL;GACA,IAAI,EAAE,QAAQ;IACZ,OACE,UACE,iBAAiB,KAAK,EAAY,GAClC,KAAK,MACL,KAAK,UAAU,YAAY,YAAY,MACzC,GACA,IACF;GACF;EACF,CAAC;CACH;AACF,CAAC"}
|
package/dist/_chunks/mcp.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { t as exitOnClosedPipe } from "./output.mjs";
|
|
1
2
|
import { defineCommand } from "citty";
|
|
2
3
|
import { LogLevels, consola } from "consola";
|
|
4
|
+
//#region src/commands/mcp.ts
|
|
3
5
|
var mcp_default = defineCommand({
|
|
4
6
|
meta: {
|
|
5
7
|
name: "mcp",
|
|
@@ -8,9 +10,16 @@ var mcp_default = defineCommand({
|
|
|
8
10
|
async run() {
|
|
9
11
|
consola.level = LogLevels.warn;
|
|
10
12
|
const [{ createMcpServer }, { StdioServerTransport }] = await Promise.all([import("../mcp.mjs"), import("@modelcontextprotocol/sdk/server/stdio.js")]);
|
|
11
|
-
|
|
13
|
+
const server = createMcpServer();
|
|
14
|
+
await server.connect(new StdioServerTransport());
|
|
15
|
+
process.stdin.once("end", () => void server.close());
|
|
16
|
+
process.stdout.off("error", exitOnClosedPipe).on("error", (error) => {
|
|
17
|
+
if (error.code !== "EPIPE") throw error;
|
|
18
|
+
server.close();
|
|
19
|
+
});
|
|
12
20
|
}
|
|
13
21
|
});
|
|
22
|
+
//#endregion
|
|
14
23
|
export { mcp_default as default };
|
|
15
24
|
|
|
16
25
|
//# sourceMappingURL=mcp.mjs.map
|
package/dist/_chunks/mcp.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.mjs","names":[],"sources":["../../src/commands/mcp.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { consola, LogLevels } from \"consola\";\n\nexport default defineCommand({\n meta: {\n name: \"mcp\",\n description: \"Run the harnesses MCP server over stdio\",\n },\n /**\n * The server and the SDK load here, because citty resolves every subcommand\n * to print `harnesses --help` and to look for an alias of an unknown command.\n *\n * stdout carries the JSON-RPC frames. consola's default reporter sends\n * anything at log level or below to that same descriptor, and `DEBUG` in the\n * environment raises the level on import, so one stray line would corrupt\n * the stream. Warnings and errors still reach stderr.\n */\n async run() {\n consola.level = LogLevels.warn;\n\n const [{ createMcpServer }, { StdioServerTransport }] = await Promise.all([\n import(\"../mcp.ts\"),\n import(\"@modelcontextprotocol/sdk/server/stdio.js\"),\n ]);\n
|
|
1
|
+
{"version":3,"file":"mcp.mjs","names":[],"sources":["../../src/commands/mcp.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { consola, LogLevels } from \"consola\";\nimport { exitOnClosedPipe } from \"./output.ts\";\n\nexport default defineCommand({\n meta: {\n name: \"mcp\",\n description: \"Run the harnesses MCP server over stdio\",\n },\n /**\n * The server and the SDK load here, because citty resolves every subcommand\n * to print `harnesses --help` and to look for an alias of an unknown command.\n *\n * stdout carries the JSON-RPC frames. consola's default reporter sends\n * anything at log level or below to that same descriptor, and `DEBUG` in the\n * environment raises the level on import, so one stray line would corrupt\n * the stream. Warnings and errors still reach stderr.\n *\n * The SDK's stdio transport never watches for the end of stdin, so a client\n * that exits would leave an in-flight `harnesses_run` going until its own\n * deadline. Closing the server aborts every pending request signal, which\n * stops each run's process group the same way a cancellation does, and the\n * process then exits once that cleanup has nothing left to wait for. A\n * write to a stdout the client already closed means the same, so it closes\n * the server too instead of exiting past that cleanup.\n */\n async run() {\n consola.level = LogLevels.warn;\n\n const [{ createMcpServer }, { StdioServerTransport }] = await Promise.all([\n import(\"../mcp.ts\"),\n import(\"@modelcontextprotocol/sdk/server/stdio.js\"),\n ]);\n const server = createMcpServer();\n await server.connect(new StdioServerTransport());\n process.stdin.once(\"end\", () => void server.close());\n process.stdout\n .off(\"error\", exitOnClosedPipe)\n .on(\"error\", (error: Readonly<NodeJS.ErrnoException>) => {\n if (error.code !== \"EPIPE\") throw error;\n void server.close();\n });\n },\n});\n"],"mappings":";;;;AAIA,IAAA,cAAe,cAAc;CAC3B,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CAkBA,MAAM,MAAM;EACV,QAAQ,QAAQ,UAAU;EAE1B,MAAM,CAAC,EAAE,mBAAmB,EAAE,0BAA0B,MAAM,QAAQ,IAAI,CACxE,OAAO,eACP,OAAO,4CACT,CAAC;EACD,MAAM,SAAS,gBAAgB;EAC/B,MAAM,OAAO,QAAQ,IAAI,qBAAqB,CAAC;EAC/C,QAAQ,MAAM,KAAK,aAAa,KAAK,OAAO,MAAM,CAAC;EACnD,QAAQ,OACL,IAAI,SAAS,gBAAgB,CAAC,CAC9B,GAAG,UAAU,UAA2C;GACvD,IAAI,MAAM,SAAS,SAAS,MAAM;GAClC,OAAY,MAAM;EACpB,CAAC;CACL;AACF,CAAC"}
|
package/dist/_chunks/output.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { stripVTControlCharacters } from "node:util";
|
|
2
2
|
import { consola } from "consola";
|
|
3
3
|
import { encode } from "@toon-format/toon";
|
|
4
|
+
//#region src/commands/output.ts
|
|
4
5
|
const formatArgs = {
|
|
5
6
|
json: {
|
|
6
7
|
type: "boolean",
|
|
@@ -11,6 +12,10 @@ const formatArgs = {
|
|
|
11
12
|
description: "Output as TOON"
|
|
12
13
|
}
|
|
13
14
|
};
|
|
15
|
+
function exitOnClosedPipe(error) {
|
|
16
|
+
if (error.code !== "EPIPE") throw error;
|
|
17
|
+
process.exit();
|
|
18
|
+
}
|
|
14
19
|
const UNSAFE_TERMINAL_CONTROLS = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
|
|
15
20
|
const UNSAFE_UNICODE_FORMATTING = /[\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
16
21
|
function sanitize(text) {
|
|
@@ -23,6 +28,7 @@ function report(result, args) {
|
|
|
23
28
|
else console.log(sanitize(result.content[0]?.text));
|
|
24
29
|
if (result.isError) process.exitCode = 1;
|
|
25
30
|
}
|
|
26
|
-
|
|
31
|
+
//#endregion
|
|
32
|
+
export { formatArgs as n, report as r, exitOnClosedPipe as t };
|
|
27
33
|
|
|
28
34
|
//# sourceMappingURL=output.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.mjs","names":["toToon"],"sources":["../../src/commands/output.ts"],"sourcesContent":["import { stripVTControlCharacters } from \"node:util\";\nimport { encode as toToon } from \"@toon-format/toon\";\nimport { consola } from \"consola\";\nimport type { ToolResult } from \"../tool-operations.ts\";\n\nexport const formatArgs = {\n json: { type: \"boolean\" as const, description: \"Output as JSON\" },\n toon: { type: \"boolean\" as const, description: \"Output as TOON\" },\n};\n\ntype OutputFormat = Readonly<{ json?: boolean; toon?: boolean }>;\n\n/** Control bytes removed from reports because they can echo untrusted configuration values. */\n/* oxlint-disable-next-line no-control-regex */\nconst UNSAFE_TERMINAL_CONTROLS = /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F]/g;\nconst UNSAFE_UNICODE_FORMATTING = /[\\p{Cf}\\p{Zl}\\p{Zp}]/gu;\n\nfunction sanitize(text: string | undefined): string {\n return stripVTControlCharacters(text ?? \"\")\n .replace(UNSAFE_TERMINAL_CONTROLS, \" \")\n .replace(UNSAFE_UNICODE_FORMATTING, \" \");\n}\n\n/**\n * Emits one tool result in the selected machine or human format.\n *\n * @param result - Shared executor result to emit.\n * @param args - Selected output format.\n */\nexport function report(result: ToolResult<unknown>, args: OutputFormat): void {\n if (args.json) console.log(JSON.stringify(result.details, null, 2));\n else if (args.toon) console.log(toToon(result.details));\n else if (result.isError) consola.error(sanitize(result.content[0]?.text));\n else console.log(sanitize(result.content[0]?.text));\n\n if (result.isError) process.exitCode = 1;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"output.mjs","names":["toToon"],"sources":["../../src/commands/output.ts"],"sourcesContent":["import { stripVTControlCharacters } from \"node:util\";\nimport { encode as toToon } from \"@toon-format/toon\";\nimport { consola } from \"consola\";\nimport type { ToolResult } from \"../tool-operations.ts\";\n\nexport const formatArgs = {\n json: { type: \"boolean\" as const, description: \"Output as JSON\" },\n toon: { type: \"boolean\" as const, description: \"Output as TOON\" },\n};\n\n/**\n * Ends the process once the reader of stdout or stderr is gone, as after\n * `| head -1` or a pager that quits early. Node ignores SIGPIPE, so without a\n * listener the next write throws `EPIPE` with a stack trace. The exit code\n * stays whatever the command set.\n *\n * @param error - The error the stream emitted.\n */\nexport function exitOnClosedPipe(error: Readonly<NodeJS.ErrnoException>): void {\n if (error.code !== \"EPIPE\") throw error;\n process.exit();\n}\n\ntype OutputFormat = Readonly<{ json?: boolean; toon?: boolean }>;\n\n/** Control bytes removed from reports because they can echo untrusted configuration values. */\n/* oxlint-disable-next-line no-control-regex */\nconst UNSAFE_TERMINAL_CONTROLS = /[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F]/g;\nconst UNSAFE_UNICODE_FORMATTING = /[\\p{Cf}\\p{Zl}\\p{Zp}]/gu;\n\nfunction sanitize(text: string | undefined): string {\n return stripVTControlCharacters(text ?? \"\")\n .replace(UNSAFE_TERMINAL_CONTROLS, \" \")\n .replace(UNSAFE_UNICODE_FORMATTING, \" \");\n}\n\n/**\n * Emits one tool result in the selected machine or human format.\n *\n * @param result - Shared executor result to emit.\n * @param args - Selected output format.\n */\nexport function report(result: ToolResult<unknown>, args: OutputFormat): void {\n if (args.json) console.log(JSON.stringify(result.details, null, 2));\n else if (args.toon) console.log(toToon(result.details));\n else if (result.isError) consola.error(sanitize(result.content[0]?.text));\n else console.log(sanitize(result.content[0]?.text));\n\n if (result.isError) process.exitCode = 1;\n}\n"],"mappings":";;;;AAKA,MAAa,aAAa;CACxB,MAAM;EAAE,MAAM;EAAoB,aAAa;CAAiB;CAChE,MAAM;EAAE,MAAM;EAAoB,aAAa;CAAiB;AAClE;AAUA,SAAgB,iBAAiB,OAA8C;CAC7E,IAAI,MAAM,SAAS,SAAS,MAAM;CAClC,QAAQ,KAAK;AACf;AAMA,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAElC,SAAS,SAAS,MAAkC;CAClD,OAAO,yBAAyB,QAAQ,EAAE,CAAC,CACxC,QAAQ,0BAA0B,GAAG,CAAC,CACtC,QAAQ,2BAA2B,GAAG;AAC3C;AAQA,SAAgB,OAAO,QAA6B,MAA0B;CAC5E,IAAI,KAAK,MAAM,QAAQ,IAAI,KAAK,UAAU,OAAO,SAAS,MAAM,CAAC,CAAC;MAC7D,IAAI,KAAK,MAAM,QAAQ,IAAIA,OAAO,OAAO,OAAO,CAAC;MACjD,IAAI,OAAO,SAAS,QAAQ,MAAM,SAAS,OAAO,QAAQ,EAAE,EAAE,IAAI,CAAC;MACnE,QAAQ,IAAI,SAAS,OAAO,QAAQ,EAAE,EAAE,IAAI,CAAC;CAElD,IAAI,OAAO,SAAS,QAAQ,WAAW;AACzC"}
|
package/dist/_chunks/prompts.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { promptsSync } from "./tool-operations.mjs";
|
|
2
|
-
import { formatArgs, report } from "./output.mjs";
|
|
1
|
+
import { d as promptsSync } from "./tool-operations.mjs";
|
|
2
|
+
import { n as formatArgs, r as report } from "./output.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
|
+
//#region src/commands/prompts.ts
|
|
4
5
|
var prompts_default = defineCommand({
|
|
5
6
|
meta: { description: "Manage shared prompt templates across harnesses" },
|
|
6
7
|
subCommands: { sync: defineCommand({
|
|
@@ -23,6 +24,7 @@ var prompts_default = defineCommand({
|
|
|
23
24
|
}
|
|
24
25
|
}) }
|
|
25
26
|
});
|
|
27
|
+
//#endregion
|
|
26
28
|
export { prompts_default as default };
|
|
27
29
|
|
|
28
30
|
//# sourceMappingURL=prompts.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/commands/prompts.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { promptsSync } from \"../tool-operations.ts\";\nimport { formatArgs, report } from \"./output.ts\";\n\nexport default defineCommand({\n meta: { description: \"Manage shared prompt templates across harnesses\" },\n subCommands: {\n sync: defineCommand({\n meta: {\n description:\n \"Sync Markdown templates from the agntn XDG data directory into harness destinations\",\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 report(promptsSync(id, args.check === true), args);\n },\n }),\n },\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/commands/prompts.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { promptsSync } from \"../tool-operations.ts\";\nimport { formatArgs, report } from \"./output.ts\";\n\nexport default defineCommand({\n meta: { description: \"Manage shared prompt templates across harnesses\" },\n subCommands: {\n sync: defineCommand({\n meta: {\n description:\n \"Sync Markdown templates from the agntn XDG data directory into harness destinations\",\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 report(promptsSync(id, args.check === true), args);\n },\n }),\n },\n});\n"],"mappings":";;;;AAIA,IAAA,kBAAe,cAAc;CAC3B,MAAM,EAAE,aAAa,kDAAkD;CACvE,aAAa,EACX,MAAM,cAAc;EAClB,MAAM,EACJ,aACE,sFACJ;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,OAAO,YAAY,IAAI,KAAK,UAAU,IAAI,GAAG,IAAI;EACnD;CACF,CAAC,EACH;AACF,CAAC"}
|