command-gate 0.2.4__tar.gz

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 (103) hide show
  1. command_gate-0.2.4/.github/workflows/publish-pypi.yml +32 -0
  2. command_gate-0.2.4/.github/workflows/release.yml +105 -0
  3. command_gate-0.2.4/.gitignore +20 -0
  4. command_gate-0.2.4/.python-version +1 -0
  5. command_gate-0.2.4/LICENSE +21 -0
  6. command_gate-0.2.4/PKG-INFO +204 -0
  7. command_gate-0.2.4/README.md +156 -0
  8. command_gate-0.2.4/TECHNICAL.md +261 -0
  9. command_gate-0.2.4/packaging/README.md +76 -0
  10. command_gate-0.2.4/packaging/brew/cgate.template.rb +19 -0
  11. command_gate-0.2.4/packaging/scoop/cgate.template.json +18 -0
  12. command_gate-0.2.4/pyproject.toml +115 -0
  13. command_gate-0.2.4/scripts/_ensure_repo.py +78 -0
  14. command_gate-0.2.4/scripts/_ensure_version.py +87 -0
  15. command_gate-0.2.4/scripts/build-binary.py +166 -0
  16. command_gate-0.2.4/scripts/verify-post-walkthrough.py +138 -0
  17. command_gate-0.2.4/src/cgate/__init__.py +26 -0
  18. command_gate-0.2.4/src/cgate/__main__.py +8 -0
  19. command_gate-0.2.4/src/cgate/_version.py +24 -0
  20. command_gate-0.2.4/src/cgate/cli/__init__.py +1 -0
  21. command_gate-0.2.4/src/cgate/cli/_console.py +17 -0
  22. command_gate-0.2.4/src/cgate/cli/connections.py +212 -0
  23. command_gate-0.2.4/src/cgate/cli/history.py +191 -0
  24. command_gate-0.2.4/src/cgate/cli/install.py +182 -0
  25. command_gate-0.2.4/src/cgate/cli/main.py +115 -0
  26. command_gate-0.2.4/src/cgate/cli/mcp.py +197 -0
  27. command_gate-0.2.4/src/cgate/cli/uninstall.py +403 -0
  28. command_gate-0.2.4/src/cgate/cli/update.py +538 -0
  29. command_gate-0.2.4/src/cgate/cli/watch.py +20 -0
  30. command_gate-0.2.4/src/cgate/connections/__init__.py +1 -0
  31. command_gate-0.2.4/src/cgate/connections/auth.py +93 -0
  32. command_gate-0.2.4/src/cgate/connections/detect.py +78 -0
  33. command_gate-0.2.4/src/cgate/connections/store.py +88 -0
  34. command_gate-0.2.4/src/cgate/core/__init__.py +1 -0
  35. command_gate-0.2.4/src/cgate/core/path_env.py +218 -0
  36. command_gate-0.2.4/src/cgate/core/paths.py +35 -0
  37. command_gate-0.2.4/src/cgate/core/update_log.py +36 -0
  38. command_gate-0.2.4/src/cgate/db/__init__.py +1 -0
  39. command_gate-0.2.4/src/cgate/db/batches.py +111 -0
  40. command_gate-0.2.4/src/cgate/db/commands.py +191 -0
  41. command_gate-0.2.4/src/cgate/db/connection.py +104 -0
  42. command_gate-0.2.4/src/cgate/db/mode.py +74 -0
  43. command_gate-0.2.4/src/cgate/db/rows.py +99 -0
  44. command_gate-0.2.4/src/cgate/db/schema.py +54 -0
  45. command_gate-0.2.4/src/cgate/db/server_settings.py +105 -0
  46. command_gate-0.2.4/src/cgate/db/types.py +77 -0
  47. command_gate-0.2.4/src/cgate/executor/__init__.py +7 -0
  48. command_gate-0.2.4/src/cgate/executor/base.py +71 -0
  49. command_gate-0.2.4/src/cgate/executor/selector.py +61 -0
  50. command_gate-0.2.4/src/cgate/executor/ssh.py +157 -0
  51. command_gate-0.2.4/src/cgate/executor/winrm.py +129 -0
  52. command_gate-0.2.4/src/cgate/helper/__init__.py +10 -0
  53. command_gate-0.2.4/src/cgate/helper/__main__.py +112 -0
  54. command_gate-0.2.4/src/cgate/helper/waiter.py +123 -0
  55. command_gate-0.2.4/src/cgate/mcp_installer.py +161 -0
  56. command_gate-0.2.4/src/cgate/mcp_server/__init__.py +6 -0
  57. command_gate-0.2.4/src/cgate/mcp_server/__main__.py +6 -0
  58. command_gate-0.2.4/src/cgate/mcp_server/auto_resolution.py +80 -0
  59. command_gate-0.2.4/src/cgate/mcp_server/server.py +271 -0
  60. command_gate-0.2.4/src/cgate/mcp_server/tools.py +351 -0
  61. command_gate-0.2.4/src/cgate/risk.py +129 -0
  62. command_gate-0.2.4/src/cgate/update.py +713 -0
  63. command_gate-0.2.4/src/cgate/watch/__init__.py +7 -0
  64. command_gate-0.2.4/src/cgate/watch/app.py +560 -0
  65. command_gate-0.2.4/src/cgate/watch/approval.py +237 -0
  66. command_gate-0.2.4/src/cgate/watch/command_detail_modal.py +68 -0
  67. command_gate-0.2.4/src/cgate/watch/history_modal.py +242 -0
  68. command_gate-0.2.4/src/cgate/watch/mode_modal.py +110 -0
  69. command_gate-0.2.4/src/cgate/watch/queue.py +106 -0
  70. command_gate-0.2.4/src/cgate/watch/render.py +156 -0
  71. command_gate-0.2.4/src/cgate/watch/server_settings_modal.py +179 -0
  72. command_gate-0.2.4/src/cgate/watch/session.py +40 -0
  73. command_gate-0.2.4/src/cgate/watch/theme.py +32 -0
  74. command_gate-0.2.4/src/cgate/watch/widgets.py +35 -0
  75. command_gate-0.2.4/tests/__init__.py +1 -0
  76. command_gate-0.2.4/tests/conftest.py +18 -0
  77. command_gate-0.2.4/tests/fixtures/attestation_response.json +77 -0
  78. command_gate-0.2.4/tests/test_auto_resolution.py +108 -0
  79. command_gate-0.2.4/tests/test_build.py +133 -0
  80. command_gate-0.2.4/tests/test_cli_history.py +133 -0
  81. command_gate-0.2.4/tests/test_connections.py +213 -0
  82. command_gate-0.2.4/tests/test_connections_cli.py +268 -0
  83. command_gate-0.2.4/tests/test_db.py +638 -0
  84. command_gate-0.2.4/tests/test_executor.py +414 -0
  85. command_gate-0.2.4/tests/test_helper.py +220 -0
  86. command_gate-0.2.4/tests/test_install.py +259 -0
  87. command_gate-0.2.4/tests/test_main.py +112 -0
  88. command_gate-0.2.4/tests/test_mcp_installer.py +244 -0
  89. command_gate-0.2.4/tests/test_mcp_server.py +468 -0
  90. command_gate-0.2.4/tests/test_path_env.py +265 -0
  91. command_gate-0.2.4/tests/test_risk.py +97 -0
  92. command_gate-0.2.4/tests/test_smoke.py +45 -0
  93. command_gate-0.2.4/tests/test_uninstall.py +440 -0
  94. command_gate-0.2.4/tests/test_update.py +966 -0
  95. command_gate-0.2.4/tests/test_update_cli.py +349 -0
  96. command_gate-0.2.4/tests/test_update_log.py +43 -0
  97. command_gate-0.2.4/tests/test_update_status.py +92 -0
  98. command_gate-0.2.4/tests/test_watch.py +556 -0
  99. command_gate-0.2.4/tests/test_watch_app.py +563 -0
  100. command_gate-0.2.4/tests/test_watch_history.py +397 -0
  101. command_gate-0.2.4/tests/test_watch_modals.py +228 -0
  102. command_gate-0.2.4/tests/test_watch_render.py +190 -0
  103. command_gate-0.2.4/uv.lock +2010 -0
