@bike4mind/cli 0.2.29-slack-native-search.18848 → 0.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/HydrationEngine-WGYKF46H.js +9 -0
- package/dist/{artifactExtractor-RDMRUAYS.js → artifactExtractor-4SBIE5OK.js} +1 -1
- package/dist/{chunk-T33BVGIR.js → chunk-22VR7SKO.js} +2 -2
- package/dist/{chunk-MDOLASLI.js → chunk-5J4RL57F.js} +33 -18
- package/dist/chunk-RUI6HNLO.js +766 -0
- package/dist/{chunk-MPSPHOIZ.js → chunk-TNFZP7FG.js} +1037 -200
- package/dist/{chunk-WO5GGMEO.js → chunk-UYN4HMRF.js} +2 -2
- package/dist/{chunk-LM4PJCVX.js → chunk-YQWFK5O2.js} +2 -2
- package/dist/{create-YRA72AUB.js → create-DCHG36CP.js} +3 -3
- package/dist/index.js +1469 -403
- package/dist/{llmMarkdownGenerator-NLBVQLSH.js → llmMarkdownGenerator-6MQKVOWW.js} +1 -1
- package/dist/{markdownGenerator-FOWCMKGZ.js → markdownGenerator-4GDO3PTY.js} +1 -1
- package/dist/{mementoService-ACNWTJBC.js → mementoService-WDQFLLRV.js} +3 -3
- package/dist/{src-7NOQH3YS.js → src-DVEE3PS5.js} +9 -2
- package/dist/{src-JZ7PFQMP.js → src-ZAT7QMNA.js} +155 -1
- package/dist/{subtractCredits-CP4A2VYJ.js → subtractCredits-LE54FRKC.js} +3 -3
- package/package.json +11 -6
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
getEffectiveApiKey,
|
|
6
6
|
getOpenWeatherKey,
|
|
7
7
|
getSerperKey
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-22VR7SKO.js";
|
|
9
|
+
import "./chunk-RUI6HNLO.js";
|
|
9
10
|
import {
|
|
10
11
|
ConfigStore,
|
|
11
12
|
logger
|
|
@@ -14,8 +15,8 @@ import {
|
|
|
14
15
|
selectActiveBackgroundAgents,
|
|
15
16
|
useCliStore
|
|
16
17
|
} from "./chunk-TVW4ZESU.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-UYN4HMRF.js";
|
|
19
|
+
import "./chunk-YQWFK5O2.js";
|
|
19
20
|
import {
|
|
20
21
|
BFLImageService,
|
|
21
22
|
BaseStorage,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
OpenAIBackend,
|
|
28
29
|
OpenAIImageService,
|
|
29
30
|
XAIImageService
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-5J4RL57F.js";
|
|
31
32
|
import {
|
|
32
33
|
AiEvents,
|
|
33
34
|
ApiKeyEvents,
|
|
@@ -83,32 +84,37 @@ import {
|
|
|
83
84
|
XAI_IMAGE_MODELS,
|
|
84
85
|
b4mLLMTools,
|
|
85
86
|
getMcpProviderMetadata
|
|
86
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-TNFZP7FG.js";
|
|
87
88
|
import {
|
|
88
89
|
Logger
|
|
89
90
|
} from "./chunk-OCYRD7D6.js";
|
|
90
91
|
|
|
91
92
|
// src/index.tsx
|
|
92
|
-
import React21, { useState as
|
|
93
|
+
import React21, { useState as useState10, useEffect as useEffect7, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
93
94
|
import { render, Box as Box20, Text as Text20, useApp, useInput as useInput9 } from "ink";
|
|
94
95
|
import { execSync } from "child_process";
|
|
95
96
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
96
97
|
import { v4 as uuidv411 } from "uuid";
|
|
97
98
|
|
|
98
99
|
// src/components/App.tsx
|
|
99
|
-
import React15, { useState as
|
|
100
|
+
import React15, { useState as useState6, useEffect as useEffect5 } from "react";
|
|
100
101
|
import { Box as Box14, Text as Text14, Static, useInput as useInput6 } from "ink";
|
|
101
102
|
|
|
102
103
|
// src/components/StatusBar.tsx
|
|
103
104
|
import React from "react";
|
|
104
105
|
import { Box, Text } from "ink";
|
|
105
|
-
var StatusBar = React.memo(function StatusBar2({
|
|
106
|
+
var StatusBar = React.memo(function StatusBar2({
|
|
107
|
+
sessionName,
|
|
108
|
+
model,
|
|
109
|
+
tokenUsage,
|
|
110
|
+
creditsUsage
|
|
111
|
+
}) {
|
|
106
112
|
const autoAcceptEdits = useCliStore((state) => state.autoAcceptEdits);
|
|
107
|
-
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)));
|
|
113
|
+
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)));
|
|
108
114
|
});
|
|
109
115
|
|
|
110
116
|
// src/components/InputPrompt.tsx
|
|
111
|
-
import React5, { useState as
|
|
117
|
+
import React5, { useState as useState3, useMemo, useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
112
118
|
import { Box as Box4, Text as Text5, useInput as useInput2 } from "ink";
|
|
113
119
|
|
|
114
120
|
// src/components/CustomTextInput.tsx
|
|
@@ -321,65 +327,53 @@ function CommandAutocomplete({ commands, selectedIndex }) {
|
|
|
321
327
|
// src/components/FileAutocomplete.tsx
|
|
322
328
|
import React4 from "react";
|
|
323
329
|
import { Box as Box3, Text as Text4 } from "ink";
|
|
330
|
+
import * as path2 from "path";
|
|
324
331
|
|
|
325
332
|
// src/utils/fileSearch.ts
|
|
326
333
|
import * as fs from "fs";
|
|
327
334
|
import * as path from "path";
|
|
328
|
-
import
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
".
|
|
334
|
-
"
|
|
335
|
-
|
|
336
|
-
".cache",
|
|
337
|
-
"coverage",
|
|
338
|
-
".DS_Store",
|
|
339
|
-
".env",
|
|
340
|
-
".env.local",
|
|
341
|
-
".env.secrets"
|
|
342
|
-
];
|
|
343
|
-
function walkDirectory(basePath = process.cwd(), maxDepth = 5, ignorePatterns = DEFAULT_IGNORE_PATTERNS) {
|
|
344
|
-
const results = [];
|
|
345
|
-
const normalizedBase = path.resolve(basePath);
|
|
346
|
-
function walk(currentPath, depth) {
|
|
347
|
-
if (depth > maxDepth) return;
|
|
335
|
+
import { AsyncFzf } from "fzf";
|
|
336
|
+
import { fdir } from "fdir";
|
|
337
|
+
import ignore from "ignore";
|
|
338
|
+
function loadIgnoreRules(projectRoot) {
|
|
339
|
+
const ig = ignore();
|
|
340
|
+
ig.add(".git/");
|
|
341
|
+
const gitignorePath = path.join(projectRoot, ".gitignore");
|
|
342
|
+
if (fs.existsSync(gitignorePath)) {
|
|
348
343
|
try {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
if (ignorePatterns.some((pattern) => entry.name === pattern || entry.name.startsWith(pattern))) {
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
const fullPath = path.join(currentPath, entry.name);
|
|
355
|
-
const relativePath = path.relative(normalizedBase, fullPath);
|
|
356
|
-
if (entry.isDirectory()) {
|
|
357
|
-
results.push({
|
|
358
|
-
path: relativePath,
|
|
359
|
-
isDirectory: true
|
|
360
|
-
});
|
|
361
|
-
walk(fullPath, depth + 1);
|
|
362
|
-
} else if (entry.isFile()) {
|
|
363
|
-
try {
|
|
364
|
-
const stats = fs.statSync(fullPath);
|
|
365
|
-
results.push({
|
|
366
|
-
path: relativePath,
|
|
367
|
-
isDirectory: false,
|
|
368
|
-
size: stats.size
|
|
369
|
-
});
|
|
370
|
-
} catch {
|
|
371
|
-
results.push({
|
|
372
|
-
path: relativePath,
|
|
373
|
-
isDirectory: false
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
344
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
|
|
345
|
+
ig.add(gitignoreContent);
|
|
378
346
|
} catch {
|
|
379
347
|
}
|
|
380
348
|
}
|
|
381
|
-
|
|
382
|
-
|
|
349
|
+
return ig;
|
|
350
|
+
}
|
|
351
|
+
function crawlDirectory(projectRoot, maxDepth = 10, maxFiles = 2e4, ig) {
|
|
352
|
+
let fileCount = 0;
|
|
353
|
+
const crawler = new fdir().withRelativePaths().withDirs().withPathSeparator("/").exclude((dirPath) => {
|
|
354
|
+
const relativePath = path.posix.relative(projectRoot, dirPath);
|
|
355
|
+
if (!relativePath || relativePath === ".") {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
const pathWithSlash = `${relativePath}/`;
|
|
359
|
+
return ig.ignores(pathWithSlash);
|
|
360
|
+
}).filter(() => {
|
|
361
|
+
if (fileCount >= maxFiles) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
fileCount++;
|
|
365
|
+
return true;
|
|
366
|
+
});
|
|
367
|
+
if (maxDepth !== void 0) {
|
|
368
|
+
crawler.withMaxDepth(maxDepth);
|
|
369
|
+
}
|
|
370
|
+
const paths = crawler.crawl(projectRoot).sync();
|
|
371
|
+
return paths.filter((p) => {
|
|
372
|
+
if (!p || p === ".") {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
return !ig.ignores(p);
|
|
376
|
+
});
|
|
383
377
|
}
|
|
384
378
|
function formatFileSize(bytes) {
|
|
385
379
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -390,42 +384,112 @@ function formatFileSize(bytes) {
|
|
|
390
384
|
var cachedFiles = null;
|
|
391
385
|
var cacheTimestamp = 0;
|
|
392
386
|
var CACHE_TTL_MS = 3e4;
|
|
393
|
-
function getCachedFiles() {
|
|
387
|
+
function getCachedFiles(projectRoot = process.cwd()) {
|
|
394
388
|
const now = Date.now();
|
|
395
389
|
if (!cachedFiles || now - cacheTimestamp > CACHE_TTL_MS) {
|
|
396
|
-
|
|
390
|
+
const normalizedBase = path.resolve(projectRoot);
|
|
391
|
+
const ig = loadIgnoreRules(normalizedBase);
|
|
392
|
+
cachedFiles = crawlDirectory(normalizedBase, 10, 2e4, ig);
|
|
397
393
|
cacheTimestamp = now;
|
|
398
394
|
}
|
|
399
395
|
return cachedFiles;
|
|
400
396
|
}
|
|
401
|
-
function
|
|
402
|
-
|
|
397
|
+
function warmFileCache() {
|
|
398
|
+
if (!cachedFiles || Date.now() - cacheTimestamp > CACHE_TTL_MS) {
|
|
399
|
+
setImmediate(() => {
|
|
400
|
+
getCachedFiles();
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function listAbsoluteDirectory(absolutePath, filterQuery) {
|
|
405
|
+
try {
|
|
406
|
+
const normalizedPath = path.normalize(absolutePath);
|
|
407
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
408
|
+
return [];
|
|
409
|
+
}
|
|
410
|
+
const stats = fs.statSync(normalizedPath);
|
|
411
|
+
if (!stats.isDirectory()) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
const entries = fs.readdirSync(normalizedPath, { withFileTypes: true });
|
|
415
|
+
let filteredEntries = entries;
|
|
416
|
+
if (filterQuery && filterQuery.length > 0) {
|
|
417
|
+
const lowerQuery = filterQuery.toLowerCase();
|
|
418
|
+
filteredEntries = entries.filter((entry) => entry.name.toLowerCase().includes(lowerQuery));
|
|
419
|
+
}
|
|
420
|
+
return filteredEntries.slice(0, 15).map((entry) => {
|
|
421
|
+
const fullPath = path.join(normalizedPath, entry.name);
|
|
422
|
+
const result = {
|
|
423
|
+
path: fullPath,
|
|
424
|
+
isDirectory: entry.isDirectory()
|
|
425
|
+
};
|
|
426
|
+
if (entry.isFile()) {
|
|
427
|
+
try {
|
|
428
|
+
const fileStats = fs.statSync(fullPath);
|
|
429
|
+
result.size = fileStats.size;
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
});
|
|
435
|
+
} catch {
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function searchFiles(query, maxResults = 20) {
|
|
440
|
+
const projectRoot = process.cwd();
|
|
441
|
+
if (path.isAbsolute(query)) {
|
|
442
|
+
try {
|
|
443
|
+
const stats = fs.statSync(query);
|
|
444
|
+
if (stats.isDirectory()) {
|
|
445
|
+
return listAbsoluteDirectory(query);
|
|
446
|
+
}
|
|
447
|
+
} catch {
|
|
448
|
+
}
|
|
449
|
+
let dirToList;
|
|
450
|
+
let filterQuery;
|
|
451
|
+
if (query.endsWith("/") || query.endsWith(path.sep)) {
|
|
452
|
+
dirToList = query;
|
|
453
|
+
filterQuery = void 0;
|
|
454
|
+
} else {
|
|
455
|
+
dirToList = path.dirname(query);
|
|
456
|
+
filterQuery = path.basename(query);
|
|
457
|
+
}
|
|
458
|
+
return listAbsoluteDirectory(dirToList, filterQuery);
|
|
459
|
+
}
|
|
460
|
+
const files = getCachedFiles(projectRoot);
|
|
403
461
|
if (!query || query.trim() === "") {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
462
|
+
const rootFiles = files.filter((f) => !f.includes("/")).slice(0, maxResults);
|
|
463
|
+
return rootFiles.map((p) => {
|
|
464
|
+
const fullPath = path.join(projectRoot, p);
|
|
465
|
+
try {
|
|
466
|
+
const stats = fs.statSync(fullPath);
|
|
467
|
+
return {
|
|
468
|
+
path: p,
|
|
469
|
+
isDirectory: stats.isDirectory(),
|
|
470
|
+
size: stats.isFile() ? stats.size : void 0
|
|
471
|
+
};
|
|
472
|
+
} catch {
|
|
473
|
+
return { path: p, isDirectory: false };
|
|
474
|
+
}
|
|
413
475
|
});
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
476
|
+
}
|
|
477
|
+
const fzf = new AsyncFzf(files);
|
|
478
|
+
const results = await fzf.find(query);
|
|
479
|
+
return results.slice(0, maxResults).map((result) => {
|
|
480
|
+
const p = result.item;
|
|
481
|
+
const fullPath = path.join(projectRoot, p);
|
|
482
|
+
try {
|
|
483
|
+
const stats = fs.statSync(fullPath);
|
|
484
|
+
return {
|
|
485
|
+
path: p,
|
|
486
|
+
isDirectory: stats.isDirectory(),
|
|
487
|
+
size: stats.isFile() ? stats.size : void 0
|
|
488
|
+
};
|
|
489
|
+
} catch {
|
|
490
|
+
return { path: p, isDirectory: false };
|
|
491
|
+
}
|
|
426
492
|
});
|
|
427
|
-
const results = fuse.search(query);
|
|
428
|
-
return results.slice(0, 20).map((result) => result.item);
|
|
429
493
|
}
|
|
430
494
|
function isPathWithinCwd(filePath) {
|
|
431
495
|
const cwd = path.resolve(process.cwd());
|
|
@@ -509,10 +573,27 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
509
573
|
// src/components/FileAutocomplete.tsx
|
|
510
574
|
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
511
575
|
if (files.length === 0) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
576
|
+
if (path2.isAbsolute(query)) {
|
|
577
|
+
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)"));
|
|
578
|
+
}
|
|
579
|
+
return /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No matching files", query ? ` for "${query}"` : " in root directory"));
|
|
580
|
+
}
|
|
581
|
+
const VIEWPORT_SIZE = 6;
|
|
582
|
+
const totalFiles = files.length;
|
|
583
|
+
let startIndex = 0;
|
|
584
|
+
let endIndex = Math.min(VIEWPORT_SIZE, totalFiles);
|
|
585
|
+
if (totalFiles > VIEWPORT_SIZE) {
|
|
586
|
+
const halfViewport = Math.floor(VIEWPORT_SIZE / 2);
|
|
587
|
+
startIndex = Math.max(0, selectedIndex - halfViewport);
|
|
588
|
+
endIndex = Math.min(totalFiles, startIndex + VIEWPORT_SIZE);
|
|
589
|
+
if (endIndex === totalFiles) {
|
|
590
|
+
startIndex = Math.max(0, totalFiles - VIEWPORT_SIZE);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const visibleFiles = files.slice(startIndex, endIndex);
|
|
594
|
+
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) => {
|
|
595
|
+
const actualIndex = startIndex + viewportIndex;
|
|
596
|
+
const isSelected = actualIndex === selectedIndex;
|
|
516
597
|
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
517
598
|
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
518
599
|
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
@@ -521,7 +602,7 @@ function FileAutocomplete({ files, selectedIndex, query }) {
|
|
|
521
602
|
}
|
|
522
603
|
|
|
523
604
|
// src/utils/fuzzySearch.ts
|
|
524
|
-
import
|
|
605
|
+
import Fuse from "fuse.js";
|
|
525
606
|
|
|
526
607
|
// src/config/commands.ts
|
|
527
608
|
var COMMANDS = [
|
|
@@ -698,13 +779,13 @@ function searchCommands(query, commands = COMMANDS) {
|
|
|
698
779
|
if (!query || query.trim() === "") {
|
|
699
780
|
return commands;
|
|
700
781
|
}
|
|
701
|
-
const fuse = new
|
|
782
|
+
const fuse = new Fuse(commands, fuseOptions);
|
|
702
783
|
const results = fuse.search(query);
|
|
703
784
|
return results.map((result) => result.item);
|
|
704
785
|
}
|
|
705
786
|
|
|
706
787
|
// src/utils/imageDetector.ts
|
|
707
|
-
import { readFileSync, existsSync, statSync as statSync2 } from "fs";
|
|
788
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
708
789
|
import { extname as extname2 } from "path";
|
|
709
790
|
var ImageInputDetector = class {
|
|
710
791
|
static {
|
|
@@ -845,14 +926,14 @@ var ImageInputDetector = class {
|
|
|
845
926
|
let filepath = input.trim();
|
|
846
927
|
filepath = filepath.replace(/^["']|["']$/g, "");
|
|
847
928
|
filepath = filepath.replace(/\\(.)/g, "$1");
|
|
848
|
-
if (!
|
|
929
|
+
if (!existsSync2(filepath)) return null;
|
|
849
930
|
const stats = statSync2(filepath);
|
|
850
931
|
if (!stats.isFile()) return null;
|
|
851
932
|
const ext = extname2(filepath).toLowerCase();
|
|
852
933
|
if (!this.SUPPORTED_EXTENSIONS.includes(ext)) return null;
|
|
853
934
|
if (stats.size > this.MAX_IMAGE_SIZE) return null;
|
|
854
935
|
try {
|
|
855
|
-
const data =
|
|
936
|
+
const data = readFileSync2(filepath);
|
|
856
937
|
const format = ext.substring(1) === "jpeg" ? "jpg" : ext.substring(1);
|
|
857
938
|
const filename = filepath.split("/").pop() || "image";
|
|
858
939
|
return {
|
|
@@ -907,6 +988,21 @@ var ImageInputDetector = class {
|
|
|
907
988
|
}
|
|
908
989
|
};
|
|
909
990
|
|
|
991
|
+
// src/hooks/useDebounce.ts
|
|
992
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
993
|
+
function useDebounce(value, delay = 300) {
|
|
994
|
+
const [debouncedValue, setDebouncedValue] = useState2(value);
|
|
995
|
+
useEffect2(() => {
|
|
996
|
+
const timer = setTimeout(() => {
|
|
997
|
+
setDebouncedValue(value);
|
|
998
|
+
}, delay);
|
|
999
|
+
return () => {
|
|
1000
|
+
clearTimeout(timer);
|
|
1001
|
+
};
|
|
1002
|
+
}, [value, delay]);
|
|
1003
|
+
return debouncedValue;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
910
1006
|
// src/components/InputPrompt.tsx
|
|
911
1007
|
function looksLikeFilePath(input) {
|
|
912
1008
|
const trimmed = input.trim();
|
|
@@ -947,20 +1043,20 @@ function InputPrompt({
|
|
|
947
1043
|
const value = useCliStore((state) => state.inputValue);
|
|
948
1044
|
const setInputValue = useCliStore((state) => state.setInputValue);
|
|
949
1045
|
const setValue = setInputValue;
|
|
950
|
-
const [selectedIndex, setSelectedIndex] =
|
|
951
|
-
const [historyIndex, setHistoryIndex] =
|
|
952
|
-
const [tempInput, setTempInput] =
|
|
1046
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1047
|
+
const [historyIndex, setHistoryIndex] = useState3(-1);
|
|
1048
|
+
const [tempInput, setTempInput] = useState3("");
|
|
953
1049
|
const inputKey = useRef2(0);
|
|
954
|
-
|
|
1050
|
+
useEffect3(() => {
|
|
955
1051
|
if (prefillInput) {
|
|
956
1052
|
setValue(prefillInput);
|
|
957
1053
|
onPrefillConsumed?.();
|
|
958
1054
|
}
|
|
959
1055
|
}, [prefillInput, onPrefillConsumed]);
|
|
960
|
-
const [fileAutocomplete, setFileAutocomplete] =
|
|
961
|
-
const [fileSelectedIndex, setFileSelectedIndex] =
|
|
1056
|
+
const [fileAutocomplete, setFileAutocomplete] = useState3(null);
|
|
1057
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState3(0);
|
|
962
1058
|
const isBashMode = value.startsWith("!");
|
|
963
|
-
|
|
1059
|
+
useEffect3(() => {
|
|
964
1060
|
onBashModeChange?.(isBashMode);
|
|
965
1061
|
}, [isBashMode, onBashModeChange]);
|
|
966
1062
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active && !looksLikeFilePath(value);
|
|
@@ -969,14 +1065,32 @@ function InputPrompt({
|
|
|
969
1065
|
if (!shouldShowCommandAutocomplete) return [];
|
|
970
1066
|
return searchCommands(commandQuery, commands);
|
|
971
1067
|
}, [shouldShowCommandAutocomplete, commandQuery, commands]);
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1068
|
+
const debouncedFileQuery = useDebounce(fileAutocomplete?.query ?? "", 200);
|
|
1069
|
+
const [filteredFiles, setFilteredFiles] = useState3([]);
|
|
1070
|
+
useEffect3(() => {
|
|
1071
|
+
if (!fileAutocomplete?.active) {
|
|
1072
|
+
setFilteredFiles([]);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
let cancelled = false;
|
|
1076
|
+
searchFiles(debouncedFileQuery).then((results) => {
|
|
1077
|
+
if (!cancelled) {
|
|
1078
|
+
setFilteredFiles(results);
|
|
1079
|
+
}
|
|
1080
|
+
}).catch((error) => {
|
|
1081
|
+
if (!cancelled) {
|
|
1082
|
+
console.error("File search error:", error);
|
|
1083
|
+
setFilteredFiles([]);
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
return () => {
|
|
1087
|
+
cancelled = true;
|
|
1088
|
+
};
|
|
1089
|
+
}, [fileAutocomplete?.active, debouncedFileQuery]);
|
|
1090
|
+
useEffect3(() => {
|
|
977
1091
|
setSelectedIndex(0);
|
|
978
1092
|
}, [filteredCommands]);
|
|
979
|
-
|
|
1093
|
+
useEffect3(() => {
|
|
980
1094
|
setFileSelectedIndex(0);
|
|
981
1095
|
}, [filteredFiles]);
|
|
982
1096
|
useInput2(
|
|
@@ -1214,13 +1328,13 @@ function BackgroundAgentStatus() {
|
|
|
1214
1328
|
}
|
|
1215
1329
|
|
|
1216
1330
|
// src/components/CompletedGroupNotification.tsx
|
|
1217
|
-
import React9, { useEffect as
|
|
1331
|
+
import React9, { useEffect as useEffect4 } from "react";
|
|
1218
1332
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
1219
1333
|
var NOTIFICATION_DISPLAY_DURATION_MS = 3e3;
|
|
1220
1334
|
function CompletedGroupNotification() {
|
|
1221
1335
|
const notifications = useCliStore((state) => state.completedGroupNotifications);
|
|
1222
1336
|
const clearNotifications = useCliStore((state) => state.clearCompletedGroupNotifications);
|
|
1223
|
-
|
|
1337
|
+
useEffect4(() => {
|
|
1224
1338
|
if (notifications.length > 0) {
|
|
1225
1339
|
const timer = setTimeout(() => {
|
|
1226
1340
|
clearNotifications();
|
|
@@ -1233,7 +1347,7 @@ function CompletedGroupNotification() {
|
|
|
1233
1347
|
}
|
|
1234
1348
|
|
|
1235
1349
|
// src/components/PermissionPrompt.tsx
|
|
1236
|
-
import React10, { useState as
|
|
1350
|
+
import React10, { useState as useState4, useCallback } from "react";
|
|
1237
1351
|
import { Box as Box9, Text as Text9, useInput as useInput3 } from "ink";
|
|
1238
1352
|
function renderDiffPreview(preview) {
|
|
1239
1353
|
const lines = preview.split("\n");
|
|
@@ -1260,14 +1374,16 @@ function PermissionPrompt({
|
|
|
1260
1374
|
}) {
|
|
1261
1375
|
const items = canBeTrusted ? [
|
|
1262
1376
|
{ label: "\u2713 Allow once", value: "allow-once" },
|
|
1377
|
+
{ label: "\u2713 Allow for this session", value: "allow-session" },
|
|
1263
1378
|
{ label: "\u2713 Always allow (trust this tool)", value: "allow-always" },
|
|
1264
1379
|
{ label: "\u2717 Deny", value: "deny" }
|
|
1265
1380
|
] : [
|
|
1266
1381
|
{ label: "\u2713 Allow once", value: "allow-once" },
|
|
1382
|
+
{ label: "\u2713 Allow for this session", value: "allow-session" },
|
|
1267
1383
|
{ label: "\u2717 Deny", value: "deny" }
|
|
1268
1384
|
];
|
|
1269
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1270
|
-
const [responded, setResponded] =
|
|
1385
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
1386
|
+
const [responded, setResponded] = useState4(false);
|
|
1271
1387
|
const handleSelect = useCallback(() => {
|
|
1272
1388
|
if (responded) return;
|
|
1273
1389
|
setResponded(true);
|
|
@@ -1315,7 +1431,7 @@ function PermissionPrompt({
|
|
|
1315
1431
|
}
|
|
1316
1432
|
|
|
1317
1433
|
// src/components/ConfigEditor.tsx
|
|
1318
|
-
import React11, { useState as
|
|
1434
|
+
import React11, { useState as useState5, useMemo as useMemo3 } from "react";
|
|
1319
1435
|
import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
|
|
1320
1436
|
var MAX_ITERATIONS_OPTIONS = [
|
|
1321
1437
|
{ label: "10", value: 10 },
|
|
@@ -1460,10 +1576,10 @@ function buildConfigItems(availableModels) {
|
|
|
1460
1576
|
return items;
|
|
1461
1577
|
}
|
|
1462
1578
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
1463
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1464
|
-
const [editedConfig, setEditedConfig] =
|
|
1465
|
-
const [saveError, setSaveError] =
|
|
1466
|
-
const [isSaving, setIsSaving] =
|
|
1579
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
1580
|
+
const [editedConfig, setEditedConfig] = useState5(config);
|
|
1581
|
+
const [saveError, setSaveError] = useState5(null);
|
|
1582
|
+
const [isSaving, setIsSaving] = useState5(false);
|
|
1467
1583
|
const configItems = useMemo3(() => buildConfigItems(availableModels), [availableModels]);
|
|
1468
1584
|
const hasChanges = useMemo3(() => {
|
|
1469
1585
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
@@ -1744,12 +1860,12 @@ var MessageItem = React14.memo(function MessageItem2({ message }) {
|
|
|
1744
1860
|
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)}`));
|
|
1745
1861
|
}
|
|
1746
1862
|
return null;
|
|
1747
|
-
}).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 })),
|
|
1863
|
+
}).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"}` : "", ")")));
|
|
1748
1864
|
});
|
|
1749
1865
|
|
|
1750
1866
|
// src/utils/processFileReferences.ts
|
|
1751
1867
|
import * as fs2 from "fs";
|
|
1752
|
-
import * as
|
|
1868
|
+
import * as path3 from "path";
|
|
1753
1869
|
|
|
1754
1870
|
// src/utils/constants.ts
|
|
1755
1871
|
var NAME_SUFFIXES = ["jr", "sr", "ii", "iii", "iv", "v", "phd", "md", "esq"];
|
|
@@ -1760,7 +1876,7 @@ function isNameSuffix(value) {
|
|
|
1760
1876
|
// src/utils/processFileReferences.ts
|
|
1761
1877
|
var FILE_REFERENCE_REGEX = /(?:^|\s)@([^\s@]+)/g;
|
|
1762
1878
|
function looksLikeFilePath2(ref) {
|
|
1763
|
-
if (ref.includes("/") || ref.includes(
|
|
1879
|
+
if (ref.includes("/") || ref.includes(path3.sep)) {
|
|
1764
1880
|
return true;
|
|
1765
1881
|
}
|
|
1766
1882
|
const extensionMatch = /\.(\w+)$/.exec(ref);
|
|
@@ -1790,9 +1906,13 @@ function extractFileReferences(message) {
|
|
|
1790
1906
|
}
|
|
1791
1907
|
function readFileContents(filePath) {
|
|
1792
1908
|
const cwd = process.cwd();
|
|
1793
|
-
const
|
|
1794
|
-
if (
|
|
1795
|
-
return { error: `Security: Path "${filePath}"
|
|
1909
|
+
const isAbsolutePath = path3.isAbsolute(filePath);
|
|
1910
|
+
if (filePath.includes("..")) {
|
|
1911
|
+
return { error: `Security: Path traversal detected in "${filePath}"` };
|
|
1912
|
+
}
|
|
1913
|
+
const absolutePath = isAbsolutePath ? path3.normalize(filePath) : path3.resolve(cwd, filePath);
|
|
1914
|
+
if (!isAbsolutePath && !isPathWithinCwd(filePath)) {
|
|
1915
|
+
return { error: `Security: Relative path "${filePath}" escapes the current working directory` };
|
|
1796
1916
|
}
|
|
1797
1917
|
if (!fs2.existsSync(absolutePath)) {
|
|
1798
1918
|
return { error: `File not found: "${filePath}"` };
|
|
@@ -1883,6 +2003,7 @@ function App({
|
|
|
1883
2003
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
1884
2004
|
const currentModel = useCliStore((state) => state.session?.model || "claude-sonnet-4-5-20250929");
|
|
1885
2005
|
const totalTokens = useCliStore((state) => state.session?.metadata.totalTokens || 0);
|
|
2006
|
+
const totalCredits = useCliStore((state) => state.session?.metadata.totalCredits);
|
|
1886
2007
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
1887
2008
|
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
1888
2009
|
const showConfigEditor = useCliStore((state) => state.showConfigEditor);
|
|
@@ -1893,7 +2014,7 @@ function App({
|
|
|
1893
2014
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1894
2015
|
const pendingBackgroundTrigger = useCliStore((state) => state.pendingBackgroundTrigger);
|
|
1895
2016
|
const setPendingBackgroundTrigger = useCliStore((state) => state.setPendingBackgroundTrigger);
|
|
1896
|
-
|
|
2017
|
+
useEffect5(() => {
|
|
1897
2018
|
if (pendingBackgroundTrigger && !isThinking && onBackgroundCompletion) {
|
|
1898
2019
|
setPendingBackgroundTrigger(false);
|
|
1899
2020
|
onBackgroundCompletion();
|
|
@@ -1905,7 +2026,7 @@ function App({
|
|
|
1905
2026
|
toggleAutoAcceptEdits();
|
|
1906
2027
|
}
|
|
1907
2028
|
});
|
|
1908
|
-
const [isBashMode, setIsBashMode] =
|
|
2029
|
+
const [isBashMode, setIsBashMode] = useState6(false);
|
|
1909
2030
|
const handleSubmit = React15.useCallback(
|
|
1910
2031
|
async (input) => {
|
|
1911
2032
|
const trimmed = input.trim();
|
|
@@ -1968,7 +2089,15 @@ ${errorBlock}`;
|
|
|
1968
2089
|
onPrefillConsumed,
|
|
1969
2090
|
onBashModeChange: setIsBashMode
|
|
1970
2091
|
}
|
|
1971
|
-
)), /* @__PURE__ */ React15.createElement(
|
|
2092
|
+
)), /* @__PURE__ */ React15.createElement(
|
|
2093
|
+
StatusBar,
|
|
2094
|
+
{
|
|
2095
|
+
sessionName,
|
|
2096
|
+
model: currentModel,
|
|
2097
|
+
tokenUsage: totalTokens,
|
|
2098
|
+
creditsUsage: totalCredits
|
|
2099
|
+
}
|
|
2100
|
+
)));
|
|
1972
2101
|
}
|
|
1973
2102
|
|
|
1974
2103
|
// src/components/MessageList.tsx
|
|
@@ -2044,12 +2173,12 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
2044
2173
|
}
|
|
2045
2174
|
|
|
2046
2175
|
// src/components/RewindSelector.tsx
|
|
2047
|
-
import React18, { useState as
|
|
2176
|
+
import React18, { useState as useState7 } from "react";
|
|
2048
2177
|
import { Box as Box17, Text as Text17, useInput as useInput7 } from "ink";
|
|
2049
2178
|
import SelectInput2 from "ink-select-input";
|
|
2050
2179
|
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
2051
|
-
const [step, setStep] =
|
|
2052
|
-
const [selectedMessageIndex, setSelectedMessageIndex] =
|
|
2180
|
+
const [step, setStep] = useState7("selection");
|
|
2181
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState7(null);
|
|
2053
2182
|
useInput7((input, key) => {
|
|
2054
2183
|
if (key.escape) {
|
|
2055
2184
|
if (step === "confirmation") {
|
|
@@ -2113,12 +2242,12 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
2113
2242
|
}
|
|
2114
2243
|
|
|
2115
2244
|
// src/components/SessionSelector.tsx
|
|
2116
|
-
import React19, { useState as
|
|
2245
|
+
import React19, { useState as useState8 } from "react";
|
|
2117
2246
|
import { Box as Box18, Text as Text18, useInput as useInput8 } from "ink";
|
|
2118
2247
|
import SelectInput3 from "ink-select-input";
|
|
2119
2248
|
function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
2120
|
-
const [step, setStep] =
|
|
2121
|
-
const [selectedSession, setSelectedSession] =
|
|
2249
|
+
const [step, setStep] = useState8("selection");
|
|
2250
|
+
const [selectedSession, setSelectedSession] = useState8(null);
|
|
2122
2251
|
useInput8((_input, key) => {
|
|
2123
2252
|
if (key.escape) {
|
|
2124
2253
|
if (step === "confirmation") {
|
|
@@ -2193,7 +2322,7 @@ function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
|
2193
2322
|
}
|
|
2194
2323
|
|
|
2195
2324
|
// src/components/LoginFlow.tsx
|
|
2196
|
-
import React20, { useState as
|
|
2325
|
+
import React20, { useState as useState9, useEffect as useEffect6 } from "react";
|
|
2197
2326
|
import { Box as Box19, Text as Text19 } from "ink";
|
|
2198
2327
|
import Spinner3 from "ink-spinner";
|
|
2199
2328
|
import jwt from "jsonwebtoken";
|
|
@@ -2310,11 +2439,11 @@ var OAuthClient = class {
|
|
|
2310
2439
|
|
|
2311
2440
|
// src/components/LoginFlow.tsx
|
|
2312
2441
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
2313
|
-
const [status, setStatus] =
|
|
2314
|
-
const [deviceFlow, setDeviceFlow] =
|
|
2315
|
-
const [statusMessage, setStatusMessage] =
|
|
2316
|
-
const [error, setError] =
|
|
2317
|
-
|
|
2442
|
+
const [status, setStatus] = useState9("initiating");
|
|
2443
|
+
const [deviceFlow, setDeviceFlow] = useState9(null);
|
|
2444
|
+
const [statusMessage, setStatusMessage] = useState9("Initiating device authorization...");
|
|
2445
|
+
const [error, setError] = useState9(null);
|
|
2446
|
+
useEffect6(() => {
|
|
2318
2447
|
const runLoginFlow = async () => {
|
|
2319
2448
|
const oauth = new OAuthClient(apiUrl);
|
|
2320
2449
|
try {
|
|
@@ -2349,7 +2478,7 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
2349
2478
|
};
|
|
2350
2479
|
runLoginFlow();
|
|
2351
2480
|
}, [apiUrl, configStore, onSuccess, onError]);
|
|
2352
|
-
|
|
2481
|
+
useEffect6(() => {
|
|
2353
2482
|
if (deviceFlow && status === "waiting") {
|
|
2354
2483
|
open(deviceFlow.verification_uri_complete).catch((err) => {
|
|
2355
2484
|
console.error("Failed to auto-open browser:", err);
|
|
@@ -2370,12 +2499,12 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
2370
2499
|
|
|
2371
2500
|
// src/storage/SessionStore.ts
|
|
2372
2501
|
import { promises as fs3 } from "fs";
|
|
2373
|
-
import
|
|
2502
|
+
import path4 from "path";
|
|
2374
2503
|
import { homedir } from "os";
|
|
2375
2504
|
import { v4 as uuidv4 } from "uuid";
|
|
2376
2505
|
var SessionStore = class {
|
|
2377
2506
|
constructor(basePath) {
|
|
2378
|
-
this.basePath = basePath ||
|
|
2507
|
+
this.basePath = basePath || path4.join(homedir(), ".bike4mind", "sessions");
|
|
2379
2508
|
}
|
|
2380
2509
|
/**
|
|
2381
2510
|
* Initialize storage directory
|
|
@@ -2396,7 +2525,7 @@ var SessionStore = class {
|
|
|
2396
2525
|
throw new Error("Cannot save session with no messages");
|
|
2397
2526
|
}
|
|
2398
2527
|
await this.init();
|
|
2399
|
-
const filePath =
|
|
2528
|
+
const filePath = path4.join(this.basePath, `${session.id}.json`);
|
|
2400
2529
|
try {
|
|
2401
2530
|
await fs3.writeFile(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
2402
2531
|
} catch (error) {
|
|
@@ -2408,7 +2537,7 @@ var SessionStore = class {
|
|
|
2408
2537
|
* Load a session from disk by ID
|
|
2409
2538
|
*/
|
|
2410
2539
|
async load(id) {
|
|
2411
|
-
const filePath =
|
|
2540
|
+
const filePath = path4.join(this.basePath, `${id}.json`);
|
|
2412
2541
|
try {
|
|
2413
2542
|
const data = await fs3.readFile(filePath, "utf-8");
|
|
2414
2543
|
const session = JSON.parse(data);
|
|
@@ -2446,7 +2575,7 @@ var SessionStore = class {
|
|
|
2446
2575
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
2447
2576
|
const sessionsWithFiles = await Promise.all(
|
|
2448
2577
|
jsonFiles.map(async (file) => {
|
|
2449
|
-
const filePath =
|
|
2578
|
+
const filePath = path4.join(this.basePath, file);
|
|
2450
2579
|
const data = await fs3.readFile(filePath, "utf-8");
|
|
2451
2580
|
const session = JSON.parse(data);
|
|
2452
2581
|
session.messages = session.messages.map((msg) => {
|
|
@@ -2481,7 +2610,7 @@ var SessionStore = class {
|
|
|
2481
2610
|
* Delete a session
|
|
2482
2611
|
*/
|
|
2483
2612
|
async delete(id) {
|
|
2484
|
-
const filePath =
|
|
2613
|
+
const filePath = path4.join(this.basePath, `${id}.json`);
|
|
2485
2614
|
try {
|
|
2486
2615
|
await fs3.unlink(filePath);
|
|
2487
2616
|
return true;
|
|
@@ -2520,19 +2649,19 @@ var SessionStore = class {
|
|
|
2520
2649
|
|
|
2521
2650
|
// src/storage/CommandHistoryStore.ts
|
|
2522
2651
|
import { promises as fs4 } from "fs";
|
|
2523
|
-
import
|
|
2652
|
+
import path5 from "path";
|
|
2524
2653
|
import { homedir as homedir2 } from "os";
|
|
2525
2654
|
var MAX_HISTORY_ENTRIES = 1e3;
|
|
2526
2655
|
var CommandHistoryStore = class {
|
|
2527
2656
|
constructor(historyPath) {
|
|
2528
2657
|
this.history = null;
|
|
2529
|
-
this.historyPath = historyPath ||
|
|
2658
|
+
this.historyPath = historyPath || path5.join(homedir2(), ".bike4mind", "history.jsonl");
|
|
2530
2659
|
}
|
|
2531
2660
|
/**
|
|
2532
2661
|
* Initialize history directory
|
|
2533
2662
|
*/
|
|
2534
2663
|
async init() {
|
|
2535
|
-
const dir =
|
|
2664
|
+
const dir = path5.dirname(this.historyPath);
|
|
2536
2665
|
try {
|
|
2537
2666
|
await fs4.mkdir(dir, { recursive: true });
|
|
2538
2667
|
} catch (error) {
|
|
@@ -2635,7 +2764,7 @@ var CommandHistoryStore = class {
|
|
|
2635
2764
|
|
|
2636
2765
|
// src/storage/CustomCommandStore.ts
|
|
2637
2766
|
import fs5 from "fs/promises";
|
|
2638
|
-
import
|
|
2767
|
+
import path6 from "path";
|
|
2639
2768
|
import os from "os";
|
|
2640
2769
|
|
|
2641
2770
|
// src/utils/commandParser.ts
|
|
@@ -2798,14 +2927,14 @@ var CustomCommandStore = class {
|
|
|
2798
2927
|
const home = os.homedir();
|
|
2799
2928
|
const root = projectRoot || process.cwd();
|
|
2800
2929
|
this.globalCommandsDirs = [
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2930
|
+
path6.join(home, ".bike4mind", "commands"),
|
|
2931
|
+
path6.join(home, ".claude", "commands"),
|
|
2932
|
+
path6.join(home, ".claude", "skills")
|
|
2804
2933
|
];
|
|
2805
2934
|
this.projectCommandsDirs = [
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2935
|
+
path6.join(root, ".bike4mind", "commands"),
|
|
2936
|
+
path6.join(root, ".claude", "commands"),
|
|
2937
|
+
path6.join(root, ".claude", "skills")
|
|
2809
2938
|
];
|
|
2810
2939
|
}
|
|
2811
2940
|
/**
|
|
@@ -2865,7 +2994,7 @@ var CustomCommandStore = class {
|
|
|
2865
2994
|
try {
|
|
2866
2995
|
const entries = await fs5.readdir(directory, { withFileTypes: true });
|
|
2867
2996
|
for (const entry of entries) {
|
|
2868
|
-
const fullPath =
|
|
2997
|
+
const fullPath = path6.join(directory, entry.name);
|
|
2869
2998
|
if (entry.isDirectory()) {
|
|
2870
2999
|
const subFiles = await this.findCommandFiles(fullPath);
|
|
2871
3000
|
files.push(...subFiles);
|
|
@@ -2885,7 +3014,7 @@ var CustomCommandStore = class {
|
|
|
2885
3014
|
* @param source - Source identifier ('global' or 'project')
|
|
2886
3015
|
*/
|
|
2887
3016
|
async loadCommandFile(filePath, source) {
|
|
2888
|
-
const filename =
|
|
3017
|
+
const filename = path6.basename(filePath);
|
|
2889
3018
|
const isSkillFile = filename.toLowerCase() === "skill.md";
|
|
2890
3019
|
const commandName = isSkillFile ? this.extractSkillName(filePath) : extractCommandName(filename);
|
|
2891
3020
|
if (!commandName) {
|
|
@@ -2904,7 +3033,7 @@ var CustomCommandStore = class {
|
|
|
2904
3033
|
* @returns Skill name or null if invalid
|
|
2905
3034
|
*/
|
|
2906
3035
|
extractSkillName(filePath) {
|
|
2907
|
-
const parentDir =
|
|
3036
|
+
const parentDir = path6.basename(path6.dirname(filePath));
|
|
2908
3037
|
return parentDir && parentDir !== "skills" ? parentDir : null;
|
|
2909
3038
|
}
|
|
2910
3039
|
/**
|
|
@@ -2966,7 +3095,7 @@ var CustomCommandStore = class {
|
|
|
2966
3095
|
*/
|
|
2967
3096
|
async createCommandFile(name, isGlobal = false) {
|
|
2968
3097
|
const targetDir = isGlobal ? this.globalCommandsDirs[0] : this.projectCommandsDirs[0];
|
|
2969
|
-
const filePath =
|
|
3098
|
+
const filePath = path6.join(targetDir, `${name}.md`);
|
|
2970
3099
|
const fileExists = await fs5.access(filePath).then(
|
|
2971
3100
|
() => true,
|
|
2972
3101
|
() => false
|
|
@@ -3122,6 +3251,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
3122
3251
|
super();
|
|
3123
3252
|
this.steps = [];
|
|
3124
3253
|
this.totalTokens = 0;
|
|
3254
|
+
this.totalCredits = 0;
|
|
3125
3255
|
this.toolCallCount = 0;
|
|
3126
3256
|
this.observationQueue = [];
|
|
3127
3257
|
this.context = {
|
|
@@ -3141,6 +3271,7 @@ var ReActAgent = class extends EventEmitter {
|
|
|
3141
3271
|
async run(query, options = {}) {
|
|
3142
3272
|
this.steps = [];
|
|
3143
3273
|
this.totalTokens = 0;
|
|
3274
|
+
this.totalCredits = 0;
|
|
3144
3275
|
this.toolCallCount = 0;
|
|
3145
3276
|
const maxIterations = options.maxIterations ?? this.context.maxIterations ?? 5;
|
|
3146
3277
|
const temperature = options.temperature ?? this.context.temperature ?? 0.7;
|
|
@@ -3177,6 +3308,7 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3177
3308
|
steps: this.steps,
|
|
3178
3309
|
completionInfo: {
|
|
3179
3310
|
totalTokens: this.totalTokens,
|
|
3311
|
+
totalCredits: this.totalCredits > 0 ? this.totalCredits : void 0,
|
|
3180
3312
|
iterations,
|
|
3181
3313
|
toolCalls: this.toolCallCount,
|
|
3182
3314
|
reachedMaxIterations: false
|
|
@@ -3212,6 +3344,9 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3212
3344
|
const inputTokens = completionInfo.inputTokens || 0;
|
|
3213
3345
|
const outputTokens = completionInfo.outputTokens || 0;
|
|
3214
3346
|
this.totalTokens += inputTokens + outputTokens;
|
|
3347
|
+
if (completionInfo.creditsUsed) {
|
|
3348
|
+
this.totalCredits += completionInfo.creditsUsed;
|
|
3349
|
+
}
|
|
3215
3350
|
if (currentText.trim() && completionInfo.toolsUsed?.length) {
|
|
3216
3351
|
const thoughtStep = {
|
|
3217
3352
|
type: "thought",
|
|
@@ -3325,6 +3460,7 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3325
3460
|
steps: this.steps,
|
|
3326
3461
|
completionInfo: {
|
|
3327
3462
|
totalTokens: this.totalTokens,
|
|
3463
|
+
totalCredits: this.totalCredits > 0 ? this.totalCredits : void 0,
|
|
3328
3464
|
iterations,
|
|
3329
3465
|
toolCalls: this.toolCallCount,
|
|
3330
3466
|
reachedMaxIterations
|
|
@@ -3342,6 +3478,7 @@ ${options.context}` : this.getSystemPrompt()
|
|
|
3342
3478
|
steps: this.steps,
|
|
3343
3479
|
completionInfo: {
|
|
3344
3480
|
totalTokens: this.totalTokens,
|
|
3481
|
+
totalCredits: this.totalCredits > 0 ? this.totalCredits : void 0,
|
|
3345
3482
|
iterations,
|
|
3346
3483
|
toolCalls: this.toolCallCount,
|
|
3347
3484
|
reachedMaxIterations: false
|
|
@@ -3587,6 +3724,50 @@ function isReadOnlyTool(toolName, customCategories) {
|
|
|
3587
3724
|
return getToolCategory(toolName, customCategories) !== "prompt_always";
|
|
3588
3725
|
}
|
|
3589
3726
|
|
|
3727
|
+
// src/core/skillsPrompt.ts
|
|
3728
|
+
function getSkillDisplayName(cmd) {
|
|
3729
|
+
return cmd.displayName || cmd.name;
|
|
3730
|
+
}
|
|
3731
|
+
function formatSkillEntry(cmd) {
|
|
3732
|
+
const displayName = getSkillDisplayName(cmd);
|
|
3733
|
+
const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
3734
|
+
const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
|
|
3735
|
+
return `- ${nameDisplay}${argHint}: ${cmd.description}
|
|
3736
|
+
`;
|
|
3737
|
+
}
|
|
3738
|
+
function formatSkillGroup(heading, commands) {
|
|
3739
|
+
if (commands.length === 0) {
|
|
3740
|
+
return "";
|
|
3741
|
+
}
|
|
3742
|
+
return `
|
|
3743
|
+
### ${heading}
|
|
3744
|
+
${commands.map(formatSkillEntry).join("")}`;
|
|
3745
|
+
}
|
|
3746
|
+
function filterAIVisibleSkills(commands) {
|
|
3747
|
+
return commands.filter((cmd) => !cmd.disableModelInvocation);
|
|
3748
|
+
}
|
|
3749
|
+
function filterSkillsByAllowedList(commands, allowedSkills) {
|
|
3750
|
+
if (!allowedSkills || allowedSkills.length === 0) {
|
|
3751
|
+
return commands;
|
|
3752
|
+
}
|
|
3753
|
+
return commands.filter((cmd) => allowedSkills.includes(cmd.name));
|
|
3754
|
+
}
|
|
3755
|
+
function buildSkillsPromptSection(commands, allowedSkills) {
|
|
3756
|
+
const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
|
|
3757
|
+
const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
|
|
3758
|
+
if (visibleCommands.length === 0) {
|
|
3759
|
+
return "";
|
|
3760
|
+
}
|
|
3761
|
+
const projectSkills = visibleCommands.filter((c) => c.source === "project");
|
|
3762
|
+
const globalSkills = visibleCommands.filter((c) => c.source === "global");
|
|
3763
|
+
return `
|
|
3764
|
+
|
|
3765
|
+
## Available Skills
|
|
3766
|
+
|
|
3767
|
+
Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
|
|
3768
|
+
` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3590
3771
|
// src/core/prompts.ts
|
|
3591
3772
|
var TOOL_GREP_SEARCH = "grep_search";
|
|
3592
3773
|
var TOOL_GLOB_FILES = "glob_files";
|
|
@@ -3597,7 +3778,38 @@ var TOOL_BASH_EXECUTE = "bash_execute";
|
|
|
3597
3778
|
var TOOL_SUBAGENT_DELEGATE = "subagent_delegate";
|
|
3598
3779
|
var TOOL_WRITE_TODOS = "write_todos";
|
|
3599
3780
|
var EXPLORE_SUBAGENT_TYPE = "explore";
|
|
3600
|
-
function buildCoreSystemPrompt(contextSection
|
|
3781
|
+
function buildCoreSystemPrompt(contextSection, config) {
|
|
3782
|
+
let projectContextSection = "";
|
|
3783
|
+
let agentDirectoryContext = "";
|
|
3784
|
+
let skillsSection = "";
|
|
3785
|
+
if (typeof contextSection === "string") {
|
|
3786
|
+
projectContextSection = contextSection;
|
|
3787
|
+
if (config) {
|
|
3788
|
+
if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) {
|
|
3789
|
+
skillsSection = buildSkillsPromptSection(config.customCommands);
|
|
3790
|
+
}
|
|
3791
|
+
if (config.agentStore) {
|
|
3792
|
+
agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
} else if (contextSection && typeof contextSection === "object") {
|
|
3796
|
+
config = contextSection;
|
|
3797
|
+
if (config.contextContent) {
|
|
3798
|
+
projectContextSection = `
|
|
3799
|
+
|
|
3800
|
+
## Project Context
|
|
3801
|
+
|
|
3802
|
+
Follow these project-specific instructions:
|
|
3803
|
+
|
|
3804
|
+
${config.contextContent}`;
|
|
3805
|
+
}
|
|
3806
|
+
if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) {
|
|
3807
|
+
skillsSection = buildSkillsPromptSection(config.customCommands);
|
|
3808
|
+
}
|
|
3809
|
+
if (config.agentStore) {
|
|
3810
|
+
agentDirectoryContext = config.agentStore.getDirectoryContext();
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3601
3813
|
return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
|
|
3602
3814
|
|
|
3603
3815
|
CORE BEHAVIOR:
|
|
@@ -3627,12 +3839,8 @@ When requested to perform tasks like fixing bugs, adding features, refactoring,
|
|
|
3627
3839
|
|
|
3628
3840
|
SUBAGENT DELEGATION:
|
|
3629
3841
|
- You have access to specialized subagents via the ${TOOL_SUBAGENT_DELEGATE} tool
|
|
3630
|
-
-
|
|
3631
|
-
* explore: Fast read-only codebase search (e.g., "find all auth files", "locate API endpoints")
|
|
3632
|
-
* plan: Break down complex tasks into actionable steps
|
|
3633
|
-
* review: Analyze code quality and identify issues
|
|
3842
|
+
- ${agentDirectoryContext ? `${agentDirectoryContext}` : ""}
|
|
3634
3843
|
- Subagents keep the main conversation clean and run faster with optimized models
|
|
3635
|
-
- Delegate when you need to search extensively or analyze code structure
|
|
3636
3844
|
|
|
3637
3845
|
CODE SEARCH BEST PRACTICES:
|
|
3638
3846
|
When searching code, follow this hierarchy for speed and efficiency:
|
|
@@ -3689,7 +3897,7 @@ EXAMPLES:
|
|
|
3689
3897
|
- "what packages installed?" \u2192 ${TOOL_GLOB_FILES} "**/package.json" \u2192 ${TOOL_FILE_READ}
|
|
3690
3898
|
- "find all components using React hooks" \u2192 ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
|
|
3691
3899
|
|
|
3692
|
-
Remember: Use context from previous messages to understand follow-up questions.${
|
|
3900
|
+
Remember: Use context from previous messages to understand follow-up questions.${projectContextSection}${skillsSection}`;
|
|
3693
3901
|
}
|
|
3694
3902
|
|
|
3695
3903
|
// ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
|
|
@@ -3865,7 +4073,8 @@ var recalculateUserStorageSchema = z14.object({
|
|
|
3865
4073
|
// ../../b4m-core/packages/services/dist/src/userService/listRecentActivities.js
|
|
3866
4074
|
import { z as z15 } from "zod";
|
|
3867
4075
|
var listRecentActivitiesSchema = z15.object({
|
|
3868
|
-
coverage: z15.enum(["all", "important"]).default("important")
|
|
4076
|
+
coverage: z15.enum(["all", "important"]).default("important"),
|
|
4077
|
+
userId: z15.string().optional()
|
|
3869
4078
|
});
|
|
3870
4079
|
var IMPORTANT_COUNTER_NAMES = [
|
|
3871
4080
|
SessionEvents.CREATE_SESSION,
|
|
@@ -5509,7 +5718,19 @@ import { z as z129 } from "zod";
|
|
|
5509
5718
|
var createArtifactSchema = z129.object({
|
|
5510
5719
|
id: z129.string().optional(),
|
|
5511
5720
|
// Allow custom ID for AI-generated artifacts
|
|
5512
|
-
type: z129.enum([
|
|
5721
|
+
type: z129.enum([
|
|
5722
|
+
"mermaid",
|
|
5723
|
+
"recharts",
|
|
5724
|
+
"python",
|
|
5725
|
+
"react",
|
|
5726
|
+
"html",
|
|
5727
|
+
"svg",
|
|
5728
|
+
"code",
|
|
5729
|
+
"quest",
|
|
5730
|
+
"file",
|
|
5731
|
+
"questmaster",
|
|
5732
|
+
"lattice"
|
|
5733
|
+
]),
|
|
5513
5734
|
title: z129.string().min(1).max(255),
|
|
5514
5735
|
description: z129.string().max(1e3).optional(),
|
|
5515
5736
|
content: z129.string().min(1),
|
|
@@ -5818,8 +6039,8 @@ async function processAndStoreImages(images, context) {
|
|
|
5818
6039
|
const buffer = await downloadImage(image);
|
|
5819
6040
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
5820
6041
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
5821
|
-
const
|
|
5822
|
-
return
|
|
6042
|
+
const path19 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6043
|
+
return path19;
|
|
5823
6044
|
}));
|
|
5824
6045
|
}
|
|
5825
6046
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -7145,8 +7366,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
7145
7366
|
const buffer = await downloadImage2(imageUrl);
|
|
7146
7367
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
7147
7368
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
7148
|
-
const
|
|
7149
|
-
return
|
|
7369
|
+
const path19 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
7370
|
+
return path19;
|
|
7150
7371
|
}
|
|
7151
7372
|
async function generateFullMask(imageBuffer) {
|
|
7152
7373
|
const sharp = (await import("sharp")).default;
|
|
@@ -9088,18 +9309,18 @@ var planetVisibilityTool = {
|
|
|
9088
9309
|
|
|
9089
9310
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
|
|
9090
9311
|
import { promises as fs7 } from "fs";
|
|
9091
|
-
import { existsSync as
|
|
9092
|
-
import
|
|
9312
|
+
import { existsSync as existsSync4, statSync as statSync4 } from "fs";
|
|
9313
|
+
import path7 from "path";
|
|
9093
9314
|
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
9094
9315
|
async function readFileContent(params) {
|
|
9095
9316
|
const { path: filePath, encoding = "utf-8", offset = 0, limit } = params;
|
|
9096
|
-
const normalizedPath =
|
|
9097
|
-
const resolvedPath =
|
|
9098
|
-
const cwd =
|
|
9317
|
+
const normalizedPath = path7.normalize(filePath);
|
|
9318
|
+
const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
|
|
9319
|
+
const cwd = path7.resolve(process.cwd());
|
|
9099
9320
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9100
9321
|
throw new Error(`Access denied: Cannot read files outside of current working directory`);
|
|
9101
9322
|
}
|
|
9102
|
-
if (!
|
|
9323
|
+
if (!existsSync4(resolvedPath)) {
|
|
9103
9324
|
throw new Error(`File not found: ${filePath}`);
|
|
9104
9325
|
}
|
|
9105
9326
|
const stats = statSync4(resolvedPath);
|
|
@@ -9168,7 +9389,7 @@ var fileReadTool = {
|
|
|
9168
9389
|
context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
|
|
9169
9390
|
try {
|
|
9170
9391
|
const content = await readFileContent(params);
|
|
9171
|
-
const stats = statSync4(
|
|
9392
|
+
const stats = statSync4(path7.resolve(process.cwd(), path7.normalize(params.path)));
|
|
9172
9393
|
context.logger.info("\u2705 FileRead: Success", {
|
|
9173
9394
|
path: params.path,
|
|
9174
9395
|
size: stats.size,
|
|
@@ -9213,20 +9434,20 @@ var fileReadTool = {
|
|
|
9213
9434
|
|
|
9214
9435
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
|
|
9215
9436
|
import { promises as fs8 } from "fs";
|
|
9216
|
-
import { existsSync as
|
|
9217
|
-
import
|
|
9437
|
+
import { existsSync as existsSync5 } from "fs";
|
|
9438
|
+
import path8 from "path";
|
|
9218
9439
|
async function createFile(params) {
|
|
9219
9440
|
const { path: filePath, content, createDirectories = true } = params;
|
|
9220
|
-
const normalizedPath =
|
|
9221
|
-
const resolvedPath =
|
|
9222
|
-
const cwd =
|
|
9441
|
+
const normalizedPath = path8.normalize(filePath);
|
|
9442
|
+
const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
|
|
9443
|
+
const cwd = path8.resolve(process.cwd());
|
|
9223
9444
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9224
9445
|
throw new Error(`Access denied: Cannot create files outside of current working directory`);
|
|
9225
9446
|
}
|
|
9226
|
-
const fileExists =
|
|
9447
|
+
const fileExists = existsSync5(resolvedPath);
|
|
9227
9448
|
const action = fileExists ? "overwritten" : "created";
|
|
9228
9449
|
if (createDirectories) {
|
|
9229
|
-
const dir =
|
|
9450
|
+
const dir = path8.dirname(resolvedPath);
|
|
9230
9451
|
await fs8.mkdir(dir, { recursive: true });
|
|
9231
9452
|
}
|
|
9232
9453
|
await fs8.writeFile(resolvedPath, content, "utf-8");
|
|
@@ -9241,7 +9462,7 @@ var createFileTool = {
|
|
|
9241
9462
|
implementation: (context) => ({
|
|
9242
9463
|
toolFn: async (value) => {
|
|
9243
9464
|
const params = value;
|
|
9244
|
-
const fileExists =
|
|
9465
|
+
const fileExists = existsSync5(path8.resolve(process.cwd(), path8.normalize(params.path)));
|
|
9245
9466
|
context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
|
|
9246
9467
|
path: params.path,
|
|
9247
9468
|
size: params.content.length
|
|
@@ -9283,8 +9504,8 @@ var createFileTool = {
|
|
|
9283
9504
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
|
|
9284
9505
|
import { glob } from "glob";
|
|
9285
9506
|
import { stat } from "fs/promises";
|
|
9286
|
-
import
|
|
9287
|
-
var
|
|
9507
|
+
import path9 from "path";
|
|
9508
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
9288
9509
|
"**/node_modules/**",
|
|
9289
9510
|
"**/.git/**",
|
|
9290
9511
|
"**/dist/**",
|
|
@@ -9299,11 +9520,11 @@ var DEFAULT_IGNORE_PATTERNS2 = [
|
|
|
9299
9520
|
async function findFiles(params) {
|
|
9300
9521
|
const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
|
|
9301
9522
|
const baseCwd = process.cwd();
|
|
9302
|
-
const targetDir = dir_path ?
|
|
9523
|
+
const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
|
|
9303
9524
|
if (!targetDir.startsWith(baseCwd)) {
|
|
9304
9525
|
throw new Error(`Access denied: Cannot search outside of current working directory`);
|
|
9305
9526
|
}
|
|
9306
|
-
const ignorePatterns = respect_git_ignore ?
|
|
9527
|
+
const ignorePatterns = respect_git_ignore ? DEFAULT_IGNORE_PATTERNS : [];
|
|
9307
9528
|
const matches = await glob(pattern, {
|
|
9308
9529
|
cwd: targetDir,
|
|
9309
9530
|
dot: false,
|
|
@@ -9339,7 +9560,7 @@ async function findFiles(params) {
|
|
|
9339
9560
|
const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
|
|
9340
9561
|
const dirInfo = dir_path ? `
|
|
9341
9562
|
Directory: ${dir_path}` : "";
|
|
9342
|
-
const filesList = results.map((file) =>
|
|
9563
|
+
const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
|
|
9343
9564
|
return `${summary}${dirInfo}
|
|
9344
9565
|
|
|
9345
9566
|
${filesList}`;
|
|
@@ -9393,7 +9614,7 @@ var globFilesTool = {
|
|
|
9393
9614
|
|
|
9394
9615
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
|
|
9395
9616
|
import { stat as stat2 } from "fs/promises";
|
|
9396
|
-
import
|
|
9617
|
+
import path10 from "path";
|
|
9397
9618
|
import { execFile } from "child_process";
|
|
9398
9619
|
import { promisify } from "util";
|
|
9399
9620
|
import { createRequire } from "module";
|
|
@@ -9402,18 +9623,18 @@ var require2 = createRequire(import.meta.url);
|
|
|
9402
9623
|
function getRipgrepPath() {
|
|
9403
9624
|
try {
|
|
9404
9625
|
const ripgrepPath = require2.resolve("@vscode/ripgrep");
|
|
9405
|
-
const ripgrepDir =
|
|
9406
|
-
const rgBinary =
|
|
9626
|
+
const ripgrepDir = path10.dirname(ripgrepPath);
|
|
9627
|
+
const rgBinary = path10.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
|
|
9407
9628
|
return rgBinary;
|
|
9408
9629
|
} catch (error) {
|
|
9409
9630
|
throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
|
|
9410
9631
|
}
|
|
9411
9632
|
}
|
|
9412
9633
|
function isPathWithinWorkspace(targetPath, baseCwd) {
|
|
9413
|
-
const resolvedTarget =
|
|
9414
|
-
const resolvedBase =
|
|
9415
|
-
const relativePath =
|
|
9416
|
-
return !relativePath.startsWith("..") && !
|
|
9634
|
+
const resolvedTarget = path10.resolve(targetPath);
|
|
9635
|
+
const resolvedBase = path10.resolve(baseCwd);
|
|
9636
|
+
const relativePath = path10.relative(resolvedBase, resolvedTarget);
|
|
9637
|
+
return !relativePath.startsWith("..") && !path10.isAbsolute(relativePath);
|
|
9417
9638
|
}
|
|
9418
9639
|
function convertGlobToRipgrepGlobs(globPattern) {
|
|
9419
9640
|
if (!globPattern || globPattern === "**/*") {
|
|
@@ -9429,7 +9650,7 @@ function convertGlobToRipgrepGlobs(globPattern) {
|
|
|
9429
9650
|
async function searchFiles2(params) {
|
|
9430
9651
|
const { pattern, dir_path, include } = params;
|
|
9431
9652
|
const baseCwd = process.cwd();
|
|
9432
|
-
const targetDir = dir_path ?
|
|
9653
|
+
const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
|
|
9433
9654
|
if (!isPathWithinWorkspace(targetDir, baseCwd)) {
|
|
9434
9655
|
throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
|
|
9435
9656
|
}
|
|
@@ -9491,7 +9712,7 @@ async function searchFiles2(params) {
|
|
|
9491
9712
|
if (item.type === "match") {
|
|
9492
9713
|
const match = item;
|
|
9493
9714
|
allMatches.push({
|
|
9494
|
-
filePath:
|
|
9715
|
+
filePath: path10.relative(targetDir, match.data.path.text) || path10.basename(match.data.path.text),
|
|
9495
9716
|
lineNumber: match.data.line_number,
|
|
9496
9717
|
line: match.data.lines.text.trimEnd()
|
|
9497
9718
|
// Remove trailing newline
|
|
@@ -9585,17 +9806,17 @@ var grepSearchTool = {
|
|
|
9585
9806
|
|
|
9586
9807
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
|
|
9587
9808
|
import { promises as fs9 } from "fs";
|
|
9588
|
-
import { existsSync as
|
|
9589
|
-
import
|
|
9809
|
+
import { existsSync as existsSync6, statSync as statSync5 } from "fs";
|
|
9810
|
+
import path11 from "path";
|
|
9590
9811
|
async function deleteFile(params) {
|
|
9591
9812
|
const { path: filePath, recursive = false } = params;
|
|
9592
|
-
const normalizedPath =
|
|
9593
|
-
const resolvedPath =
|
|
9594
|
-
const cwd =
|
|
9813
|
+
const normalizedPath = path11.normalize(filePath);
|
|
9814
|
+
const resolvedPath = path11.resolve(process.cwd(), normalizedPath);
|
|
9815
|
+
const cwd = path11.resolve(process.cwd());
|
|
9595
9816
|
if (!resolvedPath.startsWith(cwd)) {
|
|
9596
9817
|
throw new Error(`Access denied: Cannot delete files outside of current working directory`);
|
|
9597
9818
|
}
|
|
9598
|
-
if (!
|
|
9819
|
+
if (!existsSync6(resolvedPath)) {
|
|
9599
9820
|
throw new Error(`File or directory not found: ${filePath}`);
|
|
9600
9821
|
}
|
|
9601
9822
|
const stats = statSync5(resolvedPath);
|
|
@@ -9618,8 +9839,8 @@ var deleteFileTool = {
|
|
|
9618
9839
|
implementation: (context) => ({
|
|
9619
9840
|
toolFn: async (value) => {
|
|
9620
9841
|
const params = value;
|
|
9621
|
-
const resolvedPath =
|
|
9622
|
-
const isDirectory =
|
|
9842
|
+
const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
|
|
9843
|
+
const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
|
|
9623
9844
|
context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
|
|
9624
9845
|
path: params.path,
|
|
9625
9846
|
recursive: params.recursive
|
|
@@ -9744,7 +9965,7 @@ var knowledgeBaseSearchTool = {
|
|
|
9744
9965
|
|
|
9745
9966
|
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
|
|
9746
9967
|
import { spawn } from "child_process";
|
|
9747
|
-
import
|
|
9968
|
+
import path12 from "path";
|
|
9748
9969
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
9749
9970
|
var MAX_OUTPUT_SIZE = 100 * 1024;
|
|
9750
9971
|
var DANGEROUS_PATTERNS = [
|
|
@@ -9928,7 +10149,7 @@ async function executeBashCommand(params) {
|
|
|
9928
10149
|
};
|
|
9929
10150
|
}
|
|
9930
10151
|
const baseCwd = process.cwd();
|
|
9931
|
-
const targetCwd = relativeCwd ?
|
|
10152
|
+
const targetCwd = relativeCwd ? path12.resolve(baseCwd, relativeCwd) : baseCwd;
|
|
9932
10153
|
const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
|
|
9933
10154
|
return new Promise((resolve3) => {
|
|
9934
10155
|
let stdout = "";
|
|
@@ -10117,69 +10338,882 @@ BLOCKED OPERATIONS:
|
|
|
10117
10338
|
})
|
|
10118
10339
|
};
|
|
10119
10340
|
|
|
10120
|
-
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
const { path: filePath, old_string, new_string } = params;
|
|
10147
|
-
const normalizedPath = path12.normalize(filePath);
|
|
10148
|
-
const resolvedPath = path12.resolve(process.cwd(), normalizedPath);
|
|
10149
|
-
const cwd = path12.resolve(process.cwd());
|
|
10150
|
-
if (!resolvedPath.startsWith(cwd)) {
|
|
10151
|
-
throw new Error(`Access denied: Cannot edit files outside of current working directory`);
|
|
10152
|
-
}
|
|
10153
|
-
if (!existsSync6(resolvedPath)) {
|
|
10154
|
-
throw new Error(`File not found: ${filePath}`);
|
|
10155
|
-
}
|
|
10156
|
-
const currentContent = await fs10.readFile(resolvedPath, "utf-8");
|
|
10157
|
-
if (!currentContent.includes(old_string)) {
|
|
10158
|
-
const preview = old_string.length > 100 ? old_string.substring(0, 100) + "..." : old_string;
|
|
10159
|
-
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}"`);
|
|
10160
|
-
}
|
|
10161
|
-
const occurrences = currentContent.split(old_string).length - 1;
|
|
10162
|
-
if (occurrences > 1) {
|
|
10163
|
-
throw new Error(`Found ${occurrences} occurrences of the string to replace. Please provide a more specific old_string that matches exactly one location.`);
|
|
10164
|
-
}
|
|
10165
|
-
const newContent = currentContent.replace(old_string, new_string);
|
|
10166
|
-
await fs10.writeFile(resolvedPath, newContent, "utf-8");
|
|
10167
|
-
const diffResult = generateDiff(old_string, new_string);
|
|
10168
|
-
return `File edited successfully: ${filePath}
|
|
10169
|
-
Changes: +${diffResult.additions} lines, -${diffResult.deletions} lines
|
|
10170
|
-
|
|
10171
|
-
Diff:
|
|
10172
|
-
${diffResult.diff}`;
|
|
10341
|
+
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/lattice/index.js
|
|
10342
|
+
function createModelData(name, modelType, userId, description, sessionId) {
|
|
10343
|
+
const now = /* @__PURE__ */ new Date();
|
|
10344
|
+
return {
|
|
10345
|
+
name,
|
|
10346
|
+
description: description || "",
|
|
10347
|
+
modelType,
|
|
10348
|
+
userId,
|
|
10349
|
+
sessionId,
|
|
10350
|
+
data: { entities: [], relationships: [] },
|
|
10351
|
+
rules: { rules: [], rulesets: [] },
|
|
10352
|
+
views: { views: [] },
|
|
10353
|
+
settings: {
|
|
10354
|
+
currency: "USD",
|
|
10355
|
+
fiscalYearStart: "01-01",
|
|
10356
|
+
periodGrain: "quarter",
|
|
10357
|
+
defaultDecimalPlaces: 2,
|
|
10358
|
+
negativeFormat: "parentheses"
|
|
10359
|
+
},
|
|
10360
|
+
scenarios: [],
|
|
10361
|
+
operations: [],
|
|
10362
|
+
operationIndex: -1,
|
|
10363
|
+
version: 1,
|
|
10364
|
+
createdAt: now,
|
|
10365
|
+
updatedAt: now
|
|
10366
|
+
};
|
|
10173
10367
|
}
|
|
10174
|
-
var
|
|
10175
|
-
name: "
|
|
10368
|
+
var latticeCreateModelTool = {
|
|
10369
|
+
name: "lattice_create_model",
|
|
10176
10370
|
implementation: (context) => ({
|
|
10177
|
-
toolFn: async (
|
|
10178
|
-
const
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10371
|
+
toolFn: async (params) => {
|
|
10372
|
+
const { name, description, modelType = "custom", initialData } = params;
|
|
10373
|
+
console.log("[LATTICE DEBUG] \u{1F3D7}\uFE0F lattice_create_model called:", {
|
|
10374
|
+
name,
|
|
10375
|
+
description,
|
|
10376
|
+
modelType,
|
|
10377
|
+
hasInitialData: !!initialData,
|
|
10378
|
+
entityCount: initialData?.entities?.length || 0,
|
|
10379
|
+
ruleCount: initialData?.rules?.length || 0
|
|
10380
|
+
});
|
|
10381
|
+
const modelData = createModelData(name, modelType, context.userId, description);
|
|
10382
|
+
if (initialData?.entities && modelData.data) {
|
|
10383
|
+
const now = /* @__PURE__ */ new Date();
|
|
10384
|
+
for (const entityDef of initialData.entities) {
|
|
10385
|
+
const entityId = entityDef.name.toLowerCase().replace(/\s+/g, "_");
|
|
10386
|
+
const attributes = [];
|
|
10387
|
+
if (entityDef.values) {
|
|
10388
|
+
for (const val of entityDef.values) {
|
|
10389
|
+
attributes.push({
|
|
10390
|
+
key: "period",
|
|
10391
|
+
value: val.period,
|
|
10392
|
+
dataType: "string",
|
|
10393
|
+
isComputed: false
|
|
10394
|
+
});
|
|
10395
|
+
attributes.push({
|
|
10396
|
+
key: "value",
|
|
10397
|
+
value: val.value,
|
|
10398
|
+
dataType: "currency",
|
|
10399
|
+
isComputed: false
|
|
10400
|
+
});
|
|
10401
|
+
attributes.push({
|
|
10402
|
+
key: "category",
|
|
10403
|
+
value: entityDef.name,
|
|
10404
|
+
dataType: "string",
|
|
10405
|
+
isComputed: false
|
|
10406
|
+
});
|
|
10407
|
+
modelData.data.entities.push({
|
|
10408
|
+
id: `${entityId}_${val.period.toLowerCase().replace(/\s+/g, "_")}`,
|
|
10409
|
+
type: entityDef.type || "line_item",
|
|
10410
|
+
name: `${entityDef.name} ${val.period}`,
|
|
10411
|
+
displayName: `${entityDef.name} ${val.period}`,
|
|
10412
|
+
attributes: [
|
|
10413
|
+
{ key: "period", value: val.period, dataType: "string", isComputed: false },
|
|
10414
|
+
{ key: "value", value: val.value, dataType: "currency", isComputed: false },
|
|
10415
|
+
{ key: "category", value: entityDef.name, dataType: "string", isComputed: false }
|
|
10416
|
+
],
|
|
10417
|
+
metadata: {},
|
|
10418
|
+
createdAt: now,
|
|
10419
|
+
updatedAt: now
|
|
10420
|
+
});
|
|
10421
|
+
}
|
|
10422
|
+
}
|
|
10423
|
+
console.log(`[LATTICE DEBUG] \u2795 Created entity: ${entityDef.name} with ${entityDef.values?.length || 0} period values`);
|
|
10424
|
+
}
|
|
10425
|
+
}
|
|
10426
|
+
if (initialData?.rules && modelData.rules) {
|
|
10427
|
+
const now = /* @__PURE__ */ new Date();
|
|
10428
|
+
for (const ruleDef of initialData.rules) {
|
|
10429
|
+
const ruleId = `rule_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
|
|
10430
|
+
const parsedRule = parseFormula(ruleDef.formula);
|
|
10431
|
+
modelData.rules.rules.push({
|
|
10432
|
+
id: ruleId,
|
|
10433
|
+
name: ruleDef.name,
|
|
10434
|
+
description: ruleDef.formula,
|
|
10435
|
+
type: "formula",
|
|
10436
|
+
definition: {
|
|
10437
|
+
operation: parsedRule.operation,
|
|
10438
|
+
inputs: parsedRule.inputs.map((ref) => ({ type: "entity", ref })),
|
|
10439
|
+
output: {
|
|
10440
|
+
targetEntityId: parsedRule.outputEntity.toLowerCase().replace(/\s+/g, "_"),
|
|
10441
|
+
targetAttribute: "computed",
|
|
10442
|
+
dataType: "number"
|
|
10443
|
+
}
|
|
10444
|
+
},
|
|
10445
|
+
dependencies: parsedRule.inputs.map((i) => i.toLowerCase().replace(/\s+/g, "_")),
|
|
10446
|
+
priority: 0,
|
|
10447
|
+
enabled: true,
|
|
10448
|
+
createdAt: now,
|
|
10449
|
+
updatedAt: now
|
|
10450
|
+
});
|
|
10451
|
+
console.log(`[LATTICE DEBUG] \u{1F4D0} Created rule: ${ruleDef.name} = ${ruleDef.formula}`);
|
|
10452
|
+
}
|
|
10453
|
+
}
|
|
10454
|
+
let model;
|
|
10455
|
+
let persistenceStatus = "unknown";
|
|
10456
|
+
const dbKeys = Object.keys(context.db || {});
|
|
10457
|
+
const hasLatticeModels = !!context.db?.latticeModels;
|
|
10458
|
+
if (context.db.latticeModels) {
|
|
10459
|
+
try {
|
|
10460
|
+
model = await context.db.latticeModels.create(modelData);
|
|
10461
|
+
persistenceStatus = `PERSISTED to MongoDB (id: ${model.id})`;
|
|
10462
|
+
context.logger.info(`[Lattice] Created model ${model.id} in database`);
|
|
10463
|
+
} catch (error) {
|
|
10464
|
+
persistenceStatus = `FAILED: ${error instanceof Error ? error.message : String(error)}`;
|
|
10465
|
+
context.logger.error(`[Lattice] Failed to persist model to database:`, error);
|
|
10466
|
+
model = {
|
|
10467
|
+
...modelData,
|
|
10468
|
+
id: `lattice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
10469
|
+
};
|
|
10470
|
+
}
|
|
10471
|
+
} else {
|
|
10472
|
+
persistenceStatus = `IN-MEMORY (no latticeModels adapter). DB keys: [${dbKeys.join(", ")}]`;
|
|
10473
|
+
context.logger.warn("[Lattice] No database adapter available, creating in-memory model");
|
|
10474
|
+
model = {
|
|
10475
|
+
...modelData,
|
|
10476
|
+
id: `lattice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
10477
|
+
};
|
|
10478
|
+
}
|
|
10479
|
+
const artifactData = {
|
|
10480
|
+
type: "lattice",
|
|
10481
|
+
id: model.id,
|
|
10482
|
+
title: name,
|
|
10483
|
+
content: JSON.stringify(model),
|
|
10484
|
+
metadata: {
|
|
10485
|
+
modelType,
|
|
10486
|
+
periodGrain: model.settings?.periodGrain || "quarter",
|
|
10487
|
+
currency: model.settings?.currency || "USD",
|
|
10488
|
+
entityCount: model.data?.entities?.length || 0,
|
|
10489
|
+
ruleCount: model.rules?.rules?.length || 0,
|
|
10490
|
+
// DEBUG: Include persistence info in metadata
|
|
10491
|
+
_debug: {
|
|
10492
|
+
persistenceStatus,
|
|
10493
|
+
hasLatticeModels,
|
|
10494
|
+
dbKeys
|
|
10495
|
+
}
|
|
10496
|
+
},
|
|
10497
|
+
createdAt: model.createdAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString(),
|
|
10498
|
+
updatedAt: model.updatedAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString()
|
|
10499
|
+
};
|
|
10500
|
+
const entityCount = model.data?.entities?.length || 0;
|
|
10501
|
+
const ruleCount = model.rules?.rules?.length || 0;
|
|
10502
|
+
return `Created "${name}" model with ${entityCount} entities and ${ruleCount} rules.
|
|
10503
|
+
|
|
10504
|
+
<artifact identifier="${model.id}" type="application/vnd.b4m.lattice" title="${name}">
|
|
10505
|
+
${JSON.stringify(artifactData, null, 2)}
|
|
10506
|
+
</artifact>
|
|
10507
|
+
|
|
10508
|
+
The model is ready for viewing. You can add more data by asking to add line items or create formulas.`;
|
|
10509
|
+
},
|
|
10510
|
+
toolSchema: {
|
|
10511
|
+
name: "lattice_create_model",
|
|
10512
|
+
description: `Create a new Lattice financial model with optional initial data. Use initialData to populate entities and rules in ONE call.
|
|
10513
|
+
|
|
10514
|
+
**IMPORTANT**: Always include initialData when the user provides specific values! This creates a fully populated model immediately.
|
|
10515
|
+
|
|
10516
|
+
**When to use:** When the user wants to:
|
|
10517
|
+
- Create a new financial model, spreadsheet, or pro-forma
|
|
10518
|
+
- Start building an income statement, balance sheet, or cash flow
|
|
10519
|
+
- Set up a SaaS metrics dashboard
|
|
10520
|
+
|
|
10521
|
+
**Example with initialData:**
|
|
10522
|
+
User: "Create income statement with $100K revenue and $60K costs for Q1"
|
|
10523
|
+
Call: lattice_create_model with:
|
|
10524
|
+
- name: "Income Statement"
|
|
10525
|
+
- modelType: "income_statement"
|
|
10526
|
+
- initialData: {
|
|
10527
|
+
entities: [
|
|
10528
|
+
{ name: "Revenue", values: [{ period: "Q1", value: 100000 }] },
|
|
10529
|
+
{ name: "Costs", values: [{ period: "Q1", value: 60000 }] }
|
|
10530
|
+
],
|
|
10531
|
+
rules: [
|
|
10532
|
+
{ name: "Gross Profit", formula: "Gross Profit = Revenue - Costs" }
|
|
10533
|
+
]
|
|
10534
|
+
}`,
|
|
10535
|
+
parameters: {
|
|
10536
|
+
type: "object",
|
|
10537
|
+
properties: {
|
|
10538
|
+
name: {
|
|
10539
|
+
type: "string",
|
|
10540
|
+
description: 'Name of the model (e.g., "2024 Budget", "Q1 Forecast")'
|
|
10541
|
+
},
|
|
10542
|
+
description: {
|
|
10543
|
+
type: "string",
|
|
10544
|
+
description: "Optional description of what this model represents"
|
|
10545
|
+
},
|
|
10546
|
+
modelType: {
|
|
10547
|
+
type: "string",
|
|
10548
|
+
enum: ["income_statement", "balance_sheet", "cashflow", "saas_metrics", "custom"],
|
|
10549
|
+
description: "Type of financial model to create"
|
|
10550
|
+
},
|
|
10551
|
+
initialData: {
|
|
10552
|
+
type: "object",
|
|
10553
|
+
description: "Initial entities and rules to populate the model. ALWAYS use this when user provides data!",
|
|
10554
|
+
properties: {
|
|
10555
|
+
entities: {
|
|
10556
|
+
type: "array",
|
|
10557
|
+
description: "Line items with their period values",
|
|
10558
|
+
items: {
|
|
10559
|
+
type: "object",
|
|
10560
|
+
properties: {
|
|
10561
|
+
name: { type: "string", description: 'Entity name (e.g., "Revenue", "COGS")' },
|
|
10562
|
+
type: { type: "string", description: "Entity type (default: line_item)" },
|
|
10563
|
+
values: {
|
|
10564
|
+
type: "array",
|
|
10565
|
+
description: "Period-value pairs",
|
|
10566
|
+
items: {
|
|
10567
|
+
type: "object",
|
|
10568
|
+
properties: {
|
|
10569
|
+
period: { type: "string", description: 'Period name (e.g., "Q1", "Jan", "2024")' },
|
|
10570
|
+
value: { type: "number", description: "Numeric value" }
|
|
10571
|
+
},
|
|
10572
|
+
required: ["period", "value"]
|
|
10573
|
+
}
|
|
10574
|
+
}
|
|
10575
|
+
},
|
|
10576
|
+
required: ["name"]
|
|
10577
|
+
}
|
|
10578
|
+
},
|
|
10579
|
+
rules: {
|
|
10580
|
+
type: "array",
|
|
10581
|
+
description: "Formulas/calculations to create",
|
|
10582
|
+
items: {
|
|
10583
|
+
type: "object",
|
|
10584
|
+
properties: {
|
|
10585
|
+
name: { type: "string", description: 'Rule name (e.g., "Gross Profit Calculation")' },
|
|
10586
|
+
formula: {
|
|
10587
|
+
type: "string",
|
|
10588
|
+
description: 'Natural language formula (e.g., "Gross Profit = Revenue - Costs")'
|
|
10589
|
+
}
|
|
10590
|
+
},
|
|
10591
|
+
required: ["name", "formula"]
|
|
10592
|
+
}
|
|
10593
|
+
}
|
|
10594
|
+
}
|
|
10595
|
+
}
|
|
10596
|
+
},
|
|
10597
|
+
required: ["name"]
|
|
10598
|
+
}
|
|
10599
|
+
}
|
|
10600
|
+
})
|
|
10601
|
+
};
|
|
10602
|
+
var latticeAddEntityTool = {
|
|
10603
|
+
name: "lattice_add_entity",
|
|
10604
|
+
implementation: (context) => ({
|
|
10605
|
+
toolFn: async (params) => {
|
|
10606
|
+
const { modelId, name, type, displayName, initialValues = [] } = params;
|
|
10607
|
+
console.log("[LATTICE DEBUG] \u2795 lattice_add_entity called:", { modelId, name, type, initialValues });
|
|
10608
|
+
const entityId = name.toLowerCase().replace(/\s+/g, "_");
|
|
10609
|
+
const entityData = {
|
|
10610
|
+
id: entityId,
|
|
10611
|
+
type,
|
|
10612
|
+
name,
|
|
10613
|
+
displayName: displayName || name,
|
|
10614
|
+
attributes: initialValues.map((v) => ({
|
|
10615
|
+
key: v.key,
|
|
10616
|
+
value: v.value,
|
|
10617
|
+
dataType: v.dataType || (typeof v.value === "number" ? "number" : "string"),
|
|
10618
|
+
isComputed: false
|
|
10619
|
+
})),
|
|
10620
|
+
metadata: {},
|
|
10621
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
10622
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
10623
|
+
};
|
|
10624
|
+
if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
|
|
10625
|
+
try {
|
|
10626
|
+
const model = await context.db.latticeModels.findById(modelId);
|
|
10627
|
+
if (model) {
|
|
10628
|
+
const existingIndex = model.data.entities.findIndex((e) => e.id === entityId);
|
|
10629
|
+
if (existingIndex >= 0) {
|
|
10630
|
+
model.data.entities[existingIndex] = entityData;
|
|
10631
|
+
} else {
|
|
10632
|
+
model.data.entities.push(entityData);
|
|
10633
|
+
}
|
|
10634
|
+
await context.db.latticeModels.update({
|
|
10635
|
+
id: modelId,
|
|
10636
|
+
data: model.data,
|
|
10637
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
10638
|
+
});
|
|
10639
|
+
context.logger.info(`[Lattice] Added entity ${entityId} to model ${modelId}`);
|
|
10640
|
+
} else {
|
|
10641
|
+
context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
|
|
10642
|
+
}
|
|
10643
|
+
} catch (error) {
|
|
10644
|
+
context.logger.error(`[Lattice] Failed to persist entity to database:`, error);
|
|
10645
|
+
}
|
|
10646
|
+
}
|
|
10647
|
+
return JSON.stringify({
|
|
10648
|
+
success: true,
|
|
10649
|
+
action: "ADD_ENTITY",
|
|
10650
|
+
modelId,
|
|
10651
|
+
entityId,
|
|
10652
|
+
data: entityData,
|
|
10653
|
+
message: `Added ${type} "${displayName || name}" to model. ${initialValues.length > 0 ? `Set initial values: ${initialValues.map((v) => `${v.key}=${v.value}`).join(", ")}` : "No initial values set."}`
|
|
10654
|
+
});
|
|
10655
|
+
},
|
|
10656
|
+
toolSchema: {
|
|
10657
|
+
name: "lattice_add_entity",
|
|
10658
|
+
description: `Add a line item, account, period, or other entity to a Lattice model.
|
|
10659
|
+
|
|
10660
|
+
**When to use:** When the user mentions:
|
|
10661
|
+
- Adding a revenue line, expense category, or account
|
|
10662
|
+
- Creating periods (Q1, Q2, Jan, Feb, etc.)
|
|
10663
|
+
- Adding any measurable item to their model
|
|
10664
|
+
|
|
10665
|
+
**Entity types:**
|
|
10666
|
+
- line_item: Revenue streams, expense lines, KPIs
|
|
10667
|
+
- account: Cash, AR, AP, inventory, etc.
|
|
10668
|
+
- period: Q1 2024, Jan, FY2025, etc.
|
|
10669
|
+
- category: Groups of line items (Operating Expenses)
|
|
10670
|
+
- scenario: Base case, upside, downside
|
|
10671
|
+
- custom: Any other structured element
|
|
10672
|
+
|
|
10673
|
+
**Examples:**
|
|
10674
|
+
- "Add Revenue" \u2192 line_item named "Revenue"
|
|
10675
|
+
- "Create a Marketing Expenses category" \u2192 category named "Marketing Expenses"
|
|
10676
|
+
- "Add quarters Q1 through Q4" \u2192 4 period entities`,
|
|
10677
|
+
parameters: {
|
|
10678
|
+
type: "object",
|
|
10679
|
+
properties: {
|
|
10680
|
+
modelId: {
|
|
10681
|
+
type: "string",
|
|
10682
|
+
description: "ID of the model to add the entity to"
|
|
10683
|
+
},
|
|
10684
|
+
name: {
|
|
10685
|
+
type: "string",
|
|
10686
|
+
description: "Internal name for the entity (used in formulas)"
|
|
10687
|
+
},
|
|
10688
|
+
type: {
|
|
10689
|
+
type: "string",
|
|
10690
|
+
enum: ["line_item", "account", "period", "category", "scenario", "custom"],
|
|
10691
|
+
description: "Type of entity"
|
|
10692
|
+
},
|
|
10693
|
+
displayName: {
|
|
10694
|
+
type: "string",
|
|
10695
|
+
description: "Human-readable display name (defaults to name)"
|
|
10696
|
+
},
|
|
10697
|
+
initialValues: {
|
|
10698
|
+
type: "array",
|
|
10699
|
+
items: {
|
|
10700
|
+
type: "object",
|
|
10701
|
+
properties: {
|
|
10702
|
+
key: { type: "string", description: 'Period or attribute key (e.g., "Q1_2024")' },
|
|
10703
|
+
value: { type: "string", description: "The value (number or string)" },
|
|
10704
|
+
dataType: {
|
|
10705
|
+
type: "string",
|
|
10706
|
+
enum: ["number", "currency", "percentage", "string"],
|
|
10707
|
+
description: "Data type for the value"
|
|
10708
|
+
}
|
|
10709
|
+
},
|
|
10710
|
+
required: ["key", "value"]
|
|
10711
|
+
},
|
|
10712
|
+
description: "Optional initial values for this entity"
|
|
10713
|
+
}
|
|
10714
|
+
},
|
|
10715
|
+
required: ["modelId", "name", "type"]
|
|
10716
|
+
}
|
|
10717
|
+
}
|
|
10718
|
+
})
|
|
10719
|
+
};
|
|
10720
|
+
var latticeSetValueTool = {
|
|
10721
|
+
name: "lattice_set_value",
|
|
10722
|
+
implementation: (context) => ({
|
|
10723
|
+
toolFn: async (params) => {
|
|
10724
|
+
const { modelId, entityName, attributeKey, value: rawValue } = params;
|
|
10725
|
+
console.log("[LATTICE DEBUG] \u{1F4DD} lattice_set_value called:", { modelId, entityName, attributeKey, rawValue });
|
|
10726
|
+
let value = rawValue;
|
|
10727
|
+
const numValue = parseFloat(rawValue);
|
|
10728
|
+
if (!isNaN(numValue)) {
|
|
10729
|
+
value = numValue;
|
|
10730
|
+
} else if (rawValue.toLowerCase() === "true") {
|
|
10731
|
+
value = true;
|
|
10732
|
+
} else if (rawValue.toLowerCase() === "false") {
|
|
10733
|
+
value = false;
|
|
10734
|
+
}
|
|
10735
|
+
const entityId = entityName.toLowerCase().replace(/\s+/g, "_");
|
|
10736
|
+
if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
|
|
10737
|
+
try {
|
|
10738
|
+
const model = await context.db.latticeModels.findById(modelId);
|
|
10739
|
+
if (model) {
|
|
10740
|
+
const entity = model.data.entities.find((e) => e.id === entityId || e.name === entityName);
|
|
10741
|
+
if (entity) {
|
|
10742
|
+
const attrIndex = entity.attributes.findIndex((a) => a.key === attributeKey);
|
|
10743
|
+
const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string";
|
|
10744
|
+
const attributeData = {
|
|
10745
|
+
key: attributeKey,
|
|
10746
|
+
value,
|
|
10747
|
+
dataType,
|
|
10748
|
+
isComputed: false
|
|
10749
|
+
};
|
|
10750
|
+
if (attrIndex >= 0) {
|
|
10751
|
+
entity.attributes[attrIndex] = attributeData;
|
|
10752
|
+
} else {
|
|
10753
|
+
entity.attributes.push(attributeData);
|
|
10754
|
+
}
|
|
10755
|
+
entity.updatedAt = /* @__PURE__ */ new Date();
|
|
10756
|
+
await context.db.latticeModels.update({
|
|
10757
|
+
id: modelId,
|
|
10758
|
+
data: model.data,
|
|
10759
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
10760
|
+
});
|
|
10761
|
+
context.logger.info(`[Lattice] Set ${entityId}.${attributeKey} = ${value} in model ${modelId}`);
|
|
10762
|
+
} else {
|
|
10763
|
+
context.logger.warn(`[Lattice] Entity ${entityName} not found in model ${modelId}`);
|
|
10764
|
+
}
|
|
10765
|
+
} else {
|
|
10766
|
+
context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
|
|
10767
|
+
}
|
|
10768
|
+
} catch (error) {
|
|
10769
|
+
context.logger.error(`[Lattice] Failed to persist value to database:`, error);
|
|
10770
|
+
}
|
|
10771
|
+
}
|
|
10772
|
+
return JSON.stringify({
|
|
10773
|
+
success: true,
|
|
10774
|
+
action: "SET_VALUE",
|
|
10775
|
+
modelId,
|
|
10776
|
+
data: {
|
|
10777
|
+
entityName,
|
|
10778
|
+
attributeKey,
|
|
10779
|
+
value
|
|
10780
|
+
},
|
|
10781
|
+
message: `Set ${entityName}.${attributeKey} = ${value}`
|
|
10782
|
+
});
|
|
10783
|
+
},
|
|
10784
|
+
toolSchema: {
|
|
10785
|
+
name: "lattice_set_value",
|
|
10786
|
+
description: `Set a specific value in a Lattice model.
|
|
10787
|
+
|
|
10788
|
+
**When to use:** When the user provides a specific number:
|
|
10789
|
+
- "Revenue in Q1 is 150,000"
|
|
10790
|
+
- "Set marketing spend to 50k for January"
|
|
10791
|
+
- "COGS is 40% of revenue" (use lattice_create_rule for formulas)
|
|
10792
|
+
|
|
10793
|
+
**Important:** This tool is for setting raw input values. For calculated values (formulas), use \`lattice_create_rule\` instead.
|
|
10794
|
+
|
|
10795
|
+
**Examples:**
|
|
10796
|
+
- "Q1 revenue is $100,000" \u2192 entityName="Revenue", attributeKey="Q1", value=100000
|
|
10797
|
+
- "Set headcount to 25" \u2192 entityName="Headcount", attributeKey="current", value=25`,
|
|
10798
|
+
parameters: {
|
|
10799
|
+
type: "object",
|
|
10800
|
+
properties: {
|
|
10801
|
+
modelId: {
|
|
10802
|
+
type: "string",
|
|
10803
|
+
description: "ID of the model"
|
|
10804
|
+
},
|
|
10805
|
+
entityName: {
|
|
10806
|
+
type: "string",
|
|
10807
|
+
description: "Name of the entity (line item, account, etc.)"
|
|
10808
|
+
},
|
|
10809
|
+
attributeKey: {
|
|
10810
|
+
type: "string",
|
|
10811
|
+
description: 'Period or attribute key (e.g., "Q1_2024", "current", "budget")'
|
|
10812
|
+
},
|
|
10813
|
+
value: {
|
|
10814
|
+
type: "string",
|
|
10815
|
+
description: "The value to set (will be parsed as number if numeric)"
|
|
10816
|
+
}
|
|
10817
|
+
},
|
|
10818
|
+
required: ["modelId", "entityName", "attributeKey", "value"]
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
})
|
|
10822
|
+
};
|
|
10823
|
+
var latticeCreateRuleTool = {
|
|
10824
|
+
name: "lattice_create_rule",
|
|
10825
|
+
implementation: (context) => ({
|
|
10826
|
+
toolFn: async (params) => {
|
|
10827
|
+
const { modelId, name, description, formula } = params;
|
|
10828
|
+
console.log("[LATTICE DEBUG] \u{1F4D0} lattice_create_rule called:", { modelId, name, formula });
|
|
10829
|
+
const parsedRule = parseFormula(formula);
|
|
10830
|
+
const ruleId = `rule_${Date.now()}`;
|
|
10831
|
+
const ruleData = {
|
|
10832
|
+
id: ruleId,
|
|
10833
|
+
name,
|
|
10834
|
+
description: description || formula,
|
|
10835
|
+
type: "formula",
|
|
10836
|
+
definition: {
|
|
10837
|
+
operation: parsedRule.operation,
|
|
10838
|
+
inputs: parsedRule.inputs.map((ref) => ({
|
|
10839
|
+
type: "entity",
|
|
10840
|
+
ref
|
|
10841
|
+
})),
|
|
10842
|
+
output: {
|
|
10843
|
+
targetEntityId: parsedRule.outputEntity.toLowerCase().replace(/\s+/g, "_"),
|
|
10844
|
+
targetAttribute: "computed",
|
|
10845
|
+
dataType: "number"
|
|
10846
|
+
}
|
|
10847
|
+
},
|
|
10848
|
+
dependencies: parsedRule.inputs.map((i) => i.toLowerCase().replace(/\s+/g, "_")),
|
|
10849
|
+
priority: 0,
|
|
10850
|
+
enabled: true,
|
|
10851
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
10852
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
10853
|
+
};
|
|
10854
|
+
const outputEntityId = parsedRule.outputEntity.toLowerCase().replace(/\s+/g, "_");
|
|
10855
|
+
let entityCreatedMessage = "";
|
|
10856
|
+
if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
|
|
10857
|
+
try {
|
|
10858
|
+
const model = await context.db.latticeModels.findById(modelId);
|
|
10859
|
+
if (model) {
|
|
10860
|
+
const outputEntityExists = model.data.entities.some((e) => e.id === outputEntityId || e.name.toLowerCase() === parsedRule.outputEntity.toLowerCase());
|
|
10861
|
+
if (!outputEntityExists && parsedRule.outputEntity !== "unknown") {
|
|
10862
|
+
const now = /* @__PURE__ */ new Date();
|
|
10863
|
+
const newEntity = {
|
|
10864
|
+
id: outputEntityId,
|
|
10865
|
+
type: "line_item",
|
|
10866
|
+
name: parsedRule.outputEntity,
|
|
10867
|
+
displayName: parsedRule.outputEntity,
|
|
10868
|
+
attributes: [
|
|
10869
|
+
{
|
|
10870
|
+
key: "value",
|
|
10871
|
+
value: 0,
|
|
10872
|
+
dataType: "currency",
|
|
10873
|
+
isComputed: true
|
|
10874
|
+
},
|
|
10875
|
+
{
|
|
10876
|
+
key: "category",
|
|
10877
|
+
value: "Computed",
|
|
10878
|
+
dataType: "string",
|
|
10879
|
+
isComputed: false
|
|
10880
|
+
}
|
|
10881
|
+
],
|
|
10882
|
+
metadata: { isComputed: true },
|
|
10883
|
+
createdAt: now,
|
|
10884
|
+
updatedAt: now
|
|
10885
|
+
};
|
|
10886
|
+
model.data.entities.push(newEntity);
|
|
10887
|
+
entityCreatedMessage = ` Created output entity "${parsedRule.outputEntity}".`;
|
|
10888
|
+
context.logger.info(`[Lattice] Auto-created output entity ${outputEntityId} for rule ${ruleId}`);
|
|
10889
|
+
}
|
|
10890
|
+
const existingIndex = model.rules.rules.findIndex((r) => r.id === ruleId || r.name === name);
|
|
10891
|
+
if (existingIndex >= 0) {
|
|
10892
|
+
model.rules.rules[existingIndex] = ruleData;
|
|
10893
|
+
} else {
|
|
10894
|
+
model.rules.rules.push(ruleData);
|
|
10895
|
+
}
|
|
10896
|
+
await context.db.latticeModels.update({
|
|
10897
|
+
id: modelId,
|
|
10898
|
+
data: model.data,
|
|
10899
|
+
rules: model.rules,
|
|
10900
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
10901
|
+
});
|
|
10902
|
+
context.logger.info(`[Lattice] Created rule ${ruleId} in model ${modelId}`);
|
|
10903
|
+
} else {
|
|
10904
|
+
context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
|
|
10905
|
+
}
|
|
10906
|
+
} catch (error) {
|
|
10907
|
+
context.logger.error(`[Lattice] Failed to persist rule to database:`, error);
|
|
10908
|
+
}
|
|
10909
|
+
}
|
|
10910
|
+
return JSON.stringify({
|
|
10911
|
+
success: true,
|
|
10912
|
+
action: "CREATE_RULE",
|
|
10913
|
+
modelId,
|
|
10914
|
+
ruleId,
|
|
10915
|
+
data: {
|
|
10916
|
+
name,
|
|
10917
|
+
description,
|
|
10918
|
+
formula,
|
|
10919
|
+
parsed: parsedRule,
|
|
10920
|
+
outputEntityCreated: entityCreatedMessage !== ""
|
|
10921
|
+
},
|
|
10922
|
+
message: `Created rule "${name}": ${formula}.${entityCreatedMessage}`
|
|
10923
|
+
});
|
|
10924
|
+
},
|
|
10925
|
+
toolSchema: {
|
|
10926
|
+
name: "lattice_create_rule",
|
|
10927
|
+
description: `Create a formula or calculation rule in a Lattice model.
|
|
10928
|
+
|
|
10929
|
+
**When to use:** When the user defines a calculation:
|
|
10930
|
+
- "Gross profit equals revenue minus COGS"
|
|
10931
|
+
- "Net margin is net income divided by revenue times 100"
|
|
10932
|
+
- "Total expenses is the sum of all expense line items"
|
|
10933
|
+
|
|
10934
|
+
**IMPORTANT: For per-period calculations like "calculate X for each quarter", you MUST call this tool ONCE for EACH period.** For example, if asked to "calculate gross margin for each quarter", call this tool 4 times:
|
|
10935
|
+
1. lattice_create_rule with formula "Q1 Gross Margin = Q1 Revenue - Q1 COGS"
|
|
10936
|
+
2. lattice_create_rule with formula "Q2 Gross Margin = Q2 Revenue - Q2 COGS"
|
|
10937
|
+
3. lattice_create_rule with formula "Q3 Gross Margin = Q3 Revenue - Q3 COGS"
|
|
10938
|
+
4. lattice_create_rule with formula "Q4 Gross Margin = Q4 Revenue - Q4 COGS"
|
|
10939
|
+
|
|
10940
|
+
**Formula syntax (natural language):**
|
|
10941
|
+
- Arithmetic: "X equals Y plus Z", "A minus B", "C times D"
|
|
10942
|
+
- Aggregation: "sum of", "average of", "total"
|
|
10943
|
+
- Percentage: "X percent of Y", "as a percentage"
|
|
10944
|
+
- Comparison: "if X is greater than Y then A else B"
|
|
10945
|
+
|
|
10946
|
+
**Examples:**
|
|
10947
|
+
- "Gross Profit = Revenue - COGS"
|
|
10948
|
+
- "Gross Margin = Gross Profit / Revenue * 100"
|
|
10949
|
+
- "Total OpEx = sum of all Operating Expenses"
|
|
10950
|
+
- "YoY Growth = (Current Year - Prior Year) / Prior Year * 100"
|
|
10951
|
+
- "Q1 Gross Margin = Q1 Revenue - Q1 COGS" (per-period)`,
|
|
10952
|
+
parameters: {
|
|
10953
|
+
type: "object",
|
|
10954
|
+
properties: {
|
|
10955
|
+
modelId: {
|
|
10956
|
+
type: "string",
|
|
10957
|
+
description: "ID of the model"
|
|
10958
|
+
},
|
|
10959
|
+
name: {
|
|
10960
|
+
type: "string",
|
|
10961
|
+
description: 'Name for this rule (e.g., "Gross Profit Calculation")'
|
|
10962
|
+
},
|
|
10963
|
+
description: {
|
|
10964
|
+
type: "string",
|
|
10965
|
+
description: "Optional description of what this rule calculates"
|
|
10966
|
+
},
|
|
10967
|
+
formula: {
|
|
10968
|
+
type: "string",
|
|
10969
|
+
description: "The formula in natural language or equation format"
|
|
10970
|
+
}
|
|
10971
|
+
},
|
|
10972
|
+
required: ["modelId", "name", "formula"]
|
|
10973
|
+
}
|
|
10974
|
+
}
|
|
10975
|
+
})
|
|
10976
|
+
};
|
|
10977
|
+
var latticeQueryTool = {
|
|
10978
|
+
name: "lattice_query",
|
|
10979
|
+
implementation: () => ({
|
|
10980
|
+
toolFn: async (params) => {
|
|
10981
|
+
const { modelId, query } = params;
|
|
10982
|
+
const parsedQuery = parseQuery(query);
|
|
10983
|
+
return JSON.stringify({
|
|
10984
|
+
success: true,
|
|
10985
|
+
action: "QUERY",
|
|
10986
|
+
modelId,
|
|
10987
|
+
data: {
|
|
10988
|
+
query,
|
|
10989
|
+
parsed: parsedQuery
|
|
10990
|
+
},
|
|
10991
|
+
message: `Querying model for: ${query}`
|
|
10992
|
+
});
|
|
10993
|
+
},
|
|
10994
|
+
toolSchema: {
|
|
10995
|
+
name: "lattice_query",
|
|
10996
|
+
description: `Query a Lattice model for specific values or aggregations.
|
|
10997
|
+
|
|
10998
|
+
**When to use:** When the user asks about values in the model:
|
|
10999
|
+
- "What's Q1 gross margin?"
|
|
11000
|
+
- "Show me total revenue for 2024"
|
|
11001
|
+
- "What are all the expense categories?"
|
|
11002
|
+
- "Compare Q1 vs Q2 performance"
|
|
11003
|
+
|
|
11004
|
+
**Query types:**
|
|
11005
|
+
- Single value: "What is [entity] for [period]?"
|
|
11006
|
+
- Aggregation: "Total [entity] for [range]"
|
|
11007
|
+
- Comparison: "Compare [entity] across [periods]"
|
|
11008
|
+
- List: "Show all [entity type]"
|
|
11009
|
+
|
|
11010
|
+
**Examples:**
|
|
11011
|
+
- "What's Q2 revenue?" \u2192 Returns specific value
|
|
11012
|
+
- "Total revenue for 2024" \u2192 Sums Q1-Q4
|
|
11013
|
+
- "Compare gross margin Q1 vs Q2" \u2192 Side-by-side comparison`,
|
|
11014
|
+
parameters: {
|
|
11015
|
+
type: "object",
|
|
11016
|
+
properties: {
|
|
11017
|
+
modelId: {
|
|
11018
|
+
type: "string",
|
|
11019
|
+
description: "ID of the model to query"
|
|
11020
|
+
},
|
|
11021
|
+
query: {
|
|
11022
|
+
type: "string",
|
|
11023
|
+
description: "Natural language query about the model"
|
|
11024
|
+
}
|
|
11025
|
+
},
|
|
11026
|
+
required: ["modelId", "query"]
|
|
11027
|
+
}
|
|
11028
|
+
}
|
|
11029
|
+
})
|
|
11030
|
+
};
|
|
11031
|
+
var latticeExplainTool = {
|
|
11032
|
+
name: "lattice_explain",
|
|
11033
|
+
implementation: () => ({
|
|
11034
|
+
toolFn: async (params) => {
|
|
11035
|
+
const { modelId, entityName, attributeKey } = params;
|
|
11036
|
+
return JSON.stringify({
|
|
11037
|
+
success: true,
|
|
11038
|
+
action: "EXPLAIN",
|
|
11039
|
+
modelId,
|
|
11040
|
+
data: {
|
|
11041
|
+
entityName,
|
|
11042
|
+
attributeKey
|
|
11043
|
+
},
|
|
11044
|
+
message: `Explaining calculation for ${entityName}.${attributeKey}`
|
|
11045
|
+
});
|
|
11046
|
+
},
|
|
11047
|
+
toolSchema: {
|
|
11048
|
+
name: "lattice_explain",
|
|
11049
|
+
description: `Explain how a value in a Lattice model was calculated.
|
|
11050
|
+
|
|
11051
|
+
**When to use:** When the user wants to understand a calculation:
|
|
11052
|
+
- "How did you calculate gross profit?"
|
|
11053
|
+
- "Explain the Q2 margin number"
|
|
11054
|
+
- "Show me the formula for total expenses"
|
|
11055
|
+
- "Why is this number so high?"
|
|
11056
|
+
|
|
11057
|
+
This tool traces the calculation chain from the target value back through all its inputs and rules, providing a step-by-step explanation.`,
|
|
11058
|
+
parameters: {
|
|
11059
|
+
type: "object",
|
|
11060
|
+
properties: {
|
|
11061
|
+
modelId: {
|
|
11062
|
+
type: "string",
|
|
11063
|
+
description: "ID of the model"
|
|
11064
|
+
},
|
|
11065
|
+
entityName: {
|
|
11066
|
+
type: "string",
|
|
11067
|
+
description: "Name of the entity containing the value to explain"
|
|
11068
|
+
},
|
|
11069
|
+
attributeKey: {
|
|
11070
|
+
type: "string",
|
|
11071
|
+
description: "Period or attribute key of the value to explain"
|
|
11072
|
+
}
|
|
11073
|
+
},
|
|
11074
|
+
required: ["modelId", "entityName", "attributeKey"]
|
|
11075
|
+
}
|
|
11076
|
+
}
|
|
11077
|
+
})
|
|
11078
|
+
};
|
|
11079
|
+
function parseFormula(formula) {
|
|
11080
|
+
const normalized = formula.toLowerCase().trim();
|
|
11081
|
+
const equalsMatch = normalized.match(/^(.+?)\s*(?:=|equals?)\s*(.+)$/i);
|
|
11082
|
+
if (equalsMatch) {
|
|
11083
|
+
const [, output, expression] = equalsMatch;
|
|
11084
|
+
if (expression.includes("+") || expression.includes("plus")) {
|
|
11085
|
+
return {
|
|
11086
|
+
operation: "ADD",
|
|
11087
|
+
outputEntity: output,
|
|
11088
|
+
inputs: expression.split(/[+]|plus/i).map((s) => s.trim())
|
|
11089
|
+
};
|
|
11090
|
+
}
|
|
11091
|
+
if (expression.includes("-") || expression.includes("minus")) {
|
|
11092
|
+
return {
|
|
11093
|
+
operation: "SUBTRACT",
|
|
11094
|
+
outputEntity: output,
|
|
11095
|
+
inputs: expression.split(/[-]|minus/i).map((s) => s.trim())
|
|
11096
|
+
};
|
|
11097
|
+
}
|
|
11098
|
+
if (expression.includes("*") || expression.includes("times") || expression.includes("x")) {
|
|
11099
|
+
return {
|
|
11100
|
+
operation: "MULTIPLY",
|
|
11101
|
+
outputEntity: output,
|
|
11102
|
+
inputs: expression.split(/[*x]|times/i).map((s) => s.trim())
|
|
11103
|
+
};
|
|
11104
|
+
}
|
|
11105
|
+
if (expression.includes("/") || expression.includes("divided")) {
|
|
11106
|
+
return {
|
|
11107
|
+
operation: "DIVIDE",
|
|
11108
|
+
outputEntity: output,
|
|
11109
|
+
inputs: expression.split(/[/]|divided by/i).map((s) => s.trim())
|
|
11110
|
+
};
|
|
11111
|
+
}
|
|
11112
|
+
if (expression.includes("sum")) {
|
|
11113
|
+
return {
|
|
11114
|
+
operation: "SUM",
|
|
11115
|
+
outputEntity: output,
|
|
11116
|
+
inputs: [expression.replace(/sum of/i, "").trim()]
|
|
11117
|
+
};
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
return {
|
|
11121
|
+
operation: "REFERENCE",
|
|
11122
|
+
outputEntity: "unknown",
|
|
11123
|
+
inputs: [formula]
|
|
11124
|
+
};
|
|
11125
|
+
}
|
|
11126
|
+
function parseQuery(query) {
|
|
11127
|
+
const normalized = query.toLowerCase();
|
|
11128
|
+
let type = "single_value";
|
|
11129
|
+
if (normalized.includes("total") || normalized.includes("sum")) {
|
|
11130
|
+
type = "aggregation";
|
|
11131
|
+
} else if (normalized.includes("compare") || normalized.includes("vs")) {
|
|
11132
|
+
type = "comparison";
|
|
11133
|
+
} else if (normalized.includes("list") || normalized.includes("show all")) {
|
|
11134
|
+
type = "list";
|
|
11135
|
+
}
|
|
11136
|
+
const entities = [];
|
|
11137
|
+
const periods = [];
|
|
11138
|
+
const periodPatterns = [/q[1-4]/gi, /\d{4}/g, /jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/gi];
|
|
11139
|
+
for (const pattern of periodPatterns) {
|
|
11140
|
+
const matches = query.match(pattern);
|
|
11141
|
+
if (matches) {
|
|
11142
|
+
periods.push(...matches);
|
|
11143
|
+
}
|
|
11144
|
+
}
|
|
11145
|
+
const commonEntities = ["revenue", "expenses", "profit", "margin", "income", "cost", "cogs", "ebitda"];
|
|
11146
|
+
for (const entity of commonEntities) {
|
|
11147
|
+
if (normalized.includes(entity)) {
|
|
11148
|
+
entities.push(entity);
|
|
11149
|
+
}
|
|
11150
|
+
}
|
|
11151
|
+
return { type, entities, periods };
|
|
11152
|
+
}
|
|
11153
|
+
|
|
11154
|
+
// ../../b4m-core/packages/services/dist/src/llm/tools/implementation/editLocalFile/index.js
|
|
11155
|
+
import { promises as fs10 } from "fs";
|
|
11156
|
+
import { existsSync as existsSync7 } from "fs";
|
|
11157
|
+
import path13 from "path";
|
|
11158
|
+
import { diffLines as diffLines3 } from "diff";
|
|
11159
|
+
function generateDiff(original, modified) {
|
|
11160
|
+
const differences = diffLines3(original, modified);
|
|
11161
|
+
let diffString = "";
|
|
11162
|
+
let additions = 0;
|
|
11163
|
+
let deletions = 0;
|
|
11164
|
+
differences.forEach((part) => {
|
|
11165
|
+
if (part.added) {
|
|
11166
|
+
additions += part.count || 0;
|
|
11167
|
+
diffString += part.value.split("\n").filter((line) => line).map((line) => `+ ${line}`).join("\n");
|
|
11168
|
+
if (diffString && !diffString.endsWith("\n"))
|
|
11169
|
+
diffString += "\n";
|
|
11170
|
+
} else if (part.removed) {
|
|
11171
|
+
deletions += part.count || 0;
|
|
11172
|
+
diffString += part.value.split("\n").filter((line) => line).map((line) => `- ${line}`).join("\n");
|
|
11173
|
+
if (diffString && !diffString.endsWith("\n"))
|
|
11174
|
+
diffString += "\n";
|
|
11175
|
+
}
|
|
11176
|
+
});
|
|
11177
|
+
return { additions, deletions, diff: diffString.trim() };
|
|
11178
|
+
}
|
|
11179
|
+
async function editLocalFile(params) {
|
|
11180
|
+
const { path: filePath, old_string, new_string } = params;
|
|
11181
|
+
const normalizedPath = path13.normalize(filePath);
|
|
11182
|
+
const resolvedPath = path13.resolve(process.cwd(), normalizedPath);
|
|
11183
|
+
const cwd = path13.resolve(process.cwd());
|
|
11184
|
+
if (!resolvedPath.startsWith(cwd)) {
|
|
11185
|
+
throw new Error(`Access denied: Cannot edit files outside of current working directory`);
|
|
11186
|
+
}
|
|
11187
|
+
if (!existsSync7(resolvedPath)) {
|
|
11188
|
+
throw new Error(`File not found: ${filePath}`);
|
|
11189
|
+
}
|
|
11190
|
+
const currentContent = await fs10.readFile(resolvedPath, "utf-8");
|
|
11191
|
+
if (!currentContent.includes(old_string)) {
|
|
11192
|
+
const preview = old_string.length > 100 ? old_string.substring(0, 100) + "..." : old_string;
|
|
11193
|
+
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}"`);
|
|
11194
|
+
}
|
|
11195
|
+
const occurrences = currentContent.split(old_string).length - 1;
|
|
11196
|
+
if (occurrences > 1) {
|
|
11197
|
+
throw new Error(`Found ${occurrences} occurrences of the string to replace. Please provide a more specific old_string that matches exactly one location.`);
|
|
11198
|
+
}
|
|
11199
|
+
const newContent = currentContent.replace(old_string, new_string);
|
|
11200
|
+
await fs10.writeFile(resolvedPath, newContent, "utf-8");
|
|
11201
|
+
const diffResult = generateDiff(old_string, new_string);
|
|
11202
|
+
return `File edited successfully: ${filePath}
|
|
11203
|
+
Changes: +${diffResult.additions} lines, -${diffResult.deletions} lines
|
|
11204
|
+
|
|
11205
|
+
Diff:
|
|
11206
|
+
${diffResult.diff}`;
|
|
11207
|
+
}
|
|
11208
|
+
var editLocalFileTool = {
|
|
11209
|
+
name: "edit_local_file",
|
|
11210
|
+
implementation: (context) => ({
|
|
11211
|
+
toolFn: async (value) => {
|
|
11212
|
+
const params = value;
|
|
11213
|
+
context.logger.info(`\u{1F4DD} EditLocalFile: Editing file`, {
|
|
11214
|
+
path: params.path,
|
|
11215
|
+
oldStringLength: params.old_string.length,
|
|
11216
|
+
newStringLength: params.new_string.length
|
|
10183
11217
|
});
|
|
10184
11218
|
try {
|
|
10185
11219
|
const result = await editLocalFile(params);
|
|
@@ -10588,7 +11622,14 @@ var cliOnlyTools = {
|
|
|
10588
11622
|
// Shell execution
|
|
10589
11623
|
bash_execute: bashExecuteTool,
|
|
10590
11624
|
// Git operations
|
|
10591
|
-
recent_changes: recentChangesTool
|
|
11625
|
+
recent_changes: recentChangesTool,
|
|
11626
|
+
// Lattice financial modeling tools
|
|
11627
|
+
lattice_create_model: latticeCreateModelTool,
|
|
11628
|
+
lattice_add_entity: latticeAddEntityTool,
|
|
11629
|
+
lattice_set_value: latticeSetValueTool,
|
|
11630
|
+
lattice_create_rule: latticeCreateRuleTool,
|
|
11631
|
+
lattice_query: latticeQueryTool,
|
|
11632
|
+
lattice_explain: latticeExplainTool
|
|
10592
11633
|
};
|
|
10593
11634
|
var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools = b4mTools) => {
|
|
10594
11635
|
const context = {
|
|
@@ -10714,6 +11755,7 @@ var QuestStartBodySchema = z139.object({
|
|
|
10714
11755
|
enableMementos: z139.boolean().optional(),
|
|
10715
11756
|
enableArtifacts: z139.boolean().optional(),
|
|
10716
11757
|
enableAgents: z139.boolean().optional(),
|
|
11758
|
+
enableLattice: z139.boolean().optional(),
|
|
10717
11759
|
promptMeta: PromptMetaZodSchema,
|
|
10718
11760
|
tools: z139.array(z139.union([b4mLLMTools, z139.string()])).optional(),
|
|
10719
11761
|
mcpServers: z139.array(z139.string()).optional(),
|
|
@@ -11341,10 +12383,10 @@ var ToolErrorType;
|
|
|
11341
12383
|
// src/utils/diffPreview.ts
|
|
11342
12384
|
import * as Diff from "diff";
|
|
11343
12385
|
import { readFile } from "fs/promises";
|
|
11344
|
-
import { existsSync as
|
|
12386
|
+
import { existsSync as existsSync8 } from "fs";
|
|
11345
12387
|
async function generateFileDiffPreview(args) {
|
|
11346
12388
|
try {
|
|
11347
|
-
if (!
|
|
12389
|
+
if (!existsSync8(args.path)) {
|
|
11348
12390
|
const lines2 = args.content.split("\n");
|
|
11349
12391
|
const preview = lines2.slice(0, 20).join("\n");
|
|
11350
12392
|
const hasMore = lines2.length > 20;
|
|
@@ -11382,7 +12424,7 @@ ${diffLines4.join("\n")}`;
|
|
|
11382
12424
|
}
|
|
11383
12425
|
async function generateFileDeletePreview(args) {
|
|
11384
12426
|
try {
|
|
11385
|
-
if (!
|
|
12427
|
+
if (!existsSync8(args.path)) {
|
|
11386
12428
|
return `[File does not exist: ${args.path}]`;
|
|
11387
12429
|
}
|
|
11388
12430
|
const stats = await import("fs/promises").then((fs14) => fs14.stat(args.path));
|
|
@@ -11676,21 +12718,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
11676
12718
|
async upload(input, destination, options) {
|
|
11677
12719
|
return `/tmp/${destination}`;
|
|
11678
12720
|
}
|
|
11679
|
-
async download(
|
|
12721
|
+
async download(path19) {
|
|
11680
12722
|
throw new Error("Download not supported in CLI");
|
|
11681
12723
|
}
|
|
11682
|
-
async delete(
|
|
12724
|
+
async delete(path19) {
|
|
11683
12725
|
}
|
|
11684
|
-
async getSignedUrl(
|
|
11685
|
-
return `/tmp/${
|
|
12726
|
+
async getSignedUrl(path19) {
|
|
12727
|
+
return `/tmp/${path19}`;
|
|
11686
12728
|
}
|
|
11687
|
-
getPublicUrl(
|
|
11688
|
-
return `/tmp/${
|
|
12729
|
+
getPublicUrl(path19) {
|
|
12730
|
+
return `/tmp/${path19}`;
|
|
11689
12731
|
}
|
|
11690
|
-
async getPreview(
|
|
11691
|
-
return `/tmp/${
|
|
12732
|
+
async getPreview(path19) {
|
|
12733
|
+
return `/tmp/${path19}`;
|
|
11692
12734
|
}
|
|
11693
|
-
async getMetadata(
|
|
12735
|
+
async getMetadata(path19) {
|
|
11694
12736
|
return { size: 0, contentType: "application/octet-stream" };
|
|
11695
12737
|
}
|
|
11696
12738
|
};
|
|
@@ -11764,6 +12806,9 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
11764
12806
|
if (response.action === "deny") {
|
|
11765
12807
|
throw new PermissionDeniedError(toolName, args);
|
|
11766
12808
|
}
|
|
12809
|
+
if (response.action === "allow-session") {
|
|
12810
|
+
permissionManager.trustToolForSession(toolName);
|
|
12811
|
+
}
|
|
11767
12812
|
if (response.action === "allow-always") {
|
|
11768
12813
|
const canTrust = permissionManager.trustTool(toolName);
|
|
11769
12814
|
if (canTrust) {
|
|
@@ -11968,6 +13013,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
11968
13013
|
var PermissionManager = class {
|
|
11969
13014
|
constructor(trustedTools = [], customCategories, deniedTools) {
|
|
11970
13015
|
this.trustedTools = /* @__PURE__ */ new Set();
|
|
13016
|
+
this.sessionTrustedTools = /* @__PURE__ */ new Set();
|
|
11971
13017
|
this.deniedTools = /* @__PURE__ */ new Set();
|
|
11972
13018
|
this.trustedTools = new Set(trustedTools);
|
|
11973
13019
|
this.customCategories = new Map(Object.entries(customCategories || {}));
|
|
@@ -11993,6 +13039,9 @@ var PermissionManager = class {
|
|
|
11993
13039
|
if (category === "auto_approve") {
|
|
11994
13040
|
return false;
|
|
11995
13041
|
}
|
|
13042
|
+
if (this.sessionTrustedTools.has(toolName)) {
|
|
13043
|
+
return false;
|
|
13044
|
+
}
|
|
11996
13045
|
if (category === "prompt_always") {
|
|
11997
13046
|
return true;
|
|
11998
13047
|
}
|
|
@@ -12060,6 +13109,29 @@ var PermissionManager = class {
|
|
|
12060
13109
|
getDeniedTools() {
|
|
12061
13110
|
return Array.from(this.deniedTools).sort();
|
|
12062
13111
|
}
|
|
13112
|
+
/**
|
|
13113
|
+
* Trust a tool for the current session only (in-memory, no persistence)
|
|
13114
|
+
* Works for all tool categories including prompt_always, but NOT project-denied tools
|
|
13115
|
+
*/
|
|
13116
|
+
trustToolForSession(toolName) {
|
|
13117
|
+
if (this.deniedTools.has(toolName)) {
|
|
13118
|
+
return false;
|
|
13119
|
+
}
|
|
13120
|
+
this.sessionTrustedTools.add(toolName);
|
|
13121
|
+
return true;
|
|
13122
|
+
}
|
|
13123
|
+
/**
|
|
13124
|
+
* Check if a tool is trusted for the current session
|
|
13125
|
+
*/
|
|
13126
|
+
isSessionTrusted(toolName) {
|
|
13127
|
+
return this.sessionTrustedTools.has(toolName);
|
|
13128
|
+
}
|
|
13129
|
+
/**
|
|
13130
|
+
* Clear all session-scoped trust (called on exit or session reset)
|
|
13131
|
+
*/
|
|
13132
|
+
clearSessionTrust() {
|
|
13133
|
+
this.sessionTrustedTools.clear();
|
|
13134
|
+
}
|
|
12063
13135
|
/**
|
|
12064
13136
|
* Clear all trusted tools
|
|
12065
13137
|
*/
|
|
@@ -12097,7 +13169,7 @@ function getEnvironmentName(configApiConfig) {
|
|
|
12097
13169
|
|
|
12098
13170
|
// src/utils/contextLoader.ts
|
|
12099
13171
|
import * as fs11 from "fs";
|
|
12100
|
-
import * as
|
|
13172
|
+
import * as path14 from "path";
|
|
12101
13173
|
import { homedir as homedir3 } from "os";
|
|
12102
13174
|
var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
|
|
12103
13175
|
var PROJECT_CONTEXT_FILES = [
|
|
@@ -12118,7 +13190,7 @@ function formatFileSize2(bytes) {
|
|
|
12118
13190
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
12119
13191
|
}
|
|
12120
13192
|
function tryReadContextFile(dir, filename, source) {
|
|
12121
|
-
const filePath =
|
|
13193
|
+
const filePath = path14.join(dir, filename);
|
|
12122
13194
|
try {
|
|
12123
13195
|
const stats = fs11.lstatSync(filePath);
|
|
12124
13196
|
if (stats.isDirectory()) {
|
|
@@ -12186,7 +13258,7 @@ ${project.content}`;
|
|
|
12186
13258
|
}
|
|
12187
13259
|
async function loadContextFiles(projectDir) {
|
|
12188
13260
|
const errors = [];
|
|
12189
|
-
const globalDir =
|
|
13261
|
+
const globalDir = path14.join(homedir3(), ".bike4mind");
|
|
12190
13262
|
const projectDirectory = projectDir || process.cwd();
|
|
12191
13263
|
const [globalResult, projectResult] = await Promise.all([
|
|
12192
13264
|
Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
|
|
@@ -12457,8 +13529,8 @@ function substituteArguments(template, args) {
|
|
|
12457
13529
|
// ../../b4m-core/packages/mcp/dist/src/client.js
|
|
12458
13530
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
12459
13531
|
import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
12460
|
-
import
|
|
12461
|
-
import { existsSync as
|
|
13532
|
+
import path15 from "path";
|
|
13533
|
+
import { existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
|
|
12462
13534
|
var MCPClient = class {
|
|
12463
13535
|
// Note: This class handles MCP server communication with repository filtering
|
|
12464
13536
|
mcp;
|
|
@@ -12499,18 +13571,18 @@ var MCPClient = class {
|
|
|
12499
13571
|
const root = process.env.INIT_CWD || process.cwd();
|
|
12500
13572
|
const candidatePaths = [
|
|
12501
13573
|
// When running from SST Lambda with node_modules structure (copyFiles)
|
|
12502
|
-
|
|
13574
|
+
path15.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
|
|
12503
13575
|
// When running from SST Lambda deployed environment (/var/task)
|
|
12504
|
-
|
|
13576
|
+
path15.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
12505
13577
|
// When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
|
|
12506
|
-
|
|
13578
|
+
path15.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
12507
13579
|
// When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
|
|
12508
|
-
|
|
13580
|
+
path15.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
12509
13581
|
// Original paths (backward compatibility)
|
|
12510
|
-
|
|
12511
|
-
|
|
13582
|
+
path15.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
13583
|
+
path15.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
|
|
12512
13584
|
];
|
|
12513
|
-
const serverScriptPath = candidatePaths.find((p) =>
|
|
13585
|
+
const serverScriptPath = candidatePaths.find((p) => existsSync9(p));
|
|
12514
13586
|
if (!serverScriptPath) {
|
|
12515
13587
|
const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
12516
13588
|
console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
|
|
@@ -13070,6 +14142,14 @@ ${totalText}`);
|
|
|
13070
14142
|
function stripThinkingBlocks(text) {
|
|
13071
14143
|
return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
13072
14144
|
}
|
|
14145
|
+
function extractUsageInfo(parsed) {
|
|
14146
|
+
return {
|
|
14147
|
+
inputTokens: parsed.usage?.inputTokens,
|
|
14148
|
+
outputTokens: parsed.usage?.outputTokens,
|
|
14149
|
+
creditsUsed: parsed.credits?.used,
|
|
14150
|
+
usdCost: parsed.credits?.usdCost
|
|
14151
|
+
};
|
|
14152
|
+
}
|
|
13073
14153
|
var ServerLlmBackend = class {
|
|
13074
14154
|
constructor(options) {
|
|
13075
14155
|
this.completionsEndpoint = "/api/ai/v1/completions";
|
|
@@ -13221,11 +14301,8 @@ var ServerLlmBackend = class {
|
|
|
13221
14301
|
if (parsed.type === "content") {
|
|
13222
14302
|
const textChunk = parsed.text || "";
|
|
13223
14303
|
accumulatedText += textChunk;
|
|
13224
|
-
if (parsed.usage) {
|
|
13225
|
-
lastUsageInfo =
|
|
13226
|
-
inputTokens: parsed.usage.inputTokens,
|
|
13227
|
-
outputTokens: parsed.usage.outputTokens
|
|
13228
|
-
};
|
|
14304
|
+
if (parsed.usage || parsed.credits) {
|
|
14305
|
+
lastUsageInfo = extractUsageInfo(parsed);
|
|
13229
14306
|
}
|
|
13230
14307
|
streamLogger.onContent(eventCount, textChunk, accumulatedText);
|
|
13231
14308
|
} else if (parsed.type === "tool_use") {
|
|
@@ -13252,11 +14329,8 @@ var ServerLlmBackend = class {
|
|
|
13252
14329
|
thinkingBlocks = parsed.thinking;
|
|
13253
14330
|
streamLogger.onCriticalEvent(eventCount, "THINKING", `${thinkingBlocks.length} thinking blocks`);
|
|
13254
14331
|
}
|
|
13255
|
-
if (parsed.usage) {
|
|
13256
|
-
lastUsageInfo =
|
|
13257
|
-
inputTokens: parsed.usage.inputTokens,
|
|
13258
|
-
outputTokens: parsed.usage.outputTokens
|
|
13259
|
-
};
|
|
14332
|
+
if (parsed.usage || parsed.credits) {
|
|
14333
|
+
lastUsageInfo = extractUsageInfo(parsed);
|
|
13260
14334
|
}
|
|
13261
14335
|
}
|
|
13262
14336
|
} catch (parseError) {
|
|
@@ -13561,7 +14635,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
13561
14635
|
// package.json
|
|
13562
14636
|
var package_default = {
|
|
13563
14637
|
name: "@bike4mind/cli",
|
|
13564
|
-
version: "0.2.29
|
|
14638
|
+
version: "0.2.29",
|
|
13565
14639
|
type: "module",
|
|
13566
14640
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
13567
14641
|
license: "UNLICENSED",
|
|
@@ -13632,10 +14706,13 @@ var package_default = {
|
|
|
13632
14706
|
diff: "^8.0.2",
|
|
13633
14707
|
dotenv: "^16.3.1",
|
|
13634
14708
|
"eventsource-parser": "^3.0.6",
|
|
14709
|
+
fdir: "^6.5.0",
|
|
13635
14710
|
"file-type": "^18.7.0",
|
|
13636
14711
|
"fuse.js": "^7.1.0",
|
|
14712
|
+
fzf: "^0.5.2",
|
|
13637
14713
|
glob: "^13.0.0",
|
|
13638
14714
|
"gray-matter": "^4.0.3",
|
|
14715
|
+
ignore: "^7.0.5",
|
|
13639
14716
|
ink: "^6.5.1",
|
|
13640
14717
|
"ink-select-input": "^6.2.0",
|
|
13641
14718
|
"ink-spinner": "^5.0.0",
|
|
@@ -13651,6 +14728,7 @@ var package_default = {
|
|
|
13651
14728
|
open: "^11.0.0",
|
|
13652
14729
|
openai: "^6.18.0",
|
|
13653
14730
|
"p-limit": "^6.2.0",
|
|
14731
|
+
picomatch: "^4.0.3",
|
|
13654
14732
|
qrcode: "^1.5.4",
|
|
13655
14733
|
react: "^19.2.3",
|
|
13656
14734
|
sharp: "^0.34.5",
|
|
@@ -13671,14 +14749,15 @@ var package_default = {
|
|
|
13671
14749
|
},
|
|
13672
14750
|
devDependencies: {
|
|
13673
14751
|
"@bike4mind/agents": "0.1.0",
|
|
13674
|
-
"@bike4mind/common": "2.
|
|
13675
|
-
"@bike4mind/mcp": "1.
|
|
13676
|
-
"@bike4mind/services": "2.
|
|
13677
|
-
"@bike4mind/utils": "2.
|
|
14752
|
+
"@bike4mind/common": "2.51.0",
|
|
14753
|
+
"@bike4mind/mcp": "1.30.0",
|
|
14754
|
+
"@bike4mind/services": "2.49.0",
|
|
14755
|
+
"@bike4mind/utils": "2.6.0",
|
|
13678
14756
|
"@types/better-sqlite3": "^7.6.13",
|
|
13679
14757
|
"@types/diff": "^5.0.9",
|
|
13680
14758
|
"@types/jsonwebtoken": "^9.0.4",
|
|
13681
14759
|
"@types/node": "^22.9.0",
|
|
14760
|
+
"@types/picomatch": "^4.0.2",
|
|
13682
14761
|
"@types/react": "^19.2.7",
|
|
13683
14762
|
"@types/uuid": "^9.0.7",
|
|
13684
14763
|
"@types/yargs": "^17.0.32",
|
|
@@ -13691,7 +14770,7 @@ var package_default = {
|
|
|
13691
14770
|
optionalDependencies: {
|
|
13692
14771
|
"@vscode/ripgrep": "^1.17.0"
|
|
13693
14772
|
},
|
|
13694
|
-
gitHead: "
|
|
14773
|
+
gitHead: "dd78e3f7ac6eb31fbbae8df3be1f33310779a9d5"
|
|
13695
14774
|
};
|
|
13696
14775
|
|
|
13697
14776
|
// src/config/constants.ts
|
|
@@ -13928,50 +15007,6 @@ ${expandedBody}
|
|
|
13928
15007
|
};
|
|
13929
15008
|
}
|
|
13930
15009
|
|
|
13931
|
-
// src/core/skillsPrompt.ts
|
|
13932
|
-
function getSkillDisplayName(cmd) {
|
|
13933
|
-
return cmd.displayName || cmd.name;
|
|
13934
|
-
}
|
|
13935
|
-
function formatSkillEntry(cmd) {
|
|
13936
|
-
const displayName = getSkillDisplayName(cmd);
|
|
13937
|
-
const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
|
|
13938
|
-
const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
|
|
13939
|
-
return `- ${nameDisplay}${argHint}: ${cmd.description}
|
|
13940
|
-
`;
|
|
13941
|
-
}
|
|
13942
|
-
function formatSkillGroup(heading, commands) {
|
|
13943
|
-
if (commands.length === 0) {
|
|
13944
|
-
return "";
|
|
13945
|
-
}
|
|
13946
|
-
return `
|
|
13947
|
-
### ${heading}
|
|
13948
|
-
${commands.map(formatSkillEntry).join("")}`;
|
|
13949
|
-
}
|
|
13950
|
-
function filterAIVisibleSkills(commands) {
|
|
13951
|
-
return commands.filter((cmd) => !cmd.disableModelInvocation);
|
|
13952
|
-
}
|
|
13953
|
-
function filterSkillsByAllowedList(commands, allowedSkills) {
|
|
13954
|
-
if (!allowedSkills || allowedSkills.length === 0) {
|
|
13955
|
-
return commands;
|
|
13956
|
-
}
|
|
13957
|
-
return commands.filter((cmd) => allowedSkills.includes(cmd.name));
|
|
13958
|
-
}
|
|
13959
|
-
function buildSkillsPromptSection(commands, allowedSkills) {
|
|
13960
|
-
const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
|
|
13961
|
-
const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
|
|
13962
|
-
if (visibleCommands.length === 0) {
|
|
13963
|
-
return "";
|
|
13964
|
-
}
|
|
13965
|
-
const projectSkills = visibleCommands.filter((c) => c.source === "project");
|
|
13966
|
-
const globalSkills = visibleCommands.filter((c) => c.source === "global");
|
|
13967
|
-
return `
|
|
13968
|
-
|
|
13969
|
-
## Available Skills
|
|
13970
|
-
|
|
13971
|
-
Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
|
|
13972
|
-
` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
|
|
13973
|
-
}
|
|
13974
|
-
|
|
13975
15010
|
// src/agents/SubagentOrchestrator.ts
|
|
13976
15011
|
var SubagentOrchestrator = class {
|
|
13977
15012
|
constructor(deps) {
|
|
@@ -14185,7 +15220,7 @@ var SubagentOrchestrator = class {
|
|
|
14185
15220
|
|
|
14186
15221
|
// src/agents/AgentStore.ts
|
|
14187
15222
|
import fs12 from "fs/promises";
|
|
14188
|
-
import
|
|
15223
|
+
import path16 from "path";
|
|
14189
15224
|
import os2 from "os";
|
|
14190
15225
|
import matter2 from "gray-matter";
|
|
14191
15226
|
var FULL_MODEL_ID_PREFIXES = [
|
|
@@ -14334,10 +15369,10 @@ var AgentStore = class {
|
|
|
14334
15369
|
const root = projectRoot || process.cwd();
|
|
14335
15370
|
const home = os2.homedir();
|
|
14336
15371
|
this.builtinAgentsDir = builtinDir;
|
|
14337
|
-
this.globalB4MAgentsDir =
|
|
14338
|
-
this.globalClaudeAgentsDir =
|
|
14339
|
-
this.projectB4MAgentsDir =
|
|
14340
|
-
this.projectClaudeAgentsDir =
|
|
15372
|
+
this.globalB4MAgentsDir = path16.join(home, ".bike4mind", "agents");
|
|
15373
|
+
this.globalClaudeAgentsDir = path16.join(home, ".claude", "agents");
|
|
15374
|
+
this.projectB4MAgentsDir = path16.join(root, ".bike4mind", "agents");
|
|
15375
|
+
this.projectClaudeAgentsDir = path16.join(root, ".claude", "agents");
|
|
14341
15376
|
}
|
|
14342
15377
|
/**
|
|
14343
15378
|
* Load all agents from all directories
|
|
@@ -14391,7 +15426,7 @@ var AgentStore = class {
|
|
|
14391
15426
|
try {
|
|
14392
15427
|
const entries = await fs12.readdir(directory, { withFileTypes: true });
|
|
14393
15428
|
for (const entry of entries) {
|
|
14394
|
-
const fullPath =
|
|
15429
|
+
const fullPath = path16.join(directory, entry.name);
|
|
14395
15430
|
if (entry.isDirectory()) {
|
|
14396
15431
|
const subFiles = await this.findAgentFiles(fullPath);
|
|
14397
15432
|
files.push(...subFiles);
|
|
@@ -14411,7 +15446,7 @@ var AgentStore = class {
|
|
|
14411
15446
|
const content = await fs12.readFile(filePath, "utf-8");
|
|
14412
15447
|
const { data: frontmatter, content: body } = matter2(content);
|
|
14413
15448
|
const parsed = AgentFrontmatterSchema.parse(frontmatter);
|
|
14414
|
-
const name =
|
|
15449
|
+
const name = path16.basename(filePath, ".md");
|
|
14415
15450
|
const modelInput = parsed.model || DEFAULT_AGENT_MODEL;
|
|
14416
15451
|
const resolution = resolveModelAlias(modelInput, name, filePath);
|
|
14417
15452
|
if (!resolution.resolved && resolution.warning) {
|
|
@@ -14491,7 +15526,7 @@ var AgentStore = class {
|
|
|
14491
15526
|
*/
|
|
14492
15527
|
async createAgentFile(name, isGlobal = false, useClaude = true) {
|
|
14493
15528
|
const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
|
|
14494
|
-
const filePath =
|
|
15529
|
+
const filePath = path16.join(targetDir, `${name}.md`);
|
|
14495
15530
|
try {
|
|
14496
15531
|
await fs12.access(filePath);
|
|
14497
15532
|
throw new Error(`Agent file already exists: ${filePath}`);
|
|
@@ -14552,6 +15587,21 @@ Describe the expected output format here.
|
|
|
14552
15587
|
}
|
|
14553
15588
|
return { builtin, global, project, total: this.agents.size };
|
|
14554
15589
|
}
|
|
15590
|
+
/**
|
|
15591
|
+
* Generates a markdown "Phone Book" of available agents and their schemas.
|
|
15592
|
+
* This MUST be injected into the System Prompt of the parent agent.
|
|
15593
|
+
*/
|
|
15594
|
+
getDirectoryContext() {
|
|
15595
|
+
if (this.agents.size === 0) {
|
|
15596
|
+
return "No sub-agents are currently available.";
|
|
15597
|
+
}
|
|
15598
|
+
let context = "Use `subagent_delegate` for complex tasks requiring specialized analysis.\n";
|
|
15599
|
+
for (const [name, def] of this.agents) {
|
|
15600
|
+
context += ` - **${name}**: ${def.description}
|
|
15601
|
+
`;
|
|
15602
|
+
}
|
|
15603
|
+
return context;
|
|
15604
|
+
}
|
|
14555
15605
|
};
|
|
14556
15606
|
|
|
14557
15607
|
// src/agents/delegateTool.ts
|
|
@@ -15173,7 +16223,7 @@ function createTodoStore(onUpdate) {
|
|
|
15173
16223
|
|
|
15174
16224
|
// src/tools/findDefinitionTool.ts
|
|
15175
16225
|
import { stat as stat3 } from "fs/promises";
|
|
15176
|
-
import
|
|
16226
|
+
import path17 from "path";
|
|
15177
16227
|
import { execFile as execFile2 } from "child_process";
|
|
15178
16228
|
import { promisify as promisify2 } from "util";
|
|
15179
16229
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -15194,8 +16244,8 @@ var ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
|
|
|
15194
16244
|
function getRipgrepPath2() {
|
|
15195
16245
|
try {
|
|
15196
16246
|
const ripgrepPath = require3.resolve("@vscode/ripgrep");
|
|
15197
|
-
const ripgrepDir =
|
|
15198
|
-
return
|
|
16247
|
+
const ripgrepDir = path17.dirname(ripgrepPath);
|
|
16248
|
+
return path17.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
|
|
15199
16249
|
} catch {
|
|
15200
16250
|
throw new Error(
|
|
15201
16251
|
"ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services"
|
|
@@ -15203,10 +16253,10 @@ function getRipgrepPath2() {
|
|
|
15203
16253
|
}
|
|
15204
16254
|
}
|
|
15205
16255
|
function isPathWithinWorkspace2(targetPath, baseCwd) {
|
|
15206
|
-
const resolvedTarget =
|
|
15207
|
-
const resolvedBase =
|
|
15208
|
-
const relativePath =
|
|
15209
|
-
return !relativePath.startsWith("..") && !
|
|
16256
|
+
const resolvedTarget = path17.resolve(targetPath);
|
|
16257
|
+
const resolvedBase = path17.resolve(baseCwd);
|
|
16258
|
+
const relativePath = path17.relative(resolvedBase, resolvedTarget);
|
|
16259
|
+
return !relativePath.startsWith("..") && !path17.isAbsolute(relativePath);
|
|
15210
16260
|
}
|
|
15211
16261
|
function escapeRegex(str) {
|
|
15212
16262
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -15239,7 +16289,7 @@ async function findDefinitions(params) {
|
|
|
15239
16289
|
throw new Error("symbol_name is required");
|
|
15240
16290
|
}
|
|
15241
16291
|
const baseCwd = process.cwd();
|
|
15242
|
-
const targetDir = search_path ?
|
|
16292
|
+
const targetDir = search_path ? path17.resolve(baseCwd, search_path) : baseCwd;
|
|
15243
16293
|
if (!isPathWithinWorkspace2(targetDir, baseCwd)) {
|
|
15244
16294
|
throw new Error(`Path validation failed: "${search_path}" resolves outside the allowed workspace directory`);
|
|
15245
16295
|
}
|
|
@@ -15294,7 +16344,7 @@ async function findDefinitions(params) {
|
|
|
15294
16344
|
const lineText = match.data.lines.text.trimEnd();
|
|
15295
16345
|
if (isLikelyDefinition(lineText)) {
|
|
15296
16346
|
allMatches.push({
|
|
15297
|
-
filePath:
|
|
16347
|
+
filePath: path17.relative(targetDir, match.data.path.text) || path17.basename(match.data.path.text),
|
|
15298
16348
|
lineNumber: match.data.line_number,
|
|
15299
16349
|
line: lineText
|
|
15300
16350
|
});
|
|
@@ -15372,8 +16422,8 @@ function createFindDefinitionTool() {
|
|
|
15372
16422
|
}
|
|
15373
16423
|
|
|
15374
16424
|
// src/tools/getFileStructure/index.ts
|
|
15375
|
-
import { existsSync as
|
|
15376
|
-
import
|
|
16425
|
+
import { existsSync as existsSync10, promises as fs13, statSync as statSync6 } from "fs";
|
|
16426
|
+
import path18 from "path";
|
|
15377
16427
|
|
|
15378
16428
|
// src/tools/getFileStructure/formatter.ts
|
|
15379
16429
|
var SECTIONS = [
|
|
@@ -15417,11 +16467,11 @@ function createGetFileStructureTool() {
|
|
|
15417
16467
|
const params = value;
|
|
15418
16468
|
try {
|
|
15419
16469
|
const cwd = process.cwd();
|
|
15420
|
-
const resolvedPath =
|
|
16470
|
+
const resolvedPath = path18.resolve(cwd, params.path);
|
|
15421
16471
|
if (!resolvedPath.startsWith(cwd)) {
|
|
15422
16472
|
return "Error: Access denied - cannot read files outside of current working directory";
|
|
15423
16473
|
}
|
|
15424
|
-
if (!
|
|
16474
|
+
if (!existsSync10(resolvedPath)) {
|
|
15425
16475
|
return `Error: File not found: ${params.path}`;
|
|
15426
16476
|
}
|
|
15427
16477
|
const stats = statSync6(resolvedPath);
|
|
@@ -15431,7 +16481,7 @@ function createGetFileStructureTool() {
|
|
|
15431
16481
|
if (stats.size > MAX_FILE_SIZE3) {
|
|
15432
16482
|
return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE3 / 1024 / 1024}MB`;
|
|
15433
16483
|
}
|
|
15434
|
-
const ext =
|
|
16484
|
+
const ext = path18.extname(resolvedPath).toLowerCase();
|
|
15435
16485
|
const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-4SGFQDY3.js");
|
|
15436
16486
|
const languageId = getLanguageForExtension(ext);
|
|
15437
16487
|
if (!languageId) {
|
|
@@ -15478,7 +16528,7 @@ var usageCache = null;
|
|
|
15478
16528
|
function CliApp() {
|
|
15479
16529
|
const { exit } = useApp();
|
|
15480
16530
|
const imageRenderer = new ImageRenderer();
|
|
15481
|
-
const [state, setState] =
|
|
16531
|
+
const [state, setState] = useState10({
|
|
15482
16532
|
session: null,
|
|
15483
16533
|
sessionStore: new SessionStore(),
|
|
15484
16534
|
configStore: new ConfigStore(),
|
|
@@ -15501,9 +16551,9 @@ function CliApp() {
|
|
|
15501
16551
|
contextContent: "",
|
|
15502
16552
|
backgroundManager: null
|
|
15503
16553
|
});
|
|
15504
|
-
const [isInitialized, setIsInitialized] =
|
|
15505
|
-
const [initError, setInitError] =
|
|
15506
|
-
const [commandHistory, setCommandHistory] =
|
|
16554
|
+
const [isInitialized, setIsInitialized] = useState10(false);
|
|
16555
|
+
const [initError, setInitError] = useState10(null);
|
|
16556
|
+
const [commandHistory, setCommandHistory] = useState10([]);
|
|
15507
16557
|
const imageStoreInitPromise = useRef3(null);
|
|
15508
16558
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
15509
16559
|
const enqueuePermissionPrompt = useCliStore((state2) => state2.enqueuePermissionPrompt);
|
|
@@ -15809,18 +16859,12 @@ function CliApp() {
|
|
|
15809
16859
|
for (const error of contextResult.errors) {
|
|
15810
16860
|
startupLog.push(`\u26A0\uFE0F Context file error: ${error}`);
|
|
15811
16861
|
}
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
${contextResult.mergedContent}` : "";
|
|
15819
|
-
if (enableSkillTool) {
|
|
15820
|
-
const commands = state.customCommandStore.getAllCommands();
|
|
15821
|
-
contextSection += buildSkillsPromptSection(commands);
|
|
15822
|
-
}
|
|
15823
|
-
const cliSystemPrompt = buildCoreSystemPrompt(contextSection);
|
|
16862
|
+
const cliSystemPrompt = buildCoreSystemPrompt({
|
|
16863
|
+
contextContent: contextResult.mergedContent,
|
|
16864
|
+
agentStore,
|
|
16865
|
+
customCommands: state.customCommandStore.getAllCommands(),
|
|
16866
|
+
enableSkillTool
|
|
16867
|
+
});
|
|
15824
16868
|
const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
|
|
15825
16869
|
const agent = new ReActAgent({
|
|
15826
16870
|
userId: config.userId,
|
|
@@ -15926,7 +16970,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
15926
16970
|
setStoreSession,
|
|
15927
16971
|
setState
|
|
15928
16972
|
]);
|
|
15929
|
-
|
|
16973
|
+
useEffect7(() => {
|
|
15930
16974
|
init();
|
|
15931
16975
|
}, [init]);
|
|
15932
16976
|
const handleCustomCommandMessage = async (fullTemplate, displayMessage) => {
|
|
@@ -16037,6 +17081,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16037
17081
|
completion: 0,
|
|
16038
17082
|
total: result.completionInfo.totalTokens
|
|
16039
17083
|
},
|
|
17084
|
+
creditsUsed: result.completionInfo.totalCredits,
|
|
16040
17085
|
model: state.session.model,
|
|
16041
17086
|
permissionDenied
|
|
16042
17087
|
}
|
|
@@ -16048,6 +17093,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16048
17093
|
metadata: {
|
|
16049
17094
|
...currentSession.metadata,
|
|
16050
17095
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
17096
|
+
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
16051
17097
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
16052
17098
|
}
|
|
16053
17099
|
};
|
|
@@ -16150,7 +17196,12 @@ ${contextResult.mergedContent}` : "";
|
|
|
16150
17196
|
const tokenCounter2 = getTokenCounter();
|
|
16151
17197
|
const contextWindow = tokenCounter2.getContextWindow(activeSession.model, state.availableModels);
|
|
16152
17198
|
const threshold = contextWindow * 0.8;
|
|
16153
|
-
const systemPrompt = buildCoreSystemPrompt(
|
|
17199
|
+
const systemPrompt = buildCoreSystemPrompt({
|
|
17200
|
+
contextContent: state.contextContent,
|
|
17201
|
+
agentStore: state.agentStore || void 0,
|
|
17202
|
+
customCommands: state.customCommandStore.getAllCommands(),
|
|
17203
|
+
enableSkillTool: config?.preferences.enableSkillTool !== false
|
|
17204
|
+
});
|
|
16154
17205
|
const contextUsage = tokenCounter2.countSessionTokens(activeSession, systemPrompt);
|
|
16155
17206
|
if (contextUsage.totalTokens >= threshold) {
|
|
16156
17207
|
console.log("\n\u26A0\uFE0F Context window 80% full. Auto-compacting...\n");
|
|
@@ -16245,6 +17296,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16245
17296
|
completion: 0,
|
|
16246
17297
|
total: result.completionInfo.totalTokens
|
|
16247
17298
|
},
|
|
17299
|
+
creditsUsed: result.completionInfo.totalCredits,
|
|
16248
17300
|
steps: result.steps.map(formatStep),
|
|
16249
17301
|
// Complete history: thoughts, actions, observations
|
|
16250
17302
|
permissionDenied
|
|
@@ -16258,6 +17310,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16258
17310
|
metadata: {
|
|
16259
17311
|
...currentSession.metadata,
|
|
16260
17312
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
17313
|
+
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
16261
17314
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
16262
17315
|
}
|
|
16263
17316
|
};
|
|
@@ -16356,7 +17409,8 @@ ${contextResult.mergedContent}` : "";
|
|
|
16356
17409
|
prompt: 0,
|
|
16357
17410
|
completion: 0,
|
|
16358
17411
|
total: result.completionInfo.totalTokens
|
|
16359
|
-
}
|
|
17412
|
+
},
|
|
17413
|
+
creditsUsed: result.completionInfo.totalCredits
|
|
16360
17414
|
}
|
|
16361
17415
|
};
|
|
16362
17416
|
const updatedSession = {
|
|
@@ -16366,6 +17420,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
16366
17420
|
metadata: {
|
|
16367
17421
|
...currentSession.metadata,
|
|
16368
17422
|
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
17423
|
+
totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
|
|
16369
17424
|
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
16370
17425
|
}
|
|
16371
17426
|
};
|
|
@@ -17068,15 +18123,21 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
17068
18123
|
}
|
|
17069
18124
|
const tokenCounter2 = getTokenCounter();
|
|
17070
18125
|
const contextWindow = tokenCounter2.getContextWindow(state.session.model, state.availableModels);
|
|
17071
|
-
const corePromptTokens = tokenCounter2.countTokens(buildCoreSystemPrompt(
|
|
18126
|
+
const corePromptTokens = tokenCounter2.countTokens(buildCoreSystemPrompt());
|
|
17072
18127
|
const projectContextTokens = state.contextContent ? tokenCounter2.countTokens(state.contextContent) : 0;
|
|
17073
18128
|
const commands = state.customCommandStore.getAllCommands();
|
|
17074
18129
|
const skillsSection = buildSkillsPromptSection(commands);
|
|
17075
18130
|
const skillsTokens = skillsSection ? tokenCounter2.countTokens(skillsSection) : 0;
|
|
18131
|
+
const agentDirectoryTokens = state.agentStore ? tokenCounter2.countTokens(state.agentStore.getDirectoryContext()) : 0;
|
|
17076
18132
|
const mcpTools = state.mcpManager?.getTools() || [];
|
|
17077
18133
|
const mcpToolsTokens = tokenCounter2.countToolSchemaTokens(mcpTools);
|
|
17078
18134
|
const mcpToolCount = state.mcpManager?.getToolCount() || [];
|
|
17079
|
-
const systemPrompt = buildCoreSystemPrompt(
|
|
18135
|
+
const systemPrompt = buildCoreSystemPrompt({
|
|
18136
|
+
contextContent: state.contextContent,
|
|
18137
|
+
agentStore: state.agentStore || void 0,
|
|
18138
|
+
customCommands: commands,
|
|
18139
|
+
enableSkillTool: state.config?.preferences.enableSkillTool !== false
|
|
18140
|
+
});
|
|
17080
18141
|
const usage = tokenCounter2.countSessionTokens(state.session, systemPrompt);
|
|
17081
18142
|
const totalWithTools = usage.totalTokens + mcpToolsTokens;
|
|
17082
18143
|
const usagePercent = totalWithTools / contextWindow * 100;
|
|
@@ -17095,6 +18156,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
17095
18156
|
if (commands.length > 0) {
|
|
17096
18157
|
console.log(` Skills Section: ${skillsTokens.toLocaleString()} tokens (${commands.length} skills)`);
|
|
17097
18158
|
}
|
|
18159
|
+
if (agentDirectoryTokens > 0) {
|
|
18160
|
+
const agentCount = state.agentStore?.getAgentCount() || 0;
|
|
18161
|
+
console.log(` Agent Directory: ${agentDirectoryTokens.toLocaleString()} tokens (${agentCount} agents)`);
|
|
18162
|
+
}
|
|
17098
18163
|
if (mcpTools.length > 0) {
|
|
17099
18164
|
console.log("\nMCP Tools:");
|
|
17100
18165
|
console.log(` Total: ${mcpToolsTokens.toLocaleString()} tokens (${mcpTools.length} tools)`);
|
|
@@ -17499,6 +18564,7 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
17499
18564
|
if (isDevMode) {
|
|
17500
18565
|
logger.debug("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
17501
18566
|
}
|
|
18567
|
+
warmFileCache();
|
|
17502
18568
|
render(/* @__PURE__ */ React21.createElement(CliApp, null), {
|
|
17503
18569
|
exitOnCtrlC: false
|
|
17504
18570
|
});
|