• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

acossta / captan / 17147426189

22 Aug 2025 06:00AM UTC coverage: 83.051% (+2.4%) from 80.673%
17147426189

Pull #24

github

web-flow
Merge e8b55cf5c into 067376d50
Pull Request #24: feat: Complete CLI redesign with comprehensive testing

1206 of 1393 branches covered (86.58%)

Branch coverage included in aggregate %.

2460 of 3091 new or added lines in 16 files covered. (79.59%)

4 existing lines in 1 file now uncovered.

4586 of 5581 relevant lines covered (82.17%)

1628.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.19
/src/cli.ts
1
#!/usr/bin/env node
2✔
2
import { Command } from 'commander';
×
3
import { LOGO, NAME, TAGLINE } from './branding.js';
×
NEW
4
import * as handlers from './handlers/index.js';
×
5
import { createRequire } from 'module';
×
6

7
const require = createRequire(import.meta.url);
×
8
const packageJson = require('../package.json');
×
9

10
const program = new Command();
×
11

12
program
×
13
  .name('captan')
×
14
  .description(`${NAME} — ${TAGLINE}`)
×
15
  .version(packageJson.version)
×
16
  .showHelpAfterError('(use --help for usage)')
×
17
  .addHelpText('before', LOGO + '\n');
×
18

19
// ============================================
20
// STAKEHOLDER RESOURCE COMMANDS
21
// ============================================
NEW
22
const stakeholder = program.command('stakeholder').description('Manage stakeholders');
×
23

NEW
24
stakeholder
×
NEW
25
  .command('add')
×
NEW
26
  .description('Add a new stakeholder')
×
NEW
27
  .requiredOption('--name <name>', 'stakeholder name')
×
NEW
28
  .option('--email <email>', 'email address')
×
NEW
29
  .option('--entity <type>', 'entity type (PERSON or ENTITY)', 'PERSON')
×
NEW
30
  .action((opts) => {
×
NEW
31
    const result = handlers.handleStakeholderAdd(opts);
×
32
    if (!result.success) {
×
33
      console.error(result.message);
×
34
      process.exit(1);
×
35
    }
×
36
    console.log(result.message);
×
37
  });
×
38

NEW
39
stakeholder
×
NEW
40
  .command('list')
×
NEW
41
  .description('List all stakeholders')
×
NEW
42
  .option('--format <format>', 'output format (table or json)', 'table')
×
NEW
43
  .action((opts) => {
×
NEW
44
    const result = handlers.handleStakeholderList(opts);
×
NEW
45
    if (!result.success) {
×
NEW
46
      console.error(result.message);
×
NEW
47
      process.exit(1);
×
NEW
48
    }
×
NEW
49
    console.log(result.message);
×
NEW
50
  });
×
51

NEW
52
stakeholder
×
NEW
53
  .command('show [id-or-email]')
×
NEW
54
  .description('Show stakeholder details')
×
NEW
55
  .action((idOrEmail, opts) => {
×
NEW
56
    const result = handlers.handleStakeholderShow(idOrEmail, opts);
×
NEW
57
    if (!result.success) {
×
NEW
58
      console.error(result.message);
×
NEW
59
      process.exit(1);
×
NEW
60
    }
×
NEW
61
    console.log(result.message);
×
NEW
62
  });
×
63

NEW
64
stakeholder
×
NEW
65
  .command('update [id-or-email]')
×
NEW
66
  .description('Update stakeholder information')
×
NEW
67
  .option('--name <name>', 'new name')
×
NEW
68
  .option('--email <email>', 'new email')
×
NEW
69
  .action((idOrEmail, opts) => {
×
NEW
70
    const result = handlers.handleStakeholderUpdate(idOrEmail, opts);
×
NEW
71
    if (!result.success) {
×
NEW
72
      console.error(result.message);
×
NEW
73
      process.exit(1);
×
NEW
74
    }
×
NEW
75
    console.log(result.message);
×
NEW
76
  });
