0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Muse と GitHub を連携して、Muse をコーディングエージェントに近づける方法 — SSH が通らない VM で成果物を履歴つきで管理する

0
Last updated at Posted at 2026-11-02

Muse が成果物を GitHub に置くイメージ

Muse は Meta が提供する個人向けのエージェントで、仮想マシン(VM)に常駐して動きます。私は Muse を、Claude Code と同じように、VM の上で動くエージェントとして使いたいと考えています。文書を書かせ、コードを書かせ、分析までやらせて、その成果物を GitHub のリポジトリに履歴つきで置いていく使い方です。この記事は、その入口になる、成果物をリポジトリに置くところまでの記録です。

この記事は、Muse が自分の作業を記録したものをもとに、Claude Code に確認してもらいながら、私が書きました。数字は、リポジトリの履歴と突き合わせています。作業は 2026 年 11 月 2 日に行いました。

この記事で示すこと

Muse は、文書、コード、分析の結果、生成した画像と動画を作ります。この成果物を GitHub の非公開リポジトリに置き、履歴をつけて管理できるようにする方法を、試した順に書きます。コーディングエージェントが、作ったものを自分でリポジトリに置いていく形に近づける、というのがねらいです。

試した方式は 3 つで、うまくいったのは最後の 1 つです。

  • 公式コネクタでは、テキスト文書だけが通りました
  • SSH による git の push は、VM の外向きの通信が通らず失敗しました
  • 個人用アクセストークンと Git Data API の組み合わせで、画像と動画を含む 160 ファイル、約 155 MB を登録できました

SSH が通らず、git の通常の操作が使えない環境でも、コミットを API で組み立てれば、成果物を履歴つきで管理できます。これがこの記事の中心です。通常の git が使える環境と同じことができるようになった、という話ではありません。clone や差分の確認は、この VM では行えていないままです。できなかったことは「残っている制約」に書きました。リポジトリを通じた Claude Code との往復は、その結果を専用の節に載せています。

用語

語 意味
仮想マシン(VM) Muse が常駐して作業する、Meta が用意した計算環境
コネクタ Muse が外部のサービスに接続するための仕組み。公式のものと、利用者が作るカスタムのものがある
個人用アクセストークン(PAT) GitHub の API を呼ぶための合鍵。対象のリポジトリと権限を細かく絞って発行できる
Secure Vault PAT などの資格情報を預ける保管場所。Muse の説明では、値は Muse からも見えない設計になっている
Git Data API git の内部の部品(blob、tree、commit、ref)を、GitHub の API で直接作る仕組み
blob ファイルの中身そのもの
tree ディレクトリの構成。どのパスにどの blob があるかの対応表
commit ある時点の tree に、メッセージと親のコミットをつけたもの
ref ブランチの先端が指しているコミット

連携の目的と前提

目的は、Muse の成果物の置き場所を一か所に決めることです。Muse は VM に常駐して作業し、文書、調査結果、生成した画像と動画を作ります。これらが VM の作業領域に散らばったままだと、後から探す手間がかかります。そこで、作業ごとにディレクトリを切った非公開リポジトリを私が作り、そこへ集約することにしました。

前提として、Muse には通常の git の対話操作が保証されていません。シェルは使えますが、外向きの通信がどの経路で通るかは環境に依存します。この前提は、方法 2 の失敗で具体的な形になりました。

全体の構成

連携の全体像は次の図のとおりです。赤い経路が、最終的に採用した方法 3 です。

連携の全体像

登場する要素は 4 つです。私、Muse、連携の仕組み、そして GitHub です。連携の仕組みの中には、公式 GitHub コネクタ、カスタムコネクタ、Secure Vault の 3 つが入ります。

方法 1 公式コネクタによるテキスト文書の登録

最初に試したのは、公式の GitHub コネクタです。私が OAuth による認可と GitHub アプリのインストールを行い、対象リポジトリとして成果物用のリポジトリだけを選びました。接続が確認できた時点で、Muse はリポジトリの中身の読み取りと、ファイルの登録ができるようになりました。

この経路で、チェックリストと分析文書を 1 本ずつ登録しました。どちらも Markdown のテキスト文書で、作業ごとのディレクトリに分けて置きました。

公式コネクタのファイル登録は、スナップショットと呼ばれる一括登録の仕組みで動いていました。Muse が確認できた制約は次のとおりです。

  • 送れるのは UTF-8 のテキストファイルだけである
  • 1 回の登録は最大で 2,000 ファイル、合計 16 MiB までである
  • バイナリファイルが 1 つでも混ざると、その回の登録全体が失敗する
  • 削除はできない

