agent-ecosystem/.planning/codebase/CONVENTIONS.md
matt ae4833e310 feat(planning): add project planning documents and configuration for SSH multi-context workspaces
- Introduced new planning files including PROJECT.md, REQUIREMENTS.md, ROADMAP.md, and STATE.md to outline the vision and requirements for SSH multi-context workspaces.
- Added ARCHITECTURE.md and CONCERNS.md to detail the codebase structure and address technical debt, known bugs, and security considerations.
- Created CONVENTIONS.md to establish coding standards and practices for the project.
- Updated .gitignore to exclude demo files and added configuration for planning tools.

This commit lays the groundwork for enhancing SSH functionality and user experience in managing multiple workspaces.
2026-02-12 09:37:58 +09:00

389 lines
10 KiB
Markdown

# Coding Conventions
**Analysis Date:** 2026-02-12
## Naming Patterns
**Files:**
- Services/Components: PascalCase - `ChunkBuilder.ts`, `ProjectScanner.ts`, `AIChatGroup.tsx`
- Utilities: camelCase - `pathDecoder.ts`, `tokenizer.ts`, `formatters.ts`
- Type definitions: camelCase - `messages.ts`, `chunks.ts`, `data.ts`
- Test files: `*.test.ts` - mirrors source file name
**Functions:**
- Standard functions: camelCase - `encodePath`, `extractProjectName`, `formatDuration`
- Type guards: `isXxx` - `isParsedRealUserMessage`, `isAIChunk`, `isValidEncodedPath`
- Builder functions: `buildXxx` - `buildChunks`, `buildUserChunk`, `buildSessionPath`
- Getter functions: `getXxx` - `getProjectsBasePath`, `getExpandedDisplayItemIds`
- React components: Arrow functions with PascalCase names
**Variables:**
- Standard: camelCase - `chunkBuilder`, `sessionId`, `projectPath`
- Constants: UPPER_SNAKE_CASE - `EMPTY_METRICS`, `HARD_NOISE_TAGS`, `CONFIG_GET`
- React components: PascalCase - `AIChatGroup`, `TokenUsageDisplay`
- Unused parameters: Leading underscore - `_event`, `_arg`
**Types:**
- Interfaces/Types: PascalCase - `ParsedMessage`, `EnhancedChunk`, `SessionMetrics`
- Interfaces: NO "I" prefix (modern convention) - `ToolCall`, not `IToolCall`
- Enum members: PascalCase or UPPER_CASE - `TriggerColor`, `EMPTY_STDOUT`
## Code Style
**Formatting:**
- Tool: Prettier 3.8.1
- Config: `/Users/bskim/claude-devtools/.prettierrc.json`
- Key settings:
- Semi: true (always use semicolons)
- Single quotes: true
- Tab width: 2 spaces
- Trailing comma: es5
- Print width: 100 characters
- Arrow parens: always
- End of line: lf
- Bracket spacing: true
- Bracket same line: false
- Tailwind plugin: `prettier-plugin-tailwindcss` (auto-sorts classes)
**Linting:**
- Tool: ESLint 9.39.2 with typescript-eslint
- Config: `/Users/bskim/claude-devtools/eslint.config.js`
- Key rules:
- TypeScript strict mode enabled
- Type-aware linting via `projectService`
- React + Hooks + A11y rules
- Security plugin for AI-generated code
- SonarJS for code quality
- Module boundaries enforced (main/renderer/preload/shared separation)
- Import sorting via `simple-import-sort`
- No default exports (prefer named exports)
- Explicit function return types (warn)
- Explicit module boundary types (warn)
## Import Organization
**Order (enforced by `simple-import-sort`):**
1. Side effect imports (CSS, styles)
2. Node.js builtins (`node:fs`, `node:path`)
3. React packages (`react`, `react-dom`)
4. External packages (`@?\\w`)
5. Internal aliases (`@/`)
6. Parent imports (`../`)
7. Same-folder imports (`./`)
8. Type imports (last)
**Path Aliases:**
- `@main/*``src/main/*`
- `@renderer/*``src/renderer/*`
- `@shared/*``src/shared/*`
- `@preload/*``src/preload/*`
**Example:**
```typescript
import React, { useCallback, useMemo } from 'react';
import { ChunkBuilder, ProjectScanner } from '@main/services';
import { useStore } from '@renderer/store';
import { formatDuration } from '@shared/utils/formatters';
import { DisplayItemList } from './DisplayItemList';
import type { EnhancedChunk } from '@main/types';
```
**Import Restrictions:**
- No deep relative imports (`../../../`) - use path aliases
- No circular dependencies (max depth: 3)
- Module boundaries enforced:
- Renderer → renderer + shared only
- Main → main + shared only
- Preload → preload + shared only
- Shared → shared + main (for type re-exports)
## Type Conventions
**Type Imports:**
Use `type` modifier for type-only imports:
```typescript
import { type EnhancedChunk, type ParsedMessage } from '@main/types';
```
**Type Exports:**
```typescript
export type { Session, SessionDetail } from './types';
```
**Function Return Types:**
Always specify for exported functions (warn-level enforcement):
```typescript
export function buildChunks(messages: ParsedMessage[]): EnhancedChunk[] {
// ...
}
```
**Type Guards:**
Use discriminated union pattern with type predicates:
```typescript
export function isParsedRealUserMessage(msg: ParsedMessage): boolean {
return msg.type === 'user' && !msg.isMeta && typeof msg.content === 'string';
}
export function isUserChunk(chunk: Chunk | EnhancedChunk): chunk is UserChunk {
return chunk.type === 'user';
}
```
**Barrel Exports:**
Services use barrel exports via `index.ts`:
```typescript
// src/main/services/index.ts
export * from './analysis';
export * from './discovery';
export * from './error';
export * from './infrastructure';
export * from './parsing';
// Usage:
import { ChunkBuilder, ProjectScanner } from '@main/services';
```
Renderer utils/hooks/types do NOT have barrel exports - import directly from files.
## Error Handling
**Main Process:**
```typescript
try {
const result = await somethingRisky();
return result;
} catch (error) {
console.error('Operation failed:', error);
return safeDefault;
}
```
**Renderer:**
```typescript
// Store error state
set({
sessionsError: error instanceof Error ? error.message : 'Operation failed',
sessionsLoading: false,
});
```
**IPC Handlers:**
```typescript
// Validate parameters
if (!isValidProjectId(projectId)) {
throw new Error(`Invalid project ID: ${projectId}`);
}
// Return safe defaults on error
catch (error) {
logger.error('Handler failed:', error);
return [];
}
```
## Logging
**Framework:** Custom logger via `@shared/utils/logger`
**Pattern:**
```typescript
import { createLogger } from '@shared/utils/logger';
const logger = createLogger('Service:ChunkBuilder');
logger.info('Building chunks', { messageCount: messages.length });
logger.warn('Missing subagent', { taskId });
logger.error('Failed to parse', error);
```
**Console Usage:**
- Main process: `console.log/error` allowed (logging is expected)
- Renderer: Avoid console - use logger or store error state
- Tests: Console mocked in setup, errors/warnings cause test failures
## Comments
**When to Comment:**
- Complex business logic (chunk building, semantic step extraction)
- Non-obvious type guards (message classification rules)
- Workarounds or technical debt
- Public API documentation
**JSDoc/TSDoc:**
Used extensively for exported functions and types:
```typescript
/**
* Encodes an absolute path into Claude Code's directory naming format.
* Replaces all path separators (/ and \) with dashes.
*
* @param absolutePath - The absolute path to encode (e.g., "/Users/username/projectname")
* @returns The encoded directory name (e.g., "-Users-username-projectname")
*/
export function encodePath(absolutePath: string): string {
// ...
}
```
**Section Headers:**
Used to organize large files:
```typescript
// =============================================================================
// Chunk Building
// =============================================================================
```
## Function Design
**Size:** Keep focused and composable. Services orchestrate specialized modules.
**Parameters:**
- Use explicit types, no `any` (except in tests)
- Optional parameters last
- Use destructuring for options objects
**Return Values:**
- Always specify return type for exported functions
- Prefer explicit returns over implicit
- Return safe defaults instead of throwing (where appropriate)
**React Components:**
```typescript
// Prefer arrow functions for components
export const AIChatGroup: React.FC<AIChatGroupProps> = ({ aiGroup, userGroup }) => {
// Component implementation
};
```
**Component Props:**
```typescript
interface AIChatGroupProps {
aiGroup: AIGroup;
userGroup?: UserGroup;
onNavigate?: (messageId: string) => void;
}
```
## Module Design
**Exports:**
- Prefer named exports over default exports
- Barrel exports for service domains
- Direct exports for renderer utils/hooks
**File Organization:**
```typescript
// Imports
import { ... } from '...';
// Types/Interfaces
export interface MyInterface { ... }
// Constants
export const MY_CONSTANT = ...;
// Functions
export function myFunction() { ... }
// Classes
export class MyClass { ... }
```
**Services Pattern:**
```typescript
export class ChunkBuilder {
buildChunks(messages: ParsedMessage[]): EnhancedChunk[] {
// Implementation
}
buildSessionDetail(session: Session): SessionDetail {
// Implementation
}
}
```
## React Conventions
**Component Structure:**
```typescript
// Imports
import React, { useCallback, useMemo } from 'react';
// Types
interface ComponentProps { ... }
// Component
export const Component: React.FC<ComponentProps> = ({ prop1, prop2 }) => {
// Hooks first
const store = useStore();
const [state, setState] = useState();
// Memoized values
const computed = useMemo(() => ..., [deps]);
// Callbacks
const handleClick = useCallback(() => ..., [deps]);
// Render
return <div>...</div>;
};
```
**Hooks Rules:**
- Always use exhaustive deps (error-level enforcement)
- Custom hooks start with `use`: `useTabUI`, `useAutoScrollBottom`
- Keep hooks focused and composable
**State Management (Zustand):**
```typescript
// Slice pattern
export interface SessionSlice {
// State
sessions: Session[];
selectedSessionId: string | null;
sessionsLoading: boolean;
sessionsError: string | null;
// Actions
fetchSessions: (projectId: string) => Promise<void>;
selectSession: (id: string) => void;
}
```
## Tailwind CSS
**Use theme-aware classes:**
```tsx
<div className="bg-surface text-text border-border">
<div className="bg-surface-raised text-text-secondary">
```
**Auto-sorted by Prettier plugin:**
Classes automatically ordered by Prettier's Tailwind plugin.
**Custom CSS variables:**
Defined in `src/renderer/index.css` - use via Tailwind classes:
- `--color-surface``bg-surface`
- `--color-text``text-text`
- `--color-border``border-border`
## Security
**Enabled Rules:**
- `security/detect-eval-with-expression`: error
- `security/detect-child-process`: warn
- File system access: allowed (desktop app requirement)
- Dynamic patterns: allowed (intentional in this app)
**Parameter Mutation:**
Prevented via `no-param-reassign` except for:
- `draft` (Immer patterns)
- `acc` (reduce accumulators)
- `state` (Zustand)
---
*Convention analysis: 2026-02-12*