@botconnector/bccli 0.2.0-beta.1 → 0.3.0-beta.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/package.json CHANGED
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "@botconnector/bccli",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.3.0-beta.1",
4
4
  "description": "BCCLI — BotConnector AI coding agent for the terminal",
5
5
  "type": "module",
6
- "bin": { "bccli": "dist/cli.js" },
7
- "files": ["dist"],
8
- "engines": { "node": ">=22" },
9
- "repository": { "type": "git", "url": "git+https://github.com/farijarifriyanto-debug/bccli.git" },
6
+ "bin": {
7
+ "bccli": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=22"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/farijarifriyanto-debug/bccli.git"
18
+ },
10
19
  "license": "MIT",
11
20
  "scripts": {
12
21
  "build": "tsup",
@@ -15,6 +24,7 @@
15
24
  "test": "vitest run"
16
25
  },
17
26
  "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.30.1",
18
28
  "diff": "^9.0.0",
19
29
  "fast-glob": "^3.3.3",
20
30
  "ink": "^7.1.1",
@@ -1,506 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- ignorePatterns,
4
- nextMode,
5
- toolLabel
6
- } from "./chunk-WZYWKWPD.js";
7
-
8
- // src/ui/index.tsx
9
- import { render } from "ink";
10
-
11
- // src/ui/App.tsx
12
- import { homedir } from "os";
13
- import { Box as Box7, Static, Text as Text9, useApp, useInput as useInput4 } from "ink";
14
- import { useCallback, useEffect as useEffect2, useRef, useState as useState4 } from "react";
15
-
16
- // src/commands.ts
17
- var SLASH_COMMANDS = [
18
- { name: "help", description: "daftar perintah dan pintasan" },
19
- { name: "model", description: "ganti model" },
20
- { name: "clear", description: "mulai percakapan baru" },
21
- { name: "compact", description: "ringkas percakapan" },
22
- { name: "cost", description: "pemakaian token sesi ini" },
23
- { name: "exit", description: "keluar" }
24
- ];
25
- function parseSlash(text) {
26
- if (!text.startsWith("/")) return void 0;
27
- const [name, ...rest] = text.slice(1).trim().split(/\s+/);
28
- return { name: name.toLowerCase(), args: rest.join(" ") };
29
- }
30
-
31
- // src/ui/Markdown.tsx
32
- import { Box, Text } from "ink";
33
-
34
- // src/ui/theme.ts
35
- function color(name) {
36
- return process.env.NO_COLOR ? void 0 : name;
37
- }
38
- var ACCENT = "green";
39
-
40
- // src/ui/Markdown.tsx
41
- import { jsx } from "react/jsx-runtime";
42
- function inline(line) {
43
- return line.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).map((part, i) => {
44
- if (part.length > 4 && part.startsWith("**") && part.endsWith("**")) {
45
- return /* @__PURE__ */ jsx(Text, { bold: true, children: part.slice(2, -2) }, i);
46
- }
47
- if (part.length > 2 && part.startsWith("`") && part.endsWith("`")) {
48
- return /* @__PURE__ */ jsx(Text, { color: color("cyan"), children: part.slice(1, -1) }, i);
49
- }
50
- return part;
51
- });
52
- }
53
- function Markdown({ text }) {
54
- let inCode = false;
55
- const rows = [];
56
- text.split("\n").forEach((line, i) => {
57
- if (line.trimStart().startsWith("```")) {
58
- inCode = !inCode;
59
- return;
60
- }
61
- rows.push(
62
- inCode ? /* @__PURE__ */ jsx(Text, { color: color("cyan"), children: ` ${line}` }, i) : /* @__PURE__ */ jsx(Text, { children: line ? inline(line) : " " }, i)
63
- );
64
- });
65
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: rows });
66
- }
67
-
68
- // src/ui/ModelPicker.tsx
69
- import { Box as Box2, Text as Text2, useInput } from "ink";
70
- import { useState } from "react";
71
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
72
- function ModelPicker({ models, current, onPick }) {
73
- const [index, setIndex] = useState(Math.max(0, models.indexOf(current)));
74
- useInput((_input, key) => {
75
- if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
76
- else if (key.downArrow) setIndex((i) => Math.min(models.length - 1, i + 1));
77
- else if (key.return) onPick(models[index]);
78
- else if (key.escape) onPick(void 0);
79
- });
80
- const start = Math.max(0, Math.min(index - 5, models.length - 10));
81
- return /* @__PURE__ */ jsxs(Box2, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
82
- /* @__PURE__ */ jsx2(Text2, { bold: true, children: "Pilih model (\u2191\u2193 enter, esc batal)" }),
83
- models.slice(start, start + 10).map((m, i) => /* @__PURE__ */ jsx2(Text2, { color: start + i === index ? color("green") : void 0, children: `${start + i === index ? "\u203A" : " "} ${m}${m === current ? " (aktif)" : ""}` }, m))
84
- ] });
85
- }
86
-
87
- // src/ui/PermissionPrompt.tsx
88
- import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
89
-
90
- // src/ui/DiffView.tsx
91
- import { Box as Box3, Text as Text3 } from "ink";
92
- import { jsx as jsx3 } from "react/jsx-runtime";
93
- function DiffView({ diff }) {
94
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: diff.split("\n").map((line, i) => {
95
- const mark = line[6];
96
- const c = mark === "+" ? color("green") : mark === "-" ? color("red") : void 0;
97
- return /* @__PURE__ */ jsx3(Text3, { color: c, dimColor: mark !== "+" && mark !== "-", children: line }, i);
98
- }) });
99
- }
100
-
101
- // src/ui/PermissionPrompt.tsx
102
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
103
- var VERB = { edit: "edit", write: "tulis", bash: "jalankan", fetch: "ambil" };
104
- function PermissionPrompt({
105
- request,
106
- onAnswer
107
- }) {
108
- useInput2((input, key) => {
109
- const k = input.toLowerCase();
110
- if (k === "y") onAnswer("yes");
111
- else if (k === "a" && request.sessionRules?.length) onAnswer("session");
112
- else if (k === "n" || key.escape) onAnswer("no");
113
- });
114
- const rules = request.sessionRules ?? [];
115
- const sessionLabel = !rules.length ? null : request.kind === "edit" ? "ya untuk semua edit di project ini, sesi ini" : `ya sesi ini untuk ${rules.join(", ")}`;
116
- return /* @__PURE__ */ jsxs2(Box4, { flexDirection: "column", borderStyle: "round", borderColor: color("yellow"), paddingX: 1, children: [
117
- /* @__PURE__ */ jsx4(Text4, { bold: true, children: `Izinkan ${VERB[request.tool] ?? request.tool} ${request.target}?` }),
118
- request.preview ? /* @__PURE__ */ jsx4(DiffView, { diff: request.preview }) : null,
119
- /* @__PURE__ */ jsxs2(Text4, { children: [
120
- /* @__PURE__ */ jsx4(Text4, { color: color("green"), children: "[y] ya" }),
121
- " ",
122
- sessionLabel ? /* @__PURE__ */ jsx4(Text4, { color: color("cyan"), children: `[a] ${sessionLabel} ` }) : null,
123
- /* @__PURE__ */ jsx4(Text4, { color: color("red"), children: "[n] tidak" })
124
- ] })
125
- ] });
126
- }
127
-
128
- // src/ui/PromptInput.tsx
129
- import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
130
- import { useState as useState2 } from "react";
131
-
132
- // src/ui/complete.ts
133
- import fg from "fast-glob";
134
- function completeFile(value, cwd) {
135
- const match = /(^|\s)@([^\s]*)$/.exec(value);
136
- if (!match) return value;
137
- const prefix = match[2];
138
- const escaped = prefix.replace(/([*?[\]{}()!])/g, "\\$1");
139
- const found = fg.sync(`${escaped}*`, { cwd, ignore: ignorePatterns(cwd), onlyFiles: false, markDirectories: true, dot: false }).sort();
140
- if (!found.length) return value;
141
- const pick = found[0];
142
- return `${value.slice(0, value.length - prefix.length)}${pick}${pick.endsWith("/") ? "" : " "}`;
143
- }
144
-
145
- // src/ui/PromptInput.tsx
146
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
147
- function PromptInput({ disabled, history, cwd, onSubmit }) {
148
- const [value, setValue] = useState2("");
149
- const [historyIndex, setHistoryIndex] = useState2(null);
150
- useInput3(
151
- (input, key) => {
152
- if (key.return) {
153
- if (value.endsWith("\\")) {
154
- setValue(`${value.slice(0, -1)}
155
- `);
156
- return;
157
- }
158
- const text = value.trim();
159
- if (!text) return;
160
- setValue("");
161
- setHistoryIndex(null);
162
- onSubmit(text);
163
- return;
164
- }
165
- if (key.backspace || key.delete) {
166
- setValue((v) => v.slice(0, -1));
167
- return;
168
- }
169
- if (key.upArrow) {
170
- if (!history.length) return;
171
- const i = historyIndex === null ? history.length - 1 : Math.max(0, historyIndex - 1);
172
- setHistoryIndex(i);
173
- setValue(history[i]);
174
- return;
175
- }
176
- if (key.downArrow) {
177
- if (historyIndex === null) return;
178
- const i = historyIndex + 1;
179
- setHistoryIndex(i >= history.length ? null : i);
180
- setValue(i >= history.length ? "" : history[i]);
181
- return;
182
- }
183
- if (key.tab && !key.shift) {
184
- setValue((v) => completeFile(v, cwd));
185
- return;
186
- }
187
- if (key.ctrl || key.meta || key.escape || key.tab || key.leftArrow || key.rightArrow) return;
188
- setValue((v) => v + input.replace(/\r/g, "\n"));
189
- },
190
- { isActive: !disabled }
191
- );
192
- const suggestions = value.startsWith("/") && !/\s/.test(value) ? SLASH_COMMANDS.filter((c) => c.name.startsWith(value.slice(1))) : [];
193
- return /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", children: [
194
- /* @__PURE__ */ jsx5(Box5, { borderStyle: "round", borderColor: color("gray"), paddingX: 1, children: /* @__PURE__ */ jsxs3(Text5, { children: [
195
- /* @__PURE__ */ jsx5(Text5, { color: color("green"), children: "> " }),
196
- value,
197
- disabled ? "" : /* @__PURE__ */ jsx5(Text5, { inverse: true, children: " " })
198
- ] }) }),
199
- suggestions.map((s) => /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: ` /${s.name} ${s.description}` }, s.name))
200
- ] });
201
- }
202
-
203
- // src/ui/Spinner.tsx
204
- import { Text as Text6 } from "ink";
205
- import { useEffect, useState as useState3 } from "react";
206
- import { jsx as jsx6 } from "react/jsx-runtime";
207
- var FRAMES = ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "\u273D"];
208
- function Spinner({ label, startedAt }) {
209
- const [tick, setTick] = useState3(0);
210
- useEffect(() => {
211
- const id = setInterval(() => setTick((t) => t + 1), 120);
212
- return () => clearInterval(id);
213
- }, []);
214
- const seconds = Math.floor((Date.now() - startedAt) / 1e3);
215
- return /* @__PURE__ */ jsx6(Text6, { color: color(ACCENT), children: `${FRAMES[tick % FRAMES.length]} ${label}\u2026 (${seconds} dtk \xB7 esc batal)` });
216
- }
217
-
218
- // src/ui/StatusBar.tsx
219
- import { Text as Text7 } from "ink";
220
- import { jsx as jsx7 } from "react/jsx-runtime";
221
- var LABEL = {
222
- default: "\u23F5 default",
223
- acceptEdits: "\u23F5 accept edits",
224
- plan: "\u23F8 plan mode",
225
- allowAll: "\u23F5\u23F5 allow all"
226
- };
227
- function formatTokens(n) {
228
- return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
229
- }
230
- function StatusBar({ mode, tokens, busy }) {
231
- const danger = mode === "allowAll";
232
- return /* @__PURE__ */ jsx7(Text7, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]} \xB7 shift+tab ganti mode \xB7 ${formatTokens(tokens)} token \xB7 ${busy ? "esc batal" : "/ perintah"}` });
233
- }
234
-
235
- // src/ui/ToolBlock.tsx
236
- import { Box as Box6, Text as Text8 } from "ink";
237
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
238
- var PREVIEW_LINES = 5;
239
- function ToolBlock({ tool, target, output, display, isError, done, expanded }) {
240
- const isDiff = !!display && /^\s*\d+ [+\- ] /.test(display);
241
- const lines = (output ?? "").split("\n");
242
- const shown = expanded ? lines : lines.slice(0, PREVIEW_LINES);
243
- return /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", marginLeft: 2, children: [
244
- /* @__PURE__ */ jsxs4(Text8, { children: [
245
- /* @__PURE__ */ jsx8(Text8, { color: isError ? color("red") : done ? color("green") : color("yellow"), children: "\u23BF " }),
246
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: toolLabel(tool) }),
247
- /* @__PURE__ */ jsx8(Text8, { children: ` ${target}` }),
248
- display && !isDiff ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: ` (${display})` }) : null
249
- ] }),
250
- isDiff ? /* @__PURE__ */ jsx8(Box6, { marginLeft: 3, children: /* @__PURE__ */ jsx8(DiffView, { diff: display }) }) : output && (isError || tool === "bash") ? /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", marginLeft: 3, children: [
251
- shown.map((line, i) => /* @__PURE__ */ jsx8(Text8, { dimColor: !isError, color: isError ? color("red") : void 0, children: line }, i)),
252
- lines.length > shown.length ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: `\u2026 ${lines.length - shown.length} baris lagi (ctrl+o)` }) : null
253
- ] }) : null
254
- ] });
255
- }
256
-
257
- // src/ui/transcript.ts
258
- var nextId = 0;
259
- var entry = (e) => ({ ...e, id: nextId++ });
260
- var NOTICES = {
261
- compacted: ["Percakapan diringkas agar muat di konteks model.", "info"],
262
- stepLimit: ['Batas 50 langkah tercapai. Ketik "lanjut" untuk meneruskan.', "warn"],
263
- aborted: ["Dibatalkan.", "warn"]
264
- };
265
- function endTurn(t) {
266
- return { done: [...t.done, ...t.live], live: [] };
267
- }
268
- function applyEvent(t, event) {
269
- switch (event.type) {
270
- case "text": {
271
- const last = t.live.at(-1);
272
- if (last?.kind === "assistant") return { ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] };
273
- return { ...t, live: [...t.live, entry({ kind: "assistant", text: event.delta })] };
274
- }
275
- case "toolStart":
276
- return {
277
- done: [...t.done, ...t.live],
278
- live: [entry({ kind: "tool", callId: event.id, tool: event.tool, target: event.target, done: false })]
279
- };
280
- case "toolEnd": {
281
- const live = t.live.map(
282
- (e) => e.kind === "tool" && e.callId === event.id ? { ...e, output: event.output, display: event.display, isError: event.isError, done: true } : e
283
- );
284
- return { done: [...t.done, ...live], live: [] };
285
- }
286
- case "error":
287
- return { ...t, live: [...t.live, entry({ kind: "notice", text: `Error: ${event.message}`, tone: "error" })] };
288
- default: {
289
- const notice = NOTICES[event.type];
290
- return notice ? { ...t, live: [...t.live, entry({ kind: "notice", text: notice[0], tone: notice[1] })] } : t;
291
- }
292
- }
293
- }
294
-
295
- // src/ui/App.tsx
296
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
297
- var HELP = [
298
- ...SLASH_COMMANDS.map((c) => `/${c.name.padEnd(8)} ${c.description}`),
299
- "",
300
- "shift+tab ganti mode izin esc batalkan giliran",
301
- "ctrl+o output alat terakhir lengkap \\ + enter baris baru",
302
- "@file + tab lengkapi nama file \u2191\u2193 riwayat input"
303
- ].join("\n");
304
- function App({ runtime, initialPrompt, version }) {
305
- const { exit } = useApp();
306
- const [transcript, setTranscript] = useState4(() => ({ done: [entry({ kind: "header" })], live: [] }));
307
- const [busy, setBusy] = useState4(false);
308
- const [startedAt, setStartedAt] = useState4(0);
309
- const [mode, setMode] = useState4(runtime.agent.permissions.mode);
310
- const [tokens, setTokens] = useState4(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
311
- const [pending, setPending] = useState4(
312
- null
313
- );
314
- const [picker, setPicker] = useState4(null);
315
- const [history, setHistory] = useState4([]);
316
- const controller = useRef(null);
317
- const lastTool = useRef(null);
318
- const toolTargets = useRef(/* @__PURE__ */ new Map());
319
- const notice = useCallback((text, tone = "info") => {
320
- setTranscript((t) => ({ ...t, done: [...t.done, entry({ kind: "notice", text, tone })] }));
321
- }, []);
322
- const onEvent = useCallback((event) => {
323
- if (event.type === "toolStart") toolTargets.current.set(event.id, event.target);
324
- if (event.type === "toolEnd") {
325
- lastTool.current = { tool: event.tool, target: toolTargets.current.get(event.id) ?? "", output: event.output };
326
- }
327
- if (event.type === "usage") setTokens(event.inputTokens + event.outputTokens);
328
- setTranscript((t) => applyEvent(t, event));
329
- }, []);
330
- useEffect2(() => {
331
- runtime.agent.onEvent = onEvent;
332
- runtime.agent.askPermission = (request) => new Promise((resolve) => setPending({ request, resolve }));
333
- }, [runtime, onEvent]);
334
- const runTurn = useCallback(
335
- async (text) => {
336
- setTranscript((t) => ({ ...t, done: [...t.done, entry({ kind: "user", text })] }));
337
- setBusy(true);
338
- setStartedAt(Date.now());
339
- controller.current = new AbortController();
340
- await runtime.agent.run(text, controller.current.signal);
341
- controller.current = null;
342
- setTranscript(endTurn);
343
- setBusy(false);
344
- },
345
- [runtime]
346
- );
347
- const runSlash = useCallback(
348
- async (name, args) => {
349
- switch (name) {
350
- case "help":
351
- notice(HELP);
352
- return;
353
- case "exit":
354
- exit();
355
- return;
356
- case "clear":
357
- runtime.agent.clear();
358
- setTokens(0);
359
- notice("Percakapan dikosongkan.");
360
- return;
361
- case "cost": {
362
- const u = runtime.agent.totalUsage;
363
- notice(`${u.inputTokens.toLocaleString("id-ID")} token masuk \xB7 ${u.outputTokens.toLocaleString("id-ID")} token keluar`);
364
- return;
365
- }
366
- case "compact": {
367
- setBusy(true);
368
- setStartedAt(Date.now());
369
- controller.current = new AbortController();
370
- try {
371
- await runtime.agent.compact(controller.current.signal);
372
- } catch (error) {
373
- notice(`Gagal meringkas: ${error.message}`, "error");
374
- }
375
- controller.current = null;
376
- setBusy(false);
377
- return;
378
- }
379
- case "model": {
380
- if (args) {
381
- try {
382
- runtime.setModel(args);
383
- notice(`Model: ${args}`);
384
- } catch (error) {
385
- notice(error.message, "error");
386
- }
387
- return;
388
- }
389
- try {
390
- const provider = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
391
- const models = await runtime.agent.provider.listModels();
392
- setPicker(models.map((m) => `${provider}/${m}`));
393
- } catch (error) {
394
- notice(`Tidak bisa mengambil daftar model: ${error.message}`, "error");
395
- }
396
- return;
397
- }
398
- default:
399
- notice(`Perintah tidak dikenal: /${name}. Ketik /help.`, "warn");
400
- }
401
- },
402
- [runtime, notice, exit]
403
- );
404
- const submit = useCallback(
405
- (text) => {
406
- setHistory((h) => [...h, text]);
407
- const slash = parseSlash(text);
408
- if (slash) void runSlash(slash.name, slash.args);
409
- else void runTurn(text);
410
- },
411
- [runSlash, runTurn]
412
- );
413
- const initialSent = useRef(false);
414
- useEffect2(() => {
415
- if (initialPrompt && !initialSent.current) {
416
- initialSent.current = true;
417
- submit(initialPrompt);
418
- }
419
- }, [initialPrompt, submit]);
420
- useInput4((input, key) => {
421
- if (key.tab && key.shift) {
422
- const next = nextMode(runtime.agent.permissions.mode);
423
- runtime.agent.permissions.mode = next;
424
- setMode(next);
425
- } else if (key.escape && busy && !pending) {
426
- controller.current?.abort();
427
- } else if (key.ctrl && input === "o" && lastTool.current) {
428
- const t = lastTool.current;
429
- notice(`\u23BF ${t.tool} ${t.target}
430
- ${t.output}`);
431
- }
432
- });
433
- const answer = (a) => {
434
- pending?.resolve(a);
435
- setPending(null);
436
- };
437
- const cwd = runtime.cwd.startsWith(homedir()) ? `~${runtime.cwd.slice(homedir().length)}` : runtime.cwd;
438
- const renderEntry = (e) => {
439
- switch (e.kind) {
440
- case "header":
441
- return /* @__PURE__ */ jsx9(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs5(Text9, { children: [
442
- /* @__PURE__ */ jsx9(Text9, { color: color(ACCENT), bold: true, children: " \u273B " }),
443
- /* @__PURE__ */ jsx9(Text9, { bold: true, children: `BCCLI ${version}` }),
444
- /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: ` \xB7 ${runtime.modelRef} \xB7 ${cwd}` })
445
- ] }) }, e.id);
446
- case "user":
447
- return /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: `> ${e.text}` }) }, e.id);
448
- case "assistant":
449
- if (!e.text.trim()) return null;
450
- return /* @__PURE__ */ jsxs5(Box7, { marginTop: 1, children: [
451
- /* @__PURE__ */ jsx9(Text9, { color: color(ACCENT), children: "\u25CF " }),
452
- /* @__PURE__ */ jsx9(Markdown, { text: e.text.replace(/^\s+/, "") })
453
- ] }, e.id);
454
- case "tool":
455
- return /* @__PURE__ */ jsx9(ToolBlock, { tool: e.tool, target: e.target, output: e.output, display: e.display, isError: e.isError, done: e.done }, e.id);
456
- case "notice":
457
- return /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: e.tone === "error" ? color("red") : e.tone === "warn" ? color("yellow") : void 0, dimColor: e.tone === "info", children: e.text }) }, e.id);
458
- }
459
- };
460
- return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", children: [
461
- /* @__PURE__ */ jsx9(Static, { items: transcript.done, children: renderEntry }),
462
- transcript.live.map(renderEntry),
463
- busy && !pending ? /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Spinner, { label: "Berpikir", startedAt }) }) : null,
464
- pending ? /* @__PURE__ */ jsx9(PermissionPrompt, { request: pending.request, onAnswer: answer }) : null,
465
- picker ? /* @__PURE__ */ jsx9(
466
- ModelPicker,
467
- {
468
- models: picker,
469
- current: runtime.modelRef,
470
- onPick: (m) => {
471
- setPicker(null);
472
- if (m) void runSlash("model", m);
473
- }
474
- }
475
- ) : null,
476
- /* @__PURE__ */ jsx9(PromptInput, { disabled: busy || !!pending || !!picker, history, cwd: runtime.cwd, onSubmit: submit }),
477
- /* @__PURE__ */ jsx9(StatusBar, { mode, tokens, busy })
478
- ] });
479
- }
480
-
481
- // src/ui/index.tsx
482
- import { jsx as jsx10 } from "react/jsx-runtime";
483
- async function startInteractive(rt, opts) {
484
- if (opts.resume) {
485
- const { Session } = await import("./session-7UB77CEG.js");
486
- const list = Session.list(rt.home, rt.cwd).slice(0, 20);
487
- if (!list.length) console.log("Belum ada sesi di folder ini; memulai sesi baru.");
488
- else {
489
- list.forEach((s, i) => {
490
- console.log(`${String(i + 1).padStart(2)}. ${s.mtime.toLocaleString("id-ID")} ${s.preview}`);
491
- });
492
- const { createInterface } = await import("readline/promises");
493
- const rl = createInterface({ input: process.stdin, output: process.stdout });
494
- const answer = Number(await rl.question("Nomor sesi (enter = baru): "));
495
- rl.close();
496
- const chosen = list[answer - 1];
497
- if (chosen) rt.resume(chosen.session);
498
- }
499
- }
500
- const instance = render(/* @__PURE__ */ jsx10(App, { runtime: rt, initialPrompt: opts.initialPrompt, version: opts.version }), { exitOnCtrlC: true });
501
- await instance.waitUntilExit();
502
- return 0;
503
- }
504
- export {
505
- startInteractive
506
- };