@lnai/core 0.6.5 → 0.6.7
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 +3 -3
- package/dist/index.js +178 -193
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -283,7 +283,7 @@ declare const claudeCodePlugin: Plugin;
|
|
|
283
283
|
* Output structure:
|
|
284
284
|
* - AGENTS.md (symlink -> .ai/AGENTS.md) [at project root]
|
|
285
285
|
* - <dir>/AGENTS.md (generated from .ai/rules/*.md, per glob directory)
|
|
286
|
-
* - .
|
|
286
|
+
* - .agents/skills/<name>/ (symlink -> ../../.ai/skills/<name>)
|
|
287
287
|
* - .codex/config.toml (generated from settings.mcpServers)
|
|
288
288
|
* - .codex/<path> (symlink -> ../.ai/.codex/<path>) for override files
|
|
289
289
|
*/
|
|
@@ -295,7 +295,7 @@ declare const codexPlugin: Plugin;
|
|
|
295
295
|
* Output structure:
|
|
296
296
|
* - AGENTS.md (symlink -> .ai/AGENTS.md) [at project root]
|
|
297
297
|
* - .opencode/rules/ (symlink -> ../.ai/rules)
|
|
298
|
-
* - .
|
|
298
|
+
* - .agents/skills/<name>/ (symlink -> ../../.ai/skills/<name>)
|
|
299
299
|
* - opencode.json (generated config merged with .ai/.opencode/opencode.json)
|
|
300
300
|
* - .opencode/<path> (symlink -> ../.ai/.opencode/<path>) for other override files
|
|
301
301
|
*/
|
|
@@ -343,7 +343,7 @@ declare function writeFiles(files: OutputFile[], options: WriterOptions): Promis
|
|
|
343
343
|
/**
|
|
344
344
|
* Update .gitignore with paths that should not be version controlled.
|
|
345
345
|
* Manages a dedicated "lnai-generated" section to avoid conflicts with user entries.
|
|
346
|
-
*
|
|
346
|
+
* Replaces the managed section on each run so stale paths are removed.
|
|
347
347
|
*/
|
|
348
348
|
declare function updateGitignore(rootDir: string, paths: string[]): Promise<void>;
|
|
349
349
|
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ var CONFIG_DIRS = {
|
|
|
25
25
|
skills: "skills",
|
|
26
26
|
subagents: "subagents"
|
|
27
27
|
};
|
|
28
|
+
var CROSS_TOOL_SKILLS_DIR = ".agents";
|
|
28
29
|
var TOOL_OUTPUT_DIRS = {
|
|
29
30
|
claudeCode: ".claude",
|
|
30
31
|
opencode: ".opencode",
|
|
@@ -413,6 +414,39 @@ function validateToolIds(tools) {
|
|
|
413
414
|
}
|
|
414
415
|
return { valid: true, errors: [], warnings: [], skipped: [] };
|
|
415
416
|
}
|
|
417
|
+
|
|
418
|
+
// src/utils/agents.ts
|
|
419
|
+
function createRootAgentsMdSymlink(state) {
|
|
420
|
+
if (!state.agents) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
path: "AGENTS.md",
|
|
425
|
+
type: "symlink",
|
|
426
|
+
target: `${UNIFIED_DIR}/AGENTS.md`
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function createSkillSymlinks(state, outputDir) {
|
|
430
|
+
const depth = outputDir.split("/").length + 1;
|
|
431
|
+
const prefix = "../".repeat(depth);
|
|
432
|
+
return state.skills.map((skill) => ({
|
|
433
|
+
path: `${outputDir}/skills/${skill.path}`,
|
|
434
|
+
type: "symlink",
|
|
435
|
+
target: `${prefix}${UNIFIED_DIR}/skills/${skill.path}`
|
|
436
|
+
}));
|
|
437
|
+
}
|
|
438
|
+
function createNoAgentsMdWarning(outputDescription) {
|
|
439
|
+
return {
|
|
440
|
+
path: ["AGENTS.md"],
|
|
441
|
+
message: `No AGENTS.md found - ${outputDescription} will not be created`
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function hasPermissionsConfigured(permissions) {
|
|
445
|
+
if (!permissions) {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
return (permissions.allow?.length ?? 0) > 0 || (permissions.ask?.length ?? 0) > 0 || (permissions.deny?.length ?? 0) > 0;
|
|
449
|
+
}
|
|
416
450
|
async function scanOverrideDirectory(rootDir, toolId) {
|
|
417
451
|
const overrideDir = path.join(rootDir, UNIFIED_DIR, OVERRIDE_DIRS[toolId]);
|
|
418
452
|
try {
|
|
@@ -457,6 +491,7 @@ async function applyFileOverrides(files, rootDir, toolId) {
|
|
|
457
491
|
}
|
|
458
492
|
|
|
459
493
|
// src/plugins/claude-code/index.ts
|
|
494
|
+
var OUTPUT_DIR = TOOL_OUTPUT_DIRS.claudeCode;
|
|
460
495
|
var claudeCodePlugin = {
|
|
461
496
|
id: "claudeCode",
|
|
462
497
|
name: "Claude Code",
|
|
@@ -468,28 +503,21 @@ var claudeCodePlugin = {
|
|
|
468
503
|
},
|
|
469
504
|
async export(state, rootDir) {
|
|
470
505
|
const files = [];
|
|
471
|
-
const outputDir = TOOL_OUTPUT_DIRS.claudeCode;
|
|
472
506
|
if (state.agents) {
|
|
473
507
|
files.push({
|
|
474
|
-
path: `${
|
|
508
|
+
path: `${OUTPUT_DIR}/CLAUDE.md`,
|
|
475
509
|
type: "symlink",
|
|
476
510
|
target: `../${UNIFIED_DIR}/AGENTS.md`
|
|
477
511
|
});
|
|
478
512
|
}
|
|
479
513
|
if (state.rules.length > 0) {
|
|
480
514
|
files.push({
|
|
481
|
-
path: `${
|
|
515
|
+
path: `${OUTPUT_DIR}/rules`,
|
|
482
516
|
type: "symlink",
|
|
483
517
|
target: `../${UNIFIED_DIR}/rules`
|
|
484
518
|
});
|
|
485
519
|
}
|
|
486
|
-
|
|
487
|
-
files.push({
|
|
488
|
-
path: `${outputDir}/skills/${skill.path}`,
|
|
489
|
-
type: "symlink",
|
|
490
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
491
|
-
});
|
|
492
|
-
}
|
|
520
|
+
files.push(...createSkillSymlinks(state, OUTPUT_DIR));
|
|
493
521
|
const settings = {};
|
|
494
522
|
if (state.settings?.permissions) {
|
|
495
523
|
settings["permissions"] = state.settings.permissions;
|
|
@@ -499,7 +527,7 @@ var claudeCodePlugin = {
|
|
|
499
527
|
}
|
|
500
528
|
if (Object.keys(settings).length > 0) {
|
|
501
529
|
files.push({
|
|
502
|
-
path: `${
|
|
530
|
+
path: `${OUTPUT_DIR}/settings.json`,
|
|
503
531
|
type: "json",
|
|
504
532
|
content: settings
|
|
505
533
|
});
|
|
@@ -509,10 +537,7 @@ var claudeCodePlugin = {
|
|
|
509
537
|
validate(state) {
|
|
510
538
|
const warnings = [];
|
|
511
539
|
if (!state.agents) {
|
|
512
|
-
warnings.push(
|
|
513
|
-
path: ["AGENTS.md"],
|
|
514
|
-
message: "No AGENTS.md found - .claude/CLAUDE.md will not be created"
|
|
515
|
-
});
|
|
540
|
+
warnings.push(createNoAgentsMdWarning(".claude/CLAUDE.md"));
|
|
516
541
|
}
|
|
517
542
|
return { valid: true, errors: [], warnings, skipped: [] };
|
|
518
543
|
}
|
|
@@ -554,6 +579,7 @@ ${rule.content}
|
|
|
554
579
|
}
|
|
555
580
|
|
|
556
581
|
// src/plugins/codex/index.ts
|
|
582
|
+
var OUTPUT_DIR2 = TOOL_OUTPUT_DIRS.codex;
|
|
557
583
|
var codexPlugin = {
|
|
558
584
|
id: "codex",
|
|
559
585
|
name: "Codex",
|
|
@@ -565,13 +591,9 @@ var codexPlugin = {
|
|
|
565
591
|
},
|
|
566
592
|
async export(state, rootDir) {
|
|
567
593
|
const files = [];
|
|
568
|
-
const
|
|
569
|
-
if (
|
|
570
|
-
files.push(
|
|
571
|
-
path: "AGENTS.md",
|
|
572
|
-
type: "symlink",
|
|
573
|
-
target: `${UNIFIED_DIR}/AGENTS.md`
|
|
574
|
-
});
|
|
594
|
+
const agentsSymlink = createRootAgentsMdSymlink(state);
|
|
595
|
+
if (agentsSymlink) {
|
|
596
|
+
files.push(agentsSymlink);
|
|
575
597
|
}
|
|
576
598
|
const rulesMap = groupRulesByDirectory(state.rules);
|
|
577
599
|
for (const [dir, contents] of rulesMap.entries()) {
|
|
@@ -585,17 +607,11 @@ var codexPlugin = {
|
|
|
585
607
|
content: combinedContent
|
|
586
608
|
});
|
|
587
609
|
}
|
|
588
|
-
|
|
589
|
-
files.push({
|
|
590
|
-
path: `${outputDir}/skills/${skill.path}`,
|
|
591
|
-
type: "symlink",
|
|
592
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
593
|
-
});
|
|
594
|
-
}
|
|
610
|
+
files.push(...createSkillSymlinks(state, CROSS_TOOL_SKILLS_DIR));
|
|
595
611
|
const configToml = buildCodexConfigToml(state.settings?.mcpServers);
|
|
596
612
|
if (configToml) {
|
|
597
613
|
files.push({
|
|
598
|
-
path: `${
|
|
614
|
+
path: `${OUTPUT_DIR2}/config.toml`,
|
|
599
615
|
type: "text",
|
|
600
616
|
content: configToml
|
|
601
617
|
});
|
|
@@ -606,10 +622,7 @@ var codexPlugin = {
|
|
|
606
622
|
const warnings = [];
|
|
607
623
|
const skipped = [];
|
|
608
624
|
if (!state.agents) {
|
|
609
|
-
warnings.push(
|
|
610
|
-
path: ["AGENTS.md"],
|
|
611
|
-
message: "No AGENTS.md found - root AGENTS.md will not be created"
|
|
612
|
-
});
|
|
625
|
+
warnings.push(createNoAgentsMdWarning("root AGENTS.md"));
|
|
613
626
|
}
|
|
614
627
|
const rulesMap = groupRulesByDirectory(state.rules);
|
|
615
628
|
if (rulesMap.has(".")) {
|
|
@@ -629,14 +642,11 @@ var codexPlugin = {
|
|
|
629
642
|
}
|
|
630
643
|
}
|
|
631
644
|
}
|
|
632
|
-
if (state.settings?.permissions) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
reason: "Codex rules are not generated from LNAI permissions"
|
|
638
|
-
});
|
|
639
|
-
}
|
|
645
|
+
if (hasPermissionsConfigured(state.settings?.permissions)) {
|
|
646
|
+
skipped.push({
|
|
647
|
+
feature: "permissions",
|
|
648
|
+
reason: "Codex rules are not generated from LNAI permissions"
|
|
649
|
+
});
|
|
640
650
|
}
|
|
641
651
|
return { valid: true, errors: [], warnings, skipped };
|
|
642
652
|
}
|
|
@@ -826,6 +836,7 @@ function transformMcpToCopilot(servers) {
|
|
|
826
836
|
}
|
|
827
837
|
|
|
828
838
|
// src/plugins/copilot/index.ts
|
|
839
|
+
var OUTPUT_DIR3 = TOOL_OUTPUT_DIRS.copilot;
|
|
829
840
|
var copilotPlugin = {
|
|
830
841
|
id: "copilot",
|
|
831
842
|
name: "GitHub Copilot",
|
|
@@ -837,12 +848,9 @@ var copilotPlugin = {
|
|
|
837
848
|
},
|
|
838
849
|
async export(state, rootDir) {
|
|
839
850
|
const files = [];
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
type: "symlink",
|
|
844
|
-
target: `../${UNIFIED_DIR}/AGENTS.md`
|
|
845
|
-
});
|
|
851
|
+
const agentsSymlink = createRootAgentsMdSymlink(state);
|
|
852
|
+
if (agentsSymlink) {
|
|
853
|
+
files.push(agentsSymlink);
|
|
846
854
|
}
|
|
847
855
|
for (const rule of state.rules) {
|
|
848
856
|
const transformed = transformRuleToCopilot(rule);
|
|
@@ -852,18 +860,12 @@ var copilotPlugin = {
|
|
|
852
860
|
);
|
|
853
861
|
const outputFilename = rule.path.replace(/\.md$/, ".instructions.md");
|
|
854
862
|
files.push({
|
|
855
|
-
path:
|
|
863
|
+
path: `${OUTPUT_DIR3}/instructions/${outputFilename}`,
|
|
856
864
|
type: "text",
|
|
857
865
|
content: ruleContent
|
|
858
866
|
});
|
|
859
867
|
}
|
|
860
|
-
|
|
861
|
-
files.push({
|
|
862
|
-
path: `.github/skills/${skill.path}`,
|
|
863
|
-
type: "symlink",
|
|
864
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
865
|
-
});
|
|
866
|
-
}
|
|
868
|
+
files.push(...createSkillSymlinks(state, OUTPUT_DIR3));
|
|
867
869
|
const mcpConfig = transformMcpToCopilot(state.settings?.mcpServers);
|
|
868
870
|
if (mcpConfig) {
|
|
869
871
|
files.push({
|
|
@@ -878,14 +880,9 @@ var copilotPlugin = {
|
|
|
878
880
|
const warnings = [];
|
|
879
881
|
const skipped = [];
|
|
880
882
|
if (!state.agents) {
|
|
881
|
-
warnings.push(
|
|
882
|
-
path: ["AGENTS.md"],
|
|
883
|
-
message: "No AGENTS.md found - .github/copilot-instructions.md will not be created"
|
|
884
|
-
});
|
|
883
|
+
warnings.push(createNoAgentsMdWarning("root AGENTS.md"));
|
|
885
884
|
}
|
|
886
|
-
|
|
887
|
-
const hasPermissions = permissions && (permissions.allow && permissions.allow.length > 0 || permissions.ask && permissions.ask.length > 0 || permissions.deny && permissions.deny.length > 0);
|
|
888
|
-
if (hasPermissions) {
|
|
885
|
+
if (hasPermissionsConfigured(state.settings?.permissions)) {
|
|
889
886
|
skipped.push({
|
|
890
887
|
feature: "permissions",
|
|
891
888
|
reason: "GitHub Copilot does not support declarative permissions"
|
|
@@ -1017,6 +1014,7 @@ function transformPermissionRule(rule) {
|
|
|
1017
1014
|
}
|
|
1018
1015
|
|
|
1019
1016
|
// src/plugins/cursor/index.ts
|
|
1017
|
+
var OUTPUT_DIR4 = TOOL_OUTPUT_DIRS.cursor;
|
|
1020
1018
|
var cursorPlugin = {
|
|
1021
1019
|
id: "cursor",
|
|
1022
1020
|
name: "Cursor",
|
|
@@ -1028,13 +1026,9 @@ var cursorPlugin = {
|
|
|
1028
1026
|
},
|
|
1029
1027
|
async export(state, rootDir) {
|
|
1030
1028
|
const files = [];
|
|
1031
|
-
const
|
|
1032
|
-
if (
|
|
1033
|
-
files.push(
|
|
1034
|
-
path: "AGENTS.md",
|
|
1035
|
-
type: "symlink",
|
|
1036
|
-
target: `${UNIFIED_DIR}/AGENTS.md`
|
|
1037
|
-
});
|
|
1029
|
+
const agentsSymlink = createRootAgentsMdSymlink(state);
|
|
1030
|
+
if (agentsSymlink) {
|
|
1031
|
+
files.push(agentsSymlink);
|
|
1038
1032
|
}
|
|
1039
1033
|
for (const rule of state.rules) {
|
|
1040
1034
|
const transformed = transformRuleToCursor(rule);
|
|
@@ -1044,22 +1038,16 @@ var cursorPlugin = {
|
|
|
1044
1038
|
);
|
|
1045
1039
|
const outputFilename = rule.path.replace(/\.md$/, ".mdc");
|
|
1046
1040
|
files.push({
|
|
1047
|
-
path: `${
|
|
1041
|
+
path: `${OUTPUT_DIR4}/rules/${outputFilename}`,
|
|
1048
1042
|
type: "text",
|
|
1049
1043
|
content: ruleContent
|
|
1050
1044
|
});
|
|
1051
1045
|
}
|
|
1052
|
-
|
|
1053
|
-
files.push({
|
|
1054
|
-
path: `${outputDir}/skills/${skill.path}`,
|
|
1055
|
-
type: "symlink",
|
|
1056
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1046
|
+
files.push(...createSkillSymlinks(state, OUTPUT_DIR4));
|
|
1059
1047
|
const mcpServers = transformMcpToCursor(state.settings?.mcpServers);
|
|
1060
1048
|
if (mcpServers) {
|
|
1061
1049
|
files.push({
|
|
1062
|
-
path: `${
|
|
1050
|
+
path: `${OUTPUT_DIR4}/mcp.json`,
|
|
1063
1051
|
type: "json",
|
|
1064
1052
|
content: { mcpServers }
|
|
1065
1053
|
});
|
|
@@ -1067,7 +1055,7 @@ var cursorPlugin = {
|
|
|
1067
1055
|
const cliContent = buildCliContent(state.settings?.permissions);
|
|
1068
1056
|
if (cliContent) {
|
|
1069
1057
|
files.push({
|
|
1070
|
-
path: `${
|
|
1058
|
+
path: `${OUTPUT_DIR4}/cli.json`,
|
|
1071
1059
|
type: "json",
|
|
1072
1060
|
content: cliContent
|
|
1073
1061
|
});
|
|
@@ -1077,10 +1065,7 @@ var cursorPlugin = {
|
|
|
1077
1065
|
validate(state) {
|
|
1078
1066
|
const warnings = [];
|
|
1079
1067
|
if (!state.agents) {
|
|
1080
|
-
warnings.push(
|
|
1081
|
-
path: ["AGENTS.md"],
|
|
1082
|
-
message: "No AGENTS.md found - root AGENTS.md will not be created"
|
|
1083
|
-
});
|
|
1068
|
+
warnings.push(createNoAgentsMdWarning("root AGENTS.md"));
|
|
1084
1069
|
}
|
|
1085
1070
|
const permissionsResult = transformPermissionsToCursor(
|
|
1086
1071
|
state.settings?.permissions
|
|
@@ -1131,6 +1116,7 @@ function transformMcpToGemini(mcpServers) {
|
|
|
1131
1116
|
}
|
|
1132
1117
|
|
|
1133
1118
|
// src/plugins/gemini/index.ts
|
|
1119
|
+
var OUTPUT_DIR5 = TOOL_OUTPUT_DIRS.gemini;
|
|
1134
1120
|
var geminiPlugin = {
|
|
1135
1121
|
id: "gemini",
|
|
1136
1122
|
name: "Gemini CLI",
|
|
@@ -1142,13 +1128,9 @@ var geminiPlugin = {
|
|
|
1142
1128
|
},
|
|
1143
1129
|
async export(state, rootDir) {
|
|
1144
1130
|
const files = [];
|
|
1145
|
-
const
|
|
1146
|
-
if (
|
|
1147
|
-
files.push(
|
|
1148
|
-
path: `${outputDir}/GEMINI.md`,
|
|
1149
|
-
type: "symlink",
|
|
1150
|
-
target: `../${UNIFIED_DIR}/AGENTS.md`
|
|
1151
|
-
});
|
|
1131
|
+
const agentsSymlink = createRootAgentsMdSymlink(state);
|
|
1132
|
+
if (agentsSymlink) {
|
|
1133
|
+
files.push(agentsSymlink);
|
|
1152
1134
|
}
|
|
1153
1135
|
const rulesMap = groupRulesByDirectory(state.rules);
|
|
1154
1136
|
for (const [dir, contents] of rulesMap.entries()) {
|
|
@@ -1160,19 +1142,21 @@ var geminiPlugin = {
|
|
|
1160
1142
|
content: combinedContent
|
|
1161
1143
|
});
|
|
1162
1144
|
}
|
|
1163
|
-
|
|
1164
|
-
files.push({
|
|
1165
|
-
path: `${outputDir}/skills/${skill.path}`,
|
|
1166
|
-
type: "symlink",
|
|
1167
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1145
|
+
files.push(...createSkillSymlinks(state, OUTPUT_DIR5));
|
|
1170
1146
|
const mcpServers = transformMcpToGemini(state.settings?.mcpServers);
|
|
1171
|
-
|
|
1147
|
+
const hasAgents = !!state.agents;
|
|
1148
|
+
if (mcpServers || hasAgents) {
|
|
1149
|
+
const settingsContent = {};
|
|
1150
|
+
if (hasAgents) {
|
|
1151
|
+
settingsContent["context"] = { fileName: ["AGENTS.md"] };
|
|
1152
|
+
}
|
|
1153
|
+
if (mcpServers) {
|
|
1154
|
+
settingsContent["mcpServers"] = mcpServers;
|
|
1155
|
+
}
|
|
1172
1156
|
files.push({
|
|
1173
|
-
path: `${
|
|
1157
|
+
path: `${OUTPUT_DIR5}/settings.json`,
|
|
1174
1158
|
type: "json",
|
|
1175
|
-
content:
|
|
1159
|
+
content: settingsContent
|
|
1176
1160
|
});
|
|
1177
1161
|
}
|
|
1178
1162
|
return applyFileOverrides(files, rootDir, "gemini");
|
|
@@ -1181,19 +1165,13 @@ var geminiPlugin = {
|
|
|
1181
1165
|
const warnings = [];
|
|
1182
1166
|
const skipped = [];
|
|
1183
1167
|
if (!state.agents) {
|
|
1184
|
-
warnings.push(
|
|
1185
|
-
path: ["AGENTS.md"],
|
|
1186
|
-
message: "No AGENTS.md found - GEMINI.md will not be created"
|
|
1187
|
-
});
|
|
1168
|
+
warnings.push(createNoAgentsMdWarning("root AGENTS.md"));
|
|
1188
1169
|
}
|
|
1189
|
-
if (state.settings?.permissions) {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
reason: "Gemini CLI does not support declarative permissions - permissions must be granted interactively"
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1170
|
+
if (hasPermissionsConfigured(state.settings?.permissions)) {
|
|
1171
|
+
skipped.push({
|
|
1172
|
+
feature: "permissions",
|
|
1173
|
+
reason: "Gemini CLI does not support declarative permissions - permissions must be granted interactively"
|
|
1174
|
+
});
|
|
1197
1175
|
}
|
|
1198
1176
|
if (state.rules.length > 0) {
|
|
1199
1177
|
warnings.push({
|
|
@@ -1278,6 +1256,7 @@ function parsePermissionRuleForOpenCode(rule) {
|
|
|
1278
1256
|
}
|
|
1279
1257
|
|
|
1280
1258
|
// src/plugins/opencode/index.ts
|
|
1259
|
+
var OUTPUT_DIR6 = TOOL_OUTPUT_DIRS.opencode;
|
|
1281
1260
|
var opencodePlugin = {
|
|
1282
1261
|
id: "opencode",
|
|
1283
1262
|
name: "OpenCode",
|
|
@@ -1289,33 +1268,23 @@ var opencodePlugin = {
|
|
|
1289
1268
|
},
|
|
1290
1269
|
async export(state, rootDir) {
|
|
1291
1270
|
const files = [];
|
|
1292
|
-
const
|
|
1293
|
-
if (
|
|
1294
|
-
files.push(
|
|
1295
|
-
path: "AGENTS.md",
|
|
1296
|
-
type: "symlink",
|
|
1297
|
-
target: `${UNIFIED_DIR}/AGENTS.md`
|
|
1298
|
-
});
|
|
1271
|
+
const agentsSymlink = createRootAgentsMdSymlink(state);
|
|
1272
|
+
if (agentsSymlink) {
|
|
1273
|
+
files.push(agentsSymlink);
|
|
1299
1274
|
}
|
|
1300
1275
|
if (state.rules.length > 0) {
|
|
1301
1276
|
files.push({
|
|
1302
|
-
path: `${
|
|
1277
|
+
path: `${OUTPUT_DIR6}/rules`,
|
|
1303
1278
|
type: "symlink",
|
|
1304
1279
|
target: `../${UNIFIED_DIR}/rules`
|
|
1305
1280
|
});
|
|
1306
1281
|
}
|
|
1307
|
-
|
|
1308
|
-
files.push({
|
|
1309
|
-
path: `${outputDir}/skills/${skill.path}`,
|
|
1310
|
-
type: "symlink",
|
|
1311
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1282
|
+
files.push(...createSkillSymlinks(state, CROSS_TOOL_SKILLS_DIR));
|
|
1314
1283
|
const config = {
|
|
1315
1284
|
$schema: "https://opencode.ai/config.json"
|
|
1316
1285
|
};
|
|
1317
1286
|
if (state.rules.length > 0) {
|
|
1318
|
-
config["instructions"] = [`${
|
|
1287
|
+
config["instructions"] = [`${OUTPUT_DIR6}/rules/*.md`];
|
|
1319
1288
|
}
|
|
1320
1289
|
const mcp = transformMcpToOpenCode(state.settings?.mcpServers);
|
|
1321
1290
|
if (mcp) {
|
|
@@ -1337,10 +1306,7 @@ var opencodePlugin = {
|
|
|
1337
1306
|
validate(state) {
|
|
1338
1307
|
const warnings = [];
|
|
1339
1308
|
if (!state.agents) {
|
|
1340
|
-
warnings.push(
|
|
1341
|
-
path: ["AGENTS.md"],
|
|
1342
|
-
message: "No AGENTS.md found - root AGENTS.md will not be created"
|
|
1343
|
-
});
|
|
1309
|
+
warnings.push(createNoAgentsMdWarning("root AGENTS.md"));
|
|
1344
1310
|
}
|
|
1345
1311
|
warnings.push(
|
|
1346
1312
|
...validateMcpServers(state.settings?.mcpServers, [
|
|
@@ -1403,6 +1369,7 @@ function serializeWindsurfRule(frontmatter, content) {
|
|
|
1403
1369
|
}
|
|
1404
1370
|
|
|
1405
1371
|
// src/plugins/windsurf/index.ts
|
|
1372
|
+
var OUTPUT_DIR7 = TOOL_OUTPUT_DIRS.windsurf;
|
|
1406
1373
|
var windsurfPlugin = {
|
|
1407
1374
|
id: "windsurf",
|
|
1408
1375
|
name: "Windsurf",
|
|
@@ -1414,13 +1381,9 @@ var windsurfPlugin = {
|
|
|
1414
1381
|
},
|
|
1415
1382
|
async export(state, rootDir) {
|
|
1416
1383
|
const files = [];
|
|
1417
|
-
const
|
|
1418
|
-
if (
|
|
1419
|
-
files.push(
|
|
1420
|
-
path: "AGENTS.md",
|
|
1421
|
-
type: "symlink",
|
|
1422
|
-
target: `${UNIFIED_DIR}/AGENTS.md`
|
|
1423
|
-
});
|
|
1384
|
+
const agentsSymlink = createRootAgentsMdSymlink(state);
|
|
1385
|
+
if (agentsSymlink) {
|
|
1386
|
+
files.push(agentsSymlink);
|
|
1424
1387
|
}
|
|
1425
1388
|
for (const rule of state.rules) {
|
|
1426
1389
|
const transformed = transformRuleToWindsurf(rule);
|
|
@@ -1429,28 +1392,19 @@ var windsurfPlugin = {
|
|
|
1429
1392
|
transformed.content
|
|
1430
1393
|
);
|
|
1431
1394
|
files.push({
|
|
1432
|
-
path: `${
|
|
1395
|
+
path: `${OUTPUT_DIR7}/rules/${rule.path}`,
|
|
1433
1396
|
type: "text",
|
|
1434
1397
|
content: ruleContent
|
|
1435
1398
|
});
|
|
1436
1399
|
}
|
|
1437
|
-
|
|
1438
|
-
files.push({
|
|
1439
|
-
path: `${outputDir}/skills/${skill.path}`,
|
|
1440
|
-
type: "symlink",
|
|
1441
|
-
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1400
|
+
files.push(...createSkillSymlinks(state, OUTPUT_DIR7));
|
|
1444
1401
|
return applyFileOverrides(files, rootDir, "windsurf");
|
|
1445
1402
|
},
|
|
1446
1403
|
validate(state) {
|
|
1447
1404
|
const warnings = [];
|
|
1448
1405
|
const skipped = [];
|
|
1449
1406
|
if (!state.agents) {
|
|
1450
|
-
warnings.push(
|
|
1451
|
-
path: ["AGENTS.md"],
|
|
1452
|
-
message: "No AGENTS.md found - root AGENTS.md will not be created"
|
|
1453
|
-
});
|
|
1407
|
+
warnings.push(createNoAgentsMdWarning("root AGENTS.md"));
|
|
1454
1408
|
}
|
|
1455
1409
|
if (state.rules.length > 0) {
|
|
1456
1410
|
warnings.push({
|
|
@@ -1464,14 +1418,11 @@ var windsurfPlugin = {
|
|
|
1464
1418
|
reason: "Windsurf uses global MCP config at ~/.codeium/windsurf/mcp_config.json - project-level MCP servers are not exported"
|
|
1465
1419
|
});
|
|
1466
1420
|
}
|
|
1467
|
-
if (state.settings?.permissions) {
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
reason: "Windsurf does not support declarative permissions"
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1421
|
+
if (hasPermissionsConfigured(state.settings?.permissions)) {
|
|
1422
|
+
skipped.push({
|
|
1423
|
+
feature: "permissions",
|
|
1424
|
+
reason: "Windsurf does not support declarative permissions"
|
|
1425
|
+
});
|
|
1475
1426
|
}
|
|
1476
1427
|
return { valid: true, errors: [], warnings, skipped };
|
|
1477
1428
|
}
|
|
@@ -1642,23 +1593,37 @@ async function writeFiles(files, options) {
|
|
|
1642
1593
|
async function updateGitignore(rootDir, paths) {
|
|
1643
1594
|
const gitignorePath = path.join(rootDir, ".gitignore");
|
|
1644
1595
|
let content = "";
|
|
1596
|
+
let hasExistingFile = true;
|
|
1645
1597
|
try {
|
|
1646
1598
|
content = await fs4.readFile(gitignorePath, "utf-8");
|
|
1647
1599
|
} catch {
|
|
1600
|
+
hasExistingFile = false;
|
|
1648
1601
|
}
|
|
1649
1602
|
const marker = "# lnai-generated";
|
|
1650
1603
|
const endMarker = "# end lnai-generated";
|
|
1651
|
-
const extractRegex = new RegExp(`${marker}\\n([\\s\\S]*?)${endMarker}`);
|
|
1652
|
-
const match = extractRegex.exec(content);
|
|
1653
|
-
const existingSection = match?.[1] ?? "";
|
|
1654
|
-
const existingPaths = existingSection.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
1655
1604
|
const markerRegex = new RegExp(`${marker}[\\s\\S]*?${endMarker}\\n?`, "g");
|
|
1605
|
+
const hasManagedSection = new RegExp(`${marker}[\\s\\S]*?${endMarker}`).test(
|
|
1606
|
+
content
|
|
1607
|
+
);
|
|
1656
1608
|
content = content.replace(markerRegex, "");
|
|
1657
|
-
|
|
1658
|
-
const uniquePaths = [
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1609
|
+
const baseContent = content.trimEnd();
|
|
1610
|
+
const uniquePaths = [...new Set(paths)].sort();
|
|
1611
|
+
if (uniquePaths.length === 0) {
|
|
1612
|
+
if (!hasManagedSection && !hasExistingFile) {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const cleanedContent = baseContent.length > 0 ? `${baseContent}
|
|
1616
|
+
` : baseContent;
|
|
1617
|
+
await fs4.writeFile(gitignorePath, cleanedContent, "utf-8");
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const managedSection = [marker, ...uniquePaths, endMarker].join("\n");
|
|
1621
|
+
const nextContent = baseContent.length > 0 ? `${baseContent}
|
|
1622
|
+
|
|
1623
|
+
${managedSection}
|
|
1624
|
+
` : `${managedSection}
|
|
1625
|
+
`;
|
|
1626
|
+
await fs4.writeFile(gitignorePath, nextContent, "utf-8");
|
|
1662
1627
|
}
|
|
1663
1628
|
|
|
1664
1629
|
// src/manifest/index.ts
|
|
@@ -1726,23 +1691,6 @@ function createEmptyManifest() {
|
|
|
1726
1691
|
}
|
|
1727
1692
|
|
|
1728
1693
|
// src/pipeline/index.ts
|
|
1729
|
-
function getToolsToSync(config, requestedTools) {
|
|
1730
|
-
if (requestedTools && requestedTools.length > 0) {
|
|
1731
|
-
return requestedTools.filter((tool) => pluginRegistry.has(tool));
|
|
1732
|
-
}
|
|
1733
|
-
const enabledTools = [];
|
|
1734
|
-
if (config.tools) {
|
|
1735
|
-
for (const [toolId, toolConfig] of Object.entries(config.tools)) {
|
|
1736
|
-
if (toolConfig?.enabled && pluginRegistry.has(toolId)) {
|
|
1737
|
-
enabledTools.push(toolId);
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
if (enabledTools.length === 0) {
|
|
1742
|
-
return pluginRegistry.getIds();
|
|
1743
|
-
}
|
|
1744
|
-
return enabledTools;
|
|
1745
|
-
}
|
|
1746
1694
|
async function runSyncPipeline(options) {
|
|
1747
1695
|
const {
|
|
1748
1696
|
rootDir,
|
|
@@ -1777,7 +1725,6 @@ async function runSyncPipeline(options) {
|
|
|
1777
1725
|
}
|
|
1778
1726
|
let manifest = await readManifest(rootDir) ?? createEmptyManifest();
|
|
1779
1727
|
const results = [];
|
|
1780
|
-
const pathsToIgnore = [];
|
|
1781
1728
|
for (const toolId of toolsToSync) {
|
|
1782
1729
|
const plugin = pluginRegistry.get(toolId);
|
|
1783
1730
|
if (!plugin) {
|
|
@@ -1803,19 +1750,57 @@ async function runSyncPipeline(options) {
|
|
|
1803
1750
|
if (!dryRun) {
|
|
1804
1751
|
manifest = updateToolManifest(manifest, toolId, outputFiles);
|
|
1805
1752
|
}
|
|
1806
|
-
const toolConfig = state.config.tools?.[toolId];
|
|
1807
|
-
if (!toolConfig?.versionControl) {
|
|
1808
|
-
pathsToIgnore.push(...outputFiles.map((f) => f.path));
|
|
1809
|
-
}
|
|
1810
1753
|
}
|
|
1811
1754
|
if (!dryRun) {
|
|
1812
1755
|
await writeManifest(rootDir, manifest);
|
|
1813
|
-
|
|
1814
|
-
if (pathsToIgnore.length > 0 && !dryRun) {
|
|
1756
|
+
const pathsToIgnore = computePathsToIgnore(manifest, state.config.tools);
|
|
1815
1757
|
await updateGitignore(rootDir, pathsToIgnore);
|
|
1816
1758
|
}
|
|
1817
1759
|
return results;
|
|
1818
1760
|
}
|
|
1761
|
+
function getToolsToSync(config, requestedTools) {
|
|
1762
|
+
if (requestedTools && requestedTools.length > 0) {
|
|
1763
|
+
return requestedTools.filter((tool) => pluginRegistry.has(tool));
|
|
1764
|
+
}
|
|
1765
|
+
const enabledTools = [];
|
|
1766
|
+
if (config.tools) {
|
|
1767
|
+
for (const [toolId, toolConfig] of Object.entries(config.tools)) {
|
|
1768
|
+
if (toolConfig?.enabled && pluginRegistry.has(toolId)) {
|
|
1769
|
+
enabledTools.push(toolId);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
if (enabledTools.length === 0) {
|
|
1774
|
+
return pluginRegistry.getIds();
|
|
1775
|
+
}
|
|
1776
|
+
return enabledTools;
|
|
1777
|
+
}
|
|
1778
|
+
function computePathsToIgnore(manifest, toolConfigs) {
|
|
1779
|
+
const pathToTools = /* @__PURE__ */ new Map();
|
|
1780
|
+
for (const [toolId, toolManifest] of Object.entries(manifest.tools)) {
|
|
1781
|
+
if (!toolManifest?.files) {
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1784
|
+
for (const file of toolManifest.files) {
|
|
1785
|
+
let tools = pathToTools.get(file.path);
|
|
1786
|
+
if (!tools) {
|
|
1787
|
+
tools = /* @__PURE__ */ new Set();
|
|
1788
|
+
pathToTools.set(file.path, tools);
|
|
1789
|
+
}
|
|
1790
|
+
tools.add(toolId);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
const result = [];
|
|
1794
|
+
for (const [filePath, toolIds] of pathToTools) {
|
|
1795
|
+
const anyVersionControlled = [...toolIds].some(
|
|
1796
|
+
(toolId) => toolConfigs?.[toolId]?.versionControl === true
|
|
1797
|
+
);
|
|
1798
|
+
if (!anyVersionControlled) {
|
|
1799
|
+
result.push(filePath);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return result;
|
|
1803
|
+
}
|
|
1819
1804
|
async function initUnifiedConfig(options) {
|
|
1820
1805
|
const {
|
|
1821
1806
|
rootDir,
|