@baimingtao/lbs-agent 1.5.0 → 1.6.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
@@ -96,14 +96,11 @@ function gradient(text) {
96
96
  return out;
97
97
  }
98
98
 
99
- // ── Banner — 极简留白 ─────────────────────────────────────────
99
+ // ── Banner — 单行品牌 ─────────────────────────────────────────
100
100
 
101
101
  function printBanner() {
102
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));
103
+ console.log(' ' + gradient('LBS Agent') + ' ' + paint('Autonomous AI Runtime', C.subtle));
107
104
  console.log();
108
105
  }
109
106
  // ── 初始化检查 ─────────────────────────────────────────────────
@@ -1,11 +1,9 @@
1
- """LBS CLI Theme Aurora Pink 现代终端设计系统。
1
+ """LBS CLI Terminal Design System.
2
2
 
3
- 设计原则:
4
- 留白 > 密集
5
- 符号 > 字符画
6
- 渐变 > 纯色
7
-
8
- 对标: Claude Code, Gemini CLI, Warp Terminal, Charm BubbleTea
3
+ 原则:
4
+ 终端 = Canvas, 不是 stdout
5
+ 所有输出走 Layout, 有版心、间距、层级
6
+ 一套图标, 一种节奏
9
7
  """
10
8
 
11
9
  from __future__ import annotations
@@ -13,6 +11,33 @@ from __future__ import annotations
13
11
  import os
14
12
  import sys
15
13
 
14
+ # ── 版心 ───────────────────────────────────────────────────────
15
+
16
+ MARGIN = 2 # 左缩进
17
+ MAX_W = 110 # 内容宽度上限
18
+
19
+ def _term_width() -> int:
20
+ try:
21
+ import shutil
22
+ return shutil.get_terminal_size().columns
23
+ except Exception:
24
+ return 80
25
+
26
+ def content_width() -> int:
27
+ return min(_term_width() - MARGIN * 2, MAX_W)
28
+
29
+ def pad(text: str, level: int = 0) -> str:
30
+ """给每行加左缩进。level=0 用 MARGIN, 更深层级递加。"""
31
+ indent = " " * (MARGIN + level * 2)
32
+ lines = text.split("\n")
33
+ return "\n".join(indent + ln if ln.strip() else ln for ln in lines)
34
+
35
+ def out(text: str = "", level: int = 0) -> None:
36
+ """统一输出 — 所有 CLI 输出的唯一出口。"""
37
+ if text == "":
38
+ print()
39
+ else:
40
+ print(pad(text, level))
16
41
 
17
42
  # ── 色彩 ───────────────────────────────────────────────────────
18
43
 
@@ -22,212 +47,167 @@ class C:
22
47
  DIM = "\033[2m"
23
48
  ITALIC = "\033[3m"
24
49
 
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:
50
+ PINK = "\033[38;2;255;79;164m"
51
+ PINK_L = "\033[38;2;255;167;213m"
52
+ PURPLE = "\033[38;2;194;107;255m"
53
+ BLUE = "\033[38;2;110;139;255m"
54
+ MINT = "\033[38;2;111;196;176m"
55
+ MINT_L = "\033[38;2;152;216;200m"
56
+ OK = "\033[38;2;62;229;138m"
57
+ WARN = "\033[38;2;255;179;71m"
58
+ ERR = "\033[38;2;255;107;129m"
59
+ TEXT = "\033[38;2;235;235;245m"
60
+ SUBTLE = "\033[38;2;138;138;150m"
61
+ FAINT = "\033[38;2;90;90;102m"
62
+
63
+
64
+ def _color_ok() -> bool:
48
65
  if os.environ.get("NO_COLOR"):
49
66
  return False
50
67
  if os.environ.get("TERM") == "dumb":
51
68
  return False
52
69
  return sys.stdout.isatty()
53
70
 
54
-
55
71
  def p(text: str, *codes: str) -> str:
56
- """上色"""
57
- if not _ok():
72
+ if not _color_ok():
58
73
  return text
59
74
  return "".join(codes) + text + C.RESET
60
75
 
76
+ def dim(t: str) -> str:
77
+ return p(t, C.SUBTLE)
61
78
 
62
- def dim(text: str) -> str:
63
- return p(text, C.SUBTLE)
64
-
65
-
66
- def faint(text: str) -> str:
67
- return p(text, C.FAINT)
79
+ def faint(t: str) -> str:
80
+ return p(t, C.FAINT)
68
81
 
82
+ def pink(t: str) -> str:
83
+ return p(t, C.PINK)
69
84
 
70
- def pink(text: str) -> str:
71
- return p(text, C.PINK)
85
+ def purple(t: str) -> str:
86
+ return p(t, C.PURPLE)
72
87
 
88
+ def mint(t: str) -> str:
89
+ return p(t, C.MINT)
73
90
 
74
- def purple(text: str) -> str:
75
- return p(text, C.PURPLE)
91
+ def ok(t: str) -> str:
92
+ return p(t, C.OK)
76
93
 
94
+ def err(t: str) -> str:
95
+ return p(t, C.ERR)
77
96
 
78
- def mint(text: str) -> str:
79
- return p(text, C.MINT_D)
97
+ def bold(t: str) -> str:
98
+ return p(t, C.BOLD)
80
99
 
81
-
82
- def ok(text: str) -> str:
83
- return p(text, C.OK)
84
-
85
-
86
- def err(text: str) -> str:
87
- return p(text, C.ERR)
88
-
89
-
90
- # ── 组件 ───────────────────────────────────────────────────────
91
-
92
- def gradient_text(text: str) -> str:
93
- """Pink → Purple → Blue 逐字符渐变。"""
94
- if not _ok():
100
+ def gradient(text: str) -> str:
101
+ """Pink Purple Blue 渐变"""
102
+ if not _color_ok():
95
103
  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]
104
+ stops = [
105
+ "\033[38;2;255;79;164m",
106
+ "\033[38;2;224;119;220m",
107
+ "\033[38;2;194;107;255m",
108
+ "\033[38;2;160;123;240m",
109
+ "\033[38;2;110;139;255m",
110
+ ]
99
111
  n = len(text)
100
112
  result = []
101
113
  for i, ch in enumerate(text):
102
114
  if ch == " ":
103
115
  result.append(ch)
104
116
  continue
105
- idx = min(int(i / max(n, 1) * len(colors)), len(colors) - 1)
106
- result.append(colors[idx] + ch + C.RESET)
117
+ idx = min(int(i / max(n, 1) * len(stops)), len(stops) - 1)
118
+ result.append(stops[idx] + ch + C.RESET)
107
119
  return "".join(result)
108
120
 
109
121
 
110
- def header() -> str:
111
- """启动 header — 极简留白风格。"""
122
+ # ── 统一图标体系 ───────────────────────────────────────────────
123
+ #
124
+ # 唯一一套符号, 不混用:
125
+ # ✦ 品牌/模型
126
+ # ◐ Agent
127
+ # ◇ Session
128
+ # ▸ 工具
129
+ # ✓ 成功
130
+ # ✕ 失败
131
+ # ❯ 输入
132
+ # · 分隔
133
+ #
134
+
135
+
136
+ # ── 组件 ───────────────────────────────────────────────────────
137
+
138
+ def render_banner(version: str = "") -> str:
139
+ """极简 Header — 品牌名一行, 副标题一行, 结束。"""
112
140
  lines = [
113
141
  "",
114
- f" {p('', C.PINK)}",
115
- "",
116
- f" {gradient_text('LBS Agent')}",
117
- f" {dim('Autonomous AI Runtime')}",
142
+ f"{gradient('LBS Agent')}" + (f" {faint(version)}" if version else ""),
143
+ faint("Autonomous AI Runtime"),
118
144
  "",
119
145
  ]
120
146
  return "\n".join(lines)
121
147
 
122
-
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)
141
-
142
-
143
- def separator(width: int = 52) -> str:
144
- """分隔线 最弱色"""
145
- return p("" * width, C.FAINT)
146
-
147
-
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"),
161
- ]
162
- parts = []
163
- for key, label in keys:
164
- parts.append(f"{p(key, C.PURPLE)} {faint(label)}")
165
- return faint(" ") + faint(" · ".join(parts))
166
-
167
-
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}"),
174
- ]
175
- return " " + faint(" · ").join(parts)
176
-
177
-
178
- def result_text(content: str) -> str:
179
- """最终结果"""
180
- return p(content, C.TEXT)
181
-
182
-
183
- def error_text(content: str) -> str:
184
- """错误"""
185
- return p(f"✕ {content}", C.ERR)
186
-
187
-
188
- def agent_tag(name: str) -> str:
189
- """Agent 标签"""
190
- return p(f"◐ {name}", C.PURPLE)
191
-
192
-
193
- def model_tag(model: str) -> str:
194
- """模型标签"""
195
- return p(f"✦ {model}", C.PINK)
196
-
197
-
198
- def react_header(agent_display: str, model_chain: list[str], tools: list[str]) -> str:
199
- """ReAct 任务头 — 极简留白"""
148
+ def render_session_line(agent: str, model: str = "", provider: str = "", session_id: str = "") -> str:
149
+ """Session 信息压成一行。"""
150
+ parts = [f"{p('◐', C.PURPLE)} {pink(agent)}"]
151
+ if model:
152
+ parts.append(f"{p('✦', C.PINK)} {pink(model)}")
153
+ if provider:
154
+ parts.append(f"{faint(provider)}")
155
+ if session_id:
156
+ parts.append(faint(session_id[:12]))
157
+ return faint(" · ").join(parts)
158
+
159
+ def render_prompt() -> str:
160
+ """输入提示符。"""
161
+ return f"{p('❯', C.PINK)} "
162
+
163
+ def render_footer() -> str:
164
+ """快捷键栏 一行, 最弱色。"""
165
+ keys = [("↵", "send"), ("/help", "commands"), ("Ctrl+C", "exit")]
166
+ return faint(" ") + faint(" · ").join(
167
+ f"{p(k, C.SUBTLE)} {faint(label)}" for k, label in keys
168
+ )
169
+
170
+ def render_react_header(agent: str, model_chain: list[str], tools: list[str]) -> str:
171
+ """ReAct 任务头 无边框, 纯留白。"""
172
+ lines = ["", f" {p('◐', C.PURPLE)} {bold(agent)}"]
200
173
  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)
174
+ lines.append(f" {model_str}")
175
+ if tools:
176
+ lines.append(f" {faint(', ').join(mint(t) for t in tools)}")
177
+ lines.append("")
178
+ return "\n".join(lines)
219
179
 
180
+ def render_iter(iteration: int, max_iter: int = 0) -> str:
181
+ """迭代标记。"""
182
+ suffix = faint(f"/{max_iter}") if max_iter else ""
183
+ return f" {p('✦', C.PINK)} {p(f'iter {iteration}', C.BOLD)}{suffix}"
220
184
 
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}"
185
+ def render_thinking(content: str) -> str:
186
+ """思考块 — 薄荷绿斜体。"""
187
+ return f" {p(content, C.ITALIC, C.MINT)}"
227
188
 
189
+ def render_tool_call(name: str, args_preview: str = "") -> str:
190
+ """工具调用。"""
191
+ suffix = faint(f"({args_preview})") if args_preview else ""
192
+ return f" {p('▸', C.PURPLE)} {mint(name)}{suffix}"
228
193
 
229
- def timeline_item(timestamp: str, label: str, success: bool = True) -> str:
230
- """时间线条目"""
194
+ def render_tool_result(name: str, success: bool, preview: str = "") -> str:
195
+ """工具结果。"""
231
196
  icon = "✓" if success else "✕"
232
- color = C.OK if success else C.ERR
233
- return f" {faint(timestamp)} {p(icon, color)} {dim(label)}"
197
+ color_fn = ok if success else err
198
+ suffix = f" {faint(preview)}" if preview else ""
199
+ return f" {color_fn(icon)} {faint(name)}{suffix}"
200
+
201
+ def render_result(agent: str, success: bool, iterations: int, tools: int, duration: float, error: str = "") -> str:
202
+ """最终结果摘要 — 两行, 无框。"""
203
+ icon = ok("✓") if success else err("✕")
204
+ parts = [faint(f"iter {iterations}"), faint(f"tools {tools}"), mint(f"{duration:.1f}s")]
205
+ lines = [
206
+ "",
207
+ f" {icon} {bold(agent)}",
208
+ f" {faint(' · ').join(parts)}",
209
+ ]
210
+ if error:
211
+ lines.append(f" {err(error[:200])}")
212
+ lines.append("")
213
+ return "\n".join(lines)
@@ -1,6 +1,7 @@
1
- """LBS Agent REPL — Aurora Terminal v2
1
+ """LBS Agent REPL — Terminal Product v3
2
2
 
3
- 极简留白,符号驱动,渐变品牌。
3
+ 终端 = Canvas, 不是 stdout。
4
+ 所有输出走 TDS (Terminal Design System)。
4
5
  """
