@pencil-agent/nano-pencil 2.0.1 → 2.0.3

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 (188) hide show
  1. package/README.md +267 -267
  2. package/dist/build-meta.json +3 -3
  3. package/dist/core/export-html/AGENT.md +11 -11
  4. package/dist/core/export-html/template.css +971 -971
  5. package/dist/core/export-html/template.html +54 -54
  6. package/dist/core/model/custom-providers.js +1 -1
  7. package/dist/core/model-registry.js +5 -5
  8. package/dist/extensions/builtin/AGENT.md +115 -115
  9. package/dist/extensions/builtin/browser/AGENT.md +17 -17
  10. package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
  11. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
  12. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
  13. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
  14. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
  15. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
  16. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
  17. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
  18. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
  19. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
  20. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
  21. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
  22. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
  23. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
  24. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
  25. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
  26. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
  27. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
  28. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
  29. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
  30. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
  31. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
  32. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
  33. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
  34. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
  35. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
  36. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
  37. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
  38. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
  39. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
  40. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
  41. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
  42. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
  43. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
  44. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
  45. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
  46. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
  47. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
  48. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
  49. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
  50. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
  51. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
  52. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
  53. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
  54. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
  55. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
  56. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
  57. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
  58. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
  59. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
  60. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
  61. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
  62. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
  63. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
  64. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
  65. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
  66. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
  67. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
  68. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
  69. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
  70. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
  71. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
  72. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
  73. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
  74. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
  75. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
  76. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
  77. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
  78. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
  79. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
  80. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
  81. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
  82. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
  83. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
  84. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
  85. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
  86. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
  87. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
  88. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
  89. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
  90. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
  91. package/dist/extensions/builtin/browser/browser.md +73 -73
  92. package/dist/extensions/builtin/browser/install.md +142 -142
  93. package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
  94. package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
  95. package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
  96. package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
  97. package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
  98. package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
  99. package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
  100. package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
  101. package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
  102. package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
  103. package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
  104. package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
  105. package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
  106. package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
  107. package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
  108. package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
  109. package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
  110. package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
  111. package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
  112. package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
  113. package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
  114. package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
  115. package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
  116. package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
  117. package/dist/extensions/builtin/debug/index.js +9 -9
  118. package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
  119. package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
  120. package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
  121. package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
  122. package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
  123. package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
  124. package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
  125. package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
  126. package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
  127. package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
  128. package/dist/extensions/builtin/goal/README.md +67 -67
  129. package/dist/extensions/builtin/goal/index.js +6 -6
  130. package/dist/extensions/builtin/grub/README.md +112 -112
  131. package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
  132. package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
  133. package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
  134. package/dist/extensions/builtin/link-world/linkworld.md +313 -313
  135. package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
  136. package/dist/extensions/builtin/loop/README.md +92 -92
  137. package/dist/extensions/builtin/mcp/figma-design.md +68 -68
  138. package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
  139. package/dist/extensions/builtin/recap/AGENT.md +15 -15
  140. package/dist/extensions/builtin/sal/README.md +72 -72
  141. package/dist/extensions/builtin/security-audit/README.md +289 -289
  142. package/dist/extensions/builtin/team/AGENT.md +112 -112
  143. package/dist/extensions/builtin/team/TESTING.md +299 -299
  144. package/dist/extensions/builtin/token-save/README.md +56 -56
  145. package/dist/extensions/optional/AGENT.md +10 -10
  146. package/dist/modes/interactive/controllers/input-submit-controller.js +2 -2
  147. package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
  148. package/dist/modes/interactive/interactive-mode.js +19 -19
  149. package/dist/modes/interactive/theme/dark.json +85 -85
  150. package/dist/modes/interactive/theme/light.json +84 -84
  151. package/dist/modes/interactive/theme/theme-schema.json +335 -335
  152. package/dist/modes/interactive/theme/warm.json +81 -81
  153. package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
  154. package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
  155. 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
  156. package/docs/SDK-TESTING.md +364 -0
  157. package/docs/codex-goal-command-impl.md +1055 -1055
  158. package/docs/codex-goal-vs-grub.md +500 -500
  159. package/docs/custom-provider.md +27 -27
  160. package/docs/extensions.md +27 -27
  161. package/docs/keybindings.md +27 -27
  162. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
  163. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
  164. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
  165. 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
  166. 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
  167. package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
  168. package/docs/loop-usage-examples.md +214 -214
  169. package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
  170. package/docs/models.md +27 -27
  171. package/docs/packages.md +27 -27
  172. package/docs/pi-design-philosophy.md +457 -457
  173. package/docs/planmode.md +1987 -1987
  174. package/docs/prompt-templates.md +27 -27
  175. package/docs/providers.md +27 -27
  176. package/docs/sdk.md +27 -27
  177. package/docs/skills.md +27 -27
  178. package/docs/startup-performance-optimization.md +301 -0
  179. package/docs/themes.md +27 -27
  180. package/docs/tui.md +27 -27
  181. package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
  182. package/package.json +190 -190
  183. package/docs/cc-agent-design.md +0 -1297
  184. package/docs/cc-tui-design.md +0 -1333
  185. package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
  186. package/docs/scan-report.md +0 -3820
  187. package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
  188. 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,321 +1,321 @@
1
- # `/loop` 命令重构计划
2
-
3
- > 基于 Claude Code v2.1.88 逆向分析文档的重构方案
4
- > 日期:2026-04-18
5
-
6
- ## 当前状态分析
7
-
8
- ### 现有实现
9
-
10
- nanoPencil 当前已经有工作的 loop 实现:
11
-
12
- - **核心文件**:
13
- - `extensions/defaults/loop/index.ts` (363行) - 主扩展逻辑
14
- - `scheduler-controller.ts` (155行) - 任务控制器
15
- - `scheduler-parser.ts` (246行) - 命令解析器
16
- - `scheduler-types.ts` (49行) - 类型定义
17
-
18
- - **已支持功能**:
19
- - 定时调度(s/m/h/d间隔)
20
- - 暂停/恢复/取消/运行
21
- - maxRuns自动取消
22
- - quiet模式
23
- - 命名任务
24
- - session-scoped(会话关闭清除)
25
- - slash command和prompt支持
26
-
27
- ### 架构对比
28
-
29
- | 特性 | Claude Code (文档) | nanoPencil 当前 | 差距 |
30
- |------|-------------------|-----------------|------|
31
- | 任务存储 | session + durable 文件 | 仅 session | ❌ 缺少持久化 |
32
- | 多进程支持 | scheduler lock | 无 | ❌ 缺少锁机制 |
33
- | 调度器设计 | 独立cronScheduler | 集成在扩展中 | ⚠️ 架构不同 |
34
- | jitter机制 | 有 | 无 | ❌ 缺少流量控制 |
35
- | 参数解析 | prompt-based (skill) | 直接解析 | ⚠️ 设计哲学不同 |
36
- | teammate支持 | agentId路由 | 无 | ❌ 缺少多Agent支持 |
37
- | 自动过期 | 7天recurring过期 | maxRuns | ⚠️ 机制不同 |
38
-
39
- ## 重构目标
40
-
41
- ### 阶段一:核心功能补全(必须)
42
-
43
- #### 目标1.1:添加 durable 持久化存储
44
-
45
- **当前问题**:
46
- - loop任务只保存在内存中,会话关闭后丢失
47
- - 无法跨session保持loop任务
48
-
49
- **实施方案**:
50
- 1. 创建 `loop-tasks.ts` 文件存储模块
51
- 2. 在项目目录创建 `.nanopencil/loop-tasks.json`
52
- 3. 支持 `--durable` 或 `-d` flag
53
- 4. 任务启动时自动恢复durable任务
54
-
55
- **文件变更**:
56
- - 新建 `extensions/defaults/loop/loop-tasks.ts`
57
- - 修改 `scheduler-types.ts` 增加 `durable` 字段
58
- - 修改 `scheduler-controller.ts` 支持durable读写
59
- - 修改 `index.ts` 在session_start时恢复durable任务
60
-
61
- #### 目标1.2:添加 scheduler lock
62
-
63
- **当前问题**:
64
- - 多个nanoPencil实例共享项目目录时,可能重复触发任务
65
- - durable任务需要确保只被一个实例执行
66
-
67
- **实施方案**:
68
- 1. 使用 `proper-lockfile` 包(已存在依赖)
69
- 2. 创建 `.nanopencil/loop-scheduler.lock`
70
- 3. 调度器启动时获取锁
71
- 4. 失败时降级为非owner模式(不触发durable任务)
72
-
73
- **文件变更**:
74
- - 修改 `scheduler-controller.ts` 增加锁管理
75
- - 修改 `index.ts` 启动时获取锁
76
-
77
- ### 阶段二:架构优化(推荐)
78
-
79
- #### 目标2.1:独立调度器模块
80
-
81
- **当前问题**:
82
- - 调度逻辑混合在扩展代码中
83
- - 难以独立测试和复用
84
-
85
- **实施方案**:
86
- 1. 提取 `loop-scheduler.ts` 独立调度器
87
- 2. 使用回调接口(`onFire`, `isLoading`等)
88
- 3. 支持外部测试和mock
89
-
90
- **文件变更**:
91
- - 新建 `extensions/defaults/loop/loop-scheduler.ts`
92
- - 修改 `scheduler-controller.ts` 瘦身为数据管理器
93
- - 修改 `index.ts` 使用独立调度器
94
-
95
- #### 目标2.2:添加 jitter 机制
96
-
97
- **当前问题**:
98
- - 所有任务都在整点触发
99
- - 可能造成流量尖峰
100
-
101
- **实施方案**:
102
- 1. 为每个任务添加确定性jitter
103
- 2. 基于taskId计算jitter值
104
- 3. 避免流量尖峰
105
-
106
- **文件变更**:
107
- - 修改 `scheduler-controller.ts` 计算jitter
108
- - 修改 `loop-tasks.ts` 保存jitter偏移
109
-
110
- ### 阶段三:高级特性(可选)
111
-
112
- #### 目标3.1:teammate 路由支持
113
-
114
- **实施方案**:
115
- - 增加任务的 `agentId` 字段
116
- - 集成 subagent/team 扩展路由逻辑
117
-
118
- #### 目标3.2:自动过期机制
119
-
120
- **实施方案**:
121
- - recurring任务默认7天后自动删除
122
- - 在 `markDispatched` 中检查过期时间
123
-
124
- ## 重构步骤
125
-
126
- ### 第一阶段:durable 持久化
127
-
128
- #### 步骤1.1:创建 loop-tasks.ts
129
-
130
- ```typescript
131
- // extensions/defaults/loop/loop-tasks.ts
132
- import { randomBytes } from "node:crypto";
133
- import { promises as fs } from "node:fs";
134
- import { join } from "node:path";
135
-
136
- const LOOP_TASKS_FILE = ".nanopencil/loop-tasks.json";
137
-
138
- export async function readLoopTasks(projectRoot: string): Promise<ScheduledLoopTask[]> {
139
- // 实现读取逻辑,处理文件不存在、JSON错误等
140
- }
141
-
142
- export async function writeLoopTasks(projectRoot: string, tasks: ScheduledLoopTask[]): Promise<void> {
143
- // 实现写入逻辑,创建目录,原子写入
144
- }
145
-
146
- export async function addDurableLoopTask(
147
- projectRoot: string,
148
- task: ScheduledLoopTask,
149
- ): Promise<string> {
150
- // 实现添加durable任务
151
- }
152
- ```
153
-
154
- #### 步骤1.2:修改类型定义
155
-
156
- ```typescript
157
- // scheduler-types.ts
158
- export interface ScheduledLoopTask {
159
- // 现有字段...
160
- durable?: boolean; // 新增
161
- agentId?: string; // 为teammate预留
162
- }
163
- ```
164
-
165
- #### 步骤1.3:修改 controller 支持durable
166
-
167
- ```typescript
168
- // scheduler-controller.ts
169
- export class SchedulerController {
170
- private projectRoot?: string;
171
-
172
- setProjectRoot(root: string): void {
173
- this.projectRoot = root;
174
- }
175
-
176
- async loadDurableTasks(): Promise<void> {
177
- // 从文件加载durable任务
178
- }
179
-
180
- async saveDurableTasks(): Promise<void> {
181
- // 保存durable任务到文件
182
- }
183
- }
184
- ```
185
-
186
- #### 步骤1.4:修改扩展初始化
187
-
188
- ```typescript
189
- // index.ts
190
- api.on("session_start", async () => {
191
- if (ctx.projectRoot) {
192
- controller.setProjectRoot(ctx.projectRoot);
193
- await controller.loadDurableTasks();
194
- }
195
- ensureSchedulerTicker(api, bus);
196
- });
197
- ```
198
-
199
- ### 第二阶段:scheduler lock
200
-
201
- #### 步骤2.1:添加锁管理
202
-
203
- ```typescript
204
- // scheduler-controller.ts
205
- import lockfile from "proper-lockfile";
206
-
207
- export class SchedulerController {
208
- private lock?: lockfile.Lock;
209
-
210
- async acquireSchedulerLock(): Promise<boolean> {
211
- try {
212
- this.lock = await lockfile.lock(this.getLockPath());
213
- return true;
214
- } catch {
215
- return false;
216
- }
217
- }
218
-
219
- releaseSchedulerLock(): void {
220
- if (this.lock) {
221
- lockfile.unlock(this.lock);
222
- this.lock = undefined;
223
- }
224
- }
225
- }
226
- ```
227
-
228
- #### 步骤2.2:修改调度逻辑
229
-
230
- ```typescript
231
- // 只有lock owner才触发durable任务
232
- if (isDurableTask(task) && !isLockOwner) {
233
- return; // 跳过
234
- }
235
- ```
236
-
237
- ### 第三阶段:独立调度器(可选)
238
-
239
- #### 步骤3.1:提取调度器
240
-
241
- ```typescript
242
- // loop-scheduler.ts
243
- export interface LoopSchedulerOptions {
244
- onFire: (task: ScheduledLoopTask) => void;
245
- isLoading: () => boolean;
246
- isKilled?: () => boolean;
247
- }
248
-
249
- export function createLoopScheduler(options: LoopSchedulerOptions): LoopScheduler {
250
- // 独立的调度器实现
251
- }
252
- ```
253
-
254
- ## 验证计划
255
-
256
- ### 功能测试
257
-
258
- 1. **durable持久化**
259
- - 创建durable任务,关闭会话,重新打开,任务仍在
260
- - 多个会话共享同一目录,只有一个会话触发任务
261
-
262
- 2. **scheduler lock**
263
- - 启动两个nanoPencil实例,观察任务执行情况
264
- - 只有lock owner触发durable任务
265
-
266
- 3. **向后兼容**
267
- - 现有session-only任务不受影响
268
- - 命令接口保持不变
269
-
270
- ### 性能测试
271
-
272
- 1. **文件IO性能**
273
- - 50个durable任务的读写性能
274
- - 频繁调度的性能影响
275
-
276
- 2. **并发安全**
277
- - 多进程同时创建任务
278
- - 文件损坏恢复能力
279
-
280
- ## 风险与缓解
281
-
282
- | 风险 | 缓解措施 |
283
- |------|----------|
284
- | 文件IO阻塞主线程 | 使用异步API,限制文件大小 |
285
- | 锁机制死锁 | 设置锁超时,提供手动清理 |
286
- | 文件损坏 | 容错读取,备份机制 |
287
- | 向后兼容性 | 保留现有接口,增量添加 |
288
-
289
- ## 交付标准
290
-
291
- - [x] durable任务支持完成,测试通过
292
- - [x] scheduler lock实现,测试通过
293
- - [x] auto-expiry自动过期实现,测试通过
294
- - [x] 向后兼容,现有功能不受影响
295
- - [x] 文档更新(README.md)
296
- - [x] 单元测试通过(5个测试全部通过)
297
- - [x] 使用示例文档
298
- - [ ] 手动集成测试
299
-
300
- ## 时间估算
301
-
302
- - 阶段一(核心功能):2-3天
303
- - 阶段二(架构优化):1-2天
304
- - 阶段三(高级特性):按需
305
-
306
- ## 优先级建议
307
-
308
- **建议执行顺序**:
309
- 1. **必须**:阶段一.1(durable持久化)- 解决主要痛点
310
- 2. **强烈推荐**:阶段一.2(scheduler lock)- 保证多进程安全
311
- 3. **可选**:阶段二(架构优化)- 提升代码质量
312
- 4. **未来考虑**:阶段三(高级特性)- 根据实际需求
313
-
314
- ## 结论
315
-
316
- 当前nanoPencil的loop实现已经具备基本功能,主要缺失的是:
317
-
318
- 1. **持久化存储** - 用户最常要求的功能
319
- 2. **多进程安全** - 生产环境必需
320
-
1
+ # `/loop` 命令重构计划
2
+
3
+ > 基于 Claude Code v2.1.88 逆向分析文档的重构方案
4
+ > 日期:2026-04-18
5
+
6
+ ## 当前状态分析
7
+
8
+ ### 现有实现
9
+
10
+ nanoPencil 当前已经有工作的 loop 实现:
11
+
12
+ - **核心文件**:
13
+ - `extensions/defaults/loop/index.ts` (363行) - 主扩展逻辑
14
+ - `scheduler-controller.ts` (155行) - 任务控制器
15
+ - `scheduler-parser.ts` (246行) - 命令解析器
16
+ - `scheduler-types.ts` (49行) - 类型定义
17
+
18
+ - **已支持功能**:
19
+ - 定时调度(s/m/h/d间隔)
20
+ - 暂停/恢复/取消/运行
21
+ - maxRuns自动取消
22
+ - quiet模式
23
+ - 命名任务
24
+ - session-scoped(会话关闭清除)
25
+ - slash command和prompt支持
26
+
27
+ ### 架构对比
28
+
29
+ | 特性 | Claude Code (文档) | nanoPencil 当前 | 差距 |
30
+ |------|-------------------|-----------------|------|
31
+ | 任务存储 | session + durable 文件 | 仅 session | ❌ 缺少持久化 |
32
+ | 多进程支持 | scheduler lock | 无 | ❌ 缺少锁机制 |
33
+ | 调度器设计 | 独立cronScheduler | 集成在扩展中 | ⚠️ 架构不同 |
34
+ | jitter机制 | 有 | 无 | ❌ 缺少流量控制 |
35
+ | 参数解析 | prompt-based (skill) | 直接解析 | ⚠️ 设计哲学不同 |
36
+ | teammate支持 | agentId路由 | 无 | ❌ 缺少多Agent支持 |
37
+ | 自动过期 | 7天recurring过期 | maxRuns | ⚠️ 机制不同 |
38
+
39
+ ## 重构目标
40
+
41
+ ### 阶段一:核心功能补全(必须)
42
+
43
+ #### 目标1.1:添加 durable 持久化存储
44
+
45
+ **当前问题**:
46
+ - loop任务只保存在内存中,会话关闭后丢失
47
+ - 无法跨session保持loop任务
48
+
49
+ **实施方案**:
50
+ 1. 创建 `loop-tasks.ts` 文件存储模块
51
+ 2. 在项目目录创建 `.nanopencil/loop-tasks.json`
52
+ 3. 支持 `--durable` 或 `-d` flag
53
+ 4. 任务启动时自动恢复durable任务
54
+
55
+ **文件变更**:
56
+ - 新建 `extensions/defaults/loop/loop-tasks.ts`
57
+ - 修改 `scheduler-types.ts` 增加 `durable` 字段
58
+ - 修改 `scheduler-controller.ts` 支持durable读写
59
+ - 修改 `index.ts` 在session_start时恢复durable任务
60
+
61
+ #### 目标1.2:添加 scheduler lock
62
+
63
+ **当前问题**:
64
+ - 多个nanoPencil实例共享项目目录时,可能重复触发任务
65
+ - durable任务需要确保只被一个实例执行
66
+
67
+ **实施方案**:
68
+ 1. 使用 `proper-lockfile` 包(已存在依赖)
69
+ 2. 创建 `.nanopencil/loop-scheduler.lock`
70
+ 3. 调度器启动时获取锁
71
+ 4. 失败时降级为非owner模式(不触发durable任务)
72
+
73
+ **文件变更**:
74
+ - 修改 `scheduler-controller.ts` 增加锁管理
75
+ - 修改 `index.ts` 启动时获取锁
76
+
77
+ ### 阶段二:架构优化(推荐)
78
+
79
+ #### 目标2.1:独立调度器模块
80
+
81
+ **当前问题**:
82
+ - 调度逻辑混合在扩展代码中
83
+ - 难以独立测试和复用
84
+
85
+ **实施方案**:
86
+ 1. 提取 `loop-scheduler.ts` 独立调度器
87
+ 2. 使用回调接口(`onFire`, `isLoading`等)
88
+ 3. 支持外部测试和mock
89
+
90
+ **文件变更**:
91
+ - 新建 `extensions/defaults/loop/loop-scheduler.ts`
92
+ - 修改 `scheduler-controller.ts` 瘦身为数据管理器
93
+ - 修改 `index.ts` 使用独立调度器
94
+
95
+ #### 目标2.2:添加 jitter 机制
96
+
97
+ **当前问题**:
98
+ - 所有任务都在整点触发
99
+ - 可能造成流量尖峰
100
+
101
+ **实施方案**:
102
+ 1. 为每个任务添加确定性jitter
103
+ 2. 基于taskId计算jitter值
104
+ 3. 避免流量尖峰
105
+
106
+ **文件变更**:
107
+ - 修改 `scheduler-controller.ts` 计算jitter
108
+ - 修改 `loop-tasks.ts` 保存jitter偏移
109
+
110
+ ### 阶段三:高级特性(可选)
111
+
112
+ #### 目标3.1:teammate 路由支持
113
+
114
+ **实施方案**:
115
+ - 增加任务的 `agentId` 字段
116
+ - 集成 subagent/team 扩展路由逻辑
117
+
118
+ #### 目标3.2:自动过期机制
119
+
120
+ **实施方案**:
121
+ - recurring任务默认7天后自动删除
122
+ - 在 `markDispatched` 中检查过期时间
123
+
124
+ ## 重构步骤
125
+
126
+ ### 第一阶段:durable 持久化
127
+
128
+ #### 步骤1.1:创建 loop-tasks.ts
129
+
130
+ ```typescript
131
+ // extensions/defaults/loop/loop-tasks.ts
132
+ import { randomBytes } from "node:crypto";
133
+ import { promises as fs } from "node:fs";
134
+ import { join } from "node:path";
135
+
136
+ const LOOP_TASKS_FILE = ".nanopencil/loop-tasks.json";
137
+
138
+ export async function readLoopTasks(projectRoot: string): Promise<ScheduledLoopTask[]> {
139
+ // 实现读取逻辑,处理文件不存在、JSON错误等
140
+ }
141
+
142
+ export async function writeLoopTasks(projectRoot: string, tasks: ScheduledLoopTask[]): Promise<void> {
143
+ // 实现写入逻辑,创建目录,原子写入
144
+ }
145
+
146
+ export async function addDurableLoopTask(
147
+ projectRoot: string,
148
+ task: ScheduledLoopTask,
149
+ ): Promise<string> {
150
+ // 实现添加durable任务
151
+ }
152
+ ```
153
+
154
+ #### 步骤1.2:修改类型定义
155
+
156
+ ```typescript
157
+ // scheduler-types.ts
158
+ export interface ScheduledLoopTask {
159
+ // 现有字段...
160
+ durable?: boolean; // 新增
161
+ agentId?: string; // 为teammate预留
162
+ }
163
+ ```
164
+
165
+ #### 步骤1.3:修改 controller 支持durable
166
+
167
+ ```typescript
168
+ // scheduler-controller.ts
169
+ export class SchedulerController {
170
+ private projectRoot?: string;
171
+
172
+ setProjectRoot(root: string): void {
173
+ this.projectRoot = root;
174
+ }
175
+
176
+ async loadDurableTasks(): Promise<void> {
177
+ // 从文件加载durable任务
178
+ }
179
+
180
+ async saveDurableTasks(): Promise<void> {
181
+ // 保存durable任务到文件
182
+ }
183
+ }
184
+ ```
185
+
186
+ #### 步骤1.4:修改扩展初始化
187
+
188
+ ```typescript
189
+ // index.ts
190
+ api.on("session_start", async () => {
191
+ if (ctx.projectRoot) {
192
+ controller.setProjectRoot(ctx.projectRoot);
193
+ await controller.loadDurableTasks();
194
+ }
195
+ ensureSchedulerTicker(api, bus);
196
+ });
197
+ ```
198
+
199
+ ### 第二阶段:scheduler lock
200
+
201
+ #### 步骤2.1:添加锁管理
202
+
203
+ ```typescript
204
+ // scheduler-controller.ts
205
+ import lockfile from "proper-lockfile";
206
+
207
+ export class SchedulerController {
208
+ private lock?: lockfile.Lock;
209
+
210
+ async acquireSchedulerLock(): Promise<boolean> {
211
+ try {
212
+ this.lock = await lockfile.lock(this.getLockPath());
213
+ return true;
214
+ } catch {
215
+ return false;
216
+ }
217
+ }
218
+
219
+ releaseSchedulerLock(): void {
220
+ if (this.lock) {
221
+ lockfile.unlock(this.lock);
222
+ this.lock = undefined;
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ #### 步骤2.2:修改调度逻辑
229
+
230
+ ```typescript
231
+ // 只有lock owner才触发durable任务
232
+ if (isDurableTask(task) && !isLockOwner) {
233
+ return; // 跳过
234
+ }
235
+ ```
236
+
237
+ ### 第三阶段:独立调度器(可选)
238
+
239
+ #### 步骤3.1:提取调度器
240
+
241
+ ```typescript
242
+ // loop-scheduler.ts
243
+ export interface LoopSchedulerOptions {
244
+ onFire: (task: ScheduledLoopTask) => void;
245
+ isLoading: () => boolean;
246
+ isKilled?: () => boolean;
247
+ }
248
+
249
+ export function createLoopScheduler(options: LoopSchedulerOptions): LoopScheduler {
250
+ // 独立的调度器实现
251
+ }
252
+ ```
253
+
254
+ ## 验证计划
255
+
256
+ ### 功能测试
257
+
258
+ 1. **durable持久化**
259
+ - 创建durable任务,关闭会话,重新打开,任务仍在
260
+ - 多个会话共享同一目录,只有一个会话触发任务
261
+
262
+ 2. **scheduler lock**
263
+ - 启动两个nanoPencil实例,观察任务执行情况
264
+ - 只有lock owner触发durable任务
265
+
266
+ 3. **向后兼容**
267
+ - 现有session-only任务不受影响
268
+ - 命令接口保持不变
269
+
270
+ ### 性能测试
271
+
272
+ 1. **文件IO性能**
273
+ - 50个durable任务的读写性能
274
+ - 频繁调度的性能影响
275
+
276
+ 2. **并发安全**
277
+ - 多进程同时创建任务
278
+ - 文件损坏恢复能力
279
+
280
+ ## 风险与缓解
281
+
282
+ | 风险 | 缓解措施 |
283
+ |------|----------|
284
+ | 文件IO阻塞主线程 | 使用异步API,限制文件大小 |
285
+ | 锁机制死锁 | 设置锁超时,提供手动清理 |
286
+ | 文件损坏 | 容错读取,备份机制 |
287
+ | 向后兼容性 | 保留现有接口,增量添加 |
288
+
289
+ ## 交付标准
290
+
291
+ - [x] durable任务支持完成,测试通过
292
+ - [x] scheduler lock实现,测试通过
293
+ - [x] auto-expiry自动过期实现,测试通过
294
+ - [x] 向后兼容,现有功能不受影响
295
+ - [x] 文档更新(README.md)
296
+ - [x] 单元测试通过(5个测试全部通过)
297
+ - [x] 使用示例文档
298
+ - [ ] 手动集成测试
299
+
300
+ ## 时间估算
301
+
302
+ - 阶段一(核心功能):2-3天
303
+ - 阶段二(架构优化):1-2天
304
+ - 阶段三(高级特性):按需
305
+
306
+ ## 优先级建议
307
+
308
+ **建议执行顺序**:
309
+ 1. **必须**:阶段一.1(durable持久化)- 解决主要痛点
310
+ 2. **强烈推荐**:阶段一.2(scheduler lock)- 保证多进程安全
311
+ 3. **可选**:阶段二(架构优化)- 提升代码质量
312
+ 4. **未来考虑**:阶段三(高级特性)- 根据实际需求
313
+
314
+ ## 结论
315
+
316
+ 当前nanoPencil的loop实现已经具备基本功能,主要缺失的是:
317
+
318
+ 1. **持久化存储** - 用户最常要求的功能
319
+ 2. **多进程安全** - 生产环境必需
320
+
321
321
  建议优先实现这两个核心功能,其他优化可以根据实际使用情况逐步迭代。