lazyclaw 5.0.2 → 5.0.4
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/cli.mjs +23 -23
- package/package.json +1 -1
- package/tui/banner.generated.mjs +41 -13
- package/tui/splash.mjs +32 -31
package/cli.mjs
CHANGED
|
@@ -1634,31 +1634,31 @@ function _renderBanner(version) {
|
|
|
1634
1634
|
];
|
|
1635
1635
|
}
|
|
1636
1636
|
|
|
1637
|
-
// v5
|
|
1638
|
-
//
|
|
1639
|
-
// chat splash
|
|
1640
|
-
// to fall back to the
|
|
1641
|
-
let
|
|
1642
|
-
async function
|
|
1643
|
-
if (
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
}
|
|
1637
|
+
// v5 hero banner — the chafa-rendered braille sloth shared with the chat
|
|
1638
|
+
// splash (tui/banner.generated.mjs). Centered in TERM_WIDTH at runtime so
|
|
1639
|
+
// the launcher and chat splash present identical visual identity.
|
|
1640
|
+
// Opt out with LAZYCLAW_LEGACY_MENU=1 to fall back to the v4 figlet box.
|
|
1641
|
+
let _slothBannerRowsCache = null;
|
|
1642
|
+
async function _loadSlothBanner() {
|
|
1643
|
+
if (_slothBannerRowsCache !== null) return _slothBannerRowsCache;
|
|
1644
|
+
try {
|
|
1645
|
+
const { banner } = await import('./tui/banner.generated.mjs');
|
|
1646
|
+
_slothBannerRowsCache = banner;
|
|
1647
|
+
} catch {
|
|
1648
|
+
_slothBannerRowsCache = null;
|
|
1650
1649
|
}
|
|
1651
|
-
|
|
1650
|
+
return _slothBannerRowsCache;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
async function _renderV5Banner(version) {
|
|
1654
|
+
const b = await _loadSlothBanner();
|
|
1655
|
+
if (!b) return _renderBanner(version); // missing tarball asset → v4 figlet
|
|
1656
|
+
const TERM_W = Math.max(80, process.stdout.columns || 80);
|
|
1657
|
+
const pad = Math.max(0, Math.floor((TERM_W - b.width) / 2));
|
|
1658
|
+
const rows = b.rows.map(r => _orange(' '.repeat(pad) + r));
|
|
1652
1659
|
const v = String(version || '?.?.?');
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
// Caption row — overlay "lazyclaw v<version>" centered on the 24-wide art
|
|
1656
|
-
const cap = `lazyclaw v${v}`;
|
|
1657
|
-
const pad = Math.max(0, Math.floor((24 - cap.length) / 2));
|
|
1658
|
-
return _orange(' '.repeat(pad) + cap + ' '.repeat(24 - pad - cap.length));
|
|
1659
|
-
}
|
|
1660
|
-
return _orange(row);
|
|
1661
|
-
});
|
|
1660
|
+
rows.push(_orange((' '.repeat(pad) + `lazyclaw v${v}`).padEnd(TERM_W)));
|
|
1661
|
+
return rows;
|
|
1662
1662
|
}
|
|
1663
1663
|
|
|
1664
1664
|
function _printChatBanner(activeProvName, activeModel, version) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazyclaw",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "Lazy, elegant terminal CLI for chatting with Claude / OpenAI / Gemini / Ollama, orchestrating multi-step LLM workflows, and running multi-agent Slack teams with cross-task memory. Banner-on-launch, slash-command ghost autocomplete, persistent sessions, local HTTP gateway.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
package/tui/banner.generated.mjs
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Auto-generated by `chafa --format=symbols --symbols=braille --invert
|
|
2
|
+
// --size=48x35 --fg-only docs/assets/sleepy-sloth-source.png`. Source:
|
|
3
|
+
// Pexels three-toed sloth by Tomas Malik (free license). Style is the
|
|
4
|
+
// dense inverted braille that user signed off on in v5.0.4 review.
|
|
2
5
|
export const banner = {
|
|
3
6
|
rows: [
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
7
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀ ⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
8
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
9
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣏⠉⠉⠉⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
10
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ ⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
11
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡄ ⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
12
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ ⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
13
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁ ⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
14
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
15
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
16
|
+
"⣯⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
17
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
18
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
19
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
20
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
21
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
22
|
+
" ⠁ ⠚⠒⣚⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣿⠟⠻⠭⠻⢻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
23
|
+
" ⢀⣠⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣺⡋⡂⡺⠿⢋⣷⣲⣰⢀⣤⣴⣬⣏⣽⡮⠟⢿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
24
|
+
"⣤⣴⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣽⢶⠈ ⢈⠿⠮⠭⢿⠕⡼⣿⠿⡚⣯⡙⠚⣚⢽⣿⣿⣿⣿⣿⣿⣿",
|
|
25
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣼⣷⡒⢉⣽⣿⣾⡜⡀⡀⡐⠁⣑⢯⡜⠃⣀⡿⡿⣿⣿⣿⣿⣿⣿",
|
|
26
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣿⣿⣌⢻⠝⣞⠞⣒⣲⡤⣾⣽⡕⣘⣾⣿⣿⣿⣿⣿",
|
|
27
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⡿⣿⣿⣿⣿⣿⣿⣄⣂⢸⣿⣗⢿⣺⣳⠶⢤⣶⠏⠁⠴⢛⣻⣿⣿⣿⣿",
|
|
28
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣠⣀⡀ ⢉⣿⣿⣿⣿⣿⡅⣿⣿⡕⠋⠠⡕⢕⠟⢀⠈ ⢐⡱⣿⣿⣿⣿⣿",
|
|
29
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣶⣿⣿⣿⣿⣿⣜⣿⣯⡃⢢⠴⠰⠘⡂⠾⠆⣀ ⢙⣽⣿⣟⣿",
|
|
30
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣽⣿⣪⡫⠊⠄ ⡈⢉⣛⣿",
|
|
31
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⣟⣻⣟⣻⡿ ⠛⣺⡡⣔⠋ ⠈⣽⣿",
|
|
32
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡅⡖⢮⣺⣷⣴⣧⣀⣙⣧⣮⠦ ⠨⣷⣿",
|
|
33
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡷⢎⢶⡹⡝⡯⢮⡛⠼⠟⢙⡳⢡⣵⣾⣥⣾⣿",
|
|
34
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⠿⢷⣿⣷⢻⠱⢳⣮⡜⣰⣻⣷⣴⣻⣽⣿⣿⣿⣿",
|
|
35
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣗⡃⠺⠿⠗⢿⣧⠗⢑⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
36
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢽⣟⡦⢂⣀⣀⣠⣾⣿⣿⣿⣿⣿⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
37
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣯⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣂⡽⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣟⣿⣿⣿⣿⣿",
|
|
38
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢹⣻⢻⡿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣾⣿⣿⡿⣿",
|
|
39
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣿⣿⣾⣷⣶⣯⣭⣛⣻⠿⣿⣷⣿⣿⣿⣿⣿⣿⣿⡿⠿⡿⣟",
|
|
40
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⣶⣶⣶⣷⣶⣿⣶⣶⣾⣿⣿⣿",
|
|
41
|
+
"⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣯⣷⣟⣀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿",
|
|
14
42
|
],
|
|
15
|
-
width:
|
|
16
|
-
height:
|
|
43
|
+
width: 47,
|
|
44
|
+
height: 35,
|
|
17
45
|
fg: "#FFB347"
|
|
18
46
|
};
|
package/tui/splash.mjs
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
// tui/splash.mjs —
|
|
1
|
+
// tui/splash.mjs — hero-banner launch splash.
|
|
2
2
|
//
|
|
3
3
|
// Public surface:
|
|
4
4
|
// - <Splash {...props} /> ink component for live REPL mount
|
|
5
5
|
// - renderSplashToString(props) pure string builder used by tests
|
|
6
6
|
// and by the non-TTY path.
|
|
7
7
|
//
|
|
8
|
-
// Layout:
|
|
9
|
-
//
|
|
10
|
-
//
|
|
8
|
+
// Layout: 47-cell-wide hero banner centered in 80-col terminal,
|
|
9
|
+
// followed by a two-column tools|skills section (36 cells each)
|
|
10
|
+
// and a 4-line footer.
|
|
11
11
|
import React from 'react';
|
|
12
12
|
import { Box, Text } from 'ink';
|
|
13
13
|
import stringWidth from 'string-width';
|
|
14
14
|
import { theme } from './theme.mjs';
|
|
15
15
|
import { banner } from './banner.generated.mjs';
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const TERM_WIDTH = 80;
|
|
18
|
+
const COL_WIDTH = 36; // 2 (lpad) + 36 + 2 (mid) + 36 + 2 (rpad) = 78 ≈ 80 with slack
|
|
19
19
|
|
|
20
20
|
function fit(text, max) {
|
|
21
21
|
if (stringWidth(text) <= max) return text.padEnd(max);
|
|
@@ -48,29 +48,26 @@ function skillRow({ group, names }) {
|
|
|
48
48
|
return `${fit(group, 9)}${tail}${more}`;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (tools.length > 8) right.push(`... and ${tools.length - 8} more tool groups`);
|
|
58
|
-
right.push('');
|
|
59
|
-
right.push('Available Skills');
|
|
60
|
-
right.push('─'.repeat(45));
|
|
61
|
-
for (const s of skills.slice(0, 8)) right.push(skillRow(s));
|
|
62
|
-
if (skills.length > 8) right.push(`... and ${skills.length - 8} more skill groups`);
|
|
63
|
-
|
|
64
|
-
const left = banner.rows.slice();
|
|
65
|
-
while (left.length < right.length) left.push('');
|
|
66
|
-
while (right.length < left.length) right.push('');
|
|
51
|
+
function buildBanner() {
|
|
52
|
+
// Center the hero banner inside TERM_WIDTH. banner.width may be < TERM_WIDTH;
|
|
53
|
+
// pad each side equally so it floats over the centerline.
|
|
54
|
+
const pad = Math.max(0, Math.floor((TERM_WIDTH - banner.width) / 2));
|
|
55
|
+
return banner.rows.map(row => ' '.repeat(pad) + row);
|
|
56
|
+
}
|
|
67
57
|
|
|
58
|
+
function buildToolsAndSkills(props) {
|
|
59
|
+
const { tools = [], skills = [] } = props;
|
|
60
|
+
const SECT_WIDTH = TERM_WIDTH - 4;
|
|
68
61
|
const lines = [];
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
lines.push(` ${fit('Available Tools', SECT_WIDTH)}`);
|
|
63
|
+
lines.push(` ${'─'.repeat(SECT_WIDTH)}`);
|
|
64
|
+
for (const t of tools.slice(0, 8)) lines.push(` ${fit(toolRow(t), SECT_WIDTH)}`);
|
|
65
|
+
if (tools.length > 8) lines.push(` ${fit(`... and ${tools.length - 8} more tool groups`, SECT_WIDTH)}`);
|
|
66
|
+
lines.push('');
|
|
67
|
+
lines.push(` ${fit('Available Skills', SECT_WIDTH)}`);
|
|
68
|
+
lines.push(` ${'─'.repeat(SECT_WIDTH)}`);
|
|
69
|
+
for (const s of skills.slice(0, 8)) lines.push(` ${fit(skillRow(s), SECT_WIDTH)}`);
|
|
70
|
+
if (skills.length > 8) lines.push(` ${fit(`... and ${skills.length - 8} more skill groups`, SECT_WIDTH)}`);
|
|
74
71
|
return lines;
|
|
75
72
|
}
|
|
76
73
|
|
|
@@ -89,10 +86,14 @@ function buildFooter(props) {
|
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
export function renderSplashToString(props, { columns = 80 } = {}) {
|
|
92
|
-
void columns; //
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
void columns; // splash is fixed-width 80
|
|
90
|
+
return [
|
|
91
|
+
...buildBanner(),
|
|
92
|
+
'',
|
|
93
|
+
...buildToolsAndSkills(props),
|
|
94
|
+
'',
|
|
95
|
+
...buildFooter(props),
|
|
96
|
+
].join('\n');
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
export function Splash(props) {
|