@bike4mind/cli 0.2.28 → 0.2.29-cache-mcp-tools-schema-and-refactor.18897
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.md +1 -1
- package/dist/{artifactExtractor-36XHJYOZ.js → artifactExtractor-NO6YMWJN.js} +1 -1
- package/dist/{chunk-JJBDHUGY.js → chunk-7DZY5UWH.js} +2 -2
- package/dist/{chunk-ZEMWV6IR.js → chunk-AQMAXCNK.js} +2 -2
- package/dist/{chunk-UNOJBVD2.js → chunk-BAYMEX5W.js} +83 -5
- package/dist/{chunk-VGYTNJXN.js → chunk-BPXPHQXQ.js} +15 -15
- package/dist/{chunk-XJRPAAUS.js → chunk-E6AOHC4U.js} +30 -4
- package/dist/{chunk-23T2XGSZ.js → chunk-LBTTUQJM.js} +290 -33
- package/dist/commands/mcpCommand.js +1 -1
- package/dist/{create-NGI7IIQ4.js → create-AMD5NY5D.js} +3 -3
- package/dist/index.js +723 -626
- package/dist/{llmMarkdownGenerator-JDYKJUEC.js → llmMarkdownGenerator-KMLHDAZU.js} +1 -1
- package/dist/{markdownGenerator-QXBC2EW5.js → markdownGenerator-3SQD4T42.js} +1 -1
- package/dist/{mementoService-X35OMHVC.js → mementoService-RQHVW32J.js} +3 -3
- package/dist/{src-6NIYIWZD.js → src-YXJL5FLE.js} +3 -1
- package/dist/{src-CBU6EAO3.js → src-ZEBZX6IS.js} +4 -2
- package/dist/{subtractCredits-RL6JVDMI.js → subtractCredits-JX5MH4ES.js} +3 -3
- package/dist/treeSitterEngine-4SGFQDY3.js +330 -0
- package/package.json +13 -6
package/dist/index.js
CHANGED
|
@@ -5,16 +5,17 @@ import {
|
|
|
5
5
|
getEffectiveApiKey,
|
|
6
6
|
getOpenWeatherKey,
|
|
7
7
|
getSerperKey
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BPXPHQXQ.js";
|
|
9
9
|
import {
|
|
10
|
-
ConfigStore
|
|
11
|
-
|
|
10
|
+
ConfigStore,
|
|
11
|
+
logger
|
|
12
|
+
} from "./chunk-LBTTUQJM.js";
|
|
12
13
|
import {
|
|
13
14
|
selectActiveBackgroundAgents,
|
|
14
15
|
useCliStore
|
|
15
16
|
} from "./chunk-TVW4ZESU.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-7DZY5UWH.js";
|
|
18
|
+
import "./chunk-AQMAXCNK.js";
|
|
18
19
|
import {
|
|
19
20
|
BFLImageService,
|
|
20
21
|
BaseStorage,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
OpenAIBackend,
|
|
27
28
|
OpenAIImageService,
|
|
28
29
|
XAIImageService
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-BAYMEX5W.js";
|
|
30
31
|
import {
|
|
31
32
|
AiEvents,
|
|
32
33
|
ApiKeyEvents,
|
|
@@ -82,32 +83,37 @@ import {
|
|
|
82
83
|
XAI_IMAGE_MODELS,
|
|
83
84
|
b4mLLMTools,
|
|
84
85
|
getMcpProviderMetadata
|
|
85
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-E6AOHC4U.js";
|
|
86
87
|
import {
|
|
87
88
|
Logger
|
|
88
89
|
} from "./chunk-OCYRD7D6.js";
|
|
89
90
|
|
|
90
91
|
// src/index.tsx
|
|
91
|
-
import React21, { useState as
|
|
92
|
+
import React21, { useState as useState10, useEffect as useEffect7, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
92
93
|
import { render, Box as Box20, Text as Text20, useApp, useInput as useInput9 } from "ink";
|
|
93
94
|
import { execSync } from "child_process";
|
|
94
95
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
95
96
|
import { v4 as uuidv411 } from "uuid";
|
|
96
97
|
|
|
97
98
|
// src/components/App.tsx
|
|
98
|
-
import React15, { useState as
|
|
99
|
+
import React15, { useState as useState6, useEffect as useEffect5 } from "react";
|
|
99
100
|
import { Box as Box14, Text as Text14, Static, useInput as useInput6 } from "ink";
|
|
100
101
|
|
|
101
102
|
// src/components/StatusBar.tsx
|
|
102
103
|
import React from "react";
|
|
103
104
|
import { Box, Text } from "ink";
|
|
104
|
-
var StatusBar = React.memo(function StatusBar2({
|
|
105
|
+
var StatusBar = React.memo(function StatusBar2({
|
|
106
|
+
sessionName,
|
|
107
|
+
model,
|
|
108
|
+
tokenUsage,
|
|
109
|
+
creditsUsage
|
|
110
|
+
}) {
|
|
105
111
|
const autoAcceptEdits = useCliStore((state) => state.autoAcceptEdits);
|
|
106
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", width: "100%", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, sessionName), /* @__PURE__ */ React.createElement(Box, { gap: 2 }, autoAcceptEdits && /* @__PURE__ */ React.createElement(Text, { color: "green", bold: true }, "AUTO ACCEPT: Edits"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, tokenUsage.toLocaleString(), " tokens"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, model)));
|
|
112
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", width: "100%", paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, sessionName), /* @__PURE__ */ React.createElement(Box, { gap: 2 }, autoAcceptEdits && /* @__PURE__ */ React.createElement(Text, { color: "green", bold: true }, "AUTO ACCEPT: Edits"), tokenUsage > 0 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, tokenUsage.toLocaleString(), " tokens"), creditsUsage !== void 0 && creditsUsage > 0 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, creditsUsage.toLocaleString(), " ", creditsUsage === 1 ? "credit" : "credits"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, model)));
|
|
107
113
|
});
|
|
108
114
|
|
|
109
115
|
// src/components/InputPrompt.tsx
|
|
110
|
-
import React5, { useState as
|
|
116
|
+
import React5, { useState as useState3, useMemo, useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
111
117
|
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
112
118
|
|
|
113
119
|
// src/components/CustomTextInput.tsx
|
|
@@ -320,65 +326,53 @@ function CommandAutocomplete({ commands, selectedIndex }) {
|
|
|
320
326
|
// src/components/FileAutocomplete.tsx
|
|
321
327
|
import React4 from "react";
|
|
322
328
|
import { Box as Box3, Text as Text4 } from "ink";
|
|
329
|
+
import * as path2 from "path";
|
|
323
330
|
|
|
324
331
|
// src/utils/fileSearch.ts
|
|
325
332
|
import * as fs from "fs";
|
|
326
333
|
import * as path from "path";
|
|
327
|
-
import
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
".
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
".cache",
|
|
336
|
-
"coverage",
|
|
337
|
-
".DS_Store",
|
|
338
|
-
".env",
|
|
339
|
-
".env.local",
|
|
340
|
-
".env.secrets"
|
|
341
|
-
];
|
|
342
|
-
function walkDirectory(basePath = process.cwd(), maxDepth = 5, ignorePatterns = DEFAULT_IGNORE_PATTERNS) {
|
|
343
|
-
const results = [];
|
|
344
|
-
const normalizedBase = path.resolve(basePath);
|
|
345
|
-
function walk(currentPath, depth) {
|
|
346
|
-
if (depth > maxDepth) return;
|
|
334
|
+
import { AsyncFzf } from "fzf";
|
|
335
|
+
import { fdir } from "fdir";
|
|
336
|
+
import ignore from "ignore";
|
|
337
|
+
function loadIgnoreRules(projectRoot) {
|
|
338
|
+
const ig = ignore();
|
|
339
|
+
ig.add(".git/");
|
|
340
|
+
const gitignorePath = path.join(projectRoot, ".gitignore");
|
|
341
|
+
if (fs.existsSync(gitignorePath)) {
|
|
347
342
|
try {
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
if (ignorePatterns.some((pattern) => entry.name === pattern || entry.name.startsWith(pattern))) {
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
const fullPath = path.join(currentPath, entry.name);
|
|
354
|
-
const relativePath = path.relative(normalizedBase, fullPath);
|
|
355
|
-
if (entry.isDirectory()) {
|
|
356
|
-
results.push({
|
|
357
|
-
path: relativePath,
|
|
358
|
-
isDirectory: true
|
|
359
|
-
});
|
|
360
|
-
walk(fullPath, depth + 1);
|
|
361
|
-
} else if (entry.isFile()) {
|
|
362
|
-
try {
|
|
363
|
-
const stats = fs.statSync(fullPath);
|
|
364
|
-
results.push({
|
|
365
|
-
path: relativePath,
|
|
366
|
-
isDirectory: false,
|
|
367
|
-
size: stats.size
|
|
368
|
-
});
|
|
369
|
-
} catch {
|
|
370
|
-
results.push({
|
|
371
|
-
path: relativePath,
|
|
372
|
-
isDirectory: false
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
343
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
|
|
344
|
+
ig.add(gitignoreContent);
|
|
377
345
|
} catch {
|
|
378
346
|
}
|
|
379
347
|
}
|
|
380
|
-
|
|
381
|
-
|
|
348
|
+
return ig;
|
|
349
|
+
}
|
|
350
|
+
function crawlDirectory(projectRoot, maxDepth = 10, maxFiles = 2e4, ig) {
|
|
351
|
+
let fileCount = 0;
|
|
352
|
+
const crawler = new fdir().withRelativePaths().withDirs().withPathSeparator("/").exclude((dirPath) => {
|
|
353
|
+
const relativePath = path.posix.relative(projectRoot, dirPath);
|
|
354
|
+
if (!relativePath || relativePath === ".") {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
const pathWithSlash = `${relativePath}/`;
|
|
358
|
+
return ig.ignores(pathWithSlash);
|
|
359
|
+
}).filter(() => {
|
|
360
|
+
if (fileCount >= maxFiles) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
fileCount++;
|
|
364
|
+
return true;
|
|
365
|
+
});
|
|
366
|
+
if (maxDepth !== void 0) {
|
|
367
|
+
crawler.withMaxDepth(maxDepth);
|
|
368
|
+
}
|
|
369
|
+
const paths = crawler.crawl(projectRoot).sync();
|
|
370
|
+
return paths.filter((p) => {
|
|
371
|
+
if (!p || p === ".") {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
return !ig.ignores(p);
|
|
375
|
+
});
|
|
382
376
|
}
|
|
383
377
|
function formatFileSize(bytes) {
|
|
384
378
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -389,42 +383,112 @@ function formatFileSize(bytes) {
|
|
|
389
383
|
var cachedFiles = null;
|
|
390
384
|
var cacheTimestamp = 0;
|
|
391
385
|
var CACHE_TTL_MS = 3e4;
|
|
392
|
-
function getCachedFiles() {
|
|
386
|
+
function getCachedFiles(projectRoot = process.cwd()) {
|
|
393
387
|
const now = Date.now();
|
|
394
388
|
if (!cachedFiles || now - cacheTimestamp > CACHE_TTL_MS) {
|
|
395
|
-
|
|
389
|
+
const normalizedBase = path.resolve(projectRoot);
|
|
390
|
+
const ig = loadIgnoreRules(normalizedBase);
|
|
391
|
+
cachedFiles = crawlDirectory(normalizedBase, 10, 2e4, ig);
|
|
396
392
|
cacheTimestamp = now;
|
|
397
393
|
}
|
|
398
394
|
return cachedFiles;
|
|
399
395
|
}
|
|
400
|
-
function
|
|
401
|
-
|
|
396
|
+
function warmFileCache() {
|
|
397
|
+
if (!cachedFiles || Date.now() - cacheTimestamp > CACHE_TTL_MS) {
|
|
398
|
+
setImmediate(() => {
|
|
399
|
+
getCachedFiles();
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function listAbsoluteDirectory(absolutePath, filterQuery) {
|
|
404
|
+
try {
|
|
405
|
+
const normalizedPath = path.normalize(absolutePath);
|
|
406
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
const stats = fs.statSync(normalizedPath);
|
|
410
|
+
if (!stats.isDirectory()) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
const entries = fs.readdirSync(normalizedPath, { withFileTypes: true });
|
|
414
|
+
let filteredEntries = entries;
|
|
415
|
+
if (filterQuery && filterQuery.length > 0) {
|
|
416
|
+
const lowerQuery = filterQuery.toLowerCase();
|
|
417
|
+
filteredEntries = entries.filter((entry) => entry.name.toLowerCase().includes(lowerQuery));
|
|
418
|
+
}
|
|
419
|
+
return filteredEntries.slice(0, 15).map((entry) => {
|
|
420
|
+
const fullPath = path.join(normalizedPath, entry.name);
|
|
421
|
+
const result = {
|
|
422
|
+
path: fullPath,
|
|
423
|
+
isDirectory: entry.isDirectory()
|
|
424
|
+
};
|
|
425
|
+
if (entry.isFile()) {
|
|
426
|
+
try {
|
|
427
|
+
const fileStats = fs.statSync(fullPath);
|
|
428
|
+
result.size = fileStats.size;
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return result;
|
|
433
|
+
});
|
|
434
|
+
} catch {
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async function searchFiles(query, maxResults = 20) {
|
|
439
|
+
const projectRoot = process.cwd();
|
|
440
|
+
if (path.isAbsolute(query)) {
|
|
441
|
+
try {
|
|
442
|
+
const stats = fs.statSync(query);
|
|
443
|
+
if (stats.isDirectory()) {
|
|
444
|
+
return listAbsoluteDirectory(query);
|
|
445
|
+
}
|
|
446
|
+
} catch {
|
|
447
|
+
}
|
|
448
|
+
let dirToList;
|
|
449
|
+
let filterQuery;
|
|
450
|
+
if (query.endsWith("/") || query.endsWith(path.sep)) {
|
|
451
|
+
dirToList = query;
|
|
452
|
+
filterQuery = void 0;
|
|
453
|
+
} else {
|
|
454
|
+
dirToList = path.dirname(query);
|
|
455
|
+
filterQuery = path.basename(query);
|
|
456
|
+
}
|
|
457
|
+
return listAbsoluteDirectory(dirToList, filterQuery);
|
|
458
|
+
}
|
|
459
|
+
const files = getCachedFiles(projectRoot);
|
|
402
460
|
if (!query || query.trim() === "") {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
461
|
+
const rootFiles = files.filter((f) => !f.includes("/")).slice(0, maxResults);
|
|
462
|
+
return rootFiles.map((p) => {
|
|
463
|
+
const fullPath = path.join(projectRoot, p);
|
|
464
|
+
try {
|
|
465
|
+
const stats = fs.statSync(fullPath);
|
|
466
|
+
return {
|
|
467
|
+
path: p,
|
|
468
|
+
isDirectory: stats.isDirectory(),
|
|
469
|
+
size: stats.isFile() ? stats.size : void 0
|
|
470
|
+
};
|
|
471
|
+
} catch {
|
|
472
|
+
return { path: p, isDirectory: false };
|
|
473
|
+
}
|
|
412
474
|
});
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
475
|
+
}
|
|
476
|
+
const fzf = new AsyncFzf(files);
|
|
477
|
+
const results = await fzf.find(query);
|
|
478
|
+
return results.slice(0, maxResults).map((result) => {
|
|
479
|
+
const p = result.item;
|
|
480
|
+
const fullPath = path.join(projectRoot, p);
|
|
481
|
+
try {
|
|
482
|
+
const stats = fs.statSync(fullPath);
|
|
483
|
+
return {
|
|
484
|
+
path: p,
|
|
485
|
+
isDirectory: stats.isDirectory(),
|
|
486
|
+
size: stats.isFile() ? stats.size : void 0
|
|
487
|
+
};
|
|
488
|
+
} catch {
|
|
489
|
+
return { path: p, isDirectory: false };
|
|
490
|
+
}
|
|
425
491
|
});
|
|
426
|
-
const results = fuse.search(query);
|
|
427
|
-
return results.slice(0, 20).map((result) => result.item);
|
|
428
492
|
}
|
|
429
493
|
function isPathWithinCwd(filePath) {
|
|
430
494
|
const cwd = path.resolve(process.cwd());
|
|
@@ -508,10 +572,27 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
508
572
|
// src/components/FileAutocomplete.tsx
|
|
509
573
|
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
510
574
|
if (files.length === 0) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
575
|
+
if (path2.isAbsolute(query)) {
|
|
576
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No items in "), /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, query), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, " (or path does not exist)"));
|
|
577
|
+
}
|
|
578
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No matching files", query ? ` for "${query}"` : " in root directory"));
|
|
579
|
+
}
|
|
580
|
+
const VIEWPORT_SIZE = 6;
|
|
581
|
+
const totalFiles = files.length;
|
|
582
|
+
let startIndex = 0;
|
|
583
|
+
let endIndex = Math.min(VIEWPORT_SIZE, totalFiles);
|
|
584
|
+
if (totalFiles > VIEWPORT_SIZE) {
|
|
585
|
+
const halfViewport = Math.floor(VIEWPORT_SIZE / 2);
|
|
586
|
+
startIndex = Math.max(0, selectedIndex - halfViewport);
|
|
587
|
+
endIndex = Math.min(totalFiles, startIndex + VIEWPORT_SIZE);
|
|
588
|
+
if (endIndex === totalFiles) {
|
|
589
|
+
startIndex = Math.max(0, totalFiles - VIEWPORT_SIZE);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const visibleFiles = files.slice(startIndex, endIndex);
|
|
593
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, totalFiles === 1 ? "1 match" : `${totalFiles} matches`, totalFiles > VIEWPORT_SIZE && ` (${selectedIndex + 1}/${totalFiles})`, " - Use up/down to navigate, Tab to select")), visibleFiles.map((file, viewportIndex) => {
|
|
594
|
+
const actualIndex = startIndex + viewportIndex;
|
|
595
|
+
const isSelected = actualIndex === selectedIndex;
|
|
515
596
|
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
516
597
|
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
517
598
|
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
@@ -520,7 +601,7 @@ function FileAutocomplete({ files, selectedIndex, query }) {
|
|
|
520
601
|
}
|
|
521
602
|
|
|
522
603
|
// src/utils/fuzzySearch.ts
|
|
523
|
-
import
|
|
604
|
+
import Fuse from "fuse.js";
|
|
524
605
|
|
|
525
606
|
// src/config/commands.ts
|
|
526
607
|
var COMMANDS = [
|
|
@@ -551,8 +632,9 @@ var COMMANDS = [
|
|
|
551
632
|
args: "<name>"
|
|
552
633
|
},
|
|
553
634
|
{
|
|
554
|
-
name: "
|
|
555
|
-
description: "List saved sessions"
|
|
635
|
+
name: "resume",
|
|
636
|
+
description: "List and resume saved sessions",
|
|
637
|
+
aliases: ["sessions"]
|
|
556
638
|
},
|
|
557
639
|
{
|
|
558
640
|
name: "config",
|
|
@@ -696,13 +778,13 @@ function searchCommands(query, commands = COMMANDS) {
|
|
|
696
778
|
if (!query || query.trim() === "") {
|
|
697
779
|
return commands;
|
|
698
780
|
}
|
|
699
|
-
const fuse = new
|
|
781
|
+
const fuse = new Fuse(commands, fuseOptions);
|
|
700
782
|
const results = fuse.search(query);
|
|
701
783
|
return results.map((result) => result.item);
|
|
702
784
|
}
|
|
703
785
|
|
|
704
786
|
// src/utils/imageDetector.ts
|
|
705
|
-
import { readFileSync, existsSync, statSync as statSync2 } from "fs";
|
|
787
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
706
788
|
import { extname as extname2 } from "path";
|
|
707
789
|
var ImageInputDetector = class {
|
|
708
790
|
static {
|
|
@@ -843,14 +925,14 @@ var ImageInputDetector = class {
|
|
|
843
925
|
let filepath = input.trim();
|
|
844
926
|
filepath = filepath.replace(/^["']|["']$/g, "");
|
|
845
927
|
filepath = filepath.replace(/\\(.)/g, "$1");
|
|
846
|
-
if (!
|
|
928
|
+
if (!existsSync2(filepath)) return null;
|
|
847
929
|
const stats = statSync2(filepath);
|
|
848
930
|
if (!stats.isFile()) return null;
|
|
849
931
|
const ext = extname2(filepath).toLowerCase();
|
|
850
932
|
if (!this.SUPPORTED_EXTENSIONS.includes(ext)) return null;
|
|
851
933
|
if (stats.size > this.MAX_IMAGE_SIZE) return null;
|
|
852
934
|
try {
|
|
853
|
-
const data =
|
|
935
|
+
const data = readFileSync2(filepath);
|
|
854
936
|
const format = ext.substring(1) === "jpeg" ? "jpg" : ext.substring(1);
|
|
855
937
|
const filename = filepath.split("/").pop() || "image";
|
|
856
938
|
return {
|
|
@@ -905,6 +987,21 @@ var ImageInputDetector = class {
|
|
|
905
987
|
}
|
|
906
988
|
};
|
|
907
989
|
|
|
990
|
+
// src/hooks/useDebounce.ts
|
|
991
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
992
|
+
function useDebounce(value, delay = 300) {
|
|
993
|
+
const [debouncedValue, setDebouncedValue] = useState2(value);
|
|
994
|
+
useEffect2(() => {
|
|
995
|
+
const timer = setTimeout(() => {
|
|
996
|
+
setDebouncedValue(value);
|
|
997
|
+
}, delay);
|
|
998
|
+
return () => {
|
|
999
|
+
clearTimeout(timer);
|
|
1000
|
+
};
|
|
1001
|
+
}, [value, delay]);
|
|
1002
|
+
return debouncedValue;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
908
1005
|
// src/components/InputPrompt.tsx
|
|
909
1006
|
function looksLikeFilePath(input) {
|
|
910
1007
|
const trimmed = input.trim();
|
|
@@ -945,20 +1042,20 @@ function InputPrompt({
|
|
|
945
1042
|
const value = useCliStore((state) => state.inputValue);
|
|
946
1043
|
const setInputValue = useCliStore((state) => state.setInputValue);
|
|
947
1044
|
const setValue = setInputValue;
|
|
948
|
-
const [selectedIndex, setSelectedIndex] =
|
|
949
|
-
const [historyIndex, setHistoryIndex] =
|
|
950
|
-
const [tempInput, setTempInput] =
|
|
1045
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1046
|
+
const [historyIndex, setHistoryIndex] = useState3(-1);
|
|
1047
|
+
const [tempInput, setTempInput] = useState3("");
|
|
951
1048
|
const inputKey = useRef2(0);
|
|
952
|
-
|
|
1049
|
+
useEffect3(() => {
|
|
953
1050
|
if (prefillInput) {
|
|
954
1051
|
setValue(prefillInput);
|
|
955
1052
|
onPrefillConsumed?.();
|
|
956
1053
|
}
|
|
957
1054
|
}, [prefillInput, onPrefillConsumed]);
|
|
958
|
-
const [fileAutocomplete, setFileAutocomplete] =
|
|
959
|
-
const [fileSelectedIndex, setFileSelectedIndex] =
|
|
1055
|
+
const [fileAutocomplete, setFileAutocomplete] = useState3(null);
|
|
1056
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState3(0);
|
|
960
1057
|
const isBashMode = value.startsWith("!");
|
|
961
|
-
|
|
1058
|
+
useEffect3(() => {
|
|
962
1059
|
onBashModeChange?.(isBashMode);
|
|
963
1060
|
}, [isBashMode, onBashModeChange]);
|
|
964
1061
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active && !looksLikeFilePath(value);
|
|
@@ -967,14 +1064,32 @@ function InputPrompt({
|
|
|
967
1064
|
if (!shouldShowCommandAutocomplete) return [];
|
|
968
1065
|
return searchCommands(commandQuery, commands);
|
|
969
1066
|
}, [shouldShowCommandAutocomplete, commandQuery, commands]);
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1067
|
+
const debouncedFileQuery = useDebounce(fileAutocomplete?.query ?? "", 200);
|
|
1068
|
+
const [filteredFiles, setFilteredFiles] = useState3([]);
|
|
1069
|
+
useEffect3(() => {
|
|
1070
|
+
if (!fileAutocomplete?.active) {
|
|
1071
|
+
setFilteredFiles([]);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
let cancelled = false;
|
|
1075
|
+
searchFiles(debouncedFileQuery).then((results) => {
|
|
1076
|
+
if (!cancelled) {
|
|
1077
|
+
setFilteredFiles(results);
|
|
1078
|
+
}
|
|
1079
|
+
}).catch((error) => {
|
|
1080
|
+
if (!cancelled) {
|
|
1081
|
+
console.error("File search error:", error);
|
|
1082
|
+
setFilteredFiles([]);
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
return () => {
|
|
1086
|
+
cancelled = true;
|
|
1087
|
+
};
|
|
1088
|
+
}, [fileAutocomplete?.active, debouncedFileQuery]);
|
|
1089
|
+
useEffect3(() => {
|
|
975
1090
|
setSelectedIndex(0);
|
|
976
1091
|
}, [filteredCommands]);
|
|
977
|
-
|
|
1092
|
+
useEffect3(() => {
|
|
978
1093
|
setFileSelectedIndex(0);
|
|
979
1094
|
}, [filteredFiles]);
|
|
980
1095
|
useInput2(
|
|
@@ -1112,11 +1227,11 @@ function InputPrompt({
|
|
|
1112
1227
|
}
|
|
1113
1228
|
};
|
|
1114
1229
|
const getPlaceholder = () => {
|
|
1115
|
-
if (disabled) return "
|
|
1230
|
+
if (disabled) return "";
|
|
1116
1231
|
if (isBashMode) return "Enter shell command to execute...";
|
|
1117
1232
|
return "Type your message, /help for commands, @file to reference, or ! for bash";
|
|
1118
1233
|
};
|
|
1119
|
-
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: isBashMode ? "yellow" : "cyan" }, isBashMode ? "$ " : "> "), /* @__PURE__ */ React5.createElement(
|
|
1234
|
+
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: disabled ? "gray" : isBashMode ? "yellow" : "cyan", dimColor: disabled }, isBashMode ? "$ " : "> "), /* @__PURE__ */ React5.createElement(
|
|
1120
1235
|
CustomTextInput,
|
|
1121
1236
|
{
|
|
1122
1237
|
key: inputKey.current,
|
|
@@ -1139,7 +1254,7 @@ import React6 from "react";
|
|
|
1139
1254
|
import { Box as Box5, Text as Text6 } from "ink";
|
|
1140
1255
|
import Spinner from "ink-spinner";
|
|
1141
1256
|
var ThoughtStream = React6.memo(function ThoughtStream2({ isThinking }) {
|
|
1142
|
-
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column"
|
|
1257
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, isThinking && /* @__PURE__ */ React6.createElement(Box5, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Thinking...")));
|
|
1143
1258
|
});
|
|
1144
1259
|
|
|
1145
1260
|
// src/components/AgentThinking.tsx
|
|
@@ -1212,13 +1327,13 @@ function BackgroundAgentStatus() {
|
|
|
1212
1327
|
}
|
|
1213
1328
|
|
|
1214
1329
|
// src/components/CompletedGroupNotification.tsx
|
|
1215
|
-
import React9, { useEffect as
|
|
1330
|
+
import React9, { useEffect as useEffect4 } from "react";
|
|
1216
1331
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
1217
1332
|
var NOTIFICATION_DISPLAY_DURATION_MS = 3e3;
|
|
1218
1333
|
function CompletedGroupNotification() {
|
|
1219
1334
|
const notifications = useCliStore((state) => state.completedGroupNotifications);
|
|
1220
1335
|
const clearNotifications = useCliStore((state) => state.clearCompletedGroupNotifications);
|
|
1221
|
-
|
|
1336
|
+
useEffect4(() => {
|
|
1222
1337
|
if (notifications.length > 0) {
|
|
1223
1338
|
const timer = setTimeout(() => {
|
|
1224
1339
|
clearNotifications();
|
|
@@ -1231,7 +1346,7 @@ function CompletedGroupNotification() {
|
|
|
1231
1346
|
}
|
|
1232
1347
|
|
|
1233
1348
|
// src/components/PermissionPrompt.tsx
|
|
1234
|
-
import React10, { useState as
|
|
1349
|
+
import React10, { useState as useState4, useCallback } from "react";
|
|
1235
1350
|
import { Box as Box9, Text as Text9, useInput as useInput3 } from "ink";
|
|
1236
1351
|
function renderDiffPreview(preview) {
|
|
1237
1352
|
const lines = preview.split("\n");
|
|
@@ -1264,16 +1379,37 @@ function PermissionPrompt({
|
|
|
1264
1379
|
{ label: "\u2713 Allow once", value: "allow-once" },
|
|
1265
1380
|
{ label: "\u2717 Deny", value: "deny" }
|
|
1266
1381
|
];
|
|
1267
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1268
|
-
const [responded, setResponded] =
|
|
1382
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
1383
|
+
const [responded, setResponded] = useState4(false);
|
|
1269
1384
|
const handleSelect = useCallback(() => {
|
|
1270
1385
|
if (responded) return;
|
|
1271
1386
|
setResponded(true);
|
|
1272
1387
|
onResponse(items[selectedIndex].value);
|
|
1273
1388
|
}, [responded, onResponse, items, selectedIndex]);
|
|
1389
|
+
const handleSelectIndex = useCallback(
|
|
1390
|
+
(index) => {
|
|
1391
|
+
if (responded) return;
|
|
1392
|
+
setResponded(true);
|
|
1393
|
+
onResponse(items[index].value);
|
|
1394
|
+
},
|
|
1395
|
+
[responded, onResponse, items]
|
|
1396
|
+
);
|
|
1274
1397
|
useInput3(
|
|
1275
|
-
(
|
|
1398
|
+
(input, key) => {
|
|
1276
1399
|
if (responded) return;
|
|
1400
|
+
const num = parseInt(input, 10);
|
|
1401
|
+
if (num >= 1 && num <= items.length) {
|
|
1402
|
+
handleSelectIndex(num - 1);
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
if (input.toLowerCase() === "y") {
|
|
1406
|
+
handleSelectIndex(0);
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
if (input.toLowerCase() === "n") {
|
|
1410
|
+
handleSelectIndex(items.length - 1);
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1277
1413
|
if (key.upArrow) {
|
|
1278
1414
|
setSelectedIndex((i) => i > 0 ? i - 1 : items.length - 1);
|
|
1279
1415
|
} else if (key.downArrow) {
|
|
@@ -1288,11 +1424,11 @@ function PermissionPrompt({
|
|
|
1288
1424
|
const rawArgsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
1289
1425
|
const argsString = rawArgsString.length > MAX_ARGS_LENGTH ? rawArgsString.slice(0, MAX_ARGS_LENGTH) + `
|
|
1290
1426
|
... (${rawArgsString.length - MAX_ARGS_LENGTH} more chars)` : rawArgsString;
|
|
1291
|
-
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Tool: "), /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Action: "), /* @__PURE__ */ React10.createElement(Text9, null, toolDescription)), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Arguments:"), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Preview:"), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React10.createElement(Box9, { key: item.value }, /* @__PURE__ */ React10.createElement(Text9, { color: index === selectedIndex ? "cyan" : void 0, bold: index === selectedIndex }, index === selectedIndex ? "\u276F " : "
|
|
1427
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Tool: "), /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Action: "), /* @__PURE__ */ React10.createElement(Text9, null, toolDescription)), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Arguments:"), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { bold: true }, "Preview:"), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React10.createElement(Box9, { key: item.value }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, index + 1, "."), /* @__PURE__ */ React10.createElement(Text9, { color: index === selectedIndex ? "cyan" : void 0, bold: index === selectedIndex }, index === selectedIndex ? " \u276F " : " ", item.label)))), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Press 1-", items.length, ", y/n, or \u2191\u2193 + Enter")));
|
|
1292
1428
|
}
|
|
1293
1429
|
|
|
1294
1430
|
// src/components/ConfigEditor.tsx
|
|
1295
|
-
import React11, { useState as
|
|
1431
|
+
import React11, { useState as useState5, useMemo as useMemo3 } from "react";
|
|
1296
1432
|
import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
|
|
1297
1433
|
var MAX_ITERATIONS_OPTIONS = [
|
|
1298
1434
|
{ label: "10", value: 10 },
|
|
@@ -1437,10 +1573,10 @@ function buildConfigItems(availableModels) {
|
|
|
1437
1573
|
return items;
|
|
1438
1574
|
}
|
|
1439
1575
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
1440
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1441
|
-
const [editedConfig, setEditedConfig] =
|
|
1442
|
-
const [saveError, setSaveError] =
|
|
1443
|
-
const [isSaving, setIsSaving] =
|
|
1576
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
1577
|
+
const [editedConfig, setEditedConfig] = useState5(config);
|
|
1578
|
+
const [saveError, setSaveError] = useState5(null);
|
|
1579
|
+
const [isSaving, setIsSaving] = useState5(false);
|
|
1444
1580
|
const configItems = useMemo3(() => buildConfigItems(availableModels), [availableModels]);
|
|
1445
1581
|
const hasChanges = useMemo3(() => {
|
|
1446
1582
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
@@ -1707,38 +1843,26 @@ function truncateValue(value, maxLength) {
|
|
|
1707
1843
|
}
|
|
1708
1844
|
return str.slice(0, maxLength) + "...";
|
|
1709
1845
|
}
|
|
1710
|
-
function getRoleDisplay(role) {
|
|
1711
|
-
switch (role) {
|
|
1712
|
-
case "user":
|
|
1713
|
-
return { color: "cyan", label: "\u{1F464} You" };
|
|
1714
|
-
case "system":
|
|
1715
|
-
return { color: "gray", label: "\u2139\uFE0F System" };
|
|
1716
|
-
default:
|
|
1717
|
-
return { color: "green", label: "\u{1F916} Assistant" };
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
1846
|
var MessageItem = React14.memo(function MessageItem2({ message }) {
|
|
1721
1847
|
const isUser = message.role === "user";
|
|
1722
|
-
|
|
1723
|
-
const roleDisplay = getRoleDisplay(message.role);
|
|
1724
|
-
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", marginBottom: 1 }, !isContinuation && /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { bold: true, color: roleDisplay.color }, roleDisplay.label), isUser && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1848
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column" }, isUser && message.content && /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { backgroundColor: "whiteBright", color: "black" }, "\u276F ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.filter((s) => ["thought", "action"].includes(s.type)).length > 0 && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, flexDirection: "column", marginBottom: 1 }, message.metadata.steps.map((step, idx) => {
|
|
1725
1849
|
if (step.type === "thought") {
|
|
1726
|
-
return /* @__PURE__ */ React14.createElement(Box13, { key: idx,
|
|
1850
|
+
return /* @__PURE__ */ React14.createElement(Box13, { key: idx, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` ${step.content}`));
|
|
1727
1851
|
}
|
|
1728
1852
|
if (step.type === "action") {
|
|
1729
1853
|
const toolName = step.metadata?.toolName || "unknown";
|
|
1730
1854
|
const toolInput = step.metadata?.toolInput;
|
|
1731
1855
|
const observationStep = message.metadata?.steps?.[idx + 1];
|
|
1732
1856
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1733
|
-
return /* @__PURE__ */ React14.createElement(Box13, { key: idx,
|
|
1857
|
+
return /* @__PURE__ */ React14.createElement(Box13, { key: idx, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u{1F527} ", toolName), toolInput && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Input: ${truncateValue(toolInput, 100)}`), result && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Result: ${truncateValue(result, 200)}`));
|
|
1734
1858
|
}
|
|
1735
1859
|
return null;
|
|
1736
|
-
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2,
|
|
1860
|
+
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginBottom: 1 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React14.createElement(MarkdownRenderer, { content: message.content })), !isUser && message.content !== "..." && message.metadata && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginBottom: 1 }, (message.metadata.tokenUsage?.total || message.metadata.creditsUsed) && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "(", message.metadata.tokenUsage?.total ? `${message.metadata.tokenUsage.total.toLocaleString()} tokens` : "", message.metadata.creditsUsed && message.metadata.creditsUsed > 0 ? (message.metadata.tokenUsage?.total ? " \u2022 " : "") + `used ${message.metadata.creditsUsed.toLocaleString()} ${message.metadata.creditsUsed === 1 ? "credit" : "credits"}` : "", ")")));
|
|
1737
1861
|
});
|
|
1738
1862
|
|
|
1739
1863
|
// src/utils/processFileReferences.ts
|
|
1740
1864
|
import * as fs2 from "fs";
|
|
1741
|
-
import * as
|
|
1865
|
+
import * as path3 from "path";
|
|
1742
1866
|
|
|
1743
1867
|
// src/utils/constants.ts
|
|
1744
1868
|
var NAME_SUFFIXES = ["jr", "sr", "ii", "iii", "iv", "v", "phd", "md", "esq"];
|
|
@@ -1749,7 +1873,7 @@ function isNameSuffix(value) {
|
|
|
1749
1873
|
// src/utils/processFileReferences.ts
|
|
1750
1874
|
var FILE_REFERENCE_REGEX = /(?:^|\s)@([^\s@]+)/g;
|
|
1751
1875
|
function looksLikeFilePath2(ref) {
|
|
1752
|
-
if (ref.includes("/") || ref.includes(
|
|
1876
|
+
if (ref.includes("/") || ref.includes(path3.sep)) {
|
|
1753
1877
|
return true;
|
|
1754
1878
|
}
|
|
1755
1879
|
const extensionMatch = /\.(\w+)$/.exec(ref);
|
|
@@ -1779,9 +1903,13 @@ function extractFileReferences(message) {
|
|
|
1779
1903
|
}
|
|
1780
1904
|
function readFileContents(filePath) {
|
|
1781
1905
|
const cwd = process.cwd();
|
|
1782
|
-
const
|
|
1783
|
-
if (
|
|
1784
|
-
return { error: `Security: Path "${filePath}"
|
|
1906
|
+
const isAbsolutePath = path3.isAbsolute(filePath);
|
|
1907
|
+
if (filePath.includes("..")) {
|
|
1908
|
+
return { error: `Security: Path traversal detected in "${filePath}"` };
|
|
1909
|
+
}
|
|
1910
|
+
const absolutePath = isAbsolutePath ? path3.normalize(filePath) : path3.resolve(cwd, filePath);
|
|
1911
|
+
if (!isAbsolutePath && !isPathWithinCwd(filePath)) {
|
|
1912
|
+
return { error: `Security: Relative path "${filePath}" escapes the current working directory` };
|
|
1785
1913
|
}
|
|
1786
1914
|
if (!fs2.existsSync(absolutePath)) {
|
|
1787
1915
|
return { error: `File not found: "${filePath}"` };
|
|
@@ -1872,6 +2000,7 @@ function App({
|
|
|
1872
2000
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
1873
2001
|
const currentModel = useCliStore((state) => state.session?.model || "claude-sonnet-4-5-20250929");
|
|
1874
2002
|
const totalTokens = useCliStore((state) => state.session?.metadata.totalTokens || 0);
|
|
2003
|
+
const totalCredits = useCliStore((state) => state.session?.metadata.totalCredits);
|
|
1875
2004
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
1876
2005
|
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
1877
2006
|
const showConfigEditor = useCliStore((state) => state.showConfigEditor);
|
|
@@ -1882,7 +2011,7 @@ function App({
|
|
|
1882
2011
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1883
2012
|
const pendingBackgroundTrigger = useCliStore((state) => state.pendingBackgroundTrigger);
|
|
1884
2013
|
const setPendingBackgroundTrigger = useCliStore((state) => state.setPendingBackgroundTrigger);
|
|
1885
|
-
|
|
2014
|
+
useEffect5(() => {
|
|
1886
2015
|
if (pendingBackgroundTrigger && !isThinking && onBackgroundCompletion) {
|
|
1887
2016
|
setPendingBackgroundTrigger(false);
|
|
1888
2017
|
onBackgroundCompletion();
|
|
@@ -1894,7 +2023,7 @@ function App({
|
|
|
1894
2023
|
toggleAutoAcceptEdits();
|
|
1895
2024
|
}
|
|
1896
2025
|
});
|
|
1897
|
-
const [isBashMode, setIsBashMode] =
|
|
2026
|
+
const [isBashMode, setIsBashMode] = useState6(false);
|
|
1898
2027
|
const handleSubmit = React15.useCallback(
|
|
1899
2028
|
async (input) => {
|
|
1900
2029
|
const trimmed = input.trim();
|
|
@@ -1957,7 +2086,15 @@ ${errorBlock}`;
|
|
|
1957
2086
|
onPrefillConsumed,
|
|
1958
2087
|
onBashModeChange: setIsBashMode
|
|
1959
2088
|
}
|
|
1960
|
-
)), /* @__PURE__ */ React15.createElement(
|
|
2089
|
+
)), /* @__PURE__ */ React15.createElement(
|
|
2090
|
+
StatusBar,
|
|
2091
|
+
{
|
|
2092
|
+
sessionName,
|
|
2093
|
+
model: currentModel,
|
|
2094
|
+
tokenUsage: totalTokens,
|
|
2095
|
+
creditsUsage: totalCredits
|
|
2096
|
+
}
|
|
2097
|
+
)));
|
|
1961
2098
|
}
|
|
1962
2099
|
|
|
1963
2100
|
// src/components/MessageList.tsx
|
|
@@ -2033,12 +2170,12 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
2033
2170
|
}
|
|
2034
2171
|
|
|
2035
2172
|
// src/components/RewindSelector.tsx
|
|
2036
|
-
import React18, { useState as
|
|
2173
|
+
import React18, { useState as useState7 } from "react";
|
|
2037
2174
|
import { Box as Box17, Text as Text17, useInput as useInput7 } from "ink";
|
|
2038
2175
|
import SelectInput2 from "ink-select-input";
|
|
2039
2176
|
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
2040
|
-
const [step, setStep] =
|
|
2041
|
-
const [selectedMessageIndex, setSelectedMessageIndex] =
|
|
2177
|
+
const [step, setStep] = useState7("selection");
|
|
2178
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState7(null);
|
|
2042
2179
|
useInput7((input, key) => {
|
|
2043
2180
|
if (key.escape) {
|
|
2044
2181
|
if (step === "confirmation") {
|
|
@@ -2102,12 +2239,12 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
2102
2239
|
}
|
|
2103
2240
|
|
|
2104
2241
|
// src/components/SessionSelector.tsx
|
|
2105
|
-
import React19, { useState as
|
|
2242
|
+
import React19, { useState as useState8 } from "react";
|
|
2106
2243
|
import { Box as Box18, Text as Text18, useInput as useInput8 } from "ink";
|
|
2107
2244
|
import SelectInput3 from "ink-select-input";
|
|
2108
2245
|
function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
2109
|
-
const [step, setStep] =
|
|
2110
|
-
const [selectedSession, setSelectedSession] =
|
|
2246
|
+
const [step, setStep] = useState8("selection");
|
|
2247
|
+
const [selectedSession, setSelectedSession] = useState8(null);
|
|
2111
2248
|
useInput8((_input, key) => {
|
|
2112
2249
|
if (key.escape) {
|
|
2113
2250
|
if (step === "confirmation") {
|
|
@@ -2163,7 +2300,7 @@ function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
|
2163
2300
|
{
|
|
2164
2301
|
items,
|
|
2165
2302
|
onSelect: handleSelectionSelect,
|
|
2166
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 },
|
|
2303
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 }, label))
|
|
2167
2304
|
}
|
|
2168
2305
|
), /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
2169
2306
|
}
|
|
@@ -2176,13 +2313,13 @@ function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
|
2176
2313
|
{
|
|
2177
2314
|
items: confirmationItems,
|
|
2178
2315
|
onSelect: handleConfirmationSelect,
|
|
2179
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 },
|
|
2316
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Text18, { color: isSelected ? "cyan" : void 0 }, label))
|
|
2180
2317
|
}
|
|
2181
2318
|
), /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
2182
2319
|
}
|
|
2183
2320
|
|
|
2184
2321
|
// src/components/LoginFlow.tsx
|
|
2185
|
-
import React20, { useState as
|
|
2322
|
+
import React20, { useState as useState9, useEffect as useEffect6 } from "react";
|
|
2186
2323
|
import { Box as Box19, Text as Text19 } from "ink";
|
|
2187
2324
|
import Spinner3 from "ink-spinner";
|
|
2188
2325
|
import jwt from "jsonwebtoken";
|
|
@@ -2299,11 +2436,11 @@ var OAuthClient = class {
|
|
|
2299
2436
|
|
|
2300
2437
|
// src/components/LoginFlow.tsx
|
|
2301
2438
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
2302
|
-
const [status, setStatus] =
|
|
2303
|
-
const [deviceFlow, setDeviceFlow] =
|
|
2304
|
-
const [statusMessage, setStatusMessage] =
|
|
2305
|
-
const [error, setError] =
|
|
2306
|
-
|
|
2439
|
+
const [status, setStatus] = useState9("initiating");
|
|
2440
|
+
const [deviceFlow, setDeviceFlow] = useState9(null);
|
|
2441
|
+
const [statusMessage, setStatusMessage] = useState9("Initiating device authorization...");
|
|
2442
|
+
const [error, setError] = useState9(null);
|
|
2443
|
+
useEffect6(() => {
|
|
2307
2444
|
const runLoginFlow = async () => {
|
|
2308
2445
|
const oauth = new OAuthClient(apiUrl);
|
|
2309
2446
|
try {
|
|
@@ -2338,7 +2475,7 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
2338
2475
|
};
|
|
2339
2476
|
runLoginFlow();
|
|
2340
2477
|
}, [apiUrl, configStore, onSuccess, onError]);
|
|
2341
|
-
|
|
2478
|
+
useEffect6(() => {
|
|
2342
2479
|
if (deviceFlow && status === "waiting") {
|
|
2343
2480
|
open(deviceFlow.verification_uri_complete).catch((err) => {
|
|
2344
2481
|
console.error("Failed to auto-open browser:", err);
|
|
@@ -2359,12 +2496,12 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
2359
2496
|
|
|
2360
2497
|
// src/storage/SessionStore.ts
|
|
2361
2498
|
import { promises as fs3 } from "fs";
|
|
2362
|
-
import
|
|
2499
|
+
import path4 from "path";
|
|
2363
2500
|
import { homedir } from "os";
|
|
2364
2501
|
import { v4 as uuidv4 } from "uuid";
|
|
2365
2502
|
var SessionStore = class {
|
|
2366
2503
|
constructor(basePath) {
|
|
2367
|
-
this.basePath = basePath ||
|
|
2504
|
+
this.basePath = basePath || path4.join(homedir(), ".bike4mind", "sessions");
|
|
2368
2505
|
}
|
|
2369
2506
|
/**
|
|
2370
2507
|
* Initialize storage directory
|
|
@@ -2385,7 +2522,7 @@ var SessionStore = class {
|
|
|
2385
2522
|
throw new Error("Cannot save session with no messages");
|
|
2386
2523
|
}
|
|
2387
2524
|
await this.init();
|
|
2388
|
-
const filePath =
|
|
2525
|
+
const filePath = path4.join(this.basePath, `${session.id}.json`);
|
|
2389
2526
|
try {
|
|
2390
2527
|
await fs3.writeFile(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
2391
2528
|
} catch (error) {
|
|
@@ -2397,7 +2534,7 @@ var SessionStore = class {
|
|
|
2397
2534
|
* Load a session from disk by ID
|
|
2398
2535
|
*/
|
|
2399
2536
|
async load(id) {
|
|
2400
|
-
const filePath =
|
|
2537
|
+
const filePath = path4.join(this.basePath, `${id}.json`);
|
|
2401
2538
|
try {
|
|
2402
2539
|
const data = await fs3.readFile(filePath, "utf-8");
|
|
2403
2540
|
const session = JSON.parse(data);
|
|
@@ -2435,7 +2572,7 @@ var SessionStore = class {
|
|
|
2435
2572
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
2436
2573
|
const sessionsWithFiles = await Promise.all(
|
|
2437
2574
|
jsonFiles.map(async (file) => {
|
|
2438
|
-
const filePath =
|
|
2575
|
+
const filePath = path4.join(this.basePath, file);
|
|
2439
2576
|
const data = await fs3.readFile(filePath, "utf-8");
|
|
2440
2577
|
const session = JSON.parse(data);
|
|
2441
2578
|
session.messages = session.messages.map((msg) => {
|
|
@@ -2470,7 +2607,7 @@ var SessionStore = class {
|
|
|
2470
2607
|
* Delete a session
|
|
2471
2608
|
*/
|
|
2472
2609
|
async delete(id) {
|
|
2473
|
-
const filePath =
|
|
2610
|
+
const filePath = path4.join(this.basePath, `${id}.json`);
|
|
2474
2611
|
try {
|
|
2475
2612
|
await fs3.unlink(filePath);
|
|
2476
2613
|
return true;
|
|
@@ -2509,19 +2646,19 @@ var SessionStore = class {
|
|
|
2509
2646
|
|
|
2510
2647
|
// src/storage/CommandHistoryStore.ts
|
|
2511
2648
|
import { promises as fs4 } from "fs";
|
|
2512
|
-
import
|
|
2649
|
+
import path5 from "path";
|
|
2513
2650
|
import { homedir as homedir2 } from "os";
|
|
2514
2651
|
var MAX_HISTORY_ENTRIES = 1e3;
|
|
2515
2652
|
var CommandHistoryStore = class {
|
|
2516
2653
|
constructor(historyPath) {
|
|
2517
2654
|
this.history = null;
|
|
2518
|
-
this.historyPath = historyPath ||
|
|
2655
|
+
this.historyPath = historyPath || path5.join(homedir2(), ".bike4mind", "history.jsonl");
|
|
2519
2656
|
}
|
|
2520
2657
|
/**
|
|
2521
2658
|
* Initialize history directory
|
|
2522
2659
|
*/
|
|
2523
2660
|
async init() {
|
|
2524
|
-
const dir =
|
|
2661
|
+
const dir = path5.dirname(this.historyPath);
|
|
2525
2662
|
try {
|
|
2526
2663
|
await fs4.mkdir(dir, { recursive: true });
|
|
2527
2664
|
} catch (error) {
|
|
@@ -2623,271 +2760,13 @@ var CommandHistoryStore = class {
|
|
|
2623
2760
|
};
|
|
2624
2761
|
|
|
2625
2762
|
// src/storage/CustomCommandStore.ts
|
|
2626
|
-
import
|
|
2763
|
+
import fs5 from "fs/promises";
|
|
2627
2764
|
import path6 from "path";
|
|
2628
|
-
import
|
|
2765
|
+
import os from "os";
|
|
2629
2766
|
|
|
2630
2767
|
// src/utils/commandParser.ts
|
|
2631
2768
|
import matter from "gray-matter";
|
|
2632
2769
|
import { z } from "zod";
|
|
2633
|
-
|
|
2634
|
-
// src/utils/Logger.ts
|
|
2635
|
-
import fs5 from "fs/promises";
|
|
2636
|
-
import path5 from "path";
|
|
2637
|
-
import os from "os";
|
|
2638
|
-
var Logger2 = class _Logger {
|
|
2639
|
-
constructor() {
|
|
2640
|
-
this.logFilePath = null;
|
|
2641
|
-
this.sessionId = null;
|
|
2642
|
-
this.fileLoggingEnabled = true;
|
|
2643
|
-
this.consoleVerbose = false;
|
|
2644
|
-
}
|
|
2645
|
-
static {
|
|
2646
|
-
this.instance = null;
|
|
2647
|
-
}
|
|
2648
|
-
static getInstance() {
|
|
2649
|
-
if (!_Logger.instance) {
|
|
2650
|
-
_Logger.instance = new _Logger();
|
|
2651
|
-
}
|
|
2652
|
-
return _Logger.instance;
|
|
2653
|
-
}
|
|
2654
|
-
/**
|
|
2655
|
-
* Initialize the logger with a session ID
|
|
2656
|
-
*/
|
|
2657
|
-
async initialize(sessionId) {
|
|
2658
|
-
this.sessionId = sessionId;
|
|
2659
|
-
const debugDir = path5.join(os.homedir(), ".bike4mind", "debug");
|
|
2660
|
-
await fs5.mkdir(debugDir, { recursive: true });
|
|
2661
|
-
this.logFilePath = path5.join(debugDir, `${sessionId}.txt`);
|
|
2662
|
-
await this.writeToFile("INFO", "=== CLI SESSION START ===");
|
|
2663
|
-
}
|
|
2664
|
-
/**
|
|
2665
|
-
* Set whether verbose console logging is enabled
|
|
2666
|
-
*/
|
|
2667
|
-
setVerbose(enabled) {
|
|
2668
|
-
this.consoleVerbose = enabled;
|
|
2669
|
-
}
|
|
2670
|
-
/**
|
|
2671
|
-
* Set whether file logging is enabled
|
|
2672
|
-
*/
|
|
2673
|
-
setFileLoggingEnabled(enabled) {
|
|
2674
|
-
this.fileLoggingEnabled = enabled;
|
|
2675
|
-
}
|
|
2676
|
-
/**
|
|
2677
|
-
* DEBUG level - verbose-only console, always file
|
|
2678
|
-
*/
|
|
2679
|
-
debug(message) {
|
|
2680
|
-
this.writeToFile("DEBUG", message).catch(() => {
|
|
2681
|
-
});
|
|
2682
|
-
if (this.consoleVerbose) {
|
|
2683
|
-
console.log(message);
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
/**
|
|
2687
|
-
* INFO level - always shown to user
|
|
2688
|
-
*/
|
|
2689
|
-
info(message) {
|
|
2690
|
-
this.writeToFile("INFO", message).catch(() => {
|
|
2691
|
-
});
|
|
2692
|
-
console.log(message);
|
|
2693
|
-
}
|
|
2694
|
-
/**
|
|
2695
|
-
* WARN level - always shown to user
|
|
2696
|
-
*/
|
|
2697
|
-
warn(message) {
|
|
2698
|
-
this.writeToFile("WARN", message).catch(() => {
|
|
2699
|
-
});
|
|
2700
|
-
console.warn(message);
|
|
2701
|
-
}
|
|
2702
|
-
/**
|
|
2703
|
-
* ERROR level - always shown to user
|
|
2704
|
-
*/
|
|
2705
|
-
error(message, err) {
|
|
2706
|
-
this.writeToFile("ERROR", message).catch(() => {
|
|
2707
|
-
});
|
|
2708
|
-
if (err) {
|
|
2709
|
-
this.logErrorDetailsToFile(err).catch(() => {
|
|
2710
|
-
});
|
|
2711
|
-
}
|
|
2712
|
-
console.error(message);
|
|
2713
|
-
}
|
|
2714
|
-
/**
|
|
2715
|
-
* Write log entry to file
|
|
2716
|
-
*/
|
|
2717
|
-
async writeToFile(level, message) {
|
|
2718
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
2719
|
-
return;
|
|
2720
|
-
}
|
|
2721
|
-
try {
|
|
2722
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
|
|
2723
|
-
const logEntry = `[${timestamp}] [${level}] ${message}
|
|
2724
|
-
`;
|
|
2725
|
-
await fs5.appendFile(this.logFilePath, logEntry, "utf-8");
|
|
2726
|
-
} catch (error) {
|
|
2727
|
-
console.error("File logging failed:", error);
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
/**
|
|
2731
|
-
* Log error details to file
|
|
2732
|
-
*/
|
|
2733
|
-
async logErrorDetailsToFile(err) {
|
|
2734
|
-
if (!this.fileLoggingEnabled || !this.logFilePath) {
|
|
2735
|
-
return;
|
|
2736
|
-
}
|
|
2737
|
-
try {
|
|
2738
|
-
if (err && typeof err === "object" && "response" in err && err.response) {
|
|
2739
|
-
const response = err.response;
|
|
2740
|
-
const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
|
|
2741
|
-
await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
|
|
2742
|
-
await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
|
|
2743
|
-
await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
|
|
2744
|
-
if (response.data) {
|
|
2745
|
-
const errorText = this.extractErrorMessage(response.data);
|
|
2746
|
-
if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
|
|
2747
|
-
await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
|
|
2748
|
-
const parsedError = this.parseHtmlError(errorText);
|
|
2749
|
-
if (parsedError) {
|
|
2750
|
-
await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
|
|
2751
|
-
}
|
|
2752
|
-
await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
|
|
2753
|
-
} else {
|
|
2754
|
-
const preview = this.truncate(errorText, 500);
|
|
2755
|
-
await this.writeToFile("ERROR", ` Response: ${preview}`);
|
|
2756
|
-
}
|
|
2757
|
-
} else {
|
|
2758
|
-
await this.writeToFile("ERROR", ` Response: (no data)`);
|
|
2759
|
-
}
|
|
2760
|
-
}
|
|
2761
|
-
if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
|
|
2762
|
-
const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
|
|
2763
|
-
await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
|
|
2764
|
-
} else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
2765
|
-
await this.writeToFile("ERROR", ` Message: ${err.message}`);
|
|
2766
|
-
}
|
|
2767
|
-
} catch (error) {
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
/**
|
|
2771
|
-
* Safely stringify object, handling circular references
|
|
2772
|
-
*/
|
|
2773
|
-
safeStringify(obj) {
|
|
2774
|
-
try {
|
|
2775
|
-
return JSON.stringify(obj);
|
|
2776
|
-
} catch (error) {
|
|
2777
|
-
if (error instanceof Error && error.message.includes("circular")) {
|
|
2778
|
-
try {
|
|
2779
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
2780
|
-
return JSON.stringify(obj, (key, value) => {
|
|
2781
|
-
if (typeof value === "object" && value !== null) {
|
|
2782
|
-
if (seen.has(value)) {
|
|
2783
|
-
return "[Circular]";
|
|
2784
|
-
}
|
|
2785
|
-
seen.add(value);
|
|
2786
|
-
}
|
|
2787
|
-
return value;
|
|
2788
|
-
});
|
|
2789
|
-
} catch {
|
|
2790
|
-
return "[Unable to stringify]";
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
return "[Stringify error]";
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
/**
|
|
2797
|
-
* Extract readable text from error response data
|
|
2798
|
-
*/
|
|
2799
|
-
extractErrorMessage(data) {
|
|
2800
|
-
if (Buffer.isBuffer(data)) {
|
|
2801
|
-
return data.toString("utf-8");
|
|
2802
|
-
}
|
|
2803
|
-
if (typeof data === "string") {
|
|
2804
|
-
return data;
|
|
2805
|
-
}
|
|
2806
|
-
if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
|
|
2807
|
-
const chunks = [];
|
|
2808
|
-
for (const chunk of data._readableState.buffer) {
|
|
2809
|
-
if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
|
|
2810
|
-
chunks.push(Buffer.from(chunk.data));
|
|
2811
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
2812
|
-
chunks.push(chunk);
|
|
2813
|
-
} else if (chunk && typeof chunk === "object" && "data" in chunk) {
|
|
2814
|
-
if (Buffer.isBuffer(chunk.data)) {
|
|
2815
|
-
chunks.push(chunk.data);
|
|
2816
|
-
} else if (Array.isArray(chunk.data)) {
|
|
2817
|
-
chunks.push(Buffer.from(chunk.data));
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
if (chunks.length > 0) {
|
|
2822
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
return this.safeStringify(data);
|
|
2826
|
-
}
|
|
2827
|
-
/**
|
|
2828
|
-
* Parse HTML error page to extract error message
|
|
2829
|
-
*/
|
|
2830
|
-
parseHtmlError(html) {
|
|
2831
|
-
const titleMatch = html.match(/<title>(.*?)<\/title>/i);
|
|
2832
|
-
const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
|
|
2833
|
-
const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
|
|
2834
|
-
if (titleMatch && titleMatch[1] !== "Error") {
|
|
2835
|
-
return titleMatch[1].trim();
|
|
2836
|
-
}
|
|
2837
|
-
if (h1Match) {
|
|
2838
|
-
return h1Match[1].trim();
|
|
2839
|
-
}
|
|
2840
|
-
if (bodyMatch) {
|
|
2841
|
-
const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
2842
|
-
return text.substring(0, 200);
|
|
2843
|
-
}
|
|
2844
|
-
return null;
|
|
2845
|
-
}
|
|
2846
|
-
/**
|
|
2847
|
-
* Truncate string to max length with ellipsis
|
|
2848
|
-
*/
|
|
2849
|
-
truncate(str, maxLength) {
|
|
2850
|
-
if (str.length <= maxLength) {
|
|
2851
|
-
return str;
|
|
2852
|
-
}
|
|
2853
|
-
return str.substring(0, maxLength) + "... [truncated]";
|
|
2854
|
-
}
|
|
2855
|
-
/**
|
|
2856
|
-
* Format bytes to human-readable size
|
|
2857
|
-
*/
|
|
2858
|
-
formatBytes(bytes) {
|
|
2859
|
-
if (bytes === 0) return "0 B";
|
|
2860
|
-
const k = 1024;
|
|
2861
|
-
const sizes = ["B", "KB", "MB", "GB"];
|
|
2862
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
2863
|
-
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
2864
|
-
}
|
|
2865
|
-
/**
|
|
2866
|
-
* Clean up old debug logs (older than 30 days)
|
|
2867
|
-
*/
|
|
2868
|
-
async cleanupOldLogs() {
|
|
2869
|
-
if (!this.fileLoggingEnabled) return;
|
|
2870
|
-
try {
|
|
2871
|
-
const debugDir = path5.join(os.homedir(), ".bike4mind", "debug");
|
|
2872
|
-
const files = await fs5.readdir(debugDir);
|
|
2873
|
-
const now = Date.now();
|
|
2874
|
-
const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
|
|
2875
|
-
for (const file of files) {
|
|
2876
|
-
const filePath = path5.join(debugDir, file);
|
|
2877
|
-
const stats = await fs5.stat(filePath);
|
|
2878
|
-
if (stats.mtime.getTime() < thirtyDaysAgo) {
|
|
2879
|
-
await fs5.unlink(filePath);
|
|
2880
|
-
}
|
|
2881
|
-
}
|
|
2882
|
-
} catch (error) {
|
|
2883
|
-
console.error("Failed to cleanup old logs:", error);
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
};
|
|
2887
|
-
var logger = Logger2.getInstance();
|
|
2888
|
-
logger.setVerbose(process.env.B4M_VERBOSE === "1");
|
|
2889
|
-
|
|
2890
|
-
// src/utils/commandParser.ts
|
|
2891
2770
|
var flexibleString = z.union([z.string(), z.array(z.string())]).transform((val) => Array.isArray(val) ? val.join(" ") : val).optional();
|
|
2892
2771
|
var flexibleStringArray = z.union([z.string(), z.array(z.string())]).transform((val) => Array.isArray(val) ? val : [val]).optional();
|
|
2893
2772
|
function needsQuoting(value) {
|
|
@@ -3042,7 +2921,7 @@ function extractCommandName(filename) {
|
|
|
3042
2921
|
var CustomCommandStore = class {
|
|
3043
2922
|
constructor(projectRoot) {
|
|
3044
2923
|
this.commands = /* @__PURE__ */ new Map();
|
|
3045
|
-
const home =
|
|
2924
|
+
const home = os.homedir();
|
|
3046
2925
|
const root = projectRoot || process.cwd();
|
|
3047
2926
|
this.globalCommandsDirs = [
|
|
3048
2927
|
path6.join(home, ".bike4mind", "commands"),
|
|
@@ -3077,7 +2956,7 @@ var CustomCommandStore = class {
|
|
|
3077
2956
|
*/
|
|
3078
2957
|
async loadCommandsFromDirectory(directory, source) {
|
|
3079
2958
|
try {
|
|
3080
|
-
const stats = await
|
|
2959
|
+
const stats = await fs5.stat(directory);
|
|
3081
2960
|
if (!stats.isDirectory()) {
|
|
3082
2961
|
return;
|
|
3083
2962
|
}
|
|
@@ -3110,7 +2989,7 @@ var CustomCommandStore = class {
|
|
|
3110
2989
|
async findCommandFiles(directory) {
|
|
3111
2990
|
const files = [];
|
|
3112
2991
|
try {
|
|
3113
|
-
const entries = await
|
|
2992
|
+
const entries = await fs5.readdir(directory, { withFileTypes: true });
|
|
3114
2993
|
for (const entry of entries) {
|
|
3115
2994
|
const fullPath = path6.join(directory, entry.name);
|
|
3116
2995
|
if (entry.isDirectory()) {
|
|
@@ -3139,7 +3018,7 @@ var CustomCommandStore = class {
|
|
|
3139
3018
|
console.warn(`Invalid command filename: ${filename} (must end with .md and have valid name)`);
|
|
3140
3019
|
return;
|
|
3141
3020
|
}
|
|
3142
|
-
const fileContent = await
|
|
3021
|
+
const fileContent = await fs5.readFile(filePath, "utf-8");
|
|
3143
3022
|
const command = parseCommandFile(fileContent, filePath, commandName, source);
|
|
3144
3023
|
this.commands.set(commandName, command);
|
|
3145
3024
|
}
|
|
@@ -3214,14 +3093,14 @@ var CustomCommandStore = class {
|
|
|
3214
3093
|
async createCommandFile(name, isGlobal = false) {
|
|
3215
3094
|
const targetDir = isGlobal ? this.globalCommandsDirs[0] : this.projectCommandsDirs[0];
|
|
3216
3095
|
const filePath = path6.join(targetDir, `${name}.md`);
|
|
3217
|
-
const fileExists = await
|
|
3096
|
+
const fileExists = await fs5.access(filePath).then(
|
|
3218
3097
|
() => true,
|
|
3219
3098
|
() => false
|
|
3220
3099
|
);
|
|
3221
3100
|
if (fileExists) {
|
|
3222
3101
|
throw new Error(`Command file already exists: ${filePath}`);
|
|
3223
3102
|
}
|
|
3224
|
-
await
|
|
3103
|
+
await fs5.mkdir(targetDir, { recursive: true });
|
|
3225
3104
|
const template = `---
|
|
3226
3105
|
description: ${name} command
|
|
3227
3106
|
argument-hint: [args]
|
|
@@ -3236,7 +3115,7 @@ You can use:
|
|
|
3236
3115
|
- $1, $2, etc. for positional arguments
|
|
3237
3116
|
- @filename for file references
|
|
3238
3117
|
`;
|
|
3239
|
-
await
|
|
3118
|
+
await fs5.writeFile(filePath, template, "utf-8");
|
|
3240
3119
|
return filePath;
|
|
3241
3120
|
}
|
|
3242
3121
|
};
|
|
@@ -3369,6 +3248,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
3369
3248
|
super();
|
|
3370
3249
|
this.steps = [];
|
|
3371
3250
|
this.totalTokens = 0;
|
|
3251
|
+
this.totalCredits = 0;
|
|
3372
3252
|
this.toolCallCount = 0;
|
|
3373
3253
|
this.observationQueue = [];
|
|
3374
3254
|
this.context = {
|
|
@@ -3388,6 +3268,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
3388
3268
|
async run(query, options = {}) {
|
|
3389
3269
|
this.steps = [];
|
|
3390
3270
|
this.totalTokens = 0;
|
|
3271
|
+
this.totalCredits = 0;
|
|
3391
3272
|
this.toolCallCount = 0;
|
|
3392
3273
|
const maxIterations = options.maxIterations ?? this.context.maxIterations ?? 5;
|
|
3393
3274
|
const temperature = options.temperature ?? this.context.temperature ?? 0.7;
|
|
@@ -3424,6 +3305,7 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3424
3305
|
steps: this.steps,
|
|
3425
3306
|
completionInfo: {
|
|
3426
3307
|
totalTokens: this.totalTokens,
|
|
3308
|
+
totalCredits: this.totalCredits > 0 ? this.totalCredits : void 0,
|
|
3427
3309
|
iterations,
|
|
3428
3310
|
toolCalls: this.toolCallCount,
|
|
3429
3311
|
reachedMaxIterations: false
|
|
@@ -3459,6 +3341,9 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3459
3341
|
const inputTokens = completionInfo.inputTokens || 0;
|
|
3460
3342
|
const outputTokens = completionInfo.outputTokens || 0;
|
|
3461
3343
|
this.totalTokens += inputTokens + outputTokens;
|
|
3344
|
+
if (completionInfo.creditsUsed) {
|
|
3345
|
+
this.totalCredits += completionInfo.creditsUsed;
|
|
3346
|
+
}
|
|
3462
3347
|
if (currentText.trim() && completionInfo.toolsUsed?.length) {
|
|
3463
3348
|
const thoughtStep = {
|
|
3464
3349
|
type: "thought",
|
|
@@ -3572,6 +3457,7 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3572
3457
|
steps: this.steps,
|
|
3573
3458
|
completionInfo: {
|
|
3574
3459
|
totalTokens: this.totalTokens,
|
|
3460
|
+
totalCredits: this.totalCredits > 0 ? this.totalCredits : void 0,
|
|
3575
3461
|
iterations,
|
|
3576
3462
|
toolCalls: this.toolCallCount,
|
|
3577
3463
|
reachedMaxIterations
|
|
@@ -3589,6 +3475,7 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3589
3475
|
steps: this.steps,
|
|
3590
3476
|
completionInfo: {
|
|
3591
3477
|
totalTokens: this.totalTokens,
|
|
3478
|
+
totalCredits: this.totalCredits > 0 ? this.totalCredits : void 0,
|
|
3592
3479
|
iterations,
|
|
3593
3480
|
toolCalls: this.toolCallCount,
|
|
3594
3481
|
reachedMaxIterations: false
|
|
@@ -3717,18 +3604,23 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
|
|
|
3717
3604
|
async executeToolWithQueueFallback(toolUse) {
|
|
3718
3605
|
const queuedObs = this.observationQueue.find((obs) => obs.toolId === getToolId(toolUse));
|
|
3719
3606
|
if (queuedObs) {
|
|
3720
|
-
const
|
|
3607
|
+
const result = queuedObs.result;
|
|
3721
3608
|
const index = this.observationQueue.indexOf(queuedObs);
|
|
3722
3609
|
this.observationQueue.splice(index, 1);
|
|
3723
|
-
return typeof
|
|
3610
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
3724
3611
|
}
|
|
3725
3612
|
const tool = this.context.tools.find((t) => t.toolSchema.name === toolUse.name);
|
|
3726
3613
|
if (!tool) {
|
|
3727
|
-
|
|
3614
|
+
return `Error: Tool ${toolUse.name} not found in agent context`;
|
|
3615
|
+
}
|
|
3616
|
+
try {
|
|
3617
|
+
const params = this.parseToolArguments(toolUse.arguments);
|
|
3618
|
+
const result = await tool.toolFn(params);
|
|
3619
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
3620
|
+
} catch (error) {
|
|
3621
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3622
|
+
return `Error: ${message}`;
|
|
3728
3623
|
}
|
|
3729
|
-
const params = this.parseToolArguments(toolUse.arguments);
|
|
3730
|
-
const result = await tool.toolFn(params);
|
|
3731
|
-
return typeof result === "string" ? result : JSON.stringify(result);
|
|
3732
3624
|
}
|
|
3733
3625
|
/**
|
|
3734
3626
|
* Build and append tool call/result messages for the conversation history
|
|
@@ -3806,6 +3698,7 @@ var DEFAULT_TOOL_CATEGORIES = {
|
|
|
3806
3698
|
grep_search: "prompt_default",
|
|
3807
3699
|
glob_files: "prompt_default",
|
|
3808
3700
|
get_file_tree: "prompt_default",
|
|
3701
|
+
get_file_structure: "prompt_default",
|
|
3809
3702
|
git_status: "prompt_default",
|
|
3810
3703
|
git_diff: "prompt_default",
|
|
3811
3704
|
git_log: "prompt_default",
|
|
@@ -3828,6 +3721,50 @@ function isReadOnlyTool(toolName, customCategories) {
|
|
|
3828
3721
|
return getToolCategory(toolName, customCategories) !== "prompt_always";
|
|
3829
3722
|
}
|
|
3830
3723
|
|
|
3724
|
+
// src/core/skillsPrompt.ts
|
|
3725
|
+
function getSkillDisplayName(cmd) {
|
|
3726
|
+
return cmd.displayName || cmd.name;
|
|
3727
|
+
}
|
|
3728
|
+
function formatSkillEntry(cmd) {
|
|
3729
|
+
const displayName = getSkillDisplayName(cmd);
|
|
3730
|
+
const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
3731
|
+
const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
|
|
3732
|
+
return `- ${nameDisplay}${argHint}: ${cmd.description}
|
|
3733
|
+
`;
|
|
3734
|
+
}
|
|
3735
|
+
function formatSkillGroup(heading, commands) {
|
|
3736
|
+
if (commands.length === 0) {
|
|
3737
|
+
return "";
|
|
3738
|
+
}
|
|
3739
|
+
return `
|
|
3740
|
+
### ${heading}
|
|
3741
|
+
${commands.map(formatSkillEntry).join("")}`;
|
|
3742
|
+
}
|
|
3743
|
+
function filterAIVisibleSkills(commands) {
|
|
3744
|
+
return commands.filter((cmd) => !cmd.disableModelInvocation);
|
|
3745
|
+
}
|
|
3746
|
+
function filterSkillsByAllowedList(commands, allowedSkills) {
|
|
3747
|
+
if (!allowedSkills || allowedSkills.length === 0) {
|
|
3748
|
+
return commands;
|
|
3749
|
+
}
|
|
3750
|
+
return commands.filter((cmd) => allowedSkills.includes(cmd.name));
|
|
3751
|
+
}
|
|
3752
|
+
function buildSkillsPromptSection(commands, allowedSkills) {
|
|
3753
|
+
const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
|
|
3754
|
+
const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
|
|
3755
|
+
if (visibleCommands.length === 0) {
|
|
3756
|
+
return "";
|
|
3757
|
+
}
|
|
3758
|
+
const projectSkills = visibleCommands.filter((c) => c.source === "project");
|
|
3759
|
+
const globalSkills = visibleCommands.filter((c) => c.source === "global");
|
|
3760
|
+
return `
|
|
3761
|
+
|
|
3762
|
+
## Available Skills
|
|
3763
|
+
|
|
3764
|
+
Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
|
|
3765
|
+
` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3831
3768
|
// src/core/prompts.ts
|
|
3832
3769
|
var TOOL_GREP_SEARCH = "grep_search";
|
|
3833
3770
|
var TOOL_GLOB_FILES = "glob_files";
|
|
@@ -3838,7 +3775,38 @@ var TOOL_BASH_EXECUTE = "bash_execute";
|
|
|
3838
3775
|
var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
|
|
3839
3776
|
var TOOL_WRITE_TODOS = "write_todos";
|
|
3840
3777
|
var EXPLORE_SUBAGENT_TYPE = "explore";
|
|
3841
|
-
function buildCoreSystemPrompt(contextSection
|
|
3778
|
+
function buildCoreSystemPrompt(contextSection, config) {
|
|
3779
|
+
let projectContextSection = "";
|
|
3780
|
+
let agentDirectoryContext = "";
|
|
3781
|
+
let skillsSection = "";
|
|
3782
|
+
if (typeof contextSection === "string") {
|
|
3783
|
+
projectContextSection = contextSection;
|
|
3784
|
+
if (config) {
|
|
3785
|
+
if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) {
|
|
3786
|
+
skillsSection = buildSkillsPromptSection(config.customCommands);
|
|
3787
|
+
}
|
|
3788
|
+
if (config.agentStore) {
|
|
3789
|
+
agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
} else if (contextSection && typeof contextSection === "object") {
|
|
3793
|
+
config = contextSection;
|
|
3794
|
+
if (config.contextContent) {
|
|
3795
|
+
projectContextSection = `
|
|
3796
|
+
|
|
3797
|
+
## Project Context
|
|
3798
|
+
|
|
3799
|
+
Follow these project-specific instructions:
|
|
3800
|
+
|
|
3801
|
+
${config.contextContent}`;
|
|
3802
|
+
}
|
|
3803
|
+
if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) {
|
|
3804
|
+
skillsSection = buildSkillsPromptSection(config.customCommands);
|
|
3805
|
+
}
|
|
3806
|
+
if (config.agentStore) {
|
|
3807
|
+
agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3842
3810
|
return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
|
|
3843
3811
|
|
|
3844
3812
|
CORE BEHAVIOR:
|
|
@@ -3868,12 +3836,8 @@ When requested to perform tasks like fixing bugs, adding features, refactoring,
|
|
|
3868
3836
|
|
|
3869
3837
|
SUBAGENT DELEGATION:
|
|
3870
3838
|
- You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
|
|
3871
|
-
-
|
|
3872
|
-
* explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
|
|
3873
|
-
* plan: Break down complex tasks into actionable steps
|
|
3874
|
-
* review: Analyze code quality and identify issues
|
|
3839
|
+
- ${agentDirectoryContext ? `${agentDirectoryContext}` : ""}
|
|
3875
3840
|
- Subagents keep the main conversation clean and run faster with optimized models
|
|
3876
|
-
- Delegate when you need to search extensively or analyze code structure
|
|
3877
3841
|
|
|
3878
3842
|
CODE SEARCH BEST PRACTICES:
|
|
3879
3843
|
When searching code, follow this hierarchy for speed and efficiency:
|
|
@@ -3930,7 +3894,7 @@ EXAMPLES:
|
|
|
3930
3894
|
- "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
|
|
3931
3895
|
- "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
|
|
3932
3896
|
|
|
3933
|
-
Remember: Use context from previous messages to understand follow-up questions.${
|
|
3897
|
+
Remember: Use context from previous messages to understand follow-up questions.${projectContextSection}${skillsSection}`;
|
|
3934
3898
|
}
|
|
3935
3899
|
|
|
3936
3900
|
// ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
|
|
@@ -4380,7 +4344,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
|
4380
4344
|
import OpenAI2 from "openai";
|
|
4381
4345
|
|
|
4382
4346
|
// ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
|
|
4383
|
-
import
|
|
4347
|
+
import fs6, { unlinkSync } from "fs";
|
|
4384
4348
|
import yauzl from "yauzl";
|
|
4385
4349
|
import axios3 from "axios";
|
|
4386
4350
|
|
|
@@ -6059,8 +6023,8 @@ async function processAndStoreImages(images, context) {
|
|
|
6059
6023
|
const buffer = await downloadImage(image);
|
|
6060
6024
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
6061
6025
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
6062
|
-
const
|
|
6063
|
-
return
|
|
6026
|
+
const path19 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6027
|
+
return path19;
|
|
6064
6028
|
}));
|
|
6065
6029
|
}
|
|
6066
6030
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -7386,8 +7350,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
7386
7350
|
const buffer = await downloadImage2(imageUrl);
|
|
7387
7351
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
7388
7352
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
7389
|
-
const
|
|
7390
|
-
return
|
|
7353
|
+
const path19 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
7354
|
+
return path19;
|
|
7391
7355
|
}
|
|
7392
7356
|
async function generateFullMask(imageBuffer) {
|
|
7393
7357
|
const sharp = (await import("sharp")).default;
|
|
@@ -9328,8 +9292,8 @@ var planetVisibilityTool = {
|
|
|
9328
9292
|
};
|
|
9329
9293
|
|
|
9330
9294
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
|
|
9331
|
-
import { promises as
|
|
9332
|
-
import { existsSync as
|
|
9295
|
+
import { promises as fs7 } from "fs";
|
|
9296
|
+
import { existsSync as existsSync4, statSync as statSync4 } from "fs";
|
|
9333
9297
|
import path7 from "path";
|
|
9334
9298
|
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
9335
9299
|
async function readFileContent(params) {
|
|
@@ -9340,7 +9304,7 @@ async function readFileContent(params) {
|
|
|
9340
9304
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9341
9305
|
throw new Error(`Access denied: Cannot read files outside of current working directory`);
|
|
9342
9306
|
}
|
|
9343
|
-
if (!
|
|
9307
|
+
if (!existsSync4(resolvedPath)) {
|
|
9344
9308
|
throw new Error(`File not found: ${filePath}`);
|
|
9345
9309
|
}
|
|
9346
9310
|
const stats = statSync4(resolvedPath);
|
|
@@ -9354,7 +9318,7 @@ async function readFileContent(params) {
|
|
|
9354
9318
|
if (isBinary && encoding === "utf-8") {
|
|
9355
9319
|
throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
|
|
9356
9320
|
}
|
|
9357
|
-
const content = await
|
|
9321
|
+
const content = await fs7.readFile(resolvedPath, encoding);
|
|
9358
9322
|
if (typeof content === "string") {
|
|
9359
9323
|
const lines = content.split("\n");
|
|
9360
9324
|
const totalLines = lines.length;
|
|
@@ -9392,7 +9356,7 @@ ${content}`;
|
|
|
9392
9356
|
}
|
|
9393
9357
|
async function checkIfBinary(filePath) {
|
|
9394
9358
|
const buffer = Buffer.alloc(8192);
|
|
9395
|
-
const fd = await
|
|
9359
|
+
const fd = await fs7.open(filePath, "r");
|
|
9396
9360
|
try {
|
|
9397
9361
|
const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
|
|
9398
9362
|
const chunk = buffer.slice(0, bytesRead);
|
|
@@ -9453,8 +9417,8 @@ var fileReadTool = {
|
|
|
9453
9417
|
};
|
|
9454
9418
|
|
|
9455
9419
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
|
|
9456
|
-
import { promises as
|
|
9457
|
-
import { existsSync as
|
|
9420
|
+
import { promises as fs8 } from "fs";
|
|
9421
|
+
import { existsSync as existsSync5 } from "fs";
|
|
9458
9422
|
import path8 from "path";
|
|
9459
9423
|
async function createFile(params) {
|
|
9460
9424
|
const { path: filePath, content, createDirectories = true } = params;
|
|
@@ -9464,14 +9428,14 @@ async function createFile(params) {
|
|
|
9464
9428
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9465
9429
|
throw new Error(`Access denied: Cannot create files outside of current working directory`);
|
|
9466
9430
|
}
|
|
9467
|
-
const fileExists =
|
|
9431
|
+
const fileExists = existsSync5(resolvedPath);
|
|
9468
9432
|
const action = fileExists ? "overwritten" : "created";
|
|
9469
9433
|
if (createDirectories) {
|
|
9470
9434
|
const dir = path8.dirname(resolvedPath);
|
|
9471
|
-
await
|
|
9435
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
9472
9436
|
}
|
|
9473
|
-
await
|
|
9474
|
-
const stats = await
|
|
9437
|
+
await fs8.writeFile(resolvedPath, content, "utf-8");
|
|
9438
|
+
const stats = await fs8.stat(resolvedPath);
|
|
9475
9439
|
const lines = content.split("\n").length;
|
|
9476
9440
|
return `File ${action} successfully: ${filePath}
|
|
9477
9441
|
Size: ${stats.size} bytes
|
|
@@ -9482,7 +9446,7 @@ var createFileTool = {
|
|
|
9482
9446
|
implementation: (context) => ({
|
|
9483
9447
|
toolFn: async (value) => {
|
|
9484
9448
|
const params = value;
|
|
9485
|
-
const fileExists =
|
|
9449
|
+
const fileExists = existsSync5(path8.resolve(process.cwd(), path8.normalize(params.path)));
|
|
9486
9450
|
context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
|
|
9487
9451
|
path: params.path,
|
|
9488
9452
|
size: params.content.length
|
|
@@ -9525,7 +9489,7 @@ var createFileTool = {
|
|
|
9525
9489
|
import { glob } from "glob";
|
|
9526
9490
|
import { stat } from "fs/promises";
|
|
9527
9491
|
import path9 from "path";
|
|
9528
|
-
var
|
|
9492
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
9529
9493
|
"**/node_modules/**",
|
|
9530
9494
|
"**/.git/**",
|
|
9531
9495
|
"**/dist/**",
|
|
@@ -9544,7 +9508,7 @@ async function findFiles(params) {
|
|
|
9544
9508
|
if (!targetDir.startsWith(baseCwd)) {
|
|
9545
9509
|
throw new Error(`Access denied: Cannot search outside of current working directory`);
|
|
9546
9510
|
}
|
|
9547
|
-
const ignorePatterns = respect_git_ignore ?
|
|
9511
|
+
const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS : [];
|
|
9548
9512
|
const matches = await glob(pattern, {
|
|
9549
9513
|
cwd: targetDir,
|
|
9550
9514
|
dot: false,
|
|
@@ -9825,8 +9789,8 @@ var grepSearchTool = {
|
|
|
9825
9789
|
};
|
|
9826
9790
|
|
|
9827
9791
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
9828
|
-
import { promises as
|
|
9829
|
-
import { existsSync as
|
|
9792
|
+
import { promises as fs9 } from "fs";
|
|
9793
|
+
import { existsSync as existsSync6, statSync as statSync5 } from "fs";
|
|
9830
9794
|
import path11 from "path";
|
|
9831
9795
|
async function deleteFile(params) {
|
|
9832
9796
|
const { path: filePath, recursive = false } = params;
|
|
@@ -9836,7 +9800,7 @@ async function deleteFile(params) {
|
|
|
9836
9800
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9837
9801
|
throw new Error(`Access denied: Cannot delete files outside of current working directory`);
|
|
9838
9802
|
}
|
|
9839
|
-
if (!
|
|
9803
|
+
if (!existsSync6(resolvedPath)) {
|
|
9840
9804
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
9841
9805
|
}
|
|
9842
9806
|
const stats = statSync5(resolvedPath);
|
|
@@ -9846,10 +9810,10 @@ async function deleteFile(params) {
|
|
|
9846
9810
|
throw new Error(`Path is a directory: ${filePath}. Use recursive=true to delete directories and their contents.`);
|
|
9847
9811
|
}
|
|
9848
9812
|
if (isDirectory) {
|
|
9849
|
-
await
|
|
9813
|
+
await fs9.rm(resolvedPath, { recursive: true, force: true });
|
|
9850
9814
|
return `Directory deleted successfully: ${filePath}`;
|
|
9851
9815
|
} else {
|
|
9852
|
-
await
|
|
9816
|
+
await fs9.unlink(resolvedPath);
|
|
9853
9817
|
return `File deleted successfully: ${filePath}
|
|
9854
9818
|
Size: ${size} bytes`;
|
|
9855
9819
|
}
|
|
@@ -9860,7 +9824,7 @@ var deleteFileTool = {
|
|
|
9860
9824
|
toolFn: async (value) => {
|
|
9861
9825
|
const params = value;
|
|
9862
9826
|
const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
|
|
9863
|
-
const isDirectory =
|
|
9827
|
+
const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
|
|
9864
9828
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
9865
9829
|
path: params.path,
|
|
9866
9830
|
recursive: params.recursive
|
|
@@ -10359,8 +10323,8 @@ BLOCKED OPERATIONS:
|
|
|
10359
10323
|
};
|
|
10360
10324
|
|
|
10361
10325
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/editLocalFile/index.js
|
|
10362
|
-
import { promises as
|
|
10363
|
-
import { existsSync as
|
|
10326
|
+
import { promises as fs10 } from "fs";
|
|
10327
|
+
import { existsSync as existsSync7 } from "fs";
|
|
10364
10328
|
import path13 from "path";
|
|
10365
10329
|
import { diffLines as diffLines3 } from "diff";
|
|
10366
10330
|
function generateDiff(original, modified) {
|
|
@@ -10391,10 +10355,10 @@ async function editLocalFile(params) {
|
|
|
10391
10355
|
if (!resolvedPath.startsWith(cwd)) {
|
|
10392
10356
|
throw new Error(`Access denied: Cannot edit files outside of current working directory`);
|
|
10393
10357
|
}
|
|
10394
|
-
if (!
|
|
10358
|
+
if (!existsSync7(resolvedPath)) {
|
|
10395
10359
|
throw new Error(`File not found: ${filePath}`);
|
|
10396
10360
|
}
|
|
10397
|
-
const currentContent = await
|
|
10361
|
+
const currentContent = await fs10.readFile(resolvedPath, "utf-8");
|
|
10398
10362
|
if (!currentContent.includes(old_string)) {
|
|
10399
10363
|
const preview = old_string.length > 100 ? old_string.substring(0, 100) + "..." : old_string;
|
|
10400
10364
|
throw new Error(`String to replace not found in file. Make sure the old_string matches exactly (including whitespace and line endings). Searched for: "${preview}"`);
|
|
@@ -10404,7 +10368,7 @@ async function editLocalFile(params) {
|
|
|
10404
10368
|
throw new Error(`Found ${occurrences} occurrences of the string to replace. Please provide a more specific old_string that matches exactly one location.`);
|
|
10405
10369
|
}
|
|
10406
10370
|
const newContent = currentContent.replace(old_string, new_string);
|
|
10407
|
-
await
|
|
10371
|
+
await fs10.writeFile(resolvedPath, newContent, "utf-8");
|
|
10408
10372
|
const diffResult = generateDiff(old_string, new_string);
|
|
10409
10373
|
return `File edited successfully: ${filePath}
|
|
10410
10374
|
Changes: +${diffResult.additions} lines, -${diffResult.deletions} lines
|
|
@@ -10979,7 +10943,9 @@ var QuestStartBodySchema = z139.object({
|
|
|
10979
10943
|
fabFileIds: z139.array(z139.string()).optional()
|
|
10980
10944
|
})).optional(),
|
|
10981
10945
|
/** User's timezone (IANA format, e.g., "America/New_York") */
|
|
10982
|
-
timezone: z139.string().optional()
|
|
10946
|
+
timezone: z139.string().optional(),
|
|
10947
|
+
/** Pre-fetched API key table from invoke phase — avoids redundant DB call in process (#6616 P1-a) */
|
|
10948
|
+
apiKeyTable: z139.record(z139.string(), z139.string().nullable()).optional()
|
|
10983
10949
|
});
|
|
10984
10950
|
|
|
10985
10951
|
// ../../b4m-core/packages/services/dist/src/llm/StatusManager.js
|
|
@@ -11580,10 +11546,10 @@ var ToolErrorType;
|
|
|
11580
11546
|
// src/utils/diffPreview.ts
|
|
11581
11547
|
import * as Diff from "diff";
|
|
11582
11548
|
import { readFile } from "fs/promises";
|
|
11583
|
-
import { existsSync as
|
|
11549
|
+
import { existsSync as existsSync8 } from "fs";
|
|
11584
11550
|
async function generateFileDiffPreview(args) {
|
|
11585
11551
|
try {
|
|
11586
|
-
if (!
|
|
11552
|
+
if (!existsSync8(args.path)) {
|
|
11587
11553
|
const lines2 = args.content.split("\n");
|
|
11588
11554
|
const preview = lines2.slice(0, 20).join("\n");
|
|
11589
11555
|
const hasMore = lines2.length > 20;
|
|
@@ -11621,7 +11587,7 @@ ${diffLines4.join("\n")}`;
|
|
|
11621
11587
|
}
|
|
11622
11588
|
async function generateFileDeletePreview(args) {
|
|
11623
11589
|
try {
|
|
11624
|
-
if (!
|
|
11590
|
+
if (!existsSync8(args.path)) {
|
|
11625
11591
|
return `[File does not exist: ${args.path}]`;
|
|
11626
11592
|
}
|
|
11627
11593
|
const stats = await import("fs/promises").then((fs14) => fs14.stat(args.path));
|
|
@@ -11915,21 +11881,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
11915
11881
|
async upload(input, destination, options) {
|
|
11916
11882
|
return `/tmp/${destination}`;
|
|
11917
11883
|
}
|
|
11918
|
-
async download(
|
|
11884
|
+
async download(path19) {
|
|
11919
11885
|
throw new Error("Download not supported in CLI");
|
|
11920
11886
|
}
|
|
11921
|
-
async delete(
|
|
11887
|
+
async delete(path19) {
|
|
11922
11888
|
}
|
|
11923
|
-
async getSignedUrl(
|
|
11924
|
-
return `/tmp/${
|
|
11889
|
+
async getSignedUrl(path19) {
|
|
11890
|
+
return `/tmp/${path19}`;
|
|
11925
11891
|
}
|
|
11926
|
-
getPublicUrl(
|
|
11927
|
-
return `/tmp/${
|
|
11892
|
+
getPublicUrl(path19) {
|
|
11893
|
+
return `/tmp/${path19}`;
|
|
11928
11894
|
}
|
|
11929
|
-
async getPreview(
|
|
11930
|
-
return `/tmp/${
|
|
11895
|
+
async getPreview(path19) {
|
|
11896
|
+
return `/tmp/${path19}`;
|
|
11931
11897
|
}
|
|
11932
|
-
async getMetadata(
|
|
11898
|
+
async getMetadata(path19) {
|
|
11933
11899
|
return { size: 0, contentType: "application/octet-stream" };
|
|
11934
11900
|
}
|
|
11935
11901
|
};
|
|
@@ -12335,7 +12301,7 @@ function getEnvironmentName(configApiConfig) {
|
|
|
12335
12301
|
}
|
|
12336
12302
|
|
|
12337
12303
|
// src/utils/contextLoader.ts
|
|
12338
|
-
import * as
|
|
12304
|
+
import * as fs11 from "fs";
|
|
12339
12305
|
import * as path14 from "path";
|
|
12340
12306
|
import { homedir as homedir3 } from "os";
|
|
12341
12307
|
var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
|
|
@@ -12359,7 +12325,7 @@ function formatFileSize2(bytes) {
|
|
|
12359
12325
|
function tryReadContextFile(dir, filename, source) {
|
|
12360
12326
|
const filePath = path14.join(dir, filename);
|
|
12361
12327
|
try {
|
|
12362
|
-
const stats =
|
|
12328
|
+
const stats = fs11.lstatSync(filePath);
|
|
12363
12329
|
if (stats.isDirectory()) {
|
|
12364
12330
|
return null;
|
|
12365
12331
|
}
|
|
@@ -12373,7 +12339,7 @@ function tryReadContextFile(dir, filename, source) {
|
|
|
12373
12339
|
error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
|
|
12374
12340
|
};
|
|
12375
12341
|
}
|
|
12376
|
-
const content =
|
|
12342
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
12377
12343
|
return {
|
|
12378
12344
|
filename,
|
|
12379
12345
|
content,
|
|
@@ -12697,7 +12663,7 @@ function substituteArguments(template, args) {
|
|
|
12697
12663
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
12698
12664
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
12699
12665
|
import path15 from "path";
|
|
12700
|
-
import { existsSync as
|
|
12666
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
|
|
12701
12667
|
var MCPClient = class {
|
|
12702
12668
|
// Note: This class handles MCP server communication with repository filtering
|
|
12703
12669
|
mcp;
|
|
@@ -12734,7 +12700,6 @@ var MCPClient = class {
|
|
|
12734
12700
|
if (this.customCommand && this.customCommand.trim() !== "") {
|
|
12735
12701
|
command = this.customCommand;
|
|
12736
12702
|
args = this.customArgs ?? [];
|
|
12737
|
-
console.log(`[MCP] Using external command for ${this.serverName}: ${command} ${args.join(" ")}`);
|
|
12738
12703
|
} else {
|
|
12739
12704
|
const root = process.env.INIT_CWD || process.cwd();
|
|
12740
12705
|
const candidatePaths = [
|
|
@@ -12750,7 +12715,7 @@ var MCPClient = class {
|
|
|
12750
12715
|
path15.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
12751
12716
|
path15.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
12752
12717
|
];
|
|
12753
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
12718
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync9(p));
|
|
12754
12719
|
if (!serverScriptPath) {
|
|
12755
12720
|
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
12756
12721
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
@@ -13310,6 +13275,14 @@ ${totalText}`);
|
|
|
13310
13275
|
function stripThinkingBlocks(text) {
|
|
13311
13276
|
return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
13312
13277
|
}
|
|
13278
|
+
function extractUsageInfo(parsed) {
|
|
13279
|
+
return {
|
|
13280
|
+
inputTokens: parsed.usage?.inputTokens,
|
|
13281
|
+
outputTokens: parsed.usage?.outputTokens,
|
|
13282
|
+
creditsUsed: parsed.credits?.used,
|
|
13283
|
+
usdCost: parsed.credits?.usdCost
|
|
13284
|
+
};
|
|
13285
|
+
}
|
|
13313
13286
|
var ServerLlmBackend = class {
|
|
13314
13287
|
constructor(options) {
|
|
13315
13288
|
this.completionsEndpoint = "/api/ai/v1/completions";
|
|
@@ -13461,11 +13434,8 @@ var ServerLlmBackend = class {
|
|
|
13461
13434
|
if (parsed.type === "content") {
|
|
13462
13435
|
const textChunk = parsed.text || "";
|
|
13463
13436
|
accumulatedText += textChunk;
|
|
13464
|
-
if (parsed.usage) {
|
|
13465
|
-
lastUsageInfo =
|
|
13466
|
-
inputTokens: parsed.usage.inputTokens,
|
|
13467
|
-
outputTokens: parsed.usage.outputTokens
|
|
13468
|
-
};
|
|
13437
|
+
if (parsed.usage || parsed.credits) {
|
|
13438
|
+
lastUsageInfo = extractUsageInfo(parsed);
|
|
13469
13439
|
}
|
|
13470
13440
|
streamLogger.onContent(eventCount, textChunk, accumulatedText);
|
|
13471
13441
|
} else if (parsed.type === "tool_use") {
|
|
@@ -13492,11 +13462,8 @@ var ServerLlmBackend = class {
|
|
|
13492
13462
|
thinkingBlocks = parsed.thinking;
|
|
13493
13463
|
streamLogger.onCriticalEvent(eventCount, "THINKING", `${thinkingBlocks.length} thinking blocks`);
|
|
13494
13464
|
}
|
|
13495
|
-
if (parsed.usage) {
|
|
13496
|
-
lastUsageInfo =
|
|
13497
|
-
inputTokens: parsed.usage.inputTokens,
|
|
13498
|
-
outputTokens: parsed.usage.outputTokens
|
|
13499
|
-
};
|
|
13465
|
+
if (parsed.usage || parsed.credits) {
|
|
13466
|
+
lastUsageInfo = extractUsageInfo(parsed);
|
|
13500
13467
|
}
|
|
13501
13468
|
}
|
|
13502
13469
|
} catch (parseError) {
|
|
@@ -13563,7 +13530,7 @@ var ServerLlmBackend = class {
|
|
|
13563
13530
|
logger.info("\u26A0\uFE0F Using fallback model list (no CLI-compatible models available)");
|
|
13564
13531
|
return this.getFallbackModels();
|
|
13565
13532
|
}
|
|
13566
|
-
logger.
|
|
13533
|
+
logger.debug(`\u{1F4CB} Loaded ${filteredModels.length} models from server`);
|
|
13567
13534
|
return filteredModels;
|
|
13568
13535
|
} catch (error) {
|
|
13569
13536
|
logger.warn(
|
|
@@ -13801,7 +13768,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
13801
13768
|
// package.json
|
|
13802
13769
|
var package_default = {
|
|
13803
13770
|
name: "@bike4mind/cli",
|
|
13804
|
-
version: "0.2.
|
|
13771
|
+
version: "0.2.29-cache-mcp-tools-schema-and-refactor.18897+4545eccff",
|
|
13805
13772
|
type: "module",
|
|
13806
13773
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
13807
13774
|
license: "UNLICENSED",
|
|
@@ -13872,10 +13839,13 @@ var package_default = {
|
|
|
13872
13839
|
diff: "^8.0.2",
|
|
13873
13840
|
dotenv: "^16.3.1",
|
|
13874
13841
|
"eventsource-parser": "^3.0.6",
|
|
13842
|
+
fdir: "^6.5.0",
|
|
13875
13843
|
"file-type": "^18.7.0",
|
|
13876
13844
|
"fuse.js": "^7.1.0",
|
|
13845
|
+
fzf: "^0.5.2",
|
|
13877
13846
|
glob: "^13.0.0",
|
|
13878
13847
|
"gray-matter": "^4.0.3",
|
|
13848
|
+
ignore: "^7.0.5",
|
|
13879
13849
|
ink: "^6.5.1",
|
|
13880
13850
|
"ink-select-input": "^6.2.0",
|
|
13881
13851
|
"ink-spinner": "^5.0.0",
|
|
@@ -13891,15 +13861,18 @@ var package_default = {
|
|
|
13891
13861
|
open: "^11.0.0",
|
|
13892
13862
|
openai: "^6.18.0",
|
|
13893
13863
|
"p-limit": "^6.2.0",
|
|
13864
|
+
picomatch: "^4.0.3",
|
|
13894
13865
|
qrcode: "^1.5.4",
|
|
13895
13866
|
react: "^19.2.3",
|
|
13896
13867
|
sharp: "^0.34.5",
|
|
13897
13868
|
speakeasy: "^2.0.0",
|
|
13898
13869
|
tiktoken: "^1.0.16",
|
|
13870
|
+
"tree-sitter-wasms": "^0.1.13",
|
|
13899
13871
|
turndown: "^7.2.0",
|
|
13900
13872
|
unpdf: "^0.10.0",
|
|
13901
13873
|
uuid: "^9.0.1",
|
|
13902
13874
|
voyageai: "^0.0.4",
|
|
13875
|
+
"web-tree-sitter": "0.25.10",
|
|
13903
13876
|
xlsx: "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
13904
13877
|
yargs: "^17.7.2",
|
|
13905
13878
|
yauzl: "^3.1.3",
|
|
@@ -13909,14 +13882,15 @@ var package_default = {
|
|
|
13909
13882
|
},
|
|
13910
13883
|
devDependencies: {
|
|
13911
13884
|
"@bike4mind/agents": "0.1.0",
|
|
13912
|
-
"@bike4mind/common": "2.50.
|
|
13913
|
-
"@bike4mind/mcp": "1.29.
|
|
13914
|
-
"@bike4mind/services": "2.48.
|
|
13915
|
-
"@bike4mind/utils": "2.5.
|
|
13885
|
+
"@bike4mind/common": "2.50.1-cache-mcp-tools-schema-and-refactor.18897+4545eccff",
|
|
13886
|
+
"@bike4mind/mcp": "1.29.1-cache-mcp-tools-schema-and-refactor.18897+4545eccff",
|
|
13887
|
+
"@bike4mind/services": "2.48.1-cache-mcp-tools-schema-and-refactor.18897+4545eccff",
|
|
13888
|
+
"@bike4mind/utils": "2.5.1-cache-mcp-tools-schema-and-refactor.18897+4545eccff",
|
|
13916
13889
|
"@types/better-sqlite3": "^7.6.13",
|
|
13917
13890
|
"@types/diff": "^5.0.9",
|
|
13918
13891
|
"@types/jsonwebtoken": "^9.0.4",
|
|
13919
13892
|
"@types/node": "^22.9.0",
|
|
13893
|
+
"@types/picomatch": "^4.0.2",
|
|
13920
13894
|
"@types/react": "^19.2.7",
|
|
13921
13895
|
"@types/uuid": "^9.0.7",
|
|
13922
13896
|
"@types/yargs": "^17.0.32",
|
|
@@ -13929,7 +13903,7 @@ var package_default = {
|
|
|
13929
13903
|
optionalDependencies: {
|
|
13930
13904
|
"@vscode/ripgrep": "^1.17.0"
|
|
13931
13905
|
},
|
|
13932
|
-
gitHead: "
|
|
13906
|
+
gitHead: "4545eccff3e4227edc2e97613d9d12607cac9530"
|
|
13933
13907
|
};
|
|
13934
13908
|
|
|
13935
13909
|
// src/config/constants.ts
|
|
@@ -14166,50 +14140,6 @@ ${expandedBody}
|
|
|
14166
14140
|
};
|
|
14167
14141
|
}
|
|
14168
14142
|
|
|
14169
|
-
// src/core/skillsPrompt.ts
|
|
14170
|
-
function getSkillDisplayName(cmd) {
|
|
14171
|
-
return cmd.displayName || cmd.name;
|
|
14172
|
-
}
|
|
14173
|
-
function formatSkillEntry(cmd) {
|
|
14174
|
-
const displayName = getSkillDisplayName(cmd);
|
|
14175
|
-
const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
14176
|
-
const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
|
|
14177
|
-
return `- ${nameDisplay}${argHint}: ${cmd.description}
|
|
14178
|
-
`;
|
|
14179
|
-
}
|
|
14180
|
-
function formatSkillGroup(heading, commands) {
|
|
14181
|
-
if (commands.length === 0) {
|
|
14182
|
-
return "";
|
|
14183
|
-
}
|
|
14184
|
-
return `
|
|
14185
|
-
### ${heading}
|
|
14186
|
-
${commands.map(formatSkillEntry).join("")}`;
|
|
14187
|
-
}
|
|
14188
|
-
function filterAIVisibleSkills(commands) {
|
|
14189
|
-
return commands.filter((cmd) => !cmd.disableModelInvocation);
|
|
14190
|
-
}
|
|
14191
|
-
function filterSkillsByAllowedList(commands, allowedSkills) {
|
|
14192
|
-
if (!allowedSkills || allowedSkills.length === 0) {
|
|
14193
|
-
return commands;
|
|
14194
|
-
}
|
|
14195
|
-
return commands.filter((cmd) => allowedSkills.includes(cmd.name));
|
|
14196
|
-
}
|
|
14197
|
-
function buildSkillsPromptSection(commands, allowedSkills) {
|
|
14198
|
-
const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
|
|
14199
|
-
const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
|
|
14200
|
-
if (visibleCommands.length === 0) {
|
|
14201
|
-
return "";
|
|
14202
|
-
}
|
|
14203
|
-
const projectSkills = visibleCommands.filter((c) => c.source === "project");
|
|
14204
|
-
const globalSkills = visibleCommands.filter((c) => c.source === "global");
|
|
14205
|
-
return `
|
|
14206
|
-
|
|
14207
|
-
## Available Skills
|
|
14208
|
-
|
|
14209
|
-
Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
|
|
14210
|
-
` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
|
|
14211
|
-
}
|
|
14212
|
-
|
|
14213
14143
|
// src/agents/SubagentOrchestrator.ts
|
|
14214
14144
|
var SubagentOrchestrator = class {
|
|
14215
14145
|
constructor(deps) {
|
|
@@ -14422,9 +14352,9 @@ var SubagentOrchestrator = class {
|
|
|
14422
14352
|
};
|
|
14423
14353
|
|
|
14424
14354
|
// src/agents/AgentStore.ts
|
|
14425
|
-
import
|
|
14355
|
+
import fs12 from "fs/promises";
|
|
14426
14356
|
import path16 from "path";
|
|
14427
|
-
import
|
|
14357
|
+
import os2 from "os";
|
|
14428
14358
|
import matter2 from "gray-matter";
|
|
14429
14359
|
var FULL_MODEL_ID_PREFIXES = [
|
|
14430
14360
|
"claude-",
|
|
@@ -14570,7 +14500,7 @@ var AgentStore = class {
|
|
|
14570
14500
|
constructor(builtinDir, projectRoot) {
|
|
14571
14501
|
this.agents = /* @__PURE__ */ new Map();
|
|
14572
14502
|
const root = projectRoot || process.cwd();
|
|
14573
|
-
const home =
|
|
14503
|
+
const home = os2.homedir();
|
|
14574
14504
|
this.builtinAgentsDir = builtinDir;
|
|
14575
14505
|
this.globalB4MAgentsDir = path16.join(home, ".bike4mind", "agents");
|
|
14576
14506
|
this.globalClaudeAgentsDir = path16.join(home, ".claude", "agents");
|
|
@@ -14599,7 +14529,7 @@ var AgentStore = class {
|
|
|
14599
14529
|
*/
|
|
14600
14530
|
async loadAgentsFromDirectory(directory, source) {
|
|
14601
14531
|
try {
|
|
14602
|
-
const stats = await
|
|
14532
|
+
const stats = await fs12.stat(directory);
|
|
14603
14533
|
if (!stats.isDirectory()) {
|
|
14604
14534
|
return;
|
|
14605
14535
|
}
|
|
@@ -14627,7 +14557,7 @@ var AgentStore = class {
|
|
|
14627
14557
|
async findAgentFiles(directory) {
|
|
14628
14558
|
const files = [];
|
|
14629
14559
|
try {
|
|
14630
|
-
const entries = await
|
|
14560
|
+
const entries = await fs12.readdir(directory, { withFileTypes: true });
|
|
14631
14561
|
for (const entry of entries) {
|
|
14632
14562
|
const fullPath = path16.join(directory, entry.name);
|
|
14633
14563
|
if (entry.isDirectory()) {
|
|
@@ -14646,7 +14576,7 @@ var AgentStore = class {
|
|
|
14646
14576
|
* Parse a single agent markdown file
|
|
14647
14577
|
*/
|
|
14648
14578
|
async parseAgentFile(filePath, source) {
|
|
14649
|
-
const content = await
|
|
14579
|
+
const content = await fs12.readFile(filePath, "utf-8");
|
|
14650
14580
|
const { data: frontmatter, content: body } = matter2(content);
|
|
14651
14581
|
const parsed = AgentFrontmatterSchema.parse(frontmatter);
|
|
14652
14582
|
const name = path16.basename(filePath, ".md");
|
|
@@ -14731,14 +14661,14 @@ var AgentStore = class {
|
|
|
14731
14661
|
const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
|
|
14732
14662
|
const filePath = path16.join(targetDir, `${name}.md`);
|
|
14733
14663
|
try {
|
|
14734
|
-
await
|
|
14664
|
+
await fs12.access(filePath);
|
|
14735
14665
|
throw new Error(`Agent file already exists: ${filePath}`);
|
|
14736
14666
|
} catch (error) {
|
|
14737
14667
|
if (error.code !== "ENOENT") {
|
|
14738
14668
|
throw error;
|
|
14739
14669
|
}
|
|
14740
14670
|
}
|
|
14741
|
-
await
|
|
14671
|
+
await fs12.mkdir(targetDir, { recursive: true });
|
|
14742
14672
|
const template = `---
|
|
14743
14673
|
description: ${name} agent description
|
|
14744
14674
|
model: claude-3-5-haiku-20241022
|
|
@@ -14773,7 +14703,7 @@ You are a ${name} specialist. Your job is to [describe primary task].
|
|
|
14773
14703
|
## Output Format
|
|
14774
14704
|
Describe the expected output format here.
|
|
14775
14705
|
`;
|
|
14776
|
-
await
|
|
14706
|
+
await fs12.writeFile(filePath, template, "utf-8");
|
|
14777
14707
|
return filePath;
|
|
14778
14708
|
}
|
|
14779
14709
|
/**
|
|
@@ -14790,6 +14720,21 @@ Describe the expected output format here.
|
|
|
14790
14720
|
}
|
|
14791
14721
|
return { builtin, global, project, total: this.agents.size };
|
|
14792
14722
|
}
|
|
14723
|
+
/**
|
|
14724
|
+
* Generates a markdown "Phone Book" of available agents and their schemas.
|
|
14725
|
+
* This MUST be injected into the System Prompt of the parent agent.
|
|
14726
|
+
*/
|
|
14727
|
+
getDirectoryContext() {
|
|
14728
|
+
if (this.agents.size === 0) {
|
|
14729
|
+
return "No sub-agents are currently available.";
|
|
14730
|
+
}
|
|
14731
|
+
let context = "Use `subagent_delegate` for complex tasks requiring specialized analysis.\n";
|
|
14732
|
+
for (const [name, def] of this.agents) {
|
|
14733
|
+
context += ` - **${name}**: ${def.description}
|
|
14734
|
+
`;
|
|
14735
|
+
}
|
|
14736
|
+
return context;
|
|
14737
|
+
}
|
|
14793
14738
|
};
|
|
14794
14739
|
|
|
14795
14740
|
// src/agents/delegateTool.ts
|
|
@@ -15609,6 +15554,99 @@ function createFindDefinitionTool() {
|
|
|
15609
15554
|
};
|
|
15610
15555
|
}
|
|
15611
15556
|
|
|
15557
|
+
// src/tools/getFileStructure/index.ts
|
|
15558
|
+
import { existsSync as existsSync10, promises as fs13, statSync as statSync6 } from "fs";
|
|
15559
|
+
import path18 from "path";
|
|
15560
|
+
|
|
15561
|
+
// src/tools/getFileStructure/formatter.ts
|
|
15562
|
+
var SECTIONS = [
|
|
15563
|
+
{ title: "IMPORTS", kind: "import", exportable: false },
|
|
15564
|
+
{ title: "EXPORTS", kind: "export", exportable: false },
|
|
15565
|
+
{ title: "FUNCTIONS", kind: "function", exportable: true },
|
|
15566
|
+
{ title: "CLASSES", kind: "class", exportable: true },
|
|
15567
|
+
{ title: "INTERFACES", kind: "interface", exportable: true },
|
|
15568
|
+
{ title: "TYPES", kind: "type", exportable: true },
|
|
15569
|
+
{ title: "ENUMS", kind: "enum", exportable: true }
|
|
15570
|
+
];
|
|
15571
|
+
function formatStructureOutput(filePath, items, fileSize, lineCount) {
|
|
15572
|
+
const sizeStr = fileSize < 1024 ? `${fileSize} B` : `${(fileSize / 1024).toFixed(1)} KB`;
|
|
15573
|
+
const lines = [`File: ${filePath} (${sizeStr}, ${lineCount} lines)`, ""];
|
|
15574
|
+
for (const section of SECTIONS) {
|
|
15575
|
+
const sectionItems = items.filter((i) => i.kind === section.kind);
|
|
15576
|
+
formatSection(lines, section.title, sectionItems, (item) => {
|
|
15577
|
+
const prefix = section.exportable && item.exported ? "export " : "";
|
|
15578
|
+
return `L${item.line}: ${prefix}${item.name}`;
|
|
15579
|
+
});
|
|
15580
|
+
}
|
|
15581
|
+
return lines.join("\n").trimEnd();
|
|
15582
|
+
}
|
|
15583
|
+
function formatSection(lines, title, items, format) {
|
|
15584
|
+
lines.push(`${title} (${items.length}):`);
|
|
15585
|
+
if (items.length === 0) {
|
|
15586
|
+
lines.push(" (none)");
|
|
15587
|
+
} else {
|
|
15588
|
+
for (const item of items) {
|
|
15589
|
+
lines.push(` ${format(item)}`);
|
|
15590
|
+
}
|
|
15591
|
+
}
|
|
15592
|
+
lines.push("");
|
|
15593
|
+
}
|
|
15594
|
+
|
|
15595
|
+
// src/tools/getFileStructure/index.ts
|
|
15596
|
+
var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
|
|
15597
|
+
function createGetFileStructureTool() {
|
|
15598
|
+
return {
|
|
15599
|
+
toolFn: async (value) => {
|
|
15600
|
+
const params = value;
|
|
15601
|
+
try {
|
|
15602
|
+
const cwd = process.cwd();
|
|
15603
|
+
const resolvedPath = path18.resolve(cwd, params.path);
|
|
15604
|
+
if (!resolvedPath.startsWith(cwd)) {
|
|
15605
|
+
return "Error: Access denied - cannot read files outside of current working directory";
|
|
15606
|
+
}
|
|
15607
|
+
if (!existsSync10(resolvedPath)) {
|
|
15608
|
+
return `Error: File not found: ${params.path}`;
|
|
15609
|
+
}
|
|
15610
|
+
const stats = statSync6(resolvedPath);
|
|
15611
|
+
if (stats.isDirectory()) {
|
|
15612
|
+
return `Error: Path is a directory, not a file: ${params.path}`;
|
|
15613
|
+
}
|
|
15614
|
+
if (stats.size > MAX_FILE_SIZE3) {
|
|
15615
|
+
return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE3 / 1024 / 1024}MB`;
|
|
15616
|
+
}
|
|
15617
|
+
const ext = path18.extname(resolvedPath).toLowerCase();
|
|
15618
|
+
const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-4SGFQDY3.js");
|
|
15619
|
+
const languageId = getLanguageForExtension(ext);
|
|
15620
|
+
if (!languageId) {
|
|
15621
|
+
const supported = getSupportedLanguages();
|
|
15622
|
+
return `Error: Unsupported file type "${ext}". Supported languages: ${supported.join(", ")}`;
|
|
15623
|
+
}
|
|
15624
|
+
const sourceCode = await fs13.readFile(resolvedPath, "utf-8");
|
|
15625
|
+
const lineCount = sourceCode.split("\n").length;
|
|
15626
|
+
const items = await parseFileStructure(sourceCode, languageId);
|
|
15627
|
+
return formatStructureOutput(params.path, items, stats.size, lineCount);
|
|
15628
|
+
} catch (error) {
|
|
15629
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15630
|
+
return `Error analyzing file structure: ${errorMessage}`;
|
|
15631
|
+
}
|
|
15632
|
+
},
|
|
15633
|
+
toolSchema: {
|
|
15634
|
+
name: "get_file_structure",
|
|
15635
|
+
description: "Extract the high-level structure of a source file (imports, exports, functions, classes, interfaces, types) using AST parsing. Returns a concise outline with line numbers. Much more efficient than file_read when you only need to understand what a file contains or exports. Use this before file_read to decide which parts of a large file to read.",
|
|
15636
|
+
parameters: {
|
|
15637
|
+
type: "object",
|
|
15638
|
+
properties: {
|
|
15639
|
+
path: {
|
|
15640
|
+
type: "string",
|
|
15641
|
+
description: "Path to the source file (relative to cwd or absolute within cwd). Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py"
|
|
15642
|
+
}
|
|
15643
|
+
},
|
|
15644
|
+
required: ["path"]
|
|
15645
|
+
}
|
|
15646
|
+
}
|
|
15647
|
+
};
|
|
15648
|
+
}
|
|
15649
|
+
|
|
15612
15650
|
// src/index.tsx
|
|
15613
15651
|
process.removeAllListeners("warning");
|
|
15614
15652
|
process.on("warning", (warning) => {
|
|
@@ -15623,7 +15661,7 @@ var usageCache = null;
|
|
|
15623
15661
|
function CliApp() {
|
|
15624
15662
|
const { exit } = useApp();
|
|
15625
15663
|
const imageRenderer = new ImageRenderer();
|
|
15626
|
-
const [state, setState] =
|
|
15664
|
+
const [state, setState] = useState10({
|
|
15627
15665
|
session: null,
|
|
15628
15666
|
sessionStore: new SessionStore(),
|
|
15629
15667
|
configStore: new ConfigStore(),
|
|
@@ -15646,9 +15684,9 @@ function CliApp() {
|
|
|
15646
15684
|
contextContent: "",
|
|
15647
15685
|
backgroundManager: null
|
|
15648
15686
|
});
|
|
15649
|
-
const [isInitialized, setIsInitialized] =
|
|
15650
|
-
const [initError, setInitError] =
|
|
15651
|
-
const [commandHistory, setCommandHistory] =
|
|
15687
|
+
const [isInitialized, setIsInitialized] = useState10(false);
|
|
15688
|
+
const [initError, setInitError] = useState10(null);
|
|
15689
|
+
const [commandHistory, setCommandHistory] = useState10([]);
|
|
15652
15690
|
const imageStoreInitPromise = useRef3(null);
|
|
15653
15691
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
15654
15692
|
const enqueuePermissionPrompt = useCliStore((state2) => state2.enqueuePermissionPrompt);
|
|
@@ -15725,6 +15763,7 @@ function CliApp() {
|
|
|
15725
15763
|
});
|
|
15726
15764
|
const init = useCallback2(async () => {
|
|
15727
15765
|
try {
|
|
15766
|
+
const startupLog = [];
|
|
15728
15767
|
const config = await state.configStore.load();
|
|
15729
15768
|
const history = await state.commandHistoryStore.load();
|
|
15730
15769
|
setCommandHistory(history);
|
|
@@ -15732,7 +15771,7 @@ function CliApp() {
|
|
|
15732
15771
|
await state.customCommandStore.loadCommands();
|
|
15733
15772
|
const customCommandCount = state.customCommandStore.getCommandCount();
|
|
15734
15773
|
if (customCommandCount > 0) {
|
|
15735
|
-
|
|
15774
|
+
startupLog.push(`\u{1F4DD} Loaded ${customCommandCount} custom command${customCommandCount !== 1 ? "s" : ""}`);
|
|
15736
15775
|
}
|
|
15737
15776
|
} catch (error) {
|
|
15738
15777
|
console.warn("Failed to load custom commands:", error instanceof Error ? error.message : String(error));
|
|
@@ -15753,7 +15792,7 @@ function CliApp() {
|
|
|
15753
15792
|
} else {
|
|
15754
15793
|
isAuthenticated = true;
|
|
15755
15794
|
const daysUntilExpiry = Math.floor((expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60 * 24));
|
|
15756
|
-
|
|
15795
|
+
startupLog.push(`\u2705 Authenticated (expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? "s" : ""})`);
|
|
15757
15796
|
}
|
|
15758
15797
|
}
|
|
15759
15798
|
if (!isAuthenticated) {
|
|
@@ -15867,22 +15906,23 @@ function CliApp() {
|
|
|
15867
15906
|
state.configStore,
|
|
15868
15907
|
apiClient
|
|
15869
15908
|
);
|
|
15870
|
-
|
|
15909
|
+
startupLog.push(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
|
|
15871
15910
|
const mcpManager = new McpManager(config);
|
|
15872
15911
|
await mcpManager.initialize();
|
|
15873
15912
|
const mcpTools = mcpManager.getTools();
|
|
15874
15913
|
if (mcpTools.length > 0) {
|
|
15875
|
-
const
|
|
15876
|
-
|
|
15914
|
+
const toolCountByServer = mcpManager.getToolCount();
|
|
15915
|
+
const serverSummaries = toolCountByServer.map((s) => `${s.serverName} (${s.count})`).join(", ");
|
|
15916
|
+
startupLog.push(`\u{1F4E1} Loaded ${mcpTools.length} MCP tool(s): ${serverSummaries}`);
|
|
15877
15917
|
} else {
|
|
15878
|
-
|
|
15918
|
+
startupLog.push(`\u{1F4E1} No MCP tools loaded`);
|
|
15879
15919
|
}
|
|
15880
15920
|
const builtinAgentsDir = new URL("./agents/defaults/", import.meta.url).pathname;
|
|
15881
15921
|
const agentProjectDir = state.configStore.getProjectConfigDir();
|
|
15882
15922
|
const agentStore = new AgentStore(builtinAgentsDir, agentProjectDir || process.cwd());
|
|
15883
15923
|
await agentStore.loadAgents();
|
|
15884
15924
|
const agentSummary = agentStore.getSummary();
|
|
15885
|
-
|
|
15925
|
+
startupLog.push(
|
|
15886
15926
|
`\u{1F916} Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`
|
|
15887
15927
|
);
|
|
15888
15928
|
const orchestrator = new SubagentOrchestrator({
|
|
@@ -15917,18 +15957,25 @@ function CliApp() {
|
|
|
15917
15957
|
sessionId: newSession.id
|
|
15918
15958
|
}) : null;
|
|
15919
15959
|
const findDefinitionTool = createFindDefinitionTool();
|
|
15920
|
-
const
|
|
15960
|
+
const getFileStructureTool = createGetFileStructureTool();
|
|
15961
|
+
const cliTools = [
|
|
15962
|
+
agentDelegateTool,
|
|
15963
|
+
...backgroundTools,
|
|
15964
|
+
writeTodosTool,
|
|
15965
|
+
findDefinitionTool,
|
|
15966
|
+
getFileStructureTool
|
|
15967
|
+
];
|
|
15921
15968
|
if (skillTool) {
|
|
15922
15969
|
cliTools.push(skillTool);
|
|
15923
15970
|
}
|
|
15924
15971
|
const allTools = [...b4mTools2, ...mcpTools, ...cliTools];
|
|
15925
|
-
|
|
15972
|
+
startupLog.push(`\u{1F4C2} Working directory: ${process.cwd()}`);
|
|
15926
15973
|
const agentNamesList = agentStore.getAgentNames().join(", ");
|
|
15927
|
-
|
|
15974
|
+
startupLog.push(`\u{1F916} Subagent delegation enabled (${agentNamesList}) + background execution`);
|
|
15928
15975
|
if (skillTool) {
|
|
15929
15976
|
const skillCount = state.customCommandStore.getCommandCount();
|
|
15930
15977
|
if (skillCount > 0) {
|
|
15931
|
-
|
|
15978
|
+
startupLog.push(`\u{1F6E0}\uFE0F Skill tool enabled (${skillCount} skills available)`);
|
|
15932
15979
|
}
|
|
15933
15980
|
}
|
|
15934
15981
|
logger.debug(
|
|
@@ -15937,26 +15984,20 @@ function CliApp() {
|
|
|
15937
15984
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
15938
15985
|
const contextResult = await loadContextFiles(projectDir);
|
|
15939
15986
|
if (contextResult.globalContext) {
|
|
15940
|
-
|
|
15987
|
+
startupLog.push(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
|
|
15941
15988
|
}
|
|
15942
15989
|
if (contextResult.projectContext) {
|
|
15943
|
-
|
|
15990
|
+
startupLog.push(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
|
|
15944
15991
|
}
|
|
15945
15992
|
for (const error of contextResult.errors) {
|
|
15946
|
-
|
|
15993
|
+
startupLog.push(`\u26A0\uFE0F Context file error: ${error}`);
|
|
15947
15994
|
}
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
${contextResult.mergedContent}` : "";
|
|
15955
|
-
if (enableSkillTool) {
|
|
15956
|
-
const commands = state.customCommandStore.getAllCommands();
|
|
15957
|
-
contextSection += buildSkillsPromptSection(commands);
|
|
15958
|
-
}
|
|
15959
|
-
const cliSystemPrompt = buildCoreSystemPrompt(contextSection);
|
|
15995
|
+
const cliSystemPrompt = buildCoreSystemPrompt({
|
|
15996
|
+
contextContent: contextResult.mergedContent,
|
|
15997
|
+
agentStore,
|
|
15998
|
+
customCommands: state.customCommandStore.getAllCommands(),
|
|
15999
|
+
enableSkillTool
|
|
16000
|
+
});
|
|
15960
16001
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
15961
16002
|
const agent = new ReActAgent({
|
|
15962
16003
|
userId: config.userId,
|
|
@@ -16019,8 +16060,36 @@ ${contextResult.mergedContent}` : "";
|
|
|
16019
16060
|
// Store for grouped notification turn tracking
|
|
16020
16061
|
}));
|
|
16021
16062
|
setStoreSession(newSession);
|
|
16063
|
+
const bannerLines = [
|
|
16064
|
+
{ text: "\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557", ansi: "\x1B[36m\x1B[1m" },
|
|
16065
|
+
{ text: "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
|
|
16066
|
+
{ text: "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
|
|
16067
|
+
{ text: "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
|
|
16068
|
+
{ text: "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551", ansi: "\x1B[36m\x1B[1m" },
|
|
16069
|
+
{ text: "\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D", ansi: "\x1B[36m\x1B[1m" },
|
|
16070
|
+
{ text: "", ansi: "" },
|
|
16071
|
+
{ text: `v${package_default.version} - AI-Powered CLI`, ansi: "\x1B[2m" },
|
|
16072
|
+
{ text: "/help for more information", ansi: "\x1B[2m" }
|
|
16073
|
+
];
|
|
16074
|
+
const bannerWidth = 30;
|
|
16075
|
+
const termWidth = process.stdout.columns || 80;
|
|
16076
|
+
const rightColWidth = termWidth - bannerWidth - 2;
|
|
16077
|
+
const truncate = (str, max) => {
|
|
16078
|
+
if (str.length > max) return str.slice(0, max - 1) + "\u2026";
|
|
16079
|
+
return str;
|
|
16080
|
+
};
|
|
16081
|
+
const totalLines = Math.max(bannerLines.length, startupLog.length);
|
|
16082
|
+
for (let i = 0; i < totalLines; i++) {
|
|
16083
|
+
const banner = bannerLines[i];
|
|
16084
|
+
const leftText = banner?.text || "";
|
|
16085
|
+
const leftAnsi = banner?.ansi || "";
|
|
16086
|
+
const right = startupLog[i] || "";
|
|
16087
|
+
const coloredLeft = leftText ? `${leftAnsi}${leftText}\x1B[0m` : "";
|
|
16088
|
+
const gap = " ".repeat(bannerWidth - leftText.length + 2);
|
|
16089
|
+
console.log(coloredLeft + gap + truncate(right, rightColWidth));
|
|
16090
|
+
}
|
|
16022
16091
|
setIsInitialized(true);
|
|
16023
|
-
console.log("
|
|
16092
|
+
console.log("");
|
|
16024
16093
|
} catch (error) {
|
|
16025
16094
|
console.error("Initialization error:", error);
|
|
16026
16095
|
setInitError(error instanceof Error ? error.message : "Unknown error");
|
|
@@ -16034,7 +16103,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16034
16103
|
setStoreSession,
|
|
16035
16104
|
setState
|
|
16036
16105
|
]);
|
|
16037
|
-
|
|
16106
|
+
useEffect7(() => {
|
|
16038
16107
|
init();
|
|
16039
16108
|
}, [init]);
|
|
16040
16109
|
const handleCustomCommandMessage = async (fullTemplate, displayMessage) => {
|
|
@@ -16145,6 +16214,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16145
16214
|
completion: 0,
|
|
16146
16215
|
total: result.completionInfo.totalTokens
|
|
16147
16216
|
},
|
|
16217
|
+
creditsUsed: result.completionInfo.totalCredits,
|
|
16148
16218
|
model: state.session.model,
|
|
16149
16219
|
permissionDenied
|
|
16150
16220
|
}
|
|
@@ -16156,6 +16226,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16156
16226
|
metadata: {
|
|
16157
16227
|
...currentSession.metadata,
|
|
16158
16228
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
16229
|
+
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
16159
16230
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
16160
16231
|
}
|
|
16161
16232
|
};
|
|
@@ -16258,7 +16329,12 @@ ${contextResult.mergedContent}` : "";
|
|
|
16258
16329
|
const tokenCounter2 = getTokenCounter();
|
|
16259
16330
|
const contextWindow = tokenCounter2.getContextWindow(activeSession.model, state.availableModels);
|
|
16260
16331
|
const threshold = contextWindow * 0.8;
|
|
16261
|
-
const systemPrompt = buildCoreSystemPrompt(
|
|
16332
|
+
const systemPrompt = buildCoreSystemPrompt({
|
|
16333
|
+
contextContent: state.contextContent,
|
|
16334
|
+
agentStore: state.agentStore || void 0,
|
|
16335
|
+
customCommands: state.customCommandStore.getAllCommands(),
|
|
16336
|
+
enableSkillTool: config?.preferences.enableSkillTool !== false
|
|
16337
|
+
});
|
|
16262
16338
|
const contextUsage = tokenCounter2.countSessionTokens(activeSession, systemPrompt);
|
|
16263
16339
|
if (contextUsage.totalTokens >= threshold) {
|
|
16264
16340
|
console.log("\n\u26A0\uFE0F Context window 80% full. Auto-compacting...\n");
|
|
@@ -16353,6 +16429,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16353
16429
|
completion: 0,
|
|
16354
16430
|
total: result.completionInfo.totalTokens
|
|
16355
16431
|
},
|
|
16432
|
+
creditsUsed: result.completionInfo.totalCredits,
|
|
16356
16433
|
steps: result.steps.map(formatStep),
|
|
16357
16434
|
// Complete history: thoughts, actions, observations
|
|
16358
16435
|
permissionDenied
|
|
@@ -16366,6 +16443,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16366
16443
|
metadata: {
|
|
16367
16444
|
...currentSession.metadata,
|
|
16368
16445
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
16446
|
+
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
16369
16447
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
16370
16448
|
}
|
|
16371
16449
|
};
|
|
@@ -16464,7 +16542,8 @@ ${contextResult.mergedContent}` : "";
|
|
|
16464
16542
|
prompt: 0,
|
|
16465
16543
|
completion: 0,
|
|
16466
16544
|
total: result.completionInfo.totalTokens
|
|
16467
|
-
}
|
|
16545
|
+
},
|
|
16546
|
+
creditsUsed: result.completionInfo.totalCredits
|
|
16468
16547
|
}
|
|
16469
16548
|
};
|
|
16470
16549
|
const updatedSession = {
|
|
@@ -16474,6 +16553,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16474
16553
|
metadata: {
|
|
16475
16554
|
...currentSession.metadata,
|
|
16476
16555
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
16556
|
+
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
16477
16557
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
16478
16558
|
}
|
|
16479
16559
|
};
|
|
@@ -16675,7 +16755,7 @@ Available commands:
|
|
|
16675
16755
|
/whoami - Show current authenticated user
|
|
16676
16756
|
/usage - Show credit usage and balance
|
|
16677
16757
|
/save <name> - Save current session
|
|
16678
|
-
/
|
|
16758
|
+
/resume - List and resume saved sessions
|
|
16679
16759
|
/config - Show configuration
|
|
16680
16760
|
|
|
16681
16761
|
API Configuration:
|
|
@@ -16694,7 +16774,24 @@ Project Configuration:
|
|
|
16694
16774
|
Custom Commands:
|
|
16695
16775
|
/commands - List all custom commands
|
|
16696
16776
|
/commands:new <name> - Create a new custom command
|
|
16697
|
-
/commands:reload - Reload custom commands from disk
|
|
16777
|
+
/commands:reload - Reload custom commands from disk
|
|
16778
|
+
|
|
16779
|
+
Keyboard Shortcuts:
|
|
16780
|
+
Ctrl+C - Press twice to exit
|
|
16781
|
+
Esc - Abort current operation
|
|
16782
|
+
Shift+Tab - Toggle auto-accept edits
|
|
16783
|
+
Ctrl+U - Clear current line
|
|
16784
|
+
Ctrl+K - Clear from cursor to end of line
|
|
16785
|
+
Ctrl+W - Delete word before cursor
|
|
16786
|
+
Ctrl+A - Move cursor to beginning
|
|
16787
|
+
Ctrl+E - Move cursor to end
|
|
16788
|
+
Ctrl+B / \u2190 - Move cursor left
|
|
16789
|
+
Ctrl+F / \u2192 - Move cursor right
|
|
16790
|
+
Ctrl+D - Delete character at cursor
|
|
16791
|
+
Ctrl+L - Clear input
|
|
16792
|
+
\u2191 / \u2193 - Navigate history / autocomplete
|
|
16793
|
+
Tab - Accept autocomplete suggestion
|
|
16794
|
+
Shift+Cmd+Click - Open links in browser${hasCustomCommands ? "\n\n\u{1F4DD} Custom Commands Available:" : ""}${hasCustomCommands ? customCommands.map((cmd) => {
|
|
16698
16795
|
const source = cmd.source === "global" ? "\u{1F3E0}" : "\u{1F4C1}";
|
|
16699
16796
|
const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
16700
16797
|
return `
|
|
@@ -16724,6 +16821,7 @@ Custom Commands:
|
|
|
16724
16821
|
console.log(`\u2705 Session saved as "${sessionName}"`);
|
|
16725
16822
|
break;
|
|
16726
16823
|
}
|
|
16824
|
+
case "resume":
|
|
16727
16825
|
case "sessions": {
|
|
16728
16826
|
const sessions = await state.sessionStore.list(20);
|
|
16729
16827
|
if (sessions.length === 0) {
|
|
@@ -16734,7 +16832,6 @@ Custom Commands:
|
|
|
16734
16832
|
const handleSessionSelect = async (selectedSession) => {
|
|
16735
16833
|
setState((prev) => ({ ...prev, sessionSelector: null }));
|
|
16736
16834
|
if (!selectedSession) {
|
|
16737
|
-
console.log("\u274C Session selection cancelled");
|
|
16738
16835
|
return;
|
|
16739
16836
|
}
|
|
16740
16837
|
const loadedSession = await state.sessionStore.load(selectedSession.id);
|
|
@@ -17159,15 +17256,21 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
17159
17256
|
}
|
|
17160
17257
|
const tokenCounter2 = getTokenCounter();
|
|
17161
17258
|
const contextWindow = tokenCounter2.getContextWindow(state.session.model, state.availableModels);
|
|
17162
|
-
const corePromptTokens = tokenCounter2.countTokens(buildCoreSystemPrompt(
|
|
17259
|
+
const corePromptTokens = tokenCounter2.countTokens(buildCoreSystemPrompt());
|
|
17163
17260
|
const projectContextTokens = state.contextContent ? tokenCounter2.countTokens(state.contextContent) : 0;
|
|
17164
17261
|
const commands = state.customCommandStore.getAllCommands();
|
|
17165
17262
|
const skillsSection = buildSkillsPromptSection(commands);
|
|
17166
17263
|
const skillsTokens = skillsSection ? tokenCounter2.countTokens(skillsSection) : 0;
|
|
17264
|
+
const agentDirectoryTokens = state.agentStore ? tokenCounter2.countTokens(state.agentStore.getDirectoryContext()) : 0;
|
|
17167
17265
|
const mcpTools = state.mcpManager?.getTools() || [];
|
|
17168
17266
|
const mcpToolsTokens = tokenCounter2.countToolSchemaTokens(mcpTools);
|
|
17169
17267
|
const mcpToolCount = state.mcpManager?.getToolCount() || [];
|
|
17170
|
-
const systemPrompt = buildCoreSystemPrompt(
|
|
17268
|
+
const systemPrompt = buildCoreSystemPrompt({
|
|
17269
|
+
contextContent: state.contextContent,
|
|
17270
|
+
agentStore: state.agentStore || void 0,
|
|
17271
|
+
customCommands: commands,
|
|
17272
|
+
enableSkillTool: state.config?.preferences.enableSkillTool !== false
|
|
17273
|
+
});
|
|
17171
17274
|
const usage = tokenCounter2.countSessionTokens(state.session, systemPrompt);
|
|
17172
17275
|
const totalWithTools = usage.totalTokens + mcpToolsTokens;
|
|
17173
17276
|
const usagePercent = totalWithTools / contextWindow * 100;
|
|
@@ -17186,6 +17289,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
17186
17289
|
if (commands.length > 0) {
|
|
17187
17290
|
console.log(` Skills Section: ${skillsTokens.toLocaleString()} tokens (${commands.length} skills)`);
|
|
17188
17291
|
}
|
|
17292
|
+
if (agentDirectoryTokens > 0) {
|
|
17293
|
+
const agentCount = state.agentStore?.getAgentCount() || 0;
|
|
17294
|
+
console.log(` Agent Directory: ${agentDirectoryTokens.toLocaleString()} tokens (${agentCount} agents)`);
|
|
17295
|
+
}
|
|
17189
17296
|
if (mcpTools.length > 0) {
|
|
17190
17297
|
console.log("\nMCP Tools:");
|
|
17191
17298
|
console.log(` Total: ${mcpToolsTokens.toLocaleString()} tokens (${mcpTools.length} tools)`);
|
|
@@ -17586,21 +17693,11 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
17586
17693
|
}
|
|
17587
17694
|
);
|
|
17588
17695
|
}
|
|
17589
|
-
var BANNER = `
|
|
17590
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557
|
|
17591
|
-
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551
|
|
17592
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551
|
|
17593
|
-
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551
|
|
17594
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551
|
|
17595
|
-
\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
17596
|
-
`;
|
|
17597
|
-
console.log("\x1B[36m\x1B[1m%s\x1B[0m", BANNER);
|
|
17598
|
-
console.log(`\x1B[2m v${package_default.version} - AI-Powered CLI\x1B[0m`);
|
|
17599
|
-
console.log(" /help for more information\n");
|
|
17600
17696
|
var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "development";
|
|
17601
17697
|
if (isDevMode) {
|
|
17602
17698
|
logger.debug("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
17603
17699
|
}
|
|
17700
|
+
warmFileCache();
|
|
17604
17701
|
render(/* @__PURE__ */ React21.createElement(CliApp, null), {
|
|
17605
17702
|
exitOnCtrlC: false
|
|
17606
17703
|
});
|