{"id":50403251,"url":"https://github.com/haileyok/cc-discord","last_synced_at":"2026-05-31T00:04:52.166Z","repository":{"id":359231182,"uuid":"1232487365","full_name":"haileyok/cc-discord","owner":"haileyok","description":"A bridge for using Claude Code TUI via Zellij and Discord simultaneously - No Agent SDK Required","archived":false,"fork":false,"pushed_at":"2026-05-20T23:54:25.000Z","size":685,"stargazers_count":4,"open_issues_count":2,"forks_count":3,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T05:58:19.362Z","etag":null,"topics":["agent","ai","claude","claude-code","harness"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/haileyok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-08T01:31:24.000Z","updated_at":"2026-05-20T23:54:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/haileyok/cc-discord","commit_stats":null,"previous_names":["haileyok/cc-discord"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/haileyok/cc-discord","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haileyok%2Fcc-discord","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haileyok%2Fcc-discord/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haileyok%2Fcc-discord/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haileyok%2Fcc-discord/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/haileyok","download_url":"https://codeload.github.com/haileyok/cc-discord/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haileyok%2Fcc-discord/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33714036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","ai","claude","claude-code","harness"],"created_at":"2026-05-31T00:04:51.504Z","updated_at":"2026-05-31T00:04:52.160Z","avatar_url":"https://github.com/haileyok.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# claude-discord-bridge\n\nLocalhost HTTP bridge between Claude Code sessions and Discord. Long turns ping your phone, permission prompts surface in a thread, Claude can `/ask-discord \u003cquestion\u003e` when it's blocked, and you can drive whole Claude sessions from Discord slash commands without ever attaching to the terminal.\n\n## What it does\n\nRuns as a small Python daemon (`aiohttp` + `discord.py`) on `127.0.0.1:8787`. Two modes, share one daemon:\n\n**Notification mode** (you run Claude in your terminal, the bridge listens):\n- **Stop hook** — Pings Discord when a Claude turn took \u003e10 minutes. Result lands in a per-session thread.\n- **Notification hook** — Permission prompts and idle states surface as `⏸ awaiting input` in the same thread.\n- **`/ask-discord` skill** — Claude calls this when blocked; the question lands in the thread, the daemon waits up to 15 min for your reply, and Claude continues.\n\n**Discord-driven mode** (`/start` from Discord spawns Claude in a zellij tab):\n- Slash commands manage the lifecycle: `/start`, `/spawn`, `/list`, `/stop`, `/kill`, `/restart`, `/skill`, `/rename`, `/stats`, `/tasks`, `/pin`, `/unpin`.\n- The bridge mirrors assistant text, tool use (with fenced diffs for Edit/Write), subagent activity (live-updated embed per agent), and the session's task list back to its thread.\n- Discord replies in the thread relay into the pane; attachments are saved and their paths get inlined into the prompt so Claude reads them with the `Read` tool. Voice memos are auto-transcribed (Wispr Flow API or local `whisper`).\n- `AskUserQuestion` and `ExitPlanMode` round-trip through Discord reactions / text replies — no need to attach to the pane to answer.\n- The agent can attach files back by emitting `[[attach: /absolute/path]]` markers in its replies.\n\nA separate webhook URL at `~/.claude/discord-notify-webhook` is used as a fallback when the daemon isn't running, so you don't lose pings if you forgot to start it.\n\n## Prereqs\n\n- Python 3.12 managed by [uv](https://github.com/astral-sh/uv) (the repo pins it via `.python-version`).\n- A Discord application with a bot, message-content intent enabled, invited to a guild you control, with permission to view + send messages + create public threads in one channel.\n- [Claude Code](https://docs.claude.com/claude-code) installed.\n\n## Setup\n\n### 1. Discord bot\n\n1. https://discord.com/developers/applications → **New Application** → **Bot** tab → **Reset Token**, copy it.\n2. **Privileged Gateway Intents** → enable **Message Content Intent**. Save.\n3. **OAuth2 → URL Generator** → scopes: `bot` → bot permissions: `View Channels`, `Send Messages`, `Create Public Threads`, `Send Messages in Threads`, `Read Message History` (+ `Manage Channels` if you'll use `/pin`) → open the generated URL → invite the bot to your server.\n4. In the Discord client: User Settings → Advanced → enable **Developer Mode** → right-click the target channel → **Copy Channel ID**.\n\n### 2. Bridge daemon\n\n```bash\ngit clone https://github.com/haileyok/cc-discord.git claude-discord-bridge\ncd claude-discord-bridge\nuv sync\nuv run claude-discord-bridge init\n```\n\n`init` prompts for the bot token and channel ID, writes `~/.config/claude-discord-bridge/secrets.json` at mode `0600`, validates the token by connecting to Discord (15s timeout), and posts a confirmation message to your channel. If the token's wrong it exits 2 and leaves the secrets file so you can fix and retry.\n\n### 3. Wire Claude Code hooks\n\nThe Stop and Notification hooks are referenced by absolute path from `~/.claude/settings.json`. Open it and add (or merge with existing `hooks`):\n\n```json\n{\n  \"hooks\": {\n    \"Stop\": [\n      {\n        \"hooks\": [\n          { \"type\": \"command\", \"command\": \"python3 /home/\u003cyou\u003e/claude-discord-bridge/hooks/notify-stop.py\", \"async\": true }\n        ]\n      }\n    ],\n    \"Notification\": [\n      {\n        \"hooks\": [\n          { \"type\": \"command\", \"command\": \"python3 /home/\u003cyou\u003e/claude-discord-bridge/hooks/notify-notification.py\", \"async\": true }\n        ]\n      }\n    ]\n  }\n}\n```\n\nReplace `/home/\u003cyou\u003e/claude-discord-bridge` with the actual repo path. Validate with `python3 -m json.tool ~/.claude/settings.json \u003e /dev/null`.\n\nIf you already use `~/.claude/hooks/notify-long-task.sh` (or any other Stop hook), keep it on disk as rollback insurance — both hooks can coexist; this one just supersedes it.\n\nThese two hooks cover **notification mode**. **Discord-driven mode** (sessions spawned via `/start`) gets a different set of hooks injected via `claude --settings \u003ctask-scoped-path\u003e` automatically — you don't add them to your user `settings.json`. The task-scoped settings file is generated per `/start` invocation and cleaned up when the task ends. See the `Discord-driven sessions` section below.\n\n### 4. Install the `/ask-discord` skill\n\nClaude Code discovers skills under `~/.claude/skills/\u003cname\u003e/SKILL.md`. The bridge ships the source-of-truth markdown in the repo; symlink it into place:\n\n```bash\nmkdir -p ~/.claude/skills/ask-discord\nln -sfn \"$(pwd)/skills/SKILL.md\" ~/.claude/skills/ask-discord/SKILL.md\n```\n\nAfter symlinking, run `/reload-plugins` in a Claude Code session — `/ask-discord` will appear in the slash-command picker.\n\n### 5. Webhook fallback (optional)\n\nCreate a Discord channel webhook (channel settings → Integrations → Webhooks → New Webhook → copy URL), then write the URL to `~/.claude/discord-notify-webhook`:\n\n```bash\necho 'https://discord.com/api/webhooks/...' \u003e ~/.claude/discord-notify-webhook\nchmod 0600 ~/.claude/discord-notify-webhook\n```\n\nWhen the daemon's down, the Stop and Notification hooks fall back to this webhook (channel root instead of a thread), so you still get pinged.\n\n### 6. Start the daemon\n\n**Foreground (simplest):**\n```bash\nuv run claude-discord-bridge serve\n```\nWait for `Bot ready as \u003cname\u003e, watching #\u003cchannel\u003e`. Use `tmux` or `nohup` if you want it to outlive the shell.\n\n**systemd user unit (survives reboots):**\n```bash\nuv tool install .                      # places `claude-discord-bridge` at ~/.local/bin/\nbash scripts/install-systemd-user.sh   # copies the unit file into ~/.config/systemd/user/\nsystemctl --user daemon-reload\nsystemctl --user enable --now claude-discord-bridge\n```\nIf `systemctl --user` errors with `Operation not permitted`, run `sudo loginctl enable-linger $USER` first.\n\n**macOS launchd user agent (survives reboots and login):**\n\n```bash\nuv tool install .   # places `claude-discord-bridge` at ~/.local/bin/\n```\n\nWrite `~/Library/LaunchAgents/local.claude-discord-bridge.plist`, replacing `\u003cyou\u003e` with your home dir leaf:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003c!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"\u003e\n\u003cplist version=\"1.0\"\u003e\n\u003cdict\u003e\n    \u003ckey\u003eLabel\u003c/key\u003e\n    \u003cstring\u003elocal.claude-discord-bridge\u003c/string\u003e\n    \u003ckey\u003eProgramArguments\u003c/key\u003e\n    \u003carray\u003e\n        \u003cstring\u003e/Users/\u003cyou\u003e/.local/bin/claude-discord-bridge\u003c/string\u003e\n        \u003cstring\u003eserve\u003c/string\u003e\n    \u003c/array\u003e\n    \u003ckey\u003eRunAtLoad\u003c/key\u003e\u003ctrue/\u003e\n    \u003ckey\u003eKeepAlive\u003c/key\u003e\u003ctrue/\u003e\n    \u003ckey\u003eStandardOutPath\u003c/key\u003e\n    \u003cstring\u003e/Users/\u003cyou\u003e/Library/Logs/claude-discord-bridge.log\u003c/string\u003e\n    \u003ckey\u003eStandardErrorPath\u003c/key\u003e\n    \u003cstring\u003e/Users/\u003cyou\u003e/Library/Logs/claude-discord-bridge.log\u003c/string\u003e\n    \u003ckey\u003eEnvironmentVariables\u003c/key\u003e\n    \u003cdict\u003e\n        \u003ckey\u003ePATH\u003c/key\u003e\n        \u003cstring\u003e/Users/\u003cyou\u003e/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin\u003c/string\u003e\n    \u003c/dict\u003e\n\u003c/dict\u003e\n\u003c/plist\u003e\n```\n\nLoad it:\n```bash\nlaunchctl load -w ~/Library/LaunchAgents/local.claude-discord-bridge.plist\n```\n\n`PATH` must include wherever your `zellij` and `claude` binaries live (Homebrew, cargo, nix) — launchd agents don't inherit your shell's `PATH`. Tail the log with `tail -f ~/Library/Logs/claude-discord-bridge.log`. To stop: `launchctl unload ~/Library/LaunchAgents/local.claude-discord-bridge.plist`.\n\n### 7. Verify\n\n```bash\nuv run claude-discord-bridge doctor\n```\nYou should see `[ok]` for each check: secrets file present + 0600, daemon health, settings.json hooks, `/ask-discord` skill symlink, `zellij` installed, bridge session reachable, task-settings dir writable, hook scripts present, `claude` on PATH. `[fail]` lines tell you what to fix; `[warn]` lines are non-blocking.\n\n## Usage — notification mode\n\nOnce the daemon is running, these surfaces work without further intervention:\n\n| Surface | Trigger |\n|---|---|\n| Long turn ping | Run any Claude Code turn that takes \u003e10 minutes |\n| Permission prompt ping | Run any Claude Code action that needs your approval |\n| `/ask-discord` from inside Claude | Ask Claude to use `/ask-discord` when it's blocked |\n| Manual `POST /v1/notify` | `curl -X POST http://127.0.0.1:8787/v1/notify -H 'Content-Type: application/json' -d '{\"session_id\":\"...\",\"cwd\":\"...\",\"message\":\"...\"}'` |\n| Manual `POST /v1/ask` | Same, but `/v1/ask` with a `question` field; blocks for the reply (default 15 min, capped at 60) |\n| Manual `GET /v1/health` | `curl http://127.0.0.1:8787/v1/health` |\n\nThreads are named `cc · \u003ccwd-leaf\u003e · \u003csession-prefix\u003e`. Same `session_id` always routes to the same thread; different sessions get different threads. Mappings persist in SQLite at `~/.local/state/claude-discord-bridge/state.db` and survive daemon restarts. Archived/deleted threads recreate transparently.\n\n## Discord-driven sessions\n\nSpawning Claude Code sessions directly from Discord slash commands. Each task is one zellij tab in a shared session; the bridge injects task-scoped hooks via `claude --settings \u003cpath\u003e` so it can mirror everything back to a per-task thread.\n\n### Slash commands\n\n| Command | What it does |\n|---|---|\n| `/start cwd:\u003cpath\u003e [prompt:\u003ctext\u003e]` | Spawn a new Claude session in `cwd`, opens a fresh thread, optionally writes the initial prompt after bind. |\n| `/spawn project:\u003cpicker\u003e [prompt:\u003ctext\u003e]` | Same as `/start` but the `project` arg is an autocompleted picker over immediate subfolders of `BRIDGE_PROJECT_ROOTS` — no typing paths. |\n| `/list` | List active tasks with status, cwd leaf, age, and thread link. |\n| `/stop [thread:\u003c#thread\u003e]` | Graceful stop — writes `/exit` to the pane, archives the thread on session end. |\n| `/kill [thread:\u003c#thread\u003e]` | Force-close the pane — marks the task crashed, archives the thread. |\n| `/restart [thread:\u003c#thread\u003e]` | Resume a stopped task via `claude --resume \u003csession_id\u003e`; reuses the existing pane if alive, otherwise spawns a fresh one. |\n| `/skill \u003cname\u003e [args:\u003ctext\u003e]` | Type `/\u003cname\u003e [args]` into the running session. Autocomplete shows installed user + plugin skills. |\n| `/rename [name:\u003ctext\u003e]` | Rename the thread; omit `name` to auto-generate via `claude -p` against the transcript. |\n| `/stats [thread:\u003c#thread\u003e]` | Token / cost / context-fill stats for the task, parsed from its transcript. |\n| `/tasks [thread:\u003c#thread\u003e]` | Show the session's `TaskCreate`/`TaskUpdate` mirror as an embed. |\n| `/pin [name:\u003ctext\u003e] [project:\u003cpicker\u003e]` | Create a Discord channel bound to a cwd. Inside a task thread, inherits that thread's cwd; outside, the `project:` autocomplete picks one from `BRIDGE_PROJECT_ROOTS`. Subsequent messages in the new channel auto-spawn a Claude session if none is live. Requires `Manage Channels` permission. |\n| `/unpin` | Remove the pin binding from the current channel (the channel itself is not deleted). Future messages won't auto-spawn. |\n\nCommands without an explicit `thread:` argument operate on the task whose thread you're invoking from.\n\n### What gets mirrored to the thread\n\n- Assistant text and `thinking` blocks at each tool boundary (deduped by entry uuid).\n- Tool use as one-liner summaries, coalesced into bursts; `Edit` / `MultiEdit` / `Write` get a separate fenced-diff block; `TodoWrite` gets a checklist.\n- Subagent activity rolls up into one live-edited embed per agent (yellow while running → green when finished).\n- `AskUserQuestion` posts each question with reaction-based options (single- or multi-select); `ExitPlanMode` posts the plan with ✅/❌. Free-text replies in the thread also work.\n- Voice memos are transcribed (Wispr Flow API if `WISPR_FLOW_API_TOKEN` is set, otherwise local `whisper` CLI) and inlined as `[voice memo] \u003ctext\u003e` in the relayed prompt.\n- Discord file attachments are saved under `~/.local/state/claude-discord-bridge/attachments/\u003ctask_id\u003e/` and their absolute paths are appended to the prompt, one per line.\n- Token / cost / context-fill summary posts after every `Stop`.\n\n### One-time setup\n\n1. **Install `zellij` ≥ 0.44** (older versions have a teardown-race panic that takes down the whole session):\n   ```bash\n   nix-env -iA nixpkgs.zellij   # nix\n   brew install zellij           # macOS\n   cargo install zellij          # build from source\n   ```\n   Verify: `zellij --version`\n\n2. **Pick a session name** (optional). The bridge defaults to `meow`; override by exporting `BRIDGE_ZELLIJ_SESSION=\u003cname\u003e` before starting the daemon. To attach and watch tabs:\n   ```bash\n   zellij attach meow\n   ```\n\n3. **State directories** are auto-created under `~/.local/state/claude-discord-bridge/` (task-settings, attachments, the SQLite db). No manual setup needed.\n\n4. **Optional: get `@`-mentioned when claude is stuck**. Export `BRIDGE_NOTIFY_USER_ID=\u003cyour-discord-user-id\u003e` so AskUserQuestion / ExitPlanMode / free-text-stall prompts prefix with a mention.\n\n### Configuration env vars\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `BRIDGE_URL` | `http://127.0.0.1:8787` | Where hooks POST events. Override only if you run the daemon on a non-default port. |\n| `BRIDGE_ZELLIJ_SESSION` | `meow` | zellij session name the bridge spawns task tabs into. |\n| `BRIDGE_NOTIFY_USER_ID` | _(unset)_ | Discord user id to `@`-mention on TUI-blocking prompts. |\n| `BRIDGE_ATTACHMENT_TTL_SECS` | `604800` | TTL for attachment cleanup (default 7 days). |\n| `BRIDGE_CONTEXT_LIMIT` | _(model default)_ | Override the per-model context window for `/stats` math (e.g. `1000000` for `[1m]`). |\n| `BRIDGE_PROJECT_ROOTS` | _(unset)_ | Colon-separated parent paths whose immediate subfolders are spawnable from `/spawn`'s autocomplete picker. Example: `/Users/me/code/Work:/Users/me/code/Personal`. |\n| `BRIDGE_SPAWN_BIND_TIMEOUT_SECS` | `60` | How long to wait for claude's `SessionStart` hook before giving up and relaying the user's message anyway. Cold-start with multiple MCP servers + plugin sync regularly takes 15–40s; bump higher on slow hardware. |\n| `WISPR_FLOW_API_TOKEN` | _(unset)_ | If set, voice memos use Wispr Flow's API; otherwise local `whisper`. |\n| `BRIDGE_WHISPER_BIN` | `whisper` | Override the local-whisper binary path. |\n| `BRIDGE_WHISPER_MODEL` | `base` | Whisper model size. |\n\n### Verify setup\n\n```bash\nuv run claude-discord-bridge doctor\n```\n\nRuns ten checks: secrets file present + 0600, daemon health, settings.json hooks (Stop/Notification), `/ask-discord` skill symlink, `zellij` installed, bridge session reachable, task-settings dir writable, all hook scripts present, `claude` on PATH.\n\n## Architecture\n\nSingle-process Python daemon. `aiohttp.web.AppRunner` and `discord.py` share one asyncio event loop. Per-session thread mapping lives in SQLite (WAL). Reply routing uses a per-thread `asyncio.Lock` (FIFO) plus a sliding 3-second coalescing window so multi-message replies fold into one response.\n\n| File | Role |\n|---|---|\n| `src/bridge/server.py` | aiohttp app, endpoints `/v1/notify`, `/v1/ask`, `/v1/health`, `/v1/hook/event`, `/v1/hook/pretooluse` |\n| `src/bridge/bot.py` | discord.py wrapper — chunked send, retries on 5xx, `on_message` dispatch, embed edits |\n| `src/bridge/threads.py` | session_id → thread_id with create-on-miss + recreate-on-404 |\n| `src/bridge/listener.py` | Pending-ask state, sliding coalescing window, future lifecycle |\n| `src/bridge/state.py` | aiosqlite — `sessions`, `tasks`, `approval_log` tables |\n| `src/bridge/secrets.py` | 0600 JSON loader/writer |\n| `src/bridge/cli.py` | click CLI: `init`, `serve`, `doctor` |\n| `src/bridge/commands.py` | discord.py slash-command tree (`/start`, `/list`, `/stop`, `/kill`, `/restart`, `/skill`, `/rename`, `/stats`, `/tasks`) |\n| `src/bridge/tasks.py` | `TaskRegistry`: Discord-driven task lifecycle, hook-event dispatch, transcript streaming, subagent block management, task-list mirror |\n| `src/bridge/zellij.py` | Async wrapper around the `zellij` CLI (≥ 0.44 recommended) |\n| `src/bridge/tool_summary.py` | One-liner formatter + fenced diff/code/checklist blocks per tool name |\n| `src/bridge/transcript.py` | Bounded utf-8 JSONL reader for claude transcripts |\n| `src/bridge/usage.py` | Token/cost/context-fill computation for `/stats` and Stop footer |\n| `src/bridge/voice.py` | Audio transcription (Wispr Flow API or local `whisper` CLI) |\n| `src/bridge/skills.py` | Enumerate user-level + enabled-plugin skills for `/skill` autocomplete |\n| `src/bridge/approvals.py` | `ApprovalRouter` — PreToolUse and TUI-prompt round-trips via reactions/text |\n| `hooks/notify-stop.py` | Standalone-mode Stop hook (long-turn ping) |\n| `hooks/notify-notification.py` | Standalone-mode Notification hook (permission/idle ping) |\n| `hooks/event.py` | Discord-driven mode multi-event dispatcher (`SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop`, `SubagentStop`, `Notification`, `SessionEnd`, `PreCompact`) |\n| `hooks/pretooluse-approve.py` | Discord-driven mode PreToolUse approval wrapper (fail-closed, used selectively for `AskUserQuestion` / `ExitPlanMode`) |\n| `skills/SKILL.md` | `/ask-discord` skill instructions for Claude (symlinked into `~/.claude/skills/ask-discord/`) |\n\nSee `CLAUDE.md` for the full set of gotchas and invariants — start there before adding features.\n\n## Development\n\n```bash\nuv run pytest -q --ignore=tests/test_zellij.py    # ~400 tests\nuv run pytest -q tests/test_\u003cmodule\u003e.py\n```\n\nTests use a `FakeBot` and in-memory SQLite, so the suite never hits real Discord. `tests/test_zellij.py` is excluded by default because the (older) tests in it can crash a live zellij session; run it deliberately in isolation if you need to.\n\n## License\n\nTBD.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhaileyok%2Fcc-discord","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhaileyok%2Fcc-discord","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhaileyok%2Fcc-discord/lists"}