Introduction

curo: /ˈkuː.roː/, [ˈkuːroː]

  1. to arrange, see to, attend to, take care of, look after, ensure, tend to
  2. to heal, cure
  3. to govern, command, preside over

curo is a command-line tool for managing development environments, both on local machines as well as inside CI/CD systems. It provides a set of standard build, test, and management commands ("verbs"), such as build, test, and deploy, so you don’t need to invent your own script names or targets. Think of curo as similar to tools like Make, Just and Please, but with hints of what build tools like Bazel, Pants and all have also created through the years.

The main takeaway of curo is that it does not force a particular toolchain for the language(s) you are developing in. You set up the environment, but once you have, curo is there to make sense of the separate parts.

Why would I use curo?

There are many tools that exist to run commands and structure codebases. The philosophy behind curo is to standardize the language used when building code projectsand to reduce complexity in managing different environments.

Key Benefits

  • Consistent Commands: Everyone on your team runs curo build, curo test, etc.—no more “what do I run here?”.
  • Portable Configuration: All logic lives in a single curo.toml file, alongside your code. And you see what the actual commands are.
  • No Language Lock-in: You aren’t tied to any specific toolchain; you define how commands run.
  • Smooth for CI/CD: The same commands work in dev and CI without extra glue scripts.
# Build all components
curo build

# Run unit tests
curo test

# Deploy your project
curo deploy

Project configuration (in curo.toml):

[dev]
build = "go build -o bin/myapp ."
test = "go test ./..."

Installation

Run the following command in your terminal:

curl -sfL "https://gitlab.com/scottlindeman/public/-/raw/main/01_projects/curo/install" | bash

This will install the latest curo version. If you want a specific version, use the following bash syntax

bash -s -- -v "0.93.0"

Additionally, the script will install curo into /usr/local/bin. If another directory is preferable, use the following bash syntax

bash -s -- -i "~/bin"

Example:

curl -sfL "https://gitlab.com/scottlindeman/public/-/raw/main/01_projects/curo/install" | bash -s -- -v "0.93.0" -i "/usr/local/bin"

This will install the curo binary to /usr/local/bin.

Usage

Once installed, curo provides a simple, unified CLI for common development workflow tasks. Typical commands are:

curo build         # Build your project or component(s)
curo test          # Run tests
curo deploy        # Deploy built artifacts

You can always see the available commands by running:

curo --help

Common Workflow

  1. Install project dependencies

    curo install
    
  2. Build the project

    curo build
    
  3. Run tests

    curo test
    
  4. Start your project

    curo start
    

Command Structure

Most commands follow this structure:

curo [options] <command> [command-options] [component ...]
  • options: Use curo --help to see all available flags (e.g. --env, --all, --exclude, etc.).
  • <command>: One of the standard verbs such as build, test, lint, etc.
  • command-options: Options for the specific command, such as passing arguments or chaining commands
  • [component ...]: Optionally target specific components.

Examples

Build a specific component:

curo build cli

Run in CI/CD environment:

curo build --env cicd

Chaining Commands

You can chain commands:

curo format --and lint --and build --and test

Supplying Extra Arguments

Some commands accept extra arguments:

curo lint -D "--lang py" python_project

Configuration

curo uses a curo.toml file at the root of your repository to define your project settings and the commands available for each environment.

Below is an example curo.toml that covers common scenarios:

curo_toml_version = "0.60.0"

[about]
name = "cli"
version = "{{ inherit }}"
description = "A command line tool for building projects."
code_dir = "."

[dev]
build = [
  "mkdir -p build-output",
  "go build -v -o build-output/testcuro"
]
deploy = "cp build-output/testcuro $REPO_BIN/testcuro"
generate = [ "argbash -i .curo/actions/cicd.build.sh" ]

[dev.test]
unit = "go test -v -run TestUnit curo curo/common curo/commands/actions curo/commands/general curo/tui curo/core"
integration = "go test -v -run TestIntegration curo curo/common curo/commands/actions curo/commands/general curo/tui curo/core"
functional = "poetry run py.test -vvvv tests/functional {{ args }}"

Sections

  • curo_toml_version: Specifies the version of the configuration format.
  • [about]: Basic information about the project.
    • name: Project name.
    • version: Project version (use "{{ inherit }}" to match the main version).
    • description: Short description.
    • code_dir: Root code directory.
  • [dev]: Commands for the development environment.
    • build: List or string of shell commands to build your project.
    • deploy: How to deploy built artifacts locally.
    • generate: Example of code generation or scripts.
  • [dev.test]: Defines test categories.
    • unit: Unit test command(s).
    • integration: Integration test command(s).
    • functional: Functional test command(s).

Editing & Extending

  • Add or update commands for other project verbs supported by curo, such as lint, format, etc.
  • You can use arrays for multi-step shell scripts or strings for single-step commands.
  • Environment variables and command-line arguments are supported (see Advanced Usage).

