|
| 1 | +# Discover Before Implement: Find Existing Patterns First |
| 2 | + |
| 3 | +Before implementing ANY new feature, search for existing utilities, constants, and templates in the codebase. Duplicating content that already exists is the most common source of architectural drift. |
| 4 | + |
| 5 | +## Key Locations to Check in This Codebase |
| 6 | + |
| 7 | +### Project Root Utilities |
| 8 | +- **`cli/src/project-files.ts`** — Use `getProjectRoot()` (NOT `process.cwd()`) to get the user's project directory. `setProjectRoot()` sets it at startup. |
| 9 | +- **`cli/src/utils/analytics.ts`** — Use `trackEvent(AnalyticsEvent.X, {...})` to track user actions. Constants are in `common/src/constants/analytics-events.ts`. |
| 10 | + |
| 11 | +### Template Files (DO NOT duplicate as strings) |
| 12 | +Template files that users receive when scaffolding live in: |
| 13 | +``` |
| 14 | +common/src/templates/initial-agents-dir/ |
| 15 | + types/agent-definition.ts ← import with Bun text import |
| 16 | + types/tools.ts ← import with Bun text import |
| 17 | + types/util-types.ts ← import with Bun text import |
| 18 | + my-custom-agent.ts |
| 19 | + package.json |
| 20 | +``` |
| 21 | + |
| 22 | +Import them as text (Bun-specific, requires `@ts-expect-error`): |
| 23 | +```typescript |
| 24 | +// @ts-expect-error - Bun text import attribute not supported by TypeScript |
| 25 | +import agentDefinitionSource from '../../../common/src/templates/initial-agents-dir/types/agent-definition' with { type: 'text' } |
| 26 | +``` |
| 27 | + |
| 28 | +### Named Constants |
| 29 | +- **Knowledge file name**: `PRIMARY_KNOWLEDGE_FILE_NAME` from `@codebuff/common/constants/knowledge` — use this, don't hardcode `'knowledge.md'` |
| 30 | +- **Brand name**: `IS_FREEBUFF` from `cli/src/utils/constants` → use `const brandName = IS_FREEBUFF ? 'Freebuff' : 'Codebuff'` |
| 31 | + |
| 32 | +## CLI Command Pattern |
| 33 | + |
| 34 | +When a command produces system messages (not sending to the AI), the handler returns `{ postUserMessage }` and the command-registry calls `params.sendMessage({ content, agentMode, postUserMessage })`: |
| 35 | + |
| 36 | +```typescript |
| 37 | +// In command-registry.ts: |
| 38 | +defineCommand({ |
| 39 | + name: 'init', |
| 40 | + handler: async (params) => { |
| 41 | + const { postUserMessage } = handleInitializationFlowLocally() |
| 42 | + // Handle streaming/queue state check... |
| 43 | + params.sendMessage({ |
| 44 | + content: trimmed, |
| 45 | + agentMode: params.agentMode, |
| 46 | + postUserMessage, // ← injected into message, NOT setMessages directly |
| 47 | + }) |
| 48 | + }, |
| 49 | +}) |
| 50 | +``` |
| 51 | + |
| 52 | +For commands that only show system messages (no AI response), use `params.setMessages`: |
| 53 | +```typescript |
| 54 | +params.setMessages((prev) => postUserMessage(prev)) |
| 55 | +``` |
| 56 | + |
| 57 | +## The postUserMessage Contract |
| 58 | + |
| 59 | +Handlers that produce UI messages return this shape (from `cli/src/types/contracts/send-message.ts`): |
| 60 | +```typescript |
| 61 | +type PostUserMessageFn = (prev: ChatMessage[]) => ChatMessage[] |
| 62 | +// Return: { postUserMessage: PostUserMessageFn } |
| 63 | +``` |
| 64 | +
|
| 65 | +Use `getSystemMessage(text)` from `cli/src/utils/message-history.ts` to create each message. |
| 66 | +
|
| 67 | +## Checklist Before Writing New Code |
| 68 | +
|
| 69 | +1. **Is there a constant for this?** Search `common/src/constants/` first |
| 70 | +2. **Is there a utility for this path operation?** Check `cli/src/project-files.ts` |
| 71 | +3. **Does a template file already exist?** Check `common/src/templates/` |
| 72 | +4. **Should I track analytics?** Most user-facing actions should call `trackEvent()` |
| 73 | +5. **What is the naming convention?** Look at 2-3 existing similar handlers (e.g., `handleHelpCommand`, `handleUsageCommand`) before naming your function |
| 74 | +
|
| 75 | +## Anti-patterns |
| 76 | +
|
| 77 | +**DON'T** hardcode template content as string literals: |
| 78 | +```typescript |
| 79 | +// BAD - duplicates content that exists in common/src/templates/ |
| 80 | +const TYPES_AGENT_DEFINITION = `export interface AgentDefinition { ... }` |
| 81 | +``` |
| 82 | + |
| 83 | +**DO** import from the canonical template location: |
| 84 | +```typescript |
| 85 | +// GOOD |
| 86 | +import agentDefinitionSource from '../../../common/src/templates/initial-agents-dir/types/agent-definition' with { type: 'text' } |
| 87 | +``` |
| 88 | + |
| 89 | +**DON'T** use `process.cwd()` in CLI commands: |
| 90 | +```typescript |
| 91 | +// BAD |
| 92 | +const projectRoot = process.cwd() |
| 93 | +``` |
| 94 | + |
| 95 | +**DO** use the project-files utility: |
| 96 | +```typescript |
| 97 | +// GOOD |
| 98 | +import { getProjectRoot } from '../project-files' |
| 99 | +const projectRoot = getProjectRoot() |
| 100 | +``` |
0 commit comments