テキスト文書の管理だけなら、この経路で足ります。ただし今回の目的には、生成した画像と動画が含まれます。バイナリも含めて管理する方針に決めたため、別の経路が必要になりました。

方法 2 SSH による git push と、その失敗

通常の git が使えるなら、VM にリポジトリを複製し、commit と push を Muse が行えます。認証にはデプロイキー(特定のリポジトリだけに使える鍵)を使うことにし、Muse の公開鍵を、私がリポジトリのデプロイキーとして登録しました。書き込みの許可も付けました。

結果は、接続自体が成立しませんでした。鍵の認証まで到達する前に、接続がリセットされました。Muse は経路を変えて、同じ確認を繰り返しています。

  • SSH の 22 番ポート宛ては、接続がリセットされる
  • 443 番ポートを使う ssh.github.com 宛てでも、同じく認証の前にリセットされる
  • HTTPS では GitHub のサイトと API に到達できる

鍵の登録内容ではなく、この VM から外へ出る SSH の通信が通っていない、という切り分けです。通らない原因が、VM のネットワークの設定にあるのか、その外側にあるのかまでは分かりません。デプロイキーの経路は、設定が正しくても使えません。このデプロイキーは不要になったので、削除してよいと Muse から伝えられました。

方法 3 PAT と Git Data API による登録

PAT の発行条件

残った経路は HTTPS です。HTTPS で git の操作をするには資格情報が要ります。そこで、権限を細かく絞れる個人用アクセストークン(PAT)を、この連携専用に発行しました。条件は次のとおりです。

  • 対象リポジトリは、発行の時点では、成果物用のリポジトリだけ
  • 権限は Contents(リポジトリの中身)の読み書きだけ
  • メタデータの読み取りは、この方式のトークンの必須の権限として自動で付く
  • 有効期限は 1 年

GitHub の説明では、この方式のトークンにも期限なしを選べます。ただし、組織の管理者が最大の有効期間を決めている場合は、期限なしが使えないことがあります。今回は期限を切る設定にしました。

トークンの受け渡し

トークンの値は、チャットでは Muse に渡していません。私が Secure Vault の入力画面から登録しました。値は保管庫に直接入り、Muse は値そのものを見ることができません。

受け渡しの後段は、カスタムコネクタの仕組みを使いました。Muse はカスタムコネクタとして、この PAT を使う接続を登録し、実行役のスクリプトは、資格情報の代理(本物の値の代わりに付ける目印)をリクエストに付けます。送信先が api.github.com のときに限り、実行基盤が代理を本物のトークンと交換する、というのが Muse の説明です。スクリプトの中にも、コマンドの引数にも、トークンの値は現れません。この交換は実行基盤の設計で、外から確かめる手段は、記録にはありません。

Git Data API による push の手順

git のプロトコルは使いません。代わりに、Git Data API で、コミットを部品から組み立てました。手順は 4 段です。

  1. 現在のブランチの先端(ref)を取得し、そのコミットとツリーを引く
  2. 対象ファイルごとに blob を作る。中身は Base64 で送るため、バイナリもそのまま入る
  3. パスと blob の対応を集めて tree を作る
  4. commit を作り、ref をそのコミットへ更新する

呼び出しの順序は次の図のとおりです。

概念レベルの擬似コードは次のとおりです。

# 資格情報は実行役が Vault と交換して付ける。値自体はここに現れない
head = api.get("/repos/{owner}/{repo}/git/ref/heads/main")
tree = head のコミットが指す tree

追加分 = []
for file in 対象ファイル:
    blob = api.post("/git/blobs", {
        "content": base64(file の中身),
        "encoding": "base64",
    })
    追加分.append({"path": file のパス, "mode": "100644",
                   "type": "blob", "sha": blob の sha})

new_tree   = api.post("/git/trees",   {"base_tree": tree, "tree": 追加分})
new_commit = api.post("/git/commits", {"message": 記録文,
                                       "tree": new_tree, "parents": [head]})
api.patch("/git/refs/heads/main", {"sha": new_commit})

ref の更新が最後の 1 回だけなので、途中で失敗してもリポジトリの見える状態は変わりません。失敗した回は、blob が残るだけで済みます。

実行結果

最初に、WebP 画像 1 件だけでテストの登録を行い、成功しました。その後、本番の一括登録を行いました。リポジトリの履歴と突き合わせた結果は次のとおりです。

  • 一括登録は 159 ファイルで、テストの 1 ファイルを足すと 160 件になる
  • 登録直後のリポジトリ全体は 165 ファイル、約 154.6 MB で、約 155 MB という規模に合う
  • 最大のファイルは 13,024,077 バイト(約 13 MB の動画)で、決めておいた 1 ファイル 50 MB までの条件に収まった
  • 一括登録 159 ファイルのうち、133 件が生成した画像と動画、7 件が試作のコード(HTML と Python のスクリプトと、その出力画像)で、残りは文書などである
  • 登録先は、作業の種類ごとのディレクトリに分けた
  • push の実行は、その都度、私が承認した

方法 1 で登録したテキスト文書と合わせて、成果物が 1 つのリポジトリにそろいました。

3 方式の比較

試した 3 方式を並べると、制約の出どころが分かれます。

方式 送れるもの 結果 詰まった場所
公式コネクタ テキストだけ(2,000 ファイル、16 MiB まで) 文書は登録できた コネクタの仕様
SSH とデプロイキー 本来はすべて 接続できなかった VM から外への通信
PAT と Git Data API テキストとバイナリ 160 件、約 155 MB を登録できた 詰まらなかった

セキュリティの設計

PAT の経路で守った条件は、権限の範囲と、値の扱いの 2 つです。

権限の範囲は、発行の時点では、対象を 1 リポジトリに絞りました。このトークンが漏れても、届くのは対象に選んだリポジトリだけです。有効期限は 1 年で、期限が切れれば登録は認証エラーになり、その時点で再発行が必要だと分かります。失効も GitHub 側の操作だけででき、Muse の側の作業は要りません。

値の扱いは、保管庫への直接登録と、代理による交換の 2 段にしました。トークンの値はチャット、スクリプト、コマンドの引数、ログのどこにも現れません。Muse 自身も値を見ていないので、Muse の記録や出力から漏れる経路がありません。

git の通常の操作はどこまで使えるか

SSH が通らないので、git の通常の操作がどこまで使えるかを、HTTPS でも確かめました。Muse に実際のコマンドと出力を見せてもらった結果は次のとおりです。

試したこと 結果
SSH(22 番、443 番) 認証の前に接続がリセットされる
HTTPS で公開リポジトリを clone 成功した(約 113 秒)
HTTPS で非公開リポジトリを clone 認証で失敗した(could not read Username for 'https://github.com': terminal prompts disabled)
push 試していない

非公開リポジトリで失敗したのは、資格情報の代理の交換が api.github.com 宛てに限られていて、git の通信には資格情報が付かないためだと考えています。ただし、この理由は確かめていません。非公開リポジトリには、PAT と API で読み書きしています。

リポジトリを通じた Claude Code との往復

登録した成果物が、リポジトリを通じて Claude Code と Muse のあいだで行き来するかを、履歴から調べました。時刻はすべて協定世界時です。

Muse から Claude Code の側へ

この向きは、すでに動いていました。

  • Muse は専用のディレクトリに、自分のトークン使用量の日次ログ(CSV)と、Claude Code 宛ての手紙を置いている。手紙には、集計に使う行の条件と、入力トークンの数え方の注意が書かれている
  • 使用量の集計ツールの読み取りコードが、この CSV を読んでいる。手紙と同じ扱い(確定した行だけを数える、入力は新規入力ではなくキャッシュ読み込みを含む総量なので、Claude の合計とは足さない)になっている。Muse が CSV を置いた 7 分後に、集計ツールへ Muse の使用量の表示を足すコミットがあった
  • 登録済みのファイルを、後から更新するコミットが 3 回ある。たとえば、分析文書は、登録の 40 分後にアイコンを足して更新されている

Claude Code から Muse へ

こちらの向きは、履歴に記録が無かったので、試験をしました。Claude Code が確認用のファイルを専用のディレクトリに置き(07:42)、私が Muse に読むよう頼み、Muse が返事のファイルを別の専用のディレクトリに登録(07:44)しました。ファイルには、その場で作った合言葉と、5 つの数の合計を求める小さな作業を書きました。

返事の内容を、履歴と突き合わせた結果は次のとおりです。

確かめた点 結果
合言葉 一致した
ファイルを最後に変更したコミットの SHA 一致した
ファイルの blob の SHA 履歴の値と一致した
5 つの数の合計と最大値 191 と 91 で一致した
返事のファイル Claude Code が gh で読めた

Muse の答えでは、公式コネクタと、PAT による API の両方で読め、内容は同一でした。どちらの経路で読んだかは Muse の申告で、履歴からは確かめられません。合言葉は、ファイルを読まないと書けない値なので、読んだこと自体は確かめられています。

