@lnai/core 0.5.0 → 0.6.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.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const UNIFIED_DIR = ".ai";
4
- declare const TOOL_IDS: readonly ["claudeCode", "opencode", "cursor", "copilot", "windsurf", "gemini"];
4
+ declare const TOOL_IDS: readonly ["claudeCode", "opencode", "cursor", "copilot", "windsurf", "gemini", "codex"];
5
5
  type ToolId = (typeof TOOL_IDS)[number];
6
6
  declare const CONFIG_FILES: {
7
7
  readonly config: "config.json";
@@ -69,6 +69,7 @@ declare const toolIdSchema: z.ZodEnum<{
69
69
  copilot: "copilot";
70
70
  windsurf: "windsurf";
71
71
  gemini: "gemini";
72
+ codex: "codex";
72
73
  }>;
73
74
  /** Settings configuration (Claude format as source of truth) */
74
75
  declare const settingsSchema: z.ZodObject<{
@@ -116,6 +117,10 @@ declare const configSchema: z.ZodObject<{
116
117
  enabled: z.ZodBoolean;
117
118
  versionControl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
118
119
  }, z.core.$strip>>;
120
+ codex: z.ZodOptional<z.ZodObject<{
121
+ enabled: z.ZodBoolean;
122
+ versionControl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
123
+ }, z.core.$strip>>;
119
124
  }, z.core.$strip>>;
120
125
  }, z.core.$strip>;
121
126
  /** Skill frontmatter (name and description required) */
@@ -249,6 +254,18 @@ interface Plugin {
249
254
  */
250
255
  declare const claudeCodePlugin: Plugin;
251
256
 
257
+ /**
258
+ * Codex plugin for exporting to .codex/ format
259
+ *
260
+ * Output structure:
261
+ * - AGENTS.md (symlink -> .ai/AGENTS.md) [at project root]
262
+ * - <dir>/AGENTS.md (generated from .ai/rules/*.md, per glob directory)
263
+ * - .codex/skills/<name>/ (symlink -> ../../.ai/skills/<name>)
264
+ * - .codex/config.toml (generated from settings.mcpServers)
265
+ * - .codex/<path> (symlink -> ../.ai/.codex/<path>) for override files
266
+ */
267
+ declare const codexPlugin: Plugin;
268
+
252
269
  /**
253
270
  * OpenCode plugin for exporting to opencode.json format
254
271
  *
@@ -320,4 +337,4 @@ declare function initUnifiedConfig(options: InitOptions): Promise<InitResult>;
320
337
  declare function hasUnifiedConfig(rootDir: string): Promise<boolean>;
321
338
  declare function generateDefaultConfig(tools?: ToolId[], versionControl?: Record<ToolId, boolean>): Config;
322
339
 
323
- export { CONFIG_DIRS, CONFIG_FILES, type ChangeResult, type Config, FileNotFoundError, type InitOptions, type InitResult, LnaiError, type MarkdownFile, type MarkdownFrontmatter, type McpServer, type OutputFile, ParseError, type PermissionLevel, type Permissions, type Plugin, PluginError, type RuleFrontmatter, type Settings, type SkillFrontmatter, type SkippedFeatureDetail, type SyncOptions, type SyncResult, TOOL_IDS, TOOL_OUTPUT_DIRS, type ToolConfig, type ToolId, UNIFIED_DIR, type UnifiedState, ValidationError, type ValidationErrorDetail, type ValidationResult, type ValidationWarningDetail, WriteError, type WriterOptions, claudeCodePlugin, computeHash, configSchema, generateDefaultConfig, hasUnifiedConfig, initUnifiedConfig, mcpServerSchema, opencodePlugin, parseFrontmatter, parseUnifiedConfig, permissionsSchema, pluginRegistry, ruleFrontmatterSchema, runSyncPipeline, settingsSchema, skillFrontmatterSchema, toolConfigSchema, toolIdSchema, updateGitignore, validateConfig, validateSettings, validateUnifiedState, writeFiles };
340
+ export { CONFIG_DIRS, CONFIG_FILES, type ChangeResult, type Config, FileNotFoundError, type InitOptions, type InitResult, LnaiError, type MarkdownFile, type MarkdownFrontmatter, type McpServer, type OutputFile, ParseError, type PermissionLevel, type Permissions, type Plugin, PluginError, type RuleFrontmatter, type Settings, type SkillFrontmatter, type SkippedFeatureDetail, type SyncOptions, type SyncResult, TOOL_IDS, TOOL_OUTPUT_DIRS, type ToolConfig, type ToolId, UNIFIED_DIR, type UnifiedState, ValidationError, type ValidationErrorDetail, type ValidationResult, type ValidationWarningDetail, WriteError, type WriterOptions, claudeCodePlugin, codexPlugin, computeHash, configSchema, generateDefaultConfig, hasUnifiedConfig, initUnifiedConfig, mcpServerSchema, opencodePlugin, parseFrontmatter, parseUnifiedConfig, permissionsSchema, pluginRegistry, ruleFrontmatterSchema, runSyncPipeline, settingsSchema, skillFrontmatterSchema, toolConfigSchema, toolIdSchema, updateGitignore, validateConfig, validateSettings, validateUnifiedState, writeFiles };
package/dist/index.js CHANGED
@@ -12,7 +12,8 @@ var TOOL_IDS = [
12
12
  "cursor",
13
13
  "copilot",
14
14
  "windsurf",
15
- "gemini"
15
+ "gemini",
16
+ "codex"
16
17
  ];
17
18
  var CONFIG_FILES = {
18
19
  config: "config.json",
@@ -30,7 +31,8 @@ var TOOL_OUTPUT_DIRS = {
30
31
  cursor: ".cursor",
31
32
  copilot: ".github",
32
33
  windsurf: ".windsurf",
33
- gemini: ".gemini"
34
+ gemini: ".gemini",
35
+ codex: ".codex"
34
36
  };
35
37
  var OVERRIDE_DIRS = {
36
38
  claudeCode: ".claude",
@@ -38,7 +40,8 @@ var OVERRIDE_DIRS = {
38
40
  cursor: ".cursor",
39
41
  copilot: ".copilot",
40
42
  windsurf: ".windsurf",
41
- gemini: ".gemini"
43
+ gemini: ".gemini",
44
+ codex: ".codex"
42
45
  };
43
46
 
44
47
  // src/errors.ts
@@ -124,7 +127,8 @@ var toolIdSchema = z.enum([
124
127
  "cursor",
125
128
  "copilot",
126
129
  "windsurf",
127
- "gemini"
130
+ "gemini",
131
+ "codex"
128
132
  ]);
129
133
  var settingsSchema = z.object({
130
134
  permissions: permissionsSchema.optional(),
@@ -137,7 +141,8 @@ var configSchema = z.object({
137
141
  cursor: toolConfigSchema,
138
142
  copilot: toolConfigSchema,
139
143
  windsurf: toolConfigSchema,
140
- gemini: toolConfigSchema
144
+ gemini: toolConfigSchema,
145
+ codex: toolConfigSchema
141
146
  }).partial().optional()
142
147
  });
143
148
  var skillFrontmatterSchema = z.object({
@@ -501,6 +506,183 @@ var claudeCodePlugin = {
501
506
  return { valid: true, errors: [], warnings, skipped: [] };
502
507
  }
503
508
  };
509
+ function getDirFromGlob(glob) {
510
+ const cleanPath = glob.replace(/(\*\*|\*|\{.*,.*\}).*$/, "");
511
+ const dir = cleanPath.replace(/\/$/, "");
512
+ if (dir === glob) {
513
+ const dirname4 = path.dirname(dir);
514
+ return dirname4 === "." && !dir.includes("/") ? "." : dirname4;
515
+ }
516
+ if (!dir) {
517
+ return ".";
518
+ }
519
+ return dir;
520
+ }
521
+ function groupRulesByDirectory(rules) {
522
+ const rulesMap = /* @__PURE__ */ new Map();
523
+ const addedRules = /* @__PURE__ */ new Map();
524
+ for (const rule of rules) {
525
+ for (const pathGlob of rule.frontmatter.paths) {
526
+ const dir = getDirFromGlob(pathGlob);
527
+ if (!rulesMap.has(dir)) {
528
+ rulesMap.set(dir, []);
529
+ addedRules.set(dir, /* @__PURE__ */ new Set());
530
+ }
531
+ if (addedRules.get(dir)?.has(rule.path)) {
532
+ continue;
533
+ }
534
+ addedRules.get(dir)?.add(rule.path);
535
+ const content = `## ${rule.path}
536
+
537
+ ${rule.content}
538
+ `;
539
+ rulesMap.get(dir)?.push(content);
540
+ }
541
+ }
542
+ return rulesMap;
543
+ }
544
+
545
+ // src/plugins/codex/index.ts
546
+ var codexPlugin = {
547
+ id: "codex",
548
+ name: "Codex",
549
+ async detect(_rootDir) {
550
+ return false;
551
+ },
552
+ async import(_rootDir) {
553
+ return null;
554
+ },
555
+ async export(state, rootDir) {
556
+ const files = [];
557
+ const outputDir = TOOL_OUTPUT_DIRS.codex;
558
+ if (state.agents) {
559
+ files.push({
560
+ path: "AGENTS.md",
561
+ type: "symlink",
562
+ target: `${UNIFIED_DIR}/AGENTS.md`
563
+ });
564
+ }
565
+ const rulesMap = groupRulesByDirectory(state.rules);
566
+ for (const [dir, contents] of rulesMap.entries()) {
567
+ if (dir === ".") {
568
+ continue;
569
+ }
570
+ const combinedContent = contents.join("\n---\n\n");
571
+ files.push({
572
+ path: `${dir}/AGENTS.md`,
573
+ type: "text",
574
+ content: combinedContent
575
+ });
576
+ }
577
+ for (const skill of state.skills) {
578
+ files.push({
579
+ path: `${outputDir}/skills/${skill.path}`,
580
+ type: "symlink",
581
+ target: `../../${UNIFIED_DIR}/skills/${skill.path}`
582
+ });
583
+ }
584
+ const configToml = buildCodexConfigToml(state.settings?.mcpServers);
585
+ if (configToml) {
586
+ files.push({
587
+ path: `${outputDir}/config.toml`,
588
+ type: "text",
589
+ content: configToml
590
+ });
591
+ }
592
+ return applyFileOverrides(files, rootDir, "codex");
593
+ },
594
+ validate(state) {
595
+ const warnings = [];
596
+ const skipped = [];
597
+ if (!state.agents) {
598
+ warnings.push({
599
+ path: ["AGENTS.md"],
600
+ message: "No AGENTS.md found - root AGENTS.md will not be created"
601
+ });
602
+ }
603
+ const rulesMap = groupRulesByDirectory(state.rules);
604
+ if (rulesMap.has(".")) {
605
+ warnings.push({
606
+ path: ["rules"],
607
+ message: "Rules with root globs are not exported - Codex only receives subdirectory AGENTS.md files"
608
+ });
609
+ }
610
+ const mcpServers = state.settings?.mcpServers;
611
+ if (mcpServers) {
612
+ for (const [name, server] of Object.entries(mcpServers)) {
613
+ if (!server.command && !server.url) {
614
+ warnings.push({
615
+ path: ["settings", "mcpServers", name],
616
+ message: `MCP server "${name}" has no command or url - it will be skipped`
617
+ });
618
+ }
619
+ }
620
+ }
621
+ if (state.settings?.permissions) {
622
+ const hasPermissions = (state.settings.permissions.allow?.length ?? 0) > 0 || (state.settings.permissions.ask?.length ?? 0) > 0 || (state.settings.permissions.deny?.length ?? 0) > 0;
623
+ if (hasPermissions) {
624
+ skipped.push({
625
+ feature: "permissions",
626
+ reason: "Codex rules are not generated from LNAI permissions"
627
+ });
628
+ }
629
+ }
630
+ return { valid: true, errors: [], warnings, skipped };
631
+ }
632
+ };
633
+ function buildCodexConfigToml(mcpServers) {
634
+ if (!mcpServers || Object.keys(mcpServers).length === 0) {
635
+ return void 0;
636
+ }
637
+ const lines = [];
638
+ for (const [name, server] of Object.entries(mcpServers)) {
639
+ const hasCommand = !!server.command;
640
+ const hasUrl = !!server.url;
641
+ if (!hasCommand && !hasUrl) {
642
+ continue;
643
+ }
644
+ lines.push(`[mcp_servers.${formatTomlKey(name)}]`);
645
+ if (server.command) {
646
+ lines.push(`command = ${formatTomlString(server.command)}`);
647
+ if (server.args && server.args.length > 0) {
648
+ lines.push(`args = ${formatTomlArray(server.args)}`);
649
+ }
650
+ if (server.env && Object.keys(server.env).length > 0) {
651
+ lines.push(`env = ${formatTomlInlineTable(server.env)}`);
652
+ }
653
+ }
654
+ if (server.url) {
655
+ lines.push(`url = ${formatTomlString(server.url)}`);
656
+ if (server.headers && Object.keys(server.headers).length > 0) {
657
+ lines.push(`http_headers = ${formatTomlInlineTable(server.headers)}`);
658
+ }
659
+ }
660
+ lines.push("");
661
+ }
662
+ if (lines.length === 0) {
663
+ return void 0;
664
+ }
665
+ return `${lines.join("\n").trimEnd()}
666
+ `;
667
+ }
668
+ function formatTomlString(value) {
669
+ return JSON.stringify(value);
670
+ }
671
+ function formatTomlArray(values) {
672
+ return `[${values.map(formatTomlString).join(", ")}]`;
673
+ }
674
+ function formatTomlKey(key) {
675
+ if (/^[A-Za-z0-9_-]+$/.test(key)) {
676
+ return key;
677
+ }
678
+ return JSON.stringify(key);
679
+ }
680
+ function formatTomlInlineTable(values) {
681
+ const entries = Object.entries(values).map(
682
+ ([key, value]) => `${formatTomlKey(key)} = ${formatTomlString(value)}`
683
+ );
684
+ return `{ ${entries.join(", ")} }`;
685
+ }
504
686
 
505
687
  // src/utils/transforms.ts
506
688
  var ENV_VAR_PATTERN = /\$\{([^}:]+)(:-[^}]*)?\}/g;
@@ -907,18 +1089,6 @@ function buildCliContent(permissions) {
907
1089
  }
908
1090
  return { permissions: permissionsResult.permissions };
909
1091
  }
910
- function getDirFromGlob(glob) {
911
- const cleanPath = glob.replace(/(\*\*|\*|\{.*,.*\}).*$/, "");
912
- const dir = cleanPath.replace(/\/$/, "");
913
- if (dir === glob) {
914
- const dirname4 = path.dirname(dir);
915
- return dirname4 === "." && !dir.includes("/") ? "." : dirname4;
916
- }
917
- if (!dir) {
918
- return ".";
919
- }
920
- return dir;
921
- }
922
1092
 
923
1093
  // src/plugins/gemini/transforms.ts
924
1094
  function transformMcpToGemini(mcpServers) {
@@ -941,23 +1111,6 @@ function transformMcpToGemini(mcpServers) {
941
1111
  }
942
1112
  return Object.keys(geminiMcp).length > 0 ? geminiMcp : void 0;
943
1113
  }
944
- function groupRulesByDirectory(rules) {
945
- const rulesMap = /* @__PURE__ */ new Map();
946
- for (const rule of rules) {
947
- for (const pathGlob of rule.frontmatter.paths) {
948
- const dir = getDirFromGlob(pathGlob);
949
- if (!rulesMap.has(dir)) {
950
- rulesMap.set(dir, []);
951
- }
952
- const content = `## ${rule.path}
953
-
954
- ${rule.content}
955
- `;
956
- rulesMap.get(dir)?.push(content);
957
- }
958
- }
959
- return rulesMap;
960
- }
961
1114
 
