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