スマホだけで完結!TermuxでGemini AIエージェントを自作した話
📅 2026年2月19日 | 📁 開発体験談
1. はじめに:ジェンスパークのコストが悩みの種だった
このブログ「ジェンスパーク開発奮闘記」は、ブログシステム自体からジェンスパーク(Genspark)で作っています。記事の体裁を整えたり、投稿するのも全部ジェンスパーク任せです。とても便利なのですが、使用するにはコストがかかります。記事を追加するたびにクレジットが減っていくのが、正直なところ悩みの種でした。
そこで「維持管理や記事追加だけ別のツールでできないか」と色々探し始めました。しかし、自分の要望が特殊なのか、しっくりくるものが全然見つかりませんでした。
- とにかくスマホだけで完結したい
- ブログのソースはGitHub管理、Cloudflareで公開、D1データベースに記事保存という構成
- 自然言語で指示して、デプロイまでの工程を自動化したい
Cursor や Claude Code はPC前提ですし、そもそも有料です。Firebase は環境的には良さそうでしたが、入っているAIエージェントの能力が低く、GitHubなど外部へのアクセスができませんでした。
2. スマホ完結の開発環境を探して
スマホで開発作業を完結させたいというのは、一見すると無茶な要望に聞こえるかもしれません。しかし、20年間ソフトウェアエンジニアをやってきた筆者としては「もうパソコンを広げたくない」というわがままな理由がありました。
既存のモバイル向け開発ツールをいくつか試しましたが、GitHubへのアクセス権限がなかったり、自然言語でのコマンド実行ができなかったりと、いずれも要件を満たしませんでした。そこでたどり着いたのが、AndroidスマホにLinux環境を構築できる Termux というアプリです。
3. Termuxとの出会いと苦難のインストール
Termuxは、Android上でLinuxのターミナル環境を動かせるアプリです。bash、python3、git、curlなどの標準的なツールが使えるため、理論上はLinuxでできることの多くがスマホ上で実現できます。
インストール自体がなかなかの難関でした。最初にPlayストア版を入れたものの、開発停止と聞いてF-Droid版へ入れ直し。インストールが途中で固まって何も起きない時間が続き、2回目でなぜかすんなり完了。さらにGoogleからセキュリティ警告が何度も届くという洗礼を受けながら、なんとか環境構築に成功しました。
pkg update && pkg upgrade でパッケージを最新化し、pkg install git python curl で基本ツールを入れておきましょう。
4. AIエージェントを自作することにした
環境が整ったら次は自然言語で指示できるツールです。Termuxで使えるAIチャットツール(ai chatなど)を探しましたが、どうもTermuxには対応していないようでインストールできず。Gemini公式CLIツールも同様でした。
そこで発想を転換し、「Gemini APIを直接叩いて、自分でエージェントを作ってしまおう」という結論に至りました。必要な材料はbash、python3、curlだけ。どれもTermuxに標準で入っています。
AIエージェントの作成自体は、Claude(Anthropic)に相談しながら進めました。試行錯誤の末、デバッグや設計方針の見直しを繰り返し、最終的にv1.08として動作する状態になりました。
5. エージェントの処理フロー
完成したエージェントの基本的な処理フローはこのようになっています。
ユーザー指示 → ①Geminiが指示を分析しコマンド判断 → コマンド実行(git, curl, npm など) → ②Geminiが結果を判断・説明 → ユーザーが続行/終了/追加指示を選択
ポイントは「Geminiを2回呼ぶ」設計です。1回目(①)で何をすべきかを判断し、コマンドを実行。2回目(②)でその結果を評価し、タスクが完了したか次のステップが必要かを判断します。ユーザーはその②の判断を見た上で y(続行) / n(終了) / 自由なメッセージで追加指示を出せます。
TYPE: COMMAND— シェルコマンドを実行TYPE: SEARCH— Brave APIまたはDuckDuckGoで検索TYPE: IMAGE— スクリーンショットを解析TYPE: NEED_INPUT— ユーザーへの確認TYPE: DONE— タスク完了TYPE: NEXT— 次のステップへ継続
使用モデルは当初Gemini 2.5 Flashでしたが、能力的に追いつかない場面が多く、Gemini 3.0 Flash Preview(gemini-3-flash-preview)に変更しました。APIキーは1つで全モデルに対応しているため、モデル名を変えるだけで切り替えられます。
6. 出来上がったもの・出来なかったもの
- GitHubへのアクセス(clone、push、pull など)
- GitHub Actionsを使った自動デプロイ(pushでCloudflareへ自動反映)
- プロジェクトごとの設定・履歴管理(.ai_config / .ai_history)
- ちょっとしたソースコード修正やファイル操作
- 長い文章の記事作成(Gemini APIのトークン制限の関係で、記事作成はジェンスパークのAIチャットで行う)
- Wranglerを使ったCloudflareへの直接デプロイ(TermuxへのWranglerインストールが困難なため断念し、GitHub Actions経由のCI/CDで代替)
最初は「大型バイクが欲しかったけど、苦労の末に手に入れたのは原チャリだった」という感覚もありました(笑)。ただ、スマホ単体でGitHub連携・ビルド・デプロイまでの一連の作業を自然言語で指示できるのは、それなりに実用的です。
7. どんな人におすすめか
このアプローチが特に合いそうな方を考えてみました。
- 移動が多いSEやエンジニア — 電車や出張先でのちょっとした修正・確認作業に使えます
- PCを持ち歩きたくない開発者 — スマホ1台で軽微な開発作業を完結させたい方
- Linux/bashに慣れている方 — スクリプトをカスタマイズして自分好みの環境に育てられます
- Gemini APIを試してみたい方 — フレームワーク不要でAPIの動作を手軽に確認できます
逆に、大規模なCLI自動化や複雑なフロントエンド開発には向きません。あくまでも「スマホで軽くGitHub操作や設定変更をしたい」という用途にフィットします。
8. 完成スクリプト(全文公開)
試行錯誤の末に完成したスクリプトを全文公開します。ClaudeのAPIを使って作成したものです。中身はほとんど確認していないので、ご利用は自己責任でお願いします。
GOOGLE_API_KEY を環境変数に設定してください。export GOOGLE_API_KEY='your-api-key' を ~/.bashrc に追記して source ~/.bashrc を実行します。APIキーは Google AI Studio から取得できます。
スクリプトを ~/bin/ai として保存し、chmod +x ~/bin/ai で実行権限を付与してください。
#!/bin/bash
# AI Assistant v1.08
# Mobile Development AI Assistant powered by Gemini
# ============================================================
# 定数・環境変数
# ============================================================
GOOGLE_API_KEY="${GOOGLE_API_KEY:-}"
BRAVE_API_KEY="${BRAVE_API_KEY:-}"
MODEL="gemini-3-flash-preview"
API_URL="https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent"
SCREENSHOT_DIR="${HOME}/storage/pictures/Screenshots"
CURRENT_PROJECT_FILE="${HOME}/.ai_current_project"
IMAGE_SCRIPT="${HOME}/bin/ai_image.py"
TASK_RESULT_FILE="${HOME}/tmp/ai_task_result.tmp"
HISTORY_FILE="${HOME}/.ai_history"
TMP_DIR="${HOME}/tmp"
PAYLOAD_FILE="${TMP_DIR}/ai_payload.tmp"
# ============================================================
# プロジェクト管理
# ============================================================
PROJECT_NAME_CURRENT=""
PROJECT_DIR_CURRENT=""
PROJECT_CONFIG=""
PROJECT_CONTEXT=""
load_config() {
PROJECT_NAME_CURRENT=""
PROJECT_DIR_CURRENT=""
PROJECT_CONFIG=""
PROJECT_CONTEXT=""
HISTORY_FILE="${HOME}/.ai_history"
if [[ -f "$CURRENT_PROJECT_FILE" ]]; then
local project_path
project_path=$(cat "$CURRENT_PROJECT_FILE")
if [[ -n "$project_path" && -d "$project_path" ]]; then
PROJECT_DIR_CURRENT="$project_path"
PROJECT_NAME_CURRENT=$(basename "$project_path")
HISTORY_FILE="${PROJECT_DIR_CURRENT}/.ai_history"
[[ -f "${PROJECT_DIR_CURRENT}/.ai_config" ]] && PROJECT_CONFIG=$(cat "${PROJECT_DIR_CURRENT}/.ai_config")
[[ -f "${PROJECT_DIR_CURRENT}/.ai_context" ]] && PROJECT_CONTEXT=$(cat "${PROJECT_DIR_CURRENT}/.ai_context")
while IFS='=' read -r key value; do
[[ "$key" =~ ^[[:space:]]*# ]] && continue
[[ -z "$key" ]] && continue
key=$(echo "$key" | tr -d ' ')
value=$(echo "$value" | tr -d '"' | tr -d "'")
export "$key=$value"
done < <(grep '=' "${PROJECT_DIR_CURRENT}/.ai_config" 2>/dev/null)
fi
fi
}
restore_last_project() { load_config; }
detect_project_switch() {
local input="$1"
echo "$input" | grep -qiE "プロジェクト.*(切|替|変)|switch.*project|project.*switch"
}
# ============================================================
# システムプロンプト
# ============================================================
build_system_prompt() {
cat << SYSPROMPT
あなたはモバイル開発支援AIアシスタントです。
## 【設定ファイル】プロジェクト情報
- プロジェクト名: ${PROJECT_NAME_CURRENT:-未選択}
- プロジェクトディレクトリ: ${PROJECT_DIR_CURRENT:-未設定}
- 履歴ファイル: ${HISTORY_FILE}
## 【設定ファイル】.ai_config
${PROJECT_CONFIG:-設定なし}
## 【設定ファイル】.ai_context
${PROJECT_CONTEXT:-コンテキストなし}
## コマンド実行ルール(最重要・厳守)
- git, curl, npm, cp, mv などは必ずCOMMAND形式で自分で実行すること
- 「ユーザーが実行してください」は絶対禁止
- cdコマンド単体は使わず「cd /path && 次のコマンド」の形式にすること
## 応答形式(TYPE行を最初に出力)
TYPE: COMMAND / SEARCH / IMAGE / NEED_INPUT / DONE / NEXT
SYSPROMPT
}
build_continuation_prompt() {
local original_task="$1"
local last_result="$2"
cat << CONTPROMPT
## 【元のタスク】
${original_task}
## 【直前の実行結果】
${last_result}
タスクは完了しましたか?結果をわかりやすく説明した上で継続判断してください。
応答形式: TYPE: DONE / NEXT / NEED_INPUT
CONTPROMPT
}
# ============================================================
# Gemini API呼び出し
# ============================================================
_call_gemini_with_prompt() {
local user_message="$1"
local system_prompt history_content payload response text
system_prompt=$(build_system_prompt)
[[ -f "$HISTORY_FILE" ]] && history_content=$(tail -50 "$HISTORY_FILE")
payload=$(python3 -c "
import json, sys
sp = sys.argv[1]; msg = sys.argv[2]; hist = sys.argv[3]
full_sys = sp + '\n\n## 【過去の会話履歴】\n' + (hist if hist else '履歴なし') + '\n---以上が過去の履歴---'
print(json.dumps({'system_instruction':{'parts':[{'text':full_sys}]},'contents':[{'role':'user','parts':[{'text':msg}]}],'generationConfig':{'temperature':0.7,'maxOutputTokens':2048}}))
" "$system_prompt" "$user_message" "$history_content" 2>/dev/null)
[[ -z "$payload" ]] && echo "ERROR: payload build failed" >&2 && return 1
echo "$payload" > "$PAYLOAD_FILE"
response=$(curl -s -X POST "${API_URL}?key=${GOOGLE_API_KEY}" \
-H "Content-Type: application/json" -d @"$PAYLOAD_FILE" 2>/dev/null)
[[ -z "$response" ]] && echo "ERROR: empty response" >&2 && return 1
text=$(echo "$response" | python3 -c "
import json,sys
try:
d=json.load(sys.stdin); print(d['candidates'][0]['content']['parts'][0]['text'])
except Exception as e:
print('ERROR:'+str(e),file=sys.stderr); sys.exit(1)
" 2>/dev/null)
[[ -z "$text" ]] && echo "ERROR: text extract failed" >&2 && return 1
echo "$text"
}
call_gemini() {
_call_gemini_with_prompt "$(printf '## 【最新の指示】\n%s' "$1")"
}
call_gemini_continuation() {
_call_gemini_with_prompt "$(build_continuation_prompt "$1" "$2")"
}
# ============================================================
# 履歴管理
# ============================================================
save_history() {
printf "[%s] %s: %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" "$2" >> "$HISTORY_FILE"
local lc; lc=$(wc -l < "$HISTORY_FILE" 2>/dev/null || echo 0)
[[ "$lc" -gt 200 ]] && tail -100 "$HISTORY_FILE" > "${HISTORY_FILE}.tmp" && mv "${HISTORY_FILE}.tmp" "$HISTORY_FILE"
}
# ============================================================
# Web検索
# ============================================================
search_web() {
local query="$1" results=""
local enc; enc=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$query" 2>/dev/null)
[[ -n "$BRAVE_API_KEY" ]] && results=$(curl -s \
-H "X-Subscription-Token: ${BRAVE_API_KEY}" \
"https://api.search.brave.com/res/v1/web/search?q=${enc}&count=3" 2>/dev/null | \
python3 -c "
import json,sys
try:
d=json.load(sys.stdin)
for r in d.get('web',{}).get('results',[])[:3]:
print(f'タイトル: {r.get(\"title\",\"\")}\nURL: {r.get(\"url\",\"\")}\n概要: {r.get(\"description\",\"\")}\n---')
except: pass
" 2>/dev/null)
[[ -z "$results" ]] && results=$(curl -s "https://html.duckduckgo.com/html/?q=${enc}" 2>/dev/null | \
grep -o '<a class="result__a"[^>]*>[^<]*</a>' | head -3 | sed 's/<[^>]*>//g')
echo "${results:-検索結果なし}"
}
# ============================================================
# 画像解析
# ============================================================
handle_image_request() {
local image_path="$1"
[[ "$image_path" == "latest" || -z "$image_path" ]] && \
image_path=$(ls -t "$SCREENSHOT_DIR"/*.png "$SCREENSHOT_DIR"/*.jpg 2>/dev/null | head -1)
[[ -z "$image_path" || ! -f "$image_path" ]] && echo "画像ファイルが見つかりません" && return 1
python3 "$IMAGE_SCRIPT" "$image_path" "この画像を詳しく説明してください" 2>/dev/null || echo "画像解析エラー"
}
# ============================================================
# コマンド実行
# ============================================================
execute_command() {
local cmd="$1"
local dangerous_patterns=("rm -rf /" "mkfs" "dd if=" ":(){ :|:& };" "> /dev/sda")
for pattern in "${dangerous_patterns[@]}"; do
echo "$cmd" | grep -qF "$pattern" && echo "⚠️ 危険なコマンドをブロック: $cmd" && return 1
done
if echo "$cmd" | grep -qE "^rm "; then
echo -n "⚠️ 削除コマンドを実行しますか? ($cmd) [y/N]: "
read -r confirm
[[ "$confirm" != "y" && "$confirm" != "Y" ]] && echo "キャンセル" && return 1
fi
echo "🔧 実行: $cmd"
local result exit_code
result=$(eval "$cmd" 2>&1); exit_code=$?
echo "$result"; echo "終了コード: $exit_code"
printf "%s\n終了コード: %d" "$result" "$exit_code" > "$TASK_RESULT_FILE"
return $exit_code
}
# ============================================================
# レスポンス解析・ディスパッチ
# ============================================================
dispatch_response() {
local response="$1"
local task_type task_content
task_type=$(echo "$response" | grep "^TYPE:" | head -1 | sed 's/^TYPE: *//' | tr -d '\r')
task_content=$(echo "$response" | grep -v "^TYPE:" | sed '/^[[:space:]]*$/d' | head -1)
echo "📌 [DEBUG] task_type=[$task_type]"
case "$task_type" in
COMMAND)
save_history "AI" "COMMAND実行: $task_content"
execute_command "$task_content"
save_history "SYSTEM" "実行結果: $(cat "$TASK_RESULT_FILE" 2>/dev/null | head -c 300)"
return 3 ;;
SEARCH)
echo "🔍 検索中: $task_content"
local results; results=$(search_web "$task_content")
echo "$results"; printf "%s" "$results" > "$TASK_RESULT_FILE"
save_history "SYSTEM" "検索結果: ${results:0:300}"
return 3 ;;
IMAGE)
local img_result; img_result=$(handle_image_request "$task_content")
echo "$img_result"; printf "%s" "$img_result" > "$TASK_RESULT_FILE"
return 3 ;;
NEED_INPUT)
echo "❓ $task_content"; echo -n "回答: "; read -r user_answer
save_history "USER" "$user_answer"
local new_response; new_response=$(call_gemini "$user_answer")
dispatch_response "$new_response"; return $? ;;
DONE)
echo "✅ $task_content"; save_history "AI" "完了: $task_content"; return 0 ;;
NEXT)
echo "⏭️ 次: $task_content"
printf "%s" "$task_content" > "$TASK_RESULT_FILE"; return 3 ;;
*)
echo "💬 $response"; save_history "AI" "${response:0:200}"; return 0 ;;
esac
}
# ============================================================
# メインタスクループ
# ============================================================
execute_task() {
local user_input="$1" max_iterations=10 iteration=0
local original_task="$user_input" current_input="$user_input"
save_history "USER" "$user_input"
detect_project_switch "$user_input" && echo "🔄 プロジェクト切り替え検知..." && load_config
while [[ $iteration -lt $max_iterations ]]; do
((iteration++))
echo ""; echo "═══ ステップ $iteration ═══"
echo "🌐 ①Gemini分析中..."
local response; response=$(call_gemini "$current_input")
[[ -z "$response" ]] && echo "❌ ①Geminiエラー" && return 1
dispatch_response "$response"
local action_status=$?
case $action_status in
0) echo "✅ タスク完了"; return 0 ;;
1) echo "❌ エラー"; return 1 ;;
3)
echo ""; echo "🌐 ②Gemini継続判断中..."
local last_result; last_result=$(cat "$TASK_RESULT_FILE" 2>/dev/null || echo "結果なし")
local cont_response; cont_response=$(call_gemini_continuation "$original_task" "$last_result")
[[ -z "$cont_response" ]] && echo "❌ ②Geminiエラー" && return 1
local cont_type cont_content
cont_type=$(echo "$cont_response" | grep "^TYPE:" | head -1 | sed 's/^TYPE: *//' | tr -d '\r')
cont_content=$(echo "$cont_response" | grep -v "^TYPE:" | sed '/^[[:space:]]*$/d' | head -1)
echo ""; echo "── ②Gemini判断: [$cont_type] ──"; echo "$cont_content"; echo ""
echo -n "続行しますか? [y=続行 / n=終了 / メッセージ=指示追加]: "; read -r user_choice
case "$user_choice" in
n|N) echo "中断しました"; return 0 ;;
""|y|Y)
case "$cont_type" in
DONE) echo "✅ 完了: $cont_content"; save_history "AI" "完了: $cont_content"; return 0 ;;
NEXT|COMMAND|SEARCH|IMAGE) current_input="$cont_content" ;;
NEED_INPUT)
echo "❓ $cont_content"; echo -n "回答: "; read -r user_answer
save_history "USER" "$user_answer"; current_input="$user_answer" ;;
*) echo "💬 $cont_response"; return 0 ;;
esac ;;
*) save_history "USER" "$user_choice"; current_input="$user_choice" ;;
esac ;;
esac
done
echo "⚠️ 最大ステップ数($max_iterations)に達しました"; return 0
}
# ============================================================
# チャットモード
# ============================================================
chat_mode() {
echo "🤖 AI Assistant v1.08 | プロジェクト: ${PROJECT_NAME_CURRENT:-未選択}"
echo "exit=終了 / clear=履歴クリア / history=履歴表示 / project <dir>=プロジェクト切替"
echo "════════════════════════════════════════"
while true; do
echo ""; echo -n "あなた: "; read -r user_input
case "$user_input" in
exit|quit) echo "終了します"; break ;;
clear) > "$HISTORY_FILE"; echo "✅ 履歴クリア (${HISTORY_FILE})" ;;
history) cat "$HISTORY_FILE" 2>/dev/null || echo "履歴なし" ;;
project\ *)
local np="${user_input#project }"
if [[ -d "$np" ]]; then echo "$np" > "$CURRENT_PROJECT_FILE"; load_config
echo "✅ プロジェクト切替: $PROJECT_NAME_CURRENT | 履歴: $HISTORY_FILE"
else echo "❌ ディレクトリなし: $np"; fi ;;
"") continue ;;
*) execute_task "$user_input" ;;
esac
done
}
# ============================================================
# 画像スクリプト生成
# ============================================================
generate_image_script() {
cat > "$IMAGE_SCRIPT" << 'IMGSCRIPT'
#!/usr/bin/env python3
import sys, base64, json, urllib.request, os
def analyze_image(path, inst="この画像を詳しく説明してください"):
key = os.environ.get("GOOGLE_API_KEY","")
if not key: print("GOOGLE_API_KEY not set"); return
with open(path,"rb") as f: img=base64.b64encode(f.read()).decode()
ext=path.rsplit(".",1)[-1].lower()
mime={"jpg":"image/jpeg","jpeg":"image/jpeg","png":"image/png","gif":"image/gif","webp":"image/webp"}.get(ext,"image/jpeg")
payload={"contents":[{"parts":[{"inline_data":{"mime_type":mime,"data":img}},{"text":inst}]}],"generationConfig":{"temperature":0.7,"maxOutputTokens":1024}}
url=f"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key={key}"
req=urllib.request.Request(url,data=json.dumps(payload).encode(),headers={"Content-Type":"application/json"})
try:
with urllib.request.urlopen(req) as r: print(json.loads(r.read())["candidates"][0]["content"]["parts"][0]["text"])
except Exception as e: print(f"エラー: {e}")
if __name__=="__main__":
if len(sys.argv)<2: print("使用方法: ai_image.py <path> [instruction]"); sys.exit(1)
analyze_image(sys.argv[1], sys.argv[2] if len(sys.argv)>2 else "この画像を詳しく説明してください")
IMGSCRIPT
chmod +x "$IMAGE_SCRIPT"
}
# ============================================================
# メイン
# ============================================================
main() {
[[ -z "$GOOGLE_API_KEY" ]] && echo "❌ GOOGLE_API_KEY未設定" && exit 1
mkdir -p "$TMP_DIR"
[[ ! -f "$IMAGE_SCRIPT" ]] && generate_image_script
restore_last_project
[[ $# -gt 0 ]] && execute_task "$*" || chat_mode
}
main "$@"
9. まとめ
スマホ完結の開発環境を求めて辿り着いたTermux × Gemini APIという組み合わせ。苦労した割に用途は限られますが、「スマホのターミナルからGitHubを自然言語で操作できる」という体験は、一度やると手放せなくなります。
- bash + python3 + curl だけで動く最小構成のAIエージェント
- Geminiを2回呼ぶ設計(①分析・②結果判断)で自律性を確保
- プロジェクトごとに設定・履歴を分離(.ai_config / .ai_history)
- Wranglerが使えなくてもGitHub Actionsで自動デプロイを実現
このスクリプトはTermux専用ではなく、SCREENSHOT_DIR のパスを変えるだけで通常のLinux/Macでも動きます。フレームワーク不要でGemini APIを手軽に試したい方にも参考になれば幸いです。
📚 関連記事
📅 2026/02/20 追記:ついに公式(Gemini CLI)が動いた!
自作スクリプトが完成し、喜び勇んでこの記事をデータベースに登録しようとしたのですが、ここで大きな壁にぶつかりました。HTMLファイルのサイズが大きすぎたのか、自作スクリプトでは処理しきれず、記事の登録ができませんでした……。やはり、急場しのぎの自作ツールには限界があったようです。
「やはりスマホでこれ以上は無理か」と悔しい思いをしましたが、執念でもう一度ジェンスパークに「TermuxでGemini CLIを動かす方法」を尋ねてみたところ、なんと驚きの解決策が提示されました!
npm install -g @google/gemini-cli --ignore-scripts
この魔法のオプションで見事にインストール成功! 立ち上げて話してみると、最初は画面右下のモデルが「Gemini 2.5」になっており、少し期待外れな回答もありました。そこで「モデルを3に変えられるか」と聞いたところ、直接の指定コマンドはないものの、設定で general.previewFeatures を true にすればいけるかもしれないとのこと。
指示通り設定を変えて再起動すると、無事に **Gemini 3 (Flash Preview)** が立ち上がりました! 自作スクリプトよりも遥かに賢く、大きなファイルも難なく扱えます。
実は、今読んでいただいているこの記事のD1データベースへの登録作業は、この **Gemini CLI** を使ってスマホから行っています。無事にデータベース登録も完了し、ついに理想のスマホ開発環境が手に入りました!