@aeondave/pi-skill-optimizer 3.0.0 → 3.1.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/BENCHMARK.md CHANGED
@@ -62,7 +62,7 @@ The benchmark asks:
62
62
  2. Is the stable base byte-identical across unrelated human requests?
63
63
  3. Does bounded prefetch improve first-turn recall without changing that base?
64
64
  4. Can `skill_search` recover relevant tail skills through bounded pagination?
65
- 5. Can `tool_search` defer schemas without making required tools unavailable?
65
+ 5. Can `optimizer_tool_search` defer schemas without making required tools unavailable?
66
66
  6. Do output and history reductions preserve protected evidence and exact recovery?
67
67
  7. Is every accepted serialized transformation smaller?
68
68
  8. What do providers report for tokens, cache reads, cache writes, and cost?
@@ -174,7 +174,7 @@ Blocking invariants:
174
174
 
175
175
  - core, configured, and already used tools remain active;
176
176
  - inactive schemas are absent from the initial provider request;
177
- - `tool_search` remains active while searchable definitions remain;
177
+ - `optimizer_tool_search` remains active while searchable definitions remain;
178
178
  - activation makes selected tools callable in the same session;
179
179
  - activation is additive and cannot strand an active provider tool loop;
180
180
  - schema indexing work and response pagination remain bounded;
@@ -188,7 +188,7 @@ Reported metrics:
188
188
  tool_recall
189
189
  initial_tool_schema_bytes
190
190
  activated_tool_schema_bytes
191
- tool_search_calls
191
+ optimizer_tool_search_calls
192
192
  activation_failures
193
193
  ```
194
194
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes are documented here.
4
4
 
5
+ ## 3.1.0 - 2026-10-03
6
+
7
+ ### Breaking
8
+
9
+ - Rename the optimizer's model-callable `tool_search` to `optimizer_tool_search`. Explicit tool calls must use the new name. Pi's built-in `tool_search` now loads alongside the optimizer without a replacement warning.
10
+
11
+ ### Fixed
12
+
13
+ - Preserve an active built-in `tool_search` during optimizer replanning.
14
+ - Retain tools activated later by Pi or another extension, and honor `alwaysTools` for initially inactive `deferred` or `codemode` tools while respecting hidden and inactive direct tools.
15
+ - Capture an explicitly requested benchmark catalog from Pi 1.0's `before_agent_start` skill options.
16
+
17
+ ### Changed
18
+
19
+ - Require Pi 1.0.0 or newer for this release.
20
+ - Run typechecking and host integration tests against Pi 1.0.0 development packages.
21
+ - Add a reproducible Pi CLI black-box smoke test for the packed artifact.
22
+
23
+ ## 3.0.1 - 2026-09-30
24
+
25
+ ### Fixed
26
+
27
+ - 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.
28
+ - 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.
29
+
5
30
  ## 3.0.0 - 2026-09-28
6
31
 
7
32
  ### Scoped distribution - 2026-09-29
package/README.md CHANGED
@@ -14,7 +14,7 @@ No embedding service or extra model call for ordinary routing. No changes to you
14
14
 
15
15
  ## Install
16
16
 
17
- Requires **Pi 0.87.1+** and **Node.js 26.3+**. Upgrade both before installing v3.
17
+ Requires **Pi 1.0.0+** and **Node.js 26.3+**. Upgrade both before installing v3.1.
18
18
 
19
19
  ```bash
20
20
  pi install npm:@aeondave/pi-skill-optimizer
@@ -26,12 +26,18 @@ Restart Pi, then check that it loaded:
26
26
  /skill-optimizer
27
27
  ```
28
28
 
29
+ On Pi 1.0.0, the optimizer's search tool is `optimizer_tool_search`. Pi's
30
+ built-in `tool_search` can load alongside it without a name conflict. If you
31
+ previously disabled `builtin:tool-search` to avoid the warning, you can remove
32
+ that setting after updating the optimizer. See the
33
+ [tool-search configuration](docs/CONFIGURATION.md#tool-search-and-pi-100).
34
+
29
35
  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
36
 
31
37
  | Need | Command |
32
38
  | --- | --- |
33
39
  | 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.0` |
