pharos を使った Azure DevOps の操作
pharos は、ブラウザログインではなく環境変数で認証する、シェルから Azure DevOps を操作できる CLI です。
実行可能なすべての操作を以下に示します。pharos --help を実行して調べないでください — それは数回の呼び出しを消費し、このセクションに同じ情報が記載されています。
whoami ADO_PAT の所有者を表示します。@Me はこれに解決されます
types --state と --type が受け入れる値を、タイプごとに表示します
query どの作業項目か — 自分に割り当てられたもの、スプリント内のもの、
特定のタイプのもの、まだオープンなもの。ID ではなく、展開されたアイテムを返します。
task <id> 1 つの作業項目全体:フィールド、コメント、
添付ファイル、タイトル付きの関連、およびリンクされたすべての Wiki ページの
内容とディスカッション
--children 1 回の呼び出しで EPIC とその配下すべてを取得します。
--depth <n> より深い階層を取得します(最大 5)
--compact ID をフラット化し、ボードキーを削除します
create <type> --title "…" 1 つの作業項目を作成します。--parent は同じパッチで設定します
update <id> フィールドを変更します:--state --priority --assignee
--title、またはその他は --field Name=value
link <id> --parent <id> 2 つのアイテムを関連付けます。--child --related も可
unlink <id> --parent <id> --predecessor --successor --duplicate も可
link <id> --wiki-page <path> Wiki ページを作業項目にリンクします — 作業項目側
wiki links <path> どの作業項目がページをリンクしているか(ADO には API がありません)
wiki link <path> --item <id> Wiki 側。--item は繰り返し可能
wiki unlink <path> --item <id>
history <id> 何が CHANGED したか、フィールドごと、誰がいつ
iterations | areas 日付付きのスプリント、およびエリア
links この ORG が持つすべての関連タイプと、
どの --flag がそれに到達するか(約 18 個中 6 個)
fields [--type T] フィールドが ACCEPT するもの — 許可される値
attach <id> <file> ファイルを作業項目に追加します(添付ファイルリスト)
detach <id> <url-or-guid> 削除します。--yes が必要。または --name <file>
download <item-id|guid|url> 読み戻します。WORK ITEM id と --name <file>、
または --all --out <dir>。--out がない場合は書き込みません
image <file> テキスト内で使用するための PICTURE をアップロードします。
貼り付ける markdown を出力します。attach とは異なります
people [query] @メンションできる人、@<guid> 形式で表示
delete <id> --yes ごみ箱へ移動します(意図的に完全削除はしません)
restore <id> 復元します
deleted ごみ箱にあるものを名前付きで表示します。
--top <n> — デフォルトで 50 件返し、
実際の総数を "count" として報告します
wiki list | tree | read <path> | write <path> | delete <path>
wiki move <path> <new path> ページを移動します。サブページも一緒に移動します
wiki rename <path> <new name> 同じ呼び出しですが、リーフのみです。どちらも --yes が必要
wiki duplicate <path> [to] Azure DevOps にない動詞です。"<path> - コピー N"
wiki image <file> ページまたはページコメント用の画像です。
`image` とは異なるエンドポイントで、名前はユニーク化されます
wiki import <file...> .md .txt .docx .pdf .rtf .html -> ページ。
.pptx は意図的に拒否されます — 以下を参照
--under <path> --as <name>
comment list | add | edit | delete <target> は作業項目 ID または Wiki パス
comment react | unreact | reactors like dislike heart hooray smile confused
hooks list | check | create | repoint | delete サービスフックをリアルタイムで。
`check` は --hub <url> が必要。`list` は既に使用中の URL を表示
plan <file> 実装計画 → 作業項目ツリー
setup org、project、token → キーチェーン + シェルプロファイル
テキスト入力:--text / --file / --stdin。グローバルオプション:--pretty(人間向け)、
--yes(破壊的操作)、--dry-run(プレビュー)。
ADO_ORG、ADO_PROJECT、ADO_PAT はセットアップ後に環境変数に既に設定されています。
作業を開始する前にこれらを確認しないでください。不足しているものは、終了コード 2 で
"kind": "config" として通知されます。これが重要になる唯一のケースです。
作業項目を見つける:pharos query
curl と WIQL エンドポイントに頼らないでください。 このスキルは以前、そのためのレシピを提供していました。
なぜならクエリ動詞がなかったからです。今はそれが存在し、レシピではできなかった部分を実行します。
WIQL は ID のみ を返すため、レシピでは 16 の数字と、それらを意味あるものにするために
アイテムごとに 1 回の呼び出しが必要でした。
pharos query --mine # 自分に割り当てられており、まだオープンなもの
pharos query --sprint "Sprint 1" # --sprint current は @currentIteration
pharos query --type Epic --state Doing # 両方とも繰り返し可能
pharos query --assignee "[email protected]"
pharos query --tag api --all # --all は完了した作業も含む
pharos query --wiql "SELECT [System.Id] FROM WorkItems WHERE …" # エスケープハッチ
フラグは AND で結合されます。出力は展開されたアイテム(id、type、title、state、assignee、
iteration、tags、priority、changed)なので、--mine は 1 つのコマンドで完了し、
結果ごとにクエリとフェッチを続ける必要はありません。実際に作業する数個のアイテムについてのみ、
pharos task <id> でフォローアップしてください。
件数を報告する前に、openness フィールドを読んでください。 「12 件オープン」は、
何が完了とカウントされたかを知らなければ無意味です。Azure DevOps ではプロセステンプレートが
すべての状態を改名できます。query はプロジェクト自身の状態カテゴリを読み取り、
どの状態を終端とみなしたかを教えます。読み取れなかった場合はその旨を伝え、推測にフォールバックします。
その場合は、明示的に --state を渡す必要があります。assignedTo は、@Me が実際に誰に解決されたかを
示します。共有トークンやサービス トークンの場合、「自分に割り当て」が静かに他人を意味することがあります。
--all は他のすべてのフィルターと組み合わせ可能なので、--mine --all は
自分に何も割り当てられていない ことと 開いているアイテムがない ことを区別する方法です。
裸の pharos query --all も有効で、プロジェクト全体を返します。
--sprint は、名前そのものか fieldPath のいずれかを受け入れ、それらを正規化します。
fieldPath ルールは フィールド値としての System.IterationPath に関するものです。
ただし、名前付きスプリントは UNDER(サブイテレーションを含む)で一致し、
--sprint current は =(完全一致)で一致するため、同じクエリではありません。
--wiql は FROM WorkItemLinks … MODE (MustContain) クエリも受け付け、
同じ方法で ID を展開します。これにより、1 回の呼び出しで特定の関連タイプを持つ
すべてのアイテムを見つけることができます。
--wiql を使用する場合、[System.State] NOT IN GROUP 'Completed' と書かないでください。
これはパースされ、200 を返し、すべて に一致します。IN GROUP は作業項目の
TYPE カテゴリのみをカバーし、未知のグループはエラーなしで空のセットに解決されます。
測定日:2026-08-05。
作業項目の変更
pharos update 225 --state Doing # 作業が進んだら状態を変更
pharos update 225 --priority 1 --assignee "[email protected]"
pharos update 225 --field Microsoft.VSTS.Scheduling.RemainingWork=3
pharos attach 225 ./bestsellers.xlsx --comment "数字です"
update は アイテムを読み取り、/rev に対する test 操作で変更を適用します。
そのため、あなたの読み取りと書き込みの間に誰かが書き込んだ場合、あなたの編集が失われるのではなく
conflict が発生します。既に設定されている値を設定しようとすると、何も書き込まれず、
その旨が報告されます。意味のない PATCH でも System.Rev は増加し、アイテム上の他のすべての
キャッシュされたリビジョンを無効にします。--dry-run は変更前 → 変更後を表示し、
何も書き込みません。--yes はありません。フィールド編集は通常の編集であり、
Azure DevOps はすべてのリビジョンを保持します。
attach はバイトをアップロードし、それを AttachedFile リレーションとしてリンクします —
2 回の呼び出し、1 つのコマンドです。添付ファイルは 不変 です。同じファイルを 2 回添付すると
2 つになり、置換もバージョン管理もありません。detach は 1 つ削除します。--yes が必要で、
位置ではなく ID で添付ファイルを見つけ、Azure DevOps にバイトを残すため、URL を再添付することで
元に戻せます。ファイルに --name <file> で名前を付けるか、GUID を渡します。download と同様に、
同じ名前の 2 つのファイルは、ソート順で先のものを削除するのではなく 拒否 します。
create は --parent をフィールドと同じパッチに配置するため、子が一瞬でも孤立することはありません。
また、update と同じフラグ名を受け付けます。
unlink は推測することを拒否します。遭遇する前に知っておく価値があります。
Azure DevOps は配列内の位置によってリレーションを削除するため、古い読み取りからのインデックスは
別のリンクを切断し、リクエストは成功します。unlink は ID でリレーションを見つけ、
読み取ったリビジョンに対する test 操作で削除します。リレーションが存在しない場合は
終了コード 3 が返されます — これは何も起こらなかったことを意味し、呼び出しが失敗したわけではありません。
delete は ごみ箱 に移動し、--yes が必要です。拒否時には最初にタイトルを引用します。
restore <id> で戻します。フラグは必要ありません。
ここには意図的に完全削除はありません。 これは Azure DevOps が持つ唯一の元に戻せない操作であり、
--yes は渡すことを学習したフラグです。Web UI がそれを担当します。
どうしてもパージする必要がある場合は、フラグを探すのではなく、Web UI に誘導してください。
テキスト内の画像と、それに付随するファイル
これらは 2 つの異なるものであり、間違った方を選ぶのが避けるべきミスです。
pharos attach 225 ./bestsellers.xlsx # ファイル、添付ファイルリストに表示
pharos image ./screenshot.png # 画像、テキスト内に配置するため
添付ファイル は AttachedFile リレーションを作成し、作業項目の添付ファイルリストに表示されます。
インライン画像 はリレーションを一切作成しません。測定例 #333 では、説明に貼り付けられた 2 つのスクリーンショットが
relations: 0 と attachments: [] を与えました。フィールドテキスト内の  であり、それ以外には何もありません。
そのため、添付ファイルリストに何も表示されないのは正しい動作です。
したがって、image は markdown の行を出力し、書き込みはあなたに委ねます。
pharos image ./chart.png --pretty
# 
pharos update 225 --field System.Description="$(cat <<'EOF'
収益は上昇しています。チャートをご覧ください:

