@botconnector/bccli 0.3.0-beta.1 → 0.3.0-beta.3

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.
@@ -21,6 +21,21 @@ var ProviderError = class extends Error {
21
21
  status;
22
22
  };
23
23
  var retryable = (status) => status === 429 || status >= 500;
24
+ function detectRepetition(text) {
25
+ const tail = text.slice(-600);
26
+ for (let len = 1; len <= 12; len++) {
27
+ const unit = tail.slice(-len);
28
+ if (unit.length < len || !unit.trim()) continue;
29
+ let i = tail.length;
30
+ let count = 0;
31
+ while (i >= len && tail.slice(i - len, i) === unit) {
32
+ count++;
33
+ i -= len;
34
+ }
35
+ if (count >= 20 && count * len >= 300) return text.length - count * len;
36
+ }
37
+ return -1;
38
+ }
24
39
  function sleep(ms, signal) {
25
40
  return new Promise((resolve, reject) => {
26
41
  const timer = setTimeout(resolve, ms);
@@ -52,6 +67,7 @@ async function readStream(res, onText) {
52
67
  let current;
53
68
  const decoder = new TextDecoder();
54
69
  let buffer = "";
70
+ let loopAt = -1;
55
71
  const handle = (line) => {
56
72
  if (!line.startsWith("data:")) return;
57
73
  const data = line.slice(5).trim();
@@ -71,6 +87,7 @@ async function readStream(res, onText) {
71
87
  if (delta.content) {
72
88
  text += delta.content;
73
89
  onText?.(delta.content);
90
+ if (loopAt < 0) loopAt = detectRepetition(text);
74
91
  }
75
92
  for (const tc of delta.tool_calls ?? []) {
76
93
  let target = tc.index !== void 0 ? byIndex.get(tc.index) : current;
@@ -88,11 +105,12 @@ async function readStream(res, onText) {
88
105
  for await (const chunk of res.body) {
89
106
  buffer += decoder.decode(chunk, { stream: true });
90
107
  let newline = buffer.indexOf("\n");
91
- while (newline >= 0) {
108
+ while (newline >= 0 && loopAt < 0) {
92
109
  handle(buffer.slice(0, newline).trim());
93
110
  buffer = buffer.slice(newline + 1);
94
111
  newline = buffer.indexOf("\n");
95
112
  }
113
+ if (loopAt >= 0) return { text: text.slice(0, loopAt), toolCalls: [], usage, finishReason: "repetition" };
96
114
  }
97
115
  handle(buffer.trim());
98
116
  const toolCalls = calls.map((call, index) => ({ ...call, id: call.id || `call_${index}` }));
@@ -35,7 +35,7 @@ function readSecret(prompt) {
35
35
  });
36
36
  }
37
37
  async function runLogin(providerId, cwd) {
38
- const { runProviderCommand } = await import("./providerCli-7QNAYIFT.js");
38
+ const { runProviderCommand } = await import("./providerCli-SJRCBSRE.js");
39
39
  const { parseCliArgs } = await import("./args-EVIFNGPQ.js");
40
40
  return runProviderCommand(parseCliArgs(["provider", "add", providerId]), {
41
41
  env: process.env,
package/dist/cli.js CHANGED
@@ -7,10 +7,10 @@ import {
7
7
  addProviderKey,
8
8
  createProvider,
9
9
  listAllModels
10
- } from "./chunk-MANM7HY6.js";
10
+ } from "./chunk-5ISYVAXI.js";
11
11
  import {
12
12
  runLogin
13
- } from "./chunk-E4BWUMQ2.js";
13
+ } from "./chunk-R37QWDTL.js";
14
14
  import {
15
15
  Session,
16
16
  pruneSessions
@@ -624,6 +624,14 @@ ${completion.text}` });
624
624
  }))
625
625
  } : {}
626
626
  });
627
+ if (completion.finishReason === "repetition") {
628
+ this.onEvent({ type: "textReplace", text: completion.text });
629
+ this.onEvent({
630
+ type: "error",
631
+ message: "Model terjebak mengulang teks, jawaban dihentikan. Coba ulangi atau ganti model dengan /model."
632
+ });
633
+ return;
634
+ }
627
635
  if (!completion.toolCalls.length) {
628
636
  this.onEvent({ type: "done" });
629
637
  return;
@@ -715,6 +723,7 @@ Model ini sepertinya tidak mendukung tool calling. Coba model lain dengan /model
715
723
  ...this.opts.label ? { agent: this.opts.label } : {}
716
724
  });
717
725
  if (answer === "session") this.permissions.allowForSession(request);
726
+ if (answer === "all") this.permissions.mode = "allowAll";
718
727
  decision = answer === "no" ? "deny" : "allow";
719
728
  }
720
729
  if (decision === "deny") {
@@ -1182,7 +1191,7 @@ function createRuntime(opts) {
1182
1191
  }
1183
1192
 
1184
1193
  // src/version.ts
1185
- var VERSION = true ? "0.3.0-beta.1" : "0.0.0-dev";
1194
+ var VERSION = true ? "0.3.0-beta.3" : "0.0.0-dev";
1186
1195
 
1187
1196
  // src/cli.ts
1188
1197
  async function main() {
@@ -1199,12 +1208,12 @@ async function main() {
1199
1208
  if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd);
1200
1209
  if (args.command === "mcp") {
1201
1210
  const { runMcpCommand } = await import("./mcpCli-BV3IGR47.js");
1202
- const { readSecret } = await import("./login-IXJD5O7Z.js");
1211
+ const { readSecret } = await import("./login-WJMGVDRM.js");
1203
1212
  return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
1204
1213
  }
1205
1214
  if (args.command === "provider") {
1206
- const { runProviderCommand } = await import("./providerCli-7QNAYIFT.js");
1207
- const { readSecret } = await import("./login-IXJD5O7Z.js");
1215
+ const { runProviderCommand } = await import("./providerCli-SJRCBSRE.js");
1216
+ const { readSecret } = await import("./login-WJMGVDRM.js");
1208
1217
  return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
1209
1218
  }
1210
1219
  if (args.command === "models") {
@@ -1234,7 +1243,7 @@ async function main() {
1234
1243
  }
1235
1244
  }
1236
1245
  if (!process.stdin.isTTY) throw new ConfigError('Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"');
1237
- const { startInteractive } = await import("./ui-6PGC4T6N.js");
1246
+ const { startInteractive } = await import("./ui-HAW5NMFA.js");
1238
1247
  return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
1239
1248
  }
1240
1249
  main().then(
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  readSecret,
4
4
  runLogin
5
- } from "./chunk-E4BWUMQ2.js";
5
+ } from "./chunk-R37QWDTL.js";
6
6
  export {
7
7
  readSecret,
8
8
  runLogin
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  addProviderKey,
4
4
  runProviderCommand
5
- } from "./chunk-MANM7HY6.js";
5
+ } from "./chunk-5ISYVAXI.js";
6
6
  import "./chunk-5C6NQFSI.js";
7
7
  import "./chunk-Z5JM76EL.js";
8
8
  export {
@@ -209,6 +209,7 @@ function PermissionPrompt({
209
209
  const k = input.toLowerCase();
210
210
  if (k === "y") onAnswer("yes");
211
211
  else if (k === "a" && request.sessionRules?.length) onAnswer("session");
212
+ else if (k === "s") onAnswer("all");
212
213
  else if (k === "n" || key.escape) onAnswer("no");
213
214
  });
214
215
  const rules = request.sessionRules ?? [];
@@ -220,6 +221,7 @@ function PermissionPrompt({
220
221
  /* @__PURE__ */ jsx6(Text6, { color: color("green"), children: "[y] ya" }),
221
222
  " ",
222
223
  sessionLabel ? /* @__PURE__ */ jsx6(Text6, { color: color("cyan"), children: `[a] ${sessionLabel} ` }) : null,
224
+ /* @__PURE__ */ jsx6(Text6, { color: color("yellow"), children: "[s] ya semua " }),
223
225
  /* @__PURE__ */ jsx6(Text6, { color: color("red"), children: "[n] tidak" })
224
226
  ] })
225
227
  ] });
@@ -247,23 +249,51 @@ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
247
249
  function PromptInput({ disabled, history, cwd, onSubmit, extraCommands }) {
248
250
  const [value, setValue] = useState4("");
249
251
  const [historyIndex, setHistoryIndex] = useState4(null);
252
+ const [selected, setSelected] = useState4(0);
253
+ const all = [...SLASH_COMMANDS, ...(extraCommands ?? []).filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name))];
254
+ const suggestions = value.startsWith("/") && !/\s/.test(value) ? all.filter((c) => c.name.startsWith(value.slice(1))).slice(0, 8) : [];
255
+ const highlighted = Math.min(selected, Math.max(0, suggestions.length - 1));
256
+ const edit = (next) => {
257
+ setValue(next);
258
+ setSelected(0);
259
+ };
250
260
  useInput5(
251
261
  (input, key) => {
262
+ if (suggestions.length) {
263
+ if (key.upArrow) {
264
+ setSelected(Math.max(0, highlighted - 1));
265
+ return;
266
+ }
267
+ if (key.downArrow) {
268
+ setSelected(Math.min(suggestions.length - 1, highlighted + 1));
269
+ return;
270
+ }
271
+ if (key.tab && !key.shift) {
272
+ edit(`/${suggestions[highlighted].name} `);
273
+ return;
274
+ }
275
+ if (key.return) {
276
+ edit("");
277
+ setHistoryIndex(null);
278
+ onSubmit(`/${suggestions[highlighted].name}`);
279
+ return;
280
+ }
281
+ }
252
282
  if (key.return) {
253
283
  if (value.endsWith("\\")) {
254
- setValue(`${value.slice(0, -1)}
284
+ edit(`${value.slice(0, -1)}
255
285
  `);
256
286
  return;
257
287
  }
258
288
  const text = value.trim();
259
289
  if (!text) return;
260
- setValue("");
290
+ edit("");
261
291
  setHistoryIndex(null);
262
292
  onSubmit(text);
263
293
  return;
264
294
  }
265
295
  if (key.backspace || key.delete) {
266
- setValue((v) => v.slice(0, -1));
296
+ edit((v) => v.slice(0, -1));
267
297
  return;
268
298
  }
269
299
  if (key.upArrow) {
@@ -281,23 +311,21 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands }) {
281
311
  return;
282
312
  }
283
313
  if (key.tab && !key.shift) {
284
- setValue((v) => completeFile(v, cwd));
314
+ edit((v) => completeFile(v, cwd));
285
315
  return;
286
316
  }
287
317
  if (key.ctrl || key.meta || key.escape || key.tab || key.leftArrow || key.rightArrow) return;
288
- setValue((v) => v + input.replace(/\r/g, "\n"));
318
+ edit((v) => v + input.replace(/\r/g, "\n"));
289
319
  },
290
320
  { isActive: !disabled }
291
321
  );
292
- const all = [...SLASH_COMMANDS, ...(extraCommands ?? []).filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name))];
293
- const suggestions = value.startsWith("/") && !/\s/.test(value) ? all.filter((c) => c.name.startsWith(value.slice(1))).slice(0, 8) : [];
294
322
  return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", children: [
295
323
  /* @__PURE__ */ jsx7(Box7, { borderStyle: "round", borderColor: color("gray"), paddingX: 1, children: /* @__PURE__ */ jsxs5(Text7, { children: [
296
324
  /* @__PURE__ */ jsx7(Text7, { color: color("green"), children: "> " }),
297
325
  value,
298
326
  disabled ? "" : /* @__PURE__ */ jsx7(Text7, { inverse: true, children: " " })
299
327
  ] }) }),
300
- suggestions.map((s) => /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: ` /${s.name} ${s.description}` }, s.name))
328
+ suggestions.map((s, i) => /* @__PURE__ */ jsx7(Text7, { color: i === highlighted ? color("green") : void 0, dimColor: i !== highlighted, children: `${i === highlighted ? "\u203A" : " "} /${s.name} ${s.description}` }, s.name))
301
329
  ] });
