0. はじめに
このツールは、ローカル環境で動作する Copilot の操作を自動化するためのツール、auto_ai_cです。
Copilot の UI を自動操作し、プロンプト送信や応答取得を補助する仕組みを提供します。
API サーバー化やクラウド提供を目的としたものではありません。
すべての処理はユーザー自身の PC 内で完結します。
1. 注意事項
本ツールは、以下のCopilot利用規約に完全に従う形で設計されています。
ツールやコンピューター プログラム (ボットやスクレイパーなど) を使用して Copilot にアクセスしないでください。
Copilot は、個人的な使用のためにのみ使用できます。
本ツールは以下の点で規約に抵触しません:
- 個人利用の範囲でのみ使用されること
- クラウドに公開されず、外部サービスとして提供されないこと
- Copilot の出力を第三者に提供しないこと
- Copilot の制限解除(ジェイルブレイク)を目的としないこと
また、以下の規約にも準拠します:
お客様や他のお客様に Copilot を提供する Microsoft の能力を損なわないこと。
技術的な攻撃、過剰な使用、プロンプトベースの操作、「ジェイルブレイク」、その他の悪用に関与しないこと。
本ツールは Copilot の制限を解除したり、Microsoft の技術的制御を回避するものではありません。
あくまで ユーザー自身の PC 上での操作を自動化する補助ツールです。
2. 規約
利用規約(本ツール独自)
- 本ツールを使用して Copilot を API サーバー化しないこと
- 本ツールを使用して 他者の権利・プライバシーを侵害しないこと
- 本ツールを使用して 違法行為を行わないこと
- Copilot の複製・模倣 AI を作成・公開しないこと
- 本ツールの使用によって発生したトラブル・損害について、開発者は一切責任を負いません
3. 許可された使い方
必ず Copilot の利用規約に従って使用してください。
許可される利用例
-
ローカルで完結する自動化ツールとして使用すること
例)Copilot の応答をもとに、ファイル編集やターミナルコマンドを実行する -
Flask を使って、自分のスマホから “自分の PC 上の Copilot” を遠隔操作すること
※LAN 内または VPN 内のみ
※外部公開は禁止
禁止される利用例
絶対に 個人利用の範囲を超えて使用しないでください。
他者に公開・提供することは禁止します。
- Copilot の機能をクラウド経由で提供する
- Copilot の出力を第三者に配布する
- Copilot を API のように外部サービス化する
- Copilot の制限解除(ジェイルブレイク)を目的とする
4.使用方法
使い方
※Cドライブ直下にファイルを置いたとします。
argument.exe
プロンプトをcopilotに送信します。
cmdなら
cd C:\
argumen.exe "プロンプト"
ubuntuなら
cd mnt/c
argument.exe "プロンプト"
必ず、Egde内のcopilotタブをアクティブ(開いている状態)にしておいてください。
copilotからの返答は、.exeとおなじディレクトリに、myfile.txtとして配置されています。
clipboard.exe
クリップボードの中身をcopilotに送信します。
cmdなら
cd C:\
clipbpard.exe
ubuntuなら
cd mnt/c
clipboard.exe
必ず、Egde内のcopilotタブをアクティブ(開いている状態)にしておいてください。
copilotからの返答は、.exeとおなじディレクトリに、myfile.txtとして配置されています。
5.clipboard.exeの仕組み
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
namespace clipboard
{
class Program
{
// Copilot の入力欄にクリップボードの内容を送信
static void SendToCopilot(string text)
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return;
// Copilot の入力欄(AutomationId=userInput)
var input = edge.FindFirst(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, "userInput")
);
if (input == null) return;
// ValuePattern で直接入力
if (input.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern))
{
var vp = (ValuePattern)pattern;
vp.SetValue(text);
}
// Enter で送信
SendKeys.SendWait("{ENTER}");
}
// Copilot の最新メッセージを取得
static string GetLatestMessage()
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return "";
while (true)
{
try
{
// Copilot の返答ブロック(ai-message)だけを取得
var msgs = edge.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
);
if (msgs == null || msgs.Count == 0)
return "";
// 最新の返答ブロック
var latest = msgs[msgs.Count - 1];
// 返答ブロック内の Text を全部取得
var texts = latest.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
);
if (texts == null || texts.Count == 0)
return "";
var sb = new StringBuilder();
// texts[0] はラベルなのでスキップ
for (int i = 1; i < texts.Count; i++)
{
string line = texts[i].Current.Name;
if (string.IsNullOrWhiteSpace(line))
continue;
// 正規化
line = line
.Replace('\u2010', '-') // ハイフン
.Replace('\u2011', '-') // ノンブレークハイフン
.Replace('\u2012', '-') // フィギュアダッシュ
.Replace('\u2013', '-') // エンダッシュ
.Replace('\u2014', '-') // エムダッシュ
.Replace('\u2015', '-') // ホリゾンタルバー
.Replace('\u2212', '-') // マイナス記号
.Replace('/', '/') // 全角スラッシュ
.Replace('\', '\\') // 全角バックスラッシュ
.Replace('_', '_'); // 全角アンダーバー
sb.AppendLine(line);
}
return sb.ToString().TrimEnd('\r', '\n');
}
catch (ElementNotAvailableException)
{
Thread.Sleep(50);
continue;
}
}
}
// Copilot の行数
static int GetLatestMessageLineCount()
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return 0;
// 最新の ai-message を探す
var msgs = edge.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
);
if (msgs == null || msgs.Count == 0)
return 0;
var latest = msgs[msgs.Count - 1];
// その中の Text ノードを全部数える
var texts = latest.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
);
return texts?.Count ?? 0;
}
// Copilot の生成完了待ち
static string WaitUntilDone()
{
string last = "";
int stable = 0;
while (true)
{
string text = GetLatestMessage();
if (string.IsNullOrWhiteSpace(text))
{
Thread.Sleep(100);
continue;
}
// deepthink の2行は無視
if (text == "考え中…" ||
text == "自分の考えを整理し、計画を作成する")
{
Thread.Sleep(100);
continue;
}
// 3行以上になっていなければまだ生成途中
int lineCount = text.Split('\n').Length;
if (lineCount < 3)
{
Thread.Sleep(100);
continue;
}
// 安定判定
if (text != last)
{
last = text;
stable = 0;
}
else
{
stable++;
}
// 4回連続で同じ → 生成完了
if (stable >= 4)
{
// deepthink の2行を削除して返す
var cleaned = new StringBuilder();
foreach (var line in text.Split('\n'))
{
if (line.Contains("考え中…")) continue;
if (line.Contains("自分の考えを整理し、計画を作成する")) continue;
cleaned.AppendLine(line);
}
return cleaned.ToString().TrimEnd();
}
Console.WriteLine("待機中");
Thread.Sleep(100);
}
}
[STAThread]
static void Main()
{
// テスト用のプロンプト
// deepthinkを使って、世の中のめちゃくちゃ難しい問題に自分なりの答えを出して。
// myfile.txt を空にする
File.WriteAllText("myfile.txt", "", Encoding.UTF8);
// クリップボードの内容を取得
string prompt = Clipboard.GetText();
if (string.IsNullOrWhiteSpace(prompt))
{
Console.WriteLine("クリップボードが空です。");
return;
}
// Copilot に送信
SendToCopilot(prompt);
// 完了待ち
string result = WaitUntilDone();
// 実行ファイルと同じフォルダに myfile.txt を保存
string exeDir = AppDomain.CurrentDomain.BaseDirectory;
string outPath = Path.Combine(exeDir, "myfile.txt");
File.WriteAllText(outPath, result, Encoding.UTF8);
Console.WriteLine("=== Copilot 応答 ===");
Console.WriteLine(result);
Console.WriteLine("====================");
Environment.Exit(0);
}
}
}
主に、関数(送信、解答生成まで待機、解答取得)とmainでできています。
送信
static void SendToCopilot(string text)
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return;
// Copilot の入力欄(AutomationId=userInput)
var input = edge.FindFirst(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, "userInput")
);
if (input == null) return;
// ValuePattern で直接入力
if (input.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern))
{
var vp = (ValuePattern)pattern;
vp.SetValue(text);
}
// Enter で送信
SendKeys.SendWait("{ENTER}");
}
まず、edge内のcopilotの入力欄のUIデータ(inspect.exeを使用)を取得し、そのデータに当てはまるところに直接、文字列を入れます。(マウスを使用しない)
その後、SendKeysを使用し、ctrl+v→enterを入力し、送信します。
WaitUntilDoneとGetLatestMessageLineCount
// Copilot の行数
static int GetLatestMessageLineCount()
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return 0;
// 最新の ai-message を探す
var msgs = edge.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
);
if (msgs == null || msgs.Count == 0)
return 0;
var latest = msgs[msgs.Count - 1];
// その中の Text ノードを全部数える
var texts = latest.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
);
return texts?.Count ?? 0;
}
// Copilot の生成完了待ち
static string WaitUntilDone()
{
string last = "";
int stable = 0;
while (true)
{
string text = GetLatestMessage();
if (string.IsNullOrWhiteSpace(text))
{
Thread.Sleep(100);
continue;
}
// deepthink の2行は無視
if (text == "考え中…" ||
text == "自分の考えを整理し、計画を作成する")
{
Thread.Sleep(100);
continue;
}
// 3行以上になっていなければまだ生成途中
int lineCount = text.Split('\n').Length;
if (lineCount < 3)
{
Thread.Sleep(100);
continue;
}
// 安定判定
if (text != last)
{
last = text;
stable = 0;
}
else
{
stable++;
}
// 4回連続で同じ → 生成完了
if (stable >= 4)
{
// deepthink の2行を削除して返す
var cleaned = new StringBuilder();
foreach (var line in text.Split('\n'))
{
if (line.Contains("考え中…")) continue;
if (line.Contains("自分の考えを整理し、計画を作成する")) continue;
cleaned.AppendLine(line);
}
return cleaned.ToString().TrimEnd();
}
Console.WriteLine("待機中");
Thread.Sleep(100);
}
}
こちらも、edge内のcopilotのテキストのUIデータ(inspect.exeを使用)を取得し、そのデータに当てはまるところに直接、文字を取得します。(マウスを使用しない)
それを繰り返し、GetLatestMessageLineCount(copilotの行数)が三行以上(deepthinkでノイズが二行入ってしまう。)で、テキストの更新が4回行われていなかったら、テキストが全て生成されたと判断します。
GetLatestMessage
// Copilot の最新メッセージを取得
static string GetLatestMessage()
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return "";
while (true)
{
try
{
// Copilot の返答ブロック(ai-message)だけを取得
var msgs = edge.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
);
if (msgs == null || msgs.Count == 0)
return "";
// 最新の返答ブロック
var latest = msgs[msgs.Count - 1];
// 返答ブロック内の Text を全部取得
var texts = latest.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
);
if (texts == null || texts.Count == 0)
return "";
var sb = new StringBuilder();
// texts[0] はラベルなのでスキップ
for (int i = 1; i < texts.Count; i++)
{
string line = texts[i].Current.Name;
if (string.IsNullOrWhiteSpace(line))
continue;
// 正規化
line = line
.Replace('\u2010', '-') // ハイフン
.Replace('\u2011', '-') // ノンブレークハイフン
.Replace('\u2012', '-') // フィギュアダッシュ
.Replace('\u2013', '-') // エンダッシュ
.Replace('\u2014', '-') // エムダッシュ
.Replace('\u2015', '-') // ホリゾンタルバー
.Replace('\u2212', '-') // マイナス記号
.Replace('/', '/') // 全角スラッシュ
.Replace('\', '\\') // 全角バックスラッシュ
.Replace('_', '_'); // 全角アンダーバー
sb.AppendLine(line);
}
return sb.ToString().TrimEnd('\r', '\n');
}
catch (ElementNotAvailableException)
{
Thread.Sleep(50);
continue;
}
}
}
こちらは、copilotの応答を取得する関数です。
1番下にある最新メッセージだけを取得します。
改行の読み取りができないので、空白を改行とみなします。
なので、プロンプトに「半角のスペースを入れないでください」という旨の内容を入れてください。
main
[STAThread]
static void Main()
{
// テスト用のプロンプト
// deepthinkを使って、世の中のめちゃくちゃ難しい問題に自分なりの答えを出して。
// myfile.txt を空にする
File.WriteAllText("myfile.txt", "", Encoding.UTF8);
// クリップボードの内容を取得
string prompt = Clipboard.GetText();
if (string.IsNullOrWhiteSpace(prompt))
{
Console.WriteLine("クリップボードが空です。");
return;
}
// Copilot に送信
SendToCopilot(prompt);
// 完了待ち
string result = WaitUntilDone();
// 実行ファイルと同じフォルダに myfile.txt を保存
string exeDir = AppDomain.CurrentDomain.BaseDirectory;
string outPath = Path.Combine(exeDir, "myfile.txt");
File.WriteAllText(outPath, result, Encoding.UTF8);
Console.WriteLine("=== Copilot 応答 ===");
Console.WriteLine(result);
Console.WriteLine("====================");
Environment.Exit(0);
}
先ほどの関数を元に、
入力(SendToCopilot)
⬇︎
出力完成まで待つ(WaitUntilDone)
⬇︎
出力を取得(GetLatestMessage)
に、出力をmyfile.txtに保存をmainで追加しました。
6.argument.exeの仕組み
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
namespace argument
{
class Program
{
// Copilot に送信
static void SendToCopilot(string text)
{
Clipboard.SetText(text);
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return;
var input = edge.FindFirst(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, "userInput")
);
if (input == null)
{
Console.WriteLine("入力欄が見つかりません");
return;
}
input.SetFocus();
SendKeys.SendWait("^v");
SendKeys.SendWait("{ENTER}");
}
// 最新の返答本文を取得
static string GetLatestMessage()
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return "";
var msgs = edge.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
);
if (msgs == null || msgs.Count == 0)
return "";
var latest = msgs[msgs.Count - 1];
var texts = latest.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
);
if (texts == null || texts.Count == 0)
return "";
var sb = new StringBuilder();
for (int i = 1; i < texts.Count; i++)
{
string line = texts[i].Current.Name;
if (string.IsNullOrWhiteSpace(line))
continue;
line = line
.Replace('\u2010', '-')
.Replace('\u2011', '-')
.Replace('\u2012', '-')
.Replace('\u2013', '-')
.Replace('\u2014', '-')
.Replace('\u2015', '-')
.Replace('\u2212', '-')
.Replace('/', '/')
.Replace('\', '\\')
.Replace('_', '_');
sb.AppendLine(line);
}
return sb.ToString().Trim();
}
// 最新 ai-message の Text ノード数
static int GetLatestMessageLineCount()
{
var edge = AutomationElement.RootElement.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
);
if (edge == null) return 0;
var msgs = edge.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
);
if (msgs == null || msgs.Count == 0)
return 0;
var latest = msgs[msgs.Count - 1];
var texts = latest.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
);
return texts?.Count ?? 0;
}
// Copilot の生成完了待ち
static string WaitUntilDone()
{
string last = "";
int stable = 0;
while (true)
{
string text = GetLatestMessage();
if (string.IsNullOrWhiteSpace(text))
{
Thread.Sleep(100);
continue;
}
// deepthink の2行は無視
if (text == "考え中…" ||
text == "自分の考えを整理し、計画を作成する")
{
Thread.Sleep(100);
continue;
}
// 3行以上になっていなければまだ生成途中
int lineCount = text.Split('\n').Length;
if (lineCount < 3)
{
Thread.Sleep(100);
continue;
}
// 安定判定
if (text != last)
{
last = text;
stable = 0;
}
else
{
stable++;
}
// 4回連続で同じ → 生成完了
if (stable >= 4)
{
// deepthink の2行を削除して返す
var cleaned = new StringBuilder();
foreach (var line in text.Split('\n'))
{
if (line.Contains("考え中…")) continue;
if (line.Contains("自分の考えを整理し、計画を作成する")) continue;
cleaned.AppendLine(line);
}
return cleaned.ToString().TrimEnd();
}
Console.WriteLine("待機中");
Thread.Sleep(100);
}
}
[STAThread]
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("引数がありません。");
return;
}
string prompt = args[0];
// Copilot に送信
SendToCopilot(prompt);
// 完了待ち
string result = WaitUntilDone();
// 保存処理
string exeDir = AppDomain.CurrentDomain.BaseDirectory;
string outPath = Path.Combine(exeDir, "myfile.txt");
File.WriteAllText(outPath, result, Encoding.UTF8);
Console.WriteLine("=== Copilot 応答 ===");
Console.WriteLine(result);
Console.WriteLine("====================");
Environment.Exit(0);
}
}
}
これは、clipboard版からmainを差し替えて、因数版にしたものです。
[STAThread]
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("引数がありません。");
return;
}
string prompt = args[0];
// Copilot に送信
SendToCopilot(prompt);
// 完了待ち
string result = WaitUntilDone();
// 保存処理
string exeDir = AppDomain.CurrentDomain.BaseDirectory;
string outPath = Path.Combine(exeDir, "myfile.txt");
File.WriteAllText(outPath, result, Encoding.UTF8);
Console.WriteLine("=== Copilot 応答 ===");
Console.WriteLine(result);
Console.WriteLine("====================");
Environment.Exit(0);
}
}
args[0]を使用して、因数に対応しています。
基本的なコードは同じです。
7.終わりに
いかがだったでしょうか。
ぜひauto_ai_cを使って、AI使用の効率化をしてみてください!
copilotの解答を、そのまま使用することも出来ますので、AIツールなどにも向いてるかと思います。
copilot利用規約と、本ツールの利用規約をしっかり守って、正しい使い方をしてくださいね!