@baimingtao/lbs-agent 1.3.0 → 1.4.0

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/bin/lbs.js CHANGED
@@ -46,21 +46,21 @@ const INIT_SCRIPT = path.join(PKG_ROOT, 'scripts', 'init.js');
46
46
 
47
47
  // ── 颜色与图标 (对标 Hermes banner/colors) ─────────────────────
48
48
 
49
- // ── Sakura × Mint 配色 (对标 Web 端 tailwind.config.js) ───────
49
+ // ── Aurora Pink 配色 ───────────────────────────────────────────
50
50
 
51
51
  const C = {
52
52
  reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', italic: '\x1b[3m',
53
- // 品牌色
54
- sakura: '\x1b[38;2;255;183;197m', // #FFB7C5
55
- sakuraDark: '\x1b[38;2;232;154;168m', // #E89AA8
56
- mint: '\x1b[38;2;152;216;200m', // #98D8C8
57
- mintDark: '\x1b[38;2;111;196;176m', // #6FC4B0
58
- // 功能色
59
- violet: '\x1b[38;2;110;90;168m', // #6E5AA8
60
- honey: '\x1b[38;2;255;184;107m', // #FFB86B
61
- green: '\x1b[38;2;69;214;163m', // #45D6A3
62
- red: '\x1b[38;2;255;107;129m', // #FF6B81
63
- blue: '\x1b[38;2;101;168;255m', // #65A8FF
53
+ pink: '\x1b[38;2;255;79;164m', // #FF4FA4
54
+ pinkL: '\x1b[38;2;255;167;213m', // #FFA7D5
55
+ purple: '\x1b[38;2;194;107;255m', // #C26BFF
56
+ blue: '\x1b[38;2;110;139;255m', // #6E8BFF
57
+ mint: '\x1b[38;2;152;216;200m', // #98D8C8
58
+ mintD: '\x1b[38;2;111;196;176m', // #6FC4B0
59
+ ok: '\x1b[38;2;62;229;138m', // #3EE58A
60
+ warn: '\x1b[38;2;255;179;71m', // #FFB347
61
+ err: '\x1b[38;2;255;107;129m', // #FF6B81
62
+ subtle: '\x1b[38;2;138;138;150m', // #8A8A96
63
+ faint: '\x1b[38;2;90;90;102m', // #5A5A66
64
64
  };
65
65
 
66
66
  function supportsColor() {
@@ -74,23 +74,36 @@ function paint(text, ...colors) {
74
74
  return colors.join('') + text + C.reset;
75
75
  }
76
76
 
77
- // ── ASCII Logo — Sakura 主题 ───────────────────────────────────
77
+ // 逐字符渐变
78
+ function gradient(text) {
79
+ if (!supportsColor()) return text;
80
+ const stops = [
81
+ '\x1b[38;2;255;79;164m', // pink
82
+ '\x1b[38;2;255;127;184m', // pink-pu
83
+ '\x1b[38;2;224;119;220m', // mid
84
+ '\x1b[38;2;194;107;255m', // purple
85
+ '\x1b[38;2;160;123;240m', // pu-blue
86
+ '\x1b[38;2;130;131;248m', // blue-ish
87
+ '\x1b[38;2;110;139;255m', // blue
88
+ ];
89
+ const n = text.length;
90
+ let out = '';
91
+ for (let i = 0; i < n; i++) {
92
+ if (text[i] === ' ') { out += ' '; continue; }
93
+ const idx = Math.min(Math.floor(i / Math.max(n, 1) * stops.length), stops.length - 1);
94
+ out += stops[idx] + text[i] + C.reset;
95
+ }
96
+ return out;
97
+ }
78
98
 
79
- const LOGO = [
80
- '',
81
- ' ' + paint('╔══╗', C.sakura) + ' ' + paint('╔═══╗', C.mint) + ' ' + paint('╔═════╗', C.mint),
82
- ' ' + paint('║ ║', C.sakura) + ' ' + paint('║ ║', C.mint) + ' ' + paint('║ ╔═╝', C.mint),
83
- ' ' + paint('╠══╣', C.sakura) + ' ' + paint('╠═══╣', C.mint) + ' ' + paint('║ ╚═╗', C.mint),
84
- ' ' + paint('║ ║', C.sakura) + ' ' + paint('║ ║', C.mint) + ' ' + paint('╚═════╝', C.mint),
85
- ' ' + paint('╚══╝', C.sakura) + ' ' + paint('╚═══╝', C.mint) + ' ' + paint('AGENT', C.dim),
86
- ].join('\n');
99
+ // ── Banner — 极简留白 ─────────────────────────────────────────
87
100
 
88
101
  function printBanner() {
89
- const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf-8'));
90
- console.log(LOGO);
91
- console.log(paint(' v' + pkg.version, C.bold, C.sakuraDark) +
92
- paint(' · ', C.sakura) +
93
- paint('Leaving Blank Space Agent', C.dim));
102
+ console.log();
103
+ console.log(' ' + paint('✦', C.pink));
104
+ console.log();
105
+ console.log(' ' + gradient('LBS Agent'));
106
+ console.log(' ' + paint('Autonomous AI Runtime', C.subtle));
94
107
  console.log();
95
108
  }
96
109
  // ── 初始化检查 ─────────────────────────────────────────────────
@@ -102,13 +115,13 @@ function isInitialized() {
102
115
  function ensureInitialized() {
103
116
  if (isInitialized()) return true;
104
117
 
105
- console.log(paint('⚙ ', C.sakura) + paint('首次运行,正在初始化 LBS Agent...', C.bold));
118
+ console.log(paint('⚙ ', C.pink) + paint('首次运行,正在初始化 LBS Agent...', C.bold));
106
119
  console.log(paint(' 状态目录: ', C.dim) + paint(STATE_ROOT, C.mint));
107
120
  console.log(paint(' 创建 Python 虚拟环境 + 安装依赖(约 1-2 分钟)', C.dim));
108
121
  console.log();
109
122
 
110
123
  if (!fs.existsSync(INIT_SCRIPT)) {
111
- console.error(paint('✖ ', C.red) + '初始化脚本不存在: ' + INIT_SCRIPT);
124
+ console.error(paint('✖ ', C.err) + '初始化脚本不存在: ' + INIT_SCRIPT);
112
125
  return false;
113
126
  }
114
127
 
@@ -119,11 +132,11 @@ function ensureInitialized() {
119
132
  });
120
133
 
121
134
  if (result.status !== 0) {
122
- console.error(paint('\n✖ ', C.red) + '初始化失败');
135
+ console.error(paint('\n✖ ', C.err) + '初始化失败');
123
136
  return false;
124
137
  }
125
138
 
126
- console.log(paint('\n✓ ', C.green) + paint('初始化完成!', C.bold, C.green));
139
+ console.log(paint('\n✓ ', C.ok) + paint('初始化完成!', C.bold, C.ok));
127
140
  console.log();
128
141
  return true;
129
142
  }
@@ -144,7 +157,7 @@ if (subcmd === 'init' || subcmd === '--init') {
144
157
  });
145
158
  process.exit(result.status || 0);
146
159
  }
147
- console.error(paint('✖ ', C.red) + '初始化脚本不存在');
160
+ console.error(paint('✖ ', C.err) + '初始化脚本不存在');
148
161
  process.exit(1);
149
162
  }
150
163
 
