ClaudeMods
☰
KO
● 0 명 접속 중 · 조회 0 회
후원프로젝트 제출
GitHub 저장소 · 작성자 KeygripGit

workorder-pane — Claude Code 안의 Keygrip work order

Keygrip client 배포 큐를 패널에 그리는 Claude Code 플러그인입니다. /workorders가 work order를 나열하고 Reader는 각 order를 Overview, Fields, Edits, Schema, Verify, Notes, live/proposed 전환이 가능한 서버 렌더링 Preview로 나눕니다. plugin marketplace, --plugin-dir/CLAUDE_CODE_PLUGIN_DIRS 경로 또는 Windows 설정 복사본으로 설치할 수 있으며 $.http.fetch로 프로젝트 key를 사용해 원격 client MCP server를 호출합니다。

KeygripGit@KeygripGit

KeygripGit/claude-mods/tree/main/plugins/workorder-pane

번역 완료

이 mod 소개

workorder-pane — Claude Code 안의 Keygrip work order

Claude Code mod(function hooks 플러그인, docs/claude-code-mods.md 참조)로 client 배포 루프를 도구 줄의 Markdown 벽 대신 패널에 그립니다. issue #1516을 추적합니다.

/workorders          # 연결된 프로젝트가 가진 모든 work order 큐
/workorders 502      # optimization id로 한 order 바로 열기

표시 내용

Queue. kg_workorders의 work order마다 id, version, mode, state, keyword를 한 줄로 표시합니다. 차단된 행은 흐려지고 열 수 없으며 서버도 거부합니다. r은 새로 고침, s는 superseded version 표시를 전환합니다.

Reader는 선택한 행에서 kg_fetch_workorder를 한 번 가져온 뒤 고정 제목으로 문서를 나눕니다. 1–7로 탭을 바꾸고 b로 큐로 돌아갑니다.

| 탭 | 표시 | | --- | --- | | 1 Overview | mode, 준비 날짜, keyword 대상, “무엇이 바뀌었는가”, hero image | | 2 Fields | 배포 가능한 값을 코드 블록에 넣고 SEO 제한과 비교한 길이 미터를 표시하며 초과 시 빨간색입니다. live page와 같으면 unchanged를 표시하고 복사 버튼을 둡니다 | | 3 Edits | 각 본문 수정을 why, where, Find 텍스트, Replace/Insert HTML과 함께 박스로 표시하고 복사 버튼을 둡니다. 새 order는 전체 본문을 보여 줍니다 | | 4 Schema | script wrapper를 벗긴 JSON-LD와 복사 버튼 | | 5 Verify | 게시 후 단계를 토글로 표시하고 체크된 수를 보여 줍니다 | | 6 Notes | 이 version 이후의 context 변화, recommended-not-applied, checked-and-left-alone | | 7 Preview | 서버의 kg_workorder_preview가 페이지를 렌더링합니다. order를 여는 순간 첫 slice 렌더링을 시작하므로 보통 즉시 표시됩니다. p로 live(현재 제공 중인 페이지)와 proposed(브라우저에서 order 수정 적용)를 전환하고, 수정은 브랜드 cyan 테두리, 삭제는 빨간 점선으로 표시합니다. 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 commands)이며 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에 server를 선언하지 않는 것은 의도적입니다. 버튼에서 나온 tool call은 Auto-mode permission classifier가 거부합니다(“이 action을 만든 request가 그것을 요구하지 않았다”). 플러그인 내장 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, 한 세션: 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는 규칙에서 plugin_workorder-pane_keygrip-workorder입니다. allow list는 위 developer path의 Auto permission mode에서만 중요합니다. MCP call은 prompt가 아니라 button에서 오므로 classifier에 맞출 내용이 없어 거부되고 allowed tool은 classifier를 건너뜁니다. 쓰기 도구 kg_report_deployed는 의도적으로 허용하지 않습니다. 패널에서 deploy를 보고하려면 Auto를 끄거나 prompt를 승인하세요.

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에 전달하는 이름이며 Desktop app의 .claude.json에도 같은 server가 필요합니다. key는 앱의 프로젝트 설정에서 만듭니다(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로 페이지를 캡처합니다. 한 번의 호출은 image block 하나와 JSON block 하나(slices, height, 각 edit의 applied, view_url)를 반환합니다. render는 web process에서 (run, version, mode)별 10분 캐시됩니다. Web twin인 …/studio/runs/<id>/preview/?mode=proposed는 모든 slice를 쌓고 각 edit 결과를 나열하며, Desktop pane이 연결하는 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)는 문서 제목을 key로 삼고 hooks/fixture.ts는 optimization 502를 모델로 한 이 형태의 작은 work order입니다. validator가 강제하는 규칙은 매번 한 라운드 비용이 듭니다. $는 top-level function declaration에만 넘길 수 있고 atom() 참조에는 literal plugin string이 필요합니다. Button은 string child 하나 또는 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 사이에서 너무 차가울 때를 위한 공유 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 proposed render 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.

비슷한 프로젝트