Skip to content

Commit 0120fe1

Browse files
committed
feat: add explicit tui entrypoint and changelog
1 parent 69341b7 commit 0120fe1

File tree

4 files changed

+47
-15
lines changed

4 files changed

+47
-15
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 0.5.3 — 2025-12-06
4+
5+
### Changed
6+
- `oracle` with no arguments now prints the help/usage banner; launch the interactive UI explicitly via `oracle tui` (keeps `ORACLE_FORCE_TUI` for automation/tests). README updated to match.
7+
8+
### Fixed
9+
- Prevented accidental TUI entry in scripts/automations that previously invoked `oracle` without arguments.
10+
311
## 0.5.2 — 2025-12-06
412

513
### Changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ npx @steipete/oracle status --hours 72
3838
npx @steipete/oracle session <id> --render
3939

4040
# TUI (interactive, only for humans)
41-
npx @steipete/oracle
41+
npx @steipete/oracle tui
4242
```
4343

4444
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.

bin/oracle-cli.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ const CLI_ENTRYPOINT = fileURLToPath(import.meta.url);
154154
const rawCliArgs = process.argv.slice(2);
155155
const userCliArgs = rawCliArgs[0] === CLI_ENTRYPOINT ? rawCliArgs.slice(1) : rawCliArgs;
156156
const isTty = process.stdout.isTTY;
157-
const tuiEnabled = () => isTty && process.env.ORACLE_NO_TUI !== '1';
158157

159158
const program = new Command();
160159
let introPrinted = false;
@@ -171,8 +170,8 @@ program.hook('preAction', (thisCommand) => {
171170
if (userCliArgs.some((arg) => arg === '--help' || arg === '-h')) {
172171
return;
173172
}
174-
if (userCliArgs.length === 0 && tuiEnabled()) {
175-
// Skip prompt enforcement; runRootCommand will launch the TUI.
173+
if (userCliArgs.length === 0) {
174+
// Let the root action handle zero-arg entry (help + hint to `oracle tui`).
176175
return;
177176
}
178177
const opts = thisCommand.optsWithGlobals() as CliOptions;
@@ -424,6 +423,14 @@ program
424423
});
425424
});
426425

426+
program
427+
.command('tui')
428+
.description('Launch the interactive terminal UI for humans (no automation).')
429+
.action(async () => {
430+
await sessionStore.ensureStorage();
431+
await launchTui({ version: VERSION, printIntro: false });
432+
});
433+
427434
const sessionCommand = program
428435
.command('session [id]')
429436
.description('Attach to a stored session or list recent sessions when no ID is provided.')
@@ -659,12 +666,8 @@ async function runRootCommand(options: CliOptions): Promise<void> {
659666
}
660667

661668
if (userCliArgs.length === 0) {
662-
if (tuiEnabled()) {
663-
await launchTui({ version: VERSION, printIntro: false });
664-
return;
665-
}
666-
console.log(chalk.yellow('No prompt or subcommand supplied. See `oracle --help` for usage.'));
667-
program.help({ error: false });
669+
console.log(chalk.yellow('No prompt or subcommand supplied. Run `oracle --help` or `oracle tui` for the TUI.'));
670+
program.outputHelp();
668671
return;
669672
}
670673
const retentionHours = typeof options.retainHours === 'number' ? options.retainHours : undefined;

tests/cli/noArgTui.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
import { describe, expect, test, vi } from 'vitest';
1+
import { beforeEach, describe, expect, test, vi } from 'vitest';
22

33
vi.mock('../../src/cli/tui/index.js', () => ({
44
launchTui: vi.fn().mockResolvedValue(undefined),
55
}));
66

77
const launchTuiMock = vi.mocked(await import('../../src/cli/tui/index.js')).launchTui as ReturnType<typeof vi.fn>;
88

9+
beforeEach(() => {
10+
vi.clearAllMocks();
11+
vi.resetModules();
12+
});
13+
914
describe('zero-arg TUI entry', () => {
10-
test('invokes launchTui when no args and TTY', async () => {
15+
test('shows help when no args (no TUI)', async () => {
1116
const originalArgv = process.argv;
1217
const originalTty = process.stdout.isTTY;
1318
process.argv = ['node', 'bin/oracle-cli.js']; // mimics zero-arg user input
1419
Object.defineProperty(process.stdout, 'isTTY', { value: true, configurable: true });
15-
process.env.ORACLE_FORCE_TUI = '1';
1620

1721
await import('../../bin/oracle-cli.js');
1822

@@ -21,10 +25,27 @@ describe('zero-arg TUI entry', () => {
2125
await new Promise((resolve) => setTimeout(resolve, 10));
2226
}
2327

24-
expect(launchTuiMock).toHaveBeenCalled();
28+
expect(launchTuiMock).not.toHaveBeenCalled();
2529

2630
// restore
27-
delete process.env.ORACLE_FORCE_TUI;
31+
process.argv = originalArgv;
32+
Object.defineProperty(process.stdout, 'isTTY', { value: originalTty, configurable: true });
33+
}, 15_000);
34+
35+
test('invokes launchTui via subcommand', async () => {
36+
const originalArgv = process.argv;
37+
const originalTty = process.stdout.isTTY;
38+
process.argv = ['node', 'bin/oracle-cli.js', 'tui'];
39+
Object.defineProperty(process.stdout, 'isTTY', { value: true, configurable: true });
40+
41+
await import('../../bin/oracle-cli.js');
42+
43+
for (let i = 0; i < 10 && launchTuiMock.mock.calls.length === 0; i += 1) {
44+
await new Promise((resolve) => setTimeout(resolve, 10));
45+
}
46+
47+
expect(launchTuiMock).toHaveBeenCalled();
48+
2849
process.argv = originalArgv;
2950
Object.defineProperty(process.stdout, 'isTTY', { value: originalTty, configurable: true });
3051
}, 15_000);

0 commit comments

Comments
 (0)