@@ -0,0 +1,32 @@
1
+ name: "Publish to PyPI"
2
+
3
+ "on":
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: "read"
10
+
11
+ jobs:
12
+ publish:
13
+ name: "Build and publish"
14
+ runs-on: "ubuntu-latest"
15
+ environment:
16
+ name: "pypi"
17
+ url: "https://pypi.org/p/command-gate"
18
+ permissions:
19
+ id-token: "write"
20
+ steps:
21
+ - name: "Check out source"
22
+ uses: "actions/checkout@11d5960a326750d5838078e36cf38b85af677262" # v4.4.0
23
+ - name: "Set up Python"
24
+ uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" # v5.6.0
25
+ with:
26
+ python-version: "3.12"
27
+ - name: "Install uv"
28
+ run: "python -m pip install 'uv>=0.8'"
29
+ - name: "Build sdist and wheel"
30
+ run: "uv build"
31
+ - name: "Publish to PyPI"
32
+ uses: "pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33" # v1.14.2
@@ -0,0 +1,105 @@
1
+ name: "Release binaries"
2
+
3
+ "on":
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: "write"
10
+
11
+ jobs:
12
+ build:
13
+ name: "Build ${{ matrix.asset }}"
14
+ runs-on: "${{ matrix.os }}"
15
+ permissions:
16
+ contents: "read"
17
+ id-token: "write"
18
+ attestations: "write"
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ include:
23
+ - os: "windows-latest"
24
+ asset: "cgate-windows-amd64.exe"
25
+ - os: "macos-latest"
26
+ asset: "cgate-macos-arm64"
27
+ - os: "ubuntu-latest"
28
+ asset: "cgate-linux-x86_64"
29
+ env:
30
+ UV_CACHE_DIR: "${{ github.workspace }}/.uv-cache"
31
+ steps:
32
+ - name: "Check out source"
33
+ uses: "actions/checkout@11d5960a326750d5838078e36cf38b85af677262" # v4.4.0
34
+ - name: "Set up Python"
35
+ uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" # v5.6.0
36
+ with:
37
+ python-version: "3.12"
38
+ - name: "Cache uv packages"
39
+ uses: "actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830" # v4.3.0
40
+ with:
41
+ path: "${{ env.UV_CACHE_DIR }}"
42
+ key: >-
43
+ uv-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
44
+ restore-keys: >-
45
+ uv-${{ runner.os }}-${{ runner.arch }}-
46
+ - name: "Install build tools"
47
+ run: "python -m pip install 'uv>=0.8' 'pyinstaller==6.*'"
48
+ - name: "Install project dependencies"
49
+ run: "uv sync --extra dev"
50
+ - name: "Build binary"
51
+ run: "uv run python scripts/build-binary.py"
52
+ - name: "Attest build provenance"
53
+ uses: "actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be" # v2.4.0
54
+ with:
55
+ subject-path: "dist/${{ matrix.asset }}"
56
+ - name: "Upload binary artifact"
57
+ uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" # v4.6.2
58
+ with:
59
+ name: "${{ matrix.asset }}"
60
+ path: "dist/${{ matrix.asset }}"
61
+ if-no-files-found: "error"
62
+ # Windows-only companion binary: performs the actual file swap/delete
63
+ # for `update apply` / `uninstall --binary` from a process that never
64
+ # shares an image name with cgate.exe, so there's nothing to
65
+ # disambiguate on Windows's process list (see src/cgate/helper/).
66
+ - name: "Build helper binary"
67
+ if: "matrix.os == 'windows-latest'"
68
+ run: >-
69
+ uv run python scripts/build-binary.py --name=cgate-helper
70
+ --entry=src/cgate/helper/__main__.py --minimal --windowed
71
+ - name: "Attest helper build provenance"
72
+ if: "matrix.os == 'windows-latest'"
73
+ uses: "actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be" # v2.4.0
74
+ with:
75
+ subject-path: "dist/cgate-helper-windows-amd64.exe"
76
+ - name: "Upload helper binary artifact"
77
+ if: "matrix.os == 'windows-latest'"
78
+ uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" # v4.6.2
79
+ with:
80
+ name: "cgate-helper-windows-amd64.exe"
81
+ path: "dist/cgate-helper-windows-amd64.exe"
82
+ if-no-files-found: "error"
83
+
84
+ release:
85
+ name: "Publish GitHub Release"
86
+ needs: "build"
87
+ runs-on: "ubuntu-latest"
88
+ steps:
89
+ - name: "Download binary artifacts"
90
+ uses: "actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093" # v4.3.0
91
+ with:
92
+ path: "."
93
+ merge-multiple: true
94
+ - name: "Create GitHub Release"
95
+ uses: "softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65" # v2.6.2
96
+ with:
97
+ tag_name: "${{ github.ref_name }}"
98
+ name: "cgate ${{ github.ref_name }}"
99
+ body: "Auto-generated release for ${{ github.ref_name }}."
100
+ files: |
101
+ cgate-windows-amd64.exe
102
+ cgate-macos-arm64
103
+ cgate-linux-x86_64
104
+ cgate-helper-windows-amd64.exe
105
+ fail_on_unmatched_files: true
@@ -0,0 +1,20 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .basedpyright_cache/
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ .idea/
12
+ .vscode/
13
+ .omo/
14
+ .codegraph/
15
+
16
+ # Generated by hatch-vcs at build time; mirrors the most recent git tag.
17
+ src/cgate/_version.py
18
+
19
+ # Generated by scripts/_ensure_repo.py; mirrors GITHUB_REPOSITORY / origin.
20
+ src/cgate/_repo.py
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wanderlp
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.
@@ -0,0 +1,204 @@
1
+ Metadata-Version: 2.5
2
+ Name: command-gate
3
+ Version: 0.2.4
4
+ Summary: Middleware and CLI between AI assistants and managed servers so AI proposes commands, humans approve them, and every action is audited.
5
+ Project-URL: Homepage, https://github.com/wanderlp/command-gate-for-ai-agents
6
+ Project-URL: Repository, https://github.com/wanderlp/command-gate-for-ai-agents
7
+ Project-URL: Issues, https://github.com/wanderlp/command-gate-for-ai-agents/issues
8
+ Project-URL: Changelog, https://github.com/wanderlp/command-gate-for-ai-agents/releases
9
+ Author: wanderlp
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-agents,audit,cli,human-in-the-loop,mcp,ssh,winrm
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Operating System :: POSIX :: Linux
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Security
25
+ Classifier: Topic :: System :: Systems Administration
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.11
28
+ Requires-Dist: keyring>=25.0
29
+ Requires-Dist: mcp>=1.0
30
+ Requires-Dist: packaging>=23.0
31
+ Requires-Dist: paramiko>=3.0
32
+ Requires-Dist: pydantic>=2.7
33
+ Requires-Dist: pywinrm>=0.4
34
+ Requires-Dist: requests>=2.31
35
+ Requires-Dist: rich>=13.0
36
+ Requires-Dist: sigstore>=4.5.0
37
+ Requires-Dist: textual>=8.2.8
38
+ Requires-Dist: typer>=0.12
39
+ Requires-Dist: typing-extensions>=4.12
40
+ Provides-Extra: dev
41
+ Requires-Dist: basedpyright>=1.13; extra == 'dev'
42
+ Requires-Dist: hatch-vcs>=0.5; extra == 'dev'
43
+ Requires-Dist: pyinstaller>=6.0; extra == 'dev'
44
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
45
+ Requires-Dist: pytest>=8; extra == 'dev'
46
+ Requires-Dist: ruff>=0.6; extra == 'dev'
47
+ Description-Content-Type: text/markdown
48
+
49
+ # command-gate-for-ai-agents (`cgate`)
50
+
51
+ Give your AI agents hands — safely.
52
+
53
+ AI agents can now write and run real shell commands against real servers. That's
54
+ powerful, and also the kind of thing that goes badly the first time an agent
55
+ misunderstands what you asked for. `cgate` sits between the agent and your
56
+ infrastructure: the agent proposes a command, a human decides whether it runs, and
57
+ every proposal, decision, and result is recorded.
58
+
59
+ You stop choosing between "the agent can't actually do anything" and "the agent can do
60
+ anything, unsupervised."
61
+
62
+ > **⚠️ Beta.** `cgate` is under active development. Expect rough edges, breaking
63
+ > changes between releases, and incoming revisions as it matures — feedback and bug
64
+ > reports are welcome.
65
+
66
+ ## Why teams use it
67
+
68
+ - **Agents propose, humans decide.** Every command an AI agent wants to run on a
69
+ connected server lands in an approval queue first. Nothing executes without a human
70
+ saying yes.
71
+ - **A full audit trail.** Every proposal, approval, rejection, and execution result is
72
+ recorded — who approved it, when, and what happened.
73
+ - **Drops into the AI tools you already use.** Registers itself as an MCP server for
74
+ Claude Code, opencode, Cursor, and other MCP-compatible clients.
75
+ - **One binary, nothing to configure.** Ships as a single executable per platform —
76
+ download it and run it, done.
77
+
78
+ ## Modes: PROPOSE and AUTO
79
+
80
+ `cgate` ships in two modes, and you pick per server which ones can ever run
81
+ unattended. The default is the safe one.
82
+
83
+ **PROPOSE** (default): every command an AI agent calls lands in the watch queue. You
84
+ press `y` or `n` per command. Nothing runs without you.
85
+
86
+ **AUTO**: when the agent calls `propose_command`, it runs immediately **only if the
87
+ target server is on your allow-list**. Servers are off by default — you opt them in
88
+ explicitly. Two switches need to land in the "go" position before anything runs
89
+ unattended: the global mode is AUTO *and* that specific server is opted in.
90
+
91
+ ```text
92
+ ┌─────────────────────────────────────────────────────────────┐
93
+ │ cgate watch MODE: AUTO ⚡ | 1 servers auto-allowed │
94
+ ├─────────────────────────────────────────────────────────────┤
95
+ │ [Queue] [Servers] │
96
+ │ ▶ "deploy v2" dev-1 LNX [✓] │
97
+ │ stage-2 LNX [ ] │
98
+ │ prod-db WIN [ ] │
99
+ ├─────────────────────────────────────────────────────────────┤
100
+ │ [y]approve [n]reject [a]approve batch [m]ode [h]istory ... │
101
+ └─────────────────────────────────────────────────────────────┘
102
+ ```
103
+
104
+ ↑/↓ + Enter on the queue sidebar jumps straight to any pending batch — you're not
105
+ forced to clear the queue in strict arrival order to get to the one you actually
106
+ want to approve next.
107
+
108
+ ### Switching modes
109
+
110
+ Press **`m`** in `cgate watch` to flip the global mode (one keystroke + `y` to
111
+ confirm). Press **`s`** to open the server allow-list and toggle any connection on or
112
+ off. Both modals need an explicit `y` or `Enter`; everything else cancels without
113
+ writing. AI agents have no MCP tool that can change either setting — only your
114
+ keystrokes can.
115
+
116
+ ### Why two switches?
117
+
118
+ If you leave `MODE: AUTO` on by accident, the worst case is that nothing runs on
119
+ servers that haven't been explicitly opted in. To actually run something
120
+ unattended you must enable the mode **and** flip the per-server switch. Belt and
121
+ suspenders, default-safe at every level.
122
+
123
+ ### A third switch: high-blast-radius commands never auto-run
124
+
125
+ Even with both switches flipped, a command matching a known high-blast-radius
126
+ pattern — a recursive force-delete, a raw disk write, deleting shadow copies or
127
+ backups, disabling System Restore, and similar — always queues for a human. It
128
+ shows a bold red `⚠ RISKY` warning in `cgate watch` (in **either** mode, so you
129
+ notice it in PROPOSE too, not only when it changed what AUTO would have done),
130
+ and the MCP response carries `effective_reason: "risky_command"` so the agent
131
+ knows why. This is a regex-based heuristic, not a guarantee — it catches the
132
+ common, obvious forms (see `src/cgate/risk.py`), not every possible obfuscation.
133
+ Absence of the flag is not the same as "safe."
134
+
135
+ ### The audit trail still works
136
+
137
+ Every command a batch ever queued shows an `⚙ auto` or `👤 <username>` badge next
138
+ to it, so you always know whether a human or AUTO mode approved it — and once a
139
+ batch resolves, it doesn't vanish: press **`h`** in `cgate watch` to browse every
140
+ resolved batch, see the agent's stated `reason` for wanting the command, and view
141
+ the full, untruncated result. Press **`/`** there to filter live against a batch's
142
+ title, its commands, and their server aliases all at once -- `Esc` clears the
143
+ filter first, then closes the screen on a second press.
144
+
145
+ Need it outside the TUI — for a compliance report, or just to grep offline?
146
+
147
+ ```bash
148
+ cgate history export # full audit trail as CSV, to stdout
149
+ cgate history export --format json -o audit.json
150
+ cgate history export --limit 100 # only the 100 most recently resolved batches
151
+ ```
152
+
153
+ Human-only: this isn't exposed to AI agents over MCP. An agent that proposed a
154
+ batch can already poll `check_status(batch_id)` for it at any time, resolved or
155
+ not, so there's no reason to hand it a browse of everyone else's history too.
156
+
157
+ Prefer raw SQL? The same data is still just a query away:
158
+
159
+ ```sql
160
+ SELECT * FROM commands WHERE approved_by LIKE 'auto:%';
161
+ ```
162
+
163
+ ### What the AI sees (and what it can ask first)
164
+
165
+ The MCP server exposes three read-only tools the AI can use to plan with full
166
+ information, plus `propose_command` to act:
167
+
168
+ - **`list_connections`** — every saved connection and its `auto_allowed` flag
169
+ (the AI knows up front which servers could auto-execute).
170
+ - **`get_mode`** — the current global mode (`propose` or `auto`) and the list of
171
+ aliases opted into auto-execution. Read-only, safe to call any time.
172
+ - **`check_status`** — what's pending in a given batch.
173
+
174
+ With these, the AI never has to guess whether a command will auto-run or wait
175
+ for you — it can call `get_mode` and `list_connections` first, plan accordingly,
176
+ and only invoke `propose_command` when it knows what will happen.
177
+
178
+ ## Get started
179
+
180
+ 1. Download the binary for your platform from the
181
+ [latest release](https://github.com/wanderlp/command-gate-for-ai-agents/releases/latest).
182
+ 2. Run it (double-click it, or just `.\cgate-windows-amd64.exe` from the folder you
183
+ downloaded it to). On first run it installs itself into a per-user bin directory,
184
+ adds that directory to your `PATH`, and registers with any AI clients it finds —
185
+ no admin/sudo, no flags to remember.
186
+ 3. Open a new terminal and restart your AI client. Done.
187
+
188
+ From there, `cgate watch` opens a live approval queue: agent proposals show up as they
189
+ come in, you approve or reject with a keypress, and the result streams back to the
190
+ agent.
191
+
192
+ > First run on Windows or macOS may show a one-time "unrecognized publisher" warning —
193
+ > these binaries aren't code-signed yet. See
194
+ > [TECHNICAL.md](./TECHNICAL.md#first-run-warnings-binaries-are-not-code-signed) for
195
+ > what to do about it.
196
+
197
+ ## Learn more
198
+
199
+ Package-manager installs, architecture, development setup, and building from source
200
+ live in [TECHNICAL.md](./TECHNICAL.md).
201
+
202
+ ## License
203
+
204
+ [MIT](./LICENSE)
@@ -0,0 +1,156 @@
1
+ # command-gate-for-ai-agents (`cgate`)
2
+
3
+ Give your AI agents hands — safely.
4
+
5
+ AI agents can now write and run real shell commands against real servers. That's
6
+ powerful, and also the kind of thing that goes badly the first time an agent
7
+ misunderstands what you asked for. `cgate` sits between the agent and your
8
+ infrastructure: the agent proposes a command, a human decides whether it runs, and
9
+ every proposal, decision, and result is recorded.
10
+
11
+ You stop choosing between "the agent can't actually do anything" and "the agent can do
12
+ anything, unsupervised."
13
+
14
+ > **⚠️ Beta.** `cgate` is under active development. Expect rough edges, breaking
15
+ > changes between releases, and incoming revisions as it matures — feedback and bug
16
+ > reports are welcome.
17
+
18
+ ## Why teams use it
19
+
20
+ - **Agents propose, humans decide.** Every command an AI agent wants to run on a
21
+ connected server lands in an approval queue first. Nothing executes without a human
22
+ saying yes.
23
+ - **A full audit trail.** Every proposal, approval, rejection, and execution result is
24
+ recorded — who approved it, when, and what happened.
25
+ - **Drops into the AI tools you already use.** Registers itself as an MCP server for
26
+ Claude Code, opencode, Cursor, and other MCP-compatible clients.
27
+ - **One binary, nothing to configure.** Ships as a single executable per platform —
28
+ download it and run it, done.
29
+
30
+ ## Modes: PROPOSE and AUTO
31
+
32
+ `cgate` ships in two modes, and you pick per server which ones can ever run
33
+ unattended. The default is the safe one.
34
+
35
+ **PROPOSE** (default): every command an AI agent calls lands in the watch queue. You
36
+ press `y` or `n` per command. Nothing runs without you.
37
+
38
+ **AUTO**: when the agent calls `propose_command`, it runs immediately **only if the
39
+ target server is on your allow-list**. Servers are off by default — you opt them in
40
+ explicitly. Two switches need to land in the "go" position before anything runs
41
+ unattended: the global mode is AUTO *and* that specific server is opted in.
42
+
43
+ ```text
44
+ ┌─────────────────────────────────────────────────────────────┐
45
+ │ cgate watch MODE: AUTO ⚡ | 1 servers auto-allowed │
46
+ ├─────────────────────────────────────────────────────────────┤
47
+ │ [Queue] [Servers] │
48
+ │ ▶ "deploy v2" dev-1 LNX [✓] │
49
+ │ stage-2 LNX [ ] │
50
+ │ prod-db WIN [ ] │
51
+ ├─────────────────────────────────────────────────────────────┤
52
+ │ [y]approve [n]reject [a]approve batch [m]ode [h]istory ... │
53
+ └─────────────────────────────────────────────────────────────┘
54
+ ```
55
+
56
+ ↑/↓ + Enter on the queue sidebar jumps straight to any pending batch — you're not
57
+ forced to clear the queue in strict arrival order to get to the one you actually
58
+ want to approve next.
59
+
60
+ ### Switching modes
61
+
62
+ Press **`m`** in `cgate watch` to flip the global mode (one keystroke + `y` to
63
+ confirm). Press **`s`** to open the server allow-list and toggle any connection on or
64
+ off. Both modals need an explicit `y` or `Enter`; everything else cancels without
65
+ writing. AI agents have no MCP tool that can change either setting — only your
66
+ keystrokes can.
67
+
68
+ ### Why two switches?
69
+
70
+ If you leave `MODE: AUTO` on by accident, the worst case is that nothing runs on
71
+ servers that haven't been explicitly opted in. To actually run something
72
+ unattended you must enable the mode **and** flip the per-server switch. Belt and
73
+ suspenders, default-safe at every level.
74
+
75
+ ### A third switch: high-blast-radius commands never auto-run
76
+
77
+ Even with both switches flipped, a command matching a known high-blast-radius
78
+ pattern — a recursive force-delete, a raw disk write, deleting shadow copies or
79
+ backups, disabling System Restore, and similar — always queues for a human. It
80
+ shows a bold red `⚠ RISKY` warning in `cgate watch` (in **either** mode, so you
81
+ notice it in PROPOSE too, not only when it changed what AUTO would have done),
82
+ and the MCP response carries `effective_reason: "risky_command"` so the agent
83
+ knows why. This is a regex-based heuristic, not a guarantee — it catches the
84
+ common, obvious forms (see `src/cgate/risk.py`), not every possible obfuscation.
85
+ Absence of the flag is not the same as "safe."
86
+
87
+ ### The audit trail still works
88
+
89
+ Every command a batch ever queued shows an `⚙ auto` or `👤 <username>` badge next
90
+ to it, so you always know whether a human or AUTO mode approved it — and once a
91
+ batch resolves, it doesn't vanish: press **`h`** in `cgate watch` to browse every
92
+ resolved batch, see the agent's stated `reason` for wanting the command, and view
93
+ the full, untruncated result. Press **`/`** there to filter live against a batch's
94
+ title, its commands, and their server aliases all at once -- `Esc` clears the
95
+ filter first, then closes the screen on a second press.
96
+
97
+ Need it outside the TUI — for a compliance report, or just to grep offline?
98
+
99
+ ```bash
100
+ cgate history export # full audit trail as CSV, to stdout
101
+ cgate history export --format json -o audit.json
102
+ cgate history export --limit 100 # only the 100 most recently resolved batches
103
+ ```
104
+
105
+ Human-only: this isn't exposed to AI agents over MCP. An agent that proposed a
106
+ batch can already poll `check_status(batch_id)` for it at any time, resolved or
107
+ not, so there's no reason to hand it a browse of everyone else's history too.
108
+
109
+ Prefer raw SQL? The same data is still just a query away:
110
+
111
+ ```sql
112
+ SELECT * FROM commands WHERE approved_by LIKE 'auto:%';
113
+ ```
114
+
115
+ ### What the AI sees (and what it can ask first)
116
+
117
+ The MCP server exposes three read-only tools the AI can use to plan with full
118
+ information, plus `propose_command` to act:
119
+
120
+ - **`list_connections`** — every saved connection and its `auto_allowed` flag
121
+ (the AI knows up front which servers could auto-execute).
122
+ - **`get_mode`** — the current global mode (`propose` or `auto`) and the list of
123
+ aliases opted into auto-execution. Read-only, safe to call any time.
124
+ - **`check_status`** — what's pending in a given batch.
125
+
126
+ With these, the AI never has to guess whether a command will auto-run or wait
127
+ for you — it can call `get_mode` and `list_connections` first, plan accordingly,
128
+ and only invoke `propose_command` when it knows what will happen.
129
+
130
+ ## Get started
131
+
132
+ 1. Download the binary for your platform from the
133
+ [latest release](https://github.com/wanderlp/command-gate-for-ai-agents/releases/latest).
134
+ 2. Run it (double-click it, or just `.\cgate-windows-amd64.exe` from the folder you
135
+ downloaded it to). On first run it installs itself into a per-user bin directory,
136
+ adds that directory to your `PATH`, and registers with any AI clients it finds —
137
+ no admin/sudo, no flags to remember.
138
+ 3. Open a new terminal and restart your AI client. Done.
139
+
140
+ From there, `cgate watch` opens a live approval queue: agent proposals show up as they
141
+ come in, you approve or reject with a keypress, and the result streams back to the
142
+ agent.
143
+
144
+ > First run on Windows or macOS may show a one-time "unrecognized publisher" warning —
145
+ > these binaries aren't code-signed yet. See
146
+ > [TECHNICAL.md](./TECHNICAL.md#first-run-warnings-binaries-are-not-code-signed) for
147
+ > what to do about it.
148
+
149
+ ## Learn more
150
+
151
+ Package-manager installs, architecture, development setup, and building from source
152
+ live in [TECHNICAL.md](./TECHNICAL.md).
153
+
154
+ ## License
155
+
156
+ [MIT](./LICENSE)