@lesliechan721/aw 0.1.0-beta.10 → 0.1.0-beta.11

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 (218) hide show
  1. package/dist/authoring/assets/aw-config.d.mts +1 -50
  2. package/dist/authoring/assets/aw-config.mjs +58 -928
  3. package/dist/authoring/assets/aw-config.mjs.map +1 -1
  4. package/dist/authoring/scripts/init-plugin.mjs +28 -33
  5. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  6. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
  7. package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
  8. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  9. package/dist/authoring/scripts/validate-plugin.mjs +165 -45
  10. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  11. package/dist/scripts/bundle-plugins.js +18 -4
  12. package/dist/scripts/bundle-plugins.js.map +1 -1
  13. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  14. package/dist/scripts/test-plugin-scripts.js +21 -5
  15. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  16. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  17. package/dist/src/commands/aw-capabilities.js +1 -1
  18. package/dist/src/commands/aw-capabilities.js.map +1 -1
  19. package/dist/src/commands/catalog-dev-source.d.ts +6 -3
  20. package/dist/src/commands/catalog-dev-source.js +96 -35
  21. package/dist/src/commands/catalog-dev-source.js.map +1 -1
  22. package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
  23. package/dist/src/commands/catalog-publication-inspection.js +54 -8
  24. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  25. package/dist/src/commands/catalog-publication-journal.js +1 -1
  26. package/dist/src/commands/catalog-publication-journal.js.map +1 -1
  27. package/dist/src/commands/catalog-source.d.ts +19 -4
  28. package/dist/src/commands/catalog-source.js +121 -12
  29. package/dist/src/commands/catalog-source.js.map +1 -1
  30. package/dist/src/commands/catalog-store.d.ts +26 -7
  31. package/dist/src/commands/catalog-store.js +361 -42
  32. package/dist/src/commands/catalog-store.js.map +1 -1
  33. package/dist/src/commands/console.js +28 -17
  34. package/dist/src/commands/console.js.map +1 -1
  35. package/dist/src/commands/operation-plan.d.ts +2 -1
  36. package/dist/src/commands/operation-plan.js +24 -0
  37. package/dist/src/commands/operation-plan.js.map +1 -1
  38. package/dist/src/commands/operation-planner.d.ts +2 -2
  39. package/dist/src/commands/operation-planner.js +11 -19
  40. package/dist/src/commands/operation-planner.js.map +1 -1
  41. package/dist/src/commands/options.d.ts +2 -2
  42. package/dist/src/commands/options.js +18 -4
  43. package/dist/src/commands/options.js.map +1 -1
  44. package/dist/src/commands/plugin-capabilities.js +2 -2
  45. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  46. package/dist/src/commands/rebuild-command.d.ts +7 -0
  47. package/dist/src/commands/rebuild-command.js +769 -279
  48. package/dist/src/commands/rebuild-command.js.map +1 -1
  49. package/dist/src/commands/rebuild-integration.d.ts +24 -47
  50. package/dist/src/commands/rebuild-integration.js +1074 -1011
  51. package/dist/src/commands/rebuild-integration.js.map +1 -1
  52. package/dist/src/commands/rebuild-operations.d.ts +50 -12
  53. package/dist/src/commands/rebuild-operations.js +207 -101
  54. package/dist/src/commands/rebuild-operations.js.map +1 -1
  55. package/dist/src/commands/status-v6.d.ts +21 -2
  56. package/dist/src/commands/status-v6.js +163 -31
  57. package/dist/src/commands/status-v6.js.map +1 -1
  58. package/dist/src/commands/sync-command.d.ts +17 -0
  59. package/dist/src/commands/sync-command.js +45 -37
  60. package/dist/src/commands/sync-command.js.map +1 -1
  61. package/dist/src/commands/write-commands.d.ts +5 -0
  62. package/dist/src/commands/write-commands.js +50 -3
  63. package/dist/src/commands/write-commands.js.map +1 -1
  64. package/dist/src/generators/local-plugin.js +14 -4
  65. package/dist/src/generators/local-plugin.js.map +1 -1
  66. package/dist/src/index.js +216 -85
  67. package/dist/src/index.js.map +1 -1
  68. package/dist/src/output/command-receipt.js +1 -3
  69. package/dist/src/output/command-receipt.js.map +1 -1
  70. package/dist/src/output/rebuild-progress.js +1 -3
  71. package/dist/src/output/rebuild-progress.js.map +1 -1
  72. package/dist/src/portable/materializer.js +6 -6
  73. package/dist/src/portable/materializer.js.map +1 -1
  74. package/dist/src/tui/catalog-generation.js +41 -1
  75. package/dist/src/tui/catalog-generation.js.map +1 -1
  76. package/dist/src/tui/catalog-sources/session.d.ts +1 -0
  77. package/dist/src/tui/catalog-sources/session.js +16 -3
  78. package/dist/src/tui/catalog-sources/session.js.map +1 -1
  79. package/dist/src/tui/catalog-sources/types.d.ts +3 -2
  80. package/dist/src/tui/components.d.ts +4 -0
  81. package/dist/src/tui/components.js +36 -24
  82. package/dist/src/tui/components.js.map +1 -1
  83. package/dist/src/tui/console-app-v4.d.ts +13 -4
  84. package/dist/src/tui/console-app-v4.js +2067 -839
  85. package/dist/src/tui/console-app-v4.js.map +1 -1
  86. package/dist/src/tui/controller-runtime.js +585 -33
  87. package/dist/src/tui/controller-runtime.js.map +1 -1
  88. package/dist/src/tui/controller.d.ts +152 -6
  89. package/dist/src/tui/controller.js +1168 -156
  90. package/dist/src/tui/controller.js.map +1 -1
  91. package/dist/src/tui/diagnostic-projection.d.ts +22 -0
  92. package/dist/src/tui/diagnostic-projection.js +437 -0
  93. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  94. package/dist/src/tui/diagnostics.d.ts +1 -1
  95. package/dist/src/tui/diagnostics.js +6 -1
  96. package/dist/src/tui/diagnostics.js.map +1 -1
  97. package/dist/src/tui/glossary.d.ts +23 -0
  98. package/dist/src/tui/glossary.js +42 -0
  99. package/dist/src/tui/glossary.js.map +1 -0
  100. package/dist/src/tui/index.d.ts +4 -1
  101. package/dist/src/tui/index.js +4 -1
  102. package/dist/src/tui/index.js.map +1 -1
  103. package/dist/src/tui/keys.d.ts +44 -0
  104. package/dist/src/tui/keys.js +43 -15
  105. package/dist/src/tui/keys.js.map +1 -1
  106. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  107. package/dist/src/tui/lifecycle-action-view.js +34 -0
  108. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  109. package/dist/src/tui/model.d.ts +12 -2
  110. package/dist/src/tui/model.js +128 -52
  111. package/dist/src/tui/model.js.map +1 -1
  112. package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
  113. package/dist/src/tui/operation-diagnostic-screens.js +101 -0
  114. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  115. package/dist/src/tui/operation-history.d.ts +80 -0
  116. package/dist/src/tui/operation-history.js +130 -0
  117. package/dist/src/tui/operation-history.js.map +1 -0
  118. package/dist/src/tui/operation-review-planner.js +306 -86
  119. package/dist/src/tui/operation-review-planner.js.map +1 -1
  120. package/dist/src/tui/router.d.ts +82 -17
  121. package/dist/src/tui/router.js +186 -38
  122. package/dist/src/tui/router.js.map +1 -1
  123. package/dist/src/tui/screen-registry.d.ts +546 -0
  124. package/dist/src/tui/screen-registry.js +179 -0
  125. package/dist/src/tui/screen-registry.js.map +1 -0
  126. package/dist/src/tui/session-console-app.d.ts +6 -0
  127. package/dist/src/tui/session-console-app.js +13 -0
  128. package/dist/src/tui/session-console-app.js.map +1 -0
  129. package/dist/src/tui/session-host.d.ts +44 -0
  130. package/dist/src/tui/session-host.js +197 -0
  131. package/dist/src/tui/session-host.js.map +1 -0
  132. package/dist/src/types.d.ts +64 -3
  133. package/dist/src/types.js +1 -1
  134. package/dist/src/types.js.map +1 -1
  135. package/dist/src/utils/agent-version.js +1 -1
  136. package/dist/src/utils/agent-version.js.map +1 -1
  137. package/dist/src/utils/artifact-renderer.d.ts +2 -1
  138. package/dist/src/utils/artifact-renderer.js +52 -52
  139. package/dist/src/utils/artifact-renderer.js.map +1 -1
  140. package/dist/src/utils/bundle-layout.js +4 -4
  141. package/dist/src/utils/bundle-layout.js.map +1 -1
  142. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  143. package/dist/src/utils/catalog-capability-projection.js +14 -0
  144. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  145. package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
  146. package/dist/src/utils/catalog-generation-resolver.js +49 -27
  147. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  148. package/dist/src/utils/catalog-generation-service.d.ts +8 -1
  149. package/dist/src/utils/catalog-generation-service.js +81 -47
  150. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  151. package/dist/src/utils/catalog-generation-store.d.ts +1 -0
  152. package/dist/src/utils/catalog-generation-store.js +60 -5
  153. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  154. package/dist/src/utils/catalog-operation-context.js +1 -0
  155. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  156. package/dist/src/utils/catalog-public-error.js +4 -0
  157. package/dist/src/utils/catalog-public-error.js.map +1 -1
  158. package/dist/src/utils/catalog-source-impact.d.ts +3 -2
  159. package/dist/src/utils/catalog-source-impact.js +142 -20
  160. package/dist/src/utils/catalog-source-impact.js.map +1 -1
  161. package/dist/src/utils/catalog-source-references.js +95 -9
  162. package/dist/src/utils/catalog-source-references.js.map +1 -1
  163. package/dist/src/utils/catalog-sources.d.ts +1 -1
  164. package/dist/src/utils/catalog-sources.js +19 -3
  165. package/dist/src/utils/catalog-sources.js.map +1 -1
  166. package/dist/src/utils/codex-manager.js +8 -0
  167. package/dist/src/utils/codex-manager.js.map +1 -1
  168. package/dist/src/utils/codex-native-observation.d.ts +34 -1
  169. package/dist/src/utils/codex-native-observation.js +132 -0
  170. package/dist/src/utils/codex-native-observation.js.map +1 -1
  171. package/dist/src/utils/codex-reconciler.d.ts +0 -2
  172. package/dist/src/utils/codex-reconciler.js.map +1 -1
  173. package/dist/src/utils/codex-shared-native.d.ts +2 -26
  174. package/dist/src/utils/codex-shared-native.js +8 -82
  175. package/dist/src/utils/codex-shared-native.js.map +1 -1
  176. package/dist/src/utils/git-catalog-provider.d.ts +3 -0
  177. package/dist/src/utils/git-catalog-provider.js +40 -1
  178. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  179. package/dist/src/utils/local-marketplace.d.ts +1 -0
  180. package/dist/src/utils/local-marketplace.js +69 -73
  181. package/dist/src/utils/local-marketplace.js.map +1 -1
  182. package/dist/src/utils/multi-source-catalog.js +1 -0
  183. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  184. package/dist/src/utils/native-cli.d.ts +9 -4
  185. package/dist/src/utils/native-cli.js +280 -34
  186. package/dist/src/utils/native-cli.js.map +1 -1
  187. package/dist/src/utils/path-catalog-provider.js +2 -0
  188. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  189. package/dist/src/utils/plugin-config-write.d.ts +15 -13
  190. package/dist/src/utils/plugin-config-write.js +418 -198
  191. package/dist/src/utils/plugin-config-write.js.map +1 -1
  192. package/dist/src/utils/plugin-config.d.ts +48 -4
  193. package/dist/src/utils/plugin-config.js +289 -38
  194. package/dist/src/utils/plugin-config.js.map +1 -1
  195. package/dist/src/utils/plugin-scripts.d.ts +20 -12
  196. package/dist/src/utils/plugin-scripts.js +376 -109
  197. package/dist/src/utils/plugin-scripts.js.map +1 -1
  198. package/dist/src/utils/process-tree.d.ts +2 -2
  199. package/dist/src/utils/process-tree.js +1 -1
  200. package/dist/src/utils/process-tree.js.map +1 -1
  201. package/dist/src/utils/project-boundary.d.ts +11 -1
  202. package/dist/src/utils/project-boundary.js +17 -8
  203. package/dist/src/utils/project-boundary.js.map +1 -1
  204. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  205. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  206. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  207. package/dist/src/utils/sensitive-content.d.ts +1 -0
  208. package/dist/src/utils/sensitive-content.js +15 -0
  209. package/dist/src/utils/sensitive-content.js.map +1 -0
  210. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  211. package/dist/src/utils/windows-claude-shim.js +141 -0
  212. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  213. package/dist/vitest.config.js +91 -10
  214. package/dist/vitest.config.js.map +1 -1
  215. package/package.json +9 -9
  216. package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
  217. package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
  218. package/dist/authoring/templates/aw-config-runtime.mjs.map +0 -1
