@lnai/core 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.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"];
4
+ declare const TOOL_IDS: readonly ["claudeCode", "opencode", "cursor", "copilot", "windsurf", "gemini"];
5
5
  type ToolId = (typeof TOOL_IDS)[number];
6
6
  declare const CONFIG_FILES: {
7
7
  readonly config: "config.json";
@@ -68,6 +68,7 @@ declare const toolIdSchema: z.ZodEnum<{
68
68
  cursor: "cursor";
69
69
  copilot: "copilot";
70
70
  windsurf: "windsurf";
71
+ gemini: "gemini";
71
72
  }>;
72
73
  /** Settings configuration (Claude format as source of truth) */
73
74
  declare const settingsSchema: z.ZodObject<{
@@ -111,6 +112,10 @@ declare const configSchema: z.ZodObject<{
111
112
  enabled: z.ZodBoolean;
112
113
  versionControl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
113
114
  }, z.core.$strip>>;
115
+ gemini: z.ZodOptional<z.ZodObject<{
116
+ enabled: z.ZodBoolean;
117
+ versionControl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
118
+ }, z.core.$strip>>;
114
119
  }, z.core.$strip>>;
115
120
  }, z.core.$strip>;
116
121
  /** Skill frontmatter (name and description required) */
@@ -248,7 +253,7 @@ declare const claudeCodePlugin: Plugin;
248
253
  * OpenCode plugin for exporting to opencode.json format
249
254
  *
250
255
  * Output structure:
251
- * - .opencode/AGENTS.md (symlink -> ../.ai/AGENTS.md)
256
+ * - AGENTS.md (symlink -> .ai/AGENTS.md) [at project root]
252
257
  * - .opencode/rules/ (symlink -> ../.ai/rules)
253
258
  * - .opencode/skills/<name>/ (symlink -> ../../.ai/skills/<name>)
254
259
  * - opencode.json (generated config merged with .ai/.opencode/opencode.json)
package/dist/index.js CHANGED
@@ -11,7 +11,8 @@ var TOOL_IDS = [
11
11
  "opencode",
12
12
  "cursor",
13
13
  "copilot",
14
- "windsurf"
14
+ "windsurf",
15
+ "gemini"
15
16
  ];
16
17
  var CONFIG_FILES = {
17
18
  config: "config.json",
@@ -28,14 +29,16 @@ var TOOL_OUTPUT_DIRS = {
28
29
  opencode: ".opencode",
29
30
  cursor: ".cursor",
30
31
  copilot: ".github",
31
- windsurf: ".windsurf"
32
+ windsurf: ".windsurf",
33
+ gemini: ".gemini"
32
34
  };
33
35
  var OVERRIDE_DIRS = {
34
36
  claudeCode: ".claude",
35
37
  opencode: ".opencode",
36
38
  cursor: ".cursor",
37
39
  copilot: ".copilot",
38
- windsurf: ".windsurf"
40
+ windsurf: ".windsurf",
41
+ gemini: ".gemini"
39
42
  };
40
43
 
41
44
  // src/errors.ts
@@ -61,10 +64,10 @@ var ParseError = class extends LnaiError {
61
64
  var ValidationError = class extends LnaiError {
62
65
  path;
63
66
  value;
64
- constructor(message, path5, value) {
67
+ constructor(message, path6, value) {
65
68
  super(message, "VALIDATION_ERROR");
66
69
  this.name = "ValidationError";
67
- this.path = path5;
70
+ this.path = path6;
68
71
  this.value = value;
69
72
  }
70
73
  };
@@ -120,7 +123,8 @@ var toolIdSchema = z.enum([
120
123
  "opencode",
121
124
  "cursor",
122
125
  "copilot",
123
- "windsurf"
126
+ "windsurf",
127
+ "gemini"
124
128
  ]);
125
129
  var settingsSchema = z.object({
126
130
  permissions: permissionsSchema.optional(),
@@ -132,7 +136,8 @@ var configSchema = z.object({
132
136
  opencode: toolConfigSchema,
133
137
  cursor: toolConfigSchema,
134
138
  copilot: toolConfigSchema,
135
- windsurf: toolConfigSchema
139
+ windsurf: toolConfigSchema,
140
+ gemini: toolConfigSchema
136
141
  }).partial().optional()
137
142
  });
138
143
  var skillFrontmatterSchema = z.object({
@@ -652,6 +657,7 @@ var copilotPlugin = {
652
657
  },
653
658
  validate(state) {
654
659
  const warnings = [];
660
+ const skipped = [];
655
661
  if (!state.agents) {
656
662
  warnings.push({
657
663
  path: ["AGENTS.md"],
@@ -661,9 +667,9 @@ var copilotPlugin = {
661
667
  const permissions = state.settings?.permissions;
662
668
  const hasPermissions = permissions && (permissions.allow && permissions.allow.length > 0 || permissions.ask && permissions.ask.length > 0 || permissions.deny && permissions.deny.length > 0);
663
669
  if (hasPermissions) {
664
- warnings.push({
665
- path: ["settings", "permissions"],
666
- message: "GitHub Copilot does not support permissions - they will be ignored"
670
+ skipped.push({
671
+ feature: "permissions",
672
+ reason: "GitHub Copilot does not support declarative permissions"
667
673
  });
668
674
  }
669
675
  const mcpServers = state.settings?.mcpServers;
@@ -684,7 +690,7 @@ var copilotPlugin = {
684
690
  }
685
691
  }
686
692
  }
687
- return { valid: true, errors: [], warnings, skipped: [] };
693
+ return { valid: true, errors: [], warnings, skipped };
688
694
  }
689
695
  };
690
696
 
@@ -901,6 +907,132 @@ function buildCliContent(permissions) {
901
907
  }
902
908
  return { permissions: permissionsResult.permissions };
903
909
  }
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
+
923
+ // src/plugins/gemini/transforms.ts
924
+ function transformMcpToGemini(mcpServers) {
925
+ if (!mcpServers) {
926
+ return void 0;
927
+ }
928
+ const geminiMcp = {};
929
+ for (const [name, config] of Object.entries(mcpServers)) {
930
+ if (!config.command && !config.url && !config.type) {
931
+ continue;
932
+ }
933
+ geminiMcp[name] = {
934
+ command: config.command,
935
+ args: config.args,
936
+ env: config.env
937
+ };
938
+ if (config.url) {
939
+ geminiMcp[name].httpUrl = config.url;
940
+ }
941
+ }
942
+ return Object.keys(geminiMcp).length > 0 ? geminiMcp : void 0;
943
+ }
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
+
962
+ // src/plugins/gemini/index.ts
963
+ var geminiPlugin = {
964
+ id: "gemini",
965
+ name: "Gemini CLI",
966
+ async detect(_rootDir) {
967
+ return false;
968
+ },
969
+ async import(_rootDir) {
970
+ return null;
971
+ },
972
+ async export(state, rootDir) {
973
+ const files = [];
974
+ const outputDir = TOOL_OUTPUT_DIRS.gemini;
975
+ if (state.agents) {
976
+ files.push({
977
+ path: `${outputDir}/GEMINI.md`,
978
+ type: "symlink",
979
+ target: `../${UNIFIED_DIR}/AGENTS.md`
980
+ });
981
+ }
982
+ const rulesMap = groupRulesByDirectory(state.rules);
983
+ for (const [dir, contents] of rulesMap.entries()) {
984
+ const combinedContent = contents.join("\n---\n\n");
985
+ const filePath = dir === "." ? "GEMINI.md" : `${dir}/GEMINI.md`;
986
+ files.push({
987
+ path: filePath,
988
+ type: "text",
989
+ content: combinedContent
990
+ });
991
+ }
992
+ for (const skill of state.skills) {
993
+ files.push({
994
+ path: `${outputDir}/skills/${skill.path}`,
995
+ type: "symlink",
996
+ target: `../../${UNIFIED_DIR}/skills/${skill.path}`
997
+ });
998
+ }
999
+ const mcpServers = transformMcpToGemini(state.settings?.mcpServers);
1000
+ if (mcpServers) {
1001
+ files.push({
1002
+ path: `${outputDir}/settings.json`,
1003
+ type: "json",
1004
+ content: { mcpServers }
1005
+ });
1006
+ }
1007
+ return applyFileOverrides(files, rootDir, "gemini");
1008
+ },
1009
+ validate(state) {
1010
+ const warnings = [];
1011
+ const skipped = [];
1012
+ if (!state.agents) {
1013
+ warnings.push({
1014
+ path: ["AGENTS.md"],
1015
+ message: "No AGENTS.md found - GEMINI.md will not be created"
1016
+ });
1017
+ }
1018
+ if (state.settings?.permissions) {
1019
+ const hasPermissions = (state.settings.permissions.allow?.length ?? 0) > 0 || (state.settings.permissions.ask?.length ?? 0) > 0 || (state.settings.permissions.deny?.length ?? 0) > 0;
1020
+ if (hasPermissions) {
1021
+ skipped.push({
1022
+ feature: "permissions",
1023
+ reason: "Gemini CLI does not support declarative permissions - permissions must be granted interactively"
1024
+ });
1025
+ }
1026
+ }
1027
+ if (state.rules.length > 0) {
1028
+ warnings.push({
1029
+ path: ["rules"],
1030
+ message: "Rules will be generated into GEMINI.md files in their respective subdirectories (e.g. apps/cli/GEMINI.md)."
1031
+ });
1032
+ }
1033
+ return { valid: true, errors: [], warnings, skipped };
1034
+ }
1035
+ };
904
1036
 
905
1037
  // src/plugins/opencode/transforms.ts
906
1038
  function transformMcpToOpenCode(servers) {
@@ -989,9 +1121,9 @@ var opencodePlugin = {
989
1121
  const outputDir = TOOL_OUTPUT_DIRS.opencode;
990
1122
  if (state.agents) {
991
1123
  files.push({
992
- path: `${outputDir}/AGENTS.md`,
1124
+ path: "AGENTS.md",
993
1125
  type: "symlink",
994
- target: `../${UNIFIED_DIR}/AGENTS.md`
1126
+ target: `${UNIFIED_DIR}/AGENTS.md`
995
1127
  });
996
1128
  }
997
1129
  if (state.rules.length > 0) {
@@ -1036,7 +1168,7 @@ var opencodePlugin = {
1036
1168
  if (!state.agents) {
1037
1169
  warnings.push({
1038
1170
  path: ["AGENTS.md"],
1039
- message: "No AGENTS.md found - .opencode/AGENTS.md will not be created"
1171
+ message: "No AGENTS.md found - root AGENTS.md will not be created"
1040
1172
  });
1041
1173
  }
1042
1174
  return { valid: true, errors: [], warnings, skipped: [] };
@@ -1174,6 +1306,7 @@ pluginRegistry.register(copilotPlugin);
1174
1306
  pluginRegistry.register(cursorPlugin);
1175
1307
  pluginRegistry.register(opencodePlugin);
1176
1308
  pluginRegistry.register(windsurfPlugin);
1309
+ pluginRegistry.register(geminiPlugin);
1177
1310
  function computeHash(content) {
1178
1311
  return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
1179
1312
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lnai/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Core library for LNAI - unified AI config management",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,6 +20,8 @@
20
20
  "configuration",
21
21
  "claude",
22
22
  "cursor",
23
+ "gemini",
24
+ "antigravity",
23
25
  "opencode",
24
26
  "copilot",
25
27
  "github-copilot",