KeygripGit/claude-mods/tree/main/plugins/workorder-pane
workorder-pane — Keygrip work order を Claude Code 内で扱う
Keygrip client の deploy queue をペインに描画する Claude Code プラグインです。/workorders が work order を一覧し、Reader は各 order を Overview、Fields、Edits、Schema、Verify、Notes、live/proposed を切り替えられる server-rendered Preview に分割します。plugin marketplace、--plugin-dir/CLAUDE_CODE_PLUGIN_DIRS、Windows settings copy から導入でき、$.http.fetch 経由で project key を使い remote client MCP server を呼びます。
この mod について
workorder-pane — Keygrip work order を Claude Code 内で扱う
これは Claude Code の mod(function hooks のプラグイン。docs/claude-code-mods.md 参照)で、client のデプロイループをツール行の大量の Markdown ではなくペインに描画します。issue #1516 を追跡します。
/workorders # 接続中のプロジェクトにある全 work order のキュー
/workorders 502 # optimization id で 1 件を直接開く
表示するもの
Queue。 kg_workorders の各 work order を id、version、mode、state、keyword の 1 行で表示します。Blocked な行は暗くなり開けません(サーバーも拒否します)。r で更新、s で superseded version の表示を切り替えます。
Reader は選んだ行に対して kg_fetch_workorder を 1 回だけ取得し、固定見出しで文書を分割します。1–7 でタブを切り替え、b でキューに戻ります。
| タブ | 表示 | | --- | --- | | 1 Overview | mode、準備日、keyword target、「何が変わったか」、hero image | | 2 Fields | デプロイ可能な値をコードブロックに入れ、SEO の上限に対する長さメーターを表示(超過時は赤)。live page と同じなら unchanged、コピー用ボタンも表示 | | 3 Edits | 各 body edit を why、where、Find text、Replace/Insert HTML と一緒に枠で囲み、コピー用ボタンを付けます。新規 order では body 全体を表示 | | 4 Schema | script wrapper を外した JSON-LD とコピー用ボタン | | 5 Verify | publish 後の手順を toggle で表示し、チェック済み数を示す | | 6 Notes | この version 以降の context change、recommended-not-applied、checked-and-left-alone | | 7 Preview | server 上の kg_workorder_preview がページを描画します。order を開いた瞬間に最初の slice の描画を始めるので、通常はすぐ表示されます。p で live(現在配信中)と proposed(ブラウザで order の編集を適用)を切り替え、各編集はブランドシアンの枠、削除は赤い破線で示します。n/u で 1600 px の slice を移動し、r で再描画します。ターミナルは pixels(kitty/Ghostty)、Desktop/VS Code は Svg 内の半分サイズの JPEG と app.keygrip.ai の同じ preview page へのリンクを表示します。 |
下部の Live URL フィールドを送信すると target: manual で kg_report_deployed を呼びます。空の URL は拒否され、この mod は他には書き込みません。
インストール(client と monorepo 外)
アプリはこのフォルダーを Claude Code plugin marketplace として提供します(web/mods.py、#1516)。GET /mods/marketplace.json は app/mods/ の各 mod を archive source として列挙し、GET /mods/<name>-<version>.zip は要求時に zip を作ります(決定的で、process ごとにキャッシュし、manifest に sha256 を持ちます)。/healthz のように公開され、client に GitHub アカウントもマシン上の git も必要ありません。client 向けガイドは /mods/(Desktop のクリック手順と terminal command)で、Client MCP access box は貼り付ける key と次の 2 コマンドを表示します。
claude plugin marketplace add https://app.keygrip.ai/mods/marketplace.json
claude plugin install workorder-pane@keygrip
ペインは $.http.fetch で https://app.keygrip.ai/client/mcp に直接接続します。ツールごとに JSON-RPC tools/call の POST を 1 回送り、プロジェクトの kg_client_ key を Bearer header に使います。key はペインで 1 度だけ尋ね、プラグイン自身の $.store に保存します(キュー上の k で変更)。manifest に server を宣言しないのは意図的です。ボタンからの tool call は Auto-mode の permission classifier に拒否されます(「この action を生成した request はそれを求めていない」)。plugin-bundled server でも Desktop で測定済みで、宣言すると各 client が allow list を貼る必要があり、manifest server では /plugin configure 経由で key が必要です。直接 fetch ならどちらも不要で、--permission-mode auto の 2026-10-03 測定で確認しています。endpoint は stateless な JSON 応答なので handshake はありません。インストールに変更を反映したいときは plugin.json の version を毎回上げます。install は見た version に固定され、claude plugin update は数字が変わった場合だけ進み、zip URL にも version が入ります。
⚠️ --plugin-dir/CLAUDE_CODE_PLUGIN_DIRS からロードしたプラグインは同名の marketplace install を上書きします(その後 claude plugin install --config は「failed to load after install」と報告)。開発マシンではどちらか一方を使います。
checkout からの読み込み(開発者)
CLI、1 セッション: claude --plugin-dir app/mods/workorder-pane
CLI または Desktop、常時: CLAUDE_CODE_PLUGIN_DIRS にフォルダーを指定します。process environment、またはユーザーファイルである ~/.claude/settings.json の env に置けます。
Windows の Claude Desktop は独自の engine と %USERPROFILE%.claude を使うため Windows path が必要です。
// C:\\Users\\<you>\\.claude\\settings.json
{
"env": { "CLAUDE_CODE_PLUGIN_DIRS": "C:\\\\Users\\\\<you>\\\\.claude\\\\mods\\\\workorder-pane" },
"permissions": { "allow": [
"mcp__keygrip-workorder__kg_workorders",
"mcp__keygrip-workorder__kg_fetch_workorder",
"mcp__keygrip-workorder__kg_articles",
"mcp__keygrip-workorder__kg_article_status",
"mcp__keygrip-workorder__kg_workorder_preview"
] }
}
marketplace install の server は permission rule では plugin_workorder-pane_keygrip-workorder です。allow list が必要なのは上の developer path の Auto permission mode だけです。MCP call は prompt ではなく button から来るため classifier に一致するものがなく拒否され、allow された tool は classifier を飛ばします。書き込みの kg_report_deployed は意図的に許可していません。ペインから deploy を報告するには Auto を切るか prompt を承認します。
WSL から Windows copy を同期します。
rsync -a --delete --exclude .cache --exclude '.claude-plugin/types' --exclude tsconfig.json \
app/mods/workorder-pane/ /mnt/c/Users/<you>/.claude/mods/workorder-pane/
接続する MCP server
keygrip-workorder —— https://app.keygrip.ai/client/mcp と、project-scoped な kg_client_ key(claude mcp add --transport http keygrip-workorder <url> -H "Authorization: Bearer kg_client_…")を使います。server 名は $.mcp.call に渡す名前で、Desktop app の .claude.json にも同じ server が必要です。key の発行場所は app の project settings、issue #1071 です。
Preview の生成
ラップトップでは何も実行しません。client MCP server の kg_workorder_preview(optimization_id, mode, slice, format, scale)(内部 /mcp にも同じ tool があります)は GET /api/v1/optimizations/{id}/preview を呼び、content/page_preview.py が提供します。アプリ自身の Chromium が run の page_url を読み、proposed では CMS editor と同じ規則で BodyEdit を live DOM に適用します(意味で探し、同レベルの次の見出しまでを区間とし、順番に適用)。その後 1280×1600 の slice にして screenshot を返します。1 回の call は image block 1 つと JSON block 1 つ(slices、height、各 edit の applied、view_url)を返します。render は web process 内で(run、version、mode)ごとに 10 分 cache されます。Web twin の …/studio/runs/<id>/preview/?mode=proposed は slice を重ね、各 edit の結果を列挙します。これが Desktop pane がリンクする view_url です。
Terminal の pixel 表示には kitty ≥ 0.28 または Ghostty が必要です(docs/claude-code-mods.md 参照)。
開発
claude plugin validate app/mods/workorder-pane # manifest + module hooks/calls
claude plugin test app/mods/workorder-pane # hooks/*.test.tsx against the engine
parser(hooks/parse.ts)は文書の見出しを key にします。hooks/fixture.ts はこの形の小さな work order で、optimization 502 をモデルにしています。validator が強制する規則は 1 回ごとに余計な手間を発生させます。$ は top-level function declaration にしか渡せず、atom() 参照は literal な plugin string が必要です。Button は string child 1 つまたは label を受け、fetch は render hook ではなく command または button handler から始めなければなりません(draw の async chain からの write は静かに捨てられます)。
未実装
- server の隣に structured JSON がないため、heading parser をなくせません。
- net-new page(live URL がまだない)の proposed render は未実装です。代わりに Keygrip 自身の preview template で body を描画します。
- process ごとの render cache が 2 台の web box 間で冷たすぎる場合に備えた共用 cache(R2 または Postgres)は未実装です。
インストール
まず作者の README で marketplace とプラグイン名を確認してください。コマンドはリポジトリの構成によって変わる場合があります。
claude plugin marketplace add KeygripGit/claude-mods claude plugin install workorder-pane
原文 / README
workorder-pane — Keygrip work orders inside Claude Code
A Claude Code mod (a plugin of function hooks, see docs/claude-code-mods.md) that draws the
client deploy loop in a pane instead of a wall of markdown in a tool row. Tracks issue #1516.
/workorders # the queue: every work order the connected project has
/workorders 502 # straight to one order by optimization id
What it draws
Queue. One row per work order from kg_workorders: id, version, mode, state, keyword.
Blocked rows are dimmed and refuse to open (the server refuses them too). r refresh,
s show/hide superseded versions.
Reader (pick a row). Fetches kg_fetch_workorder once and splits the document on its fixed
headings. Tabs on 1–7, b back to the queue:
| Tab | Shows |
|---|---|
| 1 Overview | mode, prepared date, keyword target, "what changed", hero image |
| 2 Fields | each deployable value in a code block, a length meter against its SEO limit (red when over), unchanged when it matches the live page, a copy button |
| 3 Edits | each body edit boxed: why, where, the Find text and the Replace/Insert HTML, a copy button; a net-new order shows the full body |
| 4 Schema | the JSON-LD with the <script> wrapper stripped, a copy button |
| 5 Verify | the post-publish steps as toggles, with a checked count |
| 6 Notes | context changed since this version, recommended-not-applied, checked-and-left-alone |
| 7 Preview | the page rendered on the server by kg_workorder_preview (the first slice starts rendering the moment an order is opened, so the tab usually draws at once): p toggles live (as served now) and proposed (the work order's edits applied in the browser, each outlined in brand cyan, removals marked with a dashed red rule); n/u move through 1600 px slices, r re-renders. Terminal: pixels (kitty/Ghostty). Desktop/VS Code: a half-scale JPEG inside an Svg, plus a link to the same preview as a page on app.keygrip.ai |
At the bottom, a Live URL field whose submit calls kg_report_deployed with target: manual.
It refuses an empty URL. Nothing else in the mod writes.
Installing it (clients, and anyone outside the monorepo)
The app serves this folder as a Claude Code plugin marketplace (web/mods.py, #1516):
GET /mods/marketplace.json lists each mod under app/mods/ as an archive source and
GET /mods/<name>-<version>.zip is the mod zipped on demand (deterministic, cached per
process, sha256 in the manifest). Public, like /healthz: a client needs no GitHub account and
no git on the machine. The client-facing guide is /mods/ (desktop click path, terminal
commands); the project's Client MCP access box prints the two commands and the key to paste:
claude plugin marketplace add https://app.keygrip.ai/mods/marketplace.json
claude plugin install workorder-pane@keygrip
The pane talks to https://app.keygrip.ai/client/mcp itself — one JSON-RPC tools/call POST
per tool over $.http.fetch, the project's kg_client_ key as the Bearer header — and asks for
that key once, in the pane, keeping it in the plugin's own $.store (k on the queue changes
it). It deliberately does not declare the server in the manifest: a tool call made from a
button is refused by the Auto-mode permission classifier ("the request that produced this action
did not ask for one"), measured on the desktop with a plugin-bundled server too, which would
make every client paste an allow list; and a manifest server needs the key through /plugin configure. A direct fetch needs neither — measured under --permission-mode auto on
2026-10-03. The endpoint is stateless and answers JSON, so there is no handshake. Bump
version in plugin.json with every change you want installs to pick up: an install pins to
the version it saw, claude plugin update only moves when the number does, and the zip URL
carries the version.
⚠️ A plugin loaded through --plugin-dir / CLAUDE_CODE_PLUGIN_DIRS overrides a marketplace
install of the same name (and claude plugin install --config then reports "failed to load after
install"); on a developer machine use one or the other.
Loading it from the checkout (developers)
CLI, one session: claude --plugin-dir app/mods/workorder-pane
CLI or desktop, always: name the folder in CLAUDE_CODE_PLUGIN_DIRS, in the process
environment or the env block of ~/.claude/settings.json (the user file, never a project's).
Claude Desktop on Windows runs its own engine with its own %USERPROFILE%\.claude, so it
needs a Windows path. The copy that works today:
// C:\Users\<you>\.claude\settings.json
{
"env": { "CLAUDE_CODE_PLUGIN_DIRS": "C:\\Users\\<you>\\.claude\\mods\\workorder-pane" },
"permissions": { "allow": [
"mcp__keygrip-workorder__kg_workorders",
"mcp__keygrip-workorder__kg_fetch_workorder",
"mcp__keygrip-workorder__kg_articles",
"mcp__keygrip-workorder__kg_article_status",
"mcp__keygrip-workorder__kg_workorder_preview"
] }
}
(For a marketplace install the server is plugin_workorder-pane_keygrip-workorder in those rules.)
The allow list matters in Auto permission mode only for the developer path above, where
the tools come from a hand-configured server: the pane's MCP calls come from a button, not
a prompt, so the auto-mode classifier has nothing to match them against and refuses them
("the request that produced this action did not ask for one"). Allowed tools skip the classifier.
The write tool kg_report_deployed is deliberately not allowed; switch the session off Auto, or
approve the prompt, when you report a deploy from the pane.
Sync the Windows copy from WSL after a change:
rsync -a --delete --exclude .cache --exclude '.claude-plugin/types' --exclude tsconfig.json \
app/mods/workorder-pane/ /mnt/c/Users/<you>/.claude/mods/workorder-pane/
The MCP server it talks to
keygrip-workorder → https://app.keygrip.ai/client/mcp with a project-scoped kg_client_ key
(claude mcp add --transport http keygrip-workorder <url> -H "Authorization: Bearer kg_client_…").
The server name is what $.mcp.call is given; the desktop app needs the same server in its
.claude.json. Minting the key: project settings in the app (issue #1071).
How the preview is made
Nothing runs on the laptop. kg_workorder_preview(optimization_id, mode, slice, format, scale)
on the client MCP server (and the same tool on the internal /mcp) calls
GET /api/v1/optimizations/{id}/preview, which content/page_preview.py serves: the app
image's own Chromium loads the run's page_url, and in proposed mode runs the work order's
BodyEdits against the live DOM by the same rules a person follows in the CMS editor (find by
meaning, a section runs to the next heading of the same level, edits apply in order), then
screenshots the page in 1280×1600 slices. One call returns one slice as an image block plus a
JSON block (slices, height, applied per edit, view_url). Renders are cached ten
minutes per (run, version, mode) in the web process. The web twin is
…/studio/runs/<id>/preview/?mode=proposed, which stacks every slice and lists each edit's
outcome; that is the view_url the desktop pane links to.
Pixels in the terminal still need kitty ≥ 0.28 or Ghostty (see docs/claude-code-mods.md).
Developing
claude plugin validate app/mods/workorder-pane # manifest + what the module hooks/calls
claude plugin test app/mods/workorder-pane # hooks/*.test.tsx against the engine
The parser (hooks/parse.ts) keys on the document's headings; hooks/fixture.ts is a compact
work order in that shape, modelled on optimization 502. Rules the validator enforces that cost a
round each: $ may only be passed to a top-level function declaration; atom() references need
literal plugin strings; a Button takes one string child or label; a fetch must start from a
command or button handler, never from the render hook (a write from the draw's async chain is
dropped silently).
Not built
- Structured JSON from the server beside the markdown, so the heading parser can go.
- A
proposedrender for a net-new page (no live URL yet): render the body in Keygrip's own preview template instead. - A shared render cache (R2 or Postgres) if the per-process one proves too cold across the two web boxes.
