The official Command Line Interface (CLI) for the Astrical framework.
This project serves as the primary entry point for managing Astrical projects, handling tasks such as project initialization, module management, and development workflows. It is designed to be extensible, fast, and developer-friendly, focusing on a clean architecture that allows for easy addition of new commands.
- Purpose
- Architecture & Design
- Getting Started
- Project Structure
- Development Workflow
- Adding New Commands
- Contributing
- License
The Astrical CLI allows developers to:
- Initialize new Astrical projects with best practices built-in.
- Manage project configuration and dependencies.
- Extend the framework functionality through modular commands.
It acts as a unification layer, bringing together various tools and configurations into a cohesive developer experience.
This CLI is built with TypeScript and follows a Class-Based Command Pattern to ensure type safety and maintainability.
- CAC (Command And Conquer): A lightweight, robust framework for building CLIs. It handles argument parsing, help generation, and command registration.
- Consola: Elegant console logging with fallback and structured output capabilities.
- Lilconfig: Configuration loading (searching for
astrical.yml,astrical.yaml) akin tocosmiconfigbut lighter. - Vitest: A blazing fast unit test framework powered by Vite.
CLIClass (src/core/CLI.ts): The orchestrator. It initializes the CAC instance, discovers commands using theCommandLoader, registers them, and handles the execution lifecycle.CommandLoader(src/core/CommandLoader.ts): Responsible for dynamically discovering and importing command files from the filesystem. It supports:- Recursive directory scanning.
- Nested commands (e.g.,
module/add.ts->module add). - Index files as parent commands (e.g.,
module/index.ts->module).
BaseCommand(src/core/BaseCommand.ts): The abstract base class that all commands MUST extend. It provides:- Standardized
init()andrun()lifecycle methods. - Built-in access to global options (like
--root-dir). - Helper methods for logging (
this.log,this.warn,this.error). - Project root detection (
this.projectRoot).
- Standardized
- Zero-Config Defaults: It should work out of the box but allow rich configuration via
astrical.yml. - Extensibility: Adding a command should be as simple as adding a file.
- Testability: Every component is designed to be unit-testable, with dependency injection where appropriate (e.g.,
CommandLoaderimporter).
While currently in development, you can run the CLI from the repository source or link it.
# From within the package directory
npm install
npm run build# Run the built CLI
npx astrical <command> [options]
# Example: Initialize a new project
npx astrical init my-new-project
# Get help
npx astrical help
# Get help for a specific command
npx astrical help init
npx astrical help module addInitializes a new Astrical project by cloning a starter repository, setting up dependencies, and preparing a fresh git history.
Usage:
npx astrical init <directory> [options]Arguments:
directory(Required): The directory to initialize the project in. If the directory does not exist, it will be created. If it does exist, it must be empty.
Options:
--repo <url>(Default:https://github.com/nexical/astrical-starter): The URL of the starter repository to clone.- Supports standard Git URLs (e.g.,
https://github.com/user/repo.git). - Supports GitHub short syntax
gh@owner/repo(e.g.,gh@nexical/astrical-starter).
- Supports standard Git URLs (e.g.,
What it does:
- Clones the specified starter repository (recursively, including submodules) into the target directory.
- Updates all submodules to their latest
mainbranch. - Installs dependencies using
npm install. - Resets Git history:
- Creates an orphan branch (
new-main). - Commits all files as an "Initial commit".
- Deletes the old history (removes
main/master). - Renames the branch to
main. - Removes the
originremote to prevent accidental pushes to the starter repo.
- Creates an orphan branch (
Output:
- A ready-to-use Astrical project in the specified directory, with fresh git history and installed dependencies.
Starts the development server in ephemeral mode. It constructs a temporary build environment in site and runs the Astro dev server with Hot Module Replacement (HMR).
Usage:
npx astrical devWhat it does:
- Prepares the
sitedirectory by mountingsrc/core,src/modules, and content. - Starts the Astro development server (accessible at
http://localhost:4321by default). - Watches for changes in your project and updates the ephemeral build automatically.
Compiles the project for production. It assembles the final site structure in site and generates static assets.
Usage:
npx astrical buildWhat it does:
- Cleans the
sitedirectory to ensure a fresh build. - Copies all necessary source files (
src/core,src/modules,src/content,public) intosite. - Runs
astro buildto generate the production output insite/dist.
Output:
- A production-ready static site in
site/dist.
Previews the locally built production site. This is useful for verifying the output of astrical build before deploying.
Usage:
npx astrical previewPrerequisites:
- You must run
astrical buildfirst.
What it does:
- Starts a local web server serving the static files from
site/dist.
Removes generated build artifacts and temporary directories to ensure a clean state.
Usage:
npx astrical cleanWhat it does:
- Deletes
site,dist, andnode_modules/.vite.
Executes a script within the Astrical environment context. This handles path resolution and environment variable setup for you.
Usage:
npx astrical run <script> [args...]Arguments:
script(Required): The name of the script to run.- Can be a standard
package.jsonscript (e.g.,test). - Can be a module-specific script using
module:scriptsyntax (e.g.,blog:sync).
- Can be a standard
args(Optional): Additional arguments to pass to the script.
Examples:
# Run a core project script
npx astrical run test
# Run a script defined in the 'blog' module's package.json
npx astrical run blog:sync --forceManages the modular architecture of your Astrical project. Allows you to add, remove, update, and list Git-based modules.
Adds a new module as a Git submodule.
Usage:
npx astrical module add <url> [name]Arguments:
url(Required): The Git repository URL of the module.- Supports
gh@owner/reposhorthand.
- Supports
name(Optional): The folder name for the module. Defaults to the repository name.
What it does:
- Adds the repository as a git submodule in
src/modules/<name>. - Installs any new dependencies via
npm install.
Lists all installed modules in the project.
Usage:
npx astrical module listOutput:
- A table showing the name, version, and description of each installed module found in
src/modules.
Updates one or all modules to their latest remote commit.
Usage:
npx astrical module update [name]Arguments:
name(Optional): The specific module to update. If omitted, all modules are updated.
What it does:
- Runs
git submodule update --remote --mergefor the target(s). - Re-installs dependencies to ensure
package-lock.jsonis consistent.
Removes an installed module and cleans up references.
Usage:
npx astrical module remove <name>Arguments:
name(Required): The name of the module to remove.
What it does:
- De-initializes the git submodule.
- Removes the module directory from
src/modules. - Cleans up internal git metadata (
.git/modules). - Updates
npmdependencies.
graph TD
src-->commands
src-->core
src-->utils
commands-->init.ts
core-->CLI.ts
core-->BaseCommand.ts
core-->CommandLoader.ts
utils-->config.ts
utils-->logger.ts
src/commands/: Contains the implementations of individual CLI commands. File names correspond to command names.src/core/: The framework logic (Command loading, Base class, CLI orchestration).src/utils/: Shared utilities (Logging, Configuration parsing).test/unit/: Co-located unit tests. Mirrors thesrcstructure.
- Node.js (v18+ recommended)
- NPM
-
Install Dependencies:
npm install
-
Build in Watch Mode:
npm run dev
This uses
tsupto watch for changes and rebuilddist/.
We prioritize 100% Test Coverage. All logic branches, statements, and lines must be covered.
# Run all unit tests (with coverage report)
npm run testTests are written using vitest and are located in test/unit/. When submitting changes, ensure coverage remains at 100%.
To create a new command, add a TypeScript file to src/commands/.
Example: Create src/commands/hello.ts
import { BaseCommand } from '../core/BaseCommand.js';
export default class HelloCommand extends BaseCommand {
// 1. Define command metadata
static description = 'Say hello to the world';
static args = {
args: [
{ name: 'name', required: false, description: 'User name' }
],
options: [
{ name: '--shout', description: 'Say it loud', default: false }
]
};
// 2. Implement the run method
async run(options: any) {
const name = options.name || 'World';
if (options.shout) {
this.success(`HELLO ${name.toUpperCase()}!`);
} else {
this.log(`Hello ${name}`);
}
}
}Key Requirement: The file MUST default export a class extending BaseCommand.
- File Naming:
hello.ts-> Command:hellousers/create.ts-> Command:users createusers/index.ts-> Command:users(Parent command)
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a feature branch.
- Add your changes and ensure tests pass with 100% coverage.
- Submit a Pull Request.
This project is licensed under the Apache License 2.0.