icoa-cli 2.19.88 → 2.19.89

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.
package/dist/index.js CHANGED
@@ -98,7 +98,10 @@ async function pauseWithSkip(ms) {
98
98
  const program = new Command();
99
99
  program
100
100
  .name('icoa')
101
- .version('1.2.0')
101
+ // T4-X2: version now reads live from package.json (PKG_VERSION).
102
+ // Was hardcoded to '1.2.0' which made `icoa --version` misreport on every
103
+ // release — bug since v1.2.0 era, finally fixed.
104
+ .version(PKG_VERSION)
102
105
  .description('ICOA CLI — CLI-Native CTF Competition Terminal')
103
106
  .option('--resume', 'Resume previous session')
104
107
  .action(async (opts) => {
package/dist/repl.js CHANGED
@@ -426,6 +426,9 @@ export async function startRepl(program, resumeMode) {
426
426
  console.log(chalk.white(' scoreboard') + chalk.gray(' Live rankings'));
427
427
  console.log(chalk.white(' help') + chalk.gray(' Full command list'));
428
428
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
429
+ // T4-X1: parity with Selection-mode footer — give Olympiad users the
430
+ // same beginner escape-hatch hint so newcomers don't get stuck.
431
+ console.log(chalk.gray(' Tip: ') + chalk.cyan('help') + chalk.gray(' · ') + chalk.cyan('tutorial') + chalk.gray(' 30-sec tour · ') + chalk.cyan('Ctrl+C') + chalk.gray(' pauses · ') + chalk.cyan('quit') + chalk.gray(' closes'));
429
432
  console.log();
430
433
  }
431
434
  else if (activated) {
@@ -440,8 +443,9 @@ export async function startRepl(program, resumeMode) {
440
443
  console.log(chalk.white(' Step 2 ') + chalk.bold.cyan('challenges') + chalk.gray(' Browse & solve challenges'));
441
444
  console.log(chalk.white(' Step 3 ') + chalk.bold.cyan('hint') + chalk.gray(' Ask AI when stuck'));
442
445
  console.log();
443
- console.log(chalk.gray(' Also: ') + chalk.white('env') + chalk.gray(' check tools ') + chalk.white('help') + chalk.gray(' all commands'));
446
+ console.log(chalk.gray(' Also: ') + chalk.white('env') + chalk.gray(' check tools ') + chalk.white('help') + chalk.gray(' all commands ') + chalk.white('tutorial') + chalk.gray(' 30-sec tour'));
444
447
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
448
+ console.log(chalk.gray(' Tip: ') + chalk.cyan('Ctrl+C') + chalk.gray(' pauses · ') + chalk.cyan('exit') + chalk.gray(' → menu · ') + chalk.cyan('quit') + chalk.gray(' closes CLI'));
445
449
  console.log();
446
450
  }
447
451
  else {
@@ -457,7 +461,9 @@ export async function startRepl(program, resumeMode) {
457
461
  console.log(chalk.white(' ref web') + chalk.gray(' Quick reference for Web'));
458
462
  console.log(chalk.white(' env') + chalk.gray(' Check your tools'));
459
463
  console.log(chalk.white(' help') + chalk.gray(' All available commands'));
464
+ console.log(chalk.white(' tutorial') + chalk.gray(' 30-second walkthrough for first-timers'));
460
465
  console.log(chalk.gray(' ─────────────────────────────────────────────'));
466
+ console.log(chalk.gray(' Tip: ') + chalk.cyan('Ctrl+C') + chalk.gray(' pauses · ') + chalk.cyan('exit') + chalk.gray(' → menu · ') + chalk.cyan('quit') + chalk.gray(' closes CLI'));
461
467
  console.log();
462
468
  }
463
469
  }
@@ -971,7 +977,30 @@ export async function startRepl(program, resumeMode) {
971
977
  // Command tried to exit — continue REPL
972
978
  }
973
979
  else if (msg.includes('commander.unknownCommand')) {
974
- console.log(chalk.yellow(` Unknown command: ${cmd}. Type 'help' for commands.`));
980
+ // T4-12: typo suggestion via Levenshtein distance. Shorten the
981
+ // "unknown command" frustration by pointing to the likely-intended
982
+ // command when the user's input is within 2 edits of a real one.
983
+ const { distance } = await import('fastest-levenshtein');
984
+ const KNOWN_CMDS = [
985
+ 'ctf', 'hint', 'hint-b', 'hint-c', 'hint-budget', 'ref', 'shell',
986
+ 'files', 'connect', 'note', 'log', 'lang', 'setup', 'env',
987
+ 'ai4ctf', 'exam', 'ctf4ai', 'tutorial',
988
+ 'clear', 'cls', 'quit', 'exit', 'back', 'menu', 'help',
989
+ 'continue', 'activate', 'demo', 'challenges', 'status', 'scoreboard',
990
+ 'join', 'logout',
991
+ ];
992
+ const firstWord = cmd.split(/\s+/)[0] || cmd;
993
+ let best = { word: '', dist: Infinity };
994
+ for (const known of KNOWN_CMDS) {
995
+ const d = distance(firstWord.toLowerCase(), known);
996
+ if (d < best.dist)
997
+ best = { word: known, dist: d };
998
+ }
999
+ console.log(chalk.yellow(` Unknown command: ${cmd}.`));
1000
+ if (best.dist > 0 && best.dist <= 2) {
1001
+ console.log(chalk.gray(' Did you mean: ') + chalk.bold.cyan(best.word) + chalk.gray('?'));
1002
+ }
1003
+ console.log(chalk.gray(' Type ') + chalk.cyan('help') + chalk.gray(' for the full command list.'));
975
1004
  }
976
1005
  else if (msg.includes('commander.')) {
977
1006
  // Internal Commander errors — ignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.88",
3
+ "version": "2.19.89",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,6 +32,7 @@
32
32
  "chalk": "^5.4.1",
33
33
  "cli-table3": "^0.6.5",
34
34
  "commander": "^13.1.0",
35
+ "fastest-levenshtein": "^1.0.16",
35
36
  "marked": "^15.0.7",
36
37
  "marked-terminal": "^7.3.0",
37
38
  "ora": "^8.2.0"