This file provides guidance to AI agents when working with code in this repository. It serves as the root intent node and points to deeper context in subdirectories.
cli/- Tuist CLI (Swift) - seecli/AGENTS.mdserver/- Tuist Server (Elixir/Phoenix) - seeserver/AGENTS.mdcache/- Tuist cache service (Elixir/Phoenix) - seecache/AGENTS.mdkura/- Kura distributed cache mesh (Rust) - seekura/AGENTS.mdtuist_common/- Shared Elixir utilities used across services - seetuist_common/AGENTS.mdapp/- Tuist iOS and macOS app - seeapp/AGENTS.mdandroid/- Tuist Android app (Kotlin/Compose) - seeandroid/AGENTS.mdhandbook/- Company handbook (VitePress) - seehandbook/AGENTS.mdnoora/- Noora design system (Elixir/Phoenix web components) - seenoora/AGENTS.mdmise/tasks/registry/- Operational scripts for Swift package registry management (purge, sync)skills/- Agent Skills (published to tuist/agent-skills)server/native/xcactivitylog_nif/- Swift NIF linked into the server release for xcactivitylog parsing. The processor is no longer a standalone Elixir app; it's the sameghcr.io/tuist/tuistimage booted withTUIST_MODE=processorto run the:process_buildOban queue consumer.xcode_processor/- Xcode processor service for xcresult processing (Elixir/Phoenix + Swift NIF, macOS) - seexcode_processor/AGENTS.mdsearch/- Search infrastructure (TypeSense) - seesearch/AGENTS.mdinfra/- Infrastructure and deployment assets - seeinfra/AGENTS.md
- Do not modify
CHANGELOG.md(auto-generated). - Do not edit translation
.pofiles; only thetuistitbot should change them. - Do not modify content in languages other than English (source language).
When making changes in a directory with an AGENTS.md, keep that node up to date. If a new subsystem or boundary is introduced, add a new leaf AGENTS.md and link it from the nearest parent node.
When creating commits and pull requests, use these conventional commit scopes:
app- Changes to the Tuist iOS and macOS appandroid- Changes to the Tuist Android appserver- Changes to the Tuist server (Elixir/Phoenix)cache- Changes to the Tuist cache service (Elixir/Phoenix)kura- Changes to the Kura distributed cache mesh servicecli- Changes to the Tuist CLI (Swift)noora- Changes to the Noora web component libraryskills- Changes to the Agent Skills packagesearch- Changes to the search infrastructure (TypeSense)docs- Changes to documentationhandbook- Changes to the handbook/guides
Examples:
feat(server): add new telemetry sanitizer modulefix(cli): resolve cache artifact upload issuefeat(cache): add new S3 transfer workerfeat(kura): add peer discovery backoff handlingfeat(skills): add new migration skilldocs(handbook): update project setup guide
- Do not add one-line comments unless you think they are really useful.
- For faster builds, generate only the required targets:
tuist generate tuist ProjectDescription --no-open - When compiling Swift changes, use
xcodebuild build -workspace Tuist.xcworkspace -scheme tuist CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=""instead ofswift build - When testing Swift changes, use
xcodebuild test -workspace Tuist.xcworkspace -scheme Tuist-Workspace -only-testing MyTests/SuiteTests CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY=""instead ofswift test. - Prefer running test suites or individual test cases, and not the whole test target, for performance
- When using
swift build,swift test, orswift package resolvealways include--replace-scm-with-registryto avoid switching packages from registry to source control resolution
- Use Swift Testing framework with custom traits for tests that need temporary directories
- For tests requiring temporary directories, use
@Test(.inTemporaryDirectory)and access the directory viaFileSystem.temporaryTestDirectory - Import
FileSystemTestingwhen using the.inTemporaryDirectorytrait - Example pattern:
import FileSystemTesting import Testing @Test(.inTemporaryDirectory) func test_example() async throws { let temporaryDirectory = try #require(FileSystem.temporaryTestDirectory) // Test implementation }
- Do not modify CHANGELOG.md as it is auto-generated
- The server's OpenAPI spec and CLI Swift client code are regenerated with:
mise run generate-api-cli-code(run from theserver/directory) - This exports the spec to
cli/Sources/TuistServer/OpenAPI/server.ymland regeneratesTypes.swiftandClient.swift - Do not edit
server.yml,Types.swift, orClient.swiftmanually — update the controller schemas in the server and regenerate
- To check for linting issues:
mise run lint - To automatically fix fixable linting issues:
mise run lint --fix
Tuist Server is an Elixir/Phoenix web application that extends the functionality of the Tuist CLI for iOS/macOS development. It provides binary caching, app preview deployment, build analytics, and Swift package registry services.
Key Technologies:
- Backend: Elixir 1.19.5 with Phoenix 1.7.12 framework
- Databases:
- PostgreSQL (primary database)
- ClickHouse (analytics database, write-only through IngestRepo)
- Frontend: Phoenix LiveView with JavaScript/TypeScript and esbuild
- Package Management: pnpm for JavaScript dependencies
Core Architecture Components:
lib/tuist/- Core business logic modules (accounts, billing, bundles, projects, registry, etc.)lib/tuist_web/- Web interface (controllers, LiveView components, marketing site)priv/repo/migrations/- PostgreSQL database schema migrationspriv/ingest_repo/migrations/- ClickHouse database schema migrations (analytics, write-only)assets/- Frontend source files (JS/CSS)config/- Application configuration
Main Business Domains:
- Accounts - User authentication, organizations, billing management
- Projects - Project management, tokens, permissions
- Bundles - Binary cache management for build optimization
- Previews - iOS app preview generation and deployment
- Registry - Swift package registry implementation
- Command Events - CLI interaction tracking and analytics
Prerequisites:
- PostgreSQL 16
- Mise development environment manager
- Private key from 1Password for
priv/secrets/dev.key
Setup Commands:
brew install postgresql@16 # Make sure `postgresql@16` is installed locally via Homebrew
brew services start postgresql@16
mise run clickhouse:start # Start ClickHouse
mise install # Install dependencies and bootstrap the local development database
mise run dev # Start development serverTest User Account:
- Email:
tuistrocks@tuist.dev - Password:
tuistrocks
Database Management:
mise run db:setup- Complete database setup (create, migrate, seed)mise run db:reset- Drop, recreate, and migrate databasemix ecto.migrate- Run pending migrationsmix ecto.rollback- Rollback last migration
Development Server:
mise run dev- Start Phoenix development servermix phx.server- Alternative way to start serveriex -S mix phx.server- Start server with interactive shell
Testing:
mix test- Run full test suite (includes database setup)mix test test/path/to/specific_test.exs- Run specific test filemix test test/path/to/test_file.exs:line_number_of_test- Test single casemix test --only tag_name- Run tests with specific tag
Code Quality:
mix credo- Run code analysis and lintingmix format- Format Elixir codemise run format- Format all code (Elixir + JS)mix sobelow- Security analysismise run security- Run security static checks
Frontend Assets:
mix assets.setup- Install esbuildmix assets.build- Build all assets (app, marketing, apidocs)mix assets.deploy- Build and minify assets for production
Database Utilities:
mix ecto.dump- Export database structuremix ecto.load- Import database structuremix excellent_migrations.check_safety- Check migration safety
.mise.toml- Development environment and tool versionsmix.exs- Elixir project configuration and dependenciespackage.json- JavaScript dependencies managed by pnpmconfig/directory - Phoenix application configurationpriv/secrets/dev.key- Development secrets encryption key (not in repo)
The codebase uses ExUnit for testing. Test files follow the pattern test/**/module_name_test.exs. Tests automatically set up a clean database before running.
Running Specific Tests:
mix test test/tuist/accounts_test.exs
mix test test/tuist_web/live/dashboard_live_test.exsTesting Guidelines:
- Never modify System environment variables in tests as they are shared state and can cause flaky tests
- Use mocks, stubs, or dependency injection to test environment-dependent behavior
- If environment testing is absolutely necessary, use process-scoped alternatives or test tags to isolate tests
- Formatting: Follow standard Elixir and Phoenix conventions. Consider using an Elixir formatter.
- Imports/Aliases: Use
aliasfor modules used multiple times. Avoidimportunless for specific DSLs (e.g., Ecto.Query). - Modules Aliases: Always declare module aliases at the module level in files, not within individual functions. This improves readability and avoids repetition.
- Mocking: Copy the modules for mocking in @server/test/test_helper.exs not in the individual functions.
- Types: Do not add typespecs (
@spec,@type, etc.) to functions or modules. - Naming Conventions:
- Modules: PascalCase (e.g.,
MyModule) - Functions: snake_case (e.g.,
my_function) - Variables: snake_case (e.g.,
my_variable)
- Modules: PascalCase (e.g.,
- Error Handling: Prefer tagged tuples
{:ok, value}and{:error, reason}for functions that can fail. Use exceptions for unrecoverable errors. - Credo: Adhere to rules in
.credo.exs.- Timestamps in migrations should be
:timestamptz. - Timestamps in
lib/should be:utc_datetime.
- Timestamps in migrations should be
- Comments: Add comments for complex logic or non-obvious code. Remove
TODOcomments once addressed.
Important: Translations are managed through Weblate. Do not manually edit translation files.
Translation File Types:
.potfiles (templates) - CAN be modified by developers when adding/changing translatable strings.pofiles (translations) - MUST NOT be modified by developers- Only the
tuistitbot should modify.pofiles
Workflow:
- Add translatable strings using
dgettext/2in your code - Run
mix gettext.extractto update the.pottemplate files - Commit only the
.potfiles (and your code changes) - Weblate will automatically sync the
.potchanges and create translation PRs via thetuistitbot - Never run
mix gettext.extract --mergein your PRs as this modifies.pofiles
Key Principles:
- Currency symbols and monetary amounts should NOT be wrapped in
dgettext/2- they must remain consistent across languages - Descriptive text around prices (like "and up", "per unit") SHOULD be translatable
- Example:
"0€ " <> dgettext("marketing", "and up")✅ - Anti-example:
dgettext("marketing", "0€ and up")❌
CI Protection:
The CI pipeline will fail if any .po files are modified by anyone other than tuistit.
The application deploys to managed Syself Kubernetes clusters on Hetzner via Helm. See infra/AGENTS.md for the full layout.
- Push to
maintriggers.github/workflows/server-production-deployment.yml, which cascades canary → acceptance tests → production (hotfix fast-path available). - Single-environment deploys use
.github/workflows/server-deployment.ymlviaworkflow_dispatch. - Chart and per-env values live in
infra/helm/tuist/(values-managed-{staging,canary,production}.yaml).
- Always run
mix ecto.migrateafter pulling database migrations - Use
mise run installafter pulling dependency changes or when bootstrapping a fresh worktree - Local development connects to
http://localhost:8080for Tuist CLI integration
The Tuist handbook is a VitePress-based documentation site that contains company policies, procedures, and guidelines. It's organized into several main sections:
- Company: Mission, vision, principles, and leadership information
- Security: Comprehensive security policies and procedures
- Engineering: Technical standards, practices, and technologies
- People: Benefits, values, code of conduct, and how we work
- Marketing: Guidelines and case studies
- Product: Product development processes
- Support: Support processes and procedures
- Community: Community-related content
- Build command:
mise run handbook:build(can be run from any directory)- This command also verifies that there are no dead links
- Development server:
mise run handbook:dev(from the handbook directory) - Deployment: The handbook is automatically deployed to Cloudflare Pages at handbook.tuist.io
handbook/
├── .vitepress/
│ └── config.mjs # Navigation and site configuration
├── handbook/ # Content directory
│ ├── company/
│ ├── security/
│ ├── engineering/
│ ├── people/
│ ├── marketing/
│ ├── product/
│ ├── support/
│ └── community/
└── package.json
When creating or modifying security policies:
-
Follow the standard format:
- Include frontmatter with title, titleTemplate, and description
- Start with policy owner and effective date
- Use consistent section structure
-
Standard policy sections:
- Purpose
- Scope
- Policy Statement
- Requirements (numbered subsections)
- Roles and Responsibilities
- Exceptions
- Compliance Monitoring
- Policy Review
- Version History
-
Key considerations:
- Keep policies practical for a 4-person company
- Reference the shared responsibility model when discussing infrastructure
- Infrastructure providers (Render, Supabase, Tigris, Cloudflare) handle their own layer security
- Focus on application-layer responsibilities
The site navigation is configured in .vitepress/config.mjs:
- The sidebar structure should match the directory structure
- When adding new pages, ensure they're included in the navigation
- Redirects for moved pages are handled in the buildEnd hook
- Use clear, concise language
- Write for a small, technical team
- Avoid overly bureaucratic language
- Focus on practical implementation
---
title: Page Title
titleTemplate: :title | Section | Tuist Handbook
description: Brief description of the page content
---- Use standard GitHub-flavored markdown
- Include anchors for major sections
- Use numbered lists for sequential steps
- Use bullet points for non-sequential items
-
Always verify builds: Run
mise run handbook:buildbefore committing to ensure:- The handbook builds successfully
- There are no broken links
- Navigation is properly configured
-
Security policy updates: When updating security policies, consider:
- Impact on the small team size
- Alignment with shared responsibility model
- Practical implementation requirements
-
Infrastructure responsibilities: Remember that Tuist relies on:
- Render for application hosting
- Supabase for database services
- Tigris for data storage
- Cloudflare for CDN and edge services
Each provider handles security at their infrastructure layer, while Tuist focuses on application-layer security.
- Create the markdown file in the appropriate directory
- Add proper frontmatter
- Update
.vitepress/config.mjsto include it in navigation - Run
mise run handbook:buildto verify - Commit and create a PR with @tuist/company team as reviewer
- Make changes to the markdown file
- Verify internal links are still valid
- Run
mise run handbook:buildto test - Commit with a descriptive message
- Create a PR with @tuist/company team as reviewer
- Move/rename the file
- Update navigation in
.vitepress/config.mjs - Add a redirect in the buildEnd hook if needed
- Update any internal links to the page
- Run
mise run handbook:buildto verify all links - Create a PR with @tuist/company team as reviewer
The server/data-export.md file documents all personal and organizational data that Tuist stores and can export for customers upon legal request. This file must be kept current whenever database schema changes or new data storage is introduced.
You MUST update server/data-export.md when making any of the following changes:
- Adding new tables to PostgreSQL or ClickHouse
- Adding new columns that store customer/user data
- Modifying data relationships between tables
- Adding new Ecto schemas or models
- Changes to data retention policies
- Adding new types of files stored in S3
- Changing S3 storage path structures
- Adding new file categories or storage buckets
- Modifying file upload/download processes
- Adding new user data fields
- Implementing new analytics or tracking
- Adding new integrations that store customer data
- New features that generate customer-owned content
When making qualifying changes:
- Review the change: Identify what new data is being stored or how existing data storage is modified
- Update the documentation: Add or modify the relevant sections in
server/data-export.md - Be comprehensive: Include:
- Data type and purpose
- Storage location (database table, S3 path structure, etc.)
- Data relationships
- Retention policies
- Export capabilities
- Maintain format: Follow the existing documentation structure and style
- Test documentation: Verify that the export process could actually retrieve the documented data
This documentation is critical for:
- GDPR Article 20 (Right to data portability)
- CCPA data export requirements
- General customer data transparency
- Incident response and data breach notifications
Failure to keep this documentation current could result in incomplete data exports for legal requests, potentially leading to compliance violations.
- Don't modify content in languages other than English (source language)