Skip to content

feat: configurable community commands — enabled toggles for all features#106

Merged
BillChirico merged 1 commit intomainfrom
feat/command-config-gates
Feb 27, 2026
Merged

feat: configurable community commands — enabled toggles for all features#106
BillChirico merged 1 commit intomainfrom
feat/command-config-gates

Conversation

@BillChirico
Copy link
Collaborator

Summary

All community feature commands are now configurable per guild with an enabled toggle.

Changes

Web Dashboard

  • New Community Features card with toggles for all 5 commands
  • All 5 sections added to SAFE_CONFIG_KEYS so the API allows saving

Config

All features disabled by default (opt-in). Admins enable what they want:

{ "help": { "enabled": true }, "poll": { "enabled": true } }

Mod commands

/ban, /kick, /warn, etc. remain always-on — they're already permission-gated.


1643 tests passing, 0 lint errors.

Copilot AI review requested due to automatic review settings February 27, 2026 05:06
@claude
Copy link

claude bot commented Feb 27, 2026

Claude finished @BillChirico's task in 2m 12s —— View job


Code Review: PR #106 — Configurable community commands

  • Read project conventions (AGENTS.md, CLAUDE.md)
  • Read all changed files
  • Analyze code quality, architecture, testing, security, and documentation
  • Post inline comments for issues
  • Post review verdict

4 issues found (0 critical, 3 warnings, 1 nitpick)

# Severity File Issue
1 🟡 Warning tests/commands/help.test.js No test for help.enabled === false early-return path
2 🟡 Warning tests/commands/announce.test.js No test for announce.enabled === false early-return path
3 🟡 Warning src/commands/help.js:371 autocomplete() not gated — queries DB and returns suggestions when help is disabled
4 🟡 Warning config.json / README.md New config sections (help, announce, snippet, poll, tldr) not documented in README config reference
5 🔵 Nitpick config.json Missing trailing newline
AI fix prompt (copy-paste into Claude Code)
On the main branch, fix these 5 issues from PR #106 review:

1. tests/commands/help.test.js — Add a test "should reject when help is disabled":
   - Use getConfig.mockReturnValueOnce({ help: { enabled: false } })
   - Call execute() and assert interaction.reply was called with content containing "not enabled" and ephemeral: true
   - Assert interaction.deferReply was NOT called

2. tests/commands/announce.test.js — Add a test "should reject when announce is disabled":
   - Use getConfig.mockReturnValueOnce({ announce: { enabled: false }, permissions: { enabled: true, adminRoleId: null, usePermissions: true } })
   - Call execute() and assert interaction.reply was called with content containing "not enabled" and ephemeral: true
   - Assert interaction.deferReply was NOT called

3. src/commands/help.js — In autocomplete() (line 371), add an early return when help is disabled:
   const config = getConfig(interaction.guildId);
   if (!config.help?.enabled) { return await interaction.respond([]); }

4. README.md — After the Permissions section (around line 225), add config reference tables for:
   - Help (help): enabled (boolean)
   - Announcements (announce): enabled (boolean)
   - Code Snippets (snippet): enabled (boolean)
   - Polls (poll): enabled (boolean)
   - TL;DR (tldr): enabled (boolean), defaultMessages (number, default 50), maxMessages (number, default 200), cooldownSeconds (number, default 300)

5. config.json — Add a trailing newline at end of file.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7ad31b and 69ef675.

📒 Files selected for processing (7)
  • config.json
  • src/api/utils/configAllowlist.js
  • src/commands/announce.js
  • src/commands/help.js
  • tests/commands/announce.test.js
  • tests/commands/help.test.js
  • web/src/components/dashboard/config-editor.tsx

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for new top-level config sections and feature flags: Help/FAQ, Announcements, Code Snippets, Polls, and TL;DR Summaries.
    • Dashboard gained a "Community Features" panel with toggles to enable/disable each feature.
    • Help and Announce commands now check those feature flags and will exit with a brief message if disabled.
  • Tests

    • Updated command tests to include the new feature flags in mocked configurations.

