Skip to main content

Development Workflow Guide

Development Workflow Guide

Separation of Duties: See Separation of Duties - Development workflow documentation is responsible for development patterns and workflows. It does NOT own agent manifests, execution, or infrastructure configuration.

Consolidated development patterns for TypeScript/JavaScript projects in the LLM Platform ecosystem.


Table of Contents

  1. NPM Scripts
  2. Pre-commit Hooks (Lefthook)
  3. TypeScript Configuration
  4. ESLint and Prettier Setup
  5. Git Workflow with Worktrees
  6. Environment Setup

NPM Scripts

Standard Script Patterns

All packages in the ecosystem follow consistent npm script patterns:

Core Scripts (Required)

{ "scripts": { "build": "tsc", "dev": "next dev", "start": "next start", "test": "jest", "lint": "eslint src/", "typecheck": "tsc --noEmit" } }

Testing Scripts

{ "scripts": { "test": "jest", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "test:e2e:debug": "playwright test --debug", "test:e2e:report": "playwright show-report", "test:php": "phpunit", "test:drupal": "drush test-run --verbose", "test:integration": "jest --testPathPattern=integration", "test:coverage": "jest --coverage", "install:playwright": "playwright install" } }

OpenAPI Scripts

{ "scripts": { "generate:types": "node scripts/generate-types.cjs openapi.yaml src/generated", "generate:types:clean": "rm -rf src/generated", "openapi:validate": "npx @redocly/cli lint openapi.yaml", "openapi:bundle": "npx @redocly/cli bundle openapi.yaml -o openapi.json", "openapi:html": "npx redoc-cli bundle openapi.yaml -o openapi.html" } }

Standard Dependencies

Runtime Dependencies

{ "dependencies": { "commander": "^11.0.0", "chalk": "^5.3.0", "zod": "^3.22.4" } }

Development Dependencies

{ "devDependencies": { "@playwright/test": "^1.54.2", "@types/node": "^20.0.0", "typescript": "^5.0.0", "eslint": "^8.57.0", "prettier": "^3.0.0", "axe-playwright": "^2.0.3" } }

Frontend (Next.js) Dependencies

{ "dependencies": { "@radix-ui/react-slot": "^1.0.2", "@tanstack/react-query": "^5.0.0", "axios": "^1.7.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.403.0", "next": "15.5.3", "react": "^18.2.0", "react-dom": "^18.2.0", "recharts": "^2.8.0", "tailwind-merge": "^2.3.0", "zod": "^3.23.8" }, "devDependencies": { "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.19", "eslint-config-next": "14.2.3", "postcss": "^8.4.38", "tailwindcss": "^3.4.3" } }

Pre-commit Hooks (Lefthook)

Standard Lefthook Configuration

All projects use Lefthook for Git hooks. Create lefthook.yml in project root:

