casabot 1.1.9 → 1.1.12
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/Logo.svg +24 -0
- package/Logo2.png +0 -0
- package/README.md +4 -0
- package/dist/agent/base.js +21 -7
- package/dist/cli/braille-logo.d.ts +2 -0
- package/dist/cli/braille-logo.js +59 -0
- package/dist/cli/index.js +17 -0
- package/dist/tui/app.js +10 -8
- package/package.json +2 -1
- package/skills/agent/SKILL.md +51 -4
- package/src/agent/base.ts +20 -5
- package/src/cli/braille-logo.ts +72 -0
- package/src/cli/index.ts +18 -0
- package/src/tui/app.tsx +20 -16
package/Logo.svg
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg width="3701" height="1227" viewBox="0 0 3701 1227" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="3701" height="1227" fill="white"/>
|
|
3
|
+
<path d="M660.525 238.407C676.289 208.559 719.039 208.559 734.803 238.407L775.914 316.25C785.995 335.338 808.903 343.676 828.895 335.534L910.424 302.328C941.686 289.596 974.435 317.076 967.324 350.073L948.781 436.13C944.233 457.232 956.423 478.345 976.971 484.958L1060.77 511.927C1092.9 522.268 1100.33 564.369 1073.67 585.076L1004.15 639.08C987.1 652.322 982.866 676.33 994.357 694.604L1041.22 769.129C1059.18 797.705 1037.81 834.728 1004.08 833.455L916.107 830.137C894.536 829.323 875.861 844.994 872.916 866.378L860.908 953.588C856.304 987.027 816.132 1001.65 791.11 978.992L725.855 919.904C709.853 905.416 685.475 905.416 669.473 919.904L604.218 978.992C579.196 1001.65 539.024 987.027 534.42 953.588L522.412 866.378C519.467 844.994 500.792 829.323 479.221 830.137L391.252 833.455C357.521 834.728 336.145 797.705 354.113 769.129L400.971 694.604C412.461 676.33 408.228 652.322 391.181 639.08L321.659 585.076C295.002 564.369 302.426 522.268 334.557 511.927L418.357 484.958C438.905 478.345 451.094 457.232 446.547 436.13L428.004 350.073C420.893 317.076 453.642 289.596 484.904 302.328L566.433 335.534C586.425 343.676 609.333 335.338 619.414 316.25L660.525 238.407Z" fill="#EA3A23"/>
|
|
4
|
+
<path d="M797.472 250.022C822.885 227.806 862.796 243.126 866.816 276.641L877.3 364.047C879.871 385.479 898.27 401.473 919.851 401.036L1007.87 399.254C1041.61 398.57 1062.34 435.961 1043.88 464.218L995.724 537.914C983.917 555.985 987.73 580.063 1004.54 593.601L1073.11 648.81C1099.4 669.979 1091.25 711.944 1058.94 721.723L974.682 747.225C954.022 753.479 941.466 774.375 945.644 795.553L962.683 881.921C969.216 915.037 935.993 941.941 904.958 928.665L824.021 894.042C804.174 885.552 781.124 893.489 770.711 912.398L728.248 989.511C711.966 1019.08 669.222 1018.33 653.982 988.215L614.235 909.666C604.489 890.406 581.73 881.669 561.599 889.461L479.503 921.239C448.024 933.423 415.759 905.377 423.445 872.508L443.487 786.788C448.402 765.769 436.583 744.447 416.153 737.476L332.837 709.048C300.891 698.148 294.203 655.924 321.218 635.685L391.671 582.903C408.947 569.96 413.599 546.03 402.429 527.558L356.878 452.227C339.413 423.342 361.431 386.697 395.134 388.558L483.032 393.411C504.586 394.601 523.532 379.259 526.849 357.929L540.377 270.943C545.564 237.589 585.986 223.671 610.608 246.761L674.822 306.978C690.568 321.744 714.943 322.17 731.195 307.962L797.472 250.022Z" fill="#2BADEE" fill-opacity="0.5"/>
|
|
5
|
+
<circle cx="487.27" cy="557.093" r="116.956" fill="white"/>
|
|
6
|
+
<circle cx="871.191" cy="531.667" r="116.956" fill="white"/>
|
|
7
|
+
<circle cx="430.063" cy="545.651" r="34.3241" fill="black"/>
|
|
8
|
+
<circle cx="923.313" cy="497.343" r="34.3241" fill="black"/>
|
|
9
|
+
<path d="M822.883 682.312C822.883 698.756 819.645 715.039 813.352 730.232C807.059 745.424 797.835 759.228 786.208 770.856C774.58 782.484 760.776 791.707 745.583 798C730.391 804.293 714.108 807.532 697.664 807.532C681.22 807.532 664.937 804.293 649.744 798C634.552 791.707 620.748 782.484 609.12 770.856C597.493 759.228 588.269 745.424 581.976 730.232C575.683 715.039 572.444 698.756 572.444 682.312L629.702 682.312C629.702 691.237 631.46 700.075 634.875 708.32C638.291 716.566 643.297 724.058 649.607 730.369C655.918 736.68 663.41 741.686 671.656 745.101C679.902 748.516 688.739 750.274 697.664 750.274C706.589 750.274 715.426 748.516 723.672 745.101C731.917 741.686 739.41 736.68 745.72 730.369C752.031 724.058 757.037 716.566 760.453 708.32C763.868 700.075 765.626 691.237 765.626 682.312H822.883Z" fill="black"/>
|
|
10
|
+
<path d="M1685.16 695.25C1696.64 670.106 1732.36 670.106 1743.84 695.25V695.25C1749.56 707.757 1762.57 715.269 1776.25 713.962V713.962C1803.77 711.335 1821.63 742.271 1805.6 764.788V764.788C1797.62 775.988 1797.62 791.012 1805.6 802.212V802.212C1821.63 824.729 1803.77 855.665 1776.25 853.038V853.038C1762.57 851.731 1749.56 859.243 1743.84 871.75V871.75C1732.36 896.894 1696.64 896.894 1685.16 871.75V871.75C1679.44 859.243 1666.43 851.731 1652.75 853.038V853.038C1625.23 855.665 1607.37 824.729 1623.4 802.212V802.212C1631.38 791.012 1631.38 775.988 1623.4 764.788V764.788C1607.37 742.271 1625.23 711.335 1652.75 713.962V713.962C1666.43 715.269 1679.44 707.757 1685.16 695.25V695.25Z" fill="#EA3A23"/>
|
|
11
|
+
<path d="M2697.58 657.708C2710.48 633.24 2745.52 633.24 2758.42 657.708V657.708C2765.46 671.079 2780.42 678.279 2795.26 675.453V675.453C2822.44 670.28 2844.28 697.676 2833.19 723.015V723.015C2827.14 736.862 2830.83 753.042 2842.3 762.888V762.888C2863.28 780.906 2855.48 815.068 2828.76 822.197V822.197C2814.15 826.093 2803.81 839.068 2803.26 854.172V854.172C2802.26 881.813 2770.69 897.017 2748.45 880.567V880.567C2736.3 871.578 2719.7 871.578 2707.55 880.567V880.567C2685.31 897.017 2653.74 881.813 2652.74 854.172V854.172C2652.19 839.068 2641.85 826.093 2627.24 822.197V822.197C2600.52 815.068 2592.72 780.906 2613.7 762.888V762.888C2625.17 753.042 2628.86 736.862 2622.81 723.015V723.015C2611.72 697.676 2633.56 670.28 2660.74 675.453V675.453C2675.58 678.279 2690.54 671.079 2697.58 657.708V657.708Z" fill="black"/>
|
|
12
|
+
<path d="M3078.87 866.916C3058.12 899.232 3010.88 899.232 2990.13 866.916L2976.57 845.808C2969.43 834.689 2958.37 826.657 2945.59 823.3L2921.33 816.929C2884.18 807.174 2869.58 762.253 2893.9 732.527L2909.79 713.11C2918.15 702.882 2922.38 689.885 2921.62 676.692L2920.18 651.646C2917.98 613.302 2956.19 585.539 2991.98 599.482L3015.35 608.59C3027.67 613.388 3041.33 613.388 3053.65 608.59L3077.02 599.482C3112.81 585.539 3151.02 613.302 3148.82 651.646L3147.38 676.692C3146.62 689.885 3150.85 702.882 3159.21 713.11L3175.1 732.527C3199.42 762.253 3184.82 807.174 3147.67 816.929L3123.41 823.3C3110.63 826.657 3099.57 834.689 3092.43 845.808L3078.87 866.916Z" fill="#2BADEE"/>
|
|
13
|
+
<path d="M1545.44 819.589C1515.81 849.148 1478.24 869.518 1437.26 878.238C1396.29 886.957 1353.65 883.654 1314.52 868.728C1275.38 853.801 1241.41 827.888 1216.71 794.12C1192.01 760.351 1177.64 720.17 1175.33 678.43C1173.02 636.69 1182.87 595.176 1203.7 558.901C1224.52 522.625 1255.43 493.141 1292.68 474.008C1329.93 454.874 1371.94 446.911 1413.63 451.079C1455.33 455.247 1494.92 471.367 1527.62 497.495L1481.9 554.471C1460.22 537.151 1433.98 526.465 1406.34 523.702C1378.71 520.94 1350.86 526.219 1326.16 538.902C1301.47 551.584 1280.98 571.129 1267.18 595.175C1253.38 619.221 1246.84 646.74 1248.37 674.408C1249.91 702.077 1259.43 728.712 1275.8 751.096C1292.18 773.48 1314.69 790.657 1340.64 800.552C1366.58 810.446 1394.84 812.636 1422 806.856C1449.17 801.076 1474.07 787.573 1493.71 767.979L1545.44 819.589Z" fill="black"/>
|
|
14
|
+
<path d="M1962.5 777C1937.17 777 1912.88 766.886 1894.97 748.882C1877.06 730.879 1867 706.461 1867 681C1867 655.539 1877.06 631.121 1894.97 613.118C1912.88 595.114 1937.17 585 1962.5 585L1962.5 639.079C1951.44 639.079 1940.83 643.495 1933.01 651.357C1925.19 659.219 1920.8 669.882 1920.8 681C1920.8 692.118 1925.19 702.781 1933.01 710.643C1940.83 718.505 1951.44 722.921 1962.5 722.921L1962.5 777Z" fill="black"/>
|
|
15
|
+
<path d="M1962.5 701C1987.83 701 2012.12 711.062 2030.03 728.971C2047.94 746.881 2058 771.172 2058 796.5C2058 821.828 2047.94 846.119 2030.03 864.029C2012.12 881.938 1987.83 892 1962.5 892L1962.5 838.203C1973.56 838.203 1984.17 833.809 1991.99 825.989C1999.81 818.168 2004.2 807.56 2004.2 796.5C2004.2 785.44 1999.81 774.832 1991.99 767.011C1984.17 759.191 1973.56 754.797 1962.5 754.797L1962.5 701Z" fill="black"/>
|
|
16
|
+
<path d="M1624.31 640.094C1631.91 618.283 1647.15 599.963 1667.22 588.527C1687.28 577.092 1710.81 573.316 1733.45 577.899C1756.09 582.482 1776.3 595.113 1790.33 613.452C1804.37 631.792 1811.29 654.598 1809.8 677.646L1753.63 674.024C1754.24 664.56 1751.4 655.197 1745.64 647.667C1739.87 640.137 1731.57 634.951 1722.28 633.069C1712.99 631.188 1703.32 632.738 1695.09 637.433C1686.85 642.128 1680.59 649.65 1677.47 658.605L1624.31 640.094Z" fill="black"/>
|
|
17
|
+
<rect x="2148.83" y="455.492" width="75" height="420.804" transform="rotate(9 2148.83 455.492)" fill="black"/>
|
|
18
|
+
<rect x="2522" y="450" width="75" height="434" fill="black"/>
|
|
19
|
+
<rect x="3262" y="455" width="75" height="434" fill="black"/>
|
|
20
|
+
<rect x="3417" y="539" width="75" height="234" transform="rotate(90 3417 539)" fill="black"/>
|
|
21
|
+
<rect x="3417" y="817" width="75" height="155" transform="rotate(90 3417 817)" fill="black"/>
|
|
22
|
+
<rect x="2223" y="466.871" width="75" height="420.804" transform="rotate(-13 2223 466.871)" fill="black"/>
|
|
23
|
+
<rect x="2413" y="657" width="75" height="280" transform="rotate(90 2413 657)" fill="black"/>
|
|
24
|
+
</svg>
|
package/Logo2.png
ADDED
|
Binary file
|
package/README.md
CHANGED
package/dist/agent/base.js
CHANGED
|
@@ -3,6 +3,8 @@ import { appendMessage } from "../history/store.js";
|
|
|
3
3
|
import { formatSkillsForPrompt } from "../skills/loader.js";
|
|
4
4
|
import { CASABOT_HOME } from "../config/manager.js";
|
|
5
5
|
const MAX_ITERATIONS = 20;
|
|
6
|
+
const MAX_RETRIES = 3;
|
|
7
|
+
const RETRY_BASE_DELAY_MS = 2000;
|
|
6
8
|
function raceAbort(promise, signal) {
|
|
7
9
|
if (signal.aborted)
|
|
8
10
|
return Promise.reject(new Error("AbortError"));
|
|
@@ -56,13 +58,25 @@ export async function* runAgent(provider, userMessage, conversation, skills, sig
|
|
|
56
58
|
...conversation.messages,
|
|
57
59
|
];
|
|
58
60
|
let assistantMsg;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
for (let attempt = 0;; attempt++) {
|
|
62
|
+
try {
|
|
63
|
+
assistantMsg = await raceAbort(provider.chat(messagesWithSystem, tools), signal);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (signal.aborted)
|
|
68
|
+
return;
|
|
69
|
+
if (attempt >= MAX_RETRIES)
|
|
70
|
+
throw err;
|
|
71
|
+
const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
|
|
72
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
73
|
+
const retryMsg = {
|
|
74
|
+
role: "assistant",
|
|
75
|
+
content: `⏳ API request failed: ${errorMsg}. Retrying in ${delay}ms... (${attempt + 1}/${MAX_RETRIES})`,
|
|
76
|
+
};
|
|
77
|
+
yield retryMsg;
|
|
78
|
+
await raceAbort(new Promise((resolve) => setTimeout(resolve, delay)), signal);
|
|
79
|
+
}
|
|
66
80
|
}
|
|
67
81
|
await appendMessage(conversation, assistantMsg);
|
|
68
82
|
yield assistantMsg;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Jimp, intToRGBA } from "jimp";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
const BRAILLE_BASE = 0x2800;
|
|
4
|
+
const BRAILLE_MAP = [
|
|
5
|
+
[0x01, 0x08],
|
|
6
|
+
[0x02, 0x10],
|
|
7
|
+
[0x04, 0x20],
|
|
8
|
+
[0x40, 0x80],
|
|
9
|
+
];
|
|
10
|
+
export async function renderBrailleLogo(logoPath, maxWidth) {
|
|
11
|
+
const image = await Jimp.read(logoPath);
|
|
12
|
+
const targetPixelWidth = maxWidth * 2;
|
|
13
|
+
image.resize({ w: targetPixelWidth });
|
|
14
|
+
const { width, height } = image.bitmap;
|
|
15
|
+
const lines = [];
|
|
16
|
+
for (let y = 0; y < height; y += 4) {
|
|
17
|
+
let line = "";
|
|
18
|
+
for (let x = 0; x < width; x += 2) {
|
|
19
|
+
let pattern = 0;
|
|
20
|
+
let rSum = 0;
|
|
21
|
+
let gSum = 0;
|
|
22
|
+
let bSum = 0;
|
|
23
|
+
let onCount = 0;
|
|
24
|
+
for (let row = 0; row < 4; row++) {
|
|
25
|
+
for (let col = 0; col < 2; col++) {
|
|
26
|
+
const px = x + col;
|
|
27
|
+
const py = y + row;
|
|
28
|
+
if (px >= width || py >= height)
|
|
29
|
+
continue;
|
|
30
|
+
const { r, g, b, a } = intToRGBA(image.getPixelColor(px, py));
|
|
31
|
+
const brightness = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
32
|
+
if (a > 20 && brightness > 30) {
|
|
33
|
+
pattern |= BRAILLE_MAP[row][col];
|
|
34
|
+
rSum += r;
|
|
35
|
+
gSum += g;
|
|
36
|
+
bSum += b;
|
|
37
|
+
onCount++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (onCount > 0) {
|
|
42
|
+
const ch = String.fromCharCode(BRAILLE_BASE + pattern);
|
|
43
|
+
const avgR = Math.round(rSum / onCount);
|
|
44
|
+
const avgG = Math.round(gSum / onCount);
|
|
45
|
+
const avgB = Math.round(bSum / onCount);
|
|
46
|
+
line += chalk.rgb(avgR, avgG, avgB)(ch);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
line += " ";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
lines.push(line);
|
|
53
|
+
}
|
|
54
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
55
|
+
lines.pop();
|
|
56
|
+
}
|
|
57
|
+
return lines.join("\n");
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=braille-logo.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { dirname, join } from "path";
|
|
2
4
|
import { Command } from "commander";
|
|
3
5
|
import { loadConfig, saveConfig, getDefaultConfig, ensureDirectories } from "../config/manager.js";
|
|
4
6
|
import { createProvider } from "../providers/index.js";
|
|
@@ -6,6 +8,20 @@ import { loadSkills } from "../skills/loader.js";
|
|
|
6
8
|
import { createConversation } from "../history/store.js";
|
|
7
9
|
import { startTUI } from "../tui/app.js";
|
|
8
10
|
import { setupWizard } from "./setup.js";
|
|
11
|
+
import { renderBrailleLogo } from "./braille-logo.js";
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
async function displayLogo() {
|
|
14
|
+
try {
|
|
15
|
+
const logoPath = join(__dirname, "..", "..", "Logo2.png");
|
|
16
|
+
const maxWidth = Math.min(process.stdout.columns || 80, 80);
|
|
17
|
+
const art = await renderBrailleLogo(logoPath, maxWidth);
|
|
18
|
+
console.log(art);
|
|
19
|
+
console.log("");
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Logo display is non-critical; silently skip on failure
|
|
23
|
+
}
|
|
24
|
+
}
|
|
9
25
|
const program = new Command();
|
|
10
26
|
program
|
|
11
27
|
.name("casabot")
|
|
@@ -58,6 +74,7 @@ program
|
|
|
58
74
|
const provider = createProvider(providerConfig);
|
|
59
75
|
const skills = await loadSkills();
|
|
60
76
|
const conversation = createConversation();
|
|
77
|
+
await displayLogo();
|
|
61
78
|
startTUI(provider, conversation, skills);
|
|
62
79
|
}
|
|
63
80
|
catch (err) {
|
package/dist/tui/app.js
CHANGED
|
@@ -8,6 +8,8 @@ import { Marked } from "marked";
|
|
|
8
8
|
import { markedTerminal } from "marked-terminal";
|
|
9
9
|
import { runAgent } from "../agent/base.js";
|
|
10
10
|
import { createConversation, listConversations, saveConversation, appendMessage, } from "../history/store.js";
|
|
11
|
+
const BRAND_BLUE = "#2BADEE";
|
|
12
|
+
const BRAND_RED = "#EA3A23";
|
|
11
13
|
function renderMarkdown(content) {
|
|
12
14
|
const width = Math.max((process.stdout.columns ?? 80) - 8, 40);
|
|
13
15
|
const md = new Marked({ gfm: true });
|
|
@@ -37,17 +39,17 @@ function HRule({ columns }) {
|
|
|
37
39
|
return (_jsx(Box, { paddingX: 1, width: columns, children: _jsx(Text, { dimColor: true, children: "─".repeat(Math.max(columns - 4, 1)) }) }));
|
|
38
40
|
}
|
|
39
41
|
function HeaderBlock({ columns }) {
|
|
40
|
-
return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, width: columns, children: [_jsx(Box, { paddingX: 2, children: _jsx(Gradient, {
|
|
42
|
+
return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, width: columns, children: [_jsx(Box, { paddingX: 2, children: _jsx(Gradient, { colors: [BRAND_RED, BRAND_BLUE], children: _jsx(Text, { bold: true, children: "✦ CasAbot" }) }) }), _jsx(Box, { paddingX: 2, children: _jsx(Text, { wrap: "wrap", dimColor: true, children: "Cassiopeia A — Freely creates everything, like a supernova explosion." }) }), _jsx(HRule, { columns: columns })] }));
|
|
41
43
|
}
|
|
42
44
|
function UserMessageView({ content, columns }) {
|
|
43
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color:
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color: BRAND_RED, bold: true, children: "▶ You" }), _jsx(Box, { marginLeft: 2, width: Math.max(columns - 6, 10), children: _jsx(Text, { wrap: "wrap", children: content }) })] }));
|
|
44
46
|
}
|
|
45
47
|
function AssistantMessageView({ content, columns, }) {
|
|
46
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color:
|
|
48
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color: BRAND_BLUE, bold: true, children: "✦ CasAbot" }), _jsx(Box, { marginLeft: 2, width: Math.max(columns - 6, 10), children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(content) }) })] }));
|
|
47
49
|
}
|
|
48
50
|
function ToolCallsView({ message, columns, }) {
|
|
49
51
|
const boxWidth = Math.max(columns - 6, 10);
|
|
50
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color:
|
|
52
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, width: columns, children: [_jsx(Text, { color: BRAND_BLUE, bold: true, children: "✦ CasAbot" }), message.content ? (_jsx(Box, { marginLeft: 2, width: boxWidth, children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(message.content) }) })) : null, _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, borderStyle: "round", borderColor: BRAND_RED, paddingX: 1, width: boxWidth, overflow: "hidden", children: [_jsx(Text, { color: BRAND_RED, bold: true, children: "⚡ Tool Calls" }), message.toolCalls?.map((tc, i) => {
|
|
51
53
|
let display = tc.arguments;
|
|
52
54
|
try {
|
|
53
55
|
const args = JSON.parse(tc.arguments);
|
|
@@ -87,7 +89,7 @@ function WelcomeHint({ columns }) {
|
|
|
87
89
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, marginBottom: 1, width: columns, children: [_jsx(Text, { dimColor: true, children: "Type a message below to get started." }), _jsx(Text, { dimColor: true, children: "CasAbot will orchestrate agents to help you." })] }));
|
|
88
90
|
}
|
|
89
91
|
function ProcessingIndicator({ columns }) {
|
|
90
|
-
return (_jsxs(Box, { paddingX: 2, marginTop: 1, gap: 1, width: columns, children: [_jsx(Text, { color:
|
|
92
|
+
return (_jsxs(Box, { paddingX: 2, marginTop: 1, gap: 1, width: columns, children: [_jsx(Text, { color: BRAND_BLUE, children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: BRAND_BLUE, children: "Thinking…" })] }));
|
|
91
93
|
}
|
|
92
94
|
function HistoryBrowser({ columns, currentId, onSelect, onBack, }) {
|
|
93
95
|
const [conversations, setConversations] = useState([]);
|
|
@@ -120,13 +122,13 @@ function HistoryBrowser({ columns, currentId, onSelect, onBack, }) {
|
|
|
120
122
|
}
|
|
121
123
|
});
|
|
122
124
|
const boxWidth = Math.max(columns - 4, 10);
|
|
123
|
-
return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, width: columns, children: [_jsx(Box, { paddingX: 2, children: _jsx(Gradient, {
|
|
125
|
+
return (_jsxs(Box, { flexDirection: "column", paddingTop: 1, width: columns, children: [_jsx(Box, { paddingX: 2, children: _jsx(Gradient, { colors: [BRAND_RED, BRAND_BLUE], children: _jsx(Text, { bold: true, children: "📋 Session History" }) }) }), _jsx(HRule, { columns: columns }), isLoading ? (_jsxs(Box, { paddingX: 2, marginTop: 1, gap: 1, children: [_jsx(Text, { color: BRAND_BLUE, children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: BRAND_BLUE, children: "Loading sessions…" })] })) : conversations.length === 0 ? (_jsx(Box, { paddingX: 2, marginTop: 1, children: _jsx(Text, { dimColor: true, children: "No previous sessions found." }) })) : (_jsx(Box, { flexDirection: "column", marginX: 2, marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, paddingY: 1, width: boxWidth, overflow: "hidden", children: conversations.map((conv, i) => {
|
|
124
126
|
const isSelected = i === selectedIndex;
|
|
125
127
|
const isCurrent = conv.id === currentId;
|
|
126
128
|
const dateStr = formatDate(conv.startedAt);
|
|
127
129
|
const preview = getPreview(conv, Math.max(boxWidth - dateStr.length - 12, 20));
|
|
128
130
|
const msgCount = conv.messages.filter((m) => m.role === "user").length;
|
|
129
|
-
return (_jsxs(Box, { width: boxWidth - 4, children: [_jsx(Text, { color: isSelected ?
|
|
131
|
+
return (_jsxs(Box, { width: boxWidth - 4, children: [_jsx(Text, { color: isSelected ? BRAND_BLUE : undefined, bold: isSelected, dimColor: !isSelected, children: isSelected ? " ▶ " : " " }), _jsx(Text, { color: isSelected ? BRAND_BLUE : undefined, bold: isSelected, dimColor: !isSelected, children: dateStr }), _jsx(Text, { dimColor: !isSelected, children: " │ " }), _jsx(Text, { color: isSelected ? "white" : undefined, dimColor: !isSelected, wrap: "truncate", children: preview }), isCurrent ? (_jsx(Text, { color: BRAND_RED, bold: true, children: " (current)" })) : null, _jsx(Text, { dimColor: true, children: ` [${msgCount}]` })] }, conv.id));
|
|
130
132
|
}) })), _jsx(HRule, { columns: columns }), _jsxs(Box, { paddingX: 2, width: columns, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: "↑↓ navigate Enter select ESC back" }), _jsx(Text, { dimColor: true, children: `${conversations.length} sessions` })] })] }));
|
|
131
133
|
}
|
|
132
134
|
function App({ provider, conversation: initialConversation, skills, }) {
|
|
@@ -242,7 +244,7 @@ function App({ provider, conversation: initialConversation, skills, }) {
|
|
|
242
244
|
return (_jsx(Box, { flexDirection: "column", width: columns, children: _jsx(HeaderBlock, { columns: columns }) }, item.key));
|
|
243
245
|
}
|
|
244
246
|
return (_jsx(Box, { flexDirection: "column", width: columns, children: _jsx(MessageView, { message: item.message, columns: columns }) }, item.key));
|
|
245
|
-
} }), messages.length === 0 && !isProcessing && _jsx(WelcomeHint, { columns: columns }), isProcessing && _jsx(ProcessingIndicator, { columns: columns }), _jsx(HRule, { columns: columns }), _jsx(Box, { paddingX: 1, width: columns, children: _jsxs(Box, { borderStyle: "round", borderColor: isProcessing ? "gray" :
|
|
247
|
+
} }), messages.length === 0 && !isProcessing && _jsx(WelcomeHint, { columns: columns }), isProcessing && _jsx(ProcessingIndicator, { columns: columns }), _jsx(HRule, { columns: columns }), _jsx(Box, { paddingX: 1, width: columns, children: _jsxs(Box, { borderStyle: "round", borderColor: isProcessing ? "gray" : BRAND_BLUE, paddingX: 1, width: Math.max(columns - 2, 10), overflow: "hidden", children: [_jsx(Text, { color: BRAND_BLUE, bold: true, children: "❯ " }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: (val) => {
|
|
246
248
|
handleSubmit(val).catch(() => { });
|
|
247
249
|
}, placeholder: "Type your message\u2026", focus: !isProcessing, showCursor: true })] }) }), _jsxs(Box, { paddingX: 2, width: columns, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: isProcessing
|
|
248
250
|
? "ESC / Ctrl+C cancel"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "casabot",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"description": "CasAbot — Skill-driven multi-agent orchestrator system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"ink-gradient": "^4.0.0",
|
|
36
36
|
"ink-spinner": "^5.0.0",
|
|
37
37
|
"ink-text-input": "^6.0.0",
|
|
38
|
+
"jimp": "^1.6.0",
|
|
38
39
|
"marked": "^15.0.12",
|
|
39
40
|
"marked-terminal": "^7.3.0",
|
|
40
41
|
"openai": "^5.1.0",
|
package/skills/agent/SKILL.md
CHANGED
|
@@ -17,15 +17,62 @@ This manual explains how the base agent creates and manages podman-based sub-age
|
|
|
17
17
|
|
|
18
18
|
podman must be installed before creating sub-agents.
|
|
19
19
|
|
|
20
|
+
### Step 1: Check if podman is already installed
|
|
21
|
+
|
|
20
22
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
which podman && podman --version
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If podman is found, skip to Section 2.
|
|
27
|
+
|
|
28
|
+
### Step 2: Gather requirements from the user
|
|
29
|
+
|
|
30
|
+
> **Important:** Before installing podman, you **must** ask the user the following questions. Do not assume or proceed without their answers.
|
|
31
|
+
|
|
32
|
+
1. **Detect or ask the distro:**
|
|
33
|
+
```bash
|
|
34
|
+
cat /etc/os-release
|
|
35
|
+
```
|
|
36
|
+
If the distro cannot be determined, ask the user: *"Which Linux distribution are you using? (e.g. Ubuntu, Fedora, Arch, Debian, RHEL, etc.)"*
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
2. **Ask about sudo privileges:**
|
|
39
|
+
*"Do you have sudo (root) privileges on this system?"*
|
|
40
|
+
— If the user does not have sudo, guide them to request it from an administrator, or suggest rootless podman setup if possible.
|
|
41
|
+
|
|
42
|
+
3. **Ask about rootless mode:**
|
|
43
|
+
*"Would you like to run podman in rootless mode (recommended for security)?"*
|
|
44
|
+
|
|
45
|
+
4. **Ask about special requirements:**
|
|
46
|
+
*"Do you have any specific requirements? (e.g. a particular podman version, a custom storage location, proxy settings, etc.)"*
|
|
47
|
+
|
|
48
|
+
### Step 3: Install podman based on user's answers
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Debian / Ubuntu
|
|
25
52
|
sudo apt update && sudo apt install -y podman
|
|
26
53
|
|
|
27
|
-
#
|
|
54
|
+
# Fedora / RHEL / CentOS
|
|
28
55
|
sudo dnf install -y podman
|
|
56
|
+
|
|
57
|
+
# Arch Linux
|
|
58
|
+
sudo pacman -S podman
|
|
59
|
+
|
|
60
|
+
# openSUSE
|
|
61
|
+
sudo zypper install -y podman
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If the user requested rootless mode, also configure subuid/subgid:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami)
|
|
68
|
+
podman system migrate
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Step 4: Verify installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
podman --version
|
|
75
|
+
podman info
|
|
29
76
|
```
|
|
30
77
|
|
|
31
78
|
## 2. Configure podman storage
|
package/src/agent/base.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { formatSkillsForPrompt } from "../skills/loader.js";
|
|
|
6
6
|
import { CASABOT_HOME } from "../config/manager.js";
|
|
7
7
|
|
|
8
8
|
const MAX_ITERATIONS = 20;
|
|
9
|
+
const MAX_RETRIES = 3;
|
|
10
|
+
const RETRY_BASE_DELAY_MS = 2000;
|
|
9
11
|
|
|
10
12
|
function raceAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {
|
|
11
13
|
if (signal.aborted) return Promise.reject(new Error("AbortError"));
|
|
@@ -76,11 +78,24 @@ export async function* runAgent(
|
|
|
76
78
|
];
|
|
77
79
|
|
|
78
80
|
let assistantMsg: Message;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
for (let attempt = 0; ; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
assistantMsg = await raceAbort(provider.chat(messagesWithSystem, tools), signal);
|
|
84
|
+
break;
|
|
85
|
+
} catch (err: unknown) {
|
|
86
|
+
if (signal.aborted) return;
|
|
87
|
+
if (attempt >= MAX_RETRIES) throw err;
|
|
88
|
+
|
|
89
|
+
const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
|
|
90
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
91
|
+
const retryMsg: Message = {
|
|
92
|
+
role: "assistant",
|
|
93
|
+
content: `⏳ API request failed: ${errorMsg}. Retrying in ${delay}ms... (${attempt + 1}/${MAX_RETRIES})`,
|
|
94
|
+
};
|
|
95
|
+
yield retryMsg;
|
|
96
|
+
|
|
97
|
+
await raceAbort(new Promise((resolve) => setTimeout(resolve, delay)), signal);
|
|
98
|
+
}
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
await appendMessage(conversation, assistantMsg);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Jimp, intToRGBA } from "jimp";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
const BRAILLE_BASE = 0x2800;
|
|
5
|
+
|
|
6
|
+
const BRAILLE_MAP = [
|
|
7
|
+
[0x01, 0x08],
|
|
8
|
+
[0x02, 0x10],
|
|
9
|
+
[0x04, 0x20],
|
|
10
|
+
[0x40, 0x80],
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
export async function renderBrailleLogo(
|
|
14
|
+
logoPath: string,
|
|
15
|
+
maxWidth: number,
|
|
16
|
+
): Promise<string> {
|
|
17
|
+
const image = await Jimp.read(logoPath);
|
|
18
|
+
const targetPixelWidth = maxWidth * 2;
|
|
19
|
+
image.resize({ w: targetPixelWidth });
|
|
20
|
+
|
|
21
|
+
const { width, height } = image.bitmap;
|
|
22
|
+
const lines: string[] = [];
|
|
23
|
+
|
|
24
|
+
for (let y = 0; y < height; y += 4) {
|
|
25
|
+
let line = "";
|
|
26
|
+
|
|
27
|
+
for (let x = 0; x < width; x += 2) {
|
|
28
|
+
let pattern = 0;
|
|
29
|
+
let rSum = 0;
|
|
30
|
+
let gSum = 0;
|
|
31
|
+
let bSum = 0;
|
|
32
|
+
let onCount = 0;
|
|
33
|
+
|
|
34
|
+
for (let row = 0; row < 4; row++) {
|
|
35
|
+
for (let col = 0; col < 2; col++) {
|
|
36
|
+
const px = x + col;
|
|
37
|
+
const py = y + row;
|
|
38
|
+
if (px >= width || py >= height) continue;
|
|
39
|
+
|
|
40
|
+
const { r, g, b, a } = intToRGBA(image.getPixelColor(px, py));
|
|
41
|
+
const brightness = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
42
|
+
|
|
43
|
+
if (a > 20 && brightness > 30) {
|
|
44
|
+
pattern |= BRAILLE_MAP[row][col];
|
|
45
|
+
rSum += r;
|
|
46
|
+
gSum += g;
|
|
47
|
+
bSum += b;
|
|
48
|
+
onCount++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (onCount > 0) {
|
|
54
|
+
const ch = String.fromCharCode(BRAILLE_BASE + pattern);
|
|
55
|
+
const avgR = Math.round(rSum / onCount);
|
|
56
|
+
const avgG = Math.round(gSum / onCount);
|
|
57
|
+
const avgB = Math.round(bSum / onCount);
|
|
58
|
+
line += chalk.rgb(avgR, avgG, avgB)(ch);
|
|
59
|
+
} else {
|
|
60
|
+
line += " ";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
lines.push(line);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
68
|
+
lines.pop();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return lines.join("\n");
|
|
72
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
3
5
|
import { Command } from "commander";
|
|
4
6
|
import { loadConfig, saveConfig, getDefaultConfig, ensureDirectories } from "../config/manager.js";
|
|
5
7
|
import { createProvider } from "../providers/index.js";
|
|
@@ -7,6 +9,21 @@ import { loadSkills } from "../skills/loader.js";
|
|
|
7
9
|
import { createConversation } from "../history/store.js";
|
|
8
10
|
import { startTUI } from "../tui/app.js";
|
|
9
11
|
import { setupWizard } from "./setup.js";
|
|
12
|
+
import { renderBrailleLogo } from "./braille-logo.js";
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
async function displayLogo(): Promise<void> {
|
|
17
|
+
try {
|
|
18
|
+
const logoPath = join(__dirname, "..", "..", "Logo2.png");
|
|
19
|
+
const maxWidth = Math.min(process.stdout.columns || 80, 80);
|
|
20
|
+
const art = await renderBrailleLogo(logoPath, maxWidth);
|
|
21
|
+
console.log(art);
|
|
22
|
+
console.log("");
|
|
23
|
+
} catch {
|
|
24
|
+
// Logo display is non-critical; silently skip on failure
|
|
25
|
+
}
|
|
26
|
+
}
|
|
10
27
|
|
|
11
28
|
const program = new Command();
|
|
12
29
|
|
|
@@ -70,6 +87,7 @@ program
|
|
|
70
87
|
const skills = await loadSkills();
|
|
71
88
|
const conversation = createConversation();
|
|
72
89
|
|
|
90
|
+
await displayLogo();
|
|
73
91
|
startTUI(provider, conversation, skills);
|
|
74
92
|
} catch (err: unknown) {
|
|
75
93
|
const msg = err instanceof Error ? err.message : String(err);
|
package/src/tui/app.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { markedTerminal } from "marked-terminal";
|
|
|
8
8
|
import type { ChatProvider } from "../providers/base.js";
|
|
9
9
|
import type { ConversationHistory, Message, Skill } from "../config/types.js";
|
|
10
10
|
import { runAgent } from "../agent/base.js";
|
|
11
|
+
|
|
11
12
|
import {
|
|
12
13
|
createConversation,
|
|
13
14
|
listConversations,
|
|
@@ -15,6 +16,9 @@ import {
|
|
|
15
16
|
appendMessage,
|
|
16
17
|
} from "../history/store.js";
|
|
17
18
|
|
|
19
|
+
const BRAND_BLUE = "#2BADEE";
|
|
20
|
+
const BRAND_RED = "#EA3A23";
|
|
21
|
+
|
|
18
22
|
function renderMarkdown(content: string): string {
|
|
19
23
|
const width = Math.max((process.stdout.columns ?? 80) - 8, 40);
|
|
20
24
|
const md = new Marked({ gfm: true });
|
|
@@ -56,7 +60,7 @@ function HeaderBlock({ columns }: { columns: number }): React.ReactElement {
|
|
|
56
60
|
return (
|
|
57
61
|
<Box flexDirection="column" paddingTop={1} width={columns}>
|
|
58
62
|
<Box paddingX={2}>
|
|
59
|
-
<Gradient
|
|
63
|
+
<Gradient colors={[BRAND_RED, BRAND_BLUE]}>
|
|
60
64
|
<Text bold>{"✦ CasAbot"}</Text>
|
|
61
65
|
</Gradient>
|
|
62
66
|
</Box>
|
|
@@ -73,7 +77,7 @@ function HeaderBlock({ columns }: { columns: number }): React.ReactElement {
|
|
|
73
77
|
function UserMessageView({ content, columns }: { content: string; columns: number }): React.ReactElement {
|
|
74
78
|
return (
|
|
75
79
|
<Box flexDirection="column" paddingX={2} marginTop={1} width={columns}>
|
|
76
|
-
<Text color=
|
|
80
|
+
<Text color={BRAND_RED} bold>
|
|
77
81
|
{"▶ You"}
|
|
78
82
|
</Text>
|
|
79
83
|
<Box marginLeft={2} width={Math.max(columns - 6, 10)}>
|
|
@@ -92,7 +96,7 @@ function AssistantMessageView({
|
|
|
92
96
|
}): React.ReactElement {
|
|
93
97
|
return (
|
|
94
98
|
<Box flexDirection="column" paddingX={2} marginTop={1} width={columns}>
|
|
95
|
-
<Text color=
|
|
99
|
+
<Text color={BRAND_BLUE} bold>
|
|
96
100
|
{"✦ CasAbot"}
|
|
97
101
|
</Text>
|
|
98
102
|
<Box marginLeft={2} width={Math.max(columns - 6, 10)}>
|
|
@@ -112,7 +116,7 @@ function ToolCallsView({
|
|
|
112
116
|
const boxWidth = Math.max(columns - 6, 10);
|
|
113
117
|
return (
|
|
114
118
|
<Box flexDirection="column" paddingX={2} marginTop={1} width={columns}>
|
|
115
|
-
<Text color=
|
|
119
|
+
<Text color={BRAND_BLUE} bold>
|
|
116
120
|
{"✦ CasAbot"}
|
|
117
121
|
</Text>
|
|
118
122
|
{message.content ? (
|
|
@@ -125,12 +129,12 @@ function ToolCallsView({
|
|
|
125
129
|
marginLeft={2}
|
|
126
130
|
marginTop={1}
|
|
127
131
|
borderStyle="round"
|
|
128
|
-
borderColor=
|
|
132
|
+
borderColor={BRAND_RED}
|
|
129
133
|
paddingX={1}
|
|
130
134
|
width={boxWidth}
|
|
131
135
|
overflow="hidden"
|
|
132
136
|
>
|
|
133
|
-
<Text color=
|
|
137
|
+
<Text color={BRAND_RED} bold>
|
|
134
138
|
{"⚡ Tool Calls"}
|
|
135
139
|
</Text>
|
|
136
140
|
{message.toolCalls?.map((tc, i) => {
|
|
@@ -219,10 +223,10 @@ function WelcomeHint({ columns }: { columns: number }): React.ReactElement {
|
|
|
219
223
|
function ProcessingIndicator({ columns }: { columns: number }): React.ReactElement {
|
|
220
224
|
return (
|
|
221
225
|
<Box paddingX={2} marginTop={1} gap={1} width={columns}>
|
|
222
|
-
<Text color=
|
|
226
|
+
<Text color={BRAND_BLUE}>
|
|
223
227
|
<Spinner type="dots" />
|
|
224
228
|
</Text>
|
|
225
|
-
<Text color=
|
|
229
|
+
<Text color={BRAND_BLUE}>{"Thinking…"}</Text>
|
|
226
230
|
</Box>
|
|
227
231
|
);
|
|
228
232
|
}
|
|
@@ -277,7 +281,7 @@ function HistoryBrowser({
|
|
|
277
281
|
return (
|
|
278
282
|
<Box flexDirection="column" paddingTop={1} width={columns}>
|
|
279
283
|
<Box paddingX={2}>
|
|
280
|
-
<Gradient
|
|
284
|
+
<Gradient colors={[BRAND_RED, BRAND_BLUE]}>
|
|
281
285
|
<Text bold>{"📋 Session History"}</Text>
|
|
282
286
|
</Gradient>
|
|
283
287
|
</Box>
|
|
@@ -286,10 +290,10 @@ function HistoryBrowser({
|
|
|
286
290
|
|
|
287
291
|
{isLoading ? (
|
|
288
292
|
<Box paddingX={2} marginTop={1} gap={1}>
|
|
289
|
-
<Text color=
|
|
293
|
+
<Text color={BRAND_BLUE}>
|
|
290
294
|
<Spinner type="dots" />
|
|
291
295
|
</Text>
|
|
292
|
-
<Text color=
|
|
296
|
+
<Text color={BRAND_BLUE}>{"Loading sessions…"}</Text>
|
|
293
297
|
</Box>
|
|
294
298
|
) : conversations.length === 0 ? (
|
|
295
299
|
<Box paddingX={2} marginTop={1}>
|
|
@@ -317,14 +321,14 @@ function HistoryBrowser({
|
|
|
317
321
|
return (
|
|
318
322
|
<Box key={conv.id} width={boxWidth - 4}>
|
|
319
323
|
<Text
|
|
320
|
-
color={isSelected ?
|
|
324
|
+
color={isSelected ? BRAND_BLUE : undefined}
|
|
321
325
|
bold={isSelected}
|
|
322
326
|
dimColor={!isSelected}
|
|
323
327
|
>
|
|
324
328
|
{isSelected ? " ▶ " : " "}
|
|
325
329
|
</Text>
|
|
326
330
|
<Text
|
|
327
|
-
color={isSelected ?
|
|
331
|
+
color={isSelected ? BRAND_BLUE : undefined}
|
|
328
332
|
bold={isSelected}
|
|
329
333
|
dimColor={!isSelected}
|
|
330
334
|
>
|
|
@@ -339,7 +343,7 @@ function HistoryBrowser({
|
|
|
339
343
|
{preview}
|
|
340
344
|
</Text>
|
|
341
345
|
{isCurrent ? (
|
|
342
|
-
<Text color=
|
|
346
|
+
<Text color={BRAND_RED} bold>{" (current)"}</Text>
|
|
343
347
|
) : null}
|
|
344
348
|
<Text dimColor>
|
|
345
349
|
{` [${msgCount}]`}
|
|
@@ -545,12 +549,12 @@ function App({
|
|
|
545
549
|
<Box paddingX={1} width={columns}>
|
|
546
550
|
<Box
|
|
547
551
|
borderStyle="round"
|
|
548
|
-
borderColor={isProcessing ? "gray" :
|
|
552
|
+
borderColor={isProcessing ? "gray" : BRAND_BLUE}
|
|
549
553
|
paddingX={1}
|
|
550
554
|
width={Math.max(columns - 2, 10)}
|
|
551
555
|
overflow="hidden"
|
|
552
556
|
>
|
|
553
|
-
<Text color=
|
|
557
|
+
<Text color={BRAND_BLUE} bold>
|
|
554
558
|
{"❯ "}
|
|
555
559
|
</Text>
|
|
556
560
|
<TextInput
|