@@ -1,12 +1,11 @@
1
- """LBS CLI 主题系统Sakura × Mint (对标 Web 端 tailwind.config.js)。
2
-
3
- Web 端配色 → ANSI True Color 映射:
4
- sakura #FFB7C5 → 粉色 (品牌主色)
5
- mint #98D8C8 薄荷绿 (辅助色)
6
- berry #2B1828 深莓 (正文)
7
- violet #6E5AA8 → 紫色 (Agent)
8
- honey #FFB86B 蜂蜜 (警告)
9
- green #45D6A3 → 翠绿 (成功)
1
+ """LBS CLI ThemeAurora Pink 现代终端设计系统。
2
+
3
+ 设计原则:
4
+ 留白 > 密集
5
+ 符号 > 字符画
6
+ 渐变 > 纯色
7
+
8
+ 对标: Claude Code, Gemini CLI, Warp Terminal, Charm BubbleTea
10
9
  """
11
10
 
12
11
  from __future__ import annotations
@@ -15,51 +14,37 @@ import os
15
14
  import sys
16
15
 
17
16
 
18
- # ── ANSI True Color 码 (直接用 Web 端 hex) ─────────────────────
19
-
20
- class Theme:
21
- """LBS CLI 色彩主题,映射 Web 端 tailwind config。"""
22
-
23
- # 基础
24
- RESET = "\033[0m"
25
- BOLD = "\033[1m"
26
- DIM = "\033[2m"
27
- ITALIC = "\033[3m"
28
- UNDERLINE = "\033[4m"
29
-
30
- # 品牌色 (对标 tailwind)
31
- SAKURA = "\033[38;2;255;183;197m" # #FFB7C5
32
- SAKURA_LIGHT = "\033[38;2;255;212;222m" # #FFD4DE
33
- SAKURA_DARK = "\033[38;2;232;154;168m" # #E89AA8
34
- MINT = "\033[38;2;152;216;200m" # #98D8C8
35
- MINT_LIGHT = "\033[38;2;184;232;220m" # #B8E8DC
36
- MINT_DARK = "\033[38;2;111;196;176m" # #6FC4B0
37
-
38
- # 功能色
39
- BERRY = "\033[38;2;43;24;40m" # #2B1828 (深莓正文)
40
- BERRY_LIGHT = "\033[38;2;74;47;69m" # #4A2F45
41
- VIOLET = "\033[38;2;110;90;168m" # #6E5AA8
42
- VIOLET_LIGHT = "\033[38;2;144;128;200m" # #9080C8
43
- HONEY = "\033[38;2;255;184;107m" # #FFB86B
44
- GREEN = "\033[38;2;69;214;163m" # #45D6A3
45
- RED = "\033[38;2;255;107;129m" # #FF6B81
46
- BLUE = "\033[38;2;101;168;255m" # #65A8FF
47
-
48
- # 状态色 (终端兼容)
49
- OK = "\033[1;38;2;69;214;163m" # green bold
50
- ERR = "\033[1;38;2;255;107;129m" # red bold
51
- WARN = "\033[1;38;2;255;184;107m" # honey bold
52
- INFO = "\033[38;2;101;168;255m" # blue
53
- DIM_TEXT = "\033[2m"
54
-
55
- # 渐变 (用多段 ANSI 近似模拟)
56
- GRADIENT_PINK = "\033[1;38;2;255;183;197m"
57
- GRADIENT_MINT = "\033[1;38;2;152;216;200m"
58
- GRADIENT_VIOLET = "\033[1;38;2;110;90;168m"
59
-
60
-
61
- def _use_color() -> bool:
62
- """检测是否使用彩色输出。"""
17
+ # ── 色彩 ───────────────────────────────────────────────────────
18
+
19
+ class C:
20
+ RESET = "\033[0m"
21
+ BOLD = "\033[1m"
22
+ DIM = "\033[2m"
23
+ ITALIC = "\033[3m"
24
+
25
+ # Aurora Pink 渐变 (Pink → Purple → Blue)
26
+ PINK = "\033[38;2;255;79;164m" # #FF4FA4
27
+ PINK_L = "\033[38;2;255;167;213m" # #FFA7D5
28
+ PURPLE = "\033[38;2;194;107;255m" # #C26BFF
29
+ BLUE = "\033[38;2;110;139;255m" # #6E8BFF
30
+
31
+ # Mint (Web 端品牌色)
32
+ MINT = "\033[38;2;152;216;200m" # #98D8C8
33
+ MINT_D = "\033[38;2;111;196;176m" # #6FC4B0
34
+
35
+ # 状态
36
+ OK = "\033[38;2;62;229;138m" # #3EE58A
37
+ WARN = "\033[38;2;255;179;71m" # #FFB347
38
+ ERR = "\033[38;2;255;107;129m" # #FF6B81
39
+ INFO = "\033[38;2;101;168;255m" # #65A8FF
40
+
41
+ # 文字
42
+ TEXT = "\033[38;2;235;235;245m" # #EBEBF5 (正文)
43
+ SUBTLE = "\033[38;2;138;138;150m" # #8A8A96 (辅助)
44
+ FAINT = "\033[38;2;90;90;102m" # #5A5A66 (最弱)
45
+
46
+
47
+ def _ok() -> bool:
63
48
  if os.environ.get("NO_COLOR"):
64
49
  return False
65
50
  if os.environ.get("TERM") == "dumb":
@@ -67,134 +52,182 @@ def _use_color() -> bool:
67
52
  return sys.stdout.isatty()
68
53
 
69
54
 
70
- def paint(text: str, *codes: str) -> str:
71
- """给文本上色 (无 TTY 时自动降级)。"""
72
- if not _use_color():
55
+ def p(text: str, *codes: str) -> str:
56
+ """上色"""
57
+ if not _ok():
73
58
  return text
74
- return "".join(codes) + text + Theme.RESET
59
+ return "".join(codes) + text + C.RESET
75
60
 
76
61
 
77
- # ── 高级组件 ───────────────────────────────────────────────────
62
+ def dim(text: str) -> str:
63
+ return p(text, C.SUBTLE)
78
64
 
79
- def sakura_label(text: str) -> str:
80
- """粉色标签 (用于品牌名)。"""
81
- return paint(text, Theme.BOLD, Theme.SAKURA_DARK)
82
65
 
66
+ def faint(text: str) -> str:
67
+ return p(text, C.FAINT)
83
68
 
84
- def mint_label(text: str) -> str:
85
- """薄荷标签 (用于成功状态)。"""
86
- return paint(text, Theme.BOLD, Theme.MINT_DARK)
87
69
 
70
+ def pink(text: str) -> str:
71
+ return p(text, C.PINK)
88
72
 
89
- def dim(text: str) -> str:
90
- """灰色辅助文字。"""
91
- return paint(text, Theme.DIM)
92
73
 
74
+ def purple(text: str) -> str:
75
+ return p(text, C.PURPLE)
93
76
 
94
- def agent_badge(name: str) -> str:
95
- """Agent 名称紫色胶囊。"""
96
- return paint(f" {name} ", Theme.BOLD, Theme.VIOLET)
97
77
 
78
+ def mint(text: str) -> str:
79
+ return p(text, C.MINT_D)
98
80
 
99
- def model_pill(model: str) -> str:
100
- """模型名粉色文字。"""
101
- return paint(model, Theme.SAKURA_DARK)
102
81
 
82
+ def ok(text: str) -> str:
83
+ return p(text, C.OK)
103
84
 
104
- def status_dot(ok: bool = True) -> str:
105
- """状态圆点。"""
106
- color = Theme.GREEN if ok else Theme.RED
107
- return paint("●", color)
108
85
 
86
+ def err(text: str) -> str:
87
+ return p(text, C.ERR)
109
88
 
110
- def divider(width: int = 56) -> str:
111
- """分隔线 — 樱花粉渐变薄荷。"""
112
- return paint("─" * width, Theme.SAKURA, Theme.DIM)
113
89
 
90
+ # ── 组件 ───────────────────────────────────────────────────────
114
91
 
115
- def header_box(title: str, subtitle: str = "") -> str:
116
- """带圆角的标题框 (对标 Web glass card)。"""
117
- w = max(len(title) + 4, 44)
118
- top = paint("╭" + "─" * (w - 2) + "╮", Theme.SAKURA)
119
- bottom = paint("╰" + "─" * (w - 2) + "╯", Theme.SAKURA)
120
- mid_title = paint(f"│ {title}", Theme.BOLD, Theme.SAKURA_DARK)
121
- mid_title += " " * (w - 2 - len(title) - 1) + paint("│", Theme.SAKURA)
122
- lines = [top, mid_title]
123
- if subtitle:
124
- mid_sub = paint(f"│ ", Theme.SAKURA) + paint(subtitle, Theme.DIM)
125
- mid_sub += " " * (w - 2 - len(subtitle) - 1) + paint("│", Theme.SAKURA)
126
- lines.append(mid_sub)
127
- lines.append(bottom)
92
+ def gradient_text(text: str) -> str:
93
+ """Pink Purple Blue 逐字符渐变。"""
94
+ if not _ok():
95
+ return text
96
+ colors = [C.PINK, C.PINK_L, "\033[38;2;255;127;184m",
97
+ "\033[38;2;224;119;220m", C.PURPLE,
98
+ "\033[38;2;160;123;240m", C.BLUE]
99
+ n = len(text)
100
+ result = []
101
+ for i, ch in enumerate(text):
102
+ if ch == " ":
103
+ result.append(ch)
104
+ continue
105
+ idx = min(int(i / max(n, 1) * len(colors)), len(colors) - 1)
106
+ result.append(colors[idx] + ch + C.RESET)
107
+ return "".join(result)
108
+
109
+
110
+ def header() -> str:
111
+ """启动 header — 极简留白风格。"""
112
+ lines = [
113
+ "",
114
+ f" {p('✦', C.PINK)}",
115
+ "",
116
+ f" {gradient_text('LBS Agent')}",
117
+ f" {dim('Autonomous AI Runtime')}",
118
+ "",
119
+ ]
128
120
  return "\n".join(lines)
129
121
 
130
122
 
131
- def kv_row(label: str, value: str, label_width: int = 9) -> str:
132
- """键值行 (对标 Web status dock)。"""
133
- return f" {paint(label.ljust(label_width), Theme.DIM)} {paint('│', Theme.SAKURA, Theme.DIM)} {value}"
123
+ def session_card(rows: list[tuple[str, str]]) -> str:
124
+ """Session 信息卡 上下布局,图标 + label + value。"""
125
+ icons = {
126
+ "session": "◇",
127
+ "workspace": "▸",
128
+ "agent": "◐",
129
+ "model": "✦",
130
+ "provider": "☁",
131
+ "memory": "◈",
132
+ "runtime": "⚙",
133
+ "turns": "⟩",
134
+ "elapsed": "⏱",
135
+ }
136
+ lines = []
137
+ for label, value in rows:
138
+ icon = icons.get(label, "·")
139
+ lines.append(f" {p(icon, C.PURPLE)} {dim(f'{label:11}')} {value}")
140
+ return "\n".join(lines)
134
141
 
135
142
 
136
- def turn_prompt(agent: str, turn: int) -> str:
137
- """Chat turn 提示符。"""
138
- return f"{agent_badge(agent)} {dim(f'turn {turn}')} {paint('›', Theme.SAKURA)} "
143
+ def separator(width: int = 52) -> str:
144
+ """分隔线 最弱色"""
145
+ return p("─" * width, C.FAINT)
139
146
 
140
147
 
141
- def usage_bar(seconds: float, iterations: int, tools: int) -> str:
142
- """用量状态条 (对标 Web 端 status dock)。"""
143
- parts = [
144
- paint(f"{seconds:.1f}s", Theme.MINT_DARK),
145
- paint(f"iter {iterations}", Theme.DIM),
146
- paint(f"tools {tools}", Theme.DIM),
148
+ def prompt_symbol() -> str:
149
+ """输入提示符"""
150
+ return p("❯", C.PINK)
151
+
152
+
153
+ def footer() -> str:
154
+ """底部快捷键栏"""
155
+ keys = [
156
+ ("↵", "Send"),
157
+ ("↑↓", "History"),
158
+ ("Tab", "Complete"),
159
+ ("/help", "Commands"),
160
+ ("Ctrl+C", "Exit"),
147
161
  ]
148
- return " " + paint(" │ ", Theme.SAKURA, Theme.DIM).join(parts)
162
+ parts = []
163
+ for key, label in keys:
164
+ parts.append(f"{p(key, C.PURPLE)} {faint(label)}")
165
+ return faint(" ") + faint(" · ".join(parts))
149
166
 
150
167
 
151
- def react_header(agent_display: str, model_chain: list[str], tools: list[str]) -> str:
152
- """ReAct 迭代头 (对标 Web 端 ChatFlow 的任务头)。"""
153
- model_str = paint(" → ", Theme.SAKURA).join(
154
- paint(m, Theme.SAKURA_DARK) for m in model_chain
155
- )
156
- tools_str = paint(", ", Theme.DIM).join(
157
- paint(t, Theme.MINT_DARK) for t in tools
158
- )
159
- lines = [
160
- paint("━" * 56, Theme.SAKURA, Theme.DIM),
161
- f" {paint('Agent', Theme.DIM)} {agent_badge(agent_display)}",
162
- f" {paint('Model', Theme.DIM)} {model_str}",
163
- f" {paint('Tools', Theme.DIM)} {tools_str}",
164
- paint("━" * 56, Theme.SAKURA, Theme.DIM),
168
+ def usage_line(seconds: float, iterations: int, tools: int) -> str:
169
+ """用量条"""
170
+ parts = [
171
+ p(f"{seconds:.1f}s", C.MINT_D),
172
+ faint(f"iter {iterations}"),
173
+ faint(f"tools {tools}"),
165
174
  ]
166
- return "\n".join(lines)
175
+ return " " + faint(" · ").join(parts)
167
176
 
168
177
 
169
- def react_iteration(iteration: int, max_iterations: int, phase: str = "") -> str:
170
- """ReAct 迭代标记。"""
171
- phase_str = f" {paint(phase, Theme.VIOLET)}" if phase else ""
172
- return f"\n {paint('⟡', Theme.SAKURA)} {paint(f'iter {iteration}', Theme.BOLD, Theme.SAKURA_DARK)}{paint(f'/{max_iterations}', Theme.DIM)}{phase_str}"
178
+ def result_text(content: str) -> str:
179
+ """最终结果"""
180
+ return p(content, C.TEXT)
173
181
 
174
182
 
175
- def thinking_block(content: str) -> str:
176
- """思考过程块 (薄荷绿斜体)。"""
177
- return paint(content, Theme.ITALIC, Theme.MINT_DARK)
183
+ def error_text(content: str) -> str:
184
+ """错误"""
185
+ return p(f"✕ {content}", C.ERR)
178
186
 
179
187
 
180
- def result_block(content: str) -> str:
181
- """最终结果块。"""
182
- return paint(content, Theme.BOLD)
188
+ def agent_tag(name: str) -> str:
189
+ """Agent 标签"""
190
+ return p(f"◐ {name}", C.PURPLE)
183
191
 
184
192
 
185
- def error_block(content: str) -> str:
186
- """错误块。"""
187
- return paint(f" {content}", Theme.BOLD, Theme.RED)
193
+ def model_tag(model: str) -> str:
194
+ """模型标签"""
195
+ return p(f" {model}", C.PINK)
188
196
 
189
197
 
190
- def logo() -> str:
191
- """LBS Agent ASCII Logo Sakura 主题。"""
192
- lines = [
193
- "",
194
- f" {paint('╔══╗', Theme.SAKURA)} {paint('╔═══╗', Theme.MINT)} {paint('╔═════╗', Theme.MINT)}",
195
- f" {paint('║ ║', Theme.SAKURA)} {paint('║ ║', Theme.MINT)} {paint('║ ╔═╝', Theme.MINT)}",
196
- f" {paint('╠══╣', Theme.SAKURA)} {paint('╠═══╣', Theme.MINT)} {paint('║ ╚═╗', Theme.MINT)}",
197
- f" {paint('║ ║', Theme.SAKURA)} {paint('║ ║', Theme.MINT)} {paint('╚═════╝', Theme.MINT)}",
198
- f" {paint('╚══╝', Theme.SAKURA)} {paint('╚═══╝', Theme.MINT)} {paint('AGENT', Theme.DIM)}",
199
- ]
200
- return "\n".join(lines)
198
+ def react_header(agent_display: str, model_chain: list[str], tools: list[str]) -> str:
199
+ """ReAct 任务头极简留白"""
200
+ model_str = faint(" → ").join(pink(m) for m in model_chain)
201
+ tools_str = faint(", ").join(mint(t) for t in tools)
202
+ return "\n".join([
203
+ separator(),
204
+ f" {agent_tag(agent_display)}",
205
+ f" {model_str}",
206
+ f" {faint('tools')} {tools_str}" if tools_str else "",
207
+ separator(),
208
+ ])
209
+
210
+
211
+ def react_iter(iteration: int) -> str:
212
+ """迭代标记"""
213
+ return f"\n {p('⟡', C.PINK)} {p(f'iter {iteration}', C.BOLD, C.PINK)}"
214
+
215
+
216
+ def thinking_block(content: str) -> str:
217
+ """思考块"""
218
+ return p(content, C.ITALIC, C.MINT_D)
219
+
220
+
221
+ def tool_card(name: str, status: str, duration: str = "") -> str:
222
+ """工具调用卡片"""
223
+ status_color = C.OK if status == "ok" else C.ERR
224
+ icon = "✓" if status == "ok" else "✕"
225
+ dur = f" {faint(duration)}" if duration else ""
226
+ return f" {p(icon, status_color)} {faint(name)}{dur}"
227
+
228
+
229
+ def timeline_item(timestamp: str, label: str, success: bool = True) -> str:
230
+ """时间线条目"""
231
+ icon = "✓" if success else "✕"
232
+ color = C.OK if success else C.ERR
233
+ return f" {faint(timestamp)} {p(icon, color)} {dim(label)}"
@@ -1,17 +1,6 @@
1
- """LBS Agent REPL 交互模式。
2
-
3
- 设计:
4
- - 用户进入交互循环,每次输入一行
5
- - 内建命令以 `/` 开头(/help, /exit, /model, /agent, /session, /status, /clear, /new)
6
- - 其他输入走 Orchestrator.run_agent_sync()
7
- - 每次 turn 自动保存到 session(持久化)
8
-
9
- 用法:
10
- lbs chat # 默认 agent
11
- lbs chat --agent code
12
- lbs chat --agent code --provider minmax --model MiniMax-M3
13
- lbs chat --session <id> # 恢复已有 session
14
- lbs chat --query "single query" # 非交互(等价于 lbs run)
1
+ """LBS Agent REPL — Aurora Terminal v2。
2
+
3
+ 极简留白,符号驱动,渐变品牌。
15
4
  """
