@agntn/harnesses 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +47 -15
  2. package/dist/_chunks/agents.mjs +2 -30
  3. package/dist/_chunks/agents.mjs.map +1 -1
  4. package/dist/_chunks/libs/typebox.mjs +12 -9
  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} +122 -10
  13. package/dist/_chunks/prompt-sync.d.mts.map +1 -0
  14. package/dist/_chunks/{agents-sync.mjs → prompt-sync.mjs} +863 -50
  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 +57 -19
  19. package/dist/_chunks/tool-operations.mjs.map +1 -1
  20. package/dist/_chunks/tool-schemas.d.mts +6 -0
  21. package/dist/_chunks/tool-schemas.d.mts.map +1 -1
  22. package/dist/_chunks/tool-schemas.mjs +14 -9
  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 +26 -8
  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 +2 -2
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/mcp.d.mts.map +1 -1
  33. package/dist/mcp.mjs +179 -1
  34. package/dist/mcp.mjs.map +1 -0
  35. package/dist/tool-operations.d.mts +19 -3
  36. package/dist/tool-operations.d.mts.map +1 -1
  37. package/dist/tool-operations.mjs +2 -2
  38. package/dist/tool-schemas.d.mts +2 -2
  39. package/package.json +16 -14
  40. package/packages/omp/extensions/harnesses.ts +19 -1
  41. package/packages/pi/extensions/harnesses.ts +23 -1
  42. package/packages/shared/tui.ts +22 -1
  43. package/dist/_chunks/agents-sync.d.mts.map +0 -1
  44. package/dist/_chunks/agents-sync.mjs.map +0 -1
  45. package/dist/_chunks/mcp2.mjs +0 -17
  46. package/dist/_chunks/mcp2.mjs.map +0 -1
@@ -1,38 +1,8 @@
1
- import { isHarnessId, listHarnesses } from "./agents-sync.mjs";
1
+ import { isHarnessId, listHarnesses } from "./prompt-sync.mjs";
2
2
  import { mcpAdd, mcpList, mcpRemove, mcpSync } 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 sanitize(text) {
19
- return stripVTControlCharacters(text ?? "").replace(UNSAFE_TERMINAL_CONTROLS, " ");
20
- }
21
- function report(result, args) {
22
- if (result.isError) {
23
- consola.error(sanitize(result.content[0]?.text));
24
- process.exit(1);
25
- }
26
- if (args.json) {
27
- console.log(JSON.stringify(result.details, null, 2));
28
- return;
29
- }
30
- if (args.toon) {
31
- console.log(encode(result.details));
32
- return;
33
- }
34
- console.log(sanitize(result.content[0]?.text));
35
- }
36
6
  function requireHarnessId(id) {
37
7
  if (!isHarnessId(id)) {
38
8
  consola.error(`Unknown harness: ${id}\nKnown: ${listHarnesses().join(", ")}`);
@@ -95,13 +65,14 @@ var mcp_servers_default = defineCommand({
95
65
  },
96
66
  run({ args }) {
97
67
  const env = args.env ? Object.fromEntries(args.env.split(",").map((pair) => pair.split(/=(.*)/s)).filter((pair) => pair[0]).map((pair) => [pair[0], pair[1] ?? ""])) : void 0;
98
- report(mcpAdd(requireHarnessId(args.id), {
68
+ const result = mcpAdd(requireHarnessId(args.id), {
99
69
  name: args.name,
100
70
  ...args.command ? { command: args.command } : {},
101
71
  ...args.args ? { args: args.args.split(" ").filter(Boolean) } : {},
102
72
  ...env ? { env } : {},
103
73
  ...args.url ? { url: args.url } : {}
104
- }, args.scope === "project" ? "project" : "user"), args);
74
+ }, args.scope === "project" ? "project" : "user");
75
+ report(result, args);
105
76
  }
106
77
  }),
107
78
  sync: defineCommand({
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-servers.mjs","names":["toToon"],"sources":["../../src/commands/mcp-servers.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 { mcpAdd, mcpList, mcpRemove, mcpSync, 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// MCP listings echo values read from project-controlled config files, so a\n// malicious server name could smuggle ANSI/OSC sequences into the terminal.\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 sanitize(text: string | undefined): string {\n return stripVTControlCharacters(text ?? \"\").replace(UNSAFE_TERMINAL_CONTROLS, \" \");\n}\n\n/**\n * Emits one tool result: machine format when asked, sanitized text otherwise.\n *\n * @param result - Tool result to emit.\n * @param args - Selected output-format flags.\n */\nfunction report(\n result: ToolResult<unknown>,\n args: Readonly<{ json?: boolean; toon?: boolean }>,\n): void {\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\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":"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"}
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,166 +1,16 @@
1
- import { Check, Errors, typebox_exports } from "./libs/typebox.mjs";
2
- import { version } from "./types.mjs";
3
- import { harnessToolSchemas } from "./tool-schemas.mjs";
4
- import { agentsSync, detectHarnesses, harnessInfo, listHarnessModels, mcpAdd, mcpList, mcpRemove, mcpSync, runHarness } from "./tool-operations.mjs";
5
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
- import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
7
- const READ_ONLY = {
8
- readOnlyHint: true,
9
- destructiveHint: false,
10
- idempotentHint: true,
11
- openWorldHint: false
12
- };
13
- const EXEC_READ = {
14
- readOnlyHint: false,
15
- destructiveHint: false,
16
- idempotentHint: false,
17
- openWorldHint: true
18
- };
19
- const CONFIG_WRITE = {
20
- readOnlyHint: false,
21
- destructiveHint: true,
22
- idempotentHint: true,
23
- openWorldHint: false
24
- };
25
- const schemas = harnessToolSchemas(typebox_exports);
26
- const tools = [
27
- {
28
- name: "harnesses_detect",
29
- title: "Harnesses Detect",
30
- description: "List every known AI coding harness with its install state and version.",
31
- inputSchema: schemas.detect,
32
- annotations: READ_ONLY,
33
- execute: () => detectHarnesses()
34
- },
35
- {
36
- name: "harnesses_info",
37
- title: "Harnesses Info",
38
- description: "Full metadata for one or more AI coding harnesses, including supported invocation and model operations, configuration, sessions, instructions, skills, commands, hooks, and resolved paths.",
39
- inputSchema: schemas.info,
40
- annotations: READ_ONLY,
41
- execute: (args) => harnessInfo(args.id)
42
- },
43
- {
44
- name: "harnesses_models",
45
- title: "Harnesses Models",
46
- description: "List the models currently available to one AI coding harness through its native CLI, normalized across providers. An optional search filter is passed to harnesses that support it. The command may load project configuration and extensions.",
47
- inputSchema: schemas.models,
48
- annotations: EXEC_READ,
49
- execute: (args, signal) => listHarnessModels(args.id, {
50
- search: args.search,
51
- cwd: args.cwd,
52
- timeoutSeconds: args.timeoutSeconds,
53
- signal
54
- })
55
- },
56
- {
57
- name: "harnesses_run",
58
- title: "Harnesses Run",
59
- description: "Run one prompt through an AI coding harness's normalized non-interactive invocation and return its output. Pass tools=true whenever the task needs harness tools, including Grok native X search. Add readOnly=true to require native read-only tool enforcement. Pass tools=false only for an advisor without tools. Unsupported modes never widen access.",
60
- inputSchema: schemas.run,
61
- annotations: {
62
- readOnlyHint: false,
63
- destructiveHint: true,
64
- idempotentHint: false,
65
- openWorldHint: true
66
- },
67
- execute: (args, signal) => runHarness(args.id, args.prompt, {
68
- cwd: args.cwd,
69
- model: args.model,
70
- timeoutSeconds: args.timeoutSeconds,
71
- signal,
72
- structured: args.structured,
73
- tools: args.tools,
74
- readOnly: args.readOnly
75
- })
76
- },
77
- {
78
- name: "harnesses_mcp_list",
79
- title: "Harnesses MCP List",
80
- description: "List the MCP servers configured in each harness's config files, normalized across dialects. Omit id to scan every harness.",
81
- inputSchema: schemas.mcpList,
82
- annotations: READ_ONLY,
83
- execute: (args) => mcpList(args.id)
84
- },
85
- {
86
- name: "harnesses_mcp_add",
87
- title: "Harnesses MCP Add",
88
- description: "Add or replace one MCP server in a harness config. JSON configs are rewritten; TOML configs get a surgical, comment-preserving edit.",
89
- inputSchema: schemas.mcpAdd,
90
- annotations: CONFIG_WRITE,
91
- execute: (args) => mcpAdd(args.id, args, args.scope ?? "user")
92
- },
93
- {
94
- name: "harnesses_mcp_sync",
95
- title: "Harnesses MCP Sync",
96
- description: "Reset every harness's user-scope MCP config to exactly the master list from ~/.config/agntn/mcp.jsonc: servers are added, replaced, and extras removed. Excluded harnesses keep their own servers, but master-listed names are withdrawn from them.",
97
- inputSchema: schemas.mcpSync,
98
- annotations: CONFIG_WRITE,
99
- execute: (args) => mcpSync(args.id)
100
- },
101
- {
102
- name: "harnesses_agents_sync",
103
- title: "Harnesses Agents Sync",
104
- description: "Link every harness's global instructions file and declared companions to the master bundle, so edits made through any harness land in one place. Diverged copies are backed up and relinked. Pass check to only report.",
105
- inputSchema: schemas.agentsSync,
106
- annotations: CONFIG_WRITE,
107
- execute: (args) => agentsSync(args.id, args.check === true)
108
- },
109
- {
110
- name: "harnesses_mcp_remove",
111
- title: "Harnesses MCP Remove",
112
- description: "Remove one MCP server from a harness config. JSON configs are rewritten; TOML configs get a surgical, comment-preserving edit.",
113
- inputSchema: schemas.mcpRemove,
114
- annotations: CONFIG_WRITE,
115
- execute: (args) => mcpRemove(args.id, args.name, args.scope ?? "user")
1
+ import { defineCommand } from "citty";
2
+ import { LogLevels, consola } from "consola";
3
+ var mcp_default = defineCommand({
4
+ meta: {
5
+ name: "mcp",
6
+ description: "Run the harnesses MCP server over stdio"
7
+ },
8
+ async run() {
9
+ consola.level = LogLevels.warn;
10
+ const [{ createMcpServer }, { StdioServerTransport }] = await Promise.all([import("../mcp.mjs"), import("@modelcontextprotocol/sdk/server/stdio.js")]);
11
+ await createMcpServer().connect(new StdioServerTransport());
116
12
  }
117
- ];
118
- function validationError(schema, value) {
119
- const first = Errors(schema, value)[0];
120
- if (!first) return "Invalid arguments";
121
- return `Invalid arguments at ${first.instancePath || "/"}: ${first.message}`;
122
- }
123
- function errorResult(text) {
124
- return {
125
- content: [{
126
- type: "text",
127
- text: text.replaceAll(/\p{Cc}/gu, " ")
128
- }],
129
- isError: true
130
- };
131
- }
132
- function toCallToolResult(result) {
133
- return {
134
- content: result.content,
135
- ...result.isError === void 0 ? {} : { isError: result.isError }
136
- };
137
- }
138
- function createMcpServer() {
139
- const toolsByName = new Map(tools.map((tool) => [tool.name, tool]));
140
- const server = new Server({
141
- name: "harnesses",
142
- version
143
- }, { capabilities: { tools: {} } });
144
- server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: tools.map((tool) => ({
145
- name: tool.name,
146
- title: tool.title,
147
- description: tool.description,
148
- inputSchema: tool.inputSchema,
149
- annotations: tool.annotations
150
- })) }));
151
- server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
152
- const tool = toolsByName.get(request.params.name);
153
- if (!tool) return errorResult(`Unknown harnesses tool: ${JSON.stringify(request.params.name)}`);
154
- const args = request.params.arguments ?? {};
155
- if (!Check(tool.inputSchema, args)) return errorResult(validationError(tool.inputSchema, args));
156
- try {
157
- return toCallToolResult(await tool.execute(args, extra.signal));
158
- } catch (error) {
159
- return errorResult(`${tool.name} failed: ${error instanceof Error ? error.message : String(error)}`);
160
- }
161
- });
162
- return server;
163
- }
164
- export { createMcpServer };
13
+ });
14
+ export { mcp_default as default };
165
15
 
166
16
  //# sourceMappingURL=mcp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.mjs","names":["Type","Value.Errors","Value.Check"],"sources":["../../src/mcp.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type CallToolResult,\n type Tool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Type, type TSchema } from \"typebox\";\nimport { Value } from \"typebox/value\";\nimport {\n detectHarnesses,\n harnessInfo,\n listHarnessModels,\n mcpAdd,\n mcpList,\n mcpRemove,\n mcpSync,\n agentsSync,\n runHarness,\n type McpServerParams,\n type ToolResult,\n} from \"./tool-operations.ts\";\nimport { harnessToolSchemas } from \"./tool-schemas.ts\";\nimport { version } from \"./types.ts\";\n\ninterface ToolDefinition {\n name: string;\n title: string;\n description: string;\n inputSchema: TSchema;\n annotations: Tool[\"annotations\"];\n execute(\n args: Readonly<Record<string, unknown>>,\n signal: AbortSignal,\n ): ToolResult<unknown> | Promise<ToolResult<unknown>>;\n}\n\nconst READ_ONLY: Tool[\"annotations\"] = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n};\n\nconst EXEC_READ: Tool[\"annotations\"] = {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n};\n\nconst CONFIG_WRITE: Tool[\"annotations\"] = {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: false,\n};\n\nconst schemas = harnessToolSchemas<TSchema, TSchema>(Type);\n\nconst tools: ToolDefinition[] = [\n {\n name: \"harnesses_detect\",\n title: \"Harnesses Detect\",\n description: \"List every known AI coding harness with its install state and version.\",\n inputSchema: schemas.detect,\n annotations: READ_ONLY,\n execute: () => detectHarnesses(),\n },\n {\n name: \"harnesses_info\",\n title: \"Harnesses Info\",\n description:\n \"Full metadata for one or more AI coding harnesses, including supported invocation and model operations, configuration, sessions, instructions, skills, commands, hooks, and resolved paths.\",\n inputSchema: schemas.info,\n annotations: READ_ONLY,\n execute: (args) => harnessInfo(args.id as string | string[]),\n },\n {\n name: \"harnesses_models\",\n title: \"Harnesses Models\",\n description:\n \"List the models currently available to one AI coding harness through its native CLI, normalized across providers. An optional search filter is passed to harnesses that support it. The command may load project configuration and extensions.\",\n inputSchema: schemas.models,\n annotations: EXEC_READ,\n execute: (args, signal) =>\n listHarnessModels(args.id as string, {\n search: args.search as string | undefined,\n cwd: args.cwd as string | undefined,\n timeoutSeconds: args.timeoutSeconds as number | undefined,\n signal,\n }),\n },\n {\n name: \"harnesses_run\",\n title: \"Harnesses Run\",\n description:\n \"Run one prompt through an AI coding harness's normalized non-interactive invocation and return its output. Pass tools=true whenever the task needs harness tools, including Grok native X search. Add readOnly=true to require native read-only tool enforcement. Pass tools=false only for an advisor without tools. Unsupported modes never widen access.\",\n inputSchema: schemas.run,\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: false,\n openWorldHint: true,\n },\n execute: (args, signal) =>\n runHarness(args.id as string, args.prompt as string, {\n cwd: args.cwd as string | undefined,\n model: args.model as string | undefined,\n timeoutSeconds: args.timeoutSeconds as number | undefined,\n signal,\n structured: args.structured as boolean | undefined,\n tools: args.tools as boolean | undefined,\n readOnly: args.readOnly as boolean | undefined,\n }),\n },\n {\n name: \"harnesses_mcp_list\",\n title: \"Harnesses MCP List\",\n description:\n \"List the MCP servers configured in each harness's config files, normalized across dialects. Omit id to scan every harness.\",\n inputSchema: schemas.mcpList,\n annotations: READ_ONLY,\n execute: (args) => mcpList(args.id as string | undefined),\n },\n {\n name: \"harnesses_mcp_add\",\n title: \"Harnesses MCP Add\",\n description:\n \"Add or replace one MCP server in a harness config. JSON configs are rewritten; TOML configs get a surgical, comment-preserving edit.\",\n inputSchema: schemas.mcpAdd,\n annotations: CONFIG_WRITE,\n execute: (args) =>\n mcpAdd(\n args.id as string,\n args as unknown as McpServerParams,\n (args.scope as \"user\" | \"project\") ?? \"user\",\n ),\n },\n {\n name: \"harnesses_mcp_sync\",\n title: \"Harnesses MCP Sync\",\n description:\n \"Reset every harness's user-scope MCP config to exactly the master list from ~/.config/agntn/mcp.jsonc: servers are added, replaced, and extras removed. Excluded harnesses keep their own servers, but master-listed names are withdrawn from them.\",\n inputSchema: schemas.mcpSync,\n annotations: CONFIG_WRITE,\n execute: (args) => mcpSync(args.id as string | undefined),\n },\n {\n name: \"harnesses_agents_sync\",\n title: \"Harnesses Agents Sync\",\n description:\n \"Link every harness's global instructions file and declared companions to the master bundle, so edits made through any harness land in one place. Diverged copies are backed up and relinked. Pass check to only report.\",\n inputSchema: schemas.agentsSync,\n annotations: CONFIG_WRITE,\n execute: (args) => agentsSync(args.id as string | undefined, args.check === true),\n },\n {\n name: \"harnesses_mcp_remove\",\n title: \"Harnesses MCP Remove\",\n description:\n \"Remove one MCP server from a harness config. JSON configs are rewritten; TOML configs get a surgical, comment-preserving edit.\",\n inputSchema: schemas.mcpRemove,\n annotations: CONFIG_WRITE,\n execute: (args) =>\n mcpRemove(\n args.id as string,\n args.name as string,\n (args.scope as \"user\" | \"project\") ?? \"user\",\n ),\n },\n];\n\n/**\n * Formats the first TypeBox validation failure for an MCP client.\n *\n * @param schema - Schema that rejected the value.\n * @param value - Rejected input value.\n * @returns {string} A client-facing validation message.\n */\nfunction validationError(schema: TSchema, value: unknown): string {\n const first = Value.Errors(schema, value)[0];\n if (!first) return \"Invalid arguments\";\n return `Invalid arguments at ${first.instancePath || \"/\"}: ${first.message}`;\n}\n\n/**\n * Wraps error text for the MCP client, replacing control bytes with spaces.\n *\n * Every error branch goes through here because parts of these messages echo\n * client-controlled values (a tool name, an argument) or downstream error\n * messages: one raw newline or escape byte would forge extra lines that read\n * as the server's own answer.\n *\n * @param text - Untrusted error text.\n * @returns {CallToolResult} A sanitized MCP error result.\n */\nfunction errorResult(text: string): CallToolResult {\n return {\n content: [{ type: \"text\", text: text.replaceAll(/\\p{Cc}/gu, \" \") }],\n isError: true,\n };\n}\n\n/**\n * Converts a shared tool result to the MCP text-result contract.\n *\n * `details` is dropped and `structuredContent` is never set: clients that see\n * structured output prefer it over `content` and would hide the readable answer.\n *\n * @param result - Shared tool result to adapt.\n * @returns {CallToolResult} The MCP text-result envelope.\n */\nfunction toCallToolResult(result: ToolResult<unknown>): CallToolResult {\n return {\n content: result.content,\n ...(result.isError === undefined ? {} : { isError: result.isError }),\n };\n}\n\n/**\n * Creates an unconnected MCP server exposing the harness registry tools.\n *\n * Built on the low-level `Server` even though the SDK marks it `@deprecated`,\n * because `McpServer.registerTool` accepts Standard Schema (Zod) only. TypeBox 1.x\n * does not implement Standard Schema, and this package's tool schemas are TypeBox,\n * shared with the Pi and OMP extensions. The high-level API would force a second\n * definition of every parameter.\n *\n * @returns {Server} An unconnected MCP server.\n */\nexport function createMcpServer(): Server {\n const toolsByName = new Map(tools.map((tool) => [tool.name, tool]));\n const server = new Server({ name: \"harnesses\", version }, { capabilities: { tools: {} } });\n\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: tools.map((tool): Tool => ({\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema as Tool[\"inputSchema\"],\n annotations: tool.annotations,\n })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {\n const tool = toolsByName.get(request.params.name);\n if (!tool) {\n return errorResult(`Unknown harnesses tool: ${JSON.stringify(request.params.name)}`);\n }\n\n const args = request.params.arguments ?? {};\n if (!Value.Check(tool.inputSchema, args)) {\n return errorResult(validationError(tool.inputSchema, args));\n }\n\n try {\n return toCallToolResult(await tool.execute(args, extra.signal));\n } catch (error) {\n return errorResult(\n `${tool.name} failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n });\n\n return server;\n}\n"],"mappings":"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"}
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"}
@@ -0,0 +1,28 @@
1
+ import { stripVTControlCharacters } from "node:util";
2
+ import { consola } from "consola";
3
+ import { encode } from "@toon-format/toon";
4
+ const formatArgs = {
5
+ json: {
6
+ type: "boolean",
7
+ description: "Output as JSON"
8
+ },
9
+ toon: {
10
+ type: "boolean",
11
+ description: "Output as TOON"
12
+ }
13
+ };
14
+ const UNSAFE_TERMINAL_CONTROLS = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
15
+ const UNSAFE_UNICODE_FORMATTING = /[\p{Cf}\p{Zl}\p{Zp}]/gu;
16
+ function sanitize(text) {
17
+ return stripVTControlCharacters(text ?? "").replace(UNSAFE_TERMINAL_CONTROLS, " ").replace(UNSAFE_UNICODE_FORMATTING, " ");
18
+ }
19
+ function report(result, args) {
20
+ if (args.json) console.log(JSON.stringify(result.details, null, 2));
21
+ else if (args.toon) console.log(encode(result.details));
22
+ else if (result.isError) consola.error(sanitize(result.content[0]?.text));
23
+ else console.log(sanitize(result.content[0]?.text));
24
+ if (result.isError) process.exitCode = 1;
25
+ }
26
+ export { formatArgs, report };
27
+
28
+ //# sourceMappingURL=output.mjs.map
@@ -0,0 +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"}
@@ -9,6 +9,22 @@ export interface PathCandidate {
9
9
  platforms?: Platform[];
10
10
  note?: string;
11
11
  }
