Architecture Module Structure - aku11i/phantom GitHub Wiki

Module Structure

This document details Phantom's code organization, module dependencies, and architectural boundaries.

Directory Structure Overview

Source: CLAUDE.md#L17-L42

phantom/
├── src/                    # Source code root
│   ├── bin/               # Entry points
│   │   └── phantom.ts     # Main CLI executable
│   ├── cli/               # CLI-specific layer
│   │   ├── handlers/      # Command implementations
│   │   ├── output.ts      # Console formatting
│   │   └── errors.ts      # Error handling
│   └── core/              # Business logic layer
│       ├── worktree/      # Worktree operations
│       ├── git/           # Git abstractions
│       ├── process/       # Process execution
│       ├── paths.ts       # Path management
│       ├── version.ts     # Version info
│       └── types/         # Type definitions
├── dist/                  # Build output
├── docs/                  # Documentation
└── wiki/                  # GitHub Wiki content

Module Dependency Graph

graph TB
    subgraph "Entry Layer"
        bin[bin/phantom.ts]
    end
    
    subgraph "CLI Layer"
        handlers[cli/handlers/*]
        output[cli/output.ts]
        errors[cli/errors.ts]
    end
    
    subgraph "Core Layer - Domain"
        worktree[core/worktree/*]
        version[core/version.ts]
        paths[core/paths.ts]
    end
    
    subgraph "Core Layer - Infrastructure"
        git[core/git/*]
        process[core/process/*]
        types[core/types/*]
    end
    
    bin --> handlers
    handlers --> output
    handlers --> errors
    handlers --> worktree
    handlers --> process
    handlers --> version
    
    worktree --> git
    worktree --> paths
    worktree --> types
    
    process --> types
    git --> types
    paths --> types
    
    classDef entry fill:#f9f,stroke:#333,stroke-width:2px
    classDef cli fill:#9ff,stroke:#333,stroke-width:2px
    classDef domain fill:#ff9,stroke:#333,stroke-width:2px
    classDef infra fill:#9f9,stroke:#333,stroke-width:2px
    
    class bin entry
    class handlers,output,errors cli
    class worktree,version,paths domain
    class git,process,types infra
Loading

Layer Architecture

1. Entry Layer (src/bin/)

Purpose: Application entry points

Files:

  • phantom.ts - Main CLI entry point

Responsibilities:

  • Bootstrap the application
  • Parse command-line arguments
  • Route to appropriate handlers
  • Handle uncaught errors

Dependencies:

  • Imports from CLI layer only
  • No direct core imports

2. CLI Layer (src/cli/)

Purpose: User interface and interaction

Command Handlers (src/cli/handlers/)

Source: src/cli/handlers/

Files:

  • create.ts - Create phantom command
  • delete.ts - Delete phantom command
  • list.ts - List phantoms command
  • exec.ts - Execute command in phantom
  • shell.ts - Interactive shell command
  • where.ts - Get phantom path command
  • version.ts - Show version command
  • attach.ts - Attach existing worktree

Responsibilities:

  • Orchestrate core operations
  • Handle user input validation
  • Format output messages
  • Manage exit codes

Output Module (src/cli/output.ts)

Source: src/cli/output.ts

Responsibilities:

  • Console output formatting
  • Table rendering
  • Color management
  • Progress indicators

Key Functions:

  • formatTable() - Render data as table
  • formatError() - Format error messages
  • formatSuccess() - Format success messages

Error Handler (src/cli/errors.ts)

Source: src/cli/errors.ts

Responsibilities:

  • Map errors to exit codes
  • Format error messages
  • Handle unexpected errors

3. Core Layer (src/core/)

Purpose: Business logic and domain operations

Worktree Module (src/core/worktree/)

Source: src/core/worktree/

Files:

  • create.ts - Worktree creation logic
  • delete.ts - Worktree deletion logic
  • list.ts - List worktrees logic
  • where.ts - Locate worktree logic
  • attach.ts - Attach existing worktree
  • validate.ts - Name validation
  • errors.ts - Domain-specific errors

Core Types:

interface WorktreeInfo {
  name: string;
  path: string;
  branch: string;
  isMain: boolean;
}

Dependencies:

  • Uses Git module for operations
  • Uses Paths module for locations
  • Returns Result types

Git Module (src/core/git/)

Source: src/core/git/

Structure:

git/
├── executor.ts          # Main Git command executor
└── libs/               # Git operation wrappers
    ├── add-worktree.ts
    ├── attach-worktree.ts
    ├── branch-exists.ts
    ├── get-current-branch.ts
    ├── get-git-root.ts
    └── list-worktrees.ts

Git Executor:

class GitExecutor {
  execute(args: string[]): Result<string>;
  executeJson<T>(args: string[]): Result<T>;
}

Git Libraries: Each library wraps specific Git operations:

  • addWorktree() - Create new worktree
  • getCurrentBranch() - Get active branch
  • getGitRoot() - Find repository root
  • listWorktrees() - List all worktrees

Process Module (src/core/process/)

Source: src/core/process/

Files:

  • spawn.ts - Core process spawning
  • exec.ts - Command execution
  • shell.ts - Interactive shells
  • errors.ts - Process errors

Key Types:

interface SpawnOptions {
  cwd?: string;
  env?: Record<string, string>;
  stdio?: StdioOptions;
}

Responsibilities:

  • Safe process spawning
  • I/O stream management
  • Signal handling
  • Error propagation

Path Management (src/core/paths.ts)

Source: src/core/paths.ts

Functions:

  • getWorktreeBasePath() - Base directory for phantoms
  • getWorktreePath() - Full path to phantom
  • ensureWorktreeDirectory() - Create directories

Path Structure:

/tmp/phantom/
└── <repo-name>/
    ├── <phantom-1>/
    ├── <phantom-2>/
    └── <phantom-3>/

Types Module (src/core/types/)

Source: src/core/types/

Files:

  • result.ts - Result type implementation

Result Type:

type Result<T, E = Error> = 
  | { ok: true; value: T }
  | { ok: false; error: E }

namespace Result {
  function ok<T>(value: T): Result<T>;
  function error<E>(error: E): Result<never, E>;
}

Module Communication Patterns

1. Command Flow

sequenceDiagram
    participant CLI
    participant Handler
    participant Core
    participant Git
    
    CLI->>Handler: Command + Args
    Handler->>Core: Business Operation
    Core->>Git: Git Command
    Git-->>Core: Result<Data>
    Core-->>Handler: Result<DomainObject>
    Handler->>CLI: Format Output
Loading

2. Error Flow

graph LR
    A[Git Error] --> B[Result.error]
    B --> C[Core Layer]
    C --> D[Domain Error]
    D --> E[Handler]
    E --> F[User Message]
    
    style A fill:#f99
    style F fill:#9f9
Loading

3. Data Transformation

Each layer transforms data appropriately:

  1. Git Layer: Raw strings → Parsed data
  2. Core Layer: Parsed data → Domain objects
  3. CLI Layer: Domain objects → User output

Module Boundaries and Rules

1. Dependency Rules

  • Downward Only: Higher layers depend on lower layers
  • No Upward: Core never imports from CLI
  • No Circular: Enforced by structure

2. Interface Boundaries

Each module exposes a clear interface:

// Good: Clear module interface
export function createWorktree(
  name: string, 
  branch?: string
): Result<WorktreeInfo>

// Bad: Exposing internals
export function executeGitCommand(
  args: string[]
): ChildProcess  // Don't expose Node.js types

3. Type Boundaries

  • Domain types in core layer
  • CLI types stay in CLI layer
  • Shared types in types/ module

Testing Structure

Test files are co-located with source:

src/
├── core/
│   ├── worktree/
│   │   ├── create.ts
│   │   ├── create.test.ts    # Unit tests
│   │   ├── delete.ts
│   │   └── delete.test.ts
│   └── git/
│       ├── executor.ts
│       └── executor.test.ts

Test Patterns:

  • Mock external dependencies
  • Test pure functions directly
  • Integration tests for workflows

Module Responsibilities

Clear Separation

Module Responsible For Not Responsible For
CLI Handlers User interaction Business logic
Core Worktree Phantom lifecycle Git commands
Git Module Git execution Business rules
Process Module Process spawning Command logic
Types Module Shared types Implementation

Single Responsibility Examples

Good - Single Responsibility:

// validate.ts - Only validation
export function validateWorktreeName(name: string): Result<void>

// create.ts - Only creation
export function createWorktree(name: string): Result<WorktreeInfo>

Bad - Multiple Responsibilities:

// Don't mix concerns
export function createAndValidateWorktree(
  name: string,
  showOutput: boolean  // UI concern in core!
): Result<WorktreeInfo>

Import Guidelines

1. Import Order

// 1. Node.js built-ins
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";

// 2. External dependencies (none in runtime)

// 3. Internal - absolute paths
import { Result } from "../types/result.js";

// 4. Internal - relative paths
import { validateWorktreeName } from "./validate.js";

2. Barrel Exports

Avoid barrel exports (index.ts) to:

  • Keep imports explicit
  • Improve tree shaking
  • Reduce circular dependencies

3. File Extensions

Always include .js extension:

// Correct - ESM requires extensions
import { createWorktree } from "./create.js";

// Wrong - Will fail at runtime
import { createWorktree } from "./create";

Future Module Considerations

Potential New Modules

  1. Config Module

    • User preferences
    • Repository settings
    • Default values
  2. Plugin Module

    • Plugin loading
    • Hook management
    • API surface
  3. Telemetry Module

    • Usage metrics
    • Performance tracking
    • Error reporting

Module Principles

When adding new modules:

  1. Single Purpose: One clear responsibility
  2. Clear Interface: Well-defined public API
  3. Testable: Pure functions where possible
  4. Documented: Clear module documentation
  5. Consistent: Follow existing patterns

Summary

Phantom's module structure provides:

  1. Clear Boundaries: Well-defined layers and responsibilities
  2. Maintainability: Easy to find and modify code
  3. Testability: Isolated modules with clear interfaces
  4. Scalability: New features fit naturally
  5. Type Safety: Strong typing throughout

The modular architecture ensures the codebase remains clean, understandable, and extensible as the project grows.

⚠️ **GitHub.com Fallback** ⚠️