@pencil-agent/nano-pencil 1.3.0 → 1.3.2

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/README.md CHANGED
@@ -1,102 +1,398 @@
1
- # NanoPencil
1
+ <div align="center">
2
2
 
3
- CLI writing agent with read, bash, edit, write tools and session management. Based on [pi](https://github.com/badlogic/pi-mono); supports DashScope Coding Plan only.
3
+ # NanoPencil
4
4
 
5
- ## 安装
5
+ ### 🤖 The Next-Generation AI Coding Agent for Chinese Developers
6
+
7
+ **让 AI 成为你的编程助手** - 支持文件读写、代码编辑、Shell 命令、会话管理、持久记忆与性格进化
8
+
9
+ [![npm version](https://badge.fury.io/js/%40pencil-agent%2Fnano-pencil.svg)](https://www.npmjs.com/package/@pencil-agent/nano-pencil)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
+ [![Node.js Version](https://img.shields.io/node/v/@pencil-agent/nano-pencil.svg)](https://nodejs.org)
12
+
13
+ [English](./README_EN.md) | 简体中文
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## ✨ 特性
20
+
21
+ NanoPencil 是一个为中文开发者优化的 AI 编程代理,基于 [pi-mono](https://github.com/badlogic/pi-mono) 构建,提供了更强大的功能:
22
+
23
+ - 🛠️ **全功能工具集** - 支持文件读写 (`read`/`write`)、精准编辑 (`edit`)、搜索 (`grep`/`find`)、Bash 执行等
24
+ - 💾 **智能会话管理** - 自动保存会话历史,支持分支、切换、树状浏览
25
+ - 🧠 **持久记忆系统 (NanoMem)** - 自动学习项目知识、用户偏好、错误模式
26
+ - 😊 **AI 性格进化 (NanoSoul)** - AI 性格随交互演化,形成独特风格
27
+ - 🎨 **精美 TUI 界面** - 支持 warm/dark/light 主题,✎ 呼吸加载动画
28
+ - 🔌 **MCP 协议支持** - 内置 Filesystem、Fetch、Puppeteer、SQLite、Git 等工具
29
+ - 🌐 **多模型支持** - 阿里百炼、通义千问、OpenAI、Gemini、Ollama 等
30
+ - 📦 **开箱即用** - 无需复杂配置,安装即用,默认使用百炼 Coding Plan
31
+
32
+ ---
33
+
34
+ ## 🚀 快速开始
35
+
36
+ ### 安装
6
37
 
7
38
  ```bash
8
39
  npm install -g @pencil-agent/nano-pencil
9
40
  ```
10
41
 
11
- ## 首次使用
42
+ ### 首次运行
43
+
44
+ ```bash
45
+ nanopencil
46
+ ```
47
+
48
+ 首次运行会提示输入**阿里百炼 Coding Plan API Key**(格式 `sk-sp-...`),输入后会自动保存到 `~/.nanopencil/agent/auth.json`,之后无需再输。
12
49
 
13
- 配置目录为 `~/.nanopencil/agent/`,与官方 pi(`~/.pi`)互不干扰。
50
+ > 💡 **提示**:百炼 Coding Plan 是专门为代码生成优化的模型,效果出色且价格实惠。
14
51
 
15
- 首次运行(交互模式)会提示输入**百炼 Coding Plan API Key**(格式 `sk-sp-...`),输入后会持久化到 `~/.nanopencil/agent/auth.json`,之后直接使用无需再输。
52
+ ### 开始对话
16
53
 
17
- 非交互模式(管道或 RPC)下若未配置 API Key,会报错并提示先交互运行 `nanopencil` 完成配置。
54
+ 进入交互界面后,直接输入你的需求即可:
18
55
 
19
- ## 运行
56
+ ```
57
+ 你: 帮我写一个 Express 中间件来验证 JWT token
20
58
 
21
- ```bash
22
- nanopencil
59
+ AI: [自动创建 jwt-auth.middleware.ts 文件,包含完整实现]
23
60
  ```
24
61
 
25
- 进入后可直接输入需求,模型会使用 read、write、edit、bash 等工具完成任务。
62
+ ---
63
+
64
+ ## 📖 核心功能
65
+
66
+ ### 1. 工具系统
26
67
 
27
- ### MCP 支持
68
+ NanoPencil 内置强大的工具系统,AI 可以自主调用:
28
69
 
29
- NanoPencil 内置支持 MCP (Model Context Protocol),**默认启用**免费工具(Filesystem、Fetch、Puppeteer、SQLite、Git)。
70
+ | 工具 | 功能 | 示例 |
71
+ |------|------|------|
72
+ | `read` | 读取文件内容 | "查看 package.json" |
73
+ | `write` | 创建/覆盖文件 | "创建 utils/helpers.ts" |
74
+ | `edit` | 精准编辑(基于行号) | "将第 15 行的 `console.log` 改为 `logger.info`" |
75
+ | `grep` | 内容搜索 | "查找所有使用 `useState` 的地方" |
76
+ | `find` | 文件名搜索 | "找到所有测试文件" |
77
+ | `bash` | 执行 Shell 命令 | "运行 npm install 并安装 lodash" |
78
+
79
+ ### 2. 会话管理
80
+
81
+ - **自动保存** - 所有对话自动保存,随时可恢复
82
+ - **会话分支** - 在任意节点创建新分支进行探索
83
+ - **会话切换** - `/resume` 快速切换历史会话
84
+ - **会话树** - `/tree` 可视化浏览会话分支结构
85
+
86
+ ### 3. 记忆与性格 (NanoMem & NanoSoul)
87
+
88
+ **NanoMem** - 项目记忆系统:
89
+ - 📚 自动记录项目知识(API 端点、配置项、架构决策)
90
+ - 💡 学习错误模式和解决方案
91
+ - ❤️ 识别用户偏好和编码风格
92
+ - 🔄 A-MEM 风格的动态关联网络
93
+
94
+ **NanoSoul** - AI 性格系统:
95
+ - 🎭 大五人格 + 编程特质(代码冗长度、抽象层级、安全边际)
96
+ - 🎯 动态专长领域追踪(前端、后端、DevOps、AI 等)
97
+ - 😊 情绪状态建模(信心、好奇心、挫败感、心流)
98
+ - 📈 每次交互后自动进化
30
99
 
31
100
  ```bash
32
- nanopencil # MCP 默认启用
101
+ /soul # 查看 AI 当前性格和统计
102
+ /memory # 查看项目记忆信息
33
103
  ```
34
104
 
105
+ ### 4. 斜线命令
106
+
107
+ | 命令 | 功能 |
108
+ |------|------|
109
+ | `/model` | 切换 AI 模型 |
110
+ | `/thinking` | 设置思考级别 (off/minimal/low/medium/high/xhigh) |
111
+ | `/settings` | 打开设置菜单 |
112
+ | `/clear` 或 `/new` | 清空当前会话 |
113
+ | `/fork` | 从当前消息创建新会话分支 |
114
+ | `/tree` | 浏览会话树 |
115
+ | `/resume` | 恢复历史会话 |
116
+ | `/compact` | 手动压缩上下文 |
117
+ | `/soul` | 查看 AI 性格 |
118
+ | `/memory` | 查看项目记忆 |
119
+ | `/export` | 导出为 HTML |
120
+ | `/login` | 配置 API Key |
121
+
122
+ ### 5. MCP 协议支持
123
+
124
+ NanoPencil **默认启用** MCP (Model Context Protocol),免费内置以下工具:
125
+
126
+ - 📁 **Filesystem** - 文件系统操作
127
+ - 🌐 **Fetch** - HTTP 请求
128
+ - 🎭 **Puppeteer** - 浏览器自动化
129
+ - 🗄️ **SQLite** - 数据库操作
130
+ - 🔧 **Git** - Git 版本控制
131
+
35
132
  详见 [MCP 文档](docs/MCP_GUIDE.md) 和 [内置工具列表](docs/BUILTIN_MCP_TOOLS.md)。
36
133
 
37
- ## 常用命令与快捷键
134
+ ---
38
135
 
39
- 在输入框输入 `/` 可触发命令,例如:
136
+ ## 🎨 界面展示
40
137
 
41
- | 命令 | 说明 |
42
- |------|------|
43
- | `/model` | 切换模型(Qwen3 Coder Plus / Qwen3 Max) |
44
- | `/settings` | 思考级别、主题、会话等设置 |
45
- | `/resume` | 从历史会话中选择继续 |
46
- | `/new` | 新建会话 |
47
- | `/session` | 查看当前会话信息 |
48
- | `/hotkeys` | 查看全部快捷键 |
138
+ ### TUI 界面
49
139
 
50
- 常用快捷键:`Ctrl+L` 选模型,`Ctrl+P` / `Shift+Ctrl+P` 在已选模型间切换,`Escape` 取消/中止。
140
+ ```
141
+ .-~~~~~~~~~-._ _.-~~~~~~~~~-.
142
+ __.' ~. .~ `.__
143
+ .'// \./ \\`.
144
+ .'// | \\`.
145
+ .\'// .-~""~~~~-._ | _,-~~~~""~-. \\\`.
146
+ .\'//.-" `-. .-' \"-.\\\`.
147
+ .\'//______.============-.. \ | / ..-============.______\\\`.
148
+ .\'______________________________\|/______________________________`\.
51
149
 
52
- ## 配置与数据
150
+ nano-pencil v1.3.1
151
+ DashScope Coding Plan · Qwen3 Coder Plus
152
+ C:\Users\Windows11\nanoPencil
53
153
 
54
- | 路径 | 说明 |
55
- |------|------|
56
- | `~/.nanopencil/agent/auth.json` | API Key 等认证信息 |
57
- | `~/.nanopencil/agent/models.json` | 模型配置(默认仅百炼 Coding Plan) |
58
- | `~/.nanopencil/agent/settings.json` | 全局设置 |
59
- | `~/.nanopencil/agent/sessions/` | 会话存储 |
154
+ /model to switch model
155
+
156
+ ────────────────────────────────────────────────────────────────
157
+
158
+ ✎▓ Working... (Ctrl+L to cancel)
159
+
160
+ 你> 帮我重构这个组件
161
+ ```
162
+
163
+ ### 主题支持
164
+
165
+ - 🌙 **Dark** - 经典暗色主题
166
+ - ☀️ **Light** - 明亮清新主题
167
+ - 🧡 **Warm** - 温暖护眼主题(推荐)
168
+
169
+ 使用 `/settings` → `Theme` 切换主题。
170
+
171
+ ---
172
+
173
+ ## 💻 CLI 用法
174
+
175
+ ### 交互模式
60
176
 
61
- ## CLI 示例
177
+ ```bash
178
+ nanopencil # 启动交互界面
179
+ nanopencil -c # 继续最近会话
180
+ nanopencil -r # 选择历史会话
181
+ nanopencil -m qwen3-max # 指定模型启动
182
+ ```
183
+
184
+ ### 单次提问模式
185
+
186
+ ```bash
187
+ nanopencil -p "解释这段代码"
188
+ nanopencil -p "重构 src/app.ts" < src/app.ts
189
+ ```
190
+
191
+ ### 管道模式
62
192
 
63
193
  ```bash
64
- nanopencil # 交互模式
65
- nanopencil -p "总结这段代码" # 单次提问并打印结果
66
- nanopencil -c # 继续最近会话
67
- nanopencil -r # 选择历史会话
68
- nanopencil --list-models # 列出可用模型
69
- nanopencil -h # 帮助
194
+ echo "添加 TypeScript 类型定义" | nanopencil
195
+ cat code.js | nanopencil -p "转成 TypeScript"
70
196
  ```
71
197
 
72
- ## 环境变量
198
+ ### RPC 模式
199
+
200
+ 为 IDE 集成提供 JSON-RPC 接口:
201
+
202
+ ```bash
203
+ nanopencil --rpc
204
+ ```
205
+
206
+ ### 其他选项
207
+
208
+ ```bash
209
+ nanopencil --list-models # 列出可用模型
210
+ nanopencil --version # 显示版本
211
+ nanopencil --help # 显示帮助
212
+ ```
213
+
214
+ ---
215
+
216
+ ## ⚙️ 配置
217
+
218
+ ### 配置目录
219
+
220
+ ```
221
+ ~/.nanopencil/agent/
222
+ ├── auth.json # API Keys 和 OAuth 凭证
223
+ ├── models.json # 模型配置
224
+ ├── settings.json # 全局设置
225
+ ├── sessions/ # 会话历史
226
+ │ └── *.jsonl # 会话文件
227
+ ├── memory/ # NanoMem 记忆存储
228
+ │ ├── knowledge.json # 项目知识
229
+ │ ├── lessons.json # 经验教训
230
+ │ ├── preferences.json # 用户偏好
231
+ │ └── patterns.json # 行为模式
232
+ └── soul/ # NanoSoul 性格存储
233
+ └── profile.json # AI 性格档案
234
+ ```
235
+
236
+ ### 项目级配置
237
+
238
+ 在项目根目录创建 `.pi/settings.json` 可覆盖全局设置:
239
+
240
+ ```json
241
+ {
242
+ "thinkingLevel": "high",
243
+ "theme": "warm",
244
+ "enableSkillCommands": true
245
+ }
246
+ ```
247
+
248
+ ### 上下文文件
249
+
250
+ NanoPencil 会自动加载以下文件作为上下文:
251
+
252
+ - `.pencil-context.md` - 项目特定上下文
253
+ - `.PENCIL.md` - 全局上下文(`~/.nanopencil/agent/.PENCIL.md`)
254
+ - `CLAUDE.md` - Claude 项目指令
255
+ - `AGENTS.md` - 多代理协作指令
256
+
257
+ ### 环境变量
73
258
 
74
259
  | 变量 | 说明 |
75
260
  |------|------|
76
- | `NANOPENCIL_CODING_AGENT_DIR` | 覆盖配置目录(默认 `~/.nanopencil/agent`) |
261
+ | `NANOPENCIL_CODING_AGENT_DIR` | 覆盖配置目录 |
262
+ | `PI_SKIP_VERSION_CHECK` | 跳过版本检查 |
263
+ | `PI_OFFLINE` | 离线模式 |
264
+
265
+ ---
77
266
 
78
- ## 开发
267
+ ## 🔧 开发
268
+
269
+ ### 克隆仓库
79
270
 
80
271
  ```bash
81
- # 克隆仓库
82
- git clone https://codeup.aliyun.com/67d1a8677564dc59f36547a9/nanoPencil.git
272
+ git clone https://github.com/your-org/nanoPencil.git
83
273
  cd nanoPencil
274
+ ```
84
275
 
85
- # 安装依赖
276
+ ### 安装依赖
277
+
278
+ ```bash
86
279
  npm install
280
+ ```
87
281
 
88
- # 开发模式运行
89
- npm run dev
282
+ ### 开发模式
90
283
 
91
- # 构建
284
+ ```bash
285
+ npm run dev # 使用 tsx 直接运行
286
+ npm run dev -- -p "test" # 传递参数
287
+ ```
288
+
289
+ ### 构建
290
+
291
+ ```bash
92
292
  npm run build
293
+ npm run build:watch # 监听模式
294
+ ```
295
+
296
+ ### 运行构建版本
297
+
298
+ ```bash
299
+ npm start # 等同于 node dist/cli.js
300
+ npm start -- -p "test"
301
+ ```
302
+
303
+ ### 项目结构
93
304
 
94
- # 运行构建后的版本
95
- npm start
96
305
  ```
306
+ nanopencil/
307
+ ├── cli/ # CLI 参数解析
308
+ ├── core/ # 核心逻辑
309
+ │ ├── agent-session.ts # 会话管理
310
+ │ ├── extensions/ # 扩展系统
311
+ │ ├── tools/ # 内置工具
312
+ │ └── ...
313
+ ├── modes/ # 运行模式
314
+ │ ├── interactive/ # TUI 交互模式
315
+ │ ├── print-mode.ts # 标准输出模式
316
+ │ └── rpc/ # JSON-RPC 模式
317
+ ├── extensions/ # 内置扩展
318
+ │ ├── simplify/ # MCP 集成
319
+ │ └── ...
320
+ └── packages/ # Monorepo 子包
321
+ ├── nanomem/ # 记忆系统
322
+ └── nanosoul/ # 性格系统
323
+ ```
324
+
325
+ ---
326
+
327
+ ## 🌟 对比其他 AI 编程工具
328
+
329
+ | 特性 | NanoPencil | Cursor | Aider | Continue |
330
+ |------|------------|--------|-------|----------|
331
+ | 中文优化 | ✅ | ❌ | ❌ | ❌ |
332
+ | 持久记忆 | ✅ NanoMem | ❌ | ❌ | ❌ |
333
+ | AI 性格 | ✅ NanoSoul | ❌ | ❌ | ❌ |
334
+ | 多模型支持 | ✅ | ✅ | ❌ | ✅ |
335
+ | 会话管理 | ✅ 树状 | ✅ | ❌ | ✅ |
336
+ | MCP 协议 | ✅ 内置 | ❌ | ❌ | 部分支持 |
337
+ | 开源免费 | ✅ MIT | ❌ | ✅ GPL | ✅ Apache |
338
+ | 百炼优化 | ✅ | ❌ | ❌ | ❌ |
339
+
340
+ ---
341
+
342
+ ## 🤝 贡献
343
+
344
+ 欢迎贡献!请遵循以下流程:
345
+
346
+ 1. Fork 本仓库
347
+ 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
348
+ 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
349
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
350
+ 5. 开启 Pull Request
351
+
352
+ ### 开发指南
353
+
354
+ - 遵循现有代码风格
355
+ - 添加测试覆盖新功能
356
+ - 更新相关文档
357
+ - 确保 `npm run build` 成功
358
+
359
+ ---
360
+
361
+ ## 📄 许可证
362
+
363
+ [MIT License](LICENSE)
364
+
365
+ ---
366
+
367
+ ## 🙏 致谢
368
+
369
+ NanoPencil 基于 [pi-mono](https://github.com/badlogic/pi-mono) 构建,感谢原作者 [Mario Zechner](https://github.com/badlogic) 的卓越工作。
370
+
371
+ 同时感谢以下开源项目:
372
+
373
+ - [@mariozechner/pi-agent-core](https://www.npmjs.com/package/@mariozechner/pi-agent-core)
374
+ - [@mariozechner/pi-ai](https://www.npmjs.com/package/@mariozechner/pi-ai)
375
+ - [@mariozechner/pi-tui](https://www.npmjs.com/package/@mariozechner/pi-tui)
376
+ - [cli-highlight](https://github.com/felixfbecker/cli-highlight)
377
+ - [chalk](https://github.com/chalk/chalk)
378
+
379
+ ---
380
+
381
+ ## 📚 相关资源
382
+
383
+ - [MCP 协议指南](docs/MCP_GUIDE.md)
384
+ - [内置 MCP 工具列表](docs/BUILTIN_MCP_TOOLS.md)
385
+ - [记忆系统文档](docs/MEMORY_SYSTEM.md)
386
+ - [更新日志](CHANGELOG.md)
387
+ - [问题反馈](https://github.com/your-org/nanoPencil/issues)
388
+ - [讨论区](https://github.com/your-org/nanoPencil/discussions)
389
+
390
+ ---
391
+
392
+ <div align="center">
97
393
 
98
- ## 依赖与许可
394
+ **Made with ❤️ by Chinese Developers**
99
395
 
100
- 依赖 [@mariozechner/pi-agent-core](https://www.npmjs.com/package/@mariozechner/pi-agent-core)[@mariozechner/pi-ai](https://www.npmjs.com/package/@mariozechner/pi-ai)[@mariozechner/pi-tui](https://www.npmjs.com/package/@mariozechner/pi-tui) 等。Node.js >= 20。
396
+ [⭐ Star](https://github.com/your-org/nanoPencil) · [🐛 报告问题](https://github.com/your-org/nanoPencil/issues) · [💡 建议](https://github.com/your-org/nanoPencil/discussions)
101
397
 
102
- MIT License.
398
+ </div>
@@ -24,6 +24,11 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
24
24
  },
25
25
  { name: "apikey", description: "Update API key for current provider" },
26
26
  { name: "mcp", description: "Manage MCP servers (list, enable, disable)" },
27
+ { name: "soul", description: "Show AI personality and stats (Soul)" },
28
+ {
29
+ name: "memory",
30
+ description: "Show project memory and knowledge (NanoMem)",
31
+ },
27
32
  { name: "export", description: "Export session to HTML file" },
28
33
  { name: "share", description: "Share session as a secret GitHub gist" },
29
34
  { name: "copy", description: "Copy last agent message to clipboard" },
@@ -25,6 +25,7 @@ export {
25
25
  export { LoginDialogComponent } from "./login-dialog.js";
26
26
  export { ModelSelectorComponent } from "./model-selector.js";
27
27
  export { OAuthSelectorComponent } from "./oauth-selector.js";
28
+ export { PencilLoader } from "./pencil-loader.js";
28
29
  export { ProviderSelectorComponent } from "./provider-selector.js";
29
30
  export {
30
31
  type ModelsCallbacks,
@@ -0,0 +1,139 @@
1
+ /**
2
+ * [INPUT]: NanoMem engine
3
+ * [OUTPUT]: Formatted display of memory stats
4
+ * [POS]: Interactive mode component for /memory command
5
+ */
6
+
7
+ // Use any type for NanoMemEngine since it's a local package
8
+ interface NanoMemEngine {
9
+ getStats(): {
10
+ knowledge: number;
11
+ lessons: number;
12
+ preferences: number;
13
+ facets: number;
14
+ work: number;
15
+ episodes: number;
16
+ totalSessions: number;
17
+ };
18
+ }
19
+
20
+ interface DisplayOptions {
21
+ compact?: boolean;
22
+ }
23
+
24
+ /**
25
+ * Format NanoMem stats for display
26
+ */
27
+ export function formatMemoryStats(
28
+ memory: any,
29
+ options: DisplayOptions = {},
30
+ ): string {
31
+ if (options.compact) {
32
+ return formatCompactMemory(memory);
33
+ }
34
+
35
+ return formatFullMemory(memory);
36
+ }
37
+
38
+ /**
39
+ * Format compact memory stats (single line)
40
+ */
41
+ function formatCompactMemory(memory: NanoMemEngine): string {
42
+ const stats = memory.getStats();
43
+
44
+ const totalMemories =
45
+ stats.knowledge +
46
+ stats.lessons +
47
+ stats.preferences +
48
+ stats.facets +
49
+ stats.episodes;
50
+
51
+ return `📚 Memory: ${totalMemories} 项 | Knowledge: ${stats.knowledge} | Lessons: ${stats.lessons} | Episodes: ${stats.episodes}`;
52
+ }
53
+
54
+ /**
55
+ * Format full memory stats (detailed view)
56
+ */
57
+ function formatFullMemory(memory: NanoMemEngine): string {
58
+ const lines: string[] = [];
59
+
60
+ lines.push("╔═════════════════════════════════════════════════╗");
61
+ lines.push("║ 📚 Project Memory - NanoMem ║");
62
+ lines.push("╠═════════════════════════════════════════════════╣");
63
+ lines.push("║");
64
+
65
+ const stats = memory.getStats();
66
+
67
+ // Memory Types
68
+ const memoryTypes = [
69
+ { type: "knowledge", name: "知识", emoji: "📖", count: stats.knowledge },
70
+ { type: "lessons", name: "经验教训", emoji: "💡", count: stats.lessons },
71
+ {
72
+ type: "preferences",
73
+ name: "用户偏好",
74
+ emoji: "❤️",
75
+ count: stats.preferences,
76
+ },
77
+ { type: "facets", name: "模式/困境", emoji: "🧩", count: stats.facets },
78
+ { type: "work", name: "工作摘要", emoji: "📋", count: stats.work },
79
+ { type: "episodes", name: "会话记录", emoji: "📝", count: stats.episodes },
80
+ ];
81
+
82
+ lines.push("║ 📊 Memory Types");
83
+ lines.push("║ ────────────────────────────────────────────────");
84
+
85
+ for (const mem of memoryTypes) {
86
+ const bar = createBar(Math.min(mem.count / 100, 1), 15);
87
+ lines.push(
88
+ `║ ${mem.emoji} ${mem.name.padEnd(12)} ${bar} ${mem.count.toString().padStart(5)} 项`,
89
+ );
90
+ }
91
+
92
+ lines.push("║");
93
+ lines.push("║ 🔍 Recent Knowledge (Top 5)");
94
+ lines.push("║ ────────────────────────────────────────────────");
95
+
96
+ // Note: This would require loading actual entries
97
+ // For now, show stats only
98
+ lines.push(`║ (共 ${stats.knowledge} 条项目知识)`);
99
+ lines.push(`║ 最近更新: ${stats.totalSessions > 0 ? "本次会话" : "无"}`);
100
+
101
+ lines.push("║");
102
+ lines.push("║ 💡 Lessons Learned");
103
+ lines.push("║ ────────────────────────────────────────────────");
104
+ lines.push(`║ (共 ${stats.lessons} 条经验教训)`);
105
+ lines.push(` ⚠️ 从错误中学习,避免重复犯错`);
106
+
107
+ lines.push("║");
108
+ lines.push("║ 🧩 Patterns & Struggles");
109
+ lines.push("║ ────────────────────────────────────────────────");
110
+ lines.push(`║ 模式: ${stats.facets} 条`);
111
+ lines.push(` 困境: 已识别的行为模式`);
112
+
113
+ lines.push("║");
114
+ lines.push("║ 📝 Session History");
115
+ lines.push("║ ────────────────────────────────────────────────");
116
+ lines.push(`║ 会话记录: ${stats.episodes} 条`);
117
+ lines.push(` 总会话数: ${stats.totalSessions}`);
118
+
119
+ lines.push("║");
120
+ lines.push("╚═════════════════════════════════════════════════╝");
121
+
122
+ return lines.join("\n");
123
+ }
124
+
125
+ /**
126
+ * Create a visual bar for counts
127
+ */
128
+ function createBar(filled: number, width: number): string {
129
+ const intFilled = Math.floor(filled);
130
+ const partial = Math.floor((filled - intFilled) * 8);
131
+
132
+ let bar = "█".repeat(intFilled);
133
+ if (partial > 0) {
134
+ bar += "▓".repeat(1); // Partial fill
135
+ }
136
+ bar += "░".repeat(width - intFilled - (partial > 0 ? 1 : 0));
137
+
138
+ return bar;
139
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * PencilLoader - Custom loader with ✎ breathing animation
3
+ * Replaces the default spinner with a pencil icon that pulses
4
+ */
5
+
6
+ import { Container, Spacer, Text, type TUI } from "@mariozechner/pi-tui";
7
+ import type { Theme } from "../theme/theme.js";
8
+
9
+ export class PencilLoader extends Container {
10
+ private tui: TUI;
11
+ private theme: Theme;
12
+ private message: string;
13
+ private interval: NodeJS.Timeout | undefined;
14
+ private currentFrame = 0;
15
+ private textComponent: Text;
16
+ private messageComponent: Text;
17
+ private isStopped = false;
18
+
19
+ // Breathing animation frames - opacity levels for ✎
20
+ private readonly frames = [
21
+ "░", // very dim
22
+ "▒", // dim
23
+ "▓", // medium
24
+ "█", // bright
25
+ "▓", // medium
26
+ "▒", // dim
27
+ ];
28
+
29
+ constructor(tui: TUI, theme: Theme, message: string) {
30
+ super();
31
+ this.tui = tui;
32
+ this.theme = theme;
33
+ this.message = message;
34
+
35
+ this.textComponent = new Text("", 0, 0);
36
+ this.messageComponent = new Text("", 0, 0);
37
+
38
+ this.addChild(new Spacer(1));
39
+ this.addChild(this.textComponent);
40
+ this.addChild(this.messageComponent);
41
+ this.addChild(new Spacer(1));
42
+
43
+ this.startAnimation();
44
+ }
45
+
46
+ private startAnimation(): void {
47
+ const updateFrame = () => {
48
+ if (this.isStopped) return;
49
+
50
+ const frameChar = this.frames[this.currentFrame];
51
+ const pencil = this.theme.fg("accent", `✎${frameChar}`);
52
+
53
+ this.textComponent.setText(`${pencil} ${this.message}`);
54
+ this.tui.requestRender();
55
+
56
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
57
+ };
58
+
59
+ // Initial render
60
+ updateFrame();
61
+
62
+ // Update every 200ms for smooth breathing effect
63
+ this.interval = setInterval(updateFrame, 200);
64
+ }
65
+
66
+ setMessage(message: string): void {
67
+ this.message = message;
68
+ const frameChar = this.frames[this.currentFrame];
69
+ const pencil = this.theme.fg("accent", `✎${frameChar}`);
70
+ this.messageComponent.setText(`${pencil} ${this.message}`);
71
+ this.tui.requestRender();
72
+ }
73
+
74
+ stop(): void {
75
+ this.isStopped = true;
76
+ if (this.interval) {
77
+ clearInterval(this.interval);
78
+ this.interval = undefined;
79
+ }
80
+ }
81
+
82
+ dispose(): void {
83
+ this.stop();
84
+ }
85
+ }