tycono 0.3.13-beta.1 → 0.3.13-beta.3

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/bin/cli.js CHANGED
@@ -12,6 +12,7 @@ if (!process.env.__TYCONO_HEAP_SET && !process.execArgv.some(a => a.includes('ma
12
12
  execFileSync(process.execPath, [
13
13
  '--max-old-space-size=8192',
14
14
  '--expose-gc',
15
+ // '--heapsnapshot-near-heap-limit=1', // Enable for OOM diagnosis (creates large files)
15
16
  ...process.execArgv,
16
17
  fileURLToPath(import.meta.url),
17
18
  ...process.argv.slice(2),
package/bin/tycono.ts CHANGED
@@ -317,6 +317,19 @@ async function startServerForTui(): Promise<void> {
317
317
  process.on('SIGINT', shutdown);
318
318
  process.on('SIGTERM', shutdown);
319
319
 
320
+ // Handle TTY read errors gracefully (EIO on stdin when terminal disconnects)
321
+ // Without this: "Error: read EIO" → Unhandled 'error' event → crash
322
+ process.stdin.on('error', (err) => {
323
+ logStream.write(`[TTY] stdin error: ${err.code ?? err.message}\n`);
324
+ if (err.code === 'EIO' || err.code === 'EPIPE') {
325
+ // Terminal disconnected — graceful shutdown
326
+ shutdown();
327
+ }
328
+ });
329
+ process.stdout.on('error', (err) => {
330
+ logStream.write(`[TTY] stdout error: ${err.code ?? err.message}\n`);
331
+ });
332
+
320
333
  // Start TUI — stdout.write is NOT intercepted, Ink has full control
321
334
  const { startTui } = await import('../src/tui/index.tsx');
322
335
  await startTui({ port });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.3.13-beta.1",
3
+ "version": "0.3.13-beta.3",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -286,6 +286,23 @@ function StreamLineRow({ line }: { line: StreamLine }) {
286
286
  const QUICK_ACTIONS = ['waves', 'agents', 'sessions', 'ports', 'docs'] as const;
287
287
  type QuickAction = typeof QUICK_ACTIONS[number];
288
288
 
289
+ /* ─── Command Autocomplete ─── */
290
+ const COMMANDS: Array<{ cmd: string; desc: string }> = [
291
+ { cmd: '/new [text]', desc: 'Create new wave' },
292
+ { cmd: '/waves', desc: 'List waves' },
293
+ { cmd: '/focus <n>', desc: 'Switch wave' },
294
+ { cmd: '/stop', desc: 'Stop execution' },
295
+ { cmd: '/docs', desc: 'Wave files' },
296
+ { cmd: '/read <path>', desc: 'Preview file' },
297
+ { cmd: '/open <path>', desc: 'Open in editor' },
298
+ { cmd: '/agents', desc: 'Agent tree' },
299
+ { cmd: '/sessions', desc: 'Active sessions' },
300
+ { cmd: '/kill <id>', desc: 'Kill session' },
301
+ { cmd: '/cleanup', desc: 'Remove dead' },
302
+ { cmd: '/help', desc: 'Help' },
303
+ { cmd: '/quit', desc: 'Exit' },
304
+ ];
305
+
289
306
  export const CommandMode: React.FC<CommandModeProps> = ({
290
307
  events,
291
308
  allRoleIds,
@@ -300,6 +317,7 @@ export const CommandMode: React.FC<CommandModeProps> = ({
300
317
  const [userInputs, setUserInputs] = useState<StreamLine[]>([]);
301
318
  const [quickBarActive, setQuickBarActive] = useState(false);
302
319
  const [quickBarIndex, setQuickBarIndex] = useState(0);
320
+ const [acIndex, setAcIndex] = useState(0);
303
321
 
304
322
  // Convert events to stream lines (collapse consecutive thinking from same role)
305
323
  const eventLines: StreamLine[] = [];
@@ -332,8 +350,45 @@ export const CommandMode: React.FC<CommandModeProps> = ({
332
350
  const committedLines = allLines.slice(Math.max(0, committedRef.current - 20), committedRef.current);
333
351
  const liveLines = allLines.slice(committedRef.current);
334
352
 
335
- // Quick bar navigation
353
+ // Autocomplete: filter commands when input starts with /
354
+ const acOpen = input.startsWith('/') && input.length >= 1;
355
+ const acCandidates = acOpen
356
+ ? COMMANDS.filter(c => c.cmd.startsWith(input) || c.cmd.split(' ')[0].startsWith(input))
357
+ : [];
358
+ const acVisible = acOpen && acCandidates.length > 0;
359
+
360
+ // Quick bar + autocomplete navigation
336
361
  useInput((ch, key) => {
362
+ // Autocomplete mode
363
+ if (acVisible) {
364
+ if (key.downArrow) {
365
+ setAcIndex(i => Math.min(acCandidates.length - 1, i + 1));
366
+ return;
367
+ }
368
+ if (key.upArrow) {
369
+ setAcIndex(i => Math.max(0, i - 1));
370
+ return;
371
+ }
372
+ if (key.tab) {
373
+ // Tab → fill input with selected command (don't execute)
374
+ const selected = acCandidates[acIndex];
375
+ if (selected) {
376
+ const base = selected.cmd.split(' ')[0]; // e.g. "/new" from "/new [text]"
377
+ setInput(base + ' ');
378
+ setAcIndex(0);
379
+ }
380
+ return;
381
+ }
382
+ if (key.escape) {
383
+ setInput('');
384
+ setAcIndex(0);
385
+ return;
386
+ }
387
+ // Don't intercept Enter — let TextInput handle it for submission
388
+ return;
389
+ }
390
+
391
+ // Quick bar mode
337
392
  if (quickBarActive) {
338
393
  if (key.upArrow || key.escape) {
339
394
  setQuickBarActive(false);
@@ -421,13 +476,28 @@ export const CommandMode: React.FC<CommandModeProps> = ({
421
476
  <Text color={quickBarActive ? 'gray' : 'yellow'} bold>&gt; </Text>
422
477
  <TextInput
423
478
  value={input}
424
- onChange={setInput}
479
+ onChange={(v) => { setInput(v); setAcIndex(0); }}
425
480
  onSubmit={handleSubmit}
426
481
  placeholder=""
427
482
  focus={!quickBarActive}
428
483
  />
429
484
  </Box>
430
485
 
486
+ {/* Command autocomplete — shown when typing / */}
487
+ {acVisible && (
488
+ <Box flexDirection="column" paddingX={0}>
489
+ {acCandidates.slice(0, 8).map((c, i) => (
490
+ <Box key={c.cmd}>
491
+ <Text color={i === acIndex ? 'cyan' : 'gray'} bold={i === acIndex}>
492
+ {i === acIndex ? '\u25B8 ' : ' '}{c.cmd.split(' ')[0].padEnd(12)}
493
+ </Text>
494
+ <Text color="gray" dimColor> {c.desc}</Text>
495
+ </Box>
496
+ ))}
497
+ <Text color="gray" dimColor> \u2191\u2193 select Tab fill Enter run Esc cancel</Text>
498
+ </Box>
499
+ )}
500
+
431
501
  {/* Quick action bar — shown when arrow down from input */}
432
502
  {quickBarActive && (
433
503
  <Box paddingX={0} marginTop={0}>