pi-quiver 4.3.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +74 -24
- package/dist/bin/pi-quiver.js +490 -8
- package/extensions/doc_to_md.ts +4 -382
- package/extensions/fast-mode.ts +1 -1
- package/extensions/provider-stall-watchdog.ts +3 -3
- package/extensions/session-name.ts +1 -1
- package/extensions/sword-header.ts +1 -1
- package/lib/doc-to-md-core.ts +591 -0
- package/lib/extension-config.ts +46 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,14 @@ Published to npm as `pi-quiver` (`pi install npm:pi-quiver`). Pushing a
|
|
|
8
8
|
via OIDC trusted publishing. The release helper at
|
|
9
9
|
`.agents/skills/release/scripts/release.sh` cuts the tag; CI publishes.
|
|
10
10
|
|
|
11
|
+
## v4.5.0 - 2026-08-29
|
|
12
|
+
|
|
13
|
+
- Settings resolution: every pi-quiver setting is now read from an optional `"quiver"` root object in `settings.json` (`quiver.<key>`), grouping the four legacy flat keys (`fastMode`, `sessionAutoName`, `swordHeader`, `providerStallWatchdog`) plus any future key. The flat top-level form keeps working, but only for those four legacy keys - it is frozen there and never extended to new settings. Within a layer, `quiver.<key>` wins over flat `<key>` by presence; malformed values and flat/nested duplicates now emit a warning instead of resolving silently.
|
|
14
|
+
|
|
15
|
+
## v4.4.0 - 2026-08-26
|
|
16
|
+
|
|
17
|
+
- doc_to_md: backend ladder now tries a system Python >= 3.12 with `pymupdf4llm` importable, then a one-time managed venv (bootstrapped at the version pin into a per-OS cache dir) between the existing `uv` and `unpdf` rungs. Data plane extracted to pi-free `lib/doc-to-md-core.ts`; new `pi-quiver doc-to-md <path>` CLI subcommand and `doc-to-md` Claude Code skill. `uv`/`soffice` detection is now spawn-based (Windows-correct). The bundled Python conversion script is now resolved from the package root, fixing a path bug that broke it under the bundled CLI.
|
|
18
|
+
|
|
11
19
|
## v4.3.0 - 2026-08-25
|
|
12
20
|
|
|
13
21
|
- fetch: GitHub Actions job URLs (`.../actions/runs/<runId>/job/<jobId>`, singular `/job/` as GitHub's UI produces) now route through `gh run view --job <jobId> --repo <slug>`; plural `/jobs/<id>` paths still fall back to plain HTTP. Both run and job fetches now make a best-effort second `gh run view ... --log-failed` call and append its output under a `## Failed step logs` heading; when nothing failed, the run is still in progress, or logs have expired, the call yields no section and behavior is unchanged (summary-only).
|
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ A 300 KB changelog page never touches your context window - you get a preview an
|
|
|
63
63
|
| Extension | Tool | What it does |
|
|
64
64
|
| --- | --- | --- |
|
|
65
65
|
| `extensions/fetch.ts` | `fetch` | Retrieve URLs over HTTP(S). HTML -> Markdown (Readability extraction, Turndown conversion). Binary saved untouched to a temp file. GitHub issue/PR/repo/actions-run/actions-job URLs auto-route through `gh` (falls back to HTTP); failed runs/jobs include failed-step logs (best-effort, summary-only otherwise). Same size gate as `doc_to_md`. Behavior lives in `lib/fetch-core.ts`; also exposed as the `pi-quiver fetch` CLI (see [Claude Code support](#claude-code-support)). |
|
|
66
|
-
| `extensions/doc_to_md.ts` | `doc_to_md` | Convert a local PDF/DOCX/PPTX to Markdown. High-fidelity via `pymupdf4llm
|
|
66
|
+
| `extensions/doc_to_md.ts` | `doc_to_md` | Convert a local PDF/DOCX/PPTX to Markdown. High-fidelity via `pymupdf4llm`, resolved per process (`uv` -> system Python >= 3.12 with the package -> one-time managed venv in the user cache dir); degraded pure-JS fallback (`unpdf`) otherwise. DOCX/PPTX convert via LibreOffice first. Behavior lives in `lib/doc-to-md-core.ts`; also exposed as the `pi-quiver doc-to-md` CLI (see [Claude Code support](#claude-code-support)). |
|
|
67
67
|
| `extensions/session-name.ts` | `/session-name` | Manual + opt-in automatic session naming, naming rules and deny list, long-session revisits, and Ghostty tab rename. OFF by default. |
|
|
68
68
|
| `extensions/sword-header.ts` | `/builtin-header` | Themed ASCII startup header replacing pi's default logo. OFF by default. |
|
|
69
69
|
| `extensions/fast-mode.ts` | `/fast` | Inject Anthropic fast-mode (`speed: "fast"` + `anthropic-beta: fast-mode-2026-02-01`) into every Claude Opus 4.8 / Opus 5 request, any thinking level. `--fast` flag + `/fast [on\|off\|status]`. OFF by default. |
|
|
@@ -130,52 +130,78 @@ The npm package's bundled JS deps install automatically on `pi install`. A few *
|
|
|
130
130
|
| Prerequisite | Needed by | If absent |
|
|
131
131
|
| --- | --- | --- |
|
|
132
132
|
| `gh` (GitHub CLI, installed + `gh auth login`) | `fetch` GitHub issue/PR/repo/actions-run/actions-job routing | Falls back to an HTTP fetch of the rendered page (private repos hit a login wall). |
|
|
133
|
-
| `uv` (+ managed Python 3.14, fetched on first use) | `doc_to_md` high-fidelity PDF conversion |
|
|
133
|
+
| `uv` (+ managed Python 3.14, fetched on first use) | `doc_to_md` high-fidelity PDF conversion (preferred route) | Falls back to a system Python >= 3.12 with `pymupdf4llm` (or a one-time managed venv it bootstraps); only when no capable Python exists does it degrade to `unpdf`. |
|
|
134
134
|
| LibreOffice (`soffice` on `PATH`) | `doc_to_md` DOCX/PPTX conversion | Office inputs error (no JS fallback for office->PDF); PDFs unaffected. |
|
|
135
135
|
|
|
136
136
|
None is a hard install-time dependency of the package; they are tools you provide in the environment where pi runs.
|
|
137
137
|
|
|
138
138
|
### Opt-in extension config
|
|
139
139
|
|
|
140
|
-
These extensions are opt-in via `settings.json` (project `.pi/settings.json` overrides the global agent-dir layer):
|
|
140
|
+
These extensions are opt-in via `settings.json` (project `.pi/settings.json` overrides the global agent-dir layer), nested under an optional `"quiver"` root:
|
|
141
141
|
|
|
142
142
|
```jsonc
|
|
143
143
|
{
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
"quiver": {
|
|
145
|
+
"sessionAutoName": {
|
|
146
|
+
"enabled": false,
|
|
147
|
+
"ghosttyTab": true,
|
|
148
|
+
"rules": [],
|
|
149
|
+
"deny": [],
|
|
150
|
+
"revisitFirstTurn": 0,
|
|
151
|
+
"revisitEveryTurns": 0
|
|
152
|
+
}, // or boolean shorthand
|
|
153
|
+
"swordHeader": false, // or { "enabled": true }
|
|
154
|
+
"fastMode": false, // or { "enabled": true }
|
|
155
|
+
"providerStallWatchdog": false // or { "enabled": true }
|
|
156
|
+
}
|
|
155
157
|
}
|
|
156
158
|
```
|
|
157
159
|
|
|
160
|
+
Each key resolves independently: within a layer, `quiver.<key>` wins over a
|
|
161
|
+
flat top-level `<key>` by presence alone (even when the winning value is
|
|
162
|
+
malformed); across layers, each layer's candidate is validated into a partial
|
|
163
|
+
patch and `Object.assign`ed over the accumulator in layer order (project
|
|
164
|
+
last), so project fields override matching global fields while unmatched
|
|
165
|
+
global fields survive - a flat-vs-nested shape difference between layers
|
|
166
|
+
never changes this. The flat top-level form still works, but only for four
|
|
167
|
+
legacy keys, frozen at `fastMode`, `sessionAutoName`, `swordHeader`, and
|
|
168
|
+
`providerStallWatchdog` - never extended to new settings (see [Migrating from
|
|
169
|
+
flat keys](#migrating-from-flat-keys)).
|
|
170
|
+
|
|
171
|
+
Worked mixed-shape example: global `settings.json` has flat
|
|
172
|
+
`"fastMode": false`, project `.pi/settings.json` has
|
|
173
|
+
`"quiver": { "fastMode": { "enabled": true } }`. The project layer's
|
|
174
|
+
patch (`{ "enabled": true }`) is `Object.assign`ed over the accumulator
|
|
175
|
+
seeded from global's patch, so the resolved config is `{ "enabled": true }` -
|
|
176
|
+
same outcome here because `enabled` is the only field either layer sets, but
|
|
177
|
+
the merge is per-field: a global field with no project counterpart would
|
|
178
|
+
survive untouched.
|
|
179
|
+
|
|
158
180
|
`sessionAutoName.enabled` makes one extra short LLM call per session (once, after the first turn) to title it; `false` (default) makes no model calls. `rules` appends house conventions to the naming prompt (later rules win when they conflict with the built-ins). Literal, case-insensitive `deny` phrases are stripped from every name; whitespace inside a phrase is loose, so `"acme corp"` also catches `AcmeCorp`. `revisitFirstTurn` re-evaluates the name once that many model round trips have completed, while `revisitEveryTurns` does so at every multiple; both default to `0` (off) because each revisit costs another short LLM call. For example, `10` and `100` mark round trips 10, 100, 200, 300. Revisits only run when the agent has fully settled (idle, nothing queued) - an automated multi-turn run such as a subagent chain is never renamed or delayed mid-flight; cadence points it crossed fire once, at the settle. A machine-generated name is replaced when stale. A name set by a human is never overwritten: the extension strongly prefers it, and announces a suggestion only when the work has clearly moved on. Counts come from the persisted transcript, so they survive resume.
|
|
159
181
|
|
|
160
182
|
`fastMode` only affects `claude-opus-4-8` and `claude-opus-5` requests on Anthropic's `anthropic-messages` API; enabling it opts into premium fast-mode pricing. `--fast` forces it on for one launch; `/fast on|off` toggles live. Proxy providers (opencode, cloudflare-ai-gateway) are excluded. `fastMode`'s header injection needs the `before_provider_headers` hook (pi bundling `@earendil-works/pi-coding-agent` >= 0.80.5); on older pi the beta header is silently not sent. See [doc/fetch.md](doc/fetch.md) and [doc/doc-to-md.md](doc/doc-to-md.md) for the ingestion tools' full reference; session-name/sword-header behavior above is complete.
|
|
161
183
|
|
|
162
184
|
`pi-ai` prices every fast request at standard rates - it has no `usage.speed` support and no request-level pricing modifier - so `fastMode` corrects the reported cost itself: a `message_end` handler scales all four `usage.cost` components by `FAST_MODE_COST_MULTIPLIER` (2x) and returns the corrected message. Persisted session JSONL and pi's own native cost display are always exact, since they're written from this corrected message. pi-cohort's live `Σ$` reflects the correction only when pi-quiver's `message_end` handler runs before pi-cohort's - best-effort, depending on extension load order - and is reconciled on pi-cohort's next `session_start` regardless. The upstream fix (teaching `pi-ai`'s `Usage`/`calculateCost` about `usage.speed`) is the better long-term path and is tracked separately.
|
|
163
185
|
|
|
164
|
-
Recommended explicit retry and watchdog settings
|
|
186
|
+
Recommended explicit retry and watchdog settings - `providerStallWatchdog`
|
|
187
|
+
nests under `quiver`, while pi-core's own `retry` stays flat beside it (it is
|
|
188
|
+
not a pi-quiver setting and is never nested):
|
|
165
189
|
|
|
166
190
|
```json
|
|
167
191
|
{
|
|
192
|
+
"quiver": {
|
|
193
|
+
"providerStallWatchdog": {
|
|
194
|
+
"enabled": true,
|
|
195
|
+
"firstEventMs": 20000,
|
|
196
|
+
"warningMs": 120000,
|
|
197
|
+
"recoveryMs": 240000,
|
|
198
|
+
"maxStallRetries": 3
|
|
199
|
+
}
|
|
200
|
+
},
|
|
168
201
|
"retry": {
|
|
169
202
|
"enabled": true,
|
|
170
203
|
"maxRetries": 3,
|
|
171
204
|
"baseDelayMs": 2000
|
|
172
|
-
},
|
|
173
|
-
"providerStallWatchdog": {
|
|
174
|
-
"enabled": true,
|
|
175
|
-
"firstEventMs": 20000,
|
|
176
|
-
"warningMs": 120000,
|
|
177
|
-
"recoveryMs": 240000,
|
|
178
|
-
"maxStallRetries": 3
|
|
179
205
|
}
|
|
180
206
|
}
|
|
181
207
|
```
|
|
@@ -205,13 +231,37 @@ Operational notes:
|
|
|
205
231
|
- **A watchdog abort that the provider ignores escalates after a fixed 10s.** Any post-abort stream event re-arms that deadline (bytes prove only that the connection was alive at that instant), so a stream that emits a straggler and then wedges still escalates 10s after its last event. This reduces the hang; it cannot force the provider to stop, and undici's timeouts remain the final backstop.
|
|
206
232
|
- **Headless runs report on stderr.** In `print`/`json` mode pi binds a no-op UI, so watchdog notices go out via `console.warn`. Nothing is ever written to stdout, which `json` mode uses for its protocol. In TUI and RPC the notices render as main-window notifications, not the bottom status line.
|
|
207
233
|
|
|
234
|
+
### Migrating from flat keys
|
|
235
|
+
|
|
236
|
+
The flat top-level form (`"fastMode": ...` etc. directly under `settings.json`)
|
|
237
|
+
is the outdated configuration style. It is legacy-frozen to exactly the four
|
|
238
|
+
keys above - `fastMode`, `sessionAutoName`, `swordHeader`,
|
|
239
|
+
`providerStallWatchdog` - and will never gain a fifth. To migrate, wrap your
|
|
240
|
+
existing keys under `"quiver": { ... }` and delete the flat copies:
|
|
241
|
+
|
|
242
|
+
```jsonc
|
|
243
|
+
// before
|
|
244
|
+
{ "fastMode": true }
|
|
245
|
+
|
|
246
|
+
// after
|
|
247
|
+
{ "quiver": { "fastMode": true } }
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Until you delete the flat copy, having both set is not an error - the
|
|
251
|
+
duplicate resolves per the precedence above (nested wins within a layer) -
|
|
252
|
+
but it emits a warning notification, deduped per process (each unique
|
|
253
|
+
message fires at most once per pi process - in practice once per interactive
|
|
254
|
+
session) until the flat entry is removed. Every new pi-quiver setting introduced after this change
|
|
255
|
+
(for example a future `slack` key) is nested-only from day one: it has no
|
|
256
|
+
flat form to fall back to.
|
|
257
|
+
|
|
208
258
|
## Claude Code support
|
|
209
259
|
|
|
210
260
|
`fetch`'s core (`lib/fetch-core.ts`) is also published as a CLI, so Claude Code can use the same routing, size gate, and spill behavior as pi's native tool - without pi ever seeing Claude-only files.
|
|
211
261
|
|
|
212
|
-
**Exposed:** the `quiver` plugin, served from this repo's `.claude-plugin/marketplace.json`, with
|
|
262
|
+
**Exposed:** the `quiver` plugin, served from this repo's `.claude-plugin/marketplace.json`, with two skills: `fetch` (invoked as `quiver:fetch` / `/quiver:fetch`) and `doc-to-md` (invoked as `quiver:doc-to-md` / `/quiver:doc-to-md`). The `fetch` skill runs `npx -y pi-quiver@latest fetch <url> [flags]` via Bash - full parameter parity with the pi tool (`--method`, `--header`, `--body`, `--raw`, `--timeout-ms`), same GitHub `gh` routing (including failed-step logs on failed runs/jobs), same size gate, same binary-to-temp-file handling. See [doc/fetch.md](doc/fetch.md#claude-code-cli-pi-quiver-fetch) for exit codes and flags. The `doc-to-md` skill runs `npx -y pi-quiver@latest doc-to-md <path>` via Bash - same backend ladder, size gate, and degraded-fallback marking as the pi tool. See [doc/doc-to-md.md](doc/doc-to-md.md#cli-pi-quiver-doc-to-md) for exit codes.
|
|
213
263
|
|
|
214
|
-
**Not exposed:** pi extensions
|
|
264
|
+
**Not exposed:** the other pi extensions in this package (`session-name`, `sword-header`, `fast-mode`, `provider-stall-watchdog`) - the marketplace allowlists only `./skills/fetch` and `./skills/doc-to-md`, and the npm tarball never ships `skills/` or `.claude-plugin/` (pi's own `files` allowlist excludes them, and pi's explicit `pi.extensions` manifest makes them invisible to pi's convention-directory auto-discovery either way).
|
|
215
265
|
|
|
216
266
|
Add the marketplace and enable the plugin in `.claude/settings.json`:
|
|
217
267
|
|