-
Notifications
You must be signed in to change notification settings - Fork 1
chore: repo infrastructure setup (Biome, CI, docs, Vitest) #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
ae0091c
chore: install and configure Biome
BillChirico cdce895
style: format codebase with Biome
BillChirico 394e2b8
ci: add GitHub Actions workflows
BillChirico da14dc0
docs: add comprehensive documentation and editor config
BillChirico 40e63ac
test: set up Vitest with smoke tests
BillChirico 8075750
chore: align env vars and add command deploy script
BillChirico 7d40516
chore: use claude-opus-4-6 for PR review action
BillChirico 90299cc
docs: replace CLAUDE.md content with link to AGENTS.md
BillChirico c584c22
docs: add documentation maintenance rules to AGENTS.md
BillChirico 5edd06c
docs: enforce no console.* logging rule in AGENTS.md
BillChirico de877f0
refactor: replace all console.* calls with Winston logger
BillChirico 86c82aa
chore: regenerate lockfile with biome and vitest dependencies
BillChirico 1458dbc
chore: update dotenv 17.2.3→17.2.4, pnpm 10.28.2→10.29.2
BillChirico 0219421
test: add 80% coverage threshold with @vitest/coverage-v8
BillChirico bb0a611
chore: remove leftover verification scripts from repo root
BillChirico 368a8c9
ci: enforce 80% test coverage threshold in CI pipeline
BillChirico 8043038
chore: remove leftover test-log-levels.js script
BillChirico bcb3e0e
📝 CodeRabbit Chat: Add generated unit tests
coderabbitai[bot] 8af60a7
📝 CodeRabbit Chat: Add unit tests for PR changes
coderabbitai[bot] 99f0bf1
chore: remove coverage output and add to gitignore
BillChirico ead65e4
test: comprehensive unit tests achieving 80%+ coverage
BillChirico a218e85
ci: add id-token write permission for Claude Code review action
BillChirico 35eca61
fix: address PR #16 review comments (round 1 partial)
BillChirico 519d3f4
chore: remove temporary fix tracking files
BillChirico c661207
fix: address PR #16 review comments (round 1 continued)
BillChirico be98128
fix: implement test quality improvements from review feedback
BillChirico 6597d1b
ci: pin workflow actions to immutable commit SHAs
BillChirico 089a35d
docs: align Node 22 references in AGENTS and README
BillChirico 4651d70
fix(config): sanitize production error output and escape reset section
BillChirico 0b854fc
fix(deploy): remove unused __filename and log stack on failure
BillChirico b208df9
fix(events): guard process unhandledRejection handler from duplicate …
BillChirico 4fc995a
test(config): clarify not-deferred error-path intent in comment
BillChirico 8a1ecbd
fix(index): use Events.ClientReady constant in startup handler
BillChirico 1e9d469
test(ai): ensure OPENCLAW_API_KEY cleanup via finally
BillChirico 030f7b6
test(errors): use distinct empty-message unknown error case
BillChirico ec42599
refactor: share command loader across startup and deploy
BillChirico File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # EditorConfig — https://editorconfig.org | ||
|
|
||
| root = true | ||
|
|
||
| [*] | ||
| charset = utf-8 | ||
| end_of_line = lf | ||
| indent_style = space | ||
| indent_size = 2 | ||
| trim_trailing_whitespace = true | ||
| insert_final_newline = true | ||
|
|
||
| [*.md] | ||
| trim_trailing_whitespace = false | ||
|
|
||
| [Makefile] | ||
| indent_style = tab |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,34 @@ | ||
| # Discord bot token | ||
| # Discord bot token (required) | ||
| DISCORD_TOKEN=your_discord_bot_token | ||
|
|
||
| # Discord application client ID (for slash command registration) | ||
| CLIENT_ID=your_discord_client_id | ||
| # Discord application/client ID for slash command deployment (required) | ||
| # Preferred name: | ||
| DISCORD_CLIENT_ID=your_discord_client_id | ||
| # Backward-compatible alias (optional): | ||
| # CLIENT_ID=your_discord_client_id | ||
|
|
||
| # Discord guild/server ID (optional - for faster command deployment during development) | ||
| # If not set, commands deploy globally (takes up to 1 hour to propagate) | ||
| # Discord guild/server ID (optional) | ||
| # If set, commands deploy to one guild instantly (great for development). | ||
| # If omitted, commands deploy globally (can take up to 1 hour). | ||
| GUILD_ID=your_discord_guild_id | ||
|
|
||
| # OpenClaw API (routes through your Claude subscription) | ||
| # Local: http://localhost:18789/v1/chat/completions | ||
| # Remote (Railway/etc): https://your-tailscale-hostname.ts.net/v1/chat/completions | ||
| OPENCLAW_URL=http://localhost:18789/v1/chat/completions | ||
| OPENCLAW_TOKEN=your_openclaw_gateway_token | ||
| # OpenClaw chat completions endpoint (required) | ||
| # Local: http://localhost:18789/v1/chat/completions | ||
| # Remote: https://your-tailscale-hostname.ts.net/v1/chat/completions | ||
| OPENCLAW_API_URL=http://localhost:18789/v1/chat/completions | ||
| # Backward-compatible alias (optional): | ||
| # OPENCLAW_URL=http://localhost:18789/v1/chat/completions | ||
|
|
||
| # Logging level (options: debug, info, warn, error) | ||
| # OpenClaw API key / gateway token (required) | ||
| OPENCLAW_API_KEY=your_openclaw_gateway_token | ||
| # Backward-compatible alias (optional): | ||
| # OPENCLAW_TOKEN=your_openclaw_gateway_token | ||
|
|
||
| # PostgreSQL connection string (required for persistent config/state) | ||
| DATABASE_URL=postgresql://user:password@host:5432/database | ||
|
|
||
| # Optional: force SSL for DB connections if needed by your host | ||
| # DATABASE_SSL=true | ||
|
|
||
| # Logging level (optional: debug, info, warn, error) | ||
| LOG_LEVEL=info |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| lint-and-test: | ||
| name: Lint & Test | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
|
|
||
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | ||
| with: | ||
| node-version: 22 | ||
| cache: pnpm | ||
|
|
||
| - name: Install dependencies | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Lint (Biome) | ||
| run: pnpm lint | ||
|
|
||
| - name: Test with coverage (Vitest) | ||
| run: pnpm test:coverage | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| name: Claude Code Review | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, reopened, synchronize] | ||
| issue_comment: | ||
| types: [created] | ||
|
|
||
| jobs: | ||
| claude-review: | ||
| if: | | ||
| (github.event_name == 'pull_request') || | ||
| (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| issues: write | ||
| id-token: write | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
|
|
||
| - name: Claude Code Review | ||
| uses: anthropics/claude-code-action@23ed4cb53d6eacddbc22ec16652c98bcc54e0476 # v1.0.48 | ||
| with: | ||
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| model: claude-opus-4-6-20250616 | ||
| timeout_minutes: 10 | ||
BillChirico marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| # AGENTS.md — AI Coding Agent Guide | ||
|
|
||
| > This file provides context for AI coding agents (Claude Code, Copilot, Cursor, etc.) working on bills-bot. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| **Bill Bot** is a Discord bot for the Volvox developer community. It provides AI chat (via OpenClaw/Claude), dynamic welcome messages, spam detection, and runtime configuration management backed by PostgreSQL. | ||
|
|
||
| ## Stack | ||
|
|
||
| - **Runtime:** Node.js 22 (ESM modules, `"type": "module"`) | ||
| - **Framework:** discord.js v14 | ||
| - **Database:** PostgreSQL (via `pg` — raw SQL, no ORM) | ||
| - **Logging:** Winston with daily file rotation | ||
| - **AI:** Claude via OpenClaw chat completions API | ||
| - **Linting:** Biome | ||
| - **Testing:** Vitest | ||
| - **Hosting:** Railway | ||
|
|
||
| ## Key Files | ||
|
|
||
| | File | Purpose | | ||
| |------|---------| | ||
| | `src/index.js` | Entry point — client setup, command loading, startup sequence | | ||
| | `src/db.js` | PostgreSQL pool management (init, query, close) | | ||
| | `src/logger.js` | Winston logger setup with file + console transports | | ||
| | `src/commands/*.js` | Slash commands (auto-loaded) | | ||
| | `src/modules/ai.js` | AI chat handler — conversation history, OpenClaw API calls | | ||
| | `src/modules/chimeIn.js` | Organic conversation joining logic | | ||
| | `src/modules/welcome.js` | Dynamic welcome message generation | | ||
| | `src/modules/spam.js` | Spam/scam pattern detection | | ||
| | `src/modules/config.js` | Config loading/saving (DB + file), runtime updates | | ||
| | `src/modules/events.js` | Event handler registration (wires modules to Discord events) | | ||
| | `src/utils/errors.js` | Error classes and handling utilities | | ||
| | `src/utils/health.js` | Health monitoring singleton | | ||
| | `src/utils/permissions.js` | Permission checking for commands | | ||
| | `src/utils/retry.js` | Retry utility for flaky operations | | ||
| | `src/utils/registerCommands.js` | Discord REST API command registration | | ||
| | `src/utils/splitMessage.js` | Message splitting for Discord's 2000-char limit | | ||
| | `config.json` | Default configuration (seeded to DB on first run) | | ||
| | `.env.example` | Environment variable template | | ||
|
|
||
| ## Code Conventions | ||
|
|
||
| ### General | ||
|
|
||
| - **ESM only** — use `import`/`export`, never `require()` | ||
| - **No TypeScript** — plain JavaScript with JSDoc comments for documentation | ||
| - **Node.js builtins** — always use `node:` protocol (e.g. `import { readFileSync } from 'node:fs'`) | ||
| - **Semicolons** — always use them | ||
| - **Single quotes** — enforced by Biome | ||
| - **2-space indentation** — enforced by Biome | ||
|
|
||
| ### Logging | ||
|
|
||
| - **Always use Winston** — `import { info, warn, error } from '../logger.js'` | ||
| - **NEVER use `console.log`, `console.warn`, `console.error`, or any `console.*` method** in src/ files — no exceptions | ||
| - If you see `console.*` in existing code, replace it with the Winston equivalent | ||
| - Pass structured metadata: `info('Message processed', { userId, channelId })` | ||
|
|
||
| ### Error Handling | ||
|
|
||
| - Use custom error classes from `src/utils/errors.js` | ||
| - Always log errors with context before re-throwing | ||
| - Graceful shutdown is handled in `src/index.js` | ||
|
|
||
| ### Config | ||
|
|
||
| - Config is loaded from PostgreSQL (falls back to `config.json`) | ||
| - Use `getConfig()` from `src/modules/config.js` to read config | ||
| - Use `setConfigValue(key, value)` to update at runtime | ||
| - Config is a live object reference — mutations propagate automatically | ||
|
|
||
| ## How to Add a Slash Command | ||
|
|
||
| 1. Create `src/commands/yourcommand.js`: | ||
|
|
||
| ```js | ||
| import { SlashCommandBuilder } from 'discord.js'; | ||
|
|
||
| export const data = new SlashCommandBuilder() | ||
| .setName('yourcommand') | ||
| .setDescription('What it does'); | ||
|
|
||
| export async function execute(interaction) { | ||
| await interaction.reply('Hello!'); | ||
| } | ||
| ``` | ||
|
|
||
| 2. Commands are auto-discovered from `src/commands/` on startup | ||
| 3. Run `pnpm run deploy` to register with Discord (or restart the bot) | ||
| 4. Add permission in `config.json` under `permissions.allowedCommands` | ||
|
|
||
| ## How to Add a Module | ||
|
|
||
| 1. Create `src/modules/yourmodule.js` with handler functions | ||
| 2. Register handlers in `src/modules/events.js`: | ||
|
|
||
| ```js | ||
| import { yourHandler } from './yourmodule.js'; | ||
| // In registerEventHandlers(): | ||
| client.on('eventName', (args) => yourHandler(args, config)); | ||
| ``` | ||
|
|
||
| 3. Config for your module goes in `config.json` under a new key | ||
| 4. Check `config.yourModule.enabled` before processing | ||
|
|
||
| ## Testing | ||
|
|
||
| - **Framework:** Vitest (`pnpm test`) | ||
| - **Test directory:** `tests/` | ||
| - **Coverage:** `pnpm test:coverage` — **mandatory 80% threshold** on statements, branches, functions, and lines | ||
| - Coverage provider: `@vitest/coverage-v8` | ||
| - Tests are smoke/unit tests — the bot requires Discord credentials so we don't test live connections | ||
| - Test config structure, command exports, utility functions | ||
| - Run `pnpm test` before every commit | ||
| - **Any new code must include tests** — PRs that drop coverage below 80% will fail CI | ||
|
|
||
| ## Documentation | ||
|
|
||
| **Keep docs up to date — this is non-negotiable.** | ||
|
|
||
| After every code change, check whether these files need updating: | ||
|
|
||
| - **`README.md`** — setup instructions, architecture overview, config reference, env vars | ||
| - **`AGENTS.md`** (this file) — key files table, code conventions, "how to add" guides, common pitfalls | ||
| - **`CONTRIBUTING.md`** — workflow, branching, commit conventions | ||
| - **`.env.example`** — if you add/remove/rename an environment variable, update this immediately | ||
| - **`config.json`** — if you add a new config section or key, document it in README.md's config reference | ||
|
|
||
| **When to update:** | ||
| - Added a new command → update Key Files table, add to README command list | ||
| - Added a new module → update Key Files table, document config section | ||
| - Changed env vars → update `.env.example` and README's environment section | ||
| - Changed architecture (new dependency, new pattern) → update Stack section and relevant guides | ||
| - Found a new pitfall → add to Common Pitfalls below | ||
|
|
||
| **Rule of thumb:** If a new contributor (human or AI) would be confused without the update, write it. | ||
|
|
||
| ## Common Pitfalls | ||
|
|
||
| 1. **Missing `node:` prefix** — Biome will catch this, but remember it for new imports | ||
| 2. **Config is async** — `loadConfig()` returns a Promise; it must be awaited at startup | ||
| 3. **Discord intents** — the bot needs MessageContent, GuildMembers, and GuildVoiceStates intents enabled | ||
| 4. **DATABASE_URL optional** — the bot works without a database (uses config.json only), but config persistence requires PostgreSQL | ||
| 5. **Undici override** — `pnpm.overrides` pins undici; this was originally added for Node 18 compatibility and may no longer be needed on Node 22. Verify before removing | ||
| 6. **2000-char limit** — Discord messages can't exceed 2000 characters; use `splitMessage()` utility |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # CLAUDE.md | ||
|
|
||
| See [AGENTS.md](./AGENTS.md) for full project context, architecture, and coding guidelines. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Contributing to Bill Bot | ||
|
|
||
| Thanks for your interest in contributing! Bill Bot is part of the [Volvox](https://volvox.dev) open-source community. | ||
|
|
||
| ## Getting Started | ||
|
|
||
| 1. Fork the repository | ||
| 2. Follow the [setup instructions](README.md#-setup) in the README | ||
| 3. Create a feature branch from `main` | ||
|
|
||
| ## Development Workflow | ||
|
|
||
| ### Branch Naming | ||
|
|
||
| Use descriptive branch names with prefixes: | ||
|
|
||
| - `feat/add-music-command` — new features | ||
| - `fix/welcome-message-crash` — bug fixes | ||
| - `chore/update-dependencies` — maintenance | ||
| - `docs/update-readme` — documentation | ||
| - `refactor/simplify-config` — code improvements | ||
|
|
||
| ### Commit Messages | ||
|
|
||
| We use [Conventional Commits](https://www.conventionalcommits.org/): | ||
|
|
||
| ```text | ||
| feat: add music playback command | ||
| fix: prevent crash on empty welcome channel | ||
| chore: update discord.js to v14.16 | ||
| docs: add API reference to README | ||
| refactor: simplify config loading logic | ||
| style: format with Biome | ||
| test: add config validation tests | ||
| ci: update Node.js version in CI | ||
| ``` | ||
|
|
||
| ### Before Submitting | ||
|
|
||
| 1. **Lint:** `pnpm lint` — must pass with no errors | ||
| 2. **Format:** `pnpm format` — auto-format your code | ||
| 3. **Test:** `pnpm test` — all tests must pass | ||
| 4. **Commit:** use conventional commit messages | ||
|
|
||
| ### Pull Requests | ||
|
|
||
| 1. Open a PR against `main` | ||
| 2. Fill in the PR description with what changed and why | ||
| 3. PRs are automatically reviewed by Claude Code | ||
| 4. CI must pass (lint + tests) | ||
| 5. Wait for a maintainer review | ||
|
|
||
| ## Code Style | ||
|
|
||
| Code style is enforced by [Biome](https://biomejs.dev/): | ||
|
|
||
| - Single quotes | ||
| - Semicolons always | ||
| - 2-space indentation | ||
| - Trailing commas | ||
| - 100-character line width | ||
|
|
||
| Run `pnpm format` to auto-format. The CI will reject PRs with formatting issues. | ||
|
|
||
| ## Project Structure | ||
|
|
||
| See [AGENTS.md](AGENTS.md) for a detailed guide to the codebase, including: | ||
|
|
||
| - Key files and their purposes | ||
| - How to add commands and modules | ||
| - Code conventions | ||
| - Common pitfalls | ||
|
|
||
| ## Questions? | ||
|
|
||
| - Open an issue on GitHub | ||
| - Ask in the Volvox Discord server at [volvox.dev](https://volvox.dev) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.