linear-sdk-cli 0.2.3 → 0.3.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/MIGRATING.md +7 -6
  3. package/README.md +114 -31
  4. package/package.json +5 -5
  5. package/skills/linear-sdk-cli/SKILL.md +43 -8
  6. package/skills/linear-sdk-cli/references/api.md +1 -1
  7. package/skills/linear-sdk-cli/references/attachment.md +1 -1
  8. package/skills/linear-sdk-cli/references/auth.md +26 -7
  9. package/skills/linear-sdk-cli/references/commands.md +1 -1
  10. package/skills/linear-sdk-cli/references/comment.md +1 -1
  11. package/skills/linear-sdk-cli/references/completion.md +1 -1
  12. package/skills/linear-sdk-cli/references/config.md +5 -1
  13. package/skills/linear-sdk-cli/references/cycle.md +1 -1
  14. package/skills/linear-sdk-cli/references/document.md +1 -1
  15. package/skills/linear-sdk-cli/references/favorite.md +1 -1
  16. package/skills/linear-sdk-cli/references/initiative-update.md +1 -1
  17. package/skills/linear-sdk-cli/references/initiative.md +1 -1
  18. package/skills/linear-sdk-cli/references/issue.md +1 -1
  19. package/skills/linear-sdk-cli/references/label.md +1 -1
  20. package/skills/linear-sdk-cli/references/milestone.md +1 -1
  21. package/skills/linear-sdk-cli/references/notification.md +1 -1
  22. package/skills/linear-sdk-cli/references/open.md +1 -1
  23. package/skills/linear-sdk-cli/references/organization.md +1 -1
  24. package/skills/linear-sdk-cli/references/project-update.md +1 -1
  25. package/skills/linear-sdk-cli/references/project.md +1 -1
  26. package/skills/linear-sdk-cli/references/roadmap.md +1 -1
  27. package/skills/linear-sdk-cli/references/schema.md +1 -1
  28. package/skills/linear-sdk-cli/references/state.md +1 -1
  29. package/skills/linear-sdk-cli/references/team.md +1 -1
  30. package/skills/linear-sdk-cli/references/user.md +1 -1
  31. package/skills/linear-sdk-cli/references/webhook.md +1 -1
  32. package/skills/linear-sdk-cli/references/whoami.md +1 -1
  33. package/skills/linear-sdk-cli/scripts/generate-docs.ts +11 -10
  34. package/src/client.ts +57 -2
  35. package/src/commands/meta.ts +240 -65
  36. package/src/config.ts +319 -16
  37. package/src/context.ts +2 -0
  38. package/src/index.ts +19 -0
  39. package/src/lib/introspect.ts +1 -0
  40. package/src/lib/options.ts +1 -0
  41. package/src/lib/output-shapes.ts +21 -2
  42. package/src/lib/pagination.ts +3 -1
  43. package/src/oauth.ts +582 -0
@@ -4,7 +4,7 @@
4
4
 
5
5
  _Generated from `linear commands --json`. `linear open --help` (or `<subcommand> --help`) is authoritative._
6
6
 
7
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
7
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
8
8
 
9
9
  ### `linear open`
10
10
 
@@ -6,7 +6,7 @@ Group alias: `org`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear organization --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear organization`
12
12
 
@@ -6,7 +6,7 @@ Group alias: `pu`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear project-update --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear project-update`
12
12
 
@@ -6,7 +6,7 @@ Group alias: `p`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear project --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear project`
12
12
 
@@ -6,7 +6,7 @@ Group alias: `rm`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear roadmap --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear roadmap`
12
12
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  _Generated from `linear commands --json`. `linear schema --help` (or `<subcommand> --help`) is authoritative._
6
6
 
7
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
7
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
8
8
 
9
9
  ### `linear schema`
10
10
 
@@ -6,7 +6,7 @@ Group alias: `st`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear state --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear state`
12
12
 
@@ -6,7 +6,7 @@ Group alias: `t`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear team --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear team`
12
12
 
@@ -6,7 +6,7 @@ Group alias: `u`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear user --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear user`
12
12
 
@@ -6,7 +6,7 @@ Group alias: `wh`
6
6
 
7
7
  _Generated from `linear commands --json`. `linear webhook --help` (or `<subcommand> --help`) is authoritative._
8
8
 
9
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
9
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
10
10
 
11
11
  ### `linear webhook`
12
12
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  _Generated from `linear commands --json`. `linear whoami --help` (or `<subcommand> --help`) is authoritative._
6
6
 
7
- Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
7
+ Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
8
8
 
9
9
  ### `linear whoami`
10
10
 
@@ -66,6 +66,7 @@ const GLOBAL_OPTIONS = new Map([
66
66
  ["-j, --json", "output machine-readable JSON"],
67
67
  ["--no-ansi", "disable colored output"],
68
68
  ["--api-key <key>", "Linear API key (overrides env/config)"],
69
+ ["--access-token <token>", "Linear OAuth access token (overrides env/config)"],
69
70
  ["--workspace <slug>", "select workspace credential profile"],
70
71
  ["-t, --team <key>", "default team key (e.g. TES)"],
71
72
  ["-n, --limit <n>", "max results (positive integer; 0 = all)"],
@@ -229,7 +230,8 @@ function buildReference(group: string, commands: CliCommand[]): string {
229
230
  lines.push("");
230
231
  lines.push(
231
232
  "Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, " +
232
- "`-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, " +
233
+ "`-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, " +
234
+ "`-t/--team <key>`, " +
233
235
  "`-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), " +
234
236
  "and `--debug`. " +
235
237
  "Only command-specific options are listed below.",
@@ -295,7 +297,7 @@ async function updateSkillMd(commands: CliCommand[], groups: string[]): Promise<
295
297
 