5
6
  from __future__ import annotations
6
7
 
@@ -13,11 +14,9 @@ from application.container import AgentSystemContainer
13
14
  from config.config import AppConfig
14
15
  from interfaces.cli._impl.commands.common import project_root
15
16
  from interfaces.cli._impl.cli_theme import (
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,
17
+ C, p, dim, faint, pink, purple, mint, ok, err, bold,
18
+ gradient, out, pad, render_banner, render_session_line,
19
+ render_prompt, render_footer, render_result,
21
20
  )
22
21
 
23
22
 
@@ -46,34 +45,33 @@ def _resolve_agent(config: AppConfig) -> str:
46
45
 
47
46
 
48
47
  def _print_banner(state: ChatState) -> None:
49
- """启动极简留白"""
48
+ """极简启动版心内, 大留白"""
50
49
  print()
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))
50
+ print(pad(gradient("LBS Agent")))
51
+ print(pad(faint("Autonomous AI Runtime")))
52
+ print()
53
+ # session 信息一行
54
+ info = render_session_line(
55
+ agent=state.agent_name,
56
+ model=state.model_name,
57
+ provider=state.provider_name,
58
+ session_id=state.session_id,
59
+ )
60
+ print(pad(info))
61
61
  print()
62
- print(separator())
63
- print(f" {faint('Ask anything...')}")
64
- print(separator())
62
+ print(pad(render_footer()))
65
63
  print()