@@ -1,5 +1,8 @@
1
1
  import { requestedIntentAction } from '../output/command-receipt.js';
2
2
  import { catalogSourceAutoCheckOnlyValue } from './catalog-sources/review.js';
3
+ import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, } from '../commands/options.js';
4
+ import { MANAGED_CODEX_VERSION } from '../utils/codex-manager.js';
5
+ import { platformLabel, scopeLabel } from './glossary.js';
3
6
  function plannerError(code, message) {
4
7
  return new Error(`${code}: ${message}`);
5
8
  }
@@ -7,10 +10,85 @@ function pluginNameFromId(pluginId) {
7
10
  const separator = pluginId.indexOf('/');
8
11
  return separator < 0 ? pluginId : pluginId.slice(separator + 1);
9
12
  }
13
+ function scriptTimeoutChanges(intent) {
14
+ let configuredTimeoutMs;
15
+ if ((intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch')
16
+ && (intent.action === 'install' || intent.action === 'refreshAndSync'
17
+ || intent.action === 'reinstall' || intent.action === 'sync')) {
18
+ configuredTimeoutMs = intent.scriptTimeoutMs;
19
+ }
20
+ else if (intent.kind === 'configSave' && intent.apply) {
21
+ configuredTimeoutMs = intent.scriptTimeoutMs;
22
+ }
23
+ else {
24
+ return [];
25
+ }
26
+ const timeoutMs = parseScriptTimeoutMs(configuredTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS);
27
+ return [{
28
+ kind: 'keep',
29
+ group: 'runtime',
30
+ title: 'preApply 脚本超时',
31
+ description: `${timeoutMs} ms`,
32
+ }];
33
+ }
34
+ function sourceMutationReviewChanges(mutation) {
35
+ if (mutation.action === 'add') {
36
+ const source = mutation.source;
37
+ return [
38
+ { kind: 'add', title: '保存来源配置' },
39
+ { kind: 'add', title: source.type === 'git' ? 'Git URL' : '本地目录', description: '已设置' },
40
+ ...(source.type === 'git' ? [{
41
+ kind: 'add',
42
+ title: 'Git ref policy',
43
+ description: source.refPolicy.type === 'cliChannel'
44
+ ? '跟随 CLI channel'
45
+ : `固定 ref · ${source.refPolicy.ref}`,
46
+ }, {
47
+ kind: 'add',
48
+ title: '插件目录子目录',
49
+ description: source.subdir ?? '根目录',
50
+ }] : []),
51
+ {
52
+ kind: 'add',
53
+ title: '自动检查',
54
+ description: source.autoCheck === false ? '关闭' : '开启',
55
+ },
56
+ ];
57
+ }
58
+ const update = mutation.update;
59
+ const changes = [];
60
+ if ('url' in update && update.url !== undefined) {
61
+ changes.push({ kind: 'change', title: 'Git URL', description: '将更新' });
62
+ }
63
+ if ('path' in update && update.path !== undefined) {
64
+ changes.push({ kind: 'change', title: '本地目录', description: '将更新' });
65
+ }
66
+ if ('refPolicy' in update && update.refPolicy)
67
+ changes.push({
68
+ kind: 'change',
69
+ title: 'Git ref policy',
70
+ description: update.refPolicy.type === 'cliChannel'
71
+ ? '跟随 CLI channel'
72
+ : `固定 ref · ${update.refPolicy.ref}`,
73
+ });
74
+ if ('subdir' in update && update.subdir !== undefined)
75
+ changes.push({
76
+ kind: 'change',
77
+ title: '插件目录子目录',
78
+ description: update.subdir ?? '根目录',
79
+ });
80
+ if ('autoCheck' in update && update.autoCheck !== undefined)
81
+ changes.push({
82
+ kind: 'change',
83
+ title: '自动检查',
84
+ description: update.autoCheck ? '开启' : '关闭',
85
+ });
86
+ return changes.length > 0 ? changes : [{ kind: 'change', title: '更新来源配置' }];
87
+ }
10
88
  function operationLabels(intent, evidence) {
11
89
  if (intent.kind === 'catalogSourcesRecovery') {
12
90
  return {
13
- title: '恢复 Catalog 来源配置',
91
+ title: '恢复插件目录来源配置',
14
92
  description: '备份损坏配置,并恢复唯一的主来源 official。',
15
93
  primaryLabel: '备份并恢复官方默认来源',
16
94
  destructive: false,
@@ -24,10 +102,27 @@ function operationLabels(intent, evidence) {
24
102
  destructive: false,
25
103
  };
26
104
  }
105
+ if (intent.kind === 'pluginWriteBatch') {
106
+ const labels = {
107
+ install: ['批量添加插件', '添加所选插件及其必需依赖。'],
108
+ sync: ['批量同步插件', '按已保存的设置同步所选插件。'],
109
+ reinstall: ['批量重新安装插件', '清理 AW 管理的内容后完整重建所选插件。'],
110
+ uninstall: ['批量移除插件', '移除所选插件的 AW 管理安装内容。配置保持不变。'],
111
+ cleanup: ['批量清理插件', '只移除可证明属于 AW 的现场内容。配置保持不变。'],
112
+ };
113
+ const [title, description] = labels[intent.action];
114
+ return {
115
+ title,
116
+ description,
117
+ primaryLabel: title,
118
+ destructive: intent.action === 'reinstall' || intent.action === 'uninstall'
119
+ || intent.action === 'cleanup',
120
+ };
121
+ }
27
122
  if (intent.kind === 'pluginWrite' && intent.dependencyPlacementAdjustment) {
28
123
  return {
29
124
  title: '调整依赖位置',
30
- description: '按最终依赖位置更新 desired intent,并应用非破坏性变更。',
125
+ description: '保存最终的依赖使用位置,并应用非破坏性变更。',
31
126
  primaryLabel: '应用依赖位置',
32
127
  destructive: false,
33
128
  };
@@ -59,11 +154,11 @@ function operationLabels(intent, evidence) {
59
154
  }
60
155
  const labels = {
61
156
  install: ['添加插件', '添加所选平台和使用位置。', false],
62
- refreshAndSync: ['刷新并同步', '刷新相关来源后,使用新的 Active 内容同步所选安装。', false],
157
+ refreshAndSync: ['刷新并同步', '刷新相关来源后,使用新的当前启用版本同步所选安装。', false],
63
158
  reinstall: ['重新安装插件', '清理 AW 管理的内容后完整重建。', true],
64
159
  uninstall: ['移除插件', '移除 AW 管理的安装内容。配置保持不变。', true],
65
160
  cleanup: ['清理插件', '只移除可证明属于 AW 的现场内容。配置保持不变。', true],
66
- sync: ['同步插件', '按已保存的 desired intent 修复当前现场。', false],
161
+ sync: ['同步插件', '按已保存使用设置修复当前使用位置。', false],
67
162
  };
68
163
  const [title, description, destructive] = labels[intent.action];
69
164
  return { title, description, primaryLabel: title, destructive };
@@ -100,7 +195,7 @@ function operationLabels(intent, evidence) {
100
195
  if (autoCheck !== undefined) {
101
196
  return {
102
197
  title: '保存自动检查设置',
103
- description: '只保存自动检查设置;Active Catalog 和 Catalog store 保持不变。',
198
+ description: '只保存自动检查设置;当前启用版本和本地目录副本保持不变。',
104
199
  primaryLabel: '保存设置',
105
200
  destructive: false,
106
201
  };
@@ -117,18 +212,18 @@ function operationLabels(intent, evidence) {
117
212
  if (intent.kind === 'catalogGeneration') {
118
213
  if (intent.action === 'update' && evidence?.catalogGeneration?.exactRepair) {
119
214
  return {
120
- title: '验证并精确修复 Catalog',
121
- description: '候选与当前 Active store key 相同。精确替换损坏条目,并保留 Active 身份。',
215
+ title: '验证并精确修复插件目录',
216
+ description: '待检查版本与当前启用版本的技术标识相同。精确替换损坏内容,并保留当前启用版本。',
122
217
  primaryLabel: '验证并精确修复',
123
218
  destructive: false,
124
219
  };
125
220
  }
126
221
  const labels = {
127
- activate: ['获取并启用 Catalog', '验证候选后启用。不会升级已安装 Plugin,也不会修改其他来源。', '获取并启用'],
128
- update: ['验证并切换 Catalog', '验证候选后切换 Active。失败时保留旧 Active。', '验证并切换'],
129
- repair: ['重新下载并修复 Catalog', '只修复这个来源的 Active 条目,不删除其他 digest。', '重新下载并修复'],
130
- replaceDamaged: ['替换损坏的 Active Catalog', '使用当前候选替换无法精确修复的 Active。失败时保留旧 Active。', '确认并替换'],
131
- recoverState: ['恢复 Catalog 状态并启用', '重建无效 generation state,并只激活已确认的目标来源。来源配置和 Catalog store 保持不变。', '恢复状态并启用'],
222
+ activate: ['获取并启用插件目录', '检查待检查版本后启用。不会升级已安装插件,也不会修改其他来源。', '获取并启用'],
223
+ update: ['检查并切换插件目录', '检查待检查版本后切换当前启用版本。失败时保留原当前启用版本。', '检查并切换'],
224
+ repair: ['重新下载并修复插件目录', '只修复这个来源的当前启用版本,不删除其他目录版本副本。', '重新下载并修复'],
225
+ replaceDamaged: ['替换损坏的当前启用版本', '使用待检查版本替换无法精确修复的当前启用版本。失败时保留原版本。', '确认并替换'],
226
+ recoverState: ['恢复插件目录状态并启用', '重建无效的目录版本状态,并只启用已确认的目标来源。来源配置和本地目录副本保持不变。', '恢复状态并启用'],
132
227
  };
133
228
  const [title, description, primaryLabel] = labels[intent.action];
134
229
  return { title, description, primaryLabel, destructive: false };
@@ -180,20 +275,23 @@ function reviewChanges(intent, evidence, dependencyOverview) {
180
275
  }
181
276
  if (intent.kind === 'pluginWrite') {
182
277
  const desiredIntentKind = requestedIntentAction(intent.action === 'refreshAndSync' ? 'sync' : intent.action);
183
- const runtimeChanges = evidence?.codexManager
184
- ? [{
185
- kind: evidence.codexManager.state === 'requires-install' ? 'add' : 'keep',
186
- group: 'runtime',
187
- title: evidence.codexManager.state === 'requires-install'
188
- ? '获取 Codex runtime'
189
- : 'Codex runtime',
190
- description: evidence.codexManager.state === 'requires-install'
191
- ? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · AW-managed 缓存`
192
- : evidence.codexManager.state === 'cached'
193
- ? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · 复用 AW-managed 缓存`
194
- : `使用兼容的 system Codex ${evidence.codexManager.version}`,
195
- }]
196
- : [];
278
+ const runtimeChanges = [
279
+ ...scriptTimeoutChanges(intent),
280
+ ...(evidence?.codexManager
281
+ ? [{
282
+ kind: evidence.codexManager.state === 'requires-install' ? 'add' : 'keep',
283
+ group: 'runtime',
284
+ title: evidence.codexManager.state === 'requires-install'
285
+ ? '获取 Codex 运行组件'
286
+ : 'Codex 运行组件',
287
+ description: evidence.codexManager.state === 'requires-install'
288
+ ? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · AW 管理缓存`
289
+ : evidence.codexManager.state === 'cached'
290
+ ? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · 复用 AW 管理缓存`
291
+ : `使用兼容的系统 Codex ${evidence.codexManager.version}`,
292
+ }]
293
+ : []),
294
+ ];
197
295
  const codexChanges = (evidence?.codexReconcilePlans ?? []).flatMap((codexPlan) => [
198
296
  ...codexPlan.sharedNativeActions.map((action) => ({
199
297
  kind: action.action === 'reuse' || action.action === 'none' ? 'keep'
@@ -202,22 +300,29 @@ function reviewChanges(intent, evidence, dependencyOverview) {
202
300
  pluginId: action.pluginId,
203
301
  title: action.codexPluginId,
204
302
  description: action.action === 'reuse'
205
- ? '复用已存在的精确 shared native;不建立 ownership ledger'
206
- : `${action.action} · shared${action.affectsOtherScopes ? ' · 可能影响其他项目' : ''}`,
303
+ ? '复用完全相同的平台共享内容'
304
+ : `${{
305
+ install: '添加',
306
+ update: '更新',
307
+ repair: '修复',
308
+ replace: '替换',
309
+ remove: '移除',
310
+ none: '保持不变',
311
+ }[action.action]}平台共享内容${action.affectsOtherScopes ? ' · 可能影响其他项目' : ''}`,
207
312
  })),
208
313
  ...codexPlan.activationActions.user.map((action) => ({
209
314
  kind: action.action === 'none' ? 'keep' : action.desired ? 'change' : 'remove',
210
315
  group: 'userActivation',
211
316
  pluginId: action.pluginId,
212
317
  title: action.codexPluginId,
213
- description: `desired=${String(action.desired)} · ${action.action}`,
318
+ description: `${action.desired ? '启用' : '停用'}所有项目位置 · ${action.action === 'none' ? '保持不变' : '将更新'}`,
214
319
  })),
215
320
  ...codexPlan.activationActions.project.map((action) => ({
216
321
  kind: action.action === 'none' ? 'keep' : action.desired ? 'change' : 'remove',
217
322
  group: 'projectActivation',
218
323
  pluginId: action.pluginId,
219
324
  title: action.codexPluginId,
220
- description: `desired=${String(action.desired)} · ${action.action}`,
325
+ description: `${action.desired ? '启用' : '停用'}当前项目位置 · ${action.action === 'none' ? '保持不变' : '将更新'}`,
221
326
  })),
222
327
  ...[...codexPlan.extensionWrites, ...codexPlan.extensionRemovals]
223
328
  .map((action) => ({
@@ -225,7 +330,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
225
330
  group: 'awExtensions',
226
331
  pluginId: action.pluginId,
227
332
  title: action.componentKey,
228
- description: `${action.scope} · ${action.action}`,
333
+ description: `${scopeLabel(action.scope)} · ${action.action === 'write' ? '写入' : '移除'}`,
229
334
  })),
230
335
  ]);
231
336
  const plan = evidence?.dependencyPlan;
@@ -239,7 +344,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
239
344
  pluginId: target.pluginId,
240
345
  relation,
241
346
  title: pluginNameFromId(target.pluginId),
242
- description: `${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.resolvedScope === 'user' ? '所有项目' : '当前项目'}${target.action === 'keep' ? ' · 已可用' : ''}${target.assignmentSource === 'existingWiderTarget' ? ' · 复用健康的所有项目安装' : target.assignmentSource === 'consumerPromotion' ? ' · 随上游依赖一并调整' : ''}`,
347
+ description: `${platformLabel(target.platform)} · ${scopeLabel(target.resolvedScope)}${target.action === 'keep' ? ' · 已可用' : ''}${target.assignmentSource === 'existingWiderTarget' ? ' · 复用健康的所有项目安装' : target.assignmentSource === 'consumerPromotion' ? ' · 根据依赖自动扩大范围' : ''}`,
243
348
  };
244
349
  });
245
350
  changes.push({
@@ -249,7 +354,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
249
354
  ? '根插件和插件设置保持不变'
250
355
  : '插件设置和已有依赖保持不变',
251
356
  description: intent.dependencyPlacementAdjustment
252
- ? '这里只调整所列 dependency placement。'
357
+ ? '这里只调整所列依赖使用位置。'
253
358
  : '移除根插件时不会自动清理依赖。',
254
359
  });
255
360
  return [...changes, ...codexChanges, ...runtimeChanges];
@@ -262,14 +367,14 @@ function reviewChanges(intent, evidence, dependencyOverview) {
262
367
  group: 'requested',
263
368
  pluginId: intent.pluginId,
264
369
  relation: 'requested',
265
- title: `${platform === 'codex' ? 'Codex' : 'Claude Code'} · ${intent.scope === 'user' ? '所有项目' : '当前项目'}`,
370
+ title: `${platformLabel(platform)} · ${scopeLabel(intent.scope)}`,
266
371
  description: intent.action === 'uninstall' || intent.action === 'cleanup'
267
372
  ? '插件设置不会删除。'
268
373
  : '执行前会再次检查当前安装状态。',
269
374
  }));
270
375
  if (intent.action === 'cleanup' && evidence?.offlineCleanup) {
271
376
  const actionLabels = {
272
- removeOwnedOutput: '移除 AW-owned 输出',
377
+ removeOwnedOutput: '移除 AW 管理输出',
273
378
  removeNativeRegistration: '移除原生插件注册',
274
379
  };
275
380
  changes.push(...evidence.offlineCleanup.plan.actions.map((action) => ({
@@ -305,8 +410,8 @@ function reviewChanges(intent, evidence, dependencyOverview) {
305
410
  changes.push({
306
411
  kind: 'warning',
307
412
  group: 'preserved',
308
- title: `后续:${intent.scope === 'user' ? '当前项目' : '所有项目'} sync`,
309
- description: intent.recoveryDescription,
413
+ title: `后续:同步${intent.scope === 'user' ? '当前项目' : '所有项目'}位置`,
414
+ description: '完成此操作后,同步要保留的使用位置。',
310
415
  });
311
416
  }
312
417
  return [...changes, ...codexChanges, ...runtimeChanges];
@@ -365,75 +470,74 @@ function reviewChanges(intent, evidence, dependencyOverview) {
365
470
  if (intent.kind === 'catalogGeneration') {
366
471
  const generation = evidence?.catalogGeneration;
367
472
  if (!generation) {
368
- throw plannerError('catalogGenerationUnavailable', '缺少 Catalog 候选证据。');
473
+ throw plannerError('catalogGenerationUnavailable', '缺少插件目录待检查版本证据。');
369
474
  }
370
475
  return [
371
476
  ...(generation.previousActive ? [{
372
477
  kind: 'keep',
373
478
  title: generation.previousActiveVersion
374
- ? `当前 Active Catalog ${generation.previousActiveVersion}` : '当前 Active',
479
+ ? `当前启用版本 ${generation.previousActiveVersion}` : '当前启用版本',
375
480
  description: `${generation.previousActive.resolvedCommit.slice(0, 8)} · ${generation.previousActive.snapshotDigest.slice(0, 15)}`,
376
481
  }] : []),
377
482
  {
378
483
  kind: intent.action === 'activate' ? 'add' : 'change',
379
484
  title: generation.exactRepair
380
- ? `精确修复候选 Catalog ${generation.candidate.catalogVersion}`
381
- : `候选 Catalog ${generation.candidate.catalogVersion}`,
485
+ ? `精确修复待检查版本 ${generation.candidate.catalogVersion}`
486
+ : `待检查版本 ${generation.candidate.catalogVersion}`,
382
487
  description: `${generation.candidate.resolvedCommit.slice(0, 8)} · ${generation.candidate.snapshotDigest.slice(0, 15)}`,
383
488
  },
384
489
  ...(generation.exactRepair ? [{
385
490
  kind: 'change',
386
- title: '精确替换损坏的 Active 条目',
387
- description: 'logical identity、snapshot digest、manifest digest 和 commit 保持不变。',
491
+ title: '精确替换损坏的当前启用版本',
492
+ description: '版本和内容的技术标识保持不变。',
388
493
  }] : []),
389
494
  ...(intent.action === 'replaceDamaged' ? [{
390
495
  kind: 'warning',
391
- title: '使用当前候选替换损坏的 Active',
392
- description: '旧版本无法安全比较;确认后只切换到本次绑定的当前候选。',
393
- }] : []),
394
- ...(generation.sourceMutation ? [{
395
- kind: generation.sourceMutation.action === 'add' ? 'add' : 'change',
396
- title: generation.sourceMutation.action === 'add' ? '保存来源配置'
397
- : 'url' in generation.sourceMutation.update ? 'Git URL'
398
- : 'path' in generation.sourceMutation.update ? '本地目录'
399
- : 'refPolicy' in generation.sourceMutation.update
400
- && generation.sourceMutation.update.refPolicy ? 'Git ref policy'
401
- : 'subdir' in generation.sourceMutation.update
402
- && generation.sourceMutation.update.subdir !== undefined ? 'Catalog 子目录'
403
- : '更新来源配置',
404
- description: `${generation.configDigest.slice(0, 8)} → ${generation.proposedConfigDigest?.slice(0, 8) ?? '未知'}`,
496
+ title: '使用待检查版本替换损坏的当前启用版本',
497
+ description: '旧版本无法安全比较;确认后只切换到本次已检查的版本。',
405
498
  }] : []),
499
+ ...(generation.sourceMutation ? sourceMutationReviewChanges(generation.sourceMutation) : []),
406
500
  ...(generation.explicitSourceChange && generation.candidateRelation === 'rollbackDetected' ? [{
407
501
  kind: 'warning',
408
- title: '显式来源切换将降低 Catalog 版本',
502
+ title: '来源切换将降低插件目录版本',
409
503
  description: `${generation.previousActiveVersion ?? '当前版本'} → ${generation.candidate.catalogVersion}`,
410
504
  }] : generation.explicitSourceChange && generation.candidateRelation === 'versionConflict' ? [{
411
505
  kind: 'warning',
412
- title: '显式来源切换包含同版本不同内容',
506
+ title: '来源切换包含同版本不同内容',
413
507
  description: `${generation.candidate.catalogVersion} · ${generation.candidate.snapshotDigest.slice(0, 15)}`,
414
508
  }] : generation.explicitSourceChange && generation.candidateRelation === 'incompatible' ? [{
415
509
  kind: 'warning',
416
- title: '显式来源切换将改变 Catalog logical identity',
417
- description: '确认前请核对新来源身份和候选摘要。',
510
+ title: '来源切换将改变插件目录身份',
511
+ description: '确认前请核对新来源身份和待检查版本摘要。',
418
512
  }] : []),
419
513
  ...((generation.impact?.affectedReferences ?? []).length > 0 ? [{
420
514
  kind: 'warning',
421
- title: '现有引用受候选变化影响',
515
+ title: '现有引用受待检查版本影响',
422
516
  description: `${generation.impact.affectedReferences.length} 项;已安装插件不会自动修改。`,
423
517
  }] : []),
424
518
  ...(generation.impact && !generation.impact.inventoryComplete ? [{
425
519
  kind: 'warning',
426
- title: '候选影响未知',
427
- description: '部分状态或旧 Manifest 无法读取;不能把影响视为零。',
520
+ title: '待检查版本的影响未知',
521
+ description: '部分状态或旧插件定义无法读取;不能把影响视为零。',
522
+ }] : []),
523
+ ...((generation.primaryImpact?.affectedReferences ?? []).length > 0 ? [{
524
+ kind: 'warning',
525
+ title: '切换主来源会影响现有引用',
526
+ description: `${generation.primaryImpact.affectedReferences.length} 项;切换将在插件目录启用后执行。`,
428
527
  }] : []),
429
- { kind: 'keep', title: '不会升级已安装 Plugin' },
528
+ ...(generation.primaryImpact && !generation.primaryImpact.inventoryComplete ? [{
529
+ kind: 'warning',
530
+ title: '主来源切换影响未知',
531
+ description: '部分状态无法读取;不能把影响视为零。',
532
+ }] : []),
533
+ { kind: 'keep', title: '不会升级已安装插件' },
430
534
  { kind: 'keep', title: intent.action === 'recoverState'
431
- ? '不会修改其他来源配置或 store' : '不会修改其他来源' },
432
- { kind: 'keep', title: '失败时保留旧 Active' },
535
+ ? '不会修改其他来源配置或本地目录副本' : '不会修改其他来源' },
536
+ { kind: 'keep', title: '失败时保留原当前启用版本' },
433
537
  ...(intent.action === 'recoverState' ? [{
434
538
  kind: 'warning',
435
- title: '无效 state 中的其他 Active 指针不会被信任或保留',
436
- description: '其他来源配置和 store 内容不会删除;可分别重新启用。',
539
+ title: '无效状态中的其他当前启用版本记录不会保留',
540
+ description: '其他来源配置和本地目录副本不会删除;可分别重新启用。',
437
541
  }] : []),
438
542
  ];
439
543
  }
@@ -445,6 +549,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
445
549
  const layerLabel = intent.scope === 'user' ? '个人默认' : '当前项目';
446
550
  if (intent.retryApply) {
447
551
  return [
552
+ ...scriptTimeoutChanges(intent),
448
553
  ...(evidence?.config?.affectedTargets.map((target) => ({
449
554
  kind: 'change',
450
555
  group: 'configImpact',
@@ -466,21 +571,28 @@ function reviewChanges(intent, evidence, dependencyOverview) {
466
571
  {
467
572
  kind: 'change', group: 'configImpact',
468
573
  title: `重新生成 ${sharedNativeTargets.map((target) => target.skillKey).join('、')} 动态 Skill`,
469
- description: '配置来源为个人默认;更新共享 Codex native 插件会影响所有启用项目。',
574
+ description: '配置来源为个人默认;更新 Codex 平台共享内容会影响所有启用项目。',
470
575
  },
471
- { kind: 'keep', group: 'preserved', title: '保留当前 user/project activation' },
472
- { kind: 'keep', group: 'preserved', title: 'project Agent role 继续按当前项目配置' },
576
+ { kind: 'keep', group: 'preserved', title: '保留所有项目和当前项目的启用位置' },
577
+ { kind: 'keep', group: 'preserved', title: '当前项目 Agent role 继续使用当前项目配置' },
473
578
  ]
474
579
  : [
475
580
  {
476
581
  kind: 'warning', group: 'configImpact',
477
- title: '共享 Codex native render 待同步',
582
+ title: 'Codex 平台共享内容待同步',
478
583
  description: '个人默认已保存;动态 Skill 尚未重新生成。',
479
584
  },
480
- { kind: 'keep', group: 'preserved', title: '当前 native 插件和 activation 保持不变' },
481
- { kind: 'keep', group: 'preserved', title: 'project Agent role 继续按当前项目配置' },
585
+ { kind: 'keep', group: 'preserved', title: '当前平台共享内容和启用位置保持不变' },
586
+ { kind: 'keep', group: 'preserved', title: '当前项目 Agent role 继续使用当前项目配置' },
482
587
  ];
483
588
  return [
589
+ ...scriptTimeoutChanges(intent),
590
+ ...(evidence?.config?.shareability === 'unverified' ? [{
591
+ kind: 'warning',
592
+ group: 'configImpact',
593
+ title: 'Git 无法验证 project config 可共享',
594
+ description: '可以继续写入,但不要将该路径视为已验证可提交。',
595
+ }] : []),
484
596
  ...intent.changes.map((change) => ({
485
597
  kind: removeKeys.has(change.key) ? 'remove' : 'change',
486
598
  group: 'configChanges',
@@ -507,6 +619,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
507
619
  }
508
620
  const updateKeys = Object.keys(intent.updates ?? {});
509
621
  return [
622
+ ...scriptTimeoutChanges(intent),
510
623
  ...updateKeys.map((key) => ({
511
624
  kind: 'change',
512
625
  title: key,
@@ -519,6 +632,81 @@ function reviewChanges(intent, evidence, dependencyOverview) {
519
632
  })),
520
633
  ];
521
634
  }
635
+ function batchGroupEvidence(evidence, group) {
636
+ const manager = group.preview.codexManagerPreview;
637
+ return {
638
+ catalog: evidence.catalog,
639
+ ...(group.preview.dependencyPlan ? { dependencyPlan: group.preview.dependencyPlan } : {}),
640
+ ...(group.preview.codexReconcilePlans ? { codexReconcilePlans: group.preview.codexReconcilePlans } : {}),
641
+ ...(group.preview.operationPlan ? { operationPlan: group.preview.operationPlan } : {}),
642
+ ...(manager && manager.state !== 'blocked' ? {
643
+ codexManager: {
644
+ state: manager.state,
645
+ packageName: '@openai/codex',
646
+ version: manager.state === 'requires-install'
647
+ ? MANAGED_CODEX_VERSION
648
+ : manager.manager.version,
649
+ },
650
+ } : {}),
651
+ };
652
+ }
653
+ function batchReviewChanges(intent, evidence) {
654
+ const changes = evidence.groups.flatMap((group) => {
655
+ const representative = group.requestedPluginIds[0];
656
+ const groupEvidence = batchGroupEvidence(evidence, group);
657
+ const projected = reviewChanges({
658
+ kind: 'pluginWrite',
659
+ action: intent.action,
660
+ pluginId: representative,
661
+ platform: group.platform,
662
+ scope: intent.scope,
663
+ ...(intent.scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs: intent.scriptTimeoutMs }),
664
+ }, groupEvidence);
665
+ const targetChanges = group.preview.targetResults.map((target) => {
666
+ const pluginId = target.target.pluginId ?? `${group.sourceId}/${target.target.pluginName}`;
667
+ const requested = group.requestedPluginIds.includes(pluginId);
668
+ return {
669
+ kind: intent.action === 'uninstall' || intent.action === 'cleanup' ? 'remove'
670
+ : intent.action === 'install' ? 'add' : 'change',
671
+ group: requested ? 'requested' : 'dependencies',
672
+ pluginId,
673
+ relation: requested ? 'requested'
674
+ : (target.requiredBy ?? []).some((plugin) => group.requestedPluginIds.includes(plugin))
675
+ ? 'direct' : 'transitive',
676
+ title: pluginNameFromId(pluginId),
677
+ description: `${group.sourceId} · ${target.target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.target.scope === 'user' ? '所有项目' : '当前项目'}${target.state === 'failed' || target.action === 'skipped' ? ` · 无法执行${target.errorCode ? ` (${target.errorCode})` : ''}` : ''}`,
678
+ };
679
+ });
680
+ const effective = group.preview.dependencyPlan
681
+ ? projected
682
+ : [
683
+ ...targetChanges,
684
+ ...projected.filter((change) => change.group !== 'requested'
685
+ && change.group !== 'dependencies'),
686
+ ];
687
+ return [
688
+ ...effective.filter((change) => change.title !== 'preApply 脚本超时')
689
+ .map((change) => ({
690
+ ...change,
691
+ batchGroupId: group.groupId,
692
+ sourceId: group.sourceId,
693
+ })),
694
+ ...(!group.executable ? [{
695
+ kind: 'warning',
696
+ group: 'portableWarnings',
697
+ batchGroupId: group.groupId,
698
+ sourceId: group.sourceId,
699
+ title: `${group.sourceId} 组无法执行`,
700
+ description: '确认后保留本次预览失败;不会重新求值或开始写入。',
701
+ }] : []),
702
+ ];
703
+ });
704
+ return [
705
+ ...scriptTimeoutChanges(intent),
706
+ ...changes,
707
+ { kind: 'keep', group: 'preserved', title: '插件设置保持不变' },
708
+ ];
709
+ }
522
710
  function technicalDetails(evidence) {
523
711
  const short = (value) => {
524
712
  const normalized = value.startsWith('sha256:') ? value.slice(7) : value;
@@ -528,6 +716,13 @@ function technicalDetails(evidence) {
528
716
  `Catalog 配置:${short(evidence.catalog.configDigest)}`,
529
717
  `Catalog 组合:${short(evidence.catalog.compositeDigest)}`,
530
718
  ];
719
+ if ('groups' in evidence && Array.isArray(evidence.groups)) {
720
+ const batch = evidence;
721
+ details.push(`批量根插件:${batch.requestedPluginIds.length} 个`);
722
+ for (const group of batch.groups) {
723
+ details.push(`批量分组:${group.sourceId} · ${group.platform} · ${group.requestedPluginIds.length} 个根插件 · ${group.executable ? `计划 ${short(group.preview.operationPlan.planFingerprint)}` : '预览失败'}`);
724
+ }
725
+ }
531
726
  if (evidence.catalog.generationSetDigest) {
532
727
  details.push(`Catalog generation:${short(evidence.catalog.generationSetDigest)}`);
533
728
  }
@@ -576,14 +771,28 @@ function technicalDetails(evidence) {
576
771
  }
577
772
  export function buildOperationReviewProjection(options) {
578
773
  const { intent, evidence } = options;
774
+ const batchEvidence = intent.kind === 'pluginWriteBatch'
775
+ ? evidence
776
+ : undefined;
579
777
  const labels = operationLabels(intent, evidence);
580
- const requiresConfirmation = (evidence.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation)
778
+ const sharedNativeConfigApply = intent.kind === 'configSave'
779
+ && intent.apply
780
+ && (evidence.config?.sharedNativeRenderTargets?.length ?? 0) > 0
781
+ && (evidence.config?.affectedTargets.some((target) => target.platform === 'codex') ?? false);
782
+ const requiresConfirmation = sharedNativeConfigApply
783
+ || Boolean(batchEvidence?.groups.some((group) => (group.preview.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation)))
784
+ || (evidence.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation)
581
785
  || Boolean(evidence.sourceWrite
582
786
  && (!evidence.sourceWrite.inventoryComplete || evidence.sourceWrite.referenceCount > 0))
583
787
  || Boolean(evidence.catalogGeneration?.impact
584
788
  && (!evidence.catalogGeneration.impact.inventoryComplete
585
- || evidence.catalogGeneration.impact.affectedReferences.length > 0));
586
- const warning = (evidence.codexReconcilePlans ?? []).find((plan) => plan.review.sharedImpact.warning)?.review.sharedImpact.warning;
789
+ || evidence.catalogGeneration.impact.affectedReferences.length > 0))
790
+ || Boolean(evidence.catalogGeneration?.primaryImpact
791
+ && (!evidence.catalogGeneration.primaryImpact.inventoryComplete
792
+ || evidence.catalogGeneration.primaryImpact.affectedReferences.length > 0));
793
+ const warning = (evidence.codexReconcilePlans ?? []).find((plan) => plan.review.sharedImpact.warning)?.review.sharedImpact.warning
794
+ ?? batchEvidence?.groups.flatMap((group) => group.preview.codexReconcilePlans ?? [])
795
+ .find((plan) => plan.review.sharedImpact.warning)?.review.sharedImpact.warning;
587
796
  const preparationWarnings = (evidence.preparationWarnings ?? []).map(() => ({
588
797
  kind: 'warning',
589
798
  group: 'portableWarnings',
@@ -595,27 +804,38 @@ export function buildOperationReviewProjection(options) {
595
804
  description: labels.description,
596
805
  changes: [
597
806
  ...preparationWarnings,
598
- ...reviewChanges(intent, evidence, options.dependencyOverview),
807
+ ...(intent.kind === 'pluginWriteBatch'
808
+ ? batchReviewChanges(intent, batchEvidence)
809
+ : reviewChanges(intent, evidence, options.dependencyOverview)),
599
810
  ],
600
811
  technicalDetails: technicalDetails(evidence),
601
812
  ...(intent.kind === 'pluginWrite'
602
- ? { equivalentCommand: `aw ${intent.action === 'refreshAndSync' ? 'sync' : intent.action} ${intent.pluginId} --platform ${intent.platform} --scope ${intent.scope}${intent.action === 'refreshAndSync' ? ' --refresh' : ''}${(intent.dependencyScopeOverrides ?? []).map((override) => ` --dependency-scope ${override.pluginId}#${override.platform}=${override.scope}`).join('')}${intent.action === 'cleanup' && intent.force === true ? ' --force' : ''}${evidence.codexManager?.state === 'requires-install' ? ' --allow-managed-codex-install' : ''}${requiresConfirmation ? ' --yes' : ''}` }
813
+ ? { equivalentCommand: `aw ${intent.action === 'refreshAndSync' ? 'sync' : intent.action} ${intent.pluginId} --platform ${intent.platform} --scope ${intent.scope}${intent.action === 'refreshAndSync' ? ' --refresh' : ''}${scriptTimeoutChanges(intent).length > 0 ? ` --script-timeout ${parseScriptTimeoutMs(intent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS)}` : ''}${(intent.dependencyScopeOverrides ?? []).map((override) => ` --dependency-scope ${override.pluginId}#${override.platform}=${override.scope}`).join('')}${intent.action === 'cleanup' && intent.force === true ? ' --force' : ''}${evidence.codexManager?.state === 'requires-install' ? ' --allow-managed-codex-install' : ''}${requiresConfirmation ? ' --yes' : ''}` }
603
814
  : {}),
604
- primaryLabel: intent.kind === 'pluginWrite' && evidence.dependencyPlan
605
- ? `${labels.primaryLabel} ${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`
606
- : labels.primaryLabel,
815
+ primaryLabel: intent.kind === 'pluginWriteBatch'
816
+ ? `${labels.primaryLabel} ${batchEvidence.requestedPluginIds.length} 个插件`
817
+ : intent.kind === 'pluginWrite' && evidence.dependencyPlan
818
+ ? `${labels.primaryLabel} ${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`
819
+ : labels.primaryLabel,
607
820
  ...(labels.destructive ? { destructive: true } : {}),
608
821
  requiresConfirmation,
609
822
  sharedImpact: {
610
823
  pluginIds: [...new Set([
611
824
  ...(evidence.codexReconcilePlans ?? []).flatMap((plan) => plan.review.sharedImpact.pluginIds),
825
+ ...(batchEvidence?.groups.flatMap((group) => (group.preview.codexReconcilePlans ?? []).flatMap((plan) => plan.review.sharedImpact.pluginIds)) ?? []),
612
826
  ...(evidence.sourceWrite?.impact?.affectedReferences ?? []).flatMap((reference) => reference.pluginId ? [reference.pluginId] : []),
613
827
  ...(evidence.catalogGeneration?.impact?.affectedReferences ?? []).flatMap((reference) => reference.pluginId ? [reference.pluginId] : []),
828
+ ...(evidence.catalogGeneration?.primaryImpact?.affectedReferences ?? []).flatMap((reference) => reference.pluginId ? [reference.pluginId] : []),
829
+ ...(sharedNativeConfigApply && 'pluginId' in intent ? [intent.pluginId] : []),
614
830
  ])],
615
831
  ...(warning ? { warning }
616
- : requiresConfirmation && (evidence.sourceWrite || evidence.catalogGeneration?.impact)
617
- ? { warning: '确认来源影响后才会继续。未知影响不会按零处理。' }
618
- : {}),
832
+ : sharedNativeConfigApply
833
+ ? { warning: '更新共享 Codex native 动态 Skill 会影响所有启用项目。' }
834
+ : requiresConfirmation && (evidence.sourceWrite
835
+ || evidence.catalogGeneration?.impact
836
+ || evidence.catalogGeneration?.primaryImpact)
837
+ ? { warning: '确认来源影响后才会继续。未知影响不会按零处理。' }
838
+ : {}),
619
839
  },
620
840
  };
621
841
  }