agent-slack 0.8.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -231,6 +231,8 @@ Attach options for `message send`:
231
231
 
232
232
  - `--attach <path>` upload a local file (repeatable)
233
233
 
234
+ `message send` returns `channel_id` plus the posted `ts` and a `permalink` (for non-attachment sends). `thread_ts` appears only when replying in a thread.
235
+
234
236
  ### List, create, and invite channels
235
237
 
236
238
  ```bash
@@ -335,6 +337,81 @@ agent-slack user get U12345678 --workspace "https://workspace.slack.com" | jq .
335
337
  agent-slack user get "@alice" --workspace "https://workspace.slack.com" | jq .
336
338
  ```
337
339
 
340
+ ### Unreads (inbox view)
341
+
342
+ See all unread messages across channels, DMs, and threads in one place:
343
+
344
+ ```bash
345
+ # Show all unreads with message content
346
+ agent-slack unreads
347
+
348
+ # Show only unread counts (no message content)
349
+ agent-slack unreads --counts-only
350
+
351
+ # Limit messages per channel (default 10)
352
+ agent-slack unreads --max-messages 5
353
+
354
+ # Include system messages (joins, leaves, topic changes)
355
+ agent-slack unreads --include-system
356
+ ```
357
+
358
+ Output includes channels sorted by mention count, then unread count:
359
+
360
+ ```json
361
+ {
362
+ "channels": [
363
+ {
364
+ "channel_id": "C123...",
365
+ "channel_name": "general",
366
+ "channel_type": "channel",
367
+ "unread_count": 5,
368
+ "mention_count": 2,
369
+ "messages": [...]
370
+ }
371
+ ],
372
+ "threads": {
373
+ "has_unreads": true,
374
+ "mention_count": 3
375
+ }
376
+ }
377
+ ```
378
+
379
+ Note: This feature uses the `client.counts` API which may be restricted in some Enterprise Grid workspaces (`team_is_restricted` error).
380
+
381
+ ### Later (saved messages)
382
+
383
+ Manage your saved-for-later messages (Slack's Later tab):
384
+
385
+ ```bash
386
+ # List saved messages (in-progress by default)
387
+ agent-slack later list
388
+
389
+ # Show only counts per state
390
+ agent-slack later list --counts-only
391
+
392
+ # Filter by state: in_progress, completed, archived, all
393
+ agent-slack later list --state completed
394
+
395
+ # Save a message for later
396
+ agent-slack later save "https://workspace.slack.com/archives/C123/p1700000000000000"
397
+
398
+ # Mark as completed
399
+ agent-slack later complete "https://workspace.slack.com/archives/C123/p1700000000000000"
400
+
401
+ # Archive
402
+ agent-slack later archive "https://workspace.slack.com/archives/C123/p1700000000000000"
403
+
404
+ # Move back to in-progress
405
+ agent-slack later reopen "https://workspace.slack.com/archives/C123/p1700000000000000"
406
+
407
+ # Remove from saved
408
+ agent-slack later remove "https://workspace.slack.com/archives/C123/p1700000000000000"
409
+
410
+ # Set a reminder
411
+ agent-slack later remind "https://workspace.slack.com/archives/C123/p1700000000000000" --in 1h
412
+ agent-slack later remind "https://workspace.slack.com/archives/C123/p1700000000000000" --in tomorrow
413
+ ```
414
+
338
415
  ### Fetch a Canvas as Markdown
339
416
 
340
417
  ```bash
package/dist/index.js CHANGED
@@ -3541,6 +3541,16 @@ function parseSlackMessageUrl(input) {
3541
3541
  const workspace_url = `${url.protocol}//${url.host}`;
3542
3542
  return { workspace_url, channel_id, message_ts, thread_ts_hint, raw: input, possiblyTruncated };
3543
3543
  }
3544
+ function buildSlackMessageUrl(input) {
3545
+ const workspaceUrl = input.workspace_url.replace(/\/$/, "");
3546
+ const digits = input.message_ts.replace(".", "");
3547
+ const url = new URL(`${workspaceUrl}/archives/${input.channel_id}/p${digits}`);
3548
+ if (input.thread_ts && input.thread_ts !== input.message_ts) {
3549
+ url.searchParams.set("thread_ts", input.thread_ts);
3550
+ url.searchParams.set("cid", input.channel_id);
3551
+ }
3552
+ return url.toString();
3553
+ }
3544
3554
 
3545
3555
  // src/cli/targets.ts
