Commit Graph

83 Commits

Author SHA1 Message Date
Jinjing 8c44393f41 refactor: optimize fetchUnreads API usage and filtering
- Concurrently fetch conversations.info and conversations.history instead of sequentially
- Utilize client.counts' unread_count and unread_count_display to prevent history fetches for --counts-only
- Change filtering to use a system subtype denylist instead of a broad allowlist to prevent missing custom messages or file shares
- Fix mangled ellipsis character in truncated messages
2026-04-07 17:19:57 -07:00
Joshua Yoes 204571de7d feat: add unreads command for inbox-style unread message view
Uses Slack's internal client.counts API (available with xoxc browser
tokens) to efficiently fetch unread state across all conversations in a
single call, then hydrates each channel with conversations.info for
names and conversations.history for actual unread messages.

Features:
- Shows unread messages across channels, DMs, group DMs, and threads
- Sorts by mention count then unread count (most important first)
- Resolves DM display names via users.info
- Filters system messages (joins/leaves/topic changes) by default
- --counts-only mode for lightweight overview
- --max-messages and --max-body-chars for token budget control
- --include-system to show filtered system messages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:17:44 -07:00
Joshua Yoes 8a68c0512c feat: add later command for managing saved-for-later messages (#76)
* feat: add later command for managing saved-for-later messages

Adds `agent-slack later` with subcommands for managing Slack's Later tab:
list, complete, archive, reopen, save, remove, and remind.

Uses Slack's internal saved.update API (with xoxc browser tokens) to
manage saved items. Key implementation detail: saved.update requires
multipart/form-data encoding — the standard form-urlencoded format
silently ignores state-change parameters like `mark`. Adds apiMultipart()
to SlackApiClient for this.

The `mark` parameter controls state transitions:
- completed/uncompleted: toggle completion
- archived/unarchived: toggle archive
- date_due (unix ts): set/clear reminder on item

Includes 23 unit tests covering API calls, state filtering, message
hydration, DM name resolution, content truncation, and duration parsing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback for later command

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
2026-04-07 14:10:19 -07:00
Paul Somers 1eac9e177e feat: add workflow discovery and execution (#40)
* feat: add workflow discovery and execution

Add `workflow` command with subcommands:
- `list` — discover workflows bookmarked in a channel
- `preview` — get trigger metadata
- `get` — get workflow definition including form fields
- `run` — trip a workflow trigger

* fix: use trigger_id instead of bookmark_id in trip context

The context payload passed to workflows.triggers.trip was labeling the
trigger ID as bookmark_id, which is misleading. Renamed to trigger_id
to accurately reflect what the value represents.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 21:15:36 -07:00
Jinjing fec9babe9d fix: align message download failure paths 2026-03-21 20:16:18 -07:00
Jinjing f85b6a8b76 fix: preserve local paths for failed message downloads 2026-03-21 20:15:05 -07:00
Jinjing 684bc4b82d test: cover partial file-search download failures 2026-03-21 20:12:31 -07:00
Jinjing bb93e496d4 fix: preserve message attachment metadata on download errors 2026-03-21 20:08:49 -07:00
Paul Somers 56d53860c3 style: fix curly brace lint error 2026-03-21 11:54:41 +00:00
Paul Somers d7c39e76d3 fix: structured error handling for file downloads
Introduce SlackDownloadError and tryDownloadSlackFile wrapper that
distinguishes network/HTTP errors (caught) from disk/write errors
(propagated). Failed downloads now produce DownloadResult objects
that flow through toCompactMessage, preserving file metadata so
content-type filtering works correctly even when downloads fail.
2026-03-21 11:47:22 +00:00
Paul Somers 1ac249cc93 fix: skip file download errors in search instead of aborting
A 401/404 on a single file attachment (deleted file, no read
permission) would throw through downloadSlackFile and kill the
entire search operation. The message-read path already handled
this gracefully via downloadMessageFiles' per-file try/catch,
but search-messages and search-files called downloadSlackFile
directly with no error handling.

Wrap each downloadSlackFile call in try/catch { continue } so
failed downloads are skipped and remaining results still return.
2026-03-20 00:12:16 +00:00
Robert Lucy 7aba3e78ee Add channel mark command (#63)
* Add `channel mark` command to mark conversations as read

Wraps Slack's `conversations.mark` API. Accepts message URLs,
channel names/IDs, or DM channel IDs. Works with channels, DMs,
and group DMs.

* Reject --workspace when target is a URL in channel mark

URL targets are authoritative for workspace — allowing --workspace
to override creates a footgun where a permalink from workspace A
could be executed against workspace B. This aligns channel mark
with the existing message command behavior.

Adds tests for URL workspace rejection and --ts validation.

* Reject --ts when target is a URL in channel mark

A URL is a fully-specified target (workspace + channel + ts), so
allowing --ts to override the timestamp is inconsistent with the
existing --workspace rejection. If you want a different ts, use
the #channel --ts form instead.

Adds test for URL --ts rejection and updates commands.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Allow --ts override with URL targets in channel mark

A URL provides channel + workspace + ts, but --ts override is useful
(e.g. marking to just before a message to make it appear unread).
This differs from --workspace which remains rejected since overriding
the workspace is a genuine footgun.

Adds test for --ts override with URL targets and updates commands.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 17:51:59 -07:00
Neil 1a7a043e71 Fix eslint warnings: max-params, unused imports, max-lines
- Refactor decryptChromiumCookieValue to use options object (max-params)
- Remove unused imports in message-actions.ts and context.ts
- Extract getSafeStoragePasswords and decryptCookieWindows to desktop-crypto.ts (max-lines)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:48:16 -07:00
unsafe9 5d143d688f Refactor getSafeStoragePasswords to single loop 2026-03-09 12:02:41 +09:00
unsafe9 a38e0493ea Fix import-desktop when both Electron and MAS Slack coexist on macOS 2026-03-09 11:54:55 +09:00
Wooseok Han 957e783f64 Fix auth import-desktop when multiple Slack installations exist (#59)
Previously, getSlackPaths() returned only the first Slack data directory
found. On macOS, if both the Electron (~/Library/Application Support/Slack)
and App Store (~/Library/Containers/com.tinyspeck.slackmacgap/...) directories
existed, the Electron path was always picked — even if its cookie could not
be decrypted (e.g. stale data from a previous installation).

Now getAllSlackPaths() returns all valid candidate paths, and
extractFromSlackDesktop() tries each one until both LevelDB team extraction
and cookie decryption succeed. The error message now reports all attempted
paths with their individual failures.
2026-03-08 09:10:48 -07:00
Jinjing 2a6a9e5c9d Merge pull request #58 from TheOutdoorProgrammer/main
Misc Message Draft Fixes
2026-03-08 09:07:39 -07:00
Jinjing 50768ed2f7 Merge pull request #57 from jprichardson/fix/connect-user-real-name
fix: resolve real_name for Slack Connect users
2026-03-08 09:06:04 -07:00
TheOutdoorProgrammer e717ae2d18 fix: inline draft-editor.html into bundle via static text import 2026-03-07 20:53:50 -05:00
TheOutdoorProgrammer 3362d7e7cd style: format draft-editor.html with oxfmt 2026-03-07 20:47:17 -05:00
TheOutdoorProgrammer 4051d8a2d6 fix: repair double-escaped regex patterns in draft editor 2026-03-07 20:45:48 -05:00
JP Richardson f95ffac930 fix: resolve real_name for Slack Connect users
For Slack Connect users (external org), the `users.info` API response
does not include `real_name` at the top level — it only appears inside
`profile.real_name`. This causes `user get` to return only the handle
(e.g. "bob") instead of the full name (e.g. "Bob Smith").

Fall back to `profile.real_name` when the top-level field is missing.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-07 18:28:46 -06:00
Jinjing 6ab23739ff fix(nix): harden update script with GitHub token auth and add .gitattributes (#54)
- fix(nix): harden update script with GitHub token auth and add .gitattributes

- fix(nix): harden update script with GitHub token auth and add .gitattributes
- fix issues
- fix issues
- fix issues
- fix issues
2026-03-06 21:45:44 -08:00
Paul Somers a3c19d555d feat[users]: add dm-channel command for group DM channel discovery (#38)
* feat[users]: add dm-channel command for discovering DM and group DM channels

* feat[users]: add dm-open command for discovering DM and group DM channels
2026-03-06 15:54:39 -08:00
Jinjing b4b5da9dec 0.5.2 2026-03-06 12:28:35 -08:00
Jinjing e09762012f Merge pull request #47 from tusharmagar/feat/windows-desktop-import
feat: Add Windows support for import-desktop command
2026-03-06 11:49:35 -08:00
Paul Somers 29a6ebf564 fix: lazy-load draft-editor.html to prevent startup crash in Bun bundle (#50)
The top-level readFileSync caused the binary to crash on import because
the HTML file isn't available in the Bun single-file executable filesystem.
2026-03-06 08:28:55 -08:00
tusharmagar 3db3dc5a8e fix: use execFileSync for PowerShell, add DPAPI error check, fix JSDoc
- Replace execSync with execFileSync for PowerShell invocation to bypass
  cmd.exe shell interpolation, consistent with every other exec call in
  the file. Eliminates double-quote injection surface.
- Add existence check for decrypted key file after PowerShell DPAPI call
  to provide a meaningful error instead of cryptic ENOENT.
- Update JSDoc to document v10/v11 prefix support (both are handled).
- Remove unused execSync import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:26:07 +05:30
Jinjing 27c069ddc9 feat: add file attachment support to message send (#48)
* feat: implement file attachment functionality for messages

- feat: implement file attachment functionality for messages

- initial implementation of attaching files to a message

- feat(message): auto-detect bullet lists and send as rich_text blocks (#45)

- fix(lint): add curly braces to single-line if statements in users.ts

* feat(send): make message text optional when attaching files

No longer need to pass a throwaway "Files" text when the intent is
just to upload attachments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:40:11 -08:00
tusharmagar b182b4a593 fix: address code review feedback for Windows desktop import
- Remove require("node:crypto") in ESM module; use existing top-level
  createDecipheriv import instead (fixes ReferenceError in Node ESM)
- Escape single quotes in PowerShell temp file paths to prevent breakage
  and command injection for profiles like C:\Users\O'Connor
- Update README auth section to reflect Windows platform support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:49:25 +05:30
JP Richardson ed7f74560f feat(message): auto-detect bullet lists and send as rich_text blocks (#45)
Closes #44

When `message send` text contains bullet or numbered list patterns
(- , * , • , 1. , etc.), automatically convert to Slack rich_text
blocks so recipients see native editable bullets instead of plain
dashes. Plain text without lists is sent unchanged (zero behavior
change).

Also fixes object/array serialization in browserApi() where String(v)
would produce "[object Object]" — now uses JSON.stringify for objects.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-05 13:22:40 -08:00
Tushar Magar 3926072175 feat: Add Windows support for import-desktop command
Add support for extracting Slack credentials from the Windows desktop
app (both regular installer and Microsoft Store version).

- Add Windows Slack data paths (%APPDATA%\Slack and Microsoft Store package)
- Implement DPAPI + AES-256-GCM cookie decryption via PowerShell
- Copy Cookies DB to temp file before reading (Windows file locking)
- URL-decode the decrypted cookie value
- Allow refreshFromDesktopIfPossible on win32
2026-03-05 19:36:06 +05:30
JP Richardson 529a1db975 fix(lint): add curly braces to single-line if statements in users.ts
The eslint `curly` rule requires braces on all control flow statements.
Seven violations in src/slack/users.ts cause CI to fail on every PR.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-04 21:13:25 -06:00
Neil 7d84e93010 Merge pull request #39 from jneb802/feature/add-dm_id-to-user-list 2026-03-03 00:20:12 -08:00
Major Hayden e690a87a5f feat(auth): add Firefox credential import support
Support importing xoxc/xoxd from Firefox profiles on macOS/Linux so browser auth can be bootstrapped without Chrome or Desktop extraction.

Refs: #42
Signed-off-by: Major Hayden <major@mhtx.net>
2026-02-27 15:08:01 -06:00
jneb802 c777d9f92a Add dm_id to user list output
Fetches IM conversations in parallel with users.list and enriches each
user object with their DM channel ID (dm_id) when one exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 10:54:52 -08:00
Neil 3425ca901b fix: improve test coverage and allow small --limit values
Add tests for call ordering, user-not-found error path, upper bound
clamping, listAllConversations forwarding, and normalizeConversationsPage
edge cases. Remove arbitrary min-10 floor on --limit to support
token-efficient small fetches (e.g. --limit 3).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:42:33 -08:00
Neil e6c90f1cca Add rebased channel list command from PR #3 2026-02-22 01:31:14 -08:00
bheemreddy-samsara e745dd7801 fix: support user ID targets for message send (DM via conversations.open)
When a Slack user ID (U[A-Z0-9]{8,}) is passed as the target to
`message send`, the CLI now recognizes it as a user target, opens a DM
channel via `conversations.open`, and posts the message there.

Previously, user IDs fell through to the channel-name resolution path,
which called `search.messages` with `in:#U12345...` — a query that
returns no results and falls back to paginating `conversations.list`
across the entire workspace, hanging indefinitely on large workspaces.

Other subcommands (get, list, edit, delete, react, draft) that don't
support DMs now throw a clear error instead of hanging.

Fixes #35
2026-02-21 23:09:36 -06:00
Neil 4371e234a5 Fix install method detection and stdio handling
- Check execPath basename to distinguish bun runtime from compiled binary
- Use basename match for node detection instead of substring
- Capture pkg manager stdout to preserve JSON output contract

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 20:04:42 -08:00
Neil 96b6b413b8 Add package manager-aware update command
Detect install method (npm, bun, or standalone binary) and use the
appropriate update strategy. npm/bun installs now run the package
manager directly instead of downloading a GitHub release binary.
Background update notices also show the correct command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:53:19 -08:00
Neil 7b89d10f4f Branding tweaks: clickable logo, spacing adjustments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:38:00 -08:00
Neil 9b3128327a Move Stably logo inline with byline text, use Inter font
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:35:58 -08:00
Neil 2bbf375fe1 Add Stably branding to draft editor
- 'By Stably.ai' subtitle under Agent Slack header (links to stably.ai)
- Stably logo in top-right corner (inlined as base64 data URI)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:31:57 -08:00
Neil ed2c7fc464 Fix review findings: error handling, input validation, XSS, timeout
- Keep server open on /send failure so user can retry (was silently cancelling)
- Validate /send body has non-empty text string before calling onSend
- Escape < as \u003c in injected config to prevent </script> XSS
- Add 30-minute idle timeout to prevent CLI hanging forever
- Preserve leading indentation in code block serialization (trimEnd vs trim)
- Return ts from sendFn in CI mode for downstream consumers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:21:29 -08:00
Neil c8b0b93e31 Resolve channel/DM names and show workspace in draft editor
- Add resolveChannelName() using conversations.info + users.info for DMs
- URL targets now show human-readable names (e.g. 'Brennan Benson' not 'D05CB2ZQRDF')
- Channel ID targets also resolve to names via resolveChannelName
- Extract and display workspace name from URL in editor header

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:11:36 -08:00
Neil 3074845238 Polish draft editor UI and add View in Slack link
- Match Slack compositor styling (Aa toolbar toggle, square send button,
  border colors, bottom bar layout)
- Return message ts from onSend to build permalink after sending
- Show View in Slack link in post-send overlay
- Update README, SKILL.md, and commands.md with draft command docs
- Add editor screenshot for PR

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:50:32 -08:00
Neil c7813ba48c Add message draft command with rich Slack-like browser editor
Adds `message draft` CLI command that opens a WYSIWYG editor in the
browser for composing Slack messages with full rich text support (bold,
italic, strikethrough, code, code blocks, lists, blockquotes, links).

Features:
- Self-contained HTML editor served via ephemeral HTTP server
- Slack dark theme with Agent Slack branding
- Toolbar + keyboard shortcuts matching Slack (Cmd+B/I/E/K/etc.)
- Code block escape via double-Enter
- Inline link popover with Text + URL fields (Cmd+K)
- mrkdwn source mode toggle
- Thread context with Slack hyperlink
- Sends message directly via Slack API on submit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:25:02 -08:00
Neil 644cf81909 Add Slack Connect external invite mode for channel invite 2026-02-20 10:33:42 -08:00
Neil 5bacb21803 Add channel create/invite commands and sync docs 2026-02-20 10:25:28 -08:00