@kaydash9999/banker-plugins 0.21.0 โ†’ 0.23.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 (129) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +69 -0
  4. package/PRIVACY.md +8 -3
  5. package/README.md +78 -15
  6. package/bin/banker.js +154 -32
  7. package/bin/lib/codex-hooks.mjs +123 -20
  8. package/bin/lib/codex-names.mjs +4 -0
  9. package/bin/lib/codex-routes.mjs +204 -0
  10. package/bin/lib/command-skill.cjs +50 -0
  11. package/bin/lib/telemetry-config.mjs +2 -1
  12. package/codex/command-skills/front-qa/SKILL.md +56 -0
  13. package/codex/command-skills/setup/SKILL.md +87 -0
  14. package/codex/hooks.json +29 -0
  15. package/codex/manifest.json +18 -15
  16. package/codex/skills/graceful-pause/SKILL.md +2 -2
  17. package/codex/skills/progress/scripts/progress.mjs +44 -14
  18. package/codex/skills/teamwork/SKILL.md +137 -0
  19. package/codex/skills/teamwork/scripts/teamwork.mjs +957 -0
  20. package/codex/transform-matrix.md +5 -3
  21. package/commands/setup.md +2 -2
  22. package/hooks/obsidize-hook.mjs +75 -32
  23. package/hooks/telemetry-count-codex.mjs +303 -35
  24. package/hooks/update-fetch.mjs +2 -1
  25. package/hooks/update-notify.mjs +146 -13
  26. package/package.json +3 -1
  27. package/skills/3d-intro-build/SKILL.md +101 -20
  28. package/skills/3d-intro-build/references/NOTICE.md +9 -2
  29. package/skills/3d-intro-build/references/assemble.mjs +113 -22
  30. package/skills/3d-intro-build/references/azure-adapter.mjs +110 -24
  31. package/skills/3d-intro-build/references/clip-post.mjs +443 -0
  32. package/skills/3d-intro-build/references/dive-mode.mjs +299 -0
  33. package/skills/3d-intro-build/references/intro-fixes.css +11 -0
  34. package/skills/3d-intro-build/references/prompts.md +51 -4
  35. package/skills/3d-intro-build/references/scrub-engine.js +1 -0
  36. package/skills/3d-intro-build/references/step-nav.css +29 -0
  37. package/skills/3d-intro-build/references/step-nav.js +277 -106
  38. package/skills/3d-intro-build/references/verify-intro.mjs +387 -32
  39. package/skills/3d-intro-build/references/verify.md +6 -0
  40. package/skills/3d-intro-build/references/video-pool.mjs +142 -12
  41. package/skills/3d-intro-setup/SKILL.md +20 -7
  42. package/skills/3d-intro-setup/references/azure-adapter.mjs +110 -24
  43. package/skills/3d-intro-setup/references/video-pool.mjs +142 -12
  44. package/skills/all-in-one/SKILL.md +125 -86
  45. package/skills/anti-drm/SKILL.md +1 -1
  46. package/skills/anti-drm/scripts/instruction-toggle.mjs +57 -4
  47. package/skills/arch-diagram/build_pptx_template.py +47 -28
  48. package/skills/audit-web-page/SKILL.md +1 -1
  49. package/skills/audit-web-page/harness.py +0 -1
  50. package/skills/lineage/lineage.py +1435 -533
  51. package/skills/motion-graphic-make/SKILL.md +1 -1
  52. package/skills/obsidizer/SKILL.md +20 -5
  53. package/skills/omc-patch/SKILL.md +1 -1
  54. package/skills/payload-mon/SKILL.md +2 -2
  55. package/skills/play-qa/SKILL.md +7 -5
  56. package/skills/progress-mon/README.md +162 -0
  57. package/skills/progress-mon/SKILL.md +165 -0
  58. package/skills/progress-mon/assets/fixture/README.md +1 -0
  59. package/skills/progress-mon/assets/fixture/arch.json +33 -0
  60. package/skills/progress-mon/assets/fixture/commits.json +7 -0
  61. package/skills/progress-mon/assets/fixture/config.json +30 -0
  62. package/skills/progress-mon/assets/fixture/events.jsonl +7 -0
  63. package/skills/progress-mon/assets/fixture/flow.json +44 -0
  64. package/skills/progress-mon/assets/fixture/items.json +14 -0
  65. package/skills/progress-mon/assets/fixture/req-spec.json +31 -0
  66. package/skills/progress-mon/assets/fixture/reqs.json +11 -0
  67. package/skills/progress-mon/assets/fixture/variants/arch-auto/arch.json +58 -0
  68. package/skills/progress-mon/assets/fixture/variants/check-auto/arch.json +15 -0
  69. package/skills/progress-mon/assets/fixture/variants/check-overlap/arch.json +14 -0
  70. package/skills/progress-mon/assets/fixture/variants/history-manual/config.json +13 -0
  71. package/skills/progress-mon/assets/fixture/variants/history-manual/items.json +7 -0
  72. package/skills/progress-mon/assets/fixture/variants/workflow-wide/flow.json +35 -0
  73. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a01.jsonl +1 -0
  74. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a02.jsonl +1 -0
  75. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a03.jsonl +1 -0
  76. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/agent-a04.jsonl +1 -0
  77. package/skills/progress-mon/assets/fixture/workflows/wf_ex1/journal.jsonl +7 -0
  78. package/skills/progress-mon/references/arch.md +280 -0
  79. package/skills/progress-mon/references/authoring.md +199 -0
  80. package/skills/progress-mon/references/config.md +115 -0
  81. package/skills/progress-mon/references/flow.md +75 -0
  82. package/skills/progress-mon/references/inventory.md +58 -0
  83. package/skills/progress-mon/references/items.md +74 -0
  84. package/skills/progress-mon/references/req-spec.md +74 -0
  85. package/skills/progress-mon/references/reqs.md +30 -0
  86. package/skills/progress-mon/scripts/index.js +31 -0
  87. package/skills/progress-mon/scripts/pm-check.mjs +526 -0
  88. package/skills/progress-mon/scripts/pm-config.mjs +341 -0
  89. package/skills/progress-mon/scripts/pm-data.mjs +394 -0
  90. package/skills/progress-mon/scripts/pm-html.mjs +77 -0
  91. package/skills/progress-mon/scripts/pm-memory.mjs +308 -0
  92. package/skills/progress-mon/scripts/pm-render.mjs +1644 -0
  93. package/skills/progress-mon/scripts/pm-server.mjs +735 -0
  94. package/skills/progress-mon/scripts/pm-sources.mjs +501 -0
  95. package/skills/progress-mon/scripts/progress-mon.mjs +167 -0
  96. package/skills/ralph-qa/SKILL.md +105 -16
  97. package/skills/ralph-qa/references/gate-evidence.mjs +457 -0
  98. package/skills/ralph-qa/references/payload-scan.mjs +19 -2
  99. package/skills/ralph-qa/references/run-folder.mjs +299 -0
  100. package/skills/ralph-qa/references/seat-payload.mjs +60 -13
  101. package/skills/ralph-qa/references/seat-run.mjs +159 -155
  102. package/skills/remains/SKILL.md +2 -1
  103. package/skills/remains/scripts/boxes.mjs +6 -2
  104. package/skills/rfp-author/scripts/md2docx.py +63 -34
  105. package/skills/setup-bypass-permissions/SKILL.md +2 -2
  106. package/skills/setup-bypass-permissions/scripts/codex-config.mjs +19 -8
  107. package/skills/setup-bypass-permissions/scripts/fallback/bypass-permissions.py +66 -15
  108. package/skills/setup-mcp/SKILL.md +1 -1
  109. package/skills/setup-omc-hud/SKILL.md +2 -1
  110. package/skills/setup-omc-hud/assets/codex-config.mjs +19 -8
  111. package/skills/smart-compact/SKILL.md +1 -1
  112. package/skills/smart-compact/scripts/codex-gate.mjs +2 -1
  113. package/skills/theme-selection/SKILL.md +1 -1
  114. package/skills/tone-compact/SKILL.md +1 -1
  115. package/skills/tone-compact/scripts/instruction-toggle.mjs +57 -4
  116. package/skills/ultra-chain/SKILL.md +34 -16
  117. package/skills/ultra-chain/references/chain-ledger.mjs +111 -3
  118. package/skills/ultra-init/SKILL.md +30 -22
  119. package/skills/ultra-ui-qa/SKILL.md +1 -1
  120. package/skills/ultraqa/NOTICE.md +34 -0
  121. package/skills/ultraqa/SKILL.md +196 -0
  122. package/skills/ultraqa/references/gate-evidence.mjs +457 -0
  123. package/skills/ultraqa/references/run-folder.mjs +299 -0
  124. package/skills/ultraqa/references/tmux-drive.md +52 -0
  125. package/skills/ultraqa/references/uqa-run.mjs +339 -0
  126. package/skills/update-banker/SKILL.md +32 -7
  127. package/skills/vertical-pptx/SKILL.md +1 -1
  128. package/skills/vertical-pptx/build_a4p.js +20 -2
  129. package/skills/vertical-pptx/build_a4p.py +560 -359
