<ContentPage eyebrow="Tutorial" title="Accept commands" description="Read arguments, filter tasks, and explain how to use the tool.">

<TutorialNavigation slug="tutorial-commands" />

Start with chapter 3's task structs and formatting function. The next version will show open tasks by default, include completed tasks with `list --all`, and explain itself with `--help`.

## Read the arguments

Add this import at the top of `tasks.tea`:

```tea
use args from "std.args"
```

`use` imports a module under a local name. `std.args` is part of Tea's standard library; you don't need to install a package. Call its functions through the alias, such as `args.all()`.

Move the program's executable statements into `def run() -> Void ... end` and call `run()` at the bottom of the file. `Void` means the function doesn't return a useful value. Keep the struct and `format_task` above it.

At the beginning of `run`, read the arguments and supply a default command:

```tea
var argv = args.all()
if @len(argv) == 0
  argv = ["list"]
end
```

Running `tea tasks.tea list --all` gives this program `["list", "--all"]`. The program name isn't included. Checking the length before using an index avoids reading a missing element.

## Separate selecting from displaying

Add the `show_tasks` function from the complete checkpoint below after `format_task`. Move the sample list into the `list` branch of `run`.

The display function takes a `List[Task]` and a `Bool`. Its condition is `show_all || !task.done`: show a task if all tasks were requested, or if this task is not done. `||` means “or” and `!` negates a boolean.

Increment the task number on every iteration, including tasks you hide. That makes a displayed number keep referring to the same task when the view changes. The separate `shown` counter lets us distinguish an empty view from an empty collection.

## Validate the command

The complete `run` function handles `--help` and `list`. It rejects extra or unrecognized arguments, rather than quietly ignoring them. `&&` means “and”; conditions joined by `&&` and `||` short-circuit, so a length check can guard access to `argv[1]`.

The `help` function writes usage to standard output. The `usage` function writes an error to standard error with `@eprintln`, then calls `@exit(2)`. We'll reserve status `2` for usage errors and `1` for data or file failures. Finishing normally means success, status `0`.

## Run both views

```bash
tea tasks.tea list
```

```text
1. [ ] Learn Tea
2. [ ] Build a CLI
```

```bash
tea tasks.tea list --all
```

```text
1. [ ] Learn Tea
2. [ ] Build a CLI
3. [x] Take a break
```

Running `tea tasks.tea` gives the first view too. Try `tea tasks.tea -- --help` to see the supported command shapes. The first `--` separates Tea's own options from your script's arguments; it is not included in `args.all()`. Use it when the first argument for your script starts with a hyphen. `tea tasks.tea list --unknown` reports an error rather than running a different command.

<NoteCard title="Script exit status">
  The current Tea launcher reports a failed script as status 1 and adds a
  “program exited with status” message, even when the script calls{" "}
  <code>@exit(2)</code>. The native executable you'll build in chapter 9
  preserves the program's status codes.
</NoteCard>

## Complete checkpoint

<details>
<summary>Complete tasks.tea for this chapter</summary>

**`tasks.tea`**

```tea checkpoint="examples/tutorial/tasks/04-commands/tasks.tea"
use args from "std.args"

struct Task {
  title: String
  done: Bool
}

def format_task(task: Task, number: Int) -> String
  const marker = if(task.done) "[x]" else "[ ]"
  `${number}. ${marker} ${task.title}`
end

def show_tasks(tasks: List[Task], show_all: Bool) -> Void
  if @len(tasks) == 0
    @println("No tasks yet.")
    return
  end

  var number = 1
  var shown = 0

  for task in tasks
    if show_all ||!task.done
      @println(format_task(task, number))
      shown = shown + 1
    end

    number = number + 1
  end

  if shown == 0
    @println("No open tasks.")
  end
end

def help() -> Void
  @println("Usage: tasks [list [--all] | --help]")
  @println("Tasks are numbered from 1. Listing shows open tasks; --all includes completed tasks.")
end

def usage() -> Void
  @eprintln("tasks: invalid arguments; run tasks --help for usage")
  @exit(2)
end

def run() -> Void
  var argv = args.all()

  if @len(argv) == 0
    argv = ["list"]
  end

  if argv[0] == "--help" && @len(argv) == 1
    help()
    return
  end

  if argv[0] == "list"
    if @len(argv) > 2 ||(@len(argv) == 2 && argv[1] != "--all")
      usage()
      return
    end

    const tasks = [
      Task(title: "Learn Tea", done: false),
      Task(title: "Build a CLI", done: false),
      Task(title: "Take a break", done: true)
    ]
    show_tasks(tasks, @len(argv) == 2)
    return
  end

  usage()
end

run()
```

</details>

## Try it yourself

Add a `count` command that prints the number of sample tasks. Keep it separate from the display function.

<details>
<summary>Hint</summary>

Check the first argument and the total argument count before running the command. Reuse `@len(tasks)` from chapter 2. For this exercise you can move the sample list above the command branches; the main walkthrough keeps it in the `list` branch until the next chapter replaces it with file loading.

</details>

You now have a small command-line interface. The sample data still resets each time the program runs; next we'll load it from a file.

Read more: [command-line argument helpers](/reference/args), [imports](/docs/modules).

<TutorialNavigation slug="tutorial-commands" position="bottom" />

</ContentPage>
