@fractary/codex 0.3.1 → 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;
@@ -785,6 +897,12 @@ function normalizeLegacyMetadata(parsed) {
785
897
  if (parsed.codex?.excludes && !parsed.codex_sync_exclude) {
786
898
  normalized.codex_sync_exclude = parsed.codex.excludes;
787
899
  }
900
+ if (parsed.codex_sync_includes && !normalized.codex_sync_include) {
901
+ normalized.codex_sync_include = parsed.codex_sync_includes;
902
+ }
903
+ if (parsed.codex_sync_excludes && !normalized.codex_sync_exclude) {
904
+ normalized.codex_sync_exclude = parsed.codex_sync_excludes;
905
+ }
788
906
  return normalized;
789
907
  }
790
908
  function hasFrontmatter(content) {
@@ -806,32 +924,9 @@ function extractRawFrontmatter(content) {
806
924
  const match = normalized.match(/^---\n([\s\S]*?)\n---\n/);
807
925
  return match && match[1] ? match[1] : null;
808
926
  }
809
- function matchPattern(pattern, value) {
810
- if (pattern === value) return true;
811
- return micromatch3__default.default.isMatch(value, pattern);
812
- }
813
- function matchAnyPattern(patterns, value) {
814
- if (patterns.length === 1 && patterns[0] === "*") {
815
- return true;
816
- }
817
- if (patterns.length === 0) {
818
- return false;
819
- }
820
- return patterns.some((pattern) => matchPattern(pattern, value));
821
- }
822
- function filterByPatterns(patterns, values) {
823
- return values.filter((value) => matchAnyPattern(patterns, value));
824
- }
825
- function evaluatePatterns(options) {
826
- const { value, include = [], exclude = [] } = options;
827
- if (exclude.length > 0 && matchAnyPattern(exclude, value)) {
828
- return false;
829
- }
830
- if (include.length === 0) {
831
- return true;
832
- }
833
- return matchAnyPattern(include, value);
834
- }
927
+
928
+ // src/core/patterns/index.ts
929
+ init_matcher();
835
930
 
836
931
  // src/core/config/organization.ts
837
932
  function resolveOrganization(options = {}) {
@@ -942,11 +1037,24 @@ function mergeConfigs(base, override) {
942
1037
  autoSyncPatterns: override.rules?.autoSyncPatterns ?? base.rules?.autoSyncPatterns,
943
1038
  defaultInclude: override.rules?.defaultInclude ?? base.rules?.defaultInclude,
944
1039
  defaultExclude: override.rules?.defaultExclude ?? base.rules?.defaultExclude
945
- }
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
+ } : {}
946
1053
  };
947
1054
  }
948
1055
 
949
1056
  // src/core/routing/evaluator.ts
1057
+ init_matcher();
950
1058
  function shouldSyncToRepo(options) {
951
1059
  const {
952
1060
  filePath,
@@ -2720,9 +2828,10 @@ async function scanCodexWithRouting(options) {
2720
2828
  org,
2721
2829
  rules,
2722
2830
  storage,
2723
- skipNoFrontmatter = true,
2724
- maxFileSize = 10 * 1024 * 1024
2831
+ skipNoFrontmatter = false,
2832
+ maxFileSize = 10 * 1024 * 1024,
2725
2833
  // 10MB default
2834
+ fromCodexPatterns
2726
2835
  } = options;
2727
2836
  const startTime = Date.now();
2728
2837
  const routedFiles = [];
@@ -2730,14 +2839,17 @@ async function scanCodexWithRouting(options) {
2730
2839
  const errors = [];
2731
2840
  let totalScanned = 0;
2732
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
+ }
2733
2849
  const allFiles = await listAllFilesRecursive(codexDir);
2734
2850
  for (const filePath of allFiles) {
2735
2851
  totalScanned++;
2736
2852
  try {
2737
- if (!filePath.endsWith(".md")) {
2738
- totalSkipped++;
2739
- continue;
2740
- }
2741
2853
  const fullPath = path3__default.default.join(codexDir, filePath);
2742
2854
  const stats = await fs2__default.default.stat(fullPath);
2743
2855
  if (stats.size > maxFileSize) {
@@ -2750,18 +2862,23 @@ async function scanCodexWithRouting(options) {
2750
2862
  }
2751
2863
  const content = await storage.readText(fullPath);
2752
2864
  const parseResult = parseMetadata(content, { strict: false });
2753
- if (skipNoFrontmatter && Object.keys(parseResult.metadata).length === 0) {
2865
+ if (!matchFromCodexPattern2 && skipNoFrontmatter && Object.keys(parseResult.metadata).length === 0) {
2754
2866
  totalSkipped++;
2755
2867
  continue;
2756
2868
  }
2757
2869
  const sourceProject = extractProjectFromPath(filePath, org);
2758
- const shouldSync = shouldSyncToRepo({
2759
- filePath,
2760
- fileMetadata: parseResult.metadata,
2761
- targetRepo: targetProject,
2762
- sourceRepo: sourceProject,
2763
- rules
2764
- });
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
+ }
2765
2882
  if (shouldSync) {
2766
2883
  const buffer = Buffer.from(content);
2767
2884
  const hash = calculateContentHash(buffer);
@@ -2912,7 +3029,16 @@ var SyncManager = class {
2912
3029
  * @param options - Sync options
2913
3030
  */
2914
3031
  async createPlan(_org, _project, sourceDir, targetFiles, options) {
2915
- 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
+ }
2916
3042
  const plan = createSyncPlan(
2917
3043
  sourceFiles,
2918
3044
  targetFiles,
@@ -2950,13 +3076,16 @@ var SyncManager = class {
2950
3076
  * ```
2951
3077
  */
2952
3078
  async createRoutingAwarePlan(org, project, codexDir, options) {
3079
+ const fromCodexPatterns = this.config.from_codex || this.config.default_from_codex;
2953
3080
  const routingScan = await scanCodexWithRouting({
2954
3081
  codexDir,
2955
3082
  targetProject: project,
2956
3083
  org,
2957
3084
  rules: void 0,
2958
3085
  // Use default routing rules (preventSelfSync, preventCodexSync, etc.)
2959
- storage: this.localStorage
3086
+ storage: this.localStorage,
3087
+ fromCodexPatterns
3088
+ // Use directional patterns if configured
2960
3089
  });
2961
3090
  const sourceFiles = routingScan.files.map((rf) => ({
2962
3091
  path: rf.path,
@@ -2965,7 +3094,15 @@ var SyncManager = class {
2965
3094
  hash: rf.hash
2966
3095
  }));
2967
3096
  const targetFiles = await this.listLocalFiles(process.cwd());
2968
- 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);
2969
3106
  plan.estimatedTime = estimateSyncTime(plan);
2970
3107
  return {
2971
3108
  ...plan,