agent-council 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,852 @@
1
+ # agent-council
2
+
3
+ Run one prompt through several AI coding agents at once, against your
4
+ own project, and get two things back: a **consolidated report** that is
5
+ better than any single agent's, and a **head-to-head comparison** of the
6
+ models and providers behind the agents, measured on speed, token usage,
7
+ cost, and quality.
8
+
9
+ agent-council runs the **same analytical prompt** through each agent
10
+ using the [Pi](https://github.com/earendil-works/pi) coding-agent harness,
11
+ with whatever mix of providers and models you choose. It saves each
12
+ agent's final response as a separate Markdown report and records how long
13
+ it took, how many tokens it used, and what it cost. A **consolidator**
14
+ agent then combines the reports into a findings report of consensus
15
+ findings, unique findings, and contradictions, and a companion report
16
+ that assesses what each agent contributed.
17
+
18
+ **New to Ruby? [Install agent-council from scratch](INSTALL.md)**
19
+
20
+ ## Why use it
21
+
22
+ ### A council of second opinions
23
+
24
+ - **Catch what one model misses.** Different models notice different
25
+ problems and weigh trade-offs differently; asking several surfaces
26
+ blind spots that no single answer reveals.
27
+ - **Get one report to read, not five.** The optional consolidator reads
28
+ every analyst report, checks their claims against your project where it
29
+ can, and writes a synthesis, so you can see where the models agree and
30
+ where they differ.
31
+
32
+ ### A benchmark built from your own work
33
+
34
+ Public leaderboards tell you how models do on someone else's tasks.
35
+ agent-council tells you how they do on *yours*: your codebase, your
36
+ questions, your standards. Every run is a controlled experiment, with the
37
+ same prompt, the same repository, and the same tools at the same moment,
38
+ so the differences you see are differences in the models and in the
39
+ providers serving them.
40
+
41
+ - **Speed.** Every agent's wall-clock duration, side by side. Find out
42
+ which models answer in two minutes and which take twenty, and whether
43
+ the wait buys anything.
44
+ - **Tokens and cost.** Input, cached input, and output tokens, and dollar
45
+ cost where pi can price the model, per agent and in total. See whether
46
+ the model that costs ten times as much finds ten times as much.
47
+ - **Quality.** The reports sit side by side, labeled by source. The
48
+ consolidator shows which findings each model shared with the others,
49
+ which only it found, and where models contradicted each other, and its
50
+ *Agent performance* section assesses each agent's depth of research
51
+ (did it read the code, run commands, verify its claims?) and how much it
52
+ added, as opposed to noise or errors.
53
+ - **Providers, not just models.** Run the same model through different
54
+ providers, such as its maker's API and an aggregator, and compare their
55
+ latency, price, and reliability. Failures are recorded with their
56
+ reasons, so an unreliable provider shows up in the record.
57
+ - **Evidence that accumulates.** Each run's measurements are saved in a
58
+ machine-readable `run.json`. Over weeks of real work, those files
59
+ become a dataset for choosing your default model, your ensemble, and
60
+ your budget from your own results, not from marketing or hunches.
61
+
62
+ ### Repeatable and cheap to set up
63
+
64
+ A prompt file and a short YAML config are all a run needs; every run's
65
+ reports, prompt, config, and measurements are saved together in a
66
+ timestamped directory, so a comparison can be rerun next month, against
67
+ next month's models.
68
+
69
+ The project is at an early stage; support for more harnesses than Pi,
70
+ such as Claude Code and Codex, is among the
71
+ [planned features](DESIGN.md#18-status-and-future-work), and will extend
72
+ these comparisons to the same model running under different harnesses. See
73
+ [`DESIGN.md`](DESIGN.md) for the rationale.
74
+
75
+ > **Between releases**, the documentation in the repository describes the
76
+ > next release, not the latest published one, so it may cover features
77
+ > (and a version) that are not yet in a released gem. The documentation
78
+ > installed with the gem matches the installed version.
79
+
80
+ ## Quick start
81
+
82
+ A first run from scratch, using two models and a consolidator that
83
+ synthesizes their reports. The sections after this one cover each step
84
+ in detail.
85
+
86
+ 1. **Install the prerequisites:** Ruby 3.3+ (or JRuby 10+) and the
87
+ [`pi` CLI](https://github.com/earendil-works/pi) (`pi --version`). See
88
+ [Prerequisites](#prerequisites), or [INSTALL.md](INSTALL.md) if you
89
+ need to install Ruby too.
90
+ 2. **Set up credentials for at least one provider in pi** (see
91
+ [pi's documentation][pi-docs]), then list the models available to pi
92
+ (a listed model can still be one your plan doesn't include). Two
93
+ different providers give more diverse viewpoints, but two models from
94
+ one provider work too:
95
+
96
+ ```bash
97
+ pi --list-models
98
+ ```
99
+
100
+ 3. **Install the gem:**
101
+
102
+ ```bash
103
+ gem install agent-council
104
+ ```
105
+
106
+ 4. **Go to the project you want analyzed:**
107
+
108
+ ```bash
109
+ cd /path/to/the/project/you/want/analyzed
110
+ ```
111
+
112
+ 5. **Write a config** named `agent-council.yml` there, replacing the
113
+ placeholders with providers and models from step 2 (copy its
114
+ `provider` and `model` columns as they are). The
115
+ `consolidator` can be any model, including one of the agents:
116
+
117
+ ```yaml
118
+ # agent-council.yml
119
+ agents:
120
+ - provider: <provider-1>
121
+ model: <model-1>
122
+ - provider: <provider-2>
123
+ model: <model-2>
124
+ consolidator:
125
+ provider: <provider-1>
126
+ model: <model-1>
127
+ ```
128
+
129
+ 6. **Write a prompt and run it** from that same directory (the prompt
130
+ file can live anywhere):
131
+
132
+ ```bash
133
+ echo 'In one paragraph, what does this project do?' > what-is-this-prompt.md
134
+ agent-council what-is-this-prompt.md
135
+ ```
136
+
137
+ This calls each configured model at your providers' usual rates. Any
138
+ files an agent reads, and the output of any commands it runs, go to
139
+ that agent's provider; see [Safety and privacy](#safety-and-privacy).
140
+
141
+ Each model's answer is saved as a Markdown file under
142
+ `agent-council-reports/<yyyy-mm-dd>/<hh-mm-ss>/reviews/`, and the
143
+ consolidated report is written to the run directory itself, split into a
144
+ `_findings.md` file (findings, contradictions, and overall assessment, with
145
+ no front matter or orchestration tables, so it can be pasted into an AI chat as is) and a
146
+ `_meta.md` file (front matter, tables, agents consulted, and agent
147
+ performance), or a single `_consolidated_...md` file if the model's report
148
+ couldn't be split; see
149
+ [What you get](#what-you-get). To add more models or change the
150
+ consolidator, see
151
+ [Discovering and configuring models](#discovering-and-configuring-models).
152
+
153
+ ## Prerequisites
154
+
155
+ - Ruby 3.3 or later, or JRuby 10 or later (standard library only for the
156
+ tool itself; CI tests 3.3, 3.4, 4.0, JRuby 10.0 and the latest
157
+ JRuby)
158
+ - The [`pi` CLI](https://github.com/earendil-works/pi) installed
159
+ (`pi --version`; tested with pi 1.3.0 and 1.4.0)
160
+ - Credentials for at least one provider (several give more diverse
161
+ viewpoints), configured through pi itself (see
162
+ [pi's documentation][pi-docs])
163
+ - On Windows, WSL (e.g. Ubuntu): native Windows is not supported. Install
164
+ and run Ruby, pi, and agent-council inside WSL, from a WSL shell
165
+ ([INSTALL.md](INSTALL.md) has the steps). Windows drives are reachable
166
+ at `/mnt/c/...`, but file access there is much slower than in the Linux
167
+ filesystem (e.g. `~/projects`). Set up pi's provider credentials inside
168
+ WSL too; WSL doesn't share them with Windows.
169
+
170
+ ## Installation
171
+
172
+ With [the prerequisites](#prerequisites) in place (or following
173
+ [INSTALL.md](INSTALL.md) from scratch), install the gem to make the
174
+ `agent-council` command available on your RubyGems executable path:
175
+
176
+ ```bash
177
+ gem install agent-council
178
+ agent-council -h
179
+ ```
180
+
181
+ The gem creates no config for you; it ships samples in `examples/configs/`. See
182
+ [Discovering and configuring models](#discovering-and-configuring-models)
183
+ to write one. To work on agent-council itself, see
184
+ [Development](#development).
185
+
186
+ ### Finding the installed documentation and examples
187
+
188
+ The installed gem contains this README, [`INSTALL.md`](INSTALL.md),
189
+ [`DESIGN.md`](DESIGN.md), [`code-review-agents.md`](code-review-agents.md),
190
+ `RELEASE_NOTES.md`, and the sample prompts and configs under `examples/`,
191
+ all matching the installed version. The help output shows where they are
192
+ for the copy of `agent-council` you are actually running:
193
+
194
+ ```bash
195
+ agent-council -h # see "Documentation (Local)" and "Examples (Local)"
196
+ ```
197
+
198
+ To get the gem's directory in a shell variable:
199
+
200
+ ```bash
201
+ AC_HOME=$(ruby <<'RUBY'
202
+ print Gem::Specification
203
+ .find_by_name('agent-council', '>= 0.a')
204
+ .full_gem_path
205
+ RUBY
206
+ )
207
+ ```
208
+
209
+ If several versions are installed, this finds the newest, prereleases
210
+ included: the same one a plain `agent-council` command runs.
211
+
212
+ ## Discovering and configuring models
213
+
214
+ ```bash
215
+ pi --list-models # models of the providers you have credentials for
216
+ ```
217
+
218
+ `pi --list-models` lists only the models of providers pi has
219
+ credentials for, not its whole catalog, so a provider missing from it
220
+ may just lack credentials. A listed model can still be unusable, e.g.
221
+ one your plan doesn't include. Credentials, and which one pi uses when
222
+ a provider has several, are pi's business; see
223
+ [pi's documentation][pi-docs].
224
+
225
+ Edit the config to choose the ensemble. The config is the optional second
226
+ argument; if omitted, `./agent-council.yml` in the current directory is used
227
+ (parent directories are not searched), else `~/.agent-council.yml` in your
228
+ home directory. It is an error if neither exists. Each entry becomes one
229
+ pi run with that `--provider` and `--model` (see
230
+ [How it works](#how-it-works-and-what-pi-is-told) for the full command), so no Ruby changes
231
+ are needed to add, remove, or swap models. A small config (replace the
232
+ providers and models with ones you have credentials for):
233
+
234
+ ```yaml
235
+ # agent-council.yml
236
+ agents:
237
+ - provider: opencode-go
238
+ model: kimi-k2.7-code
239
+ - provider: openai
240
+ model: gpt-6.1-sol
241
+ consolidator: # optional; omit to skip consolidation
242
+ provider: opencode-go
243
+ model: kimi-k2.7-code
244
+ ```
245
+
246
+ Use at least two agents: a council of one gives a single viewpoint, and
247
+ consolidating a single report adds cost without adding anything.
248
+
249
+ The config must be a YAML mapping with a non-empty `agents` list. Each
250
+ agent, and the consolidator, needs string `provider` and `model` values
251
+ (quote any value YAML would read as something else, such as a number,
252
+ a date, or `yes`); other keys are ignored.
253
+
254
+ More samples are in [`examples/configs/`](examples/configs/), which is
255
+ included in the installed gem. For scripts and agents,
256
+ [`pi-models`](#occasional-utility-pi-models) lists the models in
257
+ machine-readable forms.
258
+
259
+ For a recommended code-review ensemble, see
260
+ [`code-review-agents.md`](code-review-agents.md). It is Markdown, not a
261
+ config file: copy its YAML block into your config.
262
+
263
+ An optional top-level `consolidator:` entry (provider + model, same
264
+ shape as an agent) runs after all agents finish. That model receives:
265
+
266
+ - the original prompt;
267
+ - the analyst reports in `reviews/`, each labeled with its source model;
268
+ - a list of the agents without a report, with each one's failure reason
269
+ (in a normal run);
270
+ - the instructions in `lib/agent_council/templates/consolidate_prompt.md`
271
+ in the gem's directory (`$AC_HOME`; see
272
+ [Finding the installed documentation and examples](#finding-the-installed-documentation-and-examples))
273
+ or a source checkout, where you can edit it to tune the consolidation.
274
+
275
+ agent-council saves its response as a report in the run dir itself (see
276
+ [What you get](#what-you-get) for its name). Remove the key to skip consolidation. To
277
+ consolidate a run that has already finished, see
278
+ [Consolidating a finished run](#consolidating-a-finished-run).
279
+
280
+ ## Running
281
+
282
+ ```bash
283
+ cd /path/to/the/repo/you/want/analyzed
284
+ agent-council /path/to/prompt.md
285
+ ```
286
+
287
+ There is no option to choose the directory analyzed: Pi analyzes its **own
288
+ working directory**, which is the directory you run `agent-council` from. Run it from
289
+ the project you want analyzed; the prompt file can live anywhere, as can a
290
+ config given as an argument.
291
+ Reports land in `./agent-council-reports` under that working directory,
292
+ so each analyzed project gets its own reports tree. To put them
293
+ elsewhere, pass `-o DIR` / `--output-dir DIR`; a relative `DIR` is
294
+ resolved against the working directory. (An explicit `--output-dir` can't be
295
+ combined with `--consolidate`, which works on the reports of an existing
296
+ run and writes its consolidated report into that run's directory.)
297
+
298
+ ```bash
299
+ # Uses ./agent-council.yml, else ~/.agent-council.yml
300
+ agent-council my-analysis-prompt.md
301
+ # Uses another config
302
+ agent-council my-analysis-prompt.md other-config.yml
303
+ # Writes reports under ./tmp/council
304
+ agent-council -o tmp/council my-analysis-prompt.md
305
+ # Usage, options, and any AGENT_COUNCIL_OPTS value
306
+ agent-council -h
307
+ # Version
308
+ agent-council -v
309
+ # See Consolidating a finished run
310
+ agent-council --consolidate agent-council-reports/2026-09-03/09-14-02
311
+ ```
312
+
313
+ Relative paths are resolved from the working directory. agent-council
314
+ expands `~` itself only in `--output-dir`; in other paths, the shell
315
+ must expand it.
316
+
317
+ [`examples/`](examples/) holds sample prompts and configs — pure samples, not
318
+ used at runtime. `examples/prompts/` and `examples/configs/` are included in
319
+ the installed gem. The commands below use `$AC_HOME`, the installed gem's
320
+ directory (see [Finding the installed documentation and
321
+ examples](#finding-the-installed-documentation-and-examples)); in a source
322
+ checkout, set `AC_HOME` to the checkout's path and put its `exe/` on
323
+ your `PATH` (see [Development](#development)). They still analyze your
324
+ current directory, and the sample configs name specific providers and
325
+ models — copy and edit one if yours differ.
326
+
327
+ - `examples/prompts/state-of-the-code-base-prompt.md` — a real codebase-analysis
328
+ prompt; with `examples/configs/consolidated.yml` this is the full
329
+ ensemble:
330
+
331
+ ```bash
332
+ agent-council \
333
+ "$AC_HOME/examples/prompts/state-of-the-code-base-prompt.md" \
334
+ "$AC_HOME/examples/configs/consolidated.yml"
335
+ ```
336
+
337
+ - `examples/prompts/smoke-test-prompt.md` +
338
+ `examples/configs/single.yml` — a tiny, cheap prompt (one multiplication
339
+ question — enough to prove a real answer came back) and a one-model
340
+ config, for verifying your setup in seconds (the
341
+ [report-format addendum](#how-it-works-and-what-pi-is-told) may wrap
342
+ the number in a short report):
343
+
344
+ ```bash
345
+ agent-council \
346
+ "$AC_HOME/examples/prompts/smoke-test-prompt.md" \
347
+ "$AC_HOME/examples/configs/single.yml"
348
+ ```
349
+
350
+ - `examples/prompts/project-name-prompt.md` + `examples/configs/consolidated.yml` —
351
+ a small creative-writing prompt useful for spotting prompt-sensitivity
352
+ differences across models:
353
+
354
+ ```bash
355
+ agent-council \
356
+ "$AC_HOME/examples/prompts/project-name-prompt.md" \
357
+ "$AC_HOME/examples/configs/consolidated.yml"
358
+ ```
359
+
360
+ - `examples/prompts/recommend-model-ensemble-prompt.md` — a reusable prompt
361
+ for recommending a provider-diverse model ensemble for a task (agents
362
+ run [`pi-models`](#occasional-utility-pi-models), which is on your
363
+ `PATH` once the gem is installed, and otherwise say they need a model
364
+ list); its sample
365
+ code-review recommendation is in
366
+ [`examples/results/recommend-model-ensemble-code-review.md`](https://github.com/keithrbennett/agent-council/blob/main/examples/results/recommend-model-ensemble-code-review.md)
367
+ (generated output, in the source repository only — not in the gem; it
368
+ shares its title with, but is not, the curated
369
+ [`code-review-agents.md`](code-review-agents.md)).
370
+ - `examples/prompts/review-latest-commit-prompt.md` — a code-review prompt
371
+ for the latest commit (plus any uncommitted changes); run it from the
372
+ repository under review with a multi-model config.
373
+ - `examples/prompts/review-pull-request-prompt.md` — the same review for a
374
+ GitHub pull request, plus its unaddressed review comments and failing
375
+ checks. Check out the PR first (`gh pr checkout <number>`) and run from
376
+ that checkout; agents need an authenticated
377
+ [`gh`](https://cli.github.com/) and read the PR through it.
378
+ - `examples/prompts/review-docs-prompt.md` — a documentation review: checks
379
+ the README, other Markdown files, help output, code comments, and samples
380
+ against the code for accuracy, completeness, consistency, first-run
381
+ usability, and clarity (including duplication);
382
+ run it from the repository whose docs you want reviewed.
383
+ - `examples/prompts/test-coverage-analysis-prompt.md` — a line-coverage
384
+ analysis organized by the logical parts of the system, with gaps ranked by
385
+ risk and a ready-to-use prompt for each recommended fix. It reads
386
+ SimpleCov's `coverage/coverage.json` through
387
+ [cov-loupe](https://github.com/keithrbennett/cov-loupe) and runs no tests,
388
+ so run your full test suite first; it stops with an error if the coverage
389
+ data looks out of date.
390
+
391
+ ### Setting your own defaults with `AGENT_COUNCIL_OPTS`
392
+
393
+ To use the same options on every run, put them in the
394
+ `AGENT_COUNCIL_OPTS` environment variable. agent-council inserts its
395
+ arguments before the command-line ones, so an option given on the command
396
+ line overrides the same option there:
397
+
398
+ ```bash
399
+ export AGENT_COUNCIL_OPTS="--output-dir tmp/council"
400
+ agent-council my-analysis-prompt.md
401
+ # Overrides AGENT_COUNCIL_OPTS
402
+ agent-council --output-dir other-reports my-analysis-prompt.md
403
+ ```
404
+
405
+ Its arguments are treated exactly as if typed first on the command line,
406
+ so set it with care: for example, an `--output-dir` there makes
407
+ `--consolidate` fail, as if both were typed on the command line. The value is split into
408
+ words like a shell would, but agent-council doesn't expand variables in
409
+ it: `~` works, but a literal `$HOME` stays literal. (In a double-quoted
410
+ `export`, your shell has already expanded it.) A malformed value, such as
411
+ one with an unclosed quote, makes every command fail, `-h` and `-v`
412
+ included, with a message naming `AGENT_COUNCIL_OPTS`.
413
+
414
+ ### Stopping a run
415
+
416
+ Ctrl-C (or SIGTERM, SIGHUP, or SIGQUIT) stops the run, keeping reports
417
+ already written:
418
+
419
+ - Running agents, the consolidator included, get SIGTERM (pi then also
420
+ stops the commands it started) and a shared 5 seconds to exit before
421
+ they are killed. A killed agent can't stop the commands it started, so
422
+ those may keep running.
423
+ - A second SIGINT, SIGTERM, or SIGQUIT kills them at once; further SIGHUPs
424
+ are ignored.
425
+ - agent-council then dies by that signal, so a shell loop running it
426
+ stops too; for SIGQUIT it exits with status 131 instead of dumping core.
427
+ - A signal agent-council was started ignoring (e.g. SIGHUP under `nohup`)
428
+ stays ignored, and Ctrl-Z suspends agent-council but not the agents.
429
+ - Under JRuby, SIGQUIT is left to the JVM, which prints a thread dump,
430
+ and doesn't stop the run.
431
+
432
+ [`DESIGN.md`](DESIGN.md) §7 explains why it works this way.
433
+
434
+ ### Consolidating a finished run
435
+
436
+ To consolidate a finished run later — say its consolidation failed, it
437
+ had no consolidator, or you want another model's synthesis — pass its
438
+ run dir:
439
+
440
+ ```bash
441
+ agent-council --consolidate agent-council-reports/2026-09-03/09-14-02
442
+ ```
443
+
444
+ This runs only the consolidator, over the analyst reports in that run's
445
+ `reviews/`, and writes its report (split into findings and meta files,
446
+ as in a normal run) into the same run dir, named as in a normal run, with the run's original `run_stamp` (its date and time, plus
447
+ any suffix). Exit status is 0 only if the consolidation succeeded.
448
+
449
+ - **Run it from the analyzed project's directory**, as for a normal run:
450
+ the consolidator is asked to check the reports' claims against the
451
+ current directory where it can.
452
+ - **The prompt and config are the run's own copies**, and the
453
+ consolidator is the one in the config copy:
454
+ - To try a different model, edit the copy's `consolidator:` (the copy
455
+ then no longer records the run's original consolidator).
456
+ - If the run had no consolidator, add a `consolidator:` entry to the
457
+ copy first; otherwise `--consolidate` exits with an error.
458
+ - Leave the copy's `agents:` exactly as they were (same order,
459
+ providers, and models): an agent counts as having no report when no
460
+ report carries its number, provider, and model.
461
+ - **The consolidation instructions are the current template's** (the
462
+ installed gem's, or a checkout's own), which may have changed since
463
+ the run.
464
+ - **Existing reports are kept.** If a file with the name the new
465
+ report would get, or the name of its `_findings` or `_meta` file, already
466
+ exists (as it does when the same model configuration, meaning harness,
467
+ provider, and model, consolidated the run before), nothing runs; rename
468
+ or delete the file or files the message lists to redo it. Reports
469
+ from different model configurations have different names and sit side
470
+ by side, so you can compare their syntheses.
471
+ - **The run needs at least one analyst report written by
472
+ agent-council**: the prompt and config copies are found through the
473
+ reports' front matter (`prompt_file` and `config_file`, with
474
+ `run_stamp` and `query` naming the consolidated report). A run where
475
+ every analyst failed, or that was interrupted before any report was
476
+ written, can't be consolidated.
477
+ - **Failure reasons aren't saved in the run dir**, so the consolidator
478
+ is told only which configured agents have no report, with a note that
479
+ the reason wasn't recorded.
480
+
481
+ ### Time zone
482
+
483
+ Run dir names, report filenames, and the front matter's `timestamp` and
484
+ `consolidated_at` all use the **local time zone**. There is no
485
+ agent-council option to change it; instead, set the standard `TZ`
486
+ environment variable, which Ruby (like most programs) honors. Timestamps
487
+ always carry their UTC offset (`2026-09-02T16:21:43+00:00` in UTC), so
488
+ they stay unambiguous whatever the zone.
489
+
490
+ | Platform | One run in UTC | Value format |
491
+ |---|---|---|
492
+ | Linux, macOS, WSL (bash, zsh, fish) | `TZ=UTC agent-council prompt.md` | IANA name [[1]][tz-iana] |
493
+ | Windows PowerShell | `$env:TZ = 'UTC0'; agent-council prompt.md` | POSIX-style [[2]][tz-gnu] [[3]][tz-posix-db] [[4]][tz-ms] |
494
+ | Windows cmd | `set "TZ=UTC0" && agent-council prompt.md` | POSIX-style [[2]][tz-gnu] [[3]][tz-posix-db] [[4]][tz-ms] |
495
+
496
+ On Windows, `TZ` set this way lasts for the rest of the shell session.
497
+ Git Bash on Windows accepts the Linux syntax but runs Windows Ruby, so it
498
+ takes POSIX-style values.
499
+
500
+ **Finding the value to use:**
501
+
502
+ - **IANA names** (Linux, macOS, WSL): `Area/City`, such as
503
+ `America/Chicago` or `Europe/Berlin`, plus `UTC`. List them with
504
+ `ls /usr/share/zoneinfo`, or see the list on Wikipedia [[1]][tz-iana].
505
+ A misspelled name isn't reported: it silently falls back to UTC, so
506
+ check the timestamp in a report's front matter.
507
+ - **POSIX-style values** (native Windows): a zone abbreviation, its
508
+ hours **west** of UTC (so the sign is the reverse of the usual
509
+ offset), and an optional daylight saving abbreviation: `UTC0`,
510
+ `EST5EDT` (US Eastern), `PST8PDT` (US Pacific), `CET-1CEST`
511
+ (Central Europe). The GNU C Library manual explains the format with
512
+ examples [[2]][tz-gnu]; to find your zone's value, look up its IANA
513
+ name in the `posix_tz_db` table [[3]][tz-posix-db] and use the part
514
+ before the first comma (`America/Chicago` gives
515
+ `CST6CDT,M3.2.0,M11.1.0`, so use `CST6CDT`), since Windows doesn't
516
+ support the daylight saving rules after it and always follows US
517
+ rules instead [[4]][tz-ms]. These values also work on Linux and
518
+ macOS.
519
+
520
+ References:
521
+
522
+ 1. [List of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) (Wikipedia)
523
+ 2. [The TZ Variable](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html) (GNU C Library manual)
524
+ 3. `posix_tz_db`: [IANA names mapped to POSIX TZ strings](https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv)
525
+ 4. `_tzset`: [Microsoft C runtime documentation](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/tzset)
526
+
527
+ [tz-iana]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
528
+ [tz-gnu]: https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
529
+ [tz-posix-db]: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
530
+ [tz-ms]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/tzset
531
+
532
+ To always use a zone, set `TZ` in your shell profile (e.g.
533
+ `export TZ=UTC`), keeping in mind that this affects every program
534
+ started from that shell. The agents inherit `TZ` either way, so it also
535
+ changes the time they see.
536
+
537
+ ## What you get
538
+
539
+ Each run creates one run directory, holding its inputs and reports,
540
+ day-bucketed
541
+ (`--consolidate` instead writes into the existing run directory it is
542
+ given):
543
+
544
+ ```text
545
+ agent-council-reports/
546
+ 2026-09-02/
547
+ 16-21-43/
548
+ codebase-review.md # prompt copy
549
+ analyst_format.md # report-format addendum copy
550
+ consolidated.yml # config copy
551
+ run.json # every agent run's outcome, duration, and token usage
552
+ reviews/ # analyst reports
553
+ 2026-09-02-16-21-43_codebase-review_01_pi_opencode-go_glm-5.3.md
554
+ 2026-09-02-16-21-43_codebase-review_02_pi_opencode-go_kimi-k3.md
555
+ 2026-09-02-16-21-43_codebase-review_03_pi_openai_gpt-6.1-sol.md
556
+ 2026-09-02-16-21-43_codebase-review_04_pi_cursor_claude-sonnet-5-5-high.md
557
+ # consolidated report, if a consolidator is configured, normally in two
558
+ # files: the findings, ready to paste into a chat, and the run's
559
+ # metadata (a report that can't be split is one file, without the
560
+ # _findings or _meta suffix)
561
+ 2026-09-02-16-21-43_codebase-review_consolidated_pi_opencode-go_glm-5.3_findings.md
562
+ 2026-09-02-16-21-43_codebase-review_consolidated_pi_opencode-go_glm-5.3_meta.md
563
+ 18-05-11/
564
+ ...
565
+ 2026-09-03/
566
+ 09-14-02/
567
+ ...
568
+ ```
569
+
570
+ - **Run dir name**: `hh-mm-ss` inside a `yyyy-mm-dd` day bucket (local
571
+ time, second resolution). Pure time, nothing else — except that if that
572
+ name is already taken (two runs started in the same second, or local
573
+ time repeating when clocks fall back or are set back), the new run gets
574
+ `hh-mm-ss-2`, `-3`, ... instead, so each run has its own directory.
575
+ - **Time zone**: local by default; see [Time zone](#time-zone) to use
576
+ UTC or another zone.
577
+ - **Report filenames** carry everything else:
578
+ `<yyyy-mm-dd>-<hh-mm-ss>_<query-slug>_<agent-number>_<harness>_<provider>_<model>.md`,
579
+ so a report stays self-describing when you copy or attach it outside the
580
+ tree (a consolidated report has `consolidated` in place of
581
+ `<agent-number>`, and, if the report was split, ends in `_findings` or `_meta` before `.md`); `<hh-mm-ss>` includes the run dir's suffix, if any,
582
+ and `<provider>` and `<model>` are sanitized like the query slug below,
583
+ but keep their case. `<harness>` is currently always `pi`, the only
584
+ harness agent-council supports so far.
585
+ `<agent-number>` is the agent's 1-based position in the config's
586
+ `agents` list (`01`, `02`, ...), so every report has a unique name even
587
+ if the same provider/model is configured more than once. The query slug
588
+ is the prompt file name without its extension, minus any trailing
589
+ `-prompt` or `_prompt` in any case (`codebase-review-prompt.md` and
590
+ `codebase-review_prompt.md` both become `codebase-review`), lowercased, with each run of characters
591
+ other than ASCII letters, digits, `.`, `_`, and `-` replaced by `-` and
592
+ leading or trailing `-` and `.` dropped (`My Prompt (draft).md` becomes
593
+ `my-prompt-draft`); a name left empty becomes `prompt`.
594
+ - **Day buckets** keep the reports directory itself navigable when you have hundreds
595
+ of runs.
596
+ - Each report file, except a consolidated report's `_findings.md`, begins with a small **YAML front matter** block,
597
+ written by the orchestrator (never by the model), identifying the
598
+ report (what was measured about its run is in `run.json`, below):
599
+ - in every report: `role` (`analyst` or `consolidator`), `timestamp`
600
+ (unquoted ISO 8601), `query` (the query slug), `harness`, `provider`, `model`,
601
+ `prompt_file` and `config_file` (the names of
602
+ the run dir's input copies), `config_source` (where the config was
603
+ found, with `~` for the home directory; informational only) and
604
+ `run_stamp` (the report filenames' date-time prefix, with the run
605
+ dir's suffix, if any);
606
+ - in analyst reports: `agent_index` (the agent number, as an integer);
607
+ - in consolidated reports: `sources` (the analyst labels that were
608
+ synthesized) and `consolidated_at` (when the
609
+ consolidation ran; with `--consolidate`, possibly long after
610
+ `timestamp`, the run's start).
611
+
612
+ - The run dir's **`run.json`** records every agent run, for tools and
613
+ for analysis across many runs: `analysts` and `consolidations`, one
614
+ entry per run with its `agent_index` (analysts only), `harness`,
615
+ `provider`, `model`, `ok`, `duration_seconds`, `usage`, and `report` (its
616
+ path in the run dir; for a split consolidation, the `_findings.md` file)
617
+ or `error`; a split consolidation also has `meta_report`, the `_meta.md`
618
+ file, which has the front matter; `total_usage`, the
619
+ entries' summed usage, whose `cost` is included only if every summed
620
+ entry's cost is known; and `total_usage_runs`, how many entries reported
621
+ usage (and so are in the total). Failed runs are included, since their
622
+ tokens were spent.
623
+ It is written once the reviews finish and updated after each
624
+ consolidation, including each `--consolidate`; a `--consolidate` of a
625
+ run dir without a readable `run.json` warns and writes none.
626
+
627
+ `usage` is the model's token usage, summed over every request of the
628
+ run, as reported by the harness: `input` (excluding cached input),
629
+ `cache_read`, `cache_write`, and `output` token counts, plus `cost` in
630
+ US dollars when the harness could price the run (pi uses its own price
631
+ table, so models missing from it have no cost). It is `null` when the
632
+ provider reports no usage. Providers count tokens somewhat differently
633
+ (reasoning tokens, for example), so compare counts across providers
634
+ only roughly.
635
+
636
+ `--consolidate` relies on the analysts' `prompt_file`, `config_file`,
637
+ `run_stamp`, and `query`, and on `agent_index` to tell which configured
638
+ agents have no report. Reports are identified by their front matter,
639
+ not by filename parsing, so `.md` files in `reviews/` without
640
+ `provider` and `model` front matter are ignored.
641
+ - Below the front matter, the orchestrator adds a metadata table
642
+ summarizing its main fields and its run's duration and token usage
643
+ (for consolidated reports, under a `# Consolidated Report` title and a
644
+ `### Prompt:` line, and followed by a `### Sources` table giving each
645
+ source's duration, input (cached included), cached input, output, and
646
+ cost, from `run.json`), followed by the
647
+ model's final message, minus any top-level `#` heading the message
648
+ starts with. The consolidator is asked to put a line containing only `=== META ===`
649
+ between its findings and its `Agent Consultants` and `Agent performance`
650
+ sections, and the report is split there: the text after the marker stays
651
+ with this header and front matter in the `_meta.md` file, and the text
652
+ before it goes to the `_findings.md` file under a `# Consolidated Findings`
653
+ title, a few lines identifying the run (its stamp and query, the prompt
654
+ file, and the consolidator and time), and a legend of the agents (number,
655
+ harness, provider, model). If
656
+ the marker is missing, repeated, inside a code fence, or not followed by
657
+ the `Agent Consultants` heading, or the report no longer starts with the
658
+ header written to it, the report is left unsplit, under the unsuffixed
659
+ name, and a warning says why.
660
+ - The copied prompt, config, and report-format addendum keep an analyst
661
+ run's inputs recoverable after the fact. The pi and agent-council
662
+ versions, the scaffolding text appended to pi's system prompt, and the
663
+ consolidation template are not recorded.
664
+ - The prompt and config copies keep their own file names (e.g.
665
+ `single.yml`), so the prompt, the config, and `analyst_format.md` must
666
+ have different file names, and none can be named `reviews`; a run that
667
+ would break either rule stops before starting.
668
+
669
+ Each run starts by printing its config, prompt, and run directory
670
+ (`Run Dir: agent-council-reports/2026-09-03/09-14-02`), then its agents
671
+ under `Review Agents (N):` and, if configured, `Consolidator Agent:`;
672
+ these labels also name the harness (e.g. `#2 pi / openai / gpt-6.1-sol`).
673
+ You can open the run dir while agents are still working, or after a
674
+ Ctrl-C (see [Stopping a run](#stopping-a-run)). Other access patterns:
675
+
676
+ ```bash
677
+ # Last run by name: usually the newest
678
+ ls -d agent-council-reports/*/* | tail -1
679
+ # Today's runs
680
+ ls agent-council-reports/$(date +%F)/
681
+ # Every analyst report for one question, all runs
682
+ ls agent-council-reports/*/*/reviews/*codebase-review*
683
+ ```
684
+
685
+ While the agents run, a `Review Progress:` section prints one line per
686
+ agent as it finishes: `✓` or `✗`, its duration, its label (e.g.
687
+ `#2 openai / gpt-6.1-sol`) and, for a failure, the reason on the line
688
+ below. When all reviews finish, a `Reviews` table shows which models
689
+ succeeded, how long each took (`HH:MM:SS`) and their token usage in
690
+ `In`, `Cached`, `Out`, and `Cost` columns (`In` counts cached input too;
691
+ `-` when unknown; failed runs show what they used), with the failures'
692
+ reasons listed below it under `Errors:`. A
693
+ `--- Reviews complete (N/M succeeded; <total usage>) ---` line follows,
694
+ e.g. `2.1M in (2.0M cached) / 32.7k out, $0.18`. The total's cost is
695
+ shown only when every counted analyst's cost is known, and the total
696
+ notes `(from K of M agents)` when some reported no usage (as some
697
+ providers don't). With a consolidator,
698
+ the `Consolidation Report (in <run dir>):` heading (with
699
+ `--consolidate`, `(in <run dir>, from N review(s)):`) then prints while it
700
+ runs, its result row when it finishes, and finally `--- Consolidation
701
+ complete ---` (or `--- Consolidation error ---`). One model's failure
702
+ does not affect the others. Exit status is 0 only if every analyst
703
+ agent succeeded; the consolidator's result doesn't affect it.
704
+
705
+ Output paths are printed relative to the current working directory (e.g.
706
+ `agent-council-reports/2026-09-04/21-39-01/reviews`), not to where
707
+ agent-council is installed. Paths outside the working directory (such
708
+ as an `--output-dir` elsewhere) are printed absolute, as is a default
709
+ config's (`Config: [default] /path/to/agent-council.yml`), to show
710
+ which default was picked up.
711
+
712
+ ## How it works (and what pi is told)
713
+
714
+ - Every analyst agent receives the **same bytes**: the prompt file's
715
+ contents (with one trailing newline removed), a blank line, and the
716
+ report-format addendum
717
+ (`lib/agent_council/templates/analyst_format.md`, copied into each run
718
+ dir), piped to pi on stdin as the prompt. No roles, no per-model customization. The addendum asks for a
719
+ Markdown report with `##` section headings, which can conflict with a
720
+ prompt that asks for some other output form (the smoke-test prompt's
721
+ "Reply with just the number, then one sentence explaining how you got
722
+ it.", for example); models usually reconcile the two. (The
723
+ consolidator gets its own prompt; see
724
+ [Discovering and configuring models](#discovering-and-configuring-models).)
725
+ - Each agent runs as
726
+ `pi -p --mode json --no-session --tools read,grep,find,ls,bash
727
+ --provider P --model M --append-system-prompt <scaffolding>`.
728
+ - `--no-session` keeps runs independent: no conversation state leaks
729
+ between ensemble members.
730
+ - `--tools` is an allowlist for pi's built-in, extension, and custom
731
+ tools: `bash` remains for investigation (`git log`, dependency
732
+ inspection, ...), while file-editing tools and unlisted extension and
733
+ custom tools are unavailable, as are MCP tools. The allowlist is the
734
+ only enforced restriction (see [Safety and privacy](#safety-and-privacy)). A fixed
735
+ scaffolding text appended to the system prompt (identical for all
736
+ agents, the consolidator included, except the scratch path) tells agents
737
+ not to modify project files, git state, software, or configuration, or
738
+ start persistent services (they get a scratch directory for temporary
739
+ files), and
740
+ not to run tests, builds, or similar commands, which can collide between
741
+ concurrent analysts, unless the prompt permits them (as
742
+ `examples/prompts/review-latest-commit-prompt.md` does for tests and
743
+ linters); where such a command isn't permitted but would help, the
744
+ agent describes what it would run instead. The full text is
745
+ `scaffold_text` in `lib/agent_council/harnesses/pi_harness.rb`;
746
+ [`DESIGN.md`](DESIGN.md) §10 explains the rules.
747
+ - Each agent gets its own disposable temp directory (exposed via `TMPDIR`
748
+ and named in the scaffolding), deleted after the run. After Ctrl-C, a
749
+ directory can be left behind if a second signal stops agent-council at
750
+ once, or if cleanup after a killed agent doesn't finish in time.
751
+ - Otherwise pi behaves normally: it loads context files such as
752
+ `AGENTS.md` and `CLAUDE.md` as in any pi run, and the analyzed repo's
753
+ `.pi` project resources only if pi already trusts the project
754
+ (agent-council passes no `--approve`). See
755
+ [pi's documentation][pi-docs] for what loads from where.
756
+ - The final response is read from pi's JSONL event stream. pi exits 0
757
+ even when the provider rejects the request (e.g. Copilot's
758
+ `model_not_available_for_integrator`) or the output is cut off;
759
+ agent-council detects these in the event stream and reports them as
760
+ failures, not reports. [`DESIGN.md`](DESIGN.md) §13 has the details.
761
+
762
+ ## Safety and privacy
763
+
764
+ agent-council does not sandbox the agents. Before running it, know that:
765
+
766
+ - **Agents have a shell.** Each agent, the consolidator included, gets pi's
767
+ `bash` tool, running as you, in the directory you run from, with your
768
+ environment (including any API keys in it; agent-council only sets
769
+ `TMPDIR`), your file permissions, and your network access. Of pi's
770
+ built-in tools, only `edit` and `write` (and the Windows-only
771
+ `powershell`) are withheld. The read-only rules described above are
772
+ instructions in the system prompt, which a model can ignore or misapply.
773
+ - **MCP servers still start.** agent-council doesn't pass `--no-mcp`,
774
+ so pi starts or connects the MCP servers you have configured for every
775
+ agent, as you. Their tools can't be called (tested with pi 1.4.0):
776
+ the `--tools` allowlist names none of them, nor pi's `codemode` and
777
+ `tool_search` tools, which reach them indirectly. A server that does
778
+ something merely by starting still does it.
779
+ - **The analyzed repository can steer the agents.** Agents read the
780
+ repository's files, and pi loads its context files (such as
781
+ `AGENTS.md`) and, if the project is trusted, its `.pi` resources, so
782
+ text in the repository can try to direct what the agents run (prompt
783
+ injection). Prompts that permit tests,
784
+ such as `examples/prompts/review-latest-commit-prompt.md`, run the
785
+ project's own code.
786
+ - **Your code goes to every configured provider.** Whatever an agent reads,
787
+ and the output of whatever it runs, is sent to that agent's provider. The
788
+ consolidator's provider receives the prompt and all reports, plus
789
+ anything the consolidator itself reads or runs. Check those providers'
790
+ data policies before analyzing private code.
791
+ - **Reports stay in the analyzed project by default.** They are written
792
+ under `agent-council-reports/` in the working directory, unless you give
793
+ an `--output-dir`; add that directory to the project's `.gitignore` if
794
+ you don't want them committed.
795
+
796
+ Run agent-council only on repositories you trust, or inside a container, VM,
797
+ or disposable checkout that holds no credentials you aren't willing to
798
+ expose.
799
+
800
+ ## Limitations (deliberate)
801
+
802
+ No retries or timeouts of agent-council's own (pi itself may retry
803
+ transient provider failures), no ranking
804
+ or scoring of reports
805
+ (beyond the optional consolidator's synthesis), no manifests or indexes —
806
+ the directory layout is the index. See [`DESIGN.md`](DESIGN.md) §18–19.
807
+
808
+ ## Stability
809
+
810
+ This is a 0.x release. The config format, command-line interface, run
811
+ directory layout, and report front matter may change incompatibly before 1.0.
812
+
813
+ ## Development
814
+
815
+ In a git checkout (the gemspec lists files with `git ls-files`),
816
+ `./exe/agent-council` and `./exe/pi-models` are equivalent to the installed `agent-council` and
817
+ `pi-models`. Agents analyze the working directory, so running
818
+ `./exe/agent-council` from the checkout analyzes agent-council itself; to
819
+ analyze another project, `cd` there and run the checkout's executable by
820
+ its full path. Sample prompts such as `recommend-model-ensemble-prompt.md`
821
+ expect `pi-models` on your `PATH`; to use them from a checkout, add its
822
+ `exe/` directory (`export PATH="/path/to/agent-council/exe:$PATH"`).
823
+
824
+ Setup, tests, lint, the pre-commit hook, and the security audit are
825
+ described in [`CONTRIBUTING.md`](https://github.com/keithrbennett/agent-council/blob/main/CONTRIBUTING.md)
826
+ in the source repository. Before submitting a pull request, open an
827
+ issue and wait for explicit approval; `CONTRIBUTING.md` describes the
828
+ process.
829
+
830
+ ## Occasional utility: `pi-models`
831
+
832
+ To choose models yourself, use `pi --list-models` (see
833
+ [Discovering and configuring models](#discovering-and-configuring-models)).
834
+ The gem also includes `pi-models`, which lists the same kind of
835
+ provider/model pairs in forms meant for scripts and agents; the sample
836
+ `recommend-model-ensemble-prompt.md` has its agents run it. They differ in
837
+ two ways:
838
+
839
+ - **Output.** `pi --list-models` prints a table for people. `pi-models` prints a table, JSON with pi's model fields
840
+ (`--json`, for `jq`), or one `provider/id` per line (`--ids`, for shell
841
+ loops), and exits non-zero with a clear message if pi is missing or
842
+ fails. `pi-models -h` lists the options and the JSON fields. In a config
843
+ entry, its `provider` is `provider` and its `id` is `model`.
844
+ - **Source.** `pi-models` asks pi over its RPC interface instead of running
845
+ `pi --list-models`, so its list can include models that
846
+ `pi --list-models` omits (e.g. extra `github-copilot` models), and those
847
+ may not be usable on your account.
848
+
849
+ `pi-models` requires `pi` and is on your `PATH` once the gem is installed;
850
+ like agent-council, it needs WSL on Windows.
851
+
852
+ [pi-docs]: https://github.com/earendil-works/pi/tree/main/packages/coding-agent/docs