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

workflow-gates

Claude Code 플러그인으로, 워크플로 엔진의 gate를 프롬프트 위 띠에 대화형 행으로 그립니다. 모델이 재현하는 텍스트 메뉴 대신 선택·큐잉·지속 저장·재개를 처리합니다.

leeovery@leeovery

leeovery/portal/tree/main/.claude/skills/workflow-gates

번역 완료

이 mod 소개

workflow-gates

워크플로 엔진의 gate를 프롬프트 위 띠에 그리는 Claude Code mod입니다. 모델이 다시 만들어 내도록 두지 않습니다.

엔진은 자신이 구성한 메뉴 옆에 각 gate를 데이터로 기록합니다. 이 mod는 세션 시작에 자신을 알리고, 엔진이 그 데이터를 수집하게 하며, 데이터를 담은 Bash 결과를 바탕으로 gate를 활성화하고, 모델이 읽는 내용에서 메뉴를 잘라 내고, 트랜스크립트가 스크롤되어도 행을 같은 자리에 그립니다. 연결된 화면이 터미널이 아니면 모든 화면에서 메뉴를 볼 수 있도록 텍스트로 남깁니다. 다만 터미널에 메뉴를 그린 뒤 연결된 화면에는 그 메뉴가 전달되지 않습니다. 어떤 gate의 문장도 mod의 이름을 말하지 않습니다. workflow-start의 설정 단계만 이름을 말하며, Claude Code 터미널 앱에서 mod가 실행될 때까지 세션을 멈춥니다.

행을 클릭하면 답이 프롬프트 상자에 들어가고, 다시 클릭하면 다음 메시지로 전송되어 워크플로가 답으로 읽습니다. 클릭으로 띠가 키보드를 받으면 화살표로 행 사이를 이동하고, Enter나 행 고유 키로 선택하며, 선택한 행에서 Enter를 누르면 전송합니다. 전송한 답은 플러그인 이름 아래로 들어가 모델용 형식으로 감싸지고 트랜스크립트에서는 플러그인의 내용으로 표시됩니다. mod는 보낸 내용을 대화 전용 폴더(아래 참고)에 sent.json으로 남깁니다. 두 번째 플러그인 workflow-gates-rows(../workflow-gates-rows/)는 이 기록을 읽어 행을 질문과 답으로 그립니다. 제출한 프롬프트의 행을 어떤 플러그인도 다시 그릴 수 없기 때문입니다. 답은 입력으로도 보낼 수 있습니다. 프롬프트에서 키와 Enter를 누르거나, 선택 뒤 Esc와 Enter를 누릅니다. 입력만 가능한 행은 Ask, Comment, 범위에 답할 수 있으며 어둡게 표시됩니다. 클릭하면 프롬프트에 입력하라고 안내합니다. 행 아래 footer는 답하는 방법, 프롬프트에 들어 있는 내용, 입력할 위치를 알려 줍니다.

띠의 높이는 Claude Code가 제공하는 행보다 커지지 않으므로 스크롤되지 않습니다. 들어가는 gate는 규칙, 설명과 질문, 행, footer를 모두 보여 줍니다. 들어가지 않는 gate는 규칙·질문·footer를 고정하고 행을 페이지별로 보여 줍니다. 각 페이지의 높이는 같고, 아래에 ↑ previous ↓ next page 1 of 3라는 줄이 있습니다. 어느 쪽이든 클릭하면 페이지가 바뀌고 첫 행에 커서가 놓입니다. 화살표는 페이지를 넘나들며 커서를 옮기고 다음 페이지로 이어 줍니다. 행 고유 키는 어느 페이지에 있는 행이든 선택합니다.

