TrendShift 딥다이브 · 2026-07-01

Herdr 플러그인 예제 딥다이브
— "SDK 없이" 터미널 에이전트를 4개 언어로 확장하기

herdr-plugin-examples는 요즘 뜨는 터미널 도구 herdr (AI 코딩 에이전트를 위한 터미널 워크스페이스 매니저 — tmux처럼 창을 쪼개되, 각 pane에서 코딩 에이전트를 굴리고 그 상태를 추적한다)공식 예제 플러그인 모음(쿡북)이다. 서브디렉토리 4개가 곧 독립 플러그인 4개이고, 저마다 다른 언어(JavaScript · Lua · Bash · Rust)로, 다른 확장 지점(이벤트 훅 · 액션 · pane · 링크 핸들러 · 빌드)을 하나씩 시연한다. 이 저장소가 가르치는 핵심은 코드 그 자체가 아니라 "herdr 플러그인엔 전용 SDK가 없다 — herdr의 CLI 전체가 곧 플러그인 API다"라는 설계 철학이다. 플러그인은 herdr-plugin.toml 매니페스트 한 장 + herdr가 실행할 수 있는 아무 argv 명령이면 된다. (저장소: ogulcancelik/herdr-plugin-examples · Polyglot(JS·Lua·Bash·Rust) · 예제 플러그인 4종 · 별도 LICENSE 미표기(as-is) · herdr 0.7.0+ 필요 · 호스트 herdr는 Rust·AGPL-3.0-or-later)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이 저장소가 대체 무엇인가.

핵심 메시지

"플러그인은 SDK 연동이 아니다 — 매니페스트 한 장 + 실행 가능한 스크립트 한 개다."

herdr는 무대(설치·매니페스트 검증·키바인딩·pane·이벤트·컨텍스트 주입·로그)를 깔아준다. 플러그인은 그 무대 위에서 자기가 편한 언어로 배우만 서면 된다 — herdr는 플러그인을 그냥 하나의 프로그램으로 실행하고, 플러그인은 더 많은 일이 필요하면 herdr ... 명령을 되불러 처리한다.

비유하면: herdr = 콘센트가 표준 규격인 전기 시스템, 플러그인 = 그 콘센트에 꽂는 아무 가전제품. 드라이어든 충전기든(=JS든 Rust든) 플러그 모양(=herdr-plugin.toml)만 맞으면 꽂힌다. 전기회사(herdr)는 제품 내부를 몰라도 되고, 제품은 전기 규격만 지키면 된다.

README의 자기소개는 짧다: "Small example plugins for Herdr. Each subdirectory is a standalone plugin with its own herdr-plugin.toml." — "herdr용 작은 예제 플러그인들. 각 하위 폴더가 자기 매니페스트를 가진 독립 플러그인이다." 즉 이건 라이브러리도, 프레임워크도 아니고 "복사해서 시작하라"고 만든 참고용 레시피 모음이다. README도 못 박는다 — "as-is로 제공되고 적극적으로 유지보수하지 않는다. 이 저장소에 의존하지 말고 필요한 걸 네 플러그인으로 복사해가라."

herdr 본체(별도로 이미 딥다이브한 herdr-deep-dive 참고)가 "여러 코딩 에이전트를 터미널 pane에 늘어놓고 지휘하는 멀티플렉서"라면, 이 저장소는 그 확장 표면(extension surface)을 어떻게 두드리는지 보여주는 4개의 미니 튜토리얼이다. 그래서 이 딥다이브는 herdr 본체의 내부 구조가 아니라 "herdr 플러그인을 어떻게 만드는가"에 초점을 맞춘다.

용어
터미널 멀티플렉서 / 워크스페이스 매니저 (Terminal Multiplexer)
하나의 터미널 창을 여러 개의 독립된 pane(칸)으로 쪼개, 각 칸에서 서로 다른 프로그램을 동시에 돌리는 도구. tmux·screen이 원조다. herdr는 여기에 "각 pane이 지금 어떤 AI 코딩 에이전트를 돌리는 중이고 그 에이전트가 작업 중인지·끝났는지(agent status)"를 추적하는 기능을 더한, AI 코딩 에이전트 전용 멀티플렉서다. 플러그인은 바로 이 pane·이벤트·에이전트 상태를 소재로 동작한다.

2왜 주목받는가

트렌딩 이유 · 다른 플러그인 시스템(VS Code·tmux) 대비 배울 점.

