# Add a Command

> Add posts:list to the blog App with act make:command, use the post store from the container, and return a result for people and agents.

This chapter adds a Command that lists the blog's posts in the terminal. You will learn how to:

- Create a Command with `act make:command`
- Use the App's services from a Command
- Return a result that people read as text and agents read as JSON
- Run the Command with `act`, and in Agent mode

## Overview

A **Command** is an action you run from the terminal. The App's own binary hosts its Commands: run with no arguments, it serves requests, and run with a Command's name, it runs that Command. `act` builds the binary and runs it for you, so `act posts:list` runs the App's `posts:list`.

Before a Command runs, the App starts as it does for serving, without listening for requests: providers register their bindings, and the boot, start and ready hooks run. It shuts down once the Command returns. The Command then asks the container for the store, the same binding the routes use.

## Create the Command

`act make:command` writes a Command into `app/commands`, named as you type it, and adds it to the App. The `posts:` part groups it with other Commands about posts:

```sh
act make:command posts:list
```

```text title="Output"
CREATE app/commands/posts_list.go
UPDATE start/app.go
         + app.Command(commands.PostsList(app))
```

The generator imported the new package in `start/app.go`:

```go title="start/app.go" ins={1}
	"blog/app/commands"
	"blog/app/exceptions"
	"blog/app/providers"
	"blog/config"
```

It also added the Command to the App, after the provider:

```go title="start/app.go" ins={3}
	app := tinka.New(cfg.HTTP)
	app.Provide(&providers.PostsProvider{})
	app.Command(commands.PostsList(app))
	app.OnError(exceptions.Handler)
```

The generated Command already runs. It writes a status line and returns nothing:

```sh
act posts:list
```

```text title="Output"
  INFO  posts:list ran
```

## List the posts

A Command doesn't print its result: its `Run` function returns it as a `cli.Result`, which is any type with a `String` method. People see what `String` returns. Agents, and anyone who passes `--json`, get the type's exported fields as JSON instead, so one Command serves both. `postList` holds the posts, with a `json` tag for the JSON key, and its `String` method writes one line per post.

`PostsList` receives the App, so `Run` can ask its container for the store. `Make` returns an error instead of panicking, which `Run` returns as the Command's failure. `Run` passes its `ctx` to the store, as the handlers pass the request: it's cancelled when you press Ctrl+C. Replace the generated file's contents:

```go title="app/commands/posts_list.go"
package commands

import (
	"context"
	"fmt"
	"strings"

	"github.com/mucolabs/tinka"
	"github.com/mucolabs/tinka/cli"

	"blog/app/posts"
)

// postList is the posts:list Command's result.
type postList struct {
	Posts []posts.Post `json:"posts"`
}

// String lists the posts for people, one per line.
func (l postList) String() string {
	var b strings.Builder
	for _, p := range l.Posts {
		fmt.Fprintf(&b, "%4d  %s\n", p.ID, p.Title)
	}
	return strings.TrimSuffix(b.String(), "\n")
}

// PostsList is the posts:list Command. It runs with the App started,
// so it can use the App's services.
func PostsList(app *tinka.App) *cli.Command {
	return &cli.Command{
		Name:    "posts:list",
		Summary: "List the blog's posts",
		Run: func(ctx context.Context, c *cli.Call) (cli.Result, error) {
			store, err := app.Container().Make[*posts.Store]()
			if err != nil {
				return nil, err
			}
			all, err := store.All(ctx)
			if err != nil {
				return nil, err
			}
			return postList{Posts: all}, nil
		},
	}
}
```

## Run the Command

Run it from the App's directory:

```sh
act posts:list
```

```text title="Output"
   1  Hello, Tinka
   2  Routes and handlers
```

The Command lists the two posts the store starts with, even when you created others through `act serve`. The Command runs in a process of its own, with its own App and its own store in memory. A store backed by a database would list the same posts in both, since both would read the same rows.

`Summary` describes the Command in the App's list of Commands, which `act --help` prints:

```sh
act --help
```

```text title="Output"
Run the App's Commands

Usage:
  act <command> [flags]

Commands:
  about         Describe the App: versions, Mode and what its providers configure
  generate:key  Generate a random APP_KEY and write it to .env
  posts:list    List the blog's posts
  completion    Print a shell completion script

Flags:
  -h, --help     Show help
      --json     Print a single JSON result instead of text
      --version  Print the version
```

## Run it as an agent

When an agent, such as a coding assistant, runs a Command, it gets **Agent mode**: the Command never prompts, and prints a single JSON result. `act` detects agents on its own. Pass `--json` to see what they get:

```sh
act posts:list --json
```

```text title="Output"
{"command":"posts:list","result":"ok","posts":[{"id":1,"title":"Hello, Tinka","body":"The first post on the blog."},{"id":2,"title":"Routes and handlers","body":"How a request finds its handler."}],"duration_ms":0}
```

`postList`'s `posts` field sits between the keys every result has: `command`, `result`, and `duration_ms`. A Command that fails answers with `"result":"error"` and an `error` object instead, so an agent never has to parse text written for people.

## Recap

`posts:list` lives in `app/commands/posts_list.go`, and `start/app.go` adds it with `app.Command`. It runs with the App started, takes the store from the container, and returns a `postList` that people read as lines and agents read as JSON. Next, [Testing](testing.md) checks the routes and this Command with tests.

See also: [Command line](../guides/command-line/introduction.md), [Creating Commands](../guides/command-line/commands.md), [Flags](../guides/command-line/flags.md), [Agent mode](../reference/agent-mode.md)
