@shgroup/opencode-serenity-plugin 0.2.0

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 (151) hide show
  1. package/README.md +199 -0
  2. package/bin/opencode-serenity-plugin.js +316 -0
  3. package/dist/activation.d.ts +40 -0
  4. package/dist/activation.d.ts.map +1 -0
  5. package/dist/activation.js +133 -0
  6. package/dist/activation.js.map +1 -0
  7. package/dist/bash-override.d.ts +10 -0
  8. package/dist/bash-override.d.ts.map +1 -0
  9. package/dist/bash-override.js +24 -0
  10. package/dist/bash-override.js.map +1 -0
  11. package/dist/bash-toggle.d.ts +17 -0
  12. package/dist/bash-toggle.d.ts.map +1 -0
  13. package/dist/bash-toggle.js +42 -0
  14. package/dist/bash-toggle.js.map +1 -0
  15. package/dist/config-schema.d.ts +300 -0
  16. package/dist/config-schema.d.ts.map +1 -0
  17. package/dist/config-schema.js +185 -0
  18. package/dist/config-schema.js.map +1 -0
  19. package/dist/errors.d.ts +90 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +151 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/fs/file-system-tool.d.ts +25 -0
  24. package/dist/fs/file-system-tool.d.ts.map +1 -0
  25. package/dist/fs/file-system-tool.js +318 -0
  26. package/dist/fs/file-system-tool.js.map +1 -0
  27. package/dist/fs/resolve-path.d.ts +53 -0
  28. package/dist/fs/resolve-path.d.ts.map +1 -0
  29. package/dist/fs/resolve-path.js +100 -0
  30. package/dist/fs/resolve-path.js.map +1 -0
  31. package/dist/hooks/compacting.d.ts +23 -0
  32. package/dist/hooks/compacting.d.ts.map +1 -0
  33. package/dist/hooks/compacting.js +90 -0
  34. package/dist/hooks/compacting.js.map +1 -0
  35. package/dist/hooks/permission-auto-reply.d.ts +91 -0
  36. package/dist/hooks/permission-auto-reply.d.ts.map +1 -0
  37. package/dist/hooks/permission-auto-reply.js +158 -0
  38. package/dist/hooks/permission-auto-reply.js.map +1 -0
  39. package/dist/hooks/permission-guards.d.ts +41 -0
  40. package/dist/hooks/permission-guards.d.ts.map +1 -0
  41. package/dist/hooks/permission-guards.js +153 -0
  42. package/dist/hooks/permission-guards.js.map +1 -0
  43. package/dist/hooks/shell-env.d.ts +20 -0
  44. package/dist/hooks/shell-env.d.ts.map +1 -0
  45. package/dist/hooks/shell-env.js +38 -0
  46. package/dist/hooks/shell-env.js.map +1 -0
  47. package/dist/hooks/util.d.ts +81 -0
  48. package/dist/hooks/util.d.ts.map +1 -0
  49. package/dist/hooks/util.js +172 -0
  50. package/dist/hooks/util.js.map +1 -0
  51. package/dist/index.d.ts +26 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +63 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/init/init-wizard.d.ts +39 -0
  56. package/dist/init/init-wizard.d.ts.map +1 -0
  57. package/dist/init/init-wizard.js +297 -0
  58. package/dist/init/init-wizard.js.map +1 -0
  59. package/dist/install.d.ts +117 -0
  60. package/dist/install.d.ts.map +1 -0
  61. package/dist/install.js +255 -0
  62. package/dist/install.js.map +1 -0
  63. package/dist/msm-schema.d.ts +76 -0
  64. package/dist/msm-schema.d.ts.map +1 -0
  65. package/dist/msm-schema.js +207 -0
  66. package/dist/msm-schema.js.map +1 -0
  67. package/dist/msm.d.ts +25 -0
  68. package/dist/msm.d.ts.map +1 -0
  69. package/dist/msm.js +317 -0
  70. package/dist/msm.js.map +1 -0
  71. package/dist/session/lib.d.ts +33 -0
  72. package/dist/session/lib.d.ts.map +1 -0
  73. package/dist/session/lib.js +475 -0
  74. package/dist/session/lib.js.map +1 -0
  75. package/dist/session/session-tool.d.ts +17 -0
  76. package/dist/session/session-tool.d.ts.map +1 -0
  77. package/dist/session/session-tool.js +109 -0
  78. package/dist/session/session-tool.js.map +1 -0
  79. package/dist/skills/install-skill.d.ts +36 -0
  80. package/dist/skills/install-skill.d.ts.map +1 -0
  81. package/dist/skills/install-skill.js +91 -0
  82. package/dist/skills/install-skill.js.map +1 -0
  83. package/dist/skills/template-loader.d.ts +79 -0
  84. package/dist/skills/template-loader.d.ts.map +1 -0
  85. package/dist/skills/template-loader.js +170 -0
  86. package/dist/skills/template-loader.js.map +1 -0
  87. package/dist/state.d.ts +35 -0
  88. package/dist/state.d.ts.map +1 -0
  89. package/dist/state.js +62 -0
  90. package/dist/state.js.map +1 -0
  91. package/dist/tui.d.ts +61 -0
  92. package/dist/tui.d.ts.map +1 -0
  93. package/dist/tui.js +279 -0
  94. package/dist/tui.js.map +1 -0
  95. package/dist/types/index.d.ts +30 -0
  96. package/dist/types/index.d.ts.map +1 -0
  97. package/dist/types/index.js +17 -0
  98. package/dist/types/index.js.map +1 -0
  99. package/dist/util/config-patch.d.ts +58 -0
  100. package/dist/util/config-patch.d.ts.map +1 -0
  101. package/dist/util/config-patch.js +117 -0
  102. package/dist/util/config-patch.js.map +1 -0
  103. package/dist/util/git.d.ts +29 -0
  104. package/dist/util/git.d.ts.map +1 -0
  105. package/dist/util/git.js +74 -0
  106. package/dist/util/git.js.map +1 -0
  107. package/dist/util/init-check.d.ts +22 -0
  108. package/dist/util/init-check.d.ts.map +1 -0
  109. package/dist/util/init-check.js +76 -0
  110. package/dist/util/init-check.js.map +1 -0
  111. package/dist/util/init.d.ts +54 -0
  112. package/dist/util/init.d.ts.map +1 -0
  113. package/dist/util/init.js +87 -0
  114. package/dist/util/init.js.map +1 -0
  115. package/dist/util/log.d.ts +25 -0
  116. package/dist/util/log.d.ts.map +1 -0
  117. package/dist/util/log.js +28 -0
  118. package/dist/util/log.js.map +1 -0
  119. package/dist/util/msm-call.d.ts +48 -0
  120. package/dist/util/msm-call.d.ts.map +1 -0
  121. package/dist/util/msm-call.js +86 -0
  122. package/dist/util/msm-call.js.map +1 -0
  123. package/dist/util/msm-exec-runtime.d.ts +123 -0
  124. package/dist/util/msm-exec-runtime.d.ts.map +1 -0
  125. package/dist/util/msm-exec-runtime.js +532 -0
  126. package/dist/util/msm-exec-runtime.js.map +1 -0
  127. package/dist/util/path.d.ts +10 -0
  128. package/dist/util/path.d.ts.map +1 -0
  129. package/dist/util/path.js +21 -0
  130. package/dist/util/path.js.map +1 -0
  131. package/dist/util/ready-state.d.ts +43 -0
  132. package/dist/util/ready-state.d.ts.map +1 -0
  133. package/dist/util/ready-state.js +104 -0
  134. package/dist/util/ready-state.js.map +1 -0
  135. package/dist/util/serenity-file.d.ts +30 -0
  136. package/dist/util/serenity-file.d.ts.map +1 -0
  137. package/dist/util/serenity-file.js +69 -0
  138. package/dist/util/serenity-file.js.map +1 -0
  139. package/dist/util/tui-install.d.ts +61 -0
  140. package/dist/util/tui-install.d.ts.map +1 -0
  141. package/dist/util/tui-install.js +94 -0
  142. package/dist/util/tui-install.js.map +1 -0
  143. package/docs/architecture-v0.md +294 -0
  144. package/docs/contract-v0.md +417 -0
  145. package/docs/plugin-self-contained-msm-v1.md +182 -0
  146. package/docs/refactor-direction-v1.11.md +78 -0
  147. package/docs/requirements-v0-scope.md +104 -0
  148. package/docs/requirements-v0-summary.md +108 -0
  149. package/docs/rr7-init-design.md +304 -0
  150. package/docs/v0.1-candidates.md +132 -0
  151. package/package.json +54 -0
