@siming-org/opencode-plugin 0.1.13 → 0.1.16

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 (57) hide show
  1. package/README.md +37 -3
  2. package/dist/assets-store.d.ts +2 -1
  3. package/dist/assets-store.d.ts.map +1 -1
  4. package/dist/assets-store.js +2 -1
  5. package/dist/assets-store.js.map +1 -1
  6. package/dist/fetch-assets.d.ts +44 -1
  7. package/dist/fetch-assets.d.ts.map +1 -1
  8. package/dist/fetch-assets.js +141 -13
  9. package/dist/fetch-assets.js.map +1 -1
  10. package/dist/guide-skill.d.ts +2 -2
  11. package/dist/guide-skill.d.ts.map +1 -1
  12. package/dist/guide-skill.js +2 -2
  13. package/dist/guide-skill.js.map +1 -1
  14. package/dist/host-api.d.ts +35 -4
  15. package/dist/host-api.d.ts.map +1 -1
  16. package/dist/host-api.js +5 -24
  17. package/dist/host-api.js.map +1 -1
  18. package/dist/register/skills.d.ts +7 -4
  19. package/dist/register/skills.d.ts.map +1 -1
  20. package/dist/register/skills.js +11 -5
  21. package/dist/register/skills.js.map +1 -1
  22. package/dist/register/todo-suite.d.ts +28 -0
  23. package/dist/register/todo-suite.d.ts.map +1 -0
  24. package/dist/register/todo-suite.js +133 -0
  25. package/dist/register/todo-suite.js.map +1 -0
  26. package/dist/revalidate.d.ts +3 -1
  27. package/dist/revalidate.d.ts.map +1 -1
  28. package/dist/revalidate.js +5 -3
  29. package/dist/revalidate.js.map +1 -1
  30. package/dist/server/index.d.ts.map +1 -1
  31. package/dist/server/index.js +50 -4
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/todo/config.d.ts +21 -0
  34. package/dist/todo/config.d.ts.map +1 -0
  35. package/dist/todo/config.js +272 -0
  36. package/dist/todo/config.js.map +1 -0
  37. package/dist/todo/counter.d.ts +14 -0
  38. package/dist/todo/counter.d.ts.map +1 -0
  39. package/dist/todo/counter.js +19 -0
  40. package/dist/todo/counter.js.map +1 -0
  41. package/dist/todo/nudge-log.d.ts +5 -0
  42. package/dist/todo/nudge-log.d.ts.map +1 -0
  43. package/dist/todo/nudge-log.js +45 -0
  44. package/dist/todo/nudge-log.js.map +1 -0
  45. package/dist/todo/prompt.d.ts +9 -0
  46. package/dist/todo/prompt.d.ts.map +1 -0
  47. package/dist/todo/prompt.js +18 -0
  48. package/dist/todo/prompt.js.map +1 -0
  49. package/dist/todo/rate-limit.d.ts +7 -0
  50. package/dist/todo/rate-limit.d.ts.map +1 -0
  51. package/dist/todo/rate-limit.js +20 -0
  52. package/dist/todo/rate-limit.js.map +1 -0
  53. package/dist/todo/task-planning-skill.d.ts +9 -0
  54. package/dist/todo/task-planning-skill.d.ts.map +1 -0
  55. package/dist/todo/task-planning-skill.js +58 -0
  56. package/dist/todo/task-planning-skill.js.map +1 -0
  57. package/package.json +2 -2
