Templates
Templates
Section titled “Templates”Create Confluence pages from reusable templates with variable substitution and Jinja syntax.
Prerequisites
Section titled “Prerequisites”- Authenticated profile (
atlcli auth login) - Space permission: Edit permission to create pages from templates
Overview
Section titled “Overview”The template system provides:
- Hierarchical storage: Global, profile, and space-level templates
- Precedence: Space > Profile > Global (most specific wins)
- Jinja syntax: Conditions, loops, filters and
{% set %} - Built-in variables: Date, time, user, space, and more
- Import/export: Share template packs via directories, Git, or URLs
Quick Start
Section titled “Quick Start”# Create a templateatlcli wiki template create meeting-notes --file ./meeting.md
# List available templatesatlcli wiki template list
# Create a page from templateatlcli wiki page create --template meeting-notes \ --var title="Sprint Planning" \ --var date=2025-01-14 \ --var type=planning \ --space TEAM
# Render template to stdout (preview)atlcli wiki template render meeting-notes --var title="Test" --var date=2025-01-14 --var type=standupTemplate File Format
Section titled “Template File Format”Templates are Markdown files with YAML frontmatter:
---name: meeting-notesdescription: Template for team meetingsauthor: Your Nameversion: 1.0.0tags: - meeting - teamvariables: - name: title type: string required: true description: Meeting title - name: date type: date required: true - name: attendees type: string default: "TBD" - name: type type: select options: - standup - planning - retro required: true---# {{ title }}
**Date:** {{ date }}**Type:** {{ type }}**Attendees:** {{ attendees }}
## Agenda
{% if agenda %}{{ agenda }}{% else %}- Item 1- Item 2{% endif %}
## Notes
<!-- Add meeting notes here -->
## Action Items
| Owner | Action | Due ||-------|--------|-----|| | | |Variable Types
Section titled “Variable Types”| Type | Description | Validation |
|---|---|---|
string |
Free text | None |
number |
Numeric value | Must be valid number |
date |
Date value | ISO 8601 or relative (today, tomorrow) |
boolean |
True/false | Accepts true/false, yes/no, 1/0 |
select |
Enum from options | Must match one of options |
template render, page create --template and docs add --template stop with a usage error (exit code 1) when a required variable without a default is missing or a value does not match its type; the error names the variables. --interactive asks for them instead. A variable the template uses but does not declare renders empty, with a warning on stderr.
Built-in Variables
Section titled “Built-in Variables”Built-in variables are plain names. A declared or passed variable of the same name wins: in the example above, date and title are the values you pass.
| Variable | Description | Example |
|---|---|---|
{{ date }} |
Current date | 2025-01-14 |
{{ datetime }} |
Date and time | 2025-01-14T10:30:00 |
{{ time }} |
Current time | 10:30 |
{{ year }} |
Current year | 2025 |
{{ month }} |
Current month | 01 |
{{ day }} |
Current day | 14 |
{{ weekday }} |
Day of week | Tuesday |
{{ user }} |
Current user | john.doe |
{{ space }} |
Space key | TEAM |
{{ profile }} |
Profile name | work |
{{ title }} |
Page title | From context |
{{ parent.id }} |
Parent page ID | 12345 |
{{ parent.title }} |
Parent page title | Parent Page |
{{ uuid }} |
Random UUID | 550e8400-e29b-… |
{{ today }} |
Current date, always ISO | 2025-01-14 |
{{ now }} |
Same as datetime |
2025-01-14T10:30:00 |
{{ quarter }} |
Quarter | 1 |
{{ week }} |
ISO week | 03 |
The filters date, dateAdd, default, replace, lower and upper, the
operators ==, !=, and, or, not and the other Nunjucks built-in
filters work in page templates too: {{ today | dateAdd(7, "d") | date("DD.MM.YYYY") }}.
The same built-ins and filters apply to copies of live pages and issues; see
Templating.
Storage Locations
Section titled “Storage Locations”atlcli stores templates at three levels with precedence (space > profile > global):
Global Templates
Section titled “Global Templates”~/.atlcli/templates/global/├── meeting-notes.md└── decision-record.mdProfile Templates
Section titled “Profile Templates”~/.atlcli/templates/profiles/└── work/ └── standup.mdSpace Templates
Section titled “Space Templates”# In synced docs folder (checked first)./my-docs/.atlcli/templates/└── runbook.md
# Or under config~/.atlcli/templates/spaces/TEAM/└── team-specific.mdSelecting a Level
Section titled “Selecting a Level”The same template name can exist at several levels. show, create, edit, delete, rename, validate <name>, render, list, and export all select the level the same way:
| Flag | Effect |
|---|---|
--level global|profile|space |
Use that level exactly |
--profile <name> |
Use the profile level, for that profile (implies --level profile) |
--space <key> |
Use the space level, for that space (implies --level space) |
| (none) | Reads resolve by precedence (space > profile > global); create targets the current space, else global |
Three rules keep a named level from turning into a different one:
- A named level is never a fallback.
--level profilereads, writes, and deletes at the profile level only. If the template is not there, the command fails withTemplate 'x' not found at profile:work.— it does not quietly use the space or global copy. - Contradictory flags fail.
--level global --profile work,--level space --profile work, and--profile work --space TEAMeach name two levels, so the command exits1instead of picking one. - Config defaults supply coordinates, not levels. An active profile and a default space fill in the which profile / which space for a level you named. They never select the level themselves. Naming a level with no context available —
--level profilewith no active profile,--level spacewith no default space — is a usage error.
# Delete the profile copy; the space and global copies are untouchedatlcli wiki template delete standup --level profile --force
# Read the global copy even though a space copy shadows itatlcli wiki template show meeting-notes --level global
# Rejected: two different levels namedatlcli wiki template show meeting-notes --level global --profile work# → --level global conflicts with --profile work.Commands
Section titled “Commands”List Templates
Section titled “List Templates”# List all templatesatlcli wiki template list
# Filter by levelatlcli wiki template list --level globalatlcli wiki template list --profile workatlcli wiki template list --space TEAM
# Filter by tagatlcli wiki template list --tag meeting
# Searchatlcli wiki template list --search retro
# Include overridden templatesatlcli wiki template list --all
# JSON outputatlcli wiki template list --jsonShow Template
Section titled “Show Template”# Show template details and content (precedence: space > profile > global)atlcli wiki template show meeting-notes
# Show from a specific levelatlcli wiki template show standup --profile workatlcli wiki template show meeting-notes --level globalatlcli wiki template show runbook --level spaceSee Selecting a Level for how --level, --profile, and --space interact.
Create Template
Section titled “Create Template”# From fileatlcli wiki template create meeting-notes --file ./template.md
# Open in $EDITORatlcli wiki template create meeting-notes
# Interactive wizardatlcli wiki template create --interactive
# Create at a specific levelatlcli wiki template create standup --profile work --file ./standup.mdatlcli wiki template create runbook --space TEAM --file ./runbook.mdatlcli wiki template create shared --level global --file ./shared.md
# Without a level flag, create targets the current space if there is one,# otherwise global. Name the level when it matters.atlcli wiki template create standup --level profile --file ./standup.md
# Overwrite existingatlcli wiki template create meeting-notes --file ./updated.md --forceEdit Template
Section titled “Edit Template”# Opens in $EDITORatlcli wiki template edit meeting-notes
# Edit at a specific levelatlcli wiki template edit standup --profile workatlcli wiki template edit meeting-notes --level globaledit saves back to the level it read from, so naming the level decides which file you change.
Delete Template
Section titled “Delete Template”# With confirmationatlcli wiki template delete meeting-notes
# Force deleteatlcli wiki template delete meeting-notes --force
# Delete from a specific levelatlcli wiki template delete standup --profile work --forceatlcli wiki template delete meeting-notes --level global --forceWithout a level flag, delete removes whatever precedence resolves to — the space copy first. The command reports the level it deleted from, e.g. Deleted template 'standup' from [profile:work].
Rename Template
Section titled “Rename Template”atlcli wiki template rename old-name new-nameatlcli wiki template rename standup daily-standup --profile workatlcli wiki template rename runbook ops-runbook --level spaceRenaming affects one level only; same-named templates at other levels keep their name.
Validate Template
Section titled “Validate Template”# Validate specific templateatlcli wiki template validate meeting-notes
# Validate a template at a specific levelatlcli wiki template validate standup --level profile
# Validate from fileatlcli wiki template validate --file ./template.md
# Validate all templatesatlcli wiki template validate --allExit codes — validate is a CI gate:
| Outcome | Exit code |
|---|---|
| Valid, no warnings | 0 |
| Valid, with warnings (e.g. undeclared variable) | 0 |
Invalid (e.g. unclosed {% if %}, Jinja syntax error: …) |
1 |
| Template or file not found | 1 |
The report is written to stdout either way; with --json it stays a single JSON document whose valid field carries the verdict.
# Fail a pipeline on any invalid templateatlcli wiki template validate --all || exit 1Render Template
Section titled “Render Template”# Render to stdoutatlcli wiki template render meeting-notes --var title="Sprint Planning"
# With multiple variablesatlcli wiki template render meeting-notes \ --var title="Planning" \ --var date=today \ --var type=planning
# Interactive prompts for missing variablesatlcli wiki template render meeting-notes --interactive
# Output to fileatlcli wiki template render meeting-notes --var title="Test" > output.md
# JSON outputatlcli wiki template render meeting-notes --var title="Test" --jsonInit Template from Existing Content
Section titled “Init Template from Existing Content”# From Confluence page IDatlcli wiki template init meeting-template --from 12345
# From page title (requires --from-space)atlcli wiki template init meeting-template --from "Team Meetings" --from-space TEAM
# From local fileatlcli wiki template init meeting-template --from ./docs/meetings.md
# Save to specific levelatlcli wiki template init retro --from 12345 --to-profile workCopy Template
Section titled “Copy Template”# Copy between levelsatlcli wiki template copy meeting-notes --from-level global --to-profile work
# Copy with renameatlcli wiki template copy meeting-notes team-meeting --from-level global --to-space TEAMImport/Export
Section titled “Import/Export”Export Templates
Section titled “Export Templates”# Export all to directoryatlcli wiki template export -o ./my-templates
# Export single template to stdoutatlcli wiki template export meeting-notes
# Export single to fileatlcli wiki template export meeting-notes -o ./meeting-notes.md
# Export from a specific levelatlcli wiki template export --profile work -o ./work-templates
# Export one template from a named levelatlcli wiki template export meeting-notes --level globalExport directory structure:
./my-templates/├── manifest.yml├── global/│ └── meeting-notes.md├── profiles/│ └── work/│ └── standup.md└── spaces/ └── TEAM/ └── runbook.mdImport Templates
Section titled “Import Templates”# From local directoryatlcli wiki template import ./my-templates
# From Git URLatlcli wiki template import https://github.com/user/template-pack
# From tar.gz URLatlcli wiki template import https://example.com/templates.tar.gz
# Flatten to single levelatlcli wiki template import ./templates --to-profile work
# Replace existing (default: skip)atlcli wiki template import ./templates --replace
# Import specific templates onlyatlcli wiki template import ./templates meeting-notes standupUpdate from Remote
Section titled “Update from Remote”# Update all tracked templatesatlcli wiki template update
# Update specific templatesatlcli wiki template update meeting-notes standup
# Update from specific sourceatlcli wiki template update --source https://github.com/user/template-packUsing Templates with Page Create
Section titled “Using Templates with Page Create”# Create page from templateatlcli wiki page create --template meeting-notes \ --var title="Sprint Planning" \ --var date=2025-01-14 \ --space TEAM
# Interactive prompts for variablesatlcli wiki page create --template meeting-notes --space TEAM
# Dry run (preview)atlcli wiki page create --template meeting-notes \ --var title="Test" \ --dry-runTemplates for Jira Report Pages
Section titled “Templates for Jira Report Pages”atlcli jira version notes and atlcli jira sprint review render Jira data through templates too. They look for a template by name at the space, profile and global level first, then fall back to the bundled release-notes, release-notes-by-epic and sprint-review. See Release Notes and Sprint Review Pages for the available variables.
Jinja Syntax
Section titled “Jinja Syntax”Templates use Jinja syntax. A line that holds only a block tag ({% if %},
{% endfor %}) disappears with its line break, so block tags can stand on
their own lines without leaving blank lines. Missing values render empty. See
Templating for the full reference.
Conditionals
Section titled “Conditionals”{% if attendees %}**Attendees:** {{ attendees }}{% else %}**Attendees:** TBD{% endif %}
{% if not draft %}This is published content.{% endif %}{% for item in items %}- {{ item }}{% endfor %}
{% for task in tasks %}- [ ] {{ task.title }} ({{ task.assignee }}){% else %}- No tasks yet{% endfor %}loop.index (from 1), loop.first and loop.last are set inside a loop.
Nested Values
Section titled “Nested Values”**Author:** {{ author.name }} ({{ author.email }})Default Values
Section titled “Default Values”{{ attendees | default("TBD") }}Literal Template Syntax
Section titled “Literal Template Syntax”{% raw %}Write {{ name }} to insert a variable.{% endraw %}- Use built-in variables for dynamic content like dates and user info
- Define required variables in frontmatter to ensure pages are complete
- Use tags to organize templates by category
- Export templates to share with your team or back up
- Track sources with import to enable easy updates
Troubleshooting
Section titled “Troubleshooting”Variable Renders Empty
Section titled “Variable Renders Empty”Symptom: A {{ variable }} renders as nothing.
Causes:
- Variable name typo
- Variable not passed with
--var - An
@left over from Handlebars ({{@date}}is a syntax error; write{{ date }})
Fix: Check variable names match frontmatter definitions. Use --interactive to see prompts, or {{ variable | default("…") }} for an inline fallback.
Template Render Failed
Section titled “Template Render Failed”Symptom: Template render failed: … when rendering, or Jinja syntax error: … (line N) from validate.
Cause: The template does not parse as Jinja: an unclosed {% if %} or {% for %}, or Handlebars syntax such as {{#each}} or {{/if}}.
Fix: Close every block ({% endif %}, {% endfor %}) and rewrite Handlebars syntax with the migration table.
Template Not Found
Section titled “Template Not Found”Symptom: Error: Template 'name' not found
Cause: Template doesn’t exist at any storage level.
Fix: List available templates with atlcli wiki template list --all and check the name.
Template Not Found at a Named Level
Section titled “Template Not Found at a Named Level”Symptom: Template 'name' not found at profile:work.
Cause: The template exists, but not at the level you named. atlcli does not fall back to another level — that is what used to delete the wrong file.
Fix: Run atlcli wiki template list --all to see which level holds it, then name that level, drop the level flag to resolve by precedence, or copy it across with atlcli wiki template copy <name> --from-level global --to-profile work.
Level Flags Conflict
Section titled “Level Flags Conflict”Symptom: --level global conflicts with --profile work. or --profile work and --space TEAM name different levels. Pass only one.
Cause: The flags name two different levels, so there is no single target.
Fix: Pass one level. Use --level profile --profile work to name the level and the profile together, or just --profile work.
No Profile or Space Context
Section titled “No Profile or Space Context”Symptom: No profile context for the profile level. Pass --profile <name> or set an active profile.
Cause: You named a level whose coordinate is unknown — no --profile/--space flag, and no active profile or default space in config.
Fix: Pass the coordinate explicitly (--profile work, --space TEAM), or set it in config with atlcli auth switch <name> for the profile, or a default space in ~/.atlcli/config.json (global.space).
Related Topics
Section titled “Related Topics”- Pages - Create pages from templates with
--templateflag - Macros - Use macros within template content
- Configuration - Template storage paths