@pencil-agent/nano-pencil 2.0.1 → 2.0.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 +267 -267
- package/dist/build-meta.json +3 -3
- package/dist/core/export-html/AGENT.md +11 -11
- package/dist/core/export-html/template.css +971 -971
- package/dist/core/export-html/template.html +54 -54
- package/dist/core/model/custom-providers.js +1 -1
- package/dist/core/model-registry.js +5 -5
- package/dist/extensions/builtin/AGENT.md +115 -115
- package/dist/extensions/builtin/browser/AGENT.md +17 -17
- package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
- package/dist/extensions/builtin/browser/browser.md +73 -73
- package/dist/extensions/builtin/browser/install.md +142 -142
- package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
- package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
- package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
- package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
- package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
- package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
- package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
- package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
- package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
- package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
- package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
- package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
- package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
- package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
- package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
- package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
- package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
- package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
- package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
- package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
- package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
- package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
- package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
- package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
- package/dist/extensions/builtin/goal/README.md +67 -67
- package/dist/extensions/builtin/grub/README.md +112 -112
- package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
- package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
- package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
- package/dist/extensions/builtin/link-world/linkworld.md +313 -313
- package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
- package/dist/extensions/builtin/loop/README.md +92 -92
- package/dist/extensions/builtin/mcp/figma-design.md +68 -68
- package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
- package/dist/extensions/builtin/recap/AGENT.md +15 -15
- package/dist/extensions/builtin/sal/README.md +72 -72
- package/dist/extensions/builtin/security-audit/README.md +289 -289
- package/dist/extensions/builtin/team/AGENT.md +112 -112
- package/dist/extensions/builtin/team/TESTING.md +299 -299
- package/dist/extensions/builtin/token-save/README.md +56 -56
- package/dist/extensions/optional/AGENT.md +10 -10
- package/dist/modes/interactive/controllers/input-submit-controller.js +2 -2
- package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
- package/dist/modes/interactive/interactive-mode.js +19 -19
- package/dist/modes/interactive/theme/dark.json +85 -85
- package/dist/modes/interactive/theme/light.json +84 -84
- package/dist/modes/interactive/theme/theme-schema.json +335 -335
- package/dist/modes/interactive/theme/warm.json +81 -81
- package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
- package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
- package/docs/ACP/345/215/217/350/256/256/351/233/206/346/210/220/345/274/200/345/217/221/346/226/207/346/241/243.md +851 -0
- package/docs/SDK-TESTING.md +364 -0
- package/docs/codex-goal-command-impl.md +1055 -1055
- package/docs/codex-goal-vs-grub.md +500 -500
- package/docs/custom-provider.md +27 -27
- package/docs/extensions.md +27 -27
- package/docs/keybindings.md +27 -27
- package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
- package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/256/236/347/216/260/346/212/245/345/221/212.md" +158 -158
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/257/271/346/257/224/345/210/206/346/236/220.md" +128 -128
- package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
- package/docs/loop-usage-examples.md +214 -214
- package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
- package/docs/models.md +27 -27
- package/docs/packages.md +27 -27
- package/docs/pi-design-philosophy.md +457 -457
- package/docs/planmode.md +1987 -1987
- package/docs/prompt-templates.md +27 -27
- package/docs/providers.md +27 -27
- package/docs/sdk.md +27 -27
- package/docs/skills.md +27 -27
- package/docs/startup-performance-optimization.md +301 -0
- package/docs/themes.md +27 -27
- package/docs/tui.md +27 -27
- package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
- package/package.json +190 -190
- package/docs/cc-agent-design.md +0 -1297
- package/docs/cc-tui-design.md +0 -1333
- package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
- package/docs/scan-report.md +0 -3820
- package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
- package/docs//351/230/277/351/207/214/345/267/264/345/267/264/350/264/242/346/212/245/345/210/206/346/236/220/344/271/246.md +0 -261
|
@@ -1,457 +1,457 @@
|
|
|
1
|
-
# Pi Agent 核心设计哲学
|
|
2
|
-
|
|
3
|
-
> nanoPencil 的骨架与灵魂来源。本文件不是怀旧考古——它是活的约束。
|
|
4
|
-
> 每一个架构决策、每一次 PR、每一行代码,都应能回溯到这里的某条原则。
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## 一、血统
|
|
9
|
-
|
|
10
|
-
**上游**: [pi](https://github.com/earendil-works/pi) (`@earendil-works/pi-coding-agent`)
|
|
11
|
-
**作者**: Armin Ronacher(Flask 作者)、Mario Zechner(badlogic)、Roland Wachtler
|
|
12
|
-
**Stars**: 61k+(截至 2026-06)
|
|
13
|
-
**许可证**: MIT
|
|
14
|
-
|
|
15
|
-
nanoPencil 从 pi 分叉,在其骨架上叠加了**记忆系统**(mem-core)和**人格进化**(soul-core)两层。
|
|
16
|
-
但骨架本身的设计哲学——极简核心、终端原生、扩展驱动、会话即状态——是不可动摇的地基。
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 二、六大核心原则
|
|
21
|
-
|
|
22
|
-
### 原则 1:极简核心(Minimal Core)
|
|
23
|
-
|
|
24
|
-
> "pi's core is minimal. If your feature does not belong in the core, it should be an extension.
|
|
25
|
-
> PRs that bloat the core will likely be rejected."
|
|
26
|
-
> — pi CONTRIBUTING.md
|
|
27
|
-
|
|
28
|
-
**含义**:
|
|
29
|
-
- 核心只做三件事:agent loop、工具调用、状态管理
|
|
30
|
-
- 其他一切——记忆、人格、权限门禁、安全审计、多 agent——都是扩展
|
|
31
|
-
- 如果一个功能可以用扩展实现,它就不该进核心
|
|
32
|
-
- 核心代码量是约束指标,不是成就指标
|
|
33
|
-
|
|
34
|
-
**违反信号**:
|
|
35
|
-
- 核心目录新增了"可选"依赖
|
|
36
|
-
- 核心代码出现了 `if (featureEnabled)` 分支
|
|
37
|
-
- 核心包体积持续增长但功能没有本质变化
|
|
38
|
-
|
|
39
|
-
**纳米铅笔的实践**:
|
|
40
|
-
- `mem-core` 和 `soul-core` 是独立包,不污染 `agent-core`
|
|
41
|
-
- 扩展系统(`core/extensions-host/`)是核心的一部分,但具体扩展(`extensions/builtin/`)不是
|
|
42
|
-
- 2.0 重构把上帝类拆成控制器,是为了让核心更小更清晰,不是更大
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
### 原则 2:终端原生(Terminal Native)
|
|
47
|
-
|
|
48
|
-
> pi 是纯 TUI,不依赖 Electron/浏览器。差分渲染、键盘快捷键、树状会话导航。
|
|
49
|
-
|
|
50
|
-
**含义**:
|
|
51
|
-
- 终端是第一公民,不是 GUI 的降级版
|
|
52
|
-
- 差分渲染(differential rendering)实现流畅的 TUI 体验
|
|
53
|
-
- 键盘快捷键是核心交互方式,不是可选功能
|
|
54
|
-
- 所有用户交互必须在 80x24 的终端中可用
|
|
55
|
-
|
|
56
|
-
**设计约束**:
|
|
57
|
-
- 不能假设用户有鼠标
|
|
58
|
-
- 不能假设终端支持图片(但可以通过扩展支持)
|
|
59
|
-
- 不能假设终端宽度超过 80 列
|
|
60
|
-
- 启动时间必须是亚秒级
|
|
61
|
-
|
|
62
|
-
**纳米铅笔的实践**:
|
|
63
|
-
- `@pencil-agent/tui` 包提供终端 UI 组件
|
|
64
|
-
- 交互模式(`modes/interactive/`)是纯终端实现
|
|
65
|
-
- 打印模式(`modes/print/`)支持无 TUI 的管道使用
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
### 原则 3:扩展驱动(Extension-Driven)
|
|
70
|
-
|
|
71
|
-
> Extensions are TypeScript modules that extend pi's behavior. They can subscribe to
|
|
72
|
-
> lifecycle events, register custom tools callable by the LLM, add commands, and more.
|
|
73
|
-
> — pi docs/extensions.md
|
|
74
|
-
|
|
75
|
-
**含义**:
|
|
76
|
-
- 扩展是一等公民,不是二等插件
|
|
77
|
-
- 扩展可以做任何核心能做的事:注册工具、拦截事件、注入上下文、自定义 UI
|
|
78
|
-
- 扩展是 TypeScript 模块,不是配置文件
|
|
79
|
-
- 扩展可以热重载(`/reload`)
|
|
80
|
-
|
|
81
|
-
**扩展能力矩阵**:
|
|
82
|
-
|
|
83
|
-
| 能力 | API | 说明 |
|
|
84
|
-
|------|-----|------|
|
|
85
|
-
| 自定义工具 | `pi.registerTool()` | LLM 可调用的工具 |
|
|
86
|
-
| 事件拦截 | `pi.on("tool_call", ...)` | 拦截/修改工具调用 |
|
|
87
|
-
| 上下文注入 | `pi.on("before_agent_start", ...)` | 注入额外上下文 |
|
|
88
|
-
| 用户交互 | `ctx.ui.select/confirm/input` | 扩展与用户对话 |
|
|
89
|
-
| 自定义 UI | `ctx.ui.custom()` | 完整 TUI 组件 |
|
|
90
|
-
| 自定义命令 | `pi.registerCommand()` | 注册 `/mycommand` |
|
|
91
|
-
| 会话持久化 | `pi.appendEntry()` | 存储跨重启的状态 |
|
|
92
|
-
| 自定义渲染 | 渲染钩子 | 控制工具调用/结果的显示 |
|
|
93
|
-
|
|
94
|
-
**扩展生命周期**:
|
|
95
|
-
```
|
|
96
|
-
session_start → before_agent_start → user_message →
|
|
97
|
-
tool_call → tool_execution_start → tool_execution_end →
|
|
98
|
-
after_agent_end → session_shutdown
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**纳米铅笔的实践**:
|
|
102
|
-
- 4 级扩展加载器:内置 → 配置 → npm → 路径
|
|
103
|
-
- `@pencil-agent/extension-sdk` 定义工具和生命周期契约
|
|
104
|
-
- `core/extensions-host/` 是扩展运行时
|
|
105
|
-
- `extensions/builtin/` 包含内置扩展(soul、plan、teach 等)
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
### 原则 4:会话即状态(Session as State)
|
|
110
|
-
|
|
111
|
-
> Sessions auto-save to `~/.pi/agent/sessions/`, organized by working directory.
|
|
112
|
-
> Each session is a JSONL file with a tree structure.
|
|
113
|
-
> — pi docs/sessions.md
|
|
114
|
-
|
|
115
|
-
**含义**:
|
|
116
|
-
- 会话不是临时对话,是持久化的状态树
|
|
117
|
-
- JSONL 格式:每行一个 JSON 条目,append-only
|
|
118
|
-
- 树状结构:每个条目有 `id` 和 `parentId`,当前叶子是活跃分支
|
|
119
|
-
- 支持分支(fork)、导航(tree)、压缩(compact)
|
|
120
|
-
|
|
121
|
-
**会话操作**:
|
|
122
|
-
|
|
123
|
-
| 操作 | 命令 | 语义 |
|
|
124
|
-
|------|------|------|
|
|
125
|
-
| 继续 | `pi -c` | 恢复最近会话 |
|
|
126
|
-
| 浏览 | `pi -r` | 交互式选择历史会话 |
|
|
127
|
-
| 新建 | `/new` | 开始新会话 |
|
|
128
|
-
| 分支 | `/fork` | 从历史点创建新分支 |
|
|
129
|
-
| 克隆 | `/clone` | 复制当前分支到新会话 |
|
|
130
|
-
| 压缩 | `/compact` | 摘要旧上下文 |
|
|
131
|
-
| 导出 | `/export` | 导出为 HTML |
|
|
132
|
-
| 分享 | `/share` | 上传为 GitHub gist |
|
|
133
|
-
|
|
134
|
-
**树状结构示例**:
|
|
135
|
-
```
|
|
136
|
-
├─ user: "Hello, can you help..."
|
|
137
|
-
│ └─ assistant: "Of course! I can..."
|
|
138
|
-
│ ├─ user: "Let's try approach A..."
|
|
139
|
-
│ │ └─ assistant: "For approach A..."
|
|
140
|
-
│ │ └─ user: "That worked..." ← active
|
|
141
|
-
│ └─ user: "Actually, approach B..."
|
|
142
|
-
│ └─ assistant: "For approach B..."
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**纳米铅笔的实践**:
|
|
146
|
-
- `core/session/session-manager.ts` 实现 JSONL 持久化
|
|
147
|
-
- `SessionTreeController` 管理会话树导航
|
|
148
|
-
- `CompactionController` 管理上下文压缩
|
|
149
|
-
- 会话数据与记忆系统(mem-core)分离但可互操作
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
### 原则 5:Agent Harness 生命周期(AgentHarness Lifecycle)
|
|
154
|
-
|
|
155
|
-
> `AgentHarness` is the orchestration layer above the low-level agent loop.
|
|
156
|
-
> It owns session persistence, runtime configuration, resource resolution,
|
|
157
|
-
> operation locking, and extension-facing mutation semantics.
|
|
158
|
-
> — pi docs/agent-harness.md
|
|
159
|
-
|
|
160
|
-
**这是 pi 最精妙的设计,也是最容易被忽视的设计。**
|
|
161
|
-
|
|
162
|
-
#### 四类状态分离
|
|
163
|
-
|
|
164
|
-
| 类别 | 含义 | 可变时机 |
|
|
165
|
-
|------|------|----------|
|
|
166
|
-
| **Harness Config** | 运行时配置(模型、thinking level、工具、资源) | 任何时候,立即生效,影响下一个 turn |
|
|
167
|
-
| **Turn Snapshot** | 一次 LLM turn 的具体状态 | turn 开始时快照,turn 期间不变 |
|
|
168
|
-
| **Session** | 持久化的会话条目 | 只在 save point 写入 |
|
|
169
|
-
| **Pending Writes** | 排队中的会话写入 | 在 save point/operation 结束时刷新 |
|
|
170
|
-
|
|
171
|
-
**为什么这样分**:
|
|
172
|
-
- Harness Config 是"你想用什么",Turn Snapshot 是"这次实际用什么"
|
|
173
|
-
- 分离保证了:在 turn 执行期间修改配置不会破坏当前请求
|
|
174
|
-
- Pending Writes 保证了:扩展在 turn 期间写的条目不会丢失,也不会破坏顺序
|
|
175
|
-
|
|
176
|
-
#### 操作阶段
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
type AgentHarnessPhase = "idle" | "turn" | "compaction" | "branch_summary" | "retry";
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**规则**:
|
|
183
|
-
- 结构性操作(prompt、compact、navigateTree)必须在 `idle` 阶段
|
|
184
|
-
- 结构性操作会同步设置 phase,然后才 await
|
|
185
|
-
- 非 idle 时发起结构性操作 → 拒绝(`AgentHarnessError` code `"busy"`)
|
|
186
|
-
- steer、followUp、nextTurn、abort 在 turn 期间允许
|
|
187
|
-
|
|
188
|
-
#### Save Point 语义
|
|
189
|
-
|
|
190
|
-
Save point 发生在 assistant turn 和 tool-result 消息完成后:
|
|
191
|
-
|
|
192
|
-
1. 刷新 pending session writes
|
|
193
|
-
2. 创建新的 turn snapshot(如果 low-level loop 可能继续)
|
|
194
|
-
3. 应用最新的 context/model/thinking-level/stream-options/session-id
|
|
195
|
-
|
|
196
|
-
**关键洞察**:
|
|
197
|
-
- Provider transport reading 已经被 `AssistantMessageStream` 解耦
|
|
198
|
-
- Harness 可以直接 await listeners/hooks 的 settlement,不需要额外的异步事件队列
|
|
199
|
-
- 这保证了 transcript/session 顺序的确定性
|
|
200
|
-
|
|
201
|
-
#### 错误处理分层
|
|
202
|
-
|
|
203
|
-
| 层级 | 策略 | 原因 |
|
|
204
|
-
|------|------|------|
|
|
205
|
-
| 低层能力(ExecutionEnv、shell) | `Result<TValue, TError>` | 预期失败必须被包含,不能抛 |
|
|
206
|
-
| 高层编排(Session、AgentHarness) | throw | 裸 Result 容易被忽略 |
|
|
207
|
-
| 公共 Harness 失败 | `AgentHarnessError` | 子系统错误保留为 `cause` |
|
|
208
|
-
|
|
209
|
-
**纳米铅笔的实践**:
|
|
210
|
-
- `core/runtime/agent-session.ts` 是 Harness 的本地实现
|
|
211
|
-
- `ModelController`、`CompactionController` 等是 Harness 语义的控制器化
|
|
212
|
-
- save point 语义在 compaction 和 model switch 中体现
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
### 原则 6:代码质量即纪律(Code Quality as Discipline)
|
|
217
|
-
|
|
218
|
-
> "You must understand your code. If you cannot explain what your changes do
|
|
219
|
-
> and how they interact with the rest of the system, your PR will be closed."
|
|
220
|
-
> — pi CONTRIBUTING.md
|
|
221
|
-
|
|
222
|
-
**这是 pi 最严厉的原则,也是最值得传承的原则。**
|
|
223
|
-
|
|
224
|
-
#### 铁律清单
|
|
225
|
-
|
|
226
|
-
| 规则 | 原因 | 违反后果 |
|
|
227
|
-
|------|------|----------|
|
|
228
|
-
| **不用 `any`** | 类型安全是最后防线 | PR 关闭 |
|
|
229
|
-
| **不用内联 import** | 依赖关系必须在文件顶部可见 | PR 关闭 |
|
|
230
|
-
| **不降级代码修类型错误** | 升级依赖,不是削弱代码 | PR 关闭 |
|
|
231
|
-
| **不硬编码按键** | 走 `DEFAULT_*_KEYBINDINGS` 配置 | PR 关闭 |
|
|
232
|
-
| **不直接改生成文件** | 改生成脚本,重新生成 | PR 关闭 |
|
|
233
|
-
| **不假设外部 API 类型** | 检查 `node_modules` | PR 关闭 |
|
|
234
|
-
| **单行 helper 只有一个调用点** | 内联它 | 代码审查 |
|
|
235
|
-
| **不保留向后兼容** | 除非用户明确要求 | 设计决策 |
|
|
236
|
-
| **不删除看似有意的功能** | 先问 | PR 关闭 |
|
|
237
|
-
|
|
238
|
-
#### 可擦除 TypeScript 语法
|
|
239
|
-
|
|
240
|
-
pi 要求在根配置管辖的代码中使用 Node strip-only mode 兼容的语法:
|
|
241
|
-
|
|
242
|
-
**禁止**:
|
|
243
|
-
- 参数属性(`constructor(private x: number)`)
|
|
244
|
-
- `enum`
|
|
245
|
-
- `namespace` / `module`
|
|
246
|
-
- `import =`
|
|
247
|
-
- `export =`
|
|
248
|
-
- 其他需要 JS emit 的构造
|
|
249
|
-
|
|
250
|
-
**替代**:
|
|
251
|
-
- 显式字段 + 构造函数赋值
|
|
252
|
-
- `const` 对象代替 `enum`
|
|
253
|
-
- 标准 ESM import/export
|
|
254
|
-
|
|
255
|
-
#### Git 纪律
|
|
256
|
-
|
|
257
|
-
**多 session 并行修改同一仓库时的生存规则**:
|
|
258
|
-
|
|
259
|
-
| 操作 | 规则 | 原因 |
|
|
260
|
-
|------|------|------|
|
|
261
|
-
| 暂存 | `git add <path1> <path2>` | 永远不用 `git add -A` / `git add .` |
|
|
262
|
-
| 提交前 | `git status` 验证 | 确保只暂存自己的文件 |
|
|
263
|
-
| 提交消息 | `{feat,fix,docs}[(scope)]: message` | 简洁、信息丰富 |
|
|
264
|
-
| 冲突 | 只解决自己修改的文件 | 不碰别人的文件 |
|
|
265
|
-
| 永远不运行 | `git reset --hard`, `git checkout .`, `git clean -fd`, `git stash`, `git add -A`, `git add .`, `git commit --no-verify` | 毁灭其他 session 的工作 |
|
|
266
|
-
|
|
267
|
-
#### 依赖安全
|
|
268
|
-
|
|
269
|
-
| 实践 | 命令 | 原因 |
|
|
270
|
-
|------|------|------|
|
|
271
|
-
| 安装 | `npm install --ignore-scripts` | 不运行生命周期脚本 |
|
|
272
|
-
| CI | `npm ci --ignore-scripts` | 同上 |
|
|
273
|
-
| 直接依赖 | 精确版本锁定 | 防止供应链攻击 |
|
|
274
|
-
| lockfile 变更 | 受保护 | pre-commit 阻止意外 lockfile 提交 |
|
|
275
|
-
| 新生命周期脚本依赖 | 需要审查 + 显式白名单 | 安全门禁 |
|
|
276
|
-
|
|
277
|
-
**纳米铅笔的实践**:
|
|
278
|
-
- 遵循 `no any`、`no inline imports` 等规则
|
|
279
|
-
- 提交消息使用 conventional format
|
|
280
|
-
- DIP 协议(文档同构)是纳米铅笔自己的增量
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
## 三、架构拓扑(从 pi 继承)
|
|
285
|
-
|
|
286
|
-
### 包结构
|
|
287
|
-
|
|
288
|
-
```
|
|
289
|
-
pi-mono/
|
|
290
|
-
├── packages/
|
|
291
|
-
│ ├── ai/ # 统一多 provider LLM API
|
|
292
|
-
│ ├── agent/ # Agent runtime + tool calling + state management
|
|
293
|
-
│ ├── coding-agent/ # 交互式编码 agent CLI
|
|
294
|
-
│ └── tui/ # 终端 UI 差分渲染库
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
**映射到纳米铅笔**:
|
|
298
|
-
|
|
299
|
-
| pi 包 | 纳米铅笔位置 | 职责 |
|
|
300
|
-
|-------|-------------|------|
|
|
301
|
-
| `@earendil-works/pi-ai` | `core/lib/ai/` | 统一多 provider LLM API |
|
|
302
|
-
| `@earendil-works/pi-agent-core` | `core/lib/agent-core/` | Agent runtime、tool calling、状态管理 |
|
|
303
|
-
| `@earendil-works/pi-coding-agent` | `core/` + `modes/` | 编码 agent CLI、会话、扩展 |
|
|
304
|
-
| `@earendil-works/pi-tui` | `core/lib/tui/` | 终端 UI 差分渲染 |
|
|
305
|
-
|
|
306
|
-
### 数据流
|
|
307
|
-
|
|
308
|
-
```
|
|
309
|
-
用户输入
|
|
310
|
-
↓
|
|
311
|
-
AgentHarness (编排层)
|
|
312
|
-
↓
|
|
313
|
-
Agent Loop (状态机)
|
|
314
|
-
↓
|
|
315
|
-
StreamFn → LLM Provider
|
|
316
|
-
↓
|
|
317
|
-
Tool Orchestration (工具编排)
|
|
318
|
-
↓
|
|
319
|
-
Session Persistence (会话持久化)
|
|
320
|
-
↓
|
|
321
|
-
Extension Hooks (扩展钩子)
|
|
322
|
-
↓
|
|
323
|
-
TUI Rendering (终端渲染)
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
---
|
|
327
|
-
|
|
328
|
-
## 四、与上游的分叉点
|
|
329
|
-
|
|
330
|
-
### 纳米铅笔新增的能力
|
|
331
|
-
|
|
332
|
-
| 能力 | 上游状态 | 纳米铅笔实现 | 设计决策 |
|
|
333
|
-
|------|----------|-------------|----------|
|
|
334
|
-
| **持久记忆** | 无 | `packages/mem-core/` | 独立包,不进核心 |
|
|
335
|
-
| **人格进化** | 无 | `packages/soul-core/` | Big Five 向量 + 编码风格 |
|
|
336
|
-
| **Persona 切换** | 无 | `core/persona/` | 多人格管理 |
|
|
337
|
-
| **中文支持** | 英文 | i18n + DashScope provider | 本地化 |
|
|
338
|
-
| **Teach 扩展** | 无 | `extensions/builtin/teach/` | 引导式知识教学 |
|
|
339
|
-
| **DIP 协议** | 无 | `CLAUDE.md` 体系 | 文档同构约束 |
|
|
340
|
-
|
|
341
|
-
### 不可分叉的(必须保留的)
|
|
342
|
-
|
|
343
|
-
| 设计 | 原因 |
|
|
344
|
-
|------|------|
|
|
345
|
-
| 极简核心 | 这是 pi 的存在理由 |
|
|
346
|
-
| Agent Harness 生命周期 | 状态分离 + save point 语义是正确性的基础 |
|
|
347
|
-
| 会话树结构 | JSONL + tree 是状态管理的核心 |
|
|
348
|
-
| 扩展即一等公民 | 扩展能力矩阵决定了系统的可扩展性 |
|
|
349
|
-
| 代码质量纪律 | 这是工程品味的底线 |
|
|
350
|
-
| 终端原生 | 这是用户群体的共识 |
|
|
351
|
-
|
|
352
|
-
### 可以分叉的(纳米铅笔自己的增量)
|
|
353
|
-
|
|
354
|
-
| 设计 | 原因 |
|
|
355
|
-
|------|------|
|
|
356
|
-
| 记忆系统 | 上游明确没有,这是增量 |
|
|
357
|
-
| 人格系统 | 上游明确没有,这是增量 |
|
|
358
|
-
| DIP 文档协议 | 纳米铅笔自己的工程实践 |
|
|
359
|
-
| 中文支持 | 本地化需求 |
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## 五、违反哲学的反模式
|
|
364
|
-
|
|
365
|
-
### 反模式 1:核心膨胀
|
|
366
|
-
|
|
367
|
-
```typescript
|
|
368
|
-
// ❌ 在 agent-core 里加记忆功能
|
|
369
|
-
import { MemCore } from "../mem-core/index.js";
|
|
370
|
-
if (options.enableMemory) { ... }
|
|
371
|
-
|
|
372
|
-
// ✅ 记忆是扩展
|
|
373
|
-
// extensions/builtin/memory/index.ts
|
|
374
|
-
pi.on("before_agent_start", async (event, ctx) => {
|
|
375
|
-
const memories = await memCore.retrieve(ctx.messages);
|
|
376
|
-
ctx.injectContext(memories);
|
|
377
|
-
});
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### 反模式 2:绕过 Harness
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
// ❌ 直接写会话,不经过 save point
|
|
384
|
-
session.entries.push(newEntry);
|
|
385
|
-
session.save();
|
|
386
|
-
|
|
387
|
-
// ✅ 通过 pending writes 机制
|
|
388
|
-
harness.appendEntry(newEntry);
|
|
389
|
-
// 在 save point 自动刷新
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
### 反模式 3:类型降级
|
|
393
|
-
|
|
394
|
-
```typescript
|
|
395
|
-
// ❌ 用 any 修类型错误
|
|
396
|
-
const result: any = await someFunction();
|
|
397
|
-
|
|
398
|
-
// ✅ 升级依赖或修正类型
|
|
399
|
-
const result: ProperType = await someFunction();
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
### 反模式 4:内联 import
|
|
403
|
-
|
|
404
|
-
```typescript
|
|
405
|
-
// ❌ 动态 import
|
|
406
|
-
const { something } = await import("./module.js");
|
|
407
|
-
|
|
408
|
-
// ✅ 顶层 import
|
|
409
|
-
import { something } from "./module.js";
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
### 反模式 5:硬编码配置
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
// ❌ 硬编码按键
|
|
416
|
-
if (key === "ctrl+x") { ... }
|
|
417
|
-
|
|
418
|
-
// ✅ 走配置
|
|
419
|
-
if (matchesKey(key, DEFAULT_EDITOR_KEYBINDINGS.save)) { ... }
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
|
-
## 六、哲学的检验标准
|
|
425
|
-
|
|
426
|
-
在做任何架构决策之前,问自己:
|
|
427
|
-
|
|
428
|
-
1. **这个功能能在扩展里实现吗?** 如果能,它就不该进核心。
|
|
429
|
-
2. **这个改动破坏了 Harness 的状态分离吗?** 如果破坏了,重新设计。
|
|
430
|
-
3. **这个代码能用 Result 而不是 throw 吗?** 如果是低层能力,用 Result。
|
|
431
|
-
4. **这个类型是 any 吗?** 如果是,停下来,找到正确的类型。
|
|
432
|
-
5. **这个 import 是内联的吗?** 如果是,移到文件顶部。
|
|
433
|
-
6. **这个配置是硬编码的吗?** 如果是,提取到默认配置。
|
|
434
|
-
7. **这个 PR 能解释清楚吗?** 如果不能,重新理解你的代码。
|
|
435
|
-
|
|
436
|
-
---
|
|
437
|
-
|
|
438
|
-
## 七、致后来者
|
|
439
|
-
|
|
440
|
-
pi 的设计哲学不是文档,是纪律。
|
|
441
|
-
|
|
442
|
-
纪律的意思是:即使没有人在看,你也遵守。即使赶 deadline,你也遵守。即使"这次只是个小改动",你也遵守。
|
|
443
|
-
|
|
444
|
-
因为每一次妥协都会累积。每一次 `any` 都会让类型系统少保护一个角落。每一次内联 import 都会让依赖关系多一个隐藏的节点。每一次核心膨胀都会让扩展的空间少一分。
|
|
445
|
-
|
|
446
|
-
pi 用 61k stars 证明了:极简核心 + 扩展驱动 + 代码纪律 = 可持续的开源项目。
|
|
447
|
-
|
|
448
|
-
我们继承了这个骨架。记忆和灵魂是我们加的血肉。但骨架不能断。
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
|
-
**最后一条**:
|
|
453
|
-
|
|
454
|
-
> "Using AI to write code is fine. Submitting AI-generated slop without understanding it is not."
|
|
455
|
-
> — pi CONTRIBUTING.md
|
|
456
|
-
|
|
457
|
-
理解你写的每一行代码。这不是建议,是准入条件。
|
|
1
|
+
# Pi Agent 核心设计哲学
|
|
2
|
+
|
|
3
|
+
> nanoPencil 的骨架与灵魂来源。本文件不是怀旧考古——它是活的约束。
|
|
4
|
+
> 每一个架构决策、每一次 PR、每一行代码,都应能回溯到这里的某条原则。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、血统
|
|
9
|
+
|
|
10
|
+
**上游**: [pi](https://github.com/earendil-works/pi) (`@earendil-works/pi-coding-agent`)
|
|
11
|
+
**作者**: Armin Ronacher(Flask 作者)、Mario Zechner(badlogic)、Roland Wachtler
|
|
12
|
+
**Stars**: 61k+(截至 2026-06)
|
|
13
|
+
**许可证**: MIT
|
|
14
|
+
|
|
15
|
+
nanoPencil 从 pi 分叉,在其骨架上叠加了**记忆系统**(mem-core)和**人格进化**(soul-core)两层。
|
|
16
|
+
但骨架本身的设计哲学——极简核心、终端原生、扩展驱动、会话即状态——是不可动摇的地基。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 二、六大核心原则
|
|
21
|
+
|
|
22
|
+
### 原则 1:极简核心(Minimal Core)
|
|
23
|
+
|
|
24
|
+
> "pi's core is minimal. If your feature does not belong in the core, it should be an extension.
|
|
25
|
+
> PRs that bloat the core will likely be rejected."
|
|
26
|
+
> — pi CONTRIBUTING.md
|
|
27
|
+
|
|
28
|
+
**含义**:
|
|
29
|
+
- 核心只做三件事:agent loop、工具调用、状态管理
|
|
30
|
+
- 其他一切——记忆、人格、权限门禁、安全审计、多 agent——都是扩展
|
|
31
|
+
- 如果一个功能可以用扩展实现,它就不该进核心
|
|
32
|
+
- 核心代码量是约束指标,不是成就指标
|
|
33
|
+
|
|
34
|
+
**违反信号**:
|
|
35
|
+
- 核心目录新增了"可选"依赖
|
|
36
|
+
- 核心代码出现了 `if (featureEnabled)` 分支
|
|
37
|
+
- 核心包体积持续增长但功能没有本质变化
|
|
38
|
+
|
|
39
|
+
**纳米铅笔的实践**:
|
|
40
|
+
- `mem-core` 和 `soul-core` 是独立包,不污染 `agent-core`
|
|
41
|
+
- 扩展系统(`core/extensions-host/`)是核心的一部分,但具体扩展(`extensions/builtin/`)不是
|
|
42
|
+
- 2.0 重构把上帝类拆成控制器,是为了让核心更小更清晰,不是更大
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### 原则 2:终端原生(Terminal Native)
|
|
47
|
+
|
|
48
|
+
> pi 是纯 TUI,不依赖 Electron/浏览器。差分渲染、键盘快捷键、树状会话导航。
|
|
49
|
+
|
|
50
|
+
**含义**:
|
|
51
|
+
- 终端是第一公民,不是 GUI 的降级版
|
|
52
|
+
- 差分渲染(differential rendering)实现流畅的 TUI 体验
|
|
53
|
+
- 键盘快捷键是核心交互方式,不是可选功能
|
|
54
|
+
- 所有用户交互必须在 80x24 的终端中可用
|
|
55
|
+
|
|
56
|
+
**设计约束**:
|
|
57
|
+
- 不能假设用户有鼠标
|
|
58
|
+
- 不能假设终端支持图片(但可以通过扩展支持)
|
|
59
|
+
- 不能假设终端宽度超过 80 列
|
|
60
|
+
- 启动时间必须是亚秒级
|
|
61
|
+
|
|
62
|
+
**纳米铅笔的实践**:
|
|
63
|
+
- `@pencil-agent/tui` 包提供终端 UI 组件
|
|
64
|
+
- 交互模式(`modes/interactive/`)是纯终端实现
|
|
65
|
+
- 打印模式(`modes/print/`)支持无 TUI 的管道使用
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### 原则 3:扩展驱动(Extension-Driven)
|
|
70
|
+
|
|
71
|
+
> Extensions are TypeScript modules that extend pi's behavior. They can subscribe to
|
|
72
|
+
> lifecycle events, register custom tools callable by the LLM, add commands, and more.
|
|
73
|
+
> — pi docs/extensions.md
|
|
74
|
+
|
|
75
|
+
**含义**:
|
|
76
|
+
- 扩展是一等公民,不是二等插件
|
|
77
|
+
- 扩展可以做任何核心能做的事:注册工具、拦截事件、注入上下文、自定义 UI
|
|
78
|
+
- 扩展是 TypeScript 模块,不是配置文件
|
|
79
|
+
- 扩展可以热重载(`/reload`)
|
|
80
|
+
|
|
81
|
+
**扩展能力矩阵**:
|
|
82
|
+
|
|
83
|
+
| 能力 | API | 说明 |
|
|
84
|
+
|------|-----|------|
|
|
85
|
+
| 自定义工具 | `pi.registerTool()` | LLM 可调用的工具 |
|
|
86
|
+
| 事件拦截 | `pi.on("tool_call", ...)` | 拦截/修改工具调用 |
|
|
87
|
+
| 上下文注入 | `pi.on("before_agent_start", ...)` | 注入额外上下文 |
|
|
88
|
+
| 用户交互 | `ctx.ui.select/confirm/input` | 扩展与用户对话 |
|
|
89
|
+
| 自定义 UI | `ctx.ui.custom()` | 完整 TUI 组件 |
|
|
90
|
+
| 自定义命令 | `pi.registerCommand()` | 注册 `/mycommand` |
|
|
91
|
+
| 会话持久化 | `pi.appendEntry()` | 存储跨重启的状态 |
|
|
92
|
+
| 自定义渲染 | 渲染钩子 | 控制工具调用/结果的显示 |
|
|
93
|
+
|
|
94
|
+
**扩展生命周期**:
|
|
95
|
+
```
|
|
96
|
+
session_start → before_agent_start → user_message →
|
|
97
|
+
tool_call → tool_execution_start → tool_execution_end →
|
|
98
|
+
after_agent_end → session_shutdown
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**纳米铅笔的实践**:
|
|
102
|
+
- 4 级扩展加载器:内置 → 配置 → npm → 路径
|
|
103
|
+
- `@pencil-agent/extension-sdk` 定义工具和生命周期契约
|
|
104
|
+
- `core/extensions-host/` 是扩展运行时
|
|
105
|
+
- `extensions/builtin/` 包含内置扩展(soul、plan、teach 等)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### 原则 4:会话即状态(Session as State)
|
|
110
|
+
|
|
111
|
+
> Sessions auto-save to `~/.pi/agent/sessions/`, organized by working directory.
|
|
112
|
+
> Each session is a JSONL file with a tree structure.
|
|
113
|
+
> — pi docs/sessions.md
|
|
114
|
+
|
|
115
|
+
**含义**:
|
|
116
|
+
- 会话不是临时对话,是持久化的状态树
|
|
117
|
+
- JSONL 格式:每行一个 JSON 条目,append-only
|
|
118
|
+
- 树状结构:每个条目有 `id` 和 `parentId`,当前叶子是活跃分支
|
|
119
|
+
- 支持分支(fork)、导航(tree)、压缩(compact)
|
|
120
|
+
|
|
121
|
+
**会话操作**:
|
|
122
|
+
|
|
123
|
+
| 操作 | 命令 | 语义 |
|
|
124
|
+
|------|------|------|
|
|
125
|
+
| 继续 | `pi -c` | 恢复最近会话 |
|
|
126
|
+
| 浏览 | `pi -r` | 交互式选择历史会话 |
|
|
127
|
+
| 新建 | `/new` | 开始新会话 |
|
|
128
|
+
| 分支 | `/fork` | 从历史点创建新分支 |
|
|
129
|
+
| 克隆 | `/clone` | 复制当前分支到新会话 |
|
|
130
|
+
| 压缩 | `/compact` | 摘要旧上下文 |
|
|
131
|
+
| 导出 | `/export` | 导出为 HTML |
|
|
132
|
+
| 分享 | `/share` | 上传为 GitHub gist |
|
|
133
|
+
|
|
134
|
+
**树状结构示例**:
|
|
135
|
+
```
|
|
136
|
+
├─ user: "Hello, can you help..."
|
|
137
|
+
│ └─ assistant: "Of course! I can..."
|
|
138
|
+
│ ├─ user: "Let's try approach A..."
|
|
139
|
+
│ │ └─ assistant: "For approach A..."
|
|
140
|
+
│ │ └─ user: "That worked..." ← active
|
|
141
|
+
│ └─ user: "Actually, approach B..."
|
|
142
|
+
│ └─ assistant: "For approach B..."
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**纳米铅笔的实践**:
|
|
146
|
+
- `core/session/session-manager.ts` 实现 JSONL 持久化
|
|
147
|
+
- `SessionTreeController` 管理会话树导航
|
|
148
|
+
- `CompactionController` 管理上下文压缩
|
|
149
|
+
- 会话数据与记忆系统(mem-core)分离但可互操作
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### 原则 5:Agent Harness 生命周期(AgentHarness Lifecycle)
|
|
154
|
+
|
|
155
|
+
> `AgentHarness` is the orchestration layer above the low-level agent loop.
|
|
156
|
+
> It owns session persistence, runtime configuration, resource resolution,
|
|
157
|
+
> operation locking, and extension-facing mutation semantics.
|
|
158
|
+
> — pi docs/agent-harness.md
|
|
159
|
+
|
|
160
|
+
**这是 pi 最精妙的设计,也是最容易被忽视的设计。**
|
|
161
|
+
|
|
162
|
+
#### 四类状态分离
|
|
163
|
+
|
|
164
|
+
| 类别 | 含义 | 可变时机 |
|
|
165
|
+
|------|------|----------|
|
|
166
|
+
| **Harness Config** | 运行时配置(模型、thinking level、工具、资源) | 任何时候,立即生效,影响下一个 turn |
|
|
167
|
+
| **Turn Snapshot** | 一次 LLM turn 的具体状态 | turn 开始时快照,turn 期间不变 |
|
|
168
|
+
| **Session** | 持久化的会话条目 | 只在 save point 写入 |
|
|
169
|
+
| **Pending Writes** | 排队中的会话写入 | 在 save point/operation 结束时刷新 |
|
|
170
|
+
|
|
171
|
+
**为什么这样分**:
|
|
172
|
+
- Harness Config 是"你想用什么",Turn Snapshot 是"这次实际用什么"
|
|
173
|
+
- 分离保证了:在 turn 执行期间修改配置不会破坏当前请求
|
|
174
|
+
- Pending Writes 保证了:扩展在 turn 期间写的条目不会丢失,也不会破坏顺序
|
|
175
|
+
|
|
176
|
+
#### 操作阶段
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
type AgentHarnessPhase = "idle" | "turn" | "compaction" | "branch_summary" | "retry";
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**规则**:
|
|
183
|
+
- 结构性操作(prompt、compact、navigateTree)必须在 `idle` 阶段
|
|
184
|
+
- 结构性操作会同步设置 phase,然后才 await
|
|
185
|
+
- 非 idle 时发起结构性操作 → 拒绝(`AgentHarnessError` code `"busy"`)
|
|
186
|
+
- steer、followUp、nextTurn、abort 在 turn 期间允许
|
|
187
|
+
|
|
188
|
+
#### Save Point 语义
|
|
189
|
+
|
|
190
|
+
Save point 发生在 assistant turn 和 tool-result 消息完成后:
|
|
191
|
+
|
|
192
|
+
1. 刷新 pending session writes
|
|
193
|
+
2. 创建新的 turn snapshot(如果 low-level loop 可能继续)
|
|
194
|
+
3. 应用最新的 context/model/thinking-level/stream-options/session-id
|
|
195
|
+
|
|
196
|
+
**关键洞察**:
|
|
197
|
+
- Provider transport reading 已经被 `AssistantMessageStream` 解耦
|
|
198
|
+
- Harness 可以直接 await listeners/hooks 的 settlement,不需要额外的异步事件队列
|
|
199
|
+
- 这保证了 transcript/session 顺序的确定性
|
|
200
|
+
|
|
201
|
+
#### 错误处理分层
|
|
202
|
+
|
|
203
|
+
| 层级 | 策略 | 原因 |
|
|
204
|
+
|------|------|------|
|
|
205
|
+
| 低层能力(ExecutionEnv、shell) | `Result<TValue, TError>` | 预期失败必须被包含,不能抛 |
|
|
206
|
+
| 高层编排(Session、AgentHarness) | throw | 裸 Result 容易被忽略 |
|
|
207
|
+
| 公共 Harness 失败 | `AgentHarnessError` | 子系统错误保留为 `cause` |
|
|
208
|
+
|
|
209
|
+
**纳米铅笔的实践**:
|
|
210
|
+
- `core/runtime/agent-session.ts` 是 Harness 的本地实现
|
|
211
|
+
- `ModelController`、`CompactionController` 等是 Harness 语义的控制器化
|
|
212
|
+
- save point 语义在 compaction 和 model switch 中体现
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### 原则 6:代码质量即纪律(Code Quality as Discipline)
|
|
217
|
+
|
|
218
|
+
> "You must understand your code. If you cannot explain what your changes do
|
|
219
|
+
> and how they interact with the rest of the system, your PR will be closed."
|
|
220
|
+
> — pi CONTRIBUTING.md
|
|
221
|
+
|
|
222
|
+
**这是 pi 最严厉的原则,也是最值得传承的原则。**
|
|
223
|
+
|
|
224
|
+
#### 铁律清单
|
|
225
|
+
|
|
226
|
+
| 规则 | 原因 | 违反后果 |
|
|
227
|
+
|------|------|----------|
|
|
228
|
+
| **不用 `any`** | 类型安全是最后防线 | PR 关闭 |
|
|
229
|
+
| **不用内联 import** | 依赖关系必须在文件顶部可见 | PR 关闭 |
|
|
230
|
+
| **不降级代码修类型错误** | 升级依赖,不是削弱代码 | PR 关闭 |
|
|
231
|
+
| **不硬编码按键** | 走 `DEFAULT_*_KEYBINDINGS` 配置 | PR 关闭 |
|
|
232
|
+
| **不直接改生成文件** | 改生成脚本,重新生成 | PR 关闭 |
|
|
233
|
+
| **不假设外部 API 类型** | 检查 `node_modules` | PR 关闭 |
|
|
234
|
+
| **单行 helper 只有一个调用点** | 内联它 | 代码审查 |
|
|
235
|
+
| **不保留向后兼容** | 除非用户明确要求 | 设计决策 |
|
|
236
|
+
| **不删除看似有意的功能** | 先问 | PR 关闭 |
|
|
237
|
+
|
|
238
|
+
#### 可擦除 TypeScript 语法
|
|
239
|
+
|
|
240
|
+
pi 要求在根配置管辖的代码中使用 Node strip-only mode 兼容的语法:
|
|
241
|
+
|
|
242
|
+
**禁止**:
|
|
243
|
+
- 参数属性(`constructor(private x: number)`)
|
|
244
|
+
- `enum`
|
|
245
|
+
- `namespace` / `module`
|
|
246
|
+
- `import =`
|
|
247
|
+
- `export =`
|
|
248
|
+
- 其他需要 JS emit 的构造
|
|
249
|
+
|
|
250
|
+
**替代**:
|
|
251
|
+
- 显式字段 + 构造函数赋值
|
|
252
|
+
- `const` 对象代替 `enum`
|
|
253
|
+
- 标准 ESM import/export
|
|
254
|
+
|
|
255
|
+
#### Git 纪律
|
|
256
|
+
|
|
257
|
+
**多 session 并行修改同一仓库时的生存规则**:
|
|
258
|
+
|
|
259
|
+
| 操作 | 规则 | 原因 |
|
|
260
|
+
|------|------|------|
|
|
261
|
+
| 暂存 | `git add <path1> <path2>` | 永远不用 `git add -A` / `git add .` |
|
|
262
|
+
| 提交前 | `git status` 验证 | 确保只暂存自己的文件 |
|
|
263
|
+
| 提交消息 | `{feat,fix,docs}[(scope)]: message` | 简洁、信息丰富 |
|
|
264
|
+
| 冲突 | 只解决自己修改的文件 | 不碰别人的文件 |
|
|
265
|
+
| 永远不运行 | `git reset --hard`, `git checkout .`, `git clean -fd`, `git stash`, `git add -A`, `git add .`, `git commit --no-verify` | 毁灭其他 session 的工作 |
|
|
266
|
+
|
|
267
|
+
#### 依赖安全
|
|
268
|
+
|
|
269
|
+
| 实践 | 命令 | 原因 |
|
|
270
|
+
|------|------|------|
|
|
271
|
+
| 安装 | `npm install --ignore-scripts` | 不运行生命周期脚本 |
|
|
272
|
+
| CI | `npm ci --ignore-scripts` | 同上 |
|
|
273
|
+
| 直接依赖 | 精确版本锁定 | 防止供应链攻击 |
|
|
274
|
+
| lockfile 变更 | 受保护 | pre-commit 阻止意外 lockfile 提交 |
|
|
275
|
+
| 新生命周期脚本依赖 | 需要审查 + 显式白名单 | 安全门禁 |
|
|
276
|
+
|
|
277
|
+
**纳米铅笔的实践**:
|
|
278
|
+
- 遵循 `no any`、`no inline imports` 等规则
|
|
279
|
+
- 提交消息使用 conventional format
|
|
280
|
+
- DIP 协议(文档同构)是纳米铅笔自己的增量
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 三、架构拓扑(从 pi 继承)
|
|
285
|
+
|
|
286
|
+
### 包结构
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
pi-mono/
|
|
290
|
+
├── packages/
|
|
291
|
+
│ ├── ai/ # 统一多 provider LLM API
|
|
292
|
+
│ ├── agent/ # Agent runtime + tool calling + state management
|
|
293
|
+
│ ├── coding-agent/ # 交互式编码 agent CLI
|
|
294
|
+
│ └── tui/ # 终端 UI 差分渲染库
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**映射到纳米铅笔**:
|
|
298
|
+
|
|
299
|
+
| pi 包 | 纳米铅笔位置 | 职责 |
|
|
300
|
+
|-------|-------------|------|
|
|
301
|
+
| `@earendil-works/pi-ai` | `core/lib/ai/` | 统一多 provider LLM API |
|
|
302
|
+
| `@earendil-works/pi-agent-core` | `core/lib/agent-core/` | Agent runtime、tool calling、状态管理 |
|
|
303
|
+
| `@earendil-works/pi-coding-agent` | `core/` + `modes/` | 编码 agent CLI、会话、扩展 |
|
|
304
|
+
| `@earendil-works/pi-tui` | `core/lib/tui/` | 终端 UI 差分渲染 |
|
|
305
|
+
|
|
306
|
+
### 数据流
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
用户输入
|
|
310
|
+
↓
|
|
311
|
+
AgentHarness (编排层)
|
|
312
|
+
↓
|
|
313
|
+
Agent Loop (状态机)
|
|
314
|
+
↓
|
|
315
|
+
StreamFn → LLM Provider
|
|
316
|
+
↓
|
|
317
|
+
Tool Orchestration (工具编排)
|
|
318
|
+
↓
|
|
319
|
+
Session Persistence (会话持久化)
|
|
320
|
+
↓
|
|
321
|
+
Extension Hooks (扩展钩子)
|
|
322
|
+
↓
|
|
323
|
+
TUI Rendering (终端渲染)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 四、与上游的分叉点
|
|
329
|
+
|
|
330
|
+
### 纳米铅笔新增的能力
|
|
331
|
+
|
|
332
|
+
| 能力 | 上游状态 | 纳米铅笔实现 | 设计决策 |
|
|
333
|
+
|------|----------|-------------|----------|
|
|
334
|
+
| **持久记忆** | 无 | `packages/mem-core/` | 独立包,不进核心 |
|
|
335
|
+
| **人格进化** | 无 | `packages/soul-core/` | Big Five 向量 + 编码风格 |
|
|
336
|
+
| **Persona 切换** | 无 | `core/persona/` | 多人格管理 |
|
|
337
|
+
| **中文支持** | 英文 | i18n + DashScope provider | 本地化 |
|
|
338
|
+
| **Teach 扩展** | 无 | `extensions/builtin/teach/` | 引导式知识教学 |
|
|
339
|
+
| **DIP 协议** | 无 | `CLAUDE.md` 体系 | 文档同构约束 |
|
|
340
|
+
|
|
341
|
+
### 不可分叉的(必须保留的)
|
|
342
|
+
|
|
343
|
+
| 设计 | 原因 |
|
|
344
|
+
|------|------|
|
|
345
|
+
| 极简核心 | 这是 pi 的存在理由 |
|
|
346
|
+
| Agent Harness 生命周期 | 状态分离 + save point 语义是正确性的基础 |
|
|
347
|
+
| 会话树结构 | JSONL + tree 是状态管理的核心 |
|
|
348
|
+
| 扩展即一等公民 | 扩展能力矩阵决定了系统的可扩展性 |
|
|
349
|
+
| 代码质量纪律 | 这是工程品味的底线 |
|
|
350
|
+
| 终端原生 | 这是用户群体的共识 |
|
|
351
|
+
|
|
352
|
+
### 可以分叉的(纳米铅笔自己的增量)
|
|
353
|
+
|
|
354
|
+
| 设计 | 原因 |
|
|
355
|
+
|------|------|
|
|
356
|
+
| 记忆系统 | 上游明确没有,这是增量 |
|
|
357
|
+
| 人格系统 | 上游明确没有,这是增量 |
|
|
358
|
+
| DIP 文档协议 | 纳米铅笔自己的工程实践 |
|
|
359
|
+
| 中文支持 | 本地化需求 |
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 五、违反哲学的反模式
|
|
364
|
+
|
|
365
|
+
### 反模式 1:核心膨胀
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// ❌ 在 agent-core 里加记忆功能
|
|
369
|
+
import { MemCore } from "../mem-core/index.js";
|
|
370
|
+
if (options.enableMemory) { ... }
|
|
371
|
+
|
|
372
|
+
// ✅ 记忆是扩展
|
|
373
|
+
// extensions/builtin/memory/index.ts
|
|
374
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
375
|
+
const memories = await memCore.retrieve(ctx.messages);
|
|
376
|
+
ctx.injectContext(memories);
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 反模式 2:绕过 Harness
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// ❌ 直接写会话,不经过 save point
|
|
384
|
+
session.entries.push(newEntry);
|
|
385
|
+
session.save();
|
|
386
|
+
|
|
387
|
+
// ✅ 通过 pending writes 机制
|
|
388
|
+
harness.appendEntry(newEntry);
|
|
389
|
+
// 在 save point 自动刷新
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### 反模式 3:类型降级
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
// ❌ 用 any 修类型错误
|
|
396
|
+
const result: any = await someFunction();
|
|
397
|
+
|
|
398
|
+
// ✅ 升级依赖或修正类型
|
|
399
|
+
const result: ProperType = await someFunction();
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 反模式 4:内联 import
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// ❌ 动态 import
|
|
406
|
+
const { something } = await import("./module.js");
|
|
407
|
+
|
|
408
|
+
// ✅ 顶层 import
|
|
409
|
+
import { something } from "./module.js";
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 反模式 5:硬编码配置
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
// ❌ 硬编码按键
|
|
416
|
+
if (key === "ctrl+x") { ... }
|
|
417
|
+
|
|
418
|
+
// ✅ 走配置
|
|
419
|
+
if (matchesKey(key, DEFAULT_EDITOR_KEYBINDINGS.save)) { ... }
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## 六、哲学的检验标准
|
|
425
|
+
|
|
426
|
+
在做任何架构决策之前,问自己:
|
|
427
|
+
|
|
428
|
+
1. **这个功能能在扩展里实现吗?** 如果能,它就不该进核心。
|
|
429
|
+
2. **这个改动破坏了 Harness 的状态分离吗?** 如果破坏了,重新设计。
|
|
430
|
+
3. **这个代码能用 Result 而不是 throw 吗?** 如果是低层能力,用 Result。
|
|
431
|
+
4. **这个类型是 any 吗?** 如果是,停下来,找到正确的类型。
|
|
432
|
+
5. **这个 import 是内联的吗?** 如果是,移到文件顶部。
|
|
433
|
+
6. **这个配置是硬编码的吗?** 如果是,提取到默认配置。
|
|
434
|
+
7. **这个 PR 能解释清楚吗?** 如果不能,重新理解你的代码。
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## 七、致后来者
|
|
439
|
+
|
|
440
|
+
pi 的设计哲学不是文档,是纪律。
|
|
441
|
+
|
|
442
|
+
纪律的意思是:即使没有人在看,你也遵守。即使赶 deadline,你也遵守。即使"这次只是个小改动",你也遵守。
|
|
443
|
+
|
|
444
|
+
因为每一次妥协都会累积。每一次 `any` 都会让类型系统少保护一个角落。每一次内联 import 都会让依赖关系多一个隐藏的节点。每一次核心膨胀都会让扩展的空间少一分。
|
|
445
|
+
|
|
446
|
+
pi 用 61k stars 证明了:极简核心 + 扩展驱动 + 代码纪律 = 可持续的开源项目。
|
|
447
|
+
|
|
448
|
+
我们继承了这个骨架。记忆和灵魂是我们加的血肉。但骨架不能断。
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
**最后一条**:
|
|
453
|
+
|
|
454
|
+
> "Using AI to write code is fine. Submitting AI-generated slop without understanding it is not."
|
|
455
|
+
> — pi CONTRIBUTING.md
|
|
456
|
+
|
|
457
|
+
理解你写的每一行代码。这不是建议,是准入条件。
|