fp-core 0.1.1.dev0__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.
Files changed (65) hide show
  1. fp_core-0.1.1.dev0/PKG-INFO +188 -0
  2. fp_core-0.1.1.dev0/README.md +168 -0
  3. fp_core-0.1.1.dev0/pyproject.toml +39 -0
  4. fp_core-0.1.1.dev0/setup.cfg +4 -0
  5. fp_core-0.1.1.dev0/src/fp_core/__init__.py +21 -0
  6. fp_core-0.1.1.dev0/src/fp_core/commands/__init__.py +132 -0
  7. fp_core-0.1.1.dev0/src/fp_core/commands/back.py +73 -0
  8. fp_core-0.1.1.dev0/src/fp_core/commands/clear.py +14 -0
  9. fp_core-0.1.1.dev0/src/fp_core/commands/compact.py +10 -0
  10. fp_core-0.1.1.dev0/src/fp_core/commands/exit_bang.py +27 -0
  11. fp_core-0.1.1.dev0/src/fp_core/commands/exit_cmd.py +9 -0
  12. fp_core-0.1.1.dev0/src/fp_core/commands/fork.py +16 -0
  13. fp_core-0.1.1.dev0/src/fp_core/commands/help.py +25 -0
  14. fp_core-0.1.1.dev0/src/fp_core/commands/history.py +32 -0
  15. fp_core-0.1.1.dev0/src/fp_core/commands/memory_cmd.py +30 -0
  16. fp_core-0.1.1.dev0/src/fp_core/commands/model.py +24 -0
  17. fp_core-0.1.1.dev0/src/fp_core/commands/resume.py +166 -0
  18. fp_core-0.1.1.dev0/src/fp_core/commands/session.py +13 -0
  19. fp_core-0.1.1.dev0/src/fp_core/config.py +329 -0
  20. fp_core-0.1.1.dev0/src/fp_core/core/__init__.py +0 -0
  21. fp_core-0.1.1.dev0/src/fp_core/core/agent.py +762 -0
  22. fp_core-0.1.1.dev0/src/fp_core/core/conversation.py +355 -0
  23. fp_core-0.1.1.dev0/src/fp_core/core/io.py +180 -0
  24. fp_core-0.1.1.dev0/src/fp_core/core/lifecycle.py +342 -0
  25. fp_core-0.1.1.dev0/src/fp_core/core/llm_client.py +228 -0
  26. fp_core-0.1.1.dev0/src/fp_core/core/llm_service.py +109 -0
  27. fp_core-0.1.1.dev0/src/fp_core/core/prompt_builder.py +123 -0
  28. fp_core-0.1.1.dev0/src/fp_core/core/session.py +369 -0
  29. fp_core-0.1.1.dev0/src/fp_core/core/tool_executor.py +59 -0
  30. fp_core-0.1.1.dev0/src/fp_core/display.py +366 -0
  31. fp_core-0.1.1.dev0/src/fp_core/plugins/base/__init__.py +0 -0
  32. fp_core-0.1.1.dev0/src/fp_core/plugins/base/plugin.py +273 -0
  33. fp_core-0.1.1.dev0/src/fp_core/prompts/__init__.py +5 -0
  34. fp_core-0.1.1.dev0/src/fp_core/prompts/agent.md +47 -0
  35. fp_core-0.1.1.dev0/src/fp_core/prompts/agent.py +29 -0
  36. fp_core-0.1.1.dev0/src/fp_core/skills/__init__.py +5 -0
  37. fp_core-0.1.1.dev0/src/fp_core/skills/loader.py +209 -0
  38. fp_core-0.1.1.dev0/src/fp_core/skills/modify_skills.md +11 -0
  39. fp_core-0.1.1.dev0/src/fp_core/skills/python_syntax_check.md +15 -0
  40. fp_core-0.1.1.dev0/src/fp_core/skills/self_modification.md +16 -0
  41. fp_core-0.1.1.dev0/src/fp_core/skills/subagent.md +69 -0
  42. fp_core-0.1.1.dev0/src/fp_core/tools/__init__.py +216 -0
  43. fp_core-0.1.1.dev0/src/fp_core/tools/core.py +239 -0
  44. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/__init__.py +0 -0
  45. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/memory_read_plugin.py +138 -0
  46. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/memory_save_plugin.py +88 -0
  47. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/python_plugin.py +106 -0
  48. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/subagent_plugin.py +320 -0
  49. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/task_clear_plugin.py +59 -0
  50. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/task_create_plugin.py +73 -0
  51. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/task_list_plugin.py +68 -0
  52. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/task_update_plugin.py +78 -0
  53. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/web_fetch_plugin.py +71 -0
  54. fp_core-0.1.1.dev0/src/fp_core/tools/plugins/web_search_plugin.py +255 -0
  55. fp_core-0.1.1.dev0/src/fp_core.egg-info/PKG-INFO +188 -0
  56. fp_core-0.1.1.dev0/src/fp_core.egg-info/SOURCES.txt +63 -0
  57. fp_core-0.1.1.dev0/src/fp_core.egg-info/dependency_links.txt +1 -0
  58. fp_core-0.1.1.dev0/src/fp_core.egg-info/requires.txt +4 -0
  59. fp_core-0.1.1.dev0/src/fp_core.egg-info/top_level.txt +1 -0
  60. fp_core-0.1.1.dev0/tests/__init__.py +1 -0
  61. fp_core-0.1.1.dev0/tests/conftest.py +67 -0
  62. fp_core-0.1.1.dev0/tests/test_config.py +211 -0
  63. fp_core-0.1.1.dev0/tests/test_conversation.py +384 -0
  64. fp_core-0.1.1.dev0/tests/test_session.py +233 -0
  65. fp_core-0.1.1.dev0/tests/test_skills.py +261 -0
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: fp-core
3
+ Version: 0.1.1.dev0
4
+ Summary: 五块卵石 Agent 引擎 - 基于生命周期钩子的插件化 Agent 核心
5
+ Author: zpb
6
+ License-Expression: MIT
7
+ Keywords: agent,llm,lifecycle,plugin,ai
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: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: httpx>=0.28.0
17
+ Requires-Dist: rich>=14.0.0
18
+ Requires-Dist: wcwidth>=0.2.0
19
+ Requires-Dist: pyyaml>=6.0
20
+
21
+ # fp-core — 五块卵石 Agent 引擎
22
+
23
+ [![PyPI](https://img.shields.io/pypi/v/fp-core)](https://pypi.org/project/fp-core/)
24
+ [![Python](https://img.shields.io/pypi/pyversions/fp-core)](https://pypi.org/project/fp-core/)
25
+ [![License](https://img.shields.io/pypi/l/fp-core)](LICENSE)
26
+
27
+ ## 简介
28
+
29
+ **fp-core** 是五块卵石(Five Pebbles)Agent 框架的核心引擎。它提供了 Agent 生命周期管理、LLM 交互、工具执行、会话管理及插件化扩展等基础能力,是整个框架的基石。
30
+
31
+ > 如果你只是**使用** Agent,请安装 [`fp`](https://pypi.org/project/fp/) 主包;如果你要**自定义/扩展** Agent 行为,你正在正确的地方。
32
+
33
+ ---
34
+
35
+ ## 特性
36
+
37
+ - **生命周期钩子系统** — 在 Agent 的启动、消息处理、工具调用、响应生成等阶段注入自定义逻辑
38
+ - **插件化架构** — 通过插件动态加载工具、命令、事件监听器,无需修改核心代码
39
+ - **多 LLM 支持** — 统一的 LLM 交互接口,可接入 OpenAI、Claude、本地模型等
40
+ - **工具执行引擎** — 安全的沙箱化工具执行,支持同步/异步、超时控制、结果缓存
41
+ - **会话管理** — 持久化的会话存储与恢复,支持多会话并行
42
+ - **提示词模板** — 结构化提示词组装,支持动态注入上下文与历史
43
+
44
+ ---
45
+
46
+ ## 安装
47
+
48
+ ```bash
49
+ pip install fp-core
50
+ ```
51
+
52
+ 要求 Python >= 3.11。
53
+
54
+ ---
55
+
56
+ ## 快速使用
57
+
58
+ ### 初始化 Agent 引擎
59
+
60
+ ```python
61
+ from fp_core import AgentEngine
62
+
63
+ engine = AgentEngine(config={
64
+ "llm": {"provider": "openai", "model": "gpt-4o"},
65
+ "session": {"storage": "file", "path": "./sessions"},
66
+ })
67
+ ```
68
+
69
+ ### 注册插件
70
+
71
+ ```python
72
+ from fp_core.plugins import BasePlugin
73
+
74
+ class MyPlugin(BasePlugin):
75
+ name = "my_plugin"
76
+
77
+ def on_agent_start(self, ctx):
78
+ print("Agent 启动了!")
79
+
80
+ def on_message(self, ctx, message):
81
+ # 在消息处理前注入逻辑
82
+ return message
83
+
84
+ engine.plugin_manager.register(MyPlugin())
85
+ ```
86
+
87
+ ### 执行一次对话
88
+
89
+ ```python
90
+ response = engine.chat("你好,请介绍一下你自己")
91
+ print(response)
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 架构概览
97
+
98
+ ```
99
+ fp_core/
100
+ ├── core/ # 核心运行时:AgentEngine, LLM 接口, 上下文管理
101
+ │ ├── agent.py # Agent 主循环
102
+ │ ├── llm.py # LLM 客户端抽象
103
+ │ └── context.py # 会话上下文
104
+ ├── commands/ # 内置命令(内置工具)
105
+ ├── tools/ # 工具执行引擎与内置工具集
106
+ │ └── plugins/ # 工具插件加载器
107
+ ├── plugins/ # 插件系统
108
+ │ └── base/ # 插件基类与钩子定义
109
+ ├── prompts/ # 提示词模板管理
110
+ ├── skills/ # 技能定义与加载
111
+ └── sessions/ # 会话存储与管理
112
+ ```
113
+
114
+ ### 生命周期钩子
115
+
116
+ 插件可以通过实现以下钩子方法介入 Agent 运行流程:
117
+
118
+ | 钩子 | 触发时机 | 典型用途 |
119
+ |------|---------|---------|
120
+ | `on_agent_start` | Agent 初始化完成 | 加载自定义资源 |
121
+ | `on_agent_stop` | Agent 关闭前 | 资源清理 |
122
+ | `on_message` | 收到用户消息后 | 消息预处理/校验 |
123
+ | `before_llm_call` | 调用 LLM 前 | 注入系统提示词 |
124
+ | `after_llm_call` | 收到 LLM 响应后 | 响应后处理/过滤 |
125
+ | `before_tool_exec` | 执行工具前 | 权限检查/参数校验 |
126
+ | `after_tool_exec` | 工具执行完成后 | 结果格式化/缓存 |
127
+ | `on_response` | 生成最终响应前 | 响应润色/转译 |
128
+
129
+ ---
130
+
131
+ ## 依赖
132
+
133
+ | 依赖 | 版本 | 用途 |
134
+ |------|------|------|
135
+ | `httpx` | >= 0.28.0 | LLM API 异步 HTTP 请求 |
136
+ | `rich` | >= 14.0.0 | 控制台输出格式化 |
137
+ | `wcwidth` | >= 0.2.0 | 终端文本宽度计算 |
138
+ | `pyyaml` | >= 6.0 | 配置文件解析 |
139
+
140
+ ---
141
+
142
+ ## 扩展开发
143
+
144
+ ### 编写自定义插件
145
+
146
+ ```python
147
+ from fp_core.plugins import BasePlugin
148
+ from fp_core.plugins.base import hook
149
+
150
+ class LogPlugin(BasePlugin):
151
+ name = "logger"
152
+
153
+ @hook("on_message")
154
+ def log_message(self, ctx, message):
155
+ print(f"[LOG] 收到消息: {message[:50]}...")
156
+ return message
157
+
158
+ @hook("before_llm_call")
159
+ def inject_system_prompt(self, ctx, messages):
160
+ messages.insert(0, {"role": "system", "content": "请用简洁的语言回答"})
161
+ return messages
162
+ ```
163
+
164
+ ### 编写自定义工具
165
+
166
+ ```python
167
+ from fp_core.tools import BaseTool
168
+
169
+ class WeatherTool(BaseTool):
170
+ name = "get_weather"
171
+ description = "获取指定城市的天气"
172
+
173
+ async def execute(self, city: str) -> dict:
174
+ # 实现天气查询逻辑
175
+ return {"city": city, "temperature": 22}
176
+ ```
177
+
178
+ 注册到 Agent:
179
+
180
+ ```python
181
+ engine.tool_manager.register_tool(WeatherTool())
182
+ ```
183
+
184
+ ---
185
+
186
+ ## 许可
187
+
188
+ MIT © zpb
@@ -0,0 +1,168 @@
1
+ # fp-core — 五块卵石 Agent 引擎
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/fp-core)](https://pypi.org/project/fp-core/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/fp-core)](https://pypi.org/project/fp-core/)
5
+ [![License](https://img.shields.io/pypi/l/fp-core)](LICENSE)
6
+
7
+ ## 简介
8
+
9
+ **fp-core** 是五块卵石(Five Pebbles)Agent 框架的核心引擎。它提供了 Agent 生命周期管理、LLM 交互、工具执行、会话管理及插件化扩展等基础能力,是整个框架的基石。
10
+
11
+ > 如果你只是**使用** Agent,请安装 [`fp`](https://pypi.org/project/fp/) 主包;如果你要**自定义/扩展** Agent 行为,你正在正确的地方。
12
+
13
+ ---
14
+
15
+ ## 特性
16
+
17
+ - **生命周期钩子系统** — 在 Agent 的启动、消息处理、工具调用、响应生成等阶段注入自定义逻辑
18
+ - **插件化架构** — 通过插件动态加载工具、命令、事件监听器,无需修改核心代码
19
+ - **多 LLM 支持** — 统一的 LLM 交互接口,可接入 OpenAI、Claude、本地模型等
20
+ - **工具执行引擎** — 安全的沙箱化工具执行,支持同步/异步、超时控制、结果缓存
21
+ - **会话管理** — 持久化的会话存储与恢复,支持多会话并行
22
+ - **提示词模板** — 结构化提示词组装,支持动态注入上下文与历史
23
+
24
+ ---
25
+
26
+ ## 安装
27
+
28
+ ```bash
29
+ pip install fp-core
30
+ ```
31
+
32
+ 要求 Python >= 3.11。
33
+
34
+ ---
35
+
36
+ ## 快速使用
37
+
38
+ ### 初始化 Agent 引擎
39
+
40
+ ```python
41
+ from fp_core import AgentEngine
42
+
43
+ engine = AgentEngine(config={
44
+ "llm": {"provider": "openai", "model": "gpt-4o"},
45
+ "session": {"storage": "file", "path": "./sessions"},
46
+ })
47
+ ```
48
+
49
+ ### 注册插件
50
+
51
+ ```python
52
+ from fp_core.plugins import BasePlugin
53
+
54
+ class MyPlugin(BasePlugin):
55
+ name = "my_plugin"
56
+
57
+ def on_agent_start(self, ctx):
58
+ print("Agent 启动了!")
59
+
60
+ def on_message(self, ctx, message):
61
+ # 在消息处理前注入逻辑
62
+ return message
63
+
64
+ engine.plugin_manager.register(MyPlugin())
65
+ ```
66
+
67
+ ### 执行一次对话
68
+
69
+ ```python
70
+ response = engine.chat("你好,请介绍一下你自己")
71
+ print(response)
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 架构概览
77
+
78
+ ```
79
+ fp_core/
80
+ ├── core/ # 核心运行时:AgentEngine, LLM 接口, 上下文管理
81
+ │ ├── agent.py # Agent 主循环
82
+ │ ├── llm.py # LLM 客户端抽象
83
+ │ └── context.py # 会话上下文
84
+ ├── commands/ # 内置命令(内置工具)
85
+ ├── tools/ # 工具执行引擎与内置工具集
86
+ │ └── plugins/ # 工具插件加载器
87
+ ├── plugins/ # 插件系统
88
+ │ └── base/ # 插件基类与钩子定义
89
+ ├── prompts/ # 提示词模板管理
90
+ ├── skills/ # 技能定义与加载
91
+ └── sessions/ # 会话存储与管理
92
+ ```
93
+
94
+ ### 生命周期钩子
95
+
96
+ 插件可以通过实现以下钩子方法介入 Agent 运行流程:
97
+
98
+ | 钩子 | 触发时机 | 典型用途 |
99
+ |------|---------|---------|
100
+ | `on_agent_start` | Agent 初始化完成 | 加载自定义资源 |
101
+ | `on_agent_stop` | Agent 关闭前 | 资源清理 |
102
+ | `on_message` | 收到用户消息后 | 消息预处理/校验 |
103
+ | `before_llm_call` | 调用 LLM 前 | 注入系统提示词 |
104
+ | `after_llm_call` | 收到 LLM 响应后 | 响应后处理/过滤 |
105
+ | `before_tool_exec` | 执行工具前 | 权限检查/参数校验 |
106
+ | `after_tool_exec` | 工具执行完成后 | 结果格式化/缓存 |
107
+ | `on_response` | 生成最终响应前 | 响应润色/转译 |
108
+
109
+ ---
110
+
111
+ ## 依赖
112
+
113
+ | 依赖 | 版本 | 用途 |
114
+ |------|------|------|
115
+ | `httpx` | >= 0.28.0 | LLM API 异步 HTTP 请求 |
116
+ | `rich` | >= 14.0.0 | 控制台输出格式化 |
117
+ | `wcwidth` | >= 0.2.0 | 终端文本宽度计算 |
118
+ | `pyyaml` | >= 6.0 | 配置文件解析 |
119
+
120
+ ---
121
+
122
+ ## 扩展开发
123
+
124
+ ### 编写自定义插件
125
+
126
+ ```python
127
+ from fp_core.plugins import BasePlugin
128
+ from fp_core.plugins.base import hook
129
+
130
+ class LogPlugin(BasePlugin):
131
+ name = "logger"
132
+
133
+ @hook("on_message")
134
+ def log_message(self, ctx, message):
135
+ print(f"[LOG] 收到消息: {message[:50]}...")
136
+ return message
137
+
138
+ @hook("before_llm_call")
139
+ def inject_system_prompt(self, ctx, messages):
140
+ messages.insert(0, {"role": "system", "content": "请用简洁的语言回答"})
141
+ return messages
142
+ ```
143
+
144
+ ### 编写自定义工具
145
+
146
+ ```python
147
+ from fp_core.tools import BaseTool
148
+
149
+ class WeatherTool(BaseTool):
150
+ name = "get_weather"
151
+ description = "获取指定城市的天气"
152
+
153
+ async def execute(self, city: str) -> dict:
154
+ # 实现天气查询逻辑
155
+ return {"city": city, "temperature": 22}
156
+ ```
157
+
158
+ 注册到 Agent:
159
+
160
+ ```python
161
+ engine.tool_manager.register_tool(WeatherTool())
162
+ ```
163
+
164
+ ---
165
+
166
+ ## 许可
167
+
168
+ MIT © zpb
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64.0", "setuptools-scm>=8.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fp-core"
7
+ dynamic = ["version"]
8
+ description = "五块卵石 Agent 引擎 - 基于生命周期钩子的插件化 Agent 核心"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ authors = [{name = "zpb"}]
13
+ keywords = ["agent", "llm", "lifecycle", "plugin", "ai"]
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
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ ]
22
+
23
+ dependencies = [
24
+ "httpx>=0.28.0",
25
+ "rich>=14.0.0",
26
+ "wcwidth>=0.2.0",
27
+ "pyyaml>=6.0",
28
+ ]
29
+
30
+ [tool.setuptools.packages.find]
31
+ where = ["src"]
32
+ include = ["fp_core*"]
33
+
34
+ [tool.setuptools.package-data]
35
+ "fp_core" = ["*.md", "*.json", "*.yaml", "*.yml"]
36
+
37
+ [tool.setuptools_scm]
38
+ root = "../.."
39
+ local_scheme = "no-local-version"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,21 @@
1
+ """fp-core - 五块卵石 Agent 引擎"""
2
+
3
+ __version__ = "0.1.0"
4
+ __license__ = "MIT"
5
+ __author__ = "zpb"
6
+
7
+ from fp_core.core.agent import Agent, Message, Response
8
+ from fp_core.core.lifecycle import HookContext, LifecycleHook, LifecycleManager
9
+ from fp_core.plugins.base.plugin import Plugin, PluginConfig, PluginRegistry
10
+
11
+ __all__ = [
12
+ "Agent",
13
+ "Message",
14
+ "Response",
15
+ "LifecycleManager",
16
+ "LifecycleHook",
17
+ "HookContext",
18
+ "Plugin",
19
+ "PluginConfig",
20
+ "PluginRegistry",
21
+ ]
@@ -0,0 +1,132 @@
1
+ """
2
+ commands/__init__.py — 命令注册表与自动发现
3
+
4
+ 自动扫描 commands/ 目录下所有 .py 文件(排除 __init__.py),
5
+ 导入每个模块并检查 name/execute 接口,构建命令名→模块的映射(含别名)。
6
+ """
7
+
8
+ import asyncio
9
+ import importlib
10
+ import importlib.util
11
+ import os
12
+
13
+ from fp_core import display
14
+
15
+ # 缓存:命令名 → 模块对象
16
+ _commands: dict[str, "CommandModule"] = {}
17
+
18
+
19
+ # 类型标注
20
+ class CommandModule:
21
+ name: str
22
+ aliases: list[str]
23
+ description: str
24
+
25
+ # execute 返回 (已处理, 输出文本);
26
+ # 兼容旧版:也可只返回 bool(自动转为 ("", False/True))
27
+ # 同步或异步均可,由 execute() 自动适配
28
+ async def execute(self, arg: str) -> tuple[bool, str]: ...
29
+
30
+
31
+ def _discover_commands():
32
+ """扫描并注册所有命令模块(内置 → 用户,同名覆盖)"""
33
+ global _commands
34
+ _commands = {}
35
+
36
+ builtin_dir = os.path.dirname(os.path.abspath(__file__))
37
+ _scan_dir(builtin_dir, "fp_core.commands")
38
+
39
+ # 用户命令目录
40
+ _xdg_data = os.environ.get("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share"))
41
+ user_dir = os.path.join(_xdg_data, "fp", "commands")
42
+ _scan_dir(user_dir) # 直接 import 路径,通过 sys.path 解析
43
+
44
+
45
+ def _scan_dir(directory: str, package_prefix: str | None = None):
46
+ """扫描单个目录下的命令文件"""
47
+ if not os.path.isdir(directory):
48
+ return
49
+
50
+ for fname in sorted(os.listdir(directory)):
51
+ if not fname.endswith(".py") or fname == "__init__.py":
52
+ continue
53
+
54
+ mod_name = fname[:-3]
55
+ try:
56
+ if package_prefix:
57
+ mod = importlib.import_module(f"{package_prefix}.{mod_name}")
58
+ else:
59
+ spec = importlib.util.spec_from_file_location(mod_name, os.path.join(directory, fname))
60
+ if spec is None or spec.loader is None:
61
+ continue
62
+ mod = importlib.util.module_from_spec(spec)
63
+ spec.loader.exec_module(mod)
64
+ except Exception as e:
65
+ display.warning(f"⚠️ 命令加载失败 [{mod_name}]: {e}")
66
+ continue
67
+
68
+ # 校验接口
69
+ if not hasattr(mod, "name") or not hasattr(mod, "execute"):
70
+ display.warning(f"⚠️ 命令模块 [{mod_name}] 缺少 name/execute,已跳过")
71
+ continue
72
+
73
+ name = mod.name
74
+ if name in _commands:
75
+ display.warning(f"⚠️ 命令 [{name}] 重复定义,已覆盖")
76
+ _commands[name] = mod
77
+
78
+ # 注册别名
79
+ for alias in getattr(mod, "aliases", []):
80
+ if alias in _commands:
81
+ display.warning(f"⚠️ 别名 [{alias}] 与已有命令/别名冲突,已跳过")
82
+ continue
83
+ _commands[alias] = mod
84
+
85
+
86
+ _discover_commands()
87
+
88
+
89
+ def get_command(name: str) -> CommandModule | None:
90
+ """根据命令名(含斜杠)或别名查找命令模块"""
91
+ return _commands.get(name)
92
+
93
+
94
+ def get_all_commands() -> dict[str, str]:
95
+ """返回 {命令名: 描述} 字典(只返回主名称,不含别名)"""
96
+ result: dict[str, str] = {}
97
+ seen: set[int] = set()
98
+ for name, mod in _commands.items():
99
+ mod_id = id(mod)
100
+ if mod_id in seen:
101
+ continue
102
+ if hasattr(mod, "name") and mod.name == name:
103
+ result[name] = getattr(mod, "description", "")
104
+ seen.add(mod_id)
105
+ return result
106
+
107
+
108
+ async def execute(agent, cmd_name: str, arg: str) -> tuple[bool, str]:
109
+ """执行命令,返回 (是否已处理, 输出文本)。
110
+
111
+ 兼容旧版只返回 bool 的命令(自动补为 ("", False/True))。
112
+ 新版命令可返回 tuple[bool, str] 或 tuple[bool, str, str]。
113
+ """
114
+ mod = get_command(cmd_name)
115
+ if mod is None:
116
+ return (False, "")
117
+
118
+ # 执行命令(自动适配同步/异步)
119
+ if asyncio.iscoroutinefunction(mod.execute):
120
+ result = await mod.execute(agent, arg)
121
+ else:
122
+ result = mod.execute(agent, arg)
123
+
124
+ # 兼容旧版:只返回 bool
125
+ if isinstance(result, bool):
126
+ return (result, "")
127
+
128
+ # 新版:返回 (handled, output)
129
+ if isinstance(result, tuple):
130
+ return result
131
+
132
+ return (True, str(result))
@@ -0,0 +1,73 @@
1
+ """back 命令 — 回退到对话的某个历史时刻
2
+
3
+ 用法:
4
+ /back list 查看历史消息列表(仅非 system 消息,按 1-based 编号)
5
+ /back <index> 回退到指定位置(删除后续消息)
6
+ /back <index> 2 同上(删除后续消息)
7
+ """
8
+
9
+ name = "back"
10
+ aliases = []
11
+ description = "回退到对话的某个历史时刻。用法: /back list 查看列表, /back <N> 直接回退"
12
+
13
+
14
+ async def execute(agent, arg: str) -> tuple[bool, str]:
15
+ parts = arg.strip().split()
16
+
17
+ if not parts:
18
+ msg = "❌ 用法: /back list (查看列表) 或 /back <N> (直接回退)"
19
+ agent.io.error(msg)
20
+ return (True, msg)
21
+
22
+ cmd = parts[0]
23
+
24
+ # ── /back list ─────────────────────────────────────────────
25
+ if cmd == "list":
26
+ history_msgs = agent.get_history_for_display()
27
+ if not history_msgs:
28
+ msg = "没有历史记录"
29
+ agent.io.info(msg)
30
+ return (True, msg)
31
+
32
+ roles_zh = {"user": "👤 用户", "assistant": "🤖 AI", "tool": "🔧 工具"}
33
+ lines = [f"📜 对话历史(共 {len(history_msgs)} 条消息,使用 /back <N> 回退):"]
34
+ for i, msg in enumerate(history_msgs):
35
+ role = roles_zh.get(msg["role"], msg["role"])
36
+ content = msg.get("content", "")
37
+ if msg["role"] == "tool":
38
+ content = content[:60] + "..." if len(content) > 60 else content
39
+ else:
40
+ content = content[:120] + "..." if len(content) > 120 else content
41
+ content = content.replace("\n", " ")
42
+ lines.append(f" [{i + 1:3d}] {role}: {content}")
43
+
44
+ agent.io.info(lines[0])
45
+ for line in lines[1:]:
46
+ agent.io.item(line)
47
+
48
+ return (True, "\n".join(lines))
49
+
50
+ # ── /back <index> [2] ──────────────────────────────────────
51
+ try:
52
+ index = int(cmd)
53
+ except ValueError:
54
+ msg = f"❌ 无效参数:'{cmd}' — 请用 /back list 查看列表,/back <N> 回退"
55
+ agent.io.error(msg)
56
+ return (True, msg)
57
+
58
+ mode = None
59
+ if len(parts) >= 2:
60
+ try:
61
+ mode = int(parts[1])
62
+ if mode != 2:
63
+ msg = "❌ mode=1(保留后续消息)暂不支持,请用 mode=2 或 /fork"
64
+ agent.io.error(msg)
65
+ return (True, msg)
66
+ except ValueError:
67
+ msg = f"❌ 无效参数:'{parts[1]}' 不是数字"
68
+ agent.io.error(msg)
69
+ return (True, msg)
70
+
71
+ result = await agent.back(target_idx=index, mode=mode)
72
+ agent.io.info(result)
73
+ return (True, result)
@@ -0,0 +1,14 @@
1
+ """clear 命令 — 清空当前会话"""
2
+
3
+ from fp_core import display
4
+
5
+ name = "clear"
6
+ aliases = []
7
+ description = "清空当前会话"
8
+
9
+
10
+ def execute(agent, arg: str) -> tuple[bool, str]:
11
+ agent.clear_session()
12
+ msg = "🧹 当前会话已清空"
13
+ display.info(msg)
14
+ return (True, msg)
@@ -0,0 +1,10 @@
1
+ """compact 命令 — 压缩对话历史(异步)"""
2
+
3
+ name = "compact"
4
+ aliases = []
5
+ description = "压缩对话历史"
6
+
7
+
8
+ async def execute(agent, arg: str) -> tuple[bool, str]:
9
+ await agent.compact_context()
10
+ return (True, "📦 历史已压缩")
@@ -0,0 +1,27 @@
1
+ """exit! 命令 — 核弹级退出:删除当前会话、不留痕迹"""
2
+
3
+ import os
4
+
5
+ from fp_core import display
6
+
7
+ name = "exit!"
8
+ aliases = []
9
+ description = "核弹级退出:删除当前会话、不留痕迹"
10
+
11
+
12
+ def execute(agent, arg: str) -> bool:
13
+ sid = agent.session.session_id
14
+ path = agent.session.get_session_path()
15
+
16
+ # 标记核弹退出 — shutdown 时会删除会话文件
17
+ agent.set_nuclear_exit()
18
+
19
+ # 提前删除文件(shutdown 也会删,双重保险)
20
+ if os.path.exists(path):
21
+ try:
22
+ os.remove(path)
23
+ display.info(f"💥 会话 {sid} 已删除,不留痕迹")
24
+ except Exception as e:
25
+ display.warning(f"⚠️ 删除失败: {e}")
26
+
27
+ raise SystemExit()
@@ -0,0 +1,9 @@
1
+ """exit 命令 — 退出程序"""
2
+
3
+ name = "exit"
4
+ aliases = ["quit"]
5
+ description = "退出程序"
6
+
7
+
8
+ def execute(agent, arg: str) -> bool:
9
+ raise SystemExit()