@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.
- package/README.md +199 -0
- package/bin/opencode-serenity-plugin.js +316 -0
- package/dist/activation.d.ts +40 -0
- package/dist/activation.d.ts.map +1 -0
- package/dist/activation.js +133 -0
- package/dist/activation.js.map +1 -0
- package/dist/bash-override.d.ts +10 -0
- package/dist/bash-override.d.ts.map +1 -0
- package/dist/bash-override.js +24 -0
- package/dist/bash-override.js.map +1 -0
- package/dist/bash-toggle.d.ts +17 -0
- package/dist/bash-toggle.d.ts.map +1 -0
- package/dist/bash-toggle.js +42 -0
- package/dist/bash-toggle.js.map +1 -0
- package/dist/config-schema.d.ts +300 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config-schema.js +185 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/errors.d.ts +90 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +151 -0
- package/dist/errors.js.map +1 -0
- package/dist/fs/file-system-tool.d.ts +25 -0
- package/dist/fs/file-system-tool.d.ts.map +1 -0
- package/dist/fs/file-system-tool.js +318 -0
- package/dist/fs/file-system-tool.js.map +1 -0
- package/dist/fs/resolve-path.d.ts +53 -0
- package/dist/fs/resolve-path.d.ts.map +1 -0
- package/dist/fs/resolve-path.js +100 -0
- package/dist/fs/resolve-path.js.map +1 -0
- package/dist/hooks/compacting.d.ts +23 -0
- package/dist/hooks/compacting.d.ts.map +1 -0
- package/dist/hooks/compacting.js +90 -0
- package/dist/hooks/compacting.js.map +1 -0
- package/dist/hooks/permission-auto-reply.d.ts +91 -0
- package/dist/hooks/permission-auto-reply.d.ts.map +1 -0
- package/dist/hooks/permission-auto-reply.js +158 -0
- package/dist/hooks/permission-auto-reply.js.map +1 -0
- package/dist/hooks/permission-guards.d.ts +41 -0
- package/dist/hooks/permission-guards.d.ts.map +1 -0
- package/dist/hooks/permission-guards.js +153 -0
- package/dist/hooks/permission-guards.js.map +1 -0
- package/dist/hooks/shell-env.d.ts +20 -0
- package/dist/hooks/shell-env.d.ts.map +1 -0
- package/dist/hooks/shell-env.js +38 -0
- package/dist/hooks/shell-env.js.map +1 -0
- package/dist/hooks/util.d.ts +81 -0
- package/dist/hooks/util.d.ts.map +1 -0
- package/dist/hooks/util.js +172 -0
- package/dist/hooks/util.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/init/init-wizard.d.ts +39 -0
- package/dist/init/init-wizard.d.ts.map +1 -0
- package/dist/init/init-wizard.js +297 -0
- package/dist/init/init-wizard.js.map +1 -0
- package/dist/install.d.ts +117 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +255 -0
- package/dist/install.js.map +1 -0
- package/dist/msm-schema.d.ts +76 -0
- package/dist/msm-schema.d.ts.map +1 -0
- package/dist/msm-schema.js +207 -0
- package/dist/msm-schema.js.map +1 -0
- package/dist/msm.d.ts +25 -0
- package/dist/msm.d.ts.map +1 -0
- package/dist/msm.js +317 -0
- package/dist/msm.js.map +1 -0
- package/dist/session/lib.d.ts +33 -0
- package/dist/session/lib.d.ts.map +1 -0
- package/dist/session/lib.js +475 -0
- package/dist/session/lib.js.map +1 -0
- package/dist/session/session-tool.d.ts +17 -0
- package/dist/session/session-tool.d.ts.map +1 -0
- package/dist/session/session-tool.js +109 -0
- package/dist/session/session-tool.js.map +1 -0
- package/dist/skills/install-skill.d.ts +36 -0
- package/dist/skills/install-skill.d.ts.map +1 -0
- package/dist/skills/install-skill.js +91 -0
- package/dist/skills/install-skill.js.map +1 -0
- package/dist/skills/template-loader.d.ts +79 -0
- package/dist/skills/template-loader.d.ts.map +1 -0
- package/dist/skills/template-loader.js +170 -0
- package/dist/skills/template-loader.js.map +1 -0
- package/dist/state.d.ts +35 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +62 -0
- package/dist/state.js.map +1 -0
- package/dist/tui.d.ts +61 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +279 -0
- package/dist/tui.js.map +1 -0
- package/dist/types/index.d.ts +30 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +17 -0
- package/dist/types/index.js.map +1 -0
- package/dist/util/config-patch.d.ts +58 -0
- package/dist/util/config-patch.d.ts.map +1 -0
- package/dist/util/config-patch.js +117 -0
- package/dist/util/config-patch.js.map +1 -0
- package/dist/util/git.d.ts +29 -0
- package/dist/util/git.d.ts.map +1 -0
- package/dist/util/git.js +74 -0
- package/dist/util/git.js.map +1 -0
- package/dist/util/init-check.d.ts +22 -0
- package/dist/util/init-check.d.ts.map +1 -0
- package/dist/util/init-check.js +76 -0
- package/dist/util/init-check.js.map +1 -0
- package/dist/util/init.d.ts +54 -0
- package/dist/util/init.d.ts.map +1 -0
- package/dist/util/init.js +87 -0
- package/dist/util/init.js.map +1 -0
- package/dist/util/log.d.ts +25 -0
- package/dist/util/log.d.ts.map +1 -0
- package/dist/util/log.js +28 -0
- package/dist/util/log.js.map +1 -0
- package/dist/util/msm-call.d.ts +48 -0
- package/dist/util/msm-call.d.ts.map +1 -0
- package/dist/util/msm-call.js +86 -0
- package/dist/util/msm-call.js.map +1 -0
- package/dist/util/msm-exec-runtime.d.ts +123 -0
- package/dist/util/msm-exec-runtime.d.ts.map +1 -0
- package/dist/util/msm-exec-runtime.js +532 -0
- package/dist/util/msm-exec-runtime.js.map +1 -0
- package/dist/util/path.d.ts +10 -0
- package/dist/util/path.d.ts.map +1 -0
- package/dist/util/path.js +21 -0
- package/dist/util/path.js.map +1 -0
- package/dist/util/ready-state.d.ts +43 -0
- package/dist/util/ready-state.d.ts.map +1 -0
- package/dist/util/ready-state.js +104 -0
- package/dist/util/ready-state.js.map +1 -0
- package/dist/util/serenity-file.d.ts +30 -0
- package/dist/util/serenity-file.d.ts.map +1 -0
- package/dist/util/serenity-file.js +69 -0
- package/dist/util/serenity-file.js.map +1 -0
- package/dist/util/tui-install.d.ts +61 -0
- package/dist/util/tui-install.d.ts.map +1 -0
- package/dist/util/tui-install.js +94 -0
- package/dist/util/tui-install.js.map +1 -0
- package/docs/architecture-v0.md +294 -0
- package/docs/contract-v0.md +417 -0
- package/docs/plugin-self-contained-msm-v1.md +182 -0
- package/docs/refactor-direction-v1.11.md +78 -0
- package/docs/requirements-v0-scope.md +104 -0
- package/docs/requirements-v0-summary.md +108 -0
- package/docs/rr7-init-design.md +304 -0
- package/docs/v0.1-candidates.md +132 -0
- package/package.json +54 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Plugin Architecture v0 — 方案/设计层
|
|
2
|
+
|
|
3
|
+
> **会话**:opencode-serenity-plugin(项目即会话模式)
|
|
4
|
+
> **状态**:v0.0.2 已发布(2026-06-07)。本文件随代码演进持续修订。
|
|
5
|
+
> **范围**:v0 plugin 整体架构 + 启动协议 + 模块划分 + 数据流 + 失败模式。
|
|
6
|
+
> **不进**:接口/契约层(`docs/contract-v0.md`)、实现/代码层(`src/`)、需求层(`docs/requirements-v0-scope.md`)。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. 顶层架构图
|
|
11
|
+
|
|
12
|
+
opencode 1.16+ 强制 `PluginModule` 二选一(`server | tui`),所以 plugin 走**两条独立 entry 并行加载**:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
16
|
+
│ opencode 宿主运行时(任何 cwd) │
|
|
17
|
+
│ │
|
|
18
|
+
│ ┌────────────────┐ 启动时(一次) │
|
|
19
|
+
│ │ plugin 加载 │ ─────────────────────────┐ │
|
|
20
|
+
│ │ (loader) │ ▼ │
|
|
21
|
+
│ └────────────────┘ ┌──────────────────────────────────────┐ │
|
|
22
|
+
│ │ opencode-serenity-plugin (server) │ │
|
|
23
|
+
│ │ src/index.ts (entry) │ │
|
|
24
|
+
│ │ │ │
|
|
25
|
+
│ │ ┌───────────────────────────────┐ │ │
|
|
26
|
+
│ │ │ activation.ts (两阶段 init) │ │ │
|
|
27
|
+
│ │ │ Phase 1 sync: RR6 git check │ │ │
|
|
28
|
+
│ │ │ Phase 2 async: RR1 + RR2 │ │ │
|
|
29
|
+
│ │ │ + v1.5 init-check │ │ │
|
|
30
|
+
│ │ │ + v1.7 config-patch │ │ │
|
|
31
|
+
│ │ └───────────────────────────────┘ │ │
|
|
32
|
+
│ │ │ │ │
|
|
33
|
+
│ │ ▼ │ │
|
|
34
|
+
│ │ ┌───────────────────────────────┐ │ │
|
|
35
|
+
│ │ │ plugin state (singleton): │ │ │
|
|
36
|
+
│ │ │ { activated, cwdRoot, │ │ │
|
|
37
|
+
│ │ │ instanceName, skillPath, │ │ │
|
|
38
|
+
│ │ │ skillContent } │ │ │
|
|
39
|
+
│ │ └───────────────────────────────┘ │ │
|
|
40
|
+
│ │ │ │ │
|
|
41
|
+
│ │ ▼ │ │
|
|
42
|
+
│ │ 注册 hooks + tools: │ │
|
|
43
|
+
│ │ • tool.execute.before — RR5 hard block │ │
|
|
44
|
+
│ │ • experimental.chat.system.transform │ │
|
|
45
|
+
│ │ (注入 SKILL.md) │ │
|
|
46
|
+
│ │ • experimental.session.compacting │ │
|
|
47
|
+
│ │ • shell.env — 注入 HOME_SERENITY_ROOT │ │
|
|
48
|
+
│ │ • event permission.asked — auto-reply │ │
|
|
49
|
+
│ │ • tool: bash(override)/msm_list/ │ │
|
|
50
|
+
│ │ msm_exec/msm_admin │ │
|
|
51
|
+
│ └──────────────────────────────────────┘ │
|
|
52
|
+
│ │
|
|
53
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
54
|
+
│ │ opencode-serenity-plugin (TUI, 平行 entry) │ │
|
|
55
|
+
│ │ src/tui.ts (entry) │ │
|
|
56
|
+
│ │ │ │
|
|
57
|
+
│ │ • 加载 toast: "v${VERSION} loaded" │ │
|
|
58
|
+
│ │ • 激活 toast: "plugin activated — read/edit = allow" │ │
|
|
59
|
+
│ │ • 自检自装到 global tui.json(v1.10.1 修复) │ │
|
|
60
|
+
│ │ • /serenity-init slash command (RR7 init) │ │
|
|
61
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
62
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
两条 entry 独立加载但共享 `src/state.ts` 模块级 singleton——server entry 写、tui entry 读(不依赖 RR1 即可展示 toast)。
|
|
66
|
+
|
|
67
|
+
## 2. 启动协议 — 两阶段 init (v0.1)
|
|
68
|
+
|
|
69
|
+
**触发**:opencode 启动时,plugin loader 加载 `src/index.ts` 的 default export 一次。
|
|
70
|
+
|
|
71
|
+
**前提**:`@opencode-ai/plugin@1.16.2` 的 `Plugin` 类型签名 `(input: PluginInput) => Promise<Hooks>`,input 提供 `directory / worktree / client / serverUrl`。
|
|
72
|
+
|
|
73
|
+
### Phase 1:同步(plugin 入口立即返回)
|
|
74
|
+
|
|
75
|
+
| 步 | 动作 | 失败处理 | 来源 |
|
|
76
|
+
|---|------|---------|------|
|
|
77
|
+
| 1 | `cwd = input.directory` | 异常 → return `{}`(plugin 静默不工作)| RR6 隐含 |
|
|
78
|
+
| 2 | `findGitRoot(cwd)` (git rev-parse --show-toplevel) | 失败 / 不在 → **mark disabled** + return `{}` | RR6 |
|
|
79
|
+
| 3 | 启动 Phase 2 fire-and-forget(不 await)| — | 内部 |
|
|
80
|
+
| 4 | 注册 hooks + tools(Phase 2 未完成时 hook 内 `ensureReady()` 阻塞)| — | 内部 |
|
|
81
|
+
| 5 | return `hooks` | — | 内部 |
|
|
82
|
+
|
|
83
|
+
### Phase 2:异步(fire-and-forget 后台 IO)
|
|
84
|
+
|
|
85
|
+
| 步 | 动作 | 失败处理 | 来源 |
|
|
86
|
+
|---|------|---------|------|
|
|
87
|
+
| 1 | RR1 读 `<gitRoot>/.serenity` | 失败 → state.error(machine 推 error) | RR1 |
|
|
88
|
+
| 2 | RR2 验证 `<gitRoot>/.opencode/skills/<N>/SKILL.md` | 失败 → state.error | RR2 |
|
|
89
|
+
| 3 | 读 SKILL.md 全文(system.transform 注入用)| 失败 → 降级为 null(plugin 仍工作) | 内部 |
|
|
90
|
+
| 4 | setState + markReady | — | 内部 |
|
|
91
|
+
| 5 | v1.5 init-check:warn 不 patch | 失败 → log.warn | 内部 |
|
|
92
|
+
| 6 | v1.7 config-patch:自动 grant read/edit=allow + TUI toast 通知 | 失败 → log.warn(不阻断) | 内部 |
|
|
93
|
+
|
|
94
|
+
**重要约束**:
|
|
95
|
+
- **前 2 步**任意失败 → `return {}`(plugin 啥也不做,**就像没装一样**——Q6 用户原话)
|
|
96
|
+
- **不 throw**(抛错会中断 opencode 启动,且违反"不工作"语义)
|
|
97
|
+
- **不 console.error**(用 `log.warn`/`log.debug`——不污染用户)
|
|
98
|
+
- **Exception**:`tool.execute.before` (permission-guards) 故意让 throw 透传(RR5 hard block 想要的行为;其他 hook 走 `safeHook` 静默)
|
|
99
|
+
|
|
100
|
+
## 3. 模块划分(v0.0.2 实际)
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
opencode-serenity-plugin/
|
|
104
|
+
├── README.md # 用户入口 + 安装说明
|
|
105
|
+
├── SESSION.md # 项目即会话追踪
|
|
106
|
+
├── package.json # version 0.0.2, deps: @opencode-ai/plugin@1.16.2 + zod@4.1.8
|
|
107
|
+
├── tsconfig.json / tsconfig.test.json # TS 5.x strict + Node 20+
|
|
108
|
+
├── vitest.config.ts # testTimeout 20s(msm-call 冷启动)
|
|
109
|
+
├── bin/
|
|
110
|
+
│ └── opencode-serenity-plugin.js # CLI: install/uninstall (v1.11)
|
|
111
|
+
├── src/ # ── 12 顶层 .ts ──
|
|
112
|
+
│ ├── index.ts # server entry (4 tool + 4 hook)
|
|
113
|
+
│ ├── tui.ts # TUI entry (toast + /serenity-init + self-install)
|
|
114
|
+
│ ├── activation.ts # 两阶段 init + config-patch wiring
|
|
115
|
+
│ ├── state.ts # 全局激活状态 singleton + ReadyStateMachine
|
|
116
|
+
│ ├── msm.ts # msmListTool + msmExecTool + msmAdminTool (4 tool)
|
|
117
|
+
│ ├── msm-schema.ts # flag normalize (v0/v1) + tokenizeArgs + path-arg 校验 (v0.1-2 + v1-1)
|
|
118
|
+
│ ├── config-schema.ts # zod-first 4 schemas (v1.13)
|
|
119
|
+
│ ├── install.ts # bin install lib (project + global, XDG + APPDATA)
|
|
120
|
+
│ ├── bash-override.ts # 同名 bash tool 覆盖 (RR3 第三层)
|
|
121
|
+
│ ├── errors.ts # 13 个 SerenityError 子类 + 1 基类
|
|
122
|
+
│ ├── types/
|
|
123
|
+
│ │ └── index.ts # SerenityState + INACTIVE_STATE + SerenityPluginInput
|
|
124
|
+
│ ├── hooks/ # ── 5 hook 工厂 ──
|
|
125
|
+
│ │ ├── util.ts # isHookEnabled + safeHook + safeCreateHook (oMo 模式 v1.12)
|
|
126
|
+
│ │ ├── permission-guards.ts # tool.execute.before — RR5 hard block + bash 防御
|
|
127
|
+
│ │ ├── compacting.ts # system.transform + session.compacting
|
|
128
|
+
│ │ ├── shell-env.ts # shell.env 注入 HOME_SERENITY_ROOT + SERENITY_PLUGIN_VERSION
|
|
129
|
+
│ │ └── permission-auto-reply.ts # event hook — permission.asked → "always" (v1.3-v4)
|
|
130
|
+
│ └── util/ # ── 10 helper ──
|
|
131
|
+
│ ├── git.ts # findGitRoot + tryFindGitRoot + isPathInside + git ops
|
|
132
|
+
│ ├── path.ts # buildSkillPath + validateSkillExists
|
|
133
|
+
│ ├── serenity-file.ts # readSerenityFile (RR1)
|
|
134
|
+
│ ├── ready-state.ts # ReadyStateMachine (idle/loading/ready/error/disabled)
|
|
135
|
+
│ ├── init.ts # initSerenity (RR7)
|
|
136
|
+
│ ├── init-check.ts # checkSerenityConfig (v1.5, warn-only)
|
|
137
|
+
│ ├── config-patch.ts # patchMainRepoOpencodeJson (v1.7, auto-grant)
|
|
138
|
+
│ ├── msm-call.ts # callMsmExec + callMsmExecMeta + parseProtocolFlags (S022 RFC)
|
|
139
|
+
│ ├── tui-install.ts # global tui.json 自安装 (v1.10.1, 薄包装 install.ts)
|
|
140
|
+
│ └── log.ts # 统一 log wrapper (no-op, 65 sites)
|
|
141
|
+
├── tests/ # ── 23 测 / 320 cases ──
|
|
142
|
+
│ ├── activation.test.ts / ready-state.test.ts # Phase 1+2 + machine
|
|
143
|
+
│ ├── msm-{call,schema,registry,admin}.test.ts # 协议层 + admin
|
|
144
|
+
│ ├── config-{schema,patch}.test.ts # zod + patch
|
|
145
|
+
│ ├── errors.test.ts # 13 错误类
|
|
146
|
+
│ ├── hooks-{util,guard}.test.ts # 工厂 + guard
|
|
147
|
+
│ ├── permission-{auto-reply,guards-v16}.test.ts # event + RR5
|
|
148
|
+
│ ├── compacting-skill-inject.test.ts # SKILL.md 注入
|
|
149
|
+
│ ├── init-check.test.ts / install.test.ts # init + bin
|
|
150
|
+
│ ├── tui{-install,}.test.ts # tui 入口
|
|
151
|
+
│ ├── plugin.test.ts # full entry
|
|
152
|
+
│ └── util-{git,init,path,serenity-file}.test.ts # helpers
|
|
153
|
+
└── docs/ # ── 7 文档 ──
|
|
154
|
+
├── requirements-v0-scope.md # RR1-RR7 范围层 (v0 终版)
|
|
155
|
+
├── architecture-v0.md # 本文件
|
|
156
|
+
├── contract-v0.md # 6 契约 + 13 错误类
|
|
157
|
+
├── requirements-v0-summary.md # ⚠️ 已过时(旧 R1-R5 保留演进史)
|
|
158
|
+
├── v0.1-candidates.md # ✅ ALL DONE (3 候选全部已实施)
|
|
159
|
+
├── rr7-init-design.md # v1.10 + v1.10.1 实施记录
|
|
160
|
+
└── refactor-direction-v1.11.md # ✅ Done 2026-06-07 (v1.11-v1.17)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 4. 数据流
|
|
164
|
+
|
|
165
|
+
### 4.1 启动阶段(一次性)
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
opencode 启动
|
|
169
|
+
↓
|
|
170
|
+
plugin loader 加载 src/index.ts (server) + src/tui.ts (TUI) 并行
|
|
171
|
+
↓
|
|
172
|
+
server: activation.tryActivateSync
|
|
173
|
+
↓
|
|
174
|
+
[Phase 1 sync — RR6 git check]
|
|
175
|
+
↓ (ok)
|
|
176
|
+
注册 hooks + tools + 启动 Phase 2 fire-and-forget + 立即返回 hooks
|
|
177
|
+
↓
|
|
178
|
+
Phase 2 async (后台): RR1 + RR2 + skillContent + init-check + config-patch
|
|
179
|
+
↓
|
|
180
|
+
setState(activated) + machine.markAsReady()
|
|
181
|
+
↓
|
|
182
|
+
ensureReady() 在所有 tools/hooks 内不再阻塞
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 4.2 工具调用阶段(每次)
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
LLM 决定调工具
|
|
189
|
+
↓
|
|
190
|
+
opencode 查 tool 注册表
|
|
191
|
+
├─ msm_list ─→ src/msm.ts#msmListTool → loadMechRegistry → 字符串输出
|
|
192
|
+
├─ msm_exec ─→ src/msm.ts#msmExecTool → 1. tokenizeArgs + parseProtocolFlags
|
|
193
|
+
│ 2. findMsm + path-arg 校验
|
|
194
|
+
│ 3. msm-exec.ts (协议层) — 含 6 必含 flag
|
|
195
|
+
│ 4. 错误类: MsmExecutionError (持 stdout)
|
|
196
|
+
├─ msm_admin ─→ src/msm.ts#msmAdminTool → register/deregister + auto-commit
|
|
197
|
+
└─ bash ─→ src/bash-override.ts → throw BashDisabledError (RR3)
|
|
198
|
+
↓
|
|
199
|
+
permission.asked event hook (parallel) — 无条件 reply "always"
|
|
200
|
+
↓
|
|
201
|
+
permission-guards tool.execute.before — RR5 hard block (read/edit/write 路径守卫)
|
|
202
|
+
↓
|
|
203
|
+
执行结果回 LLM
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 4.3 关键判定
|
|
207
|
+
|
|
208
|
+
| 判定 | 函数 | 来源 |
|
|
209
|
+
|------|------|------|
|
|
210
|
+
| cwd 在 git repo 内 | `findGitRoot(cwd)` (git rev-parse) | RR6 |
|
|
211
|
+
| `/.serenity` 存在 + 非空 | `readSerenityFile(gitRoot)` | RR1 |
|
|
212
|
+
| 实例名 N 解析 | `parseInstanceName(content)` | RR1 |
|
|
213
|
+
| skill 路径存在 | `validateSkillPath(gitRoot, N)` | RR2 |
|
|
214
|
+
| 路径在 cwdRoot 内 | `isPathInside(cwdRoot, abs)` | RR5 |
|
|
215
|
+
| path-arg 解析在 cwdRoot 内 | `validatePathArgsFromTokens(rest, flags, cwdRoot)` (v0.1-2) | msm_exec 路径守卫 |
|
|
216
|
+
| symlink 防御 | `realpathSync` 比较 | v1-1 |
|
|
217
|
+
| msm_name 在注册表 | `findMsm(name, registry)` | 内部 |
|
|
218
|
+
| hook 启用 | `isHookEnabled(name, config?)` + env var + session disable | v1.12 |
|
|
219
|
+
|
|
220
|
+
## 5. 与主仓关系
|
|
221
|
+
|
|
222
|
+
| 维度 | plugin 仓 | 主仓(home-serenity) | 其他工作项目 |
|
|
223
|
+
|------|----------|-------------------|------------|
|
|
224
|
+
| `/.serenity` | ❌ | ✅(内容 = `home-serenity`) | ❌ |
|
|
225
|
+
| 是 git repo | ✅ | ✅ | 视情况 |
|
|
226
|
+
| 加载 plugin 时激活? | ❌(plugin 仓不满足 RR1) | ✅ | ❌ |
|
|
227
|
+
| 行为 | plugin **开发** | plugin **生效** | opencode 默认行为 |
|
|
228
|
+
|
|
229
|
+
**plugin 仓** = plugin **工程实践**(git 维护、tsconfig 严格、vitest 单测、commit 历史)
|
|
230
|
+
**主仓** = plugin **运行时宿主**(plugin 激活后所有行为以 cwd = 主仓根为基础)
|
|
231
|
+
**plugin 不需要知道"主仓"在哪**——只看 cwd
|
|
232
|
+
|
|
233
|
+
**工程层独立(决策 #22)**:plugin 仓的 git/test/文档约束**独立于 RR6**——plugin 仓本身是 git repo 是为了工程需求(如 commit 历史、单测覆盖率追踪),**不是**为满足 RR6(plugin 加载时 cwd 仍是主仓根,不在 plugin 仓内)。
|
|
234
|
+
|
|
235
|
+
## 6. 失败模式
|
|
236
|
+
|
|
237
|
+
| 失败 | 行为 | 用户可见性 |
|
|
238
|
+
|------|------|----------|
|
|
239
|
+
| cwd 不在 git repo | plugin 完全不激活 | opencode 默认行为(无变化) |
|
|
240
|
+
| `/.serenity` 不存在 | plugin 不激活 | 同上 |
|
|
241
|
+
| `/.serenity` 内容为空 | plugin 不激活 | log warn |
|
|
242
|
+
| `/.serenity` 找不到对应 SKILL.md | plugin 不激活 | log warn |
|
|
243
|
+
| msm_name 不在注册表 | `msm_exec` 抛 `MsmNotRegisteredError` | LLM 看到错误消息 |
|
|
244
|
+
| msm 子进程超时 (>30s) | `msm_exec` 抛 `MsmTimeoutError` | LLM 看到错误消息 |
|
|
245
|
+
| msm 子进程 exit ≠ 0 | `msm_exec` 抛 `MsmExecutionError`(持 stdout) | LLM 看到错误消息 + stdout |
|
|
246
|
+
| path-arg 越界 | `msm_exec` 抛 `MsmPathEscapeError` (v0.1-2) | LLM 看到错误消息 |
|
|
247
|
+
| path-arg 是 symlink 链 | `msm_exec` 抛 `MsmSymlinkError` (v1-1) | LLM 看到错误消息 |
|
|
248
|
+
| bash 被 LLM 调用 | 抛 `BashDisabledError` | LLM 看到错误消息(RR3 行为) |
|
|
249
|
+
| cwd 外 read/edit/write | tool.execute.before 抛 `[serenity] ... is outside the serenity workspace root (RR5)` | LLM 看到错误消息(v1.6 RR5 hard block) |
|
|
250
|
+
| hook factory 抛错 | `safeCreateHook` 自动 disable + 返回 no-op | 不影响 LLM(retry-storm 防护) |
|
|
251
|
+
| init-check opencode.json 缺字段 | 只 warn 不 patch | log.warn |
|
|
252
|
+
| config-patch 写失败 | log.warn + plugin 继续工作 | 用户需手动配置 |
|
|
253
|
+
| 外部 slash command(如 `/clear`) | 不拦截 | opencode 默认行为 |
|
|
254
|
+
|
|
255
|
+
## 7. 演进路径(v0 → v1+)— ✅ 全部已实施
|
|
256
|
+
|
|
257
|
+
| 候选 | 描述 | 实施 |
|
|
258
|
+
|------|------|------|
|
|
259
|
+
| 候选 1 | `permission.asked` event hook 自动 reply "always"(v1.3-v4 决策:trust opencode own always list,无条件 reply) | ✅ v1.3 (commit `809bf94`) |
|
|
260
|
+
| 候选 2 | `experimental.session.compacting` hook(压缩后重注入 SKILL.md + 关键状态) | ✅ v1.4 (commit `cee8c2e`) |
|
|
261
|
+
| 候选 3 | `shell.env` hook 注入 `$HOME_SERENITY_ROOT` 等环境变量 | ✅ v1.5 (commit `00fcd19`) |
|
|
262
|
+
| 候选 4 | 多实例 registry(如果用户超过 3 个实例时) | ⏸ v0.0.2 不实施(实操未遇 ≥3 实例场景) |
|
|
263
|
+
|
|
264
|
+
候选 1-3 实施于 v1.3-v1.5;候选 4 推迟到 v0.0.3+ 按需。完整记录见 `SESSION.md` 项目演进历史。
|
|
265
|
+
|
|
266
|
+
## 8. 与 SDK 对接点
|
|
267
|
+
|
|
268
|
+
| 抽象层概念 | SDK 实现 (opencode 1.16.2) | 文档 |
|
|
269
|
+
|----------|--------------------------|------|
|
|
270
|
+
| 工具注册 | `import { tool } from '@opencode-ai/plugin'; tool({ description, args: z.object({...}), execute })` | `@opencode-ai/plugin/tool` |
|
|
271
|
+
| Hooks 集合 | `const hooks: Hooks = { tool: {...}, 'experimental.chat.system.transform': ... }` | `@opencode-ai/plugin/Hooks` |
|
|
272
|
+
| permission.asked event | `event: { 'permission.asked': async (input, output) => { output.reply = 'always' } }` | `@opencode-ai/plugin/Hooks.event` |
|
|
273
|
+
| tool.execute.before | `'tool.execute.before': async (input, output) => { ... throw ... }` | 同上 |
|
|
274
|
+
| system.transform | `'experimental.chat.system.transform': async (input, output) => { output.system.push(...) }` | 同上 |
|
|
275
|
+
| session.compacting | `'experimental.session.compacting': async (input, output) => { output.context.push(...) }` | 同上 |
|
|
276
|
+
| shell.env | `'shell.env': async (input, output) => { output.env.X = ... }` | 同上 |
|
|
277
|
+
| TUI plugin entry | `import type { TuiPlugin } from '@opencode-ai/plugin/tui'; export default { id, tui: Tui }` | `@opencode-ai/plugin/tui` |
|
|
278
|
+
| PluginModule 二选一 | `export default { id, server }` 或 `export default { id, tui }` | `@opencode-ai/plugin/Plugin` |
|
|
279
|
+
| toast 通知 | `api.ui.toast({ title, message, variant, duration })` (TuiPlugin) | 同上 |
|
|
280
|
+
| slash command | `api.command?.register(() => [{ title, value, slash, onSelect }])` | 同上 |
|
|
281
|
+
|
|
282
|
+
**SDK 升级注意**:opencode 1.16+ 强制 PluginModule 二选一,v1.8 引入 TUI 独立 entry 绕过此限制。**严禁**在同一 module 同时 export `server` 和 `tui`。
|
|
283
|
+
|
|
284
|
+
## 9. 文档演进规则
|
|
285
|
+
|
|
286
|
+
- **范围层**(RR1-RR7)→ `docs/requirements-v0-scope.md`
|
|
287
|
+
- **方案层**(本文)→ `docs/architecture-v0.md`(本文)
|
|
288
|
+
- **接口层**(6 契约 + 13 错误类)→ `docs/contract-v0.md`
|
|
289
|
+
- **设计草案 / 演进候选** → `docs/v0.1-candidates.md` + `docs/refactor-direction-*.md` + `docs/rr7-init-design.md`
|
|
290
|
+
- **实现层** → `src/`
|
|
291
|
+
- **测试层** → `tests/`
|
|
292
|
+
- **SESSION 跟踪** → `SESSION.md`(项目即会话)
|
|
293
|
+
|
|
294
|
+
> **本文件最后修订**:2026-06-07(v0.0.2 文档债务清理 Phase 3 D7+D8:架构图、模块结构、SDK API 全部按 v0.0.2 实际重写;演进路径 4 候选全部标 ✅)
|