LogoCyanPrint

Tutorial 4 - Asking Questions

Collect user input to customize generated projects

Tutorial 4: Asking Questions

Collect user input to customize the generated project. Questions make templates interactive and reusable.

The IInquirer Interface

The i parameter provides methods to ask different question types:

StartTemplateWithLambda(async (i, d) => {
// i is the IInquirer interface
const name = await i.text('Project name?', 'project.name', 'Enter name');
// ...
});
def template(i: Inquirer, d: DirectoryTree) -> dict:
# i is the Inquirer interface
name = i.text('Project name?', 'project.name', 'Enter name')
# ...
StartTemplate.WithLambda(async (i, d) => {
// i is the IInquirer interface
var name = await i.Text("Project name?", "project.name", "Enter name");
// ...
});

Adding Questions

Combine multiple question types for a rich setup experience:

import { StartTemplateWithLambda, GlobType } from '@atomicloud/cyan-sdk';
StartTemplateWithLambda(async (i, d) => {
// Text input
const name = await i.text('Project name?', 'my-template.name', 'Enter project name');
// Boolean confirmation
const docker = await i.confirm('Add Docker?', 'my-template.docker', 'Include Dockerfile');
// Multi-select
const features = await i.checkbox(
'Features?',
['TypeScript', 'ESLint', 'Prettier', 'Testing'],
'my-template.features',
'Select features'
);
return {
processors: [{
name: 'cyan/default',
files: [{ root: 'templates', glob: '**/*', exclude: [], type: GlobType.Template }],
config: {
vars: {
name,
docker: docker ? 'yes' : 'no',
features: features.join(', ')
}
}
}]
};
});
from cyan import Inquirer, DirectoryTree
from cyan import GlobType
def template(i: Inquirer, d: DirectoryTree) -> dict:
# Text input
name = i.text('Project name?', 'my-template.name', 'Enter project name')
# Boolean confirmation
docker = i.confirm('Add Docker?', 'my-template.docker', 'Include Dockerfile')
# Multi-select
features = i.checkbox(
'Features?',
['TypeScript', 'ESLint', 'Prettier', 'Testing'],
'my-template.features',
'Select features'
)
return {
'processors': [{
'name': 'cyan/default',
'files': [{'root': 'templates', 'glob': '**/*', 'exclude': [], 'type': GlobType.Template}],
'config': {
'vars': {
'name': name,
'docker': 'yes' if docker else 'no',
'features': ', '.join(features)
}
}
}]
}
using Atomiq.Cloud.Cyan;
StartTemplate.WithLambda(async (i, d) => {
// Text input
var name = await i.Text("Project name?", "my-template.name", "Enter project name");
// Boolean confirmation
var docker = await i.Confirm("Add Docker?", "my-template.docker", "Include Dockerfile");
// Multi-select
var features = await i.Checkbox(
"Features?",
new[] { "TypeScript", "ESLint", "Prettier", "Testing" },
"my-template.features",
"Select features"
);
return new {
processors = new[] {
new {
name = "cyan/default",
files = new[] {
new { root = "templates", glob = "**/*", exclude = Array.Empty<string>(), type = GlobType.Template }
},
config = new {
vars = new {
name,
docker = docker ? "yes" : "no",
features = string.Join(", ", features)
}
}
}
}
};
});

Question Types

MethodReturnsExample Use
text()stringProject name, author
select()stringLicense, framework choice
confirm()booleanYes/no decisions
checkbox()string[]Feature selection
password()stringSecrets, API keys
dateSelect()stringDates, deadlines
MethodReturnsExample Use
text()strProject name, author
select()strLicense, framework choice
confirm()boolYes/no decisions
checkbox()list[str]Feature selection
password()strSecrets, API keys
date_select()strDates, deadlines
MethodReturnsExample Use
Text()stringProject name, author
Select()stringLicense, framework choice
Confirm()boolYes/no decisions
Checkbox()string[]Feature selection
Password()stringSecrets, API keys
DateSelect()stringDates, deadlines

For detailed usage of each question type, see the How-to guides:

Question IDs (Keys)

The second argument is the question ID (also called a key). Keys enable answer reuse:

// Both questions get the same answer!
const name1 = await i.text('Project name?', 'name', '...');
const name2 = await i.text('Confirm name?', 'name', '...');
// name1 === name2 (same key = same answer)
# Both questions get the same answer!
name1 = i.text('Project name?', 'name', '...')
name2 = i.text('Confirm name?', 'name', '...')
# name1 == name2 (same key = same answer)
// Both questions get the same answer!
var name1 = await i.Text("Project name?", "name", "...");
var name2 = await i.Text("Confirm name?", "name", "...");
// name1 == name2 (same key = same answer)

Namespacing Your Keys

Always namespace your keys to prevent collisions when templates are composed.

// Good: namespaced keys
const name = await i.text('Project name?', 'my-template.project.name', '...');
// Bad: generic keys that might collide
const name = await i.text('Project name?', 'name', '...');
# Good: namespaced keys
name = i.text('Project name?', 'my-template.project.name', '...')
# Bad: generic keys that might collide
name = i.text('Project name?', 'name', '...')
// Good: namespaced keys
var name = await i.Text("Project name?", "my-template.project.name", "...");
// Bad: generic keys that might collide
var name = await i.Text("Project name?", "name", "...");

For a detailed explanation of how question namespacing works and why it matters, see the Question Namespacing concept guide.

Using Answers in Templates

Pass collected answers to processor config:

config: {
vars: {
name, // Project name
docker: docker ? 'yes' : 'no', // Conditional value
features: features.join(', ') // Array to string
}
}
'config': {
'vars': {
'name': name,
'docker': 'yes' if docker else 'no',
'features': ', '.join(features)
}
}
config = new {
vars = new {
name,
docker = docker ? "yes" : "no",
features = string.Join(", ", features)
}
}

Use in template files:

# var__name__
## Docker Support
This project includes Docker: **var__docker__**
## Features
This project includes: var__features__

What You Learned

  • How to use basic question types: text, confirm, checkbox
  • What question IDs are and how they enable answer reuse
  • Why namespacing keys is important
  • How to use answers in template files

Next Steps

Now you understand the basics of template development. Explore specific patterns in the How-to Guides: