@curviate/cli 0.21.1 → 0.23.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 (30) hide show
  1. package/CHANGELOG.md +236 -116
  2. package/README.md +18 -18
  3. package/dist/{account-OIV73PXX.js → account-MIPWTAG5.js} +27 -27
  4. package/dist/chunk-3BO6GVS2.js +182 -0
  5. package/dist/{chunk-HZFK45IW.js → chunk-H6XD3F66.js} +1 -1
  6. package/dist/{chunk-VPKMJYIF.js → chunk-JA3NNST6.js} +9 -1
  7. package/dist/{chunk-OE7OMTCM.js → chunk-MBO4LBLY.js} +19 -23
  8. package/dist/{chunk-M6UDWFHX.js → chunk-PMRQXBCP.js} +1 -1
  9. package/dist/{chunk-WYRYCTUM.js → chunk-RFUO2G3M.js} +2 -2
  10. package/dist/{chunk-QJZ3LWOX.js → chunk-RJTG5YWE.js} +1 -1
  11. package/dist/{chunk-DMQZEPQE.js → chunk-UEAX6KUB.js} +10 -0
  12. package/dist/cli.js +58 -26
  13. package/dist/{comment-W4XTOTHN.js → comment-TUXC7K72.js} +19 -22
  14. package/dist/{company-NT4VO6JV.js → company-LHYDAO2C.js} +31 -34
  15. package/dist/{connect-VXWRLGQZ.js → connect-5S4TFQAF.js} +23 -26
  16. package/dist/{exit-codes-63JE5GM5.js → exit-codes-KAVZN5BQ.js} +1 -1
  17. package/dist/{feed-4Q3B56WI.js → feed-ISNH4NXB.js} +8 -11
  18. package/dist/{group-43PHTFP7.js → group-PHGUOOJE.js} +15 -15
  19. package/dist/{inbox-GZAOUWZP.js → inbox-3IQPPQMH.js} +14 -17
  20. package/dist/{inboxes-6AZOB43U.js → inboxes-JK36F6SK.js} +11 -14
  21. package/dist/{job-VOVQZGCK.js → job-JQG34ATV.js} +19 -22
  22. package/dist/{message-FIEWH2BU.js → message-2WIZESSM.js} +5 -3
  23. package/dist/{notification-65V67NWK.js → notification-ORSZY2IJ.js} +10 -13
  24. package/dist/{post-G4IMWUE2.js → post-JRJMGV2I.js} +22 -25
  25. package/dist/{profile-GADE33FY.js → profile-SUKHRWQP.js} +27 -30
  26. package/dist/{recruiter-LI2TMRHY.js → recruiter-NIKKDGMR.js} +38 -41
  27. package/dist/{sales-nav-OXLRN6YK.js → sales-nav-4MFL2JSE.js} +23 -26
  28. package/dist/{search-V35MFTXL.js → search-MZQJDJTX.js} +26 -29
  29. package/dist/{webhook-5HXMJQFO.js → webhook-7ESDSIRU.js} +9 -9
  30. package/package.json +4 -3
package/CHANGELOG.md CHANGED
@@ -8,6 +8,126 @@ a new command or flag is a minor; a breaking command/flag/exit-code change is a
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.23.0] - 2026-08-10
12
+
13
+ A security and correctness release. Upgrade promptly if `--account` is ever
14
+ set from anything other than a literal you typed yourself (an environment
15
+ variable, a config profile, or agent- or model-generated text): every
16
+ published version through 0.22.0 interpolated it into the request path with
17
+ no validation at all, so a value carrying a slash, `..`, a question mark, a
18
+ hash, or a percent sign could redirect a request, including a write, to a
19
+ different endpoint on the API host. This release also lets `--account` take a
20
+ connected account's name, not just its id.
21
+
22
+ ### Fixed
23
+
24
+ - **`--account` could redirect a request to a different endpoint.**
25
+ `inbox mark-read` on `--account 'x/../../../v1/accounts'` built
26
+ `PATCH /v1/accounts/chats/chat_1` instead of touching the account the caller
27
+ named. `--account 'a?x=1'` injected a query string into the middle of the
28
+ path. Neither needed a literal `..`: the URL Standard percent-decodes when it
29
+ decides whether a segment is a double-dot path segment, so `%2e%2e` walked up
30
+ the path with no slash in the value at all. Every account-scoped command was
31
+ reachable this way, since all of them build the request URL from `--account`.
32
+ The value is now checked before any request is built, and a value that could
33
+ redirect one is refused with `[INVALID_PATH_SEGMENT]` (exit `2`) naming the
34
+ character it contains, rather than being sent.
35
+
36
+ - **`group get` / `group members` never actually accepted a group URL,
37
+ despite documenting it.** The help and this file both claimed the server
38
+ extracted the numeric id from a full `https://www.linkedin.com/groups/...`
39
+ URL passed through verbatim; it does not, and cannot: the URL's own slashes
40
+ split it into several path segments and the request landed on a route that
41
+ does not exist. The numeric id is now extracted client-side, the same way
42
+ member, company, chat, and job URLs already are. A bare numeric id is
43
+ unaffected.
44
+
45
+ ### Added
46
+
47
+ - **`--account` accepts a connected account's name, not just its id.** A value
48
+ that is not shaped like an account id is looked up against
49
+ `accounts.list`: an exact name match wins outright, otherwise a unique
50
+ prefix match resolves. A name matching more than one connected account
51
+ exits with `[ACCOUNT_AMBIGUOUS]` (exit `2`) rather than guessing, since
52
+ acting on the wrong live persona cannot be undone. A name matching none
53
+ exits with `[ACCOUNT_NAME_NOT_FOUND]` (exit `4`) and lists what is
54
+ connected. On an API key with more connected accounts than the resolver
55
+ reads (250 per page, 10 pages), a name lookup exits with
56
+ `[ACCOUNT_LIST_TRUNCATED]` (exit `2`) instead of matching against a list
57
+ that might be missing the very account the name would have matched; pass
58
+ the id instead, which needs no lookup. An id-shaped value still costs no
59
+ extra request. `--preview` never issues the lookup, consistent with it
60
+ never calling the API.
61
+
62
+ ### Changed
63
+
64
+ - **`@curviate/sdk` dependency bumped to `^0.20.1`.** ^0.20.0 still resolves
65
+ the vulnerable 0.20.0 build on a fresh install or an old lockfile; 0.20.1 is
66
+ a hard floor at the fix. Percent-encoding of every other command's path
67
+ parameters (chat, job, group, company and member ids, and so on) is the
68
+ SDK's job as of 0.20.0, not this CLI's: a previous CLI-side guard that
69
+ inspected each call's leading string argument has been removed, because it
70
+ could not know which argument actually became a path segment and was wrong
71
+ in both directions, missing a path parameter passed inside an object and
72
+ rejecting a body field that never reached a path (`post save <share URL>`
73
+ was one such false rejection). `--account` is guarded separately, above,
74
+ because it is the one value this CLI genuinely understands the meaning of.
75
+
76
+ ## [0.22.0] - 2026-08-07
77
+
78
+ Two correctness fixes for the same underlying failure: a command that answers
79
+ a different question than the one you asked, without saying so.
80
+
81
+ ### Changed
82
+
83
+ - **A trailing argument a command cannot use is now an error (exit 2), not
84
+ something to ignore.** `curviate profile me relations` used to return your
85
+ own profile at exit 0 with nothing on stderr, so a caller reading `.items`
86
+ found nothing and concluded the account had no connections. Any unusable
87
+ trailing token behaved the same way, so a typo such as `profile me
88
+ reltaions` was swallowed just as quietly. The check now runs on the command
89
+ that is actually about to execute, whatever route reached it, and the
90
+ message names what you typed, how many positional arguments it takes, and
91
+ its own `--help`. When the stray token is a real sibling command, the
92
+ message names the form you meant, so `profile me relations` points you at
93
+ `profile relations`. **This can turn an invocation that used to exit 0 into
94
+ an exit 2.** It never turned into the answer you wanted, so a script
95
+ relying on it was already getting the wrong data; the fix makes that
96
+ visible.
97
+
98
+ - **Punctuation swept out of the published copy and the printed help.** The
99
+ README, this changelog, and the help and error strings in `src/` used
100
+ typographic characters with no plain-ASCII equivalent on the keyboard: em and
101
+ en dashes, the single-glyph ellipsis, arrows. An id placeholder now elides
102
+ with `acc_...`, a range reads `1-100`, and a mapping reads
103
+ `write->read`. No release note changed what it claims; the changelog's word
104
+ stream is byte-identical to before. Two non-ASCII characters stay on purpose:
105
+ the redaction mask `config list` prints, and the literal emoji that documents
106
+ `message react`'s own argument.
107
+
108
+ ### Fixed
109
+
110
+ - **`notices[]` now survives projection.** 0.21.1 taught the renderer to show
111
+ a response's notices, but the slim projection and `--fields` both run
112
+ before it, and several of them rebuilt the response from a fixed list of
113
+ keys that never included `notices`. So on `account list`, `connect sent`,
114
+ `connect received`, and on any single-object response under `--fields`, the
115
+ array was destroyed before it could be shown. The notice is now carried
116
+ across projection at the single point every command's output passes
117
+ through, so it reaches you regardless of the command or the flags. A
118
+ response without notices renders exactly as before.
119
+
120
+ ### Added
121
+
122
+ - **`pnpm check:copy`**, a pre-publish copy-quality gate over the npm-page copy
123
+ (README, changelog, package description), chained into `prepack` next to the
124
+ existing internal-reference scan. It fails the pack on any non-ASCII
125
+ typographic character and reports, without failing, wording from the generic
126
+ marketing register. The printed help and error strings keep their own gate,
127
+ `test/copy-tells.test.ts`, which walks the TypeScript AST so it judges string
128
+ literals and never comments; that test now covers the full character set
129
+ rather than the em dash alone.
130
+
11
131
  ## [0.21.1] - 2026-08-06