사용자가 시작하지 않은 턴——백그라운드 에이전트의 보고, 알림, 일정——에서는 띠를 그대로 두고 행도 살아 있게 둡니다. 사용자가 턴을 시작하거나 실행 중인 턴에 답하거나 다른 gate를 그리는 턴이 시작되면 띠가 사라집니다. 그런 턴에서 Esc를 눌러도 띠는 그대로입니다. 단, 그 턴이 이미 다른 gate를 렌더링했다면 모델이 그 gate의 중지 지점에서 기다리므로 띠가 비워집니다. Claude가 작업 중 다시 클릭하면 답을 보내지 않고 보류합니다. 행에는 · queued가 표시되고 footer에는 Claude가 끝나면 보낸다고 나옵니다. 큐에 있는 행을 클릭하면 선택 상태로 돌아가고, 다른 행을 클릭하면 그 행을 선택합니다. 턴이 끝날 때 같은 gate가 띠에 남아 있으면 보류한 답을 보냅니다. 다른 gate가 렌더링되었다면 답은 전송하지 않고 버리며 새 gate의 footer가 그 사실을 알립니다. Esc로 턴을 멈췄고 같은 gate가 아직 떠 있다면 답은 선택 상태로 프롬프트 상자에 돌아갑니다. 선택한 gate가 사라지면 답도 프롬프트 상자에서 사라지지만, 사용자가 그곳에서 편집했다면 남습니다. Claude가 작업하는 동안 입력한 답은 Claude Code 자체 큐에 합류합니다.

답이 시작했거나 합류한 턴에서 Esc를 누르면 그 gate를 되돌리고 그 턴이 그린 내용을 버립니다. 해당 턴에서 아직 도구가 실행되지 않았을 때만 가능합니다. 도구가 한 번이라도 실행되면 mod가 읽기와 쓰기를 구분할 수 없으므로 띠는 빈 채로 남습니다. /clear는 띠에서 gate를 제거합니다.

모든 턴이 끝날 때와 대화가 끝날 때 mod는 띠가 보여 주는 것(gate 또는 없음)을 대화 전용 폴더에 저장합니다. 경로는 ~/.config/workflows/conversations/{session-id}/gate.json이며 WORKFLOWS_CONFIG_DIR가 설정되어 있으면 워크플로 시스템 설정 옆에 둡니다. 세션의 작업 디렉터리가 이동해도 세션 ID로 찾고, 중단된 턴 주위에 Claude Code가 쓴 줄은 세지 않은 채 트랜스크립트가 끝난 위치를 기록합니다. claude --resume나 /resume으로 재개한 대화, 재시작이나 mod 파일 재로드 뒤 다시 이어진 대화는 트랜스크립트가 그 위치에서 끝나 있으면 gate를 복원합니다. 선택이나 보류는 복원하지 않습니다. 보류 답은 돌아오지 않는 턴을 기다리기 때문입니다. mod가 로드되지 않은 동안 진행된 대화는 아무것도 복원하지 않습니다. 시작 후 mod를 로드한 세션에는 알림이 없으므로 저장하거나 다시 읽지도 않습니다. 워크플로를 실행하지 않는 대화에는 폴더가 없고 아무것도 보존하지 않습니다. 홈 디렉터리와 WORKFLOWS_CONFIG_DIR 중 어느 것도 지정하지 않은 프로세스의 대화도 마찬가지입니다. 폴더에는 gate 하나만 있고 Claude Code가 대화 트랜스크립트를 삭제하면 폴더도 사라집니다. 따라서 저장된 gate는 대화를 재개할 수 있는 동안만 남습니다. 단, session-end hook이 종료를 보지 못한 대화는 예외입니다. hook을 처음 설치한 세션(Claude Code는 세션 시작 때만 가져옴)이나 충돌한 세션은 폴더를 남깁니다.

엔진은 mod가 꺼져 있거나 없어도 메뉴를 출력합니다. mod가 없는 곳이나 꺼진 곳에서 모델이 읽는 것은 엔진이 쓴 텍스트 메뉴입니다.

