council-review 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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +374 -0
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +19 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/cli.d.ts +14 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +829 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +61 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +235 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/herdr.d.ts +36 -0
  16. package/dist/herdr.d.ts.map +1 -0
  17. package/dist/herdr.js +196 -0
  18. package/dist/herdr.js.map +1 -0
  19. package/dist/index.d.ts +30 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +30 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/levels.d.ts +9 -0
  24. package/dist/levels.d.ts.map +1 -0
  25. package/dist/levels.js +19 -0
  26. package/dist/levels.js.map +1 -0
  27. package/dist/merge.d.ts +90 -0
  28. package/dist/merge.d.ts.map +1 -0
  29. package/dist/merge.js +441 -0
  30. package/dist/merge.js.map +1 -0
  31. package/dist/node-guard.d.ts +9 -0
  32. package/dist/node-guard.d.ts.map +1 -0
  33. package/dist/node-guard.js +21 -0
  34. package/dist/node-guard.js.map +1 -0
  35. package/dist/panel.d.ts +89 -0
  36. package/dist/panel.d.ts.map +1 -0
  37. package/dist/panel.js +275 -0
  38. package/dist/panel.js.map +1 -0
  39. package/dist/picker.d.ts +24 -0
  40. package/dist/picker.d.ts.map +1 -0
  41. package/dist/picker.js +182 -0
  42. package/dist/picker.js.map +1 -0
  43. package/dist/providers.d.ts +66 -0
  44. package/dist/providers.d.ts.map +1 -0
  45. package/dist/providers.js +544 -0
  46. package/dist/providers.js.map +1 -0
  47. package/dist/report.d.ts +116 -0
  48. package/dist/report.d.ts.map +1 -0
  49. package/dist/report.js +493 -0
  50. package/dist/report.js.map +1 -0
  51. package/dist/resolve.d.ts +58 -0
  52. package/dist/resolve.d.ts.map +1 -0
  53. package/dist/resolve.js +147 -0
  54. package/dist/resolve.js.map +1 -0
  55. package/dist/reviewer-spawn.d.ts +67 -0
  56. package/dist/reviewer-spawn.d.ts.map +1 -0
  57. package/dist/reviewer-spawn.js +94 -0
  58. package/dist/reviewer-spawn.js.map +1 -0
  59. package/dist/reviewer-tools.d.ts +113 -0
  60. package/dist/reviewer-tools.d.ts.map +1 -0
  61. package/dist/reviewer-tools.js +557 -0
  62. package/dist/reviewer-tools.js.map +1 -0
  63. package/dist/runner.d.ts +79 -0
  64. package/dist/runner.d.ts.map +1 -0
  65. package/dist/runner.js +811 -0
  66. package/dist/runner.js.map +1 -0
  67. package/dist/schema.d.ts +63 -0
  68. package/dist/schema.d.ts.map +1 -0
  69. package/dist/schema.js +302 -0
  70. package/dist/schema.js.map +1 -0
  71. package/dist/scope.d.ts +40 -0
  72. package/dist/scope.d.ts.map +1 -0
  73. package/dist/scope.js +372 -0
  74. package/dist/scope.js.map +1 -0
  75. package/dist/snapshot.d.ts +57 -0
  76. package/dist/snapshot.d.ts.map +1 -0
  77. package/dist/snapshot.js +471 -0
  78. package/dist/snapshot.js.map +1 -0
  79. package/dist/status.d.ts +90 -0
  80. package/dist/status.d.ts.map +1 -0
  81. package/dist/status.js +336 -0
  82. package/dist/status.js.map +1 -0
  83. package/dist/thinking.d.ts +53 -0
  84. package/dist/thinking.d.ts.map +1 -0
  85. package/dist/thinking.js +116 -0
  86. package/dist/thinking.js.map +1 -0
  87. package/package.json +81 -0
  88. package/src/bin.ts +20 -0
  89. package/src/cli.ts +996 -0
  90. package/src/config.ts +338 -0
  91. package/src/herdr.ts +231 -0
  92. package/src/index.ts +30 -0
  93. package/src/levels.ts +23 -0
  94. package/src/merge.ts +548 -0
  95. package/src/node-guard.ts +25 -0
  96. package/src/panel.ts +354 -0
  97. package/src/picker.ts +230 -0
  98. package/src/providers.ts +778 -0
  99. package/src/report.ts +594 -0
  100. package/src/resolve.ts +179 -0
  101. package/src/reviewer-spawn.ts +131 -0
  102. package/src/reviewer-tools.ts +715 -0
  103. package/src/runner.ts +1120 -0
  104. package/src/schema.ts +373 -0
  105. package/src/scope.ts +448 -0
  106. package/src/snapshot.ts +536 -0
  107. package/src/status.ts +470 -0
  108. package/src/thinking.ts +148 -0
