@fractary/codex-cli 0.10.16 → 0.10.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,25 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import * as path3 from 'path';
2
+ import * as path4 from 'path';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import * as fs4 from 'fs/promises';
6
- import * as yaml from 'js-yaml';
7
- import { readCodexConfig, CONFIG_SCHEMA_VERSION, expandEnvVarsInConfig, expandEnvVars, parseSize, parseDuration, ValidationError, PermissionDeniedError, ConfigurationError, CodexError } from '@fractary/codex';
5
+ import { createConfigManager, resolveOrganization, validateNameFormat, formatBytes, createHealthChecker, readCodexConfig, formatDuration, createCodexClient, ValidationError, PermissionDeniedError, ConfigurationError, CodexError, CodexClient } from '@fractary/codex';
8
6
  import * as os from 'os';
7
+ import * as fs2 from 'fs/promises';
9
8
  import { spawn } from 'child_process';
10
9
  import { Command } from 'commander';
11
- import chalk7 from 'chalk';
10
+ import chalk10 from 'chalk';
12
11
  import * as crypto from 'crypto';
12
+ import 'js-yaml';
13
13
  import { readFileSync } from 'fs';
14
14
 
15
15
  var __defProp = Object.defineProperty;
16
16
  var __getOwnPropNames = Object.getOwnPropertyNames;
17
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
- }) : x)(function(x) {
20
- if (typeof require !== "undefined") return require.apply(this, arguments);
21
- throw Error('Dynamic require of "' + x + '" is not supported');
22
- });
23
17
  var __esm = (fn, res) => function __init() {
24
18
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
25
19
  };
@@ -32,222 +26,6 @@ var init_esm_shims = __esm({
32
26
  }
33
27
  });
34
28
 
