@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,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Text } from "ink";
4
+ import Spinner from "ink-spinner";
5
+ import { listHistory, getHistoryEntry } from "../core/history.js";
6
+ export function HistoryCommand({ id }) {
7
+ const [loading, setLoading] = useState(true);
8
+ const [error, setError] = useState(null);
9
+ const [entries, setEntries] = useState([]);
10
+ const [detail, setDetail] = useState(null);
11
+ useEffect(() => {
12
+ async function run() {
13
+ try {
14
+ if (id) {
15
+ const result = await getHistoryEntry(id);
16
+ setDetail(result);
17
+ }
18
+ else {
19
+ const list = await listHistory();
20
+ setEntries(list);
21
+ }
22
+ }
23
+ catch (err) {
24
+ setError(err instanceof Error ? err.message : String(err));
25
+ }
26
+ finally {
27
+ setLoading(false);
28
+ }
29
+ }
30
+ run();
31
+ }, [id]);
32
+ if (loading) {
33
+ return (_jsxs(Box, { children: [_jsx(Text, { color: "green", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { children: " Loading history..." })] }));
34
+ }
35
+ if (error) {
36
+ return _jsxs(Text, { color: "red", children: ["Error: ", error] });
37
+ }
38
+ if (detail) {
39
+ return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Topic: ", detail.topic] }), _jsxs(Text, { dimColor: true, children: [detail.counsellors.map(c => c.name).join(", "), " | ", detail.rounds, " round", detail.rounds > 1 ? "s" : "", " | ", new Date(detail.startedAt).toLocaleString()] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: detail.turns.map((turn, i) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: "cyan", children: ["[", turn.counsellorName, "] Round ", turn.round] }), _jsx(Text, { children: turn.content })] }, i))) })] }));
40
+ }
41
+ if (entries.length === 0) {
42
+ return _jsx(Text, { dimColor: true, children: "No discussion history found." });
43
+ }
44
+ return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { bold: true, children: "Discussion History" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: entries.map((entry) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, children: entry.topic.slice(0, 60) }), entry.topic.length > 60 ? "..." : ""] }), _jsxs(Text, { dimColor: true, children: [new Date(entry.startedAt).toLocaleString(), " | ", entry.counsellors.join(", "), " | ", entry.rounds, " round", entry.rounds > 1 ? "s" : ""] }), _jsxs(Text, { color: "gray", children: ["ID: ", entry.id] })] }, entry.id))) })] }));
45
+ }
46
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/commands/history.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ interface Props {
2
+ councilDir: string;
3
+ }
4
+ export declare function ListCommand({ councilDir }: Props): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { loadCounsellors } from "../core/counsellor-loader.js";
5
+ import { getRegisteredPaths } from "../core/counsellor-registry.js";
6
+ import { readFile } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { homedir } from "node:os";
9
+ export function ListCommand({ councilDir }) {
10
+ const [counsellors, setCounsellors] = useState([]);
11
+ const [error, setError] = useState(null);
12
+ const [loading, setLoading] = useState(true);
13
+ useEffect(() => {
14
+ async function load() {
15
+ let config = { backends: {} };
16
+ try {
17
+ config = JSON.parse(await readFile(join(homedir(), ".ai-council", "config.json"), "utf-8"));
18
+ }
19
+ catch { /* no config */ }
20
+ const paths = getRegisteredPaths(config);
21
+ return loadCounsellors(councilDir, paths);
22
+ }
23
+ load()
24
+ .then((c) => {
25
+ setCounsellors(c);
26
+ setLoading(false);
27
+ })
28
+ .catch((err) => {
29
+ setError(err instanceof Error ? err.message : String(err));
30
+ setLoading(false);
31
+ });
32
+ }, []);
33
+ if (loading)
34
+ return _jsx(Text, { children: "Loading counsellors..." });
35
+ if (error) {
36
+ return _jsxs(Text, { color: "red", children: ["Error: ", error] });
37
+ }
38
+ return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Available Counsellors (", counsellors.length, ") from ", councilDir] }), _jsx(Text, { children: " " }), counsellors.map((c) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, color: "cyan", children: c.frontmatter.name }), " ", _jsxs(Text, { dimColor: true, children: ["(", c.id, ")"] })] }), _jsxs(Text, { children: [" ", c.frontmatter.description] }), _jsxs(Text, { dimColor: true, children: [" ", "Backend: ", c.frontmatter.backend, c.frontmatter.model ? ` (${c.frontmatter.model})` : "", " | Interests:", " ", c.frontmatter.interests.join(", ")] })] }, c.id)))] }));
39
+ }
40
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Counsellor, ConversationTurn, ConversationResult, ConversationEvent, CouncilConfig } from "../types.js";
2
+ export interface RunConversationOptions {
3
+ topic: string;
4
+ topicSource: "inline" | "file";
5
+ counsellors: Counsellor[];
6
+ rounds: number;
7
+ onEvent: (event: ConversationEvent) => void;
8
+ beforeTurn?: () => Promise<ConversationTurn | null>;
9
+ signal?: AbortSignal;
10
+ mode?: "freeform" | "debate";
11
+ config?: CouncilConfig;
12
+ }
13
+ export declare function runConversation(topicOrOpts: string | RunConversationOptions, topicSource?: "inline" | "file", counsellors?: Counsellor[], rounds?: number, onEvent?: (event: ConversationEvent) => void): Promise<ConversationResult>;
@@ -0,0 +1,226 @@
1
+ import { getBackend } from "../backends/index.js";
2
+ import { runInterimSummary } from "./secretary.js";
3
+ import { log } from "./logger.js";
4
+ function buildMessages(topic, turns, currentCounsellorId) {
5
+ const messages = [{ role: "user", content: topic }];
6
+ for (const turn of turns) {
7
+ if (turn.counsellorId === currentCounsellorId) {
8
+ messages.push({ role: "assistant", content: turn.content });
9
+ }
10
+ else {
11
+ messages.push({
12
+ role: "user",
13
+ content: `[${turn.counsellorName}, Round ${turn.round}]: ${turn.content}`,
14
+ });
15
+ }
16
+ }
17
+ return messages;
18
+ }
19
+ function buildDebateMessages(topic, turns, currentCounsellorId, currentRound) {
20
+ const messages = [{ role: "user", content: topic }];
21
+ if (currentRound === 1) {
22
+ // Constructive: only the topic, no other turns visible
23
+ return messages;
24
+ }
25
+ // Rebuttal: show all round 1 (constructive) turns
26
+ const constructiveTurns = turns.filter((t) => t.round === 1);
27
+ for (const turn of constructiveTurns) {
28
+ if (turn.counsellorId === currentCounsellorId) {
29
+ messages.push({ role: "assistant", content: turn.content });
30
+ }
31
+ else {
32
+ messages.push({
33
+ role: "user",
34
+ content: `[${turn.counsellorName}, Constructive]: ${turn.content}`,
35
+ });
36
+ }
37
+ }
38
+ // Show only the previous rebuttal round (N-1)
39
+ const prevRound = currentRound - 1;
40
+ if (prevRound > 1) {
41
+ const prevTurns = turns.filter((t) => t.round === prevRound);
42
+ for (const turn of prevTurns) {
43
+ if (turn.counsellorId === currentCounsellorId) {
44
+ messages.push({ role: "assistant", content: turn.content });
45
+ }
46
+ else {
47
+ messages.push({
48
+ role: "user",
49
+ content: `[${turn.counsellorName}, Round ${prevRound}]: ${turn.content}`,
50
+ });
51
+ }
52
+ }
53
+ }
54
+ // Include current counsellor's own prior turns from other rebuttal rounds (not round 1, not prevRound)
55
+ for (const turn of turns) {
56
+ if (turn.counsellorId === currentCounsellorId &&
57
+ turn.round !== 1 &&
58
+ turn.round !== prevRound &&
59
+ turn.round < currentRound) {
60
+ messages.push({ role: "assistant", content: turn.content });
61
+ }
62
+ }
63
+ return messages;
64
+ }
65
+ function shuffleWithSeed(array, seed) {
66
+ const result = [...array];
67
+ // Simple seeded PRNG (mulberry32)
68
+ let s = seed | 0;
69
+ const rand = () => {
70
+ s = (s + 0x6d2b79f5) | 0;
71
+ let t = Math.imul(s ^ (s >>> 15), 1 | s);
72
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
73
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
74
+ };
75
+ // Fisher-Yates shuffle
76
+ for (let i = result.length - 1; i > 0; i--) {
77
+ const j = Math.floor(rand() * (i + 1));
78
+ [result[i], result[j]] = [result[j], result[i]];
79
+ }
80
+ return result;
81
+ }
82
+ function buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries) {
83
+ return {
84
+ topic: opts.topic,
85
+ topicSource: opts.topicSource,
86
+ counsellors: opts.counsellors.map((c) => ({
87
+ id: c.id,
88
+ name: c.frontmatter.name,
89
+ description: c.frontmatter.description,
90
+ backend: c.frontmatter.backend,
91
+ model: c.frontmatter.model ?? "default",
92
+ avatarUrl: c.avatarUrl,
93
+ })),
94
+ rounds: opts.rounds,
95
+ turns,
96
+ startedAt,
97
+ completedAt: new Date().toISOString(),
98
+ totalTokenUsage: { input: totalInput, output: totalOutput },
99
+ ...(roundSummaries && Object.keys(roundSummaries).length > 0 ? { roundSummaries } : {}),
100
+ ...(opts.mode === "debate" ? { mode: "debate" } : {}),
101
+ };
102
+ }
103
+ export async function runConversation(topicOrOpts, topicSource, counsellors, rounds, onEvent) {
104
+ let opts;
105
+ if (typeof topicOrOpts === "string") {
106
+ opts = {
107
+ topic: topicOrOpts,
108
+ topicSource: topicSource,
109
+ counsellors: counsellors,
110
+ rounds: rounds,
111
+ onEvent: onEvent,
112
+ };
113
+ }
114
+ else {
115
+ opts = topicOrOpts;
116
+ }
117
+ const startedAt = new Date().toISOString();
118
+ const turns = [];
119
+ let totalInput = 0;
120
+ let totalOutput = 0;
121
+ const isDebate = opts.mode === "debate";
122
+ const roundSummaries = {};
123
+ log.info("conversation", `Starting ${isDebate ? "debate" : "freeform"} — ${opts.counsellors.length} counsellors, ${opts.rounds} rounds`, {
124
+ counsellors: opts.counsellors.map((c) => `${c.frontmatter.name} (${c.frontmatter.backend}/${c.frontmatter.model ?? "default"})`),
125
+ topic: opts.topic.slice(0, 200),
126
+ });
127
+ for (let round = 1; round <= opts.rounds; round++) {
128
+ // In debate mode: round 1 keeps original order, rounds 2+ shuffle
129
+ const roundCounsellors = isDebate && round > 1
130
+ ? shuffleWithSeed(opts.counsellors, round)
131
+ : opts.counsellors;
132
+ for (const counsellor of roundCounsellors) {
133
+ if (opts.signal?.aborted) {
134
+ return buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries);
135
+ }
136
+ if (opts.beforeTurn) {
137
+ const injected = await opts.beforeTurn();
138
+ if (injected) {
139
+ turns.push(injected);
140
+ opts.onEvent({ type: "turn_complete", turn: injected });
141
+ }
142
+ }
143
+ opts.onEvent({ type: "turn_start", round, counsellorName: counsellor.frontmatter.name });
144
+ try {
145
+ const backend = await getBackend(counsellor.frontmatter.backend);
146
+ const model = counsellor.frontmatter.model ?? backend.defaultModel;
147
+ const messages = isDebate
148
+ ? buildDebateMessages(opts.topic, turns, counsellor.id, round)
149
+ : buildMessages(opts.topic, turns, counsellor.id);
150
+ const chatRequest = {
151
+ model,
152
+ systemPrompt: counsellor.systemPrompt,
153
+ messages,
154
+ temperature: counsellor.frontmatter.temperature,
155
+ };
156
+ let content;
157
+ let tokenUsage;
158
+ if (backend.chatStream) {
159
+ content = "";
160
+ for await (const chunk of backend.chatStream(chatRequest)) {
161
+ if (opts.signal?.aborted)
162
+ break;
163
+ content += chunk.delta;
164
+ if (chunk.delta) {
165
+ opts.onEvent({ type: "turn_chunk", counsellorName: counsellor.frontmatter.name, delta: chunk.delta });
166
+ }
167
+ if (chunk.tokenUsage) {
168
+ tokenUsage = chunk.tokenUsage;
169
+ }
170
+ }
171
+ }
172
+ else {
173
+ const response = await backend.chat(chatRequest);
174
+ content = response.content;
175
+ tokenUsage = response.tokenUsage;
176
+ }
177
+ const turn = {
178
+ round,
179
+ counsellorId: counsellor.id,
180
+ counsellorName: counsellor.frontmatter.name,
181
+ content,
182
+ timestamp: new Date().toISOString(),
183
+ model,
184
+ backend: counsellor.frontmatter.backend,
185
+ tokenUsage,
186
+ avatarUrl: counsellor.avatarUrl,
187
+ };
188
+ if (tokenUsage) {
189
+ totalInput += tokenUsage.input;
190
+ totalOutput += tokenUsage.output;
191
+ }
192
+ turns.push(turn);
193
+ opts.onEvent({ type: "turn_complete", turn });
194
+ }
195
+ catch (err) {
196
+ const message = err instanceof Error ? err.message : String(err);
197
+ log.error("conversation", `Turn failed for ${counsellor.frontmatter.name} (round ${round}, model ${counsellor.frontmatter.model ?? "default"}, backend ${counsellor.frontmatter.backend})`, err);
198
+ opts.onEvent({ type: "error", counsellorName: counsellor.frontmatter.name, error: message });
199
+ }
200
+ }
201
+ opts.onEvent({ type: "round_complete", round });
202
+ // Interim secretary summary after each round (debate mode only)
203
+ if (isDebate && opts.config?.secretary?.backend && !opts.signal?.aborted) {
204
+ try {
205
+ const interimResult = buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries);
206
+ opts.onEvent({ type: "round_summary_start", round });
207
+ const summary = await runInterimSummary({
208
+ result: interimResult,
209
+ roundNumber: round,
210
+ config: opts.config,
211
+ onChunk: (delta) => {
212
+ opts.onEvent({ type: "round_summary_chunk", round, delta });
213
+ },
214
+ signal: opts.signal,
215
+ });
216
+ roundSummaries[round] = summary;
217
+ opts.onEvent({ type: "round_summary_complete", round, summary });
218
+ }
219
+ catch (err) {
220
+ log.error("conversation", `Interim summary failed for round ${round}`, err);
221
+ }
222
+ }
223
+ }
224
+ return buildResult(opts, turns, startedAt, totalInput, totalOutput, roundSummaries);
225
+ }
226
+ //# sourceMappingURL=conversation-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-engine.js","sourceRoot":"","sources":["../../src/core/conversation-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Counsellor } from "../types.js";
2
+ export declare function loadSingleCounsellor(dirPath: string): Promise<Counsellor>;
3
+ export declare function loadCounsellors(councilDir: string, registeredPaths?: string[]): Promise<Counsellor[]>;
4
+ export declare function loadSpecificCounsellors(paths: string[]): Promise<Counsellor[]>;
@@ -0,0 +1,97 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { join, basename, resolve } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import matter from "gray-matter";
5
+ import { CounsellorFrontmatterSchema } from "../types.js";
6
+ import { resolveSkills } from "./skill-loader.js";
7
+ function resolveAvatar(avatar, dirPath) {
8
+ if (!avatar)
9
+ return undefined;
10
+ if (avatar.startsWith("http://") || avatar.startsWith("https://"))
11
+ return avatar;
12
+ const absPath = avatar.startsWith("/") ? avatar : join(dirPath, avatar);
13
+ return `council-file://${absPath}`;
14
+ }
15
+ async function resolveReferences(content, counsellorDir) {
16
+ const refPattern = /\{\{(.+?)\}\}/g;
17
+ let resolved = content;
18
+ for (const match of content.matchAll(refPattern)) {
19
+ const refPath = match[1].trim();
20
+ const fullPath = join(counsellorDir, refPath);
21
+ if (existsSync(fullPath)) {
22
+ const refContent = await readFile(fullPath, "utf-8");
23
+ resolved = resolved.replace(match[0], refContent.trim());
24
+ }
25
+ else {
26
+ resolved = resolved.replace(match[0], `[Reference not found: ${refPath}]`);
27
+ }
28
+ }
29
+ return resolved;
30
+ }
31
+ export async function loadSingleCounsellor(dirPath) {
32
+ const absPath = resolve(dirPath);
33
+ const aboutPath = join(absPath, "ABOUT.md");
34
+ if (!existsSync(aboutPath)) {
35
+ throw new Error(`No ABOUT.md found in ${absPath}`);
36
+ }
37
+ const raw = await readFile(aboutPath, "utf-8");
38
+ const { data, content } = matter(raw);
39
+ const frontmatter = CounsellorFrontmatterSchema.parse(data);
40
+ let systemPrompt = await resolveReferences(content.trim(), absPath);
41
+ if (frontmatter.skills.length > 0) {
42
+ const skillContent = await resolveSkills(frontmatter.skills, absPath);
43
+ if (skillContent) {
44
+ systemPrompt += "\n\n" + skillContent;
45
+ }
46
+ }
47
+ return {
48
+ id: basename(absPath),
49
+ frontmatter,
50
+ systemPrompt,
51
+ dirPath: absPath,
52
+ avatarUrl: resolveAvatar(frontmatter.avatar, absPath),
53
+ };
54
+ }
55
+ export async function loadCounsellors(councilDir, registeredPaths) {
56
+ const counsellors = [];
57
+ const seenIds = new Set();
58
+ // Load registered counsellors first (they win on dedup)
59
+ if (registeredPaths?.length) {
60
+ for (const rPath of registeredPaths) {
61
+ if (!existsSync(join(rPath, "ABOUT.md")))
62
+ continue;
63
+ try {
64
+ const c = await loadSingleCounsellor(rPath);
65
+ if (!seenIds.has(c.id)) {
66
+ counsellors.push(c);
67
+ seenIds.add(c.id);
68
+ }
69
+ }
70
+ catch {
71
+ // skip invalid registered counsellors
72
+ }
73
+ }
74
+ }
75
+ // Then scan councilDir
76
+ if (existsSync(councilDir)) {
77
+ const entries = await readdir(councilDir);
78
+ for (const entry of entries) {
79
+ const entryPath = join(councilDir, entry);
80
+ const info = await stat(entryPath);
81
+ if (info.isDirectory() && existsSync(join(entryPath, "ABOUT.md"))) {
82
+ if (!seenIds.has(basename(entryPath))) {
83
+ counsellors.push(await loadSingleCounsellor(entryPath));
84
+ seenIds.add(basename(entryPath));
85
+ }
86
+ }
87
+ }
88
+ }
89
+ if (counsellors.length === 0) {
90
+ throw new Error(`No counsellors found in ${councilDir}. Each counsellor needs a directory with an ABOUT.md file.`);
91
+ }
92
+ return counsellors;
93
+ }
94
+ export async function loadSpecificCounsellors(paths) {
95
+ return Promise.all(paths.map(loadSingleCounsellor));
96
+ }
97
+ //# sourceMappingURL=counsellor-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counsellor-loader.js","sourceRoot":"","sources":["../../src/core/counsellor-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { CouncilConfig, CounsellorRegistryEntry } from "../types.js";
2
+ export declare function getRegistry(config: CouncilConfig): Record<string, CounsellorRegistryEntry>;
3
+ export declare function getRegisteredPaths(config: CouncilConfig): string[];
4
+ export declare function addLocalCounsellor(dirPath: string): Promise<{
5
+ id: string;
6
+ name: string;
7
+ }>;
8
+ export declare function addRemoteCounsellor(url: string): Promise<{
9
+ id: string;
10
+ name: string;
11
+ }[]>;
12
+ export declare function removeCounsellor(id: string, deleteFiles?: boolean): Promise<void>;
@@ -0,0 +1,131 @@
1
+ import { readFile, writeFile, mkdir, rm, readdir, stat } from "node:fs/promises";
2
+ import { join, basename, resolve } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { existsSync } from "node:fs";
5
+ import { execFile } from "node:child_process";
6
+ import { promisify } from "node:util";
7
+ const execFileAsync = promisify(execFile);
8
+ const CONFIG_PATH = join(homedir(), ".ai-council", "config.json");
9
+ const CLONES_DIR = join(homedir(), ".ai-council", "counsellors");
10
+ async function loadConfig() {
11
+ try {
12
+ return JSON.parse(await readFile(CONFIG_PATH, "utf-8"));
13
+ }
14
+ catch {
15
+ return { backends: {} };
16
+ }
17
+ }
18
+ async function saveConfig(config) {
19
+ const dir = join(homedir(), ".ai-council");
20
+ await mkdir(dir, { recursive: true });
21
+ await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
22
+ }
23
+ export function getRegistry(config) {
24
+ return config.counsellors ?? {};
25
+ }
26
+ export function getRegisteredPaths(config) {
27
+ return Object.values(config.counsellors ?? {}).map((e) => e.path);
28
+ }
29
+ export async function addLocalCounsellor(dirPath) {
30
+ const absPath = resolve(dirPath);
31
+ const aboutPath = join(absPath, "ABOUT.md");
32
+ if (!existsSync(aboutPath)) {
33
+ throw new Error(`No ABOUT.md found in ${absPath}`);
34
+ }
35
+ const id = basename(absPath);
36
+ const config = await loadConfig();
37
+ const registry = config.counsellors ?? {};
38
+ if (registry[id]) {
39
+ throw new Error(`Counsellor "${id}" is already registered (path: ${registry[id].path})`);
40
+ }
41
+ // Read the name from frontmatter
42
+ const raw = await readFile(aboutPath, "utf-8");
43
+ const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
44
+ const displayName = nameMatch?.[1] ?? id;
45
+ registry[id] = {
46
+ path: absPath,
47
+ source: "local",
48
+ addedAt: new Date().toISOString(),
49
+ };
50
+ config.counsellors = registry;
51
+ await saveConfig(config);
52
+ return { id, name: displayName };
53
+ }
54
+ export async function addRemoteCounsellor(url) {
55
+ await mkdir(CLONES_DIR, { recursive: true });
56
+ // Derive name from URL
57
+ const repoName = basename(url, ".git").replace(/\.git$/, "");
58
+ const clonePath = join(CLONES_DIR, repoName);
59
+ if (existsSync(clonePath)) {
60
+ throw new Error(`Directory already exists: ${clonePath}. Remove it first or use a different URL.`);
61
+ }
62
+ await execFileAsync("git", ["clone", "--depth", "1", url, clonePath]);
63
+ const results = [];
64
+ const config = await loadConfig();
65
+ const registry = config.counsellors ?? {};
66
+ // Check if root has ABOUT.md (single counsellor repo)
67
+ if (existsSync(join(clonePath, "ABOUT.md"))) {
68
+ const id = repoName;
69
+ if (registry[id]) {
70
+ throw new Error(`Counsellor "${id}" is already registered`);
71
+ }
72
+ const raw = await readFile(join(clonePath, "ABOUT.md"), "utf-8");
73
+ const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
74
+ const displayName = nameMatch?.[1] ?? id;
75
+ registry[id] = {
76
+ path: clonePath,
77
+ source: "git",
78
+ url,
79
+ addedAt: new Date().toISOString(),
80
+ };
81
+ results.push({ id, name: displayName });
82
+ }
83
+ else {
84
+ // Multi-counsellor repo: scan child directories
85
+ const entries = await readdir(clonePath);
86
+ for (const entry of entries) {
87
+ if (entry.startsWith("."))
88
+ continue;
89
+ const entryPath = join(clonePath, entry);
90
+ const info = await stat(entryPath);
91
+ if (info.isDirectory() && existsSync(join(entryPath, "ABOUT.md"))) {
92
+ const id = entry;
93
+ if (registry[id])
94
+ continue; // skip duplicates
95
+ const raw = await readFile(join(entryPath, "ABOUT.md"), "utf-8");
96
+ const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
97
+ const displayName = nameMatch?.[1] ?? id;
98
+ registry[id] = {
99
+ path: entryPath,
100
+ source: "git",
101
+ url,
102
+ addedAt: new Date().toISOString(),
103
+ };
104
+ results.push({ id, name: displayName });
105
+ }
106
+ }
107
+ if (results.length === 0) {
108
+ await rm(clonePath, { recursive: true, force: true });
109
+ throw new Error(`No counsellors found in cloned repository (no ABOUT.md files)`);
110
+ }
111
+ }
112
+ config.counsellors = registry;
113
+ await saveConfig(config);
114
+ return results;
115
+ }
116
+ export async function removeCounsellor(id, deleteFiles = false) {
117
+ const config = await loadConfig();
118
+ const registry = config.counsellors ?? {};
119
+ const entry = registry[id];
120
+ if (!entry) {
121
+ throw new Error(`Counsellor "${id}" is not registered`);
122
+ }
123
+ // Only delete files for git-cloned counsellors when requested
124
+ if (deleteFiles && entry.source === "git" && existsSync(entry.path)) {
125
+ await rm(entry.path, { recursive: true, force: true });
126
+ }
127
+ delete registry[id];
128
+ config.counsellors = registry;
129
+ await saveConfig(config);
130
+ }
131
+ //# sourceMappingURL=counsellor-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counsellor-registry.js","sourceRoot":"","sources":["../../src/core/counsellor-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
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 declare function getExcalidrawCheatsheet(): string;