@nexical/cli-core 0.1.0 → 0.1.1

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.
Files changed (49) hide show
  1. package/README.md +195 -0
  2. package/cli.ts +9 -0
  3. package/dist/chunk-2NZ4H42M.js +255 -0
  4. package/dist/chunk-2NZ4H42M.js.map +1 -0
  5. package/dist/chunk-5YGASH25.js +48 -0
  6. package/dist/chunk-5YGASH25.js.map +1 -0
  7. package/dist/chunk-6LSNSDTL.js +67 -0
  8. package/dist/chunk-6LSNSDTL.js.map +1 -0
  9. package/dist/chunk-C4IL52NB.js +2 -0
  10. package/dist/chunk-C4IL52NB.js.map +1 -0
  11. package/dist/chunk-HETFF3FQ.js +20 -0
  12. package/dist/chunk-HETFF3FQ.js.map +1 -0
  13. package/dist/chunk-PFEYDXIW.js +69 -0
  14. package/dist/chunk-PFEYDXIW.js.map +1 -0
  15. package/dist/cli.d.ts +1 -0
  16. package/dist/cli.js +15 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/index.d.ts +4 -0
  19. package/dist/index.js +16 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/src/BaseCommand.d.ts +21 -0
  22. package/dist/src/BaseCommand.js +10 -0
  23. package/dist/src/BaseCommand.js.map +1 -0
  24. package/dist/src/CLI.d.ts +23 -0
  25. package/dist/src/CLI.js +10 -0
  26. package/dist/src/CLI.js.map +1 -0
  27. package/dist/src/CommandInterface.d.ts +21 -0
  28. package/dist/src/CommandInterface.js +3 -0
  29. package/dist/src/CommandInterface.js.map +1 -0
  30. package/dist/src/CommandLoader.d.ts +20 -0
  31. package/dist/src/CommandLoader.js +9 -0
  32. package/dist/src/CommandLoader.js.map +1 -0
  33. package/dist/src/commands/help.d.ts +19 -0
  34. package/dist/src/commands/help.js +134 -0
  35. package/dist/src/commands/help.js.map +1 -0
  36. package/dist/src/utils/config.d.ts +7 -0
  37. package/dist/src/utils/config.js +13 -0
  38. package/dist/src/utils/config.js.map +1 -0
  39. package/dist/src/utils/logger.d.ts +6 -0
  40. package/dist/src/utils/logger.js +10 -0
  41. package/dist/src/utils/logger.js.map +1 -0
  42. package/dist/src/utils/shell.d.ts +3 -0
  43. package/dist/src/utils/shell.js +28 -0
  44. package/dist/src/utils/shell.js.map +1 -0
  45. package/index.ts +3 -9
  46. package/package.json +7 -2
  47. package/test/utils/integration-helpers.ts +1 -1
  48. package/tsconfig.json +2 -2
  49. package/tsup.config.ts +1 -1
package/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # @nexical/cli-core
2
+
3
+ The core framework for building powerful, extensible Command Line Interfaces (CLIs) within the Nexical ecosystem.
4
+
5
+ This package provides the foundational architecture for specialized CLI toolsets, including command discovery, execution orchestration, and a class-based command pattern. It is designed to be **agnostic**, allowing it to be used as the backbone for other CLI tools that need a similar structure and extensibility.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Features](#features)
10
+ - [Installation](#installation)
11
+ - [Usage](#usage)
12
+ - [Configuration](#configuration)
13
+ - [Directory Structure](#directory-structure)
14
+ - [Creating Commands](#creating-commands)
15
+ - [The BaseCommand](#the-basecommand)
16
+ - [Defining Arguments & Options](#defining-arguments--options)
17
+ - [Command Discovery Rules](#command-discovery-rules)
18
+ - [Architecture](#architecture)
19
+ - [License](#license)
20
+
21
+ ---
22
+
23
+ ## Features
24
+
25
+ * **Class-Based Architecture**: Build commands as TypeScript classes with inheritance and lifecycle methods.
26
+ * **Dynamic Discovery**: Automatically recursively finds and registers commands from specified directories.
27
+ * **Type-Safe Definitions**: Declarative definition of arguments and options.
28
+ * **Built-in Help**: Automatic generation of help text for commands and subcommands.
29
+ * **Configuration Support**: Aware of project-level configuration (e.g., `{command_name}.yml`).
30
+ * **Robust Error Handling**: Standardized error reporting and debug modes.
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ This package is typically used as a dependency within a specific CLI implementation (like `@astrical/cli`).
37
+
38
+ ```bash
39
+ npm install @nexical/cli-core
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Usage
45
+
46
+ To use the core framework, you need to instantiate the `CLI` class and start it. This is typically done in your CLI's entry point (e.g., `index.ts`).
47
+
48
+ ### Configuration
49
+
50
+ The `CLI` class accepts a `CLIConfig` object to customize behavior:
51
+
52
+ ```typescript
53
+ import { CLI } from '@nexical/cli-core';
54
+ import path from 'node:path';
55
+ import { fileURLToPath } from 'node:url';
56
+
57
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
58
+
59
+ const app = new CLI({
60
+ // 1. The name of your binary/command (displayed in help)
61
+ commandName: 'my-cli',
62
+
63
+ // 2. Directories to recursively search for command files
64
+ searchDirectories: [
65
+ path.resolve(__dirname, 'commands'),
66
+ // You can add multiple directories, e.g., for plugins
67
+ path.resolve(process.cwd(), 'plugins/commands')
68
+ ]
69
+ });
70
+
71
+ app.start();
72
+ ```
73
+
74
+ ### Directory Structure
75
+
76
+ A typical project using `@nexical/cli-core` looks like this:
77
+
78
+ ```
79
+ my-cli/
80
+ ├── package.json
81
+ ├── src/
82
+ │ ├── index.ts <-- Entry point (initializes CLI)
83
+ │ └── commands/ <-- Command files
84
+ │ ├── init.ts
85
+ │ ├── build.ts
86
+ │ └── module/ <-- Subcommands
87
+ │ ├── add.ts
88
+ │ └── list.ts
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Creating Commands
94
+
95
+ The core framework itself only includes a **Help** command. All functional commands must be implemented by consuming libraries.
96
+
97
+ ### The BaseCommand
98
+
99
+ All commands must extend the `BaseCommand` abstract class exported by the core.
100
+
101
+ ```typescript
102
+ // src/commands/greet.ts
103
+ import { BaseCommand } from '@nexical/cli-core';
104
+
105
+ export default class GreetCommand extends BaseCommand {
106
+ // Description shown in help
107
+ static description = 'Greets the user';
108
+
109
+ // Implement the run method
110
+ async run(options: any) {
111
+ this.log('Hello from my-cli!');
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### Defining Arguments & Options
117
+
118
+ You can define arguments and options using the static `args` property.
119
+
120
+ ```typescript
121
+ export default class GreetCommand extends BaseCommand {
122
+ static description = 'Greets the user with a custom message';
123
+
124
+ static args = {
125
+ // Positional arguments
126
+ args: [
127
+ {
128
+ name: 'name',
129
+ required: false,
130
+ description: 'Name to greet',
131
+ default: 'World'
132
+ }
133
+ ],
134
+ // Flags/Options
135
+ options: [
136
+ {
137
+ name: '--shout',
138
+ description: 'Print in uppercase',
139
+ default: false
140
+ },
141
+ {
142
+ name: '--count <n>',
143
+ description: 'Number of times to greet',
144
+ default: 1
145
+ }
146
+ ]
147
+ };
148
+
149
+ async run(options: any) {
150
+ // 'name' comes from args (mapped to options by name)
151
+ // 'shout' and 'count' come from options
152
+ const { name, shout, count } = options;
153
+
154
+ const message = `Hello, ${name}!`;
155
+ const finalMessage = shout ? message.toUpperCase() : message;
156
+
157
+ for (let i = 0; i < count; i++) {
158
+ this.log(finalMessage);
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### Command Discovery Rules
165
+
166
+ The `CommandLoader` uses the file structure to determine command names:
167
+
168
+ * **File Name = Command Name**:
169
+ * `commands/build.ts` -> `my-cli build`
170
+ * **Nested Directories = Subcommands**:
171
+ * `commands/user/create.ts` -> `my-cli user create`
172
+ * **Index Files = Parent Command**:
173
+ * `commands/user/index.ts` -> `my-cli user` (The handler for the root `user` command)
174
+
175
+ > **Note**: A file must default export a class extending `BaseCommand` to be registered.
176
+
177
+ ---
178
+
179
+ ## Architecture
180
+
181
+ The core is built around three main components:
182
+
183
+ 1. **`CLI`**: The main entry point. It wraps [CAC](https://github.com/cacjs/cac) to handle argument parsing and acts as the dependency injection container for commands.
184
+ 2. **`CommandLoader`**: Scans the filesystem for command files. It handles importing typescript files and validating that they export a valid command class.
185
+ 3. **`BaseCommand`**: Provides the interface for commands, including:
186
+ * `init()`: Async initialization hook (pre-run).
187
+ * `run()`: The main execution logic.
188
+ * `this.projectRoot`: Automatically resolved path to the project root (if running in a project context).
189
+ * Logging helpers (`this.success`, `this.warn`, `this.error`).
190
+
191
+ ---
192
+
193
+ ## License
194
+
195
+ Apache-2.0
package/cli.ts ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { CLI } from './src/CLI.js';
3
+
4
+ import { logger } from './src/utils/logger.js';
5
+
6
+ logger.debug('CLI ENTRY POINT HIT', process.argv);
7
+
8
+ const app = new CLI();
9
+ app.start();
@@ -0,0 +1,255 @@
1
+ import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
+ import {
3
+ CommandLoader
4
+ } from "./chunk-PFEYDXIW.js";
5
+ import {
6
+ logger,
7
+ setDebugMode
8
+ } from "./chunk-HETFF3FQ.js";
9
+
10
+ // src/CLI.ts
11
+ import { cac } from "cac";
12
+ import path from "path";
13
+ import fs from "fs";
14
+ import { fileURLToPath } from "url";
15
+ import pc from "picocolors";
16
+
17
+ // package.json
18
+ var package_default = {
19
+ name: "@nexical/cli-core",
20
+ version: "0.1.1",
21
+ type: "module",
22
+ main: "dist/index.js",
23
+ types: "dist/index.d.ts",
24
+ exports: {
25
+ ".": "./dist/index.js"
26
+ },
27
+ scripts: {
28
+ build: "tsup",
29
+ dev: "tsup --watch",
30
+ cli: "node dist/cli.js",
31
+ test: "npm run test:unit && npm run test:integration && npm run test:e2e",
32
+ "test:unit": "vitest run --config vitest.config.ts --coverage",
33
+ "test:integration": "vitest run --config vitest.integration.config.ts",
34
+ "test:e2e": "npm run build && vitest run --config vitest.e2e.config.ts",
35
+ "test:watch": "vitest"
36
+ },
37
+ dependencies: {
38
+ cac: "^6.7.14",
39
+ consola: "^3.4.2",
40
+ lilconfig: "^3.1.3",
41
+ picocolors: "^1.1.1",
42
+ yaml: "^2.8.2",
43
+ zod: "^3.22.4"
44
+ },
45
+ devDependencies: {
46
+ "@types/fs-extra": "^11.0.4",
47
+ "@types/node": "^20.10.0",
48
+ "@vitest/coverage-v8": "^4.0.15",
49
+ execa: "^9.6.1",
50
+ "fs-extra": "^11.3.2",
51
+ tsup: "^8.0.1",
52
+ typescript: "^5.3.3",
53
+ vitest: "^4.0.15"
54
+ }
55
+ };
56
+
57
+ // src/CLI.ts
58
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
59
+ var CLI = class {
60
+ name;
61
+ cli;
62
+ loader;
63
+ HelpCommandClass;
64
+ config;
65
+ constructor(config = {}) {
66
+ this.config = config;
67
+ this.name = this.config.commandName || "app";
68
+ this.cli = cac(this.name);
69
+ this.loader = new CommandLoader(this);
70
+ }
71
+ loadedCommands = [];
72
+ getCommands() {
73
+ return this.loadedCommands;
74
+ }
75
+ getRawCLI() {
76
+ return this.cli;
77
+ }
78
+ async start() {
79
+ if (process.argv.includes("--debug")) {
80
+ setDebugMode(true);
81
+ logger.debug("Debug mode enabled via --debug flag");
82
+ }
83
+ let commandsDirs = [];
84
+ if (this.config.searchDirectories && this.config.searchDirectories.length > 0) {
85
+ commandsDirs = this.config.searchDirectories;
86
+ } else {
87
+ const possibleDirs = [
88
+ path.resolve(__dirname, "./src/commands"),
89
+ path.resolve(process.cwd(), "commands")
90
+ // Fallback relative to cwd?
91
+ ];
92
+ for (const dir of possibleDirs) {
93
+ if (fs.existsSync(dir)) {
94
+ commandsDirs.push(dir);
95
+ break;
96
+ }
97
+ }
98
+ }
99
+ if (commandsDirs.length === 0) {
100
+ logger.debug("No commands directory found.");
101
+ }
102
+ for (const dir of commandsDirs) {
103
+ await this.loader.load(dir);
104
+ }
105
+ this.loadedCommands = this.loader.getCommands();
106
+ const commandGroups = {};
107
+ const helpCmd = this.loadedCommands.find((c) => c.command === "help");
108
+ if (helpCmd) {
109
+ this.HelpCommandClass = helpCmd.class;
110
+ }
111
+ for (const cmd of this.loadedCommands) {
112
+ const root = cmd.command.split(" ")[0];
113
+ if (!commandGroups[root]) commandGroups[root] = [];
114
+ commandGroups[root].push(cmd);
115
+ }
116
+ for (const [root, cmds] of Object.entries(commandGroups)) {
117
+ if (cmds.length === 1 && cmds[0].command === root) {
118
+ const cmd = cmds[0];
119
+ const CommandClass = cmd.class;
120
+ let commandName = cmd.command;
121
+ const argsDef = CommandClass.args || {};
122
+ if (argsDef.args) {
123
+ argsDef.args.forEach((arg) => {
124
+ const isVariadic = arg.name.endsWith("...");
125
+ const cleanName = isVariadic ? arg.name.slice(0, -3) : arg.name;
126
+ commandName += isVariadic ? ` [...${cleanName}]` : ` [${cleanName}]`;
127
+ });
128
+ }
129
+ const cacCommand = this.cli.command(commandName, CommandClass.description || "");
130
+ if (argsDef.options) {
131
+ argsDef.options.forEach((opt) => {
132
+ cacCommand.option(opt.name, opt.description, { default: opt.default });
133
+ });
134
+ }
135
+ this.registerGlobalOptions(cacCommand);
136
+ cacCommand.action(async (...args) => {
137
+ const options = args.pop();
138
+ if (options.help) {
139
+ await this.runHelp([cmd.command]);
140
+ return;
141
+ }
142
+ const positionalArgs = args;
143
+ if (argsDef.args) {
144
+ argsDef.args.forEach((arg, index) => {
145
+ const isVariadic = arg.name.endsWith("...");
146
+ const name = isVariadic ? arg.name.slice(0, -3) : arg.name;
147
+ const val = positionalArgs[index];
148
+ if (val !== void 0) {
149
+ options[name] = val;
150
+ } else if (arg.required) {
151
+ console.error(pc.red(`Missing required argument: ${name}`));
152
+ this.runHelp([cmd.command]).then(() => process.exit(1));
153
+ return;
154
+ }
155
+ });
156
+ }
157
+ await this.runCommand(CommandClass, options, [cmd.command]);
158
+ });
159
+ } else {
160
+ const commandName = `${root} [subcommand] [...args]`;
161
+ const cacCommand = this.cli.command(commandName, `Manage ${root} commands`);
162
+ cacCommand.allowUnknownOptions();
163
+ this.registerGlobalOptions(cacCommand);
164
+ cacCommand.action(async (subcommand, ...args) => {
165
+ const options = args.pop();
166
+ if (!subcommand || options.help) {
167
+ await this.runHelp([root, subcommand].filter(Boolean));
168
+ return;
169
+ }
170
+ const fullCommandName = `${root} ${subcommand}`;
171
+ const cmd = cmds.find((c) => c.command === fullCommandName);
172
+ if (!cmd) {
173
+ console.error(pc.red(`Unknown subcommand '${subcommand}' for '${root}'`));
174
+ process.exit(1);
175
+ }
176
+ const CommandClass = cmd.class;
177
+ const argsDef = CommandClass.args || {};
178
+ const positionalArgs = args.length > 0 && Array.isArray(args[0]) ? args[0] : args;
179
+ const childOptions = { ...options };
180
+ const cmdParts = [root, subcommand];
181
+ if (argsDef.args) {
182
+ argsDef.args.forEach((arg, index) => {
183
+ const isVariadic = arg.name.endsWith("...");
184
+ const name = isVariadic ? arg.name.slice(0, -3) : arg.name;
185
+ const val = positionalArgs[index];
186
+ if (val !== void 0) {
187
+ if (isVariadic) {
188
+ childOptions[name] = positionalArgs.slice(index);
189
+ } else {
190
+ childOptions[name] = val;
191
+ }
192
+ } else if (arg.required) {
193
+ console.error(pc.red(`Missing required argument: ${name}`));
194
+ this.runHelp(cmdParts).then(() => process.exit(1));
195
+ return;
196
+ }
197
+ });
198
+ }
199
+ await this.runCommand(CommandClass, childOptions, cmdParts);
200
+ });
201
+ }
202
+ }
203
+ this.cli.option("--help, -h", "Display help");
204
+ this.cli.version(package_default.version);
205
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
206
+ const args = process.argv.slice(2).filter((a) => !a.startsWith("-"));
207
+ if (args.length === 0) {
208
+ await this.runHelp([]);
209
+ process.exit(0);
210
+ }
211
+ }
212
+ try {
213
+ this.cli.parse();
214
+ } catch (e) {
215
+ console.error(pc.red(e.message));
216
+ console.log("");
217
+ const args = process.argv.slice(2);
218
+ const potentialCommand = args.find((a) => !a.startsWith("-"));
219
+ const helpArgs = potentialCommand ? [potentialCommand] : [];
220
+ await this.runHelp(helpArgs);
221
+ process.exit(1);
222
+ }
223
+ }
224
+ registerGlobalOptions(cacCommand) {
225
+ cacCommand.option("--root-dir <path>", "Override project root");
226
+ cacCommand.option("--debug", "Enable debug mode");
227
+ cacCommand.option("--help, -h", "Display help message");
228
+ }
229
+ async runHelp(commandParts) {
230
+ if (this.HelpCommandClass) {
231
+ const helpInstance = new this.HelpCommandClass(this);
232
+ await helpInstance.run({ command: commandParts });
233
+ } else {
234
+ this.cli.outputHelp();
235
+ }
236
+ }
237
+ async runCommand(CommandClass, options, commandParts = []) {
238
+ try {
239
+ const instance = new CommandClass(this, options);
240
+ await instance.init();
241
+ await instance.run(options);
242
+ } catch (e) {
243
+ console.error(pc.red(e.message));
244
+ if (options.debug) console.error(e.stack);
245
+ console.log("");
246
+ await this.runHelp(commandParts);
247
+ process.exit(1);
248
+ }
249
+ }
250
+ };
251
+
252
+ export {
253
+ CLI
254
+ };
255
+ //# sourceMappingURL=chunk-2NZ4H42M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/CLI.ts","../package.json"],"sourcesContent":["import { cac } from 'cac';\nimport { CommandLoader } from './CommandLoader.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport pc from 'picocolors';\nimport pkg from '../package.json';\nimport { logger, setDebugMode } from './utils/logger.js';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface CLIConfig {\n commandName?: string;\n searchDirectories?: string[];\n}\n\nexport class CLI {\n public name: string;\n private cli: ReturnType<typeof cac>;\n private loader: CommandLoader;\n private HelpCommandClass: any;\n private config: CLIConfig;\n\n constructor(config: CLIConfig = {}) {\n this.config = config;\n this.name = this.config.commandName || 'app';\n this.cli = cac(this.name);\n this.loader = new CommandLoader(this);\n }\n\n private loadedCommands: any[] = [];\n\n getCommands() {\n return this.loadedCommands;\n }\n\n getRawCLI() {\n return this.cli;\n }\n\n async start() {\n // In built version, we are in dist/index.js (from cli/index.ts) -> core bundled? or just imported.\n // The core logic is now in src/cli/core/src/CLI.ts or dist/core/src/CLI.js\n\n // Check for debug flag early\n if (process.argv.includes('--debug')) {\n setDebugMode(true);\n logger.debug('Debug mode enabled via --debug flag');\n }\n\n let commandsDirs: string[] = [];\n\n if (this.config.searchDirectories && this.config.searchDirectories.length > 0) {\n commandsDirs = this.config.searchDirectories;\n } else {\n // We assume the standard structure:\n // cli/\n // index.js\n // commands/\n // core/\n // src/\n // CLI.ts\n\n // When running from source (ts-node src/cli/index.ts), specific commands are in src/cli/commands.\n // core is in src/cli/core/src.\n // Relative path from CLI.ts to commands: ../../../commands\n\n const possibleDirs = [\n path.resolve(__dirname, './src/commands'),\n path.resolve(process.cwd(), 'commands') // Fallback relative to cwd?\n ];\n\n for (const dir of possibleDirs) {\n if (fs.existsSync(dir)) {\n commandsDirs.push(dir);\n break; // Keep existing behavior: verify if we want multiple or just first found\n }\n }\n }\n\n // Fallback or error\n if (commandsDirs.length === 0) {\n logger.debug(\"No commands directory found.\");\n }\n\n for (const dir of commandsDirs) {\n // Loader accumulates commands\n await this.loader.load(dir);\n }\n this.loadedCommands = this.loader.getCommands();\n\n // Group commands by root command name\n const commandGroups: Record<string, any[]> = {};\n // Locate HelpCommand for fallback usage\n const helpCmd = this.loadedCommands.find(c => c.command === 'help');\n if (helpCmd) {\n this.HelpCommandClass = helpCmd.class;\n }\n\n for (const cmd of this.loadedCommands) {\n const root = cmd.command.split(' ')[0];\n if (!commandGroups[root]) commandGroups[root] = [];\n commandGroups[root].push(cmd);\n }\n\n for (const [root, cmds] of Object.entries(commandGroups)) {\n // Case 1: Single command, no subcommands (e.g. 'init')\n if (cmds.length === 1 && cmds[0].command === root) {\n const cmd = cmds[0];\n const CommandClass = cmd.class;\n // Original logic for single command\n let commandName = cmd.command;\n const argsDef = CommandClass.args || {};\n if (argsDef.args) {\n argsDef.args.forEach((arg: any) => {\n const isVariadic = arg.name.endsWith('...');\n const cleanName = isVariadic ? arg.name.slice(0, -3) : arg.name;\n // Always use optional brackets [] for CAC to allow --help to pass validation\n // We will enforce 'required' manually in the action handler\n commandName += isVariadic ? ` [...${cleanName}]` : ` [${cleanName}]`;\n });\n }\n const cacCommand = this.cli.command(commandName, CommandClass.description || '');\n\n // Register options\n if (argsDef.options) {\n argsDef.options.forEach((opt: any) => {\n cacCommand.option(opt.name, opt.description, { default: opt.default });\n });\n }\n this.registerGlobalOptions(cacCommand);\n\n cacCommand.action(async (...args: any[]) => {\n const options = args.pop();\n\n if (options.help) {\n await this.runHelp([cmd.command]);\n return;\n }\n\n const positionalArgs = args;\n\n if (argsDef.args) {\n argsDef.args.forEach((arg: any, index: number) => {\n const isVariadic = arg.name.endsWith('...');\n const name = isVariadic ? arg.name.slice(0, -3) : arg.name;\n const val = positionalArgs[index];\n\n if (val !== undefined) {\n options[name] = val;\n } else if (arg.required) {\n console.error(pc.red(`Missing required argument: ${name}`));\n this.runHelp([cmd.command]).then(() => process.exit(1));\n return;\n }\n });\n }\n await this.runCommand(CommandClass, options, [cmd.command]);\n });\n } else {\n // Case 2: Command with subcommands (e.g. 'module add')\n // Register 'module <subcommand>' catch-all\n const commandName = `${root} [subcommand] [...args]`;\n const cacCommand = this.cli.command(commandName, `Manage ${root} commands`);\n\n cacCommand.allowUnknownOptions(); // Pass options to subcommand\n this.registerGlobalOptions(cacCommand);\n\n cacCommand.action(async (subcommand: string, ...args: any[]) => {\n const options = args.pop(); // last is options\n\n if (!subcommand || options.help) {\n // If --help is passed to 'module --help', subcommand might be caught as 'module' if args parsing is weird? \n // ACTUALLY: cac parses 'module add --help' as subcommand=\"add\".\n // 'module --help' might trigger the command itself? No, 'module <subcommand>' expects a subcommand.\n // If I run 'module --help', it might fail validation or parse 'help' as subcommand if unlucky, \n // but likely it just prints help if we didn't override.\n\n await this.runHelp([root, subcommand].filter(Boolean));\n return;\n }\n\n\n // Find matching command\n // Match against \"root subcommand\"\n const fullCommandName = `${root} ${subcommand}`;\n const cmd = cmds.find(c => c.command === fullCommandName);\n\n if (!cmd) {\n console.error(pc.red(`Unknown subcommand '${subcommand}' for '${root}'`));\n process.exit(1);\n }\n\n const CommandClass = cmd.class;\n // Map remaining args? \n // The args array contains positional args AFTER subcommand.\n // But we didn't define them in CAC, so they are just strings.\n // We need to map them manually to the Target Command's args definition.\n // argsDef.args usually starts after the command.\n // For 'module add <url>', <url> is the first arg after 'add'.\n // So 'args' here corresponds to <url>.\n\n const argsDef = CommandClass.args || {};\n // If using [...args], the variadic args are collected into the first argument array\n // args here is what remains after popping options.\n const positionalArgs = (args.length > 0 && Array.isArray(args[0])) ? args[0] : args;\n\n const childOptions = { ...options }; // Copy options\n\n const cmdParts = [root, subcommand];\n\n if (argsDef.args) {\n argsDef.args.forEach((arg: any, index: number) => {\n const isVariadic = arg.name.endsWith('...');\n const name = isVariadic ? arg.name.slice(0, -3) : arg.name;\n const val = positionalArgs[index];\n\n if (val !== undefined) {\n if (isVariadic) {\n childOptions[name] = positionalArgs.slice(index);\n } else {\n childOptions[name] = val;\n }\n } else if (arg.required) {\n console.error(pc.red(`Missing required argument: ${name}`));\n this.runHelp(cmdParts).then(() => process.exit(1));\n return;\n }\n });\n }\n\n await this.runCommand(CommandClass, childOptions, cmdParts);\n });\n }\n }\n // Disable default help\n // this.cli.help(); \n\n // Manually register global help to ensure it's allowed\n this.cli.option('--help, -h', 'Display help');\n\n this.cli.version(pkg.version);\n\n // Global help interception for root command\n // If we run `app --help`, we need to catch it.\n // CAC doesn't expose a clean global action without a command content.\n // However, if we parse and no command matches, it usually errors or shows help.\n // If we have default logic, we can put it here?\n // Let's rely on standard parsing but maybe inspect raw args first?\n\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n // Inspect non-option args to see if there's a command?\n const args = process.argv.slice(2).filter(a => !a.startsWith('-'));\n if (args.length === 0) {\n await this.runHelp([]);\n process.exit(0);\n }\n }\n\n try {\n this.cli.parse();\n } catch (e: any) {\n console.error(pc.red(e.message));\n\n // Try to provide helpful context\n console.log('');\n // Simple heuristic: find first non-flag arg as command\n const args = process.argv.slice(2);\n const potentialCommand = args.find(a => !a.startsWith('-'));\n // If it matches a loaded command root, show help for it\n // Otherwise show global help\n\n // We need to match 'module add' etc?\n // Just pass the potential command parts to runHelp. \n // runHelp handles filtering itself? No, runHelp takes commandParts to pass to HelpCommand.\n // HelpCommand expects `command` array.\n\n const helpArgs = potentialCommand ? [potentialCommand] : [];\n await this.runHelp(helpArgs);\n\n process.exit(1);\n }\n }\n\n private registerGlobalOptions(cacCommand: any) {\n cacCommand.option('--root-dir <path>', 'Override project root');\n cacCommand.option('--debug', 'Enable debug mode');\n cacCommand.option('--help, -h', 'Display help message');\n }\n\n private async runHelp(commandParts: string[]) {\n if (this.HelpCommandClass) {\n const helpInstance = new this.HelpCommandClass(this);\n await helpInstance.run({ command: commandParts });\n } else {\n // Fallback if HelpCommand not loaded (shouldn't happen)\n this.cli.outputHelp();\n }\n }\n\n private async runCommand(CommandClass: any, options: any, commandParts: string[] = []) {\n try {\n const instance = new CommandClass(this, options);\n await instance.init();\n await instance.run(options);\n } catch (e: any) {\n console.error(pc.red(e.message));\n if (options.debug) console.error(e.stack);\n\n console.log(''); // spacer\n await this.runHelp(commandParts);\n\n process.exit(1);\n }\n }\n}\n","{\n \"name\": \"@nexical/cli-core\",\n \"version\": \"0.1.1\",\n \"type\": \"module\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"cli\": \"node dist/cli.js\",\n \"test\": \"npm run test:unit && npm run test:integration && npm run test:e2e\",\n \"test:unit\": \"vitest run --config vitest.config.ts --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.ts\",\n \"test:e2e\": \"npm run build && vitest run --config vitest.e2e.config.ts\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"cac\": \"^6.7.14\",\n \"consola\": \"^3.4.2\",\n \"lilconfig\": \"^3.1.3\",\n \"picocolors\": \"^1.1.1\",\n \"yaml\": \"^2.8.2\",\n \"zod\": \"^3.22.4\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^20.10.0\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"execa\": \"^9.6.1\",\n \"fs-extra\": \"^11.3.2\",\n \"tsup\": \"^8.0.1\",\n \"typescript\": \"^5.3.3\",\n \"vitest\": \"^4.0.15\"\n }\n}"],"mappings":"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","names":[]}
@@ -0,0 +1,48 @@
1
+ import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
+ import {
3
+ logger
4
+ } from "./chunk-HETFF3FQ.js";
5
+
6
+ // src/utils/config.ts
7
+ import { lilconfig } from "lilconfig";
8
+ import path from "path";
9
+ import YAML from "yaml";
10
+ var loadYaml = (filepath, content) => {
11
+ return YAML.parse(content);
12
+ };
13
+ async function findProjectRoot(commandName, startDir) {
14
+ const searchPlaces = [`${commandName}.yml`, `${commandName}.yaml`];
15
+ const explorer = lilconfig(commandName, {
16
+ searchPlaces,
17
+ loaders: {
18
+ ".yml": loadYaml,
19
+ ".yaml": loadYaml
20
+ }
21
+ });
22
+ const result = await explorer.search(startDir);
23
+ if (result) {
24
+ logger.debug(`Project root found at: ${path.dirname(result.filepath)}`);
25
+ return path.dirname(result.filepath);
26
+ }
27
+ return null;
28
+ }
29
+ async function loadConfig(commandName, rootDir) {
30
+ const searchPlaces = [`${commandName}.yml`, `${commandName}.yaml`];
31
+ const explorer = lilconfig(commandName, {
32
+ searchPlaces,
33
+ loaders: {
34
+ ".yml": loadYaml,
35
+ ".yaml": loadYaml
36
+ }
37
+ });
38
+ const result = await explorer.search(rootDir);
39
+ logger.debug(result ? `Loaded config from ${result.filepath}` : `No config found in ${rootDir}`);
40
+ return result ? result.config : {};
41
+ }
42
+
43
+ export {
44
+ loadYaml,
45
+ findProjectRoot,
46
+ loadConfig
47
+ };
48
+ //# sourceMappingURL=chunk-5YGASH25.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/config.ts"],"sourcesContent":["import { lilconfig, type Loader } from 'lilconfig';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport { logger } from './logger.js';\n\nexport const loadYaml: Loader = (filepath, content) => {\n return YAML.parse(content);\n};\n\nexport async function findProjectRoot(commandName: string, startDir: string): Promise<string | null> {\n const searchPlaces = [`${commandName}.yml`, `${commandName}.yaml`];\n\n // We use lilconfig to find the file up the tree\n const explorer = lilconfig(commandName, {\n searchPlaces,\n loaders: {\n '.yml': loadYaml,\n '.yaml': loadYaml,\n }\n });\n\n const result = await explorer.search(startDir);\n if (result) {\n logger.debug(`Project root found at: ${path.dirname(result.filepath)}`);\n return path.dirname(result.filepath);\n }\n\n return null;\n}\n\nexport async function loadConfig(commandName: string, rootDir: string): Promise<any> {\n const searchPlaces = [`${commandName}.yml`, `${commandName}.yaml`];\n const explorer = lilconfig(commandName, {\n searchPlaces,\n loaders: {\n '.yml': loadYaml,\n '.yaml': loadYaml,\n }\n });\n const result = await explorer.search(rootDir);\n logger.debug(result ? `Loaded config from ${result.filepath}` : `No config found in ${rootDir}`);\n return result ? result.config : {};\n}\n"],"mappings":";;;;;;AAAA,SAAS,iBAA8B;AACvC,OAAO,UAAU;AACjB,OAAO,UAAU;AAGV,IAAM,WAAmB,CAAC,UAAU,YAAY;AACnD,SAAO,KAAK,MAAM,OAAO;AAC7B;AAEA,eAAsB,gBAAgB,aAAqB,UAA0C;AACjG,QAAM,eAAe,CAAC,GAAG,WAAW,QAAQ,GAAG,WAAW,OAAO;AAGjE,QAAM,WAAW,UAAU,aAAa;AAAA,IACpC;AAAA,IACA,SAAS;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,IACb;AAAA,EACJ,CAAC;AAED,QAAM,SAAS,MAAM,SAAS,OAAO,QAAQ;AAC7C,MAAI,QAAQ;AACR,WAAO,MAAM,0BAA0B,KAAK,QAAQ,OAAO,QAAQ,CAAC,EAAE;AACtE,WAAO,KAAK,QAAQ,OAAO,QAAQ;AAAA,EACvC;AAEA,SAAO;AACX;AAEA,eAAsB,WAAW,aAAqB,SAA+B;AACjF,QAAM,eAAe,CAAC,GAAG,WAAW,QAAQ,GAAG,WAAW,OAAO;AACjE,QAAM,WAAW,UAAU,aAAa;AAAA,IACpC;AAAA,IACA,SAAS;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,IACb;AAAA,EACJ,CAAC;AACD,QAAM,SAAS,MAAM,SAAS,OAAO,OAAO;AAC5C,SAAO,MAAM,SAAS,sBAAsB,OAAO,QAAQ,KAAK,sBAAsB,OAAO,EAAE;AAC/F,SAAO,SAAS,OAAO,SAAS,CAAC;AACrC;","names":[]}
@@ -0,0 +1,67 @@
1
+ import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
+ import {
3
+ findProjectRoot,
4
+ loadConfig
5
+ } from "./chunk-5YGASH25.js";
6
+ import {
7
+ logger
8
+ } from "./chunk-HETFF3FQ.js";
9
+
10
+ // src/BaseCommand.ts
11
+ import process from "process";
12
+ var BaseCommand = class {
13
+ static usage = "";
14
+ static description = "";
15
+ static args = {};
16
+ // Configurable flags
17
+ static requiresProject = false;
18
+ projectRoot = null;
19
+ config = {};
20
+ globalOptions = {};
21
+ cli = null;
22
+ constructor(cli, globalOptions = {}) {
23
+ this.globalOptions = globalOptions;
24
+ this.cli = cli;
25
+ }
26
+ async init() {
27
+ if (this.globalOptions.rootDir) {
28
+ this.projectRoot = this.globalOptions.rootDir;
29
+ } else {
30
+ this.projectRoot = await findProjectRoot(this.cli.name, process.cwd());
31
+ }
32
+ const requiresProject = this.constructor.requiresProject;
33
+ if (requiresProject && !this.projectRoot) {
34
+ this.error("This command requires to be run within an app project (app.yml not found).", 1);
35
+ return;
36
+ }
37
+ if (this.projectRoot) {
38
+ this.config = await loadConfig(this.cli.name, this.projectRoot);
39
+ }
40
+ }
41
+ // Helpers
42
+ success(msg) {
43
+ logger.success(msg);
44
+ }
45
+ info(msg) {
46
+ logger.info(msg);
47
+ }
48
+ warn(msg) {
49
+ logger.warn(msg);
50
+ }
51
+ error(msg, code = 1) {
52
+ if (msg instanceof Error) {
53
+ logger.error(msg.message);
54
+ if (this.globalOptions.debug) {
55
+ logger.error(msg.stack);
56
+ }
57
+ } else {
58
+ logger.error(msg);
59
+ }
60
+ process.exit(code);
61
+ }
62
+ };
63
+
64
+ export {
65
+ BaseCommand
66
+ };
67
+ //# sourceMappingURL=chunk-6LSNSDTL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/BaseCommand.ts"],"sourcesContent":["import { logger } from './utils/logger.js';\nimport { CommandDefinition, CommandInterface } from './CommandInterface.js';\nimport { findProjectRoot, loadConfig } from './utils/config.js';\nimport process from 'node:process';\n\nexport abstract class BaseCommand implements CommandInterface {\n static usage = '';\n static description = '';\n static args: CommandDefinition = {};\n\n // Configurable flags\n static requiresProject = false;\n\n protected projectRoot: string | null = null;\n protected config: any = {};\n protected globalOptions: any = {};\n protected cli: any = null;\n\n\n constructor(cli: any, globalOptions: any = {}) {\n this.globalOptions = globalOptions;\n this.cli = cli;\n }\n\n async init() {\n // 1. Root detection strategy\n if (this.globalOptions.rootDir) {\n this.projectRoot = this.globalOptions.rootDir;\n } else {\n this.projectRoot = await findProjectRoot(this.cli.name, process.cwd());\n }\n\n const requiresProject = (this.constructor as any).requiresProject;\n\n if (requiresProject && !this.projectRoot) {\n this.error('This command requires to be run within an app project (app.yml not found).', 1);\n return; // TS doesn't know error exits\n }\n\n if (this.projectRoot) {\n this.config = await loadConfig(this.cli.name, this.projectRoot);\n // logger.debug(`Loaded config from ${this.projectRoot}`);\n }\n }\n\n abstract run(options: any): Promise<void>;\n\n // Helpers\n success(msg: string) {\n logger.success(msg);\n }\n\n\n info(msg: string) {\n logger.info(msg);\n }\n\n warn(msg: string) {\n logger.warn(msg);\n }\n\n error(msg: string | Error, code = 1) {\n if (msg instanceof Error) {\n logger.error(msg.message);\n if (this.globalOptions.debug) {\n logger.error(msg.stack);\n }\n } else {\n logger.error(msg);\n }\n process.exit(code);\n }\n}\n"],"mappings":";;;;;;;;;;AAGA,OAAO,aAAa;AAEb,IAAe,cAAf,MAAuD;AAAA,EAC1D,OAAO,QAAQ;AAAA,EACf,OAAO,cAAc;AAAA,EACrB,OAAO,OAA0B,CAAC;AAAA;AAAA,EAGlC,OAAO,kBAAkB;AAAA,EAEf,cAA6B;AAAA,EAC7B,SAAc,CAAC;AAAA,EACf,gBAAqB,CAAC;AAAA,EACtB,MAAW;AAAA,EAGrB,YAAY,KAAU,gBAAqB,CAAC,GAAG;AAC3C,SAAK,gBAAgB;AACrB,SAAK,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,OAAO;AAET,QAAI,KAAK,cAAc,SAAS;AAC5B,WAAK,cAAc,KAAK,cAAc;AAAA,IAC1C,OAAO;AACH,WAAK,cAAc,MAAM,gBAAgB,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,IACzE;AAEA,UAAM,kBAAmB,KAAK,YAAoB;AAElD,QAAI,mBAAmB,CAAC,KAAK,aAAa;AACtC,WAAK,MAAM,8EAA8E,CAAC;AAC1F;AAAA,IACJ;AAEA,QAAI,KAAK,aAAa;AAClB,WAAK,SAAS,MAAM,WAAW,KAAK,IAAI,MAAM,KAAK,WAAW;AAAA,IAElE;AAAA,EACJ;AAAA;AAAA,EAKA,QAAQ,KAAa;AACjB,WAAO,QAAQ,GAAG;AAAA,EACtB;AAAA,EAGA,KAAK,KAAa;AACd,WAAO,KAAK,GAAG;AAAA,EACnB;AAAA,EAEA,KAAK,KAAa;AACd,WAAO,KAAK,GAAG;AAAA,EACnB;AAAA,EAEA,MAAM,KAAqB,OAAO,GAAG;AACjC,QAAI,eAAe,OAAO;AACtB,aAAO,MAAM,IAAI,OAAO;AACxB,UAAI,KAAK,cAAc,OAAO;AAC1B,eAAO,MAAM,IAAI,KAAK;AAAA,MAC1B;AAAA,IACJ,OAAO;AACH,aAAO,MAAM,GAAG;AAAA,IACpB;AACA,YAAQ,KAAK,IAAI;AAAA,EACrB;AACJ;","names":[]}
@@ -0,0 +1,2 @@
1
+ import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
+ //# sourceMappingURL=chunk-C4IL52NB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,20 @@
1
+ import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
+
3
+ // src/utils/logger.ts
4
+ import { consola, LogLevels } from "consola";
5
+ var logger = consola.create({
6
+ defaults: {
7
+ tag: "DEBUG"
8
+ },
9
+ level: LogLevels.info
10
+ // Default to info
11
+ });
12
+ function setDebugMode(enabled) {
13
+ logger.level = enabled ? LogLevels.debug : LogLevels.info;
14
+ }
15
+
16
+ export {
17
+ logger,
18
+ setDebugMode
19
+ };
20
+ //# sourceMappingURL=chunk-HETFF3FQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/logger.ts"],"sourcesContent":["import { consola, LogLevels } from 'consola';\n\nexport const logger = consola.create({\n defaults: {\n tag: 'DEBUG',\n },\n level: LogLevels.info, // Default to info\n});\n\nexport function setDebugMode(enabled: boolean) {\n logger.level = enabled ? LogLevels.debug : LogLevels.info;\n}\n"],"mappings":";;;AAAA,SAAS,SAAS,iBAAiB;AAE5B,IAAM,SAAS,QAAQ,OAAO;AAAA,EACjC,UAAU;AAAA,IACN,KAAK;AAAA,EACT;AAAA,EACA,OAAO,UAAU;AAAA;AACrB,CAAC;AAEM,SAAS,aAAa,SAAkB;AAC3C,SAAO,QAAQ,UAAU,UAAU,QAAQ,UAAU;AACzD;","names":[]}