@fractary/codex-cli 0.2.2 → 0.4.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,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import * as path3 from 'path';
2
+ import * as path4 from 'path';
3
3
  import 'url';
4
4
  import * as fs from 'fs/promises';
5
5
  import * as yaml from 'js-yaml';
6
6
  import { ValidationError, PermissionDeniedError, ConfigurationError, CodexError } from '@fractary/codex';
7
7
  import { Command } from 'commander';
8
- import chalk6 from 'chalk';
9
- import { execSync } from 'child_process';
8
+ import chalk8 from 'chalk';
9
+ import * as crypto from 'crypto';
10
10
 
11
11
  var __defProp = Object.defineProperty;
12
12
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -61,7 +61,7 @@ async function migrateConfig(legacyConfigPath, options) {
61
61
  organization: legacy.organization || legacy.organizationSlug || "default"
62
62
  };
63
63
  if (legacy.cache) {
64
- yamlConfig.cacheDir = legacy.cache.directory || ".codex-cache";
64
+ yamlConfig.cacheDir = legacy.cache.directory || ".fractary/codex/cache";
65
65
  }
66
66
  if (legacy.storage?.providers) {
67
67
  yamlConfig.storage = [];
@@ -157,7 +157,7 @@ async function migrateConfig(legacyConfigPath, options) {
157
157
  }
158
158
  }
159
159
  async function writeYamlConfig(config, outputPath) {
160
- const dir = path3.dirname(outputPath);
160
+ const dir = path4.dirname(outputPath);
161
161
  await fs.mkdir(dir, { recursive: true });
162
162
  const yamlContent = yaml.dump(config, {
163
163
  indent: 2,
@@ -170,7 +170,7 @@ async function writeYamlConfig(config, outputPath) {
170
170
  function getDefaultYamlConfig(organization) {
171
171
  return {
172
172
  organization,
173
- cacheDir: ".codex-cache",
173
+ cacheDir: ".fractary/codex/cache",
174
174
  storage: [
175
175
  {
176
176
  type: "local",
@@ -381,7 +381,7 @@ var init_codex_client = __esm({
381
381
  const { readYamlConfig: readYamlConfig2 } = await Promise.resolve().then(() => (init_migrate_config(), migrate_config_exports));
382
382
  const { resolveEnvVarsInConfig: resolveEnvVarsInConfig2 } = await Promise.resolve().then(() => (init_config_types(), config_types_exports));
383
383
  try {
384
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
384
+ const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
385
385
  let config;
386
386
  try {
387
387
  config = await readYamlConfig2(configPath);
@@ -593,13 +593,110 @@ var init_codex_client = __esm({
593
593
  // src/cli.ts
594
594
  init_esm_shims();
595
595
 
596
- // src/commands/init.ts
596
+ // src/commands/document/index.ts
597
+ init_esm_shims();
598
+
599
+ // src/commands/document/fetch.ts
600
+ init_esm_shims();
601
+
602
+ // src/client/get-client.ts
603
+ init_esm_shims();
604
+ var clientInstance = null;
605
+ async function getClient(options) {
606
+ if (!clientInstance) {
607
+ const { CodexClient: CodexClient2 } = await Promise.resolve().then(() => (init_codex_client(), codex_client_exports));
608
+ clientInstance = await CodexClient2.create(options);
609
+ }
610
+ return clientInstance;
611
+ }
612
+
613
+ // src/commands/document/fetch.ts
614
+ function hashContent(content) {
615
+ return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
616
+ }
617
+ function fetchCommand() {
618
+ const cmd = new Command("fetch");
619
+ cmd.description("Fetch a document by codex:// URI reference").argument("<uri>", "Codex URI (e.g., codex://org/project/docs/file.md)").option("--bypass-cache", "Skip cache and fetch directly from source").option("--ttl <seconds>", "Override default TTL (in seconds)", parseInt).option("--json", "Output as JSON with metadata").option("--output <file>", "Write content to file instead of stdout").action(async (uri, options) => {
620
+ try {
621
+ const { validateUri } = await import('@fractary/codex');
622
+ if (!validateUri(uri)) {
623
+ console.error(chalk8.red("Error: Invalid URI format"));
624
+ console.log(chalk8.dim("Expected: codex://org/project/path/to/file.md"));
625
+ console.log(chalk8.dim("Example: codex://fractary/codex/docs/api.md"));
626
+ process.exit(1);
627
+ }
628
+ const client = await getClient();
629
+ if (!options.json && !options.bypassCache) {
630
+ console.error(chalk8.dim(`Fetching ${uri}...`));
631
+ }
632
+ const result = await client.fetch(uri, {
633
+ bypassCache: options.bypassCache,
634
+ ttl: options.ttl
635
+ });
636
+ if (options.json) {
637
+ const output = {
638
+ uri,
639
+ content: result.content.toString("utf-8"),
640
+ metadata: {
641
+ fromCache: result.fromCache,
642
+ fetchedAt: result.metadata?.fetchedAt,
643
+ expiresAt: result.metadata?.expiresAt,
644
+ contentLength: result.metadata?.contentLength || result.content.length,
645
+ contentHash: hashContent(result.content)
646
+ }
647
+ };
648
+ console.log(JSON.stringify(output, null, 2));
649
+ } else if (options.output) {
650
+ await fs.writeFile(options.output, result.content);
651
+ console.log(chalk8.green("\u2713"), `Written to ${options.output}`);
652
+ console.log(chalk8.dim(` Size: ${result.content.length} bytes`));
653
+ if (result.fromCache) {
654
+ console.log(chalk8.dim(" Source: cache"));
655
+ } else {
656
+ console.log(chalk8.dim(" Source: storage"));
657
+ }
658
+ } else {
659
+ if (result.fromCache && !options.bypassCache) {
660
+ console.error(chalk8.green("\u2713"), chalk8.dim("from cache\n"));
661
+ } else {
662
+ console.error(chalk8.green("\u2713"), chalk8.dim("fetched\n"));
663
+ }
664
+ console.log(result.content.toString("utf-8"));
665
+ }
666
+ } catch (error) {
667
+ console.error(chalk8.red("Error:"), error.message);
668
+ if (error.message.includes("Failed to load configuration")) {
669
+ console.log(chalk8.dim('\nRun "fractary codex init" to create a configuration.'));
670
+ } else if (error.message.includes("GITHUB_TOKEN")) {
671
+ console.log(chalk8.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
672
+ } else if (error.message.includes("not found") || error.message.includes("404")) {
673
+ console.log(chalk8.dim("\nThe document may not exist or you may not have access."));
674
+ console.log(chalk8.dim("Check the URI and ensure your storage providers are configured correctly."));
675
+ }
676
+ process.exit(1);
677
+ }
678
+ });
679
+ return cmd;
680
+ }
681
+
682
+ // src/commands/document/index.ts
683
+ function documentCommand() {
684
+ const cmd = new Command("document");
685
+ cmd.description("Manage document operations");
686
+ cmd.addCommand(fetchCommand());
687
+ return cmd;
688
+ }
689
+
690
+ // src/commands/config/index.ts
691
+ init_esm_shims();
692
+
693
+ // src/commands/config/init.ts
597
694
  init_esm_shims();
598
695
  init_migrate_config();
599
696
  async function getOrgFromGitRemote() {
600
697
  try {
601
- const { execSync: execSync2 } = __require("child_process");
602
- const remote = execSync2("git remote get-url origin 2>/dev/null", { encoding: "utf-8" }).trim();
698
+ const { execSync } = __require("child_process");
699
+ const remote = execSync("git remote get-url origin 2>/dev/null", { encoding: "utf-8" }).trim();
603
700
  const sshMatch = remote.match(/git@github\.com:([^/]+)\//);
604
701
  const httpsMatch = remote.match(/github\.com\/([^/]+)\//);
605
702
  return sshMatch?.[1] || httpsMatch?.[1] || null;
@@ -619,7 +716,7 @@ function initCommand() {
619
716
  const cmd = new Command("init");
620
717
  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) => {
621
718
  try {
622
- console.log(chalk6.blue("Initializing Codex v3.0 (YAML format)...\n"));
719
+ console.log(chalk8.blue("Initializing Codex v3.0 (YAML format)...\n"));
623
720
  let org = options.org;
624
721
  if (!org) {
625
722
  org = await getOrgFromGitRemote();
@@ -628,41 +725,35 @@ function initCommand() {
628
725
  try {
629
726
  const { resolveOrganization } = await import('@fractary/codex');
630
727
  org = resolveOrganization({
631
- repoName: path3.basename(process.cwd())
728
+ repoName: path4.basename(process.cwd())
632
729
  });
633
730
  } catch {
634
731
  }
635
732
  }
636
733
  if (!org) {
637
- org = path3.basename(process.cwd()).split("-")[0] || "default";
638
- console.log(chalk6.yellow(`\u26A0 Could not detect organization, using: ${org}`));
639
- console.log(chalk6.dim(" Use --org <slug> to specify explicitly\n"));
734
+ org = path4.basename(process.cwd()).split("-")[0] || "default";
735
+ console.log(chalk8.yellow(`\u26A0 Could not detect organization, using: ${org}`));
736
+ console.log(chalk8.dim(" Use --org <slug> to specify explicitly\n"));
640
737
  } else {
641
- console.log(chalk6.dim(`Organization: ${chalk6.cyan(org)}
738
+ console.log(chalk8.dim(`Organization: ${chalk8.cyan(org)}
642
739
  `));
643
740
  }
644
- const configDir = path3.join(process.cwd(), ".fractary");
645
- const configPath = path3.join(configDir, "codex.yaml");
741
+ const configDir = path4.join(process.cwd(), ".fractary", "codex");
742
+ const configPath = path4.join(configDir, "config.yaml");
646
743
  const configExists = await fileExists(configPath);
647
- const legacyConfigPath = path3.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
648
- const legacyExists = await fileExists(legacyConfigPath);
649
744
  if (configExists && !options.force) {
650
- console.log(chalk6.yellow("\u26A0 Configuration already exists at .fractary/codex.yaml"));
651
- console.log(chalk6.dim("Use --force to overwrite"));
745
+ console.log(chalk8.yellow("\u26A0 Configuration already exists at .fractary/codex/config.yaml"));
746
+ console.log(chalk8.dim("Use --force to overwrite"));
652
747
  process.exit(1);
653
748
  }
654
- if (legacyExists && !configExists) {
655
- console.log(chalk6.yellow("\u26A0 Legacy configuration detected at .fractary/plugins/codex/config.json"));
656
- console.log(chalk6.dim('Run "fractary codex migrate" to upgrade to YAML format\n'));
657
- }
658
749
  console.log("Creating directory structure...");
659
750
  const dirs = [
660
- ".fractary",
661
- ".codex-cache"
751
+ ".fractary/codex",
752
+ ".fractary/codex/cache"
662
753
  ];
663
754
  for (const dir of dirs) {
664
- await fs.mkdir(path3.join(process.cwd(), dir), { recursive: true });
665
- console.log(chalk6.green("\u2713"), chalk6.dim(dir + "/"));
755
+ await fs.mkdir(path4.join(process.cwd(), dir), { recursive: true });
756
+ console.log(chalk8.green("\u2713"), chalk8.dim(dir + "/"));
666
757
  }
667
758
  console.log("\nCreating YAML configuration...");
668
759
  const config = getDefaultYamlConfig(org);
@@ -670,113 +761,179 @@ function initCommand() {
670
761
  config.mcp.enabled = true;
671
762
  }
672
763
  await writeYamlConfig(config, configPath);
673
- console.log(chalk6.green("\u2713"), chalk6.dim(".fractary/codex.yaml"));
674
- console.log(chalk6.green("\n\u2713 Codex v3.0 initialized successfully!\n"));
675
- console.log(chalk6.bold("Configuration:"));
676
- console.log(chalk6.dim(` Organization: ${org}`));
677
- console.log(chalk6.dim(` Cache: .codex-cache/`));
678
- console.log(chalk6.dim(` Config: .fractary/codex.yaml`));
764
+ console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/codex/config.yaml"));
765
+ console.log(chalk8.green("\n\u2713 Codex v4.0 initialized successfully!\n"));
766
+ console.log(chalk8.bold("Configuration:"));
767
+ console.log(chalk8.dim(` Organization: ${org}`));
768
+ console.log(chalk8.dim(` Cache: .fractary/codex/cache/`));
769
+ console.log(chalk8.dim(` Config: .fractary/codex/config.yaml`));
679
770
  if (options.mcp) {
680
- console.log(chalk6.dim(` MCP Server: Enabled (port 3000)`));
681
- }
682
- console.log(chalk6.bold("\nStorage providers configured:"));
683
- console.log(chalk6.dim(" - Local filesystem (./knowledge)"));
684
- console.log(chalk6.dim(" - GitHub (requires GITHUB_TOKEN)"));
685
- console.log(chalk6.dim(" - HTTP endpoint"));
686
- console.log(chalk6.bold("\nNext steps:"));
687
- console.log(chalk6.dim(' 1. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
688
- console.log(chalk6.dim(" 2. Edit .fractary/codex.yaml to configure storage providers"));
689
- console.log(chalk6.dim(" 3. Fetch a document: fractary codex fetch codex://org/project/path"));
690
- console.log(chalk6.dim(" 4. Check cache: fractary codex cache list"));
691
- if (legacyExists) {
692
- console.log(chalk6.yellow("\n\u26A0 Legacy config detected:"));
693
- console.log(chalk6.dim(' Run "fractary codex migrate" to convert your existing config'));
694
- }
771
+ console.log(chalk8.dim(` MCP Server: Enabled (port 3000)`));
772
+ }
773
+ console.log(chalk8.bold("\nStorage providers configured:"));
774
+ console.log(chalk8.dim(" - Local filesystem (./knowledge)"));
775
+ console.log(chalk8.dim(" - GitHub (requires GITHUB_TOKEN)"));
776
+ console.log(chalk8.dim(" - HTTP endpoint"));
777
+ console.log(chalk8.bold("\nNext steps:"));
778
+ console.log(chalk8.dim(' 1. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
779
+ console.log(chalk8.dim(" 2. Edit .fractary/codex/config.yaml to configure storage providers"));
780
+ console.log(chalk8.dim(" 3. Fetch a document: fractary codex fetch codex://org/project/path"));
781
+ console.log(chalk8.dim(" 4. Check cache: fractary codex cache list"));
695
782
  } catch (error) {
696
- console.error(chalk6.red("Error:"), error.message);
783
+ console.error(chalk8.red("Error:"), error.message);
697
784
  process.exit(1);
698
785
  }
699
786
  });
700
787
  return cmd;
701
788
  }
702
789
 
703
- // src/commands/fetch.ts
704
- init_esm_shims();
705
-
706
- // src/client/get-client.ts
790
+ // src/commands/config/migrate.ts
707
791
  init_esm_shims();
708
- var clientInstance = null;
709
- async function getClient(options) {
710
- if (!clientInstance) {
711
- const { CodexClient: CodexClient2 } = await Promise.resolve().then(() => (init_codex_client(), codex_client_exports));
712
- clientInstance = await CodexClient2.create(options);
792
+ init_migrate_config();
793
+ async function fileExists2(filePath) {
794
+ try {
795
+ await fs.access(filePath);
796
+ return true;
797
+ } catch {
798
+ return false;
713
799
  }
714
- return clientInstance;
715
800
  }
716
-
717
- // src/commands/fetch.ts
718
- function hashContent(content) {
719
- const crypto = __require("crypto");
720
- return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
801
+ async function readFileContent(filePath) {
802
+ return fs.readFile(filePath, "utf-8");
721
803
  }
722
- function fetchCommand() {
723
- const cmd = new Command("fetch");
724
- cmd.description("Fetch a document by codex:// URI reference").argument("<uri>", "Codex URI (e.g., codex://org/project/docs/file.md)").option("--bypass-cache", "Skip cache and fetch directly from source").option("--ttl <seconds>", "Override default TTL (in seconds)", parseInt).option("--json", "Output as JSON with metadata").option("--output <file>", "Write content to file instead of stdout").action(async (uri, options) => {
804
+ function migrateCommand() {
805
+ const cmd = new Command("migrate");
806
+ 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) => {
725
807
  try {
726
- const { validateUri } = await import('@fractary/codex');
727
- if (!validateUri(uri)) {
728
- console.error(chalk6.red("Error: Invalid URI format"));
729
- console.log(chalk6.dim("Expected: codex://org/project/path/to/file.md"));
730
- console.log(chalk6.dim("Example: codex://fractary/codex/docs/api.md"));
808
+ const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
809
+ const newConfigPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
810
+ if (!await fileExists2(legacyConfigPath)) {
811
+ if (options.json) {
812
+ console.log(JSON.stringify({
813
+ status: "no_config",
814
+ message: "No legacy configuration file found",
815
+ path: legacyConfigPath
816
+ }));
817
+ } else {
818
+ console.log(chalk8.yellow("\u26A0 No legacy configuration file found."));
819
+ console.log(chalk8.dim(` Expected: ${legacyConfigPath}`));
820
+ console.log(chalk8.dim('\nRun "fractary codex init" to create a new v3.0 YAML configuration.'));
821
+ }
822
+ return;
823
+ }
824
+ if (await fileExists2(newConfigPath) && !options.dryRun) {
825
+ if (options.json) {
826
+ console.log(JSON.stringify({
827
+ status: "already_migrated",
828
+ message: "YAML configuration already exists",
829
+ path: newConfigPath
830
+ }));
831
+ } else {
832
+ console.log(chalk8.yellow("\u26A0 YAML configuration already exists."));
833
+ console.log(chalk8.dim(` Path: ${newConfigPath}`));
834
+ console.log(chalk8.dim('\nUse "fractary codex init --force" to recreate.'));
835
+ }
836
+ return;
837
+ }
838
+ const legacyContent = await readFileContent(legacyConfigPath);
839
+ let legacyConfig;
840
+ try {
841
+ legacyConfig = JSON.parse(legacyContent);
842
+ } catch {
843
+ console.error(chalk8.red("Error:"), "Invalid JSON in legacy config file.");
731
844
  process.exit(1);
732
845
  }
733
- const client = await getClient();
734
- if (!options.json && !options.bypassCache) {
735
- console.error(chalk6.dim(`Fetching ${uri}...`));
846
+ if (!options.json && !options.dryRun) {
847
+ console.log(chalk8.blue("Migrating Codex configuration to v3.0 YAML format...\n"));
848
+ }
849
+ const migrationResult = await migrateConfig(
850
+ legacyConfigPath,
851
+ {
852
+ createBackup: options.backup !== false,
853
+ backupSuffix: (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
854
+ }
855
+ );
856
+ if (!options.json) {
857
+ console.log(chalk8.bold("Legacy Configuration:"));
858
+ console.log(chalk8.dim(` Path: ${legacyConfigPath}`));
859
+ console.log(chalk8.dim(` Organization: ${legacyConfig.organization || legacyConfig.organizationSlug || "unknown"}`));
860
+ console.log("");
861
+ console.log(chalk8.bold("Migration Changes:"));
862
+ console.log(chalk8.green(" + Format: JSON \u2192 YAML"));
863
+ console.log(chalk8.green(" + Location: .fractary/plugins/codex/ \u2192 .fractary/"));
864
+ console.log(chalk8.green(" + File: config.json \u2192 codex.yaml"));
865
+ console.log(chalk8.green(" + Storage: Multi-provider configuration"));
866
+ console.log(chalk8.green(" + Cache: Modern cache management"));
867
+ console.log(chalk8.green(" + Types: Custom type registry"));
868
+ if (migrationResult.warnings.length > 0) {
869
+ console.log("");
870
+ console.log(chalk8.yellow("Warnings:"));
871
+ for (const warning of migrationResult.warnings) {
872
+ console.log(chalk8.yellow(" \u26A0"), chalk8.dim(warning));
873
+ }
874
+ }
875
+ console.log("");
876
+ if (options.dryRun) {
877
+ console.log(chalk8.blue("Dry run - no changes made."));
878
+ console.log(chalk8.dim("Run without --dry-run to execute migration."));
879
+ return;
880
+ }
736
881
  }
737
- const result = await client.fetch(uri, {
738
- bypassCache: options.bypassCache,
739
- ttl: options.ttl
740
- });
741
882
  if (options.json) {
742
883
  const output = {
743
- uri,
744
- content: result.content.toString("utf-8"),
745
- metadata: {
746
- fromCache: result.fromCache,
747
- fetchedAt: result.metadata?.fetchedAt,
748
- expiresAt: result.metadata?.expiresAt,
749
- contentLength: result.metadata?.contentLength || result.content.length,
750
- contentHash: hashContent(result.content)
751
- }
884
+ status: options.dryRun ? "migration_ready" : "migrated",
885
+ dryRun: options.dryRun || false,
886
+ legacyConfig: {
887
+ path: legacyConfigPath,
888
+ organization: legacyConfig.organization || legacyConfig.organizationSlug
889
+ },
890
+ newConfig: {
891
+ path: newConfigPath,
892
+ organization: migrationResult.yamlConfig.organization
893
+ },
894
+ warnings: migrationResult.warnings,
895
+ backupPath: migrationResult.backupPath
752
896
  };
753
897
  console.log(JSON.stringify(output, null, 2));
754
- } else if (options.output) {
755
- await fs.writeFile(options.output, result.content);
756
- console.log(chalk6.green("\u2713"), `Written to ${options.output}`);
757
- console.log(chalk6.dim(` Size: ${result.content.length} bytes`));
758
- if (result.fromCache) {
759
- console.log(chalk6.dim(" Source: cache"));
760
- } else {
761
- console.log(chalk6.dim(" Source: storage"));
898
+ if (options.dryRun) {
899
+ return;
762
900
  }
763
- } else {
764
- if (result.fromCache && !options.bypassCache) {
765
- console.error(chalk6.green("\u2713"), chalk6.dim("from cache\n"));
766
- } else {
767
- console.error(chalk6.green("\u2713"), chalk6.dim("fetched\n"));
901
+ }
902
+ if (!options.dryRun) {
903
+ await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
904
+ const cacheDir = path4.join(process.cwd(), ".codex-cache");
905
+ await fs.mkdir(cacheDir, { recursive: true });
906
+ if (!options.json) {
907
+ console.log(chalk8.green("\u2713"), "YAML configuration created");
908
+ console.log(chalk8.green("\u2713"), "Cache directory initialized");
909
+ if (migrationResult.backupPath) {
910
+ console.log(chalk8.green("\u2713"), "Legacy config backed up");
911
+ }
912
+ console.log("");
913
+ console.log(chalk8.bold("New Configuration:"));
914
+ console.log(chalk8.dim(` Path: ${newConfigPath}`));
915
+ console.log(chalk8.dim(` Organization: ${migrationResult.yamlConfig.organization}`));
916
+ console.log(chalk8.dim(` Cache: ${migrationResult.yamlConfig.cacheDir || ".codex-cache"}`));
917
+ console.log(chalk8.dim(` Storage Providers: ${migrationResult.yamlConfig.storage?.length || 0}`));
918
+ console.log("");
919
+ console.log(chalk8.bold("Next Steps:"));
920
+ console.log(chalk8.dim(" 1. Review the new configuration: .fractary/codex.yaml"));
921
+ console.log(chalk8.dim(' 2. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
922
+ console.log(chalk8.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
923
+ if (migrationResult.backupPath) {
924
+ console.log("");
925
+ console.log(chalk8.dim(`Backup saved: ${path4.basename(migrationResult.backupPath)}`));
926
+ }
768
927
  }
769
- console.log(result.content.toString("utf-8"));
770
928
  }
771
929
  } catch (error) {
772
- console.error(chalk6.red("Error:"), error.message);
773
- if (error.message.includes("Failed to load configuration")) {
774
- console.log(chalk6.dim('\nRun "fractary codex init" to create a configuration.'));
775
- } else if (error.message.includes("GITHUB_TOKEN")) {
776
- console.log(chalk6.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
777
- } else if (error.message.includes("not found") || error.message.includes("404")) {
778
- console.log(chalk6.dim("\nThe document may not exist or you may not have access."));
779
- console.log(chalk6.dim("Check the URI and ensure your storage providers are configured correctly."));
930
+ if (options.json) {
931
+ console.log(JSON.stringify({
932
+ status: "error",
933
+ message: error.message
934
+ }));
935
+ } else {
936
+ console.error(chalk8.red("Error:"), error.message);
780
937
  }
781
938
  process.exit(1);
782
939
  }
@@ -784,6 +941,15 @@ function fetchCommand() {
784
941
  return cmd;
785
942
  }
786
943
 
944
+ // src/commands/config/index.ts
945
+ function configCommand() {
946
+ const cmd = new Command("config");
947
+ cmd.description("Manage configuration");
948
+ cmd.addCommand(initCommand());
949
+ cmd.addCommand(migrateCommand());
950
+ return cmd;
951
+ }
952
+
787
953
  // src/commands/cache/index.ts
788
954
  init_esm_shims();
789
955
 
@@ -804,8 +970,8 @@ function cacheListCommand() {
804
970
  if (options.json) {
805
971
  console.log(JSON.stringify({ entries: 0, message: "Cache is empty" }));
806
972
  } else {
807
- console.log(chalk6.yellow("Cache is empty."));
808
- console.log(chalk6.dim("Fetch some documents to populate the cache."));
973
+ console.log(chalk8.yellow("Cache is empty."));
974
+ console.log(chalk8.dim("Fetch some documents to populate the cache."));
809
975
  }
810
976
  return;
811
977
  }
@@ -819,25 +985,25 @@ function cacheListCommand() {
819
985
  }, null, 2));
820
986
  return;
821
987
  }
822
- console.log(chalk6.bold("Cache Overview\n"));
823
- console.log(chalk6.bold("Entries:"));
824
- console.log(` Total: ${chalk6.cyan(stats.entryCount.toString())} entries`);
825
- console.log(` Fresh: ${chalk6.green(stats.freshCount.toString())} entries`);
826
- console.log(` Stale: ${stats.staleCount > 0 ? chalk6.yellow(stats.staleCount.toString()) : chalk6.dim("0")} entries`);
827
- console.log(` Expired: ${stats.expiredCount > 0 ? chalk6.red(stats.expiredCount.toString()) : chalk6.dim("0")} entries`);
988
+ console.log(chalk8.bold("Cache Overview\n"));
989
+ console.log(chalk8.bold("Entries:"));
990
+ console.log(` Total: ${chalk8.cyan(stats.entryCount.toString())} entries`);
991
+ console.log(` Fresh: ${chalk8.green(stats.freshCount.toString())} entries`);
992
+ console.log(` Stale: ${stats.staleCount > 0 ? chalk8.yellow(stats.staleCount.toString()) : chalk8.dim("0")} entries`);
993
+ console.log(` Expired: ${stats.expiredCount > 0 ? chalk8.red(stats.expiredCount.toString()) : chalk8.dim("0")} entries`);
828
994
  console.log("");
829
- console.log(chalk6.bold("Storage:"));
830
- console.log(` Total size: ${chalk6.cyan(formatSize(stats.totalSize))}`);
995
+ console.log(chalk8.bold("Storage:"));
996
+ console.log(` Total size: ${chalk8.cyan(formatSize(stats.totalSize))}`);
831
997
  console.log("");
832
998
  const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
833
- const healthColor = healthPercent > 80 ? chalk6.green : healthPercent > 50 ? chalk6.yellow : chalk6.red;
999
+ const healthColor = healthPercent > 80 ? chalk8.green : healthPercent > 50 ? chalk8.yellow : chalk8.red;
834
1000
  console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
835
1001
  console.log("");
836
- console.log(chalk6.dim("Note: Individual cache entries are managed by the SDK."));
837
- console.log(chalk6.dim('Use "fractary codex cache stats" for detailed statistics.'));
838
- console.log(chalk6.dim('Use "fractary codex cache clear" to clear cache entries.'));
1002
+ console.log(chalk8.dim("Note: Individual cache entries are managed by the SDK."));
1003
+ console.log(chalk8.dim('Use "fractary codex cache stats" for detailed statistics.'));
1004
+ console.log(chalk8.dim('Use "fractary codex cache clear" to clear cache entries.'));
839
1005
  } catch (error) {
840
- console.error(chalk6.red("Error:"), error.message);
1006
+ console.error(chalk8.red("Error:"), error.message);
841
1007
  process.exit(1);
842
1008
  }
843
1009
  });
@@ -853,7 +1019,7 @@ function cacheClearCommand() {
853
1019
  const client = await getClient();
854
1020
  const statsBefore = await client.getCacheStats();
855
1021
  if (statsBefore.entryCount === 0) {
856
- console.log(chalk6.yellow("Cache is already empty. Nothing to clear."));
1022
+ console.log(chalk8.yellow("Cache is already empty. Nothing to clear."));
857
1023
  return;
858
1024
  }
859
1025
  let pattern;
@@ -862,45 +1028,45 @@ function cacheClearCommand() {
862
1028
  } else if (options.pattern) {
863
1029
  pattern = options.pattern;
864
1030
  } else {
865
- console.log(chalk6.yellow("Please specify what to clear:"));
866
- console.log(chalk6.dim(" --all Clear entire cache"));
867
- console.log(chalk6.dim(' --pattern Clear entries matching pattern (e.g., "codex://fractary/*")'));
1031
+ console.log(chalk8.yellow("Please specify what to clear:"));
1032
+ console.log(chalk8.dim(" --all Clear entire cache"));
1033
+ console.log(chalk8.dim(' --pattern Clear entries matching pattern (e.g., "codex://fractary/*")'));
868
1034
  console.log("");
869
- console.log(chalk6.dim("Examples:"));
870
- console.log(chalk6.dim(" fractary codex cache clear --all"));
871
- console.log(chalk6.dim(' fractary codex cache clear --pattern "codex://fractary/cli/*"'));
1035
+ console.log(chalk8.dim("Examples:"));
1036
+ console.log(chalk8.dim(" fractary codex cache clear --all"));
1037
+ console.log(chalk8.dim(' fractary codex cache clear --pattern "codex://fractary/cli/*"'));
872
1038
  return;
873
1039
  }
874
1040
  if (options.dryRun) {
875
- console.log(chalk6.blue("Dry run - would clear:\n"));
1041
+ console.log(chalk8.blue("Dry run - would clear:\n"));
876
1042
  if (pattern) {
877
- console.log(chalk6.dim(` Pattern: ${pattern}`));
878
- console.log(chalk6.dim(` This would invalidate matching cache entries`));
1043
+ console.log(chalk8.dim(` Pattern: ${pattern}`));
1044
+ console.log(chalk8.dim(` This would invalidate matching cache entries`));
879
1045
  } else {
880
- console.log(chalk6.dim(` All cache entries (${statsBefore.entryCount} entries)`));
1046
+ console.log(chalk8.dim(` All cache entries (${statsBefore.entryCount} entries)`));
881
1047
  }
882
- console.log(chalk6.dim(`
1048
+ console.log(chalk8.dim(`
883
1049
  Total size: ${formatSize2(statsBefore.totalSize)}`));
884
1050
  return;
885
1051
  }
886
1052
  if (pattern) {
887
- console.log(chalk6.blue(`Clearing cache entries matching pattern: ${pattern}
1053
+ console.log(chalk8.blue(`Clearing cache entries matching pattern: ${pattern}
888
1054
  `));
889
1055
  await client.invalidateCache(pattern);
890
1056
  } else {
891
- console.log(chalk6.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
1057
+ console.log(chalk8.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
892
1058
  `));
893
1059
  await client.invalidateCache();
894
1060
  }
895
1061
  const statsAfter = await client.getCacheStats();
896
1062
  const entriesCleared = statsBefore.entryCount - statsAfter.entryCount;
897
1063
  const sizeFreed = statsBefore.totalSize - statsAfter.totalSize;
898
- console.log(chalk6.green(`\u2713 Cleared ${entriesCleared} entries (${formatSize2(sizeFreed)} freed)`));
1064
+ console.log(chalk8.green(`\u2713 Cleared ${entriesCleared} entries (${formatSize2(sizeFreed)} freed)`));
899
1065
  if (statsAfter.entryCount > 0) {
900
- console.log(chalk6.dim(` Remaining: ${statsAfter.entryCount} entries (${formatSize2(statsAfter.totalSize)})`));
1066
+ console.log(chalk8.dim(` Remaining: ${statsAfter.entryCount} entries (${formatSize2(statsAfter.totalSize)})`));
901
1067
  }
902
1068
  } catch (error) {
903
- console.error(chalk6.red("Error:"), error.message);
1069
+ console.error(chalk8.red("Error:"), error.message);
904
1070
  process.exit(1);
905
1071
  }
906
1072
  });
@@ -929,93 +1095,333 @@ function cacheStatsCommand() {
929
1095
  console.log(JSON.stringify(stats, null, 2));
930
1096
  return;
931
1097
  }
932
- console.log(chalk6.bold("Cache Statistics\n"));
933
- console.log(chalk6.bold("Overview"));
934
- console.log(` Total entries: ${chalk6.cyan(stats.entryCount.toString())}`);
935
- console.log(` Total size: ${chalk6.cyan(formatSize3(stats.totalSize))}`);
936
- console.log(` Fresh entries: ${chalk6.green(stats.freshCount.toString())}`);
937
- console.log(` Stale entries: ${stats.staleCount > 0 ? chalk6.yellow(stats.staleCount.toString()) : chalk6.dim("0")}`);
938
- console.log(` Expired entries: ${stats.expiredCount > 0 ? chalk6.red(stats.expiredCount.toString()) : chalk6.dim("0")}`);
1098
+ console.log(chalk8.bold("Cache Statistics\n"));
1099
+ console.log(chalk8.bold("Overview"));
1100
+ console.log(` Total entries: ${chalk8.cyan(stats.entryCount.toString())}`);
1101
+ console.log(` Total size: ${chalk8.cyan(formatSize3(stats.totalSize))}`);
1102
+ console.log(` Fresh entries: ${chalk8.green(stats.freshCount.toString())}`);
1103
+ console.log(` Stale entries: ${stats.staleCount > 0 ? chalk8.yellow(stats.staleCount.toString()) : chalk8.dim("0")}`);
1104
+ console.log(` Expired entries: ${stats.expiredCount > 0 ? chalk8.red(stats.expiredCount.toString()) : chalk8.dim("0")}`);
939
1105
  console.log("");
940
1106
  const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
941
- const healthColor = healthPercent > 80 ? chalk6.green : healthPercent > 50 ? chalk6.yellow : chalk6.red;
1107
+ const healthColor = healthPercent > 80 ? chalk8.green : healthPercent > 50 ? chalk8.yellow : chalk8.red;
942
1108
  console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
943
1109
  if (stats.expiredCount > 0) {
944
- console.log(chalk6.dim('\nRun "fractary codex cache clear --pattern <pattern>" to clean up entries.'));
1110
+ console.log(chalk8.dim('\nRun "fractary codex cache clear --pattern <pattern>" to clean up entries.'));
945
1111
  }
946
1112
  if (stats.entryCount === 0) {
947
- console.log(chalk6.dim("\nNo cached entries. Fetch some documents to populate the cache."));
1113
+ console.log(chalk8.dim("\nNo cached entries. Fetch some documents to populate the cache."));
948
1114
  }
949
1115
  } catch (error) {
950
- console.error(chalk6.red("Error:"), error.message);
1116
+ console.error(chalk8.red("Error:"), error.message);
951
1117
  process.exit(1);
952
1118
  }
953
1119
  });
954
1120
  return cmd;
955
1121
  }
956
1122
 
957
- // src/commands/cache/index.ts
958
- function cacheCommand() {
959
- const cmd = new Command("cache");
960
- cmd.description("Manage the codex document cache");
961
- cmd.addCommand(cacheListCommand());
962
- cmd.addCommand(cacheClearCommand());
963
- cmd.addCommand(cacheStatsCommand());
964
- return cmd;
965
- }
966
-
967
- // src/commands/sync/index.ts
968
- init_esm_shims();
969
-
970
- // src/commands/sync/project.ts
1123
+ // src/commands/cache/health.ts
971
1124
  init_esm_shims();
972
1125
  init_migrate_config();
973
- function getEnvironmentBranch(config, env) {
974
- const envMap = config.sync?.environments || {
975
- dev: "develop",
976
- test: "test",
977
- staging: "staging",
978
- prod: "main"
979
- };
980
- return envMap[env] || env;
981
- }
982
- function formatBytes(bytes) {
983
- if (bytes < 1024) return `${bytes} B`;
984
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
985
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
986
- }
987
- function formatDuration(ms) {
988
- if (ms < 1e3) return `${ms}ms`;
989
- return `${(ms / 1e3).toFixed(1)}s`;
1126
+ async function fileExists3(filePath) {
1127
+ try {
1128
+ await fs.access(filePath);
1129
+ return true;
1130
+ } catch {
1131
+ return false;
1132
+ }
990
1133
  }
991
- function syncProjectCommand() {
992
- const cmd = new Command("project");
993
- 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) => {
994
- try {
995
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
996
- let config;
997
- try {
998
- config = await readYamlConfig(configPath);
999
- } catch (error) {
1000
- console.error(chalk6.red("Error:"), "Codex not initialized.");
1001
- console.log(chalk6.dim('Run "fractary codex init" first.'));
1002
- process.exit(1);
1003
- }
1004
- const { createSyncManager, createLocalStorage, detectCurrentProject } = await import('@fractary/codex');
1005
- let projectName = name;
1134
+ async function checkConfiguration() {
1135
+ const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
1136
+ const legacyConfigPath = path4.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1137
+ try {
1138
+ if (!await fileExists3(configPath)) {
1139
+ if (await fileExists3(legacyConfigPath)) {
1140
+ return {
1141
+ name: "Configuration",
1142
+ status: "warn",
1143
+ message: "Legacy JSON configuration detected",
1144
+ details: 'Run "fractary codex migrate" to upgrade to YAML format'
1145
+ };
1146
+ }
1147
+ return {
1148
+ name: "Configuration",
1149
+ status: "fail",
1150
+ message: "No configuration found",
1151
+ details: 'Run "fractary codex init" to create configuration'
1152
+ };
1153
+ }
1154
+ const config = await readYamlConfig(configPath);
1155
+ if (!config.organization) {
1156
+ return {
1157
+ name: "Configuration",
1158
+ status: "warn",
1159
+ message: "No organization configured",
1160
+ details: "Organization slug is required"
1161
+ };
1162
+ }
1163
+ const providerCount = config.storage?.length || 0;
1164
+ if (providerCount === 0) {
1165
+ return {
1166
+ name: "Configuration",
1167
+ status: "warn",
1168
+ message: "No storage providers configured",
1169
+ details: "At least one storage provider is recommended"
1170
+ };
1171
+ }
1172
+ return {
1173
+ name: "Configuration",
1174
+ status: "pass",
1175
+ message: "Valid YAML configuration",
1176
+ details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
1177
+ };
1178
+ } catch (error) {
1179
+ return {
1180
+ name: "Configuration",
1181
+ status: "fail",
1182
+ message: "Invalid configuration",
1183
+ details: error.message
1184
+ };
1185
+ }
1186
+ }
1187
+ async function checkSDKClient() {
1188
+ try {
1189
+ const client = await getClient();
1190
+ const organization = client.getOrganization();
1191
+ return {
1192
+ name: "SDK Client",
1193
+ status: "pass",
1194
+ message: "CodexClient initialized successfully",
1195
+ details: `Organization: ${organization}`
1196
+ };
1197
+ } catch (error) {
1198
+ return {
1199
+ name: "SDK Client",
1200
+ status: "fail",
1201
+ message: "Failed to initialize CodexClient",
1202
+ details: error.message
1203
+ };
1204
+ }
1205
+ }
1206
+ async function checkCache() {
1207
+ try {
1208
+ const client = await getClient();
1209
+ const stats = await client.getCacheStats();
1210
+ if (stats.entryCount === 0) {
1211
+ return {
1212
+ name: "Cache",
1213
+ status: "warn",
1214
+ message: "Cache is empty",
1215
+ details: "Fetch some documents to populate cache"
1216
+ };
1217
+ }
1218
+ const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1219
+ if (healthPercent < 50) {
1220
+ return {
1221
+ name: "Cache",
1222
+ status: "warn",
1223
+ message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1224
+ details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
1225
+ };
1226
+ }
1227
+ return {
1228
+ name: "Cache",
1229
+ status: "pass",
1230
+ message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1231
+ details: `${formatSize4(stats.totalSize)} total`
1232
+ };
1233
+ } catch (error) {
1234
+ return {
1235
+ name: "Cache",
1236
+ status: "fail",
1237
+ message: "Cache check failed",
1238
+ details: error.message
1239
+ };
1240
+ }
1241
+ }
1242
+ async function checkStorage() {
1243
+ const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
1244
+ try {
1245
+ const config = await readYamlConfig(configPath);
1246
+ const providers = config.storage || [];
1247
+ if (providers.length === 0) {
1248
+ return {
1249
+ name: "Storage",
1250
+ status: "warn",
1251
+ message: "No storage providers configured",
1252
+ details: "Configure at least one provider in .fractary/codex.yaml"
1253
+ };
1254
+ }
1255
+ const providerTypes = providers.map((p) => p.type).join(", ");
1256
+ const hasGitHub = providers.some((p) => p.type === "github");
1257
+ if (hasGitHub && !process.env.GITHUB_TOKEN) {
1258
+ return {
1259
+ name: "Storage",
1260
+ status: "warn",
1261
+ message: `${providers.length} provider(s): ${providerTypes}`,
1262
+ details: "GITHUB_TOKEN not set (required for GitHub provider)"
1263
+ };
1264
+ }
1265
+ return {
1266
+ name: "Storage",
1267
+ status: "pass",
1268
+ message: `${providers.length} provider(s): ${providerTypes}`,
1269
+ details: "All configured providers available"
1270
+ };
1271
+ } catch (error) {
1272
+ return {
1273
+ name: "Storage",
1274
+ status: "fail",
1275
+ message: "Storage check failed",
1276
+ details: error.message
1277
+ };
1278
+ }
1279
+ }
1280
+ async function checkTypes() {
1281
+ try {
1282
+ const client = await getClient();
1283
+ const registry = client.getTypeRegistry();
1284
+ const allTypes = registry.list();
1285
+ const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
1286
+ const customCount = allTypes.length - builtinCount;
1287
+ return {
1288
+ name: "Type Registry",
1289
+ status: "pass",
1290
+ message: `${allTypes.length} types registered`,
1291
+ details: `${builtinCount} built-in, ${customCount} custom`
1292
+ };
1293
+ } catch (error) {
1294
+ return {
1295
+ name: "Type Registry",
1296
+ status: "fail",
1297
+ message: "Type registry check failed",
1298
+ details: error.message
1299
+ };
1300
+ }
1301
+ }
1302
+ function formatSize4(bytes) {
1303
+ if (bytes < 1024) return `${bytes} B`;
1304
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1305
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1306
+ }
1307
+ function healthCommand() {
1308
+ const cmd = new Command("health");
1309
+ cmd.description("Run diagnostics on codex setup").option("--json", "Output as JSON").action(async (options) => {
1310
+ try {
1311
+ const checks = [];
1312
+ checks.push(await checkConfiguration());
1313
+ checks.push(await checkSDKClient());
1314
+ checks.push(await checkCache());
1315
+ checks.push(await checkStorage());
1316
+ checks.push(await checkTypes());
1317
+ const passed = checks.filter((c) => c.status === "pass").length;
1318
+ const warned = checks.filter((c) => c.status === "warn").length;
1319
+ const failed = checks.filter((c) => c.status === "fail").length;
1320
+ if (options.json) {
1321
+ console.log(JSON.stringify({
1322
+ summary: {
1323
+ total: checks.length,
1324
+ passed,
1325
+ warned,
1326
+ failed,
1327
+ healthy: failed === 0
1328
+ },
1329
+ checks
1330
+ }, null, 2));
1331
+ return;
1332
+ }
1333
+ console.log(chalk8.bold("Codex Health Check\n"));
1334
+ for (const check of checks) {
1335
+ const icon = check.status === "pass" ? chalk8.green("\u2713") : check.status === "warn" ? chalk8.yellow("\u26A0") : chalk8.red("\u2717");
1336
+ const statusColor = check.status === "pass" ? chalk8.green : check.status === "warn" ? chalk8.yellow : chalk8.red;
1337
+ console.log(`${icon} ${chalk8.bold(check.name)}`);
1338
+ console.log(` ${statusColor(check.message)}`);
1339
+ if (check.details) {
1340
+ console.log(` ${chalk8.dim(check.details)}`);
1341
+ }
1342
+ console.log("");
1343
+ }
1344
+ console.log(chalk8.dim("\u2500".repeat(60)));
1345
+ const overallStatus = failed > 0 ? chalk8.red("UNHEALTHY") : warned > 0 ? chalk8.yellow("DEGRADED") : chalk8.green("HEALTHY");
1346
+ console.log(`Status: ${overallStatus}`);
1347
+ console.log(chalk8.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
1348
+ if (failed > 0 || warned > 0) {
1349
+ console.log("");
1350
+ console.log(chalk8.dim("Run checks individually for more details:"));
1351
+ console.log(chalk8.dim(" fractary codex cache stats"));
1352
+ console.log(chalk8.dim(" fractary codex types list"));
1353
+ }
1354
+ if (failed > 0) {
1355
+ process.exit(1);
1356
+ }
1357
+ } catch (error) {
1358
+ console.error(chalk8.red("Error:"), error.message);
1359
+ process.exit(1);
1360
+ }
1361
+ });
1362
+ return cmd;
1363
+ }
1364
+
1365
+ // src/commands/cache/index.ts
1366
+ function cacheCommand() {
1367
+ const cmd = new Command("cache");
1368
+ cmd.description("Manage the codex document cache");
1369
+ cmd.addCommand(cacheListCommand());
1370
+ cmd.addCommand(cacheClearCommand());
1371
+ cmd.addCommand(cacheStatsCommand());
1372
+ cmd.addCommand(healthCommand());
1373
+ return cmd;
1374
+ }
1375
+
1376
+ // src/commands/sync.ts
1377
+ init_esm_shims();
1378
+ init_migrate_config();
1379
+ function getEnvironmentBranch(config, env) {
1380
+ const envMap = config.sync?.environments || {
1381
+ dev: "develop",
1382
+ test: "test",
1383
+ staging: "staging",
1384
+ prod: "main"
1385
+ };
1386
+ return envMap[env] || env;
1387
+ }
1388
+ function formatBytes(bytes) {
1389
+ if (bytes < 1024) return `${bytes} B`;
1390
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1391
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1392
+ }
1393
+ function formatDuration(ms) {
1394
+ if (ms < 1e3) return `${ms}ms`;
1395
+ return `${(ms / 1e3).toFixed(1)}s`;
1396
+ }
1397
+ function syncCommand() {
1398
+ const cmd = new Command("sync");
1399
+ 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) => {
1400
+ try {
1401
+ const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
1402
+ let config;
1403
+ try {
1404
+ config = await readYamlConfig(configPath);
1405
+ } catch (error) {
1406
+ console.error(chalk8.red("Error:"), "Codex not initialized.");
1407
+ console.log(chalk8.dim('Run "fractary codex init" first.'));
1408
+ process.exit(1);
1409
+ }
1410
+ const { createSyncManager, createLocalStorage, detectCurrentProject } = await import('@fractary/codex');
1411
+ let projectName = name;
1006
1412
  if (!projectName) {
1007
1413
  const detected = detectCurrentProject();
1008
- projectName = detected.project || null;
1414
+ projectName = detected.project || void 0;
1009
1415
  }
1010
1416
  if (!projectName) {
1011
- console.error(chalk6.red("Error:"), "Could not determine project name.");
1012
- console.log(chalk6.dim("Provide project name as argument or run from a git repository."));
1417
+ console.error(chalk8.red("Error:"), "Could not determine project name.");
1418
+ console.log(chalk8.dim("Provide project name as argument or run from a git repository."));
1013
1419
  process.exit(1);
1014
1420
  }
1015
1421
  const validDirections = ["to-codex", "from-codex", "bidirectional"];
1016
1422
  if (!validDirections.includes(options.direction)) {
1017
- console.error(chalk6.red("Error:"), `Invalid direction: ${options.direction}`);
1018
- console.log(chalk6.dim("Valid options: to-codex, from-codex, bidirectional"));
1423
+ console.error(chalk8.red("Error:"), `Invalid direction: ${options.direction}`);
1424
+ console.log(chalk8.dim("Valid options: to-codex, from-codex, bidirectional"));
1019
1425
  process.exit(1);
1020
1426
  }
1021
1427
  const direction = options.direction;
@@ -1026,9 +1432,9 @@ function syncProjectCommand() {
1026
1432
  const syncManager = createSyncManager({
1027
1433
  localStorage,
1028
1434
  config: config.sync,
1029
- manifestPath: path3.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1435
+ manifestPath: path4.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1030
1436
  });
1031
- const defaultPatterns = config.sync?.include || [
1437
+ const defaultPatterns = [
1032
1438
  "docs/**/*.md",
1033
1439
  "specs/**/*.md",
1034
1440
  ".fractary/standards/**",
@@ -1063,13 +1469,36 @@ function syncProjectCommand() {
1063
1469
  include: includePatterns,
1064
1470
  exclude: excludePatterns
1065
1471
  };
1066
- const plan = await syncManager.createPlan(
1067
- config.organization,
1068
- projectName,
1069
- sourceDir,
1070
- targetFiles,
1071
- syncOptions
1072
- );
1472
+ let plan;
1473
+ let routingScan;
1474
+ if (direction === "from-codex") {
1475
+ const codexRepoPath = config.cacheDir || path4.join(process.cwd(), ".fractary", "codex-cache");
1476
+ if (options.json) {
1477
+ console.log(JSON.stringify({
1478
+ info: "Routing-aware sync: scanning entire codex repository for files targeting this project",
1479
+ codexPath: codexRepoPath
1480
+ }, null, 2));
1481
+ } else {
1482
+ console.log(chalk8.blue("\u2139 Using routing-aware sync"));
1483
+ console.log(chalk8.dim(" Scanning entire codex for files routing to this project...\n"));
1484
+ }
1485
+ const planWithRouting = await syncManager.createRoutingAwarePlan(
1486
+ config.organization,
1487
+ projectName,
1488
+ codexRepoPath,
1489
+ syncOptions
1490
+ );
1491
+ plan = planWithRouting;
1492
+ routingScan = planWithRouting.routingScan;
1493
+ } else {
1494
+ plan = await syncManager.createPlan(
1495
+ config.organization,
1496
+ projectName,
1497
+ sourceDir,
1498
+ targetFiles,
1499
+ syncOptions
1500
+ );
1501
+ }
1073
1502
  if (plan.totalFiles === 0) {
1074
1503
  if (options.json) {
1075
1504
  console.log(JSON.stringify({
@@ -1079,7 +1508,7 @@ function syncProjectCommand() {
1079
1508
  synced: 0
1080
1509
  }, null, 2));
1081
1510
  } else {
1082
- console.log(chalk6.yellow("No files to sync."));
1511
+ console.log(chalk8.yellow("No files to sync."));
1083
1512
  }
1084
1513
  return;
1085
1514
  }
@@ -1122,364 +1551,104 @@ function syncProjectCommand() {
1122
1551
  }, null, 2));
1123
1552
  return;
1124
1553
  }
1125
- console.log(chalk6.bold("Sync Plan\n"));
1126
- console.log(` Project: ${chalk6.cyan(projectName)}`);
1127
- console.log(` Organization: ${chalk6.cyan(config.organization)}`);
1128
- console.log(` Environment: ${chalk6.cyan(options.env)} (${targetBranch})`);
1129
- console.log(` Direction: ${chalk6.cyan(direction)}`);
1130
- console.log(` Files: ${chalk6.cyan(plan.totalFiles.toString())}`);
1131
- console.log(` Total size: ${chalk6.cyan(formatBytes(plan.totalBytes))}`);
1554
+ console.log(chalk8.bold("Sync Plan\n"));
1555
+ console.log(` Project: ${chalk8.cyan(projectName)}`);
1556
+ console.log(` Organization: ${chalk8.cyan(config.organization)}`);
1557
+ console.log(` Environment: ${chalk8.cyan(options.env)} (${targetBranch})`);
1558
+ console.log(` Direction: ${chalk8.cyan(direction)}`);
1559
+ console.log(` Files: ${chalk8.cyan(plan.totalFiles.toString())}`);
1560
+ console.log(` Total size: ${chalk8.cyan(formatBytes(plan.totalBytes))}`);
1132
1561
  if (plan.estimatedTime) {
1133
- console.log(` Est. time: ${chalk6.dim(formatDuration(plan.estimatedTime))}`);
1562
+ console.log(` Est. time: ${chalk8.dim(formatDuration(plan.estimatedTime))}`);
1563
+ }
1564
+ if (routingScan) {
1565
+ console.log("");
1566
+ console.log(chalk8.bold("Routing Statistics\n"));
1567
+ console.log(` Scanned: ${chalk8.cyan(routingScan.stats.totalScanned.toString())} files`);
1568
+ console.log(` Matched: ${chalk8.cyan(routingScan.stats.totalMatched.toString())} files`);
1569
+ console.log(` Source projects: ${chalk8.cyan(routingScan.stats.sourceProjects.length.toString())}`);
1570
+ if (routingScan.stats.sourceProjects.length > 0) {
1571
+ console.log(chalk8.dim(` ${routingScan.stats.sourceProjects.slice(0, 5).join(", ")}`));
1572
+ if (routingScan.stats.sourceProjects.length > 5) {
1573
+ console.log(chalk8.dim(` ... and ${routingScan.stats.sourceProjects.length - 5} more`));
1574
+ }
1575
+ }
1576
+ console.log(` Scan time: ${chalk8.dim(formatDuration(routingScan.stats.durationMs))}`);
1134
1577
  }
1135
1578
  console.log("");
1136
1579
  if (plan.conflicts.length > 0) {
1137
- console.log(chalk6.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
1580
+ console.log(chalk8.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
1138
1581
  for (const conflict of plan.conflicts.slice(0, 5)) {
1139
- console.log(chalk6.yellow(` \u2022 ${conflict.path}`));
1582
+ console.log(chalk8.yellow(` \u2022 ${conflict.path}`));
1140
1583
  }
1141
1584
  if (plan.conflicts.length > 5) {
1142
- console.log(chalk6.dim(` ... and ${plan.conflicts.length - 5} more`));
1585
+ console.log(chalk8.dim(` ... and ${plan.conflicts.length - 5} more`));
1143
1586
  }
1144
1587
  console.log("");
1145
1588
  }
1146
1589
  if (plan.skipped.length > 0) {
1147
- console.log(chalk6.dim(`${plan.skipped.length} files skipped (no changes)`));
1590
+ console.log(chalk8.dim(`${plan.skipped.length} files skipped (no changes)`));
1148
1591
  console.log("");
1149
1592
  }
1150
1593
  if (options.dryRun) {
1151
- console.log(chalk6.blue("Dry run - would sync:\n"));
1594
+ console.log(chalk8.blue("Dry run - would sync:\n"));
1152
1595
  const filesToShow = plan.files.slice(0, 10);
1153
1596
  for (const file of filesToShow) {
1154
1597
  const arrow = direction === "to-codex" ? "\u2192" : direction === "from-codex" ? "\u2190" : "\u2194";
1155
- const opColor = file.operation === "create" ? chalk6.green : file.operation === "update" ? chalk6.yellow : chalk6.dim;
1598
+ const opColor = file.operation === "create" ? chalk8.green : file.operation === "update" ? chalk8.yellow : chalk8.dim;
1156
1599
  console.log(
1157
- chalk6.dim(` ${arrow}`),
1600
+ chalk8.dim(` ${arrow}`),
1158
1601
  opColor(file.operation.padEnd(7)),
1159
1602
  file.path,
1160
- chalk6.dim(`(${formatBytes(file.size || 0)})`)
1603
+ chalk8.dim(`(${formatBytes(file.size || 0)})`)
1161
1604
  );
1162
1605
  }
1163
1606
  if (plan.files.length > 10) {
1164
- console.log(chalk6.dim(` ... and ${plan.files.length - 10} more files`));
1607
+ console.log(chalk8.dim(` ... and ${plan.files.length - 10} more files`));
1165
1608
  }
1166
- console.log(chalk6.dim(`
1609
+ console.log(chalk8.dim(`
1167
1610
  Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
1168
- console.log(chalk6.dim("Run without --dry-run to execute sync."));
1611
+ console.log(chalk8.dim("Run without --dry-run to execute sync."));
1169
1612
  return;
1170
1613
  }
1171
- console.log(chalk6.blue("Syncing...\n"));
1614
+ console.log(chalk8.blue("Syncing...\n"));
1172
1615
  const startTime = Date.now();
1173
1616
  const result = await syncManager.executePlan(plan, syncOptions);
1174
1617
  const duration = Date.now() - startTime;
1175
1618
  console.log("");
1176
1619
  if (result.success) {
1177
- console.log(chalk6.green(`\u2713 Sync completed successfully`));
1178
- console.log(chalk6.dim(` Synced: ${result.synced} files`));
1620
+ console.log(chalk8.green(`\u2713 Sync completed successfully`));
1621
+ console.log(chalk8.dim(` Synced: ${result.synced} files`));
1179
1622
  if (result.skipped > 0) {
1180
- console.log(chalk6.dim(` Skipped: ${result.skipped} files`));
1623
+ console.log(chalk8.dim(` Skipped: ${result.skipped} files`));
1181
1624
  }
1182
- console.log(chalk6.dim(` Duration: ${formatDuration(duration)}`));
1625
+ console.log(chalk8.dim(` Duration: ${formatDuration(duration)}`));
1183
1626
  } else {
1184
- console.log(chalk6.yellow(`\u26A0 Sync completed with errors`));
1185
- console.log(chalk6.green(` Synced: ${result.synced} files`));
1186
- console.log(chalk6.red(` Failed: ${result.failed} files`));
1627
+ console.log(chalk8.yellow(`\u26A0 Sync completed with errors`));
1628
+ console.log(chalk8.green(` Synced: ${result.synced} files`));
1629
+ console.log(chalk8.red(` Failed: ${result.failed} files`));
1187
1630
  if (result.skipped > 0) {
1188
- console.log(chalk6.dim(` Skipped: ${result.skipped} files`));
1631
+ console.log(chalk8.dim(` Skipped: ${result.skipped} files`));
1189
1632
  }
1190
1633
  if (result.errors.length > 0) {
1191
1634
  console.log("");
1192
- console.log(chalk6.red("Errors:"));
1635
+ console.log(chalk8.red("Errors:"));
1193
1636
  for (const error of result.errors.slice(0, 5)) {
1194
- console.log(chalk6.red(` \u2022 ${error.path}: ${error.error}`));
1637
+ console.log(chalk8.red(` \u2022 ${error.path}: ${error.error}`));
1195
1638
  }
1196
1639
  if (result.errors.length > 5) {
1197
- console.log(chalk6.dim(` ... and ${result.errors.length - 5} more errors`));
1640
+ console.log(chalk8.dim(` ... and ${result.errors.length - 5} more errors`));
1198
1641
  }
1199
1642
  }
1200
1643
  }
1201
1644
  } catch (error) {
1202
- console.error(chalk6.red("Error:"), error.message);
1645
+ console.error(chalk8.red("Error:"), error.message);
1203
1646
  process.exit(1);
1204
1647
  }
1205
1648
  });
1206
1649
  return cmd;
1207
1650
  }
1208
1651
 
1209
- // src/commands/sync/org.ts
1210
- init_esm_shims();
1211
- init_migrate_config();
1212
- function getEnvironmentBranch2(config, env) {
1213
- const envMap = config.sync?.environments || {
1214
- dev: "develop",
1215
- test: "test",
1216
- staging: "staging",
1217
- prod: "main"
1218
- };
1219
- return envMap[env] || env;
1220
- }
1221
- async function discoverRepos(org) {
1222
- try {
1223
- const output = execSync(
1224
- `gh repo list ${org} --json name,url,defaultBranchRef --limit 100`,
1225
- { encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
1226
- );
1227
- const repos = JSON.parse(output);
1228
- return repos.map((repo) => ({
1229
- name: repo.name,
1230
- url: repo.url,
1231
- defaultBranch: repo.defaultBranchRef?.name || "main"
1232
- }));
1233
- } catch (error) {
1234
- throw new Error(`Failed to discover repos: ${error.message}. Ensure GitHub CLI is installed and authenticated.`);
1235
- }
1236
- }
1237
- function shouldExclude(repoName, excludePatterns) {
1238
- for (const pattern of excludePatterns) {
1239
- const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
1240
- if (regex.test(repoName)) {
1241
- return true;
1242
- }
1243
- }
1244
- return false;
1245
- }
1246
- async function syncRepository(repo, config, direction, syncOptions) {
1247
- const startTime = Date.now();
1248
- try {
1249
- const { createSyncManager, createLocalStorage } = await import('@fractary/codex');
1250
- const repoDir = path3.join(process.cwd(), "..", repo.name);
1251
- const localStorage = createLocalStorage({
1252
- baseDir: repoDir
1253
- });
1254
- const syncManager = createSyncManager({
1255
- localStorage,
1256
- config: config.sync,
1257
- manifestPath: path3.join(repoDir, ".fractary", ".codex-sync-manifest.json")
1258
- });
1259
- const includePatterns = config.sync?.include || [
1260
- "docs/**/*.md",
1261
- "specs/**/*.md",
1262
- ".fractary/standards/**",
1263
- ".fractary/templates/**"
1264
- ];
1265
- const allFiles = await syncManager.listLocalFiles(repoDir);
1266
- const targetFiles = allFiles.filter((file) => {
1267
- for (const pattern of includePatterns) {
1268
- const regex = new RegExp("^" + pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*") + "$");
1269
- if (regex.test(file.path)) {
1270
- return true;
1271
- }
1272
- }
1273
- return false;
1274
- });
1275
- const plan = await syncManager.createPlan(
1276
- config.organization,
1277
- repo.name,
1278
- repoDir,
1279
- targetFiles,
1280
- syncOptions
1281
- );
1282
- if (plan.totalFiles === 0) {
1283
- return {
1284
- repo: repo.name,
1285
- status: "skipped",
1286
- filesSynced: 0,
1287
- duration: Date.now() - startTime
1288
- };
1289
- }
1290
- const result = await syncManager.executePlan(plan, syncOptions);
1291
- return {
1292
- repo: repo.name,
1293
- status: result.success ? "success" : "error",
1294
- filesSynced: result.synced,
1295
- duration: Date.now() - startTime,
1296
- error: result.success ? void 0 : `${result.failed} files failed`
1297
- };
1298
- } catch (error) {
1299
- return {
1300
- repo: repo.name,
1301
- status: "error",
1302
- duration: Date.now() - startTime,
1303
- error: error.message
1304
- };
1305
- }
1306
- }
1307
- function syncOrgCommand() {
1308
- const cmd = new Command("org");
1309
- cmd.description("Sync all projects in organization with codex repository").option("--env <env>", "Target environment (dev/test/staging/prod)", "prod").option("--dry-run", "Show what would sync without executing").option("--exclude <pattern>", "Exclude repos matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--parallel <n>", "Number of parallel syncs", parseInt, 3).option("--direction <dir>", "Sync direction (to-codex/from-codex/bidirectional)", "bidirectional").option("--json", "Output as JSON").action(async (options) => {
1310
- try {
1311
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
1312
- let config;
1313
- try {
1314
- config = await readYamlConfig(configPath);
1315
- } catch (error) {
1316
- console.error(chalk6.red("Error:"), "Codex not initialized.");
1317
- console.log(chalk6.dim('Run "fractary codex init" first.'));
1318
- process.exit(1);
1319
- }
1320
- const validDirections = ["to-codex", "from-codex", "bidirectional"];
1321
- if (!validDirections.includes(options.direction)) {
1322
- console.error(chalk6.red("Error:"), `Invalid direction: ${options.direction}`);
1323
- console.log(chalk6.dim("Valid options: to-codex, from-codex, bidirectional"));
1324
- process.exit(1);
1325
- }
1326
- const direction = options.direction;
1327
- const org = config.organization;
1328
- const targetBranch = getEnvironmentBranch2(config, options.env);
1329
- const excludePatterns = [
1330
- ...config.sync?.exclude || [],
1331
- ...options.exclude
1332
- ];
1333
- if (!options.json) {
1334
- console.log(chalk6.bold("Organization Sync\n"));
1335
- console.log(` Organization: ${chalk6.cyan(org)}`);
1336
- console.log(` Environment: ${chalk6.cyan(options.env)} (${targetBranch})`);
1337
- console.log(` Direction: ${chalk6.cyan(direction)}`);
1338
- console.log(` Parallelism: ${chalk6.cyan(options.parallel.toString())}`);
1339
- if (excludePatterns.length > 0) {
1340
- console.log(` Excluding: ${chalk6.dim(excludePatterns.join(", "))}`);
1341
- }
1342
- console.log("");
1343
- console.log(chalk6.dim("Discovering repositories..."));
1344
- }
1345
- let repos;
1346
- try {
1347
- repos = await discoverRepos(org);
1348
- } catch (error) {
1349
- if (options.json) {
1350
- console.log(JSON.stringify({ error: error.message }));
1351
- } else {
1352
- console.error(chalk6.red("Error:"), error.message);
1353
- }
1354
- process.exit(1);
1355
- }
1356
- const eligibleRepos = repos.filter((repo) => !shouldExclude(repo.name, excludePatterns));
1357
- const excludedRepos = repos.filter((repo) => shouldExclude(repo.name, excludePatterns));
1358
- if (!options.json) {
1359
- console.log(chalk6.dim(`Found ${repos.length} repositories (${excludedRepos.length} excluded)
1360
- `));
1361
- }
1362
- if (options.dryRun) {
1363
- if (options.json) {
1364
- console.log(JSON.stringify({
1365
- organization: org,
1366
- environment: options.env,
1367
- branch: targetBranch,
1368
- direction,
1369
- dryRun: true,
1370
- repos: {
1371
- total: repos.length,
1372
- eligible: eligibleRepos.map((r) => r.name),
1373
- excluded: excludedRepos.map((r) => r.name)
1374
- }
1375
- }, null, 2));
1376
- } else {
1377
- console.log(chalk6.blue("Dry run - would sync:\n"));
1378
- for (const repo of eligibleRepos) {
1379
- console.log(chalk6.green(" \u2713"), repo.name);
1380
- }
1381
- if (excludedRepos.length > 0) {
1382
- console.log("");
1383
- console.log(chalk6.dim("Excluded:"));
1384
- for (const repo of excludedRepos) {
1385
- console.log(chalk6.dim(` - ${repo.name}`));
1386
- }
1387
- }
1388
- console.log(chalk6.dim(`
1389
- Total: ${eligibleRepos.length} repos would be synced`));
1390
- console.log(chalk6.dim("Run without --dry-run to execute sync."));
1391
- }
1392
- return;
1393
- }
1394
- if (!options.json) {
1395
- console.log(chalk6.blue("Syncing repositories...\n"));
1396
- }
1397
- const results = [];
1398
- const syncOptions = {
1399
- direction,
1400
- dryRun: false,
1401
- force: false
1402
- };
1403
- for (let i = 0; i < eligibleRepos.length; i += options.parallel) {
1404
- const batch = eligibleRepos.slice(i, i + options.parallel);
1405
- const batchResults = await Promise.all(
1406
- batch.map((repo) => syncRepository(repo, config, direction, syncOptions))
1407
- );
1408
- for (const result of batchResults) {
1409
- results.push(result);
1410
- if (!options.json) {
1411
- if (result.status === "success") {
1412
- console.log(
1413
- chalk6.green(" \u2713"),
1414
- result.repo,
1415
- chalk6.dim(`(${result.filesSynced} files in ${result.duration}ms)`)
1416
- );
1417
- } else if (result.status === "skipped") {
1418
- console.log(
1419
- chalk6.dim(" -"),
1420
- result.repo,
1421
- chalk6.dim("(no files to sync)")
1422
- );
1423
- } else if (result.status === "error") {
1424
- console.log(
1425
- chalk6.red(" \u2717"),
1426
- result.repo,
1427
- chalk6.red(`(${result.error})`)
1428
- );
1429
- }
1430
- }
1431
- }
1432
- }
1433
- const successful = results.filter((r) => r.status === "success");
1434
- const skipped = results.filter((r) => r.status === "skipped");
1435
- const failed = results.filter((r) => r.status === "error");
1436
- const totalFiles = successful.reduce((sum, r) => sum + (r.filesSynced || 0), 0);
1437
- if (options.json) {
1438
- console.log(JSON.stringify({
1439
- organization: org,
1440
- environment: options.env,
1441
- branch: targetBranch,
1442
- direction,
1443
- results: {
1444
- total: results.length,
1445
- successful: successful.length,
1446
- skipped: skipped.length,
1447
- failed: failed.length,
1448
- filesSynced: totalFiles,
1449
- details: results
1450
- }
1451
- }, null, 2));
1452
- } else {
1453
- console.log("");
1454
- console.log(chalk6.green(`\u2713 Synced ${successful.length}/${results.length} repos (${totalFiles} files)`));
1455
- if (skipped.length > 0) {
1456
- console.log(chalk6.dim(` Skipped ${skipped.length} repos (no changes)`));
1457
- }
1458
- if (failed.length > 0) {
1459
- console.log(chalk6.red(`\u2717 ${failed.length} repos failed`));
1460
- }
1461
- }
1462
- } catch (error) {
1463
- if (options.json) {
1464
- console.log(JSON.stringify({ error: error.message }));
1465
- } else {
1466
- console.error(chalk6.red("Error:"), error.message);
1467
- }
1468
- process.exit(1);
1469
- }
1470
- });
1471
- return cmd;
1472
- }
1473
-
1474
- // src/commands/sync/index.ts
1475
- function syncCommand() {
1476
- const cmd = new Command("sync");
1477
- cmd.description("Synchronize with codex repository");
1478
- cmd.addCommand(syncProjectCommand());
1479
- cmd.addCommand(syncOrgCommand());
1480
- return cmd;
1481
- }
1482
-
1483
1652
  // src/commands/types/index.ts
1484
1653
  init_esm_shims();
1485
1654
 
@@ -1525,35 +1694,35 @@ function typesListCommand() {
1525
1694
  return;
1526
1695
  }
1527
1696
  if (types.length === 0) {
1528
- console.log(chalk6.yellow("No types found."));
1697
+ console.log(chalk8.yellow("No types found."));
1529
1698
  return;
1530
1699
  }
1531
- console.log(chalk6.bold("Artifact Types\n"));
1700
+ console.log(chalk8.bold("Artifact Types\n"));
1532
1701
  const builtinTypes = types.filter((t) => registry.isBuiltIn(t.name));
1533
1702
  const customTypes = types.filter((t) => !registry.isBuiltIn(t.name));
1534
1703
  if (builtinTypes.length > 0 && !options.customOnly) {
1535
- console.log(chalk6.bold("Built-in Types"));
1536
- console.log(chalk6.dim("\u2500".repeat(70)));
1704
+ console.log(chalk8.bold("Built-in Types"));
1705
+ console.log(chalk8.dim("\u2500".repeat(70)));
1537
1706
  for (const type of builtinTypes) {
1538
1707
  const patternStr = type.patterns[0] || "";
1539
- console.log(` ${chalk6.cyan(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk6.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
1540
- console.log(` ${chalk6.dim(" ".repeat(12) + type.description)}`);
1708
+ console.log(` ${chalk8.cyan(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk8.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
1709
+ console.log(` ${chalk8.dim(" ".repeat(12) + type.description)}`);
1541
1710
  }
1542
1711
  console.log("");
1543
1712
  }
1544
1713
  if (customTypes.length > 0 && !options.builtinOnly) {
1545
- console.log(chalk6.bold("Custom Types"));
1546
- console.log(chalk6.dim("\u2500".repeat(70)));
1714
+ console.log(chalk8.bold("Custom Types"));
1715
+ console.log(chalk8.dim("\u2500".repeat(70)));
1547
1716
  for (const type of customTypes) {
1548
1717
  const patternStr = type.patterns[0] || "";
1549
- console.log(` ${chalk6.green(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk6.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
1550
- console.log(` ${chalk6.dim(" ".repeat(12) + type.description)}`);
1718
+ console.log(` ${chalk8.green(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk8.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
1719
+ console.log(` ${chalk8.dim(" ".repeat(12) + type.description)}`);
1551
1720
  }
1552
1721
  console.log("");
1553
1722
  }
1554
- console.log(chalk6.dim(`Total: ${types.length} types (${builtinTypes.length} built-in, ${customTypes.length} custom)`));
1723
+ console.log(chalk8.dim(`Total: ${types.length} types (${builtinTypes.length} built-in, ${customTypes.length} custom)`));
1555
1724
  } catch (error) {
1556
- console.error(chalk6.red("Error:"), error.message);
1725
+ console.error(chalk8.red("Error:"), error.message);
1557
1726
  process.exit(1);
1558
1727
  }
1559
1728
  });
@@ -1576,8 +1745,8 @@ function typesShowCommand() {
1576
1745
  const registry = client.getTypeRegistry();
1577
1746
  const type = registry.get(name);
1578
1747
  if (!type) {
1579
- console.error(chalk6.red("Error:"), `Type "${name}" not found.`);
1580
- console.log(chalk6.dim('Run "fractary codex types list" to see available types.'));
1748
+ console.error(chalk8.red("Error:"), `Type "${name}" not found.`);
1749
+ console.log(chalk8.dim('Run "fractary codex types list" to see available types.'));
1581
1750
  process.exit(1);
1582
1751
  }
1583
1752
  const isBuiltin = registry.isBuiltIn(name);
@@ -1596,39 +1765,39 @@ function typesShowCommand() {
1596
1765
  }, null, 2));
1597
1766
  return;
1598
1767
  }
1599
- const nameColor = isBuiltin ? chalk6.cyan : chalk6.green;
1600
- console.log(chalk6.bold(`Type: ${nameColor(name)}
1768
+ const nameColor = isBuiltin ? chalk8.cyan : chalk8.green;
1769
+ console.log(chalk8.bold(`Type: ${nameColor(name)}
1601
1770
  `));
1602
- console.log(` ${chalk6.dim("Source:")} ${isBuiltin ? "Built-in" : "Custom"}`);
1603
- console.log(` ${chalk6.dim("Description:")} ${type.description}`);
1604
- console.log(` ${chalk6.dim("TTL:")} ${formatTtl2(type.defaultTtl)} (${type.defaultTtl} seconds)`);
1771
+ console.log(` ${chalk8.dim("Source:")} ${isBuiltin ? "Built-in" : "Custom"}`);
1772
+ console.log(` ${chalk8.dim("Description:")} ${type.description}`);
1773
+ console.log(` ${chalk8.dim("TTL:")} ${formatTtl2(type.defaultTtl)} (${type.defaultTtl} seconds)`);
1605
1774
  console.log("");
1606
- console.log(chalk6.bold("Patterns"));
1775
+ console.log(chalk8.bold("Patterns"));
1607
1776
  for (const pattern of type.patterns) {
1608
- console.log(` ${chalk6.dim("\u2022")} ${pattern}`);
1777
+ console.log(` ${chalk8.dim("\u2022")} ${pattern}`);
1609
1778
  }
1610
1779
  if (type.archiveAfterDays !== null) {
1611
1780
  console.log("");
1612
- console.log(chalk6.bold("Archive Settings"));
1613
- console.log(` ${chalk6.dim("After:")} ${type.archiveAfterDays} days`);
1614
- console.log(` ${chalk6.dim("Storage:")} ${type.archiveStorage || "not set"}`);
1781
+ console.log(chalk8.bold("Archive Settings"));
1782
+ console.log(` ${chalk8.dim("After:")} ${type.archiveAfterDays} days`);
1783
+ console.log(` ${chalk8.dim("Storage:")} ${type.archiveStorage || "not set"}`);
1615
1784
  }
1616
1785
  if (type.syncPatterns && type.syncPatterns.length > 0) {
1617
1786
  console.log("");
1618
- console.log(chalk6.bold("Sync Patterns"));
1787
+ console.log(chalk8.bold("Sync Patterns"));
1619
1788
  for (const pattern of type.syncPatterns) {
1620
- console.log(` ${chalk6.dim("\u2022")} ${pattern}`);
1789
+ console.log(` ${chalk8.dim("\u2022")} ${pattern}`);
1621
1790
  }
1622
1791
  }
1623
1792
  if (type.excludePatterns && type.excludePatterns.length > 0) {
1624
1793
  console.log("");
1625
- console.log(chalk6.bold("Exclude Patterns"));
1794
+ console.log(chalk8.bold("Exclude Patterns"));
1626
1795
  for (const pattern of type.excludePatterns) {
1627
- console.log(` ${chalk6.dim("\u2022")} ${pattern}`);
1796
+ console.log(` ${chalk8.dim("\u2022")} ${pattern}`);
1628
1797
  }
1629
1798
  }
1630
1799
  } catch (error) {
1631
- console.error(chalk6.red("Error:"), error.message);
1800
+ console.error(chalk8.red("Error:"), error.message);
1632
1801
  process.exit(1);
1633
1802
  }
1634
1803
  });
@@ -1672,33 +1841,33 @@ function typesAddCommand() {
1672
1841
  cmd.description("Add a custom artifact type").argument("<name>", "Type name (lowercase, alphanumeric with hyphens)").requiredOption("--pattern <glob>", "File pattern (glob syntax)").option("--ttl <duration>", 'Cache TTL (e.g., "24h", "7d")', "24h").option("--description <text>", "Type description").option("--json", "Output as JSON").action(async (name, options) => {
1673
1842
  try {
1674
1843
  if (!isValidTypeName(name)) {
1675
- console.error(chalk6.red("Error:"), "Invalid type name.");
1676
- console.log(chalk6.dim("Type name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens."));
1844
+ console.error(chalk8.red("Error:"), "Invalid type name.");
1845
+ console.log(chalk8.dim("Type name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens."));
1677
1846
  process.exit(1);
1678
1847
  }
1679
1848
  const client = await getClient();
1680
1849
  const registry = client.getTypeRegistry();
1681
1850
  if (registry.isBuiltIn(name)) {
1682
- console.error(chalk6.red("Error:"), `Cannot override built-in type "${name}".`);
1851
+ console.error(chalk8.red("Error:"), `Cannot override built-in type "${name}".`);
1683
1852
  const builtinNames = registry.list().filter((t) => registry.isBuiltIn(t.name)).map((t) => t.name);
1684
- console.log(chalk6.dim("Built-in types: " + builtinNames.join(", ")));
1853
+ console.log(chalk8.dim("Built-in types: " + builtinNames.join(", ")));
1685
1854
  process.exit(1);
1686
1855
  }
1687
1856
  if (registry.has(name)) {
1688
- console.error(chalk6.red("Error:"), `Custom type "${name}" already exists.`);
1689
- console.log(chalk6.dim('Use "fractary codex types remove" first to remove it.'));
1857
+ console.error(chalk8.red("Error:"), `Custom type "${name}" already exists.`);
1858
+ console.log(chalk8.dim('Use "fractary codex types remove" first to remove it.'));
1690
1859
  process.exit(1);
1691
1860
  }
1692
1861
  let ttlSeconds;
1693
1862
  try {
1694
1863
  ttlSeconds = parseTtl(options.ttl);
1695
1864
  } catch {
1696
- console.error(chalk6.red("Error:"), "Invalid TTL format.");
1697
- console.log(chalk6.dim("Expected format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days)"));
1698
- console.log(chalk6.dim("Examples: 30m, 24h, 7d"));
1865
+ console.error(chalk8.red("Error:"), "Invalid TTL format.");
1866
+ console.log(chalk8.dim("Expected format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days)"));
1867
+ console.log(chalk8.dim("Examples: 30m, 24h, 7d"));
1699
1868
  process.exit(1);
1700
1869
  }
1701
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
1870
+ const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
1702
1871
  const config = await readYamlConfig(configPath);
1703
1872
  if (!config.types) {
1704
1873
  config.types = { custom: {} };
@@ -1727,19 +1896,19 @@ function typesAddCommand() {
1727
1896
  }, null, 2));
1728
1897
  return;
1729
1898
  }
1730
- console.log(chalk6.green("\u2713"), `Added custom type "${chalk6.cyan(name)}"`);
1899
+ console.log(chalk8.green("\u2713"), `Added custom type "${chalk8.cyan(name)}"`);
1731
1900
  console.log("");
1732
- console.log(` ${chalk6.dim("Pattern:")} ${options.pattern}`);
1733
- console.log(` ${chalk6.dim("TTL:")} ${formatTtl3(ttlSeconds)} (${ttlSeconds} seconds)`);
1901
+ console.log(` ${chalk8.dim("Pattern:")} ${options.pattern}`);
1902
+ console.log(` ${chalk8.dim("TTL:")} ${formatTtl3(ttlSeconds)} (${ttlSeconds} seconds)`);
1734
1903
  if (options.description) {
1735
- console.log(` ${chalk6.dim("Description:")} ${options.description}`);
1904
+ console.log(` ${chalk8.dim("Description:")} ${options.description}`);
1736
1905
  }
1737
1906
  console.log("");
1738
- console.log(chalk6.dim("Note: Custom type will be available on next CLI invocation."));
1907
+ console.log(chalk8.dim("Note: Custom type will be available on next CLI invocation."));
1739
1908
  } catch (error) {
1740
- console.error(chalk6.red("Error:"), error.message);
1909
+ console.error(chalk8.red("Error:"), error.message);
1741
1910
  if (error.message.includes("Failed to load configuration")) {
1742
- console.log(chalk6.dim('\nRun "fractary codex init" to create a configuration.'));
1911
+ console.log(chalk8.dim('\nRun "fractary codex init" to create a configuration.'));
1743
1912
  }
1744
1913
  process.exit(1);
1745
1914
  }
@@ -1757,20 +1926,20 @@ function typesRemoveCommand() {
1757
1926
  const client = await getClient();
1758
1927
  const registry = client.getTypeRegistry();
1759
1928
  if (registry.isBuiltIn(name)) {
1760
- console.error(chalk6.red("Error:"), `Cannot remove built-in type "${name}".`);
1761
- console.log(chalk6.dim("Built-in types are permanent and cannot be removed."));
1929
+ console.error(chalk8.red("Error:"), `Cannot remove built-in type "${name}".`);
1930
+ console.log(chalk8.dim("Built-in types are permanent and cannot be removed."));
1762
1931
  process.exit(1);
1763
1932
  }
1764
1933
  if (!registry.has(name)) {
1765
- console.error(chalk6.red("Error:"), `Custom type "${name}" not found.`);
1766
- console.log(chalk6.dim('Run "fractary codex types list --custom-only" to see custom types.'));
1934
+ console.error(chalk8.red("Error:"), `Custom type "${name}" not found.`);
1935
+ console.log(chalk8.dim('Run "fractary codex types list --custom-only" to see custom types.'));
1767
1936
  process.exit(1);
1768
1937
  }
1769
1938
  const typeInfo = registry.get(name);
1770
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
1939
+ const configPath = path4.join(process.cwd(), ".fractary", "codex.yaml");
1771
1940
  const config = await readYamlConfig(configPath);
1772
1941
  if (!config.types?.custom?.[name]) {
1773
- console.error(chalk6.red("Error:"), `Custom type "${name}" not found in configuration.`);
1942
+ console.error(chalk8.red("Error:"), `Custom type "${name}" not found in configuration.`);
1774
1943
  process.exit(1);
1775
1944
  }
1776
1945
  delete config.types.custom[name];
@@ -1794,17 +1963,17 @@ function typesRemoveCommand() {
1794
1963
  }, null, 2));
1795
1964
  return;
1796
1965
  }
1797
- console.log(chalk6.green("\u2713"), `Removed custom type "${chalk6.cyan(name)}"`);
1966
+ console.log(chalk8.green("\u2713"), `Removed custom type "${chalk8.cyan(name)}"`);
1798
1967
  console.log("");
1799
- console.log(chalk6.dim("Removed configuration:"));
1800
- console.log(` ${chalk6.dim("Pattern:")} ${typeInfo.patterns.join(", ")}`);
1801
- console.log(` ${chalk6.dim("Description:")} ${typeInfo.description}`);
1968
+ console.log(chalk8.dim("Removed configuration:"));
1969
+ console.log(` ${chalk8.dim("Pattern:")} ${typeInfo.patterns.join(", ")}`);
1970
+ console.log(` ${chalk8.dim("Description:")} ${typeInfo.description}`);
1802
1971
  console.log("");
1803
- console.log(chalk6.dim("Note: Custom type will be removed on next CLI invocation."));
1972
+ console.log(chalk8.dim("Note: Custom type will be removed on next CLI invocation."));
1804
1973
  } catch (error) {
1805
- console.error(chalk6.red("Error:"), error.message);
1974
+ console.error(chalk8.red("Error:"), error.message);
1806
1975
  if (error.message.includes("Failed to load configuration")) {
1807
- console.log(chalk6.dim('\nRun "fractary codex init" to create a configuration.'));
1976
+ console.log(chalk8.dim('\nRun "fractary codex init" to create a configuration.'));
1808
1977
  }
1809
1978
  process.exit(1);
1810
1979
  }
@@ -1823,413 +1992,15 @@ function typesCommand() {
1823
1992
  return cmd;
1824
1993
  }
1825
1994
 
1826
- // src/commands/health.ts
1827
- init_esm_shims();
1828
- init_migrate_config();
1829
- async function fileExists2(filePath) {
1830
- try {
1831
- await fs.access(filePath);
1832
- return true;
1833
- } catch {
1834
- return false;
1835
- }
1836
- }
1837
- async function checkConfiguration() {
1838
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
1839
- const legacyConfigPath = path3.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1840
- try {
1841
- if (!await fileExists2(configPath)) {
1842
- if (await fileExists2(legacyConfigPath)) {
1843
- return {
1844
- name: "Configuration",
1845
- status: "warn",
1846
- message: "Legacy JSON configuration detected",
1847
- details: 'Run "fractary codex migrate" to upgrade to YAML format'
1848
- };
1849
- }
1850
- return {
1851
- name: "Configuration",
1852
- status: "fail",
1853
- message: "No configuration found",
1854
- details: 'Run "fractary codex init" to create configuration'
1855
- };
1856
- }
1857
- const config = await readYamlConfig(configPath);
1858
- if (!config.organization) {
1859
- return {
1860
- name: "Configuration",
1861
- status: "warn",
1862
- message: "No organization configured",
1863
- details: "Organization slug is required"
1864
- };
1865
- }
1866
- const providerCount = config.storage?.length || 0;
1867
- if (providerCount === 0) {
1868
- return {
1869
- name: "Configuration",
1870
- status: "warn",
1871
- message: "No storage providers configured",
1872
- details: "At least one storage provider is recommended"
1873
- };
1874
- }
1875
- return {
1876
- name: "Configuration",
1877
- status: "pass",
1878
- message: "Valid YAML configuration",
1879
- details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
1880
- };
1881
- } catch (error) {
1882
- return {
1883
- name: "Configuration",
1884
- status: "fail",
1885
- message: "Invalid configuration",
1886
- details: error.message
1887
- };
1888
- }
1889
- }
1890
- async function checkSDKClient() {
1891
- try {
1892
- const client = await getClient();
1893
- const organization = client.getOrganization();
1894
- return {
1895
- name: "SDK Client",
1896
- status: "pass",
1897
- message: "CodexClient initialized successfully",
1898
- details: `Organization: ${organization}`
1899
- };
1900
- } catch (error) {
1901
- return {
1902
- name: "SDK Client",
1903
- status: "fail",
1904
- message: "Failed to initialize CodexClient",
1905
- details: error.message
1906
- };
1907
- }
1908
- }
1909
- async function checkCache() {
1910
- try {
1911
- const client = await getClient();
1912
- const stats = await client.getCacheStats();
1913
- if (stats.entryCount === 0) {
1914
- return {
1915
- name: "Cache",
1916
- status: "warn",
1917
- message: "Cache is empty",
1918
- details: "Fetch some documents to populate cache"
1919
- };
1920
- }
1921
- const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1922
- if (healthPercent < 50) {
1923
- return {
1924
- name: "Cache",
1925
- status: "warn",
1926
- message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1927
- details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
1928
- };
1929
- }
1930
- return {
1931
- name: "Cache",
1932
- status: "pass",
1933
- message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1934
- details: `${formatSize4(stats.totalSize)} total`
1935
- };
1936
- } catch (error) {
1937
- return {
1938
- name: "Cache",
1939
- status: "fail",
1940
- message: "Cache check failed",
1941
- details: error.message
1942
- };
1943
- }
1944
- }
1945
- async function checkStorage() {
1946
- const configPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
1947
- try {
1948
- const config = await readYamlConfig(configPath);
1949
- const providers = config.storage || [];
1950
- if (providers.length === 0) {
1951
- return {
1952
- name: "Storage",
1953
- status: "warn",
1954
- message: "No storage providers configured",
1955
- details: "Configure at least one provider in .fractary/codex.yaml"
1956
- };
1957
- }
1958
- const providerTypes = providers.map((p) => p.type).join(", ");
1959
- const hasGitHub = providers.some((p) => p.type === "github");
1960
- if (hasGitHub && !process.env.GITHUB_TOKEN) {
1961
- return {
1962
- name: "Storage",
1963
- status: "warn",
1964
- message: `${providers.length} provider(s): ${providerTypes}`,
1965
- details: "GITHUB_TOKEN not set (required for GitHub provider)"
1966
- };
1967
- }
1968
- return {
1969
- name: "Storage",
1970
- status: "pass",
1971
- message: `${providers.length} provider(s): ${providerTypes}`,
1972
- details: "All configured providers available"
1973
- };
1974
- } catch (error) {
1975
- return {
1976
- name: "Storage",
1977
- status: "fail",
1978
- message: "Storage check failed",
1979
- details: error.message
1980
- };
1981
- }
1982
- }
1983
- async function checkTypes() {
1984
- try {
1985
- const client = await getClient();
1986
- const registry = client.getTypeRegistry();
1987
- const allTypes = registry.list();
1988
- const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
1989
- const customCount = allTypes.length - builtinCount;
1990
- return {
1991
- name: "Type Registry",
1992
- status: "pass",
1993
- message: `${allTypes.length} types registered`,
1994
- details: `${builtinCount} built-in, ${customCount} custom`
1995
- };
1996
- } catch (error) {
1997
- return {
1998
- name: "Type Registry",
1999
- status: "fail",
2000
- message: "Type registry check failed",
2001
- details: error.message
2002
- };
2003
- }
2004
- }
2005
- function formatSize4(bytes) {
2006
- if (bytes < 1024) return `${bytes} B`;
2007
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
2008
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2009
- }
2010
- function healthCommand() {
2011
- const cmd = new Command("health");
2012
- cmd.description("Run diagnostics on codex setup").option("--json", "Output as JSON").action(async (options) => {
2013
- try {
2014
- const checks = [];
2015
- checks.push(await checkConfiguration());
2016
- checks.push(await checkSDKClient());
2017
- checks.push(await checkCache());
2018
- checks.push(await checkStorage());
2019
- checks.push(await checkTypes());
2020
- const passed = checks.filter((c) => c.status === "pass").length;
2021
- const warned = checks.filter((c) => c.status === "warn").length;
2022
- const failed = checks.filter((c) => c.status === "fail").length;
2023
- if (options.json) {
2024
- console.log(JSON.stringify({
2025
- summary: {
2026
- total: checks.length,
2027
- passed,
2028
- warned,
2029
- failed,
2030
- healthy: failed === 0
2031
- },
2032
- checks
2033
- }, null, 2));
2034
- return;
2035
- }
2036
- console.log(chalk6.bold("Codex Health Check\n"));
2037
- for (const check of checks) {
2038
- const icon = check.status === "pass" ? chalk6.green("\u2713") : check.status === "warn" ? chalk6.yellow("\u26A0") : chalk6.red("\u2717");
2039
- const statusColor = check.status === "pass" ? chalk6.green : check.status === "warn" ? chalk6.yellow : chalk6.red;
2040
- console.log(`${icon} ${chalk6.bold(check.name)}`);
2041
- console.log(` ${statusColor(check.message)}`);
2042
- if (check.details) {
2043
- console.log(` ${chalk6.dim(check.details)}`);
2044
- }
2045
- console.log("");
2046
- }
2047
- console.log(chalk6.dim("\u2500".repeat(60)));
2048
- const overallStatus = failed > 0 ? chalk6.red("UNHEALTHY") : warned > 0 ? chalk6.yellow("DEGRADED") : chalk6.green("HEALTHY");
2049
- console.log(`Status: ${overallStatus}`);
2050
- console.log(chalk6.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
2051
- if (failed > 0 || warned > 0) {
2052
- console.log("");
2053
- console.log(chalk6.dim("Run checks individually for more details:"));
2054
- console.log(chalk6.dim(" fractary codex cache stats"));
2055
- console.log(chalk6.dim(" fractary codex types list"));
2056
- }
2057
- if (failed > 0) {
2058
- process.exit(1);
2059
- }
2060
- } catch (error) {
2061
- console.error(chalk6.red("Error:"), error.message);
2062
- process.exit(1);
2063
- }
2064
- });
2065
- return cmd;
2066
- }
2067
-
2068
- // src/commands/migrate.ts
2069
- init_esm_shims();
2070
- init_migrate_config();
2071
- async function fileExists3(filePath) {
2072
- try {
2073
- await fs.access(filePath);
2074
- return true;
2075
- } catch {
2076
- return false;
2077
- }
2078
- }
2079
- async function readFileContent(filePath) {
2080
- return fs.readFile(filePath, "utf-8");
2081
- }
2082
- function migrateCommand() {
2083
- const cmd = new Command("migrate");
2084
- 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) => {
2085
- try {
2086
- const legacyConfigPath = path3.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
2087
- const newConfigPath = path3.join(process.cwd(), ".fractary", "codex.yaml");
2088
- if (!await fileExists3(legacyConfigPath)) {
2089
- if (options.json) {
2090
- console.log(JSON.stringify({
2091
- status: "no_config",
2092
- message: "No legacy configuration file found",
2093
- path: legacyConfigPath
2094
- }));
2095
- } else {
2096
- console.log(chalk6.yellow("\u26A0 No legacy configuration file found."));
2097
- console.log(chalk6.dim(` Expected: ${legacyConfigPath}`));
2098
- console.log(chalk6.dim('\nRun "fractary codex init" to create a new v3.0 YAML configuration.'));
2099
- }
2100
- return;
2101
- }
2102
- if (await fileExists3(newConfigPath) && !options.dryRun) {
2103
- if (options.json) {
2104
- console.log(JSON.stringify({
2105
- status: "already_migrated",
2106
- message: "YAML configuration already exists",
2107
- path: newConfigPath
2108
- }));
2109
- } else {
2110
- console.log(chalk6.yellow("\u26A0 YAML configuration already exists."));
2111
- console.log(chalk6.dim(` Path: ${newConfigPath}`));
2112
- console.log(chalk6.dim('\nUse "fractary codex init --force" to recreate.'));
2113
- }
2114
- return;
2115
- }
2116
- const legacyContent = await readFileContent(legacyConfigPath);
2117
- let legacyConfig;
2118
- try {
2119
- legacyConfig = JSON.parse(legacyContent);
2120
- } catch {
2121
- console.error(chalk6.red("Error:"), "Invalid JSON in legacy config file.");
2122
- process.exit(1);
2123
- }
2124
- if (!options.json && !options.dryRun) {
2125
- console.log(chalk6.blue("Migrating Codex configuration to v3.0 YAML format...\n"));
2126
- }
2127
- const migrationResult = await migrateConfig(
2128
- legacyConfigPath,
2129
- {
2130
- createBackup: options.backup !== false,
2131
- backupSuffix: (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
2132
- }
2133
- );
2134
- if (!options.json) {
2135
- console.log(chalk6.bold("Legacy Configuration:"));
2136
- console.log(chalk6.dim(` Path: ${legacyConfigPath}`));
2137
- console.log(chalk6.dim(` Organization: ${legacyConfig.organization || legacyConfig.organizationSlug || "unknown"}`));
2138
- console.log("");
2139
- console.log(chalk6.bold("Migration Changes:"));
2140
- console.log(chalk6.green(" + Format: JSON \u2192 YAML"));
2141
- console.log(chalk6.green(" + Location: .fractary/plugins/codex/ \u2192 .fractary/"));
2142
- console.log(chalk6.green(" + File: config.json \u2192 codex.yaml"));
2143
- console.log(chalk6.green(" + Storage: Multi-provider configuration"));
2144
- console.log(chalk6.green(" + Cache: Modern cache management"));
2145
- console.log(chalk6.green(" + Types: Custom type registry"));
2146
- if (migrationResult.warnings.length > 0) {
2147
- console.log("");
2148
- console.log(chalk6.yellow("Warnings:"));
2149
- for (const warning of migrationResult.warnings) {
2150
- console.log(chalk6.yellow(" \u26A0"), chalk6.dim(warning));
2151
- }
2152
- }
2153
- console.log("");
2154
- if (options.dryRun) {
2155
- console.log(chalk6.blue("Dry run - no changes made."));
2156
- console.log(chalk6.dim("Run without --dry-run to execute migration."));
2157
- return;
2158
- }
2159
- }
2160
- if (options.json) {
2161
- const output = {
2162
- status: options.dryRun ? "migration_ready" : "migrated",
2163
- dryRun: options.dryRun || false,
2164
- legacyConfig: {
2165
- path: legacyConfigPath,
2166
- organization: legacyConfig.organization || legacyConfig.organizationSlug
2167
- },
2168
- newConfig: {
2169
- path: newConfigPath,
2170
- organization: migrationResult.yamlConfig.organization
2171
- },
2172
- warnings: migrationResult.warnings,
2173
- backupPath: migrationResult.backupPath
2174
- };
2175
- console.log(JSON.stringify(output, null, 2));
2176
- if (options.dryRun) {
2177
- return;
2178
- }
2179
- }
2180
- if (!options.dryRun) {
2181
- await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
2182
- const cacheDir = path3.join(process.cwd(), ".codex-cache");
2183
- await fs.mkdir(cacheDir, { recursive: true });
2184
- if (!options.json) {
2185
- console.log(chalk6.green("\u2713"), "YAML configuration created");
2186
- console.log(chalk6.green("\u2713"), "Cache directory initialized");
2187
- if (migrationResult.backupPath) {
2188
- console.log(chalk6.green("\u2713"), "Legacy config backed up");
2189
- }
2190
- console.log("");
2191
- console.log(chalk6.bold("New Configuration:"));
2192
- console.log(chalk6.dim(` Path: ${newConfigPath}`));
2193
- console.log(chalk6.dim(` Organization: ${migrationResult.yamlConfig.organization}`));
2194
- console.log(chalk6.dim(` Cache: ${migrationResult.yamlConfig.cacheDir || ".codex-cache"}`));
2195
- console.log(chalk6.dim(` Storage Providers: ${migrationResult.yamlConfig.storage?.length || 0}`));
2196
- console.log("");
2197
- console.log(chalk6.bold("Next Steps:"));
2198
- console.log(chalk6.dim(" 1. Review the new configuration: .fractary/codex.yaml"));
2199
- console.log(chalk6.dim(' 2. Set your GitHub token: export GITHUB_TOKEN="your_token"'));
2200
- console.log(chalk6.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
2201
- if (migrationResult.backupPath) {
2202
- console.log("");
2203
- console.log(chalk6.dim(`Backup saved: ${path3.basename(migrationResult.backupPath)}`));
2204
- }
2205
- }
2206
- }
2207
- } catch (error) {
2208
- if (options.json) {
2209
- console.log(JSON.stringify({
2210
- status: "error",
2211
- message: error.message
2212
- }));
2213
- } else {
2214
- console.error(chalk6.red("Error:"), error.message);
2215
- }
2216
- process.exit(1);
2217
- }
2218
- });
2219
- return cmd;
2220
- }
2221
-
2222
1995
  // src/cli.ts
2223
1996
  function createCLI() {
2224
1997
  const program = new Command("fractary-codex");
2225
- program.description("Centralized knowledge management and distribution for AI agents").version("0.2.0");
2226
- program.addCommand(initCommand());
2227
- program.addCommand(fetchCommand());
1998
+ program.description("Centralized knowledge management and distribution for AI agents").version("0.3.0");
1999
+ program.addCommand(documentCommand());
2000
+ program.addCommand(configCommand());
2228
2001
  program.addCommand(cacheCommand());
2229
2002
  program.addCommand(syncCommand());
2230
2003
  program.addCommand(typesCommand());
2231
- program.addCommand(healthCommand());
2232
- program.addCommand(migrateCommand());
2233
2004
  return program;
2234
2005
  }
2235
2006
  async function main() {