| 436 | |
| 437 | // ---- Helper: handle slash commands ---- |
| 438 | function handleSlash(input: string): 'exit' | 'ok' { |
| 439 | const parts = input.trim().split(/\s+/); |
| 440 | const cmd = parts[0]; |
| 441 | const arg = parts.slice(1).join(' '); |
| 442 | |
| 443 | switch (cmd) { |
| 444 | case '/exit': |
| 445 | stdout.write(`${dim}Goodbye!${reset}\n`); |
| 446 | return 'exit'; |
| 447 | |
| 448 | case '/help': |
| 449 | showHelp(); |
| 450 | return 'ok'; |
| 451 | |
| 452 | case '/clear': |
| 453 | state.messages = []; |
| 454 | stdout.write(`${dim}Conversation cleared.${reset}\n`); |
| 455 | return 'ok'; |
| 456 | |
| 457 | case '/system': { |
| 458 | if (arg) { |
| 459 | state.system = arg; |
| 460 | stdout.write(`${dim}System prompt set.${reset}\n`); |
| 461 | } else { |
| 462 | if (state.system) { |
| 463 | stdout.write(`${dim}System prompt:${reset}\n${state.system}\n`); |
| 464 | } else { |
| 465 | stdout.write(`${dim}No system prompt set.${reset}\n`); |
| 466 | } |
| 467 | } |
| 468 | return 'ok'; |
| 469 | } |
| 470 | |
| 471 | case '/model': { |
| 472 | if (arg) { |
| 473 | state.model = arg; |
| 474 | stdout.write(`${dim}Model set to: ${state.model}${reset}\n`); |
| 475 | } else { |
| 476 | stdout.write(`${dim}Current model: ${state.model}${reset}\n`); |
| 477 | } |
| 478 | return 'ok'; |
| 479 | } |
| 480 | |
| 481 | case '/save': { |
| 482 | if (!arg) { |
| 483 | stdout.write(`${dim}Usage: /save <file-path>${reset}\n`); |
| 484 | return 'ok'; |
| 485 | } |
| 486 | try { |
| 487 | const toSave: Array<{ role: string; content: string }> = []; |
| 488 | if (state.system) toSave.push({ role: 'system', content: state.system }); |
| 489 | for (const m of state.messages) { |
| 490 | toSave.push({ |
| 491 | role: m.role, |
| 492 | content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content), |
| 493 | }); |
| 494 | } |
| 495 | writeFileSync(arg, JSON.stringify(toSave, null, 2), 'utf-8'); |