Skip to content
eshanclioPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Karahi

Karahi is a command runner for your projects. Define commands in a simple JSON file and run them from the terminal — either directly via CLI flags or through an interactive TUI with fuzzy search, parameter forms, and styled output.

Installation

Prerequisites: Go 1.25+

# Clone and build
git clone https://github.com/eshanclio/karahi.git
cd karahi
go build -o karahi .

# Move to a directory on your PATH
mv karahi /usr/local/bin/

Or run directly without installing:

go run .

Quick Start

  1. Create a karahi.json in your project root:
{
  "commands": [
    {
      "name": "greet",
      "description": "Greet someone by name",
      "params": [
        {
          "name": "name",
          "description": "The person to greet",
          "type": "input",
          "validation": { "required": true }
        }
      ],
      "exec": [
        "echo 'Hello, {{name}}!'"
      ]
    }
  ]
}
  1. Run it:
# CLI mode
karahi greet --name Alice

# TUI mode
karahi

Usage

CLI Mode

Run any command directly with flags:

karahi <command> [--param value ...]

Examples:

karahi greet --name Alice
karahi deploy --environment staging --version v1.2.0
karahi status

Flags accept both --param value and --param=value syntax.

Help

karahi help              # List all available commands
karahi help <command>    # Show details for a specific command

The help output shows each command's description, parameters (with types and whether they're required), and the exec steps that will run.

TUI Mode

Launch the interactive terminal UI:

karahi       # TUI mode (default)
karahi tui   # TUI mode (explicit)

Key Bindings

Key Action
/ Open the command list
enter Select a command / submit the form
esc Go back / cancel
? Show help
q Quit (when not typing)
ctrl+c Force quit
up / k Scroll up
down / j Scroll down
pgup / pgdown Page up / down
ctrl+u / ctrl+d Half page up / down
home / end Scroll to top / bottom

Navigation Flow

  1. Idle — press / to open the command list.
  2. Command Select — type to filter commands with fuzzy search, then press enter to select one.
  3. Form Input — fill in the command's parameters (skipped if the command has no params).
  4. Executing — the command runs with a spinner and streamed output.

Press esc at any point to go back to the idle state.

Configuration

Commands are defined in karahi.json. Karahi checks two locations:

Location Path Priority
Project ./karahi.json Higher (overrides global)
Global ~/.karahi/karahi.json Lower (fallback)

If both files define a command with the same name, the project version is used. Global commands that don't conflict are still available.

Command Schema

{
  "commands": [
    {
      "name": "deploy",
      "description": "Deploy to an environment",
      "params": [
        {
          "name": "environment",
          "description": "Target environment",
          "type": "choice",
          "options": ["staging", "production"],
          "validation": { "required": true }
        },
        {
          "name": "version",
          "description": "Version tag",
          "type": "input",
          "validation": {
            "required": true,
            "min_length": 2,
            "max_length": 50,
            "pattern": "^v\\d+\\.\\d+\\.\\d+$"
          }
        }
      ],
      "exec": [
        "echo 'Deploying {{version}} to {{environment}}...'",
        "echo 'Done!'"
      ]
    }
  ]
}

Fields

Field Type Required Description
name string Yes The command name (used as the CLI subcommand).
description string Yes A short description shown in help and the TUI list.
params array No Parameters the command accepts.
exec array Yes Shell commands to run sequentially via sh -c.

Parameter Fields

Field Type Required Description
name string Yes Parameter name (used as --name in CLI).
description string Yes Shown in help text and TUI form labels.
type string Yes "input" (free text) or "choice" (select from options).
options array For choice The allowed values when type is "choice".
validation object No Validation rules for this parameter.

Validation Rules

All validation fields are optional. When multiple rules are set, they are all evaluated in sequence.

Rule Type Description
required bool Value must be non-empty.
min_length int Minimum character count.
max_length int Maximum character count.
pattern string A regex the value must match.
one_of array Value must be one of the listed strings.

Parameter Substitution

Use {{param_name}} in your exec strings to reference parameter values:

{
  "exec": ["echo 'Deploying {{version}} to {{environment}}'"]
}

Commands Without Parameters

Commands don't need parameters. Omit the params field entirely:

{
  "name": "status",
  "description": "Show project status",
  "exec": ["echo 'All good'", "date"]
}

Project Structure

main.go                     Entry point: loads config, routes to CLI or TUI
karahi.json                 Example project-level command definitions
internal/
  domain/
    config.go               Config loading and merging (project + global)
    config_test.go          Tests for config loading, merging, FindCommand
    command.go              Data types: Command, Param, ValidationRule
  cli/
    cli.go                  CLI runner: flag parsing, validation, execution
    cli_test.go             Tests for flag parsing, validation, help, Run
  executor/
    executor.go             Shell execution and parameter substitution
    executor_test.go        Tests for Substitute, Run, RunStructured, RunCapture
  tui/
    model.go                Bubble Tea model and state machine
    commands.go             Filterable command list (bubbles/list)
    form.go                 Dynamic form builder (huh)
    output.go               Output viewport with spinner
    keys.go                 Key bindings
    tui_test.go             Tests for helpText, collectValues, commandItem, commandList
  validation/
    validator.go            Composable validator registry
    rules.go                Built-in validation rules
    rules_test.go           Tests for all rule factories and BuildValidators

Development

Prerequisites

Building

go build .        # Produces ./karahi

Running

go run .          # Run directly
./karahi          # Run the built binary

Testing

Run the full test suite:

go test ./...

Run tests for a specific package:

go test ./internal/cli
go test ./internal/executor
go test ./internal/domain
go test ./internal/validation
go test ./internal/tui

Run a single test by name:

go test ./internal/cli -run TestParseFlags
go test ./internal/validation -run TestBuildValidators

Run tests with verbose output:

go test -v ./...

Test Coverage

All non-TUI-interactive packages have tests:

Package What's Tested
internal/domain Config file loading, JSON parsing, command merging (project overrides global), FindCommand lookup, LoadConfig integration
internal/cli Flag parsing (--key value and --key=value), parameter validation, help / help <command> output, end-to-end Run
internal/executor Substitute placeholder replacement, RunStructured / Run / RunCapture execution, error propagation and stop-on-first-error behavior
internal/validation Each rule factory (required, minLength, maxLength, pattern, oneOf), BuildValidators composition, nil-rule passthrough, UTF-8 rune counting
internal/tui helpText rendering, collectValues pointer dereferencing, commandItem interface methods, commandList construction (including synthetic help entry), show/hide visibility

Conventions

When writing new tests, follow these conventions:

  • Use t.Parallel() where possible. Avoid it only when test state is process-global (e.g., os.Chdir).
  • Use t.TempDir() for temporary directories.
  • Use table-driven tests for functions with many input/output cases.
  • Use subtests (t.Run) to group related assertions.

Dependencies

Package Role
bubbletea v2 TUI framework (Elm architecture)
bubbles v2 Reusable TUI components (list, viewport, spinner)
huh v2 Interactive form builder
lipgloss v2 Terminal styling

Code Conventions

  • Imports: group stdlib, then external, then internal packages.
  • Naming: PascalCase exported, camelCase unexported.
  • Errors: return explicitly, wrap with fmt.Errorf("...: %w", err).
  • JSON tags: snake_case with omitempty where appropriate.
  • Pure Go — no CGO, no C dependencies.

Architecture Overview

Karahi follows a clean separation between its three main layers:

  • Domain (internal/domain) holds all shared data types (Command, Param, ValidationRule, ResolvedCommand) and config loading logic. It has no dependencies on the CLI or TUI layers.

  • Executor (internal/executor) handles shell execution. It substitutes {{param}} placeholders and runs each exec step sequentially via sh -c, stopping on the first error. Three execution modes are available: Run (streamed output to a writer), RunStructured (structured results for the TUI), and RunCapture (captures output as a string while also writing to a writer).

  • Validation (internal/validation) uses a registry pattern. Each rule is a RuleFactory that returns a ValidateFunc. BuildValidators composes all applicable rules into a single validation function. New rules can be added via Register().

  • CLI (internal/cli) parses flags, validates parameters, and runs commands via the executor. It also handles the help subcommand.

  • TUI (internal/tui) implements the interactive UI using Bubble Tea's model/update/view pattern with a four-state machine: idle, command select, form input, and executing.

Extending Karahi

Adding a New Validation Rule

  1. Create a factory function in internal/validation/rules.go.
  2. Register it with Register() in the init() function.
  3. Add the corresponding field to ValidationRule in internal/domain/command.go.

Adding a New Parameter Type

  1. Add the type string to ParamType in internal/domain/command.go.
  2. Handle it in internal/tui/form.go (buildForm).
  3. Handle it in internal/cli/cli.go (parseFlags) if CLI behavior differs.

Adding a New TUI State

  1. Add the state constant in internal/tui/model.go.
  2. Add a key handler function (handleXxxKey).
  3. Wire it into Update and View.
  4. Update recalcLayout if it needs custom sizing.

License

See LICENSE for details.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages