or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

command-execution.mdcommand-line.mdconfiguration.mdcore-generator.mdfile-system.mdgit-integration.mdindex.mdpackage-management.mdtask-lifecycle.mduser-interaction.md

user-interaction.mddocs/

0

# User Interaction & Prompting

1

2

Interactive prompting system with storage, prefilling, and validation capabilities built on Inquirer.js for collecting user input and preferences.

3

4

## Capabilities

5

6

### Interactive Prompting

7

8

Main prompting system for collecting user input with automatic storage and prefilling.

9

10

```typescript { .api }

11

/**

12

* Prompt user to answer questions with automatic storage and prefilling

13

* On top of Inquirer.js API, provides {store: true} property for automatic storage

14

* @param questions - Array of question descriptor objects or single question

15

* @param storage - Storage object or name (generator property) for storing responses

16

* @returns Promise resolving to user answers

17

*/

18

async prompt<A extends PromptAnswers = PromptAnswers>(

19

questions: PromptQuestions<A>,

20

storage?: string | Storage

21

): Promise<A>;

22

```

23

24

**Usage Examples:**

25

26

```typescript

27

export default class MyGenerator extends Generator {

28

async prompting() {

29

// Basic prompting

30

this.answers = await this.prompt([

31

{

32

type: 'input',

33

name: 'name',

34

message: 'What is your project name?',

35

default: this.appname

36

},

37

{

38

type: 'confirm',

39

name: 'includeTests',

40

message: 'Include test files?',

41

default: true

42

},

43

{

44

type: 'list',

45

name: 'license',

46

message: 'Choose a license:',

47

choices: ['MIT', 'GPL-3.0', 'Apache-2.0', 'ISC'],

48

default: 'MIT'

49

}

50

]);

51

52

// Prompting with automatic storage

53

const userPrefs = await this.prompt([

54

{

55

type: 'input',

56

name: 'authorName',

57

message: 'Your name:',

58

store: true // Automatically store for reuse

59

},

60

{

61

type: 'input',

62

name: 'authorEmail',

63

message: 'Your email:',

64

store: true

65

}

66

]);

67

68

// Prompting with custom storage

69

const dbConfig = await this.prompt([

70

{

71

type: 'list',

72

name: 'database',

73

message: 'Database type:',

74

choices: ['mysql', 'postgresql', 'sqlite']

75

}

76

], this.config.createStorage('database'));

77

}

78

}

79

```

80

81

### Question Registration

82

83

Register prompts with automatic option export and storage configuration.

84

85

```typescript { .api }

86

/**

87

* Register stored config prompts and optional option alternative

88

* @param questions - Inquirer question or questions with export options

89

*/

90

registerConfigPrompts(questions: QuestionRegistrationOptions[]): void;

91

```

92

93

**Usage Example:**

94

95

```typescript

96

export default class MyGenerator extends Generator {

97

constructor(args, opts) {

98

super(args, opts);

99

100

// Register questions that can also be passed as CLI options

101

this.registerConfigPrompts([

102

{

103

type: 'input',

104

name: 'projectName',

105

message: 'Project name:',

106

storage: this.config,

107

exportOption: {

108

type: String,

109

alias: 'n',

110

description: 'Project name'

111

}

112

},

113

{

114

type: 'confirm',

115

name: 'typescript',

116

message: 'Use TypeScript?',

117

storage: this.config,

118

exportOption: true // Export with default option config

119

}

120

]);

121

}

122

}

123

```

124

125

### Question Types

126

127

Comprehensive question type support from Inquirer.js.

128

129

```typescript { .api }

130

interface PromptQuestion<T extends PromptAnswers = PromptAnswers> {

131

name: string;

132

type?: 'input' | 'number' | 'confirm' | 'list' | 'rawlist' | 'expand' | 'checkbox' | 'password' | 'editor';

133

message?: string | ((answers: T) => string);

134

default?: any | ((answers: T) => any);

135

choices?: Array<any> | ((answers: T) => Array<any>);

136

validate?: (input: any, answers?: T) => boolean | string | Promise<boolean | string>;

137

filter?: (input: any, answers?: T) => any | Promise<any>;

138

transformer?: (input: any, answers?: T, flags?: any) => string | Promise<string>;

139

when?: boolean | ((answers: T) => boolean | Promise<boolean>);

140

pageSize?: number;

141

prefix?: string;

142

suffix?: string;

143

askAnswered?: boolean;

144

loop?: boolean;

145

146

// Yeoman-specific extensions

147

storage?: Storage;

148

store?: boolean;

149

}

150

```

151

152

**Usage Examples:**

153

154

```typescript

155

export default class MyGenerator extends Generator {

156

async prompting() {

157

this.answers = await this.prompt([

158

// Input with validation

159

{

160

type: 'input',

161

name: 'port',

162

message: 'Server port:',

163

default: '3000',

164

validate: (input) => {

165

const port = parseInt(input);

166

return (port > 0 && port < 65536) || 'Port must be between 1 and 65535';

167

},

168

filter: (input) => parseInt(input)

169

},

170

171

// Conditional question

172

{

173

type: 'input',

174

name: 'dbUrl',

175

message: 'Database URL:',

176

when: (answers) => answers.database !== 'sqlite'

177

},

178

179

// Choice transformation

180

{

181

type: 'list',

182

name: 'framework',

183

message: 'Choose framework:',

184

choices: [

185

{ name: 'Express.js', value: 'express' },

186

{ name: 'Koa.js', value: 'koa' },

187

{ name: 'Fastify', value: 'fastify' }

188

]

189

},

190

191

// Checkbox with validation

192

{

193

type: 'checkbox',

194

name: 'features',

195

message: 'Select features:',

196

choices: ['Authentication', 'Database', 'Caching', 'Logging'],

197

validate: (choices) => choices.length > 0 || 'Select at least one feature'

198

}

199

]);

200

}

201

}

202

```

203

204

### Storage Integration

205

206

Automatic storage and prefilling of prompt answers from global and local configuration.

207

208

```typescript { .api }

209

interface QuestionRegistrationOptions<T extends PromptAnswers = PromptAnswers>

210

extends PromptQuestion<T> {

211

/**

212

* A value indicating whether an option should be exported for this question

213

*/

214

exportOption?: boolean | Record<string, unknown>;

215

}

216

217

// Utility functions for prompt storage (internal)

218

function prefillQuestions<A extends PromptAnswers = PromptAnswers>(

219

store: Storage,

220

questions: Array<PromptQuestion<A>>

221

): Array<PromptQuestion<A>>;

222

223

function storeAnswers(

224

store: Storage,

225

questions: any,

226

answers: PromptAnswers,

227

storeAll: boolean

228

): void;

229

```

230

231

**Usage Example:**

232

233

```typescript

234

export default class MyGenerator extends Generator {

235

async prompting() {

236

// Questions with storage will be automatically prefilled

237

// from previous runs and stored after completion

238

this.answers = await this.prompt([

239

{

240

type: 'input',

241

name: 'authorName',

242

message: 'Author name:',

243

store: true, // Store globally for reuse

244

storage: this.config // Store in generator config

245

},

246

{

247

type: 'input',

248

name: 'gitRepo',

249

message: 'Git repository URL:',

250

store: true,

251

default: async () => {

252

// Dynamic default using git

253

try {

254

const email = await this.git.email();

255

return `https://github.com/${email?.split('@')[0]}/`;

256

} catch {

257

return '';

258

}

259

}

260

}

261

]);

262

}

263

}

264

```

265

266

### Answer Processing

267

268

Methods for handling and processing prompt answers.

269

270

```typescript { .api }

271

type PromptAnswers = Record<string, any>;

272

273

type PromptQuestions<A extends PromptAnswers = PromptAnswers> =

274

PromptQuestion<A> | Array<PromptQuestion<A>>;

275

```

276

277

**Usage Example:**

278

279

```typescript

280

