fp-terminal 0.1.2__tar.gz
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.
- fp_terminal-0.1.2/PKG-INFO +175 -0
- fp_terminal-0.1.2/README.md +157 -0
- fp_terminal-0.1.2/pyproject.toml +34 -0
- fp_terminal-0.1.2/setup.cfg +4 -0
- fp_terminal-0.1.2/src/fp_cli/__init__.py +3 -0
- fp_terminal-0.1.2/src/fp_cli/main.py +232 -0
- fp_terminal-0.1.2/src/fp_terminal.egg-info/PKG-INFO +175 -0
- fp_terminal-0.1.2/src/fp_terminal.egg-info/SOURCES.txt +9 -0
- fp_terminal-0.1.2/src/fp_terminal.egg-info/dependency_links.txt +1 -0
- fp_terminal-0.1.2/src/fp_terminal.egg-info/requires.txt +2 -0
- fp_terminal-0.1.2/src/fp_terminal.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fp-terminal
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: 五块卵石 - 终端 REPL
|
|
5
|
+
Author: zpb
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: agent,cli
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: fp-core>=0.1.0
|
|
17
|
+
Requires-Dist: prompt-toolkit>=3.0.40
|
|
18
|
+
|
|
19
|
+
# fp-terminal — 五块卵石终端 REPL
|
|
20
|
+
|
|
21
|
+
[](https://pypi.org/project/fp-terminal/)
|
|
22
|
+
[](https://pypi.org/project/fp-terminal/)
|
|
23
|
+
[](LICENSE)
|
|
24
|
+
|
|
25
|
+
## 简介
|
|
26
|
+
|
|
27
|
+
**fp-terminal** 是五块卵石(Five Pebbles)Agent 框架的交互式命令行界面。它基于 `prompt-toolkit` 构建,提供了语法高亮、自动补全、多行编辑、历史记录等现代终端体验。
|
|
28
|
+
|
|
29
|
+
> 通常通过主包 `fp` 安装,无需单独安装。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 特性
|
|
34
|
+
|
|
35
|
+
- **REPL 交互模式** — 实时与 Agent 对话,流式输出响应
|
|
36
|
+
- **命令系统** — 内置 `/` 命令用于控制会话、加载插件、切换模型等
|
|
37
|
+
- **语法高亮** — 代码块自动识别并着色
|
|
38
|
+
- **自动补全** — 命令与参数 Tab 补全
|
|
39
|
+
- **多行编辑** — 支持粘贴多行文本或代码
|
|
40
|
+
- **会话历史** — 对话历史持久化存储,跨会话可回溯
|
|
41
|
+
- **多彩输出** — 基于 `rich` 的渲染引擎
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 安装
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install fp-terminal
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
要求 Python >= 3.11。
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 快速使用
|
|
56
|
+
|
|
57
|
+
### 启动 REPL
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
fp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
或显式调用:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python -m fp_cli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
启动后进入交互模式:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
╭─ Five Pebbles ───────────────────────────╮
|
|
73
|
+
│ 输入 /help 查看可用命令 │
|
|
74
|
+
│ 按 Ctrl+D 或输入 /exit 退出 │
|
|
75
|
+
╰──────────────────────────────────────────╯
|
|
76
|
+
|
|
77
|
+
🤖 你好!我是五块卵石,请告诉我你的问题。
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 开始对话
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
>> 请用 Python 写一个快速排序
|
|
84
|
+
# 直接输入即发送消息,Agent 会流式输出回复
|
|
85
|
+
|
|
86
|
+
>> /save session-quicksort
|
|
87
|
+
# 保存当前会话
|
|
88
|
+
|
|
89
|
+
>> /load session-quicksort
|
|
90
|
+
# 恢复历史会话
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 命令列表
|
|
96
|
+
|
|
97
|
+
| 命令 | 别名 | 说明 |
|
|
98
|
+
|------|------|------|
|
|
99
|
+
| `/help` | `/h` | 显示帮助信息 |
|
|
100
|
+
| `/exit` | `/q`, `/quit` | 退出 REPL |
|
|
101
|
+
| `/clear` | `/c` | 清屏 |
|
|
102
|
+
| `/save <name>` | — | 保存当前会话 |
|
|
103
|
+
| `/load <name>` | — | 加载指定会话 |
|
|
104
|
+
| `/list` | `/ls` | 列出所有会话 |
|
|
105
|
+
| `/delete <name>` | `/rm` | 删除指定会话 |
|
|
106
|
+
| `/model <name>` | — | 切换 LLM 模型 |
|
|
107
|
+
| `/plugin list` | — | 列出已加载的插件 |
|
|
108
|
+
| `/plugin load <name>` | — | 动态加载插件 |
|
|
109
|
+
| `/tool list` | — | 列出可用工具 |
|
|
110
|
+
| `/env` | — | 查看当前环境配置 |
|
|
111
|
+
| `/config <key>=<value>` | — | 动态修改配置 |
|
|
112
|
+
| `/reset` | — | 重置当前会话上下文 |
|
|
113
|
+
| `/export <format>` | — | 导出会话(json/md) |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 快捷键
|
|
118
|
+
|
|
119
|
+
| 快捷键 | 功能 |
|
|
120
|
+
|--------|------|
|
|
121
|
+
| `Tab` | 自动补全命令/参数 |
|
|
122
|
+
| `Ctrl+C` | 中断当前输出 |
|
|
123
|
+
| `Ctrl+D` | 退出 REPL |
|
|
124
|
+
| `Ctrl+L` | 清屏 |
|
|
125
|
+
| `Ctrl+R` | 反向搜索历史 |
|
|
126
|
+
| `Up/Down` | 浏览历史命令 |
|
|
127
|
+
| `Shift+Enter` | 换行(多行输入) |
|
|
128
|
+
| `Ctrl+W` | 删除前一个词 |
|
|
129
|
+
| `Ctrl+U` | 删除整行 |
|
|
130
|
+
| `Ctrl+K` | 删除光标至行尾 |
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 配置
|
|
135
|
+
|
|
136
|
+
通过命令行标志自定义启动行为:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# 指定配置文件
|
|
140
|
+
fp --config ./my-config.yaml
|
|
141
|
+
|
|
142
|
+
# 指定初始模型
|
|
143
|
+
fp --model claude-3-opus
|
|
144
|
+
|
|
145
|
+
# 静默模式(跳过启动横幅)
|
|
146
|
+
fp --quiet
|
|
147
|
+
|
|
148
|
+
# 调试模式
|
|
149
|
+
fp --debug
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
配置文件示例(YAML):
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
cli:
|
|
156
|
+
theme: dark # dark / light
|
|
157
|
+
multi_line: true # 启用多行编辑
|
|
158
|
+
max_history: 1000 # 历史记录条数
|
|
159
|
+
stream: true # 流式输出
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 依赖
|
|
165
|
+
|
|
166
|
+
| 依赖 | 版本 | 用途 |
|
|
167
|
+
|------|------|------|
|
|
168
|
+
| `fp-core` | >= 0.1.0 | Agent 核心引擎 |
|
|
169
|
+
| `prompt-toolkit` | >= 3.0.40 | 终端交互框架 |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 许可
|
|
174
|
+
|
|
175
|
+
MIT © zpb
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# fp-terminal — 五块卵石终端 REPL
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/fp-terminal/)
|
|
4
|
+
[](https://pypi.org/project/fp-terminal/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
## 简介
|
|
8
|
+
|
|
9
|
+
**fp-terminal** 是五块卵石(Five Pebbles)Agent 框架的交互式命令行界面。它基于 `prompt-toolkit` 构建,提供了语法高亮、自动补全、多行编辑、历史记录等现代终端体验。
|
|
10
|
+
|
|
11
|
+
> 通常通过主包 `fp` 安装,无需单独安装。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 特性
|
|
16
|
+
|
|
17
|
+
- **REPL 交互模式** — 实时与 Agent 对话,流式输出响应
|
|
18
|
+
- **命令系统** — 内置 `/` 命令用于控制会话、加载插件、切换模型等
|
|
19
|
+
- **语法高亮** — 代码块自动识别并着色
|
|
20
|
+
- **自动补全** — 命令与参数 Tab 补全
|
|
21
|
+
- **多行编辑** — 支持粘贴多行文本或代码
|
|
22
|
+
- **会话历史** — 对话历史持久化存储,跨会话可回溯
|
|
23
|
+
- **多彩输出** — 基于 `rich` 的渲染引擎
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 安装
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install fp-terminal
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
要求 Python >= 3.11。
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 快速使用
|
|
38
|
+
|
|
39
|
+
### 启动 REPL
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
fp
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
或显式调用:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
python -m fp_cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
启动后进入交互模式:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
╭─ Five Pebbles ───────────────────────────╮
|
|
55
|
+
│ 输入 /help 查看可用命令 │
|
|
56
|
+
│ 按 Ctrl+D 或输入 /exit 退出 │
|
|
57
|
+
╰──────────────────────────────────────────╯
|
|
58
|
+
|
|
59
|
+
🤖 你好!我是五块卵石,请告诉我你的问题。
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 开始对话
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
>> 请用 Python 写一个快速排序
|
|
66
|
+
# 直接输入即发送消息,Agent 会流式输出回复
|
|
67
|
+
|
|
68
|
+
>> /save session-quicksort
|
|
69
|
+
# 保存当前会话
|
|
70
|
+
|
|
71
|
+
>> /load session-quicksort
|
|
72
|
+
# 恢复历史会话
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 命令列表
|
|
78
|
+
|
|
79
|
+
| 命令 | 别名 | 说明 |
|
|
80
|
+
|------|------|------|
|
|
81
|
+
| `/help` | `/h` | 显示帮助信息 |
|
|
82
|
+
| `/exit` | `/q`, `/quit` | 退出 REPL |
|
|
83
|
+
| `/clear` | `/c` | 清屏 |
|
|
84
|
+
| `/save <name>` | — | 保存当前会话 |
|
|
85
|
+
| `/load <name>` | — | 加载指定会话 |
|
|
86
|
+
| `/list` | `/ls` | 列出所有会话 |
|
|
87
|
+
| `/delete <name>` | `/rm` | 删除指定会话 |
|
|
88
|
+
| `/model <name>` | — | 切换 LLM 模型 |
|
|
89
|
+
| `/plugin list` | — | 列出已加载的插件 |
|
|
90
|
+
| `/plugin load <name>` | — | 动态加载插件 |
|
|
91
|
+
| `/tool list` | — | 列出可用工具 |
|
|
92
|
+
| `/env` | — | 查看当前环境配置 |
|
|
93
|
+
| `/config <key>=<value>` | — | 动态修改配置 |
|
|
94
|
+
| `/reset` | — | 重置当前会话上下文 |
|
|
95
|
+
| `/export <format>` | — | 导出会话(json/md) |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 快捷键
|
|
100
|
+
|
|
101
|
+
| 快捷键 | 功能 |
|
|
102
|
+
|--------|------|
|
|
103
|
+
| `Tab` | 自动补全命令/参数 |
|
|
104
|
+
| `Ctrl+C` | 中断当前输出 |
|
|
105
|
+
| `Ctrl+D` | 退出 REPL |
|
|
106
|
+
| `Ctrl+L` | 清屏 |
|
|
107
|
+
| `Ctrl+R` | 反向搜索历史 |
|
|
108
|
+
| `Up/Down` | 浏览历史命令 |
|
|
109
|
+
| `Shift+Enter` | 换行(多行输入) |
|
|
110
|
+
| `Ctrl+W` | 删除前一个词 |
|
|
111
|
+
| `Ctrl+U` | 删除整行 |
|
|
112
|
+
| `Ctrl+K` | 删除光标至行尾 |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 配置
|
|
117
|
+
|
|
118
|
+
通过命令行标志自定义启动行为:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 指定配置文件
|
|
122
|
+
fp --config ./my-config.yaml
|
|
123
|
+
|
|
124
|
+
# 指定初始模型
|
|
125
|
+
fp --model claude-3-opus
|
|
126
|
+
|
|
127
|
+
# 静默模式(跳过启动横幅)
|
|
128
|
+
fp --quiet
|
|
129
|
+
|
|
130
|
+
# 调试模式
|
|
131
|
+
fp --debug
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
配置文件示例(YAML):
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
cli:
|
|
138
|
+
theme: dark # dark / light
|
|
139
|
+
multi_line: true # 启用多行编辑
|
|
140
|
+
max_history: 1000 # 历史记录条数
|
|
141
|
+
stream: true # 流式输出
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 依赖
|
|
147
|
+
|
|
148
|
+
| 依赖 | 版本 | 用途 |
|
|
149
|
+
|------|------|------|
|
|
150
|
+
| `fp-core` | >= 0.1.0 | Agent 核心引擎 |
|
|
151
|
+
| `prompt-toolkit` | >= 3.0.40 | 终端交互框架 |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 许可
|
|
156
|
+
|
|
157
|
+
MIT © zpb
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64.0", "setuptools-scm>=8.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fp-terminal"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "五块卵石 - 终端 REPL"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{name = "zpb"}]
|
|
13
|
+
keywords = ["agent", "cli"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
dependencies = [
|
|
24
|
+
"fp-core>=0.1.0",
|
|
25
|
+
"prompt-toolkit>=3.0.40",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["src"]
|
|
30
|
+
include = ["fp_cli*"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools_scm]
|
|
33
|
+
root = "../.."
|
|
34
|
+
local_scheme = "no-local-version"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""
|
|
2
|
+
命令行交互界面
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
import signal
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from prompt_toolkit.completion import Completer as PtCompleter
|
|
11
|
+
|
|
12
|
+
from fp_core import config, display
|
|
13
|
+
|
|
14
|
+
# ── 信号处理器可访问的当前 agent 实例(跨线程安全) ──
|
|
15
|
+
_current_agent = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SlashCompleter(PtCompleter):
|
|
19
|
+
"""自定义补全器:仅在输入 "/" 前缀时匹配命令和工具名。
|
|
20
|
+
|
|
21
|
+
补全字典初始化时从 tools/commands/skills 系统动态加载,确保始终同步。
|
|
22
|
+
每个补全项附带描述信息作为 display_meta,帮助用户快速了解功能。
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self._words: list[str] = []
|
|
27
|
+
self._words_meta: dict[str, str] = {} # word → description
|
|
28
|
+
self._load_words()
|
|
29
|
+
|
|
30
|
+
def _load_words(self):
|
|
31
|
+
"""从 tools、commands、skills 系统加载补全词条及描述"""
|
|
32
|
+
words: set[str] = set()
|
|
33
|
+
meta: dict[str, str] = {}
|
|
34
|
+
|
|
35
|
+
# 1. 命令名(带 / 前缀)
|
|
36
|
+
try:
|
|
37
|
+
from fp_core.commands import get_all_commands
|
|
38
|
+
|
|
39
|
+
for cmd_name, desc in get_all_commands().items():
|
|
40
|
+
word = f"/{cmd_name}"
|
|
41
|
+
words.add(word)
|
|
42
|
+
if desc:
|
|
43
|
+
meta[word] = desc
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
self._words = sorted(words, key=lambda w: (not w.startswith("/"), w))
|
|
48
|
+
self._words_meta = meta
|
|
49
|
+
|
|
50
|
+
def get_completions(self, document, complete_event):
|
|
51
|
+
"""prompt_toolkit Completer 接口"""
|
|
52
|
+
from prompt_toolkit.completion import Completion
|
|
53
|
+
|
|
54
|
+
text = document.text_before_cursor
|
|
55
|
+
|
|
56
|
+
# 仅在输入以 "/" 开头时才触发补全
|
|
57
|
+
if not text.startswith("/"):
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
for word in self._words:
|
|
61
|
+
if word.startswith(text):
|
|
62
|
+
desc = self._words_meta.get(word, "")
|
|
63
|
+
yield Completion(
|
|
64
|
+
word,
|
|
65
|
+
start_position=-len(text),
|
|
66
|
+
display_meta=desc,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class InputHandler:
|
|
71
|
+
"""交互式输入(prompt_toolkit 封装,支持历史/斜杠补全)"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, prompt: str = "(Agent) > "):
|
|
74
|
+
self.prompt = prompt
|
|
75
|
+
self._session = None
|
|
76
|
+
self._init_session()
|
|
77
|
+
|
|
78
|
+
def _build_key_bindings(self):
|
|
79
|
+
"""自定义键绑定:Tab 确认补全(而非循环选择下一个)"""
|
|
80
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
81
|
+
from prompt_toolkit.keys import Keys
|
|
82
|
+
|
|
83
|
+
kb = KeyBindings()
|
|
84
|
+
|
|
85
|
+
@kb.add(Keys.Tab)
|
|
86
|
+
def _(event):
|
|
87
|
+
"""Tab 键:确认当前选中的补全项,无选中时直接触发补全"""
|
|
88
|
+
b = event.current_buffer
|
|
89
|
+
|
|
90
|
+
if b.complete_state is not None:
|
|
91
|
+
# 补全菜单已显示 → 确认当前选中项
|
|
92
|
+
current = b.complete_state.current_completion
|
|
93
|
+
if current is not None:
|
|
94
|
+
b.apply_completion(current)
|
|
95
|
+
else:
|
|
96
|
+
# 没有选中具体项时,选中第一个并确认
|
|
97
|
+
completions = b.complete_state.completions
|
|
98
|
+
if completions:
|
|
99
|
+
b.apply_completion(completions[0])
|
|
100
|
+
else:
|
|
101
|
+
# 无补全菜单 → 触发补全
|
|
102
|
+
b.start_completion(select_first=True)
|
|
103
|
+
|
|
104
|
+
return kb
|
|
105
|
+
|
|
106
|
+
def _init_session(self):
|
|
107
|
+
if not sys.stdin.isatty():
|
|
108
|
+
return
|
|
109
|
+
try:
|
|
110
|
+
from prompt_toolkit import PromptSession
|
|
111
|
+
from prompt_toolkit.history import FileHistory
|
|
112
|
+
|
|
113
|
+
history_file = os.path.join(config.MEMORY_DIR, "_input_history")
|
|
114
|
+
os.makedirs(os.path.dirname(history_file), exist_ok=True)
|
|
115
|
+
|
|
116
|
+
# 构建补全器(延迟加载,确保 tools/commands 已就绪)
|
|
117
|
+
completer = SlashCompleter()
|
|
118
|
+
key_bindings = self._build_key_bindings()
|
|
119
|
+
|
|
120
|
+
self._session = PromptSession(
|
|
121
|
+
history=FileHistory(history_file),
|
|
122
|
+
completer=completer,
|
|
123
|
+
key_bindings=key_bindings,
|
|
124
|
+
enable_open_in_editor=True,
|
|
125
|
+
)
|
|
126
|
+
except Exception:
|
|
127
|
+
self._session = None
|
|
128
|
+
|
|
129
|
+
async def prompt_async(self) -> str:
|
|
130
|
+
if self._session:
|
|
131
|
+
return await self._session.prompt_async(self.prompt)
|
|
132
|
+
return input(self.prompt)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _raw_sigint_handler(signum, frame):
|
|
136
|
+
"""跨平台 SIGINT 处理器:取消 asyncio 任务 + 通知 agent 实例"""
|
|
137
|
+
# 方式 1(Unix 主线程):直接取消所有 asyncio 任务
|
|
138
|
+
try:
|
|
139
|
+
for task in asyncio.all_tasks():
|
|
140
|
+
task.cancel()
|
|
141
|
+
except (RuntimeError, ValueError):
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
# 方式 2(跨平台回退):通知 agent 实例
|
|
145
|
+
# Windows 上 Ctrl+C 在独立线程运行,asyncio.all_tasks().cancel()
|
|
146
|
+
# 可能不立即生效,agent.cancel() 设置实例级中断标记作为安全网,
|
|
147
|
+
# _check_interrupted() 会在下一个循环检查点检测到它。
|
|
148
|
+
if _current_agent is not None:
|
|
149
|
+
_current_agent.cancel()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def main():
|
|
153
|
+
"""主入口"""
|
|
154
|
+
import argparse
|
|
155
|
+
|
|
156
|
+
parser = argparse.ArgumentParser(description="五块卵石 - AI Agent 命令行界面")
|
|
157
|
+
parser.add_argument("-m", "--message", help="单次消息模式")
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
"-r", "--resume", nargs="?", const="auto", default=None, metavar="SESSION_ID", help="恢复历史会话"
|
|
160
|
+
)
|
|
161
|
+
parser.add_argument("--init", action="store_true", help="初始化配置文件")
|
|
162
|
+
|
|
163
|
+
args = parser.parse_args()
|
|
164
|
+
|
|
165
|
+
if args.init:
|
|
166
|
+
config.init_config()
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
if not config.check_llm_config():
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
from fp_core.core.agent import Agent
|
|
173
|
+
|
|
174
|
+
agent = Agent(resume=args.resume)
|
|
175
|
+
|
|
176
|
+
# ── 挂接到模块变量,供信号处理器跨线程访问 ──────────
|
|
177
|
+
global _current_agent
|
|
178
|
+
_current_agent = agent
|
|
179
|
+
|
|
180
|
+
# ── 安装 SIGINT 处理器 ────────────────────────────────
|
|
181
|
+
#
|
|
182
|
+
# 使用 signal.signal() 而不是 loop.add_signal_handler()
|
|
183
|
+
# 原因:add_signal_handler 依赖 event loop 的 wakeup fd 机制,
|
|
184
|
+
# 在你这个终端环境下信号无法通过该链路抵达处理器。
|
|
185
|
+
# signal.signal() 安装纯 C 级处理器,信号到达时直接触发,
|
|
186
|
+
# 通过 asyncio.all_tasks().cancel() 注入 CancelledError。
|
|
187
|
+
# agent._stream_chat 的 try/except 负责优雅捕获中断。
|
|
188
|
+
signal.signal(signal.SIGINT, _raw_sigint_handler)
|
|
189
|
+
|
|
190
|
+
if not os.environ.get("FP_SUBAGENT_QUIET"):
|
|
191
|
+
display.print_logo()
|
|
192
|
+
display.startup(agent.model, resume=args.resume)
|
|
193
|
+
|
|
194
|
+
if args.message:
|
|
195
|
+
print(f"> {args.message}")
|
|
196
|
+
response = await agent.process(args.message)
|
|
197
|
+
print(f"\nAgent: {response.content}")
|
|
198
|
+
else:
|
|
199
|
+
inp = InputHandler()
|
|
200
|
+
|
|
201
|
+
if args.resume:
|
|
202
|
+
display.hint(f"💡 续会话: {agent.session.session_id},输入 /help 查看命令")
|
|
203
|
+
else:
|
|
204
|
+
display.hint("💡 输入 /help 查看命令,/resume 可回到历史会话")
|
|
205
|
+
print()
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
while True:
|
|
209
|
+
try:
|
|
210
|
+
user_input = await inp.prompt_async()
|
|
211
|
+
except (EOFError, KeyboardInterrupt):
|
|
212
|
+
print()
|
|
213
|
+
break
|
|
214
|
+
|
|
215
|
+
if not user_input.strip():
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
response = await agent.process(user_input)
|
|
220
|
+
except (SystemExit, asyncio.CancelledError):
|
|
221
|
+
break
|
|
222
|
+
except Exception as e:
|
|
223
|
+
display.error(f"错误: {e}")
|
|
224
|
+
print()
|
|
225
|
+
except KeyboardInterrupt:
|
|
226
|
+
print()
|
|
227
|
+
|
|
228
|
+
await agent.shutdown()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fp-terminal
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: 五块卵石 - 终端 REPL
|
|
5
|
+
Author: zpb
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: agent,cli
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: fp-core>=0.1.0
|
|
17
|
+
Requires-Dist: prompt-toolkit>=3.0.40
|
|
18
|
+
|
|
19
|
+
# fp-terminal — 五块卵石终端 REPL
|
|
20
|
+
|
|
21
|
+
[](https://pypi.org/project/fp-terminal/)
|
|
22
|
+
[](https://pypi.org/project/fp-terminal/)
|
|
23
|
+
[](LICENSE)
|
|
24
|
+
|
|
25
|
+
## 简介
|
|
26
|
+
|
|
27
|
+
**fp-terminal** 是五块卵石(Five Pebbles)Agent 框架的交互式命令行界面。它基于 `prompt-toolkit` 构建,提供了语法高亮、自动补全、多行编辑、历史记录等现代终端体验。
|
|
28
|
+
|
|
29
|
+
> 通常通过主包 `fp` 安装,无需单独安装。
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 特性
|
|
34
|
+
|
|
35
|
+
- **REPL 交互模式** — 实时与 Agent 对话,流式输出响应
|
|
36
|
+
- **命令系统** — 内置 `/` 命令用于控制会话、加载插件、切换模型等
|
|
37
|
+
- **语法高亮** — 代码块自动识别并着色
|
|
38
|
+
- **自动补全** — 命令与参数 Tab 补全
|
|
39
|
+
- **多行编辑** — 支持粘贴多行文本或代码
|
|
40
|
+
- **会话历史** — 对话历史持久化存储,跨会话可回溯
|
|
41
|
+
- **多彩输出** — 基于 `rich` 的渲染引擎
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 安装
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install fp-terminal
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
要求 Python >= 3.11。
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 快速使用
|
|
56
|
+
|
|
57
|
+
### 启动 REPL
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
fp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
或显式调用:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python -m fp_cli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
启动后进入交互模式:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
╭─ Five Pebbles ───────────────────────────╮
|
|
73
|
+
│ 输入 /help 查看可用命令 │
|
|
74
|
+
│ 按 Ctrl+D 或输入 /exit 退出 │
|
|
75
|
+
╰──────────────────────────────────────────╯
|
|
76
|
+
|
|
77
|
+
🤖 你好!我是五块卵石,请告诉我你的问题。
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 开始对话
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
>> 请用 Python 写一个快速排序
|
|
84
|
+
# 直接输入即发送消息,Agent 会流式输出回复
|
|
85
|
+
|
|
86
|
+
>> /save session-quicksort
|
|
87
|
+
# 保存当前会话
|
|
88
|
+
|
|
89
|
+
>> /load session-quicksort
|
|
90
|
+
# 恢复历史会话
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 命令列表
|
|
96
|
+
|
|
97
|
+
| 命令 | 别名 | 说明 |
|
|
98
|
+
|------|------|------|
|
|
99
|
+
| `/help` | `/h` | 显示帮助信息 |
|
|
100
|
+
| `/exit` | `/q`, `/quit` | 退出 REPL |
|
|
101
|
+
| `/clear` | `/c` | 清屏 |
|
|
102
|
+
| `/save <name>` | — | 保存当前会话 |
|
|
103
|
+
| `/load <name>` | — | 加载指定会话 |
|
|
104
|
+
| `/list` | `/ls` | 列出所有会话 |
|
|
105
|
+
| `/delete <name>` | `/rm` | 删除指定会话 |
|
|
106
|
+
| `/model <name>` | — | 切换 LLM 模型 |
|
|
107
|
+
| `/plugin list` | — | 列出已加载的插件 |
|
|
108
|
+
| `/plugin load <name>` | — | 动态加载插件 |
|
|
109
|
+
| `/tool list` | — | 列出可用工具 |
|
|
110
|
+
| `/env` | — | 查看当前环境配置 |
|
|
111
|
+
| `/config <key>=<value>` | — | 动态修改配置 |
|
|
112
|
+
| `/reset` | — | 重置当前会话上下文 |
|
|
113
|
+
| `/export <format>` | — | 导出会话(json/md) |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 快捷键
|
|
118
|
+
|
|
119
|
+
| 快捷键 | 功能 |
|
|
120
|
+
|--------|------|
|
|
121
|
+
| `Tab` | 自动补全命令/参数 |
|
|
122
|
+
| `Ctrl+C` | 中断当前输出 |
|
|
123
|
+
| `Ctrl+D` | 退出 REPL |
|
|
124
|
+
| `Ctrl+L` | 清屏 |
|
|
125
|
+
| `Ctrl+R` | 反向搜索历史 |
|
|
126
|
+
| `Up/Down` | 浏览历史命令 |
|
|
127
|
+
| `Shift+Enter` | 换行(多行输入) |
|
|
128
|
+
| `Ctrl+W` | 删除前一个词 |
|
|
129
|
+
| `Ctrl+U` | 删除整行 |
|
|
130
|
+
| `Ctrl+K` | 删除光标至行尾 |
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 配置
|
|
135
|
+
|
|
136
|
+
通过命令行标志自定义启动行为:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# 指定配置文件
|
|
140
|
+
fp --config ./my-config.yaml
|
|
141
|
+
|
|
142
|
+
# 指定初始模型
|
|
143
|
+
fp --model claude-3-opus
|
|
144
|
+
|
|
145
|
+
# 静默模式(跳过启动横幅)
|
|
146
|
+
fp --quiet
|
|
147
|
+
|
|
148
|
+
# 调试模式
|
|
149
|
+
fp --debug
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
配置文件示例(YAML):
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
cli:
|
|
156
|
+
theme: dark # dark / light
|
|
157
|
+
multi_line: true # 启用多行编辑
|
|
158
|
+
max_history: 1000 # 历史记录条数
|
|
159
|
+
stream: true # 流式输出
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 依赖
|
|
165
|
+
|
|
166
|
+
| 依赖 | 版本 | 用途 |
|
|
167
|
+
|------|------|------|
|
|
168
|
+
| `fp-core` | >= 0.1.0 | Agent 核心引擎 |
|
|
169
|
+
| `prompt-toolkit` | >= 3.0.40 | 终端交互框架 |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 许可
|
|
174
|
+
|
|
175
|
+
MIT © zpb
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/fp_cli/__init__.py
|
|
4
|
+
src/fp_cli/main.py
|
|
5
|
+
src/fp_terminal.egg-info/PKG-INFO
|
|
6
|
+
src/fp_terminal.egg-info/SOURCES.txt
|
|
7
|
+
src/fp_terminal.egg-info/dependency_links.txt
|
|
8
|
+
src/fp_terminal.egg-info/requires.txt
|
|
9
|
+
src/fp_terminal.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fp_cli
|