@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 +108 -73
- package/package.json +1 -1
- package/templates/.claude/agents/codex.md +31 -71
- package/templates/.claude/commands/git-all.md +12 -12
- package/templates/.claude/commands/git-merge.md +4 -4
- package/templates/.claude/commands/git-session.md +10 -10
- package/templates/.claude/commands/lint-fix.md +5 -5
- package/templates/.claude/commands/pre-deploy.md +7 -7
- package/templates/.claude/commands/ralph-loop.md +2 -2
- package/templates/.claude/commands/version-update.md +7 -7
- package/templates/.claude/hooks/session-env-setup.sh +33 -0
- package/templates/.claude/scripts/agent-teams/check-availability.sh +1 -1
- package/templates/.claude/scripts/agent-teams/setup-tmux.sh +1 -1
- package/templates/.claude/skills/codex/SKILL.md +70 -97
- package/templates/.claude/skills/teams/SKILL.md +1 -1
- package/templates/.claude/skills/teams/references/fallback-strategy.md +1 -1
package/dist/index.js
CHANGED
|
@@ -22,9 +22,9 @@ var banner = () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
|
-
import
|
|
25
|
+
import fs10 from "fs-extra";
|
|
26
26
|
import os2 from "os";
|
|
27
|
-
import
|
|
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
|
|
752
|
-
import
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
864
|
+
import fs9 from "fs-extra";
|
|
830
865
|
import os from "os";
|
|
831
|
-
import
|
|
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 =
|
|
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 =
|
|
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 =
|
|
984
|
+
const normalizedTarget = path11.resolve(targetDir);
|
|
950
985
|
for (const [projectPath, projectState] of Object.entries(projects)) {
|
|
951
|
-
if (
|
|
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
|
|
1017
|
+
if (!await fs9.pathExists(filePath)) {
|
|
983
1018
|
return void 0;
|
|
984
1019
|
}
|
|
985
1020
|
try {
|
|
986
|
-
const raw = await
|
|
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 =
|
|
1164
|
-
const normalizedTarget =
|
|
1198
|
+
const homeDir = path11.resolve(os.homedir());
|
|
1199
|
+
const normalizedTarget = path11.resolve(targetDir);
|
|
1165
1200
|
const isUserScope = normalizedTarget === homeDir;
|
|
1166
|
-
const claudeStatePath =
|
|
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 =
|
|
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 =
|
|
1192
|
-
const normalizedTarget =
|
|
1226
|
+
const homeDir = path11.resolve(os.homedir());
|
|
1227
|
+
const normalizedTarget = path11.resolve(targetDir);
|
|
1193
1228
|
const isUserScope = normalizedTarget === homeDir;
|
|
1194
|
-
const codexBaseDir = isUserScope ?
|
|
1195
|
-
const codexMcpConfigPath =
|
|
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
|
|
1234
|
+
const existingConfig = await fs9.pathExists(codexMcpConfigPath) ? await fs9.readFile(codexMcpConfigPath, "utf8") : "";
|
|
1200
1235
|
const nextConfig = mergeCodexConfigToml(existingConfig, renderedMcpBlocks);
|
|
1201
|
-
await
|
|
1202
|
-
await
|
|
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
|
|
1246
|
+
await fs9.ensureDir(codexSkillsDir);
|
|
1212
1247
|
let sourceSkillNames = [];
|
|
1213
|
-
if (await
|
|
1214
|
-
const sourceEntries = await
|
|
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
|
|
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
|
|
1266
|
+
await fs9.remove(path11.join(codexSkillsDir, entry.name));
|
|
1232
1267
|
}
|
|
1233
1268
|
}
|
|
1234
1269
|
for (const skillName of sourceSkillNames) {
|
|
1235
|
-
const src =
|
|
1236
|
-
const dest =
|
|
1237
|
-
if (await
|
|
1238
|
-
await
|
|
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
|
|
1246
|
-
await
|
|
1280
|
+
if (!await fs9.pathExists(claudeCommandsDir)) {
|
|
1281
|
+
await fs9.remove(codexCommandsDir);
|
|
1247
1282
|
return 0;
|
|
1248
1283
|
}
|
|
1249
|
-
const entries = await
|
|
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
|
|
1287
|
+
await fs9.remove(codexCommandsDir);
|
|
1253
1288
|
return 0;
|
|
1254
1289
|
}
|
|
1255
|
-
await
|
|
1256
|
-
await
|
|
1290
|
+
await fs9.remove(codexCommandsDir);
|
|
1291
|
+
await fs9.ensureDir(codexCommandsDir);
|
|
1257
1292
|
for (const commandFile of commandFiles) {
|
|
1258
|
-
const commandPath =
|
|
1259
|
-
const commandRaw = await
|
|
1260
|
-
const commandName =
|
|
1261
|
-
const skillDir =
|
|
1262
|
-
const skillPath =
|
|
1263
|
-
await
|
|
1264
|
-
await
|
|
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
|
|
1273
|
-
await
|
|
1307
|
+
if (!await fs9.pathExists(claudeInstructionsDir)) {
|
|
1308
|
+
await fs9.remove(codexInstructionsDir);
|
|
1274
1309
|
return 0;
|
|
1275
1310
|
}
|
|
1276
|
-
await
|
|
1277
|
-
await
|
|
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
|
|
1333
|
+
if (!await fs9.pathExists(rootDir)) {
|
|
1299
1334
|
return collector;
|
|
1300
1335
|
}
|
|
1301
|
-
const entries = await
|
|
1336
|
+
const entries = await fs9.readdir(rootDir, { withFileTypes: true });
|
|
1302
1337
|
for (const entry of entries) {
|
|
1303
|
-
const fullPath =
|
|
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
|
|
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 =
|
|
1331
|
-
if (await
|
|
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 =
|
|
1348
|
-
const codexSkillsDir =
|
|
1349
|
-
const codexCommandsDir =
|
|
1350
|
-
const codexInstructionsDir =
|
|
1351
|
-
await
|
|
1352
|
-
const claudeRootDir =
|
|
1353
|
-
const claudeSkillsDir =
|
|
1354
|
-
const claudeCommandsDir =
|
|
1355
|
-
const claudeInstructionsDir =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
1636
|
-
const 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.
|
|
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,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
|
|
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: '
|
|
34
|
+
Task({ subagent_type: 'implementation-executor', team_name: 'project', name: 'impl', prompt: '...' })
|
|
38
35
|
// 품질 검증
|
|
39
|
-
|
|
36
|
+
mcp__codex__codex_review({ uncommitted: true })
|
|
40
37
|
// 정리
|
|
41
|
-
SendMessage({ type: 'shutdown_request', recipient: '
|
|
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
|
-
<
|
|
46
|
+
<tools>
|
|
50
47
|
|
|
51
48
|
## MCP 도구
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
mcp__codex__codex_reply({ thread_id: r.thread_id, prompt: "보안, 엣지케이스, 성능 리뷰" })
|
|
63
|
+
// 리뷰
|
|
64
|
+
mcp__codex__codex_review({ uncommitted: true })
|
|
83
65
|
```
|
|
84
66
|
|
|
85
|
-
</
|
|
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
|
|
75
|
+
**구현:** Read → codex → Bash(테스트) → Edit(미세 조정)
|
|
94
76
|
|
|
95
|
-
**리뷰:**
|
|
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
|
|
125
|
-
| 구현 후 테스트 실행 | 테스트 없이 완료
|
|
126
|
-
|
|
|
127
|
-
| 구조화된 결과 보고 | 충돌 시 임의 결정 |
|
|
87
|
+
| codex MCP 도구 사용 | MCP 없이 시뮬레이션 |
|
|
88
|
+
| 구현 후 테스트 실행 | 테스트 없이 완료 |
|
|
89
|
+
| 경계 조건 처리 | Claude 영역 침범 |
|
|
128
90
|
|
|
129
91
|
</rules>
|
|
130
92
|
|
|
131
93
|
---
|
|
132
94
|
|
|
133
|
-
<
|
|
95
|
+
<errors>
|
|
134
96
|
|
|
135
97
|
| 에러 | 대응 |
|
|
136
98
|
|------|------|
|
|
137
|
-
|
|
|
138
|
-
| 401 | `codex auth login` 안내 |
|
|
139
|
-
| 타임아웃 | 재시도 |
|
|
99
|
+
| 401 | `codex login` 안내 |
|
|
140
100
|
| 세션 not found | 새 codex 세션 |
|
|
141
|
-
| 동시 요청
|
|
101
|
+
| 동시 요청 | 이전 요청 완료 대기 |
|
|
142
102
|
|
|
143
|
-
</
|
|
103
|
+
</errors>
|
|
@@ -14,11 +14,11 @@ description: 모든 변경사항 커밋 후 푸시
|
|
|
14
14
|
|
|
15
15
|
| 스크립트 | 용도 |
|
|
16
16
|
|----------|------|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
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
|
-
|
|
35
|
+
${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
|
|
36
36
|
|
|
37
37
|
# 2. 모든 변경사항 커밋 + 푸시
|
|
38
|
-
|
|
38
|
+
${CLAUDE_SCRIPTS_ROOT}/git/git-all.sh "feat: 기능 추가"
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
### 복잡한 케이스 (논리적 분리 필요)
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
# 1. 상태 확인
|
|
45
|
-
|
|
45
|
+
${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
|
|
46
46
|
|
|
47
47
|
# 2. 그룹별 커밋
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
${CLAUDE_SCRIPTS_ROOT}/git/git-merge.sh deploy/prod dev
|
|
50
50
|
|
|
51
51
|
# main에 feature/auth 머지
|
|
52
|
-
|
|
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
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
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
|
-
|
|
32
|
+
${CLAUDE_SCRIPTS_ROOT}/git/git-info.sh
|
|
33
33
|
|
|
34
34
|
# 2. 현재 세션 파일만 선택하여 커밋
|
|
35
|
-
|
|
35
|
+
${CLAUDE_SCRIPTS_ROOT}/git/git-commit.sh "feat: 로그인 기능" src/auth/login.ts src/auth/logout.ts
|
|
36
36
|
|
|
37
37
|
# 3. 푸시
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
|
20
|
-
|
|
|
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
|
-
|
|
34
|
+
${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh
|
|
35
35
|
|
|
36
36
|
# 2. 오류 수정 후 재검사
|
|
37
|
-
|
|
37
|
+
${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
### 특정 파일 검사
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
# 특정 파일만
|
|
44
|
-
|
|
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
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
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
|
-
|
|
36
|
+
${CLAUDE_SCRIPTS_ROOT}/deploy/deploy-check.sh
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
### 단계별 검증
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
# 1. lint 검사
|
|
43
|
-
|
|
43
|
+
${CLAUDE_SCRIPTS_ROOT}/lint/lint-check.sh
|
|
44
44
|
|
|
45
45
|
# 2. 오류 수정 후 build
|
|
46
|
-
|
|
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(
|
|
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
|
-
"
|
|
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
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
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
|
-
|
|
49
|
+
${CLAUDE_SCRIPTS_ROOT}/version/version-find.sh
|
|
50
50
|
|
|
51
51
|
# 2. 현재 버전 확인 (package.json 읽기)
|
|
52
52
|
|
|
53
53
|
# 3. 새 버전 계산
|
|
54
|
-
|
|
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
|
-
|
|
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,104 +1,100 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: codex
|
|
3
|
-
description: Claude와 OpenAI 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
|
|
12
|
+
# Codex Pair Programming
|
|
15
13
|
|
|
16
|
-
> Claude +
|
|
14
|
+
> Claude + Codex CLI 페어 프로그래밍. codex-mcp 서버가 Codex CLI를 래핑.
|
|
17
15
|
|
|
18
16
|
---
|
|
19
17
|
|
|
20
18
|
<overview>
|
|
21
19
|
|
|
22
|
-
**codex-mcp
|
|
20
|
+
**codex-mcp:** Codex CLI 래퍼 MCP 서버 (Rust)
|
|
23
21
|
|
|
24
22
|
| 특성 | 상세 |
|
|
25
23
|
|------|------|
|
|
26
|
-
| **인증** | Codex CLI OAuth (
|
|
27
|
-
|
|
|
28
|
-
|
|
|
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
|
-
<
|
|
32
|
+
<setup>
|
|
36
33
|
|
|
37
|
-
##
|
|
34
|
+
## 설정
|
|
38
35
|
|
|
39
36
|
```bash
|
|
40
|
-
#
|
|
41
|
-
codex
|
|
37
|
+
# Codex CLI 로그인
|
|
38
|
+
codex login
|
|
42
39
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
claude mcp add -s user codex -- /path/to/target/release/codex-mcp
|
|
40
|
+
# codex-mcp 등록 확인
|
|
41
|
+
# 앱 설정 → OpenAI / Codex 탭 → 등록 버튼
|
|
46
42
|
```
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
**인증:** `~/.codex/auth.json` (OAuth 토큰, 자동 갱신)
|
|
51
|
-
|
|
52
|
-
</prerequisites>
|
|
44
|
+
</setup>
|
|
53
45
|
|
|
54
46
|
---
|
|
55
47
|
|
|
56
|
-
<
|
|
48
|
+
<tools>
|
|
57
49
|
|
|
58
50
|
## MCP 도구
|
|
59
51
|
|
|
60
|
-
###
|
|
52
|
+
### codex — 새 태스크
|
|
61
53
|
|
|
62
54
|
```typescript
|
|
63
55
|
mcp__codex__codex({
|
|
64
56
|
prompt: "작업 지시",
|
|
65
|
-
working_directory: "/path/to/project",
|
|
66
|
-
|
|
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
|
-
// →
|
|
62
|
+
// → { thread_id, result }
|
|
69
63
|
```
|
|
70
64
|
|
|
71
|
-
###
|
|
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
|
-
//
|
|
79
|
-
// 세션 모델 자동 유지
|
|
72
|
+
// 세션 cwd/model 자동 유지
|
|
80
73
|
```
|
|
81
74
|
|
|
82
|
-
###
|
|
75
|
+
### codex_review — 코드 리뷰
|
|
83
76
|
|
|
84
77
|
```typescript
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
</collaboration_modes>
|
|
107
|
+
</collaboration>
|
|
116
108
|
|
|
117
109
|
---
|
|
118
110
|
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
## 실행 흐름
|
|
111
|
+
<patterns>
|
|
122
112
|
|
|
123
|
-
|
|
124
|
-
|-------|------|
|
|
125
|
-
| **0** | MCP 가용 확인 + 복잡도 분석 |
|
|
126
|
-
| **1** | 협업 모드 결정 |
|
|
127
|
-
| **2** | 역할 분담 (TodoWrite) |
|
|
128
|
-
| **3** | 협업 실행 (Task codex agent + 직접 작업) |
|
|
129
|
-
| **4** | 결과 통합 (충돌 시 AskUserQuestion) |
|
|
130
|
-
| **5** | 검증 + 커밋 |
|
|
113
|
+
## 사용 패턴
|
|
131
114
|
|
|
132
|
-
###
|
|
115
|
+
### 구현 후 리뷰
|
|
133
116
|
|
|
134
117
|
```typescript
|
|
135
|
-
// Claude 구현
|
|
118
|
+
// Claude가 구현
|
|
136
119
|
Edit/Write → 코드 작성
|
|
137
|
-
|
|
138
|
-
|
|
120
|
+
|
|
121
|
+
// Codex가 리뷰
|
|
122
|
+
mcp__codex__codex_review({ uncommitted: true })
|
|
139
123
|
```
|
|
140
124
|
|
|
141
|
-
###
|
|
125
|
+
### 멀티턴 대화
|
|
142
126
|
|
|
143
127
|
```typescript
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
###
|
|
139
|
+
### 설계 → 구현
|
|
150
140
|
|
|
151
141
|
```typescript
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
142
|
+
// Claude 설계
|
|
143
|
+
sequentialthinking → 아키텍처
|
|
144
|
+
|
|
145
|
+
// Codex 구현
|
|
146
|
+
Task({ subagent_type: 'codex', prompt: '설계 기반 구현' })
|
|
157
147
|
```
|
|
158
148
|
|
|
159
|
-
</
|
|
149
|
+
</patterns>
|
|
160
150
|
|
|
161
151
|
---
|
|
162
152
|
|
|
@@ -164,52 +154,35 @@ TeamDelete()
|
|
|
164
154
|
|
|
165
155
|
## 역할별 강점
|
|
166
156
|
|
|
167
|
-
| Claude
|
|
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
|
-
<
|
|
179
|
+
<errors>
|
|
205
180
|
|
|
206
181
|
| 에러 | 해결 |
|
|
207
182
|
|------|------|
|
|
208
183
|
| MCP 연결 실패 | 앱 설정 → OpenAI/Codex → 등록 |
|
|
209
|
-
| 401 인증 오류 | `codex
|
|
210
|
-
| 토큰 갱신 실패 | `codex auth login` 재실행 |
|
|
211
|
-
| 타임아웃 | 재시도 |
|
|
184
|
+
| 401 인증 오류 | `codex login` |
|
|
212
185
|
| 세션 not found | 새 `codex` 세션 시작 |
|
|
213
186
|
| 동시 요청 에러 | 이전 요청 완료 대기 |
|
|
214
187
|
|
|
215
|
-
</
|
|
188
|
+
</errors>
|