Files
automaker/docs/llm-shared-packages.md
SuperComboGamer 584f5a3426 Merge main into massive-terminal-upgrade
Resolves merge conflicts:
- apps/server/src/routes/terminal/common.ts: Keep randomBytes import, use @automaker/utils for createLogger
- apps/ui/eslint.config.mjs: Use main's explicit globals list with XMLHttpRequest and MediaQueryListEvent additions
- apps/ui/src/components/views/terminal-view.tsx: Keep our terminal improvements (killAllSessions, beforeunload, better error handling)
- apps/ui/src/config/terminal-themes.ts: Keep our search highlight colors for all themes
- apps/ui/src/store/app-store.ts: Keep our terminal settings persistence improvements (merge function)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:27:44 -05:00

13 KiB

AutoMaker Shared Packages - LLM Guide

This guide helps AI assistants understand how to use AutoMaker's shared packages effectively.

Package Overview

AutoMaker uses a monorepo structure with shared packages in libs/:

libs/
├── types/              # Type definitions (no dependencies)
├── utils/              # Utility functions
├── prompts/            # AI prompt templates
├── platform/           # Platform utilities
├── model-resolver/     # Claude model resolution
├── dependency-resolver/# Feature dependency resolution
└── git-utils/          # Git operations

When to Use Each Package

@automaker/types

Use when: You need type definitions for any AutoMaker concept.

Import for:

  • Feature - Feature interface with all properties
  • ExecuteOptions - Claude agent execution options
  • ConversationMessage - Chat message format
  • ErrorType, ErrorInfo - Error handling types
  • CLAUDE_MODEL_MAP - Model alias to ID mapping
  • DEFAULT_MODELS - Default model configurations

Example:

import type { Feature, ExecuteOptions } from '@automaker/types';

Never import from: services/feature-loader, providers/types

@automaker/utils

Use when: You need common utilities like logging, error handling, or image processing.

Import for:

  • createLogger(context) - Structured logging
  • isAbortError(error) - Error type checking
  • classifyError(error) - Error classification
  • buildPromptWithImages() - Prompt building with images
  • readImageAsBase64() - Image handling
  • extractTextFromContent() - Message parsing

Example:

import { createLogger, classifyError } from '@automaker/utils';

Never import from: lib/logger, lib/error-handler, lib/prompt-builder, lib/image-handler

@automaker/prompts

Use when: You need AI prompt templates for text enhancement or other AI-powered features.

Import for:

  • getEnhancementPrompt(mode) - Get complete prompt for enhancement mode
  • getSystemPrompt(mode) - Get system prompt for specific mode
  • getExamples(mode) - Get few-shot examples for a mode
  • buildUserPrompt(description, mode) - Build user prompt with examples
  • isValidEnhancementMode(mode) - Check if mode is valid
  • IMPROVE_SYSTEM_PROMPT - System prompt for improving vague descriptions
  • TECHNICAL_SYSTEM_PROMPT - System prompt for adding technical details
  • SIMPLIFY_SYSTEM_PROMPT - System prompt for simplifying verbose text
  • ACCEPTANCE_SYSTEM_PROMPT - System prompt for adding acceptance criteria

Example:

import { getEnhancementPrompt, isValidEnhancementMode } from '@automaker/prompts';

if (isValidEnhancementMode('improve')) {
  const { systemPrompt, userPrompt } = getEnhancementPrompt('improve', description);
  const result = await callClaude(systemPrompt, userPrompt);
}

Never import from: lib/enhancement-prompts

Enhancement modes:

  • improve - Transform vague requests into clear, actionable tasks
  • technical - Add implementation details and technical specifications
  • simplify - Make verbose descriptions concise and focused
  • acceptance - Add testable acceptance criteria

@automaker/platform

Use when: You need to work with AutoMaker's directory structure or spawn processes.

Import for:

  • getAutomakerDir(projectPath) - Get .automaker directory
  • getFeaturesDir(projectPath) - Get features directory
  • getFeatureDir(projectPath, featureId) - Get specific feature directory
  • ensureAutomakerDir(projectPath) - Create .automaker if needed
  • spawnJSONLProcess() - Spawn process with JSONL output
  • initAllowedPaths() - Security path validation

Example:

import { getFeatureDir, ensureAutomakerDir } from '@automaker/platform';

Never import from: lib/automaker-paths, lib/subprocess-manager, lib/security

@automaker/model-resolver

Use when: You need to convert model aliases to full model IDs.

Import for:

  • resolveModelString(modelOrAlias) - Convert alias to full ID
  • DEFAULT_MODELS - Access default models

