@kaydash9999/banker-plugins 0.26.0 → 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 +30 -0
- package/README.md +10 -7
- 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 +2 -1
- 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 +6 -5
- 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/make-evidence/SKILL.md +207 -131
- package/skills/make-evidence/scripts/evidence.mjs +162 -0
- package/skills/make-evidence/scripts/me-check.mjs +125 -0
- package/skills/make-evidence/scripts/me-commit.mjs +48 -0
- package/skills/make-evidence/scripts/me-git.mjs +63 -0
- package/skills/make-evidence/scripts/me-oracle.mjs +244 -0
- package/skills/make-evidence/scripts/me-precheck.mjs +122 -0
- package/skills/make-evidence/scripts/me-run.mjs +214 -0
- 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,35 @@
|
|
|
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
|
+
|
|
20
|
+
## [0.26.1] - 2026-11-05
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- `make-evidence`: 검토 6회와 시험용 서비스에서 세 번 실제로 돌려 본 결과를 반영해 다시 만든다. 절차 글만으로는 거짓 증거를 막지 못하는 자리를 Node 도우미 `scripts/evidence.mjs` 로 기계화한다(의존성 없음, 네트워크 없음).
|
|
24
|
+
- `run`: 명령을 로그로 남긴다. 다시 돌릴 수 있게 인용한 명령 줄, HEAD, 종료 코드를 남기고 그 코드를 그대로 돌려준다. 입력은 닫고, 시간 제한에 걸리면 프로세스 트리를 끝낸다. 출력을 쥔 채 떨어져 나간 자손은 기다리지 않는다. 흔한 비밀값 꼴은 가린다. 시험 통과 수(node --test, pytest, jest)를 METRIC 줄로 찍는다.
|
|
25
|
+
- `check`: 문서의 `` `이름=값` `` 을 그 줄이 가리킨 로그의 `METRIC 이름=값` 줄과 정확히 대조한다. `run` 이 만든 로그만 받고, 상수를 찍는 명령의 로그는 거부한다. 그 밖의 손 숫자는 경고하고, 오류나 경고가 있으면 종료 1. `count` 는 trace 표의 상태 칸을 머리 줄로 찾아 센다.
|
|
26
|
+
- `room`, `collect`: 판정기 작성자(새 맥락 하위 에이전트)에게 저장소 밖 임시 폴더를 주고 요구 문서와 담당자 사실만 넣는다. 방에 다른 파일이 생겼거나 입력이 바뀌었으면 가져오지 않는다.
|
|
27
|
+
- `worktree`, `bug`: 버그 셋을 HEAD 의 별도 작업 트리에서만 넣고, 끝나면 작업 트리를 지운다. 사용자 작업 트리와 링크한 의존 폴더가 그대로인지 확인한다. 판정기의 ERROR 줄, 기준선과 다른 사례, 기준선에 PASS 가 없는 종류, 원래 트리를 가리키는 링크는 `caught` 가 아니라 `undecidable` 이다. 빌드 결과 폴더는 링크하지 않는다.
|
|
28
|
+
- `precheck`: 스테이징된 변경, 커밋하지 않은 변경, 이 컴퓨터 밖 DB 와 서비스 주소를 찾는다. 설정 파일은 네 단계 아래까지 보고, 환경 변수는 저장소가 부르는 것과 프레임워크와 DB 라이브러리가 스스로 읽는 이름을 본다. 비로컬이면 구현을 띄우기 전에 묻는다.
|
|
29
|
+
- `commit`: 증거 경로만 커밋한다. `.env`, 큰 파일, 비밀값 꼴이 남은 파일은 거부하고, 커밋 훅은 우회하지 않는다.
|
|
30
|
+
- `make-evidence`: `--oracle-only` 로 구현 전에 판정기를 먼저 커밋한다. 구현 뒤에 쓴 판정기는 `verify.md` 첫 줄에 `요구 미충족` 으로 적는다. 구현 코드를 찾지 못하면 판정기만 쓰고 멈출지 묻는다. 요구 미충족이면 다음 프로젝트에서 할 일을 알린다.
|
|
31
|
+
- `make-evidence`: 구현을 고치지 않고, 설치하거나 내려받지 않고, 운영 서버와 운영 DB 에 보내지 않는다는 원칙을 명시한다. 판정 실행의 DB 는 일회용이어야 한다.
|
|
32
|
+
|
|
3
33
|
## [0.26.0] - 2026-11-05
|
|
4
34
|
|
|
5
35
|
### Added
|
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` 뒤에도 세션이 끝날 때까지 남고, 그동안 부르면 패널이 꺼져 있다고 답합니다.
|
|
@@ -158,7 +159,7 @@ Claude Code 2.1.296 은 Claude 5 모델(Opus 5.5 등)에 작업 목록 도구(Ta
|
|
|
158
159
|
| `smart-compact` | 컨텍스트 임계 초과 시 위키·resume 저장 게이트 자동 무장 |
|
|
159
160
|
| `deep-research` | 다중 소스 팬아웃 + 적대적 검증 인용 리서치 |
|
|
160
161
|
| `lfm` | learn from master. 그 분야 최고 전문가 그룹이 일하는 방식을 1차 자료로 조사해 계획 적용 항목으로 정리(ultra-interview, ultra-chain, deep-research, rfp-author 의 리서치에서도 씀) |
|
|
161
|
-
| `make-evidence` | 구현한 서비스의 증거 문서 여섯(optimize, verify, flow, scale, decisions, trace)을 실제 실행으로 만들어 `docs/evidence/` 에 커밋.
|
|
162
|
+
| `make-evidence` | 구현한 서비스의 증거 문서 여섯(optimize, verify, flow, scale, decisions, trace)을 실제 실행으로 만들어 `docs/evidence/` 에 커밋. 도우미 `evidence.mjs` 가 명령을 로그로 남기고(종료 코드, 시간 제한, 비밀값 가림), 문서의 수치를 로그의 `METRIC` 줄과 대조하고, 증거 경로만 커밋. 판정기는 저장소 밖 방에서 README 와 담당자 사실만 받은 새 맥락 하위 에이전트가 쓰고(새 프로젝트는 구현 전에 `--oracle-only` 로 먼저 불러 판정기를 앞서 커밋, 구현 전으로 보이면 스킬이 물음), 버그 셋은 별도 작업 트리에서만 넣고 지움. 구현을 고치지 않고, 설치와 push 는 안 함 |
|
|
162
163
|
| `ultra-interview` | 리서치 선행으로 사람 질문 최소화, 모호성 3% 이하까지 인터뷰 |
|
|
163
164
|
| `interval-report` | 장기 수행 중간 보고를 `docs/intermission.md` 로 갱신(경과·잔여·단일 표) |
|
|
164
165
|
| `summary-wiki` | 위키를 `docs/` 단일 파일 개조식으로 요약(지식 동기화 검토용, 읽기 전용) |
|
|
@@ -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
|
@@ -80,12 +80,13 @@
|
|
|
80
80
|
{ "name": "git-init", "type": "skill", "target": "both", "reason": "makes the first commit of a folder that is not a git repository yet, so worktrees and other parallel work can start from it: writes a .gitignore that keeps secrets and local state out, scans the files to track for secrets by name and content (payload-scan.mjs, file:line:kind only, never the value), then runs git init, git add . and git commit -m init; a hit stops it before any .git exists; pure Node, no deps, no network", "supportingFiles": ["scripts/git-init.mjs","scripts/marker-block.mjs","scripts/payload-scan.mjs"], "caveat": "needs git on PATH (before git 2.28 the first branch is set with git symbolic-ref) and a commit identity (user.name and user.email, or --local-identity, written to .git/config only); an existing .gitignore keeps its bytes and gets one marked block at its end, the first original kept in .gitignore.git-init.bak, which the block itself ignores; a .gitignore symbolic link is refused unless --follow-symlink; key file names cannot be accepted, a content hit only by its exact file:line:kind with --accept; commit hooks are not skipped; it never adds a remote or pushes; on Codex run asks for sandbox_permissions require_escalated, since the default sandbox may block writes to .git" },
|
|
81
81
|
{ "name": "build-testbox", "type": "skill", "target": "both", "reason": "a test environment this machine builds from its own resources: a browser matrix (Playwright Chromium and WebKit, desktop and mobile device emulation) against a loopback URL and container OSes (Rocky Linux 8, 9; macOS only as a Tart VM on Apple Silicon) run in parallel, every requested cell kept as a pass, fail, unverified or unsupported row with a reason, results.json and summary.md; detect, run, setup; pure Node, no deps", "supportingFiles": ["scripts/testbox.mjs","scripts/args.mjs","scripts/detect.mjs","scripts/browsers.mjs","scripts/browser-cell.mjs","scripts/containers.mjs","scripts/setup.mjs","scripts/report.mjs","scripts/proc.mjs"], "caveat": "Playwright (a global install, or the project's with --playwright-from-project) and docker or podman are optional; a missing one leaves its cells unverified with the reason; run downloads nothing (a missing image or browser build is unverified, never pulled); setup only lists what it would fetch and its rough size, and fetches after the user confirms and it runs again with --yes (exit 4 and network needed when the sources are unreachable); macOS runs only as a Tart VM on an Apple Silicon host (other hosts are unsupported under the Apple SLA), and the skill never downloads or starts the VM; on Codex detect and run ask for sandbox_permissions require_escalated for the container engine and the headless browsers, the server under test on a 127.0.0.1 port is the user's own (approved where it is started), and setup --yes, or a container command that uses the network, asks for network access (--network host lifts that cell's isolation, and the user is told)" },
|
|
82
82
|
{ "name": "tune-tmux", "type": "skill", "target": "both", "reason": "adds or removes one marked block in the tmux or psmux config file so mouse drag copies to the OS clipboard and PageUp/PageDown scroll inside tmux, without taking keys or the mouse from full-screen apps; status shows the chosen file, the lines and why any are left out, conflicts in other files tmux reads, and per-app behavior (Claude Code, Codex); first original kept, off removes only the block, --reload applies it to a running server; pure Node, no deps, no network", "supportingFiles": ["scripts/tune-tmux.mjs","scripts/tmux-config.mjs","scripts/app-notes.mjs","scripts/marker-block.mjs","scripts/psmux-caps.json"], "caveat": "needs tmux or psmux (install with setup-tmux; status alone works without them); it writes the user's config file outside the workspace (~/.tmux.conf or the XDG tmux.conf, ~/.psmux.conf for psmux), keeping the first original as <conf>.tune-tmux.bak, so on Codex on, off and --reload ask for sandbox_permissions require_escalated (no --add-dir); a config file that is a symbolic link is refused unless --follow-symlink; psmux gets a block of zero lines today (its defaults give mouse, set-clipboard and wheel copy-mode; drag to the Windows clipboard and PageUp/PageDown inside full-screen apps are unverified; outside apps PageUp needs the prefix, PageDown does not leave copy-mode at the bottom and #{alternate_on} is always 0), so on reports the unverified and broken counts with exit 0 and only takes out an old tune-tmux block left in that file; inside full-screen apps (Claude Code fullscreen, Codex Fullscreen) the app keeps PageUp/PageDown and the mouse, and tmux copy-mode cannot scroll Codex history, a limit tmux config cannot fix; it never changes Claude Code or Codex settings" },
|
|
83
|
-
{ "name": "make-evidence", "type": "skill", "target": "both", "reason": "writes six evidence documents for a built service from real runs and commits them under docs/evidence/ (optimize
|
|
83
|
+
{ "name": "make-evidence", "type": "skill", "target": "both", "reason": "writes six evidence documents for a built service from real runs and commits them under docs/evidence/ (optimize, verify, flow, scale, decisions, trace) with scripts/evidence.mjs: run records each command (quoted command line, HEAD and dirty count, both streams with common secret forms masked, exit code passed through, stdin closed, a time limit that ends the process tree, no overwrite), measured numbers are METRIC name=value lines that check matches exactly against the log a document line names, room and collect give the oracle writer (a fresh-context sub-agent with a fixed prompt) a folder outside the repository holding only the requirement document and the owner facts, worktree and bug put each seeded bug (constraint, objective, permission) into a separate worktree of HEAD and judge it by ORACLE lines against a baseline (caught, missed, undecidable) while the user's work tree is left as it was, precheck stops on staged changes and asks about uncommitted ones and config hosts that are not this machine, commit takes only docs/evidence and tests/oracle (ignored logs included, hooks not skipped); --oracle-only writes the oracle before implementation; never edits the implementation, installs, downloads or pushes", "supportingFiles": ["scripts/evidence.mjs", "scripts/me-git.mjs", "scripts/me-run.mjs", "scripts/me-precheck.mjs", "scripts/me-check.mjs", "scripts/me-oracle.mjs", "scripts/me-commit.mjs"], "caveat": "the project supplies the server, its tests and the tools; a missing tool, a server that cannot be reached or a non-local config is written as unverified, never filled from unit tests; the sub-agent's list of files read is its own report, and context the runtime adds (the session folder's CLAUDE.md or AGENTS.md, memory) is written down as a limit; on Codex worktree, bug, commit, serve, stop and runs that reach a local server or DB ask for sandbox_permissions require_escalated" },
|
|
84
84
|
|
|
85
85
|
{ "name": "front-qa", "type": "command", "target": "both", "reason": "ralplan->ralph frontend-QA pipeline; Claude=OMC, Codex=OMX", "supportingFiles": [] },
|
|
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
|
}
|