66
64
 
67
65
 
68
66
  def _print_turn_header(state: ChatState) -> None:
69
- """输入提示符 — 只有 """
67
+ """输入提示符 — 版心内, 无横线"""
70
68
  print()
71
- print(f" {prompt_symbol()} ", end="", flush=True)
69
+ print(pad(render_prompt()), end="", flush=True)
72
70
 
73
71
 
74
72
  def _print_help() -> None:
75
73
  print()
76
- print(f" {gradient_text('Commands')}")
74
+ out(gradient("Commands"))
77
75
  print()
78
76
  cmds = [
79
77
  ("/help", "显示帮助"),
@@ -86,7 +84,7 @@ def _print_help() -> None:
86
84
  ("/clear", "清屏"),
87
85
  ]
88
86
  for cmd, desc in cmds:
89
- print(f" {pink(cmd.ljust(12))} {faint(desc)}")
87
+ out(f"{pink(cmd.ljust(12))} {faint(desc)}", level=1)
90
88
  print()
91
89
 
92
90
 
@@ -94,8 +92,8 @@ def _cmd_list_agents(state: ChatState) -> None:
94
92
  agents = state.container.config.agents.agents
95
93
  print()
96
94
  for name, a in agents.items():
97
- current = f" {ok('← current')}" if name == state.agent_name else ""
98
- print(f" {agent_tag(name)} {faint(a.display_name)}{current}")
95
+ current = f" {ok('←')}" if name == state.agent_name else ""
96
+ out(f"{p('◐', C.PURPLE)} {pink(name)} {faint(a.display_name)}{current}")
99
97
  print()
100
98
 
101
99
 
@@ -103,21 +101,20 @@ def _cmd_list_models(state: ChatState) -> None:
103
101
  config = state.container.config
104
102
  provider_name = state.provider_name or config.models.default_provider
105
103
  if provider_name not in config.models.providers:
106
- msg = f"无 provider '{provider_name}'"
107
- print(f" {error_text(msg)}")
104
+ print(f" {err(f'无 provider {provider_name!r}')}")
108
105
  return
109
106
  prov = config.models.providers[provider_name]
110
107
  print()
111
- print(f" {faint('provider')} {purple(provider_name)}")
108
+ out(f"{faint('provider')} {purple(provider_name)}")
112
109
  for m in prov.models:
113
110
  marker = f" {ok('←')}" if m == state.model_name else ""
114
- print(f" {model_tag(m)}{marker}")
111
+ out(f"{p('✦', C.PINK)} {pink(m)}{marker}", level=1)
115
112
  print()
116
113
 
117
114
 
118
115
  def _cmd_history(state: ChatState, n: int = 5) -> None:
119
116
  if not state.session_id:
120
- print(f" {faint('无 session')}")
117
+ out(faint("无 session"))
121
118
  return
122
119
  try:
123
120
  messages = list(state.session_service.session_store.load_session_messages(state.session_id))