×
77

NEW
78
stakeholder
×
NEW
79
  .command('delete [id-or-email]')
×
NEW
80
  .description('Delete a stakeholder')
×
NEW
81
  .option('--force', 'force deletion even if stakeholder has holdings')
×
NEW
82
  .action((idOrEmail, opts) => {
×
NEW
83
    const result = handlers.handleStakeholderDelete(idOrEmail, opts);
×
NEW
84
    if (!result.success) {
×
NEW
85
      console.error(result.message);
×
NEW
86
      process.exit(1);
×
NEW
87
    }
×
NEW
88
    console.log(result.message);
×
NEW
89
  });
×
90

91
// ============================================
92
// SECURITY CLASS RESOURCE COMMANDS
93
// ============================================
NEW
94
const security = program.command('security').description('Manage security classes');
×
95

NEW
96
security
×
NEW
97
  .command('add')
×
NEW
98
  .description('Add a new security class')
×
NEW
99
  .requiredOption('--kind <kind>', 'security type (COMMON, PREFERRED, or OPTION_POOL)')
×
NEW
100
  .requiredOption('--label <label>', 'display label')
×
NEW
101
  .option('--authorized <amount>', 'authorized shares/units', '10000000')
×
NEW
102
  .option('--par <value>', 'par value per share')
×
103
  .action((opts) => {
×
NEW
104
    const result = handlers.handleSecurityAdd(opts);
×
105
    if (!result.success) {
×
106
      console.error(result.message);
×
107
      process.exit(1);
×
108
    }
×
109
    console.log(result.message);
×
110
  });
×
111

NEW
112
security
×
NEW
113
  .command('list')
×
NEW
114
  .description('List all security classes')
×
NEW
115
  .option('--format <format>', 'output format (table or json)', 'table')
×
116
  .action((opts) => {
×
NEW
117
    const result = handlers.handleSecurityList(opts);
×
118
    if (!result.success) {
×
119
      console.error(result.message);
×
120
      process.exit(1);
×
121
    }
×
122
    console.log(result.message);
×
123
  });
×
124

NEW
125
security
×
NEW
126
  .command('show [id]')
×
NEW
127
  .description('Show security class details')
×
NEW
128
  .action((id, opts) => {
×
NEW
129
    const result = handlers.handleSecurityShow(id, opts);
×
NEW
130
    if (!result.success) {
×
NEW
131
      console.error(result.message);
×
NEW
132
      process.exit(1);
×
NEW
133
    }
×
NEW
134
    console.log(result.message);
×
NEW
135
  });
×
136

NEW
137
security
×
NEW
138
  .command('update [id]')
×
NEW
139
  .description('Update security class')
×
NEW
140
  .option('--authorized <amount>', 'new authorized amount')
×
NEW
141
  .option('--label <label>', 'new label')
×
NEW
142
  .action((id, opts) => {
×
NEW
143
    const result = handlers.handleSecurityUpdate(id, opts);
×
NEW
144
    if (!result.success) {
×
NEW
145
      console.error(result.message);
×
NEW
146
      process.exit(1);
×
NEW
147
    }
×
NEW
148
    console.log(result.message);
×
NEW
149
  });
×
150

NEW
151
security
×
NEW
152
  .command('delete [id]')
×
NEW
153
  .description('Delete a security class')
×
NEW
154
  .option('--force', 'force deletion even if shares have been issued')
×
NEW
155
  .action((id, opts) => {
×
NEW
156
    const result = handlers.handleSecurityDelete(id, opts);
×
NEW
157
    if (!result.success) {
×
NEW
158
      console.error(result.message);
×
NEW
159
      process.exit(1);
×
NEW
160
    }
×
NEW
161
    console.log(result.message);
×
NEW
162
  });
×
163

164
// ============================================
165
// ISSUANCE RESOURCE COMMANDS
166
// ============================================
NEW
167
const issuance = program.command('issuance').description('Manage share issuances');
×
168

NEW
169
issuance
×
NEW
170
  .command('add')
×
NEW
171
  .description('Issue new shares')
×
NEW
172
  .requiredOption('--stakeholder [id-or-email]', 'stakeholder ID or email')
×
NEW
173
  .requiredOption('--security [id]', 'security class ID')
×
NEW
174
  .requiredOption('--qty <amount>', 'number of shares')
×
NEW
175
  .option('--pps <price>', 'price per share')
×
NEW
176
  .option('--date <date>', 'issuance date (YYYY-MM-DD)', new Date().toISOString().slice(0, 10))
×
177
  .action((opts) => {
×
NEW
178
    const result = handlers.handleIssuanceAdd(opts);
×
179
    if (!result.success) {
×
180
      console.error(result.message);
×
181
      process.exit(1);
×
182
    }
×
183
    console.log(result.message);
×
184
  });
×
185

NEW
186
issuance
×
NEW
187
  .command('list')
×
NEW
188
  .description('List all issuances')
×
NEW
189
  .option('--stakeholder [id-or-email]', 'filter by stakeholder')
×
NEW
190
  .option('--format <format>', 'output format (table or json)', 'table')
×
UNCOV
191
  .action((opts) => {
×
NEW
192
    const result = handlers.handleIssuanceList(opts);
×
193
    if (!result.success) {
×
194
      console.error(result.message);
×
195
      process.exit(1);
×
196
    }
×
197
    console.log(result.message);
×
198
  });
×
199

NEW
200
issuance
×
NEW
201
  .command('show [id]')
×
NEW
202
  .description('Show issuance details')
×
NEW
203
  .action((id, opts) => {
×
NEW
204
    const result = handlers.handleIssuanceShow(id, opts);
×
NEW
205
    if (!result.success) {
×
NEW
206
      console.error(result.message);
×
NEW
207
      process.exit(1);
×
NEW
208
    }
×
NEW
209
    console.log(result.message);
×
NEW
210
  });
×
211

NEW
212
issuance
×
NEW
213
  .command('update [id]')
×
NEW
214
  .description('Update an issuance')
×
NEW
215
  .option('--qty <amount>', 'new share quantity')
×
NEW
216
  .option('--pps <price>', 'new price per share')
×
NEW
217
  .action((id, opts) => {
×
NEW
218
    const result = handlers.handleIssuanceUpdate(id, opts);
×
NEW
219
    if (!result.success) {
×
NEW
220
      console.error(result.message);
×
NEW
221
      process.exit(1);
×
NEW
222
    }
×
NEW
223
    console.log(result.message);
×
NEW
224
  });
×
225

NEW
226
issuance
×
NEW
227
  .command('delete [id]')
×
NEW
228
  .description('Delete an issuance')
×
NEW
229
  .option('--force', 'force deletion')
×
NEW
230
  .action((id, opts) => {
×
NEW
231
    const result = handlers.handleIssuanceDelete(id, opts);
×
NEW
232
    if (!result.success) {
×
NEW
233
      console.error(result.message);
×
NEW
234
      process.exit(1);
×
NEW
235
    }
×
NEW
236
    console.log(result.message);
×
NEW
237
  });
×
238

239
// ============================================
240
// GRANT RESOURCE COMMANDS
241
// ============================================
NEW
242
const grant = program.command('grant').description('Manage option grants');
×
243

NEW
244
grant
×
NEW
245
  .command('add')
×
NEW
246
  .description('Grant new options')
×
NEW
247
  .requiredOption('--stakeholder [id-or-email]', 'stakeholder ID or email')
×
NEW
248
  .requiredOption('--qty <amount>', 'number of options')
×
NEW
249
  .requiredOption('--exercise <price>', 'exercise price per share')
×
NEW
250
  .option('--pool [id]', 'option pool ID (defaults to first pool)')
×
NEW
251
  .option('--date <date>', 'grant date (YYYY-MM-DD)', new Date().toISOString().slice(0, 10))
