unframed 0.1.0__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 (37) hide show
  1. unframed-0.1.0/.gitignore +34 -0
  2. unframed-0.1.0/LICENSE.txt +18 -0
  3. unframed-0.1.0/PKG-INFO +271 -0
  4. unframed-0.1.0/README.md +243 -0
  5. unframed-0.1.0/build/build.py +78 -0
  6. unframed-0.1.0/docs/HELP.md +182 -0
  7. unframed-0.1.0/docs/SEED_SPEC.md +214 -0
  8. unframed-0.1.0/pyproject.toml +73 -0
  9. unframed-0.1.0/seeds/cyberpunk.json +4 -0
  10. unframed-0.1.0/seeds/cyberpunk.md +42 -0
  11. unframed-0.1.0/seeds/lonely_star.json +4 -0
  12. unframed-0.1.0/seeds/lonely_star.md +65 -0
  13. unframed-0.1.0/seeds/loop_day.json +4 -0
  14. unframed-0.1.0/seeds/loop_day.md +77 -0
  15. unframed-0.1.0/seeds/neon_verdict.json +4 -0
  16. unframed-0.1.0/seeds/neon_verdict.md +76 -0
  17. unframed-0.1.0/seeds/rust_oasis.json +4 -0
  18. unframed-0.1.0/seeds/rust_oasis.md +62 -0
  19. unframed-0.1.0/seeds/sudo_apt_survival.json +4 -0
  20. unframed-0.1.0/seeds/sudo_apt_survival.md +95 -0
  21. unframed-0.1.0/seeds/tide_prison.json +4 -0
  22. unframed-0.1.0/seeds/tide_prison.md +61 -0
  23. unframed-0.1.0/seeds/underground_throne.json +4 -0
  24. unframed-0.1.0/seeds/underground_throne.md +76 -0
  25. unframed-0.1.0/src/unframed/__about__.py +4 -0
  26. unframed-0.1.0/src/unframed/__init__.py +23 -0
  27. unframed-0.1.0/src/unframed/cli.py +940 -0
  28. unframed-0.1.0/src/unframed/engine.py +1150 -0
  29. unframed-0.1.0/src/unframed/render.py +150 -0
  30. unframed-0.1.0/src/unframed/settings.py +92 -0
  31. unframed-0.1.0/src/unframed/tui/__init__.py +1 -0
  32. unframed-0.1.0/src/unframed/tui/app.py +1115 -0
  33. unframed-0.1.0/tests/__init__.py +3 -0
  34. unframed-0.1.0/tests/test_engine.py +308 -0
  35. unframed-0.1.0/tests/test_plot.py +146 -0
  36. unframed-0.1.0/tests/test_render.py +141 -0
  37. unframed-0.1.0/tests/test_settings.py +120 -0
@@ -0,0 +1,34 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ venv/
6
+ .venv/
7
+ dist/
8
+ *.spec
9
+
10
+ # PyInstaller output
11
+ build/unframed/
12
+ build/*.exe
13
+ build/*.manifest
14
+
15
+ # IDE
16
+ .vscode/
17
+ .idea/
18
+
19
+ # OS
20
+ .DS_Store
21
+ Thumbs.db
22
+
23
+ # Environment
24
+ .env
25
+ .env.*
26
+ game.json
27
+ .marscode/
28
+ password.txt
29
+
30
+ # Superpowers brainstorming artifacts
31
+ docs/superpowers/
32
+
33
+ # Runtime
34
+ *.log
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026-present zaf-x <baoshuwen2013@outlook.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,271 @@
1
+ Metadata-Version: 2.4
2
+ Name: unframed
3
+ Version: 0.1.0
4
+ Summary: An unframed AI-driven narrative game - AI builds the world, rules, characters and story from scratch.
5
+ Project-URL: Documentation, https://github.com/zaf-x/unframed#readme
6
+ Project-URL: Issues, https://github.com/zaf-x/unframed/issues
7
+ Project-URL: Source, https://github.com/zaf-x/unframed
8
+ Author-email: zaf-x <baoshuwen2013@outlook.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.txt
11
+ Keywords: ai,game,llm,narrative,storytelling
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: Implementation :: CPython
20
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
21
+ Requires-Python: >=3.8
22
+ Requires-Dist: colorama>=0.4.6
23
+ Requires-Dist: openai>=1.0.0
24
+ Requires-Dist: rich>=13.0.0
25
+ Requires-Dist: textual>=0.41.0
26
+ Requires-Dist: zaf-ai-util>=0.4.0
27
+ Description-Content-Type: text/markdown
28
+
29
+ # unframed
30
+
31
+ [![Python](https://img.shields.io/badge/python-3.8+-blue.svg)]()
32
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.txt)
33
+ [![Version](https://img.shields.io/badge/version-0.1.0-blue)]()
34
+ [![GitHub](https://img.shields.io/badge/GitHub-zaf--x%2Funframed-181717?logo=github)](https://github.com/zaf-x/unframed)
35
+
36
+ **AI 自举叙事游戏** — AI 从零开始构建世界观、规则、角色与剧情。
37
+
38
+ ## 核心设计
39
+
40
+ - **AI 是唯一的创世者**:没有硬编码的游戏机制,所有规则由 AI 在运行时自行定义。
41
+ - **状态即真相**:游戏世界的唯一状态存储在 `vars` 字典中,AI 通过工具调用显式读写。
42
+ - **记忆分层**:核心区 (Pinned) / 活跃区 (Active) / 目录区 (Catalog) 三层显示,防止上下文溢出。
43
+ - **元数据锁定**:变量语义 (`meta`) 一旦定义不可覆盖,确保概念一致性。
44
+ - **剧情规划**:AI 构建剧情树,提前规划走向,跳过的分支自动修剪。
45
+ - **子AI系统**:AI 可创建持久化的 NPC 角色,拥有独立记忆。
46
+
47
+ ## 安装
48
+
49
+ Python 3.8+ 环境:
50
+
51
+ ```bash
52
+ pip install git+https://github.com/zaf-x/unframed.git
53
+ ```
54
+
55
+ 或克隆后本地安装:
56
+
57
+ ```bash
58
+ git clone https://github.com/zaf-x/unframed.git
59
+ cd unframed
60
+ pip install .
61
+ ```
62
+
63
+ ## 使用
64
+
65
+ ```bash
66
+ # 需要设置 OPENAI_API_KEY 环境变量,或在 TUI 设置页填写
67
+ export OPENAI_API_KEY="sk-..."
68
+ unframed # 默认启动 TUI
69
+
70
+ unframed --cli # CLI 模式
71
+
72
+ # 指定模型(支持所有 OpenAI 兼容 API)
73
+ unframed --model deepseek-chat
74
+
75
+ # 自定义 API 地址
76
+ unframed --base-url https://api.example.com/v1
77
+
78
+ # 调整温度(越高越有创意,越低越稳定)
79
+ unframed --temperature 0.9
80
+
81
+ # 加载种子(剧本),自动使用 CLI 模式
82
+ unframed --seed seeds/cyberpunk.md
83
+
84
+ # 继续上次游戏,自动使用 CLI 模式
85
+ unframed --continue
86
+
87
+ # 显示帮助
88
+ unframed --help
89
+ ```
90
+
91
+ > **注**:使用 `--seed`、`--continue` 或 `--debug` 时会自动切换到 CLI 模式。
92
+
93
+ ## CLI 选项
94
+
95
+ ```
96
+ unframed [--api-key API_KEY] [--base-url BASE_URL] [--model MODEL]
97
+ [--temperature TEMP] [--seed SEED] [--continue] [--cli]
98
+ [--debug] [--help]
99
+ ```
100
+
101
+ | 选项 | 说明 |
102
+ |------|------|
103
+ | `--api-key KEY` | API Key(默认读 `OPENAI_API_KEY` 环境变量或配置文件) |
104
+ | `--base-url URL` | 自定义 API 地址(默认读 `OPENAI_BASE_URL` 或配置文件) |
105
+ | `--model NAME` | 模型名,默认 `gpt-4o`(优先级:参数 > `OPENAI_MODEL` > 配置) |
106
+ | `--temperature NUM` | 采样温度 0-2,默认 `0.7`(优先级:参数 > `OPENAI_TEMPERATURE` > 配置) |
107
+ | `--seed PATH` | 种子文件路径(Markdown),跳过菜单直接加载(自动 CLI) |
108
+ | `--continue` | 继续上次游戏(自动 CLI) |
109
+ | `--cli` | 使用 CLI 模式(默认 TUI) |
110
+ | `--debug` | 显示工具调用详情(自动 CLI) |
111
+ | `--help` | 显示玩家手册 |
112
+
113
+ 设置保存在 `~/.unframed_config.json`(权限 0o600),TUI 中主菜单→"设置"即可修改。
114
+
115
+ ### 环境变量
116
+
117
+ | 变量 | 说明 |
118
+ |------|------|
119
+ | `OPENAI_API_KEY` | API Key |
120
+ | `OPENAI_BASE_URL` | API 地址(如 `https://api.example.com/v1`) |
121
+ | `OPENAI_MODEL` | 模型名(默认 `gpt-4o`) |
122
+ | `OPENAI_TEMPERATURE` | 采样温度(默认 `0.7`) |
123
+
124
+ 读取优先级:**CLI 参数 > 环境变量 > 配置文件 > 内置默认**
125
+
126
+ ## TUI 模式
127
+
128
+ ### TUI 界面布局
129
+
130
+ ```
131
+ ┌──────────────────────────────────────────────────┐
132
+ │ U N F R A M E D 14:30:21 │
133
+ ├────────────────────────────────┬─────────────────┤
134
+ │ │ ## 角色状态 │
135
+ │ 叙事区域 │ - **生命值**: 85 │
136
+ │ (AI 的故事输出,Markdown 渲染) │ - **位置**: 断指 │
137
+ │ │ 酒吧 │
138
+ │ │ │
139
+ │ │ --- │
140
+ │ │ 回合 12 │
141
+ ├────────────────────────────────┴─────────────────┤
142
+ │ AI 正在构思剧情... │
143
+ ├──────────────────────────────────────────────────┤
144
+ │ > 输入你的行动... [发送] │
145
+ └──────────────────────────────────────────────────┘
146
+ ```
147
+
148
+ - **左侧**:叙事输出区,AI 生成的故事以 Markdown 渲染
149
+ - **右侧**:角色状态面板 — AI 使用 `show_var` 工具标记为玩家可见的变量
150
+ - **底部**:输入框,输入你的行动或对话
151
+ - **状态栏**:AI 处理时显示当前操作(如"正在修改世界状态")
152
+
153
+ ### 启动菜单
154
+
155
+ | 选项 | 说明 |
156
+ |------|------|
157
+ | **新建存档** | 输入存档名 → 选择种子 → 开始新游戏 |
158
+ | **继续上一个存档** | 直接回到上次玩的存档(仅在有历史时显示) |
159
+ | **加载存档** | 从已有存档中选择一个加载 |
160
+ | **存档管理** | 查看/保存/删除/重命名所有存档 |
161
+ | **设置** | 配置 API Key、模型、温度等 |
162
+ | **文档** | 查看玩家手册 |
163
+ | **退出** | 退出游戏 |
164
+
165
+ ### TUI 快捷键
166
+
167
+ | 快捷键 | 功能 |
168
+ |--------|------|
169
+ | `Enter` | 发送消息 |
170
+ | `Escape` | 返回主菜单(当前回合会先自动存档) |
171
+ | `Ctrl+S` | 打开存档管理器(Enter=保存,Delete=删除,R=重命名) |
172
+ | `Ctrl+L` | 打开读档列表 |
173
+ | `Ctrl+D` | 打开删档列表 |
174
+
175
+ ## CLI 模式
176
+
177
+ ### 启动菜单
178
+
179
+ | 选项 | 说明 |
180
+ |------|------|
181
+ | **新建存档** | 输入存档名 → 选择种子 → 开始新游戏 |
182
+ | **继续上一个存档** | 直接回到上次玩的存档 |
183
+ | **加载存档** | 从已有存档中选择一个加载 |
184
+ | **帮助文档** | 查看玩家手册 |
185
+
186
+ ### 游戏中命令
187
+
188
+ | 命令 | 说明 |
189
+ |------|------|
190
+ | 直接输入 | 你的行动或对话,AI 会推进剧情 |
191
+ | `/save` | 打开存档菜单 |
192
+ | `/save 我的存档` | 快速存到指定名称(新建或覆盖) |
193
+ | `/save list` | 列出所有存档 |
194
+ | `/load` | 打开读档菜单 |
195
+ | `/load 我的存档` | 快速加载指定名称的存档 |
196
+ | `/delete` | 打开删档菜单 |
197
+ | `/quit` | 退出游戏 |
198
+
199
+ ## 种子(Seed)
200
+
201
+ 种子是游戏的"剧本文件",定义了世界观、规则、角色和目标。每个种子由一对文件组成:`seeds/<名称>.json`(元数据)和 `seeds/<名称>.md`(剧本内容)。游戏启动时自动识别 `seeds/` 下所有 `.json` 索引文件。
202
+
203
+ 内置种子:
204
+
205
+ - **赛博朋克:深渊回响** — 2087 年,永夜之城"新重庆"
206
+ - **潮汐监狱** — 2147 年,深海环形海上堡垒
207
+ - **锈蚀边境:绿洲协议** — 2147 年,核冬天后的废土求生
208
+ - **孤星:坠落日** — 2247 年,殖民船坠毁后的外星求生
209
+ - **霓虹裁决** — 2140 年,同步轨道升降梯内
210
+ - **循环日** — 无法逃脱的时间循环
211
+ - **sudo apt 求生记** — 当 Linux 系统故障出现在神秘世界
212
+ - **地下王座** — 地下王国的权力与阴谋
213
+
214
+ 你也可以自己编写种子——参考 `docs/SEED_SPEC.md`。
215
+
216
+ ## 子AI系统(NPC)
217
+
218
+ unframed 支持 AI 在游戏中创建和管理长期存在的子 AI 角色,作为游戏世界中的 NPC、同伴或组织:
219
+
220
+ | 工具 | 功能 |
221
+ |------|------|
222
+ | **创建角色** | AI 可创建拥有独立性格、知识范围和记忆的 NPC |
223
+ | **与角色对话** | AI 可与已有 NPC 对话,并根据其性格做出回应 |
224
+ | **移除角色** | NPC 死亡或离开时,AI 可终止该角色 |
225
+
226
+ 子 AI 可以读取世界状态变量了解当前局势,但不能直接修改它们。它们拥有独立对话记忆,会记住每一次交流。玩家通过主 AI 的叙事感知子 AI 的行为,子 AI 不直接与玩家交互。
227
+
228
+ ## 存档
229
+
230
+ 存档文件存储在 `~/.unframed_saves/` 目录,每局一个 UUID 文件(`{uuid}.json`)。
231
+
232
+ - **自动保存**:每轮自动保存到当前存档
233
+ - **继续存档**:主菜单"继续上一个存档"可直接回到上次的游戏
234
+ - **存档管理**:TUI 中 `Ctrl+S` 可保存/重命名/删除/新建存档
235
+ - **存档内容**:回合数、变量数据库、对话历史、剧情树、子 AI 状态
236
+
237
+ ## 开发
238
+
239
+ ```bash
240
+ git clone https://github.com/zaf-x/unframed.git
241
+ cd unframed
242
+ pip install -e .
243
+ ```
244
+
245
+ 运行测试:
246
+
247
+ ```bash
248
+ pip install pytest
249
+ python -m pytest tests/
250
+ ```
251
+
252
+ ## 架构
253
+
254
+ ```text
255
+ src/unframed/
256
+ ├── __about__.py — 版本信息
257
+ ├── __init__.py — 公共 API
258
+ ├── engine.py — 核心引擎:状态管理、工具定义、Prompt 组装、游戏循环
259
+ ├── cli.py — 交互式 CLI 入口(启动菜单、命令行命令)
260
+ ├── settings.py — 持久化配置(~/.unframed_config.json)
261
+ ├── render.py — BBCode → ANSI 渲染器
262
+ └── tui/
263
+ ├── __init__.py — TUI 模块声明
264
+ └── app.py — Textual TUI 前端(图形界面)
265
+ ```
266
+
267
+ 基于 [ai-util](https://github.com/zaf-x/ai-util) 框架。
268
+
269
+ ## License
270
+
271
+ `unframed` 基于 MIT 协议开源。
@@ -0,0 +1,243 @@
1
+ # unframed
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.8+-blue.svg)]()
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.txt)
5
+ [![Version](https://img.shields.io/badge/version-0.1.0-blue)]()
6
+ [![GitHub](https://img.shields.io/badge/GitHub-zaf--x%2Funframed-181717?logo=github)](https://github.com/zaf-x/unframed)
7
+
8
+ **AI 自举叙事游戏** — AI 从零开始构建世界观、规则、角色与剧情。
9
+
10
+ ## 核心设计
11
+
12
+ - **AI 是唯一的创世者**:没有硬编码的游戏机制,所有规则由 AI 在运行时自行定义。
13
+ - **状态即真相**:游戏世界的唯一状态存储在 `vars` 字典中,AI 通过工具调用显式读写。
14
+ - **记忆分层**:核心区 (Pinned) / 活跃区 (Active) / 目录区 (Catalog) 三层显示,防止上下文溢出。
15
+ - **元数据锁定**:变量语义 (`meta`) 一旦定义不可覆盖,确保概念一致性。
16
+ - **剧情规划**:AI 构建剧情树,提前规划走向,跳过的分支自动修剪。
17
+ - **子AI系统**:AI 可创建持久化的 NPC 角色,拥有独立记忆。
18
+
19
+ ## 安装
20
+
21
+ Python 3.8+ 环境:
22
+
23
+ ```bash
24
+ pip install git+https://github.com/zaf-x/unframed.git
25
+ ```
26
+
27
+ 或克隆后本地安装:
28
+
29
+ ```bash
30
+ git clone https://github.com/zaf-x/unframed.git
31
+ cd unframed
32
+ pip install .
33
+ ```
34
+
35
+ ## 使用
36
+
37
+ ```bash
38
+ # 需要设置 OPENAI_API_KEY 环境变量,或在 TUI 设置页填写
39
+ export OPENAI_API_KEY="sk-..."
40
+ unframed # 默认启动 TUI
41
+
42
+ unframed --cli # CLI 模式
43
+
44
+ # 指定模型(支持所有 OpenAI 兼容 API)
45
+ unframed --model deepseek-chat
46
+
47
+ # 自定义 API 地址
48
+ unframed --base-url https://api.example.com/v1
49
+
50
+ # 调整温度(越高越有创意,越低越稳定)
51
+ unframed --temperature 0.9
52
+
53
+ # 加载种子(剧本),自动使用 CLI 模式
54
+ unframed --seed seeds/cyberpunk.md
55
+
56
+ # 继续上次游戏,自动使用 CLI 模式
57
+ unframed --continue
58
+
59
+ # 显示帮助
60
+ unframed --help
61
+ ```
62
+
63
+ > **注**:使用 `--seed`、`--continue` 或 `--debug` 时会自动切换到 CLI 模式。
64
+
65
+ ## CLI 选项
66
+
67
+ ```
68
+ unframed [--api-key API_KEY] [--base-url BASE_URL] [--model MODEL]
69
+ [--temperature TEMP] [--seed SEED] [--continue] [--cli]
70
+ [--debug] [--help]
71
+ ```
72
+
73
+ | 选项 | 说明 |
74
+ |------|------|
75
+ | `--api-key KEY` | API Key(默认读 `OPENAI_API_KEY` 环境变量或配置文件) |
76
+ | `--base-url URL` | 自定义 API 地址(默认读 `OPENAI_BASE_URL` 或配置文件) |
77
+ | `--model NAME` | 模型名,默认 `gpt-4o`(优先级:参数 > `OPENAI_MODEL` > 配置) |
78
+ | `--temperature NUM` | 采样温度 0-2,默认 `0.7`(优先级:参数 > `OPENAI_TEMPERATURE` > 配置) |
79
+ | `--seed PATH` | 种子文件路径(Markdown),跳过菜单直接加载(自动 CLI) |
80
+ | `--continue` | 继续上次游戏(自动 CLI) |
81
+ | `--cli` | 使用 CLI 模式(默认 TUI) |
82
+ | `--debug` | 显示工具调用详情(自动 CLI) |
83
+ | `--help` | 显示玩家手册 |
84
+
85
+ 设置保存在 `~/.unframed_config.json`(权限 0o600),TUI 中主菜单→"设置"即可修改。
86
+
87
+ ### 环境变量
88
+
89
+ | 变量 | 说明 |
90
+ |------|------|
91
+ | `OPENAI_API_KEY` | API Key |
92
+ | `OPENAI_BASE_URL` | API 地址(如 `https://api.example.com/v1`) |
93
+ | `OPENAI_MODEL` | 模型名(默认 `gpt-4o`) |
94
+ | `OPENAI_TEMPERATURE` | 采样温度(默认 `0.7`) |
95
+
96
+ 读取优先级:**CLI 参数 > 环境变量 > 配置文件 > 内置默认**
97
+
98
+ ## TUI 模式
99
+
100
+ ### TUI 界面布局
101
+
102
+ ```
103
+ ┌──────────────────────────────────────────────────┐
104
+ │ U N F R A M E D 14:30:21 │
105
+ ├────────────────────────────────┬─────────────────┤
106
+ │ │ ## 角色状态 │
107
+ │ 叙事区域 │ - **生命值**: 85 │
108
+ │ (AI 的故事输出,Markdown 渲染) │ - **位置**: 断指 │
109
+ │ │ 酒吧 │
110
+ │ │ │
111
+ │ │ --- │
112
+ │ │ 回合 12 │
113
+ ├────────────────────────────────┴─────────────────┤
114
+ │ AI 正在构思剧情... │
115
+ ├──────────────────────────────────────────────────┤
116
+ │ > 输入你的行动... [发送] │
117
+ └──────────────────────────────────────────────────┘
118
+ ```
119
+
120
+ - **左侧**:叙事输出区,AI 生成的故事以 Markdown 渲染
121
+ - **右侧**:角色状态面板 — AI 使用 `show_var` 工具标记为玩家可见的变量
122
+ - **底部**:输入框,输入你的行动或对话
123
+ - **状态栏**:AI 处理时显示当前操作(如"正在修改世界状态")
124
+
125
+ ### 启动菜单
126
+
127
+ | 选项 | 说明 |
128
+ |------|------|
129
+ | **新建存档** | 输入存档名 → 选择种子 → 开始新游戏 |
130
+ | **继续上一个存档** | 直接回到上次玩的存档(仅在有历史时显示) |
131
+ | **加载存档** | 从已有存档中选择一个加载 |
132
+ | **存档管理** | 查看/保存/删除/重命名所有存档 |
133
+ | **设置** | 配置 API Key、模型、温度等 |
134
+ | **文档** | 查看玩家手册 |
135
+ | **退出** | 退出游戏 |
136
+
137
+ ### TUI 快捷键
138
+
139
+ | 快捷键 | 功能 |
140
+ |--------|------|
141
+ | `Enter` | 发送消息 |
142
+ | `Escape` | 返回主菜单(当前回合会先自动存档) |
143
+ | `Ctrl+S` | 打开存档管理器(Enter=保存,Delete=删除,R=重命名) |
144
+ | `Ctrl+L` | 打开读档列表 |
145
+ | `Ctrl+D` | 打开删档列表 |
146
+
147
+ ## CLI 模式
148
+
149
+ ### 启动菜单
150
+
151
+ | 选项 | 说明 |
152
+ |------|------|
153
+ | **新建存档** | 输入存档名 → 选择种子 → 开始新游戏 |
154
+ | **继续上一个存档** | 直接回到上次玩的存档 |
155
+ | **加载存档** | 从已有存档中选择一个加载 |
156
+ | **帮助文档** | 查看玩家手册 |
157
+
158
+ ### 游戏中命令
159
+
160
+ | 命令 | 说明 |
161
+ |------|------|
162
+ | 直接输入 | 你的行动或对话,AI 会推进剧情 |
163
+ | `/save` | 打开存档菜单 |
164
+ | `/save 我的存档` | 快速存到指定名称(新建或覆盖) |
165
+ | `/save list` | 列出所有存档 |
166
+ | `/load` | 打开读档菜单 |
167
+ | `/load 我的存档` | 快速加载指定名称的存档 |
168
+ | `/delete` | 打开删档菜单 |
169
+ | `/quit` | 退出游戏 |
170
+
171
+ ## 种子(Seed)
172
+
173
+ 种子是游戏的"剧本文件",定义了世界观、规则、角色和目标。每个种子由一对文件组成:`seeds/<名称>.json`(元数据)和 `seeds/<名称>.md`(剧本内容)。游戏启动时自动识别 `seeds/` 下所有 `.json` 索引文件。
174
+
175
+ 内置种子:
176
+
177
+ - **赛博朋克:深渊回响** — 2087 年,永夜之城"新重庆"
178
+ - **潮汐监狱** — 2147 年,深海环形海上堡垒
179
+ - **锈蚀边境:绿洲协议** — 2147 年,核冬天后的废土求生
180
+ - **孤星:坠落日** — 2247 年,殖民船坠毁后的外星求生
181
+ - **霓虹裁决** — 2140 年,同步轨道升降梯内
182
+ - **循环日** — 无法逃脱的时间循环
183
+ - **sudo apt 求生记** — 当 Linux 系统故障出现在神秘世界
184
+ - **地下王座** — 地下王国的权力与阴谋
185
+
186
+ 你也可以自己编写种子——参考 `docs/SEED_SPEC.md`。
187
+
188
+ ## 子AI系统(NPC)
189
+
190
+ unframed 支持 AI 在游戏中创建和管理长期存在的子 AI 角色,作为游戏世界中的 NPC、同伴或组织:
191
+
192
+ | 工具 | 功能 |
193
+ |------|------|
194
+ | **创建角色** | AI 可创建拥有独立性格、知识范围和记忆的 NPC |
195
+ | **与角色对话** | AI 可与已有 NPC 对话,并根据其性格做出回应 |
196
+ | **移除角色** | NPC 死亡或离开时,AI 可终止该角色 |
197
+
198
+ 子 AI 可以读取世界状态变量了解当前局势,但不能直接修改它们。它们拥有独立对话记忆,会记住每一次交流。玩家通过主 AI 的叙事感知子 AI 的行为,子 AI 不直接与玩家交互。
199
+
200
+ ## 存档
201
+
202
+ 存档文件存储在 `~/.unframed_saves/` 目录,每局一个 UUID 文件(`{uuid}.json`)。
203
+
204
+ - **自动保存**:每轮自动保存到当前存档
205
+ - **继续存档**:主菜单"继续上一个存档"可直接回到上次的游戏
206
+ - **存档管理**:TUI 中 `Ctrl+S` 可保存/重命名/删除/新建存档
207
+ - **存档内容**:回合数、变量数据库、对话历史、剧情树、子 AI 状态
208
+
209
+ ## 开发
210
+
211
+ ```bash
212
+ git clone https://github.com/zaf-x/unframed.git
213
+ cd unframed
214
+ pip install -e .
215
+ ```
216
+
217
+ 运行测试:
218
+
219
+ ```bash
220
+ pip install pytest
221
+ python -m pytest tests/
222
+ ```
223
+
224
+ ## 架构
225
+
226
+ ```text
227
+ src/unframed/
228
+ ├── __about__.py — 版本信息
229
+ ├── __init__.py — 公共 API
230
+ ├── engine.py — 核心引擎:状态管理、工具定义、Prompt 组装、游戏循环
231
+ ├── cli.py — 交互式 CLI 入口(启动菜单、命令行命令)
232
+ ├── settings.py — 持久化配置(~/.unframed_config.json)
233
+ ├── render.py — BBCode → ANSI 渲染器
234
+ └── tui/
235
+ ├── __init__.py — TUI 模块声明
236
+ └── app.py — Textual TUI 前端(图形界面)
237
+ ```
238
+
239
+ 基于 [ai-util](https://github.com/zaf-x/ai-util) 框架。
240
+
241
+ ## License
242
+
243
+ `unframed` 基于 MIT 协议开源。
@@ -0,0 +1,78 @@
1
+ """
2
+ Build script for PyInstaller.
3
+
4
+ Usage:
5
+ pip install pyinstaller
6
+ python build/build.py
7
+
8
+ Output:
9
+ dist/unframed/ # Directory with all dependencies
10
+ dist/unframed.exe # Windows executable (on Windows)
11
+ dist/unframed # Linux/macOS executable
12
+ """
13
+
14
+ import os
15
+ import sys
16
+ import subprocess
17
+ from pathlib import Path
18
+
19
+ ROOT = Path(__file__).resolve().parent.parent
20
+ DIST = ROOT / "dist"
21
+ SPEC = ROOT / "build" / "unframed.spec"
22
+
23
+
24
+ def main():
25
+ # Ensure seeds/ and docs/ are included
26
+ data_args = []
27
+ seeds_dir = ROOT / "seeds"
28
+ docs_dir = ROOT / "docs"
29
+
30
+ if seeds_dir.is_dir():
31
+ data_args.append(f"--add-data={seeds_dir}{os.pathsep}seeds")
32
+ if docs_dir.is_dir():
33
+ data_args.append(f"--add-data={docs_dir}{os.pathsep}docs")
34
+
35
+ cmd = [
36
+ sys.executable or "python",
37
+ "-m", "PyInstaller",
38
+ "--name=unframed",
39
+ "--onefile", # Single executable
40
+ "--console", # Console window
41
+ "--clean",
42
+ "--noconfirm",
43
+ # Add ai-util as a hidden import (git dependency)
44
+ "--hidden-import=ai_util",
45
+ "--hidden-import=ai_util.bot",
46
+ "--hidden-import=ai_util.agent",
47
+ "--hidden-import=ai_util.tools",
48
+ "--hidden-import=ai_util.sandbox",
49
+ # Textual hidden imports
50
+ "--hidden-import=textual",
51
+ "--hidden-import=textual.app",
52
+ "--hidden-import=textual.screen",
53
+ "--hidden-import=textual.widgets",
54
+ "--hidden-import=textual.containers",
55
+ "--hidden-import=textual.keys",
56
+ # Rich hidden imports
57
+ "--hidden-import=rich.markdown",
58
+ "--hidden-import=rich.console",
59
+ "--hidden-import=rich.panel",
60
+ "--hidden-import=rich.text",
61
+ # Colorama
62
+ "--hidden-import=colorama",
63
+ # OpenAI
64
+ "--hidden-import=openai",
65
+ *data_args,
66
+ str(ROOT / "src" / "unframed" / "__init__.py"),
67
+ ]
68
+
69
+ print("Running PyInstaller...")
70
+ print(" ".join(cmd))
71
+ subprocess.check_call(cmd, cwd=ROOT)
72
+
73
+ print(f"\nDone! Executable at: {DIST / 'unframed'}{'.exe' if sys.platform == 'win32' else ''}")
74
+ print("Note: seeds/ and docs/ are bundled inside the executable.")
75
+
76
+
77
+ if __name__ == "__main__":
78
+ main()