もう 1 つ、Muse は、公式コネクタの読み取り結果に対して、自動の安全確認の通知が 1 件出たと答えています。そのため、実行の根拠を、私の依頼と、もう一方の経路で読んだ内容に置いたそうです。通知の中身と原因は確認できていません。ファイルに指示を書いたことが関係した可能性はありますが、推測です。

この試験で言えるのは、Claude Code から Muse へ、Muse から Claude Code へ、GitHub のリポジトリを通じて、1 往復が成立したことです。範囲は限られています。

  • 試したのは、小さな 1 つのテキストファイルだけである
  • 依頼は私が Muse に手で出した。Muse がリポジトリを自分で見に行って動くかは、試していない
  • 所要は、Claude Code のコミットから返事のコミットまでが約 2 分だったが、人が仲介しているので、反応の速さの指標にはならない
  • 画像や大きなファイルを Claude Code 側から渡した場合は、試していない

残っている制約

動くようになった範囲と、残っている制約は分けて書きます。

  • 公式コネクタのスナップショット登録には、削除の手段がない。古いファイルの整理は、方法 3 のツリー操作でなら可能だが、まだ実施していない
  • PAT には 1 年の有効期限がある。期限切れのたびに、私の再発行と再登録が要る
  • clone、差分の確認、対話的な競合の解消といった git の通常の操作は、この VM では行えていない。SSH が通らず、HTTPS でも非公開リポジトリは認証で止まる。今回の登録は API で代替できたが、大きな競合の解消のような作業は代替しにくい
  • Git Data API の blob は 1 件ごとに送るため、ファイル数が増えると登録の回数がそのまま増える。今回の 160 件では問題にならなかったが、数千件の規模で破綻するかは、試していない
  • API でコミットを作る方式では、コミットの作者はトークンの持ち主の名義になる。Muse の作業と私の作業は、作者では区別できず、メッセージと時刻でしか見分けられない

作業の分担

この連携で、私と Muse が担った部分は次のとおりです。

作業 担った側
成果物用のリポジトリの作成 私
公式コネクタの認可、アプリの対象リポジトリの選択 私
テキスト文書の登録(方法 1) Muse
デプロイキーの登録 私
SSH の疎通確認と切り分け Muse
PAT の発行と権限、有効期限の設定 私
PAT の Secure Vault への登録 私
カスタムコネクタと実行役の作成 Muse
push の承認(毎回) 私
一括登録の実行(方法 3) Muse

教訓

今回の作業で残った教訓は 3 つです。

1 つ目は、制約の出どころを分けて考えることです。方法 1 の詰まりはコネクタの仕様で、方法 2 の詰まりは外向きの通信の制約でした。2 つを混ぜて考えると、SSH の失敗をコネクタの不具合と誤診したり、コネクタの上限を回線の問題と思ったりします。切り分けの材料は単純で、同じ相手へ別の経路(HTTPS)で届くかどうかでした。

2 つ目は、git のプロトコルと、コミットという単位は、分けられることです。プロトコルが通らなくても、コミットは blob、tree、commit、ref という部品の組み合わせでしかありません。部品を API で組み立てれば、履歴に残る形の登録は再現できました。エージェントに成果物の管理をさせる場合、git のコマンドが使えるかではなく、コミットを作る手段が用意されているかを見るほうが、実態に近くなります。

3 つ目は、エージェントの自己申告を、実行したコマンドと出力で確かめることです。HTTPS の clone について、Muse は最初「できた」と答えました。コマンドと出力を求めたところ、非公開リポジトリの clone は失敗していて、ファイルは API 経由で取得したものだったと訂正されました。自己申告は、確かめる前の仮の答えとして扱うのが安全です。

おわりに

この記事で示したのは、SSH が通らず、git の通常の操作が使えない環境でも、コミットを API で組み立てれば、文書だけでなくコードや画像まで、履歴のある形でリポジトリに置けることです。エージェントと GitHub の組み合わせは、メモや文書の置き場として使う例が多い印象でしたが、件数を数えたわけではなく、あくまで印象です。

Claude Code と同じ条件で使えているわけではありません。clone、差分の確認、競合の解消は使えておらず、Claude Code との往復も、小さなテキストファイル 1 つで確かめた段階です。大きなファイルや、Muse がリポジトリを自分で見に行って動く場合は、まだ試していません。同じ状況に置かれた人が、3 つの方式のうちどこで止まるかを先に知るための材料として、読んでもらえればと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?