Skip to content

Inheritance Guide

Learn how to build scalable, maintainable instruction hierarchies using PromptScript's inheritance system.

Overview

PromptScript uses single inheritance to build hierarchical instruction sets:

flowchart TD
    A["@org/base<br/>Organization defaults"] --> B["@org/frontend<br/>Frontend team"]
    A --> C["@org/backend<br/>Backend team"]
    B --> D["project-web<br/>Web application"]
    B --> E["project-mobile<br/>Mobile app"]
    C --> F["project-api<br/>API service"]

Basic Inheritance

Use @inherit to extend another PromptScript file:

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

@inherit @company/frontend-team

Try in Playground

The child inherits all blocks from the parent, which can then be extended.

Registry Structure

Organize your registry with namespaces:

registry/
├── @company/
│   ├── base.prs           # Organization base
│   ├── frontend.prs       # Frontend team
│   ├── backend.prs        # Backend team
│   └── mobile.prs         # Mobile team
├── @core/
│   ├── security.prs       # Security standards
│   └── compliance.prs     # Compliance rules
└── @fragments/
    ├── testing.prs        # Testing patterns
    └── logging.prs        # Logging standards

Merge Behavior

Different blocks merge differently during inheritance:

Text Blocks (Concatenate)

@identity, @knowledge, and text content in other blocks concatenate:

# parent.prs
@meta {
  id: "parent"
  syntax: "1.0.0"
}

@identity {
  """
  You are a helpful assistant.
  """
}

Try in Playground

# child.prs
@meta {
  id: "child"
  syntax: "1.0.0"
}

@inherit ./parent

@identity {
  """
  You specialize in React development.
  """
}

Try in Playground

## Identity

You are a helpful assistant.

You specialize in React development.

Objects (Deep Merge)

@standards and object properties deep merge:

# parent.prs
@meta {
  id: "parent"
  syntax: "1.0.0"
}

@standards {
  code: ["Follow clean code principles", "Testing required"]
}

Try in Playground

# child.prs
@meta {
  id: "child"
  syntax: "1.0.0"
}

@inherit ./parent

@standards {
  code: ["Use React framework", "80% test coverage required"]
}

Try in Playground

code:
  # Arrays are concatenated (parent first, then child)
  - Follow clean code principles
  - Testing required
  - Use React framework
  - 80% test coverage required

Arrays (Concatenate)

@restrictions and array values concatenate:

# parent.prs
@meta {
  id: "parent"
  syntax: "1.0.0"
}

@restrictions {
  - "Never expose secrets"
}

Try in Playground

# child.prs
@meta {
  id: "child"
  syntax: "1.0.0"
}

@inherit ./parent

@restrictions {
  - "Always use TypeScript"
}

Try in Playground

## Restrictions

- Never expose secrets
- Always use TypeScript

Shortcuts (Override)

@shortcuts entries override by key:

# parent.prs
@meta {
  id: "parent"
  syntax: "1.0.0"
}

@shortcuts {
  "/test": "Write unit tests"
  "/docs": "Generate documentation"
}

Try in Playground

# child.prs
@meta {
  id: "child"
  syntax: "1.0.0"
}

@inherit ./parent

@shortcuts {
  "/test": "Write tests with Vitest"
  "/lint": "Run ESLint"
}

Try in Playground

## Shortcuts

| Command | Description |
|---------|-------------|
| /test | Write tests with Vitest |
| /docs | Generate documentation |
| /lint | Run ESLint |

Using @extend

The @extend block modifies specific paths:

Extending Top-Level Blocks

@inherit @company/base

# Add to identity
@extend identity {
  """
  Additional identity context.
  """
}

Try in Playground

Extending Nested Paths

@inherit @company/base

# Modify nested structure
@extend standards.code.testing {
  e2e: required
  coverage: 90
}

Try in Playground

Multiple Extensions

@inherit @company/base

@extend identity {
  """
  You are a frontend expert.
  """
}

@extend standards.code {
  framework: "react"
}

@extend restrictions {
  - "Use functional components only"
}

Try in Playground

Composition with @use

Use @use to import and merge fragments (like mixins):

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

@inherit @company/frontend

# Import fragments - blocks are merged into your file
@use @core/security
@use @core/compliance
@use @fragments/testing

