@oh-my-pi/pi-coding-agent 17.0.0 → 17.0.2

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 (202) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/dist/cli.js +4006 -3966
  3. package/dist/types/advisor/advise-tool.d.ts +5 -2
  4. package/dist/types/advisor/config.d.ts +14 -6
  5. package/dist/types/advisor/runtime.d.ts +20 -11
  6. package/dist/types/autolearn/controller.d.ts +1 -0
  7. package/dist/types/cli-commands.d.ts +14 -1
  8. package/dist/types/config/model-discovery.d.ts +17 -0
  9. package/dist/types/config/model-registry.d.ts +4 -0
  10. package/dist/types/config/model-resolver.d.ts +6 -2
  11. package/dist/types/config/models-config-schema.d.ts +10 -0
  12. package/dist/types/config/models-config.d.ts +6 -0
  13. package/dist/types/config/settings-schema.d.ts +32 -7
  14. package/dist/types/config/settings.d.ts +50 -0
  15. package/dist/types/cursor.d.ts +11 -0
  16. package/dist/types/discovery/helpers.d.ts +5 -2
  17. package/dist/types/discovery/substitute-plugin-root.d.ts +20 -7
  18. package/dist/types/exec/bash-executor.d.ts +2 -0
  19. package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
  20. package/dist/types/extensibility/extensions/runner.d.ts +7 -0
  21. package/dist/types/extensibility/extensions/types.d.ts +18 -0
  22. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +40 -0
  23. package/dist/types/extensibility/shared-events.d.ts +6 -0
  24. package/dist/types/extensibility/utils.d.ts +2 -2
  25. package/dist/types/internal-urls/agent-protocol.d.ts +1 -0
  26. package/dist/types/lib/xai-http.d.ts +11 -0
  27. package/dist/types/mcp/transports/stdio.d.ts +13 -1
  28. package/dist/types/modes/acp/acp-agent.d.ts +3 -0
  29. package/dist/types/modes/acp/acp-mode.d.ts +6 -2
  30. package/dist/types/modes/components/advisor-config.d.ts +8 -1
  31. package/dist/types/modes/components/assistant-message.d.ts +2 -0
  32. package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +1 -0
  33. package/dist/types/modes/components/custom-editor.d.ts +23 -1
  34. package/dist/types/modes/components/hook-editor.d.ts +16 -2
  35. package/dist/types/modes/components/login-dialog.test.d.ts +1 -0
  36. package/dist/types/modes/components/model-hub.d.ts +5 -2
  37. package/dist/types/modes/components/session-selector.d.ts +4 -0
  38. package/dist/types/modes/components/tool-execution.d.ts +8 -1
  39. package/dist/types/modes/components/tool-execution.test.d.ts +1 -0
  40. package/dist/types/modes/controllers/command-controller.d.ts +8 -0
  41. package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
  42. package/dist/types/modes/interactive-mode.d.ts +6 -0
  43. package/dist/types/modes/noninteractive-dispose.test.d.ts +1 -0
  44. package/dist/types/modes/print-mode.d.ts +1 -1
  45. package/dist/types/modes/types.d.ts +10 -0
  46. package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -2
  47. package/dist/types/modes/warp-events.d.ts +24 -0
  48. package/dist/types/modes/warp-events.test.d.ts +1 -0
  49. package/dist/types/plan-mode/model-transition.d.ts +47 -0
  50. package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
  51. package/dist/types/registry/agent-lifecycle.d.ts +26 -1
  52. package/dist/types/sdk.d.ts +13 -2
  53. package/dist/types/session/agent-session.d.ts +43 -11
  54. package/dist/types/session/session-history-format.d.ts +10 -0
  55. package/dist/types/session/session-manager.d.ts +14 -0
  56. package/dist/types/session/streaming-output.d.ts +2 -0
  57. package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
  58. package/dist/types/slash-commands/types.d.ts +4 -4
  59. package/dist/types/system-prompt.d.ts +1 -1
  60. package/dist/types/task/label.d.ts +1 -1
  61. package/dist/types/telemetry-export.d.ts +34 -9
  62. package/dist/types/tools/approval.d.ts +8 -0
  63. package/dist/types/tools/bash.d.ts +2 -0
  64. package/dist/types/tools/browser/registry.d.ts +7 -3
  65. package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
  66. package/dist/types/tools/essential-tools.d.ts +29 -0
  67. package/dist/types/tools/image-gen.d.ts +2 -1
  68. package/dist/types/tools/index.d.ts +4 -1
  69. package/dist/types/tools/manage-skill.d.ts +2 -0
  70. package/dist/types/tools/xdev.d.ts +11 -2
  71. package/dist/types/utils/title-generator.d.ts +2 -1
  72. package/dist/types/web/search/index.d.ts +2 -0
  73. package/dist/types/web/search/providers/base.d.ts +3 -0
  74. package/dist/types/web/search/providers/kimi.d.ts +4 -1
  75. package/dist/types/web/search/types.d.ts +1 -1
  76. package/package.json +21 -16
  77. package/src/advisor/__tests__/advisor.test.ts +1358 -86
  78. package/src/advisor/__tests__/config.test.ts +58 -2
  79. package/src/advisor/advise-tool.ts +7 -3
  80. package/src/advisor/config.ts +76 -24
  81. package/src/advisor/runtime.ts +445 -92
  82. package/src/autolearn/controller.ts +23 -28
  83. package/src/cli/bench-cli.ts +4 -1
  84. package/src/cli/grep-cli.ts +2 -1
  85. package/src/cli-commands.ts +57 -23
  86. package/src/cli.ts +5 -1
  87. package/src/config/model-discovery.ts +81 -21
  88. package/src/config/model-registry.ts +34 -6
  89. package/src/config/model-resolver.ts +57 -12
  90. package/src/config/models-config-schema.ts +1 -0
  91. package/src/config/settings-schema.ts +43 -6
  92. package/src/config/settings.ts +405 -25
  93. package/src/cursor.ts +20 -3
  94. package/src/debug/report-bundle.ts +40 -4
  95. package/src/discovery/codex.ts +14 -4
  96. package/src/discovery/helpers.ts +28 -5
  97. package/src/discovery/substitute-plugin-root.ts +23 -7
  98. package/src/exec/bash-executor.ts +14 -5
  99. package/src/export/html/template.js +2 -0
  100. package/src/extensibility/custom-tools/loader.ts +3 -3
  101. package/src/extensibility/custom-tools/wrapper.ts +2 -1
  102. package/src/extensibility/extensions/loader.ts +3 -3
  103. package/src/extensibility/extensions/managed-timers.ts +83 -0
  104. package/src/extensibility/extensions/runner.ts +26 -0
  105. package/src/extensibility/extensions/types.ts +18 -0
  106. package/src/extensibility/extensions/wrapper.ts +2 -1
  107. package/src/extensibility/hooks/loader.ts +3 -3
  108. package/src/extensibility/legacy-pi-coding-agent-shim.ts +96 -1
  109. package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
  110. package/src/extensibility/plugins/manager.ts +2 -2
  111. package/src/extensibility/shared-events.ts +6 -0
  112. package/src/extensibility/utils.ts +91 -25
  113. package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +73 -0
  114. package/src/internal-urls/agent-protocol.ts +73 -39
  115. package/src/irc/bus.ts +22 -3
  116. package/src/launch/broker.ts +3 -2
  117. package/src/launch/client.ts +2 -2
  118. package/src/launch/presence.ts +2 -2
  119. package/src/lib/xai-http.ts +28 -2
  120. package/src/lsp/client.ts +1 -1
  121. package/src/main.ts +11 -8
  122. package/src/mcp/manager.ts +9 -3
  123. package/src/mcp/transports/stdio.ts +103 -23
  124. package/src/modes/acp/acp-agent.ts +28 -9
  125. package/src/modes/acp/acp-event-mapper.ts +8 -0
  126. package/src/modes/acp/acp-mode.ts +18 -4
  127. package/src/modes/components/advisor-config.ts +65 -3
  128. package/src/modes/components/ask-dialog.ts +1 -1
  129. package/src/modes/components/assistant-message.ts +35 -14
  130. package/src/modes/components/chat-transcript-builder.ts +2 -0
  131. package/src/modes/components/custom-editor-plugin-ctor.test.ts +36 -0
  132. package/src/modes/components/custom-editor.ts +62 -1
  133. package/src/modes/components/hook-editor.ts +35 -5
  134. package/src/modes/components/login-dialog.test.ts +56 -0
  135. package/src/modes/components/login-dialog.ts +7 -3
  136. package/src/modes/components/model-hub.ts +138 -42
  137. package/src/modes/components/plan-review-overlay.ts +51 -10
  138. package/src/modes/components/session-selector.ts +10 -0
  139. package/src/modes/components/status-line/component.test.ts +1 -0
  140. package/src/modes/components/status-line/component.ts +2 -4
  141. package/src/modes/components/status-line/segments.ts +21 -6
  142. package/src/modes/components/tool-execution.test.ts +101 -0
  143. package/src/modes/components/tool-execution.ts +117 -9
  144. package/src/modes/components/transcript-container.ts +30 -2
  145. package/src/modes/controllers/command-controller-shared.ts +1 -1
  146. package/src/modes/controllers/command-controller.ts +168 -48
  147. package/src/modes/controllers/event-controller.ts +6 -0
  148. package/src/modes/controllers/extension-ui-controller.ts +4 -22
  149. package/src/modes/controllers/input-controller.ts +12 -12
  150. package/src/modes/controllers/selector-controller.ts +208 -46
  151. package/src/modes/interactive-mode.ts +205 -65
  152. package/src/modes/noninteractive-dispose.test.ts +60 -0
  153. package/src/modes/print-mode.ts +10 -5
  154. package/src/modes/rpc/host-tools.ts +2 -1
  155. package/src/modes/rpc/rpc-mode.ts +31 -7
  156. package/src/modes/theme/theme.ts +2 -2
  157. package/src/modes/types.ts +10 -0
  158. package/src/modes/utils/interactive-context-helpers.ts +3 -1
  159. package/src/modes/utils/transcript-render-helpers.ts +3 -2
  160. package/src/modes/warp-events.test.ts +794 -0
  161. package/src/modes/warp-events.ts +232 -0
  162. package/src/plan-mode/model-transition.test.ts +60 -0
  163. package/src/plan-mode/model-transition.ts +51 -0
  164. package/src/prompts/system/system-prompt.md +1 -1
  165. package/src/registry/agent-lifecycle.ts +133 -18
  166. package/src/sdk.ts +227 -44
  167. package/src/session/agent-session.ts +1364 -382
  168. package/src/session/session-history-format.ts +20 -5
  169. package/src/session/session-listing.ts +6 -5
  170. package/src/session/session-loader.ts +9 -0
  171. package/src/session/session-manager.ts +48 -0
  172. package/src/session/session-persistence.ts +11 -0
  173. package/src/session/streaming-output.ts +3 -1
  174. package/src/slash-commands/builtin-registry.ts +9 -0
  175. package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
  176. package/src/slash-commands/types.ts +4 -4
  177. package/src/system-prompt.ts +2 -2
  178. package/src/task/executor.ts +1 -1
  179. package/src/task/label.ts +2 -0
  180. package/src/telemetry-export.ts +453 -97
  181. package/src/tools/approval.ts +11 -0
  182. package/src/tools/bash.ts +71 -38
  183. package/src/tools/browser/registry.ts +9 -2
  184. package/src/tools/browser/tab-supervisor.ts +65 -10
  185. package/src/tools/browser.ts +4 -3
  186. package/src/tools/essential-tools.ts +45 -0
  187. package/src/tools/eval.ts +3 -0
  188. package/src/tools/gh.ts +169 -2
  189. package/src/tools/image-gen.ts +624 -513
  190. package/src/tools/index.ts +10 -6
  191. package/src/tools/manage-skill.ts +5 -3
  192. package/src/tools/output-meta.ts +7 -0
  193. package/src/tools/path-utils.ts +8 -5
  194. package/src/tools/read.ts +48 -3
  195. package/src/tools/write.ts +22 -4
  196. package/src/tools/xdev.ts +14 -3
  197. package/src/utils/title-generator.ts +15 -4
  198. package/src/web/search/index.ts +21 -6
  199. package/src/web/search/providers/base.ts +3 -0
  200. package/src/web/search/providers/kimi.ts +18 -12
  201. package/src/web/search/providers/xai.ts +40 -9
  202. package/src/web/search/types.ts +6 -1
