Skip to content

Discord MVP slice 1: bot foundation (sidecar + bridge + settings)#260

Merged
ANonABento merged 3 commits into
mainfrom
discord-mvp
Jun 9, 2026
Merged

Discord MVP slice 1: bot foundation (sidecar + bridge + settings)#260
ANonABento merged 3 commits into
mainfrom
discord-mvp

Conversation

@ANonABento

Copy link
Copy Markdown
Owner

First slice of the Discord MVP (.tickets/discord/MVP-PLAN.md): connect a bot and test it. Task threads + agent-output streaming are the next slice.

What's here

  • Node sidecar (src-tauri/sidecars/discord-bot) — discord.js, driven over newline-delimited JSON on stdin/stdout. Commands: connect/disconnect/ping/create_thread/post_message; events: ready/error/disconnect/message. connect waits for gateway-ready and returns the bot tag.
  • Rust bridge (src/discord/mod.rs) — spawns + supervises the sidecar, id-correlated request/response with timeout, forwards events as discord:<event>, drains stderr to the log, resolves node + the sidecar path.
  • Commands (src/commands/discord.rs) — discord_get_config / discord_status / discord_test_connection (throwaway probe → bot tag) / discord_save_config (persist + reconcile the single managed bridge). Auto-reconciles on startup.
  • ConfigAppSettings.discord_enabled / discord_bot_token / discord_thread_channel_id (opt-in, off by default).
  • Settings UI — a Discord tab: enable, token + Test connection, task-thread channel, Save.

Notes

  • Opt-in & isolated: the app runs fully without Node/Discord.
  • Sidecar deps are a one-time npm install in the sidecar dir (node_modules gitignored; package-lock.json committed). CI doesn't need them — the Rust code only references the sidecar at runtime.
  • To test live: create a Discord bot (enable the Message Content intent), invite it with Create Public Threads + Send Messages in Threads, paste the token in Settings → Discord → Test connection.

clippy · cargo check · config tests · tsc · lint · sidecar smoke all green.

…e 1)

First slice of the Discord MVP (.tickets/discord/MVP-PLAN.md): connect a bot and
test it. No task threads / output streaming yet (next slice).

- Node.js discord.js sidecar at src-tauri/sidecars/discord-bot — driven over
  newline-delimited JSON on stdin/stdout (commands: connect/disconnect/ping/
  create_thread/post_message; events: ready/error/disconnect/message). `connect`
  waits for the gateway-ready event and returns the bot tag.
- Rust bridge src-tauri/src/discord/mod.rs: spawns + supervises the sidecar,
  id-correlated request/response with timeout, forwards events as discord:<event>
  to the frontend, drains stderr to the log, resolves node + the sidecar path.
- Commands src-tauri/src/commands/discord.rs: discord_get_config / discord_status
  / discord_test_connection (throwaway probe → bot tag) / discord_save_config
  (persist + reconcile the single managed bridge). Auto-reconciles on startup.
- Config: AppSettings gains discord_enabled / discord_bot_token /
  discord_thread_channel_id (opt-in, off by default).
- Settings UI: a Discord tab (enable, token + test connection, task-thread
  channel, save).

Opt-in and isolated: the app runs fully without Node/Discord. Sidecar deps are a
separate `npm install` in the sidecar dir (node_modules gitignored; package-lock
committed). clippy · cargo check · config tests · tsc · lint · sidecar smoke green.
Slice 1 only posts to Discord (create thread + send message = REST + the
non-privileged Guilds gateway intent). Requesting the PRIVILEGED MessageContent
intent made login fail with "disallowed intents" for any bot that doesn't have
it enabled in the Developer Portal. Drop it (and GuildMessages) until the
reply-routing slice (T058) actually reads message text — so an existing bot can
connect with no portal changes. The (now inert) messageCreate handler stays for
that later slice.
The board now mirrors to Discord (one direction): when a trigger-spawned agent
runs, a thread is created for the task; on completion its output tail + status
are posted into that thread.

- Migration 047 + db::discord: `discord_task_threads` (task_id ↔ thread_id,
  channel_id) with upsert/get helpers.
- discord::notify: best-effort, fire-and-forget hooks — `on_task_started`
  (create thread "▸ <title>" in the configured channel, persist the mapping,
  post "agent started") and `on_task_finished` (post the ANSI-stripped output
  tail in code fences, chunked under Discord's limit, then ✅/❌ status). Guarded:
  a no-op unless Discord is enabled, the bridge is running, and a channel is set.
  `chunk_message` mirrors choomfie's line-aware splitter (unit-tested).
- Wired into the headless trigger path (bridge::run_trigger_in_tmux) at start +
  completion; both calls are spawned so they never block or fail agent execution.

Interactive-path hooks + real-time (mid-run) streaming are follow-ups; this posts
the output tail at completion. clippy · cargo test (502, +4 chunker) green.
@ANonABento ANonABento merged commit 00169c4 into main Jun 9, 2026
3 checks passed
@ANonABento ANonABento deleted the discord-mvp branch June 9, 2026 04:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant