@fractary/codex-cli 0.8.0 → 0.9.0

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,129 @@ 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/commands/config/init.ts
872
994
  async function getOrgFromGitRemote() {
873
995
  try {
874
996
  const { execSync } = __require("child_process");
@@ -890,9 +1012,9 @@ async function fileExists(filePath) {
890
1012
  }
891
1013
  function initCommand() {
892
1014
  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) => {
1015
+ 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
1016
  try {
895
- console.log(chalk8.blue("Initializing Codex v3.0 (YAML format)...\n"));
1017
+ console.log(chalk8.blue("Initializing unified Fractary configuration...\n"));
896
1018
  let org = options.org;
897
1019
  if (!org) {
898
1020
  org = await getOrgFromGitRemote();
@@ -901,60 +1023,71 @@ function initCommand() {
901
1023
  try {
902
1024
  const { resolveOrganization } = await import('@fractary/codex');
903
1025
  org = resolveOrganization({
904
- repoName: path4.basename(process.cwd())
1026
+ repoName: path5.basename(process.cwd())
905
1027
  });
906
1028
  } catch {
907
1029
  }
908
1030
  }
909
1031
  if (!org) {
910
- org = path4.basename(process.cwd()).split("-")[0] || "default";
1032
+ org = path5.basename(process.cwd()).split("-")[0] || "default";
911
1033
  console.log(chalk8.yellow(`\u26A0 Could not detect organization, using: ${org}`));
912
1034
  console.log(chalk8.dim(" Use --org <slug> to specify explicitly\n"));
913
1035
  } else {
914
1036
  console.log(chalk8.dim(`Organization: ${chalk8.cyan(org)}
915
1037
  `));
916
1038
  }
917
- const configDir = path4.join(process.cwd(), ".fractary", "codex");
918
- const configPath = path4.join(configDir, "config.yaml");
1039
+ let project = options.project;
1040
+ if (!project) {
1041
+ project = path5.basename(process.cwd());
1042
+ console.log(chalk8.dim(`Project: ${chalk8.cyan(project)}
1043
+ `));
1044
+ }
1045
+ const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
919
1046
  const configExists = await fileExists(configPath);
920
1047
  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);
1048
+ console.log(chalk8.yellow(`\u26A0 Configuration already exists at .fractary/config.yaml`));
1049
+ console.log(chalk8.dim("Merging with existing configuration...\n"));
924
1050
  }
925
1051
  console.log("Creating directory structure...");
926
1052
  const dirs = [
1053
+ ".fractary",
1054
+ ".fractary/specs",
1055
+ ".fractary/logs",
927
1056
  ".fractary/codex",
928
1057
  ".fractary/codex/cache"
929
1058
  ];
930
1059
  for (const dir of dirs) {
931
- await fs.mkdir(path4.join(process.cwd(), dir), { recursive: true });
1060
+ await fs.mkdir(path5.join(process.cwd(), dir), { recursive: true });
932
1061
  console.log(chalk8.green("\u2713"), chalk8.dim(dir + "/"));
933
1062
  }
934
- console.log("\nCreating YAML configuration...");
935
- const config = getDefaultYamlConfig(org);
936
- if (options.mcp && config.mcp) {
937
- config.mcp.enabled = true;
1063
+ console.log("\nInitializing configuration...");
1064
+ const result = await initializeUnifiedConfig(
1065
+ configPath,
1066
+ org,
1067
+ project,
1068
+ { force: options.force }
1069
+ );
1070
+ if (result.created) {
1071
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/config.yaml (created)"));
1072
+ } else if (result.merged) {
1073
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/config.yaml (merged with existing)"));
938
1074
  }
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"));
1075
+ console.log(chalk8.green("\n\u2713 Unified configuration initialized successfully!\n"));
942
1076
  console.log(chalk8.bold("Configuration:"));
943
1077
  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"));
1078
+ console.log(chalk8.dim(` Project: ${project}`));
1079
+ console.log(chalk8.dim(` Config: .fractary/config.yaml`));
1080
+ console.log(chalk8.bold("\nFile plugin sources:"));
1081
+ console.log(chalk8.dim(" - specs: .fractary/specs/ \u2192 S3"));
1082
+ console.log(chalk8.dim(" - logs: .fractary/logs/ \u2192 S3"));
1083
+ console.log(chalk8.bold("\nCodex plugin:"));
1084
+ console.log(chalk8.dim(" - Cache: .fractary/codex/cache/"));
1085
+ console.log(chalk8.dim(" - Dependencies: (none configured)"));
953
1086
  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"));
1087
+ console.log(chalk8.dim(" 1. Configure AWS credentials for S3 access"));
1088
+ console.log(chalk8.dim(" 2. Edit .fractary/config.yaml to add external project dependencies"));
1089
+ console.log(chalk8.dim(" 3. Access current project files: codex://specs/SPEC-001.md"));
1090
+ console.log(chalk8.dim(" 4. Access external projects: codex://org/project/docs/README.md"));
958
1091
  } catch (error) {
959
1092
  console.error(chalk8.red("Error:"), error.message);
960
1093
  process.exit(1);
@@ -981,8 +1114,8 @@ function migrateCommand() {
981
1114
  const cmd = new Command("migrate");
982
1115
  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
1116
  try {
984
- const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
985
- const newConfigPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1117
+ const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1118
+ const newConfigPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
986
1119
  if (!await fileExists2(legacyConfigPath)) {
987
1120
  if (options.json) {
988
1121
  console.log(JSON.stringify({
@@ -1077,7 +1210,7 @@ function migrateCommand() {
1077
1210
  }
1078
1211
  if (!options.dryRun) {
1079
1212
  await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
1080
- const cacheDir = path4.join(process.cwd(), ".codex-cache");
1213
+ const cacheDir = path5.join(process.cwd(), ".codex-cache");
1081
1214
  await fs.mkdir(cacheDir, { recursive: true });
1082
1215
  if (!options.json) {
1083
1216
  console.log(chalk8.green("\u2713"), "YAML configuration created");
@@ -1098,7 +1231,7 @@ function migrateCommand() {
1098
1231
  console.log(chalk8.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
1099
1232
  if (migrationResult.backupPath) {
1100
1233
  console.log("");
1101
- console.log(chalk8.dim(`Backup saved: ${path4.basename(migrationResult.backupPath)}`));
1234
+ console.log(chalk8.dim(`Backup saved: ${path5.basename(migrationResult.backupPath)}`));
1102
1235
  }
1103
1236
  }
1104
1237
  }
@@ -1308,8 +1441,8 @@ async function fileExists3(filePath) {
1308
1441
  }
1309
1442
  }
1310
1443
  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");
1444
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
1445
+ const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1313
1446
  try {
1314
1447
  if (!await fileExists3(configPath)) {
1315
1448
  if (await fileExists3(legacyConfigPath)) {
@@ -1416,7 +1549,7 @@ async function checkCache() {
1416
1549
  }
1417
1550
  }
1418
1551
  async function checkStorage() {
1419
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1552
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
1420
1553
  try {
1421
1554
  const config = await readYamlConfig(configPath);
1422
1555
  const providers = config.storage || [];
@@ -1574,7 +1707,7 @@ function syncCommand() {
1574
1707
  const cmd = new Command("sync");
1575
1708
  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
1709
  try {
1577
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
1710
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
1578
1711
  let config;
1579
1712
  try {
1580
1713
  config = await readYamlConfig(configPath);
@@ -1608,7 +1741,7 @@ function syncCommand() {
1608
1741
  const syncManager = createSyncManager({
1609
1742
  localStorage,
1610
1743
  config: config.sync,
1611
- manifestPath: path4.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1744
+ manifestPath: path5.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1612
1745
  });
1613
1746
  const defaultToCodexPatterns = [
1614
1747
  "docs/**/*.md",
@@ -1659,8 +1792,8 @@ function syncCommand() {
1659
1792
  }
1660
1793
  const targetFiles = await Promise.all(
1661
1794
  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));
1795
+ const fullPath = path5.join(sourceDir, filePath);
1796
+ const stats = await import('fs/promises').then((fs8) => fs8.stat(fullPath));
1664
1797
  return {
1665
1798
  path: filePath,
1666
1799
  size: stats.size,
@@ -2108,7 +2241,7 @@ function typesAddCommand() {
2108
2241
  console.log(chalk8.dim("Examples: 30m, 24h, 7d"));
2109
2242
  process.exit(1);
2110
2243
  }
2111
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
2244
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
2112
2245
  const config = await readYamlConfig(configPath);
2113
2246
  if (!config.types) {
2114
2247
  config.types = { custom: {} };
@@ -2177,7 +2310,7 @@ function typesRemoveCommand() {
2177
2310
  process.exit(1);
2178
2311
  }
2179
2312
  const typeInfo = registry.get(name);
2180
- const configPath = path4.join(process.cwd(), ".fractary", "codex", "config.yaml");
2313
+ const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
2181
2314
  const config = await readYamlConfig(configPath);
2182
2315
  if (!config.types?.custom?.[name]) {
2183
2316
  console.error(chalk8.red("Error:"), `Custom type "${name}" not found in configuration.`);