302
330
  }
303
331
 
@@ -433,6 +461,11 @@ function applyEvent(t, event) {
433
461
  if (last?.kind === "assistant") return { ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] };
434
462
  return { ...t, live: [...t.live, entry({ kind: "assistant", text: event.delta })] };
435
463
  }
464
+ case "textReplace": {
465
+ const last = t.live.at(-1);
466
+ if (last?.kind !== "assistant") return t;
467
+ return { ...t, live: [...t.live.slice(0, -1), { ...last, text: event.text }] };
468
+ }
436
469
  case "toolStart": {
437
470
  const running = t.live.some((e) => e.kind === "tool" && !e.done);
438
471
  const tool = entry({ kind: "tool", callId: event.id, tool: event.tool, target: event.target, done: false });
@@ -716,6 +749,7 @@ ${t.output}`);
716
749
  const answer = (a) => {
717
750
  pending?.resolve(a);
718
751
  setAsks((q) => q.slice(1));
752
+ if (a === "all") setMode("allowAll");
719
753
  };
720
754
  const cwd = runtime.cwd.startsWith(homedir()) ? `~${runtime.cwd.slice(homedir().length)}` : runtime.cwd;
721
755
  const renderEntry = (e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botconnector/bccli",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.0-beta.3",
4
4
  "description": "BCCLI — BotConnector AI coding agent for the terminal",
5
5
  "type": "module",
6
6
  "bin": {