@aliou/pi-guardrails 0.11.2 → 0.12.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.
Files changed (95) hide show
  1. package/README.md +72 -167
  2. package/extensions/guardrails/commands/examples/index.ts +520 -0
  3. package/extensions/guardrails/commands/onboarding/config.ts +54 -0
  4. package/{src/commands/onboarding-command.ts → extensions/guardrails/commands/onboarding/index.ts} +5 -31
  5. package/extensions/guardrails/commands/settings/add-rule-wizard.ts +267 -0
  6. package/extensions/guardrails/commands/settings/examples.ts +399 -0
  7. package/extensions/guardrails/commands/settings/index.ts +596 -0
  8. package/extensions/guardrails/commands/settings/path-list-editor.ts +158 -0
  9. package/extensions/guardrails/commands/settings/scope-picker-submenu.ts +69 -0
  10. package/extensions/guardrails/commands/settings/utils.ts +108 -0
  11. package/extensions/guardrails/components/onboarding-choice-step.ts +140 -0
  12. package/extensions/guardrails/components/onboarding-finish-step.ts +50 -0
  13. package/extensions/guardrails/components/onboarding-intro-step.ts +30 -0
  14. package/extensions/guardrails/components/onboarding-types.ts +10 -0
  15. package/extensions/guardrails/components/onboarding-wizard.ts +116 -0
  16. package/{src → extensions/guardrails}/components/pattern-editor.ts +11 -10
  17. package/extensions/guardrails/index.ts +106 -0
  18. package/extensions/guardrails/rules.test.ts +107 -0
  19. package/extensions/guardrails/rules.ts +119 -0
  20. package/extensions/guardrails/targets.test.ts +44 -0
  21. package/extensions/guardrails/targets.ts +66 -0
  22. package/extensions/path-access/grants.test.ts +47 -0
  23. package/extensions/path-access/grants.ts +68 -0
  24. package/extensions/path-access/index.ts +143 -0
  25. package/extensions/path-access/prompt.ts +196 -0
  26. package/extensions/path-access/rules.test.ts +46 -0
  27. package/extensions/path-access/rules.ts +37 -0
  28. package/extensions/path-access/targets.test.ts +40 -0
  29. package/extensions/path-access/targets.ts +19 -0
  30. package/extensions/permission-gate/grants.ts +21 -0
  31. package/extensions/permission-gate/index.ts +122 -0
  32. package/extensions/permission-gate/prompt.ts +222 -0
  33. package/extensions/permission-gate/rules.test.ts +132 -0
  34. package/extensions/permission-gate/rules.ts +72 -0
  35. package/package.json +18 -20
  36. package/schema.json +286 -0
  37. package/src/core/check.test.ts +169 -0
  38. package/src/core/check.ts +38 -0
  39. package/src/{hooks/permission-gate/dangerous-commands.test.ts → core/commands/dangerous.test.ts} +134 -2
  40. package/src/{hooks/permission-gate/dangerous-commands.ts → core/commands/dangerous.ts} +119 -1
  41. package/src/core/commands/index.ts +15 -0
  42. package/src/core/index.ts +13 -0
  43. package/src/{utils/path-access.test.ts → core/paths/access.test.ts} +1 -5
  44. package/src/core/paths/index.ts +14 -0
  45. package/src/core/shell/command-args.test.ts +142 -0
  46. package/src/{utils → core/shell}/command-args.ts +71 -0
  47. package/src/core/shell/index.ts +2 -0
  48. package/src/core/types.ts +55 -0
  49. package/src/shared/config/defaults.ts +118 -0
  50. package/src/shared/config/index.ts +17 -0
  51. package/src/shared/config/loader.ts +64 -0
  52. package/src/shared/config/migration/001-v0-format-upgrade.ts +107 -0
  53. package/src/shared/config/migration/002-strip-toolchain-fields.ts +39 -0
  54. package/src/shared/config/migration/003-strip-command-explainer-fields.ts +42 -0
  55. package/src/shared/config/migration/004-env-files-to-policies.ts +87 -0
  56. package/src/shared/config/migration/005-normalize-allowed-paths.ts +43 -0
  57. package/src/shared/config/migration/006-apply-builtin-defaults.ts +19 -0
  58. package/src/shared/config/migration/007-mark-onboarding-done.ts +25 -0
  59. package/src/shared/config/migration/index.ts +44 -0
  60. package/src/shared/config/migration/version.ts +7 -0
  61. package/src/shared/config/types.ts +141 -0
  62. package/src/shared/events.ts +100 -0
  63. package/src/shared/index.ts +6 -0
  64. package/src/shared/matching.test.ts +86 -0
  65. package/src/{utils → shared}/matching.ts +4 -4
  66. package/src/{utils → shared/paths}/bash-paths.test.ts +32 -2
  67. package/src/{utils → shared/paths}/bash-paths.ts +4 -4
  68. package/src/shared/paths/index.ts +1 -0
  69. package/src/shared/warnings.ts +17 -0
  70. package/docs/defaults.md +0 -140
  71. package/docs/examples.md +0 -170
  72. package/src/commands/onboarding.ts +0 -390
  73. package/src/commands/settings-command.ts +0 -1616
  74. package/src/config.ts +0 -392
  75. package/src/hooks/index.ts +0 -11
  76. package/src/hooks/path-access.ts +0 -395
  77. package/src/hooks/permission-gate/index.test.ts +0 -332
  78. package/src/hooks/permission-gate/index.ts +0 -595
  79. package/src/hooks/policies.ts +0 -322
  80. package/src/index.ts +0 -96
  81. package/src/lib/executor.ts +0 -280
  82. package/src/lib/index.ts +0 -16
  83. package/src/lib/model-resolver.ts +0 -47
  84. package/src/lib/timing.ts +0 -42
  85. package/src/lib/types.ts +0 -115
  86. package/src/utils/command-args.test.ts +0 -83
  87. package/src/utils/events.ts +0 -32
  88. package/src/utils/migration.test.ts +0 -58
  89. package/src/utils/migration.ts +0 -340
  90. package/src/utils/warnings.ts +0 -7
  91. /package/src/{utils/path-access.ts → core/paths/access.ts} +0 -0
  92. /package/src/{utils → core/paths}/path.test.ts +0 -0
  93. /package/src/{utils → core/paths}/path.ts +0 -0
  94. /package/src/{utils/shell-utils.ts → core/shell/ast.ts} +0 -0
  95. /package/src/{utils/glob-expander.ts → shared/glob.ts} +0 -0
