---
title: "commands"
description: "Declare client commands from a script: arguments, replies, completion, lifetime."
---

> Documentation Index
> Fetch the complete documentation index at: https://aesthetic-docs.pages.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# commands

Client commands typed in chat behind the client prefix (`.` by default). A scripted command
behaves like a built-in one: it appears in `.help`, completes in the chat box and shows its
usage hint while you type.

```lua
commands.register({
name = "ping",
description = "Prints your latency",
execute = function(ctx)
    ctx:info(server.ping() .. " ms")
end,
})
```

| Function | Does |
|----------|------|
| `commands.register(desc)` | Registers a command and returns a handle |
| `commands.prefix()` | The prefix in effect, e.g. `"."` |
| `commands.run(line)` | Runs a client command as if typed, with or without the prefix |

Commands aimed at the **server** go through [`chat.command`](/chat) instead.

## The descriptor

| Field | Meaning |
|-------|---------|
| `name` | Command name, no prefix and no spaces. Required |
| `execute` | `function(ctx, args)`. Required |
| `aliases` | Extra names it answers to |
| `description` | The line `.help` lists it with. Defaults to the script file name |
| `usage` | Detail lines shown by `.help <name>` |
| `params` | Argument hints shown in the chat box while typing |
| `hidden` | Keeps it out of `.help` and completion |
| `complete` | `function(ctx, args) -> string[]` |

Registering a name or alias another command already owns raises an error, so a script cannot
shadow `.help` or another script's command.

## Arguments

`args` holds the words after the command name, with quoted sections kept together
(`.warp "spawn area"` is one argument). Everything arrives as a string; use `tonumber` when
you need a number.

```lua
commands.register({
name = "warp",
params = {
    { name = "name" },
    { name = "distance", type = "number", optional = true },
},
execute = function(ctx, args)
    if #args == 0 then
        ctx:error("usage: " .. ctx.prefix .. "warp <name>")
        return
    end
    ctx:success(("warping to %s"):format(args[1]))
end,
})
```

`params` only shapes the hint the chat box shows (`<name: string> [distance: number]`).
`type` is a label, not a parser, so validate values yourself.

## Replying

`ctx` carries the prefix in effect, the alias the player typed (`ctx.label`) and the raw line
after the prefix (`ctx.raw`), plus four output channels:

| Call | Prints |
|------|--------|
| `ctx:respond(msg)` | A [text component](/text) or string, as an indented line |
| `ctx:info(text)` | Grey `aesthetic »` line |
| `ctx:success(text)` | Green `aesthetic »` line |
| `ctx:error(text)` | Red `aesthetic »` line |

## Completion

`complete` runs on every keystroke with the arguments typed so far. The last one is the word
being completed, empty right after a space, so `#args` is its position. Return every
candidate and the client filters by what is already typed.

```lua
commands.register({
name = "tp",
params = { { name = "player" } },
execute = function(ctx, args)
    chat.command("tp " .. (args[1] or ""))
end,
complete = function(ctx, args)
    if #args ~= 1 then return {} end
    local names = {}
    for _, entry in ipairs(server.tablist.entries() or {}) do
        names[#names + 1] = entry.name
    end
    return names
end,
})
```

## Lifetime

A command belongs to the script that registered it: saving the file re-registers it, deleting
the file removes it. To drop one earlier, keep the handle:

```lua
local cmd = commands.register({
name = "debug",
hidden = true,
execute = function(ctx) ctx:info("still here") end,
})

cmd:unregister()
```

`cmd:usage()` returns the usage line with the current prefix, useful in a bad-argument reply.

Errors from `execute` land in chat with the `lua »` prefix like any script error. An error
inside `complete` is reported once, then that completer goes quiet.

Source: https://aesthetic-docs.pages.dev/commands/index.md