package/dist/tui.js ADDED
@@ -0,0 +1,279 @@
1
+ /**
2
+ * opencode-serenity-plugin TUI entry(v1.9 → ... → v1.15 → v0.1 D6)
3
+ *
4
+ * 独立 TUI plugin(与 server plugin 平级)。opencode 1.16+ 强制 PluginModule
5
+ * 二选一(server | tui),所以走两条独立 entry:
6
+ * - server entry: dist/index.js(走 Hooks 系统)
7
+ * - tui entry: dist/tui.js (走 TuiPluginApi)
8
+ *
9
+ * v0.1 D6:
10
+ * - 启动时检测当前目录的宁静号状态,显示在 toast 上
11
+ * - 三种状态:Activated / Not Activated / Error
12
+ * - 版本号始终显示
13
+ *
14
+ * v1.9 修复:
15
+ * - R-α fix: TUI entry 不放 opencode.json;放到主仓 tui.json#plugin
16
+ * - R-β fix: default export 改为 { id, tui } 对象形式(之前是裸函数)
17
+ * - R-γ fix: 路径 plugin 显式 export id
18
+ *
19
+ * v1.9.1 调整(slot 暂未实现):
20
+ * - 移除 JSX slot。@opentui/solid 的 JSX runtime 只支持 build-time transform
21
+ * (bun-plugin / babel-preset-solid),运行时 import 必然 throw。
22
+ * 我们用 tsc 编译没有 bun-plugin,所以 slot 加载会炸掉整个 plugin。
23
+ * - 保留 toast(不依赖 JSX),用户至少看到 "plugin activated" 通知。
24
+ * - 永久 slot 状态指示器待 v1.10 — 需要切到 bun build + bun-plugin-solid
25
+ * 或者重写为 createElement/spread 直调。
26
+ *
27
+ * v1.10 RR7 init:
28
+ * - 注册 /serenity-init slash command
29
+ * - onSelect(dialog) → DialogPrompt → initSerenity
30
+ * - 失败用 toast 通知(不抛错给 TUI)
31
+ * - 成功提示"请重启 opencode"(不做 live re-activation)
32
+ *
33
+ * v1.10.1 修复(/serenity-init 在非 serenity 目录不可见的 bug):
34
+ * - 根因:plugin path 只登记在项目 tui.json;非 serenity 目录 walk-up
35
+ * 找不到 tui.json → plugin 不加载 → Tui(api) 永不调 → slash 不出现
36
+ * 详见 AGENT_SESSIONS/2026-06-06--S020--fix-serenity-init-visibility
37
+ * - 修复:B 段自安装到 global tui.json($XDG_CONFIG_HOME/opencode/tui.json
38
+ * 或 ~/.config/opencode/tui.json),让 opencode 在**任何**目录启动都加载
39
+ * plugin。slash command 注册不受 self-install 成功与否影响
40
+ * (try/catch 包住,失败仅 log.warn)
41
+ * - 一次性行为:self-install 幂等,no-op 当 plugin path 已存在
42
+ *
43
+ * v1.15 版本号可见性:
44
+ * - 每次 plugin 加载在 toast 里显示 `opencode-serenity-plugin v${VERSION}`,
45
+ * 用户重启 opencode 时即可确认实际加载的版本(避免 dev 缓存/旧 dist)。
46
+ * - VERSION 动态从 package.json 读(`import pkg ... with { type: 'json' }`),
47
+ * release 时改 package.json#version 即可,无需同步本文件。
48
+ * - "loaded" toast 放在 self-install 之前,无论 self-install 是否成功都
49
+ * 能看到版本号。
50
+ *
51
+ * 与 server plugin 协同:
52
+ * - server plugin 负责"拦截 + 行为"(RR1-RR7 + permission auto-reply + config-patch)
53
+ * - tui plugin 负责"通知用户"(激活提示 + 自安装 + RR7 初始化入口)
54
+ */
55
+ import { basename } from 'node:path';
56
+ import { fileURLToPath } from 'node:url';
57
+ import { defaultPrefix, initSerenity, isValidPrefix, } from './util/init.js';
58
+ import { ensureGlobalTuiPluginRegistration } from './util/tui-install.js';
59
+ import { InvalidInstanceNameError, NotInGitRepoError, InitGitCommitError, } from './errors.js';
60
+ import { log } from './util/log.js';
61
+ import { findSerenityRootSafe, readSerenityInstanceName } from './fs/resolve-path.js';
62
+ import { setBashDisabled, isBashDisabled } from './bash-toggle.js';
63
+ import pkg from '../package.json' with { type: 'json' };
64
+ const VERSION = pkg.version;
65
+ const Tui = async (api) => {
66
+ // v1.15 — 每次加载都显示版本号
67
+ api.ui.toast({
68
+ title: `opencode-serenity-plugin v${VERSION}`,
69
+ message: 'loaded',
70
+ variant: 'success',
71
+ duration: 3000,
72
+ });
73
+ // v0.1 D6 — 检测当前目录的宁静号状态
74
+ const cwd = api.state.path.directory;
75
+ let serenityStatus;
76
+ let serenityVariant;
77
+ let serenityInstance = null;
78
+ const root = findSerenityRootSafe(cwd);
79
+ if (root) {
80
+ const name = readSerenityInstanceName(root);
81
+ serenityInstance = name;
82
+ // 验证 SKILL.md 存在(RR2 同级检测)
83
+ const { existsSync } = await import('node:fs');
84
+ const { resolve } = await import('node:path');
85
+ const skillPath = name
86
+ ? resolve(root, '.opencode', 'skills', name, 'SKILL.md')
87
+ : null;
88
+ if (skillPath && existsSync(skillPath)) {
89
+ serenityStatus = `✓ Serenity Activated${name ? ` (${name})` : ''}`;
90
+ serenityVariant = 'success';
91
+ }
92
+ else {
93
+ serenityStatus = `⚠ Serenity Error${name ? ` (${name})` : ''}`;
94
+ serenityVariant = 'error';
95
+ }
96
+ }
97
+ else {
98
+ serenityStatus = '○ Serenity Not Activated';
99
+ serenityVariant = 'info';
100
+ }
101
+ api.ui.toast({
102
+ title: `serenity v${VERSION}`,
103
+ message: serenityInstance
104
+ ? `${serenityStatus} — instance: ${serenityInstance}`
105
+ : serenityStatus,
106
+ variant: serenityVariant,
107
+ duration: 5000,
108
+ });
109
+ // A: v1.1 — self-install to global tui.json
110
+ // 让 plugin 在非 serenity 目录也被加载,从而 /serenity-init 全局可见
111
+ // 失败不抛(仅 log),slash command 仍注册
112
+ try {
113
+ const pluginFile = fileURLToPath(import.meta.url);
114
+ const result = ensureGlobalTuiPluginRegistration(pluginFile);
115
+ if (result.changed) {
116
+ api.ui.toast({
117
+ title: 'serenity',
118
+ message: `opencode-serenity-plugin v${VERSION} installed; restart opencode to ` +
119
+ `enable /serenity-init in non-serenity directories`,
120
+ variant: 'info',
121
+ duration: 8000,
122
+ });
123
+ }
124
+ else if (result.error) {
125
+ log.warn('tui-install', 'self-install failed; /serenity-init visible only in this project', {
126
+ error: result.error,
127
+ configPath: result.configPath,
128
+ });
129
+ }
130
+ }
131
+ catch (err) {
132
+ const reason = err instanceof Error ? err.message : String(err);
133
+ log.warn('tui-install', 'unexpected error during self-install (slash command still registered)', {
134
+ error: reason,
135
+ });
136
+ }
137
+ // C: v1.10 RR7 — /serenity-init slash command
138
+ // 走 api.command.register(v1 SDK 的 legacy slash 入口,TS 类型仍支持)
139
+ // 返回值是 disposer(不存,plugin 卸载时 TUI 框架负责清理)
140
+ api.command?.register(() => [
141
+ {
142
+ title: 'serenity: init cwd',
143
+ value: 'serenity-init',
144
+ description: 'Create /.serenity and git-commit (requires restart)',
145
+ slash: { name: 'serenity-init' },
146
+ onSelect: (dialog) => {
147
+ if (!dialog) {
148
+ api.ui.toast({
149
+ title: 'Error',
150
+ message: 'dialog unavailable; cannot open serenity init prompt',
151
+ variant: 'error',
152
+ duration: 5000,
153
+ });
154
+ return;
155
+ }
156
+ const cwd = api.state.path.directory;
157
+ const prefill = defaultPrefix(basename(cwd));
158
+ dialog.replace(() => api.ui.DialogPrompt({
159
+ title: 'Initialize serenity',
160
+ placeholder: 'kebab-case prefix (e.g. xx, tg)',
161
+ value: prefill,
162
+ onConfirm: async (value) => {
163
+ const prefix = value.trim();
164
+ if (!isValidPrefix(prefix)) {
165
+ api.ui.toast({
166
+ title: 'Error',
167
+ message: `Invalid prefix "${prefix}"; ` +
168
+ `must be kebab-case (lowercase a-z, 0-9, dashes; no leading or trailing dash)`,
169
+ variant: 'error',
170
+ duration: 5000,
171
+ });
172
+ return; // dialog 保持开启,让用户改完重试
173
+ }
174
+ try {
175
+ const result = await initSerenity(cwd, prefix);
176
+ dialog.clear();
177
+ if (result.kind === 'created') {
178
+ api.ui.toast({
179
+ title: 'serenity',
180
+ message: `Initialized ${result.name}; please restart opencode`,
181
+ variant: 'success',
182
+ duration: 5000,
183
+ });
184
+ }
185
+ else {
186
+ api.ui.toast({
187
+ title: 'serenity',
188
+ message: `Already initialized as ${result.name}`,
189
+ variant: 'info',
190
+ duration: 5000,
191
+ });
192
+ }
193
+ }
194
+ catch (err) {
195
+ dialog.clear();
196
+ let msg;
197
+ if (err instanceof NotInGitRepoError) {
198
+ msg = 'cwd is not a git repository; run `git init` first, then `/serenity-init` again';
199
+ }
200
+ else if (err instanceof InitGitCommitError) {
201
+ msg = `git add+commit failed (rolled back): ${err.message}`;
202
+ }
203
+ else if (err instanceof InvalidInstanceNameError) {
204
+ msg = err.message;
205
+ }
206
+ else {
207
+ msg = err instanceof Error ? err.message : String(err);
208
+ }
209
+ api.ui.toast({ title: 'Error', message: msg, variant: 'error', duration: 5000 });
210
+ log.warn('serenity-init', 'init failed', { err: msg });
211
+ }
212
+ },
213
+ onCancel: () => {
214
+ dialog.clear();
215
+ api.ui.toast({
216
+ title: 'serenity',
217
+ message: 'Cancelled',
218
+ variant: 'info',
219
+ duration: 5000,
220
+ });
221
+ },
222
+ }));
223
+ },
224
+ },
225
+ // D: /serenity-bash-on — 启用 bash
226
+ {
227
+ title: 'serenity: enable bash',
228
+ value: 'serenity-bash-on',
229
+ description: 'Enable bash tool (default)',
230
+ slash: { name: 'serenity-bash-on' },
231
+ onSelect: () => {
232
+ setBashDisabled(false);
233
+ api.ui.toast({
234
+ title: 'Bash',
235
+ message: 'bash is now enabled',
236
+ variant: 'success',
237
+ duration: 3000,
238
+ });
239
+ },
240
+ },
241
+ // D: /serenity-bash-off — 禁用 bash(静默拒绝)
242
+ {
243
+ title: 'serenity: disable bash',
244
+ value: 'serenity-bash-off',
245
+ description: 'Disable bash tool (silent reject via plugin hook)',
246
+ slash: { name: 'serenity-bash-off' },
247
+ onSelect: () => {
248
+ setBashDisabled(true);
249
+ api.ui.toast({
250
+ title: 'Bash',
251
+ message: 'bash is now disabled (silent reject)',
252
+ variant: 'warning',
253
+ duration: 3000,
254
+ });
255
+ },
256
+ },
257
+ // D: /serenity-bash-status — 查看当前状态
258
+ {
259
+ title: 'serenity: bash status',
260
+ value: 'serenity-bash-status',
261
+ description: 'Show current bash toggle status',
262
+ slash: { name: 'serenity-bash-status' },
263
+ onSelect: () => {
264
+ const disabled = isBashDisabled();
265
+ api.ui.toast({
266
+ title: 'Bash Status',
267
+ message: disabled ? 'bash is DISABLED (silent reject)' : 'bash is enabled',
268
+ variant: disabled ? 'warning' : 'success',
269
+ duration: 5000,
270
+ });
271
+ },
272
+ },
273
+ ]);
274
+ };
275
+ export default {
276
+ id: 'opencode-serenity-plugin-tui',
277
+ tui: Tui,
278
+ };
279
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 内部类型定义 — plugin 内部状态 + 内部接口
3
+ *
4
+ * 与外部契约(contract-v0.md)区别:
5
+ * - 外部契约 = plugin 暴露给 opencode runtime + LLM 的接口
6
+ * - 内部类型 = plugin 内部模块间共享的状态 / 函数签名
7
+ */
8
+ import type { PluginInput } from '@opencode-ai/plugin';
9
+ /** plugin 激活后维护的运行时状态(仅在 plugin 内部使用) */
10
+ export type SerenityState = {
11
+ /** 是否激活(RR1+RR6 任一不满足 = false)*/
12
+ activated: boolean;
13
+ /** cwd 根(git root),plugin 一切判断基于此 */
14
+ cwdRoot: string;
15
+ /** 实例名(从 /.serenity 文件内容读取)*/
16
+ instanceName: string;
17
+ /** SKILL.md 绝对路径(.opencode/skills/<instanceName>/SKILL.md)*/
18
+ skillPath: string;
19
+ /** SKILL.md 全文(phase2 读取,用于 system.transform 注入到 system prompt)*/
20
+ skillContent: string | null;
21
+ /** 激活失败原因(仅在 activated=false 时有意义)*/
22
+ failureReason?: string;
23
+ };
24
+ /** plugin 不激活时的状态工厂 */
25
+ export declare const INACTIVE_STATE: Readonly<SerenityState>;
26
+ /** PluginInput 的简化别名(plugin 入口签名用) */
27
+ export type SerenityPluginInput = PluginInput;
28
+ /** 错误恢复策略(每个 hook 内部决定如何处理抛错) */
29
+ export type ErrorRecovery = 'silent' | 'log' | 'throw';
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,yCAAyC;AACzC,MAAM,MAAM,aAAa,GAAG;IAC1B,iCAAiC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,uBAAuB;AACvB,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,aAAa,CAOjD,CAAC;AAEH,sCAAsC;AACtC,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE9C,iCAAiC;AACjC,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 内部类型定义 — plugin 内部状态 + 内部接口
3
+ *
4
+ * 与外部契约(contract-v0.md)区别:
5
+ * - 外部契约 = plugin 暴露给 opencode runtime + LLM 的接口
6
+ * - 内部类型 = plugin 内部模块间共享的状态 / 函数签名
7
+ */
8
+ /** plugin 不激活时的状态工厂 */
9
+ export const INACTIVE_STATE = Object.freeze({
10
+ activated: false,
11
+ cwdRoot: '',
12
+ instanceName: '',
13
+ skillPath: '',
14
+ skillContent: null,
15
+ failureReason: 'plugin not activated',
16
+ });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH,uBAAuB;AACvB,MAAM,CAAC,MAAM,cAAc,GAA4B,MAAM,CAAC,MAAM,CAAC;IACnE,SAAS,EAAE,KAAK;IAChB,OAAO,EAAE,EAAE;IACX,YAAY,EAAE,EAAE;IAChB,SAAS,EAAE,EAAE;IACb,YAAY,EAAE,IAAI;IAClB,aAAa,EAAE,sBAAsB;CACtC,CAAC,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * opencode.json 自动 patch 模块(v1.7)
3
+ *
4
+ * 目标:plugin 启动时自动修正主仓 opencode.json(让 cwdRoot 内 read/edit = allow)。
5
+ * 与 v1.5 init-check 的关系:
6
+ * - v1.5 只 warn 不 patch
7
+ * - v1.7 **自动 patch**(用户 m0649 决定"全自动")
8
+ *
9
+ * 行为:
10
+ * - 幂等(已 "allow" 跳过)
11
+ * - 缺 permission 字段自动补
12
+ * - 自动 `git add + commit`
13
+ * - 失败 log.warn(不阻断 plugin 启动)
14
+ * - 改完调 TUI toast 通知用户
15
+ */
16
+ export type PatchField = 'read' | 'edit';
17
+ export type PatchResult = {
18
+ changed: boolean;
19
+ diff: Array<{
20
+ path: string;
21
+ from: unknown;
22
+ to: unknown;
23
+ }>;
24
+ configPath: string;
25
+ error?: string;
26
+ };
27
+ /**
28
+ * TUI toast 客户端最小接口 (v1.18 抽出)
29
+ *
30
+ * 设计:保持 config-patch 与具体 opencode 客户端类型解耦。
31
+ * activation.ts 通过 `as ToastClient | null` 适配(避免反推 v1/v2 客户端类型)。
32
+ */
33
+ export type ToastOpts = {
34
+ title?: string;
35
+ message: string;
36
+ variant?: 'info' | 'success' | 'warning' | 'error';
37
+ duration?: number;
38
+ };
39
+ export type ToastClient = {
40
+ tui?: {
41
+ showToast: (opts: {
42
+ body: ToastOpts;
43
+ }) => Promise<unknown>;
44
+ };
45
+ };
46
+ /** 工厂签名:返回 toast 客户端或 null */
47
+ export type GetToastClient = () => ToastClient | null;
48
+ /**
49
+ * Patch 主仓 opencode.json(idempotent + 自动 commit)
50
+ *
51
+ * 目标:permission.read 和 permission.edit 设为 "allow"
52
+ * 已为 "allow" 跳过;缺 permission 字段自动建
53
+ *
54
+ * @param cwdRoot 主仓根路径
55
+ * @param getClient SDK client 工厂(用于 tui toast 通知)—— 可选
56
+ */
57
+ export declare function patchMainRepoOpencodeJson(cwdRoot: string, getClient?: GetToastClient): Promise<PatchResult>;
58
+ //# sourceMappingURL=config-patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-patch.d.ts","sourceRoot":"","sources":["../../src/util/config-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAKF;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,CAAC,EAAE;QACJ,SAAS,EAAE,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,SAAS,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC5D,CAAC;CACH,CAAC;AAEF,8BAA8B;AAC9B,MAAM,MAAM,cAAc,GAAG,MAAM,WAAW,GAAG,IAAI,CAAC;AA2BtD;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,WAAW,CAAC,CAkEtB"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * opencode.json 自动 patch 模块(v1.7)
3
+ *
4
+ * 目标:plugin 启动时自动修正主仓 opencode.json(让 cwdRoot 内 read/edit = allow)。
5
+ * 与 v1.5 init-check 的关系:
6
+ * - v1.5 只 warn 不 patch
7
+ * - v1.7 **自动 patch**(用户 m0649 决定"全自动")
8
+ *
9
+ * 行为:
10
+ * - 幂等(已 "allow" 跳过)
11
+ * - 缺 permission 字段自动补
12
+ * - 自动 `git add + commit`
13
+ * - 失败 log.warn(不阻断 plugin 启动)
14
+ * - 改完调 TUI toast 通知用户
15
+ */
16
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+ import { log } from './log.js';
19
+ import { gitAddAndCommit } from './git.js';
20
+ const TOAST_TITLE = 'serenity plugin';
21
+ const TOAST_DURATION_MS = 8000;
22
+ /**
23
+ * 主仓 opencode.json 读 + 解析
24
+ */
25
+ function readMainConfig(cwdRoot) {
26
+ const configPath = join(cwdRoot, 'opencode.json');
27
+ if (!existsSync(configPath)) {
28
+ return null;
29
+ }
30
+ try {
31
+ const raw = readFileSync(configPath, 'utf8');
32
+ return { config: JSON.parse(raw), path: configPath };
33
+ }
34
+ catch (err) {
35
+ const reason = err instanceof Error ? err.message : String(err);
36
+ log.warn('config-patch', 'opencode.json parse error', { path: configPath, err: reason });
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * 主仓 opencode.json 写(格式化 2-space)
42
+ */
43
+ function writeMainConfig(configPath, config) {
44
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
45
+ }
46
+ /**
47
+ * Patch 主仓 opencode.json(idempotent + 自动 commit)
48
+ *
49
+ * 目标:permission.read 和 permission.edit 设为 "allow"
50
+ * 已为 "allow" 跳过;缺 permission 字段自动建
51
+ *
52
+ * @param cwdRoot 主仓根路径
53
+ * @param getClient SDK client 工厂(用于 tui toast 通知)—— 可选
54
+ */
55
+ export async function patchMainRepoOpencodeJson(cwdRoot, getClient) {
56
+ const fields = ['read', 'edit'];
57
+ const read = readMainConfig(cwdRoot);
58
+ if (!read) {
59
+ return { changed: false, diff: [], configPath: join(cwdRoot, 'opencode.json'), error: 'opencode.json not found or parse error' };
60
+ }
61
+ const { config, path: configPath } = read;
62
+ // 计算 diff
63
+ const diff = [];
64
+ const perm = (config['permission'] ?? {});
65
+ const newPerm = { ...perm };
66
+ for (const f of fields) {
67
+ if (perm[f] === 'allow')
68
+ continue;
69
+ diff.push({ path: `permission.${f}`, from: perm[f] ?? null, to: 'allow' });
70
+ newPerm[f] = 'allow';
71
+ }
72
+ if (diff.length === 0) {
73
+ log.info('config-patch', 'no changes needed; already allowed', { configPath });
74
+ return { changed: false, diff: [], configPath };
75
+ }
76
+ // 应用 patch(只改业务字段,不加任何 marker)
77
+ config['permission'] = newPerm;
78
+ try {
79
+ writeMainConfig(configPath, config);
80
+ }
81
+ catch (err) {
82
+ const reason = err instanceof Error ? err.message : String(err);
83
+ log.warn('config-patch', 'write failed', { configPath, err: reason });
84
+ return { changed: false, diff, configPath, error: `write failed: ${reason}` };
85
+ }
86
+ log.info('config-patch', 'patched opencode.json', { configPath, diff });
87
+ // 自动 commit(不阻断)
88
+ try {
89
+ gitAddAndCommit(cwdRoot, 'opencode.json', 'chore(serenity): auto-grant read/edit permissions');
90
+ log.info('config-patch', 'auto-committed', { configPath });
91
+ }
92
+ catch (err) {
93
+ const reason = err instanceof Error ? err.message : String(err);
94
+ log.warn('config-patch', 'auto-commit failed; patch is on disk but uncommitted', { configPath, err: reason });
95
+ }
96
+ // TUI toast 通知用户
97
+ const client = getClient?.();
98
+ if (client?.tui?.showToast) {
99
+ try {
100
+ const fieldList = diff.map((d) => d.path.replace('permission.', '')).join(' + ');
101
+ await client.tui.showToast({
102
+ body: {
103
+ title: TOAST_TITLE,
104
+ message: `auto-granted ${fieldList} (restart opencode to apply)`,
105
+ variant: 'info',
106
+ duration: TOAST_DURATION_MS,
107
+ },
108
+ });
109
+ }
110
+ catch (err) {
111
+ const reason = err instanceof Error ? err.message : String(err);
112
+ log.warn('config-patch', 'tui toast failed (non-blocking)', { err: reason });
113
+ }
114
+ }
115
+ return { changed: true, diff, configPath };
116
+ }
117
+ //# sourceMappingURL=config-patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-patch.js","sourceRoot":"","sources":["../../src/util/config-patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Git 工具 — RR6 验证 + git root 查找
3
+ *
4
+ * 严格使用 SDK 提供的 `input.$` (BunShell) — 不调 git 二进制以保证一致性
5
+ * 注:v0 实际测试中发现 input.$ 是 BunShell,与 POSIX shell 有差异
6
+ * v0 改用 spawn git 二进制(通过 node:child_process),更可靠
7
+ */
8
+ /**
9
+ * 从任意 cwd 出发,向上 walk 找到 git root(git rev-parse --show-toplevel)
10
+ * @throws NotInGitRepoError 如果不在 git repo 内
11
+ */
12
+ export declare function findGitRoot(cwd: string): string;
13
+ /**
14
+ * 同 findGitRoot,但不在 git repo 时返回 null 而非抛错
15
+ * 给 bin/opencode-serenity-plugin.js 用(不抛错便于分支处理)
16
+ */
17
+ export declare function tryFindGitRoot(cwd: string): string | null;
18
+ /**
19
+ * 判断 childPath 是否在 parentPath 内(路径前缀比较)
20
+ * - 规范化路径(resolve)
21
+ * - 用 path.relative 算相对路径
22
+ * - 相对路径不以 '..' 开头 = 在内部
23
+ */
24
+ export declare function isPathInside(parentPath: string, childPath: string): boolean;
25
+ /** git repo 是否是干净的(用于 RR7 init 前的 sanity check) */
26
+ export declare function isGitClean(cwd: string): boolean;
27
+ /** git add + commit(用于 RR7) */
28
+ export declare function gitAddAndCommit(cwd: string, file: string, message: string): void;
29
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/util/git.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAW/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAM3E;AAED,mDAAmD;AACnD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQhF"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Git 工具 — RR6 验证 + git root 查找
3
+ *
4
+ * 严格使用 SDK 提供的 `input.$` (BunShell) — 不调 git 二进制以保证一致性
5
+ * 注:v0 实际测试中发现 input.$ 是 BunShell,与 POSIX shell 有差异
6
+ * v0 改用 spawn git 二进制(通过 node:child_process),更可靠
7
+ */
8
+ import { execFileSync } from 'node:child_process';
9
+ import { NotInGitRepoError } from '../errors.js';
10
+ /**
11
+ * 从任意 cwd 出发,向上 walk 找到 git root(git rev-parse --show-toplevel)
12
+ * @throws NotInGitRepoError 如果不在 git repo 内
13
+ */
14
+ export function findGitRoot(cwd) {
15
+ try {
16
+ const out = execFileSync('git', ['rev-parse', '--show-toplevel'], {
17
+ cwd,
18
+ encoding: 'utf8',
19
+ stdio: ['ignore', 'pipe', 'ignore'],
20
+ });
21
+ return out.trim();
22
+ }
23
+ catch {
24
+ throw new NotInGitRepoError(cwd);
25
+ }
26
+ }
27
+ /**
28
+ * 同 findGitRoot,但不在 git repo 时返回 null 而非抛错
29
+ * 给 bin/opencode-serenity-plugin.js 用(不抛错便于分支处理)
30
+ */
31
+ export function tryFindGitRoot(cwd) {
32
+ try {
33
+ return findGitRoot(cwd);
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /**
40
+ * 判断 childPath 是否在 parentPath 内(路径前缀比较)
41
+ * - 规范化路径(resolve)
42
+ * - 用 path.relative 算相对路径
43
+ * - 相对路径不以 '..' 开头 = 在内部
44
+ */
45
+ export function isPathInside(parentPath, childPath) {
46
+ const parent = parentPath.replace(/\/+$/, '');
47
+ const child = childPath.startsWith('/') ? childPath : `/${childPath}`;
48
+ // 简单前缀比较(已规范化的绝对路径)
49
+ if (parent === '/')
50
+ return child.startsWith('/');
51
+ return child === parent || child.startsWith(parent + '/');
52
+ }
53
+ /** git repo 是否是干净的(用于 RR7 init 前的 sanity check) */
54
+ export function isGitClean(cwd) {
55
+ try {
56
+ execFileSync('git', ['diff-index', '--quiet', 'HEAD', '--'], { cwd, stdio: 'ignore' });
57
+ return true;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ /** git add + commit(用于 RR7) */
64
+ export function gitAddAndCommit(cwd, file, message) {
65
+ try {
66
+ execFileSync('git', ['add', file], { cwd, stdio: 'ignore' });
67
+ execFileSync('git', ['commit', '-m', message], { cwd, stdio: 'ignore' });
68
+ }
69
+ catch (err) {
70
+ const reason = err instanceof Error ? err.message : String(err);
71
+ throw new Error(`git add+commit failed: ${reason}`);
72
+ }
73
+ }
74
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/util/git.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE;YAChE,GAAG;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,SAAiB;IAChE,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC;IACtE,oBAAoB;IACpB,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACjD,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,CAAC;QACH,YAAY,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,IAAY,EAAE,OAAe;IACxE,IAAI,CAAC;QACH,YAAY,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,YAAY,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}