package/dist/picker.js ADDED
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Three-stage interactive panel selection: providers, then models scoped to those providers,
3
+ * then a thinking level per selected reasoning model. See `openspec/changes/add-council-review/
4
+ * specs/council-review/panel-selection/spec.md`'s "Staged interactive selection" requirement.
5
+ *
6
+ * This module never writes configuration itself — it only resolves a `Reviewer[]`. The caller
7
+ * (`cli.ts`) is what persists a panel, which is what makes "cancelling writes nothing" true by
8
+ * construction rather than by a save/rollback dance in here. It also never runs the
9
+ * vendor-independence guard: that is `panel.ts`'s `enforceIndependence`, called by the CLI once
10
+ * selection finishes and again immediately before launch.
11
+ */
12
+ import { PassThrough } from 'node:stream';
13
+ import { checkbox, select } from '@inquirer/prompts';
14
+ import { resolveThinking, supportedLevels } from './thinking.js';
15
+ export class PickerCancelled extends Error {
16
+ exitCode = 2;
17
+ constructor(message = 'selection cancelled; no configuration was written') {
18
+ super(message);
19
+ this.name = 'PickerCancelled';
20
+ Object.setPrototypeOf(this, PickerCancelled.prototype);
21
+ }
22
+ }
23
+ export class PickerNonInteractive extends Error {
24
+ exitCode = 2;
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = 'PickerNonInteractive';
28
+ Object.setPrototypeOf(this, PickerNonInteractive.prototype);
29
+ }
30
+ }
31
+ /**
32
+ * `@inquirer/prompts` rejects with `ExitPromptError` on Ctrl+C / SIGINT / a closed input stream,
33
+ * and with `CancelPromptError` when a prompt's own `context.signal` (not used here) fires. Both
34
+ * are checked by `.name` rather than `instanceof` against an imported class, because
35
+ * `@inquirer/core` is a transitive dependency of `@inquirer/prompts` — this package depends only
36
+ * on the latter, per the contract's "do not edit package.json" rule.
37
+ */
38
+ function isCancellation(err) {
39
+ return (err instanceof Error && (err.name === 'ExitPromptError' || err.name === 'CancelPromptError'));
40
+ }
41
+ function defaultIO() {
42
+ return {
43
+ input: process.stdin,
44
+ output: process.stdout,
45
+ isTTY: process.stdin.isTTY === true,
46
+ };
47
+ }
48
+ function formatTokenCount(n) {
49
+ if (n === null)
50
+ return '?';
51
+ if (n >= 1_000_000)
52
+ return `${(n / 1_000_000).toFixed(1)}M`;
53
+ if (n >= 1_000)
54
+ return `${(n / 1_000).toFixed(1)}K`;
55
+ return String(n);
56
+ }
57
+ function formatCostRate(n) {
58
+ return n === null ? '?' : `$${n}`;
59
+ }
60
+ function providerChoiceLabel(p) {
61
+ const auth = p.authType ?? 'unknown auth';
62
+ const models = p.modelCount === 1 ? '1 model' : `${p.modelCount} models`;
63
+ return `${p.id} (${auth}, ${models})`;
64
+ }
65
+ function modelChoiceLabel(m) {
66
+ return (`${m.provider}/${m.id} — vendor: ${m.vendor}, context: ${formatTokenCount(m.contextWindow)}, ` +
67
+ `in: ${formatCostRate(m.inputCostPerMTok)}/Mtok, out: ${formatCostRate(m.outputCostPerMTok)}/Mtok`);
68
+ }
69
+ // JSON-encoded rather than joined with a separator character, because a host model id may
70
+ // itself contain '/' (or, in principle, anything else) — encoding sidesteps needing a separator
71
+ // character that is provably absent from every id.
72
+ function modelKey(provider, id) {
73
+ return JSON.stringify([provider, id]);
74
+ }
75
+ function splitModelKey(key) {
76
+ const [provider, id] = JSON.parse(key);
77
+ return { provider, id };
78
+ }
79
+ /**
80
+ * Builds a fresh `{ input, output }` context for one prompt call. Each `@inquirer/prompts` call
81
+ * ends its own internal stream wrapped around whatever `output` it is given as part of normal
82
+ * cleanup, which — unlike `process.stdout`, which silently tolerates `.end()` — would close a
83
+ * caller-supplied stream for good after the very first of this picker's three stages. Relaying
84
+ * through a disposable `PassThrough` with `{ end: false }` on the downstream pipe means only the
85
+ * disposable relay ever gets closed; `io.output` stays open for every later stage.
86
+ *
87
+ * The relay also needs to mirror `io.output`'s `columns`/`rows`/`isTTY`, because that is how
88
+ * inquirer measures the terminal: `@inquirer/core`'s `readlineWidth()` goes through `cli-width`,
89
+ * and node's own `readline.Interface#columns`, both of which end up reading `.columns` off
90
+ * whatever stream inquirer was handed — here, the relay, not the real terminal. A relay that
91
+ * reports no width silently falls back to 80 columns, which hard-wraps long model labels mid-word
92
+ * and makes `ScreenManager` erase the wrong number of lines on every re-render (arrow keys leave
93
+ * stale copies of the list on screen). Defined as getters rather than copied once, so a mid-prompt
94
+ * terminal resize is still reflected on the next render, exactly as it would be against real
95
+ * stdout.
96
+ */
97
+ function promptContext(io) {
98
+ const relay = new PassThrough();
99
+ Object.defineProperties(relay, {
100
+ columns: { get: () => io.output.columns, configurable: true },
101
+ rows: { get: () => io.output.rows, configurable: true },
102
+ isTTY: { get: () => io.output.isTTY === true, configurable: true },
103
+ });
104
+ relay.pipe(io.output, { end: false });
105
+ return { input: io.input, output: relay };
106
+ }
107
+ /**
108
+ * Runs the three-stage picker: providers, then models scoped to those providers, then a
109
+ * thinking level per selected reasoning model (non-reasoning selections are shown as "no
110
+ * thinking" and skip the prompt entirely). Throws `PickerNonInteractive` (exit code 2) without a
111
+ * terminal, before any prompt runs, and `PickerCancelled` (exit code 2) if the user cancels at
112
+ * any stage.
113
+ */
114
+ export async function pickPanel(catalog, io = defaultIO()) {
115
+ if (!io.isTTY) {
116
+ throw new PickerNonInteractive('interactive selection requires a terminal; supply a panel via "council-review init" ' +
117
+ 'configuration or the --models flag in a non-interactive environment.');
118
+ }
119
+ try {
120
+ const readyProviders = catalog.providers.filter((p) => p.ready);
121
+ const selectedProviderIds = await checkbox({
122
+ message: 'Select providers to draw reviewers from:',
123
+ required: true,
124
+ choices: readyProviders.map((p) => ({ value: p.id, name: providerChoiceLabel(p) })),
125
+ }, promptContext(io));
126
+ const scopedModels = catalog.models.filter((m) => selectedProviderIds.includes(m.provider));
127
+ const selectedModelKeys = await checkbox({
128
+ message: 'Select models for the panel:',
129
+ required: true,
130
+ choices: scopedModels.map((m) => ({
131
+ value: modelKey(m.provider, m.id),
132
+ name: modelChoiceLabel(m),
133
+ })),
134
+ }, promptContext(io));
135
+ const selectedModels = selectedModelKeys.map((key) => {
136
+ const { provider, id } = splitModelKey(key);
137
+ const found = scopedModels.find((m) => m.provider === provider && m.id === id);
138
+ if (!found) {
139
+ // Unreachable in practice — every choice value is built from `scopedModels` above — but
140
+ // guarded rather than asserted away, since a thrown internal error is safer than a
141
+ // reviewer silently resolving to `undefined`.
142
+ throw new Error(`picker: selected model not found in scope: ${provider}/${id}`);
143
+ }
144
+ return found;
145
+ });
146
+ const reviewers = [];
147
+ for (const model of selectedModels) {
148
+ const supported = supportedLevels(model);
149
+ const label = `${model.provider}/${model.id}`;
150
+ if (supported.length === 0) {
151
+ io.output.write(`${label}: no thinking (model does not support reasoning)\n`);
152
+ reviewers.push({
153
+ provider: model.provider,
154
+ model: model.id,
155
+ vendor: model.vendor,
156
+ catalog: model,
157
+ thinking: resolveThinking(model, {}),
158
+ });
159
+ continue;
160
+ }
161
+ const picked = await select({
162
+ message: `Thinking level for ${label}:`,
163
+ choices: supported.map((level) => ({ value: level, name: level })),
164
+ }, promptContext(io));
165
+ reviewers.push({
166
+ provider: model.provider,
167
+ model: model.id,
168
+ vendor: model.vendor,
169
+ catalog: model,
170
+ thinking: resolveThinking(model, { cliPin: picked }),
171
+ });
172
+ }
173
+ return reviewers;
174
+ }
175
+ catch (err) {
176
+ if (isCancellation(err)) {
177
+ throw new PickerCancelled();
178
+ }
179
+ throw err;
180
+ }
181
+ }
182
+ //# sourceMappingURL=picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker.js","sourceRoot":"","sources":["../src/picker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Model discovery and vendor mapping.
3
+ *
4
+ * Reads the review agent host's model catalog (or, failing that, its model-listing command) to
5
+ * learn which providers and models are available, without ever making a model call and without
6
+ * ever opening the host's credential store. See `openspec/changes/add-council-review/specs/
7
+ * council-review/model-discovery/spec.md` for the requirements this module satisfies.
8
+ */
9
+ import type { ThinkingLevel } from './levels.js';
10
+ export interface CatalogModel {
11
+ id: string;
12
+ name: string;
13
+ provider: string;
14
+ vendor: string;
15
+ contextWindow: number | null;
16
+ maxOutputTokens: number | null;
17
+ inputCostPerMTok: number | null;
18
+ outputCostPerMTok: number | null;
19
+ reasoning: boolean;
20
+ thinkingLevelMap: Partial<Record<ThinkingLevel, string | null>> | undefined;
21
+ }
22
+ export interface ProviderInfo {
23
+ id: string;
24
+ ready: boolean;
25
+ authType: string | null;
26
+ reason: string | null;
27
+ modelCount: number;
28
+ }
29
+ export interface Catalog {
30
+ providers: ProviderInfo[];
31
+ models: CatalogModel[];
32
+ skipped: number;
33
+ source: 'store' | 'listing';
34
+ }
35
+ export interface DiscoveryOptions {
36
+ vendorOverrides?: Record<string, string>;
37
+ homeDir?: string;
38
+ piBin?: string;
39
+ }
40
+ export declare class DiscoveryError extends Error {
41
+ readonly exitCode: 2;
42
+ constructor(message: string);
43
+ }
44
+ export declare function deriveVendor(provider: string, modelId: string, overrides: Record<string, string>): string;
45
+ /** vendor -> ['provider/modelId', ...]; only groups with >1 member matter to callers, but return all. */
46
+ export declare function collapsedVendorGroups(models: readonly CatalogModel[]): Map<string, string[]>;
47
+ interface ListedModel {
48
+ provider: string;
49
+ id: string;
50
+ contextWindow: number | null;
51
+ maxOutputTokens: number | null;
52
+ reasoning: boolean;
53
+ }
54
+ /**
55
+ * Parses the plain-text table printed by `pi --list-models`. Only lines matching the exact
56
+ * six-column data shape (provider, model, context, max-out, thinking, images) after the header
57
+ * are treated as models; everything else — the header itself, blank lines, and extension banner
58
+ * lines printed at startup (which happen to also tokenize into a handful of words) — is dropped
59
+ * silently, because banner lines never carry a `yes`/`no` thinking column.
60
+ */
61
+ export declare function parseListModelsOutput(text: string): ListedModel[];
62
+ export declare function loadCatalog(opts?: DiscoveryOptions): Promise<Catalog>;
63
+ export declare function readyModels(c: Catalog): CatalogModel[];
64
+ export declare function findModel(c: Catalog, provider: string, modelId: string): CatalogModel | undefined;
65
+ export {};
66
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CAC7E;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,QAAQ,EAAG,CAAC,CAAU;gBAEnB,OAAO,EAAE,MAAM;CAI5B;AA2DD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAgBR;AAED,yGAAyG;AACzG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAY5F;AA8SD,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;CACpB;AAiBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAmCjE;AA2ND,wBAAsB,WAAW,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CA6B3E;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,EAAE,CAGtD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEjG"}