Skip to main content
Glama

bash-mcp

A small MCP server that exposes WSL bash as a structured tool to MiniMax Code. Replaces the old wsl -d ... -- bash -c "..." PowerShell pattern with a single tool call.

Why

Running WSL commands from PowerShell (wsl -d Ubuntu-22.04 -- bash -lc "...") has daily friction costs:

  • Quoting hell — PowerShell reinterprets $() as Get-Date, backticks, and regex escapes break.

  • UTF-16 garbage — wsl.exe to PowerShell pipe occasionally emits garbled output.

  • Stateless shells — each call is a fresh shell; no cwd, env, or history.

  • Output is unstructured — stdout/stderr mixed, no exit code, no duration.

  • No safety net — a single bad rm -rf quote wipes the home.

bash-mcp solves all of these with one tool call returning {stdout, stderr, exit_code, duration_ms, timed_out, truncated, classification, audit_id}.

Related MCP server: AdminMCP

License

MIT

What's new in v0.3.0

  • Audit log rotation — audit.jsonl rotates at 25 MB (configurable) keeping 5 backups (audit.jsonl.{1..5}). Lazy + thread-safe.

  • Concurrency limit — max 8 concurrent subprocesses via threading.BoundedSemaphore at the executor boundary. Excess calls queue, never drop.

  • bash_mcp_status extended with audit.{max_bytes, backup_count, backups_present} and concurrency.{max_concurrent, active} fields.

  • 95 tests passing (up from 84).

What's new in v0.8.0

  • Per-project cwd allowlists via .bash-mcp.toml — drop a TOML file at your repo root (or any ancestor) and bash-mcp picks it up for calls whose cwd lives under that directory. Two modes: extend (default; merge with global) and replace (sandbox; replace global). Zero behavior change when no file is present. See Configuration → .bash-mcp.toml below for the schema. No new MCP tool — v0.8 is purely a config-driven feature.

What's new in v0.7.1

  • bash_mcp_audit_read(backup_index, max_entries?, tool_filter?) — closes the v0.7 gzip one-way archival caveat. Transparently decompresses audit.jsonl.N.gz backups in addition to plaintext. Returns a stable {backup_index, path, format, entries, returned} object. Audited as tool="bash_mcp_audit_read".

  • Cleanup — deleted stray infra/install.sh.before-addendum (untracked, never committed).

  • 12 tools total (up from 11). 289 tests passing (up from 274). 86 smoke cases (up from 78).

All changes are non-breaking. v0.7.0 callers see zero behavior change.

What's new in v0.7.0

  • bash_mcp_classify(command) denylist explainer — read-only tool that returns {class, matched_pattern, pattern_index, hint_with_dangerous_false, hint_with_dangerous_true, would_execute, would_execute_with_dangerous_true, audit_id}. Use BEFORE bash_run_command if you're unsure whether a command will be rejected. Saves a round-trip and gives the exact regex that would fire.

  • Session auto-TTL (opt-in) — set BASH_MCP_SESSION_IDLE_TIMEOUT_S to enable. A daemon janitor evicts sessions whose last_used_at is older than the timeout. Each eviction is audited as tool="bash_mcp_session_destroy" reason="idle_ttl_expired". Default 0 = disabled (preserves v0.6 contract).

  • Audit log gzip-on-rotation (opt-in) — set BASH_MCP_AUDIT_GZIP_THRESHOLD_BYTES to enable. After rotation, any backup larger than the threshold is gzipped to .jsonl.N.gz (one-way; use zcat to inspect). Default 0 = disabled (preserves v0.3 plaintext-only behavior).

  • bash_mcp_status extended with audit.{gzip_threshold_bytes, gzip_enabled} and sessions.{idle_timeout_s, janitor_enabled}.

  • 274 tests passing (up from 220).

All three features are opt-in via env vars and default to off, so existing callers see zero behavior change.