@@ -1,340 +0,0 @@
1
- /**
2
- * Config migration from v0 (no version field) to current format.
3
- *
4
- * v0 configs store patterns as plain strings (regex). The migration
5
- * converts them to PatternConfig objects with `regex: true` to preserve
6
- * existing behavior.
7
- */
8
-
9
- import { copyFile, stat } from "node:fs/promises";
10
- import { dirname, resolve } from "node:path";
11
- import type {
12
- DangerousPattern,
13
- GuardrailsConfig,
14
- PatternConfig,
15
- } from "../config";
16
- import { pendingWarnings } from "./warnings";
17
-
18
- /**
19
- * Config schema version.
20
- *
21
- * Keep this independent from package.json version.
22
- * Bump only when config schema/default migration markers change.
23
- */
24
- export const CURRENT_VERSION = "0.9.0-20260327";
25
-
26
- /**
27
- * Check if a config needs migration (no version field = v0).
28
- */
29
- export function needsMigration(config: GuardrailsConfig): boolean {
30
- return config.version === undefined;
31
- }
32
-
33
- /**
34
- * Migrate a v0 config to the current format.
35
- * All string patterns become `{ pattern, regex: true }` to preserve behavior.
36
- */
37
- export function migrateV0(config: GuardrailsConfig): GuardrailsConfig {
38
- const migrated = structuredClone(config);
39
-
40
- // Migrate envFiles patterns
41
- if (migrated.envFiles) {
42
- if (migrated.envFiles.protectedPatterns) {
43
- migrated.envFiles.protectedPatterns = migrateStringArray(
44
- migrated.envFiles.protectedPatterns,
45
- );
46
- }
47
- if (migrated.envFiles.allowedPatterns) {
48
- migrated.envFiles.allowedPatterns = migrateStringArray(
49
- migrated.envFiles.allowedPatterns,
50
- );
51
- }
52
- if (migrated.envFiles.protectedDirectories) {
53
- migrated.envFiles.protectedDirectories = migrateStringArray(
54
- migrated.envFiles.protectedDirectories,
55
- );
56
- }
57
- }
58
-
59
- // Migrate permissionGate patterns
60
- if (migrated.permissionGate) {
61
- if (migrated.permissionGate.patterns) {
62
- migrated.permissionGate.patterns = migrateDangerousPatterns(
63
- migrated.permissionGate.patterns,
64
- );
65
- }
66
- if (migrated.permissionGate.customPatterns) {
67
- migrated.permissionGate.customPatterns = migrateDangerousPatterns(
68
- migrated.permissionGate.customPatterns,
69
- );
70
- }
71
- if (migrated.permissionGate.allowedPatterns) {
72
- migrated.permissionGate.allowedPatterns = migrateStringArray(
73
- migrated.permissionGate.allowedPatterns,
74
- );
75
- }
76
- if (migrated.permissionGate.autoDenyPatterns) {
77
- migrated.permissionGate.autoDenyPatterns = migrateStringArray(
78
- migrated.permissionGate.autoDenyPatterns,
79
- );
80
- }
81
- }
82
-
83
- migrated.version = CURRENT_VERSION;
84
- return migrated;
85
- }
86
-
87
- /**
88
- * Check if a config still uses deprecated envFiles/protectEnvFiles fields.
89
- */
90
- export function needsEnvFilesToPoliciesMigration(
91
- config: GuardrailsConfig,
92
- ): boolean {
93
- const raw = config as Record<string, unknown>;
94
- if (raw.envFiles !== undefined) return true;
95
-
96
- const features = raw.features as Record<string, unknown> | undefined;
97
- return features?.protectEnvFiles !== undefined;
98
- }
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
-
154
- /**
155
- * Migrate allowedPaths entries accidentally written as PatternConfig objects.
156
- */
157
- export function needsAllowedPathsMigration(config: GuardrailsConfig): boolean {
158
- const raw = config as Record<string, unknown>;
159
- const pathAccess = raw.pathAccess as Record<string, unknown> | undefined;
160
- if (!Array.isArray(pathAccess?.allowedPaths)) return false;
161
- return pathAccess.allowedPaths.some((item) => typeof item !== "string");
162
- }
163
-
164
- export function normalizeAllowedPaths(items: unknown): string[] {
165
- if (!Array.isArray(items)) return [];
166
-
167
- const paths = new Set<string>();
168
- for (const item of items) {
169
- let path: string | null = null;
170
- if (typeof item === "string") {
171
- path = item;
172
- } else if (typeof item === "object" && item !== null) {
173
- const pattern = (item as Record<string, unknown>).pattern;
174
- if (typeof pattern === "string") path = pattern;
175
- }
176
-
177
- const normalized = path?.trim();
178
- if (normalized) paths.add(normalized);
179
- }
180
-
181
- return [...paths];
182
- }
183
-
184
- export function migrateAllowedPaths(
185
- config: GuardrailsConfig,
186
- ): GuardrailsConfig {
187
- const migrated = structuredClone(config) as Record<string, unknown>;
188
- const pathAccess = migrated.pathAccess as Record<string, unknown> | undefined;
189
- if (!pathAccess) return migrated as GuardrailsConfig;
190
-
191
- pathAccess.allowedPaths = normalizeAllowedPaths(pathAccess.allowedPaths);
192
- migrated.version = CURRENT_VERSION;
193
- pendingWarnings.push(
194
- "[guardrails] pathAccess.allowedPaths was migrated from pattern objects to path strings.",
195
- );
196
- return migrated as GuardrailsConfig;
197
- }
198
-
199
- /**
200
- * Migrate deprecated envFiles/protectEnvFiles fields to policies.
201
- */
202
- export function migrateEnvFilesToPolicies(
203
- config: GuardrailsConfig,
204
- ): GuardrailsConfig {
205
- const migrated = structuredClone(config);
206
- const raw = migrated as Record<string, unknown>;
207
- const features = raw.features as Record<string, unknown> | undefined;
208
- const envFiles = raw.envFiles as Record<string, unknown> | undefined;
209
-
210
- if (features?.protectEnvFiles !== undefined) {
211
- features.policies = features.protectEnvFiles;
212
- delete features.protectEnvFiles;
213
- }
214
-
215
- if (envFiles) {
216
- const rule: Record<string, unknown> = {
217
- id: "secret-files",
218
- description: "Files containing secrets (migrated from envFiles)",
219
- protection: "noAccess",
220
- };
221
-
222
- if (envFiles.protectedPatterns) {
223
- rule.patterns = envFiles.protectedPatterns;
224
- }
225
- if (envFiles.allowedPatterns) {
226
- rule.allowedPatterns = envFiles.allowedPatterns;
227
- }
228
- if (envFiles.onlyBlockIfExists !== undefined) {
229
- rule.onlyIfExists = envFiles.onlyBlockIfExists;
230
- }
231
- if (typeof envFiles.blockMessage === "string") {
232
- rule.blockMessage = envFiles.blockMessage;
233
- }
234
-
235
- if (Array.isArray(envFiles.protectedDirectories)) {
236
- const dirs = envFiles.protectedDirectories as Array<
237
- Record<string, unknown>
238
- >;
239
- const patterns = Array.isArray(rule.patterns)
240
- ? ([...rule.patterns] as Array<Record<string, unknown>>)
241
- : [];
242
-
243
- for (const dir of dirs) {
244
- const dirPattern = dir.pattern;
245
- if (typeof dirPattern !== "string" || dirPattern.trim() === "") {
246
- continue;
247
- }
248
-
249
- const normalized = dirPattern.endsWith("/**")
250
- ? dirPattern
251
- : `${dirPattern}/**`;
252
- patterns.push({ pattern: normalized, regex: dir.regex });
253
- }
254
-
255
- if (patterns.length > 0) {
256
- rule.patterns = patterns;
257
- }
258
- }
259
-
260
- if (Array.isArray(envFiles.protectedTools)) {
261
- pendingWarnings.push(
262
- "[guardrails] envFiles.protectedTools is deprecated and has no direct policies equivalent. " +
263
- "The migrated secret-files rule uses protection=noAccess.",
264
- );
265
- }
266
-
267
- if (!Array.isArray(rule.patterns) || rule.patterns.length === 0) {
268
- rule.patterns = [
269
- { pattern: ".env" },
270
- { pattern: ".env.local" },
271
- { pattern: ".env.production" },
272
- { pattern: ".env.prod" },
273
- { pattern: ".dev.vars" },
274
- ];
275
- }
276
-
277
- raw.policies = { rules: [rule] };
278
- delete raw.envFiles;
279
- }
280
-
281
- raw.version = CURRENT_VERSION;
282
- return migrated as GuardrailsConfig;
283
- }
284
-
285
- /**
286
- * Migrate a string[] or PatternConfig[] to PatternConfig[] with regex: true.
287
- * Handles mixed arrays (some already migrated, some still strings).
288
- */
289
- function migrateStringArray(
290
- items: (string | PatternConfig)[],
291
- ): PatternConfig[] {
292
- return items.map((item) => {
293
- if (typeof item === "string") {
294
- return { pattern: item, regex: true };
295
- }
296
- // Already a PatternConfig, ensure regex is set
297
- if (item.regex === undefined) {
298
- return { ...item, regex: true };
299
- }
300
- return item;
301
- });
302
- }
303
-
304
- /**
305
- * Migrate dangerous pattern arrays. Handles both legacy
306
- * `{ pattern: string, description: string }` and already-migrated formats.
307
- */
308
- function migrateDangerousPatterns(
309
- items: (DangerousPattern | { pattern: string; description: string })[],
310
- ): DangerousPattern[] {
311
- return items.map((item) => {
312
- if ("regex" in item && item.regex !== undefined) {
313
- return item as DangerousPattern;
314
- }
315
- return { ...item, regex: true };
316
- });
317
- }
318
-
319
- /**
320
- * Back up a config file before migration.
321
- * Creates `<name>.v0.json` in the same directory.
322
- * Skips if backup already exists.
323
- */
324
- export async function backupConfig(configPath: string): Promise<void> {
325
- const dir = dirname(configPath);
326
- const basename = configPath.split("/").pop() ?? "guardrails.json";
327
- const backupName = basename.replace(".json", ".v0.json");
328
- const backupPath = resolve(dir, backupName);
329
-
330
- try {
331
- await stat(backupPath);
332
- // Backup already exists, skip
333
- } catch {
334
- try {
335
- await copyFile(configPath, backupPath);
336
- } catch (err) {
337
- pendingWarnings.push(`guardrails: could not back up config: ${err}`);
338
- }
339
- }
340
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Module-level warnings queue for messages that arise before any session
3
- * context is available (config loading, migration, pattern compilation).
4
- *
5
- * Drained and reported via ctx.ui.notify in the session_start handler.
6
- */
7
- export const pendingWarnings: string[] = [];
File without changes
File without changes
File without changes
File without changes