Walkthrough

Adds feature flags for community features (help, announce, snippet, poll, tldr): new config sections with enabled flags, updates config allowlist, adds runtime guards in commands, updates tests, and exposes toggles in the dashboard editor.

Changes

Cohort / File(s) Summary
Top-level config
config.json
Inserted new top-level sections: help, announce, snippet, poll, tldr (each with enabled: false) and relocated tldr within the overall structure.
Config allowlist
src/api/utils/configAllowlist.js
Added help, announce, snippet, poll, tldr to SAFE_CONFIG_KEYS (thus expanding readable config keys).
Command guards
src/commands/announce.js, src/commands/help.js
Added early feature-enabled checks that fetch guild config and return an error if the feature is disabled, short-circuiting command execution.
Tests
tests/commands/announce.test.js, tests/commands/help.test.js
Updated mocked getConfig returns to include announce.enabled and help.enabled respectively so tests exercise enabled paths.
Dashboard UI / Type guards
web/src/components/dashboard/config-editor.tsx
Extended isGuildConfig to recognize new top-level sections and added a "Community Features" UI block with per-feature toggles bound to draftConfig[feature].enabled.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: configurable community commands — enabled toggles for all features' accurately summarizes the main change: adding configurable enabled toggles for community feature commands.
Description check ✅ Passed The description is well-related to the changeset, clearly explaining the enabled toggles added for community commands, the web dashboard updates, and config behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/command-config-gates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR adds configurable toggles for community commands (/help, /announce, /snippet, /poll, /tldr) with config gates and web UI controls.

Changes:

  • Config defaults all 5 features to enabled: false (opt-in model)
  • Web dashboard adds "Community Features" card with toggle switches
  • API allowlist includes all 5 sections for persistence
  • /help and /announce correctly implement config.*.enabled gate checks
  • Tests updated to mock enabled: true in config

Critical Issues:

  • /snippet and /poll commands are missing the enabled gate checks — they will function even when disabled via UI/config
  • Missing safeReply imports in snippet.js and poll.js for the gate check implementation

Recommendations:

  • Add enabled gate checks to /snippet and /poll (see inline comments)
  • Consider adding test cases for the disabled state to ensure gates work correctly

Confidence Score: 1/5

  • Critical logic bugs prevent feature from working as designed
  • The PR implements enabled toggles for 5 commands but only 2 of them (/help, /announce) actually check the config. The /snippet and /poll commands are missing the gate checks entirely, meaning users can toggle them "off" in the UI but the commands will still execute. This is a critical functional bug that breaks the core feature promise.
  • src/commands/snippet.js and src/commands/poll.js require immediate attention — both need enabled gate checks and safeReply imports

Important Files Changed

Filename Overview
src/commands/announce.js adds config.announce?.enabled gate check with proper error message
src/commands/help.js adds config.help?.enabled gate check with proper error message
web/src/components/dashboard/config-editor.tsx adds Community Features card with toggles for all 5 commands and updates knownSections array
src/commands/snippet.js missing enabled gate check — command will work even when disabled in config/UI
src/commands/poll.js missing enabled gate check — command will work even when disabled in config/UI

Last reviewed commit: 69ef675

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

4 issues found (0 critical, 2 warnings, 2 nitpicks)

🟡 Warnings

  1. Missing test for disabled help gate (tests/commands/help.test.js) — Mock updated to enabled: true but no test verifies the early-return when help.enabled is false.
  2. Missing test for disabled announce gate (tests/commands/announce.test.js) — Same gap: no test for the announce.enabled: false path.

🔵 Nitpicks

  1. Autocomplete not gated (src/commands/help.js:371) — autocomplete() still returns suggestions when help.enabled is false, leading to confusing UX (suggestions appear, but command rejects).
  2. Missing trailing newline (config.json:168) — File no longer ends with a newline.

