my-opencode-toolkit 0.2.4 → 0.2.6

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
@@ -136,7 +136,7 @@ OpenCode v2 插件:系统通知、持久目标、定时任务与循环、TUI
136
136
  - 侧栏统计是进程内数据:重启后第一次打开面板会从已存消息重建(重建瞬间完成的一步可能漏算)
137
137
  - 通知依赖系统命令:Windows 用 PowerShell toast,macOS 用 `osascript`,Linux 用 `notify-send`
138
138
  - 定时任务按 OpenCode 进程的本地时区解析;不支持秒级字段与 `L` / `W` / `?` 等扩展语法
139
- - 夏令时切换当天,本地时间的 cron 可能跳过或重复一次(标准 cron 语义的固有问题)
139
+ - 夏令时:春季拨快时不存在的时间顺延到次日;秋季拨慢时重复的一小时会被跳过(每天只触发一次)
140
140
  - 同一会话不要同时开多个 OpenCode 实例执行目标/定时任务;插件存储没有跨进程锁
141
141
  - 目标进行中时 cron/loop 让路,避免和续作抢同一轮对话
142
142
  - 子代理会话不提供 goal/scheduling 工具,也不会被注入续作
@@ -172,11 +172,12 @@ OpenCode v2 插件:系统通知、持久目标、定时任务与循环、TUI
172
172
 
173
173
  ```bash
174
174
  pnpm install
175
- pnpm check # typecheck + lint + 175 单测 + TUI 测试 + 发布包内容校验
175
+ pnpm check # typecheck + lint + 175 单测 + 7 bun 测试 + 发布包内容校验
176
176
  pnpm test:e2e # 需要本机 opencode 2.x 与 bun,使用隔离的 HOME/DB/config
177
+ pnpm test:e2e:tui # 真实 TUI:PTY 启动 opencode --standalone,打开设置菜单
177
178
  ```
178
179
 
179
- e2e 用假 provider 驱动工具调用,不会请求真实模型;它会在临时目录里启动一个隔离的 OpenCode server,不读写日常数据。
180
+ e2e 用假 provider 驱动工具调用,不会请求真实模型;它会在临时目录里启动一个隔离的 OpenCode server,不读写日常数据。`test:e2e:tui` 通过 ConPTY(Windows)或 openpty(macOS/Linux)驱动真实 TUI,验证 TUI 插件加载、斜杠命令与设置菜单渲染。
180
181
 
181
182
  ## 许可证
182
183
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-opencode-toolkit",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "OpenCode 插件:系统通知、持久目标、定时任务与 TUI 面板",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -50,6 +50,7 @@
50
50
  "test": "vitest run",
51
51
  "test:tui": "cd tui-test && bun test",
52
52
  "test:e2e": "bun scripts/e2e-load.mjs",
53
+ "test:e2e:tui": "bun scripts/e2e-tui.mjs",
53
54
  "test:watch": "vitest"
54
55
  },
55
56
  "devDependencies": {
package/src/stats.ts CHANGED
@@ -5,8 +5,10 @@ import { asRecord } from "./shared/json.ts"
5
5
  /** 单个会话的进程内统计。 */
6
6
  export type SessionStats = {
7
7
  readonly steps: number
8
+ /** 总提示词 token:`input + cache.read + cache.write`。 */
8
9
  readonly input: number
9
10
  readonly output: number
11
+ /** 缓存命中 token:只算 `cache.read`。 */
10
12
  readonly cached: number
11
13
  readonly tools: Readonly<Record<string, number>>
12
14
  readonly subagents: number
@@ -70,9 +72,10 @@ export function createStatsTracker(): StatsTracker {
70
72
  const cache = asRecord(record["cache"]) ?? {}
71
73
  const stats = of(sessionID)
72
74
  stats.steps += 1
73
- stats.input += count(record["input"])
75
+ // 分母是完整提示词;cache.write 是写入成本,不算命中。
76
+ stats.input += count(record["input"]) + count(cache["read"]) + count(cache["write"])
74
77
  stats.output += count(record["output"])
75
- stats.cached += count(cache["read"]) + count(cache["write"])
78
+ stats.cached += count(cache["read"])
76
79
  },
77
80
 
78
81
  recordTool(sessionID, tool) {
@@ -189,9 +192,10 @@ export async function buildStats(ctx: Plugin.Context, sessionID: string): Promis
189
192
  steps += 1
190
193
  const tokens = message.tokens
191
194
  if (tokens !== undefined) {
192
- input += tokens.input
195
+ // 与 recordStep 保持同一口径:input 是完整提示词,cached 只算读命中。
196
+ input += tokens.input + tokens.cache.read + tokens.cache.write
193
197
  output += tokens.output
194
- cached += tokens.cache.read + tokens.cache.write
198
+ cached += tokens.cache.read
195
199
  }
196
200
  for (const part of message.content) {
197
201
  if (part.type !== "tool") continue
@@ -204,12 +208,11 @@ export async function buildStats(ctx: Plugin.Context, sessionID: string): Promis
204
208
  }
205
209
 
206
210
  /**
207
- * 输入:输入 token 与缓存 token。
211
+ * 输入:完整提示词 token(含缓存读写)与命中 token(`cache.read`)。
208
212
  * 输出:缓存命中率百分比,向下截断到两位小数。
209
- * 预期:分母为 0 时返回 0;`100` 只代表真实全命中。
213
+ * 预期:分母为 0 时返回 0;结果夹在 0..100;`100` 只代表真实全命中。
210
214
  */
211
215
  export function hitRatePercent(input: number, cached: number): number {
212
- const total = input + cached
213
- if (total <= 0) return 0
214
- return Math.floor((cached / total) * 10_000) / 100
216
+ if (input <= 0 || cached <= 0) return 0
217
+ return Math.floor(Math.min(1, cached / input) * 10_000) / 100
215
218
  }