@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
@@ -0,0 +1,297 @@
1
+ /**
2
+ * init-wizard.ts — D1 Init 向导 Phase 1
3
+ *
4
+ * CLI 交互式创建宁静号实例的目录骨架并复制标准技能模板。
5
+ *
6
+ * 流程:
7
+ * 1. 收集基本信息(prefix, description)
8
+ * 2. 创建目录骨架(.serenity, .gitignore, AGENT_SESSIONS/, docs/)
9
+ * 3. 复制 9 个标准技能模板到 .opencode/skills/
10
+ * 4. 生成 opencode.json(注册 plugin)
11
+ * 5. 写入 Phase 2 Agent prompt
12
+ * 6. 输出完成信息
13
+ */
14
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
15
+ import { join, basename } from 'node:path';
16
+ import { createInterface } from 'node:readline';
17
+ import { stdin, stdout } from 'node:process';
18
+ import { getTemplatesDir, installTemplate, defaultPlaceholders, } from '../skills/template-loader.js';
19
+ // ── 默认值 ──
20
+ const STANDARD_SKILLS = [
21
+ 'compass',
22
+ 'session',
23
+ 'sqc',
24
+ 'exploration',
25
+ 'quality-review',
26
+ 'landscape',
27
+ 'git',
28
+ ];
29
+ /** 从目录名推断 prefix */
30
+ function inferPrefix(dirName) {
31
+ // 取第一个词(小写,非字母开头的去掉)
32
+ const cleaned = dirName.replace(/[^a-zA-Z0-9-]/g, '');
33
+ const first = cleaned.split(/[-_]/)[0];
34
+ if (!first)
35
+ return 'my';
36
+ return first.toLowerCase().slice(0, 8);
37
+ }
38
+ /** 从 prefix 推断中文名 */
39
+ function inferName(prefix) {
40
+ const names = {
41
+ home: '宁静号',
42
+ tg: '天工宁静号',
43
+ pg: '盘古宁静号',
44
+ };
45
+ return names[prefix] ?? `${prefix.toUpperCase()} 宁静号`;
46
+ }
47
+ // ── 交互式问答 ──
48
+ async function askQuestion(prompt, defaultValue) {
49
+ const rl = createInterface({ input: stdin, output: stdout });
50
+ return new Promise((resolve) => {
51
+ const fullPrompt = defaultValue
52
+ ? `${prompt} [${defaultValue}]: `
53
+ : `${prompt}: `;
54
+ rl.question(fullPrompt, (answer) => {
55
+ rl.close();
56
+ resolve(answer.trim() || defaultValue || '');
57
+ });
58
+ });
59
+ }
60
+ async function collectInfo(prefill) {
61
+ const prefix = await askQuestion('What prefix for skill names? (kebab-case, e.g. "home", "tg", "pg")', prefill);
62
+ const description = await askQuestion('Describe this serenity instance in one sentence', '');
63
+ return {
64
+ prefix: prefix || prefill,
65
+ description: description || 'A serenity instance',
66
+ };
67
+ }
68
+ function buildSkeleton(prefix) {
69
+ const skillDir = join('.opencode', 'skills');
70
+ const rootSkill = `${prefix}-serenity`;
71
+ return [
72
+ // 目录
73
+ { path: join('AGENT_SESSIONS'), content: '', isDir: true },
74
+ { path: join('docs'), content: '', isDir: true },
75
+ { path: join('.opencode', 'scripts'), content: '', isDir: true },
76
+ { path: join('.opencode', 'references'), content: '', isDir: true },
77
+ // .serenity
78
+ { path: '.serenity', content: `${rootSkill}\n` },
79
+ // .gitignore
80
+ {
81
+ path: '.gitignore',
82
+ content: [
83
+ '# 宁静号标准 .gitignore — 外部仓库不被纳入本仓库',
84
+ '# 用户可在此追加专属的排除规则',
85
+ '',
86
+ '# 在宁静号项目中,所有子项目作为独立 git 仓库存放',
87
+ '# 通过根 .gitignore 排除它们,避免意外提交',
88
+ ].join('\n') + '\n',
89
+ },
90
+ // opencode.json
91
+ {
92
+ path: 'opencode.json',
93
+ content: JSON.stringify({
94
+ $schema: 'https://opencode.ai/config.json',
95
+ permission: {
96
+ read: 'allow',
97
+ edit: 'allow',
98
+ },
99
+ }, null, 2) + '\n',
100
+ },
101
+ // 根 skill 骨架(等待 Phase 2 生成)
102
+ {
103
+ path: join(skillDir, rootSkill, 'SKILL.md'),
104
+ content: [
105
+ `# ${rootSkill} — ${inferName(prefix)}`,
106
+ '',
107
+ '> 由 serenity-plugin init 创建。此文件将在 Phase 2 由 Agent 完善。',
108
+ '> 运行 `npx tsx ` + resolve + ` 根据引导完成初始化。',
109
+ '',
110
+ '## 初始配置',
111
+ '',
112
+ `- prefix: ${prefix}`,
113
+ '- 标准技能已预装(见 .opencode/skills/)',
114
+ '- 等待 Phase 2 生成完整的根 skill',
115
+ ].join('\n') + '\n',
116
+ },
117
+ // 根 skill 目录结构
118
+ { path: join(skillDir, rootSkill, 'references'), content: '', isDir: true },
119
+ { path: join(skillDir, rootSkill, 'scripts'), content: '', isDir: true },
120
+ // Agent Phase 2 prompt
121
+ {
122
+ path: join(skillDir, rootSkill, 'scripts', 'generate-root-skill.prompt.md'),
123
+ content: generatePhase2Prompt(prefix),
124
+ },
125
+ // 根 skill 注册表骨架
126
+ {
127
+ path: join(skillDir, rootSkill, 'references', 'mech-registry.json'),
128
+ content: JSON.stringify({
129
+ version: 1,
130
+ serenity: rootSkill,
131
+ description: `Register for ${rootSkill}`,
132
+ entries: [],
133
+ }, null, 2) + '\n',
134
+ },
135
+ ];
136
+ }
137
+ // ── Phase 2 Agent Prompt ──
138
+ function generatePhase2Prompt(prefix) {
139
+ return [
140
+ `# Phase 2: Complete ${prefix}-serenity Root Skill`,
141
+ '',
142
+ 'This serenity instance has been initialized with all standard meta-skills.',
143
+ 'Now you need to complete the root skill SKILL.md.',
144
+ '',
145
+ '## Interview Questions',
146
+ '',
147
+ 'Answer these questions interactively with the user, then fill in the root skill at',
148
+ `.opencode/skills/${prefix}-serenity/SKILL.md`,
149
+ '',
150
+ '1. **System description**: What project/system does this serenity manage?',
151
+ '2. **Scope**: Single person, small team, or enterprise?',
152
+ '3. **Key components**: What sub-projects or modules does the system contain?',
153
+ '4. **Collaboration style**: Structured (formal docs/SOP) or flexible (ad-hoc)?',
154
+ '5. **Language preference**: Precise, conversational, or technical?',
155
+ '',
156
+ '## Root Skill Skeleton',
157
+ '',
158
+ `Create the SKILL.md for ${prefix}-serenity with these sections:`,
159
+ '',
160
+ '- `# Skill: ${prefix}-serenity — <Name>`',
161
+ '- System identity and core principles',
162
+ '- Skill list (all 9 pre-installed skills)',
163
+ '- Task route table (basic routes)',
164
+ '- Collaboration protocols (Neat + naming + session conventions)',
165
+ '- EAP checklist',
166
+ '',
167
+ '## Available Tools',
168
+ '',
169
+ 'The plugin provides these tools that work in any serenity instance:',
170
+ '- **file_system**: root/resolve/list (no instance-specific coupling)',
171
+ '- **session_tool**: list/show/create/health/archive/summary',
172
+ '- **msm_list / msm_exec / msm_admin**: standard MSM management',
173
+ '',
174
+ '## Important',
175
+ '',
176
+ '- All 9 standard skills are already installed',
177
+ '- The AGENT_SESSIONS/ directory exists and session_tool is ready',
178
+ '- Guide the user through the 5 questions, then write the SKILL.md',
179
+ '- The SKILL.md should be fully usable — no placeholder text',
180
+ ].join('\n') + '\n';
181
+ }
182
+ // ── 主入口 ──
183
+ export async function initWizard(opts) {
184
+ const { targetPath, pluginRoot, force } = opts;
185
+ const targetDirName = basename(targetPath);
186
+ // 检查目标目录
187
+ if (existsSync(targetPath)) {
188
+ const hasFiles = existsSync(join(targetPath, '.serenity'));
189
+ if (hasFiles && !force) {
190
+ return {
191
+ success: false,
192
+ prefix: '',
193
+ name: '',
194
+ message: `Target already contains a serenity instance: ${targetPath}. Use --force to overwrite.`,
195
+ createdDirs: [],
196
+ installedSkills: [],
197
+ };
198
+ }
199
+ }
200
+ // Phase 1: 收集基本信息
201
+ const prefill = opts.prefix ?? inferPrefix(targetDirName);
202
+ let prefix;
203
+ let description;
204
+ if (opts.nonInteractive) {
205
+ prefix = prefill;
206
+ description = opts.description ?? 'Serenity instance';
207
+ }
208
+ else {
209
+ const info = await collectInfo(prefill);
210
+ prefix = info.prefix;
211
+ description = info.description;
212
+ }
213
+ // 验证 prefix
214
+ if (!/^[a-z][a-z0-9-]{0,19}$/.test(prefix)) {
215
+ return {
216
+ success: false, prefix: '', name: '',
217
+ message: `Invalid prefix "${prefix}": must be kebab-case (lowercase a-z, 0-9, dashes; max 20 chars)`,
218
+ createdDirs: [], installedSkills: [],
219
+ };
220
+ }
221
+ const name = opts.name ?? inferName(prefix);
222
+ const ph = defaultPlaceholders(prefix);
223
+ // Phase 1a: 创建目录骨架
224
+ const createdDirs = [];
225
+ const skeleton = buildSkeleton(prefix);
226
+ mkdirSync(targetPath, { recursive: true });
227
+ for (const item of skeleton) {
228
+ const fullPath = join(targetPath, item.path);
229
+ if (item.isDir) {
230
+ if (!existsSync(fullPath)) {
231
+ mkdirSync(fullPath, { recursive: true });
232
+ createdDirs.push(item.path);
233
+ }
234
+ }
235
+ else {
236
+ if (existsSync(fullPath) && !force)
237
+ continue;
238
+ const parentDir = fullPath.slice(0, fullPath.lastIndexOf('/'));
239
+ if (!existsSync(parentDir)) {
240
+ mkdirSync(parentDir, { recursive: true });
241
+ }
242
+ writeFileSync(fullPath, item.content, 'utf8');
243
+ createdDirs.push(item.path);
244
+ }
245
+ }
246
+ // Phase 1b: 复制标准技能模板
247
+ const templatesDir = getTemplatesDir(pluginRoot);
248
+ const installedSkills = [];
249
+ let templatesMissing = false;
250
+ for (const skillName of STANDARD_SKILLS) {
251
+ try {
252
+ const result = installTemplate({
253
+ templateDir: templatesDir,
254
+ name: skillName,
255
+ serenityRoot: targetPath,
256
+ prefix,
257
+ placeholders: ph,
258
+ dryRun: false,
259
+ });
260
+ if (result.changed) {
261
+ installedSkills.push(skillName);
262
+ }
263
+ }
264
+ catch {
265
+ // 模板缺失是允许的 — 用户可后续 install-skill
266
+ templatesMissing = true;
267
+ }
268
+ }
269
+ // Phase 1c: 写 .serenity 确认
270
+ const serenityPath = join(targetPath, '.serenity');
271
+ if (!existsSync(serenityPath) || force) {
272
+ writeFileSync(serenityPath, `${prefix}-serenity\n`, 'utf8');
273
+ }
274
+ // 输出
275
+ const skillSummary = installedSkills.length > 0
276
+ ? `Pre-installed ${installedSkills.length} skill(s): ${installedSkills.join(', ')}`
277
+ : 'No templates were copied (templates may not exist yet)';
278
+ return {
279
+ success: true,
280
+ prefix,
281
+ name,
282
+ message: [
283
+ `Serenity instance "${name}" created at ${targetPath}`,
284
+ ` prefix: ${prefix}`,
285
+ ` description: ${description}`,
286
+ ` ${skillSummary}`,
287
+ '',
288
+ `Next steps:`,
289
+ ` 1. Open ${targetPath} in OpenCode`,
290
+ ` 2. Agent will guide you through Phase 2 to complete the root skill`,
291
+ templatesMissing ? ' (Some skill templates not found — run `opencode-serenity-plugin install-skill <name>` to add them later)' : '',
292
+ ].filter(Boolean).join('\n'),
293
+ createdDirs,
294
+ installedSkills,
295
+ };
296
+ }
297
+ //# sourceMappingURL=init-wizard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-wizard.js","sourceRoot":"","sources":["../../src/init/init-wizard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * v1.11 — bin install CLI lib (pure logic, no side effects at import)
3
+ *
4
+ * 职责:
5
+ * - 检测 global config home (XDG_CONFIG_HOME / ~/.config / %APPDATA%)
6
+ * - 解析 install path → 两个 plugin entry (server + tui)
7
+ * - 读写 JSON config (atomic write via tmp + rename)
8
+ * - 幂等: 同一 plugin (id 或 abs path) 不重复添加
9
+ * - 追踪 _plugin_origins: 知道每个 entry 是哪个 plugin 装的、何时装、从哪装
10
+ *
11
+ * 与 src/util/tui-install.ts 的关系:
12
+ * - tui-install.ts 只管 global tui.json,只放 dist/tui.js,函数风格 imperative
13
+ * - install.ts 通用化: 既管 tui.json 也管 opencode.json,同时写多个 entry
14
+ * - 复用 tui-install.ts 的 toPluginSpec 思想 (realpath + file://)
15
+ *
16
+ * D23 (2026-06-07): 两 entry 架构不可破 — server entry 仅 project-level
17
+ * 加载,TUI entry 全局加载,保留 V2 非侵入语义。
18
+ */
19
+ export declare const PLUGIN_ID = "opencode-serenity-plugin";
20
+ export type PluginEntry = {
21
+ /** Plugin 逻辑 id (e.g. opencode-serenity-plugin) */
22
+ id: string;
23
+ /** 规范化的 file:// URL,用于 opencode 配置 */
24
+ path: string;
25
+ /** 绝对文件系统路径,用于 _plugin_origins 追踪 */
26
+ absPath: string;
27
+ };
28
+ export type ResolvedEntries = {
29
+ id: string;
30
+ server: PluginEntry;
31
+ tui: PluginEntry;
32
+ };
33
+ export type InstallResult = {
34
+ changed: boolean;
35
+ configPath: string;
36
+ error?: string;
37
+ addedPaths?: string[];
38
+ skippedPaths?: string[];
39
+ };
40
+ export type ConfigType = 'opencode.json' | 'tui.json';
41
+ /** 读出的 config 结构 (可包含 _plugin_origins 元数据) */
42
+ export type ReadConfig = {
43
+ [key: string]: unknown;
44
+ plugin?: unknown;
45
+ };
46
+ /**
47
+ * 解析 global opencode config 目录。
48
+ *
49
+ * 优先级:
50
+ * 1. $XDG_CONFIG_HOME/opencode (XDG Base Directory 规范,Linux/macOS)
51
+ * 2. $APPDATA/opencode (Windows 优先)
52
+ * 3. ~/AppData/Roaming/opencode (Windows 兜底)
53
+ * 4. ~/.config/opencode (Unix 兜底)
54
+ */
55
+ export declare function detectGlobalConfigHome(): string;
56
+ /** global config 文件路径 */
57
+ export declare function getGlobalConfigPath(filename: ConfigType): string;
58
+ /**
59
+ * 解析 install path → 两个 plugin entry。
60
+ *
61
+ * - installPath 应为 package 根目录 (含 dist/index.js + dist/tui.js)
62
+ * - 自动 realpath 解析 symlink (npm global install / pnpm link 都会建 symlink)
63
+ * - 绝对路径验证 (相对路径直接抛)
64
+ */
65
+ export declare function resolvePluginEntries(installPath: string): ResolvedEntries;
66
+ /**
67
+ * 从 bin 文件路径反推 install path (package 根目录)。
68
+ *
69
+ * 用于 CLI: bin 位于 <pkg>/bin/<name>.js,install path = <pkg>/。
70
+ * 自动 realpath 解析 symlink (npm global install 会建 symlink)。
71
+ */
72
+ export declare function resolveInstallPathFromBin(binFilePath: string): string;
73
+ /**
74
+ * 读 JSON config。文件不存在或为空 → 返回 {}。
75
+ *
76
+ * 严格模式 (v1.18 收口 — 旧 tui-install.ts 行为):
77
+ * - 文件不存在或空 → 返回 {} (允许新装)
78
+ * - JSON.parse 失败 → 抛 Error (writePluginEntry 捕获并返回 error, 不覆盖用户数据)
79
+ * - 根节点非 object (array / null / 标量) → 抛 Error
80
+ *
81
+ * 不抛的"宽松"行为会静默覆盖用户已损坏的 config, 风险高。
82
+ * 严格模式让 writePluginEntry 显式告诉用户 "config 损坏, 请人工修复"。
83
+ */
84
+ export declare function readJsonConfig(path: string): ReadConfig;
85
+ /**
86
+ * 原子写 JSON config。
87
+ *
88
+ * 流程: mkdir -p parent → 写 path.tmp → rename 到 path。
89
+ * rename 在同一文件系统下是原子操作,避免半写状态。
90
+ */
91
+ export declare function writeJsonConfig(path: string, data: ReadConfig): void;
92
+ /**
93
+ * 判定 entries 是否已安装。
94
+ *
95
+ * 只检查 plugin 数组:已有相同 file:// URL 的视为已安装 (string / tuple 形式)
96
+ * 不写 _plugin_origins — opencode 严格校验 config JSON schema,不认未知 key。
97
+ *
98
+ * 返回 true = 全部 entries 都不必再写 (no-op)
99
+ * 返回 false = 至少一个 entry 还没装
100
+ */
101
+ export declare function isAlreadyInstalled(entries: PluginEntry[], existing: ReadConfig): boolean;
102
+ /**
103
+ * 把 plugin entries 写入 config file 的 `plugin` 数组 (idempotent)。
104
+ *
105
+ * 行为契约:
106
+ * - 文件不存在 → 创建 (含 $schema + plugin 数组)
107
+ * - 已有 plugin 数组 → 保留,append 不重复 entry
108
+ * - 已有非 plugin 字段 → 完全保留
109
+ * - 写失败 → 返回 { changed: false, error },不抛
110
+ *
111
+ * 注意: 不写 _plugin_origins 等非标准 key — opencode 严格校验 config schema。
112
+ *
113
+ * @param configPath 目标 config 文件绝对路径
114
+ * @param entries 要注册的 plugin entries (server/tui 之一或两者)
115
+ */
116
+ export declare function writePluginEntry(configPath: string, entries: PluginEntry[]): InstallResult;
117
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAcH,eAAO,MAAM,SAAS,6BAA6B,CAAC;AAOpD,MAAM,MAAM,WAAW,GAAG;IACxB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,EAAE,WAAW,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,UAAU,CAAC;AAEtD,8CAA8C;AAC9C,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAIF;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAa/C;AAED,yBAAyB;AACzB,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAoBzE;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAOrE;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAsBvD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAKpE;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,UAAU,GACnB,OAAO,CAWT;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,EAAE,GACrB,aAAa,CA4Df"}