Add 88 new unit tests covering critical business logic in shared packages: - libs/git-utils/tests/diff.test.ts (22 tests) * Synthetic diff generation for new files * Binary file handling * Large file handling * Untracked file diff appending * Directory file listing with exclusions * Non-git directory handling - libs/dependency-resolver/tests/resolver.test.ts (30 tests) * Topological sorting with dependencies * Priority-aware ordering * Circular dependency detection * Missing dependency tracking * Blocked feature detection * Complex dependency graphs - libs/utils/tests/error-handler.test.ts (36 tests) * Abort error detection * Cancellation error detection * Authentication error detection * Error classification logic * User-friendly error messages All tests use vitest and follow best practices with proper setup/teardown. Resolves PR review issue #1 (HIGH PRIORITY) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
12 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
├── 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 propertiesExecuteOptions- Claude agent execution optionsConversationMessage- Chat message formatErrorType,ErrorInfo- Error handling typesCLAUDE_MODEL_MAP- Model alias to ID mappingDEFAULT_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 loggingisAbortError(error)- Error type checkingclassifyError(error)- Error classificationbuildPromptWithImages()- Prompt building with imagesreadImageAsBase64()- Image handlingextractTextFromContent()- Message parsing
Example:
import { createLogger, classifyError } from '@automaker/utils';
Never import from: lib/logger, lib/error-handler, lib/prompt-builder, lib/image-handler
@automaker/platform
Use when: You need to work with AutoMaker's directory structure or spawn processes.
Import for:
getAutomakerDir(projectPath)- Get .automaker directorygetFeaturesDir(projectPath)- Get features directorygetFeatureDir(projectPath, featureId)- Get specific feature directoryensureAutomakerDir(projectPath)- Create .automaker if neededspawnJSONLProcess()- Spawn process with JSONL outputinitAllowedPaths()- 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 IDDEFAULT_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:
haiku→claude-haiku-4-5(fast, simple tasks)sonnet→claude-sonnet-4-20250514(balanced, recommended)opus→claude-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 priorityareDependenciesSatisfied(feature, allFeatures)- Check if ready to executegetBlockingDependencies(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 repositoryparseGitStatus(output)- Parsegit status --porcelainoutputgetGitRepositoryDiffs(path)- Get complete diffs (tracked + untracked)generateSyntheticDiffForNewFile()- Create diff for untracked filelistAllFilesInDirectory()- 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 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'; // ❌
// 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
Featureimports use@automaker/types - All
ExecuteOptionsimports use@automaker/types - All logger usage uses
@automaker/utils - 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-loaderfor types - No imports from
providers/types
Package Dependencies
Understanding the dependency chain helps prevent circular dependencies:
@automaker/types (no dependencies)
↓
@automaker/utils
@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
- dependency-resolver: ES modules (
type: "module") for Vite compatibility - All others: CommonJS for Node.js compatibility
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 →
@automaker/utils - 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.