@lanjak/council 0.0.0-stage → 0.5.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 (108) hide show
  1. package/README.md +512 -2
  2. package/dist/artifacts.d.ts +24 -0
  3. package/dist/artifacts.js +227 -0
  4. package/dist/artifacts.js.map +1 -0
  5. package/dist/blocks.d.ts +12 -0
  6. package/dist/blocks.js +197 -0
  7. package/dist/blocks.js.map +1 -0
  8. package/dist/chairman.d.ts +7 -0
  9. package/dist/chairman.js +51 -0
  10. package/dist/chairman.js.map +1 -0
  11. package/dist/cli.d.ts +2 -0
  12. package/dist/cli.js +109 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands/debate.d.ts +15 -0
  15. package/dist/commands/debate.js +172 -0
  16. package/dist/commands/debate.js.map +1 -0
  17. package/dist/commands/home.d.ts +1 -0
  18. package/dist/commands/home.js +27 -0
  19. package/dist/commands/home.js.map +1 -0
  20. package/dist/commands/hook.d.ts +3 -0
  21. package/dist/commands/hook.js +138 -0
  22. package/dist/commands/hook.js.map +1 -0
  23. package/dist/commands/plan.d.ts +6 -0
  24. package/dist/commands/plan.js +23 -0
  25. package/dist/commands/plan.js.map +1 -0
  26. package/dist/commands/review.d.ts +18 -0
  27. package/dist/commands/review.js +37 -0
  28. package/dist/commands/review.js.map +1 -0
  29. package/dist/commands/setup.d.ts +1 -0
  30. package/dist/commands/setup.js +23 -0
  31. package/dist/commands/setup.js.map +1 -0
  32. package/dist/config.d.ts +17 -0
  33. package/dist/config.js +101 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/council.d.ts +3 -0
  36. package/dist/council.js +61 -0
  37. package/dist/council.js.map +1 -0
  38. package/dist/debate/loop.d.ts +28 -0
  39. package/dist/debate/loop.js +188 -0
  40. package/dist/debate/loop.js.map +1 -0
  41. package/dist/debate/prompts.d.ts +10 -0
  42. package/dist/debate/prompts.js +47 -0
  43. package/dist/debate/prompts.js.map +1 -0
  44. package/dist/debate/render.d.ts +22 -0
  45. package/dist/debate/render.js +112 -0
  46. package/dist/debate/render.js.map +1 -0
  47. package/dist/debate/session.d.ts +7 -0
  48. package/dist/debate/session.js +85 -0
  49. package/dist/debate/session.js.map +1 -0
  50. package/dist/errors.d.ts +5 -0
  51. package/dist/errors.js +18 -0
  52. package/dist/errors.js.map +1 -0
  53. package/dist/exit.d.ts +7 -0
  54. package/dist/exit.js +13 -0
  55. package/dist/exit.js.map +1 -0
  56. package/dist/hooks/gate.d.ts +12 -0
  57. package/dist/hooks/gate.js +41 -0
  58. package/dist/hooks/gate.js.map +1 -0
  59. package/dist/hooks/payload.d.ts +8 -0
  60. package/dist/hooks/payload.js +63 -0
  61. package/dist/hooks/payload.js.map +1 -0
  62. package/dist/hooks/state.d.ts +6 -0
  63. package/dist/hooks/state.js +73 -0
  64. package/dist/hooks/state.js.map +1 -0
  65. package/dist/index.d.ts +16 -0
  66. package/dist/index.js +16 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/input.d.ts +14 -0
  69. package/dist/input.js +30 -0
  70. package/dist/input.js.map +1 -0
  71. package/dist/output.d.ts +3 -0
  72. package/dist/output.js +80 -0
  73. package/dist/output.js.map +1 -0
  74. package/dist/prompts.d.ts +3 -0
  75. package/dist/prompts.js +48 -0
  76. package/dist/prompts.js.map +1 -0
  77. package/dist/providers/anthropic.d.ts +12 -0
  78. package/dist/providers/anthropic.js +98 -0
  79. package/dist/providers/anthropic.js.map +1 -0
  80. package/dist/providers/base.d.ts +35 -0
  81. package/dist/providers/base.js +7 -0
  82. package/dist/providers/base.js.map +1 -0
  83. package/dist/providers/constants.d.ts +2 -0
  84. package/dist/providers/constants.js +11 -0
  85. package/dist/providers/constants.js.map +1 -0
  86. package/dist/providers/index.d.ts +6 -0
  87. package/dist/providers/index.js +20 -0
  88. package/dist/providers/index.js.map +1 -0
  89. package/dist/providers/openai-compatible.d.ts +12 -0
  90. package/dist/providers/openai-compatible.js +68 -0
  91. package/dist/providers/openai-compatible.js.map +1 -0
  92. package/dist/runners/plan.d.ts +14 -0
  93. package/dist/runners/plan.js +43 -0
  94. package/dist/runners/plan.js.map +1 -0
  95. package/dist/runners/review.d.ts +14 -0
  96. package/dist/runners/review.js +45 -0
  97. package/dist/runners/review.js.map +1 -0
  98. package/dist/stdin.d.ts +9 -0
  99. package/dist/stdin.js +51 -0
  100. package/dist/stdin.js.map +1 -0
  101. package/dist/types.d.ts +114 -0
  102. package/dist/types.js +2 -0
  103. package/dist/types.js.map +1 -0
  104. package/dist/verdict.d.ts +2 -0
  105. package/dist/verdict.js +12 -0
  106. package/dist/verdict.js.map +1 -0
  107. package/package.json +54 -4
  108. package/skills/council/SKILL.md +82 -0
