@steipete/oracle 0.5.2 → 0.5.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/README.md CHANGED
@@ -38,7 +38,7 @@ npx @steipete/oracle status --hours 72
38
38
  npx @steipete/oracle session <id> --render
39
39
 
40
40
  # TUI (interactive, only for humans)
41
- npx @steipete/oracle
41
+ npx @steipete/oracle tui
42
42
  ```
43
43
 
44
44
  Engine auto-picks API when `OPENAI_API_KEY` is set, otherwise browser; browser is stable on macOS and works on Linux and Windows. On Linux pass `--browser-chrome-path/--browser-cookie-path` if detection fails; on Windows prefer `--browser-manual-login` or inline cookies if decryption is blocked.
@@ -49,7 +49,6 @@ const CLI_ENTRYPOINT = fileURLToPath(import.meta.url);
49
49
  const rawCliArgs = process.argv.slice(2);
50
50
  const userCliArgs = rawCliArgs[0] === CLI_ENTRYPOINT ? rawCliArgs.slice(1) : rawCliArgs;
51
51
  const isTty = process.stdout.isTTY;
52
- const tuiEnabled = () => isTty && process.env.ORACLE_NO_TUI !== '1';
53
52
  const program = new Command();
54
53
  let introPrinted = false;
55
54
  program.hook('preAction', () => {
@@ -66,8 +65,8 @@ program.hook('preAction', (thisCommand) => {
66
65
  if (userCliArgs.some((arg) => arg === '--help' || arg === '-h')) {
67
66
  return;
68
67
  }
69
- if (userCliArgs.length === 0 && tuiEnabled()) {
70
- // Skip prompt enforcement; runRootCommand will launch the TUI.
68
+ if (userCliArgs.length === 0) {
69
+ // Let the root action handle zero-arg entry (help + hint to `oracle tui`).
71
70
  return;
72
71
  }
73
72
  const opts = thisCommand.optsWithGlobals();
@@ -212,6 +211,13 @@ program
212
211
  token: commandOptions.token,
213
212
  });
214
213
  });
214
+ program
215
+ .command('tui')
216
+ .description('Launch the interactive terminal UI for humans (no automation).')
217
+ .action(async () => {
218
+ await sessionStore.ensureStorage();
219
+ await launchTui({ version: VERSION, printIntro: false });
220
+ });
215
221
  const sessionCommand = program
216
222
  .command('session [id]')
217
223
  .description('Attach to a stored session or list recent sessions when no ID is provided.')
@@ -422,12 +428,8 @@ async function runRootCommand(options) {
422
428
  console.log(chalk.dim(`Remote browser host detected: ${remoteHost}`));
423
429
  }
424
430
  if (userCliArgs.length === 0) {
425
- if (tuiEnabled()) {
426
- await launchTui({ version: VERSION, printIntro: false });
427
- return;
428
- }
429
- console.log(chalk.yellow('No prompt or subcommand supplied. See `oracle --help` for usage.'));
430
- program.help({ error: false });
431
+ console.log(chalk.yellow('No prompt or subcommand supplied. Run `oracle --help` or `oracle tui` for the TUI.'));
432
+ program.outputHelp();
431
433
  return;
432
434
  }
433
435
  const retentionHours = typeof options.retainHours === 'number' ? options.retainHours : undefined;
@@ -21,6 +21,8 @@ const PAGE_SIZE = 10;
21
21
  export async function launchTui({ version, printIntro = true }) {
22
22
  const userConfig = (await loadUserConfig()).config;
23
23
  const rich = isTty();
24
+ let pagingFailures = 0;
25
+ let exitMessageShown = false;
24
26
  if (printIntro) {
25
27
  if (rich) {
26
28
  console.log(chalk.bold('🧿 oracle'), `${version}`, dim('— Whispering your tokens to the silicon sage'));
@@ -76,15 +78,31 @@ export async function launchTui({ version, printIntro = true }) {
76
78
  prompt
77
79
  .then(({ selection: answer }) => resolve(answer))
78
80
  .catch((error) => {
79
- console.error(chalk.red('Paging failed; returning to recent list.'), error instanceof Error ? error.message : error);
81
+ pagingFailures += 1;
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ if (message.includes('SIGINT') || message.includes('force closed the prompt')) {
84
+ console.log(chalk.green('🧿 Closing the book. See you next prompt.'));
85
+ exitMessageShown = true;
86
+ resolve('__exit__');
87
+ return;
88
+ }
89
+ console.error(chalk.red('Paging failed; returning to recent list.'), message);
90
+ if (message.includes('setRawMode') || message.includes('EIO') || pagingFailures >= 3) {
91
+ console.error(chalk.red('Terminal input unavailable; exiting TUI.'), dim('Try `stty sane` then rerun oracle, or use `oracle recent`.'));
92
+ resolve('__exit__');
93
+ return;
94
+ }
80
95
  resolve('__reset__');
81
96
  });
82
97
  });
83
98
  if (process.env.ORACLE_DEBUG_TUI === '1') {
84
99
  console.error(`[tui] selection=${JSON.stringify(selection)}`);
85
100
  }
101
+ pagingFailures = 0;
86
102
  if (selection === '__exit__') {
87
- console.log(chalk.green('🧿 Closing the book. See you next prompt.'));
103
+ if (!exitMessageShown) {
104
+ console.log(chalk.green('🧿 Closing the book. See you next prompt.'));
105
+ }
88
106
  return;
89
107
  }
90
108
  if (selection === '__ask__') {
@@ -156,14 +174,26 @@ async function showSessionDetail(sessionId) {
156
174
  ...(isRunning ? [{ name: 'Refresh', value: 'refresh' }] : []),
157
175
  { name: 'Back', value: 'back' },
158
176
  ];
159
- const { next } = await inquirer.prompt([
160
- {
161
- name: 'next',
162
- type: 'select',
163
- message: 'Actions',
164
- choices: actions,
165
- },
166
- ]);
177
+ let next;
178
+ try {
179
+ ({ next } = await inquirer.prompt([
180
+ {
181
+ name: 'next',
182
+ type: 'select',
183
+ message: 'Actions',
184
+ choices: actions,
185
+ },
186
+ ]));
187
+ }
188
+ catch (error) {
189
+ const message = error instanceof Error ? error.message : String(error);
190
+ if (message.includes('SIGINT') || message.includes('force closed the prompt')) {
191
+ console.log(chalk.green('🧿 Closing the book. See you next prompt.'));
192
+ return;
193
+ }
194
+ console.error(chalk.red('Paging failed; returning to session list.'), message);
195
+ return;
196
+ }
167
197
  if (next === 'back') {
168
198
  return;
169
199
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steipete/oracle",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "CLI wrapper around OpenAI Responses API with GPT-5.1 Pro, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
5
5
  "type": "module",
6
6
  "main": "dist/bin/oracle-cli.js",