×
NEW
252
  .option('--vesting-months <months>', 'total vesting period in months', '48')
×
NEW
253
  .option('--cliff-months <months>', 'cliff period in months', '12')
×
NEW
254
  .option('--vesting-start <date>', 'vesting start date (defaults to grant date)')
×
255
  .option('--no-vesting', 'grant without vesting schedule')
×
256
  .action((opts) => {
×
NEW
257
    const result = handlers.handleGrantAdd(opts);
×
258
    if (!result.success) {
×
259
      console.error(result.message);
×
260
      process.exit(1);
×
261
    }
×
262
    console.log(result.message);
×
263
  });
×
264

NEW
265
grant
×
NEW
266
  .command('list')
×
NEW
267
  .description('List all option grants')
×
NEW
268
  .option('--stakeholder [id-or-email]', 'filter by stakeholder')
×
NEW
269
  .option('--format <format>', 'output format (table or json)', 'table')
×
NEW
270
  .action((opts) => {
×
NEW
271
    const result = handlers.handleGrantList(opts);
×
NEW
272
    if (!result.success) {
×
NEW
273
      console.error(result.message);
×
NEW
274
      process.exit(1);
×
NEW
275
    }
×
NEW
276
    console.log(result.message);
×
NEW
277
  });
×
278

NEW
279
grant
×
NEW
280
  .command('show [id]')
×
NEW
281
  .description('Show grant details')
×
NEW
282
  .action((id, opts) => {
×
NEW
283
    const result = handlers.handleGrantShow(id, opts);
×
NEW
284
    if (!result.success) {
×
NEW
285
      console.error(result.message);
×
NEW
286
      process.exit(1);
×
NEW
287
    }
×
NEW
288
    console.log(result.message);
×
NEW
289
  });
×
290

NEW
291
grant
×
NEW
292
  .command('update [id]')
×
NEW
293
  .description('Update a grant')
×
NEW
294
  .option('--vesting-start <date>', 'new vesting start date')
×
NEW
295
  .option('--exercise <price>', 'new exercise price')
×
NEW
296
  .action((id, opts) => {
×
NEW
297
    const result = handlers.handleGrantUpdate(id, opts);
×
NEW
298
    if (!result.success) {
×
NEW
299
      console.error(result.message);
×
NEW
300
      process.exit(1);
×
NEW
301
    }
×
NEW
302
    console.log(result.message);
×
NEW
303
  });
×
304

NEW
305
grant
×
NEW
306
  .command('delete [id]')
×
NEW
307
  .description('Delete a grant')
×
NEW
308
  .option('--force', 'force deletion even if partially vested')
×
NEW
309
  .action((id, opts) => {
×
NEW
310
    const result = handlers.handleGrantDelete(id, opts);
×
NEW
311
    if (!result.success) {
×
NEW
312
      console.error(result.message);
×
NEW
313
      process.exit(1);
×
NEW
314
    }
×
NEW
315
    console.log(result.message);
×
NEW
316
  });
×
317

318
// ============================================
319
// SAFE RESOURCE COMMANDS
320
// ============================================
NEW
321
const safe = program.command('safe').description('Manage SAFE investments');
×
322

NEW
323
safe
×
NEW
324
  .command('add')
×
NEW
325
  .description('Add a new SAFE')
×
NEW
326
  .requiredOption('--stakeholder [id-or-email]', 'stakeholder ID or email')
×
NEW
327
  .requiredOption('--amount <amount>', 'investment amount')
×
NEW
328
  .option('--cap <amount>', 'valuation cap')
×
329
  .option('--discount <pct>', 'discount percentage (e.g., 20 for 20%)')
×
NEW
330
  .option('--type <type>', 'SAFE type (pre-money or post-money)', 'post-money')
×
NEW
331
  .option('--date <date>', 'investment date (YYYY-MM-DD)', new Date().toISOString().slice(0, 10))
