@lanjak/council 0.0.0-stage → 0.6.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.
- package/README.md +522 -2
- package/dist/artifacts.d.ts +24 -0
- package/dist/artifacts.js +227 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/blocks.d.ts +12 -0
- package/dist/blocks.js +197 -0
- package/dist/blocks.js.map +1 -0
- package/dist/chairman.d.ts +7 -0
- package/dist/chairman.js +51 -0
- package/dist/chairman.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +110 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/debate.d.ts +15 -0
- package/dist/commands/debate.js +172 -0
- package/dist/commands/debate.js.map +1 -0
- package/dist/commands/home.d.ts +1 -0
- package/dist/commands/home.js +27 -0
- package/dist/commands/home.js.map +1 -0
- package/dist/commands/hook.d.ts +3 -0
- package/dist/commands/hook.js +138 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/plan.d.ts +6 -0
- package/dist/commands/plan.js +23 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/review.d.ts +18 -0
- package/dist/commands/review.js +37 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +23 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/council.d.ts +3 -0
- package/dist/council.js +62 -0
- package/dist/council.js.map +1 -0
- package/dist/debate/loop.d.ts +28 -0
- package/dist/debate/loop.js +190 -0
- package/dist/debate/loop.js.map +1 -0
- package/dist/debate/prompts.d.ts +10 -0
- package/dist/debate/prompts.js +47 -0
- package/dist/debate/prompts.js.map +1 -0
- package/dist/debate/render.d.ts +22 -0
- package/dist/debate/render.js +112 -0
- package/dist/debate/render.js.map +1 -0
- package/dist/debate/session.d.ts +7 -0
- package/dist/debate/session.js +85 -0
- package/dist/debate/session.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/exit.d.ts +7 -0
- package/dist/exit.js +13 -0
- package/dist/exit.js.map +1 -0
- package/dist/hooks/gate.d.ts +12 -0
- package/dist/hooks/gate.js +41 -0
- package/dist/hooks/gate.js.map +1 -0
- package/dist/hooks/payload.d.ts +8 -0
- package/dist/hooks/payload.js +63 -0
- package/dist/hooks/payload.js.map +1 -0
- package/dist/hooks/state.d.ts +6 -0
- package/dist/hooks/state.js +73 -0
- package/dist/hooks/state.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +14 -0
- package/dist/input.js +30 -0
- package/dist/input.js.map +1 -0
- package/dist/output.d.ts +3 -0
- package/dist/output.js +80 -0
- package/dist/output.js.map +1 -0
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.js +48 -0
- package/dist/prompts.js.map +1 -0
- package/dist/providers/anthropic.d.ts +12 -0
- package/dist/providers/anthropic.js +98 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +37 -0
- package/dist/providers/base.js +7 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/constants.d.ts +2 -0
- package/dist/providers/constants.js +11 -0
- package/dist/providers/constants.js.map +1 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.js +20 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +12 -0
- package/dist/providers/openai-compatible.js +68 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/runners/plan.d.ts +14 -0
- package/dist/runners/plan.js +45 -0
- package/dist/runners/plan.js.map +1 -0
- package/dist/runners/review.d.ts +14 -0
- package/dist/runners/review.js +47 -0
- package/dist/runners/review.js.map +1 -0
- package/dist/stdin.d.ts +9 -0
- package/dist/stdin.js +51 -0
- package/dist/stdin.js.map +1 -0
- package/dist/telemetry.d.ts +5 -0
- package/dist/telemetry.js +135 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verdict.d.ts +2 -0
- package/dist/verdict.js +12 -0
- package/dist/verdict.js.map +1 -0
- package/package.json +54 -4
- package/skills/council/SKILL.md +82 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RunControlFlags } from '../config.js';
|
|
2
|
+
import { type ArtifactOptions } from './review.js';
|
|
3
|
+
export interface DebateCommandOptions extends ArtifactOptions, RunControlFlags {
|
|
4
|
+
models?: string;
|
|
5
|
+
maxRounds?: string;
|
|
6
|
+
full?: boolean;
|
|
7
|
+
participate?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function debateCommand(prompt: string, options: DebateCommandOptions): Promise<void>;
|
|
10
|
+
export declare function debateTurnCommand(sessionId: string, response: string | undefined, options: {
|
|
11
|
+
stdin?: boolean;
|
|
12
|
+
full?: boolean;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
export declare function debateAbortCommand(sessionId: string): Promise<void>;
|
|
15
|
+
export declare function parseMaxRounds(raw: string | undefined): number;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { loadConfig, resolveModels, parseRunControls } from '../config.js';
|
|
2
|
+
import { runDebate, activeParticipants, CALLER } from '../debate/loop.js';
|
|
3
|
+
import { buildDebateOutput, renderDebateTOON, renderDebatePaused } from '../debate/render.js';
|
|
4
|
+
import { cleanupExpired, newSessionId, saveSession, loadSession, deleteSession } from '../debate/session.js';
|
|
5
|
+
import { formatTranscript } from '../debate/prompts.js';
|
|
6
|
+
import { parseVerdict } from '../verdict.js';
|
|
7
|
+
import { readStdinPayload } from '../stdin.js';
|
|
8
|
+
import { CouncilError } from '../errors.js';
|
|
9
|
+
import { buildPrompt, parseModelList } from '../input.js';
|
|
10
|
+
import { readArtifactInput } from './review.js';
|
|
11
|
+
// 'caller' is a reserved participant name, never a provider key. Filter it
|
|
12
|
+
// AFTER resolution so an explicit --models list and a COUNCIL_PROVIDERS
|
|
13
|
+
// fallback are both covered - the earlier pre-filter only guarded the
|
|
14
|
+
// explicit path.
|
|
15
|
+
function parseModels(raw, providers) {
|
|
16
|
+
return resolveModels(parseModelList(raw), providers).filter((m) => m !== CALLER);
|
|
17
|
+
}
|
|
18
|
+
export async function debateCommand(prompt, options) {
|
|
19
|
+
const controls = parseRunControls(options);
|
|
20
|
+
cleanupExpired();
|
|
21
|
+
const config = loadConfig();
|
|
22
|
+
const models = parseModels(options.models, config.providers);
|
|
23
|
+
if (models.length < 2) {
|
|
24
|
+
throw new CouncilError('A debate needs at least 2 judges; configure more providers or widen --models', 'NO_QUORUM');
|
|
25
|
+
}
|
|
26
|
+
const maxRounds = parseMaxRounds(options.maxRounds);
|
|
27
|
+
const fullPrompt = buildPrompt(prompt, await readArtifactInput(options));
|
|
28
|
+
// The caller participates by default: it authored the prompt, so it sits
|
|
29
|
+
// in the rotation and its verdict gates consensus. --no-participate drops
|
|
30
|
+
// it, which is what makes an unattended run possible.
|
|
31
|
+
const participate = options.participate !== false;
|
|
32
|
+
const progress = await runDebate(config, {
|
|
33
|
+
prompt: fullPrompt.text,
|
|
34
|
+
models,
|
|
35
|
+
participate,
|
|
36
|
+
maxRounds,
|
|
37
|
+
...controls,
|
|
38
|
+
});
|
|
39
|
+
if (progress.status === 'awaiting-caller') {
|
|
40
|
+
const session = {
|
|
41
|
+
id: newSessionId(),
|
|
42
|
+
createdAt: new Date().toISOString(),
|
|
43
|
+
prompt: fullPrompt.text,
|
|
44
|
+
models: [...models, CALLER],
|
|
45
|
+
maxRounds,
|
|
46
|
+
...controls,
|
|
47
|
+
turns: progress.turns,
|
|
48
|
+
nextTurn: progress.nextTurn,
|
|
49
|
+
warnings: fullPrompt.warnings,
|
|
50
|
+
};
|
|
51
|
+
saveSession(session);
|
|
52
|
+
console.log(renderPause(session));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const output = buildDebateOutput({
|
|
56
|
+
prompt: fullPrompt.text,
|
|
57
|
+
turns: progress.turns,
|
|
58
|
+
consensus: progress.status === 'consensus',
|
|
59
|
+
maxRounds,
|
|
60
|
+
totalCount: models.length + (participate ? 1 : 0),
|
|
61
|
+
warnings: fullPrompt.warnings,
|
|
62
|
+
});
|
|
63
|
+
console.log(renderDebateTOON(output, { full: options.full === true }));
|
|
64
|
+
}
|
|
65
|
+
function renderPause(session) {
|
|
66
|
+
const callerTurnIndexes = session.turns
|
|
67
|
+
.map((t, i) => (t.provider === CALLER ? i : -1))
|
|
68
|
+
.filter((i) => i >= 0);
|
|
69
|
+
const lastCallerIdx = callerTurnIndexes.length > 0 ? callerTurnIndexes[callerTurnIndexes.length - 1] : -1;
|
|
70
|
+
const seenTurns = session.turns.slice(0, lastCallerIdx + 1);
|
|
71
|
+
const unseenTurns = session.turns.slice(lastCallerIdx + 1);
|
|
72
|
+
const order = session.nextTurn.order;
|
|
73
|
+
return renderDebatePaused({
|
|
74
|
+
sessionId: session.id,
|
|
75
|
+
round: session.nextTurn.round,
|
|
76
|
+
maxRounds: session.maxRounds,
|
|
77
|
+
turnIndex: order.indexOf(CALLER) + 1,
|
|
78
|
+
turnCount: order.length,
|
|
79
|
+
unseenTurns,
|
|
80
|
+
seenRounds: seenTurns.length > 0 ? new Set(seenTurns.map((t) => t.round)).size : 0,
|
|
81
|
+
seenBytes: Buffer.byteLength(formatTranscript(seenTurns), 'utf8'),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export async function debateTurnCommand(sessionId, response, options) {
|
|
85
|
+
const text = await readTurnText(response, options.stdin === true);
|
|
86
|
+
const session = loadSession(sessionId);
|
|
87
|
+
if (session.nextTurn.participant !== CALLER) {
|
|
88
|
+
throw new CouncilError(`It is not the caller's turn in session ${sessionId}; the state file may be damaged. Run: council debate abort ${sessionId}`, 'NOT_YOUR_TURN');
|
|
89
|
+
}
|
|
90
|
+
const callerTurn = {
|
|
91
|
+
round: session.nextTurn.round,
|
|
92
|
+
provider: CALLER,
|
|
93
|
+
model: CALLER,
|
|
94
|
+
status: 'success',
|
|
95
|
+
response: text,
|
|
96
|
+
verdict: parseVerdict(text),
|
|
97
|
+
};
|
|
98
|
+
const turns = [...session.turns, callerTurn];
|
|
99
|
+
const config = loadConfig();
|
|
100
|
+
const judges = session.models.filter((m) => m !== CALLER);
|
|
101
|
+
const next = participantAfterCaller(session, turns);
|
|
102
|
+
const progress = await runDebate(config, { prompt: session.prompt, models: judges, participate: true, maxRounds: session.maxRounds, maxTokens: session.maxTokens, effort: session.effort }, next ? { turns, nextTurn: next } : { turns, nextTurn: { round: session.maxRounds + 1, participant: CALLER, order: [] } });
|
|
103
|
+
if (progress.status === 'awaiting-caller') {
|
|
104
|
+
const updated = { ...session, turns: progress.turns, nextTurn: progress.nextTurn };
|
|
105
|
+
saveSession(updated);
|
|
106
|
+
console.log(renderPause(updated));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
deleteSession(sessionId);
|
|
110
|
+
const output = buildDebateOutput({
|
|
111
|
+
prompt: session.prompt,
|
|
112
|
+
turns: progress.turns,
|
|
113
|
+
consensus: progress.status === 'consensus',
|
|
114
|
+
maxRounds: session.maxRounds,
|
|
115
|
+
totalCount: session.models.length,
|
|
116
|
+
warnings: session.warnings,
|
|
117
|
+
});
|
|
118
|
+
console.log(renderDebateTOON(output, { full: options.full === true }));
|
|
119
|
+
}
|
|
120
|
+
// The next speaker after the caller's turn: the next still-active,
|
|
121
|
+
// not-yet-spoken participant after CALLER in the round's FROZEN order (never
|
|
122
|
+
// recomputed - a judge that errored earlier this round must not shift who
|
|
123
|
+
// speaks after the caller), or the opener of the next round when the caller
|
|
124
|
+
// closed this one (the engine computes that round's order fresh on resume,
|
|
125
|
+
// since no turns are recorded for it yet). Returns undefined past maxRounds
|
|
126
|
+
// (engine will cap).
|
|
127
|
+
function participantAfterCaller(session, turns) {
|
|
128
|
+
const order = session.nextTurn.order;
|
|
129
|
+
const round = session.nextTurn.round;
|
|
130
|
+
const active = activeParticipants(session.models, turns);
|
|
131
|
+
const idx = order.indexOf(CALLER);
|
|
132
|
+
for (let i = idx + 1; i < order.length; i++) {
|
|
133
|
+
const candidate = order[i];
|
|
134
|
+
if (turns.some((t) => t.round === round && t.provider === candidate))
|
|
135
|
+
continue;
|
|
136
|
+
if (!active.includes(candidate))
|
|
137
|
+
continue;
|
|
138
|
+
return { round, participant: candidate, order };
|
|
139
|
+
}
|
|
140
|
+
if (round + 1 > session.maxRounds)
|
|
141
|
+
return undefined;
|
|
142
|
+
return { round: round + 1, participant: CALLER, order: [] };
|
|
143
|
+
}
|
|
144
|
+
async function readTurnText(response, stdin) {
|
|
145
|
+
if (stdin === (response !== undefined)) {
|
|
146
|
+
throw new CouncilError('Provide the turn as an argument or via --stdin, not both/neither', 'BAD_ARG');
|
|
147
|
+
}
|
|
148
|
+
if (!stdin)
|
|
149
|
+
return response;
|
|
150
|
+
if (process.stdin.isTTY) {
|
|
151
|
+
throw new CouncilError('--stdin given but stdin is a terminal; pipe content or omit the flag', 'STDIN_TTY');
|
|
152
|
+
}
|
|
153
|
+
return readStdinPayload();
|
|
154
|
+
}
|
|
155
|
+
export async function debateAbortCommand(sessionId) {
|
|
156
|
+
deleteSession(sessionId);
|
|
157
|
+
console.log(`session ${sessionId}: aborted (no-op if it did not exist)`);
|
|
158
|
+
}
|
|
159
|
+
export function parseMaxRounds(raw) {
|
|
160
|
+
if (raw === undefined)
|
|
161
|
+
return 5;
|
|
162
|
+
const trimmed = raw.trim();
|
|
163
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
164
|
+
throw new CouncilError(`--max-rounds must be a positive integer, got "${raw}"`, 'BAD_ARG');
|
|
165
|
+
}
|
|
166
|
+
const n = Number.parseInt(trimmed, 10);
|
|
167
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
168
|
+
throw new CouncilError(`--max-rounds must be a positive integer, got "${raw}"`, 'BAD_ARG');
|
|
169
|
+
}
|
|
170
|
+
return n;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=debate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debate.js","sourceRoot":"","sources":["../../src/commands/debate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function homeCommand(): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { loadConfig, listProviders } from '../config.js';
|
|
2
|
+
import { loadProvider } from '../providers/index.js';
|
|
3
|
+
export async function homeCommand() {
|
|
4
|
+
const config = loadConfig();
|
|
5
|
+
const providers = listProviders(config);
|
|
6
|
+
const lines = [];
|
|
7
|
+
lines.push('council: multi-LLM adversarial review council');
|
|
8
|
+
lines.push(`providers[${providers.length}]{name,authenticated}:`);
|
|
9
|
+
for (const name of providers) {
|
|
10
|
+
const providerConfig = config.providers[name];
|
|
11
|
+
const provider = loadProvider(name, providerConfig);
|
|
12
|
+
const auth = await provider.checkAuth();
|
|
13
|
+
lines.push(` ${name},${auth.authenticated}`);
|
|
14
|
+
}
|
|
15
|
+
if (providers.length === 0) {
|
|
16
|
+
lines.push(' (none configured)');
|
|
17
|
+
}
|
|
18
|
+
lines.push('commands[3]{name,purpose}:');
|
|
19
|
+
lines.push(' setup,check provider authentication');
|
|
20
|
+
lines.push(' review,adversarial review of an artifact or question');
|
|
21
|
+
lines.push(' plan,pressure-test a plan or decision');
|
|
22
|
+
lines.push('help[2]:');
|
|
23
|
+
lines.push(' Set COUNCIL_PROVIDERS and per-provider env vars');
|
|
24
|
+
lines.push(' Run `council review "<prompt>" --models <provider1>,<provider2>`');
|
|
25
|
+
console.log(lines.join('\n'));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home.js","sourceRoot":"","sources":["../../src/commands/home.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC5D,KAAK,CAAC,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,wBAAwB,CAAC,CAAC;IAClE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;IACrE,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IAEtD,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAChE,KAAK,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;IAEjF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { loadConfig, listProviders } from '../config.js';
|
|
2
|
+
import { readStdinPayload } from '../stdin.js';
|
|
3
|
+
import { parseHookPayload } from '../hooks/payload.js';
|
|
4
|
+
import { resolveSessionKey, recordEdit, pendingEdits, clearSession } from '../hooks/state.js';
|
|
5
|
+
import { synthesize } from '../chairman.js';
|
|
6
|
+
import { runCouncil } from '../council.js';
|
|
7
|
+
import { assembleArtifacts, formatArtifactPreamble } from '../artifacts.js';
|
|
8
|
+
import { decideGate } from '../hooks/gate.js';
|
|
9
|
+
import { parseCapBytesOverride } from '../input.js';
|
|
10
|
+
const GATE_SYSTEM_PROMPT = `You are one judge on a review council gating completion of a coding task. Review the diff of uncommitted changes. Be adversarial: look for bugs, security issues, broken contracts, missing tests. End your response with a final line of exactly one of:
|
|
11
|
+
VERDICT: pass
|
|
12
|
+
VERDICT: concerns
|
|
13
|
+
VERDICT: fail
|
|
14
|
+
Use fail only for issues that must be fixed before this work is acceptable.`;
|
|
15
|
+
const EVENTS = ['session-start', 'post-edit', 'stop'];
|
|
16
|
+
export async function hookCommand(event, options) {
|
|
17
|
+
if (!event || !EVENTS.includes(event)) {
|
|
18
|
+
console.error(`council hook: unknown event "${event ?? '(none)'}"\navailable events: ${EVENTS.join(', ')}`);
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const rawPayload = options.payload ?? (await readStdinPayload());
|
|
23
|
+
switch (event) {
|
|
24
|
+
case 'session-start':
|
|
25
|
+
return sessionStart(rawPayload);
|
|
26
|
+
case 'post-edit':
|
|
27
|
+
return postEdit(rawPayload);
|
|
28
|
+
case 'stop':
|
|
29
|
+
return stop(rawPayload);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function sessionStart(_rawPayload) {
|
|
33
|
+
try {
|
|
34
|
+
const config = loadConfig();
|
|
35
|
+
const providers = listProviders(config);
|
|
36
|
+
if (providers.length === 0) {
|
|
37
|
+
return; // nothing configured: stay silent, exit 0
|
|
38
|
+
}
|
|
39
|
+
console.log(`council[available]: providers ${providers.join(',')} | review gate active`);
|
|
40
|
+
console.log('help: run `council review "<question>" --diff` for a council review');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// A context hook must never break session startup.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function postEdit(rawPayload) {
|
|
47
|
+
try {
|
|
48
|
+
const payload = parseHookPayload(rawPayload);
|
|
49
|
+
const cwd = payload.cwd ?? process.cwd();
|
|
50
|
+
const key = resolveSessionKey(payload);
|
|
51
|
+
for (const file of payload.files ?? []) {
|
|
52
|
+
recordEdit(key, file, cwd);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error(`council hook post-edit: ${err instanceof Error ? err.message : String(err)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function safelyClearSession(key) {
|
|
60
|
+
try {
|
|
61
|
+
clearSession(key);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Best-effort cleanup only. The block/pass decision (exit code, stdout
|
|
65
|
+
// synthesis, stderr summary) is already final at this point - a failed
|
|
66
|
+
// cleanup must never reclassify it as fail-open.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function stop(rawPayload) {
|
|
70
|
+
const payload = parseHookPayload(rawPayload);
|
|
71
|
+
const key = resolveSessionKey(payload, () => { }); // stop stays quiet; post-edit already warned
|
|
72
|
+
let edits;
|
|
73
|
+
try {
|
|
74
|
+
edits = pendingEdits(key);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
process.exitCode = 0;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (edits.length === 0) {
|
|
81
|
+
process.exitCode = 0;
|
|
82
|
+
return; // zero-cost pass-through
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const config = loadConfig();
|
|
86
|
+
const models = listProviders(config);
|
|
87
|
+
if (models.length === 0) {
|
|
88
|
+
console.log('council[gate_error]: no providers configured - gate inert');
|
|
89
|
+
process.exitCode = 0;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const bundle = assembleArtifacts({
|
|
93
|
+
diff: { paths: edits },
|
|
94
|
+
cwd: payload.cwd ?? process.cwd(),
|
|
95
|
+
capBytes: parseCapBytesOverride(),
|
|
96
|
+
});
|
|
97
|
+
if (bundle.blocks.length === 0) {
|
|
98
|
+
// Edits were pending but produced no diff content (e.g. already
|
|
99
|
+
// committed) - there is nothing to review, so treat this the same as
|
|
100
|
+
// the no-pending-edits case, but the pending edits WERE reviewed (as
|
|
101
|
+
// "nothing to flag"), so clear the state instead of leaving it stale.
|
|
102
|
+
process.exitCode = 0;
|
|
103
|
+
safelyClearSession(key);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const warningsSuffix = bundle.warnings.length > 0 ? ` (warnings: ${bundle.warnings.length})` : '';
|
|
107
|
+
const prompt = formatArtifactPreamble(bundle) +
|
|
108
|
+
`Review the above uncommitted changes to these files: ${edits.join(', ')}`;
|
|
109
|
+
const judges = await runCouncil(config, {
|
|
110
|
+
prompt,
|
|
111
|
+
mode: 'review',
|
|
112
|
+
models,
|
|
113
|
+
systemPrompt: GATE_SYSTEM_PROMPT,
|
|
114
|
+
});
|
|
115
|
+
const decision = decideGate(judges);
|
|
116
|
+
if (decision.outcome === 'fail-open') {
|
|
117
|
+
console.log(`council[gate_error]: ${decision.reason} - gate failed open, edits kept for re-review${warningsSuffix}`);
|
|
118
|
+
process.exitCode = 0;
|
|
119
|
+
return; // state NOT cleared
|
|
120
|
+
}
|
|
121
|
+
const synthesis = synthesize({ prompt, mode: 'review', models, systemPrompt: GATE_SYSTEM_PROMPT }, judges);
|
|
122
|
+
if (decision.outcome === 'block') {
|
|
123
|
+
console.log(synthesis);
|
|
124
|
+
console.error(`council gate: blocked - ${decision.reason}${warningsSuffix}`);
|
|
125
|
+
process.exitCode = 2;
|
|
126
|
+
safelyClearSession(key);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(`council[gate]: pass - ${decision.reason}${warningsSuffix}`);
|
|
130
|
+
process.exitCode = 0;
|
|
131
|
+
safelyClearSession(key);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
console.log(`council[gate_error]: ${err instanceof Error ? err.message : String(err)} - gate failed open, edits kept for re-review`);
|
|
135
|
+
process.exitCode = 0; // fail open, state kept
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type RunControlFlags } from '../config.js';
|
|
2
|
+
import { type ArtifactOptions } from './review.js';
|
|
3
|
+
import { type ExitPolicyOptions } from '../exit.js';
|
|
4
|
+
export declare function planCommand(prompt: string, options: {
|
|
5
|
+
models?: string;
|
|
6
|
+
} & RunControlFlags & ArtifactOptions & ExitPolicyOptions): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { renderTOON } from '../output.js';
|
|
2
|
+
import { loadConfig, resolveModels, parseRunControls } from '../config.js';
|
|
3
|
+
import { parseModelList } from '../input.js';
|
|
4
|
+
import { readArtifactInput } from './review.js';
|
|
5
|
+
import { runPlan } from '../runners/plan.js';
|
|
6
|
+
import { exitCodeFor } from '../exit.js';
|
|
7
|
+
export async function planCommand(prompt, options) {
|
|
8
|
+
// Resolve and validate the model list BEFORE reading stdin. A bad --models
|
|
9
|
+
// must fail immediately rather than after a blocking stdin read drains.
|
|
10
|
+
const controls = parseRunControls(options);
|
|
11
|
+
const config = loadConfig();
|
|
12
|
+
const models = resolveModels(parseModelList(options.models), config.providers);
|
|
13
|
+
const output = await runPlan({
|
|
14
|
+
prompt,
|
|
15
|
+
models,
|
|
16
|
+
config,
|
|
17
|
+
...controls,
|
|
18
|
+
...(await readArtifactInput(options)),
|
|
19
|
+
});
|
|
20
|
+
console.log(renderTOON(output));
|
|
21
|
+
process.exitCode = exitCodeFor(output, options);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAwB,MAAM,cAAc,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAwB,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAA0B,MAAM,YAAY,CAAC;AAEjE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,OAAoF;IAEpF,2EAA2E;IAC3E,wEAAwE;IACxE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;QAC3B,MAAM;QACN,MAAM;QACN,MAAM;QACN,GAAG,QAAQ;QACX,GAAG,CAAC,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;KACtC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAChC,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type RunControlFlags } from '../config.js';
|
|
2
|
+
import { type ExitPolicyOptions } from '../exit.js';
|
|
3
|
+
export interface ArtifactOptions {
|
|
4
|
+
file?: string[];
|
|
5
|
+
diff?: string | boolean;
|
|
6
|
+
stdin?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function readArtifactInput(options: ArtifactOptions): Promise<{
|
|
9
|
+
files: string[] | undefined;
|
|
10
|
+
diff: {
|
|
11
|
+
range: string | undefined;
|
|
12
|
+
} | undefined;
|
|
13
|
+
stdinContent: string | undefined;
|
|
14
|
+
capBytes: number | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function reviewCommand(prompt: string, options: {
|
|
17
|
+
models?: string;
|
|
18
|
+
} & RunControlFlags & ArtifactOptions & ExitPolicyOptions): Promise<void>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { renderTOON } from '../output.js';
|
|
2
|
+
import { CouncilError } from '../errors.js';
|
|
3
|
+
import { loadConfig, resolveModels, parseRunControls } from '../config.js';
|
|
4
|
+
import { parseCapBytesOverride, parseModelList } from '../input.js';
|
|
5
|
+
import { readStdinPayload } from '../stdin.js';
|
|
6
|
+
import { runReview } from '../runners/review.js';
|
|
7
|
+
import { exitCodeFor } from '../exit.js';
|
|
8
|
+
export async function readArtifactInput(options) {
|
|
9
|
+
if (options.stdin && process.stdin.isTTY) {
|
|
10
|
+
throw new CouncilError('--stdin given but stdin is a terminal; pipe content or omit the flag', 'STDIN_TTY');
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
files: options.file,
|
|
14
|
+
diff: options.diff === undefined
|
|
15
|
+
? undefined
|
|
16
|
+
: { range: typeof options.diff === 'string' ? options.diff : undefined },
|
|
17
|
+
stdinContent: options.stdin ? await readStdinPayload() : undefined,
|
|
18
|
+
capBytes: parseCapBytesOverride(),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function reviewCommand(prompt, options) {
|
|
22
|
+
// Resolve and validate the model list BEFORE reading stdin. A bad --models
|
|
23
|
+
// must fail immediately rather than after a blocking stdin read drains.
|
|
24
|
+
const controls = parseRunControls(options);
|
|
25
|
+
const config = loadConfig();
|
|
26
|
+
const models = resolveModels(parseModelList(options.models), config.providers);
|
|
27
|
+
const output = await runReview({
|
|
28
|
+
prompt,
|
|
29
|
+
models,
|
|
30
|
+
config,
|
|
31
|
+
...controls,
|
|
32
|
+
...(await readArtifactInput(options)),
|
|
33
|
+
});
|
|
34
|
+
console.log(renderTOON(output));
|
|
35
|
+
process.exitCode = exitCodeFor(output, options);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAwB,MAAM,cAAc,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAA0B,MAAM,YAAY,CAAC;AAQjE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAwB;IAC9D,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzC,MAAM,IAAI,YAAY,CACpB,sEAAsE,EACtE,WAAW,CACZ,CAAC;IACJ,CAAC;IACD,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,IAAI;QACnB,IAAI,EACF,OAAO,CAAC,IAAI,KAAK,SAAS;YACxB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE;QAC5E,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,gBAAgB,EAAE,CAAC,CAAC,CAAC,SAAS;QAClE,QAAQ,EAAE,qBAAqB,EAAE;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,OAAoF;IAEpF,2EAA2E;IAC3E,wEAAwE;IACxE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;QAC7B,MAAM;QACN,MAAM;QACN,MAAM;QACN,GAAG,QAAQ;QACX,GAAG,CAAC,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;KACtC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAChC,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupCommand(): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { loadConfig, listProviders } from '../config.js';
|
|
2
|
+
import { loadProvider } from '../providers/index.js';
|
|
3
|
+
export async function setupCommand() {
|
|
4
|
+
const config = loadConfig();
|
|
5
|
+
const providers = listProviders(config);
|
|
6
|
+
const lines = [];
|
|
7
|
+
lines.push(`providers[${providers.length}]{name,authenticated,detail}:`);
|
|
8
|
+
for (const name of providers) {
|
|
9
|
+
const providerConfig = config.providers[name];
|
|
10
|
+
const provider = loadProvider(name, providerConfig);
|
|
11
|
+
const auth = await provider.checkAuth();
|
|
12
|
+
lines.push(` ${name},${auth.authenticated},${auth.detail}`);
|
|
13
|
+
}
|
|
14
|
+
if (providers.length === 0) {
|
|
15
|
+
lines.push(' (none configured)');
|
|
16
|
+
}
|
|
17
|
+
lines.push('help[3]:');
|
|
18
|
+
lines.push(' Set COUNCIL_PROVIDERS and per-provider env vars to add judges');
|
|
19
|
+
lines.push(' Example: COUNCIL_PROVIDERS=openai OPENAI_API_KEY=sk-... OPENAI_BASE_URL=https://api.openai.com/v1 OPENAI_MODEL=gpt-4o');
|
|
20
|
+
lines.push(' Anthropic example: COUNCIL_PROVIDERS=claude CLAUDE_KIND=anthropic CLAUDE_API_KEY=sk-ant-... CLAUDE_MODEL=claude-opus-5');
|
|
21
|
+
console.log(lines.join('\n'));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,+BAA+B,CAAC,CAAC;IACzE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,KAAK,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;IAC9E,KAAK,CAAC,IAAI,CAAC,yHAAyH,CAAC,CAAC;IACtI,KAAK,CAAC,IAAI,CAAC,0HAA0H,CAAC,CAAC;IAEvI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProviderConfig, Effort } from './types.js';
|
|
2
|
+
export interface CouncilConfig {
|
|
3
|
+
providers: Record<string, ProviderConfig>;
|
|
4
|
+
}
|
|
5
|
+
export declare function loadConfig(env?: Record<string, string | undefined>): CouncilConfig;
|
|
6
|
+
export declare function parseEffort(value: string, source?: string): Effort;
|
|
7
|
+
export interface RunControlFlags {
|
|
8
|
+
maxTokens?: string;
|
|
9
|
+
effort?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseRunControls(options: RunControlFlags): {
|
|
12
|
+
maxTokens: number | undefined;
|
|
13
|
+
effort: Effort | undefined;
|
|
14
|
+
};
|
|
15
|
+
export declare function listProviders(config: CouncilConfig): string[];
|
|
16
|
+
export declare function resolveModel(providerConfig: ProviderConfig): string | undefined;
|
|
17
|
+
export declare function resolveModels(models: string[] | undefined, providers: Record<string, ProviderConfig>): string[];
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { CouncilError } from './errors.js';
|
|
2
|
+
export function loadConfig(env = process.env) {
|
|
3
|
+
const providers = {};
|
|
4
|
+
const defaultEffort = env.COUNCIL_EFFORT === undefined
|
|
5
|
+
? undefined
|
|
6
|
+
: parseEffort(env.COUNCIL_EFFORT, 'COUNCIL_EFFORT');
|
|
7
|
+
const configured = env.COUNCIL_PROVIDERS?.split(',').map((p) => p.trim()).filter(Boolean) ?? [];
|
|
8
|
+
for (const provider of configured) {
|
|
9
|
+
const prefix = provider.toUpperCase();
|
|
10
|
+
const apiKey = env[`${prefix}_API_KEY`] ?? '';
|
|
11
|
+
const baseURL = env[`${prefix}_BASE_URL`] ?? '';
|
|
12
|
+
const model = env[`${prefix}_MODEL`];
|
|
13
|
+
const displayName = env[`${prefix}_DISPLAY_NAME`] ?? capitalize(provider);
|
|
14
|
+
const maxTokens = parsePositiveInt(env[`${prefix}_MAX_TOKENS`]);
|
|
15
|
+
const temperature = parseTemperature(env[`${prefix}_TEMPERATURE`]);
|
|
16
|
+
const kind = parseKind(env[`${prefix}_KIND`], prefix);
|
|
17
|
+
providers[provider] = {
|
|
18
|
+
name: provider,
|
|
19
|
+
apiKey,
|
|
20
|
+
baseURL,
|
|
21
|
+
model,
|
|
22
|
+
displayName,
|
|
23
|
+
maxTokens,
|
|
24
|
+
effort: env[`${prefix}_EFFORT`] === undefined
|
|
25
|
+
? defaultEffort
|
|
26
|
+
: parseEffort(env[`${prefix}_EFFORT`], `${prefix}_EFFORT`),
|
|
27
|
+
temperature,
|
|
28
|
+
kind,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return { providers };
|
|
32
|
+
}
|
|
33
|
+
export function parseEffort(value, source = '--effort') {
|
|
34
|
+
if (value === 'low' || value === 'medium' || value === 'high' || value === 'xhigh')
|
|
35
|
+
return value;
|
|
36
|
+
throw new CouncilError(`${source} must be low|medium|high|xhigh, got "${value}"`, 'BAD_EFFORT');
|
|
37
|
+
}
|
|
38
|
+
export function parseRunControls(options) {
|
|
39
|
+
let maxTokens;
|
|
40
|
+
if (options.maxTokens !== undefined) {
|
|
41
|
+
const raw = options.maxTokens.trim();
|
|
42
|
+
maxTokens = Number(raw);
|
|
43
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(maxTokens) || maxTokens < 1) {
|
|
44
|
+
throw new CouncilError(`--max-tokens must be a positive integer, got "${options.maxTokens}"`, 'BAD_ARG');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { maxTokens, effort: options.effort === undefined ? undefined : parseEffort(options.effort) };
|
|
48
|
+
}
|
|
49
|
+
function capitalize(text) {
|
|
50
|
+
return text.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
51
|
+
}
|
|
52
|
+
function parsePositiveInt(value) {
|
|
53
|
+
if (!value)
|
|
54
|
+
return undefined;
|
|
55
|
+
const n = Number.parseInt(value, 10);
|
|
56
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
57
|
+
}
|
|
58
|
+
function parseTemperature(value) {
|
|
59
|
+
if (!value)
|
|
60
|
+
return undefined;
|
|
61
|
+
const n = Number.parseFloat(value);
|
|
62
|
+
return Number.isFinite(n) && n >= 0 ? n : undefined;
|
|
63
|
+
}
|
|
64
|
+
function parseKind(value, prefix) {
|
|
65
|
+
// Trim BEFORE the emptiness check: a whitespace-only value (e.g. a stray
|
|
66
|
+
// space from a sloppy .env edit) must default to 'openai' like an unset
|
|
67
|
+
// or empty value, not throw - trimming after the check would only catch
|
|
68
|
+
// the empty-string case and treat whitespace as an unrecognized value.
|
|
69
|
+
const normalized = (value ?? '').trim().toLowerCase();
|
|
70
|
+
if (normalized === '')
|
|
71
|
+
return 'openai';
|
|
72
|
+
if (normalized === 'openai')
|
|
73
|
+
return 'openai';
|
|
74
|
+
if (normalized === 'anthropic')
|
|
75
|
+
return 'anthropic';
|
|
76
|
+
throw new CouncilError(`Unrecognized ${prefix}_KIND "${value}" (expected "openai" or "anthropic")`, 'BAD_KIND');
|
|
77
|
+
}
|
|
78
|
+
export function listProviders(config) {
|
|
79
|
+
return Object.keys(config.providers);
|
|
80
|
+
}
|
|
81
|
+
export function resolveModel(providerConfig) {
|
|
82
|
+
return providerConfig.model;
|
|
83
|
+
}
|
|
84
|
+
// One resolver for every command. Previously duplicated three times, and the
|
|
85
|
+
// copies had drifted: only debate filtered empty entries, so `--models a,,b`
|
|
86
|
+
// asked review and plan for a provider named "".
|
|
87
|
+
export function resolveModels(models, providers) {
|
|
88
|
+
const explicit = (models ?? []).map((m) => m.trim()).filter((m) => m.length > 0);
|
|
89
|
+
if (explicit.length > 0)
|
|
90
|
+
return explicit;
|
|
91
|
+
// A supplied-but-empty list is a typo, not a request for every provider.
|
|
92
|
+
if (models !== undefined && models.length > 0) {
|
|
93
|
+
throw new CouncilError('--models was given but listed no providers', 'BAD_ARG');
|
|
94
|
+
}
|
|
95
|
+
const configured = Object.keys(providers);
|
|
96
|
+
if (configured.length === 0) {
|
|
97
|
+
throw new CouncilError('No providers configured. Set COUNCIL_PROVIDERS or pass --models.', 'NO_PROVIDERS');
|
|
98
|
+
}
|
|
99
|
+
return configured;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=config.js.map
|