icoa-cli 2.19.57 → 2.19.58

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.
@@ -327,10 +327,14 @@ function printQuestionProgress(current, total, answered) {
327
327
  console.log();
328
328
  console.log(` ${bar} ${chalk.white.bold(`${current}`)}${chalk.gray(`/${total}`)} ${chalk.gray(`(${answered} answered)`)} ${chalk.gray(`${pct}%`)}`);
329
329
  }
330
+ // Help budget per exam.md §3: 10 base + 5 hidden bonus (unlocked via `more help`).
331
+ // Demo still uses the lighter 5 + 3 set via _helpMax overrides at demo start.
332
+ const HELP_BASE = 10;
333
+ const HELP_WITH_BONUS = 15;
330
334
  function getHelpState(state) {
331
335
  return {
332
336
  used: state._helpUsed || 0,
333
- max: state._helpMax || 5,
337
+ max: state._helpMax || HELP_BASE,
334
338
  perQ: state._helpPerQ || {},
335
339
  eliminated: state._eliminated || {},
336
340
  };
@@ -488,9 +492,12 @@ function printQuestion(q, answer) {
488
492
  console.log(chalk.yellow(' !python3') + chalk.gray(' start Python'));
489
493
  }
490
494
  else {
495
+ // "used up" prompt shows until the bonus tier is reached; after that
496
+ // (already unlocked), show the full-used final state.
497
+ const bonusUnlocked = help.max >= HELP_WITH_BONUS;
491
498
  const helpLabel = remaining > 0
492
499
  ? chalk.gray(`${t('helpRemove')} `) + chalk.yellow(`(${remaining}/${help.max})`)
493
- : (help.max < 8 ? chalk.gray(`${t('helpUsedUp')}`) : chalk.gray(`${t('helpAllUsed')} (${help.used}/${help.max})`));
500
+ : (!bonusUnlocked ? chalk.gray(`${t('helpUsedUp')}`) : chalk.gray(`${t('helpAllUsed')} (${help.used}/${help.max})`));
494
501
  console.log(chalk.yellow(' A/B/C/D') + chalk.gray(` ${t('answerThis')}`));
495
502
  console.log(chalk.yellow(' help') + ' ' + helpLabel);
496
503
  }
@@ -835,10 +842,12 @@ export function registerExamCommand(program) {
835
842
  }
836
843
  // Check budget
837
844
  if (help.used >= help.max) {
838
- if (help.max < 8) {
845
+ const bonusUnlocked = help.max >= HELP_WITH_BONUS;
846
+ const bonusRemaining = HELP_WITH_BONUS - help.max;
847
+ if (!bonusUnlocked) {
839
848
  console.log();
840
849
  console.log(chalk.yellow(` Help used: ${help.used}/${help.max}`));
841
- console.log(chalk.white(' Need more? Type: ') + chalk.bold.yellow('more help') + chalk.gray(' (+3 bonus uses)'));
850
+ console.log(chalk.white(' Need more? Type: ') + chalk.bold.yellow('more help') + chalk.gray(` (+${bonusRemaining} bonus uses)`));
842
851
  console.log();
843
852
  }
844
853
  else {
@@ -904,7 +913,7 @@ export function registerExamCommand(program) {
904
913
  // ─── exam more-help ───
905
914
  exam
906
915
  .command('more-help')
907
- .description('Unlock 3 bonus help uses')
916
+ .description('Unlock hidden bonus help uses')
908
917
  .action(() => {
909
918
  logCommand('exam more-help');
910
919
  const state = getExamState();
@@ -913,7 +922,12 @@ export function registerExamCommand(program) {
913
922
  return;
914
923
  }
915
924
  const help = getHelpState(state);
916
- if (help.max >= 8) {
925
+ // Demo (5→8) and real exam (10→15) each add their own bonus tier. Pick
926
+ // the target based on what's already in state: demo-free exam keeps the
927
+ // smaller step, real exams get the full 15.
928
+ const isDemo = state.session.examId === 'demo-free';
929
+ const bonusTarget = isDemo ? 8 : HELP_WITH_BONUS;
930
+ if (help.max >= bonusTarget) {
917
931
  console.log(chalk.gray(' Bonus help already unlocked.'));
918
932
  return;
919
933
  }
@@ -921,10 +935,11 @@ export function registerExamCommand(program) {
921
935
  console.log(chalk.gray(` You still have ${help.max - help.used} helps remaining. Use them first!`));
922
936
  return;
923
937
  }
924
- state._helpMax = 8;
938
+ const bonusDelta = bonusTarget - help.max;
939
+ state._helpMax = bonusTarget;
925
940
  saveExamState(state);
926
941
  console.log();
927
- console.log(chalk.green(' 🎁 +3 bonus help unlocked! (3/8 remaining)'));
942
+ console.log(chalk.green(` 🎁 +${bonusDelta} bonus help unlocked! (${bonusDelta}/${bonusTarget} remaining)`));
928
943
  console.log(chalk.gray(' Type "help" on any question to use.'));
929
944
  console.log();
930
945
  });
@@ -1741,7 +1756,9 @@ export function registerExamCommand(program) {
1741
1756
  drawProgress(100, 'Ready!');
1742
1757
  console.log();
1743
1758
  console.log();
1744
- saveExamState({ session, questions: DEMO_QUESTIONS, answers: {} });
1759
+ // Demo keeps the lighter 5 + 3 help budget (10 questions, so 5 base =
1760
+ // half the questions; plenty without making the exam trivial).
1761
+ saveExamState({ session, questions: DEMO_QUESTIONS, answers: {}, _helpMax: 5 });
1745
1762
  printKeyValue('Questions', String(DEMO_PICK_SIZE));
1746
1763
  printKeyValue('Duration', 'No time limit');
1747
1764
  // Show first question
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.57",
3
+ "version": "2.19.58",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {