@intercom/cli 0.7.0 → 0.8.2

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,90 @@
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.2] - 2026-06-17
11
+
12
+ ### Fixed
13
+
14
+ - EU- and AU-hosted workspaces now send API requests to their regional host (`api.eu.intercom.io` / `api.au.intercom.io`) automatically. `intercom auth login` detects the workspace's region from `/me` and stores it, so commands route correctly. This fixes `fin workflows upload` (and `enable`/`disable`) failing with `No file provided` for EU/AU workspaces, which happened because uploads to the US host were proxied cross-region in a way that dropped the file. Existing EU/AU users should run `intercom auth login --token <token>` once to record their region. You can still override the host with `INTERCOM_API_BASE_URL`, or set `INTERCOM_REGION=eu|au|us` when authenticating via the `INTERCOM_TOKEN` environment variable.
15
+
16
+ ## [0.8.1] - 2026-06-16
17
+
18
+ ### Fixed
19
+
20
+ - 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.
21
+ - `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.
22
+
23
+ ## [0.7.0] - 2026-06-10
24
+
25
+ ### Added
26
+
27
+ - `intercom changelog` command to view the CLI's release notes from the terminal
28
+ - `fin data-connectors versions <id>` to view a data connector's version history
29
+ - Auto-enable fin-sdk and retry when a fin command hits the gate (#102)
30
+
31
+ ### Changed
32
+
33
+ - `fin data-connectors list` now shows each connector's state
34
+ - `fin data-connectors get` now shows the full connector definition (method, URL, inputs, outputs)
35
+ - `fin data-connectors create` now takes `--name` and returns the URL to finish configuring the connector in the UI
36
+
37
+ ### Fixed
38
+
39
+ - Handle `login_verification` redirect in CLI sign-in (#99)
40
+
41
+ ## [0.6.0] - 2026-05-25
42
+
43
+ ### Added
44
+
45
+ - `fin workflows upload`, `fin workflows enable`, and `fin workflows disable` (#95)
46
+ - `fin monitor pull-flagged` and `fin conversation bulk-send` (#83)
47
+
48
+ ### Changed
49
+
50
+ - Strip internal fields from conversation responses (#97)
51
+
52
+ ### Fixed
53
+
54
+ - Target the correct workspace during bootstrap (#96)
55
+ - Parse `fin workflows get` output as YAML, not JSON (#90)
56
+
57
+ ## [0.5.0] - 2026-04-27
58
+
59
+ ### Added
60
+
61
+ - Emit a Claude Code plugin hint from `--help` and `auth login` (#91)
62
+
63
+ ## [0.4.0] - 2026-04-20
64
+
65
+ ### Added
66
+
67
+ - `articles search` subcommand (#85)
68
+ - `pricing` command to fetch live pricing information (#67)
69
+ - `fin guidance create` command (#69)
70
+ - `fin` as a binary alias for `intercom` (#66)
71
+ - `intercom config` command (#43)
72
+ - `auth list` and `auth switch` commands (#41)
73
+ - Global `--dry-run` / `-n` flag (#48)
74
+ - `--input` flag on the `api` command and search subcommands (#46)
75
+ - Color output and a `--wide` flag for tables (#44)
76
+ - 5xx retry with exponential backoff (#49)
77
+
78
+ ### Changed
79
+
80
+ - Strip article bodies from list/search by default (#86)
81
+ - Default messenger snippets to JWT authentication (#81)
82
+
83
+ ### Fixed
84
+
85
+ - Improve `setup` command reliability for greenfield workspaces (#64)
86
+
87
+ [0.7.0]: https://github.com/intercom/cli/releases/tag/v0.7.0
88
+ [0.6.0]: https://github.com/intercom/cli/releases/tag/v0.6.0
89
+ [0.5.0]: https://github.com/intercom/cli/releases/tag/v0.5.0
90
+ [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
@@ -106,4 +106,4 @@ export {
106
106
  formatJson,
107
107
  applyJq
108
108
  };
109
- //# sourceMappingURL=chunk-3V2ONMAU.js.map
109
+ //# sourceMappingURL=chunk-HMPO42TQ.js.map
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  applyJq,
10
10
  detectAuthType,
11
11
  formatJson
12
- } from "./chunk-3V2ONMAU.js";
12
+ } from "./chunk-HMPO42TQ.js";
13
13
 
14
14
  // src/index.ts
15
15
  import { Command } from "commander";
@@ -61,6 +61,25 @@ 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;
@@ -189,9 +208,21 @@ var IntercomClient = class _IntercomClient {
189
208
  return response.json();
190
209
  }
191
210
  }
192
- const errorBody = await response.json().catch(() => ({}));
193
- const errorMsg = errorBody?.errors?.[0]?.message ?? `HTTP ${response.status}`;
194
- const errorCode = errorBody?.errors?.[0]?.code ?? "";
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 ?? "";
195
226
  if (response.status === 401) {
196
227
  throw new AuthError(
197
228
  `Authentication expired or invalid: ${errorMsg}. Run "intercom auth login" to re-authenticate.`
@@ -316,6 +347,43 @@ var IntercomClient = class _IntercomClient {
316
347
  }
317
348
  };
318
349
 
350
+ // src/api/regions.ts
351
+ var REGION_BASE_URLS = {
352
+ us: "https://api.intercom.io",
353
+ eu: "https://api.eu.intercom.io",
354
+ au: "https://api.au.intercom.io"
355
+ };
356
+ function regionBaseUrl(region) {
357
+ if (!region) return void 0;
358
+ return REGION_BASE_URLS[region];
359
+ }
360
+ function normalizeRegion(value) {
361
+ if (typeof value !== "string") return void 0;
362
+ switch (value.trim().toLowerCase()) {
363
+ case "us":
364
+ case "usa":
365
+ case "united states":
366
+ return "us";
367
+ case "eu":
368
+ case "europe":
369
+ return "eu";
370
+ case "au":
371
+ case "australia":
372
+ return "au";
373
+ default:
374
+ return void 0;
375
+ }
376
+ }
377
+ function detectRegion(me) {
378
+ if (!me || typeof me !== "object") return void 0;
379
+ const m = me;
380
+ const app = m.app;
381
+ return normalizeRegion(app?.region ?? m.region);
382
+ }
383
+ function resolveBaseUrl(region) {
384
+ return process.env.INTERCOM_API_BASE_URL || regionBaseUrl(region) || void 0;
385
+ }
386
+
319
387
  // src/auth/store.ts
320
388
  import { readFileSync, mkdirSync } from "fs";
321
389
 
@@ -552,7 +620,7 @@ async function resolveClient(options = {}) {
552
620
  token: envToken,
553
621
  authType: detectAuthType(envToken),
554
622
  appId: process.env.INTERCOM_APP_ID ?? "",
555
- baseUrl: process.env.INTERCOM_API_BASE_URL || void 0,
623
+ baseUrl: resolveBaseUrl(normalizeRegion(process.env.INTERCOM_REGION)),
556
624
  verbose: options.verbose,
557
625
  maxRetries: options.retry === false ? 0 : void 0,
558
626
  timeoutMs: options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0
@@ -582,7 +650,7 @@ async function resolveClient(options = {}) {
582
650
  token: cred.token,
583
651
  authType: cred.auth_type,
584
652
  appId: cred.app_id,
585
- baseUrl: process.env.INTERCOM_API_BASE_URL || void 0,
653
+ baseUrl: resolveBaseUrl(cred.region),
586
654
  verbose: options.verbose,
587
655
  maxRetries: options.retry === false ? 0 : void 0,
588
656
  timeoutMs: options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0
@@ -802,11 +870,13 @@ async function loginWithToken(token) {
802
870
  const app = me.app;
803
871
  const appId = app?.id_code ?? me.id;
804
872
  const name = app?.name ?? "Unknown";
873
+ const region = detectRegion(me);
805
874
  const configDir2 = process.env.INTERCOM_CONFIG_DIR;
806
875
  const store = new CredentialStore(createDefaultBackend(configDir2));
807
- await store.set(appId, { token, auth_type: authType, app_id: appId });
808
- await addWorkspace(configDir2, { app_id: appId, name });
809
- console.log(`Authenticated to ${name} (${appId})`);
876
+ await store.set(appId, { token, auth_type: authType, app_id: appId, region });
877
+ await addWorkspace(configDir2, { app_id: appId, name, region });
878
+ const regionLabel = region ? ` [${region.toUpperCase()}]` : "";
879
+ console.log(`Authenticated to ${name} (${appId})${regionLabel}`);
810
880
  emitClaudeCodeHint();
811
881
  }
812
882
 
@@ -2545,9 +2615,13 @@ function registerProceduresCommand(fin) {
2545
2615
  }
2546
2616
 
2547
2617
  // src/commands/fin/data-connectors.ts
2618
+ function summarizeKeys(obj) {
2619
+ if (!obj) return "";
2620
+ return Object.keys(obj).join(", ");
2621
+ }
2548
2622
  function registerDataConnectorsCommand(fin) {
2549
2623
  const dataConnectors = fin.command("data-connectors").description("Manage Fin SDK data connectors");
2550
- 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() {
2624
+ 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() {
2551
2625
  const opts = this.optsWithGlobals();
2552
2626
  const client = await resolveClient(opts);
2553
2627
  const result = await client.get("/fin-sdk/manifest");
@@ -2559,33 +2633,38 @@ function registerDataConnectorsCommand(fin) {
2559
2633
  }
2560
2634
  const rows = items.map((item) => ({
2561
2635
  ID: String(item.id ?? ""),
2562
- NAME: String(item.name ?? "")
2636
+ NAME: String(item.name ?? ""),
2637
+ STATE: String(item.state ?? "")
2563
2638
  }));
2564
- return table({ columns: ["ID", "NAME"], rows });
2639
+ return table({ columns: ["ID", "NAME", "STATE"], rows });
2565
2640
  }, items);
2566
2641
  });
2567
- 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() {
2642
+ 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(
2643
+ "after",
2644
+ `
2645
+ The create endpoint only sets the name \u2014 it returns a web_url where you finish
2646
+ configuring the connector (request, inputs, outputs, auth) in the Intercom UI.
2647
+
2648
+ Example:
2649
+ $ intercom fin data-connectors create --name "Order lookup"`
2650
+ ).action(async function() {
2568
2651
  const opts = this.optsWithGlobals();
2569
2652
  const client = await resolveClient(opts);
2570
- const fields = opts.field ?? [];
2571
- const body = {};
2572
- for (const f of fields) {
2573
- const eq = f.indexOf("=");
2574
- if (eq === -1) throw new Error(`Invalid field format: ${f}. Expected key=value`);
2575
- body[f.slice(0, eq)] = f.slice(eq + 1);
2576
- }
2577
- const result = await client.post("/fin-sdk/data-connectors", body);
2653
+ const result = await client.post("/fin-sdk/data-connectors", {
2654
+ name: opts.name
2655
+ });
2578
2656
  render(
2579
2657
  result,
2580
2658
  opts,
2581
2659
  () => detail({
2582
- "ID": String(result.id ?? ""),
2583
- "Name": String(result.name ?? ""),
2584
- "Status": result.status != null ? String(result.status) : void 0
2660
+ ID: String(result.id ?? ""),
2661
+ Name: String(result.name ?? ""),
2662
+ "Web URL": result.web_url != null ? String(result.web_url) : void 0,
2663
+ "Next step": result.message != null ? String(result.message) : void 0
2585
2664
  })
2586
2665
  );
2587
2666
  });
2588
- 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) {
2667
+ 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) {
2589
2668
  const opts = this.optsWithGlobals();
2590
2669
  const client = await resolveClient(opts);
2591
2670
  const result = await client.get(`/fin-sdk/data-connectors/${id}/download`);
@@ -2593,11 +2672,36 @@ function registerDataConnectorsCommand(fin) {
2593
2672
  result,
2594
2673
  opts,
2595
2674
  () => detail({
2596
- "ID": String(result.id ?? ""),
2597
- "Name": String(result.name ?? "")
2675
+ ID: String(result.id ?? ""),
2676
+ Name: String(result.name ?? ""),
2677
+ State: result.state != null ? String(result.state) : void 0,
2678
+ Description: result.description != null ? String(result.description) : void 0,
2679
+ "Direct Fin usage": result.enable_direct_usage != null ? String(result.enable_direct_usage) : void 0,
2680
+ Method: result.request?.method != null ? String(result.request.method) : void 0,
2681
+ URL: result.request?.url != null ? String(result.request.url) : void 0,
2682
+ Inputs: summarizeKeys(result.inputs) || void 0,
2683
+ Outputs: summarizeKeys(result.outputs) || void 0
2598
2684
  })
2599
2685
  );
2600
2686
  });
2687
+ 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) {
2688
+ const opts = this.optsWithGlobals();
2689
+ const client = await resolveClient(opts);
2690
+ const result = await client.get(`/fin-sdk/data-connectors/${id}/versions`);
2691
+ const items = result.versions ?? [];
2692
+ render(items, opts, () => {
2693
+ if (items.length === 0) {
2694
+ return "No versions found.";
2695
+ }
2696
+ const rows = items.map((v) => ({
2697
+ STATUS: String(v.status ?? ""),
2698
+ CREATED: String(v.created_at ?? ""),
2699
+ AUTHOR: String(v.author?.name ?? ""),
2700
+ NOTE: String(v.note ?? "")
2701
+ }));
2702
+ return table({ columns: ["STATUS", "CREATED", "AUTHOR", "NOTE"], rows });
2703
+ }, items);
2704
+ });
2601
2705
  }
2602
2706
 
2603
2707
  // src/commands/fin/guidance.ts
@@ -2708,8 +2812,8 @@ function registerAudiencesCommand(fin) {
2708
2812
  import { readFile as readFile3 } from "fs/promises";
2709
2813
  import { basename as basename2 } from "path";
2710
2814
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2711
- var STATE_LIVE = 1;
2712
- var STATE_PAUSED = 2;
2815
+ var STATE_LIVE = "live";
2816
+ var STATE_PAUSED = "paused";
2713
2817
  function registerWorkflowsCommand(fin) {
2714
2818
  const workflows = fin.command("workflows").description("Manage Fin SDK workflows");
2715
2819
  workflows.command("list").description("List Fin SDK workflows").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
@@ -4025,7 +4129,7 @@ function registerPricingCommand(program) {
4025
4129
  }
4026
4130
  const markdown = await response.text();
4027
4131
  if (opts.json) {
4028
- const { formatJson: formatJson2 } = await import("./json-OEUXXCTX.js");
4132
+ const { formatJson: formatJson2 } = await import("./json-BJB55URV.js");
4029
4133
  console.log(formatJson2({ url: PRICING_URL, content: markdown }));
4030
4134
  } else {
4031
4135
  console.log(markdown);
@@ -4127,6 +4231,104 @@ function registerConfigCommand(program) {
4127
4231
  });
4128
4232
  }
4129
4233
 
4234
+ // src/commands/changelog.ts
4235
+ import pc2 from "picocolors";
4236
+
4237
+ // src/changelog.ts
4238
+ import { readFileSync as readFileSync5 } from "fs";
4239
+ import { fileURLToPath } from "url";
4240
+ var RELEASE_RE = /^##\s+\[?([^\]]+?)\]?(?:\s+-\s+(.+))?$/;
4241
+ var SECTION_RE = /^###\s+(.+)$/;
4242
+ var ENTRY_RE = /^[-*]\s+(.+)$/;
4243
+ function parseChangelog(markdown) {
4244
+ const releases = [];
4245
+ let release = null;
4246
+ let section = null;
4247
+ for (const raw of markdown.split("\n")) {
4248
+ const line = raw.trimEnd();
4249
+ const releaseMatch = line.match(RELEASE_RE);
4250
+ if (releaseMatch) {
4251
+ release = { version: releaseMatch[1].trim(), date: releaseMatch[2]?.trim() ?? "", sections: [] };
4252
+ section = null;
4253
+ releases.push(release);
4254
+ continue;
4255
+ }
4256
+ if (!release) continue;
4257
+ const sectionMatch = line.match(SECTION_RE);
4258
+ if (sectionMatch) {
4259
+ section = { title: sectionMatch[1].trim(), entries: [] };
4260
+ release.sections.push(section);
4261
+ continue;
4262
+ }
4263
+ const entryMatch = line.match(ENTRY_RE);
4264
+ if (entryMatch && section) {
4265
+ section.entries.push(entryMatch[1].trim());
4266
+ }
4267
+ }
4268
+ return releases;
4269
+ }
4270
+ var cached;
4271
+ function loadChangelog() {
4272
+ if (cached) return cached;
4273
+ const path = fileURLToPath(new URL("../CHANGELOG.md", import.meta.url));
4274
+ cached = parseChangelog(readFileSync5(path, "utf8"));
4275
+ return cached;
4276
+ }
4277
+
4278
+ // src/commands/changelog.ts
4279
+ function normalizeVersion(v) {
4280
+ return v.replace(/^v/i, "");
4281
+ }
4282
+ function colorEnabled() {
4283
+ return !!process.stdout.isTTY && !process.env.NO_COLOR;
4284
+ }
4285
+ function renderHuman(releases) {
4286
+ const color = colorEnabled();
4287
+ const out = [];
4288
+ for (const release of releases) {
4289
+ const heading = release.date ? `${release.version} \u2014 ${release.date}` : release.version;
4290
+ out.push(color ? pc2.bold(heading) : heading);
4291
+ for (const section of release.sections) {
4292
+ out.push(` ${color ? pc2.dim(section.title) : section.title}`);
4293
+ for (const entry of section.entries) {
4294
+ out.push(` \u2022 ${entry}`);
4295
+ }
4296
+ }
4297
+ out.push("");
4298
+ }
4299
+ return out.join("\n").trimEnd();
4300
+ }
4301
+ function registerChangelogCommand(program) {
4302
+ 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(
4303
+ "after",
4304
+ `
4305
+ Examples:
4306
+ $ intercom changelog Show the full release history
4307
+ $ intercom changelog --limit 1 Show only the latest release
4308
+ $ intercom changelog 0.7.0 Show a specific version
4309
+ $ intercom changelog --json Machine-readable release history`
4310
+ ).action(async function(version) {
4311
+ const opts = this.optsWithGlobals();
4312
+ let releases = loadChangelog();
4313
+ if (version) {
4314
+ const wanted = normalizeVersion(version);
4315
+ const match = releases.find((r) => normalizeVersion(r.version) === wanted);
4316
+ if (!match) {
4317
+ const known = releases.map((r) => r.version).join(", ");
4318
+ throw new CLIError(`No changelog entry for version "${version}". Known versions: ${known}`);
4319
+ }
4320
+ releases = [match];
4321
+ } else if (opts.limit !== void 0) {
4322
+ const limit = parseInt(String(opts.limit), 10);
4323
+ if (isNaN(limit) || limit <= 0) {
4324
+ throw new CLIError("--limit must be a positive integer");
4325
+ }
4326
+ releases = releases.slice(0, limit);
4327
+ }
4328
+ render(releases, opts, () => renderHuman(releases), releases);
4329
+ });
4330
+ }
4331
+
4130
4332
  // src/index.ts
4131
4333
  function detectBinaryName() {
4132
4334
  const argv1 = process.argv[1] ?? "";
@@ -4171,6 +4373,7 @@ Environment: Set INTERCOM_TOKEN to skip login entirely.`;
4171
4373
  registerPricingCommand(program);
4172
4374
  registerCompletionCommand(program);
4173
4375
  registerConfigCommand(program);
4376
+ registerChangelogCommand(program);
4174
4377
  program.hook("preAction", (thisCommand, actionCommand) => {
4175
4378
  const rootOpts = thisCommand.opts();
4176
4379
  if (rootOpts.agent && rootOpts.human) {
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  applyJq,
4
4
  formatJson
5
- } from "./chunk-3V2ONMAU.js";
5
+ } from "./chunk-HMPO42TQ.js";
6
6
  export {
7
7
  applyJq,
8
8
  formatJson
9
9
  };
10
- //# sourceMappingURL=json-OEUXXCTX.js.map
10
+ //# sourceMappingURL=json-BJB55URV.js.map
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@intercom/cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.2",
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": {
@@ -39,5 +41,9 @@
39
41
  "tsup": "^8.5.1",
40
42
  "typescript": "^5.9.3",
41
43
  "vitest": "^4.1.0"
44
+ },
45
+ "pnpm": {
46
+ "overrides": {},
47
+ "onlyBuiltDependencies": []
42
48
  }
43
49
  }