@diegopetrucci/pi-extensions 0.1.48 → 0.1.49
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.
|
@@ -4,7 +4,7 @@ A pi GitHub research scout inspired by `pi-librarian`, with a local checkout cac
|
|
|
4
4
|
|
|
5
5
|
When the `librarian` tool runs, it can cache/reuse repository checkouts locally. Use `/librarian-cache off` to force GitHub API/search and temporary fetched files only, or `/librarian-cache on` to re-enable cached local checkouts.
|
|
6
6
|
|
|
7
|
-
The internal librarian subagent uses a
|
|
7
|
+
The internal librarian subagent uses a fast auto-selected model by default, requests `low` thinking, and prompts its scout to batch independent GitHub probes in parallel. Use `/librarian-config` to set a persistent internal model or thinking-level preference.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -39,7 +39,7 @@ Then reload pi:
|
|
|
39
39
|
- Uses `gh` for GitHub search/API access
|
|
40
40
|
- Uses cached local checkouts only when enabled
|
|
41
41
|
- Toggle cache behavior for future calls with `/librarian-cache on | off | toggle | status`
|
|
42
|
-
- Configure internal subagent defaults with `/librarian-config status | model <provider/model|auto
|
|
42
|
+
- Configure internal subagent defaults with `/librarian-config status | model <provider/model|auto> | thinking <off|minimal|low|medium|high|xhigh|auto> | clear [all|model|thinking]`
|
|
43
43
|
- Cached repos are removed lazily after 7 days without use
|
|
44
44
|
|
|
45
45
|
## Commands
|
|
@@ -54,9 +54,8 @@ Then reload pi:
|
|
|
54
54
|
```text
|
|
55
55
|
/librarian-config status
|
|
56
56
|
/librarian-config model auto
|
|
57
|
-
/librarian-config model current
|
|
58
57
|
/librarian-config model anthropic/claude-haiku-4-5:medium
|
|
59
|
-
/librarian-config thinking
|
|
58
|
+
/librarian-config thinking low
|
|
60
59
|
/librarian-config clear model
|
|
61
60
|
```
|
|
62
61
|
|
|
@@ -33,9 +33,20 @@ type CacheMode = "disabled" | "enabled";
|
|
|
33
33
|
type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
34
34
|
|
|
35
35
|
const DEFAULT_CACHE_MODE: CacheMode = "disabled";
|
|
36
|
-
const DEFAULT_THINKING_LEVEL: ThinkingLevel = "
|
|
36
|
+
const DEFAULT_THINKING_LEVEL: ThinkingLevel = "low";
|
|
37
37
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
38
38
|
|
|
39
|
+
const PREFERRED_FAST_MODEL_PATTERNS = [
|
|
40
|
+
/\bgpt[-_. ]?5\.5(?:[-_. ].*)?\b(?:mini|nano|fast|lite)\b/,
|
|
41
|
+
/\bgpt[-_. ]?5(?:[-_. ].*)?\b(?:mini|nano|fast|lite)\b/,
|
|
42
|
+
/\bgpt[-_. ]?4(?:\.1|o)?(?:[-_. ].*)?\b(?:mini|nano)\b/,
|
|
43
|
+
/\bgemini\b.*\b(?:flash|flash-lite|lite)\b/,
|
|
44
|
+
/\bclaude\b.*\bhaiku\b/,
|
|
45
|
+
/\b(?:mistral|codestral)\b.*\b(?:small|mini|lite)\b/,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const HEAVY_MODEL_PATTERN = /\b(?:opus|pro|ultra|max)\b/;
|
|
49
|
+
|
|
39
50
|
type ToolCall = {
|
|
40
51
|
id: string;
|
|
41
52
|
name: string;
|
|
@@ -106,7 +117,7 @@ const LibrarianParams = Type.Object({
|
|
|
106
117
|
),
|
|
107
118
|
model: Type.Optional(
|
|
108
119
|
Type.String({
|
|
109
|
-
description: "Optional model override for the internal librarian subagent. Use provider/model
|
|
120
|
+
description: "Optional model override for the internal librarian subagent. Use provider/model or auto.",
|
|
110
121
|
}),
|
|
111
122
|
),
|
|
112
123
|
thinkingLevel: Type.Optional(
|
|
@@ -308,7 +319,8 @@ function parseThinkingLevel(value: unknown): ThinkingLevel | undefined {
|
|
|
308
319
|
function normalizeModelPreference(value: unknown): string | undefined {
|
|
309
320
|
if (typeof value !== "string") return undefined;
|
|
310
321
|
const trimmed = value.trim();
|
|
311
|
-
|
|
322
|
+
const normalized = trimmed.toLowerCase();
|
|
323
|
+
if (!trimmed || normalized === "auto" || normalized === "current") return undefined;
|
|
312
324
|
return trimmed;
|
|
313
325
|
}
|
|
314
326
|
|
|
@@ -317,7 +329,11 @@ function parseModelPreference(value: unknown): { model?: string; thinkingLevel?:
|
|
|
317
329
|
if (!model) return {};
|
|
318
330
|
const match = model.match(/^(.*):(off|minimal|low|medium|high|xhigh)$/i);
|
|
319
331
|
if (!match?.[1]) return { model };
|
|
320
|
-
|
|
332
|
+
const baseModel = match[1].trim();
|
|
333
|
+
if (!baseModel || baseModel.toLowerCase() === "auto" || baseModel.toLowerCase() === "current") {
|
|
334
|
+
return { thinkingLevel: parseThinkingLevel(match[2]) };
|
|
335
|
+
}
|
|
336
|
+
return { model: baseModel, thinkingLevel: parseThinkingLevel(match[2]) };
|
|
321
337
|
}
|
|
322
338
|
|
|
323
339
|
async function readLibrarianPreferences(): Promise<LibrarianPreferences> {
|
|
@@ -415,11 +431,25 @@ function rankLibrarianModel(model: any): number {
|
|
|
415
431
|
let score = modelCostScore(model) * 1_000_000;
|
|
416
432
|
if (model.reasoning) score += 50;
|
|
417
433
|
if (/\b(?:mini|nano|haiku|flash|lite|small|fast|instant)\b/.test(text)) score -= 10;
|
|
418
|
-
if (
|
|
434
|
+
if (HEAVY_MODEL_PATTERN.test(text)) score += 1_000;
|
|
419
435
|
if ((model.contextWindow ?? 0) < 32_000) score += 100;
|
|
420
436
|
return score;
|
|
421
437
|
}
|
|
422
438
|
|
|
439
|
+
function isPreferredFastLibrarianModel(model: any): boolean {
|
|
440
|
+
const text = `${model.provider ?? ""} ${model.id ?? ""} ${model.name ?? ""}`.toLowerCase();
|
|
441
|
+
if (HEAVY_MODEL_PATTERN.test(text)) return false;
|
|
442
|
+
return PREFERRED_FAST_MODEL_PATTERNS.some((pattern) => pattern.test(text));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function rankPreferredFastLibrarianModel(model: any): number {
|
|
446
|
+
const text = `${model.provider ?? ""} ${model.id ?? ""} ${model.name ?? ""}`.toLowerCase();
|
|
447
|
+
let score = rankLibrarianModel(model);
|
|
448
|
+
const patternIndex = PREFERRED_FAST_MODEL_PATTERNS.findIndex((pattern) => pattern.test(text));
|
|
449
|
+
if (patternIndex >= 0) score += patternIndex * 100;
|
|
450
|
+
return score;
|
|
451
|
+
}
|
|
452
|
+
|
|
423
453
|
async function findAvailableModel(
|
|
424
454
|
ctx: { model?: any; modelRegistry: { getAvailable(): any[] | Promise<any[]> } },
|
|
425
455
|
modelPreference: string,
|
|
@@ -453,21 +483,6 @@ async function selectLibrarianModel(
|
|
|
453
483
|
thinkingLevel: ThinkingLevel,
|
|
454
484
|
): Promise<{ model: any; details: LibrarianModelDetails }> {
|
|
455
485
|
const normalized = modelPreference?.trim();
|
|
456
|
-
if (normalized?.toLowerCase() === "current") {
|
|
457
|
-
if (!ctx.model) throw new Error("Librarian model=current needs an active pi model, but ctx.model is unavailable.");
|
|
458
|
-
return {
|
|
459
|
-
model: ctx.model,
|
|
460
|
-
details: {
|
|
461
|
-
modelRef: modelRef(ctx.model),
|
|
462
|
-
provider: ctx.model.provider,
|
|
463
|
-
modelId: ctx.model.id,
|
|
464
|
-
thinkingLevel,
|
|
465
|
-
autoSelected: false,
|
|
466
|
-
selectionReason: "Using the caller's current model because librarian model=current is configured.",
|
|
467
|
-
},
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
486
|
if (normalized) {
|
|
472
487
|
const matched = await findAvailableModel(ctx, normalized);
|
|
473
488
|
if (matched) {
|
|
@@ -486,15 +501,18 @@ async function selectLibrarianModel(
|
|
|
486
501
|
}
|
|
487
502
|
|
|
488
503
|
const available = await ctx.modelRegistry.getAvailable();
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
504
|
+
const preferred = available.filter(isPreferredFastLibrarianModel);
|
|
505
|
+
const winner = preferred.length > 0
|
|
506
|
+
? [...preferred].sort((a, b) => rankPreferredFastLibrarianModel(a) - rankPreferredFastLibrarianModel(b))[0]
|
|
507
|
+
: [...available].sort((a, b) => rankLibrarianModel(a) - rankLibrarianModel(b))[0];
|
|
493
508
|
if (!winner) {
|
|
494
509
|
throw new Error("No authenticated models are available for Librarian. Log in or configure an API key first.");
|
|
495
510
|
}
|
|
496
511
|
|
|
497
512
|
const fallbackText = normalized ? ` Configured model ${normalized} was unavailable, so Librarian fell back to auto-selection.` : "";
|
|
513
|
+
const selectionReason = preferred.length > 0
|
|
514
|
+
? `Selected a preferred fast Librarian model.${fallbackText}`
|
|
515
|
+
: `Selected the cheapest available model because no preferred fast Librarian model was available.${fallbackText}`;
|
|
498
516
|
return {
|
|
499
517
|
model: winner,
|
|
500
518
|
details: {
|
|
@@ -503,7 +521,7 @@ async function selectLibrarianModel(
|
|
|
503
521
|
modelId: winner.id,
|
|
504
522
|
thinkingLevel,
|
|
505
523
|
autoSelected: true,
|
|
506
|
-
selectionReason
|
|
524
|
+
selectionReason,
|
|
507
525
|
},
|
|
508
526
|
};
|
|
509
527
|
}
|
|
@@ -621,7 +639,7 @@ function buildSystemPrompt(options: {
|
|
|
621
639
|
? `\nLocal checkout cache is ENABLED for this call.\n- Cache root: ${options.cacheRoot}\n- Use checkout path pattern: ${options.cacheRoot}/github.com/<owner>/<repo>\n- Reuse an existing checkout when it has a .git directory. Fetch/prune before relying on it: git -C "$DIR" fetch --all --prune --tags --quiet\n- If missing, clone with gh repo clone "$REPO" "$DIR" (or git clone https://github.com/$REPO.git "$DIR").\n- If a ref/branch/SHA is requested, fetch it and check it out locally before citing files from that ref.\n- After using a checkout, update its cache marker: touch "$DIR/${CACHE_MARKER_FILE}"\n- Prefer local rg/read inside cached checkouts once a repo is cloned, and cite absolute cached paths with line ranges.\n- Clone only repositories that are relevant to the query; do not bulk-clone broad owner/org scopes unless necessary.`
|
|
622
640
|
: `\nLocal checkout cache is DISABLED for this call.\n- Do not clone repositories.\n- Use gh search/API/tree/contents calls and cache only necessary proof files under ${options.workspace}/repos/<owner>/<repo>/<path>.`;
|
|
623
641
|
|
|
624
|
-
return `You are Librarian, an evidence-first GitHub code scout running inside pi.\n\nUse only the available bash/read tools. Use gh, jq, rg, find/fd, ls, stat, mkdir, base64, and nl -ba for GitHub reconnaissance and numbered evidence. Use read for focused local file inspection.\n\nWorkspace: ${options.workspace}\nDefault gh search limit: ${options.maxSearchResults}\nTurn budget: ${MAX_TURNS} turns total, including your final answer. Stop searching once you have enough evidence.\n${cacheSection}\n\nNon-negotiable constraints:\n- Never treat gh search snippets as proof by themselves. Use fetched files or local checkouts for code-content claims.\n- Keep temporary workspace writes under ${options.workspace}/repos unless local checkout cache is enabled, in which case writes under the cache root are also allowed.
|
|
642
|
+
return `You are Librarian, an evidence-first GitHub code scout running inside pi.\n\nUse only the available bash/read tools. Use gh, jq, rg, find/fd, ls, stat, mkdir, base64, and nl -ba for GitHub reconnaissance and numbered evidence. Use read for focused local file inspection.\n\nWorkspace: ${options.workspace}\nDefault gh search limit: ${options.maxSearchResults}\nTurn budget: ${MAX_TURNS} turns total, including your final answer. Stop searching once you have enough evidence.\n${cacheSection}\n\nPerformance guidance:\n- Prefer fast, parallel exploration. When probes are independent, issue multiple separate tool calls in the same assistant turn instead of waiting for each result one-by-one.\n- Aim for 4-8 independent bash/read calls in an exploration turn when useful: parallel gh searches, tree probes, contents fetches, and targeted file reads.\n- Keep dependent work sequential, and avoid concurrent writes to the same file, checkout, or cache directory.\n\nNon-negotiable constraints:\n- Never treat gh search snippets as proof by themselves. Use fetched files or local checkouts for code-content claims.\n- Keep temporary workspace writes under ${options.workspace}/repos unless local checkout cache is enabled, in which case writes under the cache root are also allowed.
|
|
625
643
|
- A runtime guard blocks destructive shell commands, credential/environment inspection, and reads outside the workspace/cache.\n- Never paste whole files. Use short snippets only when they clarify the evidence.\n- If evidence is partial or access fails (404/403), state the limitation clearly.\n- Do not present anything as fact unless it appeared in tool output or in a file you read.\n\nRecommended search flow:\n1. If symbols/text are known, start with gh search code and the provided repo/owner filters.\n2. If a repo is known but paths are unclear, resolve the default branch and inspect the git tree or contents API.\n3. Fetch or clone only the files/repos required to prove the answer.\n4. Use rg/read/nl -ba locally to produce stable path and line evidence.\n\nUseful gh patterns:\n- gh repo view "$REPO" --json defaultBranchRef --jq '.defaultBranchRef.name'\n- gh search code '<terms>' --json path,repository,sha,url,textMatches --limit ${options.maxSearchResults}\n- gh api "repos/$REPO/git/trees/$REF?recursive=1" > tree.json\n- gh api "repos/$REPO/contents/$FILE?ref=$REF" --jq .content | tr -d '\\n' | base64 --decode > "repos/$REPO/$FILE"\n- rg -n '<pattern>' '<local path>'\n- nl -ba '<local file>' | sed -n '10,30p'\n\nOutput format, exact order:\n## Summary\n1-3 concise sentences.\n## Locations\n- \`path\` or \`path:lineStart-lineEnd\` — what is here and why it matters; include GitHub URL when useful. If nothing relevant is found, write \`- (none)\`.\n## Evidence\n- \`path\` or \`path:lineStart-lineEnd\` — what this proves. Include concise snippets only if useful.\n## Searched\nOnly include when incomplete/not found or when the search path matters. List queries, filters, and probes used.\n## Next steps\nOptional: 1-3 narrow follow-up checks for remaining ambiguity.`;
|
|
626
644
|
}
|
|
627
645
|
|
|
@@ -767,16 +785,20 @@ export default function librarianExtension(pi: ExtensionAPI) {
|
|
|
767
785
|
if (action === "model") {
|
|
768
786
|
const value = rest.join(" ").trim();
|
|
769
787
|
if (!value) {
|
|
770
|
-
notifyCommand(ctx, "Usage: /librarian-config model <provider/model|auto
|
|
788
|
+
notifyCommand(ctx, "Usage: /librarian-config model <provider/model|auto>", "warning");
|
|
771
789
|
return;
|
|
772
790
|
}
|
|
773
791
|
const normalized = value.toLowerCase();
|
|
792
|
+
if (normalized === "current" || normalized.startsWith("current:")) {
|
|
793
|
+
notifyCommand(ctx, "Librarian model=current is no longer supported. Use /librarian-config model auto or choose an explicit provider/model.", "warning");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
774
796
|
const parsedModel = parseModelPreference(value);
|
|
775
797
|
const next = normalized === "auto" || normalized === "clear" || normalized === "default"
|
|
776
798
|
? { ...currentPreferences(), model: undefined }
|
|
777
799
|
: {
|
|
778
800
|
...currentPreferences(),
|
|
779
|
-
model:
|
|
801
|
+
model: parsedModel.model,
|
|
780
802
|
thinkingLevel: parsedModel.thinkingLevel ?? thinkingPreference,
|
|
781
803
|
};
|
|
782
804
|
const warning = await savePreferences(next);
|
|
@@ -817,7 +839,7 @@ export default function librarianExtension(pi: ExtensionAPI) {
|
|
|
817
839
|
return;
|
|
818
840
|
}
|
|
819
841
|
|
|
820
|
-
notifyCommand(ctx, "Usage: /librarian-config status | model <provider/model|auto
|
|
842
|
+
notifyCommand(ctx, "Usage: /librarian-config status | model <provider/model|auto> | thinking <off|minimal|low|medium|high|xhigh|auto> | clear [all|model|thinking]", "warning");
|
|
821
843
|
},
|
|
822
844
|
});
|
|
823
845
|
|
|
@@ -896,7 +918,7 @@ export default function librarianExtension(pi: ExtensionAPI) {
|
|
|
896
918
|
description:
|
|
897
919
|
"GitHub research scout for coding and personal-assistant tasks. Use when the answer likely lives in GitHub repos, exact repo/path locations are unknown, or you'd otherwise do exploratory gh search/tree probes plus local rg/read inspection. Librarian uses an optional 7-day local checkout cache that is disabled by default; toggle it with /librarian-cache. Configure its internal subagent defaults with /librarian-config.",
|
|
898
920
|
promptSnippet:
|
|
899
|
-
"Research GitHub repositories with evidence-first path and line citations; local checkout cache is disabled by default and user-toggleable with /librarian-cache. Internal subagent defaults are user-configurable with /librarian-config and default to
|
|
921
|
+
"Research GitHub repositories with evidence-first path and line citations; local checkout cache is disabled by default and user-toggleable with /librarian-cache. Internal subagent defaults are user-configurable with /librarian-config and default to low thinking.",
|
|
900
922
|
promptGuidelines: [
|
|
901
923
|
"Use librarian when the answer likely requires exploratory GitHub repository search or line-cited evidence from external repos.",
|
|
902
924
|
"Do not use librarian for files already present in the current workspace unless the user asks for external GitHub research.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
4
4
|
"description": "A collection of pi extensions and skills for annotation UIs, context management, workflow audits, review-comment triage, notifications, brrr push alerts, safety guards, GitHub research, repo-local knowledge, todos, tool rendering, model/provider helpers, and Xiaohei-style article illustrations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|