@botconnector/bccli 0.3.0-beta.3 → 0.4.0-beta.2
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 +30 -1
- package/dist/{args-EVIFNGPQ.js → args-FCLPQ7TO.js} +3 -3
- package/dist/{chunk-5ISYVAXI.js → chunk-2JBRLGKD.js} +2 -2
- package/dist/{chunk-5XN44Q55.js → chunk-ELML4KVQ.js} +7 -1
- package/dist/{chunk-R37QWDTL.js → chunk-L7L4RGKG.js} +2 -2
- package/dist/{chunk-XQLXE666.js → chunk-NGLKANMD.js} +1 -1
- package/dist/chunk-OSWQPSA4.js +252 -0
- package/dist/{chunk-Z5JM76EL.js → chunk-RZUJYAQV.js} +2 -1
- package/dist/{chunk-5C6NQFSI.js → chunk-U7DLMVPZ.js} +1 -1
- package/dist/{chunk-TV6LEHOH.js → chunk-UESQNU4A.js} +1 -1
- package/dist/{chunk-RDQ7HPWS.js → chunk-UN7QXPCB.js} +2 -2
- package/dist/cli.js +119 -110
- package/dist/{config-XPWPGNZJ.js → config-NPSYFAFD.js} +2 -2
- package/dist/{login-WJMGVDRM.js → login-KHU7ONSR.js} +1 -1
- package/dist/{mcpCli-BV3IGR47.js → mcpCli-Q2H63ISP.js} +3 -3
- package/dist/{providerCli-SJRCBSRE.js → providerCli-G3X7KENO.js} +3 -3
- package/dist/{ui-HAW5NMFA.js → ui-A32PTF4F.js} +575 -162
- package/package.json +1 -1
- package/dist/chunk-TBPPXOOZ.js +0 -165
|
@@ -1,39 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
BUILTIN_AGENTS,
|
|
3
4
|
ignorePatterns,
|
|
5
|
+
instructionPaths,
|
|
4
6
|
parseFrontmatter,
|
|
5
7
|
toolLabel
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OSWQPSA4.js";
|
|
7
9
|
import {
|
|
8
10
|
nextMode
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ELML4KVQ.js";
|
|
10
12
|
import {
|
|
11
13
|
hasKey,
|
|
12
14
|
providerName,
|
|
13
15
|
writeGlobalConfig
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-U7DLMVPZ.js";
|
|
17
|
+
import {
|
|
18
|
+
Session
|
|
19
|
+
} from "./chunk-SJH3YLW4.js";
|
|
15
20
|
import {
|
|
16
21
|
CATALOG,
|
|
17
22
|
fillTemplate
|
|
18
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UESQNU4A.js";
|
|
19
24
|
import {
|
|
20
25
|
addGlobalServer,
|
|
21
26
|
globalMcpPath,
|
|
22
27
|
readMcpFile,
|
|
23
28
|
removeGlobalServer
|
|
24
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-NGLKANMD.js";
|
|
25
30
|
import {
|
|
26
|
-
PRESETS
|
|
27
|
-
|
|
31
|
+
PRESETS,
|
|
32
|
+
removeCredential
|
|
33
|
+
} from "./chunk-RZUJYAQV.js";
|
|
28
34
|
|
|
29
35
|
// src/ui/index.tsx
|
|
30
36
|
import { render } from "ink";
|
|
31
37
|
|
|
32
38
|
// src/ui/App.tsx
|
|
39
|
+
import { execFile as execFile2 } from "child_process";
|
|
40
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
33
41
|
import { homedir } from "os";
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
42
|
+
import { dirname, join, relative, resolve } from "path";
|
|
43
|
+
import { Box as Box13, Static, Text as Text15, useApp, useInput as useInput9 } from "ink";
|
|
44
|
+
import { useCallback, useEffect as useEffect2, useRef, useState as useState8 } from "react";
|
|
37
45
|
|
|
38
46
|
// src/commands.ts
|
|
39
47
|
var SLASH_COMMANDS = [
|
|
@@ -41,6 +49,22 @@ var SLASH_COMMANDS = [
|
|
|
41
49
|
{ name: "model", description: "ganti model" },
|
|
42
50
|
{ name: "provider", description: "tambah/pilih provider AI" },
|
|
43
51
|
{ name: "mcp", description: "pasang/kelola server MCP" },
|
|
52
|
+
{ name: "new", description: "sesi baru (sesi lama tetap tersimpan)" },
|
|
53
|
+
{ name: "resume", description: "lanjutkan sesi lain di folder ini" },
|
|
54
|
+
{ name: "session", description: "info sesi ini" },
|
|
55
|
+
{ name: "status", description: "versi, model, mode izin, MCP, konteks" },
|
|
56
|
+
{ name: "permissions", description: "lihat/cabut izin" },
|
|
57
|
+
{ name: "undo", description: "batalkan edit file giliran terakhir" },
|
|
58
|
+
{ name: "diff", description: "git diff project" },
|
|
59
|
+
{ name: "copy", description: "salin jawaban terakhir" },
|
|
60
|
+
{ name: "export", description: "simpan percakapan ke markdown" },
|
|
61
|
+
{ name: "memory", description: "instruksi project (/memory <teks>, /memory global <teks>)" },
|
|
62
|
+
{ name: "init", description: "buat/perbarui AGENTS.md untuk project ini" },
|
|
63
|
+
{ name: "agents", description: "daftar subagent" },
|
|
64
|
+
{ name: "skills", description: "daftar skill dan perintah custom" },
|
|
65
|
+
{ name: "doctor", description: "cek kesehatan instalasi" },
|
|
66
|
+
{ name: "login", description: "simpan API key provider aktif" },
|
|
67
|
+
{ name: "logout", description: "hapus API key provider aktif" },
|
|
44
68
|
{ name: "clear", description: "mulai percakapan baru" },
|
|
45
69
|
{ name: "compact", description: "ringkas percakapan" },
|
|
46
70
|
{ name: "cost", description: "pemakaian token sesi ini" },
|
|
@@ -58,6 +82,125 @@ function expandCommand(def, args) {
|
|
|
58
82
|
${args}` : def.body;
|
|
59
83
|
}
|
|
60
84
|
|
|
85
|
+
// src/slash/copy.ts
|
|
86
|
+
function osc52(text, inTmux) {
|
|
87
|
+
const seq = `\x1B]52;c;${Buffer.from(text).toString("base64")}\x07`;
|
|
88
|
+
return inTmux ? `\x1BPtmux;${seq.replaceAll("\x1B", "\x1B\x1B")}\x1B\\` : seq;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/slash/diff.ts
|
|
92
|
+
import { execFile } from "child_process";
|
|
93
|
+
function git(cwd, args, bin = "git") {
|
|
94
|
+
return new Promise((resolve2) => {
|
|
95
|
+
execFile(
|
|
96
|
+
bin,
|
|
97
|
+
args,
|
|
98
|
+
{ cwd, maxBuffer: 20 * 1024 * 1024 },
|
|
99
|
+
(error, stdout, stderr) => resolve2({ ok: !error, out: error ? stderr : stdout, missing: error?.code === "ENOENT" })
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async function gitDiff(cwd, maxLines = 400, bin = "git") {
|
|
104
|
+
const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"], bin);
|
|
105
|
+
if (inside.missing) return "git tidak terpasang atau tidak ada di PATH, jadi /diff tidak bisa dipakai.";
|
|
106
|
+
if (!inside.ok || inside.out.trim() !== "true") return "Folder ini bukan repository git, jadi tidak ada diff.";
|
|
107
|
+
const base = (await git(cwd, ["rev-parse", "--verify", "-q", "HEAD"])).ok ? "HEAD" : "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
108
|
+
const [stat, diff, untracked] = await Promise.all([
|
|
109
|
+
git(cwd, ["diff", "--stat", base]),
|
|
110
|
+
git(cwd, ["diff", base]),
|
|
111
|
+
git(cwd, ["ls-files", "--others", "--exclude-standard"])
|
|
112
|
+
]);
|
|
113
|
+
const fresh = untracked.out.split("\n").filter(Boolean);
|
|
114
|
+
if (!diff.out.trim() && !fresh.length) return "Tidak ada perubahan yang belum di-commit.";
|
|
115
|
+
const lines = diff.out.trimEnd().split("\n");
|
|
116
|
+
const more = lines.length > maxLines ? `
|
|
117
|
+
\u2026 ${lines.length - maxLines} baris lagi` : "";
|
|
118
|
+
return [stat.out.trim(), lines.slice(0, maxLines).join("\n") + more, fresh.length ? `File baru (belum di-track): ${fresh.join(", ")}` : ""].filter(Boolean).join("\n\n");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/slash/doctor.ts
|
|
122
|
+
async function doctorText(input, deps) {
|
|
123
|
+
const major = Number(deps.nodeVersion.replace(/^v/, "").split(".")[0]);
|
|
124
|
+
const lines = [major >= 22 ? `\u2713 Node ${deps.nodeVersion}` : `\u2717 Node ${deps.nodeVersion} (butuh \u2265 22)`];
|
|
125
|
+
for (const p of input.providers) lines.push(p.ready ? `\u2713 ${p.name}` : `\u25CB ${p.name} (belum ada key)`);
|
|
126
|
+
const cmds = ["rg", "git", "npx", "uvx"];
|
|
127
|
+
const [connection, found] = await Promise.all([
|
|
128
|
+
deps.listModels().then(
|
|
129
|
+
(models) => `\u2713 Koneksi ${input.activeProvider} (${models.length} model)`,
|
|
130
|
+
(error) => `\u2717 Koneksi ${input.activeProvider}: ${error.message}`
|
|
131
|
+
),
|
|
132
|
+
Promise.all(cmds.map(async (cmd) => deps.which(cmd)))
|
|
133
|
+
]);
|
|
134
|
+
lines.push(connection);
|
|
135
|
+
for (const m of input.mcp) lines.push(m.status === "error" ? `\u2717 MCP ${m.name}: ${m.error ?? ""}` : `\u2713 MCP ${m.name} (${m.status})`);
|
|
136
|
+
for (const [i, cmd] of cmds.entries()) lines.push(`${found[i] ? "\u2713" : "\u2717"} ${cmd}`);
|
|
137
|
+
return lines.join("\n");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/slash/export.ts
|
|
141
|
+
function exportMarkdown(messages, title) {
|
|
142
|
+
const parts = [`# ${title}`, ""];
|
|
143
|
+
for (const m of messages) {
|
|
144
|
+
if (m.role === "user") parts.push("## User", "", String(m.content), "");
|
|
145
|
+
else if (m.role === "assistant") {
|
|
146
|
+
for (const c of m.tool_calls ?? []) parts.push(`\u23BF ${c.function.name} ${c.function.arguments.slice(0, 200)}`, "");
|
|
147
|
+
if (m.content) parts.push("## BCCLI", "", m.content, "");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return parts.join("\n");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/slash/info.ts
|
|
154
|
+
var k = (n) => n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
155
|
+
function statusText(s) {
|
|
156
|
+
const ready = s.mcp.filter((m) => m.status === "ready").length;
|
|
157
|
+
const errors = s.mcp.filter((m) => m.status === "error").length;
|
|
158
|
+
return [
|
|
159
|
+
`BCCLI ${s.version}`,
|
|
160
|
+
`Model: ${s.modelRef} (${s.providerLabel})`,
|
|
161
|
+
`Mode izin: ${s.mode}`,
|
|
162
|
+
`Folder: ${s.cwd}`,
|
|
163
|
+
`MCP: ${ready} aktif, ${errors} error`,
|
|
164
|
+
`Token sesi: ${k(s.usage.inputTokens)} masuk \xB7 ${k(s.usage.outputTokens)} keluar`,
|
|
165
|
+
`Konteks: \xB1${Math.round(s.lastInputTokens / 128e3 * 100)}% dari 128k`
|
|
166
|
+
].join("\n");
|
|
167
|
+
}
|
|
168
|
+
function agentsText(defs) {
|
|
169
|
+
return defs.map((d) => `${d.name} \u2014 ${d.description} \xB7 alat: ${d.tools ? d.tools.join(", ") : "semua"} \xB7 model: ${d.model ?? "ikut utama"}`).join("\n");
|
|
170
|
+
}
|
|
171
|
+
function skillsText(skills, commands) {
|
|
172
|
+
const s = skills.length ? skills.map((x) => `${x.name} \u2014 ${x.description.slice(0, 80)} (${x.dir})`).join("\n") : "(tidak ada skill)";
|
|
173
|
+
const c = commands.length ? commands.map((x) => `/${x.name} \u2014 ${x.description ?? ""}`).join("\n") : "(tidak ada perintah custom)";
|
|
174
|
+
return `Skill:
|
|
175
|
+
${s}
|
|
176
|
+
|
|
177
|
+
Perintah custom:
|
|
178
|
+
${c}`;
|
|
179
|
+
}
|
|
180
|
+
function sessionText(s) {
|
|
181
|
+
return [
|
|
182
|
+
`Mulai: ${s.started.toLocaleString("id-ID")}`,
|
|
183
|
+
`${s.messages} pesan \xB7 ${k(s.usage.inputTokens)} token masuk \xB7 ${k(s.usage.outputTokens)} keluar`,
|
|
184
|
+
`Model: ${s.modelRef}`,
|
|
185
|
+
`File: ${s.file}`
|
|
186
|
+
].join("\n");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/slash/memory.ts
|
|
190
|
+
import { appendFileSync, existsSync, readFileSync } from "fs";
|
|
191
|
+
function instructionFiles(cwd, home) {
|
|
192
|
+
return instructionPaths(cwd, home).filter((path) => existsSync(path)).map((path) => ({ path, lines: readFileSync(path, "utf8").split("\n").filter(Boolean).length }));
|
|
193
|
+
}
|
|
194
|
+
function appendMemory(file, text) {
|
|
195
|
+
const prefix = existsSync(file) && !readFileSync(file, "utf8").endsWith("\n") ? "\n" : "";
|
|
196
|
+
appendFileSync(file, `${prefix}- ${text.trim()}
|
|
197
|
+
`);
|
|
198
|
+
}
|
|
199
|
+
function parseMemoryArgs(args) {
|
|
200
|
+
const match = /^global(?:\s+|$)/i.exec(args.trim());
|
|
201
|
+
return match ? { global: true, text: args.trim().slice(match[0].length).trim() } : { global: false, text: args.trim() };
|
|
202
|
+
}
|
|
203
|
+
|
|
61
204
|
// src/ui/Markdown.tsx
|
|
62
205
|
import { Box, Text } from "ink";
|
|
63
206
|
|
|
@@ -116,14 +259,48 @@ function LinePrompt({ label, mask, onSubmit, onCancel }) {
|
|
|
116
259
|
] }) });
|
|
117
260
|
}
|
|
118
261
|
|
|
119
|
-
// src/ui/
|
|
262
|
+
// src/ui/ListPicker.tsx
|
|
120
263
|
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
121
264
|
import { useState as useState2 } from "react";
|
|
122
265
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
123
|
-
var VISIBLE =
|
|
124
|
-
function
|
|
266
|
+
var VISIBLE = 12;
|
|
267
|
+
function ListPicker({ title, items, onPick }) {
|
|
125
268
|
const [filter, setFilter] = useState2("");
|
|
126
269
|
const [index, setIndex] = useState2(0);
|
|
270
|
+
const shown = items.filter((i) => i.label.toLowerCase().includes(filter.toLowerCase()));
|
|
271
|
+
const cursor = Math.min(index, Math.max(0, shown.length - 1));
|
|
272
|
+
useInput2((input, key) => {
|
|
273
|
+
if (key.upArrow) setIndex(Math.max(0, cursor - 1));
|
|
274
|
+
else if (key.downArrow) setIndex(Math.min(shown.length - 1, cursor + 1));
|
|
275
|
+
else if (key.return) onPick(shown[cursor]?.id);
|
|
276
|
+
else if (key.escape) onPick(void 0);
|
|
277
|
+
else if (key.backspace || key.delete) {
|
|
278
|
+
setFilter((f) => f.slice(0, -1));
|
|
279
|
+
setIndex(0);
|
|
280
|
+
} else if (!key.ctrl && !key.meta && !key.tab && input) {
|
|
281
|
+
setFilter((f) => f + input);
|
|
282
|
+
setIndex(0);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
const start = Math.max(0, Math.min(cursor - 5, shown.length - VISIBLE));
|
|
286
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
287
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: `${title} (\u2191\u2193 enter \xB7 ketik untuk menyaring${filter ? `: ${filter}` : ""} \xB7 esc batal)` }),
|
|
288
|
+
shown.slice(start, start + VISIBLE).map((item, i) => /* @__PURE__ */ jsxs2(Text3, { color: start + i === cursor ? color("green") : void 0, children: [
|
|
289
|
+
`${start + i === cursor ? "\u203A" : " "} ${item.label}`,
|
|
290
|
+
item.hint ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` ${item.hint}` }) : null
|
|
291
|
+
] }, item.id)),
|
|
292
|
+
shown.length ? null : /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Tidak ada yang cocok." })
|
|
293
|
+
] });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/ui/ModelPicker.tsx
|
|
297
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
298
|
+
import { useState as useState3 } from "react";
|
|
299
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
300
|
+
var VISIBLE2 = 14;
|
|
301
|
+
function ModelPicker({ groups, current, onPick }) {
|
|
302
|
+
const [filter, setFilter] = useState3("");
|
|
303
|
+
const [index, setIndex] = useState3(0);
|
|
127
304
|
const needle = filter.toLowerCase();
|
|
128
305
|
const rows = [];
|
|
129
306
|
for (const g of groups) {
|
|
@@ -135,7 +312,7 @@ function ModelPicker({ groups, current, onPick }) {
|
|
|
135
312
|
}
|
|
136
313
|
const selectable = rows.flatMap((r, i) => r.kind === "model" ? [i] : []);
|
|
137
314
|
const cursor = selectable[Math.min(index, selectable.length - 1)] ?? -1;
|
|
138
|
-
|
|
315
|
+
useInput3((input, key) => {
|
|
139
316
|
if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
|
|
140
317
|
else if (key.downArrow) setIndex((i) => Math.min(selectable.length - 1, i + 1));
|
|
141
318
|
else if (key.return) {
|
|
@@ -150,86 +327,86 @@ function ModelPicker({ groups, current, onPick }) {
|
|
|
150
327
|
setIndex(0);
|
|
151
328
|
}
|
|
152
329
|
});
|
|
153
|
-
const start = Math.max(0, Math.min(cursor - 5, rows.length -
|
|
154
|
-
return /* @__PURE__ */
|
|
155
|
-
/* @__PURE__ */
|
|
156
|
-
rows.slice(start, start +
|
|
157
|
-
(row, i) => row.kind === "header" ? /* @__PURE__ */
|
|
330
|
+
const start = Math.max(0, Math.min(cursor - 5, rows.length - VISIBLE2));
|
|
331
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
332
|
+
/* @__PURE__ */ jsx4(Text4, { bold: true, children: `Pilih model (ketik untuk menyaring${filter ? `: ${filter}` : ""} \xB7 \u2191\u2193 enter \xB7 esc batal)` }),
|
|
333
|
+
rows.slice(start, start + VISIBLE2).map(
|
|
334
|
+
(row, i) => row.kind === "header" ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: `\u2500\u2500 ${row.text} ` }, `h${start + i}`) : /* @__PURE__ */ jsx4(Text4, { color: start + i === cursor ? color("green") : void 0, children: `${start + i === cursor ? "\u203A" : " "} ${row.label}${row.ref === current ? " (aktif)" : ""}` }, row.ref)
|
|
158
335
|
),
|
|
159
|
-
!selectable.length ? /* @__PURE__ */
|
|
160
|
-
groups.filter((g) => g.error).map((g) => /* @__PURE__ */
|
|
336
|
+
!selectable.length ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Tidak ada model yang cocok." }) : null,
|
|
337
|
+
groups.filter((g) => g.error).map((g) => /* @__PURE__ */ jsx4(Text4, { color: color("yellow"), children: `${g.providerName} \u2014 ${g.error}` }, `e-${g.providerId}`))
|
|
161
338
|
] });
|
|
162
339
|
}
|
|
163
340
|
|
|
164
341
|
// src/ui/ProviderMenu.tsx
|
|
165
|
-
import { Box as
|
|
166
|
-
import { useState as
|
|
167
|
-
import { jsx as
|
|
342
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
343
|
+
import { useState as useState4 } from "react";
|
|
344
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
168
345
|
function ProviderMenu({ entries, onPick }) {
|
|
169
346
|
const rows = [...entries.map((e) => e.id), "custom"];
|
|
170
|
-
const [index, setIndex] =
|
|
171
|
-
|
|
347
|
+
const [index, setIndex] = useState4(0);
|
|
348
|
+
useInput4((_input, key) => {
|
|
172
349
|
if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
|
|
173
350
|
else if (key.downArrow) setIndex((i) => Math.min(rows.length - 1, i + 1));
|
|
174
351
|
else if (key.return) onPick(rows[index]);
|
|
175
352
|
else if (key.escape) onPick(void 0);
|
|
176
353
|
});
|
|
177
|
-
return /* @__PURE__ */
|
|
178
|
-
/* @__PURE__ */
|
|
179
|
-
entries.map((e, i) => /* @__PURE__ */
|
|
354
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
355
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: "Provider (\u2191\u2193 enter, esc batal) \xB7 \u2713 siap \xB7 \u25CB butuh API key" }),
|
|
356
|
+
entries.map((e, i) => /* @__PURE__ */ jsxs4(Text5, { color: i === index ? color("green") : void 0, children: [
|
|
180
357
|
`${i === index ? "\u203A" : " "} ${e.ready ? "\u2713" : "\u25CB"} ${e.name}`,
|
|
181
|
-
/* @__PURE__ */
|
|
358
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: ` ${e.baseURL}` })
|
|
182
359
|
] }, e.id)),
|
|
183
|
-
/* @__PURE__ */
|
|
360
|
+
/* @__PURE__ */ jsx5(Text5, { color: index === rows.length - 1 ? color("green") : void 0, children: `${index === rows.length - 1 ? "\u203A" : " "} Custom\u2026` })
|
|
184
361
|
] });
|
|
185
362
|
}
|
|
186
363
|
|
|
187
364
|
// src/ui/PermissionPrompt.tsx
|
|
188
|
-
import { Box as
|
|
365
|
+
import { Box as Box7, Text as Text7, useInput as useInput5 } from "ink";
|
|
189
366
|
|
|
190
367
|
// src/ui/DiffView.tsx
|
|
191
|
-
import { Box as
|
|
192
|
-
import { jsx as
|
|
368
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
369
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
193
370
|
function DiffView({ diff }) {
|
|
194
|
-
return /* @__PURE__ */
|
|
371
|
+
return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: diff.split("\n").map((line, i) => {
|
|
195
372
|
const mark = line[6];
|
|
196
373
|
const c = mark === "+" ? color("green") : mark === "-" ? color("red") : void 0;
|
|
197
|
-
return /* @__PURE__ */
|
|
374
|
+
return /* @__PURE__ */ jsx6(Text6, { color: c, dimColor: mark !== "+" && mark !== "-", children: line }, i);
|
|
198
375
|
}) });
|
|
199
376
|
}
|
|
200
377
|
|
|
201
378
|
// src/ui/PermissionPrompt.tsx
|
|
202
|
-
import { jsx as
|
|
379
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
203
380
|
var VERB = { edit: "edit", write: "tulis", bash: "jalankan", fetch: "ambil" };
|
|
204
381
|
function PermissionPrompt({
|
|
205
382
|
request,
|
|
206
383
|
onAnswer
|
|
207
384
|
}) {
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
if (
|
|
211
|
-
else if (
|
|
212
|
-
else if (
|
|
213
|
-
else if (
|
|
385
|
+
useInput5((input, key) => {
|
|
386
|
+
const k2 = input.toLowerCase();
|
|
387
|
+
if (k2 === "y") onAnswer("yes");
|
|
388
|
+
else if (k2 === "a" && request.sessionRules?.length) onAnswer("session");
|
|
389
|
+
else if (k2 === "s") onAnswer("all");
|
|
390
|
+
else if (k2 === "n" || key.escape) onAnswer("no");
|
|
214
391
|
});
|
|
215
392
|
const rules = request.sessionRules ?? [];
|
|
216
393
|
const sessionLabel = !rules.length ? null : request.kind === "edit" ? "ya untuk semua edit di project ini, sesi ini" : `ya sesi ini untuk ${rules.join(", ")}`;
|
|
217
|
-
return /* @__PURE__ */
|
|
218
|
-
/* @__PURE__ */
|
|
219
|
-
request.preview ? /* @__PURE__ */
|
|
220
|
-
/* @__PURE__ */
|
|
221
|
-
/* @__PURE__ */
|
|
394
|
+
return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", borderStyle: "round", borderColor: color("yellow"), paddingX: 1, children: [
|
|
395
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: `${request.agent ? `[${request.agent}] ` : ""}Izinkan ${VERB[request.tool] ?? request.tool} ${request.target}?` }),
|
|
396
|
+
request.preview ? /* @__PURE__ */ jsx7(DiffView, { diff: request.preview }) : null,
|
|
397
|
+
/* @__PURE__ */ jsxs5(Text7, { children: [
|
|
398
|
+
/* @__PURE__ */ jsx7(Text7, { color: color("green"), children: "[y] ya" }),
|
|
222
399
|
" ",
|
|
223
|
-
sessionLabel ? /* @__PURE__ */
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
-
/* @__PURE__ */
|
|
400
|
+
sessionLabel ? /* @__PURE__ */ jsx7(Text7, { color: color("cyan"), children: `[a] ${sessionLabel} ` }) : null,
|
|
401
|
+
/* @__PURE__ */ jsx7(Text7, { color: color("yellow"), children: "[s] ya semua " }),
|
|
402
|
+
/* @__PURE__ */ jsx7(Text7, { color: color("red"), children: "[n] tidak" })
|
|
226
403
|
] })
|
|
227
404
|
] });
|
|
228
405
|
}
|
|
229
406
|
|
|
230
407
|
// src/ui/PromptInput.tsx
|
|
231
|
-
import { Box as
|
|
232
|
-
import { useState as
|
|
408
|
+
import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
|
|
409
|
+
import { useState as useState5 } from "react";
|
|
233
410
|
|
|
234
411
|
// src/ui/complete.ts
|
|
235
412
|
import fg from "fast-glob";
|
|
@@ -245,11 +422,11 @@ function completeFile(value, cwd) {
|
|
|
245
422
|
}
|
|
246
423
|
|
|
247
424
|
// src/ui/PromptInput.tsx
|
|
248
|
-
import { jsx as
|
|
425
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
249
426
|
function PromptInput({ disabled, history, cwd, onSubmit, extraCommands }) {
|
|
250
|
-
const [value, setValue] =
|
|
251
|
-
const [historyIndex, setHistoryIndex] =
|
|
252
|
-
const [selected, setSelected] =
|
|
427
|
+
const [value, setValue] = useState5("");
|
|
428
|
+
const [historyIndex, setHistoryIndex] = useState5(null);
|
|
429
|
+
const [selected, setSelected] = useState5(0);
|
|
253
430
|
const all = [...SLASH_COMMANDS, ...(extraCommands ?? []).filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name))];
|
|
254
431
|
const suggestions = value.startsWith("/") && !/\s/.test(value) ? all.filter((c) => c.name.startsWith(value.slice(1))).slice(0, 8) : [];
|
|
255
432
|
const highlighted = Math.min(selected, Math.max(0, suggestions.length - 1));
|
|
@@ -257,7 +434,7 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands }) {
|
|
|
257
434
|
setValue(next);
|
|
258
435
|
setSelected(0);
|
|
259
436
|
};
|
|
260
|
-
|
|
437
|
+
useInput6(
|
|
261
438
|
(input, key) => {
|
|
262
439
|
if (suggestions.length) {
|
|
263
440
|
if (key.upArrow) {
|
|
@@ -319,34 +496,34 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands }) {
|
|
|
319
496
|
},
|
|
320
497
|
{ isActive: !disabled }
|
|
321
498
|
);
|
|
322
|
-
return /* @__PURE__ */
|
|
323
|
-
/* @__PURE__ */
|
|
324
|
-
/* @__PURE__ */
|
|
499
|
+
return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", children: [
|
|
500
|
+
/* @__PURE__ */ jsx8(Box8, { borderStyle: "round", borderColor: color("gray"), paddingX: 1, children: /* @__PURE__ */ jsxs6(Text8, { children: [
|
|
501
|
+
/* @__PURE__ */ jsx8(Text8, { color: color("green"), children: "> " }),
|
|
325
502
|
value,
|
|
326
|
-
disabled ? "" : /* @__PURE__ */
|
|
503
|
+
disabled ? "" : /* @__PURE__ */ jsx8(Text8, { inverse: true, children: " " })
|
|
327
504
|
] }) }),
|
|
328
|
-
suggestions.map((s, i) => /* @__PURE__ */
|
|
505
|
+
suggestions.map((s, i) => /* @__PURE__ */ jsx8(Text8, { color: i === highlighted ? color("green") : void 0, dimColor: i !== highlighted, children: `${i === highlighted ? "\u203A" : " "} /${s.name} ${s.description}` }, s.name))
|
|
329
506
|
] });
|
|
330
507
|
}
|
|
331
508
|
|
|
332
509
|
// src/ui/Spinner.tsx
|
|
333
|
-
import { Text as
|
|
334
|
-
import { useEffect, useState as
|
|
335
|
-
import { jsx as
|
|
510
|
+
import { Text as Text9 } from "ink";
|
|
511
|
+
import { useEffect, useState as useState6 } from "react";
|
|
512
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
336
513
|
var FRAMES = ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "\u273D"];
|
|
337
514
|
function Spinner({ label, startedAt }) {
|
|
338
|
-
const [tick, setTick] =
|
|
515
|
+
const [tick, setTick] = useState6(0);
|
|
339
516
|
useEffect(() => {
|
|
340
517
|
const id = setInterval(() => setTick((t) => t + 1), 120);
|
|
341
518
|
return () => clearInterval(id);
|
|
342
519
|
}, []);
|
|
343
520
|
const seconds = Math.floor((Date.now() - startedAt) / 1e3);
|
|
344
|
-
return /* @__PURE__ */
|
|
521
|
+
return /* @__PURE__ */ jsx9(Text9, { color: color(ACCENT), children: `${FRAMES[tick % FRAMES.length]} ${label}\u2026 (${seconds} dtk \xB7 esc batal)` });
|
|
345
522
|
}
|
|
346
523
|
|
|
347
524
|
// src/ui/StatusBar.tsx
|
|
348
|
-
import { Text as
|
|
349
|
-
import { jsx as
|
|
525
|
+
import { Text as Text10 } from "ink";
|
|
526
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
350
527
|
var LABEL = {
|
|
351
528
|
default: "\u23F5 default",
|
|
352
529
|
acceptEdits: "\u23F5 accept edits",
|
|
@@ -358,87 +535,87 @@ function formatTokens(n) {
|
|
|
358
535
|
}
|
|
359
536
|
function StatusBar({ mode, tokens, busy, model }) {
|
|
360
537
|
const danger = mode === "allowAll";
|
|
361
|
-
return /* @__PURE__ */
|
|
538
|
+
return /* @__PURE__ */ jsx10(Text10, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 shift+tab ganti mode \xB7 ${formatTokens(tokens)} token \xB7 ${busy ? "esc batal" : "/ perintah"}` });
|
|
362
539
|
}
|
|
363
540
|
|
|
364
541
|
// src/ui/McpMenu.tsx
|
|
365
|
-
import { Box as
|
|
366
|
-
import { useState as
|
|
367
|
-
import { jsx as
|
|
542
|
+
import { Box as Box9, Text as Text11, useInput as useInput7 } from "ink";
|
|
543
|
+
import { useState as useState7 } from "react";
|
|
544
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
368
545
|
var MARK = { ready: "\u2713", error: "\u2717", starting: "\u2026", off: "\u25CB" };
|
|
369
546
|
function McpMenu({ items, onPick }) {
|
|
370
|
-
const [index, setIndex] =
|
|
371
|
-
|
|
547
|
+
const [index, setIndex] = useState7(0);
|
|
548
|
+
useInput7((_input, key) => {
|
|
372
549
|
if (key.upArrow) setIndex((i) => Math.max(0, i - 1));
|
|
373
550
|
else if (key.downArrow) setIndex((i) => Math.min(items.length - 1, i + 1));
|
|
374
551
|
else if (key.return) onPick(items[index]?.name);
|
|
375
552
|
else if (key.escape) onPick(void 0);
|
|
376
553
|
});
|
|
377
|
-
return /* @__PURE__ */
|
|
378
|
-
/* @__PURE__ */
|
|
379
|
-
items.map((item, i) => /* @__PURE__ */
|
|
554
|
+
return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
555
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, children: "Server MCP (enter: pasang / hapus \xB7 esc tutup) \xB7 \u2713 aktif \u25CB belum \u2717 error" }),
|
|
556
|
+
items.map((item, i) => /* @__PURE__ */ jsxs7(Text11, { color: i === index ? color("green") : void 0, children: [
|
|
380
557
|
`${i === index ? "\u203A" : " "} ${MARK[item.status]} ${item.name.padEnd(12)}`,
|
|
381
|
-
/* @__PURE__ */
|
|
558
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: item.status === "error" ? ` ${item.error ?? ""}` : ` ${item.description}` })
|
|
382
559
|
] }, item.name))
|
|
383
560
|
] });
|
|
384
561
|
}
|
|
385
562
|
|
|
386
563
|
// src/ui/PlanApproval.tsx
|
|
387
|
-
import { Box as
|
|
388
|
-
import { jsx as
|
|
564
|
+
import { Box as Box10, Text as Text12, useInput as useInput8 } from "ink";
|
|
565
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
389
566
|
function PlanApproval({ plan, onAnswer }) {
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
if (
|
|
393
|
-
else if (
|
|
394
|
-
else if (
|
|
567
|
+
useInput8((input, key) => {
|
|
568
|
+
const k2 = input.toLowerCase();
|
|
569
|
+
if (k2 === "a") onAnswer("acceptEdits");
|
|
570
|
+
else if (k2 === "y") onAnswer("default");
|
|
571
|
+
else if (k2 === "n" || key.escape) onAnswer("no");
|
|
395
572
|
});
|
|
396
|
-
return /* @__PURE__ */
|
|
397
|
-
/* @__PURE__ */
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
573
|
+
return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
574
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, children: "Rencana dari agent" }),
|
|
575
|
+
/* @__PURE__ */ jsx12(Markdown, { text: plan }),
|
|
576
|
+
/* @__PURE__ */ jsxs8(Text12, { children: [
|
|
577
|
+
/* @__PURE__ */ jsx12(Text12, { color: color("green"), children: "[a] ya, edit otomatis" }),
|
|
401
578
|
" ",
|
|
402
|
-
/* @__PURE__ */
|
|
579
|
+
/* @__PURE__ */ jsx12(Text12, { color: color("cyan"), children: "[y] ya, tanya tiap langkah" }),
|
|
403
580
|
" ",
|
|
404
|
-
/* @__PURE__ */
|
|
581
|
+
/* @__PURE__ */ jsx12(Text12, { color: color("red"), children: "[n] belum, perbaiki dulu" })
|
|
405
582
|
] })
|
|
406
583
|
] });
|
|
407
584
|
}
|
|
408
585
|
|
|
409
586
|
// src/ui/TodoList.tsx
|
|
410
|
-
import { Box as
|
|
411
|
-
import { jsx as
|
|
587
|
+
import { Box as Box11, Text as Text13 } from "ink";
|
|
588
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
412
589
|
var MARK2 = { completed: "\u2611", in_progress: "\u25FC", pending: "\u2610" };
|
|
413
590
|
function TodoList({ items }) {
|
|
414
591
|
if (!items.length || items.every((t) => t.status === "completed")) return null;
|
|
415
|
-
return /* @__PURE__ */
|
|
592
|
+
return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: items.map((t, i) => /* @__PURE__ */ jsx13(Text13, { dimColor: t.status === "completed", color: t.status === "in_progress" ? color("yellow") : void 0, children: `${MARK2[t.status]} ${t.content}` }, i)) });
|
|
416
593
|
}
|
|
417
594
|
|
|
418
595
|
// src/ui/ToolBlock.tsx
|
|
419
|
-
import { Box as
|
|
420
|
-
import { jsx as
|
|
596
|
+
import { Box as Box12, Text as Text14 } from "ink";
|
|
597
|
+
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
421
598
|
var PREVIEW_LINES = 5;
|
|
422
599
|
function ToolBlock({ tool, target, output, display, isError, done, expanded, sub }) {
|
|
423
600
|
const isTask = tool === "task";
|
|
424
601
|
const isDiff = !!display && /^\s*\d+ [+\- ] /.test(display);
|
|
425
602
|
const lines = (output ?? "").split("\n");
|
|
426
603
|
const shown = expanded ? lines : lines.slice(0, PREVIEW_LINES);
|
|
427
|
-
return /* @__PURE__ */
|
|
428
|
-
/* @__PURE__ */
|
|
429
|
-
/* @__PURE__ */
|
|
430
|
-
/* @__PURE__ */
|
|
431
|
-
/* @__PURE__ */
|
|
432
|
-
display && !isDiff && !isTask ? /* @__PURE__ */
|
|
604
|
+
return /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", marginLeft: 2, children: [
|
|
605
|
+
/* @__PURE__ */ jsxs9(Text14, { children: [
|
|
606
|
+
/* @__PURE__ */ jsx14(Text14, { color: isError ? color("red") : done ? color("green") : color("yellow"), children: "\u23BF " }),
|
|
607
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: toolLabel(tool) }),
|
|
608
|
+
/* @__PURE__ */ jsx14(Text14, { children: ` ${target}` }),
|
|
609
|
+
display && !isDiff && !isTask ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: ` (${display})` }) : null
|
|
433
610
|
] }),
|
|
434
|
-
sub?.length ? /* @__PURE__ */
|
|
435
|
-
sub.slice(-6).map((s) => /* @__PURE__ */
|
|
436
|
-
sub.length > 6 ? /* @__PURE__ */
|
|
611
|
+
sub?.length ? /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", marginLeft: 3, children: [
|
|
612
|
+
sub.slice(-6).map((s) => /* @__PURE__ */ jsx14(Text14, { dimColor: s.done && !s.isError, color: s.isError ? color("red") : void 0, children: `\u23BF ${toolLabel(s.tool)} ${s.target}` }, s.id)),
|
|
613
|
+
sub.length > 6 ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: `\u2026 ${sub.length - 6} langkah sebelumnya` }) : null
|
|
437
614
|
] }) : null,
|
|
438
|
-
isTask && done ? /* @__PURE__ */
|
|
439
|
-
isDiff ? /* @__PURE__ */
|
|
440
|
-
shown.map((line, i) => /* @__PURE__ */
|
|
441
|
-
lines.length > shown.length ? /* @__PURE__ */
|
|
615
|
+
isTask && done ? /* @__PURE__ */ jsx14(Box12, { marginLeft: 3, children: /* @__PURE__ */ jsx14(Text14, { color: isError ? color("red") : color("green"), children: `${isError ? "\u2717 gagal" : "\u2713 selesai"}${display ? ` \xB7 ${display}` : ""}` }) }) : null,
|
|
616
|
+
isDiff ? /* @__PURE__ */ jsx14(Box12, { marginLeft: 3, children: /* @__PURE__ */ jsx14(DiffView, { diff: display }) }) : output && (isError || tool === "bash") && !(isTask && !isError) ? /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", marginLeft: 3, children: [
|
|
617
|
+
shown.map((line, i) => /* @__PURE__ */ jsx14(Text14, { dimColor: !isError, color: isError ? color("red") : void 0, children: line }, i)),
|
|
618
|
+
lines.length > shown.length ? /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: `\u2026 ${lines.length - shown.length} baris lagi (ctrl+o)` }) : null
|
|
442
619
|
] }) : null
|
|
443
620
|
] });
|
|
444
621
|
}
|
|
@@ -500,11 +677,11 @@ function applyEvent(t, event) {
|
|
|
500
677
|
}
|
|
501
678
|
|
|
502
679
|
// src/ui/App.tsx
|
|
503
|
-
import { jsx as
|
|
680
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
504
681
|
function helpText(runtime) {
|
|
505
682
|
const custom = runtime.commands.filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name));
|
|
506
683
|
return [
|
|
507
|
-
...SLASH_COMMANDS.map((c) => `/${c.name.padEnd(
|
|
684
|
+
...SLASH_COMMANDS.map((c) => `/${c.name.padEnd(12)} ${c.description}`),
|
|
508
685
|
...custom.length ? ["", "Perintah custom:", ...custom.map((c) => `/${c.name.padEnd(8)} ${c.description ?? ""}`)] : [],
|
|
509
686
|
...runtime.skills.length ? ["", "Skill:", ...runtime.skills.map((s) => `/${s.name.padEnd(8)} ${s.description.slice(0, 60)}`)] : [],
|
|
510
687
|
"",
|
|
@@ -513,27 +690,29 @@ function helpText(runtime) {
|
|
|
513
690
|
"@file + tab lengkapi nama file \u2191\u2193 riwayat input"
|
|
514
691
|
].join("\n");
|
|
515
692
|
}
|
|
693
|
+
var INIT_PROMPT = "Pelajari project ini (struktur folder, file package/build, perintah test dan lint, konvensi kode yang terlihat). Lalu buat AGENTS.md di root project, atau perbarui bila sudah ada, berisi: cara build, test, dan lint; struktur singkat; konvensi penting. Ringkas dan faktual, hanya yang benar-benar ada di project.";
|
|
516
694
|
var labelFor = (runtime, ref) => `${ref.slice(ref.indexOf("/") + 1)} \xB7 ${runtime.providerLabel()}`;
|
|
517
695
|
function App({ runtime, initialPrompt, version }) {
|
|
518
696
|
const { exit } = useApp();
|
|
519
|
-
const [transcript, setTranscript] =
|
|
520
|
-
const [busy, setBusy] =
|
|
521
|
-
const [startedAt, setStartedAt] =
|
|
522
|
-
const [mode, setMode] =
|
|
523
|
-
const [tokens, setTokens] =
|
|
524
|
-
const [asks, setAsks] =
|
|
697
|
+
const [transcript, setTranscript] = useState8(() => ({ done: [entry({ kind: "header" })], live: [] }));
|
|
698
|
+
const [busy, setBusy] = useState8(false);
|
|
699
|
+
const [startedAt, setStartedAt] = useState8(0);
|
|
700
|
+
const [mode, setMode] = useState8(runtime.agent.permissions.mode);
|
|
701
|
+
const [tokens, setTokens] = useState8(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
|
|
702
|
+
const [asks, setAsks] = useState8([]);
|
|
525
703
|
const pending = asks[0] ?? null;
|
|
526
704
|
const askId = useRef(0);
|
|
527
|
-
const [picker, setPicker] =
|
|
528
|
-
const [providerMenu, setProviderMenu] =
|
|
529
|
-
const [prompt, setPrompt] =
|
|
530
|
-
const ask = (label, mask = false) => new Promise((
|
|
531
|
-
const [modelLabel, setModelLabel] =
|
|
532
|
-
const [history, setHistory] =
|
|
705
|
+
const [picker, setPicker] = useState8(null);
|
|
706
|
+
const [providerMenu, setProviderMenu] = useState8(null);
|
|
707
|
+
const [prompt, setPrompt] = useState8(null);
|
|
708
|
+
const ask = useCallback((label, mask = false) => new Promise((resolve2) => setPrompt({ label, mask, resolve: resolve2 })), []);
|
|
709
|
+
const [modelLabel, setModelLabel] = useState8(() => labelFor(runtime, runtime.modelRef));
|
|
710
|
+
const [history, setHistory] = useState8([]);
|
|
533
711
|
const controller = useRef(null);
|
|
534
|
-
const [todos, setTodos] =
|
|
535
|
-
const [mcpMenu, setMcpMenu] =
|
|
536
|
-
const [
|
|
712
|
+
const [todos, setTodos] = useState8(runtime.todos.items);
|
|
713
|
+
const [mcpMenu, setMcpMenu] = useState8(null);
|
|
714
|
+
const [listPicker, setListPicker] = useState8(null);
|
|
715
|
+
const [planAsk, setPlanAsk] = useState8(null);
|
|
537
716
|
useEffect2(() => runtime.todos.subscribe(setTodos), [runtime]);
|
|
538
717
|
const extraCommands = [
|
|
539
718
|
...runtime.commands.filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name.toLowerCase())).map((c) => ({ name: c.name.toLowerCase(), description: c.description ?? "perintah custom" })),
|
|
@@ -554,8 +733,8 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
554
733
|
}, []);
|
|
555
734
|
useEffect2(() => {
|
|
556
735
|
runtime.agent.onEvent = onEvent;
|
|
557
|
-
runtime.agent.askPermission = (request) => new Promise((
|
|
558
|
-
runtime.interaction.approvePlan = (plan) => new Promise((
|
|
736
|
+
runtime.agent.askPermission = (request) => new Promise((resolve2) => setAsks((q) => [...q, { id: askId.current++, request, resolve: resolve2 }]));
|
|
737
|
+
runtime.interaction.approvePlan = (plan) => new Promise((resolve2) => setPlanAsk({ plan, resolve: resolve2 }));
|
|
559
738
|
}, [runtime, onEvent]);
|
|
560
739
|
const runTurn = useCallback(
|
|
561
740
|
async (text) => {
|
|
@@ -570,7 +749,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
570
749
|
},
|
|
571
750
|
[runtime]
|
|
572
751
|
);
|
|
573
|
-
const
|
|
752
|
+
const runSlashCommand = useCallback(
|
|
574
753
|
async (name, args) => {
|
|
575
754
|
switch (name) {
|
|
576
755
|
case "help":
|
|
@@ -634,6 +813,235 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
634
813
|
setMcpMenu(items);
|
|
635
814
|
return;
|
|
636
815
|
}
|
|
816
|
+
case "new":
|
|
817
|
+
runtime.newSession();
|
|
818
|
+
setTokens(0);
|
|
819
|
+
notice("Sesi baru dimulai. Sesi sebelumnya tetap tersimpan (/resume).");
|
|
820
|
+
return;
|
|
821
|
+
case "session":
|
|
822
|
+
notice(
|
|
823
|
+
sessionText({
|
|
824
|
+
file: runtime.session.file,
|
|
825
|
+
started: runtime.startedAt,
|
|
826
|
+
messages: runtime.agent.messages.length,
|
|
827
|
+
usage: runtime.agent.totalUsage,
|
|
828
|
+
modelRef: runtime.modelRef
|
|
829
|
+
})
|
|
830
|
+
);
|
|
831
|
+
return;
|
|
832
|
+
case "resume": {
|
|
833
|
+
const others = Session.list(runtime.home, runtime.cwd).filter((s) => s.session.file !== runtime.session.file);
|
|
834
|
+
if (!others.length) {
|
|
835
|
+
notice("Belum ada sesi lain di folder ini.");
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
setListPicker({
|
|
839
|
+
title: "Lanjutkan sesi",
|
|
840
|
+
items: others.map((s) => ({
|
|
841
|
+
id: s.session.file,
|
|
842
|
+
label: `${s.mtime.toLocaleString("id-ID")} ${s.preview || "(kosong)"}`,
|
|
843
|
+
hint: `${s.session.load().length} pesan`
|
|
844
|
+
})),
|
|
845
|
+
onPick: (id) => {
|
|
846
|
+
setListPicker(null);
|
|
847
|
+
const chosen = others.find((s) => s.session.file === id);
|
|
848
|
+
if (!chosen) return;
|
|
849
|
+
runtime.resume(chosen.session);
|
|
850
|
+
setTokens(0);
|
|
851
|
+
const tail = runtime.agent.messages.filter((m) => (m.role === "user" || m.role === "assistant") && m.content).slice(-3);
|
|
852
|
+
notice(
|
|
853
|
+
[`Melanjutkan sesi (${runtime.agent.messages.length} pesan):`, ...tail.map((m) => `${m.role === "user" ? ">" : "\u25CF"} ${String(m.content).slice(0, 200)}`)].join(
|
|
854
|
+
"\n"
|
|
855
|
+
)
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
case "status":
|
|
862
|
+
notice(
|
|
863
|
+
statusText({
|
|
864
|
+
version,
|
|
865
|
+
modelRef: runtime.modelRef,
|
|
866
|
+
providerLabel: runtime.providerLabel(),
|
|
867
|
+
mode: runtime.agent.permissions.mode,
|
|
868
|
+
cwd: runtime.cwd,
|
|
869
|
+
mcp: runtime.mcp.states(),
|
|
870
|
+
usage: runtime.agent.totalUsage,
|
|
871
|
+
lastInputTokens: runtime.agent.lastInputTokens
|
|
872
|
+
})
|
|
873
|
+
);
|
|
874
|
+
return;
|
|
875
|
+
case "agents":
|
|
876
|
+
notice(agentsText([...BUILTIN_AGENTS, ...runtime.agentDefs]));
|
|
877
|
+
return;
|
|
878
|
+
case "skills":
|
|
879
|
+
notice(skillsText(runtime.skills, runtime.commands));
|
|
880
|
+
return;
|
|
881
|
+
case "doctor": {
|
|
882
|
+
notice("Memeriksa\u2026");
|
|
883
|
+
const c = runtime.config;
|
|
884
|
+
notice(
|
|
885
|
+
await doctorText(
|
|
886
|
+
{
|
|
887
|
+
providers: Object.keys(c.providers).map((id) => ({ id, name: providerName(c, id), ready: hasKey(c, id, runtime.env) })),
|
|
888
|
+
mcp: runtime.mcp.states(),
|
|
889
|
+
activeProvider: runtime.providerLabel()
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
which: (cmd) => new Promise((done) => execFile2(process.platform === "win32" ? "where" : "which", [cmd], (error) => done(!error))),
|
|
893
|
+
nodeVersion: process.version,
|
|
894
|
+
listModels: () => runtime.agent.provider.listModels()
|
|
895
|
+
}
|
|
896
|
+
)
|
|
897
|
+
);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
case "permissions": {
|
|
901
|
+
const rules = runtime.agent.permissions.list();
|
|
902
|
+
if (!rules.length) {
|
|
903
|
+
notice("Belum ada izin tersimpan. Izin dari jawaban [a] dan dari config akan muncul di sini.");
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
setListPicker({
|
|
907
|
+
title: "Izin aktif (enter: cabut izin sesi)",
|
|
908
|
+
items: rules.map((r) => ({ id: r.rule, label: r.rule, hint: r.source === "config" ? "config" : "sesi ini" })),
|
|
909
|
+
onPick: (id) => {
|
|
910
|
+
setListPicker(null);
|
|
911
|
+
const rule = rules.find((r) => r.rule === id);
|
|
912
|
+
if (!rule) return;
|
|
913
|
+
if (rule.source === "config") notice(`${rule.rule} berasal dari config/flag; hapus dari ~/.bccli/config.json untuk mencabutnya.`, "warn");
|
|
914
|
+
else {
|
|
915
|
+
runtime.agent.permissions.revoke(rule.rule);
|
|
916
|
+
notice(`Izin ${rule.rule} dicabut.`);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
case "memory": {
|
|
923
|
+
if (!args) {
|
|
924
|
+
const files = instructionFiles(runtime.cwd, runtime.home);
|
|
925
|
+
notice(files.length ? files.map((f) => `${f.path} (${f.lines} baris)`).join("\n") : "Belum ada AGENTS.md / BCCLI.md. Tambah dengan /memory <teks>.");
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
const { global, text } = parseMemoryArgs(args);
|
|
929
|
+
if (!text) {
|
|
930
|
+
notice("Tulis teksnya: /memory global <teks>.", "warn");
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
const file = global ? join(runtime.home, "BCCLI.md") : join(runtime.cwd, "AGENTS.md");
|
|
934
|
+
try {
|
|
935
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
936
|
+
appendMemory(file, text);
|
|
937
|
+
} catch (error) {
|
|
938
|
+
notice(`Gagal menulis ${file}: ${error.message}`, "error");
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
runtime.rebuildSystemPrompt();
|
|
942
|
+
notice(`Ditambahkan ke ${file}.`);
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
case "init":
|
|
946
|
+
void runTurn(INIT_PROMPT);
|
|
947
|
+
return;
|
|
948
|
+
case "login": {
|
|
949
|
+
const id = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
950
|
+
const key = await ask(`API key untuk ${runtime.providerLabel()}`, true);
|
|
951
|
+
if (!key) return;
|
|
952
|
+
const result = await runtime.addProviderKey(id, key);
|
|
953
|
+
if (!result.ok) {
|
|
954
|
+
notice(result.error, "error");
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
runtime.setModel(runtime.modelRef);
|
|
958
|
+
const envName = runtime.config.providers[id]?.apiKeyEnv;
|
|
959
|
+
if (envName && runtime.env[envName]) {
|
|
960
|
+
notice(`Key ${runtime.providerLabel()} tersimpan, tapi ${envName} di environment lebih diutamakan dan tetap dipakai.`, "warn");
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
notice(`Key ${runtime.providerLabel()} tersimpan \xB7 ${result.models} model.`);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
case "logout": {
|
|
967
|
+
const id = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
968
|
+
const removed = removeCredential(id, runtime.env);
|
|
969
|
+
const envName = runtime.config.providers[id]?.apiKeyEnv;
|
|
970
|
+
if (envName && runtime.env[envName]) {
|
|
971
|
+
notice(
|
|
972
|
+
`${removed ? "Key tersimpan dihapus, tapi" : "Tidak ada key tersimpan;"} ${envName} masih ada di environment dan tetap dipakai.`,
|
|
973
|
+
"warn"
|
|
974
|
+
);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
if (!removed) {
|
|
978
|
+
notice(`Tidak ada key tersimpan untuk ${runtime.providerLabel()}.`);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
try {
|
|
982
|
+
runtime.setModel(runtime.modelRef);
|
|
983
|
+
} catch (error) {
|
|
984
|
+
const fail = async () => {
|
|
985
|
+
throw error;
|
|
986
|
+
};
|
|
987
|
+
runtime.agent.provider = { chat: fail, listModels: fail };
|
|
988
|
+
}
|
|
989
|
+
notice(`Key ${runtime.providerLabel()} dihapus. Pakai /login atau /provider untuk menambah lagi.`);
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
case "diff":
|
|
993
|
+
notice(await gitDiff(runtime.cwd));
|
|
994
|
+
return;
|
|
995
|
+
case "undo": {
|
|
996
|
+
const result = await runtime.checkpoints.undo();
|
|
997
|
+
if (!result) {
|
|
998
|
+
notice("Tidak ada edit file yang bisa dibatalkan.");
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
const rel = (p) => relative(runtime.cwd, p) || p;
|
|
1002
|
+
notice(
|
|
1003
|
+
[
|
|
1004
|
+
result.failed.length ? `Gagal dikembalikan: ${result.failed.map(rel).join(", ")}` : "",
|
|
1005
|
+
result.restored.length ? `Dikembalikan: ${result.restored.map(rel).join(", ")}` : "",
|
|
1006
|
+
result.deleted.length ? `Dihapus (file baru): ${result.deleted.map(rel).join(", ")}` : "",
|
|
1007
|
+
result.skipped.length ? `Terlalu besar untuk disimpan, tidak diubah: ${result.skipped.map(rel).join(", ")}` : "",
|
|
1008
|
+
"Catatan: perubahan lewat perintah bash tidak bisa di-undo."
|
|
1009
|
+
].filter(Boolean).join("\n")
|
|
1010
|
+
);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
case "copy": {
|
|
1014
|
+
const last = runtime.agent.messages.findLast((m) => m.role === "assistant" && typeof m.content === "string" && m.content.trim());
|
|
1015
|
+
if (!last || typeof last.content !== "string") {
|
|
1016
|
+
notice("Belum ada jawaban untuk disalin.");
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
process.stdout.write(osc52(last.content, !!process.env.TMUX));
|
|
1020
|
+
const file = join(runtime.home, "last-answer.md");
|
|
1021
|
+
mkdirSync(runtime.home, { recursive: true });
|
|
1022
|
+
writeFileSync(file, last.content);
|
|
1023
|
+
notice(`Disalin ke clipboard (bila terminal mendukung OSC 52) dan disimpan di ${file}.`);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
case "export": {
|
|
1027
|
+
const name2 = args || `bccli-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[:T]/g, "-")}.md`;
|
|
1028
|
+
const file = resolve(runtime.cwd, name2);
|
|
1029
|
+
if (existsSync2(file)) {
|
|
1030
|
+
const answer2 = await ask(`${file} sudah ada. Timpa? (y/N)`);
|
|
1031
|
+
if (!answer2 || !/^y(a|es)?$/i.test(answer2.trim())) {
|
|
1032
|
+
notice("Ekspor dibatalkan.");
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
try {
|
|
1037
|
+
writeFileSync(file, exportMarkdown(runtime.agent.messages, `Sesi BCCLI ${runtime.startedAt.toLocaleString("id-ID")}`));
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
notice(`Gagal menulis ${file}: ${error.message}`, "error");
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
notice(`Percakapan disimpan di ${file}.`);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
637
1045
|
case "provider": {
|
|
638
1046
|
const c = runtime.config;
|
|
639
1047
|
setProviderMenu(
|
|
@@ -645,7 +1053,11 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
645
1053
|
notice(`Perintah tidak dikenal: /${name}. Ketik /help.`, "warn");
|
|
646
1054
|
}
|
|
647
1055
|
},
|
|
648
|
-
[runtime, notice, exit]
|
|
1056
|
+
[runtime, notice, exit, version, runTurn, ask]
|
|
1057
|
+
);
|
|
1058
|
+
const runSlash = useCallback(
|
|
1059
|
+
(name, args) => runSlashCommand(name, args).catch((error) => notice(`/${name}: ${error.message}`, "error")),
|
|
1060
|
+
[runSlashCommand, notice]
|
|
649
1061
|
);
|
|
650
1062
|
const submit = useCallback(
|
|
651
1063
|
(text) => {
|
|
@@ -662,7 +1074,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
662
1074
|
else if (skill) {
|
|
663
1075
|
let body;
|
|
664
1076
|
try {
|
|
665
|
-
body = parseFrontmatter(
|
|
1077
|
+
body = parseFrontmatter(readFileSync2(skill.file, "utf8")).body;
|
|
666
1078
|
} catch (error) {
|
|
667
1079
|
notice(`Skill ${skill.name} tidak bisa dibaca: ${error.code ?? error.message}`, "error");
|
|
668
1080
|
return;
|
|
@@ -681,7 +1093,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
681
1093
|
submit(initialPrompt);
|
|
682
1094
|
}
|
|
683
1095
|
}, [initialPrompt, submit]);
|
|
684
|
-
|
|
1096
|
+
useInput9((input, key) => {
|
|
685
1097
|
if (key.tab && key.shift) {
|
|
686
1098
|
const next = nextMode(runtime.agent.permissions.mode);
|
|
687
1099
|
runtime.agent.permissions.mode = next;
|
|
@@ -755,31 +1167,31 @@ ${t.output}`);
|
|
|
755
1167
|
const renderEntry = (e) => {
|
|
756
1168
|
switch (e.kind) {
|
|
757
1169
|
case "header":
|
|
758
|
-
return /* @__PURE__ */
|
|
759
|
-
/* @__PURE__ */
|
|
760
|
-
/* @__PURE__ */
|
|
761
|
-
/* @__PURE__ */
|
|
1170
|
+
return /* @__PURE__ */ jsx15(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs10(Text15, { children: [
|
|
1171
|
+
/* @__PURE__ */ jsx15(Text15, { color: color(ACCENT), bold: true, children: " \u273B " }),
|
|
1172
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: `BCCLI ${version}` }),
|
|
1173
|
+
/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: ` \xB7 ${runtime.modelRef} (${runtime.providerLabel()}) \xB7 ${cwd}` })
|
|
762
1174
|
] }) }, e.id);
|
|
763
1175
|
case "user":
|
|
764
|
-
return /* @__PURE__ */
|
|
1176
|
+
return /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: `> ${e.text}` }) }, e.id);
|
|
765
1177
|
case "assistant":
|
|
766
1178
|
if (!e.text.trim()) return null;
|
|
767
|
-
return /* @__PURE__ */
|
|
768
|
-
/* @__PURE__ */
|
|
769
|
-
/* @__PURE__ */
|
|
1179
|
+
return /* @__PURE__ */ jsxs10(Box13, { marginTop: 1, children: [
|
|
1180
|
+
/* @__PURE__ */ jsx15(Text15, { color: color(ACCENT), children: "\u25CF " }),
|
|
1181
|
+
/* @__PURE__ */ jsx15(Markdown, { text: e.text.replace(/^\s+/, "") })
|
|
770
1182
|
] }, e.id);
|
|
771
1183
|
case "tool":
|
|
772
|
-
return /* @__PURE__ */
|
|
1184
|
+
return /* @__PURE__ */ jsx15(ToolBlock, { tool: e.tool, target: e.target, output: e.output, display: e.display, isError: e.isError, done: e.done, sub: e.sub }, e.id);
|
|
773
1185
|
case "notice":
|
|
774
|
-
return /* @__PURE__ */
|
|
1186
|
+
return /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: e.tone === "error" ? color("red") : e.tone === "warn" ? color("yellow") : void 0, dimColor: e.tone === "info", children: e.text }) }, e.id);
|
|
775
1187
|
}
|
|
776
1188
|
};
|
|
777
|
-
return /* @__PURE__ */
|
|
778
|
-
/* @__PURE__ */
|
|
1189
|
+
return /* @__PURE__ */ jsxs10(Box13, { flexDirection: "column", children: [
|
|
1190
|
+
/* @__PURE__ */ jsx15(Static, { items: transcript.done, children: renderEntry }),
|
|
779
1191
|
transcript.live.map(renderEntry),
|
|
780
|
-
busy && !pending && !planAsk ? /* @__PURE__ */
|
|
781
|
-
pending ? /* @__PURE__ */
|
|
782
|
-
picker ? /* @__PURE__ */
|
|
1192
|
+
busy && !pending && !planAsk ? /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Spinner, { label: "Berpikir", startedAt }) }) : null,
|
|
1193
|
+
pending ? /* @__PURE__ */ jsx15(PermissionPrompt, { request: pending.request, onAnswer: answer }, pending.id) : null,
|
|
1194
|
+
picker ? /* @__PURE__ */ jsx15(
|
|
783
1195
|
ModelPicker,
|
|
784
1196
|
{
|
|
785
1197
|
groups: picker,
|
|
@@ -790,9 +1202,10 @@ ${t.output}`);
|
|
|
790
1202
|
}
|
|
791
1203
|
}
|
|
792
1204
|
) : null,
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1205
|
+
listPicker ? /* @__PURE__ */ jsx15(ListPicker, { title: listPicker.title, items: listPicker.items, onPick: listPicker.onPick }) : null,
|
|
1206
|
+
mcpMenu ? /* @__PURE__ */ jsx15(McpMenu, { items: mcpMenu, onPick: (n) => void pickMcp(n) }) : null,
|
|
1207
|
+
providerMenu ? /* @__PURE__ */ jsx15(ProviderMenu, { entries: providerMenu, onPick: (id) => void pickProvider(id) }) : null,
|
|
1208
|
+
prompt ? /* @__PURE__ */ jsx15(
|
|
796
1209
|
LinePrompt,
|
|
797
1210
|
{
|
|
798
1211
|
label: prompt.label,
|
|
@@ -807,7 +1220,7 @@ ${t.output}`);
|
|
|
807
1220
|
}
|
|
808
1221
|
}
|
|
809
1222
|
) : null,
|
|
810
|
-
planAsk ? /* @__PURE__ */
|
|
1223
|
+
planAsk ? /* @__PURE__ */ jsx15(
|
|
811
1224
|
PlanApproval,
|
|
812
1225
|
{
|
|
813
1226
|
plan: planAsk.plan,
|
|
@@ -818,18 +1231,18 @@ ${t.output}`);
|
|
|
818
1231
|
}
|
|
819
1232
|
}
|
|
820
1233
|
) : null,
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
1234
|
+
/* @__PURE__ */ jsx15(TodoList, { items: todos }),
|
|
1235
|
+
/* @__PURE__ */ jsx15(PromptInput, { disabled: busy || !!pending || !!picker || !!providerMenu || !!prompt || !!planAsk || !!mcpMenu || !!listPicker, history, cwd: runtime.cwd, onSubmit: submit, extraCommands }),
|
|
1236
|
+
/* @__PURE__ */ jsx15(StatusBar, { mode, tokens, busy, model: modelLabel })
|
|
824
1237
|
] });
|
|
825
1238
|
}
|
|
826
1239
|
|
|
827
1240
|
// src/ui/index.tsx
|
|
828
|
-
import { jsx as
|
|
1241
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
829
1242
|
async function startInteractive(rt, opts) {
|
|
830
1243
|
if (opts.resume) {
|
|
831
|
-
const { Session } = await import("./session-7UB77CEG.js");
|
|
832
|
-
const list =
|
|
1244
|
+
const { Session: Session2 } = await import("./session-7UB77CEG.js");
|
|
1245
|
+
const list = Session2.list(rt.home, rt.cwd).slice(0, 20);
|
|
833
1246
|
if (!list.length) console.log("Belum ada sesi di folder ini; memulai sesi baru.");
|
|
834
1247
|
else {
|
|
835
1248
|
list.forEach((s, i) => {
|
|
@@ -843,7 +1256,7 @@ async function startInteractive(rt, opts) {
|
|
|
843
1256
|
if (chosen) rt.resume(chosen.session);
|
|
844
1257
|
}
|
|
845
1258
|
}
|
|
846
|
-
const { serversToStart, setProjectTrust } = await import("./config-
|
|
1259
|
+
const { serversToStart, setProjectTrust } = await import("./config-NPSYFAFD.js");
|
|
847
1260
|
const plan = serversToStart(rt.home, rt.cwd);
|
|
848
1261
|
if (plan.needTrust.length) {
|
|
849
1262
|
const { createInterface } = await import("readline/promises");
|
|
@@ -857,7 +1270,7 @@ async function startInteractive(rt, opts) {
|
|
|
857
1270
|
rl.close();
|
|
858
1271
|
}
|
|
859
1272
|
void rt.startMcp(plan.start);
|
|
860
|
-
const instance = render(/* @__PURE__ */
|
|
1273
|
+
const instance = render(/* @__PURE__ */ jsx16(App, { runtime: rt, initialPrompt: opts.initialPrompt, version: opts.version }), { exitOnCtrlC: true });
|
|
861
1274
|
await instance.waitUntilExit();
|
|
862
1275
|
await rt.mcp.stop();
|
|
863
1276
|
return 0;
|