一键导入
building-guardrails-extensions
// Builds new pi-guardrails feature extensions using the core/shared split. Use when adding guardrails features such as zones, policy engines, path controls, permission gates, or new Pi hooks in this repository.
// Builds new pi-guardrails feature extensions using the core/shared split. Use when adding guardrails features such as zones, policy engines, path controls, permission gates, or new Pi hooks in this repository.
| name | building-guardrails-extensions |
| description | Builds new pi-guardrails feature extensions using the core/shared split. Use when adding guardrails features such as zones, policy engines, path controls, permission gates, or new Pi hooks in this repository. |
Use this when adding a new feature extension to @aliou/pi-guardrails.
src/core only when they are generic and reusable.src/shared only when multiple extensions need it.extensions/<feature>.guardrails.json via src/shared/config/loader.ts.Create:
extensions/<feature>/
index.ts # Pi adapter: load config, register hooks/events/commands
rules.ts # Rule<TMeta> factories and feature-specific metadata
targets.ts # Tool input -> Action targets, if needed
prompt.ts # UI prompt, if needed
grants.ts # Persisted/session grants, if needed
Do not add feature-specific metadata to src/shared.
Use core typed rules:
import type { Action, Rule } from "../../src/core";
export type ZonesMeta = {
zoneId: string;
path: string;
};
export function createZonesRule(): Rule<ZonesMeta> {
return {
key: "zones.access",
check(action: Action) {
if (action.kind !== "file") return { kind: "pass" };
return {
kind: "match",
reason: "Zone policy blocks this access.",
metadata: { zoneId: "workspace", path: action.path },
};
},
};
}
Rules must return { kind: "pass" } or { kind: "match", reason, metadata }. Metadata and reason are required. Use TMeta = null only when there is truly no metadata.
In extensions/<feature>/index.ts:
configLoader.getConfig() inside hooks, not once at startup.!config.enabled or feature flag is false.Actions.checkAction() with feature rules.For a new feature such as issue #29 zones:
src/shared/config/types.ts.src/shared/config/defaults.ts.features.<feature> using GuardrailsFeatureId if it is user-toggleable.Hypothetical zones config should stay feature-owned at runtime:
{
"features": { "zones": true },
"zones": [
{
"id": "workspace",
"path": "~/workspace",
"bash": "safe-only",
"files": "readOnly"
}
],
"zonesDefault": { "bash": "block", "files": "noAccess" }
}
For zones, keep CWD priority semantics in the zones feature, not in shared path helpers, unless it becomes generally reusable.
Reuse existing helpers before adding new parsing:
src/shared/paths.src/core/shell.src/core/paths.src/shared/matching.If a feature must inspect bash paths, add a feature targets.ts that converts tool calls into file actions or feature-specific targets.
extensions/guardrails/commands/settings.registerSettingsCommand for settings screens only.pi.registerCommand plus Wizard for guided flows.Add focused tests next to the feature:
extensions/<feature>/rules.test.ts
extensions/<feature>/targets.test.ts
extensions/<feature>/grants.test.ts # if grants exist
Prefer pure rule/target tests over full extension harness tests. Use hook-level tests only when Pi event integration is the behavior under test.
When adding or changing defaults, permission patterns, or presets:
schema.json with pnpm gen:schema if config types changed.README.md if commands, feature flags, or public behavior changes.src/shared/config/defaults.ts and extensions/guardrails/commands/settings/examples.ts as the source of truth for defaults and presets.Add a changeset for user-facing behavior before release.