@statechange/council 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/cli.js +15 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/gui.d.ts +1 -0
  4. package/dist/commands/gui.js +63 -0
  5. package/dist/commands/gui.js.map +1 -0
  6. package/dist/commands/install.d.ts +5 -0
  7. package/dist/commands/install.js +131 -0
  8. package/dist/commands/install.js.map +1 -0
  9. package/dist-electron/index-B7VRloBj.js +1293 -0
  10. package/dist-electron/index-C03cLUYg.js +5911 -0
  11. package/dist-electron/index-Dw6I25AL.js +5911 -0
  12. package/dist-electron/index-NMn1koXq.js +31737 -0
  13. package/dist-electron/index-zpksia2X.js +5911 -0
  14. package/dist-electron/main-C_og2Wvn.js +32969 -0
  15. package/dist-electron/main-DBohq-SO.js +32969 -0
  16. package/dist-electron/main.js +791 -1217
  17. package/dist-electron/multipart-parser-Bdb1JOpV.js +353 -0
  18. package/dist-electron/multipart-parser-Cc_drctG.js +353 -0
  19. package/dist-electron/multipart-parser-Do9VIIY5.js +353 -0
  20. package/dist-electron/preload.mjs +1 -0
  21. package/dist-renderer/assets/Tableau10-B-NsZVaP.js +1 -0
  22. package/dist-renderer/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
  23. package/dist-renderer/assets/ar-SA-G6X2FPQ2-CNUtTBRd.js +10 -0
  24. package/dist-renderer/assets/arc-BsV2XMg-.js +1 -0
  25. package/dist-renderer/assets/array-BKyUJesY.js +1 -0
  26. package/dist-renderer/assets/az-AZ-76LH7QW2-BU-6SB21.js +1 -0
  27. package/dist-renderer/assets/bg-BG-XCXSNQG7-L1xMEEzs.js +5 -0
  28. package/dist-renderer/assets/blockDiagram-38ab4fdb-BHS1f8SU.js +118 -0
  29. package/dist-renderer/assets/bn-BD-2XOGV67Q-BgaYSNWB.js +5 -0
  30. package/dist-renderer/assets/c4Diagram-3d4e48cf-D2Rl9Buo.js +10 -0
  31. package/dist-renderer/assets/ca-ES-6MX7JW3Y-DsFYhkLC.js +8 -0
  32. package/dist-renderer/assets/channel-D6rvl4sk.js +1 -0
  33. package/dist-renderer/assets/classDiagram-70f12bd4-BGxDdVFC.js +2 -0
  34. package/dist-renderer/assets/classDiagram-v2-f2320105-BuoUwdmW.js +2 -0
  35. package/dist-renderer/assets/clone-ya1OS7ZU.js +1 -0
  36. package/dist-renderer/assets/createText-2e5e7dd3-BX6eAgL1.js +7 -0
  37. package/dist-renderer/assets/cs-CZ-2BRQDIVT-CXV-i633.js +11 -0
  38. package/dist-renderer/assets/da-DK-5WZEPLOC-DmYIifsm.js +5 -0
  39. package/dist-renderer/assets/de-DE-XR44H4JA-B2T52TzN.js +8 -0
  40. package/dist-renderer/assets/directory-open-01563666-DWU9wJ6I.js +1 -0
  41. package/dist-renderer/assets/directory-open-4ed118d0-CunoC1EB.js +1 -0
  42. package/dist-renderer/assets/edges-e0da2a9e-ChSiyO2u.js +4 -0
  43. package/dist-renderer/assets/el-GR-BZB4AONW-DlS_8ZJJ.js +10 -0
  44. package/dist-renderer/assets/erDiagram-9861fffd-REAc3tDU.js +51 -0
  45. package/dist-renderer/assets/es-ES-U4NZUMDT-DL-zHXpT.js +9 -0
  46. package/dist-renderer/assets/eu-ES-A7QVB2H4-Cq0Kx947.js +11 -0
  47. package/dist-renderer/assets/fa-IR-HGAKTJCU-DJqZOqAL.js +8 -0
  48. package/dist-renderer/assets/fi-FI-Z5N7JZ37-CQiIDJrs.js +6 -0
  49. package/dist-renderer/assets/file-open-002ab408-DIuFHtCF.js +1 -0
  50. package/dist-renderer/assets/file-open-7c801643-684qeFg4.js +1 -0
  51. package/dist-renderer/assets/file-save-3189631c-C1wFhQhH.js +1 -0
  52. package/dist-renderer/assets/file-save-745eba88-Bb9F9Kg7.js +1 -0
  53. package/dist-renderer/assets/flowDb-956e92f1-iZnRwdLd.js +10 -0
  54. package/dist-renderer/assets/flowDiagram-66a62f08-Cww5ErID.js +4 -0
  55. package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-DMQnUrJv.js +1 -0
  56. package/dist-renderer/assets/flowchart-elk-definition-4a651766-B3GjdX0I.js +139 -0
  57. package/dist-renderer/assets/fr-FR-RHASNOE6-DXRyDBSp.js +9 -0
  58. package/dist-renderer/assets/ganttDiagram-c361ad54-Dh5UuCf5.js +257 -0
  59. package/dist-renderer/assets/gitGraphDiagram-72cf32ee-CUrdiK9f.js +70 -0
  60. package/dist-renderer/assets/gl-ES-HMX3MZ6V-DB2LhjQl.js +10 -0
  61. package/dist-renderer/assets/graph-KS9cHKTv.js +1 -0
  62. package/dist-renderer/assets/he-IL-6SHJWFNN-CWdud4mG.js +10 -0
  63. package/dist-renderer/assets/hi-IN-IWLTKZ5I-DkfCxaBI.js +4 -0
  64. package/dist-renderer/assets/hu-HU-A5ZG7DT2-DVX9rddx.js +7 -0
  65. package/dist-renderer/assets/id-ID-SAP4L64H-CIosyByg.js +10 -0
  66. package/dist-renderer/assets/image-blob-reduce.esm-BLtmMM_J.js +2 -0
  67. package/dist-renderer/assets/index-3862675e-CVxUAJo4.js +1 -0
  68. package/dist-renderer/assets/index-3RkkhxnO.js +36 -0
  69. package/dist-renderer/assets/index-B61Czmwn.js +95 -0
  70. package/dist-renderer/assets/index-BF6L2_KC.css +1 -0
  71. package/dist-renderer/assets/infoDiagram-f8f76790-CWACEGb6.js +7 -0
  72. package/dist-renderer/assets/init-Gi6I4Gst.js +1 -0
  73. package/dist-renderer/assets/it-IT-JPQ66NNP-D-TuMToN.js +11 -0
  74. package/dist-renderer/assets/ja-JP-DBVTYXUO-CK6puaHx.js +8 -0
  75. package/dist-renderer/assets/journeyDiagram-49397b02-DM-laNCt.js +139 -0
  76. package/dist-renderer/assets/kaa-6HZHGXH3-CiRWfMKd.js +1 -0
  77. package/dist-renderer/assets/kab-KAB-ZGHBKWFO-D4w9BQ8H.js +8 -0
  78. package/dist-renderer/assets/katex-DhXJpUyf.js +261 -0
  79. package/dist-renderer/assets/kk-KZ-P5N5QNE5-RZi2AGvr.js +1 -0
  80. package/dist-renderer/assets/km-KH-HSX4SM5Z-Dm-OPxhU.js +11 -0
  81. package/dist-renderer/assets/ko-KR-MTYHY66A-PbPGVqVz.js +9 -0
  82. package/dist-renderer/assets/ku-TR-6OUDTVRD-Bw0cMtYj.js +9 -0
  83. package/dist-renderer/assets/layout-zADYSvqS.js +1 -0
  84. package/dist-renderer/assets/line-DfBkzhQg.js +1 -0
  85. package/dist-renderer/assets/linear-O3lyK7Iz.js +1 -0
  86. package/dist-renderer/assets/lt-LT-XHIRWOB4-06ru_RZe.js +3 -0
  87. package/dist-renderer/assets/lv-LV-5QDEKY6T-DsGBjFui.js +7 -0
  88. package/dist-renderer/assets/mindmap-definition-fc14e90a-Bg9UaNPK.js +415 -0
  89. package/dist-renderer/assets/mr-IN-CRQNXWMA-C1kWOErC.js +13 -0
  90. package/dist-renderer/assets/my-MM-5M5IBNSE-C8wD2OFs.js +1 -0
  91. package/dist-renderer/assets/nb-NO-T6EIAALU-B7ac-R_W.js +10 -0
  92. package/dist-renderer/assets/nl-NL-IS3SIHDZ-Up67nxB7.js +8 -0
  93. package/dist-renderer/assets/nn-NO-6E72VCQL-BPlkzXM9.js +8 -0
  94. package/dist-renderer/assets/oc-FR-POXYY2M6-TRLIkgml.js +8 -0
  95. package/dist-renderer/assets/ordinal-BENe2yWM.js +1 -0
  96. package/dist-renderer/assets/pa-IN-N4M65BXN-Dl7hF9RX.js +4 -0
  97. package/dist-renderer/assets/path-CbwjOpE9.js +1 -0
  98. package/dist-renderer/assets/percentages-BXMCSKIN-wBXtRqNn.js +199 -0
  99. package/dist-renderer/assets/pica-CGZUgvhx.js +2 -0
  100. package/dist-renderer/assets/pieDiagram-8a3498a8-ZPRkRtpu.js +35 -0
  101. package/dist-renderer/assets/pl-PL-T2D74RX3-DkwkT852.js +9 -0
  102. package/dist-renderer/assets/pt-BR-5N22H2LF-BMFWH_jN.js +9 -0
  103. package/dist-renderer/assets/pt-PT-UZXXM6DQ-BPSC-yrG.js +9 -0
  104. package/dist-renderer/assets/quadrantDiagram-120e2f19-DB1TyNBK.js +7 -0
  105. package/dist-renderer/assets/requirementDiagram-deff3bca-DqVc_A29.js +52 -0
  106. package/dist-renderer/assets/ro-RO-JPDTUUEW-CgkEXyU-.js +11 -0
  107. package/dist-renderer/assets/roundRect-mAH3dD0p.js +1 -0
  108. package/dist-renderer/assets/ru-RU-B4JR7IUQ-BJIahd0q.js +9 -0
  109. package/dist-renderer/assets/sankeyDiagram-04a897e0-DZqGbQ0e.js +8 -0
  110. package/dist-renderer/assets/sequenceDiagram-704730f1-D0Q04jBP.js +122 -0
  111. package/dist-renderer/assets/si-LK-N5RQ5JYF-DZ97xsfW.js +1 -0
  112. package/dist-renderer/assets/sk-SK-C5VTKIMK-3O0GcaTE.js +6 -0
  113. package/dist-renderer/assets/sl-SI-NN7IZMDC-x_-Ca4Po.js +6 -0
  114. package/dist-renderer/assets/stateDiagram-587899a1-BkW3tbK-.js +1 -0
  115. package/dist-renderer/assets/stateDiagram-v2-d93cdb3a-CZYVILLk.js +1 -0
  116. package/dist-renderer/assets/styles-6aaf32cf-BQKVAqEs.js +207 -0
  117. package/dist-renderer/assets/styles-9a916d00-DTjDY-zu.js +160 -0
  118. package/dist-renderer/assets/styles-c10674c1-CviHLZ2K.js +116 -0
  119. package/dist-renderer/assets/subset-shared.chunk-DIxPfZhi.js +22 -0
  120. package/dist-renderer/assets/subset-worker.chunk-DDMwauN2.js +1 -0
  121. package/dist-renderer/assets/sv-SE-XGPEYMSR-DmtUCQiw.js +10 -0
  122. package/dist-renderer/assets/svgDrawCommon-08f97a94-B6bAmadW.js +1 -0
  123. package/dist-renderer/assets/ta-IN-2NMHFXQM-D7fiXwNW.js +9 -0
  124. package/dist-renderer/assets/th-TH-HPSO5L25-D_F5D_Tj.js +2 -0
  125. package/dist-renderer/assets/timeline-definition-85554ec2-BZuaxri2.js +61 -0
  126. package/dist-renderer/assets/tr-TR-DEFEU3FU-CzwSWbpN.js +7 -0
  127. package/dist-renderer/assets/uk-UA-QMV73CPH-BaaxaBLn.js +6 -0
  128. package/dist-renderer/assets/vi-VN-M7AON7JQ-BQ9szPme.js +5 -0
  129. package/dist-renderer/assets/xychartDiagram-e933f94c-7rTYP6vy.js +7 -0
  130. package/dist-renderer/assets/zh-CN-LNUGB5OW-DDjjE3j0.js +10 -0
  131. package/dist-renderer/assets/zh-HK-E62DVLB3-CUaw1jmU.js +1 -0
  132. package/dist-renderer/assets/zh-TW-RAJ6MFWO-BFOuD9P0.js +9 -0
  133. package/dist-renderer/index.html +13 -0
  134. package/package.json +7 -3
  135. package/skills/council-install/SKILL.md +63 -0
@@ -1,583 +1,429 @@
1
- import { shell, dialog, protocol, app, net, BrowserWindow, ipcMain } from "electron";
2
- import { join, basename, resolve, dirname } from "node:path";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { existsSync, writeFileSync, appendFileSync } from "node:fs";
5
- import { readFile, readdir, stat, mkdir, rm, writeFile, appendFile } from "node:fs/promises";
6
- import { execFile } from "node:child_process";
7
- import { promisify } from "node:util";
8
- import { homedir } from "node:os";
9
- import matter from "gray-matter";
10
- import { z } from "zod";
11
- import Anthropic from "@anthropic-ai/sdk";
12
- import OpenAI from "openai";
13
- import { GoogleGenerativeAI } from "@google/generative-ai";
14
- import { Ollama } from "ollama";
15
- import { createRequire } from "node:module";
1
+ import { shell as q, dialog as be, protocol as ue, app as j, net as ke, BrowserWindow as de, ipcMain as ve } from "electron";
2
+ import { join as u, basename as C, resolve as me, dirname as _e } from "node:path";
3
+ import { fileURLToPath as Ee, pathToFileURL as Ae } from "node:url";
4
+ import { existsSync as v, writeFileSync as Ie, appendFileSync as Se } from "node:fs";
5
+ import { readFile as y, readdir as H, stat as fe, mkdir as N, rm as B, writeFile as $, appendFile as Oe } 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 z from "gray-matter";
10
+ import { z as k } from "zod";
11
+ import Pe from "@anthropic-ai/sdk";
12
+ import $e from "openai";
13
+ import { GoogleGenerativeAI as Ce } from "@google/generative-ai";
14
+ import { Ollama as Ne } from "ollama";
15
+ import { createRequire as xe } from "node:module";
16
16
  import "dotenv/config";
