@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,36 @@
1
+ /**
2
+ * install-skill.ts — D2: 技能安装命令处理器
3
+ *
4
+ * CLI: opencode-serenity-plugin install-skill <name> [--prefix=<str>] [--include=<feat>] [--dry-run]
5
+ *
6
+ * 流程:
7
+ * 1. 确定 prefix(从 --prefix 或 .serenity)
8
+ * 2. 找到 plugin 的 src/templates/<name>/
9
+ * 3. 加载模板,替换占位符
10
+ * 4. 写入 <serenityRoot>/.opencode/skills/<prefix>-<name>/
11
+ * 5. 注册 MSM(若 manifest 有声明)
12
+ * 6. 输出结果
13
+ */
14
+ export interface InstallSkillOptions {
15
+ /** plugin 根目录(resolveInstallPathFromBin 的产出) */
16
+ pluginRoot: string;
17
+ /** 技能名(如 auth, pagedesigner) */
18
+ name: string;
19
+ /** 宁静号项目 cwd(用于定位 .serenity) */
20
+ cwd: string;
21
+ /** 技能前缀(可选,默认从 .serenity 推断) */
22
+ prefix?: string;
23
+ /** dry-run 模式 */
24
+ dryRun?: boolean;
25
+ }
26
+ export interface InstallSkillResult {
27
+ success: boolean;
28
+ skillDir?: string;
29
+ createdFiles?: string[];
30
+ message: string;
31
+ }
32
+ /**
33
+ * 主入口:安装技能到 serenity 实例
34
+ */
35
+ export declare function installSkill(opts: InstallSkillOptions): InstallSkillResult;
36
+ //# sourceMappingURL=install-skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skill.d.ts","sourceRoot":"","sources":["../../src/skills/install-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,kBAAkB,CA6E1E"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * install-skill.ts — D2: 技能安装命令处理器
3
+ *
4
+ * CLI: opencode-serenity-plugin install-skill <name> [--prefix=<str>] [--include=<feat>] [--dry-run]
5
+ *
6
+ * 流程:
7
+ * 1. 确定 prefix(从 --prefix 或 .serenity)
8
+ * 2. 找到 plugin 的 src/templates/<name>/
9
+ * 3. 加载模板,替换占位符
10
+ * 4. 写入 <serenityRoot>/.opencode/skills/<prefix>-<name>/
11
+ * 5. 注册 MSM(若 manifest 有声明)
12
+ * 6. 输出结果
13
+ */
14
+ import { existsSync } from 'node:fs';
15
+ import { findSerenityRootSafe, readSerenityInstanceName, } from '../fs/resolve-path.js';
16
+ import { getTemplatesDir, listAvailableTemplates, installTemplate, } from './template-loader.js';
17
+ /**
18
+ * 主入口:安装技能到 serenity 实例
19
+ */
20
+ export function installSkill(opts) {
21
+ const { pluginRoot, name, cwd, dryRun } = opts;
22
+ // 1. 验证 .serenity 存在
23
+ const serenityRoot = findSerenityRootSafe(cwd);
24
+ if (!serenityRoot) {
25
+ return {
26
+ success: false,
27
+ message: `Not inside a serenity instance: no .serenity found from ${cwd}`,
28
+ };
29
+ }
30
+ // 2. 确定 prefix
31
+ const prefix = opts.prefix ?? readSerenityInstanceName(serenityRoot);
32
+ if (!prefix) {
33
+ return {
34
+ success: false,
35
+ message: `Failed to determine instance prefix: .serenity at ${serenityRoot} is empty or unreadable`,
36
+ };
37
+ }
38
+ // 3. 验证模板存在
39
+ const templatesDir = getTemplatesDir(pluginRoot);
40
+ if (!existsSync(templatesDir)) {
41
+ return {
42
+ success: false,
43
+ message: `Templates directory not found: ${templatesDir} (plugin not built or src/ missing)`,
44
+ };
45
+ }
46
+ const available = listAvailableTemplates(templatesDir);
47
+ if (!available.includes(name)) {
48
+ const hint = available.length > 0
49
+ ? `Available templates: ${available.join(', ')}`
50
+ : 'No templates available.';
51
+ return {
52
+ success: false,
53
+ message: `Template "${name}" not found. ${hint}`,
54
+ };
55
+ }
56
+ // 4. 安装模板
57
+ try {
58
+ const result = installTemplate({
59
+ templateDir: templatesDir,
60
+ name,
61
+ serenityRoot,
62
+ prefix,
63
+ dryRun,
64
+ });
65
+ if (result.error) {
66
+ return { success: false, message: result.error };
67
+ }
68
+ if (!result.changed) {
69
+ return {
70
+ success: true,
71
+ skillDir: result.skillDirName,
72
+ createdFiles: [],
73
+ message: `Already installed: ${result.skillDirName} (no changes)`,
74
+ };
75
+ }
76
+ const fileCount = result.createdFiles.length;
77
+ return {
78
+ success: true,
79
+ skillDir: result.skillDirName,
80
+ createdFiles: result.createdFiles,
81
+ message: dryRun
82
+ ? `[dry-run] Would install: ${result.skillDirName}/ (${fileCount} file(s))`
83
+ : `Installed: ${result.skillDirName}/ (${fileCount} file(s))`,
84
+ };
85
+ }
86
+ catch (err) {
87
+ const msg = err instanceof Error ? err.message : String(err);
88
+ return { success: false, message: `Install failed: ${msg}` };
89
+ }
90
+ }
91
+ //# sourceMappingURL=install-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skill.js","sourceRoot":"","sources":["../../src/skills/install-skill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * template-loader.ts — 技能模板加载器(v0.1 D2)
3
+ *
4
+ * 职责:
5
+ * 1. 从 plugin 的 src/templates/<name>/ 目录加载模板
6
+ * 2. 替换占位符({{prefix}}, {{instance_name}}, {{date}})
7
+ * 3. 输出目标路径 → 文件内容映射
8
+ */
9
+ export interface TemplateManifest {
10
+ name: string;
11
+ eal?: string;
12
+ description: string;
13
+ /** 依赖的其他技能名 */
14
+ dependencies?: string[];
15
+ /** 需要注册的 MSM 列表 */
16
+ msm?: Array<{
17
+ name: string;
18
+ path: string;
19
+ description: string;
20
+ category: 'mech' | 'semi-mech';
21
+ }>;
22
+ version?: string;
23
+ }
24
+ export interface TemplateFiles {
25
+ /** 根目录下要创建的文件(key = 相对路径, value = 内容) */
26
+ files: Record<string, string>;
27
+ /** 模板清单 */
28
+ manifest: TemplateManifest;
29
+ }
30
+ export interface Placeholders {
31
+ prefix: string;
32
+ instanceName: string;
33
+ date: string;
34
+ }
35
+ export declare function defaultPlaceholders(prefix: string): Placeholders;
36
+ /**
37
+ * 返回 plugin 内置模板的 src/templates/ 目录路径。
38
+ * 开发模式:相对于 bin/ 脚本的安装路径。
39
+ */
40
+ export declare function getTemplatesDir(pluginRoot: string): string;
41
+ /**
42
+ * 列出所有可用模板名
43
+ */
44
+ export declare function listAvailableTemplates(templatesDir: string): string[];
45
+ /**
46
+ * 加载模板目录,返回文件映射(已替换占位符)。
47
+ * 跳过 manifest.yaml 本身(不作为目标文件)。
48
+ */
49
+ export declare function loadTemplate(templatesDir: string, name: string, placeholders: Placeholders): TemplateFiles;
50
+ export interface InstallTemplateOptions {
51
+ /** 源模板路径(plugin 的 src/templates/<name>/) */
52
+ templateDir: string;
53
+ /** 模板名 */
54
+ name: string;
55
+ /** 目标 serenity 根路径(cwdRoot) */
56
+ serenityRoot: string;
57
+ /** 实例前缀 */
58
+ prefix: string;
59
+ /** 占位符(可选,不传则用默认值) */
60
+ placeholders?: Placeholders;
61
+ /** dry-run 模式 */
62
+ dryRun?: boolean;
63
+ }
64
+ export interface InstallTemplateResult {
65
+ /** 是否做了实际写入 */
66
+ changed: boolean;
67
+ /** 安装的目标技能目录名(如 "prefix-auth") */
68
+ skillDirName: string;
69
+ /** 创建的文件列表 */
70
+ createdFiles: string[];
71
+ /** 错误信息(可选) */
72
+ error?: string;
73
+ }
74
+ /**
75
+ * 安装技能模板到 serenity 实例。
76
+ * 输出到 <serenityRoot>/.opencode/skills/<prefix>-<name>/
77
+ */
78
+ export declare function installTemplate(opts: InstallTemplateOptions): InstallTemplateResult;
79
+ //# sourceMappingURL=template-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-loader.d.ts","sourceRoot":"","sources":["../../src/skills/template-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe;IACf,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,mBAAmB;IACnB,GAAG,CAAC,EAAE,KAAK,CAAC;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAC;KAChC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,WAAW;IACX,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAOhE;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CASrE;AAiBD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,GACzB,aAAa,CA0Cf;AA0BD,MAAM,WAAW,sBAAsB;IACrC,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU;IACV,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,qBAAqB,CA8CnF"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * template-loader.ts — 技能模板加载器(v0.1 D2)
3
+ *
4
+ * 职责:
5
+ * 1. 从 plugin 的 src/templates/<name>/ 目录加载模板
6
+ * 2. 替换占位符({{prefix}}, {{instance_name}}, {{date}})
7
+ * 3. 输出目标路径 → 文件内容映射
8
+ */
9
+ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ // ── 默认值 ──
12
+ export function defaultPlaceholders(prefix) {
13
+ const now = new Date();
14
+ return {
15
+ prefix,
16
+ instanceName: `${prefix}-serenity`,
17
+ date: now.toISOString().slice(0, 10),
18
+ };
19
+ }
20
+ // ── 模板发现 ──
21
+ /**
22
+ * 返回 plugin 内置模板的 src/templates/ 目录路径。
23
+ * 开发模式:相对于 bin/ 脚本的安装路径。
24
+ */
25
+ export function getTemplatesDir(pluginRoot) {
26
+ return join(pluginRoot, 'src', 'templates');
27
+ }
28
+ /**
29
+ * 列出所有可用模板名
30
+ */
31
+ export function listAvailableTemplates(templatesDir) {
32
+ try {
33
+ return readdirSync(templatesDir, { withFileTypes: true })
34
+ .filter((e) => e.isDirectory())
35
+ .map((e) => e.name)
36
+ .sort();
37
+ }
38
+ catch {
39
+ return [];
40
+ }
41
+ }
42
+ // ── 模板加载 ──
43
+ const PLACEHOLDER_PATTERN = /\{\{(\w+)\}\}/g;
44
+ function replacePlaceholders(content, placeholders) {
45
+ return content.replace(PLACEHOLDER_PATTERN, (_, key) => {
46
+ switch (key) {
47
+ case 'prefix': return placeholders.prefix;
48
+ case 'instance_name': return placeholders.instanceName;
49
+ case 'date': return placeholders.date;
50
+ default: return `{{${key}}}`; // 保持未知占位符不变
51
+ }
52
+ });
53
+ }
54
+ /**
55
+ * 加载模板目录,返回文件映射(已替换占位符)。
56
+ * 跳过 manifest.yaml 本身(不作为目标文件)。
57
+ */
58
+ export function loadTemplate(templatesDir, name, placeholders) {
59
+ const templatePath = join(templatesDir, name);
60
+ if (!existsSync(templatePath)) {
61
+ throw new Error(`Template not found: "${name}" at ${templatePath}`);
62
+ }
63
+ if (!statSync(templatePath).isDirectory()) {
64
+ throw new Error(`Template path is not a directory: ${templatePath}`);
65
+ }
66
+ // 读取 manifest
67
+ const manifestPath = join(templatePath, 'manifest.yaml');
68
+ let manifest;
69
+ if (existsSync(manifestPath)) {
70
+ const raw = readFileSync(manifestPath, 'utf8');
71
+ manifest = parseManifest(raw, name);
72
+ }
73
+ else {
74
+ manifest = { name, description: `Skill: ${name}` };
75
+ }
76
+ // 收集所有文件(递归)
77
+ const files = {};
78
+ function collectFiles(dir, relativeDir) {
79
+ const entries = readdirSync(dir, { withFileTypes: true });
80
+ for (const entry of entries) {
81
+ if (entry.name === 'manifest.yaml')
82
+ continue; // 跳过 manifest
83
+ const fullPath = join(dir, entry.name);
84
+ const relPath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
85
+ if (entry.isDirectory()) {
86
+ collectFiles(fullPath, relPath);
87
+ }
88
+ else if (entry.isFile()) {
89
+ const content = readFileSync(fullPath, 'utf8');
90
+ files[relPath] = replacePlaceholders(content, placeholders);
91
+ }
92
+ }
93
+ }
94
+ collectFiles(templatePath, '');
95
+ return { files, manifest };
96
+ }
97
+ // ── Manifest 解析(简单 yaml 解析) ──
98
+ function parseManifest(raw, fallbackName) {
99
+ const result = { name: fallbackName, description: '' };
100
+ for (const line of raw.split('\n')) {
101
+ const trimmed = line.trim();
102
+ if (!trimmed || trimmed.startsWith('#'))
103
+ continue;
104
+ const colonIdx = trimmed.indexOf(':');
105
+ if (colonIdx === -1)
106
+ continue;
107
+ const key = trimmed.slice(0, colonIdx).trim();
108
+ const value = trimmed.slice(colonIdx + 1).trim();
109
+ switch (key) {
110
+ case 'name':
111
+ result.name = value;
112
+ break;
113
+ case 'eal':
114
+ result.eal = value;
115
+ break;
116
+ case 'description':
117
+ result.description = value;
118
+ break;
119
+ case 'version':
120
+ result.version = value;
121
+ break;
122
+ }
123
+ }
124
+ return result;
125
+ }
126
+ /**
127
+ * 安装技能模板到 serenity 实例。
128
+ * 输出到 <serenityRoot>/.opencode/skills/<prefix>-<name>/
129
+ */
130
+ export function installTemplate(opts) {
131
+ const { templateDir, name, serenityRoot, prefix, dryRun } = opts;
132
+ const ph = opts.placeholders ?? defaultPlaceholders(prefix);
133
+ // 加载模板
134
+ const { files } = loadTemplate(templateDir, name, ph);
135
+ // 目标路径
136
+ const targetDir = join(serenityRoot, '.opencode', 'skills', `${prefix}-${name}`);
137
+ const createdFiles = [];
138
+ if (dryRun) {
139
+ return {
140
+ changed: true,
141
+ skillDirName: `${prefix}-${name}`,
142
+ createdFiles: Object.keys(files),
143
+ };
144
+ }
145
+ // 写入文件
146
+ let hasChanges = false;
147
+ for (const [relPath, content] of Object.entries(files)) {
148
+ const targetFile = join(targetDir, relPath);
149
+ const parentDir = targetFile.slice(0, targetFile.lastIndexOf('/'));
150
+ // 检查是否已存在且内容一致
151
+ if (existsSync(targetFile)) {
152
+ const existing = readFileSync(targetFile, 'utf8');
153
+ if (existing === content)
154
+ continue;
155
+ }
156
+ // 创建父目录
157
+ if (!existsSync(parentDir)) {
158
+ mkdirSync(parentDir, { recursive: true });
159
+ }
160
+ writeFileSync(targetFile, content, 'utf8');
161
+ createdFiles.push(relPath);
162
+ hasChanges = true;
163
+ }
164
+ return {
165
+ changed: hasChanges,
166
+ skillDirName: `${prefix}-${name}`,
167
+ createdFiles,
168
+ };
169
+ }
170
+ //# sourceMappingURL=template-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-loader.js","sourceRoot":"","sources":["../../src/skills/template-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 全局激活状态 — v0.1 两阶段 init 支持
3
+ *
4
+ * 状态机(ReadyStateMachine):
5
+ * idle → loading → ready / error / disabled
6
+ *
7
+ * 维持旧 API 兼容:
8
+ * - getState() / setState() / isActive() / resetState() 不变
9
+ *
10
+ * 新增 v0.1 API:
11
+ * - getReadyMachine() 拿状态机
12
+ * - ensureReady() tools/hooks execute 阻塞等待激活完成
13
+ *
14
+ * 注意:plugin 入口的 input 是 opencode 调用时传入的,Hooks 闭包要访问状态
15
+ * —— 用模块级 singleton 缓存。
16
+ */
17
+ import { type SerenityState } from './types/index.js';
18
+ import { ReadyStateMachine } from './util/ready-state.js';
19
+ /** 写入激活状态(仅 activation.ts 调用) */
20
+ export declare function setState(state: SerenityState): void;
21
+ /** 读取当前激活状态(其他模块用) */
22
+ export declare function getState(): SerenityState;
23
+ /** 是否已激活(最常用判定,**同步快路径**) */
24
+ export declare function isActive(): boolean;
25
+ /** 重置为不激活(测试用) */
26
+ export declare function resetState(): void;
27
+ /** v0.1:拿状态机(activation.ts / hooks 内部用) */
28
+ export declare function getReadyMachine(): ReadyStateMachine;
29
+ /** v0.1:tools/hooks execute 阻塞等待激活完成 */
30
+ export declare function ensureReady(): Promise<void>;
31
+ /** v0.1:把状态机推 ready(activation.ts 异步完成时调) */
32
+ export declare function markReady(): void;
33
+ /** v0.1:把状态机推 disabled(sync 阶段失败时调) */
34
+ export declare function markDisabled(reason: string): void;
35
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK1D,iCAAiC;AACjC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAMnD;AAED,sBAAsB;AACtB,wBAAgB,QAAQ,IAAI,aAAa,CAExC;AAED,6BAA6B;AAC7B,wBAAgB,QAAQ,IAAI,OAAO,CAElC;AAED,kBAAkB;AAClB,wBAAgB,UAAU,IAAI,IAAI,CAGjC;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,IAAI,iBAAiB,CAEnD;AAED,wCAAwC;AACxC,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD;AAED,6CAA6C;AAC7C,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAED,uCAAuC;AACvC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEjD"}
package/dist/state.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * 全局激活状态 — v0.1 两阶段 init 支持
3
+ *
4
+ * 状态机(ReadyStateMachine):
5
+ * idle → loading → ready / error / disabled
6
+ *
7
+ * 维持旧 API 兼容:
8
+ * - getState() / setState() / isActive() / resetState() 不变
9
+ *
10
+ * 新增 v0.1 API:
11
+ * - getReadyMachine() 拿状态机
12
+ * - ensureReady() tools/hooks execute 阻塞等待激活完成
13
+ *
14
+ * 注意:plugin 入口的 input 是 opencode 调用时传入的,Hooks 闭包要访问状态
15
+ * —— 用模块级 singleton 缓存。
16
+ */
17
+ import { INACTIVE_STATE } from './types/index.js';
18
+ import { ReadyStateMachine } from './util/ready-state.js';
19
+ let _state = INACTIVE_STATE;
20
+ const _machine = new ReadyStateMachine();
21
+ /** 写入激活状态(仅 activation.ts 调用) */
22
+ export function setState(state) {
23
+ _state = state;
24
+ if (state.activated) {
25
+ // 立即把状态机推到 ready(v0 路径:同步激活完成)
26
+ // 注:setState 自身不暴露 machine API;activation.ts 走的是 markReady 显式调用
27
+ }
28
+ }
29
+ /** 读取当前激活状态(其他模块用) */
30
+ export function getState() {
31
+ return _state;
32
+ }
33
+ /** 是否已激活(最常用判定,**同步快路径**) */
34
+ export function isActive() {
35
+ return _state.activated;
36
+ }
37
+ /** 重置为不激活(测试用) */
38
+ export function resetState() {
39
+ _state = INACTIVE_STATE;
40
+ _machine.reset();
41
+ }
42
+ /** v0.1:拿状态机(activation.ts / hooks 内部用) */
43
+ export function getReadyMachine() {
44
+ return _machine;
45
+ }
46
+ /** v0.1:tools/hooks execute 阻塞等待激活完成 */
47
+ export async function ensureReady() {
48
+ // 快路径:v0 同步激活完成,state.activated === true
49
+ if (_state.activated)
50
+ return;
51
+ // 慢路径:v0.1 异步激活,阻塞等待状态机
52
+ await _machine.whenReady();
53
+ }
54
+ /** v0.1:把状态机推 ready(activation.ts 异步完成时调) */
55
+ export function markReady() {
56
+ _machine.markAsReady();
57
+ }
58
+ /** v0.1:把状态机推 disabled(sync 阶段失败时调) */
59
+ export function markDisabled(reason) {
60
+ _machine.markDisabled(reason);
61
+ }
62
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAsB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,IAAI,MAAM,GAAkB,cAAc,CAAC;AAC3C,MAAM,QAAQ,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAEzC,iCAAiC;AACjC,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,GAAG,KAAK,CAAC;IACf,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,+BAA+B;QAC/B,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,sBAAsB;AACtB,MAAM,UAAU,QAAQ;IACtB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,QAAQ;IACtB,OAAO,MAAM,CAAC,SAAS,CAAC;AAC1B,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,UAAU;IACxB,MAAM,GAAG,cAAc,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,eAAe;IAC7B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,yCAAyC;IACzC,IAAI,MAAM,CAAC,SAAS;QAAE,OAAO;IAC7B,wBAAwB;IACxB,MAAM,QAAQ,CAAC,SAAS,EAAE,CAAC;AAC7B,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,SAAS;IACvB,QAAQ,CAAC,WAAW,EAAE,CAAC;AACzB,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC"}
package/dist/tui.d.ts ADDED
@@ -0,0 +1,61 @@
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 type { TuiPlugin } from '@opencode-ai/plugin/tui';
56
+ declare const _default: {
57
+ id: string;
58
+ tui: TuiPlugin;
59
+ };
60
+ export default _default;
61
+ //# sourceMappingURL=tui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;;;;;AAwOzD,wBAGE"}