@botconnector/bccli 0.4.0-beta.1 → 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.
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  HELP_TEXT,
4
4
  parseCliArgs
5
- } from "./chunk-M7INIL2C.js";
5
+ } from "./chunk-UN7QXPCB.js";
6
6
  import "./chunk-ELML4KVQ.js";
7
- import "./chunk-Z5JM76EL.js";
7
+ import "./chunk-RZUJYAQV.js";
8
8
  export {
9
9
  HELP_TEXT,
10
10
  parseCliArgs
@@ -4,13 +4,13 @@ import {
4
4
  providerName,
5
5
  removeGlobalProvider,
6
6
  writeGlobalConfig
7
- } from "./chunk-5C6NQFSI.js";
7
+ } from "./chunk-U7DLMVPZ.js";
8
8
  import {
9
9
  PRESETS,
10
10
  loadConfig,
11
11
  readCredentials,
12
12
  saveCredential
13
- } from "./chunk-Z5JM76EL.js";
13
+ } from "./chunk-RZUJYAQV.js";
14
14
 
15
15
  // src/provider.ts
16
16
  var ProviderError = class extends Error {
@@ -35,8 +35,8 @@ function readSecret(prompt) {
35
35
  });
36
36
  }
37
37
  async function runLogin(providerId, cwd) {
38
- const { runProviderCommand } = await import("./providerCli-SJRCBSRE.js");
39
- const { parseCliArgs } = await import("./args-EAKPBBGX.js");
38
+ const { runProviderCommand } = await import("./providerCli-G3X7KENO.js");
39
+ const { parseCliArgs } = await import("./args-FCLPQ7TO.js");
40
40
  return runProviderCommand(parseCliArgs(["provider", "add", providerId]), {
41
41
  env: process.env,
42
42
  cwd,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigError
4
- } from "./chunk-Z5JM76EL.js";
4
+ } from "./chunk-RZUJYAQV.js";
5
5
 
6
6
  // src/mcp/config.ts
7
7
  import { createHash } from "crypto";
@@ -91,11 +91,12 @@ function resolveModel(config, modelRef, env = process.env) {
91
91
  }
92
92
  function removeCredential(providerId, env = process.env) {
93
93
  const creds = readCredentials(env);
94
- if (!(providerId in creds)) return;
94
+ if (!(providerId in creds)) return false;
95
95
  delete creds[providerId];
96
96
  const path = join(bccliHome(env), "credentials");
97
97
  writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 384 });
98
98
  chmodSync(path, 384);
99
+ return true;
99
100
  }
100
101
 
101
102
  export {
@@ -5,7 +5,7 @@ import {
5
5
  readCredentials,
6
6
  readJsonConfig,
7
7
  removeCredential
8
- } from "./chunk-Z5JM76EL.js";
8
+ } from "./chunk-RZUJYAQV.js";
9
9
 
10
10
  // src/providers.ts
11
11
  import { mkdirSync, writeFileSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigError
4
- } from "./chunk-Z5JM76EL.js";
4
+ } from "./chunk-RZUJYAQV.js";
5
5
 
6
6
  // src/mcp/catalog.ts
7
7
  var CATALOG = [
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ELML4KVQ.js";
5
5
  import {
6
6
  ConfigError
7
- } from "./chunk-Z5JM76EL.js";
7
+ } from "./chunk-RZUJYAQV.js";
8
8
 
9
9
  // src/args.ts
10
10
  import { parseArgs } from "util";
package/dist/cli.js CHANGED
@@ -2,15 +2,15 @@
2
2
  import {
3
3
  HELP_TEXT,
4
4
  parseCliArgs
5
- } from "./chunk-M7INIL2C.js";
5
+ } from "./chunk-UN7QXPCB.js";
6
6
  import {
7
7
  addProviderKey,
8
8
  createProvider,
9
9
  listAllModels
10
- } from "./chunk-5ISYVAXI.js";
10
+ } from "./chunk-2JBRLGKD.js";
11
11
  import {
12
12
  runLogin
13
- } from "./chunk-QH7XXNHU.js";
13
+ } from "./chunk-L7L4RGKG.js";
14
14
  import {
15
15
  BUILTIN_AGENTS,
16
16
  buildSystemPrompt,
@@ -26,7 +26,7 @@ import {
26
26
  } from "./chunk-ELML4KVQ.js";
27
27
  import {
28
28
  providerName
29
- } from "./chunk-5C6NQFSI.js";
29
+ } from "./chunk-U7DLMVPZ.js";
30
30
  import {
31
31
  Session,
32
32
  pruneSessions
@@ -36,7 +36,7 @@ import {
36
36
  bccliHome,
37
37
  loadConfig,
38
38
  resolveModel
39
- } from "./chunk-Z5JM76EL.js";
39
+ } from "./chunk-RZUJYAQV.js";
40
40
 
