lazyclaw 5.0.8 → 5.0.9

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 CHANGED
@@ -1658,7 +1658,17 @@ async function _renderV5Banner(version) {
1658
1658
  if (!a) return _renderBanner(version); // missing tarball asset → v4 figlet
1659
1659
  const v = String(version || '?.?.?');
1660
1660
  const rows = [];
1661
- for (const r of a.wordmark.rows) rows.push(_orange(' ' + r));
1661
+ const palette = a.wordmark.palette || [];
1662
+ const gradient = a.wordmark.gradient || [];
1663
+ function tint(idx, s) {
1664
+ const hex = palette[idx] || '#FFB347';
1665
+ const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
1666
+ if (!m) return _orange(s);
1667
+ const [, r, g, b] = m;
1668
+ const R = parseInt(r, 16), G = parseInt(g, 16), B = parseInt(b, 16);
1669
+ return `\x1b[38;2;${R};${G};${B}m${s}\x1b[0m`;
1670
+ }
1671
+ a.wordmark.rows.forEach((r, i) => rows.push(tint(gradient[i] ?? 1, ' ' + r)));
1662
1672
  rows.push('');
1663
1673
  for (const r of a.banner.rows) rows.push(_orange(' ' + r));
1664
1674
  rows.push(_orange(' ' + `lazyclaw v${v}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazyclaw",
3
- "version": "5.0.8",
3
+ "version": "5.0.9",
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/splash.mjs CHANGED
@@ -1,23 +1,29 @@
1
- // tui/splash.mjs — Hermes-style hero splash.
1
+ // tui/splash.mjs — Hermes-style hero splash with gradient wordmark,
2
+ // subcommand catalog, tool registry, skill index, and a bottom status bar.
2
3
  //
3
4
  // Layout (terminal-width responsive):
4
5
  //
5
- // ██╗ █████╗ ... ← wordmark (top, single-tone orange)
6
- // ...
6
+ // LAZYCLAW wordmark (top, per-row gradient orange)
7
7
  //
8
- // ╭───── lazyclaw vX.Y.Z · trainer-split + FTS5 recall ─────────────╮
9
- // │ [sloth braille] Available Tools
10
- // │ fs read · write · ...
11
- // │ Available Skills
12
- // │ N tools · M skills · /help for commands
13
- // ╰──────────────────────────────────────────────────────────────────╯
8
+ // ╭─ lazyclaw vX.Y.Z · trainer-split · FTS5 recall · 6-backend sandbox ─╮
9
+ // │ [sloth braille] Subcommands
10
+ // │ core / workflow / config / state / ...
11
+ // │ Available Tools
12
+ // │ fs / exec / git / ...
13
+ // │ Available Skills │
14
+ // │ N subcommands · M tool groups · K skills · /help │
15
+ // ╰──────────────────────────────────────────────────────────────────────╯
14
16
  //
15
- // provider · X · Y trainer · A · B
16
- // /Users/o/cwd
17
- // Session: 20260605_180543_2e0351
17
+ // provider · X · Y · trainer · A · B
18
+ // /cwd
19
+ // Session: ...
18
20
  //
19
21
  // Welcome to lazyclaw. Type your message or /help for commands.
20
22
  // + Tip: ...
23
+ //
24
+ // ───────────────────────────────────────────────────────────────────────
25
+ // provider · model | ctx -- | [progress] | 0s
26
+ // ───────────────────────────────────────────────────────────────────────
21
27
  import React from 'react';
22
28
  import { Box, Text } from 'ink';
23
29
  import stringWidth from 'string-width';
@@ -28,6 +34,19 @@ import { wordmark } from './wordmark.mjs';
28
34
  const LMARGIN = ' ';
29
35
  const TITLE = ' trainer-split · FTS5 recall · 6-backend sandbox ';
30
36
 
37
+ // Subcommand catalog — grouped for the splash so a new user sees the
38
+ // surface area at a glance. Mirrors SUBCOMMANDS in cli.mjs.
39
+ export const SUBCOMMAND_GROUPS = [
40
+ ['core', ['chat', 'agent', 'orchestrator', 'dashboard', 'menu']],
41
+ ['workflow', ['run', 'resume', 'inspect', 'clear', 'validate', 'graph']],
42
+ ['config', ['config', 'auth', 'rates', 'providers', 'setup', 'onboard']],
43
+ ['state', ['sessions', 'skills', 'workspace', 'memory', 'status', 'doctor']],
44
+ ['runtime', ['daemon', 'cron', 'loop', 'loops', 'goal']],
45
+ ['channels', ['slack', 'telegram', 'matrix', 'channels', 'message', 'pairing']],
46
+ ['v5', ['sandbox', 'personality', 'migrate', 'hermes', 'openclaw', 'trajectories']],
47
+ ['utility', ['browse', 'version', 'completion', 'help', 'export', 'import', 'nodes']],
48
+ ];
49
+
31
50
  function fit(text, max) {
32
51
  if (stringWidth(text) <= max) return text.padEnd(max);
33
52
  let lo = 0, hi = text.length;
@@ -56,38 +75,49 @@ function skillRow({ group, names }) {
56
75
  return `${group.padEnd(12)} ${tail}`;
57
76
  }
58
77
 
78
+ function subcommandRow([label, verbs]) {
79
+ return `${label.padEnd(12)} ${verbs.join(' · ')}`;
80
+ }
81
+
59
82
  export function renderSplashToString(props, opts = {}) {
60
83
  const cols = opts.columns || process.stdout.columns || 100;
61
84
  const TERM = Math.max(80, cols);
62
85
  const PANEL_W = TERM - LMARGIN.length * 2;
63
- const INNER = PANEL_W - 4; // 2 border + 2 padding
86
+ const INNER = PANEL_W - 4;
64
87
  const SLOTH_W = banner.width;
65
88
  const RIGHT_W = Math.max(40, INNER - SLOTH_W - 2);
66
89
 
67
90
  const lines = [];
68
91
 
69
- // Wordmark
92
+ // 1) wordmark
70
93
  for (const r of wordmark.rows) lines.push(LMARGIN + r);
71
94
  lines.push('');
72
95
 
73
- // Panel top with title
96
+ // 2) panel top with inset title
74
97
  const versionLabel = ` lazyclaw ${props.version || ''} ·${TITLE} `;
75
98
  const dashLeft = '─'.repeat(8);
76
99
  const dashRight = '─'.repeat(Math.max(2, PANEL_W - 2 - dashLeft.length - stringWidth(versionLabel)));
77
100
  lines.push(`${LMARGIN}╭${dashLeft}${versionLabel}${dashRight}╮`);
78
101
 
79
- // Right column content
102
+ // 3) right column content (subcommands + tools + skills)
80
103
  const { tools = [], skills = [] } = props;
81
104
  const right = [];
105
+ right.push('Subcommands');
106
+ for (const g of SUBCOMMAND_GROUPS) right.push(subcommandRow(g));
107
+ right.push('');
82
108
  right.push('Available Tools');
83
- for (const t of tools.slice(0, 8)) right.push(toolRow(t));
84
- if (tools.length > 8) right.push(`(and ${tools.length - 8} more tool groups...)`);
109
+ for (const t of tools.slice(0, 14)) right.push(toolRow(t));
110
+ if (tools.length > 14) right.push(`(and ${tools.length - 14} more...)`);
85
111
  right.push('');
86
112
  right.push('Available Skills');
87
- for (const s of skills.slice(0, 8)) right.push(skillRow(s));
88
- if (skills.length > 8) right.push(`(and ${skills.length - 8} more skill groups...)`);
113
+ if (skills.length === 0) right.push('(none installed)');
114
+ else {
115
+ for (const s of skills.slice(0, 8)) right.push(skillRow(s));
116
+ if (skills.length > 8) right.push(`(and ${skills.length - 8} more skill groups...)`);
117
+ }
89
118
  right.push('');
90
- right.push(`${tools.length} tools · ${skills.length} skills · /help for commands`);
119
+ const subcmdCount = SUBCOMMAND_GROUPS.reduce((n, [, v]) => n + v.length, 0);
120
+ right.push(`${subcmdCount} subcommands · ${tools.length} tool groups · ${skills.length} skills · /help for commands`);
91
121
 
92
122
  const sloth = banner.rows.slice();
93
123
  while (sloth.length < right.length) sloth.push(' '.repeat(SLOTH_W));
@@ -101,7 +131,7 @@ export function renderSplashToString(props, opts = {}) {
101
131
  lines.push(`${LMARGIN}╰${'─'.repeat(PANEL_W - 2)}╯`);
102
132
  lines.push('');
103
133
 
104
- // Provider / session info
134
+ // 4) provider / session info
105
135
  const { provider, model, trainer = {}, sessionId, cwd } = props;
106
136
  const tProv = trainer.provider || provider;
107
137
  const tModel = trainer.model || model;
@@ -111,19 +141,37 @@ export function renderSplashToString(props, opts = {}) {
111
141
  lines.push('');
112
142
  lines.push(`${LMARGIN}Welcome to lazyclaw. Type your message or /help for commands.`);
113
143
  lines.push(`${LMARGIN}+ Tip: trainer learns from your Claude Pro subscription at $0.`);
144
+ lines.push('');
145
+
146
+ // 5) bottom border + status bar + border (Hermes-style separator)
147
+ const sep = '─'.repeat(PANEL_W);
148
+ lines.push(LMARGIN + sep);
149
+ const ctx = props.ctxUsed != null && props.ctxTotal != null
150
+ ? `[${'░'.repeat(20)}] ${props.ctxUsed}/${props.ctxTotal}`
151
+ : `[${'░'.repeat(20)}] --`;
152
+ lines.push(`${LMARGIN} ${provider} · ${model} | ctx -- | ${ctx} | 0s`);
153
+ lines.push(LMARGIN + sep);
114
154
 
115
155
  return lines.join('\n');
116
156
  }
117
157
 
118
158
  export function Splash(props) {
119
- const lines = renderSplashToString(props).split('\n');
120
- // Color the wordmark and sloth rows; everything else stays default.
121
- const heroRowCount = wordmark.height + 1 + 1 + banner.height + 1; // word + blank + top + sloth + bottom
159
+ const cols = process.stdout.columns || 100;
160
+ const TERM = Math.max(80, cols);
161
+ const PANEL_W = TERM - LMARGIN.length * 2;
162
+ const out = renderSplashToString(props, { columns: cols });
163
+ const lines = out.split('\n');
164
+ const palette = wordmark.palette;
165
+ const gradient = wordmark.gradient;
166
+
122
167
  return React.createElement(
123
168
  Box,
124
169
  { flexDirection: 'column' },
125
- lines.map((line, i) =>
126
- React.createElement(Text, { key: i, color: i < heroRowCount ? theme.fg : undefined }, line)
127
- )
170
+ lines.map((line, i) => {
171
+ let color;
172
+ if (i < wordmark.height) color = palette[gradient[i] ?? 1];
173
+ else if (i < wordmark.height + 1 + 1 + banner.height) color = theme.fg; // panel + sloth tinted
174
+ return React.createElement(Text, { key: i, color }, line);
175
+ })
128
176
  );
129
177
  }
package/tui/wordmark.mjs CHANGED
@@ -1,5 +1,21 @@
1
- // User-supplied "Larry 3D"-style LAZYCLAW wordmark — 13 rows × 120 cols.
2
- // Replaces the v5.0.6 ANSI Shadow version on operator request.
1
+ // "Larry 3D"-style LAZYCLAW wordmark — 13 rows × 120 cols, shared between
2
+ // chat splash and launcher. Gradient palette maps each row to one of four
3
+ // warm-orange shades; top rows = brightest, bottom rows = shadow-dark.
4
+ //
5
+ // The string-output renderer (renderSplashToString) emits the raw rows;
6
+ // the ink/launcher color path picks PALETTE[GRADIENT[row]] when ANSI is
7
+ // available.
8
+
9
+ export const PALETTE = [
10
+ '#FFD580', // 0 top — warm white-gold
11
+ '#FFB347', // 1 highlight (current single-tone reference)
12
+ '#E08020', // 2 midtone — amber
13
+ '#A05010', // 3 bottom — burnt shadow
14
+ ];
15
+
16
+ // 13 rows; bias toward bottom for shadow depth.
17
+ export const GRADIENT = [0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3];
18
+
3
19
  export const wordmark = {
4
20
  rows: [
5
21
  " ____ ____ _____ _____ _____ _____ ____ ____ _____ ",
@@ -18,5 +34,7 @@ export const wordmark = {
18
34
  ],
19
35
  width: 120,
20
36
  height: 13,
21
- fg: "#FFB347"
37
+ fg: "#FFB347",
38
+ gradient: GRADIENT,
39
+ palette: PALETTE,
22
40
  };