# With alias - also available for @extend
@use @fragments/api-standards as api

Try in Playground

How @use Differs from @inherit

Feature @inherit @use
Quantity Single parent only Multiple allowed
Semantics "IS-A" (this project IS a TypeScript library) "HAS-A" (this project HAS security standards)
Purpose Define fundamental project type Add optional capabilities
Merge precedence Child overrides parent Later @use overrides earlier
@extend support Always available Only with alias

When to Use Which

Use @inherit for:

  • Defining your project's fundamental type (library, backend, frontend)
  • Building organizational hierarchies (base → team → project)
  • When you want a single, clear inheritance chain
# This project IS a TypeScript library
@inherit @stacks/typescript-lib

Try in Playground

Use @use for:

  • Adding optional capabilities (security, testing, quality)
  • Mixing in reusable fragments
  • When you need multiple imports
# This project HAS these capabilities
@use @core/security
@use @core/quality
@use @fragments/testing

Try in Playground

Merge Precedence

When the same property exists in multiple sources:

@inherit @stacks/typescript-lib    # Base values
@use @core/security                # Overrides @inherit for same keys
@use @core/quality                 # Overrides earlier @use for same keys
@standards { ... }                 # Local values override everything

Rule: Later sources override earlier sources for the same keys.

flowchart LR
    A["@inherit<br/>(base)"] --> B["@use #1"] --> C["@use #2"] --> D["Local<br/>(wins)"]

Fragment Files

Create reusable fragments:

# @fragments/testing.prs
@meta {
  id: "@fragments/testing"
  syntax: "1.0.0"
}

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

@shortcuts {
  "/test": "Write comprehensive tests"
  "/coverage": "Check test coverage"
}

Try in Playground

When imported with @use @fragments/testing, these blocks are merged directly into your file.

Best Practices

1. Keep Base Configurations Minimal

Organization base should include only universal standards:

# @company/base.prs
@meta {
  id: "@company/base"
  syntax: "1.0.0"
}

@identity {
  """
  You are an AI assistant at ACME Corp.
  Follow company guidelines and best practices.
  """
}

@restrictions {
  - "Never expose credentials"
  - "Follow data protection policies"
}

Try in Playground

2. Use Team Configurations for Specialization

# @company/frontend.prs
@meta {
  id: "@company/frontend"
  syntax: "1.0.0"
}

@inherit @company/base

@identity {
  """
  You specialize in frontend development.
  """
}

@context {
  """
  Tech stack: React, TypeScript, Vite
  """
}

Try in Playground

3. Project Configurations for Specifics

# project.prs
@meta {
  id: "checkout-app"
  syntax: "1.0.0"
}

@inherit @company/frontend

@context {
  project: "Checkout Application"

  """
  E-commerce checkout flow with Stripe integration.
  """
}

Try in Playground

4. Version Your Registry

Use semantic versioning for registry files:

@inherit @company/frontend@1.0.0

Try in Playground

5. Document Inheritance Chains

Include comments explaining the hierarchy:

# Inheritance chain:
# @company/base → @company/frontend → this file
@inherit @company/frontend

Try in Playground

Common Patterns

Platform-Specific Configurations

flowchart TD
    A["@company/base"] --> B["@company/web"]
    A --> C["@company/mobile"]
    A --> D["@company/backend"]

    B --> E["@company/web-react"]
    B --> F["@company/web-vue"]

    C --> G["@company/mobile-ios"]
    C --> H["@company/mobile-android"]

Shared Standards with Team Overrides

# Use shared security, override team-specific
@inherit @company/frontend
@use @core/security
@use @core/compliance

@extend standards.security {
  additionalRules: ["CSP headers"]
}

Try in Playground

Environment-Specific Extensions

@inherit @company/frontend

@context {
  environment: production
}

@extend restrictions {
  - "No console.log statements"
  - "No debug code"
}

Try in Playground

Parameterized Inheritance (Templates)

PromptScript supports parameterized inheritance, allowing you to create reusable templates with configurable values. This is similar to generics in programming languages or Handlebars-like templates.

Defining Parameters

Define parameters in the @meta block using the params field:

