@aeondave/pi-skill-optimizer 3.0.0 → 3.0.1
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 +7 -0
- package/README.md +8 -1
- package/docs/CONFIGURATION.md +6 -0
- package/package.json +2 -2
- package/src/index.ts +41 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes are documented here.
|
|
4
4
|
|
|
5
|
+
## 3.0.1 - 2026-09-30
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Keep Pi 0.99's initially inactive `deferred` and `codemode` tools discoverable through the optimizer's `tool_search`. Activate only matching search results, retain them across later replanning, and leave initially unavailable direct or hidden tools outside the catalog.
|
|
10
|
+
- Reject blank tool searches without activating fallback results. Document how to disable Pi's replaceable built-in `tool-search` at the same scope as the optimizer, avoiding its startup conflict warning.
|
|
11
|
+
|
|
5
12
|
## 3.0.0 - 2026-09-28
|
|
6
13
|
|
|
7
14
|
### Scoped distribution - 2026-09-29
|
package/README.md
CHANGED
|
@@ -26,12 +26,19 @@ Restart Pi, then check that it loaded:
|
|
|
26
26
|
/skill-optimizer
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
Pi 0.99.0+ also includes a built-in `tool-search` extension. This optimizer provides
|
|
30
|
+
its own `tool_search`, so disable the built-in under **Built-in extensions** in
|
|
31
|
+
`pi config` to avoid the startup conflict warning. Use the global setting for a
|
|
32
|
+
personal install, or `pi config -l` for a project-only install. The optimizer
|
|
33
|
+
continues to find registered `deferred` and `codemode` tools. See the
|
|
34
|
+
[tool-search configuration](docs/CONFIGURATION.md#replacing-the-built-in-tool-search).
|
|
35
|
+
|
|
29
36
|
That's enough to start. Optionally run `/skill-optimizer init` to generate routing hints for your library. **Init uses your current Pi model and may incur cost**; normal discovery does not.
|
|
30
37
|
|
|
31
38
|
| Need | Command |
|
|
32
39
|
| --- | --- |
|
|
33
40
|
| Install only in this project | `pi install -l npm:@aeondave/pi-skill-optimizer` |
|
|
34
|
-
| Pin the current release | `pi install npm:@aeondave/pi-skill-optimizer@3.0.
|
|
41
|
+
| Pin the current release | `pi install npm:@aeondave/pi-skill-optimizer@3.0.1` |
|
|
35
42
|
| Update an unpinned install | `pi update npm:@aeondave/pi-skill-optimizer` |
|
|
36
43
|
|
|
37
44
|
Restart after updating. Pinned installs stay pinned until you explicitly install another version.
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -14,6 +14,12 @@ Exact skill loading accepts only registered names. Resource and location resolut
|
|
|
14
14
|
|
|
15
15
|
`excludeSkills` removes names from discovery and search, not from explicit exact loading. It is a routing preference, not an access-control boundary. The three resolver tools return their content without another output-reduction pass, so full skill instructions and recovered originals remain readable.
|
|
16
16
|
|
|
17
|
+
### Replacing the built-in tool search
|
|
18
|
+
|
|
19
|
+
The extension registers `tool_search` itself, so it replaces Pi's built-in `builtin:tool-search` extension (the built-in is marked replaceable and the tool name decides). While both are in the load set, Pi prints a startup warning that the built-in was not loaded. The optimizer indexes eligible registered tools, including ones it deferred from Pi's active set and Pi-registered `deferred` or `codemode` tools. Matching inactive results activate additively; unrelated fallback results remain inactive. Pi's built-in MCP extension can still activate `tool_search` by name when a deferred server connects.
|
|
20
|
+
|
|
21
|
+
To silence the warning, disable the built-in where the optimizer runs: toggle `tool-search` off under Built-in extensions in `pi config`, or set `"extensions": ["-builtin:tool-search"]` in the applicable `settings.json`. A personal install needs the global setting in `~/.pi/agent/settings.json`; a project-only install can use `.pi/settings.json`. For development with `pi -e ./src/index.ts`, set the toggle locally or globally. Re-enable the built-in if you remove the optimizer.
|
|
22
|
+
|
|
17
23
|
## Commands
|
|
18
24
|
|
|
19
25
|
| Command | Purpose |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeondave/pi-skill-optimizer",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Less repeated context for Pi: local skill routing, on-demand tools, and recoverable output reduction.",
|
|
@@ -80,6 +80,6 @@
|
|
|
80
80
|
"extensions": [
|
|
81
81
|
"./src/index.ts"
|
|
82
82
|
],
|
|
83
|
-
"image": "https://raw.githubusercontent.com/AeonDave/pi-skill-optimizer/v3.0.
|
|
83
|
+
"image": "https://raw.githubusercontent.com/AeonDave/pi-skill-optimizer/v3.0.1/assets/banner.png"
|
|
84
84
|
}
|
|
85
85
|
}
|
package/src/index.ts
CHANGED
|
@@ -112,6 +112,14 @@ const MAX_SEEN_EVIDENCE = 4_096;
|
|
|
112
112
|
const MAX_HISTORY_ARTIFACTS = 128;
|
|
113
113
|
const PROTECTED_RUNTIME_TOOLS = ["skill_search", "tool_search", "retrieve_output"] as const;
|
|
114
114
|
|
|
115
|
+
function isHostSearchableTool(tool: object): boolean {
|
|
116
|
+
return "exposure" in tool && (tool.exposure === "deferred" || tool.exposure === "codemode");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isHostHiddenTool(tool: object): boolean {
|
|
120
|
+
return "exposure" in tool && tool.exposure === "hidden";
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
type ConfigRecord = ReturnType<typeof getConfig>;
|
|
116
124
|
|
|
117
125
|
const EMPTY_STORED_PROFILE: StoredProfile = {
|
|
@@ -565,12 +573,20 @@ export default function skillOptimizer(pi: ExtensionAPI) {
|
|
|
565
573
|
};
|
|
566
574
|
|
|
567
575
|
const rebuildToolCatalog = (state: RuntimeSnapshot): ToolDiscoveryCatalog => {
|
|
576
|
+
const allTools = pi.getAllTools();
|
|
568
577
|
permittedToolNames ??= new Set([
|
|
569
578
|
...pi.getActiveTools().map((name) => name.toLowerCase()),
|
|
570
579
|
...PROTECTED_RUNTIME_TOOLS,
|
|
571
580
|
]);
|
|
572
|
-
const
|
|
573
|
-
|
|
581
|
+
for (const tool of allTools) {
|
|
582
|
+
if (!isHostHiddenTool(tool)) continue;
|
|
583
|
+
const name = tool.name.toLowerCase();
|
|
584
|
+
permittedToolNames.delete(name);
|
|
585
|
+
retainedToolNames.delete(name);
|
|
586
|
+
deactivatedToolNames.delete(name);
|
|
587
|
+
}
|
|
588
|
+
const discoverable = allTools.filter((tool) =>
|
|
589
|
+
!isHostHiddenTool(tool) && permittedToolNames?.has(tool.name.toLowerCase()),
|
|
574
590
|
);
|
|
575
591
|
toolCatalog = buildToolDiscoveryCatalog(discoverable, {
|
|
576
592
|
protect: [...PROTECTED_RUNTIME_TOOLS, ...state.config.alwaysTools],
|
|
@@ -578,13 +594,25 @@ export default function skillOptimizer(pi: ExtensionAPI) {
|
|
|
578
594
|
return toolCatalog;
|
|
579
595
|
};
|
|
580
596
|
|
|
597
|
+
const buildToolSearchCatalog = (state: RuntimeSnapshot): ToolDiscoveryCatalog => {
|
|
598
|
+
rebuildToolCatalog(state);
|
|
599
|
+
return buildToolDiscoveryCatalog(pi.getAllTools().filter((tool) =>
|
|
600
|
+
!isHostHiddenTool(tool)
|
|
601
|
+
&& (permittedToolNames?.has(tool.name.toLowerCase()) || isHostSearchableTool(tool)),
|
|
602
|
+
), {
|
|
603
|
+
protect: [...PROTECTED_RUNTIME_TOOLS, ...state.config.alwaysTools],
|
|
604
|
+
});
|
|
605
|
+
};
|
|
606
|
+
|
|
581
607
|
const activateAdditively = (names: readonly string[]): string[] => {
|
|
582
608
|
const requested = new Set(names.map((name) => name.toLowerCase()));
|
|
583
609
|
const current = new Set(pi.getActiveTools().map((name) => name.toLowerCase()));
|
|
584
610
|
const added: string[] = [];
|
|
585
611
|
for (const tool of pi.getAllTools()) {
|
|
586
|
-
if (
|
|
612
|
+
if (isHostHiddenTool(tool)) continue;
|
|
613
|
+
if (!permittedToolNames?.has(tool.name.toLowerCase()) && !isHostSearchableTool(tool)) continue;
|
|
587
614
|
if (!requested.has(tool.name.toLowerCase())) continue;
|
|
615
|
+
permittedToolNames?.add(tool.name.toLowerCase());
|
|
588
616
|
retainedToolNames.add(tool.name.toLowerCase());
|
|
589
617
|
if (current.has(tool.name.toLowerCase())) continue;
|
|
590
618
|
current.add(tool.name.toLowerCase());
|
|
@@ -592,8 +620,8 @@ export default function skillOptimizer(pi: ExtensionAPI) {
|
|
|
592
620
|
added.push(tool.name);
|
|
593
621
|
}
|
|
594
622
|
const ordered = pi.getAllTools()
|
|
595
|
-
.
|
|
596
|
-
.
|
|
623
|
+
.filter((tool) => !isHostHiddenTool(tool) && current.has(tool.name.toLowerCase()))
|
|
624
|
+
.map((tool) => tool.name);
|
|
597
625
|
pi.setActiveTools(ordered);
|
|
598
626
|
return added;
|
|
599
627
|
};
|
|
@@ -607,8 +635,8 @@ export default function skillOptimizer(pi: ExtensionAPI) {
|
|
|
607
635
|
for (const name of deactivatedToolNames) active.add(name);
|
|
608
636
|
pi.setActiveTools(
|
|
609
637
|
pi.getAllTools()
|
|
610
|
-
.
|
|
611
|
-
.
|
|
638
|
+
.filter((tool) => !isHostHiddenTool(tool) && active.has(tool.name.toLowerCase()))
|
|
639
|
+
.map((tool) => tool.name),
|
|
612
640
|
);
|
|
613
641
|
deactivatedToolNames.clear();
|
|
614
642
|
toolCatalog = undefined;
|
|
@@ -658,12 +686,13 @@ export default function skillOptimizer(pi: ExtensionAPI) {
|
|
|
658
686
|
pi.registerTool({
|
|
659
687
|
name: "tool_search",
|
|
660
688
|
label: "Tool Search",
|
|
661
|
-
description: "Search
|
|
689
|
+
description: "Search local tool definitions and activate matching inactive tools without disabling current tools.",
|
|
662
690
|
promptSnippet: "Search and activate additional tools on demand",
|
|
663
691
|
parameters: TOOL_SEARCH_PARAMS,
|
|
664
692
|
async execute(_id, params, _signal, _update, ctx) {
|
|
693
|
+
if (!params.query.trim()) throw new TypeError("query must not be empty");
|
|
665
694
|
const state = ensureSnapshot(ctx);
|
|
666
|
-
const catalog =
|
|
695
|
+
const catalog = buildToolSearchCatalog(state);
|
|
667
696
|
const page = searchToolDiscoveryCatalog(catalog, params.query, {
|
|
668
697
|
cursor: params.cursor,
|
|
669
698
|
pageSize: Math.min(
|
|
@@ -671,7 +700,9 @@ export default function skillOptimizer(pi: ExtensionAPI) {
|
|
|
671
700
|
state.config.toolSearchPageSize,
|
|
672
701
|
),
|
|
673
702
|
});
|
|
674
|
-
const activated = activateAdditively(page.results
|
|
703
|
+
const activated = activateAdditively(page.results
|
|
704
|
+
.filter((result) => result.matchedTerms.length > 0)
|
|
705
|
+
.map((result) => result.name));
|
|
675
706
|
return {
|
|
676
707
|
content: [{ type: "text", text: JSON.stringify({ ...page, activated }) }],
|
|
677
708
|
details: { activated, confidence: page.confidence, failOpen: page.failOpen },
|