@@ -0,0 +1,204 @@
1
+ /*
2
+ * banker ๊ฐ€ Codex ์— ๋“ค์–ด๊ฐ€๋Š” ๋‘ ๊ธธ.
3
+ * - npm: `banker setup --codex` ๊ฐ€ <CODEX_HOME>/skills/banker-<์ด๋ฆ„>/ ์— ๋ณต์‚ฌํ•œ๋‹ค. ์Šคํ‚ฌ์€ $banker-<์ด๋ฆ„>.
4
+ * - ํ”Œ๋Ÿฌ๊ทธ์ธ: `codex plugin add banker@<๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค>` ๊ฐ€ <CODEX_HOME>/plugins/cache/<๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค>/banker/<ํŒ>/ ์—
5
+ * ์ €์žฅ์†Œ๋ฅผ ๋‘๊ณ  config.toml ์— [plugins."banker@<๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค>"] ๋ฅผ ์ ๋Š”๋‹ค. ์Šคํ‚ฌ์€ $banker:<์ด๋ฆ„>.
6
+ * ๋‘˜์„ ํ•จ๊ป˜ ๋‘๋ฉด ์Šคํ‚ฌ๊ณผ ํ›…์ด ๋‘ ๋ฒŒ์ด ๋œ๋‹ค. ์ง€์šฐ์ง€๋Š” ์•Š๊ณ  ์•Œ๋ฆฌ๊ธฐ๋งŒ ํ•œ๋‹ค(update-notify, banker setup --codex, doctor).
7
+ * ํ›…(update-notify, telemetry-count-codex, obsidize-hook ์˜ Codex ๊ธธ)๊ณผ bin/banker.js ๊ฐ€ ํ•จ๊ป˜ ์“ฐ๋ฏ€๋กœ npm ๊ธธ์˜ ํ›… ์‚ฌ๋ณธ(codex-hooks.mjs ์˜
8
+ * PAYLOAD)์—๋„ ๋“ค์–ด๊ฐ„๋‹ค.
9
+ */
10
+ import fs from 'node:fs';
11
+ import os from 'node:os';
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ export const PLUGIN = 'banker';
16
+ export const DEFAULT_MARKETPLACE = 'banker-plugins';
17
+ const NAME_RE = /^[A-Za-z0-9._-]{1,80}$/;
18
+
19
+ const MAX_CONFIG = 4 << 20;
20
+ // 4 MB ๊นŒ์ง€์˜ ์ผ๋ฐ˜ ํŒŒ์ผ๋งŒ ์ฝ๋Š”๋‹ค(FIFO ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Œ, ํ›…์˜ 5์ดˆ ์•ˆ์— ๋๋‚จ). ์—†๊ฑฐ๋‚˜ ์ฝ์ง€ ๋ชปํ•˜๋ฉด ''.
21
+ function readRegular(file) {
22
+ let fd;
23
+ try {
24
+ fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NONBLOCK ?? 0));
25
+ const st = fs.fstatSync(fd);
26
+ return st.isFile() && st.size <= MAX_CONFIG ? fs.readFileSync(fd, 'utf8') : '';
27
+ } catch {
28
+ return '';
29
+ } finally {
30
+ if (fd !== undefined) try { fs.closeSync(fd); } catch { /* best-effort */ }
31
+ }
32
+ }
33
+
34
+ // ํ”Œ๋Ÿฌ๊ทธ์ธ ํ›…์˜ PLUGIN_ROOT(<ํ™ˆ>/plugins/cache/<๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค>/banker/<ํŒ>)์—์„œ { home, marketplace }. ๊ทธ ๋ชจ์–‘์ด ์•„๋‹ˆ๋ฉด null.
35
+ export function pluginRootInfo(root) {
36
+ if (!root || !path.isAbsolute(root)) return null;
37
+ const ver = path.resolve(root);
38
+ const plugin = path.dirname(ver);
39
+ const mkt = path.dirname(plugin);
40
+ const cache = path.dirname(mkt);
41
+ const plugins = path.dirname(cache);
42
+ if (path.basename(plugin) !== PLUGIN || path.basename(cache) !== 'cache' || path.basename(plugins) !== 'plugins') return null;
43
+ const marketplace = path.basename(mkt);
44
+ return NAME_RE.test(marketplace) && NAME_RE.test(path.basename(ver)) ? { home: path.dirname(plugins), marketplace } : null;
45
+ }
46
+
47
+ // ์ด ์„ธ์…˜์˜ Codex ํ™ˆ: rollout ์ด <ํ™ˆ>/sessions/YYYY/MM/DD/ ์— ์žˆ์œผ๋ฉด ๊ทธ ํ™ˆ(์ƒ๋Œ€ ๊ฒฝ๋กœ CODEX_HOME ๋„ Codex ๊ฐ€ ํ‘ผ ๊ทธ๋Œ€๋กœ),
48
+ // ์•„๋‹ˆ๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ ํ›…์˜ PLUGIN_ROOT ๊ฐ€ ๋“  ํ™ˆ, ์•„๋‹ˆ๋ฉด CODEX_HOME, ๊ทธ๊ฒƒ๋„ ์—†์œผ๋ฉด ~/.codex.
49
+ export function codexHomeOf(transcript, env = process.env) {
50
+ const t = String(transcript ?? '');
51
+ const sessions = path.dirname(path.dirname(path.dirname(path.dirname(t))));
52
+ if (t && path.isAbsolute(t) && path.basename(sessions) === 'sessions') return path.dirname(sessions);
53
+ const info = pluginRootInfo(env.PLUGIN_ROOT);
54
+ if (info) return info.home;
55
+ return env.CODEX_HOME || path.join(os.homedir(), '.codex');
56
+ }
57
+
58
+ // Codex ํ™ˆ์˜ ์‹ค์ œ ๊ฒฝ๋กœ๊ฐ€ ์„ธ์…˜ ํด๋”(cwd) ์•„๋ž˜์ธ๋ฐ ๊ทธ ๋งจ ์œ„์˜ .codex ๊ฐ€ ์•„๋‹Œ์ง€. Codex 0.165.0 ์ƒŒ๋“œ๋ฐ•์Šค๋Š” ์„ธ์…˜ ํด๋” ๋งจ ์œ„์˜
59
+ // .git, .codex, .agents ๋งŒ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋‘๋ฏ€๋กœ, ๊ทธ ๋ฐ–(CODEX_HOME=<์„ธ์…˜>/deep/.codex, <์„ธ์…˜>/codexhome)์˜ Codex ํ™ˆ์€
60
+ // ์ƒŒ๋“œ๋ฐ•์Šค ์•ˆ์˜ ์„ธ์…˜์ด ๊ณ ์น  ์ˆ˜ ์žˆ๋‹ค(์„ค์ •, ํ›… ์‚ฌ๋ณธ, ํ”Œ๋Ÿฌ๊ทธ์ธ). ์„ธ์…˜ ์‹œ์ž‘ ์•Œ๋ฆผ๊ณผ banker doctor ๊ฐ€ ์“ด๋‹ค.
61
+ export function homeInSession(home, cwd) {
62
+ if (!home || !cwd || !path.isAbsolute(cwd)) return false;
63
+ const rel = path.relative(realPath(cwd), realPath(home));
64
+ if (rel.startsWith('..') || path.isAbsolute(rel)) return false;
65
+ return rel !== '.codex';
66
+ }
67
+
68
+ // ์‹ค์ œ ๊ฒฝ๋กœ. ์•„์ง ์—†๋Š” ๊ฒฝ๋กœ๋Š” ์žˆ๋Š” ์กฐ์ƒ๊นŒ์ง€๋งŒ ํ’€๊ณ  ๋‚˜๋จธ์ง€๋ฅผ ๋ถ™์ธ๋‹ค(macOS ์˜ /var ๋Š” /private/var ๋กœ ๊ฐ€๋Š” ๋งํฌ).
69
+ // native ๋ฅผ ๋จผ์ € ์“ด๋‹ค: Windows ์˜ JS ํŒ์€ 8.3 ์งง์€ ์ด๋ฆ„(C:\Users\LONGNA~1)์„ ๊ธด ์ด๋ฆ„์œผ๋กœ ํ’€์ง€ ์•Š๋Š”๋‹ค(Windows ๋ฐ•์Šค ์‹ค์ธก,
70
+ // Node 24.19.0). native ๊ฐ€ ๋ชป ์ฝ๋Š” ๋“œ๋ผ์ด๋ธŒ(์ผ๋ถ€ RAM ๋””์Šคํฌ)๋ฉด JS ํŒ์œผ๋กœ.
71
+ export function realPath(f) {
72
+ try {
73
+ return fs.realpathSync.native(f);
74
+ } catch {
75
+ try {
76
+ return fs.realpathSync(f);
77
+ } catch {
78
+ const up = path.dirname(f);
79
+ return up === f ? f : path.join(realPath(up), path.basename(f));
80
+ }
81
+ }
82
+ }
83
+
84
+ // config.toml ์˜ [sandbox_workspace_write] writable_roots(ํ‘œ ์•ˆ์˜ ํ‚ค์™€ ์  ํ‘œ๊ธฐ ๋‘˜ ๋‹ค). teamwork ์—”์ง„๊ณผ ๊ฐ™์€ ์ฝ๊ธฐ:
85
+ // ์ƒ๋Œ€ ๊ฒฝ๋กœ๋Š” Codex ํ™ˆ ๊ธฐ์ค€, ~ ๋Š” ํ™ˆ ํด๋”, ์‹ค์ œ ๊ฒฝ๋กœ๋กœ. config.toml ์ด ์—†๊ฑฐ๋‚˜ ์ฝ์ง€ ๋ชปํ•˜๋ฉด [].
86
+ export function writableRoots(home) {
87
+ const t = readRegular(path.join(home, 'config.toml'));
88
+ const sec = /^\[sandbox_workspace_write\][^\n]*\n([\s\S]*?)(?=^\s*\[|(?![\s\S]))/m.exec(t);
89
+ const lists = [sec && /^\s*writable_roots\s*=\s*\[([\s\S]*?)\]/m.exec(sec[1]), /^\s*sandbox_workspace_write\.writable_roots\s*=\s*\[([\s\S]*?)\]/m.exec(t)];
90
+ const items = lists.filter(Boolean).flatMap((m) => [...m[1].matchAll(/"((?:[^"\\]|\\.)*)"|'([^']*)'/g)].map((x) => x[1] ?? x[2]));
91
+ return items.map((p) => realPath(path.resolve(home, p.replace(/^~(?=[/\\]|$)/, os.homedir()))));
92
+ }
93
+
94
+ // ํ›…์ด ๋„๋Š” ๊ณณ: ํ›… ์Šคํฌ๋ฆฝํŠธ(๋ชจ๋“ˆ URL), ๊ทธ ์œ„์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ํด๋”๋‚˜ npm ๊ธธ ์‚ฌ๋ณธ ํด๋”(<ํด๋”>/hooks/<์Šคํฌ๋ฆฝํŠธ>), ์‹คํ–‰ ์ค‘์ธ node.
95
+ export function hookPlaces(scriptUrl) {
96
+ const file = fileURLToPath(scriptUrl);
97
+ return [file, path.dirname(path.dirname(file)), process.execPath];
98
+ }
99
+
100
+ // ํ›…์ด ๋„๋Š” ๊ณณ ๊ฐ€์šด๋ฐ writable_roots ํ•ญ๋ชฉ ์•„๋ž˜์ธ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ์•Œ๋ฆผ ๊ธ€, ์—†์œผ๋ฉด ''. Codex 0.165.0 ์ƒŒ๋“œ๋ฐ•์Šค๋Š” ์“ฐ๊ธฐ ํ—ˆ์šฉ
101
+ // ํด๋”๋งˆ๋‹ค ๋งจ ์œ„์˜ .git, .codex, .agents ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋‘๋ฏ€๋กœ ๊ทธ ํ•ญ๋ชฉ์˜ ๋งจ ์œ„ .codex ์•„๋ž˜(๊ธฐ๋ณธ ~/.codex)๋Š” ๋บ€๋‹ค.
102
+ // ๊ทธ๋Ÿฐ ๊ณณ์˜ ํ›… ํŒŒ์ผ๊ณผ node ๋Š” ์ƒŒ๋“œ๋ฐ•์Šค ์•ˆ์˜ ์„ธ์…˜์ด ๊ณ ์น  ์ˆ˜ ์žˆ๊ณ , Codex ๋Š” ์‹ ๋ขฐํ•œ ํ›…์„ ์ƒŒ๋“œ๋ฐ•์Šค ๋ฐ–์—์„œ ๋Œ๋ฆฐ๋‹ค.
103
+ // ์ด ๊ฒ€์‚ฌ๋Š” ๊ณ ์น˜๊ธฐ ์ „์˜ ์ฒซ ์„ธ์…˜์—์„œ ์•Œ๋ฆฌ๊ณ  ๋ฉˆ์ถ”๋Š” ๊ฒƒ์ด๋‹ค: ์ด๋ฏธ ๊ณ ์ณ์ง„ ํŒŒ์ผ์€ ์ด ๊ฒ€์‚ฌ๋„ ๊ณ ์น  ์ˆ˜ ์žˆ๋‹ค.
104
+ export function writableRootsNote(home, places) {
105
+ const roots = writableRoots(home);
106
+ if (!roots.length) return '';
107
+ for (const place of places) {
108
+ const p = realPath(place);
109
+ const root = roots.find((d) => {
110
+ const rel = path.relative(d, p);
111
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel) && rel.split(path.sep)[0] !== '.codex');
112
+ });
113
+ if (root) return `banker hooks skipped: ${p} is under the sandbox writable root ${root} (config.toml [sandbox_workspace_write] writable_roots), so a Codex session could change what this hook runs outside the sandbox`;
114
+ }
115
+ return '';
116
+ }
117
+
118
+ // npm ๊ธธ๋กœ ์„ค์น˜๋œ banker ์Šคํ‚ฌ์ด ์žˆ๋Š”์ง€(<ํ™ˆ>/skills/banker-*/SKILL.md).
119
+ export function npmRoute(home) {
120
+ try {
121
+ return fs.readdirSync(path.join(home, 'skills')).some((d) => d.startsWith('banker-') && fs.existsSync(path.join(home, 'skills', d, 'SKILL.md')));
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+
127
+ // ์ผœ์ ธ ์žˆ๋Š” banker ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ id(banker@<๋งˆ์ผ“ํ”Œ๋ ˆ์ด์Šค>) ๋ชฉ๋ก. config.toml ์˜ [plugins."banker@..."] ํ‘œ ์ค‘
128
+ // enabled = false ๊ฐ€ ์•„๋‹Œ ๊ฒƒ.
129
+ export function pluginRoutes(home) {
130
+ const lines = readRegular(path.join(home, 'config.toml')).split(/\r?\n/);
131
+ const out = [];
132
+ for (let i = 0; i < lines.length; i++) {
133
+ const m = /^\s*\[\s*plugins\s*\.\s*["']banker@([A-Za-z0-9._-]{1,80})["']\s*\]\s*(?:#.*)?$/.exec(lines[i]);
134
+ if (!m) continue;
135
+ let enabled = true;
136
+ for (let j = i + 1; j < lines.length && !/^\s*\[/.test(lines[j]); j++) {
137
+ if (/^\s*enabled\s*=\s*false\s*(?:#.*)?$/.test(lines[j])) enabled = false;
138
+ }
139
+ if (enabled) out.push(`${PLUGIN}@${m[1]}`);
140
+ }
141
+ return out;
142
+ }
143
+
144
+ // ํ”„๋กœ์ ํŠธ ๋ฒ”์œ„ npm ์„ค์น˜(`banker setup --codex --scope project` ์˜ <ํด๋”>/.codex/skills/banker-*)๊ฐ€ ์žˆ๋Š” ํด๋”. cwd ์—์„œ
145
+ // ์ €์žฅ์†Œ ์ตœ์ƒ์œ„(.git ์ด ์žˆ๋Š” ํด๋”)๊นŒ์ง€ ์˜ฌ๋ผ๊ฐ€๋ฉฐ ์ฐพ๋Š”๋‹ค(์ €์žฅ์†Œ ๋ฐ–์ด๋ฉด cwd ๋งŒ). ์—†์œผ๋ฉด ''.
146
+ export function projectNpmRoot(cwd) {
147
+ if (!cwd || !path.isAbsolute(cwd)) return '';
148
+ let dir = path.resolve(cwd);
149
+ const top = (() => {
150
+ for (let d = dir, i = 0; i < 64; i++) {
151
+ if (fs.existsSync(path.join(d, '.git'))) return d;
152
+ const up = path.dirname(d);
153
+ if (up === d) return '';
154
+ d = up;
155
+ }
156
+ return '';
157
+ })();
158
+ for (let i = 0; i < 64; i++) {
159
+ if (npmRoute(path.join(dir, '.codex'))) return dir;
160
+ if (!top || dir === top) return '';
161
+ dir = path.dirname(dir);
162
+ }
163
+ return '';
164
+ }
165
+
166
+ // ๋‘ ๊ธธ์ด ํ•จ๊ป˜ ์žˆ์œผ๋ฉด ์•Œ๋ฆผ ๊ธ€, ์•„๋‹ˆ๋ฉด ''. npm ๊ธธ์€ ์‚ฌ์šฉ์ž ๋ฒ”์œ„(<ํ™ˆ>/skills)์™€, cwd ๊ฐ€ ์žˆ์œผ๋ฉด ํ”„๋กœ์ ํŠธ ๋ฒ”์œ„๋„ ๋ณธ๋‹ค.
167
+ export function dualInstallNote(home, cwd = '') {
168
+ const plugins = pluginRoutes(home);
169
+ if (!plugins.length || !(npmRoute(home) || projectNpmRoot(cwd))) return '';
170
+ return `banker ๊ฐ€ Codex ์— ๋‘ ๋ฒˆ ์„ค์น˜๋จ: npm ์˜ banker setup --codex ์™€ Codex ํ”Œ๋Ÿฌ๊ทธ์ธ ${plugins.join(', ')}. `
171
+ + '์Šคํ‚ฌ์ด $banker-<์ด๋ฆ„> ๊ณผ $banker:<์ด๋ฆ„> ์œผ๋กœ ๊ฒน์นฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋งŒ ๋‘์‹ญ์‹œ์˜ค: '
172
+ + `npm ์ชฝ์„ ๋นผ๋ ค๋ฉด banker uninstall --codex, ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋นผ๋ ค๋ฉด codex plugin remove ${plugins[0]}`;
173
+ }
174
+
175
+ // ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ธธ์˜ ์Šคํ‚ฌ ๋ชฉ๋ก(.codex-plugin/plugin.json skills ์˜ ์Šคํ‚ฌ๋ณ„ ๊ฒฝ๋กœ)์„ ์ฝ๋Š”๋‹ค๊ณ  ํ™•์ธํ•œ ๊ฐ€์žฅ ๋‚ฎ์€ Codex ํŒ.
176
+ // 0.160.1(Windows), 0.165.0(Linux) ์—์„œ ํ™•์ธํ–ˆ๊ณ  0.144.5 ๋Š” ์žฌ์ง€ ๋ชปํ–ˆ๋‹ค. ๊ทธ๋ณด๋‹ค ๋‚ฎ์€ ํŒ์€ banker ์Šคํ‚ฌ์ด ๊ฑฐ์˜ ๋‹ค ์•ˆ ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค.
177
+ export const MIN_PLUGIN_CODEX = '0.160.1';
178
+
179
+ const triple = (v) => (/^(\d+)\.(\d+)\.(\d+)/.exec(String(v || '')) || []).slice(1, 4).map(Number);
180
+
181
+ // ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ธธ์—์„œ Codex ํŒ์ด MIN_PLUGIN_CODEX ๋ณด๋‹ค ๋‚ฎ์œผ๋ฉด ์•Œ๋ฆผ ํ•œ ์ค„, ์•„๋‹ˆ๋ฉด(ํŒ์„ ๋ชจ๋ฅผ ๋•Œ ํฌํ•จ) ''.
182
+ export function pluginVersionNote(version) {
183
+ const v = triple(version);
184
+ const min = triple(MIN_PLUGIN_CODEX);
185
+ if (v.length !== 3) return '';
186
+ const lower = v[0] !== min[0] ? v[0] < min[0] : v[1] !== min[1] ? v[1] < min[1] : v[2] < min[2];
187
+ if (!lower) return '';
188
+ return `banker: Codex ${v.join('.')} ์€ ${MIN_PLUGIN_CODEX} ๋ณด๋‹ค ๋‚ฎ์•„ banker ํ”Œ๋Ÿฌ๊ทธ์ธ ์Šคํ‚ฌ์ด ์•ˆ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(${MIN_PLUGIN_CODEX} ์ด์ƒ์—์„œ ํ™•์ธ). `
189
+ + 'Codex ๋ฅผ ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜ npm ๊ธธ(banker setup --codex)์„ ์“ฐ์‹ญ์‹œ์˜ค';
190
+ }
191
+
192
+ // `codex --version` ์˜ ํŒ(x.y.z). ์—†๊ฑฐ๋‚˜ 2์ดˆ ์•ˆ์— ๋‹ตํ•˜์ง€ ์•Š์œผ๋ฉด ''. Windows ๋Š” codex.cmd ๋ฅผ cmd ๋กœ ๋ถ€๋ฅธ๋‹ค. cmd ๋Š”
193
+ // ์ด๋ฆ„๋งŒ ์ค€ ๋ช…๋ น์„ PATH ๋ณด๋‹ค ์ง€๊ธˆ ํด๋”์—์„œ ๋จผ์ € ์ฐพ์œผ๋ฏ€๋กœ(ํ›…์˜ ์ง€๊ธˆ ํด๋”๋Š” ์„ธ์…˜ ํด๋”) ํ™ˆ ํด๋”์—์„œ ๋ถ€๋ฅด๊ณ 
194
+ // NoDefaultCurrentDirectoryInExePath ๋กœ ์ง€๊ธˆ ํด๋” ์ฐพ๊ธฐ๋ฅผ ๋ˆ๋‹ค.
195
+ export function codexCliVersion(spawn) {
196
+ const win = process.platform === 'win32';
197
+ try {
198
+ const opts = { encoding: 'utf8', timeout: 2000, shell: win, input: '', windowsHide: true, cwd: os.homedir(), env: { ...process.env, NoDefaultCurrentDirectoryInExePath: '1' } };
199
+ const r = spawn(win ? 'codex.cmd --version' : 'codex', win ? [] : ['--version'], opts);
200
+ return (/(\d+\.\d+\.\d+)/.exec(String(r.stdout || '')) || [])[1] || '';
201
+ } catch {
202
+ return '';
203
+ }
204
+ }
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+ /*
3
+ * A command (commands/<name>.md) as a Codex skill. Codex 0.144+ has no custom prompts, so each command is also a skill.
4
+ * Two installs use it:
5
+ * - npm (banker setup --codex): writes <CODEX_HOME>/skills/banker-<name>/SKILL.md, invoked as $banker-<name>.
6
+ * - Codex plugin (codex plugin add banker@banker-plugins): scripts/sync-codex-plugin.js writes
7
+ * codex/command-skills/<name>/SKILL.md in the repo, invoked as $banker:<name>.
8
+ * The command's description becomes the skill's, or the manifest's codexDescription when it has one. Skills Codex
9
+ * names apart (codex/manifest.json codexName: setup-omc is setup-omx there) take their Codex name in the body: whole
10
+ * names only, so omc-patch, payload-mon and .omc/ paths stay. A phrase that says what the renamed skill installs
11
+ * (CODEX_PHRASES) is written for Codex as a whole first: the name alone would send an OMX skill to install OMC. Codex
12
+ * skills get no $ARGUMENTS substitution, so a note says where the arguments come from.
13
+ */
14
+ // [command text, its Codex text]. scripts/sync-codex-plugin.test.mjs checks that each still occurs in commands/.
15
+ const CODEX_PHRASES = [
16
+ ['oh-my-claudecode(๋˜๋Š” `setup-omc` ์Šคํ‚ฌ / `npm install -g oh-my-claude-sisyphus@latest`)', 'oh-my-codex(OMX)(๋˜๋Š” `setup-omx` ์Šคํ‚ฌ)'],
17
+ ['**setup-omc** โ€” oh-my-claudecode(OMC) ์„ค์น˜/๊ฐฑ์‹ (Codex๋Š” OMX).', '**setup-omx** โ€” oh-my-codex(OMX) ์„ค์น˜/๊ฐฑ์‹ .'],
18
+ ['**setup-omc-hud** โ€” omc_hud ์ƒํƒœํ‘œ์‹œ์ค„(๋ž˜ํผ v4, ์Šคํ‚ฌ์— ๋“  ํŒŒ์ผ๋กœ ์˜คํ”„๋ผ์ธ ์„ค์น˜).', '**setup-omx-hud** โ€” Codex TUI ์ƒํƒœํ‘œ์‹œ์ค„(`[tui] status_line`)์„ OMX ํ”„๋ฆฌ์…‹์œผ๋กœ. ๊ฐ™์€ ๋จธ์‹ ์˜ Claude Code omc_hud ๋ž˜ํผ(v4)๋ฅผ ๊ณ ๋ฅด๋ฉด ์Šคํ‚ฌ์— ๋“  ํŒŒ์ผ๋กœ ์˜คํ”„๋ผ์ธ ์„ค์น˜.'],
19
+ ];
20
+
21
+ function codexNames(text, rename) {
22
+ const keys = Object.keys(rename || {}).sort((a, b) => b.length - a.length);
23
+ if (!keys.length) return text;
24
+ const re = new RegExp(`(?<![A-Za-z0-9_-])(${keys.map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})(?![A-Za-z0-9_-])`, 'g');
25
+ return text.replace(re, (k) => rename[k]);
26
+ }
27
+
28
+ function commandSkillText(text, name, invoke, { description, rename } = {}) {
29
+ const fm = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
30
+ const desc = fm && fm[1].match(/^description:\s*(.*)$/m);
31
+ const raw = fm ? text.slice(fm[0].length) : text;
32
+ const body = codexNames(rename ? CODEX_PHRASES.reduce((t, [from, to]) => t.split(from).join(to), raw) : raw, rename);
33
+ const note = '> Codex: ์ด ๋ช…๋ น์€ ์Šคํ‚ฌ๋กœ ์„ค์น˜๋๋‹ค. ๋ณธ๋ฌธ์˜ `$ARGUMENTS`, `$1` ์€ ์‚ฌ์šฉ์ž๊ฐ€ `' + invoke + '` ๋’ค์— ์“ด ๊ธ€์ด๋‹ค.\n\n';
34
+ const line = description ? JSON.stringify(description) : (desc ? desc[1] : '""');
35
+ return `---\nname: ${name}\ndescription: ${line}\n---\n\n${note}${body}`;
36
+ }
37
+
38
+ // A SKILL.md with its frontmatter name set (added first when missing) and, when given, its description: only those
39
+ // two lines change, and the file's line ends stay. The description is written as a JSON string, which YAML reads as a
40
+ // double-quoted one. A file without frontmatter comes back as it is.
41
+ function codexFrontmatter(src, name, description) {
42
+ const fm = src.match(/^---(\r?\n)([\s\S]*?)(\r?\n)---/);
43
+ if (!fm) return src;
44
+ let block = fm[2];
45
+ block = /^name:.*$/m.test(block) ? block.replace(/^name:.*$/m, () => `name: ${name}`) : `name: ${name}${fm[1]}${block}`;
46
+ if (description) block = block.replace(/^description:.*$/m, () => `description: ${JSON.stringify(description)}`);
47
+ return `${src.slice(0, fm.index)}---${fm[1]}${block}${fm[3]}---${src.slice(fm.index + fm[0].length)}`;
48
+ }
49
+
50
+ module.exports = { commandSkillText, codexNames, codexFrontmatter, CODEX_PHRASES };
@@ -62,7 +62,8 @@ export const SKILL_CHANGES_URL = 'https://raw.githubusercontent.com/smallOpenSou
62
62
 
63
63
  // usage-log ์™€ ์ฒดํฌ์ธ ๋ณธ๋ฌธ์— ์‹ค๋ฆฌ๋Š” ์Šคํ‚ฌ/์ปค๋งจ๋“œ ์ด๋ฆ„ ๊ผด. ์“ฐ๋Š” ํ›… ๋‘˜๊ณผ ๋ณด๋‚ด๋Š” ํ›…์ด ๊ฐ™์€ ๊ทœ์น™์„ ์“ด๋‹ค.
64
64
  // ์ด๋ฆ„์€ 64์ž๊นŒ์ง€๋‹ค(๊ฐ€์žฅ ๊ธด ์‹ค์ œ ์ด๋ฆ„์€ 24์ž). ๋ชจ๋ธ์ด ์ง€์–ด๋‚ธ ๊ธด ์ด๋ฆ„์ด ๊ธฐ๋ก๊ณผ ์ฒดํฌ์ธ์— ์‹ค๋ฆฌ์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.
65
- export const SKILL_NAME_RE = /^banker:[a-z0-9_-]{1,64}$/i;
65
+ // ์Šคํ‚ฌ ํด๋” ์ด๋ฆ„์€ ๋ชจ๋‘ ์†Œ๋ฌธ์ž๋ผ ๋Œ€๋ฌธ์ž๋Š” ๋ฐ›์ง€ ์•Š๋Š”๋‹ค(`banker:FRONT-QA` ๊ฐ€ ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์„ธ์–ด์ง€์ง€ ์•Š๊ฒŒ).
66
+ export const SKILL_NAME_RE = /^banker:[a-z0-9_-]{1,64}$/;
66
67
 
67
68
  // ๊ฐฑ์‹  ํ™•์ธ ์š”์ฒญ ํ•˜๋‚˜์˜ ์ „์ฒด ๋งˆ๊ฐ(ms). ์‹œํ—˜์ด ์ค„์ผ ์ˆ˜ ์žˆ๊ฒŒ BANKER_NET_DEADLINE_MS ๋ฅผ ๋ฐ›๋˜ ์ค„์ด๊ธฐ๋งŒ ํ•œ๋‹ค: 30์ดˆ๋ณด๋‹ค
68
69
  // ๊ธธ๋ฉด 30์ดˆ๋‹ค. setTimeout ์ด 1ms ๋กœ ๋ฐ”๊ฟ” ๋ฒ„๋ฆฌ๋Š” ๊ฐ’(0 ์ดํ•˜, ์ˆซ์ž ์•„๋‹˜)๋„ ๋ฒ„๋ฆฌ๊ณ  30์ดˆ๋ฅผ ์“ด๋‹ค. ๋ฌธ์„œ์— ์ ์ง€ ์•Š๋Š” ์‹œํ—˜์šฉ ์žฅ์น˜๋‹ค.
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: front-qa
3
+ description: "์ŠคํŽ™ ํŒŒ์ผ(๊ธฐ๋ณธ `docs/note.md`)์„ ๋ฐ”ํƒ•์œผ๋กœ ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ๋ ˆํผ๋Ÿฐ์Šค ๊ตฌํ˜„๊ณผ ๊ฐ™์€์ง€ ์—„๊ฒฉํ•˜๊ฒŒ QA ํ•˜๋Š” ์›Œํฌํ”Œ๋กœ(`ralplan`, `ralph`, `/loop`)."
4
+ ---
5
+
6
+ > Codex: ์ด ๋ช…๋ น์€ ์Šคํ‚ฌ๋กœ ์„ค์น˜๋๋‹ค. ๋ณธ๋ฌธ์˜ `$ARGUMENTS`, `$1` ์€ ์‚ฌ์šฉ์ž๊ฐ€ `$banker:front-qa` ๋’ค์— ์“ด ๊ธ€์ด๋‹ค.
7
+
8
+
9
+ # /front-qa โ€” ์ŠคํŽ™ ๊ธฐ๋ฐ˜ ํ”„๋ก ํŠธ์—”๋“œ ๊ตฌํ˜„ + ์—„๊ฒฉ QA
10
+
11
+ `$1`(์—†์œผ๋ฉด `docs/note.md`. ๊ทธ ํŒŒ์ผ์ด ์—†๊ณ  `docs/note.txt` ๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ํŒŒ์ผ)์˜ ์š”๊ตฌ์‚ฌํ•ญ์„ React ํ”„๋ก ํŠธ์—”๋“œ(`$FRONTEND_DIR`, ์˜ˆ: `apps/web`)์— ๊ตฌํ˜„ํ•˜๊ณ ,
12
+ **๋ ˆํผ๋Ÿฐ์Šค ๊ตฌํ˜„(reference implementation โ€” ์˜ˆ: ์ด์ „ Streamlit/ํƒ€ ๋นŒ๋“œ)๊ณผ ์—„๊ฒฉ ๋น„๊ต + ์ŠคํŽ™ ์ง€์‹œ ์ •ํ™• ์ค€์ˆ˜**๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค. ๋ชจ๋“  ํ•ญ๋ชฉ ํ•ด๊ฒฐ๊นŒ์ง€ ์ง„ํ–‰.
13
+
14
+ ## 0. ์ „์ œ์กฐ๊ฑด: OMC (์—†์œผ๋ฉด ์„ค์น˜๋ถ€ํ„ฐ)
15
+
16
+ ์ด ์ปค๋งจ๋“œ๋Š” `/ralplan`ยท`/ralph` ๋“ฑ oh-my-claudecode(OMC)์— ์˜์กดํ•œ๋‹ค. **์ง„ํ–‰ ์ „ ๋จผ์ € ํ™•์ธ**ํ•˜๊ณ , ์—†๊ฑฐ๋‚˜ ๋‚ก์•˜์œผ๋ฉด **์„ค์น˜ยท๊ฐฑ์‹ ๋ถ€ํ„ฐ ์•ˆ๋‚ด**ํ•œ๋‹ค:
17
+ - ํ™•์ธ(Claude Code): `/oh-my-claudecode:*` ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ + `omc` CLI ๊ฐ€ `ralph verify` ๋ฅผ ๊ฐ–์ท„๋Š”์ง€. OMC 5 ์˜ `ralph` ๋Š” ์ฒซ ๋ฐ˜๋ณต์— `omc ralph verify` ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์—†์œผ๋ฉด ๋ฉˆ์ถ˜๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ๋ณด๋‹ค ๋‚ก์€ 4.x CLI ๋Š” ์ด ๋ช…๋ น์ด ์—†๋Š”๋ฐ๋„ `--help` ๋ฅผ ์ฃผ๋ฉด ์ „์ฒด ๋„์›€๋ง์„ ์ฐ๊ณ  ์ข…๋ฃŒ ์ฝ”๋“œ 0 ์„ ๋‚ด๋ฏ€๋กœ **์ถœ๋ ฅ ๋ฌธ๊ตฌ๋กœ ํŒ์ •**ํ•œ๋‹ค(`--help` ์—†์ด ๋ถ€๋ฅด๋ฉด Claude ์‹คํ–‰์œผ๋กœ ๋„˜์–ด๊ฐ€๋‹ˆ ๋ฐ˜๋“œ์‹œ ๋ถ™์ธ๋‹ค). Codex ๋Š” `omx --version` ์œผ๋กœ OMX ๋ฅผ ํ™•์ธํ•œ๋‹ค:
18
+ ```bash
19
+ omc ralph verify --help 2>&1 | grep -q 'Usage: omc ralph verify' && echo "omc ok" || echo "omc CLI ์—†์Œ ๋˜๋Š” ๋‚ก์Œ"
20
+ ```
21
+ - ์‹คํŒจํ•˜๋ฉด `/banker:setup` โ†’ oh-my-codex(OMX)(๋˜๋Š” `setup-omx` ์Šคํ‚ฌ)๋กœ ๋จผ์ € ๊ตฌ์„ฑํ•œ ๋’ค ์ด์–ด์„œ ์ง„ํ–‰. ์ ๊ฒ€์„ ํ†ต๊ณผํ•˜๊ธฐ ์ „์—๋Š” ์ ˆ์ฐจ 1์„ ์‹œ์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
22
+ - **๋Ÿฐํƒ€์ž„ ๋งคํ•‘:** ์•„๋ž˜ ์ ˆ์ฐจ์˜ `/ralplan`ยท`/ralph`๋Š” Claude Code์—์„  OMC ์Šคํ‚ฌ, **Codex์—์„  oh-my-codex(OMX)์˜ ๋™๋ช… ์Šคํ‚ฌ**์ด๋‹ค(Codex๋Š” OMC ๋Œ€์‹  OMX ์ „์ œ). ๊ฐ ๋Ÿฐํƒ€์ž„์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ํ˜ธ์ถœํ•œ๋‹ค.
23
+
24
+ ## ์ ˆ์ฐจ (CLAUDE.md: ๊ณ„ํš ์—†๋Š” ์ฆ‰์‹œ์‹คํ–‰ ๊ธˆ์ง€)
25
+
26
+ 1. **์ŠคํŽ™ ์ •๋…**: ๋Œ€์ƒ ํŒŒ์ผ์„ ์ฝ์–ด ์š”๊ตฌ์‚ฌํ•ญ์„ ํ•ญ๋ชฉํ™”. ์ฝ”๋“œ๋ฒ ์ด์Šค ์ •์ฐฐ(๋ณ‘๋ ฌ explore/general-purpose ์—์ด์ „ํŠธ๋กœ
27
+ ๊ฐ ํ•ญ๋ชฉ์˜ ํ˜„์žฌ React ์ƒํƒœ โ†” ๋ ˆํผ๋Ÿฐ์Šค ๊ตฌํ˜„ ๊ฐญ์„ file:line ์ฆ๊ฑฐ๋กœ ๋งคํ•‘).
28
+ 2. **`/ralplan --deliberate`**: Planner(์ •์ฐฐ ์ข…ํ•ฉ)โ†’Architectโ†’Critic consensus(ITERATE ํ๋ฃจํ”„). Principles/Drivers/
29
+ Options(๊ธฐ๊ฐ ์‚ฌ์œ )/pre-mortem/ํ™•์žฅ ํ…Œ์ŠคํŠธ๊ณ„ํš(unitยทintegrationยทe2e์„ ํƒยทobservability)/ADR. ํ•ญ๋ชฉ์„ ์ถฉ๋Œ-์•ˆ์ „ ์ˆœ์„œ์˜
30
+ ์Šคํ…Œ์ด์ง€๋กœ ๋ถ„ํ•ด(ํŒŒ์ผ ๊ฒฝํ•ฉ ์‹œ ์ˆœ์ฐจ, ๋…๋ฆฝ ์‹œ ๋ณ‘๋ ฌ ๊ฐ€๋Šฅ). ๊ณ„ํš์„ `.omc/plans/`์— ์ €์žฅ.
31
+ 3. **`/ralph --critic=critic`**: ์ƒˆ feature ๋ธŒ๋žœ์น˜(ํ˜„ HEAD off). ์Šคํ…Œ์ด์ง€๋ณ„:
32
+ - executor(opus) ์œ„์ž„ ๊ตฌํ˜„ + ํ…Œ์ŠคํŠธ ์ž‘์„ฑ/์ˆ˜์ •.
33
+ - ๐Ÿ”ด **orchestrator fresh ์žฌ๊ฒ€์ฆ**(executor ์™„๋ฃŒ๋ณด๊ณ  ์‹ ๋ขฐ ๊ธˆ์ง€ โ€” ๊ณผ๊ฑฐ ์˜ค๋ณด๊ณ  ์‚ฌ๋ก€): typecheck + ๊ด€๋ จ/์ „์ฒด vitest.
34
+ - critic(opus) APPROVE๊นŒ์ง€ ITERATE.
35
+ - ๋กœ์ปฌ ์ปค๋ฐ‹ + `scripts/parity-gate.sh:TEST_FLOOR` ์‹ค์ธก ์ƒํ–ฅ(๊ฐ™์€ ์ปค๋ฐ‹). dead-code ์ œ๊ฑฐ ์‹œ floor ํ•˜ํ–ฅ๋„ ์ •๋‹น(๋ฌธ์„œํ™”).
36
+ - ์œ„ํ—˜ ์Šคํ…Œ์ด์ง€(envยทdepsยทbuildยทCSSํ† ํฐยท์Šคํ‚ค๋งˆ)๋Š” **์ „์ฒด parity-gate** ์‹คํ–‰.
37
+ 4. **ํ™€๋ฆฌ์Šคํ‹ฑ ๋ฆฌ๋ทฐ(Step7)**: critic(opus)์ด **์ŠคํŽ™ Nํ•ญ๋ชฉ ้€ๆก ์ถฉ์กฑ + ๋ ˆํผ๋Ÿฐ์Šค parity + ๊ต์ฐจ์ถฉ๋Œ 0 + ๋ถˆ๋ณ€ ๋ณด์กด**์„ ๊ฒ€์ฆ(์ถฉ์กฑํ‘œ).
38
+ 5. **ํ•„์ˆ˜ deslop(Step7.5)**: `ai-slop-cleaner` ๋ณ€๊ฒฝ ํŒŒ์ผ ๋ฒ”์œ„. dead-code/์ค‘๋ณต/๊ณผ์ถ”์ƒ/์žฅํ™ฉ์ฃผ์„๋งŒ(๋งฅ๋ฝ ์ฃผ์„ ๋ณด์กด). ๋ฌด๋ณ€๊ฒฝ์ด๋ฉด ๋ฌด๋ณ€๊ฒฝ.
39
+ 6. **regression(Step7.6)** + `/oh-my-claudecode:cancel`.
40
+
41
+ ## ๋ฃจํ”„/์ค‘๋‹จ (์‚ฌ์šฉ์ž directive)
42
+ - **/loop**: ๋ชจ๋“  ๋ฌธ์ œ ํ•ด๊ฒฐ๊นŒ์ง€ ์ง„ํ–‰. credit/llm/token ๋น„์šฉ ๋น„๊ณ ๋ ค.
43
+ - **์ค‘๋‹จ ์กฐ๊ฑด**: ์ปจํ…์ŠคํŠธ 85% ๋„๋‹ฌ OR ์ž‘์—… ์™„๋ฃŒ โ†’ ์ž‘์—…ยทloop ์ค‘๋‹จ(์ฒดํฌํฌ์ธํŠธ: prd.jsonยทprogress.txtยท๋ฉ”๋ชจ๋ฆฌ).
44
+
45
+ ## ๐Ÿ”ด ๋ถˆ๋ณ€ ์ œ์•ฝ
46
+ - ๋ณด์•ˆ๋ชจ๋ธ: ๋น„๋ฐ€ ์„œ๋ฒ„์‚ฌ์ด๋“œ ์ฃผ์ž…ยทVITE_ ๋น„๋ฐ€ ๊ธˆ์ง€ยท`client.ts` onResponse ์ธํ„ฐ์…‰ํ„ฐ ๊ธˆ์ง€(openapi-fetch body 1ํšŒ ์†Œ๋น„)ยท
47
+ `.env`๋Š” gitignore ์œ ์ง€(dist ์‹คํ† ํฐ 0).
48
+ - FSD ๋‹จ๋ฐฉํ–ฅ(appโ†’pagesโ†’widgetsโ†’featuresโ†’entitiesโ†’shared)ยทfeatureโ†”feature import 0.
49
+ - No-mock/ํ•˜๋“œ์ฝ”๋”ฉ ๊ธˆ์ง€ยทenv ๊ธฐ๋ฐ˜. **๋ฐฑ์—”๋“œ ๋ฌด์ˆ˜์ •**(ํ”„๋ก ํŠธ ์ „์šฉ; server/auth BFF๋Š” ํ”„๋ก ํŠธ ์ฝ”๋“œ๋ผ ๊ฐ€๋Šฅ).
50
+ - ๋น„๋ฐฐํฌ ๋กœ์ปฌ๋„๊ตฌ(๋ฐฐํฌยท์—”๋“œํฌ์ธํŠธ RBAC ์„œ๋ฒ„๊ฐ•์ œ = ๋น„๋ชฉํ‘œ). push/๋จธ์ง€ = **์‚ฌ์šฉ์ž ํ†ต์ œ**(์›๊ฒฉ origin์€ ์‚ฌ์šฉ์ž ์„ค์ •).
51
+ - ๋‹ต๋ณ€ยท๋ฌธ์„œ ํ•œ๊ธ€(๊ธฐ์ˆ  ํ† ํฐ ์˜๋ฌธ).
52
+
53
+ ## ์™„๋ฃŒ ๋ณด๊ณ 
54
+ - ์ŠคํŽ™ ํ•ญ๋ชฉ๋ณ„ ๊ฒฐ๊ณผ๋ฅผ **ํ‘œ**(ํ•ญ๋ชฉ/์ƒํƒœ/๊ทผ๊ฑฐ file:line)๋กœ ๊ฐ„๊ฒฐํžˆ.
55
+ - ํ…Œ์ŠคํŠธ ์ˆ˜ ๋ณ€ํ™”(๊ธฐ์ค€ floorโ†’์ตœ์ข…)ยท๊ฐ ์Šคํ…Œ์ด์ง€ critic ํŒ์ •ยท๊ฒŒ์ดํŠธ ๊ฒฐ๊ณผ.
56
+ - ๋ฏธ์ถฉ์กฑ/์ˆ˜์šฉ-๋ฒ”์œ„ํ•œ์ •/ํ›„์†๋ถ„๋ฆฌ ํ•ญ๋ชฉ ์ •์ง ๋ช…์‹œ.
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: setup
3
+ description: "๊ตฌ์„ฑ์š”์†Œ์™€ ์˜์กด์„ฑ์„ ์—ฌ๋Ÿฌ ๊ฐœ ๊ณจ๋ผ ์„ค์น˜ํ•˜๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ. ๋Ÿฐํƒ€์ž„(Node, Python, Java), LSP, tmux, MCP, ์ƒŒ๋“œ๋ฐ•์Šค, OMX, Playwright ๋“ฑ. ๊ถŒํ•œ ํ™•์ธ ๋„๊ธฐ๋Š” ๋”ฐ๋กœ ๋ฌป๊ณ  ๊ธฐ๋ณธ์œผ๋กœ ๊ณ ๋ฅด์ง€ ์•Š์œผ๋ฉฐ, ๊ณจ๋ผ๋„ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์ง์ ‘ ์ž…๋ ฅํ•  ๋ช…๋ น๋งŒ ์•ˆ๋‚ด."
4
+ ---
5
+
6
+ > Codex: ์ด ๋ช…๋ น์€ ์Šคํ‚ฌ๋กœ ์„ค์น˜๋๋‹ค. ๋ณธ๋ฌธ์˜ `$ARGUMENTS`, `$1` ์€ ์‚ฌ์šฉ์ž๊ฐ€ `$banker:setup` ๋’ค์— ์“ด ๊ธ€์ด๋‹ค.
7
+
8
+
9
+ # /banker:setup โ€” ๊ตฌ์„ฑ์š”์†Œ ์„ค์น˜ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ
10
+
11
+ banker ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ œ๊ณตํ•˜๋Š” ์„ค์น˜ ์Šคํ‚ฌ๋“ค์„ **multi-select**๋กœ ๊ณจ๋ผ ์‹คํ–‰ํ•œ๋‹ค.
12
+ ๊ธฐ์กด ์Šคํ‚ฌ๋“ค์˜ OS ์˜์กด์„ฑ(playwright ๋“ฑ)์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•จ. ๋‹ต๋ณ€์€ ํ•œ๊ธ€(๊ธฐ์ˆ  ํ† ํฐ ์˜๋ฌธ).
13
+
14
+ ## ์ ˆ์ฐจ
15
+
16
+ 0. **๋„คํŠธ์›Œํฌ ํ™•์ธ**: ํ•ญ๋ชฉ์„ ๊ณ ๋ฅด๊ธฐ ์ „์— ์›์ฒœ์— ๋‹ฟ๋Š”์ง€ ํ•œ ๋ฒˆ ๋ณธ๋‹ค(curl ์€ 10์ดˆ, npm ์€ 15์ดˆ ์•ˆ). ๊ฒฐ๊ณผ๋Š” ์•Œ๋ฆผ์œผ๋กœ๋งŒ ์“ฐ๊ณ , ์ด๊ฒƒ๋งŒ์œผ๋กœ ํ•ญ๋ชฉ์„ ๊ฑด๋„ˆ๋›ฐ์ง€ ์•Š๋Š”๋‹ค.
17
+ ```bash
18
+ [ "${CODEX_SANDBOX_NETWORK_DISABLED:-}" = 1 ] && echo "Codex sandbox ๊ฐ€ ๋„คํŠธ์›Œํฌ๋ฅผ ๋ง‰์Œ(์•„๋ž˜ ์‹คํŒจ๋Š” ์˜คํ”„๋ผ์ธ์ด ์•„๋‹˜)"
19
+ in15() { "$@" & p=$!; ( sleep 15 && kill "$p" ) > /dev/null 2>&1 & k=$!; wait "$p" 2>/dev/null; r=$?; kill "$k" 2>/dev/null; wait "$k" 2>/dev/null; [ "$r" = 143 ] && echo "15์ดˆ ์•ˆ์— ๋๋‚˜์ง€ ์•Š์•„ ๋ฉˆ์ถค(ํŒจํ‚ท์„ ๋ฒ„๋ฆฌ๋Š” ๋ง์ผ ์ˆ˜ ์žˆ์Œ)"; return "$r"; }
20
+ if command -v npm >/dev/null; then in15 sh -c 'cd ~ && exec npm ping --fetch-retries=0 --fetch-timeout=10000 --no-update-notifier'; else echo "npm ์—†์Œ(ํ™•์ธํ•˜์ง€ ๋ชปํ•จ)"; fi
21
+ curl --ssl-no-revoke -sSI -o /dev/null --connect-timeout 5 --max-time 10 https://github.com
22
+ ```
23
+ - `curl -sSI` ๋Š” ๋จธ๋ฆฌ๋งŒ ๋ฐ›๋Š”๋‹ค. ํ™•์ธ ์ค„์€ `--ssl-no-revoke` ๋ฅผ ์ค€๋‹ค. Windows ์˜ Git Bash curl ๋„ Schannel ์ด๋ผ, ํ์ง€ ๋ชฉ๋ก ์„œ๋ฒ„์— ๋‹ฟ์ง€ ๋ชปํ•˜๋Š” ๋ง์—์„œ ํ์ง€ ํ™•์ธ์ด ์‹คํŒจํ•˜๋ฉด ์ข…๋ฃŒ 60 ์ด๋‹ค(Windows ๋ฐ•์Šค ์‹ค์ธก). ๋‹ค๋ฅธ curl ์€ ์ด ๊นƒ๋ฐœ์„ ๋ฐ›๊ณ  ๋ฌด์‹œํ•œ๋‹ค(macOS curl 8.7.1, Rocky 8.10 curl 7.61.1 ์‹ค์ธก).
24
+ - ๋‘˜ ๋‹ค ์‹คํŒจํ•˜๋ฉด "๋„คํŠธ์›Œํฌ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ง‰ํžŒ ๊ฒƒ์œผ๋กœ ๋ณด์ž„. ํ•ญ๋ชฉ๋งˆ๋‹ค ์ž๊ธฐ ์›์ฒœ์„ ๋‹ค์‹œ ํ™•์ธํ•˜๊ณ , ๋‹ฟ์ง€ ์•Š๋Š” ์›์ฒœ์—์„œ ๋ฐ›๋Š” ๋‹จ๊ณ„๋Š” ํ•˜์ง€ ์•Š์Œ" ์œผ๋กœ ๋จผ์ € ์•Œ๋ฆฐ๋‹ค.
25
+ - ํ•ญ๋ชฉ์„ ๊ฑด๋„ˆ๋›ธ์ง€๋Š” ๊ฐ ์Šคํ‚ฌ์˜ 0๋‹จ๊ณ„๊ฐ€ ๊ทธ ์Šคํ‚ฌ์˜ ์›์ฒœ์œผ๋กœ ์ •ํ•œ๋‹ค. ์‚ฌ๋‚ด ๋ฏธ๋Ÿฌ๋งŒ ์—ด๋ฆฐ ๋ง(`npm ping` ์— ๋‹ตํ•˜์ง€ ์•Š๋Š” npm ๋ฏธ๋Ÿฌ, PyPI ๋ฏธ๋Ÿฌ)์—์„œ๋„ ๊ทธ ์›์ฒœ์„ ์“ฐ๋Š” ํ•ญ๋ชฉ์€ ์ง„ํ–‰๋œ๋‹ค. ๋‹ฟ์ง€ ์•Š๋Š” ์›์ฒœ์—์„œ ๋ฐ›๋Š” ๋‹จ๊ณ„๋งŒ "์˜คํ”„๋ผ์ธ์ด๋ผ ์„ค์น˜ํ•˜์ง€ ์•Š์Œ(ํ•„์š”ํ•œ ๊ฒƒ: <ํ•ญ๋ชฉ>)" ์œผ๋กœ ๋ณด๊ณ ๋œ๋‹ค.
26
+ - `npm` ์ด ์•„์ง ์—†์œผ๋ฉด(์ƒˆ PC) npm ์ชฝ์€ ์˜คํ”„๋ผ์ธ์ด ์•„๋‹ˆ๋ผ ํ™•์ธํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ์ด๋‹ค. `setup-node` ๊ฐ€ npm ์„ ๊น ๋’ค ๊ฐ ์Šคํ‚ฌ์˜ 0๋‹จ๊ณ„๊ฐ€ ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค.
27
+ - `npm ping` ์€ ํ™ˆ ํด๋”์—์„œ ๋ถ€๋ฅธ๋‹ค. ์ง€๊ธˆ ํด๋”์— ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์˜ `.npmrc` ๊ฐ€ ์ •ํ•œ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์™€ ํ† ํฐ์„ ๋”ฐ๋ฅด์ง€ ์•Š๊ฒŒ ํ•˜๋ ค๋Š” ๊ฒƒ์ด๋‹ค.
28
+ - `npm ping` ์€ 15์ดˆ ๊ฐ์‹œ(`in15`) ์•ˆ์—์„œ ๋ถ€๋ฅธ๋‹ค. ํŒจํ‚ท์„ ๋ฒ„๋ฆฌ๋Š” ๋ง์—์„œ npm ์€ ์—ฐ๊ฒฐ์— ์‹œ๊ฐ„ ์ œํ•œ์„ ๋‘์ง€ ์•Š์•„(`--fetch-timeout` ์€ ๋ฐ›๋Š” ์ค‘์˜ ์‰ผ๋งŒ ์žฐ๋‹ค) OS ์˜ ์—ฐ๊ฒฐ ์žฌ์‹œ๋„๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค(Rocky 8.10 ์‹ค์ธก 130์ดˆ). 15์ดˆ ์•ˆ์— ๋๋‚˜์ง€ ์•Š์œผ๋ฉด `15์ดˆ ์•ˆ์— ๋๋‚˜์ง€ ์•Š์•„ ๋ฉˆ์ถค(ํŒจํ‚ท์„ ๋ฒ„๋ฆฌ๋Š” ๋ง์ผ ์ˆ˜ ์žˆ์Œ)` ๊ณผ ์ข…๋ฃŒ 143 ์ด๋‹ค. `--no-update-notifier` ๋Š” npm ์ด ์ œ ํŒ์„ ๋ฌป๋Š” ์š”์ฒญ์„ ํ•˜๋‚˜ ๋” ๋‚ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.
29
+ - OS ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž(dnf, apt, brew, winget)๋งŒ ์“ฐ๋Š” ๋‹จ๊ณ„๋Š” ์ž๊ธฐ ์„ค์ •์˜ ๋ฏธ๋Ÿฌ๋ฅผ ์“ฐ๋ฏ€๋กœ ์ด ํ™•์ธ๊ณผ ๊ด€๊ณ„์—†์ด ๊ฐ ์Šคํ‚ฌ์˜ ๊ทœ์น™๋Œ€๋กœ ์‹œ๋„ํ•œ๋‹ค.
30
+ - ํŒจํ‚ท์„ ๋ฒ„๋ฆฌ๊ฑฐ๋‚˜ ๋‹ตํ•˜์ง€ ์•Š๋Š” ๋ง์—์„œ dnf ๋Š” ์ €์žฅ์†Œ๋งˆ๋‹ค ์•ฝ 2๋ถ„(SYN ์„ ๋ฒ„๋ฆฌ๋Š” ๋ง ์‹ค์ธก 121์ดˆ), apt ๋Š” ์›์ฒœ๋งˆ๋‹ค ์•ฝ 4๋ถ„(๋‹ตํ•˜์ง€ ์•Š๋Š” ์›์ฒœ ์‹ค์ธก 247์ดˆ) ๊ธฐ๋‹ค๋ฆฐ ๋’ค ์‹คํŒจ๋กœ ๋๋‚œ๋‹ค. ์‹œ๊ฐ„ ์ œํ•œ์€ ๊ด€๋ฆฌ์ž ์„ค์ •์„ ๋”ฐ๋ฅด๋ฏ€๋กœ ์ด ์Šคํ‚ฌ์ด ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.
31
+ - `apt-get update` ๋Š” ๋ฐ›์ง€ ๋ชปํ•ด๋„ ์ข…๋ฃŒ 0 ์ด๋‹ค. ์ถœ๋ ฅ์˜ `W: Failed to fetch` ๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ๋‹จ๊ณ„๋ฅผ ์‹คํŒจ๋กœ ๋ณด๊ณ ํ•œ๋‹ค.
32
+ - ๋‚ด๋ ค๋ฐ›๊ธฐ๊ฐ€ ์—†๋Š” ํ•ญ๋ชฉ(`setup-omx-hud`)๊ณผ ์ด๋ฏธ ์„ค์น˜๋œ ๊ฒƒ์˜ ํ™•์ธ์€ ๊ทธ๋Œ€๋กœ ์ง„ํ–‰ํ•œ๋‹ค.
33
+ - Windows PowerShell ์—์„œ๋Š” `curl.exe --ssl-no-revoke -sSI -o NUL --connect-timeout 5 --max-time 10 https://github.com` ์˜ `$LASTEXITCODE` ๋กœ ๋ณธ๋‹ค. `curl.exe` ๋Š” Windows ์‹œ์Šคํ…œ ํ”„๋ก์‹œ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๊ณ  `HTTPS_PROXY` ๋งŒ ๋”ฐ๋ฅธ๋‹ค. `--ssl-no-revoke` ๋Š” ํ์ง€ ๋ชฉ๋ก ์„œ๋ฒ„์— ๋‹ฟ์ง€ ๋ชปํ•˜๋Š” ๋ง์—์„œ Schannel ์˜ ํ์ง€ ํ™•์ธ ์‹คํŒจ๋ฅผ ์˜คํ”„๋ผ์ธ์œผ๋กœ ์ฝ์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค. ๋‹ฟ๋Š”์ง€๋งŒ ๋ณด๊ณ  ๋ฐ›์€ ๋‚ด์šฉ์€ ์“ฐ์ง€ ์•Š์œผ๋ฏ€๋กœ ์ด ํ™•์ธ์—๋งŒ ์ค€๋‹ค. `curl.exe` ๊ฐ€ ์—†์œผ๋ฉด `$LASTEXITCODE` ์— ์•ž ๋ช…๋ น์˜ ๊ฐ’์ด ๋‚จ์œผ๋ฏ€๋กœ `Get-Command curl.exe -ErrorAction SilentlyContinue` ๋กœ ๋จผ์ € ๋ณด๊ณ , ์—†์œผ๋ฉด ํ™•์ธํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ์œผ๋กœ ์ ๋Š”๋‹ค. `npm ping` ์€ `Push-Location ~; npm ping --fetch-retries=0 --fetch-timeout=10000 --no-update-notifier; Pop-Location` ์ฒ˜๋Ÿผ ํ™ˆ์—์„œ ๋ถ€๋ฅด๊ณ  ๋Œ์•„์˜จ๋‹ค(๋Œ์•„์˜ค์ง€ ์•Š์œผ๋ฉด ๋’ค ๋‹จ๊ณ„๊ฐ€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ํ™ˆ์—์„œ ๋ˆ๋‹ค). PowerShell ์—๋Š” 15์ดˆ ๊ฐ์‹œ๋ฅผ ๋‘์ง€ ์•Š๋Š”๋‹ค(Windows ์˜ ์—ฐ๊ฒฐ ์‹œ๊ฐ„ ์ œํ•œ์€ ์•ฝ 21์ดˆ๋กœ ์•Œ๋ ค์ ธ ์žˆ๋‹ค. ๋ฏธ์‹ค์ธก).
34
+ - Codex sandbox ๊ฐ€ ๋„คํŠธ์›Œํฌ๋ฅผ ๋ง‰๋Š” ๋ชจ๋“œ(๊ธฐ๋ณธ `workspace-write`, `read-only`)์—์„œ๋Š” `CODEX_SANDBOX_NETWORK_DISABLED=1` ์ด ์„œ๊ณ  ํ™•์ธ์ด ๋ชจ๋‘ ์‹คํŒจํ•œ๋‹ค(curl ์ข…๋ฃŒ 7, npm `EPERM`). ๋ธ”๋ก์˜ ์ฒซ ์ค„์ด ๊ทธ๋ ‡๋‹ค๊ณ  ์•Œ๋ฆฐ๋‹ค. ๊ทธ๋•Œ๋Š” '์˜คํ”„๋ผ์ธ' ์ด ์•„๋‹ˆ๋ผ 'sandbox ๊ฐ€ ๋„คํŠธ์›Œํฌ๋ฅผ ๋ง‰์Œ' ์œผ๋กœ ๋ณด๊ณ ํ•˜๊ณ , ์Šน์ธ์„ ๋ฐ›์•„ sandbox ๋ฐ–์—์„œ ํ™•์ธ๋ถ€ํ„ฐ ๋‹ค์‹œ ํ•œ๋‹ค. Claude Code sandbox ์•ˆ(`SANDBOX_RUNTIME=1`)์—์„œ๋Š” ํ—ˆ์šฉ ๋ชฉ๋ก ๋ฐ– ์ฃผ์†Œ๋ฅผ sandbox ํ”„๋ก์‹œ๊ฐ€ ๋ง‰๋Š”๋‹ค. Windows PowerShell ์—์„œ๋Š” `$env:CODEX_SANDBOX_NETWORK_DISABLED -eq '1'` ๋กœ ๋ณธ๋‹ค.
35
+ - ํ™•์ธ์ด ์‹คํŒจํ•˜๋ฉด ๋ณด๊ณ ์— ๊ทธ ๋ช…๋ น, ์ข…๋ฃŒ ์ฝ”๋“œ, stderr ์ฒซ ์ค„์„ ๋ถ™์ธ๋‹ค(์˜ˆ: `npm error code ETIMEDOUT`, `curl: (28) ...`). ์›์ธ์„ '์˜คํ”„๋ผ์ธ' ํ•˜๋‚˜๋กœ ์ง์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
36
+ - curl ์˜ ์ข…๋ฃŒ 60(์ธ์ฆ์„œ ๊ฒ€์ฆ ์‹คํŒจ)๋„ ์˜คํ”„๋ผ์ธ์ด ์•„๋‹ˆ๋‹ค. ๋ง์€ ์—ด๋ ค ์žˆ๊ณ , TLS ๋ฅผ ๊ฐ€๋กœ์ฑ„๋Š” ์‚ฌ๋‚ด ํ”„๋ก์‹œ์˜ ์ธ์ฆ์„œ๋ฅผ OS ๊ฐ€ ๋ฏฟ์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค. '์ธ์ฆ์„œ ๋ฌธ์ œ๋กœ ์„ค์น˜ํ•˜์ง€ ์•Š์Œ' ์œผ๋กœ ๋ณด๊ณ ํ•œ๋‹ค.
37
+ - curl ์€ ๋‹ฟ๋Š”๋ฐ npm ๋งŒ ์ธ์ฆ์„œ ์˜ค๋ฅ˜(`UNABLE_TO_VERIFY_LEAF_SIGNATURE`, `SELF_SIGNED_CERT_IN_CHAIN`, `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`)๋กœ ์‹คํŒจํ•˜๋ฉด ๋ง์€ ์—ด๋ ค ์žˆ๊ณ  npm ์ด ์‚ฌ๋‚ด ์ธ์ฆ์„œ๋ฅผ ๋ฏฟ์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค(npm ์€ OS ์ธ์ฆ์„œ ์ €์žฅ์†Œ๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค). '์ธ์ฆ์„œ ๋ฌธ์ œ๋กœ ์„ค์น˜ํ•˜์ง€ ์•Š์Œ' ์œผ๋กœ ๋ณด๊ณ ํ•˜๊ณ  `NODE_EXTRA_CA_CERTS=<์‚ฌ๋‚ด CA ํŒŒ์ผ>` ์„ ์•ˆ๋‚ดํ•œ๋‹ค. Node 24 ๋Š” `NODE_OPTIONS=--use-system-ca` ๋„ ๋œ๋‹ค.
38
+ - ์ด ์‹คํ–‰์—์„œ ๋ถ€๋ฅด๋Š” ์Šคํ‚ฌ์˜ 0๋‹จ๊ณ„๋Š” /banker:setup 0๋‹จ๊ณ„์™€ ์•ž ์Šคํ‚ฌ์ด ์ด๋ฏธ ๋Œ๋ฆฐ ๊ฐ™์€ ํ™•์ธ ์ค„(๊ฐ™์€ ๋ช…๋ น๊ณผ ๊ฐ™์€ ์ฃผ์†Œ)์˜ ๊ฒฐ๊ณผ๋ฅผ ์“ฐ๊ณ  ๋‹ค์‹œ ๋Œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ์ฒ˜์Œ ๋ณด๋Š” ์›์ฒœ๋งŒ ์ƒˆ๋กœ ๋ณธ๋‹ค. ๊ฒฐ๊ณผ๋Š” ์ด ์‹คํ–‰ ์•ˆ์—์„œ๋งŒ ์“ด๋‹ค(๊ทธ ์‚ฌ์ด ๋ง์ด ๋ฐ”๋€Œ๋ฉด ์˜› ๊ฒฐ๊ณผ์ผ ์ˆ˜ ์žˆ๋‹ค). ๋ง‰ํžŒ ๋ง์—์„œ ํ•ญ๋ชฉ๋งˆ๋‹ค ๊ฐ™์€ ํ™•์ธ์„ ๋‹ค์‹œ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ฒŒ ํ•˜๋ ค๋Š” ๊ฒƒ์ด๋‹ค.
39
+ 1. **์„ ํƒ ๋ฐ›๊ธฐ**: `$ARGUMENTS` ์— ์ปดํฌ๋„ŒํŠธ๋ช…์ด ๋ช…์‹œ๋ผ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ๋งŒ ์‹คํ–‰(์งˆ๋ฌธ ์ƒ๋žต).
40
+ ๋น„์–ด ์žˆ์œผ๋ฉด ์•„๋ž˜๋ฅผ `AskUserQuestion`(**multiSelect: true**)์œผ๋กœ ์ œ์‹œํ•œ๋‹ค(**Codex ๋Ÿฐํƒ€์ž„์—” AskUserQuestion ์ด ์—†์œผ๋ฏ€๋กœ ๋ชฉ๋ก์„ ์ œ์‹œํ•˜๊ณ  ์„ค์น˜ํ•  ํ•ญ๋ชฉ์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฌผ์–ด ๋ฐ›๋Š”๋‹ค**). ํ•ญ๋ชฉ์€ **๊ณ„์ธต/์˜์กด ์ˆœ์„œ**๋กœ ๋ฌถ์—ฌ ์žˆ์œผ๋‹ˆ, ๋’ค ๊ณ„์ธต์„ ๊ณ ๋ฅด๋ฉด ์•ž ๊ณ„์ธต ์ „์ œ๋ฅผ ๋จผ์ € ์ฑ„์šด๋‹ค:
41
+ - ์งˆ๋ฌธ ํ•˜๋‚˜์— ์„ ํƒ์ง€๋Š” 4๊ฐœ๊นŒ์ง€๋‹ค. 5๊ฐœ ์ด์ƒ์ธ ๋ฌถ์Œ์€ ๋‘ ์งˆ๋ฌธ์œผ๋กœ ๋‚˜๋ˆˆ๋‹ค.
42
+ - ๋งจ ์•„๋ž˜ **๊ถŒํ•œ** ํ•ญ๋ชฉ์€ ์ด ๋‹ค์ค‘ ์„ ํƒ์— ๋„ฃ์ง€ ์•Š๊ณ , ๊ทธ ํ•ญ๋ชฉ์˜ ๊ทœ์น™๋Œ€๋กœ ๋”ฐ๋กœ ๋ฌป๋Š”๋‹ค.
43
+
44
+ **๋Ÿฐํƒ€์ž„ (๊ธฐ๋ฐ˜ โ€” ๋จผ์ €)**
45
+ - **setup-node** โ€” nvm + Node 22(winget/nvm-windows). npx ๊ธฐ๋ฐ˜ MCPยทCLI ์ „์ œ.
46
+ - **setup-python** โ€” Python 3.11 + pipx + uv(PEP668). `setup-lsp`ยท`setup-mcp`ยท`docs-setup` ์ „์ œ.
47
+ - **setup-java** โ€” JDK 21 + JAVA_HOME(Debian=Adoptium). `setup-lsp` ์˜ jdtls ์š”๊ตฌ.
48
+
49
+ **๊ฐœ๋ฐœ๋„๊ตฌ**
50
+ - **setup-lsp** โ€” ์–ธ์–ด๋ณ„ LSP(vtslsยทbasedpyrightยทbashยทjdtlsยทspring) + lsp-mcp ๋ธŒ๋ฆฌ์ง€. (์˜์กด: nodeยทpythonยทjava)
51
+ - **setup-tmux** โ€” tmux(Rocky8 3.6a ์†Œ์Šค๋นŒ๋“œ)/psmux(Windows). OMC teamยทOMX `$team`/HUD ์ „์ œ.
52
+ - **setup-pwsh** โ€” Windows PowerShell 7 ํ™˜๊ฒฝ($PROFILE UTF-8ยทTerminalยทGit). ๋น„-Windows๋Š” no-op.
53
+ - **setup-mcp** โ€” ๊ณตํ†ต MCP ์„œ๋ฒ„(context7ยทseq-thinkingยทfilesystemยทgitยทfetch). (์˜์กด: nodeยทpython)
54
+ - **setup-sandbox** โ€” OS๋ณ„ ์ƒŒ๋“œ๋ฐ•์Šค(bubblewrap+userns / AppContainer / Seatbelt) + rust + git safe.directory.
55
+
56
+ **ํ”„๋ ˆ์ž„์›Œํฌยทํ”Œ๋Ÿฌ๊ทธ์ธ**
57
+ - **setup-omx** โ€” oh-my-codex(OMX) ์„ค์น˜/๊ฐฑ์‹ . `all-in-one`ยท`ultra-chain`ยท`ultra-init`ยท`/banker:front-qa` ์˜์กด์„ฑ.
58
+ - **setup-harness-factory** โ€” revfactory/harness(ํŒ€ ์•„ํ‚คํ…์ฒ˜ ํŒฉํ† ๋ฆฌ) ์„ค์น˜+๊ตฌ์„ฑ+์•ˆ๋‚ด(Codex=meta-harness).
59
+ - **setup-insane-search** โ€” insane-search ํ”Œ๋Ÿฌ๊ทธ์ธ(์ฐจ๋‹จ ์‚ฌ์ดํŠธ ์šฐํšŒ, Claude/Codex ์–‘์ชฝ).
60
+ - **setup-omx-hud** โ€” Codex TUI ์ƒํƒœํ‘œ์‹œ์ค„(`[tui] status_line`)์„ OMX ํ”„๋ฆฌ์…‹์œผ๋กœ. ๊ฐ™์€ ๋จธ์‹ ์˜ Claude Code omc_hud ๋ž˜ํผ(v4)๋ฅผ ๊ณ ๋ฅด๋ฉด ์Šคํ‚ฌ์— ๋“  ํŒŒ์ผ๋กœ ์˜คํ”„๋ผ์ธ ์„ค์น˜.
61
+ - **setup-stitch** โ€” Stitch(๋””์ž์ธ ์ƒ์„ฑ) MCP ํ”„๋ก์‹œ ๋“ฑ๋ก(RockyLinux8 proxy-script, API key ํ•„์š”).
62
+
63
+ **์˜์กด์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ**
64
+ - **setup-playwright** โ€” Playwright + ๋ธŒ๋ผ์šฐ์ € + Xvfb(RHEL8/Rocky8 ํด๋ฐฑ). (`ultra-ui-qa`ยท`audit-web-page`ยท`play-qa`ยท`ultra-chain` ์˜์กด์„ฑ)
65
+ - **docs-setup** โ€” arch-diagramยทpdf-vision-extract ์˜์กด์„ฑ(python-pptxยทpymupdfยทplantuml). (์˜์กด: python)
66
+ - **motion-graphic-setup** โ€” hyperframes(๋ฌด๋ฃŒ ๋ชจ์…˜ ๊ทธ๋ž˜ํ”ฝ CLI) ์ „์ œ์กฐ๊ฑด(Nodeโ‰ฅ22 + ffmpeg โ†’ `npx hyperframes`). (`motion-graphic-make` ์˜์กด์„ฑ)
67
+ - **3d-intro-setup** โ€” 3D ์ธํŠธ๋กœ์šฉ ํฌ๋ ˆ๋ด์…œ(Azure gpt-imageยทSora-2, ์„ ํƒ WAN ์˜์ƒ ํ‚ค ํ’€)ยท์˜์กด์„ฑ(Node/ffmpeg) + ๋ฌด๊ณผ๊ธˆ ํ”„๋ฆฌํ”Œ๋ผ์ดํŠธ. (`3d-intro-build` ์˜์กด์„ฑ)
68
+
69
+ **๊ถŒํ•œ (์ฃผ์˜, ๊ธฐ๋ณธ ๋ฏธ์„ ํƒ)**
70
+ - **setup-bypass-permissions** โ€” Claude Code ์˜ ๊ธฐ๋ณธ ๊ถŒํ•œ ๋ชจ๋“œ๋ฅผ `bypassPermissions` ๋กœ ๋ฐ”๊ฟ” ๋ชจ๋“  ์„ธ์…˜์—์„œ ๋„๊ตฌ ์‹คํ–‰ ํ™•์ธ์„ ๋”.
71
+ - ์œ„ ๋ฌถ์Œ๊ณผ ์„ž์ง€ ์•Š๊ณ  ๋‹จ์ผ ์„ ํƒ ์งˆ๋ฌธ ํ•˜๋‚˜๋กœ ๋”ฐ๋กœ ๋ฌป๋Š”๋‹ค(`multiSelect: false`). ์„ ํƒ์ง€๋Š” `๋„ฃ์ง€ ์•Š์Œ (๊ถŒ์žฅ)`, `๋„ฃ๊ธฐ (์œ„ํ—˜)` ์ˆœ์„œ๋‹ค.
72
+ - ์‚ฌ์šฉ์ž๊ฐ€ ์ด ํ•ญ๋ชฉ์„ ์ง์ ‘ ๊ณ ๋ฅผ ๋•Œ๋งŒ ๋„ฃ๋Š”๋‹ค. "์ „๋ถ€" ๊ฐ™์€ ๋ฌถ์Œ ์„ ํƒ์ด๋‚˜ ๋ฒˆํ˜ธ ๋ฒ”์œ„์—๋Š” ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค.
73
+ - ์ด ์Šคํ‚ฌ์€ ๋ชจ๋ธ์ด ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค(`disable-model-invocation`). `๋„ฃ๊ธฐ` ๋ฅผ ๊ณ ๋ฅด๋ฉด ์‹คํ–‰ํ•˜์ง€ ๋ง๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ `/banker:setup-bypass-permissions on` ์„ ์ง์ ‘ ์ž…๋ ฅํ•˜๋ผ๊ณ  ์•ˆ๋‚ดํ•œ๋‹ค. Codex ๋Š” `$banker-setup-bypass-permissions`(ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜๋ฉด `$banker:setup-bypass-permissions`)๋ฅผ ์ง์ ‘ ๋ถ€๋ฅด๋ผ๊ณ  ์•ˆ๋‚ดํ•œ๋‹ค.
74
+ - ์ž…๋ ฅํ•˜๋ฉด ๊ทธ ์Šคํ‚ฌ์ด ์œ„ํ—˜ ๊ฒฝ๊ณ ๋ฅผ ๋ณด์—ฌ ์ฃผ๊ณ  ํ•œ ๋ฒˆ ๋” ํ™•์ธ๋ฐ›๋Š”๋‹ค. ๋˜๋Œ๋ฆฌ๊ธฐ๋Š” `off`.
75
+
76
+ 2. **์‹คํ–‰**: ์„ ํƒ๋œ ๊ฐ ํ•ญ๋ชฉ์„ ํ˜ธ์ถœํ•œ๋‹ค(๋ฏธ์„ ํƒ์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Œ). ๋‹จ `setup-bypass-permissions` ๋Š” ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค(์œ„ ๊ถŒํ•œ ํ•ญ๋ชฉ์˜ ์•ˆ๋‚ด๋งŒ). Claude Code๋Š” `Skill("banker:<name>")`, **Codex ๋Ÿฐํƒ€์ž„์—์„  ์„ค์น˜๋œ `banker-<name>` ์Šคํ‚ฌ(ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜๋ฉด `banker:<name>`)์„ ์ ์šฉ**ํ•œ๋‹ค(์˜ˆ: `Skill("banker:setup-node")`).
77
+ - **์˜์กด ์ˆœ์„œ ๊ฐ•์ œ**: ๋’ค ๊ณ„์ธต์„ ๊ณ ๋ฅด๋ฉด ์ „์ œ ์Šคํ‚ฌ์„ ๋จผ์ € ์‹คํ–‰ํ•œ๋‹ค โ€” `setup-lsp` ์ „์— setup-nodeยทsetup-pythonยทsetup-java; `setup-mcp` ์ „์— setup-nodeยทsetup-python; `docs-setup` ์ „์— setup-python. ์ด๋ฏธ ์„ค์น˜๋ผ ์žˆ์œผ๋ฉด ๊ฐ ์Šคํ‚ฌ์ด ๊ฐ์ง€ํ•ด skip(๋ฉฑ๋“ฑ).
78
+ - ๊ฐ ์Šคํ‚ฌ์ด OS๋ฅผ ๊ฐ์ง€ํ•ด ์•Œ๋งž์€ ์ ˆ์ฐจ๋กœ ์„ค์น˜ํ•˜๊ณ  ๋์— ๊ฒ€์ฆํ•œ๋‹ค. ํ•œ ํ•ญ๋ชฉ์ด ์‹คํŒจํ•ด๋„ ์ •์งํžˆ ๋ณด๊ณ ํ•˜๊ณ  ๋‹ค์Œ ํ•ญ๋ชฉ ์ง„ํ–‰.
79
+
80
+ 3. **๋งˆ๋ฌด๋ฆฌ ๋ณด๊ณ (ํ•œ๊ธ€)**: ์„ค์น˜๋œ ํ•ญ๋ชฉยท๊ฒ€์ฆ ๊ฒฐ๊ณผยท**์žฌ์‹œ์ž‘/reload ํ•„์š” ์—ฌ๋ถ€**.
81
+ - ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜(insane-search)๋Š” `/reload-plugins`(Claude) ๋˜๋Š” ์žฌ์‹œ์ž‘ ํ›„ ๋ฐ˜์˜(Codex๋Š” ์žฌ์‹œ์ž‘).
82
+ - MCP ์ถ”๊ฐ€(stitch)๋Š” ์„ธ์…˜ ๊ฐฑ์‹  ํ›„ ์—ฐ๊ฒฐ ํ™•์ธ โ€” Claude `claude mcp list`, Codex `codex mcp list`.
83
+
84
+ ## ์›์น™
85
+ - ๊ฐ ์„ค์น˜๋Š” **๋น„ํŒŒ๊ดดยท๋ฉฑ๋“ฑ**(์ด๋ฏธ ์„ค์น˜๋ฉด skip).
86
+ - ์‹œํฌ๋ฆฟ(API key ๋“ฑ)์€ ์‚ฌ์šฉ์ž ์ž…๋ ฅ/ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ๋งŒ โ€” ์ถœ๋ ฅยท์ปค๋ฐ‹ ๊ธˆ์ง€.
87
+ - root ๋ถˆ๊ฐ€/conda ๋ถ€์žฌ ๋“ฑ์€ ๊ฐ ์Šคํ‚ฌ์˜ ํด๋ฐฑ ๊ฒฝ๋กœ๋กœ ์ฒ˜๋ฆฌ(์ถ”์ • ๊ธˆ์ง€, ๊ฐ์ง€ ๊ธฐ๋ฐ˜).
@@ -0,0 +1,29 @@
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.",
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": ": banker-hooks-rev 7fad90560d65; 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 7fad90560d65'; 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
+ "timeout": 5
12
+ }
13
+ ]
14
+ }
15
+ ],
16
+ "Stop": [
17
+ {
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": ": banker-hooks-rev 7fad90560d65; 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 7fad90560d65'; 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
+ }
29
+ }