@@ -129,26 +126,24 @@ def _cmd_history(state: ChatState, n: int = 5) -> None:
129
126
  recent.append((role, content))
130
127
  print()
131
128
  for role, content in recent:
132
- rc = C.PINK if role == "user" else C.MINT_D
133
- print(f" {p(role.ljust(9), rc)} {faint(content)}")
129
+ rc = C.PINK if role == "user" else C.MINT
130
+ out(f"{p(role.ljust(9), rc)} {faint(content)}", level=1)
134
131
  print()
135
132
  except Exception as e:
136
- msg = f"读取失败: {e}"
137
- print(f" {error_text(msg)}")
133
+ out(err(f"读取失败: {e}"))
138
134
 
139
135
 
140
136
  def _cmd_status(state: ChatState) -> None:
141
137
  elapsed = int(time.time() - state.started_at)
142
- rows = [
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")),
149
- ]
138
+ info = render_session_line(
139
+ agent=state.agent_name,
140
+ model=state.model_name,
141
+ provider=state.provider_name,
142
+ session_id=state.session_id,
143
+ )
150
144
  print()
151
- print(session_card(rows))
145
+ out(info)
146
+ out(faint(f"turns {state.turn_count} · {elapsed}s"))
152
147
  print()
153
148
 
154
149
 
@@ -165,14 +160,14 @@ def _handle_command(state: ChatState, line: str) -> bool:
165
160
  state.session_id = ""
166
161
  state.turn_count = 0
167
162
  state.started_at = time.time()
168
- print(f" {ok('◇')} {faint('new session')}")
163
+ out(f"{ok('◇')} {faint('new session')}")
169
164
  elif cmd == "/session":
170
165
  if arg:
171
166
  state.session_id = arg
172
- print(f" {ok('◇')} {faint('switched to')} {pink(arg)}")
167
+ out(f"{ok('◇')} {faint('')} {pink(arg)}")
173
168
  else:
174
169
  sid = state.session_id or "(none)"
175
- print(f" {faint('session:')} {sid}")
170
+ out(f"{faint('session')} {sid}")
176
171
  elif cmd == "/status":
177
172
  _cmd_status(state)
178
173
  elif cmd == "/agent":
@@ -182,16 +177,15 @@ def _handle_command(state: ChatState, line: str) -> bool:
182
177
  agents = state.container.config.agents.agents
183
178
  if arg in agents:
184
179
  state.agent_name = arg
185
- print(f" {ok('◇')} {faint('agent')} {agent_tag(arg)}")
180
+ out(f"{ok('◇')} {faint('')} {p('◐', C.PURPLE)} {pink(arg)}")
186
181
  else:
187
- msg = f"无 agent '{arg}'"
188
- print(f" {error_text(msg)}")
182
+ out(err(f"无 agent '{arg}'"))
189
183
  elif cmd == "/model":
190
184
  if not arg:
191
185
  _cmd_list_models(state)
192
186
  else:
193
187
  state.model_name = arg
194
- print(f" {ok('◇')} {faint('model')} {model_tag(arg)}")
188
+ out(f"{ok('◇')} {faint('')} {p('✦', C.PINK)} {pink(arg)}")
195
189
  elif cmd == "/clear":
196
190
  print("\033[2J\033[H", end="")
197
191
  elif cmd == "/history":
@@ -199,16 +193,15 @@ def _handle_command(state: ChatState, line: str) -> bool:
199
193
  n = int(arg) if arg else 5