이 mod는 워크플로의 일부이며 첫 /workflow-start가 실행 가능한 곳에서 켭니다. 즉, 이 디렉터리가 프로젝트에 설치된 2.1.282 이상의 Claude Code 터미널 앱입니다. Claude Code는 사용자의 설정, 관리 설정 또는 셸에서 CLAUDE_CODE_ENABLE_FUNCTION_HOOKS를 가져오며 프로젝트 설정에서는 가져오지 않습니다. 그래서 터미널 앱에서는 모든 /workflow-start가 사용자의 Claude Code 설정 env에 "1"을 씁니다. CLAUDE_CONFIG_DIR가 있으면 그 안의 settings.json, 없으면 ~/.claude/settings.json입니다. Claude Code는 시작할 때만 설정을 읽으므로 설정을 쓰는 시작은 재시작을 요청하며 끝나고 다음 세션에서 mod를 로드합니다. 터미널 앱의 워크플로는 mod가 실행 중일 때만 동작합니다. flag가 있는데 mod가 실행되지 않거나, 파일을 읽고 쓸 수 없거나, 2.1.282보다 오래된 Claude Code에서 실행되면 시작을 멈추고 이유를 말합니다. 웹, IDE 확장, 다른 진입점, 이 디렉터리가 없는 프로젝트에서는 아무것도 쓰지 않고 텍스트 메뉴로 계속 진행합니다.

Function hooks는 mod가 실행될 수 없는 곳에서도 켤 수 있습니다. 사용자의 설정에 있는 flag는 그것을 읽는 모든 Claude Code 앱과 버전에 닿고, 누구나 자신의 설정이나 셸에서 설정할 수 있습니다. 세션 시작 시 mod는 부트 규칙을 적용합니다. CLAUDE_CODE_ENTRYPOINT가 cli가 아니거나 CLAUDE_CODE_REMOTE가 설정되어 있거나, 버전이 2.1.282보다 오래되었거나 릴리스 버전이 아니면(개발 빌드 포함)아무것도 알리지 않고 그리지도, 보존하지도, 설정하지도 않습니다. 메뉴는 텍스트로 남고 Claude Code는 mod가 없을 때처럼 실행됩니다.

Claude Code에 설정하는 내용

2.1.282 이상의 Claude Code 터미널 앱에서는 모든 세션에 Claude Code의 SendUserMessage 도구를 켭니다(CLAUDE_CODE_PEWTER_OWL_TOOL=true). Claude Code는 세션 시작 직후 도구 목록을 만들기 때문에 스위치가 유효한 순간은 그때뿐입니다. mod는 그런 세션마다 도구를 ToolSearch 뒤에 둡니다. 답은 바뀌지 않으므로 프롬프트 캐시를 소비하지 않습니다. 일반 세션의 도구 목록은 Claude Code 자체가 정합니다.

그곳에서 워크플로를 실행하는 대화에서는 CLAUDE_CODE_THINKING_DISPLAY_UPDATES=false를 설정해 Claude의 사고 한 줄 요약이 출력처럼 인쇄되지 않게 하고, CLAUDE_CODE_SILENT_TURN_REMINDER=false를 설정해 Claude에게 무엇을 하는지 말하라는 재촉을 막습니다. 두 번째 항목은 프로젝트 설정으로 지정할 수 없습니다. Claude Code는 두 값을 요청마다 읽습니다. 엔진을 호출할 때마다 호출한 대화를 표시합니다. 대화 폴더에 Claude Code가 각 명령에 넘긴 세션 ID를 이름으로 한 workflow 파일을 둡니다. mod는 각 Bash 호출 후와 시작 시 자신의 세션 ID로 그 표시를 읽으므로 엔진을 언급하기만 하는 명령은 표시하지 않습니다. 설정이 대체한 값(사용자 값 또는 값 없음)은 프로세스 환경의 WORKFLOWS_HARNESS_REPLACED에 보존됩니다. mod 파일을 다시 로드해도 남고 /clear나 재개 시 정확히 돌려놓습니다. 표시가 있는 대화는 claude --resume, 재시작, 같은 프로세스의 /resume 중 무엇으로 돌아오든 mod가 다음에 따라갈 때 워크플로 값을 되찾습니다. 같은 프로젝트의 일반 대화는 Claude Code 기본값과 사용자의 설정을 그대로 두며 mod는 어느 쪽도 건드리지 않습니다.

작업하기

npm run mod:types       # API 선언을 types/로 가져옴(gitignore 처리)
npm run typecheck:mod   # 해당 선언에 대해 tsc 실행
npm run test:mod        # claude plugin test

