linear-sdk-cli 0.1.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/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +9 -0
- package/CHANGELOG.md +807 -0
- package/COVERAGE.md +495 -0
- package/LICENSE +21 -0
- package/MIGRATING.md +220 -0
- package/README.md +524 -0
- package/package.json +87 -0
- package/skills/linear-sdk-cli/SKILL.md +534 -0
- package/skills/linear-sdk-cli/references/api.md +27 -0
- package/skills/linear-sdk-cli/references/attachment.md +82 -0
- package/skills/linear-sdk-cli/references/auth.md +174 -0
- package/skills/linear-sdk-cli/references/commands.md +37 -0
- package/skills/linear-sdk-cli/references/comment.md +154 -0
- package/skills/linear-sdk-cli/references/completion.md +17 -0
- package/skills/linear-sdk-cli/references/config.md +99 -0
- package/skills/linear-sdk-cli/references/cycle.md +131 -0
- package/skills/linear-sdk-cli/references/document.md +180 -0
- package/skills/linear-sdk-cli/references/favorite.md +78 -0
- package/skills/linear-sdk-cli/references/initiative-update.md +67 -0
- package/skills/linear-sdk-cli/references/initiative.md +256 -0
- package/skills/linear-sdk-cli/references/issue.md +928 -0
- package/skills/linear-sdk-cli/references/label.md +112 -0
- package/skills/linear-sdk-cli/references/milestone.md +139 -0
- package/skills/linear-sdk-cli/references/notification.md +123 -0
- package/skills/linear-sdk-cli/references/open.md +29 -0
- package/skills/linear-sdk-cli/references/organization.md +99 -0
- package/skills/linear-sdk-cli/references/project-update.md +67 -0
- package/skills/linear-sdk-cli/references/project.md +234 -0
- package/skills/linear-sdk-cli/references/roadmap.md +150 -0
- package/skills/linear-sdk-cli/references/schema.md +21 -0
- package/skills/linear-sdk-cli/references/state.md +61 -0
- package/skills/linear-sdk-cli/references/team.md +227 -0
- package/skills/linear-sdk-cli/references/user.md +103 -0
- package/skills/linear-sdk-cli/references/webhook.md +151 -0
- package/skills/linear-sdk-cli/references/whoami.md +26 -0
- package/skills/linear-sdk-cli/scripts/generate-docs.ts +339 -0
- package/src/bin/linear.ts +97 -0
- package/src/cli.ts +232 -0
- package/src/client.ts +172 -0
- package/src/commands/agent-session.ts +121 -0
- package/src/commands/api.ts +317 -0
- package/src/commands/attach.ts +90 -0
- package/src/commands/attachment.ts +74 -0
- package/src/commands/comment.ts +225 -0
- package/src/commands/completion.ts +69 -0
- package/src/commands/cycle.ts +125 -0
- package/src/commands/discover.ts +170 -0
- package/src/commands/document.ts +211 -0
- package/src/commands/favorite.ts +72 -0
- package/src/commands/initiative-update.ts +47 -0
- package/src/commands/initiative.ts +261 -0
- package/src/commands/issue.ts +1263 -0
- package/src/commands/label.ts +119 -0
- package/src/commands/meta.ts +534 -0
- package/src/commands/milestone.ts +163 -0
- package/src/commands/notification.ts +110 -0
- package/src/commands/open.ts +84 -0
- package/src/commands/organization.ts +79 -0
- package/src/commands/project-update.ts +47 -0
- package/src/commands/project.ts +339 -0
- package/src/commands/roadmap.ts +131 -0
- package/src/commands/state.ts +58 -0
- package/src/commands/team.ts +243 -0
- package/src/commands/user.ts +107 -0
- package/src/commands/webhook.ts +156 -0
- package/src/config.ts +1236 -0
- package/src/context.ts +117 -0
- package/src/git.ts +174 -0
- package/src/index.ts +10 -0
- package/src/lib/action.ts +26 -0
- package/src/lib/body.ts +172 -0
- package/src/lib/errors.ts +195 -0
- package/src/lib/introspect.ts +150 -0
- package/src/lib/keyring.ts +270 -0
- package/src/lib/mutation.ts +53 -0
- package/src/lib/open.ts +32 -0
- package/src/lib/options.ts +640 -0
- package/src/lib/output-shapes.ts +562 -0
- package/src/lib/pagination.ts +145 -0
- package/src/lib/prompt.ts +91 -0
- package/src/lib/resolve.ts +596 -0
- package/src/lib/shape.ts +273 -0
- package/src/lib/status-update.ts +91 -0
- package/src/lib/upload.ts +329 -0
- package/src/output/color.ts +52 -0
- package/src/output/format.ts +238 -0
- package/src/output/markdown.ts +133 -0
- package/src/output/pager.ts +131 -0
- package/src/output/sanitize.ts +76 -0
- package/src/output/table.ts +198 -0
- package/src/services/agent-session.ts +289 -0
- package/src/services/attachment.ts +198 -0
- package/src/services/comment.ts +262 -0
- package/src/services/cycle.ts +224 -0
- package/src/services/document.ts +357 -0
- package/src/services/favorite.ts +147 -0
- package/src/services/initiative-update.ts +59 -0
- package/src/services/initiative.ts +449 -0
- package/src/services/issue.ts +1153 -0
- package/src/services/label.ts +193 -0
- package/src/services/milestone.ts +262 -0
- package/src/services/notification.ts +190 -0
- package/src/services/organization.ts +144 -0
- package/src/services/project-update.ts +36 -0
- package/src/services/project.ts +494 -0
- package/src/services/roadmap.ts +177 -0
- package/src/services/state.ts +127 -0
- package/src/services/team.ts +386 -0
- package/src/services/user.ts +137 -0
- package/src/services/webhook.ts +203 -0
package/README.md
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
# linear-sdk-cli
|
|
2
|
+
|
|
3
|
+
**An ergonomic command-line interface for [Linear](https://linear.app), built on the official
|
|
4
|
+
[`@linear/sdk`](https://www.npmjs.com/package/@linear/sdk).**
|
|
5
|
+
|
|
6
|
+
It's designed to be pleasant for humans _and_ dependable for scripts and agents. By default you
|
|
7
|
+
get clean, aligned tables and detail views; add `--json` to any command for a stable,
|
|
8
|
+
machine-readable shape. It's git-aware (the "current issue" comes from your branch name) and
|
|
9
|
+
forgiving about input (`--assignee me`, team key `TES`, state and label by name). Anything the
|
|
10
|
+
curated commands don't wrap is still reachable through a raw GraphQL escape hatch (`linear api`),
|
|
11
|
+
so nothing in the Linear API is out of bounds.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
linear issue list --assignee me --state started # what's on my plate
|
|
15
|
+
linear # the issue for the branch you're on
|
|
16
|
+
linear issue list --json | jq -r '.[].identifier' # ready for scripts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
> Design influenced by [`schpet/linear-cli`](https://github.com/schpet/linear-cli) (human-first,
|
|
20
|
+
> git-aware) and [`linearis`](https://github.com/linearis-oss/linearis) (JSON-first for agents).
|
|
21
|
+
|
|
22
|
+
## Highlights
|
|
23
|
+
|
|
24
|
+
- **Human-first by default** — aligned tables, rendered Markdown, safe paging for long detail,
|
|
25
|
+
color controls, and a clear notice when a list was truncated.
|
|
26
|
+
- **Agent- and script-friendly** — every data command takes `--json` and emits a stable, documented envelope on stdout; status text stays on stderr.
|
|
27
|
+
- **Git-aware** — the current issue is inferred from your branch (`tes-123-fix` → `TES-123`), so most issue commands let you drop the id.
|
|
28
|
+
- **Git + GitHub workflow** — `issue start` checks out the branch and marks the issue started, `issue describe` prints a commit message with Linear-issue trailers, `issue pr` opens a GitHub PR — all linked back to the issue.
|
|
29
|
+
- **Forgiving inputs** — refer to things the way you think of them: `TES-123`, team `TES`, `--assignee me`, `--cycle current`, state and label by name.
|
|
30
|
+
- **Multi-workspace** — store credentials for several workspaces and switch with a global `--workspace`.
|
|
31
|
+
- **Complete & honest** — first-class commands for the core resource graph, a raw `linear api` for everything else, and a [measured coverage audit](#coverage) that CI keeps honest.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
Requires [Bun](https://bun.sh) **1.1 or newer** and a Linear API key
|
|
36
|
+
(Settings → Security & access → **Personal API keys**). The CLI ships as TypeScript and runs
|
|
37
|
+
directly on Bun — no build step, no bundle, no Node.
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
git clone https://github.com/zhendalf/linear-sdk-cli.git
|
|
41
|
+
cd linear-sdk-cli
|
|
42
|
+
bun install
|
|
43
|
+
bun run dev -- --help
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The npm package name has not been published yet. Source installation is the supported route until
|
|
47
|
+
the maintainers choose package ownership and publish the first release.
|
|
48
|
+
|
|
49
|
+
This installs two equivalent binaries: **`linear`** and the shorter **`lin`**. If you already
|
|
50
|
+
have a different tool named `linear` on your `PATH`, just use `lin`.
|
|
51
|
+
|
|
52
|
+
### If you already have `schpet/linear-cli` installed
|
|
53
|
+
|
|
54
|
+
Both CLIs install a binary called **`linear`**, so whichever is earlier on your `PATH` wins and
|
|
55
|
+
the other is silently shadowed — `linear --version` tells you which you got (`0.x` is this one,
|
|
56
|
+
`2.x` is schpet's), and `which -a linear` lists both. Three ways out, pick one:
|
|
57
|
+
|
|
58
|
+
- **Use `lin`.** It is ours alone; nothing of theirs claims it. Every example in this README works
|
|
59
|
+
with `lin` in place of `linear`. Zero-risk for the transition week.
|
|
60
|
+
- **Keep theirs reachable as `linear-schpet`** and let ours have `linear`:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
# installed with deno: reinstall under a new name, then drop the old one
|
|
64
|
+
deno install -A --reload -f -g -n linear-schpet jsr:@schpet/linear-cli
|
|
65
|
+
deno uninstall -g linear
|
|
66
|
+
# installed with homebrew (schpet/tap/linear): keep the keg, relink under a new name
|
|
67
|
+
brew unlink schpet/tap/linear
|
|
68
|
+
ln -s "$(brew --prefix)/opt/linear/bin/linear" ~/.local/bin/linear-schpet # any dir on your PATH
|
|
69
|
+
# installed with npm/bun globally: rename the shim in place
|
|
70
|
+
mv "$(command -v linear)" "$(dirname "$(command -v linear)")/linear-schpet"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
(A package-manager upgrade of theirs may put `linear` back; re-run the line for your channel.)
|
|
74
|
+
|
|
75
|
+
- **Uninstall theirs** once you no longer need it. Your credentials survive: they are in the OS
|
|
76
|
+
keyring under the same service and account, and we read them — see [Authentication](#authentication).
|
|
77
|
+
|
|
78
|
+
Their project-pinned install (`bun add -D @schpet/linear-cli`, run as `bunx linear`) does not
|
|
79
|
+
collide with a global install of ours; inside such a project `bunx linear` is theirs and
|
|
80
|
+
`linear`/`lin` on your `PATH` is ours.
|
|
81
|
+
|
|
82
|
+
<details>
|
|
83
|
+
<summary>Run from source instead</summary>
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
git clone https://github.com/zhendalf/linear-sdk-cli.git && cd linear-sdk-cli
|
|
87
|
+
bun install
|
|
88
|
+
bun run src/bin/linear.ts --help # or: bun run dev -- --help
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
</details>
|
|
92
|
+
|
|
93
|
+
## Quickstart
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
export LINEAR_API_KEY=lin_api_xxxxxxxx # quickest way to get going
|
|
97
|
+
linear whoami # confirm you're connected
|
|
98
|
+
|
|
99
|
+
linear issue list --assignee me --state started # my in-progress work
|
|
100
|
+
linear issue view TES-42 # full detail
|
|
101
|
+
linear issue create --title "Fix login" --team TES -P 2 # new High-priority issue
|
|
102
|
+
linear issue start TES-42 # check out its branch + mark it started
|
|
103
|
+
linear issue comment TES-42 "shipped — please review"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
In a git repository, bare `linear` (and `linear issue`) shows the issue inferred from the current
|
|
107
|
+
branch, so most issue commands let you omit the id entirely.
|
|
108
|
+
|
|
109
|
+
## Authentication
|
|
110
|
+
|
|
111
|
+
The CLI resolves your API key in this order: the `--api-key` flag → the `LINEAR_API_KEY`
|
|
112
|
+
environment variable → a plaintext key in the user config file (`~/.config/linear/config.toml`,
|
|
113
|
+
written `0600`) → the **OS keyring** (macOS Keychain, or `secret-tool` on Linux).
|
|
114
|
+
|
|
115
|
+
> **Credential trust boundary.** The API key is **never** read from a project-local
|
|
116
|
+
> `.linear.toml` — only non-secret settings live there — so a key can't be committed by accident.
|
|
117
|
+
> A project may select one of your already-stored credentials with `workspace = "<slug>"`, but it
|
|
118
|
+
> cannot provide or override the secret.
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
linear auth login # store a key (prompts, validates, saves to the keyring)
|
|
122
|
+
linear auth login --plaintext # …or keep it in the 0600 config file instead
|
|
123
|
+
linear auth status # where the key came from (value redacted)
|
|
124
|
+
linear auth migrate # move plaintext keys from the file into the keyring
|
|
125
|
+
linear auth token # print the resolved key (for scripting)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`auth login` stores the secret in the system keyring by default and writes only a
|
|
129
|
+
`keyring = true` marker to the config file; where there is no keyring (other platforms, or a Linux
|
|
130
|
+
box without `libsecret`) it falls back to the file, and says so. The keyring entry is
|
|
131
|
+
`service = linear-cli`, `account = <workspace slug>` — the same convention as
|
|
132
|
+
[schpet/linear-cli](https://github.com/schpet/linear-cli), so if you are coming from it your key is
|
|
133
|
+
found without a re-login (`auth status` reports `Source: keychain`). Note the entry is shared:
|
|
134
|
+
`auth logout` here removes it for both tools. `--key -` reads the key from stdin for scripts;
|
|
135
|
+
passing it as `--key <value>` works but earns a warning, since argv is visible to other processes.
|
|
136
|
+
|
|
137
|
+
### Multiple workspaces
|
|
138
|
+
|
|
139
|
+
Credentials are stored per **workspace slug**. `auth login` validates the key and derives the slug
|
|
140
|
+
from the key's organization unless you pass `--workspace`:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
linear auth login --workspace acme # store a key for the "acme" workspace
|
|
144
|
+
linear auth login --workspace other-org # …and another
|
|
145
|
+
linear auth list # show configured workspaces + which is default
|
|
146
|
+
linear auth default acme # choose the default workspace
|
|
147
|
+
linear --workspace other-org issue list # use a specific workspace for one command
|
|
148
|
+
linear auth logout --workspace acme # remove one credential
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Selection precedence** (strict): the `--api-key` flag and `LINEAR_API_KEY` env are absolute and
|
|
152
|
+
bypass selection entirely; otherwise the workspace is chosen by `--workspace` → `LINEAR_WORKSPACE`
|
|
153
|
+
env → project config `workspace` → `default_workspace` in the user config. With one configured
|
|
154
|
+
workspace it's used automatically; with several and no selection, the CLI asks you to pick (via
|
|
155
|
+
`--workspace`, project config, or `auth default`).
|
|
156
|
+
|
|
157
|
+
## Core concepts
|
|
158
|
+
|
|
159
|
+
A few ideas run through every command:
|
|
160
|
+
|
|
161
|
+
- **Git-branch awareness.** On a `tes-123-*` branch, bare `linear` shows `TES-123` (identical to
|
|
162
|
+
`issue view TES-123`), and nearly every issue subcommand infers the id from the branch — so
|
|
163
|
+
`linear issue comment "…"`, `linear issue start`, `linear issue pr` all "just work" in context.
|
|
164
|
+
- **Human by default, `--json` for machines.** Without `--json` you get tables and detail views
|
|
165
|
+
meant to be read. With `--json`, stdout carries _only_ machine JSON (a [stable
|
|
166
|
+
envelope](#scripting--agents)); status and progress always go to stderr.
|
|
167
|
+
- **Forgiving inputs.** Resolve things by how you think about them: `--assignee me`, assignee by
|
|
168
|
+
email or name, team key `TES`, `--cycle current`, workflow state and label by name (case-
|
|
169
|
+
insensitively). Ambiguous names produce a clear error (exit `3`), not a wrong guess.
|
|
170
|
+
|
|
171
|
+
## Common workflows
|
|
172
|
+
|
|
173
|
+
**Issue lifecycle**
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
linear issue create --title "Fix login redirect" --team TES -P 2 --assignee me
|
|
177
|
+
linear issue list --assignee me --state started
|
|
178
|
+
linear issue update TES-42 --state "In Review" --add-label backend
|
|
179
|
+
linear issue comment TES-42 "ready for another look"
|
|
180
|
+
linear issue archive TES-42 --yes
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Git + GitHub PR** — turn an issue into commits and a pull request. The id is inferred from the
|
|
184
|
+
branch everywhere below.
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
linear issue start TES-123 # checkout tes-123-* branch and mark it started (--no-move: branch only)
|
|
188
|
+
git commit -m "$(linear issue describe)" # "TES-123 Title" + Linear-issue trailers
|
|
189
|
+
linear issue pr # open a GitHub PR titled "TES-123 Title", body = the trailers
|
|
190
|
+
linear issue pr --draft --base main # …as a draft against a specific base
|
|
191
|
+
linear issue pr --json | jq -r .url # the created PR URL is the only thing on stdout
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`issue describe` prints a whole commit message — `TES-123 Title`, a blank line, then two git
|
|
195
|
+
trailers, `Linear-issue: Fixes TES-123` and `Linear-issue-url: <url>` (the same text
|
|
196
|
+
schpet/linear-cli prints, so `git interpret-trailers` and jj read it back). The magic word sits
|
|
197
|
+
right before the id, which is where Linear's
|
|
198
|
+
[git integration](https://linear.app/docs/github#link-prs-and-commits) reads it, so the issue is
|
|
199
|
+
linked — and closed on merge — when the commit lands; `-r` swaps in `References` to link without
|
|
200
|
+
closing. `issue pull-request` (alias `pr`) opens the PR via the [`gh`](https://cli.github.com) CLI:
|
|
201
|
+
title `TES-123 Title` (a custom `--title` is prefixed the same way), body the same two trailers,
|
|
202
|
+
so the PR and issue reference each other; the issue description stays in Linear. It never pushes
|
|
203
|
+
or creates branches for you, and fails with a clear error (not a stack trace) when `gh` is
|
|
204
|
+
missing, unauthenticated, or the branch isn't pushed.
|
|
205
|
+
|
|
206
|
+
**Projects & status updates**
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
linear project list --team TES
|
|
210
|
+
linear project view "Q3 Launch"
|
|
211
|
+
linear project-update create "Q3 Launch" --health onTrack --body "Beta is out to 10% of users."
|
|
212
|
+
linear initiative-update create "Platform" --health atRisk --body-file update.md
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Status updates (`project-update`/`pu`, `initiative-update`/`iu`) take the body from `--body`,
|
|
216
|
+
`--body-file <path>` (`-` for stdin), or `--editor` (`$EDITOR`), plus an optional
|
|
217
|
+
`--health <onTrack|atRisk|offTrack>`.
|
|
218
|
+
|
|
219
|
+
## Command overview
|
|
220
|
+
|
|
221
|
+
Every group has `--help` with full options and (for the busy ones) an Examples section. Aliases
|
|
222
|
+
are shown in parentheses. For a machine-readable tree of _every_ command, run
|
|
223
|
+
`linear commands --json`.
|
|
224
|
+
|
|
225
|
+
| Group | What you can do |
|
|
226
|
+
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
227
|
+
| **`issue`** (`i`) | `view` · `list` · `mine` · `search` · `create` · `update` · `delete` · `archive`/`unarchive` · `start` (git branch) · `describe` · `pull-request`/`pr` · `assign` · `state` · `label` · `comment`/`comments` · `relation` · `subscribe`/`unsubscribe` · `id`/`title`/`url`/`branch` |
|
|
228
|
+
| **`team`** (`t`) | `list` · `view` · `members` · `states` · `labels` · `cycles` · `create` · `update` |
|
|
229
|
+
| **`project`** (`p`) | `list` · `view` · `create` · `update` · `archive` · `milestones` |
|
|
230
|
+
| **`project-update`** (`pu`) | `create` · `list` (project status updates, with `--health`) |
|
|
231
|
+
| **`milestone`** (`m`) | `list` · `view` · `create` · `update` · `delete` |
|
|
232
|
+
| **`cycle`** (`c`) | `list` · `view` · `current` · `create` · `update` |
|
|
233
|
+
| **`user`** (`u`) | `list` · `view` · `me` |
|
|
234
|
+
| **`label`** (`lb`) | `list` · `create` · `update` · `delete` |
|
|
235
|
+
| **`state`** (`st`) | `list` · `view` (workflow states) |
|
|
236
|
+
| **`comment`** (`cm`) | `list` · `add` · `reply` · `update` · `delete` · `resolve`/`unresolve` |
|
|
237
|
+
| **`document`** (`doc`) | `list` · `view` · `create` · `update` · `delete` |
|
|
238
|
+
| **`attachment`** (`at`) | `list` · `create` · `delete` |
|
|
239
|
+
| **`favorite`** (`fav`) | `list` · `add` · `remove` |
|
|
240
|
+
| **`initiative`** (`init`) | `list` · `view` · `create` · `update` · `archive` · `delete` |
|
|
241
|
+
| **`initiative-update`** (`iu`) | `create` · `list` (initiative status updates, with `--health`) |
|
|
242
|
+
| **`roadmap`** (`rm`) | `list` · `view` · `create` · `update` · `delete` <sup>†</sup> |
|
|
243
|
+
| **`notification`** (`notif`) | `list` · `read`/`unread` · `read-all` · `archive` · `snooze` |
|
|
244
|
+
| **`organization`** (`org`) | `view` · `members` · `invites` |
|
|
245
|
+
| **`webhook`** (`wh`) | `list` · `view` · `create` · `update` · `delete` |
|
|
246
|
+
| **top-level** | `whoami` · `auth` (`login` · `list` · `default` · `token` · `status` · `logout`) · `config` · `api` · `commands` · `schema` · `completion` |
|
|
247
|
+
|
|
248
|
+
<sup>†</sup> Linear has **deprecated roadmaps** in favor of initiatives — reads still work, but the
|
|
249
|
+
API rejects roadmap mutations with a deprecation notice. Use `initiative` for new work.
|
|
250
|
+
|
|
251
|
+
### Global flags
|
|
252
|
+
|
|
253
|
+
| Flag | Effect |
|
|
254
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
255
|
+
| `--json` | Emit machine JSON only on stdout (see [the contract](#scripting--agents)). |
|
|
256
|
+
| `-f, --fields a,b,c` | Choose which columns to show (table output), detail lines, or JSON keys. Refused on a command that prints only a receipt. |
|
|
257
|
+
| `-n, --limit <n>` / `--all` | Cap results, or fetch every page. Refused on a command that pages nothing. |
|
|
258
|
+
| `-t, --team <key>` | Set the default team for the command. |
|
|
259
|
+
| `--workspace <slug>` | Select which stored workspace credential to use. |
|
|
260
|
+
| `-y, --yes` | Skip confirmation prompts (required for destructive actions when not a TTY). |
|
|
261
|
+
| `--no-input` | Never prompt; fail with a usage error instead of hanging. |
|
|
262
|
+
| `--no-ansi` | Disable colored output (`--no-color` is accepted as an alias). |
|
|
263
|
+
| `-q, --quiet` · `--debug` | Silence status output · verbose errors. |
|
|
264
|
+
|
|
265
|
+
## Scripting & agents
|
|
266
|
+
|
|
267
|
+
The CLI is built to be driven by scripts and agents. Everything in this section is a stable
|
|
268
|
+
contract.
|
|
269
|
+
|
|
270
|
+
**The `--json` envelope.** With `--json`, stdout carries _only_ machine JSON, pretty-printed, one
|
|
271
|
+
value per command:
|
|
272
|
+
|
|
273
|
+
- **list** commands emit a **bare array** (`[...]`) — even when empty (`[]`) and even for a single result.
|
|
274
|
+
- **single-resource** commands (`view`, `whoami`, …) emit a **bare object** (`{...}`).
|
|
275
|
+
- **mutations** (`create`/`update`/`delete`/`archive`/…) emit the affected object — typically a
|
|
276
|
+
small shape like `{ "id", "identifier", "url" }`, or `{ "id", "success": true }` when the API
|
|
277
|
+
returns no body. Destructive commands add a flag such as `{ "deleted": true }` / `{ "archived": true }`.
|
|
278
|
+
- **errors** go to **stderr** as `{"error":{"message":"…","code":"…"}}` and never to stdout. With
|
|
279
|
+
`--debug`, the extra detail is carried _inside_ that object as `error.detail` rather than appended
|
|
280
|
+
after it, so `--json --debug` output stays parseable.
|
|
281
|
+
|
|
282
|
+
Status, progress, and pagination notes always go to **stderr**, so `cmd --json` is safe to pipe
|
|
283
|
+
into `jq` unconditionally:
|
|
284
|
+
|
|
285
|
+
```sh
|
|
286
|
+
linear issue list --json | jq -r '.[].identifier'
|
|
287
|
+
linear issue view TES-42 --json | jq -r '.url'
|
|
288
|
+
ID=$(linear issue create --title "Fix" --team TES --json | jq -r '.id')
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Exit codes** are stable and distinct, so a script can branch on the failure class:
|
|
292
|
+
|
|
293
|
+
| Code | Name | When |
|
|
294
|
+
| ---: | ------------------- | ------------------------------------------------------------------- |
|
|
295
|
+
| `0` | ok | success |
|
|
296
|
+
| `1` | runtime/API | network/GraphQL/other runtime failure (also feature-not-accessible) |
|
|
297
|
+
| `2` | usage | bad flags/arguments, missing required input, validation |
|
|
298
|
+
| `3` | not-found/ambiguous | the referenced resource doesn't exist, or a name matched many |
|
|
299
|
+
| `4` | auth | missing/invalid API key, or forbidden |
|
|
300
|
+
| `5` | rate-limited | Linear rate limit hit |
|
|
301
|
+
| `6` | cancelled | a confirmation prompt was declined — nothing was changed |
|
|
302
|
+
|
|
303
|
+
The error `code` field in the JSON envelope is one of: `usage`, `auth`, `not_found`, `ambiguous`,
|
|
304
|
+
`forbidden`, `validation`, `rate_limited`, `network`, `feature_not_accessible`, `api`, `runtime`.
|
|
305
|
+
Several map to the same exit code (e.g. `ambiguous` → `3`, `validation` → `2`, `forbidden` → `4`),
|
|
306
|
+
so prefer the `code` field for fine-grained handling and the exit code for coarse branching.
|
|
307
|
+
|
|
308
|
+
**Non-interactive flags** make runs deterministic in CI and agent loops:
|
|
309
|
+
|
|
310
|
+
- **`--no-input`** — never prompt; anything that would be prompted for becomes a usage error
|
|
311
|
+
(exit `2`) instead of hanging. Use this whenever there's no human at the keyboard. **`--json`
|
|
312
|
+
implies this**, as does a stdout that is not a TTY: a prompt inside a pipeline is a hang, not a
|
|
313
|
+
question, so you never have to remember both flags.
|
|
314
|
+
- **`-y, --yes`** — pre-confirm destructive actions (`delete`/`archive`). Without a TTY,
|
|
315
|
+
destructive commands _require_ `--yes` (they refuse rather than block). If a human declines the
|
|
316
|
+
prompt, the command exits **`6`** and reports `{"cancelled": true, "action": "…"}` under `--json`
|
|
317
|
+
— never `0`, so `linear issue delete X && …` cannot run the `&&` side after a "no".
|
|
318
|
+
- **`-q, --quiet`** — suppress success/status lines on stderr (errors still print).
|
|
319
|
+
- **`-n, --limit <n>` / `--all`** — `--limit` caps results; `--all` exhausts pagination. With
|
|
320
|
+
neither, the default cap is **50**. `--all` (and very large `--limit`) can be slow and
|
|
321
|
+
rate-limit-prone on big workspaces.
|
|
322
|
+
|
|
323
|
+
```sh
|
|
324
|
+
# agent-safe: no prompts, no chatter, fail fast with a parseable error
|
|
325
|
+
linear issue delete TES-42 --yes --no-input --quiet --json
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Discovery — learn the surface without scraping `--help`:**
|
|
329
|
+
|
|
330
|
+
- **`linear commands --json`** — a bare array of `{ path, description, aliases, arguments, options }`
|
|
331
|
+
for every (sub)command, so an agent can enumerate what's available and how to call it.
|
|
332
|
+
- **`linear schema`** — the Linear GraphQL schema as SDL (`-o, --output <file>` writes to a file;
|
|
333
|
+
`--json` prints raw introspection). Pair it with `linear api` to reach anything the curated
|
|
334
|
+
commands don't wrap.
|
|
335
|
+
|
|
336
|
+
```sh
|
|
337
|
+
linear commands --json | jq -r '.[].path' # every command path
|
|
338
|
+
linear schema -o /tmp/linear.graphql # dump SDL, then explore
|
|
339
|
+
grep 'type Issue ' /tmp/linear.graphql
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**File-based bodies & stdin.** Long text (issue descriptions, comments, status updates) can come
|
|
343
|
+
from a file or stdin instead of a flag — e.g. `--body-file <path>` with `-` for stdin, or
|
|
344
|
+
`--editor` to open `$EDITOR`. The raw `linear api` reads from `--query-file -` and `--vars-file -`
|
|
345
|
+
too, so you can pipe GraphQL straight in. Inline bodies containing literal `\n` sequences are
|
|
346
|
+
rejected with this guidance: ordinary shell quotes do not convert them into line breaks.
|
|
347
|
+
|
|
348
|
+
**Agent skill.** This repo ships a Claude agent skill at `skills/linear-sdk-cli/` that teaches an
|
|
349
|
+
agent to drive the CLI — the JSON envelope, exit codes, discovery, forgiving inputs, and how to
|
|
350
|
+
install the CLI itself if it is missing. Three ways to install it:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
# Claude Code — as a plugin (the repo is its own marketplace)
|
|
354
|
+
claude plugin marketplace add <owner>/linear-sdk-cli
|
|
355
|
+
claude plugin install linear-sdk-cli
|
|
356
|
+
|
|
357
|
+
# any agent that reads SKILL.md — the package ships skills/, so from a clone or an install:
|
|
358
|
+
ln -s "$PWD/skills/linear-sdk-cli" ~/.claude/skills/linear-sdk-cli
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The skill is self-contained: an agent that has only the skill is told to install from a source
|
|
362
|
+
clone until the first npm release, and one arriving from `schpet/linear-cli` is told not to
|
|
363
|
+
re-enter a key before `linear auth status`, since existing credentials are found. `linear commands
|
|
364
|
+
--json` gives it the full command surface at runtime, so the reference docs are a starting point,
|
|
365
|
+
not a cage.
|
|
366
|
+
|
|
367
|
+
## Coming from linear-cli
|
|
368
|
+
|
|
369
|
+
> The full guide — install without a collision, credentials found without a re-login, config
|
|
370
|
+
> read from the same files, and the three places the two CLIs would silently differ — is
|
|
371
|
+
> [`MIGRATING.md`](MIGRATING.md). This section is the flag-level cheat sheet.
|
|
372
|
+
|
|
373
|
+
If your fingers or your scripts learned the other `linear-cli`, most of its spellings work here
|
|
374
|
+
unchanged. The left column is theirs, the right is the canonical one this CLI documents and prints
|
|
375
|
+
in `--help`; both are accepted, and passing both at once is a usage error rather than a silent pick.
|
|
376
|
+
|
|
377
|
+
| linear-cli | here | where |
|
|
378
|
+
| ----------------------------------------- | ---------------------- | -------------------------------------------------- |
|
|
379
|
+
| `-j, --json` | same | every command (global) |
|
|
380
|
+
| `-w, --web` | same | `issue view`, `issue pull-request` |
|
|
381
|
+
| `--due-date` | `--due` | `issue create`, `issue update` |
|
|
382
|
+
| `--target-date` | `--target` | `project`/`milestone`/`initiative` create & update |
|
|
383
|
+
| `--start-date` | `--start` | `project create`, `project update` |
|
|
384
|
+
| `--search` | `--query` | `issue list`, `issue mine` |
|
|
385
|
+
| `--status` | `--state` | `project list` |
|
|
386
|
+
| `--all-states` | (no-op) | `issue list` — it already spans every state |
|
|
387
|
+
| `--limit 0` | `--all` | every list; `--all` is the spelling we teach |
|
|
388
|
+
| `--assignee self` | `me` / `@me` | anywhere a user is named |
|
|
389
|
+
| `--cycle active` | `current` | anywhere a cycle is named |
|
|
390
|
+
| `--cycle "<name>"` | number, name, or id | all three resolve |
|
|
391
|
+
| `issue query` | `issue list` | same command |
|
|
392
|
+
| `auth whoami` | `whoami` | both spellings registered |
|
|
393
|
+
| `issue comment add\|list\|update\|delete` | `comment add\|list\|…` | both mounted on one implementation |
|
|
394
|
+
|
|
395
|
+
Their query filters all exist here too, under the same names — `issue list`, `issue mine` and
|
|
396
|
+
`issue search` share one filter set:
|
|
397
|
+
|
|
398
|
+
| linear-cli | here | notes |
|
|
399
|
+
| ------------------------------------ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
400
|
+
| `-U, --unassigned` | same | `issue list`/`search`; passing it with `--assignee` is a usage error |
|
|
401
|
+
| `--team A --team B` | same | repeatable **on the three issue queries only**; elsewhere `--team` is the single default-team global |
|
|
402
|
+
| `--state a --state b` | same | repeatable; several states OR together (an issue is in one state), and each value is a state name _or_ type |
|
|
403
|
+
| `--created-after`, `--updated-after` | same | `YYYY-MM-DD` or ISO 8601, inclusive; a malformed date is rejected locally instead of returning an empty list |
|
|
404
|
+
| `--project-label` | same | matches the _project's_ label; mutually exclusive with `--project` |
|
|
405
|
+
| `--milestone` | same | theirs requires `--project`; here that scoping is optional — without it the milestone is matched by name across projects |
|
|
406
|
+
| `--search-comments` | `--search-comments` | `issue search` only — the plain list query has nowhere to put it |
|
|
407
|
+
| `issue update --team` | same | a real team move: the issue is renumbered, and Linear remaps its state while dropping the cycle, team-scoped labels and any project the new team is not part of |
|
|
408
|
+
|
|
409
|
+
Four differences we deliberately did **not** adopt (see `ALIGNMENT.md` for the reasoning): their
|
|
410
|
+
`issue list` is an alias of `mine` (a `list` that silently filters to you and hides started work is
|
|
411
|
+
the worst transition hazard, so we added `issue mine` instead of changing `list`); their JSON shape
|
|
412
|
+
wraps results in connection envelopes and `mine` has no `--json` at all (our uniform bare
|
|
413
|
+
array/object is the point); their short flags are reassigned per command (`-t` is both `--title`
|
|
414
|
+
and `--team` in their own tree, so there is no coherent target to match); and their per-command
|
|
415
|
+
option model, where we keep true globals.
|
|
416
|
+
|
|
417
|
+
## Configuration
|
|
418
|
+
|
|
419
|
+
Non-secret defaults live in `~/.config/linear/config.toml` (user-wide) or a project-local config
|
|
420
|
+
file. **Secrets never go in a project file** — the API key is only ever read from the flag, the
|
|
421
|
+
env, the user config, or the OS keyring.
|
|
422
|
+
|
|
423
|
+
Precedence, highest first — `linear config` prints each value with the tier and file it came from:
|
|
424
|
+
|
|
425
|
+
1. the flag (`--team`, `--workspace`, `--sort`)
|
|
426
|
+
2. the environment (`LINEAR_TEAM`/`LINEAR_TEAM_ID`, `LINEAR_WORKSPACE`, `LINEAR_ISSUE_SORT`)
|
|
427
|
+
3. the **project config**: the first file found walking up from the working directory, checking in
|
|
428
|
+
each directory `linear.toml`, then `.linear.toml`, then `.config/linear.toml` — the same
|
|
429
|
+
names and order as [schpet/linear-cli](https://github.com/schpet/linear-cli), so a repo set
|
|
430
|
+
up with its `linear config` (which writes `<git root>/.config/linear.toml`) is picked up as-is
|
|
431
|
+
4. the **user config**, `~/.config/linear/config.toml` (`$XDG_CONFIG_HOME` honored)
|
|
432
|
+
5. schpet/linear-cli's **global config**, `~/.config/linear/linear.toml` — read for non-secret
|
|
433
|
+
settings only, so a migrating user's defaults carry over
|
|
434
|
+
|
|
435
|
+
Keys: `team` (or `team_id`), `workspace`, and `sort` (or `issue_sort`). You can write these from
|
|
436
|
+
the CLI — comments and layout in an existing file are preserved, and the write is atomic:
|
|
437
|
+
|
|
438
|
+
```sh
|
|
439
|
+
linear config init # pick a team from a list → <git root>/.linear.toml
|
|
440
|
+
linear config init --team TES # …or say which (scripts); --sort, --path, --force
|
|
441
|
+
linear config set sort updated # edit one key in the project config in effect
|
|
442
|
+
linear config set team ENG --user # …or in ~/.config/linear/config.toml
|
|
443
|
+
linear config # show the result, with each value's source
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`config set` will not write `api_key` or anything else that belongs to the credential store — that
|
|
447
|
+
is what `auth login` is for.
|
|
448
|
+
|
|
449
|
+
```toml
|
|
450
|
+
# ~/.config/linear/config.toml
|
|
451
|
+
default_workspace = "acme" # which stored credential is active
|
|
452
|
+
team = "TES" # default team key
|
|
453
|
+
sort = "priority" # default issue-list sort: priority | updated | created
|
|
454
|
+
|
|
455
|
+
[workspaces."acme"] # per-workspace credentials (hyphenated slugs are quoted)
|
|
456
|
+
keyring = true # secret lives in the OS keyring (service linear-cli, account acme)
|
|
457
|
+
[workspaces."other-org"]
|
|
458
|
+
api_key = "lin_api_yyyyyyyy" # …or, with `auth login --plaintext`, in this 0600 file
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Relevant environment variables: **`LINEAR_API_KEY`** (absolute — bypasses workspace selection) and
|
|
462
|
+
**`LINEAR_WORKSPACE`** (selects a stored credential when no flag is given).
|
|
463
|
+
|
|
464
|
+
### Shell completion
|
|
465
|
+
|
|
466
|
+
```sh
|
|
467
|
+
source <(linear completion bash) # bash — add to ~/.bashrc
|
|
468
|
+
source <(linear completion zsh) # zsh — add to ~/.zshrc
|
|
469
|
+
linear completion fish > ~/.config/fish/completions/linear.fish
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## Raw API escape hatch
|
|
473
|
+
|
|
474
|
+
Anything without a tailored command is reachable through raw GraphQL — queries or mutations, from
|
|
475
|
+
an argument, a file, or stdin, with variables and optional auto-pagination:
|
|
476
|
+
|
|
477
|
+
```sh
|
|
478
|
+
linear api '{ viewer { id name } }'
|
|
479
|
+
linear api 'query($id:String!){ issue(id:$id){ title } }' --var id=TES-1
|
|
480
|
+
echo '{ teams { nodes { key name } } }' | linear api --query-file -
|
|
481
|
+
linear api --query-file q.graphql --vars-file vars.json --paginate
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Use `linear schema` to discover types and fields first, then reach for `linear api`.
|
|
485
|
+
|
|
486
|
+
### Coverage
|
|
487
|
+
|
|
488
|
+
Coverage of the SDK is **measured, not asserted.** `linear api` reaches the full GraphQL API, and
|
|
489
|
+
a generated audit ([COVERAGE.md](./COVERAGE.md)) classifies every one of the ~460 `LinearClient`
|
|
490
|
+
members as `curated` (a first-class command), `raw-only` (reachable via `linear api`), or
|
|
491
|
+
`excluded` (admin/integration/SDK plumbing). CI fails on any drift from the committed snapshot, so
|
|
492
|
+
the claim stays honest as the SDK evolves.
|
|
493
|
+
|
|
494
|
+
## Programmatic use
|
|
495
|
+
|
|
496
|
+
The CLI can also be embedded:
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
import { createProgram } from "linear-sdk-cli";
|
|
500
|
+
|
|
501
|
+
await createProgram().parseAsync(["node", "linear", "issue", "list", "--json"]);
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Development
|
|
505
|
+
|
|
506
|
+
```sh
|
|
507
|
+
bun run verify # typecheck + lint + unit/contract tests
|
|
508
|
+
bun run test:live # live integration tests (needs LINEAR_API_KEY + LINEAR_CLI_LIVE=1)
|
|
509
|
+
bun run test:live:admin # also runs admin-tier suites (e.g. team create/update)
|
|
510
|
+
bun run audit:coverage # regenerate COVERAGE.md (add --update to re-baseline the snapshot)
|
|
511
|
+
bun run janitor # sweep leaked `clitest-` fixtures from the test workspace
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Architecture is three layers — **commands** (commander wiring) → **services** (one module per
|
|
515
|
+
resource, the only place that touches the SDK) → **`@linear/sdk`**. The machine JSON envelope is
|
|
516
|
+
locked and contract-tested so output never silently drifts.
|
|
517
|
+
|
|
518
|
+
> Live integration tests run against a real workspace and share one API key; running the entire
|
|
519
|
+
> suite repeatedly can hit Linear's rate limit. Run a subset (a few files at a time) or re-run
|
|
520
|
+
> after a short pause if you see transient rate-limit failures.
|
|
521
|
+
|
|
522
|
+
## License
|
|
523
|
+
|
|
524
|
+
[MIT](./LICENSE) © Eugene Beloded
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "linear-sdk-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ergonomic command-line interface for Linear, built on @linear/sdk",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Eugene Beloded <belodedevgenij@gmail.com>",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/zhendalf/linear-sdk-cli.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/zhendalf/linear-sdk-cli#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/zhendalf/linear-sdk-cli/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"linear",
|
|
18
|
+
"cli",
|
|
19
|
+
"linear-app",
|
|
20
|
+
"issue-tracker",
|
|
21
|
+
"graphql"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"linear": "./src/bin/linear.ts",
|
|
25
|
+
"lin": "./src/bin/linear.ts"
|
|
26
|
+
},
|
|
27
|
+
"main": "./src/index.ts",
|
|
28
|
+
"types": "./src/index.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./src/index.ts",
|
|
32
|
+
"bun": "./src/index.ts",
|
|
33
|
+
"import": "./src/index.ts"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"files": [
|
|
39
|
+
".claude-plugin",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"COVERAGE.md",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"MIGRATING.md",
|
|
44
|
+
"README.md",
|
|
45
|
+
"skills",
|
|
46
|
+
"src"
|
|
47
|
+
],
|
|
48
|
+
"engines": {
|
|
49
|
+
"bun": ">=1.1.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"prepublishOnly": "bun run verify",
|
|
53
|
+
"dev": "bun run src/bin/linear.ts",
|
|
54
|
+
"lin": "bun run src/bin/linear.ts",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"lint": "eslint .",
|
|
57
|
+
"format": "prettier --write .",
|
|
58
|
+
"format:check": "prettier --check .",
|
|
59
|
+
"test": "bun test test/unit test/contract",
|
|
60
|
+
"test:unit": "bun test test/unit",
|
|
61
|
+
"test:contract": "bun test test/contract",
|
|
62
|
+
"test:live": "LINEAR_CLI_LIVE=1 bun test --timeout 60000 test/integration",
|
|
63
|
+
"test:live:admin": "LINEAR_CLI_LIVE=1 LINEAR_CLI_LIVE_ADMIN=1 bun test --timeout 60000 test/integration",
|
|
64
|
+
"verify": "bun run typecheck && bun run lint && bun run audit:coverage && bun run format:check && bun run test",
|
|
65
|
+
"skill:docs": "bun run skills/linear-sdk-cli/scripts/generate-docs.ts",
|
|
66
|
+
"audit:coverage": "bun run scripts/coverage-audit.ts",
|
|
67
|
+
"janitor": "bun run test/janitor.ts"
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@inquirer/prompts": "^8.5.2",
|
|
71
|
+
"@linear/sdk": "^89.0.0",
|
|
72
|
+
"commander": "^15.0.0",
|
|
73
|
+
"graphql": "^17.0.2",
|
|
74
|
+
"picocolors": "^1.1.1",
|
|
75
|
+
"smol-toml": "^1.8.0"
|
|
76
|
+
},
|
|
77
|
+
"packageManager": "bun@1.3.14",
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^10.0.1",
|
|
80
|
+
"@types/bun": "^1.3.14",
|
|
81
|
+
"@types/node": "^26.2.0",
|
|
82
|
+
"eslint": "^10.8.1",
|
|
83
|
+
"prettier": "^3.9.6",
|
|
84
|
+
"typescript": "^6.0.3",
|
|
85
|
+
"typescript-eslint": "^8.67.0"
|
|
86
|
+
}
|
|
87
|
+
}
|