296
298
  const before = current.slice(0, begin + BEGIN_MARKER.length);
297
299
  const after = current.slice(end);
298
- // Format chosen to match Prettier: no blank line after the opening marker,
300
+ // Format chosen to match Oxfmt: no blank line after the opening marker,
299
301
  // one blank line before the closing marker, so re-running stays a no-op.
300
302
  const block = ["", "```text", buildCommandList(commands), "```", "", ""].join("\n");
301
303
  const next = `${before}${block}${after}`;
@@ -311,18 +313,17 @@ async function updateSkillMd(commands: CliCommand[], groups: string[]): Promise<
311
313
  }
312
314
 
313
315
  /**
314
- * Run Prettier over the generated markdown so the output matches the repo's
316
+ * Run Oxfmt over the generated markdown so the output matches the repo's
315
317
  * formatting (table alignment, list spacing) and re-running stays a clean no-op.
316
- * Best-effort: if Prettier isn't available the docs are still valid markdown.
318
+ * Best-effort: if Oxfmt isn't available the docs are still valid markdown.
317
319
  */
318
320
  function formatGenerated(): void {
319
- const result = spawnSync(
320
- "bunx",
321
- ["prettier", "--write", "--log-level", "warn", `${SKILL_DIR}/**/*.md`],
322
- { stdio: "inherit", cwd: REPO_ROOT },
323
- );
321
+ const result = spawnSync("bunx", ["oxfmt", "."], {
322
+ stdio: "inherit",
323
+ cwd: SKILL_DIR,
324
+ });
324
325
  if (result.status !== 0) {
325
- console.warn("Warning: prettier formatting step skipped (non-zero exit).");
326
+ console.warn("Warning: Oxfmt formatting step skipped (non-zero exit).");
326
327
  }
327
328
  }
328
329
 
package/src/client.ts CHANGED
@@ -3,21 +3,76 @@
3
3
  */
4
4
 
5
5
  import { LinearClient } from "@linear/sdk";
6
- import type { ResolvedConfig } from "./config.js";
6
+ import { rotateOAuthCredential, type ResolvedConfig } from "./config.js";
7
+ import { OAuthUserTokenProvider } from "./oauth.js";
7
8
  import { authError, normalizeError, CliError } from "./lib/errors.js";
8
9
 
9
10
  export function createClient(config: ResolvedConfig): LinearClient {
11
+ if (config.oauthCredential) {
12
+ return createRefreshingOAuthClient(config);
13
+ }
14
+ if (config.accessToken) {
15
+ return new LinearClient({ accessToken: config.accessToken });
16
+ }
10
17
  if (!config.apiKey) {
11
18
  // Surface the precise credential-selection error (ambiguous / unstored
12
19
  // workspace) only now, when a client is actually required.
13
20
  throw (
14
21
  config.apiKeyError ??
15
- authError("No API key found. Set LINEAR_API_KEY, pass --api-key, or run `linear auth login`.")
22
+ authError(
23
+ "No Linear credential found. Set LINEAR_ACCESS_TOKEN or LINEAR_API_KEY, pass --access-token or --api-key, or run `linear auth login`.",
24
+ )
16
25
  );
17
26
  }
18
27
  return new LinearClient({ apiKey: config.apiKey });
19
28
  }
20
29
 
30
+ function isAuthenticationFailure(err: unknown): boolean {
31
+ const value = err as Record<string, any> | undefined;
32
+ const status = value?.status ?? value?.response?.status ?? value?.raw?.response?.status;
33
+ if (status === 401) return true;
34
+ const errors = value?.response?.errors ?? value?.errors ?? value?.raw?.response?.errors;
35
+ return (
36
+ Array.isArray(errors) &&
37
+ errors.some((item) => {
38
+ const type = `${item?.extensions?.type ?? ""} ${item?.extensions?.code ?? ""}`;
39
+ const message = `${item?.message ?? ""}`;
40
+ return (
41
+ /^(?:authentication(?:error)?|unauthorized|invalidtoken|invalid_token)$/i.test(
42
+ type.trim(),
43
+ ) ||
44
+ /authentication (?:failed|required)|unauthori[sz]ed|invalid (?:access )?token|token (?:expired|invalid)/i.test(
45
+ message,
46
+ )
47
+ );
48
+ })
49
+ );
50
+ }
51
+
52
+ /** SDK client whose request boundary refreshes stored PKCE credentials before expiry and once on 401. */
53
+ function createRefreshingOAuthClient(config: ResolvedConfig): LinearClient {
54
+ const credential = config.oauthCredential!;
55
+ const provider = new OAuthUserTokenProvider({
56
+ credential,
57
+ persist: (previous, next) => rotateOAuthCredential(credential.workspace.urlKey, previous, next),
58
+ });
59
+ const client = new LinearClient({ accessToken: credential.accessToken });
60
+ const original = client.client.request.bind(client.client);
61
+ client.client.request = async (document, variables, requestHeaders) => {
62
+ const accessToken = await provider.getAccessToken();
63
+ client.client.setHeader("Authorization", `Bearer ${accessToken}`);
64
+ try {
65
+ return await original(document, variables, requestHeaders);
66
+ } catch (err) {
67
+ if (!isAuthenticationFailure(err)) throw err;
68
+ const replacement = await provider.getAccessToken({ forceRefresh: true });
69
+ client.client.setHeader("Authorization", `Bearer ${replacement}`);
70
+ return original(document, variables, requestHeaders);
71
+ }
72
+ };
73
+ return client;
74
+ }
75
+
21
76
  /**
22
77
  * The longest single wait `withRetry` will honour. Linear's request quota
23
78
  * resets hourly, so a `Retry-After` can be in the thousands of seconds; a CLI