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

10 KiB

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:

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:

import { type EnhancedChunk, type ParsedMessage } from '@main/types';

Type Exports:

export type { Session, SessionDetail } from './types';

Function Return Types: Always specify for exported functions (warn-level enforcement):

export function buildChunks(messages: ParsedMessage[]): EnhancedChunk[] {
  // ...
}

Type Guards: Use discriminated union pattern with type predicates:

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:

// 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:

try {
  const result = await somethingRisky();
  return result;
} catch (error) {
  console.error('Operation failed:', error);
  return safeDefault;
}

Renderer:

// Store error state
set({
  sessionsError: error instanceof Error ? error.message : 'Operation failed',
  sessionsLoading: false,
});

IPC Handlers:

// 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:

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:

/**
 * 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:

// =============================================================================
// 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:

// Prefer arrow functions for components
export const AIChatGroup: React.FC<AIChatGroupProps> = ({ aiGroup, userGroup }) => {
  // Component implementation
};

Component Props:

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:

// Imports
import { ... } from '...';

// Types/Interfaces
export interface MyInterface { ... }

// Constants
export const MY_CONSTANT = ...;

// Functions
export function myFunction() { ... }

// Classes
export class MyClass { ... }

Services Pattern:

export class ChunkBuilder {
  buildChunks(messages: ParsedMessage[]): EnhancedChunk[] {
    // Implementation
  }

  buildSessionDetail(session: Session): SessionDetail {
    // Implementation
  }
}

React Conventions

Component Structure:

// 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):

// 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:

<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-surfacebg-surface
  • --color-texttext-text
  • --color-borderborder-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