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

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 (227) 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 +159 -48
  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 +22 -20
  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 +19 -0
  47. package/dist/src/commands/rebuild-command.js +828 -283
  48. package/dist/src/commands/rebuild-command.js.map +1 -1
  49. package/dist/src/commands/rebuild-integration.d.ts +25 -47
  50. package/dist/src/commands/rebuild-integration.js +1176 -1016
  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 +260 -91
  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 +10 -8
  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 +2065 -857
  85. package/dist/src/tui/console-app-v4.js.map +1 -1
  86. package/dist/src/tui/controller-runtime.js +731 -62
  87. package/dist/src/tui/controller-runtime.js.map +1 -1
  88. package/dist/src/tui/controller.d.ts +156 -10
  89. package/dist/src/tui/controller.js +1187 -163
  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 +7 -2
  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 +366 -99
  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-plan.d.ts +2 -2
  138. package/dist/src/utils/artifact-plan.js +8 -6
  139. package/dist/src/utils/artifact-plan.js.map +1 -1
  140. package/dist/src/utils/artifact-renderer.d.ts +2 -1
  141. package/dist/src/utils/artifact-renderer.js +139 -120
  142. package/dist/src/utils/artifact-renderer.js.map +1 -1
  143. package/dist/src/utils/artifact-request.d.ts +5 -0
  144. package/dist/src/utils/artifact-request.js +40 -0
  145. package/dist/src/utils/artifact-request.js.map +1 -1
  146. package/dist/src/utils/bundle-layout.js +4 -4
  147. package/dist/src/utils/bundle-layout.js.map +1 -1
  148. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  149. package/dist/src/utils/catalog-capability-projection.js +14 -0
  150. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  151. package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
  152. package/dist/src/utils/catalog-generation-resolver.js +49 -27
  153. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  154. package/dist/src/utils/catalog-generation-service.d.ts +8 -1
  155. package/dist/src/utils/catalog-generation-service.js +81 -47
  156. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  157. package/dist/src/utils/catalog-generation-store.d.ts +1 -0
  158. package/dist/src/utils/catalog-generation-store.js +60 -5
  159. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  160. package/dist/src/utils/catalog-operation-context.js +1 -0
  161. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  162. package/dist/src/utils/catalog-public-error.js +4 -0
  163. package/dist/src/utils/catalog-public-error.js.map +1 -1
  164. package/dist/src/utils/catalog-source-impact.d.ts +3 -2
  165. package/dist/src/utils/catalog-source-impact.js +142 -20
  166. package/dist/src/utils/catalog-source-impact.js.map +1 -1
  167. package/dist/src/utils/catalog-source-references.js +95 -9
  168. package/dist/src/utils/catalog-source-references.js.map +1 -1
  169. package/dist/src/utils/catalog-sources.d.ts +1 -1
  170. package/dist/src/utils/catalog-sources.js +19 -3
  171. package/dist/src/utils/catalog-sources.js.map +1 -1
  172. package/dist/src/utils/codex-manager.js +8 -0
  173. package/dist/src/utils/codex-manager.js.map +1 -1
  174. package/dist/src/utils/codex-native-observation.d.ts +34 -1
  175. package/dist/src/utils/codex-native-observation.js +132 -0
  176. package/dist/src/utils/codex-native-observation.js.map +1 -1
  177. package/dist/src/utils/codex-reconciler.d.ts +0 -2
  178. package/dist/src/utils/codex-reconciler.js.map +1 -1
  179. package/dist/src/utils/codex-shared-native.d.ts +2 -26
  180. package/dist/src/utils/codex-shared-native.js +8 -82
  181. package/dist/src/utils/codex-shared-native.js.map +1 -1
  182. package/dist/src/utils/git-catalog-provider.d.ts +3 -0
  183. package/dist/src/utils/git-catalog-provider.js +40 -1
  184. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  185. package/dist/src/utils/local-marketplace.d.ts +1 -0
  186. package/dist/src/utils/local-marketplace.js +69 -73
  187. package/dist/src/utils/local-marketplace.js.map +1 -1
  188. package/dist/src/utils/multi-source-catalog.js +1 -0
  189. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  190. package/dist/src/utils/native-cli.d.ts +9 -4
  191. package/dist/src/utils/native-cli.js +280 -34
  192. package/dist/src/utils/native-cli.js.map +1 -1
  193. package/dist/src/utils/path-catalog-provider.js +2 -0
  194. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  195. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  196. package/dist/src/utils/plugin-config-impact.js +298 -0
  197. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  198. package/dist/src/utils/plugin-config-write.d.ts +36 -14
  199. package/dist/src/utils/plugin-config-write.js +709 -225
  200. package/dist/src/utils/plugin-config-write.js.map +1 -1
  201. package/dist/src/utils/plugin-config.d.ts +58 -4
  202. package/dist/src/utils/plugin-config.js +328 -60
  203. package/dist/src/utils/plugin-config.js.map +1 -1
  204. package/dist/src/utils/plugin-scripts.d.ts +20 -12
  205. package/dist/src/utils/plugin-scripts.js +389 -114
  206. package/dist/src/utils/plugin-scripts.js.map +1 -1
  207. package/dist/src/utils/process-tree.d.ts +2 -2
  208. package/dist/src/utils/process-tree.js +1 -1
  209. package/dist/src/utils/process-tree.js.map +1 -1
  210. package/dist/src/utils/project-boundary.d.ts +11 -1
  211. package/dist/src/utils/project-boundary.js +33 -11
  212. package/dist/src/utils/project-boundary.js.map +1 -1
  213. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  214. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  215. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  216. package/dist/src/utils/sensitive-content.d.ts +4 -0
  217. package/dist/src/utils/sensitive-content.js +36 -0
  218. package/dist/src/utils/sensitive-content.js.map +1 -0
  219. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  220. package/dist/src/utils/windows-claude-shim.js +141 -0
  221. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  222. package/dist/vitest.config.js +91 -10
  223. package/dist/vitest.config.js.map +1 -1
  224. package/package.json +9 -9
  225. package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
  226. package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
  227. 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,109 @@ 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 preApplyScopeChanges(targets) {
35
+ return [...new Map(targets.map((target) => [[
36
+ target.pluginId,
37
+ target.platform,
38
+ target.scope,
39
+ ].join('\0'), target])).values()].map((target) => ({
40
+ kind: 'change',
41
+ group: 'runtime',
42
+ pluginId: target.pluginId,
43
+ ...(target.relation ? { relation: target.relation } : {}),
44
+ title: `运行 ${target.pluginId} preApply`,
45
+ description: `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`,
46
+ }));
47
+ }
48
+ function operationPreApplyTargets(evidence) {
49
+ return (evidence?.operationPlan?.actions ?? []).flatMap((action) => action.kind === 'runPreApply'
50
+ ? [{
51
+ ...action,
52
+ ...(evidence?.dependencyPlan
53
+ ? { relation: dependencyRelation(evidence.dependencyPlan, action.pluginId) }
54
+ : {}),
55
+ }]
56
+ : []);
57
+ }
58
+ function sourceMutationReviewChanges(mutation) {
59
+ if (mutation.action === 'add') {
60
+ const source = mutation.source;
61
+ return [
62
+ { kind: 'add', title: '保存来源配置' },
63
+ { kind: 'add', title: source.type === 'git' ? 'Git URL' : '本地目录', description: '已设置' },
64
+ ...(source.type === 'git' ? [{
65
+ kind: 'add',
66
+ title: 'Git ref policy',
67
+ description: source.refPolicy.type === 'cliChannel'
68
+ ? '跟随 CLI channel'
69
+ : `固定 ref · ${source.refPolicy.ref}`,
70
+ }, {
71
+ kind: 'add',
72
+ title: '插件目录子目录',
73
+ description: source.subdir ?? '根目录',
74
+ }] : []),
75
+ {
76
+ kind: 'add',
77
+ title: '自动检查',
78
+ description: source.autoCheck === false ? '关闭' : '开启',
79
+ },
80
+ ];
81
+ }
82
+ const update = mutation.update;
83
+ const changes = [];
84
+ if ('url' in update && update.url !== undefined) {
85
+ changes.push({ kind: 'change', title: 'Git URL', description: '将更新' });
86
+ }
87
+ if ('path' in update && update.path !== undefined) {
88
+ changes.push({ kind: 'change', title: '本地目录', description: '将更新' });
89
+ }
90
+ if ('refPolicy' in update && update.refPolicy)
91
+ changes.push({
92
+ kind: 'change',
93
+ title: 'Git ref policy',
94
+ description: update.refPolicy.type === 'cliChannel'
95
+ ? '跟随 CLI channel'
96
+ : `固定 ref · ${update.refPolicy.ref}`,
97
+ });
98
+ if ('subdir' in update && update.subdir !== undefined)
99
+ changes.push({
100
+ kind: 'change',
101
+ title: '插件目录子目录',
102
+ description: update.subdir ?? '根目录',
103
+ });
104
+ if ('autoCheck' in update && update.autoCheck !== undefined)
105
+ changes.push({
106
+ kind: 'change',
107
+ title: '自动检查',
108
+ description: update.autoCheck ? '开启' : '关闭',
109
+ });
110
+ return changes.length > 0 ? changes : [{ kind: 'change', title: '更新来源配置' }];
111
+ }
10
112
  function operationLabels(intent, evidence) {
11
113
  if (intent.kind === 'catalogSourcesRecovery') {
12
114
  return {
13
- title: '恢复 Catalog 来源配置',
115
+ title: '恢复插件目录来源配置',
14
116
  description: '备份损坏配置,并恢复唯一的主来源 official。',
15
117
  primaryLabel: '备份并恢复官方默认来源',
16
118
  destructive: false,
@@ -24,12 +126,21 @@ function operationLabels(intent, evidence) {
24
126
  destructive: false,
25
127
  };
26
128
  }
27
- if (intent.kind === 'pluginWrite' && intent.dependencyPlacementAdjustment) {
129
+ if (intent.kind === 'pluginWriteBatch') {
130
+ const labels = {
131
+ install: ['批量添加插件', '添加所选插件及其必需依赖。'],
132
+ sync: ['批量同步插件', '按已保存的设置同步所选插件。'],
133
+ reinstall: ['批量重新安装插件', '清理 AW 管理的内容后完整重建所选插件。'],
134
+ uninstall: ['批量移除插件', '移除所选插件的 AW 管理安装内容。配置保持不变。'],
135
+ cleanup: ['批量清理插件', '只移除可证明属于 AW 的现场内容。配置保持不变。'],
136
+ };
137
+ const [title, description] = labels[intent.action];
28
138
  return {
29
- title: '调整依赖位置',
30
- description: '按最终依赖位置更新 desired intent,并应用非破坏性变更。',
31
- primaryLabel: '应用依赖位置',
32
- destructive: false,
139
+ title,
140
+ description,
141
+ primaryLabel: title,
142
+ destructive: intent.action === 'reinstall' || intent.action === 'uninstall'
143
+ || intent.action === 'cleanup',
33
144
  };
34
145
  }
35
146
  if (intent.kind === 'pluginWrite') {
@@ -59,11 +170,11 @@ function operationLabels(intent, evidence) {
59
170
  }
60
171
  const labels = {
61
172
  install: ['添加插件', '添加所选平台和使用位置。', false],
62
- refreshAndSync: ['刷新并同步', '刷新相关来源后,使用新的 Active 内容同步所选安装。', false],
173
+ refreshAndSync: ['刷新并同步', '刷新相关来源后,使用新的当前启用版本同步所选安装。', false],
63
174
  reinstall: ['重新安装插件', '清理 AW 管理的内容后完整重建。', true],
64
175
  uninstall: ['移除插件', '移除 AW 管理的安装内容。配置保持不变。', true],
65
176
  cleanup: ['清理插件', '只移除可证明属于 AW 的现场内容。配置保持不变。', true],
66
- sync: ['同步插件', '按已保存的 desired intent 修复当前现场。', false],
177
+ sync: ['同步插件', '按已保存使用设置修复当前使用位置。', false],
67
178
  };
68
179
  const [title, description, destructive] = labels[intent.action];
69
180
  return { title, description, primaryLabel: title, destructive };
@@ -100,7 +211,7 @@ function operationLabels(intent, evidence) {
100
211
  if (autoCheck !== undefined) {
101
212
  return {
102
213
  title: '保存自动检查设置',
103
- description: '只保存自动检查设置;Active Catalog 和 Catalog store 保持不变。',
214
+ description: '只保存自动检查设置;当前启用版本和本地目录副本保持不变。',
104
215
  primaryLabel: '保存设置',
105
216
  destructive: false,
106
217
  };
@@ -117,18 +228,18 @@ function operationLabels(intent, evidence) {
117
228
  if (intent.kind === 'catalogGeneration') {
118
229
  if (intent.action === 'update' && evidence?.catalogGeneration?.exactRepair) {
119
230
  return {
120
- title: '验证并精确修复 Catalog',
121
- description: '候选与当前 Active store key 相同。精确替换损坏条目,并保留 Active 身份。',
231
+ title: '验证并精确修复插件目录',
232
+ description: '待检查版本与当前启用版本的技术标识相同。精确替换损坏内容,并保留当前启用版本。',
122
233
  primaryLabel: '验证并精确修复',
123
234
  destructive: false,
124
235
  };
125
236
  }
126
237
  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 保持不变。', '恢复状态并启用'],
238
+ activate: ['获取并启用插件目录', '检查待检查版本后启用。不会升级已安装插件,也不会修改其他来源。', '获取并启用'],
239
+ update: ['检查并切换插件目录', '检查待检查版本后切换当前启用版本。失败时保留原当前启用版本。', '检查并切换'],
240
+ repair: ['重新下载并修复插件目录', '只修复这个来源的当前启用版本,不删除其他目录版本副本。', '重新下载并修复'],
241
+ replaceDamaged: ['替换损坏的当前启用版本', '使用待检查版本替换无法精确修复的当前启用版本。失败时保留原版本。', '确认并替换'],
242
+ recoverState: ['恢复插件目录状态并启用', '重建无效的目录版本状态,并只启用已确认的目标来源。来源配置和本地目录副本保持不变。', '恢复状态并启用'],
132
243
  };
133
244
  const [title, description, primaryLabel] = labels[intent.action];
134
245
  return { title, description, primaryLabel, destructive: false };
@@ -180,20 +291,24 @@ function reviewChanges(intent, evidence, dependencyOverview) {
180
291
  }
181
292
  if (intent.kind === 'pluginWrite') {
182
293
  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
- : [];
294
+ const runtimeChanges = [
295
+ ...scriptTimeoutChanges(intent),
296
+ ...preApplyScopeChanges(operationPreApplyTargets(evidence)),
297
+ ...(evidence?.codexManager
298
+ ? [{
299
+ kind: evidence.codexManager.state === 'requires-install' ? 'add' : 'keep',
300
+ group: 'runtime',
301
+ title: evidence.codexManager.state === 'requires-install'
302
+ ? '获取 Codex 运行组件'
303
+ : 'Codex 运行组件',
304
+ description: evidence.codexManager.state === 'requires-install'
305
+ ? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · AW 管理缓存`
306
+ : evidence.codexManager.state === 'cached'
307
+ ? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · 复用 AW 管理缓存`
308
+ : `使用兼容的系统 Codex ${evidence.codexManager.version}`,
309
+ }]
310
+ : []),
311
+ ];
197
312
  const codexChanges = (evidence?.codexReconcilePlans ?? []).flatMap((codexPlan) => [
198
313
  ...codexPlan.sharedNativeActions.map((action) => ({
199
314
  kind: action.action === 'reuse' || action.action === 'none' ? 'keep'
@@ -202,22 +317,29 @@ function reviewChanges(intent, evidence, dependencyOverview) {
202
317
  pluginId: action.pluginId,
203
318
  title: action.codexPluginId,
204
319
  description: action.action === 'reuse'
205
- ? '复用已存在的精确 shared native;不建立 ownership ledger'
206
- : `${action.action} · shared${action.affectsOtherScopes ? ' · 可能影响其他项目' : ''}`,
320
+ ? '复用完全相同的平台共享内容'
321
+ : `${{
322
+ install: '添加',
323
+ update: '更新',
324
+ repair: '修复',
325
+ replace: '替换',
326
+ remove: '移除',
327
+ none: '保持不变',
328
+ }[action.action]}平台共享内容${action.affectsOtherScopes ? ' · 可能影响其他项目' : ''}`,
207
329
  })),
208
330
  ...codexPlan.activationActions.user.map((action) => ({
209
331
  kind: action.action === 'none' ? 'keep' : action.desired ? 'change' : 'remove',
210
332
  group: 'userActivation',
211
333
  pluginId: action.pluginId,
212
334
  title: action.codexPluginId,
213
- description: `desired=${String(action.desired)} · ${action.action}`,
335
+ description: `${action.desired ? '启用' : '停用'}所有项目位置 · ${action.action === 'none' ? '保持不变' : '将更新'}`,
214
336
  })),
215
337
  ...codexPlan.activationActions.project.map((action) => ({
216
338
  kind: action.action === 'none' ? 'keep' : action.desired ? 'change' : 'remove',
217
339
  group: 'projectActivation',
218
340
  pluginId: action.pluginId,
219
341
  title: action.codexPluginId,
220
- description: `desired=${String(action.desired)} · ${action.action}`,
342
+ description: `${action.desired ? '启用' : '停用'}当前项目位置 · ${action.action === 'none' ? '保持不变' : '将更新'}`,
221
343
  })),
222
344
  ...[...codexPlan.extensionWrites, ...codexPlan.extensionRemovals]
223
345
  .map((action) => ({
@@ -225,7 +347,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
225
347
  group: 'awExtensions',
226
348
  pluginId: action.pluginId,
227
349
  title: action.componentKey,
228
- description: `${action.scope} · ${action.action}`,
350
+ description: `${scopeLabel(action.scope)} · ${action.action === 'write' ? '写入' : '移除'}`,
229
351
  })),
230
352
  ]);
231
353
  const plan = evidence?.dependencyPlan;
@@ -239,18 +361,14 @@ function reviewChanges(intent, evidence, dependencyOverview) {
239
361
  pluginId: target.pluginId,
240
362
  relation,
241
363
  title: pluginNameFromId(target.pluginId),
242
- description: `${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.resolvedScope === 'user' ? '所有项目' : '当前项目'}${target.action === 'keep' ? ' · 已可用' : ''}${target.assignmentSource === 'existingWiderTarget' ? ' · 复用健康的所有项目安装' : target.assignmentSource === 'consumerPromotion' ? ' · 随上游依赖一并调整' : ''}`,
364
+ description: `${platformLabel(target.platform)} · ${scopeLabel(target.resolvedScope)}${target.action === 'keep' ? ' · 已可用' : ''}${target.assignmentSource === 'existingWiderTarget' ? ' · 复用健康的所有项目安装' : target.assignmentSource === 'consumerPromotion' ? ' · 根据依赖自动扩大范围' : ''}`,
243
365
  };
244
366
  });
245
367
  changes.push({
246
368
  kind: 'keep',
247
369
  group: 'preserved',
248
- title: intent.dependencyPlacementAdjustment
249
- ? '根插件和插件设置保持不变'
250
- : '插件设置和已有依赖保持不变',
251
- description: intent.dependencyPlacementAdjustment
252
- ? '这里只调整所列 dependency placement。'
253
- : '移除根插件时不会自动清理依赖。',
370
+ title: '插件设置和已有依赖保持不变',
371
+ description: '移除根插件时不会自动清理依赖。',
254
372
  });
255
373
  return [...changes, ...codexChanges, ...runtimeChanges];
256
374
  }
@@ -262,14 +380,14 @@ function reviewChanges(intent, evidence, dependencyOverview) {
262
380
  group: 'requested',
263
381
  pluginId: intent.pluginId,
264
382
  relation: 'requested',
265
- title: `${platform === 'codex' ? 'Codex' : 'Claude Code'} · ${intent.scope === 'user' ? '所有项目' : '当前项目'}`,
383
+ title: `${platformLabel(platform)} · ${scopeLabel(intent.scope)}`,
266
384
  description: intent.action === 'uninstall' || intent.action === 'cleanup'
267
385
  ? '插件设置不会删除。'
268
386
  : '执行前会再次检查当前安装状态。',
269
387
  }));
270
388
  if (intent.action === 'cleanup' && evidence?.offlineCleanup) {
271
389
  const actionLabels = {
272
- removeOwnedOutput: '移除 AW-owned 输出',
390
+ removeOwnedOutput: '移除 AW 管理输出',
273
391
  removeNativeRegistration: '移除原生插件注册',
274
392
  };
275
393
  changes.push(...evidence.offlineCleanup.plan.actions.map((action) => ({
@@ -305,8 +423,8 @@ function reviewChanges(intent, evidence, dependencyOverview) {
305
423
  changes.push({
306
424
  kind: 'warning',
307
425
  group: 'preserved',
308
- title: `后续:${intent.scope === 'user' ? '当前项目' : '所有项目'} sync`,
309
- description: intent.recoveryDescription,
426
+ title: `后续:同步${intent.scope === 'user' ? '当前项目' : '所有项目'}位置`,
427
+ description: '完成此操作后,同步要保留的使用位置。',
310
428
  });
311
429
  }
312
430
  return [...changes, ...codexChanges, ...runtimeChanges];
@@ -315,6 +433,20 @@ function reviewChanges(intent, evidence, dependencyOverview) {
315
433
  const layerLabel = intent.scope === 'user'
316
434
  ? '个人默认'
317
435
  : '当前项目';
436
+ const impact = evidence?.config;
437
+ const impactChanges = impact?.impactState === 'unknown'
438
+ ? [{
439
+ kind: 'warning', group: 'configImpact', title: '设置重置后的应用影响未知',
440
+ description: '当前无法解析来源或设置;重置仍会删除所选设置层。',
441
+ }]
442
+ : impact?.noEffectiveChange
443
+ ? [{ kind: 'keep', group: 'configImpact', title: '有效设置输入不变;无需同步当前安装' }]
444
+ : (impact?.affectedTargets ?? []).length > 0
445
+ ? impact.affectedTargets.map((target) => ({
446
+ kind: 'warning', group: 'configImpact',
447
+ title: `重置后需同步 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
448
+ }))
449
+ : [{ kind: 'keep', group: 'configImpact', title: '只删除设置层;当前安装保持不变' }];
318
450
  return [
319
451
  ...(intent.changes?.map((change) => ({
320
452
  kind: 'remove', group: 'configChanges', title: `${layerLabel} · ${change.title}`,
@@ -322,7 +454,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
322
454
  ? `${change.before} → ${change.after};${change.children.length} 个子项变化;${change.children.map((child) => `${child.property}: ${child.private ? '已设置 → 将更新' : `${child.before} → ${child.after}`}`).join(';')}`
323
455
  : `${change.before} → ${change.after}`,
324
456
  })) ?? [{ kind: 'remove', group: 'configChanges', title: `删除${layerLabel}中的已存值`, description: '删除整个设置层且不创建备份;有效值将恢复为继承值或默认值。' }]),
325
- { kind: 'keep', group: 'configImpact', title: '只保存设置;当前安装保持不变' },
457
+ ...impactChanges,
326
458
  { kind: 'keep', group: 'preserved', title: intent.scope === 'user' ? '当前项目设置保持不变' : '个人默认保持不变' },
327
459
  ];
328
460
  }
@@ -365,75 +497,74 @@ function reviewChanges(intent, evidence, dependencyOverview) {
365
497
  if (intent.kind === 'catalogGeneration') {
366
498
  const generation = evidence?.catalogGeneration;
367
499
  if (!generation) {
368
- throw plannerError('catalogGenerationUnavailable', '缺少 Catalog 候选证据。');
500
+ throw plannerError('catalogGenerationUnavailable', '缺少插件目录待检查版本证据。');
369
501
  }
370
502
  return [
371
503
  ...(generation.previousActive ? [{
372
504
  kind: 'keep',
373
505
  title: generation.previousActiveVersion
374
- ? `当前 Active Catalog ${generation.previousActiveVersion}` : '当前 Active',
506
+ ? `当前启用版本 ${generation.previousActiveVersion}` : '当前启用版本',
375
507
  description: `${generation.previousActive.resolvedCommit.slice(0, 8)} · ${generation.previousActive.snapshotDigest.slice(0, 15)}`,
376
508
  }] : []),
377
509
  {
378
510
  kind: intent.action === 'activate' ? 'add' : 'change',
379
511
  title: generation.exactRepair
380
- ? `精确修复候选 Catalog ${generation.candidate.catalogVersion}`
381
- : `候选 Catalog ${generation.candidate.catalogVersion}`,
512
+ ? `精确修复待检查版本 ${generation.candidate.catalogVersion}`
513
+ : `待检查版本 ${generation.candidate.catalogVersion}`,
382
514
  description: `${generation.candidate.resolvedCommit.slice(0, 8)} · ${generation.candidate.snapshotDigest.slice(0, 15)}`,
383
515
  },
384
516
  ...(generation.exactRepair ? [{
385
517
  kind: 'change',
386
- title: '精确替换损坏的 Active 条目',
387
- description: 'logical identity、snapshot digest、manifest digest 和 commit 保持不变。',
518
+ title: '精确替换损坏的当前启用版本',
519
+ description: '版本和内容的技术标识保持不变。',
388
520
  }] : []),
389
521
  ...(intent.action === 'replaceDamaged' ? [{
390
522
  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) ?? '未知'}`,
523
+ title: '使用待检查版本替换损坏的当前启用版本',
524
+ description: '旧版本无法安全比较;确认后只切换到本次已检查的版本。',
405
525
  }] : []),
526
+ ...(generation.sourceMutation ? sourceMutationReviewChanges(generation.sourceMutation) : []),
406
527
  ...(generation.explicitSourceChange && generation.candidateRelation === 'rollbackDetected' ? [{
407
528
  kind: 'warning',
408
- title: '显式来源切换将降低 Catalog 版本',
529
+ title: '来源切换将降低插件目录版本',
409
530
  description: `${generation.previousActiveVersion ?? '当前版本'} → ${generation.candidate.catalogVersion}`,
410
531
  }] : generation.explicitSourceChange && generation.candidateRelation === 'versionConflict' ? [{
411
532
  kind: 'warning',
412
- title: '显式来源切换包含同版本不同内容',
533
+ title: '来源切换包含同版本不同内容',
413
534
  description: `${generation.candidate.catalogVersion} · ${generation.candidate.snapshotDigest.slice(0, 15)}`,
414
535
  }] : generation.explicitSourceChange && generation.candidateRelation === 'incompatible' ? [{
415
536
  kind: 'warning',
416
- title: '显式来源切换将改变 Catalog logical identity',
417
- description: '确认前请核对新来源身份和候选摘要。',
537
+ title: '来源切换将改变插件目录身份',
538
+ description: '确认前请核对新来源身份和待检查版本摘要。',
418
539
  }] : []),
419
540
  ...((generation.impact?.affectedReferences ?? []).length > 0 ? [{
420
541
  kind: 'warning',
421
- title: '现有引用受候选变化影响',
542
+ title: '现有引用受待检查版本影响',
422
543
  description: `${generation.impact.affectedReferences.length} 项;已安装插件不会自动修改。`,
423
544
  }] : []),
424
545
  ...(generation.impact && !generation.impact.inventoryComplete ? [{
425
546
  kind: 'warning',
426
- title: '候选影响未知',
427
- description: '部分状态或旧 Manifest 无法读取;不能把影响视为零。',
547
+ title: '待检查版本的影响未知',
548
+ description: '部分状态或旧插件定义无法读取;不能把影响视为零。',
428
549
  }] : []),
429
- { kind: 'keep', title: '不会升级已安装 Plugin' },
550
+ ...((generation.primaryImpact?.affectedReferences ?? []).length > 0 ? [{
551
+ kind: 'warning',
552
+ title: '切换主来源会影响现有引用',
553
+ description: `${generation.primaryImpact.affectedReferences.length} 项;切换将在插件目录启用后执行。`,
554
+ }] : []),
555
+ ...(generation.primaryImpact && !generation.primaryImpact.inventoryComplete ? [{
556
+ kind: 'warning',
557
+ title: '主来源切换影响未知',
558
+ description: '部分状态无法读取;不能把影响视为零。',
559
+ }] : []),
560
+ { kind: 'keep', title: '不会升级已安装插件' },
430
561
  { kind: 'keep', title: intent.action === 'recoverState'
431
- ? '不会修改其他来源配置或 store' : '不会修改其他来源' },
432
- { kind: 'keep', title: '失败时保留旧 Active' },
562
+ ? '不会修改其他来源配置或本地目录副本' : '不会修改其他来源' },
563
+ { kind: 'keep', title: '失败时保留原当前启用版本' },
433
564
  ...(intent.action === 'recoverState' ? [{
434
565
  kind: 'warning',
435
- title: '无效 state 中的其他 Active 指针不会被信任或保留',
436
- description: '其他来源配置和 store 内容不会删除;可分别重新启用。',
566
+ title: '无效状态中的其他当前启用版本记录不会保留',
567
+ description: '其他来源配置和本地目录副本不会删除;可分别重新启用。',
437
568
  }] : []),
438
569
  ];
439
570
  }
@@ -445,6 +576,8 @@ function reviewChanges(intent, evidence, dependencyOverview) {
445
576
  const layerLabel = intent.scope === 'user' ? '个人默认' : '当前项目';
446
577
  if (intent.retryApply) {
447
578
  return [
579
+ ...scriptTimeoutChanges(intent),
580
+ ...preApplyScopeChanges(evidence?.config?.preApplyAuthorization ?? []),
448
581
  ...(evidence?.config?.affectedTargets.map((target) => ({
449
582
  kind: 'change',
450
583
  group: 'configImpact',
@@ -455,32 +588,41 @@ function reviewChanges(intent, evidence, dependencyOverview) {
455
588
  ];
456
589
  }
457
590
  if (intent.changes) {
591
+ const noEffectiveChange = evidence?.config?.noEffectiveChange === true;
458
592
  const hasAffectedCodexTarget = evidence?.config?.affectedTargets.some((target) => target.platform === 'codex') ?? false;
459
593
  const sharedNativeTargets = hasAffectedCodexTarget
460
594
  ? evidence?.config?.sharedNativeRenderTargets ?? []
461
595
  : [];
462
- const sharedNativeImpact = sharedNativeTargets.length === 0
596
+ const sharedNativeImpact = noEffectiveChange || sharedNativeTargets.length === 0
463
597
  ? []
464
598
  : intent.apply
465
599
  ? [
466
600
  {
467
601
  kind: 'change', group: 'configImpact',
468
602
  title: `重新生成 ${sharedNativeTargets.map((target) => target.skillKey).join('、')} 动态 Skill`,
469
- description: '配置来源为个人默认;更新共享 Codex native 插件会影响所有启用项目。',
603
+ description: '配置来源为个人默认;更新 Codex 平台共享内容会影响所有启用项目。',
470
604
  },
471
- { kind: 'keep', group: 'preserved', title: '保留当前 user/project activation' },
472
- { kind: 'keep', group: 'preserved', title: 'project Agent role 继续按当前项目配置' },
605
+ { kind: 'keep', group: 'preserved', title: '保留所有项目和当前项目的启用位置' },
606
+ { kind: 'keep', group: 'preserved', title: '当前项目 Agent role 继续使用当前项目配置' },
473
607
  ]
474
608
  : [
475
609
  {
476
610
  kind: 'warning', group: 'configImpact',
477
- title: '共享 Codex native render 待同步',
611
+ title: 'Codex 平台共享内容待同步',
478
612
  description: '个人默认已保存;动态 Skill 尚未重新生成。',
479
613
  },
480
- { kind: 'keep', group: 'preserved', title: '当前 native 插件和 activation 保持不变' },
481
- { kind: 'keep', group: 'preserved', title: 'project Agent role 继续按当前项目配置' },
614
+ { kind: 'keep', group: 'preserved', title: '当前平台共享内容和启用位置保持不变' },
615
+ { kind: 'keep', group: 'preserved', title: '当前项目 Agent role 继续使用当前项目配置' },
482
616
  ];
483
617
  return [
618
+ ...scriptTimeoutChanges(intent),
619
+ ...preApplyScopeChanges(evidence?.config?.preApplyAuthorization ?? []),
620
+ ...(evidence?.config?.shareability === 'unverified' ? [{
621
+ kind: 'warning',
622
+ group: 'configImpact',
623
+ title: 'Git 无法验证 project config 可共享',
624
+ description: '可以继续写入,但不要将该路径视为已验证可提交。',
625
+ }] : []),
484
626
  ...intent.changes.map((change) => ({
485
627
  kind: removeKeys.has(change.key) ? 'remove' : 'change',
486
628
  group: 'configChanges',
@@ -494,19 +636,33 @@ function reviewChanges(intent, evidence, dependencyOverview) {
494
636
  : privateKeys.has(change.key) ? '已设置 → 将更新'
495
637
  : `${change.before} → ${change.after}`,
496
638
  })),
639
+ ...(evidence?.config?.impactState === 'unknown' ? [{
640
+ kind: 'warning', group: 'configImpact',
641
+ title: '设置的应用影响未知',
642
+ description: '当前无法比较保存前后的有效设置输入。',
643
+ }] : noEffectiveChange ? [{
644
+ kind: 'keep', group: 'configImpact',
645
+ title: '有效设置输入不变;无需重新应用',
646
+ }] : []),
497
647
  ...sharedNativeImpact,
498
- ...(sharedNativeTargets.length > 0
648
+ ...(evidence?.config?.impactState === 'unknown' || noEffectiveChange || sharedNativeTargets.length > 0
499
649
  ? []
500
650
  : intent.apply && (evidence?.config?.affectedTargets.length ?? 0) > 0
501
651
  ? evidence.config.affectedTargets.map((target) => ({
502
652
  kind: 'change', group: 'configImpact', title: `重新应用 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
503
653
  }))
504
- : [{ kind: 'keep', group: 'configImpact', title: '当前没有可见安装;只保存设置' }]),
654
+ : !intent.apply && (evidence?.config?.affectedTargets.length ?? 0) > 0
655
+ ? evidence.config.affectedTargets.map((target) => ({
656
+ kind: 'warning', group: 'configImpact', title: `保存后需同步 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
657
+ }))
658
+ : [{ kind: 'keep', group: 'configImpact', title: '当前没有可见安装;只保存设置' }]),
505
659
  { kind: 'keep', group: 'preserved', title: '其他项目不会被自动扫描' },
506
660
  ];
507
661
  }
508
662
  const updateKeys = Object.keys(intent.updates ?? {});
509
663
  return [
664
+ ...scriptTimeoutChanges(intent),
665
+ ...preApplyScopeChanges(evidence?.config?.preApplyAuthorization ?? []),
510
666
  ...updateKeys.map((key) => ({
511
667
  kind: 'change',
512
668
  title: key,
@@ -519,6 +675,81 @@ function reviewChanges(intent, evidence, dependencyOverview) {
519
675
  })),
520
676
  ];
521
677
  }
678
+ function batchGroupEvidence(evidence, group) {
679
+ const manager = group.preview.codexManagerPreview;
680
+ return {
681
+ catalog: evidence.catalog,
682
+ ...(group.preview.dependencyPlan ? { dependencyPlan: group.preview.dependencyPlan } : {}),
683
+ ...(group.preview.codexReconcilePlans ? { codexReconcilePlans: group.preview.codexReconcilePlans } : {}),
684
+ ...(group.preview.operationPlan ? { operationPlan: group.preview.operationPlan } : {}),
685
+ ...(manager && manager.state !== 'blocked' ? {
686
+ codexManager: {
687
+ state: manager.state,
688
+ packageName: '@openai/codex',
689
+ version: manager.state === 'requires-install'
690
+ ? MANAGED_CODEX_VERSION
691
+ : manager.manager.version,
692
+ },
693
+ } : {}),
694
+ };
695
+ }
696
+ function batchReviewChanges(intent, evidence) {
697
+ const changes = evidence.groups.flatMap((group) => {
698
+ const representative = group.requestedPluginIds[0];
699
+ const groupEvidence = batchGroupEvidence(evidence, group);
700
+ const projected = reviewChanges({
701
+ kind: 'pluginWrite',
702
+ action: intent.action,
703
+ pluginId: representative,
704
+ platform: group.platform,
705
+ scope: intent.scope,
706
+ ...(intent.scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs: intent.scriptTimeoutMs }),
707
+ }, groupEvidence);
708
+ const targetChanges = group.preview.targetResults.map((target) => {
709
+ const pluginId = target.target.pluginId ?? `${group.sourceId}/${target.target.pluginName}`;
710
+ const requested = group.requestedPluginIds.includes(pluginId);
711
+ return {
712
+ kind: intent.action === 'uninstall' || intent.action === 'cleanup' ? 'remove'
713
+ : intent.action === 'install' ? 'add' : 'change',
714
+ group: requested ? 'requested' : 'dependencies',
715
+ pluginId,
716
+ relation: requested ? 'requested'
717
+ : (target.requiredBy ?? []).some((plugin) => group.requestedPluginIds.includes(plugin))
718
+ ? 'direct' : 'transitive',
719
+ title: pluginNameFromId(pluginId),
720
+ description: `${group.sourceId} · ${target.target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.target.scope === 'user' ? '所有项目' : '当前项目'}${target.state === 'failed' || target.action === 'skipped' ? ` · 无法执行${target.errorCode ? ` (${target.errorCode})` : ''}` : ''}`,
721
+ };
722
+ });
723
+ const effective = group.preview.dependencyPlan
724
+ ? projected
725
+ : [
726
+ ...targetChanges,
727
+ ...projected.filter((change) => change.group !== 'requested'
728
+ && change.group !== 'dependencies'),
729
+ ];
730
+ return [
731
+ ...effective.filter((change) => change.title !== 'preApply 脚本超时')
732
+ .map((change) => ({
733
+ ...change,
734
+ batchGroupId: group.groupId,
735
+ sourceId: group.sourceId,
736
+ })),
737
+ ...(!group.executable ? [{
738
+ kind: 'warning',
739
+ group: 'portableWarnings',
740
+ batchGroupId: group.groupId,
741
+ sourceId: group.sourceId,
742
+ title: `${group.sourceId} 组无法执行`,
743
+ description: '确认后保留本次预览失败;不会重新求值或开始写入。',
744
+ }] : []),
745
+ ];
746
+ });
747
+ return [
748
+ ...scriptTimeoutChanges(intent),
749
+ ...changes,
750
+ { kind: 'keep', group: 'preserved', title: '插件设置保持不变' },
751
+ ];
752
+ }
522
753
  function technicalDetails(evidence) {
523
754
  const short = (value) => {
524
755
  const normalized = value.startsWith('sha256:') ? value.slice(7) : value;
@@ -528,6 +759,13 @@ function technicalDetails(evidence) {
528
759
  `Catalog 配置:${short(evidence.catalog.configDigest)}`,
529
760
  `Catalog 组合:${short(evidence.catalog.compositeDigest)}`,
530
761
  ];
762
+ if ('groups' in evidence && Array.isArray(evidence.groups)) {
763
+ const batch = evidence;
764
+ details.push(`批量根插件:${batch.requestedPluginIds.length} 个`);
765
+ for (const group of batch.groups) {
766
+ details.push(`批量分组:${group.sourceId} · ${group.platform} · ${group.requestedPluginIds.length} 个根插件 · ${group.executable ? `计划 ${short(group.preview.operationPlan.planFingerprint)}` : '预览失败'}`);
767
+ }
768
+ }
531
769
  if (evidence.catalog.generationSetDigest) {
532
770
  details.push(`Catalog generation:${short(evidence.catalog.generationSetDigest)}`);
533
771
  }
@@ -576,14 +814,32 @@ function technicalDetails(evidence) {
576
814
  }
577
815
  export function buildOperationReviewProjection(options) {
578
816
  const { intent, evidence } = options;
817
+ const batchEvidence = intent.kind === 'pluginWriteBatch'
818
+ ? evidence
819
+ : undefined;
579
820
  const labels = operationLabels(intent, evidence);
580
- const requiresConfirmation = (evidence.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation)
821
+ const hasPreApply = intent.kind === 'pluginWriteBatch'
822
+ ? Boolean(batchEvidence?.groups.some((group) => operationPreApplyTargets(batchGroupEvidence(batchEvidence, group)).length > 0))
823
+ : operationPreApplyTargets(evidence).length > 0
824
+ || (evidence.config?.preApplyAuthorization?.length ?? 0) > 0;
825
+ const sharedNativeConfigApply = intent.kind === 'configSave'
826
+ && intent.apply
827
+ && (evidence.config?.sharedNativeRenderTargets?.length ?? 0) > 0
828
+ && (evidence.config?.affectedTargets.some((target) => target.platform === 'codex') ?? false);
829
+ const requiresConfirmation = sharedNativeConfigApply
830
+ || Boolean(batchEvidence?.groups.some((group) => (group.preview.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation)))
831
+ || (evidence.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation)
581
832
  || Boolean(evidence.sourceWrite
582
833
  && (!evidence.sourceWrite.inventoryComplete || evidence.sourceWrite.referenceCount > 0))
583
834
  || Boolean(evidence.catalogGeneration?.impact
584
835
  && (!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;
836
+ || evidence.catalogGeneration.impact.affectedReferences.length > 0))
837
+ || Boolean(evidence.catalogGeneration?.primaryImpact
838
+ && (!evidence.catalogGeneration.primaryImpact.inventoryComplete
839
+ || evidence.catalogGeneration.primaryImpact.affectedReferences.length > 0));
840
+ const warning = (evidence.codexReconcilePlans ?? []).find((plan) => plan.review.sharedImpact.warning)?.review.sharedImpact.warning
841
+ ?? batchEvidence?.groups.flatMap((group) => group.preview.codexReconcilePlans ?? [])
842
+ .find((plan) => plan.review.sharedImpact.warning)?.review.sharedImpact.warning;
587
843
  const preparationWarnings = (evidence.preparationWarnings ?? []).map(() => ({
588
844
  kind: 'warning',
589
845
  group: 'portableWarnings',
@@ -595,27 +851,38 @@ export function buildOperationReviewProjection(options) {
595
851
  description: labels.description,
596
852
  changes: [
597
853
  ...preparationWarnings,
598
- ...reviewChanges(intent, evidence, options.dependencyOverview),
854
+ ...(intent.kind === 'pluginWriteBatch'
855
+ ? batchReviewChanges(intent, batchEvidence)
856
+ : reviewChanges(intent, evidence, options.dependencyOverview)),
599
857
  ],
600
858
  technicalDetails: technicalDetails(evidence),
601
859
  ...(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' : ''}` }
860
+ ? { 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 || hasPreApply ? ' --yes' : ''}` }
603
861
  : {}),
604
- primaryLabel: intent.kind === 'pluginWrite' && evidence.dependencyPlan
605
- ? `${labels.primaryLabel} ${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`
606
- : labels.primaryLabel,
862
+ primaryLabel: intent.kind === 'pluginWriteBatch'
863
+ ? `${labels.primaryLabel} ${batchEvidence.requestedPluginIds.length} 个插件`
864
+ : intent.kind === 'pluginWrite' && evidence.dependencyPlan
865
+ ? `${labels.primaryLabel} ${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`
866
+ : labels.primaryLabel,
607
867
  ...(labels.destructive ? { destructive: true } : {}),
608
868
  requiresConfirmation,
609
869
  sharedImpact: {
610
870
  pluginIds: [...new Set([
611
871
  ...(evidence.codexReconcilePlans ?? []).flatMap((plan) => plan.review.sharedImpact.pluginIds),
872
+ ...(batchEvidence?.groups.flatMap((group) => (group.preview.codexReconcilePlans ?? []).flatMap((plan) => plan.review.sharedImpact.pluginIds)) ?? []),
612
873
  ...(evidence.sourceWrite?.impact?.affectedReferences ?? []).flatMap((reference) => reference.pluginId ? [reference.pluginId] : []),
613
874
  ...(evidence.catalogGeneration?.impact?.affectedReferences ?? []).flatMap((reference) => reference.pluginId ? [reference.pluginId] : []),
875
+ ...(evidence.catalogGeneration?.primaryImpact?.affectedReferences ?? []).flatMap((reference) => reference.pluginId ? [reference.pluginId] : []),
876
+ ...(sharedNativeConfigApply && 'pluginId' in intent ? [intent.pluginId] : []),
614
877
  ])],
615
878
  ...(warning ? { warning }
616
- : requiresConfirmation && (evidence.sourceWrite || evidence.catalogGeneration?.impact)
617
- ? { warning: '确认来源影响后才会继续。未知影响不会按零处理。' }
618
- : {}),
879
+ : sharedNativeConfigApply
880
+ ? { warning: '更新共享 Codex native 动态 Skill 会影响所有启用项目。' }
881
+ : requiresConfirmation && (evidence.sourceWrite
882
+ || evidence.catalogGeneration?.impact
883
+ || evidence.catalogGeneration?.primaryImpact)
884
+ ? { warning: '确认来源影响后才会继续。未知影响不会按零处理。' }
885
+ : {}),
619
886
  },
620
887
  };
621
888
  }