Template Syntax

AgileBuilder's EJS rendering syntax — interpolation, conditionals, loops, the default % delimiter, the 6 built-in helpers, {{var}} file-path rendering, and how binary files are handled.

AgileBuilder renders template file contents with EJS. Whenever variables.enabled: true and a file matches filePatterns, placeholders are replaced with variable values at generation time. This page assumes the following config:

variables:
  enabled: true
  delimiter: "%"

Interpolation

The default delimiter is %, so tags are combinations of <% and %>:

SyntaxBehavior
<%= value %>Outputs the value, HTML-escaped (" becomes &#34;, etc.).
<%- value %>Outputs the value unescaped.
<% ... %>Executes JavaScript without output.

When generating code, JSON, or YAML, values often contain quotes — prefer <%- %> so escaping does not pollute the output:

// Template
"name": "<%- appName %>"

// Generated with appName = order-service
"name": "order-service"

Change variables.delimiter to switch delimiters — with delimiter: "?" you would write <?= appName ?>.

Referencing an undefined variable fails with TEMPLATE_VARS_MISSING and suggests filling it via --var / --vars / --interactive. Template typos therefore surface immediately at generation time instead of silently producing empty strings.

Conditionals

<% if (useAuth) { %>
import { auth } from './auth';
<% } %>

Pairs best with a confirm-type question:

inquirerQuestions:
  - name: useAuth
    type: confirm
    message: Enable authentication?
    default: true

Loops

<% features.forEach(function (feature) { %>
- <%- feature %>
<% }) %>

With features set to ["auth", "logging"] this generates:

- auth
- logging

Multi-select variables (from checkbox-type questions) arrive as arrays — ideal for driving loops.

Built-in Helpers

Six naming-convention helpers are available both as top-level functions and under the helpers namespace; the two forms are equivalent:

<%= pascalCase(appName) %>
<%= helpers.kebabCase(appName) %>

With appName = "order-service":

HelperCallOutput
camelCase<%= camelCase(appName) %>orderService
pascalCase<%= pascalCase(appName) %>OrderService
kebabCase<%= kebabCase(camelCase(appName)) %>order-service
snakeCase<%= snakeCase(pascalCase(appName)) %>order_service
uppercase<%= uppercase(appName) %>ORDER-SERVICE
lowercase<%= lowercase("ORDER-Service") %>order-service

Typical use — derive each ecosystem's naming convention from a single variable:

package.json:  "name": "<%= appName %>"
Class name:    export class <%= pascalCase(appName) %>Service {}
Constant:      const <%= camelCase(appName) %>Version = "1.0.0";

File-Path Rendering

When variables.enabled: true, file and directory paths are rendered too. Prefer the {{variableName}} notation inside paths:

src/{{appName}}/index.ts

With appName = order-service this becomes src/order-service/index.ts.

Path rendering rules:

  • {{ }} supports dotted access, e.g. {{user.name}}; a missing variable renders as an empty string.
  • Full EJS expressions also work in paths (e.g. <%= pascalCase(appName) %>) — paths are processed for {{ }} first, then EJS-rendered.
  • Path rendering depends only on variables.enabled. filePatterns controls whether file contents are rendered, not paths.

Files That Are Never Rendered

The following files are excluded from content rendering:

  • Binary files: the CLI checks for NUL bytes near the start of a file; binaries are copied verbatim. Images, fonts, and build artifacts are safe to keep in templates.
  • Config files: .agilebuilder.config.yaml / .agilebuilder.config.json are never copied to the target directory.
  • The .git directory: skipped by default; kept only when --keep-git is passed.
  • Files excluded by filePatterns: copied as-is (see File Patterns and Hooks).

A Complete Snippet

Template file src/{{appName}}.config.ts:

export const serviceName = "<%- appName %>";
export const serviceClass = "<%- pascalCase(appName) %>Service";
<% if (useAuth) { %>
export const authEnabled = true;
<% } %>

Command and result:

ag create 1 --target ./out --var appName=order-service --var useAuth=true
// out/src/order-service.config.ts
export const serviceName = "order-service";
export const serviceClass = "OrderServiceService";
export const authEnabled = true;

What's Next