41
41
  // src/tools/index.ts
42
42
  import { z as z8 } from "zod";
@@ -1173,6 +1173,8 @@ function createRuntime(opts) {
1173
1173
  resume(previous) {
1174
1174
  session = previous;
1175
1175
  agent.load(previous.load());
1176
+ agent.totalUsage = { inputTokens: 0, outputTokens: 0 };
1177
+ todos.set([]);
1176
1178
  checkpoints.clear();
1177
1179
  },
1178
1180
  newSession() {
@@ -1198,7 +1200,7 @@ function createRuntime(opts) {
1198
1200
  }
1199
1201
 
1200
1202
  // src/version.ts
1201
- var VERSION = true ? "0.4.0-beta.1" : "0.0.0-dev";
1203
+ var VERSION = true ? "0.4.0-beta.2" : "0.0.0-dev";
1202
1204
 
1203
1205
  // src/cli.ts
1204
1206
  async function main() {
@@ -1214,13 +1216,13 @@ async function main() {
1214
1216
  const cwd = process.cwd();
1215
1217
  if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd);
1216
1218
  if (args.command === "mcp") {
1217
- const { runMcpCommand } = await import("./mcpCli-BV3IGR47.js");
1218
- const { readSecret } = await import("./login-FTMR64IA.js");
1219
+ const { runMcpCommand } = await import("./mcpCli-Q2H63ISP.js");
1220
+ const { readSecret } = await import("./login-KHU7ONSR.js");
1219
1221
  return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
1220
1222
  }
1221
1223
  if (args.command === "provider") {
1222
- const { runProviderCommand } = await import("./providerCli-SJRCBSRE.js");
1223
- const { readSecret } = await import("./login-FTMR64IA.js");
1224
+ const { runProviderCommand } = await import("./providerCli-G3X7KENO.js");
1225
+ const { readSecret } = await import("./login-KHU7ONSR.js");
1224
1226
  return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
1225
1227
  }
1226
1228
  if (args.command === "models") {
@@ -1237,7 +1239,7 @@ async function main() {
1237
1239
  }
1238
1240
  if (args.print) {
1239
1241
  if (!args.prompt) throw new ConfigError('Mode -p butuh tugas, contoh: bccli -p "jelaskan repo ini"');
1240
- const { serversToStart } = await import("./config-XPWPGNZJ.js");
1242
+ const { serversToStart } = await import("./config-NPSYFAFD.js");
1241
1243
  const plan = serversToStart(rt.home, cwd);
1242
1244
  for (const s of plan.needTrust) {
1243
1245
  console.error(`Server MCP project "${s.name}" dilewati (belum diizinkan; jalankan bccli interaktif sekali untuk menyetujuinya).`);
@@ -1250,7 +1252,7 @@ async function main() {
1250
1252
  }
1251
1253
  }
1252
1254
  if (!process.stdin.isTTY) throw new ConfigError('Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"');
1253
- const { startInteractive } = await import("./ui-LWZ75D7H.js");
1255
+ const { startInteractive } = await import("./ui-A32PTF4F.js");
1254
1256
  return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
1255
1257
  }
1256
1258
  main().then(
@@ -8,8 +8,8 @@ import {
8
8
  removeGlobalServer,
9
9
  serversToStart,
10
10
  setProjectTrust
11
- } from "./chunk-XQLXE666.js";
12
- import "./chunk-Z5JM76EL.js";
11
+ } from "./chunk-NGLKANMD.js";
12
+ import "./chunk-RZUJYAQV.js";
13
13
  export {
14
14
  addGlobalServer,
15
15
  globalMcpPath,
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  readSecret,
4
4
  runLogin
5
- } from "./chunk-QH7XXNHU.js";
5
+ } from "./chunk-L7L4RGKG.js";
6
6
  export {
7
7
  readSecret,
8
8
  runLogin
@@ -2,16 +2,16 @@
2
2
  import {
3
3
  CATALOG,
4
4
  fillTemplate
5
- } from "./chunk-TV6LEHOH.js";
5
+ } from "./chunk-UESQNU4A.js";
6
6
  import {
7
7
  addGlobalServer,
8
8
  globalMcpPath,
9
9
  readMcpFile,
10
10
  removeGlobalServer
11
- } from "./chunk-XQLXE666.js";
11
+ } from "./chunk-NGLKANMD.js";
12
12
  import {
13
13
  bccliHome
14
- } from "./chunk-Z5JM76EL.js";
14
+ } from "./chunk-RZUJYAQV.js";
15
15
 
16
16
  // src/mcpCli.ts
17
17
  async function runMcpCommand(args, deps) {
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  addProviderKey,
4
4
  runProviderCommand
5
- } from "./chunk-5ISYVAXI.js";
6
- import "./chunk-5C6NQFSI.js";
7
- import "./chunk-Z5JM76EL.js";
5
+ } from "./chunk-2JBRLGKD.js";
6
+ import "./chunk-U7DLMVPZ.js";
7
+ import "./chunk-RZUJYAQV.js";
8
8
  export {
9
9
  addProviderKey,
10
10
  runProviderCommand
@@ -13,31 +13,31 @@ import {
13
13
  hasKey,
14
14
  providerName,
15
15
  writeGlobalConfig
16
- } from "./chunk-5C6NQFSI.js";
16
+ } from "./chunk-U7DLMVPZ.js";
17
17
  import {
18
18
  Session
19
19
  } from "./chunk-SJH3YLW4.js";
