@kaydash9999/banker-plugins 0.20.5 → 0.21.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.
Files changed (41) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +24 -0
  4. package/PRIVACY.md +21 -1
  5. package/README.md +22 -14
  6. package/bin/banker.js +144 -11
  7. package/bin/lib/codex-hooks.mjs +249 -0
  8. package/bin/lib/telemetry-config.mjs +2 -0
  9. package/codex/manifest.json +13 -10
  10. package/codex/skills/graceful-pause/SKILL.md +30 -0
  11. package/codex/skills/progress/SKILL.md +42 -0
  12. package/codex/skills/progress/scripts/progress-plan.mjs +722 -0
  13. package/codex/skills/progress/scripts/progress.mjs +295 -0
  14. package/codex/transform-matrix.md +18 -13
  15. package/hooks/obsidize-hook.mjs +116 -4
  16. package/hooks/progress-plan.mjs +1 -1
  17. package/hooks/telemetry-count-codex.mjs +222 -0
  18. package/hooks/update-notify.mjs +31 -11
  19. package/package.json +1 -1
  20. package/skills/append_wiki/SKILL.md +34 -8
  21. package/skills/compact-wiki/SKILL.md +37 -6
  22. package/skills/deep-research/SKILL.md +6 -0
  23. package/skills/lfm/SKILL.md +148 -0
  24. package/skills/lineage/SKILL.md +14 -4
  25. package/skills/lineage/lineage.py +168 -4
  26. package/skills/obsidizer/SKILL.md +29 -10
  27. package/skills/obsidizer/obsidize.mjs +42 -11
  28. package/skills/omc-patch/SKILL.md +6 -0
  29. package/skills/payload-mon/SKILL.md +2 -0
  30. package/skills/rfp-author/SKILL.md +5 -3
  31. package/skills/rfp-author/references/vendor-rfp-structure.md +1 -1
  32. package/skills/setup-bypass-permissions/SKILL.md +27 -9
  33. package/skills/setup-bypass-permissions/scripts/codex-bypass.mjs +135 -0
  34. package/skills/setup-bypass-permissions/scripts/codex-config.mjs +210 -0
  35. package/skills/setup-omc-hud/SKILL.md +33 -3
  36. package/skills/setup-omc-hud/assets/codex-config.mjs +210 -0
  37. package/skills/setup-omc-hud/assets/codex-status-line.mjs +90 -0
  38. package/skills/smart-compact/SKILL.md +58 -12
  39. package/skills/smart-compact/scripts/codex-gate.mjs +384 -0
  40. package/skills/ultra-chain/SKILL.md +8 -4
  41. package/skills/ultra-interview/SKILL.md +13 -2
@@ -0,0 +1,249 @@
1
+ /*
2
+ * banker 의 Codex 훅 설치. Claude Code 는 플러그인의 hooks/hooks.json 을 바로 읽지만 Codex 는
3
+ * <CODEX_HOME>/hooks.json 만 읽고, 새 훅은 사용자가 신뢰해야 돈다("Hooks need review", /hooks).
4
+ *
5
+ * - 훅 스크립트와 그 의존 파일을 <CODEX_HOME>/hooks-custom/banker/ 에 패키지와 같은 배치로 복사하고, 훅은 그 사본을
6
+ * 돌린다. Codex 는 신뢰한 훅을 샌드박스 밖에서 돌리므로, 작업 폴더 안의 파일을 가리키지 않게 하기 위해서다.
7
+ * - 묶음은 각 이벤트의 맨 뒤에 더한다. Codex 는 신뢰를 <hooks.json>:<이벤트>:<묶음 번호>:<항목 번호> 로 기록해, 앞에
8
+ * 끼우면 OMX 훅의 번호가 밀려 다시 신뢰해야 한다. 빼기는 우리 항목만, 낡은 명령은 같은 자리에서 고친다.
9
+ * - Windows 의 Codex 는 훅 명령을 PowerShell 로 돌린다(Codex 0.160.1 실측): `& '<node>' '<스크립트>' 인자`.
10
+ * - hooks.json 이 링크이거나 JSON 객체가 아니거나 "없음" 말고 다른 이유로 확인할 수 없으면 바꾸지 않는다. 바꿀 때마다
11
+ * 직전 내용을 hooks.json.banker.bak 에 둔다. smart-compact 의 codex-gate.mjs 와 같은 규칙이다.
12
+ */
13
+ import { spawnSync } from 'node:child_process';
14
+ import fs from 'node:fs';
15
+ import os from 'node:os';
16
+ import path from 'node:path';
17
+
18
+ // 훅마다 이벤트, 스크립트(패키지 기준 경로), 인자, 도구 이름 matcher(있으면 그 도구 호출 뒤에만 돈다).
19
+ export const HOOKS = {
20
+ update: { event: 'SessionStart', script: 'hooks/update-notify.mjs', args: ['--codex'] },
21
+ count: { event: 'Stop', script: 'hooks/telemetry-count-codex.mjs', args: [] },
22
+ obsidize: { event: 'PostToolUse', script: 'hooks/obsidize-hook.mjs', args: ['--codex'], matcher: 'Bash' },
23
+ };
24
+ // 사본으로 옮길 파일(훅 스크립트와 그 의존 파일). 상대 경로를 그대로 둬 '../bin/lib/...' 가져오기가 맞는다.
25
+ export const PAYLOAD = [
26
+ '.claude-plugin/plugin.json',
27
+ 'bin/lib/telemetry-config.mjs',
28
+ 'hooks/update-notify.mjs',
29
+ 'hooks/update-fetch.mjs',
30
+ 'hooks/update-checkin.mjs',
31
+ 'hooks/telemetry-count-codex.mjs',
32
+ 'hooks/obsidize-hook.mjs',
33
+ 'skills/obsidizer/obsidize.mjs',
34
+ ];
35
+ export const DEFAULT_HOOKS = ['update', 'count'];
36
+
37
+ // Codex 처럼 상대 경로 CODEX_HOME 은 지금 폴더 기준으로 푼다.
38
+ export function codexPaths(env = process.env) {
39
+ const home = env.CODEX_HOME ? path.resolve(env.CODEX_HOME) : path.join(os.homedir(), '.codex');
40
+ return { home, hooks: path.join(home, 'hooks.json'), backup: path.join(home, 'hooks.json.banker.bak'), dir: path.join(home, 'hooks-custom', 'banker') };
41
+ }
42
+
43
+ const isObject = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);
44
+
45
+ function writeAtomic(file, data) {
46
+ const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
47
+ try {
48
+ fs.writeFileSync(tmp, data, { flag: 'wx' });
49
+ const mode = modeOf(file);
50
+ if (mode !== null) fs.chmodSync(tmp, mode);
51
+ fs.renameSync(tmp, file);
52
+ } catch (e) {
53
+ fs.rmSync(tmp, { force: true });
54
+ throw e;
55
+ }
56
+ }
57
+
58
+ function modeOf(file) {
59
+ try {
60
+ const st = fs.lstatSync(file);
61
+ return st.isFile() ? st.mode & 0o7777 : null;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+
67
+ // 명령 글. POSIX 는 큰따옴표, 셸이 그 안에서 읽는 글자는 거부. Windows 는 PowerShell 호출 연산자와 작은따옴표.
68
+ export function formatCommand(node, script, args, win = process.platform === 'win32') {
69
+ const all = [node, script, ...args].join('');
70
+ if (/[\r\n\0]/.test(all)) return null;
71
+ if (win) {
72
+ const q = (s) => `'${s.replace(/['‘-‛]/g, '$&$&')}'`;
73
+ return ['&', q(node), q(script), ...args].join(' ');
74
+ }
75
+ if (/["$`\\]/.test(all)) return null;
76
+ return [`"${node}"`, `"${script}"`, ...args].join(' ');
77
+ }
78
+
79
+ // 임시 폴더의 node 는 거부한다. Codex 안에서 돌 때는 작업 폴더 안의 node 도(샌드박스 안 세션이 고칠 수 있다).
80
+ export function safeNode(node, { win = process.platform === 'win32', cwd = process.cwd(), codex = Boolean(process.env.CODEX_THREAD_ID) } = {}) {
81
+ const P = win ? path.win32 : path.posix;
82
+ const under = (f, dir) => {
83
+ const r = P.relative(dir, f);
84
+ return r === '' || (!r.startsWith('..') && !P.isAbsolute(r));
85
+ };
86
+ // A link elsewhere is judged where it leads too, so a link in a safe place cannot pin a node from the temp folder.
87
+ const places = [node, real(node)];
88
+ return !places.some((f) => under(f, os.tmpdir()) || (codex && under(f, cwd)));
89
+ }
90
+
91
+ function real(file) {
92
+ try {
93
+ return fs.realpathSync(file);
94
+ } catch {
95
+ return file;
96
+ }
97
+ }
98
+
99
+ const scriptOf = (p, id) => path.join(p.dir, ...HOOKS[id].script.split('/'));
100
+ const markOf = (id) => `/hooks-custom/banker/${HOOKS[id].script}`;
101
+ const isOurs = (h, id) => isObject(h) && typeof h.command === 'string' && h.command.replace(/\\/g, '/').includes(markOf(id));
102
+
103
+ function fileKind(file) {
104
+ try {
105
+ const st = fs.lstatSync(file);
106
+ return st.isSymbolicLink() || !st.isFile() ? `${file} 이 일반 파일이 아님(링크나 폴더)` : '';
107
+ } catch (e) {
108
+ return e && e.code === 'ENOENT' ? 'missing' : `${file} 을 확인하지 못함(${e && e.code})`;
109
+ }
110
+ }
111
+
112
+ // hooks.json 을 객체로, 또는 바꿀 수 없는 까닭. 쓰는 이벤트의 값이 배열이 아니면 거부한다.
113
+ export function loadHooks(file, events) {
114
+ const kind = fileKind(file);
115
+ if (kind === 'missing') return { doc: { hooks: {} } };
116
+ if (kind) return { why: kind };
117
+ let doc;
118
+ try {
119
+ doc = JSON.parse(fs.readFileSync(file, 'utf8'));
120
+ } catch {
121
+ doc = null;
122
+ }
123
+ if (!isObject(doc)) return { why: `${file} 이 JSON 객체가 아님` };
124
+ if (doc.hooks === undefined) doc.hooks = {};
125
+ if (!isObject(doc.hooks)) return { why: `${file} 의 hooks 가 객체가 아님` };
126
+ const bad = events.find((e) => doc.hooks[e] !== undefined && !Array.isArray(doc.hooks[e]));
127
+ return bad ? { why: `${file} 의 ${bad} 가 배열이 아님` } : { doc };
128
+ }
129
+
130
+ function findOurs(groups, id) {
131
+ for (let g = 0; g < groups.length; g++) {
132
+ const hs = isObject(groups[g]) && Array.isArray(groups[g].hooks) ? groups[g].hooks : [];
133
+ const h = hs.findIndex((x) => isOurs(x, id));
134
+ if (h >= 0) return [g, h];
135
+ }
136
+ return null;
137
+ }
138
+
139
+ function saveHooks(p, doc) {
140
+ if (fs.existsSync(p.hooks)) writeAtomic(p.backup, fs.readFileSync(p.hooks));
141
+ writeAtomic(p.hooks, `${JSON.stringify(doc, null, 2)}\n`);
142
+ }
143
+
144
+ // 사본 폴더. 링크인 폴더는 거부하고, 파일은 내용이 다를 때만 새로 쓴다.
145
+ export function installPayload(p, pkgRoot) {
146
+ fs.mkdirSync(p.dir, { recursive: true });
147
+ if (fs.lstatSync(p.dir).isSymbolicLink()) return `${p.dir} 이 링크임`;
148
+ for (const rel of PAYLOAD) {
149
+ const dest = path.join(p.dir, ...rel.split('/'));
150
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
151
+ if (fs.existsSync(dest) && !fs.lstatSync(dest).isFile()) return `${dest} 이 일반 파일이 아님`;
152
+ const data = fs.readFileSync(path.join(pkgRoot, ...rel.split('/')));
153
+ if (!fs.existsSync(dest) || !fs.readFileSync(dest).equals(data)) writeAtomic(dest, data);
154
+ }
155
+ return '';
156
+ }
157
+
158
+ // 한 훅을 제자리에 두기: 없으면 이벤트 맨 뒤 묶음으로, 명령이 낡았으면 같은 자리에서 고친다. 바뀌었으면 true.
159
+ function place(doc, id, command) {
160
+ const ev = HOOKS[id].event;
161
+ const groups = doc.hooks[ev] || [];
162
+ const at = findOurs(groups, id);
163
+ if (at && groups[at[0]].hooks[at[1]].command === command) return false;
164
+ if (at) groups[at[0]].hooks[at[1]] = { ...groups[at[0]].hooks[at[1]], command, timeout: 5 };
165
+ else groups.push({ ...(HOOKS[id].matcher ? { matcher: HOOKS[id].matcher } : {}), hooks: [{ type: 'command', command, timeout: 5 }] });
166
+ doc.hooks[ev] = groups;
167
+ return true;
168
+ }
169
+
170
+ // Codex 홈이 지금 폴더나 그 저장소 안이면(예: 저장소의 .envrc 가 CODEX_HOME=./.codex) 훅 사본도 작업 폴더 안에 놓여,
171
+ // 샌드박스 안의 세션이 샌드박스 밖에서 도는 훅을 고칠 수 있다. 홈 폴더와 파일 시스템 루트는 작업 폴더로 보지 않는다.
172
+ export function homeInWorkspace(home, cwd = process.cwd()) {
173
+ const top = spawnSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8', windowsHide: true });
174
+ const roots = [cwd, top.status === 0 ? top.stdout.trim() : ''].filter(Boolean).map(real);
175
+ const skip = new Set([real(os.homedir()), path.parse(real(cwd)).root]);
176
+ const h = real(home);
177
+ return roots.some((r) => !skip.has(r) && (h === r || h.startsWith(r.endsWith(path.sep) ? r : r + path.sep)));
178
+ }
179
+
180
+ // ids 를 켠다. { code, changed:[id], msg }. code 1 이면 아무것도 바꾸지 않았다.
181
+ export function enableHooks(p, ids, pkgRoot, node = process.execPath, cwd = process.cwd()) {
182
+ if (!safeNode(node)) return { code: 1, changed: [], msg: `node 가 안전하지 않은 곳에 있음(${node})` };
183
+ if (homeInWorkspace(p.home, cwd)) return { code: 1, changed: [], msg: `Codex 홈(${p.home})이 작업 폴더 안이라 훅을 설치하지 않음. 샌드박스가 고칠 수 있는 곳임` };
184
+ const { doc, why } = loadHooks(p.hooks, ids.map((id) => HOOKS[id].event));
185
+ if (why) return { code: 1, changed: [], msg: why };
186
+ const cmds = ids.map((id) => [id, formatCommand(node, scriptOf(p, id), HOOKS[id].args)]);
187
+ if (cmds.some(([, c]) => !c)) return { code: 1, changed: [], msg: '경로에 셸이 읽는 글자가 있음' };
188
+ const bad = installPayload(p, pkgRoot);
189
+ if (bad) return { code: 1, changed: [], msg: bad };
190
+ const changed = cmds.filter(([id, c]) => place(doc, id, c)).map(([id]) => id);
191
+ if (changed.length) saveHooks(p, doc);
192
+ return { code: 0, changed, msg: '' };
193
+ }
194
+
195
+ // ids 를 끈다: 우리 항목만 빼고, 비는 묶음과 이벤트를 지운다. 뒤 묶음의 번호가 밀리면 shifted 에 이벤트를 적는다.
196
+ export function disableHooks(p, ids) {
197
+ const { doc, why } = loadHooks(p.hooks, ids.map((id) => HOOKS[id].event));
198
+ if (why) return { code: 1, changed: [], shifted: [], msg: why };
199
+ const changed = [];
200
+ const shifted = [];
201
+ for (const id of ids) {
202
+ const ev = HOOKS[id].event;
203
+ const groups = doc.hooks[ev] || [];
204
+ const at = findOurs(groups, id);
205
+ if (!at) continue;
206
+ const hs = groups[at[0]].hooks.filter((h) => !isOurs(h, id));
207
+ if (hs.length) groups[at[0]].hooks = hs;
208
+ else {
209
+ if (at[0] < groups.length - 1) shifted.push(ev);
210
+ groups.splice(at[0], 1);
211
+ }
212
+ if (groups.length) doc.hooks[ev] = groups;
213
+ else delete doc.hooks[ev];
214
+ changed.push(id);
215
+ }
216
+ if (changed.length) saveHooks(p, doc);
217
+ return { code: 0, changed, shifted, msg: '' };
218
+ }
219
+
220
+ // 훅마다 'on' | 'off' | 'stale'(명령이 이 node 와 사본을 가리키지 않거나 사본이 이 판과 다름). 읽지 못하면 why.
221
+ export function hookStatus(p, ids, pkgRoot, node = process.execPath) {
222
+ const { doc, why } = loadHooks(p.hooks, ids.map((id) => HOOKS[id].event));
223
+ if (why) return { why };
224
+ const out = {};
225
+ for (const id of ids) {
226
+ const groups = doc.hooks[HOOKS[id].event] || [];
227
+ const at = findOurs(groups, id);
228
+ if (!at) out[id] = 'off';
229
+ else out[id] = groups[at[0]].hooks[at[1]].command === formatCommand(node, scriptOf(p, id), HOOKS[id].args) && copyCurrent(p, pkgRoot) ? 'on' : 'stale';
230
+ }
231
+ return { status: out };
232
+ }
233
+
234
+ function copyCurrent(p, pkgRoot) {
235
+ try {
236
+ return PAYLOAD.every((rel) => fs.readFileSync(path.join(p.dir, ...rel.split('/'))).equals(fs.readFileSync(path.join(pkgRoot, ...rel.split('/')))));
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
241
+
242
+ // 모든 banker 훅을 빼고 사본 폴더를 지운다(banker uninstall --codex).
243
+ export function removeAll(p) {
244
+ const r = disableHooks(p, Object.keys(HOOKS));
245
+ if (r.code === 0) fs.rmSync(p.dir, { recursive: true, force: true });
246
+ return r;
247
+ }
248
+
249
+ export const TRUST_NOTE = 'Codex 를 다시 열면 뜨는 Hooks need review 창이나 /hooks 에서 banker 훅을 신뢰하십시오. 신뢰 전에는 돌지 않습니다.';
@@ -40,6 +40,8 @@ export const USED_SKILLS_MAX = 300;
40
40
  // import 시점 resolver 결과를 캡처한다(단명 hook 프로세스용). writeUpdateCache 는 테스트 격리를 위해
41
41
  // configDir() 를 매 호출 재계산하므로 이 상수에 의존하지 않는다.
42
42
  export const UPDATE_CHECK_PATH = path.join(configDir(), 'update-check.json');
43
+ // Codex 카운팅 훅이 턴 시작을 못 찾은 Codex 판({version, notified}). 다음 Codex 세션 시작 때 한 번 알린다.
44
+ export const CODEX_SHAPE_MISS_PATH = path.join(configDir(), 'codex-shape-miss.json');
43
45
  // 업데이트 체크/체크인 스로틀 창(24h). 이 간격 미경과면 재조회하지 않는다.
44
46
  export const UPDATE_THROTTLE_MS = 24 * 60 * 60 * 1000;
45
47
 
@@ -2,17 +2,17 @@
2
2
  "version": 1,
3
3
  "note": "Per-surface target manifest for banker multi-tool distribution. Enumerate skills by SKILL.md presence (excludes ultra-init-workspace). target=both -> install to Claude Code AND Codex; claude-only -> Claude Code only. The Codex generator (codex/generate) reads this.",
4
4
  "surfaces": [
5
- { "name": "append_wiki", "type": "skill", "target": "both", "reason": "knowledge capture; tool-agnostic workflow", "supportingFiles": [], "caveat": "uses wiki_* MCP (OMC/OMX) at runtime; copies fine, needs a wiki MCP present" },
5
+ { "name": "append_wiki", "type": "skill", "target": "both", "reason": "knowledge capture; tool-agnostic workflow", "supportingFiles": [], "caveat": "Claude Code uses the OMC wiki_* MCP over .omc/wiki; Codex has no wiki MCP and uses the omx wiki CLI over omx_wiki/ (same tool names and inputs), which writes inside the workspace only" },
6
6
  { "name": "arch-diagram", "type": "skill", "target": "both", "reason": "PlantUML+pptx diagramming; tool-agnostic", "supportingFiles": ["build_pptx_template.py"] },
7
7
  { "name": "vertical-pptx", "type": "skill", "target": "both", "reason": "A4 portrait (210x297mm) pptx spec: build, verify, repair, and 16:9 conversion; tool-agnostic", "supportingFiles": ["build_a4p.js", "build_a4p.py", "verify_a4p.mjs"], "caveat": "owns the spec only and delegates layout/color/charts to a pptx document skill when one is reachable, falling back to a minimal self-contained block otherwise; the verify path needs node, and building needs pptxgenjs or python-pptx which it never auto-installs (points at vertical-pptx-setup instead)" },
8
8
  { "name": "vertical-pptx-setup", "type": "skill", "target": "both", "reason": "installs what vertical-pptx runs on (pptxgenjs or python-pptx) plus optional LibreOffice for PDF render checks; OS and privilege adaptive", "supportingFiles": [], "caveat": "LibreOffice is optional and only buys visual/PDF confirmation; without root it extracts to a home prefix, and on RHEL8/Rocky8 the distro build is required because upstream 26.x needs a newer glibc than 2.28" },
9
9
  { "name": "audit-mock", "type": "skill", "target": "both", "reason": "static anti-pattern audit; tool-agnostic", "supportingFiles": [] },
10
10
  { "name": "audit-security", "type": "skill", "target": "both", "reason": "SCA/SAST/secrets methodology; tool-agnostic", "supportingFiles": [] },
11
11
  { "name": "audit-web-page", "type": "skill", "target": "both", "reason": "playwright live page audit; tool-agnostic", "supportingFiles": ["harness.py"], "caveat": "needs playwright (setup-playwright)" },
12
- { "name": "compact-wiki", "type": "skill", "target": "both", "reason": "wiki dedup/merge; tool-agnostic workflow", "supportingFiles": [], "caveat": "uses wiki_* MCP at runtime" },
12
+ { "name": "compact-wiki", "type": "skill", "target": "both", "reason": "wiki dedup/merge; tool-agnostic workflow", "supportingFiles": [], "caveat": "Claude Code uses the OMC wiki_* MCP over .omc/wiki; Codex uses the omx wiki CLI over omx_wiki/ (no wiki MCP on Codex); the snapshot is omx_wiki.bak-<time> there" },
13
13
  { "name": "play-qa", "type": "skill", "target": "both", "reason": "Godot HTML5 in-browser QA; tool-agnostic", "supportingFiles": [], "caveat": "needs playwright (setup-playwright)" },
14
14
  { "name": "humanizer", "type": "skill", "target": "both", "reason": "AI-writing cleanup; tool-agnostic (vendored MIT)", "supportingFiles": ["LICENSE", "README.md", "AGENTS.md"] },
15
- { "name": "lineage", "type": "skill", "target": "both", "reason": "session->HTML export; the default flow has session-model reviewers (Claude=Plan without a model, Codex=spawn_agent without a role) judge noise and write one-line summaries from a redacted review pack; --rulebase is the rules-only one-pass run; reads Claude Code transcripts only", "supportingFiles": ["lineage.py"] },
15
+ { "name": "lineage", "type": "skill", "target": "both", "reason": "session->HTML export; the default flow has session-model reviewers (Claude=Plan without a model, Codex=spawn_agent without a role) judge noise and write one-line summaries from a redacted review pack; --rulebase is the rules-only one-pass run; reads Claude Code transcripts and Codex rollouts", "supportingFiles": ["lineage.py"], "caveat": "inside Codex the session is found by CODEX_THREAD_ID (the id at the end of the rollout file name) under $CODEX_HOME/sessions, and --all-sessions takes the rollouts whose session_meta cwd is this folder; from a rollout it reads what the user typed (user_message), what the model said (agent_message) and tool call names (response_item *_call), not the model input, reasoning or tool output; under workspace-write the reviewer-decision cache in ~/.cache/lineage is not written (one WARN) unless the apply step runs with approval or Codex starts with --add-dir ~/.cache/lineage" },
16
16
  { "name": "make-notion-guide", "type": "skill", "target": "both", "reason": "API guide authoring; tool-agnostic", "supportingFiles": [] },
17
17
  { "name": "nothing-design", "type": "skill", "target": "both", "reason": "UI design system; tool-agnostic", "supportingFiles": ["references/components.md", "references/platform-mapping.md", "references/tokens.md"] },
18
18
  { "name": "pdf-vision-extract", "type": "skill", "target": "both", "reason": "PDF->PNG for vision; tool-agnostic", "supportingFiles": [] },
@@ -28,7 +28,7 @@
28
28
  { "name": "ultra-init", "type": "skill", "target": "both", "reason": "orchestrates ralplan->ralph->QA gate (no ultragoal ledger: OMC 5.6.1 checkpoints need a /goal snapshot that would compete with ralph); Claude=OMC 5 (verify cycle in place of the removed ultraqa; needs the omc CLI with `omc ralph verify`), Codex=OMX (OMX ultraqa); body runtime-aware", "supportingFiles": [] },
29
29
  { "name": "omc-reference", "type": "skill", "target": "both", "reason": "OMC/OMX agent/tool/skill registry reference; runtime-aware (Codex counterpart is OMX)", "supportingFiles": [] },
30
30
  { "name": "setup-omc", "type": "skill", "target": "both", "reason": "installs OMC (Claude) or OMX (Codex `omx setup`); body already dual-runtime", "supportingFiles": [] },
31
- { "name": "setup-omc-hud", "type": "skill", "target": "both", "reason": "installs the omc_hud statusline wrapper v4 (Claude statusLine) from files bundled in assets/, offline and with one node command on every OS, then turns payload-mon on for the PL segment; off/status handle the update-notice block older wrappers took; Codex uses OMX `hud`; runtime-aware", "supportingFiles": ["assets/apply-hud.mjs", "assets/omc-hud-custom.mjs", "assets/hud-config.json", "assets/LICENSE-omc_hud", "scripts/claude-update-last.mjs"] },
31
+ { "name": "setup-omc-hud", "type": "skill", "target": "both", "reason": "installs the omc_hud statusline wrapper v4 (Claude statusLine) from files bundled in assets/, offline and with one node command on every OS, then turns payload-mon on for the PL segment; off/status handle the update-notice block older wrappers took; Codex target sets the TUI [tui] status_line to an OMX preset (assets/codex-status-line.mjs) only when none is set and removes only its own; runtime-aware", "supportingFiles": ["assets/apply-hud.mjs", "assets/codex-status-line.mjs", "assets/codex-config.mjs", "assets/omc-hud-custom.mjs", "assets/hud-config.json", "assets/LICENSE-omc_hud", "scripts/claude-update-last.mjs"], "caveat": "the target is the Claude Code statusLine; run from Codex with no target named, it asks first (Codex's own status line is [tui] status_line) and installs nothing when it cannot ask; the Claude Code install writes ~/.claude, so under workspace-write apply-hud and payload-mon on need approval to run outside it (not --add-dir ~/.claude); omx hud --tmux splits the current tmux window" },
32
32
  { "name": "setup-stitch", "type": "skill", "target": "both", "reason": "registers the Stitch MCP proxy (RockyLinux8 ~/bin/stitch-proxy.sh); Claude `claude mcp add`, Codex `codex mcp add`", "supportingFiles": [] },
33
33
 
34
34
  { "name": "docs-setup", "type": "skill", "target": "both", "reason": "installs arch-diagram/pdf-vision-extract deps (python-pptx, pymupdf, plantuml); python-env detect/select; runtime-agnostic installer", "supportingFiles": [] },
@@ -36,8 +36,9 @@
36
36
  { "name": "curation", "type": "skill", "target": "both", "reason": "decision-framing behavior (options/rec/confidence + --perf); no runtime deps; identical in both", "supportingFiles": [] },
37
37
  { "name": "deep-init", "type": "skill", "target": "both", "reason": "hierarchical AGENTS.md generation (port of OMC deepinit); runtime-agnostic fs+doc; subagents Claude=OMC/Codex=OMX", "supportingFiles": [] },
38
38
  { "name": "deep-research", "type": "skill", "target": "both", "reason": "fan-out + adversarial-verify web research (re-authored from bundled workflow); Claude WebSearch/Workflow, Codex OMX autoresearch; runtime-aware", "supportingFiles": [] },
39
+ { "name": "lfm", "type": "skill", "target": "both", "reason": "learn from master: researches how the top expert groups of a field actually work (steps, roles, quality bars, review, tools, failures and fixes) from the primary material they publish, separates shared from group-specific practice, and turns each into a plan item (M-n: apply, adapt, drop with a reason); ultra-interview, ultra-chain, deep-research and rfp-author call it in their research step; tool-agnostic (runtime web search and fetch)", "supportingFiles": [], "caveat": "needs a web search tool; with none or no network it ends with \"웹 조사 불가\" and writes no table from model knowledge; called by another skill it gets --no-questions" },
39
40
  { "name": "ralph-qa", "type": "skill", "target": "both", "reason": "independent verify loop (ralplan+critic logic): a backbone of 3+ reviewer agents on the session model always runs (Claude=Plan without a model, Codex=spawn_agent without a role or forked context; read-only by instruction); --codex/--gemini/--opencode seat that CLI's best model (asks when ambiguous, configured default when nobody can be asked), a node runner (references/seat-run.mjs) makes the private folders and the payload (a diff that carries the listed new files, made through a copy of the index), scans it, sends each seat from its own copy with answers kept elsewhere under a 600 s limit that ends the seat's whole process group, and cleans up (codex read-only with its shell tool, exec-policy rules, MCP servers, sub-agents, hooks, notify, web search, metrics and OpenTelemetry off, not seated when a managed config or a requirements file overrides those; gemini under a deny-all admin policy that a helper checks first, with no MCP servers, extensions or prompt telemetry, its error reports kept in the answer folder, and its session records removed after, those of runs cut short too; opencode as a per-run deny-all agent reading the payload on stdin, auto-compaction off). Seat-lifecycle monotone gate (INCONCLUSIVE on seat loss); runtime-aware", "supportingFiles": ["references/verifier-probe.mjs", "references/payload-scan.mjs", "references/gemini-read-only.toml", "references/gemini-seat.mjs", "references/seat-run.mjs", "references/seat-payload.mjs", "references/seat-spawn.mjs"] },
40
- { "name": "smart-compact", "type": "skill", "target": "both", "reason": "context-threshold compaction-hygiene gate (statusLine detect + hook bridge + assistant chain + TUI handoff); runtime-aware", "supportingFiles": [] },
41
+ { "name": "smart-compact", "type": "skill", "target": "both", "reason": "context-threshold compaction-hygiene gate (statusLine detect + hook bridge + assistant chain + TUI handoff); runtime-aware", "supportingFiles": ["scripts/codex-gate.mjs"], "caveat": "Claude Code detects through a statusLine wrapper; Codex through scripts/codex-gate.mjs: on copies it to $CODEX_HOME/hooks-custom/banker-smart-compact-gate.mjs (out of any workspace, since Codex runs trusted hooks outside the sandbox) and appends a UserPromptSubmit group for it last in hooks.json (PowerShell call form on Windows); the hook reads the rollout's last token_count (last_token_usage over model_context_window). Codex runs it only after the user trusts it (Hooks need review, /hooks); on/off write ~/.codex, so under workspace-write they need approval to run outside the sandbox (not --add-dir ~/.codex); run off before removing banker or OMX" },
41
42
  { "name": "visual-ralph", "type": "skill", "target": "both", "reason": "measured visual build loop (port of OMX visual-ralph); Claude ralph+visual-verdict+Stitch imagegen, Codex $ralph+$imagegen; runtime-aware", "supportingFiles": [] },
42
43
 
43
44
  { "name": "setup-node", "type": "skill", "target": "both", "reason": "installs nvm+Node22 (or winget/nvm-windows) per-OS; prerequisite for npx MCP servers + Codex/OMX tooling; runtime-agnostic installer", "supportingFiles": [] },
@@ -47,11 +48,11 @@
47
48
  { "name": "setup-tmux", "type": "skill", "target": "both", "reason": "installs tmux per-OS (Rocky8 3.6a source-build, apt/brew, Windows psmux); OMC team/worktree + OMX $team/HUD require it", "supportingFiles": [] },
48
49
  { "name": "setup-pwsh", "type": "skill", "target": "both", "reason": "Windows PowerShell 7 env (Terminal/Git/$PROFILE UTF-8); Claude CLAUDE_CODE_GIT_BASH_PATH, Codex native shell; no-op off-Windows", "supportingFiles": [] },
49
50
  { "name": "setup-mcp", "type": "skill", "target": "both", "reason": "installs common standalone MCP servers (context7/seq-thinking/filesystem/git/fetch); Claude `claude mcp add`, Codex config.toml [mcp_servers.*]; needs setup-node/python", "supportingFiles": [] },
50
- { "name": "setup-bypass-permissions", "type": "skill", "target": "both", "reason": "opt-in, typed by the person only (disable-model-invocation; Codex agents/openai.yaml allow_implicit_invocation: false): sets Claude Code permissions.defaultMode to bypassPermissions after a risk warning and an explicit confirmation; node script first, python3/PowerShell fallback when node cannot run, a command the person runs when the account may not write; off restores; Codex leaves its own approval policy alone", "supportingFiles": ["scripts/bypass-permissions.mjs", "scripts/fallback/bypass-permissions.py", "scripts/fallback/bypass-permissions.ps1", "agents/openai.yaml"] },
51
+ { "name": "setup-bypass-permissions", "type": "skill", "target": "both", "reason": "opt-in, typed by the person only (disable-model-invocation; Codex agents/openai.yaml allow_implicit_invocation: false): sets Claude Code permissions.defaultMode to bypassPermissions after a risk warning and an explicit confirmation; node script first, python3/PowerShell fallback when node cannot run, a command the person runs when the account may not write; off restores; target codex sets top-level approval_policy = \"never\" and sandbox_mode = \"danger-full-access\" in $CODEX_HOME/config.toml (scripts/codex-bypass.mjs, records the values before for off, refuses when /etc/codex requirements do not allow them)", "supportingFiles": ["scripts/bypass-permissions.mjs", "scripts/codex-bypass.mjs", "scripts/codex-config.mjs", "scripts/fallback/bypass-permissions.py", "scripts/fallback/bypass-permissions.ps1", "agents/openai.yaml"] },
51
52
  { "name": "setup-sandbox", "type": "skill", "target": "both", "reason": "agent sandbox per-OS (bubblewrap+userns / AppContainer / Seatbelt) + rust/cargo + git safe.directory; Codex sandbox_mode", "supportingFiles": [] },
52
53
  { "name": "setup-harness-factory","type": "skill", "target": "both", "reason": "installs+configures+guides the revfactory/harness team-architecture factory plugin (Claude harness@harness-marketplace, Codex SaehwanPark/meta-harness); Apache-2.0 referenced not redistributed", "supportingFiles": [] },
53
54
 
54
- { "name": "obsidizer", "type": "skill", "target": "both", "reason": "AI-wiki -> Obsidian graph normalize/interlink; LLM semantic edits + zero-input deterministic canonicalizer, runtime-agnostic; in-place durable", "supportingFiles": ["obsidize.mjs"], "caveat": "OMC-managed trees: 9-field frontmatter envelope + reserved files + no-rename + bare [[slug]] links only (extractWikiLinks slugifies piped/heading forms -> broken-ref); aliases written in generic vaults only; Canvas sidecars and body-inline :: are durable in-place; uses wiki_lint read-only. --enable is Claude-only (Codex has no MCP tool hooks) and is an honest no-op on Codex." },
55
+ { "name": "obsidizer", "type": "skill", "target": "both", "reason": "AI-wiki -> Obsidian graph normalize/interlink; LLM semantic edits + zero-input deterministic canonicalizer, runtime-agnostic; in-place durable", "supportingFiles": ["obsidize.mjs"], "caveat": "OMC-managed trees: 9-field frontmatter envelope + reserved files + no-rename + bare [[slug]] links only (extractWikiLinks slugifies piped/heading forms -> broken-ref); aliases written in generic vaults only; Canvas sidecars and body-inline :: are durable in-place; uses wiki_lint read-only. On Codex (no wiki MCP) --enable writes omx_wiki/.obsidizer and installs a user-scope PostToolUse hook (matcher Bash, `banker hooks enable obsidize`) that canonicalizes only after an `omx wiki` ingest/add shell call, using the pages that call's JSON names; that install writes ~/.codex, so under workspace-write it needs approval to run outside the sandbox (not --add-dir ~/.codex), and Codex runs the hook only after the user trusts it (Hooks need review, /hooks)." },
55
56
 
56
57
  { "name": "ultra-interview", "type": "skill", "target": "both", "reason": "research-first interview that resolves what public sources can answer and asks the human only what they alone can decide; discrete checklist gate (unresolved/total) instead of a scalar self-score", "supportingFiles": [], "caveat": "does NOT bundle or replace the OMC/OMX native deep-interview. Owns its own rubric on purpose: OMC scores 3 dimensions and OMX scores 5, so inheriting a native score would make the same threshold mean different things per runtime. Regulatory lookup is a general 'find the jurisdiction's primary source' instruction, not a hardcoded endpoint." },
57
58
  { "name": "interval-report", "type": "skill", "target": "both", "reason": "human-facing progress ledger at docs/intermission.md for long runs; thin time layer + renderer over whatever state exists", "supportingFiles": [], "caveat": "start time is persisted in the file and never re-stamped (a compaction/resume re-stamp collapses elapsed to 0 and biases the report optimistic exactly when it matters most). Wall-clock comes from `date` because neither runtime exposes hour:minute in context. Reads ultragoal state when present but never duplicates it; leaves compaction triggering to smart-compact." },
@@ -64,9 +65,9 @@
64
65
  { "name": "motion-graphic-make", "type": "skill", "target": "both", "reason": "thin wrapper delegating to hyperframes /motion-graphics (free form categories); owns no render pipeline, only the stage gates (plan approval, snapshot and render review, independent check) and the review page and preview server it shares with 3d-intro-build; runtime-agnostic", "supportingFiles": ["references/preview-lib.mjs", "references/serve.mjs", "references/curate.mjs"], "caveat": "needs hyperframes via motion-graphic-setup (npx, Apache-2.0); the three references are byte-identical copies of 3d-intro-build's (scripts/sync-adapter.js)" },
65
66
  { "name": "3d-intro-setup", "type": "skill", "target": "both", "reason": "collects Azure creds (direct or console-sample parse) to untracked env + FREE preflight; installs Node/ffmpeg; runtime-agnostic", "supportingFiles": ["references/azure-adapter.mjs", "references/video-pool.mjs"] },
66
67
  { "name": "3d-intro-build", "type": "skill", "target": "both", "reason": "scroll-scrub 3D intro: gpt-image stills, WAN key pool video with Sora-2 fallback (forward-chaining, or the optional hold-and-flight mode), a review page for every paid stage, a page with no inline script or style (strict CSP), optional scene-by-scene navigation and glass panel, and a Playwright measurement for the independent check; vendored scroll-world engine (MIT) left unmodified; cross-platform Node", "supportingFiles": ["references/azure-adapter.mjs", "references/video-pool.mjs", "references/scrub-engine.js", "references/index-template.html", "references/prompts.md", "references/LICENSE", "references/NOTICE.md", "references/preview-lib.mjs", "references/serve.mjs", "references/assemble.mjs", "references/curate.mjs", "references/intro-fixes.css", "references/panel-glass.css", "references/step-nav.js", "references/step-nav.css", "references/verify-intro.mjs", "references/verify.md"], "caveat": "the independent check (verify-intro.mjs) needs Node Playwright with Chromium (setup-playwright); the hold-and-flight mode needs WAN keys and is not yet confirmed by a paid smoke" },
67
- { "name": "payload-mon", "type": "skill", "target": "both", "reason": "toggles an estimated API request payload segment (bytes against Claude Code's 32MB request limit) in the OMC custom HUD statusline, beside ctx; pure Node, no deps", "supportingFiles": ["scripts/payload-mon.mjs", "scripts/payload-size.mjs"], "caveat": "measures Claude Code sessions only (the 32MB limit and the transcript format are Claude Code's); run from Codex it still manages the same machine's Claude Code HUD wrapper but never measures Codex's own requests. Needs the OMC custom HUD wrapper (setup-omc-hud); the wrapper imports a module copy kept beside it because plugin install paths are versioned and retired on update" },
68
+ { "name": "payload-mon", "type": "skill", "target": "both", "reason": "toggles an estimated API request payload segment (bytes against Claude Code's 32MB request limit) in the OMC custom HUD statusline, beside ctx; pure Node, no deps", "supportingFiles": ["scripts/payload-mon.mjs", "scripts/payload-size.mjs"], "caveat": "measures Claude Code sessions only (the 32MB limit and the transcript format are Claude Code's); run from Codex it still manages the same machine's Claude Code HUD wrapper but never measures Codex's own requests. Under Codex's workspace-write sandbox, on and off need approval to run outside it (not --add-dir ~/.claude, which would let the session rewrite hooks); status only reads. Needs the OMC custom HUD wrapper (setup-omc-hud); the wrapper imports a module copy kept beside it because plugin install paths are versioned and retired on update" },
68
69
  { "name": "tone-compact", "type": "skill", "target": "both", "reason": "turns compact Korean writing rules (ASD-STE100 based: lists and tables, transliterated terms, no decorative symbols) on or off across sessions; pure Node, no deps", "supportingFiles": ["scripts/tone-compact.mjs", "scripts/instruction-toggle.mjs"], "caveat": "per runtime: Claude Code gets <config>/rules/banker-tone-compact.md; Codex gets one marked block at the end of $CODEX_HOME/AGENTS.override.md (when it has content of its own) or AGENTS.md, wrapped in USER:OMX:POLICY so omx setup --merge-agents and a plain omx setup with team mode on keep it (--force, a confirmed overwrite or a plain omx setup with team mode off replace the file; run on again). Turning it on in one runtime leaves the other as it is" },
69
- { "name": "omc-patch", "type": "skill", "target": "both", "reason": "re-applies local OMC hook patches after an OMC auto-update and pins the OMC marketplace origin; finds a timeout helper an older kit left inside a block (hooks such as project-memory silently off) and fixes it by reverting the active folder and patching again; probes the project-memory hook in a scratch project; pure Node, no deps", "supportingFiles": ["scripts/omc-patch.mjs", "scripts/probe-pm.mjs"], "caveat": "acts on the Claude Code OMC plugin cache under ~/.claude even when run from Codex; TARGET_VERSION 5.7.0 is hard-coded in the tool and only a plain apply uses it; the skill passes --no-update on every apply and check; --root touches one plugin folder only and never the marketplace pin" },
70
+ { "name": "omc-patch", "type": "skill", "target": "both", "reason": "re-applies local OMC hook patches after an OMC auto-update and pins the OMC marketplace origin; finds a timeout helper an older kit left inside a block (hooks such as project-memory silently off) and fixes it by reverting the active folder and patching again; probes the project-memory hook in a scratch project; pure Node, no deps", "supportingFiles": ["scripts/omc-patch.mjs", "scripts/probe-pm.mjs"], "caveat": "acts on the Claude Code OMC plugin cache under ~/.claude even when run from Codex; under Codex's workspace-write sandbox --check and probe-pm run as they are, while apply and revert need approval to run outside it (not --add-dir ~/.claude); TARGET_VERSION 5.7.0 is hard-coded in the tool and only a plain apply uses it; the skill passes --no-update on every apply and check; --root touches one plugin folder only and never the marketplace pin" },
70
71
  { "name": "remains", "type": "skill", "target": "both", "reason": "finds remaining bugs, defects and unverified items and shows them in one table; runs the project's tests on registered SSH test boxes; pure Node, no deps", "supportingFiles": ["scripts/boxes.mjs"], "caveat": "box runs need ssh, scp and git on this machine and key-only SSH logins; boxes are registered in ~/.config/banker/test-boxes.json; read and test only, never fixes or pushes" },
71
72
  { "name": "trouble-shooting", "type": "skill", "target": "both", "reason": "investigates a problem and reports symptom, problem, cause, fix and action plan in one table, one row per problem; --no-plan stops at the fix; report only, never fixes", "supportingFiles": [] },
72
73
  { "name": "ultra-chain", "type": "skill", "target": "both", "reason": "orchestrates nine stages in order: deep-research, ultra-interview, ralplan --deliberate followed by a ralph-qa plan quorum of 3+ reviewers (ralplan has two reviewers and no way to add more), a mock the user approves, ralph --critic=critic (Codex: OMX ralph plus a critic loop, since OMX ralph has no --critic), tests on the registered OS boxes fed to a ralph-qa quorum, visual review (machine diff against the mock, screenshots, an independent verdict), a process report rendered from its own ledger, and local sharing; Claude=OMC 5, Codex=OMX; body runtime-aware; pure Node helpers, no deps", "supportingFiles": ["references/chain-ledger.mjs", "references/chain-report.mjs", "references/capture.mjs", "references/serve.mjs", "references/preview-lib.mjs"], "caveat": "uses the sibling banker skills deep-research, ultra-interview, ralph-qa and remains (boxes.mjs); box runs need ssh, scp and git with key-only logins in ~/.config/banker/test-boxes.json; screenshots need Playwright with Chromium; serve.mjs and preview-lib.mjs are byte-identical copies of 3d-intro-build's (scripts/sync-adapter.js); never pushes, opens a PR or posts outside without an explicit request" },
@@ -74,6 +75,8 @@
74
75
  { "name": "theme-selection", "type": "skill", "target": "both", "reason": "applies 74 themes to a project as CSS custom properties, light and dark (148 combinations), with contrast correction; also emits a theme picker (markup and two scripts) and a contrast audit (check), and reads the project's DESIGN.md as more themes; pure Node, no deps, no network", "supportingFiles": ["scripts/themes.mjs", "assets/palettes.json", "assets/LICENSE", "assets/NOTICE.md", "references/integrate.md", "references/contrast.md"], "caveat": "the bundled palettes derive from VoltAgent/awesome-design-md (MIT; LICENSE and NOTICE.md in assets/); theme names only point at the sites the designs reference, and the trademarks belong to their owners; --out overwrites the file it names (refused when that file itself is a symbolic link), so the skill asks first when that file exists" },
75
76
 
76
77
  { "name": "front-qa", "type": "command", "target": "both", "reason": "ralplan->ralph frontend-QA pipeline; Claude=OMC, Codex=OMX", "supportingFiles": [] },
77
- { "name": "setup", "type": "command", "target": "both", "reason": "installer orchestrator; Claude AskUserQuestion multi-select, Codex prompt-based selection", "supportingFiles": [] }
78
+ { "name": "setup", "type": "command", "target": "both", "reason": "installer orchestrator; Claude AskUserQuestion multi-select, Codex prompt-based selection", "supportingFiles": [] },
79
+ { "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)" },
80
+ { "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" }
78
81
  ]
79
82
  }
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: graceful-pause
3
+ description: "Codex 에서 작업 중에 보내면 지금 하던 단계만 끝내고 멈춘 뒤 보고하고 지시를 기다림. 작업이 도는 동안 입력창에 `$banker-graceful-pause` 를 쓰고 Enter 하면, 실행 중인 도구 호출이 끝난 뒤 그 턴에 전달됨. 'graceful-pause'/'멈춰'/'지금 단계만 끝내고 멈춤' 시 사용."
4
+ ---
5
+
6
+ # graceful-pause (Codex)
7
+
8
+ Claude Code 의 `/graceful-pause` 에 해당하는 Codex 스킬이다. 답변은 한글.
9
+
10
+ Codex 는 작업 중에 Enter 로 보낸 글을 "Messages to be submitted after next tool call" 로 보관했다가, 실행 중인 도구 호출이 끝나면 같은 턴에 넣는다.
11
+ 이 스킬을 그렇게 보내면 지금 단계가 끝난 자리에서 멈춘다(Codex 0.144.5 실측: `sleep 40` 이 끝난 뒤 남은 명령을 실행하지 않고 멈춤).
12
+
13
+ ## 이 스킬을 받으면
14
+
15
+ 1. 방금 끝난 도구 호출의 결과를 확인한다. 그 호출은 이미 끝난 상태로 전달된다.
16
+ 2. 남은 단계를 시작하지 않는다. 새 도구 호출, 파일 수정, 명령 실행, 하위 에이전트를 시작하지 않는다.
17
+ 3. 아래를 짧게 보고하고 사용자 지시를 기다린다.
18
+ - 지금까지 한 일
19
+ - 멈춘 지점(마지막으로 끝난 단계)
20
+ - 남은 단계
21
+ 4. 멈춘 자리가 일관되지 않으면(파일을 반쯤 고친 상태, 시작만 한 백그라운드 작업) 그 사실을 보고에 적는다. 되돌리거나 마저 하지 않는다.
22
+ 5. 스킬 뒤에 새 지시가 붙어 있으면 멈춘 다음 그 지시를 따른다.
23
+
24
+ ## 사용자가 쓰는 법
25
+
26
+ - 작업이 도는 동안 입력창에 `$banker-graceful-pause` 를 쓰고 Enter 한다. 새 지시를 뒤에 붙여도 된다.
27
+ - 지금 도구 호출까지 바로 끊으려면 Esc 를 누른다. 그때는 이 스킬이 필요 없다.
28
+ - 작업이 없을 때 쓰면 멈출 작업이 없다고 답한다.
29
+ - 스킬 이름을 치면 자동 완성 창이 뜬다. 첫 Enter 는 그 선택이고, 둘째 Enter 가 보낸다. 보낸 글은 입력창 위에
30
+ "Messages to be submitted after next tool call" 아래로 옮겨진다. 입력창에 글이 남아 있으면 Enter 를 한 번 더 누른다.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: progress
3
+ description: "Codex 에서 지금 세션의 진행 상황을 텍스트 트리로 보임. 맨 위는 이 세션이 쓰거나 읽은 마지막 계획 파일의 트리, 그 아래는 update_plan 단계(없으면 요청)와 진행 중인 단계의 최근 도구 호출. tmux 안이면 `--pane` 으로 오른쪽 칸에 2초마다 갱신. 'progress'/'진행 상황'/'어디까지 했어' 시 사용."
4
+ ---
5
+
6
+ # progress (Codex)
7
+
8
+ Claude Code 의 `/progress` 패널에 해당하는 Codex 스킬이다. 답변은 한글.
9
+
10
+ Codex 에는 Claude Code 의 mod 같은 상시 패널이 없다.
11
+ 그래서 이 스킬은 부를 때마다 지금 세션의 기록(rollout)을 읽어 한 번 보여 준다.
12
+ tmux 안이면 오른쪽 칸을 열어 계속 갱신할 수 있다.
13
+
14
+ ## 보이는 것
15
+
16
+ - 계획: 이 세션이 도구 호출로 쓰거나 읽은 마지막 계획 파일의 트리. 제목 아래 체크 수(`1/2`)와 체크박스(`[x]`, `[ ]`)를 보인다.
17
+ - 계획 파일: `.omx/plans/*.md`, `.omc/plans/*.md`, `docs/superpowers/plans/*.md`, `~/.claude/plans/*.md`, ralph `prd.json`, ultragoal `goals.json`
18
+ - 리뷰 기록(`*-critic-r2.md` 등)은 다른 계획이 없을 때만 쓴다.
19
+ - 단계: 마지막 `update_plan` 의 계획. `[>]` 가 진행 중인 단계다. `update_plan` 이 없으면 이 세션에서 사용자가 입력한 요청(최근 10개)이 단계다.
20
+ - 하위 목록: 진행 중인 단계(없으면 마지막 요청) 뒤의 최근 도구 호출 5개.
21
+
22
+ ## 실행
23
+
24
+ 스크립트는 이 스킬 폴더의 `scripts/progress.mjs` 다. Codex 가 셸에 주는 `CODEX_THREAD_ID` 로 지금 세션의 기록을 찾는다.
25
+
26
+ | 사용자 인자 | 명령 |
27
+ |---|---|
28
+ | 없음, `show` | `node "<스킬 폴더>/scripts/progress.mjs"` |
29
+ | `--depth N` | `node "<스킬 폴더>/scripts/progress.mjs" --depth N` (계획 트리를 N 단계까지 펼침, 기본 2) |
30
+ | `--pane`, `on` | `node "<스킬 폴더>/scripts/progress.mjs" --pane on` |
31
+ | `off` | `node "<스킬 폴더>/scripts/progress.mjs" --pane off` |
32
+
33
+ 1. 위 표의 명령 하나를 실행한다. 파일은 읽기만 한다.
34
+ 2. 출력은 고치지 않고 코드 블록 하나에 그대로 보인다. 그 아래에 한 줄로 지금 위치(진행 중인 단계)를 말한다.
35
+ 3. `--pane` 결과가 `tmux 에 닿지 못함` 이면 샌드박스가 tmux 연결을 막은 것이다. 그 명령만 샌드박스 밖 실행 승인을 요청한다. 승인을 요청할 수 없으면 사용자가 입력창에 `! node "<스킬 폴더>/scripts/progress.mjs" --pane on` 으로 직접 실행하도록 안내한다.
36
+ 4. `Codex 세션 기록을 찾지 못함` 이면 Codex 밖에서 실행한 것이다. 사용자에게 기록 파일 경로를 받아 `--rollout <파일>` 로 실행한다.
37
+
38
+ ## 한계
39
+
40
+ - 상시 패널이 아니다. 칸 없이 쓰면 부른 때의 상태만 보인다.
41
+ - 칸은 tmux 안에서만 열린다. 칸은 `--pane off` 나 tmux 로 닫을 때까지 남는다.
42
+ - 기록은 끝에서부터 32 MB 까지만 읽는다. 아주 긴 세션의 앞쪽 계획과 요청은 보이지 않을 수 있다.