@botconnector/bccli 0.4.0-beta.28 → 0.4.0-beta.29

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.
@@ -247,10 +247,37 @@ function ignorePatterns(root) {
247
247
  return patterns;
248
248
  }
249
249
 
250
+ // src/tools/globSafety.ts
251
+ var MAX_GLOB_LENGTH = 4096;
252
+ var MAX_BRACE_DEPTH = 16;
253
+ function validateGlobPattern(pattern) {
254
+ if (pattern.length > MAX_GLOB_LENGTH) return `Glob pattern is too long (max ${MAX_GLOB_LENGTH} characters).`;
255
+ let depth = 0;
256
+ let escaped = false;
257
+ for (const char of pattern) {
258
+ if (escaped) {
259
+ escaped = false;
260
+ continue;
261
+ }
262
+ if (char === "\\") {
263
+ escaped = true;
264
+ continue;
265
+ }
266
+ if (char === "{") {
267
+ depth++;
268
+ if (depth > MAX_BRACE_DEPTH) return `Glob brace nesting is too deep (max ${MAX_BRACE_DEPTH}).`;
269
+ } else if (char === "}" && depth > 0) {
270
+ depth--;
271
+ }
272
+ }
273
+ return void 0;
274
+ }
275
+
250
276
  export {
251
277
  toolLabel,
252
278
  runPrint,
253
279
  ignorePatterns,
280
+ validateGlobPattern,
254
281
  BUILTIN_AGENTS,
255
282
  instructionPaths,
256
283
  buildSystemPrompt,
package/dist/cli.js CHANGED
@@ -7,8 +7,9 @@ import {
7
7
  loadCommands,
8
8
  loadSkills,
9
9
  parseFrontmatter,
10
- runPrint
11
- } from "./chunk-HP3J6JEY.js";
10
+ runPrint,
11
+ validateGlobPattern
12
+ } from "./chunk-YPD4D6E3.js";
12
13
  import {
13
14
  helpText,
14
15
  parseCliArgs
@@ -570,7 +571,7 @@ async function loadPage(inputUrl, signal) {
570
571
  var fetchTool = createFetchTool();
571
572
 
572
573
  // src/tools/glob.ts
573
- import fg from "fast-glob";
574
+ import { glob as nativeGlob } from "fs/promises";
574
575
  import { z as z4 } from "zod";
575
576
  var MAX_OUTPUT_CHARS = 24e3;
576
577
  var globTool = defineTool({
@@ -583,8 +584,14 @@ var globTool = defineTool({
583
584
  kind: "read",
584
585
  target: (input) => input.pattern,
585
586
  async run(input, ctx) {
587
+ const invalid = validateGlobPattern(input.pattern);
588
+ if (invalid) return { output: invalid, isError: true };
586
589
  const root = resolvePath(ctx.cwd, input.path ?? ".");
587
- const files = (await fg(input.pattern, { cwd: root, ignore: ignorePatterns(root), onlyFiles: true, dot: true })).sort();
590
+ const files = [];
591
+ for await (const file of nativeGlob(input.pattern, { cwd: root, exclude: ignorePatterns(root) })) {
592
+ files.push(file);
593
+ }
594
+ files.sort();
588
595
  if (!files.length) return { output: "No matching files." };
589
596
  const first = files.slice(0, 500);
590
597
  const shown = [];
@@ -606,13 +613,16 @@ var globTool = defineTool({
606
613
  // src/tools/grep.ts
607
614
  import { execFile } from "child_process";
608
615
  import { readFile as readFile2 } from "fs/promises";
609
- import fg2 from "fast-glob";
616
+ import { glob as nativeGlob2 } from "fs/promises";
610
617
  import { z as z5 } from "zod";
611
618
  var MAX_MATCHES = 200;
612
619
  var MAX_OUTPUT_CHARS2 = 24e3;
613
620
  async function grepJs(pattern, opts) {
614
621
  const regex = new RegExp(pattern, opts.ignoreCase ? "i" : "");
615
- const files = await fg2(opts.glob ?? "**/*", { cwd: opts.root, ignore: ignorePatterns(opts.root), onlyFiles: true, dot: true });
622
+ const files = [];
623
+ for await (const file of nativeGlob2(opts.glob ?? "**/*", { cwd: opts.root, exclude: ignorePatterns(opts.root) })) {
624
+ files.push(file);
625
+ }
616
626
  const out = [];
617
627
  for (const file of files.sort()) {
618
628
  let text;
@@ -657,6 +667,10 @@ var grepTool = defineTool({
657
667
  kind: "read",
658
668
  target: (input) => input.pattern,
659
669
  async run(input, ctx) {
670
+ if (input.glob) {
671
+ const invalid = validateGlobPattern(input.glob);
672
+ if (invalid) return { output: invalid, isError: true };
673
+ }
660
674
  const opts = { root: resolvePath(ctx.cwd, input.path ?? "."), glob: input.glob, ignoreCase: input.ignore_case };
661
675
  try {
662
676
  new RegExp(input.pattern);
@@ -1662,7 +1676,7 @@ function createRuntime(opts) {
1662
1676
  }
1663
1677
 
1664
1678
  // src/version.ts
1665
- var VERSION = true ? "0.4.0-beta.28" : "0.0.0-dev";
1679
+ var VERSION = true ? "0.4.0-beta.29" : "0.0.0-dev";
1666
1680
 
1667
1681
  // src/cli.ts
1668
1682
  function chooseLanguage(argv) {
@@ -1733,7 +1747,7 @@ async function main() {
1733
1747
  }
1734
1748
  }
1735
1749
  if (!process.stdin.isTTY) throw new ConfigError(t('Interactive mode needs a terminal. For scripts/CI use: bccli -p "task"'));
1736
- const { startInteractive } = await import("./ui-SRXPL7KU.js");
1750
+ const { startInteractive } = await import("./ui-H3IDVNE7.js");
1737
1751
  return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
1738
1752
  }
1739
1753
  main().then(
@@ -4,8 +4,9 @@ import {
4
4
  ignorePatterns,
5
5
  instructionPaths,
6
6
  parseFrontmatter,
7
- toolLabel
8
- } from "./chunk-HP3J6JEY.js";
7
+ toolLabel,
8
+ validateGlobPattern
9
+ } from "./chunk-YPD4D6E3.js";
9
10
  import {
10
11
  nextMode
11
12
  } from "./chunk-ELML4KVQ.js";
@@ -50,7 +51,7 @@ import { render } from "ink";
50
51
  import { execFile as execFile2 } from "child_process";
51
52
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
52
53
  import { homedir } from "os";
53
- import { dirname, join, relative, resolve } from "path";
54
+ import { dirname, join as join2, relative, resolve } from "path";
54
55
  import { Box as Box13, Static, Text as Text15, useApp, useInput as useInput9 } from "ink";
55
56
  import { useCallback, useEffect as useEffect2, useRef, useState as useState8 } from "react";
56
57
 
@@ -801,13 +802,23 @@ import { Box as Box8, Text as Text8, useInput as useInput6 } from "ink";
801
802
  import { useState as useState5 } from "react";
802
803
 
803
804
  // src/ui/complete.ts
804
- import fg from "fast-glob";
805
+ import { globSync } from "fs";
806
+ import { statSync } from "fs";
807
+ import { join } from "path";
805
808
  function completeFile(value, cwd) {
806
809
  const match = /(^|\s)@([^\s]*)$/.exec(value);
807
810
  if (!match) return value;
808
811
  const prefix = match[2];
809
812
  const escaped = prefix.replace(/([*?[\]{}()!])/g, "\\$1");
810
- const found = fg.sync(`${escaped}*`, { cwd, ignore: ignorePatterns(cwd), onlyFiles: false, markDirectories: true, dot: false }).sort();
813
+ const pattern = `${escaped}*`;
814
+ if (validateGlobPattern(pattern)) return value;
815
+ const found = globSync(pattern, { cwd, exclude: ignorePatterns(cwd) }).map((entry2) => {
816
+ try {
817
+ return statSync(join(cwd, entry2)).isDirectory() ? `${entry2}/` : entry2;
818
+ } catch {
819
+ return entry2;
820
+ }
821
+ }).sort();
811
822
  if (!found.length) return value;
812
823
  const pick = found[0];
813
824
  return `${value.slice(0, value.length - prefix.length)}${pick}${pick.endsWith("/") ? "" : " "}`;
@@ -1403,7 +1414,7 @@ function App({ runtime, initialPrompt, version }) {
1403
1414
  notice(t("Write the text: /memory global <text>."), "warn");
1404
1415
  return;
1405
1416
  }
1406
- const file = global ? join(runtime.home, "BCCLI.md") : join(runtime.cwd, "AGENTS.md");
1417
+ const file = global ? join2(runtime.home, "BCCLI.md") : join2(runtime.cwd, "AGENTS.md");
1407
1418
  try {
1408
1419
  mkdirSync(dirname(file), { recursive: true });
1409
1420
  appendMemory(file, text);
@@ -1509,7 +1520,7 @@ function App({ runtime, initialPrompt, version }) {
1509
1520
  return;
1510
1521
  }
1511
1522
  process.stdout.write(osc52(last.content, !!process.env.TMUX));
1512
- const file = join(runtime.home, "last-answer.md");
1523
+ const file = join2(runtime.home, "last-answer.md");
1513
1524
  mkdirSync(runtime.home, { recursive: true });
1514
1525
  writeFileSync(file, last.content);
1515
1526
  notice(t("Copied to the clipboard (if the terminal supports OSC 52) and saved to {file}.", { file }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botconnector/bccli",
3
- "version": "0.4.0-beta.28",
3
+ "version": "0.4.0-beta.29",
4
4
  "description": "BCCLI — BotConnector AI coding agent for the terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,7 +26,6 @@
26
26
  "dependencies": {
27
27
  "@modelcontextprotocol/sdk": "^1.30.1",
28
28
  "diff": "^9.0.0",
29
- "fast-glob": "^3.3.3",
30
29
  "ink": "^7.1.1",
31
30
  "openai": "^7.27.0",
32
31
  "react": "^19.3.0",
@@ -43,5 +42,8 @@
43
42
  "tsup": "^8.5.1",
44
43
  "typescript": "^7.0.2",
45
44
  "vitest": "^5.0.2"
45
+ },
46
+ "overrides": {
47
+ "esbuild": "0.28.2"
46
48
  }
47
49
  }