@@ -0,0 +1,272 @@
1
+ /**
2
+ * todoNudge 配置模型与热生效(技术方案 §4.1/§4.2,T202610260002)。
3
+ *
4
+ * 配置键:工作区 <ws>/.siming/config.json(严格 JSON)与全局 ~/.siming/config.json(jsonc 宽容)
5
+ * 新增可选键 `todoNudge: { enabled, every, text }`;既有键(serverUrl/defaultProject)互不影响。
6
+ *
7
+ * 语义要点(PRD 规则 4/17,F-1 读取路径约束):
8
+ * - 合并按字段级「最具体提供层」:工作区(就近命中的那份文件)→ 全局 → 内置默认;
9
+ * 某层提供了该字段但值非法 → 取内置默认并告警(不继承下一层——错误不跨层传染);
10
+ * - 读取不得复用 readWorkspaceConfig/tryLoadJsonc(两者把「损坏」折叠成 null=缺席):
11
+ * 本模块自建读取路径 existsSync → 读取 → 解析,解析失败即 corrupt;
12
+ * - corrupt:任一层文件存在但不可读/解析失败 → snapshot()=null(enabled 视同 false),
13
+ * 不回退其他层也不回退默认;下一次配置变更事件重读成功即恢复;
14
+ * - 热生效:fs.watch 监听集合由定位算法驱动(walkTodoConfigPath),150ms 防抖重读,
15
+ * 重读即重新定位;watcher 失败降级为冻结快照 + healthy()=false(变更需重启生效)。
16
+ *
17
+ * per-location 实例(T202610220003):每次 createTodoNudgeConfig 各自持有快照与 watcher,
18
+ * 无模块级状态;多 location 重复监听全局层为已接受的代价(§4.8)。
19
+ */
20
+ import { existsSync, readFileSync, watch } from 'node:fs';
21
+ import { homedir } from 'node:os';
22
+ import { join } from 'node:path';
23
+ import { parse as parseJsonc } from 'jsonc-parser';
24
+ import { walkTodoConfigPath } from '../fetch-assets.js';
25
+ import { DEFAULT_NUDGE_TEXT } from './prompt.js';
26
+ import { createRateLimiter } from './rate-limit.js';
27
+ /** 内置默认(PRD 规则 2/8.3 逐字) */
28
+ const DEFAULTS = { enabled: true, every: 6, text: DEFAULT_NUDGE_TEXT };
29
+ const DEBOUNCE_MS = 150;
30
+ /** 工作区层读取:严格 JSON(对齐 CLI 工作区配置读取惯例);不可读/解析失败/非对象即 corrupt
31
+ * (与全局层同口径:文件存在但不是可用配置对象 = 损坏,不是「未提供」) */
32
+ function readWorkspaceLayer(file) {
33
+ try {
34
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
35
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
36
+ return { values: null, corrupt: true };
37
+ }
38
+ return { values: parsed, corrupt: false };
39
+ }
40
+ catch {
41
+ return { values: null, corrupt: true };
42
+ }
43
+ }
44
+ /** 全局层读取:jsonc 宽容(注释/尾逗号容忍);解析结果非对象、或扫描器报 error 即 corrupt。
45
+ * F-CFG-01:jsonc-parser parse 是容错扫描器——内容损坏(如 `{ not-json`)不抛错而是返回
46
+ * 折叠结果(undefined/{}/残留对象),仅判返回值会把「损坏」视同合法空配置。必须收集 errors
47
+ * 并以「errors 非空」为损坏信号(PRD 规则 17/验收 7:损坏按关闭)。
48
+ * allowTrailingComma 必须显式 true:ParseOptions.DEFAULT.allowTrailingComma=false,尾逗号
49
+ * 默认产生 error——不开启会把合法尾逗号样本误判 corrupt(注释保持默认宽容:
50
+ * disallowComments 默认 false,不写 error)。 */
51
+ function readGlobalLayer(file) {
52
+ try {
53
+ const errors = [];
54
+ const parsed = parseJsonc(readFileSync(file, 'utf8'), errors, { allowTrailingComma: true });
55
+ if (errors.length > 0 || typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
56
+ return { values: null, corrupt: true };
57
+ }
58
+ return { values: parsed, corrupt: false };
59
+ }
60
+ catch {
61
+ return { values: null, corrupt: true };
62
+ }
63
+ }
64
+ function todoNudgeOf(values, file, allowAlert) {
65
+ if (values === null)
66
+ return { values: null, file };
67
+ const raw = values['todoNudge'];
68
+ if (raw === undefined)
69
+ return { values: null, file };
70
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
71
+ if (allowAlert(`todoNudge-shape:${file}`)) {
72
+ console.error(`[siming-plugin] ${file} 的 todoNudge 不是对象(忽略该层的 todoNudge,其余键不受影响)`);
73
+ }
74
+ return { values: null, file };
75
+ }
76
+ return { values: raw, file };
77
+ }
78
+ /** 字段级合并:最具体提供层优先;值非法取内置默认并告警(不继承下一层,PRD 规则 17) */
79
+ function mergeLayers(workspace, global, allowAlert) {
80
+ const pick = (field, isValid, fallback) => {
81
+ for (const layer of [workspace, global]) {
82
+ const raw = layer.values?.[field];
83
+ if (raw === undefined)
84
+ continue;
85
+ if (isValid(raw))
86
+ return raw;
87
+ if (allowAlert(`field:${layer.file}:${field}`)) {
88
+ console.error(`[siming-plugin] ${layer.file} 的 todoNudge.${field} 非法(${JSON.stringify(raw)}),该字段回退内置默认`);
89
+ }
90
+ return fallback;
91
+ }
92
+ return fallback;
93
+ };
94
+ const enabled = pick('enabled', (v) => typeof v === 'boolean', DEFAULTS.enabled);
95
+ const every = pick('every', (v) => typeof v === 'number' && Number.isInteger(v) && v >= 1, DEFAULTS.every);
96
+ const text = pick('text', (v) => typeof v === 'string', DEFAULTS.text);
97
+ // text 合法但空/纯白 → 默认文案(合法配置,不告警,PRD 规则 6)
98
+ const effectiveText = text.trim().length > 0 ? text : DEFAULTS.text;
99
+ return { enabled, every, text: effectiveText };
100
+ }
101
+ function describeSnapshot(snap) {
102
+ return snap === null ? '<corrupt 按关闭>' : JSON.stringify(snap);
103
+ }
104
+ /**
105
+ * 创建配置实例:同步完成首次读取(启动即建快照)与监听挂载;失败不抛——
106
+ * corrupt 态/降级态在返回对象内表达(§6 契约表)。
107
+ */
108
+ export function createTodoNudgeConfig(directory) {
109
+ const allowAlert = createRateLimiter(60_000);
110
+ // 全局层路径在实例创建时冻结:homedir() 读的是进程环境(HOME 可被中途改写——测试 fixture、
111
+ // 宿主环境异常),每次重读现取会把「别的时刻的家目录」读进本实例,跨实例互相污染快照,
112
+ // 违反 per-location 隔离(T202610220003:本文件头注声明各实例独立持有快照与监听)。
113
+ // 冻结后各实例全局层恒定指向创建时刻的家目录;生产 HOME 恒定,行为不变。
114
+ // 已知残留:工作区层遍历(walkTodoConfigPath/isHomeDir 的「遇 $HOME 即止」边界)仍每次现取
115
+ // homedir()——生产 HOME 恒定等价无影响;如需全链冻结,后续把 home 边界作为参数传入。
116
+ const homeDirAtCreate = homedir();
117
+ const globalSimingDir = join(homeDirAtCreate, '.siming');
118
+ const globalConfigFile = join(globalSimingDir, 'config.json');
119
+ let snapshot = null;
120
+ let watchers = [];
121
+ let watchTargetsKey = '';
122
+ let healthy = true;
123
+ let disposed = false;
124
+ let timer = null;
125
+ const changeCallbacks = [];
126
+ let booted = false; // 初读建快照为静默路径(不日志、不通知)——null 初始值不构成「变更」
127
+ const degrade = (reason) => {
128
+ if (!healthy)
129
+ return;
130
+ healthy = false;
131
+ for (const w of watchers) {
132
+ try {
133
+ w.close();
134
+ }
135
+ catch {
136
+ // 关闭失败即弃——watcher 随进程回收
137
+ }
138
+ }
139
+ watchers = [];
140
+ if (timer !== null) {
141
+ clearTimeout(timer);
142
+ timer = null;
143
+ }
144
+ console.error(`[siming-plugin] todoNudge 配置热重载不可用(${reason})——变更需重启宿主生效(当前快照冻结)`);
145
+ };
146
+ /** 防抖:末次事件后 150ms 合并重读(编辑器原子替换连发 rename/change) */
147
+ const scheduleReread = () => {
148
+ if (!healthy || disposed)
149
+ return;
150
+ if (timer !== null)
151
+ clearTimeout(timer);
152
+ timer = setTimeout(() => {
153
+ timer = null;
154
+ if (healthy && !disposed)
155
+ reread();
156
+ }, DEBOUNCE_MS);
157
+ };
158
+ const attach = (dir, onlySiming) => {
159
+ let w;
160
+ try {
161
+ w = watch(dir, { recursive: false }, (_eventType, filename) => {
162
+ // 缺口目录/家目录监听只关心 `.siming` 的出现(否则目录内高频活动会反复触发重读)
163
+ if (onlySiming && filename !== '.siming')
164
+ return;
165
+ scheduleReread();
166
+ });
167
+ }
168
+ catch (err) {
169
+ degrade(String(err));
170
+ return;
171
+ }
172
+ w.on('error', (err) => degrade(String(err)));
173
+ watchers.push(w);
174
+ };
175
+ /** 监听集合随定位结果重建(§4.2:集合①全部 .siming 目录 + 集合②缺口目录 + 全局层) */
176
+ const rebuildWatchers = (simingDirs, gapDir) => {
177
+ if (!healthy || disposed)
178
+ return;
179
+ const targets = [
180
+ ...simingDirs.map((dir) => ({ dir, onlySiming: false })),
181
+ ...(gapDir !== null ? [{ dir: gapDir, onlySiming: true }] : []),
182
+ // 全局层:~/.siming 存在直接监听;不存在监听家目录等 .siming 出现(出现即重读升级)
183
+ existsSync(globalSimingDir)
184
+ ? { dir: globalSimingDir, onlySiming: false }
185
+ : { dir: homeDirAtCreate, onlySiming: true },
186
+ ];
187
+ const key = targets.map((t) => `${t.onlySiming ? '!' : '='}${t.dir}`).sort().join('|');
188
+ if (key === watchTargetsKey)
189
+ return; // 集合未变不重建(避免每次配置编辑重建 watcher)
190
+ for (const w of watchers) {
191
+ try {
192
+ w.close();
193
+ }
194
+ catch {
195
+ // 同上
196
+ }
197
+ }
198
+ watchers = [];
199
+ watchTargetsKey = key;
200
+ for (const t of targets) {
201
+ if (!healthy)
202
+ return; // attach 失败已走 degrade
203
+ attach(t.dir, t.onlySiming);
204
+ }
205
+ // 补读:watcher 建立后 macOS FSEvents 存在激活窗口,窗口内的文件变更不产生事件——
206
+ // 补一次防抖重读兜底(启动/拓扑变化后紧跟的配置编辑不丢;稳态无事件则读后无变化,零副作用)
207
+ scheduleReread();
208
+ };
209
+ const reread = () => {
210
+ // corrupt 判定与字段级合并(读取顺序无关,两层独立)
211
+ const walk = walkTodoConfigPath(directory);
212
+ const wsLayer = walk.hit === null ? { values: null, corrupt: false } : readWorkspaceLayer(walk.hit.path);
213
+ const glLayer = existsSync(globalConfigFile) ? readGlobalLayer(globalConfigFile) : { values: null, corrupt: false };
214
+ let next;
215
+ if (wsLayer.corrupt || glLayer.corrupt) {
216
+ const which = [wsLayer.corrupt ? walk.hit?.path : null, glLayer.corrupt ? globalConfigFile : null]
217
+ .filter((p) => p !== null && p !== undefined)
218
+ .join('、');
219
+ if (allowAlert(`corrupt:${which}`)) {
220
+ console.error(`[siming-plugin] todoNudge 配置文件损坏(${which})——提醒按关闭处理,不回退其他层;修复后自动恢复`);
221
+ }
222
+ next = null;
223
+ }
224
+ else {
225
+ next = mergeLayers(todoNudgeOf(wsLayer.values, walk.hit?.path ?? '<无工作区层>', allowAlert), todoNudgeOf(glLayer.values, globalConfigFile, allowAlert), allowAlert);
226
+ }
227
+ if (!booted) {
228
+ // 初读建快照:静默(此前 null → 首个快照被误判为变更,会在启动时打一行假「配置已更新」并触发空通知)
229
+ snapshot = next;
230
+ booted = true;
231
+ }
232
+ else if (describeSnapshot(next) !== describeSnapshot(snapshot)) {
233
+ // 生效值变化一行日志(前→后,配置变更排障用,非提醒日志)
234
+ console.log(`[siming-plugin] todoNudge 配置已更新:${describeSnapshot(snapshot)} → ${describeSnapshot(next)}`);
235
+ snapshot = next;
236
+ for (const cb of changeCallbacks) {
237
+ try {
238
+ cb(next);
239
+ }
240
+ catch (err) {
241
+ console.error(`[siming-plugin] todoNudge onChange 回调异常(忽略):${String(err)}`);
242
+ }
243
+ }
244
+ }
245
+ rebuildWatchers(walk.simingDirs, walk.gapDir);
246
+ };
247
+ reread(); // 启动首次读取(静默建立初始快照,不触发 onChange)
248
+ return {
249
+ snapshot: () => snapshot,
250
+ healthy: () => healthy,
251
+ onChange(cb) {
252
+ changeCallbacks.push(cb);
253
+ },
254
+ dispose() {
255
+ disposed = true;
256
+ if (timer !== null) {
257
+ clearTimeout(timer);
258
+ timer = null;
259
+ }
260
+ for (const w of watchers) {
261
+ try {
262
+ w.close();
263
+ }
264
+ catch {
265
+ // 同上
266
+ }
267
+ }
268
+ watchers = [];
269
+ },
270
+ };
271
+ }
272
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/todo/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * todo 提醒计数器(技术方案 §4.4):
3
+ * - per-session 自增计数(1-based),命中判定 n % every === 0(由调用方执行);
4
+ * - 跨轮累计不重置;enabled=false 期间调用方不调用 next(关闭不计数);
5
+ * - 重开从 0:配置快照 disabled/corrupt→enabled 跃迁时调用方调 resetAll() 清空全部会话计数;
6
+ * - 防膨胀:新会话键数超 4096(异常量级的长寿进程)整体清空 + 一行日志(计数归零,语义同重启)。
7
+ * Map 实例态随 location 工厂创建,无模块级状态(T202610220003)。
8
+ */
9
+ export interface NudgeCounter {
10
+ next(sessionID: string): number;
11
+ resetAll(): void;
12
+ }
13
+ export declare function createNudgeCounter(): NudgeCounter;
14
+ //# sourceMappingURL=counter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../src/todo/counter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,QAAQ,IAAI,IAAI,CAAC;CAClB;AAID,wBAAgB,kBAAkB,IAAI,YAAY,CAgBjD"}
@@ -0,0 +1,19 @@
1
+ const SESSION_CAP = 4096;
2
+ export function createNudgeCounter() {
3
+ const counts = new Map();
4
+ return {
5
+ next(sessionID) {
6
+ if (!counts.has(sessionID) && counts.size >= SESSION_CAP) {
7
+ counts.clear();
8
+ console.error(`[siming-plugin] todo 提醒计数会话数超 ${String(SESSION_CAP)},计数整体重置(语义同重启)`);
9
+ }
10
+ const n = (counts.get(sessionID) ?? 0) + 1;
11
+ counts.set(sessionID, n);
12
+ return n;
13
+ },
14
+ resetAll() {
15
+ counts.clear();
16
+ },
17
+ };
18
+ }
19
+ //# sourceMappingURL=counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.js","sourceRoot":"","sources":["../../src/todo/counter.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,OAAO;QACL,IAAI,CAAC,SAAiB;YACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;gBACzD,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;YACtF,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC3C,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YACzB,OAAO,CAAC,CAAC;QACX,CAAC;QACD,QAAQ;YACN,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface NudgeLogger {
2
+ hit(sessionID: string, count: number, every: number, messageCount: number): void;
3
+ }
4
+ export declare function createNudgeLogger(referencesRoot: string): NudgeLogger;
5
+ //# sourceMappingURL=nudge-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nudge-log.d.ts","sourceRoot":"","sources":["../../src/todo/nudge-log.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAClF;AAID,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,CA6BrE"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * todo 提醒命中日志(技术方案 §4.5):
3
+ * - 文件 <referencesRoot>/todo-nudge.log,行格式 `<ISO> hit session=<sid> count=<n>/<N> messages=<len>`
4
+ * (PRD 规则 10 四要素:时间/会话标识/累计次数/消息数);
5
+ * - 性质:运行日志而非技能/子代理资产——不属于「资产不落盘」承诺范围,可手删;
6
+ * 技能缓存对账只清理 referencesRoot/skills/ 子目录,不会误删根级日志;
7
+ * - 只在命中时写(未命中/关闭不写);轮转 1MB → .1 保留一代(写入前 statSync 判定);
8
+ * - 写失败(磁盘满/根不可写)console.error 限频(同类 60s 一条),不影响注入。
9
+ * referencesRoot 由 setup 定格传入(referencesRootOf 语义:工作区 .siming/temp/ 或 tmpdir 兜底),
10
+ * 可能尚未被 references 物化创建——写入前防御性 mkdir。
11
+ */
12
+ import { appendFileSync, existsSync, mkdirSync, renameSync, statSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { createRateLimiter } from './rate-limit.js';
15
+ const ROTATE_BYTES = 1_048_576; // 1MB
16
+ export function createNudgeLogger(referencesRoot) {
17
+ const file = join(referencesRoot, 'todo-nudge.log');
18
+ const allowLog = createRateLimiter(60_000);
19
+ return {
20
+ hit(sessionID, count, every, messageCount) {
21
+ try {
22
+ mkdirSync(referencesRoot, { recursive: true });
23
+ // 轮转:超 1MB → .1(rename 覆盖旧 .1,保留一代)
24
+ if (existsSync(file)) {
25
+ try {
26
+ if (statSync(file).size > ROTATE_BYTES)
27
+ renameSync(file, `${file}.1`);
28
+ }
29
+ catch (err) {
30
+ if (allowLog(`rotate:${String(err)}`)) {
31
+ console.error(`[siming-plugin] todo 提醒日志轮转失败(忽略,继续写入):${String(err)}`);
32
+ }
33
+ }
34
+ }
35
+ appendFileSync(file, `${new Date().toISOString()} hit session=${sessionID} count=${String(count)}/${String(every)} messages=${String(messageCount)}\n`, 'utf8');
36
+ }
37
+ catch (err) {
38
+ if (allowLog(String(err))) {
39
+ console.error(`[siming-plugin] todo 提醒日志写入失败(不影响注入):${String(err)}`);
40
+ }
41
+ }
42
+ },
43
+ };
44
+ }
45
+ //# sourceMappingURL=nudge-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nudge-log.js","sourceRoot":"","sources":["../../src/todo/nudge-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMpD,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,MAAM;AAEtC,MAAM,UAAU,iBAAiB,CAAC,cAAsB;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO;QACL,GAAG,CAAC,SAAiB,EAAE,KAAa,EAAE,KAAa,EAAE,YAAoB;YACvE,IAAI,CAAC;gBACH,SAAS,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,oCAAoC;gBACpC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,IAAI,CAAC;wBACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,YAAY;4BAAE,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;oBACxE,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,IAAI,QAAQ,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;4BACtC,OAAO,CAAC,KAAK,CAAC,0CAA0C,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBACzE,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,cAAc,CACZ,IAAI,EACJ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,gBAAgB,SAAS,UAAU,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,aAAa,MAAM,CAAC,YAAY,CAAC,IAAI,EACjI,MAAM,CACP,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBAC1B,OAAO,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * todo 三件套内置常量(技术方案 §4.7):
3
+ * - TODO_SYSTEM_PROMPT:注入主会话 system 的常驻短规则(PRD §8.1 逐字,含标题行);
4
+ * - DEFAULT_NUDGE_TEXT:尾部注入的默认提醒文案(PRD §8.3 逐字,含「非用户输入」标识)。
5
+ * 常量只承载 todo 规则本身,不提及 todoNudge 配置(PRD 规则 14 尾款)。
6
+ */
7
+ export declare const TODO_SYSTEM_PROMPT = "## \u4EFB\u52A1\u6E05\u5355\uFF08todos\uFF09\n\n\u76F8\u5BF9\u590D\u6742\u7684\u4EFB\u52A1\uFF08\u9884\u8BA1 3 \u6B65\u4EE5\u4E0A\u3001\u6D89\u53CA\u591A\u4E2A\u6587\u4EF6\u6216\u547D\u4EE4\u3001\u6709\u5148\u540E\u4F9D\u8D56\uFF09\u5FC5\u987B\u5148\u7528 todowrite \u5EFA\u6E05\u5355\uFF1B\u5355\u6B65\u4EFB\u52A1\u3001\u7EAF\u95EE\u7B54\u4E0D\u5EFA\u3002\n\n- \u62C6\u5206\uFF1A\u600E\u4E48\u62C6\u7531\u4F60\u6309\u4EFB\u52A1\u5B9E\u9645\u5185\u5BB9\u51B3\u5B9A\uFF1B\u4E00\u6761\u5BF9\u5E94\u4E00\u4E2A\u80FD\u72EC\u7ACB\u63A8\u8FDB\u3001\u80FD\u770B\u51FA\u662F\u5426\u5B8C\u6210\u7684\u6B65\u9AA4\u3002\u7C92\u5EA6\u8981\u5408\u9002\uFF0C\u4E0D\u80FD\u7C97\u5230\u300C\u6574\u4E2A\u529F\u80FD\u300D\u6216\u300C\u6574\u4E2A DAG \u8282\u70B9\u300D\u4E00\u6761\u3002\n- siming \u6D41\u7A0B\u4EFB\u52A1\uFF1A\u53EA\u62C6\u5F53\u524D DAG \u8282\u70B9\u5185\u90E8\u7684\u6B65\u9AA4\uFF0C\u6700\u5C11 3 \u9879\u3001\u4E0D\u8BBE\u4E0A\u9650\uFF08\u6309\u8282\u70B9\u5B9E\u9645\u590D\u6742\u5EA6\u8BC4\u4F30\uFF0C\u8BE5\u591A\u5C11\u5C31\u591A\u5C11\uFF09\uFF1B\u4E0D\u8981\u628A\u591A\u4E2A\u8282\u70B9\u5408\u5E76\u8FDB\u540C\u4E00\u5F20\u6E05\u5355\u3002\n- \u72B6\u6001\uFF1A\u5F00\u59CB\u505A\u65F6\u6807 in_progress\uFF0C\u9636\u6BB5\u6027\u5B8C\u6210\u540E\u6807 completed\uFF1B\u66F4\u65B0\u4FDD\u6301\u76F8\u5BF9\u53CA\u65F6\u3002\n- \u63A8\u8FDB\uFF1A\u4E32\u884C\u3001\u5E76\u884C\u3001\u8DF3\u7740\u505A\u90FD\u7531\u4F60\u51B3\u5B9A\uFF0C\u4E0D\u8981\u6C42\u6309\u6E05\u5355\u987A\u5E8F\u63A8\u8FDB\uFF1B\u540C\u65F6\u5728\u8FDB\u884C\u7684\u6761\u76EE\u6570\u91CF\u4E0D\u505A\u9650\u5236\u3002\n- \u7EF4\u62A4\uFF1A\u53D1\u73B0\u65B0\u6B65\u9AA4\u968F\u65F6\u8FFD\u52A0\uFF1B\u653E\u5F03\u7684\u9879\u6807 cancelled \u5E76\u5199\u539F\u56E0\uFF1B\u65E7\u6E05\u5355\u5B8C\u6210\u3001\u5207\u6362\u4EFB\u52A1\u6216\u5207\u6362\u8282\u70B9\u65F6\u6574\u5F20\u66FF\u6362\uFF0C\u4E0D\u590D\u7528\u65E7\u6761\u76EE\u3002\n- \u6536\u5C3E\uFF1A\u7ED9\u6700\u7EC8\u7B54\u590D\u524D\u987A\u624B\u6838\u5BF9\u6E05\u5355\uFF0C\u72B6\u6001\u4E0E\u5B9E\u9645\u8FDB\u5EA6\u4E00\u81F4\u5373\u53EF\uFF0C\u4E0D\u5FC5\u4E3A\u4E86\u6E05\u96F6\u800C\u6539\u72B6\u6001\u3002";
8
+ export declare const DEFAULT_NUDGE_TEXT = "[todo-nudge \u81EA\u52A8\u63D0\u9192\uFF0C\u975E\u7528\u6237\u8F93\u5165] \u590D\u6742\u4EFB\u52A1\u5EFA\u8BAE\u7528 todos \u8DDF\u8E2A\uFF1B\u5B50\u9879\u5B8C\u6210\u540E\u987A\u624B\u66F4\u65B0\u72B6\u6001\u3002";
9
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/todo/prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,kBAAkB,wnEASW,CAAC;AAE3C,eAAO,MAAM,kBAAkB,0NAA0D,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * todo 三件套内置常量(技术方案 §4.7):
3
+ * - TODO_SYSTEM_PROMPT:注入主会话 system 的常驻短规则(PRD §8.1 逐字,含标题行);
4
+ * - DEFAULT_NUDGE_TEXT:尾部注入的默认提醒文案(PRD §8.3 逐字,含「非用户输入」标识)。
5
+ * 常量只承载 todo 规则本身,不提及 todoNudge 配置(PRD 规则 14 尾款)。
6
+ */
7
+ export const TODO_SYSTEM_PROMPT = `## 任务清单(todos)
8
+
9
+ 相对复杂的任务(预计 3 步以上、涉及多个文件或命令、有先后依赖)必须先用 todowrite 建清单;单步任务、纯问答不建。
10
+
11
+ - 拆分:怎么拆由你按任务实际内容决定;一条对应一个能独立推进、能看出是否完成的步骤。粒度要合适,不能粗到「整个功能」或「整个 DAG 节点」一条。
12
+ - siming 流程任务:只拆当前 DAG 节点内部的步骤,最少 3 项、不设上限(按节点实际复杂度评估,该多少就多少);不要把多个节点合并进同一张清单。
13
+ - 状态:开始做时标 in_progress,阶段性完成后标 completed;更新保持相对及时。
14
+ - 推进:串行、并行、跳着做都由你决定,不要求按清单顺序推进;同时在进行的条目数量不做限制。
15
+ - 维护:发现新步骤随时追加;放弃的项标 cancelled 并写原因;旧清单完成、切换任务或切换节点时整张替换,不复用旧条目。
16
+ - 收尾:给最终答复前顺手核对清单,状态与实际进度一致即可,不必为了清零而改状态。`;
17
+ export const DEFAULT_NUDGE_TEXT = '[todo-nudge 自动提醒,非用户输入] 复杂任务建议用 todos 跟踪;子项完成后顺手更新状态。';
18
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/todo/prompt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;0CASQ,CAAC;AAE3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,uDAAuD,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 同类事件限频闸(todo 套件内部共用):
3
+ * 配置告警、handler 异常、日志写失败均要求「同类 60s 一条」(技术方案 §4.1/§4.3/§4.5),
4
+ * 避免每请求级错误刷屏宿主日志。返回 true = 本次应输出。
5
+ */
6
+ export declare function createRateLimiter(intervalMs: number): (key: string) => boolean;
7
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/todo/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAW9E"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 同类事件限频闸(todo 套件内部共用):
3
+ * 配置告警、handler 异常、日志写失败均要求「同类 60s 一条」(技术方案 §4.1/§4.3/§4.5),
4
+ * 避免每请求级错误刷屏宿主日志。返回 true = 本次应输出。
5
+ */
6
+ export function createRateLimiter(intervalMs) {
7
+ const lastAt = new Map();
8
+ return (key) => {
9
+ const now = Date.now();
10
+ const prev = lastAt.get(key);
11
+ if (prev !== undefined && now - prev < intervalMs)
12
+ return false;
13
+ // 防膨胀:键数异常量级时整体重置(限频窗口短,重置代价可忽略)
14
+ if (lastAt.size >= 256)
15
+ lastAt.clear();
16
+ lastAt.set(key, now);
17
+ return true;
18
+ };
19
+ }
20
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/todo/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,OAAO,CAAC,GAAW,EAAW,EAAE;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,GAAG,IAAI,GAAG,UAAU;YAAE,OAAO,KAAK,CAAC;QAChE,iCAAiC;QACjC,IAAI,MAAM,CAAC,IAAI,IAAI,GAAG;YAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `task-planning` 技能内置常量(技术方案 §4.7,PRD §8.2 逐字):
3
+ * 形态镜像 guide-skill.ts——path 为合成展示路径(宿主消费内嵌 content,不落盘读取),
4
+ * 与 references 物化根同源。随插件版本发布,不做运行时拉取。
5
+ */
6
+ import type { HostSkillInfo } from '../host-api.js';
7
+ export declare const TASK_PLANNING_SKILL_NAME = "task-planning";
8
+ export declare function taskPlanningSkillInfo(referencesRoot: string): HostSkillInfo;
9
+ //# sourceMappingURL=task-planning-skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-planning-skill.d.ts","sourceRoot":"","sources":["../../src/todo/task-planning-skill.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAExD,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAS3E"}
@@ -0,0 +1,58 @@
1
+ import { skillDisplayPath } from '../host-api.js';
2
+ export const TASK_PLANNING_SKILL_NAME = 'task-planning';
3
+ export function taskPlanningSkillInfo(referencesRoot) {
4
+ return {
5
+ id: TASK_PLANNING_SKILL_NAME,
6
+ name: TASK_PLANNING_SKILL_NAME,
7
+ description: '多步骤任务开始前建立并维护 todo 清单;执行中相对及时地更新状态,任务边界变化时整张替换。用于 3 步以上、多文件多命令、有先后依赖或用户要求先规划再执行的任务。',
8
+ path: skillDisplayPath(referencesRoot, TASK_PLANNING_SKILL_NAME),
9
+ content: TASK_PLANNING_CONTENT,
10
+ };
11
+ }
12
+ const TASK_PLANNING_CONTENT = `# task-planning
13
+
14
+ ## 何时使用
15
+
16
+ - 任务预计 3 步以上,或涉及多个文件、多个命令、有先后依赖;
17
+ - 用户要求「先规划再执行」;
18
+ - 长任务执行中需要让进度对用户可见。
19
+
20
+ 单步任务、纯问答不建清单。
21
+
22
+ ## 建清单
23
+
24
+ 1. 先拆步骤再动手:每条写清要做什么、做到什么程度,便于判断是否完成。
25
+ 2. 怎么拆由你决定:一条对应一个能独立推进的步骤;粒度要合适,不要粗到「整个功能」或「整个 DAG 节点」一条。
26
+ 3. siming 流程任务:只拆**当前 DAG 节点内部**的步骤,最少 3 项、不设上限,由你按节点实际复杂度评估。
27
+ - 不要把整个节点写成一条(如「完成技术方案」);
28
+ - 不要把多个节点合并进同一张清单;
29
+ - 节点切换时整张替换清单,旧节点条目退场。
30
+ 4. 首次用 todowrite 提交完整清单;后续随进展更新即可。
31
+
32
+ ## 维护节奏
33
+
34
+ - 开始做时标 in_progress,阶段性完成后标 completed;更新保持相对及时。
35
+ - 推进顺序(串行、并行、跳着做)由你决定;同时在进行的条目数量不做限制。
36
+ - 执行中发现新步骤 → 追加;原步骤不再需要 → 标 cancelled 并写原因。
37
+ - 已完成项可以保留作为进度记录,不必为了整洁删除。
38
+
39
+ ## 边界与切换
40
+
41
+ - 旧任务结束或节点切换 → 整张替换,不复用旧条目。
42
+ - 同一会话开始新任务 → 新建清单。
43
+ - 子代理有自己的清单,不继承主会话清单。
44
+
45
+ ## 收尾检查
46
+
47
+ 给最终答复前顺手核对清单:
48
+
49
+ - 状态与实际进度一致即可;没做完的项标 cancelled 或说明后续处理;
50
+ - 未验证的步骤不要标 completed;
51
+ - 不需要为了「清零」而改状态。
52
+
53
+ ## 常见问题
54
+
55
+ - 只在开头建一次清单,之后不再更新;
56
+ - 整个功能或整个节点一条 todo(粒度太粗,看不出进度);
57
+ - 旧清单完成后不复位,新任务沿用旧列表。`;
58
+ //# sourceMappingURL=task-planning-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-planning-skill.js","sourceRoot":"","sources":["../../src/todo/task-planning-skill.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AAExD,MAAM,UAAU,qBAAqB,CAAC,cAAsB;IAC1D,OAAO;QACL,EAAE,EAAE,wBAAwB;QAC5B,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACT,qFAAqF;QACvF,IAAI,EAAE,gBAAgB,CAAC,cAAc,EAAE,wBAAwB,CAAC;QAChE,OAAO,EAAE,qBAAqB;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBA6CR,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siming-org/opencode-plugin",
3
- "version": "0.1.13",
3
+ "version": "0.1.16",
4
4
  "description": "opencode v2 plugin for siming: registers MCP task tools (siming_*), onboarding guide skill, siming_cli/siming_refresh tools, and global+project skills/subagents pulled live from the siming server",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "jsonc-parser": "^3.3.1",
17
17
  "zod": "^4.4.3",
18
- "@siming-org/cli": "0.8.15"
18
+ "@siming-org/cli": "0.8.17"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@opencode-ai/plugin": "^1.18.35",