Example:

import { resolveModelString, DEFAULT_MODELS } from '@automaker/model-resolver';

// Convert user input to model ID
const modelId = resolveModelString('sonnet'); // → 'claude-sonnet-4-20250514'

Never import from: lib/model-resolver

Model aliases:

  • haikuclaude-haiku-4-5 (fast, simple tasks)
  • sonnetclaude-sonnet-4-20250514 (balanced, recommended)
  • opusclaude-opus-4-5-20251101 (maximum capability)

@automaker/dependency-resolver

Use when: You need to order features by dependencies or check if dependencies are satisfied.

Import for:

  • resolveDependencies(features) - Topological sort with priority
  • areDependenciesSatisfied(feature, allFeatures) - Check if ready to execute
  • getBlockingDependencies(feature, allFeatures) - Get incomplete dependencies

Example:

import { resolveDependencies, areDependenciesSatisfied } from '@automaker/dependency-resolver';

const { orderedFeatures, hasCycle } = resolveDependencies(features);
if (!hasCycle) {
  for (const feature of orderedFeatures) {
    if (areDependenciesSatisfied(feature, features)) {
      await execute(feature);
    }
  }
}

Never import from: lib/dependency-resolver

Used in:

  • Auto-mode feature execution (server)
  • Board view feature ordering (UI)

@automaker/git-utils

Use when: You need git operations, status parsing, or diff generation.

Import for:

  • isGitRepo(path) - Check if path is a git repository
  • parseGitStatus(output) - Parse git status --porcelain output
  • getGitRepositoryDiffs(path) - Get complete diffs (tracked + untracked)
  • generateSyntheticDiffForNewFile() - Create diff for untracked file
  • listAllFilesInDirectory() - List files excluding build artifacts

Example:

import { isGitRepo, getGitRepositoryDiffs } from '@automaker/git-utils';

if (await isGitRepo(projectPath)) {
  const { diff, files, hasChanges } = await getGitRepositoryDiffs(projectPath);
  console.log(`Found ${files.length} changed files`);
}

Never import from: routes/common

Handles:

  • Binary file detection
  • Large file handling (>1MB)
  • Untracked file diffs
  • Non-git directory support

Common Patterns

Creating a Feature Executor

import type { Feature, ExecuteOptions } from '@automaker/types';
import { createLogger, classifyError } from '@automaker/utils';
import { resolveModelString, DEFAULT_MODELS } from '@automaker/model-resolver';
import { areDependenciesSatisfied } from '@automaker/dependency-resolver';
import { getFeatureDir } from '@automaker/platform';

const logger = createLogger('FeatureExecutor');

async function executeFeature(feature: Feature, allFeatures: Feature[], projectPath: string) {
  // Check dependencies
  if (!areDependenciesSatisfied(feature, allFeatures)) {
    logger.warn(`Dependencies not satisfied for ${feature.id}`);
    return;
  }

  // Resolve model
  const model = resolveModelString(feature.model, DEFAULT_MODELS.autoMode);

  // Get feature directory
  const featureDir = getFeatureDir(projectPath, feature.id);

  try {
    // Execute with Claude
    const options: ExecuteOptions = {
      model,
      temperature: 0.7,
    };

    await runAgent(featureDir, options);

    logger.info(`Feature ${feature.id} completed`);
  } catch (error) {
    const errorInfo = classifyError(error);
    logger.error(`Feature ${feature.id} failed:`, errorInfo.message);
  }
}

Analyzing Git Changes

import { getGitRepositoryDiffs, parseGitStatus } from '@automaker/git-utils';
import { createLogger } from '@automaker/utils';

const logger = createLogger('GitAnalyzer');

async function analyzeChanges(projectPath: string) {
  const { diff, files, hasChanges } = await getGitRepositoryDiffs(projectPath);

  if (!hasChanges) {
    logger.info('No changes detected');
    return;
  }

  // Group by status
  const modified = files.filter((f) => f.status === 'M');
  const added = files.filter((f) => f.status === 'A');
  const deleted = files.filter((f) => f.status === 'D');
  const untracked = files.filter((f) => f.status === '?');

  logger.info(
    `Changes: ${modified.length}M ${added.length}A ${deleted.length}D ${untracked.length}U`
  );

  return diff;
}

Ordering Features for Execution

import type { Feature } from '@automaker/types';
import { resolveDependencies, getBlockingDependencies } from '@automaker/dependency-resolver';
import { createLogger } from '@automaker/utils';

const logger = createLogger('FeatureOrdering');

function orderAndFilterFeatures(features: Feature[]): Feature[] {
  const { orderedFeatures, hasCycle, cyclicFeatures } = resolveDependencies(features);

  if (hasCycle) {
    logger.error(`Circular dependency detected: ${cyclicFeatures.join(' → ')}`);
    throw new Error('Cannot execute features with circular dependencies');
  }

  // Filter to only ready features
  const readyFeatures = orderedFeatures.filter((feature) => {
    const blocking = getBlockingDependencies(feature, features);
    if (blocking.length > 0) {
      logger.debug(`${feature.id} blocked by: ${blocking.join(', ')}`);
      return false;
    }
    return true;
  });

  logger.info(`${readyFeatures.length} of ${features.length} features ready`);
  return readyFeatures;
}

Import Rules for LLMs

DO

// Import types from @automaker/types
import type { Feature, ExecuteOptions } from '@automaker/types';

// Import constants from @automaker/types
import { CLAUDE_MODEL_MAP, DEFAULT_MODELS } from '@automaker/types';

// Import utilities from @automaker/utils
import { createLogger, classifyError } from '@automaker/utils';

// Import prompts from @automaker/prompts
import { getEnhancementPrompt, isValidEnhancementMode } from '@automaker/prompts';

// Import platform utils from @automaker/platform
import { getFeatureDir, ensureAutomakerDir } from '@automaker/platform';

// Import model resolution from @automaker/model-resolver
import { resolveModelString } from '@automaker/model-resolver';

// Import dependency resolution from @automaker/dependency-resolver
import { resolveDependencies } from '@automaker/dependency-resolver';

// Import git utils from @automaker/git-utils
import { getGitRepositoryDiffs } from '@automaker/git-utils';

DON'T

// DON'T import from old paths
import { Feature } from '../services/feature-loader';           // ❌
import { ExecuteOptions } from '../providers/types';            // ❌
import { createLogger } from '../lib/logger';                   // ❌
import { resolveModelString } from '../lib/model-resolver';     // ❌
import { isGitRepo } from '../routes/common';                   // ❌
import { resolveDependencies } from '../lib/dependency-resolver'; // ❌
import { getEnhancementPrompt } from '../lib/enhancement-prompts'; // ❌

// DON'T import from old lib/ paths
import { getFeatureDir } from '../lib/automaker-paths';         // ❌
import { classifyError } from '../lib/error-handler';           // ❌

// DON'T define types that exist in @automaker/types
interface Feature { ... }  // ❌ Use: import type { Feature } from '@automaker/types';

Migration Checklist

When refactoring server code, check:

  • All Feature imports use @automaker/types
  • All ExecuteOptions imports use @automaker/types
  • All logger usage uses @automaker/utils
  • All prompt templates use @automaker/prompts
  • All path operations use @automaker/platform
  • All model resolution uses @automaker/model-resolver
  • All dependency checks use @automaker/dependency-resolver
  • All git operations use @automaker/git-utils
  • No imports from old lib/ paths
  • No imports from services/feature-loader for types
  • No imports from providers/types

Package Dependencies

Understanding the dependency chain helps prevent circular dependencies:

@automaker/types (no dependencies)
    ↓
@automaker/utils
@automaker/prompts
@automaker/platform
@automaker/model-resolver
@automaker/dependency-resolver
    ↓
@automaker/git-utils
    ↓
@automaker/server
@automaker/ui

Rule: Packages can only depend on packages above them in the chain.

Building Packages

All packages must be built before use:

# Build all packages from workspace
npm run build:packages

# Or from root
npm install  # Installs and links workspace packages

Module Format

All packages use ES modules (type: "module") with NodeNext module resolution:

  • Requires explicit .js extensions in import statements
  • Compatible with both Node.js (server) and Vite (UI)
  • Centralized ESM configuration in libs/tsconfig.base.json

Testing

When writing tests:

// ✅ Import from packages
import type { Feature } from '@automaker/types';
import { createLogger } from '@automaker/utils';

// ❌ Don't import from src
import { Feature } from '../../../src/services/feature-loader';

Summary for LLMs

Quick reference:

  • Types → @automaker/types
  • Logging/Errors/Utils → @automaker/utils
  • AI Prompts → @automaker/prompts
  • Paths/Security → @automaker/platform
  • Model Resolution → @automaker/model-resolver
  • Dependency Ordering → @automaker/dependency-resolver
  • Git Operations → @automaker/git-utils

Never import from: lib/*, services/feature-loader (for types), providers/types, routes/common

Always: Use the shared packages instead of local implementations.