🤖 AI fix prompt (copy-paste into Claude Code)
On branch feat/command-config-gates, fix these 4 issues:

1. tests/commands/help.test.js — Add a test case "should reject when help is disabled":
   - Mock getConfig to return { help: { enabled: false } }
   - Call execute() and assert interaction.reply was called with content containing "not enabled" and ephemeral: true
   - Assert interaction.deferReply was NOT called

2. tests/commands/announce.test.js — Add a test case "should reject when announce is disabled":
   - Mock getConfig to return { announce: { enabled: false }, permissions: { enabled: true, adminRoleId: null, usePermissions: true } }
   - Call execute() and assert interaction.reply was called with content containing "not enabled" and ephemeral: true
   - Assert interaction.deferReply was NOT called

3. src/commands/help.js — In the autocomplete() function (line 371), add an early return when config.help.enabled is false:
   const config = getConfig(interaction.guildId);
   if (!config.help?.enabled) { return await interaction.respond([]); }

4. config.json — Add a trailing newline at the end of the file (after the closing }).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements configurable toggles for community feature commands (help, announce, snippet, poll, tldr) allowing guild admins to selectively enable features via the web dashboard. The changes introduce an enabled flag gate pattern for community commands while keeping moderation commands always available.

Changes:

  • Added enabled gates to /help and /announce commands with consistent error messaging
  • Created Community Features card in web dashboard with toggles for all 5 community commands
  • Added all 5 feature sections to backend config allowlist and default config.json
  • Updated test mocks to reflect enabled state requirement

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/commands/help.js Added config.help.enabled gate check at execute start, minor formatting fix
src/commands/announce.js Added config.announce.enabled gate check before permission validation
tests/commands/help.test.js Updated mock config to include help: { enabled: true }
tests/commands/announce.test.js Updated mock config to include announce: { enabled: true }
src/api/utils/configAllowlist.js Added help, announce, snippet, poll, tldr to SAFE_CONFIG_KEYS
config.json Added config sections for 5 community features, removed duplicate starboard entry
web/src/components/dashboard/config-editor.tsx Added Community Features card with 5 toggles, updated type guard knownSections
Comments suppressed due to low confidence (2)

tests/commands/help.test.js:13

  • The new enabled gate functionality lacks test coverage. Add a test case that verifies the command returns the expected error message when config.help.enabled is false. This would require temporarily mocking getConfig to return { help: { enabled: false } } for that specific test.
  getConfig: vi.fn().mockReturnValue({ help: { enabled: true } }),

