@kaydash9999/banker-plugins 0.26.1 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +17 -0
- package/README.md +9 -6
- package/bin/lib/codex-hooks.mjs +7 -1
- package/codex/command-skills/front-qa/SKILL.md +3 -1
- package/codex/hooks.json +15 -5
- package/codex/manifest.json +1 -0
- package/codex/skills/ralph/SKILL.md +272 -0
- package/codex/skills/ralph/scripts/gate-evidence.mjs +457 -0
- package/codex/skills/ralph/scripts/ralph-state.mjs +622 -0
- package/codex/skills/ralph/scripts/run-folder.mjs +299 -0
- package/codex/transform-matrix.md +5 -4
- package/commands/front-qa.md +3 -1
- package/hooks/ralph-continue-codex.mjs +59 -0
- package/package.json +1 -1
- package/skills/all-in-one/SKILL.md +22 -8
- package/skills/ralph-qa/references/gate-evidence.mjs +3 -3
- package/skills/ralph-qa/references/run-folder.mjs +3 -3
- package/skills/ultra-chain/SKILL.md +11 -4
- package/skills/ultra-init/SKILL.md +9 -7
- package/skills/ultraqa/references/gate-evidence.mjs +3 -3
- package/skills/ultraqa/references/run-folder.mjs +3 -3
- package/skills/visual-ralph/SKILL.md +9 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "banker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Claude Code plugin: QA/audit/docs/architecture skills + commands, a /banker:setup installer for OS dependencies (playwright, etc.), and wiki compaction.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "smallOpenSource",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.27.0] - 2026-11-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Codex 전용 새 스킬 `ralph`(npm 길 `$banker-ralph`, 플러그인 길 `$banker:ralph`): oh-my-codex 0.21 이 `$ralph` 를 없애(`$ultragoal` 을 가리키는 스텁) banker 스킬이 Codex 에서 구현 단계를 부르지 못하던 것을 대신한다. OMX 없이 동작한다.
|
|
7
|
+
- 반복마다 구현, 슬롭 점검, 게이트 실행, 새 맥락 검토자 판정을 차례로 한다. 게이트는 ralph-qa 의 실행기(`gate-evidence.mjs`, 바이트 동일 사본)가 돌린다.
|
|
8
|
+
- 끝남은 `scripts/ralph-state.mjs done` 만 정한다. 게이트 기록, 검토자 APPROVE, 슬롭 점검이 모두 지금 작업 트리 지문과 맞아야 한다. 같은 트리에 받은 REJECT 는 다음 반복에서도 남는다. 판정은 검토자가 본 지문으로만 기록한다.
|
|
9
|
+
- 시작 전부터 있던 실패(기준선)는 그대로 두면 끝나지 않는다. 이 작업과 상관없는 실패만 `accept-baseline` 으로 까닭을 적고, 그 뒤에 받은 APPROVE 로만 끝낸다. 반복 0 은 깨끗한 시작 트리에서 돈 것만 기준선이 된다.
|
|
10
|
+
- 기록은 저장소 밖 게이트 폴더에 두고 잠금 안에서 바꾼다. 저장소 안에 추적하지 않는 파일을 만들지 않는다.
|
|
11
|
+
- Codex Stop 훅 `ralph-continue-codex`: Claude Code 의 OMC ralph 처럼, 실행을 시작한 세션이 그 실행을 `done` 이나 `stop` 으로 끝내기 전에는 턴을 이어 간다. Codex goal 설정이 없어도 된다.
|
|
12
|
+
- 하위 에이전트와 다른 세션은 막지 않는다. 기록 변화 없이 다섯 번 막은 뒤, 또는 한 실행에서 모두 100번 막은 뒤에는 놓아준다.
|
|
13
|
+
- 새 세션에서 이어 갈 때는 `adopt` 로 옮긴다. `BANKER_NO_RALPH_CONTINUE` 로 끈다.
|
|
14
|
+
- npm 길(`banker setup --codex`)과 Codex 플러그인(`codex/hooks.json` 의 Stop 두 번째 묶음) 모두에 들어간다. 훅 코드가 바뀌어 Codex 가 banker 훅의 신뢰를 한 번 다시 묻는다.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- `all-in-one`, `ultra-init`, `ultra-chain`, `visual-ralph`, `/banker:front-qa`: Codex 에서 구현 단계를 OMX `$ralph` 대신 banker `ralph` 로 부른다. Codex 의 `$ralplan` 에는 계획만 쓰고 멈추라고 넘긴다. OMX 0.21 의 ralplan 은 끝에 스스로 `$ultragoal` 로 넘어가기 때문이다. Claude Code 쪽은 바뀌지 않는다.
|
|
18
|
+
- `ralph-qa`, `ultraqa`: 공유 실행기 `gate-evidence.mjs`, `run-folder.mjs` 의 머리 주석에 Codex ralph 사본을 적는다(동작 변경 없음).
|
|
19
|
+
|
|
3
20
|
## [0.26.1] - 2026-11-05
|
|
4
21
|
|
|
5
22
|
### Changed
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[빠른 시작](#빠른-시작) · [워크플로 예시](#워크플로-사용-예시) · [구성](#구성) · [설치 상세](#설치-상세-npm--codex) · [설정 변경 지점](#설정-변경-지점-claude-code--codex) · [요구사항](#요구사항) · [업데이트 / 제거](#업데이트--제거) · [업데이트 확인](#업데이트-확인-및-사용량-카운팅) · [오프라인](#오프라인에서-쓰기) · [라이선스 / 서드파티](#라이선스--서드파티)
|
|
10
10
|
|
|
11
11
|
□ banker는 다음을 포함하는 Claude Code 및 Codex CLI 플러그인입니다.
|
|
12
|
-
- QA·보안 감사·문서·아키텍처·위키·미디어 작업과 의존성·개발환경(OS별) 설치를 아우르는 **스킬 72개 + 커맨드 2개**(총 74개 구성요소. Codex 는 Claude Code 전용 `ultraqa` 를 뺀 스킬 71개와 Codex 전용 스킬
|
|
12
|
+
- QA·보안 감사·문서·아키텍처·위키·미디어 작업과 의존성·개발환경(OS별) 설치를 아우르는 **스킬 72개 + 커맨드 2개**(총 74개 구성요소. Codex 는 Claude Code 전용 `ultraqa` 를 뺀 스킬 71개와 Codex 전용 스킬 4개)
|
|
13
13
|
- Claude Code 즉시 명령 `/graceful-pause`, `/progress`
|
|
14
14
|
|
|
15
15
|
□ 설치하면 스킬과 커맨드가 `/banker:<이름>` 네임스페이스로 노출됩니다.
|
|
@@ -101,6 +101,7 @@ Claude Code는 `/banker:<이름>`, Codex는 `$banker-<이름>`(플러그인 설
|
|
|
101
101
|
| `/banker:setup` | 구성요소·의존성 설치 오케스트레이터 (multi-select) |
|
|
102
102
|
| `/graceful-pause` | 작업 중에도 바로 실행되는 정지 요청. 지금 단계만 끝내고 멈춘 뒤 보고하고 지시를 기다림 (Claude Code 2.1.289 이상, mod. Codex 는 작업 중에 `$banker-graceful-pause` 를 보내면 실행 중인 도구 호출이 끝난 뒤 멈춤) |
|
|
103
103
|
| `$banker-teamwork` (Codex 전용 스킬) | 같은 기기의 Codex CLI 세션끼리 피어 협업을 켜고 끔(`on` 기본, `off`, `status`). 켜면 열린 세션과 새 세션이 모두 팀으로 동작하고, 역할은 각 세션에 말로 주며, 요청과 결과는 Codex 내장 `codex queue` 로 오감. 보낼 대상은 역할, `/rename` 으로 정한 세션 이름, 짧은ID 로 지정. 플러그인 길은 `$banker:teamwork` |
|
|
104
|
+
| `$banker-ralph` (Codex 전용 스킬) | 작업이 실제로 끝날 때까지 구현, 게이트 실행, 슬롭 점검, 새 맥락 검토를 반복. 실행기가 돌린 게이트 기록, 검토자 APPROVE, 슬롭 점검이 모두 지금 작업 트리와 맞을 때만 `RALPH COMPLETE`. Claude Code 의 ralph 처럼 실행이 끝나기 전에는 banker Stop 훅이 턴을 이어 감(OMX 와 Codex goal 설정 불필요). OMX 0.21 에서 없어진 `$ralph` 를 대신함. `all-in-one`, `ultra-init`, `ultra-chain`, `visual-ralph`, `front-qa` 가 Codex 에서 이것을 부름. 플러그인 길은 `$banker:ralph` |
|
|
104
105
|
| `/progress` | 진행 상황 패널을 켜고 끔(`show` 기본 토글, `on`, `off`). 맨 위는 이 세션이 쓰거나 읽은 마지막 계획 파일(`.omc/plans/*.md`, `docs/superpowers/plans/*.md`, `~/.claude/plans/*.md`, ralph `prd.json`, ultragoal `goals.json`) 전체. Notion 토글 목록처럼 제목이 그 아래 내용을, 목록 항목이 하위 항목을, 표와 코드 블록이 행과 줄을 품고, 처음에는 큰 제목만 접힌 채 보임. 체크박스는 완료와 대기, 제목은 그 아래 체크 수(`2/5`). 그 아래 단계는 할 일 목록(Claude 의 작업 목록이나 `mcp__banker__todo`), 없으면 이 세션의 요청이고, 하위 목록은 그 단계가 진행 중일 때 한 도구 호출 한 단계까지. 작업 중에도 바로 실행됨(2.1.289 이상에서 확인) (mod. Codex 는 `$banker-progress` 스킬이 지금 세션의 계획 트리, `update_plan` 단계, 최근 도구 호출을 텍스트로 보이고, tmux 안이면 `--pane` 으로 오른쪽 칸에 갱신) |
|
|
105
106
|
|
|
106
107
|
Claude Code 2.1.296 은 Claude 5 모델(Opus 5.5 등)에 작업 목록 도구(TaskCreate, TodoWrite)를 주지 않습니다. 그래서 `/progress` 를 켜면 모델에게 할 일 도구 `mcp__banker__todo` 를 줍니다(다음 요청부터). 모델이 여러 단계 작업을 시작할 때 할 일 목록을 보내면 그 목록이 단계가 됩니다. 한 단계로 끝나는 일에는 쓰지 않으므로 그때는 요청이 단계입니다. 내장 작업 목록 도구가 있는 세션(옛 모델, `CLAUDE_CODE_ENABLE_TODO_TOOLS=1`)에는 이 도구를 더하지 않습니다. 도구는 `/progress off` 뒤에도 세션이 끝날 때까지 남고, 그동안 부르면 패널이 꺼져 있다고 답합니다.
|
|
@@ -233,14 +234,14 @@ banker uninstall # 제거
|
|
|
233
234
|
|
|
234
235
|
- `--scope project` 로 프로젝트 로컬(`./.codex`)에 설치하고, `--dry-run` 으로 미리 볼 수 있습니다.
|
|
235
236
|
- non-root 전용입니다(전역 sudo 설치 시 root 소유 파일을 방지). postinstall이 없으므로 `banker setup` 을 직접 실행합니다.
|
|
236
|
-
- Codex에는 스킬 71개와 Codex 전용 스킬
|
|
237
|
+
- Codex에는 스킬 71개와 Codex 전용 스킬 4개(`graceful-pause`, `progress`, `ralph`, `teamwork`)가 `~/.codex/skills/banker-<name>/` 에, 커맨드 2개가 `~/.codex/prompts/banker-<name>.md` 에 설치됩니다(`codex/manifest.json`). \
|
|
237
238
|
`CODEX_HOME` 을 설정했으면 `~/.codex` 대신 그 폴더에 설치합니다(Codex 가 읽는 곳). \
|
|
238
239
|
`lineage`, `progress`, 사용량 카운팅은 Codex 의 대화 기록(rollout)을 읽습니다. Codex 는 이 형식을 공개 약속으로 내놓지 않아 업데이트로 바뀔 수 있습니다. \
|
|
239
240
|
Codex 를 업데이트한 뒤에는 `banker doctor` 로 형식을 점검합니다. 읽지 못하면 lineage 는 빈 페이지 대신 오류로 멈추고, progress 는 경고 줄을 보이며, 카운팅은 다음 세션 시작 때 경고를 한 번 보입니다. \
|
|
240
241
|
Codex 0.144 이상에는 사용자 프롬프트가 없으므로 커맨드 2개는 스킬로도 설치되어 `$banker-front-qa`, `$banker-setup` 으로 부릅니다. \
|
|
241
242
|
디렉터리명과 일치하도록 프론트매터 `name:` 이 `banker-<name>` 으로 재작성되어 Codex가 `banker-<name>` 으로 인식합니다.
|
|
242
243
|
- Codex 플러그인 설치(`codex plugin add banker@banker-plugins`)는 저장소를 `~/.codex/plugins/cache/banker-plugins/banker/<판>/` 에 두고 `.codex-plugin/plugin.json` 을 읽습니다. \
|
|
243
|
-
스킬 71개, Codex 전용 스킬
|
|
244
|
+
스킬 71개, Codex 전용 스킬 4개, 커맨드 2개(스킬로)가 `$banker:<name>` 으로 보이고, 업데이트 알림, 사용량 카운팅, banker `ralph` 계속 훅은 `codex/hooks.json` 에서 옵니다. \
|
|
244
245
|
Codex 는 세션을 시작할 때 마켓플레이스를 스스로 올려 새 판을 설치합니다. 훅 명령 앞에는 훅이 실행하는 파일의 해시가 붙어 있어, 훅 코드가 바뀐 판에서는 `/hooks` 가 다시 신뢰를 묻고 신뢰하기 전에는 훅이 돌지 않습니다. 훅 코드가 그대로인 판은 신뢰도 그대로입니다. 스킬 파일은 신뢰 확인 없이 새 판으로 바뀝니다. \
|
|
245
246
|
obsidizer 훅은 npm 설치와 같이 `obsidizer --enable` 할 때만 사용자 범위에 설치됩니다(셸 호출마다 node 를 띄우므로). \
|
|
246
247
|
훅은 Codex 샌드박스 밖에서 돌므로, PATH 의 node 가 임시 폴더나 작업 폴더 안에 있으면(샌드박스가 바꿀 수 있는 곳) 아무것도 돌리지 않고 그 이유를 세션 시작 알림으로 보입니다. 작업 폴더가 홈 폴더 자체이면 홈 아래의 node(nvm 등)는 그대로 씁니다. 훅 스크립트, 플러그인 폴더, node 가 `config.toml` 의 `[sandbox_workspace_write] writable_roots` 아래(그 항목 맨 위 `.codex` 제외)면 훅은 다른 일을 하지 않고 그 이유를 한 줄로 알립니다. 이 검사는 훅 파일 안에서 하므로, 샌드박스가 이미 그 파일을 고친 뒤에는 막지 못합니다. writable_roots 에 Codex 홈이나 node 가 든 폴더를 넣지 마십시오. \
|
|
@@ -268,7 +269,8 @@ banker uninstall # 제거
|
|
|
268
269
|
- `payload-mon` 은 Codex에서 실행해도 같은 머신의 Claude Code HUD 래퍼만 켜고 끕니다. \
|
|
269
270
|
32MB 요청 한도는 Claude Code 의 것이라 Codex 자신의 요청 크기는 재지 않습니다.
|
|
270
271
|
- `omc-patch` 도 Codex에서 실행하면 같은 머신의 Claude Code OMC 플러그인 캐시(`~/.claude/plugins/`)만 다룹니다. Codex 의 OMX 는 건드리지 않습니다.
|
|
271
|
-
- Codex에선 OMC 대신 **oh-my-codex(OMX)** 의 동명 스킬(ralplan·
|
|
272
|
+
- Codex에선 OMC 대신 **oh-my-codex(OMX)** 의 동명 스킬(ralplan·hud 등)과 `codex mcp`·내장 `/copy` 를 사용합니다(Codex는 `omx setup` 전제). \
|
|
273
|
+
`ralph` 만 예외입니다. OMX 0.21 이 `$ralph` 를 없앴으므로 banker 스킬은 Codex 에서 banker 의 Codex 전용 `ralph`(`$banker-ralph`, 플러그인 길 `$banker:ralph`)를 부릅니다.
|
|
272
274
|
- `all-in-one`·`ultra-init` 의 마지막 QA 게이트는 두 런타임 모두 banker `ralph-qa` 입니다. 독립 검토자 3명 이상이 실행기가 돌린 `--qa` 목표의 게이트 기록으로 판정합니다.
|
|
273
275
|
- 설치기는 `~/.codex/AGENTS.md` 를 건드리지 않습니다(omx가 재생성하므로 `~/.codex/skills/` 자동 검색에 의존). \
|
|
274
276
|
예외는 사용자가 켠 `tone-compact` 와 `anti-drm` 입니다. 각각 그 파일 끝에 `USER:OMX:POLICY` 로 감싼 블록 1개를 넣고, `off` 로 뺍니다. \
|
|
@@ -285,7 +287,7 @@ banker uninstall # 제거
|
|
|
285
287
|
|---|---|---|
|
|
286
288
|
| Claude Code | 플러그인 레지스트리 | `claude plugin marketplace add` + 설치 → `/banker:*` (`settings.json` 직접수정 없음) |
|
|
287
289
|
| Codex CLI | `~/.codex/skills/banker-*/`, `~/.codex/prompts/banker-*.md` | 기존 `banker-*` 정리 후 복사(멱등). `AGENTS.md`·`config.toml` 미변경 |
|
|
288
|
-
| Codex CLI | `~/.codex/hooks.json`, `~/.codex/hooks-custom/banker/` | 업데이트 알림(`SessionStart`)
|
|
290
|
+
| Codex CLI | `~/.codex/hooks.json`, `~/.codex/hooks-custom/banker/` | 업데이트 알림(`SessionStart`), 사용량 카운팅(`Stop`), banker `ralph` 계속(`Stop`, 실행을 시작한 세션이 그 실행을 끝내기 전에는 턴을 이어 감, `BANKER_NO_RALPH_CONTINUE` 로 끔) 훅. 사본은 작업 폴더 밖에 두고, 이벤트마다 맨 뒤에 묶음 1개를 더하며, 쓰기 전 `hooks.json.banker.bak` 백업. `banker hooks status`, `banker hooks enable <이름>`, `banker hooks disable <이름>` 으로 확인하고 켜고 끔. Codex 를 다시 열 때 Hooks need review 창이나 `/hooks` 에서 신뢰해야 돎. Codex 홈(`CODEX_HOME`)이 지금 폴더나 그 저장소 안이면 샌드박스가 고칠 수 있어 훅을 설치하지 않음 |
|
|
289
291
|
|
|
290
292
|
**플러그인 자체 훅 — `hooks/hooks.json` (설치 시 Claude Code에서 자동 발화)**
|
|
291
293
|
|
|
@@ -326,7 +328,8 @@ banker uninstall # 제거
|
|
|
326
328
|
- Claude Code (마켓플레이스 경로): 스킬이 `/banker:*` 로 동작.
|
|
327
329
|
- 또는 Node.js ≥ 16.7 (npm 전역 설치 경로): `banker` CLI 제공.
|
|
328
330
|
- 일부 스킬은 별도 의존성이 필요하며 `/banker:setup` 으로 설치합니다. 의존성이 없으면 각 스킬이 실행 전에 설치부터 안내합니다.
|
|
329
|
-
- `all-in-one`, `ultra-chain`, `ultra-init`, `/banker:front-qa`: oh-my-claudecode(OMC) 5 와 같은 버전대의 `omc` CLI(`ralph` 가 `omc ralph verify` 를 부르므로 4.x CLI 로는 멈춤). Codex에서는 OMX
|
|
331
|
+
- `all-in-one`, `ultra-chain`, `ultra-init`, `/banker:front-qa`: oh-my-claudecode(OMC) 5 와 같은 버전대의 `omc` CLI(`ralph` 가 `omc ralph verify` 를 부르므로 4.x CLI 로는 멈춤). Codex에서는 계획 단계에 OMX(`$ralplan`)가 필요하고, 구현 단계는 banker 의 Codex 전용 `ralph` 라 OMX 가 필요 없습니다. 마지막 QA 의 `ralph-qa` 는 banker 에 들어 있습니다.
|
|
332
|
+
- `ralph`(Codex 전용): git 저장소. 기록은 저장소 밖 게이트 폴더에 두므로 `workspace-write` 샌드박스에서는 실행기 호출에 승인이 필요할 수 있습니다.
|
|
330
333
|
- `audit-web-page`, `play-qa`, `ultra-ui-qa`: playwright. `3d-intro-build` 의 독립 검증(`verify-intro.mjs`)도 Node 용 playwright 패키지와 Chromium 을 씁니다(`npm i -g playwright && npx playwright install chromium`).
|
|
331
334
|
- `payload-mon`: OMC 커스텀 HUD 래퍼(`setup-omc-hud` 로 설치).
|
|
332
335
|
- `omc-patch`: Node 18 이상과 `git`(OMC 마켓플레이스 고정과 확인에 사용).
|
package/bin/lib/codex-hooks.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import path from 'node:path';
|
|
|
20
20
|
export const HOOKS = {
|
|
21
21
|
update: { event: 'SessionStart', script: 'hooks/update-notify.mjs', args: ['--codex'] },
|
|
22
22
|
count: { event: 'Stop', script: 'hooks/telemetry-count-codex.mjs', args: [] },
|
|
23
|
+
ralph: { event: 'Stop', script: 'hooks/ralph-continue-codex.mjs', args: [] },
|
|
23
24
|
obsidize: { event: 'PostToolUse', script: 'hooks/obsidize-hook.mjs', args: ['--codex'], matcher: 'Bash' },
|
|
24
25
|
};
|
|
25
26
|
// 사본으로 옮길 파일(훅 스크립트와 그 의존 파일). 상대 경로를 그대로 둬 '../bin/lib/...' 가져오기가 맞는다.
|
|
@@ -34,8 +35,13 @@ export const PAYLOAD = [
|
|
|
34
35
|
'hooks/telemetry-count-codex.mjs',
|
|
35
36
|
'hooks/obsidize-hook.mjs',
|
|
36
37
|
'skills/obsidizer/obsidize.mjs',
|
|
38
|
+
'hooks/ralph-continue-codex.mjs',
|
|
39
|
+
'codex/skills/ralph/scripts/ralph-state.mjs',
|
|
40
|
+
'codex/skills/ralph/scripts/gate-evidence.mjs',
|
|
41
|
+
'codex/skills/ralph/scripts/run-folder.mjs',
|
|
37
42
|
];
|
|
38
|
-
|
|
43
|
+
// ralph: the Codex ralph keeps its turn going until its run ends (as the OMC ralph's Stop hook does in Claude Code).
|
|
44
|
+
export const DEFAULT_HOOKS = ['update', 'count', 'ralph'];
|
|
39
45
|
|
|
40
46
|
// Codex 처럼 상대 경로 CODEX_HOME 은 지금 폴더 기준으로 푼다.
|
|
41
47
|
export function codexPaths(env = process.env) {
|
|
@@ -19,7 +19,9 @@ description: "스펙 파일(기본 `docs/note.md`)을 바탕으로 프론트엔
|
|
|
19
19
|
omc ralph verify --help 2>&1 | grep -q 'Usage: omc ralph verify' && echo "omc ok" || echo "omc CLI 없음 또는 낡음"
|
|
20
20
|
```
|
|
21
21
|
- 실패하면 `/banker:setup` → oh-my-codex(OMX)(또는 `setup-omx` 스킬)로 먼저 구성한 뒤 이어서 진행. 점검을 통과하기 전에는 절차 1을 시작하지 않는다.
|
|
22
|
-
- **런타임 매핑:** 아래 절차의 `/ralplan
|
|
22
|
+
- **런타임 매핑:** 아래 절차의 `/ralplan` 은 Claude Code에선 OMC 스킬, **Codex에선 oh-my-codex(OMX)의 동명 스킬**이다(Codex는 OMC 대신 OMX 전제). `/ralph` 는 Claude Code에선 OMC 스킬, **Codex에선 banker 의 Codex 전용 `ralph`** 다(npm 길 `$banker-ralph`, 플러그인 길 `$banker:ralph`. OMX 0.21 이 `$ralph` 를 없애 OMX 것은 부르지 않음). 각 런타임에서 사용 가능한 형태로 호출한다.
|
|
23
|
+
- **Codex 의 2단계:** OMX 0.21 의 `$ralplan` 은 끝에 스스로 `$ultragoal` 로 넘어간다. 계획만 쓰고 멈추라고 넘긴다(`$ultragoal`, `$ralph`, `$team`, 구현으로 넘어가지 않음). 계획은 `.omx/plans/` 에 둔다.
|
|
24
|
+
- **Codex 의 3~6단계:** banker `ralph` 의 `SKILL.md`(스킬로 설치된 때: npm 길 `<S>/../banker-ralph/SKILL.md`, 플러그인 길 `<S>/../../skills/ralph/SKILL.md`, `<S>` 는 이 커맨드 스킬 폴더)를 따른다. 스테이지와 스펙 항목을 수용 기준으로, typecheck 와 vitest 와 parity-gate 를 게이트(게이트마다 명령 하나)로 넘긴다. 그 ralph 가 새 맥락 검토, 슬롭 점검, 회귀 확인을 함께 하고 `RALPH COMPLETE` 줄을 낸다. critic 은 ralph 의 검토자가 맡는다.
|
|
23
25
|
|
|
24
26
|
## 절차 (CLAUDE.md: 계획 없는 즉시실행 금지)
|
|
25
27
|
|
package/codex/hooks.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "banker hooks for the Codex plugin (codex plugin add banker@banker-plugins), written by scripts/sync-codex-plugin.js. Codex reads this file instead of hooks/hooks.json because .codex-plugin/plugin.json names it. update notice (SessionStart, opt-out BANKER_NO_UPDATE_CHECK), usage counting (Stop, opt-out BANKER_NO_TELEMETRY). The obsidizer hook is installed only by obsidizer --enable. Codex runs them only after you trust them in /hooks, and outside its sandbox: each command runs the node PATH names (a multicall shim such as Volta or mise, or a .cmd shim on Windows: the node binary it names) only when its real path is absolute and outside the temp folder and the session folder (a session in the home folder itself excepted), from the plugin folder when that is outside them too, and otherwise says why it skipped. A hook whose script, plugin folder or node is under a config.toml [sandbox_workspace_write] writable_roots entry (not that entry's top .codex) does nothing but say why. PATH entries under a network mount outside the home folder are not read. Each command starts with a hash of the hook files (banker-hooks-rev), so new hook code asks for trust again. command is for sh, commandWindows for PowerShell; PLUGIN_ROOT is the installed plugin folder.",
|
|
2
|
+
"description": "banker hooks for the Codex plugin (codex plugin add banker@banker-plugins), written by scripts/sync-codex-plugin.js. Codex reads this file instead of hooks/hooks.json because .codex-plugin/plugin.json names it. update notice (SessionStart, opt-out BANKER_NO_UPDATE_CHECK), usage counting (Stop, opt-out BANKER_NO_TELEMETRY), keeping a banker ralph run going until it ends (Stop, only for the session that started it, opt-out BANKER_NO_RALPH_CONTINUE). The obsidizer hook is installed only by obsidizer --enable. Codex runs them only after you trust them in /hooks, and outside its sandbox: each command runs the node PATH names (a multicall shim such as Volta or mise, or a .cmd shim on Windows: the node binary it names) only when its real path is absolute and outside the temp folder and the session folder (a session in the home folder itself excepted), from the plugin folder when that is outside them too, and otherwise says why it skipped. A hook whose script, plugin folder or node is under a config.toml [sandbox_workspace_write] writable_roots entry (not that entry's top .codex) does nothing but say why. PATH entries under a network mount outside the home folder are not read. Each command starts with a hash of the hook files (banker-hooks-rev), so new hook code asks for trust again. command is for sh, commandWindows for PowerShell; PLUGIN_ROOT is the installed plugin folder.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"SessionStart": [
|
|
5
5
|
{
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": ": banker-hooks-rev
|
|
10
|
-
"commandWindows": "$null = 'banker-hooks-rev
|
|
9
|
+
"command": ": banker-hooks-rev d1402c48baa8; x() { m=$(printf '%s' \"banker hooks skipped: $1\" | command -p tr -d '\\000-\\037\"\\\\'); printf '{\"systemMessage\":\"%s\"}\\n' \"$m\"; exit 0; }; [ -n \"$PLUGIN_ROOT\" ] || x \"PLUGIN_ROOT is not set\"; p=$(pwd -P); [ -n \"$p\" ] && [ \"$p\" != / ] || x \"the session folder is /\"; u=$(cd /tmp 2>/dev/null && pwd -P); t=$(cd \"${TMPDIR:-/tmp}\" 2>/dev/null && pwd -P); h=; case \"$HOME\" in /?*) h=$(cd \"$HOME\" 2>/dev/null && pwd -P);; esac; w() { for z in \"$u\" \"$t\"; do [ -n \"$z\" ] && case \"$1/\" in \"$z\"/*) return 0;; esac; done; [ \"$p\" = \"$h\" ] && return 1; case \"$1/\" in \"$p\"/*) return 0;; esac; return 1; }; pr=$(cd -P \"$PLUGIN_ROOT\" 2>/dev/null && pwd -P) || x \"cannot enter the plugin folder\"; w \"$pr\" && x \"the plugin folder is in a temp folder or the session folder ($pr)\"; A='function dec(s, o, i, c) { o = \"\"; for (i = 1; i <= length(s); i++) { c = substr(s, i, 1); if (c == \"\\\\\" && substr(s, i + 1, 3) ~ /^[0-7][0-7][0-7]$/) { o = o sprintf(\"%c\", substr(s, i + 1, 1) * 64 + substr(s, i + 2, 1) * 8 + substr(s, i + 3, 1)); i += 3 } else o = o c }; return o } mac { i = index($0, \" on \"); j = 0; for (k = length($0); k > i; k--) if (substr($0, k, 2) == \" (\") { j = k; break }; if (!i || !j) next; m = substr($0, i + 4, j - i - 4); t = substr($0, j + 2); sub(/[,)].*/, \"\", t) } !mac { m = dec($2); t = $3 } (t ~ /^(nfs|nfs4|cifs|smbfs|smb3|afpfs|webdav|davfs|autofs|ceph|glusterfs|lustre|9p)$/ || t ~ /^fuse\\./) && m !~ /:/ { printf \"%s:\", m }'; if [ -r '/proc/self/mounts' ]; then M=$(LC_ALL=C command -p awk \"$A\" '/proc/self/mounts' 2>/dev/null); else M=$('/sbin/mount' 2>/dev/null | LC_ALL=C command -p awk -v mac=1 \"$A\" 2>/dev/null); fi; nm() { for z in $M; do case \"$1/\" in \"$z\"/*) return 0;; esac; done; return 1; }; hm() { for z in \"$h\" \"$HOME\"; do case \"$z\" in /?*) case \"$1/\" in \"$z\"/*) return 0;; esac;; esac; done; return 1; }; set -f; o=$IFS; IFS=:; a=; q=; for v in $PATH; do case \"$v\" in /*) ;; *) a=$a${a:+:}$v; continue;; esac; nm \"$v\" && ! hm \"$v\" && continue; a=$a${a:+:}$v; y=$(cd \"$v\" 2>/dev/null && pwd -P) || continue; w \"$y\" || q=$q${q:+:}$v; done; n=; [ -n \"$a\" ] && n=$(PATH=$a; command -v node) || for d in /opt/homebrew/bin /usr/local/bin; do nm \"$d\" && continue; [ -x \"$d/node\" ] && n=$d/node && break; done; IFS=$o; set +f; [ -n \"$n\" ] || x \"node is not on PATH (a Codex started from the desktop may not have the shell's PATH)\"; case \"$n\" in /*) ;; *) x \"node on PATH is not an absolute path ($n)\";; esac; PATH=${q:-/usr/bin:/bin}; export PATH; cd \"$pr\" || x \"cannot enter the plugin folder\"; rp() { realpath \"$1\" 2>/dev/null || readlink -f \"$1\" 2>/dev/null || { f=$1; i=0; while [ -L \"$f\" ]; do i=$((i+1)); [ \"$i\" -le 40 ] || return 1; l=$(readlink \"$f\") || return 1; case \"$l\" in /*) f=$l;; *) f=${f%/*}/$l;; esac; done; [ -e \"$f\" ] || return 1; d=$(cd -P \"${f%/*}/\" 2>/dev/null && pwd -P) || return 1; printf '%s\\n' \"${d%/}/${f##*/}\"; }; }; r=$(rp \"$n\") || x \"cannot resolve the real path of $n\"; k() { case \"$1\" in /*) ;; *) x \"node path is not absolute ($1)\";; esac; for d in \"$u\" \"$t\"; do [ -n \"$d\" ] && case \"$1\" in \"$d\"/*) x \"node is in a temp folder ($1)\";; esac; done; [ \"$p\" = \"$h\" ] || case \"$1\" in \"$p\"/*) x \"node is in the session folder ($1)\";; esac; }; case \"${r##*/}\" in node) k \"$r\"; e=$r;; *) k \"$n\"; k \"$r\"; e=$(\"$n\" -p 'require(\"fs\").realpathSync(process.execPath)' 2>/dev/null) || x \"the node shim $n did not name its node\"; k \"$e\";; esac; \"$e\" \"$pr/hooks/run.cjs\" \"$pr/hooks/update-notify.mjs\" --codex --plugin",
|
|
10
|
+
"commandWindows": "$null = 'banker-hooks-rev d1402c48baa8'; function x($m) { Write-Output (ConvertTo-Json -Compress @{ systemMessage = \"banker hooks skipped: $m\" }); exit 0 }; if (-not $env:PLUGIN_ROOT) { x 'PLUGIN_ROOT is not set' }; function G($p) { try { (Get-Item -LiteralPath $p -Force -ErrorAction Stop).FullName } catch { try { [IO.Path]::GetFullPath($p) } catch { $p } } }; $sb = @{}; foreach ($l in @(& (Join-Path ([Environment]::SystemDirectory) 'subst.exe') 2>$null)) { if ($l -match '^([A-Za-z]:)\\\\: => (.+)$') { $sb[$matches[1].ToUpper()] = $matches[2] } }; function L($p) { if (-not $p) { return '' }; $p = G $p; for ($i = 0; $i -lt 32; $i++) { $d2 = if ($p.Length -ge 2) { $p.Substring(0, 2).ToUpper() } else { '' }; if ($sb.Contains($d2)) { $p = G ($sb[$d2].TrimEnd('\\') + $p.Substring(2)); continue }; $c = $p; $t = $null; while ($c) { $it = Get-Item -LiteralPath $c -Force -ErrorAction SilentlyContinue; if ($it -and $it.LinkType -in 'SymbolicLink', 'Junction' -and @($it.Target)[0]) { $t = [string]@($it.Target)[0]; break }; $c = Split-Path -LiteralPath $c }; if (-not $t) { break }; if (-not [IO.Path]::IsPathRooted($t)) { $t = Join-Path (Split-Path -LiteralPath $c) $t }; $p = G ($t.TrimEnd('\\') + $p.Substring($c.Length)) }; $p.TrimEnd('\\') }; $ds = @($env:TEMP, $env:TMP); if ($env:LOCALAPPDATA) { $ds += \"$env:LOCALAPPDATA\\Temp\" }; $w = L $PWD.Path; if ($w -ne (L $env:USERPROFILE)) { $ds += $w }; $ds = @($ds | Where-Object { $_ } | ForEach-Object { L $_ } | Where-Object { $_ }); function u($f) { $f = L $f; foreach ($d in $ds) { if ($f -eq $d -or $f.StartsWith($d + '\\', 'OrdinalIgnoreCase')) { return $true } }; $false }; function k($f) { if (u $f) { x \"node is in a temp folder or the session folder ($(L $f))\" }; L $f }; $pr = L $env:PLUGIN_ROOT; if (u $pr) { x \"the plugin folder is in a temp folder or the session folder ($pr)\" }; $nd = @(@($env:PATH -split ';') | ForEach-Object { [Environment]::ExpandEnvironmentVariables($_.Trim().Trim('\"')) } | Where-Object { $_ -match '^[A-Za-z]:\\\\' -and ([IO.DriveInfo]::new($_.Substring(0, 1))).DriveType -ne 'Network' }); $n = $null; foreach ($d in $nd) { foreach ($e in 'node.exe', 'node.bat', 'node.cmd') { $f = Join-Path $d $e; if (Test-Path -LiteralPath $f -PathType Leaf) { $n = $f; break } }; if ($n) { break } }; if (-not $n) { x 'node is not on PATH' }; $env:PATH = ($nd | Where-Object { -not (u $_) }) -join ';'; $r = k $n; if (-not (Set-Location -LiteralPath $pr -PassThru -ErrorAction SilentlyContinue)) { x 'cannot enter the plugin folder' }; if ($r -notmatch '\\.exe$') { $e = & $r -p process.execPath 2>$null | Select-Object -Last 1; if (-not $e -or $e -notmatch '\\.exe$' -or -not (Test-Path -LiteralPath $e -PathType Leaf)) { x \"the node shim $r did not name its node\" }; $r = k $e }; & $r \"$pr\\hooks\\run.cjs\" \"$pr\\hooks\\update-notify.mjs\" --codex --plugin",
|
|
11
11
|
"timeout": 5
|
|
12
12
|
}
|
|
13
13
|
]
|
|
@@ -18,8 +18,18 @@
|
|
|
18
18
|
"hooks": [
|
|
19
19
|
{
|
|
20
20
|
"type": "command",
|
|
21
|
-
"command": ": banker-hooks-rev
|
|
22
|
-
"commandWindows": "$null = 'banker-hooks-rev
|
|
21
|
+
"command": ": banker-hooks-rev d1402c48baa8; x() { printf '%s\\n' \"banker hooks skipped: $1\" >&2; exit 0; }; [ -n \"$PLUGIN_ROOT\" ] || x \"PLUGIN_ROOT is not set\"; p=$(pwd -P); [ -n \"$p\" ] && [ \"$p\" != / ] || x \"the session folder is /\"; u=$(cd /tmp 2>/dev/null && pwd -P); t=$(cd \"${TMPDIR:-/tmp}\" 2>/dev/null && pwd -P); h=; case \"$HOME\" in /?*) h=$(cd \"$HOME\" 2>/dev/null && pwd -P);; esac; w() { for z in \"$u\" \"$t\"; do [ -n \"$z\" ] && case \"$1/\" in \"$z\"/*) return 0;; esac; done; [ \"$p\" = \"$h\" ] && return 1; case \"$1/\" in \"$p\"/*) return 0;; esac; return 1; }; pr=$(cd -P \"$PLUGIN_ROOT\" 2>/dev/null && pwd -P) || x \"cannot enter the plugin folder\"; w \"$pr\" && x \"the plugin folder is in a temp folder or the session folder ($pr)\"; A='function dec(s, o, i, c) { o = \"\"; for (i = 1; i <= length(s); i++) { c = substr(s, i, 1); if (c == \"\\\\\" && substr(s, i + 1, 3) ~ /^[0-7][0-7][0-7]$/) { o = o sprintf(\"%c\", substr(s, i + 1, 1) * 64 + substr(s, i + 2, 1) * 8 + substr(s, i + 3, 1)); i += 3 } else o = o c }; return o } mac { i = index($0, \" on \"); j = 0; for (k = length($0); k > i; k--) if (substr($0, k, 2) == \" (\") { j = k; break }; if (!i || !j) next; m = substr($0, i + 4, j - i - 4); t = substr($0, j + 2); sub(/[,)].*/, \"\", t) } !mac { m = dec($2); t = $3 } (t ~ /^(nfs|nfs4|cifs|smbfs|smb3|afpfs|webdav|davfs|autofs|ceph|glusterfs|lustre|9p)$/ || t ~ /^fuse\\./) && m !~ /:/ { printf \"%s:\", m }'; if [ -r '/proc/self/mounts' ]; then M=$(LC_ALL=C command -p awk \"$A\" '/proc/self/mounts' 2>/dev/null); else M=$('/sbin/mount' 2>/dev/null | LC_ALL=C command -p awk -v mac=1 \"$A\" 2>/dev/null); fi; nm() { for z in $M; do case \"$1/\" in \"$z\"/*) return 0;; esac; done; return 1; }; hm() { for z in \"$h\" \"$HOME\"; do case \"$z\" in /?*) case \"$1/\" in \"$z\"/*) return 0;; esac;; esac; done; return 1; }; set -f; o=$IFS; IFS=:; a=; q=; for v in $PATH; do case \"$v\" in /*) ;; *) a=$a${a:+:}$v; continue;; esac; nm \"$v\" && ! hm \"$v\" && continue; a=$a${a:+:}$v; y=$(cd \"$v\" 2>/dev/null && pwd -P) || continue; w \"$y\" || q=$q${q:+:}$v; done; n=; [ -n \"$a\" ] && n=$(PATH=$a; command -v node) || for d in /opt/homebrew/bin /usr/local/bin; do nm \"$d\" && continue; [ -x \"$d/node\" ] && n=$d/node && break; done; IFS=$o; set +f; [ -n \"$n\" ] || x \"node is not on PATH (a Codex started from the desktop may not have the shell's PATH)\"; case \"$n\" in /*) ;; *) x \"node on PATH is not an absolute path ($n)\";; esac; PATH=${q:-/usr/bin:/bin}; export PATH; cd \"$pr\" || x \"cannot enter the plugin folder\"; rp() { realpath \"$1\" 2>/dev/null || readlink -f \"$1\" 2>/dev/null || { f=$1; i=0; while [ -L \"$f\" ]; do i=$((i+1)); [ \"$i\" -le 40 ] || return 1; l=$(readlink \"$f\") || return 1; case \"$l\" in /*) f=$l;; *) f=${f%/*}/$l;; esac; done; [ -e \"$f\" ] || return 1; d=$(cd -P \"${f%/*}/\" 2>/dev/null && pwd -P) || return 1; printf '%s\\n' \"${d%/}/${f##*/}\"; }; }; r=$(rp \"$n\") || x \"cannot resolve the real path of $n\"; k() { case \"$1\" in /*) ;; *) x \"node path is not absolute ($1)\";; esac; for d in \"$u\" \"$t\"; do [ -n \"$d\" ] && case \"$1\" in \"$d\"/*) x \"node is in a temp folder ($1)\";; esac; done; [ \"$p\" = \"$h\" ] || case \"$1\" in \"$p\"/*) x \"node is in the session folder ($1)\";; esac; }; case \"${r##*/}\" in node) k \"$r\"; e=$r;; *) k \"$n\"; k \"$r\"; e=$(\"$n\" -p 'require(\"fs\").realpathSync(process.execPath)' 2>/dev/null) || x \"the node shim $n did not name its node\"; k \"$e\";; esac; \"$e\" \"$pr/hooks/run.cjs\" \"$pr/hooks/telemetry-count-codex.mjs\"",
|
|
22
|
+
"commandWindows": "$null = 'banker-hooks-rev d1402c48baa8'; function x($m) { [Console]::Error.WriteLine(\"banker hooks skipped: $m\"); exit 0 }; if (-not $env:PLUGIN_ROOT) { x 'PLUGIN_ROOT is not set' }; function G($p) { try { (Get-Item -LiteralPath $p -Force -ErrorAction Stop).FullName } catch { try { [IO.Path]::GetFullPath($p) } catch { $p } } }; $sb = @{}; foreach ($l in @(& (Join-Path ([Environment]::SystemDirectory) 'subst.exe') 2>$null)) { if ($l -match '^([A-Za-z]:)\\\\: => (.+)$') { $sb[$matches[1].ToUpper()] = $matches[2] } }; function L($p) { if (-not $p) { return '' }; $p = G $p; for ($i = 0; $i -lt 32; $i++) { $d2 = if ($p.Length -ge 2) { $p.Substring(0, 2).ToUpper() } else { '' }; if ($sb.Contains($d2)) { $p = G ($sb[$d2].TrimEnd('\\') + $p.Substring(2)); continue }; $c = $p; $t = $null; while ($c) { $it = Get-Item -LiteralPath $c -Force -ErrorAction SilentlyContinue; if ($it -and $it.LinkType -in 'SymbolicLink', 'Junction' -and @($it.Target)[0]) { $t = [string]@($it.Target)[0]; break }; $c = Split-Path -LiteralPath $c }; if (-not $t) { break }; if (-not [IO.Path]::IsPathRooted($t)) { $t = Join-Path (Split-Path -LiteralPath $c) $t }; $p = G ($t.TrimEnd('\\') + $p.Substring($c.Length)) }; $p.TrimEnd('\\') }; $ds = @($env:TEMP, $env:TMP); if ($env:LOCALAPPDATA) { $ds += \"$env:LOCALAPPDATA\\Temp\" }; $w = L $PWD.Path; if ($w -ne (L $env:USERPROFILE)) { $ds += $w }; $ds = @($ds | Where-Object { $_ } | ForEach-Object { L $_ } | Where-Object { $_ }); function u($f) { $f = L $f; foreach ($d in $ds) { if ($f -eq $d -or $f.StartsWith($d + '\\', 'OrdinalIgnoreCase')) { return $true } }; $false }; function k($f) { if (u $f) { x \"node is in a temp folder or the session folder ($(L $f))\" }; L $f }; $pr = L $env:PLUGIN_ROOT; if (u $pr) { x \"the plugin folder is in a temp folder or the session folder ($pr)\" }; $nd = @(@($env:PATH -split ';') | ForEach-Object { [Environment]::ExpandEnvironmentVariables($_.Trim().Trim('\"')) } | Where-Object { $_ -match '^[A-Za-z]:\\\\' -and ([IO.DriveInfo]::new($_.Substring(0, 1))).DriveType -ne 'Network' }); $n = $null; foreach ($d in $nd) { foreach ($e in 'node.exe', 'node.bat', 'node.cmd') { $f = Join-Path $d $e; if (Test-Path -LiteralPath $f -PathType Leaf) { $n = $f; break } }; if ($n) { break } }; if (-not $n) { x 'node is not on PATH' }; $env:PATH = ($nd | Where-Object { -not (u $_) }) -join ';'; $r = k $n; if (-not (Set-Location -LiteralPath $pr -PassThru -ErrorAction SilentlyContinue)) { x 'cannot enter the plugin folder' }; if ($r -notmatch '\\.exe$') { $e = & $r -p process.execPath 2>$null | Select-Object -Last 1; if (-not $e -or $e -notmatch '\\.exe$' -or -not (Test-Path -LiteralPath $e -PathType Leaf)) { x \"the node shim $r did not name its node\" }; $r = k $e }; & $r \"$pr\\hooks\\run.cjs\" \"$pr\\hooks\\telemetry-count-codex.mjs\"",
|
|
23
|
+
"timeout": 5
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": ": banker-hooks-rev d1402c48baa8; x() { printf '%s\\n' \"banker hooks skipped: $1\" >&2; exit 0; }; [ -n \"$PLUGIN_ROOT\" ] || x \"PLUGIN_ROOT is not set\"; p=$(pwd -P); [ -n \"$p\" ] && [ \"$p\" != / ] || x \"the session folder is /\"; u=$(cd /tmp 2>/dev/null && pwd -P); t=$(cd \"${TMPDIR:-/tmp}\" 2>/dev/null && pwd -P); h=; case \"$HOME\" in /?*) h=$(cd \"$HOME\" 2>/dev/null && pwd -P);; esac; w() { for z in \"$u\" \"$t\"; do [ -n \"$z\" ] && case \"$1/\" in \"$z\"/*) return 0;; esac; done; [ \"$p\" = \"$h\" ] && return 1; case \"$1/\" in \"$p\"/*) return 0;; esac; return 1; }; pr=$(cd -P \"$PLUGIN_ROOT\" 2>/dev/null && pwd -P) || x \"cannot enter the plugin folder\"; w \"$pr\" && x \"the plugin folder is in a temp folder or the session folder ($pr)\"; A='function dec(s, o, i, c) { o = \"\"; for (i = 1; i <= length(s); i++) { c = substr(s, i, 1); if (c == \"\\\\\" && substr(s, i + 1, 3) ~ /^[0-7][0-7][0-7]$/) { o = o sprintf(\"%c\", substr(s, i + 1, 1) * 64 + substr(s, i + 2, 1) * 8 + substr(s, i + 3, 1)); i += 3 } else o = o c }; return o } mac { i = index($0, \" on \"); j = 0; for (k = length($0); k > i; k--) if (substr($0, k, 2) == \" (\") { j = k; break }; if (!i || !j) next; m = substr($0, i + 4, j - i - 4); t = substr($0, j + 2); sub(/[,)].*/, \"\", t) } !mac { m = dec($2); t = $3 } (t ~ /^(nfs|nfs4|cifs|smbfs|smb3|afpfs|webdav|davfs|autofs|ceph|glusterfs|lustre|9p)$/ || t ~ /^fuse\\./) && m !~ /:/ { printf \"%s:\", m }'; if [ -r '/proc/self/mounts' ]; then M=$(LC_ALL=C command -p awk \"$A\" '/proc/self/mounts' 2>/dev/null); else M=$('/sbin/mount' 2>/dev/null | LC_ALL=C command -p awk -v mac=1 \"$A\" 2>/dev/null); fi; nm() { for z in $M; do case \"$1/\" in \"$z\"/*) return 0;; esac; done; return 1; }; hm() { for z in \"$h\" \"$HOME\"; do case \"$z\" in /?*) case \"$1/\" in \"$z\"/*) return 0;; esac;; esac; done; return 1; }; set -f; o=$IFS; IFS=:; a=; q=; for v in $PATH; do case \"$v\" in /*) ;; *) a=$a${a:+:}$v; continue;; esac; nm \"$v\" && ! hm \"$v\" && continue; a=$a${a:+:}$v; y=$(cd \"$v\" 2>/dev/null && pwd -P) || continue; w \"$y\" || q=$q${q:+:}$v; done; n=; [ -n \"$a\" ] && n=$(PATH=$a; command -v node) || for d in /opt/homebrew/bin /usr/local/bin; do nm \"$d\" && continue; [ -x \"$d/node\" ] && n=$d/node && break; done; IFS=$o; set +f; [ -n \"$n\" ] || x \"node is not on PATH (a Codex started from the desktop may not have the shell's PATH)\"; case \"$n\" in /*) ;; *) x \"node on PATH is not an absolute path ($n)\";; esac; PATH=${q:-/usr/bin:/bin}; export PATH; cd \"$pr\" || x \"cannot enter the plugin folder\"; rp() { realpath \"$1\" 2>/dev/null || readlink -f \"$1\" 2>/dev/null || { f=$1; i=0; while [ -L \"$f\" ]; do i=$((i+1)); [ \"$i\" -le 40 ] || return 1; l=$(readlink \"$f\") || return 1; case \"$l\" in /*) f=$l;; *) f=${f%/*}/$l;; esac; done; [ -e \"$f\" ] || return 1; d=$(cd -P \"${f%/*}/\" 2>/dev/null && pwd -P) || return 1; printf '%s\\n' \"${d%/}/${f##*/}\"; }; }; r=$(rp \"$n\") || x \"cannot resolve the real path of $n\"; k() { case \"$1\" in /*) ;; *) x \"node path is not absolute ($1)\";; esac; for d in \"$u\" \"$t\"; do [ -n \"$d\" ] && case \"$1\" in \"$d\"/*) x \"node is in a temp folder ($1)\";; esac; done; [ \"$p\" = \"$h\" ] || case \"$1\" in \"$p\"/*) x \"node is in the session folder ($1)\";; esac; }; case \"${r##*/}\" in node) k \"$r\"; e=$r;; *) k \"$n\"; k \"$r\"; e=$(\"$n\" -p 'require(\"fs\").realpathSync(process.execPath)' 2>/dev/null) || x \"the node shim $n did not name its node\"; k \"$e\";; esac; \"$e\" \"$pr/hooks/run.cjs\" \"$pr/hooks/ralph-continue-codex.mjs\"",
|
|
32
|
+
"commandWindows": "$null = 'banker-hooks-rev d1402c48baa8'; function x($m) { [Console]::Error.WriteLine(\"banker hooks skipped: $m\"); exit 0 }; if (-not $env:PLUGIN_ROOT) { x 'PLUGIN_ROOT is not set' }; function G($p) { try { (Get-Item -LiteralPath $p -Force -ErrorAction Stop).FullName } catch { try { [IO.Path]::GetFullPath($p) } catch { $p } } }; $sb = @{}; foreach ($l in @(& (Join-Path ([Environment]::SystemDirectory) 'subst.exe') 2>$null)) { if ($l -match '^([A-Za-z]:)\\\\: => (.+)$') { $sb[$matches[1].ToUpper()] = $matches[2] } }; function L($p) { if (-not $p) { return '' }; $p = G $p; for ($i = 0; $i -lt 32; $i++) { $d2 = if ($p.Length -ge 2) { $p.Substring(0, 2).ToUpper() } else { '' }; if ($sb.Contains($d2)) { $p = G ($sb[$d2].TrimEnd('\\') + $p.Substring(2)); continue }; $c = $p; $t = $null; while ($c) { $it = Get-Item -LiteralPath $c -Force -ErrorAction SilentlyContinue; if ($it -and $it.LinkType -in 'SymbolicLink', 'Junction' -and @($it.Target)[0]) { $t = [string]@($it.Target)[0]; break }; $c = Split-Path -LiteralPath $c }; if (-not $t) { break }; if (-not [IO.Path]::IsPathRooted($t)) { $t = Join-Path (Split-Path -LiteralPath $c) $t }; $p = G ($t.TrimEnd('\\') + $p.Substring($c.Length)) }; $p.TrimEnd('\\') }; $ds = @($env:TEMP, $env:TMP); if ($env:LOCALAPPDATA) { $ds += \"$env:LOCALAPPDATA\\Temp\" }; $w = L $PWD.Path; if ($w -ne (L $env:USERPROFILE)) { $ds += $w }; $ds = @($ds | Where-Object { $_ } | ForEach-Object { L $_ } | Where-Object { $_ }); function u($f) { $f = L $f; foreach ($d in $ds) { if ($f -eq $d -or $f.StartsWith($d + '\\', 'OrdinalIgnoreCase')) { return $true } }; $false }; function k($f) { if (u $f) { x \"node is in a temp folder or the session folder ($(L $f))\" }; L $f }; $pr = L $env:PLUGIN_ROOT; if (u $pr) { x \"the plugin folder is in a temp folder or the session folder ($pr)\" }; $nd = @(@($env:PATH -split ';') | ForEach-Object { [Environment]::ExpandEnvironmentVariables($_.Trim().Trim('\"')) } | Where-Object { $_ -match '^[A-Za-z]:\\\\' -and ([IO.DriveInfo]::new($_.Substring(0, 1))).DriveType -ne 'Network' }); $n = $null; foreach ($d in $nd) { foreach ($e in 'node.exe', 'node.bat', 'node.cmd') { $f = Join-Path $d $e; if (Test-Path -LiteralPath $f -PathType Leaf) { $n = $f; break } }; if ($n) { break } }; if (-not $n) { x 'node is not on PATH' }; $env:PATH = ($nd | Where-Object { -not (u $_) }) -join ';'; $r = k $n; if (-not (Set-Location -LiteralPath $pr -PassThru -ErrorAction SilentlyContinue)) { x 'cannot enter the plugin folder' }; if ($r -notmatch '\\.exe$') { $e = & $r -p process.execPath 2>$null | Select-Object -Last 1; if (-not $e -or $e -notmatch '\\.exe$' -or -not (Test-Path -LiteralPath $e -PathType Leaf)) { x \"the node shim $r did not name its node\" }; $r = k $e }; & $r \"$pr\\hooks\\run.cjs\" \"$pr\\hooks\\ralph-continue-codex.mjs\"",
|
|
23
33
|
"timeout": 5
|
|
24
34
|
}
|
|
25
35
|
]
|
package/codex/manifest.json
CHANGED
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
{ "name": "setup", "type": "command", "target": "both", "reason": "installer orchestrator; Claude AskUserQuestion multi-select, Codex prompt-based selection", "supportingFiles": [], "codexDescription": "구성요소와 의존성을 여러 개 골라 설치하는 오케스트레이터. 런타임(Node, Python, Java), LSP, tmux, MCP, 샌드박스, OMX, Playwright 등. 권한 확인 끄기는 따로 묻고 기본으로 고르지 않으며, 골라도 실행하지 않고 직접 입력할 명령만 안내." },
|
|
87
87
|
{ "name": "graceful-pause", "type": "skill", "target": "codex-only", "reason": "Codex counterpart of the Claude Code /graceful-pause mod: sent mid-turn, Codex holds it until the running tool call ends and puts it into the same turn, so the session stops after the current step", "supportingFiles": [], "caveat": "source in codex/skills/graceful-pause (the Claude plugin keeps its immediate /graceful-pause mod); relies on Codex steering a message typed while a turn runs (Codex 0.144.5: delivered after the next tool call)" },
|
|
88
88
|
{ "name": "progress", "type": "skill", "target": "codex-only", "reason": "Codex counterpart of the Claude Code /progress mod: reads this session's rollout (found by CODEX_THREAD_ID) and prints the last plan file the session wrote or read as a tree (the same progress-plan.mjs engine, a byte-identical copy kept by scripts/sync-adapter.js), the last update_plan steps (or the requests) and the recent tool calls under the step in progress", "supportingFiles": ["scripts/progress.mjs", "scripts/progress-plan.mjs"], "caveat": "source in codex/skills/progress; not a live panel, it shows the state when called; --pane opens a tmux side pane that redraws every 2 s and --pane off closes it; inside the workspace-write sandbox tmux may be unreachable, so that one command needs approval to run outside it (or the user runs it with !); the rollout is read from its last 32 MB" },
|
|
89
|
+
{ "name": "ralph", "type": "skill", "target": "codex-only", "reason": "the Codex ralph: a build loop that ends only when runner-run gate records, a fresh reviewer's APPROVE and the slop pass all match the work tree as it is now (ralph-state.mjs check); replaces OMX $ralph, which OMX 0.21 removed (a sunset stub pointing at $ultragoal), and needs no OMX; like the OMC ralph in Claude Code, banker's Codex Stop hook (hooks/ralph-continue-codex.mjs, ralph-state.mjs continueFor) keeps the session that started a run going until done or stop (five stops with no change let it go; opt-out BANKER_NO_RALPH_CONTINUE); all-in-one, ultra-init, ultra-chain, visual-ralph and front-qa call it on Codex", "supportingFiles": ["scripts/ralph-state.mjs", "scripts/gate-evidence.mjs", "scripts/run-folder.mjs"], "caveat": "source in codex/skills/ralph; gate-evidence.mjs and run-folder.mjs are byte-identical copies of ralph-qa's (scripts/sync-adapter.js); the record lives in the gate folder outside the repo, so under the workspace-write sandbox its calls may ask for approval; the repo must be a git work tree" },
|
|
89
90
|
{ "name": "teamwork", "type": "skill", "target": "codex-only", "reason": "peer work between the Codex CLI sessions of one machine, turned on and off for the whole Codex home: SessionStart and UserPromptSubmit hooks register each session and add the peer rules, and requests and results travel by Codex's own codex queue through one wrapper an approval rule lets run outside the sandbox", "supportingFiles": ["scripts/teamwork.mjs"], "caveat": "source in codex/skills/teamwork; Codex 0.149.0 or later (codex queue); after the first on, trust the hooks once in the next new session (Hooks need review); sessions open before the install must be reopened; before the hooks are trusted codex exec may stall" }
|
|
90
91
|
]
|
|
91
92
|
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ralph
|
|
3
|
+
description: "Codex 전용. Claude Code 의 ralph 처럼 작업이 실제로 끝날 때까지 구현, 슬롭 점검, 게이트 실행, 새 맥락 검토를 반복하고, 끝나기 전에는 턴이 끝나지 않게 이어 감. 끝남은 실행기가 돌린 게이트 기록, 검토자 APPROVE, 슬롭 점검이 모두 지금 작업 트리와 맞을 때만. OMX 0.21 에서 없어진 `$ralph` 를 대신하고 OMX 없이 동작. 'ralph'/'banker-ralph'/'끝날 때까지 해'/'멈추지 말고 끝내' 시 사용."
|
|
4
|
+
argument-hint: "<작업 또는 계획 파일 경로> [--gates=<id,...>] [--max=<n>] [--no-deslop] [--resume]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ralph: Codex 의 검증 반복 구현
|
|
8
|
+
|
|
9
|
+
인자: `$ARGUMENTS` (Codex 는 사용자 메시지에서 읽음)
|
|
10
|
+
|
|
11
|
+
- oh-my-codex 0.20.2 `skills/ralph`(MIT) 의 반복 구조를 본뜬 banker 스킬
|
|
12
|
+
- OMX 0.21 은 `$ralph` 를 없애고 `$ultragoal` 로 대신함. banker 의 다른 스킬이 OMX 판에 기대지 않도록 이 스킬을 씀
|
|
13
|
+
- Claude Code 에는 없음. Claude Code 는 OMC `ralph` 를 씀
|
|
14
|
+
|
|
15
|
+
## 부르는 이름과 폴더
|
|
16
|
+
|
|
17
|
+
| 설치 길 | 이름 | 이 스킬 폴더 `<S>` |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| npm (`banker setup --codex`) | `$banker-ralph` | `~/.codex/skills/banker-ralph/` |
|
|
20
|
+
| Codex 플러그인 | `$banker:ralph` | `<CODEX_HOME>/plugins/cache/<마켓플레이스>/banker/<판>/codex/skills/ralph/` |
|
|
21
|
+
|
|
22
|
+
- `<T>` 는 `<S>/scripts` 다
|
|
23
|
+
- `gate-evidence.mjs`, `run-folder.mjs` 는 ralph-qa 의 바이트 동일 사본. `ralph-state.mjs` 는 이 스킬의 실행 기록과 완료 검사
|
|
24
|
+
|
|
25
|
+
## 끝까지 이어 가기 (Stop 훅)
|
|
26
|
+
|
|
27
|
+
Claude Code 의 OMC ralph 처럼 실행이 끝나기 전에는 턴이 끝나지 않는다.
|
|
28
|
+
- banker 의 Codex Stop 훅(`hooks/ralph-continue-codex.mjs`)이 턴 끝마다 이 저장소의 실행을 본다
|
|
29
|
+
- 그 실행을 시작한 세션(`start` 때의 `CODEX_THREAD_ID`)이고 상태가 `active` 면 계속하라는 이유를 넣는다. 받은 이유대로 `check` 를 보고 절차를 잇는다
|
|
30
|
+
- 끝나는 길은 `done`(완료)과 `stop`(멈춤) 둘뿐이다
|
|
31
|
+
- 기록이나 게이트 증거가 바뀌지 않은 채 멈추려 하면 훅이 다섯 번까지 막고 여섯 번째에 놓아준다. 한 실행에서 모두 100번 막은 뒤에도 놓아준다. 그때는 `stop` 으로 까닭을 남긴다
|
|
32
|
+
- 훅은 `banker setup --codex` 나 Codex 플러그인 설치가 넣는다. Codex 가 처음 한 번 신뢰를 묻는다(`/hooks`). 신뢰하지 않으면 턴이 끝날 때 멈추고, 사용자가 "계속" 이라고 하면 `find` 로 이어 간다
|
|
33
|
+
- 사용자가 멈추라고 하면 `stop --reason '사용자 요청'` 을 부르고 멈춘다. 훅을 끄려면 `BANKER_NO_RALPH_CONTINUE=1`
|
|
34
|
+
- 검토자 같은 하위 에이전트 세션은 막지 않는다(세션 id 가 다름)
|
|
35
|
+
|
|
36
|
+
## 언제 쓰나
|
|
37
|
+
|
|
38
|
+
- 끝까지 해야 하는 구현. 부분 완료를 끝으로 치면 안 될 때
|
|
39
|
+
- all-in-one, ultra-init, ultra-chain, visual-ralph, front-qa 의 구현 단계(Codex)
|
|
40
|
+
- 사용자가 "ralph", "끝날 때까지", "멈추지 말고 끝내" 라고 할 때
|
|
41
|
+
|
|
42
|
+
## 쓰지 않을 때
|
|
43
|
+
|
|
44
|
+
- 한 줄 수정. 바로 고치고 시험만 돌림
|
|
45
|
+
- 계획부터 필요할 때. `$ralplan` 이나 `$banker-all-in-one`(플러그인 길 `$banker:all-in-one`)을 씀
|
|
46
|
+
- 독립 검증만 필요할 때. `$banker-ralph-qa`(플러그인 길 `$banker:ralph-qa`)를 씀
|
|
47
|
+
|
|
48
|
+
## 인자
|
|
49
|
+
|
|
50
|
+
| 인자 | 뜻 | 기본 |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| 작업 | 문장, 또는 계획 파일 경로(`.omx/plans/prd-*.md` 등). 계획이면 그 이야기와 수용 기준을 따름 | 없음(필수) |
|
|
53
|
+
| `--gates=<id,...>` | 완료를 판정할 게이트 id. 명령은 id 마다 한 줄(`tests: npm test`)로 함께 받음. 게이트 하나에 명령 하나 | 저장소 신호에서 고름 |
|
|
54
|
+
| `--max=<n>` | 반복 한도(1~100) | 10 |
|
|
55
|
+
| `--no-deslop` | 슬롭 점검을 건너뜀 | 점검함 |
|
|
56
|
+
| `--resume` | 이 저장소의 열린 실행을 이어 감 | 새 실행 |
|
|
57
|
+
|
|
58
|
+
- `--max`, `--no-deslop` 은 1단계의 `start` 에 그대로 넘긴다(`--max <n>`, `--no-deslop`). 넘기지 않으면 기록에 남지 않는다
|
|
59
|
+
- 게이트를 받지 못하면 저장소에서 고른다(`package.json` 의 `test`, `lint`, `typecheck`, `build`, `pytest`, `go test ./...`, `cargo test` 등). 고른 명령은 시작 보고에 적는다
|
|
60
|
+
- 게이트로 쓸 명령이 하나도 없으면 `--no-gates '<까닭>'` 으로 시작한다. 까닭은 보고에 남는다
|
|
61
|
+
|
|
62
|
+
## Codex 런타임 규칙
|
|
63
|
+
|
|
64
|
+
| 항목 | 규칙 |
|
|
65
|
+
|---|---|
|
|
66
|
+
| OMX | 쓰지 않음. `omx state`, OMX Stop 훅, `$ultragoal`, `$ultrawork` 를 부르지 않음. tmux 도 필요 없음 |
|
|
67
|
+
| 기록 위치 | 게이트 폴더 `<g>`(저장소 밖 비공개 기준 폴더). 저장소 안에 기록 파일을 쓰지 않음 |
|
|
68
|
+
| 샌드박스 | `<g>` 가 쓰기 범위 밖이면 `gate-evidence.mjs`, `ralph-state.mjs` 호출과 `<g>` 아래 파일 쓰기마다 `sandbox_permissions: "require_escalated"` 로 승인받음. 승인을 못 받으면 멈추고 보고 |
|
|
69
|
+
| 검토자 | `spawn_agent` 를 `agent_type`, `model` 없이 띄움. 저자 대화를 넘기지 않음(fork 금지). 읽기 전용은 지시로 |
|
|
70
|
+
| 일꾼 | 독립 작업은 `spawn_agent` 로 나눌 수 있음. 같은 파일을 두 일꾼에게 주지 않음 |
|
|
71
|
+
| Codex goal | goal 도구가 있으면 `get_goal` 로 목표를 확인. `done` 이 `RALPH COMPLETE` 를 낸 뒤에만 `update_goal({status: "complete"})`. 호출자가 goal 을 바꾸지 말라고 하면 바꾸지 않음. 이어 가기는 goal 이 아니라 Stop 훅이 맡음 |
|
|
72
|
+
| OMX 0.20.x 의 `$ralph` | 같은 작업에 함께 켜지 않음. 이미 켜져 있으면 그 모드를 끝낸 뒤 시작 |
|
|
73
|
+
|
|
74
|
+
- 저장소 안에 기록을 쓰지 않는 까닭: 추적하지 않는 파일이 생기면 작업 트리 지문이 바뀌어 게이트 기록과 판정이 모두 `stale` 이 된다. `.omx/context/`, `.omx/state/` 도 같다
|
|
75
|
+
- 맥락 요약과 하네스(임시 시험, 자료)는 `<g>` 안에 둔다
|
|
76
|
+
|
|
77
|
+
## 절차
|
|
78
|
+
|
|
79
|
+
### 0. 준비
|
|
80
|
+
|
|
81
|
+
1. 저장소 루트 확인: `git rev-parse --show-toplevel`
|
|
82
|
+
- git 저장소가 아니면 멈춘다. 완료를 기계로 검사할 수 없기 때문이다
|
|
83
|
+
- 스스로 `git init` 하지 않는다. 사용자에게 `git init` 과 첫 커밋을 하면 쓸 수 있다고 알린다
|
|
84
|
+
2. 계속 생기는 파일 확인: 작업 중 저장소 안에 추적하지 않는 파일이 계속 생기면(OMX 훅의 `.omx/`, 커버리지 결과 등) 지문이 계속 바뀐다
|
|
85
|
+
- 그런 폴더가 있을 때만 `git check-ignore -q .omx` 처럼 확인한다. 무시되지 않으면 사용자에게 알리고 `.git/info/exclude` 에 넣을지 묻는다(쓰기에 승인이 필요할 수 있음). 없는 폴더는 확인하지 않는다
|
|
86
|
+
3. 모호한 점: 저장소를 읽어 먼저 줄인다. 그래도 사용자 결정이 필요하면 `start` 전에 한 번만 묻는다. 호출자가 질문 금지라고 했으면 가정을 정하고 진행한다
|
|
87
|
+
4. 이어 하기: `--resume` 이거나 작업이 이전 실행을 가리키면 `find` 로 찾는다
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
node '<T>/ralph-state.mjs' find --cwd '<저장소>'
|
|
91
|
+
node '<T>/ralph-state.mjs' status --dir '<g>' --cwd '<저장소>'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- `status` 가 `active` 인 가장 새 실행을 이어 간다. 다른 세션이 시작한 실행이면 `adopt --dir '<g>'` 로 먼저 옮긴다. `check.missing` 을 보고 2절(반복)의 빠진 단계부터 한다. 구현이 덜 됐으면 2절의 2(구현)부터
|
|
95
|
+
- 열린 실행이 없으면 새로 시작한다
|
|
96
|
+
|
|
97
|
+
### 1. 시작
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
node '<T>/gate-evidence.mjs' init --cwd '<저장소>'
|
|
101
|
+
node '<T>/ralph-state.mjs' start --dir '<g>' --cwd '<저장소>' --task '<한 줄 요약>' --gates tests,lint [--plan '<계획 파일>'] [--max <n>] [--no-deslop]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- `init` 출력의 `dir` 이 `<g>` 다
|
|
105
|
+
- `--plan` 은 계획 파일이 있을 때만 준다
|
|
106
|
+
- `--gates` 에는 게이트 id 만 쉼표로 적는다. 명령은 `run --cmd` 로 준다
|
|
107
|
+
- `start` 출력의 `head` 가 시작 HEAD 다. 슬롭 점검과 검토가 이 값을 쓴다
|
|
108
|
+
- `start` 출력의 `session` 이 `null` 이면(`warnings` 에 나옴) Stop 훅이 이 실행을 잇지 않는다. Codex 세션 안에서 `adopt` 로 고친다
|
|
109
|
+
- 맥락 요약을 `<g>/context.md` 에 쓴다: 작업, 바라는 결과, 아는 사실, 제약, 모르는 것, 건드릴 곳
|
|
110
|
+
- `start` 뒤에는 사용자에게 묻지 않는다. 물으려면 턴이 끝나야 하는데 Stop 훅이 그 턴을 잇기 때문이다. 가정을 `<g>/context.md` 에 적고 진행한다
|
|
111
|
+
- 꼭 사용자 결정이 필요하면 `stop --reason '사용자 결정 필요: <무엇>'` 으로 멈춘 뒤 묻는다. 답을 받으면 새 실행으로 다시 시작한다
|
|
112
|
+
- `start` 출력의 `baseline` 을 따른다
|
|
113
|
+
|
|
114
|
+
| `baseline` | 할 일 |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `run` | 고치기 전, 시작 트리 그대로에서 게이트마다 `run --iter 0` 을 한 번 돌림 |
|
|
117
|
+
| `none-dirty` | 반복 0 을 돌리지 않음. 시작 때 이미 바뀐 트리라 기준선이 바뀐 결과를 숨길 수 있음. 모든 실패를 이 변경의 것으로 봄 |
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
node '<T>/gate-evidence.mjs' run --dir '<g>' --id tests --cmd 'npm test' --timeout 600 --iter 0 --cwd '<저장소>'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- 반복 0 은 시작 트리에서 돈 것만 기준선이 된다. 파일을 고친 뒤 돌린 반복 0 은 버려진다
|
|
124
|
+
|
|
125
|
+
### 2. 반복
|
|
126
|
+
|
|
127
|
+
반복마다 아래 순서를 지킨다. 순서가 바뀌면 판정이 `stale` 이 된다.
|
|
128
|
+
|
|
129
|
+
1. 다음 반복 번호를 받는다
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
node '<T>/ralph-state.mjs' next --dir '<g>'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- 출력의 `iter` 가 이번 반복 `<k>` 다
|
|
136
|
+
- `repeatedFailures` 가 비어 있지 않으면 같은 실패가 3회 반복된 것이다. 근본 문제로 보고 4절(멈춤)로 간다
|
|
137
|
+
- `max-spent` 로 멈추면 4절로 간다
|
|
138
|
+
2. 구현: 남은 이야기와 수용 기준을 하나씩 끝낸다
|
|
139
|
+
- 금지: 시험 지우기, 시험 건너뛰기 추가, 범위 줄이기, 수용 기준 바꾸기
|
|
140
|
+
- 계획 밖 변경이 필요하면 까닭을 `<g>/context.md` 에 적는다
|
|
141
|
+
- 커밋과 `git add` 도 지문을 바꾼다. 커밋은 슬롭 점검 기록 전이나 `done` 뒤에 한다
|
|
142
|
+
3. 슬롭 점검(`--no-deslop` 이면 건너뜀): 시작 HEAD 뒤에 바뀐 파일만 본다
|
|
143
|
+
- 대상: `git diff --name-only <시작 HEAD>` 와 추적하지 않는 새 파일
|
|
144
|
+
- 볼 것: 죽은 코드, 쓰지 않는 import, 디버그 출력, 코드를 되풀이하는 주석, 복사한 중복, 쓸모없는 방어 코드, 한 번만 쓰는 추상화
|
|
145
|
+
- 고친 뒤 기록한다. 기록 뒤에 파일을 고치면 점검을 다시 기록한다
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
node '<T>/ralph-state.mjs' deslop --dir '<g>' --cwd '<저장소>' --note '<본 파일 수, 고친 것>'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
4. 게이트: 게이트마다 `run --iter <k>` 를 돌린다
|
|
152
|
+
- 명령 하나에 게이트 하나. `&&` 나 `;` 로 묶지 않음. `--timeout` 필수
|
|
153
|
+
- `run` 은 명령이 실패해도 종료 0 이다. 출력 JSON 의 `verdict` 가 `pass` 가 아니면 실패다
|
|
154
|
+
- 실패하면 고친다. 고쳤으면 3(슬롭 점검 기록)부터 다시 한다
|
|
155
|
+
- 한 번이라도 실패한 게이트(반복 0 의 실패 포함)는 같은 트리에서 3회 통과해야 통과로 센다(흔들림 규칙)
|
|
156
|
+
- 시작 전부터 같은 실패(`baseline`)는 그대로 두면 끝나지 않는다
|
|
157
|
+
- 이 작업이 고칠 실패면 고친다
|
|
158
|
+
- 이 작업과 상관없는 실패일 때만 받아들이고 까닭을 적는다. 그 뒤에 받은 검토자 APPROVE 만 센다
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
node '<T>/ralph-state.mjs' accept-baseline --dir '<g>' --cwd '<저장소>' --id tests --reason '<상관없는 까닭>'
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
5. 검토 준비: `check` 를 돌려 `fingerprint` 와 `warnings` 를 얻고, `evidence` 로 게이트 기록을 뽑는다. 이때 `check` 는 APPROVE 가 아직 없어 종료 1 이다. APPROVE 말고 다른 `missing` 이 있으면 검토 전에 그것부터 고친다
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
node '<T>/ralph-state.mjs' check --dir '<g>' --cwd '<저장소>'
|
|
168
|
+
node '<T>/ralph-state.mjs' evidence --dir '<g>'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
6. 검토: 새 맥락 검토자를 띄운다
|
|
172
|
+
- 기본 1명. 바뀐 파일이 20개를 넘거나 보안, 권한, 구조 변경이면 2명
|
|
173
|
+
- 검토 중에는 작업 트리를 바꾸지 않는다
|
|
174
|
+
- 검토자에게 주는 것: 작업, 계획과 수용 기준, 시작 HEAD, `git diff <시작 HEAD>` 를 보는 방법, `evidence` 출력(반복 0 기준선과 이번 반복의 게이트 기록, 출력 파일 경로, 받아들인 기준선), `check` 의 `warnings`, 5의 `fingerprint`
|
|
175
|
+
- 검토자 지시: 파일을 고치지 않음. 첫 줄은 `VERDICT: APPROVE` 나 `VERDICT: REJECT`. blocker 마다 `파일:줄`, 무엇이 틀렸나, 왜. 수용 기준마다 충족 여부. 받아들인 기준선이 이 작업의 실패면 blocker
|
|
176
|
+
- 판정마다 5의 `fingerprint` 로 기록한다. blocker 가 하나라도 있으면 `REJECT` 다. 기록 때 트리가 바뀌었으면 `stale-review` 로 멈추니 다시 검토받는다
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
node '<T>/ralph-state.mjs' verdict --dir '<g>' --cwd '<저장소>' --by reviewer-1 --verdict REJECT --blockers 2 --fingerprint '<fp>' --note '<blocker 요약>'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
7. 판정
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
node '<T>/ralph-state.mjs' check --dir '<g>' --cwd '<저장소>'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- 종료 0: 3절(끝)로 간다
|
|
189
|
+
- 종료 1: `missing` 을 본다. REJECT 의 blocker 를 고치고 1(다음 반복)로 간다. 같은 트리에 받은 REJECT 는 다음 반복에서도 남는다. 고쳐야 풀린다
|
|
190
|
+
- `warnings` 의 `misleading`(종료 0 인데 출력에 실패나 건너뜀): 검토자가 판단한다. 저자가 무시하지 않는다
|
|
191
|
+
|
|
192
|
+
### 3. 끝
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
node '<T>/ralph-state.mjs' done --dir '<g>' --cwd '<저장소>'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- 출력의 `line`(`RALPH COMPLETE iter=<k> gates=<...> approvals=<n> deslop=<done|skipped> baseline=<n> misleading=<n>`)을 보고 첫 줄에 쓴다
|
|
199
|
+
- `done` 이 종료 1 이면 끝나지 않은 것이다. `missing` 을 보고 2절로 돌아간다
|
|
200
|
+
- Codex goal 이 있으면 이제 `update_goal({status: "complete"})`(호출자가 막지 않았을 때만)
|
|
201
|
+
- 게이트 폴더: 호출자가 남기라고 하지 않았으면 보고 뒤에 지운다
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
node '<T>/gate-evidence.mjs' cleanup --dir '<g>'
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 4. 멈춤
|
|
208
|
+
|
|
209
|
+
다음이면 멈추고 사용자에게 보고한다.
|
|
210
|
+
- 같은 실패 3회(`repeatedFailures`)
|
|
211
|
+
- 반복 한도(`max-spent`)
|
|
212
|
+
- 사용자만 정할 수 있는 blocker(요구 충돌, 자격 증명, 운영 환경)
|
|
213
|
+
- 샌드박스 승인을 받지 못함
|
|
214
|
+
- 사용자가 멈추라고 함
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
node '<T>/ralph-state.mjs' stop --dir '<g>' --reason '<까닭>'
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- 보고 첫 줄은 `RALPH STOPPED (<까닭>)`
|
|
221
|
+
- 멈춘 실행은 이어 갈 수 없다. 사용자가 다시 지시하면 새 실행을 시작한다
|
|
222
|
+
- 게이트 폴더는 지우지 않는다. 보고에 `<g>` 경로를 적는다
|
|
223
|
+
|
|
224
|
+
## 이어 하기
|
|
225
|
+
|
|
226
|
+
- compaction 이나 새 세션: `find --cwd '<저장소>'` 로 `active` 실행을 찾는다
|
|
227
|
+
- 새 세션은 세션 id 가 달라 Stop 훅이 그 실행을 잇지 않는다. 새 세션에서 이어 갈 때는 먼저 `adopt` 로 실행을 이 세션으로 옮긴다
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
node '<T>/ralph-state.mjs' adopt --dir '<g>'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
- `ready-compact` 노트에 `<g>` 경로와 반복 번호를 적는다
|
|
234
|
+
- `<g>` 가 없어졌으면(로그아웃이나 재부팅이 `$XDG_RUNTIME_DIR` 를 비움): 새로 `init`, `start` 한다. 계획 파일과 git 기록으로 남은 일을 다시 정한다
|
|
235
|
+
|
|
236
|
+
## 보고
|
|
237
|
+
|
|
238
|
+
첫 줄은 `RALPH COMPLETE ...` 나 `RALPH STOPPED (...)`.
|
|
239
|
+
|
|
240
|
+
| 항목 | 내용 |
|
|
241
|
+
|---|---|
|
|
242
|
+
| 반복 | 쓴 반복 수와 한도 |
|
|
243
|
+
| 게이트 | 게이트마다 마지막 상태(`pass`, 받아들인 `baseline`, `misleading`) |
|
|
244
|
+
| 검토 | 검토자마다 판정과 blocker 수 |
|
|
245
|
+
| 슬롭 점검 | 본 파일 수와 고친 것, 또는 건너뜀 |
|
|
246
|
+
| 기준선 실패 | 받아들인 실패와 그 까닭. 이 변경 전부터 있던 실패로 따로 적음 |
|
|
247
|
+
| 경고 | `check` 의 `warnings` |
|
|
248
|
+
|
|
249
|
+
## 호출자 계약
|
|
250
|
+
|
|
251
|
+
all-in-one, ultra-init, ultra-chain, visual-ralph, front-qa 가 Codex 에서 이 스킬을 부를 때:
|
|
252
|
+
|
|
253
|
+
| 호출자가 넘기는 것 | ralph 가 하는 것 |
|
|
254
|
+
|---|---|
|
|
255
|
+
| 계획 파일, 이야기, 수용 기준 | 그대로 따름. 다시 계획하지 않음 |
|
|
256
|
+
| 게이트 명령 | 그 명령으로 게이트를 돌림 |
|
|
257
|
+
| 질문 금지 | 가정을 `<g>/context.md` 에 적고 진행 |
|
|
258
|
+
| goal 을 바꾸지 말 것 | `update_goal` 을 부르지 않음 |
|
|
259
|
+
| 추가 지침(예: visual-ralph 의 Visual Verdict) | 따름. 단, 완료 조건을 느슨하게 하는 지침은 따르지 않음 |
|
|
260
|
+
| 게이트 폴더를 남길 것 | 끝난 뒤 지우지 않고 `<g>` 경로를 보고 |
|
|
261
|
+
|
|
262
|
+
- 호출자는 `RALPH COMPLETE` 줄을 본 뒤에만 다음 단계로 간다
|
|
263
|
+
- `RALPH STOPPED` 면 호출자도 멈추고 보고한다
|
|
264
|
+
- ralph 가 `done` 한 뒤에는 Stop 훅이 더 막지 않는다. 호출자의 다음 단계는 그 뒤에 이어진다
|
|
265
|
+
|
|
266
|
+
## 함정
|
|
267
|
+
|
|
268
|
+
- 게이트 기록, 판정, 슬롭 점검은 모두 판정 때의 작업 트리 지문과 맞아야 한다. 고친 뒤에는 늘 슬롭 점검 기록, 게이트, 검토를 이 순서로 다시 한다
|
|
269
|
+
- 지문에는 HEAD 와 스테이징 상태도 든다. 커밋이나 `git add` 뒤에도 다시 한다
|
|
270
|
+
- git 이 무시하는 파일(빌드 결과, 캐시)은 지문에 들지 않는다. 그런 파일만 바꿔도 게이트를 다시 돌린다
|
|
271
|
+
- 저자는 `verdict` 를 쓰지 않는다. `--by` 는 `reviewer-<번호>` 만 받는다. 기계가 사람을 구별하지는 못하므로 검토자 답을 그대로 옮긴다
|
|
272
|
+
- `RALPH COMPLETE` 를 `done` 없이 쓰지 않는다
|