linear-sdk-cli 0.6.0 → 0.7.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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/skills/linear-sdk-cli/SKILL.md +3 -0
- package/skills/linear-sdk-cli/references/changelog.md +24 -0
- package/src/cli.ts +2 -0
- package/src/commands/changelog.ts +108 -0
- package/src/lib/action.ts +1 -1
- package/src/lib/options.ts +2 -1
- package/src/lib/output-shapes.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ Release Please generates this file from Conventional Commits. Feature and fix pu
|
|
|
4
4
|
not edit it directly; their squash-commit titles are the release-note source of truth. This project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.7.0](https://github.com/zhendalf/linear-sdk-cli/compare/v0.6.0...v0.7.0) (2026-09-18)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- add changelog command ([#75](https://github.com/zhendalf/linear-sdk-cli/issues/75)) ([b095e77](https://github.com/zhendalf/linear-sdk-cli/commit/b095e7782ae3fb323ce9004883c7fb047f252c63))
|
|
12
|
+
|
|
7
13
|
## [0.6.0](https://github.com/zhendalf/linear-sdk-cli/compare/v0.5.3...v0.6.0) (2026-09-17)
|
|
8
14
|
|
|
9
15
|
### Features
|
package/package.json
CHANGED
|
@@ -316,6 +316,8 @@ linear auth status
|
|
|
316
316
|
linear auth token
|
|
317
317
|
linear auth whoami
|
|
318
318
|
|
|
319
|
+
linear changelog
|
|
320
|
+
|
|
319
321
|
linear commands
|
|
320
322
|
|
|
321
323
|
linear comment
|
|
@@ -520,6 +522,7 @@ prints the same). These are supplementary — `--help` on any command is authori
|
|
|
520
522
|
- [api](references/api.md) — Run a raw GraphQL query or mutation against the Linear API
|
|
521
523
|
- [attachment](references/attachment.md) — Work with issue attachments
|
|
522
524
|
- [auth](references/auth.md) — Manage authentication
|
|
525
|
+
- [changelog](references/changelog.md) — Show recent CLI release notes
|
|
523
526
|
- [commands](references/commands.md) — Machine-readable command tree (for scripts/agents)
|
|
524
527
|
- [comment](references/comment.md) — Manage comments
|
|
525
528
|
- [completion](references/completion.md) — Output a shell completion script
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# linear changelog
|
|
2
|
+
|
|
3
|
+
> Show recent CLI release notes
|
|
4
|
+
|
|
5
|
+
_Generated from `linear commands --json`. `linear changelog --help` (or `<subcommand> --help`) is authoritative._
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
### `linear changelog`
|
|
10
|
+
|
|
11
|
+
Show recent CLI release notes
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
linear changelog [options]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Output (`--json`)**: a bare array of objects
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
version: string
|
|
21
|
+
date: string
|
|
22
|
+
url: string
|
|
23
|
+
sections: Array<{title: string, items: string[]}>
|
|
24
|
+
```
|
package/src/cli.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { addGlobalOptions, globalOptionKeys, unknownCommand, commandPath } from
|
|
|
8
8
|
import { registerMeta } from "./commands/meta.js";
|
|
9
9
|
import { registerApi } from "./commands/api.js";
|
|
10
10
|
import { registerCompletion } from "./commands/completion.js";
|
|
11
|
+
import { registerChangelog } from "./commands/changelog.js";
|
|
11
12
|
import { registerIssue, renderIssueDetail } from "./commands/issue.js";
|
|
12
13
|
import { registerAgentSession } from "./commands/agent-session.js";
|
|
13
14
|
import { registerAttach } from "./commands/attach.js";
|
|
@@ -62,6 +63,7 @@ export function createProgram(): Command {
|
|
|
62
63
|
registerMeta(program);
|
|
63
64
|
registerApi(program);
|
|
64
65
|
registerCompletion(program);
|
|
66
|
+
registerChangelog(program);
|
|
65
67
|
registerOpen(program);
|
|
66
68
|
// Phase 1: issues. Agent sessions live on issues, so their group is mounted
|
|
67
69
|
// under `issue` (`linear issue agent-session …`); so is file upload
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/** Show release notes from the CHANGELOG.md bundled with the CLI package. */
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { action } from "../lib/action.js";
|
|
7
|
+
import { CliError } from "../lib/errors.js";
|
|
8
|
+
import type { Context } from "../context.js";
|
|
9
|
+
|
|
10
|
+
export interface ChangelogSection {
|
|
11
|
+
title: string;
|
|
12
|
+
items: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ChangelogRelease {
|
|
16
|
+
version: string;
|
|
17
|
+
date: string;
|
|
18
|
+
url: string;
|
|
19
|
+
sections: ChangelogSection[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const CHANGELOG_PATH = fileURLToPath(new URL("../../CHANGELOG.md", import.meta.url));
|
|
23
|
+
const DEFAULT_RELEASE_COUNT = 3;
|
|
24
|
+
|
|
25
|
+
/** Parse the Release Please Markdown format into a stable machine-readable shape. */
|
|
26
|
+
export function parseChangelog(markdown: string): ChangelogRelease[] {
|
|
27
|
+
const releases: ChangelogRelease[] = [];
|
|
28
|
+
const releasePattern = /^## \[([^\]]+)]\(([^)]+)\) \((\d{4}-\d{2}-\d{2})\)\s*$/gm;
|
|
29
|
+
const matches = [...markdown.matchAll(releasePattern)];
|
|
30
|
+
|
|
31
|
+
for (const [index, match] of matches.entries()) {
|
|
32
|
+
const bodyStart = (match.index ?? 0) + match[0].length;
|
|
33
|
+
const bodyEnd = matches[index + 1]?.index ?? markdown.length;
|
|
34
|
+
const body = markdown.slice(bodyStart, bodyEnd);
|
|
35
|
+
const sections: ChangelogSection[] = [];
|
|
36
|
+
const sectionPattern = /^### (.+)\s*$/gm;
|
|
37
|
+
const sectionMatches = [...body.matchAll(sectionPattern)];
|
|
38
|
+
|
|
39
|
+
for (const [sectionIndex, sectionMatch] of sectionMatches.entries()) {
|
|
40
|
+
const itemsStart = (sectionMatch.index ?? 0) + sectionMatch[0].length;
|
|
41
|
+
const itemsEnd = sectionMatches[sectionIndex + 1]?.index ?? body.length;
|
|
42
|
+
const itemsBody = body.slice(itemsStart, itemsEnd).trim();
|
|
43
|
+
const items = itemsBody
|
|
44
|
+
.split(/\n(?=- )/)
|
|
45
|
+
.map((item) => item.replace(/^- /, "").replace(/\n\s+/g, " ").trim())
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
sections.push({ title: sectionMatch[1]!.trim(), items });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
releases.push({
|
|
51
|
+
version: match[1]!,
|
|
52
|
+
url: match[2]!,
|
|
53
|
+
date: match[3]!,
|
|
54
|
+
sections,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return releases;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function registerChangelog(program: Command): void {
|
|
62
|
+
program
|
|
63
|
+
.command("changelog")
|
|
64
|
+
.description("Show recent CLI release notes")
|
|
65
|
+
.addHelpText(
|
|
66
|
+
"after",
|
|
67
|
+
[
|
|
68
|
+
"",
|
|
69
|
+
"Examples:",
|
|
70
|
+
" linear changelog # latest three releases",
|
|
71
|
+
" linear changelog --limit 1 # latest release only",
|
|
72
|
+
" linear changelog --all # complete bundled history",
|
|
73
|
+
" linear changelog --json # structured release notes",
|
|
74
|
+
].join("\n"),
|
|
75
|
+
)
|
|
76
|
+
.action(action(runChangelog));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function runChangelog(ctx: Context): void {
|
|
80
|
+
let markdown: string;
|
|
81
|
+
try {
|
|
82
|
+
markdown = readFileSync(CHANGELOG_PATH, "utf8");
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new CliError(`Cannot read the bundled changelog: ${(error as Error).message}`, "runtime");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const releases = parseChangelog(markdown);
|
|
88
|
+
const count =
|
|
89
|
+
ctx.options.all || ctx.options.limit === 0
|
|
90
|
+
? releases.length
|
|
91
|
+
: (ctx.options.limit ?? DEFAULT_RELEASE_COUNT);
|
|
92
|
+
const recent = releases.slice(0, count);
|
|
93
|
+
|
|
94
|
+
ctx.output.emit(recent, () => {
|
|
95
|
+
const rendered = recent
|
|
96
|
+
.map((release) => {
|
|
97
|
+
const sections = release.sections
|
|
98
|
+
.map(
|
|
99
|
+
(section) =>
|
|
100
|
+
`### ${section.title}\n\n${section.items.map((item) => `- ${item}`).join("\n")}`,
|
|
101
|
+
)
|
|
102
|
+
.join("\n\n");
|
|
103
|
+
return `## [${release.version}](${release.url}) (${release.date})\n\n${sections}`;
|
|
104
|
+
})
|
|
105
|
+
.join("\n\n");
|
|
106
|
+
ctx.output.markdown(rendered);
|
|
107
|
+
});
|
|
108
|
+
}
|
package/src/lib/action.ts
CHANGED
|
@@ -27,7 +27,7 @@ export function action(handler: ActionHandler) {
|
|
|
27
27
|
let group = command;
|
|
28
28
|
while (group.parent?.parent) group = group.parent;
|
|
29
29
|
const offline =
|
|
30
|
-
["config", "open", "commands", "completion"].includes(group.name()) ||
|
|
30
|
+
["config", "open", "commands", "completion", "changelog"].includes(group.name()) ||
|
|
31
31
|
(group.name() === "auth" && !["whoami", "status", "token"].includes(command.name()));
|
|
32
32
|
if (!offline) await ctx.selectWorkspace();
|
|
33
33
|
try {
|
package/src/lib/options.ts
CHANGED
|
@@ -422,8 +422,9 @@ export const FIELDS_COMMANDS: ReadonlySet<string> = new Set([
|
|
|
422
422
|
"webhook view",
|
|
423
423
|
]);
|
|
424
424
|
|
|
425
|
-
/** Commands
|
|
425
|
+
/** Commands with bounded lists: `--limit <n>` caps them, `--all` (or `--limit 0`) exhausts them. */
|
|
426
426
|
export const LIMIT_COMMANDS: ReadonlySet<string> = new Set([
|
|
427
|
+
"changelog",
|
|
427
428
|
"attachment list",
|
|
428
429
|
"comment list",
|
|
429
430
|
"cycle list",
|
package/src/lib/output-shapes.ts
CHANGED
|
@@ -313,6 +313,13 @@ const RAW_API: OutputShape = {
|
|
|
313
313
|
export const OUTPUT_SHAPES: Record<string, OutputShape | null> = {
|
|
314
314
|
api: RAW_API,
|
|
315
315
|
|
|
316
|
+
changelog: list({
|
|
317
|
+
version: "string",
|
|
318
|
+
date: "string",
|
|
319
|
+
url: "string",
|
|
320
|
+
sections: [{ title: "string", items: ["string"] }],
|
|
321
|
+
}),
|
|
322
|
+
|
|
316
323
|
attachment: null,
|
|
317
324
|
"attachment create": receipt({ id: "string", title: "string", url: "string" }),
|
|
318
325
|
"attachment delete": receipt({ id: "string", title: "string", deleted: "boolean" }),
|