200
194
  _cmd_history(state, n)
201
195
  except ValueError:
202
- print(f" {error_text('/history [n] — n 须为整数')}")
196
+ out(err("/history [n] — n 须为整数"))
203
197
  elif cmd == "/save":
204
- print(f" {faint('session 每次 turn 自动保存')}")
198
+ out(faint("session 每次 turn 自动保存"))
205
199
  else:
206
- print(f" {error_text(f'未知命令: {cmd}')}")
200
+ out(err(f"未知命令: {cmd}"))
207
201
  return True
208
202
 
209
203
 
210
204
  def _run_turn(state: ChatState, user_input: str) -> None:
211
- """跑一轮对话"""
212
205
  t0 = time.time()
213
206
  try:
214
207
  result = state.orchestrator.run_agent_sync(
@@ -220,21 +213,25 @@ def _run_turn(state: ChatState, user_input: str) -> None:
220
213
  elapsed = time.time() - t0
221
214
  if result.success:
222
215
  content = (result.content or "").strip()
223
- print(f"\n{result_text(content)}")
216
+ print()
217
+ for line in content.split("\n"):
218
+ out(p(line, C.TEXT))
224
219
  else:
225
220
  e = result.error or "(无错误信息)"
226
- print(f"\n {error_text(e[:300])}")
227
- print(usage_line(elapsed, result.iterations, result.total_tool_calls))
221
+ out(err(e[:300]))
222
+ # 用量 — 缩进, 最弱色
223
+ print()
224
+ out(faint(f" {elapsed:.1f}s · iter {result.iterations} · tools {result.total_tool_calls}"))
228
225
  if not state.session_id:
229
226
  sid = result.session_id if hasattr(result, "session_id") and result.session_id else ""
230
227
  state.session_id = sid or f"chat-{int(state.started_at)}"
231
228
  except KeyboardInterrupt:
232
- print(f"\n {p('⏸', C.WARN)} {faint('已中断')}")
229
+ print()
230
+ out(f"{p('⏸', C.WARN)} {faint('已中断')}")
233
231
  except Exception as e:
234
232
  elapsed = time.time() - t0
235
- msg = f"{type(e).__name__}: {str(e)[:200]}"
236
- print(f"\n {error_text(msg)}")
237
- print(f" {faint(f'{elapsed:.1f}s')}")
233
+ out(err(f"{type(e).__name__}: {str(e)[:200]}"))
234
+ out(faint(f" {elapsed:.1f}s"))
238
235
 
239
236
 
240
237
  def cmd_chat(
@@ -245,7 +242,6 @@ def cmd_chat(
245
242
  query: str = "",
246
243
  session_id: str = "",
247
244
  ) -> int:
248
- """进入 chat REPL 或单次 query。"""
249
245
  container = AgentSystemContainer(config=config, project_root=project_root)
250
246
 
251
247
  if not agent_name:
@@ -253,7 +249,7 @@ def cmd_chat(
253
249
 
254
250
  if agent_name not in config.agents.agents:
255
251
  msg = f"Agent '{agent_name}' 不存在"
256
- print(f" {error_text(msg)}")
252
+ out(err(msg))
257
253
  return 1
258
254
 
259
255
  state = ChatState(
@@ -275,8 +271,6 @@ def cmd_chat(
275
271
 
276
272
  # REPL
277
273
  _print_banner(state)
278
- print(footer())
279
- print()
280
274
 
281
275
  while True:
282
276
  try:
@@ -299,5 +293,5 @@ def cmd_chat(
299
293
  state.turn_count += 1
300
294
  _run_turn(state, line)
301
295
 
302
- print(f"\n {ok('◇')} {faint('Session saved. Bye.')}")
296
+ out(f"{ok('◇')} {faint('Session saved. Bye.')}")
303
297
  return 0
@@ -78,20 +78,18 @@ def cmd_run_agent(config: AppConfig, agent_name: str, task: str) -> None:
78
78
  except Exception as e:
79
79
  print(f" [WARN] session save failed: {e}")
80
80
 
81
- from runtime.orchestration.display import green, red, faint, pink, bold, color, Colors
82
- sep = color("─" * 52, Colors.FAINT)
81
+ from runtime.orchestration.display import green, red, faint, pink, mint, bold
83
82
  icon = green("✓") if result.success else red("✕")
84
- print(f"\n{sep}")
83
+ print()
85
84
  print(f" {icon} {bold(result.agent_name)}")
86
85
  parts = [
87
86
  faint(f"iter {result.iterations}"),
88
87
  faint(f"tools {result.total_tool_calls}"),
89
- color(f"{result.duration_seconds:.1f}s", Colors.MINT_D),
88
+ mint(f"{result.duration_seconds:.1f}s"),
90
89
  ]
91
- print(" " + faint(" · ").join(parts))
90
+ print(f" {faint(' · ').join(parts)}")
92
91
  if result.error:
93
92
  print(f" {red(result.error[:200])}")
94
- print(sep)
95
93
  print()
96
94
  if result.content:
97
95
  print(result.content)
@@ -100,10 +98,9 @@ def cmd_run_agent(config: AppConfig, agent_name: str, task: str) -> None:
100
98
  print()
101
99
 
102
100
  if result.steps:
103
- print(f" {faint('trace')}")
104
101
  for step in result.steps:
105
102
  tools = ", ".join(t.name for t in step.tool_execs) if step.tool_execs else "-"
106
- print(f" {faint(f'{step.iteration:02d}')} {pink('·')} {faint(tools)}")
103
+ print(f" {faint(f'{step.iteration:02d}')} {pink('·')} {faint(tools)}")
107
104
  print()
108
105
  finally:
109
106
  container.shutdown()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baimingtao/lbs-agent",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -252,19 +252,18 @@ class AgentLoop:
252
252
 
253
253
  logger.info("[%s] loop started (%s): %s", self.config.name, mode, task[:100])
254
254
  if self.config.report_progress:
255
- from runtime.orchestration.display import pink, purple, mint, faint, bold, color, Colors
256
- sep = color("─" * 52, Colors.FAINT)
257
- self._print(f"\n{sep}")
255
+ from runtime.orchestration.display import pink, purple, mint, faint, bold
258
256
  agent_label = self.config.display_name or self.config.name
257
+ self._print("")
259
258
  self._print(f" {purple('◐')} {bold(agent_label)}")
260
259
  chain = [self.config.model] + self.config.fallback_models
261
260
  model_str = faint(" → ").join(pink(m) for m in chain)
262
- self._print(f" {model_str}")
261
+ self._print(f" {model_str}")
263
262
  tools_list = self.config.tools or []
264
263
  if tools_list:
265
264
  tools_str = faint(", ").join(mint(t) for t in tools_list)
266
- self._print(f" {faint('tools')} {tools_str}")
267
- self._print(f"{sep}")
265
+ self._print(f" {tools_str}")
266
+ self._print("")
268
267
 
269
268
  return tools_schema
270
269
 
@@ -2000,20 +1999,19 @@ class AgentLoop:
2000
1999
  compression_results=list(self.compression_results),
2001
2000
  )
2002
2001
 
2003
- from runtime.orchestration.display import green, red, faint, pink, purple, mint, color, Colors, bold
2004
- sep = color("─" * 52, Colors.FAINT)
2005
- self._print(f"\n{sep}")
2002
+ from runtime.orchestration.display import green, red, faint, mint, bold
2006
2003
  icon = green("✓") if success else red("✕")
2004
+ self._print("")
2007
2005
  self._print(f" {icon} {bold(self.config.name)}")
2008
2006
  parts = [
2009
2007
  faint(f"iter {self.current_iteration}"),
2010
2008
  faint(f"tools {self._tool_call_count}"),
2011
- color(f"{duration:.1f}s", Colors.MINT_D),
2009
+ mint(f"{duration:.1f}s"),
2012
2010
  ]
2013
- self._print(" " + faint(" · ").join(parts))
2011
+ self._print(f" {faint(' · ').join(parts)}")
2014
2012
  if error:
2015
2013
  self._print(f" {red(error[:200])}")
2016
- self._print(f"{sep}\n")
2014
+ self._print("")
2017
2015
 
2018
2016
  logger.info(
2019
2017
  "[%s] loop finished: success=%s iterations=%d tools=%d duration=%.1fs",