omniharness-cli 0.1.80 → 0.1.81

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/dist/cli.js CHANGED
@@ -7,23 +7,6 @@ import { ownVersion, runUpdate } from './update.js';
7
7
  import { readActiveCombo } from './config/settings.js';
8
8
  import { OmniRouteClient } from './config/omniRoute.js';
9
9
  import { doctor, helpText, models } from './doctor.js';
10
- const ALT_ENTER = '\x1b[?1049h\x1b[H';
11
- const ALT_LEAVE = '\x1b[?1049l';
12
- /** Plain-text transcript flushed to the primary buffer on exit — the audit trail. */
13
- function dumpTranscript(messages) {
14
- if (messages.length === 0)
15
- return;
16
- const label = {
17
- user: 'you', assistant: 'assistant', thought: 'think', action: 'action',
18
- tool: 'tool', command: 'command', error: 'error',
19
- };
20
- process.stdout.write('\n');
21
- for (const message of messages) {
22
- const who = message.model ?? label[message.role] ?? message.role;
23
- process.stdout.write(`\n─ ${who} ─────\n${message.content.trim()}\n`);
24
- }
25
- process.stdout.write('\n');
26
- }
27
10
  // A crash anywhere below would otherwise surface as a raw Node stack trace, or
28
11
  // as an unhandled rejection that terminates the process without saying why.
29
12
  // A CLI should fail with a sentence.
@@ -81,23 +64,26 @@ else {
81
64
  void (async () => {
82
65
  const saved = await readActiveCombo();
83
66
  const engine = await createMastraEngine({ workspaceRoot: process.cwd(), model: saved });
84
- // Render the structured UI in the alternate screen buffer so that, on exit,
85
- // the primary buffer's scrollback is left holding the plain-text transcript —
86
- // the raw audit trail, without maintaining a parallel log.
87
- const alt = process.stdout.isTTY === true;
88
- if (alt)
89
- process.stdout.write(ALT_ENTER);
67
+ // Render in the primary buffer, not the alternate screen.
68
+ //
69
+ // The alternate screen has no scrollback that is what it is for — so
70
+ // while the harness was running you could not scroll back to anything: not
71
+ // a file the agent read three steps ago, not the reasoning behind an edit.
72
+ // The transcript only appeared once you quit, dumped into the primary
73
+ // buffer on exit, which is the wrong time to want it.
74
+ //
75
+ // Staying in the primary buffer means the TUI's <Static> region writes
76
+ // settled turns straight into real scrollback, where the terminal scrolls
77
+ // them like any other output and they are still there afterwards. The exit
78
+ // dump goes with it: scrollback already holds the transcript, and printing
79
+ // it again would duplicate every line.
80
+ //
81
+ // The cost is that quitting no longer restores the pre-launch screen. For
82
+ // a tool whose output you are meant to read back, that is the right trade.
83
+ //
90
84
  // The app owns Ctrl+C so idle quits but an in-flight run is cancelled first.
91
85
  const { waitUntilExit } = render(_jsx(TerminalInterface, { engine: engine }), { exitOnCtrlC: false });
92
- try {
93
- await waitUntilExit();
94
- }
95
- finally {
96
- if (alt) {
97
- process.stdout.write(ALT_LEAVE);
98
- dumpTranscript(engine.state.messages);
99
- }
100
- }
86
+ await waitUntilExit();
101
87
  })();
102
88
  }
103
89
  //# sourceMappingURL=cli.js.map
@@ -80,6 +80,12 @@ const MODE_SEQ = ['plan', 'build', 'research', 'crazy'];
80
80
  const PERM_SEQ = ['ask', 'acceptEdits', 'bypass'];
81
81
  const PERM_LABEL = { ask: 'manual', acceptEdits: 'accept edits', bypass: 'bypass' };
82
82
  const PERM_COLOR = (p) => (p === 'bypass' ? PALETTE.error : p === 'acceptEdits' ? PALETTE.warn : PALETTE.muted);
83
+ /**
84
+ * The widest the content is allowed to get, however wide the terminal is.
85
+ * Past roughly this the eye loses the start of a line on the way back from
86
+ * the end of it, and the chrome ends up further from what it describes.
87
+ */
88
+ const MAX_MEASURE = 100;
83
89
  const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
84
90
  const widthOf = (stdout) => Math.max(48, stdout.columns ?? 80);
