@kood/claude-code 0.7.9 → 0.7.11
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 +549 -34
- package/package.json +1 -1
- package/templates/.claude/agents/codex.md +58 -248
- package/templates/.claude/commands/cancel-ralph.md +18 -0
- package/templates/.claude/commands/git-merge.md +67 -0
- package/templates/.claude/commands/ralph-loop.md +18 -0
- package/templates/.claude/hooks/ralph-stop-hook.sh +124 -0
- package/templates/.claude/scripts/git/git-merge.sh +73 -0
- package/templates/.claude/scripts/setup-ralph-loop.sh +161 -0
- package/templates/.claude/skills/codex/SKILL.md +110 -458
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
|
|
26
|
+
import os2 from "os";
|
|
27
27
|
import path11 from "path";
|
|
28
28
|
|
|
29
29
|
// src/features/templates/template-path-resolver.ts
|
|
@@ -327,6 +327,7 @@ var copyCommands = createExtrasCopier("commands");
|
|
|
327
327
|
var copyAgents = createExtrasCopier("agents");
|
|
328
328
|
var copyInstructions = createExtrasCopier("instructions");
|
|
329
329
|
var copyScripts = createExtrasCopier("scripts");
|
|
330
|
+
var copyHooks = createExtrasCopier("hooks");
|
|
330
331
|
var getSkillsToInstall = async (skillsSrc, templates) => {
|
|
331
332
|
const metadataMap = await loadAllSkillMetadata(skillsSrc);
|
|
332
333
|
const isNonUITemplate = templates.some((t) => NON_UI_TEMPLATES.includes(t));
|
|
@@ -372,6 +373,7 @@ var checkExistingClaudeFiles = async (targetDir) => {
|
|
|
372
373
|
const agentsDir = path8.join(targetDir, ".claude", "agents");
|
|
373
374
|
const instructionsDir = path8.join(targetDir, ".claude", "instructions");
|
|
374
375
|
const scriptsDir = path8.join(targetDir, ".claude", "scripts");
|
|
376
|
+
const hooksDir = path8.join(targetDir, ".claude", "hooks");
|
|
375
377
|
if (await fs6.pathExists(skillsDir)) {
|
|
376
378
|
existingFiles.push(".claude/skills/");
|
|
377
379
|
}
|
|
@@ -387,6 +389,9 @@ var checkExistingClaudeFiles = async (targetDir) => {
|
|
|
387
389
|
if (await fs6.pathExists(scriptsDir)) {
|
|
388
390
|
existingFiles.push(".claude/scripts/");
|
|
389
391
|
}
|
|
392
|
+
if (await fs6.pathExists(hooksDir)) {
|
|
393
|
+
existingFiles.push(".claude/hooks/");
|
|
394
|
+
}
|
|
390
395
|
return existingFiles;
|
|
391
396
|
};
|
|
392
397
|
var checkAllExtrasExist = async (_templates) => {
|
|
@@ -396,12 +401,21 @@ var checkAllExtrasExist = async (_templates) => {
|
|
|
396
401
|
const agentsSrc = path8.join(claudeDir, "agents");
|
|
397
402
|
const instructionsSrc = path8.join(claudeDir, "instructions");
|
|
398
403
|
const scriptsSrc = path8.join(claudeDir, "scripts");
|
|
404
|
+
const hooksSrc = path8.join(claudeDir, "hooks");
|
|
399
405
|
const hasSkills = await hasFiles(skillsSrc);
|
|
400
406
|
const hasCommands = await hasFiles(commandsSrc);
|
|
401
407
|
const hasAgents = await hasFiles(agentsSrc);
|
|
402
408
|
const hasInstructions = await hasFiles(instructionsSrc);
|
|
403
409
|
const hasScripts = await hasFiles(scriptsSrc);
|
|
404
|
-
|
|
410
|
+
const hasHooks = await hasFiles(hooksSrc);
|
|
411
|
+
return {
|
|
412
|
+
hasSkills,
|
|
413
|
+
hasCommands,
|
|
414
|
+
hasAgents,
|
|
415
|
+
hasInstructions,
|
|
416
|
+
hasScripts,
|
|
417
|
+
hasHooks
|
|
418
|
+
};
|
|
405
419
|
};
|
|
406
420
|
|
|
407
421
|
// src/features/extras/extras-installer.ts
|
|
@@ -475,6 +489,22 @@ async function installScriptsIfNeeded(templates, targetDir, shouldInstall, hasSc
|
|
|
475
489
|
);
|
|
476
490
|
return scriptsResult;
|
|
477
491
|
}
|
|
492
|
+
async function installHooksIfNeeded(templates, targetDir, shouldInstall, hasHooks) {
|
|
493
|
+
if (!shouldInstall) {
|
|
494
|
+
return { files: 0, directories: 0 };
|
|
495
|
+
}
|
|
496
|
+
if (!hasHooks) {
|
|
497
|
+
logger.warn("No hooks found in selected templates.");
|
|
498
|
+
return { files: 0, directories: 0 };
|
|
499
|
+
}
|
|
500
|
+
logger.blank();
|
|
501
|
+
logger.info("Installing hooks...");
|
|
502
|
+
const hooksResult = await copyHooks(templates, targetDir);
|
|
503
|
+
logger.success(
|
|
504
|
+
`Hooks: ${hooksResult.files} files, ${hooksResult.directories} directories`
|
|
505
|
+
);
|
|
506
|
+
return hooksResult;
|
|
507
|
+
}
|
|
478
508
|
async function installInstructionsIfNeeded(templates, targetDir, shouldInstall, hasInstructions) {
|
|
479
509
|
if (!shouldInstall) {
|
|
480
510
|
return { files: 0, directories: 0 };
|
|
@@ -497,9 +527,10 @@ async function installExtras(templates, targetDir, flags, availability) {
|
|
|
497
527
|
installCommands,
|
|
498
528
|
installAgents,
|
|
499
529
|
installInstructions,
|
|
500
|
-
installScripts
|
|
530
|
+
installScripts,
|
|
531
|
+
installHooks
|
|
501
532
|
} = flags;
|
|
502
|
-
if (!installSkills && !installCommands && !installAgents && !installInstructions && !installScripts) {
|
|
533
|
+
if (!installSkills && !installCommands && !installAgents && !installInstructions && !installScripts && !installHooks) {
|
|
503
534
|
return { files: 0, directories: 0 };
|
|
504
535
|
}
|
|
505
536
|
const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
|
|
@@ -534,8 +565,14 @@ async function installExtras(templates, targetDir, flags, availability) {
|
|
|
534
565
|
installScripts,
|
|
535
566
|
availability.hasScripts
|
|
536
567
|
);
|
|
537
|
-
const
|
|
538
|
-
|
|
568
|
+
const hooksResult = await installHooksIfNeeded(
|
|
569
|
+
templates,
|
|
570
|
+
targetDir,
|
|
571
|
+
installHooks,
|
|
572
|
+
availability.hasHooks
|
|
573
|
+
);
|
|
574
|
+
const totalFiles = skillsResult.files + commandsResult.files + agentsResult.files + instructionsResult.files + scriptsResult.files + hooksResult.files;
|
|
575
|
+
const totalDirectories = skillsResult.directories + commandsResult.directories + agentsResult.directories + instructionsResult.directories + scriptsResult.directories + hooksResult.directories;
|
|
539
576
|
return { files: totalFiles, directories: totalDirectories };
|
|
540
577
|
}
|
|
541
578
|
|
|
@@ -620,19 +657,22 @@ async function promptExtrasSelection(options) {
|
|
|
620
657
|
agents,
|
|
621
658
|
instructions,
|
|
622
659
|
scripts,
|
|
660
|
+
hooks,
|
|
623
661
|
hasSkills,
|
|
624
662
|
hasCommands,
|
|
625
663
|
hasAgents,
|
|
626
664
|
hasInstructions,
|
|
627
|
-
hasScripts
|
|
665
|
+
hasScripts,
|
|
666
|
+
hasHooks
|
|
628
667
|
} = options;
|
|
629
668
|
let installSkills = skills ?? false;
|
|
630
669
|
let installCommands = commands ?? false;
|
|
631
670
|
const installAgents = agents ?? hasAgents;
|
|
632
671
|
const installInstructions = instructions ?? hasInstructions;
|
|
633
672
|
const installScripts = scripts ?? hasScripts;
|
|
634
|
-
const
|
|
635
|
-
|
|
673
|
+
const installHooks = hooks ?? hasHooks;
|
|
674
|
+
const noOptionsProvided = skills === void 0 && commands === void 0 && agents === void 0 && instructions === void 0 && scripts === void 0 && hooks === void 0;
|
|
675
|
+
if (noOptionsProvided && (hasSkills || hasCommands || hasAgents || hasInstructions || hasScripts || hasHooks)) {
|
|
636
676
|
logger.blank();
|
|
637
677
|
if (hasSkills) {
|
|
638
678
|
const result = await promptConfirm(
|
|
@@ -654,7 +694,8 @@ async function promptExtrasSelection(options) {
|
|
|
654
694
|
installCommands,
|
|
655
695
|
installAgents,
|
|
656
696
|
installInstructions,
|
|
657
|
-
installScripts
|
|
697
|
+
installScripts,
|
|
698
|
+
installHooks
|
|
658
699
|
};
|
|
659
700
|
}
|
|
660
701
|
async function promptScopeSelection(options) {
|
|
@@ -690,7 +731,7 @@ async function promptScopeSelection(options) {
|
|
|
690
731
|
return { scope: response.value };
|
|
691
732
|
}
|
|
692
733
|
async function promptCodexSync(options) {
|
|
693
|
-
const { providedSyncCodex,
|
|
734
|
+
const { providedSyncCodex, codexPath } = options;
|
|
694
735
|
if (providedSyncCodex !== void 0) {
|
|
695
736
|
return { syncCodex: providedSyncCodex };
|
|
696
737
|
}
|
|
@@ -698,7 +739,7 @@ async function promptCodexSync(options) {
|
|
|
698
739
|
return { syncCodex: false };
|
|
699
740
|
}
|
|
700
741
|
logger.blank();
|
|
701
|
-
const pathHint =
|
|
742
|
+
const pathHint = codexPath ? ` (${codexPath})` : "";
|
|
702
743
|
const result = await promptConfirm(
|
|
703
744
|
`Also sync with Codex now?${pathHint}`,
|
|
704
745
|
false
|
|
@@ -786,8 +827,15 @@ async function updateGitignore(targetDir, options = {}) {
|
|
|
786
827
|
|
|
787
828
|
// src/features/codex-sync/codex-sync.ts
|
|
788
829
|
import fs8 from "fs-extra";
|
|
830
|
+
import os from "os";
|
|
789
831
|
import path10 from "path";
|
|
790
832
|
var RESERVED_CODEX_SKILL_DIRS = /* @__PURE__ */ new Set([".system", "claude-commands"]);
|
|
833
|
+
var COMMAND_INSTRUCTION_PREFIX_REGEX = /^@\.\.\/instructions\//gm;
|
|
834
|
+
var COMMAND_INSTRUCTION_PREFIX_REPLACEMENT = "@../../../instructions/";
|
|
835
|
+
var MAX_REFERENCE_ISSUE_SAMPLES = 10;
|
|
836
|
+
var CLAUDE_STATE_FILE = ".claude.json";
|
|
837
|
+
var CLAUDE_LOCAL_MCP_FILE = ".mcp.json";
|
|
838
|
+
var CODEX_CONFIG_FILE = "config.toml";
|
|
791
839
|
function normalizeLineEndings(content) {
|
|
792
840
|
return content.replace(/\r\n/g, "\n");
|
|
793
841
|
}
|
|
@@ -816,6 +864,10 @@ function extractDescriptionFromFrontmatter(markdown) {
|
|
|
816
864
|
function buildCommandSkillContent(commandPath, commandRaw) {
|
|
817
865
|
const commandName = path10.basename(commandPath, ".md");
|
|
818
866
|
const normalizedCommand = normalizeLineEndings(commandRaw).trim();
|
|
867
|
+
const rewrittenCommand = normalizedCommand.replace(
|
|
868
|
+
COMMAND_INSTRUCTION_PREFIX_REGEX,
|
|
869
|
+
COMMAND_INSTRUCTION_PREFIX_REPLACEMENT
|
|
870
|
+
);
|
|
819
871
|
const sourcePath = path10.resolve(commandPath);
|
|
820
872
|
const description = extractDescriptionFromFrontmatter(normalizedCommand) || `${commandName} command imported from .claude/commands`;
|
|
821
873
|
return `---
|
|
@@ -830,18 +882,344 @@ resolve them from the current workspace first; if missing, ask for the intended
|
|
|
830
882
|
|
|
831
883
|
---
|
|
832
884
|
|
|
833
|
-
${
|
|
885
|
+
${rewrittenCommand}
|
|
834
886
|
`;
|
|
835
887
|
}
|
|
888
|
+
function isRecord(value) {
|
|
889
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
890
|
+
}
|
|
891
|
+
function asStringArray(value) {
|
|
892
|
+
if (!Array.isArray(value)) {
|
|
893
|
+
return void 0;
|
|
894
|
+
}
|
|
895
|
+
const items = value.filter(
|
|
896
|
+
(item) => typeof item === "string"
|
|
897
|
+
);
|
|
898
|
+
return items.length > 0 ? items : void 0;
|
|
899
|
+
}
|
|
900
|
+
function asStringRecord(value) {
|
|
901
|
+
if (!isRecord(value)) {
|
|
902
|
+
return void 0;
|
|
903
|
+
}
|
|
904
|
+
const entries = Object.entries(value).filter(
|
|
905
|
+
(entry) => typeof entry[1] === "string"
|
|
906
|
+
);
|
|
907
|
+
if (entries.length === 0) {
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
return Object.fromEntries(entries);
|
|
911
|
+
}
|
|
912
|
+
function normalizeClaudeMcpServer(value) {
|
|
913
|
+
if (!isRecord(value)) {
|
|
914
|
+
return void 0;
|
|
915
|
+
}
|
|
916
|
+
const type = typeof value.type === "string" ? value.type : void 0;
|
|
917
|
+
const command = typeof value.command === "string" ? value.command : void 0;
|
|
918
|
+
const args = asStringArray(value.args);
|
|
919
|
+
const env = asStringRecord(value.env);
|
|
920
|
+
const cwd = typeof value.cwd === "string" ? value.cwd : void 0;
|
|
921
|
+
const url = typeof value.url === "string" ? value.url : void 0;
|
|
922
|
+
const headers = asStringRecord(value.headers);
|
|
923
|
+
const bearerTokenEnvVar = typeof value.bearerTokenEnvVar === "string" ? value.bearerTokenEnvVar : typeof value.bearer_token_env_var === "string" ? value.bearer_token_env_var : void 0;
|
|
924
|
+
const envHttpHeaders = asStringRecord(value.envHttpHeaders) || asStringRecord(value.env_http_headers);
|
|
925
|
+
return {
|
|
926
|
+
type,
|
|
927
|
+
command,
|
|
928
|
+
args,
|
|
929
|
+
env,
|
|
930
|
+
cwd,
|
|
931
|
+
url,
|
|
932
|
+
headers,
|
|
933
|
+
bearerTokenEnvVar,
|
|
934
|
+
envHttpHeaders
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
function normalizeClaudeMcpServers(value) {
|
|
938
|
+
if (!isRecord(value)) {
|
|
939
|
+
return {};
|
|
940
|
+
}
|
|
941
|
+
const normalizedEntries = Object.entries(value).map(([name, server]) => [name, normalizeClaudeMcpServer(server)]).filter((entry) => Boolean(entry[1]));
|
|
942
|
+
return Object.fromEntries(normalizedEntries);
|
|
943
|
+
}
|
|
944
|
+
function getProjectState(claudeState, targetDir) {
|
|
945
|
+
const projects = claudeState.projects;
|
|
946
|
+
if (!isRecord(projects)) {
|
|
947
|
+
return void 0;
|
|
948
|
+
}
|
|
949
|
+
const normalizedTarget = path10.resolve(targetDir);
|
|
950
|
+
for (const [projectPath, projectState] of Object.entries(projects)) {
|
|
951
|
+
if (path10.resolve(projectPath) !== normalizedTarget) {
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
if (isRecord(projectState)) {
|
|
955
|
+
return projectState;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return void 0;
|
|
959
|
+
}
|
|
960
|
+
function filterMcpJsonServersByProjectState(servers, projectState) {
|
|
961
|
+
if (!projectState) {
|
|
962
|
+
return servers;
|
|
963
|
+
}
|
|
964
|
+
const enabled = asStringArray(projectState.enabledMcpjsonServers) || [];
|
|
965
|
+
const disabled = new Set(
|
|
966
|
+
asStringArray(projectState.disabledMcpjsonServers) || []
|
|
967
|
+
);
|
|
968
|
+
const enabledSet = new Set(enabled);
|
|
969
|
+
const hasEnabledFilter = enabledSet.size > 0;
|
|
970
|
+
const filteredEntries = Object.entries(servers).filter(([name]) => {
|
|
971
|
+
if (hasEnabledFilter && !enabledSet.has(name)) {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
if (disabled.has(name)) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
return true;
|
|
978
|
+
});
|
|
979
|
+
return Object.fromEntries(filteredEntries);
|
|
980
|
+
}
|
|
981
|
+
async function readJsonFile(filePath) {
|
|
982
|
+
if (!await fs8.pathExists(filePath)) {
|
|
983
|
+
return void 0;
|
|
984
|
+
}
|
|
985
|
+
try {
|
|
986
|
+
const raw = await fs8.readFile(filePath, "utf8");
|
|
987
|
+
const parsed = JSON.parse(raw);
|
|
988
|
+
if (!isRecord(parsed)) {
|
|
989
|
+
return void 0;
|
|
990
|
+
}
|
|
991
|
+
return parsed;
|
|
992
|
+
} catch {
|
|
993
|
+
return void 0;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
function toCodexMcpServer(server) {
|
|
997
|
+
const serverType = server.type?.toLowerCase();
|
|
998
|
+
const isHttp = serverType === "http" || Boolean(server.url);
|
|
999
|
+
if (isHttp) {
|
|
1000
|
+
if (!server.url) {
|
|
1001
|
+
return void 0;
|
|
1002
|
+
}
|
|
1003
|
+
return {
|
|
1004
|
+
kind: "http",
|
|
1005
|
+
url: server.url,
|
|
1006
|
+
bearerTokenEnvVar: server.bearerTokenEnvVar,
|
|
1007
|
+
httpHeaders: server.headers,
|
|
1008
|
+
envHttpHeaders: server.envHttpHeaders
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
if (!server.command) {
|
|
1012
|
+
return void 0;
|
|
1013
|
+
}
|
|
1014
|
+
return {
|
|
1015
|
+
kind: "stdio",
|
|
1016
|
+
command: server.command,
|
|
1017
|
+
args: server.args,
|
|
1018
|
+
env: server.env,
|
|
1019
|
+
cwd: server.cwd
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function tomlQuote(value) {
|
|
1023
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1024
|
+
}
|
|
1025
|
+
function tomlKey(value) {
|
|
1026
|
+
return /^[A-Za-z0-9_-]+$/.test(value) ? value : tomlQuote(value);
|
|
1027
|
+
}
|
|
1028
|
+
function parseTomlPath(input) {
|
|
1029
|
+
const pathInput = input.trim();
|
|
1030
|
+
if (!pathInput) {
|
|
1031
|
+
return void 0;
|
|
1032
|
+
}
|
|
1033
|
+
const segments = [];
|
|
1034
|
+
let current = "";
|
|
1035
|
+
let inQuotes = false;
|
|
1036
|
+
let escaped = false;
|
|
1037
|
+
for (const char of pathInput) {
|
|
1038
|
+
if (inQuotes) {
|
|
1039
|
+
if (escaped) {
|
|
1040
|
+
current += char;
|
|
1041
|
+
escaped = false;
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
if (char === "\\") {
|
|
1045
|
+
escaped = true;
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
if (char === '"') {
|
|
1049
|
+
inQuotes = false;
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
current += char;
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
if (char === '"') {
|
|
1056
|
+
inQuotes = true;
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (char === ".") {
|
|
1060
|
+
const value = current.trim();
|
|
1061
|
+
if (!value) {
|
|
1062
|
+
return void 0;
|
|
1063
|
+
}
|
|
1064
|
+
segments.push(value);
|
|
1065
|
+
current = "";
|
|
1066
|
+
continue;
|
|
1067
|
+
}
|
|
1068
|
+
current += char;
|
|
1069
|
+
}
|
|
1070
|
+
if (inQuotes || escaped) {
|
|
1071
|
+
return void 0;
|
|
1072
|
+
}
|
|
1073
|
+
const tail = current.trim();
|
|
1074
|
+
if (!tail) {
|
|
1075
|
+
return void 0;
|
|
1076
|
+
}
|
|
1077
|
+
segments.push(tail);
|
|
1078
|
+
return segments;
|
|
1079
|
+
}
|
|
1080
|
+
function stripCodexMcpSections(configToml) {
|
|
1081
|
+
const lines = normalizeLineEndings(configToml).split("\n");
|
|
1082
|
+
const kept = [];
|
|
1083
|
+
let droppingMcpSection = false;
|
|
1084
|
+
for (const line of lines) {
|
|
1085
|
+
const headerMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
|
|
1086
|
+
if (headerMatch) {
|
|
1087
|
+
const headerPath = parseTomlPath(headerMatch[1]);
|
|
1088
|
+
droppingMcpSection = Boolean(
|
|
1089
|
+
headerPath && headerPath.length > 0 && headerPath[0] === "mcp_servers"
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
if (!droppingMcpSection) {
|
|
1093
|
+
kept.push(line);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return kept.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
1097
|
+
}
|
|
1098
|
+
function renderTomlStringArray(values) {
|
|
1099
|
+
if (!values || values.length === 0) {
|
|
1100
|
+
return void 0;
|
|
1101
|
+
}
|
|
1102
|
+
return `[${values.map((value) => tomlQuote(value)).join(", ")}]`;
|
|
1103
|
+
}
|
|
1104
|
+
function renderTomlStringTable(header, values) {
|
|
1105
|
+
if (!values || Object.keys(values).length === 0) {
|
|
1106
|
+
return void 0;
|
|
1107
|
+
}
|
|
1108
|
+
const lines = Object.entries(values).sort((a, b) => a[0].localeCompare(b[0])).map(([key, value]) => `${tomlKey(key)} = ${tomlQuote(value)}`);
|
|
1109
|
+
return `${header}
|
|
1110
|
+
${lines.join("\n")}`;
|
|
1111
|
+
}
|
|
1112
|
+
function renderCodexMcpServer(name, server) {
|
|
1113
|
+
const sectionRoot = `[mcp_servers.${tomlKey(name)}]`;
|
|
1114
|
+
const lines = [sectionRoot];
|
|
1115
|
+
if (server.kind === "http") {
|
|
1116
|
+
lines.push(`url = ${tomlQuote(server.url ?? "")}`);
|
|
1117
|
+
if (server.bearerTokenEnvVar) {
|
|
1118
|
+
lines.push(
|
|
1119
|
+
`bearer_token_env_var = ${tomlQuote(server.bearerTokenEnvVar)}`
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
} else {
|
|
1123
|
+
lines.push(`command = ${tomlQuote(server.command ?? "")}`);
|
|
1124
|
+
const args = renderTomlStringArray(server.args);
|
|
1125
|
+
if (args) {
|
|
1126
|
+
lines.push(`args = ${args}`);
|
|
1127
|
+
}
|
|
1128
|
+
if (server.cwd) {
|
|
1129
|
+
lines.push(`cwd = ${tomlQuote(server.cwd)}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
const envSection = server.kind === "stdio" ? renderTomlStringTable(`[mcp_servers.${tomlKey(name)}.env]`, server.env) : void 0;
|
|
1133
|
+
const httpHeadersSection = server.kind === "http" ? renderTomlStringTable(
|
|
1134
|
+
`[mcp_servers.${tomlKey(name)}.http_headers]`,
|
|
1135
|
+
server.httpHeaders
|
|
1136
|
+
) : void 0;
|
|
1137
|
+
const envHttpHeadersSection = server.kind === "http" ? renderTomlStringTable(
|
|
1138
|
+
`[mcp_servers.${tomlKey(name)}.env_http_headers]`,
|
|
1139
|
+
server.envHttpHeaders
|
|
1140
|
+
) : void 0;
|
|
1141
|
+
const sections = [
|
|
1142
|
+
lines.join("\n"),
|
|
1143
|
+
envSection,
|
|
1144
|
+
httpHeadersSection,
|
|
1145
|
+
envHttpHeadersSection
|
|
1146
|
+
].filter((section) => Boolean(section));
|
|
1147
|
+
return sections.join("\n\n");
|
|
1148
|
+
}
|
|
1149
|
+
function mergeCodexConfigToml(existingToml, renderedMcpBlocks) {
|
|
1150
|
+
const base = stripCodexMcpSections(existingToml);
|
|
1151
|
+
const mcp = renderedMcpBlocks.trim();
|
|
1152
|
+
if (!base) {
|
|
1153
|
+
return mcp;
|
|
1154
|
+
}
|
|
1155
|
+
if (!mcp) {
|
|
1156
|
+
return base;
|
|
1157
|
+
}
|
|
1158
|
+
return `${base}
|
|
1159
|
+
|
|
1160
|
+
${mcp}`;
|
|
1161
|
+
}
|
|
1162
|
+
async function loadClaudeMcpServers(targetDir) {
|
|
1163
|
+
const homeDir = path10.resolve(os.homedir());
|
|
1164
|
+
const normalizedTarget = path10.resolve(targetDir);
|
|
1165
|
+
const isUserScope = normalizedTarget === homeDir;
|
|
1166
|
+
const claudeStatePath = path10.join(homeDir, CLAUDE_STATE_FILE);
|
|
1167
|
+
const claudeState = await readJsonFile(claudeStatePath);
|
|
1168
|
+
if (isUserScope) {
|
|
1169
|
+
return {
|
|
1170
|
+
scope: "global",
|
|
1171
|
+
servers: normalizeClaudeMcpServers(claudeState?.mcpServers)
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
const projectState = claudeState ? getProjectState(claudeState, normalizedTarget) : void 0;
|
|
1175
|
+
const projectServers = normalizeClaudeMcpServers(projectState?.mcpServers);
|
|
1176
|
+
const localMcpPath = path10.join(normalizedTarget, CLAUDE_LOCAL_MCP_FILE);
|
|
1177
|
+
const localMcpState = await readJsonFile(localMcpPath);
|
|
1178
|
+
const localMcpServers = filterMcpJsonServersByProjectState(
|
|
1179
|
+
normalizeClaudeMcpServers(localMcpState?.mcpServers),
|
|
1180
|
+
projectState
|
|
1181
|
+
);
|
|
1182
|
+
return {
|
|
1183
|
+
scope: "local",
|
|
1184
|
+
servers: {
|
|
1185
|
+
...localMcpServers,
|
|
1186
|
+
...projectServers
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
async function syncMcpServers(targetDir) {
|
|
1191
|
+
const homeDir = path10.resolve(os.homedir());
|
|
1192
|
+
const normalizedTarget = path10.resolve(targetDir);
|
|
1193
|
+
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);
|
|
1196
|
+
const { scope, servers } = await loadClaudeMcpServers(normalizedTarget);
|
|
1197
|
+
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
|
+
const renderedMcpBlocks = codexServers.map(([name, server]) => renderCodexMcpServer(name, server)).join("\n\n");
|
|
1199
|
+
const existingConfig = await fs8.pathExists(codexMcpConfigPath) ? await fs8.readFile(codexMcpConfigPath, "utf8") : "";
|
|
1200
|
+
const nextConfig = mergeCodexConfigToml(existingConfig, renderedMcpBlocks);
|
|
1201
|
+
await fs8.ensureDir(codexBaseDir);
|
|
1202
|
+
await fs8.writeFile(codexMcpConfigPath, `${nextConfig.trimEnd()}
|
|
1203
|
+
`);
|
|
1204
|
+
return {
|
|
1205
|
+
codexMcpConfigPath,
|
|
1206
|
+
syncedMcpServers: codexServers.length,
|
|
1207
|
+
scope
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
836
1210
|
async function syncSkills(claudeSkillsDir, codexSkillsDir) {
|
|
837
1211
|
await fs8.ensureDir(codexSkillsDir);
|
|
838
1212
|
let sourceSkillNames = [];
|
|
839
1213
|
if (await fs8.pathExists(claudeSkillsDir)) {
|
|
840
|
-
const sourceEntries = await fs8.readdir(claudeSkillsDir, {
|
|
1214
|
+
const sourceEntries = await fs8.readdir(claudeSkillsDir, {
|
|
1215
|
+
withFileTypes: true
|
|
1216
|
+
});
|
|
841
1217
|
sourceSkillNames = sourceEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => name !== ".system");
|
|
842
1218
|
}
|
|
843
1219
|
const sourceSkillSet = new Set(sourceSkillNames);
|
|
844
|
-
const codexEntries = await fs8.readdir(codexSkillsDir, {
|
|
1220
|
+
const codexEntries = await fs8.readdir(codexSkillsDir, {
|
|
1221
|
+
withFileTypes: true
|
|
1222
|
+
});
|
|
845
1223
|
for (const entry of codexEntries) {
|
|
846
1224
|
if (!entry.isDirectory()) {
|
|
847
1225
|
continue;
|
|
@@ -883,26 +1261,126 @@ async function syncCommands(claudeCommandsDir, codexCommandsDir) {
|
|
|
883
1261
|
const skillDir = path10.join(codexCommandsDir, commandName);
|
|
884
1262
|
const skillPath = path10.join(skillDir, "SKILL.md");
|
|
885
1263
|
await fs8.ensureDir(skillDir);
|
|
886
|
-
await fs8.writeFile(
|
|
1264
|
+
await fs8.writeFile(
|
|
1265
|
+
skillPath,
|
|
1266
|
+
buildCommandSkillContent(commandPath, commandRaw)
|
|
1267
|
+
);
|
|
887
1268
|
}
|
|
888
1269
|
return commandFiles.length;
|
|
889
1270
|
}
|
|
1271
|
+
async function syncInstructions(claudeInstructionsDir, codexInstructionsDir) {
|
|
1272
|
+
if (!await fs8.pathExists(claudeInstructionsDir)) {
|
|
1273
|
+
await fs8.remove(codexInstructionsDir);
|
|
1274
|
+
return 0;
|
|
1275
|
+
}
|
|
1276
|
+
await fs8.remove(codexInstructionsDir);
|
|
1277
|
+
await fs8.ensureDir(codexInstructionsDir);
|
|
1278
|
+
const counter = { files: 0, directories: 0 };
|
|
1279
|
+
await copyRecursive(claudeInstructionsDir, codexInstructionsDir, counter);
|
|
1280
|
+
return counter.files;
|
|
1281
|
+
}
|
|
1282
|
+
function extractReferenceCandidate(line) {
|
|
1283
|
+
const trimmed = line.trim();
|
|
1284
|
+
if (!trimmed.startsWith("@")) {
|
|
1285
|
+
return void 0;
|
|
1286
|
+
}
|
|
1287
|
+
const [candidate] = trimmed.slice(1).split(/\s+/);
|
|
1288
|
+
if (!candidate || candidate.startsWith("#") || candidate.includes("://")) {
|
|
1289
|
+
return void 0;
|
|
1290
|
+
}
|
|
1291
|
+
const looksLikePath = candidate.startsWith("./") || candidate.startsWith("../") || candidate.startsWith("/") || candidate.includes("/") || /\.[a-z0-9]+$/i.test(candidate);
|
|
1292
|
+
if (!looksLikePath) {
|
|
1293
|
+
return void 0;
|
|
1294
|
+
}
|
|
1295
|
+
return candidate;
|
|
1296
|
+
}
|
|
1297
|
+
async function collectSkillMarkdownFiles(rootDir, collector = []) {
|
|
1298
|
+
if (!await fs8.pathExists(rootDir)) {
|
|
1299
|
+
return collector;
|
|
1300
|
+
}
|
|
1301
|
+
const entries = await fs8.readdir(rootDir, { withFileTypes: true });
|
|
1302
|
+
for (const entry of entries) {
|
|
1303
|
+
const fullPath = path10.join(rootDir, entry.name);
|
|
1304
|
+
if (entry.isDirectory()) {
|
|
1305
|
+
await collectSkillMarkdownFiles(fullPath, collector);
|
|
1306
|
+
continue;
|
|
1307
|
+
}
|
|
1308
|
+
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
1309
|
+
collector.push(fullPath);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return collector;
|
|
1313
|
+
}
|
|
1314
|
+
async function validateSkillReferences(codexSkillsDir) {
|
|
1315
|
+
const skillFiles = await collectSkillMarkdownFiles(codexSkillsDir);
|
|
1316
|
+
let count = 0;
|
|
1317
|
+
const samples = [];
|
|
1318
|
+
for (const skillFile of skillFiles) {
|
|
1319
|
+
const content = await fs8.readFile(skillFile, "utf8");
|
|
1320
|
+
const lines = normalizeLineEndings(content).split("\n");
|
|
1321
|
+
for (const line of lines) {
|
|
1322
|
+
const candidate = extractReferenceCandidate(line);
|
|
1323
|
+
if (!candidate) {
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
const referencePath = candidate.split(/[?#]/, 1)[0];
|
|
1327
|
+
if (!referencePath) {
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
const resolvedPath = path10.resolve(path10.dirname(skillFile), referencePath);
|
|
1331
|
+
if (await fs8.pathExists(resolvedPath)) {
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
count += 1;
|
|
1335
|
+
if (samples.length < MAX_REFERENCE_ISSUE_SAMPLES) {
|
|
1336
|
+
samples.push({
|
|
1337
|
+
skillPath: skillFile,
|
|
1338
|
+
reference: candidate,
|
|
1339
|
+
resolvedPath
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return { count, samples };
|
|
1345
|
+
}
|
|
890
1346
|
async function syncWithCodex(targetDir) {
|
|
891
1347
|
const codexHome = path10.resolve(targetDir, ".codex");
|
|
892
1348
|
const codexSkillsDir = path10.join(codexHome, "skills");
|
|
893
1349
|
const codexCommandsDir = path10.join(codexSkillsDir, "claude-commands");
|
|
1350
|
+
const codexInstructionsDir = path10.join(codexHome, "instructions");
|
|
894
1351
|
await fs8.ensureDir(codexSkillsDir);
|
|
895
1352
|
const claudeRootDir = path10.join(targetDir, ".claude");
|
|
896
1353
|
const claudeSkillsDir = path10.join(claudeRootDir, "skills");
|
|
897
1354
|
const claudeCommandsDir = path10.join(claudeRootDir, "commands");
|
|
1355
|
+
const claudeInstructionsDir = path10.join(claudeRootDir, "instructions");
|
|
898
1356
|
const syncedSkills = await syncSkills(claudeSkillsDir, codexSkillsDir);
|
|
899
|
-
const
|
|
1357
|
+
const syncedInstructions = await syncInstructions(
|
|
1358
|
+
claudeInstructionsDir,
|
|
1359
|
+
codexInstructionsDir
|
|
1360
|
+
);
|
|
1361
|
+
const syncedCommands = await syncCommands(
|
|
1362
|
+
claudeCommandsDir,
|
|
1363
|
+
codexCommandsDir
|
|
1364
|
+
);
|
|
1365
|
+
const {
|
|
1366
|
+
codexMcpConfigPath,
|
|
1367
|
+
syncedMcpServers,
|
|
1368
|
+
scope: mcpScope
|
|
1369
|
+
} = await syncMcpServers(targetDir);
|
|
1370
|
+
const { count: referenceIssueCount, samples: referenceIssueSamples } = await validateSkillReferences(codexSkillsDir);
|
|
900
1371
|
return {
|
|
901
1372
|
codexHome,
|
|
902
1373
|
codexSkillsDir,
|
|
903
1374
|
codexCommandsDir,
|
|
1375
|
+
codexInstructionsDir,
|
|
904
1376
|
syncedSkills,
|
|
905
|
-
syncedCommands
|
|
1377
|
+
syncedCommands,
|
|
1378
|
+
syncedInstructions,
|
|
1379
|
+
codexMcpConfigPath,
|
|
1380
|
+
syncedMcpServers,
|
|
1381
|
+
mcpScope,
|
|
1382
|
+
referenceIssueCount,
|
|
1383
|
+
referenceIssueSamples
|
|
906
1384
|
};
|
|
907
1385
|
}
|
|
908
1386
|
|
|
@@ -983,29 +1461,32 @@ async function installTemplates(templates, targetDir) {
|
|
|
983
1461
|
logger.success(`Total: ${totalFiles} files, ${totalDirectories} directories`);
|
|
984
1462
|
return { files: totalFiles, directories: totalDirectories };
|
|
985
1463
|
}
|
|
986
|
-
async function promptForExtrasInstallation(options, hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts) {
|
|
1464
|
+
async function promptForExtrasInstallation(options, hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts, hasHooks) {
|
|
987
1465
|
return await promptExtrasSelection({
|
|
988
1466
|
skills: options.skills,
|
|
989
1467
|
commands: options.commands,
|
|
990
1468
|
agents: options.agents,
|
|
991
1469
|
instructions: options.instructions,
|
|
992
1470
|
scripts: options.scripts,
|
|
1471
|
+
hooks: options.hooks,
|
|
993
1472
|
hasSkills,
|
|
994
1473
|
hasCommands,
|
|
995
1474
|
hasAgents,
|
|
996
1475
|
hasInstructions,
|
|
997
|
-
hasScripts
|
|
1476
|
+
hasScripts,
|
|
1477
|
+
hasHooks
|
|
998
1478
|
});
|
|
999
1479
|
}
|
|
1000
|
-
function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts, scope) {
|
|
1480
|
+
function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAgents, hasInstructions, hasScripts, hasHooks, scope) {
|
|
1001
1481
|
const {
|
|
1002
1482
|
installSkills,
|
|
1003
1483
|
installCommands,
|
|
1004
1484
|
installAgents,
|
|
1005
1485
|
installInstructions,
|
|
1006
|
-
installScripts
|
|
1486
|
+
installScripts,
|
|
1487
|
+
installHooks
|
|
1007
1488
|
} = flags;
|
|
1008
|
-
const hasExtrasInstalled = installSkills && hasSkills || installCommands && hasCommands || installAgents && hasAgents || installInstructions && hasInstructions || installScripts && hasScripts;
|
|
1489
|
+
const hasExtrasInstalled = installSkills && hasSkills || installCommands && hasCommands || installAgents && hasAgents || installInstructions && hasInstructions || installScripts && hasScripts || installHooks && hasHooks;
|
|
1009
1490
|
if (templates.length === 0 && !hasExtrasInstalled) {
|
|
1010
1491
|
logger.blank();
|
|
1011
1492
|
logger.info("No templates or extras installed.");
|
|
@@ -1038,6 +1519,9 @@ function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAg
|
|
|
1038
1519
|
if (installScripts && hasScripts) {
|
|
1039
1520
|
logger.step("Scripts \u2192 .claude/scripts/");
|
|
1040
1521
|
}
|
|
1522
|
+
if (installHooks && hasHooks) {
|
|
1523
|
+
logger.step("Hooks \u2192 .claude/hooks/");
|
|
1524
|
+
}
|
|
1041
1525
|
}
|
|
1042
1526
|
logger.blank();
|
|
1043
1527
|
logger.info("Next steps:");
|
|
@@ -1056,7 +1540,7 @@ var init = async (options) => {
|
|
|
1056
1540
|
const { scope } = await promptScopeSelection({
|
|
1057
1541
|
providedScope: options.scope
|
|
1058
1542
|
});
|
|
1059
|
-
const targetDir = scope === "user" ?
|
|
1543
|
+
const targetDir = scope === "user" ? os2.homedir() : projectDir;
|
|
1060
1544
|
const isUserScope = scope === "user";
|
|
1061
1545
|
if (isUserScope) {
|
|
1062
1546
|
logger.blank();
|
|
@@ -1076,21 +1560,30 @@ var init = async (options) => {
|
|
|
1076
1560
|
}
|
|
1077
1561
|
}
|
|
1078
1562
|
const templatesToCheck = templates.length > 0 ? templates : availableTemplates;
|
|
1079
|
-
const {
|
|
1563
|
+
const {
|
|
1564
|
+
hasSkills,
|
|
1565
|
+
hasCommands,
|
|
1566
|
+
hasAgents,
|
|
1567
|
+
hasInstructions,
|
|
1568
|
+
hasScripts,
|
|
1569
|
+
hasHooks
|
|
1570
|
+
} = await checkAllExtrasExist(templatesToCheck);
|
|
1080
1571
|
const flags = await promptForExtrasInstallation(
|
|
1081
1572
|
options,
|
|
1082
1573
|
hasSkills,
|
|
1083
1574
|
hasCommands,
|
|
1084
1575
|
hasAgents,
|
|
1085
1576
|
hasInstructions,
|
|
1086
|
-
hasScripts
|
|
1577
|
+
hasScripts,
|
|
1578
|
+
hasHooks
|
|
1087
1579
|
);
|
|
1088
1580
|
await installExtras(templatesToCheck, targetDir, flags, {
|
|
1089
1581
|
hasSkills,
|
|
1090
1582
|
hasCommands,
|
|
1091
1583
|
hasAgents,
|
|
1092
1584
|
hasInstructions,
|
|
1093
|
-
hasScripts
|
|
1585
|
+
hasScripts,
|
|
1586
|
+
hasHooks
|
|
1094
1587
|
});
|
|
1095
1588
|
showInstallationSummary(
|
|
1096
1589
|
templates,
|
|
@@ -1100,16 +1593,17 @@ var init = async (options) => {
|
|
|
1100
1593
|
hasAgents,
|
|
1101
1594
|
hasInstructions,
|
|
1102
1595
|
hasScripts,
|
|
1596
|
+
hasHooks,
|
|
1103
1597
|
scope
|
|
1104
1598
|
);
|
|
1105
|
-
const
|
|
1599
|
+
const codexSyncPath = path11.join(targetDir, ".codex");
|
|
1106
1600
|
const { syncCodex } = await promptCodexSync({
|
|
1107
1601
|
providedSyncCodex: options.syncCodex,
|
|
1108
|
-
|
|
1602
|
+
codexPath: codexSyncPath
|
|
1109
1603
|
});
|
|
1110
1604
|
if (syncCodex) {
|
|
1111
1605
|
logger.blank();
|
|
1112
|
-
logger.info("Syncing .claude skills/commands to Codex...");
|
|
1606
|
+
logger.info("Syncing .claude skills/commands/instructions/MCP to Codex...");
|
|
1113
1607
|
try {
|
|
1114
1608
|
const result = await syncWithCodex(targetDir);
|
|
1115
1609
|
if (result.syncedSkills > 0) {
|
|
@@ -1118,12 +1612,33 @@ var init = async (options) => {
|
|
|
1118
1612
|
if (result.syncedCommands > 0) {
|
|
1119
1613
|
logger.step(`Commands synced: ${result.syncedCommands}`);
|
|
1120
1614
|
}
|
|
1121
|
-
if (result.
|
|
1615
|
+
if (result.syncedInstructions > 0) {
|
|
1616
|
+
logger.step(`Instructions synced: ${result.syncedInstructions}`);
|
|
1617
|
+
}
|
|
1618
|
+
if (result.syncedMcpServers > 0) {
|
|
1619
|
+
logger.step(
|
|
1620
|
+
`MCP servers synced (${result.mcpScope}): ${result.syncedMcpServers}`
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
if (result.syncedSkills === 0 && result.syncedCommands === 0 && result.syncedInstructions === 0 && result.syncedMcpServers === 0) {
|
|
1122
1624
|
logger.warn(
|
|
1123
|
-
"Nothing was synced. .claude/skills
|
|
1625
|
+
"Nothing was synced. .claude/skills, .claude/commands, .claude/instructions, and Claude MCP settings were not found."
|
|
1124
1626
|
);
|
|
1125
1627
|
} else {
|
|
1126
1628
|
logger.success(`Codex sync complete: ${result.codexSkillsDir}`);
|
|
1629
|
+
logger.step(`Codex MCP config: ${result.codexMcpConfigPath}`);
|
|
1630
|
+
if (result.referenceIssueCount > 0) {
|
|
1631
|
+
logger.warn(
|
|
1632
|
+
`Codex skill reference issues found: ${result.referenceIssueCount} (showing up to ${result.referenceIssueSamples.length})`
|
|
1633
|
+
);
|
|
1634
|
+
for (const issue of result.referenceIssueSamples) {
|
|
1635
|
+
const skillPath = path11.relative(targetDir, issue.skillPath);
|
|
1636
|
+
const resolvedPath = path11.relative(targetDir, issue.resolvedPath);
|
|
1637
|
+
logger.step(
|
|
1638
|
+
`${skillPath} -> @${issue.reference} (missing: ${resolvedPath})`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1127
1642
|
}
|
|
1128
1643
|
} catch (error) {
|
|
1129
1644
|
logger.warn(
|
|
@@ -1144,13 +1659,13 @@ var init = async (options) => {
|
|
|
1144
1659
|
|
|
1145
1660
|
// src/index.ts
|
|
1146
1661
|
var program = new Command();
|
|
1147
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.
|
|
1662
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.7.11");
|
|
1148
1663
|
program.option(
|
|
1149
1664
|
"-t, --template <names>",
|
|
1150
1665
|
"template names (comma-separated: tanstack-start,hono)"
|
|
1151
1666
|
).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
1667
|
"--sync-codex",
|
|
1153
|
-
"sync installed .claude skills/commands to selected-scope .codex/
|
|
1668
|
+
"sync installed .claude skills/commands/instructions and Claude MCP settings to selected-scope .codex/"
|
|
1154
1669
|
).option("--scope <scope>", "installation scope (project|user)").action(async (options) => {
|
|
1155
1670
|
banner();
|
|
1156
1671
|
if (options.list) {
|