Chrome extension assistant for AI-powered web content extraction with Plasmo framework
AI assistant for developing moe-copy-ai, a Plasmo-based Chrome extension for AI-powered web content extraction with mobile browser support.
1. **Module-First**: Single-responsibility modules; extract shared logic at 2+ uses
2. **Pure Functions**: Isolate side effects (storage, Chrome APIs, AI calls); immutable transforms
3. **No Circular Dependencies**: Flow: `popup/options → components → hooks → utils → constants`
4. **TypeScript Strict**: No `any`; export types with implementations
```bash
pnpm dev # Development server
pnpm build # Production build
pnpm package # Store package
pnpm test # Run all tests
pnpm test:file -- "path" # Test single file
```
**Extractor** (`utils/extractor.ts`)
**AI Service** (`utils/ai-service.ts`)
**Storage** (`utils/storage.ts`)
| Use Case | Pattern |
|----------|---------|
| Local UI state | `useState` |
| Derived values | `useMemo` (not `useEffect`) |
| Chrome sync | `useStorage` (Plasmo) |
| Avoid stale closures | `useRef` + `.current` |
**State Rules:**
**Performance Rules:**
**Always:**
Example:
```typescript
const { t } = useI18n()
const message = t('error.apiKeyNotSet')
```
1. **Fake Timers**: Use `vi.useFakeTimers()` + `vi.setSystemTime()` for time-dependent tests (never real delays)
2. **Shared Mocks**: Place reusable mocks in `utils/__tests__/mocks/` to avoid duplication
3. **Test Isolation**: Always `resetMockStorage()` in `beforeEach` for clean state
4. **Mock Minimally**: Only mock external boundaries (storage, network); test real logic
```
utils/__tests__/
├── mocks/
│ ├── index.ts # Barrel export for all mocks
│ └── plasmo-storage.ts # Shared Plasmo storage mock
├── ai-service.browser.ts
└── ...
```
```typescript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { createPlasmoStorageMock, resetMockStorage } from "./mocks"
vi.mock("@plasmohq/storage", () => createPlasmoStorageMock())
describe("FeatureName", () => {
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(new Date("2024-01-01T00:00:00.000Z"))
resetMockStorage()
})
afterEach(() => {
vi.clearAllMocks()
vi.useRealTimers()
})
it("describes expected behavior", async () => {
// Arrange
// Act
// Assert
})
})
```
| ❌ Don't | ✅ Do Instead |
|----------|--------------|
| `await new Promise(r => setTimeout(r, N))` | `vi.useFakeTimers()` + `vi.setSystemTime()` |
| `(obj as unknown as MockType).clear()` | Use typed mock utilities with `resetMockStorage()` |
| Duplicate mock definitions | Import from `./mocks` |
| Test implementation details | Test behavior and public APIs |
| Mock everything | Only mock external boundaries |
**Important**: The Plasmo storage mock uses shared stores that persist across `new Storage()` calls. Always call `resetMockStorage()` in `beforeEach` for test isolation.
1. **Async Operations**: Always handle promises properly with try/catch
2. **Event Listeners**: Clean up listeners in unmount/cleanup functions
3. **Error Checking**: Check `chrome.runtime.lastError` after Chrome API calls
4. **Message Passing**: Validate messages between popup/background/content scripts
5. **Storage**: Use Chrome sync storage for settings, local for temporary data
6. **Permissions**: Request minimum necessary permissions
1. Understand existing architecture first
2. Check for existing utilities/hooks to reuse
3. Follow module-first and pure function principles
4. Update both Chinese and English i18n files
5. Add tests for new functionality
6. Verify Chrome extension functionality in browser
1. Plan architecture: components → hooks → utils
2. Identify shared logic to extract
3. Implement with minimal dependencies
4. Add comprehensive tests
5. Update CLAUDE.md if architectural changes
1. Add failing test that reproduces bug
2. Fix minimal code required
3. Verify test passes
4. Check for similar bugs in codebase
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/moe-copy-ai-development-assistant/raw