16
5
  from __future__ import annotations
17
6
 
@@ -24,21 +13,21 @@ from application.container import AgentSystemContainer
24
13
  from config.config import AppConfig
25
14
  from interfaces.cli._impl.commands.common import project_root
26
15
  from interfaces.cli._impl.cli_theme import (
27
- Theme, paint, dim, logo, agent_badge, model_pill, status_dot,
28
- divider, header_box, kv_row, turn_prompt, usage_bar,
29
- react_header, react_iteration, thinking_block, result_block,
30
- error_block, sakura_label, mint_label,
16
+ C, p, dim, faint, pink, purple, mint, ok, err,
17
+ gradient_text, header, session_card, separator,
18
+ prompt_symbol, footer, usage_line, result_text,
19
+ error_text, agent_tag, model_tag, react_header,
20
+ react_iter, thinking_block, tool_card, timeline_item,
31
21
  )
32
22
 
33
23
 
34
24
  @dataclass
35
25
  class ChatState:
36
- """REPL 运行时状态。"""
37
26
  container: object = None
38
27
  orchestrator: object = None
39
28
  session_service: object = None
40
29
  session_id: str = ""
41
- agent_name: str = "code"
30
+ agent_name: str = "chat"
42
31
  provider_name: str = ""
43
32
  model_name: str = ""
44
33
  turn_count: int = 0
@@ -46,11 +35,9 @@ class ChatState:
46
35
 
47
36
 
48
37
  def _resolve_agent(config: AppConfig) -> str:
49
- """选默认 agent:chat 优先(REPL 专用),其次 main/第一个。"""
50
38
  names = list(config.agents.agents.keys()) if config.agents else []
51
39
  if not names:
52
40
  raise RuntimeError("没有配置任何 agent")
53
- # chat 是 REPL 专用(无 evidence 强制,纯对话)
54
41
  if "chat" in names:
55
42
  return "chat"
56
43
  if "main" in names:
@@ -59,38 +46,47 @@ def _resolve_agent(config: AppConfig) -> str:
59
46
 
60
47
 
61
48
  def _print_banner(state: ChatState) -> None:
62
- """启动 banner Sakura 主题。"""
49
+ """启动 — 极简留白"""
63
50
  print()
64
- print(logo())
65
- print(f" {paint('LBS', Theme.BOLD, Theme.SAKURA_DARK)} {dim('Agent')} "
66
- f"{paint('·', Theme.SAKURA)} {dim('Interactive Chat')}")
51
+ print(header())
52
+
53
+ rows = [
54
+ ("session", faint(state.session_id) if state.session_id else faint("(unsaved)")),
55
+ ("agent", agent_tag(state.agent_name)),
56
+ ("model", model_tag(state.model_name) if state.model_name else faint("(default)")),
57
+ ("provider", purple(state.provider_name) if state.provider_name else faint("(default)")),
58
+ ("turns", faint(str(state.turn_count))),
59
+ ]
60
+ print(session_card(rows))
67
61
  print()