40
+ | Pin the current release | `pi install npm:@aeondave/pi-skill-optimizer@3.1.0` |
35
41
  | Update an unpinned install | `pi update npm:@aeondave/pi-skill-optimizer` |
36
42
 
37
43
  Restart after updating. Pinned installs stay pinned until you explicitly install another version.
@@ -58,7 +64,7 @@ AUTO keeps a compact, stable index of **all eligible names**, adds relevant desc
58
64
  | Area | What AUTO does | What stays available |
59
65
  | --- | --- | --- |
60
66
  | Skills | Shortens catalog descriptions; prefetches relevant ones | Every eligible name and exact full-instruction loading |
61
- | Tools | Defers less relevant schemas behind `tool_search` | Protected, configured, already-used and activated tools |
67
+ | Tools | Defers less relevant schemas behind `optimizer_tool_search` | Protected, configured, already-used and activated tools |
62
68
  | Large results | Applies deterministic reduction; lossless columnar JSON where suitable | Complete originals through `retrieve_output` during archive retention |
63
69
  | Repeated results | Replaces later exact duplicates with recovery references | The first result, errors, protected evidence and mixed media |
64
70
 
@@ -76,7 +82,7 @@ You can leave AUTO running with its defaults.
76
82
 
77
83
  An optional setup hint appears at the first eligible prompt when initialization is missing or needs attention. It never starts init for you or adds instructions to the model's context.
78
84
 
79
- Pi gets three tools: `skill_search`, `tool_search` and `retrieve_output`. Skill search is paginated and exact loads accept only registered names. Output recovery uses opaque identifiers, not exposed archive paths.
85
+ Pi gets three optimizer tools: `skill_search`, `optimizer_tool_search` and `retrieve_output`. Pi may also provide its built-in `tool_search`. Skill search is paginated and exact loads accept only registered names. Output recovery uses opaque identifiers, not exposed archive paths.
80
86
 
81
87
  Skills can be useful even when you never mention them. Init can tag architecture,
82
88
  testing and behavioral guidance by activity, so a matched specialist can bring
@@ -30,9 +30,9 @@ expand dependencies or treat a behavioral tag as universal. See
30
30
 
31
31
  ## Dynamic tools
32
32
 
33
- AUTO keeps protected and predicted tools active and leaves the rest behind `tool_search`. Search is bounded and paginated. A returned result is activated through Pi's runtime API without replacing already active tools. Core, configured, and previously used tools remain callable, so an in-progress tool loop cannot be stranded. Weak or absent routing signal fails open to every permitted tool; safety wins, but that request may retain the full schema cost.
33
+ AUTO keeps protected and predicted tools active and leaves the rest behind `optimizer_tool_search`. Search is bounded and paginated. A returned result is activated through Pi's runtime API without replacing already active tools. Core, configured, and previously used tools remain callable, so an in-progress tool loop cannot be stranded. Pi's built-in `tool_search` remains active when the host enables it. Weak or absent routing signal fails open to every permitted tool; safety wins, but that request may retain the full schema cost.
34
34
 
35
- Selection is recalculated before each agent run, not just the first request. Used tools and explicit search results stay active for the session. The discoverable set starts from Pi's initially active tools plus the optimizer's own resolvers; tools that were initially unavailable are not silently added. This is schema routing, not authorization: another extension's call-time permission checks remain authoritative.
35
+ Selection is recalculated before each agent run, not just the first request. Used tools and explicit search results stay active for the session. The discoverable set starts from Pi's initially active tools plus the optimizer's own resolvers. Newly activated host tools are retained; inactive `deferred` and `codemode` tools can be selected through search or `alwaysTools`. Inactive direct and hidden tools are not silently added. This is schema routing, not authorization: another extension's call-time permission checks remain authoritative.
36
36
 
37
37
  See Pi's [extension and dynamic-tool API](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md).
38
38
 
@@ -7,13 +7,19 @@
7
7
  | Tool | Purpose |
8
8
  | --- | --- |
9
9
  | `skill_search` | Paginated skill search plus exact `load`, contained `resource`, and explicit `location` actions |
10
- | `tool_search` | Search deferred tool definitions and activate bounded matches additively |
10
+ | `optimizer_tool_search` | Search deferred tool definitions and activate bounded matches additively |
11
11
  | `retrieve_output` | Recover archived output or a deduplicated history result by opaque identifier |
12
12
 
13
13
  Exact skill loading accepts only registered names. Resource and location resolution use canonical paths and containment checks to reject traversal and symlink escape. Search cursors bind to the query, catalog and ranked name order; a changed profile or usage prior that reorders results requires a new search, rather than duplicating or skipping a page. This is an exact local resolver guarantee, not a guarantee that the model will select the right skill; see the separate [ranking diagnostics](RANKING.md) and [historical model benchmark](../BENCHMARK.md).
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
+ ### Tool search and Pi 1.0.0
18
+
19
+ The extension registers `optimizer_tool_search`, leaving Pi's built-in `tool_search` available under its own name. 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. An already active built-in `tool_search` stays active during optimizer replanning. Pi's built-in MCP extension can activate its own `tool_search` when a deferred server connects.
20
+
21
+ If an older optimizer release led you to add `-builtin:tool-search` in `~/.pi/agent/settings.json` or `.pi/settings.json`, remove that entry after updating if you want Pi's built-in search available again. The built-in is inactive by default unless enabled in Pi's `defaultTools` or activated by Pi for deferred MCP tools; the optimizer's search stays available independently. Existing explicit calls to the optimizer's old `tool_search` name must use `optimizer_tool_search` after this update.
22
+
17
23
  ## Commands
18
24
 
19
25
  | Command | Purpose |
@@ -49,7 +55,7 @@ Global configuration lives at `~/.pi/agent/skill-optimizer/config.json`. A proje
49
55
  | `providers` | `[]` | Provider allowlist; empty means all |
50
56
  | `toolPrefetchMax` | `5` | Maximum proactive tool activation |
51
57
  | `toolSearchPageSize` | `8` | Default tool-search page size |
52
- | `alwaysTools` | `[]` | Tools that remain active |
58
+ | `alwaysTools` | `[]` | Keep initially active tools and named `deferred` or `codemode` tools active; hidden tools remain unavailable |
53
59
  | `historyDedupMinBytes` | `4096` | Minimum result size for history deduplication |
54
60
  | `usageHalfLifeDays` | `30` | Usage-evidence decay half-life |
55
61
  | `usageMaxEntries` | `2048` | Persistent usage-evidence bound |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aeondave/pi-skill-optimizer",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
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.",
@@ -43,12 +43,13 @@
43
43
  ],
44
44
  "scripts": {
45
45
  "typecheck": "tsc --noEmit",
46
- "test": "node --import tsx --test test/request.test.ts test/history.test.ts test/skills.test.ts test/skill-loader.test.ts test/tools.test.ts test/optimize.test.ts test/profile.test.ts test/activation.test.ts test/generate.test.ts test/init.test.ts test/init-readiness.test.ts test/config.test.ts test/fuzz.test.ts test/output.test.ts test/output-recovery.test.ts test/runtime.test.ts test/host-runtime.test.ts test/stats.test.ts test/usage.test.ts test/persistence.test.ts test/corpus.test.ts test/evaluation.test.ts test/luna.test.ts test/release-metadata.test.ts",
46
+ "test": "node --import tsx --test test/request.test.ts test/history.test.ts test/skills.test.ts test/skill-loader.test.ts test/tools.test.ts test/optimize.test.ts test/profile.test.ts test/activation.test.ts test/generate.test.ts test/init.test.ts test/init-readiness.test.ts test/config.test.ts test/fuzz.test.ts test/output.test.ts test/output-recovery.test.ts test/runtime.test.ts test/host-runtime.test.ts test/stats.test.ts test/usage.test.ts test/persistence.test.ts test/corpus.test.ts test/capture-catalog.test.ts test/evaluation.test.ts test/luna.test.ts test/release-metadata.test.ts",
47
47
  "measure": "node --import tsx scripts/measure.ts",
48
48
  "bench": "node --import tsx scripts/bench.ts",
49
49
  "bench:perf": "node --import tsx scripts/perf-bench.ts",
50
50
  "bench:ranking": "node --import tsx scripts/ranking-bench.ts",
51
51
  "bench:output": "node --import tsx scripts/output-bench.ts",
52
+ "smoke:blackbox": "node --import tsx scripts/black-box-smoke.ts",
52
53
  "corpus:build": "node --import tsx scripts/build-real-corpus.ts",
53
54
  "bench:real": "node --import tsx scripts/evaluate-real-corpus.ts"
54
55
  },