20
20
  import {
21
21
  CATALOG,
22
22
  fillTemplate
23
- } from "./chunk-TV6LEHOH.js";
23
+ } from "./chunk-UESQNU4A.js";
24
24
  import {
25
25
  addGlobalServer,
26
26
  globalMcpPath,
27
27
  readMcpFile,
28
28
  removeGlobalServer
29
- } from "./chunk-XQLXE666.js";
29
+ } from "./chunk-NGLKANMD.js";
30
30
  import {
31
31
  PRESETS,
32
32
  removeCredential
33
- } from "./chunk-Z5JM76EL.js";
33
+ } from "./chunk-RZUJYAQV.js";
34
34
 
35
35
  // src/ui/index.tsx
36
36
  import { render } from "ink";
37
37
 
38
38
  // src/ui/App.tsx
39
- import { spawnSync } from "child_process";
40
- import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
39
+ import { execFile as execFile2 } from "child_process";
40
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
41
41
  import { homedir } from "os";
42
42
  import { dirname, join, relative, resolve } from "path";
43
43
  import { Box as Box13, Static, Text as Text15, useApp, useInput as useInput9 } from "ink";
@@ -90,13 +90,19 @@ function osc52(text, inTmux) {
90
90
 
91
91
  // src/slash/diff.ts
92
92
  import { execFile } from "child_process";
93
- function git(cwd, args) {
93
+ function git(cwd, args, bin = "git") {
94
94
  return new Promise((resolve2) => {
95
- execFile("git", args, { cwd, maxBuffer: 20 * 1024 * 1024 }, (error, stdout, stderr) => resolve2({ ok: !error, out: error ? stderr : stdout }));
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
+ );
96
101
  });
97
102
  }
98
- async function gitDiff(cwd, maxLines = 400) {
99
- const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"]);
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.";
100
106
  if (!inside.ok || inside.out.trim() !== "true") return "Folder ini bukan repository git, jadi tidak ada diff.";
101
107
  const base = (await git(cwd, ["rev-parse", "--verify", "-q", "HEAD"])).ok ? "HEAD" : "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
102
108
  const [stat, diff, untracked] = await Promise.all([
@@ -117,13 +123,17 @@ async function doctorText(input, deps) {
117
123
  const major = Number(deps.nodeVersion.replace(/^v/, "").split(".")[0]);
118
124
  const lines = [major >= 22 ? `\u2713 Node ${deps.nodeVersion}` : `\u2717 Node ${deps.nodeVersion} (butuh \u2265 22)`];
119
125
  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
- }
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);
125
135
  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}`);
136
+ for (const [i, cmd] of cmds.entries()) lines.push(`${found[i] ? "\u2713" : "\u2717"} ${cmd}`);
127
137
  return lines.join("\n");
128
138
  }
129
139
 
@@ -186,6 +196,10 @@ function appendMemory(file, text) {
186
196
  appendFileSync(file, `${prefix}- ${text.trim()}
187
197
  `);
188
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
+ }
189
203
 
190
204
  // src/ui/Markdown.tsx
191
205
  import { Box, Text } from "ink";
@@ -833,6 +847,7 @@ function App({ runtime, initialPrompt, version }) {
833
847
  const chosen = others.find((s) => s.session.file === id);
834
848
  if (!chosen) return;
835
849
  runtime.resume(chosen.session);
850
+ setTokens(0);
836
851
  const tail = runtime.agent.messages.filter((m) => (m.role === "user" || m.role === "assistant") && m.content).slice(-3);
837
852
  notice(
838
853
  [`Melanjutkan sesi (${runtime.agent.messages.length} pesan):`, ...tail.map((m) => `${m.role === "user" ? ">" : "\u25CF"} ${String(m.content).slice(0, 200)}`)].join(
@@ -874,7 +889,7 @@ function App({ runtime, initialPrompt, version }) {
874
889
  activeProvider: runtime.providerLabel()
875
890
  },
876
891
  {
877
- which: (cmd) => spawnSync(process.platform === "win32" ? "where" : "which", [cmd]).status === 0,
892
+ which: (cmd) => new Promise((done) => execFile2(process.platform === "win32" ? "where" : "which", [cmd], (error) => done(!error))),
878
893
  nodeVersion: process.version,
879
894
  listModels: () => runtime.agent.provider.listModels()
880
895
  }
@@ -910,8 +925,11 @@ function App({ runtime, initialPrompt, version }) {
910
925
  notice(files.length ? files.map((f) => `${f.path} (${f.lines} baris)`).join("\n") : "Belum ada AGENTS.md / BCCLI.md. Tambah dengan /memory <teks>.");
911
926
  return;
912
927
  }
913
- const global = /^global\s/i.test(args);
914
- const text = global ? args.replace(/^global\s+/i, "") : args;
928
+ const { global, text } = parseMemoryArgs(args);
929
+ if (!text) {
930
+ notice("Tulis teksnya: /memory global <teks>.", "warn");
931
+ return;
932
+ }
915
933
  const file = global ? join(runtime.home, "BCCLI.md") : join(runtime.cwd, "AGENTS.md");
916
934
  try {
917
935
  mkdirSync(dirname(file), { recursive: true });
@@ -937,15 +955,27 @@ function App({ runtime, initialPrompt, version }) {
937
955
  return;
938
956
  }
939
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
+ }
940
963
  notice(`Key ${runtime.providerLabel()} tersimpan \xB7 ${result.models} model.`);
941
964
  return;
942
965
  }
943
966
  case "logout": {
944
967
  const id = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
945
- removeCredential(id, runtime.env);
968
+ const removed = removeCredential(id, runtime.env);
946
969
  const envName = runtime.config.providers[id]?.apiKeyEnv;
947
970
  if (envName && runtime.env[envName]) {
948
- notice(`Key tersimpan dihapus, tapi ${envName} masih ada di environment dan tetap dipakai.`, "warn");
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()}.`);
949
979
  return;
950
980
  }
951
981
  try {
@@ -996,6 +1026,13 @@ function App({ runtime, initialPrompt, version }) {
996
1026
  case "export": {
997
1027
  const name2 = args || `bccli-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[:T]/g, "-")}.md`;
998
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
+ }
999
1036
  try {
1000
1037
  writeFileSync(file, exportMarkdown(runtime.agent.messages, `Sesi BCCLI ${runtime.startedAt.toLocaleString("id-ID")}`));
1001
1038
  } catch (error) {
@@ -1219,7 +1256,7 @@ async function startInteractive(rt, opts) {
1219
1256
  if (chosen) rt.resume(chosen.session);
1220
1257
  }
1221
1258
  }
1222
- const { serversToStart, setProjectTrust } = await import("./config-XPWPGNZJ.js");
1259
+ const { serversToStart, setProjectTrust } = await import("./config-NPSYFAFD.js");
1223
1260
  const plan = serversToStart(rt.home, rt.cwd);
1224
1261
  if (plan.needTrust.length) {
1225
1262
  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.2",
4
4
  "description": "BCCLI — BotConnector AI coding agent for the terminal",
5
5
  "type": "module",
6
6
  "bin": {