@kood/claude-code 0.7.11 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,9 +22,9 @@ var banner = () => {
22
22
  };
23
23
 
24
24
  // src/commands/init.ts
25
- import fs9 from "fs-extra";
25
+ import fs10 from "fs-extra";
26
26
  import os2 from "os";
27
- import path11 from "path";
27
+ import path12 from "path";
28
28
 
29
29
  // src/features/templates/template-path-resolver.ts
30
30
  import path2 from "path";
@@ -419,6 +419,38 @@ var checkAllExtrasExist = async (_templates) => {
419
419
  };
420
420
 
421
421
  // src/features/extras/extras-installer.ts
422
+ import fs7 from "fs-extra";
423
+ import path9 from "path";
424
+ async function registerSessionStartHook(targetDir) {
425
+ const settingsPath = path9.join(targetDir, ".claude", "settings.local.json");
426
+ const hookCommand = ".claude/hooks/session-env-setup.sh";
427
+ let config = {};
428
+ if (await fs7.pathExists(settingsPath)) {
429
+ try {
430
+ config = await fs7.readJson(settingsPath);
431
+ } catch {
432
+ config = {};
433
+ }
434
+ }
435
+ if (!config.hooks) {
436
+ config.hooks = {};
437
+ }
438
+ if (!config.hooks.SessionStart) {
439
+ config.hooks.SessionStart = [];
440
+ }
441
+ const alreadyRegistered = config.hooks.SessionStart.some(
442
+ (hook) => hook.type === "command" && hook.command === hookCommand
443
+ );
444
+ if (!alreadyRegistered) {
445
+ config.hooks.SessionStart.push({
446
+ type: "command",
447
+ command: hookCommand
448
+ });
449
+ await fs7.ensureDir(path9.dirname(settingsPath));
450
+ await fs7.writeJson(settingsPath, config, { spaces: 2 });
451
+ logger.step("Registered SessionStart hook for CLAUDE_SCRIPTS_ROOT");
452
+ }
453
+ }
422
454
  function logExistingFilesUpdate(existingClaudeFiles) {
423
455
  if (existingClaudeFiles.length > 0) {
424
456
  logger.info("Updating existing extras:");
@@ -571,6 +603,9 @@ async function installExtras(templates, targetDir, flags, availability) {
571
603
  installHooks,
572
604
  availability.hasHooks
573
605
  );
606
+ if (installHooks && hooksResult.files > 0) {
607
+ await registerSessionStartHook(targetDir);
608
+ }
574
609
  const totalFiles = skillsResult.files + commandsResult.files + agentsResult.files + instructionsResult.files + scriptsResult.files + hooksResult.files;
575
610
  const totalDirectories = skillsResult.directories + commandsResult.directories + agentsResult.directories + instructionsResult.directories + scriptsResult.directories + hooksResult.directories;
576
611
  return { files: totalFiles, directories: totalDirectories };
@@ -748,8 +783,8 @@ async function promptCodexSync(options) {
748
783
  }
749
784
 
750
785
  // src/shared/gitignore-manager.ts
751
- import fs7 from "fs-extra";
752
- import path9 from "path";
786
+ import fs8 from "fs-extra";
787
+ import path10 from "path";
753
788
  var CLAUDE_GENERATED_FOLDERS = [
754
789
  ".claude/brainstorms/",
755
790
  ".claude/crawler/",
@@ -778,13 +813,13 @@ function parseIgnoreLine(line) {
778
813
  }
779
814
  async function updateGitignore(targetDir, options = {}) {
780
815
  const { includeCodex = false } = options;
781
- const gitignorePath = path9.join(targetDir, ".gitignore");
816
+ const gitignorePath = path10.join(targetDir, ".gitignore");
782
817
  const sectionComment = "# Claude Code generated files";
783
818
  const targetFolders = includeCodex ? [...CLAUDE_GENERATED_FOLDERS, ...CODEX_GENERATED_FOLDERS] : CLAUDE_GENERATED_FOLDERS;
784
819
  let content = "";
785
820
  let hasGitignore = false;
786
821
  try {
787
- content = await fs7.readFile(gitignorePath, "utf-8");
822
+ content = await fs8.readFile(gitignorePath, "utf-8");
788
823
  hasGitignore = true;
789
824
  } catch {
790
825
  content = "";
@@ -817,7 +852,7 @@ async function updateGitignore(targetDir, options = {}) {
817
852
  if (!newContent.endsWith(eol)) {
818
853
  newContent += eol;
819
854
  }
820
- await fs7.writeFile(gitignorePath, newContent, "utf-8");
855
+ await fs8.writeFile(gitignorePath, newContent, "utf-8");
821
856
  if (hasGitignore) {
822
857
  logger.success(`.gitignore updated with ${linesToAdd.length} patterns`);
823
858
  } else {
@@ -826,9 +861,9 @@ async function updateGitignore(targetDir, options = {}) {
826
861
  }
827
862
 
828
863
  // src/features/codex-sync/codex-sync.ts
829
- import fs8 from "fs-extra";
864
+ import fs9 from "fs-extra";
830
865
  import os from "os";
831
- import path10 from "path";
866
+ import path11 from "path";
832
867
  var RESERVED_CODEX_SKILL_DIRS = /* @__PURE__ */ new Set([".system", "claude-commands"]);
833
868
  var COMMAND_INSTRUCTION_PREFIX_REGEX = /^@\.\.\/instructions\//gm;
834
869
  var COMMAND_INSTRUCTION_PREFIX_REPLACEMENT = "@../../../instructions/";
@@ -862,13 +897,13 @@ function extractDescriptionFromFrontmatter(markdown) {
862
897
  return void 0;
863
898
  }
864
899
  function buildCommandSkillContent(commandPath, commandRaw) {
865
- const commandName = path10.basename(commandPath, ".md");
900
+ const commandName = path11.basename(commandPath, ".md");
866
901
  const normalizedCommand = normalizeLineEndings(commandRaw).trim();
867
902
  const rewrittenCommand = normalizedCommand.replace(
868
903
  COMMAND_INSTRUCTION_PREFIX_REGEX,
869
904
  COMMAND_INSTRUCTION_PREFIX_REPLACEMENT
870
905
  );
871
- const sourcePath = path10.resolve(commandPath);
906
+ const sourcePath = path11.resolve(commandPath);
872
907
  const description = extractDescriptionFromFrontmatter(normalizedCommand) || `${commandName} command imported from .claude/commands`;
873
908
  return `---
874
909
  name: ${yamlQuote(commandName)}
@@ -946,9 +981,9 @@ function getProjectState(claudeState, targetDir) {
946
981
  if (!isRecord(projects)) {
947
982
  return void 0;
948
983
  }
949
- const normalizedTarget = path10.resolve(targetDir);
984
+ const normalizedTarget = path11.resolve(targetDir);
950
985
  for (const [projectPath, projectState] of Object.entries(projects)) {
951
- if (path10.resolve(projectPath) !== normalizedTarget) {
986
+ if (path11.resolve(projectPath) !== normalizedTarget) {
952
987
  continue;
953
988
  }
954
989
  if (isRecord(projectState)) {
@@ -979,11 +1014,11 @@ function filterMcpJsonServersByProjectState(servers, projectState) {
979
1014
  return Object.fromEntries(filteredEntries);
980
1015
  }
981
1016
  async function readJsonFile(filePath) {
982
- if (!await fs8.pathExists(filePath)) {
1017
+ if (!await fs9.pathExists(filePath)) {
983
1018
  return void 0;
984
1019
  }
985
1020
  try {
986
- const raw = await fs8.readFile(filePath, "utf8");
1021
+ const raw = await fs9.readFile(filePath, "utf8");
987
1022
  const parsed = JSON.parse(raw);
988
1023
  if (!isRecord(parsed)) {
989
1024
  return void 0;
@@ -1160,10 +1195,10 @@ function mergeCodexConfigToml(existingToml, renderedMcpBlocks) {
1160
1195
  ${mcp}`;
1161
1196
  }
1162
1197
  async function loadClaudeMcpServers(targetDir) {
1163
- const homeDir = path10.resolve(os.homedir());
1164
- const normalizedTarget = path10.resolve(targetDir);
1198
+ const homeDir = path11.resolve(os.homedir());
1199
+ const normalizedTarget = path11.resolve(targetDir);
1165
1200
  const isUserScope = normalizedTarget === homeDir;
1166
- const claudeStatePath = path10.join(homeDir, CLAUDE_STATE_FILE);
1201
+ const claudeStatePath = path11.join(homeDir, CLAUDE_STATE_FILE);
1167
1202
  const claudeState = await readJsonFile(claudeStatePath);
1168
1203
  if (isUserScope) {
1169
1204
  return {
@@ -1173,7 +1208,7 @@ async function loadClaudeMcpServers(targetDir) {
1173
1208
  }
1174
1209
  const projectState = claudeState ? getProjectState(claudeState, normalizedTarget) : void 0;
1175
1210
  const projectServers = normalizeClaudeMcpServers(projectState?.mcpServers);
1176
- const localMcpPath = path10.join(normalizedTarget, CLAUDE_LOCAL_MCP_FILE);
1211
+ const localMcpPath = path11.join(normalizedTarget, CLAUDE_LOCAL_MCP_FILE);
1177
1212
  const localMcpState = await readJsonFile(localMcpPath);
1178
1213
  const localMcpServers = filterMcpJsonServersByProjectState(
1179
1214
  normalizeClaudeMcpServers(localMcpState?.mcpServers),
@@ -1188,18 +1223,18 @@ async function loadClaudeMcpServers(targetDir) {
1188
1223
  };
1189
1224
  }
1190
1225
  async function syncMcpServers(targetDir) {
1191
- const homeDir = path10.resolve(os.homedir());
1192
- const normalizedTarget = path10.resolve(targetDir);
1226
+ const homeDir = path11.resolve(os.homedir());
1227
+ const normalizedTarget = path11.resolve(targetDir);
1193
1228
  const isUserScope = normalizedTarget === homeDir;
1194
- const codexBaseDir = isUserScope ? path10.join(homeDir, ".codex") : path10.join(normalizedTarget, ".codex");
1195
- const codexMcpConfigPath = path10.join(codexBaseDir, CODEX_CONFIG_FILE);
1229
+ const codexBaseDir = isUserScope ? path11.join(homeDir, ".codex") : path11.join(normalizedTarget, ".codex");
1230
+ const codexMcpConfigPath = path11.join(codexBaseDir, CODEX_CONFIG_FILE);
1196
1231
  const { scope, servers } = await loadClaudeMcpServers(normalizedTarget);
1197
1232
  const codexServers = Object.entries(servers).map(([name, server]) => [name, toCodexMcpServer(server)]).filter((entry) => Boolean(entry[1])).sort((a, b) => a[0].localeCompare(b[0]));
1198
1233
  const renderedMcpBlocks = codexServers.map(([name, server]) => renderCodexMcpServer(name, server)).join("\n\n");
1199
- const existingConfig = await fs8.pathExists(codexMcpConfigPath) ? await fs8.readFile(codexMcpConfigPath, "utf8") : "";
1234
+ const existingConfig = await fs9.pathExists(codexMcpConfigPath) ? await fs9.readFile(codexMcpConfigPath, "utf8") : "";
1200
1235
  const nextConfig = mergeCodexConfigToml(existingConfig, renderedMcpBlocks);
1201
- await fs8.ensureDir(codexBaseDir);
1202
- await fs8.writeFile(codexMcpConfigPath, `${nextConfig.trimEnd()}
1236
+ await fs9.ensureDir(codexBaseDir);
1237
+ await fs9.writeFile(codexMcpConfigPath, `${nextConfig.trimEnd()}
1203
1238
  `);
1204
1239
  return {
1205
1240
  codexMcpConfigPath,
@@ -1208,16 +1243,16 @@ async function syncMcpServers(targetDir) {
1208
1243
  };
1209
1244
  }
1210
1245
  async function syncSkills(claudeSkillsDir, codexSkillsDir) {
1211
- await fs8.ensureDir(codexSkillsDir);
1246
+ await fs9.ensureDir(codexSkillsDir);
1212
1247
  let sourceSkillNames = [];
1213
- if (await fs8.pathExists(claudeSkillsDir)) {
1214
- const sourceEntries = await fs8.readdir(claudeSkillsDir, {
1248
+ if (await fs9.pathExists(claudeSkillsDir)) {
1249
+ const sourceEntries = await fs9.readdir(claudeSkillsDir, {
1215
1250
  withFileTypes: true
1216
1251
  });
1217
1252
  sourceSkillNames = sourceEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => name !== ".system");
1218
1253
  }
1219
1254
  const sourceSkillSet = new Set(sourceSkillNames);
1220
- const codexEntries = await fs8.readdir(codexSkillsDir, {
1255
+ const codexEntries = await fs9.readdir(codexSkillsDir, {
1221
1256
  withFileTypes: true
1222
1257
  });
1223
1258
  for (const entry of codexEntries) {
@@ -1228,40 +1263,40 @@ async function syncSkills(claudeSkillsDir, codexSkillsDir) {
1228
1263
  continue;
1229
1264
  }
1230
1265
  if (!sourceSkillSet.has(entry.name)) {
1231
- await fs8.remove(path10.join(codexSkillsDir, entry.name));
1266
+ await fs9.remove(path11.join(codexSkillsDir, entry.name));
1232
1267
  }
1233
1268
  }
1234
1269
  for (const skillName of sourceSkillNames) {
1235
- const src = path10.join(claudeSkillsDir, skillName);
1236
- const dest = path10.join(codexSkillsDir, skillName);
1237
- if (await fs8.pathExists(dest)) {
1238
- await fs8.remove(dest);
1270
+ const src = path11.join(claudeSkillsDir, skillName);
1271
+ const dest = path11.join(codexSkillsDir, skillName);
1272
+ if (await fs9.pathExists(dest)) {
1273
+ await fs9.remove(dest);
1239
1274
  }
1240
1275
  await copyRecursive(src, dest, { files: 0, directories: 0 });
1241
1276
  }
1242
1277
  return sourceSkillNames.length;
1243
1278
  }
1244
1279
  async function syncCommands(claudeCommandsDir, codexCommandsDir) {
1245
- if (!await fs8.pathExists(claudeCommandsDir)) {
1246
- await fs8.remove(codexCommandsDir);
1280
+ if (!await fs9.pathExists(claudeCommandsDir)) {
1281
+ await fs9.remove(codexCommandsDir);
1247
1282
  return 0;
1248
1283
  }
1249
- const entries = await fs8.readdir(claudeCommandsDir, { withFileTypes: true });
1284
+ const entries = await fs9.readdir(claudeCommandsDir, { withFileTypes: true });
1250
1285
  const commandFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
1251
1286
  if (commandFiles.length === 0) {
1252
- await fs8.remove(codexCommandsDir);
1287
+ await fs9.remove(codexCommandsDir);
1253
1288
  return 0;
1254
1289
  }
1255
- await fs8.remove(codexCommandsDir);
1256
- await fs8.ensureDir(codexCommandsDir);
1290
+ await fs9.remove(codexCommandsDir);
1291
+ await fs9.ensureDir(codexCommandsDir);
1257
1292
  for (const commandFile of commandFiles) {
1258
- const commandPath = path10.join(claudeCommandsDir, commandFile);
1259
- const commandRaw = await fs8.readFile(commandPath, "utf8");
1260
- const commandName = path10.basename(commandFile, ".md");
1261
- const skillDir = path10.join(codexCommandsDir, commandName);
1262
- const skillPath = path10.join(skillDir, "SKILL.md");
1263
- await fs8.ensureDir(skillDir);
1264
- await fs8.writeFile(
1293
+ const commandPath = path11.join(claudeCommandsDir, commandFile);
1294
+ const commandRaw = await fs9.readFile(commandPath, "utf8");
1295
+ const commandName = path11.basename(commandFile, ".md");
1296
+ const skillDir = path11.join(codexCommandsDir, commandName);
1297
+ const skillPath = path11.join(skillDir, "SKILL.md");
1298
+ await fs9.ensureDir(skillDir);
1299
+ await fs9.writeFile(
1265
1300
  skillPath,
1266
1301
  buildCommandSkillContent(commandPath, commandRaw)
1267
1302
  );
@@ -1269,12 +1304,12 @@ async function syncCommands(claudeCommandsDir, codexCommandsDir) {
1269
1304
  return commandFiles.length;
1270
1305
  }
1271
1306
  async function syncInstructions(claudeInstructionsDir, codexInstructionsDir) {
1272
- if (!await fs8.pathExists(claudeInstructionsDir)) {
1273
- await fs8.remove(codexInstructionsDir);
1307
+ if (!await fs9.pathExists(claudeInstructionsDir)) {
1308
+ await fs9.remove(codexInstructionsDir);
1274
1309
  return 0;
1275
1310
  }
1276
- await fs8.remove(codexInstructionsDir);
1277
- await fs8.ensureDir(codexInstructionsDir);
1311
+ await fs9.remove(codexInstructionsDir);
1312
+ await fs9.ensureDir(codexInstructionsDir);
1278
1313
  const counter = { files: 0, directories: 0 };
1279
1314
  await copyRecursive(claudeInstructionsDir, codexInstructionsDir, counter);
1280
1315
  return counter.files;
@@ -1295,12 +1330,12 @@ function extractReferenceCandidate(line) {
1295
1330
  return candidate;
1296
1331
  }
1297
1332
  async function collectSkillMarkdownFiles(rootDir, collector = []) {
1298
- if (!await fs8.pathExists(rootDir)) {
1333
+ if (!await fs9.pathExists(rootDir)) {
1299
1334
  return collector;
1300
1335
  }
1301
- const entries = await fs8.readdir(rootDir, { withFileTypes: true });
1336
+ const entries = await fs9.readdir(rootDir, { withFileTypes: true });
1302
1337
  for (const entry of entries) {
1303
- const fullPath = path10.join(rootDir, entry.name);
1338
+ const fullPath = path11.join(rootDir, entry.name);
1304
1339
  if (entry.isDirectory()) {
1305
1340
  await collectSkillMarkdownFiles(fullPath, collector);
1306
1341
  continue;
@@ -1316,7 +1351,7 @@ async function validateSkillReferences(codexSkillsDir) {
1316
1351
  let count = 0;
1317
1352
  const samples = [];
1318
1353
  for (const skillFile of skillFiles) {
1319
- const content = await fs8.readFile(skillFile, "utf8");
1354
+ const content = await fs9.readFile(skillFile, "utf8");
1320
1355
  const lines = normalizeLineEndings(content).split("\n");
1321
1356
  for (const line of lines) {
1322
1357
  const candidate = extractReferenceCandidate(line);
@@ -1327,8 +1362,8 @@ async function validateSkillReferences(codexSkillsDir) {
1327
1362
  if (!referencePath) {
1328
1363
  continue;
1329
1364
  }
1330
- const resolvedPath = path10.resolve(path10.dirname(skillFile), referencePath);
1331
- if (await fs8.pathExists(resolvedPath)) {
1365
+ const resolvedPath = path11.resolve(path11.dirname(skillFile), referencePath);
1366
+ if (await fs9.pathExists(resolvedPath)) {
1332
1367
  continue;
1333
1368
  }
1334
1369
  count += 1;
@@ -1344,15 +1379,15 @@ async function validateSkillReferences(codexSkillsDir) {
1344
1379
  return { count, samples };
1345
1380
  }
1346
1381
  async function syncWithCodex(targetDir) {
1347
- const codexHome = path10.resolve(targetDir, ".codex");
1348
- const codexSkillsDir = path10.join(codexHome, "skills");
1349
- const codexCommandsDir = path10.join(codexSkillsDir, "claude-commands");
1350
- const codexInstructionsDir = path10.join(codexHome, "instructions");
1351
- await fs8.ensureDir(codexSkillsDir);
1352
- const claudeRootDir = path10.join(targetDir, ".claude");
1353
- const claudeSkillsDir = path10.join(claudeRootDir, "skills");
1354
- const claudeCommandsDir = path10.join(claudeRootDir, "commands");
1355
- const claudeInstructionsDir = path10.join(claudeRootDir, "instructions");
1382
+ const codexHome = path11.resolve(targetDir, ".codex");
1383
+ const codexSkillsDir = path11.join(codexHome, "skills");
1384
+ const codexCommandsDir = path11.join(codexSkillsDir, "claude-commands");
1385
+ const codexInstructionsDir = path11.join(codexHome, "instructions");
1386
+ await fs9.ensureDir(codexSkillsDir);
1387
+ const claudeRootDir = path11.join(targetDir, ".claude");
1388
+ const claudeSkillsDir = path11.join(claudeRootDir, "skills");
1389
+ const claudeCommandsDir = path11.join(claudeRootDir, "commands");
1390
+ const claudeInstructionsDir = path11.join(claudeRootDir, "instructions");
1356
1391
  const syncedSkills = await syncSkills(claudeSkillsDir, codexSkillsDir);
1357
1392
  const syncedInstructions = await syncInstructions(
1358
1393
  claudeInstructionsDir,
@@ -1393,7 +1428,7 @@ var TEMPLATE_DESCRIPTIONS = {
1393
1428
  };
1394
1429
  async function validateTargetDirectory(targetDir) {
1395
1430
  try {
1396
- const stat = await fs9.stat(targetDir);
1431
+ const stat = await fs10.stat(targetDir);
1397
1432
  if (!stat.isDirectory()) {
1398
1433
  logger.error(`Target is not a directory: ${targetDir}`);
1399
1434
  process.exit(1);
@@ -1407,7 +1442,7 @@ async function validateTargetDirectory(targetDir) {
1407
1442
  process.exit(1);
1408
1443
  }
1409
1444
  try {
1410
- await fs9.access(targetDir, fs9.constants.W_OK);
1445
+ await fs10.access(targetDir, fs10.constants.W_OK);
1411
1446
  } catch {
1412
1447
  logger.error(`No write permission for: ${targetDir}`);
1413
1448
  process.exit(1);
@@ -1596,7 +1631,7 @@ var init = async (options) => {
1596
1631
  hasHooks,
1597
1632
  scope
1598
1633
  );
1599
- const codexSyncPath = path11.join(targetDir, ".codex");
1634
+ const codexSyncPath = path12.join(targetDir, ".codex");
1600
1635
  const { syncCodex } = await promptCodexSync({
1601
1636
  providedSyncCodex: options.syncCodex,
1602
1637
  codexPath: codexSyncPath
@@ -1632,8 +1667,8 @@ var init = async (options) => {
1632
1667
  `Codex skill reference issues found: ${result.referenceIssueCount} (showing up to ${result.referenceIssueSamples.length})`
1633
1668
  );
1634
1669
  for (const issue of result.referenceIssueSamples) {
1635
- const skillPath = path11.relative(targetDir, issue.skillPath);
1636
- const resolvedPath = path11.relative(targetDir, issue.resolvedPath);
1670
+ const skillPath = path12.relative(targetDir, issue.skillPath);
1671
+ const resolvedPath = path12.relative(targetDir, issue.resolvedPath);
1637
1672
  logger.step(
1638
1673
  `${skillPath} -> @${issue.reference} (missing: ${resolvedPath})`
1639
1674
  );
@@ -1659,7 +1694,7 @@ var init = async (options) => {
1659
1694
 
1660
1695
  // src/index.ts
1661
1696
  var program = new Command();
1662
- program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.11");
1697
+ program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.12");
1663
1698
  program.option(
1664
1699
  "-t, --template <names>",
1665
1700
  "template names (comma-separated: tanstack-start,hono)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.7.11",
3
+ "version": "0.7.12",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -1,44 +1,41 @@
1
1
  ---
2
2
  name: codex
3
3
  description: OpenAI Codex MCP 연동 에이전트. 꼼꼼한 구현, 코드 리뷰, 엣지케이스 검증 담당. Agent Teams에서 Team Lead 역할 우선.
4
- tools: Read, Write, Edit, Grep, Glob, Bash, mcp__codex__codex, mcp__codex__codex_reply
4
+ tools: Read, Write, Edit, Grep, Glob, Bash, mcp__codex__codex, mcp__codex__codex_reply, mcp__codex__codex_review
5
5
  model: sonnet
6
6
  permissionMode: default
7
7
  maxTurns: 50
8
8
  ---
9
9
 
10
- @../../instructions/agent-patterns/parallel-execution.md
11
- @../../instructions/agent-patterns/read-parallelization.md
12
10
  @../../instructions/agent-patterns/agent-teams-usage.md
13
11
  @../../instructions/validation/forbidden-patterns.md
14
12
  @../../instructions/validation/required-behaviors.md
15
13
 
16
14
  # Codex Agent
17
15
 
18
- codex-mcp 서버로 OpenAI Responses API 호출. Claude와 페어 프로그래밍.
16
+ codex-mcp Codex CLI 호출. Claude와 페어 프로그래밍.
19
17
 
20
- **역할:** Agent Teams **Team Lead** (기본), 구현, 리뷰, 테스트, 디버깅
18
+ **역할:** Agent Teams **Team Lead**, 구현, 리뷰, 테스트
21
19
 
22
20
  ---
23
21
 
24
22
  <team_lead>
25
23
 
26
- ## Team Lead
24
+ ## Team Lead 역할
27
25
 
28
26
  | 역할 | 이유 |
29
27
  |------|------|
30
- | 태스크 분해 | 꼼꼼한 분할, 누락 방지 |
31
- | 품질 게이트 | 코드 품질/테스트 검증 |
32
- | 진행 관리 | 병목 감지 |
28
+ | 태스크 분해 | 꼼꼼한 분할 |
29
+ | 품질 게이트 | 코드/테스트 검증 |
33
30
  | 충돌 조율 | 파일 충돌 방지 |
34
31
 
35
32
  ```typescript
36
33
  TeamCreate({ team_name: "project", agent_type: "codex" })
37
- Task({ subagent_type: 'implementation-executor', team_name: 'project', name: 'claude-impl', prompt: '...' })
34
+ Task({ subagent_type: 'implementation-executor', team_name: 'project', name: 'impl', prompt: '...' })
38
35
  // 품질 검증
39
- mcp__codex__codex_reply({ thread_id: "...", prompt: "코드 리뷰: 보안, 성능, 엣지케이스" })
36
+ mcp__codex__codex_review({ uncommitted: true })
40
37
  // 정리
41
- SendMessage({ type: 'shutdown_request', recipient: 'claude-impl' })
38
+ SendMessage({ type: 'shutdown_request', recipient: 'impl' })
42
39
  TeamDelete()
43
40
  ```
44
41
 
@@ -46,43 +43,28 @@ TeamDelete()
46
43
 
47
44
  ---
48
45
 
49
- <codex_mcp_usage>
46
+ <tools>
50
47
 
51
48
  ## MCP 도구
52
49
 
53
- **인증:** Codex CLI OAuth (`~/.codex/auth.json`), 토큰 자동 갱신
54
-
55
- ### 새 태스크
56
-
57
- ```typescript
58
- mcp__codex__codex({
59
- prompt: "구현 요구사항 + 품질 기준",
60
- working_directory: "/path/to/project",
61
- model: "gpt-5.3-codex high" // 선택 (세션에 저장, 생략 시 Codex CLI 기본값)
62
- })
63
- // → JSON { thread_id, result }
64
- ```
65
-
66
- **에이전트 루프:** `read_file`, `write_file`, `list_files`, `shell_exec` 자율 호출
67
-
68
- ### 세션 이어가기
50
+ | 도구 | 용도 |
51
+ |------|------|
52
+ | `codex` | 새 태스크 (세션 생성) |
53
+ | `codex_reply` | 멀티턴 대화 |
54
+ | `codex_review` | 코드 리뷰 (uncommitted/branch/commit) |
69
55
 
70
56
  ```typescript
71
- mcp__codex__codex_reply({
72
- thread_id: "이전 thread_id",
73
- prompt: "후속 지시"
74
- })
75
- // 세션 모델 자동 유지, 이전 컨텍스트 보존
76
- ```
57
+ // 구현
58
+ const r = mcp__codex__codex({ prompt: "기능 구현", working_directory: cwd })
77
59
 
78
- ### 리뷰 패턴
60
+ // 후속 작업
61
+ mcp__codex__codex_reply({ thread_id: r.thread_id, prompt: "테스트 추가" })
79
62
 
80
- ```typescript
81
- const r = mcp__codex__codex({ prompt: "src/auth/ 분석", working_directory: cwd })
82
- mcp__codex__codex_reply({ thread_id: r.thread_id, prompt: "보안, 엣지케이스, 성능 리뷰" })
63
+ // 리뷰
64
+ mcp__codex__codex_review({ uncommitted: true })
83
65
  ```
84
66
 
85
- </codex_mcp_usage>
67
+ </tools>
86
68
 
87
69
  ---
88
70
 
@@ -90,54 +72,32 @@ mcp__codex__codex_reply({ thread_id: r.thread_id, prompt: "보안, 엣지케이
90
72
 
91
73
  ## 작업 흐름
92
74
 
93
- **구현:** Read(파일 파악) → codex(구현) → Bash(테스트) → Edit(미세 조정)
75
+ **구현:** Read → codex → Bash(테스트) → Edit(미세 조정)
94
76
 
95
- **리뷰:** Bash(git diff) codex(diff 분석) → codex_reply(리뷰) → 치명적/경고/제안 분류
96
-
97
- **설계 구현:** Read(설계 문서) → codex(설계 기반 구현) → Bash(typecheck + test)
77
+ **리뷰:** codex_review → 치명적/경고/제안 분류
98
78
 
99
79
  </workflow>
100
80
 
101
81
  ---
102
82
 
103
- <collaboration>
104
-
105
- ## Claude 협업
106
-
107
- | 상황 | Codex 역할 |
108
- |------|-----------|
109
- | Claude 설계 후 | 구현 + 엣지케이스 추가 |
110
- | Claude 구현 후 | 리뷰 + 개선 제안 |
111
- | 병렬 작업 | 백엔드/테스트/리뷰 |
112
- | 의견 분기 | 꼼꼼한 관점 제시, 최종 결정은 사용자 |
113
-
114
- </collaboration>
115
-
116
- ---
117
-
118
83
  <rules>
119
84
 
120
- ## 필수 / 금지
121
-
122
85
  | 필수 | 금지 |
123
86
  |------|------|
124
- | codex 또는 codex_reply 사용 | MCP 없이 시뮬레이션 |
125
- | 구현 후 테스트 실행 | 테스트 없이 완료 선언 |
126
- | 모든 경계 조건 처리 | Claude 영역 침범 |
127
- | 구조화된 결과 보고 | 충돌 시 임의 결정 |
87
+ | codex MCP 도구 사용 | MCP 없이 시뮬레이션 |
88
+ | 구현 후 테스트 실행 | 테스트 없이 완료 |
89
+ | 경계 조건 처리 | Claude 영역 침범 |
128
90
 
129
91
  </rules>
130
92
 
131
93
  ---
132
94
 
133
- <error_handling>
95
+ <errors>
134
96
 
135
97
  | 에러 | 대응 |
136
98
  |------|------|
137
- | MCP 연결 실패 | 설정 OpenAI/Codex 등록 안내 |
138
- | 401 | `codex auth login` 안내 |
139
- | 타임아웃 | 재시도 |
99
+ | 401 | `codex login` 안내 |
140
100
  | 세션 not found | 새 codex 세션 |
141
- | 동시 요청 에러 | 이전 요청 완료 대기 |
101
+ | 동시 요청 | 이전 요청 완료 대기 |
142
102
 
143
- </error_handling>
103
+ </errors>
@@ -14,11 +14,11 @@ description: 모든 변경사항 커밋 후 푸시
14
14
 
15
15
  | 스크립트 | 용도 |
16
16
  |----------|------|
17
- | `.claude/scripts/git/git-info.sh` | 상태 + diff 요약 출력 |
18
- | `.claude/scripts/git/git-commit.sh "msg" [files]` | Co-Authored-By 포함 커밋 |
19
- | `.claude/scripts/git/git-push.sh` | 안전한 푸시 |
20
- | `.claude/scripts/git/git-all.sh "msg"` | add all + commit + push (단순 케이스) |
21
- | `.claude/scripts/git/git-clean-check.sh` | clean 여부 확인 |
17
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh` | 상태 + diff 요약 출력 |
18
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "msg" [files]` | Co-Authored-By 포함 커밋 |
19
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-push.sh` | 안전한 푸시 |
20
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-all.sh "msg"` | add all + commit + push (단순 케이스) |
21
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-clean-check.sh` | clean 여부 확인 |
22
22
 
23
23
  </scripts>
24
24
 
@@ -32,25 +32,25 @@ description: 모든 변경사항 커밋 후 푸시
32
32
 
33
33
  ```bash
34
34
  # 1. 상태 확인
35
- .claude/scripts/git/git-info.sh
35
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
36
36
 
37
37
  # 2. 모든 변경사항 커밋 + 푸시
38
- .claude/scripts/git/git-all.sh "feat: 기능 추가"
38
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-all.sh "feat: 기능 추가"
39
39
  ```
40
40
 
41
41
  ### 복잡한 케이스 (논리적 분리 필요)
42
42
 
43
43
  ```bash
44
44
  # 1. 상태 확인
45
- .claude/scripts/git/git-info.sh
45
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
46
46
 
47
47
  # 2. 그룹별 커밋
48
- .claude/scripts/git/git-commit.sh "feat: A 기능" src/a.ts src/a.test.ts
49
- .claude/scripts/git/git-commit.sh "fix: B 버그 수정" src/b.ts
48
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "feat: A 기능" src/a.ts src/a.test.ts
49
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "fix: B 버그 수정" src/b.ts
50
50
 
51
51
  # 3. clean 확인 후 푸시
52
- .claude/scripts/git/git-clean-check.sh
53
- .claude/scripts/git/git-push.sh
52
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-clean-check.sh
53
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-push.sh
54
54
  ```
55
55
 
56
56
  </workflow>
@@ -15,7 +15,7 @@ argument-hint: <target-branch> <source-branch>
15
15
 
16
16
  | 스크립트 | 용도 |
17
17
  |----------|------|
18
- | `.claude/scripts/git/git-merge.sh <target> <source>` | checkout → merge → push → 원래 브랜치 복귀 |
18
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-merge.sh <target> <source>` | checkout → merge → push → 원래 브랜치 복귀 |
19
19
 
20
20
  </scripts>
21
21
 
@@ -27,7 +27,7 @@ argument-hint: <target-branch> <source-branch>
27
27
 
28
28
  ```bash
29
29
  # 인자에서 타겟/소스 브랜치 추출 후 실행
30
- .claude/scripts/git/git-merge.sh <target-branch> <source-branch>
30
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-merge.sh <target-branch> <source-branch>
31
31
  ```
32
32
 
33
33
  **스크립트 동작:**
@@ -46,10 +46,10 @@ argument-hint: <target-branch> <source-branch>
46
46
 
47
47
  ```bash
48
48
  # deploy/prod에 dev 머지
49
- .claude/scripts/git/git-merge.sh deploy/prod dev
49
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-merge.sh deploy/prod dev
50
50
 
51
51
  # main에 feature/auth 머지
52
- .claude/scripts/git/git-merge.sh main feature/auth
52
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-merge.sh main feature/auth
53
53
  ```
54
54
 
55
55
  </examples>
@@ -14,10 +14,10 @@ description: 현재 세션에서 수정한 파일만 커밋 후 푸시
14
14
 
15
15
  | 스크립트 | 용도 |
16
16
  |----------|------|
17
- | `.claude/scripts/git/git-info.sh` | 상태 + diff 요약 출력 |
18
- | `.claude/scripts/git/git-commit.sh "msg" [files]` | Co-Authored-By 포함 커밋 |
19
- | `.claude/scripts/git/git-push.sh` | 안전한 푸시 |
20
- | `.claude/scripts/git/git-clean-check.sh` | clean 여부 확인 |
17
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh` | 상태 + diff 요약 출력 |
18
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "msg" [files]` | Co-Authored-By 포함 커밋 |
19
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-push.sh` | 안전한 푸시 |
20
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-clean-check.sh` | clean 여부 확인 |
21
21
 
22
22
  </scripts>
23
23
 
@@ -29,13 +29,13 @@ description: 현재 세션에서 수정한 파일만 커밋 후 푸시
29
29
 
30
30
  ```bash
31
31
  # 1. 상태 확인
32
- .claude/scripts/git/git-info.sh
32
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
33
33
 
34
34
  # 2. 현재 세션 파일만 선택하여 커밋
35
- .claude/scripts/git/git-commit.sh "feat: 로그인 기능" src/auth/login.ts src/auth/logout.ts
35
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "feat: 로그인 기능" src/auth/login.ts src/auth/logout.ts
36
36
 
37
37
  # 3. 푸시
38
- .claude/scripts/git/git-push.sh
38
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-push.sh
39
39
  ```
40
40
 
41
41
  </workflow>
@@ -70,14 +70,14 @@ description: 현재 세션에서 수정한 파일만 커밋 후 푸시
70
70
  ```bash
71
71
  # 상황: 로그인 기능 작업 중, 이전 프로필 기능은 미완성
72
72
 
73
- .claude/scripts/git/git-info.sh
73
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
74
74
  # modified: src/auth/login.ts (현재 세션)
75
75
  # modified: src/auth/logout.ts (현재 세션)
76
76
  # modified: src/profile/edit.ts (이전 세션)
77
77
 
78
78
  # ✅ 로그인 관련만 커밋
79
- .claude/scripts/git/git-commit.sh "feat: 로그인/로그아웃 기능 추가" src/auth/login.ts src/auth/logout.ts
80
- .claude/scripts/git/git-push.sh
79
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "feat: 로그인/로그아웃 기능 추가" src/auth/login.ts src/auth/logout.ts
80
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-push.sh
81
81
  ```
82
82
 
83
83
  </example>
@@ -16,8 +16,8 @@ argument-hint: [파일/디렉토리 경로...]
16
16
 
17
17
  | 스크립트 | 용도 |
18
18
  |----------|------|
19
- | `.claude/scripts/lint/lint-check.sh` | tsc + eslint 병렬 검사 |
20
- | `.claude/scripts/lint/lint-file.sh [files]` | 특정 파일만 검사 |
19
+ | `${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh` | tsc + eslint 병렬 검사 |
20
+ | `${CLAUDE_SCRIPTS_ROOT}/lint/lint-file.sh [files]` | 특정 파일만 검사 |
21
21
 
22
22
  </scripts>
23
23
 
@@ -31,17 +31,17 @@ argument-hint: [파일/디렉토리 경로...]
31
31
 
32
32
  ```bash
33
33
  # 1. 병렬 검사 (tsc + eslint 동시)
34
- .claude/scripts/lint/lint-check.sh
34
+ ${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh
35
35
 
36
36
  # 2. 오류 수정 후 재검사
37
- .claude/scripts/lint/lint-check.sh
37
+ ${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh
38
38
  ```
39
39
 
40
40
  ### 특정 파일 검사
41
41
 
42
42
  ```bash
43
43
  # 특정 파일만
44
- .claude/scripts/lint/lint-file.sh src/utils/api.ts src/components/Button.tsx
44
+ ${CLAUDE_SCRIPTS_ROOT}/lint/lint-file.sh src/utils/api.ts src/components/Button.tsx
45
45
  ```
46
46
 
47
47
  </workflow>
@@ -16,10 +16,10 @@ argument-hint: [파일/디렉토리 경로...]
16
16
 
17
17
  | 스크립트 | 용도 |
18
18
  |----------|------|
19
- | `.claude/scripts/deploy/deploy-check.sh` | 전체 검증 (tsc + eslint + build) |
20
- | `.claude/scripts/deploy/build-run.sh` | build만 실행 |
21
- | `.claude/scripts/lint/lint-check.sh` | tsc + eslint 병렬 검사 |
22
- | `.claude/scripts/pm/pm-detect.sh` | package manager 감지 |
19
+ | `${CLAUDE_SCRIPTS_ROOT}/deploy/deploy-check.sh` | 전체 검증 (tsc + eslint + build) |
20
+ | `${CLAUDE_SCRIPTS_ROOT}/deploy/build-run.sh` | build만 실행 |
21
+ | `${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh` | tsc + eslint 병렬 검사 |
22
+ | `${CLAUDE_SCRIPTS_ROOT}/pm/pm-detect.sh` | package manager 감지 |
23
23
 
24
24
  </scripts>
25
25
 
@@ -33,17 +33,17 @@ argument-hint: [파일/디렉토리 경로...]
33
33
 
34
34
  ```bash
35
35
  # 한 번에 tsc + eslint + build 검증
36
- .claude/scripts/deploy/deploy-check.sh
36
+ ${CLAUDE_SCRIPTS_ROOT}/deploy/deploy-check.sh
37
37
  ```
38
38
 
39
39
  ### 단계별 검증
40
40
 
41
41
  ```bash
42
42
  # 1. lint 검사
43
- .claude/scripts/lint/lint-check.sh
43
+ ${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh
44
44
 
45
45
  # 2. 오류 수정 후 build
46
- .claude/scripts/deploy/build-run.sh
46
+ ${CLAUDE_SCRIPTS_ROOT}/deploy/build-run.sh
47
47
  ```
48
48
 
49
49
  </workflow>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: "Start Ralph Loop in current session"
3
3
  argument-hint: "PROMPT [--max-iterations N] [--completion-promise TEXT]"
4
- allowed-tools: ["Bash(.claude/scripts/setup-ralph-loop.sh:*)"]
4
+ allowed-tools: ["Bash(${CLAUDE_SCRIPTS_ROOT}/setup-ralph-loop.sh:*)"]
5
5
  hide-from-slash-command-tool: "true"
6
6
  ---
7
7
 
@@ -10,7 +10,7 @@ hide-from-slash-command-tool: "true"
10
10
  Execute the setup script to initialize the Ralph loop:
11
11
 
12
12
  ```!
13
- ".claude/scripts/setup-ralph-loop.sh" $ARGUMENTS
13
+ "${CLAUDE_SCRIPTS_ROOT}/setup-ralph-loop.sh" $ARGUMENTS
14
14
  ```
15
15
 
16
16
  Please work on the task. When you try to exit, the Ralph loop will feed the SAME PROMPT back to you for the next iteration. You'll see your previous work in files and git history, allowing you to iterate and improve.
@@ -18,10 +18,10 @@ argument-hint: <new-version | +1 | +minor | +major>
18
18
 
19
19
  | 스크립트 | 용도 |
20
20
  |----------|------|
21
- | `.claude/scripts/version/version-find.sh` | 버전 파일 탐색 (package.json + .version()) |
22
- | `.claude/scripts/version/version-bump.sh <current> <type>` | 새 버전 계산 |
23
- | `.claude/scripts/git/git-commit.sh "msg" [files]` | 커밋 |
24
- | `.claude/scripts/git/git-push.sh` | 푸시 |
21
+ | `${CLAUDE_SCRIPTS_ROOT}/version/version-find.sh` | 버전 파일 탐색 (package.json + .version()) |
22
+ | `${CLAUDE_SCRIPTS_ROOT}/version/version-bump.sh <current> <type>` | 새 버전 계산 |
23
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "msg" [files]` | 커밋 |
24
+ | `${CLAUDE_SCRIPTS_ROOT}/git/git-push.sh` | 푸시 |
25
25
 
26
26
  </scripts>
27
27
 
@@ -46,18 +46,18 @@ argument-hint: <new-version | +1 | +minor | +major>
46
46
 
47
47
  ```bash
48
48
  # 1. 버전 파일 탐색
49
- .claude/scripts/version/version-find.sh
49
+ ${CLAUDE_SCRIPTS_ROOT}/version/version-find.sh
50
50
 
51
51
  # 2. 현재 버전 확인 (package.json 읽기)
52
52
 
53
53
  # 3. 새 버전 계산
54
- .claude/scripts/version/version-bump.sh 1.2.3 +1
54
+ ${CLAUDE_SCRIPTS_ROOT}/version/version-bump.sh 1.2.3 +1
55
55
  # → 1.2.4
56
56
 
57
57
  # 4. 모든 파일 Edit로 업데이트
58
58
 
59
59
  # 5. 커밋
60
- .claude/scripts/git/git-commit.sh "chore: 버전 1.2.4로 업데이트" package.json packages/*/package.json
60
+ ${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "chore: 버전 1.2.4로 업데이트" package.json packages/*/package.json
61
61
  ```
62
62
 
63
63
  </workflow>
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ # Session Environment Setup Hook
4
+ # Sets CLAUDE_SCRIPTS_ROOT based on where the command/skill is located
5
+ # - User scope (~/.claude/): CLAUDE_SCRIPTS_ROOT=~/.claude/scripts
6
+ # - Project scope (.claude/): CLAUDE_SCRIPTS_ROOT=.claude/scripts
7
+
8
+ set -euo pipefail
9
+
10
+ # Only run for SessionStart events
11
+ # The hook receives JSON input via stdin, but for SessionStart we just need to set env vars
12
+
13
+ # Determine the scripts root based on installation scope
14
+ # Check if user-level .claude exists and has scripts
15
+ USER_SCRIPTS_DIR="$HOME/.claude/scripts"
16
+ PROJECT_SCRIPTS_DIR=".claude/scripts"
17
+
18
+ # Priority: Project scope first (if exists), then User scope
19
+ if [[ -d "$PROJECT_SCRIPTS_DIR" ]]; then
20
+ SCRIPTS_ROOT="$PROJECT_SCRIPTS_DIR"
21
+ elif [[ -d "$USER_SCRIPTS_DIR" ]]; then
22
+ SCRIPTS_ROOT="$USER_SCRIPTS_DIR"
23
+ else
24
+ # Fallback to project scope (default)
25
+ SCRIPTS_ROOT="$PROJECT_SCRIPTS_DIR"
26
+ fi
27
+
28
+ # Write to CLAUDE_ENV_FILE (session-wide environment)
29
+ if [[ -n "${CLAUDE_ENV_FILE:-}" ]]; then
30
+ echo "CLAUDE_SCRIPTS_ROOT=$SCRIPTS_ROOT" >> "$CLAUDE_ENV_FILE"
31
+ fi
32
+
33
+ exit 0
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  # Agent Teams 사용 가능 여부 확인
3
- # Usage: .claude/scripts/agent-teams/check-availability.sh
3
+ # Usage: ${CLAUDE_SCRIPTS_ROOT}/agent-teams/check-availability.sh
4
4
 
5
5
  set -e
6
6
 
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  # Agent Teams용 tmux 환경 세팅
3
- # Usage: .claude/scripts/agent-teams/setup-tmux.sh
3
+ # Usage: ${CLAUDE_SCRIPTS_ROOT}/agent-teams/setup-tmux.sh
4
4
 
5
5
  set -e
6
6
 
@@ -1,104 +1,100 @@
1
1
  ---
2
2
  name: codex
3
- description: Claude와 OpenAI Codex 페어 프로그래밍. 자체 codex-mcp 서버로 자유 협업. Claude(창의적) + Codex(꼼꼼함) 강점 활용.
3
+ description: Claude와 OpenAI Codex 페어 프로그래밍. 자유 협업으로 복잡한 작업을 분업 처리. Claude(창의적) + Codex(꼼꼼함) 강점 활용.
4
4
  user-invocable: true
5
5
  ---
6
6
 
7
7
  @../../instructions/workflow-patterns/sequential-thinking.md
8
- @../../instructions/agent-patterns/parallel-execution.md
9
- @../../instructions/agent-patterns/model-routing.md
10
8
  @../../instructions/agent-patterns/agent-teams-usage.md
11
9
  @../../instructions/validation/forbidden-patterns.md
12
10
  @../../instructions/validation/required-behaviors.md
13
11
 
14
- # Codex Pair Programming Skill
12
+ # Codex Pair Programming
15
13
 
16
- > Claude + codex-mcp 페어 프로그래밍. Claude(설계/창의) + Codex(구현/검증) 역할 분담.
14
+ > Claude + Codex CLI 페어 프로그래밍. codex-mcp 서버가 Codex CLI를 래핑.
17
15
 
18
16
  ---
19
17
 
20
18
  <overview>
21
19
 
22
- **codex-mcp 서버:** Rust MCP 바이너리 → OpenAI Responses API 직접 호출
20
+ **codex-mcp:** Codex CLI 래퍼 MCP 서버 (Rust)
23
21
 
24
22
  | 특성 | 상세 |
25
23
  |------|------|
26
- | **인증** | Codex CLI OAuth (`~/.codex/auth.json`), 토큰 자동 갱신, 401시 강제 갱신+재시도 |
27
- | **에이전트 루프** | `read_file`, `write_file`, `list_files`, `shell_exec` 자율 실행 |
28
- | **세션** | `thread_id` 기반 컨텍스트 유지, 모델 세션간 자동 보존 |
29
- | **보안** | 경로순회 방지, 프로세스그룹 격리, 동시요청 방지 |
24
+ | **인증** | Codex CLI OAuth (`codex login`) |
25
+ | **세션** | `thread_id` 기반 멀티턴, cwd/model 세션간 보존 |
26
+ | **도구** | `codex`, `codex_reply`, `codex_review`, `list_sessions`, `ping` |
30
27
 
31
28
  </overview>
32
29
 
33
30
  ---
34
31
 
35
- <prerequisites>
32
+ <setup>
36
33
 
37
- ## 사전 요구사항
34
+ ## 설정
38
35
 
39
36
  ```bash
40
- # 1. Codex CLI OAuth 로그인
41
- codex auth login
37
+ # Codex CLI 로그인
38
+ codex login
42
39
 
43
- # 2. 빌드 + Claude Code 등록
44
- cargo build -p codex-mcp --release
45
- claude mcp add -s user codex -- /path/to/target/release/codex-mcp
40
+ # codex-mcp 등록 확인
41
+ # 설정 OpenAI / Codex 탭 → 등록 버튼
46
42
  ```
47
43
 
48
- 또는 앱 설정 → **OpenAI / Codex** 탭 → **등록** 버튼.
49
-
50
- **인증:** `~/.codex/auth.json` (OAuth 토큰, 자동 갱신)
51
-
52
- </prerequisites>
44
+ </setup>
53
45
 
54
46
  ---
55
47
 
56
- <codex_mcp_tools>
48
+ <tools>
57
49
 
58
50
  ## MCP 도구
59
51
 
60
- ### mcp__codex__codex — 새 태스크
52
+ ### codex — 새 태스크
61
53
 
62
54
  ```typescript
63
55
  mcp__codex__codex({
64
56
  prompt: "작업 지시",
65
- working_directory: "/path/to/project",
66
- model: "gpt-5.3-codex high" // 선택 (세션에 저장, 생략 시 Codex CLI 기본값)
57
+ working_directory: "/path/to/project", // 선택
58
+ session_id: "my-session", // 선택 (멀티턴용)
59
+ model: "gpt-5.3-codex", // 선택
60
+ reasoning_effort: "high" // 선택: none|minimal|low|medium|high|xhigh
67
61
  })
68
- // → JSON { thread_id, result }
62
+ // → { thread_id, result }
69
63
  ```
70
64
 
71
- ### mcp__codex__codex_reply — 세션 이어가기
65
+ ### codex_reply — 세션 이어가기
72
66
 
73
67
  ```typescript
74
68
  mcp__codex__codex_reply({
75
69
  thread_id: "이전 thread_id",
76
- prompt: "후속 지시 또는 리뷰 요청"
70
+ prompt: "후속 지시"
77
71
  })
78
- // JSON { thread_id, result }
79
- // 세션 모델 자동 유지
72
+ // 세션 cwd/model 자동 유지
80
73
  ```
81
74
 
82
- ### 리뷰 패턴 (2단계)
75
+ ### codex_review 코드 리뷰
83
76
 
84
77
  ```typescript
85
- // 1단계: 분석
86
- const r = mcp__codex__codex({
87
- prompt: "src/auth/ 코드 읽고 분석해줘",
88
- working_directory: cwd
89
- })
90
- // 2단계: 리뷰
91
- mcp__codex__codex_reply({
92
- thread_id: r.thread_id,
93
- prompt: "보안, 성능, 엣지케이스 리뷰"
78
+ mcp__codex__codex_review({
79
+ working_directory: "/path/to/project",
80
+ uncommitted: true, // 기본값
81
+ base: "main", // 선택: 비교 브랜치
82
+ commit: "abc123" // 선택: 특정 커밋
94
83
  })
95
84
  ```
96
85
 
97
- </codex_mcp_tools>
86
+ ### list_sessions / ping
87
+
88
+ ```typescript
89
+ mcp__codex__list_sessions() // 활성 세션 목록
90
+ mcp__codex__ping() // 헬스체크
91
+ ```
92
+
93
+ </tools>
98
94
 
99
95
  ---
100
96
 
101
- <collaboration_modes>
97
+ <collaboration>
102
98
 
103
99
  ## 협업 모드
104
100
 
@@ -107,56 +103,50 @@ mcp__codex__codex_reply({
107
103
  | **Solo+Review** | 1-2 파일 | 구현 | 리뷰 |
108
104
  | **Sequential** | 설계→구현 | 아키텍처 | 구현+테스트 |
109
105
  | **Parallel** | 독립 작업 | 창의적 부분 | 꼼꼼한 부분 |
110
- | **Discussion** | 트레이드오프 | 관점 A | 관점 B |
111
- | **Teams** | 6+ 파일 | 팀원 | **Team Lead** |
112
106
 
113
- **모드 선택:** sequentialthinking으로 복잡도 판단 → 자동 선택
114
-
115
- </collaboration_modes>
107
+ </collaboration>
116
108
 
117
109
  ---
118
110
 
119
- <workflow>
120
-
121
- ## 실행 흐름
111
+ <patterns>
122
112
 
123
- | Phase | 작업 |
124
- |-------|------|
125
- | **0** | MCP 가용 확인 + 복잡도 분석 |
126
- | **1** | 협업 모드 결정 |
127
- | **2** | 역할 분담 (TodoWrite) |
128
- | **3** | 협업 실행 (Task codex agent + 직접 작업) |
129
- | **4** | 결과 통합 (충돌 시 AskUserQuestion) |
130
- | **5** | 검증 + 커밋 |
113
+ ## 사용 패턴
131
114
 
132
- ### Solo+Review
115
+ ### 구현 후 리뷰
133
116
 
134
117
  ```typescript
135
- // Claude 구현 → Codex 리뷰
118
+ // Claude 구현
136
119
  Edit/Write → 코드 작성
137
- const r = mcp__codex__codex({ prompt: "git diff 분석: [diff]", working_directory: cwd })
138
- mcp__codex__codex_reply({ thread_id: r.thread_id, prompt: "버그, 보안, 엣지케이스 리뷰" })
120
+
121
+ // Codex가 리뷰
122
+ mcp__codex__codex_review({ uncommitted: true })
139
123
  ```
140
124
 
141
- ### Sequential
125
+ ### 멀티턴 대화
142
126
 
143
127
  ```typescript
144
- // Claude 설계 → Codex 구현
145
- sequentialthinking 아키텍처
146
- Task({ subagent_type: 'codex', prompt: `Claude 설계 기반 구현: ${설계문서}` })
128
+ const r = mcp__codex__codex({
129
+ prompt: "src/auth/ 분석해줘",
130
+ session_id: "auth-work"
131
+ })
132
+
133
+ mcp__codex__codex_reply({
134
+ thread_id: r.thread_id,
135
+ prompt: "보안 취약점 있어?"
136
+ })
147
137
  ```
148
138
 
149
- ### Teams (Codex Lead)
139
+ ### 설계 구현
150
140
 
151
141
  ```typescript
152
- TeamCreate({ team_name: "pair-codex", agent_type: "codex" })
153
- Task({ subagent_type: 'implementation-executor', team_name: 'pair-codex', name: 'claude-impl', prompt: '...' })
154
- // 완료 후
155
- SendMessage({ type: 'shutdown_request', recipient: 'claude-impl' })
156
- TeamDelete()
142
+ // Claude 설계
143
+ sequentialthinking 아키텍처
144
+
145
+ // Codex 구현
146
+ Task({ subagent_type: 'codex', prompt: '설계 기반 구현' })
157
147
  ```
158
148
 
159
- </workflow>
149
+ </patterns>
160
150
 
161
151
  ---
162
152
 
@@ -164,52 +154,35 @@ TeamDelete()
164
154
 
165
155
  ## 역할별 강점
166
156
 
167
- | Claude 우선 | Codex 우선 |
168
- |-------------|-----------|
157
+ | Claude | Codex |
158
+ |--------|-------|
169
159
  | 아키텍처 설계 | 정밀 구현 |
170
160
  | 창의적 해결책 | 엣지케이스 |
171
161
  | 문제 재정의 | 코드 리뷰 |
172
- | 통합 설계 | 테스트 작성 |
173
- | 문서화 | 디버깅 |
174
162
 
175
163
  </strengths>
176
164
 
177
165
  ---
178
166
 
179
- <validation>
180
-
181
- ## 검증
182
-
183
- **실행 전:** MCP 가용 확인 → Codex 로그인 확인 → 복잡도 분석 → 모드/역할 결정
184
- **실행 중:** 역할 범위 준수, 파일 충돌 방지 (한 파일 = 한 담당), TodoWrite 추적
185
- **완료 후:** 결과 통합 → 코드 리뷰 → 테스트 통과 → Teams 정리
186
-
187
- </validation>
188
-
189
- ---
190
-
191
167
  <forbidden>
192
168
 
193
169
  | 금지 | 대안 |
194
170
  |------|------|
195
- | MCP 미설정으로 진행 | 설정 안내 후 중단 |
171
+ | MCP 미설정 상태로 진행 | 설정 안내 후 중단 |
196
172
  | 같은 파일 동시 수정 | 작업 범위 분리 |
197
- | 충돌 시 임의 결정 | AskUserQuestion |
198
173
  | Codex 결과 무검증 수용 | 리뷰 + 테스트 |
199
174
 
200
175
  </forbidden>
201
176
 
202
177
  ---
203
178
 
204
- <troubleshooting>
179
+ <errors>
205
180
 
206
181
  | 에러 | 해결 |
207
182
  |------|------|
208
183
  | MCP 연결 실패 | 앱 설정 → OpenAI/Codex → 등록 |
209
- | 401 인증 오류 | `codex auth login` |
210
- | 토큰 갱신 실패 | `codex auth login` 재실행 |
211
- | 타임아웃 | 재시도 |
184
+ | 401 인증 오류 | `codex login` |
212
185
  | 세션 not found | 새 `codex` 세션 시작 |
213
186
  | 동시 요청 에러 | 이전 요청 완료 대기 |
214
187
 
215
- </troubleshooting>
188
+ </errors>
@@ -534,7 +534,7 @@ const args = parseArguments(ARGUMENTS)
534
534
 
535
535
  ```bash
536
536
  # Agent Teams 가용성 체크
537
- .claude/scripts/agent-teams/check-availability.sh
537
+ ${CLAUDE_SCRIPTS_ROOT}/agent-teams/check-availability.sh
538
538
  ```
539
539
 
540
540
  ### 3. 작업 분석
@@ -32,7 +32,7 @@ fi
32
32
  ### 스크립트 사용
33
33
 
34
34
  ```bash
35
- .claude/scripts/agent-teams/check-availability.sh
35
+ ${CLAUDE_SCRIPTS_ROOT}/agent-teams/check-availability.sh
36
36
  ```
37
37
 
38
38
  </availability_check>