962
1115
  // src/plugins/gemini/index.ts
963
1116
  var geminiPlugin = {
@@ -1304,6 +1457,7 @@ var windsurfPlugin = {
1304
1457
  pluginRegistry.register(claudeCodePlugin);
1305
1458
  pluginRegistry.register(copilotPlugin);
1306
1459
  pluginRegistry.register(cursorPlugin);
1460
+ pluginRegistry.register(codexPlugin);
1307
1461
  pluginRegistry.register(opencodePlugin);
1308
1462
  pluginRegistry.register(windsurfPlugin);
1309
1463
  pluginRegistry.register(geminiPlugin);
@@ -1570,4 +1724,4 @@ function generateDefaultConfig(tools, versionControl) {
1570
1724
  return { tools: toolsConfig };
1571
1725
  }
1572
1726
 
1573
- export { CONFIG_DIRS, CONFIG_FILES, FileNotFoundError, LnaiError, ParseError, PluginError, TOOL_IDS, TOOL_OUTPUT_DIRS, UNIFIED_DIR, ValidationError, WriteError, claudeCodePlugin, computeHash, configSchema, generateDefaultConfig, hasUnifiedConfig, initUnifiedConfig, mcpServerSchema, opencodePlugin, parseFrontmatter, parseUnifiedConfig, permissionsSchema, pluginRegistry, ruleFrontmatterSchema, runSyncPipeline, settingsSchema, skillFrontmatterSchema, toolConfigSchema, toolIdSchema, updateGitignore, validateConfig, validateSettings, validateUnifiedState, writeFiles };
1727
+ export { CONFIG_DIRS, CONFIG_FILES, FileNotFoundError, LnaiError, ParseError, PluginError, TOOL_IDS, TOOL_OUTPUT_DIRS, UNIFIED_DIR, ValidationError, WriteError, claudeCodePlugin, codexPlugin, computeHash, configSchema, generateDefaultConfig, hasUnifiedConfig, initUnifiedConfig, mcpServerSchema, opencodePlugin, parseFrontmatter, parseUnifiedConfig, permissionsSchema, pluginRegistry, ruleFrontmatterSchema, runSyncPipeline, settingsSchema, skillFrontmatterSchema, toolConfigSchema, toolIdSchema, updateGitignore, validateConfig, validateSettings, validateUnifiedState, writeFiles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lnai/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Core library for LNAI - unified AI config management",
5
5
  "type": "module",
6
6
  "license": "MIT",