12
132
 
13
133
  ### Fixed
@@ -169,21 +289,21 @@ covered by the published SDK, so no SDK bump was required).
169
289
 
170
290
  ### Added
171
291
 
172
- - **`inbox search`** search chats via `messaging.searchChats`.
173
- - **`search groups`, `search services`, `search service-parameters`** new search
292
+ - **`inbox search`**: search chats via `messaging.searchChats`.
293
+ - **`search groups`, `search services`, `search service-parameters`**: new search
174
294
  surfaces for groups, services, and service search parameters.
175
- - **`post saved`, `post save`, `post unsave`** list, save, and unsave posts.
295
+ - **`post saved`, `post save`, `post unsave`**: list, save, and unsave posts.
176
296
  - **`company managed`, `company followers`, `company chats`, `company chat`,
177
- `company messages`, `company message`, `company search-chats`** company-inbox
297
+ `company messages`, `company message`, `company search-chats`**: company-inbox
178
298
  and managed-company read/write surface.
179
- - **`profile subscription`, `profile analytics`, `profile visitors`, `profile ssi`**
180
- account-scoped profile insight subcommands (subscription status, post/profile
299
+ - **`profile subscription`, `profile analytics`, `profile visitors`, `profile ssi`**:
300
+ account-scoped profile insight subcommands (subscription status, post/profile
181
301
  analytics, visitor list, Social Selling Index).
182
- - **`group` command group** new top-level group closing the LinkedIn Groups gap.
183
- - **`feed`, `feed home`** new command group to read the connected account's
302
+ - **`group` command group**, new top-level group closing the LinkedIn Groups gap.
303
+ - **`feed`, `feed home`**: new command group to read the connected account's
184
304
  LinkedIn home feed as agent-actionable posts.
185
305
  - **`notification`, `notification list`, `notification delete`,
186
- `notification show-less`** new command group to read and act on LinkedIn
306
+ `notification show-less`**: new command group to read and act on LinkedIn
187
307
  notification cards.
188
308
 
189
309
  Parity with the SDK surface is now 145/0 (no known gaps).
@@ -194,11 +314,11 @@ A patch aligning `company reply` with the company-inbox chat-id cutover. No brea
194
314
 
195
315
  ### Fixed
196
316
 
197
- - **`company reply <id> <chat_id> "<text>"`** now takes the normal `2-…` conversation
317
+ - **`company reply <id> <chat_id> "<text>"`** now takes the normal `2-...` conversation
198
318
  id that `company chats` returns; the endpoint resolves the page mailbox internally from
199
319
  the company identifier, so the earlier `COMPANY_` chat-id requirement is gone. `--preview`
200
320
  now always prints a "Will send as company page `<id>`" notice derived from the resolved
201
- identifier (previously it went silent for a bare `2-…` id). Reply-only and admin-gated,
321
+ identifier (previously it went silent for a bare `2-...` id). Reply-only and admin-gated,
202
322
  otherwise unchanged.
203
323
 
204
324
  ## [0.18.0] - 2026-07-17
@@ -207,10 +327,10 @@ A minor release adding the `company reply` command. No breaking changes, built a
207
327
 
208
328
  ### Added
209
329
 
210
- - **`company reply <id> <chat_id> "<text>" [--attach <file>…]`** (write, admin-gated,
330
+ - **`company reply <id> <chat_id> "<text>" [--attach <file>...]`** (write, admin-gated,
211
331
  `--preview` accepted). Replies to an existing company-inbox conversation as the page,
212
332
  via the SDK's `companies.sendMessage`. `<chat_id>` must be a `COMPANY_` chat id from
213
- `inboxes chats`, not the `2-…` id the `company` reads return; it passes through verbatim
333
+ `inboxes chats`, not the `2-...` id the `company` reads return; it passes through verbatim
214
334
  (no client-side pre-check) and a non-`COMPANY_` id is rejected by the API with a guiding
215
335
  400 naming the fix. Reply-only: it cannot start a new conversation on the page's behalf.
216
336
  `<id>` accepts a URL, slug, or numeric id, resolved to the numeric id first (including
@@ -225,7 +345,7 @@ commands. No breaking changes, built against `@curviate/sdk` 0.17.0.
225
345
 
226
346
  ### Added
227
347
 
228
- - **`company follow-invite <id> --invitee <AC…> [--invitee <AC…> ...]`** (write, admin-gated,
348
+ - **`company follow-invite <id> --invitee <AC...> [--invitee <AC...> ...]`** (write, admin-gated,
229
349
  `--preview` accepted). Invites the connected account's 1st-degree connections to follow an
230
350
  administered company page. `--invitee` is repeatable, at least one required, max 50 per
231
351
  request. All-or-nothing: for an all-valid request you get one outcome per requested invitee,
@@ -256,7 +376,7 @@ built against `@curviate/sdk` 0.16.0.
256
376
  [--cursor] [--all]` lists a single inbox's conversations, cursor-paginated
257
377
  like every other list command. Every returned chat id is send-ready: reply
258
378
  with the existing `message send <chat_id> "<text>"`. A company inbox's
259
- chat id (e.g. `COMPANY_83734124_2-…`) sends AS THE PAGE, no separate flag
379
+ chat id (e.g. `COMPANY_83734124_2-...`) sends AS THE PAGE, no separate flag
260
380
  needed. Company inboxes are reply-only and cannot start a new conversation.
261
381
  Distinct from the existing `inbox` command group (a friendlier front door
262
382
  to the account's own message-thread inbox: `messaging.listChats`/`getChat`/
@@ -293,11 +413,11 @@ A patch release fixing an interactive-terminal hang on `account link`.
293
413
 
294
414
  - **`account link --password-stdin` / `--li-at-stdin` no longer hang on an
295
415
  interactive terminal.** These flags previously read stdin to EOF, which a
296
- human paste + Enter never produces on a TTY the command hung
416
+ human paste + Enter never produces on a TTY; the command hung
297
417
  indefinitely and the pasted secret echoed on-screen. The read is now
298
418
  mode-aware: piped/redirected stdin (non-TTY) is unchanged (read to EOF,
299
419
  trimmed); an interactive TTY now prints a single cue line, then reads one
300
- no-echo line paste + Enter resolves immediately, including a paste whose
420
+ no-echo line; paste + Enter resolves immediately, including a paste whose
301
421
  clipboard content ends in a trailing newline. An empty line still falls
302
422
  through to the normal resolution order (env var, then the password
303
423
  prompt / `li_at` fail-fast).
@@ -315,15 +435,15 @@ streams, one back-compatible reaction-signature unification, and a
315
435
  ### Added
316
436
 
317
437
  - **Successor hints for removed/renamed commands.** Reaching for a command that
318
- moved or was removed in 0.15.0 `post list`, `post comment`/`comments`,
438
+ moved or was removed in 0.15.0 (`post list`, `post comment`/`comments`,
319
439
  `connect respond`, `profile connections`, `account connect-link`/`reconnect-link`/`reconnect`,
320
440
  `inbox sync`/`sync-chat`, `recruiter add-candidate`/`project-jobs`/`sync`,
321
- `sales-nav sync`, `webhook state-diff`, `company followers` now prints a
441
+ `sales-nav sync`, `webhook state-diff`, `company followers`) now prints a
322
442
  one-line "did you mean" pointer to the replacement instead of a bare
323
443
  "unknown command". The exit code is unchanged (2).
324
444
  - **`--all` NDJSON-mode notice.** When `--all` streaming engages, a one-line
325
- notice on stderr makes the format switch explicit (`--all` streams NDJSON
326
- one object per line not the `{items, cursor}` envelope), so an agent
445
+ notice on stderr makes the format switch explicit (`--all` streams NDJSON,
446
+ one object per line, not the `{items, cursor}` envelope), so an agent
327
447
  pattern-matching the plain-mode shape does not mis-parse the stream.
328
448
  - **`--page-delay <ms>` and default `--all` pacing.** `--all` now pauses a
329
449
  modest default between page fetches, keeping a long stream under the platform
@@ -331,12 +451,12 @@ streams, one back-compatible reaction-signature unification, and a
331
451
  - **`job list --state ALL`.** A best-effort client-side union across every state
332
452
  (DRAFT/OPEN/CLOSED/REVIEW/SUSPENDED): each state is queried, re-filtered
333
453
  against its own state, then merged and de-duplicated by id. There is no
334
- unified cursor each state is walked independently and `--max-pages` applies
454
+ unified cursor; each state is walked independently and `--max-pages` applies
335
455
  per state.
336
456
  - **`--fields` unknown-field warning.** Projecting a field that matches nothing
337
457
  on the response now emits one stderr warning naming the unmatched fields and
338
458
  listing the available keys, instead of silently returning `{}`. The output is
339
- unchanged the known fields still project.
459
+ unchanged; the known fields still project.
340
460
 
341
461
  ### Changed
342
462
 
@@ -364,7 +484,7 @@ streams, one back-compatible reaction-signature unification, and a
364
484
 
365
485
  ## [0.15.0] - 2026-07-11
366
486
 
367
- Full v2 API-surface parity the coupled release with `@curviate/sdk` 0.15.0. A large
487
+ Full v2 API-surface parity, the coupled release with `@curviate/sdk` 0.15.0. A large
368
488
  **breaking** minor (pre-1.0): the CLI is re-pointed onto the v2-only API, drops the
369
489
  commands whose endpoint no longer exists, relocates several verbs, and adds commands for
370
490
  the new v2 methods. Every command noun kept its intent-shaped name; only the wiring,
@@ -374,43 +494,43 @@ removed orphans, relocations, and additions changed.
374
494
 
375
495
  Commands whose underlying v2 endpoint no longer exists:
376
496
 
377
- - **`account connect-link`**, **`account reconnect-link`**, **`account reconnect`** the hosted-link and in-place re-auth flows. Connect a new account with `account link`; poll a hosted session with `account connect-session poll`.
497
+ - **`account connect-link`**, **`account reconnect-link`**, **`account reconnect`**: the hosted-link and in-place re-auth flows. Connect a new account with `account link`; poll a hosted session with `account connect-session poll`.
378
498
  - **`company followers`**
379
- - **`inbox sync`**, **`inbox sync-chat`** message history now syncs implicitly.
499
+ - **`inbox sync`**, **`inbox sync-chat`**: message history now syncs implicitly.
380
500
  - **`post list`**
381
501
  - **`recruiter sync`**, **`recruiter add-applicant`**, **`recruiter reject-applicant`**, **`recruiter job checkpoint`**
382
502
  - **`sales-nav sync`**
383
503
  - **`webhook state-diff`**
384
504
 
385
- Flags with no v2 request-side home dropped entirely (not defined, parsed, or forwarded), with no replacement:
505
+ Flags with no v2 request-side home, dropped entirely (not defined, parsed, or forwarded), with no replacement:
386
506
 
387
- - **`profile --notify`** signal-a-view has no v2 request field.
388
- - **`message inmail --surface`** the v2 send-InMail body carries no surface/type discriminator.
389
- - **`post create --video-thumbnail`** v2 posts carry media only via `--attach`.
390
- - **`search people|companies|posts|jobs --url`** from-URL search is now the bare `search <url>` form.
507
+ - **`profile --notify`**: signal-a-view has no v2 request field.
508
+ - **`message inmail --surface`**: the v2 send-InMail body carries no surface/type discriminator.
509
+ - **`post create --video-thumbnail`**: v2 posts carry media only via `--attach`.
510
+ - **`search people|companies|posts|jobs --url`**: from-URL search is now the bare `search <url>` form.
391
511
 
392
512
  ### Changed (BREAKING)
393
513
 
394
514
  Renames and relocations:
395
515
 
396
- - **`post comment`** / **`post comments`** (and `post react --comment-id`) the new **`comment`** group (`comment add`, `comment reply`, `comment react`, and the rest). Comment threads are first-class.
397
- - **`connect respond --accept` / `--decline`** **`connect accept <id>`** / **`connect decline <id>`**; the combined `respond` is removed.
398
- - **`recruiter add-candidate`** **`recruiter save-candidate <project_id> --stage-id <id> --candidate-id <id>`** (full body reshape).
399
- - **`recruiter project-jobs`** **`recruiter project-job get <project_id>`** (cardinality fix a project has at most one attached posting; single-object read, no pagination).
400
- - **`recruiter job applicants`** **`recruiter applicants <project_id>`** (the applicant list is project-scoped, not job-scoped; `--channel-id` still required).
401
- - **`profile connections`** **`profile relations`**.
402
- - **`profile endorse --skill`** **`profile endorse --endorsement-id`** value semantics unchanged (still the target's `endorsement_id`, obtained from their skills section via `profile <id> --sections skills`); the old flag name misleadingly suggested a skill name.
516
+ - **`post comment`** / **`post comments`** (and `post react --comment-id`) -> the new **`comment`** group (`comment add`, `comment reply`, `comment react`, and the rest). Comment threads are first-class.
517
+ - **`connect respond --accept` / `--decline`** -> **`connect accept <id>`** / **`connect decline <id>`**; the combined `respond` is removed.
518
+ - **`recruiter add-candidate`** -> **`recruiter save-candidate <project_id> --stage-id <id> --candidate-id <id>`** (full body reshape).
519
+ - **`recruiter project-jobs`** -> **`recruiter project-job get <project_id>`** (cardinality fix: a project has at most one attached posting; single-object read, no pagination).
520
+ - **`recruiter job applicants`** -> **`recruiter applicants <project_id>`** (the applicant list is project-scoped, not job-scoped; `--channel-id` still required).
521
+ - **`profile connections`** -> **`profile relations`**.
522
+ - **`profile endorse --skill`** -> **`profile endorse --endorsement-id`**, value semantics unchanged (still the target's `endorsement_id`, obtained from their skills section via `profile <id> --sections skills`); the old flag name misleadingly suggested a skill name.
403
523
 
404
524
  CLI-visible shape changes:
405
525
 
406
526
  - **`job publish`** now requires **`--mode`** (`FREE | PROMOTED | PROMOTED_PLUS`); `PROMOTED`/`PROMOTED_PLUS` additionally require the full `--budget-*` triple.
407
- - **`recruiter job create`** now requires **`--project-name`** and takes the full v2 job body `--employment-status` replaces the pre-v2 `--employment-type` on this command, alongside the company / workplace / location flags.
408
- - **`recruiter message new`** is now **JSON-only** (file/voice/video attachments ride the body as base64 no multipart) and requires **`--subject`** and **`--signature`**.
527
+ - **`recruiter job create`** now requires **`--project-name`** and takes the full v2 job body; `--employment-status` replaces the pre-v2 `--employment-type` on this command, alongside the company / workplace / location flags.
528
+ - **`recruiter message new`** is now **JSON-only** (file/voice/video attachments ride the body as base64, no multipart) and requires **`--subject`** and **`--signature`**.
409
529
 
410
530
  Dependency and request grammar:
411
531
 
412
532
  - **`@curviate/sdk` bumped to `0.15.0`.**
413
- - **Account-first path grammar.** Every account-scoped request now addresses the account in the URL path instead of a query/body field. This is handled entirely inside the SDK no CLI syntax changes but every command's underlying request moved.
533
+ - **Account-first path grammar.** Every account-scoped request now addresses the account in the URL path instead of a query/body field. This is handled entirely inside the SDK, no CLI syntax changes, but every command's underlying request moved.
414
534
 
415
535
  ### Added
416
536
 
@@ -418,7 +538,7 @@ New **`comment`** command group (the comment-thread surface):
418
538
 
419
539
  - `comment list <post_id>`, `comment add`, `comment reply`, `comment edit`, `comment delete`, `comment replies`, `comment react`, `comment reactions`, `comment unreact`, `comment user`.
420
540
 
421
- Job-posting management the **`job`** family:
541
+ Job-posting management, the **`job`** family:
422
542
 
423
543
  - `job list`, `job create`, `job update`, `job budget`, `job publish`, `job close`, `job applicants`, `job applicant get`, and `job applicant resume` (binary résumé download via `-o`).
424
544
 
@@ -444,68 +564,68 @@ Sales Navigator:
444
564
 
445
565
  Account:
446
566
 
447
- - **`account link --account-id <acc_…>`** (optional, non-breaking) re-authenticate an existing account **in place** (reconnect): passing the id makes `account link` an in-place reconnect of that account; omit it for an ordinary fresh connect (unchanged no `account_id` is sent). This is the reconnect path now that the hosted `account reconnect` / `account reconnect-link` commands are removed.
567
+ - **`account link --account-id <acc_...>`** (optional, non-breaking): re-authenticate an existing account **in place** (reconnect): passing the id makes `account link` an in-place reconnect of that account; omit it for an ordinary fresh connect (unchanged, no `account_id` is sent). This is the reconnect path now that the hosted `account reconnect` / `account reconnect-link` commands are removed.
448
568
 
449
569
  Exit-code mapping:
450
570
 
451
- - **`ACCOUNT_ALREADY_LINKED`** and **`LINKEDIN_OPERATION_NOT_SUPPORTED`** are now present in `EXIT_CODE_MAP` (exit `8`, account / connection state grouped with `ACCOUNT_RESTRICTED`/`RESOURCE_ACCESS_RESTRICTED`); previously `ACCOUNT_ALREADY_LINKED` was already a valid SDK `ErrorCode` but had no exit-code entry, and `LINKEDIN_OPERATION_NOT_SUPPORTED` is a new SDK code (a permanent LinkedIn platform limitation for the attempted operation, e.g. listing a non-self user's following list). Both were silently falling through to the default exit `1`; the exhaustiveness test now covers them.
452
- - **`CONNECTION_REQUEST_CONFLICT`** (exit `8`, account / connection state the documented "already invited or already connected" contract on a `connect` retry) and **`RATE_LIMITED`** (exit `6`, rate-limited a general/unscoped rate-limit signal alongside `RATE_LIMIT_ACCOUNT`/`RATE_LIMIT_TENANT`/`PLATFORM_RATE_LIMIT`/`LINKEDIN_RATE_LIMITED`) are new SDK error codes, both now present in `EXIT_CODE_MAP`. Picked up via the refreshed `@curviate/sdk` 0.15.0 tarball dependency (pnpm-lock.yaml integrity hash only).
571
+ - **`ACCOUNT_ALREADY_LINKED`** and **`LINKEDIN_OPERATION_NOT_SUPPORTED`** are now present in `EXIT_CODE_MAP` (exit `8`, account / connection state, grouped with `ACCOUNT_RESTRICTED`/`RESOURCE_ACCESS_RESTRICTED`); previously `ACCOUNT_ALREADY_LINKED` was already a valid SDK `ErrorCode` but had no exit-code entry, and `LINKEDIN_OPERATION_NOT_SUPPORTED` is a new SDK code (a permanent LinkedIn platform limitation for the attempted operation, e.g. listing a non-self user's following list). Both were silently falling through to the default exit `1`; the exhaustiveness test now covers them.
572
+ - **`CONNECTION_REQUEST_CONFLICT`** (exit `8`, account / connection state, the documented "already invited or already connected" contract on a `connect` retry) and **`RATE_LIMITED`** (exit `6`, rate-limited, a general/unscoped rate-limit signal alongside `RATE_LIMIT_ACCOUNT`/`RATE_LIMIT_TENANT`/`PLATFORM_RATE_LIMIT`/`LINKEDIN_RATE_LIMITED`) are new SDK error codes, both now present in `EXIT_CODE_MAP`. Picked up via the refreshed `@curviate/sdk` 0.15.0 tarball dependency (pnpm-lock.yaml integrity hash only).
453
573
 
454
574
  ### Fixed
455
575
 
456
- - **`company <id> employees` / `company <id> posts` / `company <id> jobs` (id-first form) no longer silently returns the base company profile.** The router bound `<id>` and dropped the trailing sub-resource word, so the id-first form quietly returned the company profile with exit 0. It now routes the id-first form to the sub-resource (equivalent to `company <sub> <id>`), or exits 2 with an actionable error on a genuinely unexpected extra argument never a silent wrong result. The guard is applied uniformly across every bare-form command group.
576
+ - **`company <id> employees` / `company <id> posts` / `company <id> jobs` (id-first form) no longer silently returns the base company profile.** The router bound `<id>` and dropped the trailing sub-resource word, so the id-first form quietly returned the company profile with exit 0. It now routes the id-first form to the sub-resource (equivalent to `company <sub> <id>`), or exits 2 with an actionable error on a genuinely unexpected extra argument, never a silent wrong result. The guard is applied uniformly across every bare-form command group.
457
577
  - **`company employees|posts|jobs <slug>` (or a company URL) now works.** The three sub-resources previously required the numeric company id and erred on a handle; they now auto-resolve a slug/URL to the numeric id the same way the bare `company <slug>` retrieve does (a numeric id still passes straight through; a genuinely unresolvable identifier surfaces the not-found error).
458
- - **`profile follow <slug>` / `profile unfollow <slug>` (or a member URL) now work.** The follow endpoint accepts only a provider id, so a slug returned "not found"; both commands now resolve the identifier to the member's provider id first the same auto-resolution `profile`, `connect`, and `message` already do.
459
- - **`company posts` / `search posts` slim `--json` output no longer emits a permanently-null `post_urn`/`posted_at` and silently drops the post's own id (D13).** Both endpoints share the identical v2 item schema (`{id, share_url, text, author, reaction_count, comment_count, repost_count, is_repost, attachments, reactions, permissions}`) `post_urn` was never a real key and `posted_at` doesn't exist on this resource at all. Slim output now surfaces the real `id`; `--fields` projects the real v2 keys. `share_url`/`repost_count`/`is_repost`/`attachments`/`reactions`/`permissions`/the full `author` object remain verbose-only.
460
- - **`company <id>` slim `--json` output no longer emits permanently-null `employee_count`/`employee_count_range`/`followers_count`/`foundation_date`, and drops the entirely-fictitious `messaging` field.** The real v2 company-profile response nests headcount data at `insights.headcount` / `insights.headcount_range.from` (the range has no upper bound at all documented open-ended-high, so no `to` is invented), the establishment date is a bare year at `establishment_year` (not a date string), the follower count key is singular, and there is no `messaging` field anywhere on this resource. Slim output now surfaces real `employee_count` / `employee_count_range` (`{from}` only) values; **`foundation_date` is renamed `establishment_year`** and **`followers_count` is renamed `follower_count`** (both now real, non-null); `messaging` is removed outright. `headquarters` (synthesized from `locations`) now reads the real `country_code`/`postal_code` location keys the fictitious `country` key never existed and always projected null, and is renamed `country_code`; `postal_code` is added. `area` (region/state, e.g. "Washington") stays it's real and often populated (verified live), even though the SDK's OpenAPI-generated types don't declare it for this endpoint.
578
+ - **`profile follow <slug>` / `profile unfollow <slug>` (or a member URL) now work.** The follow endpoint accepts only a provider id, so a slug returned "not found"; both commands now resolve the identifier to the member's provider id first, the same auto-resolution `profile`, `connect`, and `message` already do.
579
+ - **`company posts` / `search posts` slim `--json` output no longer emits a permanently-null `post_urn`/`posted_at` and silently drops the post's own id (D13).** Both endpoints share the identical v2 item schema (`{id, share_url, text, author, reaction_count, comment_count, repost_count, is_repost, attachments, reactions, permissions}`), `post_urn` was never a real key and `posted_at` doesn't exist on this resource at all. Slim output now surfaces the real `id`; `--fields` projects the real v2 keys. `share_url`/`repost_count`/`is_repost`/`attachments`/`reactions`/`permissions`/the full `author` object remain verbose-only.
580
+ - **`company <id>` slim `--json` output no longer emits permanently-null `employee_count`/`employee_count_range`/`followers_count`/`foundation_date`, and drops the entirely-fictitious `messaging` field.** The real v2 company-profile response nests headcount data at `insights.headcount` / `insights.headcount_range.from` (the range has no upper bound at all, documented open-ended-high, so no `to` is invented), the establishment date is a bare year at `establishment_year` (not a date string), the follower count key is singular, and there is no `messaging` field anywhere on this resource. Slim output now surfaces real `employee_count` / `employee_count_range` (`{from}` only) values; **`foundation_date` is renamed `establishment_year`** and **`followers_count` is renamed `follower_count`** (both now real, non-null); `messaging` is removed outright. `headquarters` (synthesized from `locations`) now reads the real `country_code`/`postal_code` location keys, the fictitious `country` key never existed and always projected null, and is renamed `country_code`; `postal_code` is added. `area` (region/state, e.g. "Washington") stays; it's real and often populated (verified live), even though the SDK's OpenAPI-generated types don't declare it for this endpoint.
461
581
  - **`job get` / `recruiter job get` slim `--json` output no longer emits a permanently-null `company_id`/`applicants_counter`, and `job get` (Core) no longer emits a permanently-null `published_at`.** `company_id` is synthesized from the nested `company.id` (neither shape has a top-level `company_id`); `applicants_counter` is renamed to `applications_count` (the real key on both shapes); `published_at` falls back to `created_at` on the Core shape, which has no `published_at` field at all (the Recruiter shape's own `published_at` is real and unaffected).
462
- - **`profile me` / `profile <id>` slim `--json` output no longer emits permanently-null `provider_id`/`network_distance`/`is_premium`/`current_position`, restores `headline` sourced from its real location, and drops the entirely-fictitious `occupation`/`organizations` fields.** Both commands are backed by the identical real v2 user-profile response there is no top-level `provider_id` (the real identifier is `id`), no top-level `network_distance`/`is_premium` (both nested under `specifics`), and no top-level `work_experience` (the real array is `specifics.experience`, which fed the `current_position` synthesis also permanently null until now). `provider_id` now sources from `id`; `network_distance` and `is_premium` now source from `specifics.network_distance`/`specifics.is_premium`; `current_position` is synthesized from `specifics.experience[0]`. **`profile me`'s `email` is renamed `emails`** (the real field is a plural array, not a singular string). **`headline` `description`** on a v2 read, LinkedIn serves the profile headline in the `description` wire field (a separate `bio` field carries the About-section paragraph); initially assumed to have no v2 source and dropped, then restored once the real source was confirmed live (3-way evidence: a written headline read back via `description` byte-for-byte, the same result from the M3 matrix probe, and `--verbose` showing headline-shaped text in `description` across live profiles). `occupation` and `organizations` are removed outright neither has a v2 source; the real user-profile response has no occupation-summary field and no administered-organizations field of any kind. (`profile me`'s slim output drops from 10 fields to 9; `profile <id>`'s drops from 9 to 8.)
463
- - **`job list --state` now re-filters returned items against their own `state` (D10).** LinkedIn's upstream state filter is best-effort it commonly returns items whose own `state` doesn't match the request. `--json` output (and `--all` streaming) now only contains items whose own `state` actually matches (`--state OPEN` maps to the response's `LISTED`, the one value that differs between the request and response vocabularies); dropped items produce a stderr note with the count. The re-filter is page-local and never touches the pagination cursor, so `--all` still walks the same unfiltered upstream pages it may fetch more pages than the filtered item count implies. The `--state` help text now says so.
582
+ - **`profile me` / `profile <id>` slim `--json` output no longer emits permanently-null `provider_id`/`network_distance`/`is_premium`/`current_position`, restores `headline` sourced from its real location, and drops the entirely-fictitious `occupation`/`organizations` fields.** Both commands are backed by the identical real v2 user-profile response; there is no top-level `provider_id` (the real identifier is `id`), no top-level `network_distance`/`is_premium` (both nested under `specifics`), and no top-level `work_experience` (the real array is `specifics.experience`, which fed the `current_position` synthesis, also permanently null until now). `provider_id` now sources from `id`; `network_distance` and `is_premium` now source from `specifics.network_distance`/`specifics.is_premium`; `current_position` is synthesized from `specifics.experience[0]`. **`profile me`'s `email` is renamed `emails`** (the real field is a plural array, not a singular string). **`headline` <- `description`**: on a v2 read, LinkedIn serves the profile headline in the `description` wire field (a separate `bio` field carries the About-section paragraph); initially assumed to have no v2 source and dropped, then restored once the real source was confirmed live (3-way evidence: a written headline read back via `description` byte-for-byte, the same result from the M3 matrix probe, and `--verbose` showing headline-shaped text in `description` across live profiles). `occupation` and `organizations` are removed outright, neither has a v2 source; the real user-profile response has no occupation-summary field and no administered-organizations field of any kind. (`profile me`'s slim output drops from 10 fields to 9; `profile <id>`'s drops from 9 to 8.)
583
+ - **`job list --state` now re-filters returned items against their own `state` (D10).** LinkedIn's upstream state filter is best-effort; it commonly returns items whose own `state` doesn't match the request. `--json` output (and `--all` streaming) now only contains items whose own `state` actually matches (`--state OPEN` maps to the response's `LISTED`, the one value that differs between the request and response vocabularies); dropped items produce a stderr note with the count. The re-filter is page-local and never touches the pagination cursor, so `--all` still walks the same unfiltered upstream pages; it may fetch more pages than the filtered item count implies. The `--state` help text now says so.
464
584
 
465
- ### Notes no user action required
585
+ ### Notes: no user action required
466
586
 
467
587
  - **`post react --as-organization`**: unchanged at the flag level; only the internal wire key was renamed, so the flag behaves exactly as before.
468
588
 
469
589
  ## [0.14.0] - 2026-07-07
470
590
 
471
- Webhooks surface cascade the coupled release with `@curviate/sdk` 0.14.0. Additive minor.
591
+ Webhooks surface cascade, the coupled release with `@curviate/sdk` 0.14.0. Additive minor.
472
592
 
473
593
  ### Added
474
594
 
475
- - **`webhook get <id>`** get a single webhook owned by the calling tenant (`webhooks.get`). Read command; `--preview` is a usage error (nothing to mutate), matching `webhook state-diff`.
595
+ - **`webhook get <id>`**: get a single webhook owned by the calling tenant (`webhooks.get`). Read command; `--preview` is a usage error (nothing to mutate), matching `webhook state-diff`.
476
596
 
477
597
  ### Changed
478
598
 
479
- - **`@curviate/sdk` bumped to `^0.14.0`.** The SDK's webhook event catalogue expanded 21 27 (`chat.updated`, `chat.deleted`, `connection.new`, `account.initial_sync.*`, and account-lifecycle renames) and its `CurviateEvent` union re-keyed to match this CLI never imports `CurviateEvent` directly, so `webhook verify`'s offline HMAC verification is unaffected; only the dependency range changed.
599
+ - **`@curviate/sdk` bumped to `^0.14.0`.** The SDK's webhook event catalogue expanded 21 -> 27 (`chat.updated`, `chat.deleted`, `connection.new`, `account.initial_sync.*`, and account-lifecycle renames) and its `CurviateEvent` union re-keyed to match; this CLI never imports `CurviateEvent` directly, so `webhook verify`'s offline HMAC verification is unaffected; only the dependency range changed.
480
600
 
481
601
  ## [0.13.0] - 2026-07-05
482
602
 
483
- Accounts/Auth surface migration the coupled release with `@curviate/sdk` 0.13.0. This is a
603
+ Accounts/Auth surface migration, the coupled release with `@curviate/sdk` 0.13.0. This is a
484
604
  **breaking** minor (pre-1.0): the account connection and checkpoint commands were reshaped to
485
605
  match the new account-in-path grammar.
486
606
 
487
607
  ### Added
488
608
 
489
- - **`account reconnect-link <account_id>`** mint a one-time hosted **re-authorization** link for an existing disconnected account (the hosted counterpart of `account reconnect`). Same open+wait UX as `account connect-link`: on an interactive TTY the URL auto-opens and the command waits for the account to reconnect (exit `0` resolved, `9` expired/failed, `12` on a wait-window timeout); non-interactively it prints the url + session_id and returns immediately. Optional `--expires-in-seconds` / `--redirect-url`.
490
- - **`account update --metadata '<json>'`** set the account's custom metadata (a flat JSON object that replaces the store wholesale). **`account update --clear-proxy`** clear the custom proxy (revert to automatic proxy protection).
491
- - **New connect/checkpoint response fields ride through `--json` output** (coupled with the SDK 0.13.0 connect-fix regen the CLI duck-types the response, so the fields pass through verbatim with no code change):
492
- - `recovered` (boolean) on `account link` and `account checkpoint solve` completions `true` when the connect reclaimed a LinkedIn identity already present on the workspace rather than connecting a brand-new one.
609
+ - **`account reconnect-link <account_id>`**: mint a one-time hosted **re-authorization** link for an existing disconnected account (the hosted counterpart of `account reconnect`). Same open+wait UX as `account connect-link`: on an interactive TTY the URL auto-opens and the command waits for the account to reconnect (exit `0` resolved, `9` expired/failed, `12` on a wait-window timeout); non-interactively it prints the url + session_id and returns immediately. Optional `--expires-in-seconds` / `--redirect-url`.
610
+ - **`account update --metadata '<json>'`**: set the account's custom metadata (a flat JSON object that replaces the store wholesale). **`account update --clear-proxy`**: clear the custom proxy (revert to automatic proxy protection).
611
+ - **New connect/checkpoint response fields ride through `--json` output** (coupled with the SDK 0.13.0 connect-fix regen, the CLI duck-types the response, so the fields pass through verbatim with no code change):
612
+ - `recovered` (boolean) on `account link` and `account checkpoint solve` completions, `true` when the connect reclaimed a LinkedIn identity already present on the workspace rather than connecting a brand-new one.
493
613
  - the completed-account `status` is widened to `active | reconnect_needed | restricted | disconnected` (a recovered identity often reports needing a reconnect); the CLI reads `status` as a free-form string, so the wider set is unaffected.
494
614
  - `challenge_type` (`mobile_app_approval`) + `recovery_hint` on an `account checkpoint poll` that returns `status: "expired"` (a mobile-approval timeout).
495
- Surfacing `recovered` in the human-readable (non-`--json`) success line is a deferred UX follow-up it would need consistent treatment across the direct-link, interactive-solve, and standalone-solve completion paths.
615
+ Surfacing `recovered` in the human-readable (non-`--json`) success line is a deferred UX follow-up; it would need consistent treatment across the direct-link, interactive-solve, and standalone-solve completion paths.
496
616
 
497
617
  ### Changed (BREAKING)
498
618
 
499
619
  - **Checkpoint commands are now account-in-path (positional), not `--checkpoint`.**
500
- - `account checkpoint submit --checkpoint <id> --code <c>` **`account checkpoint solve <account_id> --code <c>`**.
501
- - `account checkpoint resend --checkpoint <id>` **`account checkpoint request <account_id>`**.
502
- - `account checkpoint poll --checkpoint <id>` **`account checkpoint poll <account_id>`** (the `--checkpoint` flag becomes the account_id positional; `--wait`/`--timeout` unchanged).
620
+ - `account checkpoint submit --checkpoint <id> --code <c>` -> **`account checkpoint solve <account_id> --code <c>`**.
621
+ - `account checkpoint resend --checkpoint <id>` -> **`account checkpoint request <account_id>`**.
622
+ - `account checkpoint poll --checkpoint <id>` -> **`account checkpoint poll <account_id>`** (the `--checkpoint` flag becomes the account_id positional; `--wait`/`--timeout` unchanged).
503
623
  - Update scripts: replace `checkpoint submit --checkpoint X --code Y` with `checkpoint solve X --code Y`, `checkpoint resend --checkpoint X` with `checkpoint request X`, and `checkpoint poll --checkpoint X` with `checkpoint poll X`.
504
- - **`account refresh <account_id>` removed** accounts restart and re-sync automatically now; there is no replacement command. Status freshness comes from the account-status webhook, the nightly reconcile, and `account get`.
505
- - **`account connect-link` is create-only** the `--purpose` and `--account-id` flags are removed; it only mints a link to connect a **new** account. Use `account reconnect-link <account_id>` for hosted re-auth of an existing account.
506
- - **`account update` reshaped** the managed `--country` / `--ip` flags are removed (a managed location is now chosen at connect time). The command now takes `--metadata` and/or a custom proxy (`--proxy-*` / `--clear-proxy`).
507
- - **`account link` / `account reconnect` require `--user-agent` for cookie auth** connecting by session cookie (`--auth-method cookie`) without a `--user-agent` fails fast at exit `2` (it stays optional for `--auth-method credentials`). Under `--preview` the check is skipped (a render never exits).
508
- - SDK-parity manifest (`test/parity.test.ts`) repoints the checkpoint entries (`solve`/`request`/`poll`) and swaps `account refresh` `account reconnect-link`; the manifest and SDK method count stay at 93 (`accounts` stays 12 methods).
624
+ - **`account refresh <account_id>` removed**: accounts restart and re-sync automatically now; there is no replacement command. Status freshness comes from the account-status webhook, the nightly reconcile, and `account get`.
625
+ - **`account connect-link` is create-only**; the `--purpose` and `--account-id` flags are removed; it only mints a link to connect a **new** account. Use `account reconnect-link <account_id>` for hosted re-auth of an existing account.
626
+ - **`account update` reshaped**; the managed `--country` / `--ip` flags are removed (a managed location is now chosen at connect time). The command now takes `--metadata` and/or a custom proxy (`--proxy-*` / `--clear-proxy`).
627
+ - **`account link` / `account reconnect` require `--user-agent` for cookie auth**; connecting by session cookie (`--auth-method cookie`) without a `--user-agent` fails fast at exit `2` (it stays optional for `--auth-method credentials`). Under `--preview` the check is skipped (a render never exits).
628
+ - SDK-parity manifest (`test/parity.test.ts`) repoints the checkpoint entries (`solve`/`request`/`poll`) and swaps `account refresh` -> `account reconnect-link`; the manifest and SDK method count stay at 93 (`accounts` stays 12 methods).
509
629
 
510
630
  ### Fixed
511
631
 
@@ -513,44 +633,44 @@ match the new account-in-path grammar.
513
633
 
514
634
  ### Changed
515
635
 
516
- - `@curviate/sdk` dependency bumped to `^0.13.0` the coupled release carrying the reshaped `accounts` surface (see the SDK's own CHANGELOG). The CLI duck-types the SDK, so its commands are covered by the parity manifest against that release.
636
+ - `@curviate/sdk` dependency bumped to `^0.13.0`, the coupled release carrying the reshaped `accounts` surface (see the SDK's own CHANGELOG). The CLI duck-types the SDK, so its commands are covered by the parity manifest against that release.
517
637
 
518
638
  ## [0.12.0] - 2026-07-05
519
639
 
520
640
  ### Added
521
641
 
522
- - **`company employees <id>`** list people who currently work at the company (facade over people search with the company filter). `--keywords` and `--location` narrow the result; pagination flags apply. `<id>` must be the company's numeric provider_id (the `id` field of `company <id>`).
523
- - **`company posts <id>`** list the company's posts (facade over post search). Pagination flags apply; post `text` prints verbatim.
524
- - **`company jobs <id>`** list the company's open job postings (facade over job search). `--keywords` narrows the result. An empty list is a valid result (the company currently has no open postings), not an error.
525
- - **`company followers <id>`** list the company's followers (native the same seam that backs `profile <id> --followers`). Requires the acting account to administer the target company page; a non-admin company returns the exit code for `RESOURCE_ACCESS_RESTRICTED` (new, see below).
642
+ - **`company employees <id>`**: list people who currently work at the company (facade over people search with the company filter). `--keywords` and `--location` narrow the result; pagination flags apply. `<id>` must be the company's numeric provider_id (the `id` field of `company <id>`).
643
+ - **`company posts <id>`**: list the company's posts (facade over post search). Pagination flags apply; post `text` prints verbatim.
644
+ - **`company jobs <id>`**: list the company's open job postings (facade over job search). `--keywords` narrows the result. An empty list is a valid result (the company currently has no open postings), not an error.
645
+ - **`company followers <id>`**: list the company's followers (native, the same seam that backs `profile <id> --followers`). Requires the acting account to administer the target company page; a non-admin company returns the exit code for `RESOURCE_ACCESS_RESTRICTED` (new, see below).
526
646
  - All four new subcommands support `--all` (NDJSON page streaming) alongside the existing pagination flags, and reject `--preview` (exit `2`) like every other read command.
527
- - `--account` is now required on `company <id>` (retrieve) the underlying endpoint always requires `account_id`; previously the command silently fell back to an unscoped call.
528
- - **Sales Navigator v2 list surface 5 new subcommands.** `sales-nav account-lists --account <id>` and `sales-nav lead-lists --account <id>` list the operator's saved-account/saved-lead lists (`--limit`/`--cursor`/`--all` paginate). `sales-nav browse-account-list <list_id> --account <id> [--filter --sort-by --sort-order]` and `sales-nav browse-lead-list <list_id> --account <id> [--spotlight --sort-by --sort-order]` browse the saved items in one list genuine paginated reads, so they keep all pagination flags. `sales-nav save-account <company_id> --list <id> --account <id>` saves a company into an account list (write, `--preview` supported, no pagination flags in `--help`). All five call the SDK's new `salesNavigator` methods (`accountLists`/`leadLists`/`browseAccountList`/`browseLeadList`/`saveAccount`) no re-implementation of the HTTP call.
647
+ - `--account` is now required on `company <id>` (retrieve); the underlying endpoint always requires `account_id`; previously the command silently fell back to an unscoped call.
648
+ - **Sales Navigator v2 list surface, 5 new subcommands.** `sales-nav account-lists --account <id>` and `sales-nav lead-lists --account <id>` list the operator's saved-account/saved-lead lists (`--limit`/`--cursor`/`--all` paginate). `sales-nav browse-account-list <list_id> --account <id> [--filter --sort-by --sort-order]` and `sales-nav browse-lead-list <list_id> --account <id> [--spotlight --sort-by --sort-order]` browse the saved items in one list, genuine paginated reads, so they keep all pagination flags. `sales-nav save-account <company_id> --list <id> --account <id>` saves a company into an account list (write, `--preview` supported, no pagination flags in `--help`). All five call the SDK's new `salesNavigator` methods (`accountLists`/`leadLists`/`browseAccountList`/`browseLeadList`/`saveAccount`), no re-implementation of the HTTP call.
529
649
 
530
650
  ### Changed (BREAKING)
531
651
 
532
- - **`company <id>` now routes to the SDK's `companies.get()`** instead of the retired `profiles.getCompany()` an internal repoint (the hard-moved server endpoint), not a CLI UX change: flags, output shape, and slim projection are unchanged. `--account` becoming required (above) is the one user-visible behavior change.
533
- - SDK-parity manifest (`test/parity.test.ts`) repoints `company get` `companies.get` and gains `company employees` / `company posts` / `company jobs` / `company followers`; the manifest and SDK method count both move from 84 to 88.
534
- - `@curviate/sdk` dependency bumped to `^0.12.0` the released build carrying the `companies` resource and the v2 `salesNavigator` list-surface cascade (see the SDK's own CHANGELOG).
535
- - **`sales-nav save-lead` re-signed for the v2 save-lead surface.** The old `save-lead <user_id> [--list-id <id>]` (list optional) is **retired, no alias** the v2 op always saves into a specific list. The replacement is `save-lead <user_id> --list <id>`: `--list` is now **required** and the flag is renamed from `--list-id`. Update scripts: `save-lead <id> --list-id <l>` `save-lead <id> --list <l>`.
536
- - SDK-parity manifest gains the 5 new `sales-nav` v2 subcommands; the manifest and SDK method count both move from 88 to 93 (`salesNavigator` 712 methods).
652
+ - **`company <id>` now routes to the SDK's `companies.get()`** instead of the retired `profiles.getCompany()`, an internal repoint (the hard-moved server endpoint), not a CLI UX change: flags, output shape, and slim projection are unchanged. `--account` becoming required (above) is the one user-visible behavior change.
653
+ - SDK-parity manifest (`test/parity.test.ts`) repoints `company get` -> `companies.get` and gains `company employees` / `company posts` / `company jobs` / `company followers`; the manifest and SDK method count both move from 84 to 88.
654
+ - `@curviate/sdk` dependency bumped to `^0.12.0`, the released build carrying the `companies` resource and the v2 `salesNavigator` list-surface cascade (see the SDK's own CHANGELOG).
655
+ - **`sales-nav save-lead` re-signed for the v2 save-lead surface.** The old `save-lead <user_id> [--list-id <id>]` (list optional) is **retired, no alias**; the v2 op always saves into a specific list. The replacement is `save-lead <user_id> --list <id>`: `--list` is now **required** and the flag is renamed from `--list-id`. Update scripts: `save-lead <id> --list-id <l>` -> `save-lead <id> --list <l>`.
656
+ - SDK-parity manifest gains the 5 new `sales-nav` v2 subcommands; the manifest and SDK method count both move from 88 to 93 (`salesNavigator` 7->12 methods).
537
657
 
538
658
  ### Fixed
539
659
 
540
- - **`RESOURCE_ACCESS_RESTRICTED`** a new SDK error code (the non-admin mapping for `company followers`) is now present in `EXIT_CODE_MAP` (exit `8`, grouped with `ACCOUNT_RESTRICTED`); the exhaustiveness test would otherwise have silently mapped it to the default `1`.
660
+ - **`RESOURCE_ACCESS_RESTRICTED`**: a new SDK error code (the non-admin mapping for `company followers`) is now present in `EXIT_CODE_MAP` (exit `8`, grouped with `ACCOUNT_RESTRICTED`); the exhaustiveness test would otherwise have silently mapped it to the default `1`.
541
661
 
542
662
  ## [0.11.0] - 2026-07-04
543
663
 
544
664
  ### Added
545
665
 
546
- - **Safe credential entry** for `account link` / `account reconnect` / `account update` env-var fallbacks (an explicit flag always wins over its env var), `--password-stdin` / `--li-at-stdin` flags to read a secret from stdin, and a masked TTY prompt with a non-TTY fail-fast when a credential is required but not supplied any other way. A 5-way conflict matrix rejects supplying the same credential through more than one channel. The four secret-bearing flags carry a shell-history/`ps`-visibility warning, and `--preview` masks credential values instead of ever rendering them in cleartext.
547
- - **Guided checkpoint follow-through** on `account link` / `account reconnect`. A `202 checkpoint_required` response now resolves in-process on an interactive TTY code prompt, retry loop on a `422`, chained-challenge follow-through, a codeless mobile-app-approval poll sub-loop, and a resend hint instead of just printing the envelope. A non-interactive session (either stream not a TTY, or `--no-interactive`) still prints the envelope and exits with the new `12` (`AUTH_NEEDED`) code a pending checkpoint, not an error.
548
- - **`account checkpoint poll --wait`** an adaptive-cadence loop (1000ms, then 1500ms for 30s, then 3000ms) that blocks until the checkpoint resolves (exit `0`), expires/fails (exit `9`), or the wait window elapses while still pending (exit `12`, still resolvable later). `--wait` is off by default (the single-poll behavior is unchanged). `--timeout <ms>` overrides the wall-clock bound (default: the checkpoint's own expiry) and fails fast at exit `2` on a non-numeric value, before any call. `checkpoint submit`'s one-shot path also now detects a chained `checkpoint_required` response and exits `12` instead of rendering it as a plain success.
549
- - **`account checkpoint resend --checkpoint <id>`** re-sends the pending challenge notification, mirroring `checkpoint submit` / `poll` (body-addressed, `WRITE_SINGLE_FLAGS`, `--preview` supported, no `--code` since there's nothing to submit). Exits `0` on any `200` regardless of the response's `resent` boolean `false` is an honest answer, not a command failure.
550
- - **`account connect-link` browser handoff.** The command now completes the hosted-link round trip instead of only minting a URL: on an interactive TTY it auto-opens the URL and waits on the same adaptive cadence as `checkpoint poll --wait` for the account to connect (resolved prints the connected account and exits `0`; expired/failed exit `9`; wait window elapses while still pending exit `12`). A non-interactive session (non-TTY, or `--no-interactive`) never opens a browser and never blocks it prints the URL, a relay instruction, and the `session_id`, then returns immediately.
551
- - **`account connect-session poll --session <id>`** the standalone counterpart to the above: a single poll by default (prints the body, exits `0` regardless of status), or the same adaptive wait loop with `--wait`. `--open`/`--no-open` and `--wait`/`--no-wait` are TTY-adaptive; `--timeout <ms>` overrides the wait bound (default: time remaining to the session's own expiry).
552
- - Pagination flags (`--limit`/`--cursor`/`--all`/`--max-pages`) are now suppressed on the 8 `account` subcommands that mutate or resolve exactly one resource (`link`, `connect-link`, `reconnect`, `refresh`, `update`, `disconnect`, `checkpoint submit`, `checkpoint poll`) they had no meaning on a one-row response. `account list` is unaffected. `link` / `reconnect` help text gains a one-line note about the checkpoint-required path.
553
- - SDK-parity manifest (`test/parity.test.ts`) gains `account checkpoint resend` `accounts.resendCheckpoint` and `account connect-session poll` `accounts.getConnectSession` both were held back pending the SDK's own `0.11.0` regen; the manifest and the SDK method count both move from 82 to 84.
666
+ - **Safe credential entry** for `account link` / `account reconnect` / `account update`: env-var fallbacks (an explicit flag always wins over its env var), `--password-stdin` / `--li-at-stdin` flags to read a secret from stdin, and a masked TTY prompt with a non-TTY fail-fast when a credential is required but not supplied any other way. A 5-way conflict matrix rejects supplying the same credential through more than one channel. The four secret-bearing flags carry a shell-history/`ps`-visibility warning, and `--preview` masks credential values instead of ever rendering them in cleartext.
667
+ - **Guided checkpoint follow-through** on `account link` / `account reconnect`. A `202 checkpoint_required` response now resolves in-process on an interactive TTY: code prompt, retry loop on a `422`, chained-challenge follow-through, a codeless mobile-app-approval poll sub-loop, and a resend hint, instead of just printing the envelope. A non-interactive session (either stream not a TTY, or `--no-interactive`) still prints the envelope and exits with the new `12` (`AUTH_NEEDED`) code, a pending checkpoint, not an error.
668
+ - **`account checkpoint poll --wait`**: an adaptive-cadence loop (1000ms, then 1500ms for 30s, then 3000ms) that blocks until the checkpoint resolves (exit `0`), expires/fails (exit `9`), or the wait window elapses while still pending (exit `12`, still resolvable later). `--wait` is off by default (the single-poll behavior is unchanged). `--timeout <ms>` overrides the wall-clock bound (default: the checkpoint's own expiry) and fails fast at exit `2` on a non-numeric value, before any call. `checkpoint submit`'s one-shot path also now detects a chained `checkpoint_required` response and exits `12` instead of rendering it as a plain success.
669
+ - **`account checkpoint resend --checkpoint <id>`**: re-sends the pending challenge notification, mirroring `checkpoint submit` / `poll` (body-addressed, `WRITE_SINGLE_FLAGS`, `--preview` supported, no `--code` since there's nothing to submit). Exits `0` on any `200` regardless of the response's `resent` boolean; `false` is an honest answer, not a command failure.
670
+ - **`account connect-link` browser handoff.** The command now completes the hosted-link round trip instead of only minting a URL: on an interactive TTY it auto-opens the URL and waits on the same adaptive cadence as `checkpoint poll --wait` for the account to connect (resolved -> prints the connected account and exits `0`; expired/failed -> exit `9`; wait window elapses while still pending -> exit `12`). A non-interactive session (non-TTY, or `--no-interactive`) never opens a browser and never blocks; it prints the URL, a relay instruction, and the `session_id`, then returns immediately.
671
+ - **`account connect-session poll --session <id>`**: the standalone counterpart to the above: a single poll by default (prints the body, exits `0` regardless of status), or the same adaptive wait loop with `--wait`. `--open`/`--no-open` and `--wait`/`--no-wait` are TTY-adaptive; `--timeout <ms>` overrides the wait bound (default: time remaining to the session's own expiry).
672
+ - Pagination flags (`--limit`/`--cursor`/`--all`/`--max-pages`) are now suppressed on the 8 `account` subcommands that mutate or resolve exactly one resource (`link`, `connect-link`, `reconnect`, `refresh`, `update`, `disconnect`, `checkpoint submit`, `checkpoint poll`); they had no meaning on a one-row response. `account list` is unaffected. `link` / `reconnect` help text gains a one-line note about the checkpoint-required path.
673
+ - SDK-parity manifest (`test/parity.test.ts`) gains `account checkpoint resend` -> `accounts.resendCheckpoint` and `account connect-session poll` -> `accounts.getConnectSession`; both were held back pending the SDK's own `0.11.0` regen; the manifest and the SDK method count both move from 82 to 84.
554
674
 
555
675
  ### Fixed
556
676
 
@@ -564,38 +684,38 @@ match the new account-in-path grammar.
564
684
 
565
685
  ### Added
566
686
 
567
- - `job get <url|id>` a new top-level `job` command retrieving one public LinkedIn job posting's full detail. Accepts a job URL (`https://www.linkedin.com/jobs/view/<id>`) or a bare numeric id a job URL is resolved to its numeric id client-side; anything else passes through and the API is the final validator. Slim-default output: `object`, `id`, `title`, `company`, `company_id`, `location`, `state`, `applicants_counter`, `published_at`, `description` `description` stays in the default output since retrieving it is the point of the command. Pass `--verbose` for the full response (adds `cost`, `created_at`, `hiring_team`). An unknown job id exits with the not-found exit code. This is a read command `--preview` is a usage error, matching every other single-object read.
568
- - `recruiter job get <url|id>` the Recruiter-lens sibling of `job get`, joining the existing `recruiter job` command group. Retrieves any public job posting (not only postings you manage) unlike `recruiter jobs`, which lists your own. Same URL/id resolution and slim/verbose projection as the top-level command; requires the Recruiter add-on tier (exit `5` without it).
687
+ - `job get <url|id>`: a new top-level `job` command retrieving one public LinkedIn job posting's full detail. Accepts a job URL (`https://www.linkedin.com/jobs/view/<id>`) or a bare numeric id; a job URL is resolved to its numeric id client-side; anything else passes through and the API is the final validator. Slim-default output: `object`, `id`, `title`, `company`, `company_id`, `location`, `state`, `applicants_counter`, `published_at`, `description`; `description` stays in the default output since retrieving it is the point of the command. Pass `--verbose` for the full response (adds `cost`, `created_at`, `hiring_team`). An unknown job id exits with the not-found exit code. This is a read command; `--preview` is a usage error, matching every other single-object read.
688
+ - `recruiter job get <url|id>`: the Recruiter-lens sibling of `job get`, joining the existing `recruiter job` command group. Retrieves any public job posting (not only postings you manage), unlike `recruiter jobs`, which lists your own. Same URL/id resolution and slim/verbose projection as the top-level command; requires the Recruiter add-on tier (exit `5` without it).
569
689
  - README gained a new numbered example chaining `search jobs` into `job get`, and a "Get any public job posting through the Recruiter lens" example in the Recruiter section.
570
690
 
571
691
  ### Changed
572
692
 
573
693
  - `@curviate/sdk` dependency bumped to `^0.10.0`.
574
- - `recruiter job get --help` does not advertise `--limit`/`--cursor`/`--all`/`--max-pages` a single-object read, consistent with the other Recruiter single-reads (`profile`, `project`, `applicant`). `--fields` and `--verbose` are unchanged and available.
694
+ - `recruiter job get --help` does not advertise `--limit`/`--cursor`/`--all`/`--max-pages`; a single-object read, consistent with the other Recruiter single-reads (`profile`, `project`, `applicant`). `--fields` and `--verbose` are unchanged and available.
575
695
 
576
696
  ## [0.9.0] - 2026-07-03
577
697
 
578
698
  ### Added
579
699
 
580
- - `account list` and `account get` gain a compact **slim-default** output pass `--verbose` for the full API response. Slim `account list` items: `account_id`, `status`, `auth_method`, `full_name`, `headline`, `seat_id`, `connected_at`. Slim `account get`: the same seven fields plus `last_checked_at` and `quotas`. Six cached account-detail fields (`username`, `premium_id`, `public_identifier`, `substrate_created_at`, `signatures`, `groups`) are verbose-only on both commands they are `null`/`[]` on an account that hasn't been enriched yet, never a missing key. `--all` NDJSON streaming on `account list` applies the same slim projection per item unless `--verbose` is passed.
581
- - `account get` gains `seat_id` in its slim output (previously only `account list` carried it) the seat the account occupies, `null` for an admin seatless account.
700
+ - `account list` and `account get` gain a compact **slim-default** output; pass `--verbose` for the full API response. Slim `account list` items: `account_id`, `status`, `auth_method`, `full_name`, `headline`, `seat_id`, `connected_at`. Slim `account get`: the same seven fields plus `last_checked_at` and `quotas`. Six cached account-detail fields (`username`, `premium_id`, `public_identifier`, `substrate_created_at`, `signatures`, `groups`) are verbose-only on both commands; they are `null`/`[]` on an account that hasn't been enriched yet, never a missing key. `--all` NDJSON streaming on `account list` applies the same slim projection per item unless `--verbose` is passed.
701
+ - `account get` gains `seat_id` in its slim output (previously only `account list` carried it), the seat the account occupies, `null` for an admin seatless account.
582
702
 
583
703
  ### Changed
584
704
 
585
705
  - `@curviate/sdk` dependency bumped to `^0.9.0`.
586
- - `account get --help` no longer advertises `--limit`/`--cursor`/`--all`/`--max-pages` a single-object read, those flags never applied. `--fields` is unchanged and still available. `account list` is unaffected (a genuine list read, keeps all pagination flags).
706
+ - `account get --help` no longer advertises `--limit`/`--cursor`/`--all`/`--max-pages`; a single-object read, those flags never applied. `--fields` is unchanged and still available. `account list` is unaffected (a genuine list read, keeps all pagination flags).
587
707
 
588
708
  ## [0.8.0] - 2026-07-02
589
709
 
590
710
  ### Added
591
711
 
592
- - `recruiter reject-applicant` gained `--message` and `--notify-at` flags. The applicant is only notified of the rejection when `--message` is provided (the prior behavior no notification is unchanged when both are omitted). `--notify-at` schedules the notification (a UNIX-milliseconds timestamp) and requires `--message`; passing `--notify-at` alone, or a non-numeric value, is a usage error (exit `2`).
593
- - README gained dedicated "Sales Navigator" and "Recruiter" sections with numbered, runnable examples covering every in-scope command: searching and getting profiles, saving a lead, starting a chat, listing/searching Recruiter people and hiring projects, the job create publish checkpoint lifecycle, listing/getting applicants, downloading a resume, and rejecting an applicant with and without a notification.
712
+ - `recruiter reject-applicant` gained `--message` and `--notify-at` flags. The applicant is only notified of the rejection when `--message` is provided (the prior behavior, no notification, is unchanged when both are omitted). `--notify-at` schedules the notification (a UNIX-milliseconds timestamp) and requires `--message`; passing `--notify-at` alone, or a non-numeric value, is a usage error (exit `2`).
713
+ - README gained dedicated "Sales Navigator" and "Recruiter" sections with numbered, runnable examples covering every in-scope command: searching and getting profiles, saving a lead, starting a chat, listing/searching Recruiter people and hiring projects, the job create -> publish -> checkpoint lifecycle, listing/getting applicants, downloading a resume, and rejecting an applicant with and without a notification.
594
714
 
595
715
  ### Changed
596
716
 
597
- - **Help output cleanup:** Recruiter and Sales Navigator write commands (`add-candidate`, `add-applicant`, `reject-applicant`, `job create`/`publish`/`checkpoint`, `save-lead`, `message new`) and single-object read commands (`profile`, `project`, `applicant`, `applicant resume`) no longer advertise `--limit`/`--cursor`/`--all`/`--max-pages` in `--help` those flags only ever applied to list/search commands, which keep them unchanged. Single-object reads keep `--fields`.
598
- - Corrected the `search parameters --type` flag description on both `recruiter` and `sales-nav` it previously suggested example values (`LOCATION`, `INDUSTRY`, `TITLE`) that the API does not accept for either surface; it now lists the real accepted values.
717
+ - **Help output cleanup:** Recruiter and Sales Navigator write commands (`add-candidate`, `add-applicant`, `reject-applicant`, `job create`/`publish`/`checkpoint`, `save-lead`, `message new`) and single-object read commands (`profile`, `project`, `applicant`, `applicant resume`) no longer advertise `--limit`/`--cursor`/`--all`/`--max-pages` in `--help`; those flags only ever applied to list/search commands, which keep them unchanged. Single-object reads keep `--fields`.
718
+ - Corrected the `search parameters --type` flag description on both `recruiter` and `sales-nav`; it previously suggested example values (`LOCATION`, `INDUSTRY`, `TITLE`) that the API does not accept for either surface; it now lists the real accepted values.
599
719
  - Polished the `message new --to` flag description on both surfaces with the expected provider-ID format and a note that it is not resolved from a URL or slug.
600
720
  - Updated `@curviate/sdk` dependency to `^0.8.0`. No resource method signatures changed (per the SDK 0.8.0 changelog); Recruiter's job-lifecycle endpoints (applicant get/reject/resume, applicant list, job publish/checkpoint, Recruiter profile) are now fully implemented server-side instead of returning `501`.
601
721
 
@@ -609,7 +729,7 @@ match the new account-in-path grammar.
609
729
 
610
730
  ### Fixed
611
731
 
612
- - `recruiter message new` the `--to` recipient ID is now sent as `attendees_ids` (plural) in the request body, matching the updated server contract. A prior version used the old `attendee_ids` (singular) field name which the API no longer accepts.
732
+ - `recruiter message new`: the `--to` recipient ID is now sent as `attendees_ids` (plural) in the request body, matching the updated server contract. A prior version used the old `attendee_ids` (singular) field name which the API no longer accepts.
613
733
 
614
734
  ### Changed
615
735
 
@@ -619,20 +739,20 @@ match the new account-in-path grammar.
619
739
 
620
740
  ### Added
621
741
 
622
- - `search` named filter flags that previously required raw `--filters` JSON:
742
+ - `search`: named filter flags that previously required raw `--filters` JSON:
623
743
  - **companies**: `--has-job-offers`, `--headcount <buckets>` (comma-separated
624
- size buckets `1-10 5001-10000`; `10001+` reports a usage error).
744
+ size buckets `1-10 ... 5001-10000`; `10001+` reports a usage error).
625
745
  - **jobs**: `--title <ids>`, `--presence`, `--benefits`, `--commitments`,
626
746
  `--has-verifications`, `--under-10-applicants`, `--in-your-network`,
627
747
  `--fair-chance-employer`, `--location-within-area <miles>`.
628
- - **people**: `--connections-of`, `--followers-of` (comma-separated array).
748
+ - **people**: `--connections-of`, `--followers-of` (comma-separated -> array).
629
749
  - **posts**: `--posted-by-member`, `--posted-by-company`, `--posted-by-me`,
630
750
  `--mentioning-member`, `--mentioning-company`, `--author-industry`,
631
751
  `--author-company`, `--author-keywords`.
632
752
 
633
753
  ### Fixed
634
754
 
635
- - `search jobs` slim `company_name` was always `null` now derived from the
755
+ - `search jobs` slim `company_name` was always `null`, now derived from the
636
756
  nested `company.name` (handles postings with no linked company). `--verbose`
637
757
  still returns the raw response unchanged.
638
758
  - `search parameters --type`, `search jobs --seniority`/`--job-type`, and
@@ -647,12 +767,12 @@ match the new account-in-path grammar.
647
767
 
648
768
  ### Added
649
769
 
650
- - `search people --title` ( `advanced_keywords.title` keyword, nested-merged),
770
+ - `search people --title` (-> `advanced_keywords.title` keyword, nested-merged),
651
771
  `--industry`, `--profile-language`; `--filters` deep-merge (named flags win).
652
- - `search jobs --location` `region` (single id) + `--region` alias +
772
+ - `search jobs --location` -> `region` (single id) + `--region` alias +
653
773
  `--date-posted <days>` (number).
654
- - `search posts --date-posted` hyphenunderscore normalize.
655
- - `--all` truncation emits `{"object":"stream_truncated",…}` JSON.
774
+ - `search posts --date-posted` hyphen->underscore normalize.
775
+ - `--all` truncation emits `{"object":"stream_truncated",...}` JSON.
656
776
 
657
777
  ### Changed
658
778
 
@@ -662,15 +782,15 @@ match the new account-in-path grammar.
662
782
 
663
783
  ### Added
664
784
 
665
- - `inbox list --unread` filter the inbox to chats with unread messages.
785
+ - `inbox list --unread`: filter the inbox to chats with unread messages.
666
786
  - `messages` now accepts `--before` and `--after` to page a conversation by
667
787
  timestamp window.
668
- - `sync-chat --wait` poll until a chat sync completes instead of returning
788
+ - `sync-chat --wait`: poll until a chat sync completes instead of returning
669
789
  immediately.
670
790
  - `message new --to` and `message inmail --to` now resolve a **LinkedIn profile
671
791
  URL or vanity slug** (e.g. `linkedin.com/in/<slug>`) to the recipient, in
672
792
  addition to provider ids and member URNs.
673
- - Thread-URL `chat_id` normalization a pasted conversation URL is normalized to
793
+ - Thread-URL `chat_id` normalization: a pasted conversation URL is normalized to
674
794
  the underlying chat id wherever a `chat_id` is accepted.
675
795
  - Write commands that take a TEXT positional accept `-` to read the value from
676
796
  stdin (pipe message bodies in).
@@ -687,10 +807,10 @@ match the new account-in-path grammar.
687
807
 
688
808
  ### Added
689
809
 
690
- - `message inmail --surface classic` send an InMail from the account's own premium
810
+ - `message inmail --surface classic`: send an InMail from the account's own premium
691
811
  InMail credits (in addition to `sales_nav` and `recruiter`). Use this to reach an
692
812
  out-of-network member from a LinkedIn Premium/Core account.
693
- - `message inmail --to` now accepts a member **provider id** (`ACoAAA…`) as well as a
813
+ - `message inmail --to` now accepts a member **provider id** (`ACoAAA...`) as well as a
694
814
  member URN (`urn:li:member:<id>`). The server resolves the recipient either way.
695
815
 
696
816
  ## [0.4.1] - 2026-06-29
@@ -726,7 +846,7 @@ match the new account-in-path grammar.
726
846
  ### Fixed
727
847
 
728
848
  - `company` command now exits 2 with an error when `--sections` is passed (unsupported flag for that surface).
729
- - `slimProfile` work_experience field mapping corrected (`position`→`title`, `company`→`company_name`); `is_current` now derived from `end == null`; `company_id` is always `null` (the experience-entry ID is not a company ID).
849
+ - `slimProfile` work_experience field mapping corrected (`position`->`title`, `company`->`company_name`); `is_current` now derived from `end == null`; `company_id` is always `null` (the experience-entry ID is not a company ID).
730
850
 
731
851
  ### Changed
732
852
 
@@ -750,7 +870,7 @@ match the new account-in-path grammar.
750
870
 
751
871
  ### Added
752
872
 
753
- - Initial public release full SDK-surface parity CLI over the Curviate API.
873
+ - Initial public release, full SDK-surface parity CLI over the Curviate API.
754
874
  - `curviate` root command with `--help` and `--version`.
755
875
  - Global flags: `--account`, `--json`, `--fields`, `--limit`, `--cursor`, `--all`,
756
876
  `--max-pages`, `--preview`, `--base-url`, `--timeout`, `--api-key`, `--profile`.