@giuseppe.trisciuoglio/pi-prevent-destructive-commands 1.0.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.
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: Typecheck & Test (Node ${{ matrix.node-version }})
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ node-version: [22.x, 24.x]
20
+
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Setup Node.js ${{ matrix.node-version }}
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: ${{ matrix.node-version }}
29
+
30
+ - name: Install dependencies
31
+ run: npm install
32
+
33
+ - name: Typecheck
34
+ run: npm run typecheck
35
+
36
+ - name: Run tests
37
+ run: npm test
@@ -0,0 +1,47 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ name: Publish to npm
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ contents: read
16
+ id-token: write
17
+
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v6
21
+
22
+ - name: Setup Node.js
23
+ uses: actions/setup-node@v6
24
+ with:
25
+ node-version: 24.x
26
+ registry-url: https://registry.npmjs.org
27
+
28
+ - name: Verify release tag matches package.json version
29
+ run: |
30
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
31
+ PKG_VERSION=$(node -p "require('./package.json').version")
32
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
33
+ echo "::error::Release tag ($GITHUB_REF_NAME) does not match package.json version ($PKG_VERSION)"
34
+ exit 1
35
+ fi
36
+
37
+ - name: Install dependencies
38
+ run: npm ci
39
+
40
+ - name: Typecheck
41
+ run: npm run typecheck
42
+
43
+ - name: Run tests
44
+ run: npm test
45
+
46
+ - name: Publish to npm
47
+ run: npm publish --access public
package/CHANGELOG.md ADDED
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Fixed
11
+ - `cd` within an analyzed command chain (e.g. `cd /; rm etc/passwd`) is now tracked: relative paths in subsequent path-sensitive commands resolve against the effective directory instead of the untouched real cwd, while the *original* cwd remains the safety boundary. `( … )` subshell scoping is respected — a `cd` inside parentheses no longer leaks out. A `cd` to a statically unresolvable target (`cd "$VAR"`, `cd -`) causes subsequent path-sensitive commands to be blocked conservatively.
12
+ - `xargs`/`parallel` delegating to a path-sensitive command (`rm`, `rmdir`, `shred`, ...) with no explicit target token (e.g. `echo x | xargs rm`) is now blocked: the real arguments arrive via stdin at runtime and can't be statically verified. An explicit target is still checked normally against the working directory.
13
+
14
+ ### Added
15
+ - Nx workspace protection: blocks the agent from modifying or deleting existing `package.json`, `tsconfig.json`, `tsconfig.base.json`, `tsconfig.lib.json`, and `tsconfig.spec.json` files anywhere beneath a workspace with `nx.json`. Missing files remain creatable. The guard covers direct file tools, patches, common shell mutations, and package-manager dependency commands.
16
+ - CI GitHub Actions workflow: typecheck and smoke tests on Node.js 22 and 24 for every push/PR to `main`.
17
+ - Publish GitHub Actions workflow: publishes to npm when a GitHub Release is published, after verifying the release tag matches `package.json` and re-running typecheck/tests.
18
+ - End-to-end installation test (`test/e2e-install-test.ts`, `npm run test:e2e`): uses the real `@earendil-works/pi-coding-agent` package to verify manual installation and `pi install` are both discovered and loaded correctly, and that the loaded `tool_call` handler blocks/allows commands as expected. `npm test` now runs the smoke suite and the e2e test.
19
+
20
+ ### Fixed
21
+ - `package.json`'s `pi` manifest declared an `entryPoint` field that pi's extension loader does not read, so the extension was never discovered when manually installed by cloning into `<agentDir>/extensions/prevent-destructive-commands` as documented in the README. Replaced it with the `pi.extensions` array field that pi actually resolves.
22
+
23
+ ### Changed
24
+ - Reorganized source files into `src/` and tests into `test/`.
25
+ - Split `checker.ts` into a slim recursive dispatcher plus per-category rule handlers under `src/rules/` (`git.ts`, `docker.ts`, `aws.ts`, `file-reading.ts`, `path-sensitive.ts`, `path-utils.ts`, `types.ts`). No behavior changes.
26
+
27
+ ## [1.0.0] - 2025-07-16
28
+
29
+ ### Added
30
+ - Initial release of `prevent-destructive-commands` extension for pi.
31
+ - Unconditional hard-block of destructive bash commands before execution.
32
+ - Support for destructive Git operations: `git reset --hard`, `git clean`, `git push --force`, `git branch -D`, `git rebase`, `git filter-branch`, `git filter-repo`, and more.
33
+ - Support for destructive Docker operations: `docker rm`, `docker rmi`, `docker container/volume/image/network rm`, `docker * prune`, `docker compose down -v`, and more.
34
+ - Support for destructive AWS CLI operations: `aws s3 rm`, `aws ec2 terminate-instances`, `aws rds delete-db-instance`, `aws cloudformation delete-stack`, and more (50+ subcommands).
35
+ - Path-sensitive protection for `rm`, `rmdir`, `unlink`, `shred`: blocks targets outside the working directory.
36
+ - Optional sensitive file read protection (`cat`, `grep`, etc. on `.env`, SSH keys, credentials) — disabled by default due to false positives.
37
+ - Recursive analysis through command wrappers: `sudo`, `env`, `timeout`, `nice`, `nohup`, `ionice`, `time`.
38
+ - Recursive analysis through shell invocations: `bash -c "..."`, `sh -c "..."`, etc.
39
+ - Recursive analysis through `find -exec`, `xargs`, `parallel`.
40
+ - Recursive analysis through quoted command wrappers: `watch`, `strace`, `ltrace`.
41
+ - Pipeline and concatenation support: `|`, `&&`, `||`, `;`.
42
+ - Configurable behavior flags in `config.ts`.
43
+ - Comprehensive smoke test suite with 70+ test cases.
44
+ - Full TypeScript support with type definitions.
45
+
46
+ ### Security
47
+ - Hard-block design: the agent receives a reason and must find an alternative.
48
+ - No interactive confirmation — protection works in non-interactive modes (`-p`, JSON, RPC).
49
+ - Maximum nesting depth limit (5) to prevent obfuscation attacks.
@@ -0,0 +1,105 @@
1
+ # Contributing to prevent-destructive-commands
2
+
3
+ Thank you for your interest in contributing! This document provides guidelines for contributing to this project.
4
+
5
+ ## Code of Conduct
6
+
7
+ Be respectful, constructive, and inclusive in all interactions.
8
+
9
+ ## How to Contribute
10
+
11
+ ### Reporting Issues
12
+
13
+ - Check if the issue already exists before creating a new one.
14
+ - Provide a clear description, steps to reproduce, and expected vs. actual behavior.
15
+ - Include the command that triggered (or failed to trigger) the block.
16
+ - Mention your pi version and Node.js version.
17
+
18
+ ### Suggesting Enhancements
19
+
20
+ - Open an issue with the "enhancement" label.
21
+ - Describe the use case and the benefit.
22
+ - If proposing a new blocked command category, explain why it should be considered destructive.
23
+
24
+ ### Pull Requests
25
+
26
+ 1. Fork the repository and create a feature branch.
27
+ 2. Make your changes with clear, focused commits.
28
+ 3. Run the smoke tests: `npm test` (or `npx tsx test/smoke-test.ts`).
29
+ 4. Update the README.md if your change affects user-facing behavior.
30
+ 5. Update CHANGELOG.md under the "Unreleased" section.
31
+ 6. Submit a pull request with a clear description.
32
+
33
+ ## Development Setup
34
+
35
+ ```bash
36
+ # Clone the repository
37
+ git clone https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands.git
38
+ cd pi-prevent-destructive-commands
39
+
40
+ # Install dependencies
41
+ npm install
42
+
43
+ # Run smoke tests
44
+ npm test
45
+
46
+ # Type check
47
+ npm run typecheck
48
+ ```
49
+
50
+ ## Project Structure
51
+
52
+ ```
53
+ prevent-destructive-commands/
54
+ ├── src/
55
+ │ ├── index.ts # Extension entry point (factory + tool_call hook)
56
+ │ ├── config.ts # Blacklists and behavior flags
57
+ │ ├── tokenizer.ts # Shell tokenizer (shlex-like)
58
+ │ ├── checker.ts # Recursive command walker (wrappers/shell/find/xargs)
59
+ │ └── rules/ # Per-category destructive-command handlers
60
+ │ ├── types.ts
61
+ │ ├── path-utils.ts
62
+ │ ├── git.ts
63
+ │ ├── docker.ts
64
+ │ ├── aws.ts
65
+ │ ├── file-reading.ts
66
+ │ └── path-sensitive.ts
67
+ ├── test/
68
+ │ ├── smoke-test.ts # Standalone test suite
69
+ │ └── e2e-install-test.ts # End-to-end: verifies real installation/discovery by pi
70
+ ├── tsconfig.json # TypeScript configuration
71
+ └── package.json # Package metadata
72
+ ```
73
+
74
+ ## Adding New Blocked Commands
75
+
76
+ To add a new command category:
77
+
78
+ 1. Add the command patterns to the appropriate set in `src/config.ts`.
79
+ 2. Add the checker logic to a new or existing handler in `src/rules/`, following the existing handler pattern (see `src/rules/git.ts` or `src/rules/docker.ts`), and wire it into the dispatch loop in `src/checker.ts`.
80
+ 3. Add test cases to `test/smoke-test.ts`.
81
+ 4. Update the README.md documentation.
82
+
83
+ ## Style Guide
84
+
85
+ - Use English for all code comments and documentation.
86
+ - Use 2-space indentation (tabs converted to 2 spaces).
87
+ - Prefer explicit types over implicit ones.
88
+ - Keep functions focused and modular.
89
+
90
+ ## Continuous Integration
91
+
92
+ Every push and pull request to `main` runs the [CI workflow](.github/workflows/ci.yml), which typechecks and runs the smoke test suite and the end-to-end installation test on Node.js 22 and 24.
93
+
94
+ ## Releasing
95
+
96
+ Publishing to npm is handled by the [Publish workflow](.github/workflows/publish.yml) and is restricted to maintainers:
97
+
98
+ 1. Bump `version` in `package.json` and move the relevant `CHANGELOG.md` entries from "Unreleased" to a new version section.
99
+ 2. Commit and merge the version bump to `main`.
100
+ 3. Create a GitHub Release with a tag matching `v<version>` (e.g. `v1.1.0`) for the merged commit.
101
+ 4. Publishing the Release triggers the workflow, which verifies the tag matches `package.json`, re-runs typecheck/tests, and publishes to npm through the trusted GitHub Actions publisher.
102
+
103
+ ## License
104
+
105
+ By contributing, you agree that your contributions will be licensed under the MIT License.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Giuseppe Trisciuoglio
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,239 @@
1
+ # Prevent Destructive Commands
2
+
3
+ [![CI](https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands/actions/workflows/ci.yml/badge.svg)](https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands/actions/workflows/ci.yml)
4
+ [![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
6
+ [![pi Extension](https://img.shields.io/badge/pi-extension-purple.svg)](https://pi.dev)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.4-blue.svg)](https://www.typescriptlang.org/)
8
+
9
+ > **Unconditionally blocks destructive bash commands before execution.** A pi extension that guards your codebase against accidental data loss from `rm`, `git reset --hard`, `docker rm`, `aws delete` operations, and more.
10
+
11
+ A faithful port of Claude's [`prevent-destructive-commands.py`](https://github.com/giuseppe-trisciuoglio/developer-kit/blob/main/plugins/developer-kit-core/hooks/prevent-destructive-commands.py) hook, adapted for the [pi coding agent](https://pi.dev). Same rule set, same recursive tokenizer — same peace of mind.
12
+
13
+ ---
14
+
15
+ ## Features
16
+
17
+ - **Hard-block protection** — Dangerous commands are blocked unconditionally. The agent receives a clear reason and must find a safe alternative.
18
+ - **Works in all modes** — Protection is active even in non-interactive sessions (`-p`, JSON, RPC).
19
+ - **Recursive analysis** — Traverses command wrappers, shell invocations, pipelines, and nested commands to catch obfuscated attacks.
20
+ - **Nx configuration guard** — In Nx workspaces, existing `package.json` and TypeScript configuration files are immutable to the agent; missing ones can still be created.
21
+ - **Configurable** — Tune protection levels via simple flags in `src/config.ts`.
22
+ - **Zero dependencies** — Lightweight, fast, and self-contained.
23
+ - **79+ test cases** — Comprehensive smoke test suite validates all blocking rules.
24
+
25
+ ## What Gets Blocked
26
+
27
+ All rules are defined in [`src/config.ts`](src/config.ts) and can be customized.
28
+
29
+ | Category | Examples |
30
+ |----------|----------|
31
+ | **Destructive Git** | `git reset --hard`, `git clean`, `git push --force` / `-f` / `--delete`, `git branch -D`, `git tag -d`, `git checkout -f`, `git rebase`, `git filter-branch`, `git filter-repo`, `git reflog expire`, `git update-ref -d` |
32
+ | **Git add/commit** | `git add`, `git commit` *(see `ENABLE_GIT_ADD_COMMIT_BLOCK` flag)* |
33
+ | **rm / path-sensitive** | `rm`, `rmdir`, `shred`, `unlink` targeting paths **outside** the working directory (e.g., `/etc`, `~`, `..`). Targets inside cwd are allowed. |
34
+ | **Destructive Docker** | `docker rm` / `rmi`, `docker container/image/volume/network rm`, `docker * prune`, `docker compose down -v`, `docker compose rm`, `docker context rm`, `docker swarm leave --force` |
35
+ | **Destructive AWS CLI** | `aws s3 rm`, `aws ec2 terminate-instances`, `aws rds delete-db-instance`, `aws cloudformation delete-stack`, and 50+ more subcommands (full list in `src/config.ts`) |
36
+ | **Sensitive file reads** | `cat`, `grep`, etc. on `.env`, SSH keys, `.pem` files — **disabled by default** via `ENABLE_SENSITIVE_FILE_CHECK` *(see Configuration)* |
37
+ | **Existing Nx configuration** | `package.json`, `tsconfig.json`, `tsconfig.base.json`, `tsconfig.lib.json`, and `tsconfig.spec.json` anywhere below a workspace containing `nx.json`. Creating a missing file is allowed; changing or deleting an existing file is blocked. |
38
+
39
+ ### Recursive Analysis
40
+
41
+ The analyzer traverses common command wrappers and nested structures so a destructive command can't hide:
42
+
43
+ - **Wrappers**: `sudo`, `env`, `timeout`, `nice`, `nohup`, `ionice`, `time`
44
+ - **Shell invocations**: `bash -c "..."`, `sh -c "..."`, `zsh -c "..."`
45
+ - **Execution delegation**: `find -exec`, `xargs`, `parallel`
46
+ - **Quoted wrappers**: `watch "rm foo"`, `strace "..."`
47
+ - **Pipelines & chains**: `|`, `&&`, `||`, `;`
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ ### Via pi Marketplace
54
+
55
+ ```bash
56
+ pi install @giuseppetrisciuoglio/pi-prevent-destructive-commands
57
+ ```
58
+
59
+ ### Manual Installation
60
+
61
+ 1. Clone this repository into your pi extensions directory:
62
+
63
+ ```bash
64
+ git clone https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands.git \
65
+ ~/.pi/agent/extensions/prevent-destructive-commands
66
+ ```
67
+
68
+ 2. Reload pi to discover the extension:
69
+
70
+ ```bash
71
+ pi /reload
72
+ ```
73
+
74
+ The extension is automatically discovered by pi in all projects.
75
+
76
+ ---
77
+
78
+ ## Configuration
79
+
80
+ Edit the constants in [`src/config.ts`](src/config.ts) to tune protection:
81
+
82
+ | Flag | Default | Description |
83
+ |------|---------|-------------|
84
+ | `ENABLE_GIT_ADD_COMMIT_BLOCK` | `true` | Blocks `git add` and `git commit`. Set to `false` if you want the agent to create commits autonomously. |
85
+ | `ENABLE_SENSITIVE_FILE_CHECK` | `false` | Blocks reading of sensitive files (`.env`, SSH keys, credentials). Disabled by default due to false positives from substring matching (`config` matches `tsconfig`, `vite.config`; `.env` matches `.environment.ts`). Enable only if needed and consider refining `SENSITIVE_FILE_PATTERNS`. |
86
+ | `MAX_NESTING_DEPTH` | `5` | Maximum command nesting depth before treating as obfuscated. |
87
+
88
+ After any change, reload the extension:
89
+
90
+ ```bash
91
+ pi /reload
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Project Structure
97
+
98
+ ```
99
+ prevent-destructive-commands/
100
+ ├── src/
101
+ │ ├── index.ts # Extension entry point (factory + tool_call hook)
102
+ │ ├── config.ts # Blacklists and behavior flags — tune protection here
103
+ │ ├── tokenizer.ts # Shell tokenizer (shlex-like)
104
+ │ ├── checker.ts # Recursive command walker (wrappers/shell/find/xargs)
105
+ │ ├── migration-guard.ts
106
+ │ ├── nx-guard.ts # Protects existing Nx package and TypeScript configuration files
107
+ │ └── rules/ # Per-category destructive-command handlers
108
+ │ ├── types.ts # Shared CheckResult type + helpers
109
+ │ ├── path-utils.ts # cwd-relative path resolution
110
+ │ ├── git.ts # git reset --hard, push --force, ...
111
+ │ ├── docker.ts # docker rm, system prune, ...
112
+ │ ├── aws.ts # aws s3 rm, ec2 terminate-instances, ...
113
+ │ ├── file-reading.ts # sensitive file read detection
114
+ │ └── path-sensitive.ts # rm/rmdir/... outside-cwd detection
115
+ ├── test/
116
+ │ ├── smoke-test.ts # Standalone test suite (79+ cases)
117
+ │ ├── migration-guard-test.ts
118
+ │ ├── nx-guard-test.ts # Verifies Nx configuration protection and allowed creation
119
+ │ └── e2e-install-test.ts # End-to-end: verifies real installation/discovery by pi
120
+ ├── tsconfig.json # TypeScript configuration
121
+ ├── package.json # Package metadata for pi marketplace
122
+ └── README.md # This file
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Testing
128
+
129
+ Run the full test suite (command safety, migration, Nx, and end-to-end checks):
130
+
131
+ ```bash
132
+ npm test
133
+ ```
134
+
135
+ ### Smoke tests
136
+
137
+ Standalone tests for the tokenizer/checker logic (no dependency on pi itself):
138
+
139
+ ```bash
140
+ npm run test:smoke
141
+ npm run test:nx-guard
142
+
143
+ # Or directly with tsx
144
+ npx tsx test/smoke-test.ts
145
+
146
+ # Or with jiti
147
+ npx jiti test/smoke-test.ts
148
+ ```
149
+
150
+ ### End-to-end installation test
151
+
152
+ Uses the real `@earendil-works/pi-coding-agent` package to verify the extension actually installs the way the [Installation](#installation) section describes:
153
+
154
+ - The `pi.extensions` entry in `package.json` resolves to an existing file.
155
+ - Manual installation (cloning into `<agentDir>/extensions/prevent-destructive-commands`) is discovered and loaded by pi's own extension loader.
156
+ - `pi install <path>` records the package and pi can subsequently discover and load it.
157
+ - Once loaded through that real pipeline, the registered `tool_call` handler still blocks/allows commands correctly.
158
+
159
+ ```bash
160
+ npm run test:e2e
161
+
162
+ # Or directly with tsx
163
+ npx tsx test/e2e-install-test.ts
164
+ ```
165
+
166
+ The test suite covers:
167
+ - All destructive Git operations
168
+ - Existing Nx package and TypeScript configuration protection, including direct tool writes, patches, shell redirection, and package-manager dependency changes
169
+ - Path-sensitive `rm` protection
170
+ - Docker destructive commands
171
+ - AWS CLI destructive subcommands
172
+ - Wrapper traversal (`sudo`, `env`, `timeout`)
173
+ - Shell invocation traversal (`bash -c`)
174
+ - `find -exec` and `xargs` delegation
175
+ - Pipeline and concatenation handling
176
+ - Git global flag parsing (`-C`, `--git-dir`)
177
+ - Edge cases and safe command verification
178
+
179
+ ---
180
+
181
+ ## Known Limitations
182
+
183
+ As with the original Claude plugin, the analysis is static and therefore cannot cover everything:
184
+
185
+ | Limitation | Example | Explanation |
186
+ |------------|---------|-------------|
187
+ | **Unknown wrappers** | Custom destructive tools | The extension covers known patterns; unknown wrappers or custom destructive tools are not intercepted. |
188
+
189
+ Two cases that used to be listed here are now handled:
190
+
191
+ - **Arguments via stdin/pipe** (`echo x \| xargs rm`) — a path-sensitive command (`rm`, `rmdir`, `shred`, ...) reached through `xargs`/`parallel` with no explicit target token is now blocked: its real targets arrive via stdin at runtime and can't be statically verified, so it's treated as dangerous rather than assumed safe. An explicit target (e.g. `xargs rm ./known-file`) is still checked normally against the working directory.
192
+ - **`cd` in command** (`cd /; rm etc/passwd`) — the analyzer now tracks `cd` within the same command chain (including `( … )` subshell scoping) and resolves subsequent relative paths against that effective directory, while still enforcing the *original* working directory as the safety boundary. `cd /; rm etc/passwd` now correctly resolves to `/etc/passwd` and is blocked. When a `cd` target itself can't be resolved statically (e.g. `cd "$VAR"`, `cd -`), any path-sensitive command that follows is blocked conservatively.
193
+
194
+ ---
195
+
196
+ ## How It Works
197
+
198
+ When pi attempts to execute a bash command, this extension intercepts the `tool_call` event and:
199
+
200
+ 1. **Tokenizes** the command string using a shlex-like shell tokenizer that respects quotes and escapes.
201
+ 2. **Analyzes** the token stream recursively, traversing wrappers, shell invocations, and pipelines.
202
+ 3. **Blocks** if any destructive pattern is detected, returning a clear reason to the agent.
203
+ 4. **Allows** safe commands to pass through without modification.
204
+
205
+ The agent never receives an interactive prompt — the block is final and must be handled by finding a safe alternative.
206
+
207
+ ---
208
+
209
+ ## Contributing
210
+
211
+ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
212
+
213
+ Quick start for contributors:
214
+
215
+ ```bash
216
+ git clone https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands.git
217
+ cd pi-prevent-destructive-commands
218
+ npm install
219
+ npm test
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Changelog
225
+
226
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
227
+
228
+ ---
229
+
230
+ ## License
231
+
232
+ This project is licensed under the [MIT License](LICENSE).
233
+
234
+ ---
235
+
236
+ ## Acknowledgments
237
+
238
+ - Inspired by and ported from Claude's [`prevent-destructive-commands.py`](https://github.com/giuseppe-trisciuoglio/developer-kit/blob/main/plugins/developer-kit-core/hooks/prevent-destructive-commands.py) hook.
239
+ - Built for the [pi coding agent](https://pi.dev) ecosystem.
package/icon.png ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@giuseppe.trisciuoglio/pi-prevent-destructive-commands",
3
+ "version": "1.0.0",
4
+ "description": "Unconditionally blocks destructive bash commands before execution. A pi extension that guards against rm, git reset --hard, docker rm, aws delete operations, and more.",
5
+ "keywords": [
6
+ "pi",
7
+ "pi-extension",
8
+ "ai-agent",
9
+ "safety",
10
+ "destructive-commands",
11
+ "bash",
12
+ "shell",
13
+ "guard",
14
+ "security",
15
+ "rm",
16
+ "git",
17
+ "docker",
18
+ "aws"
19
+ ],
20
+ "author": "Giuseppe Trisciuoglio",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands/issues"
28
+ },
29
+ "homepage": "https://github.com/giuseppe-trisciuoglio/pi-prevent-destructive-commands#readme",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "scripts": {
34
+ "test": "npm run test:smoke && npm run test:migration-guard && npm run test:nx-guard && npm run test:e2e",
35
+ "test:smoke": "npx tsx test/smoke-test.ts",
36
+ "test:e2e": "npx tsx test/e2e-install-test.ts",
37
+ "test:migration-guard": "npx tsx test/migration-guard-test.ts",
38
+ "test:nx-guard": "npx tsx test/nx-guard-test.ts",
39
+ "lint": "tsc --noEmit",
40
+ "typecheck": "tsc --noEmit"
41
+ },
42
+ "peerDependencies": {
43
+ "@earendil-works/pi-coding-agent": ">=0.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "@earendil-works/pi-coding-agent": ">=0.1.0",
47
+ "typescript": "^5.4.0",
48
+ "tsx": "^4.7.0"
49
+ },
50
+ "pi": {
51
+ "name": "prevent-destructive-commands",
52
+ "displayName": "Prevent Destructive Commands",
53
+ "description": "Blocks destructive bash commands (rm, git reset, docker rm, AWS delete, etc.) before execution.",
54
+ "category": "security",
55
+ "extensions": [
56
+ "./src/index.ts"
57
+ ]
58
+ }
59
+ }