선언은 Claude Code 저장소에서 오며 다시 생성할 수 있으므로 커밋하지 않습니다. 첫 typecheck 전에 가져오세요.

Function hooks는 early access입니다. 환경의 CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1, 프로젝트 설정이 아닌 설정 파일에서의 활성화, 또는 계정에서의 활성화가 있을 때만 Claude Code가 이 mod를 로드하며, 테스트 스크립트는 자체 flag를 설정합니다.

설치

먼저 작성자의 README에서 marketplace와 플러그인 이름을 확인하세요. 저장소 구조에 따라 명령어가 달라질 수 있습니다.

claude plugin marketplace add leeovery/portal
claude plugin install workflow-gates
원문 / README

workflow-gates

A Claude Code mod that draws the workflow engine's gates in the band above the prompt instead of leaving the model to reproduce them.

The engine states each gate as data beside the menu it composed. This mod announces itself at the session's start so the engine collects that data, arms the gate off the Bash result that carried it, cuts the menu out of what the model reads, and draws the rows where they stay put while the transcript scrolls; while any screen but the terminal is attached, it leaves the menu as text so every screen shows it, though a screen that attaches after a menu was drawn on the terminal does not get that menu. No gate's prose names the mod; only workflow-start's setup step does, which stops the session until the mod is running in Claude Code's terminal app.

A click on a row puts its answer in the prompt box; a second click on it sends it as the next message, which the workflows read as the answer. Once a click has given the band the keyboard, the arrows move between rows, Enter or a row's own key picks, and Enter on the picked row sends. A sent answer enters under the plugin's name, framed for the model and labelled in the transcript as the plugin's; the mod leaves what it sent in the conversation's own folder (see below) as sent.json. A second plugin, workflow-gates-rows (../workflow-gates-rows/), reads that record to draw the row as the question and the answer, since no plugin can redraw the row of a prompt it submitted. Typing answers too: a key and Enter at the prompt, or Esc then Enter after a pick. Rows only typing can answer — Ask, Comment, a range — draw dim, and a click on one says to type it in the prompt. The footer under the rows says which: how to answer, what is in the prompt, or where to type.

The band is never taller than the rows Claude Code gives it, so it never scrolls. A gate that fits shows whole: a rule, the statement and the question, the rows, the footer. One that does not keeps its rule, question and footer in place and shows its rows a page at a time, every page the same height, over a line reading ↑ previous ↓ next page 1 of 3; a click on either turns the page and puts the cursor on its first row. The arrows carry the cursor across pages, the page following it, and a row's own key picks that row whichever page it is on.

A turn the person did not start — a background agent's report, a notification, a schedule — leaves the band as it is, its rows live. The band comes off when the person starts a turn or replies into a running one, or when a turn draws a different gate over it. Esc on such a turn leaves the band as it is, unless the turn had already rendered a different gate: the model now waits at that gate's stop, so the band empties. A second click while Claude works holds the answer instead of sending it: its row reads · queued, and the footer says it sends when Claude finishes. A click on the queued row takes it back to a pick, and a click on another row picks that one instead. As the turn ends, the held answer sends if the same gate is still on the band; if the turn rendered a different gate, the answer is dropped unsent, and the new gate's footer says so; if Esc stopped the turn with the same gate still up, the answer goes back into the prompt box as a pick. When a pick's gate goes, its answer leaves the prompt box too, unless the person has edited it there. Typing while Claude works joins Claude Code's own queue.

Esc on a turn an answer started or joined puts its gate back, dropping whatever that turn drew, as long as no tool has run in it; once one has, the band stays empty, since the mod cannot tell a read from a write. A /clear takes the gate off the band.

