KeygripGit/claude-mods/tree/main/plugins/workorder-pane
workorder-pane — Keygrip 在 Claude Code 中的 work order
一個在面板中繪製 Keygrip client 部署佇列的 Claude Code 外掛:/workorders 列出 work order,Reader 將每筆拆成 Overview、Fields、Edits、Schema、Verify、Notes 和可在 live/proposed 間切換的伺服器渲染 Preview 分頁。可透過外掛市集、--plugin-dir/CLAUDE_CODE_PLUGIN_DIRS 路徑或 Windows 設定副本安裝;透過 $.http.fetch 使用專案 key 呼叫遠端 client MCP server。
關於這個 mod
workorder-pane — Keygrip work orders inside Claude Code
一個 Claude Code mod(由 function hooks 組成的外掛,見 docs/claude-code-mods.md),把 client 的部署迴圈畫在面板中,而不是塞成工具列裡一整面 Markdown。追蹤 issue #1516。
/workorders # 已連線專案的所有 work order 所在的佇列
/workorders 502 # 直接前往某個 optimization id 的 work order
它會顯示什麼
佇列。 每個來自 kg_workorders 的 work order 一列:id、version、mode、state、keyword。 被封鎖的列會變暗且拒絕開啟(伺服器也會拒絕)。r 重新整理,s 顯示/隱藏已被取代的版本。
Reader(選取一列)。只呼叫一次 kg_fetch_workorder,再依固定標題切分文件。按 1–7 切換分頁,按 b 回到佇列:
| 分頁 | 顯示內容 | | --- | --- | | 1 Overview | mode、準備日期、keyword 目標、「改了什麼」、主視覺 | | 2 Fields | 每個可部署值放在程式碼區塊中,對照 SEO 限制的長度計,超過時變紅;與即時頁面相同時顯示 unchanged,並提供複製按鈕 | | 3 Edits | 每個內文修改都會框起來:why、where、Find 文字和 Replace/Insert HTML,以及複製按鈕;全新 work order 會顯示完整內文 | | 4 Schema | 去掉 script 外層的 JSON-LD,並提供複製按鈕 | | 5 Verify | 發布後步驟以切換鈕顯示,附已勾選數量 | | 6 Notes | 自這個版本後改變的脈絡、建議但未套用、已檢查且刻意不動 | | 7 Preview | 由伺服器上的 kg_workorder_preview 渲染頁面(開啟 work order 的瞬間就開始渲染第一個切片,所以通常一進分頁就有畫面):p 切換 live(目前實際提供的頁面)與 proposed(在瀏覽器套用 work order 修改,每個修改以品牌青色框出,移除內容用紅色虛線標示);n/u 在 1600 px 切片間移動,r 重新渲染。終端機顯示像素(kitty/Ghostty)。桌面/VS Code 顯示放在 Svg 中的半尺寸 JPEG,並連到 app.keygrip.ai 上同一預覽頁面。 |
底部有一個 Live URL 欄位,送出時以 target: manual 呼叫 kg_report_deployed。 空白 URL 會被拒絕。這個 mod 不會寫入其他內容。
安裝(client,以及 monorepo 外的使用者)
應用程式會把這個資料夾提供為 Claude Code 外掛市集(web/mods.py,#1516):GET /mods/marketplace.json 會把 app/mods/ 下每個 mod 列為 archive 來源,GET /mods/<name>-<version>.zip 會在需要時壓縮 mod(結果具決定性、每個 process 會快取,manifest 內有 sha256)。它像 /healthz 一樣公開:client 不需要 GitHub 帳號,機器上也不需要 git。面向 client 的指南是 /mods/(桌面點擊路徑與終端機指令);專案的 Client MCP access box 會印出兩個指令和要貼上的 key:
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,使用專案的 kg_client_ key 作 Bearer header;並在面板中只詢問一次這個 key,存在外掛自己的 $.store 中(在佇列上按 k 可更換)。manifest 刻意不宣告伺服器:從按鈕發出的工具呼叫會被 Auto 模式的權限分類器拒絕(「產生這個動作的請求沒有要求它」),即使是外掛內建的伺服器也在桌面上測過;否則每位 client 都得貼一份 allow list,而 manifest server 又需要透過 /plugin configure 傳入 key。直接 fetch 兩者都不需要——在 2026-10-03、--permission-mode auto 下測得如此。端點是無狀態並回覆 JSON,所以沒有 handshake。每次想讓安裝取得變更,都要在 plugin.json 提升 version:安裝會固定它看到的版本,claude plugin update 只有在數字變動時才會前進,zip URL 也會帶上版本。
⚠️ 透過 --plugin-dir/CLAUDE_CODE_PLUGIN_DIRS 載入的外掛,會覆蓋同名的市集安裝(接著 claude plugin install --config 會回報「failed to load after install」);開發機器上只能選一種。
從 checkout 載入(開發者)
CLI,單一工作階段: claude --plugin-dir app/mods/workorder-pane
CLI 或桌面,永久載入: 在 CLAUDE_CODE_PLUGIN_DIRS 中指定資料夾,可放在 process environment 或 ~/.claude/settings.json 的 env 區塊(這是使用者檔案,不是專案檔案)。
Windows 上的 Claude Desktop 使用自己的引擎和自己的 %USERPROFILE%.claude,因此需要 Windows 路徑。目前可用的副本:
// 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"
] }
}
(市集安裝的 server 是規則中的 plugin_workorder-pane_keygrip-workorder。)allow 清單只有在上述 developer 路徑的 Auto 權限模式才重要;面板的 MCP 呼叫來自按鈕,不是提示,因此 auto-mode 分類器沒有可以比對的內容,會拒絕呼叫。允許的工具會跳過分類器。寫入工具 kg_report_deployed 刻意不在 allow 清單中;要從面板回報部署時,請關閉 Auto 或核准提示。
在 WSL 變更後同步 Windows 副本:
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,使用專案範圍的 kg_client_ key(claude mcp add --transport http keygrip-workorder <url> -H "Authorization: Bearer kg_client_…")。 server 名稱就是 $.mcp.call 使用的名稱;桌面應用程式也需要在自己的 .claude.json 裡有相同 server。建立 key 的位置:應用程式中的專案設定,issue #1071。
Preview 如何產生
筆電上不執行任何內容。client MCP server 上的 kg_workorder_preview(optimization_id, mode, slice, format, scale)(內部 /mcp 也有同一工具)會呼叫 GET /api/v1/optimizations/{id}/preview,由 content/page_preview.py 提供:應用程式自己的 Chromium 載入 run 的 page_url;在 proposed 模式中,依照 CMS 編輯器使用者遵循的同一套規則,把 work order 的 BodyEdit 套到 live DOM(依意義尋找、區段延續到下一個同層級標題,修改依序套用),再以 1280×1600 切片截取頁面。一次呼叫會回傳一個切片的 image block 和一個 JSON block(slices、height、每個 edit 的 applied、view_url)。渲染結果在 web process 中依(run、version、mode)快取 10 分鐘。Web twin 是 …/studio/runs/<id>/preview/?mode=proposed:堆疊所有切片並列出每個修改的結果;這個 view_url 就是桌面面板開啟的頁面。
終端機的像素顯示仍需要 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)依文件標題取鍵;hooks/fixture.ts 是符合這個形狀、以 optimization 502 為模型的精簡 work order。validator 強制的規則每項都會耗掉一輪:$ 只能傳給頂層函式宣告;atom() 參照需要字面 plugin 字串;Button 必須只有一個字串子節點或 label;fetch 必須從 command 或 button handler 開始,不能從 render hook 開始(draw 的非同步鏈裡的寫入會被靜默丟棄)。
尚未建置
- server 旁還沒有結構化 JSON,因此可以移除標題 parser。
- net-new page(尚無 live URL)還沒有 proposed render:改為在 Keygrip 自己的 preview template 中渲染內文。
- 如果每 process 的 render cache 在兩台 web box 間太冷,還沒有共用的 render 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.