tests/commands/announce.test.js:16

  • The new enabled gate functionality lacks test coverage. Add a test case that verifies the command returns the expected error message when config.announce.enabled is false. This would require temporarily mocking getConfig to return { announce: { enabled: false }, permissions: { ... } } for that specific test.
    announce: { enabled: true },

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@config.json`:
- Around line 150-167: Add documentation entries for the new config sections
added to config.json: "help", "announce", "snippet", "poll", and "tldr" into the
README.md configuration reference table; for each of help/announce/snippet/poll
document the "enabled" boolean and its purpose, and for "tldr" document
"enabled" plus "defaultMessages" (default number of messages to consider),
"maxMessages" (upper limit of messages allowed), and "cooldownSeconds" (cooldown
period between TLDR operations) with concise descriptions and expected
types/defaults so the README's config table matches the new keys in config.json.

In `@tests/commands/announce.test.js`:
- Around line 14-19: The test suite currently forces announce.enabled true via
the getConfig mock; add a new test in tests/commands/announce.test.js that sets
vi.mock('../../src/modules/config.js').getConfig to return announce: { enabled:
false } and then invokes the announce command handler (the same execute/handler
used by existing tests) asserting that the interaction receives an immediate
ephemeral reply (interaction.reply called with ephemeral: true) and that
interaction.deferReply and any DB methods (mocked DB client methods used in
other announce tests) are not called. Ensure the test resets mocks between cases
(restore/mocking getConfig) and mirrors existing test patterns for invoking the
command so it fails fast when announce.enabled is false.

In `@tests/commands/help.test.js`:
- Around line 12-14: Add a test in tests/commands/help.test.js that mocks
../../src/modules/config.js.getConfig to return { help: { enabled: false } },
then invoke the same help command/handler used by the existing tests and assert
the disabled early-return path is taken: e.g., the handler returns early
(undefined/false) or responds with the expected disabled result, and verify any
help-rendering function (the module's showHelp/sendHelp/handler) is not called
(use a spy/mock) so the help.enabled === false branch is covered.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3d0639 and c7ad31b.

📒 Files selected for processing (7)
  • config.json
  • src/api/utils/configAllowlist.js
  • src/commands/announce.js
  • src/commands/help.js
  • tests/commands/announce.test.js
  • tests/commands/help.test.js
  • web/src/components/dashboard/config-editor.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Greptile Review
  • GitHub Check: Agent
  • GitHub Check: claude-review
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use ESM modules with import/export syntax; never use require()
Always use node: protocol for Node.js builtins (e.g., import { readFileSync } from 'node:fs')
Always use semicolons in code
Use single quotes for strings (enforced by Biome)
Use 2-space indentation (enforced by Biome)

Files:

  • tests/commands/announce.test.js
  • src/api/utils/configAllowlist.js
  • src/commands/announce.js
  • tests/commands/help.test.js
  • web/src/components/dashboard/config-editor.tsx
  • src/commands/help.js
tests/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.{js,ts,jsx,tsx}: All new code must include tests; test coverage must maintain 80% threshold on statements, branches, functions, and lines
Use Vitest for testing; run pnpm test before every commit and pnpm test:coverage to verify 80% coverage threshold

Files:

  • tests/commands/announce.test.js
  • tests/commands/help.test.js
src/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{js,ts,jsx,tsx}: Always use Winston for logging via import { info, warn, error } from '../logger.js'; never use console.log, console.warn, console.error, or any console.* method in src/ files
Pass structured metadata to Winston logging calls (e.g., info('Message processed', { userId, channelId }))
Use custom error classes from src/utils/errors.js for error handling
Always log errors with context before re-throwing
Use getConfig(guildId?) from src/modules/config.js to read configuration values
Use setConfigValue(path, value, guildId?) from src/modules/config.js to update configuration at runtime
Use safeSend() utility for all outgoing Discord messages to enforce allowedMentions and prevent mention spam
Use sanitizeMentions() to strip @everyone/@here from outgoing text via zero-width space insertion before sending
Use splitMessage() utility to handle Discord's 2000-character message limit
onConfigChange callbacks receive (newValue, oldValue, fullPath, guildId) as parameters

Files:

  • src/api/utils/configAllowlist.js
  • src/commands/announce.js
  • src/commands/help.js
src/commands/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/commands/**/*.{js,ts,jsx,tsx}: Slash commands must export a data property with a SlashCommandBuilder and an async execute(interaction) function
Export adminOnly = true for moderator-only slash commands
Duration-based commands (timeout, tempban, slowmode) must use parseDuration() from src/utils/duration.js for parsing duration arguments
Always call checkHierarchy(moderator, target) before executing moderation actions to prevent moderating users with equal or higher roles

Files:

  • src/commands/announce.js
  • src/commands/help.js
config.json

📄 CodeRabbit inference engine (AGENTS.md)

Document new config sections and keys in README.md's config reference when updating config.json

Files:

  • config.json
🧠 Learnings (13)
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to config.json : Document new config sections and keys in `README.md`'s config reference when updating `config.json`

Applied to files:

  • src/api/utils/configAllowlist.js
  • web/src/components/dashboard/config-editor.tsx
  • config.json
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/commands/**/*{ban,kick}*.{js,ts,jsx,tsx} : Moderation commands must DM the target user before executing kicks/bans (as users cannot receive DMs after being kicked/banned)

Applied to files:

  • src/commands/announce.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/commands/**/*{ban,kick,warn,timeout,mute}*.{js,ts,jsx,tsx} : Moderation commands must follow the pattern: deferReply() → validate → sendDmNotification() → execute Discord action → createCase() → sendModLogEmbed() → checkEscalation()

Applied to files:

  • src/commands/announce.js
  • src/commands/help.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/commands/**/*.{js,ts,jsx,tsx} : Export `adminOnly = true` for moderator-only slash commands

Applied to files:

  • src/commands/announce.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/commands/**/*.{js,ts,jsx,tsx} : Always call `checkHierarchy(moderator, target)` before executing moderation actions to prevent moderating users with equal or higher roles

Applied to files:

  • src/commands/announce.js
  • src/commands/help.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/commands/**/*.{js,ts,jsx,tsx} : Slash commands must export a `data` property with a SlashCommandBuilder and an async `execute(interaction)` function

Applied to files:

  • src/commands/announce.js
  • src/commands/help.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to tests/**/*.{js,ts,jsx,tsx} : Use Vitest for testing; run `pnpm test` before every commit and `pnpm test:coverage` to verify 80% coverage threshold

Applied to files:

  • tests/commands/help.test.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/**/*.{js,ts,jsx,tsx} : `onConfigChange` callbacks receive `(newValue, oldValue, fullPath, guildId)` as parameters

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/**/*.{js,ts,jsx,tsx} : Use `setConfigValue(path, value, guildId?)` from `src/modules/config.js` to update configuration at runtime

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/**/*.{js,ts,jsx,tsx} : Use `getConfig(guildId?)` from `src/modules/config.js` to read configuration values

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/**/*.{js,ts,jsx,tsx} : Use `safeSend()` utility for all outgoing Discord messages to enforce allowedMentions and prevent mention spam

Applied to files:

  • src/commands/help.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/**/*.{js,ts,jsx,tsx} : Always use Winston for logging via `import { info, warn, error } from '../logger.js'`; never use `console.log`, `console.warn`, `console.error`, or any `console.*` method in src/ files

Applied to files:

  • src/commands/help.js
📚 Learning: 2026-02-26T22:59:10.394Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-26T22:59:10.394Z
Learning: Applies to src/modules/{ai,spam,moderation}.js : Per-request modules (AI, spam, moderation) call `getConfig(interaction.guildId)` on every invocation for automatic config hot-reload; `onConfigChange` listeners provide observability only

Applied to files:

  • src/commands/help.js
🧬 Code graph analysis (3)
src/commands/announce.js (1)
src/utils/safeSend.js (1)
  • safeReply (138-145)
web/src/components/dashboard/config-editor.tsx (1)
web/src/components/ui/card.tsx (3)
  • Card (76-76)
  • CardContent (81-81)
  • CardTitle (79-79)
src/commands/help.js (3)
src/commands/announce.js (2)
  • config (141-141)
  • config (350-350)
src/modules/config.js (1)
  • getConfig (282-313)
src/utils/safeSend.js (1)
  • safeReply (138-145)
🔇 Additional comments (5)
src/api/utils/configAllowlist.js (1)

16-20: Allowlist expansion looks correct.

These keys are correctly added to SAFE_CONFIG_KEYS, enabling API read/write for the new community feature toggles.

src/commands/help.js (1)

328-335: Feature gate placement is correct.

Nice early guard before deferReply, with safeReply and ephemeral feedback for disabled /help.

src/commands/announce.js (1)

143-149: /announce gate is implemented cleanly.

Early return avoids unnecessary permission checks and DB access when the feature is disabled.

web/src/components/dashboard/config-editor.tsx (2)

51-51: Type guard update is aligned with new config schema.

Including help/announce/snippet/poll/tldr in known sections is the right compatibility update for partial API responses.


1191-1224: Community feature toggle card is a solid addition.

The UI wiring to draftConfig[feature].enabled matches the new per-guild feature-flag model.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 27, 2026
…gles

- /help and /announce now check config.help.enabled / config.announce.enabled
- /snippet, /poll, /tldr already have enabled checks from their PRs
- Added help, announce, snippet, poll, tldr to SAFE_CONFIG_KEYS (API writable)
- Added Community Features card to web config editor with toggle for each
- config.json includes all 5 new sections (disabled by default = opt-in)
@BillChirico BillChirico force-pushed the feat/command-config-gates branch from c7ad31b to 69ef675 Compare February 27, 2026 13:06
@BillChirico BillChirico merged commit 6e1e0cd into main Feb 27, 2026
5 of 6 checks passed
@BillChirico BillChirico deleted the feat/command-config-gates branch February 27, 2026 13:06
}));
vi.mock('../../src/modules/config.js', () => ({
getConfig: vi.fn().mockReturnValue({}),
getConfig: vi.fn().mockReturnValue({ help: { enabled: true } }),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning: No test for the help.enabled === false path

The mock was updated to return enabled: true so existing tests pass, but there's no test verifying that the command returns early with the "not enabled" message when help.enabled is false. This is the main feature of this PR — it should be tested.

Add a test like:

it('should reject when help is disabled', async () => {
  const { getConfig } = await import('../../src/modules/config.js');
  getConfig.mockReturnValueOnce({ help: { enabled: false } });
  const interaction = createInteraction('view');
  await execute(interaction);
  expect(interaction.reply).toHaveBeenCalledWith(
    expect.objectContaining({
      content: expect.stringContaining('not enabled'),
      ephemeral: true,
    }),
  );
  expect(interaction.deferReply).not.toHaveBeenCalled();
});


vi.mock('../../src/modules/config.js', () => ({
getConfig: vi.fn().mockReturnValue({
announce: { enabled: true },
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning: No test for the announce.enabled === false path

Same as help.test.js — the mock now returns enabled: true but there's no test verifying the gate rejects when announce.enabled is false.

Add a test like:

it('should reject when announce is disabled', async () => {
  const { getConfig } = await import('../../src/modules/config.js');
  getConfig.mockReturnValueOnce({
    announce: { enabled: false },
    permissions: { enabled: true, adminRoleId: null, usePermissions: true },
  });
  const interaction = createMockInteraction('list');
  await execute(interaction);
  expect(interaction.reply).toHaveBeenCalledWith(
    expect.objectContaining({
      content: expect.stringContaining('not enabled'),
      ephemeral: true,
    }),
  );
  expect(interaction.deferReply).not.toHaveBeenCalled();
});

*/
export async function execute(interaction) {
const config = getConfig(interaction.guildId);
if (!config.help?.enabled) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning: autocomplete() (line 371) not gated behind config.help.enabled

When help.enabled is false, autocomplete() still queries the DB and returns topic suggestions. Users see autocomplete results, submit the command, and then get "not enabled" — confusing UX and a wasted DB query.

Add the same gate at the top of autocomplete():

export async function autocomplete(interaction) {
  const config = getConfig(interaction.guildId);
  if (!config.help?.enabled) {
    return await interaction.respond([]);
  }
  // ... existing logic
}

"poll": {
"enabled": false
},
"tldr": {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning: New config sections not documented in README.md

Per AGENTS.md: "Document new config sections and keys in README.md's config reference when updating config.json."

The help, announce, snippet, poll, and tldr sections need entries in the README config reference. Each needs at minimum the enabled boolean documented, and tldr also needs defaultMessages, maxMessages, and cooldownSeconds.


🔵 Nitpick: Missing trailing newline — File doesn't end with a newline character.

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found (0 critical, 3 warnings, 1 nitpick). See inline comments for details.

Warnings:

  1. Missing test for help.enabled === false path (tests/commands/help.test.js)
  2. Missing test for announce.enabled === false path (tests/commands/announce.test.js)
  3. autocomplete() not gated behind config.help.enabled (src/commands/help.js:371)
  4. New config sections not documented in README.md config reference (config.json)

Nitpick:
5. Missing trailing newline in config.json

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

}));
vi.mock('../../src/modules/config.js', () => ({
getConfig: vi.fn().mockReturnValue({}),
getConfig: vi.fn().mockReturnValue({ help: { enabled: true } }),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding test case for when help.enabled is false to verify the gate check works

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/commands/help.test.js
Line: 13

Comment:
consider adding test case for when `help.enabled` is false to verify the gate check works

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.


vi.mock('../../src/modules/config.js', () => ({
getConfig: vi.fn().mockReturnValue({
announce: { enabled: true },
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding test case for when announce.enabled is false to verify the gate check works

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/commands/announce.test.js
Line: 16

Comment:
consider adding test case for when `announce.enabled` is false to verify the gate check works

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Additional Comments (4)

src/commands/snippet.js
missing config.snippet?.enabled gate check — config and UI claim this command is toggleable but it will work regardless of the setting

export async function execute(interaction) {
  const config = getConfig(interaction.guildId);
  if (!config.snippet?.enabled) {
    await safeReply(interaction, {
      content: '❌ The /snippet command is not enabled on this server.',
      ephemeral: true,
    });
    return;
  }

  const subcommand = interaction.options.getSubcommand();
  await interaction.deferReply({ ephemeral: subcommand !== 'get' });
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/snippet.js
Line: 349-351

Comment:
missing `config.snippet?.enabled` gate check — config and UI claim this command is toggleable but it will work regardless of the setting

```suggestion
export async function execute(interaction) {
  const config = getConfig(interaction.guildId);
  if (!config.snippet?.enabled) {
    await safeReply(interaction, {
      content: '❌ The /snippet command is not enabled on this server.',
      ephemeral: true,
    });
    return;
  }

  const subcommand = interaction.options.getSubcommand();
  await interaction.deferReply({ ephemeral: subcommand !== 'get' });
```

How can I resolve this? If you propose a fix, please make it concise.

src/commands/poll.js
missing config.poll?.enabled gate check — config and UI claim this command is toggleable but it will work regardless of the setting

export async function execute(interaction) {
  const config = getConfig(interaction.guildId);
  if (!config.poll?.enabled) {
    await safeReply(interaction, {
      content: '❌ The /poll command is not enabled on this server.',
      ephemeral: true,
    });
    return;
  }

  await interaction.deferReply({ ephemeral: true });

  const pool = getPool();
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/poll.js
Line: 61-69

Comment:
missing `config.poll?.enabled` gate check — config and UI claim this command is toggleable but it will work regardless of the setting

```suggestion
export async function execute(interaction) {
  const config = getConfig(interaction.guildId);
  if (!config.poll?.enabled) {
    await safeReply(interaction, {
      content: '❌ The /poll command is not enabled on this server.',
      ephemeral: true,
    });
    return;
  }

  await interaction.deferReply({ ephemeral: true });

  const pool = getPool();
```

How can I resolve this? If you propose a fix, please make it concise.

src/commands/snippet.js
add safeReply to import for the enabled gate check

import { safeEditReply, safeReply } from '../utils/safeSend.js';
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/snippet.js
Line: 13

Comment:
add `safeReply` to import for the enabled gate check

```suggestion
import { safeEditReply, safeReply } from '../utils/safeSend.js';
```

How can I resolve this? If you propose a fix, please make it concise.

src/commands/poll.js
add safeReply to import for the enabled gate check

import { safeEditReply, safeReply } from '../utils/safeSend.js';
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/poll.js
Line: 14

Comment:
add `safeReply` to import for the enabled gate check

```suggestion
import { safeEditReply, safeReply } from '../utils/safeSend.js';
```

How can I resolve this? If you propose a fix, please make it concise.

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.

2 participants