@fractary/codex 0.4.0 → 0.5.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/index.cjs CHANGED
@@ -1,19 +1,119 @@
1
1
  'use strict';
2
2
 
3
+ var micromatch3 = require('micromatch');
3
4
  var path3 = require('path');
4
5
  var child_process = require('child_process');
5
- var micromatch3 = require('micromatch');
6
6
  var zod = require('zod');
7
7
  var yaml = require('js-yaml');
8
8
  var fs2 = require('fs/promises');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
- var path3__default = /*#__PURE__*/_interopDefault(path3);
13
12
  var micromatch3__default = /*#__PURE__*/_interopDefault(micromatch3);
13
+ var path3__default = /*#__PURE__*/_interopDefault(path3);
14
14
  var yaml__default = /*#__PURE__*/_interopDefault(yaml);
15
15
  var fs2__default = /*#__PURE__*/_interopDefault(fs2);
16
16
 
17
+ var __defProp = Object.defineProperty;
18
+ var __getOwnPropNames = Object.getOwnPropertyNames;
19
+ var __esm = (fn, res) => function __init() {
20
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
21
+ };
22
+ var __export = (target, all) => {
23
+ for (var name in all)
24
+ __defProp(target, name, { get: all[name], enumerable: true });
25
+ };
26
+ function matchPattern(pattern, value) {
27
+ if (pattern === value) return true;
28
+ return micromatch3__default.default.isMatch(value, pattern);
29
+ }
30
+ function matchAnyPattern(patterns, value) {
31
+ if (patterns.length === 1 && patterns[0] === "*") {
32
+ return true;
33
+ }
34
+ if (patterns.length === 0) {
35
+ return false;
36
+ }
37
+ return patterns.some((pattern) => matchPattern(pattern, value));
38
+ }
39
+ function filterByPatterns(patterns, values) {
40
+ return values.filter((value) => matchAnyPattern(patterns, value));
41
+ }
42
+ function evaluatePatterns(options) {
43
+ const { value, include = [], exclude = [] } = options;
44
+ if (exclude.length > 0 && matchAnyPattern(exclude, value)) {
45
+ return false;
46
+ }
47
+ if (include.length === 0) {
48
+ return true;
49
+ }
50
+ return matchAnyPattern(include, value);
51
+ }
52
+ var init_matcher = __esm({
53
+ "src/core/patterns/matcher.ts"() {
54
+ }
55
+ });
56
+
57
+ // src/sync/directional-patterns.ts
58
+ var directional_patterns_exports = {};
59
+ __export(directional_patterns_exports, {
60
+ expandPlaceholders: () => expandPlaceholders,
61
+ extractProjectFromCodexPath: () => extractProjectFromCodexPath,
62
+ getRelativePath: () => getRelativePath,
63
+ matchFromCodexPattern: () => matchFromCodexPattern,
64
+ matchToCodexPattern: () => matchToCodexPattern
65
+ });
66
+ function matchToCodexPattern(filePath, patterns) {
67
+ if (!patterns || patterns.length === 0) {
68
+ return false;
69
+ }
70
+ return patterns.some((pattern) => matchPattern(pattern, filePath));
71
+ }
72
+ function matchFromCodexPattern(codexFilePath, patterns, targetProject) {
73
+ if (!patterns || patterns.length === 0) {
74
+ return false;
75
+ }
76
+ return patterns.some((pattern) => {
77
+ const projectSeparatorIndex = pattern.indexOf("/");
78
+ if (projectSeparatorIndex === -1) {
79
+ const fullPattern = `${targetProject}/${pattern}`;
80
+ return matchPattern(fullPattern, codexFilePath);
81
+ }
82
+ const firstSegment = pattern.substring(0, projectSeparatorIndex);
83
+ if (firstSegment.includes(".")) {
84
+ return matchPattern(pattern, codexFilePath);
85
+ } else {
86
+ const fullPattern = `${targetProject}/${pattern}`;
87
+ return matchPattern(fullPattern, codexFilePath);
88
+ }
89
+ });
90
+ }
91
+ function extractProjectFromCodexPath(codexFilePath) {
92
+ const firstSlashIndex = codexFilePath.indexOf("/");
93
+ if (firstSlashIndex === -1) {
94
+ return null;
95
+ }
96
+ return codexFilePath.substring(0, firstSlashIndex);
97
+ }
98
+ function getRelativePath(codexFilePath) {
99
+ const firstSlashIndex = codexFilePath.indexOf("/");
100
+ if (firstSlashIndex === -1) {
101
+ return null;
102
+ }
103
+ return codexFilePath.substring(firstSlashIndex + 1);
104
+ }
105
+ function expandPlaceholders(patterns, targetProject) {
106
+ if (!patterns) {
107
+ return patterns;
108
+ }
109
+ return patterns.map((pattern) => pattern.replace(/{project}/g, targetProject));
110
+ }
111
+ var init_directional_patterns = __esm({
112
+ "src/sync/directional-patterns.ts"() {
113
+ init_matcher();
114
+ }
115
+ });
116
+
17
117
  // src/errors/CodexError.ts
