@allocator-one/harness-cli 1.12.1 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +584 -199
- package/dist/{chunk-VOH6BBJD.js → chunk-F2YVTB2A.js} +736 -84
- package/dist/cli.js +90 -135
- package/dist/{server-FNNMISOZ.js → server-75QJASTY.js} +142 -10
- package/package.json +2 -1
- package/spec/openapi.json +3965 -14
package/README.md
CHANGED
|
@@ -1,206 +1,548 @@
|
|
|
1
1
|
# @allocator-one/harness-cli
|
|
2
2
|
|
|
3
|
-
CLI and MCP server for [Harness](https://harness.infra.one),
|
|
4
|
-
issue tracker.
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
The `harness` CLI and MCP server for [Harness](https://harness.infra.one),
|
|
4
|
+
allocator.one's issue tracker. It is a standalone Node client that keeps no
|
|
5
|
+
local issue state. Issues live on the server, so there is nothing to commit or
|
|
6
|
+
sync.
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
|
-
Zero-install, always current:
|
|
11
|
-
|
|
12
10
|
```sh
|
|
13
|
-
|
|
11
|
+
npm i -g @allocator-one/harness-cli
|
|
12
|
+
harness ready
|
|
14
13
|
```
|
|
15
14
|
|
|
16
|
-
Or
|
|
15
|
+
Or run it without installing:
|
|
17
16
|
|
|
18
17
|
```sh
|
|
19
|
-
|
|
20
|
-
harness ready
|
|
18
|
+
npx -y @allocator-one/harness-cli ready
|
|
21
19
|
```
|
|
22
20
|
|
|
23
21
|
Requires Node >= 20.
|
|
24
22
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
accepted for uniformity but changes nothing: stdout belongs to the command. A SIGTERM sent to
|
|
62
|
-
`harness` is forwarded to the command; Ctrl+C reaches the command from the
|
|
63
|
-
terminal directly and `harness` waits to report its exit. macOS and Linux
|
|
64
|
-
only.
|
|
65
|
-
|
|
66
|
-
Slug → variable, the same table rcl uses: anthropic → `ANTHROPIC_API_KEY`,
|
|
67
|
-
openai → `OPENAI_API_KEY`, google → `GEMINI_API_KEY`, openrouter →
|
|
68
|
-
`OPENROUTER_API_KEY`, brave → `BRAVE_API_KEY`, perplexity →
|
|
69
|
-
`PERPLEXITY_API_KEY`. Reading keys needs the `model-keys:read` scope: login
|
|
70
|
-
tokens carry it, `aone_` tokens must be minted with it. There is deliberately
|
|
71
|
-
no MCP tool for this — a tool result is model-visible text.
|
|
72
|
-
|
|
73
|
-
## Environment variables
|
|
74
|
-
|
|
75
|
-
| Variable | Effect |
|
|
76
|
-
| --- | --- |
|
|
77
|
-
| `HARNESS_API_URL` | Target host (default `https://harness.infra.one`). https only, plain http allowed against localhost. |
|
|
78
|
-
| `HARNESS_API_TOKEN` | Bearer token (`aone_…`); wins over the stored login. Never inherits the stored login's host. |
|
|
79
|
-
| `HARNESS_TEAM` | Default team key for commands that take `--team`. |
|
|
80
|
-
| `HARNESS_ACTOR` | Write attribution, sent only when explicitly set. |
|
|
81
|
-
| `HARNESS_CACHE_DIR` | Where `show` downloads attached images (default `$XDG_CACHE_HOME/harness/attachments` or `~/.cache/harness/attachments`). Absolute paths only. |
|
|
82
|
-
|
|
83
|
-
## For agents
|
|
84
|
-
|
|
85
|
-
`harness mcp` runs a stdio MCP server exposing every operation as a typed
|
|
86
|
-
tool — issue_list, issue_show, activity_list, issue_create, issue_update, issue_claim,
|
|
87
|
-
issue_close, issue_comment, issue_comment_edit, issue_comment_delete,
|
|
88
|
-
relation_list/create/update/delete, relation_add/rm (blocking shorthand),
|
|
89
|
-
link_list/add/update/rm,
|
|
90
|
-
project_list/show/create/update, milestone_list/create,
|
|
91
|
-
document_list/show/create/update, label_list/create, team_list,
|
|
92
|
-
team_create, workflow_states, user_list, stats, upload. Read-only tools carry
|
|
93
|
-
`readOnlyHint`. Results are compact JSON of the data envelope.
|
|
94
|
-
|
|
95
|
-
Register it with Claude Code:
|
|
23
|
+
## Sign in
|
|
24
|
+
|
|
25
|
+
You don't need to configure anything. Commands target
|
|
26
|
+
`https://harness.infra.one`, and the first command you run in a terminal
|
|
27
|
+
opens a browser login and then carries on. One login covers every shell,
|
|
28
|
+
worktree, and agent slot on the machine.
|
|
29
|
+
|
|
30
|
+
- `harness login`: browser sign-in (PKCE, loopback callback on 127.0.0.1).
|
|
31
|
+
`--no-browser` prints the URL and asks you to paste the token instead.
|
|
32
|
+
`--url <host>` signs in to another host, and `--timeout <ms>` changes how
|
|
33
|
+
long it waits for the callback (default 3 minutes). Login tokens last
|
|
34
|
+
about 30 days.
|
|
35
|
+
- `harness logout`: revokes the stored login on the server and deletes it
|
|
36
|
+
locally.
|
|
37
|
+
- `harness whoami`: shows the resolved host and where the credential came
|
|
38
|
+
from.
|
|
39
|
+
|
|
40
|
+
The automatic login only runs when stdout is a terminal and `CI` is unset
|
|
41
|
+
or empty. Elsewhere (agents, CI, pipes), a command that has no credential
|
|
42
|
+
prints how to authenticate and exits with code `2`. For those headless
|
|
43
|
+
contexts, mint an `aone_…` API token (admin → API tokens) with the
|
|
44
|
+
`harness:read` scope, adding `harness:write` if the workflow changes
|
|
45
|
+
anything (claim, create, update, comment, close), and export it as
|
|
46
|
+
`HARNESS_API_TOKEN`.
|
|
47
|
+
|
|
48
|
+
> **Never set `HARNESS_API_TOKEN` or `HARNESS_ACTOR` in an interactive
|
|
49
|
+
> session.** The browser login attributes writes to the real user, and
|
|
50
|
+
> overriding it falsifies the audit trail. Both variables are only for
|
|
51
|
+
> headless CI and agent slots.
|
|
52
|
+
|
|
53
|
+
Every identifier includes its team key, as in `CLI-12` or `ALL-4901`. Older
|
|
54
|
+
issues keep legacy prefixes: `IO-` issues were created before team-prefixed
|
|
55
|
+
numbering, `ALL-` numbers up to the import ceiling came from Linear, and
|
|
56
|
+
`BD-` from beads.
|
|
57
|
+
|
|
58
|
+
## Daily workflow
|
|
96
59
|
|
|
97
60
|
```sh
|
|
98
|
-
|
|
61
|
+
harness ready --claim # claim the team's next ready, unassigned issue
|
|
62
|
+
harness show CLI-12 # read it, including attached screenshots
|
|
63
|
+
# … do the work …
|
|
64
|
+
harness close CLI-12 --reason "Implemented in 1a2b3c4"
|
|
99
65
|
```
|
|
100
66
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
67
|
+
**Queue.** `harness ready` lists open, unblocked issues, highest priority
|
|
68
|
+
first. Blocked issues, `hold`-labeled issues, and parents with sub-issues
|
|
69
|
+
that are not completed are excluded. Only a completed blocker resolves: a
|
|
70
|
+
cancelled or archived one still blocks until it is completed or the
|
|
71
|
+
dependency is removed or re-pointed. An issue that is waiting on a wait
|
|
72
|
+
condition (see [Plans](#plans)) is not ready either. `harness blocked` lists
|
|
73
|
+
issues that are waiting on a blocker.
|
|
74
|
+
Both commands take `--team`. `ready` marks agent steps, the plan steps
|
|
75
|
+
Operator runs, with `[agent]` before the title and a legend under the table,
|
|
76
|
+
and `--json` gives every issue an `agent_step` flag. Only the plan driver
|
|
77
|
+
claims an agent step. The issue payload carries no executor, so the CLI
|
|
78
|
+
goes by the team: agent steps are filed in the organization's Operator team
|
|
79
|
+
(named Operator, keyed `OPERATOR`, or `OPERATOR2` and so on). That is a
|
|
80
|
+
heuristic. A step handed back to a person keeps its team and stays marked,
|
|
81
|
+
and the claim itself is the authority.
|
|
82
|
+
|
|
83
|
+
**Claim the next issue.** `harness ready --claim` is a single request: the
|
|
84
|
+
server picks the team's most important ready issue that has no assignee
|
|
85
|
+
(urgent, high, medium, low, then none; oldest first, then by id), assigns
|
|
86
|
+
you, and moves it to the team's first started state, all in one transaction.
|
|
87
|
+
Concurrent callers get different issues, and the CLI does no paging, sorting,
|
|
88
|
+
or retrying of its own.
|
|
89
|
+
|
|
90
|
+
- It needs a team: `--team`, then `HARNESS_TEAM`, then the repo config. With
|
|
91
|
+
none of them it is a usage error, and the claim is never sent.
|
|
92
|
+
- `--all`, `--limit`, and `--page` are refused with `--claim`, because the
|
|
93
|
+
server already ranks the whole ready set.
|
|
94
|
+
- An empty queue is a normal outcome: it prints "Nothing to claim: team CLI
|
|
95
|
+
has no unassigned ready issues." (`null` with `--json`) and exits `0`.
|
|
96
|
+
- A rate limit, server error, or unreadable response exits `1`, and an
|
|
97
|
+
unreachable API exits `3`. None of these is reported as an empty queue.
|
|
98
|
+
The CLI never retries the claim. When an unreadable response or a lost
|
|
99
|
+
connection leaves it unknown whether the claim landed, the error says to
|
|
100
|
+
check `harness list --team <key> --assignee me --status in_progress`
|
|
101
|
+
before claiming again.
|
|
102
|
+
|
|
103
|
+
**Read.** `harness show <id>` prints the fields, relations, children, and
|
|
104
|
+
comments. It also downloads the images attached to the issue's description,
|
|
105
|
+
design, acceptance criteria, notes, and comments (up to 20, cached locally)
|
|
106
|
+
and prints their paths. Screenshots are often the reproduction, so read
|
|
107
|
+
them. `--no-images` skips the downloads. If a download fails, a stderr
|
|
108
|
+
notice says so and the command still succeeds. `harness comments <id>`
|
|
109
|
+
prints the full, untruncated comment thread with comment ids. The
|
|
110
|
+
`Blocked:` line names a cancelled or archived blocker as such
|
|
111
|
+
(`yes, by CLI-3 (cancelled), CLI-4 (archived), CLI-5`). When the issue has
|
|
112
|
+
wait conditions, `Waiting:` says how many still hold it, and a
|
|
113
|
+
`Wait conditions:` section lists each one (`[ ] gate "Finance sign-off" · <id>`,
|
|
114
|
+
`[x] not before … (satisfied …)`, `system event`) with the id
|
|
115
|
+
`harness gate satisfy` takes.
|
|
116
|
+
`harness activity <id>` prints the issue's activity timeline: comments,
|
|
117
|
+
creation, and state and assignee changes. It shows the newest 20 entries
|
|
118
|
+
(`--limit` accepts 1–100), and `--all` reads further back.
|
|
119
|
+
|
|
120
|
+
**Create.** Creating an issue before you write code keeps the work tracked:
|
|
116
121
|
|
|
117
122
|
```sh
|
|
118
|
-
harness
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
harness relation rm IO-123 IO-456
|
|
123
|
+
harness create --title "Fix login redirect" --type bug --priority high \
|
|
124
|
+
--description "…" --parent CLI-3 --assignee me --due-date 2026-11-01 \
|
|
125
|
+
--label regression --idempotency-key fix-login-redirect-1
|
|
122
126
|
```
|
|
123
127
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
- `--type task|bug|feature|epic` is applied as a `type/<type>` label.
|
|
129
|
+
- `--priority urgent|high|medium|low|none` takes names only. Bare numbers
|
|
130
|
+
are refused because bd and Harness number priorities differently.
|
|
131
|
+
- `--team <key>` falls back to `HARNESS_TEAM` and then to the repo config.
|
|
132
|
+
With none of these set, the command is a usage error.
|
|
133
|
+
- `--label` (repeatable) looks for a matching label in the target team first
|
|
134
|
+
and then at workspace scope. Missing labels are created at workspace scope
|
|
135
|
+
(which requires Harness administrator standing) before the issue is filed,
|
|
136
|
+
and the issue is created in a single request with all its labels.
|
|
137
|
+
- `--idempotency-key <key>` (1–200 printable ASCII characters, no leading or
|
|
138
|
+
trailing space) makes a retry safe. If a create times out, run it again
|
|
139
|
+
with the same key to get the original issue back instead of a duplicate.
|
|
140
|
+
The CLI never retries a write on its own.
|
|
141
|
+
- `--design`, `--notes`, and `--acceptance` set the remaining text fields.
|
|
142
|
+
|
|
143
|
+
**Update.** `harness update <id>` with:
|
|
144
|
+
|
|
145
|
+
- `--claim`: atomic. It assigns you and moves the issue to the team's first
|
|
146
|
+
started state. A conflict (exit `1`, HTTP 409) names its reason: someone
|
|
147
|
+
else holds the issue, or it is not claimable yet. Two reasons come with a
|
|
148
|
+
next step: `agent_step` (Operator runs it; to do it yourself, set its
|
|
149
|
+
executor to `none` with `harness plan edit <plan-id>` and release the plan
|
|
150
|
+
again) and `waiting_on_condition` (`harness show` lists what it waits on;
|
|
151
|
+
tick a gate with `harness gate satisfy`). It cannot be combined with
|
|
152
|
+
`--assignee`.
|
|
153
|
+
- `--state <name-or-type>`: moves the issue to the workflow state whose name
|
|
154
|
+
or type matches.
|
|
155
|
+
- `--title`, `--description`, `--design`, `--acceptance`, `--priority`,
|
|
156
|
+
`--parent`, `--assignee` (`""` unassigns), `--due-date` (`""` clears).
|
|
157
|
+
- `--notes` replaces the notes and `--append-notes` appends to them. Pass
|
|
158
|
+
only one of the two.
|
|
159
|
+
- `--add-label` / `--remove-label` (repeatable, name or id) add or remove
|
|
160
|
+
individual labels without replacing the rest. You can only add labels that
|
|
161
|
+
belong to the issue's team or the workspace, and a missing name is created
|
|
162
|
+
at workspace scope, as with `create`. If two attached labels match a name,
|
|
163
|
+
remove by id.
|
|
164
|
+
|
|
165
|
+
**Dependencies.** `harness dep add <issue> <blocker>`: **the second argument
|
|
166
|
+
blocks the first.** `harness dep rm <issue> <blocker>` removes the
|
|
167
|
+
dependency. For other relation types, see `relation` under
|
|
168
|
+
[Other commands](#other-commands).
|
|
169
|
+
|
|
170
|
+
**Close.** `harness close <id> [<id>…]` closes one or more issues as
|
|
171
|
+
completed. Use `--cancel` to close them as canceled, or `--duplicate-of <id>`
|
|
172
|
+
to close a single issue as a duplicate of the canonical one. The
|
|
173
|
+
duplicate-of form records a duplicate relation and cannot be combined with
|
|
174
|
+
`--cancel`. `--reason` records why the issue is done. The output lists the
|
|
175
|
+
issues each closure unblocked
|
|
176
|
+
(`Closed CLI-12 — unblocked: CLI-13, CLI-14`); act on them instead of
|
|
177
|
+
leaving them in the queue. Only completing releases dependents: a canceled
|
|
178
|
+
issue still blocks them. A duplicate close moves its blocking edges to the
|
|
179
|
+
canonical issue, and any edge that cannot move stays on the duplicate. When
|
|
180
|
+
the closed issue still blocks anything, an indented line names those
|
|
181
|
+
dependents (`Still blocking: CLI-13 (a cancelled issue still blocks its
|
|
182
|
+
dependents)`) so you can remove or re-point those dependencies. With
|
|
183
|
+
several ids, each one is closed on its own.
|
|
184
|
+
A failure prints `FAILED <id>: …`, and the command exits `1` after trying
|
|
185
|
+
the rest. `harness archive <id>…` archives issues, `--restore` brings them
|
|
186
|
+
back, and `harness list --scope archived` lists them.
|
|
187
|
+
|
|
188
|
+
**Comment.** `harness comment <id> "text" [--mention <name>]…` posts a
|
|
189
|
+
comment. The server resolves each mention and notifies that person.
|
|
190
|
+
`harness comment edit <id> <comment-id> "new text"` and
|
|
191
|
+
`harness comment delete <id> <comment-id>` (alias `rm`) work on your own
|
|
192
|
+
comments. Comment ids are UUIDs from `harness comments`.
|
|
193
|
+
|
|
194
|
+
**Find.** There are two search commands:
|
|
195
|
+
|
|
196
|
+
- `harness search <question…>` searches by meaning. It returns a ranked list
|
|
197
|
+
with an exact identifier first, followed by results ordered by relevance
|
|
198
|
+
across a lexical and a semantic signal, so it can find issues that share
|
|
199
|
+
none of your words. Write the question in plain language instead of
|
|
200
|
+
keywords. Results default to 10 and are capped at 20, with no pages
|
|
201
|
+
(`--page`/`--all` are accepted but ignored). Archived issues are not
|
|
202
|
+
searched. If the semantic side is unavailable, stderr says
|
|
203
|
+
`semantic search unavailable — showing lexical matches only`.
|
|
204
|
+
- `harness list --search <text>` is the paginated lexical filter, which
|
|
205
|
+
matches title or issue number.
|
|
206
|
+
|
|
207
|
+
`harness list` filters: `--status open|in_progress|closed`,
|
|
208
|
+
`--assignee <me|id|email|name|"">` (`""` means unassigned),
|
|
209
|
+
`--created-by <me|id>`, `--involved me`, `--priority <name>`,
|
|
210
|
+
`--label <name>` or `--type <type>` (only one of the two), `--team <key>`,
|
|
211
|
+
`--parent <id>` (children of an epic), and `--scope all|archived`.
|
|
212
|
+
|
|
213
|
+
**Overview.** `harness stats` prints per-team and total counts.
|
|
214
|
+
`harness prime` prints those counts, the organization's ten most important
|
|
215
|
+
ready issues across every team (urgent first and priority none last, ranked
|
|
216
|
+
the way `harness ready --claim` ranks, though that claims only unassigned
|
|
217
|
+
issues on one team), and a short workflow reminder. It is meant for the
|
|
218
|
+
start of an agent session. Against a server older than this read it shows
|
|
219
|
+
the ten newest ready issues instead.
|
|
220
|
+
|
|
221
|
+
### Text from a file
|
|
222
|
+
|
|
223
|
+
When text comes from somewhere other than your own typing, such as issue
|
|
224
|
+
content, a commit message, or model output, pass a file instead of a shell
|
|
225
|
+
argument. Quoting can be defeated: a `"` ends the string, `$(…)`
|
|
226
|
+
substitutes, and a heredoc ends at any flush-left delimiter line the text
|
|
227
|
+
happens to contain.
|
|
129
228
|
|
|
130
229
|
```sh
|
|
131
|
-
harness
|
|
230
|
+
harness create --title "…" --description-file ./body.md
|
|
231
|
+
harness update CLI-12 --notes-file ./notes.md --acceptance-file ./ac.md
|
|
232
|
+
harness comment CLI-12 --body-file ./comment.md
|
|
233
|
+
git log -1 --format=%B | harness close CLI-12 --reason-file -
|
|
132
234
|
```
|
|
133
235
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
236
|
+
| Command | File options |
|
|
237
|
+
| --- | --- |
|
|
238
|
+
| `create` | `--title-file`, `--description-file`, `--design-file`, `--notes-file`, `--acceptance-file` |
|
|
239
|
+
| `update` | the same five, plus `--append-notes-file` |
|
|
240
|
+
| `close` | `--reason-file` |
|
|
241
|
+
| `comment`, `comment edit` | `--body-file` |
|
|
242
|
+
|
|
243
|
+
Other commands (`project`, `milestone`, `doc`, `link`, and so on) take inline
|
|
244
|
+
values only. `plan create` and `plan edit` read a whole JSON or YAML
|
|
245
|
+
document with `--file` (see [Plans](#plans)), under the same stdin and
|
|
246
|
+
UTF-8 rules, up to 8 MB (the API's request limit). `-` reads from stdin, and only one option per invocation can do
|
|
247
|
+
that. stdin must not be a terminal. Passing both the inline and the file form
|
|
248
|
+
of a field is a usage error, and so is a file that produces an empty title,
|
|
249
|
+
reason, or comment. Conflicting options are caught before anything is read,
|
|
250
|
+
so a rejected command never consumes your pipe. A file must be valid UTF-8
|
|
251
|
+
and at most 1 MB, and one trailing newline is dropped, as with
|
|
252
|
+
`$(cat file)`.
|
|
253
|
+
|
|
254
|
+
## Plans
|
|
255
|
+
|
|
256
|
+
A plan is a root issue and its steps, created in one request: their
|
|
257
|
+
parents, `blocked_by` dependencies, executors, teams and wait conditions.
|
|
258
|
+
Either the whole plan is created or nothing is. Plans start held, so
|
|
259
|
+
nothing runs until a person releases one.
|
|
142
260
|
|
|
143
261
|
```sh
|
|
144
|
-
harness
|
|
145
|
-
harness
|
|
262
|
+
harness plan create --file plan.yaml --team IO --idempotency-key q3-report
|
|
263
|
+
harness plan show <plan-id>
|
|
264
|
+
harness plan release <plan-id> --collaborator grace@example.com
|
|
265
|
+
harness gate satisfy IO-121 "Finance sign-off"
|
|
146
266
|
```
|
|
147
267
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
268
|
+
**The plan file** is the API's request body, as JSON (`.json`) or YAML
|
|
269
|
+
(anything else, and stdin with `--file -`). YAML is read as YAML 1.2 and
|
|
270
|
+
held to what JSON can carry: dates stay strings, and a duplicate key, an
|
|
271
|
+
explicit tag such as `!!timestamp`, or `.inf`/`.nan` is refused. The
|
|
272
|
+
root's fields sit at the top level, and each step has a `key` that other
|
|
273
|
+
steps refer to:
|
|
274
|
+
|
|
275
|
+
```yaml
|
|
276
|
+
title: Ship the Q3 LP report
|
|
277
|
+
spend_budget_cents: 5000 # needed to release Operator steps
|
|
278
|
+
steps:
|
|
279
|
+
- key: gather
|
|
280
|
+
title: Gather the fund data
|
|
281
|
+
executor: operator # Operator runs it; the description is its instruction
|
|
282
|
+
description: Pull the Q3 NAVs for every fund in the portfolio
|
|
283
|
+
- key: review
|
|
284
|
+
title: Review the numbers
|
|
285
|
+
team: IO # a person's step; defaults to --team
|
|
286
|
+
blocked_by: [gather]
|
|
287
|
+
conditions:
|
|
288
|
+
- kind: gate
|
|
289
|
+
label: Finance sign-off
|
|
290
|
+
- key: send
|
|
291
|
+
title: Send the report
|
|
292
|
+
blocked_by: [review]
|
|
293
|
+
conditions:
|
|
294
|
+
- kind: not_before
|
|
295
|
+
not_before: "2026-10-15T09:00:00Z"
|
|
296
|
+
```
|
|
159
297
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
298
|
+
- A step takes the issue fields (`title`, `description`, `priority` as a
|
|
299
|
+
number from 0 for none to 4 for low, `assignee`, `label_ids`, …) plus
|
|
300
|
+
`key`, `parent` (the key of the step that contains it), `blocked_by`
|
|
301
|
+
(keys), `executor` (`none`, the default, or `operator`), `team`, and
|
|
302
|
+
`conditions`. Operator steps are filed in the Operator team. Only steps
|
|
303
|
+
without sub-steps carry an executor, edges, or conditions.
|
|
304
|
+
- Wait conditions: `not_before` (a UTC instant), `gate` (a `label` a person
|
|
305
|
+
ticks), and `event` (`event`, `subject_id`, and for a stage change
|
|
306
|
+
`target_id`). The allowed events are `deal_stage_changed`,
|
|
307
|
+
`opportunity_stage_changed`, `form_submitted`, and `investment_executed`.
|
|
308
|
+
Every condition must hold before the step is ready.
|
|
309
|
+
- A plan has at most 100 steps, its root included.
|
|
310
|
+
- `--team` (or `HARNESS_TEAM` or the repo config) is the root's team and the
|
|
311
|
+
default team for people's steps. `--idempotency-key` works as it does for
|
|
312
|
+
`create`: if a create times out, run it again with the same key to get
|
|
313
|
+
the same plan back.
|
|
314
|
+
|
|
315
|
+
**Validation.** The server checks the whole graph and an invalid plan
|
|
316
|
+
creates nothing. The command exits `1`, and stderr lists every violation by
|
|
317
|
+
the step key it is about:
|
|
318
|
+
|
|
319
|
+
```text
|
|
320
|
+
harness: The plan is not valid; nothing was changed (HTTP 422)
|
|
321
|
+
step "review" (steps[1]): parent names no step of this plan, or no issue in this organization (parent: nope) [unknown_parent]
|
|
322
|
+
plan: A plan with Operator steps needs a positive spend budget [spend_budget_required]
|
|
165
323
|
```
|
|
166
324
|
|
|
167
|
-
`
|
|
168
|
-
and
|
|
169
|
-
|
|
325
|
+
**Show.** `harness plan show <plan-id>` prints the plan's status, budgets,
|
|
326
|
+
revision, and spend estimate, then every step in tree order (indented by
|
|
327
|
+
depth) with its key, executor, state, and status. The status is `ready`,
|
|
328
|
+
`held` (edited since the release), `not in revision` (added after it),
|
|
329
|
+
`waiting` (on a wait condition), `container`, `done`, `cancelled`,
|
|
330
|
+
`archived`, or `-`. The edges and each step's wait conditions come after
|
|
331
|
+
the steps.
|
|
332
|
+
|
|
333
|
+
**Edit.** `harness plan edit <plan-id> --file edits.yaml` changes step
|
|
334
|
+
executors and keys, naming each step by its `step` key or `issue`
|
|
335
|
+
identifier, and the budgets (`retry_budget`, `spend_budget_cents`):
|
|
336
|
+
|
|
337
|
+
```yaml
|
|
338
|
+
steps:
|
|
339
|
+
- step: gather
|
|
340
|
+
executor: none # hand the step to a person
|
|
341
|
+
```
|
|
170
342
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
343
|
+
Editing a released step holds it until the plan is released again. A
|
|
344
|
+
budget change needs the plan held first. Descriptions, parents, and edges
|
|
345
|
+
are edited with the issue commands (`update`, `dep`). Conditions are set
|
|
346
|
+
when the plan is created. Editing them later goes through the API's issue
|
|
347
|
+
condition endpoints, which have no command yet; `gate satisfy` ticks a
|
|
348
|
+
gate.
|
|
349
|
+
|
|
350
|
+
**Release and hold.** `harness plan release <plan-id>` releases the plan as
|
|
351
|
+
you: it freezes the next revision and lets ready steps run.
|
|
352
|
+
`--collaborator <id-or-email>` (repeatable) adds people to the plan room
|
|
353
|
+
where its Operator steps run, and `--retry-budget` and
|
|
354
|
+
`--spend-budget-cents` set the budgets. `harness plan hold <plan-id>`
|
|
355
|
+
pauses every step until the plan is released again.
|
|
356
|
+
|
|
357
|
+
**Gates.** `harness gate satisfy <issue> <gate>` ticks a gate and
|
|
358
|
+
`harness gate unsatisfy <issue> <gate>` unticks it. `<gate>` is the gate's
|
|
359
|
+
label or its condition id from `harness show`. A label matches exactly
|
|
360
|
+
first, then ignoring case, and a label that more than one gate carries is
|
|
361
|
+
refused with their ids. Ticking a gate requires staff standing in the
|
|
362
|
+
organization.
|
|
363
|
+
|
|
364
|
+
A server without the plan API answers the plan commands with a 404, and the
|
|
365
|
+
error says that the server may predate plans.
|
|
366
|
+
|
|
367
|
+
## Uploads
|
|
368
|
+
|
|
369
|
+
`harness upload <path>` uploads a file and prints a markdown reference to
|
|
370
|
+
paste into a description or comment. `--json` returns the URL, filename,
|
|
371
|
+
content type, byte size, and markdown instead.
|
|
372
|
+
|
|
373
|
+
- Images (PNG, JPG, WebP, GIF) can be up to 25 MB.
|
|
374
|
+
- Videos must be `.mp4` files of up to 100 MB, with H.264 8-bit 4:2:0 video,
|
|
375
|
+
optional AAC audio, and optional mov_text subtitles. The server validates
|
|
376
|
+
the media and does not transcode other formats. Harness shows linked
|
|
377
|
+
videos as an inline player.
|
|
378
|
+
|
|
379
|
+
A new video is private to its uploader until that user saves its link in an
|
|
380
|
+
issue description or comment. After that it follows the access policy of
|
|
381
|
+
the issue that holds it, and reusing the link elsewhere doesn't change who
|
|
382
|
+
owns it. `harness show` downloads images only, never videos. The older
|
|
383
|
+
`attach-image <path>` command accepts images only (25 MB).
|
|
384
|
+
|
|
385
|
+
## Other commands
|
|
386
|
+
|
|
387
|
+
| Command | Notes |
|
|
388
|
+
| --- | --- |
|
|
389
|
+
| `relation list <issue>` | Every typed relation, with relation ids. |
|
|
390
|
+
| `relation add <issue> <other> --type <t>` | `blocks`, `blocked_by`, `related`, `duplicate`, `duplicate_of`, `duplicated_by`. |
|
|
391
|
+
| `relation update <issue> <relation-id-or-issue> --type <t>` | `blocks`, `blocked_by`, `related`, `duplicate_of`. |
|
|
392
|
+
| `relation rm <issue> <relation-id-or-issue>` | If more than one relation matches the other issue, pass the relation id. |
|
|
393
|
+
| `link list\|add\|update\|rm <issue> …` | URLs attached to an issue. `add <issue> <url> [--title]`; `update <issue> <link-id-or-url> --url … \| --title …` refuses an ambiguous URL; `rm <issue> <link-id-or-url>` removes the first match. |
|
|
394
|
+
| `project list\|show\|create\|update` | `list [--search] [--state]` (paginated). `create --name … [--description] [--summary] [--state] [--lead <user-id>] [--start-date] [--target-date]`. |
|
|
395
|
+
| `milestone list\|create --project <id>` | `create … --name … [--description] [--target-date]`. |
|
|
396
|
+
| `doc list\|show\|create\|update` | `list [--search]` (paginated). `create --title … [--content] [--project <id>]`. |
|
|
397
|
+
| `label list\|create\|update\|delete` | `create <name> [--color]`, `update <id> [--name] [--color]`, `delete <id> --yes` (refused while issues carry the label). |
|
|
398
|
+
| `team list\|create\|rename\|set-default` | `create --key <KEY> --name …` sets up the default workflow states. The key is permanent; `rename <key> --name …` changes only the display name. `set-default <key>` makes the team the org's default filing target. |
|
|
399
|
+
| `state list\|add\|update\|order\|delete` | A team's workflow states (`--team`, else `HARNESS_TEAM` or the repo config). `add --name … --type backlog\|unstarted\|started\|completed\|canceled [--color]`, `order <id,id,…>` (every state id, in order), `delete <id> --yes`. `states` is an alias of `state list`. |
|
|
400
|
+
| `member list\|add\|set-role` | The org's tracker roster. `add --email … --role admin\|staff\|collaborator`, `set-role <user-id> --role …\|none` (`none` revokes). |
|
|
401
|
+
| `user list` | Users, for resolving assignees. |
|
|
402
|
+
| `version` | CLI and server API versions. Works before you log in, and so does `harness --version`. |
|
|
403
|
+
|
|
404
|
+
`team rename`, `team set-default`, `label update`, `label delete`, and the
|
|
405
|
+
write commands of `state` and `member` require Harness administrator
|
|
406
|
+
standing, and so does `member list`.
|
|
407
|
+
|
|
408
|
+
## Provider keys
|
|
177
409
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
never consumes your pipe. One trailing newline is dropped, like `$(cat file)`.
|
|
410
|
+
Admins store the org's provider API keys (Anthropic, OpenAI, Google,
|
|
411
|
+
OpenRouter, Brave Search, Perplexity) in Harness under `Settings → Model
|
|
412
|
+
keys`. `harness keys` passes them to tools without ever printing them:
|
|
182
413
|
|
|
183
|
-
|
|
414
|
+
- `harness keys list` shows the provider slugs the org has keys for and the
|
|
415
|
+
environment variable each one maps to (`--json` returns
|
|
416
|
+
`{providers: [{provider, env}]}`). It never prints the values.
|
|
417
|
+
- `harness keys exec [--only <slugs>] [--override] -- <command> [args…]`
|
|
418
|
+
runs the command with the keys in its environment and exits with the
|
|
419
|
+
command's exit code (128 + the signal number if the command was killed by a
|
|
420
|
+
signal). `--only brave,perplexity` passes just those providers and also
|
|
421
|
+
removes your shell's own copies of the other provider variables, so the
|
|
422
|
+
command sees only what it needs. If the org has no key for a provider you
|
|
423
|
+
requested, your shell's variable is kept and stderr says so. Slugs are
|
|
424
|
+
trimmed and case-insensitive. An `--only` list that names nothing
|
|
425
|
+
(`--only ,`) is a usage error, not a request for everything. Without
|
|
426
|
+
`--override`, a variable that is already set in your shell wins (a blank
|
|
427
|
+
variable counts as unset). `--json` is accepted but has no effect, because
|
|
428
|
+
stdout belongs to the command. A SIGTERM sent to `harness` is forwarded to
|
|
429
|
+
the command. Ctrl+C reaches the command directly from the terminal, and
|
|
430
|
+
`harness` waits to report its exit. Works on macOS and Linux only.
|
|
431
|
+
|
|
432
|
+
Each slug maps to the same variable as in rcl: anthropic → `ANTHROPIC_API_KEY`,
|
|
433
|
+
openai → `OPENAI_API_KEY`, google → `GEMINI_API_KEY`, openrouter →
|
|
434
|
+
`OPENROUTER_API_KEY`, brave → `BRAVE_API_KEY`, perplexity →
|
|
435
|
+
`PERPLEXITY_API_KEY`. Reading keys requires the `model-keys:read` scope.
|
|
436
|
+
Login tokens have it; `aone_` tokens need to be minted with it. There is
|
|
437
|
+
deliberately no MCP tool for keys, because a tool result is text the model
|
|
438
|
+
can read.
|
|
439
|
+
|
|
440
|
+
## Scripting contract
|
|
441
|
+
|
|
442
|
+
- **`--json` on every command** except `mcp`. It replaces the human output
|
|
443
|
+
with the response data as JSON. `keys exec` accepts it but changes
|
|
444
|
+
nothing: its stdout is the child command's output.
|
|
445
|
+
- **stdout carries data only**, so it is safe to pipe. Diagnostics go to
|
|
446
|
+
stderr:
|
|
447
|
+
- `→ https://harness.infra.one` on every command that writes, naming the
|
|
448
|
+
host it sent the write to.
|
|
449
|
+
- `harness: showing 50 of 132 (page 1 of 3) — pass --all for the rest, or --page to step through.`
|
|
450
|
+
when a paginated read did not return everything, and
|
|
451
|
+
`harness: --limit N was capped at M by the server.` when the server
|
|
452
|
+
reduced the page size you asked for.
|
|
453
|
+
- The `activity`, `search`, and `show` notices described above.
|
|
454
|
+
- The update notice (see [Staying current](#staying-current)).
|
|
455
|
+
- **Pagination.** `ready`, `blocked`, `list`, `project list`, and `doc list`
|
|
456
|
+
return 50 records per page by default (`ready --claim` has no pages). `--limit` changes the page size,
|
|
457
|
+
`--page` selects a page, and `--all` follows every page (pass `--all` or
|
|
458
|
+
`--page`, not both). The truncation notice goes to stderr only, so a
|
|
459
|
+
script that reads just the `--json` output cannot tell a full page from a
|
|
460
|
+
complete result. Pass `--all` when you need everything.
|
|
461
|
+
- **Exit codes.** `keys exec` returns the command's exit code instead.
|
|
462
|
+
|
|
463
|
+
| Code | Meaning |
|
|
464
|
+
| --- | --- |
|
|
465
|
+
| `0` | Success, including `ready --claim` finding nothing to claim. |
|
|
466
|
+
| `1` | The command failed. Causes include an API error (the message gives the HTTP status, followed by any per-field errors or plan violations, one per line), a response the CLI cannot interpret, a partial failure across several ids, or a local refusal such as an unreadable or oversized upload. |
|
|
467
|
+
| `2` | Usage or configuration error, including "not signed in" outside a terminal. Nothing was sent. |
|
|
468
|
+
| `3` | The API was unreachable (DNS, TLS, connection refused, or timeout). Retry with backoff, and if the API stays down, say so instead of dropping tracked work. |
|
|
469
|
+
|
|
470
|
+
## MCP server
|
|
471
|
+
|
|
472
|
+
`harness mcp` runs an MCP server over stdio. To register it with Claude Code:
|
|
184
473
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
The MCP `upload` tool accepts the same local path and returns the same metadata.
|
|
474
|
+
```sh
|
|
475
|
+
claude mcp add --scope user harness -- npx -y @allocator-one/harness-cli mcp
|
|
476
|
+
```
|
|
189
477
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
subtitles. The server validates the media; it does not transcode other formats.
|
|
478
|
+
With a global install, `harness mcp` works as the command instead of the
|
|
479
|
+
`npx` form. Any other MCP client can use the same command:
|
|
193
480
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
`harness show` continues downloading images only, never linked videos.
|
|
198
|
-
The older `attach-image` command retains its 25 MB limit.
|
|
481
|
+
```json
|
|
482
|
+
{ "mcpServers": { "harness": { "command": "npx", "args": ["-y", "@allocator-one/harness-cli", "mcp"] } } }
|
|
483
|
+
```
|
|
199
484
|
|
|
200
|
-
|
|
485
|
+
The server exposes 58 tools:
|
|
201
486
|
|
|
202
|
-
|
|
203
|
-
|
|
487
|
+
| Area | Tools |
|
|
488
|
+
| --- | --- |
|
|
489
|
+
| Issues | `issue_list`, `issue_search`, `issue_show`, `activity_list`, `issue_create`, `issue_update`, `issue_claim`, `issue_close`, `issue_archive` |
|
|
490
|
+
| Comments | `issue_comment`, `issue_comment_edit`, `issue_comment_delete`, `comment_list` |
|
|
491
|
+
| Relations | `relation_add`, `relation_rm` (blocking shorthand: `blocker` blocks `issue`), `relation_list`, `relation_create`, `relation_update`, `relation_delete` |
|
|
492
|
+
| Links | `link_list`, `link_add`, `link_update`, `link_rm` |
|
|
493
|
+
| Projects and docs | `project_list`, `project_show`, `project_create`, `project_update`, `milestone_list`, `milestone_create`, `document_list`, `document_show`, `document_create`, `document_update` |
|
|
494
|
+
| Labels | `label_list`, `label_create`, `label_update`, `label_delete` |
|
|
495
|
+
| Teams and states | `team_list`, `team_create`, `team_rename`, `team_set_default`, `workflow_states`, `state_add`, `state_update`, `state_order`, `state_delete` |
|
|
496
|
+
| People | `member_list`, `member_add`, `member_set_role`, `user_list` |
|
|
497
|
+
| Plans and gates | `plan_create`, `plan_show`, `plan_edit`, `plan_release`, `plan_hold`, `gate_set` |
|
|
498
|
+
| Other | `stats`, `upload` |
|
|
499
|
+
|
|
500
|
+
The tools cover the issue workflow and the admin surface. Some commands are
|
|
501
|
+
CLI-only: `keys` (deliberately), `login`/`logout`/`whoami`, `version`, and
|
|
502
|
+
`prime`. Read-only tools carry `readOnlyHint`. Each result is compact JSON
|
|
503
|
+
of the response data. Any stderr notice the CLI would print, such as
|
|
504
|
+
truncation, is added as a second text item. A partial failure across several
|
|
505
|
+
ids is returned as an error, with the details for each id in the JSON.
|
|
506
|
+
Some tools behave differently from their commands:
|
|
507
|
+
|
|
508
|
+
- `issue_list` with `ready: true` or `blocked: true` accepts every list
|
|
509
|
+
filter. The CLI's `ready` and `blocked` take only `--team`.
|
|
510
|
+
- `issue_list` only lists. Claiming the team's next issue (`ready --claim`)
|
|
511
|
+
is CLI-only; over MCP, claim a specific issue with `issue_claim`.
|
|
512
|
+
- `issue_show` downloads images only when you pass `images: true`.
|
|
513
|
+
- `state_delete` and `label_delete` don't need the CLI's `--yes`.
|
|
514
|
+
- `plan_create` takes the plan as an object (`plan`), with `team` and
|
|
515
|
+
`idempotency_key` beside it, instead of a file. Its schema describes the
|
|
516
|
+
steps and conditions, and keys inside `plan` that the schema does not
|
|
517
|
+
know are still sent, so the server reports them. `plan_edit` takes
|
|
518
|
+
`steps`, `retry_budget`, and `spend_budget_cents` directly. As with every
|
|
519
|
+
tool, a top-level argument the tool does not declare is dropped.
|
|
520
|
+
`gate_set` takes `issue`, `gate`, and `satisfied`.
|
|
521
|
+
- An API error's details come back in the tool error text: a validation
|
|
522
|
+
error's fields, and an invalid plan's violations, one per line by step
|
|
523
|
+
key.
|
|
524
|
+
- `issue_list` with `ready: true` gives every issue an `agent_step` flag,
|
|
525
|
+
read from its team as `ready` does.
|
|
526
|
+
|
|
527
|
+
For example, `issue_list` with `{"ready": true, "team": "CLI"}` returns the
|
|
528
|
+
ready queue, and `issue_claim` with `{"id": "CLI-12"}` claims an issue
|
|
529
|
+
atomically. `issue_close` takes `ids`, `reason`, `cancel`, and
|
|
530
|
+
`duplicate_of`, and `issue_create` takes an `idempotency_key` for safe
|
|
531
|
+
retries.
|
|
532
|
+
|
|
533
|
+
The MCP server resolves auth exactly as the CLI does, on every call, so a
|
|
534
|
+
login completed while the server is running takes effect without a restart.
|
|
535
|
+
It never opens a browser. Without a credential, a tool returns an error that
|
|
536
|
+
tells the agent to run `harness login` in a terminal or set
|
|
537
|
+
`HARNESS_API_TOKEN`.
|
|
538
|
+
|
|
539
|
+
## Configuration
|
|
540
|
+
|
|
541
|
+
### Repo config
|
|
542
|
+
|
|
543
|
+
A committed `.harness-cli/config.json`, found by walking up from the working
|
|
544
|
+
directory the way git finds `.git`, gives every clone of a repo the same
|
|
545
|
+
defaults:
|
|
204
546
|
|
|
205
547
|
```json
|
|
206
548
|
{ "team": "CLI" }
|
|
@@ -208,47 +550,90 @@ from the working directory, gives every clone of a repo its defaults:
|
|
|
208
550
|
|
|
209
551
|
| Field | Effect |
|
|
210
552
|
| --- | --- |
|
|
211
|
-
| `team` | Default team key. Precedence: `--team
|
|
212
|
-
| `url` | Target host. `HARNESS_API_URL`
|
|
213
|
-
| `org` | Reserved
|
|
214
|
-
|
|
215
|
-
This is config, not state — issues stay server-side.
|
|
216
|
-
|
|
217
|
-
## Development
|
|
553
|
+
| `team` | Default team key. Precedence: `--team`, then `HARNESS_TEAM`, then the repo config. |
|
|
554
|
+
| `url` | Target host. `HARNESS_API_URL` takes precedence. Before an automatic login opens a browser at a host that only the repo names, the CLI asks for confirmation, once per host. |
|
|
555
|
+
| `org` | Reserved. It is parsed but has no effect yet. |
|
|
218
556
|
|
|
219
|
-
|
|
220
|
-
npm ci
|
|
221
|
-
npm run typecheck
|
|
222
|
-
npm test
|
|
223
|
-
npm run build # bundles to dist/cli.js (tsup)
|
|
224
|
-
node dist/cli.js version
|
|
225
|
-
```
|
|
557
|
+
A malformed file is ignored with a warning on stderr.
|
|
226
558
|
|
|
227
|
-
|
|
228
|
-
generated into `src/generated/` (committed, so npx consumers never run
|
|
229
|
-
codegen):
|
|
559
|
+
### Hosts and tokens
|
|
230
560
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
561
|
+
The host comes from `HARNESS_API_URL`, then the repo config's `url`, then
|
|
562
|
+
the host of the stored login, then `https://harness.infra.one`. Tokens are
|
|
563
|
+
bound to the host that issued them. A stored login is only sent to the host
|
|
564
|
+
that issued it. `HARNESS_API_TOKEN` is only sent to `HARNESS_API_URL`, or to
|
|
565
|
+
the default host when that variable is unset. It is never sent to the stored
|
|
566
|
+
login's host, and when a repo config names another host, the command
|
|
567
|
+
refuses with exit code 2 instead of sending the token there. Set
|
|
568
|
+
`HARNESS_API_URL` explicitly if you do mean that host. Without
|
|
569
|
+
`HARNESS_API_TOKEN`, a repo-named host needs a login of its own, and the
|
|
570
|
+
automatic login asks for confirmation first, as described above. Plain
|
|
571
|
+
`http://` is accepted only for localhost.
|
|
235
572
|
|
|
236
|
-
|
|
573
|
+
### Environment variables
|
|
237
574
|
|
|
238
|
-
|
|
575
|
+
| Variable | Effect |
|
|
576
|
+
| --- | --- |
|
|
577
|
+
| `HARNESS_API_URL` | Target host (https, or http against localhost only). |
|
|
578
|
+
| `HARNESS_API_TOKEN` | An `aone_…` API token. It takes precedence over the stored login. Headless use only. |
|
|
579
|
+
| `HARNESS_ACTOR` | Write attribution, sent only when set. Without it the server attributes writes to the signed-in user, or for `aone_` tokens to the token's name. Headless use only. |
|
|
580
|
+
| `HARNESS_TEAM` | Default team key for commands that take `--team`. |
|
|
581
|
+
| `HARNESS_CACHE_DIR` | Where `show` stores downloaded images. Must be an absolute path. |
|
|
582
|
+
| `HARNESS_NO_UPDATE_CHECK` | Set to a value other than `0`, `false`, or `no` to turn off the update notice and the automatic upgrade. |
|
|
583
|
+
| `HARNESS_AUTO_UPGRADE=0` | Keeps the notice but never installs anything (`false` and `no` also work). |
|
|
584
|
+
| `CI` | Any value other than empty, `0`, `false`, or `no` turns off the update notice and automatic upgrade. Separately, any non-empty value, including `0`, stops the automatic browser login. |
|
|
585
|
+
| `XDG_CONFIG_HOME`, `XDG_CACHE_HOME` | Used instead of `~/.config` and `~/.cache` when set to absolute paths. |
|
|
239
586
|
|
|
240
|
-
|
|
241
|
-
that PR is squash-merged, fetch `main` and tag the merge commit — never the
|
|
242
|
-
pre-merge branch tip. Pushing the tag starts the Release workflow, which builds,
|
|
243
|
-
tests, and publishes to npm:
|
|
587
|
+
### Files
|
|
244
588
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
589
|
+
| Path | Contents |
|
|
590
|
+
| --- | --- |
|
|
591
|
+
| `~/.config/harness/credentials.json` | The stored login: one `{url, token}` pair, mode 0600. |
|
|
592
|
+
| `~/.config/harness/trusted-hosts.json` | Repo-config hosts you agreed to log in to. |
|
|
593
|
+
| `~/.cache/harness/attachments/` | Images downloaded by `show` (or `HARNESS_CACHE_DIR`). |
|
|
594
|
+
| `~/.cache/harness/update-check.json` | The daily update-check stamp. |
|
|
595
|
+
| `~/.cache/harness/upgrade.lock` | Held while an automatic upgrade runs. |
|
|
596
|
+
|
|
597
|
+
None of these files is tracker state: issues stay on the server. The
|
|
598
|
+
attachment cache does hold copies of issue images, so clear it when you no
|
|
599
|
+
longer need them.
|
|
600
|
+
|
|
601
|
+
## Staying current
|
|
602
|
+
|
|
603
|
+
**Notice.** Each run reads the cached stamp and prints at most one stderr
|
|
604
|
+
line when a newer release exists:
|
|
605
|
+
`harness: 1.13.0 is available (you have 1.12.1) — npm i -g @allocator-one/harness-cli@latest`.
|
|
606
|
+
When the stamp is more than a day old, a detached background process
|
|
607
|
+
refreshes it from the npm registry. A command never waits on that network
|
|
608
|
+
request. The notice applies only to global and project-local installs, not
|
|
609
|
+
to `npx` or a git checkout.
|
|
610
|
+
|
|
611
|
+
**Automatic upgrade on HTTP 426.** The CLI sends its version with every
|
|
612
|
+
request, and the server refuses versions below its minimum with a 426. In
|
|
613
|
+
that case the CLI runs
|
|
614
|
+
`npm install --global --ignore-scripts @allocator-one/harness-cli@latest`,
|
|
615
|
+
re-runs the original command, and exits with that command's exit code.
|
|
616
|
+
Re-running is safe only for this status. The server answers 426 before
|
|
617
|
+
authentication and before the request handler, so the refused request
|
|
618
|
+
changed nothing. The CLI never retries any other failure. The upgrade is
|
|
619
|
+
limited by these guards:
|
|
620
|
+
|
|
621
|
+
- It only upgrades a global install, located through `npm root -g`, and
|
|
622
|
+
only when that directory is writable.
|
|
623
|
+
- Only one upgrade runs at a time on a machine (a lock under
|
|
624
|
+
`~/.cache/harness`). A process that finds a newer version already
|
|
625
|
+
installed re-runs with it directly.
|
|
626
|
+
- `--ignore-scripts` means an unattended upgrade never runs package scripts.
|
|
627
|
+
- The re-run carries `HARNESS_UPGRADED=1`, so each command is upgraded at
|
|
628
|
+
most once.
|
|
629
|
+
- It is skipped for `npx`, project-local installs, git checkouts, inside
|
|
630
|
+
`harness mcp`, under `CI`, and when `HARNESS_NO_UPDATE_CHECK` or
|
|
631
|
+
`HARNESS_AUTO_UPGRADE=0` is set.
|
|
632
|
+
|
|
633
|
+
When the upgrade is skipped or fails, the command prints the server's
|
|
634
|
+
message, adds the reason if the upgrade was attempted, and exits `1`.
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
Building, testing, and releasing this package are covered in `AGENTS.md`
|
|
639
|
+
in the (private) source repository.
|