sycommon-python-lib 0.2.7a0__py3-none-any.whl → 0.2.7a1__py3-none-any.whl

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.
sycli/acp/__init__.py CHANGED
@@ -1,38 +1,16 @@
1
- """ACP (Agent Communication Protocol) support for sycli.
1
+ """ACP (Agent Client Protocol — Zed) support for sycli.
2
2
 
3
- Dynamically exposes local CLI tools as ACP agents.
4
- """
5
-
6
- # Patch uvicorn config type aliases for compatibility with uvicorn>=0.34
7
- # acp-sdk uses uvicorn.config.LoopSetupType etc. which were removed in uvicorn 0.48+
8
- import uvicorn.config as _uvicorn_config
9
-
10
- _TYPE_ALIASES = (
11
- "LoopSetupType",
12
- "HTTPProtocolType",
13
- "WSProtocolType",
14
- "LifespanType",
15
- "InterfaceType",
16
- )
17
- for _alias in _TYPE_ALIASES:
18
- if not hasattr(_uvicorn_config, _alias):
19
- setattr(_uvicorn_config, _alias, str)
3
+ 把本地 CLI 工具暴露成 Zed ACP agent(stdio JSON-RPC),可被任何 ACP client
4
+ (Zed、或我们 sycommon 的 ACP client)连接调用。
20
5
 
21
- if not hasattr(_uvicorn_config, "LOGGING_CONFIG"):
22
- _uvicorn_config.LOGGING_CONFIG = {
23
- "version": 1,
24
- "disable_existing_loggers": False,
25
- "formatters": {},
26
- "handlers": {},
27
- "loggers": {},
28
- }
29
-
30
- del _uvicorn_config, _TYPE_ALIASES, _alias
6
+ 使用官方 `agent-client-protocol`(import `acp`)的 stdio transport,
7
+ 照 examples/echo_agent.py 模板实现 Agent 协议。
8
+ """
31
9
 
32
10
  from sycli.acp.config import ACPConfig, ToolArgument, ToolDef
33
11
  from sycli.acp.executor import build_cli_command, execute_command, stream_command
34
12
  from sycli.acp.registry import CLIToolAgent, build_agents
35
- from sycli.acp.server import create_acp_server, run_acp_server
13
+ from sycli.acp.server import run_acp_server
36
14
 