package/dist/errors.js ADDED
@@ -0,0 +1,18 @@
1
+ export class CouncilError extends Error {
2
+ code;
3
+ constructor(message, code) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = 'CouncilError';
7
+ }
8
+ }
9
+ export function formatError(error) {
10
+ if (error instanceof CouncilError) {
11
+ return `error: ${error.code}: ${error.message}\nhelp: npx -y @lanjak/council setup`;
12
+ }
13
+ if (error instanceof Error) {
14
+ return `error: ${error.message}\nhelp: npx -y @lanjak/council --help`;
15
+ }
16
+ return `error: ${String(error)}\nhelp: npx -y @lanjak/council --help`;
17
+ }
18
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAGnB;IAFlB,YACE,OAAe,EACC,IAAY;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;QAClC,OAAO,UAAU,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,sCAAsC,CAAC;IACtF,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,UAAU,KAAK,CAAC,OAAO,uCAAuC,CAAC;IACxE,CAAC;IACD,OAAO,UAAU,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC;AACxE,CAAC"}
package/dist/exit.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { CouncilOutput } from './types.js';
2
+ export interface ExitPolicyOptions {
3
+ failOnBlock?: boolean;
4
+ failOnUnknown?: boolean;
5
+ strict?: boolean;
6
+ }
7
+ export declare function exitCodeFor(output: CouncilOutput, options: ExitPolicyOptions): number;
package/dist/exit.js ADDED
@@ -0,0 +1,13 @@
1
+ // Exit codes 0 (success), 1 (CouncilError) and 2 (Commander) are already
2
+ // taken, so blocks and unknowns claim 3 and 4. Default stays 0 so existing
3
+ // scripts are unaffected.
4
+ export function exitCodeFor(output, options) {
5
+ const onBlock = options.failOnBlock === true || options.strict === true;
6
+ const onUnknown = options.failOnUnknown === true || options.strict === true;
7
+ if (onBlock && output.blocked)
8
+ return 3;
9
+ if (onUnknown && output.unknownJudges.length > 0)
10
+ return 4;
11
+ return 0;
12
+ }
13
+ //# sourceMappingURL=exit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exit.js","sourceRoot":"","sources":["../src/exit.ts"],"names":[],"mappings":"AAQA,yEAAyE;AACzE,2EAA2E;AAC3E,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,OAA0B;IAC3E,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IACxE,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IAE5E,IAAI,OAAO,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IACxC,IAAI,SAAS,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { JudgeResult } from '../types.js';
2
+ export type Verdict = 'pass' | 'concerns' | 'fail';
3
+ export interface GateDecision {
4
+ outcome: 'block' | 'pass' | 'fail-open';
5
+ reason: string;
6
+ verdicts: {
7
+ provider: string;
8
+ verdict: Verdict | 'error';
9
+ }[];
10
+ }
11
+ export declare function parseVerdict(response: string): Verdict;
12
+ export declare function decideGate(judges: JudgeResult[]): GateDecision;
@@ -0,0 +1,41 @@
1
+ export function parseVerdict(response) {
2
+ const lines = response.split('\n').map((l) => l.trim()).filter((l) => l.length > 0);
3
+ const last = lines[lines.length - 1] ?? '';
4
+ const match = /^VERDICT:\s*(pass|concerns|fail)\s*$/i.exec(last);
5
+ if (!match)
6
+ return 'concerns';
7
+ return match[1].toLowerCase();
8
+ }
9
+ export function decideGate(judges) {
10
+ const verdicts = judges.map((j) => ({
11
+ provider: j.provider,
12
+ verdict: (j.status === 'success' ? parseVerdict(j.response ?? '') : 'error'),
13
+ }));
14
+ const responding = verdicts.filter((v) => v.verdict !== 'error');
15
+ const errored = verdicts.filter((v) => v.verdict === 'error');
16
+ // Fail open when the response pool is too thin to mean anything: none
17
+ // responded, or at most half of the configured judges did.
18
+ if (responding.length === 0 || responding.length <= judges.length / 2) {
19
+ return {
20
+ outcome: 'fail-open',
21
+ reason: errored.length > 0
22
+ ? `quorum not met (${responding.length} of ${judges.length} responded; errored: ${errored.map((v) => v.provider).join(', ')})`
23
+ : `quorum not met (${responding.length} of ${judges.length} responded)`,
24
+ verdicts,
25
+ };
26
+ }
27
+ const fails = responding.filter((v) => v.verdict === 'fail').length;
28
+ if (fails > responding.length / 2) {
29
+ return {
30
+ outcome: 'block',
31
+ reason: `${fails} of ${responding.length} responding judges returned VERDICT: fail`,
32
+ verdicts,
33
+ };
34
+ }
35
+ return {
36
+ outcome: 'pass',
37
+ reason: `${fails} of ${responding.length} responding judges returned VERDICT: fail`,
38
+ verdicts,
39
+ };
40
+ }
41
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/hooks/gate.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC;IAC9B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAa,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClC,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAsB;KAClG,CAAC,CAAC,CAAC;IAEJ,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAE9D,sEAAsE;IACtE,2DAA2D;IAC3D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtE,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM,EACJ,OAAO,CAAC,MAAM,GAAG,CAAC;gBAChB,CAAC,CAAC,mBAAmB,UAAU,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,wBAAwB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC9H,CAAC,CAAC,mBAAmB,UAAU,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,aAAa;YAC3E,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACpE,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,GAAG,KAAK,OAAO,UAAU,CAAC,MAAM,2CAA2C;YACnF,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,MAAM;QACf,MAAM,EAAE,GAAG,KAAK,OAAO,UAAU,CAAC,MAAM,2CAA2C;QACnF,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface HookPayload {
2
+ harness?: string;
3
+ cwd?: string;
4
+ sessionId?: string;
5
+ files?: string[];
6
+ event?: string;
7
+ }
8
+ export declare function parseHookPayload(stdinJson: string): HookPayload;
@@ -0,0 +1,63 @@
1
+ // Best-effort parsing of whatever JSON a harness pipes in. Unknown shapes and
2
+ // non-JSON input yield an empty payload - never an error. Key candidates are
3
+ // scanned loosely; this is a documented maintenance surface, not a contract.
4
+ export function parseHookPayload(stdinJson) {
5
+ let obj;
6
+ try {
7
+ const parsed = JSON.parse(stdinJson);
8
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
9
+ return {};
10
+ obj = parsed;
11
+ }
12
+ catch {
13
+ return {};
14
+ }
15
+ const payload = {};
16
+ payload.sessionId =
17
+ str(obj.session_id) ??
18
+ str(obj.sessionId) ??
19
+ str(record(obj.session)?.id);
20
+ payload.cwd =
21
+ str(obj.cwd) ??
22
+ str(record(obj.workspace)?.cwd);
23
+ payload.event =
24
+ str(obj.hook_event_name) ??
25
+ str(obj.event);
26
+ payload.harness =
27
+ str(obj.harness) ??
28
+ str(obj.agent) ??
29
+ (obj.hook_event_name !== undefined ? 'claude-code' : undefined);
30
+ const files = collectFiles(obj);
31
+ if (files.length > 0)
32
+ payload.files = files;
33
+ return payload;
34
+ }
35
+ function collectFiles(obj) {
36
+ const out = [];
37
+ const push = (v) => {
38
+ if (typeof v === 'string' && v.length > 0)
39
+ out.push(v);
40
+ };
41
+ const pushAll = (v) => {
42
+ if (Array.isArray(v))
43
+ v.forEach(push);
44
+ };
45
+ const toolInput = record(obj.tool_input) ?? record(obj.toolInput);
46
+ push(toolInput?.file_path);
47
+ push(toolInput?.filePath);
48
+ pushAll(toolInput?.files);
49
+ push(obj.file);
50
+ push(obj.path);
51
+ pushAll(obj.files);
52
+ pushAll(obj.paths);
53
+ return [...new Set(out)];
54
+ }
55
+ function record(v) {
56
+ return typeof v === 'object' && v !== null && !Array.isArray(v)
57
+ ? v
58
+ : undefined;
59
+ }
60
+ function str(v) {
61
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
62
+ }
63
+ //# sourceMappingURL=payload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/hooks/payload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { HookPayload } from './payload.js';
2
+ export declare function resolveSessionKey(payload: HookPayload, warn?: (msg: string) => void): string;
3
+ export declare function recordEdit(key: string, file: string, cwd: string, env?: NodeJS.ProcessEnv): void;
4
+ export declare function pendingEdits(key: string, env?: NodeJS.ProcessEnv): string[];
5
+ export declare function clearSession(key: string, env?: NodeJS.ProcessEnv): void;
6
+ export declare function stateFilePath(key: string, env?: NodeJS.ProcessEnv, project?: string): string;
@@ -0,0 +1,73 @@
1
+ import * as crypto from 'node:crypto';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ export function resolveSessionKey(payload, warn = defaultWarn) {
6
+ const harness = sanitize(payload.harness ?? 'unknown');
7
+ if (payload.sessionId) {
8
+ return `${harness}-${sanitize(payload.sessionId)}`;
9
+ }
10
+ const cwd = payload.cwd ?? process.cwd();
11
+ const hash = crypto.createHash('sha1').update(cwd).digest('hex').slice(0, 8);
12
+ warn(`council: session id missing from hook payload, using cwd-based key; ` +
13
+ `edit tracking is shared across sessions in ${cwd}`);
14
+ return `${harness}-cwd-${hash}`;
15
+ }
16
+ export function recordEdit(key, file, cwd, env = process.env) {
17
+ const entry = { file: path.resolve(cwd, file), cwd, ts: Date.now() };
18
+ const target = stateFilePath(key, env);
19
+ fs.mkdirSync(path.dirname(target), { recursive: true });
20
+ const fd = fs.openSync(target, 'a');
21
+ try {
22
+ fs.writeSync(fd, JSON.stringify(entry) + '\n');
23
+ fs.fsyncSync(fd); // durable before exit: the stop hook may fire immediately after
24
+ }
25
+ finally {
26
+ fs.closeSync(fd);
27
+ }
28
+ }
29
+ export function pendingEdits(key, env = process.env) {
30
+ const targets = [stateFilePath(key, env), stateFilePath(key, env, 'council-axi')];
31
+ const seen = new Set();
32
+ const out = [];
33
+ for (const target of targets) {
34
+ if (!fs.existsSync(target))
35
+ continue;
36
+ for (const line of fs.readFileSync(target, 'utf8').split('\n')) {
37
+ if (line.trim().length === 0)
38
+ continue;
39
+ let entry;
40
+ try {
41
+ const parsed = JSON.parse(line);
42
+ entry = parsed;
43
+ if (typeof entry.file !== 'string')
44
+ continue;
45
+ }
46
+ catch {
47
+ continue; // killed-process partial write
48
+ }
49
+ if (!seen.has(entry.file)) {
50
+ seen.add(entry.file);
51
+ out.push(entry.file);
52
+ }
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+ export function clearSession(key, env = process.env) {
58
+ fs.rmSync(stateFilePath(key, env), { force: true });
59
+ fs.rmSync(stateFilePath(key, env, 'council-axi'), { force: true });
60
+ }
61
+ export function stateFilePath(key, env = process.env, project = 'council') {
62
+ const base = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0
63
+ ? env.XDG_STATE_HOME
64
+ : path.join(os.homedir(), '.local', 'state');
65
+ return path.join(base, project, `${sanitize(key)}.jsonl`);
66
+ }
67
+ function sanitize(text) {
68
+ return text.replace(/[^a-zA-Z0-9._-]/g, '_');
69
+ }
70
+ function defaultWarn(msg) {
71
+ console.error(msg);
72
+ }
73
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/hooks/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export { runReview, type ReviewOptions } from './runners/review.js';
2
+ export { runPlan, type PlanOptions } from './runners/plan.js';
3
+ export { runDebate, activeParticipants, roundOrder, CALLER, type DebateRunOptions, type DebateProgress } from './debate/loop.js';
4
+ export { buildDebateOutput, renderDebateTOON } from './debate/render.js';
5
+ export { parseHardBlocks, aggregateBlocks, newBlockKey, KEY_LABEL, type BlockAggregate } from './blocks.js';
6
+ export { parseVerdict } from './verdict.js';
7
+ export { renderTOON } from './output.js';
8
+ export { exitCodeFor, type ExitPolicyOptions } from './exit.js';
9
+ export { CouncilError } from './errors.js';
10
+ /**
11
+ * UNSTABLE. Configuration is env-var only today and is being reworked as part
12
+ * of the provider config-file work (backlog item 4 / tracker T-015). This
13
+ * export will change shape. Do not depend on it in long-lived code.
14
+ */
15
+ export { loadConfig, resolveModels, type CouncilConfig } from './config.js';
16
+ export type { AttributedHardBlock, BlockParse, CouncilOutput, CouncilRequest, DebateOutput, DebateRound, DebateSession, DebateTurn, Effort, RunControls, HardBlock, JudgeResult, ProviderConfig, Severity, UnknownJudge, UnparseableReason, Verdict, } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { runReview } from './runners/review.js';
2
+ export { runPlan } from './runners/plan.js';
3
+ export { runDebate, activeParticipants, roundOrder, CALLER } from './debate/loop.js';
4
+ export { buildDebateOutput, renderDebateTOON } from './debate/render.js';
5
+ export { parseHardBlocks, aggregateBlocks, newBlockKey, KEY_LABEL } from './blocks.js';
6
+ export { parseVerdict } from './verdict.js';
7
+ export { renderTOON } from './output.js';
8
+ export { exitCodeFor } from './exit.js';
9
+ export { CouncilError } from './errors.js';
10
+ /**
11
+ * UNSTABLE. Configuration is env-var only today and is being reworked as part
12
+ * of the provider config-file work (backlog item 4 / tracker T-015). This
13
+ * export will change shape. Do not depend on it in long-lived code.
14
+ */
15
+ export { loadConfig, resolveModels } from './config.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAoB,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAA8C,MAAM,kBAAkB,CAAC;AACjI,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC5G,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAA0B,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAsB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface ArtifactInput {
2
+ files?: string[];
3
+ diff?: {
4
+ range?: string;
5
+ };
6
+ stdinContent?: string;
7
+ capBytes?: number;
8
+ }
9
+ export declare function buildPrompt(prompt: string, input: ArtifactInput): {
10
+ text: string;
11
+ warnings: string[];
12
+ };
13
+ export declare function parseModelList(csv: string | undefined): string[] | undefined;
14
+ export declare function parseCapBytesOverride(): number | undefined;
package/dist/input.js ADDED
@@ -0,0 +1,30 @@
1
+ import { assembleArtifacts, formatArtifactPreamble } from './artifacts.js';
2
+ // Synchronous by design: the library accepts already-read content, so nothing
3
+ // here touches process.stdin. The CLI reads stdin and passes stdinContent.
4
+ export function buildPrompt(prompt, input) {
5
+ const bundle = assembleArtifacts({
6
+ files: input.files,
7
+ diff: input.diff,
8
+ stdin: input.stdinContent,
9
+ capBytes: input.capBytes,
10
+ });
11
+ const text = bundle.blocks.length > 0 ? formatArtifactPreamble(bundle) + prompt : prompt;
12
+ return { text, warnings: bundle.warnings };
13
+ }
14
+ // Pure split-and-trim. Deliberately does NOT drop empty entries: resolveModels
15
+ // is the sole owner of the "no list supplied" versus "a list was supplied but
16
+ // named nothing" distinction, and it needs to see the raw entries to tell them
17
+ // apart.
18
+ export function parseModelList(csv) {
19
+ if (csv === undefined)
20
+ return undefined;
21
+ return csv.split(',').map((m) => m.trim());
22
+ }
23
+ export function parseCapBytesOverride() {
24
+ const raw = process.env.COUNCIL_MAX_ARTIFACT_BYTES;
25
+ if (!raw)
26
+ return undefined;
27
+ const n = Number.parseInt(raw, 10);
28
+ return Number.isInteger(n) && n > 0 ? n : undefined;
29
+ }
30
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAS3E,8EAA8E;AAC9E,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,KAAoB;IAEpB,MAAM,MAAM,GAAG,iBAAiB,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,YAAY;QACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,SAAS;AACT,MAAM,UAAU,cAAc,CAAC,GAAuB;IACpD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IACnD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CouncilOutput } from './types.js';
2
+ export declare function renderTOON(output: CouncilOutput): string;
3
+ export declare function previewPrompt(text: string): string;
package/dist/output.js ADDED
@@ -0,0 +1,80 @@
1
+ export function renderTOON(output) {
2
+ const lines = [];
3
+ lines.push(`council[${output.mode}]: "${previewPrompt(output.prompt)}"`);
4
+ lines.push(`judges: ${output.availableCount} of ${output.totalCount} responded`);
5
+ lines.push(`judges[${output.judges.length}]{provider,model,status,verdict}:`);
6
+ for (const judge of output.judges) {
7
+ const verdict = judge.status === 'success'
8
+ ? firstLine(judge.response ?? '')
9
+ : judge.error?.message ?? 'unavailable';
10
+ lines.push(` ${judge.provider},${judge.model},${judge.status},${escapeComma(verdict)}`);
11
+ }
12
+ lines.push(`blocked: ${output.blocked ? 'yes' : 'no'}`);
13
+ pushBlockSection(lines, 'blocks', output.hardBlocks);
14
+ pushBlockSection(lines, 'diagnostic', output.diagnosticBlocks);
15
+ if (output.unknownJudges.length > 0) {
16
+ lines.push(`unknown[${output.unknownJudges.length}]:`);
17
+ for (const u of output.unknownJudges) {
18
+ lines.push(` ${u.provider}: ${u.reason}${u.detail ? ` (${u.detail})` : ''}`);
19
+ }
20
+ }
21
+ const skipped = output.judges.filter((j) => j.status !== 'success');
22
+ if (skipped.length > 0) {
23
+ lines.push(`skipped[${skipped.length}]:`);
24
+ for (const judge of skipped) {
25
+ lines.push(` ${judge.provider}: ${judge.error?.message ?? 'unavailable'}`);
26
+ }
27
+ }
28
+ if (output.warnings && output.warnings.length > 0) {
29
+ lines.push(`warnings[${output.warnings.length}]:`);
30
+ for (const w of output.warnings) {
31
+ lines.push(` ${w}`);
32
+ }
33
+ }
34
+ lines.push('synthesis:');
35
+ for (const line of output.synthesis.split('\n')) {
36
+ lines.push(` ${line}`);
37
+ }
38
+ const help = [];
39
+ if (skipped.length > 0) {
40
+ help.push('Run `npx -y @lanjak/council setup` to check provider authentication');
41
+ const responding = output.judges.filter((j) => j.status === 'success').map((j) => j.provider);
42
+ if (responding.length > 0) {
43
+ help.push(`Run \`npx -y @lanjak/council ${output.mode} "<prompt>" --models ${responding.join(',')}\` to use only responding judges`);
44
+ }
45
+ }
46
+ else {
47
+ const providers = output.judges.map((j) => j.provider).join(',');
48
+ help.push(`Run \`npx -y @lanjak/council ${output.mode} "<prompt>" --models ${providers}\``);
49
+ }
50
+ lines.push(`help[${help.length}]:`);
51
+ for (const h of help) {
52
+ lines.push(` ${h}`);
53
+ }
54
+ return lines.join('\n');
55
+ }
56
+ function firstLine(text) {
57
+ return text.split('\n')[0]?.trim() ?? '';
58
+ }
59
+ function escapeComma(text) {
60
+ return text.replace(/,/g, '\\,');
61
+ }
62
+ // Echo only a short preview of the prompt. A review often embeds an entire
63
+ // artifact (a 71KB plan, say), and dumping it back verbatim on this line made
64
+ // the prompt the bulk of the output.
65
+ export function previewPrompt(text) {
66
+ const PREVIEW_LEN = 200;
67
+ const normalized = text.replace(/\s+/g, ' ').replace(/"/g, '\\"').trim();
68
+ if (normalized.length <= PREVIEW_LEN)
69
+ return normalized;
70
+ return `${normalized.slice(0, PREVIEW_LEN)}... (${normalized.length} chars total)`;
71
+ }
72
+ function pushBlockSection(lines, label, blocks) {
73
+ if (blocks.length === 0)
74
+ return;
75
+ lines.push(`${label}[${blocks.length}]{provider,severity,title,location}:`);
76
+ for (const b of blocks) {
77
+ lines.push(` ${b.provider},${b.severity},${escapeComma(b.title)},${escapeComma(b.location ?? '')}`);
78
+ }
79
+ }
80
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function blockFooterInstruction(key: string): string;
2
+ export declare function reviewSystemPrompt(key: string): string;
3
+ export declare function planSystemPrompt(key: string): string;
@@ -0,0 +1,48 @@
1
+ import { KEY_LABEL } from './blocks.js';
2
+ // A hard block is defined narrowly on purpose. Everything listed here gates,
3
+ // so a judge that files nits as blockers makes the gate useless.
4
+ export function blockFooterInstruction(key) {
5
+ return [
6
+ 'After your prose, end your response with a machine-readable footer.',
7
+ '',
8
+ 'A hard block is a defect that must be fixed before this change ships:',
9
+ 'incorrect behaviour, a security hole, data loss, or a broken contract.',
10
+ 'Style preferences, naming, and suggestions for future work are NOT hard',
11
+ 'blocks - leave those in your prose.',
12
+ '',
13
+ 'The footer must be the last thing you write, and must start with this',
14
+ 'exact key line:',
15
+ '',
16
+ '```',
17
+ `${KEY_LABEL}: ${key}`,
18
+ 'BLOCKERS: 1',
19
+ 'BLOCKER: critical | the admin route has no auth check | src/api.ts:42',
20
+ '```',
21
+ '',
22
+ 'Rules:',
23
+ `- Always emit the "${KEY_LABEL}: ${key}" line, exactly as shown.`,
24
+ '- If you found no hard blocks, write "BLOCKERS: none" and no BLOCKER lines.',
25
+ '- Otherwise write "BLOCKERS: <n>" followed by exactly n BLOCKER lines.',
26
+ '- Each BLOCKER line is: severity | short title | location',
27
+ '- severity is "critical" or "high". location is optional.',
28
+ '- Your prose must not contradict the footer.',
29
+ ].join('\n');
30
+ }
31
+ export function reviewSystemPrompt(key) {
32
+ return [
33
+ 'You are reviewing an artifact as part of an adversarial review council.',
34
+ 'Be adversarial: hunt for defects, question assumptions, and say plainly',
35
+ 'what is wrong rather than hedging. Keep your response concise.',
36
+ '',
37
+ blockFooterInstruction(key),
38
+ ].join('\n');
39
+ }
40
+ export function planSystemPrompt(key) {
41
+ return [
42
+ 'You are reviewing a plan or decision. Be adversarial: question assumptions,',
43
+ 'identify risks, and suggest alternatives. Keep your response concise.',
44
+ '',
45
+ blockFooterInstruction(key),
46
+ ].join('\n');
47
+ }
48
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,6EAA6E;AAC7E,iEAAiE;AACjE,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,OAAO;QACL,qEAAqE;QACrE,EAAE;QACF,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,qCAAqC;QACrC,EAAE;QACF,uEAAuE;QACvE,iBAAiB;QACjB,EAAE;QACF,KAAK;QACL,GAAG,SAAS,KAAK,GAAG,EAAE;QACtB,aAAa;QACb,uEAAuE;QACvE,KAAK;QACL,EAAE;QACF,QAAQ;QACR,sBAAsB,SAAS,KAAK,GAAG,2BAA2B;QAClE,6EAA6E;QAC7E,wEAAwE;QACxE,2DAA2D;QAC3D,2DAA2D;QAC3D,8CAA8C;KAC/C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,OAAO;QACL,yEAAyE;QACzE,yEAAyE;QACzE,gEAAgE;QAChE,EAAE;QACF,sBAAsB,CAAC,GAAG,CAAC;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO;QACL,6EAA6E;QAC7E,uEAAuE;QACvE,EAAE;QACF,sBAAsB,CAAC,GAAG,CAAC;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,12 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { LLMProvider, ChatOptions, ChatResult, AuthResult, ProviderCapabilities } from './base.js';
3
+ import type { ProviderConfig } from '../types.js';
4
+ export declare class AnthropicProvider extends LLMProvider {
5
+ readonly name: string;
6
+ readonly displayName: string;
7
+ readonly capabilities: ProviderCapabilities;
8
+ protected client: Anthropic;
9
+ constructor(config: ProviderConfig);
10
+ checkAuth(): Promise<AuthResult>;
11
+ chat(options: ChatOptions): Promise<ChatResult>;
12
+ }
@@ -0,0 +1,98 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { LLMProvider } from './base.js';
3
+ import { DEFAULT_ANTHROPIC_MAX_TOKENS } from './constants.js';
4
+ export class AnthropicProvider extends LLMProvider {
5
+ name;
6
+ displayName;
7
+ capabilities;
8
+ client;
9
+ constructor(config) {
10
+ super(config);
11
+ this.name = config.name ?? 'unknown';
12
+ this.displayName = config.displayName ?? config.name ?? 'Unknown Provider';
13
+ this.capabilities = { supportsReasoning: false, supportsJsonMode: false };
14
+ // config.apiKey is always a string (config.ts defaults it to '' when
15
+ // unset, never undefined), so passing it straight through means the SDK
16
+ // never falls back to an ambient ANTHROPIC_API_KEY from the process
17
+ // environment - the per-judge key stays authoritative.
18
+ this.client = new Anthropic({
19
+ apiKey: config.apiKey,
20
+ ...(config.baseURL ? { baseURL: config.baseURL } : {}),
21
+ });
22
+ }
23
+ async checkAuth() {
24
+ // Unlike OpenAICompatibleProvider, baseURL is not required here: the
25
+ // Anthropic SDK has a built-in default endpoint (api.anthropic.com), so
26
+ // the simplest valid config omits ${PREFIX}_BASE_URL entirely.
27
+ if (!this.config.apiKey) {
28
+ return {
29
+ available: true,
30
+ authenticated: false,
31
+ detail: `${this.displayName} API key is not set`,
32
+ };
33
+ }
34
+ return { available: true, authenticated: true, detail: `${this.displayName} API key is set` };
35
+ }
36
+ async chat(options) {
37
+ const temperature = options.temperature ?? this.config.temperature;
38
+ const system = options.system?.trim() ? options.system : undefined;
39
+ // Uses the streaming helper (accumulated into one final message) even
40
+ // though this provider never exposes streaming to its own callers: the
41
+ // SDK's non-streaming create() throws client-side, before any network
42
+ // call, whenever max_tokens is large enough that the request could
43
+ // plausibly run past 10 minutes (see calculateNonstreamingTimeout in
44
+ // @anthropic-ai/sdk's client.js) - a value like 65536, which some
45
+ // OpenAI-compatible reasoning models may use,
46
+ // trips this every time (confirmed in production: every Anthropic-kind
47
+ // provider failed once max_tokens was set that high). stream().
48
+ // finalMessage() never takes the non-streaming path, so it isn't
49
+ // subject to that guard, while still resolving to the one accumulated
50
+ // Message - same shape create() would have returned.
51
+ const response = await this.client.messages
52
+ .stream({
53
+ model: options.model,
54
+ ...((options.effort ?? this.config.effort) !== undefined
55
+ ? { output_config: { effort: options.effort ?? this.config.effort } }
56
+ : {}),
57
+ max_tokens: options.maxTokens ?? this.config.maxTokens ?? DEFAULT_ANTHROPIC_MAX_TOKENS,
58
+ messages: [{ role: 'user', content: options.prompt }],
59
+ ...(system ? { system } : {}),
60
+ ...(temperature !== undefined ? { temperature } : {}),
61
+ })
62
+ .finalMessage();
63
+ // Non-text blocks (tool_use, thinking, etc.) are intentionally dropped:
64
+ // no tool use is requested and extended thinking is off by design (see
65
+ // spec Non-goals), so only `text` blocks are ever expected in practice.
66
+ const content = response.content
67
+ .filter((block) => block.type === 'text')
68
+ .map((block) => block.text)
69
+ .join('\n');
70
+ // response.usage is a required field on Anthropic's Message type (never
71
+ // undefined), unlike OpenAI's optional `completion.usage` - no presence
72
+ // check needed here, only per-field ?? 0 fallbacks since the individual
73
+ // numeric fields are nullable.
74
+ //
75
+ // Anthropic's own docs define total input as the SUM of input_tokens,
76
+ // cache_creation_input_tokens, and cache_read_input_tokens (these are
77
+ // NOT overlapping/subset values - input_tokens already excludes cached
78
+ // tokens). totalTokens below reflects that real total. Note this means
79
+ // totalTokens can exceed inputTokens + outputTokens for a cached
80
+ // request, unlike the OpenAI-compatible provider where they're always
81
+ // equal - inputTokens intentionally stays the raw (cache-excluding)
82
+ // input_tokens field, matching Anthropic's own field semantics, rather
83
+ // than folding cache tokens into it to preserve an invariant that
84
+ // nothing downstream actually relies on (checked: no code in this repo
85
+ // computes inputTokens + outputTokens instead of using totalTokens
86
+ // directly).
87
+ const usage = {
88
+ inputTokens: response.usage.input_tokens ?? 0,
89
+ outputTokens: response.usage.output_tokens ?? 0,
90
+ totalTokens: (response.usage.input_tokens ?? 0) +
91
+ (response.usage.output_tokens ?? 0) +
92
+ (response.usage.cache_creation_input_tokens ?? 0) +
93
+ (response.usage.cache_read_input_tokens ?? 0),
94
+ };
95
+ return { content, usage };
96
+ }
97
+ }
98
+ //# sourceMappingURL=anthropic.js.map