Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"yaml.schemas": {
"./schemas/fire.schema.json": [
"fire.yaml",
"*.fire.yaml",
]
}
}
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ path = "src/main.rs"
[dependencies]
dirs = "5.0.1"
serde_yaml = "0.9.34"
serde = { version = "1.0", features = ["derive"] }
107 changes: 106 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,108 @@
# Fire CLI

Under construction.
A CLI with dynamic completion powered by external configuration.

## Command Configuration
Fire loads YAML files from the current directory with these patterns:
- `fire.yaml`
- `fire.yml`
- `*.fire.yaml`
- `*.fire.yml`

Files are merged in this order:
1. `fire.yaml` / `fire.yml`
2. `*.fire.yaml` / `*.fire.yml` (lexicographic order)

If the same command name appears more than once, the last loaded definition wins.

Example:

```yaml
commands:
run:
description: Run npm scripts
exec: npm run
commands:
build: npm run build
test:
description: Run tests
run: npm run test

lint: npm run lint
```

`exec` and `run` are both supported and treated as executable command actions.

## `config.fire.yaml` Validation in VS Code
The schema is available at [`schemas/fire.schema.json`](./schemas/fire.schema.json).

You can associate it in two ways:

### Option 1: Global mapping by file name
In `.vscode/settings.json`:

```json
{
"yaml.schemas": {
"./schemas/fire.schema.json": [
"config.fire.yaml",
"*.fire.yml",
"*.fire.yaml"
]
}
}
```

### Option 2: Per-file mapping with `$schema`
In the first line of your YAML file:

```yaml
# yaml-language-server: $schema=./schemas/fire.schema.json
```

With this, VS Code (YAML extension) validates structure, types, and allowed DSL fields.

Note: `cli` is a reserved command name at the top-level `commands` map, so `commands.cli` is rejected by the schema.

Expected validation error (example in VS Code): `Property cli is not allowed.`

Why: `cli` is reserved for internal CLI behavior and cannot be overridden as a user command at the root `commands` level.

## Autocomplete Without External Scripts
Fire supports two completion modes:

- Rich zsh completion (value + description)
- Bash-compatible command completion (`complete -C`)

### zsh
```zsh
source ./zsh_completations
```

### bash
```bash
complete -o nospace -C fire fire
```

For `complete -C`, the shell invokes `fire` in completion mode using `COMP_LINE` / `COMP_POINT`.

Completion output includes command name and `description` when present (`name<TAB>description`) in the `__complete` protocol, which is consumed by the zsh completion script.

Note: `complete -C` only uses completion values; descriptions are a zsh-native feature through `zsh_completations`.

## Execution Rules
- `fire <command>` executes `exec` (or `run`) of the resolved command.
- Nested command resolution is greedy: it matches the deepest valid subcommand path.
- Remaining unmatched tokens are appended as arguments to the final executable command.

Example:
- `fire run start --host 0.0.0.0` with `run.exec: "npm run"` executes:
- `npm run start --host 0.0.0.0`

`cli` is reserved for internal CLI management and cannot be overridden by user commands.

## Run
```bash
cargo build
./target/debug/fire
```
118 changes: 118 additions & 0 deletions config.sample.fire.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# yaml-language-server: $schema=./schemas/fire.schema.json
dir: . # default working directory

env_file: .env # default env file

namespace:
id: 51170376-ea38-4317-ba49-4e63dfc8a571 # optional
description: Pumpkat
alias: pk

runtimes:
deno:
sdk: deno
runner: deno
check: deno --version
fallback_runner: docker run --rm -it denoland/deno:latest /bin/bash
paths:
- scripts/*.ts
- scripts/helpers/*.ts
py:
sdk: python
paths:
- scripts/*.py
- scripts/helpers/*.py

include:
- common.fire.yml
- database.fire.yml

x-arg-config: &arg-config
# Use {n} to represent the argument index: {{n}} -> {1}, {2}, ... ; #{n} -> #1, #2, ...
# The ...<placeholder> and [<placeholder>] forms must stay literal; they expand to arrays (string[]).
# Examples: ${n} -> ...${n} or [${n}]; {{n}} -> ...{{n}} or [{{n}}].
placeholder: "{{n}}" # replace ${n} with the index; you can also use $1, $2, $3, ...
on_unused_args: ignore # default is error; ignore skips, warn logs, error fails

# Example: running `fire hello other args` executes `echo hello world other args...`
commands:
hello: echo hello world

double:
commands:
hello:
description: "say twice"
exec:
- echo hello world
- echo again
world:
description: "say world twice"
exec:
- echo world
- echo world

npm:
dir: . # default (relative to project root)
check: npm -v # if this fails, fallback_runner will be used
fallback_runner: docker run --rm -it node:lts-alpine /bin/bash # fallback
exec: npm

run:
description: |
y si agrego descripción a este?
vamos
exec: npm run --key ${SECRET_KEY:-default_value}

run2:
before: docker compose ps -q front | grep -q . || docker compose up -d front
exec: compose exec front npm run

run3:
runner: docker compose exec front /bin/bash # equivalent to run inside the container
env_file: .env.front # override the env file for this command
exec: # multiple commands run in sequence; the last one receives the args
- npm run build
- npm run start

# fire run5 -> npm run
# fire run5 build -> npm run clean && npm run build
# fire run5 other -> npm run other
run5:
exec: npm run # if it matches subcommands, it will be ignored
commands:
build: npm run clean && npm run build
start: fire build && npm run start

up:
<<: *arg-config
compute:
arg1: deno:makeHash("{1}", "sha3-256")
description: "<name> <job>"
exec: echo "Hello {1}, your job is {2}"

computed:
<<: *arg-config
eval: py:sayHello("{1}", "{2}", ...{{n}})

vars:
compute:
arg1: deno:getServiceNameById("{1}")
macros:
"{{front}}": docker compose exec front
"{{dynamic}}": docker compose exec {{1}}
exec: "{{front}} npm run"
commands:
npm-version: "{{front}} npm -v"
node-version: "{{front}} node -v"
hello: "{{dynamic}} echo Hello"
hey:
exec: npm run # if it matches subcommands, it will be ignored
commands:
build: npm run clean && npm run build
hey:
description: "say hey!!!"
exec: npm run
start: fire build && npm run start

web:
delegate: npm-run # built-in delegate that uses package.json scripts for autocompletion
Loading