@kood/claude-code 0.7.7 → 0.7.8

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 +183 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22,8 +22,8 @@ var banner = () => {
22
22
  };
23
23
 
24
24
  // src/commands/init.ts
25
- import fs8 from "fs-extra";
26
- import os from "os";
25
+ import fs9 from "fs-extra";
26
+ import os2 from "os";
27
27
 
28
28
  // src/features/templates/template-path-resolver.ts
29
29
  import path2 from "path";
@@ -688,15 +688,37 @@ async function promptScopeSelection(options) {
688
688
  }
689
689
  return { scope: response.value };
690
690
  }
691
+ async function promptCodexSync(options) {
692
+ const { providedSyncCodex } = options;
693
+ if (providedSyncCodex !== void 0) {
694
+ return { syncCodex: providedSyncCodex };
695
+ }
696
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
697
+ return { syncCodex: false };
698
+ }
699
+ logger.blank();
700
+ const result = await promptConfirm(
701
+ "Also sync with Codex now? (~/.codex/skills/)",
702
+ false
703
+ );
704
+ return { syncCodex: result.confirmed };
705
+ }
691
706
 
692
707
  // src/shared/gitignore-manager.ts
693
708
  import fs7 from "fs-extra";
694
709
  import path9 from "path";
695
710
  var CLAUDE_GENERATED_FOLDERS = [
711
+ ".claude/brainstorms/",
712
+ ".claude/crawler/",
713
+ ".claude/first-principles/",
714
+ ".claude/genius-ideas/",
696
715
  ".claude/plan/",
716
+ ".claude/plans/",
697
717
  ".claude/ralph/",
698
718
  ".claude/refactor/",
699
- ".claude/prd/"
719
+ ".claude/prd/",
720
+ ".claude/research/",
721
+ ".claude/validation-results/"
700
722
  ];
701
723
  function normalizeIgnorePattern(pattern) {
702
724
  return pattern.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "").trim();
@@ -757,6 +779,126 @@ async function updateGitignore(targetDir) {
757
779
  }
758
780
  }
759
781
 
