protoLabs Studio is built as an npm workspace monorepo, providing a modular and maintainable codebase structure. This guide explains how the repository is organized and how packages interact.
automaker/
├── apps/
│ ├── ui/ # React + Vite frontend (port 3007)
│ └── server/ # Express + WebSocket backend (port 3008)
├── site/ # Landing page (protolabs.studio) — static HTML on Cloudflare Pages
└── libs/ # Shared packages (@protolabsai/*)
├── types/ # Core TypeScript definitions (no dependencies)
├── utils/ # Logging, errors, image processing, context loading
├── prompts/ # AI prompt templates
├── platform/ # Path management, security, process spawning
├── model-resolver/ # Claude model alias resolution
├── dependency-resolver/ # Feature dependency ordering
├── spec-parser/ # XML/markdown spec parsing for project plans
├── git-utils/ # Git operations & worktree management
├── tools/ # Unified tool definition and registry system
├── flows/ # LangGraph state graph primitives & flow orchestration
├── observability/# Langfuse tracing & cost tracking
└── ui/ # Shared UI components (@protolabsai/ui) — atoms, molecules, theme
Packages follow a strict layered dependency hierarchy. Lower-level packages cannot depend on higher-level ones:
@protolabsai/types (no dependencies)
↓
@protolabsai/utils, @protolabsai/prompts, @protolabsai/platform,
@protolabsai/model-resolver, @protolabsai/dependency-resolver,
@protolabsai/spec-parser, @protolabsai/tools,
@protolabsai/flows, @protolabsai/observability
↓
@protolabsai/git-utils, @protolabsai/ui
↓
apps/server, apps/ui (applications)
Why this matters:
- Prevents circular dependencies - Clear hierarchy makes builds predictable
- Enables incremental builds - Change a low-level package, rebuild only dependents
- Improves testability - Lower-level packages are easier to test in isolation
- Supports code reuse - Shared packages can be extracted to separate repos if needed
Foundation layer with zero dependencies. Contains all TypeScript interfaces and types used across the project.
Key exports:
import type {
Feature,
FeatureStatus,
AgentTemplate,
Project,
Milestone,
Phase,
} from '@protolabsai/types';Rule: Never import from other @protolabsai/* packages. External deps (zod, etc.) are allowed.
Common utilities for logging, error handling, image processing, and context file loading.
Key exports:
import {
createLogger, // Winston-based logger
classifyError, // Error categorization
loadContextFiles, // Load .automaker/context/*.md
} from '@protolabsai/utils';AI prompt templates for agents, organized by role (PM, engineer, specialist).
Key exports:
import {
getEnhancementPrompt, // Feature enhancement prompts
getAgentSystemPrompt, // Agent role system prompts
} from '@protolabsai/prompts';Path management, security checks, and process spawning utilities.
Key exports:
import {
getFeatureDir, // Get .automaker/features/{id}
ensureAutomakerDir, // Create .automaker/ structure
spawnChildProcess, // Secure process spawning
} from '@protolabsai/platform';Converts model aliases to full model strings.
Key exports:
import { resolveModelString } from '@protolabsai/model-resolver';
// Usage
resolveModelString('sonnet'); // → 'claude-sonnet-4-6'
resolveModelString('opus'); // → 'claude-opus-4-6'
resolveModelString('haiku'); // → 'claude-haiku-4-5-20251001'See Model Resolver Reference for details.
Resolves feature dependencies and determines execution order.
Key exports:
import { resolveDependencies } from '@protolabsai/dependency-resolver';Git operations and worktree management.
Key exports:
import {
getGitRepositoryDiffs, // Get diffs between branches
createWorktree, // Create isolated worktree
cleanupWorktree, // Remove worktree
} from '@protolabsai/git-utils';Unified tool definition and registry system for AI agents.
Key exports:
import {
ToolRegistry, // Tool registration and lookup
createTool, // Tool factory
ToolDefinition, // Tool schema
} from '@protolabsai/tools';LangGraph state graph primitives and flow orchestration for multi-agent workflows.
Key exports:
import {
StateGraph, // Graph construction
StateNode, // Node definition
executeFlow, // Flow executor
} from '@protolabsai/flows';Langfuse tracing, prompt versioning, and cost tracking.
Key exports:
import {
initializeLangfuse, // Setup Langfuse client
traceAgentExecution, // Trace agent runs
trackPromptVersion, // Version prompts
} from '@protolabsai/observability';Shared React components using atomic design pattern (atoms, molecules, organisms).
Key exports:
import {
Button, // Atom components
Card, // Molecule components
theme, // Tailwind CSS theme
} from '@protolabsai/ui';Always import from shared packages:
// ✅ Correct
import type { Feature, ExecuteOptions } from '@protolabsai/types';
import { createLogger, classifyError } from '@protolabsai/utils';
import { getEnhancementPrompt } from '@protolabsai/prompts';
import { getFeatureDir, ensureAutomakerDir } from '@protolabsai/platform';
import { resolveModelString } from '@protolabsai/model-resolver';
import { resolveDependencies } from '@protolabsai/dependency-resolver';
import { getGitRepositoryDiffs } from '@protolabsai/git-utils';
// ❌ Never import from relative paths in apps/
import { Feature } from '../services/feature-loader'; // Wrong
import { createLogger } from '../lib/logger'; // WrongWhy:
- Enforces package boundaries
- Makes refactoring safer
- Enables potential package extraction
- Improves IDE autocomplete and jump-to-definition
Due to package dependencies, builds must happen in order:
# 1. Build all shared packages first
npm run build:packages
# 2. Then build applications
npm run build:server
npm run build # Builds UIThe build:packages script runs:
npx turbo build --filter="./libs/*"This uses Turbo to build packages in dependency order with caching.
During development, you typically don't need to rebuild packages constantly:
# Start dev server (auto-imports TypeScript from libs/)
npm run dev:webWhen to rebuild packages:
- After pulling changes that modify
libs/ - After changing a package's exports or types
- Before running E2E tests
- Before building for production
# Test all packages
npm run test:packages
# Test server (unit tests)
npm run test:server
# Test E2E (requires built packages)
npm run build:packages
npm run testIf you need to create a new shared package:
- Create package directory:
mkdir -p libs/my-package/src
cd libs/my-package- Create
package.json:
{
"name": "@protolabsai/my-package",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"test": "vitest"
},
"dependencies": {
"@protolabsai/types": "workspace:*"
},
"devDependencies": {
"tsup": "^8.3.5",
"vitest": "^2.1.8"
}
}- Create
tsconfig.json:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}- Create
tsup.config.ts:
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
});-
Verify position in dependency chain - Check the hierarchy diagram above and ensure your package only depends on packages at the same or lower level.
-
Install dependencies:
npm installSolution: Build packages first:
npm run build:packagesSolution: Check your imports. You may be importing from a higher-level package. Review the dependency chain diagram.
Solution: Rebuild packages and restart TypeScript server:
npm run build:packages
# In VSCode: Cmd+Shift+P → "TypeScript: Restart TS Server"Solution: Rebuild and re-run tests:
npm run build:packages
npm run test:packages- Git Workflow - Branch strategies and PR process
- Environment Setup - Required environment variables
- Creating MCP Tools - Build custom MCP tools for agents