agent-slack 0.8.0 → 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) {
@@ -4013,12 +4023,13 @@ async function resolveUserId(client, input) {
4013
4023
  if (!handle) {
4014
4024
  return null;
4015
4025
  }
4026
+ const handleLower = handle.toLowerCase();
4016
4027
  let cursor;
4017
4028
  for (;; ) {
4018
4029
  const resp = await client.api("users.list", { limit: 200, cursor });
4019
4030
  const members = asArray(resp.members).filter(isRecord);
4020
4031
  const found = members.find((m) => {
4021
- if (getString(m.name) === handle) {
4032
+ if (getString(m.name)?.toLowerCase() === handleLower) {
4022
4033
  return true;
4023
4034
  }
4024
4035
  if (looksLikeEmail) {
@@ -4589,14 +4600,15 @@ async function sendMessage(input) {
4589
4600
  if (target.kind === "url") {
4590
4601
  const { ref } = target;
4591
4602
  warnOnTruncatedSlackUrl(ref);
4592
- await input.ctx.withAutoRefresh({
4603
+ return await input.ctx.withAutoRefresh({
4593
4604
  workspaceUrl: ref.workspace_url,
4594
4605
  work: async () => {
4595
- const { client } = await input.ctx.getClientForWorkspace(ref.workspace_url);
4606
+ const { client, workspace_url } = await input.ctx.getClientForWorkspace(ref.workspace_url);
4596
4607
  const msg = await fetchMessage(client, { ref });
4597
4608
  const threadTs = msg.thread_ts ?? msg.ts;
4598
- await sendMessageToChannel({
4609
+ return await sendMessageToChannel({
4599
4610
  client,
4611
+ workspaceUrl: workspace_url ?? ref.workspace_url,
4600
4612
  channelId: ref.channel_id,
4601
4613
  text: input.text,
4602
4614
  blocks,
@@ -4605,17 +4617,17 @@ async function sendMessage(input) {
4605
4617
  });
4606
4618
  }
4607
4619
  });
4608
- return { ok: true };
4609
4620
  }
4610
4621
  if (target.kind === "user") {
4611
4622
  const workspaceUrl2 = input.ctx.effectiveWorkspaceUrl(input.options.workspace);
4612
- await input.ctx.withAutoRefresh({
4623
+ return await input.ctx.withAutoRefresh({
4613
4624
  workspaceUrl: workspaceUrl2,
4614
4625
  work: async () => {
4615
- const { client } = await input.ctx.getClientForWorkspace(workspaceUrl2);
4626
+ const { client, workspace_url } = await input.ctx.getClientForWorkspace(workspaceUrl2);
4616
4627
  const dmChannelId = await openDmChannel(client, target.userId);
4617
- await sendMessageToChannel({
4628
+ return await sendMessageToChannel({
4618
4629
  client,
4630
+ workspaceUrl: workspace_url ?? workspaceUrl2,
4619
4631
  channelId: dmChannelId,
4620
4632
  text: input.text,
4621
4633
  blocks,
@@ -4623,20 +4635,20 @@ async function sendMessage(input) {
4623
4635
  });
4624
4636
  }
4625
4637
  });
4626
- return { ok: true };
4627
4638
  }
4628
4639
  const workspaceUrl = input.ctx.effectiveWorkspaceUrl(input.options.workspace);
4629
4640
  await input.ctx.assertWorkspaceSpecifiedForChannelNames({
4630
4641
  workspaceUrl,
4631
4642
  channels: [String(target.channel)]
4632
4643
  });
4633
- await input.ctx.withAutoRefresh({
4644
+ return await input.ctx.withAutoRefresh({
4634
4645
  workspaceUrl,
4635
4646
  work: async () => {
4636
- const { client } = await input.ctx.getClientForWorkspace(workspaceUrl);
4647
+ const { client, workspace_url } = await input.ctx.getClientForWorkspace(workspaceUrl);
4637
4648
  const channelId = await resolveChannelId(client, String(target.channel));
4638
- await sendMessageToChannel({
4649
+ return await sendMessageToChannel({
4639
4650
  client,
4651
+ workspaceUrl: workspace_url ?? workspaceUrl,
4640
4652
  channelId,
4641
4653
  text: input.text,
4642
4654
  blocks,
@@ -4645,7 +4657,6 @@ async function sendMessage(input) {
4645
4657
  });
4646
4658
  }
4647
4659
  });
4648
- return { ok: true };
4649
4660
  }
4650
4661
  function normalizeAttachPaths(raw) {
4651
4662
  if (!Array.isArray(raw) || raw.length === 0) {
@@ -4661,13 +4672,27 @@ function normalizeAttachPaths(raw) {
4661
4672
  }
4662
4673
  async function sendMessageToChannel(input) {
4663
4674
  if (input.attachPaths.length === 0) {
4664
- await input.client.api("chat.postMessage", {
4675
+ const resp = await input.client.api("chat.postMessage", {
4665
4676
  channel: input.channelId,
4666
4677
  text: input.text,
4667
4678
  thread_ts: input.threadTs,
4668
4679
  ...input.blocks ? { blocks: input.blocks } : {}
4669
4680
  });
4670
- 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
+ };
4671
4696
  }
4672
4697
  if (input.blocks) {
4673
4698
  process.stderr.write(`Warning: rich text formatting is not supported with file attachments; sending as plain text.
@@ -4684,6 +4709,11 @@ async function sendMessageToChannel(input) {
4684
4709
  });
4685
4710
  initialComment = "";
4686
4711
  }
4712
+ return {
4713
+ ok: true,
4714
+ channel_id: input.channelId,
4715
+ thread_ts: input.threadTs
4716
+ };
4687
4717
  }
4688
4718
  async function editMessage(input) {
4689
4719
  const target = parseMsgTarget(String(input.targetInput));
@@ -8430,6 +8460,16 @@ async function performUpdate(latest) {
8430
8460
  if (actual !== expected) {
8431
8461
  return { success: false, message: `Checksum mismatch: expected ${expected}, got ${actual}` };
8432
8462
  }
8463
+ if (process.platform === "darwin") {
8464
+ try {
8465
+ execSync2(`codesign --remove-signature ${JSON.stringify(binTmp)}`, {
8466
+ stdio: "ignore"
8467
+ });
8468
+ execSync2(`codesign --sign - ${JSON.stringify(binTmp)}`, {
8469
+ stdio: "ignore"
8470
+ });
8471
+ } catch {}
8472
+ }
8433
8473
  const currentBin = process.execPath;
8434
8474
  const backupPath = `${currentBin}.bak`;
8435
8475
  await rename(currentBin, backupPath);
@@ -9145,5 +9185,5 @@ if (subcommand && subcommand !== "update") {
9145
9185
  backgroundUpdateCheck();
9146
9186
  }
9147
9187
 
9148
- //# debugId=4F295E0EDEFC23F164756E2164756E21
9188
+ //# debugId=5E5A3A0EB875DD7164756E2164756E21
9149
9189
  //# sourceMappingURL=index.js.map