@fractary/codex-cli 0.8.0 → 0.9.1

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/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import * as path4 from 'path';
2
+ import * as path5 from 'path';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import * as fs from 'fs/promises';
@@ -161,7 +161,7 @@ async function migrateConfig(legacyConfigPath, options) {
161
161
  }
162
162
  }
163
163
  async function writeYamlConfig(config, outputPath) {
164
- const dir = path4.dirname(outputPath);
164
+ const dir = path5.dirname(outputPath);
165
165
  await fs.mkdir(dir, { recursive: true });
166
166
  const yamlContent = yaml.dump(config, {
167
167
  indent: 2,
@@ -385,7 +385,7 @@ var init_codex_client = __esm({
385
385
  const { readYamlConfig: readYamlConfig2 } = await Promise.resolve().then(() => (init_migrate_config(), migrate_config_exports));
386
386
  const { resolveEnvVarsInConfig: resolveEnvVarsInConfig2 } = await Promise.resolve().then(() => (init_config_types(), config_types_exports));
387
387
  try {
388
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
388
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
389
389
  let config;
390
390
  try {
391
391
  config = await readYamlConfig2(configPath);
@@ -655,7 +655,7 @@ function getTempCodexPath(config) {
655
655
  const codexRepo = config.codex_repository || "codex";
656
656
  const sanitizedOrg = sanitizePathComponent(config.organization);
657
657
  const sanitizedRepo = sanitizePathComponent(codexRepo);
658
- return path4.join(
658
+ return path5.join(
659
659
  os.tmpdir(),
660
660
  "fractary-codex-clone",
661
661
  `${sanitizedOrg}-${sanitizedRepo}-${process.pid}`
@@ -663,7 +663,7 @@ function getTempCodexPath(config) {
663
663
  }
664
664
  async function isValidGitRepo(repoPath) {
665
665
  try {
666
- const gitDir = path4.join(repoPath, ".git");
666
+ const gitDir = path5.join(repoPath, ".git");
667
667
  const stats = await fs.stat(gitDir);
668
668
  return stats.isDirectory();
669
669
  } catch {
@@ -696,7 +696,7 @@ async function execGit(repoPath, args) {
696
696
  }
697
697
  }
698
698
  async function gitClone(url, targetPath, options) {
699
- const parentDir = path4.dirname(targetPath);
699
+ const parentDir = path5.dirname(targetPath);
700
700
  await fs.mkdir(parentDir, { recursive: true });
701
701
  const args = ["clone"];
702
702
  if (options?.depth) {
@@ -868,7 +868,229 @@ init_esm_shims();
868
868
 
869
869
  // src/commands/config/init.ts
870
870
  init_esm_shims();
871
- init_migrate_config();
871
+
872
+ // src/config/unified-config.ts
873
+ init_esm_shims();
874
+ function sanitizeForS3BucketName(name) {
875
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-").substring(0, 63);
876
+ }
877
+ function getDefaultUnifiedConfig(organization, project) {
878
+ const sanitizedProject = sanitizeForS3BucketName(project);
879
+ return {
880
+ file: {
881
+ schema_version: "2.0",
882
+ sources: {
883
+ specs: {
884
+ type: "s3",
885
+ bucket: `${sanitizedProject}-files`,
886
+ prefix: "specs/",
887
+ region: "us-east-1",
888
+ local: {
889
+ base_path: ".fractary/specs"
890
+ },
891
+ push: {
892
+ compress: false,
893
+ keep_local: true
894
+ },
895
+ auth: {
896
+ profile: "default"
897
+ }
898
+ },
899
+ logs: {
900
+ type: "s3",
901
+ bucket: `${sanitizedProject}-files`,
902
+ prefix: "logs/",
903
+ region: "us-east-1",
904
+ local: {
905
+ base_path: ".fractary/logs"
906
+ },
907
+ push: {
908
+ compress: true,
909
+ keep_local: true
910
+ },
911
+ auth: {
912
+ profile: "default"
913
+ }
914
+ }
915
+ }
916
+ },
917
+ codex: {
918
+ schema_version: "2.0",
919
+ organization,
920
+ project,
921
+ dependencies: {}
922
+ }
923
+ };
924
+ }
925
+ async function readUnifiedConfig(configPath) {
926
+ try {
927
+ const content = await fs.readFile(configPath, "utf-8");
928
+ const config = yaml.load(content);
929
+ return config;
930
+ } catch (error) {
931
+ if (error.code === "ENOENT") {
932
+ return null;
933
+ }
934
+ throw error;
935
+ }
936
+ }
937
+ async function writeUnifiedConfig(config, outputPath) {
938
+ const dir = path5.dirname(outputPath);
939
+ await fs.mkdir(dir, { recursive: true });
940
+ const yamlContent = yaml.dump(config, {
941
+ indent: 2,
942
+ lineWidth: 120,
943
+ noRefs: true,
944
+ sortKeys: false
945
+ });
946
+ await fs.writeFile(outputPath, yamlContent, "utf-8");
947
+ }
948
+ function mergeUnifiedConfigs(existing, updates) {
949
+ const merged = {};
950
+ if (updates.file || existing.file) {
951
+ merged.file = {
952
+ schema_version: updates.file?.schema_version || existing.file?.schema_version || "2.0",
953
+ sources: {
954
+ ...existing.file?.sources || {},
955
+ ...updates.file?.sources || {}
956
+ }
957
+ };
958
+ }
959
+ if (updates.codex || existing.codex) {
960
+ merged.codex = {
961
+ schema_version: updates.codex?.schema_version || existing.codex?.schema_version || "2.0",
962
+ organization: updates.codex?.organization || existing.codex?.organization || "default",
963
+ project: updates.codex?.project || existing.codex?.project || "default",
964
+ dependencies: {
965
+ ...existing.codex?.dependencies || {},
966
+ ...updates.codex?.dependencies || {}
967
+ }
968
+ };
969
+ }
970
+ return merged;
971
+ }
972
+ async function initializeUnifiedConfig(configPath, organization, project, options) {
973
+ const existingConfig = await readUnifiedConfig(configPath);
974
+ if (existingConfig && !options?.force) {
975
+ const defaultConfig = getDefaultUnifiedConfig(organization, project);
976
+ const merged = mergeUnifiedConfigs(existingConfig, defaultConfig);
977
+ await writeUnifiedConfig(merged, configPath);
978
+ return {
979
+ created: false,
980
+ merged: true,
981
+ config: merged
982
+ };
983
+ }
984
+ const config = getDefaultUnifiedConfig(organization, project);
985
+ await writeUnifiedConfig(config, configPath);
986
+ return {
987
+ created: true,
988
+ merged: false,
989
+ config
990
+ };
991
+ }
992
+
993
+ // src/config/gitignore-utils.ts
994
+ init_esm_shims();
995
+ var DEFAULT_FRACTARY_GITIGNORE = `# .fractary/.gitignore
996
+ # This file is managed by multiple plugins - each plugin manages its own section
997
+
998
+ # ===== fractary-codex (managed) =====
999
+ codex/cache/
1000
+ # ===== end fractary-codex =====
1001
+ `;
1002
+ var DEFAULT_CACHE_DIR = "codex/cache/";
1003
+ async function readFractaryGitignore(projectRoot) {
1004
+ const gitignorePath = path5.join(projectRoot, ".fractary", ".gitignore");
1005
+ try {
1006
+ return await fs.readFile(gitignorePath, "utf-8");
1007
+ } catch (error) {
1008
+ if (error.code === "ENOENT") {
1009
+ return null;
1010
+ }
1011
+ throw error;
1012
+ }
1013
+ }
1014
+ async function writeFractaryGitignore(projectRoot, content) {
1015
+ const gitignorePath = path5.join(projectRoot, ".fractary", ".gitignore");
1016
+ await fs.mkdir(path5.join(projectRoot, ".fractary"), { recursive: true });
1017
+ await fs.writeFile(gitignorePath, content, "utf-8");
1018
+ }
1019
+ function normalizeCachePath(cachePath) {
1020
+ let normalized = cachePath.replace(/\\/g, "/");
1021
+ normalized = normalized.replace(/^\.fractary\//, "");
1022
+ if (!normalized.endsWith("/")) {
1023
+ normalized += "/";
1024
+ }
1025
+ return normalized;
1026
+ }
1027
+ function isCachePathIgnored(gitignoreContent, cachePath) {
1028
+ const normalized = normalizeCachePath(cachePath);
1029
+ const lines = gitignoreContent.split("\n").map((l) => l.trim());
1030
+ return lines.some((line) => {
1031
+ if (line.startsWith("#") || line === "") return false;
1032
+ let normalizedLine = line.replace(/\\/g, "/");
1033
+ if (!normalizedLine.endsWith("/")) {
1034
+ normalizedLine += "/";
1035
+ }
1036
+ return normalizedLine === normalized;
1037
+ });
1038
+ }
1039
+ function addCachePathToGitignore(gitignoreContent, cachePath, comment) {
1040
+ const normalized = normalizeCachePath(cachePath);
1041
+ if (isCachePathIgnored(gitignoreContent, cachePath)) {
1042
+ return gitignoreContent;
1043
+ }
1044
+ let addition = "";
1045
+ {
1046
+ addition += `
1047
+ # ${comment}
1048
+ `;
1049
+ }
1050
+ addition += normalized + "\n";
1051
+ return gitignoreContent.trimEnd() + addition;
1052
+ }
1053
+ async function ensureCachePathIgnored(projectRoot, cachePath) {
1054
+ const gitignorePath = path5.join(projectRoot, ".fractary", ".gitignore");
1055
+ let relativeCachePath = cachePath;
1056
+ if (path5.isAbsolute(cachePath)) {
1057
+ relativeCachePath = path5.relative(path5.join(projectRoot, ".fractary"), cachePath);
1058
+ }
1059
+ relativeCachePath = normalizeCachePath(relativeCachePath);
1060
+ let content = await readFractaryGitignore(projectRoot);
1061
+ const gitignoreExists = content !== null;
1062
+ if (!gitignoreExists) {
1063
+ content = DEFAULT_FRACTARY_GITIGNORE;
1064
+ if (!isCachePathIgnored(content, relativeCachePath)) {
1065
+ content = addCachePathToGitignore(content, relativeCachePath, "Custom cache directory");
1066
+ }
1067
+ await writeFractaryGitignore(projectRoot, content);
1068
+ return {
1069
+ created: true,
1070
+ updated: false,
1071
+ alreadyIgnored: false,
1072
+ gitignorePath
1073
+ };
1074
+ }
1075
+ if (isCachePathIgnored(content, relativeCachePath)) {
1076
+ return {
1077
+ created: false,
1078
+ updated: false,
1079
+ alreadyIgnored: true,
1080
+ gitignorePath
1081
+ };
1082
+ }
1083
+ content = addCachePathToGitignore(content, relativeCachePath, "Custom cache directory");
1084
+ await writeFractaryGitignore(projectRoot, content);
1085
+ return {
1086
+ created: false,
1087
+ updated: true,
1088
+ alreadyIgnored: false,
1089
+ gitignorePath
1090
+ };
1091
+ }
1092
+
1093
+ // src/commands/config/init.ts
872
1094
  async function getOrgFromGitRemote() {
873
1095
  try {
874
1096
  const { execSync } = __require("child_process");
@@ -890,9 +1112,9 @@ async function fileExists(filePath) {
890
1112
  }
891
1113
  function initCommand() {
892
1114
  const cmd = new Command("init");
893
- cmd.description("Initialize Codex v3.0 with YAML configuration").option("--org <slug>", 'Organization slug (e.g., "fractary")').option("--mcp", "Enable MCP server registration").option("--force", "Overwrite existing configuration").action(async (options) => {
1115
+ cmd.description("Initialize unified Fractary configuration (.fractary/config.yaml)").option("--org <slug>", 'Organization slug (e.g., "fractary")').option("--project <name>", "Project name (default: derived from directory)").option("--force", "Overwrite existing configuration").action(async (options) => {
894
1116
  try {
895
- console.log(chalk8.blue("Initializing Codex v3.0 (YAML format)...\n"));
1117
+ console.log(chalk8.blue("Initializing unified Fractary configuration...\n"));
896
1118
  let org = options.org;
897
1119
  if (!org) {
898
1120
  org = await getOrgFromGitRemote();
@@ -901,60 +1123,79 @@ function initCommand() {
901
1123
  try {
902
1124
  const { resolveOrganization } = await import('@fractary/codex');
903
1125
  org = resolveOrganization({
904
- repoName: path4.basename(process.cwd())
1126
+ repoName: path5.basename(process.cwd())
905
1127
  });
906
1128
  } catch {
907
1129
  }
908
1130
  }
909
1131
  if (!org) {
910
- org = path4.basename(process.cwd()).split("-")[0] || "default";
1132
+ org = path5.basename(process.cwd()).split("-")[0] || "default";
911
1133
  console.log(chalk8.yellow(`\u26A0 Could not detect organization, using: ${org}`));
912
1134
  console.log(chalk8.dim(" Use --org <slug> to specify explicitly\n"));
913
1135
  } else {
914
1136
  console.log(chalk8.dim(`Organization: ${chalk8.cyan(org)}
915
1137
  `));
916
1138
  }
917
- const configDir = path4.join(process.cwd(), ".fractary", "codex");
918
- const configPath = path4.join(configDir, "config.yaml");
1139
+ let project = options.project;
1140
+ if (!project) {
1141
+ project = path5.basename(process.cwd());
1142
+ console.log(chalk8.dim(`Project: ${chalk8.cyan(project)}
1143
+ `));
1144
+ }
1145
+ const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
919
1146
  const configExists = await fileExists(configPath);
920
1147
  if (configExists && !options.force) {
921
- console.log(chalk8.yellow("\u26A0 Configuration already exists at .fractary/codex/config.yaml"));
922
- console.log(chalk8.dim("Use --force to overwrite"));
923
- process.exit(1);
1148
+ console.log(chalk8.yellow(`\u26A0 Configuration already exists at .fractary/config.yaml`));
1149
+ console.log(chalk8.dim("Merging with existing configuration...\n"));
924
1150
  }
925
1151
  console.log("Creating directory structure...");
926
1152
  const dirs = [
1153
+ ".fractary",
1154
+ ".fractary/specs",
1155
+ ".fractary/logs",
927
1156
  ".fractary/codex",
928
1157
  ".fractary/codex/cache"
929
1158
  ];
930
1159
  for (const dir of dirs) {
931
- await fs.mkdir(path4.join(process.cwd(), dir), { recursive: true });
1160
+ await fs.mkdir(path5.join(process.cwd(), dir), { recursive: true });
932
1161
  console.log(chalk8.green("\u2713"), chalk8.dim(dir + "/"));
933
1162
  }
934
- console.log("\nCreating YAML configuration...");
935
- const config = getDefaultYamlConfig(org);
936
- if (options.mcp && config.mcp) {
937
- config.mcp.enabled = true;
1163
+ const gitignoreResult = await ensureCachePathIgnored(process.cwd(), ".fractary/codex/cache");
1164
+ if (gitignoreResult.created) {
1165
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/.gitignore (created)"));
1166
+ } else if (gitignoreResult.updated) {
1167
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/.gitignore (updated)"));
1168
+ } else {
1169
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/.gitignore (exists)"));
1170
+ }
1171
+ console.log("\nInitializing configuration...");
1172
+ const result = await initializeUnifiedConfig(
1173
+ configPath,
1174
+ org,
1175
+ project,
1176
+ { force: options.force }
1177
+ );
1178
+ if (result.created) {
1179
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/config.yaml (created)"));
1180
+ } else if (result.merged) {
1181
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/config.yaml (merged with existing)"));
938
1182
  }
939
- await writeYamlConfig(config, configPath);
940
- console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/codex/config.yaml"));
941
- console.log(chalk8.green("\n\u2713 Codex v4.0 initialized successfully!\n"));
1183
+ console.log(chalk8.green("\n\u2713 Unified configuration initialized successfully!\n"));
942
1184
  console.log(chalk8.bold("Configuration:"));
943
1185
  console.log(chalk8.dim(` Organization: ${org}`));
944
- console.log(chalk8.dim(` Cache: .fractary/codex/cache/`));
945
- console.log(chalk8.dim(` Config: .fractary/codex/config.yaml`));
946
- if (options.mcp) {
947
- console.log(chalk8.dim(` MCP Server: Enabled (port 3000)`));
948
- }
949
- console.log(chalk8.bold("\nStorage providers configured:"));
950
- console.log(chalk8.dim(" - Local filesystem (./knowledge)"));
951
- console.log(chalk8.dim(" - GitHub (requires GITHUB_TOKEN)"));
952
- console.log(chalk8.dim(" - HTTP endpoint"));
1186
+ console.log(chalk8.dim(` Project: ${project}`));
1187
+ console.log(chalk8.dim(` Config: .fractary/config.yaml`));
1188
+ console.log(chalk8.bold("\nFile plugin sources:"));
1189
+ console.log(chalk8.dim(" - specs: .fractary/specs/ \u2192 S3"));
1190
+ console.log(chalk8.dim(" - logs: .fractary/logs/ \u2192 S3"));
1191
+ console.log(chalk8.bold("\nCodex plugin:"));
1192
+ console.log(chalk8.dim(" - Cache: .fractary/codex/cache/"));
1193
+ console.log(chalk8.dim(" - Dependencies: (none configured)"));
953
1194
  console.log(chalk8.bold("\nNext steps:"));
954
- console.log(chalk8.dim(' 1. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
955
- console.log(chalk8.dim(" 2. Edit .fractary/codex/config.yaml to configure storage providers"));
956
- console.log(chalk8.dim(" 3. Fetch a document: fractary codex fetch codex://org/project/path"));
957
- console.log(chalk8.dim(" 4. Check cache: fractary codex cache list"));
1195
+ console.log(chalk8.dim(" 1. Configure AWS credentials for S3 access"));
1196
+ console.log(chalk8.dim(" 2. Edit .fractary/config.yaml to add external project dependencies"));
1197
+ console.log(chalk8.dim(" 3. Access current project files: codex://specs/SPEC-001.md"));
1198
+ console.log(chalk8.dim(" 4. Access external projects: codex://org/project/docs/README.md"));
958
1199
  } catch (error) {
959
1200
  console.error(chalk8.red("Error:"), error.message);
960
1201
  process.exit(1);
@@ -981,8 +1222,8 @@ function migrateCommand() {
981
1222
  const cmd = new Command("migrate");
982
1223
  cmd.description("Migrate legacy JSON configuration to v3.0 YAML format").option("--dry-run", "Show migration plan without executing").option("--no-backup", "Skip creating backup of old config").option("--json", "Output as JSON").action(async (options) => {
983
1224
  try {
984
- const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
985
- const newConfigPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1225
+ const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1226
+ const newConfigPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
986
1227
  if (!await fileExists2(legacyConfigPath)) {
987
1228
  if (options.json) {
988
1229
  console.log(JSON.stringify({
@@ -1015,8 +1256,9 @@ function migrateCommand() {
1015
1256
  let legacyConfig;
1016
1257
  try {
1017
1258
  legacyConfig = JSON.parse(legacyContent);
1018
- } catch {
1259
+ } catch (parseError) {
1019
1260
  console.error(chalk8.red("Error:"), "Invalid JSON in legacy config file.");
1261
+ console.error(chalk8.dim("Details:"), parseError.message);
1020
1262
  process.exit(1);
1021
1263
  }
1022
1264
  if (!options.json && !options.dryRun) {
@@ -1077,19 +1319,34 @@ function migrateCommand() {
1077
1319
  }
1078
1320
  if (!options.dryRun) {
1079
1321
  await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
1080
- const cacheDir = path4.join(process.cwd(), ".codex-cache");
1322
+ const configuredCacheDir = migrationResult.yamlConfig.cacheDir || ".fractary/codex/cache";
1323
+ const cacheDir = path5.join(process.cwd(), configuredCacheDir);
1081
1324
  await fs.mkdir(cacheDir, { recursive: true });
1325
+ const gitignoreResult = await ensureCachePathIgnored(process.cwd(), configuredCacheDir);
1326
+ const isCustomCachePath = normalizeCachePath(configuredCacheDir) !== DEFAULT_CACHE_DIR;
1082
1327
  if (!options.json) {
1083
1328
  console.log(chalk8.green("\u2713"), "YAML configuration created");
1084
1329
  console.log(chalk8.green("\u2713"), "Cache directory initialized");
1085
1330
  if (migrationResult.backupPath) {
1086
1331
  console.log(chalk8.green("\u2713"), "Legacy config backed up");
1087
1332
  }
1333
+ if (gitignoreResult.created) {
1334
+ console.log(chalk8.green("\u2713"), ".fractary/.gitignore created");
1335
+ } else if (gitignoreResult.updated) {
1336
+ console.log(chalk8.green("\u2713"), ".fractary/.gitignore updated with cache path");
1337
+ } else if (gitignoreResult.alreadyIgnored) {
1338
+ console.log(chalk8.green("\u2713"), "Cache path already in .fractary/.gitignore");
1339
+ }
1340
+ if (isCustomCachePath) {
1341
+ console.log("");
1342
+ console.log(chalk8.yellow("\u26A0 Custom cache directory detected:"), chalk8.dim(configuredCacheDir));
1343
+ console.log(chalk8.dim(" Ensure .fractary/.gitignore includes this path to avoid committing cache files."));
1344
+ }
1088
1345
  console.log("");
1089
1346
  console.log(chalk8.bold("New Configuration:"));
1090
1347
  console.log(chalk8.dim(` Path: ${newConfigPath}`));
1091
1348
  console.log(chalk8.dim(` Organization: ${migrationResult.yamlConfig.organization}`));
1092
- console.log(chalk8.dim(` Cache: ${migrationResult.yamlConfig.cacheDir || ".codex-cache"}`));
1349
+ console.log(chalk8.dim(` Cache: ${configuredCacheDir}`));
1093
1350
  console.log(chalk8.dim(` Storage Providers: ${migrationResult.yamlConfig.storage?.length || 0}`));
1094
1351
  console.log("");
1095
1352
  console.log(chalk8.bold("Next Steps:"));
@@ -1098,7 +1355,7 @@ function migrateCommand() {
1098
1355
  console.log(chalk8.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
1099
1356
  if (migrationResult.backupPath) {
1100
1357
  console.log("");
1101
- console.log(chalk8.dim(`Backup saved: ${path4.basename(migrationResult.backupPath)}`));
1358
+ console.log(chalk8.dim(`Backup saved: ${path5.basename(migrationResult.backupPath)}`));
1102
1359
  }
1103
1360
  }
1104
1361
  }
@@ -1308,8 +1565,8 @@ async function fileExists3(filePath) {
1308
1565
  }
1309
1566
  }
1310
1567
  async function checkConfiguration() {
1311
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1312
- const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1568
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
1569
+ const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1313
1570
  try {
1314
1571
  if (!await fileExists3(configPath)) {
1315
1572
  if (await fileExists3(legacyConfigPath)) {
@@ -1416,7 +1673,7 @@ async function checkCache() {
1416
1673
  }
1417
1674
  }
1418
1675
  async function checkStorage() {
1419
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1676
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
1420
1677
  try {
1421
1678
  const config = await readYamlConfig(configPath);
1422
1679
  const providers = config.storage || [];
@@ -1574,7 +1831,7 @@ function syncCommand() {
1574
1831
  const cmd = new Command("sync");
1575
1832
  cmd.description("Sync single project with codex repository").argument("[name]", "Project name (auto-detected if not provided)").option("--env <env>", "Target environment (dev/test/staging/prod)", "prod").option("--dry-run", "Show what would sync without executing").option("--direction <dir>", "Sync direction (to-codex/from-codex/bidirectional)", "bidirectional").option("--include <pattern>", "Include files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--exclude <pattern>", "Exclude files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--force", "Force sync without checking timestamps").option("--json", "Output as JSON").action(async (name, options) => {
1576
1833
  try {
1577
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1834
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
1578
1835
  let config;
1579
1836
  try {
1580
1837
  config = await readYamlConfig(configPath);
@@ -1608,7 +1865,7 @@ function syncCommand() {
1608
1865
  const syncManager = createSyncManager({
1609
1866
  localStorage,
1610
1867
  config: config.sync,
1611
- manifestPath: path4.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1868
+ manifestPath: path5.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1612
1869
  });
1613
1870
  const defaultToCodexPatterns = [
1614
1871
  "docs/**/*.md",
@@ -1659,8 +1916,8 @@ function syncCommand() {
1659
1916
  }
1660
1917
  const targetFiles = await Promise.all(
1661
1918
  Array.from(matchedFilePaths).map(async (filePath) => {
1662
- const fullPath = path4.join(sourceDir, filePath);
1663
- const stats = await import('fs/promises').then((fs7) => fs7.stat(fullPath));
1919
+ const fullPath = path5.join(sourceDir, filePath);
1920
+ const stats = await import('fs/promises').then((fs9) => fs9.stat(fullPath));
1664
1921
  return {
1665
1922
  path: filePath,
1666
1923
  size: stats.size,
@@ -2108,7 +2365,7 @@ function typesAddCommand() {
2108
2365
  console.log(chalk8.dim("Examples: 30m, 24h, 7d"));
2109
2366
  process.exit(1);
2110
2367
  }
2111
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
2368
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
2112
2369
  const config = await readYamlConfig(configPath);
2113
2370
  if (!config.types) {
2114
2371
  config.types = { custom: {} };
@@ -2177,7 +2434,7 @@ function typesRemoveCommand() {
2177
2434
  process.exit(1);
2178
2435
  }
2179
2436
  const typeInfo = registry.get(name);
2180
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
2437
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
2181
2438
  const config = await readYamlConfig(configPath);
2182
2439
  if (!config.types?.custom?.[name]) {
2183
2440
  console.error(chalk8.red("Error:"), `Custom type "${name}" not found in configuration.`);