Skip to content

Commit 1b3b7de

Browse files
committed
Adding an initial README.
1 parent 88c8419 commit 1b3b7de

1 file changed

Lines changed: 195 additions & 0 deletions

File tree

README.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)