@fractary/codex-cli 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +186 -53
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +183 -50
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as
|
|
2
|
+
import * as path5 from 'path';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
@@ -161,7 +161,7 @@ async function migrateConfig(legacyConfigPath, options) {
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
async function writeYamlConfig(config, outputPath) {
|
|
164
|
-
const dir =
|
|
164
|
+
const dir = path5.dirname(outputPath);
|
|
165
165
|
await fs.mkdir(dir, { recursive: true });
|
|
166
166
|
const yamlContent = yaml.dump(config, {
|
|
167
167
|
indent: 2,
|
|
@@ -385,7 +385,7 @@ var init_codex_client = __esm({
|
|
|
385
385
|
const { readYamlConfig: readYamlConfig2 } = await Promise.resolve().then(() => (init_migrate_config(), migrate_config_exports));
|
|
386
386
|
const { resolveEnvVarsInConfig: resolveEnvVarsInConfig2 } = await Promise.resolve().then(() => (init_config_types(), config_types_exports));
|
|
387
387
|
try {
|
|
388
|
-
const configPath =
|
|
388
|
+
const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
389
389
|
let config;
|
|
390
390
|
try {
|
|
391
391
|
config = await readYamlConfig2(configPath);
|
|
@@ -655,7 +655,7 @@ function getTempCodexPath(config) {
|
|
|
655
655
|
const codexRepo = config.codex_repository || "codex";
|
|
656
656
|
const sanitizedOrg = sanitizePathComponent(config.organization);
|
|
657
657
|
const sanitizedRepo = sanitizePathComponent(codexRepo);
|
|
658
|
-
return
|
|
658
|
+
return path5.join(
|
|
659
659
|
os.tmpdir(),
|
|
660
660
|
"fractary-codex-clone",
|
|
661
661
|
`${sanitizedOrg}-${sanitizedRepo}-${process.pid}`
|
|
@@ -663,7 +663,7 @@ function getTempCodexPath(config) {
|
|
|
663
663
|
}
|
|
664
664
|
async function isValidGitRepo(repoPath) {
|
|
665
665
|
try {
|
|
666
|
-
const gitDir =
|
|
666
|
+
const gitDir = path5.join(repoPath, ".git");
|
|
667
667
|
const stats = await fs.stat(gitDir);
|
|
668
668
|
return stats.isDirectory();
|
|
669
669
|
} catch {
|
|
@@ -696,7 +696,7 @@ async function execGit(repoPath, args) {
|
|
|
696
696
|
}
|
|
697
697
|
}
|
|
698
698
|
async function gitClone(url, targetPath, options) {
|
|
699
|
-
const parentDir =
|
|
699
|
+
const parentDir = path5.dirname(targetPath);
|
|
700
700
|
await fs.mkdir(parentDir, { recursive: true });
|
|
701
701
|
const args = ["clone"];
|
|
702
702
|
if (options?.depth) {
|
|
@@ -868,7 +868,129 @@ init_esm_shims();
|
|
|
868
868
|
|
|
869
869
|
// src/commands/config/init.ts
|
|
870
870
|
init_esm_shims();
|
|
871
|
-
|
|
871
|
+
|
|
872
|
+
// src/config/unified-config.ts
|
|
873
|
+
init_esm_shims();
|
|
874
|
+
function sanitizeForS3BucketName(name) {
|
|
875
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-").substring(0, 63);
|
|
876
|
+
}
|
|
877
|
+
function getDefaultUnifiedConfig(organization, project) {
|
|
878
|
+
const sanitizedProject = sanitizeForS3BucketName(project);
|
|
879
|
+
return {
|
|
880
|
+
file: {
|
|
881
|
+
schema_version: "2.0",
|
|
882
|
+
sources: {
|
|
883
|
+
specs: {
|
|
884
|
+
type: "s3",
|
|
885
|
+
bucket: `${sanitizedProject}-files`,
|
|
886
|
+
prefix: "specs/",
|
|
887
|
+
region: "us-east-1",
|
|
888
|
+
local: {
|
|
889
|
+
base_path: ".fractary/specs"
|
|
890
|
+
},
|
|
891
|
+
push: {
|
|
892
|
+
compress: false,
|
|
893
|
+
keep_local: true
|
|
894
|
+
},
|
|
895
|
+
auth: {
|
|
896
|
+
profile: "default"
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
logs: {
|
|
900
|
+
type: "s3",
|
|
901
|
+
bucket: `${sanitizedProject}-files`,
|
|
902
|
+
prefix: "logs/",
|
|
903
|
+
region: "us-east-1",
|
|
904
|
+
local: {
|
|
905
|
+
base_path: ".fractary/logs"
|
|
906
|
+
},
|
|
907
|
+
push: {
|
|
908
|
+
compress: true,
|
|
909
|
+
keep_local: true
|
|
910
|
+
},
|
|
911
|
+
auth: {
|
|
912
|
+
profile: "default"
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
codex: {
|
|
918
|
+
schema_version: "2.0",
|
|
919
|
+
organization,
|
|
920
|
+
project,
|
|
921
|
+
dependencies: {}
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
async function readUnifiedConfig(configPath) {
|
|
926
|
+
try {
|
|
927
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
928
|
+
const config = yaml.load(content);
|
|
929
|
+
return config;
|
|
930
|
+
} catch (error) {
|
|
931
|
+
if (error.code === "ENOENT") {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
throw error;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
async function writeUnifiedConfig(config, outputPath) {
|
|
938
|
+
const dir = path5.dirname(outputPath);
|
|
939
|
+
await fs.mkdir(dir, { recursive: true });
|
|
940
|
+
const yamlContent = yaml.dump(config, {
|
|
941
|
+
indent: 2,
|
|
942
|
+
lineWidth: 120,
|
|
943
|
+
noRefs: true,
|
|
944
|
+
sortKeys: false
|
|
945
|
+
});
|
|
946
|
+
await fs.writeFile(outputPath, yamlContent, "utf-8");
|
|
947
|
+
}
|
|
948
|
+
function mergeUnifiedConfigs(existing, updates) {
|
|
949
|
+
const merged = {};
|
|
950
|
+
if (updates.file || existing.file) {
|
|
951
|
+
merged.file = {
|
|
952
|
+
schema_version: updates.file?.schema_version || existing.file?.schema_version || "2.0",
|
|
953
|
+
sources: {
|
|
954
|
+
...existing.file?.sources || {},
|
|
955
|
+
...updates.file?.sources || {}
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
if (updates.codex || existing.codex) {
|
|
960
|
+
merged.codex = {
|
|
961
|
+
schema_version: updates.codex?.schema_version || existing.codex?.schema_version || "2.0",
|
|
962
|
+
organization: updates.codex?.organization || existing.codex?.organization || "default",
|
|
963
|
+
project: updates.codex?.project || existing.codex?.project || "default",
|
|
964
|
+
dependencies: {
|
|
965
|
+
...existing.codex?.dependencies || {},
|
|
966
|
+
...updates.codex?.dependencies || {}
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
return merged;
|
|
971
|
+
}
|
|
972
|
+
async function initializeUnifiedConfig(configPath, organization, project, options) {
|
|
973
|
+
const existingConfig = await readUnifiedConfig(configPath);
|
|
974
|
+
if (existingConfig && !options?.force) {
|
|
975
|
+
const defaultConfig = getDefaultUnifiedConfig(organization, project);
|
|
976
|
+
const merged = mergeUnifiedConfigs(existingConfig, defaultConfig);
|
|
977
|
+
await writeUnifiedConfig(merged, configPath);
|
|
978
|
+
return {
|
|
979
|
+
created: false,
|
|
980
|
+
merged: true,
|
|
981
|
+
config: merged
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
const config = getDefaultUnifiedConfig(organization, project);
|
|
985
|
+
await writeUnifiedConfig(config, configPath);
|
|
986
|
+
return {
|
|
987
|
+
created: true,
|
|
988
|
+
merged: false,
|
|
989
|
+
config
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// src/commands/config/init.ts
|
|
872
994
|
async function getOrgFromGitRemote() {
|
|
873
995
|
try {
|
|
874
996
|
const { execSync } = __require("child_process");
|
|
@@ -890,9 +1012,9 @@ async function fileExists(filePath) {
|
|
|
890
1012
|
}
|
|
891
1013
|
function initCommand() {
|
|
892
1014
|
const cmd = new Command("init");
|
|
893
|
-
cmd.description("Initialize
|
|
1015
|
+
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("--force", "Overwrite existing configuration").action(async (options) => {
|
|
894
1016
|
try {
|
|
895
|
-
console.log(chalk8.blue("Initializing
|
|
1017
|
+
console.log(chalk8.blue("Initializing unified Fractary configuration...\n"));
|
|
896
1018
|
let org = options.org;
|
|
897
1019
|
if (!org) {
|
|
898
1020
|
org = await getOrgFromGitRemote();
|
|
@@ -901,60 +1023,71 @@ function initCommand() {
|
|
|
901
1023
|
try {
|
|
902
1024
|
const { resolveOrganization } = await import('@fractary/codex');
|
|
903
1025
|
org = resolveOrganization({
|
|
904
|
-
repoName:
|
|
1026
|
+
repoName: path5.basename(process.cwd())
|
|
905
1027
|
});
|
|
906
1028
|
} catch {
|
|
907
1029
|
}
|
|
908
1030
|
}
|
|
909
1031
|
if (!org) {
|
|
910
|
-
org =
|
|
1032
|
+
org = path5.basename(process.cwd()).split("-")[0] || "default";
|
|
911
1033
|
console.log(chalk8.yellow(`\u26A0 Could not detect organization, using: ${org}`));
|
|
912
1034
|
console.log(chalk8.dim(" Use --org <slug> to specify explicitly\n"));
|
|
913
1035
|
} else {
|
|
914
1036
|
console.log(chalk8.dim(`Organization: ${chalk8.cyan(org)}
|
|
915
1037
|
`));
|
|
916
1038
|
}
|
|
917
|
-
|
|
918
|
-
|
|
1039
|
+
let project = options.project;
|
|
1040
|
+
if (!project) {
|
|
1041
|
+
project = path5.basename(process.cwd());
|
|
1042
|
+
console.log(chalk8.dim(`Project: ${chalk8.cyan(project)}
|
|
1043
|
+
`));
|
|
1044
|
+
}
|
|
1045
|
+
const configPath = path5.join(process.cwd(), ".fractary", "config.yaml");
|
|
919
1046
|
const configExists = await fileExists(configPath);
|
|
920
1047
|
if (configExists && !options.force) {
|
|
921
|
-
console.log(chalk8.yellow(
|
|
922
|
-
console.log(chalk8.dim("
|
|
923
|
-
process.exit(1);
|
|
1048
|
+
console.log(chalk8.yellow(`\u26A0 Configuration already exists at .fractary/config.yaml`));
|
|
1049
|
+
console.log(chalk8.dim("Merging with existing configuration...\n"));
|
|
924
1050
|
}
|
|
925
1051
|
console.log("Creating directory structure...");
|
|
926
1052
|
const dirs = [
|
|
1053
|
+
".fractary",
|
|
1054
|
+
".fractary/specs",
|
|
1055
|
+
".fractary/logs",
|
|
927
1056
|
".fractary/codex",
|
|
928
1057
|
".fractary/codex/cache"
|
|
929
1058
|
];
|
|
930
1059
|
for (const dir of dirs) {
|
|
931
|
-
await fs.mkdir(
|
|
1060
|
+
await fs.mkdir(path5.join(process.cwd(), dir), { recursive: true });
|
|
932
1061
|
console.log(chalk8.green("\u2713"), chalk8.dim(dir + "/"));
|
|
933
1062
|
}
|
|
934
|
-
console.log("\
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
1063
|
+
console.log("\nInitializing configuration...");
|
|
1064
|
+
const result = await initializeUnifiedConfig(
|
|
1065
|
+
configPath,
|
|
1066
|
+
org,
|
|
1067
|
+
project,
|
|
1068
|
+
{ force: options.force }
|
|
1069
|
+
);
|
|
1070
|
+
if (result.created) {
|
|
1071
|
+
console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/config.yaml (created)"));
|
|
1072
|
+
} else if (result.merged) {
|
|
1073
|
+
console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/config.yaml (merged with existing)"));
|
|
938
1074
|
}
|
|
939
|
-
|
|
940
|
-
console.log(chalk8.green("\u2713"), chalk8.dim(".fractary/codex/config.yaml"));
|
|
941
|
-
console.log(chalk8.green("\n\u2713 Codex v4.0 initialized successfully!\n"));
|
|
1075
|
+
console.log(chalk8.green("\n\u2713 Unified configuration initialized successfully!\n"));
|
|
942
1076
|
console.log(chalk8.bold("Configuration:"));
|
|
943
1077
|
console.log(chalk8.dim(` Organization: ${org}`));
|
|
944
|
-
console.log(chalk8.dim(`
|
|
945
|
-
console.log(chalk8.dim(` Config: .fractary/
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
console.log(chalk8.bold("\
|
|
950
|
-
console.log(chalk8.dim(" -
|
|
951
|
-
console.log(chalk8.dim(" -
|
|
952
|
-
console.log(chalk8.dim(" - HTTP endpoint"));
|
|
1078
|
+
console.log(chalk8.dim(` Project: ${project}`));
|
|
1079
|
+
console.log(chalk8.dim(` Config: .fractary/config.yaml`));
|
|
1080
|
+
console.log(chalk8.bold("\nFile plugin sources:"));
|
|
1081
|
+
console.log(chalk8.dim(" - specs: .fractary/specs/ \u2192 S3"));
|
|
1082
|
+
console.log(chalk8.dim(" - logs: .fractary/logs/ \u2192 S3"));
|
|
1083
|
+
console.log(chalk8.bold("\nCodex plugin:"));
|
|
1084
|
+
console.log(chalk8.dim(" - Cache: .fractary/codex/cache/"));
|
|
1085
|
+
console.log(chalk8.dim(" - Dependencies: (none configured)"));
|
|
953
1086
|
console.log(chalk8.bold("\nNext steps:"));
|
|
954
|
-
console.log(chalk8.dim(
|
|
955
|
-
console.log(chalk8.dim(" 2. Edit .fractary/
|
|
956
|
-
console.log(chalk8.dim(" 3.
|
|
957
|
-
console.log(chalk8.dim(" 4.
|
|
1087
|
+
console.log(chalk8.dim(" 1. Configure AWS credentials for S3 access"));
|
|
1088
|
+
console.log(chalk8.dim(" 2. Edit .fractary/config.yaml to add external project dependencies"));
|
|
1089
|
+
console.log(chalk8.dim(" 3. Access current project files: codex://specs/SPEC-001.md"));
|
|
1090
|
+
console.log(chalk8.dim(" 4. Access external projects: codex://org/project/docs/README.md"));
|
|
958
1091
|
} catch (error) {
|
|
959
1092
|
console.error(chalk8.red("Error:"), error.message);
|
|
960
1093
|
process.exit(1);
|
|
@@ -981,8 +1114,8 @@ function migrateCommand() {
|
|
|
981
1114
|
const cmd = new Command("migrate");
|
|
982
1115
|
cmd.description("Migrate legacy JSON configuration to v3.0 YAML format").option("--dry-run", "Show migration plan without executing").option("--no-backup", "Skip creating backup of old config").option("--json", "Output as JSON").action(async (options) => {
|
|
983
1116
|
try {
|
|
984
|
-
const legacyConfigPath =
|
|
985
|
-
const newConfigPath =
|
|
1117
|
+
const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
1118
|
+
const newConfigPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
986
1119
|
if (!await fileExists2(legacyConfigPath)) {
|
|
987
1120
|
if (options.json) {
|
|
988
1121
|
console.log(JSON.stringify({
|
|
@@ -1077,7 +1210,7 @@ function migrateCommand() {
|
|
|
1077
1210
|
}
|
|
1078
1211
|
if (!options.dryRun) {
|
|
1079
1212
|
await writeYamlConfig(migrationResult.yamlConfig, newConfigPath);
|
|
1080
|
-
const cacheDir =
|
|
1213
|
+
const cacheDir = path5.join(process.cwd(), ".codex-cache");
|
|
1081
1214
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
1082
1215
|
if (!options.json) {
|
|
1083
1216
|
console.log(chalk8.green("\u2713"), "YAML configuration created");
|
|
@@ -1098,7 +1231,7 @@ function migrateCommand() {
|
|
|
1098
1231
|
console.log(chalk8.dim(" 3. Test fetching: fractary codex fetch codex://org/project/path"));
|
|
1099
1232
|
if (migrationResult.backupPath) {
|
|
1100
1233
|
console.log("");
|
|
1101
|
-
console.log(chalk8.dim(`Backup saved: ${
|
|
1234
|
+
console.log(chalk8.dim(`Backup saved: ${path5.basename(migrationResult.backupPath)}`));
|
|
1102
1235
|
}
|
|
1103
1236
|
}
|
|
1104
1237
|
}
|
|
@@ -1308,8 +1441,8 @@ async function fileExists3(filePath) {
|
|
|
1308
1441
|
}
|
|
1309
1442
|
}
|
|
1310
1443
|
async function checkConfiguration() {
|
|
1311
|
-
const configPath =
|
|
1312
|
-
const legacyConfigPath =
|
|
1444
|
+
const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
1445
|
+
const legacyConfigPath = path5.join(process.cwd(), ".fractary", "plugins", "codex", "config.json");
|
|
1313
1446
|
try {
|
|
1314
1447
|
if (!await fileExists3(configPath)) {
|
|
1315
1448
|
if (await fileExists3(legacyConfigPath)) {
|
|
@@ -1416,7 +1549,7 @@ async function checkCache() {
|
|
|
1416
1549
|
}
|
|
1417
1550
|
}
|
|
1418
1551
|
async function checkStorage() {
|
|
1419
|
-
const configPath =
|
|
1552
|
+
const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
1420
1553
|
try {
|
|
1421
1554
|
const config = await readYamlConfig(configPath);
|
|
1422
1555
|
const providers = config.storage || [];
|
|
@@ -1574,7 +1707,7 @@ function syncCommand() {
|
|
|
1574
1707
|
const cmd = new Command("sync");
|
|
1575
1708
|
cmd.description("Sync single project with codex repository").argument("[name]", "Project name (auto-detected if not provided)").option("--env <env>", "Target environment (dev/test/staging/prod)", "prod").option("--dry-run", "Show what would sync without executing").option("--direction <dir>", "Sync direction (to-codex/from-codex/bidirectional)", "bidirectional").option("--include <pattern>", "Include files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--exclude <pattern>", "Exclude files matching pattern (can be used multiple times)", (val, prev) => prev.concat([val]), []).option("--force", "Force sync without checking timestamps").option("--json", "Output as JSON").action(async (name, options) => {
|
|
1576
1709
|
try {
|
|
1577
|
-
const configPath =
|
|
1710
|
+
const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
1578
1711
|
let config;
|
|
1579
1712
|
try {
|
|
1580
1713
|
config = await readYamlConfig(configPath);
|
|
@@ -1608,7 +1741,7 @@ function syncCommand() {
|
|
|
1608
1741
|
const syncManager = createSyncManager({
|
|
1609
1742
|
localStorage,
|
|
1610
1743
|
config: config.sync,
|
|
1611
|
-
manifestPath:
|
|
1744
|
+
manifestPath: path5.join(process.cwd(), ".fractary", ".codex-sync-manifest.json")
|
|
1612
1745
|
});
|
|
1613
1746
|
const defaultToCodexPatterns = [
|
|
1614
1747
|
"docs/**/*.md",
|
|
@@ -1659,8 +1792,8 @@ function syncCommand() {
|
|
|
1659
1792
|
}
|
|
1660
1793
|
const targetFiles = await Promise.all(
|
|
1661
1794
|
Array.from(matchedFilePaths).map(async (filePath) => {
|
|
1662
|
-
const fullPath =
|
|
1663
|
-
const stats = await import('fs/promises').then((
|
|
1795
|
+
const fullPath = path5.join(sourceDir, filePath);
|
|
1796
|
+
const stats = await import('fs/promises').then((fs8) => fs8.stat(fullPath));
|
|
1664
1797
|
return {
|
|
1665
1798
|
path: filePath,
|
|
1666
1799
|
size: stats.size,
|
|
@@ -2108,7 +2241,7 @@ function typesAddCommand() {
|
|
|
2108
2241
|
console.log(chalk8.dim("Examples: 30m, 24h, 7d"));
|
|
2109
2242
|
process.exit(1);
|
|
2110
2243
|
}
|
|
2111
|
-
const configPath =
|
|
2244
|
+
const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
2112
2245
|
const config = await readYamlConfig(configPath);
|
|
2113
2246
|
if (!config.types) {
|
|
2114
2247
|
config.types = { custom: {} };
|
|
@@ -2177,7 +2310,7 @@ function typesRemoveCommand() {
|
|
|
2177
2310
|
process.exit(1);
|
|
2178
2311
|
}
|
|
2179
2312
|
const typeInfo = registry.get(name);
|
|
2180
|
-
const configPath =
|
|
2313
|
+
const configPath = path5.join(process.cwd(), ".fractary", "codex", "config.yaml");
|
|
2181
2314
|
const config = await readYamlConfig(configPath);
|
|
2182
2315
|
if (!config.types?.custom?.[name]) {
|
|
2183
2316
|
console.error(chalk8.red("Error:"), `Custom type "${name}" not found in configuration.`);
|