export default class MyGenerator extends Generator {

281

async prompting() {

282

// Collect answers in stages

283

const basicInfo = await this.prompt([

284

{ name: 'name', type: 'input', message: 'Project name:' },

285

{ name: 'version', type: 'input', message: 'Version:', default: '1.0.0' }

286

]);

287

288

const advancedInfo = await this.prompt([

289

{

290

name: 'features',

291

type: 'checkbox',

292

message: 'Additional features:',

293

choices: this._getFeatureChoices(basicInfo.name)

294

}

295

]);

296

297

// Combine answers

298

this.answers = { ...basicInfo, ...advancedInfo };

299

}

300

301

_getFeatureChoices(projectName) {

302

return [

303

{ name: `${projectName} CLI`, value: 'cli' },

304

{ name: `${projectName} API`, value: 'api' },

305

{ name: `${projectName} Web UI`, value: 'web' }

306

];

307

}

308

}

309

```

310

311

### Advanced Prompting Patterns

312

313

Complex prompting scenarios with conditional logic and validation.

314

315

**Usage Examples:**

316

317

```typescript

318

export default class MyGenerator extends Generator {

319

async prompting() {

320

// Multi-step prompting with dependencies

321

const projectType = await this.prompt({

322

type: 'list',

323

name: 'type',

324

message: 'Project type:',

325

choices: ['library', 'application', 'plugin']

326

});

327

328

let additionalQuestions = [];

329

330

if (projectType.type === 'application') {

331

additionalQuestions.push({

332

type: 'list',

333

name: 'appType',

334

message: 'Application type:',

335

choices: ['web', 'cli', 'desktop']

336

});

337

}

338

339

if (projectType.type === 'library') {

340

additionalQuestions.push({

341

type: 'confirm',

342

name: 'publishToNpm',

343

message: 'Publish to npm?',

344

default: true

345

});

346

}

347

348

const moreAnswers = await this.prompt(additionalQuestions);

349

this.answers = { ...projectType, ...moreAnswers };

350

351

// Validation with async checks

352

const repoInfo = await this.prompt({

353

type: 'input',

354

name: 'repository',

355

message: 'Repository URL:',

356

validate: async (input) => {

357

if (!input) return true; // Optional

358

359

// Check if repo exists (example)

360

try {

361

const response = await fetch(input);

362

return response.ok || 'Repository URL is not accessible';

363

} catch {

364

return 'Invalid URL format';

365

}

366

}

367

});

368

369

this.answers = { ...this.answers, ...repoInfo };

370

}

371

}

372

```

373

374

## Types

375

376

```typescript { .api }

377

interface PromptQuestion<T extends PromptAnswers = PromptAnswers> {

378

name: string;

379

type?: 'input' | 'number' | 'confirm' | 'list' | 'rawlist' | 'expand' | 'checkbox' | 'password' | 'editor';

380

message?: string | ((answers: T) => string);

381

default?: any | ((answers: T) => any);

382

choices?: Array<any> | ((answers: T) => Array<any>);

383

validate?: (input: any, answers?: T) => boolean | string | Promise<boolean | string>;

384

filter?: (input: any, answers?: T) => any | Promise<any>;

385

transformer?: (input: any, answers?: T, flags?: any) => string | Promise<string>;

386

when?: boolean | ((answers: T) => boolean | Promise<boolean>);

387

pageSize?: number;

388

prefix?: string;

389

suffix?: string;

390

askAnswered?: boolean;

391

loop?: boolean;

392

393

// Yeoman extensions

394

storage?: Storage;

395

store?: boolean;

396

}

397

398

interface QuestionRegistrationOptions<T extends PromptAnswers = PromptAnswers>

399

extends PromptQuestion<T> {

400

exportOption?: boolean | Record<string, unknown>;

401

}

402

403

type PromptAnswers = Record<string, any>;

404

405

type PromptQuestions<A extends PromptAnswers = PromptAnswers> =

406

PromptQuestion<A> | Array<PromptQuestion<A>>;

407

```