@statechange/council 0.5.0 → 0.7.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 (153) hide show
  1. package/README.md +27 -27
  2. package/dist/cli.js +15 -15
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/councilor.d.ts +7 -0
  5. package/dist/commands/councilor.js +98 -0
  6. package/dist/commands/councilor.js.map +1 -0
  7. package/dist/commands/discuss.d.ts +2 -2
  8. package/dist/commands/discuss.js +14 -14
  9. package/dist/commands/discuss.js.map +1 -1
  10. package/dist/commands/history.js +2 -2
  11. package/dist/commands/history.js.map +1 -1
  12. package/dist/commands/list.js +7 -7
  13. package/dist/commands/list.js.map +1 -1
  14. package/dist/core/conversation-engine.d.ts +5 -3
  15. package/dist/core/conversation-engine.js +59 -34
  16. package/dist/core/conversation-engine.js.map +1 -1
  17. package/dist/core/councilor-loader.d.ts +4 -0
  18. package/dist/core/councilor-loader.js +98 -0
  19. package/dist/core/councilor-loader.js.map +1 -0
  20. package/dist/core/councilor-registry.d.ts +12 -0
  21. package/dist/core/councilor-registry.js +131 -0
  22. package/dist/core/councilor-registry.js.map +1 -0
  23. package/dist/core/counsellor-loader.js +2 -1
  24. package/dist/core/counsellor-loader.js.map +1 -1
  25. package/dist/core/excalidraw-cheatsheet.js +2 -2
  26. package/dist/core/history.d.ts +1 -1
  27. package/dist/core/history.js +1 -1
  28. package/dist/core/history.js.map +1 -1
  29. package/dist/core/infographic.js +2 -2
  30. package/dist/core/infographic.js.map +1 -1
  31. package/dist/core/output-formatter.js +2 -2
  32. package/dist/core/output-formatter.js.map +1 -1
  33. package/dist/core/secretary.js +7 -7
  34. package/dist/core/secretary.js.map +1 -1
  35. package/dist/core/skill-loader.d.ts +2 -2
  36. package/dist/core/skill-loader.js +6 -6
  37. package/dist/core/skill-loader.js.map +1 -1
  38. package/dist/electron/ipc-handlers.js +73 -64
  39. package/dist/electron/ipc-handlers.js.map +1 -1
  40. package/dist/electron/main.js +5 -2
  41. package/dist/electron/main.js.map +1 -1
  42. package/dist/electron/preload.js +5 -5
  43. package/dist/electron/preload.js.map +1 -1
  44. package/dist/types.d.ts +16 -13
  45. package/dist/types.js +1 -1
  46. package/dist/types.js.map +1 -1
  47. package/dist-electron/main.js +619 -595
  48. package/dist-electron/preload.mjs +1 -1
  49. package/dist-renderer/assets/{ar-SA-G6X2FPQ2-DIdoFnw2.js → ar-SA-G6X2FPQ2-BgPx2ItE.js} +1 -1
  50. package/dist-renderer/assets/{arc-BTotlQZy.js → arc-uuSymAy6.js} +1 -1
  51. package/dist-renderer/assets/{az-AZ-76LH7QW2-vKvyE204.js → az-AZ-76LH7QW2-CXxeQJ5N.js} +1 -1
  52. package/dist-renderer/assets/{bg-BG-XCXSNQG7-CUw6XMat.js → bg-BG-XCXSNQG7-B-v6aJJk.js} +1 -1
  53. package/dist-renderer/assets/{blockDiagram-38ab4fdb-KuBxDdIg.js → blockDiagram-38ab4fdb-B2x5B_qZ.js} +1 -1
  54. package/dist-renderer/assets/{bn-BD-2XOGV67Q-jAYEmCju.js → bn-BD-2XOGV67Q-CmhmKt_C.js} +1 -1
  55. package/dist-renderer/assets/{c4Diagram-3d4e48cf-BNBdnuO2.js → c4Diagram-3d4e48cf-qv_cNClp.js} +1 -1
  56. package/dist-renderer/assets/{ca-ES-6MX7JW3Y-CI1kukXq.js → ca-ES-6MX7JW3Y-Cnypgm9v.js} +1 -1
  57. package/dist-renderer/assets/channel-CbR4ut5U.js +1 -0
  58. package/dist-renderer/assets/{classDiagram-70f12bd4-CupH4cFs.js → classDiagram-70f12bd4-DdjxeBAm.js} +1 -1
  59. package/dist-renderer/assets/{classDiagram-v2-f2320105-BahsG3Tc.js → classDiagram-v2-f2320105-DjGhSmzz.js} +1 -1
  60. package/dist-renderer/assets/clone-CdbunAYL.js +1 -0
  61. package/dist-renderer/assets/{createText-2e5e7dd3-B3UjfUG4.js → createText-2e5e7dd3-DPeQ2O5Z.js} +1 -1
  62. package/dist-renderer/assets/{cs-CZ-2BRQDIVT-UI0wC0-v.js → cs-CZ-2BRQDIVT-CLjGATXX.js} +1 -1
  63. package/dist-renderer/assets/{da-DK-5WZEPLOC-CQAf8fls.js → da-DK-5WZEPLOC-D5L0qoxT.js} +1 -1
  64. package/dist-renderer/assets/{de-DE-XR44H4JA-CjKBhliG.js → de-DE-XR44H4JA-C32FRrv8.js} +1 -1
  65. package/dist-renderer/assets/{edges-e0da2a9e-2iE4P-5f.js → edges-e0da2a9e-C9UtuZtL.js} +1 -1
  66. package/dist-renderer/assets/{el-GR-BZB4AONW-CQB6VLsx.js → el-GR-BZB4AONW-DbhHCmA8.js} +1 -1
  67. package/dist-renderer/assets/{erDiagram-9861fffd-BBDZcr4V.js → erDiagram-9861fffd-BkLC6VUM.js} +1 -1
  68. package/dist-renderer/assets/{es-ES-U4NZUMDT-DiI8d1UY.js → es-ES-U4NZUMDT-DXQY7ys3.js} +1 -1
  69. package/dist-renderer/assets/{eu-ES-A7QVB2H4-DkxqpvcF.js → eu-ES-A7QVB2H4-B4VgVqIF.js} +1 -1
  70. package/dist-renderer/assets/{fa-IR-HGAKTJCU-D6yQ0OLT.js → fa-IR-HGAKTJCU-elHgBamS.js} +1 -1
  71. package/dist-renderer/assets/{fi-FI-Z5N7JZ37-YXeS6J7A.js → fi-FI-Z5N7JZ37-BQe_Oc8x.js} +1 -1
  72. package/dist-renderer/assets/{flowDb-956e92f1-BUyPBg4x.js → flowDb-956e92f1-DR2fF74n.js} +1 -1
  73. package/dist-renderer/assets/{flowDiagram-66a62f08-DlzNPiyJ.js → flowDiagram-66a62f08-wDDwHPpp.js} +1 -1
  74. package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-DYxWopzM.js +1 -0
  75. package/dist-renderer/assets/{flowchart-elk-definition-4a651766-Chzxgp5p.js → flowchart-elk-definition-4a651766-m7cC1ndJ.js} +1 -1
  76. package/dist-renderer/assets/{fr-FR-RHASNOE6-f9mGiK-9.js → fr-FR-RHASNOE6-U8yDW4RB.js} +1 -1
  77. package/dist-renderer/assets/{ganttDiagram-c361ad54-WJyNxrSM.js → ganttDiagram-c361ad54-SpXIeRET.js} +1 -1
  78. package/dist-renderer/assets/{gitGraphDiagram-72cf32ee-BVsVFoEf.js → gitGraphDiagram-72cf32ee-CqmKGlMY.js} +1 -1
  79. package/dist-renderer/assets/{gl-ES-HMX3MZ6V-BjCm-PNJ.js → gl-ES-HMX3MZ6V-ti0GznU7.js} +1 -1
  80. package/dist-renderer/assets/{graph-CkTunRXI.js → graph-KjGTQ8qd.js} +1 -1
  81. package/dist-renderer/assets/{he-IL-6SHJWFNN-DfYxNI2P.js → he-IL-6SHJWFNN-M5av5hFk.js} +1 -1
  82. package/dist-renderer/assets/{hi-IN-IWLTKZ5I-DoZMu26E.js → hi-IN-IWLTKZ5I-Ci72nkHu.js} +1 -1
  83. package/dist-renderer/assets/{hu-HU-A5ZG7DT2-DTrSQZCB.js → hu-HU-A5ZG7DT2-CJZteS36.js} +1 -1
  84. package/dist-renderer/assets/{id-ID-SAP4L64H-C_U2ELPS.js → id-ID-SAP4L64H-DmDvIS5c.js} +1 -1
  85. package/dist-renderer/assets/{index-3862675e-BNtj693g.js → index-3862675e-CCE1hBgs.js} +1 -1
  86. package/dist-renderer/assets/index-BUIE-P96.js +51 -0
  87. package/dist-renderer/assets/{index-CMFrvjlh.js → index-CsJIZuMr.js} +4 -4
  88. package/dist-renderer/assets/{index-Dfvdrrq2.css → index-HXjW9ZG3.css} +1 -1
  89. package/dist-renderer/assets/{infoDiagram-f8f76790-BgR_oHkS.js → infoDiagram-f8f76790-CcZfsGA-.js} +1 -1
  90. package/dist-renderer/assets/{it-IT-JPQ66NNP-DUHgayhE.js → it-IT-JPQ66NNP-Bzpfv7zO.js} +1 -1
  91. package/dist-renderer/assets/{ja-JP-DBVTYXUO-BvvEmKLB.js → ja-JP-DBVTYXUO-Bybhg0KI.js} +1 -1
  92. package/dist-renderer/assets/{journeyDiagram-49397b02-DMpZKzA6.js → journeyDiagram-49397b02-D5LB2Wfi.js} +1 -1
  93. package/dist-renderer/assets/{kaa-6HZHGXH3-g6rUcGHV.js → kaa-6HZHGXH3-n7D3u8lS.js} +1 -1
  94. package/dist-renderer/assets/{kab-KAB-ZGHBKWFO-7y2loFp3.js → kab-KAB-ZGHBKWFO-BsHCeWxt.js} +1 -1
  95. package/dist-renderer/assets/{kk-KZ-P5N5QNE5-CrUBYCyh.js → kk-KZ-P5N5QNE5-Dvo1EL3s.js} +1 -1
  96. package/dist-renderer/assets/{km-KH-HSX4SM5Z-BmTnQD1U.js → km-KH-HSX4SM5Z-Bo9Koyui.js} +1 -1
  97. package/dist-renderer/assets/{ko-KR-MTYHY66A-B13GdnWI.js → ko-KR-MTYHY66A-Dqbq9Ios.js} +1 -1
  98. package/dist-renderer/assets/{ku-TR-6OUDTVRD-BLvm9HOc.js → ku-TR-6OUDTVRD-B_bI20Zq.js} +1 -1
  99. package/dist-renderer/assets/{layout-BRp1ZWud.js → layout-BMJBnVbc.js} +1 -1
  100. package/dist-renderer/assets/{line-CBnb-2iQ.js → line-DYvIrKnf.js} +1 -1
  101. package/dist-renderer/assets/{linear-DflXVGQM.js → linear-CzUW4n8y.js} +1 -1
  102. package/dist-renderer/assets/{lt-LT-XHIRWOB4-o21UyUem.js → lt-LT-XHIRWOB4-DMcSSx20.js} +1 -1
  103. package/dist-renderer/assets/{lv-LV-5QDEKY6T-Co_yvRsA.js → lv-LV-5QDEKY6T-WQFa3Exi.js} +1 -1
  104. package/dist-renderer/assets/{mindmap-definition-fc14e90a-DpcRC-Qx.js → mindmap-definition-fc14e90a-CbbLnbfT.js} +1 -1
  105. package/dist-renderer/assets/{mr-IN-CRQNXWMA-4PVnnNcU.js → mr-IN-CRQNXWMA-CeiPk7t6.js} +1 -1
  106. package/dist-renderer/assets/{my-MM-5M5IBNSE-Dk5W3fXX.js → my-MM-5M5IBNSE-9E7W6sP8.js} +1 -1
  107. package/dist-renderer/assets/{nb-NO-T6EIAALU-DMkPaluQ.js → nb-NO-T6EIAALU-CmEniZIK.js} +1 -1
  108. package/dist-renderer/assets/{nl-NL-IS3SIHDZ-CYOTB3Eg.js → nl-NL-IS3SIHDZ-zXTYcbKu.js} +1 -1
  109. package/dist-renderer/assets/{nn-NO-6E72VCQL-D4OuF2An.js → nn-NO-6E72VCQL-h3sqNMWN.js} +1 -1
  110. package/dist-renderer/assets/{oc-FR-POXYY2M6-Dgdddkga.js → oc-FR-POXYY2M6-DJ0D2dhd.js} +1 -1
  111. package/dist-renderer/assets/{pa-IN-N4M65BXN-DB2Y0wYm.js → pa-IN-N4M65BXN-DZ6nLqLL.js} +1 -1
  112. package/dist-renderer/assets/{percentages-BXMCSKIN-bsDgM1tI.js → percentages-BXMCSKIN-C_WTAk1u.js} +7 -7
  113. package/dist-renderer/assets/{pica-B0wN7u7a.js → pica-41wOwwIp.js} +1 -1
  114. package/dist-renderer/assets/{pieDiagram-8a3498a8-D8QY6THh.js → pieDiagram-8a3498a8-DbqgEWOz.js} +1 -1
  115. package/dist-renderer/assets/{pl-PL-T2D74RX3-BvikiN3J.js → pl-PL-T2D74RX3-TTq9_VVs.js} +1 -1
  116. package/dist-renderer/assets/{pt-BR-5N22H2LF-DsWDLPF-.js → pt-BR-5N22H2LF-D8eWF-rx.js} +1 -1
  117. package/dist-renderer/assets/{pt-PT-UZXXM6DQ-BnTfNmgE.js → pt-PT-UZXXM6DQ-Ck4YSJms.js} +1 -1
  118. package/dist-renderer/assets/{quadrantDiagram-120e2f19-CYknF173.js → quadrantDiagram-120e2f19-astysE66.js} +1 -1
  119. package/dist-renderer/assets/{requirementDiagram-deff3bca-DpK6qRCG.js → requirementDiagram-deff3bca-BczvOV-q.js} +1 -1
  120. package/dist-renderer/assets/{ro-RO-JPDTUUEW-WzwdaLQU.js → ro-RO-JPDTUUEW-Cix9Zgf2.js} +1 -1
  121. package/dist-renderer/assets/{ru-RU-B4JR7IUQ-kSoy6NPm.js → ru-RU-B4JR7IUQ-DOEAc_qv.js} +1 -1
  122. package/dist-renderer/assets/{sankeyDiagram-04a897e0-qq5SY-Ue.js → sankeyDiagram-04a897e0-BdKNjOGj.js} +1 -1
  123. package/dist-renderer/assets/{sequenceDiagram-704730f1-WIIr9Ug4.js → sequenceDiagram-704730f1-DmhSJhf3.js} +1 -1
  124. package/dist-renderer/assets/{si-LK-N5RQ5JYF-AuePWwxD.js → si-LK-N5RQ5JYF-CdpuKOSp.js} +1 -1
  125. package/dist-renderer/assets/{sk-SK-C5VTKIMK-DC2HAG_f.js → sk-SK-C5VTKIMK-B_9Y1a-J.js} +1 -1
  126. package/dist-renderer/assets/{sl-SI-NN7IZMDC-BYS14Fmd.js → sl-SI-NN7IZMDC-6_HfcMn2.js} +1 -1
  127. package/dist-renderer/assets/{stateDiagram-587899a1-BoO-tW9h.js → stateDiagram-587899a1--iD3nJ-S.js} +1 -1
  128. package/dist-renderer/assets/{stateDiagram-v2-d93cdb3a-_GDLfMOw.js → stateDiagram-v2-d93cdb3a-D_ikJOMZ.js} +1 -1
  129. package/dist-renderer/assets/{styles-6aaf32cf-Bq02XG30.js → styles-6aaf32cf-CmNrPQpM.js} +1 -1
  130. package/dist-renderer/assets/{styles-9a916d00-DrDuz5OD.js → styles-9a916d00-DF6mmpc8.js} +1 -1
  131. package/dist-renderer/assets/{styles-c10674c1-BiUI5sb2.js → styles-c10674c1-DrNd7u60.js} +1 -1
  132. package/dist-renderer/assets/{subset-shared.chunk-B4D4aZVR.js → subset-shared.chunk-DESKTf4i.js} +1 -1
  133. package/dist-renderer/assets/{subset-worker.chunk-iv_EGnGX.js → subset-worker.chunk-foHYh0Wd.js} +1 -1
  134. package/dist-renderer/assets/{sv-SE-XGPEYMSR-C_IQ_dRL.js → sv-SE-XGPEYMSR-CIuCW4P8.js} +1 -1
  135. package/dist-renderer/assets/{svgDrawCommon-08f97a94-CGR4dkyx.js → svgDrawCommon-08f97a94-z7vJpB0X.js} +1 -1
  136. package/dist-renderer/assets/{ta-IN-2NMHFXQM-Bv6L9r0f.js → ta-IN-2NMHFXQM-BzAn6f_T.js} +1 -1
  137. package/dist-renderer/assets/{th-TH-HPSO5L25-Y-RfnO5u.js → th-TH-HPSO5L25-F8UKokOj.js} +1 -1
  138. package/dist-renderer/assets/{timeline-definition-85554ec2-DNqe_2XL.js → timeline-definition-85554ec2-CqgD9A1Q.js} +1 -1
  139. package/dist-renderer/assets/{tr-TR-DEFEU3FU-B46ZqcXK.js → tr-TR-DEFEU3FU-Blb21beE.js} +1 -1
  140. package/dist-renderer/assets/{uk-UA-QMV73CPH-DzETOoLm.js → uk-UA-QMV73CPH-BNT7k4n6.js} +1 -1
  141. package/dist-renderer/assets/{vi-VN-M7AON7JQ-CKt3VFx4.js → vi-VN-M7AON7JQ-Cfx0gaLT.js} +1 -1
  142. package/dist-renderer/assets/{xychartDiagram-e933f94c-IFYih3oh.js → xychartDiagram-e933f94c-FbPSsBHD.js} +1 -1
  143. package/dist-renderer/assets/{zh-CN-LNUGB5OW-DDBoaB0P.js → zh-CN-LNUGB5OW-CpiybZS1.js} +1 -1
  144. package/dist-renderer/assets/{zh-HK-E62DVLB3-CtYuCfrU.js → zh-HK-E62DVLB3-UIQkTr8J.js} +1 -1
  145. package/dist-renderer/assets/{zh-TW-RAJ6MFWO-61MRQ0N-.js → zh-TW-RAJ6MFWO-GvyNND72.js} +1 -1
  146. package/dist-renderer/index.html +2 -2
  147. package/package.json +2 -2
  148. package/skills/council-manage/SKILL.md +54 -43
  149. package/skills/council-setup-keys/SKILL.md +2 -2
  150. package/dist-renderer/assets/channel-CYpnUKSx.js +0 -1
  151. package/dist-renderer/assets/clone-DFY_WtGN.js +0 -1
  152. package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-D9BHRMv4.js +0 -1
  153. package/dist-renderer/assets/index-B-vvA6a2.js +0 -36
@@ -1,20 +1,20 @@
1
- import { shell as ee, dialog as ke, app as L, protocol as me, Menu as te, net as ve, BrowserWindow as pe, ipcMain as _e } from "electron";
2
- import { join as u, basename as C, resolve as G, dirname as Ee } from "node:path";
3
- import { fileURLToPath as Ae, pathToFileURL as Ie } from "node:url";
4
- import { existsSync as v, writeFileSync as Se, appendFileSync as Oe } from "node:fs";
5
- import { readFile as y, readdir as z, stat as fe, mkdir as N, rm as F, writeFile as $, appendFile as Pe } from "node:fs/promises";
6
- import { execFile as U } from "node:child_process";
7
- import { promisify as R } from "node:util";
8
- import { homedir as w } from "node:os";
9
- import V from "gray-matter";
1
+ import { shell as te, dialog as Ee, app as L, protocol as pe, Menu as ne, net as Se, BrowserWindow as fe, ipcMain as Ae } from "electron";
2
+ import { join as d, basename as C, resolve as G, dirname as Ie } from "node:path";
3
+ import { fileURLToPath as Oe, pathToFileURL as $e } from "node:url";
4
+ import { existsSync as E, mkdirSync as Pe, writeFileSync as Ce, appendFileSync as Ne } from "node:fs";
5
+ import { homedir as v } from "node:os";
6
+ import { readFile as w, readdir as V, stat as he, mkdir as N, rm as F, writeFile as P, appendFile as xe } from "node:fs/promises";
7
+ import { execFile as R } from "node:child_process";
8
+ import { promisify as U } from "node:util";
9
+ import X from "gray-matter";
10
10
  import { z as k } from "zod";
11
- import $e from "@anthropic-ai/sdk";
12
- import Ce from "openai";
13
- import { GoogleGenerativeAI as Ne } from "@google/generative-ai";
14
- import { Ollama as xe } from "ollama";
15
- import { createRequire as Ue } from "node:module";
11
+ import Re from "@anthropic-ai/sdk";
12
+ import Ue from "openai";
13
+ import { GoogleGenerativeAI as Te } from "@google/generative-ai";
14
+ import { Ollama as Le } from "ollama";
15
+ import { createRequire as De } from "node:module";
16
16
  import "dotenv/config";
