@botconnector/bccli 0.4.0 → 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/README.en.md +13 -2
- package/README.md +13 -2
- package/dist/abort.d.ts +5 -0
- package/dist/{acp-ATIL4JOZ.js → acp-UYC6APR5.js} +30 -6
- package/dist/acp.d.ts +28 -0
- package/dist/agent.d.ts +147 -0
- package/dist/agents.d.ts +4 -0
- package/dist/args-NRTZWWEV.js +13 -0
- package/dist/args.d.ts +53 -0
- package/dist/budget.d.ts +23 -0
- package/dist/checkpoints.d.ts +35 -0
- package/dist/{chunk-6M5X2KZE.js → chunk-266SY3MC.js} +1 -1
- package/dist/{chunk-7F3IWBPQ.js → chunk-3I5BW3ZT.js} +20 -9
- package/dist/{chunk-UZWJQF67.js → chunk-3WHI4LN3.js} +2 -1
- package/dist/{chunk-PUN4HMAG.js → chunk-5YJ3ZQVR.js} +73 -10
- package/dist/{chunk-L7QI6JN2.js → chunk-6M55UPES.js} +7 -7
- package/dist/{chunk-KEMYIGCP.js → chunk-6UO5GX55.js} +26 -12
- package/dist/{chunk-TLPFQIS2.js → chunk-C64KM37J.js} +36 -6
- package/dist/{chunk-JL5ZCSP2.js → chunk-H7XJDKJY.js} +4 -4
- package/dist/{chunk-SPRNLS7K.js → chunk-HQW4BROS.js} +4 -5
- package/dist/{chunk-XBO5GACQ.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-BQ2IGPBR.js → chunk-NQOGVP3I.js} +2 -2
- package/dist/{chunk-QZKLZELZ.js → chunk-OK2USAXX.js} +28 -4
- package/dist/{chunk-TVALKHFB.js → chunk-R72OV7RF.js} +421 -108
- package/dist/{chunk-LACUWBWS.js → chunk-RX4UQX43.js} +25 -10
- package/dist/{chunk-QDQU6VY2.js → chunk-SVD4LHJC.js} +73 -3
- package/dist/{chunk-BNA74HPN.js → chunk-TIMSYZIR.js} +7 -7
- package/dist/{chunk-O7M3TGIW.js → chunk-WFZKJY3R.js} +43 -4
- package/dist/{chunk-M7CALSP3.js → chunk-ZEPTUKNW.js} +120 -32
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +61 -45
- package/dist/clipboardImage.d.ts +17 -0
- package/dist/commands.d.ts +16 -0
- package/dist/{completion-HPPFDAPB.js → completion-D2BSBTQO.js} +1 -1
- package/dist/completion.d.ts +14 -0
- package/dist/{config-COPNMMV6.js → config-ENDRMOTP.js} +3 -3
- package/dist/{config-RZ5JONMX.js → config-LAINORU5.js} +8 -4
- package/dist/config.d.ts +99 -0
- package/dist/context.d.ts +13 -0
- package/dist/contextWindow.d.ts +3 -0
- package/dist/diff.d.ts +7 -0
- package/dist/exit.d.ts +9 -0
- package/dist/extensions.d.ts +34 -0
- package/dist/{ghBotCli-IBCZIWT5.js → ghBotCli-TSLRORJJ.js} +21 -20
- package/dist/ghBotCli.d.ts +9 -0
- package/dist/ghBotDispatch.d.ts +68 -0
- package/dist/ghBotRunner.d.ts +82 -0
- package/dist/ghBotServe.d.ts +21 -0
- package/dist/ghBotWebhook.d.ts +45 -0
- package/dist/hooks.d.ts +27 -0
- package/dist/i18n/id.d.ts +1 -0
- package/dist/i18n.d.ts +16 -0
- package/dist/{integrationCli-P75R2VSC.js → integrationCli-HXCSEWH2.js} +2 -2
- package/dist/integrationCli.d.ts +9 -0
- package/dist/keybinds.d.ts +22 -0
- package/dist/{login-SYIQP46J.js → login-RYESFZG6.js} +4 -4
- package/dist/login.d.ts +19 -0
- package/dist/lsp.d.ts +50 -0
- package/dist/mcp/catalog.d.ts +14 -0
- package/dist/mcp/config.d.ts +36 -0
- package/dist/mcp/manager.d.ts +52 -0
- package/dist/mcp/oauth.d.ts +82 -0
- package/dist/{mcpCli-QUV5SD64.js → mcpCli-SETTCL3K.js} +6 -6
- package/dist/mcpCli.d.ts +3 -0
- package/dist/mcpServer/serve.d.ts +8 -0
- package/dist/mcpServer/server.d.ts +19 -0
- package/dist/models.d.ts +13 -0
- package/dist/{modelsCli-TRRB5LX4.js → modelsCli-OP7CDZC7.js} +6 -6
- package/dist/modelsCli.d.ts +15 -0
- package/dist/npmRunner.d.ts +21 -0
- package/dist/{oauthLogin-6FWQABJ7.js → oauthLogin-XWY7OHCG.js} +1 -1
- package/dist/oauthLogin.d.ts +47 -0
- package/dist/permissions.d.ts +30 -0
- package/dist/pluginSpec.d.ts +25 -0
- package/dist/plugins.d.ts +45 -0
- package/dist/{pluginsCli-GNW4TPVV.js → pluginsCli-ZQNKWZHJ.js} +2 -2
- package/dist/pluginsCli.d.ts +16 -0
- package/dist/{prCli-5IMV3VNQ.js → prCli-6PUWTMTP.js} +20 -4
- package/dist/prCli.d.ts +46 -0
- package/dist/presets.d.ts +8 -0
- package/dist/print.d.ts +16 -0
- package/dist/provider.d.ts +145 -0
- package/dist/providerCli-5OZI2GQQ.js +15 -0
- package/dist/providerCli.d.ts +17 -0
- package/dist/providers.d.ts +6 -0
- package/dist/reasoning.d.ts +12 -0
- package/dist/repomap.d.ts +10 -0
- package/dist/sdk.d.ts +61 -0
- package/dist/sdk.js +829 -191
- package/dist/{serve-JG7RLIUD.js → serve-4J7HA4JO.js} +24 -18
- package/dist/{serve-6QLBJUYW.js → serve-K5AH4MGO.js} +3 -3
- package/dist/serve.d.ts +37 -0
- package/dist/serveHeartbeat.d.ts +14 -0
- package/dist/serveProtocol.d.ts +47 -0
- package/dist/session-YQXCCKXI.js +12 -0
- package/dist/session.d.ts +33 -0
- package/dist/setup-S4AJT6XE.js +24 -0
- package/dist/setup.d.ts +74 -0
- package/dist/slash/copy.d.ts +1 -0
- package/dist/slash/diff.d.ts +1 -0
- package/dist/slash/doctor.d.ts +18 -0
- package/dist/slash/export.d.ts +2 -0
- package/dist/slash/info.d.ts +46 -0
- package/dist/slash/memory.d.ts +10 -0
- package/dist/{statsCli-CF3R7ATL.js → statsCli-XR6BEZQQ.js} +2 -2
- package/dist/statsCli.d.ts +62 -0
- package/dist/textToolCalls.d.ts +6 -0
- package/dist/thinking.d.ts +17 -0
- package/dist/tokenBudget.d.ts +22 -0
- package/dist/tools/bash.d.ts +52 -0
- package/dist/tools/diagnostics.d.ts +5 -0
- package/dist/tools/edit.d.ts +12 -0
- package/dist/tools/fetch.d.ts +41 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/globSafety.d.ts +7 -0
- package/dist/tools/grep.d.ts +25 -0
- package/dist/tools/ignore.d.ts +2 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/paths.d.ts +4 -0
- package/dist/tools/plan.d.ts +10 -0
- package/dist/tools/read.d.ts +16 -0
- package/dist/tools/repomap.d.ts +2 -0
- package/dist/tools/sandbox.d.ts +12 -0
- package/dist/tools/shell.d.ts +18 -0
- package/dist/tools/skill.d.ts +3 -0
- package/dist/tools/task.d.ts +28 -0
- package/dist/tools/todo.d.ts +13 -0
- package/dist/tools/types.d.ts +52 -0
- package/dist/tools/websearch.d.ts +9 -0
- package/dist/tools/write.d.ts +5 -0
- package/dist/{transferCli-TK2GSVRK.js → transferCli-R2KEIWQR.js} +8 -6
- package/dist/transferCli.d.ts +23 -0
- package/dist/ui/App.d.ts +7 -0
- package/dist/ui/DiffView.d.ts +3 -0
- package/dist/ui/EditableText.d.ts +7 -0
- package/dist/ui/LinePrompt.d.ts +6 -0
- package/dist/ui/ListPicker.d.ts +11 -0
- package/dist/ui/Markdown.d.ts +8 -0
- package/dist/ui/McpMenu.d.ts +11 -0
- package/dist/ui/ModelPicker.d.ts +6 -0
- package/dist/ui/PermissionPrompt.d.ts +5 -0
- package/dist/ui/PlanApproval.d.ts +5 -0
- package/dist/ui/PromptInput.d.ts +18 -0
- package/dist/ui/ProviderMenu.d.ts +10 -0
- package/dist/ui/Spinner.d.ts +4 -0
- package/dist/ui/StatusBar.d.ts +9 -0
- package/dist/ui/TodoList.d.ts +4 -0
- package/dist/ui/ToolBlock.d.ts +14 -0
- package/dist/ui/complete.d.ts +6 -0
- package/dist/ui/focusReport.d.ts +1 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/lineEdit.d.ts +42 -0
- package/dist/ui/replay.d.ts +4 -0
- package/dist/ui/table.d.ts +20 -0
- package/dist/ui/theme.d.ts +5 -0
- package/dist/ui/thinkingMouse.d.ts +33 -0
- package/dist/ui/transcript.d.ts +55 -0
- package/dist/ui/vim.d.ts +26 -0
- package/dist/{ui-B3CJ6TGE.js → ui-36ZEK65V.js} +179 -54
- package/dist/{update-K5IQ2MYR.js → update-FSS7X5WV.js} +2 -2
- package/dist/update.d.ts +30 -0
- package/dist/verify.d.ts +16 -0
- package/dist/version.d.ts +1 -0
- package/dist/webBudget.d.ts +25 -0
- package/dist/worktree.d.ts +12 -0
- package/package.json +26 -13
- package/dist/args-J5ONVGIF.js +0 -13
- package/dist/providerCli-FAOZHOKW.js +0 -15
- package/dist/session-KBAQ4BZ5.js +0 -12
- package/dist/setup-FJAWDWV4.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,
|
|
@@ -274,11 +277,12 @@ function readGlobalPlugins(env = process.env) {
|
|
|
274
277
|
}
|
|
275
278
|
return value;
|
|
276
279
|
}
|
|
277
|
-
function writeFileAtomic(path, data) {
|
|
280
|
+
function writeFileAtomic(path, data, mode) {
|
|
278
281
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
279
282
|
try {
|
|
280
|
-
writeFileSync(tmp, data);
|
|
283
|
+
writeFileSync(tmp, data, mode === void 0 ? void 0 : { mode });
|
|
281
284
|
renameSync(tmp, path);
|
|
285
|
+
if (mode !== void 0) chmodSync(path, mode);
|
|
282
286
|
} catch (error) {
|
|
283
287
|
try {
|
|
284
288
|
rmSync(tmp, { force: true });
|
|
@@ -287,6 +291,18 @@ function writeFileAtomic(path, data) {
|
|
|
287
291
|
throw error;
|
|
288
292
|
}
|
|
289
293
|
}
|
|
294
|
+
function quarantineCorruptFile(path, error) {
|
|
295
|
+
const stamp = Date.now();
|
|
296
|
+
let dest = `${path}.corrupt-${stamp}`;
|
|
297
|
+
for (let n = 1; existsSync(dest); n++) dest = `${path}.corrupt-${stamp}-${n}`;
|
|
298
|
+
try {
|
|
299
|
+
renameSync(path, dest);
|
|
300
|
+
} catch {
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
console.error(`${path} is not valid JSON (${error.message}); moved it to ${dest} instead of overwriting it.`);
|
|
304
|
+
return dest;
|
|
305
|
+
}
|
|
290
306
|
function updateGlobalConfig(env, mutate) {
|
|
291
307
|
const path = globalConfigPath(env);
|
|
292
308
|
let cfg = {};
|
|
@@ -303,16 +319,15 @@ function readCredentials(env = process.env) {
|
|
|
303
319
|
if (!existsSync(path)) return {};
|
|
304
320
|
try {
|
|
305
321
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
306
|
-
} catch {
|
|
322
|
+
} catch (error) {
|
|
323
|
+
quarantineCorruptFile(path, error);
|
|
307
324
|
return {};
|
|
308
325
|
}
|
|
309
326
|
}
|
|
310
327
|
function saveCredential(providerId, key, env = process.env) {
|
|
311
328
|
const home = bccliHome(env);
|
|
312
329
|
mkdirSync(home, { recursive: true });
|
|
313
|
-
|
|
314
|
-
writeFileSync(path, JSON.stringify({ ...readCredentials(env), [providerId]: key }, null, 2), { mode: 384 });
|
|
315
|
-
chmodSync(path, 384);
|
|
330
|
+
writeFileAtomic(join(home, "credentials"), JSON.stringify({ ...readCredentials(env), [providerId]: key }, null, 2), 384);
|
|
316
331
|
}
|
|
317
332
|
function resolveModel(config, modelRef, env = process.env) {
|
|
318
333
|
const slash = modelRef.indexOf("/");
|
|
@@ -334,9 +349,7 @@ function removeCredential(providerId, env = process.env) {
|
|
|
334
349
|
const creds = readCredentials(env);
|
|
335
350
|
if (!(providerId in creds)) return false;
|
|
336
351
|
delete creds[providerId];
|
|
337
|
-
|
|
338
|
-
writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
339
|
-
chmodSync(path, 384);
|
|
352
|
+
writeFileAtomic(join(bccliHome(env), "credentials"), JSON.stringify(creds, null, 2), 384);
|
|
340
353
|
return true;
|
|
341
354
|
}
|
|
342
355
|
|
|
@@ -353,6 +366,8 @@ export {
|
|
|
353
366
|
loadConfig,
|
|
354
367
|
globalConfigPath,
|
|
355
368
|
readGlobalPlugins,
|
|
369
|
+
writeFileAtomic,
|
|
370
|
+
quarantineCorruptFile,
|
|
356
371
|
updateGlobalConfig,
|
|
357
372
|
readCredentials,
|
|
358
373
|
saveCredential,
|
|
@@ -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
|
}
|
|
@@ -79,6 +82,44 @@ var Session = class _Session {
|
|
|
79
82
|
}
|
|
80
83
|
return repairTail(messages);
|
|
81
84
|
}
|
|
85
|
+
/** Model of the most recent turn that recorded usage — serve attach continues a session with its own model. */
|
|
86
|
+
lastModel() {
|
|
87
|
+
const records = this.records();
|
|
88
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
89
|
+
const record = records[i];
|
|
90
|
+
if (record.t === "usage" && record.model) return record.model;
|
|
91
|
+
}
|
|
92
|
+
return void 0;
|
|
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
|
+
}
|
|
82
123
|
/**
|
|
83
124
|
* What the user sees on resume: the whole conversation. Compaction only shrinks what the model is sent, so
|
|
84
125
|
* its reset does not hide the older turns here. A real reset (/clear, rewind) still starts over, the
|
|
@@ -91,12 +132,22 @@ var Session = class _Session {
|
|
|
91
132
|
else if (record.t === "msg") segments[segments.length - 1].push(record.m);
|
|
92
133
|
}
|
|
93
134
|
let shown = segments[0];
|
|
135
|
+
let lastSummary;
|
|
94
136
|
for (const segment of segments.slice(1)) {
|
|
95
137
|
if (!isCompactionSummary(segment[0])) {
|
|
96
138
|
shown = segment;
|
|
139
|
+
lastSummary = void 0;
|
|
97
140
|
continue;
|
|
98
141
|
}
|
|
99
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];
|
|
100
151
|
let at = 0;
|
|
101
152
|
while (at <= shown.length && !endsWithPrefixOf(shown, at, kept)) at++;
|
|
102
153
|
shown = [...shown, ...kept.slice(shown.length - at)];
|
|
@@ -123,12 +174,31 @@ function endsWithPrefixOf(shown, at, kept) {
|
|
|
123
174
|
for (let j = 0; j < n; j++) if (!sameMessage(shown[at + j], kept[j])) return false;
|
|
124
175
|
return true;
|
|
125
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
|
+
}
|
|
126
191
|
function pruneSessions(home, now = Date.now(), maxAgeDays = 30) {
|
|
127
192
|
const root = join(home, "sessions");
|
|
128
193
|
if (!existsSync(root)) return 0;
|
|
129
194
|
let removed = 0;
|
|
130
195
|
for (const project of readdirSync(root)) {
|
|
131
196
|
const dir = join(root, project);
|
|
197
|
+
try {
|
|
198
|
+
if (!statSync(dir).isDirectory()) continue;
|
|
199
|
+
} catch {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
132
202
|
for (const file of readdirSync(dir)) {
|
|
133
203
|
const path = join(dir, file);
|
|
134
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];
|
|
@@ -20,14 +20,51 @@ function isAlwaysAsk(segment) {
|
|
|
20
20
|
}
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
|
+
var RUNNER_COMMANDS = /* @__PURE__ */ new Set([
|
|
24
|
+
"npm",
|
|
25
|
+
"npx",
|
|
26
|
+
"pnpm",
|
|
27
|
+
"pnpx",
|
|
28
|
+
"yarn",
|
|
29
|
+
"bun",
|
|
30
|
+
"bunx",
|
|
31
|
+
"deno",
|
|
32
|
+
"node",
|
|
33
|
+
"tsx",
|
|
34
|
+
"ts-node",
|
|
35
|
+
"python",
|
|
36
|
+
"python3",
|
|
37
|
+
"py",
|
|
38
|
+
"uv",
|
|
39
|
+
"uvx",
|
|
40
|
+
"pip",
|
|
41
|
+
"pip3",
|
|
42
|
+
"pipx",
|
|
43
|
+
"pytest",
|
|
44
|
+
"make",
|
|
45
|
+
"cargo",
|
|
46
|
+
"go",
|
|
47
|
+
"dotnet",
|
|
48
|
+
"java",
|
|
49
|
+
"ruby",
|
|
50
|
+
"bundle",
|
|
51
|
+
"rake",
|
|
52
|
+
"gradle",
|
|
53
|
+
"mvn",
|
|
54
|
+
"php",
|
|
55
|
+
"composer"
|
|
56
|
+
]);
|
|
23
57
|
function commandKey(segment) {
|
|
24
58
|
const words = segment.trim().split(/\s+/);
|
|
59
|
+
if (RUNNER_COMMANDS.has(words[0] ?? "") && words[2] && !words[2].startsWith("-")) return words.slice(0, 3).join(" ");
|
|
25
60
|
return words[1] ? `${words[0]} ${words[1]}` : words[0];
|
|
26
61
|
}
|
|
27
62
|
function requiresSensitiveToolApproval(req) {
|
|
28
63
|
const target = req.target.trim();
|
|
29
64
|
if (req.kind === "read" || req.kind === "edit") {
|
|
30
|
-
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);
|
|
31
68
|
}
|
|
32
69
|
if (req.kind === "bash") {
|
|
33
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);
|
|
@@ -52,7 +89,8 @@ var Permissions = class {
|
|
|
52
89
|
editInProject(target) {
|
|
53
90
|
const rel = relative(this.cwd, resolve(this.cwd, target));
|
|
54
91
|
if (!rel || rel.startsWith("..") || isAbsolute(rel)) return false;
|
|
55
|
-
|
|
92
|
+
const parts = rel.split(/[/\\]/).map((p) => p.toLowerCase());
|
|
93
|
+
return !parts.includes(".git");
|
|
56
94
|
}
|
|
57
95
|
/** Rules that "[a] allow for this session" would add; undefined when it must always be asked. */
|
|
58
96
|
rulesFor(req) {
|
|
@@ -70,7 +108,7 @@ var Permissions = class {
|
|
|
70
108
|
if (/`|\$\(|[<>]|^\s*[({]/.test(req.target)) return void 0;
|
|
71
109
|
const segments = req.target.split(/&&|\|\||[;&|\n]/).map((s) => s.trim()).filter(Boolean);
|
|
72
110
|
if (!segments.length) return void 0;
|
|
73
|
-
if (segments.some(isAlwaysAsk)) return void 0;
|
|
111
|
+
if (segments.some((s) => /^[({]/.test(s) || isAlwaysAsk(s))) return void 0;
|
|
74
112
|
return [...new Set(segments.map((s) => `bash(${commandKey(s)})`))];
|
|
75
113
|
}
|
|
76
114
|
if (req.kind === "mcp") return [`mcp(${req.tool})`];
|
|
@@ -104,5 +142,6 @@ var Permissions = class {
|
|
|
104
142
|
export {
|
|
105
143
|
MODE_ORDER,
|
|
106
144
|
nextMode,
|
|
145
|
+
requiresSensitiveToolApproval,
|
|
107
146
|
Permissions
|
|
108
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,8 +421,8 @@ 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";
|
|
407
|
-
var FILES = ["AGENTS.md", "BCCLI.md"];
|
|
424
|
+
import { dirname, join as join2 } from "path";
|
|
425
|
+
var FILES = ["AGENTS.md", "CLAUDE.md", "BCCLI.md"];
|
|
408
426
|
function readIf(path) {
|
|
409
427
|
try {
|
|
410
428
|
return existsSync3(path) ? readFileSync(path, "utf8").trim() : void 0;
|
|
@@ -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,
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|