@beunec/rivine 0.2.1 → 0.3.1
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 +79 -72
- package/dist/api/workspace.js +41 -5
- package/dist/index.js +20 -7
- package/dist/protocol/permissions.js +0 -4
- package/dist/protocol/runner.js +194 -18
- package/dist/protocol/shell.js +5 -2
- package/dist/runHeadless.js +10 -2
- package/dist/session.js +69 -3
- package/dist/ui/App.js +80 -19
- package/dist/ui/components.js +2 -3
- package/dist/ui/headless.js +7 -4
- package/dist/ui/notices.js +20 -0
- package/dist/ui/phase.js +28 -0
- package/dist/ui/render.js +3 -0
- package/dist/ui/slashCommands.js +6 -6
- package/package.json +1 -1
package/dist/ui/App.js
CHANGED
|
@@ -22,8 +22,10 @@ import { sanitizeForModel } from '../protocol/shell.js';
|
|
|
22
22
|
import { diff as gitDiff, gitBranchLine, prCreate } from '../protocol/git.js';
|
|
23
23
|
import { Activity, ApprovalPrompt, Banner, CodeModePicker, RatePicker, WORDMARK_MIN_COLUMNS, ChoicePrompt, Completions, HelpPanel, StatusBar, TranscriptLine, ShellPane, SHELL_PANE_LINES, } from './components.js';
|
|
24
24
|
import { TokenBuffer, terminalWidth } from './render.js';
|
|
25
|
+
import { initialPhase, phaseAfterBootstrap, phaseAfterModePicked, phaseAfterRatePicked, } from './phase.js';
|
|
25
26
|
import { color } from './theme.js';
|
|
26
27
|
import { completeInput, completionsFor } from './slashCommands.js';
|
|
28
|
+
import { setNoticeSink } from './notices.js';
|
|
27
29
|
function trimToPane(text) {
|
|
28
30
|
const lines = text.split('\n');
|
|
29
31
|
return lines.length > SHELL_PANE_LINES * 2
|
|
@@ -40,9 +42,10 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
40
42
|
const { exit } = useApp();
|
|
41
43
|
const [codeMode, setCodeMode] = useState(initialCodeMode);
|
|
42
44
|
const codeModeChosenRef = useRef(Boolean(initialCodeMode));
|
|
43
|
-
const [phase, setPhase] = useState(initialCodeMode
|
|
45
|
+
const [phase, setPhase] = useState(initialPhase(Boolean(initialCodeMode), Boolean(askForMode)));
|
|
44
46
|
const sessionReadyRef = useRef(false);
|
|
45
47
|
const modeChosenRef = useRef(!askForMode);
|
|
48
|
+
const invitedRef = useRef(false);
|
|
46
49
|
const [lines, setLines] = useState([]);
|
|
47
50
|
const [live, setLive] = useState('');
|
|
48
51
|
const [shellView, setShellView] = useState('');
|
|
@@ -74,6 +77,8 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
74
77
|
const mcpRef = useRef(null);
|
|
75
78
|
const snapshotRef = useRef(null);
|
|
76
79
|
const snapshotSentRef = useRef(false);
|
|
80
|
+
const minCreditsRef = useRef({ rivine_code: 10 });
|
|
81
|
+
const queuedPromptRef = useRef(null);
|
|
77
82
|
const answerRef = useRef(null);
|
|
78
83
|
const lineId = useRef(0);
|
|
79
84
|
const liveText = useRef('');
|
|
@@ -94,6 +99,12 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
94
99
|
const id = lineId.current;
|
|
95
100
|
setLines((prev) => [...prev, { id, kind, text, detail }]);
|
|
96
101
|
}, []);
|
|
102
|
+
const invite = useCallback(() => {
|
|
103
|
+
if (invitedRef.current)
|
|
104
|
+
return;
|
|
105
|
+
invitedRef.current = true;
|
|
106
|
+
push('system', 'Ask for anything. /help for commands.');
|
|
107
|
+
}, [push]);
|
|
97
108
|
const settleLive = useCallback(() => {
|
|
98
109
|
tokens.flush();
|
|
99
110
|
const text = liveText.current.trim();
|
|
@@ -119,9 +130,10 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
119
130
|
codeModeChosenRef.current = true;
|
|
120
131
|
runnerRef.current?.setCodeMode(chosen);
|
|
121
132
|
push('system', `Mode: ${CODE_MODE_LABELS[chosen]}`);
|
|
122
|
-
setPhase(
|
|
123
|
-
|
|
124
|
-
:
|
|
133
|
+
setPhase(phaseAfterModePicked({
|
|
134
|
+
sessionReady: sessionReadyRef.current,
|
|
135
|
+
rateChosen: modeChosenRef.current,
|
|
136
|
+
}));
|
|
125
137
|
}, [push]);
|
|
126
138
|
const cancelCodeModePick = useCallback(() => {
|
|
127
139
|
setPhase(sessionReadyRef.current ? 'ready' : 'starting');
|
|
@@ -130,18 +142,29 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
130
142
|
setMode(chosen);
|
|
131
143
|
modeChosenRef.current = true;
|
|
132
144
|
runnerRef.current?.setMode(chosen);
|
|
133
|
-
setPhase(
|
|
145
|
+
setPhase(phaseAfterRatePicked({
|
|
146
|
+
sessionReady: sessionReadyRef.current,
|
|
147
|
+
rateChosen: true,
|
|
148
|
+
}));
|
|
134
149
|
push('system', `Rate: ${describeMode(chosen)}`);
|
|
135
150
|
if (chosen === 'bypass') {
|
|
136
151
|
push('notice', 'Running without prompts. Files outside this project are still refused.');
|
|
137
152
|
}
|
|
138
|
-
|
|
139
|
-
}, [push]);
|
|
153
|
+
invite();
|
|
154
|
+
}, [push, invite]);
|
|
140
155
|
const cancelModePick = useCallback(() => {
|
|
141
156
|
setPhase(sessionReadyRef.current ? 'ready' : 'starting');
|
|
142
157
|
}, []);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
setNoticeSink((text) => push('notice', text));
|
|
160
|
+
return () => setNoticeSink(null);
|
|
161
|
+
}, [push]);
|
|
143
162
|
const quit = useCallback(() => {
|
|
144
|
-
void runnerRef.current
|
|
163
|
+
void runnerRef.current
|
|
164
|
+
?.shutdown()
|
|
165
|
+
.catch(() => {
|
|
166
|
+
})
|
|
167
|
+
.finally(() => exit());
|
|
145
168
|
}, [exit]);
|
|
146
169
|
const cycleMode = useCallback(() => {
|
|
147
170
|
setMode((current) => {
|
|
@@ -168,10 +191,10 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
168
191
|
setShowHelp(false);
|
|
169
192
|
setScreenGeneration((n) => n + 1);
|
|
170
193
|
return true;
|
|
171
|
-
case '/
|
|
194
|
+
case '/mode':
|
|
172
195
|
setPhase('codeMode');
|
|
173
196
|
return true;
|
|
174
|
-
case '/
|
|
197
|
+
case '/rate':
|
|
175
198
|
setPhase('mode');
|
|
176
199
|
return true;
|
|
177
200
|
case '/plan':
|
|
@@ -179,6 +202,10 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
179
202
|
codeModeChosenRef.current = true;
|
|
180
203
|
runnerRef.current?.setCodeMode('simple');
|
|
181
204
|
push('system', `Mode: ${CODE_MODE_LABELS.simple}`);
|
|
205
|
+
setPhase(phaseAfterModePicked({
|
|
206
|
+
sessionReady: sessionReadyRef.current,
|
|
207
|
+
rateChosen: modeChosenRef.current,
|
|
208
|
+
}));
|
|
182
209
|
return true;
|
|
183
210
|
case '/accept':
|
|
184
211
|
setMode('bypass');
|
|
@@ -249,7 +276,13 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
249
276
|
return;
|
|
250
277
|
if (runCommand(text))
|
|
251
278
|
return;
|
|
252
|
-
|
|
279
|
+
if (!codeMode) {
|
|
280
|
+
queuedPromptRef.current = text;
|
|
281
|
+
push('notice', 'Pick a Mode first — it decides what Rivine Code works on. Your message is saved.');
|
|
282
|
+
setPhase('codeMode');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const needed = creditShortfall(status.balance, minCreditsRef.current);
|
|
253
286
|
if (needed !== null) {
|
|
254
287
|
push('error', `Rivine Code needs $${needed.toFixed(2)} in credits to run.`);
|
|
255
288
|
push('notice', `Top up at ${siteUrl()}/credits`);
|
|
@@ -265,8 +298,12 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
265
298
|
try {
|
|
266
299
|
const result = await runnerRef.current.run(text, snapshot);
|
|
267
300
|
settleLive();
|
|
268
|
-
if (result.error)
|
|
301
|
+
if (result.error) {
|
|
269
302
|
push('error', result.error);
|
|
303
|
+
if (/credit|insufficient/i.test(result.error)) {
|
|
304
|
+
push('notice', `Top up at ${siteUrl()}/credits`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
270
307
|
else if (result.answer)
|
|
271
308
|
push('agent', result.answer);
|
|
272
309
|
}
|
|
@@ -288,7 +325,16 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
288
325
|
if (oneShot)
|
|
289
326
|
quit();
|
|
290
327
|
}
|
|
291
|
-
}, [oneShot, push, quit, refreshBalance, runCommand, settleLive, status.balance]);
|
|
328
|
+
}, [codeMode, oneShot, push, quit, refreshBalance, runCommand, settleLive, status.balance]);
|
|
329
|
+
useEffect(() => {
|
|
330
|
+
if (phase !== 'ready' || !codeMode)
|
|
331
|
+
return;
|
|
332
|
+
const queued = queuedPromptRef.current;
|
|
333
|
+
if (!queued)
|
|
334
|
+
return;
|
|
335
|
+
queuedPromptRef.current = null;
|
|
336
|
+
void submit(queued);
|
|
337
|
+
}, [phase, codeMode, submit]);
|
|
292
338
|
useEffect(() => {
|
|
293
339
|
let cancelled = false;
|
|
294
340
|
(async () => {
|
|
@@ -400,20 +446,29 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
400
446
|
setPhase('question');
|
|
401
447
|
}),
|
|
402
448
|
}, session.mcp);
|
|
403
|
-
void session.mcpReady
|
|
449
|
+
void session.mcpReady
|
|
450
|
+
.then((reports) => {
|
|
404
451
|
if (cancelled || reports.length === 0)
|
|
405
452
|
return;
|
|
406
453
|
const good = reports.filter((r) => r.ok);
|
|
407
454
|
const tools = session.mcp.listTools().length;
|
|
408
|
-
if (good.length)
|
|
455
|
+
if (good.length) {
|
|
409
456
|
push('system', `MCP: ${good.length} server(s), ${tools} tool(s) available.`);
|
|
457
|
+
}
|
|
410
458
|
for (const bad of reports.filter((r) => !r.ok)) {
|
|
411
459
|
push('notice', `MCP server "${bad.name}" did not start: ${bad.error}`);
|
|
412
460
|
}
|
|
413
461
|
runnerRef.current?.setMcpTools(session.mcp.listTools());
|
|
462
|
+
})
|
|
463
|
+
.catch((err) => {
|
|
464
|
+
push('notice', `MCP servers could not be listed: ${err.message}`);
|
|
414
465
|
});
|
|
466
|
+
minCreditsRef.current = session.bootstrap.engine_min_credits ?? minCreditsRef.current;
|
|
415
467
|
sessionReadyRef.current = true;
|
|
416
|
-
setPhase(
|
|
468
|
+
setPhase((current) => phaseAfterBootstrap(current, {
|
|
469
|
+
sessionReady: true,
|
|
470
|
+
rateChosen: modeChosenRef.current,
|
|
471
|
+
}));
|
|
417
472
|
runnerRef.current?.setMode(mode);
|
|
418
473
|
push('system', `${session.project}${session.bootstrap.created ? ' · new session' : ''}`);
|
|
419
474
|
if (session.continuing) {
|
|
@@ -424,7 +479,7 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
424
479
|
}
|
|
425
480
|
if (!oneShot && modeChosenRef.current && !askForMode) {
|
|
426
481
|
push('system', `Rate: ${describeMode(mode)}`);
|
|
427
|
-
|
|
482
|
+
invite();
|
|
428
483
|
}
|
|
429
484
|
if (oneShot)
|
|
430
485
|
void submit(oneShot);
|
|
@@ -525,9 +580,15 @@ export function App({ cwd, initialMode, askForMode, initialCodeMode, oneShot })
|
|
|
525
580
|
const compactBanner = terminalWidth() < WORDMARK_MIN_COLUMNS || Boolean(oneShot);
|
|
526
581
|
const matches = completionsFor(input);
|
|
527
582
|
const busy = phase !== 'ready';
|
|
528
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item) => item.kind === 'banner' ? (_jsx(Banner, { compact: compactBanner }, "banner")) : (_jsx(TranscriptLine, { line: item.line }, item.line.id)) }, screenGeneration), live ? (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: color('text'), children: live }) })) : null, shellView ? _jsx(ShellPane, { text: shellView, live: shellActive }) : null, phase === 'running' && stage ? _jsx(Activity, { stage: stage, tool: tool }) : null, phase === 'codeMode' ? (_jsx(CodeModePicker, { onPick: pickCodeMode, current: codeModeChosenRef.current ? codeMode : undefined, onCancel: codeModeChosenRef.current ? cancelCodeModePick : undefined })) : null, phase === 'mode' ? (_jsx(RatePicker, { onPick: pickMode, current: modeChosenRef.current ? mode : undefined, onCancel: modeChosenRef.current ? cancelModePick : undefined })) : null, phase === 'approval' && approval ? (_jsx(ApprovalPrompt, { approval: approval, allowAlways: isBatchable(approval.category), onChoice: answerApproval })) : null, phase === 'question' && question ? (question.options?.length ? (_jsx(ChoicePrompt, { prompt: question.prompt, options: question.options, onPick: answerQuestion })) : (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: color('accent'), bold: true, children: question.prompt }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: color('user'), children: '› ' }), _jsx(TextInput, { placeholder: "your answer", onSubmit: answerQuestion })] })] }))) : null, phase === 'hitl' && hitl ? (
|
|
583
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: staticItems, children: (item) => item.kind === 'banner' ? (_jsx(Banner, { compact: compactBanner }, "banner")) : (_jsx(TranscriptLine, { line: item.line }, item.line.id)) }, screenGeneration), live ? (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: color('text'), children: live }) })) : null, shellView ? _jsx(ShellPane, { text: shellView, live: shellActive }) : null, phase === 'running' && stage ? _jsx(Activity, { stage: stage, tool: tool }) : null, phase === 'codeMode' ? (_jsx(CodeModePicker, { onPick: pickCodeMode, current: codeModeChosenRef.current ? codeMode : undefined, onCancel: codeModeChosenRef.current ? cancelCodeModePick : undefined })) : null, phase === 'mode' ? (_jsx(RatePicker, { onPick: pickMode, current: modeChosenRef.current ? mode : undefined, onCancel: modeChosenRef.current ? cancelModePick : undefined })) : null, phase === 'approval' && approval ? (_jsx(ApprovalPrompt, { approval: approval, allowAlways: isBatchable(approval.category), onChoice: answerApproval })) : null, phase === 'question' && question ? (question.options?.length ? (_jsx(ChoicePrompt, { prompt: question.prompt, options: question.options, onPick: answerQuestion })) : (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: color('accent'), bold: true, children: question.prompt }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: color('user'), children: '› ' }), _jsx(TextInput, { placeholder: "your answer", onSubmit: answerQuestion })] })] }))) : null, phase === 'hitl' && hitl ? (hitl.kind === 'code_mode' ? (_jsx(CodeModePicker, { onPick: (chosen) => {
|
|
584
|
+
setCodeMode(chosen);
|
|
585
|
+
codeModeChosenRef.current = true;
|
|
586
|
+
runnerRef.current?.setCodeMode(chosen);
|
|
587
|
+
push('system', `Mode: ${CODE_MODE_LABELS[chosen]}`);
|
|
588
|
+
answerHitl({ approved: true, feedback: CODE_MODE_LABELS[chosen], codeMode: chosen });
|
|
589
|
+
}, current: codeMode })) : (_jsx(ChoicePrompt, { prompt: hitl.tool ? `${hitl.tool} — ${hitl.message}` : hitl.message, options: hitl.options?.length ? hitl.options : ['Approve', 'Decline'], onPick: (value) => answerHitl(hitl.options?.length
|
|
529
590
|
? { approved: true, feedback: value }
|
|
530
|
-
: { approved: value === 'Approve' }) })) : null, showHelp && phase === 'ready' ? _jsx(HelpPanel, {}) : null, phase === 'ready' && !oneShot ? (_jsxs(_Fragment, { children: [_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: color('user'), children: '› ' }), _jsx(TextInput, { defaultValue: inputSeed, placeholder: "What should Rivine build?", onChange: setInput, onSubmit: (value) => void submit(value) }, inputNonce)] }), _jsx(Completions, { matches: matches })] })) : null, phase === 'starting' ? _jsx(Activity, { stage: "Starting\u2026", tool: "" }) : null, _jsx(StatusBar, { info: {
|
|
591
|
+
: { approved: value === 'Approve' }) }))) : null, showHelp && phase === 'ready' ? _jsx(HelpPanel, {}) : null, phase === 'ready' && !oneShot ? (_jsxs(_Fragment, { children: [_jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: color('user'), children: '› ' }), _jsx(TextInput, { defaultValue: inputSeed, placeholder: "What should Rivine build?", onChange: setInput, onSubmit: (value) => void submit(value) }, inputNonce)] }), _jsx(Completions, { matches: matches })] })) : null, phase === 'starting' ? _jsx(Activity, { stage: "Starting\u2026", tool: "" }) : null, _jsx(StatusBar, { info: {
|
|
531
592
|
mode,
|
|
532
593
|
codeMode,
|
|
533
594
|
project: status.project,
|
package/dist/ui/components.js
CHANGED
|
@@ -62,7 +62,7 @@ export function CodeModePicker({ onPick, current, onCancel, }) {
|
|
|
62
62
|
if (found)
|
|
63
63
|
onPick(found.mode);
|
|
64
64
|
});
|
|
65
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: color('text'), bold: true, children: "Mode \u2014
|
|
65
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: color('text'), bold: true, children: "Mode \u2014 what should Rivine Code work on?" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: options.map((o) => {
|
|
66
66
|
const active = o.mode === current;
|
|
67
67
|
return (_jsxs(Box, { children: [_jsx(Text, { color: active ? color('success') : color('faint'), children: active ? ' \u203a' : ' ' }), _jsx(Text, { color: color('accent'), children: o.key }), _jsx(Text, { color: color('faint'), children: " \u00B7 " }), _jsx(Text, { color: active ? color('text') : color('muted'), bold: active, children: o.label }), _jsx(Text, { color: color('faint'), children: " \u2014 " }), _jsx(Text, { color: color('faint'), children: fit(o.detail, Math.max(10, width - 34)) })] }, o.key));
|
|
68
68
|
}) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: color('faint'), children: current ? 'esc keeps the current mode' : 'pick a number to begin' }) })] }));
|
|
@@ -93,7 +93,6 @@ export function RatePicker({ onPick, current, onCancel, }) {
|
|
|
93
93
|
? `press a number · enter keeps ${options.find((o) => o.mode === current)?.label ?? 'this'} · esc cancels`
|
|
94
94
|
: 'press a number · enter for Manual · shift+tab toggles later', width - 4) }) })] }));
|
|
95
95
|
}
|
|
96
|
-
export const ModePicker = RatePicker;
|
|
97
96
|
export const SHELL_PANE_LINES = 12;
|
|
98
97
|
export function ShellPane({ text, live }) {
|
|
99
98
|
if (!text)
|
|
@@ -137,7 +136,7 @@ export function Completions({ matches }) {
|
|
|
137
136
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [matches.slice(0, 6).map((c) => (_jsxs(Box, { children: [_jsx(Text, { color: color('accent'), children: c.name.padEnd(10) }), _jsx(Text, { color: color('faint'), children: fit(c.summary, width - 14) })] }, c.name))), matches.length === 1 ? (_jsx(Text, { color: color('faint'), children: " tab to complete" })) : null] }));
|
|
138
137
|
}
|
|
139
138
|
export function HelpPanel() {
|
|
140
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: color('text'), bold: true, children: "Commands" }), SLASH_COMMANDS.map((c) => (_jsxs(Box, { children: [_jsx(Text, { color: color('accent'), children: ` ${c.name}`.padEnd(12) }), _jsx(Text, { color: color('muted'), children: c.summary })] }, c.name))), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: color('faint'), children: " shift+tab toggle Rate (Manual \u21C4 Bypass)" }), _jsx(Text, { color: color('faint'), children: " esc stop the current turn" }), _jsx(Text, { color: color('faint'), children: " ctrl+c quit" })] })] }));
|
|
139
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Text, { color: color('text'), bold: true, children: "Commands" }), SLASH_COMMANDS.map((c) => (_jsxs(Box, { children: [_jsx(Text, { color: color('accent'), children: ` ${c.name}`.padEnd(12) }), _jsx(Text, { color: color('muted'), children: c.summary })] }, c.name))), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: color('faint'), children: " Mode what Rivine Code works on \u2014 /mode" }), _jsx(Text, { color: color('faint'), children: " Rate how edits apply \u2014 /rate, or shift+tab" })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: color('faint'), children: " shift+tab toggle Rate (Manual \u21C4 Bypass)" }), _jsx(Text, { color: color('faint'), children: " esc stop the current turn" }), _jsx(Text, { color: color('faint'), children: " ctrl+c quit" })] })] }));
|
|
141
140
|
}
|
|
142
141
|
export function StatusBar({ info }) {
|
|
143
142
|
const width = terminalColumns();
|
package/dist/ui/headless.js
CHANGED
|
@@ -73,7 +73,7 @@ export class HeadlessRenderer {
|
|
|
73
73
|
refuseApproval(approval) {
|
|
74
74
|
const fix = this.mode === 'plan'
|
|
75
75
|
? 'plan mode makes no changes; re-run without --plan'
|
|
76
|
-
: 'no terminal to ask on — re-run with
|
|
76
|
+
: 'no terminal to ask on — re-run with --rate bypass';
|
|
77
77
|
this.refused += 1;
|
|
78
78
|
this.line('notice', `Skipped: ${approval.summary}`);
|
|
79
79
|
this.line('notice', ` ${approval.reason} (${fix})`);
|
|
@@ -85,7 +85,7 @@ export class HeadlessRenderer {
|
|
|
85
85
|
return '';
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
export function installTeardown(onExit) {
|
|
88
|
+
export function installTeardown(onExit, options = {}) {
|
|
89
89
|
let finished = false;
|
|
90
90
|
const restore = () => {
|
|
91
91
|
try {
|
|
@@ -116,15 +116,18 @@ export function installTeardown(onExit) {
|
|
|
116
116
|
process.stderr.write(`\n ${err?.message ?? String(err)}\n\n`);
|
|
117
117
|
void finish(1);
|
|
118
118
|
};
|
|
119
|
+
const onRejection = options.onRejection
|
|
120
|
+
? (err) => options.onRejection(err)
|
|
121
|
+
: onFatal;
|
|
119
122
|
process.on('SIGINT', onSigint);
|
|
120
123
|
process.on('SIGTERM', onSigterm);
|
|
121
124
|
process.on('uncaughtException', onFatal);
|
|
122
|
-
process.on('unhandledRejection',
|
|
125
|
+
process.on('unhandledRejection', onRejection);
|
|
123
126
|
return () => {
|
|
124
127
|
process.off('SIGINT', onSigint);
|
|
125
128
|
process.off('SIGTERM', onSigterm);
|
|
126
129
|
process.off('uncaughtException', onFatal);
|
|
127
|
-
process.off('unhandledRejection',
|
|
130
|
+
process.off('unhandledRejection', onRejection);
|
|
128
131
|
restore();
|
|
129
132
|
};
|
|
130
133
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Rivine Code CLI
|
|
3
|
+
* Licensed and owned by Beunec Technologies, Inc.
|
|
4
|
+
*
|
|
5
|
+
* https://aselius.beunec.com
|
|
6
|
+
*
|
|
7
|
+
* This software is proprietary. Use is governed by the licence agreement
|
|
8
|
+
* accompanying it; it is not open source and is not licensed for
|
|
9
|
+
* redistribution or derivative works.
|
|
10
|
+
*/
|
|
11
|
+
let sink = null;
|
|
12
|
+
export function setNoticeSink(next) {
|
|
13
|
+
sink = next;
|
|
14
|
+
}
|
|
15
|
+
export function emitNotice(text) {
|
|
16
|
+
if (!sink || !text)
|
|
17
|
+
return false;
|
|
18
|
+
sink(text);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
package/dist/ui/phase.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Rivine Code CLI
|
|
3
|
+
* Licensed and owned by Beunec Technologies, Inc.
|
|
4
|
+
*
|
|
5
|
+
* https://aselius.beunec.com
|
|
6
|
+
*
|
|
7
|
+
* This software is proprietary. Use is governed by the licence agreement
|
|
8
|
+
* accompanying it; it is not open source and is not licensed for
|
|
9
|
+
* redistribution or derivative works.
|
|
10
|
+
*/
|
|
11
|
+
export function phaseAfterBootstrap(current, s) {
|
|
12
|
+
if (current === 'codeMode' || current === 'mode')
|
|
13
|
+
return current;
|
|
14
|
+
return s.rateChosen ? 'ready' : 'mode';
|
|
15
|
+
}
|
|
16
|
+
export function phaseAfterModePicked(s) {
|
|
17
|
+
if (!s.rateChosen)
|
|
18
|
+
return 'mode';
|
|
19
|
+
return s.sessionReady ? 'ready' : 'starting';
|
|
20
|
+
}
|
|
21
|
+
export function phaseAfterRatePicked(s) {
|
|
22
|
+
return s.sessionReady ? 'ready' : 'starting';
|
|
23
|
+
}
|
|
24
|
+
export function initialPhase(hasMode, askRate) {
|
|
25
|
+
if (!hasMode)
|
|
26
|
+
return 'codeMode';
|
|
27
|
+
return askRate ? 'mode' : 'starting';
|
|
28
|
+
}
|
package/dist/ui/render.js
CHANGED
package/dist/ui/slashCommands.js
CHANGED
|
@@ -11,19 +11,19 @@
|
|
|
11
11
|
export const SLASH_COMMANDS = [
|
|
12
12
|
{ name: '/help', summary: 'show these commands' },
|
|
13
13
|
{
|
|
14
|
-
name: '/
|
|
15
|
-
summary: 'pick
|
|
16
|
-
detail: 'Frontend, Backend, Full Stack, Plan/Ask, or Security.',
|
|
14
|
+
name: '/mode',
|
|
15
|
+
summary: 'pick the Mode — what Rivine Code works on',
|
|
16
|
+
detail: 'Frontend Only, Backend Only, Full Stack, Plan/Ask Only, or Security Scan/Fix.',
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
name: '/
|
|
19
|
+
name: '/rate',
|
|
20
20
|
summary: 'pick the Rate — Manual or Bypass',
|
|
21
|
-
detail: '
|
|
21
|
+
detail: 'Manual asks before each edit; Bypass applies them. Shift+Tab toggles.',
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
name: '/plan',
|
|
25
25
|
summary: 'switch to Plan/Ask Only Mode',
|
|
26
|
-
detail: 'Same as picking Plan/Ask in /
|
|
26
|
+
detail: 'Same as picking Plan/Ask Only in /mode.',
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
name: '/accept',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beunec/rivine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Rivine Code in your terminal — Beunec's software engineering runtime constructs, working on the files you already have open.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Beunec Technologies",
|