17
- const Re = k.object({
17
+ const Ke = k.object({
18
18
  name: k.string(),
19
19
  description: k.string(),
20
20
  interests: k.array(k.string()).default([]),
@@ -23,186 +23,192 @@ const Re = k.object({
23
23
  skills: k.array(k.string()).default([]),
24
24
  temperature: k.number().min(0).max(2).optional(),
25
25
  avatar: k.string().optional()
26
- }), Te = (n, r) => [
27
- u(n, "skills", r, "SKILL.md"),
28
- u(process.cwd(), ".claude", "skills", r, "SKILL.md"),
29
- u(w(), ".agents", "skills", r, "SKILL.md"),
30
- u(w(), ".claude", "skills", r, "SKILL.md")
26
+ }), je = (o, s) => [
27
+ d(o, "skills", s, "SKILL.md"),
28
+ d(process.cwd(), ".claude", "skills", s, "SKILL.md"),
29
+ d(v(), ".agents", "skills", s, "SKILL.md"),
30
+ d(v(), ".claude", "skills", s, "SKILL.md")
31
31
  ];
32
- async function Le(n, r) {
33
- for (const t of Te(r, n))
34
- if (v(t)) {
35
- const e = await y(t, "utf-8"), { content: o } = V(e);
36
- return o.trim();
32
+ async function Be(o, s) {
33
+ for (const t of je(s, o))
34
+ if (E(t)) {
35
+ const e = await w(t, "utf-8"), { content: r } = X(e);
36
+ return r.trim();
37
37
  }
38
38
  return null;
39
39
  }
40
- async function De(n, r) {
40
+ async function Ge(o, s) {
41
41
  const t = [];
42
- for (const e of n) {
43
- const o = await Le(e, r);
44
- o && t.push(`## Skill: ${e}
42
+ for (const e of o) {
43
+ const r = await Be(e, s);
44
+ r && t.push(`## Skill: ${e}
45
45
 
46
- ${o}`);
46
+ ${r}`);
47
47
  }
48
48
  return t.join(`
49
49
 
50
50
  `);
51
51
  }
52
- function Ke(n, r) {
53
- return n ? n.startsWith("http://") || n.startsWith("https://") ? n : `council-file://${n.startsWith("/") ? n : u(r, n)}` : void 0;
52
+ function Fe(o, s) {
53
+ return o ? o.startsWith("http://") || o.startsWith("https://") ? o : `council-file://${o.startsWith("/") ? o : d(s, o)}` : void 0;
54
54
  }
55
- async function je(n, r) {
55
+ async function We(o, s) {
56
56
  const t = /\{\{(.+?)\}\}/g;
57
- let e = n;
58
- for (const o of n.matchAll(t)) {
59
- const s = o[1].trim(), a = u(r, s);
60
- if (v(a)) {
61
- const i = await y(a, "utf-8");
62
- e = e.replace(o[0], i.trim());
57
+ let e = o;
58
+ for (const r of o.matchAll(t)) {
59
+ const n = r[1].trim(), a = d(s, n);
60
+ if (E(a)) {
61
+ const i = await w(a, "utf-8");
62
+ e = e.replace(r[0], i.trim());
63
63
  } else
64
- e = e.replace(o[0], `[Reference not found: ${s}]`);
64
+ e = e.replace(r[0], `[Reference not found: ${n}]`);
65
65
  }
66
66
  return e;
67
67
  }
68
- async function ne(n) {
69
- const r = G(n), t = u(r, "ABOUT.md");
70
- if (!v(t))
71
- throw new Error(`No ABOUT.md found in ${r}`);
72
- const e = await y(t, "utf-8"), { data: o, content: s } = V(e), a = Re.parse(o);
73
- let i = await je(s.trim(), r);
68
+ async function oe(o) {
69
+ const s = G(o), t = d(s, "ABOUT.md");
70
+ if (!E(t))
71
+ throw new Error(`No ABOUT.md found in ${s}`);
72
+ const e = await w(t, "utf-8"), { data: r, content: n } = X(e), a = Ke.parse(r);
73
+ let i = await We(n.trim(), s);
74
74
  if (a.skills.length > 0) {
75
- const l = await De(a.skills, r);
76
- l && (i += `
75
+ const u = await Ge(a.skills, s);
76
+ u && (i += `
77
77
 
78
- ` + l);
78
+ ` + u);
79
79
  }
80
80
  return {
81
- id: C(r),
81
+ id: C(s),
82
82
  frontmatter: a,
83
83
  systemPrompt: i,
84
- dirPath: r,
85
- avatarUrl: Ke(a.avatar, r)
84
+ dirPath: s,
85
+ avatarUrl: Fe(a.avatar, s)
86
86
  };
87
87
  }
88
- async function oe(n, r) {
88
+ async function re(o, s) {
89
89
  const t = [], e = /* @__PURE__ */ new Set();
90
- if (r?.length) {
91
- for (const o of r)
92
- if (v(u(o, "ABOUT.md")))
90
+ if (s?.length) {
91
+ for (const r of s)
92
+ if (E(d(r, "ABOUT.md")))
93
93
  try {
94
- const s = await ne(o);
95
- e.has(s.id) || (t.push(s), e.add(s.id));
94
+ const n = await oe(r);
95
+ e.has(n.id) || (t.push(n), e.add(n.id));
96
96
  } catch {
97
97
  }
98
98
  }
99
- if (v(n)) {
100
- const o = await z(n);
101
- for (const s of o) {
102
- const a = u(n, s);
103
- (await fe(a)).isDirectory() && v(u(a, "ABOUT.md")) && (e.has(C(a)) || (t.push(await ne(a)), e.add(C(a))));
99
+ if (E(o)) {
100
+ const r = await V(o);
101
+ for (const n of r) {
102
+ const a = d(o, n);
103
+ (await he(a)).isDirectory() && E(d(a, "ABOUT.md")) && (e.has(C(a)) || (t.push(await oe(a)), e.add(C(a))));
104
104
  }
105
105
  }
106
106
  if (t.length === 0)
107
- throw new Error(`No counsellors found in ${n}. Each counsellor needs a directory with an ABOUT.md file.`);
107
+ throw new Error(
108
+ `No councilors found. Searched ${o}${s?.length ? ` and ${s.length} registered path(s)` : ""}.
109
+ Create one with: mkdir -p ~/.ai-council/councilors/my-councilor && council councilor add ~/.ai-council/councilors/my-councilor`
110
+ );
108
111
  return t;
109
112
  }
110
- const Be = R(U), he = u(w(), ".ai-council", "config.json"), se = u(w(), ".ai-council", "counsellors");
111
- async function X() {
113
+ const Me = U(R), ge = d(v(), ".ai-council", "config.json"), se = d(v(), ".ai-council", "councilors");
114
+ async function Q() {
112
115
  try {
113
- return JSON.parse(await y(he, "utf-8"));
116
+ return JSON.parse(await w(ge, "utf-8"));
114
117
  } catch {
115
118
  return { backends: {} };
116
119
  }
117
120
  }
118
- async function Q(n) {
119
- const r = u(w(), ".ai-council");
120
- await N(r, { recursive: !0 }), await $(he, JSON.stringify(n, null, 2), "utf-8");
121
+ async function Z(o) {
122
+ const s = d(v(), ".ai-council");
123
+ await N(s, { recursive: !0 }), await P(ge, JSON.stringify(o, null, 2), "utf-8");
121
124
  }
122
- function re(n) {
123
- return Object.values(n.counsellors ?? {}).map((r) => r.path);
125
+ function Ye(o) {
126
+ return o.councilors ?? o.counsellors ?? {};
124
127
  }
125
- async function Ge(n) {
126
- const r = G(n), t = u(r, "ABOUT.md");
127
- if (!v(t))
128
- throw new Error(`No ABOUT.md found in ${r}`);
129
- const e = C(r), o = await X(), s = o.counsellors ?? {};
130
- if (s[e])
131
- throw new Error(`Counsellor "${e}" is already registered (path: ${s[e].path})`);
132
- const l = (await y(t, "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? e;
133
- return s[e] = {
134
- path: r,
128
+ function ae(o) {
129
+ return Object.values(Ye(o)).map((s) => s.path);
130
+ }
131
+ async function He(o) {
132
+ const s = G(o), t = d(s, "ABOUT.md");
133
+ if (!E(t))
134
+ throw new Error(`No ABOUT.md found in ${s}`);
135
+ const e = C(s), r = await Q(), n = r.councilors ?? {};
136
+ if (n[e])
137
+ throw new Error(`Councilor "${e}" is already registered (path: ${n[e].path})`);
138
+ const u = (await w(t, "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? e;
139
+ return n[e] = {
140
+ path: s,
135
141
  source: "local",
136
142
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
137
- }, o.counsellors = s, await Q(o), { id: e, name: l };
143
+ }, r.councilors = n, await Z(r), { id: e, name: u };
138
144
  }
139
- async function Fe(n) {
145
+ async function Je(o) {
140
146
  await N(se, { recursive: !0 });
141
- const r = C(n, ".git").replace(/\.git$/, ""), t = u(se, r);
142
- if (v(t))
147
+ const s = C(o, ".git").replace(/\.git$/, ""), t = d(se, s);
148
+ if (E(t))
143
149
  throw new Error(`Directory already exists: ${t}. Remove it first or use a different URL.`);
144
- await Be("git", ["clone", "--depth", "1", n, t]);
145
- const e = [], o = await X(), s = o.counsellors ?? {};
146
- if (v(u(t, "ABOUT.md"))) {
147
- const a = r;
148
- if (s[a])
149
- throw new Error(`Counsellor "${a}" is already registered`);
150
- const c = (await y(u(t, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? a;
151
- s[a] = {
150
+ await Me("git", ["clone", "--depth", "1", o, t]);
151
+ const e = [], r = await Q(), n = r.councilors ?? {};
152
+ if (E(d(t, "ABOUT.md"))) {
153
+ const a = s;
154
+ if (n[a])
155
+ throw new Error(`Councilor "${a}" is already registered`);
156
+ const c = (await w(d(t, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? a;
157
+ n[a] = {
152
158
  path: t,
153
159
  source: "git",
154
- url: n,
160
+ url: o,
155
161
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
156
162
  }, e.push({ id: a, name: c });
157
163
  } else {
158
- const a = await z(t);
164
+ const a = await V(t);
159
165
  for (const i of a) {
160
166
  if (i.startsWith(".")) continue;
161
- const l = u(t, i);
162
- if ((await fe(l)).isDirectory() && v(u(l, "ABOUT.md"))) {
167
+ const u = d(t, i);
168
+ if ((await he(u)).isDirectory() && E(d(u, "ABOUT.md"))) {
163
169
  const m = i;
164
- if (s[m]) continue;
165
- const f = (await y(u(l, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? m;
166
- s[m] = {
167
- path: l,
170
+ if (n[m]) continue;
171
+ const p = (await w(d(u, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? m;
172
+ n[m] = {
173
+ path: u,
168
174
  source: "git",
169
- url: n,
175
+ url: o,
170
176
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
171
- }, e.push({ id: m, name: f });
177
+ }, e.push({ id: m, name: p });
172
178
  }
173
179
  }
174
180
  if (e.length === 0)
175
- throw await F(t, { recursive: !0, force: !0 }), new Error("No counsellors found in cloned repository (no ABOUT.md files)");
181
+ throw await F(t, { recursive: !0, force: !0 }), new Error("No councilors found in cloned repository (no ABOUT.md files)");
176
182
  }
177
- return o.counsellors = s, await Q(o), e;
183
+ return r.councilors = n, await Z(r), e;
178
184
  }
179
- async function We(n, r = !1) {
180
- const t = await X(), e = t.counsellors ?? {}, o = e[n];
181
- if (!o)
182
- throw new Error(`Counsellor "${n}" is not registered`);
183
- r && o.source === "git" && v(o.path) && await F(o.path, { recursive: !0, force: !0 }), delete e[n], t.counsellors = e, await Q(t);
185
+ async function ze(o, s = !1) {
186
+ const t = await Q(), e = t.councilors ?? {}, r = e[o];
187
+ if (!r)
188
+ throw new Error(`Councilor "${o}" is not registered`);
189
+ s && r.source === "git" && E(r.path) && await F(r.path, { recursive: !0, force: !0 }), delete e[o], t.councilors = e, await Z(t);
184
190
  }
185
- function Me(n) {
186
- const r = new $e({
187
- apiKey: n.apiKey ?? process.env.ANTHROPIC_API_KEY,
188
- ...n.baseUrl ? { baseURL: n.baseUrl } : {}
191
+ function Ve(o) {
192
+ const s = new Re({
193
+ apiKey: o.apiKey ?? process.env.ANTHROPIC_API_KEY,
194
+ ...o.baseUrl ? { baseURL: o.baseUrl } : {}
189
195
  });
190
196
  return {
191
197
  name: "anthropic",
192
198
  defaultModel: "claude-sonnet-4-5-20250929",
193
199
  async chat(t) {
194
- const e = await r.messages.create({
200
+ const e = await s.messages.create({
195
201
  model: t.model,
196
202
  max_tokens: 4096,
197
203
  system: t.systemPrompt,
198
- messages: t.messages.map((s) => ({
199
- role: s.role,
200
- content: s.content
204
+ messages: t.messages.map((n) => ({
205
+ role: n.role,
206
+ content: n.content
201
207
  })),
202
208
  ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
203
209
  });
204
210
  return {
205
- content: e.content.find((s) => s.type === "text")?.text ?? "",
211
+ content: e.content.find((n) => n.type === "text")?.text ?? "",
206
212
  tokenUsage: {
207
213
  input: e.usage.input_tokens,
208
214
  output: e.usage.output_tokens
@@ -210,45 +216,45 @@ function Me(n) {
210
216
  };
211
217
  },
212
218
  async *chatStream(t) {
213
- const e = r.messages.stream({
219
+ const e = s.messages.stream({
214
220
  model: t.model,
215
221
  max_tokens: 4096,
216
222
  system: t.systemPrompt,
217
- messages: t.messages.map((s) => ({
218
- role: s.role,
219
- content: s.content
223
+ messages: t.messages.map((n) => ({
224
+ role: n.role,
225
+ content: n.content
220
226
  })),
221
227
  ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
222
228
  });
223
- for await (const s of e)
224
- s.type === "content_block_delta" && s.delta.type === "text_delta" && (yield { delta: s.delta.text });
225
- const o = await e.finalMessage();
229
+ for await (const n of e)
230
+ n.type === "content_block_delta" && n.delta.type === "text_delta" && (yield { delta: n.delta.text });
231
+ const r = await e.finalMessage();
226
232
  yield {
227
233
  delta: "",
228
234
  tokenUsage: {
229
- input: o.usage.input_tokens,
230
- output: o.usage.output_tokens
235
+ input: r.usage.input_tokens,
236
+ output: r.usage.output_tokens
231
237
  }
232
238
  };
233
239
  }
234
240
  };
235
241
  }
236
- function Ye(n) {
237
- const r = new Ce({
238
- apiKey: n.apiKey ?? process.env.OPENAI_API_KEY,
239
- ...n.baseUrl ? { baseURL: n.baseUrl } : {}
242
+ function Xe(o) {
243
+ const s = new Ue({
244
+ apiKey: o.apiKey ?? process.env.OPENAI_API_KEY,
245
+ ...o.baseUrl ? { baseURL: o.baseUrl } : {}
240
246
  });
241
247
  return {
242
248
  name: "openai",
243
249
  defaultModel: "gpt-4o",
244
250
  async chat(t) {
245
- const e = await r.chat.completions.create({
251
+ const e = await s.chat.completions.create({
246
252
  model: t.model,
247
253
  messages: [
248
254
  { role: "system", content: t.systemPrompt },
249
- ...t.messages.map((s) => ({
250
- role: s.role,
251
- content: s.content
255
+ ...t.messages.map((n) => ({
256
+ role: n.role,
257
+ content: n.content
252
258
  }))
253
259
  ],
254
260
  ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
@@ -259,48 +265,48 @@ function Ye(n) {
259
265
  };
260
266
  },
261
267
  async *chatStream(t) {
262
- const e = await r.chat.completions.create({
268
+ const e = await s.chat.completions.create({
263
269
  model: t.model,
264
270
  messages: [
265
271
  { role: "system", content: t.systemPrompt },
266
- ...t.messages.map((o) => ({
267
- role: o.role,
268
- content: o.content
272
+ ...t.messages.map((r) => ({
273
+ role: r.role,
274
+ content: r.content
269
275
  }))
270
276
  ],
271
277
  ...t.temperature !== void 0 ? { temperature: t.temperature } : {},
272
278
  stream: !0,
273
279
  stream_options: { include_usage: !0 }
274
280
  });
275
- for await (const o of e) {
276
- const s = o.choices[0]?.delta?.content;
277
- s && (yield { delta: s }), o.usage && (yield {
281
+ for await (const r of e) {
282
+ const n = r.choices[0]?.delta?.content;
283
+ n && (yield { delta: n }), r.usage && (yield {
278
284
  delta: "",
279
285
  tokenUsage: {
280
- input: o.usage.prompt_tokens,
281
- output: o.usage.completion_tokens
286
+ input: r.usage.prompt_tokens,
287
+ output: r.usage.completion_tokens
282
288
  }
283
289
  });
284
290
  }
285
291
  }
286
292
  };
287
293
  }
288
- function He(n) {
289
- const r = n.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new Ne(r);
294
+ function Qe(o) {
295
+ const s = o.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new Te(s);
290
296
  return {
291
297
  name: "google",
292
298
  defaultModel: "gemini-2.0-flash",
293
299
  async chat(e) {
294
- const o = t.getGenerativeModel({
300
+ const r = t.getGenerativeModel({
295
301
  model: e.model,
296
302
  systemInstruction: e.systemPrompt,
297
303
  generationConfig: {
298
304
  ...e.temperature !== void 0 ? { temperature: e.temperature } : {}
299
305
  }
300
- }), s = e.messages.slice(0, -1).map((m) => ({
306
+ }), n = e.messages.slice(0, -1).map((m) => ({
301
307
  role: m.role === "assistant" ? "model" : "user",
302
308
  parts: [{ text: m.content }]
303
- })), a = o.startChat({ history: s }), i = e.messages[e.messages.length - 1], c = (await a.sendMessage(i?.content ?? "")).response;
309
+ })), a = r.startChat({ history: n }), i = e.messages[e.messages.length - 1], c = (await a.sendMessage(i?.content ?? "")).response;
304
310
  return {
305
311
  content: c.text(),
306
312
  tokenUsage: c.usageMetadata ? {
@@ -310,21 +316,21 @@ function He(n) {
310
316
  };
311
317
  },
312
318
  async *chatStream(e) {
313
- const o = t.getGenerativeModel({
319
+ const r = t.getGenerativeModel({
314
320
  model: e.model,
315
321
  systemInstruction: e.systemPrompt,
316
322
  generationConfig: {
317
323
  ...e.temperature !== void 0 ? { temperature: e.temperature } : {}
318
324
  }
319
- }), s = e.messages.slice(0, -1).map((m) => ({
325
+ }), n = e.messages.slice(0, -1).map((m) => ({
320
326
  role: m.role === "assistant" ? "model" : "user",
321
327
  parts: [{ text: m.content }]
322
- })), a = o.startChat({ history: s }), i = e.messages[e.messages.length - 1], l = await a.sendMessageStream(i?.content ?? "");
323
- for await (const m of l.stream) {
324
- const g = m.text();
325
- g && (yield { delta: g });
328
+ })), a = r.startChat({ history: n }), i = e.messages[e.messages.length - 1], u = await a.sendMessageStream(i?.content ?? "");
329
+ for await (const m of u.stream) {
330
+ const y = m.text();
331
+ y && (yield { delta: y });
326
332
  }
327
- const c = await l.response;
333
+ const c = await u.response;
328
334
  yield {
329
335
  delta: "",
330
336
  tokenUsage: c.usageMetadata ? {
@@ -335,21 +341,21 @@ function He(n) {
335
341
  }
336
342
  };
337
343
  }
338
- function Je(n) {
339
- const r = new xe({
340
- host: n.baseUrl ?? "http://localhost:11434"
344
+ function Ze(o) {
345
+ const s = new Le({
346
+ host: o.baseUrl ?? "http://localhost:11434"
341
347
  });
342
348
  return {
343
349
  name: "ollama",
344
350
  defaultModel: "llama3.2",
345
351
  async chat(t) {
346
- const e = await r.chat({
352
+ const e = await s.chat({
347
353
  model: t.model,
348
354
  messages: [
349
355
  { role: "system", content: t.systemPrompt },
350
- ...t.messages.map((o) => ({
351
- role: o.role,
352
- content: o.content
356
+ ...t.messages.map((r) => ({
357
+ role: r.role,
358
+ content: r.content
353
359
  }))
354
360
  ],
355
361
  options: {
@@ -365,7 +371,7 @@ function Je(n) {
365
371
  };
366
372
  },
367
373
  async *chatStream(t) {
368
- const e = await r.chat({
374
+ const e = await s.chat({
369
375
  model: t.model,
370
376
  messages: [
371
377
  { role: "system", content: t.systemPrompt },
@@ -379,59 +385,59 @@ function Je(n) {
379
385
  },
380
386
  stream: !0
381
387
  });
382
- let o, s;
388
+ let r, n;
383
389
  for await (const a of e)
384
- a.message.content && (yield { delta: a.message.content }), a.done && (o = a.prompt_eval_count, s = a.eval_count);
390
+ a.message.content && (yield { delta: a.message.content }), a.done && (r = a.prompt_eval_count, n = a.eval_count);
385
391
  yield {
386
392
  delta: "",
387
- tokenUsage: o !== void 0 ? { input: o ?? 0, output: s ?? 0 } : void 0
393
+ tokenUsage: r !== void 0 ? { input: r ?? 0, output: n ?? 0 } : void 0
388
394
  };
389
395
  }
390
396
  };
391
397
  }
392
- const ae = {
393
- anthropic: Me,
394
- openai: Ye,
395
- google: He,
396
- ollama: Je
398
+ const ie = {
399
+ anthropic: Ve,
400
+ openai: Xe,
401
+ google: Qe,
402
+ ollama: Ze
397
403
  };
398
404
  let T = null;
399
- async function ze() {
405
+ async function qe() {
400
406
  if (T) return T;
401
- const n = u(w(), ".ai-council", "config.json");
407
+ const o = d(v(), ".ai-council", "config.json");
402
408
  try {
403
- const r = await y(n, "utf-8");
404
- T = JSON.parse(r);
409
+ const s = await w(o, "utf-8");
410
+ T = JSON.parse(s);
405
411
  } catch {
406
412
  T = { backends: {} };
407
413
  }
408
414
  return T;
409
415
  }
410
- const J = /* @__PURE__ */ new Map();
411
- function Ve() {
412
- T = null, J.clear();
416
+ const z = /* @__PURE__ */ new Map();
417
+ function et() {
418
+ T = null, z.clear();
413
419
  }
414
- async function W(n) {
415
- const r = J.get(n);
416
- if (r) return r;
417
- const t = ae[n];
420
+ async function W(o) {
421
+ const s = z.get(o);
422
+ if (s) return s;
423
+ const t = ie[o];
418
424
  if (!t)
419
- throw new Error(`Unknown backend: "${n}". Available: ${Object.keys(ae).join(", ")}`);
420
- const o = (await ze()).backends[n] ?? {}, s = t(o);
421
- return J.set(n, s), s;
425
+ throw new Error(`Unknown backend: "${o}". Available: ${Object.keys(ie).join(", ")}`);
426
+ const r = (await qe()).backends[o] ?? {}, n = t(r);
427
+ return z.set(o, n), n;
422
428
  }
423
- function Xe() {
424
- return '\n## Excalidraw Element Reference\n\nOutput a JSON array of Excalidraw elements. Each element needs these fields:\n\n### Common Fields (all elements)\n- `type`: "rectangle" | "ellipse" | "diamond" | "text" | "arrow" | "line"\n- `id`: unique string (e.g. "rect1", "text1", "arrow1")\n- `x`, `y`: number (top-left origin, x increases right, y increases down)\n- `width`, `height`: number\n- `strokeColor`: hex string (e.g. "#1e1e1e")\n- `backgroundColor`: hex string or "transparent"\n- `fillStyle`: "solid" | "hachure" | "cross-hatch"\n- `strokeWidth`: 1 | 2 | 4\n- `roughness`: 0 (sharp) | 1 (sketchy)\n- `opacity`: 100\n- `angle`: 0\n- `seed`: any integer (e.g. 1)\n- `version`: 1\n- `isDeleted`: false\n- `groupIds`: []\n- `boundElements`: null or array of { id: string, type: "text" | "arrow" }\n- `link`: null\n- `locked`: false\n\n### Text Elements\nAdditional fields: `text`, `fontSize` (16-24), `fontFamily` (1=hand, 2=normal, 3=mono), `textAlign` ("left"|"center"|"right"), `verticalAlign` ("top"|"middle"), `baseline`: 0, `containerId`: null or parent shape id\n\n### Arrow/Line Elements\nAdditional fields: `points` (array of [x,y] relative to element x,y — first point always [0,0]), `startBinding` and `endBinding`: null or { elementId: string, focus: 0, gap: 5 }, `lastCommittedPoint`: null, `startArrowhead`: null, `endArrowhead`: "arrow" | null\n\n### Color Palette\n- Blue: "#1971c2", Light blue bg: "#a5d8ff"\n- Green: "#2f9e44", Light green bg: "#b2f2bb"\n- Red: "#e03131", Light red bg: "#ffc9c9"\n- Orange: "#e8590c", Light orange bg: "#ffd8a8"\n- Purple: "#7048e8", Light purple bg: "#d0bfff"\n- Yellow: "#f08c00", Light yellow bg: "#ffec99"\n- Gray: "#868e96", Light gray bg: "#dee2e6"\n- Dark: "#1e1e1e"\n\n### Layout Tips\n- Space shapes ~200px apart horizontally, ~150px vertically\n- Typical shape size: 160×80 for rectangles, 120×60 for ellipses\n- Center text inside shapes using containerId\n- Use arrows to show relationships (agreement, disagreement, influence)\n\n### Compact Example\n```json\n[\n {"type":"rectangle","id":"r1","x":50,"y":50,"width":160,"height":80,"strokeColor":"#1971c2","backgroundColor":"#a5d8ff","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":1,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t1","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},\n {"type":"text","id":"t1","x":60,"y":70,"width":140,"height":40,"text":"Counsellor A","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r1","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":2,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},\n {"type":"rectangle","id":"r2","x":350,"y":50,"width":160,"height":80,"strokeColor":"#2f9e44","backgroundColor":"#b2f2bb","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":3,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t2","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},\n {"type":"text","id":"t2","x":360,"y":70,"width":140,"height":40,"text":"Counsellor B","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r2","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":4,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},\n {"type":"arrow","id":"a1","x":210,"y":90,"width":140,"height":0,"points":[[0,0],[140,0]],"startBinding":{"elementId":"r1","focus":0,"gap":5},"endBinding":{"elementId":"r2","focus":0,"gap":5},"startArrowhead":null,"endArrowhead":"arrow","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":5,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false,"lastCommittedPoint":null}\n]\n```\n'.trim();
429
+ function tt() {
430
+ return '\n## Excalidraw Element Reference\n\nOutput a JSON array of Excalidraw elements. Each element needs these fields:\n\n### Common Fields (all elements)\n- `type`: "rectangle" | "ellipse" | "diamond" | "text" | "arrow" | "line"\n- `id`: unique string (e.g. "rect1", "text1", "arrow1")\n- `x`, `y`: number (top-left origin, x increases right, y increases down)\n- `width`, `height`: number\n- `strokeColor`: hex string (e.g. "#1e1e1e")\n- `backgroundColor`: hex string or "transparent"\n- `fillStyle`: "solid" | "hachure" | "cross-hatch"\n- `strokeWidth`: 1 | 2 | 4\n- `roughness`: 0 (sharp) | 1 (sketchy)\n- `opacity`: 100\n- `angle`: 0\n- `seed`: any integer (e.g. 1)\n- `version`: 1\n- `isDeleted`: false\n- `groupIds`: []\n- `boundElements`: null or array of { id: string, type: "text" | "arrow" }\n- `link`: null\n- `locked`: false\n\n### Text Elements\nAdditional fields: `text`, `fontSize` (16-24), `fontFamily` (1=hand, 2=normal, 3=mono), `textAlign` ("left"|"center"|"right"), `verticalAlign` ("top"|"middle"), `baseline`: 0, `containerId`: null or parent shape id\n\n### Arrow/Line Elements\nAdditional fields: `points` (array of [x,y] relative to element x,y — first point always [0,0]), `startBinding` and `endBinding`: null or { elementId: string, focus: 0, gap: 5 }, `lastCommittedPoint`: null, `startArrowhead`: null, `endArrowhead`: "arrow" | null\n\n### Color Palette\n- Blue: "#1971c2", Light blue bg: "#a5d8ff"\n- Green: "#2f9e44", Light green bg: "#b2f2bb"\n- Red: "#e03131", Light red bg: "#ffc9c9"\n- Orange: "#e8590c", Light orange bg: "#ffd8a8"\n- Purple: "#7048e8", Light purple bg: "#d0bfff"\n- Yellow: "#f08c00", Light yellow bg: "#ffec99"\n- Gray: "#868e96", Light gray bg: "#dee2e6"\n- Dark: "#1e1e1e"\n\n### Layout Tips\n- Space shapes ~200px apart horizontally, ~150px vertically\n- Typical shape size: 160×80 for rectangles, 120×60 for ellipses\n- Center text inside shapes using containerId\n- Use arrows to show relationships (agreement, disagreement, influence)\n\n### Compact Example\n```json\n[\n {"type":"rectangle","id":"r1","x":50,"y":50,"width":160,"height":80,"strokeColor":"#1971c2","backgroundColor":"#a5d8ff","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":1,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t1","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},\n {"type":"text","id":"t1","x":60,"y":70,"width":140,"height":40,"text":"Councilor A","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r1","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":2,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},\n {"type":"rectangle","id":"r2","x":350,"y":50,"width":160,"height":80,"strokeColor":"#2f9e44","backgroundColor":"#b2f2bb","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":3,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t2","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},\n {"type":"text","id":"t2","x":360,"y":70,"width":140,"height":40,"text":"Councilor B","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r2","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":4,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},\n {"type":"arrow","id":"a1","x":210,"y":90,"width":140,"height":0,"points":[[0,0],[140,0]],"startBinding":{"elementId":"r1","focus":0,"gap":5},"endBinding":{"elementId":"r2","focus":0,"gap":5},"startArrowhead":null,"endArrowhead":"arrow","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":5,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false,"lastCommittedPoint":null}\n]\n```\n'.trim();
425
431
  }
426
- const M = "---EXCALIDRAW---", Qe = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
432
+ const M = "---EXCALIDRAW---", nt = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
427
433
 
428
434
  Structure your summary with these sections:
429
435
 
430
436
  ## Individual Positions
431
- Briefly summarize each counsellor's key arguments and stance.
437
+ Briefly summarize each councilor's key arguments and stance.
432
438
 
433
439
  ## Points of Convergence
434
- Where did the counsellors agree? What common ground emerged?
440
+ Where did the councilors agree? What common ground emerged?
435
441
 
436
442
  ## Points of Divergence
437
443
  Where did they disagree? What are the key tensions?
@@ -440,29 +446,29 @@ Where did they disagree? What are the key tensions?
440
446
  What are the most important takeaways? What would you recommend based on the full discussion?
441
447
 
442
448
  Be concise but thorough. Use markdown formatting.`;
443
- function Ze(n) {
444
- const r = [];
445
- r.push(`Topic: ${n.topic}`), r.push(`Counsellors: ${n.counsellors.map((e) => e.name).join(", ")}`), r.push(`Rounds: ${n.rounds}`), r.push("");
449
+ function ot(o) {
450
+ const s = [];
451
+ s.push(`Topic: ${o.topic}`), s.push(`Councilors: ${o.councilors.map((e) => e.name).join(", ")}`), s.push(`Rounds: ${o.rounds}`), s.push("");
446
452
  let t = 0;
447
- for (const e of n.turns)
448
- e.round !== t && (t = e.round, r.push(`--- Round ${t} ---`), r.push("")), r.push(`[${e.counsellorName}]:`), r.push(e.content), r.push("");
449
- return r.join(`
453
+ for (const e of o.turns)
454
+ e.round !== t && (t = e.round, s.push(`--- Round ${t} ---`), s.push("")), s.push(`[${e.councilorName}]:`), s.push(e.content), s.push("");
455
+ return s.join(`
450
456
  `);
451
457
  }
452
- async function qe({
453
- result: n,
454
- config: r,
458
+ async function rt({
459
+ result: o,
460
+ config: s,
455
461
  onChunk: t,
456
462
  signal: e
457
463
  }) {
458
- const o = r.secretary;
459
- if (!o?.backend)
464
+ const r = s.secretary;
465
+ if (!r?.backend)
460
466
  throw new Error("No secretary backend configured");
461
- const s = await W(o.backend), a = o.model ?? s.defaultModel, i = o.systemPrompt || Qe, l = Xe(), c = `${i}
467
+ const n = await W(r.backend), a = r.model ?? n.defaultModel, i = r.systemPrompt || nt, u = tt(), c = `${i}
462
468
 
463
- ${l}
469
+ ${u}
464
470
 
465
- After your text summary, output \`${M}\` on its own line, then a JSON array of Excalidraw elements showing a visual map of where each counsellor stands on the topic. Use shapes for each counsellor with their name, arrows to show relationships (agreement/disagreement), and position them to visually represent the discussion dynamics.`, m = Ze(n), g = {
471
+ After your text summary, output \`${M}\` on its own line, then a JSON array of Excalidraw elements showing a visual map of where each councilor stands on the topic. Use shapes for each councilor with their name, arrows to show relationships (agreement/disagreement), and position them to visually represent the discussion dynamics.`, m = ot(o), y = {
466
472
  model: a,
467
473
  systemPrompt: c,
468
474
  messages: [{ role: "user", content: `Please summarize this council discussion and create a position diagram:
@@ -470,78 +476,78 @@ After your text summary, output \`${M}\` on its own line, then a JSON array of E
470
476
  ${m}` }],
471
477
  temperature: 0.5
472
478
  };
473
- let p = "";
474
- if (s.chatStream)
475
- for await (const E of s.chatStream(g)) {
479
+ let h = "";
480
+ if (n.chatStream)
481
+ for await (const f of n.chatStream(y)) {
476
482
  if (e?.aborted) break;
477
- p += E.delta, E.delta && t && t(E.delta);
483
+ h += f.delta, f.delta && t && t(f.delta);
478
484
  }
479
485
  else
480
- p = (await s.chat(g)).content, t && t(p);
481
- const f = p.indexOf(M);
482
- if (f === -1)
483
- return { text: p.trim() };
484
- const d = p.slice(0, f).trim(), h = p.slice(f + M.length).trim();
485
- let b;
486
+ h = (await n.chat(y)).content, t && t(h);
487
+ const p = h.indexOf(M);
488
+ if (p === -1)
489
+ return { text: h.trim() };
490
+ const b = h.slice(0, p).trim(), l = h.slice(p + M.length).trim();
491
+ let g;
486
492
  try {
487
- const E = h.match(/\[[\s\S]*\]/);
488
- E && (b = JSON.parse(E[0]));
493
+ const f = l.match(/\[[\s\S]*\]/);
494
+ f && (g = JSON.parse(f[0]));
489
495
  } catch {
490
496
  }
491
- return { text: d, diagram: b };
497
+ return { text: b, diagram: g };
492
498
  }
493
- const et = "You are the Secretary of a council debate. Briefly summarize this round of discussion. Note emerging agreements, disagreements, and shifts in position. 2-3 paragraphs max. Use markdown formatting.";
494
- async function tt({
495
- result: n,
496
- roundNumber: r,
499
+ const st = "You are the Secretary of a council debate. Briefly summarize this round of discussion. Note emerging agreements, disagreements, and shifts in position. 2-3 paragraphs max. Use markdown formatting.";
500
+ async function at({
501
+ result: o,
502
+ roundNumber: s,
497
503
  config: t,
498
504
  onChunk: e,
499
- signal: o
505
+ signal: r
500
506
  }) {
501
- const s = t.secretary;
502
- if (!s?.backend)
507
+ const n = t.secretary;
508
+ if (!n?.backend)
503
509
  throw new Error("No secretary backend configured");
504
- const a = await W(s.backend), i = s.model ?? a.defaultModel, l = n.turns.filter((p) => p.round === r), c = [];
505
- c.push(`Topic: ${n.topic}`), c.push(`Round ${r}${r === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
506
- for (const p of l)
507
- c.push(`[${p.counsellorName}]:`), c.push(p.content), c.push("");
510
+ const a = await W(n.backend), i = n.model ?? a.defaultModel, u = o.turns.filter((h) => h.round === s), c = [];
511
+ c.push(`Topic: ${o.topic}`), c.push(`Round ${s}${s === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
512
+ for (const h of u)
513
+ c.push(`[${h.councilorName}]:`), c.push(h.content), c.push("");
508
514
  const m = {
509
515
  model: i,
510
- systemPrompt: et,
516
+ systemPrompt: st,
511
517
  messages: [{ role: "user", content: `Please summarize this round:
512
518
 
513
519
  ${c.join(`
514
520
  `)}` }],
515
521
  temperature: 0.5
516
522
  };
517
- let g = "";
523
+ let y = "";
518
524
  if (a.chatStream)
519
- for await (const p of a.chatStream(m)) {
520
- if (o?.aborted) break;
521
- g += p.delta, p.delta && e && e(p.delta);
525
+ for await (const h of a.chatStream(m)) {
526
+ if (r?.aborted) break;
527
+ y += h.delta, h.delta && e && e(h.delta);
522
528
  }
523
529
  else
524
- g = (await a.chat(m)).content, e && e(g);
525
- return g.trim();
530
+ y = (await a.chat(m)).content, e && e(y);
531
+ return y.trim();
526
532
  }
527
- async function nt({
528
- topic: n,
529
- firstRoundTurns: r,
533
+ async function it({
534
+ topic: o,
535
+ firstRoundTurns: s,
530
536
  config: t
531
537
  }) {
532
538
  const e = t.secretary;
533
539
  if (!e?.backend)
534
540
  throw new Error("No secretary backend configured");
535
- const o = await W(e.backend), s = e.model ?? o.defaultModel, a = r.map((l) => `[${l.counsellorName}]: ${l.content.slice(0, 300)}`).join(`
541
+ const r = await W(e.backend), n = e.model ?? r.defaultModel, a = s.map((u) => `[${u.councilorName}]: ${u.content.slice(0, 300)}`).join(`
536
542
 
537
543
  `);
538
- return (await o.chat({
539
- model: s,
544
+ return (await r.chat({
545
+ model: n,
540
546
  systemPrompt: "Generate a concise title (max 8 words) for this council discussion. Return only the title, no quotes or punctuation at the end.",
541
547
  messages: [
542
548
  {
543
549
  role: "user",
544
- content: `Topic: ${n}
550
+ content: `Topic: ${o}
545
551
 
546
552
  First round:
547
553
  ${a}`
@@ -550,85 +556,96 @@ ${a}`
550
556
  temperature: 0.3
551
557
  })).content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
552
558
  }
553
- const ge = u(w(), ".ai-council"), ot = u(ge, "council.log");
554
- let ie = !1;
555
- async function st() {
556
- ie || (await N(ge, { recursive: !0 }), ie = !0);
559
+ const ye = d(v(), ".ai-council"), ct = d(ye, "council.log");
560
+ let ce = !1;
561
+ async function lt() {
562
+ ce || (await N(ye, { recursive: !0 }), ce = !0);
557
563
  }
558
- function rt(n, r, t, e) {
559
- let s = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${n} [${r}] ${t}`;
564
+ function ut(o, s, t, e) {
565
+ let n = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${o} [${s}] ${t}`;
560
566
  if (e !== void 0) {
561
567
  const a = e instanceof Error ? `${e.message}
562
568
  ${e.stack ?? ""}` : typeof e == "string" ? e : JSON.stringify(e, null, 2);
563
- s += `
569
+ n += `
564
570
  ${a.replace(/\n/g, `
565
571
  `)}`;
566
572
  }
567
- return s + `
573
+ return n + `
568
574
  `;
569
575
  }
570
- async function Y(n, r, t, e) {
576
+ async function Y(o, s, t, e) {
571
577
  try {
572
- await st(), await Pe(ot, rt(n, r, t, e));
578
+ await lt(), await xe(ct, ut(o, s, t, e));
573
579
  } catch {
574
580
  }
575
581
  }
576
- const O = {
577
- info: (n, r, t) => Y("INFO", n, r, t),
578
- warn: (n, r, t) => Y("WARN", n, r, t),
579
- error: (n, r, t) => Y("ERROR", n, r, t)
582
+ const _ = {
583
+ info: (o, s, t) => Y("INFO", o, s, t),
584
+ warn: (o, s, t) => Y("WARN", o, s, t),
585
+ error: (o, s, t) => Y("ERROR", o, s, t)
580
586
  };
581
- function at(n, r, t) {
582
- const e = [{ role: "user", content: n }];
583
- for (const o of r)
584
- o.counsellorId === t ? e.push({ role: "assistant", content: o.content }) : e.push({
587
+ function dt(o, s, t, e, r) {
588
+ const n = [{ role: "user", content: o }];
589
+ if (e?.length) {
590
+ for (const a of e)
591
+ a.councilorId === t ? n.push({ role: "assistant", content: a.content }) : n.push({
592
+ role: "user",
593
+ content: `[${a.councilorName}, Round ${a.round}]: ${a.content}`
594
+ });
595
+ r && n.push({
585
596
  role: "user",
586
- content: `[${o.counsellorName}, Round ${o.round}]: ${o.content}`
597
+ content: `[Secretary Summary]: ${r}`
587
598
  });
588
- return e;
599
+ }
600
+ for (const a of s)
601
+ a.councilorId === t ? n.push({ role: "assistant", content: a.content }) : n.push({
602
+ role: "user",
603
+ content: `[${a.councilorName}, Round ${a.round}]: ${a.content}`
604
+ });
605
+ return n;
589
606
  }
590
- function it(n, r, t, e) {
591
- const o = [{ role: "user", content: n }];
607
+ function mt(o, s, t, e) {
608
+ const r = [{ role: "user", content: o }];
592
609
  if (e === 1)
593
- return o;
594
- const s = r.filter((i) => i.round === 1);
595
- for (const i of s)
596
- i.counsellorId === t ? o.push({ role: "assistant", content: i.content }) : o.push({
610
+ return r;
611
+ const n = s.filter((i) => i.round === 1);
612
+ for (const i of n)
613
+ i.councilorId === t ? r.push({ role: "assistant", content: i.content }) : r.push({
597
614
  role: "user",
598
- content: `[${i.counsellorName}, Constructive]: ${i.content}`
615
+ content: `[${i.councilorName}, Constructive]: ${i.content}`
599
616
  });
600
617
  const a = e - 1;
601
618
  if (a > 1) {
602
- const i = r.filter((l) => l.round === a);
603
- for (const l of i)
604
- l.counsellorId === t ? o.push({ role: "assistant", content: l.content }) : o.push({
619
+ const i = s.filter((u) => u.round === a);
620
+ for (const u of i)
621
+ u.councilorId === t ? r.push({ role: "assistant", content: u.content }) : r.push({
605
622
  role: "user",
606
- content: `[${l.counsellorName}, Round ${a}]: ${l.content}`
623
+ content: `[${u.councilorName}, Round ${a}]: ${u.content}`
607
624
  });
608
625
  }
609
- for (const i of r)
610
- i.counsellorId === t && i.round !== 1 && i.round !== a && i.round < e && o.push({ role: "assistant", content: i.content });
611
- return o;
626
+ for (const i of s)
627
+ i.councilorId === t && i.round !== 1 && i.round !== a && i.round < e && r.push({ role: "assistant", content: i.content });
628
+ return r;
612
629
  }
613
- function ct(n, r) {
614
- const t = [...n];
615
- let e = r | 0;
616
- const o = () => {
630
+ function pt(o, s) {
631
+ const t = [...o];
632
+ let e = s | 0;
633
+ const r = () => {
617
634
  e = e + 1831565813 | 0;
618
- let s = Math.imul(e ^ e >>> 15, 1 | e);
619
- return s = s + Math.imul(s ^ s >>> 7, 61 | s) ^ s, ((s ^ s >>> 14) >>> 0) / 4294967296;
635
+ let n = Math.imul(e ^ e >>> 15, 1 | e);
636
+ return n = n + Math.imul(n ^ n >>> 7, 61 | n) ^ n, ((n ^ n >>> 14) >>> 0) / 4294967296;
620
637
  };
621
- for (let s = t.length - 1; s > 0; s--) {
622
- const a = Math.floor(o() * (s + 1));
623
- [t[s], t[a]] = [t[a], t[s]];
638
+ for (let n = t.length - 1; n > 0; n--) {
639
+ const a = Math.floor(r() * (n + 1));
640
+ [t[n], t[a]] = [t[a], t[n]];
624
641
  }
625
642
  return t;
626
643
  }
627
- function H(n, r, t, e, o, s) {
644
+ function H(o, s, t, e, r, n) {
628
645
  return {
629
- topic: n.topic,
630
- topicSource: n.topicSource,
631
- counsellors: n.counsellors.map((a) => ({
646
+ topic: o.topic,
647
+ topicSource: o.topicSource,
648
+ councilors: o.councilors.map((a) => ({
632
649
  id: a.id,
633
650
  name: a.frontmatter.name,
634
651
  description: a.frontmatter.description,
@@ -636,178 +653,179 @@ function H(n, r, t, e, o, s) {
636
653
  model: a.frontmatter.model ?? "default",
637
654
  avatarUrl: a.avatarUrl
638
655
  })),
639
- rounds: n.rounds,
640
- turns: r,
656
+ rounds: o.rounds,
657
+ turns: s,
641
658
  startedAt: t,
642
659
  completedAt: (/* @__PURE__ */ new Date()).toISOString(),
643
- totalTokenUsage: { input: e, output: o },
644
- ...s && Object.keys(s).length > 0 ? { roundSummaries: s } : {},
645
- ...n.mode === "debate" ? { mode: "debate" } : {}
660
+ totalTokenUsage: { input: e, output: r },
661
+ ...n && Object.keys(n).length > 0 ? { roundSummaries: n } : {},
662
+ ...o.mode === "debate" ? { mode: "debate" } : {}
646
663
  };
647
664
  }
648
- async function lt(n, r, t, e, o) {
649
- let s;
650
- typeof n == "string" ? s = {
651
- topic: n,
652
- topicSource: r,
653
- counsellors: t,
665
+ async function ft(o, s, t, e, r) {
666
+ let n;
667
+ typeof o == "string" ? n = {
668
+ topic: o,
669
+ topicSource: s,
670
+ councilors: t,
654
671
  rounds: e,
655
- onEvent: o
656
- } : s = n;
672
+ onEvent: r
673
+ } : n = o;
657
674
  const a = (/* @__PURE__ */ new Date()).toISOString(), i = [];
658
- let l = 0, c = 0;
659
- const m = s.mode === "debate", g = {};
660
- O.info("conversation", `Starting ${m ? "debate" : "freeform"} — ${s.counsellors.length} counsellors, ${s.rounds} rounds`, {
661
- counsellors: s.counsellors.map((p) => `${p.frontmatter.name} (${p.frontmatter.backend}/${p.frontmatter.model ?? "default"})`),
662
- topic: s.topic.slice(0, 200)
675
+ let u = 0, c = 0;
676
+ const m = n.mode === "debate", y = {}, h = n.previousTurns?.length ? Math.max(...n.previousTurns.map((p) => p.round)) : 0;
677
+ _.info("conversation", `Starting ${m ? "debate" : "freeform"} — ${n.councilors.length} councilors, ${n.rounds} rounds`, {
678
+ councilors: n.councilors.map((p) => `${p.frontmatter.name} (${p.frontmatter.backend}/${p.frontmatter.model ?? "default"})`),
679
+ topic: n.topic.slice(0, 200)
663
680
  });
664
- for (let p = 1; p <= s.rounds; p++) {
665
- const f = m && p > 1 ? ct(s.counsellors, p) : s.counsellors;
666
- for (const d of f) {
667
- if (s.signal?.aborted)
668
- return H(s, i, a, l, c, g);
669
- if (s.beforeTurn) {
670
- const h = await s.beforeTurn();
671
- h && (i.push(h), s.onEvent({ type: "turn_complete", turn: h }));
681
+ for (let p = 1; p <= n.rounds; p++) {
682
+ const b = m && p > 1 ? pt(n.councilors, p) : n.councilors;
683
+ for (const l of b) {
684
+ if (n.signal?.aborted)
685
+ return H(n, i, a, u, c, y);
686
+ if (n.beforeTurn) {
687
+ const f = await n.beforeTurn();
688
+ f && (i.push(f), n.onEvent({ type: "turn_complete", turn: f }));
672
689
  }
673
- s.onEvent({ type: "turn_start", round: p, counsellorName: d.frontmatter.name });
690
+ const g = p + h;
691
+ n.onEvent({ type: "turn_start", round: g, councilorName: l.frontmatter.name });
674
692
  try {
675
- const h = await W(d.frontmatter.backend), b = d.frontmatter.model ?? h.defaultModel, E = m ? it(s.topic, i, d.id, p) : at(s.topic, i, d.id), Z = {
676
- model: b,
677
- systemPrompt: d.systemPrompt,
678
- messages: E,
679
- temperature: d.frontmatter.temperature
693
+ const f = await W(l.frontmatter.backend), D = l.frontmatter.model ?? f.defaultModel, _e = m ? mt(n.topic, i, l.id, p) : dt(n.topic, i, l.id, n.previousTurns, n.previousSummary), q = {
694
+ model: D,
695
+ systemPrompt: l.systemPrompt,
696
+ messages: _e,
697
+ temperature: l.frontmatter.temperature
680
698
  };
681
- let D, x;
682
- if (h.chatStream) {
683
- D = "";
684
- for await (const S of h.chatStream(Z)) {
685
- if (s.signal?.aborted) break;
686
- D += S.delta, S.delta && s.onEvent({ type: "turn_chunk", counsellorName: d.frontmatter.name, delta: S.delta }), S.tokenUsage && (x = S.tokenUsage);
699
+ let K, x;
700
+ if (f.chatStream) {
701
+ K = "";
702
+ for await (const O of f.chatStream(q)) {
703
+ if (n.signal?.aborted) break;
704
+ K += O.delta, O.delta && n.onEvent({ type: "turn_chunk", councilorName: l.frontmatter.name, delta: O.delta }), O.tokenUsage && (x = O.tokenUsage);
687
705
  }
688
706
  } else {
689
- const S = await h.chat(Z);
690
- D = S.content, x = S.tokenUsage;
707
+ const O = await f.chat(q);
708
+ K = O.content, x = O.tokenUsage;
691
709
  }
692
- const q = {
693
- round: p,
694
- counsellorId: d.id,
695
- counsellorName: d.frontmatter.name,
696
- content: D,
710
+ const ee = {
711
+ round: g,
712
+ councilorId: l.id,
713
+ councilorName: l.frontmatter.name,
714
+ content: K,
697
715
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
698
- model: b,
699
- backend: d.frontmatter.backend,
716
+ model: D,
717
+ backend: l.frontmatter.backend,
700
718
  tokenUsage: x,
701
- avatarUrl: d.avatarUrl
719
+ avatarUrl: l.avatarUrl
702
720
  };
703
- x && (l += x.input, c += x.output), i.push(q), s.onEvent({ type: "turn_complete", turn: q });
704
- } catch (h) {
705
- const b = h instanceof Error ? h.message : String(h);
706
- O.error("conversation", `Turn failed for ${d.frontmatter.name} (round ${p}, model ${d.frontmatter.model ?? "default"}, backend ${d.frontmatter.backend})`, h), s.onEvent({ type: "error", counsellorName: d.frontmatter.name, error: b });
721
+ x && (u += x.input, c += x.output), i.push(ee), n.onEvent({ type: "turn_complete", turn: ee });
722
+ } catch (f) {
723
+ const D = f instanceof Error ? f.message : String(f);
724
+ _.error("conversation", `Turn failed for ${l.frontmatter.name} (round ${p}, model ${l.frontmatter.model ?? "default"}, backend ${l.frontmatter.backend})`, f), n.onEvent({ type: "error", councilorName: l.frontmatter.name, error: D });
707
725
  }
708
726
  }
709
- if (s.onEvent({ type: "round_complete", round: p }), m && s.config?.secretary?.backend && !s.signal?.aborted)
727
+ if (n.onEvent({ type: "round_complete", round: p }), m && n.config?.secretary?.backend && !n.signal?.aborted)
710
728
  try {
711
- const d = H(s, i, a, l, c, g);
712
- s.onEvent({ type: "round_summary_start", round: p });
713
- const h = await tt({
714
- result: d,
729
+ const l = H(n, i, a, u, c, y);
730
+ n.onEvent({ type: "round_summary_start", round: p });
731
+ const g = await at({
732
+ result: l,
715
733
  roundNumber: p,
716
- config: s.config,
717
- onChunk: (b) => {
718
- s.onEvent({ type: "round_summary_chunk", round: p, delta: b });
734
+ config: n.config,
735
+ onChunk: (f) => {
736
+ n.onEvent({ type: "round_summary_chunk", round: p, delta: f });
719
737
  },
720
- signal: s.signal
738
+ signal: n.signal
721
739
  });
722
- g[p] = h, s.onEvent({ type: "round_summary_complete", round: p, summary: h });
723
- } catch (d) {
724
- O.error("conversation", `Interim summary failed for round ${p}`, d);
740
+ y[p] = g, n.onEvent({ type: "round_summary_complete", round: p, summary: g });
741
+ } catch (l) {
742
+ _.error("conversation", `Interim summary failed for round ${p}`, l);
725
743
  }
726
744
  }
727
- return H(s, i, a, l, c, g);
745
+ return H(n, i, a, u, c, y);
728
746
  }
729
- const I = u(w(), ".ai-council", "history");
730
- function ut(n) {
731
- return n.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
747
+ const I = d(v(), ".ai-council", "history");
748
+ function ht(o) {
749
+ return o.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
732
750
  }
733
- async function dt(n) {
751
+ async function gt(o) {
734
752
  await N(I, { recursive: !0 });
735
- const r = new Date(n.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = ut(n.topic), e = `${r}-${t}`, o = u(I, `${e}.json`);
736
- return await $(o, JSON.stringify(n, null, 2), "utf-8"), e;
753
+ const s = new Date(o.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = ht(o.topic), e = `${s}-${t}`, r = d(I, `${e}.json`);
754
+ return await P(r, JSON.stringify(o, null, 2), "utf-8"), e;
737
755
  }
738
- async function mt() {
756
+ async function yt() {
739
757
  await N(I, { recursive: !0 });
740
- const n = await z(I), r = [];
741
- for (const t of n)
758
+ const o = await V(I), s = [];
759
+ for (const t of o)
742
760
  if (t.endsWith(".json"))
743
761
  try {
744
- const e = await y(u(I, t), "utf-8"), o = JSON.parse(e);
745
- r.push({
762
+ const e = await w(d(I, t), "utf-8"), r = JSON.parse(e);
763
+ s.push({
746
764
  id: C(t, ".json"),
747
- topic: o.topic,
748
- title: o.title,
749
- counsellors: o.counsellors.map((s) => s.name),
750
- rounds: o.rounds,
751
- startedAt: o.startedAt,
752
- completedAt: o.completedAt
765
+ topic: r.topic,
766
+ title: r.title,
767
+ councilors: r.councilors.map((n) => n.name),
768
+ rounds: r.rounds,
769
+ startedAt: r.startedAt,
770
+ completedAt: r.completedAt
753
771
  });
754
772
  } catch {
755
773
  }
756
- return r.sort((t, e) => e.startedAt.localeCompare(t.startedAt));
774
+ return s.sort((t, e) => e.startedAt.localeCompare(t.startedAt));
757
775
  }
758
- async function ce(n) {
759
- const r = u(I, `${n}.json`), t = await y(r, "utf-8"), e = JSON.parse(t);
776
+ async function le(o) {
777
+ const s = d(I, `${o}.json`), t = await w(s, "utf-8"), e = JSON.parse(t);
760
778
  return e.infographic && !e.infographics && (e.infographics = [e.infographic], delete e.infographic), e;
761
779
  }
762
- async function pt(n) {
763
- const r = u(I, `${n}.json`);
764
- await F(r);
780
+ async function wt(o) {
781
+ const s = d(I, `${o}.json`);
782
+ await F(s);
765
783
  }
766
- async function ft(n, r) {
767
- const t = u(I, `${n}.json`), e = await y(t, "utf-8"), o = JSON.parse(e);
768
- o.infographics || (o.infographics = []), o.infographics.push(r), await $(t, JSON.stringify(o, null, 2), "utf-8");
784
+ async function bt(o, s) {
785
+ const t = d(I, `${o}.json`), e = await w(t, "utf-8"), r = JSON.parse(e);
786
+ r.infographics || (r.infographics = []), r.infographics.push(s), await P(t, JSON.stringify(r, null, 2), "utf-8");
769
787
  }
770
- async function ht(n, r) {
771
- const t = u(I, `${n}.json`), e = await y(t, "utf-8"), o = JSON.parse(e);
772
- o.infographics && r >= 0 && r < o.infographics.length && o.infographics.splice(r, 1), await $(t, JSON.stringify(o, null, 2), "utf-8");
788
+ async function vt(o, s) {
789
+ const t = d(I, `${o}.json`), e = await w(t, "utf-8"), r = JSON.parse(e);
790
+ r.infographics && s >= 0 && s < r.infographics.length && r.infographics.splice(s, 1), await P(t, JSON.stringify(r, null, 2), "utf-8");
773
791
  }
774
- const ye = Ue(import.meta.url);
775
- function gt(n) {
776
- const r = n.counsellors.map((e) => e.name).join(", "), t = n.summary ?? n.turns.map((e) => `${e.counsellorName}: ${e.content.slice(0, 200)}`).join(`
792
+ const we = De(import.meta.url);
793
+ function kt(o) {
794
+ const s = o.councilors.map((e) => e.name).join(", "), t = o.summary ?? o.turns.map((e) => `${e.councilorName}: ${e.content.slice(0, 200)}`).join(`
777
795
  `);
778
796
  return [
779
797
  "Create a professional infographic summarizing a panel discussion.",
780
- `Topic: ${n.topic.slice(0, 300)}`,
798
+ `Topic: ${o.topic.slice(0, 300)}`,
781
799
  `Key points: ${t.slice(0, 1500)}`,
782
- `Panelists: ${r}`,
800
+ `Panelists: ${s}`,
783
801
  "Use a clean, modern design with sections for convergence points, divergence points, and key takeaways.",
784
802
  "Include relevant icons and visual hierarchy. Use a horizontal landscape layout."
785
803
  ].join(" ");
786
804
  }
787
- function yt(n) {
788
- if (n.infographic?.backend) return n.infographic.backend;
789
- const r = !!(n.backends.google?.apiKey || process.env.GOOGLE_API_KEY), t = !!(n.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
790
- return r ? "google" : t ? "openai" : null;
805
+ function _t(o) {
806
+ if (o.infographic?.backend) return o.infographic.backend;
807
+ const s = !!(o.backends.google?.apiKey || process.env.GOOGLE_API_KEY), t = !!(o.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
808
+ return s ? "google" : t ? "openai" : null;
791
809
  }
792
- async function wt(n, r) {
793
- const t = ye("openai").default, s = (await new t({
794
- apiKey: r.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
795
- ...r.backends.openai?.baseUrl ? { baseURL: r.backends.openai.baseUrl } : {}
810
+ async function Et(o, s) {
811
+ const t = we("openai").default, n = (await new t({
812
+ apiKey: s.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
813
+ ...s.backends.openai?.baseUrl ? { baseURL: s.backends.openai.baseUrl } : {}
796
814
  }).images.generate({
797
815
  model: "gpt-image-1",
798
- prompt: n,
816
+ prompt: o,
799
817
  quality: "high",
800
818
  size: "1536x1024"
801
819
  })).data?.[0]?.b64_json;
802
- if (!s) throw new Error("No image data returned from OpenAI");
803
- return s;
820
+ if (!n) throw new Error("No image data returned from OpenAI");
821
+ return n;
804
822
  }
805
- async function bt(n, r) {
806
- const { GoogleGenAI: t } = ye("@google/genai"), e = r.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
823
+ async function St(o, s) {
824
+ const { GoogleGenAI: t } = we("@google/genai"), e = s.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
807
825
  if (!e) throw new Error("No Google API key configured");
808
826
  const a = (await new t({ apiKey: e }).models.generateContent({
809
827
  model: "gemini-3-pro-image-preview",
810
- contents: n,
828
+ contents: o,
811
829
  config: {
812
830
  responseModalities: ["IMAGE", "TEXT"]
813
831
  }
@@ -818,24 +836,24 @@ async function bt(n, r) {
818
836
  return i.inlineData.data;
819
837
  throw new Error("No image data in Gemini response");
820
838
  }
821
- async function le(n, r, t) {
822
- const e = t ?? yt(r);
839
+ async function ue(o, s, t) {
840
+ const e = t ?? _t(s);
823
841
  if (!e) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
824
- const o = gt(n);
825
- return e === "openai" ? wt(o, r) : bt(o, r);
842
+ const r = kt(o);
843
+ return e === "openai" ? Et(r, s) : St(r, s);
826
844
  }
827
- const we = {
845
+ const be = {
828
846
  anthropic: "https://api.anthropic.com",
829
847
  openai: "https://api.openai.com/v1",
830
848
  google: "https://generativelanguage.googleapis.com",
831
849
  ollama: "http://localhost:11434"
832
- }, ue = [
850
+ }, de = [
833
851
  "claude-opus-4-20250514",
834
852
  "claude-sonnet-4-5-20250514",
835
853
  "claude-sonnet-4-20250514",
836
854
  "claude-haiku-4-20250414",
837
855
  "claude-3-5-haiku-20241022"
838
- ], K = [
856
+ ], j = [
839
857
  "gemini-2.5-pro",
840
858
  "gemini-2.5-flash",
841
859
  "gemini-2.0-flash",
@@ -843,67 +861,67 @@ const we = {
843
861
  "gemini-1.5-pro",
844
862
  "gemini-1.5-flash"
845
863
  ];
846
- async function kt(n, r) {
864
+ async function At(o, s) {
847
865
  try {
848
- switch (n) {
866
+ switch (o) {
849
867
  case "ollama": {
850
868
  const { Ollama: t } = await import("ollama");
851
- return { connected: !0, models: (await new t({ host: r.baseUrl || we.ollama }).list()).models.map((a) => a.name).sort() };
869
+ return { connected: !0, models: (await new t({ host: s.baseUrl || be.ollama }).list()).models.map((a) => a.name).sort() };
852
870
  }
853
871
  case "openai": {
854
872
  const { default: t } = await import("openai");
855
873
  return { connected: !0, models: (await new t({
856
- apiKey: r.apiKey || process.env.OPENAI_API_KEY,
857
- ...r.baseUrl ? { baseURL: r.baseUrl } : {}
874
+ apiKey: s.apiKey || process.env.OPENAI_API_KEY,
875
+ ...s.baseUrl ? { baseURL: s.baseUrl } : {}
858
876
  }).models.list()).data.map((a) => a.id).filter((a) => a.startsWith("gpt-") || a.startsWith("o") || a.startsWith("chatgpt-")).sort() };
859
877
  }
860
878
  case "anthropic": {
861
879
  const { default: t } = await import("@anthropic-ai/sdk"), e = new t({
862
- apiKey: r.apiKey || process.env.ANTHROPIC_API_KEY,
863
- ...r.baseUrl ? { baseURL: r.baseUrl } : {}
880
+ apiKey: s.apiKey || process.env.ANTHROPIC_API_KEY,
881
+ ...s.baseUrl ? { baseURL: s.baseUrl } : {}
864
882
  });
865
883
  try {
866
884
  return { connected: !0, models: (await e.models.list({ limit: 100 })).data.map((a) => a.id).sort() };
867
885
  } catch {
868
- return { connected: !0, models: ue };
886
+ return { connected: !0, models: de };
869
887
  }
870
888
  }
871
889
  case "google": {
872
- const t = r.apiKey || process.env.GOOGLE_API_KEY || "";
873
- if (!t) return { connected: !1, models: K, error: "No API key" };
890
+ const t = s.apiKey || process.env.GOOGLE_API_KEY || "";
891
+ if (!t) return { connected: !1, models: j, error: "No API key" };
874
892
  const e = await fetch(
875
893
  `https://generativelanguage.googleapis.com/v1beta/models?key=${t}`
876
894
  );
877
895
  if (!e.ok) {
878
896
  const i = (await e.json().catch(() => ({})))?.error?.message || `HTTP ${e.status}`;
879
- return { connected: !1, models: K, error: i };
897
+ return { connected: !1, models: j, error: i };
880
898
  }
881
- const s = ((await e.json()).models || []).filter((a) => a.name.includes("gemini") && a.supportedGenerationMethods?.includes("generateContent")).map((a) => a.name.replace("models/", "")).sort();
882
- return { connected: !0, models: s.length > 0 ? s : K };
899
+ const n = ((await e.json()).models || []).filter((a) => a.name.includes("gemini") && a.supportedGenerationMethods?.includes("generateContent")).map((a) => a.name.replace("models/", "")).sort();
900
+ return { connected: !0, models: n.length > 0 ? n : j };
883
901
  }
884
902
  default:
885
- return { connected: !1, models: [], error: `Unknown backend: ${n}` };
903
+ return { connected: !1, models: [], error: `Unknown backend: ${o}` };
886
904
  }
887
905
  } catch (t) {
888
906
  const e = t instanceof Error ? t.message : String(t);
889
- return { connected: !1, models: n === "anthropic" ? ue : n === "google" ? K : [], error: e };
907
+ return { connected: !1, models: o === "anthropic" ? de : o === "google" ? j : [], error: e };
890
908
  }
891
909
  }
892
- let A = null, j = [];
893
- function vt(n, r) {
894
- n.handle("app:getCouncilDir", async () => {
895
- const t = process.env.COUNCIL_CWD || w();
910
+ let A = null, B = [];
911
+ function It(o, s) {
912
+ o.handle("app:getCouncilDir", async () => {
913
+ const t = process.env.COUNCIL_CWD || process.cwd();
896
914
  return G(t, "council");
897
- }), n.handle("counsellors:list", async (t, e) => {
898
- const o = u(w(), ".ai-council", "config.json");
899
- let s = { backends: {} };
915
+ }), o.handle("councilors:list", async (t, e) => {
916
+ const r = d(v(), ".ai-council", "config.json");
917
+ let n = { backends: {} };
900
918
  try {
901
- const c = await y(o, "utf-8");
902
- s = JSON.parse(c);
919
+ const c = await w(r, "utf-8");
920
+ n = JSON.parse(c);
903
921
  } catch {
904
922
  }
905
- const a = re(s), i = s.counsellors ?? {};
906
- return (await oe(e, a)).map((c) => {
923
+ const a = ae(n), i = n.councilors ?? {};
924
+ return (await re(e, a)).map((c) => {
907
925
  const m = i[c.id];
908
926
  return {
909
927
  id: c.id,
@@ -919,144 +937,148 @@ function vt(n, r) {
919
937
  registryUrl: m?.url
920
938
  };
921
939
  });
922
- }), n.handle("counsellors:get", async (t, e) => {
923
- const o = u(e, "ABOUT.md"), s = await y(o, "utf-8"), { data: a, content: i } = V(s);
924
- return { frontmatter: a, body: i.trim(), raw: s };
925
- }), n.handle("counsellors:save", async (t, e, o) => {
926
- const s = u(e, "ABOUT.md");
927
- return await $(s, o, "utf-8"), { success: !0 };
928
- }), n.handle("counsellors:create", async (t, e, o, s) => {
929
- const a = u(e, o);
930
- return await N(a, { recursive: !0 }), await $(u(a, "ABOUT.md"), s, "utf-8"), { success: !0, dirPath: a };
931
- }), n.handle("counsellors:delete", async (t, e) => (await F(e, { recursive: !0, force: !0 }), { success: !0 })), n.handle("config:get", async () => {
932
- const t = u(w(), ".ai-council", "config.json");
940
+ }), o.handle("councilors:get", async (t, e) => {
941
+ const r = d(e, "ABOUT.md"), n = await w(r, "utf-8"), { data: a, content: i } = X(n);
942
+ return { frontmatter: a, body: i.trim(), raw: n };
943
+ }), o.handle("councilors:save", async (t, e, r) => {
944
+ const n = d(e, "ABOUT.md");
945
+ return await P(n, r, "utf-8"), { success: !0 };
946
+ }), o.handle("councilors:create", async (t, e, r, n) => {
947
+ const a = d(e, r);
948
+ return await N(a, { recursive: !0 }), await P(d(a, "ABOUT.md"), n, "utf-8"), { success: !0, dirPath: a };
949
+ }), o.handle("councilors:delete", async (t, e) => (await F(e, { recursive: !0, force: !0 }), { success: !0 })), o.handle("config:get", async () => {
950
+ const t = d(v(), ".ai-council", "config.json");
933
951
  let e = { backends: {} };
934
952
  try {
935
- const i = await y(t, "utf-8");
953
+ const i = await w(t, "utf-8");
936
954
  e = JSON.parse(i);
937
955
  } catch {
938
956
  }
939
- const o = {
957
+ const r = {
940
958
  ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
941
959
  OPENAI_API_KEY: !!process.env.OPENAI_API_KEY,
942
960
  GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY
943
- }, s = (i) => i ? "..." + i.slice(-4) : void 0, a = {
944
- ANTHROPIC_API_KEY: s(process.env.ANTHROPIC_API_KEY),
945
- OPENAI_API_KEY: s(process.env.OPENAI_API_KEY),
946
- GOOGLE_API_KEY: s(process.env.GOOGLE_API_KEY)
961
+ }, n = (i) => i ? "..." + i.slice(-4) : void 0, a = {
962
+ ANTHROPIC_API_KEY: n(process.env.ANTHROPIC_API_KEY),
963
+ OPENAI_API_KEY: n(process.env.OPENAI_API_KEY),
964
+ GOOGLE_API_KEY: n(process.env.GOOGLE_API_KEY)
947
965
  };
948
- return { config: e, envStatus: o, envKeySuffix: a, defaultUrls: we };
949
- }), n.handle("backend:probe", async (t, e, o) => kt(e, o)), n.handle("config:save", async (t, e) => {
950
- const o = u(w(), ".ai-council");
951
- return await N(o, { recursive: !0 }), await $(u(o, "config.json"), JSON.stringify(e, null, 2), "utf-8"), Ve(), { success: !0 };
952
- }), n.handle("discussion:start", async (t, e) => {
953
- const o = r();
954
- if (!o) return { error: "No window" };
955
- A && A.abort(), A = new AbortController(), j = [];
956
- const s = u(w(), ".ai-council", "config.json");
957
- let a = { backends: {} };
958
- try {
959
- const f = await y(s, "utf-8");
960
- a = JSON.parse(f);
961
- } catch {
962
- }
963
- const i = re(a), l = await oe(e.councilDir, i), c = e.counsellorIds?.length ? l.filter((f) => e.counsellorIds.includes(f.id)) : l;
964
- if (c.length === 0) {
965
- o.webContents.send("discussion:event", { type: "error", counsellorName: "", error: "No counsellors found" });
966
- return;
967
- }
968
- const m = (f) => {
969
- o.isDestroyed() || o.webContents.send("discussion:event", f);
970
- }, g = async () => j.length === 0 ? null : {
971
- round: 0,
972
- counsellorId: "__user__",
973
- counsellorName: "You",
974
- content: j.shift(),
975
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
976
- model: "human",
977
- backend: "human"
978
- }, p = {
979
- topic: e.topic,
980
- topicSource: e.topicSource,
981
- counsellors: c,
982
- rounds: e.rounds,
983
- onEvent: m,
984
- beforeTurn: g,
985
- signal: A.signal,
986
- mode: e.mode,
987
- config: a
966
+ return { config: e, envStatus: r, envKeySuffix: a, defaultUrls: be };
967
+ }), o.handle("backend:probe", async (t, e, r) => At(e, r)), o.handle("config:save", async (t, e) => {
968
+ const r = d(v(), ".ai-council");
969
+ return await N(r, { recursive: !0 }), await P(d(r, "config.json"), JSON.stringify(e, null, 2), "utf-8"), et(), { success: !0 };
970
+ }), o.handle("discussion:start", async (t, e) => {
971
+ const r = s();
972
+ if (!r) return { error: "No window" };
973
+ const n = (a) => {
974
+ r.isDestroyed() || r.webContents.send("discussion:event", a);
988
975
  };
989
976
  try {
990
- const f = await lt(p);
991
- if (a.secretary?.backend)
977
+ A && A.abort(), A = new AbortController(), B = [], _.info("ipc:discussion", "Starting discussion", { councilDir: e.councilDir, councilorIds: e.councilorIds, rounds: e.rounds, mode: e.mode });
978
+ const a = d(v(), ".ai-council", "config.json");
979
+ let i = { backends: {} };
980
+ try {
981
+ const l = await w(a, "utf-8");
982
+ i = JSON.parse(l);
983
+ } catch {
984
+ }
985
+ const u = ae(i);
986
+ _.info("ipc:discussion", `Loading councilors from ${e.councilDir} + ${u.length} registered paths`);
987
+ const c = await re(e.councilDir, u), m = e.councilorIds?.length ? c.filter((l) => e.councilorIds.includes(l.id)) : c;
988
+ if (_.info("ipc:discussion", `Resolved ${m.length} councilors: ${m.map((l) => l.id).join(", ")}`), m.length === 0) {
989
+ n({ type: "error", councilorName: "", error: "No councilors found" });
990
+ return;
991
+ }
992
+ const y = async () => B.length === 0 ? null : {
993
+ round: 0,
994
+ councilorId: "__user__",
995
+ councilorName: "You",
996
+ content: B.shift(),
997
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
998
+ model: "human",
999
+ backend: "human"
1000
+ }, h = !!e.previousTurns?.length, p = {
1001
+ topic: e.topic,
1002
+ topicSource: e.topicSource,
1003
+ councilors: m,
1004
+ rounds: e.rounds,
1005
+ onEvent: n,
1006
+ beforeTurn: y,
1007
+ signal: A.signal,
1008
+ mode: h ? "freeform" : e.mode,
1009
+ config: i,
1010
+ previousTurns: e.previousTurns,
1011
+ previousSummary: e.previousSummary
1012
+ }, b = await ft(p);
1013
+ if (i.secretary?.backend)
992
1014
  try {
993
- m({ type: "summary_start" });
994
- const d = await qe({
995
- result: f,
996
- config: a,
997
- onChunk: (h) => {
998
- m({ type: "summary_chunk", delta: h });
1015
+ n({ type: "summary_start" });
1016
+ const l = await rt({
1017
+ result: b,
1018
+ config: i,
1019
+ onChunk: (g) => {
1020
+ n({ type: "summary_chunk", delta: g });
999
1021
  },
1000
1022
  signal: A?.signal
1001
1023
  });
1002
- f.summary = d.text, d.diagram && (f.diagram = d.diagram), m({ type: "summary_complete", summary: d.text, diagram: d.diagram });
1003
- } catch (d) {
1004
- O.error("ipc:discussion", "Secretary summary failed", d), m({ type: "error", counsellorName: "Secretary", error: d instanceof Error ? d.message : String(d) });
1024
+ b.summary = l.text, l.diagram && (b.diagram = l.diagram), n({ type: "summary_complete", summary: l.text, diagram: l.diagram });
1025
+ } catch (l) {
1026
+ _.error("ipc:discussion", "Secretary summary failed", l), n({ type: "error", councilorName: "Secretary", error: l instanceof Error ? l.message : String(l) });
1005
1027
  }
1006
- if (a.secretary?.backend)
1028
+ if (i.secretary?.backend)
1007
1029
  try {
1008
- const d = f.turns.filter((b) => b.round === 1), h = await nt({
1009
- topic: f.topic,
1010
- firstRoundTurns: d,
1011
- config: a
1030
+ const l = b.turns.filter((f) => f.round === 1), g = await it({
1031
+ topic: b.topic,
1032
+ firstRoundTurns: l,
1033
+ config: i
1012
1034
  });
1013
- f.title = h, m({ type: "title_generated", title: h });
1014
- } catch (d) {
1015
- O.error("ipc:discussion", "Title generation failed", d);
1035
+ b.title = g, n({ type: "title_generated", title: g });
1036
+ } catch (l) {
1037
+ _.error("ipc:discussion", "Title generation failed", l);
1016
1038
  }
1017
1039
  if (e.infographicBackends?.length)
1018
- for (const d of e.infographicBackends)
1040
+ for (const l of e.infographicBackends)
1019
1041
  try {
1020
- m({ type: "infographic_start" });
1021
- const h = await le(f, a, d);
1022
- f.infographics || (f.infographics = []), f.infographics.push(h), m({ type: "infographic_complete", infographic: h });
1023
- } catch (h) {
1024
- O.error("ipc:discussion", `Infographic generation failed (${d})`, h), m({ type: "infographic_error", error: h instanceof Error ? h.message : String(h) });
1042
+ n({ type: "infographic_start" });
1043
+ const g = await ue(b, i, l);
1044
+ b.infographics || (b.infographics = []), b.infographics.push(g), n({ type: "infographic_complete", infographic: g });
1045
+ } catch (g) {
1046
+ _.error("ipc:discussion", `Infographic generation failed (${l})`, g), n({ type: "infographic_error", error: g instanceof Error ? g.message : String(g) });
1025
1047
  }
1026
- m({ type: "complete", result: f });
1048
+ e.continuedFrom && (b.continuedFrom = e.continuedFrom), n({ type: "complete", result: b });
1027
1049
  try {
1028
- await dt(f);
1029
- } catch (d) {
1030
- O.error("ipc:discussion", "Failed to save to history", d);
1050
+ await gt(b);
1051
+ } catch (l) {
1052
+ _.error("ipc:discussion", "Failed to save to history", l);
1031
1053
  }
1032
- } catch (f) {
1033
- O.error("ipc:discussion", "Discussion failed", f), m({ type: "error", counsellorName: "", error: f instanceof Error ? f.message : String(f) });
1054
+ } catch (a) {
1055
+ _.error("ipc:discussion", "Discussion failed", a), n({ type: "error", councilorName: "", error: a instanceof Error ? a.message : String(a) });
1034
1056
  } finally {
1035
1057
  A = null;
1036
1058
  }
1037
- }), n.handle("discussion:stop", async () => (A && (A.abort(), A = null), { success: !0 })), n.handle("discussion:inject", async (t, e) => (j.push(e), { success: !0 })), n.handle("registry:add-local", async (t, e) => Ge(e)), n.handle("registry:add-remote", async (t, e) => Fe(e)), n.handle("registry:remove", async (t, e, o) => (await We(e, o), { success: !0 })), n.handle("shell:open-in-finder", async (t, e) => {
1038
- ee.showItemInFolder(u(e, "ABOUT.md"));
1039
- }), n.handle("shell:open-in-terminal", async (t, e) => {
1040
- await R(U)("open", ["-a", "Terminal", e]);
1041
- }), n.handle("shell:open-in-editor", async (t, e) => {
1042
- const o = R(U);
1059
+ }), o.handle("discussion:stop", async () => (A && (A.abort(), A = null), { success: !0 })), o.handle("discussion:inject", async (t, e) => (B.push(e), { success: !0 })), o.handle("registry:add-local", async (t, e) => He(e)), o.handle("registry:add-remote", async (t, e) => Je(e)), o.handle("registry:remove", async (t, e, r) => (await ze(e, r), { success: !0 })), o.handle("shell:open-in-finder", async (t, e) => {
1060
+ te.showItemInFolder(d(e, "ABOUT.md"));
1061
+ }), o.handle("shell:open-in-terminal", async (t, e) => {
1062
+ await U(R)("open", ["-a", "Terminal", e]);
1063
+ }), o.handle("shell:open-in-editor", async (t, e) => {
1064
+ const r = U(R);
1043
1065
  try {
1044
- await o("code", [e]);
1066
+ await r("code", [e]);
1045
1067
  } catch {
1046
- ee.openPath(e);
1068
+ te.openPath(e);
1047
1069
  }
1048
- }), n.handle("history:list", async () => mt()), n.handle("history:get", async (t, e) => ce(e)), n.handle("history:delete", async (t, e) => (await pt(e), { success: !0 })), n.handle("infographic:generate", async (t, e, o) => {
1049
- const s = u(w(), ".ai-council", "config.json");
1070
+ }), o.handle("history:list", async () => yt()), o.handle("history:get", async (t, e) => le(e)), o.handle("history:delete", async (t, e) => (await wt(e), { success: !0 })), o.handle("infographic:generate", async (t, e, r) => {
1071
+ const n = d(v(), ".ai-council", "config.json");
1050
1072
  let a = { backends: {} };
1051
1073
  try {
1052
- const c = await y(s, "utf-8");
1074
+ const c = await w(n, "utf-8");
1053
1075
  a = JSON.parse(c);
1054
1076
  } catch {
1055
1077
  }
1056
- const i = await ce(e), l = await le(i, a, o);
1057
- return await ft(e, l), { infographic: l };
1058
- }), n.handle("infographic:delete", async (t, e, o) => (await ht(e, o), { success: !0 })), n.handle("file:read-as-text", async (t, e) => {
1059
- const o = C(e), s = o.includes(".") ? "." + o.split(".").pop().toLowerCase() : "", a = /* @__PURE__ */ new Set([
1078
+ const i = await le(e), u = await ue(i, a, r);
1079
+ return await bt(e, u), { infographic: u };
1080
+ }), o.handle("infographic:delete", async (t, e, r) => (await vt(e, r), { success: !0 })), o.handle("file:read-as-text", async (t, e) => {
1081
+ const r = C(e), n = r.includes(".") ? "." + r.split(".").pop().toLowerCase() : "", a = /* @__PURE__ */ new Set([
1060
1082
  ".txt",
1061
1083
  ".md",
1062
1084
  ".csv",
@@ -1104,41 +1126,41 @@ function vt(n, r) {
1104
1126
  ".epub",
1105
1127
  ".rtf"
1106
1128
  ]);
1107
- if (a.has(s))
1129
+ if (a.has(n))
1108
1130
  try {
1109
- const l = await y(e, "utf-8");
1110
- return { name: o, content: l };
1111
- } catch (l) {
1112
- return { name: o, content: `[Error reading file: ${l instanceof Error ? l.message : String(l)}]` };
1131
+ const u = await w(e, "utf-8");
1132
+ return { name: r, content: u };
1133
+ } catch (u) {
1134
+ return { name: r, content: `[Error reading file: ${u instanceof Error ? u.message : String(u)}]` };
1113
1135
  }
1114
- if (i.has(s)) {
1115
- const l = R(U);
1136
+ if (i.has(n)) {
1137
+ const u = U(R);
1116
1138
  try {
1117
- const { stdout: c } = await l("markitdown", [e], {
1139
+ const { stdout: c } = await u("markitdown", [e], {
1118
1140
  timeout: 3e4,
1119
1141
  maxBuffer: 10485760
1120
1142
  // 10 MB
1121
1143
  });
1122
- return { name: o, content: c };
1144
+ return { name: r, content: c };
1123
1145
  } catch (c) {
1124
1146
  const m = c instanceof Error ? c.message : String(c);
1125
1147
  return m.includes("ENOENT") ? {
1126
- name: o,
1127
- content: `[Cannot convert ${s} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
1128
- } : { name: o, content: `[Error converting file: ${m}]` };
1148
+ name: r,
1149
+ content: `[Cannot convert ${n} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
1150
+ } : { name: r, content: `[Error converting file: ${m}]` };
1129
1151
  }
1130
1152
  }
1131
- return { name: o, content: `[Unsupported file type: ${o}]` };
1132
- }), n.handle("markitdown:check", async () => {
1133
- const t = R(U);
1153
+ return { name: r, content: `[Unsupported file type: ${r}]` };
1154
+ }), o.handle("markitdown:check", async () => {
1155
+ const t = U(R);
1134
1156
  try {
1135
1157
  const { stdout: e } = await t("markitdown", ["--version"], { timeout: 5e3 });
1136
1158
  return { installed: !0, version: e.trim() };
1137
1159
  } catch {
1138
1160
  return { installed: !1 };
1139
1161
  }
1140
- }), n.handle("markitdown:install", async () => {
1141
- const t = R(U);
1162
+ }), o.handle("markitdown:install", async () => {
1163
+ const t = U(R);
1142
1164
  try {
1143
1165
  return await t("pip", ["install", "markitdown[all]"], {
1144
1166
  timeout: 12e4,
@@ -1147,75 +1169,77 @@ function vt(n, r) {
1147
1169
  } catch (e) {
1148
1170
  return { success: !1, error: e instanceof Error ? e.message : String(e) };
1149
1171
  }
1150
- }), n.handle("dialog:selectDirectory", async () => {
1151
- const t = r();
1172
+ }), o.handle("dialog:selectDirectory", async () => {
1173
+ const t = s();
1152
1174
  if (!t) return null;
1153
- const e = await ke.showOpenDialog(t, {
1175
+ const e = await Ee.showOpenDialog(t, {
1154
1176
  properties: ["openDirectory"]
1155
1177
  });
1156
1178
  return e.canceled ? null : e.filePaths[0];
1157
1179
  });
1158
1180
  }
1159
- const _t = Ae(import.meta.url), B = Ee(_t), be = u(B, "..", "electron-debug.log");
1160
- function P(n, ...r) {
1161
- const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${n}] ${r.map((e) => typeof e == "string" ? e : JSON.stringify(e)).join(" ")}
1181
+ const Ot = Oe(import.meta.url), J = Ie(Ot), ve = d(v(), ".ai-council");
1182
+ Pe(ve, { recursive: !0 });
1183
+ const ke = d(ve, "electron-debug.log");
1184
+ function $(o, ...s) {
1185
+ const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${o}] ${s.map((e) => typeof e == "string" ? e : JSON.stringify(e)).join(" ")}
1162
1186
  `;
1163
- Oe(be, t);
1187
+ Ne(ke, t);
1164
1188
  }
1165
- Se(be, `=== State Change Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
1189
+ Ce(ke, `=== State Change Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
1166
1190
  `);
1167
1191
  L.name = "State Change Council";
1168
- let _ = null;
1169
- function de() {
1170
- P("main", "Creating BrowserWindow"), _ = new pe({
1192
+ let S = null;
1193
+ function me() {
1194
+ $("main", "Creating BrowserWindow"), S = new fe({
1171
1195
  width: 1200,
1172
1196
  height: 800,
1173
1197
  minWidth: 800,
1174
1198
  minHeight: 600,
1175
1199
  title: "State Change Council",
1176
- icon: G(B, "..", "assets", "icon.png"),
1200
+ icon: G(J, "..", "assets", "icon.png"),
1177
1201
  webPreferences: {
1178
1202
  contextIsolation: !0,
1179
1203
  nodeIntegration: !1,
1180
- preload: u(B, "preload.mjs")
1204
+ preload: d(J, "preload.mjs")
1181
1205
  }
1182
- }), _.webContents.on("console-message", (r, t, e, o, s) => {
1206
+ }), S.webContents.on("console-message", (s, t, e, r, n) => {
1183
1207
  const a = ["DEBUG", "INFO", "WARN", "ERROR"][t] || "LOG";
1184
- P(`renderer:${a}`, `${e} (${s}:${o})`);
1185
- }), _.webContents.on("render-process-gone", (r, t) => {
1186
- P("main:CRASH", "Renderer process gone:", t);
1187
- }), _.webContents.on("did-fail-load", (r, t, e) => {
1188
- P("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
1208
+ $(`renderer:${a}`, `${e} (${n}:${r})`);
1209
+ }), S.webContents.on("render-process-gone", (s, t) => {
1210
+ $("main:CRASH", "Renderer process gone:", t);
1211
+ }), S.webContents.on("did-fail-load", (s, t, e) => {
1212
+ $("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
1189
1213
  });
1190
- const n = process.env.VITE_DEV_SERVER_URL;
1191
- if (n)
1192
- P("main", `Loading dev server URL: ${n}`), _.loadURL(n);
1214
+ const o = process.env.VITE_DEV_SERVER_URL;
1215
+ if (o)
1216
+ $("main", `Loading dev server URL: ${o}`), S.loadURL(o);
1193
1217
  else {
1194
- const r = u(B, "../dist-renderer/index.html");
1195
- P("main", `Loading file: ${r}`), _.loadFile(r);
1218
+ const s = d(J, "../dist-renderer/index.html");
1219
+ $("main", `Loading file: ${s}`), S.loadFile(s);
1196
1220
  }
1197
- process.env.VITE_DEV_SERVER_URL && _.webContents.openDevTools(), _.on("closed", () => {
1198
- _ = null;
1221
+ process.env.VITE_DEV_SERVER_URL && S.webContents.openDevTools(), S.on("closed", () => {
1222
+ S = null;
1199
1223
  });
1200
1224
  }
1201
- me.registerSchemesAsPrivileged([
1225
+ pe.registerSchemesAsPrivileged([
1202
1226
  { scheme: "council-file", privileges: { bypassCSP: !0, supportFetchAPI: !0 } }
1203
1227
  ]);
1204
1228
  L.whenReady().then(() => {
1205
- P("main", "App ready, registering IPC handlers");
1206
- const n = "State Change Council", r = [
1229
+ $("main", "App ready, registering IPC handlers");
1230
+ const o = "State Change Council", s = [
1207
1231
  {
1208
- label: n,
1232
+ label: o,
1209
1233
  submenu: [
1210
- { role: "about", label: `About ${n}` },
1234
+ { role: "about", label: `About ${o}` },
1211
1235
  { type: "separator" },
1212
1236
  { role: "services" },
1213
1237
  { type: "separator" },
1214
- { role: "hide", label: `Hide ${n}` },
1238
+ { role: "hide", label: `Hide ${o}` },
1215
1239
  { role: "hideOthers" },
1216
1240
  { role: "unhide" },
1217
1241
  { type: "separator" },
1218
- { role: "quit", label: `Quit ${n}` }
1242
+ { role: "quit", label: `Quit ${o}` }
1219
1243
  ]
1220
1244
  },
1221
1245
  { role: "fileMenu" },
@@ -1223,11 +1247,11 @@ L.whenReady().then(() => {
1223
1247
  { role: "viewMenu" },
1224
1248
  { role: "windowMenu" }
1225
1249
  ];
1226
- te.setApplicationMenu(te.buildFromTemplate(r)), me.handle("council-file", (t) => {
1250
+ ne.setApplicationMenu(ne.buildFromTemplate(s)), pe.handle("council-file", (t) => {
1227
1251
  const e = decodeURIComponent(t.url.replace("council-file://", ""));
1228
- return ve.fetch(Ie(e).href);
1229
- }), vt(_e, () => _), de(), L.on("activate", () => {
1230
- pe.getAllWindows().length === 0 && de();
1252
+ return Se.fetch($e(e).href);
1253
+ }), It(Ae, () => S), me(), L.on("activate", () => {
1254
+ fe.getAllWindows().length === 0 && me();
1231
1255
  });
1232
1256
  });
1233
1257
  L.on("window-all-closed", () => {