35
- // src/config/migrate-config.ts
36
- var migrate_config_exports = {};
37
- __export(migrate_config_exports, {
38
- getDefaultYamlConfig: () => getDefaultYamlConfig,
39
- isLegacyConfig: () => isLegacyConfig,
40
- migrateConfig: () => migrateConfig,
41
- readYamlConfig: () => readCodexConfig,
42
- writeYamlConfig: () => writeYamlConfig
43
- });
44
- async function isLegacyConfig(configPath) {
45
- try {
46
- const content = await fs4.readFile(configPath, "utf-8");
47
- const config = JSON.parse(content);
48
- return config.version === "3.0" || config.organizationSlug !== void 0 || config.directories !== void 0 || config.rules !== void 0;
49
- } catch {
50
- return false;
51
- }
52
- }
53
- async function migrateConfig(legacyConfigPath, options) {
54
- const warnings = [];
55
- try {
56
- const content = await fs4.readFile(legacyConfigPath, "utf-8");
57
- const legacy = JSON.parse(content);
58
- let backupPath;
59
- if (options?.createBackup !== false) {
60
- const suffix = options?.backupSuffix || (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
61
- backupPath = `${legacyConfigPath}.backup-${suffix}`;
62
- await fs4.writeFile(backupPath, content, "utf-8");
63
- }
64
- const yamlConfig = {
65
- organization: legacy.organization || legacy.organizationSlug || "default"
66
- };
67
- if (legacy.cache) {
68
- yamlConfig.cacheDir = legacy.cache.directory || ".fractary/codex/cache";
69
- }
70
- if (legacy.storage?.providers) {
71
- yamlConfig.storage = [];
72
- for (const [type, config] of Object.entries(legacy.storage.providers)) {
73
- if (type === "github") {
74
- const githubConfig = config;
75
- yamlConfig.storage.push({
76
- type: "github",
77
- token: githubConfig.token || "${GITHUB_TOKEN}",
78
- apiBaseUrl: githubConfig.baseUrl || "https://api.github.com",
79
- branch: githubConfig.branch || "main",
80
- priority: 50
81
- });
82
- } else if (type === "http") {
83
- const httpConfig = config;
84
- yamlConfig.storage.push({
85
- type: "http",
86
- baseUrl: httpConfig.baseUrl,
87
- headers: httpConfig.headers,
88
- timeout: httpConfig.timeout || 3e4,
89
- priority: 100
90
- });
91
- } else if (type === "local") {
92
- const localConfig = config;
93
- yamlConfig.storage.push({
94
- type: "local",
95
- basePath: localConfig.basePath || "./knowledge",
96
- followSymlinks: localConfig.followSymlinks || false,
97
- priority: 10
98
- });
99
- } else {
100
- warnings.push(`Unknown storage provider type: ${type}`);
101
- }
102
- }
103
- if (yamlConfig.storage.length === 0) {
104
- yamlConfig.storage.push({
105
- type: "github",
106
- token: "${GITHUB_TOKEN}",
107
- apiBaseUrl: "https://api.github.com",
108
- branch: "main",
109
- priority: 50
110
- });
111
- warnings.push("No storage providers found, added default GitHub provider");
112
- }
113
- }
114
- if (legacy.types?.custom && Array.isArray(legacy.types.custom)) {
115
- yamlConfig.types = {
116
- custom: {}
117
- };
118
- for (const customType of legacy.types.custom) {
119
- if (customType.name) {
120
- yamlConfig.types.custom[customType.name] = {
121
- description: customType.description,
122
- patterns: customType.patterns || [],
123
- defaultTtl: customType.defaultTtl,
124
- archiveAfterDays: customType.archiveAfterDays,
125
- archiveStorage: customType.archiveStorage
126
- };
127
- }
128
- }
129
- }
130
- if (legacy.sync) {
131
- yamlConfig.sync = {
132
- bidirectional: true,
133
- conflictResolution: "prompt",
134
- exclude: [
135
- "node_modules/**",
136
- ".git/**",
137
- "**/*.log",
138
- ".env"
139
- ]
140
- };
141
- if (legacy.sync.environments) {
142
- warnings.push("Sync environments are not directly supported in v3.0 - please configure sync rules manually");
143
- }
144
- }
145
- if (legacy.mcp) {
146
- yamlConfig.mcp = {
147
- enabled: legacy.mcp.enabled || false,
148
- port: legacy.mcp.port || 3e3
149
- };
150
- }
151
- return {
152
- success: true,
153
- yamlConfig,
154
- warnings,
155
- backupPath
156
- };
157
- } catch (error) {
158
- throw new Error(
159
- `Migration failed: ${error instanceof Error ? error.message : String(error)}`
160
- );
161
- }
162
- }
163
- async function writeYamlConfig(config, outputPath) {
164
- const dir = path3.dirname(outputPath);
165
- await fs4.mkdir(dir, { recursive: true });
166
- const yamlContent = yaml.dump(config, {
167
- indent: 2,
168
- lineWidth: 80,
169
- noRefs: true,
170
- sortKeys: false
171
- });
172
- await fs4.writeFile(outputPath, yamlContent, "utf-8");
173
- }
174
- function getDefaultYamlConfig(organization) {
175
- return {
176
- organization,
177
- cacheDir: ".fractary/codex/cache",
178
- storage: [
179
- {
180
- type: "local",
181
- basePath: "./knowledge",
182
- followSymlinks: false,
183
- priority: 10
184
- },
185
- {
186
- type: "github",
187
- token: "${GITHUB_TOKEN}",
188
- apiBaseUrl: "https://api.github.com",
189
- branch: "main",
190
- priority: 50
191
- },
192
- {
193
- type: "http",
194
- baseUrl: "https://codex.example.com",
195
- timeout: 3e4,
196
- priority: 100
197
- }
198
- ],
199
- types: {
200
- custom: {}
201
- },
202
- permissions: {
203
- default: "read",
204
- rules: [
205
- {
206
- pattern: "internal/**",
207
- permission: "none"
208
- },
209
- {
210
- pattern: "public/**",
211
- permission: "read"
212
- }
213
- ]
214
- },
215
- sync: {
216
- bidirectional: true,
217
- conflictResolution: "prompt",
218
- exclude: [
219
- "node_modules/**",
220
- ".git/**",
221
- "**/*.log",
222
- ".env"
223
- ]
224
- },
225
- mcp: {
226
- enabled: false,
227
- port: 3e3
228
- }
229
- };
230
- }
231
- var init_migrate_config = __esm({
232
- "src/config/migrate-config.ts"() {
233
- init_esm_shims();
234
- }
235
- });
236
-
237
- // src/config/config-types.ts
238
- var config_types_exports = {};
239
- __export(config_types_exports, {
240
- parseDuration: () => parseDuration,
241
- parseSize: () => parseSize,
242
- resolveEnvVars: () => expandEnvVars,
243
- resolveEnvVarsInConfig: () => expandEnvVarsInConfig
244
- });
245
- var init_config_types = __esm({
246
- "src/config/config-types.ts"() {
247
- init_esm_shims();
248
- }
249
- });
250
-
251
29
  // src/client/codex-client.ts
252
30
  var codex_client_exports = {};
253
31
  __export(codex_client_exports, {
@@ -255,254 +33,12 @@ __export(codex_client_exports, {
255
33
  CodexError: () => CodexError,
256
34
  ConfigurationError: () => ConfigurationError,
257
35
  PermissionDeniedError: () => PermissionDeniedError,
258
- ValidationError: () => ValidationError
36
+ ValidationError: () => ValidationError,
37
+ createCodexClient: () => createCodexClient
259
38
  });
260
- var CodexClient;
261
39
  var init_codex_client = __esm({
262
40
  "src/client/codex-client.ts"() {
263
41
  init_esm_shims();
264
- CodexClient = class _CodexClient {
265
- cache;
266
- storage;
267
- types;
268
- organization;
269
- /**
270
- * Private constructor - use CodexClient.create() instead
271
- */
272
- constructor(cache, storage, types, organization) {
273
- this.cache = cache;
274
- this.storage = storage;
275
- this.types = types;
276
- this.organization = organization;
277
- }
278
- /**
279
- * Create a new CodexClient instance
280
- *
281
- * @param options - Optional configuration
282
- * @returns Promise resolving to CodexClient instance
283
- *
284
- * @example
285
- * ```typescript
286
- * const client = await CodexClient.create();
287
- * ```
288
- */
289
- static async create(options) {
290
- const {
291
- CacheManager,
292
- createStorageManager,
293
- createDefaultRegistry,
294
- CodexError: CodexError2,
295
- ConfigurationError: ConfigurationError2
296
- } = await import('@fractary/codex');
297
- const { readYamlConfig } = await Promise.resolve().then(() => (init_migrate_config(), migrate_config_exports));
298
- const { resolveEnvVarsInConfig } = await Promise.resolve().then(() => (init_config_types(), config_types_exports));
299
- try {
300
- const configPath = path3.join(process.cwd(), ".fractary", "config.yaml");
301
- let config;
302
- try {
303
- config = await readYamlConfig(configPath);
304
- config = resolveEnvVarsInConfig(config);
305
- } catch (error) {
306
- throw new ConfigurationError2(
307
- `Failed to load configuration from ${configPath}. Run "fractary-codex configure" to create a configuration.`
308
- );
309
- }
310
- const organization = options?.organizationSlug || config.organization;
311
- const cacheDir = options?.cacheDir || config.cacheDir || ".codex-cache";
312
- const storageConfig = {};
313
- if (config.storage && Array.isArray(config.storage)) {
314
- for (const provider of config.storage) {
315
- if (provider.type === "github") {
316
- storageConfig.github = {
317
- token: provider.token || process.env.GITHUB_TOKEN,
318
- apiBaseUrl: provider.apiBaseUrl || "https://api.github.com",
319
- branch: provider.branch || "main"
320
- };
321
- } else if (provider.type === "http") {
322
- storageConfig.http = {
323
- baseUrl: provider.baseUrl,
324
- headers: provider.headers,
325
- timeout: provider.timeout || 3e4
326
- };
327
- } else if (provider.type === "local") {
328
- storageConfig.local = {
329
- basePath: provider.basePath || "./knowledge",
330
- followSymlinks: provider.followSymlinks || false
331
- };
332
- }
333
- }
334
- }
335
- const storage = createStorageManager(storageConfig);
336
- const cache = new CacheManager({
337
- cacheDir,
338
- defaultTtl: 86400,
339
- // 24 hours
340
- maxMemoryEntries: 100,
341
- maxMemorySize: 50 * 1024 * 1024,
342
- // 50MB
343
- enablePersistence: true
344
- });
345
- cache.setStorageManager(storage);
346
- const types = createDefaultRegistry();
347
- if (config.types?.custom) {
348
- for (const [name, customType] of Object.entries(config.types.custom)) {
349
- const ct = customType;
350
- types.register({
351
- name,
352
- description: ct.description || `Custom type: ${name}`,
353
- patterns: ct.patterns || [],
354
- defaultTtl: ct.defaultTtl || 86400,
355
- archiveAfterDays: ct.archiveAfterDays !== void 0 ? ct.archiveAfterDays : null,
356
- archiveStorage: ct.archiveStorage || null
357
- });
358
- }
359
- }
360
- return new _CodexClient(cache, storage, types, organization);
361
- } catch (error) {
362
- if (error instanceof CodexError2) {
363
- throw error;
364
- }
365
- throw new CodexError2(
366
- `Failed to initialize CodexClient: ${error instanceof Error ? error.message : String(error)}`
367
- );
368
- }
369
- }
370
- /**
371
- * Fetch a document by codex:// URI
372
- *
373
- * This method:
374
- * 1. Validates the URI format
375
- * 2. Resolves the URI to a reference
376
- * 3. Uses CacheManager.get() which handles cache-first fetch
377
- *
378
- * @param uri - Codex URI (e.g., codex://org/project/path/to/file.md)
379
- * @param options - Fetch options
380
- * @returns Promise resolving to fetch result
381
- *
382
- * @throws {CodexError} If URI format is invalid or fetch fails
383
- *
384
- * @example
385
- * ```typescript
386
- * const result = await client.fetch('codex://fractary/codex/docs/README.md');
387
- * console.log(result.content.toString());
388
- * ```
389
- */
390
- async fetch(uri, options) {
391
- const { validateUri, resolveReference, CodexError: CodexError2 } = await import('@fractary/codex');
392
- if (!validateUri(uri)) {
393
- throw new CodexError2(`Invalid codex URI: ${uri}`);
394
- }
395
- const resolved = resolveReference(uri);
396
- if (!resolved) {
397
- throw new CodexError2(`Failed to resolve URI: ${uri}`);
398
- }
399
- try {
400
- if (options?.bypassCache) {
401
- const result2 = await this.storage.fetch(resolved);
402
- return {
403
- content: result2.content,
404
- fromCache: false,
405
- metadata: {
406
- fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
407
- contentLength: result2.size
408
- }
409
- };
410
- }
411
- const result = await this.cache.get(resolved, {
412
- ttl: options?.ttl
413
- });
414
- return {
415
- content: result.content,
416
- fromCache: true,
417
- // CacheManager.get handles cache logic
418
- metadata: {
419
- contentLength: result.size
420
- }
421
- };
422
- } catch (error) {
423
- throw new CodexError2(
424
- `Failed to fetch ${uri}: ${error instanceof Error ? error.message : String(error)}`
425
- );
426
- }
427
- }
428
- /**
429
- * Invalidate cache entries
430
- *
431
- * @param pattern - Optional glob pattern to match entries
432
- * If not provided, clears all entries
433
- *
434
- * @example
435
- * ```typescript
436
- * // Clear all cache
437
- * await client.invalidateCache();
438
- *
439
- * // Clear specific URI
440
- * await client.invalidateCache('codex://fractary/codex/docs/README.md');
441
- * ```
442
- */
443
- async invalidateCache(pattern) {
444
- if (pattern) {
445
- await this.cache.invalidate(pattern);
446
- } else {
447
- await this.cache.clear();
448
- }
449
- }
450
- /**
451
- * Get cache statistics
452
- *
453
- * @returns Promise resolving to cache stats
454
- *
455
- * @example
456
- * ```typescript
457
- * const stats = await client.getCacheStats();
458
- * console.log(`Cache entries: ${stats.totalEntries}`);
459
- * console.log(`Total size: ${stats.totalSize}`);
460
- * ```
461
- */
462
- async getCacheStats() {
463
- return this.cache.getStats();
464
- }
465
- /**
466
- * Get the type registry
467
- *
468
- * Provides access to built-in and custom artifact types
469
- *
470
- * @returns TypeRegistry instance
471
- *
472
- * @example
473
- * ```typescript
474
- * const registry = client.getTypeRegistry();
475
- * const types = registry.list();
476
- * ```
477
- */
478
- getTypeRegistry() {
479
- return this.types;
480
- }
481
- /**
482
- * Get the cache manager (for advanced operations)
483
- *
484
- * @returns CacheManager instance
485
- */
486
- getCacheManager() {
487
- return this.cache;
488
- }
489
- /**
490
- * Get the storage manager (for advanced operations)
491
- *
492
- * @returns StorageManager instance
493
- */
494
- getStorageManager() {
495
- return this.storage;
496
- }
497
- /**
498
- * Get the organization slug
499
- *
500
- * @returns Organization slug string
501
- */
502
- getOrganization() {
503
- return this.organization;
504
- }
505
- };
506
42
  }
507
43
  });
508
44
 
@@ -567,7 +103,7 @@ function getTempCodexPath(config) {
567
103
  const codexRepo = config.codex_repo || "codex";
568
104
  const sanitizedOrg = sanitizePathComponent(config.organization);
569
105
  const sanitizedRepo = sanitizePathComponent(codexRepo);
570
- return path3.join(
106
+ return path4.join(
571
107
  os.tmpdir(),
572
108
  "fractary-codex-clone",
573
109
  `${sanitizedOrg}-${sanitizedRepo}-${process.pid}`
@@ -575,8 +111,8 @@ function getTempCodexPath(config) {
575
111
  }
576
112
  async function isValidGitRepo(repoPath) {
577
113
  try {
578
- const gitDir = path3.join(repoPath, ".git");
579
- const stats = await fs4.stat(gitDir);
114
+ const gitDir = path4.join(repoPath, ".git");
115
+ const stats = await fs2.stat(gitDir);
580
116
  return stats.isDirectory();
581
117
  } catch {
582
118
  return false;
@@ -608,8 +144,8 @@ async function execGit(repoPath, args) {
608
144
  }
609
145
  }
610
146
  async function gitClone(url, targetPath, options) {
611
- const parentDir = path3.dirname(targetPath);
612
- await fs4.mkdir(parentDir, { recursive: true });
147
+ const parentDir = path4.dirname(targetPath);
148
+ await fs2.mkdir(parentDir, { recursive: true });
613
149
  const args = ["clone"];
614
150
  if (options?.depth) {
615
151
  if (!Number.isInteger(options.depth) || options.depth <= 0) {
@@ -656,12 +192,12 @@ async function ensureCodexCloned(config, options) {
656
192
  } catch (error) {
657
193
  console.warn(`Failed to update existing clone: ${error.message}`);
658
194
  console.warn(`Removing and cloning fresh...`);
659
- await fs4.rm(tempPath, { recursive: true, force: true });
195
+ await fs2.rm(tempPath, { recursive: true, force: true });
660
196
  }
661
197
  }
662
198
  const repoUrl = getCodexRepoUrl(config);
663
199
  try {
664
- await fs4.rm(tempPath, { recursive: true, force: true });
200
+ await fs2.rm(tempPath, { recursive: true, force: true });
665
201
  } catch (error) {
666
202
  console.warn(`Could not remove existing directory ${tempPath}: ${error.message}`);
667
203
  }
@@ -684,539 +220,304 @@ init_esm_shims();
684
220
  // src/commands/config/index.ts
685
221
  init_esm_shims();
686
222
 
687
- // src/commands/config/init.ts
688
- init_esm_shims();
689
-
690
- // src/config/unified-config.ts
223
+ // src/commands/config/initialize.ts
691
224
  init_esm_shims();
692
- function sanitizeForS3BucketName(name) {
693
- return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-").substring(0, 63);
694
- }
695
- function getDefaultUnifiedConfig(organization, project, codexRepo) {
696
- const sanitizedProject = sanitizeForS3BucketName(project);
697
- return {
698
- file: {
699
- schema_version: CONFIG_SCHEMA_VERSION,
700
- sources: {
701
- specs: {
702
- type: "s3",
703
- bucket: `${sanitizedProject}-files`,
704
- prefix: "specs/",
705
- region: "us-east-1",
706
- local: {
707
- base_path: ".fractary/specs"
708
- },
709
- push: {
710
- compress: false,
711
- keep_local: true
712
- },
713
- auth: {
714
- profile: "default"
715
- }
716
- },
717
- logs: {
718
- type: "s3",
719
- bucket: `${sanitizedProject}-files`,
720
- prefix: "logs/",
721
- region: "us-east-1",
722
- local: {
723
- base_path: ".fractary/logs"
724
- },
725
- push: {
726
- compress: true,
727
- keep_local: true
728
- },
729
- auth: {
730
- profile: "default"
731
- }
732
- }
733
- }
734
- },
735
- codex: {
736
- schema_version: CONFIG_SCHEMA_VERSION,
737
- organization,
738
- project,
739
- codex_repo: codexRepo,
740
- remotes: {
741
- // The codex repository - uses same token as git operations
742
- [`${organization}/${codexRepo}`]: {
743
- token: "${GITHUB_TOKEN}"
744
- }
745
- }
746
- }
747
- };
748
- }
749
- async function readUnifiedConfig(configPath) {
750
- try {
751
- const content = await fs4.readFile(configPath, "utf-8");
752
- const config = yaml.load(content);
753
- return config;
754
- } catch (error) {
755
- if (error.code === "ENOENT") {
756
- return null;
757
- }
758
- throw error;
759
- }
760
- }
761
- async function writeUnifiedConfig(config, outputPath) {
762
- const dir = path3.dirname(outputPath);
763
- await fs4.mkdir(dir, { recursive: true });
764
- const yamlContent = yaml.dump(config, {
765
- indent: 2,
766
- lineWidth: 120,
767
- noRefs: true,
768
- sortKeys: false
769
- });
770
- await fs4.writeFile(outputPath, yamlContent, "utf-8");
771
- }
772
- function mergeUnifiedConfigs(existing, updates) {
773
- const merged = {};
774
- if (updates.file || existing.file) {
775
- merged.file = {
776
- schema_version: updates.file?.schema_version || existing.file?.schema_version || CONFIG_SCHEMA_VERSION,
777
- sources: {
778
- ...existing.file?.sources || {},
779
- ...updates.file?.sources || {}
780
- }
781
- };
782
- }
783
- if (updates.codex || existing.codex) {
784
- merged.codex = {
785
- schema_version: updates.codex?.schema_version || existing.codex?.schema_version || CONFIG_SCHEMA_VERSION,
786
- organization: updates.codex?.organization || existing.codex?.organization || "default",
787
- project: updates.codex?.project || existing.codex?.project || "default",
788
- codex_repo: updates.codex?.codex_repo || existing.codex?.codex_repo || "",
789
- remotes: {
790
- ...existing.codex?.remotes || {},
791
- ...updates.codex?.remotes || {}
792
- }
793
- };
794
- }
795
- return merged;
796
- }
797
- async function initializeUnifiedConfig(configPath, organization, project, codexRepo, options) {
798
- const existingConfig = await readUnifiedConfig(configPath);
799
- if (existingConfig && !options?.force) {
800
- const defaultConfig = getDefaultUnifiedConfig(organization, project, codexRepo);
801
- const merged = mergeUnifiedConfigs(existingConfig, defaultConfig);
802
- await writeUnifiedConfig(merged, configPath);
803
- return {
804
- created: false,
805
- merged: true,
806
- config: merged
807
- };
808
- }
809
- const config = getDefaultUnifiedConfig(organization, project, codexRepo);
810
- await writeUnifiedConfig(config, configPath);
811
- return {
812
- created: true,
813
- merged: false,
814
- config
815
- };
816
- }
817
-
818
- // src/config/gitignore-utils.ts
819
- init_esm_shims();
820
- var DEFAULT_FRACTARY_GITIGNORE = `# .fractary/.gitignore
821
- # This file is managed by multiple plugins - each plugin manages its own section
822
-
823
- # ===== fractary-codex (managed) =====
824
- codex/cache/
825
- # ===== end fractary-codex =====
826
- `;
827
- async function readFractaryGitignore(projectRoot) {
828
- const gitignorePath = path3.join(projectRoot, ".fractary", ".gitignore");
829
- try {
830
- return await fs4.readFile(gitignorePath, "utf-8");
831
- } catch (error) {
832
- if (error.code === "ENOENT") {
833
- return null;
834
- }
835
- throw error;
836
- }
837
- }
838
- async function writeFractaryGitignore(projectRoot, content) {
839
- const gitignorePath = path3.join(projectRoot, ".fractary", ".gitignore");
840
- await fs4.mkdir(path3.join(projectRoot, ".fractary"), { recursive: true });
841
- await fs4.writeFile(gitignorePath, content, "utf-8");
842
- }
843
- function normalizeCachePath(cachePath) {
844
- let normalized = cachePath.replace(/\\/g, "/");
845
- normalized = normalized.replace(/^\.fractary\//, "");
846
- if (!normalized.endsWith("/")) {
847
- normalized += "/";
848
- }
849
- return normalized;
850
- }
851
- function isCachePathIgnored(gitignoreContent, cachePath) {
852
- const normalized = normalizeCachePath(cachePath);
853
- const lines = gitignoreContent.split("\n").map((l) => l.trim());
854
- return lines.some((line) => {
855
- if (line.startsWith("#") || line === "") return false;
856
- let normalizedLine = line.replace(/\\/g, "/");
857
- if (!normalizedLine.endsWith("/")) {
858
- normalizedLine += "/";
859
- }
860
- return normalizedLine === normalized;
861
- });
862
- }
863
- function addCachePathToGitignore(gitignoreContent, cachePath, comment) {
864
- const normalized = normalizeCachePath(cachePath);
865
- if (isCachePathIgnored(gitignoreContent, cachePath)) {
866
- return gitignoreContent;
867
- }
868
- let addition = "";
869
- {
870
- addition += `
871
- # ${comment}
872
- `;
873
- }
874
- addition += normalized + "\n";
875
- return gitignoreContent.trimEnd() + addition;
876
- }
877
- async function ensureCachePathIgnored(projectRoot, cachePath) {
878
- const gitignorePath = path3.join(projectRoot, ".fractary", ".gitignore");
879
- let relativeCachePath = cachePath;
880
- if (path3.isAbsolute(cachePath)) {
881
- relativeCachePath = path3.relative(path3.join(projectRoot, ".fractary"), cachePath);
882
- }
883
- relativeCachePath = normalizeCachePath(relativeCachePath);
884
- let content = await readFractaryGitignore(projectRoot);
885
- const gitignoreExists = content !== null;
886
- if (!gitignoreExists) {
887
- content = DEFAULT_FRACTARY_GITIGNORE;
888
- if (!isCachePathIgnored(content, relativeCachePath)) {
889
- content = addCachePathToGitignore(content, relativeCachePath, "Custom cache directory");
890
- }
891
- await writeFractaryGitignore(projectRoot, content);
892
- return {
893
- created: true,
894
- updated: false,
895
- alreadyIgnored: false,
896
- gitignorePath
897
- };
898
- }
899
- if (isCachePathIgnored(content, relativeCachePath)) {
900
- return {
901
- created: false,
902
- updated: false,
903
- alreadyIgnored: true,
904
- gitignorePath
905
- };
906
- }
907
- content = addCachePathToGitignore(content, relativeCachePath, "Custom cache directory");
908
- await writeFractaryGitignore(projectRoot, content);
909
- return {
910
- created: false,
911
- updated: true,
912
- alreadyIgnored: false,
913
- gitignorePath
914
- };
915
- }
916
-
917
- // src/commands/config/init.ts
918
- function validateNameFormat(name, type) {
919
- if (!name || typeof name !== "string") {
920
- throw new Error(`${type} name is required`);
921
- }
922
- if (name.length > 100) {
923
- throw new Error(`${type} name too long (max 100 characters)`);
924
- }
925
- const safePattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
926
- if (!safePattern.test(name)) {
927
- throw new Error(
928
- `Invalid ${type} name format: "${name}". Must start with alphanumeric and contain only: a-z, A-Z, 0-9, ., -, _`
929
- );
930
- }
931
- }
932
- async function getOrgFromGitRemote() {
933
- try {
934
- const { execSync } = __require("child_process");
935
- const remote = execSync("git remote get-url origin 2>/dev/null", { encoding: "utf-8" }).trim();
936
- const sshMatch = remote.match(/git@github\.com:([^/]+)\//);
937
- const httpsMatch = remote.match(/github\.com\/([^/]+)\//);
938
- return sshMatch?.[1] || httpsMatch?.[1] || null;
939
- } catch {
940
- return null;
941
- }
942
- }
943
- async function discoverCodexRepo(org) {
944
- try {
945
- validateNameFormat(org, "organization");
946
- } catch (error) {
947
- return { repo: null, error: "unknown", message: error.message };
948
- }
949
- try {
950
- const { execSync } = __require("child_process");
951
- try {
952
- execSync("gh --version", { encoding: "utf-8", stdio: "pipe" });
953
- } catch {
954
- return {
955
- repo: null,
956
- error: "gh_not_installed",
957
- message: "GitHub CLI (gh) is not installed. Install from https://cli.github.com/"
958
- };
959
- }
960
- try {
961
- execSync("gh auth status", { encoding: "utf-8", stdio: "pipe" });
962
- } catch {
963
- return {
964
- repo: null,
965
- error: "auth_failed",
966
- message: "GitHub CLI not authenticated. Run: gh auth login"
967
- };
968
- }
969
- const result = execSync(
970
- `gh repo list ${org} --json name --jq '.[].name | select(startswith("codex."))' 2>&1`,
971
- { encoding: "utf-8" }
972
- ).trim();
973
- if (result.includes("Could not resolve to an Organization") || result.includes("Not Found")) {
974
- return {
975
- repo: null,
976
- error: "org_not_found",
977
- message: `Organization '${org}' not found on GitHub`
978
- };
979
- }
980
- const repos = result.split("\n").filter(Boolean);
981
- if (repos.length === 0) {
982
- return {
983
- repo: null,
984
- error: "no_repos_found",
985
- message: `No codex.* repositories found in organization '${org}'`
986
- };
987
- }
988
- return { repo: repos[0] };
989
- } catch (error) {
990
- return {
991
- repo: null,
992
- error: "unknown",
993
- message: error.message || "Unknown error during discovery"
994
- };
995
- }
996
- }
997
- async function fileExists(filePath) {
998
- try {
999
- await fs4.access(filePath);
1000
- return true;
1001
- } catch {
1002
- return false;
1003
- }
1004
- }
1005
- async function installMcpServer(projectRoot, configPath = ".fractary/config.yaml", options = {}) {
1006
- const mcpJsonPath = path3.join(projectRoot, ".mcp.json");
1007
- const { backup = true } = options;
1008
- let existingConfig = { mcpServers: {} };
1009
- let backupPath;
1010
- let migrated = false;
1011
- if (await fileExists(mcpJsonPath)) {
225
+ function configInitializeCommand() {
226
+ const cmd = new Command("config-initialize");
227
+ cmd.description("Initialize codex section in .fractary/config.yaml (requires base config from @fractary/core)").option("--org <slug>", 'Organization slug (e.g., "fractary")').option("--project <name>", "Project name (default: derived from directory)").option("--codex-repo <name>", 'Codex repository name (e.g., "codex.fractary.com")').option("--sync-preset <name>", "Sync preset (standard, minimal)", "standard").option("--force", "Overwrite existing codex section").option("--no-mcp", "Skip MCP server installation").option("--json", "Output as JSON").action(async (options) => {
1012
228
  try {
1013
- const content = await fs4.readFile(mcpJsonPath, "utf-8");
1014
- existingConfig = JSON.parse(content);
1015
- if (backup) {
1016
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 18);
1017
- const suffix = Math.random().toString(36).substring(2, 6);
1018
- backupPath = `${mcpJsonPath}.backup.${timestamp}-${suffix}`;
1019
- await fs4.writeFile(backupPath, content);
1020
- }
1021
- } catch {
1022
- console.log(chalk7.yellow("\u26A0 Warning: .mcp.json contains invalid JSON, starting fresh"));
1023
- existingConfig = { mcpServers: {} };
1024
- }
1025
- }
1026
- if (!existingConfig.mcpServers) {
1027
- existingConfig.mcpServers = {};
1028
- }
1029
- const existing = existingConfig.mcpServers["fractary-codex"];
1030
- if (existing) {
1031
- const existingCommand = existing.command;
1032
- const existingArgs = existing.args || [];
1033
- if (existingCommand === "npx" && existingArgs.includes("@fractary/codex-mcp")) {
1034
- return {
1035
- installed: false,
1036
- migrated: false,
1037
- alreadyInstalled: true,
1038
- backupPath
1039
- };
1040
- }
1041
- if (existingCommand === "node" || existingArgs.includes("@fractary/codex")) {
1042
- migrated = true;
1043
- }
1044
- }
1045
- existingConfig.mcpServers["fractary-codex"] = {
1046
- command: "npx",
1047
- args: ["-y", "@fractary/codex-mcp", "--config", configPath]
1048
- };
1049
- await fs4.writeFile(
1050
- mcpJsonPath,
1051
- JSON.stringify(existingConfig, null, 2) + "\n"
1052
- );
1053
- return {
1054
- installed: true,
1055
- migrated,
1056
- alreadyInstalled: false,
1057
- backupPath
1058
- };
1059
- }
1060
- function configureCommand() {
1061
- const cmd = new Command("configure");
1062
- cmd.description("Initialize unified Fractary configuration (.fractary/config.yaml)").option("--org <slug>", 'Organization slug (e.g., "fractary")').option("--project <name>", "Project name (default: derived from directory)").option("--codex-repo <name>", 'Codex repository name (e.g., "codex.fractary.com")').option("--force", "Overwrite existing configuration").option("--no-mcp", "Skip MCP server installation").action(async (options) => {
1063
- try {
1064
- console.log(chalk7.blue("Initializing unified Fractary configuration...\n"));
229
+ const configManager = createConfigManager(process.cwd());
230
+ if (!options.json) {
231
+ console.log(chalk10.blue("Initializing codex configuration...\n"));
232
+ }
1065
233
  let org = options.org;
1066
234
  if (!org) {
1067
- org = await getOrgFromGitRemote();
235
+ org = await configManager.detectOrganization();
1068
236
  }
1069
237
  if (!org) {
1070
238
  try {
1071
- const { resolveOrganization } = await import('@fractary/codex');
1072
239
  org = resolveOrganization({
1073
- repoName: path3.basename(process.cwd())
240
+ repoName: path4.basename(process.cwd())
1074
241
  });
1075
242
  } catch {
1076
243
  }
1077
244
  }
1078
245
  if (!org) {
1079
- org = path3.basename(process.cwd()).split("-")[0] || "default";
1080
- console.log(chalk7.yellow(`\u26A0 Could not detect organization, using: ${org}`));
1081
- console.log(chalk7.dim(" Use --org <slug> to specify explicitly\n"));
1082
- } else {
1083
- console.log(chalk7.dim(`Organization: ${chalk7.cyan(org)}
246
+ org = path4.basename(process.cwd()).split("-")[0] || "default";
247
+ if (!options.json) {
248
+ console.log(chalk10.yellow(`\u26A0 Could not detect organization, using: ${org}`));
249
+ console.log(chalk10.dim(" Use --org <slug> to specify explicitly\n"));
250
+ }
251
+ } else if (!options.json) {
252
+ console.log(chalk10.dim(`Organization: ${chalk10.cyan(org)}
1084
253
  `));
1085
254
  }
1086
- try {
1087
- validateNameFormat(org, "organization");
1088
- } catch (error) {
1089
- console.error(chalk7.red("Error:"), error.message);
255
+ const orgValidation = validateNameFormat(org, "organization");
256
+ if (!orgValidation.valid) {
257
+ if (options.json) {
258
+ console.log(JSON.stringify({ error: orgValidation.error }, null, 2));
259
+ } else {
260
+ console.error(chalk10.red("Error:"), orgValidation.error);
261
+ }
1090
262
  process.exit(1);
1091
263
  }
1092
264
  let project = options.project;
1093
265
  if (!project) {
1094
- project = path3.basename(process.cwd());
1095
- console.log(chalk7.dim(`Project: ${chalk7.cyan(project)}
266
+ project = configManager.detectProject();
267
+ if (!options.json) {
268
+ console.log(chalk10.dim(`Project: ${chalk10.cyan(project)}
1096
269
  `));
270
+ }
1097
271
  }
1098
272
  let codexRepo = options.codexRepo;
1099
273
  if (codexRepo) {
1100
- try {
1101
- validateNameFormat(codexRepo, "repository");
1102
- } catch (error) {
1103
- console.error(chalk7.red("Error:"), error.message);
274
+ const repoValidation = validateNameFormat(codexRepo, "repository");
275
+ if (!repoValidation.valid) {
276
+ if (options.json) {
277
+ console.log(JSON.stringify({ error: repoValidation.error }, null, 2));
278
+ } else {
279
+ console.error(chalk10.red("Error:"), repoValidation.error);
280
+ }
1104
281
  process.exit(1);
1105
282
  }
1106
- console.log(chalk7.dim(`Codex repository: ${chalk7.cyan(codexRepo)}
283
+ if (!options.json) {
284
+ console.log(chalk10.dim(`Codex repository: ${chalk10.cyan(codexRepo)}
1107
285
  `));
286
+ }
1108
287
  } else {
1109
- const discoveryResult = await discoverCodexRepo(org);
288
+ const discoveryResult = await configManager.discoverCodexRepo(org);
1110
289
  if (discoveryResult.repo) {
1111
290
  codexRepo = discoveryResult.repo;
1112
- console.log(chalk7.dim(`Codex repository: ${chalk7.cyan(codexRepo)} (auto-discovered)
1113
- `));
1114
- } else {
1115
- if (discoveryResult.error === "gh_not_installed") {
1116
- console.log(chalk7.dim(` Note: ${discoveryResult.message}
1117
- `));
1118
- } else if (discoveryResult.error === "auth_failed") {
1119
- console.log(chalk7.dim(` Note: ${discoveryResult.message}
291
+ if (!options.json) {
292
+ console.log(chalk10.dim(`Codex repository: ${chalk10.cyan(codexRepo)} (auto-discovered)
1120
293
  `));
1121
- } else if (discoveryResult.error === "org_not_found") {
1122
- console.log(chalk7.dim(` Note: ${discoveryResult.message}
294
+ }
295
+ } else if (!options.json) {
296
+ if (discoveryResult.message) {
297
+ console.log(chalk10.dim(` Note: ${discoveryResult.message}
1123
298
  `));
1124
299
  }
1125
300
  }
1126
301
  }
1127
302
  if (!codexRepo) {
1128
- console.log(chalk7.yellow(`\u26A0 Could not discover codex repository in organization '${org}'`));
1129
- console.log(chalk7.dim(" Use --codex-repo <name> to specify explicitly"));
1130
- console.log(chalk7.dim(" Expected naming convention: codex.{org}.{tld} (e.g., codex.fractary.com)\n"));
1131
303
  codexRepo = `codex.${org}.com`;
1132
- console.log(chalk7.dim(` Using default: ${chalk7.cyan(codexRepo)}
304
+ if (!options.json) {
305
+ console.log(chalk10.yellow(`\u26A0 Could not discover codex repository in organization '${org}'`));
306
+ console.log(chalk10.dim(` Using default: ${chalk10.cyan(codexRepo)}
1133
307
  `));
308
+ }
1134
309
  }
1135
- const configPath = path3.join(process.cwd(), ".fractary", "config.yaml");
1136
- const configExists = await fileExists(configPath);
1137
- if (configExists && !options.force) {
1138
- console.log(chalk7.yellow(`\u26A0 Configuration already exists at .fractary/config.yaml`));
1139
- console.log(chalk7.dim("Merging with existing configuration...\n"));
1140
- }
1141
- console.log("Creating directory structure...");
1142
- const dirs = [
1143
- ".fractary",
1144
- ".fractary/specs",
1145
- ".fractary/logs",
1146
- ".fractary/codex",
1147
- ".fractary/codex/cache"
1148
- ];
1149
- for (const dir of dirs) {
1150
- await fs4.mkdir(path3.join(process.cwd(), dir), { recursive: true });
1151
- console.log(chalk7.green("\u2713"), chalk7.dim(dir + "/"));
1152
- }
1153
- const gitignoreResult = await ensureCachePathIgnored(process.cwd(), ".fractary/codex/cache");
1154
- if (gitignoreResult.created) {
1155
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/.gitignore (created)"));
1156
- } else if (gitignoreResult.updated) {
1157
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/.gitignore (updated)"));
1158
- } else {
1159
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/.gitignore (exists)"));
1160
- }
1161
- console.log("\nInitializing configuration...");
1162
- const result = await initializeUnifiedConfig(
1163
- configPath,
1164
- org,
310
+ const result = await configManager.initializeCodexSection({
311
+ organization: org,
1165
312
  project,
1166
313
  codexRepo,
1167
- { force: options.force }
1168
- );
1169
- if (result.created) {
1170
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/config.yaml (created)"));
1171
- } else if (result.merged) {
1172
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/config.yaml (merged with existing)"));
1173
- }
1174
- if (options.mcp !== false) {
1175
- console.log("\nConfiguring MCP server...");
1176
- const mcpResult = await installMcpServer(process.cwd(), ".fractary/config.yaml");
1177
- if (mcpResult.alreadyInstalled) {
1178
- console.log(chalk7.green("\u2713"), chalk7.dim(".mcp.json (already configured)"));
1179
- } else if (mcpResult.migrated) {
1180
- console.log(chalk7.green("\u2713"), chalk7.dim(".mcp.json (migrated from old format)"));
1181
- if (mcpResult.backupPath) {
1182
- console.log(chalk7.dim(` Backup: ${path3.basename(mcpResult.backupPath)}`));
1183
- }
1184
- } else if (mcpResult.installed) {
1185
- console.log(chalk7.green("\u2713"), chalk7.dim(".mcp.json (created)"));
314
+ syncPreset: options.syncPreset,
315
+ force: options.force,
316
+ skipMcp: options.mcp === false
317
+ });
318
+ if (options.json) {
319
+ console.log(
320
+ JSON.stringify(
321
+ {
322
+ success: true,
323
+ configPath: result.configPath,
324
+ codexSectionCreated: result.codexSectionCreated,
325
+ organization: org,
326
+ project,
327
+ codexRepo,
328
+ directories: result.directories,
329
+ gitignore: result.gitignore,
330
+ mcp: result.mcp
331
+ },
332
+ null,
333
+ 2
334
+ )
335
+ );
336
+ return;
337
+ }
338
+ console.log("Setting up codex directories...");
339
+ for (const dir of result.directories.created) {
340
+ console.log(chalk10.green("\u2713"), chalk10.dim(dir + "/"));
341
+ }
342
+ for (const dir of result.directories.alreadyExisted) {
343
+ console.log(chalk10.dim(" " + dir + "/ (exists)"));
344
+ }
345
+ if (result.gitignore.created) {
346
+ console.log(chalk10.green("\u2713"), chalk10.dim(".fractary/.gitignore (created)"));
347
+ } else if (result.gitignore.updated) {
348
+ console.log(chalk10.green("\u2713"), chalk10.dim(".fractary/.gitignore (updated)"));
349
+ } else {
350
+ console.log(chalk10.green("\u2713"), chalk10.dim(".fractary/.gitignore (exists)"));
351
+ }
352
+ console.log(chalk10.green("\u2713"), chalk10.dim(".fractary/config.yaml (codex section added)"));
353
+ if (result.mcp) {
354
+ if (result.mcp.alreadyInstalled) {
355
+ console.log(chalk10.green("\u2713"), chalk10.dim(".mcp.json (already configured)"));
356
+ } else if (result.mcp.migrated) {
357
+ console.log(chalk10.green("\u2713"), chalk10.dim(".mcp.json (migrated from old format)"));
358
+ } else if (result.mcp.installed) {
359
+ console.log(chalk10.green("\u2713"), chalk10.dim(".mcp.json (created)"));
1186
360
  }
1187
361
  }
1188
- console.log(chalk7.green("\n\u2713 Unified configuration initialized successfully!\n"));
1189
- console.log(chalk7.bold("Configuration:"));
1190
- console.log(chalk7.dim(` Organization: ${org}`));
1191
- console.log(chalk7.dim(` Project: ${project}`));
1192
- console.log(chalk7.dim(` Codex Repository: ${codexRepo}`));
1193
- console.log(chalk7.dim(` Config: .fractary/config.yaml`));
1194
- console.log(chalk7.bold("\nFile plugin sources:"));
1195
- console.log(chalk7.dim(" - specs: .fractary/specs/ \u2192 S3"));
1196
- console.log(chalk7.dim(" - logs: .fractary/logs/ \u2192 S3"));
1197
- console.log(chalk7.bold("\nCodex plugin:"));
1198
- console.log(chalk7.dim(" - Cache: .fractary/codex/cache/"));
1199
- console.log(chalk7.dim(" - MCP Server: @fractary/codex-mcp (via npx)"));
1200
- console.log(chalk7.dim(" - Remotes: codex repo configured"));
1201
- console.log(chalk7.bold("\nGit Authentication:"));
1202
- console.log(chalk7.dim(" Codex sync uses your existing git credentials."));
1203
- console.log(chalk7.dim(" Ensure you have access to the codex repository:"));
1204
- console.log(chalk7.dim(` gh repo view ${org}/${codexRepo}`));
1205
- console.log(chalk7.dim(" Or set GITHUB_TOKEN environment variable."));
1206
- console.log(chalk7.bold("\nNext steps:"));
1207
- console.log(chalk7.dim(" 1. Restart Claude Code to load the MCP server"));
1208
- console.log(chalk7.dim(" 2. Verify codex repository access: gh repo view " + org + "/" + codexRepo));
1209
- console.log(chalk7.dim(" 3. Configure AWS credentials for S3 access (if using file plugin)"));
1210
- console.log(chalk7.dim(" 4. Edit .fractary/config.yaml to add external project remotes"));
1211
- console.log(chalk7.dim(" 5. Reference docs via codex:// URIs (auto-fetched by MCP)"));
362
+ console.log(chalk10.green("\n\u2713 Codex configuration initialized successfully!\n"));
363
+ console.log(chalk10.bold("Configuration:"));
364
+ console.log(chalk10.dim(` Organization: ${org}`));
365
+ console.log(chalk10.dim(` Project: ${project}`));
366
+ console.log(chalk10.dim(` Codex Repository: ${codexRepo}`));
367
+ console.log(chalk10.bold("\nNext steps:"));
368
+ console.log(chalk10.dim(" 1. Restart Claude Code to load the MCP server"));
369
+ console.log(chalk10.dim(` 2. Verify codex repository access: gh repo view ${org}/${codexRepo}`));
370
+ console.log(chalk10.dim(" 3. Run first sync: /fractary-codex:sync --from-codex --dry-run"));
1212
371
  } catch (error) {
1213
- console.error(chalk7.red("Error:"), error.message);
372
+ if (options.json) {
373
+ console.log(JSON.stringify({ error: error.message }, null, 2));
374
+ } else {
375
+ console.error(chalk10.red("Error:"), error.message);
376
+ }
1214
377
  process.exit(1);
1215
378
  }
1216
379
  });
1217
380
  return cmd;
1218
381
  }
1219
382
 
383
+ // src/commands/config/update.ts
384
+ init_esm_shims();
385
+ function configUpdateCommand() {
386
+ const cmd = new Command("config-update");
387
+ cmd.description("Update codex configuration fields in .fractary/config.yaml").option("--org <slug>", "Update organization slug").option("--project <name>", "Update project name").option("--codex-repo <name>", "Update codex repository name").option("--sync-preset <name>", "Update sync preset (standard, minimal)").option("--no-mcp", "Skip MCP server update").option("--json", "Output as JSON").action(async (options) => {
388
+ try {
389
+ const configManager = createConfigManager(process.cwd());
390
+ const updateOptions = {
391
+ skipMcp: options.mcp === false
392
+ };
393
+ let hasUpdates = false;
394
+ if (options.org !== void 0) {
395
+ updateOptions.organization = options.org;
396
+ hasUpdates = true;
397
+ }
398
+ if (options.project !== void 0) {
399
+ updateOptions.project = options.project;
400
+ hasUpdates = true;
401
+ }
402
+ if (options.codexRepo !== void 0) {
403
+ updateOptions.codexRepo = options.codexRepo;
404
+ hasUpdates = true;
405
+ }
406
+ if (options.syncPreset !== void 0) {
407
+ updateOptions.syncPreset = options.syncPreset;
408
+ hasUpdates = true;
409
+ }
410
+ if (!hasUpdates) {
411
+ if (options.json) {
412
+ console.log(JSON.stringify({ error: "No fields to update. Provide at least one of: --org, --project, --codex-repo, --sync-preset" }, null, 2));
413
+ } else {
414
+ console.error(chalk10.red("Error:"), "No fields to update. Provide at least one of: --org, --project, --codex-repo, --sync-preset");
415
+ }
416
+ process.exit(1);
417
+ }
418
+ if (!options.json) {
419
+ console.log(chalk10.blue("Updating codex configuration...\n"));
420
+ }
421
+ const result = await configManager.updateCodexSection(updateOptions);
422
+ if (options.json) {
423
+ console.log(
424
+ JSON.stringify(
425
+ {
426
+ success: true,
427
+ configPath: result.configPath,
428
+ fieldsUpdated: result.fieldsUpdated,
429
+ mcp: result.mcp
430
+ },
431
+ null,
432
+ 2
433
+ )
434
+ );
435
+ return;
436
+ }
437
+ if (result.fieldsUpdated.length === 0) {
438
+ console.log(chalk10.yellow("No fields were updated."));
439
+ return;
440
+ }
441
+ for (const field of result.fieldsUpdated) {
442
+ console.log(chalk10.green("\u2713"), chalk10.dim(`Updated: ${field}`));
443
+ }
444
+ if (result.mcp) {
445
+ if (result.mcp.alreadyInstalled) {
446
+ console.log(chalk10.green("\u2713"), chalk10.dim(".mcp.json (already configured)"));
447
+ } else if (result.mcp.installed) {
448
+ console.log(chalk10.green("\u2713"), chalk10.dim(".mcp.json (updated)"));
449
+ }
450
+ }
451
+ console.log(chalk10.green("\n\u2713 Codex configuration updated successfully!"));
452
+ } catch (error) {
453
+ if (options.json) {
454
+ console.log(JSON.stringify({ error: error.message }, null, 2));
455
+ } else {
456
+ console.error(chalk10.red("Error:"), error.message);
457
+ }
458
+ process.exit(1);
459
+ }
460
+ });
461
+ return cmd;
462
+ }
463
+
464
+ // src/commands/config/validate.ts
465
+ init_esm_shims();
466
+ function configValidateCommand() {
467
+ const cmd = new Command("config-validate");
468
+ cmd.description("Validate codex configuration in .fractary/config.yaml (read-only)").option("--json", "Output as JSON").action(async (options) => {
469
+ try {
470
+ const configManager = createConfigManager(process.cwd());
471
+ if (!options.json) {
472
+ console.log(chalk10.blue("Validating codex configuration...\n"));
473
+ }
474
+ const result = await configManager.validateCodexConfig();
475
+ if (options.json) {
476
+ console.log(JSON.stringify(result, null, 2));
477
+ if (!result.valid) {
478
+ process.exit(1);
479
+ }
480
+ return;
481
+ }
482
+ if (result.errors.length > 0) {
483
+ console.log(chalk10.red.bold("Errors:"));
484
+ for (const error of result.errors) {
485
+ console.log(chalk10.red(" \u2717"), `${error.field}: ${error.message}`);
486
+ }
487
+ console.log();
488
+ }
489
+ if (result.warnings.length > 0) {
490
+ console.log(chalk10.yellow.bold("Warnings:"));
491
+ for (const warning of result.warnings) {
492
+ console.log(chalk10.yellow(" \u26A0"), `${warning.field}: ${warning.message}`);
493
+ }
494
+ console.log();
495
+ }
496
+ if (result.valid) {
497
+ if (result.warnings.length === 0) {
498
+ console.log(chalk10.green("\u2713 Codex configuration is valid with no warnings."));
499
+ } else {
500
+ console.log(chalk10.green("\u2713 Codex configuration is valid"), chalk10.yellow(`(${result.warnings.length} warning${result.warnings.length > 1 ? "s" : ""})`));
501
+ }
502
+ } else {
503
+ console.log(chalk10.red(`\u2717 Codex configuration is invalid (${result.errors.length} error${result.errors.length > 1 ? "s" : ""})`));
504
+ process.exit(1);
505
+ }
506
+ } catch (error) {
507
+ if (options.json) {
508
+ console.log(JSON.stringify({ error: error.message }, null, 2));
509
+ } else {
510
+ console.error(chalk10.red("Error:"), error.message);
511
+ }
512
+ process.exit(1);
513
+ }
514
+ });
515
+ return cmd;
516
+ }
517
+
518
+ // src/commands/config/init.ts
519
+ init_esm_shims();
520
+
1220
521
  // src/commands/document/index.ts
1221
522
  init_esm_shims();
1222
523
 
@@ -1244,14 +545,14 @@ function documentFetchCommand() {
1244
545
  try {
1245
546
  const { validateUri } = await import('@fractary/codex');
1246
547
  if (!validateUri(uri)) {
1247
- console.error(chalk7.red("Error: Invalid URI format"));
1248
- console.log(chalk7.dim("Expected: codex://org/project/path/to/file.md"));
1249
- console.log(chalk7.dim("Example: codex://fractary/codex/docs/api.md"));
548
+ console.error(chalk10.red("Error: Invalid URI format"));
549
+ console.log(chalk10.dim("Expected: codex://org/project/path/to/file.md"));
550
+ console.log(chalk10.dim("Example: codex://fractary/codex/docs/api.md"));
1250
551
  process.exit(1);
1251
552
  }
1252
553
  const client = await getClient();
1253
554
  if (!options.json && !options.bypassCache) {
1254
- console.error(chalk7.dim(`Fetching ${uri}...`));
555
+ console.error(chalk10.dim(`Fetching ${uri}...`));
1255
556
  }
1256
557
  const result = await client.fetch(uri, {
1257
558
  bypassCache: options.bypassCache,
@@ -1271,31 +572,31 @@ function documentFetchCommand() {
1271
572
  };
1272
573
  console.log(JSON.stringify(output, null, 2));
1273
574
  } else if (options.output) {
1274
- await fs4.writeFile(options.output, result.content);
1275
- console.log(chalk7.green("\u2713"), `Written to ${options.output}`);
1276
- console.log(chalk7.dim(` Size: ${result.content.length} bytes`));
575
+ await fs2.writeFile(options.output, result.content);
576
+ console.log(chalk10.green("\u2713"), `Written to ${options.output}`);
577
+ console.log(chalk10.dim(` Size: ${result.content.length} bytes`));
1277
578
  if (result.fromCache) {
1278
- console.log(chalk7.dim(" Source: cache"));
579
+ console.log(chalk10.dim(" Source: cache"));
1279
580
  } else {
1280
- console.log(chalk7.dim(" Source: storage"));
581
+ console.log(chalk10.dim(" Source: storage"));
1281
582
  }
1282
583
  } else {
1283
584
  if (result.fromCache && !options.bypassCache) {
1284
- console.error(chalk7.green("\u2713"), chalk7.dim("from cache\n"));
585
+ console.error(chalk10.green("\u2713"), chalk10.dim("from cache\n"));
1285
586
  } else {
1286
- console.error(chalk7.green("\u2713"), chalk7.dim("fetched\n"));
587
+ console.error(chalk10.green("\u2713"), chalk10.dim("fetched\n"));
1287
588
  }
1288
589
  console.log(result.content.toString("utf-8"));
1289
590
  }
1290
591
  } catch (error) {
1291
- console.error(chalk7.red("Error:"), error.message);
592
+ console.error(chalk10.red("Error:"), error.message);
1292
593
  if (error.message.includes("Failed to load configuration")) {
1293
- console.log(chalk7.dim('\nRun "fractary-codex configure" to create a configuration.'));
594
+ console.log(chalk10.dim('\nRun "fractary-codex configure" to create a configuration.'));
1294
595
  } else if (error.message.includes("GITHUB_TOKEN")) {
1295
- console.log(chalk7.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
596
+ console.log(chalk10.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
1296
597
  } else if (error.message.includes("not found") || error.message.includes("404")) {
1297
- console.log(chalk7.dim("\nThe document may not exist or you may not have access."));
1298
- console.log(chalk7.dim("Check the URI and ensure your storage providers are configured correctly."));
598
+ console.log(chalk10.dim("\nThe document may not exist or you may not have access."));
599
+ console.log(chalk10.dim("Check the URI and ensure your storage providers are configured correctly."));
1299
600
  }
1300
601
  process.exit(1);
1301
602
  }
@@ -1308,55 +609,116 @@ init_esm_shims();
1308
609
 
1309
610
  // src/commands/cache/list.ts
1310
611
  init_esm_shims();
1311
- function formatSize(bytes) {
1312
- if (bytes < 1024) return `${bytes} B`;
1313
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1314
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
612
+ function formatTtl(seconds) {
613
+ if (seconds < 0) {
614
+ const expired = -seconds;
615
+ if (expired < 60) return chalk10.red(`expired ${expired}s ago`);
616
+ if (expired < 3600) return chalk10.red(`expired ${Math.floor(expired / 60)}m ago`);
617
+ return chalk10.red(`expired ${Math.floor(expired / 3600)}h ago`);
618
+ }
619
+ if (seconds < 60) return chalk10.green(`${seconds}s`);
620
+ if (seconds < 3600) return chalk10.green(`${Math.floor(seconds / 60)}m`);
621
+ if (seconds < 86400) return chalk10.yellow(`${Math.floor(seconds / 3600)}h`);
622
+ return chalk10.dim(`${Math.floor(seconds / 86400)}d`);
623
+ }
624
+ function formatStatus(status) {
625
+ switch (status) {
626
+ case "fresh":
627
+ return chalk10.green("fresh");
628
+ case "stale":
629
+ return chalk10.yellow("stale");
630
+ case "expired":
631
+ return chalk10.red("expired");
632
+ }
1315
633
  }
1316
634
  function cacheListCommand() {
1317
635
  const cmd = new Command("cache-list");
1318
- cmd.description("List cache information").option("--json", "Output as JSON").action(async (options) => {
636
+ cmd.description("List cache entries").option("--json", "Output as JSON").option("--status <status>", "Filter by status (fresh, stale, expired, all)", "all").option("--limit <n>", "Maximum number of entries to show", parseInt).option("--sort <field>", "Sort by field (uri, size, createdAt, expiresAt)", "uri").option("--desc", "Sort in descending order").option("--verbose", "Show detailed entry information").action(async (options) => {
1319
637
  try {
1320
638
  const client = await getClient();
1321
- const stats = await client.getCacheStats();
1322
- if (stats.entryCount === 0) {
639
+ const listOptions = {
640
+ status: options.status,
641
+ limit: options.limit,
642
+ sortBy: options.sort,
643
+ sortDirection: options.desc ? "desc" : "asc"
644
+ };
645
+ const result = await client.listCacheEntries(listOptions);
646
+ if (result.total === 0) {
1323
647
  if (options.json) {
1324
- console.log(JSON.stringify({ entries: 0, message: "Cache is empty" }));
648
+ console.log(JSON.stringify({ entries: [], total: 0, message: "Cache is empty" }));
1325
649
  } else {
1326
- console.log(chalk7.yellow("Cache is empty."));
1327
- console.log(chalk7.dim("Fetch some documents to populate the cache."));
650
+ console.log(chalk10.yellow("Cache is empty."));
651
+ console.log(chalk10.dim("Fetch some documents to populate the cache."));
1328
652
  }
1329
653
  return;
1330
654
  }
1331
655
  if (options.json) {
1332
- console.log(JSON.stringify({
1333
- entryCount: stats.entryCount,
1334
- totalSize: stats.totalSize,
1335
- freshCount: stats.freshCount,
1336
- staleCount: stats.staleCount,
1337
- expiredCount: stats.expiredCount
1338
- }, null, 2));
656
+ console.log(JSON.stringify(result, null, 2));
1339
657
  return;
1340
658
  }
1341
- console.log(chalk7.bold("Cache Overview\n"));
1342
- console.log(chalk7.bold("Entries:"));
1343
- console.log(` Total: ${chalk7.cyan(stats.entryCount.toString())} entries`);
1344
- console.log(` Fresh: ${chalk7.green(stats.freshCount.toString())} entries`);
1345
- console.log(` Stale: ${stats.staleCount > 0 ? chalk7.yellow(stats.staleCount.toString()) : chalk7.dim("0")} entries`);
1346
- console.log(` Expired: ${stats.expiredCount > 0 ? chalk7.red(stats.expiredCount.toString()) : chalk7.dim("0")} entries`);
1347
- console.log("");
1348
- console.log(chalk7.bold("Storage:"));
1349
- console.log(` Total size: ${chalk7.cyan(formatSize(stats.totalSize))}`);
1350
- console.log("");
1351
- const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1352
- const healthColor = healthPercent > 80 ? chalk7.green : healthPercent > 50 ? chalk7.yellow : chalk7.red;
1353
- console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
659
+ console.log(chalk10.bold(`Cache Entries (${result.entries.length} of ${result.total})
660
+ `));
661
+ if (options.verbose) {
662
+ for (const entry of result.entries) {
663
+ console.log(chalk10.cyan(entry.uri));
664
+ console.log(` Status: ${formatStatus(entry.status)}`);
665
+ console.log(` Size: ${formatBytes(entry.size)}`);
666
+ console.log(` Content-Type: ${chalk10.dim(entry.contentType)}`);
667
+ console.log(` TTL: ${formatTtl(entry.remainingTtl)}`);
668
+ console.log(` In Memory: ${entry.inMemory ? chalk10.green("yes") : chalk10.dim("no")}`);
669
+ console.log("");
670
+ }
671
+ } else {
672
+ const maxUriLen = Math.min(
673
+ 60,
674
+ Math.max(...result.entries.map((e) => e.uri.length))
675
+ );
676
+ console.log(
677
+ chalk10.dim(
678
+ `${"URI".padEnd(maxUriLen)} ${"STATUS".padEnd(8)} ${"SIZE".padEnd(10)} TTL`
679
+ )
680
+ );
681
+ console.log(chalk10.dim("\u2500".repeat(maxUriLen + 35)));
682
+ for (const entry of result.entries) {
683
+ const uri = entry.uri.length > maxUriLen ? "..." + entry.uri.slice(-(maxUriLen - 3)) : entry.uri.padEnd(maxUriLen);
684
+ console.log(
685
+ `${chalk10.cyan(uri)} ${formatStatus(entry.status).padEnd(17)} ${formatBytes(entry.size).padEnd(10)} ${formatTtl(entry.remainingTtl)}`
686
+ );
687
+ }
688
+ }
689
+ if (result.hasMore) {
690
+ console.log("");
691
+ console.log(
692
+ chalk10.dim(
693
+ `Showing ${result.entries.length} of ${result.total} entries. Use --limit to see more.`
694
+ )
695
+ );
696
+ }
697
+ const stats = await client.getCacheStats();
1354
698
  console.log("");
1355
- console.log(chalk7.dim("Note: Individual cache entries are managed by the SDK."));
1356
- console.log(chalk7.dim('Use "fractary-codex cache-stats" for detailed statistics.'));
1357
- console.log(chalk7.dim('Use "fractary-codex cache-clear" to clear cache entries.'));
699
+ console.log(chalk10.dim("\u2500".repeat(60)));
700
+ console.log(
701
+ chalk10.dim(
702
+ `Total: ${stats.entryCount} entries (${formatBytes(stats.totalSize)}) | ${stats.freshCount} fresh, ${stats.staleCount} stale, ${stats.expiredCount} expired`
703
+ )
704
+ );
1358
705
  } catch (error) {
1359
- console.error(chalk7.red("Error:"), error.message);
706
+ const errorMessage = error.message || "Unknown error";
707
+ if (errorMessage.includes("ENOENT") || errorMessage.includes("not found")) {
708
+ console.error(chalk10.red("Error:"), "Cache directory not accessible");
709
+ console.error(chalk10.dim('Run "fractary-codex configure" to initialize the cache.'));
710
+ } else if (errorMessage.includes("EACCES") || errorMessage.includes("permission")) {
711
+ console.error(chalk10.red("Error:"), "Permission denied accessing cache directory");
712
+ console.error(chalk10.dim("Check file permissions for .fractary/codex/cache/"));
713
+ } else if (errorMessage.includes("ENOSPC")) {
714
+ console.error(chalk10.red("Error:"), "No space left on device");
715
+ console.error(chalk10.dim("Free up disk space and try again."));
716
+ } else if (errorMessage.includes("parse") || errorMessage.includes("JSON")) {
717
+ console.error(chalk10.red("Error:"), "Failed to parse cache metadata");
718
+ console.error(chalk10.dim('The cache may be corrupted. Try "fractary-codex cache-clear".'));
719
+ } else {
720
+ console.error(chalk10.red("Error:"), errorMessage);
721
+ }
1360
722
  process.exit(1);
1361
723
  }
1362
724
  });
@@ -1372,7 +734,7 @@ function cacheClearCommand() {
1372
734
  const client = await getClient();
1373
735
  const statsBefore = await client.getCacheStats();
1374
736
  if (statsBefore.entryCount === 0) {
1375
- console.log(chalk7.yellow("Cache is already empty. Nothing to clear."));
737
+ console.log(chalk10.yellow("Cache is already empty. Nothing to clear."));
1376
738
  return;
1377
739
  }
1378
740
  let pattern;
@@ -1381,63 +743,53 @@ function cacheClearCommand() {
1381
743
  } else if (options.pattern) {
1382
744
  pattern = options.pattern;
1383
745
  } else {
1384
- console.log(chalk7.yellow("Please specify what to clear:"));
1385
- console.log(chalk7.dim(" --all Clear entire cache"));
1386
- console.log(chalk7.dim(' --pattern Clear entries matching pattern (e.g., "codex://fractary/*")'));
746
+ console.log(chalk10.yellow("Please specify what to clear:"));
747
+ console.log(chalk10.dim(" --all Clear entire cache"));
748
+ console.log(chalk10.dim(' --pattern Clear entries matching pattern (e.g., "codex://fractary/*")'));
1387
749
  console.log("");
1388
- console.log(chalk7.dim("Examples:"));
1389
- console.log(chalk7.dim(" fractary-codex cache-clear --all"));
1390
- console.log(chalk7.dim(' fractary-codex cache-clear --pattern "codex://fractary/cli/*"'));
750
+ console.log(chalk10.dim("Examples:"));
751
+ console.log(chalk10.dim(" fractary-codex cache-clear --all"));
752
+ console.log(chalk10.dim(' fractary-codex cache-clear --pattern "codex://fractary/cli/*"'));
1391
753
  return;
1392
754
  }
1393
755
  if (options.dryRun) {
1394
- console.log(chalk7.blue("Dry run - would clear:\n"));
756
+ console.log(chalk10.blue("Dry run - would clear:\n"));
1395
757
  if (pattern) {
1396
- console.log(chalk7.dim(` Pattern: ${pattern}`));
1397
- console.log(chalk7.dim(` This would invalidate matching cache entries`));
758
+ console.log(chalk10.dim(` Pattern: ${pattern}`));
759
+ console.log(chalk10.dim(` This would invalidate matching cache entries`));
1398
760
  } else {
1399
- console.log(chalk7.dim(` All cache entries (${statsBefore.entryCount} entries)`));
761
+ console.log(chalk10.dim(` All cache entries (${statsBefore.entryCount} entries)`));
1400
762
  }
1401
- console.log(chalk7.dim(`
1402
- Total size: ${formatSize2(statsBefore.totalSize)}`));
763
+ console.log(chalk10.dim(`
764
+ Total size: ${formatBytes(statsBefore.totalSize)}`));
1403
765
  return;
1404
766
  }
1405
767
  if (pattern) {
1406
- console.log(chalk7.blue(`Clearing cache entries matching pattern: ${pattern}
768
+ console.log(chalk10.blue(`Clearing cache entries matching pattern: ${pattern}
1407
769
  `));
1408
770
  await client.invalidateCache(pattern);
1409
771
  } else {
1410
- console.log(chalk7.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
772
+ console.log(chalk10.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
1411
773
  `));
1412
774
  await client.invalidateCache();
1413
775
  }
1414
776
  const statsAfter = await client.getCacheStats();
1415
777
  const entriesCleared = statsBefore.entryCount - statsAfter.entryCount;
1416
778
  const sizeFreed = statsBefore.totalSize - statsAfter.totalSize;
1417
- console.log(chalk7.green(`\u2713 Cleared ${entriesCleared} entries (${formatSize2(sizeFreed)} freed)`));
779
+ console.log(chalk10.green(`\u2713 Cleared ${entriesCleared} entries (${formatBytes(sizeFreed)} freed)`));
1418
780
  if (statsAfter.entryCount > 0) {
1419
- console.log(chalk7.dim(` Remaining: ${statsAfter.entryCount} entries (${formatSize2(statsAfter.totalSize)})`));
781
+ console.log(chalk10.dim(` Remaining: ${statsAfter.entryCount} entries (${formatBytes(statsAfter.totalSize)})`));
1420
782
  }
1421
783
  } catch (error) {
1422
- console.error(chalk7.red("Error:"), error.message);
784
+ console.error(chalk10.red("Error:"), error.message);
1423
785
  process.exit(1);
1424
786
  }
1425
787
  });
1426
788
  return cmd;
1427
789
  }
1428
- function formatSize2(bytes) {
1429
- if (bytes < 1024) return `${bytes} B`;
1430
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1431
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1432
- }
1433
790
 
1434
791
  // src/commands/cache/stats.ts
1435
792
  init_esm_shims();
1436
- function formatSize3(bytes) {
1437
- if (bytes < 1024) return `${bytes} B`;
1438
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1439
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1440
- }
1441
793
  function cacheStatsCommand() {
1442
794
  const cmd = new Command("cache-stats");
1443
795
  cmd.description("Display cache statistics").option("--json", "Output as JSON").action(async (options) => {
@@ -1448,25 +800,25 @@ function cacheStatsCommand() {
1448
800
  console.log(JSON.stringify(stats, null, 2));
1449
801
  return;
1450
802
  }
1451
- console.log(chalk7.bold("Cache Statistics\n"));
1452
- console.log(chalk7.bold("Overview"));
1453
- console.log(` Total entries: ${chalk7.cyan(stats.entryCount.toString())}`);
1454
- console.log(` Total size: ${chalk7.cyan(formatSize3(stats.totalSize))}`);
1455
- console.log(` Fresh entries: ${chalk7.green(stats.freshCount.toString())}`);
1456
- console.log(` Stale entries: ${stats.staleCount > 0 ? chalk7.yellow(stats.staleCount.toString()) : chalk7.dim("0")}`);
1457
- console.log(` Expired entries: ${stats.expiredCount > 0 ? chalk7.red(stats.expiredCount.toString()) : chalk7.dim("0")}`);
803
+ console.log(chalk10.bold("Cache Statistics\n"));
804
+ console.log(chalk10.bold("Overview"));
805
+ console.log(` Total entries: ${chalk10.cyan(stats.entryCount.toString())}`);
806
+ console.log(` Total size: ${chalk10.cyan(formatBytes(stats.totalSize))}`);
807
+ console.log(` Fresh entries: ${chalk10.green(stats.freshCount.toString())}`);
808
+ console.log(` Stale entries: ${stats.staleCount > 0 ? chalk10.yellow(stats.staleCount.toString()) : chalk10.dim("0")}`);
809
+ console.log(` Expired entries: ${stats.expiredCount > 0 ? chalk10.red(stats.expiredCount.toString()) : chalk10.dim("0")}`);
1458
810
  console.log("");
1459
811
  const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1460
- const healthColor = healthPercent > 80 ? chalk7.green : healthPercent > 50 ? chalk7.yellow : chalk7.red;
812
+ const healthColor = healthPercent > 80 ? chalk10.green : healthPercent > 50 ? chalk10.yellow : chalk10.red;
1461
813
  console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
1462
814
  if (stats.expiredCount > 0) {
1463
- console.log(chalk7.dim('\nRun "fractary-codex cache-clear --pattern <pattern>" to clean up entries.'));
815
+ console.log(chalk10.dim('\nRun "fractary-codex cache-clear --pattern <pattern>" to clean up entries.'));
1464
816
  }
1465
817
  if (stats.entryCount === 0) {
1466
- console.log(chalk7.dim("\nNo cached entries. Fetch some documents to populate the cache."));
818
+ console.log(chalk10.dim("\nNo cached entries. Fetch some documents to populate the cache."));
1467
819
  }
1468
820
  } catch (error) {
1469
- console.error(chalk7.red("Error:"), error.message);
821
+ console.error(chalk10.red("Error:"), error.message);
1470
822
  process.exit(1);
1471
823
  }
1472
824
  });
@@ -1475,239 +827,81 @@ function cacheStatsCommand() {
1475
827
 
1476
828
  // src/commands/cache/health.ts
1477
829
  init_esm_shims();
1478
- init_migrate_config();
1479
- async function fileExists2(filePath) {
1480
- try {
1481
- await fs4.access(filePath);
1482
- return true;
1483
- } catch {
1484
- return false;
1485
- }
1486
- }
1487
- async function checkConfiguration() {
1488
- const configPath = path3.join(process.cwd(), ".fractary", "config.yaml");
1489
- const legacyConfigPath = path3.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1490
- try {
1491
- if (!await fileExists2(configPath)) {
1492
- if (await fileExists2(legacyConfigPath)) {
1493
- return {
1494
- name: "Configuration",
1495
- status: "warn",
1496
- message: "Legacy JSON configuration detected",
1497
- details: "Migration from legacy JSON format may be required"
1498
- };
1499
- }
1500
- return {
1501
- name: "Configuration",
1502
- status: "fail",
1503
- message: "No configuration found",
1504
- details: 'Run "fractary-codex configure" to create configuration'
1505
- };
1506
- }
1507
- const config = await readCodexConfig(configPath);
1508
- if (!config.organization) {
1509
- return {
1510
- name: "Configuration",
1511
- status: "warn",
1512
- message: "No organization configured",
1513
- details: "Organization slug is required"
1514
- };
1515
- }
1516
- const providerCount = config.storage?.length || 0;
1517
- if (providerCount === 0) {
1518
- return {
1519
- name: "Configuration",
1520
- status: "warn",
1521
- message: "No storage providers configured",
1522
- details: "At least one storage provider is recommended"
1523
- };
1524
- }
1525
- return {
1526
- name: "Configuration",
1527
- status: "pass",
1528
- message: "Valid YAML configuration",
1529
- details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
1530
- };
1531
- } catch (error) {
1532
- return {
1533
- name: "Configuration",
1534
- status: "fail",
1535
- message: "Invalid configuration",
1536
- details: error.message
1537
- };
1538
- }
1539
- }
1540
- async function checkSDKClient() {
1541
- try {
1542
- const client = await getClient();
1543
- const organization = client.getOrganization();
1544
- return {
1545
- name: "SDK Client",
1546
- status: "pass",
1547
- message: "CodexClient initialized successfully",
1548
- details: `Organization: ${organization}`
1549
- };
1550
- } catch (error) {
1551
- return {
1552
- name: "SDK Client",
1553
- status: "fail",
1554
- message: "Failed to initialize CodexClient",
1555
- details: error.message
1556
- };
1557
- }
1558
- }
1559
- async function checkCache() {
1560
- try {
1561
- const client = await getClient();
1562
- const stats = await client.getCacheStats();
1563
- if (stats.entryCount === 0) {
1564
- return {
1565
- name: "Cache",
1566
- status: "warn",
1567
- message: "Cache is empty",
1568
- details: "Fetch some documents to populate cache"
1569
- };
1570
- }
1571
- const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1572
- if (healthPercent < 50) {
1573
- return {
1574
- name: "Cache",
1575
- status: "warn",
1576
- message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1577
- details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
1578
- };
1579
- }
1580
- return {
1581
- name: "Cache",
1582
- status: "pass",
1583
- message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1584
- details: `${formatSize4(stats.totalSize)} total`
1585
- };
1586
- } catch (error) {
1587
- return {
1588
- name: "Cache",
1589
- status: "fail",
1590
- message: "Cache check failed",
1591
- details: error.message
1592
- };
1593
- }
1594
- }
1595
- async function checkStorage() {
1596
- const configPath = path3.join(process.cwd(), ".fractary", "config.yaml");
1597
- try {
1598
- const config = await readCodexConfig(configPath);
1599
- const providers = config.storage || [];
1600
- if (providers.length === 0) {
1601
- return {
1602
- name: "Storage",
1603
- status: "warn",
1604
- message: "No storage providers configured",
1605
- details: "Configure at least one provider in .fractary/config.yaml"
1606
- };
1607
- }
1608
- const providerTypes = providers.map((p) => p.type).join(", ");
1609
- const hasGitHub = providers.some((p) => p.type === "github");
1610
- if (hasGitHub && !process.env.GITHUB_TOKEN) {
1611
- return {
1612
- name: "Storage",
1613
- status: "warn",
1614
- message: `${providers.length} provider(s): ${providerTypes}`,
1615
- details: "GITHUB_TOKEN not set (required for GitHub provider)"
1616
- };
1617
- }
1618
- return {
1619
- name: "Storage",
1620
- status: "pass",
1621
- message: `${providers.length} provider(s): ${providerTypes}`,
1622
- details: "All configured providers available"
1623
- };
1624
- } catch (error) {
1625
- return {
1626
- name: "Storage",
1627
- status: "fail",
1628
- message: "Storage check failed",
1629
- details: error.message
1630
- };
1631
- }
1632
- }
1633
- async function checkTypes() {
1634
- try {
1635
- const client = await getClient();
1636
- const registry = client.getTypeRegistry();
1637
- const allTypes = registry.list();
1638
- const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
1639
- const customCount = allTypes.length - builtinCount;
1640
- return {
1641
- name: "Type Registry",
1642
- status: "pass",
1643
- message: `${allTypes.length} types registered`,
1644
- details: `${builtinCount} built-in, ${customCount} custom`
1645
- };
1646
- } catch (error) {
1647
- return {
1648
- name: "Type Registry",
1649
- status: "fail",
1650
- message: "Type registry check failed",
1651
- details: error.message
1652
- };
1653
- }
1654
- }
1655
- function formatSize4(bytes) {
1656
- if (bytes < 1024) return `${bytes} B`;
1657
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1658
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1659
- }
1660
830
  function cacheHealthCommand() {
1661
831
  const cmd = new Command("cache-health");
1662
832
  cmd.description("Run diagnostics on codex setup").option("--json", "Output as JSON").action(async (options) => {
1663
833
  try {
834
+ const healthChecker = createHealthChecker({
835
+ projectRoot: process.cwd()
836
+ });
1664
837
  const checks = [];
1665
- checks.push(await checkConfiguration());
1666
- checks.push(await checkSDKClient());
1667
- checks.push(await checkCache());
1668
- checks.push(await checkStorage());
1669
- checks.push(await checkTypes());
1670
- const passed = checks.filter((c) => c.status === "pass").length;
1671
- const warned = checks.filter((c) => c.status === "warn").length;
1672
- const failed = checks.filter((c) => c.status === "fail").length;
838
+ checks.push(await healthChecker.checkConfiguration());
839
+ checks.push(await healthChecker.checkStorage());
840
+ try {
841
+ const client = await getClient();
842
+ const organization = client.getOrganization();
843
+ checks.push({
844
+ name: "SDK Client",
845
+ status: "pass",
846
+ message: "CodexClient initialized successfully",
847
+ details: `Organization: ${organization}`
848
+ });
849
+ const cacheStats = await client.getCacheStats();
850
+ checks.push(healthChecker.checkCacheFromStats(cacheStats));
851
+ const registry = client.getTypeRegistry();
852
+ checks.push(healthChecker.checkTypesFromRegistry(registry));
853
+ } catch (error) {
854
+ checks.push({
855
+ name: "SDK Client",
856
+ status: "fail",
857
+ message: "Failed to initialize CodexClient",
858
+ details: error.message
859
+ });
860
+ checks.push({
861
+ name: "Cache",
862
+ status: "warn",
863
+ message: "Cache check skipped",
864
+ details: "SDK client not available"
865
+ });
866
+ checks.push({
867
+ name: "Type Registry",
868
+ status: "warn",
869
+ message: "Type registry check skipped",
870
+ details: "SDK client not available"
871
+ });
872
+ }
873
+ const result = healthChecker.summarize(checks);
1673
874
  if (options.json) {
1674
- console.log(JSON.stringify({
1675
- summary: {
1676
- total: checks.length,
1677
- passed,
1678
- warned,
1679
- failed,
1680
- healthy: failed === 0
1681
- },
1682
- checks
1683
- }, null, 2));
875
+ console.log(JSON.stringify(result, null, 2));
1684
876
  return;
1685
877
  }
1686
- console.log(chalk7.bold("Codex Health Check\n"));
1687
- for (const check of checks) {
1688
- const icon = check.status === "pass" ? chalk7.green("\u2713") : check.status === "warn" ? chalk7.yellow("\u26A0") : chalk7.red("\u2717");
1689
- const statusColor = check.status === "pass" ? chalk7.green : check.status === "warn" ? chalk7.yellow : chalk7.red;
1690
- console.log(`${icon} ${chalk7.bold(check.name)}`);
878
+ console.log(chalk10.bold("Codex Health Check\n"));
879
+ for (const check of result.checks) {
880
+ const icon = check.status === "pass" ? chalk10.green("\u2713") : check.status === "warn" ? chalk10.yellow("\u26A0") : chalk10.red("\u2717");
881
+ const statusColor = check.status === "pass" ? chalk10.green : check.status === "warn" ? chalk10.yellow : chalk10.red;
882
+ console.log(`${icon} ${chalk10.bold(check.name)}`);
1691
883
  console.log(` ${statusColor(check.message)}`);
1692
884
  if (check.details) {
1693
- console.log(` ${chalk7.dim(check.details)}`);
885
+ console.log(` ${chalk10.dim(check.details)}`);
1694
886
  }
1695
887
  console.log("");
1696
888
  }
1697
- console.log(chalk7.dim("\u2500".repeat(60)));
1698
- const overallStatus = failed > 0 ? chalk7.red("UNHEALTHY") : warned > 0 ? chalk7.yellow("DEGRADED") : chalk7.green("HEALTHY");
889
+ console.log(chalk10.dim("\u2500".repeat(60)));
890
+ const overallStatus = result.summary.status === "unhealthy" ? chalk10.red("UNHEALTHY") : result.summary.status === "degraded" ? chalk10.yellow("DEGRADED") : chalk10.green("HEALTHY");
1699
891
  console.log(`Status: ${overallStatus}`);
1700
- console.log(chalk7.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
1701
- if (failed > 0 || warned > 0) {
892
+ console.log(
893
+ chalk10.dim(`${result.summary.passed} passed, ${result.summary.warned} warnings, ${result.summary.failed} failed`)
894
+ );
895
+ if (result.summary.failed > 0 || result.summary.warned > 0) {
1702
896
  console.log("");
1703
- console.log(chalk7.dim("Run checks individually for more details:"));
1704
- console.log(chalk7.dim(" fractary-codex cache-stats"));
897
+ console.log(chalk10.dim("Run checks individually for more details:"));
898
+ console.log(chalk10.dim(" fractary-codex cache-stats"));
1705
899
  }
1706
- if (failed > 0) {
900
+ if (result.summary.failed > 0) {
1707
901
  process.exit(1);
1708
902
  }
1709
903
  } catch (error) {
1710
- console.error(chalk7.red("Error:"), error.message);
904
+ console.error(chalk10.red("Error:"), error.message);
1711
905
  process.exit(1);
1712
906
  }
1713
907
  });
@@ -1716,7 +910,11 @@ function cacheHealthCommand() {
1716
910
 
1717
911
  // src/commands/sync.ts
1718
912
  init_esm_shims();
1719
- init_migrate_config();
913
+
914
+ // src/config/migrate-config.ts
915
+ init_esm_shims();
916
+
917
+ // src/commands/sync.ts
1720
918
  function getEnvironmentBranch(config, env) {
1721
919
  const envMap = config.sync?.environments || {
1722
920
  dev: "develop",
@@ -1726,26 +924,17 @@ function getEnvironmentBranch(config, env) {
1726
924
  };
1727
925
  return envMap[env] || env;
1728
926
  }
1729
- function formatBytes(bytes) {
1730
- if (bytes < 1024) return `${bytes} B`;
1731
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1732
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1733
- }
1734
- function formatDuration(ms) {
1735
- if (ms < 1e3) return `${ms}ms`;
1736
- return `${(ms / 1e3).toFixed(1)}s`;
1737
- }
1738
927
  function syncCommand() {
1739
928
  const cmd = new Command("sync");
1740
929
  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").option("--work-id <id>", "GitHub issue number or URL to scope sync to").action(async (name, options) => {
1741
930
  try {
1742
- const configPath = path3.join(process.cwd(), ".fractary", "config.yaml");
931
+ const configPath = path4.join(process.cwd(), ".fractary", "config.yaml");
1743
932
  let config;
1744
933
  try {
1745
934
  config = await readCodexConfig(configPath);
1746
935
  } catch (error) {
1747
- console.error(chalk7.red("Error:"), "Codex not initialized.");
1748
- console.log(chalk7.dim('Run "fractary-codex configure" first.'));
936
+ console.error(chalk10.red("Error:"), "Codex not initialized.");
937
+ console.log(chalk10.dim('Run "fractary-codex configure" first.'));
1749
938
  process.exit(1);
1750
939
  }
1751
940
  const { createSyncManager, createLocalStorage, detectCurrentProject } = await import('@fractary/codex');
@@ -1758,14 +947,14 @@ function syncCommand() {
1758
947
  projectName = detected.project || void 0;
1759
948
  }
1760
949
  if (!projectName) {
1761
- console.error(chalk7.red("Error:"), "Could not determine project name.");
1762
- console.log(chalk7.dim("Provide project name as argument, set codex.project in config, or run from a git repository."));
950
+ console.error(chalk10.red("Error:"), "Could not determine project name.");
951
+ console.log(chalk10.dim("Provide project name as argument, set codex.project in config, or run from a git repository."));
1763
952
  process.exit(1);
1764
953
  }
1765
954
  const validDirections = ["to-codex", "from-codex", "bidirectional"];
1766
955
  if (!validDirections.includes(options.direction)) {
1767
- console.error(chalk7.red("Error:"), `Invalid direction: ${options.direction}`);
1768
- console.log(chalk7.dim("Valid options: to-codex, from-codex, bidirectional"));
956
+ console.error(chalk10.red("Error:"), `Invalid direction: ${options.direction}`);
957
+ console.log(chalk10.dim("Valid options: to-codex, from-codex, bidirectional"));
1769
958
  process.exit(1);
1770
959
  }
1771
960
  const direction = options.direction;
@@ -1776,7 +965,7 @@ function syncCommand() {
1776
965
  const syncManager = createSyncManager({
1777
966
  localStorage,
1778
967
  config: config.sync,
1779
- manifestPath: path3.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
968
+ manifestPath: path4.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1780
969
  });
1781
970
  const defaultToCodexPatterns = [
1782
971
  "docs/**/*.md",
@@ -1806,13 +995,13 @@ function syncCommand() {
1806
995
  });
1807
996
  matches.forEach((match) => matchedFilePaths.add(match));
1808
997
  } catch (error) {
1809
- console.error(chalk7.yellow(`Warning: Invalid pattern "${pattern}": ${error.message}`));
998
+ console.error(chalk10.yellow(`Warning: Invalid pattern "${pattern}": ${error.message}`));
1810
999
  }
1811
1000
  }
1812
1001
  const targetFiles = await Promise.all(
1813
1002
  Array.from(matchedFilePaths).map(async (filePath) => {
1814
- const fullPath = path3.join(sourceDir, filePath);
1815
- const stats = await import('fs/promises').then((fs8) => fs8.stat(fullPath));
1003
+ const fullPath = path4.join(sourceDir, filePath);
1004
+ const stats = await import('fs/promises').then((fs3) => fs3.stat(fullPath));
1816
1005
  return {
1817
1006
  path: filePath,
1818
1007
  size: stats.size,
@@ -1836,14 +1025,14 @@ function syncCommand() {
1836
1025
  try {
1837
1026
  const { ensureCodexCloned: ensureCodexCloned2 } = await Promise.resolve().then(() => (init_codex_repository(), codex_repository_exports));
1838
1027
  if (!options.json) {
1839
- console.log(chalk7.blue("\u2139 Cloning/updating codex repository..."));
1028
+ console.log(chalk10.blue("\u2139 Cloning/updating codex repository..."));
1840
1029
  }
1841
1030
  codexRepoPath = await ensureCodexCloned2(config, {
1842
1031
  branch: targetBranch
1843
1032
  });
1844
1033
  if (!options.json) {
1845
- console.log(chalk7.dim(` Codex cloned to: ${codexRepoPath}`));
1846
- console.log(chalk7.dim(" Scanning for files routing to this project...\n"));
1034
+ console.log(chalk10.dim(` Codex cloned to: ${codexRepoPath}`));
1035
+ console.log(chalk10.dim(" Scanning for files routing to this project...\n"));
1847
1036
  } else {
1848
1037
  console.log(JSON.stringify({
1849
1038
  info: "Routing-aware sync: cloned codex repository and scanning for files targeting this project",
@@ -1851,29 +1040,29 @@ function syncCommand() {
1851
1040
  }, null, 2));
1852
1041
  }
1853
1042
  } catch (error) {
1854
- console.error(chalk7.red("Error:"), "Failed to clone codex repository");
1855
- console.error(chalk7.dim(` ${error.message}`));
1856
- console.log(chalk7.yellow("\nTroubleshooting:"));
1043
+ console.error(chalk10.red("Error:"), "Failed to clone codex repository");
1044
+ console.error(chalk10.dim(` ${error.message}`));
1045
+ console.log(chalk10.yellow("\nTroubleshooting:"));
1857
1046
  if (error.message.includes("Git command not found")) {
1858
- console.log(chalk7.dim(" Git is not installed or not in PATH."));
1859
- console.log(chalk7.dim(" Install git: https://git-scm.com/downloads"));
1047
+ console.log(chalk10.dim(" Git is not installed or not in PATH."));
1048
+ console.log(chalk10.dim(" Install git: https://git-scm.com/downloads"));
1860
1049
  } else if (error.message.includes("authentication failed") || error.message.includes("Authentication failed")) {
1861
- console.log(chalk7.dim(" GitHub authentication is required for private repositories."));
1862
- console.log(chalk7.dim(" 1. Check auth status: gh auth status"));
1863
- console.log(chalk7.dim(" 2. Login if needed: gh auth login"));
1864
- console.log(chalk7.dim(" 3. Or set GITHUB_TOKEN environment variable"));
1050
+ console.log(chalk10.dim(" GitHub authentication is required for private repositories."));
1051
+ console.log(chalk10.dim(" 1. Check auth status: gh auth status"));
1052
+ console.log(chalk10.dim(" 2. Login if needed: gh auth login"));
1053
+ console.log(chalk10.dim(" 3. Or set GITHUB_TOKEN environment variable"));
1865
1054
  } else if (error.message.includes("Permission denied")) {
1866
- console.log(chalk7.dim(" Permission denied accessing repository files."));
1867
- console.log(chalk7.dim(" 1. Check file/directory permissions"));
1868
- console.log(chalk7.dim(" 2. Ensure you have access to the repository"));
1055
+ console.log(chalk10.dim(" Permission denied accessing repository files."));
1056
+ console.log(chalk10.dim(" 1. Check file/directory permissions"));
1057
+ console.log(chalk10.dim(" 2. Ensure you have access to the repository"));
1869
1058
  } else if (error.message.includes("not found") || error.message.includes("does not exist")) {
1870
- console.log(chalk7.dim(` Repository not found: ${config.organization}/${config.codex_repo || "codex"}`));
1871
- console.log(chalk7.dim(" 1. Verify the repository exists on GitHub"));
1872
- console.log(chalk7.dim(" 2. Check organization and repository names in config"));
1059
+ console.log(chalk10.dim(` Repository not found: ${config.organization}/${config.codex_repo || "codex"}`));
1060
+ console.log(chalk10.dim(" 1. Verify the repository exists on GitHub"));
1061
+ console.log(chalk10.dim(" 2. Check organization and repository names in config"));
1873
1062
  } else {
1874
- console.log(chalk7.dim(" 1. Ensure git is installed: git --version"));
1875
- console.log(chalk7.dim(" 2. Check GitHub auth: gh auth status"));
1876
- console.log(chalk7.dim(` 3. Verify repo exists: ${config.organization}/${config.codex_repo || "codex"}`));
1063
+ console.log(chalk10.dim(" 1. Ensure git is installed: git --version"));
1064
+ console.log(chalk10.dim(" 2. Check GitHub auth: gh auth status"));
1065
+ console.log(chalk10.dim(` 3. Verify repo exists: ${config.organization}/${config.codex_repo || "codex"}`));
1877
1066
  }
1878
1067
  process.exit(1);
1879
1068
  }
@@ -1892,17 +1081,17 @@ function syncCommand() {
1892
1081
  try {
1893
1082
  const { ensureCodexCloned: ensureCodexCloned2 } = await Promise.resolve().then(() => (init_codex_repository(), codex_repository_exports));
1894
1083
  if (!options.json) {
1895
- console.log(chalk7.blue("\u2139 Cloning/updating codex repository..."));
1084
+ console.log(chalk10.blue("\u2139 Cloning/updating codex repository..."));
1896
1085
  }
1897
1086
  codexRepoPath = await ensureCodexCloned2(config, {
1898
1087
  branch: targetBranch
1899
1088
  });
1900
1089
  if (!options.json) {
1901
- console.log(chalk7.dim(` Codex cloned to: ${codexRepoPath}`));
1090
+ console.log(chalk10.dim(` Codex cloned to: ${codexRepoPath}`));
1902
1091
  }
1903
1092
  } catch (error) {
1904
- console.error(chalk7.red("Error:"), "Failed to clone codex repository");
1905
- console.error(chalk7.dim(` ${error.message}`));
1093
+ console.error(chalk10.red("Error:"), "Failed to clone codex repository");
1094
+ console.error(chalk10.dim(` ${error.message}`));
1906
1095
  process.exit(1);
1907
1096
  }
1908
1097
  plan = await syncManager.createPlan(
@@ -1914,7 +1103,7 @@ function syncCommand() {
1914
1103
  syncOptions
1915
1104
  );
1916
1105
  plan.source = sourceDir;
1917
- plan.target = path3.join(codexRepoPath, "projects", projectName);
1106
+ plan.target = path4.join(codexRepoPath, "projects", projectName);
1918
1107
  }
1919
1108
  if (plan.totalFiles === 0) {
1920
1109
  if (options.json) {
@@ -1928,7 +1117,7 @@ function syncCommand() {
1928
1117
  message: "No files to sync"
1929
1118
  }, null, 2));
1930
1119
  } else {
1931
- console.log(chalk7.yellow("No files to sync."));
1120
+ console.log(chalk10.yellow("No files to sync."));
1932
1121
  }
1933
1122
  return;
1934
1123
  }
@@ -1984,81 +1173,81 @@ function syncCommand() {
1984
1173
  }, null, 2));
1985
1174
  return;
1986
1175
  }
1987
- console.log(chalk7.bold("Sync Plan\n"));
1988
- console.log(` Project: ${chalk7.cyan(projectName)}`);
1989
- console.log(` Organization: ${chalk7.cyan(config.organization)}`);
1990
- console.log(` Environment: ${chalk7.cyan(options.env)} (${targetBranch})`);
1991
- console.log(` Direction: ${chalk7.cyan(direction)}`);
1992
- console.log(` Files: ${chalk7.cyan(plan.totalFiles.toString())}`);
1993
- console.log(` Total size: ${chalk7.cyan(formatBytes(plan.totalBytes))}`);
1176
+ console.log(chalk10.bold("Sync Plan\n"));
1177
+ console.log(` Project: ${chalk10.cyan(projectName)}`);
1178
+ console.log(` Organization: ${chalk10.cyan(config.organization)}`);
1179
+ console.log(` Environment: ${chalk10.cyan(options.env)} (${targetBranch})`);
1180
+ console.log(` Direction: ${chalk10.cyan(direction)}`);
1181
+ console.log(` Files: ${chalk10.cyan(plan.totalFiles.toString())}`);
1182
+ console.log(` Total size: ${chalk10.cyan(formatBytes(plan.totalBytes))}`);
1994
1183
  if (plan.estimatedTime) {
1995
- console.log(` Est. time: ${chalk7.dim(formatDuration(plan.estimatedTime))}`);
1184
+ console.log(` Est. time: ${chalk10.dim(formatDuration(plan.estimatedTime))}`);
1996
1185
  }
1997
1186
  if (routingScan) {
1998
1187
  console.log("");
1999
- console.log(chalk7.bold("Routing Statistics\n"));
2000
- console.log(` Scanned: ${chalk7.cyan(routingScan.stats.totalScanned.toString())} files`);
2001
- console.log(` Matched: ${chalk7.cyan(routingScan.stats.totalMatched.toString())} files`);
2002
- console.log(` Source projects: ${chalk7.cyan(routingScan.stats.sourceProjects.length.toString())}`);
1188
+ console.log(chalk10.bold("Routing Statistics\n"));
1189
+ console.log(` Scanned: ${chalk10.cyan(routingScan.stats.totalScanned.toString())} files`);
1190
+ console.log(` Matched: ${chalk10.cyan(routingScan.stats.totalMatched.toString())} files`);
1191
+ console.log(` Source projects: ${chalk10.cyan(routingScan.stats.sourceProjects.length.toString())}`);
2003
1192
  if (routingScan.stats.sourceProjects.length > 0) {
2004
- console.log(chalk7.dim(` ${routingScan.stats.sourceProjects.slice(0, 5).join(", ")}`));
1193
+ console.log(chalk10.dim(` ${routingScan.stats.sourceProjects.slice(0, 5).join(", ")}`));
2005
1194
  if (routingScan.stats.sourceProjects.length > 5) {
2006
- console.log(chalk7.dim(` ... and ${routingScan.stats.sourceProjects.length - 5} more`));
1195
+ console.log(chalk10.dim(` ... and ${routingScan.stats.sourceProjects.length - 5} more`));
2007
1196
  }
2008
1197
  }
2009
- console.log(` Scan time: ${chalk7.dim(formatDuration(routingScan.stats.durationMs))}`);
1198
+ console.log(` Scan time: ${chalk10.dim(formatDuration(routingScan.stats.durationMs))}`);
2010
1199
  }
2011
1200
  console.log("");
2012
1201
  if (plan.conflicts.length > 0) {
2013
- console.log(chalk7.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
1202
+ console.log(chalk10.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
2014
1203
  for (const conflict of plan.conflicts.slice(0, 5)) {
2015
- console.log(chalk7.yellow(` \u2022 ${conflict.path}`));
1204
+ console.log(chalk10.yellow(` \u2022 ${conflict.path}`));
2016
1205
  }
2017
1206
  if (plan.conflicts.length > 5) {
2018
- console.log(chalk7.dim(` ... and ${plan.conflicts.length - 5} more`));
1207
+ console.log(chalk10.dim(` ... and ${plan.conflicts.length - 5} more`));
2019
1208
  }
2020
1209
  console.log("");
2021
1210
  }
2022
1211
  if (plan.skipped.length > 0) {
2023
- console.log(chalk7.dim(`${plan.skipped.length} files skipped (no changes)`));
1212
+ console.log(chalk10.dim(`${plan.skipped.length} files skipped (no changes)`));
2024
1213
  console.log("");
2025
1214
  }
2026
1215
  if (options.dryRun) {
2027
- console.log(chalk7.blue("Dry run - would sync:\n"));
1216
+ console.log(chalk10.blue("Dry run - would sync:\n"));
2028
1217
  const filesToShow = plan.files.slice(0, 10);
2029
1218
  for (const file of filesToShow) {
2030
1219
  const arrow = direction === "to-codex" ? "\u2192" : direction === "from-codex" ? "\u2190" : "\u2194";
2031
- const opColor = file.operation === "create" ? chalk7.green : file.operation === "update" ? chalk7.yellow : chalk7.dim;
1220
+ const opColor = file.operation === "create" ? chalk10.green : file.operation === "update" ? chalk10.yellow : chalk10.dim;
2032
1221
  console.log(
2033
- chalk7.dim(` ${arrow}`),
1222
+ chalk10.dim(` ${arrow}`),
2034
1223
  opColor(file.operation.padEnd(7)),
2035
1224
  file.path,
2036
- chalk7.dim(`(${formatBytes(file.size || 0)})`)
1225
+ chalk10.dim(`(${formatBytes(file.size || 0)})`)
2037
1226
  );
2038
1227
  }
2039
1228
  if (plan.files.length > 10) {
2040
- console.log(chalk7.dim(` ... and ${plan.files.length - 10} more files`));
1229
+ console.log(chalk10.dim(` ... and ${plan.files.length - 10} more files`));
2041
1230
  }
2042
- console.log(chalk7.dim(`
1231
+ console.log(chalk10.dim(`
2043
1232
  Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
2044
- console.log(chalk7.dim("Run without --dry-run to execute sync."));
1233
+ console.log(chalk10.dim("Run without --dry-run to execute sync."));
2045
1234
  return;
2046
1235
  }
2047
- console.log(chalk7.blue("Syncing...\n"));
1236
+ console.log(chalk10.blue("Syncing...\n"));
2048
1237
  const startTime = Date.now();
2049
1238
  const result = await syncManager.executePlan(plan, syncOptions);
2050
1239
  const duration = Date.now() - startTime;
2051
1240
  if (direction === "to-codex" && codexRepoPath && result.synced > 0) {
2052
1241
  try {
2053
1242
  if (!options.json) {
2054
- console.log(chalk7.blue("Committing and pushing to codex..."));
1243
+ console.log(chalk10.blue("Committing and pushing to codex..."));
2055
1244
  }
2056
1245
  const { RepoManager } = await import('@fractary/core/repo');
2057
1246
  const repoManager = new RepoManager({ platform: "github" }, codexRepoPath);
2058
1247
  repoManager.stageAll();
2059
1248
  if (repoManager.isClean()) {
2060
1249
  if (!options.json) {
2061
- console.log(chalk7.dim(" No changes to push - codex is already up to date"));
1250
+ console.log(chalk10.dim(" No changes to push - codex is already up to date"));
2062
1251
  }
2063
1252
  } else {
2064
1253
  repoManager.commit({
@@ -2066,41 +1255,41 @@ Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
2066
1255
  });
2067
1256
  repoManager.push({});
2068
1257
  if (!options.json) {
2069
- console.log(chalk7.dim(" Changes pushed to codex repository"));
1258
+ console.log(chalk10.dim(" Changes pushed to codex repository"));
2070
1259
  }
2071
1260
  }
2072
1261
  } catch (error) {
2073
- console.error(chalk7.red("Error pushing to codex:"), error.message);
1262
+ console.error(chalk10.red("Error pushing to codex:"), error.message);
2074
1263
  }
2075
1264
  }
2076
1265
  console.log("");
2077
1266
  if (result.success) {
2078
- console.log(chalk7.green(`\u2713 Sync completed successfully`));
2079
- console.log(chalk7.dim(` Synced: ${result.synced} files`));
1267
+ console.log(chalk10.green(`\u2713 Sync completed successfully`));
1268
+ console.log(chalk10.dim(` Synced: ${result.synced} files`));
2080
1269
  if (result.skipped > 0) {
2081
- console.log(chalk7.dim(` Skipped: ${result.skipped} files`));
1270
+ console.log(chalk10.dim(` Skipped: ${result.skipped} files`));
2082
1271
  }
2083
- console.log(chalk7.dim(` Duration: ${formatDuration(duration)}`));
1272
+ console.log(chalk10.dim(` Duration: ${formatDuration(duration)}`));
2084
1273
  } else {
2085
- console.log(chalk7.yellow(`\u26A0 Sync completed with errors`));
2086
- console.log(chalk7.green(` Synced: ${result.synced} files`));
2087
- console.log(chalk7.red(` Failed: ${result.failed} files`));
1274
+ console.log(chalk10.yellow(`\u26A0 Sync completed with errors`));
1275
+ console.log(chalk10.green(` Synced: ${result.synced} files`));
1276
+ console.log(chalk10.red(` Failed: ${result.failed} files`));
2088
1277
  if (result.skipped > 0) {
2089
- console.log(chalk7.dim(` Skipped: ${result.skipped} files`));
1278
+ console.log(chalk10.dim(` Skipped: ${result.skipped} files`));
2090
1279
  }
2091
1280
  if (result.errors.length > 0) {
2092
1281
  console.log("");
2093
- console.log(chalk7.red("Errors:"));
1282
+ console.log(chalk10.red("Errors:"));
2094
1283
  for (const error of result.errors.slice(0, 5)) {
2095
- console.log(chalk7.red(` \u2022 ${error.path}: ${error.error}`));
1284
+ console.log(chalk10.red(` \u2022 ${error.path}: ${error.error}`));
2096
1285
  }
2097
1286
  if (result.errors.length > 5) {
2098
- console.log(chalk7.dim(` ... and ${result.errors.length - 5} more errors`));
1287
+ console.log(chalk10.dim(` ... and ${result.errors.length - 5} more errors`));
2099
1288
  }
2100
1289
  }
2101
1290
  }
2102
1291
  } catch (error) {
2103
- console.error(chalk7.red("Error:"), error.message);
1292
+ console.error(chalk10.red("Error:"), error.message);
2104
1293
  process.exit(1);
2105
1294
  }
2106
1295
  });
@@ -2113,7 +1302,9 @@ var VERSION = packageJson.version;
2113
1302
  function createCLI() {
2114
1303
  const program = new Command("fractary-codex");
2115
1304
  program.description("Centralized knowledge management and distribution for AI agents").version(VERSION);
2116
- program.addCommand(configureCommand());
1305
+ program.addCommand(configInitializeCommand());
1306
+ program.addCommand(configUpdateCommand());
1307
+ program.addCommand(configValidateCommand());
2117
1308
  program.addCommand(documentFetchCommand());
2118
1309
  program.addCommand(cacheListCommand());
2119
1310
  program.addCommand(cacheClearCommand());