68
- print(f" {dim('Type a message, or')} {sakura_label('/help')} {dim('for commands')}")
62
+ print(separator())
63
+ print(f" {faint('Ask anything...')}")
64
+ print(separator())
69
65
  print()
70
66
 
71
67
 
72
68
  def _print_turn_header(state: ChatState) -> None:
73
- """每次 turn header(agent + turn 编号)。"""
69
+ """输入提示符 只有 """
74
70
  print()
75
- print(turn_prompt(state.agent_name, state.turn_count), end="", flush=True)
71
+ print(f" {prompt_symbol()} ", end="", flush=True)
76
72
 
77
73
 
78
74
  def _print_help() -> None:
79
75
  print()
80
- print(f" {header_box('Commands', '/ prefix to run')}")
76
+ print(f" {gradient_text('Commands')}")
81
77
  print()
82
78
  cmds = [
83
- ("/help", "显示本帮助"),
84
- ("/exit", "退出 chat"),
85
- ("/new", "开始新 session"),
86
- ("/status", "显示 session 状态"),
87
- ("/agent", "列出 / 切换 agent"),
88
- ("/model", "列出 / 切换 model"),
89
- ("/history", "显示最近对话"),
79
+ ("/help", "显示帮助"),
80
+ ("/exit", "退出"),
81
+ ("/new", " session"),
82
+ ("/status", "状态"),
83
+ ("/agent", "切换 agent"),
84
+ ("/model", "切换 model"),
85
+ ("/history", "历史"),
90
86
  ("/clear", "清屏"),
91
87
  ]
92
88
  for cmd, desc in cmds:
93
- print(f" {sakura_label(cmd.ljust(12))} {dim(desc)}")
89
+ print(f" {pink(cmd.ljust(12))} {faint(desc)}")
94
90
  print()
95
91
 
96
92
 
@@ -98,11 +94,8 @@ def _cmd_list_agents(state: ChatState) -> None:
98
94
  agents = state.container.config.agents.agents
99
95
  print()
100
96
  for name, a in agents.items():
101
- if name == state.agent_name:
102
- marker = f" {status_dot(True)} {dim('current')}"
103
- else:
104
- marker = ""
105
- print(f" {agent_badge(name)} {dim(a.display_name)}{marker}")
97
+ current = f" {ok('← current')}" if name == state.agent_name else ""
98
+ print(f" {agent_tag(name)} {faint(a.display_name)}{current}")
106
99
  print()
107
100
 
108
101
 
@@ -110,20 +103,21 @@ def _cmd_list_models(state: ChatState) -> None:
110
103
  config = state.container.config
111
104
  provider_name = state.provider_name or config.models.default_provider
112
105
  if provider_name not in config.models.providers:
113
- print(f" {error_block(f'无 provider {provider_name!r}')}")
106
+ msg = f"无 provider '{provider_name}'"
107
+ print(f" {error_text(msg)}")
114
108
  return
115
- p = config.models.providers[provider_name]
109
+ prov = config.models.providers[provider_name]
116
110
  print()
117
- print(f" {dim('provider')} {paint(provider_name, Theme.VIOLET)} {dim(f'({p.display_name or provider_name})')}")
118
- for m in p.models:
119
- marker = f" {status_dot(True)}" if m == state.model_name else ""
120
- print(f" {model_pill(m)}{marker}")
111
+ print(f" {faint('provider')} {purple(provider_name)}")
112
+ for m in prov.models:
113
+ marker = f" {ok('←')}" if m == state.model_name else ""
114
+ print(f" {model_tag(m)}{marker}")
121
115
  print()
122
116
 
123
117
 
124
118
  def _cmd_history(state: ChatState, n: int = 5) -> None:
125
119
  if not state.session_id:
126
- print(f" {dim('无 session')}")
120
+ print(f" {faint('无 session')}")
127
121
  return
128
122
  try:
129
123
  messages = list(state.session_service.session_store.load_session_messages(state.session_id))
@@ -134,33 +128,31 @@ def _cmd_history(state: ChatState, n: int = 5) -> None:
134
128
  if role in ("user", "assistant"):
135
129
  recent.append((role, content))
136
130
  print()
137
- print(f" {dim(f'最近 {len(recent)} 条消息')}")
138
131
  for role, content in recent:
139
- role_color = Theme.SAKURA_DARK if role == "user" else Theme.MINT_DARK
140
- print(f" {paint(role.ljust(9), role_color)} {paint('│', Theme.SAKURA, Theme.DIM)} {dim(content)}")
132
+ rc = C.PINK if role == "user" else C.MINT_D
133
+ print(f" {p(role.ljust(9), rc)} {faint(content)}")
141
134
  print()
142
135
  except Exception as e:
143
- print(f" {error_block(f'读取失败: {e}')}")
136
+ msg = f"读取失败: {e}"
137
+ print(f" {error_text(msg)}")
144
138
 
145
139
 
146
140
  def _cmd_status(state: ChatState) -> None:
147
141
  elapsed = int(time.time() - state.started_at)
148
142
  rows = [
149
- ("session", state.session_id or dim("(unsaved)")),
150
- ("agent", agent_badge(state.agent_name)),
151
- ("provider", state.provider_name or dim("(default)")),
152
- ("model", model_pill(state.model_name) if state.model_name else dim("(default)")),
153
- ("turns", str(state.turn_count)),
154
- ("elapsed", f"{elapsed}s"),
143
+ ("session", faint(state.session_id) if state.session_id else faint("(unsaved)")),
144
+ ("agent", agent_tag(state.agent_name)),
145
+ ("model", model_tag(state.model_name) if state.model_name else faint("(default)")),
146
+ ("provider", purple(state.provider_name) if state.provider_name else faint("(default)")),
147
+ ("turns", faint(str(state.turn_count))),
148
+ ("elapsed", faint(f"{elapsed}s")),
155
149
  ]
156
150
  print()
157
- for label, val in rows:
158
- print(kv_row(label, val))
151
+ print(session_card(rows))
159
152
  print()
160
153
 
161
154
 
162
155
  def _handle_command(state: ChatState, line: str) -> bool:
163
- """处理 / 命令。返回 True 表示继续循环,False 表示退出。"""
164
156
  parts = line.strip().split(maxsplit=1)
165
157
  cmd = parts[0].lower()
166
158
  arg = parts[1] if len(parts) > 1 else ""
@@ -173,13 +165,14 @@ def _handle_command(state: ChatState, line: str) -> bool:
173
165
  state.session_id = ""
174
166
  state.turn_count = 0
175
167
  state.started_at = time.time()
176
- print(f" {mint_label('')} {dim('new session')}")
168
+ print(f" {ok('')} {faint('new session')}")
177
169
  elif cmd == "/session":
178
170
  if arg:
179
171
  state.session_id = arg
180
- print(f" {mint_label('')} {dim('switched to')} {paint(arg, Theme.SAKURA_DARK)}")
172
+ print(f" {ok('')} {faint('switched to')} {pink(arg)}")
181
173
  else:
182
- print(f" {dim('session:')} {state.session_id or dim('(none)')}")
174
+ sid = state.session_id or "(none)"
175
+ print(f" {faint('session:')} {sid}")
183
176
  elif cmd == "/status":
184
177
  _cmd_status(state)
185
178
  elif cmd == "/agent":
@@ -189,15 +182,16 @@ def _handle_command(state: ChatState, line: str) -> bool:
189
182
  agents = state.container.config.agents.agents
190
183
  if arg in agents:
191
184
  state.agent_name = arg
192
- print(f" {mint_label('')} {dim('agent')} {agent_badge(arg)}")
185
+ print(f" {ok('')} {faint('agent')} {agent_tag(arg)}")
193
186
  else:
194
- print(f" {error_block(f'无 agent {arg!r}')}")
187
+ msg = f"无 agent '{arg}'"
188
+ print(f" {error_text(msg)}")
195
189
  elif cmd == "/model":
196
190
  if not arg:
197
191
  _cmd_list_models(state)
198
192
  else:
199
193
  state.model_name = arg
200
- print(f" {mint_label('')} {dim('model')} {model_pill(arg)}")
194
+ print(f" {ok('')} {faint('model')} {model_tag(arg)}")
201
195
  elif cmd == "/clear":
202
196
  print("\033[2J\033[H", end="")
203
197
  elif cmd == "/history":
@@ -205,16 +199,16 @@ def _handle_command(state: ChatState, line: str) -> bool:
205
199
  n = int(arg) if arg else 5
206
200
  _cmd_history(state, n)
207
201
  except ValueError:
208
- print(f" {error_block('/history [n] — n 必须是整数')}")
202
+ print(f" {error_text('/history [n] — n 须为整数')}")
209
203
  elif cmd == "/save":
210
- print(f" {dim('session 自动保存在每次 turn')}")
204
+ print(f" {faint('session 每次 turn 自动保存')}")
211
205
  else:
212
- print(f" {error_block(f'未知命令: {cmd} — 输入 /help')}")
206
+ print(f" {error_text(f'未知命令: {cmd}')}")
213
207
  return True
214
208
 
215
209
 
216
210
  def _run_turn(state: ChatState, user_input: str) -> None:
217
- """跑一轮对话(同步阻塞)。"""
211
+ """跑一轮对话"""
218
212
  t0 = time.time()
219
213
  try:
220
214
  result = state.orchestrator.run_agent_sync(
@@ -224,24 +218,23 @@ def _run_turn(state: ChatState, user_input: str) -> None:
224
218
  session_id=state.session_id,
225
219
  )
226
220
  elapsed = time.time() - t0
227
- # 显示结果
228
221
  if result.success:
229
222
  content = (result.content or "").strip()
230
- print(f"\n{result_block(content)}")
223
+ print(f"\n{result_text(content)}")
231
224
  else:
232
- err = result.error or "(无错误信息)"
233
- print(f"\n {error_block(err[:300])}")
234
- # 用量状态条
235
- print(usage_bar(elapsed, result.iterations, result.total_tool_calls))
236
- # 保存 session_id
225
+ e = result.error or "(无错误信息)"
226
+ print(f"\n {error_text(e[:300])}")
227
+ print(usage_line(elapsed, result.iterations, result.total_tool_calls))
237
228
  if not state.session_id:
238
- state.session_id = result.session_id if hasattr(result, "session_id") and result.session_id else f"chat-{int(state.started_at)}"
229
+ sid = result.session_id if hasattr(result, "session_id") and result.session_id else ""
230
+ state.session_id = sid or f"chat-{int(state.started_at)}"
239
231
  except KeyboardInterrupt:
240
- print(f"\n {paint('⏸', Theme.HONEY)} {dim('已中断')}")
232
+ print(f"\n {p('⏸', C.WARN)} {faint('已中断')}")
241
233
  except Exception as e:
242
234
  elapsed = time.time() - t0
243
- print(f"\n {error_block(f'{type(e).__name__}: {str(e)[:200]}')}")
244
- print(f" {dim(f'{elapsed:.1f}s')}")
235
+ msg = f"{type(e).__name__}: {str(e)[:200]}"
236
+ print(f"\n {error_text(msg)}")
237
+ print(f" {faint(f'{elapsed:.1f}s')}")
245
238
 
246
239
 
