스킬·어댑터 모음
pstack-claude는 코딩 에이전트의 작업 순서와 판단 기준을 스킬로 배포하고, 실행 환경마다 다른 도구를 연결하는 프로젝트입니다. 원본은 Cursor용 pstack이고, 이 포트는 Claude Code를 기준으로 지침을 유지하면서 Codex, Pi, GitHub Copilot, Muse 등에 맞는 연결부를 둡니다. OpenCode, Gemini CLI, Prime Agent는 공유 스킬 경로를 이용하는 방식으로 설명됩니다.
이 저장소에서 배울 핵심은 “AI에게 좋은 말을 하는 법”보다 “작업 지침을 코드처럼 배포하고 검사하는 법”입니다.
커밋 356c6e0에는 스킬 디렉터리 65개가 있습니다.
그중 41개는 공개 작업 스킬이고, 24개는 다른 스킬이 읽는 principle-* 원칙 문서입니다.
SKILL.md 하나가 기본 단위입니다.
하네스(harness)는 모델에 파일 읽기·터미널·편집·도구 호출 기능을 제공하는 실행 환경입니다.
같은 지침을 읽어도 하네스가 제공하는 도구 이름과 권한은 다를 수 있습니다.“맛있게 만들어라”만으로는 재료 확인이나 마지막 맛보기를 빠뜨릴 수 있습니다. pstack은 문제를 재현하고, 원인을 좁히고, 변경 후 같은 조건으로 확인하라는 순서를 줍니다.
주방마다 오븐 버튼이 다르듯 하네스마다 도구도 다릅니다. 공통 조리법은 한 벌로 두고, 버튼 설명서를 별도로 붙이는 것이 이 프로젝트의 중요한 선택입니다.
사용자: 페이지를 바꾸면 검색 필터가 초기화되는 버그를 고쳐 줘.
↓
poteto-mode: 버그 수정용 playbook 선택
↓
재현 → how / why로 조사 → 필요한 경우 architect로 설계 검토
↓
구현 위임 → 원래 재현 조건으로 재검증 → 근거와 변경 내용 보고
이 순서는 버그 수정 playbook에 적힌 작업 정책입니다. JavaScript 함수 하나가 모든 단계를 강제로 실행하는 구조는 아닙니다. 에이전트가 문서를 읽고 도구를 선택하며, 일부 경계는 별도 코드가 검사합니다.
SKILL.md는 작업의 이유와 순서를 설명합니다.
tools/는 배포 파일의 일관성을 확인하고, plugins/pstack/pi/는 실제 자식 프로세스를 다룹니다.
이 세 층을 연결해 읽으면 문서도 소프트웨어의 일부라는 점이 선명해집니다.
스킬 수의 기준은 생성기의 agentSkills()와 publicSkills()입니다.
principle-*에는 user-invocable: false를 요구하므로, 이 설정을 따르는 하네스의 슬래시 메뉴에는 공개 스킬 41개가 표시됩니다. 다만 런타임 지원 문서에 따르면 OpenCode의 선택 목록에는 원칙 스킬도 나타납니다.
2026-10-23 분석 기준 GitHub Trending JavaScript Weekly 1위이며, 스타는 3,317개입니다. 이 순위만으로 인기의 원인을 증명할 수는 없습니다. 다만 저장소가 겨냥한 문제는 명확합니다. 에이전트를 바꿀 때마다 조사·리뷰·검증 절차까지 다시 만들 필요가 없도록 하는 것입니다.
프로젝트의 장점으로 읽히는 부분은 세 가지입니다. 목적별 작업 절차, 공유 스킬과 플랫폼 연결부의 분리, 문서와 패키징을 검사하는 코드입니다. 이는 소스 구조에 근거한 해석이며, 인기도의 인과관계나 생산성 향상률을 측정한 결과는 아닙니다.
Claude Code나 Codex 자체와 속도·성능을 겨루는 제품으로 보면 역할을 오해하기 쉽습니다. pstack은 이 도구들 위에 얹는 작업 절차입니다. 아래 표는 경쟁 서비스의 최신 기능표가 아니라, 이 저장소를 선택할 때 비교할 수 있는 구성 방식의 차이입니다.
| 접근 방식 | 장점 | 유지할 때 생기는 부담 |
|---|---|---|
| 개인 지침 파일 | 짧고 쉽게 시작하며 프로젝트 규칙을 담기 좋음 | 작업별 긴 절차와 보조 도구를 한 파일에 계속 넣기 쉬움 |
| 독립 스킬 모음 | 필요한 기능을 골라 읽을 수 있음 | 스킬끼리의 참조·도구 이름·모델 정책을 맞춰야 함 |
| pstack-claude | 중앙 라우터, 작업별 playbook, 생성기와 플랫폼별 연결부를 함께 제공 | 규칙을 익혀야 하고, 모델·권한·하네스 차이를 관리해야 함 |
| 전용 실행 엔진 구현 | 상태 전이를 코드로 강하게 통제할 수 있음 | 실행 엔진과 도구 연결을 직접 개발·운영해야 함 |
pstack의 실용적인 강점은 작업 절차를 재사용하면서, 플랫폼마다 달라지는 부분을 별도 연결부에 모아 둔다는 점입니다. 그 대가로 지침을 잘 따르는 모델과 올바른 런타임 설정에 의존합니다.
| 비교점 | 원본 Cursor pstack | pstack-claude |
|---|---|---|
| 기본 대상 | Cursor용 작업 스킬 묶음 | Claude Code 기준 지침과 여러 하네스용 연결부 |
| 선택의 이유 | Cursor 환경에서 원본을 직접 사용 | Claude Code·Codex·Pi·Copilot·Muse 등에서 같은 계열의 절차를 사용 |
| 유지보수 관계 | 이 포트가 따라가는 상위 원천 | 고정 SHA·변환 규칙·의도적인 포크 목록으로 상위 변경을 수용 |
이 비교의 근거는 README의 포트 설명과 tools/upstream.json입니다.
포트의 장점은 적용 가능한 환경을 넓힌다는 점이며, 원본보다 문제 해결 능력이 높다는 실험 결과를 뜻하지 않습니다.
다른 스킬 제품과의 정량 우열도 이 자료만으로 판단할 수 없습니다.
plugins/pstack/skills/poteto-mode/playbooks/bug-fix.md는 수정 전 재현과 수정 후 같은 화면·명령 등에서의 재검증을 요구합니다.
plugins/pstack/skills/poteto-mode/scripts/orch/store.ts의 검증 원장은 PR 번호와 SHA를 묶어 기록합니다.
“테스트를 돌렸음”에서 한 걸음 나아가 어떤 코드 버전을 어떤 근거로 확인했는가를 남기는 설계입니다.
출고 검사 도장만 찍는 대신, 검사한 제품의 일련번호도 적는 셈입니다. 제품이 바뀌었는데 예전 도장을 재사용하면 안 되듯, 커밋이 바뀌면 이전 검증의 적용 범위도 다시 확인해야 합니다.
런타임 지원 문서는 OpenCode 2.0.30에서 발견·로딩과 CLI 위임을 확인했다고 기록합니다. 반면 Prime Agent와 Gemini CLI는 실제 세션 검증을 하지 않았다고 명시합니다. 세 환경의 다중 모델 작업도 미검증으로 표시됩니다. 공유 경로에서 스킬을 찾는 것과 전체 절차가 동등하게 작동하는 것은 별개입니다.
README의 “간결하고 검증된 코드”라는 방향은 playbook과 검사 코드에 반영돼 있습니다. 이 품질 목표만으로 같은 작업군에서 성공률·비용·시간이 개선됐다고 단정할 수는 없습니다. 스타 수나 테스트 파일 수를 곧바로 성능 우위로 바꾸어 읽지 마세요.
저장소 자체의 HTTP 서버, 데이터베이스, API 라우트는 중심 구성에 없습니다.
백엔드에 가까운 역할은 파일을 검사하는 .mjs 도구와 Pi 자식 프로세스, PR·작업 상태를 다루는 TypeScript 코드가 맡습니다.
node:fs, node:path, node:child_process 같은 표준 API가 자주 등장합니다.
주 언어는 GitHub에서 JavaScript로 분류되지만, 추적 파일의 줄바꿈 수를 세면 JavaScript .mjs 약 15,229줄,
TypeScript .ts 약 13,669줄입니다. 두 숫자 모두 테스트를 포함합니다.
JavaScript 저장소라서 TypeScript가 주변부일 것이라는 예상은 맞지 않습니다.
React·Vue 애플리케이션이나 자체 웹 화면을 배포하는 프로젝트가 아닙니다.
사용자는 하네스의 채팅창·슬래시 명령·터미널을 통해 접근합니다.
Codex용 메뉴 설명은 docs/reference.md의 표에서 생성되고,
Pi에서는 pi.registerTool(), pi.registerCommand(), ctx.ui.notify()로 기능과 알림을 붙입니다.
plugin.json은 서로 다른 호스트가 읽는 입구입니다.루트 package.json과
루트 bun.lock,
그리고 스킬 내부 도구의 package.json은 역할이 다릅니다.
선언한 범위와 잠금 파일이 고정한 버전을 나누어 보아야 합니다.
| 항목 | 선언 / 고정값 | 역할과 근거 |
|---|---|---|
| 패키지 버전 | 0.9.102 | VERSION과 루트 패키지에 기록된 배포 버전 |
| typebox | 루트 peer * / 잠금 1.3.34 | 도구 입력·설정·이벤트의 런타임 스키마 검사 |
| Pi 호스트 | peer *, 선택적 peer / CI 1.1.0 | @earendil-works/pi-coding-agent; 확장 실행은 호스트가 담당 |
| commander | 선언·잠금 모두 14.0.0 | 스킬 내부 orch·PR 도구 등의 CLI 인자 처리 |
| TypeScript | 내부 dev latest / 잠금 7.0.2 | 배포 파일을 내보내기보다 타입 검사에 사용 |
| bun-types | 내부 dev latest / 잠금 1.3.14 | Bun API 타입 제공 |
| Bun | CI 1.3.14 | 생성기, 테스트, 내부 도구 실행; 패키지 의존성과 구분 |
| Prettier | CI 호출 3.6.2 | 내부 도구 포맷 검사; package.json 의존성으로는 고정하지 않음 |
package.json의 별표나 latest는 허용 범위이고, 실제 재현에 필요한 버전은 잠금 파일과 CI 설정까지 읽어야 보입니다.
루트에는 일반적인 scripts 블록이 없으며, bunfig.toml은 기본 테스트 루트를 tests로 지정합니다.
// tools/generate.mjs의 frontmatter 처리
export function parseFrontmatter(text) {
const block = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
if (!block) return { data: null, body: text };
return {
data: Bun.YAML.parse(block[1]) ?? {},
body: text.slice(block[0].length)
};
}
.mjs는 ECMAScript 모듈 형식이지만, 이 함수는 Bun.YAML.parse()를 사용합니다.
내부 plugins/pstack/skills/poteto-mode/scripts/bootstrap.ts도 import.meta.dir와 Bun.spawnSync()에 의존하며 Bun이 없으면 종료합니다.
반면 링크 검사기 tools/validate-skills.mjs는 Node.js 진입점이 있습니다. 실행기를 파일별로 확인하는 습관이 필요합니다.
CI 설정은 생성물 최신성, Bun 테스트, Windows 훅, Pi 타입, 내부 도구 타입·테스트, 셸·Markdown·워크플로 문법을 검사합니다. Linux를 기본으로 쓰면서 Windows와 macOS에 특화된 검사도 둡니다. Docker는 CI 검사기를 실행하는 수단이며, 서비스용 Dockerfile이나 Kubernetes 배포가 이 프로젝트의 실행 기반은 아닙니다.
| 검사 도구 | 설정값 | 읽을 점 |
|---|---|---|
| 생성물 검사 | bun tools/generate.mjs --check | 파일을 바꾸지 않고 예상 결과와 비교 |
| Markdown | markdownlint-cli2@0.18.1 | 지침 문서도 검사 대상 |
| 상대 링크 | lychee 0.24.2, --offline | 원격 사이트 상태로 PR 검사가 흔들리지 않게 함 |
| 워크플로 | actionlint 1.7.12 | 이미지 태그와 함께 digest도 고정 |
| 보안 검사 | OSV Scanner 2.5.1, zizmor 1.29.0 | 잠금 파일 취약점과 CI 정의를 각각 검사 |
| 형식 검증 | TLA+ 모델, Lean 4.34.1 | 별도 모델 자료; 모든 검증이 기본 CI에서 실행되는 것은 아님 |
보안 설정의 근거는 .github/workflows/security.yml이며,
Lean 버전은 lean/lean-toolchain에 있습니다.
Cargo.toml·pyproject.toml·go.mod를 기반으로 한 별도 애플리케이션 패키지는 없습니다.
위쪽은 배포물을 만드는 과정이고, 아래쪽은 사용자가 작업할 때의 과정입니다.
생성기는 사용자 요청마다 실행되는 에이전트 서버가 아닙니다.
PR 기능을 쓰면 gh 또는 문서가 지원하는 origin CLI가 추가로 관여합니다.
tools/generate.mjs는
VERSION, 모델 정책, 명령 설명 표를 읽고 여러 배포 파일에 같은 사실을 반영합니다.
parseModels()는 중복 역할, 없는 모델 별칭, 없는 스킬, 잘못된 추론 수준을 거부합니다.
정책을 먼저 검사한 다음 plan()이 파일별 예상 텍스트를 구성합니다.
// tools/generate.mjs의 진입 흐름을 줄인 학습용 의사코드
const models = loadModels(repo);
const intended = plan(repo, models);
const pending = check
? changes(repo, intended)
: apply(repo, intended);
const failures = problems(repo, models);
// --check에서 변경 예정 파일이 있으면 실패로 보고한다.
// 실제 main()은 예외 처리와 오류 메시지 수집도 수행한다.
changes()는 현재 파일과 계획의 텍스트를 비교하고, 생성기가 소유한 디렉터리의 오래된 항목도 찾습니다.
apply()는 그 차이를 쓰거나 제거합니다.
같은 입력에서 같은 결과를 기대하는 멱등성을 겨냥한 구성입니다.
다만 여러 파일 쓰기를 한 번에 되돌리는 트랜잭션으로 구현한 것은 아니므로, “모든 파일이 원자적으로 갱신된다”고 설명하면 과장입니다.
한 사실을 여러 파일에 복사해야 한다면, 복사본을 손으로 맞추기보다 원천과 생성 규칙을 함께 관리하는 편이 오류를 줄입니다.
메뉴 설명의 원천은 문서 표이고, 모델 기본값의 원천은 plugins/pstack/models.json입니다. 원천이 서로 다른 이유도 함께 기억하세요.
주소가 바뀔 때 명함·홈페이지·계약서 양식을 하나씩 고치는 대신, 주소록 한 곳에서 각 양식을 만드는 방식입니다.
--check는 “새 주소록과 아직 다른 양식이 있는가”를 확인하는 검사입니다.
tools/runtimes.mjs의
RUNTIMES는 Codex·Pi·GitHub Copilot·Muse의 매핑 문서, 매니페스트, 검증 함수를 표처럼 묶습니다.
Claude Code는 기본 형식으로 별도 취급합니다.
이 배열에는 공유 스킬 발견만 사용하는 모든 하네스가 나열되는 것은 아닙니다.
| 대상 | 연결 방식 | 소스에서 확인할 지점 |
|---|---|---|
| Claude Code | 기본 도구 이름과 SessionStart 훅 | plugins/pstack/hooks/hooks.json, plugins/pstack/hooks/session-start.sh |
| Codex | 매핑 문서, 생성 메뉴 파일, 별도 훅 정의 | plugins/pstack/skills/poteto-mode/references/codex-tools.md, plugins/pstack/.codex-plugin/ |
| GitHub Copilot | 전용 매니페스트와 JSON 문맥, 모델 선택 검사 | plugins/pstack/.github/plugin/, plugins/pstack/hooks/copilot-context.awk |
| Muse | 네이티브 매니페스트와 일반 텍스트 문맥 | stampMuseManifest(), plugins/pstack/hooks/muse-context.awk |
| Pi | 공유 스킬과 실제 TypeScript 확장 | plugins/pstack/pi/index.ts의 install() |
핵심 스킬을 하네스마다 통째로 번역하지 않고, 도구 이름의 차이를 매핑 문서로 해결합니다.
반면 상위 Cursor 원본을 이 포트의 기본 형식으로 옮기는 기계적 변환은 tools/substitutions.json에 있습니다.
상위 프로젝트 동기화와 사용 시 플랫폼 적응은 서로 다른 단계입니다.
문서 매핑만으로 없는 기능이 생기지는 않습니다.
Pi에는 자식 에이전트·질문·예약 도구를 실제로 등록하는 코드가 필요합니다.
plugins/pstack/pi/index.ts의 install()이 registerAgentTools(), registerAsk(), registerSchedule() 등을 연결합니다.
plugins/pstack/pi/child.ts의
PiChild는 spawn()으로 pi --mode rpc 자식 프로세스를 다룹니다.
요청은 표준 입력의 JSON 한 줄로 보내고, 표준 출력의 이벤트를 한 줄씩 읽습니다.
긴 요청을 명령행 인자로 넘기지 않는 것도 눈여겨볼 선택입니다.
자식 시작
↓
stdin: JSON 명령 전송 → stdout: 응답·도구·메시지 이벤트 수신
↓
message_end: 최종 답변 후보 갱신
↓
agent_settled: 작업이 끝났다는 신호 → stdin 닫기
↓
프로세스 종료 확인 → 완료 / 실패 / 중지 기록
답변이 보였다는 사실과 프로세스가 끝났다는 사실은 다르다.
PiChild.onLine()은 JSON.parse() 결과를 곧바로 신뢰하지 않고 TypeBox로 이벤트 모양을 검사합니다.
plugins/pstack/pi/agents.ts의
outcomeOf()는 중지 여부, settled, 답변 텍스트, 오류를 함께 보고 결과를 정합니다.
종료 코드 하나만으로 작업 성공을 판정하지 않는 이유를 배울 수 있습니다.
예약도 상태 문제입니다. plugins/pstack/pi/schedule.ts의
Scheduler는 대기 중인 예약 하나와 반복 타이머를 관리합니다.
scheduleWakeup()은 새 예약 전에 기존 예약을 취소하고, 바쁘면 1초 뒤 다시 확인합니다.
startLoop()는 실행 중 도착한 주기 신호를 건너뛰어 작업이 계속 쌓이는 것을 막습니다.
// Scheduler.startLoop()의 실제 핵심 식
this.loop = setInterval(
() => ctx.isIdle() && this.fire(prompt),
seconds * 1000
);
this.loop.unref();
sealed는 사용자가 실행 중 반복 작업을 중지하거나 교체했을 때, 그 이전 실행이 뒤늦게 다시 예약하는 것을 거부하는 장치입니다.
agent_settled에서 봉인을 풉니다.
타이머에 대한 unref()는 타이머만 남았을 때 프로세스를 계속 붙잡지 않도록 합니다.
최신 변경을 작은 입구로 삼아 보겠습니다.
tools/validate-skills.mjs의
markdownTargets()와 destination()은 Markdown 링크에서 실제 목적지를 뽑습니다.
v0.9.102는 공백·괄호가 있는 경로를 꺾쇠로 감쌌을 때 꺾쇠까지 파일 이름으로 검사하던 문제를 고쳤습니다.
# 검사해야 할 목적지는 references/guide(v1).md
[안내]( <references/guide(v1).md> "사용 안내")
// destination()의 꺾쇠 처리 부분
const value = raw.trim();
if (value.startsWith("<")) {
const close = value.indexOf(">");
return close === -1 ? value : value.slice(1, close);
}
목적지를 얻은 뒤에는 질의·조각을 분리하고 URL 인코딩을 해제합니다.
pathIsInside()로 논리 경계, existsSync()로 존재 여부,
realpathSync()로 심볼릭 링크가 향하는 실제 위치를 검사합니다.
단순히 “경로가 지정 폴더 이름으로 시작하는가”만 확인하는 것보다 강한 이유입니다.
| 입력 사례 | 검사 의도 |
|---|---|
| 공백·괄호가 있는 정상 링크 | 올바른 목적지로 해석하고 존재하면 허용 |
missing%GG(v1).md | 잘못된 URL 인코딩을 오류로 보고 |
../../outside(v1).md | 스킬 설치 경계 밖으로 나가는 참조를 거부 |
| 외부 파일을 가리키는 심볼릭 링크 | 표면 경로가 내부여도 실제 목적지가 외부면 거부 |
| 정상 링크 뒤 같은 줄의 누락 링크 | 첫 링크의 파싱이 다음 오류를 숨기지 않는지 확인 |
사례의 근거는 tests/agent-skills.test.mjs입니다.
이 검사는 스킬 설치물의 내부 참조를 지키는 검사이며, 연결된 모든 원격 사이트를 방문하거나 임의 Markdown 문법 전체를 해석하는 파서는 아닙니다.
스킬 이름과 디렉터리가 같아야 한다는 규칙, 모델 이름이 원천 정책과 맞아야 한다는 규칙, 설치되지 않는 파일을 참조하지 말라는 규칙을 자동 검사로 옮겼습니다. 반복되는 실수 하나를 작고 재현 가능한 검사 하나로 바꾸는 연습에 좋은 사례입니다.
트리는 핵심 경로를 추린 것이며 모든 파일을 열거한 목록은 아닙니다.
.agents/plugins/는 이 저장소에서는 마켓플레이스 카탈로그입니다.
사용자 프로젝트가 추가할 수 있는 .agents/playbooks/와는 용도가 다릅니다.
CONTEXT.md에서 이 프로젝트가 쓰는 “역할”, “생성 복사본”, “동기화 경계”의 뜻을 잡습니다.plugins/pstack/skills/poteto-mode/SKILL.md와 plugins/pstack/skills/poteto-mode/playbooks/bug-fix.md를 이어 읽어 지침의 실행 순서를 이해합니다.tools/generate.mjs의 plan()부터 위로 올라가 각 입력이 어디로 복제되는지 찾습니다.plugins/pstack/pi/index.ts에서 등록 함수를 따라가고, plugins/pstack/pi/child.ts와 plugins/pstack/pi/schedule.ts로 내려갑니다.tests/를 읽어 정상 사례와 실패 사례를 비교합니다.| 바꾸려는 것 | 주된 원천 | 함께 확인할 것 |
|---|---|---|
| 배포 버전 | VERSION | CHANGES.md와 생성 매니페스트 |
| 역할별 모델 | plugins/pstack/models.json | 생성된 Models 영역·플랫폼별 모델 표 |
| Codex 메뉴 설명 | docs/reference.md의 명령 표 | 공개 스킬 41개와 표 행의 대응 |
| 플랫폼별 동작 | tools/runtimes.mjs와 각 매핑·어댑터 | 공통 스킬을 불필요하게 분기하지 않는지 |
| 상위 프로젝트 이식 | tools/substitutions.json, tools/forks.json | 변환 근거와 의도적인 정책 차이 |
파일이 존재하는 위치만 아는 것보다, 그 파일을 직접 수정해도 되는지와 진짜 원천이 어디인지를 아는 것이 유지보수에 더 중요합니다.
plugins/pstack/.codex-plugin/prompts/ 등의 생성 파일은 재생성할 때 덮어쓸 수 있습니다.
tools/upstream.json은
cursor/plugins의 pstack, cursor-team-kit, thermos를 각각 SHA로 고정합니다.
pstack 원천은 7b353f7, thermos는 118e73b로 시작하는 커밋입니다.
tools/sync.mjs의
classify()는 예전 원본·새 원본·현재 포트의 세 사본을 비교합니다.
현재 포트가 예전 원본과 같으면 새 원본으로 갱신하고, 양쪽이 바뀌면 mergeFile()로 3방향 병합을 시도합니다.
의도적인 차이는 tools/forks.json에 등록하도록 해, 실수로 생긴 차이와 구분합니다.
tools/의 .mjs는 import·export와
import.meta.url을 사용합니다.
tools/generate.mjs는 직접 실행됐을 때만 main()을 호출하도록 보호합니다.
테스트가 변환·검사 함수를 가져와도 전체 생성 작업이 실행되지 않게 하려는 구조입니다.
// tools/generate.mjs의 직접 실행 판별
if (process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href) {
try {
main();
} catch (err) {
console.error(`FAIL: ${err.message}`);
process.exit(1);
}
}
실습 아이디어: 문자열에서 버전만 바꾸는 함수를 독립 파일로 써 보고, 파일 읽기·쓰기는 진입 함수에 둡니다. 함수를 가져오기만 했을 때 결과 파일이 생기지 않아야 합니다. 이 구분을 익히면 CLI를 만들면서도 내부 로직을 쉽게 재사용할 수 있습니다.
plugins/pstack/pi/agents.ts는 실행 중 기록과 종료 기록을 별도 스키마로 표현합니다.
종료 기록에 필요한 endedAt과 finalText를 실행 중 상태에 억지로 넣지 않습니다.
외부 JSON이나 저장된 세션 기록을 읽을 때에는 Value.Check()로 실제 값도 확인합니다.
kind나 status 같은 표식을 기준으로 가능한 데이터 모양을 나누는 타입입니다.
“실행 중이면 종료 시각이 없음, 종료 상태이면 종료 시각이 있음”을 표현할 수 있습니다.
다만 TypeScript 타입 표기만으로 외부 JSON을 검사할 수는 없어서 런타임 검증이 별도로 필요합니다.// 저장소의 상태 분리를 이해하기 위한 축약 예제
type Job =
| { status: "running"; pid: number }
| { status: "completed"; endedAt: string; finalText: string };
function describe(job: Job): string {
if (job.status === "running") return `실행 중: ${job.pid}`;
return `완료: ${job.endedAt}`;
}
실습 아이디어: “완료 상태인데 답변이 없는 객체”를 어떻게 다룰지 결정해 보세요.
타입에서 금지할지, 실패 상태를 추가할지, 종료 판정에서 거부할지에 따라 책임의 위치가 달라집니다.
outcomeOf()는 작업 종료와 유효한 답변을 함께 판단하는 실제 사례입니다.
plugins/pstack/hooks/session-start.sh는 첫 인자로 하네스를 구분하고, 각 환경 변수에서 설정 시트 위치를 찾습니다.
같은 내용을 출력해도 Copilot은 JSON의 additionalContext를,
Muse는 일반 텍스트를 받습니다. 출력 형식도 연결 계약이라는 점을 보여 줍니다.
CHANGES.md의 0.9.97 항목은 역슬래시가 포함된 경로를 awk의 -v로 넘기면
이스케이프가 해석되는 문제를 설명합니다.
현재 훅은 PSTACK_HOOKS 환경 변수로 경로를 전달합니다.
셸 따옴표가 맞아도 그다음 프로그램이 같은 문자열을 다시 해석할 수 있다는 사례입니다.
실습 아이디어: plugins/pstack/hooks/session-start.sh, plugins/pstack/hooks/copilot-context.awk,
plugins/pstack/hooks/muse-context.awk를 나란히 읽고 입력·출력 계약만 표로 적습니다.
경로와 일반 문장을 같은 규칙으로 이스케이프하면 안 되는 이유도 설명해 보세요.
plugins/pstack/skills/poteto-mode/scripts/orch/store.ts의
atomicWrite()는 같은 디렉터리의 임시 파일을 쓰고 rename()으로 교체합니다.
acquireLock()는 여러 작성자가 같은 저장소를 동시에 갱신하는 문제를 별도로 다룹니다.
실습 아이디어: 위 두 작성자의 읽기와 쓰기를 종이에 순서대로 배치합니다. 완성된 파일로 교체하는 것만으로는 갱신 손실을 막지 못함을 설명해 보세요. 실제 락 구현에는 비정상 종료와 오래된 소유자 처리까지 필요하므로 단순 예제와 구분합니다.
tests/agent-skills.test.mjs는 작은 임시 트리를 만들어 링크의 허용·거부를 확인합니다.
tests/pi/interaction.test.mjs는 예약 교체, 바쁜 동안의 대기, 반복 신호 건너뛰기 같은 사용자 관찰 동작을 다룹니다.
테스트가 존재한다는 사실과 특정 환경에서 통과했다는 사실은 구분해야 합니다.
검증을 배울 때 가장 중요한 질문은 “통과했는가”와 함께 “어떤 가정과 입력 범위에서 통과했는가”를 묻는 것입니다.
tla/PiScheduler.matrix에는
실행 시작 전 틈이나 다른 실행과의 경쟁에서 실패를 기대하는 시나리오가 명시되어 있습니다.
lean/Model/WatchPoll.lean은
PR 폴링의 대기 시간과 제한을 모델링하지만, 실제 JavaScript의 소수 시간과 모델의 자연수 시간 차이 같은 가정을 처음에 적습니다.
“수학 모델이 있음”을 “전체 프로그램에 버그가 없음”으로 바꾸어 말하지 않는 태도까지 학습할 수 있습니다.
저장소는 CPU 코어 수·최소 RAM·필요 디스크 용량을 공식 수치로 정하지 않습니다. 따라서 정확한 최소 사양을 단정할 수 없습니다. 필요한 자원은 스킬 문서만 읽는지, Pi 자식을 여러 개 실행하는지, 대상 프로젝트의 빌드까지 수행하는지에 따라 달라집니다.
| 구분 | 요구사항과 확인 범위 |
|---|---|
| CPU·RAM | 공식 최소치 없음. 로컬 에이전트·자식 프로세스·대상 빌드의 사용량이 합쳐짐 |
| GPU | pstack 자체가 모델 가중치를 적재하는 코드나 GPU 실행기를 제공하지 않음. 로컬 모델 사용 시에는 별도 추론기의 요구사항 적용 |
| 디스크 | 스킬·도구 외에 대화 기록, 의존성, Git worktree가 증가 요인. 저장소 크기를 총 사용량으로 보면 안 됨 |
| 운영체제 | CI에 Ubuntu·Windows·macOS 검사 존재. 모든 기능이 모든 운영체제에서 동일하다는 뜻은 아님 |
| 실행 환경 | 선택한 코딩 하네스 필요. 저장소 검사와 내부 도구의 기준은 Bun 1.3.14 |
| Node.js | 일부 .mjs 도구가 사용. 루트 engines 필드에 최소 버전 명시 없음. Pi는 설치한 호스트의 요구사항도 확인 |
| 네트워크 | 문서 읽기는 오프라인 가능. 패키지 획득·원격 모델 호출·GitHub PR 조작에는 각각 연결 필요 |
| 인증 | 사용할 모델 제공자 인증 필요. PR 기능은 gh 또는 지원되는 forge CLI의 별도 인증 필요 |
README는 Claude Code 2.1.292 이상을 요구합니다.
CHANGES.md는 역할별 추론 수준을 에이전트 호출의 effort 필드로 전달하기 위한 조건이라고 설명합니다.
Copilot의 기록된 시험 범위는 CLI 1.0.87부터 1.0.92까지입니다.
Pi 타입 검사 CI는 1.1.0으로 고정되어 있으며, 이를 모든 미래 버전의 호환성 보장으로 읽어서는 안 됩니다.
Codex의 자동 라우팅 훅은 /hooks에서 신뢰한 뒤 실행됩니다.
Muse는 muse plugins approve pstack 승인이 필요하다고 문서에 설명됩니다.
Windows Codex는 commandWindows로 PowerShell 어댑터를 사용하며, 조직의 실행 정책에 따라 훅이 제한될 수 있습니다.
plugins/pstack/skills/poteto-mode/scripts/bootstrap.ts의
ensureDependenciesInstalled()는 패키지 선언과 잠금 파일을 SHA-256으로 묶어 설치 키를 만듭니다.
설치 키가 일치하고 commander의 package.json이 존재하면 재사용하고, 아니면 bun install --frozen-lockfile --production을 실행합니다.
스킬 파일이 보인다는 사실과 보조 도구의 의존성이 준비됐다는 사실은 다릅니다.
README는 자체 서버·텔레메트리가 없다고 설명합니다. 동시에 에이전트가 읽는 코드와 대화 기록은 사용 중인 모델 제공자로 전달된다고 명시합니다. 로컬 스크립트라는 이유만으로 모든 처리가 기기 안에서 끝난다고 해석할 수 없습니다.
Pi 자식은 별도 프로세스이며, 모델 사용량도 추가될 수 있습니다. 저장소의 Pi 매핑 문서는 Claude 구독이 있어도 Pi를 통한 Anthropic 사용에는 토큰별 추가 요금이 부과된다고 설명합니다. 고정 요금이나 무료 실행을 가정하지 말고, 실제 사용하는 제공자·계정 조건을 확인해야 합니다.
중첩 깊이는 plugins/pstack/pi/agents.ts의 MAX_SPAWN_DEPTH = 3과 도구 제외 규칙으로 제한합니다.
이는 자식의 자식으로 이어지는 깊이 제한이며, 전체 동시 실행 개수가 세 개라는 뜻은 아닙니다.
plugins/pstack/pi/config.ts의 기본 종료 유예는 killGraceMs: 5000, exitGraceMs: 30000입니다.
다섯 과제는 읽기만으로 시작하거나, 원본과 분리한 작은 예제를 작성하는 방식입니다. 저장소 도구를 설치하거나 실제 PR을 병합하지 않아도 핵심 원리를 익힐 수 있습니다. 실습의 완료 기준은 코드를 많이 쓰는 것이 아니라, 정상 사례와 실패 사례가 왜 다른 결과를 내는지 설명할 수 있는 것입니다.
준비 지식: 디렉터리와 텍스트 파일, YAML의 키·값 표기.
할 일: plugins/pstack/skills/poteto-mode/SKILL.md와 임의의 plugins/pstack/skills/principle-*/SKILL.md에서
첫 --- 블록만 비교합니다. name, description, user-invocable의 의미를 적습니다.
제출물: “디렉터리 이름 / 스킬 이름 / 메뉴 공개 여부 / 언제 읽는가” 네 열의 표.
확인 기준: 41개 공개 스킬과 24개 원칙의 차이를 설명하고, 원칙도 파일로 읽을 수 있다는 사실과 메뉴 노출 여부를 구별합니다.
# 저장소 루트에서 텍스트만 확인하는 명령
rg -n '^(name|description|user-invocable):' \
plugins/pstack/skills/poteto-mode/SKILL.md \
plugins/pstack/skills/principle-prove-it-works/SKILL.md
준비 지식: JavaScript 함수, 객체, 문자열 비교.
할 일: VERSION에 해당하는 문자열 하나로 두 매니페스트의 예상 내용을 만드는 함수를 작성합니다.
파일을 쓰는 대신 { 경로: 예상텍스트 } 객체를 반환하게 합니다.
제출물: 예상 결과와 현재 내용을 비교해 변경 경로만 반환하는 함수, 그리고 입력·출력 예시.
확인 기준: 한쪽 버전만 오래되면 그 경로만 나타나고, 이미 맞는 상태에서는 빈 목록이 나옵니다. 같은 입력으로 두 번 호출해도 결과가 같아야 합니다.
// 작은 생성기의 입출력 계약 예시
const current = { "a.json": "0.9.101", "b.json": "0.9.102" };
const intended = { "a.json": "0.9.102", "b.json": "0.9.102" };
// compare(current, intended)의 기대 결과: ["a.json"]
// compare(intended, intended)의 기대 결과: []
준비 지식: 문자열 자르기, URL 인코딩, 상대 경로.
할 일: 4절의 destination()을 따라 입력에서 경로만 추출하는 작은 함수를 작성합니다.
그다음 경로의 존재 여부 검사는 가짜 파일 목록으로 대체합니다.
제출물: 정상 경로, 공백·괄호 경로, 없는 경로, 잘못된 인코딩을 포함한 입력표.
확인 기준: <guide (v1).md>는 꺾쇠 없는 경로로 읽고,
“추출 성공”을 “그 파일이 존재함”과 혼동하지 않습니다. 한 줄에 두 링크가 있을 때도 두 번째를 놓치지 않아야 합니다.
준비 지식: 이벤트 루프, 상태 변수, 타이머 취소.
할 일: 실제 타이머 대신 사건 배열을 만듭니다.
start, stop, settled, schedule, timer를 한 사건씩 적용하고 상태를 출력합니다.
제출물: “실행 중 중지 → 이전 실행의 재예약”과 “실행 시작 전 틈에서 중지”의 두 타임라인.
확인 기준: sealed가 언제 켜지고 풀리는지 설명하고,
tla/PiScheduler.matrix의 실패 기대 사례가 어떤 호스트 가정을 바꾸는지 짚습니다.
준비 지식: 배열에서 찾기, 복합 키, Git 커밋 식별자.
할 일: plugins/pstack/skills/poteto-mode/scripts/orch/store.ts의 ledger.record와 ledger.check를 읽고,
{ pr, sha, verdict, evidence } 객체 배열로 축소 구현합니다.
제출물: 같은 PR·같은 SHA의 재기록, 같은 PR·다른 SHA의 조회 결과 표.
확인 기준: 새 SHA에 예전 근거를 자동 적용하지 않습니다.
항목이 있다는 사실과 판정이 충분한 성공 수준이라는 사실도 구별합니다.
실제 ledger.check는 일치 항목이 없으면 NOT-VERIFIED 오류를 내고, 있으면 그 항목을 반환합니다. 판정 수준은 호출 쪽에서 판단해야 합니다.
처음부터 전체 스킬을 외우기보다, 하나의 입력이 어떤 자료와 코드를 거치는지 추적하는 편이 좋습니다. 아래는 JavaScript 기본 문법을 막 익힌 독자를 위한 6주 학습 순서입니다. 각 주차는 개념 학습과 결과물 하나를 묶었습니다.
| 주차 | 배울 내용 | 소스 읽기와 결과물 |
|---|---|---|
| 1주차 | Git 경로·커밋, Markdown, YAML frontmatter | CONTEXT.md, 스킬 두 개. 과제 1의 스킬 계약 표 완성 |
| 2주차 | ESM, 표준 파일 API, 순수 변환과 입출력 | tools/generate.mjs의 stampVersion()·plan(). 과제 2의 메모리 생성기 |
| 3주차 | 파싱·상대 경로·URL 인코딩·설치 경계 | tools/validate-skills.mjs와 대응 테스트. 과제 3의 허용·거부 입력표 |
| 4주차 | TypeScript 유니온, TypeBox 런타임 스키마, 외부 입력 | plugins/pstack/pi/config.ts·plugins/pstack/pi/agents.ts. 상태별 필수 필드 표 |
| 5주차 | 자식 프로세스, JSON 줄 프로토콜, 타이머와 수명 | plugins/pstack/pi/child.ts·plugins/pstack/pi/schedule.ts. 과제 4의 사건 시뮬레이터 |
| 6주차 | 원자적 교체·락·SHA별 근거·모델의 가정 | plugins/pstack/skills/poteto-mode/scripts/orch/store.ts, tla/, lean/. 과제 5와 검증 범위 설명 |
매주 “왜 이 추상화가 필요한가”를 실제 실패 사례 하나와 연결하면, 다른 저장소에서도 같은 설계 문제를 알아볼 수 있습니다. 예를 들어 3주차의 꺾쇠 링크 오류는 경계 파싱으로, 5주차의 재예약 문제는 상태 전이와 수명 관리로 일반화할 수 있습니다.
지도를 통째로 외우기보다 집에서 역까지 한 번 걸으며 표지판을 익히는 방식입니다. 한 버전 값, 한 Markdown 링크, 한 자식 프로세스의 경로를 끝까지 따라가면 나머지 구조를 읽을 기준이 생깁니다.
| 용어 | 쉬운 설명 |
|---|---|
| pstack | 원본 Cursor 스킬 묶음의 이름. 이 저장소에서는 여러 하네스로 옮긴 작업 절차의 기반 |
| 하네스 | 모델에게 파일·편집·터미널·도구 호출을 연결하는 실행 환경 |
| Agent Skill | 언제 무엇을 어떻게 할지 적은 지침과 자료의 디렉터리 |
| frontmatter | Markdown 맨 앞의 --- 사이에 적는 이름·설명 같은 메타데이터 |
| playbook | 버그 수정·기능 개발 등 목적별로 따를 작업 순서 |
| 라우팅 | 요청의 성격을 보고 맞는 스킬이나 절차로 보내는 것 |
| SessionStart 훅 | 세션 시작 등의 사건에 맞춰 추가 지침을 넣는 연결 장치 |
| 매니페스트 | 호스트가 플러그인의 버전과 기능 위치를 알도록 하는 설정 파일 |
| 어댑터 | 공통 기능을 특정 환경의 도구·형식에 맞춰 연결하는 부분 |
| 역할·모델 계층 | 조사자·검토자 같은 일의 종류를 기본·작업자·패널 모델 정책에 연결하는 규칙 |
| override sheet | 배포 기본값 위에 사용자의 모델·훅 선택을 적는 설정 문서 |
| peer dependency | 확장이 호스트와 함께 사용할 패키지를 선언하는 의존성 방식 |
| 잠금 파일 | 허용 범위가 아니라 실제 선택된 패키지 버전과 무결성 정보를 기록한 파일 |
| ESM | import·export로 모듈을 나누는 JavaScript 표준 형식 |
| 런타임 스키마 | 실행 중 받은 JSON 값이 기대한 모양인지 확인하는 규칙 |
| 판별 유니온 | status 같은 표식에 따라 데이터 모양을 여러 경우로 나눈 타입 |
| 멱등성 | 같은 작업을 반복해도 이미 도달한 상태를 다시 흔들지 않는 성질 |
| 불변 조건 | 유효한 상태라면 계속 참이어야 하는 규칙. 예: 스킬 이름과 디렉터리 이름 일치 |
| 드리프트 | 같아야 할 설정·문서·생성물이 시간이 지나며 어긋나는 현상 |
| 3방향 병합 | 옛 공통 상태를 기준으로 원본의 새 변경과 포트의 변경을 합치는 작업 |
| 심볼릭 링크 | 파일처럼 보이지만 다른 경로를 가리키는 연결. 실제 목적지는 경계 밖일 수도 있음 |
| 원자적 교체 | 독자가 불완전한 중간 파일을 보지 않도록 완성된 파일로 바꾸는 방식 |
| 락 | 여러 작성자가 같은 자원을 동시에 고치지 않도록 순서를 제한하는 장치 |
| RPC·JSON 줄 | 다른 프로세스에 요청을 보내는 방식과, 이 Pi 연결에서 쓰는 한 줄씩의 JSON 메시지 형식 |
| 이벤트 루프 | 입력·타이머·프로세스 사건의 콜백을 순서대로 처리하는 실행 구조 |
| sealed | 중지된 이전 실행이 반복 작업을 다시 예약하지 못하도록 하는 Scheduler의 상태 |
| SHA | Git 커밋의 식별자. 검증한 코드 버전과 이후 버전을 구분할 때 사용 |
| 검증 원장 | PR·SHA·판정·근거를 함께 남겨 무엇을 확인했는지 찾는 기록 |
| worktree | 같은 Git 저장소에서 별도 디렉터리와 브랜치로 작업하게 하는 기능 |
| 형식 검증 | 명시한 모델·성질·가정 안에서 가능한 상태나 논리를 체계적으로 확인하는 방법 |
| 반례 | 주장한 성질이 깨지는 구체적인 입력이나 사건 순서 |
“이름이 등록됨”, “스킬을 발견함”, “실제 도구로 실행됨”, “결과를 검증함”은 서로 다른 단계입니다. 이 구분만 유지해도 에이전트 도구의 소개 문구와 실제 실행 능력을 훨씬 정확하게 읽을 수 있습니다.
아래 링크는 기술을 이어 공부할 공식 자료입니다. 외부 문서의 최신 예제를 적용할 때는 이 글에 기록한 버전과의 차이를 확인하세요.
| 항목 | 확인 내용 |
|---|---|
| 트렌딩 | GitHub Trending JavaScript Weekly #1 · 2026-10-23 분석 기준 |
| 스타 / 포크 | 3,317 / 308 · 2026-10-23 GitHub 기준 |
| 주 언어 | JavaScript · GitHub 분류 |
| 생성일 | 2026-05-26 |
| 최신 릴리스 | v0.9.102 · GitHub 릴리스 게시일 2026-10-22 |
| 최근 push | 2026-10-22 · GitHub 메타데이터의 push 날짜 |
| 최근 커밋 | 356c6e0 · Git 기록상 2026-10-23 · 꺾쇠 링크 목적지 검사 수정 및 0.9.102 릴리스 변경 |
| 배포 버전 | VERSION·루트 package.json 모두 0.9.102 |
| 추적 경로·스킬 | 373개 추적 경로 · 스킬 65개 = 공개 41개 + 원칙 24개 |
| JavaScript | .mjs 69개 · 15,229줄 |
| TypeScript | .ts 38개 · 13,669줄 |
| 문서 | .md 191개 · 8,310줄 |
| 보조 언어 | 셸 678줄 · awk 614줄 · Python 203줄 · PowerShell 24줄 |
| 검증 모델 | TLA+ 2개 파일·694줄 · Lean 1개 파일·1,082줄 |
| 테스트 디렉터리 | 코드 58개 파일·12,280줄. 위 언어별 집계에 포함된 수치 |
| 라이선스 | MIT · 포트 Michael Denyer, 원본 Lauren Tan 및 Cursor 귀속 고지 포함 |
줄 수는 커밋 356c6e0의 Git 추적 파일에서 확장자별로 줄바꿈을 센 대략적인 물리 줄 수입니다.
주석·빈 줄·테스트·생성 파일을 포함하며, Git 내부 자료와 바이너리·심볼릭 링크의 대상은 제외합니다.
구현 코드만의 규모나 테스트 커버리지 비율을 뜻하지 않습니다.
릴리스 게시일·최근 push·Git 커밋 날짜는 서로 다른 필드이며, 위 표는 각각의 날짜를 유지했습니다.
동작 설명의 기준은 커밋 356c6e0입니다. 테스트와 형식 모델의 검증 범위는 각각 명시된 입력·환경·가정으로 한정됩니다. 호스트별 시험 범위는 런타임 지원 문서와 Pi 동등성 검증 기록을 따릅니다.
Prime Agent·Gemini CLI의 실제 위임과 일부 다중 모델 흐름은 문서상 미검증입니다. TLA+ 예약 모델에는 특정 호스트 실행 순서에서 성질이 깨지는 사례가 남아 있고, Lean 모델에도 숫자·시간에 관한 명시적 가정이 있습니다. 검증 자료가 있다는 사실만으로 전체 동작을 보증할 수 없습니다.
원본의 자율 실행·위임 정책은 적용 전에 프로젝트의 권한·비용·데이터 규칙에 맞춰 읽어야 합니다. 사용자 지침이 우선하며, 모델이 읽는 자료는 해당 모델 제공자로 전달될 수 있습니다. 새로운 하네스 버전에서는 발견·훅·도구 호출을 각각 확인해야 합니다.