hoo-discord-bot
v0.0.2
Published
Use hoocode from Discord: a Discord bot that talks the Codex app-server protocol
Readme
hoo-discord-bot
Use hoocode from Discord.
Discord ⇄ this bot (Bun + discord.js) ⇄ Codex app-server protocol ⇄ hoocode app-serverEach Discord thread is one app-server thread. The bot speaks only standard
Codex app-server methods, so the server is swappable: hoocode app-server
(default), or the real codex app-server, set with APP_SERVER in .env.
By default the bot starts hoocode app-server itself, in HOO_WORKDIR.
To share threads with other clients (e.g. the Codex TUI with
codex --remote unix://PATH), run one server and point everyone at it:
cd workspace && hoocode app-server --listen unix:// # prints the socket path
# .env: APP_SERVER=unix:///Users/you/.cortexcode/app-server-control/app-server-control.sockInstall from npm
Needs Bun. The bot reads .env from the folder you start it in.
bun add -g hoo-discord-bot
curl -o .env https://raw.githubusercontent.com/kolisachint/hoobot/main/.env.example # then fill it in
hoo-discord-botSetup
Install dependencies:
bun installCreate your settings file:
cp .env.example .envOpen
.envand paste the bot token afterDISCORD_TOKEN=.In the developer portal, on the Bot page, turn on Message Content Intent.
Start the bot:
bun startYou should see
Logged in as hoo#1234.
Run in the background (macOS)
Runs at login and restarts itself if it crashes.
scripts/service.sh install # start + enable at login
scripts/service.sh status # running? pid?
scripts/service.sh logs # follow the log
scripts/service.sh restart # after editing .env or code
scripts/service.sh uninstall # stop + remove- Service file:
~/Library/LaunchAgents/com.hoo.discord-bot.plist - Log:
~/.local/state/hoo-discord-bot/bot.log
The Mac must be awake and logged in for the bot to answer.
Using it
- Start: in any channel, mention the bot with a request,
e.g.
@hoo list the files here. It opens a thread. - Continue: type in that thread. No mention needed.
- Steer: typing while it's busy redirects the current run.
Commands (inside a thread)
| Command | What it does |
|---|---|
| !stop | Stop the current run |
| !new | Forget the conversation |
| !status | Model, busy or not, thread, server |
| !model <name> | Switch model from the next message |
| !help | Show help |
Safety
- Only user IDs in
ALLOWED_USER_IDScan use it. The bot won't start if that list is empty. bash,editandwriteshow Allow once / Deny buttons. No click within 10 minutes means denied.- There is no "Always" button. It would change your
global
~/.hoocode/hoo-config.json.
How the approvals work: on first start the bot writes
workspace/.cortexcode/hoo-config.json, which puts that folder in a
custom discord mode. In that mode only read runs without asking.
Your normal build mode, which skips approvals, is not used here.
The server sends approvals to every client on the thread; the first
answer wins and the other clients see it resolved.
Files
| Path | Purpose |
|---|---|
| src/index.ts | Discord side: mentions, threads, commands |
| src/session.ts | One app-server thread per Discord thread; notifications → messages, buttons → approvals |
| src/codex-client.ts | Codex app-server client (unix:// WebSocket or stdio:) |
| src/links.ts | Discord thread → app-server thread links (LINKS_FILE) |
| src/format.ts | Splits long replies to fit Discord's 2000-character limit |
| workspace/ | hoocode's working folder (git-ignored); sessions are saved by hoocode |
Tests
bun run test # message splitting (what CI runs)
bun test/session.e2e.ts # real app-server, fake Discord (uses API credits)
APP_SERVER="stdio:codex app-server" bun test/session.e2e.ts # same, real Codex
bun run typecheck
bun run check # typecheck + unit testsReleasing
Releases are driven by PR labels. package.json's version is owned by CI;
don't edit it by hand.
| Label | On merge |
|---|---|
| npm:patch | 0.1.0 → 0.1.1 |
| npm:minor | 0.1.0 → 0.2.0 |
| npm:major | 0.1.0 → 1.0.0 |
| none | no release |
.github/workflows/ci.yml: typecheck, unit tests andnpm pack --dry-runon every PR and onmain..github/workflows/pr-labels.yml: fails a PR with more than onenpm:*label..github/workflows/release.yml: on merge tomain, reads the merged PR's label, bumpspackage.json, pusheschore(release): vX.Y.Zplus the tagvX.Y.Ztomain, creates the GitHub release, publishes to npm (NPM_TOKENsecret) and comments on the PR. It can also be run by hand (Actions → Release → Run workflow, pick a bump).
Workflow:
/pr minor(orpatch/major; plain/prfor no release): branches, runsbun run check, commits everything, pushes, opens the PR and labels itnpm:minor.- Merge the PR on GitHub.
/postmerge: waits for the release workflow, checks the tag, GitHub release and npm version, then switches tomain, pulls and deletes the merged branch.
The commands live in .cortexcode/prompts/. NPM_TOKEN must be an npm
Automation (or granular publish) token. The bot pushes the release commit
straight to main, so if you add branch protection, let GitHub Actions
bypass it. If a release half-fails, re-run it: the release commit records
Release-PR: #N, so a re-run finishes that version instead of bumping again.
