File Patterns and Hooks
The three filePatterns matching modes and minimatch rules, the after_write hook configuration and execution model, the --allow-hooks authorization mechanism, errorHandling strategies, and the security design behind them.
This page covers the two "fine control" capabilities of the generation pipeline: filePatterns, which decides which files get rendered, and hooks, which run scripts after files are written. Both are configured in .agilebuilder.config.yaml.
filePatterns: File Matching Rules
variables.filePatterns decides which files have their contents rendered with EJS:
variables:
enabled: true
filePatterns:
mode: include
patterns:
- "**/*.ts"
- "**/*.json"
- "README.md"
The Three Modes
| mode | Behavior | Typical use case |
|---|---|---|
all | Render every non-binary file. | Small templates with variables everywhere. |
include | Render only files matching at least one pattern. | Templates with many docs/examples that must not be rendered. |
exclude | Render files matching no pattern. | Most files need rendering; only a few directories should be left alone. |
Key points:
- Patterns match against paths relative to the template root, always using
/separators (backslashes are converted before matching on Windows). - Patterns use minimatch syntax with
dot: true— paths with leading dots such as.github/workflows/ci.ymlare matched by**. - A missing or empty
patternsarray is treated as["**/*"]; an invalidmodefalls back toall. - Files that do not match (not hit by
include, or hit byexclude) are copied verbatim. - Matching only happens when
variables.enabled: true; withfalse, every file is copied as-is. - Binary files are never rendered, regardless of the mode.
Common pattern examples:
| Pattern | Matches |
|---|---|
**/*.ts | TypeScript files at any depth |
package.json | Only the root-level package.json |
src/** | Everything under src/ |
**/*.{ts,tsx} | TypeScript and TSX files |
Path Rendering Is Not Affected by filePatterns
filePatterns controls content rendering only. As long as variables.enabled: true, {{var}} placeholders and EJS expressions in file paths are always rendered.
The after_write Hook
Hooks run after all files have been written to the target directory. Only the after_write stage is processed today, and only scriptType: shell can execute:
hooks:
after_write:
scriptType: shell
script: npm install
errorHandling: warn
env:
NPM_CONFIG_REGISTRY: https://registry.npmjs.org
| Field | Default | Description |
|---|---|---|
scriptType | shell | nodejs / custom are accepted in the config, but executing them once hooks are authorized fails with HOOK_TYPE_UNSUPPORTED — only shell runs today. |
script | — (required) | Shell command executed in the target directory. |
errorHandling | stop | Failure strategy; see below. |
env | — | Extra environment variables, merged with the current process environment. |
Execution model:
- The working directory is the target directory from
ag create --target, so commands likenpm installorgit initact directly on the generated project. - On Windows the CLI prefers
bash -c; if bash is unavailable (for example, Git Bash is not installed), it falls back to the system default shell. Other platforms use the system shell directly. - The timeout is 5 minutes: on timeout the process receives
SIGTERM, thenSIGKILLafter a 5-second grace period, and the CLI reportsHOOK_TIMEOUT. - Hook stdout/stderr stream straight to your terminal for easy debugging.
Authorization: Why Hooks Do Not Run by Default
A template is third-party code fetched from a Git repository, and a hook is an arbitrary shell command executed with your user permissions. If hooks ran by default, anyone could run code on your machine at generation time simply by committing a malicious script into a template's config file. That is why AgileBuilder makes hook execution explicitly authorized:
- Off by default: without authorization,
after_writeis recorded as skipped (hooksSkipped) and generation completes normally. - Per-run authorization:
ag create ... --allow-hooks— always takes precedence. - Default authorization:
ag config set template.allowHooksDefault trueruns hooks even when--allow-hooksis not passed. Only enable this in environments where you trust every template source.
Skipping is never silent — the generation result lists skipped hooks in the hooksSkipped field, visible in both human-readable and --json output.
errorHandling: Failure Strategies
| Value | Behavior |
|---|---|
stop (default) | Abort generation on hook failure with HOOK_FAILED; the process exits non-zero. Best when a failed hook means an unusable project (e.g. a mandatory dependency install). |
warn | Record the failure as skipped (hooksSkipped) and finish generation normally. Best for nice-to-have scripts (formatting, an initial commit). |
continue | Same behavior as warn. |
Other Security Measures
The generation pipeline has several more lines of defense, independent of hooks:
- Target directory blacklist: writing to drive roots (such as
C:\), the Unix root,C:\Windows,C:\Program Files,C:\Program Files (x86),C:\Program Files\Git, or anything beneath them is refused withUNSAFE_TARGET_DIR. - Non-empty directory protection: an existing non-empty target directory requires an explicit
--overwrite, otherwise the command fails withTARGET_NOT_EMPTY. - Subdirectory escape protection:
--subdirandsource.subdirreject absolute paths and..segments; the resolved path must stay inside the cloned template directory, or the command fails withTEMPLATE_SUBDIR_UNSAFE. - Config files stay out of the output:
.agilebuilder.config.yaml/.agilebuilder.config.jsonare never copied to the target directory. .gitis skipped by default: unless--keep-gitis passed, the output contains no Git history from the template repository.
Troubleshooting
- Hook did not run? First confirm
--allow-hookswas passed ortemplate.allowHooksDefaultistrue, then checkhooksSkippedin the output. - Variables not rendered? Check in order: is
variables.enabledtrue→ does the file matchfilePatterns→ is the file binary? - Example files in the template that must not be rendered (EJS snippets in docs, say)? Exclude them with
excludemode, or switch toincludeto draw a tight boundary around what gets rendered.
What's Next
- Template Config Reference: the full config file schema.
- CLI Reference:
--allow-hooksand the related error codes.