# @stacks/typescript-lib.prs
@meta {
  id: "@stacks/typescript-lib"
  syntax: "1.0.0"
  params: {
    projectName: string
    runtime: string = "node18"
    strict?: boolean
    testFramework: enum("vitest", "jest", "mocha") = "vitest"
  }
}

@project {
  name: {{projectName}}
  runtime: {{runtime}}
}

@standards {
  testing: ["Use {{testFramework}} for all tests"]
}

Parameter Types

Type Syntax Description
string name: string Text value
number count: number Numeric value
boolean enabled: boolean True or false
enum mode: enum("a", "b") One of specified values

Parameter Modifiers

Modifier Syntax Description
Required name: string Must be provided
Optional name?: string Can be omitted (value is undefined)
Default name: string = "value" Uses default if not provided

Passing Parameters

Pass parameters when using @inherit or @use:

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

@inherit @stacks/typescript-lib(projectName: "my-app", runtime: "node20")

Or with @use:

@use ./fragments/testing(framework: "vitest", coverage: 90)

Template Variables

Use {{variable}} syntax to reference parameters in content:

@meta {
  id: "template"
  syntax: "1.0.0"
  params: {
    projectName: string
    author: string = "Team"
  }
}

@identity {
  """
  You are working on {{projectName}}.
  This project is maintained by {{author}}.
  """
}

@project {
  name: {{projectName}}
  maintainer: {{author}}
}

Complete Example

# @stacks/react-app.prs
@meta {
  id: "@stacks/react-app"
  syntax: "1.0.0"
  params: {
    projectName: string
    port: number = 3000
    strict: boolean = true
  }
}

@identity {
  """
  You are a React developer working on {{projectName}}.
  """
}

@context {
  project: {{projectName}}
  devServer: "http://localhost:{{port}}"
  strictMode: {{strict}}
}

@standards {
  code: ["TypeScript strict mode enabled"]
}
# project.prs
@meta {
  id: "checkout-app"
  syntax: "1.0.0"
}

@inherit @stacks/react-app(projectName: "Checkout App", port: 8080)

@identity {
  """
  You specialize in e-commerce checkout flows.
  """
}
## Identity

You are a React developer working on Checkout App.

You specialize in e-commerce checkout flows.

## Context

- project: Checkout App
- devServer: http://localhost:8080
- strictMode: true

## Standards

### Code
- TypeScript strict mode enabled

Template Variables vs Environment Variables

PromptScript has two interpolation mechanisms:

Feature Syntax Resolved At Purpose
Environment Variables ${VAR} Parse time System configuration, secrets
Template Variables {{var}} Resolve time Parameterized templates
@context {
  # Environment variable - resolved during parsing
  apiKey: ${API_KEY}

  # Template variable - resolved during inheritance
  project: {{projectName}}
}

Validation Errors

PromptScript validates parameters at compile time:

Missing required parameter:

Error: Missing required parameter 'projectName' for template '@stacks/react-app'

Unknown parameter:

Error: Unknown parameter 'unknownParam' for template '@stacks/react-app'.
Available parameters: projectName, port, strict

Type mismatch:

Error: Type mismatch for parameter 'port': expected number, got string

Invalid enum value:

Error: Type mismatch for parameter 'mode': expected enum("dev", "prod"), got "staging"

Best Practices

  1. Use descriptive parameter names - projectName instead of name
  2. Provide sensible defaults - Reduce required parameters
  3. Document parameters - Add comments explaining each parameter
  4. Validate with enums - Use enums for constrained choices
  5. Keep templates focused - One purpose per template
@meta {
  id: "@stacks/api-service"
  syntax: "1.0.0"
  params: {
    # Name of the API service
    serviceName: string

    # HTTP port for the service (default: 3000)
    port: number = 3000

    # Environment mode
    mode: enum("development", "staging", "production") = "development"
  }
}

Debugging Inheritance

View Resolved Configuration

prs compile --dry-run --verbose

Validate Inheritance Chain

prs validate --verbose

Common Issues

Circular inheritance detected:

Error: Circular inheritance: a → b → a

Ensure no circular references in your inheritance chain.

Parent not found:

Error: Cannot resolve @company/unknown

Check registry configuration and file paths.

Version mismatch:

Warning: Requested @company/base@2.0.0, found 1.5.0

Update version constraints or registry.