@alisio/alisio-code 0.1.0-alpha.21 → 0.1.0-alpha.23
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 +1 -1
- package/dist/main.js +17 -1
- package/dist/tui/app.js +59 -4
- package/dist/tui/btw.d.ts +61 -0
- package/dist/tui/btw.js +84 -0
- package/dist/tui/components.d.ts +19 -0
- package/dist/tui/components.js +87 -0
- package/dist/tui/settings.d.ts +1 -1
- package/dist/tui/settings.js +2 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ Other global flags: `--cwd`, `--config`, `--model`, `--base-url`, `--api-mode`,
|
|
|
80
80
|
## In the TUI
|
|
81
81
|
|
|
82
82
|
`/connect` adds a provider; `/model` picks a saved provider/model; `/compact`, `/stats`, `/clear`,
|
|
83
|
-
`/sessions`, `/resume` manage conversations; `/plugins`, `/skills`, `/
|
|
83
|
+
`/sessions`, `/resume` manage conversations; `/plugins`, `/skills`, `/mcps`, `/settings` manage
|
|
84
84
|
extensions and preferences; `/memory` and `/agents` drive the built-in plugins; `/init` writes
|
|
85
85
|
`AGENTS.md`.
|
|
86
86
|
|
package/dist/main.js
CHANGED
|
@@ -200,7 +200,7 @@ async function run(cmd, prompt, sessionId) {
|
|
|
200
200
|
process.stderr.write(`[startup] ${JSON.stringify(d)}\n`);
|
|
201
201
|
}
|
|
202
202
|
if (!opts.quiet)
|
|
203
|
-
console.log("Alisio · /exit /new /skill:name /command plugin.id:name args");
|
|
203
|
+
console.log("Alisio · /exit /new /btw question /skill:name /command plugin.id:name args");
|
|
204
204
|
process.stdout.write("\nalisio › ");
|
|
205
205
|
try {
|
|
206
206
|
for await (const rawLine of rl) {
|
|
@@ -217,6 +217,22 @@ async function run(cmd, prompt, sessionId) {
|
|
|
217
217
|
process.stdout.write("\nalisio › ");
|
|
218
218
|
continue;
|
|
219
219
|
}
|
|
220
|
+
if (line === "/btw" || line.startsWith("/btw ")) {
|
|
221
|
+
// Side question: printed here only, never added to the conversation.
|
|
222
|
+
try {
|
|
223
|
+
const { CommandCatalog } = await import("@alisio/core");
|
|
224
|
+
const result = await new CommandCatalog(app).execute("btw", line.slice(4), {
|
|
225
|
+
sessionId: session,
|
|
226
|
+
signal: controller.signal,
|
|
227
|
+
});
|
|
228
|
+
console.log(result.text ?? "");
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
232
|
+
}
|
|
233
|
+
process.stdout.write("\nalisio › ");
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
220
236
|
if (line.startsWith("/command ")) {
|
|
221
237
|
const [name, ...args] = line.slice(9).split(" ");
|
|
222
238
|
const handler = app.plugins.commands.get(name ?? "");
|
package/dist/tui/app.js
CHANGED
|
@@ -3,8 +3,9 @@ import { CombinedAutocompleteProvider, Container, Editor, getImageDimensions, ge
|
|
|
3
3
|
import { loadVersion } from "../version.js";
|
|
4
4
|
import { activeAgentCatalog, agentPickerItems, agentRunOptions, mainAgentFromRecord, resolveActiveAgent, } from "./agents.js";
|
|
5
5
|
import { MAX_ATTACHMENTS_PER_MESSAGE, MAX_IMAGE_BYTES, pasteImageFromClipboard, removeLastAttachment, toApiAttachment, } from "./attachments.js";
|
|
6
|
+
import { BTW_DESCRIPTION, BTW_USAGE, btwBrowser, btwPending } from "./btw.js";
|
|
6
7
|
import { copyText, nodeSpawn } from "./clipboard.js";
|
|
7
|
-
import { AttachmentsBar, BannerBlock, ContentInset, clock, contentInnerWidth, Footer, Header, QuestionPanel, Switch, TranscriptSync, TreePanel, } from "./components.js";
|
|
8
|
+
import { AttachmentsBar, BannerBlock, BtwPanel, ContentInset, clock, contentInnerWidth, Footer, Header, QuestionPanel, Switch, TranscriptSync, TreePanel, } from "./components.js";
|
|
8
9
|
import { ConnectInputPrompt } from "./connect-input.js";
|
|
9
10
|
import { bounded, EXIT_PENDING_CAP_MS, EXIT_SESSION_END_CAP_MS } from "./exit.js";
|
|
10
11
|
import { BRANCH_REFRESH_MS, createBranchCache } from "./git-branch.js";
|
|
@@ -1295,7 +1296,7 @@ export async function runTui(options) {
|
|
|
1295
1296
|
const consent = value === true ? app.rememberGlobalMcpConsent() : app.revokeGlobalMcpConsent();
|
|
1296
1297
|
void consent
|
|
1297
1298
|
.then(() => notice(value === true
|
|
1298
|
-
? "MCP consent remembered globally (mcp.allow); enabled servers will auto-connect on every start. Use /
|
|
1299
|
+
? "MCP consent remembered globally (mcp.allow); enabled servers will auto-connect on every start. Use /mcps to connect them now."
|
|
1299
1300
|
: "Global MCP consent revoked: runtime permission dropped and configured servers disconnected."))
|
|
1300
1301
|
.catch(error)
|
|
1301
1302
|
.finally(settle);
|
|
@@ -1440,13 +1441,64 @@ export async function runTui(options) {
|
|
|
1440
1441
|
"**Keys**: Enter send · Shift+Enter / Alt+Enter / Ctrl+J newline · Tab complete · ↑↓ history · Esc interrupt · Ctrl+C clear input (twice to exit) · Ctrl+D exit on empty input · c / y copy last response (empty input) · x expand/collapse latest thought/tool batch/output (empty input) · click a collapsible header row to toggle it · PgUp/PgDn or mouse wheel scroll · Ctrl+X agent panel · Ctrl+B background running agents",
|
|
1441
1442
|
`**Paste**: multi-line text pastes as one block automatically · Ctrl+V attach a clipboard image (PNG/JPEG/GIF/WebP, up to ${(MAX_IMAGE_BYTES / (1024 * 1024)).toFixed(0)} MB, up to ${MAX_ATTACHMENTS_PER_MESSAGE} per message) · Ctrl+R remove the last attached image`,
|
|
1442
1443
|
].join("\n");
|
|
1444
|
+
// `/btw` side questions: answered in the picker slot, never appended to the transcript. One
|
|
1445
|
+
// question is in flight at a time; Esc cancels it (independent of the run's controller, so a
|
|
1446
|
+
// side question also works, and is cancelled on its own, while a turn is running).
|
|
1447
|
+
let btwController;
|
|
1448
|
+
let btwPanel;
|
|
1449
|
+
const btwHeight = () => Math.max(4, Math.floor((process.stdout.rows ?? 30) / 2) - 6);
|
|
1450
|
+
const closeBtw = () => {
|
|
1451
|
+
btwController?.abort(new Error("Cancelled"));
|
|
1452
|
+
btwController = undefined;
|
|
1453
|
+
if (btwPanel && picker === btwPanel)
|
|
1454
|
+
closePicker();
|
|
1455
|
+
btwPanel = undefined;
|
|
1456
|
+
};
|
|
1457
|
+
const sideQuestion = async (question) => {
|
|
1458
|
+
const asked = session;
|
|
1459
|
+
const entries = app.sideQuestions.history(asked);
|
|
1460
|
+
if (!question) {
|
|
1461
|
+
const state = btwBrowser(entries);
|
|
1462
|
+
if (!state)
|
|
1463
|
+
return notice(`${BTW_USAGE}\n${BTW_DESCRIPTION}`);
|
|
1464
|
+
btwPanel = new BtwPanel(state, closeBtw, btwHeight);
|
|
1465
|
+
return showPicker(btwPanel);
|
|
1466
|
+
}
|
|
1467
|
+
btwController?.abort(new Error("Cancelled"));
|
|
1468
|
+
const controller = new AbortController();
|
|
1469
|
+
btwController = controller;
|
|
1470
|
+
const panel = new BtwPanel(btwPending(entries, question), closeBtw, btwHeight);
|
|
1471
|
+
btwPanel = panel;
|
|
1472
|
+
showPicker(panel);
|
|
1473
|
+
try {
|
|
1474
|
+
await app.sideQuestions.ask(asked, question, { signal: controller.signal });
|
|
1475
|
+
if (controller.signal.aborted)
|
|
1476
|
+
return;
|
|
1477
|
+
panel.dispatch({ type: "answered", entries: app.sideQuestions.history(asked) });
|
|
1478
|
+
// An approval may have taken the slot meanwhile: the answer is in the history anyway.
|
|
1479
|
+
if (picker !== panel)
|
|
1480
|
+
flashHint("Side answer ready: /btw to view it", 4000);
|
|
1481
|
+
}
|
|
1482
|
+
catch (e) {
|
|
1483
|
+
if (controller.signal.aborted)
|
|
1484
|
+
return;
|
|
1485
|
+
panel.dispatch({ type: "failed", message: e instanceof Error ? e.message : String(e) });
|
|
1486
|
+
if (picker !== panel)
|
|
1487
|
+
error(e);
|
|
1488
|
+
}
|
|
1489
|
+
finally {
|
|
1490
|
+
if (btwController === controller)
|
|
1491
|
+
btwController = undefined;
|
|
1492
|
+
tui.requestRender();
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1443
1495
|
const commandCatalog = new CommandCatalog(app);
|
|
1444
1496
|
const mutating = new Set([
|
|
1445
1497
|
"connect",
|
|
1446
1498
|
"model",
|
|
1447
1499
|
"plugins",
|
|
1448
1500
|
"skills",
|
|
1449
|
-
"
|
|
1501
|
+
"mcps",
|
|
1450
1502
|
"settings",
|
|
1451
1503
|
"compact",
|
|
1452
1504
|
"clear",
|
|
@@ -1506,7 +1558,7 @@ export async function runTui(options) {
|
|
|
1506
1558
|
return managePlugins();
|
|
1507
1559
|
case "skills":
|
|
1508
1560
|
return manageSkills();
|
|
1509
|
-
case "
|
|
1561
|
+
case "mcps":
|
|
1510
1562
|
return manageMcp();
|
|
1511
1563
|
case "settings":
|
|
1512
1564
|
return openSettings();
|
|
@@ -1521,6 +1573,8 @@ export async function runTui(options) {
|
|
|
1521
1573
|
tui.flash(message);
|
|
1522
1574
|
return result.ok ? undefined : notice(message);
|
|
1523
1575
|
}
|
|
1576
|
+
case "btw":
|
|
1577
|
+
return await sideQuestion(parsed.args);
|
|
1524
1578
|
case "ask": {
|
|
1525
1579
|
if (!parsed.args)
|
|
1526
1580
|
return notice("Usage: /ask <question>");
|
|
@@ -1834,6 +1888,7 @@ export async function runTui(options) {
|
|
|
1834
1888
|
const ticker = setInterval(() => {
|
|
1835
1889
|
clock.now = Date.now();
|
|
1836
1890
|
if (busy ||
|
|
1891
|
+
btwPanel?.pending ||
|
|
1837
1892
|
view.items.some((i) => i.kind === "tool" && i.status === "running") ||
|
|
1838
1893
|
panelNodes().some((n) => n.status === "running")) {
|
|
1839
1894
|
clock.frame++;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure state of the TUI `/btw` panel: a pending side question, its answer, and browsing earlier
|
|
3
|
+
* answers of the session. The panel lives in the picker slot and never touches the transcript.
|
|
4
|
+
*
|
|
5
|
+
* Keys: ←/→ earlier/later answer, ↑/↓ (and PgUp/PgDn) scroll a long answer, Esc cancels a pending
|
|
6
|
+
* question or closes the panel.
|
|
7
|
+
*/
|
|
8
|
+
import type { SideQuestionEntry } from "@alisio/sdk";
|
|
9
|
+
export declare const BTW_USAGE = "Usage: /btw <question>";
|
|
10
|
+
export declare const BTW_DESCRIPTION = "Ask a side question about the current session without adding to the conversation.";
|
|
11
|
+
export interface BtwState {
|
|
12
|
+
entries: SideQuestionEntry[];
|
|
13
|
+
/** Index of the entry on screen (ignored while `pending`). */
|
|
14
|
+
index: number;
|
|
15
|
+
/** A question waiting for its answer. */
|
|
16
|
+
pending?: {
|
|
17
|
+
question: string;
|
|
18
|
+
startedAt: number;
|
|
19
|
+
};
|
|
20
|
+
/** Why the last question failed (shown until the user navigates). */
|
|
21
|
+
error?: {
|
|
22
|
+
question: string;
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
/** First answer line on screen. */
|
|
26
|
+
scroll: number;
|
|
27
|
+
}
|
|
28
|
+
export type BtwAction = {
|
|
29
|
+
type: "prev";
|
|
30
|
+
} | {
|
|
31
|
+
type: "next";
|
|
32
|
+
} | {
|
|
33
|
+
type: "scroll";
|
|
34
|
+
lines: number;
|
|
35
|
+
} | {
|
|
36
|
+
type: "answered";
|
|
37
|
+
entries: SideQuestionEntry[];
|
|
38
|
+
} | {
|
|
39
|
+
type: "failed";
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
/** The browser for `/btw` without a question: the newest entry, or undefined when none. */
|
|
43
|
+
export declare function btwBrowser(entries: SideQuestionEntry[]): BtwState | undefined;
|
|
44
|
+
/** The panel while `question` is being answered (earlier entries stay browsable afterwards). */
|
|
45
|
+
export declare function btwPending(entries: SideQuestionEntry[], question: string, now?: number): BtwState;
|
|
46
|
+
export declare function reduceBtw(state: BtwState, action: BtwAction): BtwState;
|
|
47
|
+
/** The entry on screen, if the panel is not pending or showing an error. */
|
|
48
|
+
export declare function btwCurrent(state: BtwState): SideQuestionEntry | undefined;
|
|
49
|
+
/** "2/5" for the entry on screen; empty while pending or on an error. */
|
|
50
|
+
export declare function btwPosition(state: BtwState): string;
|
|
51
|
+
/** Footer hints for the current state. */
|
|
52
|
+
export declare function btwHints(state: BtwState, overflow: boolean): string;
|
|
53
|
+
/** "model · 12 in · 3 out tokens" for an entry. */
|
|
54
|
+
export declare function btwUsageLine(entry: SideQuestionEntry): string;
|
|
55
|
+
/** The visible slice of `lines` for `scroll` (clamped) and whether more lines exist. */
|
|
56
|
+
export declare function btwWindow(lines: string[], scroll: number, height: number): {
|
|
57
|
+
lines: string[];
|
|
58
|
+
scroll: number;
|
|
59
|
+
above: boolean;
|
|
60
|
+
below: boolean;
|
|
61
|
+
};
|
package/dist/tui/btw.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const BTW_USAGE = "Usage: /btw <question>";
|
|
2
|
+
export const BTW_DESCRIPTION = "Ask a side question about the current session without adding to the conversation.";
|
|
3
|
+
/** The browser for `/btw` without a question: the newest entry, or undefined when none. */
|
|
4
|
+
export function btwBrowser(entries) {
|
|
5
|
+
return entries.length ? { entries, index: entries.length - 1, scroll: 0 } : undefined;
|
|
6
|
+
}
|
|
7
|
+
/** The panel while `question` is being answered (earlier entries stay browsable afterwards). */
|
|
8
|
+
export function btwPending(entries, question, now = Date.now()) {
|
|
9
|
+
return {
|
|
10
|
+
entries,
|
|
11
|
+
index: Math.max(0, entries.length - 1),
|
|
12
|
+
pending: { question, startedAt: now },
|
|
13
|
+
scroll: 0,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function reduceBtw(state, action) {
|
|
17
|
+
switch (action.type) {
|
|
18
|
+
case "prev":
|
|
19
|
+
case "next": {
|
|
20
|
+
if (state.pending || !state.entries.length)
|
|
21
|
+
return state;
|
|
22
|
+
const step = action.type === "prev" ? -1 : 1;
|
|
23
|
+
// Leaving an error shows the stored entries again (the newest first).
|
|
24
|
+
const from = state.error ? state.entries.length : state.index;
|
|
25
|
+
const index = Math.min(state.entries.length - 1, Math.max(0, from + step));
|
|
26
|
+
if (index === state.index && !state.error)
|
|
27
|
+
return state;
|
|
28
|
+
return { entries: state.entries, index, scroll: 0 };
|
|
29
|
+
}
|
|
30
|
+
case "scroll":
|
|
31
|
+
return { ...state, scroll: Math.max(0, state.scroll + action.lines) };
|
|
32
|
+
case "answered":
|
|
33
|
+
return {
|
|
34
|
+
entries: action.entries,
|
|
35
|
+
index: Math.max(0, action.entries.length - 1),
|
|
36
|
+
scroll: 0,
|
|
37
|
+
};
|
|
38
|
+
case "failed": {
|
|
39
|
+
const { pending: _pending, ...rest } = state;
|
|
40
|
+
return {
|
|
41
|
+
...rest,
|
|
42
|
+
error: { question: state.pending?.question ?? "", message: action.message },
|
|
43
|
+
scroll: 0,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** The entry on screen, if the panel is not pending or showing an error. */
|
|
49
|
+
export function btwCurrent(state) {
|
|
50
|
+
return state.pending || state.error ? undefined : state.entries[state.index];
|
|
51
|
+
}
|
|
52
|
+
/** "2/5" for the entry on screen; empty while pending or on an error. */
|
|
53
|
+
export function btwPosition(state) {
|
|
54
|
+
return btwCurrent(state) ? `${state.index + 1}/${state.entries.length}` : "";
|
|
55
|
+
}
|
|
56
|
+
/** Footer hints for the current state. */
|
|
57
|
+
export function btwHints(state, overflow) {
|
|
58
|
+
if (state.pending)
|
|
59
|
+
return "Esc cancel";
|
|
60
|
+
const current = btwCurrent(state);
|
|
61
|
+
const hints = [
|
|
62
|
+
...(state.entries.length > 1 || (state.error && state.entries.length)
|
|
63
|
+
? ["←/→ earlier/later"]
|
|
64
|
+
: []),
|
|
65
|
+
...(overflow ? ["↑/↓ scroll"] : []),
|
|
66
|
+
"Esc close",
|
|
67
|
+
];
|
|
68
|
+
return current || state.error ? hints.join(" · ") : "Esc close";
|
|
69
|
+
}
|
|
70
|
+
/** "model · 12 in · 3 out tokens" for an entry. */
|
|
71
|
+
export function btwUsageLine(entry) {
|
|
72
|
+
return `${entry.model} · ${entry.usage.input} in · ${entry.usage.output} out tokens${entry.truncated ? " · truncated" : ""}`;
|
|
73
|
+
}
|
|
74
|
+
/** The visible slice of `lines` for `scroll` (clamped) and whether more lines exist. */
|
|
75
|
+
export function btwWindow(lines, scroll, height) {
|
|
76
|
+
const max = Math.max(0, lines.length - height);
|
|
77
|
+
const start = Math.min(Math.max(0, scroll), max);
|
|
78
|
+
return {
|
|
79
|
+
lines: lines.slice(start, start + height),
|
|
80
|
+
scroll: start,
|
|
81
|
+
above: start > 0,
|
|
82
|
+
below: start + height < lines.length,
|
|
83
|
+
};
|
|
84
|
+
}
|
package/dist/tui/components.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PanelNode, TreeNode, UiBlock } from "@alisio/sdk";
|
|
2
2
|
import { type Component, Container } from "@earendil-works/pi-tui";
|
|
3
3
|
import { type PendingAttachment } from "./attachments.ts";
|
|
4
|
+
import { type BtwAction, type BtwState } from "./btw.ts";
|
|
4
5
|
import { type QuestionPanelState, type QuestionSpec } from "./questions.ts";
|
|
5
6
|
import { type ContextBudget, type FoldableKind, type FoldCandidate, type GroupedTool, type IdentityInput, type TranscriptEntry, type TranscriptItem, type ViewState } from "./state.ts";
|
|
6
7
|
export declare const SPINNER: string[];
|
|
@@ -255,3 +256,21 @@ export declare class QuestionPanel implements Component {
|
|
|
255
256
|
handleInput(data: string): void;
|
|
256
257
|
render(width: number): string[];
|
|
257
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* The `/btw` side-question panel (picker slot, never the transcript): a pending question with a
|
|
261
|
+
* spinner, then the answer rendered as Markdown like assistant text, scrollable, with ←/→ through
|
|
262
|
+
* the session's earlier side answers. `onClose` runs on Esc (the app cancels a pending question).
|
|
263
|
+
*/
|
|
264
|
+
export declare class BtwPanel implements Component {
|
|
265
|
+
private state;
|
|
266
|
+
private readonly onClose;
|
|
267
|
+
private readonly height;
|
|
268
|
+
private markdown?;
|
|
269
|
+
constructor(state: BtwState, onClose: () => void, height: () => number);
|
|
270
|
+
get pending(): boolean;
|
|
271
|
+
dispatch(action: BtwAction): void;
|
|
272
|
+
invalidate(): void;
|
|
273
|
+
handleInput(data: string): void;
|
|
274
|
+
private answerLines;
|
|
275
|
+
render(width: number): string[];
|
|
276
|
+
}
|
package/dist/tui/components.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Container, getCapabilities, getImageDimensions, Image, Key, Markdown, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { terminalCapabilities } from "../banner.js";
|
|
3
3
|
import { attachmentCaption, MAX_ATTACHMENTS_PER_MESSAGE, } from "./attachments.js";
|
|
4
|
+
import { btwCurrent, btwHints, btwPosition, btwUsageLine, btwWindow, reduceBtw, } from "./btw.js";
|
|
4
5
|
import { branchDisplay } from "./git-branch.js";
|
|
5
6
|
import { initialQuestionState, reduceQuestions, } from "./questions.js";
|
|
6
7
|
import { contextLevel, contextPercent, editSummary, entryKeyOf, fitIdentityParts, fitSegments, foldCandidateOf, formatContext, formatDuration, formatTokens, groupIdentity, groupToolEntries, humanizeToolName, isUiBlock, previewLinesFor, previewRows, REASONING_EXPAND_MAX_LINES, reasoningDurationMs, } from "./state.js";
|
|
@@ -1113,3 +1114,89 @@ export class QuestionPanel {
|
|
|
1113
1114
|
return fit(lines, width);
|
|
1114
1115
|
}
|
|
1115
1116
|
}
|
|
1117
|
+
/**
|
|
1118
|
+
* The `/btw` side-question panel (picker slot, never the transcript): a pending question with a
|
|
1119
|
+
* spinner, then the answer rendered as Markdown like assistant text, scrollable, with ←/→ through
|
|
1120
|
+
* the session's earlier side answers. `onClose` runs on Esc (the app cancels a pending question).
|
|
1121
|
+
*/
|
|
1122
|
+
export class BtwPanel {
|
|
1123
|
+
state;
|
|
1124
|
+
onClose;
|
|
1125
|
+
height;
|
|
1126
|
+
markdown;
|
|
1127
|
+
constructor(state, onClose, height) {
|
|
1128
|
+
this.state = state;
|
|
1129
|
+
this.onClose = onClose;
|
|
1130
|
+
this.height = height;
|
|
1131
|
+
}
|
|
1132
|
+
get pending() {
|
|
1133
|
+
return !!this.state.pending;
|
|
1134
|
+
}
|
|
1135
|
+
dispatch(action) {
|
|
1136
|
+
this.state = reduceBtw(this.state, action);
|
|
1137
|
+
}
|
|
1138
|
+
invalidate() {
|
|
1139
|
+
this.markdown = undefined;
|
|
1140
|
+
}
|
|
1141
|
+
handleInput(data) {
|
|
1142
|
+
if (matchesKey(data, Key.escape))
|
|
1143
|
+
return this.onClose();
|
|
1144
|
+
if (matchesKey(data, Key.left))
|
|
1145
|
+
return this.dispatch({ type: "prev" });
|
|
1146
|
+
if (matchesKey(data, Key.right))
|
|
1147
|
+
return this.dispatch({ type: "next" });
|
|
1148
|
+
if (matchesKey(data, Key.up))
|
|
1149
|
+
return this.dispatch({ type: "scroll", lines: -1 });
|
|
1150
|
+
if (matchesKey(data, Key.down))
|
|
1151
|
+
return this.dispatch({ type: "scroll", lines: 1 });
|
|
1152
|
+
if (matchesKey(data, Key.pageUp))
|
|
1153
|
+
return this.dispatch({ type: "scroll", lines: -this.height() });
|
|
1154
|
+
if (matchesKey(data, Key.pageDown))
|
|
1155
|
+
return this.dispatch({ type: "scroll", lines: this.height() });
|
|
1156
|
+
}
|
|
1157
|
+
answerLines(id, text, width) {
|
|
1158
|
+
if (this.markdown?.id === id && this.markdown.width === width)
|
|
1159
|
+
return this.markdown.lines;
|
|
1160
|
+
const lines = new Markdown(text, 0, 0, markdownTheme, markdownDefaultTextStyle, {
|
|
1161
|
+
transform: markdownTransform,
|
|
1162
|
+
}).render(Math.max(1, width));
|
|
1163
|
+
this.markdown = { id, width, lines };
|
|
1164
|
+
return lines;
|
|
1165
|
+
}
|
|
1166
|
+
render(width) {
|
|
1167
|
+
const s = this.state;
|
|
1168
|
+
const inner = Math.max(1, width - 2);
|
|
1169
|
+
const bar = style.gray("│");
|
|
1170
|
+
const current = btwCurrent(s);
|
|
1171
|
+
const position = btwPosition(s);
|
|
1172
|
+
const lines = [
|
|
1173
|
+
"",
|
|
1174
|
+
`${style.bold(style.yellow("btw"))}${position ? ` ${style.dim(position)}` : ""}${style.dim(" · side question, not added to the conversation")}`,
|
|
1175
|
+
];
|
|
1176
|
+
const question = s.pending?.question ?? s.error?.question ?? current?.question ?? "";
|
|
1177
|
+
lines.push(...wrap(question, inner).map((l) => `${bar} ${style.bold(l)}`));
|
|
1178
|
+
let overflow = false;
|
|
1179
|
+
if (s.pending) {
|
|
1180
|
+
const spinner = SPINNER[clock.frame % SPINNER.length] ?? "…";
|
|
1181
|
+
lines.push(`${bar} ${style.cyan(spinner)} Thinking… ${style.dim(formatDuration(clock.now - s.pending.startedAt))}`);
|
|
1182
|
+
}
|
|
1183
|
+
else if (s.error) {
|
|
1184
|
+
lines.push(...wrap(s.error.message, inner).map((l) => `${bar} ${style.red(l)}`));
|
|
1185
|
+
}
|
|
1186
|
+
else if (current) {
|
|
1187
|
+
const body = this.answerLines(current.id, current.answer, inner);
|
|
1188
|
+
const view = btwWindow(body, s.scroll, Math.max(3, this.height()));
|
|
1189
|
+
if (view.scroll !== s.scroll)
|
|
1190
|
+
this.state = { ...s, scroll: view.scroll };
|
|
1191
|
+
overflow = view.above || view.below;
|
|
1192
|
+
if (view.above)
|
|
1193
|
+
lines.push(`${bar} ${style.dim("↑ more")}`);
|
|
1194
|
+
lines.push(...view.lines.map((l) => `${bar} ${l}`));
|
|
1195
|
+
if (view.below)
|
|
1196
|
+
lines.push(`${bar} ${style.dim("↓ more")}`);
|
|
1197
|
+
lines.push(`${bar} ${style.dim(btwUsageLine(current))}`);
|
|
1198
|
+
}
|
|
1199
|
+
lines.push(style.dim(` ${btwHints(this.state, overflow)}`));
|
|
1200
|
+
return fit(lines, width);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
package/dist/tui/settings.d.ts
CHANGED
|
@@ -97,7 +97,7 @@ interface SettingDefinition {
|
|
|
97
97
|
export declare const SETTINGS_DEFINITIONS: readonly SettingDefinition[];
|
|
98
98
|
/**
|
|
99
99
|
* Builds the full settings menu: the real, wired setting rows followed by the navigation rows the
|
|
100
|
-
* host wires to the existing managers (`/model`, `/connect`, `/plugins`, `/skills`, `/
|
|
100
|
+
* host wires to the existing managers (`/model`, `/connect`, `/plugins`, `/skills`, `/mcps`, ...).
|
|
101
101
|
* Under `--read-only` every setting row is marked read-only: nothing can be persisted.
|
|
102
102
|
*/
|
|
103
103
|
export declare function settingsMenuRows(input: SettingsMenuInput, navigation: SettingsNavigationAction[]): SettingRow[];
|
package/dist/tui/settings.js
CHANGED
|
@@ -104,7 +104,7 @@ export const SETTINGS_DEFINITIONS = [
|
|
|
104
104
|
valueType: "boolean",
|
|
105
105
|
values: [false, true],
|
|
106
106
|
read: (_config, mcpAllowPersisted) => mcpAllowPersisted,
|
|
107
|
-
description: "Persist process/network consent for MCP servers in your user configuration (mcp.allow). On: every start grants permission and auto-connects enabled servers; use /
|
|
107
|
+
description: "Persist process/network consent for MCP servers in your user configuration (mcp.allow). On: every start grants permission and auto-connects enabled servers; use /mcps to connect now. Off: revokes the persisted consent and disconnects servers.",
|
|
108
108
|
},
|
|
109
109
|
{
|
|
110
110
|
id: "limits.maxTurns",
|
|
@@ -182,7 +182,7 @@ export const SETTINGS_DEFINITIONS = [
|
|
|
182
182
|
];
|
|
183
183
|
/**
|
|
184
184
|
* Builds the full settings menu: the real, wired setting rows followed by the navigation rows the
|
|
185
|
-
* host wires to the existing managers (`/model`, `/connect`, `/plugins`, `/skills`, `/
|
|
185
|
+
* host wires to the existing managers (`/model`, `/connect`, `/plugins`, `/skills`, `/mcps`, ...).
|
|
186
186
|
* Under `--read-only` every setting row is marked read-only: nothing can be persisted.
|
|
187
187
|
*/
|
|
188
188
|
export function settingsMenuRows(input, navigation) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alisio/alisio-code",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.23",
|
|
4
4
|
"description": "Alisio: an extensible, provider-agnostic coding-agent harness for your terminal. TUI, OpenAI-compatible providers, permissioned local tools, context compaction, persistent memory and a typed plugin SDK.",
|
|
5
5
|
"author": "Gustavo Gutiérrez",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
"alisio": "./dist/main.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@alisio/core": "0.1.0-alpha.
|
|
48
|
+
"@alisio/core": "0.1.0-alpha.20",
|
|
49
49
|
"@alisio/plugin-memory": "0.1.0-alpha.9",
|
|
50
50
|
"@alisio/plugin-openai-compatible": "0.1.0-alpha.9",
|
|
51
51
|
"@alisio/plugin-subagents": "0.1.0-alpha.10",
|
|
52
|
-
"@alisio/sdk": "0.1.0-alpha.
|
|
53
|
-
"@alisio/server": "0.1.0-alpha.
|
|
52
|
+
"@alisio/sdk": "0.1.0-alpha.16",
|
|
53
|
+
"@alisio/server": "0.1.0-alpha.3",
|
|
54
54
|
"@earendil-works/pi-tui": "0.87.1",
|
|
55
55
|
"commander": "15.0.0"
|
|
56
56
|
},
|