@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.
@@ -5,7 +5,7 @@ import {
5
5
  instructionPaths,
6
6
  parseFrontmatter,
7
7
  toolLabel
8
- } from "./chunk-OSWQPSA4.js";
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-5C6NQFSI.js";
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-TV6LEHOH.js";
29
+ } from "./chunk-DX7CZTBB.js";
24
30
  import {
25
31
  addGlobalServer,
26
32
  globalMcpPath,
27
33
  readMcpFile,
28
34
  removeGlobalServer
29
- } from "./chunk-XQLXE666.js";
35
+ } from "./chunk-BFZXRKW5.js";
30
36
  import {
31
37
  PRESETS,
32
38
  removeCredential
33
- } from "./chunk-Z5JM76EL.js";
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 { spawnSync } from "child_process";
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("git", args, { cwd, maxBuffer: 20 * 1024 * 1024 }, (error, stdout, stderr) => resolve2({ ok: !error, out: error ? stderr : stdout }));
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
- try {
121
- lines.push(`\u2713 Koneksi ${input.activeProvider} (${(await deps.listModels()).length} model)`);
122
- } catch (error) {
123
- lines.push(`\u2717 Koneksi ${input.activeProvider}: ${error.message}`);
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 cmd of ["rg", "git", "npx", "uvx"]) lines.push(`${deps.which(cmd) ? "\u2713" : "\u2717"} ${cmd}`);
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
- const tail = runtime.agent.messages.filter((m) => (m.role === "user" || m.role === "assistant") && m.content).slice(-3);
837
- notice(
838
- [`Melanjutkan sesi (${runtime.agent.messages.length} pesan):`, ...tail.map((m) => `${m.role === "user" ? ">" : "\u25CF"} ${String(m.content).slice(0, 200)}`)].join(
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) => spawnSync(process.platform === "win32" ? "where" : "which", [cmd]).status === 0,
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 = /^global\s/i.test(args);
914
- const text = global ? args.replace(/^global\s+/i, "") : args;
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(`Key tersimpan dihapus, tapi ${envName} masih ada di environment dan tetap dipakai.`, "warn");
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(PromptInput, { disabled: busy || !!pending || !!picker || !!providerMenu || !!prompt || !!planAsk || !!mcpMenu || !!listPicker, history, cwd: runtime.cwd, onSubmit: submit, extraCommands }),
1199
- /* @__PURE__ */ jsx15(StatusBar, { mode, tokens, busy, model: modelLabel })
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-XPWPGNZJ.js");
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.1",
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": {
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- addProviderKey,
4
- runProviderCommand
5
- } from "./chunk-5ISYVAXI.js";
6
- import "./chunk-5C6NQFSI.js";
7
- import "./chunk-Z5JM76EL.js";
8
- export {
9
- addProviderKey,
10
- runProviderCommand
11
- };