Skip to content

Multi-File Organization

Learn how to split PromptScript files across multiple files for better organization and maintainability.

Overview

As projects grow, a single .prs file can become unwieldy. PromptScript supports splitting your configuration across multiple files using @use imports and a registry structure.

flowchart TB
    subgraph Project["Project Files"]
        main["project.prs<br/>Main entry point"]
    end

    subgraph Fragments["Fragments"]
        security["security.prs<br/>Security rules"]
        testing["testing.prs<br/>Testing standards"]
        docs["documentation.prs<br/>Doc guidelines"]
    end

    subgraph Registry["Registry"]
        base["@company/base<br/>Organization defaults"]
    end

    main --> security
    main --> testing
    main --> docs
    main --> base

File Organization Patterns

Pattern 1: Responsibility-Based Split

Split by type of concern:

.promptscript/
├── project.prs           # Main entry, imports fragments
├── fragments/
│   ├── security.prs      # Security restrictions
│   ├── testing.prs       # Testing standards
│   ├── documentation.prs # Doc guidelines
│   └── code-style.prs    # Coding conventions
└── skills/
    ├── review.prs        # Code review skill
    └── deploy.prs        # Deployment skill

Main entry file:

# .promptscript/project.prs
@meta {
  id: "my-project"
  syntax: "1.0.0"
}

@inherit @company/frontend

# Import responsibility fragments
@use ./fragments/security
@use ./fragments/testing
@use ./fragments/documentation
@use ./fragments/code-style

@identity {
  """
  You are a senior developer on the Customer Portal team.
  """
}

@context {
  project: "Customer Portal"
  repository: "github.com/company/customer-portal"
}

Try in Playground

Security fragment:

# .promptscript/fragments/security.prs
@meta {
  id: "security-fragment"
  syntax: "1.0.0"
}

@restrictions {
  - "Never expose API keys or secrets in code"
  - "Never commit credentials to version control"
  - "Always validate and sanitize user input"
  - "Never disable security features"
  - "Use parameterized queries for database access"
}

@standards {
  security: [
    "Authentication required",
    "Use RBAC for authorization",
    "Input validation required",
    "Output encoding required"
  ]
}

Try in Playground

Testing fragment:

# .promptscript/fragments/testing.prs
@meta {
  id: "testing-fragment"
  syntax: "1.5.0"
}

@standards {
  testing: [
    "Use vitest as test framework",
    "Maintain 80% code coverage",
    "Write unit, integration, and e2e tests",
    "Use MSW for API mocking"
  ]
}

@shortcuts {
  "/test": {
    description: "Write project tests"
    content: """
      Use Vitest, Testing Library for DOM behavior, and MSW for API mocking.
    """
  }
  "/coverage": {
    description: "Check test coverage"
    content: "Report coverage gaps and recommend missing behavioral tests."
  }
}

Try in Playground

Pattern 2: Feature-Based Split

For large projects, split by feature area:

.promptscript/
├── project.prs           # Main entry
├── features/
│   ├── auth.prs          # Authentication module
│   ├── payments.prs      # Payment processing
│   ├── notifications.prs # Notification system
│   └── analytics.prs     # Analytics features
└── shared/
    ├── api.prs           # API conventions
    └── database.prs      # Database patterns

Auth feature:

# .promptscript/features/auth.prs
@meta {
  id: "auth-feature"
  syntax: "1.0.0"
}

@context {
  """
  ## Authentication Module

  - OAuth 2.0 with PKCE
  - JWT tokens with refresh rotation
  - SSO via SAML 2.0
  """
}

@standards {
  auth: [
    "Store tokens in httpOnly cookies",
    "Session timeout: 3600 seconds",
    "Enable refresh token rotation"
  ]
}

@knowledge {
  """
  ## Auth API Endpoints

  - POST /auth/login - User login
  - POST /auth/logout - User logout
  - POST /auth/refresh - Refresh token
  - GET /auth/me - Current user info
  """
}

Try in Playground

Pattern 3: Environment-Based Split

Different configurations for different environments:

.promptscript/
├── project.prs           # Main entry
├── environments/
│   ├── development.prs   # Dev-specific settings
│   ├── staging.prs       # Staging settings
│   └── production.prs    # Production settings
└── fragments/
    └── ...               # Shared fragments

Development environment:

# .promptscript/environments/development.prs
@meta {
  id: "dev-environment"
  syntax: "1.0.0"
}

@context {
  environment: development

  """
  ## Development Environment

  - Hot reloading enabled
  - Debug logging on
  - Mock services available
  """
}

@local {
  """
  Local development setup:
  - API: http://localhost:8080
  - Database: local PostgreSQL
  - Redis: local instance
  """
}

Try in Playground

Using @use for Composition

The @use directive imports and merges content from other files - like CSS imports or mixins.

Basic Import

# Import fragment - blocks are merged into your file
@use ./fragments/security

# Import from registry
@use @company/standards/testing

# Import with alias - for @extend access
@use @core/guards/security as sec

Try in Playground

How @use Merges Content

When you @use a file, all blocks from the source are merged into your file:

Content Type Merge Behavior
Text content Concatenated (source + target), identical content deduplicated
Object content Deep merged (imported source wins same-shape key conflicts)
Array content Unique concatenation (preserves order, dedupes)
Mixed content Text concatenated, properties deep merged