×
NEW
332
  .option('--note <note>', 'optional note')
×
UNCOV
333
  .action((opts) => {
×
NEW
334
    const result = handlers.handleSafeAdd(opts);
×
335
    if (!result.success) {
×
336
      console.error(result.message);
×
337
      process.exit(1);
×
338
    }
×
339
    console.log(result.message);
×
340
  });
×
341

NEW
342
safe
×
NEW
343
  .command('list')
×
NEW
344
  .description('List all SAFEs')
×
NEW
345
  .option('--stakeholder [id-or-email]', 'filter by stakeholder')
×
NEW
346
  .option('--format <format>', 'output format (table or json)', 'table')
×
NEW
347
  .action((opts) => {
×
NEW
348
    const result = handlers.handleSafeList(opts);
×
349
    if (!result.success) {
×
350
      console.error(result.message);
×
351
      process.exit(1);
×
352
    }
×
353
    console.log(result.message);
×
354
  });
×
355

NEW
356
safe
×
NEW
357
  .command('show [id]')
×
NEW
358
  .description('Show SAFE details')
×
NEW
359
  .action((id, opts) => {
×
NEW
360
    const result = handlers.handleSafeShow(id, opts);
×
NEW
361
    if (!result.success) {
×
NEW
362
      console.error(result.message);
×
NEW
363
      process.exit(1);
×
NEW
364
    }
×
NEW
365
    console.log(result.message);
×
NEW
366
  });
×
367

NEW
368
safe
×
NEW
369
  .command('update [id]')
×
NEW
370
  .description('Update a SAFE')
×
NEW
371
  .option('--discount <pct>', 'new discount percentage')
×
NEW
372
  .option('--cap <amount>', 'new valuation cap')
×
NEW
373
  .action((id, opts) => {
×
NEW
374
    const result = handlers.handleSafeUpdate(id, opts);
×
NEW
375
    if (!result.success) {
×
NEW
376
      console.error(result.message);
×
NEW
377
      process.exit(1);
×
NEW
378
    }
×
NEW
379
    console.log(result.message);
×
NEW
380
  });
×
381

NEW
382
safe
×
NEW
383
  .command('delete [id]')
×
NEW
384
  .description('Delete a SAFE')
×
NEW
385
  .option('--force', 'force deletion')
×
NEW
386
  .action((id, opts) => {
×
NEW
387
    const result = handlers.handleSafeDelete(id, opts);
×
NEW
388
    if (!result.success) {
×
NEW
389
      console.error(result.message);
×
NEW
390
      process.exit(1);
×
NEW
391
    }
×
NEW
392
    console.log(result.message);
×
NEW
393
  });
×
394

NEW
395
safe
×
396
  .command('convert')
×
NEW
397
  .description('Convert all SAFEs to shares')
×
NEW
398
  .requiredOption('--pre-money <amount>', 'pre-money valuation')
×
NEW
399
  .requiredOption('--pps <price>', 'price per share')
×
NEW
400
  .option('--new-money <amount>', 'new money raised in round')
×
NEW
401
  .option('--date <date>', 'conversion date (YYYY-MM-DD)', new Date().toISOString().slice(0, 10))
×
402
  .option('--dry-run', 'preview conversion without executing')
×
403
  .action((opts) => {
×
NEW
404
    const result = handlers.handleSafeConvert(opts);
×
NEW
405
    if (!result.success) {
×
NEW
406
      console.error(result.message);
×
407
      process.exit(1);
×
408
    }
×
NEW
409
    console.log(result.message);
×
NEW
410
  });
×
411

412
// ============================================
413
// REPORT COMMANDS
414
// ============================================
NEW
415
const report = program.command('report').description('Generate reports');
×
416

NEW
417
report
×
NEW
418
  .command('summary')
×
NEW
419
  .description('Generate a comprehensive summary report')
×
NEW
420
  .option('--format <format>', 'output format (table or json)', 'table')
×
NEW
421
  .action((opts) => {
×
NEW
422
    const result = handlers.handleReportSummary(opts);
×
423
    if (!result.success) {
×
424
      console.error(result.message);
×
425
      process.exit(1);
×
426
    }
×
427
    console.log(result.message);
×
428
  });
×
429

NEW
430
report
×
NEW
431
  .command('ownership')
×
NEW
432
  .description('Show ownership breakdown')
×
NEW
433
  .option('--date <date>', 'as-of date (YYYY-MM-DD)')
×
NEW
434
  .option('--format <format>', 'output format (table or json)', 'table')
×
435
  .action((opts) => {
×
NEW
436
    const result = handlers.handleReportOwnership(opts);
×
437
    if (!result.success) {
×
438
      console.error(result.message);
×
439
      process.exit(1);
×
440
    }
×
441
    console.log(result.message);
×
442
  });
×
443

NEW
444
report
×
NEW
445
  .command('stakeholder [id-or-email]')
×
NEW
446
  .description('Generate stakeholder report')
×
NEW
447
  .action((idOrEmail, opts) => {
×
NEW
448
    const result = handlers.handleReportStakeholder(idOrEmail, opts);
×
449
    if (!result.success) {
×
450
      console.error(result.message);
×
451
      process.exit(1);
×
452
    }
×
453
    console.log(result.message);
×
454
  });
×
455

NEW
456
report
×
NEW
457
  .command('security [id]')
×
NEW
458
  .description('Generate security class report')
×
NEW
459
  .action((id, opts) => {
×
NEW
460
    const result = handlers.handleReportSecurity(id, opts);
×
NEW
461
    if (!result.success) {
×
NEW
462
      console.error(result.message);
×
463
      process.exit(1);
×
464
    }
×
NEW
465
    console.log(result.message);
×
NEW
466
  });
×
467

468
// ============================================
469
// EXPORT COMMANDS
470
// ============================================
NEW
471
const exportCmd = program.command('export').description('Export cap table data');
×
472

NEW
473
exportCmd
×
NEW
474
  .command('csv')
×
NEW
475
  .description('Export to CSV format')
×
NEW
476
  .option('--output <file>', 'output file path', 'captable.csv')
×
NEW
477
  .option('--no-options', 'exclude option grants')
×
NEW
478
  .action((opts) => {
×
NEW
479
    const result = handlers.handleExportCsv(opts);
×
480
    if (!result.success) {
×
481
      console.error(result.message);
×
482
      process.exit(1);
×
483
    }
×
484
    console.log(result.message);
×
485
  });
×
486

NEW
487
exportCmd
×
NEW
488
  .command('json')
×
NEW
489
  .description('Export to JSON format')
×
NEW
490
  .option('--output <file>', 'output file path', 'captable-export.json')
×
NEW
491
  .option('--pretty', 'pretty-print JSON')
×
492
  .action((opts) => {
×
NEW
493
    const result = handlers.handleExportJson(opts);
×
NEW
494
    if (!result.success) {
×
NEW
495
      console.error(result.message);
×
NEW
496
      process.exit(1);
×
NEW
497
    }
×
NEW
498
    console.log(result.message);
×
NEW
499
  });
×
500

NEW
501
exportCmd
×
NEW
502
  .command('pdf')
×
NEW
503
  .description('Export to PDF format')
×
NEW
504
  .option('--output <file>', 'output file path', 'captable.pdf')
×
NEW
505
  .action((opts) => {
×
NEW
506
    const result = handlers.handleExportPdf(opts);
×
507
    if (!result.success) {
×
508
      console.error(result.message);
×
509
      process.exit(1);
×
510
    }
×
511
    console.log(result.message);
×
512
  });
×
513

514
// ============================================
515
// SYSTEM COMMANDS
516
// ============================================
517
program
×
NEW
518
  .command('init')
×
NEW
519
  .description('Initialize a new cap table')
×
NEW
520
  .option('--wizard', 'run interactive setup wizard')
