@botconnector/bccli 0.4.0-beta.1 → 0.4.0-beta.11
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 +56 -1
- package/dist/{args-EAKPBBGX.js → args-SKWUZOJD.js} +3 -2
- package/dist/{chunk-Z5JM76EL.js → chunk-2MGINW4O.js} +4 -1
- package/dist/{chunk-QH7XXNHU.js → chunk-6BLZELE6.js} +2 -2
- package/dist/{chunk-5C6NQFSI.js → chunk-7S6JXQCH.js} +1 -1
- package/dist/{chunk-XQLXE666.js → chunk-BFZXRKW5.js} +1 -1
- package/dist/chunk-CLN4Z5E3.js +60 -0
- package/dist/{chunk-TV6LEHOH.js → chunk-DX7CZTBB.js} +1 -1
- package/dist/{chunk-OSWQPSA4.js → chunk-KV27KTOX.js} +2 -0
- package/dist/{chunk-M7INIL2C.js → chunk-TRMRDQ43.js} +17 -3
- package/dist/{chunk-5ISYVAXI.js → chunk-U7GK73CK.js} +90 -15
- package/dist/cli.js +226 -40
- package/dist/{config-XPWPGNZJ.js → config-YUNKPCNB.js} +2 -2
- package/dist/integrationCli-4PEJ74UG.js +583 -0
- package/dist/{login-FTMR64IA.js → login-N2CJC6RU.js} +1 -1
- package/dist/{mcpCli-BV3IGR47.js → mcpCli-DZWQPTOS.js} +3 -3
- package/dist/providerCli-Q7LRM2NP.js +12 -0
- package/dist/{ui-LWZ75D7H.js → ui-6GTQUJOE.js} +211 -39
- package/package.json +2 -1
- package/dist/providerCli-SJRCBSRE.js +0 -11
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
instructionPaths,
|
|
6
6
|
parseFrontmatter,
|
|
7
7
|
toolLabel
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KV27KTOX.js";
|
|
9
9
|
import {
|
|
10
10
|
nextMode
|
|
11
11
|
} from "./chunk-ELML4KVQ.js";
|
|
@@ -13,31 +13,37 @@ import {
|
|
|
13
13
|
hasKey,
|
|
14
14
|
providerName,
|
|
15
15
|
writeGlobalConfig
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7S6JXQCH.js";
|
|
17
|
+
import {
|
|
18
|
+
REASONING_LEVELS,
|
|
19
|
+
assertReasoningSupported,
|
|
20
|
+
parseReasoningLevel,
|
|
21
|
+
supportedReasoningLevels
|
|
22
|
+
} from "./chunk-CLN4Z5E3.js";
|
|
17
23
|
import {
|
|
18
24
|
Session
|
|
19
25
|
} from "./chunk-SJH3YLW4.js";
|
|
20
26
|
import {
|
|
21
27
|
CATALOG,
|
|
22
28
|
fillTemplate
|
|
23
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-DX7CZTBB.js";
|
|
24
30
|
import {
|
|
25
31
|
addGlobalServer,
|
|
26
32
|
globalMcpPath,
|
|
27
33
|
readMcpFile,
|
|
28
34
|
removeGlobalServer
|
|
29
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-BFZXRKW5.js";
|
|
30
36
|
import {
|
|
31
37
|
PRESETS,
|
|
32
38
|
removeCredential
|
|
33
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-2MGINW4O.js";
|
|
34
40
|
|
|
35
41
|
// src/ui/index.tsx
|
|
36
42
|
import { render } from "ink";
|
|
37
43
|
|
|
38
44
|
// src/ui/App.tsx
|
|
39
|
-
import {
|
|
40
|
-
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
45
|
+
import { execFile as execFile2 } from "child_process";
|
|
46
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
41
47
|
import { homedir } from "os";
|
|
42
48
|
import { dirname, join, relative, resolve } from "path";
|
|
43
49
|
import { Box as Box13, Static, Text as Text15, useApp, useInput as useInput9 } from "ink";
|
|
@@ -47,6 +53,7 @@ import { useCallback, useEffect as useEffect2, useRef, useState as useState8 } f
|
|
|
47
53
|
var SLASH_COMMANDS = [
|
|
48
54
|
{ name: "help", description: "daftar perintah dan pintasan" },
|
|
49
55
|
{ name: "model", description: "ganti model" },
|
|
56
|
+
{ name: "reasoning", description: "atur reasoning: auto/off/low/medium/high/max" },
|
|
50
57
|
{ name: "provider", description: "tambah/pilih provider AI" },
|
|
51
58
|
{ name: "mcp", description: "pasang/kelola server MCP" },
|
|
52
59
|
{ name: "new", description: "sesi baru (sesi lama tetap tersimpan)" },
|
|
@@ -90,13 +97,19 @@ function osc52(text, inTmux) {
|
|
|
90
97
|
|
|
91
98
|
// src/slash/diff.ts
|
|
92
99
|
import { execFile } from "child_process";
|
|
93
|
-
function git(cwd, args) {
|
|
100
|
+
function git(cwd, args, bin = "git") {
|
|
94
101
|
return new Promise((resolve2) => {
|
|
95
|
-
execFile(
|
|
102
|
+
execFile(
|
|
103
|
+
bin,
|
|
104
|
+
args,
|
|
105
|
+
{ cwd, maxBuffer: 20 * 1024 * 1024 },
|
|
106
|
+
(error, stdout, stderr) => resolve2({ ok: !error, out: error ? stderr : stdout, missing: error?.code === "ENOENT" })
|
|
107
|
+
);
|
|
96
108
|
});
|
|
97
109
|
}
|
|
98
|
-
async function gitDiff(cwd, maxLines = 400) {
|
|
99
|
-
const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
110
|
+
async function gitDiff(cwd, maxLines = 400, bin = "git") {
|
|
111
|
+
const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"], bin);
|
|
112
|
+
if (inside.missing) return "git tidak terpasang atau tidak ada di PATH, jadi /diff tidak bisa dipakai.";
|
|
100
113
|
if (!inside.ok || inside.out.trim() !== "true") return "Folder ini bukan repository git, jadi tidak ada diff.";
|
|
101
114
|
const base = (await git(cwd, ["rev-parse", "--verify", "-q", "HEAD"])).ok ? "HEAD" : "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
102
115
|
const [stat, diff, untracked] = await Promise.all([
|
|
@@ -117,13 +130,17 @@ async function doctorText(input, deps) {
|
|
|
117
130
|
const major = Number(deps.nodeVersion.replace(/^v/, "").split(".")[0]);
|
|
118
131
|
const lines = [major >= 22 ? `\u2713 Node ${deps.nodeVersion}` : `\u2717 Node ${deps.nodeVersion} (butuh \u2265 22)`];
|
|
119
132
|
for (const p of input.providers) lines.push(p.ready ? `\u2713 ${p.name}` : `\u25CB ${p.name} (belum ada key)`);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
const cmds = ["rg", "git", "npx", "uvx"];
|
|
134
|
+
const [connection, found] = await Promise.all([
|
|
135
|
+
deps.listModels().then(
|
|
136
|
+
(models) => `\u2713 Koneksi ${input.activeProvider} (${models.length} model)`,
|
|
137
|
+
(error) => `\u2717 Koneksi ${input.activeProvider}: ${error.message}`
|
|
138
|
+
),
|
|
139
|
+
Promise.all(cmds.map(async (cmd) => deps.which(cmd)))
|
|
140
|
+
]);
|
|
141
|
+
lines.push(connection);
|
|
125
142
|
for (const m of input.mcp) lines.push(m.status === "error" ? `\u2717 MCP ${m.name}: ${m.error ?? ""}` : `\u2713 MCP ${m.name} (${m.status})`);
|
|
126
|
-
for (const
|
|
143
|
+
for (const [i, cmd] of cmds.entries()) lines.push(`${found[i] ? "\u2713" : "\u2717"} ${cmd}`);
|
|
127
144
|
return lines.join("\n");
|
|
128
145
|
}
|
|
129
146
|
|
|
@@ -186,6 +203,10 @@ function appendMemory(file, text) {
|
|
|
186
203
|
appendFileSync(file, `${prefix}- ${text.trim()}
|
|
187
204
|
`);
|
|
188
205
|
}
|
|
206
|
+
function parseMemoryArgs(args) {
|
|
207
|
+
const match = /^global(?:\s+|$)/i.exec(args.trim());
|
|
208
|
+
return match ? { global: true, text: args.trim().slice(match[0].length).trim() } : { global: false, text: args.trim() };
|
|
209
|
+
}
|
|
189
210
|
|
|
190
211
|
// src/ui/Markdown.tsx
|
|
191
212
|
import { Box, Text } from "ink";
|
|
@@ -258,7 +279,7 @@ function ListPicker({ title, items, onPick }) {
|
|
|
258
279
|
useInput2((input, key) => {
|
|
259
280
|
if (key.upArrow) setIndex(Math.max(0, cursor - 1));
|
|
260
281
|
else if (key.downArrow) setIndex(Math.min(shown.length - 1, cursor + 1));
|
|
261
|
-
else if (key.return) onPick(shown[cursor]?.id);
|
|
282
|
+
else if (key.return && !shown[cursor]?.disabled) onPick(shown[cursor]?.id);
|
|
262
283
|
else if (key.escape) onPick(void 0);
|
|
263
284
|
else if (key.backspace || key.delete) {
|
|
264
285
|
setFilter((f) => f.slice(0, -1));
|
|
@@ -271,7 +292,7 @@ function ListPicker({ title, items, onPick }) {
|
|
|
271
292
|
const start = Math.max(0, Math.min(cursor - 5, shown.length - VISIBLE));
|
|
272
293
|
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
273
294
|
/* @__PURE__ */ jsx3(Text3, { bold: true, children: `${title} (\u2191\u2193 enter \xB7 ketik untuk menyaring${filter ? `: ${filter}` : ""} \xB7 esc batal)` }),
|
|
274
|
-
shown.slice(start, start + VISIBLE).map((item, i) => /* @__PURE__ */ jsxs2(Text3, { color: start + i === cursor ? color("green") : void 0, children: [
|
|
295
|
+
shown.slice(start, start + VISIBLE).map((item, i) => /* @__PURE__ */ jsxs2(Text3, { color: start + i === cursor && !item.disabled ? color("green") : void 0, dimColor: item.disabled, children: [
|
|
275
296
|
`${start + i === cursor ? "\u203A" : " "} ${item.label}`,
|
|
276
297
|
item.hint ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` ${item.hint}` }) : null
|
|
277
298
|
] }, item.id)),
|
|
@@ -519,9 +540,9 @@ var LABEL = {
|
|
|
519
540
|
function formatTokens(n) {
|
|
520
541
|
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
521
542
|
}
|
|
522
|
-
function StatusBar({ mode, tokens, busy, model }) {
|
|
543
|
+
function StatusBar({ mode, tokens, busy, model, reasoning = "auto" }) {
|
|
523
544
|
const danger = mode === "allowAll";
|
|
524
|
-
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"}` });
|
|
545
|
+
return /* @__PURE__ */ jsx10(Text10, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 reasoning: ${reasoning} \xB7 shift+tab ganti mode \xB7 ${formatTokens(tokens)} token \xB7 ${busy ? "esc batal" : "/ perintah"}` });
|
|
525
546
|
}
|
|
526
547
|
|
|
527
548
|
// src/ui/McpMenu.tsx
|
|
@@ -614,25 +635,55 @@ var NOTICES = {
|
|
|
614
635
|
stepLimit: ['Batas 50 langkah tercapai. Ketik "lanjut" untuk meneruskan.', "warn"],
|
|
615
636
|
aborted: ["Dibatalkan.", "warn"]
|
|
616
637
|
};
|
|
638
|
+
var isEmptyCont = (e) => e.kind === "assistant" && e.cont && !e.text;
|
|
617
639
|
function endTurn(t) {
|
|
618
|
-
return { done: [...t.done, ...t.live], live: [] };
|
|
640
|
+
return { done: [...t.done, ...t.live.filter((e) => !isEmptyCont(e))], live: [] };
|
|
641
|
+
}
|
|
642
|
+
var TOOL_MARKUP = /<\/?tool_call>|<\/?arg_(?:key|value)>|Tool call list|^\s*\d+\.\s*Tool:|`\s*=\s*`/;
|
|
643
|
+
function finishedCut(text) {
|
|
644
|
+
let inFence = false;
|
|
645
|
+
let pos = 0;
|
|
646
|
+
let cut = -1;
|
|
647
|
+
const lines = text.split("\n");
|
|
648
|
+
for (const line of lines.slice(0, -1)) {
|
|
649
|
+
if (TOOL_MARKUP.test(line)) break;
|
|
650
|
+
if (line.trimStart().startsWith("```")) inFence = !inFence;
|
|
651
|
+
pos += line.length + 1;
|
|
652
|
+
if (!inFence) cut = pos - 1;
|
|
653
|
+
}
|
|
654
|
+
return cut;
|
|
655
|
+
}
|
|
656
|
+
function flushLines(t) {
|
|
657
|
+
const last = t.live.at(-1);
|
|
658
|
+
if (last?.kind !== "assistant") return t;
|
|
659
|
+
const cut = finishedCut(last.text);
|
|
660
|
+
if (cut < 0) return t;
|
|
661
|
+
const chunk = last.text.slice(0, cut);
|
|
662
|
+
if (!last.cont && !chunk.trim()) return t;
|
|
663
|
+
const rest = entry({ kind: "assistant", text: last.text.slice(cut + 1), cont: true, flushed: (last.flushed ?? 0) + cut + 1 });
|
|
664
|
+
return { done: [...t.done, ...t.live.slice(0, -1).filter((e) => !isEmptyCont(e)), { ...last, text: chunk || " " }], live: [rest] };
|
|
619
665
|
}
|
|
620
666
|
function applyEvent(t, event) {
|
|
621
667
|
switch (event.type) {
|
|
622
668
|
case "text": {
|
|
623
669
|
const last = t.live.at(-1);
|
|
624
|
-
if (last?.kind === "assistant") return { ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] };
|
|
625
|
-
return { ...t, live: [...t.live, entry({ kind: "assistant", text: event.delta })] };
|
|
670
|
+
if (last?.kind === "assistant") return flushLines({ ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] });
|
|
671
|
+
return flushLines({ ...t, live: [...t.live, entry({ kind: "assistant", text: event.delta })] });
|
|
672
|
+
}
|
|
673
|
+
case "thinking": {
|
|
674
|
+
const last = t.live.at(-1);
|
|
675
|
+
if (last?.kind === "thinking") return { ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] };
|
|
676
|
+
return { ...t, live: [...t.live, entry({ kind: "thinking", text: event.delta })] };
|
|
626
677
|
}
|
|
627
678
|
case "textReplace": {
|
|
628
679
|
const last = t.live.at(-1);
|
|
629
680
|
if (last?.kind !== "assistant") return t;
|
|
630
|
-
return { ...t, live: [...t.live.slice(0, -1), { ...last, text: event.text }] };
|
|
681
|
+
return { ...t, live: [...t.live.slice(0, -1), { ...last, text: event.text.slice(last.flushed ?? 0) }] };
|
|
631
682
|
}
|
|
632
683
|
case "toolStart": {
|
|
633
684
|
const running = t.live.some((e) => e.kind === "tool" && !e.done);
|
|
634
685
|
const tool = entry({ kind: "tool", callId: event.id, tool: event.tool, target: event.target, done: false });
|
|
635
|
-
return running ? { ...t, live: [...t.live, tool] } : { done: [...t.done, ...t.live], live: [tool] };
|
|
686
|
+
return running ? { ...t, live: [...t.live, tool] } : { done: [...t.done, ...t.live.filter((e) => !isEmptyCont(e))], live: [tool] };
|
|
636
687
|
}
|
|
637
688
|
case "toolEnd": {
|
|
638
689
|
const live = t.live.map(
|
|
@@ -673,6 +724,7 @@ function helpText(runtime) {
|
|
|
673
724
|
"",
|
|
674
725
|
"shift+tab ganti mode izin esc batalkan giliran",
|
|
675
726
|
"ctrl+o output alat terakhir lengkap \\ + enter baris baru",
|
|
727
|
+
"ctrl+t buka/tutup thinking model",
|
|
676
728
|
"@file + tab lengkapi nama file \u2191\u2193 riwayat input"
|
|
677
729
|
].join("\n");
|
|
678
730
|
}
|
|
@@ -684,6 +736,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
684
736
|
const [busy, setBusy] = useState8(false);
|
|
685
737
|
const [startedAt, setStartedAt] = useState8(0);
|
|
686
738
|
const [mode, setMode] = useState8(runtime.agent.permissions.mode);
|
|
739
|
+
const [reasoning, setReasoning] = useState8(runtime.reasoning);
|
|
687
740
|
const [tokens, setTokens] = useState8(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
|
|
688
741
|
const [asks, setAsks] = useState8([]);
|
|
689
742
|
const pending = asks[0] ?? null;
|
|
@@ -699,6 +752,8 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
699
752
|
const [mcpMenu, setMcpMenu] = useState8(null);
|
|
700
753
|
const [listPicker, setListPicker] = useState8(null);
|
|
701
754
|
const [planAsk, setPlanAsk] = useState8(null);
|
|
755
|
+
const [queued, setQueued] = useState8([]);
|
|
756
|
+
const [showThinking, setShowThinking] = useState8(false);
|
|
702
757
|
useEffect2(() => runtime.todos.subscribe(setTodos), [runtime]);
|
|
703
758
|
const extraCommands = [
|
|
704
759
|
...runtime.commands.filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name.toLowerCase())).map((c) => ({ name: c.name.toLowerCase(), description: c.description ?? "perintah custom" })),
|
|
@@ -767,6 +822,41 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
767
822
|
setBusy(false);
|
|
768
823
|
return;
|
|
769
824
|
}
|
|
825
|
+
case "reasoning": {
|
|
826
|
+
const apply = (value) => {
|
|
827
|
+
try {
|
|
828
|
+
const level = parseReasoningLevel(value, "/reasoning");
|
|
829
|
+
const providerId2 = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
830
|
+
assertReasoningSupported(providerId2, level);
|
|
831
|
+
runtime.setReasoning(level);
|
|
832
|
+
writeGlobalConfig({ reasoning: level }, runtime.env);
|
|
833
|
+
setReasoning(level);
|
|
834
|
+
notice(`Reasoning: ${level} (tersimpan sebagai default)`);
|
|
835
|
+
} catch (error) {
|
|
836
|
+
notice(error.message, "error");
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
if (args) {
|
|
840
|
+
apply(args);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const providerId = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
844
|
+
const supported = supportedReasoningLevels(providerId);
|
|
845
|
+
setListPicker({
|
|
846
|
+
title: "Reasoning",
|
|
847
|
+
items: REASONING_LEVELS.map((level) => ({
|
|
848
|
+
id: level,
|
|
849
|
+
label: level === "auto" ? "Auto (model default)" : level.charAt(0).toUpperCase() + level.slice(1),
|
|
850
|
+
disabled: !supported.includes(level),
|
|
851
|
+
hint: supported.includes(level) ? void 0 : "tidak didukung provider ini"
|
|
852
|
+
})),
|
|
853
|
+
onPick: (id) => {
|
|
854
|
+
setListPicker(null);
|
|
855
|
+
if (id) apply(id);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
770
860
|
case "model": {
|
|
771
861
|
if (args) {
|
|
772
862
|
try {
|
|
@@ -833,12 +923,24 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
833
923
|
const chosen = others.find((s) => s.session.file === id);
|
|
834
924
|
if (!chosen) return;
|
|
835
925
|
runtime.resume(chosen.session);
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
"\n"
|
|
840
|
-
)
|
|
926
|
+
setTokens(0);
|
|
927
|
+
const chat = runtime.agent.messages.filter(
|
|
928
|
+
(m) => (m.role === "user" || m.role === "assistant") && typeof m.content === "string" && !!m.content.trim()
|
|
841
929
|
);
|
|
930
|
+
setTranscript({
|
|
931
|
+
done: [
|
|
932
|
+
entry({ kind: "header" }),
|
|
933
|
+
entry({
|
|
934
|
+
kind: "notice",
|
|
935
|
+
text: `Melanjutkan sesi (${runtime.agent.messages.length} pesan tersimpan \xB7 ${chat.length} chat user/assistant ditampilkan).`,
|
|
936
|
+
tone: "info"
|
|
937
|
+
}),
|
|
938
|
+
...chat.map(
|
|
939
|
+
(m) => entry(m.role === "user" ? { kind: "user", text: m.content } : { kind: "assistant", text: m.content })
|
|
940
|
+
)
|
|
941
|
+
],
|
|
942
|
+
live: []
|
|
943
|
+
});
|
|
842
944
|
}
|
|
843
945
|
});
|
|
844
946
|
return;
|
|
@@ -874,7 +976,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
874
976
|
activeProvider: runtime.providerLabel()
|
|
875
977
|
},
|
|
876
978
|
{
|
|
877
|
-
which: (cmd) =>
|
|
979
|
+
which: (cmd) => new Promise((done) => execFile2(process.platform === "win32" ? "where" : "which", [cmd], (error) => done(!error))),
|
|
878
980
|
nodeVersion: process.version,
|
|
879
981
|
listModels: () => runtime.agent.provider.listModels()
|
|
880
982
|
}
|
|
@@ -910,8 +1012,11 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
910
1012
|
notice(files.length ? files.map((f) => `${f.path} (${f.lines} baris)`).join("\n") : "Belum ada AGENTS.md / BCCLI.md. Tambah dengan /memory <teks>.");
|
|
911
1013
|
return;
|
|
912
1014
|
}
|
|
913
|
-
const global =
|
|
914
|
-
|
|
1015
|
+
const { global, text } = parseMemoryArgs(args);
|
|
1016
|
+
if (!text) {
|
|
1017
|
+
notice("Tulis teksnya: /memory global <teks>.", "warn");
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
915
1020
|
const file = global ? join(runtime.home, "BCCLI.md") : join(runtime.cwd, "AGENTS.md");
|
|
916
1021
|
try {
|
|
917
1022
|
mkdirSync(dirname(file), { recursive: true });
|
|
@@ -937,15 +1042,27 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
937
1042
|
return;
|
|
938
1043
|
}
|
|
939
1044
|
runtime.setModel(runtime.modelRef);
|
|
1045
|
+
const envName = runtime.config.providers[id]?.apiKeyEnv;
|
|
1046
|
+
if (envName && runtime.env[envName]) {
|
|
1047
|
+
notice(`Key ${runtime.providerLabel()} tersimpan, tapi ${envName} di environment lebih diutamakan dan tetap dipakai.`, "warn");
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
940
1050
|
notice(`Key ${runtime.providerLabel()} tersimpan \xB7 ${result.models} model.`);
|
|
941
1051
|
return;
|
|
942
1052
|
}
|
|
943
1053
|
case "logout": {
|
|
944
1054
|
const id = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
945
|
-
removeCredential(id, runtime.env);
|
|
1055
|
+
const removed = removeCredential(id, runtime.env);
|
|
946
1056
|
const envName = runtime.config.providers[id]?.apiKeyEnv;
|
|
947
1057
|
if (envName && runtime.env[envName]) {
|
|
948
|
-
notice(
|
|
1058
|
+
notice(
|
|
1059
|
+
`${removed ? "Key tersimpan dihapus, tapi" : "Tidak ada key tersimpan;"} ${envName} masih ada di environment dan tetap dipakai.`,
|
|
1060
|
+
"warn"
|
|
1061
|
+
);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
if (!removed) {
|
|
1065
|
+
notice(`Tidak ada key tersimpan untuk ${runtime.providerLabel()}.`);
|
|
949
1066
|
return;
|
|
950
1067
|
}
|
|
951
1068
|
try {
|
|
@@ -996,6 +1113,13 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
996
1113
|
case "export": {
|
|
997
1114
|
const name2 = args || `bccli-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[:T]/g, "-")}.md`;
|
|
998
1115
|
const file = resolve(runtime.cwd, name2);
|
|
1116
|
+
if (existsSync2(file)) {
|
|
1117
|
+
const answer2 = await ask(`${file} sudah ada. Timpa? (y/N)`);
|
|
1118
|
+
if (!answer2 || !/^y(a|es)?$/i.test(answer2.trim())) {
|
|
1119
|
+
notice("Ekspor dibatalkan.");
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
999
1123
|
try {
|
|
1000
1124
|
writeFileSync(file, exportMarkdown(runtime.agent.messages, `Sesi BCCLI ${runtime.startedAt.toLocaleString("id-ID")}`));
|
|
1001
1125
|
} catch (error) {
|
|
@@ -1049,6 +1173,19 @@ ARGUMENTS: ${slash.args}`);
|
|
|
1049
1173
|
},
|
|
1050
1174
|
[runSlash, runTurn, runtime, notice]
|
|
1051
1175
|
);
|
|
1176
|
+
const onPrompt = useCallback(
|
|
1177
|
+
(text) => {
|
|
1178
|
+
if (busy) setQueued((q) => [...q, text]);
|
|
1179
|
+
else submit(text);
|
|
1180
|
+
},
|
|
1181
|
+
[busy, submit]
|
|
1182
|
+
);
|
|
1183
|
+
useEffect2(() => {
|
|
1184
|
+
if (busy || pending || planAsk || !queued.length) return;
|
|
1185
|
+
const [next, ...rest] = queued;
|
|
1186
|
+
setQueued(rest);
|
|
1187
|
+
submit(next);
|
|
1188
|
+
}, [busy, pending, planAsk, queued, submit]);
|
|
1052
1189
|
const initialSent = useRef(false);
|
|
1053
1190
|
useEffect2(() => {
|
|
1054
1191
|
if (initialPrompt && !initialSent.current) {
|
|
@@ -1063,6 +1200,25 @@ ARGUMENTS: ${slash.args}`);
|
|
|
1063
1200
|
setMode(next);
|
|
1064
1201
|
} else if (key.escape && busy && !pending) {
|
|
1065
1202
|
controller.current?.abort();
|
|
1203
|
+
if (queued.length) {
|
|
1204
|
+
setQueued([]);
|
|
1205
|
+
notice(`${queued.length} pesan antrian dibatalkan.`, "warn");
|
|
1206
|
+
}
|
|
1207
|
+
} else if (key.ctrl && input === "t") {
|
|
1208
|
+
const next = !showThinking;
|
|
1209
|
+
setShowThinking(next);
|
|
1210
|
+
setTranscript((t) => {
|
|
1211
|
+
const last = [...t.done, ...t.live].findLast((e) => e.kind === "thinking");
|
|
1212
|
+
const reprint = next && last?.kind === "thinking" && t.done.includes(last);
|
|
1213
|
+
return {
|
|
1214
|
+
...t,
|
|
1215
|
+
done: [
|
|
1216
|
+
...t.done,
|
|
1217
|
+
...reprint ? [entry({ kind: "thinking", text: last.text, open: true })] : [],
|
|
1218
|
+
entry({ kind: "notice", text: next ? "Thinking ditampilkan (ctrl+t untuk menutup)." : "Thinking disembunyikan (ctrl+t untuk membuka).", tone: "info" })
|
|
1219
|
+
]
|
|
1220
|
+
};
|
|
1221
|
+
});
|
|
1066
1222
|
} else if (key.ctrl && input === "o" && lastTool.current) {
|
|
1067
1223
|
const t = lastTool.current;
|
|
1068
1224
|
notice(`\u23BF ${t.tool} ${t.target}
|
|
@@ -1138,11 +1294,26 @@ ${t.output}`);
|
|
|
1138
1294
|
case "user":
|
|
1139
1295
|
return /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: `> ${e.text}` }) }, e.id);
|
|
1140
1296
|
case "assistant":
|
|
1297
|
+
if (e.cont) {
|
|
1298
|
+
return e.text ? /* @__PURE__ */ jsx15(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx15(Markdown, { text: e.text }) }, e.id) : null;
|
|
1299
|
+
}
|
|
1141
1300
|
if (!e.text.trim()) return null;
|
|
1142
1301
|
return /* @__PURE__ */ jsxs10(Box13, { marginTop: 1, children: [
|
|
1143
1302
|
/* @__PURE__ */ jsx15(Text15, { color: color(ACCENT), children: "\u25CF " }),
|
|
1144
1303
|
/* @__PURE__ */ jsx15(Markdown, { text: e.text.replace(/^\s+/, "") })
|
|
1145
1304
|
] }, e.id);
|
|
1305
|
+
case "thinking": {
|
|
1306
|
+
const text = e.text.trim();
|
|
1307
|
+
if (!text) return null;
|
|
1308
|
+
if (!(e.open ?? showThinking)) {
|
|
1309
|
+
const lines = text.split("\n").length;
|
|
1310
|
+
return /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: `\u273B Berpikir \xB7 ${lines} baris \xB7 ctrl+t buka` }) }, e.id);
|
|
1311
|
+
}
|
|
1312
|
+
return /* @__PURE__ */ jsxs10(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
1313
|
+
/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u273B Berpikir (ctrl+t tutup)" }),
|
|
1314
|
+
/* @__PURE__ */ jsx15(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, italic: true, children: text }) })
|
|
1315
|
+
] }, e.id);
|
|
1316
|
+
}
|
|
1146
1317
|
case "tool":
|
|
1147
1318
|
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);
|
|
1148
1319
|
case "notice":
|
|
@@ -1195,8 +1366,9 @@ ${t.output}`);
|
|
|
1195
1366
|
}
|
|
1196
1367
|
) : null,
|
|
1197
1368
|
/* @__PURE__ */ jsx15(TodoList, { items: todos }),
|
|
1198
|
-
/* @__PURE__ */ jsx15(
|
|
1199
|
-
/* @__PURE__ */ jsx15(
|
|
1369
|
+
queued.map((q, i) => /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: ` \u23F3 antri: ${q}` }, `${i}-${q}`)),
|
|
1370
|
+
/* @__PURE__ */ jsx15(PromptInput, { disabled: !!pending || !!picker || !!providerMenu || !!prompt || !!planAsk || !!mcpMenu || !!listPicker, history, cwd: runtime.cwd, onSubmit: onPrompt, extraCommands }),
|
|
1371
|
+
/* @__PURE__ */ jsx15(StatusBar, { mode, tokens, busy, model: modelLabel, reasoning })
|
|
1200
1372
|
] });
|
|
1201
1373
|
}
|
|
1202
1374
|
|
|
@@ -1219,7 +1391,7 @@ async function startInteractive(rt, opts) {
|
|
|
1219
1391
|
if (chosen) rt.resume(chosen.session);
|
|
1220
1392
|
}
|
|
1221
1393
|
}
|
|
1222
|
-
const { serversToStart, setProjectTrust } = await import("./config-
|
|
1394
|
+
const { serversToStart, setProjectTrust } = await import("./config-YUNKPCNB.js");
|
|
1223
1395
|
const plan = serversToStart(rt.home, rt.cwd);
|
|
1224
1396
|
if (plan.needTrust.length) {
|
|
1225
1397
|
const { createInterface } = await import("readline/promises");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botconnector/bccli",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.11",
|
|
4
4
|
"description": "BCCLI — BotConnector AI coding agent for the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"fast-glob": "^3.3.3",
|
|
30
30
|
"ink": "^7.1.1",
|
|
31
31
|
"react": "^19.3.0",
|
|
32
|
+
"yaml": "^2.9.1",
|
|
32
33
|
"zod": "^4.6.5"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|