@aliou/pi-guardrails 0.9.4 → 0.10.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/README.md +5 -0
- package/docs/defaults.md +115 -0
- package/docs/examples.md +170 -0
- package/package.json +10 -9
- package/src/commands/onboarding-command.ts +59 -0
- package/src/commands/onboarding.ts +274 -0
- package/src/commands/settings-command.ts +129 -3
- package/src/config.ts +58 -2
- package/src/hooks/permission-gate.ts +248 -105
- package/src/hooks/policies.ts +20 -4
- package/src/index.ts +62 -3
- package/src/utils/migration.ts +55 -1
- package/src/utils/path.ts +18 -0
package/src/index.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { isOnboardingPending } from "./commands/onboarding";
|
|
3
|
+
import { registerGuardrailsOnboardingCommand } from "./commands/onboarding-command";
|
|
2
4
|
import { registerGuardrailsSettings } from "./commands/settings-command";
|
|
3
5
|
import { configLoader } from "./config";
|
|
4
6
|
import { setupGuardrailsHooks } from "./hooks";
|
|
7
|
+
import {
|
|
8
|
+
migrateApplyBuiltinDefaults,
|
|
9
|
+
migrateMarkOnboardingDone,
|
|
10
|
+
needsApplyBuiltinDefaultsMigration,
|
|
11
|
+
needsOnboardingDoneMigration,
|
|
12
|
+
} from "./utils/migration";
|
|
5
13
|
import { pendingWarnings } from "./utils/warnings";
|
|
6
14
|
|
|
7
15
|
/**
|
|
@@ -22,16 +30,67 @@ import { pendingWarnings } from "./utils/warnings";
|
|
|
22
30
|
*/
|
|
23
31
|
export default async function (pi: ExtensionAPI) {
|
|
24
32
|
await configLoader.load();
|
|
25
|
-
const config = configLoader.getConfig();
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
const hasGlobalConfig = configLoader.hasConfig("global");
|
|
35
|
+
|
|
36
|
+
if (hasGlobalConfig) {
|
|
37
|
+
const globalConfig = configLoader.getRawConfig("global");
|
|
38
|
+
if (globalConfig) {
|
|
39
|
+
let migrated = globalConfig;
|
|
40
|
+
let changed = false;
|
|
41
|
+
|
|
42
|
+
if (needsApplyBuiltinDefaultsMigration(migrated)) {
|
|
43
|
+
migrated = migrateApplyBuiltinDefaults(migrated);
|
|
44
|
+
changed = true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (needsOnboardingDoneMigration(migrated)) {
|
|
48
|
+
migrated = migrateMarkOnboardingDone(migrated);
|
|
49
|
+
changed = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (changed) {
|
|
53
|
+
await configLoader.save("global", migrated);
|
|
54
|
+
await configLoader.load();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let hooksRegistered = false;
|
|
28
60
|
|
|
29
|
-
setupGuardrailsHooks(pi, config);
|
|
30
61
|
registerGuardrailsSettings(pi);
|
|
31
62
|
|
|
63
|
+
const maybeRegisterHooks = () => {
|
|
64
|
+
if (hooksRegistered) return;
|
|
65
|
+
const config = configLoader.getConfig();
|
|
66
|
+
if (!config.enabled) return;
|
|
67
|
+
setupGuardrailsHooks(pi, config);
|
|
68
|
+
hooksRegistered = true;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (isOnboardingPending(configLoader.getRawConfig("global"))) {
|
|
72
|
+
registerGuardrailsOnboardingCommand(pi, maybeRegisterHooks);
|
|
73
|
+
} else {
|
|
74
|
+
maybeRegisterHooks();
|
|
75
|
+
}
|
|
76
|
+
|
|
32
77
|
pi.on("session_start", (_event, ctx) => {
|
|
33
78
|
for (const warning of pendingWarnings.splice(0)) {
|
|
34
79
|
ctx.ui.notify(warning, "warning");
|
|
35
80
|
}
|
|
81
|
+
|
|
82
|
+
if (!ctx.hasUI) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isOnboardingPending(configLoader.getRawConfig("global"))) {
|
|
87
|
+
ctx.ui.notify(
|
|
88
|
+
"[Guardrails] setup pending. Run `/guardrails:onboarding` to choose recommended or minimal protection defaults.",
|
|
89
|
+
"info",
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
maybeRegisterHooks();
|
|
36
95
|
});
|
|
37
96
|
}
|
package/src/utils/migration.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { pendingWarnings } from "./warnings";
|
|
|
21
21
|
* Keep this independent from package.json version.
|
|
22
22
|
* Bump only when config schema/default migration markers change.
|
|
23
23
|
*/
|
|
24
|
-
export const CURRENT_VERSION = "0.
|
|
24
|
+
export const CURRENT_VERSION = "0.9.0-20260327";
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Check if a config needs migration (no version field = v0).
|
|
@@ -97,6 +97,60 @@ export function needsEnvFilesToPoliciesMigration(
|
|
|
97
97
|
return features?.protectEnvFiles !== undefined;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Check if config needs applyBuiltinDefaults bridge migration.
|
|
102
|
+
* This runs only for existing config files loaded by ConfigLoader.
|
|
103
|
+
*/
|
|
104
|
+
export function needsApplyBuiltinDefaultsMigration(
|
|
105
|
+
config: GuardrailsConfig,
|
|
106
|
+
): boolean {
|
|
107
|
+
return config.applyBuiltinDefaults === undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Bridge migration for defaults deprecation.
|
|
112
|
+
* Existing config files get applyBuiltinDefaults=true to preserve behavior.
|
|
113
|
+
*/
|
|
114
|
+
export function migrateApplyBuiltinDefaults(
|
|
115
|
+
config: GuardrailsConfig,
|
|
116
|
+
): GuardrailsConfig {
|
|
117
|
+
const migrated = structuredClone(config);
|
|
118
|
+
migrated.applyBuiltinDefaults = true;
|
|
119
|
+
migrated.version = CURRENT_VERSION;
|
|
120
|
+
|
|
121
|
+
pendingWarnings.push(
|
|
122
|
+
"Guardrails config was migrated. `applyBuiltinDefaults` was set to `true` to preserve current behavior.",
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return migrated;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function needsOnboardingDoneMigration(
|
|
129
|
+
config: GuardrailsConfig,
|
|
130
|
+
): boolean {
|
|
131
|
+
return (
|
|
132
|
+
config.onboarding?.completed === undefined &&
|
|
133
|
+
config.applyBuiltinDefaults !== undefined
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function migrateMarkOnboardingDone(
|
|
138
|
+
config: GuardrailsConfig,
|
|
139
|
+
): GuardrailsConfig {
|
|
140
|
+
const migrated = structuredClone(config);
|
|
141
|
+
pendingWarnings.push(
|
|
142
|
+
"Guardrails config was migrated. Existing setup marked as onboarding-complete.",
|
|
143
|
+
);
|
|
144
|
+
migrated.onboarding = {
|
|
145
|
+
...(migrated.onboarding ?? {}),
|
|
146
|
+
completed: true,
|
|
147
|
+
completedAt: migrated.onboarding?.completedAt ?? new Date().toISOString(),
|
|
148
|
+
version: migrated.onboarding?.version ?? CURRENT_VERSION,
|
|
149
|
+
};
|
|
150
|
+
migrated.version = CURRENT_VERSION;
|
|
151
|
+
return migrated;
|
|
152
|
+
}
|
|
153
|
+
|
|
100
154
|
/**
|
|
101
155
|
* Migrate deprecated envFiles/protectEnvFiles fields to policies.
|
|
102
156
|
*/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Expand a leading tilde to the current user's home directory.
|
|
6
|
+
* Preserves all other paths unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export function expandHomePath(input: string): string {
|
|
9
|
+
if (input === "~") {
|
|
10
|
+
return homedir();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (input.startsWith("~/")) {
|
|
14
|
+
return join(homedir(), input.slice(2));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return input;
|
|
18
|
+
}
|