- 自然言語の依頼を Google Gemini / OpenAI に渡してシェルコマンドを1つ生成する CLI ツールです。
- AI の「安全そう」という判断は信用せず、AI とは独立した決定論的なローカルポリシーで危険度(
low / medium / high / blocked)を再判定します。 - 既定では 確認プロンプトを経て実行します。
--dry-runを付ければ検査だけで止められます。 -
blocked(sudo、リモートスクリプト実行、ディスク消去、ルート削除など)は実行できません。 - リポジトリ: https://github.com/softjapan/ai-shell-agent
⚠️ 重要: このツールの安全性検査は補助的なガードレールであり、OS サンドボックスではありません。表示されたコマンド・作業ディレクトリ・危険度を必ず確認してください。
こんな課題を解きたい
「あのコマンドのオプション、なんだっけ」を毎回ぐぐるのは地味に時間を食います。かといって、AI が出したコマンドをそのままシェルに貼って実行するのは怖い。特に rm や権限変更のような取り返しのつかない操作は事故のもとです。
そこで、次の2つを両立させることを目標にしました。
- 自然言語からコマンドを手軽に生成する
- 生成物をそのまま鵜呑みにせず、実行前にローカルで危険度を判定して歯止めをかける
ポイントは「AI に安全性を判断させない」ことです。LLM に「これは安全ですか?」と聞くのではなく、生成されたコマンド文字列を、AI とは無関係な決定論的ルールで検査します。
使い方
インストール
uv を使うのが簡単です。
git clone https://github.com/softjapan/ai-shell-agent.git
cd ai-shell-agent
uv sync --locked
利用するプロバイダーの API キーを設定します(両方設定する必要はありません)。
# Google(既定プロバイダー)
export GEMINI_API_KEY="your-gemini-api-key"
# OpenAI
export OPENAI_API_KEY="your-openai-api-key"
まずは検査だけ(--dry-run)
いきなり実行せず、生成と危険度判定だけを見たいときは --dry-run を付けます。
uv run ai-shell-agent --dry-run "現在のディレクトリにあるPythonファイルを表示"
既定はシンプルな出力です。
[AI Answer]: find . -maxdepth 1 -name '*.py' -print
Dry run only. Re-run without --dry-run to allow confirmation and execution.
--verbose を付けると、プロバイダー・モデル・危険度・作業ディレクトリ・シェルなどの詳細も表示されます。
Command find . -maxdepth 1 -name '*.py' -print
Details 現在のディレクトリ直下にあるPythonファイルを表示します。
Provider openai (gpt-4o-mini)
Risk low
↳ matches a recognized read-only command
Workdir /path/to/project
Shell /bin/zsh
Dry run only. Re-run without --dry-run to allow confirmation and execution.
実行(既定)
--dry-run を外すと、生成・検査のあと確認プロンプトに進みます。
uv run ai-shell-agent "現在のディレクトリを表示"
[AI Answer]: ls
Execute? Y/N: y
(ls の結果)
high(削除・権限変更・プロセス終了など)のときは、実行前に警告と理由が表示されます。
[AI Answer]: rm old.txt
⚠ High-risk command: it may delete data, change permissions, or otherwise be hard to undo.
- deletes files or directories
Execute? Y/N:
blocked は同意しても実行できません。
設計:AI 生成と安全判定を分離する
このツールの肝は、AI によるコマンド生成とローカルの安全判定を完全に分けていることです。
ユーザー入力 + provider
↓
ai_shell_agent.generator Google/OpenAIによるCommandPlan生成
↓
ai_shell_agent.models Pydanticによる構造検証
↓
ai_shell_agent.policy プロバイダー非依存のローカル危険度判定
↓
ai_shell_agent.cli dry-run・危険度別の対話確認
↓
ai_shell_agent.executor subprocess・timeout・終了コード
生成とプロセス実行が分離されているので、テストでは両方の境界をモックでき、外部 API や実コマンドを一切呼ばずに検証できます。
1. 構造化出力で受け取る
LLM の出力は Pydantic モデル CommandPlan として受け取り、「成功なら単一行コマンドのみ」「失敗なら理由のみ」といった整合性を検証します。改行や NUL 文字を含むコマンドは弾きます。プロンプトの段階でも、Markdown フェンスを付けない・単一行にする・むやみに sudo を付けないといった方針を明示しています。
2. AI とは独立したローカルポリシー
生成されたコマンド文字列を、決定論的なルールで4段階に分類します。
| 危険度 | 例 | 動作 |
|---|---|---|
| low |
pwd, ls, git status
|
Y/N確認後に実行可能 |
| medium | ファイル作成、ネットワーク、未知のコマンド | Y/N確認後に実行可能 |
| high | ファイル削除、権限変更、プロセス終了 | 警告と理由を表示し、Y/N確認後に実行可能 |
| blocked |
sudo、リモートスクリプト実行、ディスク消去、ルート削除 |
実行不可 |
blocked の代表例は次のようなものです。
-
curl ... | shのようなリモートスクリプト実行 -
sudoによる権限昇格 -
mkfs/diskutil erase/dd of=/dev/...のようなディスク破壊 -
rm -rf /のようなルートからの広域削除 - fork bomb
文字列解析ベースなので誤検出・見逃しの可能性はあります。ドキュメントでもその旨を明記し、機密環境ではコンテナや専用ユーザー、OS サンドボックスなど追加の隔離を推奨しています。あくまで「補助的なガードレール」という位置づけです。
3. 制御されたプロセス実行
実行は os.system ではなく subprocess を使い、次を制御します。
- 実行に使うシェルを検証(
sh,bash,zsh,dash,kshのみ許可) - 作業ディレクトリの存在確認
- タイムアウト(既定30秒)
- 終了コードの伝播
プロバイダーの切り替え
Google Gemini(既定)と OpenAI を --provider で切り替えられます。
# Google(既定)
uv run ai-shell-agent "現在のディレクトリを表示"
# OpenAI
uv run ai-shell-agent --provider openai "現在のディレクトリを表示"
# モデル指定も可能
uv run ai-shell-agent --provider openai --model gpt-4o "Gitの変更内容を表示"
既定モデルは Google が gemini-2.5-flash、OpenAI が gpt-4o-mini です。
毎回 --provider を付けたくない場合は、環境変数で既定を変更できます。CLI の --provider は常に優先されます。
export AI_SHELL_AGENT_PROVIDER="openai"
uv run ai-shell-agent "現在のディレクトリを表示" # OpenAI を使用
uv run ai-shell-agent --provider google "現在のディレクトリを表示" # 明示指定が優先
優先順位は次のとおりです。
- CLI の
--provider(最優先) - 環境変数
AI_SHELL_AGENT_PROVIDER - 既定(
google)
API キーの扱い:環境変数を最優先
API キーは環境変数が最優先です。任意で .env を使う場合も、環境変数に未設定のキーだけを補完し、既存の環境変数を上書きしません。秘密情報の管理はシェル・CI・シークレットマネージャに委ね、ツールは生成・検査・実行に専念する設計です。
cp .env.example .env
# .env に使うプロバイダーのキーを設定
-
.envの場所は--env-file PATHで変更可能 -
--env-file /dev/nullで.env読み込みを無効化 -
.envは Git 管理から除外(.env.exampleのみコミット対象)
.env の自動読み込みには専用ライブラリを追加せず、依存を増やさない小さなパーサで実装しています(export KEY=value 形式やクォート、行末コメントに対応)。
終了コード
スクリプトや CI から扱いやすいように、終了コードを整理しています。
| コード | 意味 |
|---|---|
| 0 | dry-run完了、キャンセル、またはコマンド成功 |
| 2 | 引数、APIキー、生成処理のエラー |
| 3 | ローカル安全ポリシーによるブロック |
| 4 | コマンド開始前の実行エラー |
| 124 | コマンドタイムアウト |
| 130 | Ctrl+Cによる中断 |
| その他 | 実行したコマンドの終了コード |
テストと CI
外部 API と実コマンドの境界をモックしているため、テストは決定論的です。API キーを消費せず、実際に破壊的コマンドを走らせることもありません。
- 危険コマンドのブロック、高リスク判定、読み取り専用コマンドの許可
- dry-run で実行されないこと、確認プロンプトの挙動
- プロバイダー選択・既定解決・API エラーの秘匿処理
-
.env補完と環境変数優先の優先順位 - 終了コードの伝播、タイムアウト
CI(GitHub Actions)では、Python 3.10〜3.13 で以下を回しています。
uv run black --check .
uv run ruff check .
uv run pyright
uv run pytest
uv build
プロジェクト構成
ai-shell-agent/
├── ai_shell_agent/
│ ├── __init__.py # バージョン情報
│ ├── cli.py # provider選択、dry-run、確認、終了コード
│ ├── config.py # 環境変数優先の任意.env補完
│ ├── executor.py # 制御されたサブプロセス実行
│ ├── formatting.py # 依存なしのANSIカラー補助
│ ├── generator.py # Google/OpenAI連携とAPIエラー処理
│ ├── models.py # 構造化出力とドメイン型
│ └── policy.py # ローカル安全ポリシー
├── tests/ # API・プロセスをモックした単体テスト
├── .github/workflows/ # CI
├── .env.example # キー名のみのテンプレート
├── as.py # 後方互換ラッパー
├── pyproject.toml
├── requirements.txt
├── uv.lock
└── LICENSE
設計上のこだわりどころ
- AI に安全性を判断させない: 生成と検査を分離し、検査は決定論的ルールで行う。
- 既定は実行だが、必ず確認を挟む: 手軽さと安全のバランス。破壊的操作は警告、危険すぎるものはブロック。
-
秘密情報を漏らさない: API エラー本文や作業ディレクトリのフルパスを AI へのプロンプトに含めない。環境変数を最優先し、
.envは上書きしない。 - 依存は最小限・固定: 使うプロバイダーの依存だけに絞り、バージョンをピン留め。
- テスト可能な境界: 生成もプロセス実行もモックできる構造。
まとめ
「AI が出したコマンドを、AI に安全確認させずローカルで再検査してから実行する」というシンプルな方針で、自然言語シェルコマンド生成の実用性と安全性を両立させました。
とはいえ、繰り返しになりますが、これはサンドボックスではなく補助的なガードレールです。最終的な実行判断は利用者の責任で行ってください。
興味があればリポジトリをのぞいてみてください。フィードバックや Issue も歓迎です。