@bramburn/pi-model-council 1.6.2 → 1.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +342 -0
- package/CODE_OF_CONDUCT.md +134 -0
- package/CONTRIBUTING.md +128 -0
- package/DISCLAIMER.md +62 -0
- package/LICENSE +21 -0
- package/README.md +460 -0
- package/SECURITY.md +80 -0
- package/SUPPORT.md +72 -0
- package/commandParser.ts +333 -0
- package/councilRunner.ts +499 -0
- package/index.ts +304 -0
- package/markdown.ts +113 -0
- package/openrouterClient.ts +244 -0
- package/package.json +76 -1
- package/prompts.ts +299 -0
- package/retry.ts +92 -0
- package/runnerHelpers.ts +130 -0
- package/schemas.ts +44 -0
- package/searchSelector.ts +313 -0
- package/secondOpinionRunner.ts +203 -0
- package/settings-ui.ts +488 -0
- package/settings.ts +135 -0
- package/structuredOutput.ts +500 -0
- package/types.ts +169 -0
- package/index.js +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.6.0] - 2026-06-26
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Extracted shared runner helpers into `runnerHelpers.ts`.** The two
|
|
12
|
+
runner files (`councilRunner.ts` and `secondOpinionRunner.ts`) had
|
|
13
|
+
near-identical logic for:
|
|
14
|
+
- 3-source API key resolution (settings → registry → env)
|
|
15
|
+
- parse-then-repair-then-fallback pipeline for model responses
|
|
16
|
+
- "call model + timeout + wrap errors with model name" wrapper
|
|
17
|
+
|
|
18
|
+
Per Sandi Metz ("duplication is better than the wrong abstraction"),
|
|
19
|
+
the two runners share these **same-concept** cross-cutting concerns
|
|
20
|
+
while keeping the **different concepts** (fan-out orchestration,
|
|
21
|
+
synthesis step, fallback decision) in their respective files.
|
|
22
|
+
- `resolveOpenRouterApiKey(settings, modelRegistry?)` — shared
|
|
23
|
+
- `parseModelOpinionResponse(rawText)` — shared
|
|
24
|
+
- `callModelWithTimeout(args)` — shared
|
|
25
|
+
- Per-model fan-out, blind-label translation, synthesis prompt
|
|
26
|
+
building → still in `councilRunner.ts`
|
|
27
|
+
|
|
28
|
+
### Improved (secondOpinionRunner)
|
|
29
|
+
- **Now uses structured output + retry**, matching the council runner.
|
|
30
|
+
Previously `/opinion` was the only path that didn't try JSON schema
|
|
31
|
+
first and retry with structured-output-fallback on the `isStructuredOutputError`
|
|
32
|
+
heuristic. Now both runners follow the same "try JSON schema → retry
|
|
33
|
+
without schema on 4xx/structured-output-unsupported" pattern, so
|
|
34
|
+
opinion responses are more reliable on models that have stricter
|
|
35
|
+
schema requirements.
|
|
36
|
+
- **Pre-flight status updates** during API key resolution, mirroring
|
|
37
|
+
`/council`'s per-step progress. Previously the only status was
|
|
38
|
+
"Second opinion: querying model..." — now also "resolving API key..."
|
|
39
|
+
and "rendering markdown..." before and after the call.
|
|
40
|
+
- **Actionable error format** (`Fix: \`/command\``) for setup errors,
|
|
41
|
+
matching `/council` (v1.2.0). Previously said "Run /opinion-settings";
|
|
42
|
+
now says "Fix: \`/opinion-settings\`".
|
|
43
|
+
|
|
44
|
+
### Improved (councilRunner)
|
|
45
|
+
- **Pre-flight status**: added "Council: resolving API key..." before
|
|
46
|
+
the existing "validating API key..." step so the user sees activity
|
|
47
|
+
during the registry/env lookup.
|
|
48
|
+
|
|
49
|
+
### Tests
|
|
50
|
+
- 12 new tests for `runnerHelpers.ts` covering: API key resolution
|
|
51
|
+
from each of the 3 sources, whitespace trimming, registry-throw
|
|
52
|
+
fallback, env fallback, well-formed JSON parsing, markdown fence
|
|
53
|
+
stripping, fallback shape, partial-JSON repair, success and
|
|
54
|
+
failure paths of `callModelWithTimeout`.
|
|
55
|
+
- Total now **111 passing**.
|
|
56
|
+
|
|
57
|
+
## [1.5.1] - 2026-06-26
|
|
58
|
+
|
|
59
|
+
### Documentation
|
|
60
|
+
- **Added a comprehensive Installation section to the README.** Previously
|
|
61
|
+
the README jumped straight from "Features" to "Workflow" with no
|
|
62
|
+
explanation of how to actually install the extension. The new section
|
|
63
|
+
covers:
|
|
64
|
+
|
|
65
|
+
- Prerequisites (Pi version, Node.js 22+, OpenRouter key)
|
|
66
|
+
- A comparison table of all 5 install methods (npm, SSH, HTTPS+PAT,
|
|
67
|
+
local clone, one-shot try) with the destination path each lands in
|
|
68
|
+
- A "pin to a tag" recommendation for reproducibility in shared/CI use
|
|
69
|
+
- Step-by-step walkthroughs for each method, including SSH-vs-HTTPS
|
|
70
|
+
tradeoffs and the fine-grained PAT scope needed for HTTPS
|
|
71
|
+
- The `pi -l` flag for project-scoped (team-shareable) installs
|
|
72
|
+
- `pi list` / `pi config` verification commands
|
|
73
|
+
- `pi update --extensions` and overwrite-to-new-tag for updates
|
|
74
|
+
- `pi remove pi-model-council` for uninstall
|
|
75
|
+
- Where each install lands in the filesystem
|
|
76
|
+
|
|
77
|
+
No code changes — docs only.
|
|
78
|
+
|
|
79
|
+
## [1.5.0] - 2026-06-26
|
|
80
|
+
|
|
81
|
+
### Security
|
|
82
|
+
- **`commandParser.ts` rewritten as a hardened slash-command parser.**
|
|
83
|
+
Slash-command arguments flow directly into LLM prompts, so the parser
|
|
84
|
+
is both a UX surface and a security surface. Hardening (research-
|
|
85
|
+
grounded by the OWASP GenAI guidance and Trail of Bits' agent
|
|
86
|
+
injection writeups):
|
|
87
|
+
|
|
88
|
+
- **Unicode NFC normalization** of the full input before any
|
|
89
|
+
comparison or tokenisation, so homoglyphs (e.g. Cyrillic 'а'
|
|
90
|
+
vs Latin 'a') can't bypass the mode allow-list.
|
|
91
|
+
- **Control-character stripping** (null bytes, terminal escape
|
|
92
|
+
sequences, ASCII 0x00–0x1F except newline/tab). Raw control
|
|
93
|
+
bytes pasted into a prompt can break terminal rendering, smuggle
|
|
94
|
+
ANSI escapes, or confuse downstream JSON parsers.
|
|
95
|
+
- **Input length limits** enforced at multiple layers:
|
|
96
|
+
- Max 8000 chars on the problem
|
|
97
|
+
- Max 2000 chars on `--understanding`
|
|
98
|
+
- Max 1000 chars per `--constraint` / `--question`
|
|
99
|
+
- Max 4000 chars per token (early rejection in the tokenizer)
|
|
100
|
+
- Max 200 tokens total per command
|
|
101
|
+
- Max 20 constraints and 20 questions
|
|
102
|
+
- **Error messages on unterminated quotes** instead of silently
|
|
103
|
+
truncating the user's prompt at the next whitespace.
|
|
104
|
+
- **Escaped quotes inside strings** — `"He said \"hello\""` is now
|
|
105
|
+
parsed correctly as `He said "hello"`.
|
|
106
|
+
- **Duplicate `--understanding` detection** (rejected).
|
|
107
|
+
- **Clearer error messages** that always include the full usage
|
|
108
|
+
line so the user can recover without grepping docs.
|
|
109
|
+
|
|
110
|
+
### Changed
|
|
111
|
+
- **DRY'd the two slash-command parsers into one shared implementation.**
|
|
112
|
+
`parseCouncilCommandArgs` and `parseSecondOpinionCommandArgs` were
|
|
113
|
+
~95% duplicate before. They now share a single `parseCommon()`
|
|
114
|
+
function parameterised by mode allow-list, default mode, question
|
|
115
|
+
field name, and usage string. The single source of truth makes it
|
|
116
|
+
much harder to drift the two parsers out of sync on a future
|
|
117
|
+
change.
|
|
118
|
+
|
|
119
|
+
### Tests
|
|
120
|
+
- 22 new tests for the command parser covering: quoted/unquoted
|
|
121
|
+
problems, mode aliases (`arch` → `architecture`), short flag aliases
|
|
122
|
+
(`-c`/`-q`/`-u`), escaped quotes, unterminated quotes, missing flag
|
|
123
|
+
values, unknown flags, control-char stripping, oversized problem,
|
|
124
|
+
oversized token, duplicate `--understanding`, constraint/question
|
|
125
|
+
count limits, mode token recognition for opinion vs council.
|
|
126
|
+
- Total now **99 passing**.
|
|
127
|
+
|
|
128
|
+
## [1.4.0] - 2026-06-26
|
|
129
|
+
|
|
130
|
+
### Removed
|
|
131
|
+
- **Qdrant persistence layer.** The `qdrantClient.ts` and
|
|
132
|
+
`persistence.ts` files (~290 lines of code) have been removed
|
|
133
|
+
along with the `CouncilDecision.metadata.persisted` and
|
|
134
|
+
`persistenceError` fields. Removed the `COUNCIL_PERSISTENCE_ENABLED`,
|
|
135
|
+
`QDRANT_URL`, `QDRANT_API_KEY`, and `QDRANT_COUNCIL_COLLECTION`
|
|
136
|
+
environment variable lookups.
|
|
137
|
+
|
|
138
|
+
### Why
|
|
139
|
+
The original v0.1.0 intent was to use Qdrant as a vector store for
|
|
140
|
+
semantic retrieval over past council decisions. The implementation
|
|
141
|
+
never delivered on that promise:
|
|
142
|
+
|
|
143
|
+
- `createDecisionVector` produced a placeholder 8-dim vector of
|
|
144
|
+
mostly constants (one-hot mode, one-hot confidence, etc.) instead
|
|
145
|
+
of a real semantic embedding.
|
|
146
|
+
- No retrieval path existed — nothing ever read from Qdrant to
|
|
147
|
+
influence future council runs.
|
|
148
|
+
- The feature was opt-in via an undocumented env var, off by default,
|
|
149
|
+
and not described in the README setup steps.
|
|
150
|
+
|
|
151
|
+
Per the research-grounded review for v1.4.0, this was dead code
|
|
152
|
+
that added attack surface (a network client) and maintenance burden
|
|
153
|
+
without delivering any user-visible value. Removal simplifies the
|
|
154
|
+
codebase, reduces dependency footprint, and eliminates the
|
|
155
|
+
misleading "Qdrant persistence (optional)" mention in the docs.
|
|
156
|
+
|
|
157
|
+
If semantic retrieval over past decisions becomes a real feature
|
|
158
|
+
request in the future, it should be built as a separate extension
|
|
159
|
+
(`pi-model-council-memory` or similar) with proper embeddings and a
|
|
160
|
+
documented setup flow.
|
|
161
|
+
|
|
162
|
+
### Migration
|
|
163
|
+
If you somehow had `COUNCIL_PERSISTENCE_ENABLED=true` set, just
|
|
164
|
+
unset it. Nothing else changes.
|
|
165
|
+
|
|
166
|
+
## [1.3.0] - 2026-06-26
|
|
167
|
+
|
|
168
|
+
### Changed
|
|
169
|
+
- **Synthesis prompt now uses blind labels (Opinion A/B/C) for council
|
|
170
|
+
members.** Previously the chairman saw model names directly, which
|
|
171
|
+
research shows causes "anchor and prestige bias" — the chairman tends
|
|
172
|
+
to over-weight opinions from the most-recognised model regardless of
|
|
173
|
+
the actual content. The new flow:
|
|
174
|
+
1. Council members are presented anonymously as Opinion A, B, C...
|
|
175
|
+
2. The chairman synthesises one decision using only the blind labels
|
|
176
|
+
3. After the synthesis, the runner resolves each blind label back to
|
|
177
|
+
the actual model id so the final report's `modelNotes` show real
|
|
178
|
+
model names
|
|
179
|
+
|
|
180
|
+
This is the same pattern recommended for LLM-judge ensembles in
|
|
181
|
+
evaluation research and matches what tools like `llm-council` and
|
|
182
|
+
academic ensemble systems use.
|
|
183
|
+
- **Synthesis system prompt rewritten with explicit decision rules**
|
|
184
|
+
(research-grounded):
|
|
185
|
+
- "Compare options on evidence, not on which model said them"
|
|
186
|
+
- "Do NOT blend incompatible views into a mushy compromise"
|
|
187
|
+
- "Do NOT copy any single opinion verbatim"
|
|
188
|
+
- "When confidence is mixed, lower the overall confidence"
|
|
189
|
+
Each rule directly counters a known failure mode from the literature
|
|
190
|
+
(winner-take-all, averaging, hidden disagreement).
|
|
191
|
+
- **Per-model progress indicator during fan-out.** Previously the
|
|
192
|
+
footer showed a single static "Council: querying models..." line.
|
|
193
|
+
Now it updates to e.g. `Council: 2/3 models responded (waiting on
|
|
194
|
+
qwen/qwen3.7-max)` so the user sees real activity.
|
|
195
|
+
|
|
196
|
+
### Fixed
|
|
197
|
+
- **`extractJsonObject` is now robust to common LLM JSON mistakes.**
|
|
198
|
+
Previously the parser used a naive "first { to last }" substring
|
|
199
|
+
extraction, which could:
|
|
200
|
+
- Mistake a `}` inside a string literal for the closing brace
|
|
201
|
+
- Fail when JSON was truncated mid-stream (e.g. `max_tokens` hit
|
|
202
|
+
before the close brace)
|
|
203
|
+
- Reject otherwise-valid JSON containing Python literals like
|
|
204
|
+
`True` / `False` / `None` or trailing commas before `}` / `]`
|
|
205
|
+
The new implementation:
|
|
206
|
+
- Walks brace-balance forward from the first `{`, ignoring braces
|
|
207
|
+
inside string literals and respecting escaped quotes
|
|
208
|
+
- Falls back to a minimal repair pass for `True`/`False`/`None` and
|
|
209
|
+
trailing commas before parsing
|
|
210
|
+
- Returns a clearer error message including the substring length
|
|
211
|
+
when all repair attempts fail
|
|
212
|
+
|
|
213
|
+
### Tests
|
|
214
|
+
- 9 new tests for `extractJsonObject` (brace balance, escaped quotes,
|
|
215
|
+
Python literal repair, trailing comma repair, truncated JSON).
|
|
216
|
+
- 1 new test for the blind-label transformation in `buildSynthesisPrompts`.
|
|
217
|
+
- Total now **77 passing**.
|
|
218
|
+
|
|
219
|
+
## [1.2.0] - 2026-06-26
|
|
220
|
+
|
|
221
|
+
### Fixed
|
|
222
|
+
- **`saveLatestCouncilReport` / `saveLatestSecondOpinion` now use `ctx.cwd` instead of `process.cwd()`.** Previously, when the extension was launched from a directory other than the project root (e.g. via `cd /elsewhere && pi`), the report was written under `/elsewhere/.pi/council/` instead of `<project>/.pi/council/`. Now it always lands in the project Pi is running in.
|
|
223
|
+
- **`council_decide` tool now also saves the report to disk.** Previously only the `/council` slash command did — when the LLM invoked the tool directly (which is the primary entry point from the agent's perspective), no `.pi/council/last-decision.md` artifact was produced. The behaviour is now consistent regardless of which entry point is used.
|
|
224
|
+
- **Status-key collision between `/council` and `/opinion`.** Both commands previously used the same `setStatus("model-council", ...)` key, so a long-running council and a quick opinion would clobber each other's footer message when invoked concurrently. Split into `"model-council:run"` and `"model-council:opinion"`.
|
|
225
|
+
- **`/council-settings reset` now only resets council settings**, not the opinion model too. The command name implied a narrower scope. The confirm prompt was updated to reflect what actually gets cleared.
|
|
226
|
+
- **`/opinion-settings reset` no longer hardcodes the default model.** It now sources the default from `createDefaultSettings()` so the command stays in lock-step with the rest of the codebase (no drift if the default model ever changes).
|
|
227
|
+
|
|
228
|
+
### Added
|
|
229
|
+
- **Pre-flight status updates.** `runCouncil` now calls `onStatus` during pre-flight so the user sees `Council: validating API key...` and `Council: verifying configured models are available...` instead of an unresponsive spinner during the OpenRouter ping + model fetch.
|
|
230
|
+
- **Reasoning-capable badge in the model picker.** Models from pi's registry that support extended thinking now display a `[reasoning]` suffix in their second-line description, making it easier to pick a reasoning-tuned model for the synthesis role. (OpenRouter's REST `/models` endpoint doesn't expose this, so the badge only appears when models come from pi's built-in registry.)
|
|
231
|
+
- **Actionable error messages.** Pre-flight errors now start with `Fix: \`/command\`` (e.g. `Fix: \`/council-settings\` to pick replacements.`) following the actionable-error pattern (context + diagnosis + next step) — instead of the more passive `Run /command`.
|
|
232
|
+
|
|
233
|
+
### Tests
|
|
234
|
+
- 2 new tests for reasoning + contextWindow propagation through the
|
|
235
|
+
registry helper. Total now **67 passing**.
|
|
236
|
+
|
|
237
|
+
## [1.1.1] - 2026-06-26
|
|
238
|
+
|
|
239
|
+
### Fixed
|
|
240
|
+
- **Search picker now actually filters as you type.** v1.1.0 used
|
|
241
|
+
`SelectList.setFilter()` internally, which filters with
|
|
242
|
+
`value.startsWith(query)`. Typing "claude" failed to match
|
|
243
|
+
`value: "anthropic/claude-3.5-sonnet"` (the value doesn't *start* with
|
|
244
|
+
"claude"), so the list filtered to empty and looked like typing was
|
|
245
|
+
broken. v1.1.1 manages the filtered list itself using `fuzzyFilter`
|
|
246
|
+
from `@earendil-works/pi-tui` (the same primitive pi's built-in
|
|
247
|
+
`/model` selector uses), so queries now match across the model
|
|
248
|
+
name, id, and any provider tag in the search haystack.
|
|
249
|
+
|
|
250
|
+
### Changed
|
|
251
|
+
- `searchSelector.ts` no longer depends on `SelectList` (which only
|
|
252
|
+
supports `startsWith` filtering). It renders the list directly with
|
|
253
|
+
`Text` + `Container` children, so any visible-vs-scrolled logic is
|
|
254
|
+
local and easy to follow.
|
|
255
|
+
|
|
256
|
+
## [1.1.0] - 2026-06-26
|
|
257
|
+
|
|
258
|
+
### Added
|
|
259
|
+
- **Typeahead-searchable, scrollable model picker** for both
|
|
260
|
+
`/council-settings` and `/opinion-settings`. Replaces the flat
|
|
261
|
+
`ctx.ui.select()` list with a custom component that:
|
|
262
|
+
- Fuzzy-filters the model list as you type (matches model name, id,
|
|
263
|
+
and provider all at once)
|
|
264
|
+
- Scrolls with Up/Down (PageUp/PageDown jump a viewport)
|
|
265
|
+
- Shows the model id as a muted secondary line under the model name
|
|
266
|
+
- Excludes already-picked council members from later steps
|
|
267
|
+
- Falls back to `ctx.ui.select()` in non-TUI modes (RPC, JSON, print)
|
|
268
|
+
so headless environments still work
|
|
269
|
+
- **`searchSelector.ts`** — reusable helper exported for other
|
|
270
|
+
Pi extensions. Renders via `ctx.ui.custom()` with the same
|
|
271
|
+
`Input` + `SelectList` primitives as pi's built-in `/model` selector.
|
|
272
|
+
|
|
273
|
+
### Changed
|
|
274
|
+
- `/council-settings` flow now shows the searchable picker for all five
|
|
275
|
+
model-selection steps (3 council, 1 synthesis, 1 opinion) plus the
|
|
276
|
+
number of models remaining at each step.
|
|
277
|
+
- `/opinion-settings` collapses the previous two-step (Provider → Model)
|
|
278
|
+
flow into a single searchable picker that searches across all
|
|
279
|
+
configured providers.
|
|
280
|
+
|
|
281
|
+
## [1.0.0] - 2026-06-26
|
|
282
|
+
|
|
283
|
+
### Added
|
|
284
|
+
- **First public release.**
|
|
285
|
+
- **Pi-native model discovery** — `/council-settings` now uses
|
|
286
|
+
`ctx.modelRegistry.getAvailable()` filtered by `provider === "openrouter"`
|
|
287
|
+
to list available models. Skips the API-key prompt entirely when Pi
|
|
288
|
+
already knows about OpenRouter (via `OPENROUTER_API_KEY` or `/login openrouter`).
|
|
289
|
+
- **4th synthesis model selection** — when configuring the council, you can
|
|
290
|
+
now pick a separate model that reads the three council opinions and writes
|
|
291
|
+
the final decision. Defaults to "Council Model 1".
|
|
292
|
+
- **Registry-aware API key resolution** — runners now resolve the OpenRouter
|
|
293
|
+
key from three sources in order: `council-settings.json` →
|
|
294
|
+
`modelRegistry.getApiKeyForProvider("openrouter")` → `process.env.OPENROUTER_API_KEY`.
|
|
295
|
+
- **Comprehensive Quick Start workflow in README** — 3-step setup
|
|
296
|
+
(authenticate → configure → use), the 4-model orchestration flow diagram,
|
|
297
|
+
and a clear API-key resolution order.
|
|
298
|
+
- **DISCLAIMER.md** — covers AI accuracy, cost, and third-party terms
|
|
299
|
+
(OpenRouter, model providers, optional Qdrant persistence).
|
|
300
|
+
- **CONTRIBUTING.md** — dev setup, Conventional Commits, PR process.
|
|
301
|
+
- **CODE_OF_CONDUCT.md** — Contributor Covenant v2.1.
|
|
302
|
+
- **SUPPORT.md** — where to ask questions, response times.
|
|
303
|
+
- **Issue and PR templates** — bug report, feature request, PR checklist.
|
|
304
|
+
- **7 new tests** for the registry-based settings path. Suite is now
|
|
305
|
+
60 tests total.
|
|
306
|
+
|
|
307
|
+
### Changed
|
|
308
|
+
- `openCouncilSettingsUI` rewritten to prefer the pi model registry.
|
|
309
|
+
The legacy API-key-prompt flow remains as a fallback when the registry
|
|
310
|
+
exposes fewer than 3 OpenRouter models.
|
|
311
|
+
- `CouncilSettings` gains an optional `synthesis.modelId` field (defaults
|
|
312
|
+
to `models.model1` when omitted — fully backward compatible).
|
|
313
|
+
- `formatSettingsForDisplay` shows the synthesis model and the "(using
|
|
314
|
+
pi auth — no key stored locally)" indicator when the settings file
|
|
315
|
+
carries no API key.
|
|
316
|
+
- `package.json` `files` array explicitly ships all 8 markdown docs so
|
|
317
|
+
they're included when the package is installed via npm.
|
|
318
|
+
|
|
319
|
+
## [0.1.0] - 2024-06-25
|
|
320
|
+
|
|
321
|
+
### Added
|
|
322
|
+
- Initial private release
|
|
323
|
+
- `/council` command for multi-model coding decisions (3 OpenRouter models)
|
|
324
|
+
- `/opinion` command for single-model second opinions
|
|
325
|
+
- `/council-settings` command for configuring OpenRouter API key and council models
|
|
326
|
+
- `/opinion-settings` command for configuring the second opinion model
|
|
327
|
+
- Settings persistence via `~/.pi/agent/council-settings.json`
|
|
328
|
+
- OpenRouter model discovery and validation
|
|
329
|
+
- Qdrant persistence for council decisions (optional)
|
|
330
|
+
- Full test suite with Vitest
|
|
331
|
+
- Security CI/CD with Gitleaks and npm audit
|
|
332
|
+
|
|
333
|
+
[1.6.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.6.0
|
|
334
|
+
[1.5.1]: https://github.com/bramburn/pi-model-council/releases/tag/v1.5.1
|
|
335
|
+
[1.5.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.5.0
|
|
336
|
+
[1.4.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.4.0
|
|
337
|
+
[1.3.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.3.0
|
|
338
|
+
[1.2.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.2.0
|
|
339
|
+
[1.1.1]: https://github.com/bramburn/pi-model-council/releases/tag/v1.1.1
|
|
340
|
+
[1.1.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.1.0
|
|
341
|
+
[1.0.0]: https://github.com/bramburn/pi-model-council/releases/tag/v1.0.0
|
|
342
|
+
[0.1.0]: https://github.com/bramburn/pi-model-council/releases/tag/v0.1.0
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity
|
|
10
|
+
and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behaviour that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
- Demonstrating empathy and kindness toward other people
|
|
21
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
- Giving and gracefully accepting constructive feedback
|
|
23
|
+
- Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
- Focusing on what is best not just for us as individuals, but for the
|
|
26
|
+
overall community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behaviour include:
|
|
29
|
+
|
|
30
|
+
- The use of sexualised language or imagery, and sexual attention or
|
|
31
|
+
advances of any kind
|
|
32
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
- Public or private harassment
|
|
34
|
+
- Publishing others' private information, such as a physical or email
|
|
35
|
+
address, without their explicit permission
|
|
36
|
+
- Other conduct that could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behaviour and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behaviour that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
+
decisions when appropriate.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
This Code of Conduct applies within all community spaces — including the GitHub
|
|
54
|
+
repository, issue tracker, discussions, and any other channels the project uses
|
|
55
|
+
for communication. It also applies when an individual is officially representing
|
|
56
|
+
the community in public spaces (for example, using an official e-mail address,
|
|
57
|
+
posting via an official social media account, or acting as an appointed
|
|
58
|
+
representative at an online or offline event).
|
|
59
|
+
|
|
60
|
+
## Enforcement
|
|
61
|
+
|
|
62
|
+
Instances of abusive, harassing, or otherwise unacceptable behaviour may be
|
|
63
|
+
reported to the community leaders responsible for enforcement via the GitHub
|
|
64
|
+
Security Advisories page:
|
|
65
|
+
|
|
66
|
+
**https://github.com/bramburn/pi-model-council/security/advisories/new**
|
|
67
|
+
|
|
68
|
+
All complaints will be reviewed and investigated promptly and fairly. All
|
|
69
|
+
community leaders are obligated to respect the privacy and security of the
|
|
70
|
+
reporter of any incident.
|
|
71
|
+
|
|
72
|
+
## Enforcement Guidelines
|
|
73
|
+
|
|
74
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
|
75
|
+
the consequences of any action they deem in violation of this Code of Conduct:
|
|
76
|
+
|
|
77
|
+
### 1. Correction
|
|
78
|
+
|
|
79
|
+
**Community Impact**: Use of inappropriate language or other behaviour deemed
|
|
80
|
+
unprofessional or unwelcome in the community.
|
|
81
|
+
|
|
82
|
+
**Consequence**: A private, written warning from community leaders, providing
|
|
83
|
+
clarity around the nature of the violation and an explanation of why the
|
|
84
|
+
behaviour was inappropriate. A public apology may be requested.
|
|
85
|
+
|
|
86
|
+
### 2. Warning
|
|
87
|
+
|
|
88
|
+
**Community Impact**: A violation through a single incident or series
|
|
89
|
+
of actions.
|
|
90
|
+
|
|
91
|
+
**Consequence**: A warning with consequences for continued behaviour. No
|
|
92
|
+
interaction with the people involved, including unsolicited interaction with
|
|
93
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
|
94
|
+
includes avoiding interactions in community spaces as well as external channels
|
|
95
|
+
like social media. Violating these terms may lead to a temporary or
|
|
96
|
+
permanent ban.
|
|
97
|
+
|
|
98
|
+
### 3. Temporary Ban
|
|
99
|
+
|
|
100
|
+
**Community Impact**: A serious violation of community standards, including
|
|
101
|
+
sustained inappropriate behaviour.
|
|
102
|
+
|
|
103
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
|
104
|
+
communication with the community for a specified period of time. No public or
|
|
105
|
+
private interaction with the people involved is allowed during this period.
|
|
106
|
+
Violating these terms may lead to a permanent ban.
|
|
107
|
+
|
|
108
|
+
### 4. Permanent Ban
|
|
109
|
+
|
|
110
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
|
111
|
+
standards, including sustained inappropriate behaviour, harassment of an
|
|
112
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
|
113
|
+
|
|
114
|
+
**Consequence**: A permanent ban from any sort of public interaction within
|
|
115
|
+
the community.
|
|
116
|
+
|
|
117
|
+
## Attribution
|
|
118
|
+
|
|
119
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
120
|
+
version 2.1, available at
|
|
121
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
122
|
+
|
|
123
|
+
Community Impact Guidelines were inspired by
|
|
124
|
+
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
|
125
|
+
|
|
126
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
127
|
+
[https://www.contributor-covenant.org/faq][faq]. Translations are available
|
|
128
|
+
at [https://www.contributor-covenant.org/translations][translations].
|
|
129
|
+
|
|
130
|
+
[homepage]: https://www.contributor-covenant.org
|
|
131
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
132
|
+
[mozilla coc]: https://github.com/mozilla/diversity
|
|
133
|
+
[faq]: https://www.contributor-covenant.org/faq
|
|
134
|
+
[translations]: https://www.contributor-covenant.org/translations
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Contributing to `pi-model-council`
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving `pi-model-council`! This document explains how to set up the project locally, the standards we expect from pull requests, and how the review process works.
|
|
4
|
+
|
|
5
|
+
## Code of Conduct
|
|
6
|
+
|
|
7
|
+
By participating in this project, you agree to follow our [Code of Conduct](CODE_OF_CONDUCT.md). Please read it before contributing.
|
|
8
|
+
|
|
9
|
+
## Reporting issues
|
|
10
|
+
|
|
11
|
+
Before opening an issue:
|
|
12
|
+
|
|
13
|
+
1. Search existing issues to avoid duplicates.
|
|
14
|
+
2. Try the latest version on `main` to see if the bug still reproduces.
|
|
15
|
+
3. Collect relevant details: Pi version, Node version, OS, model names, and a minimal reproduction.
|
|
16
|
+
|
|
17
|
+
Use the issue templates in [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/) when filing:
|
|
18
|
+
|
|
19
|
+
- **Bug report** — something is broken or behaves unexpectedly.
|
|
20
|
+
- **Feature request** — propose a new capability.
|
|
21
|
+
|
|
22
|
+
For security vulnerabilities, do **not** open a public issue. Follow [SECURITY.md](SECURITY.md) instead.
|
|
23
|
+
|
|
24
|
+
## Development setup
|
|
25
|
+
|
|
26
|
+
### Prerequisites
|
|
27
|
+
|
|
28
|
+
- **Node.js 22+** (matches the CI runner; older versions may work but are untested)
|
|
29
|
+
- **npm 10+** (ships with Node 22)
|
|
30
|
+
- A Pi install for end-to-end smoke testing (`pi install ./pi-model-council`)
|
|
31
|
+
|
|
32
|
+
### Clone and install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
git clone https://github.com/bramburn/pi-model-council.git
|
|
36
|
+
cd pi-model-council
|
|
37
|
+
npm install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Available scripts
|
|
41
|
+
|
|
42
|
+
| Script | Purpose |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `npm run typecheck` | Run `tsc --noEmit`; must pass before review. |
|
|
45
|
+
| `npm run lint` | Run ESLint with zero warnings allowed. |
|
|
46
|
+
| `npm test` | Run the Vitest suite once. |
|
|
47
|
+
| `npm run test:watch` | Vitest in watch mode for local development. |
|
|
48
|
+
| `npm run test:coverage` | Vitest with V8 coverage report. |
|
|
49
|
+
| `npm run audit` | `npm audit --audit-level=high` for dependency vulnerabilities. |
|
|
50
|
+
|
|
51
|
+
All four of `typecheck`, `lint`, `test`, and `audit` run in CI on every push and pull request. PRs that fail any of them will not be merged.
|
|
52
|
+
|
|
53
|
+
## Making changes
|
|
54
|
+
|
|
55
|
+
### Branch and commit
|
|
56
|
+
|
|
57
|
+
1. Create a topic branch off `main`:
|
|
58
|
+
```bash
|
|
59
|
+
git checkout -b feat/short-description
|
|
60
|
+
# or
|
|
61
|
+
git checkout -b fix/short-description
|
|
62
|
+
```
|
|
63
|
+
2. Keep commits focused. One logical change per commit is ideal.
|
|
64
|
+
3. Use the [Conventional Commits](https://www.conventionalcommits.org/) format for commit messages:
|
|
65
|
+
```
|
|
66
|
+
feat: add per-model timeout override
|
|
67
|
+
fix: redact API key in /council-settings list output
|
|
68
|
+
docs: clarify DISCLAIMER.md cost section
|
|
69
|
+
chore: bump @sinclair/typebox to 0.34.1
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Code style
|
|
73
|
+
|
|
74
|
+
- TypeScript strict mode is enforced — no `any` unless you add a comment explaining why.
|
|
75
|
+
- Prefer named exports; avoid default exports except for the Pi extension entry point (`index.ts`).
|
|
76
|
+
- Keep functions small and focused. If a function grows past ~80 lines, consider splitting it.
|
|
77
|
+
- Do not add new runtime dependencies without discussion in the issue first.
|
|
78
|
+
- Use the existing `schemas.ts` (TypeBox) for any new tool parameter validation.
|
|
79
|
+
- Keep the dependency footprint minimal — see [SECURITY.md](SECURITY.md#security-dependencies).
|
|
80
|
+
|
|
81
|
+
### Tests
|
|
82
|
+
|
|
83
|
+
- New behaviour **must** include tests. The Vitest suite lives in `__tests__/`.
|
|
84
|
+
- For new settings logic, follow the pattern in `__tests__/settings.test.ts`.
|
|
85
|
+
- For new UI flows, follow `__tests__/settings-ui.test.ts` (mock the UI surface).
|
|
86
|
+
- For new tools/commands or wiring changes, add a case to `__tests__/smoke.test.ts`.
|
|
87
|
+
- Aim to keep coverage roughly flat or higher; large drops will be flagged in review.
|
|
88
|
+
|
|
89
|
+
### Documentation
|
|
90
|
+
|
|
91
|
+
- If you change a user-facing command or flag, update `README.md` in the same PR.
|
|
92
|
+
- If you add a new env-var or setting, update `.env.example` and `README.md`.
|
|
93
|
+
- If you change the security posture, update `SECURITY.md`.
|
|
94
|
+
- If your change touches AI-generated content or external APIs, update `DISCLAIMER.md`.
|
|
95
|
+
|
|
96
|
+
## Pull request process
|
|
97
|
+
|
|
98
|
+
1. Push your branch and open a PR against `main`.
|
|
99
|
+
2. Fill in the [pull request template](.github/PULL_REQUEST_TEMPLATE.md).
|
|
100
|
+
3. Ensure all CI checks pass (typecheck, lint, test, audit).
|
|
101
|
+
4. Address review feedback by pushing additional commits — do not force-push during review.
|
|
102
|
+
5. Once approved, a maintainer will squash-merge your PR.
|
|
103
|
+
|
|
104
|
+
### PR title format
|
|
105
|
+
|
|
106
|
+
Use the same Conventional Commits prefix as your commits:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
feat: add model discovery fallback to /council-settings
|
|
110
|
+
fix: handle missing opinion provider gracefully
|
|
111
|
+
docs: clarify README setup steps
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The PR title becomes the squash commit message, which feeds into the changelog.
|
|
115
|
+
|
|
116
|
+
## Release process
|
|
117
|
+
|
|
118
|
+
Releases are cut from `main` by the maintainer. Each release:
|
|
119
|
+
|
|
120
|
+
1. Bumps the version in `package.json` (semver).
|
|
121
|
+
2. Adds a dated section to `CHANGELOG.md` summarising user-facing changes.
|
|
122
|
+
3. Tags the commit and publishes to npm.
|
|
123
|
+
|
|
124
|
+
You do not need to bump versions or edit `CHANGELOG.md` in your PR — that happens at release time.
|
|
125
|
+
|
|
126
|
+
## Questions?
|
|
127
|
+
|
|
128
|
+
Open a discussion or issue if anything in this guide is unclear. We would rather answer a question than have a contribution blocked by a missing detail.
|
package/DISCLAIMER.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Disclaimer
|
|
2
|
+
|
|
3
|
+
> **tl;dr** — This extension asks third-party AI models for coding advice. That advice can be wrong, it costs real money, and your prompts leave your machine. Read the full disclaimer below before using `/council` or `/opinion`.
|
|
4
|
+
|
|
5
|
+
## What this extension does
|
|
6
|
+
|
|
7
|
+
`pi-model-council` is a Pi extension that asks one or more AI models for a second opinion on coding decisions. It does not write code itself — it returns suggestions that you (or your primary Pi agent) review and apply.
|
|
8
|
+
|
|
9
|
+
## AI-generated advice is not guaranteed to be correct
|
|
10
|
+
|
|
11
|
+
The output of `/council` and `/opinion` is produced by large language models accessed through [OpenRouter](https://openrouter.ai). These models:
|
|
12
|
+
|
|
13
|
+
- **Can be confidently wrong.** Code suggestions may compile but be incorrect, insecure, outdated, or unfit for your use case.
|
|
14
|
+
- **Have knowledge cutoffs.** They may miss recent API changes, CVEs, or best practices.
|
|
15
|
+
- **Hallucinate.** They may invent function names, library APIs, or configuration flags that do not exist.
|
|
16
|
+
|
|
17
|
+
**Always review the suggestion before applying it.** Treat the output as one input among many — not as an instruction.
|
|
18
|
+
|
|
19
|
+
## You are responsible for what you apply
|
|
20
|
+
|
|
21
|
+
This extension does not gate, lint, or sanity-check model output. You are solely responsible for:
|
|
22
|
+
|
|
23
|
+
- Verifying that the suggested code or approach is correct for your project.
|
|
24
|
+
- Running your existing test suite, type checker, and linter after applying any change.
|
|
25
|
+
- Reviewing for security regressions, data loss, or breaking changes.
|
|
26
|
+
- Complying with the licenses of any third-party code the models may have reproduced.
|
|
27
|
+
|
|
28
|
+
## It costs real money
|
|
29
|
+
|
|
30
|
+
Every invocation calls the OpenRouter API, which charges per token. The full `/council` command makes **at least four** model calls (three council members plus one synthesis call), and may retry on failure. Pricing varies by model.
|
|
31
|
+
|
|
32
|
+
- Check your OpenRouter credit balance before running large deliberations.
|
|
33
|
+
- Use cheaper models for routine decisions; reserve expensive models for high-stakes questions.
|
|
34
|
+
- Revoke your API key immediately if you suspect it has been leaked (see [SECURITY.md](SECURITY.md)).
|
|
35
|
+
|
|
36
|
+
The authors of this extension receive **no portion** of your API spend.
|
|
37
|
+
|
|
38
|
+
## Your data leaves your machine
|
|
39
|
+
|
|
40
|
+
When you run `/council` or `/opinion`, the following data is sent to OpenRouter (and from there to the configured model providers):
|
|
41
|
+
|
|
42
|
+
- The prompt you typed.
|
|
43
|
+
- File contents that Pi attaches as context for the prompt.
|
|
44
|
+
- The system prompt used by this extension (which does **not** contain your API key).
|
|
45
|
+
|
|
46
|
+
**Do not include secrets, credentials, or production data in prompts.** Strip them from any attached files first. Review [OpenRouter's privacy policy](https://openrouter.ai/privacy) for how they handle your data.
|
|
47
|
+
|
|
48
|
+
## Third-party terms apply
|
|
49
|
+
|
|
50
|
+
Your use of OpenRouter and the underlying model providers is governed by their respective terms of service, acceptable-use policies, and pricing. This extension is a thin client over their APIs and does not endorse or guarantee the behaviour of any specific model.
|
|
51
|
+
|
|
52
|
+
## No warranty
|
|
53
|
+
|
|
54
|
+
This software is provided under the MIT License (see [LICENSE](LICENSE)), "as is", without warranty of any kind, express or implied. The authors are not liable for any claim, damages, or other liability arising from the use of this extension or the AI-generated content it returns.
|
|
55
|
+
|
|
56
|
+
## Not professional advice
|
|
57
|
+
|
|
58
|
+
Nothing produced by this extension constitutes legal, financial, medical, or other professional advice. For questions in those domains, consult a qualified professional.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
If anything in this disclaimer is unclear, please open an issue or pull request to improve it.
|