tsunami-code 3.11.8 → 3.11.9

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.
Files changed (3) hide show
  1. package/index.js +13 -6
  2. package/lib/ui.js +38 -1
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  } from './lib/memory.js';
27
27
  import { listMemories, readMemory, saveMemory, deleteMemory, getMemdirPath } from './lib/memdir.js';
28
28
 
29
- const VERSION = '3.11.8';
29
+ const VERSION = '3.11.9';
30
30
  const CONFIG_DIR = join(os.homedir(), '.tsunami-code');
31
31
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
32
32
  const DEFAULT_SERVER = 'https://radiometric-reita-amuck.ngrok-free.dev';
@@ -830,12 +830,19 @@ async function run() {
830
830
  case 'mode': {
831
831
  const m = rest[0]?.toLowerCase();
832
832
  if (!m) {
833
- console.log(blue('\n Permission modes:\n'));
834
- for (const pm of PERM_MODES) {
835
- const active = pm === permMode ? green(' ← active') : '';
836
- console.log(` ${cyan(pm.padEnd(16))}${active}`);
833
+ // Interactive arrow-key picker
834
+ process.stdout.write(blue('\n Select permission mode:\n\n'));
835
+ const currentIdx = PERM_MODES.indexOf(permMode);
836
+ const selected = await ui.selectFromList(PERM_MODES, currentIdx >= 0 ? currentIdx : 0);
837
+ if (selected) {
838
+ permMode = selected;
839
+ planMode = selected === 'readonly';
840
+ ui.setPlanMode(planMode);
841
+ updateModeLabel();
842
+ console.log(green(`\n Mode: ${selected}\n`));
843
+ } else {
844
+ console.log(dim(' Cancelled.\n'));
837
845
  }
838
- console.log(dim('\n Usage: /mode <name>\n'));
839
846
  break;
840
847
  }
841
848
  if (!PERM_MODES.includes(m)) {
package/lib/ui.js CHANGED
@@ -281,6 +281,43 @@ export function createUI({ planMode: initPlanMode = false, onLine, onTab, onExit
281
281
  });
282
282
  }
283
283
 
284
+ // ── selectFromList — arrow-key interactive picker ─────────────────────────
285
+ function selectFromList(items, initialIdx = 0) {
286
+ return new Promise((resolve) => {
287
+ let idx = initialIdx;
288
+ const n = items.length;
289
+
290
+ function render(isFirst) {
291
+ if (!isFirst) process.stdout.write(`\x1b[${n}A`); // move back up
292
+ for (let i = 0; i < n; i++) {
293
+ const active = i === idx;
294
+ const label = active ? ` ${cyan('❯')} ${items[i]}` : ` ${dim(items[i])}`;
295
+ process.stdout.write(`\x1b[2K${label}\n`);
296
+ }
297
+ }
298
+
299
+ render(true);
300
+
301
+ lineHandler = (key) => {
302
+ if (key === '\x1b[A') { // up
303
+ idx = (idx - 1 + n) % n;
304
+ render(false);
305
+ } else if (key === '\x1b[B') { // down
306
+ idx = (idx + 1) % n;
307
+ render(false);
308
+ } else if (key === '\r' || key === '\n') { // confirm
309
+ lineHandler = null;
310
+ process.stdout.write('\n');
311
+ resolve(items[idx]);
312
+ } else if (key === '\x1b' || key === '\x03') { // cancel
313
+ lineHandler = null;
314
+ process.stdout.write('\n');
315
+ resolve(null);
316
+ }
317
+ };
318
+ });
319
+ }
320
+
284
321
  function readChar(promptText) {
285
322
  return new Promise((resolve) => {
286
323
  process.stdout.write(promptText);
@@ -374,7 +411,7 @@ export function createUI({ planMode: initPlanMode = false, onLine, onTab, onExit
374
411
  return {
375
412
  start, pause, resume,
376
413
  setPlanMode, setContinuation, setModelLabel, setModeLabel,
377
- readLine, readChar,
414
+ readLine, readChar, selectFromList,
378
415
  wasInterrupted, stop, exitUI,
379
416
  };
380
417
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsunami-code",
3
- "version": "3.11.8",
3
+ "version": "3.11.9",
4
4
  "description": "Tsunami Code CLI — AI coding agent by Keystone World Management Navy Seal Unit XI3",
5
5
  "type": "module",
6
6
  "bin": {