@fractary/codex-cli 0.10.15 → 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 path5 from 'path';
2
+ import * as path4 from 'path';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import * as fs from 'fs/promises';
6
- import * as yaml2 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 fs.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 fs.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 fs.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 = path5.dirname(outputPath);
165
- await fs.mkdir(dir, { recursive: true });
166
- const yamlContent = yaml2.dump(config, {
167
- indent: 2,
168
- lineWidth: 80,
169
- noRefs: true,
170
- sortKeys: false
171
- });
172
- await fs.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 = path5.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 init" 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 path5.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 = path5.join(repoPath, ".git");
579
- const stats = await fs.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 = path5.dirname(targetPath);
612
- await fs.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 fs.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 fs.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
  }
@@ -681,85 +217,162 @@ var init_codex_repository = __esm({
681
217
  // src/cli.ts
682
218
  init_esm_shims();
683
219
 
684
- // src/commands/document/index.ts
685
- init_esm_shims();
686
-
687
- // src/commands/document/fetch.ts
220
+ // src/commands/config/index.ts
688
221
  init_esm_shims();
689
222
 
690
- // src/client/get-client.ts
223
+ // src/commands/config/initialize.ts
691
224
  init_esm_shims();
692
- var clientInstance = null;
693
- async function getClient(options) {
694
- if (!clientInstance) {
695
- const { CodexClient: CodexClient2 } = await Promise.resolve().then(() => (init_codex_client(), codex_client_exports));
696
- clientInstance = await CodexClient2.create(options);
697
- }
698
- return clientInstance;
699
- }
700
-
701
- // src/commands/document/fetch.ts
702
- function hashContent(content) {
703
- return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
704
- }
705
- function fetchCommand() {
706
- const cmd = new Command("fetch");
707
- cmd.description("Fetch a document by codex:// URI reference").argument("<uri>", "Codex URI (e.g., codex://org/project/docs/file.md)").option("--bypass-cache", "Skip cache and fetch directly from source").option("--ttl <seconds>", "Override default TTL (in seconds)", parseInt).option("--json", "Output as JSON with metadata").option("--output <file>", "Write content to file instead of stdout").action(async (uri, options) => {
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) => {
708
228
  try {
709
- const { validateUri } = await import('@fractary/codex');
710
- if (!validateUri(uri)) {
711
- console.error(chalk7.red("Error: Invalid URI format"));
712
- console.log(chalk7.dim("Expected: codex://org/project/path/to/file.md"));
713
- console.log(chalk7.dim("Example: codex://fractary/codex/docs/api.md"));
229
+ const configManager = createConfigManager(process.cwd());
230
+ if (!options.json) {
231
+ console.log(chalk10.blue("Initializing codex configuration...\n"));
232
+ }
233
+ let org = options.org;
234
+ if (!org) {
235
+ org = await configManager.detectOrganization();
236
+ }
237
+ if (!org) {
238
+ try {
239
+ org = resolveOrganization({
240
+ repoName: path4.basename(process.cwd())
241
+ });
242
+ } catch {
243
+ }
244
+ }
245
+ if (!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)}
253
+ `));
254
+ }
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
+ }
714
262
  process.exit(1);
715
263
  }
716
- const client = await getClient();
717
- if (!options.json && !options.bypassCache) {
718
- console.error(chalk7.dim(`Fetching ${uri}...`));
264
+ let project = options.project;
265
+ if (!project) {
266
+ project = configManager.detectProject();
267
+ if (!options.json) {
268
+ console.log(chalk10.dim(`Project: ${chalk10.cyan(project)}
269
+ `));
270
+ }
719
271
  }
720
- const result = await client.fetch(uri, {
721
- bypassCache: options.bypassCache,
722
- ttl: options.ttl
723
- });
724
- if (options.json) {
725
- const output = {
726
- uri,
727
- content: result.content.toString("utf-8"),
728
- metadata: {
729
- fromCache: result.fromCache,
730
- fetchedAt: result.metadata?.fetchedAt,
731
- expiresAt: result.metadata?.expiresAt,
732
- contentLength: result.metadata?.contentLength || result.content.length,
733
- contentHash: hashContent(result.content)
272
+ let codexRepo = options.codexRepo;
273
+ if (codexRepo) {
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);
734
280
  }
735
- };
736
- console.log(JSON.stringify(output, null, 2));
737
- } else if (options.output) {
738
- await fs.writeFile(options.output, result.content);
739
- console.log(chalk7.green("\u2713"), `Written to ${options.output}`);
740
- console.log(chalk7.dim(` Size: ${result.content.length} bytes`));
741
- if (result.fromCache) {
742
- console.log(chalk7.dim(" Source: cache"));
743
- } else {
744
- console.log(chalk7.dim(" Source: storage"));
281
+ process.exit(1);
282
+ }
283
+ if (!options.json) {
284
+ console.log(chalk10.dim(`Codex repository: ${chalk10.cyan(codexRepo)}
285
+ `));
745
286
  }
746
287
  } else {
747
- if (result.fromCache && !options.bypassCache) {
748
- console.error(chalk7.green("\u2713"), chalk7.dim("from cache\n"));
749
- } else {
750
- console.error(chalk7.green("\u2713"), chalk7.dim("fetched\n"));
288
+ const discoveryResult = await configManager.discoverCodexRepo(org);
289
+ if (discoveryResult.repo) {
290
+ codexRepo = discoveryResult.repo;
291
+ if (!options.json) {
292
+ console.log(chalk10.dim(`Codex repository: ${chalk10.cyan(codexRepo)} (auto-discovered)
293
+ `));
294
+ }
295
+ } else if (!options.json) {
296
+ if (discoveryResult.message) {
297
+ console.log(chalk10.dim(` Note: ${discoveryResult.message}
298
+ `));
299
+ }
751
300
  }
752
- console.log(result.content.toString("utf-8"));
753
301
  }
302
+ if (!codexRepo) {
303
+ codexRepo = `codex.${org}.com`;
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)}
307
+ `));
308
+ }
309
+ }
310
+ const result = await configManager.initializeCodexSection({
311
+ organization: org,
312
+ project,
313
+ codexRepo,
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)"));
360
+ }
361
+ }
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"));
754
371
  } catch (error) {
755
- console.error(chalk7.red("Error:"), error.message);
756
- if (error.message.includes("Failed to load configuration")) {
757
- console.log(chalk7.dim('\nRun "fractary codex init" to create a configuration.'));
758
- } else if (error.message.includes("GITHUB_TOKEN")) {
759
- console.log(chalk7.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
760
- } else if (error.message.includes("not found") || error.message.includes("404")) {
761
- console.log(chalk7.dim("\nThe document may not exist or you may not have access."));
762
- console.log(chalk7.dim("Check the URI and ensure your storage providers are configured correctly."));
372
+ if (options.json) {
373
+ console.log(JSON.stringify({ error: error.message }, null, 2));
374
+ } else {
375
+ console.error(chalk10.red("Error:"), error.message);
763
376
  }
764
377
  process.exit(1);
765
378
  }
@@ -767,612 +380,345 @@ function fetchCommand() {
767
380
  return cmd;
768
381
  }
769
382
 
770
- // src/commands/document/index.ts
771
- function documentCommand() {
772
- const cmd = new Command("document");
773
- cmd.description("Manage document operations");
774
- cmd.addCommand(fetchCommand());
775
- return cmd;
776
- }
777
-
778
- // src/commands/config/index.ts
779
- init_esm_shims();
780
-
781
- // src/commands/config/init.ts
782
- init_esm_shims();
783
-
784
- // src/config/unified-config.ts
383
+ // src/commands/config/update.ts
785
384
  init_esm_shims();
786
- function sanitizeForS3BucketName(name) {
787
- return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-").substring(0, 63);
788
- }
789
- function getDefaultUnifiedConfig(organization, project, codexRepo) {
790
- const sanitizedProject = sanitizeForS3BucketName(project);
791
- return {
792
- file: {
793
- schema_version: CONFIG_SCHEMA_VERSION,
794
- sources: {
795
- specs: {
796
- type: "s3",
797
- bucket: `${sanitizedProject}-files`,
798
- prefix: "specs/",
799
- region: "us-east-1",
800
- local: {
801
- base_path: ".fractary/specs"
802
- },
803
- push: {
804
- compress: false,
805
- keep_local: true
806
- },
807
- auth: {
808
- profile: "default"
809
- }
810
- },
811
- logs: {
812
- type: "s3",
813
- bucket: `${sanitizedProject}-files`,
814
- prefix: "logs/",
815
- region: "us-east-1",
816
- local: {
817
- base_path: ".fractary/logs"
818
- },
819
- push: {
820
- compress: true,
821
- keep_local: true
822
- },
823
- auth: {
824
- profile: "default"
825
- }
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");
826
415
  }
416
+ process.exit(1);
417
+ }
418
+ if (!options.json) {
419
+ console.log(chalk10.blue("Updating codex configuration...\n"));
827
420
  }
828
- },
829
- codex: {
830
- schema_version: CONFIG_SCHEMA_VERSION,
831
- organization,
832
- project,
833
- codex_repo: codexRepo,
834
- remotes: {
835
- // The codex repository - uses same token as git operations
836
- [`${organization}/${codexRepo}`]: {
837
- token: "${GITHUB_TOKEN}"
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)"));
838
449
  }
839
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);
840
459
  }
841
- };
842
- }
843
- async function readUnifiedConfig(configPath) {
844
- try {
845
- const content = await fs.readFile(configPath, "utf-8");
846
- const config = yaml2.load(content);
847
- return config;
848
- } catch (error) {
849
- if (error.code === "ENOENT") {
850
- return null;
851
- }
852
- throw error;
853
- }
854
- }
855
- async function writeUnifiedConfig(config, outputPath) {
856
- const dir = path5.dirname(outputPath);
857
- await fs.mkdir(dir, { recursive: true });
858
- const yamlContent = yaml2.dump(config, {
859
- indent: 2,
860
- lineWidth: 120,
861
- noRefs: true,
862
- sortKeys: false
863
460
  });
864
- await fs.writeFile(outputPath, yamlContent, "utf-8");
865
- }
866
- function mergeUnifiedConfigs(existing, updates) {
867
- const merged = {};
868
- if (updates.file || existing.file) {
869
- merged.file = {
870
- schema_version: updates.file?.schema_version || existing.file?.schema_version || CONFIG_SCHEMA_VERSION,
871
- sources: {
872
- ...existing.file?.sources || {},
873
- ...updates.file?.sources || {}
874
- }
875
- };
876
- }
877
- if (updates.codex || existing.codex) {
878
- merged.codex = {
879
- schema_version: updates.codex?.schema_version || existing.codex?.schema_version || CONFIG_SCHEMA_VERSION,
880
- organization: updates.codex?.organization || existing.codex?.organization || "default",
881
- project: updates.codex?.project || existing.codex?.project || "default",
882
- codex_repo: updates.codex?.codex_repo || existing.codex?.codex_repo || "",
883
- remotes: {
884
- ...existing.codex?.remotes || {},
885
- ...updates.codex?.remotes || {}
886
- }
887
- };
888
- }
889
- return merged;
890
- }
891
- async function initializeUnifiedConfig(configPath, organization, project, codexRepo, options) {
892
- const existingConfig = await readUnifiedConfig(configPath);
893
- if (existingConfig && !options?.force) {
894
- const defaultConfig = getDefaultUnifiedConfig(organization, project, codexRepo);
895
- const merged = mergeUnifiedConfigs(existingConfig, defaultConfig);
896
- await writeUnifiedConfig(merged, configPath);
897
- return {
898
- created: false,
899
- merged: true,
900
- config: merged
901
- };
902
- }
903
- const config = getDefaultUnifiedConfig(organization, project, codexRepo);
904
- await writeUnifiedConfig(config, configPath);
905
- return {
906
- created: true,
907
- merged: false,
908
- config
909
- };
461
+ return cmd;
910
462
  }
911
463
 
912
- // src/config/gitignore-utils.ts
464
+ // src/commands/config/validate.ts
913
465
  init_esm_shims();
914
- var DEFAULT_FRACTARY_GITIGNORE = `# .fractary/.gitignore
915
- # This file is managed by multiple plugins - each plugin manages its own section
916
-
917
- # ===== fractary-codex (managed) =====
918
- codex/cache/
919
- # ===== end fractary-codex =====
920
- `;
921
- async function readFractaryGitignore(projectRoot) {
922
- const gitignorePath = path5.join(projectRoot, ".fractary", ".gitignore");
923
- try {
924
- return await fs.readFile(gitignorePath, "utf-8");
925
- } catch (error) {
926
- if (error.code === "ENOENT") {
927
- return null;
928
- }
929
- throw error;
930
- }
931
- }
932
- async function writeFractaryGitignore(projectRoot, content) {
933
- const gitignorePath = path5.join(projectRoot, ".fractary", ".gitignore");
934
- await fs.mkdir(path5.join(projectRoot, ".fractary"), { recursive: true });
935
- await fs.writeFile(gitignorePath, content, "utf-8");
936
- }
937
- function normalizeCachePath(cachePath) {
938
- let normalized = cachePath.replace(/\\/g, "/");
939
- normalized = normalized.replace(/^\.fractary\//, "");
940
- if (!normalized.endsWith("/")) {
941
- normalized += "/";
942
- }
943
- return normalized;
944
- }
945
- function isCachePathIgnored(gitignoreContent, cachePath) {
946
- const normalized = normalizeCachePath(cachePath);
947
- const lines = gitignoreContent.split("\n").map((l) => l.trim());
948
- return lines.some((line) => {
949
- if (line.startsWith("#") || line === "") return false;
950
- let normalizedLine = line.replace(/\\/g, "/");
951
- if (!normalizedLine.endsWith("/")) {
952
- normalizedLine += "/";
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);
953
513
  }
954
- return normalizedLine === normalized;
955
514
  });
956
- }
957
- function addCachePathToGitignore(gitignoreContent, cachePath, comment) {
958
- const normalized = normalizeCachePath(cachePath);
959
- if (isCachePathIgnored(gitignoreContent, cachePath)) {
960
- return gitignoreContent;
961
- }
962
- let addition = "";
963
- {
964
- addition += `
965
- # ${comment}
966
- `;
967
- }
968
- addition += normalized + "\n";
969
- return gitignoreContent.trimEnd() + addition;
970
- }
971
- async function ensureCachePathIgnored(projectRoot, cachePath) {
972
- const gitignorePath = path5.join(projectRoot, ".fractary", ".gitignore");
973
- let relativeCachePath = cachePath;
974
- if (path5.isAbsolute(cachePath)) {
975
- relativeCachePath = path5.relative(path5.join(projectRoot, ".fractary"), cachePath);
976
- }
977
- relativeCachePath = normalizeCachePath(relativeCachePath);
978
- let content = await readFractaryGitignore(projectRoot);
979
- const gitignoreExists = content !== null;
980
- if (!gitignoreExists) {
981
- content = DEFAULT_FRACTARY_GITIGNORE;
982
- if (!isCachePathIgnored(content, relativeCachePath)) {
983
- content = addCachePathToGitignore(content, relativeCachePath, "Custom cache directory");
984
- }
985
- await writeFractaryGitignore(projectRoot, content);
986
- return {
987
- created: true,
988
- updated: false,
989
- alreadyIgnored: false,
990
- gitignorePath
991
- };
992
- }
993
- if (isCachePathIgnored(content, relativeCachePath)) {
994
- return {
995
- created: false,
996
- updated: false,
997
- alreadyIgnored: true,
998
- gitignorePath
999
- };
1000
- }
1001
- content = addCachePathToGitignore(content, relativeCachePath, "Custom cache directory");
1002
- await writeFractaryGitignore(projectRoot, content);
1003
- return {
1004
- created: false,
1005
- updated: true,
1006
- alreadyIgnored: false,
1007
- gitignorePath
1008
- };
515
+ return cmd;
1009
516
  }
1010
517
 
1011
518
  // src/commands/config/init.ts
1012
- function validateNameFormat(name, type) {
1013
- if (!name || typeof name !== "string") {
1014
- throw new Error(`${type} name is required`);
1015
- }
1016
- if (name.length > 100) {
1017
- throw new Error(`${type} name too long (max 100 characters)`);
1018
- }
1019
- const safePattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
1020
- if (!safePattern.test(name)) {
1021
- throw new Error(
1022
- `Invalid ${type} name format: "${name}". Must start with alphanumeric and contain only: a-z, A-Z, 0-9, ., -, _`
1023
- );
1024
- }
1025
- }
1026
- async function getOrgFromGitRemote() {
1027
- try {
1028
- const { execSync } = __require("child_process");
1029
- const remote = execSync("git remote get-url origin 2>/dev/null", { encoding: "utf-8" }).trim();
1030
- const sshMatch = remote.match(/git@github\.com:([^/]+)\//);
1031
- const httpsMatch = remote.match(/github\.com\/([^/]+)\//);
1032
- return sshMatch?.[1] || httpsMatch?.[1] || null;
1033
- } catch {
1034
- return null;
1035
- }
1036
- }
1037
- async function discoverCodexRepo(org) {
1038
- try {
1039
- validateNameFormat(org, "organization");
1040
- } catch (error) {
1041
- return { repo: null, error: "unknown", message: error.message };
1042
- }
1043
- try {
1044
- const { execSync } = __require("child_process");
1045
- try {
1046
- execSync("gh --version", { encoding: "utf-8", stdio: "pipe" });
1047
- } catch {
1048
- return {
1049
- repo: null,
1050
- error: "gh_not_installed",
1051
- message: "GitHub CLI (gh) is not installed. Install from https://cli.github.com/"
1052
- };
1053
- }
1054
- try {
1055
- execSync("gh auth status", { encoding: "utf-8", stdio: "pipe" });
1056
- } catch {
1057
- return {
1058
- repo: null,
1059
- error: "auth_failed",
1060
- message: "GitHub CLI not authenticated. Run: gh auth login"
1061
- };
1062
- }
1063
- const result = execSync(
1064
- `gh repo list ${org} --json name --jq '.[].name | select(startswith("codex."))' 2>&1`,
1065
- { encoding: "utf-8" }
1066
- ).trim();
1067
- if (result.includes("Could not resolve to an Organization") || result.includes("Not Found")) {
1068
- return {
1069
- repo: null,
1070
- error: "org_not_found",
1071
- message: `Organization '${org}' not found on GitHub`
1072
- };
1073
- }
1074
- const repos = result.split("\n").filter(Boolean);
1075
- if (repos.length === 0) {
1076
- return {
1077
- repo: null,
1078
- error: "no_repos_found",
1079
- message: `No codex.* repositories found in organization '${org}'`
1080
- };
1081
- }
1082
- return { repo: repos[0] };
1083
- } catch (error) {
1084
- return {
1085
- repo: null,
1086
- error: "unknown",
1087
- message: error.message || "Unknown error during discovery"
1088
- };
1089
- }
1090
- }
1091
- async function fileExists(filePath) {
1092
- try {
1093
- await fs.access(filePath);
1094
- return true;
1095
- } catch {
1096
- return false;
519
+ init_esm_shims();
520
+
521
+ // src/commands/document/index.ts
522
+ init_esm_shims();
523
+
524
+ // src/commands/document/fetch.ts
525
+ init_esm_shims();
526
+
527
+ // src/client/get-client.ts
528
+ init_esm_shims();
529
+ var clientInstance = null;
530
+ async function getClient(options) {
531
+ if (!clientInstance) {
532
+ const { CodexClient: CodexClient2 } = await Promise.resolve().then(() => (init_codex_client(), codex_client_exports));
533
+ clientInstance = await CodexClient2.create(options);
1097
534
  }
535
+ return clientInstance;
1098
536
  }
1099
- async function installMcpServer(projectRoot, configPath = ".fractary/config.yaml", options = {}) {
1100
- const mcpJsonPath = path5.join(projectRoot, ".mcp.json");
1101
- const { backup = true } = options;
1102
- let existingConfig = { mcpServers: {} };
1103
- let backupPath;
1104
- let migrated = false;
1105
- if (await fileExists(mcpJsonPath)) {
1106
- try {
1107
- const content = await fs.readFile(mcpJsonPath, "utf-8");
1108
- existingConfig = JSON.parse(content);
1109
- if (backup) {
1110
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 18);
1111
- const suffix = Math.random().toString(36).substring(2, 6);
1112
- backupPath = `${mcpJsonPath}.backup.${timestamp}-${suffix}`;
1113
- await fs.writeFile(backupPath, content);
1114
- }
1115
- } catch {
1116
- console.log(chalk7.yellow("\u26A0 Warning: .mcp.json contains invalid JSON, starting fresh"));
1117
- existingConfig = { mcpServers: {} };
1118
- }
1119
- }
1120
- if (!existingConfig.mcpServers) {
1121
- existingConfig.mcpServers = {};
1122
- }
1123
- const existing = existingConfig.mcpServers["fractary-codex"];
1124
- if (existing) {
1125
- const existingCommand = existing.command;
1126
- const existingArgs = existing.args || [];
1127
- if (existingCommand === "npx" && existingArgs.includes("@fractary/codex-mcp")) {
1128
- return {
1129
- installed: false,
1130
- migrated: false,
1131
- alreadyInstalled: true,
1132
- backupPath
1133
- };
1134
- }
1135
- if (existingCommand === "node" || existingArgs.includes("@fractary/codex")) {
1136
- migrated = true;
1137
- }
1138
- }
1139
- existingConfig.mcpServers["fractary-codex"] = {
1140
- command: "npx",
1141
- args: ["-y", "@fractary/codex-mcp", "--config", configPath]
1142
- };
1143
- await fs.writeFile(
1144
- mcpJsonPath,
1145
- JSON.stringify(existingConfig, null, 2) + "\n"
1146
- );
1147
- return {
1148
- installed: true,
1149
- migrated,
1150
- alreadyInstalled: false,
1151
- backupPath
1152
- };
537
+
538
+ // src/commands/document/fetch.ts
539
+ function hashContent(content) {
540
+ return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
1153
541
  }
1154
- function initCommand() {
1155
- const cmd = new Command("init");
1156
- 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) => {
542
+ function documentFetchCommand() {
543
+ const cmd = new Command("document-fetch");
544
+ cmd.description("Fetch a document by codex:// URI reference").argument("<uri>", "Codex URI (e.g., codex://org/project/docs/file.md)").option("--bypass-cache", "Skip cache and fetch directly from source").option("--ttl <seconds>", "Override default TTL (in seconds)", parseInt).option("--json", "Output as JSON with metadata").option("--output <file>", "Write content to file instead of stdout").action(async (uri, options) => {
1157
545
  try {
1158
- console.log(chalk7.blue("Initializing unified Fractary configuration...\n"));
1159
- let org = options.org;
1160
- if (!org) {
1161
- org = await getOrgFromGitRemote();
1162
- }
1163
- if (!org) {
1164
- try {
1165
- const { resolveOrganization } = await import('@fractary/codex');
1166
- org = resolveOrganization({
1167
- repoName: path5.basename(process.cwd())
1168
- });
1169
- } catch {
1170
- }
1171
- }
1172
- if (!org) {
1173
- org = path5.basename(process.cwd()).split("-")[0] || "default";
1174
- console.log(chalk7.yellow(`\u26A0 Could not detect organization, using: ${org}`));
1175
- console.log(chalk7.dim(" Use --org <slug> to specify explicitly\n"));
1176
- } else {
1177
- console.log(chalk7.dim(`Organization: ${chalk7.cyan(org)}
1178
- `));
1179
- }
1180
- try {
1181
- validateNameFormat(org, "organization");
1182
- } catch (error) {
1183
- console.error(chalk7.red("Error:"), error.message);
546
+ const { validateUri } = await import('@fractary/codex');
547
+ if (!validateUri(uri)) {
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"));
1184
551
  process.exit(1);
1185
552
  }
1186
- let project = options.project;
1187
- if (!project) {
1188
- project = path5.basename(process.cwd());
1189
- console.log(chalk7.dim(`Project: ${chalk7.cyan(project)}
1190
- `));
553
+ const client = await getClient();
554
+ if (!options.json && !options.bypassCache) {
555
+ console.error(chalk10.dim(`Fetching ${uri}...`));
1191
556
  }
1192
- let codexRepo = options.codexRepo;
1193
- if (codexRepo) {
1194
- try {
1195
- validateNameFormat(codexRepo, "repository");
1196
- } catch (error) {
1197
- console.error(chalk7.red("Error:"), error.message);
1198
- process.exit(1);
1199
- }
1200
- console.log(chalk7.dim(`Codex repository: ${chalk7.cyan(codexRepo)}
1201
- `));
1202
- } else {
1203
- const discoveryResult = await discoverCodexRepo(org);
1204
- if (discoveryResult.repo) {
1205
- codexRepo = discoveryResult.repo;
1206
- console.log(chalk7.dim(`Codex repository: ${chalk7.cyan(codexRepo)} (auto-discovered)
1207
- `));
1208
- } else {
1209
- if (discoveryResult.error === "gh_not_installed") {
1210
- console.log(chalk7.dim(` Note: ${discoveryResult.message}
1211
- `));
1212
- } else if (discoveryResult.error === "auth_failed") {
1213
- console.log(chalk7.dim(` Note: ${discoveryResult.message}
1214
- `));
1215
- } else if (discoveryResult.error === "org_not_found") {
1216
- console.log(chalk7.dim(` Note: ${discoveryResult.message}
1217
- `));
557
+ const result = await client.fetch(uri, {
558
+ bypassCache: options.bypassCache,
559
+ ttl: options.ttl
560
+ });
561
+ if (options.json) {
562
+ const output = {
563
+ uri,
564
+ content: result.content.toString("utf-8"),
565
+ metadata: {
566
+ fromCache: result.fromCache,
567
+ fetchedAt: result.metadata?.fetchedAt,
568
+ expiresAt: result.metadata?.expiresAt,
569
+ contentLength: result.metadata?.contentLength || result.content.length,
570
+ contentHash: hashContent(result.content)
1218
571
  }
572
+ };
573
+ console.log(JSON.stringify(output, null, 2));
574
+ } else if (options.output) {
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`));
578
+ if (result.fromCache) {
579
+ console.log(chalk10.dim(" Source: cache"));
580
+ } else {
581
+ console.log(chalk10.dim(" Source: storage"));
1219
582
  }
1220
- }
1221
- if (!codexRepo) {
1222
- console.log(chalk7.yellow(`\u26A0 Could not discover codex repository in organization '${org}'`));
1223
- console.log(chalk7.dim(" Use --codex-repo <name> to specify explicitly"));
1224
- console.log(chalk7.dim(" Expected naming convention: codex.{org}.{tld} (e.g., codex.fractary.com)\n"));
1225
- codexRepo = `codex.${org}.com`;
1226
- console.log(chalk7.dim(` Using default: ${chalk7.cyan(codexRepo)}
1227
- `));
1228
- }
1229
- const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
1230
- const configExists = await fileExists(configPath);
1231
- if (configExists && !options.force) {
1232
- console.log(chalk7.yellow(`\u26A0 Configuration already exists at .fractary/config.yaml`));
1233
- console.log(chalk7.dim("Merging with existing configuration...\n"));
1234
- }
1235
- console.log("Creating directory structure...");
1236
- const dirs = [
1237
- ".fractary",
1238
- ".fractary/specs",
1239
- ".fractary/logs",
1240
- ".fractary/codex",
1241
- ".fractary/codex/cache"
1242
- ];
1243
- for (const dir of dirs) {
1244
- await fs.mkdir(path5.join(process.cwd(), dir), { recursive: true });
1245
- console.log(chalk7.green("\u2713"), chalk7.dim(dir + "/"));
1246
- }
1247
- const gitignoreResult = await ensureCachePathIgnored(process.cwd(), ".fractary/codex/cache");
1248
- if (gitignoreResult.created) {
1249
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/.gitignore (created)"));
1250
- } else if (gitignoreResult.updated) {
1251
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/.gitignore (updated)"));
1252
583
  } else {
1253
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/.gitignore (exists)"));
1254
- }
1255
- console.log("\nInitializing configuration...");
1256
- const result = await initializeUnifiedConfig(
1257
- configPath,
1258
- org,
1259
- project,
1260
- codexRepo,
1261
- { force: options.force }
1262
- );
1263
- if (result.created) {
1264
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/config.yaml (created)"));
1265
- } else if (result.merged) {
1266
- console.log(chalk7.green("\u2713"), chalk7.dim(".fractary/config.yaml (merged with existing)"));
1267
- }
1268
- if (options.mcp !== false) {
1269
- console.log("\nConfiguring MCP server...");
1270
- const mcpResult = await installMcpServer(process.cwd(), ".fractary/config.yaml");
1271
- if (mcpResult.alreadyInstalled) {
1272
- console.log(chalk7.green("\u2713"), chalk7.dim(".mcp.json (already configured)"));
1273
- } else if (mcpResult.migrated) {
1274
- console.log(chalk7.green("\u2713"), chalk7.dim(".mcp.json (migrated from old format)"));
1275
- if (mcpResult.backupPath) {
1276
- console.log(chalk7.dim(` Backup: ${path5.basename(mcpResult.backupPath)}`));
1277
- }
1278
- } else if (mcpResult.installed) {
1279
- console.log(chalk7.green("\u2713"), chalk7.dim(".mcp.json (created)"));
584
+ if (result.fromCache && !options.bypassCache) {
585
+ console.error(chalk10.green("\u2713"), chalk10.dim("from cache\n"));
586
+ } else {
587
+ console.error(chalk10.green("\u2713"), chalk10.dim("fetched\n"));
1280
588
  }
589
+ console.log(result.content.toString("utf-8"));
1281
590
  }
1282
- console.log(chalk7.green("\n\u2713 Unified configuration initialized successfully!\n"));
1283
- console.log(chalk7.bold("Configuration:"));
1284
- console.log(chalk7.dim(` Organization: ${org}`));
1285
- console.log(chalk7.dim(` Project: ${project}`));
1286
- console.log(chalk7.dim(` Codex Repository: ${codexRepo}`));
1287
- console.log(chalk7.dim(` Config: .fractary/config.yaml`));
1288
- console.log(chalk7.bold("\nFile plugin sources:"));
1289
- console.log(chalk7.dim(" - specs: .fractary/specs/ \u2192 S3"));
1290
- console.log(chalk7.dim(" - logs: .fractary/logs/ \u2192 S3"));
1291
- console.log(chalk7.bold("\nCodex plugin:"));
1292
- console.log(chalk7.dim(" - Cache: .fractary/codex/cache/"));
1293
- console.log(chalk7.dim(" - MCP Server: @fractary/codex-mcp (via npx)"));
1294
- console.log(chalk7.dim(" - Remotes: codex repo configured"));
1295
- console.log(chalk7.bold("\nGit Authentication:"));
1296
- console.log(chalk7.dim(" Codex sync uses your existing git credentials."));
1297
- console.log(chalk7.dim(" Ensure you have access to the codex repository:"));
1298
- console.log(chalk7.dim(` gh repo view ${org}/${codexRepo}`));
1299
- console.log(chalk7.dim(" Or set GITHUB_TOKEN environment variable."));
1300
- console.log(chalk7.bold("\nNext steps:"));
1301
- console.log(chalk7.dim(" 1. Restart Claude Code to load the MCP server"));
1302
- console.log(chalk7.dim(" 2. Verify codex repository access: gh repo view " + org + "/" + codexRepo));
1303
- console.log(chalk7.dim(" 3. Configure AWS credentials for S3 access (if using file plugin)"));
1304
- console.log(chalk7.dim(" 4. Edit .fractary/config.yaml to add external project remotes"));
1305
- console.log(chalk7.dim(" 5. Reference docs via codex:// URIs (auto-fetched by MCP)"));
1306
591
  } catch (error) {
1307
- console.error(chalk7.red("Error:"), error.message);
592
+ console.error(chalk10.red("Error:"), error.message);
593
+ if (error.message.includes("Failed to load configuration")) {
594
+ console.log(chalk10.dim('\nRun "fractary-codex configure" to create a configuration.'));
595
+ } else if (error.message.includes("GITHUB_TOKEN")) {
596
+ console.log(chalk10.dim('\nSet your GitHub token: export GITHUB_TOKEN="your_token"'));
597
+ } else if (error.message.includes("not found") || error.message.includes("404")) {
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."));
600
+ }
1308
601
  process.exit(1);
1309
602
  }
1310
603
  });
1311
604
  return cmd;
1312
605
  }
1313
606
 
1314
- // src/commands/config/index.ts
1315
- function configCommand() {
1316
- const cmd = new Command("config");
1317
- cmd.description("Manage configuration");
1318
- cmd.addCommand(initCommand());
1319
- return cmd;
1320
- }
1321
-
1322
607
  // src/commands/cache/index.ts
1323
608
  init_esm_shims();
1324
609
 
1325
610
  // src/commands/cache/list.ts
1326
611
  init_esm_shims();
1327
- function formatSize(bytes) {
1328
- if (bytes < 1024) return `${bytes} B`;
1329
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1330
- 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
+ }
1331
633
  }
1332
634
  function cacheListCommand() {
1333
- const cmd = new Command("list");
1334
- cmd.description("List cache information").option("--json", "Output as JSON").action(async (options) => {
635
+ const cmd = new Command("cache-list");
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) => {
1335
637
  try {
1336
638
  const client = await getClient();
1337
- const stats = await client.getCacheStats();
1338
- 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) {
1339
647
  if (options.json) {
1340
- console.log(JSON.stringify({ entries: 0, message: "Cache is empty" }));
648
+ console.log(JSON.stringify({ entries: [], total: 0, message: "Cache is empty" }));
1341
649
  } else {
1342
- console.log(chalk7.yellow("Cache is empty."));
1343
- 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."));
1344
652
  }
1345
653
  return;
1346
654
  }
1347
655
  if (options.json) {
1348
- console.log(JSON.stringify({
1349
- entryCount: stats.entryCount,
1350
- totalSize: stats.totalSize,
1351
- freshCount: stats.freshCount,
1352
- staleCount: stats.staleCount,
1353
- expiredCount: stats.expiredCount
1354
- }, null, 2));
656
+ console.log(JSON.stringify(result, null, 2));
1355
657
  return;
1356
658
  }
1357
- console.log(chalk7.bold("Cache Overview\n"));
1358
- console.log(chalk7.bold("Entries:"));
1359
- console.log(` Total: ${chalk7.cyan(stats.entryCount.toString())} entries`);
1360
- console.log(` Fresh: ${chalk7.green(stats.freshCount.toString())} entries`);
1361
- console.log(` Stale: ${stats.staleCount > 0 ? chalk7.yellow(stats.staleCount.toString()) : chalk7.dim("0")} entries`);
1362
- console.log(` Expired: ${stats.expiredCount > 0 ? chalk7.red(stats.expiredCount.toString()) : chalk7.dim("0")} entries`);
1363
- console.log("");
1364
- console.log(chalk7.bold("Storage:"));
1365
- console.log(` Total size: ${chalk7.cyan(formatSize(stats.totalSize))}`);
1366
- console.log("");
1367
- const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1368
- const healthColor = healthPercent > 80 ? chalk7.green : healthPercent > 50 ? chalk7.yellow : chalk7.red;
1369
- 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();
1370
698
  console.log("");
1371
- console.log(chalk7.dim("Note: Individual cache entries are managed by the SDK."));
1372
- console.log(chalk7.dim('Use "fractary codex cache stats" for detailed statistics.'));
1373
- 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
+ );
1374
705
  } catch (error) {
1375
- 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
+ }
1376
722
  process.exit(1);
1377
723
  }
1378
724
  });
@@ -1382,13 +728,13 @@ function cacheListCommand() {
1382
728
  // src/commands/cache/clear.ts
1383
729
  init_esm_shims();
1384
730
  function cacheClearCommand() {
1385
- const cmd = new Command("clear");
731
+ const cmd = new Command("cache-clear");
1386
732
  cmd.description("Clear cache entries").option("--all", "Clear entire cache").option("--pattern <glob>", 'Clear entries matching URI pattern (e.g., "codex://fractary/*")').option("--dry-run", "Show what would be cleared without actually clearing").action(async (options) => {
1387
733
  try {
1388
734
  const client = await getClient();
1389
735
  const statsBefore = await client.getCacheStats();
1390
736
  if (statsBefore.entryCount === 0) {
1391
- console.log(chalk7.yellow("Cache is already empty. Nothing to clear."));
737
+ console.log(chalk10.yellow("Cache is already empty. Nothing to clear."));
1392
738
  return;
1393
739
  }
1394
740
  let pattern;
@@ -1397,65 +743,55 @@ function cacheClearCommand() {
1397
743
  } else if (options.pattern) {
1398
744
  pattern = options.pattern;
1399
745
  } else {
1400
- console.log(chalk7.yellow("Please specify what to clear:"));
1401
- console.log(chalk7.dim(" --all Clear entire cache"));
1402
- 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/*")'));
1403
749
  console.log("");
1404
- console.log(chalk7.dim("Examples:"));
1405
- console.log(chalk7.dim(" fractary codex cache clear --all"));
1406
- 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/*"'));
1407
753
  return;
1408
754
  }
1409
755
  if (options.dryRun) {
1410
- console.log(chalk7.blue("Dry run - would clear:\n"));
756
+ console.log(chalk10.blue("Dry run - would clear:\n"));
1411
757
  if (pattern) {
1412
- console.log(chalk7.dim(` Pattern: ${pattern}`));
1413
- 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`));
1414
760
  } else {
1415
- console.log(chalk7.dim(` All cache entries (${statsBefore.entryCount} entries)`));
761
+ console.log(chalk10.dim(` All cache entries (${statsBefore.entryCount} entries)`));
1416
762
  }
1417
- console.log(chalk7.dim(`
1418
- Total size: ${formatSize2(statsBefore.totalSize)}`));
763
+ console.log(chalk10.dim(`
764
+ Total size: ${formatBytes(statsBefore.totalSize)}`));
1419
765
  return;
1420
766
  }
1421
767
  if (pattern) {
1422
- console.log(chalk7.blue(`Clearing cache entries matching pattern: ${pattern}
768
+ console.log(chalk10.blue(`Clearing cache entries matching pattern: ${pattern}
1423
769
  `));
1424
770
  await client.invalidateCache(pattern);
1425
771
  } else {
1426
- console.log(chalk7.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
772
+ console.log(chalk10.blue(`Clearing entire cache (${statsBefore.entryCount} entries)...
1427
773
  `));
1428
774
  await client.invalidateCache();
1429
775
  }
1430
776
  const statsAfter = await client.getCacheStats();
1431
777
  const entriesCleared = statsBefore.entryCount - statsAfter.entryCount;
1432
778
  const sizeFreed = statsBefore.totalSize - statsAfter.totalSize;
1433
- console.log(chalk7.green(`\u2713 Cleared ${entriesCleared} entries (${formatSize2(sizeFreed)} freed)`));
779
+ console.log(chalk10.green(`\u2713 Cleared ${entriesCleared} entries (${formatBytes(sizeFreed)} freed)`));
1434
780
  if (statsAfter.entryCount > 0) {
1435
- console.log(chalk7.dim(` Remaining: ${statsAfter.entryCount} entries (${formatSize2(statsAfter.totalSize)})`));
781
+ console.log(chalk10.dim(` Remaining: ${statsAfter.entryCount} entries (${formatBytes(statsAfter.totalSize)})`));
1436
782
  }
1437
783
  } catch (error) {
1438
- console.error(chalk7.red("Error:"), error.message);
784
+ console.error(chalk10.red("Error:"), error.message);
1439
785
  process.exit(1);
1440
786
  }
1441
787
  });
1442
788
  return cmd;
1443
789
  }
1444
- function formatSize2(bytes) {
1445
- if (bytes < 1024) return `${bytes} B`;
1446
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1447
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1448
- }
1449
790
 
1450
791
  // src/commands/cache/stats.ts
1451
792
  init_esm_shims();
1452
- function formatSize3(bytes) {
1453
- if (bytes < 1024) return `${bytes} B`;
1454
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1455
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1456
- }
1457
793
  function cacheStatsCommand() {
1458
- const cmd = new Command("stats");
794
+ const cmd = new Command("cache-stats");
1459
795
  cmd.description("Display cache statistics").option("--json", "Output as JSON").action(async (options) => {
1460
796
  try {
1461
797
  const client = await getClient();
@@ -1464,25 +800,25 @@ function cacheStatsCommand() {
1464
800
  console.log(JSON.stringify(stats, null, 2));
1465
801
  return;
1466
802
  }
1467
- console.log(chalk7.bold("Cache Statistics\n"));
1468
- console.log(chalk7.bold("Overview"));
1469
- console.log(` Total entries: ${chalk7.cyan(stats.entryCount.toString())}`);
1470
- console.log(` Total size: ${chalk7.cyan(formatSize3(stats.totalSize))}`);
1471
- console.log(` Fresh entries: ${chalk7.green(stats.freshCount.toString())}`);
1472
- console.log(` Stale entries: ${stats.staleCount > 0 ? chalk7.yellow(stats.staleCount.toString()) : chalk7.dim("0")}`);
1473
- 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")}`);
1474
810
  console.log("");
1475
811
  const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1476
- 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;
1477
813
  console.log(`Cache health: ${healthColor(`${healthPercent.toFixed(0)}% fresh`)}`);
1478
814
  if (stats.expiredCount > 0) {
1479
- 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.'));
1480
816
  }
1481
817
  if (stats.entryCount === 0) {
1482
- 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."));
1483
819
  }
1484
820
  } catch (error) {
1485
- console.error(chalk7.red("Error:"), error.message);
821
+ console.error(chalk10.red("Error:"), error.message);
1486
822
  process.exit(1);
1487
823
  }
1488
824
  });
@@ -1491,260 +827,94 @@ function cacheStatsCommand() {
1491
827
 
1492
828
  // src/commands/cache/health.ts
1493
829
  init_esm_shims();
1494
- init_migrate_config();
1495
- async function fileExists2(filePath) {
1496
- try {
1497
- await fs.access(filePath);
1498
- return true;
1499
- } catch {
1500
- return false;
1501
- }
1502
- }
1503
- async function checkConfiguration() {
1504
- const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
1505
- const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
1506
- try {
1507
- if (!await fileExists2(configPath)) {
1508
- if (await fileExists2(legacyConfigPath)) {
1509
- return {
1510
- name: "Configuration",
1511
- status: "warn",
1512
- message: "Legacy JSON configuration detected",
1513
- details: 'Run "fractary codex migrate" to upgrade to YAML format'
1514
- };
1515
- }
1516
- return {
1517
- name: "Configuration",
1518
- status: "fail",
1519
- message: "No configuration found",
1520
- details: 'Run "fractary codex init" to create configuration'
1521
- };
1522
- }
1523
- const config = await readCodexConfig(configPath);
1524
- if (!config.organization) {
1525
- return {
1526
- name: "Configuration",
1527
- status: "warn",
1528
- message: "No organization configured",
1529
- details: "Organization slug is required"
1530
- };
1531
- }
1532
- const providerCount = config.storage?.length || 0;
1533
- if (providerCount === 0) {
1534
- return {
1535
- name: "Configuration",
1536
- status: "warn",
1537
- message: "No storage providers configured",
1538
- details: "At least one storage provider is recommended"
1539
- };
1540
- }
1541
- return {
1542
- name: "Configuration",
1543
- status: "pass",
1544
- message: "Valid YAML configuration",
1545
- details: `Organization: ${config.organization}, ${providerCount} storage provider(s)`
1546
- };
1547
- } catch (error) {
1548
- return {
1549
- name: "Configuration",
1550
- status: "fail",
1551
- message: "Invalid configuration",
1552
- details: error.message
1553
- };
1554
- }
1555
- }
1556
- async function checkSDKClient() {
1557
- try {
1558
- const client = await getClient();
1559
- const organization = client.getOrganization();
1560
- return {
1561
- name: "SDK Client",
1562
- status: "pass",
1563
- message: "CodexClient initialized successfully",
1564
- details: `Organization: ${organization}`
1565
- };
1566
- } catch (error) {
1567
- return {
1568
- name: "SDK Client",
1569
- status: "fail",
1570
- message: "Failed to initialize CodexClient",
1571
- details: error.message
1572
- };
1573
- }
1574
- }
1575
- async function checkCache() {
1576
- try {
1577
- const client = await getClient();
1578
- const stats = await client.getCacheStats();
1579
- if (stats.entryCount === 0) {
1580
- return {
1581
- name: "Cache",
1582
- status: "warn",
1583
- message: "Cache is empty",
1584
- details: "Fetch some documents to populate cache"
1585
- };
1586
- }
1587
- const healthPercent = stats.entryCount > 0 ? stats.freshCount / stats.entryCount * 100 : 100;
1588
- if (healthPercent < 50) {
1589
- return {
1590
- name: "Cache",
1591
- status: "warn",
1592
- message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1593
- details: `${stats.expiredCount} expired, ${stats.staleCount} stale`
1594
- };
1595
- }
1596
- return {
1597
- name: "Cache",
1598
- status: "pass",
1599
- message: `${stats.entryCount} entries (${healthPercent.toFixed(0)}% fresh)`,
1600
- details: `${formatSize4(stats.totalSize)} total`
1601
- };
1602
- } catch (error) {
1603
- return {
1604
- name: "Cache",
1605
- status: "fail",
1606
- message: "Cache check failed",
1607
- details: error.message
1608
- };
1609
- }
1610
- }
1611
- async function checkStorage() {
1612
- const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
1613
- try {
1614
- const config = await readCodexConfig(configPath);
1615
- const providers = config.storage || [];
1616
- if (providers.length === 0) {
1617
- return {
1618
- name: "Storage",
1619
- status: "warn",
1620
- message: "No storage providers configured",
1621
- details: "Configure at least one provider in .fractary/config.yaml"
1622
- };
1623
- }
1624
- const providerTypes = providers.map((p) => p.type).join(", ");
1625
- const hasGitHub = providers.some((p) => p.type === "github");
1626
- if (hasGitHub && !process.env.GITHUB_TOKEN) {
1627
- return {
1628
- name: "Storage",
1629
- status: "warn",
1630
- message: `${providers.length} provider(s): ${providerTypes}`,
1631
- details: "GITHUB_TOKEN not set (required for GitHub provider)"
1632
- };
1633
- }
1634
- return {
1635
- name: "Storage",
1636
- status: "pass",
1637
- message: `${providers.length} provider(s): ${providerTypes}`,
1638
- details: "All configured providers available"
1639
- };
1640
- } catch (error) {
1641
- return {
1642
- name: "Storage",
1643
- status: "fail",
1644
- message: "Storage check failed",
1645
- details: error.message
1646
- };
1647
- }
1648
- }
1649
- async function checkTypes() {
1650
- try {
1651
- const client = await getClient();
1652
- const registry = client.getTypeRegistry();
1653
- const allTypes = registry.list();
1654
- const builtinCount = allTypes.filter((t) => registry.isBuiltIn(t.name)).length;
1655
- const customCount = allTypes.length - builtinCount;
1656
- return {
1657
- name: "Type Registry",
1658
- status: "pass",
1659
- message: `${allTypes.length} types registered`,
1660
- details: `${builtinCount} built-in, ${customCount} custom`
1661
- };
1662
- } catch (error) {
1663
- return {
1664
- name: "Type Registry",
1665
- status: "fail",
1666
- message: "Type registry check failed",
1667
- details: error.message
1668
- };
1669
- }
1670
- }
1671
- function formatSize4(bytes) {
1672
- if (bytes < 1024) return `${bytes} B`;
1673
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1674
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1675
- }
1676
- function healthCommand() {
1677
- const cmd = new Command("health");
830
+ function cacheHealthCommand() {
831
+ const cmd = new Command("cache-health");
1678
832
  cmd.description("Run diagnostics on codex setup").option("--json", "Output as JSON").action(async (options) => {
1679
833
  try {
834
+ const healthChecker = createHealthChecker({
835
+ projectRoot: process.cwd()
836
+ });
1680
837
  const checks = [];
1681
- checks.push(await checkConfiguration());
1682
- checks.push(await checkSDKClient());
1683
- checks.push(await checkCache());
1684
- checks.push(await checkStorage());
1685
- checks.push(await checkTypes());
1686
- const passed = checks.filter((c) => c.status === "pass").length;
1687
- const warned = checks.filter((c) => c.status === "warn").length;
1688
- 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);
1689
874
  if (options.json) {
1690
- console.log(JSON.stringify({
1691
- summary: {
1692
- total: checks.length,
1693
- passed,
1694
- warned,
1695
- failed,
1696
- healthy: failed === 0
1697
- },
1698
- checks
1699
- }, null, 2));
875
+ console.log(JSON.stringify(result, null, 2));
1700
876
  return;
1701
877
  }
1702
- console.log(chalk7.bold("Codex Health Check\n"));
1703
- for (const check of checks) {
1704
- const icon = check.status === "pass" ? chalk7.green("\u2713") : check.status === "warn" ? chalk7.yellow("\u26A0") : chalk7.red("\u2717");
1705
- const statusColor = check.status === "pass" ? chalk7.green : check.status === "warn" ? chalk7.yellow : chalk7.red;
1706
- 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)}`);
1707
883
  console.log(` ${statusColor(check.message)}`);
1708
884
  if (check.details) {
1709
- console.log(` ${chalk7.dim(check.details)}`);
885
+ console.log(` ${chalk10.dim(check.details)}`);
1710
886
  }
1711
887
  console.log("");
1712
888
  }
1713
- console.log(chalk7.dim("\u2500".repeat(60)));
1714
- 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");
1715
891
  console.log(`Status: ${overallStatus}`);
1716
- console.log(chalk7.dim(`${passed} passed, ${warned} warnings, ${failed} failed`));
1717
- 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) {
1718
896
  console.log("");
1719
- console.log(chalk7.dim("Run checks individually for more details:"));
1720
- console.log(chalk7.dim(" fractary codex cache stats"));
1721
- console.log(chalk7.dim(" fractary codex types list"));
897
+ console.log(chalk10.dim("Run checks individually for more details:"));
898
+ console.log(chalk10.dim(" fractary-codex cache-stats"));
1722
899
  }
1723
- if (failed > 0) {
900
+ if (result.summary.failed > 0) {
1724
901
  process.exit(1);
1725
902
  }
1726
903
  } catch (error) {
1727
- console.error(chalk7.red("Error:"), error.message);
904
+ console.error(chalk10.red("Error:"), error.message);
1728
905
  process.exit(1);
1729
906
  }
1730
907
  });
1731
908
  return cmd;
1732
909
  }
1733
910
 
1734
- // src/commands/cache/index.ts
1735
- function cacheCommand() {
1736
- const cmd = new Command("cache");
1737
- cmd.description("Manage the codex document cache");
1738
- cmd.addCommand(cacheListCommand());
1739
- cmd.addCommand(cacheClearCommand());
1740
- cmd.addCommand(cacheStatsCommand());
1741
- cmd.addCommand(healthCommand());
1742
- return cmd;
1743
- }
1744
-
1745
911
  // src/commands/sync.ts
1746
912
  init_esm_shims();
1747
- init_migrate_config();
913
+
914
+ // src/config/migrate-config.ts
915
+ init_esm_shims();
916
+
917
+ // src/commands/sync.ts
1748
918
  function getEnvironmentBranch(config, env) {
1749
919
  const envMap = config.sync?.environments || {
1750
920
  dev: "develop",
@@ -1754,26 +924,17 @@ function getEnvironmentBranch(config, env) {
1754
924
  };
1755
925
  return envMap[env] || env;
1756
926
  }
1757
- function formatBytes(bytes) {
1758
- if (bytes < 1024) return `${bytes} B`;
1759
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1760
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1761
- }
1762
- function formatDuration(ms) {
1763
- if (ms < 1e3) return `${ms}ms`;
1764
- return `${(ms / 1e3).toFixed(1)}s`;
1765
- }
1766
927
  function syncCommand() {
1767
928
  const cmd = new Command("sync");
1768
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) => {
1769
930
  try {
1770
- const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
931
+ const configPath = path4.join(process.cwd(), ".fractary", "config.yaml");
1771
932
  let config;
1772
933
  try {
1773
934
  config = await readCodexConfig(configPath);
1774
935
  } catch (error) {
1775
- console.error(chalk7.red("Error:"), "Codex not initialized.");
1776
- console.log(chalk7.dim('Run "fractary codex init" first.'));
936
+ console.error(chalk10.red("Error:"), "Codex not initialized.");
937
+ console.log(chalk10.dim('Run "fractary-codex configure" first.'));
1777
938
  process.exit(1);
1778
939
  }
1779
940
  const { createSyncManager, createLocalStorage, detectCurrentProject } = await import('@fractary/codex');
@@ -1786,14 +947,14 @@ function syncCommand() {
1786
947
  projectName = detected.project || void 0;
1787
948
  }
1788
949
  if (!projectName) {
1789
- console.error(chalk7.red("Error:"), "Could not determine project name.");
1790
- 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."));
1791
952
  process.exit(1);
1792
953
  }
1793
954
  const validDirections = ["to-codex", "from-codex", "bidirectional"];
1794
955
  if (!validDirections.includes(options.direction)) {
1795
- console.error(chalk7.red("Error:"), `Invalid direction: ${options.direction}`);
1796
- 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"));
1797
958
  process.exit(1);
1798
959
  }
1799
960
  const direction = options.direction;
@@ -1804,7 +965,7 @@ function syncCommand() {
1804
965
  const syncManager = createSyncManager({
1805
966
  localStorage,
1806
967
  config: config.sync,
1807
- manifestPath: path5.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
968
+ manifestPath: path4.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
1808
969
  });
1809
970
  const defaultToCodexPatterns = [
1810
971
  "docs/**/*.md",
@@ -1834,13 +995,13 @@ function syncCommand() {
1834
995
  });
1835
996
  matches.forEach((match) => matchedFilePaths.add(match));
1836
997
  } catch (error) {
1837
- console.error(chalk7.yellow(`Warning: Invalid pattern "${pattern}": ${error.message}`));
998
+ console.error(chalk10.yellow(`Warning: Invalid pattern "${pattern}": ${error.message}`));
1838
999
  }
1839
1000
  }
1840
1001
  const targetFiles = await Promise.all(
1841
1002
  Array.from(matchedFilePaths).map(async (filePath) => {
1842
- const fullPath = path5.join(sourceDir, filePath);
1843
- 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));
1844
1005
  return {
1845
1006
  path: filePath,
1846
1007
  size: stats.size,
@@ -1864,14 +1025,14 @@ function syncCommand() {
1864
1025
  try {
1865
1026
  const { ensureCodexCloned: ensureCodexCloned2 } = await Promise.resolve().then(() => (init_codex_repository(), codex_repository_exports));
1866
1027
  if (!options.json) {
1867
- console.log(chalk7.blue("\u2139 Cloning/updating codex repository..."));
1028
+ console.log(chalk10.blue("\u2139 Cloning/updating codex repository..."));
1868
1029
  }
1869
1030
  codexRepoPath = await ensureCodexCloned2(config, {
1870
1031
  branch: targetBranch
1871
1032
  });
1872
1033
  if (!options.json) {
1873
- console.log(chalk7.dim(` Codex cloned to: ${codexRepoPath}`));
1874
- 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"));
1875
1036
  } else {
1876
1037
  console.log(JSON.stringify({
1877
1038
  info: "Routing-aware sync: cloned codex repository and scanning for files targeting this project",
@@ -1879,29 +1040,29 @@ function syncCommand() {
1879
1040
  }, null, 2));
1880
1041
  }
1881
1042
  } catch (error) {
1882
- console.error(chalk7.red("Error:"), "Failed to clone codex repository");
1883
- console.error(chalk7.dim(` ${error.message}`));
1884
- 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:"));
1885
1046
  if (error.message.includes("Git command not found")) {
1886
- console.log(chalk7.dim(" Git is not installed or not in PATH."));
1887
- 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"));
1888
1049
  } else if (error.message.includes("authentication failed") || error.message.includes("Authentication failed")) {
1889
- console.log(chalk7.dim(" GitHub authentication is required for private repositories."));
1890
- console.log(chalk7.dim(" 1. Check auth status: gh auth status"));
1891
- console.log(chalk7.dim(" 2. Login if needed: gh auth login"));
1892
- 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"));
1893
1054
  } else if (error.message.includes("Permission denied")) {
1894
- console.log(chalk7.dim(" Permission denied accessing repository files."));
1895
- console.log(chalk7.dim(" 1. Check file/directory permissions"));
1896
- 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"));
1897
1058
  } else if (error.message.includes("not found") || error.message.includes("does not exist")) {
1898
- console.log(chalk7.dim(` Repository not found: ${config.organization}/${config.codex_repo || "codex"}`));
1899
- console.log(chalk7.dim(" 1. Verify the repository exists on GitHub"));
1900
- 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"));
1901
1062
  } else {
1902
- console.log(chalk7.dim(" 1. Ensure git is installed: git --version"));
1903
- console.log(chalk7.dim(" 2. Check GitHub auth: gh auth status"));
1904
- 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"}`));
1905
1066
  }
1906
1067
  process.exit(1);
1907
1068
  }
@@ -1920,17 +1081,17 @@ function syncCommand() {
1920
1081
  try {
1921
1082
  const { ensureCodexCloned: ensureCodexCloned2 } = await Promise.resolve().then(() => (init_codex_repository(), codex_repository_exports));
1922
1083
  if (!options.json) {
1923
- console.log(chalk7.blue("\u2139 Cloning/updating codex repository..."));
1084
+ console.log(chalk10.blue("\u2139 Cloning/updating codex repository..."));
1924
1085
  }
1925
1086
  codexRepoPath = await ensureCodexCloned2(config, {
1926
1087
  branch: targetBranch
1927
1088
  });
1928
1089
  if (!options.json) {
1929
- console.log(chalk7.dim(` Codex cloned to: ${codexRepoPath}`));
1090
+ console.log(chalk10.dim(` Codex cloned to: ${codexRepoPath}`));
1930
1091
  }
1931
1092
  } catch (error) {
1932
- console.error(chalk7.red("Error:"), "Failed to clone codex repository");
1933
- console.error(chalk7.dim(` ${error.message}`));
1093
+ console.error(chalk10.red("Error:"), "Failed to clone codex repository");
1094
+ console.error(chalk10.dim(` ${error.message}`));
1934
1095
  process.exit(1);
1935
1096
  }
1936
1097
  plan = await syncManager.createPlan(
@@ -1942,7 +1103,7 @@ function syncCommand() {
1942
1103
  syncOptions
1943
1104
  );
1944
1105
  plan.source = sourceDir;
1945
- plan.target = path5.join(codexRepoPath, "projects", projectName);
1106
+ plan.target = path4.join(codexRepoPath, "projects", projectName);
1946
1107
  }
1947
1108
  if (plan.totalFiles === 0) {
1948
1109
  if (options.json) {
@@ -1956,7 +1117,7 @@ function syncCommand() {
1956
1117
  message: "No files to sync"
1957
1118
  }, null, 2));
1958
1119
  } else {
1959
- console.log(chalk7.yellow("No files to sync."));
1120
+ console.log(chalk10.yellow("No files to sync."));
1960
1121
  }
1961
1122
  return;
1962
1123
  }
@@ -2012,81 +1173,81 @@ function syncCommand() {
2012
1173
  }, null, 2));
2013
1174
  return;
2014
1175
  }
2015
- console.log(chalk7.bold("Sync Plan\n"));
2016
- console.log(` Project: ${chalk7.cyan(projectName)}`);
2017
- console.log(` Organization: ${chalk7.cyan(config.organization)}`);
2018
- console.log(` Environment: ${chalk7.cyan(options.env)} (${targetBranch})`);
2019
- console.log(` Direction: ${chalk7.cyan(direction)}`);
2020
- console.log(` Files: ${chalk7.cyan(plan.totalFiles.toString())}`);
2021
- 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))}`);
2022
1183
  if (plan.estimatedTime) {
2023
- console.log(` Est. time: ${chalk7.dim(formatDuration(plan.estimatedTime))}`);
1184
+ console.log(` Est. time: ${chalk10.dim(formatDuration(plan.estimatedTime))}`);
2024
1185
  }
2025
1186
  if (routingScan) {
2026
1187
  console.log("");
2027
- console.log(chalk7.bold("Routing Statistics\n"));
2028
- console.log(` Scanned: ${chalk7.cyan(routingScan.stats.totalScanned.toString())} files`);
2029
- console.log(` Matched: ${chalk7.cyan(routingScan.stats.totalMatched.toString())} files`);
2030
- 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())}`);
2031
1192
  if (routingScan.stats.sourceProjects.length > 0) {
2032
- console.log(chalk7.dim(` ${routingScan.stats.sourceProjects.slice(0, 5).join(", ")}`));
1193
+ console.log(chalk10.dim(` ${routingScan.stats.sourceProjects.slice(0, 5).join(", ")}`));
2033
1194
  if (routingScan.stats.sourceProjects.length > 5) {
2034
- console.log(chalk7.dim(` ... and ${routingScan.stats.sourceProjects.length - 5} more`));
1195
+ console.log(chalk10.dim(` ... and ${routingScan.stats.sourceProjects.length - 5} more`));
2035
1196
  }
2036
1197
  }
2037
- console.log(` Scan time: ${chalk7.dim(formatDuration(routingScan.stats.durationMs))}`);
1198
+ console.log(` Scan time: ${chalk10.dim(formatDuration(routingScan.stats.durationMs))}`);
2038
1199
  }
2039
1200
  console.log("");
2040
1201
  if (plan.conflicts.length > 0) {
2041
- console.log(chalk7.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
1202
+ console.log(chalk10.yellow(`\u26A0 ${plan.conflicts.length} conflicts detected:`));
2042
1203
  for (const conflict of plan.conflicts.slice(0, 5)) {
2043
- console.log(chalk7.yellow(` \u2022 ${conflict.path}`));
1204
+ console.log(chalk10.yellow(` \u2022 ${conflict.path}`));
2044
1205
  }
2045
1206
  if (plan.conflicts.length > 5) {
2046
- console.log(chalk7.dim(` ... and ${plan.conflicts.length - 5} more`));
1207
+ console.log(chalk10.dim(` ... and ${plan.conflicts.length - 5} more`));
2047
1208
  }
2048
1209
  console.log("");
2049
1210
  }
2050
1211
  if (plan.skipped.length > 0) {
2051
- console.log(chalk7.dim(`${plan.skipped.length} files skipped (no changes)`));
1212
+ console.log(chalk10.dim(`${plan.skipped.length} files skipped (no changes)`));
2052
1213
  console.log("");
2053
1214
  }
2054
1215
  if (options.dryRun) {
2055
- console.log(chalk7.blue("Dry run - would sync:\n"));
1216
+ console.log(chalk10.blue("Dry run - would sync:\n"));
2056
1217
  const filesToShow = plan.files.slice(0, 10);
2057
1218
  for (const file of filesToShow) {
2058
1219
  const arrow = direction === "to-codex" ? "\u2192" : direction === "from-codex" ? "\u2190" : "\u2194";
2059
- 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;
2060
1221
  console.log(
2061
- chalk7.dim(` ${arrow}`),
1222
+ chalk10.dim(` ${arrow}`),
2062
1223
  opColor(file.operation.padEnd(7)),
2063
1224
  file.path,
2064
- chalk7.dim(`(${formatBytes(file.size || 0)})`)
1225
+ chalk10.dim(`(${formatBytes(file.size || 0)})`)
2065
1226
  );
2066
1227
  }
2067
1228
  if (plan.files.length > 10) {
2068
- console.log(chalk7.dim(` ... and ${plan.files.length - 10} more files`));
1229
+ console.log(chalk10.dim(` ... and ${plan.files.length - 10} more files`));
2069
1230
  }
2070
- console.log(chalk7.dim(`
1231
+ console.log(chalk10.dim(`
2071
1232
  Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
2072
- console.log(chalk7.dim("Run without --dry-run to execute sync."));
1233
+ console.log(chalk10.dim("Run without --dry-run to execute sync."));
2073
1234
  return;
2074
1235
  }
2075
- console.log(chalk7.blue("Syncing...\n"));
1236
+ console.log(chalk10.blue("Syncing...\n"));
2076
1237
  const startTime = Date.now();
2077
1238
  const result = await syncManager.executePlan(plan, syncOptions);
2078
1239
  const duration = Date.now() - startTime;
2079
1240
  if (direction === "to-codex" && codexRepoPath && result.synced > 0) {
2080
1241
  try {
2081
1242
  if (!options.json) {
2082
- console.log(chalk7.blue("Committing and pushing to codex..."));
1243
+ console.log(chalk10.blue("Committing and pushing to codex..."));
2083
1244
  }
2084
1245
  const { RepoManager } = await import('@fractary/core/repo');
2085
1246
  const repoManager = new RepoManager({ platform: "github" }, codexRepoPath);
2086
1247
  repoManager.stageAll();
2087
1248
  if (repoManager.isClean()) {
2088
1249
  if (!options.json) {
2089
- 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"));
2090
1251
  }
2091
1252
  } else {
2092
1253
  repoManager.commit({
@@ -2094,389 +1255,46 @@ Total: ${plan.totalFiles} files (${formatBytes(plan.totalBytes)})`));
2094
1255
  });
2095
1256
  repoManager.push({});
2096
1257
  if (!options.json) {
2097
- console.log(chalk7.dim(" Changes pushed to codex repository"));
1258
+ console.log(chalk10.dim(" Changes pushed to codex repository"));
2098
1259
  }
2099
1260
  }
2100
1261
  } catch (error) {
2101
- console.error(chalk7.red("Error pushing to codex:"), error.message);
1262
+ console.error(chalk10.red("Error pushing to codex:"), error.message);
2102
1263
  }
2103
1264
  }
2104
1265
  console.log("");
2105
1266
  if (result.success) {
2106
- console.log(chalk7.green(`\u2713 Sync completed successfully`));
2107
- 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`));
2108
1269
  if (result.skipped > 0) {
2109
- console.log(chalk7.dim(` Skipped: ${result.skipped} files`));
1270
+ console.log(chalk10.dim(` Skipped: ${result.skipped} files`));
2110
1271
  }
2111
- console.log(chalk7.dim(` Duration: ${formatDuration(duration)}`));
1272
+ console.log(chalk10.dim(` Duration: ${formatDuration(duration)}`));
2112
1273
  } else {
2113
- console.log(chalk7.yellow(`\u26A0 Sync completed with errors`));
2114
- console.log(chalk7.green(` Synced: ${result.synced} files`));
2115
- 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`));
2116
1277
  if (result.skipped > 0) {
2117
- console.log(chalk7.dim(` Skipped: ${result.skipped} files`));
1278
+ console.log(chalk10.dim(` Skipped: ${result.skipped} files`));
2118
1279
  }
2119
1280
  if (result.errors.length > 0) {
2120
1281
  console.log("");
2121
- console.log(chalk7.red("Errors:"));
1282
+ console.log(chalk10.red("Errors:"));
2122
1283
  for (const error of result.errors.slice(0, 5)) {
2123
- console.log(chalk7.red(` \u2022 ${error.path}: ${error.error}`));
1284
+ console.log(chalk10.red(` \u2022 ${error.path}: ${error.error}`));
2124
1285
  }
2125
1286
  if (result.errors.length > 5) {
2126
- console.log(chalk7.dim(` ... and ${result.errors.length - 5} more errors`));
1287
+ console.log(chalk10.dim(` ... and ${result.errors.length - 5} more errors`));
2127
1288
  }
2128
1289
  }
2129
1290
  }
2130
1291
  } catch (error) {
2131
- console.error(chalk7.red("Error:"), error.message);
2132
- process.exit(1);
2133
- }
2134
- });
2135
- return cmd;
2136
- }
2137
-
2138
- // src/commands/types/index.ts
2139
- init_esm_shims();
2140
-
2141
- // src/commands/types/list.ts
2142
- init_esm_shims();
2143
- function formatTtl(seconds) {
2144
- if (seconds < 60) return `${seconds}s`;
2145
- if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
2146
- if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
2147
- return `${Math.floor(seconds / 86400)}d`;
2148
- }
2149
- function typesListCommand() {
2150
- const cmd = new Command("list");
2151
- cmd.description("List all artifact types").option("--json", "Output as JSON").option("--custom-only", "Show only custom types").option("--builtin-only", "Show only built-in types").action(async (options) => {
2152
- try {
2153
- const client = await getClient();
2154
- const registry = client.getTypeRegistry();
2155
- const allTypes = registry.list();
2156
- let types = allTypes;
2157
- if (options.customOnly) {
2158
- types = allTypes.filter((t) => !registry.isBuiltIn(t.name));
2159
- } else if (options.builtinOnly) {
2160
- types = allTypes.filter((t) => registry.isBuiltIn(t.name));
2161
- }
2162
- if (options.json) {
2163
- const builtinCount = types.filter((t) => registry.isBuiltIn(t.name)).length;
2164
- const customCount = types.length - builtinCount;
2165
- console.log(JSON.stringify({
2166
- count: types.length,
2167
- builtinCount,
2168
- customCount,
2169
- types: types.map((t) => ({
2170
- name: t.name,
2171
- description: t.description,
2172
- patterns: t.patterns,
2173
- defaultTtl: t.defaultTtl,
2174
- ttl: formatTtl(t.defaultTtl),
2175
- builtin: registry.isBuiltIn(t.name),
2176
- archiveAfterDays: t.archiveAfterDays,
2177
- archiveStorage: t.archiveStorage
2178
- }))
2179
- }, null, 2));
2180
- return;
2181
- }
2182
- if (types.length === 0) {
2183
- console.log(chalk7.yellow("No types found."));
2184
- return;
2185
- }
2186
- console.log(chalk7.bold("Artifact Types\n"));
2187
- const builtinTypes = types.filter((t) => registry.isBuiltIn(t.name));
2188
- const customTypes = types.filter((t) => !registry.isBuiltIn(t.name));
2189
- if (builtinTypes.length > 0 && !options.customOnly) {
2190
- console.log(chalk7.bold("Built-in Types"));
2191
- console.log(chalk7.dim("\u2500".repeat(70)));
2192
- for (const type of builtinTypes) {
2193
- const patternStr = type.patterns[0] || "";
2194
- console.log(` ${chalk7.cyan(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk7.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
2195
- console.log(` ${chalk7.dim(" ".repeat(12) + type.description)}`);
2196
- }
2197
- console.log("");
2198
- }
2199
- if (customTypes.length > 0 && !options.builtinOnly) {
2200
- console.log(chalk7.bold("Custom Types"));
2201
- console.log(chalk7.dim("\u2500".repeat(70)));
2202
- for (const type of customTypes) {
2203
- const patternStr = type.patterns[0] || "";
2204
- console.log(` ${chalk7.green(type.name.padEnd(12))} ${patternStr.padEnd(30)} ${chalk7.dim(`TTL: ${formatTtl(type.defaultTtl)}`)}`);
2205
- console.log(` ${chalk7.dim(" ".repeat(12) + type.description)}`);
2206
- }
2207
- console.log("");
2208
- }
2209
- console.log(chalk7.dim(`Total: ${types.length} types (${builtinTypes.length} built-in, ${customTypes.length} custom)`));
2210
- } catch (error) {
2211
- console.error(chalk7.red("Error:"), error.message);
2212
- process.exit(1);
2213
- }
2214
- });
2215
- return cmd;
2216
- }
2217
-
2218
- // src/commands/types/show.ts
2219
- init_esm_shims();
2220
- function formatTtl2(seconds) {
2221
- if (seconds < 60) return `${seconds}s`;
2222
- if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
2223
- if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
2224
- return `${Math.floor(seconds / 86400)}d`;
2225
- }
2226
- function typesShowCommand() {
2227
- const cmd = new Command("show");
2228
- cmd.description("Show details for a specific type").argument("<name>", "Type name").option("--json", "Output as JSON").action(async (name, options) => {
2229
- try {
2230
- const client = await getClient();
2231
- const registry = client.getTypeRegistry();
2232
- const type = registry.get(name);
2233
- if (!type) {
2234
- console.error(chalk7.red("Error:"), `Type "${name}" not found.`);
2235
- console.log(chalk7.dim('Run "fractary codex types list" to see available types.'));
2236
- process.exit(1);
2237
- }
2238
- const isBuiltin = registry.isBuiltIn(name);
2239
- if (options.json) {
2240
- console.log(JSON.stringify({
2241
- name: type.name,
2242
- builtin: isBuiltin,
2243
- description: type.description,
2244
- patterns: type.patterns,
2245
- defaultTtl: type.defaultTtl,
2246
- ttl: formatTtl2(type.defaultTtl),
2247
- archiveAfterDays: type.archiveAfterDays,
2248
- archiveStorage: type.archiveStorage,
2249
- syncPatterns: type.syncPatterns,
2250
- excludePatterns: type.excludePatterns
2251
- }, null, 2));
2252
- return;
2253
- }
2254
- const nameColor = isBuiltin ? chalk7.cyan : chalk7.green;
2255
- console.log(chalk7.bold(`Type: ${nameColor(name)}
2256
- `));
2257
- console.log(` ${chalk7.dim("Source:")} ${isBuiltin ? "Built-in" : "Custom"}`);
2258
- console.log(` ${chalk7.dim("Description:")} ${type.description}`);
2259
- console.log(` ${chalk7.dim("TTL:")} ${formatTtl2(type.defaultTtl)} (${type.defaultTtl} seconds)`);
2260
- console.log("");
2261
- console.log(chalk7.bold("Patterns"));
2262
- for (const pattern of type.patterns) {
2263
- console.log(` ${chalk7.dim("\u2022")} ${pattern}`);
2264
- }
2265
- if (type.archiveAfterDays !== null) {
2266
- console.log("");
2267
- console.log(chalk7.bold("Archive Settings"));
2268
- console.log(` ${chalk7.dim("After:")} ${type.archiveAfterDays} days`);
2269
- console.log(` ${chalk7.dim("Storage:")} ${type.archiveStorage || "not set"}`);
2270
- }
2271
- if (type.syncPatterns && type.syncPatterns.length > 0) {
2272
- console.log("");
2273
- console.log(chalk7.bold("Sync Patterns"));
2274
- for (const pattern of type.syncPatterns) {
2275
- console.log(` ${chalk7.dim("\u2022")} ${pattern}`);
2276
- }
2277
- }
2278
- if (type.excludePatterns && type.excludePatterns.length > 0) {
2279
- console.log("");
2280
- console.log(chalk7.bold("Exclude Patterns"));
2281
- for (const pattern of type.excludePatterns) {
2282
- console.log(` ${chalk7.dim("\u2022")} ${pattern}`);
2283
- }
2284
- }
2285
- } catch (error) {
2286
- console.error(chalk7.red("Error:"), error.message);
2287
- process.exit(1);
2288
- }
2289
- });
2290
- return cmd;
2291
- }
2292
-
2293
- // src/commands/types/add.ts
2294
- init_esm_shims();
2295
- init_migrate_config();
2296
- function isValidTypeName(name) {
2297
- return /^[a-z][a-z0-9-]*$/.test(name);
2298
- }
2299
- function parseTtl(ttl) {
2300
- const match = ttl.match(/^(\d+)([smhd])$/);
2301
- if (!match) {
2302
- throw new Error("Invalid TTL format");
2303
- }
2304
- const value = parseInt(match[1], 10);
2305
- const unit = match[2];
2306
- switch (unit) {
2307
- case "s":
2308
- return value;
2309
- case "m":
2310
- return value * 60;
2311
- case "h":
2312
- return value * 3600;
2313
- case "d":
2314
- return value * 86400;
2315
- default:
2316
- throw new Error("Unknown TTL unit");
2317
- }
2318
- }
2319
- function formatTtl3(seconds) {
2320
- if (seconds < 60) return `${seconds}s`;
2321
- if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
2322
- if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
2323
- return `${Math.floor(seconds / 86400)}d`;
2324
- }
2325
- function typesAddCommand() {
2326
- const cmd = new Command("add");
2327
- cmd.description("Add a custom artifact type").argument("<name>", "Type name (lowercase, alphanumeric with hyphens)").requiredOption("--pattern <glob>", "File pattern (glob syntax)").option("--ttl <duration>", 'Cache TTL (e.g., "24h", "7d")', "24h").option("--description <text>", "Type description").option("--json", "Output as JSON").action(async (name, options) => {
2328
- try {
2329
- if (!isValidTypeName(name)) {
2330
- console.error(chalk7.red("Error:"), "Invalid type name.");
2331
- console.log(chalk7.dim("Type name must be lowercase, start with a letter, and contain only letters, numbers, and hyphens."));
2332
- process.exit(1);
2333
- }
2334
- const client = await getClient();
2335
- const registry = client.getTypeRegistry();
2336
- if (registry.isBuiltIn(name)) {
2337
- console.error(chalk7.red("Error:"), `Cannot override built-in type "${name}".`);
2338
- const builtinNames = registry.list().filter((t) => registry.isBuiltIn(t.name)).map((t) => t.name);
2339
- console.log(chalk7.dim("Built-in types: " + builtinNames.join(", ")));
2340
- process.exit(1);
2341
- }
2342
- if (registry.has(name)) {
2343
- console.error(chalk7.red("Error:"), `Custom type "${name}" already exists.`);
2344
- console.log(chalk7.dim('Use "fractary codex types remove" first to remove it.'));
2345
- process.exit(1);
2346
- }
2347
- let ttlSeconds;
2348
- try {
2349
- ttlSeconds = parseTtl(options.ttl);
2350
- } catch {
2351
- console.error(chalk7.red("Error:"), "Invalid TTL format.");
2352
- console.log(chalk7.dim("Expected format: <number><unit> where unit is s (seconds), m (minutes), h (hours), or d (days)"));
2353
- console.log(chalk7.dim("Examples: 30m, 24h, 7d"));
2354
- process.exit(1);
2355
- }
2356
- const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
2357
- const config = await readCodexConfig(configPath);
2358
- if (!config.types) {
2359
- config.types = { custom: {} };
2360
- }
2361
- if (!config.types.custom) {
2362
- config.types.custom = {};
2363
- }
2364
- config.types.custom[name] = {
2365
- description: options.description || `Custom type: ${name}`,
2366
- patterns: [options.pattern],
2367
- defaultTtl: ttlSeconds
2368
- };
2369
- await writeYamlConfig(config, configPath);
2370
- if (options.json) {
2371
- console.log(JSON.stringify({
2372
- success: true,
2373
- type: {
2374
- name,
2375
- description: config.types.custom[name].description,
2376
- patterns: config.types.custom[name].patterns,
2377
- defaultTtl: ttlSeconds,
2378
- ttl: formatTtl3(ttlSeconds),
2379
- builtin: false
2380
- },
2381
- message: "Custom type added successfully. Changes will take effect on next CLI invocation."
2382
- }, null, 2));
2383
- return;
2384
- }
2385
- console.log(chalk7.green("\u2713"), `Added custom type "${chalk7.cyan(name)}"`);
2386
- console.log("");
2387
- console.log(` ${chalk7.dim("Pattern:")} ${options.pattern}`);
2388
- console.log(` ${chalk7.dim("TTL:")} ${formatTtl3(ttlSeconds)} (${ttlSeconds} seconds)`);
2389
- if (options.description) {
2390
- console.log(` ${chalk7.dim("Description:")} ${options.description}`);
2391
- }
2392
- console.log("");
2393
- console.log(chalk7.dim("Note: Custom type will be available on next CLI invocation."));
2394
- } catch (error) {
2395
- console.error(chalk7.red("Error:"), error.message);
2396
- if (error.message.includes("Failed to load configuration")) {
2397
- console.log(chalk7.dim('\nRun "fractary codex init" to create a configuration.'));
2398
- }
2399
- process.exit(1);
2400
- }
2401
- });
2402
- return cmd;
2403
- }
2404
-
2405
- // src/commands/types/remove.ts
2406
- init_esm_shims();
2407
- init_migrate_config();
2408
- function typesRemoveCommand() {
2409
- const cmd = new Command("remove");
2410
- cmd.description("Remove a custom artifact type").argument("<name>", "Type name to remove").option("--json", "Output as JSON").option("--force", "Skip confirmation").action(async (name, options) => {
2411
- try {
2412
- const client = await getClient();
2413
- const registry = client.getTypeRegistry();
2414
- if (registry.isBuiltIn(name)) {
2415
- console.error(chalk7.red("Error:"), `Cannot remove built-in type "${name}".`);
2416
- console.log(chalk7.dim("Built-in types are permanent and cannot be removed."));
2417
- process.exit(1);
2418
- }
2419
- if (!registry.has(name)) {
2420
- console.error(chalk7.red("Error:"), `Custom type "${name}" not found.`);
2421
- console.log(chalk7.dim('Run "fractary codex types list --custom-only" to see custom types.'));
2422
- process.exit(1);
2423
- }
2424
- const typeInfo = registry.get(name);
2425
- const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
2426
- const config = await readCodexConfig(configPath);
2427
- if (!config.types?.custom?.[name]) {
2428
- console.error(chalk7.red("Error:"), `Custom type "${name}" not found in configuration.`);
2429
- process.exit(1);
2430
- }
2431
- delete config.types.custom[name];
2432
- if (Object.keys(config.types.custom).length === 0) {
2433
- delete config.types.custom;
2434
- }
2435
- if (config.types && Object.keys(config.types).length === 0) {
2436
- delete config.types;
2437
- }
2438
- await writeYamlConfig(config, configPath);
2439
- if (options.json) {
2440
- console.log(JSON.stringify({
2441
- success: true,
2442
- removed: {
2443
- name: typeInfo.name,
2444
- description: typeInfo.description,
2445
- patterns: typeInfo.patterns,
2446
- defaultTtl: typeInfo.defaultTtl
2447
- },
2448
- message: "Custom type removed successfully. Changes will take effect on next CLI invocation."
2449
- }, null, 2));
2450
- return;
2451
- }
2452
- console.log(chalk7.green("\u2713"), `Removed custom type "${chalk7.cyan(name)}"`);
2453
- console.log("");
2454
- console.log(chalk7.dim("Removed configuration:"));
2455
- console.log(` ${chalk7.dim("Pattern:")} ${typeInfo.patterns.join(", ")}`);
2456
- console.log(` ${chalk7.dim("Description:")} ${typeInfo.description}`);
2457
- console.log("");
2458
- console.log(chalk7.dim("Note: Custom type will be removed on next CLI invocation."));
2459
- } catch (error) {
2460
- console.error(chalk7.red("Error:"), error.message);
2461
- if (error.message.includes("Failed to load configuration")) {
2462
- console.log(chalk7.dim('\nRun "fractary codex init" to create a configuration.'));
2463
- }
1292
+ console.error(chalk10.red("Error:"), error.message);
2464
1293
  process.exit(1);
2465
1294
  }
2466
1295
  });
2467
1296
  return cmd;
2468
1297
  }
2469
-
2470
- // src/commands/types/index.ts
2471
- function typesCommand() {
2472
- const cmd = new Command("types");
2473
- cmd.description("Manage artifact type registry");
2474
- cmd.addCommand(typesListCommand());
2475
- cmd.addCommand(typesShowCommand());
2476
- cmd.addCommand(typesAddCommand());
2477
- cmd.addCommand(typesRemoveCommand());
2478
- return cmd;
2479
- }
2480
1298
  var __filename2 = fileURLToPath(import.meta.url);
2481
1299
  var __dirname2 = dirname(__filename2);
2482
1300
  var packageJson = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
@@ -2484,11 +1302,15 @@ var VERSION = packageJson.version;
2484
1302
  function createCLI() {
2485
1303
  const program = new Command("fractary-codex");
2486
1304
  program.description("Centralized knowledge management and distribution for AI agents").version(VERSION);
2487
- program.addCommand(documentCommand());
2488
- program.addCommand(configCommand());
2489
- program.addCommand(cacheCommand());
1305
+ program.addCommand(configInitializeCommand());
1306
+ program.addCommand(configUpdateCommand());
1307
+ program.addCommand(configValidateCommand());
1308
+ program.addCommand(documentFetchCommand());
1309
+ program.addCommand(cacheListCommand());
1310
+ program.addCommand(cacheClearCommand());
1311
+ program.addCommand(cacheStatsCommand());
1312
+ program.addCommand(cacheHealthCommand());
2490
1313
  program.addCommand(syncCommand());
2491
- program.addCommand(typesCommand());
2492
1314
  return program;
2493
1315
  }
2494
1316
  async function main() {