@aliou/pi-guardrails 0.5.3 → 0.6.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 +63 -94
- package/config-schema.ts +37 -25
- package/config.ts +108 -141
- package/events.ts +1 -6
- package/glob-expander.ts +128 -0
- package/hooks/index.ts +0 -6
- package/hooks/permission-gate.ts +243 -142
- package/hooks/protect-env-files.ts +120 -43
- package/index.ts +6 -3
- package/matching.ts +119 -0
- package/migration.ts +135 -0
- package/package.json +7 -4
- package/pattern-editor.ts +61 -10
- package/settings-command.ts +247 -426
- package/shell-utils.ts +139 -0
- package/array-editor.ts +0 -213
- package/hooks/enforce-package-manager.ts +0 -96
- package/hooks/prevent-brew.ts +0 -41
- package/hooks/prevent-python.ts +0 -45
- package/sectioned-settings.ts +0 -345
package/pattern-editor.ts
CHANGED
|
@@ -8,16 +8,17 @@ import {
|
|
|
8
8
|
} from "@mariozechner/pi-tui";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* A submenu component for editing an array of {pattern, description} objects.
|
|
11
|
+
* A submenu component for editing an array of {pattern, description, regex?} objects.
|
|
12
12
|
*
|
|
13
13
|
* List mode: navigate, delete with 'd', add with 'a', edit with 'e'/Enter.
|
|
14
|
-
* Form mode:
|
|
15
|
-
* Enter to submit, Escape to cancel.
|
|
14
|
+
* Form mode: three-field form (pattern + description + regex toggle),
|
|
15
|
+
* Tab to switch fields, Enter to submit, Escape to cancel.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
export interface PatternItem {
|
|
19
19
|
pattern: string;
|
|
20
20
|
description: string;
|
|
21
|
+
regex?: boolean;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface PatternEditorOptions {
|
|
@@ -26,10 +27,12 @@ export interface PatternEditorOptions {
|
|
|
26
27
|
theme: SettingsListTheme;
|
|
27
28
|
onSave: (items: PatternItem[]) => void;
|
|
28
29
|
onDone: () => void;
|
|
30
|
+
/** Context hint for the pattern field label. */
|
|
31
|
+
context?: "file" | "command";
|
|
29
32
|
maxVisible?: number;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
type Field = "pattern" | "description";
|
|
35
|
+
type Field = "pattern" | "description" | "regex";
|
|
33
36
|
|
|
34
37
|
export class PatternEditor implements Component {
|
|
35
38
|
private items: PatternItem[];
|
|
@@ -37,6 +40,7 @@ export class PatternEditor implements Component {
|
|
|
37
40
|
private theme: SettingsListTheme;
|
|
38
41
|
private onSave: (items: PatternItem[]) => void;
|
|
39
42
|
private onDone: () => void;
|
|
43
|
+
private context: "file" | "command";
|
|
40
44
|
private selectedIndex = 0;
|
|
41
45
|
private maxVisible: number;
|
|
42
46
|
private mode: "list" | "add" | "edit" = "list";
|
|
@@ -46,6 +50,7 @@ export class PatternEditor implements Component {
|
|
|
46
50
|
private patternInput: Input;
|
|
47
51
|
private descriptionInput: Input;
|
|
48
52
|
private activeField: Field = "pattern";
|
|
53
|
+
private regexEnabled = false;
|
|
49
54
|
|
|
50
55
|
constructor(options: PatternEditorOptions) {
|
|
51
56
|
this.items = [...options.items];
|
|
@@ -53,6 +58,7 @@ export class PatternEditor implements Component {
|
|
|
53
58
|
this.theme = options.theme;
|
|
54
59
|
this.onSave = options.onSave;
|
|
55
60
|
this.onDone = options.onDone;
|
|
61
|
+
this.context = options.context ?? "command";
|
|
56
62
|
this.maxVisible = options.maxVisible ?? 10;
|
|
57
63
|
|
|
58
64
|
this.patternInput = new Input();
|
|
@@ -72,7 +78,13 @@ export class PatternEditor implements Component {
|
|
|
72
78
|
return;
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
// If on description field
|
|
81
|
+
// If on description field, move to regex toggle
|
|
82
|
+
if (this.activeField === "description") {
|
|
83
|
+
this.activeField = "regex";
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// If on regex field, submit
|
|
76
88
|
this.submitForm();
|
|
77
89
|
}
|
|
78
90
|
|
|
@@ -89,6 +101,9 @@ export class PatternEditor implements Component {
|
|
|
89
101
|
pattern,
|
|
90
102
|
description: description || pattern,
|
|
91
103
|
};
|
|
104
|
+
if (this.regexEnabled) {
|
|
105
|
+
item.regex = true;
|
|
106
|
+
}
|
|
92
107
|
|
|
93
108
|
if (this.mode === "edit") {
|
|
94
109
|
this.items[this.editIndex] = item;
|
|
@@ -105,6 +120,7 @@ export class PatternEditor implements Component {
|
|
|
105
120
|
this.mode = "list";
|
|
106
121
|
this.editIndex = -1;
|
|
107
122
|
this.activeField = "pattern";
|
|
123
|
+
this.regexEnabled = false;
|
|
108
124
|
this.patternInput.setValue("");
|
|
109
125
|
this.descriptionInput.setValue("");
|
|
110
126
|
}
|
|
@@ -118,6 +134,7 @@ export class PatternEditor implements Component {
|
|
|
118
134
|
this.activeField = "pattern";
|
|
119
135
|
this.patternInput.setValue(item.pattern);
|
|
120
136
|
this.descriptionInput.setValue(item.description);
|
|
137
|
+
this.regexEnabled = item.regex ?? false;
|
|
121
138
|
}
|
|
122
139
|
|
|
123
140
|
private deleteSelected() {
|
|
@@ -167,7 +184,8 @@ export class PatternEditor implements Component {
|
|
|
167
184
|
const prefix = isSelected ? this.theme.cursor : " ";
|
|
168
185
|
const prefixWidth = visibleWidth(prefix);
|
|
169
186
|
const maxItemWidth = width - prefixWidth - 2;
|
|
170
|
-
const
|
|
187
|
+
const regexTag = item.regex ? " [regex]" : "";
|
|
188
|
+
const display = `${item.description} (${item.pattern})${regexTag}`;
|
|
171
189
|
const text = this.theme.value(
|
|
172
190
|
truncateToWidth(display, maxItemWidth, ""),
|
|
173
191
|
isSelected,
|
|
@@ -197,13 +215,16 @@ export class PatternEditor implements Component {
|
|
|
197
215
|
|
|
198
216
|
const patternActive = this.activeField === "pattern";
|
|
199
217
|
const descActive = this.activeField === "description";
|
|
218
|
+
const regexActive = this.activeField === "regex";
|
|
200
219
|
|
|
201
220
|
// Title
|
|
202
221
|
lines.push(this.theme.hint(isEdit ? " Edit pattern:" : " New pattern:"));
|
|
203
222
|
lines.push("");
|
|
204
223
|
|
|
205
224
|
// Pattern field
|
|
206
|
-
const
|
|
225
|
+
const patternHint =
|
|
226
|
+
this.context === "file" ? "(glob or regex)" : "(substring or regex)";
|
|
227
|
+
const patternLabel = ` Pattern ${patternHint}:`;
|
|
207
228
|
lines.push(
|
|
208
229
|
patternActive
|
|
209
230
|
? this.theme.label(patternLabel, true)
|
|
@@ -222,8 +243,21 @@ export class PatternEditor implements Component {
|
|
|
222
243
|
lines.push(` ${this.descriptionInput.render(inputWidth).join("")}`);
|
|
223
244
|
lines.push("");
|
|
224
245
|
|
|
246
|
+
// Regex toggle
|
|
247
|
+
const regexLabel = " Regex:";
|
|
248
|
+
const regexValue = this.regexEnabled ? "on" : "off";
|
|
249
|
+
const regexDisplay = `${regexLabel} ${regexValue}`;
|
|
225
250
|
lines.push(
|
|
226
|
-
|
|
251
|
+
regexActive
|
|
252
|
+
? this.theme.label(regexDisplay, true)
|
|
253
|
+
: this.theme.hint(regexDisplay),
|
|
254
|
+
);
|
|
255
|
+
lines.push("");
|
|
256
|
+
|
|
257
|
+
lines.push(
|
|
258
|
+
this.theme.hint(
|
|
259
|
+
" Tab: switch field · Space: toggle regex · Enter: next/submit · Esc: cancel",
|
|
260
|
+
),
|
|
227
261
|
);
|
|
228
262
|
|
|
229
263
|
return lines;
|
|
@@ -251,6 +285,7 @@ export class PatternEditor implements Component {
|
|
|
251
285
|
} else if (data === "a" || data === "A") {
|
|
252
286
|
this.mode = "add";
|
|
253
287
|
this.activeField = "pattern";
|
|
288
|
+
this.regexEnabled = false;
|
|
254
289
|
this.patternInput.setValue("");
|
|
255
290
|
this.descriptionInput.setValue("");
|
|
256
291
|
} else if (data === "e" || data === "E" || matchesKey(data, Key.enter)) {
|
|
@@ -264,8 +299,12 @@ export class PatternEditor implements Component {
|
|
|
264
299
|
|
|
265
300
|
private handleFormInput(data: string) {
|
|
266
301
|
if (matchesKey(data, Key.tab) || matchesKey(data, Key.shift("tab"))) {
|
|
267
|
-
|
|
268
|
-
|
|
302
|
+
const fields: Field[] = ["pattern", "description", "regex"];
|
|
303
|
+
const idx = fields.indexOf(this.activeField);
|
|
304
|
+
const dir = matchesKey(data, Key.shift("tab")) ? -1 : 1;
|
|
305
|
+
this.activeField = fields[
|
|
306
|
+
(idx + dir + fields.length) % fields.length
|
|
307
|
+
] as Field;
|
|
269
308
|
return;
|
|
270
309
|
}
|
|
271
310
|
|
|
@@ -274,6 +313,18 @@ export class PatternEditor implements Component {
|
|
|
274
313
|
return;
|
|
275
314
|
}
|
|
276
315
|
|
|
316
|
+
// Regex toggle: space toggles when on regex field
|
|
317
|
+
if (this.activeField === "regex") {
|
|
318
|
+
if (data === " " || matchesKey(data, Key.enter)) {
|
|
319
|
+
this.regexEnabled = !this.regexEnabled;
|
|
320
|
+
}
|
|
321
|
+
// Enter on regex field submits if we already have a pattern
|
|
322
|
+
if (matchesKey(data, Key.enter) && this.patternInput.getValue().trim()) {
|
|
323
|
+
this.submitForm();
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
277
328
|
// Delegate to active input
|
|
278
329
|
const activeInput =
|
|
279
330
|
this.activeField === "pattern"
|