782
+ // src/features/codex-sync/codex-sync.ts
783
+ import fs8 from "fs-extra";
784
+ import os from "os";
785
+ import path10 from "path";
786
+ function resolveCodexHome() {
787
+ const envCodexHome = process.env.CODEX_HOME?.trim();
788
+ if (envCodexHome) {
789
+ return path10.resolve(envCodexHome);
790
+ }
791
+ return path10.join(os.homedir(), ".codex");
792
+ }
793
+ function normalizeLineEndings(content) {
794
+ return content.replace(/\r\n/g, "\n");
795
+ }
796
+ function yamlQuote(value) {
797
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
798
+ }
799
+ function extractDescriptionFromFrontmatter(markdown) {
800
+ const frontmatterMatch = markdown.match(/^---\n([\s\S]*?)\n---\n?/);
801
+ if (!frontmatterMatch) {
802
+ return void 0;
803
+ }
804
+ const lines = frontmatterMatch[1].split("\n");
805
+ for (const rawLine of lines) {
806
+ const line = rawLine.trim();
807
+ if (!line.startsWith("description:")) {
808
+ continue;
809
+ }
810
+ const value = line.slice("description:".length).trim();
811
+ if (!value) {
812
+ return void 0;
813
+ }
814
+ return value.replace(/^['"]|['"]$/g, "");
815
+ }
816
+ return void 0;
817
+ }
818
+ function buildCommandSkillContent(commandPath, commandRaw) {
819
+ const commandName = path10.basename(commandPath, ".md");
820
+ const normalizedCommand = normalizeLineEndings(commandRaw).trim();
821
+ const sourcePath = path10.resolve(commandPath);
822
+ const description = extractDescriptionFromFrontmatter(normalizedCommand) || `${commandName} command imported from .claude/commands`;
823
+ return `---
824
+ name: ${yamlQuote(commandName)}
825
+ description: ${yamlQuote(description)}
826
+ ---
827
+
828
+ Imported command source: ${sourcePath}
829
+
830
+ Use this as a command playbook. If scripts are referenced with .claude-relative paths,
831
+ resolve them from the current workspace first; if missing, ask for the intended repo root.
832
+
833
+ ---
834
+
835
+ ${normalizedCommand}
836
+ `;
837
+ }
838
+ async function syncSkills(claudeSkillsDir, codexSkillsDir) {
839
+ if (!await fs8.pathExists(claudeSkillsDir)) {
840
+ return 0;
841
+ }
842
+ const entries = await fs8.readdir(claudeSkillsDir, { withFileTypes: true });
843
+ let syncedSkills = 0;
844
+ for (const entry of entries) {
845
+ if (!entry.isDirectory()) {
846
+ continue;
847
+ }
848
+ if (entry.name === ".system") {
849
+ continue;
850
+ }
851
+ const src = path10.join(claudeSkillsDir, entry.name);
852
+ const dest = path10.join(codexSkillsDir, entry.name);
853
+ if (await fs8.pathExists(dest)) {
854
+ await fs8.remove(dest);
855
+ }
856
+ await copyRecursive(src, dest, { files: 0, directories: 0 });
857
+ syncedSkills++;
858
+ }
859
+ return syncedSkills;
860
+ }
861
+ async function syncCommands(claudeCommandsDir, codexCommandsDir) {
862
+ if (!await fs8.pathExists(claudeCommandsDir)) {
863
+ return 0;
864
+ }
865
+ const entries = await fs8.readdir(claudeCommandsDir, { withFileTypes: true });
866
+ const commandFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
867
+ if (commandFiles.length === 0) {
868
+ return 0;
869
+ }
870
+ await fs8.remove(codexCommandsDir);
871
+ await fs8.ensureDir(codexCommandsDir);
872
+ for (const commandFile of commandFiles) {
873
+ const commandPath = path10.join(claudeCommandsDir, commandFile);
874
+ const commandRaw = await fs8.readFile(commandPath, "utf8");
875
+ const commandName = path10.basename(commandFile, ".md");
876
+ const skillDir = path10.join(codexCommandsDir, commandName);
877
+ const skillPath = path10.join(skillDir, "SKILL.md");
878
+ await fs8.ensureDir(skillDir);
879
+ await fs8.writeFile(skillPath, buildCommandSkillContent(commandPath, commandRaw));
880
+ }
881
+ return commandFiles.length;
882
+ }
883
+ async function syncWithCodex(targetDir) {
884
+ const codexHome = resolveCodexHome();
885
+ const codexSkillsDir = path10.join(codexHome, "skills");
886
+ const codexCommandsDir = path10.join(codexSkillsDir, "claude-commands");
887
+ await fs8.ensureDir(codexSkillsDir);
888
+ const claudeRootDir = path10.join(targetDir, ".claude");
889
+ const claudeSkillsDir = path10.join(claudeRootDir, "skills");
890
+ const claudeCommandsDir = path10.join(claudeRootDir, "commands");
891
+ const syncedSkills = await syncSkills(claudeSkillsDir, codexSkillsDir);
892
+ const syncedCommands = await syncCommands(claudeCommandsDir, codexCommandsDir);
893
+ return {
894
+ codexHome,
895
+ codexSkillsDir,
896
+ codexCommandsDir,
897
+ syncedSkills,
898
+ syncedCommands
899
+ };
900
+ }
901
+
760
902
  // src/commands/init.ts
761
903
  var TEMPLATE_DESCRIPTIONS = {
762
904
  "tanstack-start": "TanStack Start + React \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
@@ -766,7 +908,7 @@ var TEMPLATE_DESCRIPTIONS = {
766
908
  };
767
909
  async function validateTargetDirectory(targetDir) {
768
910
  try {
769
- const stat = await fs8.stat(targetDir);
911
+ const stat = await fs9.stat(targetDir);
770
912
  if (!stat.isDirectory()) {
771
913
  logger.error(`Target is not a directory: ${targetDir}`);
772
914
  process.exit(1);
@@ -780,7 +922,7 @@ async function validateTargetDirectory(targetDir) {
780
922
  process.exit(1);
781
923
  }
782
924
  try {
783
- await fs8.access(targetDir, fs8.constants.W_OK);
925
+ await fs9.access(targetDir, fs9.constants.W_OK);
784
926
  } catch {
785
927
  logger.error(`No write permission for: ${targetDir}`);
786
928
  process.exit(1);
@@ -907,7 +1049,7 @@ var init = async (options) => {
907
1049
  const { scope } = await promptScopeSelection({
908
1050
  providedScope: options.scope
909
1051
  });
910
- const targetDir = scope === "user" ? os.homedir() : projectDir;
1052
+ const targetDir = scope === "user" ? os2.homedir() : projectDir;
911
1053
  const isUserScope = scope === "user";
912
1054
  if (isUserScope) {
913
1055
  logger.blank();
@@ -962,15 +1104,46 @@ var init = async (options) => {
962
1104
  );
963
1105
  }
964
1106
  }
1107
+ const { syncCodex } = await promptCodexSync({
1108
+ providedSyncCodex: options.syncCodex
1109
+ });
1110
+ if (!syncCodex) {
1111
+ return;
1112
+ }
1113
+ logger.blank();
1114
+ logger.info("Syncing .claude skills/commands to Codex...");
1115
+ try {
1116
+ const result = await syncWithCodex(targetDir);
1117
+ if (result.syncedSkills > 0) {
1118
+ logger.step(`Skills synced: ${result.syncedSkills}`);
1119
+ }
1120
+ if (result.syncedCommands > 0) {
1121
+ logger.step(`Commands synced: ${result.syncedCommands}`);
1122
+ }
1123
+ if (result.syncedSkills === 0 && result.syncedCommands === 0) {
1124
+ logger.warn(
1125
+ "Nothing was synced. .claude/skills and .claude/commands were not found."
1126
+ );
1127
+ } else {
1128
+ logger.success(`Codex sync complete: ${result.codexSkillsDir}`);
1129
+ }
1130
+ } catch (error) {
1131
+ logger.warn(
1132
+ `Codex sync failed: ${error instanceof Error ? error.message : "Unknown error"}`
1133
+ );
1134
+ }
965
1135
  };
966
1136
 
967
1137
  // src/index.ts
968
1138
  var program = new Command();
969
- program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.7");
1139
+ program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.8");
970
1140
  program.option(
971
1141
  "-t, --template <names>",
972
1142
  "template names (comma-separated: tanstack-start,hono)"
973
- ).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("--scope <scope>", "installation scope (project|user)").action(async (options) => {
1143
+ ).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(
1144
+ "--sync-codex",
1145
+ "sync installed .claude skills/commands to Codex (~/.codex/skills/)"
1146
+ ).option("--scope <scope>", "installation scope (project|user)").action(async (options) => {
974
1147
  banner();
975
1148
  if (options.list) {
976
1149
  const templates = await listAvailableTemplates();
@@ -986,7 +1159,8 @@ program.option(
986
1159
  scope: options.scope,
987
1160
  skills: options.skills,
988
1161
  commands: options.commands,
989
- agents: options.agents
1162
+ agents: options.agents,
1163
+ syncCodex: options.syncCodex
990
1164
  });
991
1165
  });
992
1166
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",