LogoCyanPrint

Use Keys for Consistent Answers

How to use keys for consistent answers across questions and templates

How to Use Keys

Keys control answer reuse across questions and templates. Understanding keys is essential for creating composable templates.

How Keys Work

Every question has an id parameter that serves as its key:

const name = await i.text('Project name?', 'project.name', 'Enter name');
name = i.text('Project name?', 'project.name', 'Enter name')
var name = await i.Text("Project name?", "project.name", "Enter name");

Same Key = Same Answer

If multiple questions share the same key, they receive the same answer:

// First question - user enters "my-project"
const name1 = await i.text('Project name?', 'project.name', 'Enter name');
// Second question with same key - automatically gets "my-project"
const name2 = await i.text('Confirm name?', 'project.name', '...');
console.log(name1 === name2); // true
# First question - user enters "my-project"
name1 = i.text('Project name?', 'project.name', 'Enter name')
# Second question with same key - automatically gets "my-project"
name2 = i.text('Confirm name?', 'project.name', '...')
print(name1 == name2) # True
// First question - user enters "my-project"
var name1 = await i.Text("Project name?", "project.name", "Enter name");
// Second question with same key - automatically gets "my-project"
var name2 = await i.Text("Confirm name?", "project.name", "...");
Console.WriteLine(name1 == name2); // true

The user is only prompted once for each unique key. Subsequent questions with the same key receive the cached answer.

Namespacing Your Keys

Prevent collisions with other templates by namespacing:

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

Why Namespacing Matters

When templates are composed, non-namespaced keys cause conflicts:

With namespacing:

Cross-Template Keys

Keys persist across template composition. Use this intentionally:

Intentional Sharing

Share answers between templates when desired:

// Template A - sets the project name
const name = await i.text('Project name?', 'global.project.name', '...');
// Template B - uses the same name without asking again
const name = await i.text('Project name?', 'global.project.name', '...');
# Template A - sets the project name
name = i.text('Project name?', 'global.project.name', '...')
# Template B - uses the same name without asking again
name = i.text('Project name?', 'global.project.name', '...')
// Template A - sets the project name
var name = await i.Text("Project name?", "global.project.name", "...");
// Template B - uses the same name without asking again
var name = await i.Text("Project name?", "global.project.name", "...");

Avoiding Collisions

Use unique prefixes to avoid accidental sharing:

// Database template
const dbName = await i.text('Database name?', 'db-template.database.name', '...');
// API template - won't get db-template's answer
const apiName = await i.text('API name?', 'api-template.api.name', '...');
# Database template
db_name = i.text('Database name?', 'db-template.database.name', '...')
# API template - won't get db-template's answer
api_name = i.text('API name?', 'api-template.api.name', '...')
// Database template
var dbName = await i.Text("Database name?", "db-template.database.name", "...");
// API template - won't get db-template's answer
var apiName = await i.Text("API name?", "api-template.api.name", "...");

Key Naming Convention

Follow a consistent naming pattern:

// Pattern: {template-name}.{category}.{field}
// Template-specific
const name = await i.text('Name?', 'node-template.project.name', '...');
const version = await i.text('Version?', 'node-template.project.version', '...');
// Category-specific (shared within template)
const dbHost = await i.text('Host?', 'node-template.database.host', '...');
const dbPort = await i.text('Port?', 'node-template.database.port', '...');
// Global (shared across templates)
const orgName = await i.text('Organization?', 'global.organization.name', '...');
# Pattern: {template-name}.{category}.{field}
# Template-specific
name = i.text('Name?', 'node-template.project.name', '...')
version = i.text('Version?', 'node-template.project.version', '...')
# Category-specific (shared within template)
db_host = i.text('Host?', 'node-template.database.host', '...')
db_port = i.text('Port?', 'node-template.database.port', '...')
# Global (shared across templates)
org_name = i.text('Organization?', 'global.organization.name', '...')
// Pattern: {template-name}.{category}.{field}
// Template-specific
var name = await i.Text("Name?", "node-template.project.name", "...");
var version = await i.Text("Version?", "node-template.project.version", "...");
// Category-specific (shared within template)
var dbHost = await i.Text("Host?", "node-template.database.host", "...");
var dbPort = await i.Text("Port?", "node-template.database.port", "...");
// Global (shared across templates)
var orgName = await i.Text("Organization?", "global.organization.name", "...");

Common Patterns

Confirming Values

// Ask once
const email = await i.text('Email?', 'user.email', 'Your email');
// Confirm without re-prompting
const confirmEmail = await i.confirm(
`Use ${email} for notifications?`,
'user.email',
'Confirm email'
);
# Ask once
email = i.text('Email?', 'user.email', 'Your email')
# Confirm without re-prompting
confirm_email = i.confirm(
f'Use {email} for notifications?',
'user.email',
'Confirm email'
)
// Ask once
var email = await i.Text("Email?", "user.email", "Your email");
// Confirm without re-prompting
var confirmEmail = await i.Confirm(
$"Use {email} for notifications?",
"user.email",
"Confirm email"
);

Step-by-Step Collection

// Collect in stages, referencing earlier answers
const projectName = await i.text('Project name?', 'project.name', '...');
const componentName = await i.text(
'Main component name?',
'project.name',
'Defaults to project name'
);
# Collect in stages, referencing earlier answers
project_name = i.text('Project name?', 'project.name', '...')
component_name = i.text(
'Main component name?',
'project.name',
'Defaults to project name'
)
// Collect in stages, referencing earlier answers
var projectName = await i.Text("Project name?", "project.name", "...");
var componentName = await i.Text(
"Main component name?",
"project.name",
"Defaults to project name"
);

Best Practices

  1. Always namespace - Prevent collisions in composed templates
  2. Be consistent - Use a predictable naming pattern
  3. Document shared keys - If intentionally sharing, document it
  4. Test composition - Verify your template works when composed