@@ -69,8 +70,8 @@
69
70
  }
70
71
  },
71
72
  "devDependencies": {
72
- "@earendil-works/pi-ai": "^0.87.1",
73
- "@earendil-works/pi-coding-agent": "^0.87.1",
73
+ "@earendil-works/pi-ai": "^1.0.0",
74
+ "@earendil-works/pi-coding-agent": "^1.0.0",
74
75
  "@types/node": "^26.6.3",
75
76
  "tsx": "^4.23.13",
76
77
  "typebox": "^1.3.34",
@@ -80,6 +81,6 @@
80
81
  "extensions": [
81
82
  "./src/index.ts"
82
83
  ],
83
- "image": "https://raw.githubusercontent.com/AeonDave/pi-skill-optimizer/v3.0.0/assets/banner.png"
84
+ "image": "https://raw.githubusercontent.com/AeonDave/pi-skill-optimizer/v3.1.0/assets/banner.png"
84
85
  }
85
86
  }
package/src/index.ts CHANGED
@@ -110,7 +110,16 @@ const FLUSH_INTERVAL_MS = 15_000;
110
110
  const LOAD_RETRY_MS = 1_000;
111
111
  const MAX_SEEN_EVIDENCE = 4_096;
112
112
  const MAX_HISTORY_ARTIFACTS = 128;
113
- const PROTECTED_RUNTIME_TOOLS = ["skill_search", "tool_search", "retrieve_output"] as const;
113
+ const PROTECTED_RUNTIME_TOOLS = ["skill_search", "optimizer_tool_search", "retrieve_output"] as const;
114
+ const RESOLVER_RESULT_TOOLS = new Set<string>([...PROTECTED_RUNTIME_TOOLS, "tool_search"]);
115
+
116
+ function isHostSearchableTool(tool: object): boolean {
117
+ return "exposure" in tool && (tool.exposure === "deferred" || tool.exposure === "codemode");
118
+ }
119
+
120
+ function isHostHiddenTool(tool: object): boolean {
121
+ return "exposure" in tool && tool.exposure === "hidden";
122
+ }
114
123
 
115
124
  type ConfigRecord = ReturnType<typeof getConfig>;
116
125
 
@@ -565,12 +574,34 @@ export default function skillOptimizer(pi: ExtensionAPI) {
565
574
  };
566
575
 
