์๋ฆฌ์ฆ: oh-my-codex ์ํคํ ์ฒ ํด๋ถ
์ด ์๋ฆฌ์ฆ๋ OpenAI Codex CLI ํ์ฅ ๋ฐํ์์ธ oh-my-codex(OMX)์ ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ๋จ๊ณ๋ณ๋ก ํด๋ถํ๋ ๊ณผ์ ์ด๋ค.
| ํธ | ๋ด์ฉ | ํต์ฌ |
|---|---|---|
| 0ํธ | Overview | 3-Plane ์ํคํ ์ฒ, OMC์์ ์ฐจ์ด, ์ ์ฒด ํ๋ฆ |
| 1ํธ | Codex CLI Foundation | Codex CLI ์์ฒด์ ๊ตฌ์กฐ์ ํ์ฅ ํฌ์ธํธ |
| 2ํธ | OMX Integration | OMX๊ฐ Codex์ ์ด๋ป๊ฒ ์ฐ๊ฒฐ๋๋ |
| 3ํธ | Skill System | ์ํฌํ๋ก์ฐ๋ฅผ ์ด๋ป๊ฒ ์ ์ํ๋ |
| 4ํธ | Prompt & Agent System | ์์ด์ ํธ๋ ๋ญ๊ณ ์ด๋ป๊ฒ ์ ํ๋๋ |
| 5ํธ | MCP Servers | ์ด๋ค MCP ๋๊ตฌ๋ฅผ ์ธ ์ ์๋ |
| 6ํธ | State & Lifecycle | ์ํ๋ฅผ ์ด๋ป๊ฒ ์ ์งํ๋ |
| 7ํธ | Team Orchestration | Team ๋ชจ๋๋ ์ด๋ป๊ฒ ๋์ํ๋ |
| 8ํธ (๋ณธ๋ฌธ) | Native & Spark | Rust ๋ค์ดํฐ๋ธ ๋๊ตฌ๋ ๋ญ๊ฐ |
- Native & Spark๋ Rust๋ก ๊ตฌํ๋ ์ฝ๊ธฐ ์ ์ฉ ํ์ ์์ง(explore-harness)๊ณผ ์ ์์ ์ถ๋ ฅ ์์ฝ ์ฌ์ด๋์นด(sparkshell)
- ์ ธ ๋ช ๋ น์ด allowlist + ๊ฒฝ๋ก ํ์ถ ์ฐจ๋จ + read-only ์๋๋ฐ์ค๋ก ์์ด์ ํธ์ ์ฝ๋๋ฒ ์ด์ค ํ์์ ์์ ํ๊ฒ ์ ํํ๋ฉฐ, 12์ค ์ด๊ณผ ์ถ๋ ฅ์ LLM ๊ธฐ๋ฐ์ผ๋ก ์์ฝํ์ฌ ์ปจํ ์คํธ ์์ฐ์ ๋ณดํธํ๋ ๋ค์ดํฐ๋ธ ๋น ๋ฅธ ๊ฒฝ๋ก(native fast path)
- OMC์๋ ์๋ OMX๋ง์ ๊ณ ์ ๊ธฐ๋ฅ์ผ๋ก, Cargo ์ํฌ์คํ์ด์ค ๋น๋ + ๋ฉํฐ ํ๋ซํผ ๋ฐฐํฌ + SHA-256 ๊ฒ์ฆ + ์๋ hydration ์บ์ฑ์ ๊ฐ์ถ Rust-TypeScript ํ์ด๋ธ๋ฆฌ๋ ์ํคํ ์ฒ
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- 7ํธ๊น์ง์ ์์ด์ ํธ๋ ์ฝ๋๋ฒ ์ด์ค ํ์ ์ Codex CLI์ ๊ธฐ๋ณธ ์ ธ ์คํ์ ์์กด โ ์ค์๋ก ์ฐ๊ธฐ ์์ ๊ฐ๋ฅ, ํ์ดํ ์กฐํฉ์ผ๋ก ์์ธก ๋ถ๊ฐํ ๋ช ๋ น ์คํ ๊ฐ๋ฅ
- ๊ธด ์
ธ ์ถ๋ ฅ(์:
rg๊ฒฐ๊ณผ ์์ฒ ์ค)์ด ๊ทธ๋๋ก ์ปจํ ์คํธ์ ๋ค์ด๊ฐ๋ฉด ํ ํฐ ์์ฐ ๋ญ๋น + LLM ์ฃผ์๋ ฅ ๋ถ์ฐ - explore-harness๋ ์์ ํ ์ฝ๊ธฐ ์ ์ฉ ํ์, sparkshell์ ์ถ๋ ฅ ์์ฝ์ผ๋ก ์ปจํ ์คํธ ์ ์ฝ โ ๋ ๋๊ตฌ๊ฐ ์ด ๊ฐ๊ทน์ ๋ฉ์
AS-IS (Codex CLI ๊ธฐ๋ณธ ์ ธ โ ๋ฌด์ ํ ์คํ)
sequenceDiagram autonumber participant CX as Codex CLI (์์ด์ ํธ) participant SH as Shell participant FS as ํ์ผ ์์คํ CX->>SH: rg "auth" src/ | head -50 Note over SH: ํ์ดํ, ๋ฆฌ๋ค์ด๋ ์ ํ์ฉ SH->>FS: ์ฝ๊ธฐ + ์ ์ฌ์ ์ฐ๊ธฐ SH-->>CX: ์์ฒ ์ค ์์ ์ถ๋ ฅ Note over CX: ์ ์ฒด ์ถ๋ ฅ์ด ์ปจํ ์คํธ์ ์ฃผ์ Note over CX: ํ ํฐ ์์ฐ ๋ญ๋น Note over CX: ์ค์๋ก rm, mv ๋ฑ ์คํ ๊ฐ๋ฅ
TO-BE (explore-harness + sparkshell โ ์์ + ์์ฝ)
sequenceDiagram autonumber participant CX as Codex CLI (์์ด์ ํธ) participant EH as explore-harness (Rust) participant SS as sparkshell (Rust) participant LLM as Spark Model alt ํ์ ์์ CX->>EH: omx explore --prompt "API ์๋ํฌ์ธํธ ์ฐพ๊ธฐ" EH->>EH: allowlist ๊ฒ์ฆ (10๊ฐ ๋ช ๋ น์ด๋ง) EH->>EH: ๊ฒฝ๋ก ํ์ถ ์ฐจ๋จ EH->>EH: read-only ์๋๋ฐ์ค ์คํ EH-->>CX: ๋งํฌ๋ค์ด ์์ฝ ๋ฐํ else ๊ธด ์ถ๋ ฅ ๋ช ๋ น CX->>SS: omx sparkshell rg "auth" src/ SS->>SS: ๋ช ๋ น ์คํ SS->>SS: ์ถ๋ ฅ 12์ค ์ด๊ณผ? SS->>LLM: ์์ฝ ์์ฒญ (summary/failures/warnings) LLM-->>SS: ๊ตฌ์กฐํ๋ ์์ฝ SS-->>CX: ์์ฝ๋ ์ถ๋ ฅ (์ปจํ ์คํธ ์ ์ฝ) end
explore-harness โ ์ฝ๊ธฐ ์ ์ฉ ํ์ ์์ง (์์ค: crates/omx-explore/src/main.rs)
Shell Allowlist โ ํ์ฉ๋๋ ๋ช ๋ น์ด 10๊ฐ
const ALLOWED_DIRECT_COMMANDS: &[&str] = &[
"rg", "grep", "ls", "find", "wc", "cat", "head", "tail", "pwd", "printf",
];์ด 10๊ฐ ๋ช ๋ น์ด๋ง ์คํํ ์ ์๋ค. ๋๋จธ์ง๋ ๋ชจ๋ ์ฐจ๋จ๋๋ค.
๋ช ๋ น์ด๋ณ ์ ํ ์ฌํญ
| ๋ช ๋ น์ด | ์ถ๊ฐ ์ ํ | ์ด์ |
|---|---|---|
rg | --pre ์ฐจ๋จ, stdin(-) ์ฐจ๋จ | ์ฝ๋ ์คํ ๋ฐฉ์ง |
grep | ํจํด + ํ์ผ ๊ฒฝ๋ก ํ์, stdin ์ฐจ๋จ | ์์ ์ ๋ ฅ ๋ฐฉ์ง |
find | -exec, -execdir, -ok, -delete, -fprint* ์ฐจ๋จ | ์ฐ๊ธฐ/์คํ ๋ฐฉ์ง |
cat, head, wc | ํ์ผ ๊ฒฝ๋ก ํ์, stdin ์ฐจ๋จ | ์์ ์ ๋ ฅ ๋ฐฉ์ง |
tail | ํ์ผ ๊ฒฝ๋ก ํ์, -f, -F, --follow ์ฐจ๋จ | ๋ฌดํ ๋๊ธฐ ๋ฐฉ์ง |
ls, pwd, printf | ์ถ๊ฐ ์ ํ ์์ | ์์ ํ ๋ช ๋ น์ด |
์ ธ ๋ฉํ ๋ฌธ์ ์ ๋ฉด ์ฐจ๋จ
// validate_shell_invocation()์์ ์ฐจ๋จํ๋ ๋ฌธ์์ด
for fragment in ["\n", "\r", "&&", "||", ";", "|", ">", "<", "`", "$(", "${"] {
if command.contains(fragment) {
return Err(...);
}
}ํ์ดํ, ๋ฆฌ๋ค์ด๋ ์ , ๋ ผ๋ฆฌ ์ฐ์ฐ์, ์๋ธ์ ธ, ๋ณ์ ํ์ฅ โ ์ ธ ์กฐํฉ์ ๋ชจ๋ ๊ฒฝ๋ก๋ฅผ ์ฐจ๋จํ๋ค.
โ rg auth src # ๋จ์ ๊ฒ์ โ ํ์ฉ
โ find . -type f -name "*.ts" # ํ์ผ ์ฐพ๊ธฐ โ ํ์ฉ
โ rg auth src | head # ํ์ดํ โ ์ฐจ๋จ
โ find . -exec rm {} + # exec โ ์ฐจ๋จ
โ /usr/bin/rg needle # ๊ฒฝ๋ก ์ง์ โ ์ฐจ๋จ
โ tail -f logfile # follow ๋ชจ๋ โ ์ฐจ๋จ
๊ฒฝ๋ก ํ์ถ ๋ฐฉ์ง โ 4๋จ๊ณ ๊ฒ์ฆ
OMX_EXPLORE_ROOT ํ๊ฒฝ ๋ณ์๋ก ์ค์ ๋ ๋ ํฌ์งํ ๋ฆฌ ๋ฃจํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ชจ๋ ๊ฒฝ๋ก๋ฅผ ๊ฒ์ฆํ๋ค:
- ์ ๊ทํ โ
.,..์ปดํฌ๋ํธ ํด์ - ํฌํจ ํ์ธ โ ์ ๊ทํ๋ ๊ฒฝ๋ก๊ฐ repo root๋ก ์์ํ๋์ง ๊ฒ์ฆ
- ์ฌ๋งํฌ ํด์ โ
canonicalize๋ก ์ฌ๋งํฌ ํ์ถ ๊ฐ์ง - ์ ๊ท ๊ฒฝ๋ก ํ์ธ โ ํด์๋ ๊ฒฝ๋ก๊ฐ ์ฌ์ ํ repo root ๋ด์ ์๋์ง ์ฌ๊ฒ์ฆ
โ cat ../secret.txt # ../ ํ์ถ โ ์ฐจ๋จ
โ cat /tmp/passwd # ์ ๋ ๊ฒฝ๋ก ์ธ๋ถ โ ์ฐจ๋จ
โ cat linked-outside/secret # ์ฌ๋งํฌ ํ์ถ โ ์ฐจ๋จ
Allowlist ํ๊ฒฝ ๊ฒฉ๋ฆฌ โ ๋ํผ ๋ฉ์ปค๋์ฆ
explore-harness๋ ์คํ ํ๊ฒฝ ์์ฒด๋ฅผ ๊ฒฉ๋ฆฌํ๋ค:
sequenceDiagram autonumber participant EH as explore-harness participant TMP as /tmp/allowlist/bin/ participant CX as codex exec participant W as wrapper (rg) participant RG as /usr/bin/rg (์ค์ ) EH->>TMP: ํ์ฉ ๋ช ๋ น์ด๋ณ ๋ํผ ์คํฌ๋ฆฝํธ ์์ฑ Note over TMP: PATH๋ฅผ ์ด ๋๋ ํ ๋ฆฌ๋ก ๊ต์ฒด EH->>CX: codex exec (PATH=tmp/bin, SHELL=wrapper-bash) CX->>W: rg pattern path W->>EH: --internal-allowlist-direct rg:/usr/bin/rg pattern path EH->>EH: allowlist ๊ฒ์ฆ + ๊ฒฝ๋ก ๊ฒ์ฆ EH->>RG: exec /usr/bin/rg pattern path RG-->>CX: ๊ฒฐ๊ณผ
Codex๊ฐ ์คํํ ์ ์๋ PATH๋ฅผ ์์ ๋ํผ ๋๋ ํ ๋ฆฌ๋ก ๊ต์ฒดํ์ฌ, allowlist ์ธ ๋ช
๋ น์ด๋ ์์ ์ฐพ์ ์ ์๊ฒ ๋ง๋ ๋ค.
2๋จ๊ณ ๋ชจ๋ธ Fallback
// 1์ฐจ: Spark ๋ชจ๋ธ (์ ๋น์ฉ, ๋น ๋ฆ)
let result = run_codex_with_model(&args.spark_model);
if result.exit_code == 0 { return Ok(result); }
// 2์ฐจ: Fallback ๋ชจ๋ธ (๊ณ ์ฑ๋ฅ)
let fallback_result = run_codex_with_model(&args.fallback_model);Spark ๋ชจ๋ธ์ด ์คํจํ๋ฉด Fallback ๋ชจ๋ธ๋ก ์ฌ์๋ํ๋ค. ์์ชฝ ๋ชจ๋ ์คํจํ๋ฉด ์์ธ ์๋ฌ๋ฅผ ๋ฐํํ๋ค.
Codex ํธ์ถ ์ค์
command
.arg("exec")
.arg("-s").arg("read-only") // ์๋๋ฐ์ค: ์ฝ๊ธฐ ์ ์ฉ
.arg("-c").arg("model_reasoning_effort=\"low\"") // ๋น์ฉ ์ ๊ฐ
.arg("-o").arg(&output_path) // ๋งํฌ๋ค์ด ์ํฐํฉํธ
.env("PATH", &allowlist.bin_dir) // ๊ฒฉ๋ฆฌ๋ PATH
.env("SHELL", &allowlist.shell_path) // ๊ฒฉ๋ฆฌ๋ SHELL
.env("OMX_EXPLORE_ROOT", &args.cwd) // ๊ฒฝ๋ก ๊ฒ์ฆ ๊ธฐ์ค| ์ค์ | ๊ฐ | ๋ชฉ์ |
|---|---|---|
--sandbox | read-only | ์ฝ๊ธฐ ์ ์ฉ ๊ฐ์ |
model_reasoning_effort | "low" | ํ ํฐ ๋น์ฉ ์ ๊ฐ |
PATH | ์์ ๋ํผ ๋๋ ํ ๋ฆฌ | allowlist ์ธ ๋ช ๋ น์ด ์ ๊ทผ ์ฐจ๋จ |
| ์ถ๋ ฅ | ๋งํฌ๋ค์ด ์ํฐํฉํธ | ๋งํฌ๋ค์ด๋ง ๋ฐํ |
sparkshell โ ์ ์์ ์ถ๋ ฅ ์์ฝ ์ฌ์ด๋์นด (์์ค: native/omx-sparkshell/src/main.rs)
๋ ๊ฐ์ง ์คํ ๋ชจ๋
enum SparkShellInput {
Command(Vec<String>), // ์ง์ ๋ช
๋ น ์คํ
TmuxPane { pane_id: String, tail_lines: usize }, // tmux pane ์บก์ฒ
}| ๋ชจ๋ | ๋ช ๋ น์ด | ์ฉ๋ |
|---|---|---|
| ์ง์ ์คํ | omx sparkshell rg "auth" src/ | ๋ช ๋ น์ด ์คํ + ์์ฝ |
| tmux ์บก์ฒ | omx sparkshell --tmux-pane %3 | Team ๋ชจ๋ Worker pane ์ถ๋ ฅ ์บก์ฒ |
tmux ์บก์ฒ ์ tail lines ๋ฒ์: 100~1,000์ค (๊ธฐ๋ณธ 200์ค).
์์ฝ ํ๋จ โ 12์ค ์๊ณ๊ฐ
const DEFAULT_MAX_VISIBLE_LINES: usize = 12;
let line_count = combined_visible_lines(&output.stdout, &output.stderr);
if line_count <= threshold {
// ์์ ์ถ๋ ฅ ๊ทธ๋๋ก ๋ฐํ
write_raw_output(&output.stdout, &output.stderr)?;
} else {
// LLM ๊ธฐ๋ฐ ์์ฝ
match summarize_output(&execution_argv, &output) {
Ok(summary) => write(summary),
Err(_) => write_raw_output(/* fallback */),
}
}12์ค ์ดํ๋ฉด ์์ ์ถ๋ ฅ ๋ฐํ, ์ด๊ณผํ๋ฉด LLM ์์ฝ. ์์ฝ ์คํจ ์ ์์ ์ถ๋ ฅ์ผ๋ก fallback. ํ๊ฒฝ ๋ณ์ OMX_SPARKSHELL_LINES๋ก ์๊ณ๊ฐ ์กฐ์ ๊ฐ๋ฅ.
๋ช ๋ น ํจ๋ฐ๋ฆฌ ๊ฐ์ง โ 11๊ฐ ํจ๋ฐ๋ฆฌ
์์ฝ ํ๋กฌํํธ์ ๋ช ๋ น ํจ๋ฐ๋ฆฌ ์ปจํ ์คํธ๋ฅผ ์ถ๊ฐํ์ฌ ์์ฝ ํ์ง์ ๋์ธ๋ค:
| ํจ๋ฐ๋ฆฌ | ๋ช ๋ น์ด | ์ค๋ช |
|---|---|---|
| git | git | Repository inspection |
| node-js | npm, npx, pnpm, yarn, bun, node | Package management, builds |
| python | python, python3, pip, uv, poetry, pytest | Python ์คํ, ํจํค์ง |
| rust | cargo, rustc | Build, test, lint |
| go | go | Build, test, modules |
| ruby | bundle, rake, ruby | Ruby tasks |
| java-kotlin | mvn, gradle, gradlew, java, kotlinc | JVM builds |
| c-cpp | make, cmake, gcc, g++, clang | Native builds |
| csharp | dotnet | .NET SDK |
| swift | swift, xcodebuild | Apple builds |
| generic-shell | ls, cat, find, grep, sed, awk ๋ฑ | ๊ธฐ๋ณธ ์ ธ |
์์ฝ ํ๋กฌํํธ ๊ตฌ์ฑ
"You summarize shell command output.
Return markdown bullets only. Allowed top-level sections: summary:, failures:, warnings:.
Do not suggest fixes, next steps, commands, or recommendations."
Command: {command_line}
Command family: {family_key}
Exit code: {exit_code}
STDOUT: {truncated_stdout}
STDERR: {truncated_stderr}
ํ์ฉ๋๋ ์ถ๋ ฅ ์น์
3๊ฐ๋ง: summary:, failures:, warnings:. ๋๋จธ์ง(next steps:, notes: ๋ฑ)๋ ์ ๊ทํ ๊ณผ์ ์์ ๋ฒ๋ ค์ง๋ค. โ์์ ๋ฐฉ๋ฒ ์ ์โ์ด๋ โ๋ค์ ๋จ๊ณ ์ถ์ฒโ์ ์ฐจ๋จํ์ฌ ์์ ์์ฝ๋ง ๋ฐํํ๋ค.
์ถ๋ ฅ truncation โ 2๋จ๊ณ ์ ๋จ
LLM์ ๋ณด๋ด๊ธฐ ์ ์ถ๋ ฅ์ ์ ๋จํ์ฌ ํ ํฐ ๋น์ฉ์ ๊ด๋ฆฌํ๋ค:
| ๋จ๊ณ | ๊ธฐ์ค | ๊ธฐ๋ณธ๊ฐ | ์ ๋ต |
|---|---|---|---|
| 1. ์ค ์ ์ ๋จ | OMX_SPARKSHELL_SUMMARY_MAX_LINES | 400์ค | head 50% + [... omitted ...] + tail 50% |
| 2. ๋ฐ์ดํธ ์ ๋จ | OMX_SPARKSHELL_SUMMARY_MAX_BYTES | 24,000๋ฐ์ดํธ | UTF-8 ์์ prefix/suffix ๋ถํ |
๋ชจ๋ธ ํด์ โ Spark + Fallback 2๋จ๊ณ
| ์์ | ํ๊ฒฝ ๋ณ์ | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|
| Spark 1์์ | OMX_SPARKSHELL_MODEL | โ |
| Spark 2์์ | OMX_DEFAULT_SPARK_MODEL | โ |
| Spark 3์์ | OMX_SPARK_MODEL (๋ ๊ฑฐ์) | โ |
| Spark ๊ธฐ๋ณธ๊ฐ | โ | gpt-5.3-codex-spark |
| Fallback 1์์ | OMX_SPARKSHELL_FALLBACK_MODEL | โ |
| Fallback 2์์ | OMX_DEFAULT_FRONTIER_MODEL | โ |
| Fallback ๊ธฐ๋ณธ๊ฐ | โ | gpt-5.4 |
Fallback ์ฌ์๋ ์กฐ๊ฑด: rate limit(429), quota, model not found, unavailable, capacity ๋ฑ ์ผ์์ ์ค๋ฅ์๋ง ์ฌ์๋ํ๋ค.
์๋ฌ ์ฒ๋ฆฌ์ exit code ๋ณด์กด
enum SparkshellError {
InvalidArgs(String), // Exit 2
Io(io::Error), // Exit 127 (not found), 126 (permission), 1 (other)
SummaryTimeout(u64), // Exit 1
SummaryBridge(String), // Exit 1
}ํต์ฌ ๊ท์น: exit code๋ ํญ์ ์๋ณธ ๋ช ๋ น์ด์ exit code๋ฅผ ๋ฐํํ๋ค. ์์ฝ ์ฑ๊ณต/์คํจ์ ๋ฌด๊ดํ๊ฒ, sparkshell์ ์๋ณธ ๋ช ๋ น์ด๊ฐ ์คํจํ์ผ๋ฉด ์คํจ๋ฅผ ๊ทธ๋๋ก ์ ํํ๋ค.
CLI ๋ผ์ฐํ
โ explore์ sparkshell์ ๋ถ๊ธฐ (์์ค: src/cli/explore.ts)
๋ผ์ฐํ ์์ฌ ๊ฒฐ์
omx explore ๋ช
๋ น์ด๊ฐ ์คํ๋๋ฉด, TypeScript ๋ ์ด์ด๊ฐ ๋จผ์ sparkshell ๋ผ์ฐํ
์ ์๋ํ๋ค:
graph TD REQ["omx explore --prompt '...'"] REQ --> CLASSIFY["ํ๋กฌํํธ ๋ถ๋ฅ"] CLASSIFY --> |"git log, find, rg ๋ฑ<br/>read-only ์ ธ ๋ช ๋ น"| SS["sparkshell ๋ผ์ฐํ "] CLASSIFY --> |"๋ณต์กํ ํ์ ์์ฒญ"| EH["explore-harness ๋ผ์ฐํ "] SS --> |์ฑ๊ณต| DONE["๊ฒฐ๊ณผ ๋ฐํ"] SS --> |์คํจ/๋ถ๊ฐ| EH EH --> |Spark ์ฑ๊ณต| DONE EH --> |Spark ์คํจ| FB["Fallback ๋ชจ๋ธ ์ฌ์๋"] FB --> DONE style SS fill:#d4edda style EH fill:#cce5ff
sparkshell ๋ผ์ฐํ ์กฐ๊ฑด
// sparkshell๋ก ๋ผ์ฐํ
๋๋ ๊ฒฝ์ฐ
// 1. git read-only ๋ช
๋ น: log, diff, status, show, branch, rev-parse
// 2. "run " ์ ๋์ฌ + find/ls/rg/grep (๊ฒฝ๋ก ์ด์ค์ผ์ดํ ์๋ ๊ฒฝ์ฐ)
const READ_ONLY_GIT_SUBCOMMANDS = new Set([
'log', 'diff', 'status', 'show', 'branch', 'rev-parse',
]);์
ธ ๋ฉํ ๋ฌธ์(|, &, ;, >, < ๋ฑ)๊ฐ ํฌํจ๋๋ฉด sparkshell ๋ผ์ฐํ
์์ ์ ์ธ๋๋ค.
AGENTS.md ๋ผ์ฐํ
๊ฐ์ด๋ (์์ค: src/hooks/explore-routing.ts)
USE_OMX_EXPLORE_CMD=true ํ๊ฒฝ ๋ณ์๊ฐ ์ค์ ๋๋ฉด, AGENTS.md์ ํ์ ๋ผ์ฐํ
๊ฐ์ด๋๊ฐ ์ฃผ์
๋๋ค:
// ํ์ ์ ํฉ ํจํด (omx explore ์ถ์ฒ)
const SIMPLE_EXPLORATION_PATTERNS = [
/\b(where|find|locate|search|grep)\b/i,
/\b(file|symbol|usage|reference)\b/i,
/\bhow does\b/i,
/\b(read[- ]?only|explor(e|ation)|inspect)\b/i,
];
// ํ์ ๋ถ์ ํฉ ํจํด (์ผ๋ฐ Codex ๊ฒฝ๋ก ์ ์ง)
const NON_EXPLORATION_PATTERNS = [
/\b(implement|write|edit|modify|refactor|fix)\b/i,
/\b(test|lint|typecheck|compile|deploy)\b/i,
/\b(migrate|rewrite|overhaul|redesign)\b/i,
];์์ด์ ํธ๊ฐ โํ์ผ ์ฐพ๊ธฐโ, โ์ฌ๋ณผ ๊ฒ์โ ๊ฐ์ ์์
์ omx explore๋ก, โ๊ตฌํโ, โ์์ โ, โํ
์คํธโ ๊ฐ์ ์์
์ ์ผ๋ฐ Codex ๊ฒฝ๋ก๋ก ๋ณด๋ด๋ advisory ๋ผ์ฐํ
์ด๋ค.
Explore ํ๋กฌํํธ ๊ณ์ฝ์ (์์ค: prompts/explore-harness.md, 4ํธ ์ฐธ๊ณ )
<identity>
You are OMX Explore, a low-cost shell-only repository exploration harness.
</identity>
<constraints>
- Read-only only. Never create, modify, delete, rename, or move files.
- Stay inside the current repository scope.
- Use shell inspection commands only.
- Prefer narrow, concrete lookup goals.
</constraints>
<allowed_commands>
rg, grep, ls, find, wc, cat, head, tail, pwd, printf
No pipes, redirection, &&, ||, ;, subshells, command substitution
</allowed_commands>์ถ๋ ฅ ํ์์ 4๊ฐ ์น์
์ผ๋ก ๊ณ ์ : ## Files, ## Relationships, ## Answer, ## Next steps.
๋ค์ดํฐ๋ธ ๋น๋ & ๋ฐฐํฌ
Cargo ์ํฌ์คํ์ด์ค
[workspace]
members = ["crates/omx-explore", "native/omx-sparkshell"]
resolver = "2"
[workspace.package]
version = "0.10.1"
edition = "2021"
[profile.dist]
inherits = "release"
lto = "thin"๋ ํฌ๋ ์ดํธ๊ฐ ์ํฌ์คํ์ด์ค๋ฅผ ๊ณต์ ํ๋ฉฐ, dist ํ๋กํ์์ LTO(Link Time Optimization)๋ฅผ ์ ์ฉํ๋ค.
๋ฐ์ด๋๋ฆฌ ํด์ ์ฐ์ ์์ (5๋จ๊ณ)
explore-harness์ sparkshell ๋ชจ๋ ๋์ผํ ํด์ ์ฒด์ธ์ ๋ฐ๋ฅธ๋ค:
| ์์ | ์์ค | ๊ฒฝ๋ก ์์ |
|---|---|---|
| 1 | ํ๊ฒฝ ๋ณ์ ์ค๋ฒ๋ผ์ด๋ | OMX_EXPLORE_BIN, OMX_SPARKSHELL_BIN |
| 2 | ์บ์๋ ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ | ~/.cache/oh-my-codex/native/0.10.1/darwin-arm64/omx-sparkshell/omx-sparkshell |
| 3 | ํจํค์ง ๋ด์ฅ ๋ฐ์ด๋๋ฆฌ | bin/native/darwin-arm64/omx-sparkshell |
| 4 | ๋ ํฌ์งํ ๋ฆฌ ๋น๋ ๊ฒฐ๊ณผ | target/release/omx-sparkshell |
| 5 | Cargo ๋น๋ (์ตํ ์๋จ) | cargo run --release -p omx-explore-harness -- |
Hydration โ ์๋ ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ๋ค์ด๋ก๋
์บ์์๋ ํจํค์ง์๋ ๋ฐ์ด๋๋ฆฌ๊ฐ ์์ผ๋ฉด, GitHub Release์์ ์๋ ๋ค์ด๋ก๋ํ๋ค:
graph TD CHECK["์บ์ ํ์ธ"] CHECK --> |์์| MANIFEST["native-release-manifest.json ๋ค์ด๋ก๋"] MANIFEST --> FIND["ํ๋ซํผ/์ํคํ ์ฒ ๋งค์นญ"] FIND --> DL["์์นด์ด๋ธ ๋ค์ด๋ก๋ (.tar.gz)"] DL --> SHA["SHA-256 ์ฒดํฌ์ฌ ๊ฒ์ฆ"] SHA --> |์ผ์น| EXTRACT["์์ถ ํด์ + ์บ์ ์ ์ฅ"] SHA --> |๋ถ์ผ์น| FAIL["์๋ฌ: SHA256 mismatch"] EXTRACT --> DONE["๋ฐ์ด๋๋ฆฌ ์ฌ์ฉ ๊ฐ๋ฅ"] style SHA fill:#fff3cd style FAIL fill:#f8d7da style DONE fill:#d4edda
| ํญ๋ชฉ | ์์ธ |
|---|---|
| ๋งค๋ํ์คํธ URL | https://github.com/.../releases/download/v{version}/native-release-manifest.json |
| ์ง์ ํ๋ซํผ | macOS (arm64, x86_64), Linux (x64, arm64), Windows |
| ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ | SHA-256 ์ฒดํฌ์ฌ |
| ์บ์ ๊ฒฝ๋ก | ~/.cache/oh-my-codex/native/{version}/{platform}-{arch}/{product}/ |
| ๋นํ์ฑํ | OMX_NATIVE_AUTO_FETCH=0 |
GLIBC ํธํ์ฑ Fallback
Linux์์ ๋ฐ์ด๋๋ฆฌ์ GLIBC ๋ฒ์ ์ด ์์คํ ๋ณด๋ค ๋์ผ๋ฉด ์คํ์ด ์คํจํ๋ค:
const SPARKSHELL_GLIBC_INCOMPATIBLE_PATTERN = /GLIBC(?:XX)?_[0-9.]+['` ]+not found/i;์ด ํจํด์ ๊ฐ์งํ๋ฉด sparkshell์ ์์ฝ ์์ด ์์ ๋ช ๋ น ์คํ์ผ๋ก graceful fallbackํ๋ค.
sparkshell ํ๊ฒฝ ๋ณ์ ์ ์ฒด ๋งต
| ๋ณ์ | ๊ธฐ๋ณธ๊ฐ | ์ฉ๋ |
|---|---|---|
OMX_SPARKSHELL_MODEL | gpt-5.3-codex-spark | ์์ฝ Spark ๋ชจ๋ธ |
OMX_SPARKSHELL_FALLBACK_MODEL | gpt-5.4 | ์์ฝ Fallback ๋ชจ๋ธ |
OMX_SPARKSHELL_LINES | 12 | ์์ฝ ํ๋จ ์ค ์ ์๊ณ๊ฐ |
OMX_SPARKSHELL_SUMMARY_TIMEOUT_MS | 60000 | ์์ฝ ํ์์์ (ms) |
OMX_SPARKSHELL_SUMMARY_MAX_LINES | 400 | ํ๋กฌํํธ ์ต๋ ์ค ์ |
OMX_SPARKSHELL_SUMMARY_MAX_BYTES | 24000 | ํ๋กฌํํธ ์ต๋ ๋ฐ์ดํธ |
OMX_SPARKSHELL_BIN | โ | ๋ฐ์ด๋๋ฆฌ ๊ฒฝ๋ก ์ค๋ฒ๋ผ์ด๋ |
OMX_NATIVE_AUTO_FETCH | 1 | ์๋ hydration (0=๋นํ์ฑํ) |
OMX_NATIVE_CACHE_DIR | ~/.cache/oh-my-codex/native | ์บ์ ๋๋ ํ ๋ฆฌ |
USE_OMX_EXPLORE_CMD | โ | ํ์ ๋ผ์ฐํ ํ์ฑํ |
OMC์์ ๋น๊ต
| ํญ๋ชฉ | OMC | OMX |
|---|---|---|
| ๋ค์ดํฐ๋ธ ๋๊ตฌ | ์์ | explore-harness + sparkshell (Rust) |
| ์ฝ๊ธฐ ์ ์ฉ ํ์ | Claude Code ๊ธฐ๋ณธ ์ ธ | allowlist 10๊ฐ ๋ช ๋ น์ด + 4๋จ๊ณ ๊ฒฝ๋ก ๊ฒ์ฆ |
| ์ถ๋ ฅ ์์ฝ | ์์ | 12์ค ์๊ณ๊ฐ + LLM ์ ์์ ์์ฝ |
| ๋ช ๋ น ํจ๋ฐ๋ฆฌ ์ธ์ | ์์ | 11๊ฐ ํจ๋ฐ๋ฆฌ๋ณ ๋ง์ถค ํ๋กฌํํธ |
| ๋น๋ ์์คํ | TypeScript๋ง | Cargo ์ํฌ์คํ์ด์ค + TypeScript ํ์ด๋ธ๋ฆฌ๋ |
| ๋ฐ์ด๋๋ฆฌ ๋ฐฐํฌ | N/A | ๋ฉํฐ ํ๋ซํผ + SHA-256 + hydration |
| tmux ์บก์ฒ | tmux capture-pane ์ง์ | sparkshell โtmux-pane (์์ฝ ํฌํจ) |
| ๋ผ์ฐํ | ์์ | AGENTS.md advisory ๋ผ์ฐํ + regex ํจํด ๋ถ๋ฅ |
ํต์ฌ ์ฐจ์ด: OMC๋ ์์ด์ ํธ ํ์์ ๋ณ๋ ์์ ์ฅ์น๊ฐ ์์ผ๋ฉฐ, ์ถ๋ ฅ๋ ๊ทธ๋๋ก ์ปจํ ์คํธ์ ๋ค์ด๊ฐ๋ค. OMX๋ Rust ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ๋ก **๋ณด์(allowlist + ๊ฒฝ๋ก ๊ฒ์ฆ + ์๋๋ฐ์ค)**๊ณผ **ํจ์จ(์ ์์ ์์ฝ + ์ปจํ ์คํธ ์ ์ฝ)**์ ๋์์ ํ๋ณดํ๋ค.