claude-factory
by yuritada
README.md
# claude-factory
音声対話で駆動する、個人用「ループエンジニアリング」システム。
Claude アプリのボイスモードで話しかけると、裏の **Claude Code** がリポジトリで作業し、
判断が必要になると質問を返してくる。それに音声か画面で答えると作業が続く。
企画は `docs/01_企画書.md`、実装方針は `docs/02_制作指示書.md`、
セッション管理は `docs/03_セッション管理.md`。
```
あなた(音声)
└ Claude アプリ/ボイスモード(秘書)
└ カスタムコネクタ = MCP Bridge Server(Bearer 認証)
├ Orchestrator ── Claude Code(claude-agent-sdk)── 各リポジトリ
└ SQLite ── Dashboard(FastAPI + React)
```
中核は **計画 → 承認 → 実行** のゲート。書き込みが起きる作業は必ず一度計画として返り、
あなたが承認するまで実行されない。
---
## 1. セットアップ
Python 3.12 以上、Node.js 18 以上、Claude Code CLI(Max アカウントでログイン済み)。
```bash
# Mac / Linux
uv sync --extra dev # または: pip install -r requirements.txt
cp .env.example .env
python -c "import secrets; print(secrets.token_urlsafe(32))" # → .env の CF_MCP_TOKEN
python -c "import secrets; print(secrets.token_urlsafe(16))" # → .env の CF_DASHBOARD_PASSWORD
```
```powershell
# Windows
python -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
copy .env.example .env # 中身のトークンを実値に置き換える
```
Claude Code の認証は SDK が引き継ぐので、実行ホストで一度 `claude` を起動して
Max アカウントでログインしておく。
### config.yaml を自分の環境に合わせる
最低限、**触ってよいディレクトリ**を書き換える。ここに無いパスは全て拒否される。
```yaml
security:
repo_allowlist:
- ~/Private_Project # Mac
# - C:\Users\<you>\repos # Windows
```
---
## 2. 起動
```bash
./scripts/run_mcp.sh # MCP サーバー(秘書の窓口 + ジョブのワーカー)
./scripts/run_dashboard.sh # ダッシュボード(初回はフロントも自動ビルド)
```
```powershell
.\scripts\run_mcp.ps1
.\scripts\run_dashboard.ps1
```
- MCP: `http://127.0.0.1:8010/mcp`
- ダッシュボード: `http://127.0.0.1:8787`
> **ジョブを実際に走らせるのは MCP サーバープロセス**。ダッシュボードだけ起動していても
> キューは進まない。常時動かすのは `run_mcp` のほう。
疎通確認:
```bash
curl -i http://127.0.0.1:8010/mcp # 401 = 認証が効いている
curl -s -X POST http://127.0.0.1:8010/mcp \
-H "Authorization: Bearer $CF_MCP_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
---
## 3. コネクタとして登録
```bash
./scripts/tunnel.sh quick # 使い捨て(URL は起動ごとに変わる)
```
表示された `https://<ランダム>.trycloudflare.com` の末尾に `/mcp` を付けて、
Claude アプリの「+」→ コネクタ → カスタムコネクタを追加、に貼る。
**トークンの渡し方**が 2 通りある:
| 方式 | 登録する URL | 備考 |
|---|---|---|
| ヘッダ(推奨) | `https://.../mcp` | コネクタ設定で `Authorization: Bearer <CF_MCP_TOKEN>` を追加 |
| パス | `https://.../t/<CF_MCP_TOKEN>/mcp` | 登録画面がヘッダを設定できない場合の逃げ道 |
パス方式はトークンが URL に載るぶん漏れやすい(ログに残る)。ヘッダが使えるなら
`config.yaml` の `mcp.allow_path_token` を `false` にして塞ぐこと。
---
## 4. 使い方
秘書(音声)に対して、たとえばこう話しかける:
**状況を知る**
- 「今どうなってる?」 → `get_org_status`(組織全体を 1 回で。まずこれ)
- 「あの仕事は?」 → `get_job`(`detail` で summary / report / log を切り替え)
- 「調査の結果を読んで」 → `read_board`(部署が出した成果は全部ここに載る)
**組織を動かす**(重い順)
- 「新しいタスクを『請求書パーサ』で始めて」 → `create_task`(ディレクトリ作成 + git init + 登録を一手で。`config.yaml` の編集も再起動も不要)
- 「保存方式をどうするか、設計班にかけて」 → `start_council`(**ファイルは変更されない**ので気軽に使える)
- 「会議の結論は?」 → `get_council`(結論・争点ごとの議論・**残った反対意見**)
- 「demo に、テストの失敗を直すよう依頼して」 → `dispatch_to_code`(**計画だけ**作られる)
- 「承認して」 → `answer_question`(ここで初めて**実行される**)
- 「この目標で任せる」 → `grant_mandate`(**自走が始まる**。`revoke_mandate` で止まる)
**セッション**
- 「今のセッションを分岐して、別のアプローチを試して」 → `fork_session`(git worktree で隔離)
### 組織のかたち(設計書 `docs/04_組織化設計書.md`)
```
子会社 = プロジェクト(互いに不干渉)
部署 = 役割 調査 / 設計班 / 実装 / デザイン / 統合管理
成果ボード 部署はここだけを介して成果を見せ合う
```
**設計班の会議**は司会主導の 4 段階で進む。
1. **下読み** — 司会が自明な点を自分で決着させ(`resolved_by_chair` に記録)、争点だけを抽出
2. **付議** — 争点ごとに社員名簿から人を指名(理由つき)
3. **検討** — 指名された人が意見と、先行意見への批評を述べる
4. **結論** — 司会が争点ごとの結論・**残った反対意見**・人間への論点を出す
名簿は `config/personas.yaml`(司会 1 名 + メンバー 10 名)。自由に編集でき、起動時に反映される。
### 部署ごとの権限(最小権限)
| 役割 | Web | ファイル書込 | 承認 |
|---|---|---|---|
| 調査 | 可 | 不可 | 不要 |
| 設計班 | 可 | 不可 | 不要 |
| 実装 | 不可 | リポジトリ内のみ | 委任状 |
| デザイン | 可 | 成果物ディレクトリのみ | 委任状 |
| 統合管理 | 不可 | 不可 | — |
「調査は Web を見られるがファイルは書けない」「実装はファイルを書けるが Web は見られない」と
分離してある。変更は `config.yaml` の `roles:` で行い、**人だけが変えられる**
(統合管理エージェントが自分の権限を広げる経路を作らない)。
### 自走(委任状)
`grant_mandate` で目標ごと承認すると、統合管理が各部署に仕事を振り、個別の承認なしに進む。
承認を減らすかわりに、**いつでも捨てられる形**で走らせている。
- 専用の作業ブランチを切る(`main` は触らせない)
- 予算(ジョブ数・コスト)と期限を付け、使い切ると自動で止まる
- **削除・`git push`・履歴の改変・依存の追加は委任の外**。必ず止まって確認する
- ダッシュボードの「停止」ボタン(`revoke_mandate`)で実行中ごと取り消せる
ダッシュボードでは、保留質問キュー・進捗タイムライン・ライブログ・レポート・
セッションのフォークツリー・監査ログが見える。回答は音声でも画面でも同じ経路を通る。
### 秘書のスキル(`skills/`)
毎朝あたらしいチャットを立ち上げる運用のため、秘書には前日の記憶がない。
**立ち上げ手順**はスキルとして `skills/factory-startup/` に置いてある
(状況収集 → 読み上げ順序 → 今日の提案、読み上げ台本つき)。
Claude アプリの設定からアップロードし、チャットの冒頭に **`/factory-startup`**(短縮形 `/cf`)
と打って呼び出す。自然文のトリガー語は置いていない(誤爆と起動漏れを避けるため)。
アプリの `/` サジェストはスキルの `name` から出るので、**名前がそのまま合図**になる。
詳細は `skills/README.md`。
コネクタ側の `SECRETARY_GUIDE`(毎回のリクエストに載る=短く保つ)と、
スキル(必要なときだけ読まれる=手順や台本を置く)で役割を分けている。
### 秘書に効く指示(企画 §検証4)
会話の途中で勝手に依頼を飛ばさないよう、秘書側にこう言っておくとよい:
> 私が「これで依頼して」と言うまで dispatch_to_code は呼ばないで。
> それまでは相談に付き合って、指示文を一緒に整えて。
---
## 5. セキュリティ(制作指示書 §8)
実装済みの防御:
| # | 要件 | 実装 |
|---|---|---|
| 1 | MCP は Bearer トークン必須 | `BearerAuthMiddleware`。未設定なら起動を拒否 |
| 2 | repo_path は allowlist 内の絶対パスのみ | `resolve_repo_path`。`..`・シンボリックリンク脱出も拒否 |
| 3 | 秘書に生シェルを晒さない | MCP のツールは限定インターフェースのみ |
| 4 | 書換/削除/シェル実行は承認ゲート | 計画→承認→実行 + `can_use_tool` + OS サンドボックス |
| 5 | シークレットをコミットしない | `.env` は `.gitignore`、`.env.example` のみ配布 |
| 6 | 全 dispatch・answer を監査ログに記録 | `audit_log` テーブル、ダッシュボードの「履歴」 |
| 7 | レート制限 | MCP・ダッシュボードともトークンバケット |
| 8 | ダッシュボードは認証の背後 | Cookie セッション、または Cloudflare Access |
実 Claude Code で試して分かった要注意点が 2 つあり、対策済み:
- **`can_use_tool` は CLI が自動承認するツールには呼ばれない。** 認可コールバックだけを
頼りにすると、計画モードでもリポジトリ外に書き込みが起きる。`disallowed_tools` による
CLI レベルの禁止と、OS サンドボックス(`orchestrator.sandbox`)を重ねてある。
- **`cd` によるディレクトリ脱出はパス検査では止まらない。** Bash コマンド中の絶対パスと
`..` を `_bash_escapes_workspace` で見ている。
- 対象リポジトリの `.claude/settings.json` は**読み込まない**(`setting_sources=[]`)。
読み込むとリポジトリが自分の権限を自己承認できてしまう。
---
## 6. 固定公開(M5)
使い捨てトンネルは起動ごとに URL が変わるので、常用するなら名前付きに移す。
```bash
cloudflared tunnel login
cloudflared tunnel create claude-factory
cloudflared tunnel route dns claude-factory mcp.<domain>
cloudflared tunnel route dns claude-factory dash.<domain>
```
`~/.cloudflared/config.yml`:
```yaml
tunnel: claude-factory
credentials-file: /path/to/<tunnel-id>.json
ingress:
- hostname: mcp.<domain>
service: http://localhost:8010
- hostname: dash.<domain>
service: http://localhost:8787
- service: http_status:404
```
常時公開は systemd の `cloudflared.service` が担当する(`/etc/cloudflared/config.yml` を読む)。
```bash
systemctl status cloudflared # 状態確認
sudo systemctl restart cloudflared # 設定変更の反映
journalctl -u cloudflared -f # ログ
```
`./scripts/tunnel.sh named claude-factory` でも起動できるが、常駐サービスと
同じトンネルにコネクタが二重に張られるため通常は使わない。手動運転に切り替える
場合は先に `sudo systemctl stop cloudflared` すること。スクリプト側でも常駐を
検知したら警告し、確認を求める。
Route 53 側は `cloudflared tunnel route dns` が CNAME(`<tunnel-id>.cfargotunnel.com`)を
作る。コネクタ登録 URL は `https://mcp.<domain>/mcp`。
ダッシュボードは Cloudflare Access を前段に置き、その場合のみ `dashboard.auth: none` にする。
---
## 7. 開発
```bash
.venv/bin/python -m pytest -q # テスト
cd src/claude_factory/dashboard/web && npm run dev # フロントの開発サーバー
```
構成は制作指示書 §2 に対応(`src/` 直下ではなく `src/claude_factory/` パッケージにしてある):
```
src/claude_factory/
├─ config.py 設定(config.yaml + .env)
├─ models.py 型・出力規約・その解析
├─ store.py SQLite DAO
├─ security.py トークン・allowlist・レート制限
├─ runner.py claude-agent-sdk ラッパと承認ゲート(役割別の権限)
├─ orchestrator.py ジョブキュー、計画→承認→実行、自走ループ
├─ sessions.py セッション一覧/閲覧/分岐(git worktree 隔離)
├─ personas.py 社員名簿と組閣
├─ council.py 設計班の合議エンジン
├─ integrate.py 統合管理(作業計画を出すだけ。実行はしない)
├─ org.py 組織全体の状況
├─ mcp_server.py 秘書向け MCP
└─ dashboard/ FastAPI + React(Vite)
```
ドキュメント: `docs/01_企画書.md`(構想)→ `02_制作指示書.md`(基盤)→
`03_セッション管理.md`(追補)→ `04_組織化設計書.md`(組織化)。
---
## 8. 残っていること
- **M6 音声 E2E**: 実プロジェクト 1 本を音声だけで回す(コネクタ登録後に手動で確認)。
- 判断待ちのプッシュ通知(v2)。
- 分岐 worktree の後片付けルール(マージ後に消すか残すか)。
- 統合管理が同じ仕事を振り続けた場合の検知(いまは予算と期限が唯一の歯止め)。
- 部署間で意見が食い違ったときの調停者を統合管理にするか、人に上げるか。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues