icoa-cli 2.15.1 → 2.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -121,6 +121,38 @@ function printQuestion(q, answer) {
121
121
  }
122
122
  export function registerExamCommand(program) {
123
123
  const exam = program.command('exam').description('National selection exam');
124
+ // ─── exam nations ───
125
+ exam
126
+ .command('nations')
127
+ .description('View all participating countries')
128
+ .action(() => {
129
+ logCommand('exam nations');
130
+ console.log();
131
+ printHeader('ICOA Participating Countries and Regions');
132
+ console.log();
133
+ const nations = [
134
+ ['AU', 'Australia'], ['BR', 'Brazil'], ['CN', 'China'],
135
+ ['DE', 'Germany'], ['EG', 'Egypt'], ['FR', 'France'],
136
+ ['ID', 'Indonesia'], ['IN', 'India'], ['JP', 'Japan'],
137
+ ['KR', 'South Korea'], ['MY', 'Malaysia'], ['PE', 'Peru'],
138
+ ['PH', 'Philippines'], ['RU', 'Russia'], ['SA', 'Saudi Arabia'],
139
+ ['SG', 'Singapore'], ['TH', 'Thailand'], ['TR', 'Turkey'],
140
+ ['VN', 'Vietnam'], ['ZA', 'South Africa'],
141
+ ];
142
+ for (let i = 0; i < nations.length; i += 3) {
143
+ const row = nations.slice(i, i + 3)
144
+ .map(([code, name]) => ` ${chalk.cyan(code)} ${chalk.gray(name.padEnd(16))}`)
145
+ .join('');
146
+ console.log(row);
147
+ }
148
+ console.log();
149
+ console.log(chalk.gray(' 40+ countries and regions represented'));
150
+ console.log(chalk.gray(' New member? Contact: accreditation@icoa2026.au'));
151
+ console.log();
152
+ console.log(chalk.white(' Check exams for your country:'));
153
+ console.log(chalk.cyan(' exam list AU') + chalk.gray(' exam list PE') + chalk.gray(' exam list CN'));
154
+ console.log();
155
+ });
124
156
  // ─── exam list [country] ───
125
157
  exam
126
158
  .command('list [country]')
@@ -180,7 +212,7 @@ export function registerExamCommand(program) {
180
212
  ];
181
213
  });
182
214
  const countries = [...new Set(exams.map((e) => e.country))];
183
- printHeader(country ? `Exams — ${country.toUpperCase()}` : `Available Exams (${countries.length} countries)`);
215
+ printHeader(country ? `Exams — ${country.toUpperCase()}` : `Available Exams (${countries.length} regions)`);
184
216
  printTable(['ID', 'Name', 'Country', 'Questions', 'Duration', 'Status'], rows);
185
217
  console.log();
186
218
  console.log(chalk.gray(' Start: exam start <id>'));
@@ -638,6 +670,10 @@ export function registerExamCommand(program) {
638
670
  console.log(chalk.green.bold(' ai4ctf') + chalk.gray(' Chat with AI teammate (start here!)'));
639
671
  console.log(chalk.red.bold(' ctf4ai') + chalk.gray(' Trick the AI — make it say "koala"'));
640
672
  console.log();
673
+ console.log(chalk.gray(' For national selection exams:'));
674
+ console.log(chalk.white(' nations') + chalk.gray(' View all participating regions'));
675
+ console.log(chalk.white(' exam AU') + chalk.gray(' Enter Australia selection exam'));
676
+ console.log();
641
677
  }
642
678
  catch (err) {
643
679
  console.log();
package/dist/repl.js CHANGED
@@ -84,7 +84,7 @@ export async function startRepl(program, resumeMode) {
84
84
  console.log(chalk.red.bold(' CTF4AI') + chalk.gray(' [Day 2] Hack & evaluate AI — adversarial ML, red-teaming'));
85
85
  console.log();
86
86
  console.log(chalk.white(' Sydney, Australia') + chalk.gray(' · Jun 27 - Jul 2, 2026'));
87
- console.log(chalk.gray(' 40+ accredited nations and regions'));
87
+ console.log(chalk.gray(' 40+ countries and regions represented'));
88
88
  console.log();
89
89
  console.log(chalk.cyan(' ─────────────────────────────────────────────────'));
90
90
  console.log();
@@ -365,6 +365,18 @@ export async function startRepl(program, resumeMode) {
365
365
  rl.prompt();
366
366
  return;
367
367
  }
368
+ // "exam AU" / "exam PE" — shortcut to exam list <country>
369
+ const examCountryMatch = input.match(/^exam\s+([A-Z]{2,3})$/i);
370
+ if (examCountryMatch) {
371
+ processing = true;
372
+ try {
373
+ await program.parseAsync(['node', 'icoa', 'exam', 'list', examCountryMatch[1]]);
374
+ }
375
+ catch { }
376
+ processing = false;
377
+ rl.prompt();
378
+ return;
379
+ }
368
380
  // Clear
369
381
  if (input === 'clear' || input === 'cls') {
370
382
  console.clear();
@@ -427,7 +439,7 @@ export async function startRepl(program, resumeMode) {
427
439
  }
428
440
  const cmd = input.split(/\s+/)[0].toLowerCase();
429
441
  // ─── Mode-based command filtering ───
430
- const selectionCommands = ['exam', 'demo', 'next', 'prev', 'setup', 'lang', 'ref', 'ai4ctf', 'ctf4ai'];
442
+ const selectionCommands = ['exam', 'demo', 'nations', 'next', 'prev', 'setup', 'lang', 'ref', 'ai4ctf', 'ctf4ai'];
431
443
  const organizerCommands = ['join', 'exam', 'demo', 'next', 'prev', 'logout', 'setup', 'lang', 'ref', 'ctf'];
432
444
  if (mode === 'selection' && !selectionCommands.includes(cmd)) {
433
445
  console.log(chalk.gray(' Not available in Selection mode.'));
@@ -464,7 +476,7 @@ export async function startRepl(program, resumeMode) {
464
476
  'scoreboard', 'sb', 'status', 'time', 'hint', 'hint-b', 'hint-c',
465
477
  'hint-budget', 'ref', 'shell', 'files', 'connect', 'note',
466
478
  'log', 'lang', 'setup', 'env', 'ai4ctf', 'model', 'ctf',
467
- 'exam', 'demo', 'next', 'prev', 'logout', 'ctf4ai',
479
+ 'exam', 'demo', 'nations', 'next', 'prev', 'logout', 'ctf4ai',
468
480
  ];
469
481
  if (!knownCommands.includes(cmd)) {
470
482
  // Block dangerous commands
@@ -602,6 +614,7 @@ function mapCommand(input) {
602
614
  const rest = parts.slice(1);
603
615
  const ctfShortcuts = {
604
616
  'demo': ['exam', 'demo'],
617
+ 'nations': ['exam', 'nations'],
605
618
  'next': ['exam', 'next'],
606
619
  'prev': ['exam', 'prev'],
607
620
  'logout': ['ctf', 'logout'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.15.1",
3
+ "version": "2.15.3",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {