pi-quiver 6.2.0 → 6.3.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 +4 -0
- package/README.md +1 -1
- package/extensions/session-name.ts +40 -31
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@ 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
|
+
## v6.3.0 - 2026-09-16
|
|
12
|
+
|
|
13
|
+
- `session-name` Herdr sink claims any numeric tab label (optionally behind one `* `) at any position instead of only the label equal to the tab's live position, so tabs reordered before the first auto-name - or after a `/new` restore - get named; the claim resets at every `session_start` (`/new`, resume, fork behave like a fresh process) and a tab that reverts to a bare number is re-claimed on the next turn. The extension never writes a digits-only label (`1234` -> `#1234`) and the naming prompt asks for `PR 1234` / `issue 123` / `ticket ABC-123` over a bare ID.
|
|
14
|
+
|
|
11
15
|
## v6.2.0 - 2026-09-15
|
|
12
16
|
|
|
13
17
|
- `slack_thread` flattens Block Kit blocks in its default rendering (blocks joined by ` / `, `[<type>]` for unknown types) and gains an opt-in `raw: true` mode returning thread messages as a pure JSON array for `slack_update` round-trips; both modes go through the existing size gate. (#21)
|
package/README.md
CHANGED
|
@@ -200,7 +200,7 @@ survive untouched.
|
|
|
200
200
|
|
|
201
201
|
`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.
|
|
202
202
|
|
|
203
|
-
`herdrTab` (default `true`) mirrors the same curated label to the Herdr tab bar over Herdr's unix socket, independent of `ghosttyTab` - either sink can be toggled off without affecting the other.
|
|
203
|
+
`herdrTab` (default `true`) mirrors the same curated label to the Herdr tab bar over Herdr's unix socket, independent of `ghosttyTab` - either sink can be toggled off without affecting the other. A tab is claimable while its label is a bare number (`1`, `4`, ...) at any position - Herdr's vocabulary for "unnamed" - so reordering tabs before or after naming never matters; a tab you deliberately name `3` is taken over too. A non-numeric label is treated as a human's and is never overwritten; the check repeats every turn, so renumbering the tab by hand hands it back. Exactly one leading `* ` (herdr-ntfy-notify's armed marker) is not a human rename - it is preserved across renames and the shutdown restore, and removing it keeps the claim. Every session claims afresh: on each shutdown (quit, reload, `/new`, resume, fork) a claimed tab is restored to its then-current position number, and the successor session in the pane claims it again on its first name. The label the extension writes is never digits-only - a bare `1234` becomes `#1234`, and the naming prompt asks the model for `PR 1234` / `issue 123` / `ticket ABC-123` instead of a bare ID. It only ever runs in TUI mode, on an attached TTY, under Herdr (`HERDR_ENV`/`HERDR_TAB_ID`/`HERDR_SOCKET_PATH` all set) - `pi -p`/json/rpc runs and background subagents never touch the tab. Caveat: the restored number is internally a custom name (Herdr has no clear-to-auto API), so that tab keeps a number-looking label but stops renumbering on later tab closes/reorders.
|
|
204
204
|
|
|
205
205
|
`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. The `anthropic-beta` header is discovered at request time by probing pi's own request assembly (no network), so conditional betas pi adds - e.g. `server-side-fallback-2026-07-01` for models with server-side fallback - are preserved alongside `fast-mode-2026-02-01`. If the probe fails, the header falls back to the static OAuth-identity + fast-mode list. 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.
|
|
206
206
|
|
|
@@ -173,12 +173,15 @@ export function isGhosttyActive(
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
export function toTabLabel(name: string, maxWords = 4): string {
|
|
176
|
-
|
|
176
|
+
const label = name
|
|
177
177
|
.replace(/[\u0000-\u001f\u007f]/g, " ")
|
|
178
178
|
.split(/\s+/)
|
|
179
179
|
.filter(Boolean)
|
|
180
180
|
.slice(0, maxWords)
|
|
181
181
|
.join(" ");
|
|
182
|
+
// A bare number is Herdr's "unnamed tab" vocabulary; our own label must
|
|
183
|
+
// never look like one or a successor session would treat it as claimable.
|
|
184
|
+
return /^\d+$/.test(label) ? `#${label}` : label;
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
/**
|
|
@@ -338,6 +341,7 @@ export function buildNamingPrompt(conversation: string, opts: PromptOptions = {}
|
|
|
338
341
|
"- Describe the actual task, NOT the tool/skill/command used to start it.",
|
|
339
342
|
"- Lead with an action verb (e.g. refine, fix, add, rework).",
|
|
340
343
|
"- Preserve ticket/issue IDs (e.g. ABC-123, PROJ-42, #99) verbatim.",
|
|
344
|
+
"- If the whole TAB would be just an ID, say what it is: PR 1234, issue 123, ticket ABC-123. Never reply with digits only.",
|
|
341
345
|
"- No quotes, no trailing punctuation, plain ASCII.",
|
|
342
346
|
...rules.map((r) => `- ${r}`),
|
|
343
347
|
"Example -> SESSION: Refine Linear Ticket ABC-123 / TAB: Refine ABC-123",
|
|
@@ -496,12 +500,15 @@ export function installSessionName(pi: ExtensionAPI, generate: NameGenerator = g
|
|
|
496
500
|
if (currentTabLabel) renameGhosttyTab(currentTabLabel, cfg.ghosttyTab);
|
|
497
501
|
};
|
|
498
502
|
|
|
499
|
-
// Herdr sink.
|
|
500
|
-
//
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
//
|
|
504
|
-
//
|
|
503
|
+
// Herdr sink. A tab is claimable while its label is a bare number - Herdr's
|
|
504
|
+
// vocabulary for "unnamed", and the only label this extension or
|
|
505
|
+
// herdr-ntfy-notify ever write back - regardless of position: Herdr stores a
|
|
506
|
+
// renamed number as a permanent custom label that reorders never update. A
|
|
507
|
+
// non-numeric label is a human's and is never overwritten; it is re-checked
|
|
508
|
+
// each turn so a tab renumbered by hand becomes claimable again. Exactly one
|
|
509
|
+
// leading "* " is herdr-ntfy-notify's armed marker, ignored for ownership and
|
|
510
|
+
// carried through on every write. All syncs serialize on one chain so
|
|
511
|
+
// overlapping hooks never interleave a read with a rename.
|
|
505
512
|
// Used both for turn_start syncs (bounds a wedged-but-accepting Herdr so it
|
|
506
513
|
// can't stall the turn) and for the session_end restore.
|
|
507
514
|
const HERDR_TIMEOUT_MS = 500;
|
|
@@ -514,44 +521,42 @@ export function installSessionName(pi: ExtensionAPI, generate: NameGenerator = g
|
|
|
514
521
|
};
|
|
515
522
|
|
|
516
523
|
const ARMED_PREFIX = "* ";
|
|
524
|
+
const NUMERIC = /^\d+$/;
|
|
517
525
|
const matchOwned = (live: string, expected: string): { owned: boolean; armed: boolean } => {
|
|
518
526
|
if (live === expected) return { owned: true, armed: false };
|
|
519
527
|
if (live === ARMED_PREFIX + expected) return { owned: true, armed: true };
|
|
520
528
|
return { owned: false, armed: false };
|
|
521
529
|
};
|
|
530
|
+
const matchClaimable = (live: string): { claimable: boolean; armed: boolean } => {
|
|
531
|
+
if (NUMERIC.test(live)) return { claimable: true, armed: false };
|
|
532
|
+
if (live.startsWith(ARMED_PREFIX) && NUMERIC.test(live.slice(ARMED_PREFIX.length))) return { claimable: true, armed: true };
|
|
533
|
+
return { claimable: false, armed: false };
|
|
534
|
+
};
|
|
522
535
|
|
|
523
536
|
const syncHerdrTab = (cfg: Config, label: string | null, mode: Mode | undefined): Promise<void> => {
|
|
524
537
|
const run = async (): Promise<void> => {
|
|
525
538
|
if (!cfg.herdrTab || mode !== "tui" || !label) return;
|
|
526
|
-
if (herdrClaim === "backed-off") return;
|
|
527
539
|
if (!isHerdrActive()) return;
|
|
528
540
|
const sock = process.env.HERDR_SOCKET_PATH as string;
|
|
529
541
|
const tabId = process.env.HERDR_TAB_ID as string;
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
if (!owned.owned) {
|
|
538
|
-
herdrClaim = "backed-off"; // human (or crashed predecessor) owns it
|
|
542
|
+
const live = await getTab(sock, tabId, HERDR_TIMEOUT_MS);
|
|
543
|
+
if (!live) return; // transient or stale tab id; a failed read is never a human rename
|
|
544
|
+
if (typeof herdrClaim === "object" && herdrClaim !== null) {
|
|
545
|
+
const owned = matchOwned(live.label, herdrClaim.lastWritten);
|
|
546
|
+
if (owned.owned) {
|
|
547
|
+
if (label === herdrClaim.lastWritten) return;
|
|
548
|
+
if (await renameTab(sock, tabId, (owned.armed ? ARMED_PREFIX : "") + label, HERDR_TIMEOUT_MS)) herdrClaim.lastWritten = label;
|
|
539
549
|
return;
|
|
540
550
|
}
|
|
541
|
-
if (await renameTab(sock, tabId, (owned.armed ? ARMED_PREFIX : "") + label, HERDR_TIMEOUT_MS)) {
|
|
542
|
-
herdrClaim = { lastWritten: label };
|
|
543
|
-
}
|
|
544
|
-
return;
|
|
545
551
|
}
|
|
546
|
-
const
|
|
547
|
-
if (!
|
|
548
|
-
|
|
549
|
-
if (!owned.owned) {
|
|
550
|
-
herdrClaim = "backed-off";
|
|
552
|
+
const claim = matchClaimable(live.label);
|
|
553
|
+
if (!claim.claimable) {
|
|
554
|
+
herdrClaim = "backed-off"; // human owns it until the label is numeric again
|
|
551
555
|
return;
|
|
552
556
|
}
|
|
553
|
-
if (
|
|
554
|
-
|
|
557
|
+
if (await renameTab(sock, tabId, (claim.armed ? ARMED_PREFIX : "") + label, HERDR_TIMEOUT_MS)) {
|
|
558
|
+
herdrClaim = { lastWritten: label };
|
|
559
|
+
}
|
|
555
560
|
};
|
|
556
561
|
herdrChain = herdrChain.then(run, run);
|
|
557
562
|
return herdrChain;
|
|
@@ -595,6 +600,10 @@ export function installSessionName(pi: ExtensionAPI, generate: NameGenerator = g
|
|
|
595
600
|
});
|
|
596
601
|
|
|
597
602
|
pi.on("session_start", async (_event, ctx) => {
|
|
603
|
+
// Every session claims afresh, exactly like a fresh process: /new, resume,
|
|
604
|
+
// and fork each tear the previous session down (restore included) first.
|
|
605
|
+
// Before loadConfig so `enabled: false, herdrTab: true` resets too.
|
|
606
|
+
herdrClaim = null;
|
|
598
607
|
const cfg = loadConfig(ctx);
|
|
599
608
|
if (!cfg.enabled) return; // off by default; opt in via settings.json
|
|
600
609
|
const current = pi.getSessionName();
|
|
@@ -646,9 +655,9 @@ export function installSessionName(pi: ExtensionAPI, generate: NameGenerator = g
|
|
|
646
655
|
});
|
|
647
656
|
|
|
648
657
|
// Restore the numeric label on every teardown reason (quit/reload/new/
|
|
649
|
-
// resume/fork)
|
|
650
|
-
//
|
|
651
|
-
//
|
|
658
|
+
// resume/fork) so the pane reads as unnamed again. The restored label is
|
|
659
|
+
// internally a custom name - Herdr has no clear-to-auto API - so it won't
|
|
660
|
+
// renumber on reorders; the successor claims any numeric label regardless.
|
|
652
661
|
pi.on("session_shutdown", async () => {
|
|
653
662
|
await restoreHerdrTab();
|
|
654
663
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-quiver",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Personal pack of Pi coding-agent extensions: context-safe fetch, doc_to_md PDF/DOCX/PPTX-to-Markdown conversion, session naming, a themed ASCII startup header, Opus 4.8 fast mode, and a provider-stall watchdog.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|