@intercom/cli 0.6.0 → 0.8.1

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 ADDED
@@ -0,0 +1,84 @@
1
+ # Changelog
2
+
3
+ All notable changes to the Intercom CLI are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ View it from the CLI with `intercom changelog`.
9
+
10
+ ## [0.8.1] - 2026-06-16
11
+
12
+ ### Fixed
13
+
14
+ - Show the server's actual error message on failed requests instead of a bare `HTTP <status>`. This covers Fin SDK endpoints like `fin workflows upload` and `fin procedures upload`, which report problems (e.g. `YAML syntax error`, `Invalid file content`, `File too large`) in a different format than the rest of the API. `--verbose` now also prints the response body on errors.
15
+ - `fin workflows enable` and `fin workflows disable` now send the workflow state as the name the server expects (`live`/`paused`) instead of an internal numeric value, which the upload endpoint rejected. Both commands work again.
16
+
17
+ ## [0.7.0] - 2026-06-10
18
+
19
+ ### Added
20
+
21
+ - `intercom changelog` command to view the CLI's release notes from the terminal
22
+ - `fin data-connectors versions <id>` to view a data connector's version history
23
+ - Auto-enable fin-sdk and retry when a fin command hits the gate (#102)
24
+
25
+ ### Changed
26
+
27
+ - `fin data-connectors list` now shows each connector's state
28
+ - `fin data-connectors get` now shows the full connector definition (method, URL, inputs, outputs)
29
+ - `fin data-connectors create` now takes `--name` and returns the URL to finish configuring the connector in the UI
30
+
31
+ ### Fixed
32
+
33
+ - Handle `login_verification` redirect in CLI sign-in (#99)
34
+
35
+ ## [0.6.0] - 2026-05-25
36
+
37
+ ### Added
38
+
39
+ - `fin workflows upload`, `fin workflows enable`, and `fin workflows disable` (#95)
40
+ - `fin monitor pull-flagged` and `fin conversation bulk-send` (#83)
41
+
42
+ ### Changed
43
+
44
+ - Strip internal fields from conversation responses (#97)
45
+
46
+ ### Fixed
47
+
48
+ - Target the correct workspace during bootstrap (#96)
49
+ - Parse `fin workflows get` output as YAML, not JSON (#90)
50
+
51
+ ## [0.5.0] - 2026-04-27
52
+
53
+ ### Added
54
+
55
+ - Emit a Claude Code plugin hint from `--help` and `auth login` (#91)
56
+
57
+ ## [0.4.0] - 2026-04-20
58
+
59
+ ### Added
60
+
61
+ - `articles search` subcommand (#85)
62
+ - `pricing` command to fetch live pricing information (#67)
63
+ - `fin guidance create` command (#69)
64
+ - `fin` as a binary alias for `intercom` (#66)
65
+ - `intercom config` command (#43)
66
+ - `auth list` and `auth switch` commands (#41)
67
+ - Global `--dry-run` / `-n` flag (#48)
68
+ - `--input` flag on the `api` command and search subcommands (#46)
69
+ - Color output and a `--wide` flag for tables (#44)
70
+ - 5xx retry with exponential backoff (#49)
71
+
72
+ ### Changed
73
+
74
+ - Strip article bodies from list/search by default (#86)
75
+ - Default messenger snippets to JWT authentication (#81)
76
+
77
+ ### Fixed
78
+
79
+ - Improve `setup` command reliability for greenfield workspaces (#64)
80
+
81
+ [0.7.0]: https://github.com/intercom/cli/releases/tag/v0.7.0
82
+ [0.6.0]: https://github.com/intercom/cli/releases/tag/v0.6.0
83
+ [0.5.0]: https://github.com/intercom/cli/releases/tag/v0.5.0
84
+ [0.4.0]: https://github.com/intercom/cli/releases/tag/v0.4.0
package/README.md CHANGED
@@ -73,6 +73,7 @@ intercom setup --company-name "Acme" --articles-from ./docs
73
73
  | `intercom setup` | Orchestrate full workspace setup |
74
74
  | `intercom config get\|set\|list` | Manage CLI configuration |
75
75
  | `intercom pricing` | Show current Intercom pricing |
76
+ | `intercom changelog` | Show the CLI's release notes / version history |
76
77
  | `intercom completion bash\|zsh\|fish` | Generate shell completions |
77
78
 
78
79
  ## Global Flags
@@ -141,11 +142,11 @@ intercom api /contacts --jq '.data[].email'
141
142
  ```bash
142
143
  git clone git@github.com:intercom/cli.git
143
144
  cd cli
144
- npm ci
145
- npm run build # Build to dist/
146
- npm test # Run tests (~5s)
147
- npm run lint # TypeScript typecheck
148
- npm run dev # Watch mode
145
+ pnpm install
146
+ pnpm build # Build to dist/
147
+ pnpm test # Run tests (~5s)
148
+ pnpm lint # TypeScript typecheck
149
+ pnpm dev # Watch mode
149
150
 
150
151
  # Try it locally
151
152
  export INTERCOM_TOKEN="<your-token>"
@@ -154,6 +155,35 @@ node dist/index.js me
154
155
 
155
156
  See [SETUP.md](SETUP.md) for full developer setup guide.
156
157
 
158
+ ## Releasing
159
+
160
+ Publishing `@intercom/cli` to npm runs through `.github/workflows/publish.yml`, which fires when a GitHub Release is published. It uses OIDC trusted publishing (GitHub Actions mints a short-lived token per run, so there is no long-lived `NPM_TOKEN`) and npm staged publishing, which holds every version behind a human 2FA approval before it becomes installable. The org-wide reference is the [Engineering Wiki: Release new npm package versions](https://coda.io/d/Engineering-Wiki_duvlW1VbQXK/Release-new-npm-package-versions_suK1gai4).
161
+
162
+ ### Prerequisites (one-time)
163
+
164
+ - **GitHub:** write access to this repo, to bump the version and create Releases.
165
+ - **npm:** to approve a staged release, log in to npmjs.com as `intercom-npm-publisher` (the package owner). Credentials are in the 1Password Engineering Vault.
166
+
167
+ ### Cut a stable release
168
+
169
+ 1. **Bump the version.** In a PR, set the new `version` in `package.json` and move the unreleased entries in `CHANGELOG.md` under a new `## [X.Y.Z] - YYYY-MM-DD` heading. Merge it to `main` — the workflow refuses any release that is not reachable from the default branch.
170
+ 2. **Create a GitHub Release.** From the [Releases page](https://github.com/intercom/cli/releases/new), add a tag `vX.Y.Z` that matches `package.json` exactly (the `v` prefix is required; the workflow asserts `tag == version`). Leave the release label as **None**. Publishing the Release creates the tag and triggers `publish.yml`.
171
+ 3. **Wait for the workflow.** It runs the tag and branch guards, builds, then runs `npm stage publish` over OIDC. When the Actions run is green, the version sits in npm's staged queue — uploaded but not yet installable.
172
+ 4. **Approve in npm.** Logged in as `intercom-npm-publisher`, open [Staged Packages](https://www.npmjs.com/settings/intercom-npm-publisher/staged-packages), review the version, and **Approve** with 2FA. A stable version publishes to the `latest` dist-tag and is then installable via `npm install -g @intercom/cli`.
173
+
174
+ ### Cut a prerelease
175
+
176
+ Same path and same approval gate; a prerelease lands on the `next` dist-tag instead of `latest`.
177
+
178
+ 1. Bump `package.json` to a version with a `-` suffix (e.g. `X.Y.Z-beta.1`) and merge to `main` via PR. Prereleases come from the default branch too.
179
+ 2. Create a GitHub Release tagged `vX.Y.Z-beta.1` from that commit (the tag must match the version) and tick **Set as a pre-release**.
180
+ 3. The workflow detects the `-` suffix and stages to the `next` tag, never `latest`.
181
+ 4. Approve the staged version with 2FA as above. Consumers opt in explicitly with `npm install -g @intercom/cli@next`; a plain install still resolves to stable `latest`.
182
+
183
+ ### Reject a staged release
184
+
185
+ If a staged version should not ship, choose **Reject** in Staged Packages and it is removed without publishing. Rejecting leaves the git tag and GitHub Release behind, so also delete the Release and tag from the GitHub Releases UI. The version can then be re-staged — only published versions are immutable.
186
+
157
187
  ## License
158
188
 
159
189
  MIT
@@ -26,8 +26,8 @@ var NotFoundError = class extends CLIError {
26
26
  }
27
27
  };
28
28
  var PermissionError = class extends CLIError {
29
- constructor(message) {
30
- super(message, 5);
29
+ constructor(message, error) {
30
+ super(message, 5, error);
31
31
  }
32
32
  };
33
33
  var TimeoutError = class extends CLIError {
@@ -106,4 +106,4 @@ export {
106
106
  formatJson,
107
107
  applyJq
108
108
  };
109
- //# sourceMappingURL=chunk-Y4JK5AHH.js.map
109
+ //# sourceMappingURL=chunk-3V2ONMAU.js.map
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  applyJq,
10
10
  detectAuthType,
11
11
  formatJson
12
- } from "./chunk-Y4JK5AHH.js";
12
+ } from "./chunk-3V2ONMAU.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { Command } from "commander";
@@ -61,11 +61,30 @@ async function fetchWithTimeout(url, init, options) {
61
61
 
62
62
  // src/api/client.ts
63
63
  var DEFAULT_BASE_URL = "https://api.intercom.io";
64
+ var MAX_RAW_ERROR_LEN = 500;
65
+ function extractApiError(body) {
66
+ if (!body || typeof body !== "object") return {};
67
+ const b = body;
68
+ if (Array.isArray(b.errors) && b.errors.length > 0) {
69
+ const first = b.errors[0];
70
+ return {
71
+ message: typeof first?.message === "string" ? first.message : void 0,
72
+ code: typeof first?.code === "string" ? first.code : void 0
73
+ };
74
+ }
75
+ if (typeof b.error === "string") return { message: b.error };
76
+ if (b.errors && typeof b.errors === "object") {
77
+ const msgs = Object.values(b.errors).flat().filter((m) => typeof m === "string");
78
+ if (msgs.length > 0) return { message: msgs.join("; ") };
79
+ }
80
+ if (typeof b.message === "string") return { message: b.message };
81
+ return {};
82
+ }
64
83
  var _dryRun = false;
65
84
  function setDryRun(enabled) {
66
85
  _dryRun = enabled;
67
86
  }
68
- var IntercomClient = class {
87
+ var IntercomClient = class _IntercomClient {
69
88
  token;
70
89
  authType;
71
90
  appId;
@@ -88,7 +107,49 @@ var IntercomClient = class {
88
107
  }
89
108
  return `Bearer ${this.token}`;
90
109
  }
91
- async request(method, path, body, responseType = "json", extraHeaders) {
110
+ // Dedicated error code the API returns when the `fin-sdk` feature flag gates
111
+ // a fin subcommand. It's specific to that self-fixable gate (not the generic
112
+ // plan-restriction code), so seeing it is a safe signal to auto-enable.
113
+ static FIN_SDK_GATE_CODE = "fin_sdk_not_enabled";
114
+ // Thin wrapper over requestOnce that transparently recovers from the fin-sdk
115
+ // gate: if a call is blocked because the flag isn't enabled, opt the
116
+ // workspace in via POST /fin-sdk/enable and retry the original request once.
117
+ // If enabling fails, the original gate error is surfaced unchanged.
118
+ async request(method, path, body, responseType = "json", extraHeaders, isFinSdkRetry = false) {
119
+ try {
120
+ return await this.requestOnce(
121
+ method,
122
+ path,
123
+ body,
124
+ responseType,
125
+ extraHeaders
126
+ );
127
+ } catch (err) {
128
+ if (!isFinSdkRetry && !_dryRun && err instanceof PermissionError && err.error?.code === _IntercomClient.FIN_SDK_GATE_CODE) {
129
+ try {
130
+ await this.enableFinSdk();
131
+ } catch {
132
+ throw err;
133
+ }
134
+ return this.request(
135
+ method,
136
+ path,
137
+ body,
138
+ responseType,
139
+ extraHeaders,
140
+ true
141
+ );
142
+ }
143
+ throw err;
144
+ }
145
+ }
146
+ // Opt the current workspace into the fin-sdk feature flag. The notice goes to
147
+ // stderr so it never contaminates --json / piped stdout output.
148
+ async enableFinSdk() {
149
+ console.error("Fin SDK not enabled for this workspace \u2014 enabling it now...");
150
+ await this.requestOnce("POST", "/fin-sdk/enable", {}, "json");
151
+ }
152
+ async requestOnce(method, path, body, responseType = "json", extraHeaders) {
92
153
  const url = `${this.baseUrl}${path}`;
93
154
  if (_dryRun) {
94
155
  console.error(`> ${method} ${url}`);
@@ -147,15 +208,32 @@ var IntercomClient = class {
147
208
  return response.json();
148
209
  }
149
210
  }
150
- const errorBody = await response.json().catch(() => ({}));
151
- const errorMsg = errorBody?.errors?.[0]?.message ?? `HTTP ${response.status}`;
211
+ const rawBody = await response.text().catch(() => "");
212
+ if (this.verbose && rawBody) {
213
+ console.error(`< Body: ${rawBody}`);
214
+ }
215
+ let parsedBody;
216
+ try {
217
+ parsedBody = rawBody ? JSON.parse(rawBody) : void 0;
218
+ } catch {
219
+ parsedBody = void 0;
220
+ }
221
+ const extracted = extractApiError(parsedBody);
222
+ const trimmedBody = rawBody.trim();
223
+ const rawFallback = trimmedBody.length > MAX_RAW_ERROR_LEN ? `${trimmedBody.slice(0, MAX_RAW_ERROR_LEN)}\u2026` : trimmedBody;
224
+ const errorMsg = extracted.message ?? (rawFallback || `HTTP ${response.status}`);
225
+ const errorCode = extracted.code ?? "";
152
226
  if (response.status === 401) {
153
227
  throw new AuthError(
154
228
  `Authentication expired or invalid: ${errorMsg}. Run "intercom auth login" to re-authenticate.`
155
229
  );
156
230
  }
157
231
  if (response.status === 403) {
158
- throw new PermissionError(`Permission denied: ${errorMsg}`);
232
+ throw new PermissionError(`Permission denied: ${errorMsg}`, {
233
+ code: errorCode,
234
+ message: errorMsg,
235
+ status: 403
236
+ });
159
237
  }
160
238
  if (response.status === 404) {
161
239
  throw new NotFoundError(errorMsg);
@@ -905,13 +983,18 @@ async function deviseSignIn(baseUrl, email, password, timeoutMs = API_TIMEOUT_MS
905
983
  }
906
984
  const location = signInResponse.headers.get("location") ?? "";
907
985
  if (location.includes("/admins/sign_in")) {
908
- throw new Error("Invalid credentials");
986
+ throw new AuthError("Invalid credentials");
909
987
  }
910
988
  if (location.includes("two_factor")) {
911
- throw new Error(
989
+ throw new AuthError(
912
990
  "Two-factor authentication is required. Please use an API key instead."
913
991
  );
914
992
  }
993
+ if (location.includes("/login_verification")) {
994
+ throw new AuthError(
995
+ "Login email verification is required.\n Please sign in to the Intercom web app to complete verification, then retry this command."
996
+ );
997
+ }
915
998
  cookies = mergeCookies(cookies, extractCookies(signInResponse));
916
999
  return cookies;
917
1000
  }
@@ -2493,9 +2576,13 @@ function registerProceduresCommand(fin) {
2493
2576
  }
2494
2577
 
2495
2578
  // src/commands/fin/data-connectors.ts
2579
+ function summarizeKeys(obj) {
2580
+ if (!obj) return "";
2581
+ return Object.keys(obj).join(", ");
2582
+ }
2496
2583
  function registerDataConnectorsCommand(fin) {
2497
2584
  const dataConnectors = fin.command("data-connectors").description("Manage Fin SDK data connectors");
2498
- dataConnectors.command("list").description("List data connectors from the Fin SDK manifest").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
2585
+ dataConnectors.command("list").description("List data connectors from the Fin SDK manifest").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function() {
2499
2586
  const opts = this.optsWithGlobals();
2500
2587
  const client = await resolveClient(opts);
2501
2588
  const result = await client.get("/fin-sdk/manifest");
@@ -2507,33 +2594,38 @@ function registerDataConnectorsCommand(fin) {
2507
2594
  }
2508
2595
  const rows = items.map((item) => ({
2509
2596
  ID: String(item.id ?? ""),
2510
- NAME: String(item.name ?? "")
2597
+ NAME: String(item.name ?? ""),
2598
+ STATE: String(item.state ?? "")
2511
2599
  }));
2512
- return table({ columns: ["ID", "NAME"], rows });
2600
+ return table({ columns: ["ID", "NAME", "STATE"], rows });
2513
2601
  }, items);
2514
2602
  });
2515
- dataConnectors.command("create").description("Create a data connector").requiredOption("-f, --field <key=value>", "Field to set (e.g. name=<name>)", (v, acc) => [...acc, v], []).option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function() {
2603
+ dataConnectors.command("create").description("Create a draft data connector (finish configuration in the UI)").requiredOption("--name <name>", "Name for the new data connector").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
2604
+ "after",
2605
+ `
2606
+ The create endpoint only sets the name \u2014 it returns a web_url where you finish
2607
+ configuring the connector (request, inputs, outputs, auth) in the Intercom UI.
2608
+
2609
+ Example:
2610
+ $ intercom fin data-connectors create --name "Order lookup"`
2611
+ ).action(async function() {
2516
2612
  const opts = this.optsWithGlobals();
2517
2613
  const client = await resolveClient(opts);
2518
- const fields = opts.field ?? [];
2519
- const body = {};
2520
- for (const f of fields) {
2521
- const eq = f.indexOf("=");
2522
- if (eq === -1) throw new Error(`Invalid field format: ${f}. Expected key=value`);
2523
- body[f.slice(0, eq)] = f.slice(eq + 1);
2524
- }
2525
- const result = await client.post("/fin-sdk/data-connectors", body);
2614
+ const result = await client.post("/fin-sdk/data-connectors", {
2615
+ name: opts.name
2616
+ });
2526
2617
  render(
2527
2618
  result,
2528
2619
  opts,
2529
2620
  () => detail({
2530
- "ID": String(result.id ?? ""),
2531
- "Name": String(result.name ?? ""),
2532
- "Status": result.status != null ? String(result.status) : void 0
2621
+ ID: String(result.id ?? ""),
2622
+ Name: String(result.name ?? ""),
2623
+ "Web URL": result.web_url != null ? String(result.web_url) : void 0,
2624
+ "Next step": result.message != null ? String(result.message) : void 0
2533
2625
  })
2534
2626
  );
2535
2627
  });
2536
- dataConnectors.command("get <id>").description("Download a data connector by ID").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(id) {
2628
+ dataConnectors.command("get <id>").description("Download a data connector's full definition by ID").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(id) {
2537
2629
  const opts = this.optsWithGlobals();
2538
2630
  const client = await resolveClient(opts);
2539
2631
  const result = await client.get(`/fin-sdk/data-connectors/${id}/download`);
@@ -2541,11 +2633,36 @@ function registerDataConnectorsCommand(fin) {
2541
2633
  result,
2542
2634
  opts,
2543
2635
  () => detail({
2544
- "ID": String(result.id ?? ""),
2545
- "Name": String(result.name ?? "")
2636
+ ID: String(result.id ?? ""),
2637
+ Name: String(result.name ?? ""),
2638
+ State: result.state != null ? String(result.state) : void 0,
2639
+ Description: result.description != null ? String(result.description) : void 0,
2640
+ "Direct Fin usage": result.enable_direct_usage != null ? String(result.enable_direct_usage) : void 0,
2641
+ Method: result.request?.method != null ? String(result.request.method) : void 0,
2642
+ URL: result.request?.url != null ? String(result.request.url) : void 0,
2643
+ Inputs: summarizeKeys(result.inputs) || void 0,
2644
+ Outputs: summarizeKeys(result.outputs) || void 0
2546
2645
  })
2547
2646
  );
2548
2647
  });
2648
+ dataConnectors.command("versions <id>").description("List a data connector's server-side version history").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(id) {
2649
+ const opts = this.optsWithGlobals();
2650
+ const client = await resolveClient(opts);
2651
+ const result = await client.get(`/fin-sdk/data-connectors/${id}/versions`);
2652
+ const items = result.versions ?? [];
2653
+ render(items, opts, () => {
2654
+ if (items.length === 0) {
2655
+ return "No versions found.";
2656
+ }
2657
+ const rows = items.map((v) => ({
2658
+ STATUS: String(v.status ?? ""),
2659
+ CREATED: String(v.created_at ?? ""),
2660
+ AUTHOR: String(v.author?.name ?? ""),
2661
+ NOTE: String(v.note ?? "")
2662
+ }));
2663
+ return table({ columns: ["STATUS", "CREATED", "AUTHOR", "NOTE"], rows });
2664
+ }, items);
2665
+ });
2549
2666
  }
2550
2667
 
2551
2668
  // src/commands/fin/guidance.ts
@@ -2656,8 +2773,8 @@ function registerAudiencesCommand(fin) {
2656
2773
  import { readFile as readFile3 } from "fs/promises";
2657
2774
  import { basename as basename2 } from "path";
2658
2775
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2659
- var STATE_LIVE = 1;
2660
- var STATE_PAUSED = 2;
2776
+ var STATE_LIVE = "live";
2777
+ var STATE_PAUSED = "paused";
2661
2778
  function registerWorkflowsCommand(fin) {
2662
2779
  const workflows = fin.command("workflows").description("Manage Fin SDK workflows");
2663
2780
  workflows.command("list").description("List Fin SDK workflows").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
@@ -3973,7 +4090,7 @@ function registerPricingCommand(program) {
3973
4090
  }
3974
4091
  const markdown = await response.text();
3975
4092
  if (opts.json) {
3976
- const { formatJson: formatJson2 } = await import("./json-MMBOB7YU.js");
4093
+ const { formatJson: formatJson2 } = await import("./json-OEUXXCTX.js");
3977
4094
  console.log(formatJson2({ url: PRICING_URL, content: markdown }));
3978
4095
  } else {
3979
4096
  console.log(markdown);
@@ -4075,6 +4192,104 @@ function registerConfigCommand(program) {
4075
4192
  });
4076
4193
  }
4077
4194
 
4195
+ // src/commands/changelog.ts
4196
+ import pc2 from "picocolors";
4197
+
4198
+ // src/changelog.ts
4199
+ import { readFileSync as readFileSync5 } from "fs";
4200
+ import { fileURLToPath } from "url";
4201
+ var RELEASE_RE = /^##\s+\[?([^\]]+?)\]?(?:\s+-\s+(.+))?$/;
4202
+ var SECTION_RE = /^###\s+(.+)$/;
4203
+ var ENTRY_RE = /^[-*]\s+(.+)$/;
4204
+ function parseChangelog(markdown) {
4205
+ const releases = [];
4206
+ let release = null;
4207
+ let section = null;
4208
+ for (const raw of markdown.split("\n")) {
4209
+ const line = raw.trimEnd();
4210
+ const releaseMatch = line.match(RELEASE_RE);
4211
+ if (releaseMatch) {
4212
+ release = { version: releaseMatch[1].trim(), date: releaseMatch[2]?.trim() ?? "", sections: [] };
4213
+ section = null;
4214
+ releases.push(release);
4215
+ continue;
4216
+ }
4217
+ if (!release) continue;
4218
+ const sectionMatch = line.match(SECTION_RE);
4219
+ if (sectionMatch) {
4220
+ section = { title: sectionMatch[1].trim(), entries: [] };
4221
+ release.sections.push(section);
4222
+ continue;
4223
+ }
4224
+ const entryMatch = line.match(ENTRY_RE);
4225
+ if (entryMatch && section) {
4226
+ section.entries.push(entryMatch[1].trim());
4227
+ }
4228
+ }
4229
+ return releases;
4230
+ }
4231
+ var cached;
4232
+ function loadChangelog() {
4233
+ if (cached) return cached;
4234
+ const path = fileURLToPath(new URL("../CHANGELOG.md", import.meta.url));
4235
+ cached = parseChangelog(readFileSync5(path, "utf8"));
4236
+ return cached;
4237
+ }
4238
+
4239
+ // src/commands/changelog.ts
4240
+ function normalizeVersion(v) {
4241
+ return v.replace(/^v/i, "");
4242
+ }
4243
+ function colorEnabled() {
4244
+ return !!process.stdout.isTTY && !process.env.NO_COLOR;
4245
+ }
4246
+ function renderHuman(releases) {
4247
+ const color = colorEnabled();
4248
+ const out = [];
4249
+ for (const release of releases) {
4250
+ const heading = release.date ? `${release.version} \u2014 ${release.date}` : release.version;
4251
+ out.push(color ? pc2.bold(heading) : heading);
4252
+ for (const section of release.sections) {
4253
+ out.push(` ${color ? pc2.dim(section.title) : section.title}`);
4254
+ for (const entry of section.entries) {
4255
+ out.push(` \u2022 ${entry}`);
4256
+ }
4257
+ }
4258
+ out.push("");
4259
+ }
4260
+ return out.join("\n").trimEnd();
4261
+ }
4262
+ function registerChangelogCommand(program) {
4263
+ program.command("changelog").description("Show the CLI's release notes / version history").argument("[version]", "Show a single version (e.g. 0.7.0)").option("-n, --limit <count>", "Show only the most recent <count> releases").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").addHelpText(
4264
+ "after",
4265
+ `
4266
+ Examples:
4267
+ $ intercom changelog Show the full release history
4268
+ $ intercom changelog --limit 1 Show only the latest release
4269
+ $ intercom changelog 0.7.0 Show a specific version
4270
+ $ intercom changelog --json Machine-readable release history`
4271
+ ).action(async function(version) {
4272
+ const opts = this.optsWithGlobals();
4273
+ let releases = loadChangelog();
4274
+ if (version) {
4275
+ const wanted = normalizeVersion(version);
4276
+ const match = releases.find((r) => normalizeVersion(r.version) === wanted);
4277
+ if (!match) {
4278
+ const known = releases.map((r) => r.version).join(", ");
4279
+ throw new CLIError(`No changelog entry for version "${version}". Known versions: ${known}`);
4280
+ }
4281
+ releases = [match];
4282
+ } else if (opts.limit !== void 0) {
4283
+ const limit = parseInt(String(opts.limit), 10);
4284
+ if (isNaN(limit) || limit <= 0) {
4285
+ throw new CLIError("--limit must be a positive integer");
4286
+ }
4287
+ releases = releases.slice(0, limit);
4288
+ }
4289
+ render(releases, opts, () => renderHuman(releases), releases);
4290
+ });
4291
+ }
4292
+
4078
4293
  // src/index.ts
4079
4294
  function detectBinaryName() {
4080
4295
  const argv1 = process.argv[1] ?? "";
@@ -4119,6 +4334,7 @@ Environment: Set INTERCOM_TOKEN to skip login entirely.`;
4119
4334
  registerPricingCommand(program);
4120
4335
  registerCompletionCommand(program);
4121
4336
  registerConfigCommand(program);
4337
+ registerChangelogCommand(program);
4122
4338
  program.hook("preAction", (thisCommand, actionCommand) => {
4123
4339
  const rootOpts = thisCommand.opts();
4124
4340
  if (rootOpts.agent && rootOpts.human) {
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  applyJq,
4
4
  formatJson
5
- } from "./chunk-Y4JK5AHH.js";
5
+ } from "./chunk-3V2ONMAU.js";
6
6
  export {
7
7
  applyJq,
8
8
  formatJson
9
9
  };
10
- //# sourceMappingURL=json-MMBOB7YU.js.map
10
+ //# sourceMappingURL=json-OEUXXCTX.js.map
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@intercom/cli",
3
- "version": "0.6.0",
3
+ "version": "0.8.1",
4
4
  "description": "CLI tool for managing Intercom workspaces",
5
5
  "type": "module",
6
+ "packageManager": "pnpm@10.23.0",
6
7
  "bin": {
7
8
  "intercom": "dist/index.js",
8
9
  "fin": "dist/index.js"
@@ -20,7 +21,8 @@
20
21
  },
21
22
  "files": [
22
23
  "dist",
23
- "!dist/**/*.map"
24
+ "!dist/**/*.map",
25
+ "CHANGELOG.md"
24
26
  ],
25
27
  "license": "MIT",
26
28
  "dependencies": {
@@ -38,6 +40,10 @@
38
40
  "msw": "^2.12.13",
39
41
  "tsup": "^8.5.1",
40
42
  "typescript": "^5.9.3",
41
- "vitest": "^3.2.4"
43
+ "vitest": "^4.1.0"
44
+ },
45
+ "pnpm": {
46
+ "overrides": {},
47
+ "onlyBuiltDependencies": []
42
48
  }
43
49
  }