18
118
  var CodexError = class _CodexError extends Error {
19
119
  constructor(message, options) {
@@ -730,6 +830,16 @@ var SyncRulesSchema = zod.z.object({
730
830
  defaultInclude: zod.z.array(zod.z.string()).optional(),
731
831
  defaultExclude: zod.z.array(zod.z.string()).optional()
732
832
  });
833
+ var DirectionalSyncSchema = zod.z.object({
834
+ // Patterns for files to push from this project to codex
835
+ to_codex: zod.z.array(zod.z.string()).optional(),
836
+ // Patterns for files to pull from codex to this project
837
+ // Format: "project-name/path/pattern" or "project-name/**"
838
+ from_codex: zod.z.array(zod.z.string()).optional(),
839
+ // Org-level defaults (only in codex repository config)
840
+ default_to_codex: zod.z.array(zod.z.string()).optional(),
841
+ default_from_codex: zod.z.array(zod.z.string()).optional()
842
+ });
733
843
  var CodexConfigSchema = zod.z.object({
734
844
  organizationSlug: zod.z.string(),
735
845
  directories: zod.z.object({
@@ -737,7 +847,9 @@ var CodexConfigSchema = zod.z.object({
737
847
  target: zod.z.string().optional(),
738
848
  systems: zod.z.string().optional()
739
849
  }).optional(),
740
- rules: SyncRulesSchema.optional()
850
+ rules: SyncRulesSchema.optional(),
851
+ // Directional sync configuration
852
+ sync: DirectionalSyncSchema.optional()
741
853
  }).strict();
742
854
  function parseMetadata(content, options = {}) {
743
855
  const { strict = true, normalize = true } = options;
@@ -812,32 +924,9 @@ function extractRawFrontmatter(content) {
812
924
  const match = normalized.match(/^---\n([\s\S]*?)\n---\n/);
813
925
  return match && match[1] ? match[1] : null;
814
926
  }
815
- function matchPattern(pattern, value) {
816
- if (pattern === value) return true;
817
- return micromatch3__default.default.isMatch(value, pattern);
818
- }
819
- function matchAnyPattern(patterns, value) {
820
- if (patterns.length === 1 && patterns[0] === "*") {
821
- return true;
822
- }
823
- if (patterns.length === 0) {
824
- return false;
825
- }
826
- return patterns.some((pattern) => matchPattern(pattern, value));
827
- }
828
- function filterByPatterns(patterns, values) {
829
- return values.filter((value) => matchAnyPattern(patterns, value));
830
- }
831
- function evaluatePatterns(options) {
832
- const { value, include = [], exclude = [] } = options;
833
- if (exclude.length > 0 && matchAnyPattern(exclude, value)) {
834
- return false;
835
- }
836
- if (include.length === 0) {
837
- return true;
838
- }
839
- return matchAnyPattern(include, value);
840
- }
927
+
928
+ // src/core/patterns/index.ts
929
+ init_matcher();
841
930
 
842
931
  // src/core/config/organization.ts
843
932
  function resolveOrganization(options = {}) {
@@ -948,11 +1037,24 @@ function mergeConfigs(base, override) {
948
1037
  autoSyncPatterns: override.rules?.autoSyncPatterns ?? base.rules?.autoSyncPatterns,
949
1038
  defaultInclude: override.rules?.defaultInclude ?? base.rules?.defaultInclude,
950
1039
  defaultExclude: override.rules?.defaultExclude ?? base.rules?.defaultExclude
951
- }
1040
+ },
1041
+ // Only include sync if either base or override has sync config
1042
+ ...base.sync || override.sync ? {
1043
+ sync: {
1044
+ ...base.sync,
1045
+ ...override.sync,
1046
+ // Arrays are replaced, not merged
1047
+ to_codex: override.sync?.to_codex ?? base.sync?.to_codex,
1048
+ from_codex: override.sync?.from_codex ?? base.sync?.from_codex,
1049
+ default_to_codex: override.sync?.default_to_codex ?? base.sync?.default_to_codex,
1050
+ default_from_codex: override.sync?.default_from_codex ?? base.sync?.default_from_codex
1051
+ }
1052
+ } : {}
952
1053
  };
953
1054
  }
954
1055
 
955
1056
  // src/core/routing/evaluator.ts
1057
+ init_matcher();
956
1058
  function shouldSyncToRepo(options) {
957
1059
  const {
958
1060
  filePath,
@@ -2727,8 +2829,9 @@ async function scanCodexWithRouting(options) {
2727
2829
  rules,
2728
2830
  storage,
2729
2831
  skipNoFrontmatter = false,
2730
- maxFileSize = 10 * 1024 * 1024
2832
+ maxFileSize = 10 * 1024 * 1024,
2731
2833
  // 10MB default
2834
+ fromCodexPatterns
2732
2835
  } = options;
2733
2836
  const startTime = Date.now();
2734
2837
  const routedFiles = [];
@@ -2736,6 +2839,13 @@ async function scanCodexWithRouting(options) {
2736
2839
  const errors = [];
2737
2840
  let totalScanned = 0;
2738
2841
  let totalSkipped = 0;
2842
+ let expandedFromCodexPatterns = fromCodexPatterns;
2843
+ let matchFromCodexPattern2 = null;
2844
+ if (fromCodexPatterns && fromCodexPatterns.length > 0) {
2845
+ const module = await Promise.resolve().then(() => (init_directional_patterns(), directional_patterns_exports));
2846
+ matchFromCodexPattern2 = module.matchFromCodexPattern;
2847
+ expandedFromCodexPatterns = module.expandPlaceholders(fromCodexPatterns, targetProject);
2848
+ }
2739
2849
  const allFiles = await listAllFilesRecursive(codexDir);
2740
2850
  for (const filePath of allFiles) {
2741
2851
  totalScanned++;
@@ -2752,18 +2862,23 @@ async function scanCodexWithRouting(options) {
2752
2862
  }
2753
2863
  const content = await storage.readText(fullPath);
2754
2864
  const parseResult = parseMetadata(content, { strict: false });
2755
- if (skipNoFrontmatter && Object.keys(parseResult.metadata).length === 0) {
2865
+ if (!matchFromCodexPattern2 && skipNoFrontmatter && Object.keys(parseResult.metadata).length === 0) {
2756
2866
  totalSkipped++;
2757
2867
  continue;
2758
2868
  }
2759
2869
  const sourceProject = extractProjectFromPath(filePath, org);
2760
- const shouldSync = shouldSyncToRepo({
2761
- filePath,
2762
- fileMetadata: parseResult.metadata,
2763
- targetRepo: targetProject,
2764
- sourceRepo: sourceProject,
2765
- rules
2766
- });
2870
+ let shouldSync = false;
2871
+ if (matchFromCodexPattern2 && expandedFromCodexPatterns && expandedFromCodexPatterns.length > 0) {
2872
+ shouldSync = matchFromCodexPattern2(filePath, expandedFromCodexPatterns, targetProject);
2873
+ } else {
2874
+ shouldSync = shouldSyncToRepo({
2875
+ filePath,
2876
+ fileMetadata: parseResult.metadata,
2877
+ targetRepo: targetProject,
2878
+ sourceRepo: sourceProject,
2879
+ rules
2880
+ });
2881
+ }
2767
2882
  if (shouldSync) {
2768
2883
  const buffer = Buffer.from(content);
2769
2884
  const hash = calculateContentHash(buffer);
@@ -2914,7 +3029,16 @@ var SyncManager = class {
2914
3029
  * @param options - Sync options
2915
3030
  */
2916
3031
  async createPlan(_org, _project, sourceDir, targetFiles, options) {
2917
- const sourceFiles = await this.listLocalFiles(sourceDir);
3032
+ let sourceFiles = await this.listLocalFiles(sourceDir);
3033
+ if (options?.direction === "to-codex") {
3034
+ const toCodexPatterns = this.config.to_codex || this.config.default_to_codex;
3035
+ if (toCodexPatterns) {
3036
+ const { matchToCodexPattern: matchToCodexPattern2 } = await Promise.resolve().then(() => (init_directional_patterns(), directional_patterns_exports));
3037
+ sourceFiles = sourceFiles.filter(
3038
+ (file) => matchToCodexPattern2(file.path, toCodexPatterns)
3039
+ );
3040
+ }
3041
+ }
2918
3042
  const plan = createSyncPlan(
2919
3043
  sourceFiles,
2920
3044
  targetFiles,
@@ -2952,13 +3076,16 @@ var SyncManager = class {
2952
3076
  * ```
2953
3077
  */
2954
3078
  async createRoutingAwarePlan(org, project, codexDir, options) {
3079
+ const fromCodexPatterns = this.config.from_codex || this.config.default_from_codex;
2955
3080
  const routingScan = await scanCodexWithRouting({
2956
3081
  codexDir,
2957
3082
  targetProject: project,
2958
3083
  org,
2959
3084
  rules: void 0,
2960
3085
  // Use default routing rules (preventSelfSync, preventCodexSync, etc.)
2961
- storage: this.localStorage
3086
+ storage: this.localStorage,
3087
+ fromCodexPatterns
3088
+ // Use directional patterns if configured
2962
3089
  });
2963
3090
  const sourceFiles = routingScan.files.map((rf) => ({
2964
3091
  path: rf.path,
@@ -2967,7 +3094,15 @@ var SyncManager = class {
2967
3094
  hash: rf.hash
2968
3095
  }));
2969
3096
  const targetFiles = await this.listLocalFiles(process.cwd());
2970
- const plan = createSyncPlan(sourceFiles, targetFiles, options ?? {}, this.config);
3097
+ const planOptions = {
3098
+ direction: options?.direction,
3099
+ force: options?.force,
3100
+ dryRun: options?.dryRun
3101
+ // Explicitly exclude include/exclude to prevent double filtering
3102
+ // include: undefined,
3103
+ // exclude: undefined,
3104
+ };
3105
+ const plan = createSyncPlan(sourceFiles, targetFiles, planOptions, this.config);
2971
3106
  plan.estimatedTime = estimateSyncTime(plan);
2972
3107
  return {
2973
3108
  ...plan,