@botconnector/bccli 0.4.1 → 0.4.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.
- package/dist/{acp-3PHBAI64.js → acp-UYC6APR5.js} +30 -6
- package/dist/acp.d.ts +1 -0
- package/dist/agent.d.ts +5 -1
- package/dist/args-NRTZWWEV.js +13 -0
- package/dist/args.d.ts +2 -0
- package/dist/checkpoints.d.ts +1 -0
- package/dist/{chunk-BKGFA2PT.js → chunk-266SY3MC.js} +1 -1
- package/dist/{chunk-MFHCQQEN.js → chunk-3I5BW3ZT.js} +3 -2
- package/dist/{chunk-UZWJQF67.js → chunk-3WHI4LN3.js} +2 -1
- package/dist/{chunk-WNACAUDK.js → chunk-5YJ3ZQVR.js} +66 -9
- package/dist/{chunk-24JN75FZ.js → chunk-6M55UPES.js} +2 -2
- package/dist/{chunk-XV7ZVL4J.js → chunk-6UO5GX55.js} +26 -12
- package/dist/{chunk-R5RE46CW.js → chunk-C64KM37J.js} +36 -6
- package/dist/{chunk-GN4W3T6E.js → chunk-H7XJDKJY.js} +4 -4
- package/dist/{chunk-JHPGOAXU.js → chunk-HQW4BROS.js} +4 -5
- package/dist/{chunk-75BECX4F.js → chunk-IYR5PYA3.js} +4 -4
- package/dist/{chunk-45SDLMTV.js → chunk-JXM6REER.js} +1 -1
- package/dist/{chunk-BLOYFIAD.js → chunk-KPTML72Q.js} +1 -1
- package/dist/{chunk-WI22M2ID.js → chunk-NQOGVP3I.js} +2 -2
- package/dist/{chunk-QZKLZELZ.js → chunk-OK2USAXX.js} +28 -4
- package/dist/{chunk-PZRKU4UM.js → chunk-R72OV7RF.js} +421 -108
- package/dist/{chunk-FLQPE4S5.js → chunk-RX4UQX43.js} +4 -1
- package/dist/{chunk-BC7UXFE4.js → chunk-SVD4LHJC.js} +64 -3
- package/dist/{chunk-BXQQCUAC.js → chunk-TIMSYZIR.js} +7 -7
- package/dist/{chunk-MHIYLXJC.js → chunk-WFZKJY3R.js} +8 -4
- package/dist/{chunk-BGXNR74O.js → chunk-ZEPTUKNW.js} +119 -31
- package/dist/cli.js +60 -43
- package/dist/{completion-HPPFDAPB.js → completion-D2BSBTQO.js} +1 -1
- package/dist/{config-DIDLDONU.js → config-ENDRMOTP.js} +3 -3
- package/dist/{config-IU76CPK2.js → config-LAINORU5.js} +2 -2
- package/dist/config.d.ts +2 -0
- package/dist/{ghBotCli-E3VPHNZY.js → ghBotCli-TSLRORJJ.js} +21 -20
- package/dist/{integrationCli-Q2YPDTPG.js → integrationCli-HXCSEWH2.js} +2 -2
- package/dist/{login-FP3BB44A.js → login-RYESFZG6.js} +4 -4
- package/dist/lsp.d.ts +3 -0
- package/dist/mcp/manager.d.ts +18 -0
- package/dist/{mcpCli-SNBDUWJN.js → mcpCli-SETTCL3K.js} +6 -6
- package/dist/{modelsCli-CZ2HNGKR.js → modelsCli-OP7CDZC7.js} +6 -6
- package/dist/{oauthLogin-6FWQABJ7.js → oauthLogin-XWY7OHCG.js} +1 -1
- package/dist/{pluginsCli-LPIUUDWB.js → pluginsCli-ZQNKWZHJ.js} +2 -2
- package/dist/{prCli-5IMV3VNQ.js → prCli-6PUWTMTP.js} +20 -4
- package/dist/prCli.d.ts +13 -0
- package/dist/print.d.ts +2 -0
- package/dist/provider.d.ts +5 -0
- package/dist/providerCli-5OZI2GQQ.js +15 -0
- package/dist/sdk.d.ts +5 -1
- package/dist/sdk.js +733 -176
- package/dist/{serve-X2ZV4S5U.js → serve-4J7HA4JO.js} +17 -17
- package/dist/{serve-KWOECK5W.js → serve-K5AH4MGO.js} +3 -3
- package/dist/session-YQXCCKXI.js +12 -0
- package/dist/session.d.ts +3 -0
- package/dist/setup-S4AJT6XE.js +24 -0
- package/dist/slash/info.d.ts +1 -0
- package/dist/{statsCli-CF3R7ATL.js → statsCli-XR6BEZQQ.js} +2 -2
- package/dist/tokenBudget.d.ts +1 -1
- package/dist/tools/edit.d.ts +7 -2
- package/dist/tools/grep.d.ts +14 -2
- package/dist/tools/ignore.d.ts +2 -2
- package/dist/tools/paths.d.ts +2 -0
- package/dist/{transferCli-YUFPI6AH.js → transferCli-R2KEIWQR.js} +8 -6
- package/dist/ui/complete.d.ts +5 -0
- package/dist/ui/vim.d.ts +1 -1
- package/dist/{ui-IVIFNUCC.js → ui-36ZEK65V.js} +177 -54
- package/dist/{update-TZ2G6KQ3.js → update-FSS7X5WV.js} +2 -2
- package/package.json +1 -1
- package/dist/args-W6BNKY7K.js +0 -13
- package/dist/providerCli-NMUFXAAS.js +0 -15
- package/dist/session-GKEXDJKZ.js +0 -12
- package/dist/setup-OV3NWQN5.js +0 -24
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
t
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3WHI4LN3.js";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
7
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
@@ -236,8 +236,11 @@ function loadConfig(cwd, env = process.env) {
|
|
|
236
236
|
const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
|
|
237
237
|
const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
|
|
238
238
|
const fromProject = untrustedProviders(project, known);
|
|
239
|
+
const rawFallbacks = project.fallbackModels ?? global.fallbackModels;
|
|
240
|
+
const fallbackModels = Array.isArray(rawFallbacks) ? rawFallbacks.filter((m) => typeof m === "string" && m.includes("/")) : void 0;
|
|
239
241
|
return {
|
|
240
242
|
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
243
|
+
...fallbackModels?.length ? { fallbackModels } : {},
|
|
241
244
|
permissionMode: global.permissionMode ?? DEFAULT_CONFIG.permissionMode,
|
|
242
245
|
reasoning: global.reasoning ?? DEFAULT_CONFIG.reasoning,
|
|
243
246
|
thinkingVisible: global.thinkingVisible === true,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
contentText
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5YJ3ZQVR.js";
|
|
5
5
|
|
|
6
6
|
// src/session.ts
|
|
7
7
|
import { randomBytes } from "crypto";
|
|
8
8
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "fs";
|
|
9
|
-
import { join } from "path";
|
|
9
|
+
import { basename, join } from "path";
|
|
10
10
|
function projectDir(home, cwd) {
|
|
11
11
|
const slug = cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(-100) || "root";
|
|
12
12
|
return join(home, "sessions", slug);
|
|
@@ -27,6 +27,9 @@ var Session = class _Session {
|
|
|
27
27
|
this.file = file;
|
|
28
28
|
}
|
|
29
29
|
file;
|
|
30
|
+
get id() {
|
|
31
|
+
return basename(this.file, ".jsonl");
|
|
32
|
+
}
|
|
30
33
|
static create(home, cwd, now = /* @__PURE__ */ new Date()) {
|
|
31
34
|
const dir = projectDir(home, cwd);
|
|
32
35
|
mkdirSync(dir, { recursive: true });
|
|
@@ -38,7 +41,7 @@ var Session = class _Session {
|
|
|
38
41
|
if (!existsSync(dir)) return [];
|
|
39
42
|
return readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
40
43
|
const session = new _Session(join(dir, f));
|
|
41
|
-
const first = session.
|
|
44
|
+
const first = session.loadHistory().find((m) => m.role === "user");
|
|
42
45
|
return { session, mtime: statSync(session.file).mtime, preview: first ? contentText(first.content).slice(0, 80) : "" };
|
|
43
46
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime() || b.session.file.localeCompare(a.session.file));
|
|
44
47
|
}
|
|
@@ -88,6 +91,35 @@ var Session = class _Session {
|
|
|
88
91
|
}
|
|
89
92
|
return void 0;
|
|
90
93
|
}
|
|
94
|
+
/** Accumulated token usage since the last non-compaction reset. */
|
|
95
|
+
totalUsage() {
|
|
96
|
+
const segments = [{ msgs: [], usage: { inputTokens: 0, outputTokens: 0 } }];
|
|
97
|
+
for (const record of this.records()) {
|
|
98
|
+
const cur = segments[segments.length - 1];
|
|
99
|
+
if (record.t === "reset") {
|
|
100
|
+
segments.push({ msgs: [], usage: { inputTokens: 0, outputTokens: 0 } });
|
|
101
|
+
} else if (record.t === "msg") {
|
|
102
|
+
cur.msgs.push(record.m);
|
|
103
|
+
} else if (record.t === "usage") {
|
|
104
|
+
cur.usage.inputTokens += record.inputTokens;
|
|
105
|
+
cur.usage.outputTokens += record.outputTokens;
|
|
106
|
+
if (record.cachedInputTokens) cur.usage.cachedInputTokens = (cur.usage.cachedInputTokens ?? 0) + record.cachedInputTokens;
|
|
107
|
+
if (record.cacheWriteTokens) cur.usage.cacheWriteTokens = (cur.usage.cacheWriteTokens ?? 0) + record.cacheWriteTokens;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
let total = { ...segments[0].usage };
|
|
111
|
+
for (const seg of segments.slice(1)) {
|
|
112
|
+
if (!isCompactionSummary(seg.msgs[0])) {
|
|
113
|
+
total = { ...seg.usage };
|
|
114
|
+
} else {
|
|
115
|
+
total.inputTokens += seg.usage.inputTokens;
|
|
116
|
+
total.outputTokens += seg.usage.outputTokens;
|
|
117
|
+
if (seg.usage.cachedInputTokens) total.cachedInputTokens = (total.cachedInputTokens ?? 0) + seg.usage.cachedInputTokens;
|
|
118
|
+
if (seg.usage.cacheWriteTokens) total.cacheWriteTokens = (total.cacheWriteTokens ?? 0) + seg.usage.cacheWriteTokens;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return total;
|
|
122
|
+
}
|
|
91
123
|
/**
|
|
92
124
|
* What the user sees on resume: the whole conversation. Compaction only shrinks what the model is sent, so
|
|
93
125
|
* its reset does not hide the older turns here. A real reset (/clear, rewind) still starts over, the
|
|
@@ -100,12 +132,22 @@ var Session = class _Session {
|
|
|
100
132
|
else if (record.t === "msg") segments[segments.length - 1].push(record.m);
|
|
101
133
|
}
|
|
102
134
|
let shown = segments[0];
|
|
135
|
+
let lastSummary;
|
|
103
136
|
for (const segment of segments.slice(1)) {
|
|
104
137
|
if (!isCompactionSummary(segment[0])) {
|
|
105
138
|
shown = segment;
|
|
139
|
+
lastSummary = void 0;
|
|
106
140
|
continue;
|
|
107
141
|
}
|
|
108
142
|
const kept = segment.slice(2);
|
|
143
|
+
if (lastSummary && sameMessage(segment[0], lastSummary)) {
|
|
144
|
+
const subIdx = findContiguousSlice(shown, kept);
|
|
145
|
+
if (subIdx !== -1) {
|
|
146
|
+
shown = shown.slice(0, subIdx + kept.length);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
lastSummary = segment[0];
|
|
109
151
|
let at = 0;
|
|
110
152
|
while (at <= shown.length && !endsWithPrefixOf(shown, at, kept)) at++;
|
|
111
153
|
shown = [...shown, ...kept.slice(shown.length - at)];
|
|
@@ -132,12 +174,31 @@ function endsWithPrefixOf(shown, at, kept) {
|
|
|
132
174
|
for (let j = 0; j < n; j++) if (!sameMessage(shown[at + j], kept[j])) return false;
|
|
133
175
|
return true;
|
|
134
176
|
}
|
|
177
|
+
function findContiguousSlice(shown, slice) {
|
|
178
|
+
if (slice.length === 0) return shown.length;
|
|
179
|
+
for (let i = 0; i + slice.length <= shown.length; i++) {
|
|
180
|
+
let ok = true;
|
|
181
|
+
for (let j = 0; j < slice.length; j++) {
|
|
182
|
+
if (!sameMessage(shown[i + j], slice[j])) {
|
|
183
|
+
ok = false;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (ok) return i;
|
|
188
|
+
}
|
|
189
|
+
return -1;
|
|
190
|
+
}
|
|
135
191
|
function pruneSessions(home, now = Date.now(), maxAgeDays = 30) {
|
|
136
192
|
const root = join(home, "sessions");
|
|
137
193
|
if (!existsSync(root)) return 0;
|
|
138
194
|
let removed = 0;
|
|
139
195
|
for (const project of readdirSync(root)) {
|
|
140
196
|
const dir = join(root, project);
|
|
197
|
+
try {
|
|
198
|
+
if (!statSync(dir).isDirectory()) continue;
|
|
199
|
+
} catch {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
141
202
|
for (const file of readdirSync(dir)) {
|
|
142
203
|
const path = join(dir, file);
|
|
143
204
|
if (now - statSync(path).mtimeMs > maxAgeDays * 864e5) {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
defaultOpen
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3I5BW3ZT.js";
|
|
5
5
|
import {
|
|
6
6
|
removeCredential,
|
|
7
7
|
saveCredential
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RX4UQX43.js";
|
|
9
9
|
import {
|
|
10
10
|
t
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3WHI4LN3.js";
|
|
12
12
|
|
|
13
13
|
// src/login.ts
|
|
14
14
|
function readSecret(prompt) {
|
|
@@ -91,8 +91,8 @@ function defaultLoginDeps() {
|
|
|
91
91
|
fetch: globalThis.fetch,
|
|
92
92
|
// Wiring NYATA jalur lama: persis runLogin lama (provider add + prompt key).
|
|
93
93
|
legacyAdd: async (providerId, targetCwd) => {
|
|
94
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
95
|
-
const { parseCliArgs: parse } = await import("./args-
|
|
94
|
+
const { runProviderCommand } = await import("./providerCli-5OZI2GQQ.js");
|
|
95
|
+
const { parseCliArgs: parse } = await import("./args-NRTZWWEV.js");
|
|
96
96
|
return runProviderCommand(parse(["provider", "add", providerId]), {
|
|
97
97
|
env: process.env,
|
|
98
98
|
cwd: targetCwd,
|
|
@@ -117,12 +117,12 @@ async function runLogin(providerId, cwd, args, deps) {
|
|
|
117
117
|
);
|
|
118
118
|
return 1;
|
|
119
119
|
}
|
|
120
|
-
const { runOauthLogout } = await import("./oauthLogin-
|
|
120
|
+
const { runOauthLogout } = await import("./oauthLogin-XWY7OHCG.js");
|
|
121
121
|
return runOauthLogout({ out: deps.out, err: deps.err, env: deps.env, removeCredential: deps.removeCredential });
|
|
122
122
|
}
|
|
123
123
|
if (args.key) return deps.legacyAdd(providerId, cwd);
|
|
124
124
|
if (providerId === "openrouter") {
|
|
125
|
-
const { runOauthLogin } = await import("./oauthLogin-
|
|
125
|
+
const { runOauthLogin } = await import("./oauthLogin-XWY7OHCG.js");
|
|
126
126
|
return runOauthLogin({
|
|
127
127
|
out: deps.out,
|
|
128
128
|
err: deps.err,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/permissions.ts
|
|
4
|
-
import { isAbsolute, relative, resolve
|
|
4
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
5
5
|
var MODE_ORDER = ["default", "acceptEdits", "plan", "allowAll"];
|
|
6
6
|
function nextMode(mode) {
|
|
7
7
|
return MODE_ORDER[(MODE_ORDER.indexOf(mode) + 1) % MODE_ORDER.length];
|
|
@@ -62,7 +62,9 @@ function commandKey(segment) {
|
|
|
62
62
|
function requiresSensitiveToolApproval(req) {
|
|
63
63
|
const target = req.target.trim();
|
|
64
64
|
if (req.kind === "read" || req.kind === "edit") {
|
|
65
|
-
return /(?:^|[/\\])(?:\.env(
|
|
65
|
+
return /(?:^|[\s/\\])(?:\.env(?:\.[^\s/\\]+)?|\.ssh|\.aws|\.config[/\\]gcloud|\.npmrc|\.netrc|\.git-credentials|(?:secrets?|credentials?)(?:\.[a-z0-9_-]+)?)(?:[\s/\\]|$)/i.test(
|
|
66
|
+
target
|
|
67
|
+
) || /(?:id_rsa|id_ed25519|\.pem|\.p12|\.key)(?:\s|$)/i.test(target);
|
|
66
68
|
}
|
|
67
69
|
if (req.kind === "bash") {
|
|
68
70
|
return /\b(?:rm\s+-[a-z]*r[a-z]*f[a-z]*\s|rm\s+-[a-z]*f[a-z]*r[a-z]*\s|mkfs(?:\.|\s)|wipefs\b|shred\s|git\s+push\s+(?:--force|-f)\b|docker\s+(?:system\s+prune|volume\s+rm)|kubectl\s+delete\b|drop\s+(?:database|table)|npm\s+publish\b)/i.test(target) || /\b(?:deploy-service\.sh|aws\s+secretsmanager|cat\s+[^;&|]*\.env\b)/i.test(target);
|
|
@@ -87,7 +89,8 @@ var Permissions = class {
|
|
|
87
89
|
editInProject(target) {
|
|
88
90
|
const rel = relative(this.cwd, resolve(this.cwd, target));
|
|
89
91
|
if (!rel || rel.startsWith("..") || isAbsolute(rel)) return false;
|
|
90
|
-
|
|
92
|
+
const parts = rel.split(/[/\\]/).map((p) => p.toLowerCase());
|
|
93
|
+
return !parts.includes(".git");
|
|
91
94
|
}
|
|
92
95
|
/** Rules that "[a] allow for this session" would add; undefined when it must always be asked. */
|
|
93
96
|
rulesFor(req) {
|
|
@@ -105,7 +108,7 @@ var Permissions = class {
|
|
|
105
108
|
if (/`|\$\(|[<>]|^\s*[({]/.test(req.target)) return void 0;
|
|
106
109
|
const segments = req.target.split(/&&|\|\||[;&|\n]/).map((s) => s.trim()).filter(Boolean);
|
|
107
110
|
if (!segments.length) return void 0;
|
|
108
|
-
if (segments.some(isAlwaysAsk)) return void 0;
|
|
111
|
+
if (segments.some((s) => /^[({]/.test(s) || isAlwaysAsk(s))) return void 0;
|
|
109
112
|
return [...new Set(segments.map((s) => `bash(${commandKey(s)})`))];
|
|
110
113
|
}
|
|
111
114
|
if (req.kind === "mcp") return [`mcp(${req.tool})`];
|
|
@@ -139,5 +142,6 @@ var Permissions = class {
|
|
|
139
142
|
export {
|
|
140
143
|
MODE_ORDER,
|
|
141
144
|
nextMode,
|
|
145
|
+
requiresSensitiveToolApproval,
|
|
142
146
|
Permissions
|
|
143
147
|
};
|
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
} from "./chunk-PSO7R5AD.js";
|
|
5
5
|
import {
|
|
6
6
|
t
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3WHI4LN3.js";
|
|
8
8
|
|
|
9
9
|
// src/tools/bash.ts
|
|
10
10
|
import { spawn } from "child_process";
|
|
11
|
-
import { existsSync as existsSync2 } from "fs";
|
|
11
|
+
import { existsSync as existsSync2, writeFileSync } from "fs";
|
|
12
|
+
import { tmpdir } from "os";
|
|
13
|
+
import { join } from "path";
|
|
12
14
|
import { z } from "zod";
|
|
13
15
|
|
|
14
16
|
// src/tools/sandbox.ts
|
|
@@ -236,7 +238,7 @@ function offlineEnv(env = process.env) {
|
|
|
236
238
|
};
|
|
237
239
|
}
|
|
238
240
|
function runCommand(command, opts) {
|
|
239
|
-
return new Promise((
|
|
241
|
+
return new Promise((resolve3) => {
|
|
240
242
|
const child = spawnChild(command, opts.cwd, opts.env, opts.shell ?? defaultShell());
|
|
241
243
|
if (!exitHookInstalled) {
|
|
242
244
|
process.on("exit", killAllCommands);
|
|
@@ -249,7 +251,14 @@ function runCommand(command, opts) {
|
|
|
249
251
|
let dropped = 0;
|
|
250
252
|
let timedOut = false;
|
|
251
253
|
let aborted = false;
|
|
254
|
+
const fullChunks = [];
|
|
255
|
+
let fullBytes = 0;
|
|
256
|
+
const MAX_SPILL_BYTES = 10 * 1024 * 1024;
|
|
252
257
|
const onData = (data) => {
|
|
258
|
+
if (fullBytes < MAX_SPILL_BYTES) {
|
|
259
|
+
fullChunks.push(data);
|
|
260
|
+
fullBytes += data.length;
|
|
261
|
+
}
|
|
253
262
|
let s = data;
|
|
254
263
|
if (head.length < HALF) {
|
|
255
264
|
const take = s.slice(0, HALF - head.length);
|
|
@@ -288,10 +297,19 @@ function runCommand(command, opts) {
|
|
|
288
297
|
active.delete(killAsAborted);
|
|
289
298
|
clearTimeout(timer);
|
|
290
299
|
opts.signal.removeEventListener("abort", onAbort);
|
|
300
|
+
let spillNote = "";
|
|
301
|
+
if (dropped && fullChunks.length) {
|
|
302
|
+
try {
|
|
303
|
+
const spillPath = join(tmpdir(), `bccli-bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.log`);
|
|
304
|
+
writeFileSync(spillPath, fullChunks.join(""), "utf8");
|
|
305
|
+
spillNote = `; full output saved to ${spillPath}`;
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
}
|
|
291
309
|
const output = dropped ? `${head}
|
|
292
|
-
\u2026 [${dropped} characters truncated] \u2026
|
|
310
|
+
\u2026 [${dropped} characters truncated${spillNote}] \u2026
|
|
293
311
|
${tail}` : head + tail;
|
|
294
|
-
|
|
312
|
+
resolve3({ output: output + extra, exitCode, timedOut, aborted });
|
|
295
313
|
};
|
|
296
314
|
child.on("error", (error) => finish(null, error.message));
|
|
297
315
|
child.on("close", (code) => finish(code));
|
|
@@ -403,7 +421,7 @@ var BUILTIN_AGENTS = [
|
|
|
403
421
|
|
|
404
422
|
// src/context.ts
|
|
405
423
|
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
406
|
-
import { dirname, join } from "path";
|
|
424
|
+
import { dirname, join as join2 } from "path";
|
|
407
425
|
var FILES = ["AGENTS.md", "CLAUDE.md", "BCCLI.md"];
|
|
408
426
|
function readIf(path) {
|
|
409
427
|
try {
|
|
@@ -418,7 +436,7 @@ function instructionPaths(cwd, home) {
|
|
|
418
436
|
dirs2.unshift(dir);
|
|
419
437
|
if (dirname(dir) === dir) break;
|
|
420
438
|
}
|
|
421
|
-
return [
|
|
439
|
+
return [join2(home, "BCCLI.md"), ...dirs2.flatMap((dir) => FILES.map((name) => join2(dir, name)))];
|
|
422
440
|
}
|
|
423
441
|
function loadInstructions(cwd, home) {
|
|
424
442
|
const parts = [];
|
|
@@ -431,7 +449,7 @@ ${text}`);
|
|
|
431
449
|
}
|
|
432
450
|
function isGitRepo(cwd) {
|
|
433
451
|
for (let dir = cwd; ; dir = dirname(dir)) {
|
|
434
|
-
if (existsSync3(
|
|
452
|
+
if (existsSync3(join2(dir, ".git"))) return true;
|
|
435
453
|
if (dirname(dir) === dir) return false;
|
|
436
454
|
}
|
|
437
455
|
}
|
|
@@ -476,15 +494,33 @@ ${instructions}` : ""}`;
|
|
|
476
494
|
|
|
477
495
|
// src/extensions.ts
|
|
478
496
|
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
479
|
-
import { basename, join as
|
|
497
|
+
import { basename, join as join3 } from "path";
|
|
480
498
|
function parseFrontmatter(text) {
|
|
481
499
|
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
|
|
482
500
|
if (!match) return { data: {}, body: text };
|
|
483
501
|
const data = {};
|
|
502
|
+
let currentListKey;
|
|
484
503
|
for (const line of match[1].split(/\r?\n/)) {
|
|
504
|
+
const listItem = /^\s*-\s+(.+)$/.exec(line);
|
|
505
|
+
if (listItem && currentListKey) {
|
|
506
|
+
const item = listItem[1].trim().replace(/^["']|["']$/g, "");
|
|
507
|
+
const prev = data[currentListKey];
|
|
508
|
+
if (Array.isArray(prev)) prev.push(item);
|
|
509
|
+
else data[currentListKey] = [item];
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
485
512
|
const kv = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
|
|
486
|
-
if (!kv)
|
|
513
|
+
if (!kv) {
|
|
514
|
+
if (line.trim()) currentListKey = void 0;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
487
517
|
let value = kv[2].trim();
|
|
518
|
+
if (!value) {
|
|
519
|
+
currentListKey = kv[1];
|
|
520
|
+
data[kv[1]] = "";
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
currentListKey = void 0;
|
|
488
524
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
489
525
|
data[kv[1]] = value.slice(1, -1).split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
490
526
|
continue;
|
|
@@ -496,7 +532,7 @@ function parseFrontmatter(text) {
|
|
|
496
532
|
}
|
|
497
533
|
var str = (v) => Array.isArray(v) ? v.join(", ") : v;
|
|
498
534
|
function dirs(r, kind) {
|
|
499
|
-
return [
|
|
535
|
+
return [join3(r.userHome, ".claude", kind), join3(r.home, kind), join3(r.cwd, ".claude", kind), join3(r.cwd, ".bccli", kind)];
|
|
500
536
|
}
|
|
501
537
|
function list(dir) {
|
|
502
538
|
try {
|
|
@@ -509,11 +545,11 @@ function loadSkills(r) {
|
|
|
509
545
|
const byName = /* @__PURE__ */ new Map();
|
|
510
546
|
for (const dir of dirs(r, "skills")) {
|
|
511
547
|
for (const entry of list(dir)) {
|
|
512
|
-
const file =
|
|
548
|
+
const file = join3(dir, entry, "SKILL.md");
|
|
513
549
|
if (!existsSync4(file)) continue;
|
|
514
550
|
const { data } = parseFrontmatter(readFileSync2(file, "utf8"));
|
|
515
551
|
const name = str(data.name) || entry;
|
|
516
|
-
byName.set(name, { name, description: str(data.description) ?? "", dir:
|
|
552
|
+
byName.set(name, { name, description: str(data.description) ?? "", dir: join3(dir, entry), file });
|
|
517
553
|
}
|
|
518
554
|
}
|
|
519
555
|
return [...byName.values()];
|
|
@@ -522,7 +558,7 @@ function loadCommands(r) {
|
|
|
522
558
|
const byName = /* @__PURE__ */ new Map();
|
|
523
559
|
for (const dir of dirs(r, "commands")) {
|
|
524
560
|
for (const entry of list(dir)) {
|
|
525
|
-
const file =
|
|
561
|
+
const file = join3(dir, entry);
|
|
526
562
|
if (!entry.endsWith(".md") || !statSync(file).isFile()) continue;
|
|
527
563
|
const { data, body } = parseFrontmatter(readFileSync2(file, "utf8"));
|
|
528
564
|
const def = { name: basename(entry, ".md"), body: body.trim() };
|
|
@@ -538,7 +574,7 @@ function loadAgentDefs(r) {
|
|
|
538
574
|
for (const dir of dirs(r, "agents")) {
|
|
539
575
|
for (const entry of list(dir)) {
|
|
540
576
|
if (!entry.endsWith(".md")) continue;
|
|
541
|
-
const { data, body } = parseFrontmatter(readFileSync2(
|
|
577
|
+
const { data, body } = parseFrontmatter(readFileSync2(join3(dir, entry), "utf8"));
|
|
542
578
|
const name = str(data.name) || basename(entry, ".md");
|
|
543
579
|
const def = { name, description: str(data.description) ?? "", prompt: body.trim() };
|
|
544
580
|
if (data.tools)
|
|
@@ -573,7 +609,7 @@ function runOne(command, event, payload, opts) {
|
|
|
573
609
|
...payload.input !== void 0 ? { BCCLI_HOOK_INPUT: JSON.stringify(payload.input) } : {},
|
|
574
610
|
...payload.output !== void 0 ? { BCCLI_HOOK_OUTPUT: payload.output } : {}
|
|
575
611
|
};
|
|
576
|
-
return new Promise((
|
|
612
|
+
return new Promise((resolve3) => {
|
|
577
613
|
let stdout = "";
|
|
578
614
|
let stderr = "";
|
|
579
615
|
let timedOut = false;
|
|
@@ -582,7 +618,7 @@ function runOne(command, event, payload, opts) {
|
|
|
582
618
|
if (settled) return;
|
|
583
619
|
settled = true;
|
|
584
620
|
clearTimeout(timer);
|
|
585
|
-
|
|
621
|
+
resolve3(result);
|
|
586
622
|
};
|
|
587
623
|
let child;
|
|
588
624
|
const shell = defaultShell();
|
|
@@ -598,7 +634,7 @@ function runOne(command, event, payload, opts) {
|
|
|
598
634
|
child = spawn2(command, { shell: true, env, cwd: opts.cwd ?? process.cwd(), stdio: ["ignore", "pipe", "pipe"] });
|
|
599
635
|
}
|
|
600
636
|
} catch (error) {
|
|
601
|
-
|
|
637
|
+
resolve3({ failure: error.message });
|
|
602
638
|
return;
|
|
603
639
|
}
|
|
604
640
|
const timer = setTimeout(() => {
|
|
@@ -648,6 +684,38 @@ async function runHooks(hooks, event, payload = {}, opts = {}) {
|
|
|
648
684
|
return { blocked, warnings };
|
|
649
685
|
}
|
|
650
686
|
|
|
687
|
+
// src/tools/paths.ts
|
|
688
|
+
import { statSync as statSync2 } from "fs";
|
|
689
|
+
import { isAbsolute, resolve } from "path";
|
|
690
|
+
var readMtimes = /* @__PURE__ */ new WeakMap();
|
|
691
|
+
function resolvePath(cwd, path) {
|
|
692
|
+
return isAbsolute(path) ? path : resolve(cwd, path);
|
|
693
|
+
}
|
|
694
|
+
function errorCode(error) {
|
|
695
|
+
return error.code ?? error.message;
|
|
696
|
+
}
|
|
697
|
+
function recordFileMtime(readFiles, abs) {
|
|
698
|
+
readFiles.add(abs);
|
|
699
|
+
try {
|
|
700
|
+
let map = readMtimes.get(readFiles);
|
|
701
|
+
if (!map) {
|
|
702
|
+
map = /* @__PURE__ */ new Map();
|
|
703
|
+
readMtimes.set(readFiles, map);
|
|
704
|
+
}
|
|
705
|
+
map.set(abs, statSync2(abs).mtimeMs);
|
|
706
|
+
} catch {
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function isFileModifiedSinceRead(readFiles, abs) {
|
|
710
|
+
const recorded = readMtimes.get(readFiles)?.get(abs);
|
|
711
|
+
if (recorded === void 0) return false;
|
|
712
|
+
try {
|
|
713
|
+
return statSync2(abs).mtimeMs > recorded + 50;
|
|
714
|
+
} catch {
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
651
719
|
// src/slash/memory.ts
|
|
652
720
|
import { appendFileSync, existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
653
721
|
function instructionFiles(cwd, home) {
|
|
@@ -665,22 +733,38 @@ function parseMemoryArgs(args) {
|
|
|
665
733
|
|
|
666
734
|
// src/tools/ignore.ts
|
|
667
735
|
import { readFileSync as readFileSync4 } from "fs";
|
|
668
|
-
import { join as
|
|
669
|
-
function ignorePatterns(root) {
|
|
736
|
+
import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
|
|
737
|
+
function ignorePatterns(root, projectRoot) {
|
|
670
738
|
const patterns = ["**/node_modules/**", "**/.git/**"];
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
739
|
+
const seen = /* @__PURE__ */ new Set();
|
|
740
|
+
const dirs2 = [];
|
|
741
|
+
let cur = resolve2(root);
|
|
742
|
+
const stop = projectRoot ? resolve2(projectRoot) : cur;
|
|
743
|
+
while (true) {
|
|
744
|
+
if (!seen.has(cur)) {
|
|
745
|
+
seen.add(cur);
|
|
746
|
+
dirs2.push(cur);
|
|
747
|
+
}
|
|
748
|
+
if (cur === stop || !cur.startsWith(stop)) break;
|
|
749
|
+
const parent = dirname2(cur);
|
|
750
|
+
if (parent === cur) break;
|
|
751
|
+
cur = parent;
|
|
676
752
|
}
|
|
677
|
-
for (const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
753
|
+
for (const dir of dirs2) {
|
|
754
|
+
let lines = [];
|
|
755
|
+
try {
|
|
756
|
+
lines = readFileSync4(join4(dir, ".gitignore"), "utf8").split(/\r?\n/);
|
|
757
|
+
} catch {
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
for (const raw of lines) {
|
|
761
|
+
const line = raw.trim();
|
|
762
|
+
if (!line || line.startsWith("#") || line.startsWith("!")) continue;
|
|
763
|
+
const p = line.replace(/^\//, "").replace(/\/$/, "");
|
|
764
|
+
patterns.push(`**/${p}`, `**/${p}/**`);
|
|
765
|
+
}
|
|
682
766
|
}
|
|
683
|
-
return patterns;
|
|
767
|
+
return [...new Set(patterns)];
|
|
684
768
|
}
|
|
685
769
|
|
|
686
770
|
// src/tools/globSafety.ts
|
|
@@ -718,6 +802,10 @@ export {
|
|
|
718
802
|
runCommand,
|
|
719
803
|
createBashTool,
|
|
720
804
|
bashTool,
|
|
805
|
+
resolvePath,
|
|
806
|
+
errorCode,
|
|
807
|
+
recordFileMtime,
|
|
808
|
+
isFileModifiedSinceRead,
|
|
721
809
|
ignorePatterns,
|
|
722
810
|
validateGlobPattern,
|
|
723
811
|
normalizeGlobPath,
|