or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-interface.mdcollections-utilities.mdcore-runtime.mdindex.mdjson-processing.mdmeta-programming.mdscript-execution.mdsql-database.mdtemplate-processing.mdxml-processing.md

cli-interface.mddocs/

0

# Command-Line Interface

1

2

CLI building and option parsing utilities for creating robust command-line applications with support for various argument patterns and help text generation.

3

4

## Capabilities

5

6

### CliBuilder with Apache Commons CLI

7

8

Command-line interface builder using Apache Commons CLI library for robust option parsing.

9

10

```groovy { .api }

11

/**

12

* Command-line interface builder using Apache Commons CLI

13

*/

14

class CliBuilder {

15

/** Create CliBuilder with default settings */

16

CliBuilder()

17

18

/** Create CliBuilder with usage text */

19

CliBuilder(String usage)

20

21

/** Parse command-line arguments */

22

OptionAccessor parse(String[] args)

23

24

/** Parse arguments and handle errors */

25

OptionAccessor parse(String[] args, boolean stopAtNonOption)

26

27

/** Print usage information */

28

void usage()

29

30

/** Print usage to specific writer */

31

void usage(PrintWriter writer)

32

33

/** Print usage to specific writer with width */

34

void usage(PrintWriter writer, int width)

35

36

/** Set usage text */

37

void setUsage(String usage)

38

39

/** Set header text */

40

void setHeader(String header)

41

42

/** Set footer text */

43

void setFooter(String footer)

44

45

/** Set whether to stop at non-option */

46

void setStopAtNonOption(boolean stopAtNonOption)

47

48

/** Add option with short name */

49

void option(String shortOpt, String description)

50

51

/** Add option with long name */

52

void longOpt(String longOpt, String description)

53

54

/** Add option with argument */

55

void optionWithArg(String shortOpt, String longOpt, String description)

56

57

/** Add option with optional argument */

58

void optionWithOptionalArg(String shortOpt, String longOpt, String description)

59

}

60

61

/**

62

* Accessor for parsed command-line options

63

*/

64

class OptionAccessor {

65

/** Check if option was provided */

66

boolean hasOption(String option)

67

68

/** Get option value */

69

Object getOptionValue(String option)

70

71

/** Get option value with default */

72

Object getOptionValue(String option, Object defaultValue)

73

74

/** Get option values (for options that can appear multiple times) */

75

String[] getOptionValues(String option)

76

77

/** Get remaining arguments */

78

String[] getArgs()

79

80

/** Get arguments as list */

81

List<String> arguments()

82

}

83

```

84

85

**Usage Examples:**

86

87

```groovy

88

import groovy.cli.commons.*

89

90

// Basic CLI setup

91

def cli = new CliBuilder(usage: 'myapp [options] <files>')

92

cli.setHeader('My Application - File Processor')

93

cli.setFooter('For more information, visit: https://example.com')

94

95

// Define options

96

cli.h(longOpt: 'help', 'Show this help message')

97

cli.v(longOpt: 'verbose', 'Verbose output')

98

cli.q(longOpt: 'quiet', 'Quiet mode')

99

cli.o(longOpt: 'output', args: 1, argName: 'file', 'Output file')

100

cli.f(longOpt: 'format', args: 1, argName: 'format', 'Output format (xml|json|csv)')

101

cli.r(longOpt: 'recursive', 'Process directories recursively')

102

cli.n(longOpt: 'dry-run', 'Show what would be done without actually doing it')

103

104

// Parse arguments

105

def options = cli.parse(args)

106

107

if (!options) {

108

// Parsing failed

109

return

110

}

111

112

if (options.h) {

113

cli.usage()

114

return

115

}

116

117

// Access options

118

boolean verbose = options.v

119

boolean quiet = options.q

120

boolean recursive = options.r

121

boolean dryRun = options.n

122

123

String outputFile = options.o

124

String format = options.getOptionValue('f', 'json') // default to 'json'

125

126

// Get remaining arguments (files to process)

127

def files = options.arguments()

128

129

if (files.isEmpty()) {

130

println "Error: No input files specified"

131

cli.usage()

132

return

133

}

134

135

// Process based on options

136

if (verbose && !quiet) {

137

println "Processing ${files.size()} files"

138

println "Output format: $format"

139

if (outputFile) {

140

println "Output file: $outputFile"

141

}

142

if (recursive) {

143

println "Recursive processing enabled"

144

}

145

if (dryRun) {

146

println "DRY RUN MODE - no changes will be made"

147

}

148

}

149

150

files.each { file ->

151

if (verbose && !quiet) {

152

println "Processing: $file"

153

}

154

155

if (!dryRun) {

156

// Actually process the file

157

processFile(file, format, outputFile, recursive)

158

}

159

}

160

```

161

162

### Advanced Option Configuration

163

164

Configure complex option patterns with validation and type conversion.

165

166

```groovy

167

// Advanced CLI with multiple argument types

168

def cli = new CliBuilder(usage: 'deploy [options] <environment>')

169

170

// String options

171

cli.u(longOpt: 'user', args: 1, argName: 'username', 'Username for deployment')

172

cli.p(longOpt: 'password', args: 1, argName: 'password', 'Password (prompt if not provided)')

173

174

// Numeric options

175

cli.t(longOpt: 'timeout', args: 1, argName: 'seconds', type: Integer, 'Timeout in seconds (default: 300)')

176

cli.r(longOpt: 'retries', args: 1, argName: 'count', type: Integer, 'Number of retry attempts (default: 3)')

177

178

// Multiple values

179

cli.s(longOpt: 'service', args: Option.UNLIMITED_VALUES, argName: 'service', 'Services to deploy (can be specified multiple times)')

180

cli.e(longOpt: 'exclude', args: Option.UNLIMITED_VALUES, argName: 'pattern', 'Exclude patterns')

181

182

// Optional arguments

183

cli.c(longOpt: 'config', optionalArg: true, argName: 'file', 'Configuration file (default: config.yml)')

184

185

// Boolean flags

186

cli.f(longOpt: 'force', 'Force deployment even if validation fails')

187

cli.v(longOpt: 'verbose', 'Verbose output')

188

cli.q(longOpt: 'quiet', 'Quiet mode')

189

190

def options = cli.parse(args)

191

192

if (!options || options.arguments().isEmpty()) {

193

println "Error: Environment must be specified"

194

cli.usage()

195

return

196

}

197

198

// Extract values with defaults and validation

199

String environment = options.arguments()[0]

200

String user = options.u

201

String password = options.p

202

Integer timeout = options.t ? options.t as Integer : 300

203

Integer retries = options.r ? options.r as Integer : 3

204

String configFile = options.c ?: 'config.yml'

205

206

// Handle multiple values

207

List<String> services = options.ss ?: [] // Note: multiple values accessed with repeated short option

208

List<String> excludePatterns = options.es ?: []

209

210

// Validation

211

if (!user) {

212

println "Error: Username is required"

213

cli.usage()

214

return

215

}

216

217

if (timeout < 1 || timeout > 3600) {

218

println "Error: Timeout must be between 1 and 3600 seconds"

219

return

220

}

221

222

if (!password) {

223

// Prompt for password

224

print "Password: "

225

password = System.console()?.readPassword()?.toString()

226

if (!password) {

227

println "Error: Password is required"

228

return

229

}

230

}

231

232

println "Deploying to environment: $environment"

233

println "User: $user"

234

println "Timeout: ${timeout}s"

235

println "Retries: $retries"

236

println "Config: $configFile"

237

238

if (services) {

239

println "Services: ${services.join(', ')}"

240

}

241

if (excludePatterns) {

242

println "Exclude patterns: ${excludePatterns.join(', ')}"

243

}

244

```

245

246

### CliBuilder with Picocli

247

248

Alternative CLI builder using Picocli library for annotation-based configuration.

249

250

```groovy { .api }

251

/**

252

* Command-line interface builder using Picocli

253

*/

254

class CliBuilder {

255

/** Create CliBuilder with Picocli backend */

256

CliBuilder()

257

258

/** Create with usage text */

259

CliBuilder(String usage)

260

261

/** Parse arguments */

262

Object parse(String[] args)

263

264

/** Parse with command object */

265

<T> T parse(String[] args, Class<T> commandClass)

266

267

/** Set command specification */

268

void setSpec(Object spec)

269

270

/** Print usage information */

271

void usage()

272

273

/** Print version information */

274

void version()

275

}

276

```

277

278

**Usage Examples:**

279

280

```groovy

281

import groovy.cli.picocli.*

282

import picocli.CommandLine.*

283

284

// Using annotations for command specification

285

@Command(name = 'backup', description = 'Backup utility')

286

class BackupCommand {

287

@Option(names = ['-h', '--help'], usageHelp = true, description = 'Show help')

288

boolean help

289

290

@Option(names = ['-v', '--verbose'], description = 'Verbose output')

291

boolean verbose

292

293

@Option(names = ['-o', '--output'], paramLabel = 'DIR', description = 'Output directory')

294

String outputDir = './backup'

295

296

@Option(names = ['-c', '--compress'], description = 'Compress backup files')

297

boolean compress

298

299

@Option(names = ['-e', '--exclude'], paramLabel = 'PATTERN', description = 'Exclude patterns')

300

List<String> excludePatterns = []

301

302

@Parameters(paramLabel = 'SOURCE', description = 'Source directories to backup')

303

List<String> sources = []

304

305

@Spec CommandSpec spec // Injected by Picocli

306

307

void run() {

308

if (sources.isEmpty()) {

309

throw new ParameterException(spec.commandLine(), "At least one source directory must be specified")

310

}

311

312

if (verbose) {

313

println "Backup configuration:"

314

println " Sources: ${sources.join(', ')}"

315

println " Output: $outputDir"

316

println " Compress: $compress"

317

if (excludePatterns) {

318

println " Exclude: ${excludePatterns.join(', ')}"

319

}

320

}

321

322

// Perform backup

323

performBackup(sources, outputDir, compress, excludePatterns, verbose)

324

}

325

}

326

327

// Use the command

328

def cli = new CliBuilder()

329

def command = cli.parse(args, BackupCommand)

330

331

if (command) {

332

command.run()

333

}

334

```

335

336

### Subcommands and Command Hierarchies

337

338

Create complex CLI applications with subcommands.

339

340

```groovy

341

// Main command with subcommands

342

@Command(name = 'myapp',

343

description = 'Multi-purpose application',

344

subcommands = [UserCommand, ProjectCommand, ConfigCommand])

345

class MainCommand implements Runnable {

346

@Option(names = ['-h', '--help'], usageHelp = true)

347

boolean help

348

349

@Option(names = ['-v', '--version'], versionHelp = true)

350

boolean version

351

352

void run() {

353

println "Use 'myapp --help' for available commands"

354

}

355

}

356

357

// User management subcommand

358

@Command(name = 'user', description = 'User management commands')

359

class UserCommand {

360

@Command(name = 'create', description = 'Create a new user')

361

void create(

362

@Option(names = ['-n', '--name'], required = true) String name,

363

@Option(names = ['-e', '--email'], required = true) String email,

364

@Option(names = ['-r', '--role']) String role = 'user'

365

) {

366

println "Creating user: $name ($email) with role: $role"

367

// Implementation here

368

}

369

370

@Command(name = 'list', description = 'List all users')

371

void list(

372

@Option(names = ['-f', '--format']) String format = 'table',

373

@Option(names = ['-r', '--role']) String roleFilter

374

) {

375

println "Listing users (format: $format)"

376

if (roleFilter) {

377

println "Filtering by role: $roleFilter"

378

}

379

// Implementation here

380

}

381

382

@Command(name = 'delete', description = 'Delete a user')

383

void delete(@Parameters(paramLabel = 'USER_ID') String userId) {

384

println "Deleting user: $userId"

385

// Implementation here

386

}

387

}

388

389

// Project management subcommand

390

@Command(name = 'project', description = 'Project management commands')

391

class ProjectCommand {

392

@Command(name = 'init', description = 'Initialize a new project')

393

void init(

394

@Parameters(paramLabel = 'NAME') String name,

395

@Option(names = ['-t', '--template']) String template,

396

@Option(names = ['-d', '--directory']) String directory

397

) {

398

println "Initializing project: $name"

399

if (template) println "Using template: $template"

400

if (directory) println "In directory: $directory"

401

// Implementation here

402

}

403

404

@Command(name = 'build', description = 'Build the project')

405

void build(

406

@Option(names = ['-e', '--environment']) String env = 'development',

407

@Option(names = ['-c', '--clean']) boolean clean

408

) {

409

if (clean) println "Cleaning build artifacts"

410

println "Building project for environment: $env"

411

// Implementation here

412

}

413

}

414

415

// Usage

416

def commandLine = new CommandLine(new MainCommand())

417

commandLine.execute(args)

418

```

419

420

### Error Handling and Validation

421

422

Implement robust error handling and input validation.

423

424

```groovy

425

import picocli.CommandLine.*

426

427

class ValidatedCommand {

428

@Option(names = ['-p', '--port'], description = 'Server port (1024-65535)')

429

void setPort(int port) {

430

if (port < 1024 || port > 65535) {

431

throw new ParameterException(

432

CommandLine.this,

433

"Port must be between 1024 and 65535, got: $port"

434

)

435

}

436

this.port = port

437

}

438

private int port = 8080

439

440

@Option(names = ['-f', '--file'], description = 'Input file')

441

void setFile(File file) {

442

if (!file.exists()) {

443

throw new ParameterException(

444

CommandLine.this,

445

"File does not exist: ${file.absolutePath}"

446

)

447

}

448

if (!file.canRead()) {

449

throw new ParameterException(

450

CommandLine.this,

451

"Cannot read file: ${file.absolutePath}"

452

)

453

}

454

this.file = file

455

}

456

private File file

457

458

@Option(names = ['-e', '--email'], description = 'Email address')

459

void setEmail(String email) {

460

if (!email.matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {

461

throw new ParameterException(

462

CommandLine.this,

463

"Invalid email format: $email"

464

)

465

}

466

this.email = email

467

}

468

private String email

469

470

void run() {

471

println "Port: $port"

472

println "File: ${file?.absolutePath}"

473

println "Email: $email"

474

}

475

}

476

477

// Custom exception handler

478

class CustomExceptionHandler implements IExceptionHandler2<List<Object>> {

479

List<Object> handleParseException(ParameterException ex, String[] args) {

480

System.err.println("Error: ${ex.message}")

481

ex.commandLine.usage(System.err)

482

return [1] // exit code

483

}

484

485

List<Object> handleExecutionException(ExecutionException ex, ParseResult parseResult) {

486

System.err.println("Execution failed: ${ex.cause?.message}")

487

return [2] // exit code

488

}

489

}

490

491

// Use with error handling

492

def commandLine = new CommandLine(new ValidatedCommand())

493

commandLine.setExceptionHandler(new CustomExceptionHandler())

494

int exitCode = commandLine.execute(args)

495

System.exit(exitCode)

496

```