×
NEW
521
  .option('--name <name>', 'company name')
×
NEW
522
  .option('--type <type>', 'entity type (c-corp, s-corp, or llc)', 'c-corp')
×
NEW
523
  .option('--state <state>', 'state of incorporation', 'DE')
×
NEW
524
  .option('--currency <currency>', 'currency code', 'USD')
×
NEW
525
  .option('--authorized <amount>', 'authorized shares/units', '10000000')
×
NEW
526
  .option('--par <value>', 'par value per share', '0.00001')
×
NEW
527
  .option('--pool-pct <pct>', 'option pool as % of fully diluted', '10')
×
NEW
528
  .option('--founder <founder...>', 'founder(s) in format "Name:email:shares"')
×
NEW
529
  .option('--date <date>', 'incorporation date (YYYY-MM-DD)', new Date().toISOString().slice(0, 10))
×
NEW
530
  .action(async (opts) => {
×
NEW
531
    const result = await handlers.handleInit(opts);
×
532
    if (!result.success) {
×
533
      console.error(result.message);
×
534
      process.exit(1);
×
535
    }
×
536
    console.log(result.message);
×
537
  });
×
538

UNCOV
539
program
×
540
  .command('validate')
×
NEW
541
  .description('Validate cap table data')
×
NEW
542
  .option('--extended', 'perform extended validation with business rules')
×
NEW
543
  .option('--file <file>', 'captable file to validate', 'captable.json')
×
544
  .action((opts) => {
×
545
    const result = handlers.handleValidate(opts);
×
546
    if (!result.success) {
×
547
      console.error(result.message);
×
548
      process.exit(1);
×
549
    }
×
550
    console.log(result.message);
×
551
  });
×
552

UNCOV
553
program
×
554
  .command('schema')
×
NEW
555
  .description('Generate JSON schema file')
×
NEW
556
  .option('--output <file>', 'output file path', 'captable.schema.json')
×
557
  .action((opts) => {
×
558
    const result = handlers.handleSchema(opts);
×
559
    if (!result.success) {
×
560
      console.error(result.message);
×
561
      process.exit(1);
×
562
    }
×
563
    console.log(result.message);
×
564
  });
×
565

NEW
566
program
×
NEW
567
  .command('log')
×
NEW
568
  .description('View audit log')
×
NEW
569
  .option('--action <action>', 'filter by action type')
×
NEW
570
  .option('--limit <number>', 'limit number of entries', '20')
×
NEW
571
  .action((opts) => {
×
NEW
572
    const result = handlers.handleLog(opts);
×
NEW
573
    if (!result.success) {
×
NEW
574
      console.error(result.message);
×
NEW
575
      process.exit(1);
×
NEW
576
    }
×
NEW
577
    console.log(result.message);
×
NEW
578
  });
×
579

NEW
580
program
×
NEW
581
  .command('version')
×
NEW
582
  .description('Show version information')
×
NEW
583
  .action(() => {
×
NEW
584
    console.log(packageJson.version);
×
NEW
585
  });
×
586

NEW
587
program
×
NEW
588
  .command('help [command]')
×
NEW
589
  .description('Show help for a command')
×
NEW
590
  .action((command) => {
×
NEW
591
    if (command) {
×
NEW
592
      const cmd = program.commands.find((c) => c.name() === command);
×
NEW
593
      if (cmd) {
×
NEW
594
        cmd.outputHelp();
×
NEW
595
      } else {
×
NEW
596
        console.error(`Unknown command: ${command}`);
×
NEW
597
        program.outputHelp();
×
NEW
598
      }
×
NEW
599
    } else {
×
NEW
600
      program.outputHelp();
×
NEW
601
    }
×
NEW
602
  });
×
603

604
// Parse and execute
605
program.parseAsync(process.argv).catch((error) => {
×
606
  console.error(`❌ ${error.message}`);
×
607
  process.exit(1);
×
608
});
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc