lnai 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Krystian Jonca
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.js ADDED
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import * as fs from 'fs/promises';
4
+ import * as path from 'path';
5
+ import { hasUnifiedConfig, UNIFIED_DIR, initUnifiedConfig, runSyncPipeline, parseUnifiedConfig, validateUnifiedState, pluginRegistry } from '@lnai/core';
6
+ import chalk3 from 'chalk';
7
+ import ora from 'ora';
8
+
9
+ var initCommand = new Command("init").description("Initialize a new .ai/ configuration directory").option("--force", "Overwrite existing .ai/ directory").option("--minimal", "Create only config.json (no subdirectories)").option("-t, --tools <tools...>", "Enable only specific tools").action(async (options) => {
10
+ const rootDir = process.cwd();
11
+ const spinner = ora("Initializing .ai/ configuration...").start();
12
+ try {
13
+ const exists = await hasUnifiedConfig(rootDir);
14
+ if (exists && !options.force) {
15
+ spinner.fail(
16
+ `Directory ${UNIFIED_DIR}/ already exists. Use --force to overwrite.`
17
+ );
18
+ process.exit(1);
19
+ }
20
+ if (exists && options.force) {
21
+ await fs.rm(path.join(rootDir, UNIFIED_DIR), {
22
+ recursive: true,
23
+ force: true
24
+ });
25
+ }
26
+ const result = await initUnifiedConfig({
27
+ rootDir,
28
+ tools: options.tools,
29
+ minimal: options.minimal
30
+ });
31
+ spinner.succeed("Initialized .ai/ configuration");
32
+ console.log(chalk3.gray("\nCreated:"));
33
+ for (const file of result.created) {
34
+ console.log(chalk3.green(` + ${file}`));
35
+ }
36
+ console.log(
37
+ chalk3.gray("\nRun ") + chalk3.cyan("lnai sync") + chalk3.gray(" to generate tool configs.")
38
+ );
39
+ } catch (error) {
40
+ spinner.fail("Initialization failed");
41
+ console.error(
42
+ chalk3.red(error instanceof Error ? error.message : String(error))
43
+ );
44
+ process.exit(1);
45
+ }
46
+ });
47
+ var syncCommand = new Command("sync").description("Export .ai/ to native configs").option("--dry-run", "Preview without writing").option("-t, --tools <tools...>", "Filter to specific tools").option("-v, --verbose", "Detailed output").action(async (options) => {
48
+ const spinner = ora("Syncing configuration...").start();
49
+ try {
50
+ const results = await runSyncPipeline({
51
+ rootDir: process.cwd(),
52
+ dryRun: options.dryRun,
53
+ tools: options.tools,
54
+ verbose: options.verbose
55
+ });
56
+ spinner.succeed("Sync complete");
57
+ if (results.length === 0) {
58
+ console.log(chalk3.yellow("\nNo tools configured or enabled."));
59
+ return;
60
+ }
61
+ for (const result of results) {
62
+ console.log(chalk3.blue(`
63
+ ${result.tool}:`));
64
+ if (result.changes.length === 0) {
65
+ console.log(chalk3.gray(" No changes"));
66
+ }
67
+ for (const change of result.changes) {
68
+ const icon = change.action === "create" ? chalk3.green("+") : change.action === "update" ? chalk3.yellow("~") : change.action === "delete" ? chalk3.red("-") : chalk3.gray("=");
69
+ console.log(` ${icon} ${change.path}`);
70
+ }
71
+ }
72
+ } catch (error) {
73
+ spinner.fail("Sync failed");
74
+ console.error(
75
+ chalk3.red(error instanceof Error ? error.message : String(error))
76
+ );
77
+ process.exit(1);
78
+ }
79
+ });
80
+ var validateCommand = new Command("validate").description("Validate .ai/ configuration").option("-t, --tools <tools...>", "Filter to specific tools").action(async (options) => {
81
+ const spinner = ora("Validating configuration...").start();
82
+ try {
83
+ const rootDir = process.cwd();
84
+ const state = await parseUnifiedConfig(rootDir);
85
+ const unifiedResult = validateUnifiedState(state);
86
+ if (!unifiedResult.valid) {
87
+ spinner.fail("Validation failed");
88
+ console.log(chalk3.red("\nUnified config errors:"));
89
+ for (const error of unifiedResult.errors) {
90
+ console.log(
91
+ chalk3.red(` - ${error.path.join(".")}: ${error.message}`)
92
+ );
93
+ }
94
+ process.exit(1);
95
+ }
96
+ const tools = options.tools ?? pluginRegistry.getIds();
97
+ const toolErrors = [];
98
+ const toolWarnings = [];
99
+ const toolSkipped = [];
100
+ for (const toolId of tools) {
101
+ const plugin = pluginRegistry.get(toolId);
102
+ if (!plugin) {
103
+ continue;
104
+ }
105
+ const result = plugin.validate(state);
106
+ if (!result.valid) {
107
+ toolErrors.push({ plugin: plugin.name, errors: result.errors });
108
+ }
109
+ if (result.warnings.length > 0) {
110
+ toolWarnings.push({ plugin: plugin.name, warnings: result.warnings });
111
+ }
112
+ if (result.skipped.length > 0) {
113
+ toolSkipped.push({ plugin: plugin.name, skipped: result.skipped });
114
+ }
115
+ }
116
+ if (toolErrors.length > 0) {
117
+ spinner.fail("Validation failed");
118
+ for (const { plugin, errors } of toolErrors) {
119
+ console.log(chalk3.red(`
120
+ ${plugin} errors:`));
121
+ for (const error of errors) {
122
+ console.log(
123
+ chalk3.red(` - ${error.path.join(".")}: ${error.message}`)
124
+ );
125
+ }
126
+ }
127
+ process.exit(1);
128
+ }
129
+ spinner.succeed("Validation passed");
130
+ for (const { plugin, warnings } of toolWarnings) {
131
+ console.log(chalk3.yellow(`
132
+ ${plugin} warnings:`));
133
+ for (const warning of warnings) {
134
+ console.log(
135
+ chalk3.yellow(` - ${warning.path.join(".")}: ${warning.message}`)
136
+ );
137
+ }
138
+ }
139
+ for (const { plugin, skipped } of toolSkipped) {
140
+ console.log(chalk3.gray(`
141
+ ${plugin} skipped features:`));
142
+ for (const item of skipped) {
143
+ console.log(chalk3.gray(` - ${item.feature}: ${item.reason}`));
144
+ }
145
+ }
146
+ } catch (error) {
147
+ spinner.fail("Validation failed");
148
+ console.error(
149
+ chalk3.red(error instanceof Error ? error.message : String(error))
150
+ );
151
+ process.exit(1);
152
+ }
153
+ });
154
+
155
+ // src/index.ts
156
+ var program = new Command();
157
+ program.name("lnai").description(
158
+ "CLI tool that syncs a unified .ai/ config to native formats for AI coding tools"
159
+ ).version("0.1.0");
160
+ program.addCommand(initCommand);
161
+ program.addCommand(syncCommand);
162
+ program.addCommand(validateCommand);
163
+ program.parse();
164
+ //# sourceMappingURL=index.js.map
165
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts","../src/commands/sync.ts","../src/commands/validate.ts","../src/index.ts"],"names":["chalk","Command","ora"],"mappings":"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","file":"index.js","sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nimport {\n hasUnifiedConfig,\n initUnifiedConfig,\n type ToolId,\n UNIFIED_DIR,\n} from \"@lnai/core\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Initialize a new .ai/ configuration directory\")\n .option(\"--force\", \"Overwrite existing .ai/ directory\")\n .option(\"--minimal\", \"Create only config.json (no subdirectories)\")\n .option(\"-t, --tools <tools...>\", \"Enable only specific tools\")\n .action(async (options) => {\n const rootDir = process.cwd();\n const spinner = ora(\"Initializing .ai/ configuration...\").start();\n\n try {\n const exists = await hasUnifiedConfig(rootDir);\n\n if (exists && !options.force) {\n spinner.fail(\n `Directory ${UNIFIED_DIR}/ already exists. Use --force to overwrite.`\n );\n process.exit(1);\n }\n\n if (exists && options.force) {\n await fs.rm(path.join(rootDir, UNIFIED_DIR), {\n recursive: true,\n force: true,\n });\n }\n\n const result = await initUnifiedConfig({\n rootDir,\n tools: options.tools as ToolId[] | undefined,\n minimal: options.minimal,\n });\n\n spinner.succeed(\"Initialized .ai/ configuration\");\n\n console.log(chalk.gray(\"\\nCreated:\"));\n for (const file of result.created) {\n console.log(chalk.green(` + ${file}`));\n }\n\n console.log(\n chalk.gray(\"\\nRun \") +\n chalk.cyan(\"lnai sync\") +\n chalk.gray(\" to generate tool configs.\")\n );\n } catch (error) {\n spinner.fail(\"Initialization failed\");\n console.error(\n chalk.red(error instanceof Error ? error.message : String(error))\n );\n process.exit(1);\n }\n });\n","import { runSyncPipeline, type ToolId } from \"@lnai/core\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"Export .ai/ to native configs\")\n .option(\"--dry-run\", \"Preview without writing\")\n .option(\"-t, --tools <tools...>\", \"Filter to specific tools\")\n .option(\"-v, --verbose\", \"Detailed output\")\n .action(async (options) => {\n const spinner = ora(\"Syncing configuration...\").start();\n\n try {\n const results = await runSyncPipeline({\n rootDir: process.cwd(),\n dryRun: options.dryRun,\n tools: options.tools as ToolId[] | undefined,\n verbose: options.verbose,\n });\n\n spinner.succeed(\"Sync complete\");\n\n if (results.length === 0) {\n console.log(chalk.yellow(\"\\nNo tools configured or enabled.\"));\n return;\n }\n\n for (const result of results) {\n console.log(chalk.blue(`\\n${result.tool}:`));\n\n if (result.changes.length === 0) {\n console.log(chalk.gray(\" No changes\"));\n }\n\n for (const change of result.changes) {\n const icon =\n change.action === \"create\"\n ? chalk.green(\"+\")\n : change.action === \"update\"\n ? chalk.yellow(\"~\")\n : change.action === \"delete\"\n ? chalk.red(\"-\")\n : chalk.gray(\"=\");\n console.log(` ${icon} ${change.path}`);\n }\n }\n } catch (error) {\n spinner.fail(\"Sync failed\");\n console.error(\n chalk.red(error instanceof Error ? error.message : String(error))\n );\n process.exit(1);\n }\n });\n","import {\n parseUnifiedConfig,\n pluginRegistry,\n type ToolId,\n validateUnifiedState,\n} from \"@lnai/core\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\n\nexport const validateCommand = new Command(\"validate\")\n .description(\"Validate .ai/ configuration\")\n .option(\"-t, --tools <tools...>\", \"Filter to specific tools\")\n .action(async (options) => {\n const spinner = ora(\"Validating configuration...\").start();\n\n try {\n const rootDir = process.cwd();\n const state = await parseUnifiedConfig(rootDir);\n\n const unifiedResult = validateUnifiedState(state);\n\n if (!unifiedResult.valid) {\n spinner.fail(\"Validation failed\");\n console.log(chalk.red(\"\\nUnified config errors:\"));\n for (const error of unifiedResult.errors) {\n console.log(\n chalk.red(` - ${error.path.join(\".\")}: ${error.message}`)\n );\n }\n process.exit(1);\n }\n\n const tools =\n (options.tools as ToolId[] | undefined) ?? pluginRegistry.getIds();\n\n const toolErrors: Array<{\n plugin: string;\n errors: typeof unifiedResult.errors;\n }> = [];\n const toolWarnings: Array<{\n plugin: string;\n warnings: typeof unifiedResult.errors;\n }> = [];\n const toolSkipped: Array<{\n plugin: string;\n skipped: Array<{ feature: string; reason: string }>;\n }> = [];\n\n for (const toolId of tools) {\n const plugin = pluginRegistry.get(toolId);\n if (!plugin) {\n continue;\n }\n\n const result = plugin.validate(state);\n\n if (!result.valid) {\n toolErrors.push({ plugin: plugin.name, errors: result.errors });\n }\n\n if (result.warnings.length > 0) {\n toolWarnings.push({ plugin: plugin.name, warnings: result.warnings });\n }\n\n if (result.skipped.length > 0) {\n toolSkipped.push({ plugin: plugin.name, skipped: result.skipped });\n }\n }\n\n if (toolErrors.length > 0) {\n spinner.fail(\"Validation failed\");\n for (const { plugin, errors } of toolErrors) {\n console.log(chalk.red(`\\n${plugin} errors:`));\n for (const error of errors) {\n console.log(\n chalk.red(` - ${error.path.join(\".\")}: ${error.message}`)\n );\n }\n }\n process.exit(1);\n }\n\n spinner.succeed(\"Validation passed\");\n\n for (const { plugin, warnings } of toolWarnings) {\n console.log(chalk.yellow(`\\n${plugin} warnings:`));\n for (const warning of warnings) {\n console.log(\n chalk.yellow(` - ${warning.path.join(\".\")}: ${warning.message}`)\n );\n }\n }\n\n for (const { plugin, skipped } of toolSkipped) {\n console.log(chalk.gray(`\\n${plugin} skipped features:`));\n for (const item of skipped) {\n console.log(chalk.gray(` - ${item.feature}: ${item.reason}`));\n }\n }\n } catch (error) {\n spinner.fail(\"Validation failed\");\n console.error(\n chalk.red(error instanceof Error ? error.message : String(error))\n );\n process.exit(1);\n }\n });\n","import { Command } from \"commander\";\n\nimport { initCommand } from \"./commands/init.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { validateCommand } from \"./commands/validate.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"lnai\")\n .description(\n \"CLI tool that syncs a unified .ai/ config to native formats for AI coding tools\"\n )\n .version(\"0.1.0\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(syncCommand);\nprogram.addCommand(validateCommand);\n\nprogram.parse();\n"]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "lnai",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool that syncs a unified .ai/ config to native formats for AI coding tools",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Krystian Jonca",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/KrystianJonca/lnai.git",
11
+ "directory": "apps/cli"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/KrystianJonca/lnai/issues"
15
+ },
16
+ "homepage": "https://github.com/KrystianJonca/lnai#readme",
17
+ "keywords": [
18
+ "ai",
19
+ "config",
20
+ "configuration",
21
+ "claude",
22
+ "opencode",
23
+ "cli",
24
+ "ai-tools",
25
+ "lnai"
26
+ ],
27
+ "bin": {
28
+ "lnai": "./dist/index.js"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "dependencies": {
34
+ "chalk": "^5.6.2",
35
+ "commander": "^14.0.2",
36
+ "ora": "^9.1.0",
37
+ "@lnai/core": "0.1.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^25.0.10",
41
+ "eslint": "^9.39.2",
42
+ "tsup": "^8.5.1",
43
+ "typescript": "^5.9.3",
44
+ "vitest": "^4.0.18",
45
+ "@lnai/typescript-config": "0.1.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "dev": "tsup --watch",
50
+ "start": "node dist/index.js",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "lint": "eslint src",
54
+ "lint:fix": "eslint src --fix",
55
+ "typecheck": "tsc --noEmit",
56
+ "clean": "rm -rf dist .turbo"
57
+ }
58
+ }