17
- const CounsellorFrontmatterSchema = z.object({
18
- name: z.string(),
19
- description: z.string(),
20
- interests: z.array(z.string()).default([]),
21
- backend: z.enum(["anthropic", "openai", "google", "ollama"]),
22
- model: z.string().optional(),
23
- skills: z.array(z.string()).default([]),
24
- temperature: z.number().min(0).max(2).optional(),
25
- avatar: z.string().optional()
26
- });
27
- const searchPaths = (counsellorDir, skillName) => [
28
- join(counsellorDir, "skills", skillName, "SKILL.md"),
29
- join(process.cwd(), ".claude", "skills", skillName, "SKILL.md"),
30
- join(homedir(), ".agents", "skills", skillName, "SKILL.md"),
31
- join(homedir(), ".claude", "skills", skillName, "SKILL.md")
17
+ const Ue = k.object({
18
+ name: k.string(),
19
+ description: k.string(),
20
+ interests: k.array(k.string()).default([]),
21
+ backend: k.enum(["anthropic", "openai", "google", "ollama"]),
22
+ model: k.string().optional(),
23
+ skills: k.array(k.string()).default([]),
24
+ temperature: k.number().min(0).max(2).optional(),
25
+ avatar: k.string().optional()
26
+ }), Re = (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")
32
31
  ];
33
- async function resolveSkill(skillName, counsellorDir) {
34
- for (const candidate of searchPaths(counsellorDir, skillName)) {
35
- if (existsSync(candidate)) {
36
- const raw = await readFile(candidate, "utf-8");
37
- const { content } = matter(raw);
38
- return content.trim();
32
+ async function Te(n, r) {
33
+ for (const t of Re(r, n))
34
+ if (v(t)) {
35
+ const e = await y(t, "utf-8"), { content: o } = z(e);
36
+ return o.trim();
39
37
  }
40
- }
41
38
  return null;
42
39
  }
43
- async function resolveSkills(skillNames, counsellorDir) {
44
- const sections = [];
45
- for (const name of skillNames) {
46
- const content = await resolveSkill(name, counsellorDir);
47
- if (content) {
48
- sections.push(`## Skill: ${name}
40
+ async function Le(n, r) {
41
+ const t = [];
42
+ for (const e of n) {
43
+ const o = await Te(e, r);
44
+ o && t.push(`## Skill: ${e}
49
45
 
50
- ${content}`);
51
- }
46
+ ${o}`);
52
47
  }
53
- return sections.join("\n\n");
48
+ return t.join(`
49
+
50
+ `);
54
51
  }
55
- function resolveAvatar(avatar, dirPath) {
56
- if (!avatar) return void 0;
57
- if (avatar.startsWith("http://") || avatar.startsWith("https://")) return avatar;
58
- const absPath = avatar.startsWith("/") ? avatar : join(dirPath, avatar);
59
- return `council-file://${absPath}`;
52
+ function De(n, r) {
53
+ return n ? n.startsWith("http://") || n.startsWith("https://") ? n : `council-file://${n.startsWith("/") ? n : u(r, n)}` : void 0;
60
54
  }
61
- async function resolveReferences(content, counsellorDir) {
62
- const refPattern = /\{\{(.+?)\}\}/g;
63
- let resolved = content;
64
- for (const match of content.matchAll(refPattern)) {
65
- const refPath = match[1].trim();
66
- const fullPath = join(counsellorDir, refPath);
67
- if (existsSync(fullPath)) {
68
- const refContent = await readFile(fullPath, "utf-8");
69
- resolved = resolved.replace(match[0], refContent.trim());
70
- } else {
71
- resolved = resolved.replace(match[0], `[Reference not found: ${refPath}]`);
72
- }
55
+ async function Ke(n, r) {
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());
63
+ } else
64
+ e = e.replace(o[0], `[Reference not found: ${s}]`);
73
65
  }
74
- return resolved;
66
+ return e;
75
67
  }
76
- async function loadSingleCounsellor(dirPath) {
77
- const absPath = resolve(dirPath);
78
- const aboutPath = join(absPath, "ABOUT.md");
79
- if (!existsSync(aboutPath)) {
80
- throw new Error(`No ABOUT.md found in ${absPath}`);
81
- }
82
- const raw = await readFile(aboutPath, "utf-8");
83
- const { data, content } = matter(raw);
84
- const frontmatter = CounsellorFrontmatterSchema.parse(data);
85
- let systemPrompt = await resolveReferences(content.trim(), absPath);
86
- if (frontmatter.skills.length > 0) {
87
- const skillContent = await resolveSkills(frontmatter.skills, absPath);
88
- if (skillContent) {
89
- systemPrompt += "\n\n" + skillContent;
90
- }
68
+ async function ee(n) {
69
+ const r = me(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 } = z(e), a = Ue.parse(o);
73
+ let i = await Ke(s.trim(), r);
74
+ if (a.skills.length > 0) {
75
+ const l = await Le(a.skills, r);
76
+ l && (i += `
77
+
78
+ ` + l);
91
79
  }
92
80
  return {
93
- id: basename(absPath),
94
- frontmatter,
95
- systemPrompt,
96
- dirPath: absPath,
97
- avatarUrl: resolveAvatar(frontmatter.avatar, absPath)
81
+ id: C(r),
82
+ frontmatter: a,
83
+ systemPrompt: i,
84
+ dirPath: r,
85
+ avatarUrl: De(a.avatar, r)
98
86
  };
99
87
  }
100
- async function loadCounsellors(councilDir, registeredPaths) {
101
- const counsellors = [];
102
- const seenIds = /* @__PURE__ */ new Set();
103
- if (registeredPaths?.length) {
104
- for (const rPath of registeredPaths) {
105
- if (!existsSync(join(rPath, "ABOUT.md"))) continue;
106
- try {
107
- const c = await loadSingleCounsellor(rPath);
108
- if (!seenIds.has(c.id)) {
109
- counsellors.push(c);
110
- seenIds.add(c.id);
88
+ async function te(n, r) {
89
+ const t = [], e = /* @__PURE__ */ new Set();
90
+ if (r?.length) {
91
+ for (const o of r)
92
+ if (v(u(o, "ABOUT.md")))
93
+ try {
94
+ const s = await ee(o);
95
+ e.has(s.id) || (t.push(s), e.add(s.id));
96
+ } catch {
111
97
  }
112
- } catch {
113
- }
114
- }
115
98
  }
116
- if (existsSync(councilDir)) {
117
- const entries = await readdir(councilDir);
118
- for (const entry of entries) {
119
- const entryPath = join(councilDir, entry);
120
- const info = await stat(entryPath);
121
- if (info.isDirectory() && existsSync(join(entryPath, "ABOUT.md"))) {
122
- if (!seenIds.has(basename(entryPath))) {
123
- counsellors.push(await loadSingleCounsellor(entryPath));
124
- seenIds.add(basename(entryPath));
125
- }
126
- }
99
+ if (v(n)) {
100
+ const o = await H(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 ee(a)), e.add(C(a))));
127
104
  }
128
105
  }
129
- if (counsellors.length === 0) {
130
- throw new Error(`No counsellors found in ${councilDir}. Each counsellor needs a directory with an ABOUT.md file.`);
131
- }
132
- return counsellors;
106
+ if (t.length === 0)
107
+ throw new Error(`No counsellors found in ${n}. Each counsellor needs a directory with an ABOUT.md file.`);
108
+ return t;
133
109
  }
134
- const execFileAsync = promisify(execFile);
135
- const CONFIG_PATH = join(homedir(), ".ai-council", "config.json");
136
- const CLONES_DIR = join(homedir(), ".ai-council", "counsellors");
137
- async function loadConfig$1() {
110
+ const je = R(U), pe = u(w(), ".ai-council", "config.json"), ne = u(w(), ".ai-council", "counsellors");
111
+ async function V() {
138
112
  try {
139
- return JSON.parse(await readFile(CONFIG_PATH, "utf-8"));
113
+ return JSON.parse(await y(pe, "utf-8"));
140
114
  } catch {
141
115
  return { backends: {} };
142
116
  }
143
117
  }
144
- async function saveConfig(config) {
145
- const dir = join(homedir(), ".ai-council");
146
- await mkdir(dir, { recursive: true });
147
- await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
118
+ async function X(n) {
119
+ const r = u(w(), ".ai-council");
120
+ await N(r, { recursive: !0 }), await $(pe, JSON.stringify(n, null, 2), "utf-8");
148
121
  }
149
- function getRegisteredPaths(config) {
150
- return Object.values(config.counsellors ?? {}).map((e) => e.path);
122
+ function oe(n) {
123
+ return Object.values(n.counsellors ?? {}).map((r) => r.path);
151
124
  }
152
- async function addLocalCounsellor(dirPath) {
153
- const absPath = resolve(dirPath);
154
- const aboutPath = join(absPath, "ABOUT.md");
155
- if (!existsSync(aboutPath)) {
156
- throw new Error(`No ABOUT.md found in ${absPath}`);
157
- }
158
- const id = basename(absPath);
159
- const config = await loadConfig$1();
160
- const registry = config.counsellors ?? {};
161
- if (registry[id]) {
162
- throw new Error(`Counsellor "${id}" is already registered (path: ${registry[id].path})`);
163
- }
164
- const raw = await readFile(aboutPath, "utf-8");
165
- const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
166
- const displayName = nameMatch?.[1] ?? id;
167
- registry[id] = {
168
- path: absPath,
125
+ async function Be(n) {
126
+ const r = me(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 V(), 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,
169
135
  source: "local",
170
136
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
171
- };
172
- config.counsellors = registry;
173
- await saveConfig(config);
174
- return { id, name: displayName };
137
+ }, o.counsellors = s, await X(o), { id: e, name: l };
175
138
  }
176
- async function addRemoteCounsellor(url) {
177
- await mkdir(CLONES_DIR, { recursive: true });
178
- const repoName = basename(url, ".git").replace(/\.git$/, "");
179
- const clonePath = join(CLONES_DIR, repoName);
180
- if (existsSync(clonePath)) {
181
- throw new Error(`Directory already exists: ${clonePath}. Remove it first or use a different URL.`);
182
- }
183
- await execFileAsync("git", ["clone", "--depth", "1", url, clonePath]);
184
- const results = [];
185
- const config = await loadConfig$1();
186
- const registry = config.counsellors ?? {};
187
- if (existsSync(join(clonePath, "ABOUT.md"))) {
188
- const id = repoName;
189
- if (registry[id]) {
190
- throw new Error(`Counsellor "${id}" is already registered`);
191
- }
192
- const raw = await readFile(join(clonePath, "ABOUT.md"), "utf-8");
193
- const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
194
- const displayName = nameMatch?.[1] ?? id;
195
- registry[id] = {
196
- path: clonePath,
139
+ async function Ge(n) {
140
+ await N(ne, { recursive: !0 });
141
+ const r = C(n, ".git").replace(/\.git$/, ""), t = u(ne, r);
142
+ if (v(t))
143
+ throw new Error(`Directory already exists: ${t}. Remove it first or use a different URL.`);
144
+ await je("git", ["clone", "--depth", "1", n, t]);
145
+ const e = [], o = await V(), 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] = {
152
+ path: t,
197
153
  source: "git",
198
- url,
154
+ url: n,
199
155
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
200
- };
201
- results.push({ id, name: displayName });
156
+ }, e.push({ id: a, name: c });
202
157
  } else {
203
- const entries = await readdir(clonePath);
204
- for (const entry of entries) {
205
- if (entry.startsWith(".")) continue;
206
- const entryPath = join(clonePath, entry);
207
- const info = await stat(entryPath);
208
- if (info.isDirectory() && existsSync(join(entryPath, "ABOUT.md"))) {
209
- const id = entry;
210
- if (registry[id]) continue;
211
- const raw = await readFile(join(entryPath, "ABOUT.md"), "utf-8");
212
- const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
213
- const displayName = nameMatch?.[1] ?? id;
214
- registry[id] = {
215
- path: entryPath,
158
+ const a = await H(t);
159
+ for (const i of a) {
160
+ if (i.startsWith(".")) continue;
161
+ const l = u(t, i);
162
+ if ((await fe(l)).isDirectory() && v(u(l, "ABOUT.md"))) {
163
+ const m = i;
164
+ if (s[m]) continue;
165
+ const p = (await y(u(l, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? m;
166
+ s[m] = {
167
+ path: l,
216
168
  source: "git",
217
- url,
169
+ url: n,
218
170
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
219
- };
220
- results.push({ id, name: displayName });
171
+ }, e.push({ id: m, name: p });
221
172
  }
222
173
  }
223
- if (results.length === 0) {
224
- await rm(clonePath, { recursive: true, force: true });
225
- throw new Error(`No counsellors found in cloned repository (no ABOUT.md files)`);
226
- }
174
+ if (e.length === 0)
175
+ throw await B(t, { recursive: !0, force: !0 }), new Error("No counsellors found in cloned repository (no ABOUT.md files)");
227
176
  }
228
- config.counsellors = registry;
229
- await saveConfig(config);
230
- return results;
177
+ return o.counsellors = s, await X(o), e;
231
178
  }
232
- async function removeCounsellor(id, deleteFiles = false) {
233
- const config = await loadConfig$1();
234
- const registry = config.counsellors ?? {};
235
- const entry = registry[id];
236
- if (!entry) {
237
- throw new Error(`Counsellor "${id}" is not registered`);
238
- }
239
- if (deleteFiles && entry.source === "git" && existsSync(entry.path)) {
240
- await rm(entry.path, { recursive: true, force: true });
241
- }
242
- delete registry[id];
243
- config.counsellors = registry;
244
- await saveConfig(config);
179
+ async function Fe(n, r = !1) {
180
+ const t = await V(), 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 B(o.path, { recursive: !0, force: !0 }), delete e[n], t.counsellors = e, await X(t);
245
184
  }
246
- function createAnthropicBackend(config) {
247
- const client = new Anthropic({
248
- apiKey: config.apiKey ?? process.env.ANTHROPIC_API_KEY,
249
- ...config.baseUrl ? { baseURL: config.baseUrl } : {}
185
+ function We(n) {
186
+ const r = new Pe({
187
+ apiKey: n.apiKey ?? process.env.ANTHROPIC_API_KEY,
188
+ ...n.baseUrl ? { baseURL: n.baseUrl } : {}
250
189
  });
251
190
  return {
252
191
  name: "anthropic",
253
192
  defaultModel: "claude-sonnet-4-5-20250929",
254
- async chat(request) {
255
- const response = await client.messages.create({
256
- model: request.model,
193
+ async chat(t) {
194
+ const e = await r.messages.create({
195
+ model: t.model,
257
196
  max_tokens: 4096,
258
- system: request.systemPrompt,
259
- messages: request.messages.map((m) => ({
260
- role: m.role,
261
- content: m.content
197
+ system: t.systemPrompt,
198
+ messages: t.messages.map((s) => ({
199
+ role: s.role,
200
+ content: s.content
262
201
  })),
263
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
202
+ ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
264
203
  });
265
- const textBlock = response.content.find((b) => b.type === "text");
266
204
  return {
267
- content: textBlock?.text ?? "",
205
+ content: e.content.find((s) => s.type === "text")?.text ?? "",
268
206
  tokenUsage: {
269
- input: response.usage.input_tokens,
270
- output: response.usage.output_tokens
207
+ input: e.usage.input_tokens,
208
+ output: e.usage.output_tokens
271
209
  }
272
210
  };
273
211
  },
274
- async *chatStream(request) {
275
- const stream = client.messages.stream({
276
- model: request.model,
212
+ async *chatStream(t) {
213
+ const e = r.messages.stream({
214
+ model: t.model,
277
215
  max_tokens: 4096,
278
- system: request.systemPrompt,
279
- messages: request.messages.map((m) => ({
280
- role: m.role,
281
- content: m.content
216
+ system: t.systemPrompt,
217
+ messages: t.messages.map((s) => ({
218
+ role: s.role,
219
+ content: s.content
282
220
  })),
283
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
221
+ ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
284
222
  });
285
- for await (const event of stream) {
286
- if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
287
- yield { delta: event.delta.text };
288
- }
289
- }
290
- const finalMessage = await stream.finalMessage();
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();
291
226
  yield {
292
227
  delta: "",
293
228
  tokenUsage: {
294
- input: finalMessage.usage.input_tokens,
295
- output: finalMessage.usage.output_tokens
229
+ input: o.usage.input_tokens,
230
+ output: o.usage.output_tokens
296
231
  }
297
232
  };
298
233
  }
299
234
  };
300
235
  }
301
- function createOpenAIBackend(config) {
302
- const client = new OpenAI({
303
- apiKey: config.apiKey ?? process.env.OPENAI_API_KEY,
304
- ...config.baseUrl ? { baseURL: config.baseUrl } : {}
236
+ function Ye(n) {
237
+ const r = new $e({
238
+ apiKey: n.apiKey ?? process.env.OPENAI_API_KEY,
239
+ ...n.baseUrl ? { baseURL: n.baseUrl } : {}
305
240
  });
306
241
  return {
307
242
  name: "openai",
308
243
  defaultModel: "gpt-4o",
309
- async chat(request) {
310
- const response = await client.chat.completions.create({
311
- model: request.model,
244
+ async chat(t) {
245
+ const e = await r.chat.completions.create({
246
+ model: t.model,
312
247
  messages: [
313
- { role: "system", content: request.systemPrompt },
314
- ...request.messages.map((m) => ({
315
- role: m.role,
316
- content: m.content
248
+ { role: "system", content: t.systemPrompt },
249
+ ...t.messages.map((s) => ({
250
+ role: s.role,
251
+ content: s.content
317
252
  }))
318
253
  ],
319
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
254
+ ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
320
255
  });
321
- const choice = response.choices[0];
322
256
  return {
323
- content: choice?.message?.content ?? "",
324
- tokenUsage: response.usage ? { input: response.usage.prompt_tokens, output: response.usage.completion_tokens } : void 0
257
+ content: e.choices[0]?.message?.content ?? "",
258
+ tokenUsage: e.usage ? { input: e.usage.prompt_tokens, output: e.usage.completion_tokens } : void 0
325
259
  };
326
260
  },
327
- async *chatStream(request) {
328
- const stream = await client.chat.completions.create({
329
- model: request.model,
261
+ async *chatStream(t) {
262
+ const e = await r.chat.completions.create({
263
+ model: t.model,
330
264
  messages: [
331
- { role: "system", content: request.systemPrompt },
332
- ...request.messages.map((m) => ({
333
- role: m.role,
334
- content: m.content
265
+ { role: "system", content: t.systemPrompt },
266
+ ...t.messages.map((o) => ({
267
+ role: o.role,
268
+ content: o.content
335
269
  }))
336
270
  ],
337
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {},
338
- stream: true,
339
- stream_options: { include_usage: true }
271
+ ...t.temperature !== void 0 ? { temperature: t.temperature } : {},
272
+ stream: !0,
273
+ stream_options: { include_usage: !0 }
340
274
  });
341
- for await (const chunk of stream) {
342
- const delta = chunk.choices[0]?.delta?.content;
343
- if (delta) {
344
- yield { delta };
345
- }
346
- if (chunk.usage) {
347
- yield {
348
- delta: "",
349
- tokenUsage: {
350
- input: chunk.usage.prompt_tokens,
351
- output: chunk.usage.completion_tokens
352
- }
353
- };
354
- }
275
+ for await (const o of e) {
276
+ const s = o.choices[0]?.delta?.content;
277
+ s && (yield { delta: s }), o.usage && (yield {
278
+ delta: "",
279
+ tokenUsage: {
280
+ input: o.usage.prompt_tokens,
281
+ output: o.usage.completion_tokens
282
+ }
283
+ });
355
284
  }
356
285
  }
357
286
  };
358
287
  }
359
- function createGoogleBackend(config) {
360
- const apiKey = config.apiKey ?? process.env.GOOGLE_API_KEY ?? "";
361
- const genAI = new GoogleGenerativeAI(apiKey);
288
+ function Me(n) {
289
+ const r = n.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new Ce(r);
362
290
  return {
363
291
  name: "google",
364
292
  defaultModel: "gemini-2.0-flash",
365
- async chat(request) {
366
- const model = genAI.getGenerativeModel({
367
- model: request.model,
368
- systemInstruction: request.systemPrompt,
293
+ async chat(e) {
294
+ const o = t.getGenerativeModel({
295
+ model: e.model,
296
+ systemInstruction: e.systemPrompt,
369
297
  generationConfig: {
370
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
298
+ ...e.temperature !== void 0 ? { temperature: e.temperature } : {}
371
299
  }
372
- });
373
- const history = request.messages.slice(0, -1).map((m) => ({
300
+ }), s = e.messages.slice(0, -1).map((m) => ({
374
301
  role: m.role === "assistant" ? "model" : "user",
375
302
  parts: [{ text: m.content }]
376
- }));
377
- const chat = model.startChat({ history });
378
- const lastMessage = request.messages[request.messages.length - 1];
379
- const result = await chat.sendMessage(lastMessage?.content ?? "");
380
- const response = result.response;
303
+ })), a = o.startChat({ history: s }), i = e.messages[e.messages.length - 1], c = (await a.sendMessage(i?.content ?? "")).response;
381
304
  return {
382
- content: response.text(),
383
- tokenUsage: response.usageMetadata ? {
384
- input: response.usageMetadata.promptTokenCount ?? 0,
385
- output: response.usageMetadata.candidatesTokenCount ?? 0
305
+ content: c.text(),
306
+ tokenUsage: c.usageMetadata ? {
307
+ input: c.usageMetadata.promptTokenCount ?? 0,
308
+ output: c.usageMetadata.candidatesTokenCount ?? 0
386
309
  } : void 0
387
310
  };
388
311
  },
389
- async *chatStream(request) {
390
- const genModel = genAI.getGenerativeModel({
391
- model: request.model,
392
- systemInstruction: request.systemPrompt,
312
+ async *chatStream(e) {
313
+ const o = t.getGenerativeModel({
314
+ model: e.model,
315
+ systemInstruction: e.systemPrompt,
393
316
  generationConfig: {
394
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
317
+ ...e.temperature !== void 0 ? { temperature: e.temperature } : {}
395
318
  }
396
- });
397
- const history = request.messages.slice(0, -1).map((m) => ({
319
+ }), s = e.messages.slice(0, -1).map((m) => ({
398
320
  role: m.role === "assistant" ? "model" : "user",
399
321
  parts: [{ text: m.content }]
400
- }));
401
- const chat = genModel.startChat({ history });
402
- const lastMessage = request.messages[request.messages.length - 1];
403
- const result = await chat.sendMessageStream(lastMessage?.content ?? "");
404
- for await (const chunk of result.stream) {
405
- const text = chunk.text();
406
- if (text) {
407
- yield { delta: text };
408
- }
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 });
409
326
  }
410
- const response = await result.response;
327
+ const c = await l.response;
411
328
  yield {
412
329
  delta: "",
413
- tokenUsage: response.usageMetadata ? {
414
- input: response.usageMetadata.promptTokenCount ?? 0,
415
- output: response.usageMetadata.candidatesTokenCount ?? 0
330
+ tokenUsage: c.usageMetadata ? {
331
+ input: c.usageMetadata.promptTokenCount ?? 0,
332
+ output: c.usageMetadata.candidatesTokenCount ?? 0
416
333
  } : void 0
417
334
  };
418
335
  }
419
336
  };
420
337
  }
421
- function createOllamaBackend(config) {
422
- const client = new Ollama({
423
- host: config.baseUrl ?? "http://localhost:11434"
338
+ function Je(n) {
339
+ const r = new Ne({
340
+ host: n.baseUrl ?? "http://localhost:11434"
424
341
  });
425
342
  return {
426
343
  name: "ollama",
427
344
  defaultModel: "llama3.2",
428
- async chat(request) {
429
- const response = await client.chat({
430
- model: request.model,
345
+ async chat(t) {
346
+ const e = await r.chat({
347
+ model: t.model,
431
348
  messages: [
432
- { role: "system", content: request.systemPrompt },
433
- ...request.messages.map((m) => ({
434
- role: m.role,
435
- content: m.content
349
+ { role: "system", content: t.systemPrompt },
350
+ ...t.messages.map((o) => ({
351
+ role: o.role,
352
+ content: o.content
436
353
  }))
437
354
  ],
438
355
  options: {
439
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
356
+ ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
440
357
  }
441
358
  });
442
359
  return {
443
- content: response.message.content,
444
- tokenUsage: response.prompt_eval_count !== void 0 ? {
445
- input: response.prompt_eval_count ?? 0,
446
- output: response.eval_count ?? 0
360
+ content: e.message.content,
361
+ tokenUsage: e.prompt_eval_count !== void 0 ? {
362
+ input: e.prompt_eval_count ?? 0,
363
+ output: e.eval_count ?? 0
447
364
  } : void 0
448
365
  };
449
366
  },
450
- async *chatStream(request) {
451
- const response = await client.chat({
452
- model: request.model,
367
+ async *chatStream(t) {
368
+ const e = await r.chat({
369
+ model: t.model,
453
370
  messages: [
454
- { role: "system", content: request.systemPrompt },
455
- ...request.messages.map((m) => ({
456
- role: m.role,
457
- content: m.content
371
+ { role: "system", content: t.systemPrompt },
372
+ ...t.messages.map((a) => ({
373
+ role: a.role,
374
+ content: a.content
458
375
  }))
459
376
  ],
460
377
  options: {
461
- ...request.temperature !== void 0 ? { temperature: request.temperature } : {}
378
+ ...t.temperature !== void 0 ? { temperature: t.temperature } : {}
462
379
  },
463
- stream: true
380
+ stream: !0
464
381
  });
465
- let promptEvalCount;
466
- let evalCount;
467
- for await (const chunk of response) {
468
- if (chunk.message.content) {
469
- yield { delta: chunk.message.content };
470
- }
471
- if (chunk.done) {
472
- promptEvalCount = chunk.prompt_eval_count;
473
- evalCount = chunk.eval_count;
474
- }
475
- }
382
+ let o, s;
383
+ 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);
476
385
  yield {
477
386
  delta: "",
478
- tokenUsage: promptEvalCount !== void 0 ? { input: promptEvalCount ?? 0, output: evalCount ?? 0 } : void 0
387
+ tokenUsage: o !== void 0 ? { input: o ?? 0, output: s ?? 0 } : void 0
479
388
  };
480
389
  }
481
390
  };
482
391
  }
483
- const factories = {
484
- anthropic: createAnthropicBackend,
485
- openai: createOpenAIBackend,
486
- google: createGoogleBackend,
487
- ollama: createOllamaBackend
392
+ const se = {
393
+ anthropic: We,
394
+ openai: Ye,
395
+ google: Me,
396
+ ollama: Je
488
397
  };
489
- let configCache = null;
490
- async function loadConfig() {
491
- if (configCache) return configCache;
492
- const configPath = join(homedir(), ".ai-council", "config.json");
398
+ let T = null;
399
+ async function He() {
400
+ if (T) return T;
401
+ const n = u(w(), ".ai-council", "config.json");
493
402
  try {
494
- const raw = await readFile(configPath, "utf-8");
495
- configCache = JSON.parse(raw);
403
+ const r = await y(n, "utf-8");
404
+ T = JSON.parse(r);
496
405
  } catch {
497
- configCache = { backends: {} };
406
+ T = { backends: {} };
498
407
  }
499
- return configCache;
408
+ return T;
500
409
  }
501
- const backendCache = /* @__PURE__ */ new Map();
502
- function clearCaches() {
503
- configCache = null;
504
- backendCache.clear();
410
+ const M = /* @__PURE__ */ new Map();
411
+ function ze() {
412
+ T = null, M.clear();
505
413
  }
506
- async function getBackend(name) {
507
- const cached = backendCache.get(name);
508
- if (cached) return cached;
509
- const factory = factories[name];
510
- if (!factory) {
511
- throw new Error(`Unknown backend: "${name}". Available: ${Object.keys(factories).join(", ")}`);
512
- }
513
- const config = await loadConfig();
514
- const backendConfig = config.backends[name] ?? {};
515
- const backend = factory(backendConfig);
516
- backendCache.set(name, backend);
517
- return backend;
414
+ async function G(n) {
415
+ const r = M.get(n);
416
+ if (r) return r;
417
+ const t = se[n];
418
+ if (!t)
419
+ throw new Error(`Unknown backend: "${n}". Available: ${Object.keys(se).join(", ")}`);
420
+ const o = (await He()).backends[n] ?? {}, s = t(o);
421
+ return M.set(n, s), s;
518
422
  }
519
- function getExcalidrawCheatsheet() {
520
- return `
521
- ## Excalidraw Element Reference
522
-
523
- Output a JSON array of Excalidraw elements. Each element needs these fields:
524
-
525
- ### Common Fields (all elements)
526
- - \`type\`: "rectangle" | "ellipse" | "diamond" | "text" | "arrow" | "line"
527
- - \`id\`: unique string (e.g. "rect1", "text1", "arrow1")
528
- - \`x\`, \`y\`: number (top-left origin, x increases right, y increases down)
529
- - \`width\`, \`height\`: number
530
- - \`strokeColor\`: hex string (e.g. "#1e1e1e")
531
- - \`backgroundColor\`: hex string or "transparent"
532
- - \`fillStyle\`: "solid" | "hachure" | "cross-hatch"
533
- - \`strokeWidth\`: 1 | 2 | 4
534
- - \`roughness\`: 0 (sharp) | 1 (sketchy)
535
- - \`opacity\`: 100
536
- - \`angle\`: 0
537
- - \`seed\`: any integer (e.g. 1)
538
- - \`version\`: 1
539
- - \`isDeleted\`: false
540
- - \`groupIds\`: []
541
- - \`boundElements\`: null or array of { id: string, type: "text" | "arrow" }
542
- - \`link\`: null
543
- - \`locked\`: false
544
-
545
- ### Text Elements
546
- Additional 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
547
-
548
- ### Arrow/Line Elements
549
- Additional 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
550
-
551
- ### Color Palette
552
- - Blue: "#1971c2", Light blue bg: "#a5d8ff"
553
- - Green: "#2f9e44", Light green bg: "#b2f2bb"
554
- - Red: "#e03131", Light red bg: "#ffc9c9"
555
- - Orange: "#e8590c", Light orange bg: "#ffd8a8"
556
- - Purple: "#7048e8", Light purple bg: "#d0bfff"
557
- - Yellow: "#f08c00", Light yellow bg: "#ffec99"
558
- - Gray: "#868e96", Light gray bg: "#dee2e6"
559
- - Dark: "#1e1e1e"
560
-
561
- ### Layout Tips
562
- - Space shapes ~200px apart horizontally, ~150px vertically
563
- - Typical shape size: 160×80 for rectangles, 120×60 for ellipses
564
- - Center text inside shapes using containerId
565
- - Use arrows to show relationships (agreement, disagreement, influence)
566
-
567
- ### Compact Example
568
- \`\`\`json
569
- [
570
- {"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},
571
- {"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},
572
- {"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},
573
- {"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},
574
- {"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}
575
- ]
576
- \`\`\`
577
- `.trim();
423
+ function Ve() {
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();
578
425
  }
579
- const EXCALIDRAW_DELIMITER = "---EXCALIDRAW---";
580
- const DEFAULT_SYSTEM_PROMPT = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
426
+ const F = "---EXCALIDRAW---", Xe = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
581
427
 
582
428
  Structure your summary with these sections:
583
429
 
@@ -594,538 +440,402 @@ Where did they disagree? What are the key tensions?
594
440
  What are the most important takeaways? What would you recommend based on the full discussion?
595
441
 
596
442
  Be concise but thorough. Use markdown formatting.`;
597
- function buildTranscript(result) {
598
- const lines = [];
599
- lines.push(`Topic: ${result.topic}`);
600
- lines.push(`Counsellors: ${result.counsellors.map((c) => c.name).join(", ")}`);
601
- lines.push(`Rounds: ${result.rounds}`);
602
- lines.push("");
603
- let currentRound = 0;
604
- for (const turn of result.turns) {
605
- if (turn.round !== currentRound) {
606
- currentRound = turn.round;
607
- lines.push(`--- Round ${currentRound} ---`);
608
- lines.push("");
609
- }
610
- lines.push(`[${turn.counsellorName}]:`);
611
- lines.push(turn.content);
612
- lines.push("");
613
- }
614
- return lines.join("\n");
443
+ function Qe(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("");
446
+ 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(`
450
+ `);
615
451
  }
616
- async function runSecretary({
617
- result,
618
- config,
619
- onChunk,
620
- signal
452
+ async function Ze({
453
+ result: n,
454
+ config: r,
455
+ onChunk: t,
456
+ signal: e
621
457
  }) {
622
- const secretaryConfig = config.secretary;
623
- if (!secretaryConfig?.backend) {
458
+ const o = r.secretary;
459
+ if (!o?.backend)
624
460
  throw new Error("No secretary backend configured");
625
- }
626
- const backend = await getBackend(secretaryConfig.backend);
627
- const model = secretaryConfig.model ?? backend.defaultModel;
628
- const basePrompt = secretaryConfig.systemPrompt || DEFAULT_SYSTEM_PROMPT;
629
- const cheatsheet = getExcalidrawCheatsheet();
630
- const systemPrompt = `${basePrompt}
461
+ const s = await G(o.backend), a = o.model ?? s.defaultModel, i = o.systemPrompt || Xe, l = Ve(), c = `${i}
631
462
 
632
- ${cheatsheet}
463
+ ${l}
633
464
 
634
- After your text summary, output \`${EXCALIDRAW_DELIMITER}\` 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.`;
635
- const transcript = buildTranscript(result);
636
- const chatRequest = {
637
- model,
638
- systemPrompt,
465
+ After your text summary, output \`${F}\` 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 = Qe(n), g = {
466
+ model: a,
467
+ systemPrompt: c,
639
468
  messages: [{ role: "user", content: `Please summarize this council discussion and create a position diagram:
640
469
 
641
- ${transcript}` }],
470
+ ${m}` }],
642
471
  temperature: 0.5
643
472
  };
644
- let fullResponse = "";
645
- if (backend.chatStream) {
646
- for await (const chunk of backend.chatStream(chatRequest)) {
647
- if (signal?.aborted) break;
648
- fullResponse += chunk.delta;
649
- if (chunk.delta && onChunk) {
650
- onChunk(chunk.delta);
651
- }
473
+ let f = "";
474
+ if (s.chatStream)
475
+ for await (const E of s.chatStream(g)) {
476
+ if (e?.aborted) break;
477
+ f += E.delta, E.delta && t && t(E.delta);
652
478
  }
653
- } else {
654
- const response = await backend.chat(chatRequest);
655
- fullResponse = response.content;
656
- if (onChunk) onChunk(fullResponse);
657
- }
658
- const delimiterIndex = fullResponse.indexOf(EXCALIDRAW_DELIMITER);
659
- if (delimiterIndex === -1) {
660
- return { text: fullResponse.trim() };
661
- }
662
- const text = fullResponse.slice(0, delimiterIndex).trim();
663
- const diagramRaw = fullResponse.slice(delimiterIndex + EXCALIDRAW_DELIMITER.length).trim();
664
- let diagram;
479
+ else
480
+ f = (await s.chat(g)).content, t && t(f);
481
+ const p = f.indexOf(F);
482
+ if (p === -1)
483
+ return { text: f.trim() };
484
+ const d = f.slice(0, p).trim(), h = f.slice(p + F.length).trim();
485
+ let b;
665
486
  try {
666
- const jsonMatch = diagramRaw.match(/\[[\s\S]*\]/);
667
- if (jsonMatch) {
668
- diagram = JSON.parse(jsonMatch[0]);
669
- }
487
+ const E = h.match(/\[[\s\S]*\]/);
488
+ E && (b = JSON.parse(E[0]));
670
489
  } catch {
671
490
  }
672
- return { text, diagram };
491
+ return { text: d, diagram: b };
673
492
  }
674
- const INTERIM_SYSTEM_PROMPT = `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.`;
675
- async function runInterimSummary({
676
- result,
677
- roundNumber,
678
- config,
679
- onChunk,
680
- signal
493
+ const qe = "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 et({
495
+ result: n,
496
+ roundNumber: r,
497
+ config: t,
498
+ onChunk: e,
499
+ signal: o
681
500
  }) {
682
- const secretaryConfig = config.secretary;
683
- if (!secretaryConfig?.backend) {
501
+ const s = t.secretary;
502
+ if (!s?.backend)
684
503
  throw new Error("No secretary backend configured");
685
- }
686
- const backend = await getBackend(secretaryConfig.backend);
687
- const model = secretaryConfig.model ?? backend.defaultModel;
688
- const roundTurns = result.turns.filter((t) => t.round === roundNumber);
689
- const lines = [];
690
- lines.push(`Topic: ${result.topic}`);
691
- lines.push(`Round ${roundNumber}${roundNumber === 1 ? " (Constructive)" : " (Rebuttal)"}`);
692
- lines.push("");
693
- for (const turn of roundTurns) {
694
- lines.push(`[${turn.counsellorName}]:`);
695
- lines.push(turn.content);
696
- lines.push("");
697
- }
698
- const chatRequest = {
699
- model,
700
- systemPrompt: INTERIM_SYSTEM_PROMPT,
504
+ const a = await G(s.backend), i = s.model ?? a.defaultModel, l = n.turns.filter((f) => f.round === r), c = [];
505
+ c.push(`Topic: ${n.topic}`), c.push(`Round ${r}${r === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
506
+ for (const f of l)
507
+ c.push(`[${f.counsellorName}]:`), c.push(f.content), c.push("");
508
+ const m = {
509
+ model: i,
510
+ systemPrompt: qe,
701
511
  messages: [{ role: "user", content: `Please summarize this round:
702
512
 
703
- ${lines.join("\n")}` }],
513
+ ${c.join(`
514
+ `)}` }],
704
515
  temperature: 0.5
705
516
  };
706
- let fullResponse = "";
707
- if (backend.chatStream) {
708
- for await (const chunk of backend.chatStream(chatRequest)) {
709
- if (signal?.aborted) break;
710
- fullResponse += chunk.delta;
711
- if (chunk.delta && onChunk) {
712
- onChunk(chunk.delta);
713
- }
517
+ let g = "";
518
+ if (a.chatStream)
519
+ for await (const f of a.chatStream(m)) {
520
+ if (o?.aborted) break;
521
+ g += f.delta, f.delta && e && e(f.delta);
714
522
  }
715
- } else {
716
- const response = await backend.chat(chatRequest);
717
- fullResponse = response.content;
718
- if (onChunk) onChunk(fullResponse);
719
- }
720
- return fullResponse.trim();
523
+ else
524
+ g = (await a.chat(m)).content, e && e(g);
525
+ return g.trim();
721
526
  }
722
- async function generateTitle({
723
- topic,
724
- firstRoundTurns,
725
- config
527
+ async function tt({
528
+ topic: n,
529
+ firstRoundTurns: r,
530
+ config: t
726
531
  }) {
727
- const secretaryConfig = config.secretary;
728
- if (!secretaryConfig?.backend) {
532
+ const e = t.secretary;
533
+ if (!e?.backend)
729
534
  throw new Error("No secretary backend configured");
730
- }
731
- const backend = await getBackend(secretaryConfig.backend);
732
- const model = secretaryConfig.model ?? backend.defaultModel;
733
- const turnsSummary = firstRoundTurns.map((t) => `[${t.counsellorName}]: ${t.content.slice(0, 300)}`).join("\n\n");
734
- const response = await backend.chat({
735
- model,
535
+ const o = await G(e.backend), s = e.model ?? o.defaultModel, a = r.map((l) => `[${l.counsellorName}]: ${l.content.slice(0, 300)}`).join(`
536
+
537
+ `);
538
+ return (await o.chat({
539
+ model: s,
736
540
  systemPrompt: "Generate a concise title (max 8 words) for this council discussion. Return only the title, no quotes or punctuation at the end.",
737
541
  messages: [
738
542
  {
739
543
  role: "user",
740
- content: `Topic: ${topic}
544
+ content: `Topic: ${n}
741
545
 
742
546
  First round:
743
- ${turnsSummary}`
547
+ ${a}`
744
548
  }
745
549
  ],
746
550
  temperature: 0.3
747
- });
748
- return response.content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
551
+ })).content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
749
552
  }
750
- const LOG_DIR = join(homedir(), ".ai-council");
751
- const LOG_FILE = join(LOG_DIR, "council.log");
752
- let ensured = false;
753
- async function ensureDir() {
754
- if (ensured) return;
755
- await mkdir(LOG_DIR, { recursive: true });
756
- ensured = true;
553
+ const he = u(w(), ".ai-council"), nt = u(he, "council.log");
554
+ let re = !1;
555
+ async function ot() {
556
+ re || (await N(he, { recursive: !0 }), re = !0);
757
557
  }
758
- function formatEntry(level, context, message, extra) {
759
- const ts = (/* @__PURE__ */ new Date()).toISOString();
760
- let line = `[${ts}] ${level} [${context}] ${message}`;
761
- if (extra !== void 0) {
762
- const detail = extra instanceof Error ? `${extra.message}
763
- ${extra.stack ?? ""}` : typeof extra === "string" ? extra : JSON.stringify(extra, null, 2);
764
- line += `
765
- ${detail.replace(/\n/g, "\n ")}`;
558
+ function st(n, r, t, e) {
559
+ let s = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${n} [${r}] ${t}`;
560
+ if (e !== void 0) {
561
+ const a = e instanceof Error ? `${e.message}
562
+ ${e.stack ?? ""}` : typeof e == "string" ? e : JSON.stringify(e, null, 2);
563
+ s += `
564
+ ${a.replace(/\n/g, `
565
+ `)}`;
766
566
  }
767
- return line + "\n";
567
+ return s + `
568
+ `;
768
569
  }
769
- async function write(level, context, message, extra) {
570
+ async function W(n, r, t, e) {
770
571
  try {
771
- await ensureDir();
772
- await appendFile(LOG_FILE, formatEntry(level, context, message, extra));
572
+ await ot(), await Oe(nt, st(n, r, t, e));
773
573
  } catch {
774
574
  }
775
575
  }
776
- const log$1 = {
777
- info: (context, message, extra) => write("INFO", context, message, extra),
778
- warn: (context, message, extra) => write("WARN", context, message, extra),
779
- error: (context, message, extra) => write("ERROR", context, message, extra)
576
+ const O = {
577
+ info: (n, r, t) => W("INFO", n, r, t),
578
+ warn: (n, r, t) => W("WARN", n, r, t),
579
+ error: (n, r, t) => W("ERROR", n, r, t)
780
580
  };
781
- function buildMessages(topic, turns, currentCounsellorId) {
782
- const messages = [{ role: "user", content: topic }];
783
- for (const turn of turns) {
784
- if (turn.counsellorId === currentCounsellorId) {
785
- messages.push({ role: "assistant", content: turn.content });
786
- } else {
787
- messages.push({
788
- role: "user",
789
- content: `[${turn.counsellorName}, Round ${turn.round}]: ${turn.content}`
790
- });
791
- }
792
- }
793
- return messages;
581
+ function rt(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({
585
+ role: "user",
586
+ content: `[${o.counsellorName}, Round ${o.round}]: ${o.content}`
587
+ });
588
+ return e;
794
589
  }
795
- function buildDebateMessages(topic, turns, currentCounsellorId, currentRound) {
796
- const messages = [{ role: "user", content: topic }];
797
- if (currentRound === 1) {
798
- return messages;
799
- }
800
- const constructiveTurns = turns.filter((t) => t.round === 1);
801
- for (const turn of constructiveTurns) {
802
- if (turn.counsellorId === currentCounsellorId) {
803
- messages.push({ role: "assistant", content: turn.content });
804
- } else {
805
- messages.push({
590
+ function at(n, r, t, e) {
591
+ const o = [{ role: "user", content: n }];
592
+ 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({
597
+ role: "user",
598
+ content: `[${i.counsellorName}, Constructive]: ${i.content}`
599
+ });
600
+ const a = e - 1;
601
+ 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({
806
605
  role: "user",
807
- content: `[${turn.counsellorName}, Constructive]: ${turn.content}`
606
+ content: `[${l.counsellorName}, Round ${a}]: ${l.content}`
808
607
  });
809
- }
810
608
  }
811
- const prevRound = currentRound - 1;
812
- if (prevRound > 1) {
813
- const prevTurns = turns.filter((t) => t.round === prevRound);
814
- for (const turn of prevTurns) {
815
- if (turn.counsellorId === currentCounsellorId) {
816
- messages.push({ role: "assistant", content: turn.content });
817
- } else {
818
- messages.push({
819
- role: "user",
820
- content: `[${turn.counsellorName}, Round ${prevRound}]: ${turn.content}`
821
- });
822
- }
823
- }
824
- }
825
- for (const turn of turns) {
826
- if (turn.counsellorId === currentCounsellorId && turn.round !== 1 && turn.round !== prevRound && turn.round < currentRound) {
827
- messages.push({ role: "assistant", content: turn.content });
828
- }
829
- }
830
- return messages;
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;
831
612
  }
832
- function shuffleWithSeed(array, seed) {
833
- const result = [...array];
834
- let s = seed | 0;
835
- const rand = () => {
836
- s = s + 1831565813 | 0;
837
- let t = Math.imul(s ^ s >>> 15, 1 | s);
838
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
839
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
613
+ function it(n, r) {
614
+ const t = [...n];
615
+ let e = r | 0;
616
+ const o = () => {
617
+ 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;
840
620
  };
841
- for (let i = result.length - 1; i > 0; i--) {
842
- const j = Math.floor(rand() * (i + 1));
843
- [result[i], result[j]] = [result[j], result[i]];
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]];
844
624
  }
845
- return result;
625
+ return t;
846
626
  }
847
- function buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries) {
627
+ function Y(n, r, t, e, o, s) {
848
628
  return {
849
- topic: opts.topic,
850
- topicSource: opts.topicSource,
851
- counsellors: opts.counsellors.map((c) => ({
852
- id: c.id,
853
- name: c.frontmatter.name,
854
- description: c.frontmatter.description,
855
- backend: c.frontmatter.backend,
856
- model: c.frontmatter.model ?? "default",
857
- avatarUrl: c.avatarUrl
629
+ topic: n.topic,
630
+ topicSource: n.topicSource,
631
+ counsellors: n.counsellors.map((a) => ({
632
+ id: a.id,
633
+ name: a.frontmatter.name,
634
+ description: a.frontmatter.description,
635
+ backend: a.frontmatter.backend,
636
+ model: a.frontmatter.model ?? "default",
637
+ avatarUrl: a.avatarUrl
858
638
  })),
859
- rounds: opts.rounds,
860
- turns,
861
- startedAt,
639
+ rounds: n.rounds,
640
+ turns: r,
641
+ startedAt: t,
862
642
  completedAt: (/* @__PURE__ */ new Date()).toISOString(),
863
- totalTokenUsage: { input: totalInput, output: totalOutput },
864
- ...roundSummaries && Object.keys(roundSummaries).length > 0 ? { roundSummaries } : {},
865
- ...opts.mode === "debate" ? { mode: "debate" } : {}
643
+ totalTokenUsage: { input: e, output: o },
644
+ ...s && Object.keys(s).length > 0 ? { roundSummaries: s } : {},
645
+ ...n.mode === "debate" ? { mode: "debate" } : {}
866
646
  };
867
647
  }
868
- async function runConversation(topicOrOpts, topicSource, counsellors, rounds, onEvent) {
869
- let opts;
870
- if (typeof topicOrOpts === "string") {
871
- opts = {
872
- topic: topicOrOpts,
873
- topicSource,
874
- counsellors,
875
- rounds,
876
- onEvent
877
- };
878
- } else {
879
- opts = topicOrOpts;
880
- }
881
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
882
- const turns = [];
883
- let totalInput = 0;
884
- let totalOutput = 0;
885
- const isDebate = opts.mode === "debate";
886
- const roundSummaries = {};
887
- log$1.info("conversation", `Starting ${isDebate ? "debate" : "freeform"} — ${opts.counsellors.length} counsellors, ${opts.rounds} rounds`, {
888
- counsellors: opts.counsellors.map((c) => `${c.frontmatter.name} (${c.frontmatter.backend}/${c.frontmatter.model ?? "default"})`),
889
- topic: opts.topic.slice(0, 200)
648
+ async function ct(n, r, t, e, o) {
649
+ let s;
650
+ typeof n == "string" ? s = {
651
+ topic: n,
652
+ topicSource: r,
653
+ counsellors: t,
654
+ rounds: e,
655
+ onEvent: o
656
+ } : s = n;
657
+ 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((f) => `${f.frontmatter.name} (${f.frontmatter.backend}/${f.frontmatter.model ?? "default"})`),
662
+ topic: s.topic.slice(0, 200)
890
663
  });
891
- for (let round = 1; round <= opts.rounds; round++) {
892
- const roundCounsellors = isDebate && round > 1 ? shuffleWithSeed(opts.counsellors, round) : opts.counsellors;
893
- for (const counsellor of roundCounsellors) {
894
- if (opts.signal?.aborted) {
895
- return buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries);
896
- }
897
- if (opts.beforeTurn) {
898
- const injected = await opts.beforeTurn();
899
- if (injected) {
900
- turns.push(injected);
901
- opts.onEvent({ type: "turn_complete", turn: injected });
902
- }
664
+ for (let f = 1; f <= s.rounds; f++) {
665
+ const p = m && f > 1 ? it(s.counsellors, f) : s.counsellors;
666
+ for (const d of p) {
667
+ if (s.signal?.aborted)
668
+ return Y(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 }));
903
672
  }
904
- opts.onEvent({ type: "turn_start", round, counsellorName: counsellor.frontmatter.name });
673
+ s.onEvent({ type: "turn_start", round: f, counsellorName: d.frontmatter.name });
905
674
  try {
906
- const backend = await getBackend(counsellor.frontmatter.backend);
907
- const model = counsellor.frontmatter.model ?? backend.defaultModel;
908
- const messages = isDebate ? buildDebateMessages(opts.topic, turns, counsellor.id, round) : buildMessages(opts.topic, turns, counsellor.id);
909
- const chatRequest = {
910
- model,
911
- systemPrompt: counsellor.systemPrompt,
912
- messages,
913
- temperature: counsellor.frontmatter.temperature
675
+ const h = await G(d.frontmatter.backend), b = d.frontmatter.model ?? h.defaultModel, E = m ? at(s.topic, i, d.id, f) : rt(s.topic, i, d.id), Q = {
676
+ model: b,
677
+ systemPrompt: d.systemPrompt,
678
+ messages: E,
679
+ temperature: d.frontmatter.temperature
914
680
  };
915
- let content;
916
- let tokenUsage;
917
- if (backend.chatStream) {
918
- content = "";
919
- for await (const chunk of backend.chatStream(chatRequest)) {
920
- if (opts.signal?.aborted) break;
921
- content += chunk.delta;
922
- if (chunk.delta) {
923
- opts.onEvent({ type: "turn_chunk", counsellorName: counsellor.frontmatter.name, delta: chunk.delta });
924
- }
925
- if (chunk.tokenUsage) {
926
- tokenUsage = chunk.tokenUsage;
927
- }
681
+ let L, x;
682
+ if (h.chatStream) {
683
+ L = "";
684
+ for await (const S of h.chatStream(Q)) {
685
+ if (s.signal?.aborted) break;
686
+ L += S.delta, S.delta && s.onEvent({ type: "turn_chunk", counsellorName: d.frontmatter.name, delta: S.delta }), S.tokenUsage && (x = S.tokenUsage);
928
687
  }
929
688
  } else {
930
- const response = await backend.chat(chatRequest);
931
- content = response.content;
932
- tokenUsage = response.tokenUsage;
689
+ const S = await h.chat(Q);
690
+ L = S.content, x = S.tokenUsage;
933
691
  }
934
- const turn = {
935
- round,
936
- counsellorId: counsellor.id,
937
- counsellorName: counsellor.frontmatter.name,
938
- content,
692
+ const Z = {
693
+ round: f,
694
+ counsellorId: d.id,
695
+ counsellorName: d.frontmatter.name,
696
+ content: L,
939
697
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
940
- model,
941
- backend: counsellor.frontmatter.backend,
942
- tokenUsage,
943
- avatarUrl: counsellor.avatarUrl
698
+ model: b,
699
+ backend: d.frontmatter.backend,
700
+ tokenUsage: x,
701
+ avatarUrl: d.avatarUrl
944
702
  };
945
- if (tokenUsage) {
946
- totalInput += tokenUsage.input;
947
- totalOutput += tokenUsage.output;
948
- }
949
- turns.push(turn);
950
- opts.onEvent({ type: "turn_complete", turn });
951
- } catch (err) {
952
- const message = err instanceof Error ? err.message : String(err);
953
- log$1.error("conversation", `Turn failed for ${counsellor.frontmatter.name} (round ${round}, model ${counsellor.frontmatter.model ?? "default"}, backend ${counsellor.frontmatter.backend})`, err);
954
- opts.onEvent({ type: "error", counsellorName: counsellor.frontmatter.name, error: message });
703
+ x && (l += x.input, c += x.output), i.push(Z), s.onEvent({ type: "turn_complete", turn: Z });
704
+ } catch (h) {
705
+ const b = h instanceof Error ? h.message : String(h);
706
+ O.error("conversation", `Turn failed for ${d.frontmatter.name} (round ${f}, model ${d.frontmatter.model ?? "default"}, backend ${d.frontmatter.backend})`, h), s.onEvent({ type: "error", counsellorName: d.frontmatter.name, error: b });
955
707
  }
956
708
  }
957
- opts.onEvent({ type: "round_complete", round });
958
- if (isDebate && opts.config?.secretary?.backend && !opts.signal?.aborted) {
709
+ if (s.onEvent({ type: "round_complete", round: f }), m && s.config?.secretary?.backend && !s.signal?.aborted)
959
710
  try {
960
- const interimResult = buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries);
961
- opts.onEvent({ type: "round_summary_start", round });
962
- const summary = await runInterimSummary({
963
- result: interimResult,
964
- roundNumber: round,
965
- config: opts.config,
966
- onChunk: (delta) => {
967
- opts.onEvent({ type: "round_summary_chunk", round, delta });
711
+ const d = Y(s, i, a, l, c, g);
712
+ s.onEvent({ type: "round_summary_start", round: f });
713
+ const h = await et({
714
+ result: d,
715
+ roundNumber: f,
716
+ config: s.config,
717
+ onChunk: (b) => {
718
+ s.onEvent({ type: "round_summary_chunk", round: f, delta: b });
968
719
  },
969
- signal: opts.signal
720
+ signal: s.signal
970
721
  });
971
- roundSummaries[round] = summary;
972
- opts.onEvent({ type: "round_summary_complete", round, summary });
973
- } catch (err) {
974
- log$1.error("conversation", `Interim summary failed for round ${round}`, err);
722
+ g[f] = h, s.onEvent({ type: "round_summary_complete", round: f, summary: h });
723
+ } catch (d) {
724
+ O.error("conversation", `Interim summary failed for round ${f}`, d);
975
725
  }
976
- }
977
726
  }
978
- return buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries);
727
+ return Y(s, i, a, l, c, g);
979
728
  }
980
- const HISTORY_DIR = join(homedir(), ".ai-council", "history");
981
- function slugify(text) {
982
- return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
729
+ const I = u(w(), ".ai-council", "history");
730
+ function lt(n) {
731
+ return n.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
983
732
  }
984
- async function saveToHistory(result) {
985
- await mkdir(HISTORY_DIR, { recursive: true });
986
- const timestamp = new Date(result.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19);
987
- const slug = slugify(result.topic);
988
- const id = `${timestamp}-${slug}`;
989
- const filePath = join(HISTORY_DIR, `${id}.json`);
990
- await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
991
- return id;
733
+ async function ut(n) {
734
+ await N(I, { recursive: !0 });
735
+ const r = new Date(n.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = lt(n.topic), e = `${r}-${t}`, o = u(I, `${e}.json`);
736
+ return await $(o, JSON.stringify(n, null, 2), "utf-8"), e;
992
737
  }
993
- async function listHistory() {
994
- await mkdir(HISTORY_DIR, { recursive: true });
995
- const files = await readdir(HISTORY_DIR);
996
- const entries = [];
997
- for (const file of files) {
998
- if (!file.endsWith(".json")) continue;
999
- try {
1000
- const raw = await readFile(join(HISTORY_DIR, file), "utf-8");
1001
- const result = JSON.parse(raw);
1002
- entries.push({
1003
- id: basename(file, ".json"),
1004
- topic: result.topic,
1005
- title: result.title,
1006
- counsellors: result.counsellors.map((c) => c.name),
1007
- rounds: result.rounds,
1008
- startedAt: result.startedAt,
1009
- completedAt: result.completedAt
1010
- });
1011
- } catch {
1012
- }
1013
- }
1014
- return entries.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
738
+ async function dt() {
739
+ await N(I, { recursive: !0 });
740
+ const n = await H(I), r = [];
741
+ for (const t of n)
742
+ if (t.endsWith(".json"))
743
+ try {
744
+ const e = await y(u(I, t), "utf-8"), o = JSON.parse(e);
745
+ r.push({
746
+ 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
753
+ });
754
+ } catch {
755
+ }
756
+ return r.sort((t, e) => e.startedAt.localeCompare(t.startedAt));
1015
757
  }
1016
- async function getHistoryEntry(id) {
1017
- const filePath = join(HISTORY_DIR, `${id}.json`);
1018
- const raw = await readFile(filePath, "utf-8");
1019
- const result = JSON.parse(raw);
1020
- if (result.infographic && !result.infographics) {
1021
- result.infographics = [result.infographic];
1022
- delete result.infographic;
1023
- }
1024
- return result;
758
+ async function ae(n) {
759
+ const r = u(I, `${n}.json`), t = await y(r, "utf-8"), e = JSON.parse(t);
760
+ return e.infographic && !e.infographics && (e.infographics = [e.infographic], delete e.infographic), e;
1025
761
  }
1026
- async function deleteHistoryEntry(id) {
1027
- const filePath = join(HISTORY_DIR, `${id}.json`);
1028
- await rm(filePath);
762
+ async function mt(n) {
763
+ const r = u(I, `${n}.json`);
764
+ await B(r);
1029
765
  }
1030
- async function addInfographicToHistory(id, infographic) {
1031
- const filePath = join(HISTORY_DIR, `${id}.json`);
1032
- const raw = await readFile(filePath, "utf-8");
1033
- const result = JSON.parse(raw);
1034
- if (!result.infographics) result.infographics = [];
1035
- result.infographics.push(infographic);
1036
- await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
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");
1037
769
  }
1038
- async function deleteInfographicFromHistory(id, index) {
1039
- const filePath = join(HISTORY_DIR, `${id}.json`);
1040
- const raw = await readFile(filePath, "utf-8");
1041
- const result = JSON.parse(raw);
1042
- if (result.infographics && index >= 0 && index < result.infographics.length) {
1043
- result.infographics.splice(index, 1);
1044
- }
1045
- await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
770
+ async function pt(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");
1046
773
  }
1047
- const require$1 = createRequire(import.meta.url);
1048
- function buildPrompt(result) {
1049
- const names = result.counsellors.map((c) => c.name).join(", ");
1050
- const summary = result.summary ?? result.turns.map((t) => `${t.counsellorName}: ${t.content.slice(0, 200)}`).join("\n");
774
+ const ge = xe(import.meta.url);
775
+ function ht(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(`
777
+ `);
1051
778
  return [
1052
779
  "Create a professional infographic summarizing a panel discussion.",
1053
- `Topic: ${result.topic.slice(0, 300)}`,
1054
- `Key points: ${summary.slice(0, 1500)}`,
1055
- `Panelists: ${names}`,
780
+ `Topic: ${n.topic.slice(0, 300)}`,
781
+ `Key points: ${t.slice(0, 1500)}`,
782
+ `Panelists: ${r}`,
1056
783
  "Use a clean, modern design with sections for convergence points, divergence points, and key takeaways.",
1057
784
  "Include relevant icons and visual hierarchy. Use a horizontal landscape layout."
1058
785
  ].join(" ");
1059
786
  }
1060
- function detectBackend(config) {
1061
- if (config.infographic?.backend) return config.infographic.backend;
1062
- const hasGoogle = !!(config.backends.google?.apiKey || process.env.GOOGLE_API_KEY);
1063
- const hasOpenai = !!(config.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
1064
- if (hasGoogle) return "google";
1065
- if (hasOpenai) return "openai";
1066
- return null;
787
+ function gt(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;
1067
791
  }
1068
- async function generateViaOpenAI(prompt, config) {
1069
- const OpenAI2 = require$1("openai").default;
1070
- const client = new OpenAI2({
1071
- apiKey: config.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
1072
- ...config.backends.openai?.baseUrl ? { baseURL: config.backends.openai.baseUrl } : {}
1073
- });
1074
- const response = await client.images.generate({
792
+ async function yt(n, r) {
793
+ const t = ge("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 } : {}
796
+ }).images.generate({
1075
797
  model: "gpt-image-1",
1076
- prompt,
798
+ prompt: n,
1077
799
  quality: "high",
1078
800
  size: "1536x1024"
1079
- });
1080
- const b64 = response.data?.[0]?.b64_json;
1081
- if (!b64) throw new Error("No image data returned from OpenAI");
1082
- return b64;
801
+ })).data?.[0]?.b64_json;
802
+ if (!s) throw new Error("No image data returned from OpenAI");
803
+ return s;
1083
804
  }
1084
- async function generateViaGoogle(prompt, config) {
1085
- const { GoogleGenAI } = require$1("@google/genai");
1086
- const apiKey = config.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
1087
- if (!apiKey) throw new Error("No Google API key configured");
1088
- const ai = new GoogleGenAI({ apiKey });
1089
- const response = await ai.models.generateContent({
805
+ async function wt(n, r) {
806
+ const { GoogleGenAI: t } = ge("@google/genai"), e = r.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
807
+ if (!e) throw new Error("No Google API key configured");
808
+ const a = (await new t({ apiKey: e }).models.generateContent({
1090
809
  model: "gemini-3-pro-image-preview",
1091
- contents: prompt,
810
+ contents: n,
1092
811
  config: {
1093
812
  responseModalities: ["IMAGE", "TEXT"]
1094
813
  }
1095
- });
1096
- const parts = response.candidates?.[0]?.content?.parts;
1097
- if (!parts) throw new Error("No response parts from Gemini");
1098
- for (const part of parts) {
1099
- if (part.inlineData?.data) {
1100
- return part.inlineData.data;
1101
- }
1102
- }
814
+ })).candidates?.[0]?.content?.parts;
815
+ if (!a) throw new Error("No response parts from Gemini");
816
+ for (const i of a)
817
+ if (i.inlineData?.data)
818
+ return i.inlineData.data;
1103
819
  throw new Error("No image data in Gemini response");
1104
820
  }
1105
- async function generateInfographic(result, config, backendOverride) {
1106
- const backend = backendOverride ?? detectBackend(config);
1107
- if (!backend) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
1108
- const prompt = buildPrompt(result);
1109
- if (backend === "openai") {
1110
- return generateViaOpenAI(prompt, config);
1111
- } else {
1112
- return generateViaGoogle(prompt, config);
1113
- }
821
+ async function ie(n, r, t) {
822
+ const e = t ?? gt(r);
823
+ if (!e) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
824
+ const o = ht(n);
825
+ return e === "openai" ? yt(o, r) : wt(o, r);
1114
826
  }
1115
- const DEFAULT_URLS = {
827
+ const ye = {
1116
828
  anthropic: "https://api.anthropic.com",
1117
829
  openai: "https://api.openai.com/v1",
1118
830
  google: "https://generativelanguage.googleapis.com",
1119
831
  ollama: "http://localhost:11434"
1120
- };
1121
- const KNOWN_ANTHROPIC_MODELS = [
832
+ }, ce = [
1122
833
  "claude-opus-4-20250514",
1123
834
  "claude-sonnet-4-5-20250514",
1124
835
  "claude-sonnet-4-20250514",
1125
836
  "claude-haiku-4-20250414",
1126
837
  "claude-3-5-haiku-20241022"
1127
- ];
1128
- const KNOWN_GOOGLE_MODELS = [
838
+ ], D = [
1129
839
  "gemini-2.5-pro",
1130
840
  "gemini-2.5-flash",
1131
841
  "gemini-2.0-flash",
@@ -1133,80 +843,65 @@ const KNOWN_GOOGLE_MODELS = [
1133
843
  "gemini-1.5-pro",
1134
844
  "gemini-1.5-flash"
1135
845
  ];
1136
- async function probeBackend(name, config) {
846
+ async function bt(n, r) {
1137
847
  try {
1138
- switch (name) {
848
+ switch (n) {
1139
849
  case "ollama": {
1140
- const { Ollama: Ollama2 } = await import("ollama");
1141
- const client = new Ollama2({ host: config.baseUrl || DEFAULT_URLS.ollama });
1142
- const response = await client.list();
1143
- const models = response.models.map((m) => m.name).sort();
1144
- return { connected: true, models };
850
+ const { Ollama: t } = await import("ollama");
851
+ return { connected: !0, models: (await new t({ host: r.baseUrl || ye.ollama }).list()).models.map((a) => a.name).sort() };
1145
852
  }
1146
853
  case "openai": {
1147
- const { default: OpenAI2 } = await import("openai");
1148
- const client = new OpenAI2({
1149
- apiKey: config.apiKey || process.env.OPENAI_API_KEY,
1150
- ...config.baseUrl ? { baseURL: config.baseUrl } : {}
1151
- });
1152
- const response = await client.models.list();
1153
- const models = response.data.map((m) => m.id).filter((id) => id.startsWith("gpt-") || id.startsWith("o") || id.startsWith("chatgpt-")).sort();
1154
- return { connected: true, models };
854
+ const { default: t } = await import("openai");
855
+ return { connected: !0, models: (await new t({
856
+ apiKey: r.apiKey || process.env.OPENAI_API_KEY,
857
+ ...r.baseUrl ? { baseURL: r.baseUrl } : {}
858
+ }).models.list()).data.map((a) => a.id).filter((a) => a.startsWith("gpt-") || a.startsWith("o") || a.startsWith("chatgpt-")).sort() };
1155
859
  }
1156
860
  case "anthropic": {
1157
- const { default: Anthropic2 } = await import("@anthropic-ai/sdk");
1158
- const client = new Anthropic2({
1159
- apiKey: config.apiKey || process.env.ANTHROPIC_API_KEY,
1160
- ...config.baseUrl ? { baseURL: config.baseUrl } : {}
861
+ 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 } : {}
1161
864
  });
1162
865
  try {
1163
- const response = await client.models.list({ limit: 100 });
1164
- const models = response.data.map((m) => m.id).sort();
1165
- return { connected: true, models };
866
+ return { connected: !0, models: (await e.models.list({ limit: 100 })).data.map((a) => a.id).sort() };
1166
867
  } catch {
1167
- return { connected: true, models: KNOWN_ANTHROPIC_MODELS };
868
+ return { connected: !0, models: ce };
1168
869
  }
1169
870
  }
1170
871
  case "google": {
1171
- const apiKey = config.apiKey || process.env.GOOGLE_API_KEY || "";
1172
- if (!apiKey) return { connected: false, models: KNOWN_GOOGLE_MODELS, error: "No API key" };
1173
- const res = await fetch(
1174
- `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`
872
+ const t = r.apiKey || process.env.GOOGLE_API_KEY || "";
873
+ if (!t) return { connected: !1, models: D, error: "No API key" };
874
+ const e = await fetch(
875
+ `https://generativelanguage.googleapis.com/v1beta/models?key=${t}`
1175
876
  );
1176
- if (!res.ok) {
1177
- const body = await res.json().catch(() => ({}));
1178
- const msg = body?.error?.message || `HTTP ${res.status}`;
1179
- return { connected: false, models: KNOWN_GOOGLE_MODELS, error: msg };
877
+ if (!e.ok) {
878
+ const i = (await e.json().catch(() => ({})))?.error?.message || `HTTP ${e.status}`;
879
+ return { connected: !1, models: D, error: i };
1180
880
  }
1181
- const data = await res.json();
1182
- const models = (data.models || []).filter((m) => m.name.includes("gemini") && m.supportedGenerationMethods?.includes("generateContent")).map((m) => m.name.replace("models/", "")).sort();
1183
- return { connected: true, models: models.length > 0 ? models : KNOWN_GOOGLE_MODELS };
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 : D };
1184
883
  }
1185
884
  default:
1186
- return { connected: false, models: [], error: `Unknown backend: ${name}` };
885
+ return { connected: !1, models: [], error: `Unknown backend: ${n}` };
1187
886
  }
1188
- } catch (err) {
1189
- const error = err instanceof Error ? err.message : String(err);
1190
- const fallbackModels = name === "anthropic" ? KNOWN_ANTHROPIC_MODELS : name === "google" ? KNOWN_GOOGLE_MODELS : [];
1191
- return { connected: false, models: fallbackModels, error };
887
+ } catch (t) {
888
+ const e = t instanceof Error ? t.message : String(t);
889
+ return { connected: !1, models: n === "anthropic" ? ce : n === "google" ? D : [], error: e };
1192
890
  }
1193
891
  }
1194
- let activeAbortController = null;
1195
- let injectionBuffer = [];
1196
- function registerIpcHandlers(ipcMain2, getWindow) {
1197
- ipcMain2.handle("counsellors:list", async (_event, councilDir) => {
1198
- const configPath = join(homedir(), ".ai-council", "config.json");
1199
- let config = { backends: {} };
892
+ let A = null, K = [];
893
+ function kt(n, r) {
894
+ n.handle("counsellors:list", async (t, e) => {
895
+ const o = u(w(), ".ai-council", "config.json");
896
+ let s = { backends: {} };
1200
897
  try {
1201
- const raw = await readFile(configPath, "utf-8");
1202
- config = JSON.parse(raw);
898
+ const c = await y(o, "utf-8");
899
+ s = JSON.parse(c);
1203
900
  } catch {
1204
901
  }
1205
- const registeredPaths = getRegisteredPaths(config);
1206
- const registry = config.counsellors ?? {};
1207
- const counsellors = await loadCounsellors(councilDir, registeredPaths);
1208
- return counsellors.map((c) => {
1209
- const regEntry = registry[c.id];
902
+ const a = oe(s), i = s.counsellors ?? {};
903
+ return (await te(e, a)).map((c) => {
904
+ const m = i[c.id];
1210
905
  return {
1211
906
  id: c.id,
1212
907
  dirPath: c.dirPath,
@@ -1217,241 +912,148 @@ function registerIpcHandlers(ipcMain2, getWindow) {
1217
912
  temperature: c.frontmatter.temperature,
1218
913
  interests: c.frontmatter.interests,
1219
914
  avatarUrl: c.avatarUrl,
1220
- source: regEntry?.source,
1221
- registryUrl: regEntry?.url
915
+ source: m?.source,
916
+ registryUrl: m?.url
1222
917
  };
1223
918
  });
1224
- });
1225
- ipcMain2.handle("counsellors:get", async (_event, dirPath) => {
1226
- const aboutPath = join(dirPath, "ABOUT.md");
1227
- const raw = await readFile(aboutPath, "utf-8");
1228
- const { data, content } = matter(raw);
1229
- return { frontmatter: data, body: content.trim(), raw };
1230
- });
1231
- ipcMain2.handle("counsellors:save", async (_event, dirPath, aboutMd) => {
1232
- const aboutPath = join(dirPath, "ABOUT.md");
1233
- await writeFile(aboutPath, aboutMd, "utf-8");
1234
- return { success: true };
1235
- });
1236
- ipcMain2.handle("counsellors:create", async (_event, councilDir, id, aboutMd) => {
1237
- const dirPath = join(councilDir, id);
1238
- await mkdir(dirPath, { recursive: true });
1239
- await writeFile(join(dirPath, "ABOUT.md"), aboutMd, "utf-8");
1240
- return { success: true, dirPath };
1241
- });
1242
- ipcMain2.handle("counsellors:delete", async (_event, dirPath) => {
1243
- await rm(dirPath, { recursive: true, force: true });
1244
- return { success: true };
1245
- });
1246
- ipcMain2.handle("config:get", async () => {
1247
- const configPath = join(homedir(), ".ai-council", "config.json");
1248
- let config = { backends: {} };
919
+ }), n.handle("counsellors:get", async (t, e) => {
920
+ const o = u(e, "ABOUT.md"), s = await y(o, "utf-8"), { data: a, content: i } = z(s);
921
+ return { frontmatter: a, body: i.trim(), raw: s };
922
+ }), n.handle("counsellors:save", async (t, e, o) => {
923
+ const s = u(e, "ABOUT.md");
924
+ return await $(s, o, "utf-8"), { success: !0 };
925
+ }), n.handle("counsellors:create", async (t, e, o, s) => {
926
+ const a = u(e, o);
927
+ return await N(a, { recursive: !0 }), await $(u(a, "ABOUT.md"), s, "utf-8"), { success: !0, dirPath: a };
928
+ }), n.handle("counsellors:delete", async (t, e) => (await B(e, { recursive: !0, force: !0 }), { success: !0 })), n.handle("config:get", async () => {
929
+ const t = u(w(), ".ai-council", "config.json");
930
+ let e = { backends: {} };
1249
931
  try {
1250
- const raw = await readFile(configPath, "utf-8");
1251
- config = JSON.parse(raw);
932
+ const i = await y(t, "utf-8");
933
+ e = JSON.parse(i);
1252
934
  } catch {
1253
935
  }
1254
- const envStatus = {
936
+ const o = {
1255
937
  ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
1256
938
  OPENAI_API_KEY: !!process.env.OPENAI_API_KEY,
1257
939
  GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY
940
+ }, s = (i) => i ? "..." + i.slice(-4) : void 0, a = {
941
+ ANTHROPIC_API_KEY: s(process.env.ANTHROPIC_API_KEY),
942
+ OPENAI_API_KEY: s(process.env.OPENAI_API_KEY),
943
+ GOOGLE_API_KEY: s(process.env.GOOGLE_API_KEY)
1258
944
  };
1259
- const suffix = (key) => key ? "..." + key.slice(-4) : void 0;
1260
- const envKeySuffix = {
1261
- ANTHROPIC_API_KEY: suffix(process.env.ANTHROPIC_API_KEY),
1262
- OPENAI_API_KEY: suffix(process.env.OPENAI_API_KEY),
1263
- GOOGLE_API_KEY: suffix(process.env.GOOGLE_API_KEY)
1264
- };
1265
- return { config, envStatus, envKeySuffix, defaultUrls: DEFAULT_URLS };
1266
- });
1267
- ipcMain2.handle("backend:probe", async (_event, name, config) => {
1268
- return probeBackend(name, config);
1269
- });
1270
- ipcMain2.handle("config:save", async (_event, config) => {
1271
- const configDir = join(homedir(), ".ai-council");
1272
- await mkdir(configDir, { recursive: true });
1273
- await writeFile(join(configDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
1274
- clearCaches();
1275
- return { success: true };
1276
- });
1277
- ipcMain2.handle("discussion:start", async (_event, params) => {
1278
- const win = getWindow();
1279
- if (!win) return { error: "No window" };
1280
- if (activeAbortController) {
1281
- activeAbortController.abort();
1282
- }
1283
- activeAbortController = new AbortController();
1284
- injectionBuffer = [];
1285
- const configPath = join(homedir(), ".ai-council", "config.json");
1286
- let config = { backends: {} };
945
+ return { config: e, envStatus: o, envKeySuffix: a, defaultUrls: ye };
946
+ }), n.handle("backend:probe", async (t, e, o) => bt(e, o)), n.handle("config:save", async (t, e) => {
947
+ const o = u(w(), ".ai-council");
948
+ return await N(o, { recursive: !0 }), await $(u(o, "config.json"), JSON.stringify(e, null, 2), "utf-8"), ze(), { success: !0 };
949
+ }), n.handle("discussion:start", async (t, e) => {
950
+ const o = r();
951
+ if (!o) return { error: "No window" };
952
+ A && A.abort(), A = new AbortController(), K = [];
953
+ const s = u(w(), ".ai-council", "config.json");
954
+ let a = { backends: {} };
1287
955
  try {
1288
- const raw = await readFile(configPath, "utf-8");
1289
- config = JSON.parse(raw);
956
+ const p = await y(s, "utf-8");
957
+ a = JSON.parse(p);
1290
958
  } catch {
1291
959
  }
1292
- const registeredPaths = getRegisteredPaths(config);
1293
- const allCounsellors = await loadCounsellors(params.councilDir, registeredPaths);
1294
- const counsellors = params.counsellorIds?.length ? allCounsellors.filter((c) => params.counsellorIds.includes(c.id)) : allCounsellors;
1295
- if (counsellors.length === 0) {
1296
- win.webContents.send("discussion:event", { type: "error", counsellorName: "", error: "No counsellors found" });
960
+ const i = oe(a), l = await te(e.councilDir, i), c = e.counsellorIds?.length ? l.filter((p) => e.counsellorIds.includes(p.id)) : l;
961
+ if (c.length === 0) {
962
+ o.webContents.send("discussion:event", { type: "error", counsellorName: "", error: "No counsellors found" });
1297
963
  return;
1298
964
  }
1299
- const send = (event) => {
1300
- if (!win.isDestroyed()) {
1301
- win.webContents.send("discussion:event", event);
1302
- }
1303
- };
1304
- const beforeTurn = async () => {
1305
- if (injectionBuffer.length === 0) return null;
1306
- const content = injectionBuffer.shift();
1307
- return {
1308
- round: 0,
1309
- counsellorId: "__user__",
1310
- counsellorName: "You",
1311
- content,
1312
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1313
- model: "human",
1314
- backend: "human"
1315
- };
1316
- };
1317
- const opts = {
1318
- topic: params.topic,
1319
- topicSource: params.topicSource,
1320
- counsellors,
1321
- rounds: params.rounds,
1322
- onEvent: send,
1323
- beforeTurn,
1324
- signal: activeAbortController.signal,
1325
- mode: params.mode,
1326
- config
965
+ const m = (p) => {
966
+ o.isDestroyed() || o.webContents.send("discussion:event", p);
967
+ }, g = async () => K.length === 0 ? null : {
968
+ round: 0,
969
+ counsellorId: "__user__",
970
+ counsellorName: "You",
971
+ content: K.shift(),
972
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
973
+ model: "human",
974
+ backend: "human"
975
+ }, f = {
976
+ topic: e.topic,
977
+ topicSource: e.topicSource,
978
+ counsellors: c,
979
+ rounds: e.rounds,
980
+ onEvent: m,
981
+ beforeTurn: g,
982
+ signal: A.signal,
983
+ mode: e.mode,
984
+ config: a
1327
985
  };
1328
986
  try {
1329
- const result = await runConversation(opts);
1330
- if (config.secretary?.backend) {
987
+ const p = await ct(f);
988
+ if (a.secretary?.backend)
1331
989
  try {
1332
- send({ type: "summary_start" });
1333
- const secretaryResult = await runSecretary({
1334
- result,
1335
- config,
1336
- onChunk: (delta) => {
1337
- send({ type: "summary_chunk", delta });
990
+ m({ type: "summary_start" });
991
+ const d = await Ze({
992
+ result: p,
993
+ config: a,
994
+ onChunk: (h) => {
995
+ m({ type: "summary_chunk", delta: h });
1338
996
  },
1339
- signal: activeAbortController?.signal
997
+ signal: A?.signal
1340
998
  });
1341
- result.summary = secretaryResult.text;
1342
- if (secretaryResult.diagram) {
1343
- result.diagram = secretaryResult.diagram;
1344
- }
1345
- send({ type: "summary_complete", summary: secretaryResult.text, diagram: secretaryResult.diagram });
1346
- } catch (err) {
1347
- log$1.error("ipc:discussion", "Secretary summary failed", err);
1348
- send({ type: "error", counsellorName: "Secretary", error: err instanceof Error ? err.message : String(err) });
999
+ p.summary = d.text, d.diagram && (p.diagram = d.diagram), m({ type: "summary_complete", summary: d.text, diagram: d.diagram });
1000
+ } catch (d) {
1001
+ O.error("ipc:discussion", "Secretary summary failed", d), m({ type: "error", counsellorName: "Secretary", error: d instanceof Error ? d.message : String(d) });
1349
1002
  }
1350
- }
1351
- if (config.secretary?.backend) {
1003
+ if (a.secretary?.backend)
1352
1004
  try {
1353
- const firstRoundTurns = result.turns.filter((t) => t.round === 1);
1354
- const title = await generateTitle({
1355
- topic: result.topic,
1356
- firstRoundTurns,
1357
- config
1005
+ const d = p.turns.filter((b) => b.round === 1), h = await tt({
1006
+ topic: p.topic,
1007
+ firstRoundTurns: d,
1008
+ config: a
1358
1009
  });
1359
- result.title = title;
1360
- send({ type: "title_generated", title });
1361
- } catch (err) {
1362
- log$1.error("ipc:discussion", "Title generation failed", err);
1010
+ p.title = h, m({ type: "title_generated", title: h });
1011
+ } catch (d) {
1012
+ O.error("ipc:discussion", "Title generation failed", d);
1363
1013
  }
1364
- }
1365
- if (params.infographicBackends?.length) {
1366
- for (const backend of params.infographicBackends) {
1014
+ if (e.infographicBackends?.length)
1015
+ for (const d of e.infographicBackends)
1367
1016
  try {
1368
- send({ type: "infographic_start" });
1369
- const infographicData = await generateInfographic(result, config, backend);
1370
- if (!result.infographics) result.infographics = [];
1371
- result.infographics.push(infographicData);
1372
- send({ type: "infographic_complete", infographic: infographicData });
1373
- } catch (err) {
1374
- log$1.error("ipc:discussion", `Infographic generation failed (${backend})`, err);
1375
- send({ type: "infographic_error", error: err instanceof Error ? err.message : String(err) });
1017
+ m({ type: "infographic_start" });
1018
+ const h = await ie(p, a, d);
1019
+ p.infographics || (p.infographics = []), p.infographics.push(h), m({ type: "infographic_complete", infographic: h });
1020
+ } catch (h) {
1021
+ O.error("ipc:discussion", `Infographic generation failed (${d})`, h), m({ type: "infographic_error", error: h instanceof Error ? h.message : String(h) });
1376
1022
  }
1377
- }
1378
- }
1379
- send({ type: "complete", result });
1023
+ m({ type: "complete", result: p });
1380
1024
  try {
1381
- await saveToHistory(result);
1382
- } catch (err) {
1383
- log$1.error("ipc:discussion", "Failed to save to history", err);
1025
+ await ut(p);
1026
+ } catch (d) {
1027
+ O.error("ipc:discussion", "Failed to save to history", d);
1384
1028
  }
1385
- } catch (err) {
1386
- log$1.error("ipc:discussion", "Discussion failed", err);
1387
- send({ type: "error", counsellorName: "", error: err instanceof Error ? err.message : String(err) });
1029
+ } catch (p) {
1030
+ O.error("ipc:discussion", "Discussion failed", p), m({ type: "error", counsellorName: "", error: p instanceof Error ? p.message : String(p) });
1388
1031
  } finally {
1389
- activeAbortController = null;
1390
- }
1391
- });
1392
- ipcMain2.handle("discussion:stop", async () => {
1393
- if (activeAbortController) {
1394
- activeAbortController.abort();
1395
- activeAbortController = null;
1032
+ A = null;
1396
1033
  }
1397
- return { success: true };
1398
- });
1399
- ipcMain2.handle("discussion:inject", async (_event, content) => {
1400
- injectionBuffer.push(content);
1401
- return { success: true };
1402
- });
1403
- ipcMain2.handle("registry:add-local", async (_event, dirPath) => {
1404
- return addLocalCounsellor(dirPath);
1405
- });
1406
- ipcMain2.handle("registry:add-remote", async (_event, url) => {
1407
- return addRemoteCounsellor(url);
1408
- });
1409
- ipcMain2.handle("registry:remove", async (_event, id, deleteFiles) => {
1410
- await removeCounsellor(id, deleteFiles);
1411
- return { success: true };
1412
- });
1413
- ipcMain2.handle("shell:open-in-finder", async (_event, dirPath) => {
1414
- shell.showItemInFolder(join(dirPath, "ABOUT.md"));
1415
- });
1416
- ipcMain2.handle("shell:open-in-terminal", async (_event, dirPath) => {
1417
- const execFileAsync2 = promisify(execFile);
1418
- await execFileAsync2("open", ["-a", "Terminal", dirPath]);
1419
- });
1420
- ipcMain2.handle("shell:open-in-editor", async (_event, dirPath) => {
1421
- const execFileAsync2 = promisify(execFile);
1034
+ }), n.handle("discussion:stop", async () => (A && (A.abort(), A = null), { success: !0 })), n.handle("discussion:inject", async (t, e) => (K.push(e), { success: !0 })), n.handle("registry:add-local", async (t, e) => Be(e)), n.handle("registry:add-remote", async (t, e) => Ge(e)), n.handle("registry:remove", async (t, e, o) => (await Fe(e, o), { success: !0 })), n.handle("shell:open-in-finder", async (t, e) => {
1035
+ q.showItemInFolder(u(e, "ABOUT.md"));
1036
+ }), n.handle("shell:open-in-terminal", async (t, e) => {
1037
+ await R(U)("open", ["-a", "Terminal", e]);
1038
+ }), n.handle("shell:open-in-editor", async (t, e) => {
1039
+ const o = R(U);
1422
1040
  try {
1423
- await execFileAsync2("code", [dirPath]);
1041
+ await o("code", [e]);
1424
1042
  } catch {
1425
- shell.openPath(dirPath);
1043
+ q.openPath(e);
1426
1044
  }
1427
- });
1428
- ipcMain2.handle("history:list", async () => listHistory());
1429
- ipcMain2.handle("history:get", async (_event, id) => getHistoryEntry(id));
1430
- ipcMain2.handle("history:delete", async (_event, id) => {
1431
- await deleteHistoryEntry(id);
1432
- return { success: true };
1433
- });
1434
- ipcMain2.handle("infographic:generate", async (_event, historyId, backend) => {
1435
- const configPath = join(homedir(), ".ai-council", "config.json");
1436
- let config = { backends: {} };
1045
+ }), n.handle("history:list", async () => dt()), n.handle("history:get", async (t, e) => ae(e)), n.handle("history:delete", async (t, e) => (await mt(e), { success: !0 })), n.handle("infographic:generate", async (t, e, o) => {
1046
+ const s = u(w(), ".ai-council", "config.json");
1047
+ let a = { backends: {} };
1437
1048
  try {
1438
- const raw = await readFile(configPath, "utf-8");
1439
- config = JSON.parse(raw);
1049
+ const c = await y(s, "utf-8");
1050
+ a = JSON.parse(c);
1440
1051
  } catch {
1441
1052
  }
1442
- const result = await getHistoryEntry(historyId);
1443
- const infographicData = await generateInfographic(result, config, backend);
1444
- await addInfographicToHistory(historyId, infographicData);
1445
- return { infographic: infographicData };
1446
- });
1447
- ipcMain2.handle("infographic:delete", async (_event, historyId, index) => {
1448
- await deleteInfographicFromHistory(historyId, index);
1449
- return { success: true };
1450
- });
1451
- ipcMain2.handle("file:read-as-text", async (_event, filePath) => {
1452
- const name = basename(filePath);
1453
- const ext = name.includes(".") ? "." + name.split(".").pop().toLowerCase() : "";
1454
- const textExtensions = /* @__PURE__ */ new Set([
1053
+ const i = await ae(e), l = await ie(i, a, o);
1054
+ return await ft(e, l), { infographic: l };
1055
+ }), n.handle("infographic:delete", async (t, e, o) => (await pt(e, o), { success: !0 })), n.handle("file:read-as-text", async (t, e) => {
1056
+ const o = C(e), s = o.includes(".") ? "." + o.split(".").pop().toLowerCase() : "", a = /* @__PURE__ */ new Set([
1455
1057
  ".txt",
1456
1058
  ".md",
1457
1059
  ".csv",
@@ -1488,8 +1090,7 @@ function registerIpcHandlers(ipcMain2, getWindow) {
1488
1090
  ".conf",
1489
1091
  ".log",
1490
1092
  ".svg"
1491
- ]);
1492
- const markitdownExtensions = /* @__PURE__ */ new Set([
1093
+ ]), i = /* @__PURE__ */ new Set([
1493
1094
  ".pdf",
1494
1095
  ".docx",
1495
1096
  ".pptx",
@@ -1500,136 +1101,109 @@ function registerIpcHandlers(ipcMain2, getWindow) {
1500
1101
  ".epub",
1501
1102
  ".rtf"
1502
1103
  ]);
1503
- if (textExtensions.has(ext)) {
1104
+ if (a.has(s))
1504
1105
  try {
1505
- const content = await readFile(filePath, "utf-8");
1506
- return { name, content };
1507
- } catch (err) {
1508
- return { name, content: `[Error reading file: ${err instanceof Error ? err.message : String(err)}]` };
1106
+ const l = await y(e, "utf-8");
1107
+ return { name: o, content: l };
1108
+ } catch (l) {
1109
+ return { name: o, content: `[Error reading file: ${l instanceof Error ? l.message : String(l)}]` };
1509
1110
  }
1510
- }
1511
- if (markitdownExtensions.has(ext)) {
1512
- const execFileAsync2 = promisify(execFile);
1111
+ if (i.has(s)) {
1112
+ const l = R(U);
1513
1113
  try {
1514
- const { stdout } = await execFileAsync2("markitdown", [filePath], {
1114
+ const { stdout: c } = await l("markitdown", [e], {
1515
1115
  timeout: 3e4,
1516
- maxBuffer: 10 * 1024 * 1024
1116
+ maxBuffer: 10485760
1517
1117
  // 10 MB
1518
1118
  });
1519
- return { name, content: stdout };
1520
- } catch (err) {
1521
- const msg = err instanceof Error ? err.message : String(err);
1522
- if (msg.includes("ENOENT")) {
1523
- return {
1524
- name,
1525
- content: `[Cannot convert ${ext} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
1526
- };
1527
- }
1528
- return { name, content: `[Error converting file: ${msg}]` };
1119
+ return { name: o, content: c };
1120
+ } catch (c) {
1121
+ const m = c instanceof Error ? c.message : String(c);
1122
+ return m.includes("ENOENT") ? {
1123
+ name: o,
1124
+ content: `[Cannot convert ${s} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
1125
+ } : { name: o, content: `[Error converting file: ${m}]` };
1529
1126
  }
1530
1127
  }
1531
- return { name, content: `[Unsupported file type: ${name}]` };
1532
- });
1533
- ipcMain2.handle("markitdown:check", async () => {
1534
- const execFileAsync2 = promisify(execFile);
1128
+ return { name: o, content: `[Unsupported file type: ${o}]` };
1129
+ }), n.handle("markitdown:check", async () => {
1130
+ const t = R(U);
1535
1131
  try {
1536
- const { stdout } = await execFileAsync2("markitdown", ["--version"], { timeout: 5e3 });
1537
- return { installed: true, version: stdout.trim() };
1132
+ const { stdout: e } = await t("markitdown", ["--version"], { timeout: 5e3 });
1133
+ return { installed: !0, version: e.trim() };
1538
1134
  } catch {
1539
- return { installed: false };
1135
+ return { installed: !1 };
1540
1136
  }
1541
- });
1542
- ipcMain2.handle("markitdown:install", async () => {
1543
- const execFileAsync2 = promisify(execFile);
1137
+ }), n.handle("markitdown:install", async () => {
1138
+ const t = R(U);
1544
1139
  try {
1545
- await execFileAsync2("pip", ["install", "markitdown[all]"], {
1140
+ return await t("pip", ["install", "markitdown[all]"], {
1546
1141
  timeout: 12e4,
1547
1142
  maxBuffer: 10 * 1024 * 1024
1548
- });
1549
- return { success: true };
1550
- } catch (err) {
1551
- return { success: false, error: err instanceof Error ? err.message : String(err) };
1143
+ }), { success: !0 };
1144
+ } catch (e) {
1145
+ return { success: !1, error: e instanceof Error ? e.message : String(e) };
1552
1146
  }
1553
- });
1554
- ipcMain2.handle("dialog:selectDirectory", async () => {
1555
- const win = getWindow();
1556
- if (!win) return null;
1557
- const result = await dialog.showOpenDialog(win, {
1147
+ }), n.handle("dialog:selectDirectory", async () => {
1148
+ const t = r();
1149
+ if (!t) return null;
1150
+ const e = await be.showOpenDialog(t, {
1558
1151
  properties: ["openDirectory"]
1559
1152
  });
1560
- return result.canceled ? null : result.filePaths[0];
1153
+ return e.canceled ? null : e.filePaths[0];
1561
1154
  });
1562
1155
  }
1563
- const __filename$1 = fileURLToPath(import.meta.url);
1564
- const __dirname$1 = dirname(__filename$1);
1565
- const logFile = join(__dirname$1, "..", "electron-debug.log");
1566
- function log(source, ...args) {
1567
- const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${source}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
1156
+ const vt = Ee(import.meta.url), J = _e(vt), we = u(J, "..", "electron-debug.log");
1157
+ function P(n, ...r) {
1158
+ const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${n}] ${r.map((e) => typeof e == "string" ? e : JSON.stringify(e)).join(" ")}
1568
1159
  `;
1569
- appendFileSync(logFile, line);
1160
+ Se(we, t);
1570
1161
  }
1571
- writeFileSync(logFile, `=== AI Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
1162
+ Ie(we, `=== AI Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
1572
1163
  `);
1573
- let mainWindow = null;
1574
- function createWindow() {
1575
- log("main", "Creating BrowserWindow");
1576
- mainWindow = new BrowserWindow({
1164
+ let _ = null;
1165
+ function le() {
1166
+ P("main", "Creating BrowserWindow"), _ = new de({
1577
1167
  width: 1200,
1578
1168
  height: 800,
1579
1169
  minWidth: 800,
1580
1170
  minHeight: 600,
1581
1171
  title: "AI Council",
1582
1172
  webPreferences: {
1583
- contextIsolation: true,
1584
- nodeIntegration: false,
1585
- preload: join(__dirname$1, "preload.mjs")
1173
+ contextIsolation: !0,
1174
+ nodeIntegration: !1,
1175
+ preload: u(J, "preload.mjs")
1586
1176
  }
1177
+ }), _.webContents.on("console-message", (r, t, e, o, s) => {
1178
+ const a = ["DEBUG", "INFO", "WARN", "ERROR"][t] || "LOG";
1179
+ P(`renderer:${a}`, `${e} (${s}:${o})`);
1180
+ }), _.webContents.on("render-process-gone", (r, t) => {
1181
+ P("main:CRASH", "Renderer process gone:", t);
1182
+ }), _.webContents.on("did-fail-load", (r, t, e) => {
1183
+ P("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
1587
1184
  });
1588
- mainWindow.webContents.on("console-message", (_event, level, message, line, sourceId) => {
1589
- const levelName = ["DEBUG", "INFO", "WARN", "ERROR"][level] || "LOG";
1590
- log(`renderer:${levelName}`, `${message} (${sourceId}:${line})`);
1591
- });
1592
- mainWindow.webContents.on("render-process-gone", (_event, details) => {
1593
- log("main:CRASH", "Renderer process gone:", details);
1594
- });
1595
- mainWindow.webContents.on("did-fail-load", (_event, errorCode, errorDescription) => {
1596
- log("main:LOAD_ERROR", `Failed to load: ${errorCode} ${errorDescription}`);
1597
- });
1598
- const url = process.env.VITE_DEV_SERVER_URL;
1599
- if (url) {
1600
- log("main", `Loading dev server URL: ${url}`);
1601
- mainWindow.loadURL(url);
1602
- } else {
1603
- const filePath = join(__dirname$1, "../dist-renderer/index.html");
1604
- log("main", `Loading file: ${filePath}`);
1605
- mainWindow.loadFile(filePath);
1185
+ const n = process.env.VITE_DEV_SERVER_URL;
1186
+ if (n)
1187
+ P("main", `Loading dev server URL: ${n}`), _.loadURL(n);
1188
+ else {
1189
+ const r = u(J, "../dist-renderer/index.html");
1190
+ P("main", `Loading file: ${r}`), _.loadFile(r);
1606
1191
  }
1607
- if (process.env.VITE_DEV_SERVER_URL) {
1608
- mainWindow.webContents.openDevTools();
1609
- }
1610
- mainWindow.on("closed", () => {
1611
- mainWindow = null;
1192
+ process.env.VITE_DEV_SERVER_URL && _.webContents.openDevTools(), _.on("closed", () => {
1193
+ _ = null;
1612
1194
  });
1613
1195
  }
1614
- protocol.registerSchemesAsPrivileged([
1615
- { scheme: "council-file", privileges: { bypassCSP: true, supportFetchAPI: true } }
1196
+ ue.registerSchemesAsPrivileged([
1197
+ { scheme: "council-file", privileges: { bypassCSP: !0, supportFetchAPI: !0 } }
1616
1198
  ]);
1617
- app.whenReady().then(() => {
1618
- log("main", "App ready, registering IPC handlers");
1619
- protocol.handle("council-file", (request) => {
1620
- const filePath = decodeURIComponent(request.url.replace("council-file://", ""));
1621
- return net.fetch(pathToFileURL(filePath).href);
1622
- });
1623
- registerIpcHandlers(ipcMain, () => mainWindow);
1624
- createWindow();
1625
- app.on("activate", () => {
1626
- if (BrowserWindow.getAllWindows().length === 0) {
1627
- createWindow();
1628
- }
1199
+ j.whenReady().then(() => {
1200
+ P("main", "App ready, registering IPC handlers"), ue.handle("council-file", (n) => {
1201
+ const r = decodeURIComponent(n.url.replace("council-file://", ""));
1202
+ return ke.fetch(Ae(r).href);
1203
+ }), kt(ve, () => _), le(), j.on("activate", () => {
1204
+ de.getAllWindows().length === 0 && le();
1629
1205
  });
1630
1206
  });
1631
- app.on("window-all-closed", () => {
1632
- if (process.platform !== "darwin") {
1633
- app.quit();
1634
- }
1207
+ j.on("window-all-closed", () => {
1208
+ process.platform !== "darwin" && j.quit();
1635
1209
  });