@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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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.
|
|
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-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ?
|
|
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 =
|
|
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.
|
|
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
|
}
|