12
+ /** The path lists of a harness descriptor, as named in {@link ResolvedPaths}. */
13
+ export type PathCategory = "config" | "sessions" | "instructions" | "skills" | "commands" | "promptTemplates" | "hooks" | "temp";
14
+ /**
15
+ * An environment variable the harness reads to move one of its roots. `path`
16
+ * is the default root, used while the variable is unset or empty.
17
+ */
18
+ export interface EnvOverride extends PathCandidate {
19
+ variable: string;
20
+ /** Categories with entries under this root, which move with it. */
21
+ relocates: PathCategory[];
22
+ }
23
+ /** One stable user-scope destination used by prompt template synchronization. */
24
+ export interface PromptTemplateSyncTarget extends PathCandidate {
25
+ scope: "user";
26
+ format: "markdown" | "gemini-toml";
27
+ }
12
28
  export interface StorageDescriptor {
13
29
  format: string;
14
30
  level: EvidenceLevel;
@@ -44,6 +60,12 @@ export interface HarnessInvocation {
44
60
  readOnlyMinVersion?: string;
45
61
  /** Arguments appended when a model is selected; every "{model}" is replaced. */
46
62
  modelArgs?: string[];
63
+ /**
64
+ * Arguments appended to a plain text run so a CLI that holds its answer until
65
+ * the end reports it as events instead; `invoke` folds them back into that text,
66
+ * so a stopped run keeps what the model had already written.
67
+ */
68
+ streamArgs?: string[];
47
69
  level: EvidenceLevel;
48
70
  note?: string;
49
71
  }
@@ -60,7 +82,10 @@ export interface HarnessInvocationModes {
60
82
  export interface HarnessModelListing {
61
83
  /** Arguments used when no search filter is supplied. */
62
84
  args: string[];
63
- /** Optional argument template for a search filter; every "{search}" is replaced. */
85
+ /**
86
+ * Optional argument template for a native search filter; every "{search}" is replaced.
87
+ * Without it, a search runs `args` and keeps the ids containing the filter, ignoring case.
88
+ */
64
89
  searchArgs?: string[];
65
90
  level: EvidenceLevel;
66
91
  note?: string;
@@ -69,10 +94,13 @@ export interface HarnessModelListing {
69
94
  export interface AvailableModel {
70
95
  provider: string;
71
96
  id: string;
72
- contextWindow: number;
73
- maxOutputTokens: number;
74
- thinking: boolean;
75
- images: boolean;
97
+ /** Absent when the native listing prints only ids, like Grok's. */
98
+ contextWindow?: number;
99
+ maxOutputTokens?: number;
100
+ thinking?: boolean;
101
+ images?: boolean;
102
+ /** Set on the model the harness picks when no model is given, when the listing marks one. */
103
+ default?: boolean;
76
104
  }
77
105
  /** Options for querying the models available to one harness. */
78
106
  export interface ListModelsOptions {
@@ -91,7 +119,7 @@ export interface InvokeOptions {
91
119
  model?: string;
92
120
  /** Enable the spawned harness's tools; defaults to advisor without tools mode. */
93
121
  tools?: boolean;
94
- /** Require native enforcement of read-only tool access. Implies `tools: true`. */
122
+ /** Require native enforcement of read-only tool access. Implies `tools: true` when `tools` is omitted; `tools: false` rejects the run. */
95
123
  readOnly?: boolean;
96
124
  /** Milliseconds before cleanup starts. Unset or 0 disables the deadline. */
97
125
  timeoutMs?: number;
@@ -110,6 +138,8 @@ export interface InvokeResult {
110
138
  timedOut: boolean;
111
139
  /** True when caller cancellation wins over the deadline or completion. */
112
140
  aborted: boolean;
141
+ /** Milliseconds without output on either stream before a deadline or cancellation stopped the process. */
142
+ idleMs?: number;
113
143
  }
114
144
  /** Result of one native model-listing command. */
115
145
  export interface ListModelsResult extends InvokeResult {
@@ -140,9 +170,10 @@ export interface McpConfigFile extends PathCandidate {
140
170
  key: string[];
141
171
  /**
142
172
  * Shape of individual entries; "standard" is the {command, args, env, url}
143
- * family and "prime" is that family with env values as {"env": "NAME"} references.
173
+ * family, "prime" is that family with env values as {"env": "NAME"} references
174
+ * and "mastracode" is that family read by its keys alone, type ignored.
144
175
  */
145
- dialect: "standard" | "antigravity" | "opencode" | "prime" | "vscode";
176
+ dialect: "standard" | "antigravity" | "mastracode" | "opencode" | "prime" | "vscode";
146
177
  }
147
178
  export interface HarnessDetection {
148
179
  /** Environment variables that indicate running inside this agent. */
@@ -154,6 +185,8 @@ export interface ResolveOptions {
154
185
  homeDir?: string;
155
186
  projectRoot?: string;
156
187
  platform?: Platform;
188
+ /** Replaces `os.tmpdir()` as the value of `${TMPDIR}`. */
189
+ tempDir?: string;
157
190
  }
158
191
  export interface ResolvedPaths {
159
192
  config: PathCandidate[];
@@ -161,7 +194,11 @@ export interface ResolvedPaths {
161
194
  instructions: PathCandidate[];
162
195
  skills: PathCandidate[];
163
196
  commands: PathCandidate[];
197
+ promptTemplates: PathCandidate[];
198
+ promptTemplateSyncTarget: PromptTemplateSyncTarget | null;
164
199
  hooks: PathCandidate[];
200
+ /** Temp roots, moved by a set `temp` override from {@link EnvOverride}. */
201
+ temp: PathCandidate[];
165
202
  }
166
203
  type InvocationOptions = Readonly<{
167
204
  model?: string;
@@ -179,7 +216,15 @@ export declare abstract class Harness {
179
216
  abstract readonly instructions: PathCandidate[];
180
217
  abstract readonly skills: PathCandidate[];
181
218
  abstract readonly commands: PathCandidate[];
219
+ /** Reusable prompt files; empty when no CLI template location is verified. */
220
+ readonly promptTemplates: PathCandidate[];
221
+ /** Stable user-scope destination used by prompt template synchronization. */
222
+ readonly promptTemplateSyncTarget: PromptTemplateSyncTarget | null;
182
223
  abstract readonly hooks: PathCandidate[];
224
+ /** Where the harness keeps its temp files; empty when not verified. */
225
+ readonly temp: PathCandidate[];
226
+ /** Environment variables that move a root of this harness; empty when none is verified. */
227
+ readonly envOverrides: EnvOverride[];
183
228
  abstract readonly capabilities: HarnessCapabilities;
184
229
  abstract readonly detection: HarnessDetection;
185
230
  /** Non-interactive invocation recipe; null when the harness has no headless mode. */
@@ -234,6 +279,15 @@ export declare abstract class Harness {
234
279
  * @returns {Promise<InvokeResult>} The completed process result.
235
280
  */
236
281
  invoke(prompt: string, options?: InvokeOptions): Promise<InvokeResult>;
282
+ /**
283
+ * Turns the events a `streamArgs` run printed back into the text the plain
284
+ * run would have printed.
285
+ *
286
+ * @param _stdout - Event output as captured, possibly cut short.
287
+ * @param _complete - False when a deadline or cancellation stopped the run.
288
+ * @returns {string} The answer text, or what the model had written so far.
289
+ */
290
+ protected foldStreamOutput(_stdout: string, _complete: boolean): string;
237
291
  /**
238
292
  * Explains why the installed CLI cannot run a read-only recipe that carries a
239
293
  * version floor, or returns null when no floor applies or the installed
@@ -246,7 +300,8 @@ export declare abstract class Harness {
246
300
  /**
247
301
  * Expands the native model-listing recipe without spawning anything.
248
302
  *
249
- * @param search - Optional native model search filter.
303
+ * @param search - Optional model search filter; without native `searchArgs` it is applied
304
+ * to the parsed output instead, so the command is the unfiltered one.
250
305
  * @returns {{ command: string, args: string[] } | null} The command, or null when unsupported.
251
306
  */
252
307
  buildModelListInvocation(search?: string): {
@@ -261,6 +316,14 @@ export declare abstract class Harness {
261
316
  * @returns {Promise<ListModelsResult>} The normalized command and model result.
262
317
  */
263
318
  listModels(options?: ListModelsOptions): Promise<ListModelsResult>;
319
+ /**
320
+ * The value `model` takes for one listed model: `provider/id` unless the
321
+ * harness overrides it.
322
+ *
323
+ * @param model - A model returned by {@link listModels}.
324
+ * @returns {string} The selector this harness's `modelArgs` accept.
325
+ */
326
+ modelSelector(model: Readonly<AvailableModel>): string;
264
327
  /**
265
328
  * Converts a successful native model-listing response to the shared shape.
266
329
  *
@@ -278,6 +341,14 @@ export declare abstract class Harness {
278
341
  */
279
342
  resolveCandidates<T extends PathCandidate>(entries: readonly T[], options?: ResolveOptions): T[];
280
343
  resolve(options?: ResolveOptions): ResolvedPaths;
344
+ /**
345
+ * Temp roots for the platform. A `temp` override set in the environment
346
+ * replaces its default root, the same move the harness makes itself.
347
+ *
348
+ * @param options - Platform and path-resolution overrides.
349
+ * @returns {PathCandidate[]} Resolved temp roots.
350
+ */
351
+ private resolveTemp;
281
352
  }
282
353
  export type HarnessConstructor = new () => Harness;
283
354
  /** One resolved config file together with the servers it declares. */
@@ -444,4 +515,45 @@ export declare function readAgentsConfig(options?: ResolveOptions): AgentsConfig
444
515
  * @returns {AgentsSyncReport} Per-harness synchronization outcomes.
445
516
  */
446
517
  export declare function syncAgentsFiles(harnesses: readonly Harness[], check?: boolean, options?: ResolveOptions): AgentsSyncReport;
447
- //# sourceMappingURL=agents-sync.d.mts.map
518
+ export type PromptSyncAction = "linked" | "generated" | "relinked" | "replaced" | "adopted" | "removed" | "unchanged" | "skipped";
519
+ /** One prompt template's outcome at a harness destination. */
520
+ export interface PromptTemplateTargetResult {
521
+ readonly name: string;
522
+ readonly source?: string;
523
+ readonly path: string;
524
+ readonly action: PromptSyncAction;
525
+ /** Backup path, skip reason, or a check-mode note. */
526
+ readonly detail?: string;
527
+ }
528
+ /** One harness's outcome of a prompt template sync run. */
529
+ export interface PromptSyncTargetResult {
530
+ readonly id: string;
531
+ readonly path?: string;
532
+ readonly format?: PromptTemplateSyncTarget["format"];
533
+ /** Outcome for a Markdown destination, which is one link to the whole source directory. */
534
+ readonly action?: PromptSyncAction;
535
+ /** Backup path, skip reason, or a check-mode note. */
536
+ readonly detail?: string;
537
+ readonly templates: readonly PromptTemplateTargetResult[];
538
+ }
539
+ /** Outcome of one prompt template sync run. */
540
+ export interface PromptSyncReport {
541
+ readonly source: string;
542
+ readonly check: boolean;
543
+ readonly templates: readonly string[];
544
+ readonly targets: readonly PromptSyncTargetResult[];
545
+ }
546
+ /**
547
+ * Synchronizes canonical Markdown prompt templates into each harness's stable
548
+ * user-scope destination. A Markdown harness's destination becomes one symlink
549
+ * to the source directory; Gemini's directory holds only TOML commands
550
+ * generated from it. Anything else found there is backed up first. In check
551
+ * mode nothing is written.
552
+ *
553
+ * @param harnesses - Harnesses to inspect or update.
554
+ * @param check - Report intended changes without writing them.
555
+ * @param options - Path-resolution overrides.
556
+ * @returns {PromptSyncReport} Per-harness synchronization outcomes.
557
+ */
558
+ export declare function syncPromptTemplates(harnesses: readonly Harness[], check?: boolean, options?: ResolveOptions): PromptSyncReport;
559
+ //# sourceMappingURL=prompt-sync.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-sync.d.mts","names":[],"sources":["../../src/types.ts","../../src/harness.ts","../../src/mcp-servers.ts","../../src/agents-sync.ts","../../src/prompt-sync.ts"],"mappings":"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"}