직접적 이유는 herdr 본체가 지금 TrendShift 상위권(Daily 3위)에서 급상승 중이고, 이 저장소가 그 생태계의 공식 예제라는 점이다. 하지만 초보자가 여기서 진짜 얻어갈 건 따로 있다 — "확장 시스템(plugin system)을 어떻게 가볍게(lean) 설계하는가"의 교과서다. herdr는 무거운 SDK, 언어별 바인딩, 별도 격리 런타임을 만들지 않았다. 대신 "매니페스트 + 아무 프로세스 + CLI로 되부르기" 세 조각만으로 확장성을 확보했다.

다른 플러그인 시스템과 무엇이 다른가

비교 축VS Code 확장 / tmux 플러그인herdr 플러그인
API 표면VS Code는 거대한 전용 JS API 표면. tmux는 tmux 명령·옵션 문법herdr CLI 전체가 곧 API — 새 API를 따로 안 배운다
언어VS Code = JS/TS 강제. tmux = 셸 중심argv로 실행되는 아무 언어(JS·Lua·Bash·Rust·Go·Python…)
선언 방식package.json contributes + 런타임 등록 API 혼재전부 매니페스트에 선언(런타임 등록 API 없음, v1)
호스트 호출인프로세스 JS 객체(vscode.*)별도 프로세스 → HERDR_BIN_PATH로 CLI 콜백(소켓/파이프 추상화)
배포전용 마켓플레이스에 패키지 업로드GitHub 저장소 그대로 + 토픽 herdr-plugin 태그
흔한 함정 — 확장 시스템을 무겁게 만들기
"우리 전용 SDK를 만들자" → 언어 강제 + 버전 지옥 + 학습 곡선

많은 도구가 확장성을 위해 전용 SDK/플러그인 API를 만든다. 그러면 (1) 플러그인 작성자는 그 SDK 언어에 묶이고, (2) 호스트가 바뀔 때마다 SDK 버전을 맞춰야 하고, (3) 사용자는 그 API를 새로 배워야 한다. 표면이 커질수록 유지보수·문서화 부담이 눈덩이처럼 커진다.

herdr의 해결 — "이미 있는 CLI를 API로 재사용"
플러그인 = 매니페스트 + argv 프로세스, 호출은 herdr CLI로

herdr 공식 문서의 표현이 핵심을 찌른다: "A plugin is not an SDK integration." 별도 SDK가 없으니 작성자는 아무 언어나 쓰고, 호출은 사용자가 직접 치는 herdr ... 명령을 그대로 쓴다. 이미 존재하고 문서화된 CLI를 확장 API로 재활용해, 새 표면을 거의 안 늘리면서 확장성을 얻는다.

이런 상황을 상상해봐

회사에 새 협력업체가 들어올 때마다 "우리 전용 사내 시스템 사용법"을 처음부터 교육시키면 느리고 비싸다. 대신 "그냥 이메일로 요청서 보내세요"라고 하면 어떤 업체든(=어떤 언어든) 이미 아는 방식(=herdr CLI)으로 바로 일할 수 있다. herdr 플러그인이 딱 그 방식이다 — 이미 모두가 아는 통로(CLI)를 확장 창구로 쓴다.

3기술 스택 전체 지도

폴리글랏(polyglot) — 매니페스트는 하나, 구현 언어는 예제마다 다르다.

이 저장소의 "스택"은 특이하다. 공통 규격은 TOML 매니페스트 하나뿐이고, 나머지는 예제마다 언어가 다르다. 그게 바로 요점 — 폴리글랏 (polyglot): 여러 프로그래밍 언어가 한 시스템 안에 공존하는 것이 herdr 플러그인의 자랑이다.

레이어기술역할
매니페스트 포맷TOML (herdr-plugin.toml)모든 플러그인의 공통 계약. 메타데이터 + 확장 표면 선언
예제 1 · 이벤트JavaScript / Node.js 18+ (npm 의존성 0)에이전트 상태 변화 시 텔레그램 알림. 내장 fetch·fs만 사용
예제 2 · 레이아웃Lua 5.4os.execute/io.popen으로 herdr를 되불러 pane 3분할
예제 3 · 링크Bash + GitHub CLI(gh)클릭한 GitHub 이슈/PR 링크를 옆 pane에서 미리보기
예제 4 · 빌드Rust (edition 2021, 크레이트 의존성 0)설치 시 컴파일 → Git 트리 깨끗한지 검사
호스트(host)herdr 0.7.1 (Rust, AGPL-3.0-or-later)플러그인을 OS 프로세스로 spawn + 환경변수 주입 + 로그
콜백 채널HERDR_BIN_PATH(CLI) · HERDR_SOCKET_PATH(소켓)플러그인 → herdr 되부르기. 소켓은 Unix 소켓 / Windows 명명 파이프
마켓플레이스Cloudflare Worker(TS) + R2 + KV, cron 30분토픽 herdr-plugin 붙은 공개 GitHub 저장소를 자동 인덱싱
용어
매니페스트 (Manifest)
프로그램이 "나는 이런 것들을 제공한다"고 선언해 두는 설정 파일. herdr는 실행 전 이 herdr-plugin.toml을 읽어 어떤 액션·이벤트·pane·링크 핸들러가 있는지 파악하고 검증한다. 비유하면 식당 입구의 메뉴판 — 손님(herdr)은 주방(플러그인 코드)에 들어가지 않고 메뉴판만 보고 뭘 주문할 수 있는지 안다.
용어
TOML (Tom's Obvious Minimal Language)
사람이 읽기 쉬운 설정 파일 포맷. key = "값"[[섹션]](배열 테이블)로 구성된다. herdr 매니페스트에서 [[actions]]를 여러 번 쓰면 액션이 여러 개 있다는 뜻이다. JSON보다 주석·가독성이 좋아 설정 파일에 즐겨 쓰인다.

4아키텍처 심화 분석

"herdr가 프로세스를 띄우고, 플러그인은 CLI로 되부른다" — 양방향 계약.

herdr 플러그인 모델은 딱 두 방향의 흐름으로 요약된다. 내려오는 방향: herdr가 매니페스트를 읽고, 때가 되면 플러그인 명령을 별도 OS 프로세스로 실행하며 환경변수로 맥락을 넣어준다. 올라가는 방향: 플러그인이 더 많은 일이 필요하면 $HERDR_BIN_PATH로 herdr CLI를 되불러 pane을 만들거나 제목을 바꾼다. 이 그림 한 장이 전부다.

┌──────────────────────────────────────────────────────────┐ │ herdr 호스트 (Rust) — 터미널 워크스페이스·pane·에이전트 │ │ 설치/링크 → 매니페스트 검증 → 키바인딩 등록 → 이벤트 감시 │ └───────────────┬──────────────────────────────────────────┘ │ ① 프로세스 spawn (argv 배열, 셸 비경유) │ ② 환경변수 주입 │ HERDR_BIN_PATH · HERDR_PLUGIN_CONTEXT_JSON │ HERDR_PLUGIN_CONFIG_DIR · _STATE_DIR · _PANE_ID … ▼ ┌───────────────────────────┐ │ 플러그인 프로세스 │ ← 아무 언어 (JS·Lua·Bash·Rust) │ env 읽기 → 자기 로직 수행 │ └───────────┬───────────────┘ │ ③ 필요하면 herdr CLI로 되부름 ▼ "$HERDR_BIN_PATH" pane split / rename / run … ┌───────────────────────────┐ │ herdr 호스트가 반영 │ → pane 생성·이름변경·명령 실행 └───────────────────────────┘

매니페스트가 두드릴 수 있는 5개의 확장 표면

플러그인이 herdr에 개입하는 지점은 매니페스트에 선언하는 5종이 전부다(런타임에 코드로 등록하는 방식은 v1에 없다 — 전부 선언형). 이 저장소의 예제 4개가 이들을 골고루 시연한다.

표면매니페스트 키언제 실행되나 (어느 예제)
액션[[actions]]사용자가 키바인딩·CLI·메뉴로 부를 때 (전 예제)
이벤트 훅[[events]]herdr에 특정 사건이 일어날 때 자동 (telegram-notify)
pane[[panes]]플러그인이 여는 전용 pane의 진입점 (github-link-preview)
링크 핸들러[[link_handlers]]터미널의 URL을 Ctrl+클릭할 때 (github-link-preview)
빌드[[build]]plugin install 시 1회 (rust-release-check)
설계 패턴
"CLI is the API" — 통합 CLI를 확장 API로 재사용
herdr는 플러그인 전용 API 세트를 따로 두지 않고, 사용자가 터미널에서 직접 치는 그 herdr 명령 전체를 플러그인도 쓰게 한다. 플러그인은 HERDR_BIN_PATH가 가리키는 herdr 바이너리를 자식 프로세스로 호출한다. 덕분에 (1) 새 API 학습이 0이고, (2) Unix 소켓/Windows 명명 파이프 같은 OS별 통신 차이를 herdr CLI가 흡수해준다. 원한다면 HERDR_SOCKET_PATH로 직접 JSON 요청을 보낼 수도 있지만, 이식성을 위해 CLI 경유가 권장된다.

흐름 한 줄기 끝까지 따라가기 — "에이전트가 끝나면 텔레그램 알림이 가기까지"

가장 대표적인 예제(agent-telegram-notify, JavaScript)로, 이벤트 하나가 알림이 되어 날아가는 전 과정을 추적해보자.

① 에이전트 상태 변화 어떤 pane 안의 코딩 에이전트가 작업을 끝냄 → 상태 = "done" → herdr가 "pane.agent_status_changed" 이벤트를 발생 ② herdr가 이벤트 훅 실행 매니페스트: [[events]] on = "pane.agent_status_changed" command = ["node", "notify.mjs"] → herdr가 notify.mjs 를 새 프로세스로 spawn → 주입: HERDR_PLUGIN_EVENT_JSON / _CONTEXT_JSON / _CONFIG_DIR … ③ notify.mjs 의 판단 (전부 로컬) state 파일(enabled) 확인 → 꺼져 있으면 즉시 exit 0 HERDR_PLUGIN_CONFIG_DIR/.env 에서 봇 토큰·chat_id 로드 event.data.agent_status 가 "done" | "blocked" 인지 확인 ④ 알림 전송 (npm 의존성 0) fetch("https://api.telegram.org/bot<token>/sendMessage") → "✅ Claude is done\nmyworkspace · feature-x"

주목할 점: notify.mjs는 외부 npm 패키지를 하나도 안 쓴다. HTTP 요청은 Node 18+에 내장된 fetch, 파일 읽기는 내장 fs, .env 파싱조차 직접 짠 20줄짜리 로더(lib.mjs)로 해결한다. "플러그인은 가볍게, 의존성은 최소로"라는 이 저장소 전체의 정신이 여기 압축돼 있다.

또 하나의 흐름 — Lua 플러그인이 herdr를 되불러 pane을 조립

dev-layout-bootstrap(Lua)은 "올라가는 방향"의 좋은 예다. 플러그인이 herdr CLI를 반복 호출해 개발용 3분할 레이아웃을 만든다. 핵심은 herdr 명령의 JSON 출력에서 새로 생긴 pane의 id를 파싱해, 그 id를 다음 명령의 대상으로 넘기는 부분이다.

-- setup.lua (핵심만) : herdr를 CLI로 되불러 pane을 쪼갠다
local herdr_bin = os.getenv("HERDR_BIN_PATH") or "herdr"
local root_pane = os.getenv("HERDR_PANE_ID")   -- 현재 pane id

herdr("pane", "rename", root_pane, "editor")   -- 현재 칸 이름을 editor로

local files_json = herdr_capture(              -- 오른쪽으로 분할, 출력을 캡처
  "pane","split",root_pane,"--direction","right","--ratio","0.58","--no-focus")
local files_pane = pane_id_from_json(files_json) -- {"pane_id":"..."} 에서 id 추출

herdr("pane","run",files_pane,"ls -la")        -- 새 칸에서 명령 실행

이 프로젝트에서 "정상"인 모양새: 플러그인은 herdr 상태를 직접 만지지 못한다. 대신 herdr 명령을 실행 → 결과 JSON을 받아 → 다음 명령의 인자로 이어붙이는 "CLI 파이프라인" 패턴이 herdr 플러그인의 관용구다. 셸 인젝션을 막으려고 Lua 예제는 모든 인자를 shell_quote로 감싼다.

설계 패턴
argv 배열 — 셸을 거치지 않는다 (No Shell Expansion)
매니페스트의 command는 문자열이 아니라 ["node","notify.mjs"]처럼 배열(argv)이다. herdr는 이걸 셸(sh -c)에 넘기지 않고 프로그램을 직접 실행한다. 그래서 *·$VAR·; 같은 셸 특수문자가 확장되지 않는다 — 파일명에 공백이나 특수문자가 있어도 안전하고, 매니페스트를 통한 명령어 인젝션이 원천 차단된다. 셸 기능이 필요하면 명령이 스스로 셸을 띄워야 한다(예: ["bash","open.sh"]).
설계 패턴
설정·상태·소스의 3-디렉토리 분리
herdr는 플러그인마다 세 개의 폴더를 구분해 준다. HERDR_PLUGIN_ROOT(설치된 소스 — GitHub 설치본은 관리되는 체크아웃이라 여기 쓰면 안 됨), HERDR_PLUGIN_CONFIG_DIR(사용자가 편집하는 .env 등 설정), HERDR_PLUGIN_STATE_DIR(런타임 상태 — telegram 예제의 on/off 토글 파일). "코드는 갈아끼워도 되지만 사용자 설정·상태는 살아남아야 한다"는 관심사 분리를 폴더 수준에서 강제한다.

5디렉토리 구조 해부

폴더 하나 = 독립 플러그인 하나. 각자 다른 언어·다른 표면.

herdr-plugin-examples/ ├── README.md # 4개 예제 소개 + install/link 방법 ├── .gitignore # .env, node_modules, target/ 제외 │ ├── agent-telegram-notify/ # [JavaScript] 이벤트 훅 + 액션 │ ├── herdr-plugin.toml # events(agent_status) + actions(toggle/on/off) │ ├── notify.mjs # 이벤트 → 텔레그램 전송 (fetch, 의존성 0) │ ├── toggle.mjs # on/off 상태 파일 토글 + 터미널 제목 표시 │ ├── lib.mjs # .env 파서·상태 파일·herdr CLI 호출 헬퍼 │ └── .env.example # TELEGRAM_BOT_TOKEN / CHAT_ID 템플릿 │ ├── dev-layout-bootstrap/ # [Lua] 액션 — pane 3분할 레이아웃 │ ├── herdr-plugin.toml # actions(setup) │ └── setup.lua # herdr pane split/rename/run 반복 호출 │ ├── github-link-preview/ # [Bash] 링크 핸들러 + pane │ ├── herdr-plugin.toml # actions + panes + link_handlers(정규식) │ ├── open.sh # 클릭된 URL → herdr plugin pane open │ └── preview.sh # 옆 pane에서 gh issue/pr view 실행 │ └── rust-release-check/ # [Rust] 빌드 스텝 + 액션 ├── herdr-plugin.toml # build(cargo build --release) + actions(check) ├── Cargo.toml / Cargo.lock # 크레이트 의존성 0 (std만) └── src/main.rs # 컨텍스트 JSON 읽고 git status 검사
예제 (폴더)언어 · 시연하는 확장 표면
agent-telegram-notifyJavaScript[[events]] 이벤트 훅 + [[actions]] 토글. 설정/상태 디렉토리, 의존성 0 알림
dev-layout-bootstrapLua[[actions]] 하나로 herdr CLI를 되불러 pane 3분할 조립
github-link-previewBash[[link_handlers]] 정규식 URL 라우팅 + [[panes]] 미리보기 pane + gh
rust-release-checkRust[[build]] 설치 시 컴파일 + [[actions]]. 손수 짠 JSON 파서(의존성 0)
주의 — 이 저장소에 "의존"하지 말 것
as-is 제공 · 유지보수 안 함 · 별도 LICENSE 파일 없음

README가 직접 당부한다: "reference examples… provided as-is and are not actively maintained. Copy what you need into your own plugin rather than depending on this repo." 즉 이건 학습·복사용 레시피지 라이브러리가 아니다. 또한 저장소에 별도 LICENSE 파일이 없으므로, 코드를 가져다 쓸 땐 herdr 본체(AGPL-3.0-or-later 계열)와 원저자 의도를 확인하는 게 안전하다. plugin install 대신 plugin link로 로컬에서 뜯어보며 배우는 걸 권한다.

6학습 포인트 (기술별)

이 저장소 4개 예제로 배울 수 있는 것 + 실습 아이디어.

6-1. 매니페스트로 "확장 표면"을 선언하기

herdr 플러그인의 첫 배움은 코드가 아니라 선언(declaration)이다. 무엇을 언제 실행할지는 전부 herdr-plugin.toml에 적는다. 같은 스크립트라도 [[actions]]로 걸면 "사용자가 부를 때", [[events]]로 걸면 "사건이 일어날 때" 실행된다.

# agent-telegram-notify/herdr-plugin.toml (발췌)
[[actions]]                         # 사용자가 부르는 명령
id = "toggle"
title = "Toggle Telegram notify"
command = ["node", "toggle.mjs"]

[[events]]                          # herdr 사건에 자동 반응
on = "pane.agent_status_changed"
command = ["node", "notify.mjs"]
실습 아이디어

빈 폴더에 herdr-plugin.toml 하나와 echo hello만 하는 스크립트를 만들고, [[actions]]로 선언한 뒤 herdr plugin link .herdr plugin action invoke ...로 불러보자. "매니페스트에 선언 → herdr가 실행"의 왕복을 몸으로 익히는 가장 빠른 길이다.

6-2. 이벤트 훅 — herdr가 넣어주는 컨텍스트 JSON 읽기

이벤트 훅으로 실행된 스크립트는 "무슨 일이 일어났는지"를 환경변수 HERDR_PLUGIN_EVENT_JSON·HERDR_PLUGIN_CONTEXT_JSON으로 전달받는다. notify.mjs는 여기서 에이전트 상태를 꺼내 필터링한다.

// notify.mjs (핵심 판단부)
const event = JSON.parse(process.env.HERDR_PLUGIN_EVENT_JSON ?? "{}");
const status = event?.data?.agent_status;      // "done" | "blocked" | ...
if (!["done", "blocked"].includes(status)) {
  process.exit(0);                             // 관심 없는 상태면 조용히 종료
}
// 여기 도달하면 done/blocked → 텔레그램 전송
실습 아이디어

notify.mjs를 복사해 텔레그램 대신 console.error(JSON.stringify(event, null, 2))만 찍는 디버그 훅으로 바꿔보자. 에이전트를 돌렸다 멈춰 보면서, herdr가 실제로 어떤 필드를 event.data에 담아 주는지 눈으로 확인하는 게 확장 개발의 출발점이다.

6-3. 설정·상태 분리 + 의존성 0 유지 (lib.mjs)

lib.mjs는 이 저장소에서 가장 교육적인 파일이다. 외부 dotenv 패키지 없이 .env를 직접 파싱하고, on/off 토글 상태를 HERDR_PLUGIN_STATE_DIR 밑 파일에 저장한다. "설정은 CONFIG_DIR, 상태는 STATE_DIR, 코드는 ROOT"라는 3분리를 코드로 실천한다 — GitHub 설치본은 소스가 통째로 교체될 수 있으니 사용자 데이터를 소스 폴더에 두면 안 되기 때문이다.

// lib.mjs — 상태 파일 경로는 STATE_DIR 우선
export function modePath() {
  if (process.env.HERDR_PLUGIN_STATE_DIR)
    return join(process.env.HERDR_PLUGIN_STATE_DIR, "enabled");
  // STATE_DIR이 없던 옛 herdr용 폴백(XDG_STATE_HOME …)
}
// .env는 CONFIG_DIR/.env → 로컬 .env 순으로 직접 파싱 (dotenv 패키지 X)
실습 아이디어

echo $HERDR_PLUGIN_CONFIG_DIR가 어디를 가리키는지 herdr plugin config-dir <id>로 확인하고, 그 안에 .env를 넣었을 때와 소스 폴더에 넣었을 때 동작 차이를 관찰해보자. "왜 설정을 소스와 분리해야 하는가"를 실감하게 된다.

6-4. Lua로 herdr를 되불러 pane 조립하기

setup.lua의 배움은 "herdr 명령의 출력(JSON)에서 새 pane id를 뽑아 다음 명령에 넘기는" 파이프라인이다(4장에서 본 그 패턴). 표준 라이브러리(os.execute/io.popen)만으로 외부 프로세스를 부르고, 정규식으로 pane_id를 긁는다. 셸 인젝션 방지를 위해 모든 인자를 shell_quote로 감싸는 습관도 함께 배운다.

실습 아이디어

setup.lua를 복사해 pane 개수·분할 방향·각 pane에서 실행할 명령을 자신의 작업 흐름에 맞게 바꿔보자(예: editor / test-watch / logs 3분할). herdr CLI의 pane split --ratio·--direction 옵션을 바꿔가며 레이아웃 감각을 익힌다.

6-5. Bash 링크 핸들러 + 전용 pane 열기

github-link-preview는 한 플러그인에 표면 3개(actions+panes+link_handlers)를 엮는다. 매니페스트의 정규식이 클릭된 URL과 매칭되면 액션이 뜨고, 액션은 herdr plugin pane open으로 옆 pane을 열며 --env로 URL을 그 pane에 전달한다.

# herdr-plugin.toml — 정규식으로 GitHub 이슈/PR URL만 가로챈다
[[link_handlers]]
pattern = "^https://github\\.com/[^/]+/[^/]+/(issues|pull)/[0-9]+/?$"
action  = "open"

# open.sh — 클릭된 URL을 새 pane으로 넘긴다
exec "$herdr_bin" plugin pane open \
  --plugin examples.github-link-preview --entrypoint preview \
  --placement split --direction right \
  --env "GITHUB_URL=$url" --focus     # preview.sh가 이 URL로 gh 실행
실습 아이디어

정규식만 Jira(browse/PROJ-123)나 사내 이슈 트래커 URL 패턴으로 바꾸고, preview.shgh 호출을 해당 CLI(jira issue view 등)로 교체해보자. "터미널 링크 클릭 → 옆 pane 미리보기"라는 워크플로를 자신의 도구로 재현할 수 있다.

6-6. Rust — 설치 시 컴파일 + 손수 짠 JSON 파서(의존성 0)

rust-release-check[[build]] 표면을 배우는 예제다. plugin install 시 herdr가 cargo build --release를 먼저 돌려 바이너리를 만든 뒤 등록한다. 컴파일 언어도 "매니페스트 + argv"라는 같은 계약에 자연스럽게 들어온다는 걸 보여준다. 놀랍게도 크레이트 의존성이 0serde_json조차 안 쓰고 컨텍스트 JSON에서 필요한 필드 하나만 문자열 스캔으로 뽑는다.

// main.rs (개념 요약) — 표준 라이브러리만 사용
let ctx  = env::var("HERDR_PLUGIN_CONTEXT_JSON").unwrap_or_default();
let repo = json_string_field(&ctx, "focused_pane_cwd")   // 직접 짠 파서
    .or_else(|| json_string_field(&ctx, "workspace_cwd"));
// git -C {repo} status --short 를 실행해 트리가 깨끗한지 검사
함정 — link 할 땐 빌드가 자동으로 안 돈다
[[build]]는 install 때만 실행, plugin link 땐 직접 빌드해야 함

공식 문서와 README가 함께 경고하는 지점이다. herdr plugin install[[build]]를 자동 실행하지만, 로컬 개발용 herdr plugin link빌드 명령을 돌리지 않는다. 그래서 Rust 예제를 link로 붙일 땐 반드시 먼저 cargo build --release를 손으로 돌려야 한다. 안 그러면 액션이 가리키는 ./target/release/... 바이너리가 없어 실패한다.

실습 아이디어

json_string_field가 읽는 필드를 focused_pane_cwd 대신 다른 컨텍스트 필드로 바꿔, "현재 브랜치가 main이면 경고" 같은 검사를 추가해보자. 컴파일 플러그인이 herdr 컨텍스트를 어떻게 소비하는지, 그리고 왜 작은 도구는 의존성 0이 유지보수에 유리한지 함께 체감할 수 있다.

7시스템 요구사항

호스트 herdr 하나 + 예제별 런타임. 전부 가볍다.

항목요구사항
호스트(필수)herdr 0.7.0 이상 (매니페스트 min_herdr_version = "0.7.0"). 일부 기능(plugin config-dir)은 더 최신 빌드 필요
예제1 (telegram)Node.js 18+ · 외부 npm 패키지 0 · 텔레그램 봇 토큰+chat_id · 플랫폼 linux/macos/windows
예제2 (layout)Lua 5.4+ (lua 명령) · 플랫폼 linux/macos
예제3 (link)Bash · GitHub CLI gh(gh auth login 인증) · 플랫폼 linux/macos
예제4 (rust)cargo(Rust 툴체인) + git · 플랫폼 linux/macos
하드웨어일반 개발 노트북으로 충분. 무거운 연산 없음 — 플러그인은 짧게 떴다 지는 프로세스
네트워크telegram 예제만 외부 통신(텔레그램 API). 나머지는 로컬
정리하면

진입 장벽은 "herdr를 깔 수 있는가" 하나로 수렴한다. 그 위에서 각 예제가 요구하는 건 이미 개발자 컴퓨터에 흔한 런타임(Node·Bash·Lua·cargo)뿐이다. herdr가 툴체인을 대신 설치해주지는 않으니(빌드 실패는 보고만 함), 예제를 돌리기 전 해당 언어 런타임이 PATH에 있는지만 확인하면 된다.

8직접 해볼 수 있는 실습 과제

난이도별 6단계. herdr 0.7+ 설치 후, 신뢰할 수 있는 코드만 install 할 것.

실습 1

dev-layout-bootstrap을 link해서 pane 3분할 만들기 난이도 ★☆☆ 입문

저장소를 클론하고 herdr plugin link ./dev-layout-bootstrapherdr plugin action invoke setup --plugin examples.dev-layout-bootstrap을 실행. 현재 pane이 editor/files/tasks 3칸으로 갈라지는지 확인한다. 목표: "액션 → herdr가 pane을 조립"의 결과를 눈으로 보기.

실습 2

agent-telegram-notify로 실제 알림 받기 난이도 ★☆☆ 입문

@BotFather로 봇 토큰을 만들고, herdr plugin config-dir가 알려준 폴더에 .env를 채운 뒤 link. 코딩 에이전트를 하나 돌렸다 끝내(done) 텔레그램 메시지가 오는지 확인. 키바인딩 prefix+shift+t로 on/off 토글도 해본다. 목표: 이벤트 훅 + 설정 디렉토리 + 상태 토글을 한 번에 경험.

실습 3

나만의 액션 플러그인 처음부터 만들기 난이도 ★★☆ 중급

새 폴더에 herdr-plugin.toml([[actions]] 1개)과 스크립트 하나를 만들어, HERDR_BIN_PATHherdr workspace list를 호출해 그 결과를 출력하는 플러그인을 작성. plugin linkaction invoke로 동작 확인. 목표: "CLI is the API"를 직접 구현 — 플러그인이 herdr를 되부르는 왕복 완성.

실습 4

링크 핸들러를 내 도구로 바꾸기 난이도 ★★☆ 중급

github-link-preview를 복사해 pattern 정규식을 Jira/Linear/사내 트래커 URL로 바꾸고, preview.shgh 호출을 해당 CLI로 교체. 터미널에서 그 URL을 Ctrl+클릭했을 때 옆 pane에 미리보기가 뜨는지 확인. 목표: 정규식 라우팅 + pane 열기 + --env 전달을 자기 워크플로로 재현.

실습 5

다른 이벤트에 반응하는 훅 작성 난이도 ★★★ 고급

[[events]]onpane.output_changed·worktree.created 등 다른 herdr 이벤트로 바꾼 훅을 만들어, 전달되는 HERDR_PLUGIN_EVENT_JSON을 로깅해본다. 목표: herdr가 노출하는 이벤트 카탈로그와 각 이벤트의 페이로드 형태를 스스로 지도화(mapping)하기.

실습 6

Rust 빌드 예제 확장 + 마켓플레이스 배포 흐름 난이도 ★★★ 고급

rust-release-check에 검사 로직(예: 브랜치명·미푸시 커밋 확인)을 추가하고, 자신의 공개 GitHub 저장소에 올린 뒤 토픽 herdr-plugin을 달아 herdr plugin install <owner>/<repo>로 설치되는지 확인. 목표: 컴파일 플러그인의 [[build]] 수명주기 + GitHub 기반 배포/마켓플레이스 인덱싱까지 한 바퀴.

9관련 기술 심화 학습 로드맵

이 예제 저장소를 출발점으로, 5주 코스.

주차주제학습 자료
1주차herdr 기본 + 매니페스트 읽기herdr 공식 plugins 문서 정독 · 예제 4개의 herdr-plugin.toml 비교 · 실습 1
2주차첫 액션 플러그인 (셸/Lua)dev-layout-bootstrap 분석 · plugin link/action invoke 워크플로 · 실습 3
3주차이벤트·컨텍스트 JSON 다루기agent-telegram-notify(notify/toggle/lib.mjs) 정독 · HERDR_PLUGIN_*_JSON · 실습 2·5
4주차pane · 링크 핸들러 · herdr CLI/소켓github-link-preview 분석 · herdr CLI reference·socket API 문서 · 실습 4
5주차컴파일 플러그인 + 배포rust-release-check[[build]] · 마켓플레이스(토픽 herdr-plugin) · 실습 6

10핵심 키워드 사전

본문에 나온 용어 빠른 참조.

용어의미
herdrAI 코딩 에이전트를 위한 터미널 워크스페이스 매니저(멀티플렉서). Rust로 작성, AGPL-3.0-or-later. 이 예제들의 호스트.
Plugin Manifest플러그인이 제공하는 것을 선언한 herdr-plugin.toml. herdr가 실행 전 읽고 검증한다.
Action[[actions]]. 사용자가 키바인딩·CLI·메뉴로 부르는 명령.
Event Hook[[events]]. pane.agent_status_changed 같은 herdr 사건에 자동 실행되는 명령.
Pane 진입점[[panes]]. 플러그인이 여는 전용 pane과 거기서 돌 명령. placement는 overlay/split/tab/zoomed.
Link Handler[[link_handlers]]. 터미널 URL을 Ctrl+클릭하면 정규식으로 가로채 액션으로 라우팅.
Build Command[[build]]. plugin install 때만 1회 실행(예: cargo build). link는 실행 안 함.
argv (셸 비경유)매니페스트 command는 문자열이 아닌 배열. herdr가 셸 없이 직접 실행 → 인젝션·확장 사고 차단.
HERDR_BIN_PATH실행 중인 herdr 바이너리 경로. 플러그인이 herdr를 되부를 때 이식성 있게 쓰는 통로.
HERDR_PLUGIN_CONTEXT_JSON이번 호출의 맥락(워크스페이스·pane·에이전트·클릭 URL 등)을 담은 JSON 환경변수.
CONFIG_DIR / STATE_DIR / ROOT설정(.env) / 런타임 상태 / 소스 코드를 분리한 3개 폴더. 사용자 데이터를 소스와 안 섞기 위함.
plugin link vs installlink=로컬 폴더를 그대로 붙임(개발용, 빌드 X). install=GitHub에서 클론·빌드·등록(배포용).
Marketplace토픽 herdr-plugin 붙은 공개 GitHub 저장소를 30분마다 자동 인덱싱(Cloudflare Worker).
min_herdr_version플러그인이 요구하는 최소 herdr 버전. herdr가 더 낮으면 link/install을 거부한다.

11참고 링크