- Codex Skill Discovery๋ codex ๋ฐ์ด๋๋ฆฌ๊ฐ ์คํฌ ํ์ผ์ ํ์ํ๋ ๊ณ์ธต์ ํ์ผ ์์คํ ์ค์บ ๋ฉ์ปค๋์ฆ
HOME(USER-level)๊ณผcwd(PROJECT-level) ๋ ๊ฒฝ๋ก๋ฅผ ๋์์ ์ค์บํ์ฌ ์คํฌ ๋ชฉ๋ก์ ๊ตฌ์ฑํ๋ ์ด์ค ๋ ๋ฒจ ๋์ค์ปค๋ฒ๋ฆฌ ๊ตฌ์กฐ- app-server JSON-RPC ํ๋กํ ์ฝ์
thread/start์skills/list๋ ๋ฉ์๋๋ฅผ ํตํด ์ ์ด๋๋ ์ธ์ ๊ธฐ๋ฐ ์คํฌ ๋ผ์ฐํ
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- ์์ฒด ์๋น์ค๊ฐ codex๋ฅผ spawnํ์ฌ ์ฌ์ฉํ ๋, ์๋น์ค ์ ์ฉ ์คํฌ(์: ์ฌ๋ด DB ๊ฒ์)์ด ์ฌ์ฉ์์ ๊ธ๋ก๋ฒ CLI์ ๋ ธ์ถ๋์ง ์์์ผ ํ๋ค
- ๋ฐ๋๋ก, ์ฌ์ฉ์๊ฐ ๊ธ๋ก๋ฒํ๊ฒ ์ค์ ํ ์คํฌ์ ์๋น์ค์์๋ ์ฌ์ฉ ๊ฐ๋ฅํด์ผ ํ๋ค
- codex ๋ฐ์ด๋๋ฆฌ ๋ฒ์ ์ด ์ฌ์ฉ์๋ง๋ค ๋ค๋ฅผ ์ ์๋ ์ํฉ์์๋ ์๋น์ค๊ฐ ์ผ๊ด๋๊ฒ ๋์ํด์ผ ํ๋ค
์ผ์ด์ค A: ์๋น์ค๋ ์ฌ์ฉ์์ ๊ธ๋ก๋ฒ CLI๊ฐ ์๋ ์์ฒด ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค
๋ฐ์ด๋๋ฆฌ ํด์ ๊ฒฝ๋ก
์ฌ์ฉ์๊ฐ npm install -g @openai/codex๋ก ์ค์นํ ๊ธ๋ก๋ฒ CLI๋ /opt/homebrew/bin/codex ๋ฑ์ ์์นํ๋ค. ์๋น์ค๋ ์ด๊ฒ์ ์ฌ์ฉํ์ง ์๋๋ค.
์๋น์ค ํ๋ก์ ํธ์ package.json์ @openai/codex๋ฅผ ์์กด์ฑ์ผ๋ก ์ ์ธํ๊ณ , ํ๋ก์ ํธ ๋ด์์ npm install์ ์คํํ๋ฉด ํ๋ก์ ํธ์ node_modules/ ์์ codex ๋ฐ์ด๋๋ฆฌ๊ฐ ์ค์น๋๋ค. ์๋น์ค๋ ์ด ๋ก์ปฌ ๋ฐ์ด๋๋ฆฌ๋ฅผ ํด์ํ์ฌ spawnํ๋ค:
my-service/
โโโ node_modules/
โโโ @openai/codex-darwin-arm64/
โโโ vendor/
โโโ aarch64-apple-darwin/
โโโ codex/
โโโ codex โ ์ด ๋ฐ์ด๋๋ฆฌ๋ฅผ spawn
// package.json โ ์บ๋ฟ ์์ด ๊ณ ์ ๋ฒ์
{ "@openai/codex": "0.105.0" }์ฌ์ฉ์๊ฐ ๊ธ๋ก๋ฒ์ 0.98.0์ ์ฐ๋ 0.116์ ์ฐ๋ , ์๋น์ค๋ ํญ์ ํ๋ก์ ํธ node_modules/์ ์ค์น๋ 0.105.0 ๋ฐ์ด๋๋ฆฌ๋ฅผ spawnํ๋ค. ๊ธ๋ก๋ฒ CLI์ ์๋น์ค์ ๋ฐ์ด๋๋ฆฌ๋ ์์ ํ ๋ณ๊ฐ์ ์ค์น๋ค.
Q1. ์ฌ์ฉ์์ ๊ธ๋ก๋ฒ ์คํฌ X๋ฅผ ์๋น์ค์์ ์ฌ์ฉํ ์ ์๋๊ฐ?
YES. ์ฌ์ฉ์๊ฐ ~/.agents/skills/X/ (๋๋ ๋ ๊ฑฐ์ ~/.codex/skills/X/)์ ์ค์นํ ๊ธ๋ก๋ฒ ์คํฌ์ ์๋น์ค์์๋ ๋ฐ๊ฒฌ๋๋ค.
์ด์ : spawn ์ process.env๋ฅผ ๊ทธ๋๋ก ์ ๋ฌํ๋ฏ๋ก HOME ํ๊ฒฝ๋ณ์๊ฐ ์ ์ง๋๋ค. codex ๋ฐ์ด๋๋ฆฌ๋ ํญ์ HOME ๊ธฐ๋ฐ์ USER-level ๊ฒฝ๋ก(~/.agents/skills/ + ~/.codex/skills/)๋ฅผ ์ค์บํ๋ค.
sequenceDiagram autonumber participant User as ์ฌ์ฉ์ ํ๊ฒฝ participant App as Service Backend participant Codex as codex (๋ฒ๋ค ๋ฐ์ด๋๋ฆฌ) participant GS as ~/.agents/skills/ participant PS as {appDataDir}/.agents/skills/ User->>GS: ์คํฌ X ์ค์น (๊ธ๋ก๋ฒ CLI๋ก) App->>Codex: spawn(codexBinaryPath, env: { HOME=~ }) App->>Codex: thread/start(cwd=appDataDir) Codex->>GS: HOME ๊ธฐ์ค ์ค์บ โ ์คํฌ X ๋ฐ๊ฒฌ โ Codex->>PS: cwd ๊ธฐ์ค ์ค์บ โ ์ฌ๋ด DB ๊ฒ์ ์คํฌ ๋ฐ๊ฒฌ โ Codex-->>App: ์คํฌ X + ์ฌ๋ด DB ๊ฒ์ ์คํฌ ๋ชจ๋ ์ฌ์ฉ ๊ฐ๋ฅ
์ค์ ์์
Q. ๊ธ๋ก๋ฒ์
study-guard์คํฌ์, ํ๋ก์ ํธ์๋ด๋ถ DB ์ ๊ทผ์คํฌ์ ์ค์ ํ๋ค. ์๋น์ค์์ ๋ ๋ค ์ฌ์ฉํ ์ ์๋๊ฐ?
A. ๋ ๋ค ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
๊ธ๋ก๋ฒ ์คํฌ (USER-level):
~/.agents/skills/study-guard/
ํ๋ก์ ํธ ์คํฌ (PROJECT-level):
{appDataDir}/.agents/skills/access-db/
์๋น์ค๊ฐ codex๋ฅผ spawnํ๋ฉด:
spawn์process.env๊ฐ ๊ทธ๋๋ก ์ ๋ฌ โHOME=~โ~/.agents/skills/study-guard/๋ฐ๊ฒฌ โthread/start์์cwd๋ฅผ ์๋น์ค ๋ฐ์ดํฐ ๋๋ ํ ๋ฆฌ๋ก ์ค์ โ{appDataDir}/.agents/skills/access-db/๋ฐ๊ฒฌ โ
๋ ๋ ๋ฒจ์ ํฉ์งํฉ์ผ๋ก ๋์ํ๋ฏ๋ก, ์ธ์
์์ study-guard์ access-db ์คํฌ ๋ชจ๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
sequenceDiagram autonumber participant App as Service Backend participant Codex as codex (๋ฒ๋ค ๋ฐ์ด๋๋ฆฌ) participant GS as ~/.agents/skills/study-guard/ participant PS as {appDataDir}/.agents/skills/access-db/ App->>Codex: spawn(codexBinaryPath, env: { HOME=~ }) App->>Codex: thread/start(cwd=appDataDir) Codex->>GS: HOME ๊ธฐ์ค ์ค์บ โ study-guard ๋ฐ๊ฒฌ โ Codex->>PS: cwd ๊ธฐ์ค ์ค์บ โ access-db ์คํฌ ๋ฐ๊ฒฌ โ Codex-->>App: study-guard + access-db ๋ชจ๋ ์ฌ์ฉ ๊ฐ๋ฅ
ํต์ฌ์ ๋ฐ์ด๋๋ฆฌ๋ ๋ฒ๋ค๋ ๊ฒ์ ์ฌ์ฉํ์ง๋ง, HOME์ ์ฌ์ฉ์ ๊ฒ์ ๊ทธ๋๋ก ์ ์งํ๋ค๋ ์ ์ด๋ค. ๋ฐ์ด๋๋ฆฌ ๋ฒ์ ๊ณผ ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ ๊ฒฝ๋ก๋ ๋ณ๊ฐ์ ๋ฌธ์ ๋ค.
์ผ์ด์ค B: ๋ฐ์ด๋๋ฆฌ ๋ฒ์ ํ๋๊ณผ ์ฌ์ฉ์ ๊ฐ ๋ ๋ฆฝ์ฑ
์๋๋ฆฌ์ค: ํ์ฌ ์์ค์ฝ๋ ๊ธฐ์ค โ ์์ชฝ ๊ฒฝ๋ก ๋ชจ๋ ์ค์บ
์์ค์ฝ๋ ๋ถ์ ๊ฒฐ๊ณผ, ํ์ฌ codex๋ .codex/skills/์ .agents/skills/๋ฅผ ๋์์ ์ค์บํ๋ค. User scope์์ ~/.codex/skills/๋ deprecated์ด์ง๋ง ํ์ ํธํ์ ์ํด ์ ์ง๋๋ค.
| ๊ตฌ๋ถ | ์ฌ์ฉ์ A (๋ ๊ฑฐ์ ๊ฒฝ๋ก ์ฌ์ฉ) | ์ฌ์ฉ์ B (ํ์ฌ ๊ถ์ฅ ๊ฒฝ๋ก ์ฌ์ฉ) |
|---|---|---|
| ๊ธ๋ก๋ฒ ์คํฌ ์์น | ~/.codex/skills/ | ~/.agents/skills/ |
| ์๋น์ค ๋ฐ์ด๋๋ฆฌ | 0.105.0 (๋์ผ) | 0.105.0 (๋์ผ) |
| ์๋น์ค์์ ๊ธ๋ก๋ฒ ์คํฌ ๋ฐ๊ฒฌ | โ (deprecated ๊ฒฝ๋ก๋ ์ค์บ) | โ (ํ์ฌ ๊ถ์ฅ ๊ฒฝ๋ก ์ค์บ) |
A์ B ๋ชจ๋ ์๋น์ค์์ ์ ์ ๋์ํ๋ค. codex ๋ฐ์ด๋๋ฆฌ๊ฐ ์์ชฝ ๊ฒฝ๋ก๋ฅผ ๋ชจ๋ ์ค์บํ๋ฏ๋ก, ์ฌ์ฉ์๊ฐ ์ด๋ ๊ฒฝ๋ก์ ์คํฌ์ ์ค์นํ๋ ๋ฐ๊ฒฌ๋๋ค.
์ฌ์ฉ์ A (๋ ๊ฑฐ์ ๊ฒฝ๋ก):
์๋น์ค: {cwd}/.codex/skills/ โ
+ ~/.codex/skills/ โ
(deprecated but scanned)
+ ~/.agents/skills/ โ
(current)
์ฌ์ฉ์ B (ํ์ฌ ๊ถ์ฅ ๊ฒฝ๋ก):
์๋น์ค: {cwd}/.codex/skills/ โ
+ ~/.codex/skills/ โ
(deprecated but scanned)
+ ~/.agents/skills/ โ
(current)
๊ฒฐ๋ก : ์๋น์ค์ ํต์ฌ ๋์์ ์ฌ์ฉ์์ ๊ธ๋ก๋ฒ CLI ๋ฒ์ ๊ณผ ์์ ํ ๋
๋ฆฝ์ ์ด๋ค. package.json์ ๋ฒ์ ์ ํ๋ํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ํ ์์ชฝ ๊ฒฝ๋ก๋ฅผ ๋ชจ๋ ์ค์บํ๋ฏ๋ก ๊ฒฝ๋ก ๋ง์ด๊ทธ๋ ์ด์
๊ณผ๋๊ธฐ์๋ ๋ฌธ์ ์๋ค.
์๋น์ค ์ธก ์คํฌ ๋ฐฐํฌ ์ ๋ต
์์ค์ฝ๋ ๋ถ์ ๊ฒฐ๊ณผ codex๋ .codex/skills/์ .agents/skills/๋ฅผ ๋์์ ์ค์บํ๋ฏ๋ก, ์ด๋ ํ ๊ฒฝ๋ก์๋ง ๋ฐฐํฌํ๋ฉด ์ถฉ๋ถํ๋ค. ์๋น์ค๋ ํ์ฌ ๊ถ์ฅ ๊ฒฝ๋ก์ธ .agents/skills/์ ๋ฐฐํฌํ๋ค.
1. ๋จ์ผ ๊ฒฝ๋ก ๋ฐฐํฌ (ํ์ฌ ์๋น์ค ๊ตฌํ)
skill-deployer.service.ts์์ {appDataDir}/.agents/skills/์ ๋ฐฐํฌ:
function getCodexSkillsTarget(): string {
return path.join(getAppDataDir(), ".agents", "skills");
}codex ๋ฐ์ด๋๋ฆฌ๊ฐ repo_agents_skill_roots() ํจ์๋ฅผ ํตํด cwd ๊ฒฝ๋ก ์์ .agents/skills/๋ฅผ ์ค์บํ๋ฏ๋ก, thread/start์์ cwd=appDataDir๋ก ์ค์ ํ๋ฉด ์๋ ๋ฐ๊ฒฌ๋๋ค.
2. ๋ฏธ๋ deprecated ์ ๊ฑฐ์ ๋ํ ๋ฐฉ์ด
ํ์ฌ .codex/skills/(Project scope)๋ deprecated๊ฐ ์๋์ง๋ง, ํฅํ ์ ๊ฑฐ๋ ๊ฐ๋ฅ์ฑ์ ๋๋นํ์ฌ .agents/skills/๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์์ ํ๋ค. ๋ง์ฝ ๋ฏธ๋์ .agents/skills/๋ ๊ฒฝ๋ก๊ฐ ๋ณ๊ฒฝ๋๋ค๋ฉด, symlink ๋ฐฐํฌ๋ก ๋์ํ ์ ์๋ค:
๊ณต์ ๋ฌธ์: โCodex supports symlinked skill folders and follows symlink targets when scanning these locations.โ
3. skills/list ๊ฒ์ฆ (์ ํ)
๋ฐ์ด๋๋ฆฌ ์ ๊ทธ๋ ์ด๋ ์ ์คํฌ ๋ฐ๊ฒฌ ์ํ๋ฅผ ๊ฒ์ฆํ ์ ์๋ค:
const skillsResult = await peer.request("skills/list", {
cwds: [APP_DATA_DIR],
});
const expectedSkills = ["access-db", "internal-search"];
const foundSkills = skillsResult.skills.map(s => s.name);
const missing = expectedSkills.filter(s => !foundSkills.includes(s));
if (missing.length > 0) {
logger.error(`์คํฌ ๋ฏธ๋ฐ๊ฒฌ: ${missing.join(", ")} โ ๊ฒฝ๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ์`);
}skills/list๋ ์ฝ๊ธฐ ์ ์ฉ ์กฐํ์ด๋ฏ๋ก, ์ธ์
๋์์ ์ํฅ ์์ด ์คํฌ ๋ฐ๊ฒฌ ์ํ๋ฅผ ๊ฒ์ฆํ ์ ์๋ค.
์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ์ ๋ ๋ ๋ฒจ
| Level | ๊ฒฝ๋ก | ์ํ | ๊ฒฐ์ ์์ | ์ค๋ช |
|---|---|---|---|---|
| USER (๊ธ๋ก๋ฒ) | ~/.agents/skills/ | ํ์ฌ ๊ถ์ฅ | spawn ์ HOME ํ๊ฒฝ๋ณ์ | ๋ชจ๋ codex ์ธ์ ์์ ํญ์ ์ค์บ |
| USER (๊ธ๋ก๋ฒ) | ~/.codex/skills/ | deprecated (ํ์ ํธํ) | spawn ์ CODEX_HOME | ํ์ ํธํ์ฉ์ผ๋ก ์ฌ์ ํ ์ค์บ |
| PROJECT (ํ๋ก์ ํธ) | {cwd}/.codex/skills/ | active | thread/start์ cwd ํ๋ผ๋ฏธํฐ | ํ๋ก์ ํธ config layer ๊ธฐ์ค |
| PROJECT (ํ๋ก์ ํธ) | {root~cwd}/.agents/skills/ | ํ์ฌ ๊ถ์ฅ | thread/start์ cwd ํ๋ผ๋ฏธํฐ | ํ๋ก์ ํธ ๋ฃจํธ~cwd ์ฌ์ด ๋ชจ๋ ์ค์บ |
๋ ๋ ๋ฒจ์ ํฉ์งํฉ์ผ๋ก ๋์ํ๋ค. USER ๊ฒฝ๋ก์ PROJECT ๊ฒฝ๋ก๋ฅผ ๋ชจ๋ ์ค์บํ ํ, dedupe_skill_roots_by_path()๋ก ์ค๋ณต์ ์ ๊ฑฐํ๊ณ ๋ฐ๊ฒฌ๋ ๋ชจ๋ ์คํฌ์ ํด๋น ์ธ์
์์ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฒ ๋ง๋ ๋ค.
HOME๊ณผ cwd๋ ์คํฌ๋ง์ ์ํ ์ค์ ์ด ์๋๋ค
HOME๊ณผ cwd๋ codex ์ ๋ฐ์ ์ํฅ์ ์ฃผ๋ ์ค์ ์ด๋ฉฐ, ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ๋ ๊ทธ ์ค ํ๋์ ๋ถ๊ฐ ํจ๊ณผ์ผ ๋ฟ์ด๋ค.
HOME (spawn ํ๊ฒฝ๋ณ์) โ ~/.codex/ ์ ์ฒด + ~/.agents/ ๋ฅผ ๊ฒฐ์ :
| ๊ฒฝ๋ก | ์ฉ๋ |
|---|---|
~/.codex/config.toml | codex ์ค์ (๋ชจ๋ธ, API ํค ๊ฒฝ๋ก ๋ฑ) |
~/.codex/auth/ | ์ธ์ฆ ์ ๋ณด |
~/.codex/db/ | codex ๋ด๋ถ ๋ฐ์ดํฐ |
~/.codex/certs/ | TLS ์ธ์ฆ์ |
~/.codex/skills/ | USER-level ์คํฌ (deprecated, ํ์ ํธํ) |
~/.agents/skills/ | USER-level ์คํฌ (ํ์ฌ ๊ถ์ฅ) |
process.env๋ก HOME์ ๊ทธ๋๋ก ์ ๋ฌํ๋ ์ด์ ๋ ์ฌ์ฉ์์ codex ์ค์ , ์ธ์ฆ ์ ๋ณด ๋ฑ์ ๋ชจ๋ ํ์ฉํ๊ธฐ ์ํด์๋ค.
cwd (thread/start ํ๋ผ๋ฏธํฐ) โ ์ธ์
์ ์์
๋๋ ํ ๋ฆฌ ์ ๋ฐ์ ๊ฒฐ์ :
| ์ํฅ | ์ค๋ช |
|---|---|
| ๋ช ๋ น ์คํ | codex๊ฐ ์ ธ ๋ช ๋ น์ ์คํํ ๋์ ์์ ๋๋ ํ ๋ฆฌ |
| ํ์ผ ์ ๊ทผ | ์๋ ๊ฒฝ๋ก ๊ธฐ์ค์ (ํ์ผ ์ฝ๊ธฐ/์ฐ๊ธฐ) |
| ์๋๋ฐ์ค ๊ฒฝ๊ณ | sandbox_workspace_write ๋ชจ๋์ workspace ๋ฃจํธ |
| ํ๋ก์ ํธ ์ค์ | {cwd}/.codex/ ํ๋ก์ ํธ ๋ ๋ฒจ ์ค์ |
| ํ๋ก์ ํธ ์คํฌ | {cwd}/.codex/skills/ + {cwd}/.agents/skills/ PROJECT-level ์คํฌ |
cwd๋ฅผ ์๋น์ค ๋ฐ์ดํฐ ๋๋ ํ ๋ฆฌ๋ก ์ค์ ํ๋ ๋ณธ๋ ๋ชฉ์ ์ codex์ ํ์ผ ์์
๊ณผ ๋ช
๋ น ์คํ์ ํด๋น ๋๋ ํ ๋ฆฌ ์์์ ์ํํ๋๋ก ํ๋ ๊ฒ์ด๊ณ , PROJECT-level ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ๋ ๊ทธ์ ๋ฐ๋ผ์ค๋ ๋ถ๊ฐ ํจ๊ณผ๋ค.
์ฝ๋์์ ๋ ๋ ๋ฒจ์ด ์ค์ ๋๋ ์์น
// 1๋จ๊ณ: spawn โ ํ๋ก์ธ์ค ์์ฑ
const spawnEnv = {
...process.env, // HOME ํฌํจ โ USER-level ๋์ค์ปค๋ฒ๋ฆฌ ๊ทผ๊ฑฐ
TOOL_CLI: "/path/to/skill/script/tool-cli", // ์คํฌ์ด ์ฌ์ฉํ CLI ๊ฒฝ๋ก
TOOL_BASE_URL: `http://localhost:${backendPort}`, // ์คํฌ โ ์๋น์ค API ํต์ ์ฃผ์
TOOL_USER_ID: userId, // ์์ฒญ ์ฌ์ฉ์ ์๋ณ
};
const child = spawn(codexBinaryPath, ["app-server", ...flags], {
stdio: ["pipe", "pipe", "pipe"],
env: spawnEnv,
});
// 2๋จ๊ณ: thread/start โ ์ธ์
์์ฑ
await peer.request("thread/start", {
model: "o4-mini",
cwd: APP_DATA_DIR, // โ PROJECT-level ๋์ค์ปค๋ฒ๋ฆฌ ๊ทผ๊ฑฐ
approvalPolicy: "never",
});| ์ค์ ์์น | ๊ฒฐ์ ํ๋ ๊ฒ | ๊ธ๋ก๋ฒ ์คํฌ X | ์ฌ๋ด DB ๊ฒ์ ์คํฌ |
|---|---|---|---|
spawn env (...process.env) | HOME โ ~/.agents/skills/ + ~/.codex/skills/ | ์ฌ๊ธฐ์ ๋ฐ๊ฒฌ | - |
thread/start (cwd) | cwd โ {appDataDir}/.agents/skills/ + {appDataDir}/.codex/skills/ | - | ์ฌ๊ธฐ์ ๋ฐ๊ฒฌ |
์ฌ์ฉ์์ ๊ธ๋ก๋ฒ ์คํฌ X๋ thread/start๊ฐ ์๋๋ผ spawn ์ ์ ๋ฌ๋ HOME ํ๊ฒฝ๋ณ์๋ก ์ธํด ๋ฐ๊ฒฌ๋๋ค. spawnEnv์์ HOME์ ์ ๊ฑฐํ๊ฑฐ๋ ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ฎ์ด์ฐ๋ฉด ๊ธ๋ก๋ฒ ์คํฌ X๋ ๋ฐ๊ฒฌ๋์ง ์๋๋ค.
cwd๊ฐ ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ์์ ํ๋ ์ญํ
HOME(spawn ํ๊ฒฝ๋ณ์)์ด ์ด๋ฏธ ๊ธ๋ก๋ฒ ์คํฌ ์ ๊ทผ์ ์ ๊ณตํ๋๋ฐ, cwd(thread/start ํ๋ผ๋ฏธํฐ)๋ ์ถ๊ฐ์ ์ธ ํ๋ก์ ํธ ์ค์ฝํ ๊ฒฝ๋ก๋ฅผ ์ ๊ณตํ๋ค:
์ฌ์ฉ์๊ฐ codex CLI ์ง์ ์ฌ์ฉ:
spawn HOME=~ โ ~/.agents/skills/ (๊ธ๋ก๋ฒ ์คํฌ X โ
)
thread/start cwd=~/projects/myapp โ ~/projects/myapp/.agents/skills/ (์ฌ๋ด DB ๊ฒ์ ์คํฌ โ)
์๋น์ค๊ฐ spawn:
spawn HOME=~ โ ~/.agents/skills/ (๊ธ๋ก๋ฒ ์คํฌ X โ
)
thread/start cwd=appDataDir โ {appDataDir}/.agents/skills/ (์ฌ๋ด DB ๊ฒ์ ์คํฌ โ
)
cwd๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๋์ผํ HOME์ ๊ณต์ ํ๋ฉด์๋ ์๋น์ค ์ ์ฉ ์คํฌ์ ๊ฒฉ๋ฆฌํ ์ ์๋ค.
JSON-RPC ๋ฉ์๋: skills/list vs thread/start
codex app-server๋ JSON-RPC 2.0 ํ๋กํ ์ฝ๋ก ํต์ ํ๋ค. ์คํฌ ๊ด๋ จ ๋ฉ์๋๋ ๋ ๊ฐ์ง๋ค.
thread/start
์ธ์ ์ ์์ฑํ๊ณ ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ ๋ฉ์๋.
{
"method": "thread/start",
"params": {
"model": "o4-mini",
"cwd": "/path/to/service/data",
"approvalPolicy": "never"
}
}cwd๊ฐ PROJECT-level ์คํฌ ํ์ ๊ฒฝ๋ก๋ฅผ ๊ฒฐ์ ํ๋ค. ์ด ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด codex๋ USER-level(HOME ๊ธฐ๋ฐ)๊ณผ PROJECT-level(cwd ๊ธฐ๋ฐ)์ ๋ชจ๋ ์ค์บํ์ฌ ์ธ์
์ ์คํฌ ๋ชฉ๋ก์ ๊ตฌ์ฑํ๋ค.
skills/list
ํ์ฌ ์ฌ์ฉ ๊ฐ๋ฅํ ์คํฌ ๋ชฉ๋ก์ ์กฐํํ๋ ์ฝ๊ธฐ ์ ์ฉ ๋ฉ์๋.
{
"method": "skills/list",
"params": {
"perCwdExtraUserRoots": ["/additional/path"]
}
}perCwdExtraUserRoots ํ๋ผ๋ฏธํฐ๋ก ์ถ๊ฐ ํ์ ๊ฒฝ๋ก๋ฅผ ์ง์ ํ ์ ์์ง๋ง, ์ด๊ฒ์ ์กฐํ ์ ์ฉ์ด๋ค. ํด๋น ๊ฒฝ๋ก์ ์คํฌ ๋ชฉ๋ก์ ๋ณผ ์ ์์ ๋ฟ, ์ธ์
์์ ์ฌ์ฉ ๊ฐ๋ฅํ๊ฒ ๋ง๋๋ ๊ฒ์ ์๋๋ค. thread/start์๋ ์ด ํ๋ผ๋ฏธํฐ๊ฐ ์กด์ฌํ์ง ์์ผ๋ฏ๋ก, API๋ฅผ ํตํ ์ถ๊ฐ ๊ฒฝ๋ก ์ฃผ์
์ผ๋ก๋ ์ธ์
์ ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ์ ์ํฅ์ ์ค ์ ์๋ค. ์ด๊ฒ์ด ๋ฌผ๋ฆฌ์ ํ์ผ ๋ฐฐํฌ ๋ฐฉ์์ด ๊ฐ์ฅ ์์ ์ ์ธ ์ด์ ๋ค.
thread/start์ cwd vs spawn์ cwd ์ต์
๋ ๊ฐ์ง cwd๋ ๋ค๋ฅธ ๋ ์ด์ด์์ ๋์ํ๋ค:
| ์ค์ ๋ฐฉ๋ฒ | ๋ ์ด์ด | ์ญํ |
|---|---|---|
spawn(binary, args, { cwd: path }) | OS-level | codex ํ๋ก์ธ์ค์ ์์ ๋๋ ํ ๋ฆฌ |
thread/start { cwd: path } | JSON-RPC ํ๋ผ๋ฏธํฐ | ์คํฌ ๋์ค์ปค๋ฒ๋ฆฌ + ๋ช ๋ น ์คํ ๊ฒฝ๋ก |
spawn ์ต์
์ cwd๋ฅผ ์ค์ ํ๋ฉด codex ํ๋ก์ธ์ค์ OS-level ์์
๋๋ ํ ๋ฆฌ๊ฐ ๋ณ๊ฒฝ๋๋ค. thread/start์์ cwd๋ฅผ ์๋ตํ๋ฉด ํ๋ก์ธ์ค์ OS-level cwd๋ฅผ fallback์ผ๋ก ์ฌ์ฉํ ๊ฐ๋ฅ์ฑ์ด ์์ง๋ง, ์ด๋ ๋ฌธ์ํ๋ ๋์์ด ์๋ ๊ตฌํ ์ธ๋ถ์ฌํญ์ ์์กดํ๋ ๊ฒ์ด๋ค. thread/start์ cwd๋ฅผ ๋ช
์์ ์ผ๋ก ์ค์ ํ๋ ๊ฒ์ด ์์ ์ ์ด๋ค.
.codex/skills/ vs .agents/skills/ โ ์์ค์ฝ๋ ๊ธฐ๋ฐ ๋ถ์
์ค์ ์์ค์ฝ๋ (codex-rs/core/src/skills/loader.rs)
๋ ๋ค ์ค์บ๋๋ค. ๋จ, scope๋ณ๋ก ์ญํ ์ด ๋ค๋ฅด๋ค.
// ์์ ์ ์
const AGENTS_DIR_NAME: &str = ".agents";
const SKILLS_DIR_NAME: &str = "skills";User scope โ ~/.codex/skills/ (deprecated) + ~/.agents/skills/ (current)
ConfigLayerSource::User { .. } => {
// Deprecated user skills location (`$CODEX_HOME/skills`), kept for backward
// compatibility.
roots.push(SkillRoot {
path: config_folder.as_path().join(SKILLS_DIR_NAME), // ~/.codex/skills
scope: SkillScope::User,
});
// `$HOME/.agents/skills` (user-installed skills).
if let Some(home_dir) = home_dir {
roots.push(SkillRoot {
path: home_dir.join(AGENTS_DIR_NAME).join(SKILLS_DIR_NAME), // ~/.agents/skills
scope: SkillScope::User,
});
}
}Project scope โ {cwd}/.codex/skills/ (active) + {cwd~root}/.agents/skills/ (active)
ConfigLayerSource::Project { .. } => {
// deprecated ์ฃผ์ ์์ โ ์ ์ ๊ฒฝ๋ก
roots.push(SkillRoot {
path: config_folder.as_path().join(SKILLS_DIR_NAME), // {project}/.codex/skills
scope: SkillScope::Repo,
});
}์ถ๊ฐ๋ก repo_agents_skill_roots() ํจ์๊ฐ ํ๋ก์ ํธ ๋ฃจํธ๋ถํฐ cwd๊น์ง ๋ชจ๋ ๋๋ ํ ๋ฆฌ์์ .agents/skills/๋ฅผ ์ค์บํ๋ค:
fn repo_agents_skill_roots(config_layer_stack: &ConfigLayerStack, cwd: &Path) -> Vec<SkillRoot> {
let project_root = find_project_root(cwd, &project_root_markers);
let dirs = dirs_between_project_root_and_cwd(cwd, &project_root);
for dir in dirs {
let agents_skills = dir.join(AGENTS_DIR_NAME).join(SKILLS_DIR_NAME);
if agents_skills.is_dir() {
roots.push(SkillRoot { path: agents_skills, scope: SkillScope::Repo });
}
}
}์ ์ฒด ์ค์บ ๊ฒฝ๋ก (์ฐ์ ์์์)
| Scope | ๊ฒฝ๋ก | ์ํ | ์ฝ๋ ๊ทผ๊ฑฐ |
|---|---|---|---|
| Repo | {project}/.codex/skills/ | Active (deprecated ์๋) | ConfigLayerSource::Project |
| Repo | {root~cwd}/.agents/skills/ | Active (ํ์ฌ ๊ถ์ฅ) | repo_agents_skill_roots() |
| User | ~/.codex/skills/ | Deprecated (ํ์ ํธํ) | ConfigLayerSource::User + ์ฃผ์ |
| User | ~/.agents/skills/ | Active (ํ์ฌ ๊ถ์ฅ) | ConfigLayerSource::User + AGENTS_DIR_NAME |
| System | ~/.codex/skills/.system/ | Active (๋ฒ๋ค) | system_cache_root_dir() |
| Admin | /etc/codex/skills/ | Active | ConfigLayerSource::System |
ํต์ฌ: deprecated๋ User scope์ ~/.codex/skills/๋ง ํด๋น
์์ค์ฝ๋์ // Deprecated user skills location ($CODEX_HOME/skills) ์ฃผ์์ User scope์ ~/.codex/skills/์๋ง ์ ์ฉ๋๋ค. Project scope์ {project}/.codex/skills/๋ deprecated๊ฐ ์๋๋ฉฐ ์ ์์ ์ธ ํ๋ก์ ํธ ์ค์ ๊ฒฝ๋ก๋ค.
CODEX_HOME์ ์ ์ฌ์ฉํ์ง ์๋๊ฐ
CODEX_HOME์ ~/.codex ์ ์ฒด๋ฅผ ๋์ฒดํ๋ค. ์คํฌ๋ฟ ์๋๋ผ config, auth, DB, certs๊น์ง ๋ชจ๋ ์ํฅ๋ฐ๋๋ค.
| ํ๊ฒฝ๋ณ์ | ์ํฅ ๋ฒ์ | ์คํฌ ๊ฒฉ๋ฆฌ ์ ํฉ์ฑ |
|---|---|---|
HOME | OS ์ ์ฒด ํ ๋๋ ํ ๋ฆฌ | โ ๊ณผ๋ํจ |
CODEX_HOME | codex ์ ์ฒด ์ํ (config, auth, DB, certs, skills) | โ ๊ณผ๋ํจ |
cwd (thread/start) | PROJECT-level ์คํฌ ๊ฒฝ๋ก๋ง | โ ์ ํํ ์ ํฉ |
์์๋ก ๋ณด๋ HOME vs CODEX_HOME์ ์ฐจ์ด
์ฌ์ฉ์์ ์ค์ ํ๊ฒฝ์ด ์๋์ ๊ฐ๋ค๊ณ ๊ฐ์ ํ๋ค:
~/.codex/
โโโ config.toml โ ์ฌ์ฉ์ ์ค์ (๋ชจ๋ธ: o4-mini, sandbox: off ๋ฑ)
โโโ auth/
โ โโโ credentials.json โ ์ฌ์ฉ์์ OpenAI API ํค
โโโ db/
โ โโโ history.sqlite โ ๋ํ ํ์คํ ๋ฆฌ
โโโ log/
โ โโโ codex.log
โโโ skills/
โโโ study-guard/ โ ์ฌ์ฉ์์ ๊ธ๋ก๋ฒ ์คํฌ
๋ฐฉ๋ฒ 1: HOME์ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ
const child = spawn(codexBinaryPath, ["app-server"], {
env: { ...process.env, HOME: "/opt/service" }, // โ HOME ๋ณ๊ฒฝ
});๊ฒฐ๊ณผ:
| ํญ๋ชฉ | ๊ฒฝ๋ก | ์ํ |
|---|---|---|
| config.toml | /opt/service/.codex/config.toml | โ ์์ โ ์ฌ์ฉ์ ์ค์ ์ ์ค |
| auth | /opt/service/.codex/auth/ | โ ์์ โ API ํค ์ ์ค, ์ธ์ฆ ์คํจ |
| DB | /opt/service/.codex/db/ | โ ์์ โ ํ์คํ ๋ฆฌ ์ ์ค |
| ๊ธ๋ก๋ฒ ์คํฌ | /opt/service/.codex/skills/ | โ ์์ โ study-guard ์ ์ค |
| SSH/GPG ๋ฑ | /opt/service/.ssh/, .gnupg/ | โ OS ์ ์ฒด์ ์ํฅ |
HOME์ codex๋ฟ ์๋๋ผ OS ์ ์ฒด์ ํ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ฐ๊พธ๋ฏ๋ก, ์์ ํ๋ก์ธ์ค๊ฐ ์คํํ๋ ๋ชจ๋ ์
ธ ๋ช
๋ น(git, ssh ๋ฑ)์๋ ์ํฅ์ ์ค๋ค. ๋ชจ๋ ๊ฒ์ด ๊นจ์ง๋ค.
๋ฐฉ๋ฒ 2: CODEX_HOME์ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ
const child = spawn(codexBinaryPath, ["app-server"], {
env: { ...process.env, CODEX_HOME: "/opt/service/codex-state" }, // โ CODEX_HOME ๋ณ๊ฒฝ
});๊ฒฐ๊ณผ:
| ํญ๋ชฉ | ๊ฒฝ๋ก | ์ํ |
|---|---|---|
| config.toml | /opt/service/codex-state/config.toml | โ ์์ โ ์ฌ์ฉ์ ์ค์ ์ ์ค |
| auth | /opt/service/codex-state/auth/ | โ ์์ โ API ํค ์ ์ค, ์ธ์ฆ ์คํจ |
| DB | /opt/service/codex-state/db/ | โ ์์ โ ํ์คํ ๋ฆฌ ์ ์ค |
| ๊ธ๋ก๋ฒ ์คํฌ (CODEX_HOME ๊ธฐ๋ฐ) | /opt/service/codex-state/skills/ | โ ์์ โ study-guard ์ ์ค |
| SSH/GPG ๋ฑ | ~/.ssh/, ~/.gnupg/ | โ ์ํฅ ์์ |
HOME๋ณด๋ค๋ ๋ฒ์๊ฐ ์ข์ง๋ง(OS์๋ ์ํฅ ์์), codex์ ๋ชจ๋ ์ํ๋ฅผ ํต์งธ๋ก ๊ฒฉ๋ฆฌํ๋ค. ์คํฌ๋ง ๋ถ๋ฆฌํ๋ ค ํ๋๋ฐ config, auth, DB๊น์ง ๋ชจ๋ ์๋๋ค. ์์ค ์ฝ๋์์๋ CODEX_HOME์ config.toml, auth/, db/, log/, skills/ ๋ฑ codex ์ ์ฒด ์ํ ๋๋ ํ ๋ฆฌ์ ๋ฃจํธ๋ก ์ฌ์ฉ๋๋ค.
๋ฐฉ๋ฒ 3: cwd๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ (๊ถ์ฅ)
const child = spawn(codexBinaryPath, ["app-server"], {
env: { ...process.env }, // โ HOME ๊ทธ๋๋ก ์ ์ง
});
await peer.request("thread/start", {
cwd: "/opt/service/app-data", // โ cwd๋ง ์๋น์ค ๋๋ ํ ๋ฆฌ๋ก ์ค์
});๊ฒฐ๊ณผ:
| ํญ๋ชฉ | ๊ฒฝ๋ก | ์ํ |
|---|---|---|
| config.toml | ~/.codex/config.toml | โ ์ฌ์ฉ์ ์ค์ ์ ์ง |
| auth | ~/.codex/auth/ | โ API ํค ์ ์ง |
| DB | ~/.codex/db/ | โ ํ์คํ ๋ฆฌ ์ ์ง |
| ๊ธ๋ก๋ฒ ์คํฌ | ~/.agents/skills/study-guard/ | โ ์ ์ง |
| ์๋น์ค ์ ์ฉ ์คํฌ | /opt/service/app-data/.agents/skills/access-db/ | โ ์ถ๊ฐ ๋ฐ๊ฒฌ |
| SSH/GPG ๋ฑ | ~/.ssh/, ~/.gnupg/ | โ ์ํฅ ์์ |
๊ธฐ์กด ๋ชจ๋ ๊ฒ์ ์ ์งํ๋ฉด์, ์๋น์ค ์ ์ฉ ์คํฌ๋ง ์ถ๊ฐ๋๋ค. cwd๋ PROJECT-level ๊ฒฝ๋ก๋ง ๋ณ๊ฒฝํ๋ฏ๋ก USER-level ์ํ(config, auth, DB, ๊ธ๋ก๋ฒ ์คํฌ)์ ์ ํ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
ํ ๋์ ๋น๊ต
HOME ๋ณ๊ฒฝ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโ ๋ชจ๋ ๊ฒ์ด ๋ฐ๋ (OS + codex ์ ์ฒด)
CODEX_HOME ๋ณ๊ฒฝ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโ codex ์ ์ฒด ์ํ๊ฐ ๋ฐ๋ (config + auth + DB + skills)
cwd ๋ณ๊ฒฝ: โโโโโโโโโโโโโโโโโโโโโโโโโโโโ PROJECT-level ์คํฌ ๊ฒฝ๋ก๋ง ๋ฐ๋
โโโ OS โโโคโโโ codex ์ํ โโโคโโ ์คํฌ โโค