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