567
576
  const rebuildToolCatalog = (state: RuntimeSnapshot): ToolDiscoveryCatalog => {
577
+ const allTools = pi.getAllTools();
578
+ const activeNames = new Set(pi.getActiveTools().map((name) => name.toLowerCase()));
579
+ const alwaysToolPrefixes = state.config.alwaysTools
580
+ .map((pattern) => pattern.trim().toLowerCase())
581
+ .filter((pattern) => pattern.length > 0);
568
582
  permittedToolNames ??= new Set([
569
- ...pi.getActiveTools().map((name) => name.toLowerCase()),
583
+ ...activeNames,
570
584
  ...PROTECTED_RUNTIME_TOOLS,
571
585
  ]);
572
- const discoverable = pi.getAllTools().filter((tool) =>
573
- permittedToolNames?.has(tool.name.toLowerCase()),
586
+ for (const tool of allTools) {
587
+ const name = tool.name.toLowerCase();
588
+ if (isHostHiddenTool(tool)) {
589
+ permittedToolNames.delete(name);
590
+ retainedToolNames.delete(name);
591
+ deactivatedToolNames.delete(name);
592
+ continue;
593
+ }
594
+ if (activeNames.has(name)) {
595
+ const newlyActive = !permittedToolNames.has(name);
596
+ const reactivated = deactivatedToolNames.delete(name);
597
+ if (newlyActive || reactivated) retainedToolNames.add(name);
598
+ permittedToolNames.add(name);
599
+ } else if (isHostSearchableTool(tool) && alwaysToolPrefixes.some((pattern) => name.startsWith(pattern))) {
600
+ permittedToolNames.add(name);
601
+ }
602
+ }
603
+ const discoverable = allTools.filter((tool) =>
604
+ !isHostHiddenTool(tool) && permittedToolNames?.has(tool.name.toLowerCase()),
574
605
  );
575
606
  toolCatalog = buildToolDiscoveryCatalog(discoverable, {
576
607
  protect: [...PROTECTED_RUNTIME_TOOLS, ...state.config.alwaysTools],
@@ -578,13 +609,25 @@ export default function skillOptimizer(pi: ExtensionAPI) {
578
609
  return toolCatalog;
579
610
  };
580
611
 
612
+ const buildToolSearchCatalog = (state: RuntimeSnapshot): ToolDiscoveryCatalog => {
613
+ rebuildToolCatalog(state);
614
+ return buildToolDiscoveryCatalog(pi.getAllTools().filter((tool) =>
615
+ !isHostHiddenTool(tool)
616
+ && (permittedToolNames?.has(tool.name.toLowerCase()) || isHostSearchableTool(tool)),
617
+ ), {
618
+ protect: [...PROTECTED_RUNTIME_TOOLS, ...state.config.alwaysTools],
619
+ });
620
+ };
621
+
581
622
  const activateAdditively = (names: readonly string[]): string[] => {
582
623
  const requested = new Set(names.map((name) => name.toLowerCase()));
583
624
  const current = new Set(pi.getActiveTools().map((name) => name.toLowerCase()));
584
625
  const added: string[] = [];
585
626
  for (const tool of pi.getAllTools()) {
586
- if (!permittedToolNames?.has(tool.name.toLowerCase())) continue;
627
+ if (isHostHiddenTool(tool)) continue;
628
+ if (!permittedToolNames?.has(tool.name.toLowerCase()) && !isHostSearchableTool(tool)) continue;
587
629
  if (!requested.has(tool.name.toLowerCase())) continue;
630
+ permittedToolNames?.add(tool.name.toLowerCase());
588
631
  retainedToolNames.add(tool.name.toLowerCase());
589
632
  if (current.has(tool.name.toLowerCase())) continue;
590
633
  current.add(tool.name.toLowerCase());
@@ -592,8 +635,8 @@ export default function skillOptimizer(pi: ExtensionAPI) {
592
635
  added.push(tool.name);
593
636
  }
594
637
  const ordered = pi.getAllTools()
595
- .map((tool) => tool.name)
596
- .filter((name) => permittedToolNames?.has(name.toLowerCase()) && current.has(name.toLowerCase()));
638
+ .filter((tool) => !isHostHiddenTool(tool) && current.has(tool.name.toLowerCase()))
639
+ .map((tool) => tool.name);
597
640
  pi.setActiveTools(ordered);
598
641
  return added;
599
642
  };
@@ -607,8 +650,8 @@ export default function skillOptimizer(pi: ExtensionAPI) {
607
650
  for (const name of deactivatedToolNames) active.add(name);
608
651
  pi.setActiveTools(
609
652
  pi.getAllTools()
610
- .map((tool) => tool.name)
611
- .filter((name) => active.has(name.toLowerCase())),
653
+ .filter((tool) => !isHostHiddenTool(tool) && active.has(tool.name.toLowerCase()))
654
+ .map((tool) => tool.name),
612
655
  );
613
656
  deactivatedToolNames.clear();
614
657
  toolCatalog = undefined;
@@ -656,14 +699,15 @@ export default function skillOptimizer(pi: ExtensionAPI) {
656
699
  });
657
700
 
658
701
  pi.registerTool({
659
- name: "tool_search",
660
- label: "Tool Search",
661
- description: "Search inactive tools locally. Returned tools are activated additively without disabling current tools.",
702
+ name: "optimizer_tool_search",
703
+ label: "Optimizer Tool Search",
704
+ description: "Search local tool definitions and activate matching inactive tools without disabling current tools.",
662
705
  promptSnippet: "Search and activate additional tools on demand",
663
706
  parameters: TOOL_SEARCH_PARAMS,
664
707
  async execute(_id, params, _signal, _update, ctx) {
708
+ if (!params.query.trim()) throw new TypeError("query must not be empty");
665
709
  const state = ensureSnapshot(ctx);
666
- const catalog = toolCatalog ?? rebuildToolCatalog(state);
710
+ const catalog = buildToolSearchCatalog(state);
667
711
  const page = searchToolDiscoveryCatalog(catalog, params.query, {
668
712
  cursor: params.cursor,
669
713
  pageSize: Math.min(
@@ -671,7 +715,9 @@ export default function skillOptimizer(pi: ExtensionAPI) {
671
715
  state.config.toolSearchPageSize,
672
716
  ),
673
717
  });
674
- const activated = activateAdditively(page.results.map((result) => result.name));
718
+ const activated = activateAdditively(page.results
719
+ .filter((result) => result.matchedTerms.length > 0)
720
+ .map((result) => result.name));
675
721
  return {
676
722
  content: [{ type: "text", text: JSON.stringify({ ...page, activated }) }],
677
723
  details: { activated, confidence: page.confidence, failOpen: page.failOpen },
@@ -1146,7 +1192,7 @@ export default function skillOptimizer(pi: ExtensionAPI) {
1146
1192
 
1147
1193
  pi.on("tool_result", async (event, ctx) => {
1148
1194
  usedTools.add(event.toolName.toLowerCase());
1149
- if (PROTECTED_RUNTIME_TOOLS.includes(event.toolName as typeof PROTECTED_RUNTIME_TOOLS[number])) return;
1195
+ if (RESOLVER_RESULT_TOOLS.has(event.toolName)) return;
1150
1196
  if (isDisabled(ctx.cwd) || event.isError) return;
1151
1197
  const config = getRuntimeConfig(ctx);
1152
1198
  const providers = config.providers.length > 0 ? config.providers : undefined;
@@ -1342,7 +1388,7 @@ export default function skillOptimizer(pi: ExtensionAPI) {
1342
1388
  ` enabled: ${isDisabled(ctx.cwd) ? "no" : "yes"}`,
1343
1389
  " skills mode: AUTO (stable TSV + skill_search resolver)",
1344
1390
  ` catalog budget:${lastBudget.usedChars}/${lastBudget.requestedChars} chars, ${lastBudget.nameOnlyCount} name-only`,
1345
- ` tools: ${pi.getActiveTools().length}/${pi.getAllTools().length} active; tool_search additive`,
1391
+ ` tools: ${pi.getActiveTools().length}/${pi.getAllTools().length} active; optimizer_tool_search additive`,
1346
1392
  ` output: ${getOutputConfig(state.config).mode}; RTK ${rtkExtensionPresent() ? "detected per result" : "not detected"}`,
1347
1393
  ` scope: ${state.config.providers.join(", ") || "all providers"}`,
1348
1394
  ` init readiness: ${retryAfter !== 0 ? "unavailable (profile could not be read)" : readiness.skillCount === 0 ? "not available (no model-invocable skills)" : initReadinessSummary(readiness)}`,
package/src/tools.ts CHANGED
@@ -23,7 +23,7 @@ export const CORE_TOOLS = new Set(
23
23
  "read", "bash", "edit", "write", "multiedit", "str_replace_editor", "notebookedit",
24
24
  "glob", "grep", "ls", "find", "todo", "todowrite", "todoread",
25
25
  "subagent", "fork", "task", "mcp", "skill", "exitplanmode", "enterplanmode",
26
- "webfetch", "websearch", "askuserquestion", "powershell",
26
+ "webfetch", "websearch", "askuserquestion", "powershell", "tool_search",
27
27
  ].map((name) => name.toLowerCase()),
28
28
  );
29
29