@tamai/hub-cli
v0.1.13
Published
CLI for the hub personal dashboard: D1-backed task board and project registry.
Readme
@tamai/hub-cli
hub(tamai個人のパーソナルハブ)のタスク看板、プロジェクト、インフラ資産、デプロイ先を操作する CLI。
状態の正本は hub の D1 であり、この CLI はそれを読み書きする唯一の道具である。
依存ゼロ(node: のみ)。認証は tamai の Cloudflare Access service token が要るので、
それを持っていない環境では何もできない。API の契約・権限・エラーの意味は
tamaitakamasa/hub リポジトリ側が正本で、ここには書かない。
インストール
npm i -g @tamai/hub-clihub コマンドが PATH に入る。Node 22 以上が要る。
入っている版を確かめる:
command -v hub
hub --versioncommand not found なら入れる。入っていても古いことがあるので、動かないときは
hub --version を見てから上げる(「無ければ入れる」だけでは古い CLI を拾う)。
認証
~/.config/tamai/hub-tasks.env に service token を置く。発行と配布は たまが行う。
TASKS_CLIENT_ID=...
TASKS_CLIENT_SECRET=...
TASKS_BASE_URL=https://dash.tamaitakamasa.comprocess env に無ければ CLI がこのファイルを自動で読むので、毎回の source は要らない。
別の場所に置くなら TASKS_ENV_FILE でパスを渡す。
CLI は設定済みの認証値を出力から秘匿する(stdout / stderr / エラー文のいずれも)。
CLI 自身が値を書かないだけでなく、値そのものが API 応答や例外に含まれて返ってきた場合も
[redacted] に置き換える。出すのは「どのファイルを読もうとしたか」だけである。
秘匿は出力全体に対する文字列置換なので、認証値の形式は API を呼ぶ前に検査する。
32 文字未満・空白を含む値は「形式が不正」として弾く(短い値を通すと、秘密でない JSON の
キーや値まで置き換わってしまう)。正規の service token は Client ID が 32 桁 hex + .access、
Client Secret が 64 桁 hex か cfast_ 始まりで、この下限を優に超える。
エラー文に値そのものは出ないので、弾かれたら env ファイルの中身を直接確かめること。
HUB_ACTOR
export HUB_ACTOR=codex1service token を複数のエージェントで共有するので、誰の操作かはこの名乗りでしか分からない。
X-Hub-Actor として送られ、看板の監査 event に残る。設定を忘れると env の既定
(orchestrator 等)の名義で記録され、誤帰属になる。
使い方
hub --help
hub task help
hub project help
hub infra help
hub deploy help動詞は task が new / add / claim / next / show / edit / report / note / list / audit / brief、
project が list / show / create / update / move、
infra が list / new / show / update / note / link / unlink、
deploy が list / show / link / unlink。
正確なオプションは常に --help を見る(ここにも、スキルにも写経しない)。
全動詞が --json を受ける。機械が読むときは必ず --json を使い、人向けの整形を parse しない。
help と --version は認証情報なしでも読める。
show / claim / report / note / edit の <id> には記録番号(150 / #150)も渡せる(context_ref が docs/records/150-… のタスクを一覧から引く。番号は repo ごとの採番なので別 repo と重なることがあり、そのときは今いる作業ツリーに記録ファイルがある方を採り、割り切れなければ候補を出して止まる)。
project update の参照リンクは URL を貼るだけでよい
hub project update kki-website --add-link https://www.kki.ac.jp/
hub project update kki-website --add-link "テスト=https://stg.example.test/" --link-role preview
hub project update kki-website --remove-link-url https://stg.example.test/--add-link は http(s) で始まる値を URL 単独と見なす(ラベルと URL の判別は = で割るより先に
行うので、https://x.test/?a=1 が「ラベル ?a」に割れることはない)。ラベルを省くと hub 側が
URL から決める。"<ラベル>=<URL>" の形は従来どおり最初の = で分ける。
--link-role は同じコマンドの --add-link 全部に付く役割で、任意指定。値は production /
preview / admin / docs / minutes / code / files / communication / other。
CLI は URL から役割を推さない(判別は Worker 側で行い、CLI は語彙の打ち間違いだけを手元で弾く)。
削除はラベル完全一致の --remove-link と、URL 完全一致の --remove-link-url の 2 つ。
ラベルの重複は許すので(自動ラベルは重なりうる)、同じラベルが 2 本あるときは
--remove-link-url で指す。逆に同じ URL の重複は拒否する。
入力した URL は資格情報を抱えていることがあるため、どのエラー文にも再掲しない
(何番目の --add-link か・既存側のラベルだけを出す)。
グループ(プロジェクトの親子)
hub project update koyo --entity-type group # 案件 → グループへ
hub project update koyo-cms --parent koyo # グループに属させる
hub project update koyo-cms --parent none # グループから外す--entity-type は project(案件・既定)か group(グループ)。グループは案件の上の層で、
自分は親を持てない(1 段)。--parent は所属するグループの slug で、none で外す。
指定できるのはグループの slug だけなので、--entity-type group --parent <slug> は
CLI が送る前に断る。
配下の案件がある group の拒否は3条件で別々である。削除と案件(project)への変更は
子が1件でもあれば拒否され、終了(status=closed)は終了していない子があるときだけ拒否される
(子が全部終了していればグループも終了にできる)。いずれも API が 409 project_has_children を
返し、CLI はその message をそのまま出す。
show / list は、グループのときだけ type: group と children: N の行を、親を持つ案件には
parent: <slug> の行を出す(kind と同じく既定でないときだけ出す流儀)。show は
配下の案件を children: の下に - <slug> [<status>] <name> で並べる。--json は応答そのまま。
依頼書(hub task brief)
hub task brief 180 --role implement --worker opus35
hub task brief 181 --role review --worker codex1 --commits "4803003 79410c1" --note-file 裁定.md
hub task brief 183 --role review --worker codex1 --out /tmp/brief-183.md --sandboxed文脈ゼロの AI へ渡す依頼を Markdown 1 枚にまとめる。看板(本文・status・owner・直近の報告)、
案件(repo・リンク・デプロイ先・台帳)、作業ディレクトリの規約ファイルの場所を 1 コマンドで束ねる。
既定は stdout、--out <path> でファイルへ、--json は集めた素材をそのまま返す。
--role は implement(既定)/ review / design で定型部分が変わる。
3 つだけ性質を覚えておけばよい。
- 定型文は CLI の中にあり、repo の規約は写していない。 依頼書は作業ディレクトリの
AGENTS.md/CLAUDE.mdを「最初に読む」と指すだけで、検証コマンドもそちらへ送る。 - 看板の本文は全文が入るので、依頼書は
hub task showを指さない。API に届かない相手には--outのファイルだけを渡せばよい(--sandboxedを付けると、CLI を使わない・commit できなければ ファイルに書くだけ・報告は依頼者が代行、の段落が入り、認証の行が消える)。--sandboxedは明示するもので、--workerの呼び名から環境を推測しない。 - 台帳の口座欄(
account/account_ref/note/payer/holder)は出ない。 依頼に要らないので素材の段階で落としてあり、--jsonにも現れない。--outのファイルの中身も、書く前に認証値の秘匿を通す。
--out は生成物なので既存ファイルを上書きするが、git で追跡されているパスには書かない
(記録や指示書の上に依頼書を書かないため)。--commits は自由文で、--role review で省くと
警告が出て「対象は依頼者から受け取る」と書かれる。案件が引けないときは案件の節を省いて警告し、
依頼書そのものは出す(看板が引けないときだけ何も出さない)。
デプロイ先を読む・案件に紐付ける
hub deploy list --platform cloudflare_workers --project hub
hub deploy show hub-dash
hub deploy link hub-dash --project hub
hub deploy unlink hub-dash --project hubshow / link / unlink は名前の完全一致またはIDで指定する。同名の対象が複数あるときは、
候補のplatform・アカウント・IDを見てIDで指定する。link は現在の集合へ追加し、unlink は
指定した案件だけを外す。両方とも --project が必須で、繰り返し指定できる。
最新のversionをGETして全置換PUTへ渡す。409は自動再送せず、APIのエラーをそのまま返す。
これはhubに記録したデプロイ先の操作であり、providerへデプロイするコマンドではない。
インフラ資産を読む・更新する
hub infra list # 自分で管理する資産だけ(既定)
hub infra list --management all # 他社管理を含む全件
hub infra new --kind domain --name example.jp --provider xserver --renewal-policy undecided
hub infra show example.jp
hub infra update example.jp --renewal-policy let_expire --reason '11/30までに解約と決めた'
hub infra update example.jp --prod-platform none # 本番の置き場なし(確定値)
hub infra update example.jp --renew-source bundled --bundle-server motokura
hub infra update example.jp --transfer-to cloudflare --transfer-stage awaiting_approval \
--transfer-due 2026-09-26T11:00:00+09:00
hub infra note example.jp --note 'Xserverのパネルで自動更新onを確認'
hub infra note example.jp --note-file /tmp/note.md
hub infra note example.jp --note '…' --operation-id 01M33H236V8MACB2DFR641ATK3 # 送り直し
hub infra link example.jp --project hub
hub infra unlink example.jp --project hub
hub infra unlink example.jp # 紐付けを全部外すshow / update / link / unlink は資産名の完全一致またはIDで指定し、見つからない場合は
近い候補を3件まで出す。update は指定した項目だけをPATCHし、version競合時は最新を取り直して
1回だけ再試行する。link は現在の紐付けへ追加、unlink は指定分を削除し、APIへはどちらも
PUT /:id/projects の全置換として送る。
--status は契約の現在(active=契約中 / cancelled=終了 / needs_review=要確認)で、
--renewal-policy が継続の意思(renew / let_expire / cancel_now / while_bundled / undecided)を持つ。
--status to_cancel と --migration-plan cancel はAPIを呼ぶ前にCLIが拒否する——どちらも意思なので
--renewal-policy へ移った(台帳に残っている行はデータ移行で消える)。--renewal-policy none は
undecided へ戻す(NOT NULLの列なので空にはしない)。
--renew-source は更新の方式(paid / bundled)で、--auto-renew(確認できた更新状態)とは
別の軸である。特典ドメインでも on には化けない——特典元のサーバー契約が解約されれば更新は止まる。
--bundle-server は特典元のサーバーで、bundled のときだけ持てる(--hosted-on と同じく名前かIDで指定する)。
収容先(どのサーバーに乗っているか)とは別の事実なので、一致しない行がある。
--transfer-to / --transfer-stage / --transfer-due は移管の進み。段階は
requested / awaiting_approval / approved / rejected / cancelled で、rejected と cancelled は
provider を変えない(移管しなかった)。--transfer-due は時刻を含む ISO 8601 だけを受ける。
移管の完了は --provider <移管先> --transfer-to none --transfer-stage none --transfer-due none を
1回で送る(移管先が残ったまま --provider を変えると400)。
移管の3項目を含む更新は409で自動再送しない(--reason と同じ扱い。段階は人が申請・承認メールを
見て書く値なので、取り直して自分の古い認識で巻き戻さない)。
特典元にしているドメインがあるサーバーは、hub infra からは消せない(APIが409で拒む)。
note は台帳の経緯を1件足す(POST /:id/notes)。資産の version を進めないので、
画面を開いたまま追記しても、そのあとの保存が競合しない。本文は2,000文字までで分割しない
(hub task note と違い、長い本文は要点へ縮める。旧メモの原文保管はCLIでは行わない)。
操作ID(ULID)は1操作につき1つをCLIが作り、送信の前に決める。応答が返る前に通信が
切れた失敗だけ、同じ操作IDで1回だけ送り直す——届いていた場合はAPIがその操作IDを見て
既存のイベントを返すので、二重に積まれない。応答が返った失敗(400 / 409 など)は
サーバが答えているので再試行しない。
失敗すると 操作ID: <ULID> を出す。後から送り直すときは、素で打ち直さず
--operation-id <ULID> にその値を渡す(素の打ち直しは別の操作IDになるので、
1回目が届いていた場合に2件積まれる)。
hub infra note example.jp --note '…' --operation-id 01M33H236V8MACB2DFR641ATK3update --reason <text> はその更新の理由(500文字まで)で、列ではなく監査イベントへ入る。
値の変更と一緒に指定する(理由だけの更新は400。経緯は hub infra note)。
--reason を含む更新は409で自動再送しない——理由は読んだ状態に依存するので、
最新を確認して書き直す。--append-note は次の版で外す(使うと stderr へ予告が出る)。
「現在の補足」を上書きする --note は残り、経緯は note 動詞へ分かれる。
--dns-host / --mail-host / --migration-plan / --contract / --hosted-on-basisは
noneで未記入(null)へ戻せる。--prod-platform noneだけは「本番の置き場は無い」という
確定済みのenum値を送り、未記入には戻さない。
new は --kind domain|server、--name、--provider が必須。
その他のフィールドオプションは update と同じ変換を使い、省略値はAPIの既定に任せる。
--append-note は初期noteになるが、廃止予告は更新時と同じ。
--reason は既存の作成APIが受け付けないため、CLIも書き込み前に拒否する。
経緯は作成後に hub infra note で足す。
作成は通信エラーでも409でも自動再送しない。結果を show / list で確認してから判断する。
削除動詞は作らない。正確なオプションとserver資産へ指定できない項目は hub infra help を正本とする。
起票済みタスクを直す(hub task edit)
hub task edit 01M3QW0PAMPVYGXWAEV8KGSEMP --title '新しいタイトル'
hub task edit 196 --due 2026-10-15 --project hub
hub task edit 196 --context-ref docs/records/196-cli-task-edit.mdタイトル・本文(--body-file で全置換)・project・期限・context_ref を直す。none は未記入へ戻す。
version は CLI が取り直し、409 は1回だけ取り直して再送する。本文を含む更新は再送しない
(読んだ本文に依存するため)。owner と status は扱わない(claim / report / 看板の領分)。
doing のタスクは owner しか直せない(API が403を返す)。
--repo-card の出力先は cwd 基準
cd ~/ws/private/hub
hub task new '新しいタスク' --project hub --body-file brief.md --repo-card--repo-card は「D1 へ起票 → docs/records/NN-slug.md の雛形 → context_ref の紐付け」を
1 コマンドで行う。出力先は cwd 基準である(git リポジトリの中で実行すること)。
記録ファイル名の slug は題名中の ASCII 英数字から作る。3文字未満の場合と、非ASCII文字を含む題名から
8文字未満の断片しか残らない場合は、3文字以上のkebab-caseによる --slug <slug> を必須とし、
D1へ起票する前にエラーで止める(例: --slug project-detail-pc-layout)。
| 実行場所 | 出力先 | context_ref |
| --- | --- | --- |
| repo 直下 | docs/records/ | docs/records/NN-slug.md |
| repo の dash/ | dash/docs/records/ | dash/docs/records/NN-slug.md |
| 別 repo(stw-manage 等) | その repo の docs/records/ | その repo からの相対 |
git root は探すが、出力先の基準にはしない(context_ref だけが git root 相対)。
これは現状の挙動で、実際そう使っている。取り違えると起票してしまってからでは戻しにくいので、
解決した出力先を起票前に stderr へ 1 行出す。確認待ちや自動停止は行わない。
--repo-card <dir> でディレクトリを明示できる(リポジトリ相対のみ。絶対パスは不可)。
done にしない
done(検収)は CLI から行えない。エージェントは hub task report までで、
done にするのは人間が dash で行う。done は「本番へ反映し、本番で検証まで終えた」状態を指す。
リリース手順
publish は GitHub Actions の npm trusted publishing(OIDC)で行う。NPM_TOKEN は置かない。
packages/hub-cli/package.jsonのversionを上げる commit を main へ入れる- その commit で
deploy.ymlが成功していることを確かめる hub-cli-v<version>のタグを打って push する(例hub-cli-v0.1.1)publish hub-cliworkflow が走る。verifyがタグと version の一致・main への取り込み・ CI の成功・テスト・npm packを確かめ、publishが tarball を出す
タグと package.json の version が食い違う、prerelease(-rc.1)を書く、main に入っていない
commit にタグを打つ、workflow_dispatch の成功しかない —— これらは verify が止める。
タグ push とデプロイはオーケストレーターだけが行う。
npm 側の設定(初回だけ・人が行う)
npm の trusted publisher は workflow を足しただけでは有効にならない。 npmjs.com のパッケージ設定で登録する。
| 項目 | 値 |
| --- | --- |
| Organization or user | tamaitakamasa(GitHub 側の名前。npm の scope @tamai とは別) |
| Repository | hub |
| Workflow filename | publish-hub-cli.yml |
| Environment | 空(environment は使わない) |
あわせて direct publish を許可する。direct publish は「staged publish(npm stage publish
で段階公開してから確定する)を経ずに npm publish で直接公開できる」設定のことで、
CI 以外からの publish の可否ではない。この workflow は npm publish を使うので、
これを許可しないと publish が通らない。CI 外の手動公開や token 利用の制限は別の設定として決める。
GitHub 側には hub-cli-v* タグの作成・変更・削除を release 担当に限る ruleset を張る
(workflow の検査だけを信頼境界にしない。タグ自身の workflow を書き換えれば検査は外せる)。
hub は private repo なので provenance は付けない(npm の provenance は公開 repo が前提)。
配備(Mac mini)
Mac mini では Hermes(アシタカ)が hub を使う。npm の global install で入れ、更新も同じ
コマンドを手で打つ(自動の配布経路は無い。2026-09-26 に hub-runner とその配備 script を退役した)。
ssh mini-ts 'bash -lc "npm i -g @tamai/hub-cli@latest && command -v hub && hub --version"'認証ファイル ~/.config/tamai/hub-tasks.env の置き方は ops repo の docs/環境 mini(Mac mini).md。
repo 直参照のラッパー(~/.local/bin/hub)は Mac mini には置かない。PATH 上で先に当たると
旧実体を指すため、入れ直したら command -v hub と hub --version で実体を確かめる
(2026-09-16 に旧ラッパー /opt/homebrew/bin/hub を hub.wrapper.bak へ退避して切替済み)。
ライセンス
UNLICENSED(個人利用)。npm の scope は公開だが、利用許諾を与えるものではない。
