pi-quiver 3.1.0 → 3.1.2
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 +14 -1
- package/README.md +93 -112
- package/fetch.ts +10 -3
- package/package.json +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
Format follows sibling pi packages (e.g. [`pi-
|
|
3
|
+
Format follows sibling pi packages (e.g. [`pi-condense`](https://github.com/jjuraszek/pi-condense/blob/main/CHANGELOG.md)):
|
|
4
4
|
one entry per `vX.Y.Z` tag, newest first, terse bullets, dated.
|
|
5
5
|
|
|
6
6
|
Published to npm as `pi-quiver` (`pi install npm:pi-quiver`). Pushing a
|
|
@@ -8,6 +8,19 @@ 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
|
+
## v3.1.2 - 2026-07-07
|
|
12
|
+
|
|
13
|
+
- **`fetch` routes GitHub Actions run URLs through `gh`.** `github.com/{owner}/{repo}/actions/runs/{id}` URLs are served by `gh run view {id} --repo {owner}/{repo}` and returned through the existing size gate with a `Source: gh run view ...` header, alongside the existing issue/PR/repo routing. Only the bare run URL routes; deeper paths (`.../runs/{id}/jobs/{jobId}`, `.../actions/workflows/{file}`) fall back to HTTP. Falls back silently when `gh` is absent/unauthenticated/errors; `raw=true` forces the rendered page.
|
|
14
|
+
|
|
15
|
+
## v3.1.1 - 2026-07-05
|
|
16
|
+
|
|
17
|
+
Branding, funding, and gallery preview. No behavior change.
|
|
18
|
+
|
|
19
|
+
- **Logo + pi.dev gallery preview.** Repo-root `pi-quiver.png` (640x640), shown in the README and wired as `pi.image`.
|
|
20
|
+
- **Buy Me a Coffee funding.** `funding` in `package.json`, `.github/FUNDING.yml`, and a README badge.
|
|
21
|
+
- Rewrote `description`; added `docx`, `pptx` keywords.
|
|
22
|
+
- Fixed stale sibling references: README `pi-superpowers` -> `pi-gauntlet`; this CHANGELOG's format note `pi-context-prune` -> `pi-condense`.
|
|
23
|
+
|
|
11
24
|
## v3.1.0 - 2026-07-04
|
|
12
25
|
|
|
13
26
|
- **`fetch` auto-routes GitHub URLs through the `gh` CLI.** `github.com` issue,
|
package/README.md
CHANGED
|
@@ -1,145 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Extensions
|
|
6
|
-
|
|
7
|
-
| Extension | Tool | What it does |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `fetch.ts` | `fetch` | Retrieve URLs over HTTP(S). HTML → Markdown (main-content extraction, stripped boilerplate). Binary content saved untouched to a temp file. **Context-safe:** output over 32 KB or 1000 lines is written to a temp file with a preview + file path. Prevents a single fetch from flooding the context window. |
|
|
10
|
-
| `doc_to_md.ts` | `doc_to_md` | Convert a local PDF/DOCX/PPTX to Markdown. High-fidelity via `pymupdf4llm` (run through `uv`, fetched on first use); degraded pure-JS fallback (`unpdf`) when `uv`/Python is unavailable or conversion times out. DOCX/PPTX convert via LibreOffice (`soffice`) to PDF first. Same 32 KB / 1000-line size gate as `fetch`. |
|
|
11
|
-
| `session-name.ts` | `/session-name` | Name work sessions. Manual `/session-name [name]` always works. **OFF by default:** when opted in via `settings.json`, after the first agent turn it asks the current model for a concise session name + short tab label and applies them, and renames the **Ghostty** tab via OSC 2 (only when the active terminal is really Ghostty), re-asserting it each turn so the tab tracks the session name. |
|
|
12
|
-
| `sword-header.ts` | `/builtin-header` | Replace the TUI startup logo with a theme-colored ASCII greatsword (hilt = accent, blade = text). **OFF by default:** only installs the header when opted in via `settings.json`. `/builtin-header` restores the built-in header at runtime. |
|
|
13
|
-
|
|
14
|
-
## Prerequisites
|
|
15
|
-
|
|
16
|
-
The npm package's bundled JS deps install automatically on `pi install` - nothing to set up there. A few **runtime system binaries** are optional; each degrades gracefully when absent:
|
|
17
|
-
|
|
18
|
-
| Prerequisite | Needed by | If absent |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| `gh` (GitHub CLI, installed + `gh auth login`) | `fetch` GitHub issue/PR/repo routing | Falls back to an HTTP fetch of the rendered page (private repos hit a login wall). |
|
|
21
|
-
| `uv` (+ managed Python 3.14, fetched on first use) | `doc_to_md` high-fidelity PDF conversion | Degrades to the pure-JS `unpdf` fallback (no faithful tables/headings). |
|
|
22
|
-
| LibreOffice (`soffice` on `PATH`) | `doc_to_md` DOCX/PPTX conversion | Office inputs error (no JS fallback for office->PDF); PDFs unaffected. |
|
|
23
|
-
|
|
24
|
-
None is a hard install-time dependency of the package; they are tools you provide in the environment where pi runs.
|
|
25
|
-
|
|
26
|
-
### fetch — content routing & context hygiene
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/jjuraszek/pi-quiver/main/pi-quiver.png" alt="pi-quiver" width="180">
|
|
3
|
+
</p>
|
|
27
4
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
**HTML → Markdown:**
|
|
31
|
-
- Mozilla Readability extracts main content, strips navigation/chrome/boilerplate
|
|
32
|
-
- Turndown converts to Markdown with GFM support (pipe tables, fenced code blocks, ATX headings)
|
|
33
|
-
- Page title becomes a top-level `#` heading
|
|
34
|
-
- Download cap: **1 MB**
|
|
35
|
-
|
|
36
|
-
**Binary (images, PDFs, archives, fonts, audio/video) → temp file:**
|
|
37
|
-
- Streamed untouched to `${TMPDIR}/pi-fetch/<stamp>-<host>-<hash>.<ext>` without decoding
|
|
38
|
-
- Detection: content-type check + NUL-byte sniff in first ≤64 KB (catches mislabeled payloads)
|
|
39
|
-
- Returns: status, content-type, size, file path — **no preview**
|
|
40
|
-
- Download cap: **50 MB**
|
|
41
|
-
|
|
42
|
-
**Text / Markdown / JSON size gate:**
|
|
43
|
-
- Inline when **≤ 32 KB AND ≤ 1000 lines** (converted output size)
|
|
44
|
-
- Otherwise **spills to file** with:
|
|
45
|
-
- HTTP status, content-type, charset, byte/line counts
|
|
46
|
-
- File path (`Saved-To:`)
|
|
47
|
-
- 60-line preview
|
|
48
|
-
- Instruction to `grep` (Markdown is grep-able by heading: `^#`) or `read` slices
|
|
49
|
-
|
|
50
|
-
**JSON:** Pretty-printed with 2-space indent before the gate.
|
|
51
|
-
|
|
52
|
-
**GitHub URLs -> `gh`:** `github.com` issue (`/issues/{n}`), PR (`/pull/{n}`), and repo-root (`/{owner}/{repo}`) URLs are served by running the `gh` CLI (`gh issue|pr view --comments`, `gh repo view`) and returning its output, tagged with a `Source: gh ...` header and run through the same size gate. Requires `gh` (see [Prerequisites](#prerequisites)); if `gh` is missing or the call fails, `fetch` silently falls back to the normal HTTP path. Pass `raw=true` to force the rendered HTML page. All other GitHub paths (`tree`, `blob`, `raw`, `releases`, gists, ...) use the HTTP path unchanged. Routing is also skipped (plain HTTP used) when the request is non-GET, carries a body, or sets custom headers. gh output is bounded by a 10 MB buffer and run through the same size gate (spilled to a file when large), not the 1 MB HTTP download cap.
|
|
53
|
-
|
|
54
|
-
**Parameters:**
|
|
55
|
-
- `raw=true`: Skip HTML→Markdown and JSON pretty-printing; return decoded body as-is (still subject to the size gate).
|
|
56
|
-
- `raw=true` also bypasses GitHub `gh` routing (forces the HTTP/rendered path).
|
|
57
|
-
|
|
58
|
-
**Truncation:** Parsable content over 1 MB is truncated with a `(truncated to 1MB)` note; binary over 50 MB notes `(truncated to 50MB)`.
|
|
59
|
-
|
|
60
|
-
**Runtime dependencies:** `jsdom`, `@mozilla/readability`, `turndown`, `turndown-plugin-gfm`. Shipped in the npm package and installed automatically on `pi install` - no manual setup needed.
|
|
61
|
-
|
|
62
|
-
### doc_to_md — local document → Markdown
|
|
5
|
+
# pi-quiver
|
|
63
6
|
|
|
64
|
-
|
|
7
|
+
[](https://buymeacoffee.com/jjurasszek)
|
|
65
8
|
|
|
66
|
-
|
|
9
|
+
Ground-truth ingestion for the [Pi coding agent](https://github.com/earendil-works/pi): pull real web pages, docs, and local files into context without flooding it.
|
|
67
10
|
|
|
68
|
-
|
|
69
|
-
- **Fallback — `unpdf`** (pure JS, bundled PDF.js). Used when `uv` is not on `PATH`, the warm probe fails, or a conversion times out. Output is plain text with page breaks — **no faithful tables/headings**. Degraded results are marked in the output (`[Note: degraded extraction via unpdf ...]`) and carry a `Fallback-Reason:` line.
|
|
11
|
+
## The problem
|
|
70
12
|
|
|
71
|
-
|
|
13
|
+
Reasoning from a model's training memory instead of the real page, the current docs, or the actual PDF is how agents confidently ship wrong answers about APIs that changed last month. Mature engineering work has to be data-driven - the agent needs to read the real source.
|
|
72
14
|
|
|
73
|
-
|
|
15
|
+
But the moment an agent does that, one `fetch` or PDF read can dump hundreds of kilobytes of boilerplate into context, degrading every turn after it.
|
|
74
16
|
|
|
75
|
-
|
|
17
|
+
## Why pi-quiver exists
|
|
76
18
|
|
|
77
|
-
|
|
78
|
-
|---|---|---|
|
|
79
|
-
| `PI_DOC_TO_MD_PYMUPDF_VERSION` | `1.27.2.3` | `pymupdf4llm` version pin passed to `uv --with` (digits/dots only) |
|
|
80
|
-
| `PI_DOC_TO_MD_WARM_TIMEOUT_MS` | `120000` | Warm/install call budget — covers the cold wheel (+ managed Python) download |
|
|
81
|
-
| `PI_DOC_TO_MD_CONVERT_TIMEOUT_MS` | `60000` | Per-document conversion budget (also bounds the `unpdf` fallback) |
|
|
82
|
-
| `PI_DOC_TO_MD_SOFFICE_TIMEOUT_MS` | `120000` | LibreOffice `.docx`/`.pptx` → PDF budget |
|
|
19
|
+
`fetch` and `doc_to_md` bring real web pages, GitHub issues/PRs, and local PDF/DOCX/PPTX files into context - and every result is size-gated by construction: over 32 KB or 1000 lines spills to a temp file with a preview and a grep/read hint, so a single call can never flood the window. Ingestion is what makes data-driven work possible; the gate is what keeps it safe.
|
|
83
20
|
|
|
84
|
-
|
|
21
|
+
`session-name` and `sword-header` are smaller, opt-in ergonomics on top - session labeling and a themed startup header.
|
|
85
22
|
|
|
86
|
-
|
|
23
|
+
## Part of the pi agent toolkit
|
|
87
24
|
|
|
88
|
-
|
|
25
|
+
Four independent extensions for the [pi coding agent](https://github.com/earendil-works/pi), each owning one concern of running agents seriously:
|
|
89
26
|
|
|
90
|
-
|
|
27
|
+
- **pi-quiver** - capabilities (fetch, doc conversion, session tools)
|
|
28
|
+
- [pi-cohort](https://github.com/jjuraszek/pi-cohort) - coordination (delegate to focused child agents)
|
|
29
|
+
- [pi-condense](https://github.com/jjuraszek/pi-condense) - context economy (prune context, keep it recoverable)
|
|
30
|
+
- [pi-gauntlet](https://github.com/jjuraszek/pi-gauntlet) - process (the gated brainstorm->ship workflow)
|
|
91
31
|
|
|
92
|
-
|
|
32
|
+
No code dependency between them. pi-quiver is call-level: it gates the size of what comes *in*. [pi-condense](https://github.com/jjuraszek/pi-condense) is loop-level: it prunes what's already *in context* once a tool call is done. Different problem, same discipline.
|
|
93
33
|
|
|
94
|
-
|
|
34
|
+
## Mental model
|
|
95
35
|
|
|
96
|
-
|
|
97
|
-
- **Automatic naming (opt-in).** After the first agent turn completes, if no name is set yet, the extension asks the **current model** for a 3-6 word session title plus a 1-4 word tab label and applies both. It only runs once per session and never overwrites an existing name.
|
|
98
|
-
- **Resume reflection (opt-in).** When a session that already carries a name is loaded/resumed/reloaded, its tab label is re-applied so the Ghostty tab matches.
|
|
99
|
-
- **Per-turn re-assert (opt-in).** The tab is re-pinned to the session name at the start of every turn. Pi owns the OS terminal title (OSC 0, `pi - <name> - <cwd>`) and overwrites it on every name change and session switch; the re-assert is the only hook that fires *after* pi's writer on a session swap, so the Ghostty tab and the session name stay in sync instead of drifting. It self-heals: if the name was changed outside this extension, the tab label is re-derived from the new name.
|
|
100
|
-
- **Ghostty tab rename.** The short label is written via OSC 2 (`ESC ] 2 ; <label> BEL`) **only when the active terminal is really Ghostty** (`TERM_PROGRAM=ghostty`, `TERM=xterm-ghostty`, or a `GHOSTTY_*` dir env) **and** stdout is a TTY. Other terminals are never touched. Auto-naming keeps its curated short label; re-derived labels (resume/external rename) are the first words of the session name.
|
|
36
|
+
Every extension here is context-safe by construction, not by convention: the size check runs on every call, there's no flag to forget. Two tools bring real sources in (`fetch`, `doc_to_md`); two are opt-in ergonomics (`session-name`, `sword-header`).
|
|
101
37
|
|
|
102
|
-
|
|
38
|
+
```mermaid
|
|
39
|
+
flowchart LR
|
|
40
|
+
S[web page / PDF / doc] --> T["fetch / doc_to_md"]
|
|
41
|
+
T --> E[extract main content]
|
|
42
|
+
E --> G{"over 32KB or 1000 lines?"}
|
|
43
|
+
G -->|no| I[return inline to context]
|
|
44
|
+
G -->|yes| F[spill to temp file<br/>return preview + grep/read hint]
|
|
45
|
+
```
|
|
103
46
|
|
|
104
|
-
|
|
47
|
+
## Quick example
|
|
105
48
|
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
// full form, defaults shown
|
|
109
|
-
"sessionAutoName": { "enabled": false, "ghosttyTab": true }
|
|
110
|
-
}
|
|
49
|
+
```bash
|
|
50
|
+
pi install npm:pi-quiver
|
|
111
51
|
```
|
|
112
52
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
53
|
+
```
|
|
54
|
+
> fetch https://example.com/some-huge-changelog
|
|
55
|
+
Saved-To: /tmp/pi-fetch/2026-...-example.com-....md
|
|
56
|
+
60-line preview follows. grep '^#' the file for headings, or read a slice.
|
|
57
|
+
```
|
|
117
58
|
|
|
118
|
-
|
|
59
|
+
A 300 KB changelog page never touches your context window - you get a preview and a path.
|
|
119
60
|
|
|
120
|
-
|
|
61
|
+
## Architecture
|
|
121
62
|
|
|
122
|
-
|
|
63
|
+
| Extension | Tool | What it does |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `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 URLs auto-route through `gh` (falls back to HTTP). Same size gate as `doc_to_md`. |
|
|
66
|
+
| `doc_to_md.ts` | `doc_to_md` | Convert a local PDF/DOCX/PPTX to Markdown. High-fidelity via `pymupdf4llm` (run through `uv`); degraded pure-JS fallback (`unpdf`) when `uv`/Python is unavailable or conversion times out. DOCX/PPTX convert via LibreOffice first. |
|
|
67
|
+
| `session-name.ts` | `/session-name` | Manual + opt-in automatic session naming, with Ghostty tab rename. OFF by default. |
|
|
68
|
+
| `sword-header.ts` | `/builtin-header` | Themed ASCII startup header replacing pi's default logo. OFF by default. |
|
|
123
69
|
|
|
124
|
-
|
|
70
|
+
Full routing rules, size-gate mechanics, and config: [doc/fetch.md](doc/fetch.md), [doc/doc-to-md.md](doc/doc-to-md.md).
|
|
125
71
|
|
|
126
|
-
|
|
72
|
+
## Key concepts
|
|
127
73
|
|
|
128
|
-
|
|
74
|
+
| Concept | Meaning |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| Size gate | Text/Markdown/JSON output over 32 KB or 1000 lines spills to a temp file with a 60-line preview instead of inlining. |
|
|
77
|
+
| Content routing | HTML -> Markdown, binary -> untouched file, GitHub URLs -> `gh` CLI, everything else -> the size gate. |
|
|
78
|
+
| Graceful degradation | Optional binaries (`gh`, `uv`, LibreOffice) are never hard install-time deps; each has a defined, documented fallback or failure mode. |
|
|
79
|
+
| Opt-in ergonomics | `session-name` and `sword-header` do nothing until explicitly enabled in `settings.json`. |
|
|
129
80
|
|
|
130
|
-
|
|
131
|
-
- **`/builtin-header`** restores the built-in pi header at runtime (always available).
|
|
81
|
+
## When to use
|
|
132
82
|
|
|
133
|
-
|
|
83
|
+
- An agent needs to reason from a real web page, GitHub issue/PR, or local PDF/DOCX/PPTX instead of memory.
|
|
84
|
+
- You want that ingestion to be safe by default, with no risk of a single call blowing the context budget.
|
|
134
85
|
|
|
135
|
-
|
|
86
|
+
## When NOT to use
|
|
136
87
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// object form also accepted: "swordHeader": { "enabled": true }
|
|
141
|
-
}
|
|
142
|
-
```
|
|
88
|
+
- You need a general-purpose web scraper (JS-rendered pages, pagination, auth flows) - `fetch` does plain HTTP + Readability extraction, nothing more.
|
|
89
|
+
- You need spreadsheet conversion - `doc_to_md` explicitly excludes spreadsheets (they paginate badly via PDF).
|
|
90
|
+
- You want automatic session naming or a custom header without opting in - both stay off until you flip the config.
|
|
143
91
|
|
|
144
92
|
## Install
|
|
145
93
|
|
|
@@ -170,6 +118,31 @@ git clone git@github.com:jjuraszek/pi-quiver.git ~/repos/pi-quiver
|
|
|
170
118
|
pi -e ~/repos/pi-quiver/fetch.ts
|
|
171
119
|
```
|
|
172
120
|
|
|
121
|
+
## Prerequisites
|
|
122
|
+
|
|
123
|
+
The npm package's bundled JS deps install automatically on `pi install`. A few **runtime system binaries** are optional; each degrades gracefully when absent:
|
|
124
|
+
|
|
125
|
+
| Prerequisite | Needed by | If absent |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| `gh` (GitHub CLI, installed + `gh auth login`) | `fetch` GitHub issue/PR/repo/actions-run routing | Falls back to an HTTP fetch of the rendered page (private repos hit a login wall). |
|
|
128
|
+
| `uv` (+ managed Python 3.14, fetched on first use) | `doc_to_md` high-fidelity PDF conversion | Degrades to the pure-JS `unpdf` fallback (no faithful tables/headings). |
|
|
129
|
+
| LibreOffice (`soffice` on `PATH`) | `doc_to_md` DOCX/PPTX conversion | Office inputs error (no JS fallback for office->PDF); PDFs unaffected. |
|
|
130
|
+
|
|
131
|
+
None is a hard install-time dependency of the package; they are tools you provide in the environment where pi runs.
|
|
132
|
+
|
|
133
|
+
### session-name and sword-header config
|
|
134
|
+
|
|
135
|
+
Both are opt-in via `settings.json` (project `.pi/settings.json` overrides the global agent-dir layer):
|
|
136
|
+
|
|
137
|
+
```jsonc
|
|
138
|
+
{
|
|
139
|
+
"sessionAutoName": { "enabled": false, "ghosttyTab": true }, // or boolean shorthand
|
|
140
|
+
"swordHeader": false // or { "enabled": true }
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`sessionAutoName.enabled` makes one extra short LLM call per session (once, after the first turn) to title it; `false` (default) makes no model calls. 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.
|
|
145
|
+
|
|
173
146
|
## Development
|
|
174
147
|
|
|
175
148
|
Deps are peers (`@earendil-works/*`, `@sinclair/typebox`) plus the bundled
|
|
@@ -183,6 +156,14 @@ npm run test:all # node --test *.test.ts + tsc --noEmit typecheck
|
|
|
183
156
|
`npm test` runs the unit tests alone; `npm run typecheck` runs the type pass.
|
|
184
157
|
Both run in CI on ubuntu + windows (`.github/workflows/test.yml`).
|
|
185
158
|
|
|
159
|
+
## How this fits the platform
|
|
160
|
+
|
|
161
|
+
pi-quiver is how ground truth gets into an agent's context - real pages, PDFs, docs, cleanly and safely. The other three then coordinate work over it ([pi-cohort](https://github.com/jjuraszek/pi-cohort)), prune it once it's stale ([pi-condense](https://github.com/jjuraszek/pi-condense)), and govern the process end to end ([pi-gauntlet](https://github.com/jjuraszek/pi-gauntlet)).
|
|
162
|
+
|
|
163
|
+
## Support
|
|
164
|
+
|
|
165
|
+
If this saves you time, consider [buying me a coffee](https://buymeacoffee.com/jjurasszek).
|
|
166
|
+
|
|
186
167
|
## Release
|
|
187
168
|
|
|
188
169
|
Published to npm by CI. Pushing a `vX.Y.Z` tag triggers
|
package/fetch.ts
CHANGED
|
@@ -44,7 +44,8 @@ interface FetchToolDetails {
|
|
|
44
44
|
type GhTarget =
|
|
45
45
|
| { kind: "issue"; url: string }
|
|
46
46
|
| { kind: "pr"; url: string }
|
|
47
|
-
| { kind: "repo"; slug: string }
|
|
47
|
+
| { kind: "repo"; slug: string }
|
|
48
|
+
| { kind: "run"; slug: string; runId: string; url: string };
|
|
48
49
|
|
|
49
50
|
const RESERVED_OWNERS = new Set([
|
|
50
51
|
"orgs", "users", "sponsors", "topics", "marketplace", "apps",
|
|
@@ -67,6 +68,9 @@ export function classifyGitHubTarget(url: URL): GhTarget | null {
|
|
|
67
68
|
if (segs.length === 4 && segs[2] === "pull" && /^\d+$/.test(segs[3])) {
|
|
68
69
|
return { kind: "pr", url: `https://github.com/${owner}/${repo}/pull/${segs[3]}` };
|
|
69
70
|
}
|
|
71
|
+
if (segs.length === 5 && segs[2] === "actions" && segs[3] === "runs" && /^\d+$/.test(segs[4])) {
|
|
72
|
+
return { kind: "run", slug: `${owner}/${repo}`, runId: segs[4], url: `https://github.com/${owner}/${repo}/actions/runs/${segs[4]}` };
|
|
73
|
+
}
|
|
70
74
|
if (segs.length === 2) {
|
|
71
75
|
return { kind: "repo", slug: `${owner}/${repo}` };
|
|
72
76
|
}
|
|
@@ -76,6 +80,7 @@ export function classifyGitHubTarget(url: URL): GhTarget | null {
|
|
|
76
80
|
export function buildGhArgs(target: GhTarget): string[] {
|
|
77
81
|
if (target.kind === "issue") return ["issue", "view", target.url, "--comments"];
|
|
78
82
|
if (target.kind === "pr") return ["pr", "view", target.url, "--comments"];
|
|
83
|
+
if (target.kind === "run") return ["run", "view", target.runId, "--repo", target.slug];
|
|
79
84
|
return ["repo", "view", target.slug];
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -120,12 +125,14 @@ export function planGhRouting(params: GhRoutingParams, url: URL): GhTarget | nul
|
|
|
120
125
|
function ghCommandLabel(target: GhTarget): string {
|
|
121
126
|
if (target.kind === "issue") return "issue view --comments";
|
|
122
127
|
if (target.kind === "pr") return "pr view --comments";
|
|
128
|
+
if (target.kind === "run") return "run view";
|
|
123
129
|
return "repo view";
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
function ghSourceLine(target: GhTarget, ref: string): string {
|
|
127
133
|
if (target.kind === "issue") return `gh issue view ${ref} --comments`;
|
|
128
134
|
if (target.kind === "pr") return `gh pr view ${ref} --comments`;
|
|
135
|
+
if (target.kind === "run") return `gh run view ${target.runId} --repo ${target.slug}`;
|
|
129
136
|
return `gh repo view ${ref}`;
|
|
130
137
|
}
|
|
131
138
|
|
|
@@ -451,14 +458,14 @@ export default function fetchExtension(pi: ExtensionAPI) {
|
|
|
451
458
|
name: "fetch",
|
|
452
459
|
label: "Fetch URL",
|
|
453
460
|
description:
|
|
454
|
-
"Fetch a URL over HTTP(S). HTML is extracted to Markdown (readability + turndown). Binary content (images, PDFs, archives) is saved untouched to a temp file and only a path is returned. Text/Markdown/JSON over 32KB or 1000 lines is written to a temp file with a 60-line preview; smaller content is returned inline. Parsable downloads are capped at 1MB, binary at 50MB. GitHub issue/PR/repo URLs are served via the gh CLI when available (falls back to HTTP otherwise).",
|
|
461
|
+
"Fetch a URL over HTTP(S). HTML is extracted to Markdown (readability + turndown). Binary content (images, PDFs, archives) is saved untouched to a temp file and only a path is returned. Text/Markdown/JSON over 32KB or 1000 lines is written to a temp file with a 60-line preview; smaller content is returned inline. Parsable downloads are capped at 1MB, binary at 50MB. GitHub issue/PR/repo/actions-run URLs are served via the gh CLI when available (falls back to HTTP otherwise).",
|
|
455
462
|
promptSnippet: "Fetch the contents of a URL",
|
|
456
463
|
promptGuidelines: [
|
|
457
464
|
"Use fetch when the user provides a URL or asks to read web content.",
|
|
458
465
|
"Binary responses return a file path only — pass that path to a tool that can process the bytes; do not expect inline content.",
|
|
459
466
|
"When the body is written to a file, grep it or read with offset/limit. Converted Markdown is grep-able by heading (^#).",
|
|
460
467
|
"Pass raw=true to skip Markdown/JSON conversion and get the decoded body as-is (still subject to the size gate).",
|
|
461
|
-
"GitHub issue/PR/repo links are fetched through the gh CLI automatically; pass raw=true to force the rendered HTML page.",
|
|
468
|
+
"GitHub issue/PR/repo/actions-run links are fetched through the gh CLI automatically; pass raw=true to force the rendered HTML page.",
|
|
462
469
|
],
|
|
463
470
|
parameters: Type.Object({
|
|
464
471
|
url: Type.String({ description: "Absolute http(s) URL" }),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-quiver",
|
|
3
|
-
"version": "3.1.
|
|
4
|
-
"description": "Personal pack of Pi coding-agent extensions
|
|
3
|
+
"version": "3.1.2",
|
|
4
|
+
"description": "Personal pack of Pi coding-agent extensions: context-safe fetch, doc_to_md PDF/DOCX/PPTX-to-Markdown conversion, session naming, and a themed ASCII startup header.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"fetch",
|
|
21
21
|
"markdown",
|
|
22
22
|
"pdf",
|
|
23
|
+
"docx",
|
|
24
|
+
"pptx",
|
|
23
25
|
"cli"
|
|
24
26
|
],
|
|
25
27
|
"engines": {
|
|
@@ -48,7 +50,12 @@
|
|
|
48
50
|
"./doc_to_md.ts",
|
|
49
51
|
"./session-name.ts",
|
|
50
52
|
"./sword-header.ts"
|
|
51
|
-
]
|
|
53
|
+
],
|
|
54
|
+
"image": "https://raw.githubusercontent.com/jjuraszek/pi-quiver/main/pi-quiver.png"
|
|
55
|
+
},
|
|
56
|
+
"funding": {
|
|
57
|
+
"type": "buymeacoffee",
|
|
58
|
+
"url": "https://buymeacoffee.com/jjurasszek"
|
|
52
59
|
},
|
|
53
60
|
"peerDependencies": {
|
|
54
61
|
"@earendil-works/pi-ai": "*",
|