At the end of every turn, and as the conversation ends, the mod keeps what the band shows — the gate, or nothing — in the conversation's own folder, ~/.config/workflows/conversations/{session-id}/gate.json (under WORKFLOWS_CONFIG_DIR where that is set, beside the workflows' system config), found by the session id wherever the session's working directory has moved, and stamped with where the transcript ends, not counting the lines Claude Code writes around an interrupted turn. A conversation resumed with claude --resume or /resume, or picked up again by a restart or a reload of the mod's files, gets its gate back as long as its transcript still ends there, with nothing picked or held — a held answer waits on a turn that does not come back; one that moved on while the mod was not loaded gets nothing. A session the mod was loaded into after it started carries no announcement, so there it keeps and reads back nothing, and a conversation that does not run the workflows has no folder and keeps nothing — nor does one in a process that names neither a home directory nor WORKFLOWS_CONFIG_DIR. The folder holds one gate, and goes once Claude Code has deleted the conversation's transcript, so a kept gate lives as long as its conversation can be resumed, with one exception: a conversation whose end the session-end hook never saw keeps its folder — the session that first installed the hook, which Claude Code picks up only as a session starts, or one that crashed.

The engine emits the menu regardless, so where the mod is off or absent the model reads the text menu the engine wrote.

The mod is part of the workflows, and the first /workflow-start switches it on wherever it can run: Claude Code's terminal app, from 2.1.282, with this directory installed in the project. Claude Code takes CLAUDE_CODE_ENABLE_FUNCTION_HOOKS from the person's own settings, managed settings or the shell, never from a project's settings, so there every /workflow-start makes it "1" in the env of the person's Claude Code settings — settings.json in CLAUDE_CONFIG_DIR where that is set, else ~/.claude/settings.json. Claude Code reads its settings only when it starts, so a start that writes it ends by asking for a restart, and the next session loads the mod. In the terminal app the workflows run only with the mod: a start that finds the flag there with the mod not running, that cannot read or write that file, or that runs on a Claude Code older than 2.1.282 stops and says why. Anywhere else — the web, an IDE extension, another entrypoint, a project without this directory — nothing is written, and the workflows carry on with the text menus.

Function hooks can be on where the mod cannot run — the flag in the person's settings reaches every Claude Code app and version that reads them, and anyone's own settings or shell can set it — so at the session's start the mod applies the boot's rules: where CLAUDE_CODE_ENTRYPOINT is other than cli, CLAUDE_CODE_REMOTE is set, or the version the session reports is older than 2.1.282 or not a release's (a development build among them), it announces nothing, so it draws, keeps and sets nothing — the menus stay text, and Claude Code runs as it would without it.

What it sets in Claude Code

Every session in Claude Code's terminal app, from 2.1.282, starts with Claude Code's SendUserMessage tool switched on (CLAUDE_CODE_PEWTER_OWL_TOOL=true): Claude Code builds its tool list just after the session starts, so that is the only moment the switch counts. The mod keeps the tool behind ToolSearch in every such session, one answer that never changes and so never spends the prompt cache; a plain session's tool list is Claude Code's own.

In a conversation that runs the workflows there, the mod sets CLAUDE_CODE_THINKING_DISPLAY_UPDATES=false, which stops one-line summaries of Claude's thinking printing as if they were output, and CLAUDE_CODE_SILENT_TURN_REMINDER=false, which stops the nudge to say what Claude is doing; project settings cannot set the second. Claude Code reads both per request. Every engine call marks the conversation that made it — a workflow file in its folder, named by the session id Claude Code hands every command — and the mod reads that mark by its own session id after each of the conversation's Bash calls and when it starts, so a command that only mentions the engine marks nothing. What the settings replace, the person's own value or none, is kept in the process's environment (WORKFLOWS_HARNESS_REPLACED), which a reload of the mod's files keeps, and a /clear or a resume puts it back exactly. A marked conversation gets the workflow values back when the mod next follows it, whether claude --resume, a restart or /resume in the same process brings it back. A plain conversation in the same project keeps Claude Code's defaults and the person's own settings: the mod never touches either there.

Working on it

npm run mod:types       # fetch the API declarations into types/ (gitignored)
npm run typecheck:mod   # tsc against those declarations
npm run test:mod        # claude plugin test

The declarations come from the Claude Code repository and are regenerable, so they are not committed. Fetch them before the first typecheck.

Function hooks are early access: Claude Code loads this mod only where they are on — CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 in the environment or in any settings file but a project's, or switched on for the account — and the test script sets the flag for itself.

동명의 다른 작품

비슷한 프로젝트