85
91
  const clip = (text, width) => text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}…`;
@@ -197,7 +203,7 @@ function Field({ label, children }) {
197
203
  export function Hero(props) {
198
204
  const { width, endpoint, model, mode, perm, workspace, sessions, skills, plugins, mcpTools } = props;
199
205
  const wide = twoColumn(width);
200
- const outer = Math.min(width - 2, 84);
206
+ const outer = width;
201
207
  const column = wide ? Math.floor((outer - 3) / 2) : outer;
202
208
  const inner = Math.max(12, column - 4);
203
209
  // Values sit to the right of the label, so that is the room they actually get.
@@ -214,7 +220,7 @@ export function TerminalInterface({ engine }) {
214
220
  const { stdin } = useStdin();
215
221
  const [width, setWidth] = useState(() => widthOf(stdout));
216
222
  const [edit, setEdit] = useState({ value: '', cursor: 0 });
217
- const inputWidth = Math.max(16, width - 12);
223
+ const inputWidth = Math.max(16, Math.min(width, MAX_MEASURE) - 12);
218
224
  // Settled transcript. Rendered once each into <Static> — the terminal's own
219
225
  // scrollback is the history; there is no in-app viewport to scroll.
220
226
  const [lines, setLines] = useState(() => engine.state.messages.map(lineFromMessage));
@@ -912,7 +918,13 @@ export function TerminalInterface({ engine }) {
912
918
  }, [busy]);
913
919
  const modeKey = kitty === true ? 'M' : 'E';
914
920
  const modeAccent = MODE_ACCENT[mode];
915
- const contentWidth = Math.max(20, width - 6);
921
+ // On a wide terminal the UI used to stretch edge to edge, which puts the
922
+ // input box and the text you are reading at opposite ends of the screen.
923
+ // Hold the content to a readable measure and centre it instead; below that
924
+ // measure the gutter collapses and nothing changes.
925
+ const measure = Math.min(width, MAX_MEASURE);
926
+ const gutter = Math.max(2, Math.floor((width - measure) / 2));
927
+ const contentWidth = Math.max(20, measure - 6);
916
928
  const terminalRows = stdout.rows ?? 24;
917
929
  const metrics = engine.client.snapshotMetrics();
918
930
  const meter = contextMeter(metrics.compression.inputTokens, engine.state.activeModel, metrics.fallback.activeProvider);
@@ -933,7 +945,7 @@ export function TerminalInterface({ engine }) {
933
945
  const liveThinkView = liveThinkLines.slice(-Math.max(2, Math.floor(liveBudget / 2)));
934
946
  const liveAnswerView = liveAnswerLines.slice(-liveBudget);
935
947
  const doneAgents = agents.filter((lane) => lane.status === 'done').length;
936
- return _jsxs(Box, { flexDirection: "column", width: width, paddingX: 2, children: [_jsx(Static, { items: lines, children: (line, index) => _jsx(TranscriptEntry, { line: line, width: contentWidth, fallbackModel: engine.state.activeModel }, index) }, staticKey), _jsxs(Box, { flexDirection: "column", children: [lines.length === 0 && !busy && _jsx(Hero, { width: width, endpoint: engine.client.endpoint ?? 'omniroute', model: engine.state.activeModel, mode: mode, perm: permMode, workspace: engine.state.workspace.root, sessions: recentSessions, skills: engine.skills.length, plugins: new Set(engine.skills.map((skill) => skill.source).filter((source) => source !== undefined)).size, mcpTools: engine.mcpTools.length }), liveThink !== '' && _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.warn, children: "\u00B7 thinking" }), liveThinkView.map((segments, index) => _jsx(SegmentText, { segments: segments, role: "thinking" }, index))] }), liveAnswer !== '' && _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.accent, children: engine.state.activeModel }), liveAnswerView.map((segments, index) => _jsx(SegmentText, { segments: segments, role: "assistant" }, index))] }), toolCards.slice(-5).map((card) => {
948
+ return _jsxs(Box, { flexDirection: "column", width: width, paddingLeft: gutter, paddingRight: gutter, children: [_jsx(Static, { items: lines, children: (line, index) => _jsx(TranscriptEntry, { line: line, width: contentWidth, fallbackModel: engine.state.activeModel }, index) }, staticKey), _jsxs(Box, { flexDirection: "column", children: [lines.length === 0 && !busy && _jsx(Hero, { width: contentWidth + 2, endpoint: engine.client.endpoint ?? 'omniroute', model: engine.state.activeModel, mode: mode, perm: permMode, workspace: engine.state.workspace.root, sessions: recentSessions, skills: engine.skills.length, plugins: new Set(engine.skills.map((skill) => skill.source).filter((source) => source !== undefined)).size, mcpTools: engine.mcpTools.length }), liveThink !== '' && _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.warn, children: "\u00B7 thinking" }), liveThinkView.map((segments, index) => _jsx(SegmentText, { segments: segments, role: "thinking" }, index))] }), liveAnswer !== '' && _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.accent, children: engine.state.activeModel }), liveAnswerView.map((segments, index) => _jsx(SegmentText, { segments: segments, role: "assistant" }, index))] }), toolCards.slice(-5).map((card) => {
937
949
  const expanded = expandedTool === card.id;
938
950
  const dot = card.status === 'running' ? _jsx(Text, { color: PALETTE.warn, children: ".." }) : card.status === 'error' ? _jsx(Text, { color: PALETTE.error, children: "FAIL" }) : _jsx(Text, { color: PALETTE.success, children: "ok" });
939
951
  const head = card.name === 'run_command'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omniharness-cli",
3
- "version": "0.1.80",
3
+ "version": "0.1.81",
4
4
  "description": "OmniHarness — local-first agent orchestration harness for OmniRoute.",
5
5
  "license": "MIT",
6
6
  "type": "module",