@lnai/core 0.1.0 → 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 +12 -12
- package/dist/index.js +365 -71
- package/package.json +2 -1
- package/dist/index.js.map +0 -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;
|
|
@@ -302,12 +301,13 @@ interface InitOptions {
|
|
|
302
301
|
rootDir: string;
|
|
303
302
|
tools?: ToolId[];
|
|
304
303
|
minimal?: boolean;
|
|
304
|
+
force?: boolean;
|
|
305
305
|
}
|
|
306
306
|
interface InitResult {
|
|
307
307
|
created: string[];
|
|
308
308
|
}
|
|
309
|
+
declare function initUnifiedConfig(options: InitOptions): Promise<InitResult>;
|
|
309
310
|
declare function hasUnifiedConfig(rootDir: string): Promise<boolean>;
|
|
310
311
|
declare function generateDefaultConfig(tools?: ToolId[]): Config;
|
|
311
|
-
declare function initUnifiedConfig(options: InitOptions): Promise<InitResult>;
|
|
312
312
|
|
|
313
313
|
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 };
|
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({
|
|
@@ -356,6 +360,26 @@ function validateUnifiedState(state) {
|
|
|
356
360
|
skipped: []
|
|
357
361
|
};
|
|
358
362
|
}
|
|
363
|
+
function validateToolIds(tools) {
|
|
364
|
+
const invalidTools = tools.filter(
|
|
365
|
+
(t) => !TOOL_IDS.includes(t)
|
|
366
|
+
);
|
|
367
|
+
if (invalidTools.length > 0) {
|
|
368
|
+
return {
|
|
369
|
+
valid: false,
|
|
370
|
+
errors: [
|
|
371
|
+
{
|
|
372
|
+
path: ["tools"],
|
|
373
|
+
message: `Invalid tool(s): ${invalidTools.join(", ")}. Valid tools: ${TOOL_IDS.join(", ")}`,
|
|
374
|
+
value: invalidTools
|
|
375
|
+
}
|
|
376
|
+
],
|
|
377
|
+
warnings: [],
|
|
378
|
+
skipped: []
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return { valid: true, errors: [], warnings: [], skipped: [] };
|
|
382
|
+
}
|
|
359
383
|
async function scanOverrideDirectory(rootDir, toolId) {
|
|
360
384
|
const overrideDir = path.join(rootDir, UNIFIED_DIR, OVERRIDE_DIRS[toolId]);
|
|
361
385
|
try {
|
|
@@ -392,6 +416,26 @@ async function fileExists2(filePath) {
|
|
|
392
416
|
return false;
|
|
393
417
|
}
|
|
394
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
|
+
}
|
|
395
439
|
|
|
396
440
|
// src/plugins/claude-code/index.ts
|
|
397
441
|
var claudeCodePlugin = {
|
|
@@ -448,22 +492,236 @@ var claudeCodePlugin = {
|
|
|
448
492
|
content: finalSettings
|
|
449
493
|
});
|
|
450
494
|
}
|
|
451
|
-
const overrideFiles = await
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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;
|
|
568
|
+
}
|
|
569
|
+
if (server.env) {
|
|
570
|
+
cursorServer.env = transformEnvVarsToCursor(server.env);
|
|
460
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) {
|
|
658
|
+
files.push({
|
|
659
|
+
path: "AGENTS.md",
|
|
660
|
+
type: "symlink",
|
|
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) {
|
|
461
678
|
files.push({
|
|
462
|
-
path: `${outputDir}/${
|
|
679
|
+
path: `${outputDir}/skills/${skill.path}`,
|
|
463
680
|
type: "symlink",
|
|
464
|
-
target:
|
|
681
|
+
target: `../../${UNIFIED_DIR}/skills/${skill.path}`
|
|
682
|
+
});
|
|
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
|
|
465
721
|
});
|
|
466
722
|
}
|
|
723
|
+
const overrideFiles = await getOverrideOutputFiles(rootDir, "cursor");
|
|
724
|
+
files.push(...overrideFiles);
|
|
467
725
|
return files;
|
|
468
726
|
},
|
|
469
727
|
validate(state) {
|
|
@@ -471,9 +729,31 @@ var claudeCodePlugin = {
|
|
|
471
729
|
if (!state.agents) {
|
|
472
730
|
warnings.push({
|
|
473
731
|
path: ["AGENTS.md"],
|
|
474
|
-
message: "No AGENTS.md found - .
|
|
732
|
+
message: "No AGENTS.md found - root AGENTS.md will not be created"
|
|
733
|
+
});
|
|
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"'
|
|
475
742
|
});
|
|
476
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
|
+
}
|
|
477
757
|
return { valid: true, errors: [], warnings, skipped: [] };
|
|
478
758
|
}
|
|
479
759
|
};
|
|
@@ -484,8 +764,7 @@ function transformMcpToOpenCode(servers) {
|
|
|
484
764
|
return void 0;
|
|
485
765
|
}
|
|
486
766
|
const result = {};
|
|
487
|
-
for (const [name,
|
|
488
|
-
const server = serverRaw;
|
|
767
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
489
768
|
if (server.type === "http" || server.type === "sse") {
|
|
490
769
|
const openCodeServer = {
|
|
491
770
|
type: "remote",
|
|
@@ -628,22 +907,8 @@ var opencodePlugin = {
|
|
|
628
907
|
type: "json",
|
|
629
908
|
content: finalConfig
|
|
630
909
|
});
|
|
631
|
-
const overrideFiles = await
|
|
632
|
-
|
|
633
|
-
const targetPath = path.join(
|
|
634
|
-
rootDir,
|
|
635
|
-
outputDir,
|
|
636
|
-
overrideFile.relativePath
|
|
637
|
-
);
|
|
638
|
-
if (await fileExists2(targetPath)) {
|
|
639
|
-
continue;
|
|
640
|
-
}
|
|
641
|
-
files.push({
|
|
642
|
-
path: `${outputDir}/${overrideFile.relativePath}`,
|
|
643
|
-
type: "symlink",
|
|
644
|
-
target: `../${UNIFIED_DIR}/${OVERRIDE_DIRS.opencode}/${overrideFile.relativePath}`
|
|
645
|
-
});
|
|
646
|
-
}
|
|
910
|
+
const overrideFiles = await getOverrideOutputFiles(rootDir, "opencode");
|
|
911
|
+
files.push(...overrideFiles);
|
|
647
912
|
return files;
|
|
648
913
|
},
|
|
649
914
|
validate(state) {
|
|
@@ -681,6 +946,7 @@ var pluginRegistry = new PluginRegistry();
|
|
|
681
946
|
|
|
682
947
|
// src/plugins/index.ts
|
|
683
948
|
pluginRegistry.register(claudeCodePlugin);
|
|
949
|
+
pluginRegistry.register(cursorPlugin);
|
|
684
950
|
pluginRegistry.register(opencodePlugin);
|
|
685
951
|
function computeHash(content) {
|
|
686
952
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
@@ -821,16 +1087,26 @@ function getToolsToSync(config, requestedTools) {
|
|
|
821
1087
|
}
|
|
822
1088
|
async function runSyncPipeline(options) {
|
|
823
1089
|
const { rootDir, dryRun = false, tools: requestedTools } = options;
|
|
1090
|
+
if (requestedTools && requestedTools.length > 0) {
|
|
1091
|
+
const toolValidation = validateToolIds(requestedTools);
|
|
1092
|
+
if (!toolValidation.valid) {
|
|
1093
|
+
const error = toolValidation.errors[0];
|
|
1094
|
+
throw new ValidationError(
|
|
1095
|
+
error?.message ?? "Invalid tools",
|
|
1096
|
+
error?.path ?? ["tools"],
|
|
1097
|
+
error?.value
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
824
1101
|
const state = await parseUnifiedConfig(rootDir);
|
|
825
1102
|
const unifiedValidation = validateUnifiedState(state);
|
|
826
1103
|
if (!unifiedValidation.valid) {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
];
|
|
1104
|
+
const errorMessages = unifiedValidation.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
|
|
1105
|
+
throw new ValidationError(
|
|
1106
|
+
`Unified config validation failed: ${errorMessages}`,
|
|
1107
|
+
["config"],
|
|
1108
|
+
unifiedValidation.errors
|
|
1109
|
+
);
|
|
834
1110
|
}
|
|
835
1111
|
const toolsToSync = getToolsToSync(state.config, requestedTools);
|
|
836
1112
|
if (toolsToSync.length === 0) {
|
|
@@ -861,35 +1137,24 @@ async function runSyncPipeline(options) {
|
|
|
861
1137
|
}
|
|
862
1138
|
return results;
|
|
863
1139
|
}
|
|
864
|
-
async function hasUnifiedConfig(rootDir) {
|
|
865
|
-
const aiDir = path.join(rootDir, UNIFIED_DIR);
|
|
866
|
-
try {
|
|
867
|
-
const stats = await fs3.stat(aiDir);
|
|
868
|
-
return stats.isDirectory();
|
|
869
|
-
} catch {
|
|
870
|
-
return false;
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
function generateDefaultConfig(tools) {
|
|
874
|
-
const enabledTools = tools ?? TOOL_IDS;
|
|
875
|
-
const toolsConfig = {};
|
|
876
|
-
for (const toolId of TOOL_IDS) {
|
|
877
|
-
toolsConfig[toolId] = {
|
|
878
|
-
enabled: enabledTools.includes(toolId),
|
|
879
|
-
versionControl: false
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
return {
|
|
883
|
-
tools: toolsConfig
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
1140
|
async function initUnifiedConfig(options) {
|
|
887
|
-
const { rootDir, tools, minimal = false } = options;
|
|
1141
|
+
const { rootDir, tools, minimal = false, force = false } = options;
|
|
888
1142
|
const aiDir = path.join(rootDir, UNIFIED_DIR);
|
|
889
1143
|
const created = [];
|
|
1144
|
+
const config = generateDefaultConfig(tools);
|
|
1145
|
+
const exists = await hasUnifiedConfig(rootDir);
|
|
1146
|
+
if (exists && !force) {
|
|
1147
|
+
throw new ValidationError(
|
|
1148
|
+
`Directory ${UNIFIED_DIR}/ already exists. Use force option to overwrite.`,
|
|
1149
|
+
[UNIFIED_DIR],
|
|
1150
|
+
{ exists: true }
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
if (exists) {
|
|
1154
|
+
await fs3.rm(aiDir, { recursive: true, force: true });
|
|
1155
|
+
}
|
|
890
1156
|
await fs3.mkdir(aiDir, { recursive: true });
|
|
891
1157
|
created.push(UNIFIED_DIR);
|
|
892
|
-
const config = generateDefaultConfig(tools);
|
|
893
1158
|
const configPath = path.join(aiDir, CONFIG_FILES.config);
|
|
894
1159
|
await fs3.writeFile(
|
|
895
1160
|
configPath,
|
|
@@ -909,7 +1174,36 @@ async function initUnifiedConfig(options) {
|
|
|
909
1174
|
}
|
|
910
1175
|
return { created };
|
|
911
1176
|
}
|
|
1177
|
+
async function hasUnifiedConfig(rootDir) {
|
|
1178
|
+
const aiDir = path.join(rootDir, UNIFIED_DIR);
|
|
1179
|
+
try {
|
|
1180
|
+
const stats = await fs3.stat(aiDir);
|
|
1181
|
+
return stats.isDirectory();
|
|
1182
|
+
} catch {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
function generateDefaultConfig(tools) {
|
|
1187
|
+
if (tools) {
|
|
1188
|
+
const validation = validateToolIds(tools);
|
|
1189
|
+
if (!validation.valid) {
|
|
1190
|
+
const error = validation.errors[0];
|
|
1191
|
+
throw new ValidationError(
|
|
1192
|
+
error?.message ?? "Invalid tools",
|
|
1193
|
+
error?.path ?? ["tools"],
|
|
1194
|
+
error?.value
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const enabledTools = tools ?? TOOL_IDS;
|
|
1199
|
+
const toolsConfig = {};
|
|
1200
|
+
for (const toolId of TOOL_IDS) {
|
|
1201
|
+
toolsConfig[toolId] = {
|
|
1202
|
+
enabled: enabledTools.includes(toolId),
|
|
1203
|
+
versionControl: false
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
return { tools: toolsConfig };
|
|
1207
|
+
}
|
|
912
1208
|
|
|
913
1209
|
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 };
|
|
914
|
-
//# sourceMappingURL=index.js.map
|
|
915
|
-
//# sourceMappingURL=index.js.map
|
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"
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/schemas/index.ts","../src/parser/frontmatter.ts","../src/parser/index.ts","../src/validator/index.ts","../src/utils/overrides.ts","../src/plugins/claude-code/index.ts","../src/plugins/opencode/transforms.ts","../src/plugins/opencode/index.ts","../src/plugins/registry.ts","../src/plugins/index.ts","../src/writer/index.ts","../src/pipeline/index.ts","../src/init/index.ts"],"names":["path","fs","path2","fs2","fileExists","path3","path4","path5","path6","fs4"],"mappings":"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","file":"index.js","sourcesContent":["export const UNIFIED_DIR = \".ai\";\n\nexport const TOOL_IDS = [\"claudeCode\", \"opencode\"] as const;\n\nexport type ToolId = (typeof TOOL_IDS)[number];\n\nexport const CONFIG_FILES = {\n config: \"config.json\",\n settings: \"settings.json\",\n agents: \"AGENTS.md\",\n} as const;\n\nexport const CONFIG_DIRS = {\n rules: \"rules\",\n skills: \"skills\",\n subagents: \"subagents\",\n} as const;\n\nexport const TOOL_OUTPUT_DIRS: Record<ToolId, string> = {\n claudeCode: \".claude\",\n opencode: \".opencode\",\n};\n\n/** Tool-specific override directories within .ai/ */\nexport const OVERRIDE_DIRS: Record<ToolId, string> = {\n claudeCode: \".claude\",\n opencode: \".opencode\",\n};\n","export class LnaiError extends Error {\n public readonly code: string;\n\n constructor(message: string, code: string) {\n super(message);\n this.name = \"LnaiError\";\n this.code = code;\n }\n}\n\nexport class ParseError extends LnaiError {\n public readonly filePath: string;\n\n constructor(message: string, filePath: string, cause?: Error) {\n super(message, \"PARSE_ERROR\");\n this.name = \"ParseError\";\n this.filePath = filePath;\n if (cause) {\n this.cause = cause;\n }\n }\n}\n\nexport class ValidationError extends LnaiError {\n public readonly path: string[];\n public readonly value?: unknown;\n\n constructor(message: string, path: string[], value?: unknown) {\n super(message, \"VALIDATION_ERROR\");\n this.name = \"ValidationError\";\n this.path = path;\n this.value = value;\n }\n}\n\nexport class FileNotFoundError extends LnaiError {\n public readonly filePath: string;\n\n constructor(message: string, filePath: string) {\n super(message, \"FILE_NOT_FOUND\");\n this.name = \"FileNotFoundError\";\n this.filePath = filePath;\n }\n}\n\nexport class WriteError extends LnaiError {\n public readonly filePath: string;\n\n constructor(message: string, filePath: string, cause?: Error) {\n super(message, \"WRITE_ERROR\");\n this.name = \"WriteError\";\n this.filePath = filePath;\n if (cause) {\n this.cause = cause;\n }\n }\n}\n\nexport class PluginError extends LnaiError {\n public readonly pluginId: string;\n\n constructor(message: string, pluginId: string, cause?: Error) {\n super(message, \"PLUGIN_ERROR\");\n this.name = \"PluginError\";\n this.pluginId = pluginId;\n if (cause) {\n this.cause = cause;\n }\n }\n}\n","import { z } from \"zod\";\n\n/** MCP Server configuration (Claude format as source of truth) */\nexport const mcpServerSchema = z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n type: z.enum([\"http\", \"sse\"]).optional(),\n url: z.string().optional(),\n headers: z.record(z.string(), z.string()).optional(),\n});\n\nexport const permissionsSchema = z.object({\n allow: z.array(z.string()).optional(),\n ask: z.array(z.string()).optional(),\n deny: z.array(z.string()).optional(),\n});\n\nexport const toolConfigSchema = z.object({\n enabled: z.boolean(),\n versionControl: z.boolean().optional().default(false),\n});\n\nexport const toolIdSchema = z.enum([\"claudeCode\", \"opencode\"]);\n\n/** Settings configuration (Claude format as source of truth) */\nexport const settingsSchema = z.object({\n permissions: permissionsSchema.optional(),\n mcpServers: z.record(z.string(), mcpServerSchema).optional(),\n overrides: z\n .object({\n claudeCode: z.record(z.string(), z.unknown()).optional(),\n opencode: z.record(z.string(), z.unknown()).optional(),\n })\n .optional(),\n});\n\n/** Main config.json structure. Uses partial object to allow partial tool configs. */\nexport const configSchema = z.object({\n tools: z\n .object({\n claudeCode: toolConfigSchema,\n opencode: toolConfigSchema,\n })\n .partial()\n .optional(),\n});\n\n/** Skill frontmatter (name and description required) */\nexport const skillFrontmatterSchema = z.object({\n name: z.string(),\n description: z.string(),\n});\n\n/** Rule frontmatter (paths required) */\nexport const ruleFrontmatterSchema = z.object({\n paths: z.array(z.string()).min(1),\n});\n\nexport type McpServer = z.infer<typeof mcpServerSchema>;\nexport type Permissions = z.infer<typeof permissionsSchema>;\nexport type ToolConfig = z.infer<typeof toolConfigSchema>;\nexport type Settings = z.infer<typeof settingsSchema>;\nexport type Config = z.infer<typeof configSchema>;\nexport type SkillFrontmatter = z.infer<typeof skillFrontmatterSchema>;\nexport type RuleFrontmatter = z.infer<typeof ruleFrontmatterSchema>;\n","import matter from \"gray-matter\";\n\nexport function parseFrontmatter(content: string): {\n frontmatter: Record<string, unknown>;\n content: string;\n} {\n const parsed = matter(content);\n return {\n frontmatter: parsed.data as Record<string, unknown>,\n content: parsed.content.trim(),\n };\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { CONFIG_DIRS, CONFIG_FILES, UNIFIED_DIR } from \"../constants\";\nimport { FileNotFoundError, ParseError } from \"../errors\";\nimport type {\n MarkdownFile,\n RuleFrontmatter,\n SkillFrontmatter,\n UnifiedState,\n} from \"../types/index\";\nimport { parseFrontmatter } from \"./frontmatter\";\n\nexport { parseFrontmatter } from \"./frontmatter\";\n\n/**\n * Parse the unified .ai/ configuration directory.\n * Reads config.json, settings.json, AGENTS.md, rules, and skills.\n */\nexport async function parseUnifiedConfig(\n rootDir: string\n): Promise<UnifiedState> {\n const aiDir = path.join(rootDir, UNIFIED_DIR);\n\n if (!(await fileExists(aiDir))) {\n throw new FileNotFoundError(\n `Unified config directory not found: ${aiDir}`,\n aiDir\n );\n }\n\n const configPath = path.join(aiDir, CONFIG_FILES.config);\n let config: UnifiedState[\"config\"];\n try {\n config = await readJsonFile<UnifiedState[\"config\"]>(configPath);\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n config = { tools: {} };\n } else {\n throw error;\n }\n }\n\n const settingsPath = path.join(aiDir, CONFIG_FILES.settings);\n let settings: UnifiedState[\"settings\"] = null;\n if (await fileExists(settingsPath)) {\n settings = await readJsonFile<UnifiedState[\"settings\"]>(settingsPath);\n }\n\n const agentsPath = path.join(aiDir, CONFIG_FILES.agents);\n let agents: string | null = null;\n if (await fileExists(agentsPath)) {\n agents = await readMarkdownFile(agentsPath);\n }\n\n const rulesDir = path.join(aiDir, CONFIG_DIRS.rules);\n const rules = await readMarkdownDirectory<RuleFrontmatter>(rulesDir);\n\n const skillsDir = path.join(aiDir, CONFIG_DIRS.skills);\n const skills = await readSkillsDirectory(skillsDir);\n\n return {\n config,\n settings,\n agents,\n rules,\n skills,\n };\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readJsonFile<T>(filePath: string): Promise<T> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch (error) {\n if ((error as { code?: string }).code === \"ENOENT\") {\n throw new FileNotFoundError(`File not found: ${filePath}`, filePath);\n }\n throw new ParseError(\n `Failed to parse JSON: ${filePath}`,\n filePath,\n error as Error\n );\n }\n}\n\nasync function readMarkdownFile(filePath: string): Promise<string> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch (error) {\n if ((error as { code?: string }).code === \"ENOENT\") {\n throw new FileNotFoundError(`File not found: ${filePath}`, filePath);\n }\n throw new ParseError(\n `Failed to read markdown: ${filePath}`,\n filePath,\n error as Error\n );\n }\n}\n\nasync function readMarkdownDirectory<T>(\n dirPath: string\n): Promise<MarkdownFile<T>[]> {\n const files: MarkdownFile<T>[] = [];\n\n if (!(await fileExists(dirPath))) {\n return files;\n }\n\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile() && entry.name.endsWith(\".md\")) {\n const filePath = path.join(dirPath, entry.name);\n const content = await readMarkdownFile(filePath);\n const parsed = parseFrontmatter(content);\n\n files.push({\n path: entry.name,\n frontmatter: parsed.frontmatter as T,\n content: parsed.content,\n });\n }\n }\n\n return files;\n}\n\nasync function readSkillsDirectory(\n dirPath: string\n): Promise<MarkdownFile<SkillFrontmatter>[]> {\n const skills: MarkdownFile<SkillFrontmatter>[] = [];\n\n if (!(await fileExists(dirPath))) {\n return skills;\n }\n\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const skillFile = path.join(dirPath, entry.name, \"SKILL.md\");\n if (await fileExists(skillFile)) {\n const content = await readMarkdownFile(skillFile);\n const parsed = parseFrontmatter(content);\n\n skills.push({\n path: entry.name,\n frontmatter: parsed.frontmatter as SkillFrontmatter,\n content: parsed.content,\n });\n }\n }\n }\n\n return skills;\n}\n","import type { z } from \"zod\";\n\nimport {\n configSchema,\n ruleFrontmatterSchema,\n settingsSchema,\n skillFrontmatterSchema,\n} from \"../schemas/index\";\nimport type {\n UnifiedState,\n ValidationErrorDetail,\n ValidationResult,\n} from \"../types/index\";\n\n/**\n * Convert Zod issues to ValidationErrorDetail array\n */\nfunction zodIssuesToErrors(\n issues: z.core.$ZodIssue[],\n prefix: string[] = []\n): ValidationErrorDetail[] {\n return issues.map((issue) => ({\n path: [...prefix, ...issue.path.map(String)],\n message: issue.message,\n value: undefined,\n }));\n}\n\n/**\n * Validate config.json structure using Zod\n */\nexport function validateConfig(config: unknown): ValidationResult {\n const result = configSchema.safeParse(config);\n\n if (result.success) {\n return {\n valid: true,\n errors: [],\n warnings: [],\n skipped: [],\n };\n }\n\n return {\n valid: false,\n errors: zodIssuesToErrors(result.error.issues, [\"config\"]),\n warnings: [],\n skipped: [],\n };\n}\n\n/**\n * Validate settings.json structure using Zod\n */\nexport function validateSettings(settings: unknown): ValidationResult {\n if (settings === null || settings === undefined) {\n return {\n valid: true,\n errors: [],\n warnings: [],\n skipped: [],\n };\n }\n\n const result = settingsSchema.safeParse(settings);\n\n if (result.success) {\n return {\n valid: true,\n errors: [],\n warnings: [],\n skipped: [],\n };\n }\n\n return {\n valid: false,\n errors: zodIssuesToErrors(result.error.issues, [\"settings\"]),\n warnings: [],\n skipped: [],\n };\n}\n\n/**\n * Validate skill frontmatter\n */\nfunction validateSkillFrontmatter(\n frontmatter: unknown,\n skillPath: string\n): ValidationErrorDetail[] {\n const result = skillFrontmatterSchema.safeParse(frontmatter);\n\n if (result.success) {\n return [];\n }\n\n return zodIssuesToErrors(result.error.issues, [\n \"skills\",\n skillPath,\n \"frontmatter\",\n ]);\n}\n\n/**\n * Validate rule frontmatter\n */\nfunction validateRuleFrontmatter(\n frontmatter: unknown,\n rulePath: string\n): ValidationErrorDetail[] {\n const result = ruleFrontmatterSchema.safeParse(frontmatter);\n\n if (result.success) {\n return [];\n }\n\n return zodIssuesToErrors(result.error.issues, [\n \"rules\",\n rulePath,\n \"frontmatter\",\n ]);\n}\n\n/**\n * Validate the unified configuration state\n * @param state - Parsed unified state\n * @returns Validation result\n */\nexport function validateUnifiedState(state: UnifiedState): ValidationResult {\n const errors: ValidationErrorDetail[] = [];\n const warnings: ValidationErrorDetail[] = [];\n\n // 1. Validate config.json\n const configResult = validateConfig(state.config);\n errors.push(...configResult.errors);\n\n // 2. Validate settings.json\n const settingsResult = validateSettings(state.settings);\n errors.push(...settingsResult.errors);\n\n // 3. Validate each skill's frontmatter\n for (const skill of state.skills) {\n const skillErrors = validateSkillFrontmatter(skill.frontmatter, skill.path);\n errors.push(...skillErrors);\n }\n\n // 4. Validate each rule's frontmatter\n for (const rule of state.rules) {\n const ruleErrors = validateRuleFrontmatter(rule.frontmatter, rule.path);\n errors.push(...ruleErrors);\n }\n\n // 5. Add warnings for missing optional files\n if (!state.agents) {\n warnings.push({\n path: [\"AGENTS.md\"],\n message: \"AGENTS.md not found - no main instructions will be exported\",\n });\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n skipped: [],\n };\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport deepmerge from \"deepmerge\";\n\nimport type { ToolId } from \"../constants\";\nimport { OVERRIDE_DIRS, UNIFIED_DIR } from \"../constants\";\n\n/**\n * Information about a file in the override directory\n */\nexport interface OverrideFile {\n /** Relative path from the override directory (e.g., \"commands/custom.md\") */\n relativePath: string;\n /** Absolute path to the file */\n absolutePath: string;\n}\n\n/**\n * Recursively scan an override directory for all files.\n * Returns empty array if the directory doesn't exist.\n */\nexport async function scanOverrideDirectory(\n rootDir: string,\n toolId: ToolId\n): Promise<OverrideFile[]> {\n const overrideDir = path.join(rootDir, UNIFIED_DIR, OVERRIDE_DIRS[toolId]);\n\n try {\n await fs.access(overrideDir);\n } catch {\n return [];\n }\n\n const files: OverrideFile[] = [];\n await scanDir(overrideDir, overrideDir, files);\n return files;\n}\n\nasync function scanDir(\n baseDir: string,\n currentDir: string,\n files: OverrideFile[]\n): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const absolutePath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n await scanDir(baseDir, absolutePath, files);\n } else if (entry.isFile()) {\n const relativePath = path.relative(baseDir, absolutePath);\n files.push({ relativePath, absolutePath });\n }\n }\n}\n\nexport async function parseJsonFile(\n filePath: string\n): Promise<Record<string, unknown>> {\n const content = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(content) as Record<string, unknown>;\n}\n\n/**\n * Deep merge two config objects. Arrays are concatenated and deduplicated.\n */\nexport function deepMergeConfigs<T extends Record<string, unknown>>(\n base: T,\n override: Record<string, unknown>\n): T {\n return deepmerge(base, override, {\n arrayMerge: (target, source) => [...new Set([...target, ...source])],\n }) as T;\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","import * as path from \"node:path\";\n\nimport { OVERRIDE_DIRS, TOOL_OUTPUT_DIRS, UNIFIED_DIR } from \"../../constants\";\nimport type {\n OutputFile,\n UnifiedState,\n ValidationResult,\n} from \"../../types/index\";\nimport {\n deepMergeConfigs,\n fileExists,\n scanOverrideDirectory,\n} from \"../../utils/overrides\";\nimport type { Plugin } from \"../types\";\n\n/**\n * Claude Code plugin for exporting to .claude/ format\n *\n * Output structure:\n * - .claude/CLAUDE.md (symlink -> ../.ai/AGENTS.md)\n * - .claude/rules/ (symlink -> ../.ai/rules)\n * - .claude/skills/<name>/ (symlink -> ../../.ai/skills/<name>)\n * - .claude/settings.json (generated settings merged with .ai/.claude/settings.json)\n * - .claude/<path> (symlink -> ../.ai/.claude/<path>) for other override files\n */\nexport const claudeCodePlugin: Plugin = {\n id: \"claudeCode\",\n name: \"Claude Code\",\n\n async detect(_rootDir: string): Promise<boolean> {\n // TODO: Implement in v0.2\n return false;\n },\n\n async import(_rootDir: string): Promise<Partial<UnifiedState> | null> {\n // TODO: Implement in v0.2\n return null;\n },\n\n async export(state: UnifiedState, rootDir: string): Promise<OutputFile[]> {\n const files: OutputFile[] = [];\n const outputDir = TOOL_OUTPUT_DIRS.claudeCode;\n\n if (state.agents) {\n files.push({\n path: `${outputDir}/CLAUDE.md`,\n type: \"symlink\",\n target: `../${UNIFIED_DIR}/AGENTS.md`,\n });\n }\n\n if (state.rules.length > 0) {\n files.push({\n path: `${outputDir}/rules`,\n type: \"symlink\",\n target: `../${UNIFIED_DIR}/rules`,\n });\n }\n\n for (const skill of state.skills) {\n files.push({\n path: `${outputDir}/skills/${skill.path}`,\n type: \"symlink\",\n target: `../../${UNIFIED_DIR}/skills/${skill.path}`,\n });\n }\n\n const baseSettings: Record<string, unknown> = {};\n if (state.settings?.permissions) {\n baseSettings[\"permissions\"] = state.settings.permissions;\n }\n if (state.settings?.mcpServers) {\n baseSettings[\"mcpServers\"] = state.settings.mcpServers;\n }\n\n let finalSettings = baseSettings;\n if (state.settings?.overrides?.claudeCode) {\n finalSettings = deepMergeConfigs(\n baseSettings,\n state.settings.overrides.claudeCode\n );\n }\n\n if (Object.keys(finalSettings).length > 0) {\n files.push({\n path: `${outputDir}/settings.json`,\n type: \"json\",\n content: finalSettings,\n });\n }\n\n const overrideFiles = await scanOverrideDirectory(rootDir, \"claudeCode\");\n for (const overrideFile of overrideFiles) {\n const targetPath = path.join(\n rootDir,\n outputDir,\n overrideFile.relativePath\n );\n if (await fileExists(targetPath)) {\n continue;\n }\n files.push({\n path: `${outputDir}/${overrideFile.relativePath}`,\n type: \"symlink\",\n target: `../${UNIFIED_DIR}/${OVERRIDE_DIRS.claudeCode}/${overrideFile.relativePath}`,\n });\n }\n\n return files;\n },\n\n validate(state: UnifiedState): ValidationResult {\n const warnings: { path: string[]; message: string }[] = [];\n if (!state.agents) {\n warnings.push({\n path: [\"AGENTS.md\"],\n message: \"No AGENTS.md found - .claude/CLAUDE.md will not be created\",\n });\n }\n return { valid: true, errors: [], warnings, skipped: [] };\n },\n};\n","interface OpenCodeMcpServer {\n type: \"local\" | \"remote\";\n command?: string[];\n url?: string;\n environment?: Record<string, string>;\n headers?: Record<string, string>;\n}\n\ntype OpenCodePermission = Record<\n string,\n Record<string, \"allow\" | \"ask\" | \"deny\">\n>;\n\ninterface ClaudeMcpServer {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n type?: \"http\" | \"sse\";\n url?: string;\n headers?: Record<string, string>;\n}\n\n/**\n * Transform MCP servers from Claude format to OpenCode format.\n *\n * Claude: { command: \"npx\", args: [\"-y\", \"@example/db\"], env: { \"DB_URL\": \"${DB_URL}\" } }\n * OpenCode: { type: \"local\", command: [\"npx\", \"-y\", \"@example/db\"], environment: { \"DB_URL\": \"{env:DB_URL}\" } }\n */\nexport function transformMcpToOpenCode(\n servers: Record<string, unknown> | undefined\n): Record<string, OpenCodeMcpServer> | undefined {\n if (!servers || Object.keys(servers).length === 0) {\n return undefined;\n }\n\n const result: Record<string, OpenCodeMcpServer> = {};\n\n for (const [name, serverRaw] of Object.entries(servers)) {\n const server = serverRaw as ClaudeMcpServer;\n\n if (server.type === \"http\" || server.type === \"sse\") {\n const openCodeServer: OpenCodeMcpServer = {\n type: \"remote\",\n url: server.url,\n };\n if (server.headers) {\n openCodeServer.headers = server.headers;\n }\n result[name] = openCodeServer;\n } else if (server.command) {\n const command = [server.command, ...(server.args || [])];\n const openCodeServer: OpenCodeMcpServer = {\n type: \"local\",\n command,\n };\n if (server.env) {\n openCodeServer.environment = transformEnvVars(server.env);\n }\n result[name] = openCodeServer;\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/**\n * Transform permissions from Claude format to OpenCode format.\n *\n * Claude: { allow: [\"Bash(git:*)\"], ask: [\"Bash(npm:*)\"], deny: [\"Read(.env)\"] }\n * OpenCode: { \"bash\": { \"git *\": \"allow\", \"npm *\": \"ask\" }, \"read\": { \".env\": \"deny\" } }\n */\nexport function transformPermissionsToOpenCode(\n permissions: { allow?: string[]; ask?: string[]; deny?: string[] } | undefined\n): OpenCodePermission | undefined {\n if (!permissions) {\n return undefined;\n }\n\n const result: OpenCodePermission = {};\n\n const processRules = (\n rules: string[] | undefined,\n level: \"allow\" | \"ask\" | \"deny\"\n ) => {\n if (!rules) {\n return;\n }\n\n for (const rule of rules) {\n const parsed = parsePermissionRule(rule);\n if (!parsed) {\n continue;\n }\n\n const { tool, pattern } = parsed;\n if (!result[tool]) {\n result[tool] = {};\n }\n\n result[tool]![pattern] = level;\n }\n };\n\n // Process in priority order: allow first, then ask, then deny (highest priority overwrites)\n processRules(permissions.allow, \"allow\");\n processRules(permissions.ask, \"ask\");\n processRules(permissions.deny, \"deny\");\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n// --- Helper functions ---\n\n/** Transform ${VAR} or ${VAR:-default} to {env:VAR} */\nfunction transformEnvVar(value: string): string {\n return value.replace(/\\$\\{([^}:]+)(:-[^}]*)?\\}/g, \"{env:$1}\");\n}\n\nfunction transformEnvVars(env: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n result[key] = transformEnvVar(value);\n }\n return result;\n}\n\n/** Parse \"Tool(pattern)\" format, returns { tool, pattern } or null */\nfunction parsePermissionRule(\n rule: string\n): { tool: string; pattern: string } | null {\n const match = rule.match(/^(\\w+)\\(([^)]+)\\)$/);\n if (!match) {\n return null;\n }\n\n const tool = match[1];\n const pattern = match[2];\n\n if (!tool || !pattern) {\n return null;\n }\n\n const normalizedTool = tool.toLowerCase();\n\n // Convert `:*` to ` *` (word boundary)\n let normalizedPattern = pattern;\n if (normalizedPattern.includes(\":*\")) {\n normalizedPattern = normalizedPattern.replace(/:(\\*)/g, \" $1\");\n }\n\n return {\n tool: normalizedTool,\n pattern: normalizedPattern,\n };\n}\n","import * as path from \"node:path\";\n\nimport { OVERRIDE_DIRS, TOOL_OUTPUT_DIRS, UNIFIED_DIR } from \"../../constants\";\nimport type {\n OutputFile,\n UnifiedState,\n ValidationResult,\n} from \"../../types/index\";\nimport {\n deepMergeConfigs,\n fileExists,\n scanOverrideDirectory,\n} from \"../../utils/overrides\";\nimport type { Plugin } from \"../types\";\nimport {\n transformMcpToOpenCode,\n transformPermissionsToOpenCode,\n} from \"./transforms\";\n\n/**\n * OpenCode plugin for exporting to opencode.json format\n *\n * Output structure:\n * - .opencode/AGENTS.md (symlink -> ../.ai/AGENTS.md)\n * - .opencode/rules/ (symlink -> ../.ai/rules)\n * - .opencode/skills/<name>/ (symlink -> ../../.ai/skills/<name>)\n * - opencode.json (generated config merged with .ai/.opencode/opencode.json)\n * - .opencode/<path> (symlink -> ../.ai/.opencode/<path>) for other override files\n */\nexport const opencodePlugin: Plugin = {\n id: \"opencode\",\n name: \"OpenCode\",\n\n async detect(_rootDir: string): Promise<boolean> {\n // TODO: Implement in v0.2\n return false;\n },\n\n async import(_rootDir: string): Promise<Partial<UnifiedState> | null> {\n // TODO: Implement in v0.2\n return null;\n },\n\n async export(state: UnifiedState, rootDir: string): Promise<OutputFile[]> {\n const files: OutputFile[] = [];\n const outputDir = TOOL_OUTPUT_DIRS.opencode;\n\n if (state.agents) {\n files.push({\n path: `${outputDir}/AGENTS.md`,\n type: \"symlink\",\n target: `../${UNIFIED_DIR}/AGENTS.md`,\n });\n }\n\n if (state.rules.length > 0) {\n files.push({\n path: `${outputDir}/rules`,\n type: \"symlink\",\n target: `../${UNIFIED_DIR}/rules`,\n });\n }\n\n for (const skill of state.skills) {\n files.push({\n path: `${outputDir}/skills/${skill.path}`,\n type: \"symlink\",\n target: `../../${UNIFIED_DIR}/skills/${skill.path}`,\n });\n }\n\n const baseConfig: Record<string, unknown> = {\n $schema: \"https://opencode.ai/config.json\",\n };\n if (state.rules.length > 0) {\n baseConfig[\"instructions\"] = [`${outputDir}/rules/*.md`];\n }\n\n const mcp = transformMcpToOpenCode(state.settings?.mcpServers);\n if (mcp) {\n baseConfig[\"mcp\"] = mcp;\n }\n\n const permission = transformPermissionsToOpenCode(\n state.settings?.permissions\n );\n if (permission) {\n baseConfig[\"permission\"] = permission;\n }\n\n let finalConfig = baseConfig;\n if (state.settings?.overrides?.opencode) {\n finalConfig = deepMergeConfigs(\n baseConfig,\n state.settings.overrides.opencode\n );\n }\n\n files.push({\n path: \"opencode.json\",\n type: \"json\",\n content: finalConfig,\n });\n\n const overrideFiles = await scanOverrideDirectory(rootDir, \"opencode\");\n for (const overrideFile of overrideFiles) {\n const targetPath = path.join(\n rootDir,\n outputDir,\n overrideFile.relativePath\n );\n if (await fileExists(targetPath)) {\n continue;\n }\n files.push({\n path: `${outputDir}/${overrideFile.relativePath}`,\n type: \"symlink\",\n target: `../${UNIFIED_DIR}/${OVERRIDE_DIRS.opencode}/${overrideFile.relativePath}`,\n });\n }\n\n return files;\n },\n\n validate(state: UnifiedState): ValidationResult {\n const warnings: { path: string[]; message: string }[] = [];\n if (!state.agents) {\n warnings.push({\n path: [\"AGENTS.md\"],\n message: \"No AGENTS.md found - .opencode/AGENTS.md will not be created\",\n });\n }\n return { valid: true, errors: [], warnings, skipped: [] };\n },\n};\n","import type { ToolId } from \"../constants\";\nimport type { Plugin } from \"./types\";\n\nclass PluginRegistry {\n private plugins: Map<ToolId, Plugin> = new Map();\n\n register(plugin: Plugin): void {\n this.plugins.set(plugin.id, plugin);\n }\n\n get(id: ToolId): Plugin | undefined {\n return this.plugins.get(id);\n }\n\n getAll(): Plugin[] {\n return Array.from(this.plugins.values());\n }\n\n getIds(): ToolId[] {\n return Array.from(this.plugins.keys());\n }\n\n has(id: ToolId): boolean {\n return this.plugins.has(id);\n }\n}\n\nexport const pluginRegistry = new PluginRegistry();\n","import { claudeCodePlugin } from \"./claude-code/index\";\nimport { opencodePlugin } from \"./opencode/index\";\nimport { pluginRegistry } from \"./registry\";\n\nexport { claudeCodePlugin, opencodePlugin, pluginRegistry };\nexport type { Plugin } from \"./types\";\n\npluginRegistry.register(claudeCodePlugin);\npluginRegistry.register(opencodePlugin);\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport { WriteError } from \"../errors\";\nimport type { ChangeResult, OutputFile } from \"../types/index\";\n\n/**\n * Options for the file writer\n */\nexport interface WriterOptions {\n /** Root directory for output */\n rootDir: string;\n /** Preview changes without writing */\n dryRun?: boolean;\n}\n\nexport function computeHash(content: string): string {\n return crypto.createHash(\"sha256\").update(content, \"utf-8\").digest(\"hex\");\n}\n\nasync function readExistingFile(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nasync function getSymlinkTarget(filePath: string): Promise<string | null> {\n try {\n const stats = await fs.lstat(filePath);\n if (stats.isSymbolicLink()) {\n return await fs.readlink(filePath);\n }\n return null;\n } catch {\n return null;\n }\n}\n\nasync function ensureDir(dirPath: string): Promise<void> {\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nasync function removeIfExists(filePath: string): Promise<void> {\n try {\n const stats = await fs.lstat(filePath);\n if (stats.isDirectory() && !stats.isSymbolicLink()) {\n await fs.rm(filePath, { recursive: true, force: true });\n } else {\n await fs.unlink(filePath);\n }\n } catch (error) {\n if ((error as { code?: string }).code !== \"ENOENT\") {\n throw error;\n }\n }\n}\n\nasync function writeSingleFile(\n file: OutputFile,\n rootDir: string,\n dryRun: boolean\n): Promise<ChangeResult> {\n const fullPath = path.join(rootDir, file.path);\n const dirPath = path.dirname(fullPath);\n\n if (file.type === \"symlink\") {\n const target = file.target!;\n const existingTarget = await getSymlinkTarget(fullPath);\n\n if (existingTarget === target) {\n return {\n path: file.path,\n action: \"unchanged\",\n };\n }\n\n if (!dryRun) {\n await ensureDir(dirPath);\n await removeIfExists(fullPath);\n await fs.symlink(target, fullPath);\n }\n\n return {\n path: file.path,\n action: existingTarget ? \"update\" : \"create\",\n };\n }\n\n const content =\n file.type === \"json\"\n ? JSON.stringify(file.content, null, 2) + \"\\n\"\n : String(file.content);\n\n const newHash = computeHash(content);\n const existingContent = await readExistingFile(fullPath);\n const oldHash = existingContent ? computeHash(existingContent) : undefined;\n\n if (oldHash === newHash) {\n return {\n path: file.path,\n action: \"unchanged\",\n oldHash,\n newHash,\n };\n }\n\n if (!dryRun) {\n await ensureDir(dirPath);\n await fs.writeFile(fullPath, content, \"utf-8\");\n }\n\n return {\n path: file.path,\n action: existingContent ? \"update\" : \"create\",\n oldHash,\n newHash,\n };\n}\n\nexport async function writeFiles(\n files: OutputFile[],\n options: WriterOptions\n): Promise<ChangeResult[]> {\n const { rootDir, dryRun = false } = options;\n const results: ChangeResult[] = [];\n\n for (const file of files) {\n try {\n const result = await writeSingleFile(file, rootDir, dryRun);\n results.push(result);\n } catch (error) {\n throw new WriteError(\n `Failed to write file: ${file.path}`,\n file.path,\n error as Error\n );\n }\n }\n\n return results;\n}\n\n/**\n * Update .gitignore with paths that should not be version controlled.\n * Manages a dedicated \"lnai-generated\" section to avoid conflicts with user entries.\n */\nexport async function updateGitignore(\n rootDir: string,\n paths: string[]\n): Promise<void> {\n const gitignorePath = path.join(rootDir, \".gitignore\");\n let content = \"\";\n\n try {\n content = await fs.readFile(gitignorePath, \"utf-8\");\n } catch {\n // File doesn't exist, start fresh\n }\n\n const marker = \"# lnai-generated\";\n const endMarker = \"# end lnai-generated\";\n\n const markerRegex = new RegExp(`${marker}[\\\\s\\\\S]*?${endMarker}\\\\n?`, \"g\");\n content = content.replace(markerRegex, \"\");\n content = content.trimEnd();\n\n const newSection = [\"\", marker, ...paths.map((p) => p), endMarker, \"\"].join(\n \"\\n\"\n );\n\n content = content + newSection;\n\n await fs.writeFile(gitignorePath, content, \"utf-8\");\n}\n","import type { ToolId } from \"../constants\";\nimport { parseUnifiedConfig } from \"../parser/index\";\nimport { pluginRegistry } from \"../plugins/index\";\nimport type { SyncResult } from \"../types/index\";\nimport { validateUnifiedState } from \"../validator/index\";\nimport { updateGitignore, writeFiles } from \"../writer/index\";\n\n/**\n * Options for the sync pipeline\n */\nexport interface SyncOptions {\n /** Root directory containing .ai/ config */\n rootDir: string;\n /** Only sync specific tools (default: all enabled) */\n tools?: ToolId[];\n /** Preview changes without writing files */\n dryRun?: boolean;\n /** Enable verbose output */\n verbose?: boolean;\n}\n\nfunction getToolsToSync(\n config: {\n tools?: Partial<\n Record<ToolId, { enabled: boolean; versionControl?: boolean }>\n >;\n },\n requestedTools?: ToolId[]\n): ToolId[] {\n if (requestedTools && requestedTools.length > 0) {\n return requestedTools.filter((tool) => pluginRegistry.has(tool));\n }\n\n const enabledTools: ToolId[] = [];\n\n if (config.tools) {\n for (const [toolId, toolConfig] of Object.entries(config.tools)) {\n if (toolConfig?.enabled && pluginRegistry.has(toolId as ToolId)) {\n enabledTools.push(toolId as ToolId);\n }\n }\n }\n\n if (enabledTools.length === 0) {\n return pluginRegistry.getIds();\n }\n\n return enabledTools;\n}\n\n/**\n * Run the sync pipeline to export .ai/ config to native tool formats.\n */\nexport async function runSyncPipeline(\n options: SyncOptions\n): Promise<SyncResult[]> {\n const { rootDir, dryRun = false, tools: requestedTools } = options;\n\n const state = await parseUnifiedConfig(rootDir);\n\n const unifiedValidation = validateUnifiedState(state);\n if (!unifiedValidation.valid) {\n return [\n {\n tool: \"claudeCode\" as ToolId,\n changes: [],\n validation: unifiedValidation,\n },\n ];\n }\n\n const toolsToSync = getToolsToSync(state.config, requestedTools);\n\n if (toolsToSync.length === 0) {\n return [];\n }\n\n const results: SyncResult[] = [];\n const pathsToIgnore: string[] = [];\n\n for (const toolId of toolsToSync) {\n const plugin = pluginRegistry.get(toolId);\n if (!plugin) {\n continue;\n }\n\n const validation = plugin.validate(state);\n const outputFiles = await plugin.export(state, rootDir);\n const changes = await writeFiles(outputFiles, { rootDir, dryRun });\n\n results.push({\n tool: toolId,\n changes,\n validation,\n });\n\n const toolConfig = state.config.tools?.[toolId];\n if (!toolConfig?.versionControl) {\n pathsToIgnore.push(...outputFiles.map((f) => f.path));\n }\n }\n\n if (pathsToIgnore.length > 0 && !dryRun) {\n await updateGitignore(rootDir, pathsToIgnore);\n }\n\n return results;\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport {\n CONFIG_DIRS,\n CONFIG_FILES,\n TOOL_IDS,\n type ToolId,\n UNIFIED_DIR,\n} from \"../constants\";\nimport type { Config } from \"../types/index\";\n\nexport interface InitOptions {\n rootDir: string;\n tools?: ToolId[];\n minimal?: boolean;\n}\n\nexport interface InitResult {\n created: string[];\n}\n\nexport async function hasUnifiedConfig(rootDir: string): Promise<boolean> {\n const aiDir = path.join(rootDir, UNIFIED_DIR);\n try {\n const stats = await fs.stat(aiDir);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport function generateDefaultConfig(tools?: ToolId[]): Config {\n const enabledTools = tools ?? TOOL_IDS;\n\n const toolsConfig: Config[\"tools\"] = {};\n\n for (const toolId of TOOL_IDS) {\n toolsConfig[toolId] = {\n enabled: enabledTools.includes(toolId),\n versionControl: false,\n };\n }\n\n return {\n tools: toolsConfig,\n };\n}\n\nexport async function initUnifiedConfig(\n options: InitOptions\n): Promise<InitResult> {\n const { rootDir, tools, minimal = false } = options;\n const aiDir = path.join(rootDir, UNIFIED_DIR);\n const created: string[] = [];\n\n await fs.mkdir(aiDir, { recursive: true });\n created.push(UNIFIED_DIR);\n\n const config = generateDefaultConfig(tools);\n const configPath = path.join(aiDir, CONFIG_FILES.config);\n await fs.writeFile(\n configPath,\n JSON.stringify(config, null, 2) + \"\\n\",\n \"utf-8\"\n );\n created.push(path.join(UNIFIED_DIR, CONFIG_FILES.config));\n\n if (!minimal) {\n for (const dir of [CONFIG_DIRS.rules, CONFIG_DIRS.skills]) {\n const dirPath = path.join(aiDir, dir);\n await fs.mkdir(dirPath, { recursive: true });\n created.push(path.join(UNIFIED_DIR, dir));\n\n const gitkeepPath = path.join(dirPath, \".gitkeep\");\n await fs.writeFile(gitkeepPath, \"\", \"utf-8\");\n created.push(path.join(UNIFIED_DIR, dir, \".gitkeep\"));\n }\n }\n\n return { created };\n}\n"]}
|