Tip: Don’t forget to run curo init > curo.toml to generate a starter file! You can also give your curo.toml files prefixes, such as myproject.curo.toml


Example

Suppose your project has this structure:

src
├── backend
│   ├── database
│   └── server
└── frontend
    ├── lib
    └── ui

Each bottom-level component (ui, lib, server, database) can be started on its own.

1. Initialize the Project

From your repo root, run:

curo init > curo.toml

This generates a curo.toml` in your root directory.

2. Top-level curo.toml

Open the root curo.toml. For this layout:

curo_toml_version = "0.93.0"

[about]
name = "node-monorepo"
description = "Full-stack monorepo with Node.js frontend/backend and PostgreSQL database"
code_dir = "src"

[dev]
format = "prettier --write ."
lint = "eslint ."

components = ["frontend", "backend"]
  • code_dir = "src" tells curo all code lives in src/.
  • components lists first-level folders in src/.

3. src/frontend/curo.toml

Create src/frontend/curo.toml with curo init:

curo_toml_version = "0.93.0"

[about]
name = "frontend"
code_dir = "."

components = ["ui", "lib"]

4. src/backend/curo.toml

In src/backend/curo.toml:

curo_toml_version = "0.93.0"

[about]
name = "backend"
code_dir = "."

components = ["server", "database"]

5. Bottom-level curo.tomls

For src/frontend/ui/curo.toml:

curo_toml_version = "0.93.0"

[about]
name = "ui"
code_dir = "."

[dev]
install = "npm install"
start = "npm run start"

For src/frontend/lib/curo.toml:

curo_toml_version = "0.93.0"

[about]
name = "lib"
code_dir = "."

[dev]
install = "npm install"
build = "npm run build"

For src/backend/server/curo.toml:

curo_toml_version = "0.93.0"

[about]
name = "server"
code_dir = "."

[dev]
install = "npm install"
start = "npm run start"

For src/backend/database/curo.toml:

curo_toml_version = "0.93.0"

[about]
name = "database"
code_dir = "."

[dev]
start = "docker-compose up db"

6. Example Usage

  • Install everything:
    curo install
    
  • Format all code:
    curo format
    
  • Lint all code:
    curo lint
    
  • Start just the server:
    curo start backend server
    
  • Start the UI:
    curo start frontend ui
    
  • Start only the database:
    curo start backend database
    
  • Start everything:
    curo start
    

Advanced Usage

Curo provides powerful options and flags to help you target, customize, and compose complex workflows:

Commands

Passing Arguments to Commands (-D)

Use -D (--args) to pass additional arguments to a component’s command. Note: Can only be used with one component at a time.

curo lint -D "--fix" frontend ui

This passes --fix to the lint command when running on the frontend ui component.

Running Top-Level (Global) Commands for a Component (-g, --global)

Use -g/--global to apply the top-level definition of a command to a specific component (even if that component has its own command).

curo --global lint frontend ui

This forces the top-level lint command (from the repo root) to run for frontend ui, instead of any lint command defined in that component’s own curo.toml.

Running Local Commands from a Component Directory (-l, --local)

If you’re inside a component’s folder, use -l to run its commands without typing the full path.

cd src/frontend/ui
curo -l start

This will run the start command for the frontend ui component.

Selecting the Environment (-e, --env)

You can specify which environment’s commands to run. The default is dev

curo -e cicd deploy

Runs the deploy command for the cicd environment (e.g., for CI/CD purposes).

Running Specific Test Types

If you have multiple test commands, you can specify which to run.

curo test unit backend server
curo test functional frontend ui

This will only run the specified type of tests (e.g., unit, functional) for that component. Without specifying the level of testing, the default is to run all defined levels.

Excluding Components (-x, --exclude)

Skip specific components or subcomponents.

curo build -x "frontend lib"

This will build everything except frontend lib.

Running a Command for All Components (-a, --all)

By default, top-level commands shadow component commands. Use -a to run the specified command for every component that defines it.

curo -a install

Runs install for every component with its own install command (not just the top-level).

General

You can use curo find [component] to help navigate through your project. The find command will produce an absolute path to the referenced component.

cd $(curo find frontend lib)
cd $(curo find /) # To go back to the project root

Info

You can view your project structure by running curo info. Add, -v or more vs to see more information.

curo info

public
├── shdx
│   ├── shdx
│   └── installer
├── pydx
├── shaddo
│   ├── cli
│   └── installer
├── curo
│   ├── cli
│   ├── installer
│   └── docs
├── nenya
├── ansible-runner
├── browser-plugins
│   ├── farc
│   ├── confluence
│   ├── gitlub
│   └── productivity
└── cicd-runtime
curo -vv info

public [A collection of open source tools and libraries.]
├── dev
│   ├── install
│   ├── format
│   ├── lint
│   └── generate
├── cicd
│   ├── install
├── shdx
│   ├── shdx
│   │   ├── dev
│   │   │   ├── build
│   │   │   └── generate
│   │   └── cicd
│   │       ├── build
│   │       └── publish
│   └── installer [0.87.0 - A convenient installer to pull a release version …]
│       ├── dev
│       │   ├── build
│       │   ├── generate
│       │   └── run
│       └── cicd
│           ├── build
│           └── publish
├── pydx [A collection of libraries for python that are hel…]
│   └── dev
│       └── test
│           └── unit
├── shaddo [0.5.2 - A cli tool to bundle shell and bash scripts into …]
│   ├── cli [0.5.2 - The actual cli tool]
│   │   ├── dev
│   │   │   ├── build
│   │   │   ├── generate
│   │   │   ├── test
│   │   │   │   └── functional
│   │   │   └── deploy
│   │   └── cicd
│   │       ├── build
│   │       └── publish
│   └── installer [0.5.2 - A convenient installer to pull a release bin of s…]
│       ├── dev
│       │   ├── build
│       │   ├── generate
│       │   └── run
│       └── cicd
│           ├── build
│           └── publish
├── curo [1.0.0 - A command line tool for building projects.]
│   ├── cli [1.0.0 - A command line tool for building projects.]
│   │   ├── dev
│   │   │   ├── build
│   │   │   ├── generate
│   │   │   ├── test
│   │   │   │   ├── unit
│   │   │   │   ├── integration
│   │   │   │   └── functional
│   │   │   └── deploy
│   │   └── cicd
│   │       ├── build
│   │       └── publish
│   ├── installer [1.0.0 - A convenient installer to pull a release bin of c…]
│   │   ├── dev
│   │   │   ├── build
│   │   │   ├── generate
│   │   │   └── run
│   │   └── cicd
│   │       ├── build
│   │       └── publish
│   └── docs [1.0.0 - The documentation website for CURO]
│       ├── dev
│       │   ├── build
│       │   └── run
│       └── cicd
│           ├── build
│           └── deploy
├── nenya [0.44.0 - A js library that enhances mithriljs.]
│   ├── dev
│   │   ├── install
│   │   ├── build
│   │   └── test
│   │       └── unit
│   └── cicd
│       └── build
├── ansible-runner [1.1.0 - A Dockerfile for an image that has a very easy in…]
│   ├── dev
│   │   ├── build
│   │   └── run
│   └── cicd
│       ├── build
│       └── publish
├── browser-plugins [Plugins for browsers, mostly Firefox.]
│   ├── farc [0.2.0 - A browser experience inspired by Arc.]
│   │   ├── dev
│   │   │   ├── build
│   │   │   └── test
│   │   │       └── unit
│   │   └── cicd
│   │       └── build
│   ├── confluence [2.1.0 - Make Confluence look nice.]
│   │   └── cicd
│   │       └── build
│   ├── gitlub [2.1.0 - Style Github and Gitlab readmes and code.]
│   │   └── cicd
│   │       └── build
│   └── productivity [1.2.1 - Removes some unproductive things from websites]
│       └── cicd
│           └── build
└── cicd-runtime [1.2.0 - The image used by the public cicd pipelines for b…]
    ├── dev
    │   ├── build
    │   └── run
    └── cicd
        ├── build
        └── publish

Caveats

While curo aims to provide a simple and flexible command runner for your projects, there are some important caveats and limitations to keep in mind:

One Level of Component Discovery per curo.toml

Each curo.toml only defines components one level deeper. Hierarchies are built by placing separate curo.toml files in subcomponent directories. Curo does not recursively search for components in nested directories unless each has its own config. Curo also has a 3 depth max as well. Root, first component and sub component.

Passing Command Arguments (-D)

  • The -D/--args flag only works if you specify a single component. It cannot be used with multiple components at once.
  • These additional arguments are simply appended after the defined command; ensure your component’s script or tool will handle them as expected.

Global and Local Options

  • The --global flag forces curo to run the top-level command for a specified component, ignoring any local overrides.
  • The --local flag only works when you are inside a component’s directory. It cannot be used from outside the project/component subtree.

Component Command Precedence

By default, if both the top-level and the component define a command (like build), curo runs only the top-level version unless you use the -a/--all flag.

Environment Variable Expansion

Curo executes commands via the shell, so environment variables (like $PATH, $PWD, etc.) behave as they do in your shell. However, if your command depends on specific environment setup, make sure it is available via your shell or set in the command explicitly.

Command Chaining

Chaining commands with -n/--and does not support -D/--args.

Environment Switching (-e/--env)

Be sure that your curo.toml files actually define the requested environment (like [cicd]), otherwise you may see errors or no commands executed when using that environment.

Exclusions and Globs

The -x/--exclude flag can be used multiple times, but must exactly match a component path as curo discovers them. Pattern matching or globs are not currently supported.

Cross-Platform Considerations

Curo runs your specified shell commands as-is. If you are working in a cross-platform team (Windows, macOS, Linux), ensure your commands are portable or conditionally written.

Dependency Assumptions

Curo does not manage or install any system or language dependencies for you in your project. That is for you to set up and understand.