@agntn/harnesses 0.3.0 → 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.
Files changed (50) hide show
  1. package/README.md +34 -4
  2. package/dist/_chunks/agents.mjs +5 -3
  3. package/dist/_chunks/agents.mjs.map +1 -1
  4. package/dist/_chunks/mcp-servers.mjs +5 -3
  5. package/dist/_chunks/mcp-servers.mjs.map +1 -1
  6. package/dist/_chunks/mcp.mjs +10 -1
  7. package/dist/_chunks/mcp.mjs.map +1 -1
  8. package/dist/_chunks/output.mjs +7 -1
  9. package/dist/_chunks/output.mjs.map +1 -1
  10. package/dist/_chunks/prompts.mjs +4 -2
  11. package/dist/_chunks/prompts.mjs.map +1 -1
  12. package/dist/_chunks/{prompt-sync.d.mts → skills-sync.d.mts} +103 -50
  13. package/dist/_chunks/skills-sync.d.mts.map +1 -0
  14. package/dist/_chunks/{prompt-sync.mjs → skills-sync.mjs} +298 -98
  15. package/dist/_chunks/skills-sync.mjs.map +1 -0
  16. package/dist/_chunks/skills.mjs +30 -0
  17. package/dist/_chunks/skills.mjs.map +1 -0
  18. package/dist/_chunks/tool-operations.mjs +22 -2
  19. package/dist/_chunks/tool-operations.mjs.map +1 -1
  20. package/dist/_chunks/tool-schemas.d.mts +21 -13
  21. package/dist/_chunks/tool-schemas.d.mts.map +1 -1
  22. package/dist/_chunks/tool-schemas.mjs +7 -1
  23. package/dist/_chunks/tool-schemas.mjs.map +1 -1
  24. package/dist/_chunks/types.mjs +5 -2
  25. package/dist/_chunks/types.mjs.map +1 -1
  26. package/dist/cli.mjs +9 -3
  27. package/dist/cli.mjs.map +1 -1
  28. package/dist/index.d.mts +16 -2
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.mjs +5 -3
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/mcp.d.mts +2 -0
  33. package/dist/mcp.d.mts.map +1 -1
  34. package/dist/mcp.mjs +6256 -3
  35. package/dist/mcp.mjs.map +1 -1
  36. package/dist/tool-operations.d.mts +14 -3
  37. package/dist/tool-operations.d.mts.map +1 -1
  38. package/dist/tool-operations.mjs +3 -3
  39. package/dist/tool-schemas.d.mts +2 -2
  40. package/dist/tool-schemas.mjs +1 -1
  41. package/dist/typebox.LICENSE +23 -0
  42. package/package.json +18 -18
  43. package/packages/omp/extensions/harnesses.ts +18 -0
  44. package/packages/pi/extensions/harnesses.ts +26 -45
  45. package/packages/shared/tui.ts +13 -0
  46. package/dist/THIRD-PARTY-LICENSES.md +0 -36
  47. package/dist/_chunks/libs/typebox.mjs +0 -5439
  48. package/dist/_chunks/libs/typebox.mjs.map +0 -1
  49. package/dist/_chunks/prompt-sync.d.mts.map +0 -1
  50. 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
- - 🤖 **Ten tools, three doors.** MCP, Pi and OMP call the same executors.
29
+ - 🤖 **Eleven tools, three doors.** MCP, Pi and OMP call the same executors.
30
30
 
31
31
  ## 📦 Install
32
32
 
@@ -74,6 +74,7 @@ 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
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).
@@ -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.
@@ -141,6 +143,34 @@ location. `promptTemplateSyncTarget` is the single stable user destination the
141
143
  sync owns for that harness. Formats, frontmatter and CLI limits are in the
142
144
  [registry guide](https://harnesses.agntn.dev/guide/registry#prompt-templates).
143
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
+
144
174
  ## 🗺️ Harnesses
145
175
 
146
176
  | ID | Name | Project skills |
@@ -177,7 +207,7 @@ omp install @agntn/harnesses
177
207
  }
178
208
  ```
179
209
 
180
- 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).
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).
181
211
 
182
212
  ## 🚫 What this does not do
183
213
 
@@ -191,11 +221,11 @@ Want a fourteenth? One class extending `Harness`, then `registerHarness`. `getHa
191
221
 
192
222
  ```bash
193
223
  pnpm install
194
- pnpm lint # builds first, then oxlint and oxfmt --check
224
+ pnpm lint # builds first, then vp lint and vp fmt --check
195
225
  pnpm lint:fix
196
226
  pnpm typecheck
197
227
  pnpm test:run
198
- pnpm build # obuild
228
+ pnpm build # vp pack
199
229
  pnpm docs # the Docus site, bundles src/ itself
200
230
  ```
201
231
 
@@ -1,8 +1,9 @@
1
- import { isHarnessId, listHarnesses } from "./prompt-sync.mjs";
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":";;;;;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
+ {"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 "./prompt-sync.mjs";
2
- import { mcpAdd, mcpList, mcpRemove, mcpSync } 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 { 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":";;;;;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"}
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"}
@@ -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
- await createMcpServer().connect(new StdioServerTransport());
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
@@ -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 await createMcpServer().connect(new StdioServerTransport());\n },\n});\n"],"mappings":";;AAGA,IAAA,cAAe,cAAc;CAC3B,MAAM;EACJ,MAAM;EACN,aAAa;CACf"}
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"}
@@ -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
- export { formatArgs, report };
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":";;;AAKA,MAAa,aAAa;CACxB,MAAM;EAAE,MAAM;EAAoB,aAAa;CAAiB;CAChE,MAAM;EAAE,MAAM;EAAoB,aAAa;CAAiB;AAClE;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;;;;;;CAQA,IAAA,OAAgB,SAAO,QAA6B,WAA0B;AAC5E;AAEK,SAAA,YAAW"}
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"}
@@ -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":";;;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"}
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"}