@fractary/codex 0.4.0 → 0.5.1

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/index.d.cts CHANGED
@@ -408,6 +408,22 @@ declare const CodexConfigSchema: z.ZodObject<{
408
408
  defaultInclude?: string[] | undefined;
409
409
  defaultExclude?: string[] | undefined;
410
410
  }>>;
411
+ sync: z.ZodOptional<z.ZodObject<{
412
+ to_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
413
+ from_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
414
+ default_to_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
415
+ default_from_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
416
+ }, "strip", z.ZodTypeAny, {
417
+ to_codex?: string[] | undefined;
418
+ from_codex?: string[] | undefined;
419
+ default_to_codex?: string[] | undefined;
420
+ default_from_codex?: string[] | undefined;
421
+ }, {
422
+ to_codex?: string[] | undefined;
423
+ from_codex?: string[] | undefined;
424
+ default_to_codex?: string[] | undefined;
425
+ default_from_codex?: string[] | undefined;
426
+ }>>;
411
427
  }, "strict", z.ZodTypeAny, {
412
428
  organizationSlug: string;
413
429
  directories?: {
@@ -427,6 +443,12 @@ declare const CodexConfigSchema: z.ZodObject<{
427
443
  defaultInclude?: string[] | undefined;
428
444
  defaultExclude?: string[] | undefined;
429
445
  } | undefined;
446
+ sync?: {
447
+ to_codex?: string[] | undefined;
448
+ from_codex?: string[] | undefined;
449
+ default_to_codex?: string[] | undefined;
450
+ default_from_codex?: string[] | undefined;
451
+ } | undefined;
430
452
  }, {
431
453
  organizationSlug: string;
432
454
  directories?: {
@@ -446,6 +468,12 @@ declare const CodexConfigSchema: z.ZodObject<{
446
468
  defaultInclude?: string[] | undefined;
447
469
  defaultExclude?: string[] | undefined;
448
470
  } | undefined;
471
+ sync?: {
472
+ to_codex?: string[] | undefined;
473
+ from_codex?: string[] | undefined;
474
+ default_to_codex?: string[] | undefined;
475
+ default_from_codex?: string[] | undefined;
476
+ } | undefined;
449
477
  }>;
450
478
  type CodexConfig = z.infer<typeof CodexConfigSchema>;
451
479
 
@@ -848,6 +876,11 @@ interface SyncConfig {
848
876
  defaultExcludes: string[];
849
877
  deleteOrphans: boolean;
850
878
  conflictStrategy: 'newest' | 'local' | 'remote' | 'manual';
879
+ to_codex?: string[];
880
+ from_codex?: string[];
881
+ default_to_codex?: string[];
882
+ default_from_codex?: string[];
883
+ exclude?: string[];
851
884
  }
852
885
  declare const DEFAULT_SYNC_CONFIG: SyncConfig;
853
886
 
package/dist/index.d.ts CHANGED
@@ -408,6 +408,22 @@ declare const CodexConfigSchema: z.ZodObject<{
408
408
  defaultInclude?: string[] | undefined;
409
409
  defaultExclude?: string[] | undefined;
410
410
  }>>;
411
+ sync: z.ZodOptional<z.ZodObject<{
412
+ to_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
413
+ from_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
414
+ default_to_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
415
+ default_from_codex: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
416
+ }, "strip", z.ZodTypeAny, {
417
+ to_codex?: string[] | undefined;
418
+ from_codex?: string[] | undefined;
419
+ default_to_codex?: string[] | undefined;
420
+ default_from_codex?: string[] | undefined;
421
+ }, {
422
+ to_codex?: string[] | undefined;
423
+ from_codex?: string[] | undefined;
424
+ default_to_codex?: string[] | undefined;
425
+ default_from_codex?: string[] | undefined;
426
+ }>>;
411
427
  }, "strict", z.ZodTypeAny, {
412
428
  organizationSlug: string;
413
429
  directories?: {
@@ -427,6 +443,12 @@ declare const CodexConfigSchema: z.ZodObject<{
427
443
  defaultInclude?: string[] | undefined;
428
444
  defaultExclude?: string[] | undefined;
429
445
  } | undefined;
446
+ sync?: {
447
+ to_codex?: string[] | undefined;
448
+ from_codex?: string[] | undefined;
449
+ default_to_codex?: string[] | undefined;
450
+ default_from_codex?: string[] | undefined;
451
+ } | undefined;
430
452
  }, {
431
453
  organizationSlug: string;
432
454
  directories?: {
@@ -446,6 +468,12 @@ declare const CodexConfigSchema: z.ZodObject<{
446
468
  defaultInclude?: string[] | undefined;
447
469
  defaultExclude?: string[] | undefined;
448
470
  } | undefined;
471
+ sync?: {
472
+ to_codex?: string[] | undefined;
473
+ from_codex?: string[] | undefined;
474
+ default_to_codex?: string[] | undefined;
475
+ default_from_codex?: string[] | undefined;
476
+ } | undefined;
449
477
  }>;
450
478
  type CodexConfig = z.infer<typeof CodexConfigSchema>;
451
479
 
@@ -848,6 +876,11 @@ interface SyncConfig {
848
876
  defaultExcludes: string[];
849
877
  deleteOrphans: boolean;
850
878
  conflictStrategy: 'newest' | 'local' | 'remote' | 'manual';
879
+ to_codex?: string[];
880
+ from_codex?: string[];
881
+ default_to_codex?: string[];
882
+ default_from_codex?: string[];
883
+ exclude?: string[];
851
884
  }
852
885
  declare const DEFAULT_SYNC_CONFIG: SyncConfig;
853
886
 
package/dist/index.js CHANGED
@@ -1,10 +1,113 @@
1
+ import micromatch3 from 'micromatch';
1
2
  import path3 from 'path';
2
3
  import { execSync } from 'child_process';
3
- import micromatch3 from 'micromatch';
4
4
  import { z } from 'zod';
5
5
  import yaml from 'js-yaml';
6
6
  import fs2 from 'fs/promises';
7
7
 
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __esm = (fn, res) => function __init() {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ function matchPattern(pattern, value) {
18
+ if (pattern === value) return true;
19
+ return micromatch3.isMatch(value, pattern);
20
+ }
21
+ function matchAnyPattern(patterns, value) {
22
+ if (patterns.length === 1 && patterns[0] === "*") {
23
+ return true;
24
+ }
25
+ if (patterns.length === 0) {
26
+ return false;
27
+ }
28
+ return patterns.some((pattern) => matchPattern(pattern, value));
29
+ }
30
+ function filterByPatterns(patterns, values) {
31
+ return values.filter((value) => matchAnyPattern(patterns, value));
32
+ }
33
+ function evaluatePatterns(options) {
34
+ const { value, include = [], exclude = [] } = options;
35
+ if (exclude.length > 0 && matchAnyPattern(exclude, value)) {
36
+ return false;
37
+ }
38
+ if (include.length === 0) {
39
+ return true;
40
+ }
41
+ return matchAnyPattern(include, value);
42
+ }
43
+ var init_matcher = __esm({
44
+ "src/core/patterns/matcher.ts"() {
45
+ }
46
+ });
47
+
48
+ // src/sync/directional-patterns.ts
49
+ var directional_patterns_exports = {};
50
+ __export(directional_patterns_exports, {
51
+ expandPlaceholders: () => expandPlaceholders,
52
+ extractProjectFromCodexPath: () => extractProjectFromCodexPath,
53
+ getRelativePath: () => getRelativePath,
54
+ matchFromCodexPattern: () => matchFromCodexPattern,
55
+ matchToCodexPattern: () => matchToCodexPattern
56
+ });
57
+ function matchToCodexPattern(filePath, patterns) {
58
+ if (!patterns || patterns.length === 0) {
59
+ return false;
60
+ }
61
+ return patterns.some((pattern) => matchPattern(pattern, filePath));
62
+ }
63
+ function matchFromCodexPattern(codexFilePath, patterns, targetProject) {
64
+ if (!patterns || patterns.length === 0) {
65
+ return false;
66
+ }
67
+ return patterns.some((pattern) => {
68
+ if (pattern.startsWith("projects/")) {
69
+ return matchPattern(pattern, codexFilePath);
70
+ }
71
+ const projectSeparatorIndex = pattern.indexOf("/");
72
+ if (projectSeparatorIndex === -1) {
73
+ const fullPattern = `${targetProject}/${pattern}`;
74
+ return matchPattern(fullPattern, codexFilePath);
75
+ }
76
+ const firstSegment = pattern.substring(0, projectSeparatorIndex);
77
+ if (firstSegment.includes(".")) {
78
+ return matchPattern(pattern, codexFilePath);
79
+ } else {
80
+ const fullPattern = `${targetProject}/${pattern}`;
81
+ return matchPattern(fullPattern, codexFilePath);
82
+ }
83
+ });
84
+ }
85
+ function extractProjectFromCodexPath(codexFilePath) {
86
+ const firstSlashIndex = codexFilePath.indexOf("/");
87
+ if (firstSlashIndex === -1) {
88
+ return null;
89
+ }
90
+ return codexFilePath.substring(0, firstSlashIndex);
91
+ }
92
+ function getRelativePath(codexFilePath) {
93
+ const firstSlashIndex = codexFilePath.indexOf("/");
94
+ if (firstSlashIndex === -1) {
95
+ return null;
96
+ }
97
+ return codexFilePath.substring(firstSlashIndex + 1);
98
+ }
99
+ function expandPlaceholders(patterns, targetProject) {
100
+ if (!patterns) {
101
+ return patterns;
102
+ }
103
+ return patterns.map((pattern) => pattern.replace(/{project}/g, targetProject));
104
+ }
105
+ var init_directional_patterns = __esm({
106
+ "src/sync/directional-patterns.ts"() {
107
+ init_matcher();
108
+ }
109
+ });
110
+
8
111
  // src/errors/CodexError.ts
9
112
  var CodexError = class _CodexError extends Error {
10
113
  constructor(message, options) {
@@ -721,6 +824,16 @@ var SyncRulesSchema = z.object({
721
824
  defaultInclude: z.array(z.string()).optional(),
722
825
  defaultExclude: z.array(z.string()).optional()
723
826
  });
827
+ var DirectionalSyncSchema = z.object({
828
+ // Patterns for files to push from this project to codex
829
+ to_codex: z.array(z.string()).optional(),
830
+ // Patterns for files to pull from codex to this project
831
+ // Format: "project-name/path/pattern" or "project-name/**"
832
+ from_codex: z.array(z.string()).optional(),
833
+ // Org-level defaults (only in codex repository config)
834
+ default_to_codex: z.array(z.string()).optional(),
835
+ default_from_codex: z.array(z.string()).optional()
836
+ });
724
837
  var CodexConfigSchema = z.object({
725
838
  organizationSlug: z.string(),
726
839
  directories: z.object({
@@ -728,7 +841,9 @@ var CodexConfigSchema = z.object({
728
841
  target: z.string().optional(),
729
842
  systems: z.string().optional()
730
843
  }).optional(),
731
- rules: SyncRulesSchema.optional()
844
+ rules: SyncRulesSchema.optional(),
845
+ // Directional sync configuration
846
+ sync: DirectionalSyncSchema.optional()
732
847
  }).strict();
733
848
  function parseMetadata(content, options = {}) {
734
849
  const { strict = true, normalize = true } = options;
@@ -803,32 +918,9 @@ function extractRawFrontmatter(content) {
803
918
  const match = normalized.match(/^---\n([\s\S]*?)\n---\n/);
804
919
  return match && match[1] ? match[1] : null;
805
920
  }
806
- function matchPattern(pattern, value) {
807
- if (pattern === value) return true;
808
- return micromatch3.isMatch(value, pattern);
809
- }
810
- function matchAnyPattern(patterns, value) {
811
- if (patterns.length === 1 && patterns[0] === "*") {
812
- return true;
813
- }
814
- if (patterns.length === 0) {
815
- return false;
816
- }
817
- return patterns.some((pattern) => matchPattern(pattern, value));
818
- }
819
- function filterByPatterns(patterns, values) {
820
- return values.filter((value) => matchAnyPattern(patterns, value));
821
- }
822
- function evaluatePatterns(options) {
823
- const { value, include = [], exclude = [] } = options;
824
- if (exclude.length > 0 && matchAnyPattern(exclude, value)) {
825
- return false;
826
- }
827
- if (include.length === 0) {
828
- return true;
829
- }
830
- return matchAnyPattern(include, value);
831
- }
921
+
922
+ // src/core/patterns/index.ts
923
+ init_matcher();
832
924
 
833
925
  // src/core/config/organization.ts
834
926
  function resolveOrganization(options = {}) {
@@ -939,11 +1031,24 @@ function mergeConfigs(base, override) {
939
1031
  autoSyncPatterns: override.rules?.autoSyncPatterns ?? base.rules?.autoSyncPatterns,
940
1032
  defaultInclude: override.rules?.defaultInclude ?? base.rules?.defaultInclude,
941
1033
  defaultExclude: override.rules?.defaultExclude ?? base.rules?.defaultExclude
942
- }
1034
+ },
1035
+ // Only include sync if either base or override has sync config
1036
+ ...base.sync || override.sync ? {
1037
+ sync: {
1038
+ ...base.sync,
1039
+ ...override.sync,
1040
+ // Arrays are replaced, not merged
1041
+ to_codex: override.sync?.to_codex ?? base.sync?.to_codex,
1042
+ from_codex: override.sync?.from_codex ?? base.sync?.from_codex,
1043
+ default_to_codex: override.sync?.default_to_codex ?? base.sync?.default_to_codex,
1044
+ default_from_codex: override.sync?.default_from_codex ?? base.sync?.default_from_codex
1045
+ }
1046
+ } : {}
943
1047
  };
944
1048
  }
945
1049
 
946
1050
  // src/core/routing/evaluator.ts
1051
+ init_matcher();
947
1052
  function shouldSyncToRepo(options) {
948
1053
  const {
949
1054
  filePath,
@@ -2718,8 +2823,9 @@ async function scanCodexWithRouting(options) {
2718
2823
  rules,
2719
2824
  storage,
2720
2825
  skipNoFrontmatter = false,
2721
- maxFileSize = 10 * 1024 * 1024
2826
+ maxFileSize = 10 * 1024 * 1024,
2722
2827
  // 10MB default
2828
+ fromCodexPatterns
2723
2829
  } = options;
2724
2830
  const startTime = Date.now();
2725
2831
  const routedFiles = [];
@@ -2727,6 +2833,13 @@ async function scanCodexWithRouting(options) {
2727
2833
  const errors = [];
2728
2834
  let totalScanned = 0;
2729
2835
  let totalSkipped = 0;
2836
+ let expandedFromCodexPatterns = fromCodexPatterns;
2837
+ let matchFromCodexPattern2 = null;
2838
+ if (fromCodexPatterns && fromCodexPatterns.length > 0) {
2839
+ const module = await Promise.resolve().then(() => (init_directional_patterns(), directional_patterns_exports));
2840
+ matchFromCodexPattern2 = module.matchFromCodexPattern;
2841
+ expandedFromCodexPatterns = module.expandPlaceholders(fromCodexPatterns, targetProject);
2842
+ }
2730
2843
  const allFiles = await listAllFilesRecursive(codexDir);
2731
2844
  for (const filePath of allFiles) {
2732
2845
  totalScanned++;
@@ -2743,18 +2856,23 @@ async function scanCodexWithRouting(options) {
2743
2856
  }
2744
2857
  const content = await storage.readText(fullPath);
2745
2858
  const parseResult = parseMetadata(content, { strict: false });
2746
- if (skipNoFrontmatter && Object.keys(parseResult.metadata).length === 0) {
2859
+ if (!matchFromCodexPattern2 && skipNoFrontmatter && Object.keys(parseResult.metadata).length === 0) {
2747
2860
  totalSkipped++;
2748
2861
  continue;
2749
2862
  }
2750
2863
  const sourceProject = extractProjectFromPath(filePath, org);
2751
- const shouldSync = shouldSyncToRepo({
2752
- filePath,
2753
- fileMetadata: parseResult.metadata,
2754
- targetRepo: targetProject,
2755
- sourceRepo: sourceProject,
2756
- rules
2757
- });
2864
+ let shouldSync = false;
2865
+ if (matchFromCodexPattern2 && expandedFromCodexPatterns && expandedFromCodexPatterns.length > 0) {
2866
+ shouldSync = matchFromCodexPattern2(filePath, expandedFromCodexPatterns, targetProject);
2867
+ } else {
2868
+ shouldSync = shouldSyncToRepo({
2869
+ filePath,
2870
+ fileMetadata: parseResult.metadata,
2871
+ targetRepo: targetProject,
2872
+ sourceRepo: sourceProject,
2873
+ rules
2874
+ });
2875
+ }
2758
2876
  if (shouldSync) {
2759
2877
  const buffer = Buffer.from(content);
2760
2878
  const hash = calculateContentHash(buffer);
@@ -2794,6 +2912,14 @@ async function scanCodexWithRouting(options) {
2794
2912
  function extractProjectFromPath(filePath, org) {
2795
2913
  const normalizedPath = filePath.replace(/\\/g, "/");
2796
2914
  const withoutOrg = normalizedPath.startsWith(`${org}/`) ? normalizedPath.slice(org.length + 1) : normalizedPath;
2915
+ if (withoutOrg.startsWith("projects/")) {
2916
+ const afterProjects = withoutOrg.slice("projects/".length);
2917
+ const firstSlash2 = afterProjects.indexOf("/");
2918
+ if (firstSlash2 === -1) {
2919
+ return afterProjects;
2920
+ }
2921
+ return afterProjects.slice(0, firstSlash2);
2922
+ }
2797
2923
  const firstSlash = withoutOrg.indexOf("/");
2798
2924
  if (firstSlash === -1) {
2799
2925
  return withoutOrg;
@@ -2905,7 +3031,16 @@ var SyncManager = class {
2905
3031
  * @param options - Sync options
2906
3032
  */
2907
3033
  async createPlan(_org, _project, sourceDir, targetFiles, options) {
2908
- const sourceFiles = await this.listLocalFiles(sourceDir);
3034
+ let sourceFiles = await this.listLocalFiles(sourceDir);
3035
+ if (options?.direction === "to-codex") {
3036
+ const toCodexPatterns = this.config.to_codex || this.config.default_to_codex;
3037
+ if (toCodexPatterns) {
3038
+ const { matchToCodexPattern: matchToCodexPattern2 } = await Promise.resolve().then(() => (init_directional_patterns(), directional_patterns_exports));
3039
+ sourceFiles = sourceFiles.filter(
3040
+ (file) => matchToCodexPattern2(file.path, toCodexPatterns)
3041
+ );
3042
+ }
3043
+ }
2909
3044
  const plan = createSyncPlan(
2910
3045
  sourceFiles,
2911
3046
  targetFiles,
@@ -2943,13 +3078,16 @@ var SyncManager = class {
2943
3078
  * ```
2944
3079
  */
2945
3080
  async createRoutingAwarePlan(org, project, codexDir, options) {
3081
+ const fromCodexPatterns = this.config.from_codex || this.config.default_from_codex;
2946
3082
  const routingScan = await scanCodexWithRouting({
2947
3083
  codexDir,
2948
3084
  targetProject: project,
2949
3085
  org,
2950
3086
  rules: void 0,
2951
3087
  // Use default routing rules (preventSelfSync, preventCodexSync, etc.)
2952
- storage: this.localStorage
3088
+ storage: this.localStorage,
3089
+ fromCodexPatterns
3090
+ // Use directional patterns if configured
2953
3091
  });
2954
3092
  const sourceFiles = routingScan.files.map((rf) => ({
2955
3093
  path: rf.path,
@@ -2958,7 +3096,15 @@ var SyncManager = class {
2958
3096
  hash: rf.hash
2959
3097
  }));
2960
3098
  const targetFiles = await this.listLocalFiles(process.cwd());
2961
- const plan = createSyncPlan(sourceFiles, targetFiles, options ?? {}, this.config);
3099
+ const planOptions = {
3100
+ direction: options?.direction,
3101
+ force: options?.force,
3102
+ dryRun: options?.dryRun
3103
+ // Explicitly exclude include/exclude to prevent double filtering
3104
+ // include: undefined,
3105
+ // exclude: undefined,
3106
+ };
3107
+ const plan = createSyncPlan(sourceFiles, targetFiles, planOptions, this.config);
2962
3108
  plan.estimatedTime = estimateSyncTime(plan);
2963
3109
  return {
2964
3110
  ...plan,