EOF
)"
Wiki 画像は別のエンドポイントであり、wiki image が必要です。 Wiki は git リポジトリなので、
そこでの添付ファイルは ファイル であり、その 名前 が ID です。2 つ目の image.png をアップロードすると、
最初のものを上書きし、/.attachments/image.png を指すすべてのページの画像が黙って変わります。
wiki image は送信前に名前をユニーク化し、リポジトリ相対のリンクを出力します。
pharos wiki image ./diagram.png --pretty
# 
そのパスを使用し、絶対 URL は使用しないでください。wit/attachments URL にリンクするページは、
セッションを持つユーザーのみレンダリングされ、他のユーザーには壊れて表示されます。
同じ markdown がページとページコメントの両方で機能します。
作業項目から添付ファイルを取得する
pharos task <id> は、各ファイルの GUID とともに、何が添付されているかを一覧表示します。
JSON および --pretty 出力の両方に含まれます。その後、名前で 1 回の呼び出しで取得します。
pharos download 41 --name Skills.pptx --out ./Skills.pptx
pharos download 41 --all --out ./attachments # アイテム上のすべての添付ファイル
pharos download <guid-or-url> --out ./file.bin # 既に GUID を保持している場合
--out が書き込みを行います。 これがないと、サイズのみが表示され、ファイルは作成されません —
生のバイトを stdout に出力すると、他の動詞が出力する JSON が壊れます。
裸の pharos download 41 は、リストを表示する 使用法エラーです。名前、GUID、サイズが表示されるため、
適切なファイルを選択するために 2 回目の呼び出しは必要ありません。同じ名前の 2 つの添付ファイルは、
推測する代わりに拒否します。添付ファイルは不変であるため、ファイルを 2 回添付すると 2 つになり、
名前は ID ではありません。
detach は同じ --name <file> を受け付けるため、task --pretty を読んだ後に使う動詞は、
表示されたものをそのまま受け入れます。
pharos detach 41 --name Skills.pptx --yes # --dry-run でプレビュー可能
中身を読むのは あなた自身の 仕事であり、あなたのツール で行います
download はバイトをディスクに保存し、そこで止まります。.docx、.pptx、.xlsx、.pdf を
読み取り可能な形式に変換するのは、あなたの環境の仕事です。
そのために pharos-convert を使用しないでください。 これは wiki import のコンバーターであり、
macOS アプリと共有されているため、両方とも同じファイルから同じ ページ を生成します。
その出力は Wiki ページになるように整形されており、ここで使用すると、読んでいるものが
アプリのインポートパスに結びついてしまいます。また、.pptx はまったく読めません。
これが 静かに 失敗する 2 つの方法(両方とも実際の作業項目で測定済み):
- 間違った場所でツールを確認しました。 システム PATH に対する
which markitdownと システムpython3に対するimport docxは、そのツールがインストールされているマシンでも 空で返ってきます — スキルごとの venv 内にある場合です。これら 2 つのチェックを実行するエージェントは 「何もない」と結論付け、実際には存在していたツールを迂回します。あなたの エージェントが ツールをどこに保持しているかを確認してから、存在しないと結論付けてください。 - 空でないテキストは、ドキュメントを読んだ証拠にはなりません。 実際の
.pptxが 7 つの断片、 約 90 文字に抽出されました。仕様全体は 2 つの埋め込み PNG に含まれていました。 どんなテキストのみのリーダーでもそのデッキに対して何かを返し、動作したように見えます。 スライドデッキやスキャンされた PDF の場合、ペイロードは通常画像です。それらを抽出して実際に見てください。
まず、PDF をネイティブに読み取れるかどうかを確認してください。読み取れる場合、これは 1 つのコマンドです。
多くのエージェント(Claude Code もその 1 つ)は、PDF を 視覚的に、ページごとに、 人間が見るように読み取ります。これにより、テキストがまったくないスキャンも、OCR ステップなしでカバーされます。 それがあなたの場合、問題全体は 1 つの変換に集約されます。
soffice --headless --convert-to pdf f.docx --outdir ./out # .pptx、.xlsx も同様
# …