# lefthook.yml - Standard Configuration pre-commit: parallel: true commands: # Block markdown files in root (use GitLab Wiki) block-root-md: run: | ROOT_MD=$(git diff --cached --name-only --diff-filter=A | grep -E '^[^/]+.md$' | grep -v '^README.md$' | grep -v '^CHANGELOG.md$' | grep -v '^TESTING.md$' || true) if [ -n "$ROOT_MD" ]; then echo "ERROR: .md files in root are forbidden" echo "$ROOT_MD" | sed 's/^/ - /' echo "Use GitLab Wiki instead: buildkit gitlab wiki create --slug \"page-name\" --file file.md" echo "(Exception: README.md, CHANGELOG.md, TESTING.md required for Drupal.org)" exit 1 fi files: git diff --cached --name-only --diff-filter=A glob: '*.md' # Block shell scripts (use BuildKit CLI) block-sh-scripts: run: | SH_FILES=$(git diff --cached --name-only --diff-filter=A | grep '\.sh$' || true) if [ -n "$SH_FILES" ]; then echo "ERROR: .sh scripts are FORBIDDEN (use agent-buildkit CLI)" echo "$SH_FILES" | sed 's/^/ - /' exit 1 fi files: git diff --cached --name-only --diff-filter=A glob: '*.sh' # Format code format: run: | if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then if npm run format >/dev/null 2>&1; then npm run format fi fi root: . stage_fixed: true skip: - merge - rebase # Lint code lint: run: | if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then if npm run lint >/dev/null 2>&1; then npm run lint fi fi root: . files: git diff --cached --name-only --diff-filter=ACM skip: - merge - rebase # TypeScript type checking typecheck: run: | if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then if npm run typecheck >/dev/null 2>&1; then npm run typecheck fi fi root: . glob: '**/*.{ts,tsx}' skip: - merge - rebase # PHP CodeSniffer (Drupal modules) phpcs: glob: '**/*.{php,module,install,theme,inc}' root: . run: | if [ -f "composer.json" ] && command -v composer >/dev/null 2>&1; then if composer run-script --list 2>/dev/null | grep -q "phpcs"; then composer phpcs fi fi skip: - merge - rebase # PHPStan (Drupal modules) phpstan: glob: '**/*.{php,module,install,theme,inc}' root: . run: | if [ -f "composer.json" ] && command -v composer >/dev/null 2>&1; then if composer run-script --list 2>/dev/null | grep -q "phpstan"; then composer phpstan fi fi skip: - merge - rebase commit-msg: commands: # Block AI attribution in commits claude-protection: run: | COMMIT_MSG_FILE="{1}" if [ -z "$COMMIT_MSG_FILE" ] || [ "$COMMIT_MSG_FILE" = "{1}" ]; then COMMIT_MSG_FILE=".git/COMMIT_EDITMSG" fi if [ ! -f "$COMMIT_MSG_FILE" ]; then exit 0 fi COMMIT_MSG=$(cat "$COMMIT_MSG_FILE") if echo "$COMMIT_MSG" | grep -qiE "Generated with.*Claude|Co-Authored-By:.*Claude|claude.ai|noreply@anthropic.com"; then echo "COMMIT BLOCKED: Forbidden Claude/AI attribution detected!" echo " Write as the human developer" echo " Use professional technical language" exit 1 fi # Require issue reference for feature branches require-issue-ref: run: | BRANCH=$(git branch --show-current) if [[ "$BRANCH" =~ ^(feature|bug|hotfix|chore) ]] && ! grep -qiE "(#[0-9]+|issue|gitlab)" "$1"; then echo "WARNING: Branch '$BRANCH' should reference GitLab Issue" echo " Format: 'feat: description (fixes #123)'" fi pre-push: commands: # Run tests before push test: run: | if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then if npm test >/dev/null 2>&1; then npm test fi elif [ -f "composer.json" ] && command -v composer >/dev/null 2>&1; then if composer run-script --list 2>/dev/null | grep -q "test"; then composer test fi fi root: . skip: - merge - rebase # Security audit security-audit: run: | if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then npm audit --audit-level=moderate || true fi root: . skip: - merge - rebase # Block pushes to development branch from Claude Code block-claude-on-development: run: | BRANCH=$(git branch --show-current) if [ "$BRANCH" = "development" ]; then echo "BLOCKED: Claude Code not allowed on development branch" echo "Use feature branches only" exit 1 fi skip_output: - meta - summary

Installing Lefthook

# Install lefthook npm install -D lefthook # Initialize hooks npx lefthook install

TypeScript Configuration

Standard tsconfig.json

{ "compilerOptions": { "target": "ES2022", "module": "ES2022", "moduleResolution": "Node", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "tests"] }

Next.js tsconfig.json

