mirror of
				https://github.com/SikongJueluo/cc-utils.git
				synced 2025-11-04 11:17:50 +08:00 
			
		
		
		
	docs: ccCLI framework and ChatManager
This commit is contained in:
		
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@@ -30,7 +30,17 @@ A declarative, reactive TUI (Terminal User Interface) framework inspired by [Sol
 | 
			
		||||
- **Control Flow:** Includes `<For>` and `<Show>` components for conditional and list-based rendering.
 | 
			
		||||
- **Component-Based:** Structure your UI into reusable components. See `src/tuiExample/main.ts` for a demo.
 | 
			
		||||
 | 
			
		||||
### 4. Core Libraries
 | 
			
		||||
### 4. ccCLI Framework
 | 
			
		||||
A lightweight, functional-style framework for building command-line interfaces (CLIs) within CC:Tweaked. It supports nested commands, arguments, options, and automatic help generation. See the [ccCLI Documentation](docs/ccCLI.md) for more details.
 | 
			
		||||
 | 
			
		||||
- **Declarative API:** Define commands, arguments, and options using a simple, object-based structure.
 | 
			
		||||
- **Nested Commands:** Organize complex applications with subcommands (e.g., `mycli command subcommand`).
 | 
			
		||||
- **Automatic Help:** Generates detailed help messages for commands and subcommands.
 | 
			
		||||
- **Global Context:** Inject shared state or services into command actions.
 | 
			
		||||
- **Type-Safe:** Built with TypeScript for robust development.
 | 
			
		||||
 | 
			
		||||
### 5. Core Libraries
 | 
			
		||||
- **`ChatManager`:** A powerful manager for `chatBox` peripherals that handles message queuing, cooldowns, and asynchronous sending/receiving. See the [ChatManager Documentation](docs/ChatManager.md) for more details.
 | 
			
		||||
- **`ccLog`:** A robust logging library with automatic, time-based log file rotation.
 | 
			
		||||
- **`PeripheralManager`:** A utility for easily finding and requiring peripherals by name or type.
 | 
			
		||||
- **`CraftManager`:** A library for parsing and executing crafting recipes from Create mod packages.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										235
									
								
								docs/ChatManager.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								docs/ChatManager.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,235 @@
 | 
			
		||||
# ChatManager Documentation
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
`ChatManager` is a powerful utility for managing interactions with one or more `chatBox` peripherals in ComputerCraft. It simplifies the process of sending and receiving chat messages by handling complexities like peripheral cooldowns, message queuing, and asynchronous operations.
 | 
			
		||||
 | 
			
		||||
It is designed for applications that need to reliably send a high volume of messages or toasts without getting bogged down by peripheral limitations, or for applications that need to listen for commands or messages from players.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
*   **Multi-Peripheral Management:** Seamlessly manages one or more `chatBox` peripherals.
 | 
			
		||||
*   **Message Queuing:** Automatically queues messages and toasts, sending them as chatboxes become available.
 | 
			
		||||
*   **Cooldown Handling:** Respects the 1-second cooldown of chatboxes to prevent message loss.
 | 
			
		||||
*   **Asynchronous Operation:** Can run in the background (`runAsync`) without blocking your main program loop.
 | 
			
		||||
*   **Message Buffering:** Receives and buffers incoming chat messages for your application to process.
 | 
			
		||||
*   **Queued and Immediate Sending:** Supports both adding messages to a queue and sending them immediately (if a chatbox is available).
 | 
			
		||||
*   **Rich Content Support:** Send simple strings or complex formatted messages using `MinecraftTextComponent`.
 | 
			
		||||
*   **Robust Error Handling:** Uses a `Result`-based API to make error handling explicit and reliable.
 | 
			
		||||
*   **Comprehensive API:** Provides methods for sending global messages, private messages, and toast notifications.
 | 
			
		||||
 | 
			
		||||
## Tutorial: Getting Started with ChatManager
 | 
			
		||||
 | 
			
		||||
Here’s how to integrate `ChatManager` into your project.
 | 
			
		||||
 | 
			
		||||
### 1. Initialization
 | 
			
		||||
 | 
			
		||||
First, find your available `chatBox` peripherals and create a `ChatManager` instance.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
import { ChatManager } from '@/lib/ChatManager';
 | 
			
		||||
 | 
			
		||||
// Find all available chatbox peripherals
 | 
			
		||||
const peripheralNames = peripheral.getNames();
 | 
			
		||||
const chatboxPeripherals: ChatBoxPeripheral[] = [];
 | 
			
		||||
 | 
			
		||||
for (const name of peripheralNames) {
 | 
			
		||||
  const peripheralType = peripheral.getType(name);
 | 
			
		||||
  if (peripheralType[0] === "chatBox") {
 | 
			
		||||
    const chatbox = peripheral.wrap(name) as ChatBoxPeripheral;
 | 
			
		||||
    chatboxPeripherals.push(chatbox);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (chatboxPeripherals.length === 0) {
 | 
			
		||||
  print("Error: No chatbox peripherals found.");
 | 
			
		||||
  return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create the manager instance
 | 
			
		||||
const chatManager = new ChatManager(chatboxPeripherals);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2. Running the Manager
 | 
			
		||||
 | 
			
		||||
To start the sending and receiving loops, you must run the manager. For most use cases, running it asynchronously is best.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// Start ChatManager in the background so it doesn't block the main program
 | 
			
		||||
const runResult = chatManager.runAsync();
 | 
			
		||||
 | 
			
		||||
if (runResult.isErr()) {
 | 
			
		||||
  print(`Warning: Failed to start ChatManager: ${runResult.error.reason}`);
 | 
			
		||||
} else {
 | 
			
		||||
  print("ChatManager started successfully!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Your main program logic can continue here...
 | 
			
		||||
```
 | 
			
		||||
**Important:** `ChatManager` relies on `gTimerManager` to handle cooldowns. Ensure you are also running the global timer manager in your application.
 | 
			
		||||
```typescript
 | 
			
		||||
import { gTimerManager } from "@/lib/TimerManager";
 | 
			
		||||
 | 
			
		||||
// In your main parallel loop
 | 
			
		||||
parallel.waitForAll(
 | 
			
		||||
  () => yourMainLoop(),
 | 
			
		||||
  () => chatManager.run(), // if you choose the blocking run
 | 
			
		||||
  () => gTimerManager.run()
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3. Sending a Message (Queued)
 | 
			
		||||
 | 
			
		||||
Use `sendMessage` to add a message to the queue. `ChatManager` will send it as soon as a chatbox is free.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// Send a global message
 | 
			
		||||
chatManager.sendMessage({
 | 
			
		||||
  message: "Hello, world!",
 | 
			
		||||
  prefix: "MySystem",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Send a private message
 | 
			
		||||
chatManager.sendMessage({
 | 
			
		||||
  message: "This is a secret.",
 | 
			
		||||
  targetPlayer: "Steve",
 | 
			
		||||
  prefix: "Whisper",
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 4. Sending a Toast (Queued)
 | 
			
		||||
 | 
			
		||||
Similarly, use `sendToast` to queue a toast notification.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
chatManager.sendToast({
 | 
			
		||||
  title: "Server Alert",
 | 
			
		||||
  message: "Restart in 5 minutes!",
 | 
			
		||||
  targetPlayer: "Steve",
 | 
			
		||||
  prefix: "Admin",
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5. Receiving Messages
 | 
			
		||||
 | 
			
		||||
Use `getReceivedMessage` to pull incoming chat events from the buffer. It's best to do this in a loop.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
function myCliLoop() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    const result = chatManager.getReceivedMessage();
 | 
			
		||||
    if (result.isOk()) {
 | 
			
		||||
      const event = result.value;
 | 
			
		||||
      print(`[${event.username}]: ${event.message}`);
 | 
			
		||||
      // Process the command or message...
 | 
			
		||||
    } else {
 | 
			
		||||
      // Buffer is empty, wait a bit before checking again
 | 
			
		||||
      sleep(0.5);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Advanced Topics
 | 
			
		||||
 | 
			
		||||
### Immediate Sending
 | 
			
		||||
 | 
			
		||||
If you need to send a message right away and bypass the queue, use the `Immediate` methods. These will fail if no chatbox is currently available.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
const result = chatManager.sendMessageImmediate({
 | 
			
		||||
  message: "URGENT!",
 | 
			
		||||
  targetPlayer: "Admin",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
if (result.isErr()) {
 | 
			
		||||
  if (result.error.kind === "NoIdleChatbox") {
 | 
			
		||||
    print("Could not send immediately: all chatboxes are busy.");
 | 
			
		||||
  } else {
 | 
			
		||||
    print(`Failed to send message: ${result.error.reason}`);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Rich Text Messages (`MinecraftTextComponent`)
 | 
			
		||||
 | 
			
		||||
You can send fully formatted messages by providing a `MinecraftTextComponent` object instead of a string.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
const richMessage: MinecraftTextComponent = {
 | 
			
		||||
  text: "This is ",
 | 
			
		||||
  color: "gold",
 | 
			
		||||
  extra: [
 | 
			
		||||
    { text: "important!", color: "red", bold: true }
 | 
			
		||||
  ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
chatManager.sendMessage({
 | 
			
		||||
  message: richMessage,
 | 
			
		||||
  targetPlayer: "AllPlayers",
 | 
			
		||||
  utf8Support: true, // Recommended for complex components
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Error Handling
 | 
			
		||||
 | 
			
		||||
Methods return a `Result` object (`Ok` or `Err`). Always check the result to handle potential failures gracefully.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
const result = chatManager.sendMessage(message);
 | 
			
		||||
if (result.isErr()) {
 | 
			
		||||
  logger.error(`Failed to queue message: ${result.error.reason}`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The possible error `kind`s are:
 | 
			
		||||
*   `ChatManagerError`: General errors, e.g., failure to enqueue.
 | 
			
		||||
*   `NoIdleChatboxError`: Returned by `Immediate` methods when no chatbox is free.
 | 
			
		||||
*   `SendFailureError`: A hardware or permission error occurred during sending.
 | 
			
		||||
*   `EmptyBufferError`: Returned by `getReceivedMessage` when the buffer is empty.
 | 
			
		||||
 | 
			
		||||
### Status and Management
 | 
			
		||||
 | 
			
		||||
You can inspect and control the `ChatManager` at runtime.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// Get the number of items waiting to be sent
 | 
			
		||||
const pending = chatManager.getPendingMessageCount();
 | 
			
		||||
print(`Messages in queue: ${pending}`);
 | 
			
		||||
 | 
			
		||||
// Get the number of received messages waiting to be processed
 | 
			
		||||
const buffered = chatManager.getBufferedMessageCount();
 | 
			
		||||
print(`Received messages in buffer: ${buffered}`);
 | 
			
		||||
 | 
			
		||||
// Get the status of each chatbox (true = idle, false = busy)
 | 
			
		||||
const statuses = chatManager.getChatboxStatus();
 | 
			
		||||
 | 
			
		||||
// Clear the sending queues
 | 
			
		||||
chatManager.clearQueues();
 | 
			
		||||
 | 
			
		||||
// Clear the received message buffer
 | 
			
		||||
chatManager.clearBuffer();
 | 
			
		||||
 | 
			
		||||
// Stop the manager's background loops
 | 
			
		||||
chatManager.stop();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## API Reference
 | 
			
		||||
 | 
			
		||||
### Core Class
 | 
			
		||||
*   `ChatManager(peripherals: ChatBoxPeripheral[])`
 | 
			
		||||
 | 
			
		||||
### Primary Methods
 | 
			
		||||
*   `run(): Result<void, ChatManagerError>`: Starts the manager (blocking).
 | 
			
		||||
*   `runAsync(): Result<LuaThread, ChatManagerError>`: Starts the manager in the background.
 | 
			
		||||
*   `stop(): Result<void, ChatManagerError>`: Stops the background loops.
 | 
			
		||||
*   `sendMessage(message: ChatMessage): Result<void, ChatManagerError>`: Queues a chat message.
 | 
			
		||||
*   `sendToast(toast: ChatToast): Result<void, ChatManagerError>`: Queues a toast.
 | 
			
		||||
*   `getReceivedMessage(): Result<ChatBoxEvent, EmptyBufferError>`: Retrieves a message from the receive buffer.
 | 
			
		||||
*   `sendMessageImmediate(message: ChatMessage): Result<void, ChatError>`: Sends a message immediately.
 | 
			
		||||
*   `sendToastImmediate(toast: ChatToast): Result<void, ChatError>`: Sends a toast immediately.
 | 
			
		||||
 | 
			
		||||
### Interfaces
 | 
			
		||||
*   `ChatMessage`
 | 
			
		||||
*   `ChatToast`
 | 
			
		||||
*   `ChatError` (union of all possible error types)
 | 
			
		||||
							
								
								
									
										248
									
								
								docs/ccCLI.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								docs/ccCLI.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,248 @@
 | 
			
		||||
# ccCLI Framework Documentation
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
`ccCLI` is a lightweight, functional-style framework for building command-line interfaces (CLIs) within the CC:Tweaked environment using TSTL (TypeScriptToLua). It provides a declarative and type-safe way to define commands, arguments, and options, with built-in support for nested commands, automatic help generation, and robust error handling.
 | 
			
		||||
 | 
			
		||||
Its design is inspired by modern CLI libraries and emphasizes simplicity and ease of use, allowing developers to quickly structure complex command-based applications.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
*   **Declarative API:** Define commands as simple objects.
 | 
			
		||||
*   **Type-Safe:** Leverage TypeScript for defining commands, arguments, options, and context.
 | 
			
		||||
*   **Nested Commands:** Easily create command groups and subcommands (e.g., `git remote add`).
 | 
			
		||||
*   **Automatic Help Generation:** Generates `--help` messages for the root command and all subcommands.
 | 
			
		||||
*   **Flexible Argument & Option Parsing:** Supports long names (`--verbose`), short names (`-v`), value assignment (`--file=path.txt`), and boolean flags.
 | 
			
		||||
*   **Global Context Injection:** Share state, services, or configuration across all commands.
 | 
			
		||||
*   **Result-Based Error Handling:** Command actions return a `Result` type, ensuring that errors are handled explicitly.
 | 
			
		||||
*   **No Dependencies:** Written in pure TypeScript with no external runtime dependencies.
 | 
			
		||||
 | 
			
		||||
## Core Concepts
 | 
			
		||||
 | 
			
		||||
The framework is built around a few key interfaces:
 | 
			
		||||
 | 
			
		||||
*   `Command<TContext>`: The central piece. It defines a command's name, description, arguments, options, subcommands, and the action to perform.
 | 
			
		||||
*   `Argument`: Defines a positional argument for a command. It can be marked as required.
 | 
			
		||||
*   `Option`: Defines a named option (flag). It can have a long name, a short name, a default value, and be marked as required.
 | 
			
		||||
*   `ActionContext<TContext>`: The object passed to every command's `action` function. It contains the parsed `args`, `options`, and the shared `context` object.
 | 
			
		||||
 | 
			
		||||
## Tutorial: Creating a Simple Calculator CLI
 | 
			
		||||
 | 
			
		||||
Let's build a simple calculator to see how `ccCLI` works.
 | 
			
		||||
 | 
			
		||||
### 1. Define the Global Context (Optional)
 | 
			
		||||
 | 
			
		||||
The global context is a powerful feature for sharing data or services. Let's define a context for our app.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// src/cliExample/main.ts
 | 
			
		||||
 | 
			
		||||
interface AppContext {
 | 
			
		||||
  appName: string;
 | 
			
		||||
  log: (message: string) => void;
 | 
			
		||||
  debugMode: boolean;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2. Define Commands
 | 
			
		||||
 | 
			
		||||
Commands are just JavaScript objects. The logic goes into the `action` function.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// src/cliExample/main.ts
 | 
			
		||||
 | 
			
		||||
import { Command, CliError } from "../lib/ccCLI/index";
 | 
			
		||||
import { Ok, Result } from "../lib/thirdparty/ts-result-es";
 | 
			
		||||
 | 
			
		||||
const addCommand: Command<AppContext> = {
 | 
			
		||||
  name: "add",
 | 
			
		||||
  description: "Adds two numbers together",
 | 
			
		||||
  args: [
 | 
			
		||||
    { name: "a", description: "The first number", required: true },
 | 
			
		||||
    { name: "b", description: "The second number", required: true },
 | 
			
		||||
  ],
 | 
			
		||||
  action: ({ args, context }): Result<void, CliError> => {
 | 
			
		||||
    context.log(`Executing 'add' command in '${context.appName}'`);
 | 
			
		||||
 | 
			
		||||
    const a = tonumber(args.a as string);
 | 
			
		||||
    const b = tonumber(args.b as string);
 | 
			
		||||
 | 
			
		||||
    if (a === undefined || b === undefined) {
 | 
			
		||||
      print("Error: Arguments must be numbers.");
 | 
			
		||||
      return Ok.EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const result = a + b;
 | 
			
		||||
    print(`${a} + ${b} = ${result}`);
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3. Create Nested Commands
 | 
			
		||||
 | 
			
		||||
You can group commands under a parent command using the `subcommands` property.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// src/cliExample/main.ts
 | 
			
		||||
 | 
			
		||||
// (addCommand is defined above, subtractCommand would be similar)
 | 
			
		||||
 | 
			
		||||
const mathCommand: Command<AppContext> = {
 | 
			
		||||
  name: "math",
 | 
			
		||||
  description: "Mathematical operations",
 | 
			
		||||
  subcommands: new Map([
 | 
			
		||||
    ["add", addCommand],
 | 
			
		||||
    ["subtract", subtractCommand], // Assuming subtractCommand is defined
 | 
			
		||||
  ]),
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
If a command with subcommands is called without an action, it will automatically display its help page.
 | 
			
		||||
 | 
			
		||||
### 4. Define the Root Command
 | 
			
		||||
 | 
			
		||||
The root command is the entry point for your entire application. It contains all top-level commands and global options.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// src/cliExample/main.ts
 | 
			
		||||
 | 
			
		||||
const rootCommand: Command<AppContext> = {
 | 
			
		||||
  name: "calculator",
 | 
			
		||||
  description: "A feature-rich calculator program",
 | 
			
		||||
  options: new Map([
 | 
			
		||||
    [
 | 
			
		||||
      "debug",
 | 
			
		||||
      {
 | 
			
		||||
        name: "debug",
 | 
			
		||||
        shortName: "d",
 | 
			
		||||
        description: "Enable debug mode",
 | 
			
		||||
        defaultValue: false,
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  ]),
 | 
			
		||||
  subcommands: new Map([
 | 
			
		||||
    ["math", mathCommand],
 | 
			
		||||
    // other commands...
 | 
			
		||||
  ]),
 | 
			
		||||
  action: ({ context }) => {
 | 
			
		||||
    print(`Welcome to ${context.appName}!`);
 | 
			
		||||
    print("Use --help to see available commands");
 | 
			
		||||
    return Ok.EMPTY;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 5. Create and Run the CLI
 | 
			
		||||
 | 
			
		||||
Finally, create the context instance and pass it along with the root command to `createCli`. This returns a handler function that you can call with the program's arguments.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
// src/cliExample/main.ts
 | 
			
		||||
 | 
			
		||||
import { createCli } from "../lib/ccCLI/index";
 | 
			
		||||
 | 
			
		||||
// Create global context instance
 | 
			
		||||
const appContext: AppContext = {
 | 
			
		||||
  appName: "MyAwesome Calculator",
 | 
			
		||||
  debugMode: false,
 | 
			
		||||
  log: (message) => {
 | 
			
		||||
    if (appContext.debugMode) {
 | 
			
		||||
      print(`[LOG] ${message}`);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Create the CLI handler
 | 
			
		||||
const cli = createCli(rootCommand, { globalContext: appContext });
 | 
			
		||||
 | 
			
		||||
// Get arguments and run
 | 
			
		||||
const args = [...$vararg];
 | 
			
		||||
cli(args);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Usage
 | 
			
		||||
 | 
			
		||||
You can now run your CLI from the ComputerCraft terminal:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
> lua program.lua math add 5 7
 | 
			
		||||
12
 | 
			
		||||
 | 
			
		||||
> lua program.lua --debug math add 5 7
 | 
			
		||||
[LOG] Executing 'add' command in 'MyAwesome Calculator'
 | 
			
		||||
12
 | 
			
		||||
 | 
			
		||||
> lua program.lua math --help
 | 
			
		||||
# Displays help for the 'math' command
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Advanced Topics
 | 
			
		||||
 | 
			
		||||
### Arguments
 | 
			
		||||
 | 
			
		||||
Arguments are positional values passed after a command. They are defined in an array.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
args: [
 | 
			
		||||
  { name: "a", description: "The first number", required: true },
 | 
			
		||||
  { name: "b", description: "The second number" }, // optional
 | 
			
		||||
],
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Options
 | 
			
		||||
 | 
			
		||||
Options are named values (flags) that can appear anywhere. They are defined in a `Map`.
 | 
			
		||||
 | 
			
		||||
```typescript
 | 
			
		||||
options: new Map([
 | 
			
		||||
  [
 | 
			
		||||
    "name", // The key in the map must match the option's name
 | 
			
		||||
    {
 | 
			
		||||
      name: "name",
 | 
			
		||||
      shortName: "n",
 | 
			
		||||
      description: "The name to greet",
 | 
			
		||||
      defaultValue: "World",
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    "force",
 | 
			
		||||
    {
 | 
			
		||||
      name: "force",
 | 
			
		||||
      description: "Force the operation",
 | 
			
		||||
      defaultValue: false, // For boolean flags
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
]),
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
They can be used like this:
 | 
			
		||||
*   `--name "John"` or `-n "John"`
 | 
			
		||||
*   `--name="John"`
 | 
			
		||||
*   `--force` (sets the value to `true`)
 | 
			
		||||
 | 
			
		||||
### Error Handling
 | 
			
		||||
 | 
			
		||||
The `action` function must return a `Result<void, CliError>`.
 | 
			
		||||
*   Return `Ok.EMPTY` on success.
 | 
			
		||||
*   The framework automatically handles parsing errors like missing arguments or unknown commands. You can return your own errors from within an action if needed, though this is less common. The primary mechanism is simply printing an error message and returning `Ok.EMPTY`.
 | 
			
		||||
 | 
			
		||||
## API Reference
 | 
			
		||||
 | 
			
		||||
The public API is exposed through `src/lib/ccCLI/index.ts`.
 | 
			
		||||
 | 
			
		||||
### Core Function
 | 
			
		||||
 | 
			
		||||
*   `createCli<TContext>(rootCommand, options)`: Creates the main CLI handler function.
 | 
			
		||||
    *   `rootCommand`: The top-level command of your application.
 | 
			
		||||
    *   `options.globalContext`: The context object to be injected into all actions.
 | 
			
		||||
    *   `options.writer`: An optional function to handle output (defaults to `textutils.pagedPrint`).
 | 
			
		||||
 | 
			
		||||
### Core Types
 | 
			
		||||
 | 
			
		||||
*   `Command<TContext>`
 | 
			
		||||
*   `Argument`
 | 
			
		||||
*   `Option`
 | 
			
		||||
*   `ActionContext<TContext>`
 | 
			
		||||
*   `CliError`
 | 
			
		||||
 | 
			
		||||
This documentation provides a comprehensive overview of the `ccCLI` framework. By following the tutorial and referencing the examples, you can build powerful and well-structured command-line tools for CC:Tweaked.
 | 
			
		||||
		Reference in New Issue
	
	Block a user