@@ -201,9 +201,13 @@ describe("WATCHDOG.yml file round-trip", () => {
201
201
  });
202
202
 
203
203
  const doc: WatchdogConfigDoc = {
204
- instructions: 'Shared baseline.\nSecond line with: a colon and "quotes".',
204
+ instructions: 'Shared baseline.\n\nSecond line with: a colon and "quotes".',
205
205
  advisors: [
206
- { name: "Architecture", model: "x-ai/grok-code-fast:high", instructions: "Watch module boundaries." },
206
+ {
207
+ name: "Architecture",
208
+ model: "x-ai/grok-code-fast:high",
209
+ instructions: "Watch module boundaries.\nReport coupling.",
210
+ },
207
211
  { name: "Security", tools: ["read", "grep"] },
208
212
  ],
209
213
  };
@@ -222,11 +226,25 @@ describe("WATCHDOG.yml file round-trip", () => {
222
226
  // Block style (not the flow `{...}` form), so it stays hand-editable.
223
227
  expect(text).toContain("advisors:");
224
228
  expect(text).not.toMatch(/^\{/);
229
+ expect(text).toContain('instructions: |2-\n Shared baseline.\n \n Second line with: a colon and "quotes".');
230
+ expect(text).toContain(" instructions: |2-\n Watch module boundaries.\n Report coupling.");
231
+ expect(text).not.toContain("\\n");
225
232
  const { advisors, sharedInstructions } = await discoverAdvisorConfigs(tmp, tmp);
226
233
  expect(advisors.map(a => a.name)).toEqual(["Architecture", "Security"]);
227
234
  expect(sharedInstructions).toContain("Shared baseline.");
228
235
  });
229
236
 
237
+ it("preserves significant leading whitespace and trailing newlines in block scalars", async () => {
238
+ const file = path.join(tmp, "WATCHDOG.yml");
239
+ const whitespaceDoc: WatchdogConfigDoc = {
240
+ instructions: " indented first line\nplain second line\n\n",
241
+ advisors: [{ name: "Whitespace", instructions: "\n indented after blank\nplain" }],
242
+ };
243
+
244
+ await saveWatchdogConfigFile(file, whitespaceDoc);
245
+ expect(await loadWatchdogConfigFile(file)).toEqual(whitespaceDoc);
246
+ });
247
+
230
248
  it("round-trips an explicit empty tools list without collapsing it into the default", async () => {
231
249
  const file = path.join(tmp, "WATCHDOG.yml");
232
250
  const explicitNoToolsDoc: WatchdogConfigDoc = {
@@ -291,3 +309,41 @@ describe("resolveAdvisorConfigEditPath", () => {
291
309
  expect(await resolveAdvisorConfigEditPath("project", dirs(tmp))).toBe(path.join(tmp, "WATCHDOG.yml"));
292
310
  });
293
311
  });
312
+
313
+ describe("per-advisor enabled field", () => {
314
+ it("preserves explicit true, explicit false, and absence through save and discovery", async () => {
315
+ const tmp = await fsp.mkdtemp(path.join(os.tmpdir(), "omp-advisor-enabled-"));
316
+ try {
317
+ const doc: WatchdogConfigDoc = {
318
+ advisors: [
319
+ { name: "Explicit On", model: "test/model-a", enabled: true },
320
+ { name: "Explicit Off", model: "test/model-b", enabled: false },
321
+ { name: "Default", model: "test/model-c" },
322
+ ],
323
+ };
324
+ const file = path.join(tmp, "WATCHDOG.yml");
325
+ await saveWatchdogConfigFile(file, doc);
326
+
327
+ const loaded = await loadWatchdogConfigFile(file);
328
+ expect(loaded.advisors.map(advisor => advisor.enabled)).toEqual([true, false, undefined]);
329
+
330
+ const { advisors } = await discoverAdvisorConfigs(tmp, tmp);
331
+ expect(advisors.map(advisor => advisor.enabled)).toEqual([true, false, undefined]);
332
+ } finally {
333
+ await fsp.rm(tmp, { recursive: true, force: true });
334
+ }
335
+ });
336
+
337
+ it("emits explicit boolean values but omits an absent enabled field", () => {
338
+ const text = serializeWatchdogConfig({
339
+ advisors: [
340
+ { name: "Explicit On", enabled: true },
341
+ { name: "Explicit Off", enabled: false },
342
+ { name: "Default" },
343
+ ],
344
+ });
345
+ expect(text).toContain("enabled: true");
346
+ expect(text).toContain("enabled: false");
347
+ expect(text.match(/enabled:/g)).toHaveLength(2);
348
+ });
349
+ });
@@ -106,8 +106,11 @@ export function isAdvisorInterruptImmuneTurnActive(opts: {
106
106
  * the live turn while one is streaming, or (when idle) a triggered turn so the
107
107
  * advice is acted on immediately.
108
108
  * - If the primary tail is already a terminal text answer and there is no queued
109
- * work, late interrupting advice is preserved as a visible card instead of
110
- * waking the primary to restate completion.
109
+ * work, a late `concern` is preserved as a visible card instead of waking the
110
+ * primary to restate completion. A `blocker` is the exception: it means the
111
+ * agent handed off broken or unexercised work, so it still steers a triggered
112
+ * turn to force the primary to acknowledge and continue before the turn is
113
+ * considered done (#5628) — deferring it to the next user turn is the bug.
111
114
  * - After a deliberate user interrupt (`autoResumeSuppressed`) the advisor must
112
115
  * not auto-resume the stopped run. While the agent is idle — or still tearing
113
116
  * the interrupted turn down (`aborting`) — the note is preserved as a visible
@@ -129,7 +132,8 @@ export function resolveAdvisorDeliveryChannel(opts: {
129
132
  }): AdvisorDeliveryChannel {
130
133
  if (!isInterruptingSeverity(opts.severity)) return "aside";
131
134
  if (opts.autoResumeSuppressed && (opts.aborting || !opts.streaming)) return "preserve";
132
- if (opts.terminalAnswerNoQueuedWork && !opts.streaming && !opts.aborting) return "preserve";
135
+ if (opts.terminalAnswerNoQueuedWork && opts.severity !== "blocker" && !opts.streaming && !opts.aborting)
136
+ return "preserve";
133
137
  if (opts.interruptImmuneTurnActive) return "aside";
134
138
  return "steer";
135
139
  }
@@ -22,8 +22,23 @@ export interface AdvisorConfig {
22
22
  model?: string;
23
23
  tools?: string[];
24
24
  instructions?: string;
25
+ /** Per-advisor on/off toggle (default `true`). When `false`, the advisor
26
+ * stays in the roster but its runtime is never built — it shows `○` in
27
+ * the status line and `/advisor status` rather than disappearing. */
28
+ enabled?: boolean;
25
29
  }
26
30
 
31
+ /**
32
+ * Runtime health of a single advisor, surfaced in stats and the status line.
33
+ * - `running` — actively processing primary turns
34
+ * - `paused` — user-toggled off via per-advisor switch (runtime disposed)
35
+ * - `quota_exhausted` — provider returned a quota/rate-limit error; the
36
+ * runtime auto-retries after a cooldown so it can resume without user action
37
+ * - `error` — repeated transient failures; backlog dropped to prevent stall
38
+ * - `no_model` — no model resolved for this advisor's role/explicit model
39
+ */
40
+ export type AdvisorRuntimeStatus = "running" | "paused" | "quota_exhausted" | "error" | "no_model";
41
+
27
42
  /**
28
43
  * The result of walking the `WATCHDOG.yml`/`WATCHDOG.yaml` search path: the
29
44
  * deduped advisor roster plus the concatenated top-level `instructions` baseline
@@ -39,6 +54,7 @@ const advisorEntrySchema = type({
39
54
  "model?": "string",
40
55
  "tools?": "string[]",
41
56
  "instructions?": "string",
57
+ "enabled?": "boolean",
42
58
  });
43
59
 
44
60
  const watchdogYamlSchema = type({
@@ -156,6 +172,7 @@ export async function discoverAdvisorConfigs(cwd: string, agentDir?: string): Pr
156
172
  model: entry.model?.trim() || undefined,
157
173
  tools: filterAdvisorTools(entry.tools, item.path),
158
174
  instructions,
175
+ enabled: entry.enabled,
159
176
  });
160
177
  }
161
178
  }
@@ -236,38 +253,73 @@ export async function loadWatchdogConfigFile(filePath: string): Promise<Watchdog
236
253
  logger.warn("Advisor config: invalid schema for edit", { path: filePath, error: result.summary });
237
254
  return { advisors: [] };
238
255
  }
239
- return {
240
- instructions: result.instructions?.trim() ? result.instructions : undefined,
241
- advisors: (result.advisors ?? []).map(a => ({
242
- name: a.name,
243
- model: a.model?.trim() || undefined,
244
- tools: a.tools === undefined ? undefined : [...a.tools],
245
- instructions: a.instructions?.trim() ? a.instructions : undefined,
246
- })),
247
- };
256
+ const advisors = (result.advisors ?? []).map(a => {
257
+ const advisor: AdvisorConfig = { name: a.name };
258
+ if (a.model?.trim()) advisor.model = a.model;
259
+ if (a.tools !== undefined) advisor.tools = [...a.tools];
260
+ if (a.instructions?.trim()) advisor.instructions = a.instructions;
261
+ if (a.enabled !== undefined) advisor.enabled = a.enabled;
262
+ return advisor;
263
+ });
264
+ const doc: WatchdogConfigDoc = { advisors };
265
+ if (result.instructions?.trim()) doc.instructions = result.instructions;
266
+ return doc;
248
267
  }
249
268
 
250
269
  /**
251
- * Serialize an editable doc back to block-style `WATCHDOG.yml` text via Bun's
252
- * `YAML.stringify` (the same API the repo uses for other hand-editable config),
253
- * omitting empty fields. Round-trips through {@link loadWatchdogConfigFile}.
270
+ * Serialize an editable doc back to canonical, hand-editable `WATCHDOG.yml`.
271
+ * Multiline instruction fields use literal block scalars while scalar quoting
272
+ * delegates to Bun's YAML encoder. Round-trips through {@link loadWatchdogConfigFile}.
254
273
  * Returns `""` for an empty doc.
255
274
  */
275
+
276
+ function appendYamlString(lines: string[], indent: string, key: string, value: string): void {
277
+ const hasSignificantLeadingWhitespace = value.split("\n").some(line => /^[ \t]/.test(line));
278
+ if (!value.includes("\n") || hasSignificantLeadingWhitespace) {
279
+ lines.push(`${indent}${key}: ${YAML.stringify(value)}`);
280
+ return;
281
+ }
282
+ const normalized = value.replaceAll("\r\n", "\n");
283
+ let trailingNewlines = 0;
284
+ for (let index = normalized.length - 1; index >= 0 && normalized[index] === "\n"; index--) {
285
+ trailingNewlines++;
286
+ }
287
+ const chomp = trailingNewlines === 0 ? "|2-" : trailingNewlines === 1 ? "|2" : "|2+";
288
+ const body = trailingNewlines === 0 ? normalized : normalized.slice(0, -trailingNewlines);
289
+ lines.push(`${indent}${key}: ${chomp}`);
290
+ for (const line of body.split("\n")) {
291
+ lines.push(`${indent} ${line}`);
292
+ }
293
+ for (let index = 1; index < trailingNewlines; index++) {
294
+ lines.push(`${indent} `);
295
+ }
296
+ }
297
+
256
298
  export function serializeWatchdogConfig(doc: WatchdogConfigDoc): string {
257
- const out: { instructions?: string; advisors?: AdvisorConfig[] } = {};
258
- if (doc.instructions?.trim()) out.instructions = doc.instructions;
299
+ const lines: string[] = [];
300
+ if (doc.instructions?.trim()) appendYamlString(lines, "", "instructions", doc.instructions);
259
301
  if (doc.advisors.length > 0) {
260
- out.advisors = doc.advisors.map(a => {
261
- const entry: AdvisorConfig = { name: a.name };
262
- if (a.model?.trim()) entry.model = a.model;
263
- if (a.tools !== undefined) entry.tools = [...a.tools];
264
- if (a.instructions?.trim()) entry.instructions = a.instructions;
265
- return entry;
266
- });
302
+ lines.push("advisors:");
303
+ for (const advisor of doc.advisors) {
304
+ lines.push(` - name: ${YAML.stringify(advisor.name)}`);
305
+ if (advisor.model?.trim()) lines.push(` model: ${YAML.stringify(advisor.model)}`);
306
+ if (advisor.tools !== undefined) {
307
+ if (advisor.tools.length === 0) {
308
+ lines.push(" tools: []");
309
+ } else {
310
+ lines.push(" tools:");
311
+ for (const tool of advisor.tools) {
312
+ lines.push(` - ${YAML.stringify(tool)}`);
313
+ }
314
+ }
315
+ }
316
+ if (advisor.instructions?.trim()) {
317
+ appendYamlString(lines, " ", "instructions", advisor.instructions);
318
+ }
319
+ if (advisor.enabled !== undefined) lines.push(` enabled: ${advisor.enabled}`);
320
+ }
267
321
  }
268
- if (out.instructions === undefined && out.advisors === undefined) return "";
269
- const text = YAML.stringify(out, null, 2);
270
- return text.endsWith("\n") ? text : `${text}\n`;
322
+ return lines.length === 0 ? "" : `${lines.join("\n")}\n`;
271
323
  }
272
324
 
273
325
  /**