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

# Spec commands

> Reference for the outerlayer spec commands, which find, file and edit the issue a spec lives in.

The `outerlayer spec` commands find, file and edit the issue that holds a spec, in your factory's issue tracker. The same four commands work for GitHub, Linear and Jira. You need only an OuterLayer credential, because OuterLayer holds the tracker's credential.

The starter `/spec` skill runs these commands for you. See [Write a spec with /spec](https://docs.outerlayer.ai/write-a-spec.md). To connect a tracker, see [Connect an issue tracker](https://docs.outerlayer.ai/connect-a-tracker.md).

Every command on this page takes these options:

| Option | Default | What it does |
| - | - | - |
| `--json` | off | Machine-readable output. |
| `--url <url>` | see [Credential resolution](https://docs.outerlayer.ai/reference/cli.md#credential-resolution) | Gateway base URL. |
| `--app-id <id>` | see [Credential resolution](https://docs.outerlayer.ai/reference/cli.md#credential-resolution) | Factory id. |

A command connects the repository to a factory on its own when exactly one factory fits. See [Automatic connection](https://docs.outerlayer.ai/reference/cli-cloud.md#automatic-connection). A failed command prints the reason and exits `1`. See [Refusals](#refusals).

## outerlayer spec target

Say where a spec would be filed, and whether the factory can file there. Needs `work.read`.

```bash theme={"system"}
outerlayer spec target [--tracker <name>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--tracker <name>` | none | The tracker to report on, when the factory has more than one. At most 32 characters. |

```text theme={"system"}
tracker: github
trackers: github
scopes: none
repositories: acme/api, acme/web
can file: yes
```

| Line | Meaning |
| - | - |
| `tracker` | The tracker a spec goes to. `none chosen` when the factory has several and you named none, or has none. |
| `trackers` | Every tracker the factory can file in. |
| `scopes` | The Linear teams or Jira projects the connection covers. `none` for GitHub. |
| `repositories` | The repositories linked to the factory. |
| `can file` | `yes` when the tracker can take a write now. |

A factory can file in GitHub when it has a linked repository, and in each Linear or Jira tracker it is connected to. When it can file in more than one, `tracker` shows the factory's default tracker. With no default, it shows `none chosen` until you pass `--tracker`. To set a default, see [Connect an issue tracker](https://docs.outerlayer.ai/connect-a-tracker.md).

With `--json`, the output has `tracker`, `trackers`, `scopes`, `repositories` and `canFile`. `tracker` is `null` when none is chosen.

## outerlayer spec search

Find open and closed issues that match a query, so you do not file a spec twice. Needs `work.read`.

```bash theme={"system"}
outerlayer spec search "<query>" [--tracker <name>] [--json]
```

The query has 1 to 256 characters. The command returns at most 20 issues, newest activity first. It prints `no matching issues` when it finds none.

| Flag | Default | What it does |
| - | - | - |
| `--tracker <name>` | every connected tracker | Search one connected tracker, when the factory has more than one. At most 32 characters. |

```text theme={"system"}
open   acme/api#3  Retry storms
       https://github.com/acme/api/issues/3
closed acme/api#2  Retry budget
       https://github.com/acme/api/issues/2
```

With `--json`, the output is `{ "issues": [ ... ] }`. Each issue has `key`, `state`, `title`, `tracker`, `updatedAt` and `url`.

## outerlayer spec file

File a spec as a new issue. Needs `spec.insert`.

```bash theme={"system"}
outerlayer spec file --title "<title>" --body-file <path> [--repo <owner/name>] [--tracker <name>] [--scope <key>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--title <text>` | required | The issue title, 1 to 256 characters. |
| `--body-file <path>` | required | The spec as Markdown. `-` reads standard input. An empty body is refused. |
| `--repo <owner/name>` | the only linked repository | The repository to file in, for GitHub. Optional for Linear or Jira, where it names the repository the work lands in. At most 256 characters. |
| `--tracker <name>` | the factory's default tracker, or the only connected tracker | Needed when the factory can file in more than one and has no default. A tracker you name overrides the default. At most 32 characters. |
| `--scope <key>` | the only team or project | The Linear team or Jira project. Needed when the connection covers several. At most 128 characters. |

The command prints the key, the URL, the issue's `updatedAt` and the command that builds the issue:

```text theme={"system"}
✓ filed acme/api#12
  https://github.com/acme/api/issues/12
  updatedAt: 2026-10-12T10:00:00Z
  build it with: outerlayer work build --issue 12 --repo acme/api
```

With `--json`, the output has `key`, `number`, `repository`, `tracker`, `updatedAt`, `url` and `workBuild`. `workBuild` is the command that builds the issue.

Filing needs two things:

* **A key with `spec.insert`.** The Agent preset carries it. To add it to another key, see [API keys](https://docs.outerlayer.ai/api-keys.md).
* **A tracker connection that can write.** For GitHub, an organization admin approves the **Issues: Read and write** permission of the OuterLayer GitHub App once. For Linear and Jira, connect the tracker with write access. Until then, `spec target` prints `can file: no`, and `spec file` sends the tracker nothing.

OuterLayer keeps a record of every write it tries in your tracker. The record cannot be edited or deleted.

## outerlayer spec edit

Replace the body of an issue that holds a spec. Needs `spec.update`, which is a permission of its own. A key can file specs and not be able to rewrite them.

```bash theme={"system"}
outerlayer spec edit <key> --body-file <path> [--updated-at <timestamp>] [--json]
```

`<key>` is the issue key, such as `acme/api#12` or `ENG-142`.

| Flag | Default | What it does |
| - | - | - |
| `--body-file <path>` | required | The whole new body as Markdown. `-` reads standard input. An empty body is refused. |
| `--updated-at <timestamp>` | none | The `updatedAt` you read. The edit is refused with `conflict` if the issue changed since. |

> [!WARNING]
>
> Without `--updated-at`, the edit does not check for another person's change, and it replaces that change.

Pass the `updatedAt` that `spec file` or your last `spec edit` printed. On Linear, any change to the issue counts as a change, not only a change to its description.

The command prints the key, the URL and the issue's new `updatedAt`. With `--json`, the output has `key`, `number`, `tracker`, `updatedAt` and `url`.

```text theme={"system"}
✓ edited acme/api#12
  https://github.com/acme/api/issues/12
  updatedAt: 2026-10-12T11:30:00Z
✓ recorded 4 criteria on item #41
```

### The criteria of the work item

When the issue has a work item, `spec edit` also records the criteria of the new body as the item's list. This is what [`emit criteria`](https://docs.outerlayer.ai/reference/cli-emit#outerlayer-emit-criteria) does.

The command reads a criterion from each numbered line that opens with an id in backticks:

* For a GitHub issue, the id looks like `` `AC-412-01` ``: `AC-`, an issue number of three or more digits, and two digits.
* For a Linear or Jira issue, the id looks like `` `AC-ENG-142-01` ``: the issue key is between `AC-` and the two digits.

The command ignores a line with any other id. It records nothing when the body lists no such line, or when the item already holds the same list.

Only a person may replace a list that the item already has. Inside an agent session, or with a key bound to no member, OuterLayer refuses to record the list. The edit of the issue still stands. The command prints the refusal and the `emit criteria` command to run yourself.

With `--json`, the output also has a `criteria` object. It has `item` (the item's number), `outcome` (`recorded`, `unchanged` or `not_recorded`) and `count`. For `not_recorded`, it also has `reason`. The object is absent when the body lists no criteria or the issue has no work item.

## Refusals

A refused command prints one sentence that says what is wrong, with the choices when there are any. It exits `1`. The API sends the code as `error.code`.

| Code | The command is refused when | What to do |
| - | - | - |
| `tracker_required` | The factory has no tracker to file in, or it has several and you named none. | Link a repository or connect Linear or Jira. Or pass `--tracker`, or set a default tracker for the factory. |
| `tracker_invalid` | The tracker you named is not connected, or the tracker refused the issue. | Run `spec target` for the connected trackers, or read the tracker's message. |
| `repository_required` | A GitHub spec names no repository and the factory has several, or the repository is not one of the factory's. | Pass `--repo` with one of the repositories the message lists. |
| `scope_required` | The connection covers several Linear teams or Jira projects and you named none, or it covers none. | Pass `--scope` with one of the keys the message lists. If it lists none, choose one in the factory's tracker settings. |
| `tracker_cannot_file` | OuterLayer cannot file in this tracker. | File the issue in the tracker by hand. |
| `tracker_write_not_permitted` | The tracker connection cannot write. | GitHub: an organization admin approves the App's **Issues: Read and write** permission. Linear: disconnect the tracker and connect it again with write access. Jira: connect with a credential that has `write:jira-work`, and let its account create and edit issues in the project. See [Connect an issue tracker](https://docs.outerlayer.ai/connect-a-tracker.md). |
| `item_key_out_of_scope` | A build's own item key called the command. | Use a key you made, or a login. |
| `conflict` | The issue changed after the `updatedAt` you passed. Nothing was written. | Read the issue again and redo the edit. |
| `issue_not_found` | The tracker has no such issue, or the issue is not in a source the factory connected. | Check the key with `spec search`. |
| `issue_key_malformed` | The key is not an issue key. The message shows the form of one. | Fix the key. |
| `tracker_unavailable` | The tracker could not be reached, or it refused the search. | Try again, or reconnect the tracker. |
| `spec_service_unavailable` | OuterLayer could not handle the request. | Try again. |

A key without the needed permission gets `403`, and OuterLayer sends the tracker nothing. The message names the missing permission.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.