When block shapes conflict, existing target body wins. Under syntax 1.5.0, later local blocks and modification operations apply after an earlier import.

Example:

# security.prs
@restrictions {
  - "Never expose API keys"
}

# project.prs
@use ./security

@restrictions {
  - "Follow OWASP guidelines"
}

# Result: @restrictions contains both items

Try in Playground

Alias for Selective Extension

When you need to modify imported content rather than just merge it, use an alias:

@use @core/typescript as ts

# Extend specific imported blocks
@extend ts.standards {
  testing: { coverage: 95 }
}

Try in Playground

Without alias, blocks are simply merged. With alias, you get both:

  • Blocks merged into your file
  • Prefixed blocks available for @extend access

Import Order Matters

Imports are processed in order. For same-name object fields, each later imported source wins:

@use ./fragments/base         # @shortcuts has /test -> "Run unit tests"
@use ./fragments/advanced     # @shortcuts has /test -> "Run full suite"
# Result: /test -> "Run full suite"

Try in Playground

For object properties, later imports override:

@use ./base     # @standards.coverage = 80
@use ./strict   # @standards.coverage = 95
# Result: coverage = 95 (later imported source wins)

Try in Playground

See Composition and Precedence for the normative matrix and resolved declaration-order examples.

Registry vs Local Fragments

When to Use Registry

  • Shared across projects: Company-wide standards
  • Versioned: Need version pinning
  • Team-wide: Team conventions
  • Reusable: Generic patterns
@inherit @company/frontend@1.0.0
@use @core/security
@use @fragments/testing

Try in Playground

When to Use Local Fragments

  • Project-specific: Only relevant to this project
  • Frequently changing: Rapid iteration needed
  • Experimental: Testing new patterns
@use ./fragments/project-specific
@use ./features/checkout

Try in Playground

Best Practices

1. Keep Fragments Focused

Each fragment should have a single responsibility:

# ✅ Good: Single responsibility
# security.prs - Only security rules
# testing.prs - Only testing standards

# ❌ Bad: Mixed concerns
# everything.prs - Security, testing, docs, etc.

Try in Playground

2. Use Meaningful Names

# ✅ Good
@use ./fragments/api-conventions
@use ./fragments/error-handling

# ❌ Bad
@use ./fragments/stuff
@use ./fragments/misc

Try in Playground

3. Document Dependencies

Add comments explaining why fragments are needed:

# Security compliance required for all ACME projects
@use @core/security

# Frontend testing patterns from design system
@use @acme-ui/testing

# Project-specific payment integrations
@use ./features/stripe-integration

Try in Playground

4. Keep Import Lists Organized

# Organization/team base
@inherit @company/frontend

# Core standards (alphabetical)
@use @core/compliance
@use @core/security

# Team fragments
@use @frontend/accessibility
@use @frontend/performance

# Project fragments
@use ./fragments/api
@use ./fragments/testing

Try in Playground

5. Avoid Deep Nesting

# ✅ Good: Flat structure
@use ./fragments/security
@use ./fragments/testing

# ❌ Bad: Deep nesting
@use ./fragments/standards/code/security/v2/latest

Try in Playground

Example: Complete Multi-File Setup

Directory Structure

my-project/
├── .promptscript/
│   ├── project.prs
│   └── fragments/
│       ├── security.prs
│       ├── testing.prs
│       ├── api-standards.prs
│       └── documentation.prs
├── registry/               # Local registry (optional)
│   └── @team/
│       └── base.prs
└── promptscript.yaml

Main Entry (project.prs)

@meta {
  id: "my-project"
  syntax: "1.0.0"
}

# Inherit team base configuration
@inherit @team/base

# Import project fragments
@use ./fragments/security
@use ./fragments/testing
@use ./fragments/api-standards
@use ./fragments/documentation

@identity {
  """
  You are a senior full-stack developer working on My Project.
  You follow team conventions and project-specific patterns.
  """
}

@context {
  project: "My Project"
  team: "Platform"

  """
  A microservices-based platform for data processing.

  Tech Stack:
  - Backend: Node.js, TypeScript, NestJS
  - Frontend: React, TypeScript, Vite
  - Database: PostgreSQL, Redis
  - Infrastructure: Kubernetes, AWS
  """
}

@shortcuts {
  "/start": "Initialize development environment"
  "/deploy": "Deploy to staging environment"
}

Try in Playground

Configuration (promptscript.yaml)

id: my-project
syntax: '1.4.0'

input:
  entry: .promptscript/project.prs
  include:
    - '.promptscript/**/*.prs'

registry:
  path: ./registry

targets:
  - github
  - claude
  - cursor

Compiled Output

When you run prs compile, all fragments are merged into a single output per target. The multi-file organization is a source-level concern only.

prs compile
# Output:
# ✓ .github/copilot-instructions.md
# ✓ CLAUDE.md
# ✓ .cursor/rules/project.mdc

Debugging Multi-File Setup

View Resolved Configuration

prs compile --dry-run --verbose

This shows how all fragments merge together.

Validate All Files

prs validate

Validates main file and all imported fragments.

Check Import Resolution

If imports fail:

  1. Check file paths are correct
  2. Verify registry path in promptscript.yaml
  3. Ensure @meta.id matches expected paths

Next Steps