@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,22 @@
1
+ /**
2
+ * opencode.json 启动时自检(v1.5 init-check)
3
+ *
4
+ * 目标:plugin 启动时校验主仓 opencode.json 的关键配置。
5
+ * 行为:**只警告,不 patch**(用户明确要求"不通过修改配置实现功能")。
6
+ *
7
+ * 检查项:
8
+ * 1. opencode.json 存在
9
+ * 2. 顶层 default_agent 与 expectedInstanceName 匹配
10
+ * 3. agent 字典有 expectedInstanceName 条目
11
+ * 4. plugin 字段包含 opencode-serenity-plugin
12
+ *
13
+ * 失败:log.warn(plugin 不工作 / 用户/LLM 看 stderr)
14
+ * 成功:log.info
15
+ */
16
+ export type CheckResult = {
17
+ ok: boolean;
18
+ warnings: string[];
19
+ details: Record<string, unknown>;
20
+ };
21
+ export declare function checkSerenityConfig(cwdRoot: string, expectedInstanceName: string): CheckResult;
22
+ //# sourceMappingURL=init-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-check.d.ts","sourceRoot":"","sources":["../../src/util/init-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,oBAAoB,EAAE,MAAM,GAC3B,WAAW,CA4Db"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * opencode.json 启动时自检(v1.5 init-check)
3
+ *
4
+ * 目标:plugin 启动时校验主仓 opencode.json 的关键配置。
5
+ * 行为:**只警告,不 patch**(用户明确要求"不通过修改配置实现功能")。
6
+ *
7
+ * 检查项:
8
+ * 1. opencode.json 存在
9
+ * 2. 顶层 default_agent 与 expectedInstanceName 匹配
10
+ * 3. agent 字典有 expectedInstanceName 条目
11
+ * 4. plugin 字段包含 opencode-serenity-plugin
12
+ *
13
+ * 失败:log.warn(plugin 不工作 / 用户/LLM 看 stderr)
14
+ * 成功:log.info
15
+ */
16
+ import { readFileSync, existsSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+ import { log } from './log.js';
19
+ export function checkSerenityConfig(cwdRoot, expectedInstanceName) {
20
+ const warnings = [];
21
+ const details = { cwdRoot, expectedInstanceName };
22
+ const configPath = join(cwdRoot, 'opencode.json');
23
+ if (!existsSync(configPath)) {
24
+ warnings.push(`opencode.json not found at "${configPath}"`);
25
+ log.warn('init-check', 'opencode.json not found', { path: configPath });
26
+ return { ok: false, warnings, details };
27
+ }
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ let config;
30
+ try {
31
+ config = JSON.parse(readFileSync(configPath, 'utf8'));
32
+ }
33
+ catch (err) {
34
+ const reason = err instanceof Error ? err.message : String(err);
35
+ warnings.push(`opencode.json parse error: ${reason}`);
36
+ log.warn('init-check', 'opencode.json parse error', { path: configPath, err: reason });
37
+ return { ok: false, warnings, details };
38
+ }
39
+ // 1. default_agent
40
+ if (!config.default_agent) {
41
+ warnings.push("opencode.json missing 'default_agent'");
42
+ }
43
+ else if (config.default_agent !== expectedInstanceName) {
44
+ warnings.push(`default_agent is "${config.default_agent}", expected "${expectedInstanceName}"`);
45
+ }
46
+ details.default_agent = config.default_agent;
47
+ // 2. agent 字典
48
+ if (!config.agent || typeof config.agent !== 'object') {
49
+ warnings.push("opencode.json missing 'agent' dictionary");
50
+ }
51
+ else if (!config.agent[expectedInstanceName]) {
52
+ warnings.push(`agent dictionary missing entry for "${expectedInstanceName}"`);
53
+ }
54
+ details.has_instance_agent = !!(config.agent && config.agent[expectedInstanceName]);
55
+ // 3. plugin 字段
56
+ if (!Array.isArray(config.plugin) || config.plugin.length === 0) {
57
+ warnings.push("opencode.json missing 'plugin' array");
58
+ }
59
+ else {
60
+ const hasSerenityPlugin = config.plugin.some(
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ (p) => typeof p === 'string' && p.includes('opencode-serenity-plugin'));
63
+ if (!hasSerenityPlugin) {
64
+ warnings.push("plugin array does not include opencode-serenity-plugin");
65
+ }
66
+ }
67
+ details.plugin_count = Array.isArray(config.plugin) ? config.plugin.length : 0;
68
+ for (const w of warnings) {
69
+ log.warn('init-check', w, { configPath });
70
+ }
71
+ if (warnings.length === 0) {
72
+ log.info('init-check', 'all checks passed', details);
73
+ }
74
+ return { ok: warnings.length === 0, warnings, details };
75
+ }
76
+ //# sourceMappingURL=init-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-check.js","sourceRoot":"","sources":["../../src/util/init-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * RR7 init — slash command `/serenity-init` 业务逻辑
3
+ *
4
+ * v1.10:把"非 serenity 目录无法被 plugin 识别"的死循环打破。
5
+ * 触发:TUI slash command onSelect → DialogPrompt → onConfirm → initSerenity
6
+ *
7
+ * 设计约束(RR7 spec §5 失败矩阵):
8
+ * - prefix 不合法 → throw InvalidInstanceNameError
9
+ * - cwd 不在 git repo → throw NotInGitRepoError(透传 findGitRoot)
10
+ * - /.serenity 已存在 → return { kind: 'already', name }(不覆盖)
11
+ * - git add/commit 失败 → rollback 写文件 + throw InitGitCommitError
12
+ *
13
+ * 与 util/serenity-file.ts 协作:
14
+ * - serenityFileExists 探测 / writeSerenityFile 落盘 / removeSerenityFile rollback
15
+ * - readSerenityFile 用于 "already" 路径回读现有实例名
16
+ */
17
+ /** v1.10 RR7 命名模型:所有实例名都带 -serenity 后缀 */
18
+ export declare const SERENITY_SUFFIX = "-serenity";
19
+ /** 拼实例名 = `${prefix}-serenity` */
20
+ export declare function buildInstanceName(prefix: string): string;
21
+ /**
22
+ * 验证 prefix 是合法 kebab-case(同 instanceName 规则)。
23
+ * 独立函数(不委托 isValidInstanceName)以保持语义清晰。
24
+ */
25
+ export declare function isValidPrefix(prefix: string): boolean;
26
+ /**
27
+ * 智能默认 prefix(见设计文档 §3 表格):
28
+ * 1. lowercase
29
+ * 2. 非 [a-z0-9] 字符 → '-'
30
+ * 3. 折叠多 '-'、去首尾 '-'
31
+ * 4. 若结果以 '-serenity' 结尾且剩余 prefix 合法 → 剥后缀
32
+ *
33
+ * 边界 case:
34
+ * - 空字符串 → ''(无字符可剥,保持空)
35
+ * - '---serenity' → ''(剥后缀后空)
36
+ * - 'tg-serenity' → 'tg'
37
+ * - 'myproject' → 'myproject'(无 -serenity 后缀)
38
+ */
39
+ export declare function defaultPrefix(dirName: string): string;
40
+ export type InitResult = {
41
+ kind: 'created';
42
+ name: string;
43
+ } | {
44
+ kind: 'already';
45
+ name: string;
46
+ };
47
+ /**
48
+ * 初始化 cwd 为 serenity 实例(RR7)。
49
+ * @throws InvalidInstanceNameError prefix 不是 kebab-case
50
+ * @throws NotInGitRepoError cwd 不在 git repo(透传 findGitRoot)
51
+ * @throws InitGitCommitError git add/commit 失败(已 rollback 写文件)
52
+ */
53
+ export declare function initSerenity(cwd: string, prefix: string): Promise<InitResult>;
54
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/util/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAcH,0CAA0C;AAC1C,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,kCAAkC;AAClC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWrD;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAwBnF"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * RR7 init — slash command `/serenity-init` 业务逻辑
3
+ *
4
+ * v1.10:把"非 serenity 目录无法被 plugin 识别"的死循环打破。
5
+ * 触发:TUI slash command onSelect → DialogPrompt → onConfirm → initSerenity
6
+ *
7
+ * 设计约束(RR7 spec §5 失败矩阵):
8
+ * - prefix 不合法 → throw InvalidInstanceNameError
9
+ * - cwd 不在 git repo → throw NotInGitRepoError(透传 findGitRoot)
10
+ * - /.serenity 已存在 → return { kind: 'already', name }(不覆盖)
11
+ * - git add/commit 失败 → rollback 写文件 + throw InitGitCommitError
12
+ *
13
+ * 与 util/serenity-file.ts 协作:
14
+ * - serenityFileExists 探测 / writeSerenityFile 落盘 / removeSerenityFile rollback
15
+ * - readSerenityFile 用于 "already" 路径回读现有实例名
16
+ */
17
+ import { findGitRoot, gitAddAndCommit } from './git.js';
18
+ import { serenityFileExists, writeSerenityFile, removeSerenityFile, readSerenityFile, } from './serenity-file.js';
19
+ import { InvalidInstanceNameError, InitGitCommitError, } from '../errors.js';
20
+ /** v1.10 RR7 命名模型:所有实例名都带 -serenity 后缀 */
21
+ export const SERENITY_SUFFIX = '-serenity';
22
+ /** 拼实例名 = `${prefix}-serenity` */
23
+ export function buildInstanceName(prefix) {
24
+ return `${prefix}${SERENITY_SUFFIX}`;
25
+ }
26
+ /**
27
+ * 验证 prefix 是合法 kebab-case(同 instanceName 规则)。
28
+ * 独立函数(不委托 isValidInstanceName)以保持语义清晰。
29
+ */
30
+ export function isValidPrefix(prefix) {
31
+ return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(prefix);
32
+ }
33
+ /**
34
+ * 智能默认 prefix(见设计文档 §3 表格):
35
+ * 1. lowercase
36
+ * 2. 非 [a-z0-9] 字符 → '-'
37
+ * 3. 折叠多 '-'、去首尾 '-'
38
+ * 4. 若结果以 '-serenity' 结尾且剩余 prefix 合法 → 剥后缀
39
+ *
40
+ * 边界 case:
41
+ * - 空字符串 → ''(无字符可剥,保持空)
42
+ * - '---serenity' → ''(剥后缀后空)
43
+ * - 'tg-serenity' → 'tg'
44
+ * - 'myproject' → 'myproject'(无 -serenity 后缀)
45
+ */
46
+ export function defaultPrefix(dirName) {
47
+ const kebab = dirName
48
+ .toLowerCase()
49
+ .replace(/[^a-z0-9]+/g, '-')
50
+ .replace(/^-+|-+$/g, '');
51
+ if (kebab === '')
52
+ return '';
53
+ if (kebab.endsWith(SERENITY_SUFFIX)) {
54
+ const stripped = kebab.slice(0, -SERENITY_SUFFIX.length);
55
+ if (isValidPrefix(stripped))
56
+ return stripped;
57
+ }
58
+ return kebab;
59
+ }
60
+ /**
61
+ * 初始化 cwd 为 serenity 实例(RR7)。
62
+ * @throws InvalidInstanceNameError prefix 不是 kebab-case
63
+ * @throws NotInGitRepoError cwd 不在 git repo(透传 findGitRoot)
64
+ * @throws InitGitCommitError git add/commit 失败(已 rollback 写文件)
65
+ */
66
+ export async function initSerenity(cwd, prefix) {
67
+ if (!isValidPrefix(prefix)) {
68
+ throw new InvalidInstanceNameError(prefix);
69
+ }
70
+ const gitRoot = findGitRoot(cwd);
71
+ if (serenityFileExists(gitRoot)) {
72
+ const name = readSerenityFile(gitRoot);
73
+ return { kind: 'already', name };
74
+ }
75
+ const name = buildInstanceName(prefix);
76
+ writeSerenityFile(gitRoot, name);
77
+ try {
78
+ gitAddAndCommit(gitRoot, '.serenity', `chore: init serenity (${name})`);
79
+ }
80
+ catch (err) {
81
+ removeSerenityFile(gitRoot);
82
+ const reason = err instanceof Error ? err.message : String(err);
83
+ throw new InitGitCommitError(reason);
84
+ }
85
+ return { kind: 'created', name };
86
+ }
87
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/util/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAEtB,0CAA0C;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,kCAAkC;AAClC,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,GAAG,MAAM,GAAG,eAAe,EAAE,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAG,OAAO;SAClB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,KAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,aAAa,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAMD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,MAAc;IAC5D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACvC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACvC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEjC,IAAI,CAAC;QACH,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,yBAAyB,IAAI,GAAG,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 集中日志工具(v0.0.1: 全部 no-op,release 静默)
3
+ *
4
+ * v0.0.1 设计: release 版本应**完全静默**。
5
+ * 错误可见性走 SDK hook 的 throw(→ opencode error UI)或 permission prompt(→ 用户 UI)。
6
+ *
7
+ * 调试方式(任一):
8
+ * 1. 临时改本文件,把 noop 换成 console.error
9
+ * 2. `pnpm test` 看 vitest 输出
10
+ * 3. 在具体 call site 加 `throw new Error(...)` 临时透出问题
11
+ *
12
+ * 用法保持向后兼容(65 处 call site 不变):
13
+ * import { log } from './util/log.js';
14
+ * log.info('msm', 'calling msm_exec'); // → no-op
15
+ * log.warn('phase2', 'RR1 failed'); // → no-op
16
+ * log.error('permission', 'blocked'); // → no-op
17
+ * log.debug('event', '...'); // → no-op
18
+ */
19
+ export declare const log: {
20
+ info: (tag: string, msg: string, data?: Record<string, unknown>) => void;
21
+ warn: (tag: string, msg: string, data?: Record<string, unknown>) => void;
22
+ error: (tag: string, msg: string, data?: Record<string, unknown>) => void;
23
+ debug: (tag: string, msg: string, data?: Record<string, unknown>) => void;
24
+ };
25
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/util/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,eAAO,MAAM,GAAG;gBACF,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBACnD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;iBAClD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;iBACnD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACjE,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 集中日志工具(v0.0.1: 全部 no-op,release 静默)
3
+ *
4
+ * v0.0.1 设计: release 版本应**完全静默**。
5
+ * 错误可见性走 SDK hook 的 throw(→ opencode error UI)或 permission prompt(→ 用户 UI)。
6
+ *
7
+ * 调试方式(任一):
8
+ * 1. 临时改本文件,把 noop 换成 console.error
9
+ * 2. `pnpm test` 看 vitest 输出
10
+ * 3. 在具体 call site 加 `throw new Error(...)` 临时透出问题
11
+ *
12
+ * 用法保持向后兼容(65 处 call site 不变):
13
+ * import { log } from './util/log.js';
14
+ * log.info('msm', 'calling msm_exec'); // → no-op
15
+ * log.warn('phase2', 'RR1 failed'); // → no-op
16
+ * log.error('permission', 'blocked'); // → no-op
17
+ * log.debug('event', '...'); // → no-op
18
+ */
19
+ function noop(_level, _tag, _msg, _data) {
20
+ // v0.0.1: intentionally empty
21
+ }
22
+ export const log = {
23
+ info: (tag, msg, data) => noop('info', tag, msg, data),
24
+ warn: (tag, msg, data) => noop('warn', tag, msg, data),
25
+ error: (tag, msg, data) => noop('error', tag, msg, data),
26
+ debug: (tag, msg, data) => noop('debug', tag, msg, data),
27
+ };
28
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/util/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,SAAS,IAAI,CAAC,MAAa,EAAE,IAAY,EAAE,IAAY,EAAE,KAA+B;IACtF,8BAA8B;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAE,GAAW,EAAE,IAA8B,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC;IAChG,IAAI,EAAE,CAAC,GAAW,EAAE,GAAW,EAAE,IAA8B,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC;IAChG,KAAK,EAAE,CAAC,GAAW,EAAE,GAAW,EAAE,IAA8B,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC;IAClG,KAAK,EAAE,CAAC,GAAW,EAAE,GAAW,EAAE,IAA8B,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC;CACnG,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * msm-call.ts — plugin 端 msm_exec in-process 委托 (S028)
3
+ *
4
+ * 设计:将 plugin 的 msm_exec tool 调用委托给 plugin 仓内 msm-exec-runtime
5
+ * (零 spawn, 零外部依赖). S028 反转 S024/D26, plugin 完全自包含.
6
+ *
7
+ * 历史:
8
+ * - v0.0.2 (S024 v1.14): 薄包装, spawn serenity 仓 msm-exec.ts
9
+ * - v0.0.3 (S028): in-process 委托 plugin 仓内 msm-exec-runtime
10
+ *
11
+ * 与 msm.ts 的 msmExecTool 关系:
12
+ * - msmExecTool 接受 msm_name + args (string[])
13
+ * - msmExecTool 在 cwdRoot 注册表 path 1 校验 entry, path-arg 校验
14
+ * - msmExecTool 调 callMsmExec({msm_name, businessArgs})
15
+ * - callMsmExec 计算 path 1 (与 msm.ts 同源) 传给 runMsmExec
16
+ * - runMsmExec 用同一份注册表查找 msm + spawn 业务
17
+ *
18
+ * S028 v0.0.3 收口:
19
+ * - 业务流统一走 cwdRoot 注册表 (避免 plugin-root vs cwd-root 双源漂移)
20
+ * - plugin-root 注册表仅供 CLI 调试 (D9/D10/D6 保留为 fallback, msm-exec-runtime 自动 bootstrap)
21
+ */
22
+ export type MsmCallOptions = {
23
+ msm_name: string;
24
+ /** 业务 args 数组(已按 LLM 传入,原样传递) */
25
+ businessArgs: string[];
26
+ };
27
+ export type MsmCallResult = {
28
+ stdout: string;
29
+ stderr: string;
30
+ exitCode: number;
31
+ };
32
+ /**
33
+ * 调 msm_exec in-process (S028).
34
+ *
35
+ * 行为:
36
+ * - 直接 import plugin 仓 msm-exec-runtime.runMsmExec
37
+ * - 不 spawn 协议层子进程 (运行时本身就是 in-process 库函数)
38
+ * - 业务 msm spawn 内部由 runtime 完成, cwd = state.cwdRoot
39
+ * - 注册表路径 = cwdRoot 注册表 (与 msm.ts loadMechRegistry 同源)
40
+ * - 600s 超时 (与 v0.0.2 行为一致, 10 分钟)
41
+ *
42
+ * 错误映射:
43
+ * - 协议错误 (MsmExecError): 透传给 caller (msmExecTool 捕获并转 MsmExecutionError)
44
+ * - 超时: 抛 MsmTimeoutError (与 v0.0.2 行为一致)
45
+ * - 业务 msm 非 0 exit: 不抛, 通过 result.exitCode 透传 (msmExecTool 转 MsmExecutionError)
46
+ */
47
+ export declare function callMsmExec(opts: MsmCallOptions): Promise<MsmCallResult>;
48
+ //# sourceMappingURL=msm-call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msm-call.d.ts","sourceRoot":"","sources":["../../src/util/msm-call.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAaF;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAmC9E"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * msm-call.ts — plugin 端 msm_exec in-process 委托 (S028)
3
+ *
4
+ * 设计:将 plugin 的 msm_exec tool 调用委托给 plugin 仓内 msm-exec-runtime
5
+ * (零 spawn, 零外部依赖). S028 反转 S024/D26, plugin 完全自包含.
6
+ *
7
+ * 历史:
8
+ * - v0.0.2 (S024 v1.14): 薄包装, spawn serenity 仓 msm-exec.ts
9
+ * - v0.0.3 (S028): in-process 委托 plugin 仓内 msm-exec-runtime
10
+ *
11
+ * 与 msm.ts 的 msmExecTool 关系:
12
+ * - msmExecTool 接受 msm_name + args (string[])
13
+ * - msmExecTool 在 cwdRoot 注册表 path 1 校验 entry, path-arg 校验
14
+ * - msmExecTool 调 callMsmExec({msm_name, businessArgs})
15
+ * - callMsmExec 计算 path 1 (与 msm.ts 同源) 传给 runMsmExec
16
+ * - runMsmExec 用同一份注册表查找 msm + spawn 业务
17
+ *
18
+ * S028 v0.0.3 收口:
19
+ * - 业务流统一走 cwdRoot 注册表 (避免 plugin-root vs cwd-root 双源漂移)
20
+ * - plugin-root 注册表仅供 CLI 调试 (D9/D10/D6 保留为 fallback, msm-exec-runtime 自动 bootstrap)
21
+ */
22
+ import { join } from 'node:path';
23
+ import { runMsmExec, MsmExecError } from './msm-exec-runtime.js';
24
+ import { getState } from '../state.js';
25
+ import { MsmTimeoutError } from '../errors.js';
26
+ /** 与 v0.0.2 一致的 10 分钟超时 (S028 D11 统一) */
27
+ const MSM_TIMEOUT_MS = 600_000;
28
+ /**
29
+ * 计算 cwdRoot 注册表绝对路径(与 msm.ts:43 registryFilePath 一致)
30
+ * 单一文件路径常量集中处。S028 v0.0.3 收口:
31
+ * - msm.ts loadMechRegistry 用此路径
32
+ * - msm-call runMsmExec 用此路径 (新)
33
+ * - 两处同源, 不再有双注册表漂移风险
34
+ */
35
+ function registryFilePath(cwdRoot, instanceName) {
36
+ return join(cwdRoot, '.opencode', 'skills', instanceName, 'references', 'mech-registry.json');
37
+ }
38
+ /**
39
+ * 调 msm_exec in-process (S028).
40
+ *
41
+ * 行为:
42
+ * - 直接 import plugin 仓 msm-exec-runtime.runMsmExec
43
+ * - 不 spawn 协议层子进程 (运行时本身就是 in-process 库函数)
44
+ * - 业务 msm spawn 内部由 runtime 完成, cwd = state.cwdRoot
45
+ * - 注册表路径 = cwdRoot 注册表 (与 msm.ts loadMechRegistry 同源)
46
+ * - 600s 超时 (与 v0.0.2 行为一致, 10 分钟)
47
+ *
48
+ * 错误映射:
49
+ * - 协议错误 (MsmExecError): 透传给 caller (msmExecTool 捕获并转 MsmExecutionError)
50
+ * - 超时: 抛 MsmTimeoutError (与 v0.0.2 行为一致)
51
+ * - 业务 msm 非 0 exit: 不抛, 通过 result.exitCode 透传 (msmExecTool 转 MsmExecutionError)
52
+ */
53
+ export async function callMsmExec(opts) {
54
+ const state = getState();
55
+ // 拼 argv: <msm_name> <business-args...>
56
+ const argv = [opts.msm_name, ...opts.businessArgs];
57
+ const registryPath = registryFilePath(state.cwdRoot, state.instanceName);
58
+ // 用 Promise.race 加超时 (与 v0.0.2 行为一致, 600s)
59
+ let timeoutHandle = null;
60
+ const timeoutPromise = new Promise((_, reject) => {
61
+ timeoutHandle = setTimeout(() => reject(new MsmTimeoutError(opts.msm_name, MSM_TIMEOUT_MS)), MSM_TIMEOUT_MS);
62
+ });
63
+ try {
64
+ const result = await Promise.race([
65
+ runMsmExec(argv, { cwd: state.cwdRoot, registryPath }),
66
+ timeoutPromise,
67
+ ]);
68
+ return {
69
+ stdout: result.stdout,
70
+ stderr: result.stderr,
71
+ exitCode: result.exitCode,
72
+ };
73
+ }
74
+ catch (err) {
75
+ if (err instanceof MsmExecError) {
76
+ // 协议错误透传 (msmExecTool 处理)
77
+ throw err;
78
+ }
79
+ throw err;
80
+ }
81
+ finally {
82
+ if (timeoutHandle)
83
+ clearTimeout(timeoutHandle);
84
+ }
85
+ }
86
+ //# sourceMappingURL=msm-call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msm-call.js","sourceRoot":"","sources":["../../src/util/msm-call.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,yCAAyC;AACzC,MAAM,cAAc,GAAG,OAAO,CAAC;AAc/B;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,OAAe,EAAE,YAAoB;IAC7D,OAAO,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,oBAAoB,CAAC,CAAC;AAChG,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAoB;IACpD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IAEzB,wCAAwC;IACxC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;IACnD,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAEzE,2CAA2C;IAC3C,IAAI,aAAa,GAAyC,IAAI,CAAC;IAC/D,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACtD,aAAa,GAAG,UAAU,CACxB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,EAChE,cAAc,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAChC,UAAU,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YACtD,cAAc;SACf,CAAC,CAAC;QACH,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;YAChC,0BAA0B;YAC1B,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,IAAI,aAAa;YAAE,YAAY,CAAC,aAAa,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * msm-exec-runtime.ts — opencode-serenity-plugin 端 msm_exec 协议 runtime
4
+ *
5
+ * 角色:msm_exec 协议层 in-process runtime (S028 反转 S024/D26)。Plugin 端
6
+ * msm_exec tool 调本文件 export 的 runMsmExec(),解析 6 必含 flag 后调
7
+ * 业务 MSM,按 --format/--log 包装输出。零 spawn(除业务 msm 本身),
8
+ * plugin 端 3 msm tool 完全自包含。
9
+ *
10
+ * 源:本文件 90% 移植自 serenity 仓 msm-exec.ts
11
+ * (S024 v1.14 产物, 2026-06-07 by commit 725a9e7)
12
+ *
13
+ * 移植变更(S028):
14
+ * - resolveRegistryPath 改 plugin 仓根 mech-registry.json
15
+ * + D6 bootstrap (缺失则创建空 {version:1, entries:[]})
16
+ * - route() 重构为 routeWithArgv(argv, opts?) 返回 MsmExecResult
17
+ * 不再直接写 stdout/stderr (改 in-process 调用栈)
18
+ * - runBusinessMsm 增加 cwd 参数, timeout 30s → 600s (D11 统一)
19
+ * - 新增 runMsmExec() 导出 (msm-call 调用入口)
20
+ * - emitText/emitJson 移除 (由 msm-call 层负责格式化)
21
+ *
22
+ * 协议契约 (S022 RFC §2):
23
+ * - 协议 flag 只能出现在 args 前缀段(紧邻 msm-name 之前)
24
+ * - 6 个必含 flag:
25
+ * --format=<text|json> 输出格式 (默认 text)
26
+ * --log <path> JSON Lines 日志
27
+ * --help / -h 自身或某 msm 的帮助
28
+ * --version 打印 msm_exec 版本
29
+ * --list 列出所有 msm
30
+ * --schema <name> 打印某 msm 的 JSON schema
31
+ * - 输出契约:
32
+ * - text 模式:业务 msm stdout/stderr/exit 透传
33
+ * - json 模式:包装为 6 字段 schema (success: ok/exit/data; failure: ok/exit/error)
34
+ * - log 模式:JSON Lines (ts/level/msm/exit/error_code/error_message)
35
+ *
36
+ * 退出码:
37
+ * 0 — 成功
38
+ * 1 — user (参数错误 / 未知 msm / 拒绝协议 flag)
39
+ * 2 — system (注册表缺失 / 子进程启动失败)
40
+ * 3 — operator (业务 msm 自身非 0 exit,由 --format=json 时透传)
41
+ *
42
+ * 用法 (CLI 模式, 主要用于 plugin 仓内调试):
43
+ * npx tsx msm-exec-runtime.ts <msm-name> [args...]
44
+ * npx tsx msm-exec-runtime.ts --format=json <msm-name> [args...]
45
+ * npx tsx msm-exec-runtime.ts --log /tmp/x.log --format=json <msm-name> [args...]
46
+ * npx tsx msm-exec-runtime.ts --list
47
+ * npx tsx msm-exec-runtime.ts --schema <msm-name>
48
+ * npx tsx msm-exec-runtime.ts --help [msm-name]
49
+ * npx tsx msm-exec-runtime.ts --version
50
+ *
51
+ * 用法 (in-process 模式, plugin 端 msm-call 调用):
52
+ * import { runMsmExec } from "./msm-exec-runtime";
53
+ * const result = await runMsmExec(["--list"]);
54
+ * const result = await runMsmExec(["--format=json", "msm-name", "arg1"]);
55
+ * const result = await runMsmExec(["msm-name", "arg1"], { cwd: "/path/to/cwd" });
56
+ *
57
+ * 不接受:缩写 (--json 替代 --format=json), 短别名 (--ls 替代 --list)
58
+ * — 严格遵循 §10.4 业务 msm 零协议 flag + §2.1 flag 必须在 prefix
59
+ */
60
+ type StderrError = {
61
+ code: string;
62
+ category: string;
63
+ message: string;
64
+ cause: string;
65
+ remediation: string;
66
+ context: Record<string, unknown> | null;
67
+ };
68
+ type JsonResult = {
69
+ ok: true;
70
+ exit: 0;
71
+ data: string;
72
+ } | {
73
+ ok: false;
74
+ exit: number;
75
+ error: StderrError;
76
+ };
77
+ /**
78
+ * in-process 调用结果 (S028 新增)
79
+ *
80
+ * - stdout/stderr: runtime 捕获的"原本会写到 process.stdout/stderr"的内容
81
+ * - exitCode: 退出码 (0/1/2/3/4, 与原文协议一致)
82
+ * - jsonResult: --format=json 时填充的 6 字段 schema, 供 caller 直接透传给 opencode
83
+ */
84
+ export type MsmExecResult = {
85
+ stdout: string;
86
+ stderr: string;
87
+ exitCode: number;
88
+ jsonResult: JsonResult | null;
89
+ };
90
+ /** runMsmExec 可选参数 (S028 新增) */
91
+ export type RunMsmExecOptions = {
92
+ /** 业务 msm spawn 时的 cwd. 默认 process.cwd(). */
93
+ cwd?: string;
94
+ /**
95
+ * 注册表绝对路径. 提供时优先使用(in-process 调用由 msm-call 传入 cwdRoot 的注册表路径,
96
+ * 与 msm.ts loadMechRegistry 同一份文件 — S028 收口双注册表问题).
97
+ * 不提供时 fallback 到 plugin 仓根 `mech-registry.json` (D9 + D6 bootstrap, CLI 调试用).
98
+ */
99
+ registryPath?: string;
100
+ };
101
+ export declare class MsmExecError extends Error {
102
+ code: string;
103
+ category: "user" | "system" | "operator" | "internal";
104
+ message: string;
105
+ context: Record<string, unknown>;
106
+ remediation?: string | undefined;
107
+ cause?: unknown | undefined;
108
+ constructor(code: string, category: "user" | "system" | "operator" | "internal", message: string, context?: Record<string, unknown>, remediation?: string | undefined, cause?: unknown | undefined);
109
+ toStderr(): string;
110
+ exitCode(): number;
111
+ }
112
+ /**
113
+ * in-process 调 msm_exec 协议 runtime (S028 msm-call.ts 唯一调用点)
114
+ *
115
+ * @param argv 完整 args (协议 flag 可选 + msm-name 必需 + 业务 args)
116
+ * 例: ["--list"], ["--format=json", "session-tool", "list"], ["ssh-connect", "check", "ubuntu"]
117
+ * @param opts.cwd 业务 msm spawn 时的 cwd (默认 process.cwd())
118
+ * @returns MsmExecResult {stdout, stderr, exitCode, jsonResult}
119
+ * @throws MsmExecError 协议错误 (参数错 / 未知 msm / 注册表解析失败等)
120
+ */
121
+ export declare function runMsmExec(argv: string[], opts?: RunMsmExecOptions): Promise<MsmExecResult>;
122
+ export {};
123
+ //# sourceMappingURL=msm-exec-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msm-exec-runtime.d.ts","sourceRoot":"","sources":["../../src/util/msm-exec-runtime.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AA+BH,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC,CAAC;AAEF,KAAK,UAAU,GACX;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAcpD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,gCAAgC;AAChC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAIF,qBAAa,YAAa,SAAQ,KAAK;IAE5B,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU;IAC5C,OAAO,EAAE,MAAM;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM;IACX,KAAK,CAAC,EAAE,OAAO;gBALxB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,EAC5C,OAAO,EAAE,MAAM,EACxB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,WAAW,CAAC,EAAE,MAAM,YAAA,EACX,KAAK,CAAC,EAAE,OAAO,YAAA;IAMjC,QAAQ,IAAI,MAAM;IAiBlB,QAAQ,IAAI,MAAM;CAGnB;AA8bD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,aAAa,CAAC,CAErG"}