@spences10/pi-confirm-destructive 0.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Scott Spence
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @spences10/pi-confirm-destructive
2
+
3
+ [![built with vite+](https://img.shields.io/badge/built%20with-vite+-646CFF)](https://github.com/badlogic/vite-plus)
4
+
5
+ Git-aware destructive action guard for the Pi coding agent.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pi install npm:@spences10/pi-confirm-destructive
11
+ ```
12
+
13
+ Local development from this monorepo:
14
+
15
+ ```bash
16
+ pnpm --filter @spences10/pi-confirm-destructive run build
17
+ pi install ./packages/pi-confirm-destructive
18
+ # or for one run only
19
+ pi -e ./packages/pi-confirm-destructive
20
+ ```
21
+
22
+ ## What it does
23
+
24
+ The extension intercepts Pi `tool_call` and `user_bash` events before
25
+ they run and asks for confirmation when an action may destroy data
26
+ that Git cannot restore.
27
+
28
+ It allows common refactor operations on clean tracked files without
29
+ prompting, while guarding:
30
+
31
+ - untracked file deletes or overwrites
32
+ - tracked files with uncommitted changes
33
+ - broad destructive shell commands such as `find -delete`,
34
+ `git clean`, `rsync --delete`, `truncate`, `dd`, and disk tools
35
+ - destructive Prisma commands such as `prisma migrate reset` and
36
+ `prisma db push --force-reset`
37
+ - destructive database CLI calls through `psql`, `mysql`, `mariadb`,
38
+ or `sqlite3`
39
+ - custom/MCP tools with destructive names such as `delete`, `drop`,
40
+ `execute_write_query`, or `execute_schema_query`
41
+
42
+ In interactive mode the prompt offers:
43
+
44
+ - `Allow once`
45
+ - `Allow similar for this session`
46
+ - `Block`
47
+
48
+ In non-interactive mode destructive actions are blocked by default.
49
+
50
+ ## Using from a custom harness
51
+
52
+ ```ts
53
+ import confirm_destructive from '@spences10/pi-confirm-destructive';
54
+
55
+ // pass `confirm_destructive` as an ExtensionFactory to your Pi runtime
56
+ ```
57
+
58
+ `my-pi` imports this package directly and enables it as the built-in
59
+ confirm-destructive guard.
60
+
61
+ ## Development
62
+
63
+ ```bash
64
+ pnpm --filter @spences10/pi-confirm-destructive run check
65
+ pnpm --filter @spences10/pi-confirm-destructive run test
66
+ pnpm --filter @spences10/pi-confirm-destructive run build
67
+ ```
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,10 @@
1
+ import type { ExtensionAPI, ToolCallEvent } from '@mariozechner/pi-coding-agent';
2
+ export interface DestructiveAction {
3
+ title: string;
4
+ description: string;
5
+ reason: string;
6
+ allow_key: string;
7
+ }
8
+ export declare function assess_bash_command(command: string, cwd?: string): DestructiveAction | undefined;
9
+ export declare function assess_tool_call(event: ToolCallEvent, cwd: string): DestructiveAction | undefined;
10
+ export default function confirm_destructive(pi: ExtensionAPI): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,339 @@
1
+ // Confirm destructive tool calls before they run.
2
+ import { execFileSync } from 'node:child_process';
3
+ import { existsSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ const DESTRUCTIVE_COMMAND_PATTERNS = [
6
+ {
7
+ pattern: /(^|[;&|]\s*)(npx\s+|pnpx\s+|pnpm\s+exec\s+|bunx\s+)?prisma\s+(migrate\s+reset|db\s+push\b[^;&|]*--force-reset|db\s+execute\b)/,
8
+ reason: 'Runs a potentially destructive Prisma database operation',
9
+ allow_key: 'bash:prisma-destructive',
10
+ },
11
+ {
12
+ pattern: /(^|[;&|]\s*)(psql|mysql|mariadb|sqlite3)\b[^;&|]*\b(drop|delete\s+from|truncate|alter\s+table|update\s+\S+\s+set)\b/i,
13
+ reason: 'Runs destructive SQL through a database CLI',
14
+ allow_key: 'bash:db-cli-destructive-sql',
15
+ },
16
+ {
17
+ pattern: /(^|[;&|]\s*)find\b[^;&|]*(\s-delete\b|-exec\s+(sudo\s+)?rm\b)/,
18
+ reason: 'Deletes files found by find',
19
+ allow_key: 'bash:find-delete',
20
+ },
21
+ {
22
+ pattern: /(^|[;&|]\s*)git\s+clean\b[^;&|]*-[a-zA-Z]*[fdx][a-zA-Z]*/,
23
+ reason: 'Deletes untracked files or directories',
24
+ allow_key: 'bash:git-clean',
25
+ },
26
+ {
27
+ pattern: /(^|[;&|]\s*)git\s+(checkout|restore)\b[^;&|]*(\s--\s+\.\s*$|\s\.\s*$)/,
28
+ reason: 'Discards working tree changes',
29
+ allow_key: 'bash:git-discard-all',
30
+ },
31
+ {
32
+ pattern: /(^|[;&|]\s*)rsync\b[^;&|]*\s--delete\b/,
33
+ reason: 'Deletes destination files during sync',
34
+ allow_key: 'bash:rsync-delete',
35
+ },
36
+ {
37
+ pattern: /(^|[;&|]\s*)truncate\b[^;&|]*(\s-s\s*0\b|\s--size\s*=?\s*0\b)/,
38
+ reason: 'Empties file contents',
39
+ allow_key: 'bash:truncate-zero',
40
+ },
41
+ {
42
+ pattern: /(^|[;&|]\s*)dd\b[^;&|]*\bof=/,
43
+ reason: 'Overwrites a device or file with dd',
44
+ allow_key: 'bash:dd-output',
45
+ },
46
+ {
47
+ pattern: /(^|[;&|]\s*)(mkfs|fdisk|parted|wipefs)\b/,
48
+ reason: 'Modifies disks or filesystems',
49
+ allow_key: 'bash:disk-tool',
50
+ },
51
+ ];
52
+ const DESTRUCTIVE_CUSTOM_TOOL_NAME = /(^|[_-])(delete|destroy|drop|remove|archive|execute_write_query|execute_schema_query|bulk_insert)([_-]|$)/i;
53
+ function preview(value, max = 500) {
54
+ const normalized = value.trim().replace(/\s+/g, ' ');
55
+ return normalized.length > max
56
+ ? `${normalized.slice(0, max - 1)}…`
57
+ : normalized;
58
+ }
59
+ function git(args, cwd) {
60
+ try {
61
+ return execFileSync('git', ['-C', cwd, ...args], {
62
+ encoding: 'utf-8',
63
+ stdio: ['ignore', 'pipe', 'ignore'],
64
+ }).trim();
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ }
70
+ function is_git_repo(cwd) {
71
+ return git(['rev-parse', '--is-inside-work-tree'], cwd) === 'true';
72
+ }
73
+ function get_git_recoverability(cwd, path) {
74
+ if (!is_git_repo(cwd))
75
+ return 'not-git';
76
+ const status = git(['status', '--porcelain=v1', '--', path], cwd);
77
+ if (status === undefined)
78
+ return 'not-git';
79
+ if (status.length > 0) {
80
+ return status.split('\n').some((line) => line.startsWith('??'))
81
+ ? 'untracked'
82
+ : 'tracked-dirty';
83
+ }
84
+ const tracked = git(['ls-files', '--', path], cwd);
85
+ return tracked ? 'tracked-clean' : 'untracked';
86
+ }
87
+ function is_git_recoverable(cwd, path) {
88
+ return get_git_recoverability(cwd, path) === 'tracked-clean';
89
+ }
90
+ function parse_shell_words(command) {
91
+ const words = [];
92
+ const pattern = /"((?:\\.|[^"])*)"|'([^']*)'|(\S+)/g;
93
+ let match;
94
+ while ((match = pattern.exec(command))) {
95
+ words.push(match[1] ?? match[2] ?? match[3]);
96
+ }
97
+ return words;
98
+ }
99
+ function extract_command_paths(command, command_name) {
100
+ if (/[;&|`$()<>]/.test(command))
101
+ return undefined;
102
+ const words = parse_shell_words(command);
103
+ const command_index = command_name === 'rm'
104
+ ? words.findIndex((word) => ['rm', 'rmdir', 'unlink', 'shred'].includes(word))
105
+ : words.findIndex((word, index) => word === 'rm' && words[index - 1] === 'git');
106
+ if (command_index === -1)
107
+ return undefined;
108
+ return words
109
+ .slice(command_index + 1)
110
+ .filter((word) => word !== '--' && !word.startsWith('-'));
111
+ }
112
+ function describe_path_risk(cwd, paths) {
113
+ const risky = paths.filter((path) => !is_git_recoverable(cwd, path));
114
+ if (risky.length === 0)
115
+ return 'Deletes git-recoverable files';
116
+ const risks = new Set(risky.map((path) => get_git_recoverability(cwd, path)));
117
+ if (risks.has('untracked')) {
118
+ return 'Deletes untracked files or directories that git cannot restore';
119
+ }
120
+ if (risks.has('tracked-dirty')) {
121
+ return 'Deletes files with uncommitted changes';
122
+ }
123
+ return 'Deletes files outside git recovery';
124
+ }
125
+ function assess_rm_command(command, cwd) {
126
+ if (!/(^|[;&|]\s*)(sudo\s+)?(rm|rmdir|unlink|shred)\b/.test(command)) {
127
+ return undefined;
128
+ }
129
+ const paths = extract_command_paths(command, 'rm');
130
+ if (paths &&
131
+ paths.length > 0 &&
132
+ paths.every((path) => is_git_recoverable(cwd, path))) {
133
+ return undefined;
134
+ }
135
+ const reason = paths?.length
136
+ ? describe_path_risk(cwd, paths)
137
+ : 'Deletes files or directories';
138
+ return {
139
+ title: 'Confirm destructive command?',
140
+ description: `${reason}: ${preview(command)}`,
141
+ reason,
142
+ allow_key: 'bash:rm-risky',
143
+ };
144
+ }
145
+ function assess_git_rm_command(command, cwd) {
146
+ if (!/(^|[;&|]\s*)git\s+rm\b/.test(command))
147
+ return undefined;
148
+ if (/\s-f\b|\s--force\b/.test(command)) {
149
+ return {
150
+ title: 'Confirm forced git removal?',
151
+ description: `Forced git removal can discard uncommitted file changes: ${preview(command)}`,
152
+ reason: 'Force-removes files from git',
153
+ allow_key: 'bash:git-rm-force',
154
+ };
155
+ }
156
+ const paths = extract_command_paths(command, 'git-rm');
157
+ if (paths &&
158
+ paths.length > 0 &&
159
+ paths.every((path) => is_git_recoverable(cwd, path))) {
160
+ return undefined;
161
+ }
162
+ const reason = paths?.length
163
+ ? describe_path_risk(cwd, paths)
164
+ : 'Deletes tracked files from git';
165
+ return {
166
+ title: 'Confirm git removal?',
167
+ description: `${reason}: ${preview(command)}`,
168
+ reason,
169
+ allow_key: 'bash:git-rm-risky',
170
+ };
171
+ }
172
+ function assess_git_reset_hard(command, cwd) {
173
+ if (!/(^|[;&|]\s*)git\s+reset\b[^;&|]*--hard\b/.test(command)) {
174
+ return undefined;
175
+ }
176
+ if (git(['status', '--porcelain=v1'], cwd) === '')
177
+ return undefined;
178
+ return {
179
+ title: 'Confirm hard reset?',
180
+ description: `This can discard uncommitted tracked changes: ${preview(command)}`,
181
+ reason: 'Discards uncommitted tracked changes',
182
+ allow_key: 'bash:git-reset-hard',
183
+ };
184
+ }
185
+ export function assess_bash_command(command, cwd = process.cwd()) {
186
+ const normalized = command.trim();
187
+ if (!normalized)
188
+ return undefined;
189
+ const specific = assess_rm_command(normalized, cwd) ??
190
+ assess_git_rm_command(normalized, cwd) ??
191
+ assess_git_reset_hard(normalized, cwd);
192
+ if (specific)
193
+ return specific;
194
+ const match = DESTRUCTIVE_COMMAND_PATTERNS.find(({ pattern }) => pattern.test(normalized));
195
+ if (!match)
196
+ return undefined;
197
+ return {
198
+ title: 'Confirm destructive command?',
199
+ description: `${match.reason}: ${preview(normalized)}`,
200
+ reason: match.reason,
201
+ allow_key: match.allow_key,
202
+ };
203
+ }
204
+ function assess_file_write(cwd, path) {
205
+ if (typeof path !== 'string' || !path.trim())
206
+ return undefined;
207
+ const absolute = resolve(cwd, path);
208
+ if (!existsSync(absolute))
209
+ return undefined;
210
+ if (is_git_recoverable(cwd, path))
211
+ return undefined;
212
+ const reason = get_git_recoverability(cwd, path) === 'tracked-dirty'
213
+ ? 'Overwrites a file with uncommitted changes'
214
+ : 'Overwrites an untracked file git cannot restore';
215
+ return {
216
+ title: 'Confirm file overwrite?',
217
+ description: `${reason}: ${path}`,
218
+ reason,
219
+ allow_key: 'write:risky-overwrite',
220
+ };
221
+ }
222
+ function assess_file_edit(cwd, input) {
223
+ const path = typeof input.path === 'string' ? input.path : undefined;
224
+ const edits = Array.isArray(input.edits) ? input.edits : [];
225
+ let removed_chars = 0;
226
+ let added_chars = 0;
227
+ for (const edit of edits) {
228
+ if (!edit || typeof edit !== 'object')
229
+ continue;
230
+ const old_text = edit.oldText;
231
+ const new_text = edit.newText;
232
+ if (typeof old_text === 'string')
233
+ removed_chars += old_text.length;
234
+ if (typeof new_text === 'string')
235
+ added_chars += new_text.length;
236
+ }
237
+ if (removed_chars === 0 || removed_chars - added_chars < 200) {
238
+ return undefined;
239
+ }
240
+ if (path && is_git_recoverable(cwd, path))
241
+ return undefined;
242
+ return {
243
+ title: 'Confirm large content removal?',
244
+ description: `This edit removes ${removed_chars - added_chars} more characters than it adds${path ? ` in ${path}` : ''}.`,
245
+ reason: path
246
+ ? 'Removes substantial content from a file git cannot fully restore'
247
+ : 'Removes substantial file content',
248
+ allow_key: 'edit:large-removal-risky',
249
+ };
250
+ }
251
+ function assess_custom_tool(event) {
252
+ if (!DESTRUCTIVE_CUSTOM_TOOL_NAME.test(event.toolName)) {
253
+ return undefined;
254
+ }
255
+ const input = event.input;
256
+ const query = typeof input.query === 'string'
257
+ ? `\n\nQuery: ${preview(input.query)}`
258
+ : '';
259
+ return {
260
+ title: 'Confirm destructive tool call?',
261
+ description: `Tool ${event.toolName} appears destructive.${query}`,
262
+ reason: `Potentially destructive tool: ${event.toolName}`,
263
+ allow_key: `tool:${event.toolName}`,
264
+ };
265
+ }
266
+ export function assess_tool_call(event, cwd) {
267
+ if (event.toolName === 'bash') {
268
+ const command = event.input.command;
269
+ return typeof command === 'string'
270
+ ? assess_bash_command(command, cwd)
271
+ : undefined;
272
+ }
273
+ if (event.toolName === 'write') {
274
+ return assess_file_write(cwd, event.input.path);
275
+ }
276
+ if (event.toolName === 'edit') {
277
+ return assess_file_edit(cwd, event.input);
278
+ }
279
+ return assess_custom_tool(event);
280
+ }
281
+ async function confirm_action(action, ctx) {
282
+ if (!ctx.hasUI)
283
+ return 'block';
284
+ const choice = await ctx.ui.select(`${action.title}\n${action.description}`, ['Allow once', 'Allow similar for this session', 'Block']);
285
+ if (choice === 'Allow once')
286
+ return 'allow';
287
+ if (choice === 'Allow similar for this session') {
288
+ return 'allow-similar';
289
+ }
290
+ ctx.ui.notify('Destructive action blocked', 'info');
291
+ return 'block';
292
+ }
293
+ function blocked_reason(action) {
294
+ return `Blocked destructive action: ${action.reason}`;
295
+ }
296
+ function blocked_bash_result(action) {
297
+ return {
298
+ output: `${blocked_reason(action)}\n`,
299
+ exitCode: 130,
300
+ cancelled: false,
301
+ truncated: false,
302
+ };
303
+ }
304
+ export default async function confirm_destructive(pi) {
305
+ const allowed_for_session = new Set();
306
+ function is_allowed(action) {
307
+ return allowed_for_session.has(action.allow_key);
308
+ }
309
+ async function should_allow(action, ctx) {
310
+ if (is_allowed(action))
311
+ return true;
312
+ const decision = await confirm_action(action, ctx);
313
+ if (decision === 'allow-similar') {
314
+ allowed_for_session.add(action.allow_key);
315
+ return true;
316
+ }
317
+ return decision === 'allow';
318
+ }
319
+ pi.on('tool_call', async (event, ctx) => {
320
+ const action = assess_tool_call(event, ctx.cwd);
321
+ if (!action)
322
+ return;
323
+ if (await should_allow(action, ctx))
324
+ return;
325
+ return {
326
+ block: true,
327
+ reason: blocked_reason(action),
328
+ };
329
+ });
330
+ pi.on('user_bash', async (event, ctx) => {
331
+ const action = assess_bash_command(event.command, event.cwd);
332
+ if (!action)
333
+ return;
334
+ if (await should_allow(action, ctx))
335
+ return;
336
+ return { result: blocked_bash_result(action) };
337
+ });
338
+ }
339
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@spences10/pi-confirm-destructive",
3
+ "version": "0.0.1",
4
+ "description": "Pi extension that confirms destructive actions before they run",
5
+ "keywords": [
6
+ "confirm",
7
+ "destructive",
8
+ "guard",
9
+ "pi",
10
+ "pi-package",
11
+ "safety"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Scott Spence <me@scottspence.com>",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/spences10/my-pi.git",
18
+ "directory": "packages/pi-confirm-destructive"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "type": "module",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "@mariozechner/pi-coding-agent": "^0.70.2"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^25.6.0",
35
+ "typescript": "^6.0.0",
36
+ "vitest": "^4.1.5"
37
+ },
38
+ "engines": {
39
+ "node": ">=22.0.0"
40
+ },
41
+ "pi": {
42
+ "extensions": [
43
+ "./dist/index.js"
44
+ ]
45
+ },
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.build.json",
48
+ "check": "tsc --noEmit -p tsconfig.json",
49
+ "test": "vitest run",
50
+ "test:watch": "vitest"
51
+ }
52
+ }