@kood/claude-code 0.7.9 → 0.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +475 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ var banner = () => {
23
23
 
24
24
  // src/commands/init.ts
25
25
  import fs9 from "fs-extra";
26
- import os from "os";
26
+ import os2 from "os";
27
27
  import path11 from "path";
28
28
 
29
29
  // src/features/templates/template-path-resolver.ts
@@ -690,7 +690,7 @@ async function promptScopeSelection(options) {
690
690
  return { scope: response.value };
691
691
  }
692
692
  async function promptCodexSync(options) {
693
- const { providedSyncCodex, codexSkillsPath } = options;
693
+ const { providedSyncCodex, codexPath } = options;
694
694
  if (providedSyncCodex !== void 0) {
695
695
  return { syncCodex: providedSyncCodex };
696
696
  }
@@ -698,7 +698,7 @@ async function promptCodexSync(options) {
698
698
  return { syncCodex: false };
699
699
  }
700
700
  logger.blank();
701
- const pathHint = codexSkillsPath ? ` (${codexSkillsPath})` : "";
701
+ const pathHint = codexPath ? ` (${codexPath})` : "";
702
702
  const result = await promptConfirm(
703
703
  `Also sync with Codex now?${pathHint}`,
704
704
  false
@@ -786,8 +786,15 @@ async function updateGitignore(targetDir, options = {}) {
786
786
 
787
787
  // src/features/codex-sync/codex-sync.ts
788
788
  import fs8 from "fs-extra";
789
+ import os from "os";
789
790
  import path10 from "path";
790
791
  var RESERVED_CODEX_SKILL_DIRS = /* @__PURE__ */ new Set([".system", "claude-commands"]);
792
+ var COMMAND_INSTRUCTION_PREFIX_REGEX = /^@\.\.\/instructions\//gm;
793
+ var COMMAND_INSTRUCTION_PREFIX_REPLACEMENT = "@../../../instructions/";
794
+ var MAX_REFERENCE_ISSUE_SAMPLES = 10;
795
+ var CLAUDE_STATE_FILE = ".claude.json";
796
+ var CLAUDE_LOCAL_MCP_FILE = ".mcp.json";
797
+ var CODEX_CONFIG_FILE = "config.toml";
791
798
  function normalizeLineEndings(content) {
792
799
  return content.replace(/\r\n/g, "\n");
793
800
  }
@@ -816,6 +823,10 @@ function extractDescriptionFromFrontmatter(markdown) {
816
823
  function buildCommandSkillContent(commandPath, commandRaw) {
817
824
  const commandName = path10.basename(commandPath, ".md");
818
825
  const normalizedCommand = normalizeLineEndings(commandRaw).trim();
826
+ const rewrittenCommand = normalizedCommand.replace(
827
+ COMMAND_INSTRUCTION_PREFIX_REGEX,
828
+ COMMAND_INSTRUCTION_PREFIX_REPLACEMENT
829
+ );
819
830
  const sourcePath = path10.resolve(commandPath);
820
831
  const description = extractDescriptionFromFrontmatter(normalizedCommand) || `${commandName} command imported from .claude/commands`;
821
832
  return `---
@@ -830,18 +841,344 @@ resolve them from the current workspace first; if missing, ask for the intended
830
841
 
831
842
  ---
832
843
 
833
- ${normalizedCommand}
844
+ ${rewrittenCommand}
834
845
  `;
835
846
  }
847
+ function isRecord(value) {
848
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
849
+ }
850
+ function asStringArray(value) {
851
+ if (!Array.isArray(value)) {
852
+ return void 0;
853
+ }
854
+ const items = value.filter(
855
+ (item) => typeof item === "string"
856
+ );
857
+ return items.length > 0 ? items : void 0;
858
+ }
859
+ function asStringRecord(value) {
860
+ if (!isRecord(value)) {
861
+ return void 0;
862
+ }
863
+ const entries = Object.entries(value).filter(
864
+ (entry) => typeof entry[1] === "string"
865
+ );
866
+ if (entries.length === 0) {
867
+ return void 0;
868
+ }
869
+ return Object.fromEntries(entries);
870
+ }
871
+ function normalizeClaudeMcpServer(value) {
872
+ if (!isRecord(value)) {
873
+ return void 0;
874
+ }
875
+ const type = typeof value.type === "string" ? value.type : void 0;
876
+ const command = typeof value.command === "string" ? value.command : void 0;
877
+ const args = asStringArray(value.args);
878
+ const env = asStringRecord(value.env);
879
+ const cwd = typeof value.cwd === "string" ? value.cwd : void 0;
880
+ const url = typeof value.url === "string" ? value.url : void 0;
881
+ const headers = asStringRecord(value.headers);
882
+ const bearerTokenEnvVar = typeof value.bearerTokenEnvVar === "string" ? value.bearerTokenEnvVar : typeof value.bearer_token_env_var === "string" ? value.bearer_token_env_var : void 0;
883
+ const envHttpHeaders = asStringRecord(value.envHttpHeaders) || asStringRecord(value.env_http_headers);
884
+ return {
885
+ type,
886
+ command,
887
+ args,
888
+ env,
889
+ cwd,
890
+ url,
891
+ headers,
892
+ bearerTokenEnvVar,
893
+ envHttpHeaders
894
+ };
895
+ }
896
+ function normalizeClaudeMcpServers(value) {
897
+ if (!isRecord(value)) {
898
+ return {};
899
+ }
900
+ const normalizedEntries = Object.entries(value).map(([name, server]) => [name, normalizeClaudeMcpServer(server)]).filter((entry) => Boolean(entry[1]));
901
+ return Object.fromEntries(normalizedEntries);
902
+ }
903
+ function getProjectState(claudeState, targetDir) {
904
+ const projects = claudeState.projects;
905
+ if (!isRecord(projects)) {
906
+ return void 0;
907
+ }
908
+ const normalizedTarget = path10.resolve(targetDir);
909
+ for (const [projectPath, projectState] of Object.entries(projects)) {
910
+ if (path10.resolve(projectPath) !== normalizedTarget) {
911
+ continue;
912
+ }
913
+ if (isRecord(projectState)) {
914
+ return projectState;
915
+ }
916
+ }
917
+ return void 0;
918
+ }
919
+ function filterMcpJsonServersByProjectState(servers, projectState) {
920
+ if (!projectState) {
921
+ return servers;
922
+ }
923
+ const enabled = asStringArray(projectState.enabledMcpjsonServers) || [];
924
+ const disabled = new Set(
925
+ asStringArray(projectState.disabledMcpjsonServers) || []
926
+ );
927
+ const enabledSet = new Set(enabled);
928
+ const hasEnabledFilter = enabledSet.size > 0;
929
+ const filteredEntries = Object.entries(servers).filter(([name]) => {
930
+ if (hasEnabledFilter && !enabledSet.has(name)) {
931
+ return false;
932
+ }
933
+ if (disabled.has(name)) {
934
+ return false;
935
+ }
936
+ return true;
937
+ });
938
+ return Object.fromEntries(filteredEntries);
939
+ }
940
+ async function readJsonFile(filePath) {
941
+ if (!await fs8.pathExists(filePath)) {
942
+ return void 0;
943
+ }
944
+ try {
945
+ const raw = await fs8.readFile(filePath, "utf8");
946
+ const parsed = JSON.parse(raw);
947
+ if (!isRecord(parsed)) {
948
+ return void 0;
949
+ }
950
+ return parsed;
951
+ } catch {
952
+ return void 0;
953
+ }
954
+ }
955
+ function toCodexMcpServer(server) {
956
+ const serverType = server.type?.toLowerCase();
957
+ const isHttp = serverType === "http" || Boolean(server.url);
958
+ if (isHttp) {
959
+ if (!server.url) {
960
+ return void 0;
961
+ }
962
+ return {
963
+ kind: "http",
964
+ url: server.url,
965
+ bearerTokenEnvVar: server.bearerTokenEnvVar,
966
+ httpHeaders: server.headers,
967
+ envHttpHeaders: server.envHttpHeaders
968
+ };
969
+ }
970
+ if (!server.command) {
971
+ return void 0;
972
+ }
973
+ return {
974
+ kind: "stdio",
975
+ command: server.command,
976
+ args: server.args,
977
+ env: server.env,
978
+ cwd: server.cwd
979
+ };
980
+ }
981
+ function tomlQuote(value) {
982
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
983
+ }
984
+ function tomlKey(value) {
985
+ return /^[A-Za-z0-9_-]+$/.test(value) ? value : tomlQuote(value);
986
+ }
987
+ function parseTomlPath(input) {
988
+ const pathInput = input.trim();
989
+ if (!pathInput) {
990
+ return void 0;
991
+ }
992
+ const segments = [];
993
+ let current = "";
994
+ let inQuotes = false;
995
+ let escaped = false;
996
+ for (const char of pathInput) {
997
+ if (inQuotes) {
998
+ if (escaped) {
999
+ current += char;
1000
+ escaped = false;
1001
+ continue;
1002
+ }
1003
+ if (char === "\\") {
1004
+ escaped = true;
1005
+ continue;
1006
+ }
1007
+ if (char === '"') {
1008
+ inQuotes = false;
1009
+ continue;
1010
+ }
1011
+ current += char;
1012
+ continue;
1013
+ }
1014
+ if (char === '"') {
1015
+ inQuotes = true;
1016
+ continue;
1017
+ }
1018
+ if (char === ".") {
1019
+ const value = current.trim();
1020
+ if (!value) {
1021
+ return void 0;
1022
+ }
1023
+ segments.push(value);
1024
+ current = "";
1025
+ continue;
1026
+ }
1027
+ current += char;
1028
+ }
1029
+ if (inQuotes || escaped) {
1030
+ return void 0;
1031
+ }
1032
+ const tail = current.trim();
1033
+ if (!tail) {
1034
+ return void 0;
1035
+ }
1036
+ segments.push(tail);
1037
+ return segments;
1038
+ }
1039
+ function stripCodexMcpSections(configToml) {
1040
+ const lines = normalizeLineEndings(configToml).split("\n");
1041
+ const kept = [];
1042
+ let droppingMcpSection = false;
1043
+ for (const line of lines) {
1044
+ const headerMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
1045
+ if (headerMatch) {
1046
+ const headerPath = parseTomlPath(headerMatch[1]);
1047
+ droppingMcpSection = Boolean(
1048
+ headerPath && headerPath.length > 0 && headerPath[0] === "mcp_servers"
1049
+ );
1050
+ }
1051
+ if (!droppingMcpSection) {
1052
+ kept.push(line);
1053
+ }
1054
+ }
1055
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
1056
+ }
1057
+ function renderTomlStringArray(values) {
1058
+ if (!values || values.length === 0) {
1059
+ return void 0;
1060
+ }
1061
+ return `[${values.map((value) => tomlQuote(value)).join(", ")}]`;
1062
+ }
1063
+ function renderTomlStringTable(header, values) {
1064
+ if (!values || Object.keys(values).length === 0) {
1065
+ return void 0;
1066
+ }
1067
+ const lines = Object.entries(values).sort((a, b) => a[0].localeCompare(b[0])).map(([key, value]) => `${tomlKey(key)} = ${tomlQuote(value)}`);
1068
+ return `${header}
1069
+ ${lines.join("\n")}`;
1070
+ }
1071
+ function renderCodexMcpServer(name, server) {
1072
+ const sectionRoot = `[mcp_servers.${tomlKey(name)}]`;
1073
+ const lines = [sectionRoot];
1074
+ if (server.kind === "http") {
1075
+ lines.push(`url = ${tomlQuote(server.url ?? "")}`);
1076
+ if (server.bearerTokenEnvVar) {
1077
+ lines.push(
1078
+ `bearer_token_env_var = ${tomlQuote(server.bearerTokenEnvVar)}`
1079
+ );
1080
+ }
1081
+ } else {
1082
+ lines.push(`command = ${tomlQuote(server.command ?? "")}`);
1083
+ const args = renderTomlStringArray(server.args);
1084
+ if (args) {
1085
+ lines.push(`args = ${args}`);
1086
+ }
1087
+ if (server.cwd) {
1088
+ lines.push(`cwd = ${tomlQuote(server.cwd)}`);
1089
+ }
1090
+ }
1091
+ const envSection = server.kind === "stdio" ? renderTomlStringTable(`[mcp_servers.${tomlKey(name)}.env]`, server.env) : void 0;
1092
+ const httpHeadersSection = server.kind === "http" ? renderTomlStringTable(
1093
+ `[mcp_servers.${tomlKey(name)}.http_headers]`,
1094
+ server.httpHeaders
1095
+ ) : void 0;
1096
+ const envHttpHeadersSection = server.kind === "http" ? renderTomlStringTable(
1097
+ `[mcp_servers.${tomlKey(name)}.env_http_headers]`,
1098
+ server.envHttpHeaders
1099
+ ) : void 0;
1100
+ const sections = [
1101
+ lines.join("\n"),
1102
+ envSection,
1103
+ httpHeadersSection,
1104
+ envHttpHeadersSection
1105
+ ].filter((section) => Boolean(section));
1106
+ return sections.join("\n\n");
1107
+ }
1108
+ function mergeCodexConfigToml(existingToml, renderedMcpBlocks) {
1109
+ const base = stripCodexMcpSections(existingToml);
1110
+ const mcp = renderedMcpBlocks.trim();
1111
+ if (!base) {
1112
+ return mcp;
1113
+ }
1114
+ if (!mcp) {
1115
+ return base;
1116
+ }
1117
+ return `${base}
1118
+
1119
+ ${mcp}`;
1120
+ }
1121
+ async function loadClaudeMcpServers(targetDir) {
1122
+ const homeDir = path10.resolve(os.homedir());
1123
+ const normalizedTarget = path10.resolve(targetDir);
1124
+ const isUserScope = normalizedTarget === homeDir;
1125
+ const claudeStatePath = path10.join(homeDir, CLAUDE_STATE_FILE);
1126
+ const claudeState = await readJsonFile(claudeStatePath);
1127
+ if (isUserScope) {
1128
+ return {
1129
+ scope: "global",
1130
+ servers: normalizeClaudeMcpServers(claudeState?.mcpServers)
1131
+ };
1132
+ }
1133
+ const projectState = claudeState ? getProjectState(claudeState, normalizedTarget) : void 0;
1134
+ const projectServers = normalizeClaudeMcpServers(projectState?.mcpServers);
1135
+ const localMcpPath = path10.join(normalizedTarget, CLAUDE_LOCAL_MCP_FILE);
1136
+ const localMcpState = await readJsonFile(localMcpPath);
1137
+ const localMcpServers = filterMcpJsonServersByProjectState(
1138
+ normalizeClaudeMcpServers(localMcpState?.mcpServers),
1139
+ projectState
1140
+ );
1141
+ return {
1142
+ scope: "local",
1143
+ servers: {
1144
+ ...localMcpServers,
1145
+ ...projectServers
1146
+ }
1147
+ };
1148
+ }
1149
+ async function syncMcpServers(targetDir) {
1150
+ const homeDir = path10.resolve(os.homedir());
1151
+ const normalizedTarget = path10.resolve(targetDir);
1152
+ const isUserScope = normalizedTarget === homeDir;
1153
+ const codexBaseDir = isUserScope ? path10.join(homeDir, ".codex") : path10.join(normalizedTarget, ".codex");
1154
+ const codexMcpConfigPath = path10.join(codexBaseDir, CODEX_CONFIG_FILE);
1155
+ const { scope, servers } = await loadClaudeMcpServers(normalizedTarget);
1156
+ const codexServers = Object.entries(servers).map(([name, server]) => [name, toCodexMcpServer(server)]).filter((entry) => Boolean(entry[1])).sort((a, b) => a[0].localeCompare(b[0]));
1157
+ const renderedMcpBlocks = codexServers.map(([name, server]) => renderCodexMcpServer(name, server)).join("\n\n");
1158
+ const existingConfig = await fs8.pathExists(codexMcpConfigPath) ? await fs8.readFile(codexMcpConfigPath, "utf8") : "";
1159
+ const nextConfig = mergeCodexConfigToml(existingConfig, renderedMcpBlocks);
1160
+ await fs8.ensureDir(codexBaseDir);
1161
+ await fs8.writeFile(codexMcpConfigPath, `${nextConfig.trimEnd()}
1162
+ `);
1163
+ return {
1164
+ codexMcpConfigPath,
1165
+ syncedMcpServers: codexServers.length,
1166
+ scope
1167
+ };
1168
+ }
836
1169
  async function syncSkills(claudeSkillsDir, codexSkillsDir) {
837
1170
  await fs8.ensureDir(codexSkillsDir);
838
1171
  let sourceSkillNames = [];
839
1172
  if (await fs8.pathExists(claudeSkillsDir)) {
840
- const sourceEntries = await fs8.readdir(claudeSkillsDir, { withFileTypes: true });
1173
+ const sourceEntries = await fs8.readdir(claudeSkillsDir, {
1174
+ withFileTypes: true
1175
+ });
841
1176
  sourceSkillNames = sourceEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => name !== ".system");
842
1177
  }
843
1178
  const sourceSkillSet = new Set(sourceSkillNames);
844
- const codexEntries = await fs8.readdir(codexSkillsDir, { withFileTypes: true });
1179
+ const codexEntries = await fs8.readdir(codexSkillsDir, {
1180
+ withFileTypes: true
1181
+ });
845
1182
  for (const entry of codexEntries) {
846
1183
  if (!entry.isDirectory()) {
847
1184
  continue;
@@ -883,26 +1220,126 @@ async function syncCommands(claudeCommandsDir, codexCommandsDir) {
883
1220
  const skillDir = path10.join(codexCommandsDir, commandName);
884
1221
  const skillPath = path10.join(skillDir, "SKILL.md");
885
1222
  await fs8.ensureDir(skillDir);
886
- await fs8.writeFile(skillPath, buildCommandSkillContent(commandPath, commandRaw));
1223
+ await fs8.writeFile(
1224
+ skillPath,
1225
+ buildCommandSkillContent(commandPath, commandRaw)
1226
+ );
887
1227
  }
888
1228
  return commandFiles.length;
889
1229
  }
1230
+ async function syncInstructions(claudeInstructionsDir, codexInstructionsDir) {
1231
+ if (!await fs8.pathExists(claudeInstructionsDir)) {
1232
+ await fs8.remove(codexInstructionsDir);
1233
+ return 0;
1234
+ }
1235
+ await fs8.remove(codexInstructionsDir);
1236
+ await fs8.ensureDir(codexInstructionsDir);
1237
+ const counter = { files: 0, directories: 0 };
1238
+ await copyRecursive(claudeInstructionsDir, codexInstructionsDir, counter);
1239
+ return counter.files;
1240
+ }
1241
+ function extractReferenceCandidate(line) {
1242
+ const trimmed = line.trim();
1243
+ if (!trimmed.startsWith("@")) {
1244
+ return void 0;
1245
+ }
1246
+ const [candidate] = trimmed.slice(1).split(/\s+/);
1247
+ if (!candidate || candidate.startsWith("#") || candidate.includes("://")) {
1248
+ return void 0;
1249
+ }
1250
+ const looksLikePath = candidate.startsWith("./") || candidate.startsWith("../") || candidate.startsWith("/") || candidate.includes("/") || /\.[a-z0-9]+$/i.test(candidate);
1251
+ if (!looksLikePath) {
1252
+ return void 0;
1253
+ }
1254
+ return candidate;
1255
+ }
1256
+ async function collectSkillMarkdownFiles(rootDir, collector = []) {
1257
+ if (!await fs8.pathExists(rootDir)) {
1258
+ return collector;
1259
+ }
1260
+ const entries = await fs8.readdir(rootDir, { withFileTypes: true });
1261
+ for (const entry of entries) {
1262
+ const fullPath = path10.join(rootDir, entry.name);
1263
+ if (entry.isDirectory()) {
1264
+ await collectSkillMarkdownFiles(fullPath, collector);
1265
+ continue;
1266
+ }
1267
+ if (entry.isFile() && entry.name === "SKILL.md") {
1268
+ collector.push(fullPath);
1269
+ }
1270
+ }
1271
+ return collector;
1272
+ }
1273
+ async function validateSkillReferences(codexSkillsDir) {
1274
+ const skillFiles = await collectSkillMarkdownFiles(codexSkillsDir);
1275
+ let count = 0;
1276
+ const samples = [];
1277
+ for (const skillFile of skillFiles) {
1278
+ const content = await fs8.readFile(skillFile, "utf8");
1279
+ const lines = normalizeLineEndings(content).split("\n");
1280
+ for (const line of lines) {
1281
+ const candidate = extractReferenceCandidate(line);
1282
+ if (!candidate) {
1283
+ continue;
1284
+ }
1285
+ const referencePath = candidate.split(/[?#]/, 1)[0];
1286
+ if (!referencePath) {
1287
+ continue;
1288
+ }
1289
+ const resolvedPath = path10.resolve(path10.dirname(skillFile), referencePath);
1290
+ if (await fs8.pathExists(resolvedPath)) {
1291
+ continue;
1292
+ }
1293
+ count += 1;
1294
+ if (samples.length < MAX_REFERENCE_ISSUE_SAMPLES) {
1295
+ samples.push({
1296
+ skillPath: skillFile,
1297
+ reference: candidate,
1298
+ resolvedPath
1299
+ });
1300
+ }
1301
+ }
1302
+ }
1303
+ return { count, samples };
1304
+ }
890
1305
  async function syncWithCodex(targetDir) {
891
1306
  const codexHome = path10.resolve(targetDir, ".codex");
892
1307
  const codexSkillsDir = path10.join(codexHome, "skills");
893
1308
  const codexCommandsDir = path10.join(codexSkillsDir, "claude-commands");
1309
+ const codexInstructionsDir = path10.join(codexHome, "instructions");
894
1310
  await fs8.ensureDir(codexSkillsDir);
895
1311
  const claudeRootDir = path10.join(targetDir, ".claude");
896
1312
  const claudeSkillsDir = path10.join(claudeRootDir, "skills");
897
1313
  const claudeCommandsDir = path10.join(claudeRootDir, "commands");
1314
+ const claudeInstructionsDir = path10.join(claudeRootDir, "instructions");
898
1315
  const syncedSkills = await syncSkills(claudeSkillsDir, codexSkillsDir);
899
- const syncedCommands = await syncCommands(claudeCommandsDir, codexCommandsDir);
1316
+ const syncedInstructions = await syncInstructions(
1317
+ claudeInstructionsDir,
1318
+ codexInstructionsDir
1319
+ );
1320
+ const syncedCommands = await syncCommands(
1321
+ claudeCommandsDir,
1322
+ codexCommandsDir
1323
+ );
1324
+ const {
1325
+ codexMcpConfigPath,
1326
+ syncedMcpServers,
1327
+ scope: mcpScope
1328
+ } = await syncMcpServers(targetDir);
1329
+ const { count: referenceIssueCount, samples: referenceIssueSamples } = await validateSkillReferences(codexSkillsDir);
900
1330
  return {
901
1331
  codexHome,
902
1332
  codexSkillsDir,
903
1333
  codexCommandsDir,
1334
+ codexInstructionsDir,
904
1335
  syncedSkills,
905
- syncedCommands
1336
+ syncedCommands,
1337
+ syncedInstructions,
1338
+ codexMcpConfigPath,
1339
+ syncedMcpServers,
1340
+ mcpScope,
1341
+ referenceIssueCount,
1342
+ referenceIssueSamples
906
1343
  };
907
1344
  }
908
1345
 
@@ -1056,7 +1493,7 @@ var init = async (options) => {
1056
1493
  const { scope } = await promptScopeSelection({
1057
1494
  providedScope: options.scope
1058
1495
  });
1059
- const targetDir = scope === "user" ? os.homedir() : projectDir;
1496
+ const targetDir = scope === "user" ? os2.homedir() : projectDir;
1060
1497
  const isUserScope = scope === "user";
1061
1498
  if (isUserScope) {
1062
1499
  logger.blank();
@@ -1102,14 +1539,14 @@ var init = async (options) => {
1102
1539
  hasScripts,
1103
1540
  scope
1104
1541
  );
1105
- const codexSkillsPath = path11.join(targetDir, ".codex", "skills");
1542
+ const codexSyncPath = path11.join(targetDir, ".codex");
1106
1543
  const { syncCodex } = await promptCodexSync({
1107
1544
  providedSyncCodex: options.syncCodex,
1108
- codexSkillsPath
1545
+ codexPath: codexSyncPath
1109
1546
  });
1110
1547
  if (syncCodex) {
1111
1548
  logger.blank();
1112
- logger.info("Syncing .claude skills/commands to Codex...");
1549
+ logger.info("Syncing .claude skills/commands/instructions/MCP to Codex...");
1113
1550
  try {
1114
1551
  const result = await syncWithCodex(targetDir);
1115
1552
  if (result.syncedSkills > 0) {
@@ -1118,12 +1555,33 @@ var init = async (options) => {
1118
1555
  if (result.syncedCommands > 0) {
1119
1556
  logger.step(`Commands synced: ${result.syncedCommands}`);
1120
1557
  }
1121
- if (result.syncedSkills === 0 && result.syncedCommands === 0) {
1558
+ if (result.syncedInstructions > 0) {
1559
+ logger.step(`Instructions synced: ${result.syncedInstructions}`);
1560
+ }
1561
+ if (result.syncedMcpServers > 0) {
1562
+ logger.step(
1563
+ `MCP servers synced (${result.mcpScope}): ${result.syncedMcpServers}`
1564
+ );
1565
+ }
1566
+ if (result.syncedSkills === 0 && result.syncedCommands === 0 && result.syncedInstructions === 0 && result.syncedMcpServers === 0) {
1122
1567
  logger.warn(
1123
- "Nothing was synced. .claude/skills and .claude/commands were not found."
1568
+ "Nothing was synced. .claude/skills, .claude/commands, .claude/instructions, and Claude MCP settings were not found."
1124
1569
  );
1125
1570
  } else {
1126
1571
  logger.success(`Codex sync complete: ${result.codexSkillsDir}`);
1572
+ logger.step(`Codex MCP config: ${result.codexMcpConfigPath}`);
1573
+ if (result.referenceIssueCount > 0) {
1574
+ logger.warn(
1575
+ `Codex skill reference issues found: ${result.referenceIssueCount} (showing up to ${result.referenceIssueSamples.length})`
1576
+ );
1577
+ for (const issue of result.referenceIssueSamples) {
1578
+ const skillPath = path11.relative(targetDir, issue.skillPath);
1579
+ const resolvedPath = path11.relative(targetDir, issue.resolvedPath);
1580
+ logger.step(
1581
+ `${skillPath} -> @${issue.reference} (missing: ${resolvedPath})`
1582
+ );
1583
+ }
1584
+ }
1127
1585
  }
1128
1586
  } catch (error) {
1129
1587
  logger.warn(
@@ -1144,13 +1602,13 @@ var init = async (options) => {
1144
1602
 
1145
1603
  // src/index.ts
1146
1604
  var program = new Command();
1147
- program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.9");
1605
+ program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.10");
1148
1606
  program.option(
1149
1607
  "-t, --template <names>",
1150
1608
  "template names (comma-separated: tanstack-start,hono)"
1151
1609
  ).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").option("-a, --agents", "install agents to .claude/agents/").option(
1152
1610
  "--sync-codex",
1153
- "sync installed .claude skills/commands to selected-scope .codex/skills/"
1611
+ "sync installed .claude skills/commands/instructions and Claude MCP settings to selected-scope .codex/"
1154
1612
  ).option("--scope <scope>", "installation scope (project|user)").action(async (options) => {
1155
1613
  banner();
1156
1614
  if (options.list) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.7.9",
3
+ "version": "0.7.10",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",