37
15
  __all__ = [
38
16
  "ACPConfig",
@@ -41,7 +19,6 @@ __all__ = [
41
19
  "ToolDef",
42
20
  "build_agents",
43
21
  "build_cli_command",
44
- "create_acp_server",
45
22
  "execute_command",
46
23
  "run_acp_server",
47
24
  "stream_command",
sycli/acp/registry.py CHANGED
@@ -1,20 +1,45 @@
1
- """Dynamic ACP agent registry.
1
+ """Zed ACP agent registry.
2
2
 
3
- Creates CLIToolAgent (AgentManifest subclass) instances at runtime,
4
- one per ToolDef in the configuration. Each agent wraps a CLI tool
5
- and executes it via the executor module when a run is requested.
3
+ .sycli/acp.yaml 里声明的 CLI 工具暴露成一个 Zed ACP Agent(stdio)。
4
+ 照官方 examples/echo_agent.py 模板实现 Agent 协议。
5
+
6
+ 设计:一个 agent 进程承载多个 CLI 工具。prompt 到来时:
7
+ 1. 从输入文本解析 `tool=<name>`(或第一段当作工具名),定位 ToolDef
8
+ 2. build_cli_command 拼命令 → stream_command/execute_command 执行
9
+ 3. 通过 session_update 把输出(逐行流式或一次性)推给 client
10
+ 4. 返回 PromptResponse(stop_reason="end_turn")
6
11
  """
7
12
 
8
13
  from __future__ import annotations
9
14
 
10
15
  import os
11
16
  from collections.abc import AsyncGenerator
12
- from typing import TYPE_CHECKING
13
-
14
- from acp_sdk.models import Message, MessagePart, Metadata
15
- from acp_sdk.server.agent import AgentManifest
16
- from acp_sdk.server.context import Context
17
- from acp_sdk.server.types import RunYield, RunYieldResume
17
+ from typing import TYPE_CHECKING, Any
18
+ from uuid import uuid4
19
+
20
+ from acp import (
21
+ Agent,
22
+ InitializeResponse,
23
+ NewSessionResponse,
24
+ PromptResponse,
25
+ text_block,
26
+ update_agent_message,
27
+ update_plan,
28
+ )
29
+ from acp.interfaces import Client
30
+ from acp.schema import (
31
+ AudioContentBlock,
32
+ ClientCapabilities,
33
+ EmbeddedResourceContentBlock,
34
+ HttpMcpServer,
35
+ ImageContentBlock,
36
+ Implementation,
37
+ McpServerStdio,
38
+ PlanEntry,
39
+ ResourceContentBlock,
40
+ SseMcpServer,
41
+ TextContentBlock,
42
+ )
18
43
 
19
44
  from sycli.acp.executor import build_cli_command, execute_command, stream_command
20
45
 
@@ -22,134 +47,177 @@ if TYPE_CHECKING:
22
47
  from sycli.acp.config import ACPConfig, ToolDef
23
48
 
24
49
 
25
- class CLIToolAgent(AgentManifest):
26
- """An AgentManifest that wraps a CLI tool.
50
+ class CLIToolAgent(Agent):
51
+ """一个承载多个 CLI 工具的 Zed ACP agent。
27
52
 
28
- Each instance is configured with a ToolDef. The run() method
29
- is an async generator, enabling both streaming and batch modes
30
- via the ACP SDK executor.
53
+ 每次 prompt:解析工具名 执行对应 CLI session_update 推输出。
31
54
  """
32
55
 
33
- def __init__(self, tool_def: ToolDef, project_root: str) -> None:
34
- self._tool_def = tool_def
35
- self._project_root = project_root
36
-
37
- @property
38
- def name(self) -> str:
39
- return self._tool_def.name
40
-
41
- @property
42
- def description(self) -> str:
43
- return self._tool_def.description or f"CLI tool: {self._tool_def.command}"
56
+ _conn: Client | None = None
44
57
 
45
- @property
46
- def input_content_types(self) -> list[str]:
47
- return ["text/plain"]
48
-
49
- @property
50
- def output_content_types(self) -> list[str]:
51
- return [self._tool_def.output_content_type]
52
-
53
- @property
54
- def metadata(self) -> Metadata:
55
- return Metadata(
56
- tags=["cli-tool", self._tool_def.command],
58
+ def __init__(self, config: "ACPConfig", project_root: str) -> None:
59
+ self._config = config
60
+ self._project_root = project_root
61
+ # 工具名 → ToolDef 索引,便于路由
62
+ self._tools_by_name: dict[str, "ToolDef"] = {t.name: t for t in config.tools}
63
+
64
+ # ---- Agent 协议回调 ----
65
+ def on_connect(self, conn: Client) -> None:
66
+ self._conn = conn
67
+
68
+ async def initialize(
69
+ self,
70
+ protocol_version: int,
71
+ client_capabilities: ClientCapabilities | None = None,
72
+ client_info: Implementation | None = None,
73
+ **kwargs: Any,
74
+ ) -> InitializeResponse:
75
+ return InitializeResponse(protocol_version=protocol_version)
76
+
77
+ async def new_session(
78
+ self,
79
+ cwd: str,
80
+ additional_directories: list[str] | None = None,
81
+ mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio] | None = None,
82
+ **kwargs: Any,
83
+ ) -> NewSessionResponse:
84
+ return NewSessionResponse(session_id=uuid4().hex)
85
+
86
+ async def prompt(
87
+ self,
88
+ prompt: list[
89
+ TextContentBlock
90
+ | ImageContentBlock
91
+ | AudioContentBlock
92
+ | ResourceContentBlock
93
+ | EmbeddedResourceContentBlock
94
+ ],
95
+ session_id: str,
96
+ **kwargs: Any,
97
+ ) -> PromptResponse:
98
+ assert self._conn is not None, "Agent 未连接"
99
+
100
+ # 1. 提取输入文本
101
+ input_text = _extract_input_text_from_blocks(prompt)
102
+
103
+ # 2. 路由到具体 CLI 工具
104
+ tool_def, remaining_input = _resolve_tool(input_text, self._tools_by_name)
105
+
106
+ if tool_def is None:
107
+ # 找不到工具,列出可用工具
108
+ available = ", ".join(self._tools_by_name.keys()) or "(无)"
109
+ await self._conn.session_update(
110
+ session_id=session_id,
111
+ update=update_agent_message(text_block(
112
+ f"❌ 未识别的 CLI 工具。可用工具: {available}\n"
113
+ f"用法: 第一个词为工具名,或用 tool=<name> 指定。"
114
+ )),
115
+ )
116
+ return PromptResponse(stop_reason="end_turn")
117
+
118
+ # 3. 上报计划(让 client 看到要执行什么)
119
+ await self._conn.session_update(
120
+ session_id=session_id,
121
+ update=update_plan([PlanEntry(
122
+ content=f"执行 CLI 工具: {tool_def.name} ({tool_def.command})",
123
+ priority="medium",
124
+ status="in_progress",
125
+ )]),
57
126
  )
58
127
 
59
- async def run(
60
- self, input: list[Message], context: Context
61
- ) -> AsyncGenerator[RunYield, RunYieldResume]:
62
- """Execute the CLI tool and yield results.
63
-
64
- Async generator enabling:
65
- - Stream mode: yields MessagePart per stdout line
66
- - Sync/async mode: yields complete Message at the end
67
-
68
- Flow:
69
- 1. Extract text from input Messages (concatenate text/plain parts)
70
- 2. Build CLI command via build_cli_command()
71
- 3. If stream_output=True: stream_command(), yield MessagePart per line
72
- 4. Otherwise: execute_command(), yield single Message
73
- """
74
- # Step 1: Extract input text
75
- input_text = _extract_input_text(input)
76
-
77
- # Step 2: Build command
78
- command = build_cli_command(self._tool_def, input_text)
79
-
80
- # Step 3: Determine working directory
81
- cwd = self._tool_def.working_dir or self._project_root
82
-
83
- # Step 4: Build environment
84
- env = None
85
- if self._tool_def.env:
86
- env = {**os.environ, **self._tool_def.env}
87
-
88
- if self._tool_def.stream_output:
89
- # Streaming: yield MessagePart per stdout line
128
+ # 4. 拼命令 + 执行环境
129
+ command = build_cli_command(tool_def, remaining_input)
130
+ cwd = tool_def.working_dir or self._project_root
131
+ env = {**os.environ, **tool_def.env} if tool_def.env else None
132
+
133
+ # 5. 执行并推送输出
134
+ if tool_def.stream_output:
90
135
  async for line in stream_command(
91
- command,
92
- cwd=cwd,
93
- timeout=self._tool_def.timeout,
94
- env=env,
136
+ command, cwd=cwd, timeout=tool_def.timeout, env=env,
95
137
  ):
96
- yield MessagePart(
97
- content=line,
98
- content_type="text/plain",
138
+ await self._conn.session_update(
139
+ session_id=session_id,
140
+ update=update_agent_message(text_block(line)),
99
141
  )
100
142
  else:
101
- # Batch: execute and yield complete output
102
143
  result = await execute_command(
103
144
  command,
104
145
  cwd=cwd,
105
- timeout=self._tool_def.timeout,
146
+ timeout=tool_def.timeout,
106
147
  env=env,
107
- max_output_bytes=100_000,
108
- stdin_input=input_text if self._tool_def.input_mode == "stdin" else None,
148
+ max_output_bytes=self._config.max_output_bytes,
149
+ stdin_input=remaining_input if tool_def.input_mode == "stdin" else None,
109
150
  )
110
-
111
- if result.timed_out:
112
- output = f"Error: Command timed out after {self._tool_def.timeout}s\n{result.stdout}"
113
- elif result.exit_code not in self._tool_def.allowed_exit_codes:
114
- parts = []
115
- if result.stdout:
116
- parts.append(result.stdout)
117
- if result.stderr:
118
- parts.append(f"[stderr]\n{result.stderr}")
119
- parts.append(f"Exit code: {result.exit_code}")
120
- output = "\n".join(parts)
121
- else:
122
- output = result.stdout or "(no output)"
123
-
124
- yield Message(
125
- role="agent",
126
- parts=[MessagePart(content=output, content_type="text/plain")],
151
+ output = _format_execution_result(result, tool_def)
152
+ await self._conn.session_update(
153
+ session_id=session_id,
154
+ update=update_agent_message(text_block(output)),
127
155
  )
128
156
 
157
+ return PromptResponse(stop_reason="end_turn")
158
+
129
159
 
130
- def _extract_input_text(messages: list[Message]) -> str:
131
- """Extract and concatenate all text/plain content from input messages."""
160
+ def _extract_input_text_from_blocks(prompt: list[Any]) -> str:
161
+ """ prompt 内容块列表里拼接所有 text/plain 文本。"""
132
162
  parts: list[str] = []
133
- for msg in messages:
134
- for part in msg.parts:
135
- content_type = getattr(part, "content_type", None) or "text/plain"
136
- content = getattr(part, "content", None)
137
- if content_type in ("text/plain", None) and content:
138
- parts.append(content)
163
+ for block in prompt:
164
+ text = block.get("text", "") if isinstance(block, dict) else getattr(block, "text", "")
165
+ if text:
166
+ parts.append(text)
139
167
  return "\n".join(parts)
140
168
 
141
169
 
142
- def build_agents(config: ACPConfig, project_root: str) -> list[AgentManifest]:
143
- """Create AgentManifest instances for all tools in config.
170
+ def _resolve_tool(
171
+ input_text: str, tools_by_name: dict[str, "ToolDef"]
172
+ ) -> tuple["ToolDef | None", str]:
173
+ """从输入文本解析工具名,返回 (ToolDef, 剩余输入)。
174
+
175
+ 支持两种写法:
176
+ - `tool=<name> <args...>`(显式指定)
177
+ - `<name> <args...>`(第一段当工具名)
178
+ """
179
+ stripped = input_text.strip()
180
+ if not stripped:
181
+ return None, ""
182
+
183
+ # tool=xxx 形式
184
+ if stripped.startswith("tool="):
185
+ head, _, rest = stripped.partition(" ")
186
+ name = head[len("tool="):].strip()
187
+ if name in tools_by_name:
188
+ return tools_by_name[name], rest.strip()
189
+ return None, stripped
190
+
191
+ # 第一段当工具名
192
+ first, _, rest = stripped.partition(" ")
193
+ if first in tools_by_name:
194
+ return tools_by_name[first], rest.strip()
195
+ return None, stripped
196
+
197
+
198
+ def _format_execution_result(result: Any, tool_def: "ToolDef") -> str:
199
+ """把 executor.ExecutionResult 格式化成给 client 看的文本。"""
200
+ if result.timed_out:
201
+ return f"Error: Command timed out after {tool_def.timeout}s\n{result.stdout}"
202
+ if result.exit_code not in tool_def.allowed_exit_codes:
203
+ parts = []
204
+ if result.stdout:
205
+ parts.append(result.stdout)
206
+ if result.stderr:
207
+ parts.append(f"[stderr]\n{result.stderr}")
208
+ parts.append(f"Exit code: {result.exit_code}")
209
+ return "\n".join(parts)
210
+ return result.stdout or "(no output)"
211
+
212
+
213
+ def build_agents(config: "ACPConfig", project_root: str) -> CLIToolAgent:
214
+ """创建承载所有 CLI 工具的 Zed ACP agent 实例。
144
215
 
145
216
  Args:
146
- config: ACP configuration with tool definitions.
147
- project_root: Project root directory for command execution.
217
+ config: ACP 配置(含 tools 列表)。
218
+ project_root: CLI 命令执行根目录。
148
219
 
149
220
  Returns:
150
- List of CLIToolAgent instances ready for Server.register().
221
+ CLIToolAgent 实例,ready for acp.run_agent()
151
222
  """
152
- return [
153
- CLIToolAgent(tool_def=tool_def, project_root=project_root)
154
- for tool_def in config.tools
155
- ]
223
+ return CLIToolAgent(config=config, project_root=project_root)
sycli/acp/server.py CHANGED
@@ -1,69 +1,33 @@
1
- """ACP server bootstrap for sycli.
1
+ """Zed ACP server bootstrap for sycli.
2
2
 
3
- Creates and starts an ACP server with all CLI tool agents registered.
4
- Uses the acp_sdk FastAPI app directly with uvicorn to avoid version
5
- compatibility issues between acp-sdk and uvicorn's positional parameters.
3
+ 启动一个 Zed ACP agent(stdio JSON-RPC),把 .sycli/acp.yaml 里声明的 CLI 工具
4
+ 作为该 agent 的能力暴露。照官方 examples/echo_agent.py run_agent 模式。
5
+
6
+ 注意:Zed ACP 是 stdio 协议,不像旧 acp-sdk 走 HTTP/FastAPI/uvicorn。
7
+ 每个 CLI 工具不是独立的 agent,而是同一个 agent 用「会话内识别指令 → 路由到
8
+ 对应工具」的方式承载(一个 agent 进程,多工具)。
6
9
  """
7
10
 
8
11
  from __future__ import annotations
9
12
 
10
13
  from typing import TYPE_CHECKING
11
14
 
12
- from acp_sdk.server.app import create_app
13
- import uvicorn
14
-
15
15
  from sycli.acp.registry import build_agents
16
16
 
17
17
  if TYPE_CHECKING:
18
- from acp_sdk.server import Server
19
18
  from sycli.acp.config import ACPConfig
20
19
 
21
20
 
22
- def create_acp_app(config: ACPConfig, project_root: str):
23
- """Create a FastAPI app with all registered tool agents.
24
-
25
- Args:
26
- config: ACP configuration (loaded from .sycli/acp.yaml).
27
- project_root: Project root directory for command execution.
28
-
29
- Returns:
30
- FastAPI application instance with ACP routes.
31
- """
32
- agents = build_agents(config, project_root)
33
- app = create_app(*agents)
34
- return app
35
-
36
-
37
- def create_acp_server(config: ACPConfig, project_root: str) -> Server:
38
- """Create and configure an ACP Server with all registered tool agents.
21
+ def run_acp_server(config: ACPConfig, project_root: str) -> None:
22
+ """启动 Zed ACP agent(阻塞,直到 client 断开或收到关闭信号)。
39
23
 
40
24
  Args:
41
- config: ACP configuration (loaded from .sycli/acp.yaml).
42
- project_root: Project root directory for command execution.
43
-
44
- Returns:
45
- Configured Server instance (not yet started).
25
+ config: ACP 配置(来自 .sycli/acp.yaml),含 tools 列表。
26
+ project_root: CLI 命令执行的根目录。
46
27
  """
47
- from acp_sdk.server import Server
48
-
49
- server = Server()
50
- agents = build_agents(config, project_root)
51
- server.register(*agents)
52
- return server
53
-
28
+ import asyncio
54
29
 
55
- def run_acp_server(config: ACPConfig, project_root: str) -> None:
56
- """Create and start the ACP server (blocking until shutdown).
57
-
58
- This is the main entry point called from acp_cmd.py.
59
- Uses uvicorn directly to avoid acp-sdk / uvicorn version mismatches.
60
- """
61
- app = create_acp_app(config, project_root)
30
+ from acp import run_agent
62
31
 
63
- uvicorn.run(
64
- app,
65
- host=config.host,
66
- port=config.port,
67
- log_level="info",
68
- headers=[("server", "acp")],
69
- )
32
+ agent = build_agents(config, project_root)
33
+ asyncio.run(run_agent(agent))
sycli/cli.py CHANGED
@@ -169,10 +169,8 @@ def _create_parser() -> argparse.ArgumentParser:
169
169
  cdp_sub.add_parser("interactive", help="Start interactive CDP REPL")
170
170
 
171
171
  # ── acp ──
172
- p_acp = sub.add_parser("acp", help="Start ACP server exposing CLI tools as agents")
172
+ p_acp = sub.add_parser("acp", help="Start Zed ACP agent exposing CLI tools over stdio")
173
173
  p_acp.add_argument("--config", "-c", help="Path to ACP config file (default: .sycli/acp.yaml)")
174
- p_acp.add_argument("--host", help="Override server host")
175
- p_acp.add_argument("--port", "-p", type=int, help="Override server port")
176
174
  p_acp.add_argument("--project-root", "-r", default=".", help="Project root directory")
177
175
 
178
176
  # ── version ──
sycli/commands/acp_cmd.py CHANGED
@@ -1,7 +1,8 @@
1
- """sycli acp command — start ACP server exposing CLI tools as agents."""
1
+ """sycli acp command — start a Zed ACP agent exposing CLI tools over stdio."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import signal
6
7
  import sys
7
8
  from pathlib import Path
@@ -17,10 +18,10 @@ def handle_acp(args) -> int:
17
18
  2. Load .sycli/acp.yaml (or --config path)
18
19
  3. Validate tool definitions
19
20
  4. Print registered agents summary
20
- 5. Start ACP server (blocking)
21
+ 5. Start Zed ACP agent over stdio (blocking)
21
22
 
22
23
  Args:
23
- args: Parsed argparse namespace with config, host, port, project_root.
24
+ args: Parsed argparse namespace with config, project_root.
24
25
 
25
26
  Returns:
26
27
  Exit code (0 for success, 1 for error).
@@ -28,22 +29,23 @@ def handle_acp(args) -> int:
28
29
  project_root = Path(args.project_root or ".").resolve()
29
30
  sycli_dir = project_root / ".sycli"
30
31
 
31
- if not sycli_dir.exists():
32
- error(".sycli/ not found. Run `sycli init` first.")
33
- return 1
34
-
35
32
  # Determine config path
36
- config_path = Path(args.config) if args.config else sycli_dir / "acp.yaml"
37
- if not config_path.exists():
38
- config_path = sycli_dir / "acp.json"
33
+ # 显式 --config 优先(允许指向任意位置,不强制 .sycli/);
34
+ # 否则回退到 <project_root>/.sycli/acp.{yaml,json}
35
+ if args.config:
36
+ config_path = Path(args.config)
37
+ else:
38
+ config_path = sycli_dir / "acp.yaml"
39
+ if not config_path.exists():
40
+ config_path = sycli_dir / "acp.json"
39
41
 
40
42
  if not config_path.exists():
41
- error(f"ACP config not found at {sycli_dir / 'acp.yaml'}")
43
+ if not args.config and not sycli_dir.exists():
44
+ error(".sycli/ not found. Run `sycli init` first, or 用 --config 指定外部配置。")
45
+ error(f"ACP config not found at {config_path}")
42
46
  error("Create one with your tool definitions. Example:")
43
47
  error("""
44
- # .sycli/acp.yaml
45
- host: "127.0.0.1"
46
- port: 8100
48
+ # acp.yaml
47
49
  tools:
48
50
  - name: "git-status"
49
51
  command: "git"
@@ -62,33 +64,28 @@ def handle_acp(args) -> int:
62
64
  error(f"Failed to load ACP config: {e}")
63
65
  return 1
64
66
 
65
- # Override port/host from CLI args
66
- if args.host:
67
- config.host = args.host
68
- if args.port:
69
- config.port = args.port
70
-
71
67
  if not config.tools:
72
68
  error("No tools defined in ACP config. Add tools to acp.yaml.")
73
69
  return 1
74
70
 
75
- # Print summary
76
- header("ACP Server Configuration")
77
- info(f"Config: {config_path}")
78
- info(f"Host: {config.host}")
79
- info(f"Port: {config.port}")
80
- info(f"Agents: {len(config.tools)} registered")
71
+ # Print summary to stderr(stdout 留给 stdio JSON-RPC)
72
+ header("Zed ACP Agent Configuration")
73
+ info(f"Config: {config_path}")
74
+ info(f"Transport: stdio (JSON-RPC)")
75
+ info(f"Agents: {len(config.tools)} CLI tools exposed")
81
76
  print()
82
77
  for tool in config.tools:
83
78
  desc = tool.description or "(no description)"
84
79
  info(f" {tool.name}: {tool.command} {' '.join(tool.args) if tool.args else ''}")
85
80
  info(f" {desc}")
86
81
  print()
82
+ print("-" * 60, file=sys.stderr)
83
+ info("Agent ready — waiting for ACP client on stdin/stdout...")
84
+ print("-" * 60, file=sys.stderr)
87
85
 
88
- # Start server
86
+ # Start agent (stdio, blocking)
89
87
  from sycli.acp.server import run_acp_server
90
88
 
91
- # Graceful shutdown on SIGINT
92
89
  _shutdown = False
93
90
 
94
91
  def _signal_handler(sig, frame):
@@ -96,18 +93,17 @@ def handle_acp(args) -> int:
96
93
  if _shutdown:
97
94
  sys.exit(1)
98
95
  _shutdown = True
99
- warning("\nShutting down ACP server...")
96
+ warning("\nShutting down ACP agent...")
100
97
 
101
98
  signal.signal(signal.SIGINT, _signal_handler)
102
99
 
103
100
  try:
104
- success(f"Starting ACP server on http://{config.host}:{config.port}")
105
101
  run_acp_server(config, str(project_root))
106
102
  except KeyboardInterrupt:
107
- warning("ACP server stopped.")
103
+ warning("ACP agent stopped.")
108
104
  return 0
109
105
  except Exception as e:
110
- error(f"ACP server failed: {e}")
106
+ error(f"ACP agent failed: {e}")
111
107
  import traceback
112
108
 
113
109
  traceback.print_exc()