This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ReplyQueue is a Chrome extension (Manifest V3) that helps content creators identify social media posts relevant to their blog content and provides AI-powered reply suggestions. It extracts posts from LinkedIn feeds, matches them against RSS feed content using keyword and AI-based semantic matching, and generates contextual replies.
pnpm dev # Start dev server with hot reload
pnpm build # Production build (runs vue-tsc first)
pnpm test # Run all tests once
pnpm test:watch # Run tests in watch mode
pnpm lint # Run ESLint (includes security rules)
pnpm audit:check # Scan dependencies for vulnerabilitiesLoading the extension in Chrome:
- Run
pnpm build - Open
chrome://extensions - Enable "Developer mode"
- Click "Load unpacked" and select the
dist/directory
Three isolated contexts communicate via message passing:
- Content Script (
src/content/) - Runs on LinkedIn pages, extracts posts from DOM using MutationObserver - Background Service Worker (
src/background/) - Orchestrates RSS fetching, matching logic, and AI API calls - Side Panel UI (
src/sidepanel/) - Vue 3 app displaying matched posts and reply suggestions
src/platforms/ implements a cross-platform adapter architecture for adding new social platforms:
types.ts-PlatformAdapterinterface defining extraction contractindex.ts- Registry mapping URLs to platform adapterslinkedin/- LinkedIn-specific DOM extraction with CSS selectors_template/- Boilerplate for new platforms
Each adapter implements: extractPost(), getPostUrl(), scrollToPost(), isFeedPage()
src/shared/messages.ts defines typed messages for cross-context communication:
- Content → Background:
POSTS_EXTRACTED,CONTENT_SCRIPT_READY - Side Panel ↔ Background:
FETCH_RSS,AI_MATCH_POSTS,GENERATE_SUGGESTIONS,RESET_EXTENSION - Background → Content:
SCROLL_TO_POST,EXTRACT_POSTS
src/sidepanel/composables/ extracts business logic into reusable composables:
useAppState.ts- Global app state (loading, errors)useClipboard.ts- Copy-to-clipboard functionalityuseConfig.ts- Extension configuration persistenceuseCreditsModal.ts- Insufficient credits modal stateuseModels.ts- OpenRouter model selection and filteringuseNetworkStatus.ts- Online/offline detectionusePosts.ts- Matched posts state with filteringuseSettingsView.ts- Settings page state managementuseSetup.ts- Onboarding flow logicuseTabStatus.ts- Content script connection statususeTheme.ts- Dark/light/system theme managementuseToast.ts- Toast notification system
- chrome.storage.sync - Small config synced across devices (config, cachedModels, exampleComments if <8KB)
- chrome.storage.local - Larger local data (extractedPosts, matchedPostsWithScore, cachedRssFeed, aiMatchCache, evaluatedPostIds)
- API keys - Always stored in local storage only (never synced for security)
- Fallback - If sync storage fails, automatically falls back to local storage
Tests in tests/ mirror src/ structure. Chrome APIs are mocked in tests/setup.ts.
pnpm test # Run all tests
pnpm test:watch # Watch mode
pnpm vitest tests/background/ # Run specific directory
pnpm vitest -t "keyword matching" # Run tests matching patternTypeScript:
- Strict mode enabled
- Prefer
interfaceovertypefor objects - Explicit return types on exported functions
- Use
unknownnotany
Vue:
- Composition API with
<script setup> - Extract logic to composables
- Single-purpose components
Naming:
- Files: kebab-case (
post-card.vue) - Components: PascalCase (
PostCard) - Composables:
useprefix (useConfig) - Constants: SCREAMING_SNAKE_CASE
Formatting:
- 2 spaces, single quotes, no semicolons
- Trailing commas in multiline
import { something } from '@/path' // → src/path
import { shared } from '@shared/types' // → src/shared/typesmanifest.json- Chrome extension manifestsrc/shared/constants.ts- Default config values (thresholds, limits)src/shared/types.ts- Core TypeScript interfacessrc/background/matcher.ts- Keyword + AI matching logicsrc/background/openrouter.ts- AI API client
IMPORTANT: After every feature implementation or bug fix, you MUST run pnpm build. Chrome loads the extension from the dist/ folder, so changes are not reflected until a build is run. The user will be testing the extension manually after each change.
After implementing any plan, always run preflight checks:
pnpm build # Type check + build (REQUIRED after every change)
pnpm lint:fix # Auto-fix lint issues
pnpm format # Auto-fix formatting
pnpm test # Run tests
pnpm audit:check # Check for vulnerable dependenciesFix any errors before considering the implementation complete.
After preflight checks pass, use the AskUserQuestion tool to ask how to handle versioning:
- Current version - Add the feature/fix to the existing version section in
CHANGELOG.md - New version - Increment version in
manifest.jsonandpackage.json, then add a new section inCHANGELOG.md - Skip - Do nothing.
When incrementing versions:
- Patch (x.x.X): Bug fixes, minor improvements
- Minor (x.X.0): New features, non-breaking changes
- Major (X.0.0): Breaking changes
Files to update for new versions:
manifest.json- Updateversionfieldpackage.json- UpdateversionfieldCHANGELOG.md- Add new version section with date, update comparison links at bottom
The project uses three layers of security scanning:
-
eslint-plugin-security - Integrated into ESLint, runs on every
pnpm lint- Detects eval, non-literal RegExp, child_process usage, unsafe regex patterns
detect-object-injectiondisabled (too many false positives with TS)
-
pnpm audit - Dependency vulnerability scanning via
pnpm audit:check- Fails on moderate+ severity vulnerabilities
- Run before releases or when updating dependencies
-
Semgrep - SAST in GitHub Actions CI (
.github/workflows/security.yml)- Runs on push/PR to main
- Uses
auto,p/javascript,p/typescriptrulesets - Two rules excluded as false positives (see workflow comments)
- Origin validation:
src/background/index.tsvalidates content script origins againstALLOWED_CONTENT_SCRIPT_ORIGINS - Input validation:
src/shared/validation.tssanitizes all user inputs - Secure storage: API keys in
chrome.storage.local, never in sync storage or exposed to content scripts - No dynamic code: ESLint blocks eval/Function patterns
docker run --rm -v "$(pwd):/src" semgrep/semgrep semgrep scan --config auto --config p/javascript --config p/typescript /src