slkcli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,6 +59,9 @@ slk unread
59
59
  # See starred items and VIP users
60
60
  slk starred
61
61
 
62
+ # See saved for later items
63
+ slk saved
64
+
62
65
  # See pinned messages in a channel
63
66
  slk pins general
64
67
 
@@ -84,6 +87,7 @@ slk react general 1234567890.123456 thumbsup
84
87
  | `slk activity` | `a` | Show all channel activity with unread/mention counts |
85
88
  | `slk unread` | `ur` | Show only channels with unreads (excludes muted) |
86
89
  | `slk starred` | `star` | Show VIP users and starred items |
90
+ | `slk saved [count]` | `sv` | Show saved for later items (active by default, `--all` includes completed) |
87
91
  | `slk pins <channel>` | `pin` | Show pinned items in a channel |
88
92
 
89
93
  ### Flags
@@ -92,6 +96,7 @@ slk react general 1234567890.123456 thumbsup
92
96
  |------|-------------|
93
97
  | `--ts` | Show raw Slack timestamps (useful for getting ts to read threads) |
94
98
  | `--no-emoji` | Disable emoji in output (or set `NO_EMOJI=1`) |
99
+ | `--all` | Include completed items in `slk saved` |
95
100
 
96
101
  ```bash
97
102
  # Get timestamps to use with thread command
package/bin/slk.js CHANGED
@@ -28,6 +28,7 @@ Commands:
28
28
  slk activity (a) Channel activity with unread/mention counts
29
29
  slk unread (ur) Channels with unreads (excludes muted)
30
30
  slk starred (star) VIP users + starred items
31
+ slk saved [n] (sv) Saved for later items (--all includes completed)
31
32
  slk pins <ch> (pin) Pinned items in a channel
32
33
 
33
34
  Drafts (synced to Slack UI):
@@ -119,6 +120,11 @@ async function main() {
119
120
  await cmd.starred();
120
121
  break;
121
122
 
123
+ case "saved":
124
+ case "sv":
125
+ await cmd.saved(parseInt(args[1]) || 20, args.includes("--all"));
126
+ break;
127
+
122
128
  case "pins":
123
129
  case "pin":
124
130
  if (!args[1]) { console.error("Usage: slk pins <channel>"); process.exit(1); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slkcli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Slack CLI for macOS, so your agents can read and send messages",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/api.js CHANGED
@@ -29,6 +29,7 @@ export async function slackApi(method, params = {}, retried = false) {
29
29
  "conversations.open",
30
30
  "client.counts",
31
31
  "users.prefs.get",
32
+ "saved.list",
32
33
  ];
33
34
 
34
35
  const isWrite = writeMethods.some((m) => method.startsWith(m));
package/src/commands.js CHANGED
@@ -333,6 +333,72 @@ export async function pins(channelRef) {
333
333
  }
334
334
  }
335
335
 
336
+ export async function saved(count = 20, includeCompleted = false) {
337
+ const users = await getUsers();
338
+
339
+ // Build channel name map
340
+ const chData = await slackPaginate("conversations.list", {
341
+ types: "public_channel,private_channel,mpim,im",
342
+ exclude_archived: true,
343
+ });
344
+ const chMap = {};
345
+ if (chData.ok) {
346
+ for (const ch of chData.channels) {
347
+ chMap[ch.id] = ch.name || (ch.user ? `DM:${userName(users, ch.user)}` : ch.id);
348
+ }
349
+ }
350
+
351
+ const data = await slackApi("saved.list", { count });
352
+ if (!data.ok) {
353
+ console.error(`Error: ${data.error}`);
354
+ process.exit(1);
355
+ }
356
+
357
+ const items = data.saved_items || [];
358
+ const counts = data.counts || {};
359
+ console.log(`📑 Saved for Later — ${counts.uncompleted_count || 0} active, ${counts.completed_count || 0} completed\n`);
360
+
361
+ if (!items.length) {
362
+ console.log("No saved items.");
363
+ return;
364
+ }
365
+
366
+ for (const item of items) {
367
+ if (!includeCompleted && item.state === "completed") continue;
368
+
369
+ const chName = chMap[item.item_id] || item.item_id;
370
+ const savedAt = formatTs(item.date_created);
371
+ const state = item.state === "completed" ? " ✅" : "";
372
+
373
+ // Fetch the actual message
374
+ try {
375
+ const msgData = await slackApi("conversations.history", {
376
+ channel: item.item_id,
377
+ latest: item.ts,
378
+ inclusive: true,
379
+ limit: 1,
380
+ });
381
+ if (msgData.ok && msgData.messages?.[0]) {
382
+ const msg = msgData.messages[0];
383
+ const who = userName(users, msg.user);
384
+ const msgTime = formatTs(msg.ts);
385
+ console.log(`[saved ${savedAt}]${state} #${chName} — ${who} (${msgTime}):`);
386
+ console.log(` ${(msg.text || "").substring(0, 300)}`);
387
+ if (msg.files?.length) {
388
+ for (const f of msg.files) {
389
+ console.log(` 📎 ${f.name} (${f.mimetype})`);
390
+ }
391
+ }
392
+ } else {
393
+ console.log(`[saved ${savedAt}]${state} #${chName} (ts: ${item.ts}) — could not fetch message`);
394
+ }
395
+ } catch {
396
+ console.log(`[saved ${savedAt}]${state} #${chName} (ts: ${item.ts}) — access denied or channel not found`);
397
+ }
398
+ console.log();
399
+ }
400
+ }
401
+
336
402
  export async function react(channelRef, ts, emoji) {
337
403
  const channel = await resolveChannel(channelRef);
338
404
  const data = await slackApi("reactions.add", {