247
240
  def cmd_chat(
@@ -255,14 +248,12 @@ def cmd_chat(
255
248
  """进入 chat REPL 或单次 query。"""
256
249
  container = AgentSystemContainer(config=config, project_root=project_root)
257
250
 
258
- # 选 agent
259
251
  if not agent_name:
260
252
  agent_name = _resolve_agent(config)
261
253
 
262
- # 验证 agent 存在
263
254
  if agent_name not in config.agents.agents:
264
255
  msg = f"Agent '{agent_name}' 不存在"
265
- print(f" {error_block(msg)}")
256
+ print(f" {error_text(msg)}")
266
257
  return 1
267
258
 
268
259
  state = ChatState(
@@ -275,7 +266,6 @@ def cmd_chat(
275
266
  session_id=session_id,
276
267
  )
277
268
 
278
- # 单次 query 模式
279
269
  if query:
280
270
  state.turn_count = 1
281
271
  _print_turn_header(state)
@@ -283,9 +273,10 @@ def cmd_chat(
283
273
  _run_turn(state, query)
284
274
  return 0
285
275
 
286
- # REPL 模式
276
+ # REPL
287
277
  _print_banner(state)
288
- _cmd_status(state)
278
+ print(footer())
279
+ print()
289
280
 
290
281
  while True:
291
282
  try:
@@ -295,7 +286,7 @@ def cmd_chat(
295
286
  print("\n")
296
287
  break
297
288
  except KeyboardInterrupt:
298
- print(f"\n {paint('用 /exit 退出', Theme.HONEY)}")
289
+ print(f"\n {p('用 /exit 退出', C.WARN)}")
299
290
  continue
300
291
 
301
292
  line = line.strip()
@@ -308,5 +299,5 @@ def cmd_chat(
308
299
  state.turn_count += 1
309
300
  _run_turn(state, line)
310
301
 
311
- print(f"\n {mint_label('')} {dim('Session saved. Bye.')}")
302
+ print(f"\n {ok('')} {faint('Session saved. Bye.')}")
312
303
  return 0
@@ -73,10 +73,10 @@ def _paint(text: str, *codes: str) -> str:
73
73
  def _header(title: str) -> None:
74
74
  """打印章节标题 (带边框)。"""
75
75
  print()
76
- print(_paint(" ┌─────────────────────────────────────────────────────────┐", _C.SAKURA))
77
- print(_paint(" │ ", _C.SAKURA) + _paint(f"⚙ LBS Setup — {title}", _C.BOLD, _C.SAKURA)
78
- + _paint(" " * max(0, 47 - len(title) - 14) + "│", _C.SAKURA))
79
- print(_paint(" └─────────────────────────────────────────────────────────┘", _C.SAKURA))
76
+ print(_paint(" ┌─────────────────────────────────────────────────────────┐", _C.PINK))
77
+ print(_paint(" │ ", _C.PINK) + _paint(f"⚙ LBS Setup — {title}", _C.BOLD, _C.PINK)
78
+ + _paint(" " * max(0, 47 - len(title) - 14) + "│", _C.PINK))
79
+ print(_paint(" └─────────────────────────────────────────────────────────┘", _C.PINK))
80
80
  print()
81
81
 
82
82
 
@@ -85,7 +85,7 @@ def _info(msg: str) -> None:
85
85
 
86
86
 
87
87
  def _success(msg: str) -> None:
88
- print(_paint(" ✓ ", _C.GREEN) + _paint(msg, _C.GREEN))
88
+ print(_paint(" ✓ ", _C.OK) + _paint(msg, _C.OK))
89
89
 
90
90
 
91
91
  def _warn(msg: str) -> None:
@@ -93,7 +93,7 @@ def _warn(msg: str) -> None:
93
93
 
94
94
 
95
95
  def _error(msg: str) -> None:
96
- print(_paint(" ✖ ", _C.RED) + _paint(msg, _C.RED))
96
+ print(_paint(" ✖ ", _C.ERR) + _paint(msg, _C.ERR))
97
97
 
98
98
 
99
99
  def _prompt(label: str, default: str = "") -> str:
@@ -111,7 +111,7 @@ def _prompt_choice(label: str, options: list[str], default: str = "") -> str:
111
111
  """带选项列表的输入提示。"""
112
112
  print(_paint(f" ▸ {label}", _C.MINT))
113
113
  for i, opt in enumerate(options, 1):
114
- marker = _paint("→", _C.GREEN) if opt == default else " "
114
+ marker = _paint("→", _C.OK) if opt == default else " "
115
115
  print(f" {marker} {i}. {opt}")
116
116
  val = _prompt("选择编号或直接输入", default)
117
117
  if val.isdigit():
@@ -467,10 +467,10 @@ def run_setup(section: str | None = None) -> int:
467
467
  root.mkdir(parents=True, exist_ok=True)
468
468
 
469
469
  print()
470
- print(_paint(" ┌─────────────────────────────────────────────────────────┐", _C.SAKURA))
471
- print(_paint(" │ ", _C.SAKURA) + _paint("⚙ LBS Agent Setup Wizard", _C.BOLD, _C.SAKURA)
472
- + _paint(" │", _C.SAKURA))
473
- print(_paint(" └─────────────────────────────────────────────────────────┘", _C.SAKURA))
470
+ print(_paint(" ┌─────────────────────────────────────────────────────────┐", _C.PINK))
471
+ print(_paint(" │ ", _C.PINK) + _paint("⚙ LBS Agent Setup Wizard", _C.BOLD, _C.PINK)
472
+ + _paint(" │", _C.PINK))
473
+ print(_paint(" └─────────────────────────────────────────────────────────┘", _C.PINK))
474
474
  print()
475
475
  _info(f"配置目录: {root}")
476
476
  print()
@@ -496,15 +496,15 @@ def run_setup(section: str | None = None) -> int:
496
496
 
497
497
  def _print_done() -> None:
498
498
  print()
499
- print(_paint(" ┌─────────────────────────────────────────────────────────┐", _C.GREEN))
500
- print(_paint(" │ ", _C.GREEN) + _paint("✅ 配置完成!", _C.BOLD, _C.GREEN)
501
- + _paint(" │", _C.GREEN))
502
- print(_paint(" │ │", _C.GREEN))
503
- print(_paint(" │ ", _C.GREEN) + _paint("配置文件:", _C.BOLD) + paint_path(_models_yaml_path()) + _paint(" │", _C.GREEN))
504
- print(_paint(" │ ", _C.GREEN) + _paint("密钥文件:", _C.BOLD) + paint_path(_env_path()) + _paint(" │", _C.GREEN))
505
- print(_paint(" │ │", _C.GREEN))
506
- print(_paint(" │ ", _C.GREEN) + _paint("现在可以运行:", _C.BOLD) + paint_cmd(" lbs status") + _paint(" │", _C.GREEN))
507
- print(_paint(" └─────────────────────────────────────────────────────────┘", _C.GREEN))
499
+ print(_paint(" ┌─────────────────────────────────────────────────────────┐", _C.OK))
500
+ print(_paint(" │ ", _C.OK) + _paint("✅ 配置完成!", _C.BOLD, _C.OK)
501
+ + _paint(" │", _C.OK))
502
+ print(_paint(" │ │", _C.OK))
503
+ print(_paint(" │ ", _C.OK) + _paint("配置文件:", _C.BOLD) + paint_path(_models_yaml_path()) + _paint(" │", _C.OK))
504
+ print(_paint(" │ ", _C.OK) + _paint("密钥文件:", _C.BOLD) + paint_path(_env_path()) + _paint(" │", _C.OK))
505
+ print(_paint(" │ │", _C.OK))
506
+ print(_paint(" │ ", _C.OK) + _paint("现在可以运行:", _C.BOLD) + paint_cmd(" lbs status") + _paint(" │", _C.OK))
507
+ print(_paint(" └─────────────────────────────────────────────────────────┘", _C.OK))
508
508
  print()
509
509
 
510
510
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baimingtao/lbs-agent",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/scripts/init.js CHANGED
@@ -63,7 +63,7 @@ function ensureDir(dir) {
63
63
  // ── Step 1: 检测系统 Python ────────────────────────────────────
64
64
 
65
65
  function findSystemPython() {
66
- console.log(paint(' 🔍 ', C.sakura) + '检测系统 Python...');
66
+ console.log(paint(' 🔍 ', C.pink) + '检测系统 Python...');
67
67
 
68
68
  const candidates = isWin
69
69
  ? ['python', 'python3', 'py -3']
@@ -73,7 +73,7 @@ function findSystemPython() {
73
73
  const result = run(cmd, ['--version'], { silent: true, shell: isWin });
74
74
  if (result.status === 0) {
75
75
  const version = (result.stdout || result.stderr || '').trim();
76
- console.log(paint(' ✓ ', C.green) + `${cmd}: ${version}`);
76
+ console.log(paint(' ✓ ', C.ok) + `${cmd}: ${version}`);
77
77
 
78
78
  const match = version.match(/(\d+)\.(\d+)/);
79
79
  if (match) {
@@ -81,11 +81,11 @@ function findSystemPython() {
81
81
  const minor = parseInt(match[2]);
82
82
  if (major >= 3 && minor >= 10) return cmd;
83
83
  }
84
- console.log(paint(' ⚠ ', C.honey) + '版本低于 3.10,继续寻找...');
84
+ console.log(paint(' ⚠ ', C.warn) + '版本低于 3.10,继续寻找...');
85
85
  }
86
86
  }
87
87
 
88
- console.error(paint('\n ✖ ', C.red) + '未找到 Python 3.10+');
88
+ console.error(paint('\n ✖ ', C.err) + '未找到 Python 3.10+');
89
89
  console.error(paint(' ', C.dim) + 'Windows: https://www.python.org/downloads/');
90
90
  console.error(paint(' ', C.dim) + 'macOS: brew install python@3.12');
91
91
  console.error(paint(' ', C.dim) + 'Linux: sudo apt install python3 python3-venv');
@@ -96,20 +96,20 @@ function findSystemPython() {
96
96
 
97
97
  function createVenv(sysPython) {
98
98
  if (fs.existsSync(PYTHON)) {
99
- console.log(paint(' ✓ ', C.green) + '虚拟环境已存在,跳过创建');
99
+ console.log(paint(' ✓ ', C.ok) + '虚拟环境已存在,跳过创建');
100
100
  return true;
101
101
  }
102
102
 
103
103
  // 确保 state root 存在
104
104
  ensureDir(STATE_ROOT);
105
- console.log(paint(' 📦 ', C.sakura) + `创建 Python 虚拟环境 → ${paint(STATE_ROOT + '/venv', C.mint)}`);
105
+ console.log(paint(' 📦 ', C.pink) + `创建 Python 虚拟环境 → ${paint(STATE_ROOT + '/venv', C.mint)}`);
106
106
 
107
107
  const result = run(sysPython, ['-m', 'venv', VENV_DIR]);
108
108
  if (result.status !== 0 || !fs.existsSync(PYTHON)) {
109
- console.error(paint(' ✖ ', C.red) + '创建虚拟环境失败');
109
+ console.error(paint(' ✖ ', C.err) + '创建虚拟环境失败');
110
110
  return false;
111
111
  }
112
- console.log(paint(' ✓ ', C.green) + 'venv 已创建');
112
+ console.log(paint(' ✓ ', C.ok) + 'venv 已创建');
113
113
  return true;
114
114
  }
115
115
 
@@ -117,17 +117,17 @@ function createVenv(sysPython) {
117
117
 
118
118
  function installDeps() {
119
119
  if (!fs.existsSync(REQUIREMENTS)) {
120
- console.error(paint(' ✖ ', C.red) + 'requirements.txt 不存在: ' + REQUIREMENTS);
120
+ console.error(paint(' ✖ ', C.err) + 'requirements.txt 不存在: ' + REQUIREMENTS);
121
121
  return false;
122
122
  }
123
123
 
124
- console.log(paint(' 📥 ', C.sakura) + '安装 Python 依赖 (可能需要 1-2 分钟)...');
124
+ console.log(paint(' 📥 ', C.pink) + '安装 Python 依赖 (可能需要 1-2 分钟)...');
125
125
  const result = run(PIP, ['install', '-r', REQUIREMENTS, '--disable-pip-version-check']);
126
126
  if (result.status !== 0) {
127
- console.error(paint(' ✖ ', C.red) + '依赖安装失败');
127
+ console.error(paint(' ✖ ', C.err) + '依赖安装失败');
128
128
  return false;
129
129
  }
130
- console.log(paint(' ✓ ', C.green) + '依赖安装完成');
130
+ console.log(paint(' ✓ ', C.ok) + '依赖安装完成');
131
131
  return true;
132
132
  }
133
133
 
@@ -161,22 +161,22 @@ function createDefaultDirs() {
161
161
  fs.writeFileSync(configPath, defaultConfig, 'utf-8');
162
162
  }
163
163
 
164
- console.log(paint(' ✓ ', C.green) + '目录结构已创建');
164
+ console.log(paint(' ✓ ', C.ok) + '目录结构已创建');
165
165
  }
166
166
 
167
167
  // ── Step 5: 验证 ───────────────────────────────────────────────
168
168
 
169
169
  function verifyInstall() {
170
- console.log(paint(' 🔍 ', C.sakura) + '验证安装...');
170
+ console.log(paint(' 🔍 ', C.pink) + '验证安装...');
171
171
  const result = run(PYTHON, [path.join(PKG_ROOT, 'lbs.py'), 'status'], {
172
172
  silent: true,
173
173
  cwd: PKG_ROOT,
174
174
  env: { ...process.env, LBS_HOME: STATE_ROOT },
175
175
  });
176
176
  if (result.status === 0) {
177
- console.log(paint(' ✓ ', C.green) + 'lbs status 运行正常');
177
+ console.log(paint(' ✓ ', C.ok) + 'lbs status 运行正常');
178
178
  } else {
179
- console.log(paint(' ⚠ ', C.honey) + 'lbs status 运行异常(可能是配置问题,不影响初始化)');
179
+ console.log(paint(' ⚠ ', C.warn) + 'lbs status 运行异常(可能是配置问题,不影响初始化)');
180
180
  }
181
181
  return true;
182
182
  }
@@ -191,16 +191,16 @@ function writeFlag() {
191
191
  platform: os.platform(),
192
192
  node_version: process.version,
193
193
  }, null, 2));
194
- console.log(paint(' ✓ ', C.green) + '.lbs-initialized 已创建');
194
+ console.log(paint(' ✓ ', C.ok) + '.lbs-initialized 已创建');
195
195
  }
196
196
 
197
197
  // ── 主流程 ─────────────────────────────────────────────────────
198
198
 
199
199
  function main() {
200
200
  console.log();
201
- console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.sakura));
202
- console.log(paint(' │ ', C.sakura) + paint('⚙ LBS Agent — 初始化', C.bold, C.sakura) + paint(' │', C.sakura));
203
- console.log(paint(' └─────────────────────────────────────────────────────────┘', C.sakura));
201
+ console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.pink));
202
+ console.log(paint(' │ ', C.pink) + paint('⚙ LBS Agent — 初始化', C.bold, C.pink) + paint(' │', C.pink));
203
+ console.log(paint(' └─────────────────────────────────────────────────────────┘', C.pink));
204
204
  console.log();
205
205
  console.log(paint(' 包目录 (只读): ', C.dim) + paint(PKG_ROOT, C.mint));
206
206
  console.log(paint(' 状态目录 (可写): ', C.dim) + paint(STATE_ROOT, C.mint));
@@ -226,17 +226,17 @@ function main() {
226
226
  writeFlag();
227
227
 
228
228
  console.log();
229
- console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.green));
230
- console.log(paint(' │ ', C.green) + paint('✅ 初始化完成!', C.bold, C.green) + paint(' │', C.green));
231
- console.log(paint(' │ │', C.green));
232
- console.log(paint(' │ ', C.green) + paint('下一步:', C.bold) + paint(' │', C.green));
233
- console.log(paint(' │ ', C.green) + paint(' lbs setup 配置模型/供应商/Agent', C.mint) + paint(' │', C.green));
234
- console.log(paint(' │ ', C.green) + paint(' lbs status 查看系统状态', C.mint) + paint(' │', C.green));
235
- console.log(paint(' │ ', C.green) + paint(' lbs help 查看所有命令', C.mint) + paint(' │', C.green));
236
- console.log(paint(' │ ', C.green) + paint(' lbs web 启动 Web 界面', C.mint) + paint(' │', C.green));
237
- console.log(paint(' │ │', C.green));
238
- console.log(paint(' │ ', C.green) + paint('状态目录: ', C.dim) + paint(STATE_ROOT, C.mint) + paint(' │', C.green));
239
- console.log(paint(' └─────────────────────────────────────────────────────────┘', C.green));
229
+ console.log(paint(' ┌─────────────────────────────────────────────────────────┐', C.ok));
230
+ console.log(paint(' │ ', C.ok) + paint('✅ 初始化完成!', C.bold, C.ok) + paint(' │', C.ok));
231
+ console.log(paint(' │ │', C.ok));
232
+ console.log(paint(' │ ', C.ok) + paint('下一步:', C.bold) + paint(' │', C.ok));
233
+ console.log(paint(' │ ', C.ok) + paint(' lbs setup 配置模型/供应商/Agent', C.mint) + paint(' │', C.ok));
234
+ console.log(paint(' │ ', C.ok) + paint(' lbs status 查看系统状态', C.mint) + paint(' │', C.ok));
235
+ console.log(paint(' │ ', C.ok) + paint(' lbs help 查看所有命令', C.mint) + paint(' │', C.ok));
236
+ console.log(paint(' │ ', C.ok) + paint(' lbs web 启动 Web 界面', C.mint) + paint(' │', C.ok));
237
+ console.log(paint(' │ │', C.ok));
238
+ console.log(paint(' │ ', C.ok) + paint('状态目录: ', C.dim) + paint(STATE_ROOT, C.mint) + paint(' │', C.ok));
239
+ console.log(paint(' └─────────────────────────────────────────────────────────┘', C.ok));
240
240
  console.log();
241
241
  }
242
242