Equicord/AGENTS.md
M 77c40bdd9d
Add AGENTS.md guide for coding agents (#815)
* Add AGENTS.md guide for coding agents

Adds a comprehensive AGENTS.md guide for AI coding agents working on Equicord, based on the agents.md standard format.

Key sections included:
- Project overview and technology stack
- Setup commands (install, build, dev, test)
- Project structure with path aliases
- Code style, patterns, and philosophy
- Plugin structure with clear directory placement (src/equicordplugins/ for Equicord, src/plugins/ for Vencord)
- Forbidden patterns table with fixes
- Discord types and plugin interop
- Patch quality guidelines
- Memory leaks and cleanup
- Built-in utilities reference
- Plugin acceptance rules
- Git workflow (feature branches from fork's dev, PR to upstream dev)
- Platform-specific plugins (.desktop, .web, .discordDesktop)

Also adds CLAUDE.md symlink for Claude compatibility.

Sources:
- Merged content from .gemini/styleguide.md
- Follows agents.md standard format

* Improve AGENTS.md based on Vercel agent evals research

- Add retrieval-led reasoning directive at top
- Add Quick Reference and Key Files Index for fast navigation
- Add explicit workflow ordering (explore first, then act)
- Compact from 518 to 240 lines (54% reduction)
- Convert verbose lists to tables
- Merge related sections
- Remove redundant examples

---------

Co-authored-by: Mishal <91066601+mishalhossin@users.noreply.github.com>
2026-02-20 15:57:08 -05:00

10 KiB

AGENTS.md

IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning. When uncertain about APIs, patterns, or existing implementations, read the relevant source files rather than relying on assumptions.


Quick Reference

Task Command/Location
Install pnpm install --frozen-lockfile
Build pnpm build
Dev/Watch pnpm dev
Lint pnpm lint
Type check pnpm testTsc
Full test pnpm test
New Equicord plugin src/equicordplugins/PluginName/index.ts
New Vencord plugin src/plugins/PluginName/index.ts

Key Files Index:

Need File
Plugin types src/utils/types.ts
Settings API src/api/Settings.ts
Constants/devs src/utils/constants.ts
CSS utilities src/utils/css.ts
Webpack common exports src/webpack/common/*.ts
Example plugins src/plugins/*/index.ts or src/equicordplugins/*/index.ts
Discord types @vencord/discord-types (external)

Project Overview

Equicord is a Discord client mod forked from Vencord. It modifies the Discord web client by patching webpack modules at runtime to inject custom functionality.

Technology Stack: TypeScript (strict), React, Webpack patching Package Manager: pnpm | License: GPL-3.0-or-later


Workflow

Before writing code:

  1. Explore relevant source files to understand existing patterns
  2. Check the Key Files Index for API/type references
  3. Look at similar plugins for examples
  4. Then proceed with implementation

This "explore first, then act" approach produces better results than jumping straight to implementation.


Setup Commands

Command Description
pnpm install --frozen-lockfile Install dependencies
pnpm build Build project
pnpm buildWeb Build for web
pnpm dev / pnpm watch Start dev server (watch mode)
pnpm watchWeb Watch web build
pnpm lint Lint code
pnpm lint:fix Fix lint issues
pnpm lint-styles Lint CSS
pnpm testTsc Type check
pnpm test Full test suite (builds, type checks, lints, generates plugin JSON)
pnpm generatePluginJson Generate plugin list
pnpm generateEquicordPluginJson Generate Equicord plugin list

Requirements: Node.js >= 18, pnpm (see package.json for version)


Code Style

TypeScript: Strict mode. Use inference - don't annotate obvious types.

Formatting: Double quotes, semicolons required, single quotes only for escaping.

Patterns: ?. optional chaining, ?? nullish coalescing (never || for defaults), const over let, arrow functions, destructuring, template literals, early returns, flat code.

Philosophy: Less code is better. No comments unless requested. Delete dead code, never comment it out. No overengineering or premature abstractions. Trust TypeScript inference.

Text: Plugin/setting descriptions: capital first letter, end with period. Error messages: natural human text, no robotic formatting.

Performance: Map/Set for lookups, .find()/.some() not .filter()[0], no spread in loops, Promise.all() for parallel async.

Hygiene: Delete dead code. No unused imports. No logging (console.log, Logger). No empty catch blocks.


Plugin Structure

Place Equicord plugins in src/equicordplugins/PluginName/, upstream/Vencord plugins in src/plugins/PluginName/.

import definePlugin from "@utils/types";
import { EquicordDevs } from "@utils/constants";

export default definePlugin({
    name: "PluginName",            // PascalCase, matches directory name
    description: "Does something.", // Capital first, period at end
    authors: [EquicordDevs.Name],   // EquicordDevs for new, Devs for upstream
});

Settings: Use definePluginSettings from @api/Settings. Reject inline options objects.

Prefer declarative APIs: flux, contextMenus, chatBarButton, messagePopoverButton, managedStyle instead of manual registration.

Reject deprecated fields: renderChatBarButton, renderMessagePopoverButton, options

Lifecycle: Anything registered in start() must be cleaned up in stop().


Forbidden Patterns

Bad Good Reason
value !== null && value !== undefined value or isNonNullish(value) Verbose
array && array.length > 0 array.length Redundant
settings?.store?.value settings.store.value Store always defined
value || defaultValue value ?? defaultValue || falsifies 0, "", false
`${classA} ${classB}` classes(classA, classB) Handles null/false
"vc-plugin-class" hardcoded cl("class") via classNameFactory Typo-proof
console.log/warn/error Remove it No logging
cdn.discordapp.com/... IconUtils.* methods Handles animated, sizing
/api/v9/... Constants.Endpoints.* Endpoints change
@api/Styles @utils/css Deprecated
any for Discord objects Import from @vencord/discord-types Type safety
as unknown as Find correct type Unsafe
React.memo() Remove it Not needed
React.cloneElement/isValidElement/Children Find another approach Forbidden
React.lazy(() => import(...)) LazyComponent Framework-integrated
Empty catch {} Handle error Silent failures
CSS-only plugins Must have logic/patches Not allowed
document.querySelector(...) Use webpack patches DOM forbidden
Vencord.Plugins.plugins["X"] isPluginEnabled + import Proper interop
plugin.started isPluginEnabled(plugin.name) Proper interop
Unexplained magic numbers Named constants Readability
Unused imports Remove them Cleanliness

CSS, React, Settings

CSS: Use classNameFactory from @utils/css: const cl = classNameFactory("vc-my-plugin-"); Combine with classes() from @utils/misc.

React: Return null for conditional rendering, never undefined. Wrap complex components with ErrorBoundary.wrap(). useEffect must return cleanup for subscriptions/timers.

Settings: settings.store.key (reactive), settings.plain.key (non-reactive). Never settings.use() with arrays in variables.


Patch Quality

Patches modify Discord's minified webpack modules. Stability is paramount.

  • One patch per concern. Each replacement does one thing.
  • Surgical. Match only what needs replacing, let find target the module.
  • No hardcoded minified vars. Use \i, never e, t, n, r, i, eD, eH.
  • Bounded gaps. .{0,50} not .+? or .*?.
  • No generic patterns. Add stable anchors.
  • No raw intl hashes. Use #{intl::KEY_NAME}.
  • $& for append/prepend, $self for plugin method calls.

Memory Leaks

Every start() must have matching stop(). Common leaks: event listeners, intervals/timeouts, MutationObservers, context menu patches, chat bar buttons, styles, Flux subscriptions.

Prefer declarative APIs that handle cleanup automatically: flux, contextMenus, chatBarButton, messagePopoverButton, managedStyle.

useEffect must return cleanup for subscriptions, timers, observers, or any persistent resource.


Plugin Interop

  • @equicordplugins/pluginName for Equicord plugins
  • @plugins/pluginName for Vencord/upstream plugins

Use isPluginEnabled(plugin.name) to check if enabled. Never access Vencord.Plugins.plugins["X"] directly.


Platform-Specific Plugins

Directory suffixes: pluginName.desktop/, pluginName.web/, pluginName.discordDesktop/


Built-in Utilities

@utils/misc: classes, sleep, isObject, isObjectEmpty, parseUrl, pluralise, identity @utils/guards: isTruthy, isNonNullish @utils/text: formatDuration, formatDurationMs, humanFriendlyJoin, makeCodeblock, toInlineCode, escapeRegExp @utils/discord: getCurrentChannel, getCurrentGuild, getIntlMessage, openPrivateChannel, insertTextIntoChatInputBox, sendMessage, copyWithToast, openUserProfile, fetchUserProfile, getUniqueUsername, openInviteModal @utils/css: classNameFactory, classNameToSelector @utils/clipboard: copyToClipboard @utils/modal: openModal, closeModal, ModalRoot, ModalHeader, ModalContent, ModalFooter, ModalCloseButton @utils/margins: Margins.top8, .top16, .bottom8, .bottom16 etc. @utils/web: saveFile, chooseFile @utils/lazy: proxyLazy, makeLazy @utils/lazyReact: LazyComponent @utils/react: useAwaiter, useForceUpdater, useTimer @api/DataStore: get, set, del (IndexedDB, async) @api/Commands: sendBotMessage, findOption

@webpack/common: Stores (UserStore, GuildStore, ChannelStore, 30+ more), Actions (RestAPI, FluxDispatcher, MessageActions, NavigationRouter), Utils (Constants.Endpoints, SnowflakeUtils, Parser, PermissionsBits, moment, lodash, IconUtils), Components (Tooltip, TextInput, Select, Avatar, Menu, Popout), Toasts (Toasts, showToast)

@webpack finders: findByPropsLazy, findByCodeLazy, findStoreLazy, findComponentByCodeLazy, findExportedComponentLazy

@components/: ErrorBoundary, Flex, Button, Paragraph, Heading, BaseText, Span, ErrorCard, Link, CodeBlock, FormSwitch


Ethical Guidelines & Acceptance Rules

Not accepted: Fake deafen/mute, trolling plugins, selfbot/API abuse (auto-replies, animated statuses, message pruning, Nitro snipers).

Instant reject: Simple slash-commands, text replacement, DOM manipulation, UI-only hide/redesign, third-party bot targeting, untrusted APIs, user-provided API keys, new dependencies without justification.


Git Workflow

  • Never commit to main (protected)
  • Never commit to fork's dev branch
  • Create feature branch from dev: feature/plugin-name or fix/plugin-name
  • Keep commits atomic
  • Squash commits before merging if addressing same issue
  • Run pnpm lint and pnpm build before committing

PR title format: [PluginName] Description