icoa-cli 2.7.2 → 2.8.0

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.
@@ -85,13 +85,9 @@ function printQuestion(q, answer) {
85
85
  }
86
86
  console.log();
87
87
  // Navigation hint
88
- const nav = [];
89
- if (q.number > 1)
90
- nav.push('exam prev');
91
- if (q.number < total)
92
- nav.push('exam next');
93
- nav.push(`exam answer ${q.number} <A-D>`);
94
- console.log(chalk.gray(` ${nav.join(' · ')}`));
88
+ console.log(chalk.gray(' Type ') + chalk.white('A') + chalk.gray('/') + chalk.white('B') + chalk.gray('/') + chalk.white('C') + chalk.gray('/') + chalk.white('D') + chalk.gray(' to answer') +
89
+ (q.number > 1 ? chalk.gray(' · ') + chalk.white('prev') : '') +
90
+ (q.number < total ? chalk.gray(' · ') + chalk.white('next') : ''));
95
91
  }
96
92
  export function registerExamCommand(program) {
97
93
  const exam = program.command('exam').description('National selection exam');
@@ -210,6 +206,8 @@ export function registerExamCommand(program) {
210
206
  printError(`Question ${n} not found (1-${state.questions.length}).`);
211
207
  return;
212
208
  }
209
+ state._lastQ = num;
210
+ saveExamState(state);
213
211
  printQuestion(q, state.answers[num]);
214
212
  }
215
213
  else {
package/dist/repl.js CHANGED
@@ -5,6 +5,7 @@ import { isConnected, getConfig, saveConfig } from './lib/config.js';
5
5
  import { isActivated, activateToken, isFreeCommand, isDeviceMatch, recordExit, recordResume, isFirstRunOrUpgrade, markVersionSeen } from './lib/access.js';
6
6
  import { setReplMode } from './lib/ui.js';
7
7
  import { isChatActive, handleChatMessage } from './commands/ai4ctf.js';
8
+ import { getExamState } from './lib/exam-state.js';
8
9
  import { resetTerminalTheme } from './lib/theme.js';
9
10
  import { ensureSandbox, runInSandbox, isDockerAvailable } from './lib/sandbox.js';
10
11
  import { logCommand } from './lib/logger.js';
@@ -271,6 +272,37 @@ export async function startRepl(program, resumeMode) {
271
272
  rl.prompt();
272
273
  return;
273
274
  }
275
+ // ─── Quick exam answer shortcuts ───
276
+ // "A" / "B" / "C" / "D" → answer current question
277
+ // "2 C" / "5 A" → answer specific question
278
+ const examState = getExamState();
279
+ if (examState) {
280
+ const upper = input.toUpperCase().trim();
281
+ // Single letter: A, B, C, D → answer current question
282
+ if (/^[ABCD]$/.test(upper)) {
283
+ const currentQ = examState._lastQ || 1;
284
+ processing = true;
285
+ try {
286
+ await program.parseAsync(['node', 'icoa', 'exam', 'answer', String(currentQ), upper]);
287
+ }
288
+ catch { }
289
+ processing = false;
290
+ rl.prompt();
291
+ return;
292
+ }
293
+ // "N X" pattern: e.g. "2 C", "15 A"
294
+ const match = upper.match(/^(\d+)\s+([ABCD])$/);
295
+ if (match) {
296
+ processing = true;
297
+ try {
298
+ await program.parseAsync(['node', 'icoa', 'exam', 'answer', match[1], match[2]]);
299
+ }
300
+ catch { }
301
+ processing = false;
302
+ rl.prompt();
303
+ return;
304
+ }
305
+ }
274
306
  const cmd = input.split(/\s+/)[0].toLowerCase();
275
307
  // ─── Mode-based command filtering ───
276
308
  const selectionCommands = ['join', 'exam', 'demo', 'next', 'prev', 'setup', 'lang', 'ref', 'ctf'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.7.2",
3
+ "version": "2.8.0",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {