@statechange/council 0.1.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 (89) hide show
  1. package/README.md +207 -0
  2. package/council/creative/ABOUT.md +23 -0
  3. package/council/critic/ABOUT.md +23 -0
  4. package/council/strategist/ABOUT.md +21 -0
  5. package/dist/backends/anthropic.d.ts +2 -0
  6. package/dist/backends/anthropic.js +57 -0
  7. package/dist/backends/anthropic.js.map +1 -0
  8. package/dist/backends/google.d.ts +2 -0
  9. package/dist/backends/google.js +68 -0
  10. package/dist/backends/google.js.map +1 -0
  11. package/dist/backends/index.d.ts +3 -0
  12. package/dist/backends/index.js +47 -0
  13. package/dist/backends/index.js.map +1 -0
  14. package/dist/backends/ollama.d.ts +2 -0
  15. package/dist/backends/ollama.js +68 -0
  16. package/dist/backends/ollama.js.map +1 -0
  17. package/dist/backends/openai.d.ts +2 -0
  18. package/dist/backends/openai.js +62 -0
  19. package/dist/backends/openai.js.map +1 -0
  20. package/dist/backends/types.d.ts +1 -0
  21. package/dist/backends/types.js +2 -0
  22. package/dist/backends/types.js.map +1 -0
  23. package/dist/cli.d.ts +2 -0
  24. package/dist/cli.js +152 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/commands/config.d.ts +7 -0
  27. package/dist/commands/config.js +118 -0
  28. package/dist/commands/config.js.map +1 -0
  29. package/dist/commands/counsellor.d.ts +7 -0
  30. package/dist/commands/counsellor.js +98 -0
  31. package/dist/commands/counsellor.js.map +1 -0
  32. package/dist/commands/discuss.d.ts +12 -0
  33. package/dist/commands/discuss.js +154 -0
  34. package/dist/commands/discuss.js.map +1 -0
  35. package/dist/commands/history.d.ts +5 -0
  36. package/dist/commands/history.js +46 -0
  37. package/dist/commands/history.js.map +1 -0
  38. package/dist/commands/list.d.ts +5 -0
  39. package/dist/commands/list.js +40 -0
  40. package/dist/commands/list.js.map +1 -0
  41. package/dist/core/conversation-engine.d.ts +13 -0
  42. package/dist/core/conversation-engine.js +226 -0
  43. package/dist/core/conversation-engine.js.map +1 -0
  44. package/dist/core/counsellor-loader.d.ts +4 -0
  45. package/dist/core/counsellor-loader.js +97 -0
  46. package/dist/core/counsellor-loader.js.map +1 -0
  47. package/dist/core/counsellor-registry.d.ts +12 -0
  48. package/dist/core/counsellor-registry.js +131 -0
  49. package/dist/core/counsellor-registry.js.map +1 -0
  50. package/dist/core/excalidraw-cheatsheet.d.ts +5 -0
  51. package/dist/core/excalidraw-cheatsheet.js +65 -0
  52. package/dist/core/excalidraw-cheatsheet.js.map +1 -0
  53. package/dist/core/history.d.ts +16 -0
  54. package/dist/core/history.js +74 -0
  55. package/dist/core/history.js.map +1 -0
  56. package/dist/core/infographic.d.ts +4 -0
  57. package/dist/core/infographic.js +81 -0
  58. package/dist/core/infographic.js.map +1 -0
  59. package/dist/core/key-scanner.d.ts +8 -0
  60. package/dist/core/key-scanner.js +79 -0
  61. package/dist/core/key-scanner.js.map +1 -0
  62. package/dist/core/logger.d.ts +5 -0
  63. package/dist/core/logger.js +38 -0
  64. package/dist/core/logger.js.map +1 -0
  65. package/dist/core/output-formatter.d.ts +2 -0
  66. package/dist/core/output-formatter.js +47 -0
  67. package/dist/core/output-formatter.js.map +1 -0
  68. package/dist/core/secretary.d.ts +23 -0
  69. package/dist/core/secretary.js +171 -0
  70. package/dist/core/secretary.js.map +1 -0
  71. package/dist/core/skill-loader.d.ts +2 -0
  72. package/dist/core/skill-loader.js +32 -0
  73. package/dist/core/skill-loader.js.map +1 -0
  74. package/dist/electron/ipc-handlers.d.ts +3 -0
  75. package/dist/electron/ipc-handlers.js +477 -0
  76. package/dist/electron/ipc-handlers.js.map +1 -0
  77. package/dist/electron/main.d.ts +1 -0
  78. package/dist/electron/main.js +85 -0
  79. package/dist/electron/main.js.map +1 -0
  80. package/dist/electron/preload.d.ts +1 -0
  81. package/dist/electron/preload.js +38 -0
  82. package/dist/electron/preload.js.map +1 -0
  83. package/dist/types.d.ts +184 -0
  84. package/dist/types.js +12 -0
  85. package/dist/types.js.map +1 -0
  86. package/dist-electron/main.js +1635 -0
  87. package/package.json +87 -0
  88. package/skills/council-manage/SKILL.md +214 -0
  89. package/skills/council-setup-keys/SKILL.md +127 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Compact Excalidraw element reference for LLM diagram generation.
3
+ * Appended to the secretary system prompt so it can output valid Excalidraw JSON.
4
+ */
5
+ export function getExcalidrawCheatsheet() {
6
+ return `
7
+ ## Excalidraw Element Reference
8
+
9
+ Output a JSON array of Excalidraw elements. Each element needs these fields:
10
+
11
+ ### Common Fields (all elements)
12
+ - \`type\`: "rectangle" | "ellipse" | "diamond" | "text" | "arrow" | "line"
13
+ - \`id\`: unique string (e.g. "rect1", "text1", "arrow1")
14
+ - \`x\`, \`y\`: number (top-left origin, x increases right, y increases down)
15
+ - \`width\`, \`height\`: number
16
+ - \`strokeColor\`: hex string (e.g. "#1e1e1e")
17
+ - \`backgroundColor\`: hex string or "transparent"
18
+ - \`fillStyle\`: "solid" | "hachure" | "cross-hatch"
19
+ - \`strokeWidth\`: 1 | 2 | 4
20
+ - \`roughness\`: 0 (sharp) | 1 (sketchy)
21
+ - \`opacity\`: 100
22
+ - \`angle\`: 0
23
+ - \`seed\`: any integer (e.g. 1)
24
+ - \`version\`: 1
25
+ - \`isDeleted\`: false
26
+ - \`groupIds\`: []
27
+ - \`boundElements\`: null or array of { id: string, type: "text" | "arrow" }
28
+ - \`link\`: null
29
+ - \`locked\`: false
30
+
31
+ ### Text Elements
32
+ 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
33
+
34
+ ### Arrow/Line Elements
35
+ 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
36
+
37
+ ### Color Palette
38
+ - Blue: "#1971c2", Light blue bg: "#a5d8ff"
39
+ - Green: "#2f9e44", Light green bg: "#b2f2bb"
40
+ - Red: "#e03131", Light red bg: "#ffc9c9"
41
+ - Orange: "#e8590c", Light orange bg: "#ffd8a8"
42
+ - Purple: "#7048e8", Light purple bg: "#d0bfff"
43
+ - Yellow: "#f08c00", Light yellow bg: "#ffec99"
44
+ - Gray: "#868e96", Light gray bg: "#dee2e6"
45
+ - Dark: "#1e1e1e"
46
+
47
+ ### Layout Tips
48
+ - Space shapes ~200px apart horizontally, ~150px vertically
49
+ - Typical shape size: 160×80 for rectangles, 120×60 for ellipses
50
+ - Center text inside shapes using containerId
51
+ - Use arrows to show relationships (agreement, disagreement, influence)
52
+
53
+ ### Compact Example
54
+ \`\`\`json
55
+ [
56
+ {"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},
57
+ {"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},
58
+ {"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},
59
+ {"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},
60
+ {"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}
61
+ ]
62
+ \`\`\`
63
+ `.trim();
64
+ }
65
+ //# sourceMappingURL=excalidraw-cheatsheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excalidraw-cheatsheet.js","sourceRoot":"","sources":["../../src/core/excalidraw-cheatsheet.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDR,CAAC,IAAI,EAAE,CAAC;AACT,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ConversationResult } from "../types.js";
2
+ export interface HistoryEntry {
3
+ id: string;
4
+ topic: string;
5
+ title?: string;
6
+ counsellors: string[];
7
+ rounds: number;
8
+ startedAt: string;
9
+ completedAt: string;
10
+ }
11
+ export declare function saveToHistory(result: ConversationResult): Promise<string>;
12
+ export declare function listHistory(): Promise<HistoryEntry[]>;
13
+ export declare function getHistoryEntry(id: string): Promise<ConversationResult>;
14
+ export declare function deleteHistoryEntry(id: string): Promise<void>;
15
+ export declare function addInfographicToHistory(id: string, infographic: string): Promise<void>;
16
+ export declare function deleteInfographicFromHistory(id: string, index: number): Promise<void>;
@@ -0,0 +1,74 @@
1
+ import { readFile, writeFile, readdir, rm, mkdir } from "node:fs/promises";
2
+ import { join, basename } from "node:path";
3
+ import { homedir } from "node:os";
4
+ const HISTORY_DIR = join(homedir(), ".ai-council", "history");
5
+ function slugify(text) {
6
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
7
+ }
8
+ export async function saveToHistory(result) {
9
+ await mkdir(HISTORY_DIR, { recursive: true });
10
+ const timestamp = new Date(result.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19);
11
+ const slug = slugify(result.topic);
12
+ const id = `${timestamp}-${slug}`;
13
+ const filePath = join(HISTORY_DIR, `${id}.json`);
14
+ await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
15
+ return id;
16
+ }
17
+ export async function listHistory() {
18
+ await mkdir(HISTORY_DIR, { recursive: true });
19
+ const files = await readdir(HISTORY_DIR);
20
+ const entries = [];
21
+ for (const file of files) {
22
+ if (!file.endsWith(".json"))
23
+ continue;
24
+ try {
25
+ const raw = await readFile(join(HISTORY_DIR, file), "utf-8");
26
+ const result = JSON.parse(raw);
27
+ entries.push({
28
+ id: basename(file, ".json"),
29
+ topic: result.topic,
30
+ title: result.title,
31
+ counsellors: result.counsellors.map(c => c.name),
32
+ rounds: result.rounds,
33
+ startedAt: result.startedAt,
34
+ completedAt: result.completedAt,
35
+ });
36
+ }
37
+ catch { /* skip invalid files */ }
38
+ }
39
+ return entries.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
40
+ }
41
+ export async function getHistoryEntry(id) {
42
+ const filePath = join(HISTORY_DIR, `${id}.json`);
43
+ const raw = await readFile(filePath, "utf-8");
44
+ const result = JSON.parse(raw);
45
+ // Migrate old singular infographic field to array
46
+ if (result.infographic && !result.infographics) {
47
+ result.infographics = [result.infographic];
48
+ delete result.infographic;
49
+ }
50
+ return result;
51
+ }
52
+ export async function deleteHistoryEntry(id) {
53
+ const filePath = join(HISTORY_DIR, `${id}.json`);
54
+ await rm(filePath);
55
+ }
56
+ export async function addInfographicToHistory(id, infographic) {
57
+ const filePath = join(HISTORY_DIR, `${id}.json`);
58
+ const raw = await readFile(filePath, "utf-8");
59
+ const result = JSON.parse(raw);
60
+ if (!result.infographics)
61
+ result.infographics = [];
62
+ result.infographics.push(infographic);
63
+ await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
64
+ }
65
+ export async function deleteInfographicFromHistory(id, index) {
66
+ const filePath = join(HISTORY_DIR, `${id}.json`);
67
+ const raw = await readFile(filePath, "utf-8");
68
+ const result = JSON.parse(raw);
69
+ if (result.infographics && index >= 0 && index < result.infographics.length) {
70
+ result.infographics.splice(index, 1);
71
+ }
72
+ await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
73
+ }
74
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/core/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ConversationResult, CouncilConfig } from "../types.js";
2
+ export type ImageBackend = "openai" | "google";
3
+ export declare function generateInfographic(result: ConversationResult, config: CouncilConfig, backendOverride?: ImageBackend): Promise<string>;
4
+ export declare function hasImageBackend(config: CouncilConfig): boolean;
@@ -0,0 +1,81 @@
1
+ import { createRequire } from "node:module";
2
+ const require = createRequire(import.meta.url);
3
+ function buildPrompt(result) {
4
+ const names = result.counsellors.map((c) => c.name).join(", ");
5
+ const summary = result.summary ?? result.turns.map((t) => `${t.counsellorName}: ${t.content.slice(0, 200)}`).join("\n");
6
+ return [
7
+ "Create a professional infographic summarizing a panel discussion.",
8
+ `Topic: ${result.topic.slice(0, 300)}`,
9
+ `Key points: ${summary.slice(0, 1500)}`,
10
+ `Panelists: ${names}`,
11
+ "Use a clean, modern design with sections for convergence points, divergence points, and key takeaways.",
12
+ "Include relevant icons and visual hierarchy. Use a horizontal landscape layout.",
13
+ ].join(" ");
14
+ }
15
+ function detectBackend(config) {
16
+ if (config.infographic?.backend)
17
+ return config.infographic.backend;
18
+ const hasGoogle = !!(config.backends.google?.apiKey || process.env.GOOGLE_API_KEY);
19
+ const hasOpenai = !!(config.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
20
+ if (hasGoogle)
21
+ return "google";
22
+ if (hasOpenai)
23
+ return "openai";
24
+ return null;
25
+ }
26
+ async function generateViaOpenAI(prompt, config) {
27
+ const OpenAI = require("openai").default;
28
+ const client = new OpenAI({
29
+ apiKey: config.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
30
+ ...(config.backends.openai?.baseUrl ? { baseURL: config.backends.openai.baseUrl } : {}),
31
+ });
32
+ const response = await client.images.generate({
33
+ model: "gpt-image-1",
34
+ prompt,
35
+ quality: "high",
36
+ size: "1536x1024",
37
+ });
38
+ const b64 = response.data?.[0]?.b64_json;
39
+ if (!b64)
40
+ throw new Error("No image data returned from OpenAI");
41
+ return b64;
42
+ }
43
+ async function generateViaGoogle(prompt, config) {
44
+ const { GoogleGenAI } = require("@google/genai");
45
+ const apiKey = config.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
46
+ if (!apiKey)
47
+ throw new Error("No Google API key configured");
48
+ const ai = new GoogleGenAI({ apiKey });
49
+ const response = await ai.models.generateContent({
50
+ model: "gemini-3-pro-image-preview",
51
+ contents: prompt,
52
+ config: {
53
+ responseModalities: ["IMAGE", "TEXT"],
54
+ },
55
+ });
56
+ const parts = response.candidates?.[0]?.content?.parts;
57
+ if (!parts)
58
+ throw new Error("No response parts from Gemini");
59
+ for (const part of parts) {
60
+ if (part.inlineData?.data) {
61
+ return part.inlineData.data;
62
+ }
63
+ }
64
+ throw new Error("No image data in Gemini response");
65
+ }
66
+ export async function generateInfographic(result, config, backendOverride) {
67
+ const backend = backendOverride ?? detectBackend(config);
68
+ if (!backend)
69
+ throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
70
+ const prompt = buildPrompt(result);
71
+ if (backend === "openai") {
72
+ return generateViaOpenAI(prompt, config);
73
+ }
74
+ else {
75
+ return generateViaGoogle(prompt, config);
76
+ }
77
+ }
78
+ export function hasImageBackend(config) {
79
+ return detectBackend(config) !== null;
80
+ }
81
+ //# sourceMappingURL=infographic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infographic.js","sourceRoot":"","sources":["../../src/core/infographic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,SAAS,WAAW,CAAC,MAA0B;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAExH,OAAO;QACL,mEAAmE;QACnE,UAAU,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;QACtC,eAAe,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE;QACvC,cAAc,KAAK,EAAE;QACrB,wGAAwG;QACxG,iFAAiF;KAClF,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAID,SAAS,aAAa,CAAC,MAAqB;IAC1C,IAAI,MAAM,CAAC,WAAW,EAAE,OAAO;QAAE,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;IAEnE,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnF,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEnF,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC/B,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC/B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,MAAqB;IACpE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QACpE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxF,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC5C,KAAK,EAAE,aAAa;QACpB,MAAM;QACN,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,WAAW;KAClB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;IACzC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAChE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,MAAqB;IACpE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC5E,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAE7D,MAAM,EAAE,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC;QAC/C,KAAK,EAAE,4BAA4B;QACnC,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE;YACN,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;SACtC;KACF,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC;IACvD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAE7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAA0B,EAC1B,MAAqB,EACrB,eAA8B;IAE9B,MAAM,OAAO,GAAG,eAAe,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IAErG,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAEnC,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,OAAO,aAAa,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AACxC,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface FoundKey {
2
+ backend: string;
3
+ envVar: string;
4
+ value: string;
5
+ source: string;
6
+ }
7
+ export declare function scanForKeys(extraPaths?: string[]): Promise<FoundKey[]>;
8
+ export declare function maskKey(key: string): string;
@@ -0,0 +1,79 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ const KEY_PATTERNS = [
5
+ { backend: "anthropic", envVar: "ANTHROPIC_API_KEY", pattern: /ANTHROPIC_API_KEY\s*=\s*['"]?([^\s'"#]+)/ },
6
+ { backend: "openai", envVar: "OPENAI_API_KEY", pattern: /OPENAI_API_KEY\s*=\s*['"]?([^\s'"#]+)/ },
7
+ { backend: "google", envVar: "GOOGLE_API_KEY", pattern: /GOOGLE_API_KEY\s*=\s*['"]?([^\s'"#]+)/ },
8
+ // Also pick up GEMINI_API_KEY and GOOGLE_GEMINI_API_KEY as google
9
+ { backend: "google", envVar: "GEMINI_API_KEY", pattern: /(?<![A-Z_])GEMINI_API_KEY\s*=\s*['"]?([^\s'"#]+)/ },
10
+ { backend: "google", envVar: "GOOGLE_GEMINI_API_KEY", pattern: /GOOGLE_GEMINI_API_KEY\s*=\s*['"]?([^\s'"#]+)/ },
11
+ ];
12
+ function getSearchPaths() {
13
+ const home = homedir();
14
+ return [
15
+ // Current project
16
+ ".env",
17
+ ".env.local",
18
+ ".env.development",
19
+ ".env.production",
20
+ // Home directory dotfiles
21
+ join(home, ".env"),
22
+ join(home, ".bashrc"),
23
+ join(home, ".bash_profile"),
24
+ join(home, ".zshrc"),
25
+ join(home, ".zshenv"),
26
+ join(home, ".zprofile"),
27
+ join(home, ".profile"),
28
+ // Common project locations
29
+ join(home, ".config", "shell", "env"),
30
+ // Fish shell
31
+ join(home, ".config", "fish", "config.fish"),
32
+ ];
33
+ }
34
+ async function scanFile(filePath) {
35
+ let content;
36
+ try {
37
+ content = await readFile(filePath, "utf-8");
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ const found = [];
43
+ for (const { backend, envVar, pattern } of KEY_PATTERNS) {
44
+ const match = content.match(pattern);
45
+ if (match && match[1] && match[1].length > 5) {
46
+ found.push({ backend, envVar, value: match[1], source: filePath });
47
+ }
48
+ }
49
+ return found;
50
+ }
51
+ export async function scanForKeys(extraPaths = []) {
52
+ const paths = [...getSearchPaths(), ...extraPaths];
53
+ const results = [];
54
+ // Also check current process env
55
+ for (const { backend, envVar } of KEY_PATTERNS) {
56
+ const val = process.env[envVar];
57
+ if (val && val.length > 5) {
58
+ results.push({ backend, envVar, value: val, source: "process.env" });
59
+ }
60
+ }
61
+ for (const filePath of paths) {
62
+ const found = await scanFile(filePath);
63
+ results.push(...found);
64
+ }
65
+ // Deduplicate — prefer the first occurrence per backend
66
+ const seen = new Map();
67
+ for (const key of results) {
68
+ if (!seen.has(key.backend)) {
69
+ seen.set(key.backend, key);
70
+ }
71
+ }
72
+ return Array.from(seen.values());
73
+ }
74
+ export function maskKey(key) {
75
+ if (key.length <= 8)
76
+ return "****";
77
+ return key.slice(0, 4) + "..." + key.slice(-4);
78
+ }
79
+ //# sourceMappingURL=key-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-scanner.js","sourceRoot":"","sources":["../../src/core/key-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AASlC,MAAM,YAAY,GAA2D;IAC3E,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,EAAE,OAAO,EAAE,0CAA0C,EAAE;IAC1G,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,uCAAuC,EAAE;IACjG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,uCAAuC,EAAE;IACjG,kEAAkE;IAClE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,kDAAkD,EAAE;IAC5G,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,uBAAuB,EAAE,OAAO,EAAE,8CAA8C,EAAE;CAChH,CAAC;AAEF,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,OAAO;QACL,kBAAkB;QAClB,MAAM;QACN,YAAY;QACZ,kBAAkB;QAClB,iBAAiB;QACjB,0BAA0B;QAC1B,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QACrB,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;QAC3B,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;QACpB,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QACrB,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;QACvB,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;QACtB,2BAA2B;QAC3B,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC;QACrC,aAAa;QACb,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,QAAgB;IACtC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,YAAY,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,aAAuB,EAAE;IACzD,MAAM,KAAK,GAAG,CAAC,GAAG,cAAc,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC;IACnD,MAAM,OAAO,GAAe,EAAE,CAAC;IAE/B,iCAAiC;IACjC,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,wDAAwD;IACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACnC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare const log: {
2
+ info: (context: string, message: string, extra?: unknown) => Promise<void>;
3
+ warn: (context: string, message: string, extra?: unknown) => Promise<void>;
4
+ error: (context: string, message: string, extra?: unknown) => Promise<void>;
5
+ };
@@ -0,0 +1,38 @@
1
+ import { appendFile, mkdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ const LOG_DIR = join(homedir(), ".ai-council");
5
+ const LOG_FILE = join(LOG_DIR, "council.log");
6
+ let ensured = false;
7
+ async function ensureDir() {
8
+ if (ensured)
9
+ return;
10
+ await mkdir(LOG_DIR, { recursive: true });
11
+ ensured = true;
12
+ }
13
+ function formatEntry(level, context, message, extra) {
14
+ const ts = new Date().toISOString();
15
+ let line = `[${ts}] ${level} [${context}] ${message}`;
16
+ if (extra !== undefined) {
17
+ const detail = extra instanceof Error
18
+ ? `${extra.message}\n${extra.stack ?? ""}`
19
+ : typeof extra === "string" ? extra : JSON.stringify(extra, null, 2);
20
+ line += `\n ${detail.replace(/\n/g, "\n ")}`;
21
+ }
22
+ return line + "\n";
23
+ }
24
+ async function write(level, context, message, extra) {
25
+ try {
26
+ await ensureDir();
27
+ await appendFile(LOG_FILE, formatEntry(level, context, message, extra));
28
+ }
29
+ catch {
30
+ // Last-resort: don't let logging itself crash the app
31
+ }
32
+ }
33
+ export const log = {
34
+ info: (context, message, extra) => write("INFO", context, message, extra),
35
+ warn: (context, message, extra) => write("WARN", context, message, extra),
36
+ error: (context, message, extra) => write("ERROR", context, message, extra),
37
+ };
38
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AAE9C,IAAI,OAAO,GAAG,KAAK,CAAC;AAEpB,KAAK,UAAU,SAAS;IACtB,IAAI,OAAO;QAAE,OAAO;IACpB,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,OAAO,GAAG,IAAI,CAAC;AACjB,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,OAAe,EAAE,OAAe,EAAE,KAAe;IACnF,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,IAAI,GAAG,IAAI,EAAE,KAAK,KAAK,KAAK,OAAO,KAAK,OAAO,EAAE,CAAC;IACtD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK;YACnC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE;YAC1C,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACvE,IAAI,IAAI,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,GAAG,IAAI,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,KAAa,EAAE,OAAe,EAAE,OAAe,EAAE,KAAe;IACnF,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,OAAe,EAAE,OAAe,EAAE,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC;IACnG,IAAI,EAAE,CAAC,OAAe,EAAE,OAAe,EAAE,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC;IACnG,KAAK,EAAE,CAAC,OAAe,EAAE,OAAe,EAAE,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC;CACtG,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ConversationResult } from "../types.js";
2
+ export declare function writeOutput(result: ConversationResult, outputDir: string, format: "md" | "json" | "both"): Promise<string[]>;
@@ -0,0 +1,47 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ function toMarkdown(result) {
4
+ const lines = [];
5
+ const date = new Date(result.startedAt).toISOString().split("T")[0];
6
+ lines.push(`# Council Discussion: ${result.topic.slice(0, 100)}`);
7
+ lines.push("");
8
+ lines.push(`**Counsellors:** ${result.counsellors.map((c) => c.name).join(", ")}`);
9
+ lines.push(`**Rounds:** ${result.rounds} | **Date:** ${date}`);
10
+ lines.push("");
11
+ let currentRound = 0;
12
+ for (const turn of result.turns) {
13
+ if (turn.round !== currentRound) {
14
+ currentRound = turn.round;
15
+ lines.push("---");
16
+ lines.push("");
17
+ lines.push(`## Round ${currentRound}`);
18
+ lines.push("");
19
+ }
20
+ lines.push(`### ${turn.counsellorName}`);
21
+ lines.push(`*${turn.backend}/${turn.model}*`);
22
+ lines.push("");
23
+ lines.push(turn.content);
24
+ lines.push("");
25
+ }
26
+ lines.push("---");
27
+ lines.push("");
28
+ lines.push(`*Total tokens — input: ${result.totalTokenUsage.input}, output: ${result.totalTokenUsage.output}*`);
29
+ return lines.join("\n");
30
+ }
31
+ export async function writeOutput(result, outputDir, format) {
32
+ await mkdir(outputDir, { recursive: true });
33
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
34
+ const written = [];
35
+ if (format === "md" || format === "both") {
36
+ const mdPath = join(outputDir, `council-${timestamp}.md`);
37
+ await writeFile(mdPath, toMarkdown(result), "utf-8");
38
+ written.push(mdPath);
39
+ }
40
+ if (format === "json" || format === "both") {
41
+ const jsonPath = join(outputDir, `council-${timestamp}.json`);
42
+ await writeFile(jsonPath, JSON.stringify(result, null, 2), "utf-8");
43
+ written.push(jsonPath);
44
+ }
45
+ return written;
46
+ }
47
+ //# sourceMappingURL=output-formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-formatter.js","sourceRoot":"","sources":["../../src/core/output-formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ConversationResult, ConversationTurn, CouncilConfig } from "../types.js";
2
+ export interface SecretaryResult {
3
+ text: string;
4
+ diagram?: unknown[];
5
+ }
6
+ export declare function runSecretary({ result, config, onChunk, signal, }: {
7
+ result: ConversationResult;
8
+ config: CouncilConfig;
9
+ onChunk?: (delta: string) => void;
10
+ signal?: AbortSignal;
11
+ }): Promise<SecretaryResult>;
12
+ export declare function runInterimSummary({ result, roundNumber, config, onChunk, signal, }: {
13
+ result: ConversationResult;
14
+ roundNumber: number;
15
+ config: CouncilConfig;
16
+ onChunk?: (delta: string) => void;
17
+ signal?: AbortSignal;
18
+ }): Promise<string>;
19
+ export declare function generateTitle({ topic, firstRoundTurns, config, }: {
20
+ topic: string;
21
+ firstRoundTurns: ConversationTurn[];
22
+ config: CouncilConfig;
23
+ }): Promise<string>;