Variables

Variables provide a way to define reusable values in your configuration without requiring user interaction. Unlike prompts, which collect user input interactively, variables are resolved automatically from static values or executable commands.

Overview

Variables are useful for:

Key Differences from Prompts

Feature Prompts Variables
User Interaction Yes (interactive) No (automatic)
Value Source User input (with optional defaults) Static values or command execution
Timing Collected before tasks run Resolved before tasks run
Use Case User preferences, project configuration System info, computed values

Variable Definition

Variables are defined at the root level of the configuration and are available to all tasks.

Root-Level Variables

Root-level variables are defined in the variables array at the root of your configuration. They are available to all tasks.

{
  "$schema": "https://unpkg.com/@pixpilot/scaffoldfy/schema",
  "variables": [
    {
      "id": "currentYear",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(new Date().getFullYear())\""
      }
    },
    {
      "id": "defaultLicense",
      "value": "MIT"
    }
  ],
  "tasks": [...]
}

In this example:

Variable Types

Static Values

Static variables have fixed values that donโ€™t change:

{
  "id": "appName",
  "value": "MyApp"
}

You can also use explicit static type:

{
  "id": "portNumber",
  "value": {
    "type": "static",
    "value": 3000
  }
}

Executable Values

Executable variables run shell commands to get their value:

{
  "id": "gitUserName",
  "value": {
    "type": "exec",
    "value": "git config user.name"
  }
}

Auto-Parsing

Command output is automatically parsed as:

Examples:

{
  "variables": [
    {
      "id": "packageInfo",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(JSON.stringify({name: 'test', version: '1.0.0'}))\""
      }
    },
    {
      "id": "cpuCount",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(require('os').cpus().length)\""
      }
    },
    {
      "id": "isCI",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(process.env.CI === 'true')\""
      }
    }
  ]
}

Script File Execution (exec-file)

Execute script files to get variable values. This is useful for running complex logic in separate script files rather than inline shell commands.

{
  "id": "projectVersion",
  "value": {
    "type": "exec-file",
    "file": "scripts/get-version.js"
  }
}

Script File Properties

Property Type Required Description
type string Yes Must be "exec-file"
file string Yes Path to script file (local or remote URL). Supports `` interpolation.
runtime string No Runtime to use (node, bash, sh, pwsh, powershell). Auto-detected if omitted.
args string[] No Arguments to pass to the script. Each supports `` interpolation.
parameters Record<string,string> No Environment variables for the script. Values support `` interpolation.
cwd string No Working directory. Supports `` interpolation.

Runtime Auto-Detection

If runtime is not specified, itโ€™s automatically detected from the file extension:

Auto-Parsing

Script output is automatically parsed as:

Examples

Basic Script Execution:

{
  "id": "appVersion",
  "value": {
    "type": "exec-file",
    "file": "scripts/version.js"
  }
}

Script with Arguments:

{
  "id": "buildNumber",
  "value": {
    "type": "exec-file",
    "file": "scripts/get-build.js",
    "args": ["--branch=", "--env=production"]
  }
}

Script with Environment Variables:

{
  "id": "envConfig",
  "value": {
    "type": "exec-file",
    "file": "scripts/load-config.js",
    "parameters": {
      "CONFIG_PATH": "",
      "ENVIRONMENT": ""
    }
  }
}

Bash Script:

{
  "id": "currentBranch",
  "value": {
    "type": "exec-file",
    "file": "scripts/git-branch.sh"
    // Runtime auto-detected as 'bash' from .sh extension
  }
}

Remote Script:

{
  "id": "latestVersion",
  "value": {
    "type": "exec-file",
    "file": "https://example.com/scripts/latest-version.js"
  }
}

See Exec File Plugin documentation for more details.

Conditional Values

Conditional variables evaluate an expression and return different values based on the result. This is useful for deriving values based on other variables or prompts:

{
  "id": "pixpilot_project",
  "value": {
    "type": "conditional",
    "condition": "repoOwner === 'pixpilot' || orgName === 'pixpilot'",
    "ifTrue": true,
    "ifFalse": false
  }
}

Conditional Variable Properties

Property Type Required Description
type string Yes Must be "conditional"
condition string Yes JavaScript expression to evaluate
ifTrue any Yes Value to use if condition evaluates to true. For interpolation, use { type: 'interpolate', value: '' }
ifFalse any Yes Value to use if condition evaluates to false. For interpolation, use { type: 'interpolate', value: '' }

Condition Expressions

The condition field supports JavaScript expressions that can reference:

Examples:

{
  "variables": [
    {
      "id": "useTypeScript",
      "value": {
        "type": "conditional",
        "condition": "language === 'typescript'",
        "ifTrue": true,
        "ifFalse": false
      }
    },
    {
      "id": "configFile",
      "value": {
        "type": "conditional",
        "condition": "useTypeScript",
        "ifTrue": "tsconfig.json",
        "ifFalse": "jsconfig.json"
      }
    },
    {
      "id": "isMonorepo",
      "value": {
        "type": "conditional",
        "condition": "projectType === 'monorepo' || hasWorkspaces",
        "ifTrue": true,
        "ifFalse": false
      }
    },
    {
      "id": "securityEmail",
      "value": {
        "type": "conditional",
        "condition": "orgName === 'myorg'",
        "ifTrue": "security@myorg.com",
        "ifFalse": {
          "type": "interpolate",
          "value": ""
        }
      }
    }
  ]
}

In the last example, if orgName is "myorg", it uses a static email. Otherwise, it interpolates the value from the defaultEmail variable.

Evaluation Timing

Conditional variables are resolved in two passes:

  1. First pass (before prompts): Non-conditional variables are resolved
  2. Second pass (after prompts): Conditional variables are resolved with access to prompt values

This allows conditional variables to depend on user input from prompts.

Dynamic Configuration Enabling

Conditional variables are particularly useful with configuration-level enabled fields to dynamically enable/disable entire configurations:

{
  "name": "pixpilot-copilot-instructions",
  "dependencies": ["project-info", "pixpilot-info"],
  "enabled": {
    "type": "condition",
    "value": "pixpilot_project == true"
  },
  "tasks": []
}

Where pixpilot-info configuration defines:

{
  "name": "pixpilot-info",
  "dependencies": ["project-info"],
  "variables": [
    {
      "id": "pixpilot_project",
      "value": {
        "type": "conditional",
        "condition": "repoOwner === 'pixpilot' || orgName === 'pixpilot'",
        "ifTrue": true,
        "ifFalse": false
      }
    }
  ]
}

Interpolate Type Values

Interpolate Type Values allow you to explicitly mark a variable value as a template string that should be interpolated with previously resolved variables and prompts. This is useful when you want to compose variable values from other variables or prompts.

Basic Configuration Example

{
  "variables": [
    {
      "id": "projectName",
      "value": "my-app"
    },
    {
      "id": "apiServiceName",
      "value": {
        "type": "interpolate",
        "value": "-api"
      }
    }
  ]
}

The apiServiceName variable will have the value โ€œmy-app-apiโ€.

Composing from Multiple Variables

{
  "variables": [
    {
      "id": "repoOwner",
      "value": "myorg"
    },
    {
      "id": "repoName",
      "value": "my-repo"
    },
    {
      "id": "repoUrl",
      "value": {
        "type": "interpolate",
        "value": "https://github.com//"
      }
    }
  ]
}

Referencing Prompts in Variable Values

Variables can reference values from prompts (since prompts are resolved before variables in the second pass):

{
  "prompts": [
    {
      "id": "organizationName",
      "type": "input",
      "message": "Organization name"
    },
    {
      "id": "packageName",
      "type": "input",
      "message": "Package name"
    }
  ],
  "variables": [
    {
      "id": "npmPackageName",
      "value": {
        "type": "interpolate",
        "value": "@/"
      }
    }
  ]
}

Using Interpolate in Variables

To use interpolation in variable values, you must use the explicit type: "interpolate" format:

{
  "id": "fullName",
  "value": {
    "type": "interpolate",
    "value": " "
  }
}

Direct strings with `` placeholders (without the explicit type property) will NOT be interpolated and will be treated as static strings.

// This will NOT be interpolated - treated as a static string
{
  "id": "fullName",
  "value": " " // Results in literal " "
}

} }


**When to use explicit `type: "interpolate"`:**

- For clarity and self-documentation
- When you want to be explicit about the interpolation behavior
- In complex configurations where intent should be clear

**When to use simple string:**

- For quick, simple cases
- When brevity is preferred

#### Sequential Resolution

Variables are resolved sequentially, so an interpolate variable can only reference variables or prompts that were resolved before it:

โœ… **This works:**

```json
{
  "variables": [
    {
      "id": "firstName",
      "value": "John"
    },
    {
      "id": "lastName",
      "value": "Doe"
    },
    {
      "id": "fullName",
      "value": {
        "type": "interpolate",
        "value": " "
      }
    }
  ]
}

โŒ This wonโ€™t work:

{
  "variables": [
    {
      "id": "fullName",
      "value": {
        "type": "interpolate",
        "value": " "
      }
    },
    {
      "id": "firstName",
      "value": "John"
    }
  ]
}

Using Variables in Tasks

Variables are available in all configuration contexts using `` syntax:

In Configuration Files

{
  "type": "write",
  "config": {
    "file": "LICENSE",
    "template": "MIT License\n\nCopyright (c)  "
  }
}

In JSON Updates

{
  "type": "update-json",
  "config": {
    "file": "package.json",
    "updates": {
      "author": " <>",
      "year": ""
    }
  }
}

In Commands

{
  "type": "exec",
  "config": {
    "command": "echo \"Building project  version \""
  }
}

Common Variable Examples

Git Information

{
  "variables": [
    {
      "id": "gitUserName",
      "value": {
        "type": "exec",
        "value": "git config user.name"
      }
    },
    {
      "id": "gitUserEmail",
      "value": {
        "type": "exec",
        "value": "git config user.email"
      }
    },
    {
      "id": "gitRepoUrl",
      "value": {
        "type": "exec",
        "value": "git config --get remote.origin.url"
      }
    }
  ]
}

System Information

{
  "variables": [
    {
      "id": "nodeVersion",
      "value": {
        "type": "exec",
        "value": "node --version"
      }
    },
    {
      "id": "npmVersion",
      "value": {
        "type": "exec",
        "value": "npm --version"
      }
    },
    {
      "id": "platform",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(process.platform)\""
      }
    }
  ]
}

Date and Time

{
  "variables": [
    {
      "id": "currentYear",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(new Date().getFullYear())\""
      }
    },
    {
      "id": "timestamp",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(new Date().toISOString())\""
      }
    }
  ]
}

Combining Variables and Prompts

Variables and prompts work together seamlessly. Use top-level variables for automatic system information and top-level prompts for user input:

{
  "variables": [
    {
      "id": "currentYear",
      "value": {
        "type": "exec",
        "value": "node -e \"console.log(new Date().getFullYear())\""
      }
    },
    {
      "id": "gitUserName",
      "value": {
        "type": "exec",
        "value": "git config user.name"
      }
    }
  ],
  "prompts": [
    {
      "id": "projectName",
      "type": "input",
      "message": "Project name?",
      "required": true
    }
  ],
  "tasks": [
    {
      "id": "setup-project",
      "name": "Setup Project",
      "type": "update-json",
      "config": {
        "file": "package.json",
        "updates": {
          "name": "",
          "author": "",
          "year": ""
        }
      }
    }
  ]
}

In this example:

Variable Scoping

Root-Level Variables

Error Handling

Failed Commands

If an executable variableโ€™s command fails:

{
  "id": "mayFail",
  "value": {
    "type": "exec",
    "value": "nonexistent-command"
  }
}

Command Timeout

Commands have a 10-second timeout. If a command takes longer:

Best Practices

1. Use Variables for Non-Interactive Values

// โœ… Good: System information
{
  "id": "nodeVersion",
  "value": {
    "type": "exec",
    "value": "node --version"
  }
}

// โŒ Bad: User preferences (use prompts instead)
{
  "id": "projectName",
  "value": "my-project"
}

2. Validate Variable IDs

Variable IDs must be valid JavaScript identifiers:

// โœ… Valid
{ "id": "myVar" }
{ "id": "my_var_123" }
{ "id": "$specialVar" }

// โŒ Invalid
{ "id": "my-var" }
{ "id": "my.var" }
{ "id": "123var" }

3. Keep Commands Simple

// โœ… Good: Simple, fast commands
{
  "value": {
    "type": "exec",
    "value": "git config user.name"
  }
}

// โš ๏ธ Avoid: Complex, slow operations
{
  "value": {
    "type": "exec",
    "value": "npm list --depth=0 --json | node -e '...complex parsing...'"
  }
}

4. Provide Defaults

Combine with prompts for fallback values:

{
  "prompts": [
    {
      "id": "author",
      "type": "input",
      "message": "Author name",
      "default": {
        "type": "exec",
        "value": "git config user.name"
      }
    }
  ]
}

See Also