3546
3556
  function parseMsgTarget(input) {
@@ -4590,14 +4600,15 @@ async function sendMessage(input) {
4590
4600
  if (target.kind === "url") {
4591
4601
  const { ref } = target;
4592
4602
  warnOnTruncatedSlackUrl(ref);
4593
- await input.ctx.withAutoRefresh({
4603
+ return await input.ctx.withAutoRefresh({
4594
4604
  workspaceUrl: ref.workspace_url,
4595
4605
  work: async () => {
4596
- const { client } = await input.ctx.getClientForWorkspace(ref.workspace_url);
4606
+ const { client, workspace_url } = await input.ctx.getClientForWorkspace(ref.workspace_url);
4597
4607
  const msg = await fetchMessage(client, { ref });
4598
4608
  const threadTs = msg.thread_ts ?? msg.ts;
4599
- await sendMessageToChannel({
4609
+ return await sendMessageToChannel({
4600
4610
  client,
4611
+ workspaceUrl: workspace_url ?? ref.workspace_url,
4601
4612
  channelId: ref.channel_id,
4602
4613
  text: input.text,
4603
4614
  blocks,
@@ -4606,17 +4617,17 @@ async function sendMessage(input) {
4606
4617
  });
4607
4618
  }
4608
4619
  });
4609
- return { ok: true };
4610
4620
  }
4611
4621
  if (target.kind === "user") {
4612
4622
  const workspaceUrl2 = input.ctx.effectiveWorkspaceUrl(input.options.workspace);
4613
- await input.ctx.withAutoRefresh({
4623
+ return await input.ctx.withAutoRefresh({
4614
4624
  workspaceUrl: workspaceUrl2,
4615
4625
  work: async () => {
4616
- const { client } = await input.ctx.getClientForWorkspace(workspaceUrl2);
4626
+ const { client, workspace_url } = await input.ctx.getClientForWorkspace(workspaceUrl2);
4617
4627
  const dmChannelId = await openDmChannel(client, target.userId);
4618
- await sendMessageToChannel({
4628
+ return await sendMessageToChannel({
4619
4629
  client,
4630
+ workspaceUrl: workspace_url ?? workspaceUrl2,
4620
4631
  channelId: dmChannelId,
4621
4632
  text: input.text,
4622
4633
  blocks,
@@ -4624,20 +4635,20 @@ async function sendMessage(input) {
4624
4635
  });
4625
4636
  }
4626
4637
  });
4627
- return { ok: true };
4628
4638
  }
4629
4639
  const workspaceUrl = input.ctx.effectiveWorkspaceUrl(input.options.workspace);
4630
4640
  await input.ctx.assertWorkspaceSpecifiedForChannelNames({
4631
4641
  workspaceUrl,
4632
4642
  channels: [String(target.channel)]
4633
4643
  });
4634
- await input.ctx.withAutoRefresh({
4644
+ return await input.ctx.withAutoRefresh({
4635
4645
  workspaceUrl,
4636
4646
  work: async () => {
4637
- const { client } = await input.ctx.getClientForWorkspace(workspaceUrl);
4647
+ const { client, workspace_url } = await input.ctx.getClientForWorkspace(workspaceUrl);
4638
4648
  const channelId = await resolveChannelId(client, String(target.channel));
4639
- await sendMessageToChannel({
4649
+ return await sendMessageToChannel({
4640
4650
  client,
4651
+ workspaceUrl: workspace_url ?? workspaceUrl,
4641
4652
  channelId,
4642
4653
  text: input.text,
4643
4654
  blocks,
@@ -4646,7 +4657,6 @@ async function sendMessage(input) {
4646
4657
  });
4647
4658
  }
4648
4659
  });
4649
- return { ok: true };
4650
4660
  }
4651
4661
  function normalizeAttachPaths(raw) {
4652
4662
  if (!Array.isArray(raw) || raw.length === 0) {
@@ -4662,13 +4672,27 @@ function normalizeAttachPaths(raw) {
4662
4672
  }
4663
4673
  async function sendMessageToChannel(input) {
4664
4674
  if (input.attachPaths.length === 0) {
4665
- await input.client.api("chat.postMessage", {
4675
+ const resp = await input.client.api("chat.postMessage", {
4666
4676
  channel: input.channelId,
4667
4677
  text: input.text,
4668
4678
  thread_ts: input.threadTs,
4669
4679
  ...input.blocks ? { blocks: input.blocks } : {}
4670
4680
  });
4671
- return;
4681
+ const ts = typeof resp.ts === "string" ? resp.ts : undefined;
4682
+ const channelId = typeof resp.channel === "string" ? resp.channel : input.channelId;
4683
+ const permalink = input.workspaceUrl && ts ? buildSlackMessageUrl({
4684
+ workspace_url: input.workspaceUrl,
4685
+ channel_id: channelId,
4686
+ message_ts: ts,
4687
+ thread_ts: input.threadTs
4688
+ }) : undefined;
4689
+ return {
4690
+ ok: true,
4691
+ channel_id: channelId,
4692
+ ts,
4693
+ thread_ts: input.threadTs,
4694
+ permalink
4695
+ };
4672
4696
  }
4673
4697
  if (input.blocks) {
4674
4698
  process.stderr.write(`Warning: rich text formatting is not supported with file attachments; sending as plain text.
@@ -4685,6 +4709,11 @@ async function sendMessageToChannel(input) {
4685
4709
  });
4686
4710
  initialComment = "";
4687
4711
  }
4712
+ return {
4713
+ ok: true,
4714
+ channel_id: input.channelId,
4715
+ thread_ts: input.threadTs
4716
+ };
4688
4717
  }
4689
4718
  async function editMessage(input) {
4690
4719
  const target = parseMsgTarget(String(input.targetInput));
@@ -9156,5 +9185,5 @@ if (subcommand && subcommand !== "update") {
9156
9185
  backgroundUpdateCheck();
9157
9186
  }
9158
9187
 
9159
- //# debugId=DD5B850D326A863A64756E2164756E21
9188
+ //# debugId=5E5A3A0EB875DD7164756E2164756E21
9160
9189
  //# sourceMappingURL=index.js.map