Pi tool repair integration
A library of agent skills for developing with Pi in mind.From the repository description
npx -y skills add r3b1s/pi-dev-skills --skill pi-tool-repair-integrationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 23 days oldThe repository was created 23 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
8.0 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
pi Tool Repair Integration
Integrate @r3b1s/pi-repair-layer into a pi extension's own tools so
malformed model arguments ({file_path: "/x"} instead of {path: "/x"},
stringified JSON, stray nulls) are repaired before pi validation — without
forcing every downstream user to install the repair layer.
Verified baselines: package ^0.3.0, Node 22+, pi 0.80.6 (probed through
0.80.10).
The ownership boundary (read first)
pi runs prepareArguments → validation → tool_call → execute. Arguments that
fail validation never reach tool_call, so repair must run in
prepareArguments — and only the extension that owns a tool definition can
install that hook. Installing pi-repair-layer repairs pi's built-in tools
only; it never discovers or wraps tools registered by other extensions. Your
extension must opt in per tool. Never attempt to wrap another extension's
tools.
Step 1 — Choose the dependency posture
| Posture | When | How |
|---|---|---|
| Optional (default for standalone extensions) | The tool works fine unwrapped; repairs are an enhancement | Fallback recipe below; package stays out of runtime deps |
| Hard dependency | The tool relies on repair behavior (e.g. legacy-shape migration via preprocessors) | pnpm add @r3b1s/pi-repair-layer; import statically |
The optional posture turns "also install pi-repair-layer" into an end-user
opt-in: pi installs all npm: extensions into one shared node_modules per
scope, so a user who runs pi install npm:@r3b1s/pi-repair-layer makes it
resolvable to every consenting npm-installed extension automatically.
Step 2 — Write the tool and repair options as pure data
Repair options must be plain data validated with a type-only import, so compilation never requires the package at runtime:
import type { PiToolOwnerAdapterOptions } from "@r3b1s/pi-repair-layer/pi";
const repairOptions = {
policy: "adaptive",
preprocessors: [
{
kind: "alias",
selector: "/path",
aliases: ["file_path"],
accepts: "string",
},
],
} satisfies PiToolOwnerAdapterOptions;
Configure only transforms you know are safe for your tool — see
resources/preprocessor-catalog.md for
every kind, selector semantics, and the policy profiles. The pipeline never
guesses aliases, fuzzily renames keys, or deletes unknown fields; wrapped
tools get bounded envelope recovery and schema-located repairs for free.
Step 3 — The optional-integration recipe
Copy resources/optional-extension-template.ts (a complete extension) and adapt names. The load-bearing core:
import type { adaptToolDefinition } from "@r3b1s/pi-repair-layer/pi";
async function loadRepairAdapter(): Promise<
typeof adaptToolDefinition | undefined
> {
try {
const repair = await import("@r3b1s/pi-repair-layer/pi");
return repair.adaptToolDefinition;
} catch (error) {
const code = (error as { code?: unknown } | null)?.code;
const message = error instanceof Error ? error.message : String(error);
const packageAbsent =
(code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") &&
message.includes("@r3b1s/pi-repair-layer");
if (!packageAbsent) throw error;
return undefined;
}
}
export default async function myExtension(pi: ExtensionAPI) {
const adapt = await loadRepairAdapter();
if (!adapt) {
console.error(
"[my-extension] @r3b1s/pi-repair-layer not found; my_tool running unwrapped",
);
}
pi.registerTool(adapt ? adapt(definition, repairOptions) : definition);
}
Every detail matters — do not simplify these away:
- Both error codes.
MODULE_NOT_FOUNDis jiti's require path;ERR_MODULE_NOT_FOUNDis native ESM and the compiled pi binary. - The message must name
@r3b1s/pi-repair-layer. A present-but-broken install throws the same codes naming a transitive module; swallowing it would silently disable repairs the user believes are active. Match the package name, not the/pisubpath — native ESM reports onlyCannot find package '@r3b1s/pi-repair-layer'. - Rethrow anything else. Any other error is a real failure, not absence.
- One stderr note on fallback. The branches differ in coercion behavior; a silent divergence cannot be diagnosed from a session transcript.
- Identity fallback. Register the unmodified definition — never a partial wrapper.
Step 4 — Consumer package.json
{
"devDependencies": {
"@r3b1s/pi-repair-layer": "^0.3.0" // typecheck + local tests only
},
"peerDependencies": {
"@r3b1s/pi-repair-layer": ">=0.3.0"
},
"peerDependenciesMeta": {
"@r3b1s/pi-repair-layer": { "optional": true }
}
}
Alternative: authors who want repairs whenever the environment allows can add
"optionalDependencies": { "@r3b1s/pi-repair-layer": "^0.3.0" } — a failed
optional install does not fail the extension install. This is also the path
for consumers the shared-root story cannot reach (see caveats).
Step 5 — Test both branches
Work through resources/testing-checklist.md. Minimum bar: with the package absent, activation succeeds, the raw definition is registered, and the note is emitted; with it present, the adapter branch is taken silently and each configured repair produces valid arguments.
Hard caveats (state these to the user when relevant)
- Compiled pi binary never takes the adapter branch. Under the standalone
(Bun-compiled) pi executable, dynamic
import()cannot resolve npm-installed siblings, so the recipe falls back — safely, with the note — even when the package is installed. The optional pattern activates only under Node-based pi installs. A hard static dependency works under both. - Scope and install source matter. Git-installed extensions get their own
clone-local
node_modules; project-scope and user-scope installs do not see each other's siblings. Those consumers fall back; offeroptionalDependenciesinstead. - Fallback mode is baseline pi, not "repairs minus notes." pi's native
validation runs TypeBox
Value.Convertfirst, which silently coerces some invalid input (null→"null") instead of repairing or rejecting it. The tool owner must decide explicitly whether that is acceptable. - No double-wrap. The installable pi-repair-layer extension only overrides pi's built-ins; a tool adapted by this recipe is wrapped exactly once in either branch.
Stability contract you may rely on
- Subpaths (
/pi,/core,/grammar) and theadaptToolDefinition(definition, options?)signature are stable for the current major (semver). - Absence detection semantics (the two codes + module-naming message) are part of the contract.
- Unrecognized preprocessor
kinds are ignored — never fatal, no mutation, results still schema-validated — so options written against a newer minor degrade gracefully on older installs.
Going further
- Failing closed: the adapter throws
UnrepairableToolInputErrorwith a model-readable retry message by default;unrepairable: "passthrough"exists only for deliberate migrations. - Structured outcomes: pass
onOutcome(result)for value-free metrics (rule IDs, stages, policy, fingerprint — never argument values). <repair_note>feedback and theRepairLifecycle, plus the pure-corerunRepairPipeline, are documented in the package'sdocs/tool-owner-integration.md.
What ships with it: 4 files
9.5 KB alongside SKILL.md, 1 of them executable
resources/
- optional-extension-template.tsruns2.4 KB
- preprocessor-catalog.md3.8 KB
- testing-checklist.md2.5 KB
- metadata.json736 B