@lnai/core 0.1.1 → 0.2.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 +10 -11
- package/dist/index.js +284 -38
- package/package.json +2 -1
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"];
|
|
4
|
+
declare const TOOL_IDS: readonly ["claudeCode", "opencode", "cursor"];
|
|
5
5
|
type ToolId = (typeof TOOL_IDS)[number];
|
|
6
6
|
declare const CONFIG_FILES: {
|
|
7
7
|
readonly config: "config.json";
|
|
@@ -65,6 +65,7 @@ declare const toolConfigSchema: z.ZodObject<{
|
|
|
65
65
|
declare const toolIdSchema: z.ZodEnum<{
|
|
66
66
|
claudeCode: "claudeCode";
|
|
67
67
|
opencode: "opencode";
|
|
68
|
+
cursor: "cursor";
|
|
68
69
|
}>;
|
|
69
70
|
/** Settings configuration (Claude format as source of truth) */
|
|
70
71
|
declare const settingsSchema: z.ZodObject<{
|
|
@@ -87,6 +88,7 @@ declare const settingsSchema: z.ZodObject<{
|
|
|
87
88
|
overrides: z.ZodOptional<z.ZodObject<{
|
|
88
89
|
claudeCode: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
89
90
|
opencode: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
91
|
+
cursor: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
90
92
|
}, z.core.$strip>>;
|
|
91
93
|
}, z.core.$strip>;
|
|
92
94
|
/** Main config.json structure. Uses partial object to allow partial tool configs. */
|
|
@@ -100,6 +102,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
100
102
|
enabled: z.ZodBoolean;
|
|
101
103
|
versionControl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
102
104
|
}, z.core.$strip>>;
|
|
105
|
+
cursor: z.ZodOptional<z.ZodObject<{
|
|
106
|
+
enabled: z.ZodBoolean;
|
|
107
|
+
versionControl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
108
|
+
}, z.core.$strip>>;
|
|
103
109
|
}, z.core.$strip>>;
|
|
104
110
|
}, z.core.$strip>;
|
|
105
111
|
/** Skill frontmatter (name and description required) */
|
|
@@ -134,18 +140,11 @@ interface MarkdownFrontmatter {
|
|
|
134
140
|
/** All parsed configuration from the .ai directory */
|
|
135
141
|
interface UnifiedState {
|
|
136
142
|
config: {
|
|
137
|
-
tools?: Partial<Record<ToolId,
|
|
138
|
-
enabled: boolean;
|
|
139
|
-
versionControl?: boolean;
|
|
140
|
-
}>>;
|
|
143
|
+
tools?: Partial<Record<ToolId, ToolConfig>>;
|
|
141
144
|
};
|
|
142
145
|
settings: {
|
|
143
|
-
permissions?:
|
|
144
|
-
|
|
145
|
-
ask?: string[];
|
|
146
|
-
deny?: string[];
|
|
147
|
-
};
|
|
148
|
-
mcpServers?: Record<string, unknown>;
|
|
146
|
+
permissions?: Permissions;
|
|
147
|
+
mcpServers?: Record<string, McpServer>;
|
|
149
148
|
overrides?: Partial<Record<ToolId, Record<string, unknown>>>;
|
|
150
149
|
} | null;
|
|
151
150
|
agents: string | null;
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as crypto from 'crypto';
|
|
|
7
7
|
|
|
8
8
|
// src/constants.ts
|
|
9
9
|
var UNIFIED_DIR = ".ai";
|
|
10
|
-
var TOOL_IDS = ["claudeCode", "opencode"];
|
|
10
|
+
var TOOL_IDS = ["claudeCode", "opencode", "cursor"];
|
|
11
11
|
var CONFIG_FILES = {
|
|
12
12
|
config: "config.json",
|
|
13
13
|
settings: "settings.json",
|
|
@@ -20,11 +20,13 @@ var CONFIG_DIRS = {
|
|
|
20
20
|
};
|
|
21
21
|
var TOOL_OUTPUT_DIRS = {
|
|
22
22
|
claudeCode: ".claude",
|
|
23
|
-
opencode: ".opencode"
|
|
23
|
+
opencode: ".opencode",
|
|
24
|
+
cursor: ".cursor"
|
|
24
25
|
};
|
|
25
26
|
var OVERRIDE_DIRS = {
|
|
26
27
|
claudeCode: ".claude",
|
|
27
|
-
opencode: ".opencode"
|
|
28
|
+
opencode: ".opencode",
|
|
29
|
+
cursor: ".cursor"
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
// src/errors.ts
|
|
@@ -50,10 +52,10 @@ var ParseError = class extends LnaiError {
|
|
|
50
52
|
var ValidationError = class extends LnaiError {
|
|
51
53
|
path;
|
|
52
54
|
value;
|
|
53
|
-
constructor(message,
|
|
55
|
+
constructor(message, path5, value) {
|
|
54
56
|
super(message, "VALIDATION_ERROR");
|
|
55
57
|
this.name = "ValidationError";
|
|
56
|
-
this.path =
|
|
58
|
+
this.path = path5;
|
|
57
59
|
this.value = value;
|
|
58
60
|
}
|
|
59
61
|
};
|
|
@@ -104,19 +106,21 @@ var toolConfigSchema = z.object({
|
|
|
104
106
|
enabled: z.boolean(),
|
|
105
107
|
versionControl: z.boolean().optional().default(false)
|
|
106
108
|
});
|
|
107
|
-
var toolIdSchema = z.enum(["claudeCode", "opencode"]);
|
|
109
|
+
var toolIdSchema = z.enum(["claudeCode", "opencode", "cursor"]);
|
|
108
110
|
var settingsSchema = z.object({
|
|
109
111
|
permissions: permissionsSchema.optional(),
|
|
110
112
|
mcpServers: z.record(z.string(), mcpServerSchema).optional(),
|
|
111
113
|
overrides: z.object({
|
|
112
114
|
claudeCode: z.record(z.string(), z.unknown()).optional(),
|
|
113
|
-
opencode: z.record(z.string(), z.unknown()).optional()
|
|
115
|
+
opencode: z.record(z.string(), z.unknown()).optional(),
|
|
116
|
+
cursor: z.record(z.string(), z.unknown()).optional()
|
|
114
117
|
}).optional()
|
|
115
118
|
});
|
|
116
119
|
var configSchema = z.object({
|
|
117
120
|
tools: z.object({
|
|
118
121
|
claudeCode: toolConfigSchema,
|
|
119
|
-
opencode: toolConfigSchema
|
|
122
|
+
opencode: toolConfigSchema,
|
|
123
|
+
cursor: toolConfigSchema
|
|
120
124
|
}).partial().optional()
|
|
121
125
|
});
|
|
122
126
|
var skillFrontmatterSchema = z.object({
|
|
@@ -412,6 +416,26 @@ async function fileExists2(filePath) {
|
|
|
412
416
|
return false;
|
|
413
417
|
}
|
|
414
418
|
}
|
|
419
|
+
async function getOverrideOutputFiles(rootDir, toolId) {
|
|
420
|
+
const outputDir = TOOL_OUTPUT_DIRS[toolId];
|
|
421
|
+
const overrideFiles = await scanOverrideDirectory(rootDir, toolId);
|
|
422
|
+
const result = [];
|
|
423
|
+
for (const overrideFile of overrideFiles) {
|
|
424
|
+
const targetPath = path.join(rootDir, outputDir, overrideFile.relativePath);
|
|
425
|
+
if (await fileExists2(targetPath)) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const symlinkPath = `${outputDir}/${overrideFile.relativePath}`;
|
|
429
|
+
const symlinkDir = path.dirname(symlinkPath);
|
|
430
|
+
const sourcePath = `${UNIFIED_DIR}/${OVERRIDE_DIRS[toolId]}/${overrideFile.relativePath}`;
|
|
431
|
+
result.push({
|
|
432
|
+
path: symlinkPath,
|
|
433
|
+
type: "symlink",
|
|
434
|
+
target: path.relative(symlinkDir, sourcePath)
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
415
439
|
|
|
416
440
|
// src/plugins/claude-code/index.ts
|
|
417
441
|
var claudeCodePlugin = {
|
|
@@ -468,22 +492,236 @@ var claudeCodePlugin = {
|
|
|
468
492
|
content: finalSettings
|
|
469
493
|
});
|
|
470
494
|
}
|
|
471
|
-
const overrideFiles = await
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
495
|
+
const overrideFiles = await getOverrideOutputFiles(rootDir, "claudeCode");
|
|
496
|
+
files.push(...overrideFiles);
|
|
497
|
+
return files;
|
|
498
|
+
},
|
|
499
|
+
validate(state) {
|
|
500
|
+
const warnings = [];
|
|
501
|
+
if (!state.agents) {
|
|
502
|
+
warnings.push({
|
|
503
|
+
path: ["AGENTS.md"],
|
|
504
|
+
message: "No AGENTS.md found - .claude/CLAUDE.md will not be created"
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return { valid: true, errors: [], warnings, skipped: [] };
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/plugins/cursor/transforms.ts
|
|
512
|
+
function transformRuleToCursor(rule) {
|
|
513
|
+
const description = deriveDescription(rule.path, rule.content);
|
|
514
|
+
const globs = rule.frontmatter.paths || [];
|
|
515
|
+
const alwaysApply = globs.length === 0;
|
|
516
|
+
return {
|
|
517
|
+
frontmatter: {
|
|
518
|
+
description,
|
|
519
|
+
globs,
|
|
520
|
+
alwaysApply
|
|
521
|
+
},
|
|
522
|
+
content: rule.content
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function deriveDescription(filename, content) {
|
|
526
|
+
const headingMatch = content.match(/^#\s+(.+)$/m);
|
|
527
|
+
if (headingMatch && headingMatch[1]) {
|
|
528
|
+
return headingMatch[1];
|
|
529
|
+
}
|
|
530
|
+
const baseName = filename.replace(/\.md$/, "");
|
|
531
|
+
return baseName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
532
|
+
}
|
|
533
|
+
function serializeCursorRule(frontmatter, content) {
|
|
534
|
+
const lines = [
|
|
535
|
+
"---",
|
|
536
|
+
`description: ${JSON.stringify(frontmatter.description)}`
|
|
537
|
+
];
|
|
538
|
+
lines.push("globs:");
|
|
539
|
+
for (const glob of frontmatter.globs) {
|
|
540
|
+
lines.push(` - ${JSON.stringify(glob)}`);
|
|
541
|
+
}
|
|
542
|
+
lines.push(`alwaysApply: ${frontmatter.alwaysApply}`);
|
|
543
|
+
lines.push("---");
|
|
544
|
+
lines.push("");
|
|
545
|
+
lines.push(content);
|
|
546
|
+
return lines.join("\n");
|
|
547
|
+
}
|
|
548
|
+
function transformMcpToCursor(servers) {
|
|
549
|
+
if (!servers || Object.keys(servers).length === 0) {
|
|
550
|
+
return void 0;
|
|
551
|
+
}
|
|
552
|
+
const result = {};
|
|
553
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
554
|
+
if (server.type === "http" || server.type === "sse") {
|
|
555
|
+
const cursorServer = {
|
|
556
|
+
url: server.url
|
|
557
|
+
};
|
|
558
|
+
if (server.headers) {
|
|
559
|
+
cursorServer.headers = transformEnvVarsToCursor(server.headers);
|
|
560
|
+
}
|
|
561
|
+
result[name] = cursorServer;
|
|
562
|
+
} else if (server.command) {
|
|
563
|
+
const cursorServer = {
|
|
564
|
+
command: server.command
|
|
565
|
+
};
|
|
566
|
+
if (server.args && server.args.length > 0) {
|
|
567
|
+
cursorServer.args = server.args;
|
|
480
568
|
}
|
|
569
|
+
if (server.env) {
|
|
570
|
+
cursorServer.env = transformEnvVarsToCursor(server.env);
|
|
571
|
+
}
|
|
572
|
+
result[name] = cursorServer;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
576
|
+
}
|
|
577
|
+
function transformEnvVarToCursor(value) {
|
|
578
|
+
return value.replace(/\$\{([^}:]+)(:-[^}]*)?\}/g, "${env:$1}");
|
|
579
|
+
}
|
|
580
|
+
function transformEnvVarsToCursor(env) {
|
|
581
|
+
const result = {};
|
|
582
|
+
for (const [key, value] of Object.entries(env)) {
|
|
583
|
+
result[key] = transformEnvVarToCursor(value);
|
|
584
|
+
}
|
|
585
|
+
return result;
|
|
586
|
+
}
|
|
587
|
+
function transformPermissionsToCursor(permissions) {
|
|
588
|
+
if (!permissions) {
|
|
589
|
+
return { permissions: void 0, hasAskPermissions: false };
|
|
590
|
+
}
|
|
591
|
+
const allow = [];
|
|
592
|
+
const deny = [];
|
|
593
|
+
let hasAskPermissions = false;
|
|
594
|
+
if (permissions.allow) {
|
|
595
|
+
for (const rule of permissions.allow) {
|
|
596
|
+
const transformed = transformPermissionRule(rule);
|
|
597
|
+
if (transformed) {
|
|
598
|
+
allow.push(transformed);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (permissions.ask && permissions.ask.length > 0) {
|
|
603
|
+
hasAskPermissions = true;
|
|
604
|
+
for (const rule of permissions.ask) {
|
|
605
|
+
const transformed = transformPermissionRule(rule);
|
|
606
|
+
if (transformed) {
|
|
607
|
+
allow.push(transformed);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (permissions.deny) {
|
|
612
|
+
for (const rule of permissions.deny) {
|
|
613
|
+
const transformed = transformPermissionRule(rule);
|
|
614
|
+
if (transformed) {
|
|
615
|
+
deny.push(transformed);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (allow.length === 0 && deny.length === 0) {
|
|
620
|
+
return { permissions: void 0, hasAskPermissions };
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
permissions: {
|
|
624
|
+
allow,
|
|
625
|
+
deny
|
|
626
|
+
},
|
|
627
|
+
hasAskPermissions
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function transformPermissionRule(rule) {
|
|
631
|
+
const match = rule.match(/^(\w+)\(([^)]+)\)$/);
|
|
632
|
+
if (!match) {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
const tool = match[1];
|
|
636
|
+
let pattern = match[2];
|
|
637
|
+
const cursorTool = tool.toLowerCase() === "bash" ? "Shell" : tool;
|
|
638
|
+
if (pattern.endsWith(":*")) {
|
|
639
|
+
pattern = pattern.slice(0, -2);
|
|
640
|
+
}
|
|
641
|
+
return `${cursorTool}(${pattern})`;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/plugins/cursor/index.ts
|
|
645
|
+
var cursorPlugin = {
|
|
646
|
+
id: "cursor",
|
|
647
|
+
name: "Cursor",
|
|
648
|
+
async detect(_rootDir) {
|
|
649
|
+
return false;
|
|
650
|
+
},
|
|
651
|
+
async import(_rootDir) {
|
|
652
|
+
return null;
|
|
653
|
+
},
|
|
654
|
+
async export(state, rootDir) {
|
|
655
|
+
const files = [];
|
|
656
|
+
const outputDir = TOOL_OUTPUT_DIRS.cursor;
|
|
657
|
+
if (state.agents) {
|
|
481
658
|
files.push({
|
|
482
|
-
path:
|
|
659
|
+
path: "AGENTS.md",
|
|
483
660
|
type: "symlink",
|
|
484
|
-
target:
|
|
661
|
+
target: `${UNIFIED_DIR}/AGENTS.md`
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
for (const rule of state.rules) {
|
|
665
|
+
const transformed = transformRuleToCursor(rule);
|
|
666
|
+
const ruleContent = serializeCursorRule(
|
|
667
|
+
transformed.frontmatter,
|
|
668
|
+
transformed.content
|
|
669
|
+
);
|
|
670
|
+
const outputFilename = rule.path.replace(/\.md$/, ".mdc");
|
|
671
|
+
files.push({
|
|
672
|
+
path: `${outputDir}/rules/${outputFilename}`,
|
|
673
|
+
type: "text",
|
|
674
|
+
content: ruleContent
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
for (const skill of state.skills) {
|
|
678
|
+
files.push({
|
|
679
|
+
path: `${outputDir}/skills/${skill.path}`,
|
|
680
|
+
type: "symlink",
|
|
681
|
+
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
485
682
|
});
|
|
486
683
|
}
|
|
684
|
+
const mcpServers = transformMcpToCursor(state.settings?.mcpServers);
|
|
685
|
+
const cursorOverrides = state.settings?.overrides?.cursor;
|
|
686
|
+
const hasMcpOverrides = cursorOverrides?.["mcpServers"] !== void 0;
|
|
687
|
+
if (mcpServers || hasMcpOverrides) {
|
|
688
|
+
let mcpContent = mcpServers ? { mcpServers } : {};
|
|
689
|
+
if (cursorOverrides?.["mcpServers"]) {
|
|
690
|
+
mcpContent = deepMergeConfigs(mcpContent, {
|
|
691
|
+
mcpServers: cursorOverrides["mcpServers"]
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
files.push({
|
|
695
|
+
path: `${outputDir}/mcp.json`,
|
|
696
|
+
type: "json",
|
|
697
|
+
content: mcpContent
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
const permissionsResult = transformPermissionsToCursor(
|
|
701
|
+
state.settings?.permissions
|
|
702
|
+
);
|
|
703
|
+
const hasCliOverrides = cursorOverrides !== void 0 && Object.keys(cursorOverrides).some((key) => key !== "mcpServers");
|
|
704
|
+
if (permissionsResult.permissions || hasCliOverrides) {
|
|
705
|
+
let cliContent = permissionsResult.permissions ? { permissions: permissionsResult.permissions } : {};
|
|
706
|
+
if (cursorOverrides) {
|
|
707
|
+
const cliOverrides = {};
|
|
708
|
+
for (const [key, value] of Object.entries(cursorOverrides)) {
|
|
709
|
+
if (key !== "mcpServers") {
|
|
710
|
+
cliOverrides[key] = value;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (Object.keys(cliOverrides).length > 0) {
|
|
714
|
+
cliContent = deepMergeConfigs(cliContent, cliOverrides);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
files.push({
|
|
718
|
+
path: `${outputDir}/cli.json`,
|
|
719
|
+
type: "json",
|
|
720
|
+
content: cliContent
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
const overrideFiles = await getOverrideOutputFiles(rootDir, "cursor");
|
|
724
|
+
files.push(...overrideFiles);
|
|
487
725
|
return files;
|
|
488
726
|
},
|
|
489
727
|
validate(state) {
|
|
@@ -491,9 +729,31 @@ var claudeCodePlugin = {
|
|
|
491
729
|
if (!state.agents) {
|
|
492
730
|
warnings.push({
|
|
493
731
|
path: ["AGENTS.md"],
|
|
494
|
-
message: "No AGENTS.md found - .
|
|
732
|
+
message: "No AGENTS.md found - root AGENTS.md will not be created"
|
|
495
733
|
});
|
|
496
734
|
}
|
|
735
|
+
const permissionsResult = transformPermissionsToCursor(
|
|
736
|
+
state.settings?.permissions
|
|
737
|
+
);
|
|
738
|
+
if (permissionsResult.hasAskPermissions) {
|
|
739
|
+
warnings.push({
|
|
740
|
+
path: ["settings", "permissions", "ask"],
|
|
741
|
+
message: 'Cursor does not support "ask" permission level - these rules will be treated as "allow"'
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
const mcpServers = state.settings?.mcpServers;
|
|
745
|
+
if (mcpServers) {
|
|
746
|
+
for (const [name, server] of Object.entries(mcpServers)) {
|
|
747
|
+
const isRemote = server.type === "http" || server.type === "sse";
|
|
748
|
+
const hasCommand = !!server.command;
|
|
749
|
+
if (!isRemote && !hasCommand) {
|
|
750
|
+
warnings.push({
|
|
751
|
+
path: ["settings", "mcpServers", name],
|
|
752
|
+
message: `MCP server "${name}" has no command or type - it will be skipped`
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
497
757
|
return { valid: true, errors: [], warnings, skipped: [] };
|
|
498
758
|
}
|
|
499
759
|
};
|
|
@@ -504,8 +764,7 @@ function transformMcpToOpenCode(servers) {
|
|
|
504
764
|
return void 0;
|
|
505
765
|
}
|
|
506
766
|
const result = {};
|
|
507
|
-
for (const [name,
|
|
508
|
-
const server = serverRaw;
|
|
767
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
509
768
|
if (server.type === "http" || server.type === "sse") {
|
|
510
769
|
const openCodeServer = {
|
|
511
770
|
type: "remote",
|
|
@@ -648,22 +907,8 @@ var opencodePlugin = {
|
|
|
648
907
|
type: "json",
|
|
649
908
|
content: finalConfig
|
|
650
909
|
});
|
|
651
|
-
const overrideFiles = await
|
|
652
|
-
|
|
653
|
-
const targetPath = path.join(
|
|
654
|
-
rootDir,
|
|
655
|
-
outputDir,
|
|
656
|
-
overrideFile.relativePath
|
|
657
|
-
);
|
|
658
|
-
if (await fileExists2(targetPath)) {
|
|
659
|
-
continue;
|
|
660
|
-
}
|
|
661
|
-
files.push({
|
|
662
|
-
path: `${outputDir}/${overrideFile.relativePath}`,
|
|
663
|
-
type: "symlink",
|
|
664
|
-
target: `../${UNIFIED_DIR}/${OVERRIDE_DIRS.opencode}/${overrideFile.relativePath}`
|
|
665
|
-
});
|
|
666
|
-
}
|
|
910
|
+
const overrideFiles = await getOverrideOutputFiles(rootDir, "opencode");
|
|
911
|
+
files.push(...overrideFiles);
|
|
667
912
|
return files;
|
|
668
913
|
},
|
|
669
914
|
validate(state) {
|
|
@@ -701,6 +946,7 @@ var pluginRegistry = new PluginRegistry();
|
|
|
701
946
|
|
|
702
947
|
// src/plugins/index.ts
|
|
703
948
|
pluginRegistry.register(claudeCodePlugin);
|
|
949
|
+
pluginRegistry.register(cursorPlugin);
|
|
704
950
|
pluginRegistry.register(opencodePlugin);
|
|
705
951
|
function computeHash(content) {
|
|
706
952
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lnai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Core library for LNAI - unified AI config management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"config",
|
|
20
20
|
"configuration",
|
|
21
21
|
"claude",
|
|
22
|
+
"cursor",
|
|
22
23
|
"opencode",
|
|
23
24
|
"cli",
|
|
24
25
|
"ai-tools"
|