What's new in v0.6.0

  • Stateful sessions — four new tools (bash-mcp_session_create, _run, _destroy, _list) keep a server-side cwd and accumulated env dict across calls. Use them when you need to cd somewhere and stay there, or export a variable and have it persist for the next call.

  • Sessions are process-lifetime — a server restart wipes them. Caller is responsible for _destroy. There is no auto-TTL.

  • Best-effort parser — top-level cd PATH, export VAR=value, unset VAR are tracked; shell scripts, functions, $(...), heredocs are out of scope (the subprocess still runs correctly, but state isn't persisted from them).

  • bash_mcp_status extended with sessions.{active, max_env_per_session}.

  • 220 tests passing (up from 164).

See CHANGELOG.md for full history.

What's new in v0.4.0

  • Quarterly denylist review #1 — 6 new HARD patterns + 4 new SOFT patterns (see Safety table below).

  • Denylist review history in safety.py documenting all reviews to date (v0.1, v0.2, v0.3, v0.4).

  • 164 tests passing (up from 95).

Tools

Tool

Purpose

bash-mcp_run_command(command, cwd?, timeout_ms?, env?, dangerous?)

Execute bash. Returns structured output.

bash-mcp_check_env()

OS, kernel, Python, uv, PATH entries.

bash-mcp_list_binaries()

~37 well-known tools with {path, exists, version?}.

bash-mcp_which(name)

Resolve a single binary.

bash-mcp_status()

Service health, audit stats + rotation config, concurrency, sessions, uptime, tool list.

bash-mcp_session_create(name?, cwd?)

Create a stateful session; returns a session_id.

bash-mcp_session_run(session_id, command, ...)

Run a command in a session; persists cd / export / unset.

bash-mcp_session_destroy(session_id)

Destroy a session, free memory.

bash-mcp_session_list()

List active sessions, most-recently-used first.

bash-mcp_classify(command)

Read-only denylist explainer; returns {class, matched_pattern, hint, would_execute, ...}. v0.7.

bash-mcp_audit_read(backup_index, max_entries?, tool_filter?)

Read entries from a rotated audit backup (plaintext or gzip). v0.7.1.

All tools are namespaced as bash-mcp_* in MiniMax Code.

Quick start (for the agent)

// Discover environment
bash-mcp_check_env()

// Check if a tool is installed
bash-mcp_list_binaries()       // batch
bash-mcp_which({name: "rg"})   // single

// Run a command
bash-mcp_run_command({command: "ls -lah /tmp"})

// Run with custom cwd and timeout
bash-mcp_run_command({command: "pytest -xvs", cwd: "/home/jbecerra/projects/myapp", timeout_ms: 120000})

// Bypass the SOFT denylist
bash-mcp_run_command({command: "sudo systemctl restart nginx", dangerous: true})
bash-mcp_run_command({command: "pip uninstall requests", dangerous: true})

// Verify service health (audit + concurrency state)
bash-mcp_status()

⚠️ DO NOT use wsl -d ... -- bash -c "..."

A PreToolUse hook in MiniMax Code denies any shell call whose command starts with wsl(\.exe)?\s. If you trigger it, you'll see an abort reason pointing you to this README.

Escape hatch (for genuine testing): prefix the command with BASH_MCP_SKIP=1.

Architecture

MiniMax Code ──HTTP POST──> bash-mcp server (WSL, port 54321)
                            │
                            ├─ classify(command) → safe / dangerous / reject
                            ├─ acquire concurrency.slot()  (BoundedSemaphore, max 8)
                            ├─ subprocess.run([bash, "-lc", command], ...)
                            ├─ truncate stdout/stderr to 50KB
                            ├─ audit.log(...) → audit.jsonl
                            └─ on size > 25 MB: rotate → audit.jsonl.{1..5}

Streamable-http transport. bash-mcp_run_command is stateless per call; the four bash-mcp_session_* tools additionally clone + write cwd and env through src/bash_mcp/sessions.py (process-lifetime only). v0.7 adds a daemon janitor thread (opt-in via BASH_MCP_SESSION_IDLE_TIMEOUT_S) and gzip-on-rotation for the audit log (opt-in via BASH_MCP_AUDIT_GZIP_THRESHOLD_BYTES).

Safety

Full list lives in src/bash_mcp/safety.py. Snapshot:

HARD denylist (always rejected, cannot be bypassed):

Category

Patterns

Block-device destruction

rm -rf / (except /tmp, /home, /Users); dd of=/dev/{sd,hd,nvme,vd}*; > /dev/{sd,hd,nvme,vd}*; tee /dev/{sd,hd,nvme,vd}*; cat|cp|mv → /dev/sdX; mkfs /dev/*

Process / system

fork bombs (:(){ :|:& };: + 3 variants); find ... -delete; find ... -exec rm; chmod -R NNN /

Unrecoverable delete

shred /etc /var /usr /boot /bin /sbin/*

Supply chain

curl ... | bash; wget ... | bash; sudo rm

SOFT denylist (requires dangerous=true):

Category

Patterns

Privilege / process

sudo; kill -9; kill -SIGKILL; systemctl stop|disable|mask

System changes

git push --force / -f; chown -R; chmod NNN /; > /etc/

Package removal (v0.4)

apt remove|purge|autoremove; pip uninstall; npm uninstall|rm|remove -g|--global

Package install

pip install; npm install -g; apt(-get) install

Destructive sync (v0.4)

rsync --delete

Every error response includes hint (actionable next step) and documentation (path to the skill) — the agent does not need to parse free text to recover.

cwd is allowlist-validated: must resolve under $HOME, /tmp, /home, /mnt/c/Users/jesus, or /var/tmp. Windows-style paths (C:\foo) are auto-converted to /mnt/c/foo.

Denylist is reviewed quarterly. Last review: v0.4 (2026-09-12) — see safety.py for full history.

Configuration

All via environment variables. Set in ~/.config/systemd/user/bash-mcp.service (Environment= lines), or before running locally.

Variable

Default

Purpose

FASTMCP_SERVER_HOST

0.0.0.0

Bind address

FASTMCP_SERVER_PORT

54321

Bind port (IANA dynamic range)

BASH_MCP_AUDIT_MAX_BYTES

26214400 (25 MB)

Audit log rotation threshold

BASH_MCP_AUDIT_BACKUP_COUNT

5

Audit log backups kept

BASH_MCP_MAX_CONCURRENT

8

Max in-flight subprocesses

.bash-mcp.toml (per-project cwd allowlist, v0.8)

Drop a .bash-mcp.toml at any directory in your project. bash-mcp walks up from the call's cwd to find it (stops at $HOME or filesystem root, max 32 hops) and applies the resulting allowlist to that call. No new tool, no schema-validation library, no install hook.

Schema:

# mode is optional. Defaults to "extend".
mode = "extend"   # or "replace"

# required: list of paths the project's cwds may resolve under.
# Relative entries resolve against the TOML's parent dir;
# absolute entries (after ~ expansion) are used as-is.
# Existence is NOT checked here — executor.run checks separately.
allowed_roots = [".", "frontend", "backend", "/srv/shared-cache"]

Modes:

  • extend (default) — add the project's allowed_roots to the global ALLOWED_CWD_ROOTS. Use this when your project needs to expose additional worktrees, build dirs, or shared caches outside the global allowlist (e.g. /srv/shared-cache above).

  • replace — replace the global list with the project's roots. Use this for sandboxed environments where the global list is too permissive. An empty allowed_roots list is a deliberate lockout (no cwd is allowed; the caller receives INVALID_CWD).

Failure modes (all fail-soft — bash-mcp falls back to the global allowlist, no crash):

  • Malformed TOML → stderr warning, global roots apply.

  • Unknown mode value → stderr warning, defaults to extend.

  • allowed_roots is not a list of strings → stderr warning, global roots apply.

Cache: the loaded allowlist is cached per (start_dir, mtime_ns) for the lifetime of the bash-mcp process. Editing the TOML invalidates the cache; deleting the TOML means the next call from that directory re-walks the tree.

Deploying

infra/install.sh deploys everything to a fresh WSL/Windows setup:

  • systemd unit → ~/.config/systemd/user/bash-mcp.service

  • PreToolUse hook → ~/.minimax/agents/mavis/hooks/bash-mcp-redirect.md

  • Skill → ~/.mavis/skills/bash-mcp/

  • mcp.json entry → ~/.minimax/mcp/mcp.json

  • Agent system_prompt addendum → reported on stdout by install.sh [5/5]; applied manually once per environment via the desktop mavis agent update mavis tool (the WSL and Windows mavis CLIs do not expose the agent subcommand)

The script also prints the one mavis mcp create command that must be run from Windows PowerShell (the runtime registry is Windows-side).

./infra/install.sh

Agent integration

bash-mcp ships an infra/system-prompt-addendum.md that, when applied, is appended to the mavis agent's systemPrompt. This is the agent-side guardrail that prevents the model from falling back to the wsl -d ... -- bash -c "..." pattern from PowerShell — even when the PreToolUse hook times out or the skill isn't auto-loaded.

Auto-apply is not supported. The mavis CLI on WSL is the IDE launcher (start / stop / status only), and the Windows mavis.cmd references daemon/cli.js which is not bundled in current installs. Neither CLI exposes agent update. The apply must be performed by the desktop mavis MCP tool, which is what install.sh [5/5] documents on stdout.

Manual apply (run once per environment, anywhere with the desktop tool):

# install.sh prints the exact 3-step command (snapshot → build → apply)
# on stdout. The canonical addendum is at infra/system-prompt-addendum.md.
# Idempotent: rerun is a no-op if the marker substring is already in system_prompt.

Why this lives in bash-mcp, not in the agent repo. bash-mcp is the project that owns the bash-mcp-vs-wsl-d rule across all three sync points (skill, hook, mcp.json description). The system_prompt addendum is the fourth sync point — same wording, same escape hatch (BASH_MCP_SKIP=1), same cross-references to this README. Keeping all four together is what AGENTS.md means by "four places that must stay in sync".

Operations

Start / stop / restart

systemctl --user start bash-mcp.service
systemctl --user stop bash-mcp.service
systemctl --user restart bash-mcp.service
systemctl --user status bash-mcp.service

# Logs (via screen session)
screen -r bash-mcp

# Or via journalctl
journalctl --user -u bash-mcp.service -f

Audit log (rotation in v0.3+)

The audit log at ~/.local/share/bash-mcp/audit.jsonl rotates automatically when it crosses BASH_MCP_AUDIT_MAX_BYTES (default 25 MB).

# Tail the current log
tail -f ~/.local/share/bash-mcp/audit.jsonl

# Pretty-print last 5 entries
tail -5 ~/.local/share/bash-mcp/audit.jsonl | jq .

# Check current size + rotation config
bash-mcp_status  # → audit.size_bytes, audit.max_bytes, audit.backup_count, audit.backups_present

# List existing backups (audit.jsonl.{1..5})
ls ~/.local/share/bash-mcp/

# Read a specific backup
less ~/.local/share/bash-mcp/audit.jsonl.1 | jq .

Rotation behavior:

  • Lazy — happens on next log() call after the threshold is crossed.

  • Thread-safe — audit.jsonl and the rotation move happen under one lock.

  • Fail-soft — OSError on rename prints to stderr; next call retries.

  • Naming — audit.jsonl.{N} (stdlib convention); oldest is dropped when count exceeds BASH_MCP_AUDIT_BACKUP_COUNT.

  • No content loss — verified by test_rotation_preserves_content.

Concurrency (v0.3+)

Max 8 concurrent subprocesses by default. Excess calls queue (don't drop). Tune via BASH_MCP_MAX_CONCURRENT.

# Check current limit + active count
bash-mcp_status  # → concurrency.max_concurrent, concurrency.active

If you make 20 parallel tool calls, 8 run, 12 queue. The semaphore is at the executor boundary, so bash_run_command, bash_check_env's version probes, and any future subprocess-using tool all share the same cap.

Health check (no SSH/curl needed)

# Via MiniMax Code (recommended):
bash-mcp_status

# Or via shell + curl:
curl http://localhost:54321/mcp/  # then initialize + tools/call bash_mcp_status

WSL IP changed (after reboot)

cd /home/jbecerra/projects/bash-mcp
./infra/update-ip.sh

This rewrites only the bash-mcp URL in ~/.minimax/mcp/mcp.json (and ~/.cursor/mcp.json if present). Does not touch semantic-memory.

Bootstrap on Windows login

infra/launcher.sh ships out of the box. To run it automatically on every Windows logon, register the scheduled task (run as Administrator from PowerShell):

powershell -ExecutionPolicy Bypass -File C:\path\to\bash-mcp\infra\windows\install-task.ps1

This registers BashMcp-WSL-Bootstrap in Task Scheduler with a 60s delay, RunLevel=Highest, action = wsl.exe /home/jbecerra/projects/bash-mcp/infra/launcher.sh. The launcher ensures bash-mcp.service is running (idempotent: systemd skips if already running).

Verify:

Get-ScheduledTask -TaskName BashMcp-WSL-Bootstrap | Format-List

Remove:

Unregister-ScheduledTask -TaskName BashMcp-WSL-Bootstrap -Confirm:$false

The install.sh script (when run without --dry-run) prints the same install-task.ps1 path as a hint.

Development

Install

source ~/.local/bin/env
export PATH="$HOME/.local/bin:$PATH"
cd /home/jbecerra/projects/bash-mcp
uv sync

Run locally

uv run python -m bash_mcp.server --transport streamable-http --host 0.0.0.0 --port 54321

Run tests

uv run pytest tests/ -v

164 tests cover:

  • 115 safety cases (test_safety.py) — parametrized denylist + safe baselines + review-history

  • 27 executor cases (test_executor.py) — subprocess + cwd allowlist + Windows→WSL

  • 9 error envelope cases (test_errors.py)

  • 6 audit log rotation cases (test_audit_rotation.py)

  • 5 concurrency limit cases (test_concurrency.py)

  • 11 e2e tests against the live server (test_e2e.py)

Project layout

bash-mcp/
├── AGENTS.md                        # project-scoped rules for future agents
├── CHANGELOG.md                     # version history
├── LICENSE                          # MIT
├── README.md                        # this file
├── pyproject.toml                   # uv-managed, FastMCP 2.7.0
├── uv.lock
├── src/bash_mcp/                    # server code
│   ├── server.py                    # FastMCP + 6 tools
│   ├── executor.py                  # subprocess wrapper, timeout, truncation, cwd allowlist, concurrency slot
│   ├── safety.py                    # classify(command) + quarterly denylist reviews
│   ├── audit.py                     # JSONL append-only logger + size-based rotation
│   ├── discovery.py                 # which / list_binaries (thread-pool)
│   └── concurrency.py               # BoundedSemaphore wrapper
├── tests/                           # 164 tests
│   ├── test_safety.py               # 115 parametrized denylist cases
│   ├── test_executor.py             # 27 subprocess + cwd allowlist
│   ├── test_errors.py               # 9 error envelope shape + hints
│   ├── test_audit_rotation.py       # 6 rotation cases
│   ├── test_concurrency.py          # 5 concurrency limit cases
│   └── test_e2e.py                  # 11 live-server round-trips
└── infra/                           # deployment artifacts
    ├── install.sh                   # idempotent deploy (--dry-run supported)
    ├── launcher.sh                  # WSL bootstrap (parallel to semantic-memory-launcher.sh)
    ├── update-ip.sh                 # refresh WSL IP in mcp.json
    ├── mcp.json.snippet             # documentation reference
    ├── systemd/
    │   └── bash-mcp.service
    ├── hooks/
    │   ├── bash-mcp-redirect.md
    │   └── bash-mcp-redirect.js
    ├── skills/
    │   └── bash-mcp/
    │       ├── SKILL.md
    │       └── _meta.json
    └── windows/                     # Windows-side deployment helpers
        ├── BashMcp-WSL-Bootstrap.xml # Scheduled Task template
        └── install-task.ps1          # PowerShell installer for the task

Add a new tool

from bash_mcp import audit

@mcp.tool
def bash_my_new_tool(...) -> dict:
    """Docstring becomes the tool description for the agent."""
    ...

If your tool calls subprocess.run, wrap it in concurrency.slot() so it participates in the global cap:

from bash_mcp.concurrency import slot as concurrency_slot

with concurrency_slot():
    subprocess.run([...])

Add a denylist pattern

  1. Add to REJECT_PATTERNS (hard) or DANGEROUS_PATTERNS (soft) in src/bash_mcp/safety.py.

  2. Add a parametrized test case in tests/test_safety.py.

  3. Update README.md safety table and the deployed ~/.mavis/skills/bash-mcp/SKILL.md.

  4. Add a dated entry to the review-history block at the top of safety.py listing what was added.

  5. systemctl --user restart bash-mcp.service so the new pattern is live.

For non-trivial gaps or batch additions, treat it as a quarterly review (see AGENTS.md).

Adjust rotation / concurrency limits

  • Change the systemd unit (infra/systemd/bash-mcp.service), Environment=BASH_MCP_AUDIT_MAX_BYTES=..., etc.

  • systemctl --user daemon-reload && systemctl --user restart bash-mcp.service

  • Verify via bash_mcp_status() that the new values are loaded.

Test the hook manually

# Should deny (abort)
echo '{"input":{"toolName":"bash","toolArgs":{"command":"wsl -d Ubuntu -- bash -c \"echo hi\""}},"output":{}}' \
  | wsl -d Ubuntu-22.04 -- node /home/jbecerra/projects/bash-mcp/infra/hooks/bash-mcp-redirect.js
# => {"_abort":{"reason":"..."}}

# Should pass
echo '{"input":{"toolName":"bash","toolArgs":{"command":"git status"}},"output":{}}' \
  | wsl -d Ubuntu-22.04 -- node /home/jbecerra/projects/bash-mcp/infra/hooks/bash-mcp-redirect.js
# => {}

# Should pass (escape hatch)
echo '{"input":{"toolName":"bash","toolArgs":{"command":"BASH_MCP_SKIP=1 wsl echo"}},"output":{}}' \
  | wsl -d Ubuntu-22.04 -- node /home/jbecerra/projects/bash-mcp/infra/hooks/bash-mcp-redirect.js
# => {}

Deploy to a fresh WSL machine

git clone <this-repo> /home/jbecerra/projects/bash-mcp
cd /home/jbecerra/projects/bash-mcp
./infra/install.sh
# Then from Windows PowerShell, run the printed `mavis mcp create` command.

Out of scope (deferred to v0.9+)

Already shipped:

  • Stateful sessions with persistent cwd across calls — shipped in v0.6 (bash_mcp_session_create / _run / _destroy / _list). Process-lifetime only; see "What's new in v0.6.0".

  • Windows Scheduled Task XML for login auto-start — shipped in v0.5 at infra/windows/BashMcp-WSL-Bootstrap.xml + install-task.ps1.

  • Auto-TTL / idle cleanup — shipped in v0.7 (opt-in via BASH_MCP_SESSION_IDLE_TIMEOUT_S, daemon janitor thread + audit on each eviction). See "What's new in v0.7.0".

  • A "denylist explainer" tool — shipped in v0.7 as bash_mcp_classify(command). Returns {class, matched_pattern, hint, would_execute, ...} without executing. Lets the agent self-check before sending.

Still pending:

  • Persistent shell state (PTY-based) — v0.6 sessions are best-effort regex parsing of cd / export / unset. v0.7 could keep a real bash process alive per session (via pexpect or similar) and track shell variables, aliases, functions, set -e / pipefail, and job control. Requires a redesign of the session lifecycle and a hard cap on concurrent shells (memory + fd cost).

  • Disk persistence for sessions — v0.6 sessions are wiped on server restart. v0.7 could serialize _SESSIONS to SQLite or JSON-on-disk and restore on boot, with a migration story for callers that depend on the process-lifetime contract.

  • Session snapshots (fork a session at a point in time) — useful for branching workflows.

  • Cross-session env sharing — share a named env dict across multiple sessions.

  • Web UI for browsing the audit log — minimal Flask/FastAPI page on a separate port with filters by tool / classification / time.

  • Audit log compression — shipped in v0.7.1 via bash_mcp_audit_read (closes the gzip one-way archival caveat). See "What's new in v0.7.1". Remaining: gzip-on-rotation above a higher threshold + audit log shipping (Loki/CloudWatch) still deferred to v0.9+.

  • Audit log shipping — Loki push API, CloudWatch Logs, or local syslog. Optional, gated by config.

  • Per-project allowlists (.bash-mcp.toml in repo root) — shipped in v0.8 via the .bash-mcp.toml schema. See Configuration → .bash-mcp.toml above. Supports both extend (merge with global) and replace (sandbox) modes.

  • A formal threat-model document — STRIDE-style analysis of the audit log, the hook, the denylist, and the session lifecycle.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    Provides LLMs with administrative capabilities to execute shell commands on local systems through a pseudo-terminal environment with Unix Domain Socket communication.
    -
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables safe execution of terminal commands across different shells (bash, cmd, PowerShell) with configurable timeouts, working directories, and resource limits for command-line operations through AI assistants.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables secure command-line interactions on Windows systems with support for PowerShell, CMD, Git Bash, and WSL shells, providing controlled file access, command execution, and configurable security restrictions.
    6
    37 npm
    4
    MIT