{ "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./src/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }

ESLint and Prettier Setup

ESLint Configuration (.eslintrc.js)

module.exports = { parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, env: { node: true, es2022: true }, extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended' ], rules: { // Recommended rules for production 'no-console': 'warn', '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/explicit-function-return-type': 'off', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }] } };

ESLint Relaxed Configuration (for cleanup work)

// .eslintrc.relaxed.js module.exports = { parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, env: { node: true, es2022: true }, rules: { // Relaxed rules for cleanup/refactoring work 'no-console': 'off', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/explicit-function-return-type': 'off', 'max-lines': 'off', 'max-lines-per-function': 'off', 'complexity': 'off', 'no-magic-numbers': 'off' } };

Prettier Configuration (.prettierrc.json)

{ "printWidth": 80, "tabWidth": 2, "useTabs": false, "semi": true, "singleQuote": true, "quoteProps": "as-needed", "jsxSingleQuote": false, "trailingComma": "es5", "bracketSpacing": true, "bracketSameLine": false, "arrowParens": "always", "requirePragma": false, "insertPragma": false, "proseWrap": "preserve", "htmlWhitespaceSensitivity": "css", "vueIndentScriptAndStyle": false, "endOfLine": "lf", "embeddedLanguageFormatting": "auto", "singleAttributePerLine": false, "overrides": [ { "files": "*.md", "options": { "proseWrap": "always" } }, { "files": "*.json", "options": { "tabWidth": 2 } }, { "files": "*.yml", "options": { "tabWidth": 2 } } ] }

.prettierignore

node_modules/
dist/
build/
.next/
coverage/
*.min.js
*.min.css

Git Workflow with Worktrees

Overview

All development work happens in Git worktrees, not in the main repository directory. This ensures:

  • Main project directories always stay on development branch
  • Feature work happens in isolation
  • No branch switching disrupts other work
  • Multiple features can be developed in parallel

Worktree Directory Structure

~/Sites/LLM/                      # Main repositories (stay on development)
~/Sites/.workingtrees/LLM/        # All worktrees (CANONICAL location)
     agent-buildkit/
        feature-123-auth/
     platform-agents/
        bugfix-456-fix/
     llm-platform/
         chore-789-cleanup/

Complete Workflow

1. Create Issue in GitLab

# Browse to GitLab or use CLI glab issue create --title "Feature: Add user authentication"

2. Create MR from Issue Page

On the GitLab issue page, click "Create merge request" button. GitLab will:

  • Create the branch automatically: {issue#}-{title-slug}
  • Link MR to issue
  • Set MR to auto-close issue when merged

3. Fetch and Create Worktree

# Fetch the new branch cd ~/Sites/LLM/<project> git fetch origin # Create worktree for the branch git worktree add ~/Sites/.workingtrees/LLM/<project>/<branch> origin/<branch> # Navigate to worktree cd ~/Sites/.workingtrees/LLM/<project>/<branch>

4. Make Changes and Commit

# Work in worktree cd ~/Sites/.workingtrees/LLM/<project>/<branch> # Make changes npm install npm run build npm test # Commit with conventional format git add . git commit -m "feat(auth): implement user authentication (fixes #123)" git push

5. After MR Merges

# Return to main project cd ~/Sites/LLM/<project> # Remove worktree git worktree remove ~/Sites/.workingtrees/LLM/<project>/<branch> # Delete local branch git branch -d <branch> # Update main git pull origin development

Quick Commands Reference

# Create worktree git worktree add ~/Sites/.workingtrees/LLM/<project>/<branch> origin/<branch> # List worktrees git worktree list # Remove worktree git worktree remove ~/Sites/.workingtrees/LLM/<project>/<branch> # Prune stale worktrees git worktree prune

One-liner: Setup New Feature

git fetch origin && \ git worktree add ~/Sites/.workingtrees/LLM/<project>/<branch> origin/<branch> && \ cd ~/Sites/.workingtrees/LLM/<project>/<branch>

One-liner: Cleanup After Merge

cd ~/Sites/LLM/<project> && \ git worktree remove ~/Sites/.workingtrees/LLM/<project>/<branch> && \ git pull origin development

Critical Rules

RuleDescription
NEVER create branches manuallyUse GitLab "Create merge request" from issue
ALWAYS use worktreesNever checkout branches in main repo
Main projects stay on development~/Sites/LLM/* directories never change branches
Reference issues in commitsFormat: type(scope): description (fixes #123)

Environment Setup

Prerequisites

# Install Node.js 20+ nvm install 20 nvm use 20 # Verify versions node --version # Should be 20.x+ npm --version # Should be 10.x+

Token Management

All tokens stored in ~/.tokens/:

# Create tokens directory mkdir -p ~/.tokens chmod 700 ~/.tokens # Store API keys echo "your-key" > ~/.tokens/anthropic echo "your-key" > ~/.tokens/openai echo "your-key" > ~/.tokens/gitlab chmod 600 ~/.tokens/*

Environment Variables

# Node options export NODE_OPTIONS="--max-old-space-size=4096" # Load tokens export ANTHROPIC_API_KEY=$(cat ~/.tokens/anthropic) export OPENAI_API_KEY=$(cat ~/.tokens/openai) export GITLAB_TOKEN=$(cat ~/.tokens/gitlab) # Project paths export LLM_ROOT=~/Sites/LLM export WORKTREE_ROOT=~/Sites/.workingtrees/LLM

Package Installation

# Install dependencies npm install # Install Playwright browsers (for E2E tests) npx playwright install # Install Lefthook hooks npx lefthook install

Quick Reference

New Project Checklist

  1. Create package.json with standard scripts
  2. Add tsconfig.json with strict mode
  3. Add .eslintrc.js configuration
  4. Add .prettierrc.json configuration
  5. Add lefthook.yml with standard hooks
  6. Run npx lefthook install
  7. Create GitLab issue for initial setup
  8. Use worktree for development

Common Commands

# Build npm run build # Test npm test npm run test:e2e npm run test:coverage # Lint and format npm run lint npm run format npm run typecheck # OpenAPI npm run openapi:validate npm run generate:types # Git worktree git worktree add <path> <branch> git worktree list git worktree remove <path>


Last Updated: 2026-01-01 Version: 1.0.0