@lesliechan721/aw 0.1.0-beta.63 → 0.1.0-beta.65

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 (302) hide show
  1. package/dist/authoring/assets/aw-runtime-config.mjs +3 -3
  2. package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -1
  3. package/dist/authoring/scripts/add-skill.mjs +3 -17
  4. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  5. package/dist/authoring/scripts/init-plugin.mjs +1 -0
  6. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  7. package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
  8. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +2 -0
  9. package/dist/authoring/scripts/lib/skill-scaffold.mjs +2 -0
  10. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  11. package/dist/authoring/scripts/validate-plugin.mjs +95 -698
  12. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  13. package/dist/scripts/aw-skill-eval-materializer.d.ts +2 -0
  14. package/dist/scripts/aw-skill-eval-materializer.js +74 -44
  15. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -1
  16. package/dist/scripts/bundle-plugins.js +10 -2
  17. package/dist/scripts/bundle-plugins.js.map +1 -1
  18. package/dist/scripts/plugin-staging.d.ts +1 -1
  19. package/dist/scripts/plugin-staging.js +1 -1
  20. package/dist/scripts/plugin-staging.js.map +1 -1
  21. package/dist/scripts/test-plugin-scripts.js +31 -31
  22. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  23. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  24. package/dist/src/commands/aw-capabilities.js +2 -0
  25. package/dist/src/commands/aw-capabilities.js.map +1 -1
  26. package/dist/src/commands/console.js +4 -3
  27. package/dist/src/commands/console.js.map +1 -1
  28. package/dist/src/commands/intent-command.d.ts +30 -0
  29. package/dist/src/commands/intent-command.js +164 -0
  30. package/dist/src/commands/intent-command.js.map +1 -0
  31. package/dist/src/commands/operation-plan.d.ts +2 -0
  32. package/dist/src/commands/operation-plan.js +2 -1
  33. package/dist/src/commands/operation-plan.js.map +1 -1
  34. package/dist/src/commands/options.d.ts +3 -11
  35. package/dist/src/commands/options.js +11 -17
  36. package/dist/src/commands/options.js.map +1 -1
  37. package/dist/src/commands/plugin-capabilities.js +27 -33
  38. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  39. package/dist/src/commands/rebuild-command.d.ts +108 -6
  40. package/dist/src/commands/rebuild-command.js +617 -1549
  41. package/dist/src/commands/rebuild-command.js.map +1 -1
  42. package/dist/src/commands/rebuild-integration.d.ts +19 -3
  43. package/dist/src/commands/rebuild-integration.js +500 -459
  44. package/dist/src/commands/rebuild-integration.js.map +1 -1
  45. package/dist/src/commands/rebuild-operations.js +1 -2
  46. package/dist/src/commands/rebuild-operations.js.map +1 -1
  47. package/dist/src/commands/review-inspection.d.ts +2 -2
  48. package/dist/src/commands/review-inspection.js +30 -86
  49. package/dist/src/commands/review-inspection.js.map +1 -1
  50. package/dist/src/commands/review-preparation.js +16 -7
  51. package/dist/src/commands/review-preparation.js.map +1 -1
  52. package/dist/src/commands/skill-eval.d.ts +2 -0
  53. package/dist/src/commands/skill-eval.js +15 -9
  54. package/dist/src/commands/skill-eval.js.map +1 -1
  55. package/dist/src/commands/status-v6.d.ts +38 -5
  56. package/dist/src/commands/status-v6.js +269 -102
  57. package/dist/src/commands/status-v6.js.map +1 -1
  58. package/dist/src/commands/targets-command.d.ts +27 -0
  59. package/dist/src/commands/targets-command.js +77 -0
  60. package/dist/src/commands/targets-command.js.map +1 -0
  61. package/dist/src/commands/write-commands.js +5 -5
  62. package/dist/src/commands/write-commands.js.map +1 -1
  63. package/dist/src/eval/manifest-v1.d.ts +2 -0
  64. package/dist/src/eval/manifest-v1.js +7 -2
  65. package/dist/src/eval/manifest-v1.js.map +1 -1
  66. package/dist/src/generators/local-plugin.d.ts +2 -1
  67. package/dist/src/generators/local-plugin.js +111 -108
  68. package/dist/src/generators/local-plugin.js.map +1 -1
  69. package/dist/src/index.d.ts +10 -17
  70. package/dist/src/index.js +585 -1349
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/intent/deployment-preferences.d.ts +63 -0
  73. package/dist/src/intent/deployment-preferences.js +170 -0
  74. package/dist/src/intent/deployment-preferences.js.map +1 -0
  75. package/dist/src/intent/diagnostics.d.ts +11 -0
  76. package/dist/src/intent/diagnostics.js +14 -0
  77. package/dist/src/intent/diagnostics.js.map +1 -0
  78. package/dist/src/intent/intent-codec.d.ts +19 -0
  79. package/dist/src/intent/intent-codec.js +117 -0
  80. package/dist/src/intent/intent-codec.js.map +1 -0
  81. package/dist/src/intent/intent-store.d.ts +43 -0
  82. package/dist/src/intent/intent-store.js +118 -0
  83. package/dist/src/intent/intent-store.js.map +1 -0
  84. package/dist/src/intent/scope-context.d.ts +33 -0
  85. package/dist/src/intent/scope-context.js +145 -0
  86. package/dist/src/intent/scope-context.js.map +1 -0
  87. package/dist/src/intent/work-context.d.ts +13 -0
  88. package/dist/src/intent/work-context.js +22 -0
  89. package/dist/src/intent/work-context.js.map +1 -0
  90. package/dist/src/output/command-receipt.js +16 -9
  91. package/dist/src/output/command-receipt.js.map +1 -1
  92. package/dist/src/portable/commands.d.ts +18 -8
  93. package/dist/src/portable/commands.js +46 -562
  94. package/dist/src/portable/commands.js.map +1 -1
  95. package/dist/src/portable/historical-materialization.d.ts +22 -0
  96. package/dist/src/portable/historical-materialization.js +124 -0
  97. package/dist/src/portable/historical-materialization.js.map +1 -0
  98. package/dist/src/portable/materializer.d.ts +8 -0
  99. package/dist/src/portable/materializer.js +75 -44
  100. package/dist/src/portable/materializer.js.map +1 -1
  101. package/dist/src/portable/recovery-layout.d.ts +17 -0
  102. package/dist/src/portable/recovery-layout.js +55 -0
  103. package/dist/src/portable/recovery-layout.js.map +1 -0
  104. package/dist/src/portable/recovery.d.ts +143 -0
  105. package/dist/src/portable/recovery.js +339 -0
  106. package/dist/src/portable/recovery.js.map +1 -0
  107. package/dist/src/portable/writer.d.ts +64 -0
  108. package/dist/src/portable/writer.js +316 -0
  109. package/dist/src/portable/writer.js.map +1 -0
  110. package/dist/src/recovery/coordinator.d.ts +137 -0
  111. package/dist/src/recovery/coordinator.js +1198 -0
  112. package/dist/src/recovery/coordinator.js.map +1 -0
  113. package/dist/src/recovery/demand.d.ts +44 -0
  114. package/dist/src/recovery/demand.js +178 -0
  115. package/dist/src/recovery/demand.js.map +1 -0
  116. package/dist/src/recovery/joint-coordinator.d.ts +24 -0
  117. package/dist/src/recovery/joint-coordinator.js +770 -0
  118. package/dist/src/recovery/joint-coordinator.js.map +1 -0
  119. package/dist/src/recovery/joint-demand.d.ts +68 -0
  120. package/dist/src/recovery/joint-demand.js +166 -0
  121. package/dist/src/recovery/joint-demand.js.map +1 -0
  122. package/dist/src/recovery/receipt.d.ts +81 -0
  123. package/dist/src/recovery/receipt.js +2 -0
  124. package/dist/src/recovery/receipt.js.map +1 -0
  125. package/dist/src/recovery/retirement.d.ts +45 -0
  126. package/dist/src/recovery/retirement.js +366 -0
  127. package/dist/src/recovery/retirement.js.map +1 -0
  128. package/dist/src/recovery/status.d.ts +108 -0
  129. package/dist/src/recovery/status.js +417 -0
  130. package/dist/src/recovery/status.js.map +1 -0
  131. package/dist/src/recovery/types.d.ts +86 -0
  132. package/dist/src/recovery/types.js +2 -0
  133. package/dist/src/recovery/types.js.map +1 -0
  134. package/dist/src/review/review-profile.d.ts +2 -1
  135. package/dist/src/review/review-profile.js +15 -15
  136. package/dist/src/review/review-profile.js.map +1 -1
  137. package/dist/src/review/review-selection.d.ts +1 -2
  138. package/dist/src/review/review-selection.js +40 -114
  139. package/dist/src/review/review-selection.js.map +1 -1
  140. package/dist/src/review/review-types.d.ts +4 -4
  141. package/dist/src/review/review-types.js.map +1 -1
  142. package/dist/src/review/skill-materialization.js +15 -6
  143. package/dist/src/review/skill-materialization.js.map +1 -1
  144. package/dist/src/review/source-binding.d.ts +14 -4
  145. package/dist/src/review/source-binding.js +62 -117
  146. package/dist/src/review/source-binding.js.map +1 -1
  147. package/dist/src/tui/agent-management.d.ts +23 -0
  148. package/dist/src/tui/agent-management.js +217 -0
  149. package/dist/src/tui/agent-management.js.map +1 -0
  150. package/dist/src/tui/agent-settings.d.ts +44 -0
  151. package/dist/src/tui/agent-settings.js +98 -0
  152. package/dist/src/tui/agent-settings.js.map +1 -0
  153. package/dist/src/tui/components.d.ts +3 -0
  154. package/dist/src/tui/components.js +10 -1
  155. package/dist/src/tui/components.js.map +1 -1
  156. package/dist/src/tui/config-references.d.ts +6 -3
  157. package/dist/src/tui/config-references.js +17 -12
  158. package/dist/src/tui/config-references.js.map +1 -1
  159. package/dist/src/tui/console-app-v4.d.ts +1 -2
  160. package/dist/src/tui/console-app-v4.js +1533 -1047
  161. package/dist/src/tui/console-app-v4.js.map +1 -1
  162. package/dist/src/tui/controller-runtime.d.ts +2 -1
  163. package/dist/src/tui/controller-runtime.js +457 -1634
  164. package/dist/src/tui/controller-runtime.js.map +1 -1
  165. package/dist/src/tui/controller.d.ts +131 -81
  166. package/dist/src/tui/controller.js +307 -227
  167. package/dist/src/tui/controller.js.map +1 -1
  168. package/dist/src/tui/entity-cards.d.ts +2 -2
  169. package/dist/src/tui/entity-cards.js +98 -20
  170. package/dist/src/tui/entity-cards.js.map +1 -1
  171. package/dist/src/tui/glossary.d.ts +2 -0
  172. package/dist/src/tui/glossary.js +20 -0
  173. package/dist/src/tui/glossary.js.map +1 -1
  174. package/dist/src/tui/model.d.ts +28 -10
  175. package/dist/src/tui/model.js +41 -41
  176. package/dist/src/tui/model.js.map +1 -1
  177. package/dist/src/tui/operation-review-planner.d.ts +2 -0
  178. package/dist/src/tui/operation-review-planner.js +58 -122
  179. package/dist/src/tui/operation-review-planner.js.map +1 -1
  180. package/dist/src/tui/plugin-content.js +7 -4
  181. package/dist/src/tui/plugin-content.js.map +1 -1
  182. package/dist/src/tui/plugin-detail.d.ts +22 -3
  183. package/dist/src/tui/plugin-detail.js +113 -26
  184. package/dist/src/tui/plugin-detail.js.map +1 -1
  185. package/dist/src/tui/reader.d.ts +1 -0
  186. package/dist/src/tui/reader.js +3 -1
  187. package/dist/src/tui/reader.js.map +1 -1
  188. package/dist/src/tui/router.d.ts +23 -33
  189. package/dist/src/tui/router.js.map +1 -1
  190. package/dist/src/tui/scoped-flow.d.ts +45 -0
  191. package/dist/src/tui/scoped-flow.js +96 -0
  192. package/dist/src/tui/scoped-flow.js.map +1 -0
  193. package/dist/src/tui/scoped-inventory.d.ts +122 -0
  194. package/dist/src/tui/scoped-inventory.js +225 -0
  195. package/dist/src/tui/scoped-inventory.js.map +1 -0
  196. package/dist/src/tui/screen-registry.d.ts +25 -25
  197. package/dist/src/tui/screen-registry.js +3 -7
  198. package/dist/src/tui/screen-registry.js.map +1 -1
  199. package/dist/src/tui/session-console-app.js +1 -1
  200. package/dist/src/tui/session-console-app.js.map +1 -1
  201. package/dist/src/tui/session-host.js +12 -2
  202. package/dist/src/tui/session-host.js.map +1 -1
  203. package/dist/src/types.d.ts +15 -12
  204. package/dist/src/types.js.map +1 -1
  205. package/dist/src/utils/agent-version.js +2 -0
  206. package/dist/src/utils/agent-version.js.map +1 -1
  207. package/dist/src/utils/artifact-plan.js +13 -6
  208. package/dist/src/utils/artifact-plan.js.map +1 -1
  209. package/dist/src/utils/artifact-renderer.d.ts +6 -3
  210. package/dist/src/utils/artifact-renderer.js +27 -32
  211. package/dist/src/utils/artifact-renderer.js.map +1 -1
  212. package/dist/src/utils/artifact-request.d.ts +1 -0
  213. package/dist/src/utils/artifact-request.js +22 -12
  214. package/dist/src/utils/artifact-request.js.map +1 -1
  215. package/dist/src/utils/artifact-sources.d.ts +2 -2
  216. package/dist/src/utils/bundle-layout.js +18 -16
  217. package/dist/src/utils/bundle-layout.js.map +1 -1
  218. package/dist/src/utils/catalog-digests.js +2 -0
  219. package/dist/src/utils/catalog-digests.js.map +1 -1
  220. package/dist/src/utils/catalog-runtime-projection.d.ts +4 -3
  221. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  222. package/dist/src/utils/catalog-source-references.d.ts +5 -0
  223. package/dist/src/utils/catalog-source-references.js +42 -57
  224. package/dist/src/utils/catalog-source-references.js.map +1 -1
  225. package/dist/src/utils/claude-native-render-receipt.d.ts +14 -0
  226. package/dist/src/utils/claude-native-render-receipt.js +26 -0
  227. package/dist/src/utils/claude-native-render-receipt.js.map +1 -0
  228. package/dist/src/utils/claude-skills-root.js +14 -3
  229. package/dist/src/utils/claude-skills-root.js.map +1 -1
  230. package/dist/src/utils/codex-manager.js +6 -12
  231. package/dist/src/utils/codex-manager.js.map +1 -1
  232. package/dist/src/utils/codex-native-observation.js +29 -13
  233. package/dist/src/utils/codex-native-observation.js.map +1 -1
  234. package/dist/src/utils/codex-reconciler.d.ts +2 -5
  235. package/dist/src/utils/codex-reconciler.js +25 -1
  236. package/dist/src/utils/codex-reconciler.js.map +1 -1
  237. package/dist/src/utils/codex-surface-analysis.js +5 -3
  238. package/dist/src/utils/codex-surface-analysis.js.map +1 -1
  239. package/dist/src/utils/local-marketplace.d.ts +14 -2
  240. package/dist/src/utils/local-marketplace.js +209 -42
  241. package/dist/src/utils/local-marketplace.js.map +1 -1
  242. package/dist/src/utils/managed-command-evidence.js +1 -1
  243. package/dist/src/utils/managed-command-evidence.js.map +1 -1
  244. package/dist/src/utils/managed-command-materialization.d.ts +4 -0
  245. package/dist/src/utils/managed-command-materialization.js +15 -6
  246. package/dist/src/utils/managed-command-materialization.js.map +1 -1
  247. package/dist/src/utils/managed-command-store.d.ts +15 -0
  248. package/dist/src/utils/managed-command-store.js +73 -8
  249. package/dist/src/utils/managed-command-store.js.map +1 -1
  250. package/dist/src/utils/native-cli.d.ts +2 -0
  251. package/dist/src/utils/native-cli.js +22 -0
  252. package/dist/src/utils/native-cli.js.map +1 -1
  253. package/dist/src/utils/native-demand.d.ts +10 -0
  254. package/dist/src/utils/native-demand.js +4 -0
  255. package/dist/src/utils/native-demand.js.map +1 -0
  256. package/dist/src/utils/ownership.js +2 -0
  257. package/dist/src/utils/ownership.js.map +1 -1
  258. package/dist/src/utils/paths.js +1 -1
  259. package/dist/src/utils/paths.js.map +1 -1
  260. package/dist/src/utils/platform-support.d.ts +1 -1
  261. package/dist/src/utils/platform-support.js +1 -1
  262. package/dist/src/utils/platform-support.js.map +1 -1
  263. package/dist/src/utils/plugin-config-impact.d.ts +3 -0
  264. package/dist/src/utils/plugin-config-impact.js +115 -273
  265. package/dist/src/utils/plugin-config-impact.js.map +1 -1
  266. package/dist/src/utils/plugin-config-references.d.ts +11 -2
  267. package/dist/src/utils/plugin-config-references.js +85 -90
  268. package/dist/src/utils/plugin-config-references.js.map +1 -1
  269. package/dist/src/utils/plugin-config-write.d.ts +58 -1
  270. package/dist/src/utils/plugin-config-write.js +756 -809
  271. package/dist/src/utils/plugin-config-write.js.map +1 -1
  272. package/dist/src/utils/plugin-config.d.ts +47 -46
  273. package/dist/src/utils/plugin-config.js +292 -833
  274. package/dist/src/utils/plugin-config.js.map +1 -1
  275. package/dist/src/utils/plugin-scripts.d.ts +11 -6
  276. package/dist/src/utils/plugin-scripts.js +52 -35
  277. package/dist/src/utils/plugin-scripts.js.map +1 -1
  278. package/dist/src/utils/runtime-config.js +5 -4
  279. package/dist/src/utils/runtime-config.js.map +1 -1
  280. package/dist/src/utils/runtime-skill-closure.d.ts +2 -2
  281. package/dist/src/utils/runtime-skill-closure.js.map +1 -1
  282. package/dist/src/utils/state-paths.d.ts +8 -3
  283. package/dist/src/utils/state-paths.js +53 -16
  284. package/dist/src/utils/state-paths.js.map +1 -1
  285. package/dist/src/utils/windows-npm-shim.d.ts +2 -0
  286. package/dist/src/utils/windows-npm-shim.js +5 -7
  287. package/dist/src/utils/windows-npm-shim.js.map +1 -1
  288. package/dist/vitest.config.js +1 -0
  289. package/dist/vitest.config.js.map +1 -1
  290. package/package.json +3 -3
  291. package/dist/src/commands/sync-command.d.ts +0 -40
  292. package/dist/src/commands/sync-command.js +0 -254
  293. package/dist/src/commands/sync-command.js.map +0 -1
  294. package/dist/src/portable/skills-cli.d.ts +0 -51
  295. package/dist/src/portable/skills-cli.js +0 -202
  296. package/dist/src/portable/skills-cli.js.map +0 -1
  297. package/dist/src/tui/project-inventory.d.ts +0 -68
  298. package/dist/src/tui/project-inventory.js +0 -203
  299. package/dist/src/tui/project-inventory.js.map +0 -1
  300. package/dist/src/utils/plugin-intent-state.d.ts +0 -49
  301. package/dist/src/utils/plugin-intent-state.js +0 -320
  302. package/dist/src/utils/plugin-intent-state.js.map +0 -1
@@ -1,7 +1,9 @@
1
+ import { isPlatformTarget } from '@lesliechan721/aw-plugin-core/manifest';
1
2
  import crypto from 'node:crypto';
2
3
  import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
3
4
  import { readSafeOperationError } from '../utils/safe-operation-diagnostic.js';
4
- import { flattenStatusTargets, statusTargetActivation, statusTargetExtensions, statusTargetIntent, statusTargetOwnershipState, statusTargetSharedNative, } from '../commands/status-v6.js';
5
+ import { NativeCliError } from '../utils/native-cli.js';
6
+ import { flattenStatusTargets, statusTargetActivation, statusTargetIntent, statusTargetOwnershipState, statusTargetSharedNative, } from '../commands/status-v6.js';
5
7
  import { buildRebuildReceiptJson, requestedIntentAction } from '../output/command-receipt.js';
6
8
  import { buildOperationResultModel } from '../commands/operation-planner.js';
7
9
  import { buildOperationReviewProjection } from './operation-review-planner.js';
@@ -11,7 +13,8 @@ import { safeDiagnosticCode, sanitizeDiagnosticText } from './diagnostics.js';
11
13
  import { projectExecutionDiagnostics, projectThrownOperationDiagnostic } from './diagnostic-projection.js';
12
14
  import { OperationHistoryStore, } from './operation-history.js';
13
15
  import { targetHasArtifactCollision } from '../utils/target-eligibility.js';
14
- import { buildProjectInventory, hasStatusInstallationEvidence, } from './project-inventory.js';
16
+ import { hasStatusInstallationEvidence } from './scoped-inventory.js';
17
+ import { buildScopedInventory } from './scoped-inventory.js';
15
18
  import { TuiRouter, } from './router.js';
16
19
  import { normalizeTuiRoute } from './screen-registry.js';
17
20
  import { CatalogSourcesSession } from './catalog-sources/session.js';
@@ -91,89 +94,6 @@ function reviewPreparationStageLabel(stage) {
91
94
  evidence: '准备确认信息',
92
95
  }[stage];
93
96
  }
94
- function projectReminderTargetKey(target) {
95
- return [target.pluginId, target.platform, target.scope].join('\0');
96
- }
97
- export function projectCurrentIntentReminder(status) {
98
- if (status.projectIntent && status.projectIntent.state !== 'ready')
99
- return undefined;
100
- const actionableTargets = [];
101
- const blockedTargets = [];
102
- const unconfirmedTargets = [];
103
- const projectRoots = new Map((status.projectIntent?.state === 'ready'
104
- ? status.projectIntent.roots : status.projectIntentRoots ?? []).map((root) => [
105
- root.pluginId,
106
- new Set(root.platforms),
107
- ]));
108
- const projectTargets = flattenStatusTargets(status.payload.targets).filter((target) => {
109
- const intent = statusTargetIntent(target);
110
- return target.scope === 'project'
111
- && projectRoots.get(target.pluginId)?.has(target.platform) === true
112
- && intent.scopes.includes('project');
113
- });
114
- for (const target of projectTargets) {
115
- if (target.state === 'ready')
116
- continue;
117
- const reminderTarget = {
118
- pluginId: target.pluginId,
119
- platform: target.platform,
120
- scope: 'project',
121
- };
122
- const statusReasons = [
123
- ...target.reasons,
124
- ...(target.sharedReasons ?? []),
125
- ];
126
- const reasonCodes = [...new Set(statusReasons.flatMap((reason) => [
127
- reason.code,
128
- ...(reason.rootIssueCode ? [reason.rootIssueCode] : []),
129
- ]))].sort((left, right) => left.localeCompare(right, 'en'));
130
- const sharedNative = statusTargetSharedNative(target);
131
- const activation = statusTargetActivation(target);
132
- const extensions = statusTargetExtensions(target);
133
- const ownership = statusTargetOwnershipState(target);
134
- const blocking = target.state === 'blocked'
135
- || [...target.reasons, ...(target.sharedReasons ?? [])]
136
- .some((reason) => reason.severity === 'blocking');
137
- const unobserved = ownership === 'error'
138
- || (!blocking && (sharedNative?.state === 'unobserved'
139
- || activation?.evidence === 'unobserved'
140
- || reasonCodes.includes('codexSharedNativeUnobserved')
141
- || target.components.some((component) => component.metadata?.rootIssueCode === 'codexSharedNativeUnobserved')));
142
- const confirmedDifference = ownership === 'absent' || ownership === 'partial'
143
- || sharedNative?.state === 'missing' || sharedNative?.state === 'replaceable'
144
- || extensions?.state === 'missing' || extensions?.state === 'drifted'
145
- || activation?.evidence === 'observed' && activation.project === false
146
- || target.components.some((component) => component.kind === 'dependency'
147
- && (component.state === 'absent' || component.state === 'incomplete'));
148
- if (blocking && confirmedDifference) {
149
- blockedTargets.push({ ...reminderTarget, reasonCodes });
150
- }
151
- else if (!unobserved && target.state === 'needsSync' && confirmedDifference) {
152
- actionableTargets.push({ ...reminderTarget, reasonCodes });
153
- }
154
- else {
155
- unconfirmedTargets.push({ ...reminderTarget, reasonCodes });
156
- }
157
- }
158
- if (projectTargets.length === 0)
159
- return undefined;
160
- const canonicalActionable = actionableTargets.sort((left, right) => projectReminderTargetKey(left).localeCompare(projectReminderTargetKey(right), 'en'));
161
- const canonicalBlocked = blockedTargets.sort((left, right) => projectReminderTargetKey(left).localeCompare(projectReminderTargetKey(right), 'en'));
162
- const canonicalUnconfirmed = unconfirmedTargets.sort((left, right) => projectReminderTargetKey(left).localeCompare(projectReminderTargetKey(right), 'en'));
163
- const projectRoot = status.projectRoot ?? projectTargets[0].cwd;
164
- const signature = crypto.createHash('sha256').update(JSON.stringify({
165
- projectRoot,
166
- actionableTargets: canonicalActionable.map(projectReminderTargetKey),
167
- blockedTargets: canonicalBlocked.map(projectReminderTargetKey),
168
- })).digest('hex');
169
- return {
170
- projectRoot,
171
- signature: `sha256:${signature}`,
172
- actionableTargets: canonicalActionable,
173
- blockedTargets: canonicalBlocked,
174
- unconfirmedTargets: canonicalUnconfirmed,
175
- };
176
- }
177
97
  export class TuiOperationBlockedError extends Error {
178
98
  code;
179
99
  diagnostics;
@@ -220,16 +140,17 @@ function canonicalPluginWriteTaskTargets(targets) {
220
140
  .localeCompare(pluginWriteTaskTargetKey(right), 'en'));
221
141
  }
222
142
  function normalizeOperationIntent(intent) {
223
- if (intent.kind === 'configSave' && intent.retryApply && !intent.apply) {
224
- return { ...intent, apply: true };
143
+ if (intent.kind === 'configSave' || intent.kind === 'configReset' || intent.kind === 'configSaveBatch') {
144
+ return { ...intent, context: intent.context ?? 'user',
145
+ ...(intent.kind === 'configSave' ? { scope: 'user',
146
+ apply: intent.retryApply ? true : intent.apply } : {}) };
225
147
  }
226
148
  if (intent.kind === 'pluginWriteTask') {
227
149
  return { ...intent, targets: canonicalPluginWriteTaskTargets(intent.targets) };
228
150
  }
229
151
  if (intent.kind !== 'pluginWriteBatch')
230
152
  return intent;
231
- if (intent.selection.kind === 'targets'
232
- || intent.selection.kind === 'currentProjectIntentTargets') {
153
+ if (intent.selection.kind === 'targets') {
233
154
  return {
234
155
  ...intent,
235
156
  selection: {
@@ -713,6 +634,7 @@ export class TuiController {
713
634
  #reviewPreparationIntents = new Map();
714
635
  #inflightOperations = new Map();
715
636
  #pendingReads = new Set();
637
+ #readCleanupError;
716
638
  #pluginContent = new Map();
717
639
  #pluginContentInflight = new Map();
718
640
  #pluginContentEpoch = 0;
@@ -740,10 +662,14 @@ export class TuiController {
740
662
  #portableResultLifecycle = 0;
741
663
  #portableResultRecheck;
742
664
  #completionNotice;
743
- #projectIntentReminder;
744
- #projectInventory;
745
- #projectInventoryRefresh = { state: 'idle' };
746
- #projectInventoryRefreshToken = 0;
665
+ #scopedInventories = {};
666
+ #lastRefreshedScope;
667
+ #scopedInventoryRefresh = {};
668
+ #scopedRefreshToken = { user: 0, project: 0 };
669
+ #scopedPreparationAbort;
670
+ #scopedExecuted = new WeakSet();
671
+ #scopedTaskInflight;
672
+ #configSaveInflight;
747
673
  #loadError;
748
674
  #portableDraft;
749
675
  #recoverablePortableDraft;
@@ -860,16 +786,9 @@ export class TuiController {
860
786
  } }
861
787
  : {}),
862
788
  ...(this.#completionNotice ? { completionNotice: this.#completionNotice } : {}),
863
- ...(this.#projectIntentReminder
864
- ? { projectIntentReminder: this.#projectIntentReminder }
865
- : {}),
866
- ...(this.#projectInventory ? { projectInventory: this.#projectInventory } : {}),
867
- projectInventoryRefresh: this.#projectInventoryRefresh,
868
- ...(this.#status?.projectIntent?.state === 'absent' ? { projectDeclarationAbsent: true } : {}),
869
- ...(this.#status?.projectIntent?.state === 'invalid'
870
- || this.#status?.projectIntent?.state === 'unavailable'
871
- ? { projectIntentIssue: this.#status.projectIntent }
872
- : {}),
789
+ ...(this.#status?.projectRoot ? { projectRoot: this.#status.projectRoot } : {}),
790
+ scopedInventories: this.#scopedInventories,
791
+ scopedInventoryRefresh: this.#scopedInventoryRefresh,
873
792
  });
874
793
  }
875
794
  subscribe(listener) {
@@ -878,7 +797,8 @@ export class TuiController {
878
797
  return () => this.#listeners.delete(listener);
879
798
  }
880
799
  get hasActiveWrite() {
881
- return this.#inflightOperations.size > 0;
800
+ return this.#inflightOperations.size > 0 || this.#scopedTaskInflight !== undefined
801
+ || this.#configSaveInflight !== undefined;
882
802
  }
883
803
  get retiring() {
884
804
  return this.#retiring;
@@ -946,9 +866,16 @@ export class TuiController {
946
866
  return recordId;
947
867
  }
948
868
  #trackRead(promise) {
949
- this.#pendingReads.add(promise);
950
- void promise.finally(() => this.#pendingReads.delete(promise)).catch(() => undefined);
951
- return promise;
869
+ const tracked = promise.catch(async (error) => {
870
+ if (error instanceof NativeCliError && error.cleanupError) {
871
+ this.#readCleanupError ??= error;
872
+ await this.#logErrorSafe(error, issueCode(error));
873
+ }
874
+ throw error;
875
+ });
876
+ this.#pendingReads.add(tracked);
877
+ void tracked.finally(() => this.#pendingReads.delete(tracked)).catch(() => undefined);
878
+ return tracked;
952
879
  }
953
880
  beginRetirement() {
954
881
  this.#retiring = true;
@@ -956,8 +883,12 @@ export class TuiController {
956
883
  }
957
884
  async waitForWrites() {
958
885
  this.beginRetirement();
959
- while (this.#inflightOperations.size > 0) {
960
- await Promise.allSettled([...this.#inflightOperations.values()].map((operation) => operation.promise));
886
+ while (this.hasActiveWrite) {
887
+ await Promise.allSettled([
888
+ ...[...this.#inflightOperations.values()].map((operation) => operation.promise),
889
+ ...(this.#scopedTaskInflight ? [this.#scopedTaskInflight.promise] : []),
890
+ ...(this.#configSaveInflight ? [this.#configSaveInflight] : []),
891
+ ]);
961
892
  }
962
893
  }
963
894
  async cancelAndWaitForReads() {
@@ -974,6 +905,8 @@ export class TuiController {
974
905
  await Promise.allSettled([...this.#pendingReads]);
975
906
  }
976
907
  await catalogDrain;
908
+ if (this.#readCleanupError)
909
+ throw this.#readCleanupError;
977
910
  return !this.hasActiveWrite;
978
911
  }
979
912
  #assertAcceptingActions() {
@@ -1090,7 +1023,9 @@ export class TuiController {
1090
1023
  stage,
1091
1024
  fallbackCode: code,
1092
1025
  sensitiveValues,
1093
- ...(intent && 'pluginId' in intent && 'platform' in intent && intent.platform !== 'both'
1026
+ ...(intent && 'pluginId' in intent && 'platform' in intent
1027
+ && (intent.platform === 'codex' || intent.platform === 'claude')
1028
+ && (intent.scope === 'user' || intent.scope === 'project')
1094
1029
  ? { target: { pluginId: intent.pluginId, platform: intent.platform, scope: intent.scope } }
1095
1030
  : {}),
1096
1031
  })],
@@ -1195,7 +1130,6 @@ export class TuiController {
1195
1130
  #project() {
1196
1131
  if (!this.#status)
1197
1132
  return;
1198
- this.#projectIntentReminder = projectCurrentIntentReminder(this.#status);
1199
1133
  this.#viewModel = buildTuiViewModel({
1200
1134
  status: this.#status.payload,
1201
1135
  pluginSummaries: this.#status.pluginSummaries,
@@ -1212,15 +1146,26 @@ export class TuiController {
1212
1146
  portablePluginIds: this.#status.portablePluginIds,
1213
1147
  configStoreInventory: this.#status.configStoreInventory,
1214
1148
  });
1215
- this.#projectInventory = buildProjectInventory({
1216
- projectRoot: this.#status.projectRoot,
1217
- projectIntent: this.#status.projectIntent,
1218
- status: this.#status.payload,
1219
- pluginSummaries: this.#viewModel.plugins,
1220
- targetRevisions: this.#status.targetRevisions,
1221
- dependencyOverviews: this.#status.dependencyOverviews,
1222
- actionableTargets: this.#projectIntentReminder?.actionableTargets,
1223
- });
1149
+ for (const issue of this.#viewModel.issues) {
1150
+ // Cross-scope recovery needs separate Intent, cleanup, and sync reviews.
1151
+ issue.actions = issue.actions.filter((action) => action.id !== 'keepProjectScope' && action.id !== 'keepUserScope');
1152
+ }
1153
+ for (const scope of ['user', 'project']) {
1154
+ const recovery = this.#status.scopedRecovery?.[scope];
1155
+ if (!recovery) {
1156
+ delete this.#scopedInventories[scope];
1157
+ continue;
1158
+ }
1159
+ this.#scopedInventories[scope] = buildScopedInventory({
1160
+ recovery,
1161
+ status: recovery.nativeSnapshot?.payload ?? this.#status.payload,
1162
+ pluginSummaries: recovery.nativeSnapshot?.pluginSummaries ?? this.#viewModel.plugins,
1163
+ targetRevisions: recovery.nativeSnapshot?.targetRevisions ?? this.#status.targetRevisions,
1164
+ dependencyOverviews: recovery.nativeSnapshot?.dependencyOverviews ?? this.#status.dependencyOverviews,
1165
+ ...(scope === 'project' && this.#status.projectRoot
1166
+ ? { projectRoot: this.#status.projectRoot } : {}),
1167
+ });
1168
+ }
1224
1169
  }
1225
1170
  #normalizeRoute(route) {
1226
1171
  const state = this.#routeEntityState();
@@ -1237,8 +1182,7 @@ export class TuiController {
1237
1182
  #routeEntityState() {
1238
1183
  return {
1239
1184
  hasPlugin: (pluginId) => this.#viewModel?.plugins.some((plugin) => plugin.pluginId === pluginId)
1240
- || this.#projectInventory?.declaredRoots.some((root) => root.pluginId === pluginId)
1241
- || this.#projectInventory?.extraInstalledRoots.some((root) => root.pluginId === pluginId)
1185
+ || Object.values(this.#scopedInventories).some((inventory) => inventory.declaredRoots.some((root) => root.pluginId === pluginId) || inventory.extraInstalledRoots.some((root) => root.pluginId === pluginId))
1242
1186
  || false,
1243
1187
  hasSource: (sourceId) => this.#viewModel?.sourceTabs.some((source) => source.id === sourceId) ?? false,
1244
1188
  hasIssue: (issueId) => this.#viewModel?.issues.some((issue) => issue.issueId === issueId) ?? false,
@@ -1430,9 +1374,7 @@ export class TuiController {
1430
1374
  ? {
1431
1375
  ...structuredClone(intent),
1432
1376
  selection: {
1433
- kind: intent.selection.kind === 'currentProjectIntentRoots'
1434
- || intent.selection.kind === 'currentProjectIntentTargets'
1435
- ? 'currentProjectIntentTargets' : 'targets',
1377
+ kind: 'targets',
1436
1378
  targets: structuredClone(result.summary.batch.retryTargets),
1437
1379
  },
1438
1380
  }
@@ -1459,7 +1401,10 @@ export class TuiController {
1459
1401
  ? result.returnRoute.batchOrigin.flowId
1460
1402
  : undefined;
1461
1403
  const completedPluginId = completedSinglePluginId(intent, result);
1462
- if (completedPluginId && target.name === 'pluginDetail') {
1404
+ const hasUnreviewedTaskTargets = intent.kind === 'pluginWriteTask'
1405
+ && 'returnRoute' in result && result.returnRoute?.name === 'pluginWriteTask'
1406
+ && result.returnRoute.targetKeys?.some((key) => !intent.targets.some((candidate) => pluginWriteTaskTargetKey(candidate) === key));
1407
+ if (completedPluginId && target.name === 'pluginDetail' && !hasUnreviewedTaskTargets) {
1463
1408
  this.#recordTerminalResult();
1464
1409
  const action = intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
1465
1410
  ? intent.action : 'sync';
@@ -1494,6 +1439,7 @@ export class TuiController {
1494
1439
  this.#router.replace(terminal);
1495
1440
  }
1496
1441
  #applyStatus(status) {
1442
+ this.#lastRefreshedScope = undefined;
1497
1443
  this.#pluginContentEpoch += 1;
1498
1444
  this.#pluginContent.clear();
1499
1445
  this.#pluginContentInflight.clear();
@@ -1563,8 +1509,10 @@ export class TuiController {
1563
1509
  this.#assertAcceptingActions();
1564
1510
  this.dependencies.beginCatalogSession?.();
1565
1511
  const generation = ++this.#generation;
1566
- this.#projectInventoryRefreshToken += 1;
1567
- this.#projectInventoryRefresh = { state: 'idle' };
1512
+ this.#scopedRefreshToken.user += 1;
1513
+ this.#scopedRefreshToken.project += 1;
1514
+ this.#scopedInventoryRefresh = {};
1515
+ this.#scopedInventories = {};
1568
1516
  this.#pluginContentEpoch += 1;
1569
1517
  this.#pluginContent.clear();
1570
1518
  this.#pluginContentInflight.clear();
@@ -1592,49 +1540,106 @@ export class TuiController {
1592
1540
  this.#loadError = undefined;
1593
1541
  this.#publish();
1594
1542
  }
1595
- listProjectInventoryRoots(section = 'declared') {
1543
+ listScopedInventoryRoots(scope, section = 'declared') {
1544
+ const inventory = this.#scopedInventories[scope];
1596
1545
  return cloneFrozen(section === 'declared'
1597
- ? this.#projectInventory?.declaredRoots ?? []
1598
- : this.#projectInventory?.extraInstalledRoots ?? []);
1599
- }
1600
- getProjectInventoryRoot(pluginId) {
1601
- const root = [...(this.#projectInventory?.declaredRoots ?? []),
1602
- ...(this.#projectInventory?.extraInstalledRoots ?? [])]
1603
- .find((entry) => entry.pluginId === pluginId);
1604
- return root ? cloneFrozen(root) : undefined;
1546
+ ? inventory?.declaredRoots ?? [] : inventory?.extraInstalledRoots ?? []);
1605
1547
  }
1606
- async refreshProjectInventory() {
1548
+ async refreshScopedInventory(scope) {
1607
1549
  this.#assertAcceptingActions();
1608
- if (!this.#status || this.#phase !== 'ready')
1550
+ if (!this.#status || this.#phase !== 'ready' || !this.dependencies.loadScopedStatus)
1609
1551
  return false;
1610
- const token = ++this.#projectInventoryRefreshToken;
1552
+ const token = ++this.#scopedRefreshToken[scope];
1611
1553
  const generation = this.#generation;
1612
- this.#projectInventoryRefresh = { state: 'checking' };
1554
+ this.#scopedInventoryRefresh[scope] = { state: 'checking' };
1613
1555
  this.#publish();
1614
1556
  try {
1615
- const status = await this.#trackRead(this.dependencies.loadStatus(this.#abort.signal));
1616
- if (this.#disposed || generation !== this.#generation
1617
- || token !== this.#projectInventoryRefreshToken)
1557
+ const recovery = await this.#trackRead(this.dependencies.loadScopedStatus(scope, this.#abort.signal));
1558
+ if (this.#disposed || generation !== this.#generation || token !== this.#scopedRefreshToken[scope])
1618
1559
  return false;
1619
- this.#applyStatus(status);
1620
- this.#projectInventoryRefresh = {
1621
- state: 'idle',
1622
- checkedAtMs: this.#statusReadAtMs,
1623
- };
1560
+ const inventory = buildScopedInventory({
1561
+ recovery, status: recovery.nativeSnapshot.payload, pluginSummaries: recovery.nativeSnapshot.pluginSummaries,
1562
+ targetRevisions: recovery.nativeSnapshot.targetRevisions,
1563
+ dependencyOverviews: recovery.nativeSnapshot.dependencyOverviews,
1564
+ ...(scope === 'project' && this.#status.projectRoot ? { projectRoot: this.#status.projectRoot } : {}),
1565
+ });
1566
+ this.#status.scopedRecovery = { ...this.#status.scopedRecovery, [scope]: structuredClone(recovery) };
1567
+ this.#scopedInventories[scope] = inventory;
1568
+ this.#lastRefreshedScope = scope;
1569
+ this.#scopedInventoryRefresh[scope] = { state: 'idle' };
1624
1570
  this.#publish();
1625
1571
  return true;
1626
1572
  }
1627
1573
  catch (error) {
1628
- if (this.#disposed || generation !== this.#generation
1629
- || token !== this.#projectInventoryRefreshToken)
1574
+ if (this.#disposed || generation !== this.#generation || token !== this.#scopedRefreshToken[scope])
1630
1575
  return false;
1631
1576
  const code = issueCode(error);
1632
1577
  await this.#logErrorSafe(error, code);
1633
- this.#projectInventoryRefresh = { state: 'error', errorCode: code };
1578
+ if (this.#disposed || generation !== this.#generation || token !== this.#scopedRefreshToken[scope])
1579
+ return false;
1580
+ this.#scopedInventoryRefresh[scope] = { state: 'error', errorCode: code };
1634
1581
  this.#publish();
1635
1582
  return false;
1636
1583
  }
1637
1584
  }
1585
+ async prepareScopedTask(request) {
1586
+ this.#assertAcceptingActions();
1587
+ if (!this.dependencies.prepareScopedTask)
1588
+ throw new Error('recoveryUnavailable');
1589
+ this.#scopedPreparationAbort?.abort();
1590
+ const abort = new AbortController();
1591
+ this.#scopedPreparationAbort = abort;
1592
+ try {
1593
+ return await this.#trackRead(this.dependencies.prepareScopedTask(request, AbortSignal.any([this.#abort.signal, abort.signal])));
1594
+ }
1595
+ finally {
1596
+ if (this.#scopedPreparationAbort === abort)
1597
+ this.#scopedPreparationAbort = undefined;
1598
+ }
1599
+ }
1600
+ async readAgentSettings() {
1601
+ if (!this.dependencies.readAgentSettings)
1602
+ throw new Error('agentSettingsUnavailable');
1603
+ return this.#trackRead(this.dependencies.readAgentSettings());
1604
+ }
1605
+ async previewAgentSettings(preferences, pluginId) {
1606
+ if (!this.dependencies.previewAgentSettings)
1607
+ throw new Error('agentSettingsUnavailable');
1608
+ return this.#trackRead(this.dependencies.previewAgentSettings(preferences, pluginId));
1609
+ }
1610
+ async saveAgentSettings(request) {
1611
+ this.#assertAcceptingActions();
1612
+ if (this.hasActiveWrite || !this.dependencies.saveAgentSettings)
1613
+ throw new Error('agentSettingsUnavailable');
1614
+ return this.dependencies.saveAgentSettings(request);
1615
+ }
1616
+ cancelScopedPreparation() { this.#scopedPreparationAbort?.abort(); }
1617
+ async discardScopedPlan(plan) {
1618
+ this.#scopedExecuted.add(plan);
1619
+ if (plan.kind === 'recovery' || plan.kind === 'retire')
1620
+ await plan.plan.context.dispose();
1621
+ }
1622
+ executeScopedTask(plan) {
1623
+ if (this.#retiring || this.#disposed)
1624
+ return Promise.reject(new Error('sessionRetiring'));
1625
+ if (!this.dependencies.executeScopedTask)
1626
+ return Promise.reject(new Error('recoveryUnavailable'));
1627
+ if (this.#scopedTaskInflight?.plan === plan)
1628
+ return this.#scopedTaskInflight.promise;
1629
+ if (this.hasActiveWrite || this.#scopedExecuted.has(plan)) {
1630
+ return Promise.reject(new Error('reviewTicketInvalid'));
1631
+ }
1632
+ this.#scopedExecuted.add(plan);
1633
+ this.#progressFeed?.clearCurrent();
1634
+ this.#progress = { messages: [], startedAtMs: this.dependencies.now?.() ?? Date.now() };
1635
+ this.#publish();
1636
+ const promise = this.dependencies.executeScopedTask(plan).finally(() => {
1637
+ if (this.#scopedTaskInflight?.promise === promise)
1638
+ this.#scopedTaskInflight = undefined;
1639
+ });
1640
+ this.#scopedTaskInflight = { plan, promise };
1641
+ return promise;
1642
+ }
1638
1643
  push(route, context) {
1639
1644
  if (this.#router.snapshot.current.route.name === 'result') {
1640
1645
  this.#invalidatePortableResultRecheck();
@@ -1974,12 +1979,12 @@ export class TuiController {
1974
1979
  this.#router.replace(nextRoute, context);
1975
1980
  this.#publish();
1976
1981
  }
1977
- beginConfigFlow(pluginId) {
1982
+ beginConfigFlow(pluginId, options = {}) {
1978
1983
  this.#router.beginConfigFlow(pluginId);
1979
1984
  this.#router.push({
1980
1985
  name: 'configFields',
1981
1986
  pluginId,
1982
- layer: { scope: 'effective' },
1987
+ layer: { profile: options.platform ?? 'common', context: options.context ?? 'user' },
1983
1988
  });
1984
1989
  this.#publish();
1985
1990
  }
@@ -1998,8 +2003,9 @@ export class TuiController {
1998
2003
  this.#tickets.delete(this.#activeReview.reviewToken);
1999
2004
  this.#activeReview = undefined;
2000
2005
  this.#router.beginFlow('configEdit', route, this.#router.snapshot.currentFrameId, current.context);
2001
- this.#router.push({ name: 'configFields', pluginId, layer: { scope: 'effective' } });
2002
- this.#router.push({ name: 'configFieldEditor', pluginId, layer: { scope: 'effective' }, fieldKey, editor: 'array', mode: 'choices' });
2006
+ const layer = ('layer' in route ? route.layer : undefined) ?? { profile: 'common', context: 'user' };
2007
+ this.#router.push({ name: 'configFields', pluginId, layer });
2008
+ this.#router.push({ name: 'configFieldEditor', pluginId, layer, fieldKey, editor: 'array', mode: 'choices' });
2003
2009
  this.#publish();
2004
2010
  }
2005
2011
  beginSavedConfigRecovery(pluginId) {
@@ -2013,9 +2019,7 @@ export class TuiController {
2013
2019
  name: 'configUnavailable',
2014
2020
  pluginId,
2015
2021
  storedScopes: plugin.savedConfigScopes,
2016
- ...(plugin.savedConfigScopes.length === 1
2017
- ? { layer: { scope: plugin.savedConfigScopes[0] } }
2018
- : {}),
2022
+ layer: { profile: 'common', context: 'user' },
2019
2023
  diagnosticCode: 'catalogSourceUnavailable',
2020
2024
  diagnosticStage: 'read-config',
2021
2025
  });
@@ -2032,11 +2036,11 @@ export class TuiController {
2032
2036
  || !this.#viewModel?.sourceTabs.some((source) => source.id === sourceId)) {
2033
2037
  throw new TuiOperationBlockedError('configSourceRecoveryUnavailable', '来源恢复只能从不可用的设置流程开始。');
2034
2038
  }
2035
- const layer = route.layer?.scope ?? route.resumeRoute?.layer.scope ?? 'effective';
2039
+ const layer = route.layer ?? route.resumeRoute?.layer ?? { profile: 'common', context: 'user' };
2036
2040
  const returnTarget = route.resumeRoute ?? {
2037
2041
  name: 'configFields',
2038
2042
  pluginId: route.pluginId,
2039
- layer: { scope: layer },
2043
+ layer,
2040
2044
  };
2041
2045
  this.#router.beginFlow('sourceRecovery', returnTarget, undefined, route.resumeContext);
2042
2046
  this.#router.push({ name: 'sourceDetail', sourceId });
@@ -2048,21 +2052,22 @@ export class TuiController {
2048
2052
  kind: 'pluginWrite', action: 'cleanup', pluginId, platform, scope, force: true,
2049
2053
  };
2050
2054
  try {
2051
- const evidence = await this.#trackRead(this.dependencies.captureEvidence(intent, this.#abort.signal));
2052
- const candidate = (evidence.offlineCleanupPlans
2053
- ?? (evidence.offlineCleanup ? [evidence.offlineCleanup] : [])).find((entry) => entry.pluginId === pluginId
2054
- && entry.platform === platform
2055
- && entry.scope === scope);
2056
- if (!evidence.operationPlan
2057
- || !candidate
2058
- || candidate.platform !== platform
2059
- || candidate.scope !== scope
2060
- || (candidate.forceExclusive !== true && candidate.plan.actions.length > 0)
2061
- || (candidate.plan.actions.length === 0
2062
- && !evidence.operationPlan.intentChanges.some((change) => change.scope === scope))) {
2055
+ const prepared = await this.prepareScopedTask({ kind: 'recovery', command: 'cleanup',
2056
+ scope, selectors: [pluginId], targets: [platform === 'claude' ? 'claude' : 'codex'],
2057
+ exactTargets: [{ pluginId, agent: platform === 'claude' ? 'claude' : 'codex' }], force: true });
2058
+ if (prepared.kind !== 'recovery')
2063
2059
  return undefined;
2060
+ try {
2061
+ const candidate = prepared.plan.nativeGroups.flatMap((group) => group.preview.operationPlan ? group.preview.offlineCleanupPlans ?? [] : [])
2062
+ .find((entry) => entry.pluginId === pluginId && entry.platform === platform && entry.scope === scope);
2063
+ if (prepared.plan.blockExecution || !candidate || candidate.forceExclusive !== true
2064
+ || candidate.plan.actions.length === 0)
2065
+ return undefined;
2066
+ return cloneFrozen({ pluginId, platform, scope, actionCount: candidate.plan.actions.length });
2067
+ }
2068
+ finally {
2069
+ await this.discardScopedPlan(prepared);
2064
2070
  }
2065
- return cloneFrozen({ pluginId, platform, scope, actionCount: candidate.plan.actions.length });
2066
2071
  }
2067
2072
  catch (error) {
2068
2073
  await this.#logOperationError(error, issueCode(error), intent);
@@ -2070,14 +2075,10 @@ export class TuiController {
2070
2075
  }
2071
2076
  }
2072
2077
  startForceCleanupReview(candidate) {
2073
- return this.startReviewPreparation({
2074
- kind: 'pluginWrite',
2075
- action: 'cleanup',
2076
- pluginId: candidate.pluginId,
2077
- platform: candidate.platform,
2078
- scope: candidate.scope,
2079
- force: true,
2080
- });
2078
+ const agent = candidate.platform === 'claude' ? 'claude' : 'codex';
2079
+ return this.prepareScopedTask({ kind: 'recovery', command: 'cleanup',
2080
+ scope: candidate.scope, selectors: [candidate.pluginId], targets: [agent],
2081
+ exactTargets: [{ pluginId: candidate.pluginId, agent }], force: true });
2081
2082
  }
2082
2083
  configOrigin(pluginId) {
2083
2084
  const origin = this.#router.configOrigin(pluginId);
@@ -2426,7 +2427,7 @@ export class TuiController {
2426
2427
  platform: draft.platforms.length === 2 ? 'both' : draft.platforms[0],
2427
2428
  scope: draft.rootScope,
2428
2429
  ...(draft.dependencyScopeOverrides.length > 0
2429
- ? { dependencyScopeOverrides: structuredClone(draft.dependencyScopeOverrides) } : {}),
2430
+ ? { dependencyScopeOverrides: structuredClone(draft.dependencyScopeOverrides), saveDependencyScopeOverrides: true } : {}),
2430
2431
  };
2431
2432
  return {
2432
2433
  kind: 'pluginWrite',
@@ -2435,7 +2436,7 @@ export class TuiController {
2435
2436
  platform: draft.platforms.length === 2 ? 'both' : draft.platforms[0],
2436
2437
  scope: draft.rootScope,
2437
2438
  ...(draft.dependencyScopeOverrides.length > 0
2438
- ? { dependencyScopeOverrides: draft.dependencyScopeOverrides }
2439
+ ? { dependencyScopeOverrides: draft.dependencyScopeOverrides, saveDependencyScopeOverrides: true }
2439
2440
  : {}),
2440
2441
  };
2441
2442
  }
@@ -2534,6 +2535,8 @@ export class TuiController {
2534
2535
  listPlugins(options) {
2535
2536
  const query = options.query?.trim().toLocaleLowerCase('en');
2536
2537
  const actualScopes = new Map();
2538
+ const portableObservedScopes = new Set();
2539
+ const uncertainPortablePlugins = new Set();
2537
2540
  const statusTargets = flattenStatusTargets(this.#status?.payload.targets ?? []);
2538
2541
  const targetsByPlugin = new Map();
2539
2542
  for (const target of statusTargets) {
@@ -2554,13 +2557,39 @@ export class TuiController {
2554
2557
  scopes.add(target.scope);
2555
2558
  actualScopes.set(target.pluginId, scopes);
2556
2559
  }
2560
+ for (const inventory of Object.values(this.#scopedInventories)) {
2561
+ if (inventory.coverage?.portable) {
2562
+ for (const scope of inventory.coverage.scopes ?? [inventory.scope])
2563
+ portableObservedScopes.add(scope);
2564
+ }
2565
+ for (const root of [...inventory.declaredRoots, ...inventory.extraInstalledRoots]) {
2566
+ for (const plugin of [root, ...root.dependencies]) {
2567
+ for (const target of plugin.targets) {
2568
+ if (target.delivery !== 'agent-skills')
2569
+ continue;
2570
+ if (target.actual !== true) {
2571
+ if (target.observation !== 'known')
2572
+ uncertainPortablePlugins.add(plugin.pluginId);
2573
+ continue;
2574
+ }
2575
+ const scopes = actualScopes.get(plugin.pluginId) ?? new Set();
2576
+ scopes.add(target.scope);
2577
+ actualScopes.set(plugin.pluginId, scopes);
2578
+ }
2579
+ }
2580
+ }
2581
+ }
2582
+ const portableAbsenceObserved = portableObservedScopes.has('user')
2583
+ && (!this.#status?.projectRoot || portableObservedScopes.has('project'));
2557
2584
  return cloneFrozen((this.#viewModel?.plugins ?? []).filter((plugin) => (!options.sourceId || plugin.sourceId === options.sourceId)
2558
2585
  && (options.installFilter !== 'installed'
2559
2586
  || (actualScopes.get(plugin.pluginId)?.size ?? 0) > 0)
2560
2587
  && (options.installFilter !== 'uninstalled'
2561
2588
  || plugin.aggregateStatus === 'available'
2562
- && (targetsByPlugin.get(plugin.pluginId)?.length ?? 0) > 0
2563
- && targetsByPlugin.get(plugin.pluginId).every((target) => target.state === 'absent')
2589
+ && ((targetsByPlugin.get(plugin.pluginId)?.length ?? 0) > 0
2590
+ || plugin.portableSkills && portableAbsenceObserved)
2591
+ && (targetsByPlugin.get(plugin.pluginId) ?? []).every((target) => target.state === 'absent')
2592
+ && !uncertainPortablePlugins.has(plugin.pluginId)
2564
2593
  && (actualScopes.get(plugin.pluginId)?.size ?? 0) === 0)
2565
2594
  && (options.installFilter !== 'project'
2566
2595
  || actualScopes.get(plugin.pluginId)?.has('project') === true)
@@ -2586,7 +2615,12 @@ export class TuiController {
2586
2615
  pluginSummaries: this.#status.pluginSummaries,
2587
2616
  targetRevisions: this.#status.targetRevisions,
2588
2617
  plugins: this.#viewModel.plugins,
2589
- projectInventory: this.#projectInventory,
2618
+ scopedInventories: this.#scopedInventories,
2619
+ scopedSnapshots: {
2620
+ user: this.#status.scopedRecovery?.user?.nativeSnapshot,
2621
+ project: this.#status.scopedRecovery?.project?.nativeSnapshot,
2622
+ },
2623
+ latestScope: this.#lastRefreshedScope,
2590
2624
  dependencyOverviews: this.#status.dependencyOverviews,
2591
2625
  });
2592
2626
  return subject ? cloneFrozen(subject) : undefined;
@@ -3060,14 +3094,14 @@ export class TuiController {
3060
3094
  }
3061
3095
  return cloneFrozen(await this.#trackRead(this.dependencies.inspectCatalogStore(this.#abort.signal)));
3062
3096
  }
3063
- async getConfigGroups(pluginId, layer) {
3097
+ async getConfigGroups(pluginId, options) {
3064
3098
  this.#assertAcceptingActions();
3065
3099
  if (!this.dependencies.getConfigGroups) {
3066
3100
  throw new TuiOperationBlockedError('configUnavailable', '设置读取入口不可用。');
3067
3101
  }
3068
- return cloneFrozen(await this.#trackRead(this.dependencies.getConfigGroups(pluginId, structuredClone(layer), this.#abort.signal)));
3102
+ return cloneFrozen(await this.#trackRead(this.dependencies.getConfigGroups(pluginId, structuredClone(options), this.#abort.signal)));
3069
3103
  }
3070
- async getConfigPage(pluginId, layer, signal) {
3104
+ async getConfigPage(pluginId, options, signal) {
3071
3105
  this.#assertAcceptingActions();
3072
3106
  if (!this.dependencies.getConfigPage) {
3073
3107
  throw new TuiOperationBlockedError('configUnavailable', '设置读取入口不可用。');
@@ -3075,14 +3109,61 @@ export class TuiController {
3075
3109
  const combined = signal
3076
3110
  ? AbortSignal.any([this.#abort.signal, signal])
3077
3111
  : this.#abort.signal;
3078
- return cloneFrozen(await this.#trackRead(this.dependencies.getConfigPage(pluginId, structuredClone(layer), combined)));
3112
+ return cloneFrozen(await this.#trackRead(this.dependencies.getConfigPage(pluginId, structuredClone(options), combined)));
3079
3113
  }
3080
- async getConfigReferenceContext(pluginId, scope, draft, signal) {
3114
+ async getConfigReferenceContext(pluginId, options, draft, signal) {
3081
3115
  this.#assertAcceptingActions();
3082
3116
  if (!this.dependencies.getConfigReferenceContext)
3083
3117
  return { selections: {} };
3084
3118
  const combined = signal ? AbortSignal.any([this.#abort.signal, signal]) : this.#abort.signal;
3085
- return cloneFrozen(await this.#trackRead(this.dependencies.getConfigReferenceContext(pluginId, scope, structuredClone(draft), combined)));
3119
+ return cloneFrozen(await this.#trackRead(this.dependencies.getConfigReferenceContext(pluginId, structuredClone(options), structuredClone(draft), combined)));
3120
+ }
3121
+ async saveConfigDraft(request, signal) {
3122
+ this.#assertAcceptingActions();
3123
+ if (this.hasActiveWrite)
3124
+ throw new TuiOperationBlockedError('writeInProgress', '另一个写操作仍在执行。');
3125
+ if (request.apply || request.kind === 'configSave' && request.retryApply) {
3126
+ throw new TuiOperationBlockedError('configSaveOnlyRequired', '此入口只保存配置。请单独准备插件更新。');
3127
+ }
3128
+ const intent = normalizeOperationIntent(structuredClone(request));
3129
+ const combined = signal ? AbortSignal.any([this.#abort.signal, signal]) : this.#abort.signal;
3130
+ const promise = (async () => {
3131
+ try {
3132
+ this.#validateReviewIntent(intent);
3133
+ const evidence = await this.#prepareReviewEvidence(intent, combined);
3134
+ combined.throwIfAborted();
3135
+ const execution = await this.dependencies.executeIntent(intent, evidence, combined);
3136
+ if (intent.kind === 'configSaveBatch') {
3137
+ const batch = execution;
3138
+ return {
3139
+ status: batch.status,
3140
+ savedProfiles: batch.groups.filter((group) => group.configSaved).map((group) => group.platform ?? 'common'),
3141
+ failedProfiles: batch.groups.filter((group) => !group.configSaved).map((group) => ({
3142
+ profile: group.platform ?? 'common',
3143
+ ...(group.errorCode ? { errorCode: safeDiagnosticCode(group.errorCode, 'configWriteFailed') } : {}),
3144
+ })),
3145
+ };
3146
+ }
3147
+ const saved = typeof execution === 'object' && execution !== null
3148
+ && execution.configWrite === 'succeeded';
3149
+ return { status: saved ? 'succeeded' : 'failed', savedProfiles: saved ? [intent.platform ?? 'common'] : [],
3150
+ failedProfiles: saved ? [] : [{ profile: intent.platform ?? 'common', errorCode: 'configWriteUnconfirmed' }] };
3151
+ }
3152
+ catch (error) {
3153
+ await this.#logOperationError(error, issueCode(error), intent);
3154
+ throw new TuiOperationBlockedError(issueCode(error), sanitizeDiagnosticText(error instanceof Error ? error.message : '设置未保存。', [], collectOperationSensitiveValues(intent)));
3155
+ }
3156
+ })();
3157
+ this.#configSaveInflight = promise;
3158
+ this.#publish();
3159
+ try {
3160
+ return cloneFrozen(await promise);
3161
+ }
3162
+ finally {
3163
+ if (this.#configSaveInflight === promise)
3164
+ this.#configSaveInflight = undefined;
3165
+ this.#publish();
3166
+ }
3086
3167
  }
3087
3168
  async previewConfigSaveImpact(intent, signal) {
3088
3169
  this.#assertAcceptingActions();
@@ -3097,12 +3178,12 @@ export class TuiController {
3097
3178
  }
3098
3179
  return cloneFrozen(evidence.config);
3099
3180
  }
3100
- async getConfigResetAvailability(pluginId, layer) {
3181
+ async getConfigResetAvailability(pluginId, options) {
3101
3182
  this.#assertAcceptingActions();
3102
3183
  if (!this.dependencies.getConfigResetAvailability) {
3103
3184
  throw new TuiOperationBlockedError('configResetVerificationUnavailable', '设置层可信读取入口不可用。');
3104
3185
  }
3105
- return cloneFrozen(await this.#trackRead(this.dependencies.getConfigResetAvailability(pluginId, structuredClone(layer), this.#abort.signal)));
3186
+ return cloneFrozen(await this.#trackRead(this.dependencies.getConfigResetAvailability(pluginId, structuredClone(options), this.#abort.signal)));
3106
3187
  }
3107
3188
  listUserIssues() {
3108
3189
  return cloneFrozen(this.#viewModel?.issues ?? []);
@@ -3215,25 +3296,6 @@ export class TuiController {
3215
3296
  }
3216
3297
  }
3217
3298
  if (intent.kind === 'pluginWriteBatch') {
3218
- if ((intent.selection.kind === 'currentProjectIntentRoots'
3219
- || intent.selection.kind === 'currentProjectIntentTargets')
3220
- && (intent.action !== 'sync' || intent.scope !== 'project')) {
3221
- throw new TuiOperationBlockedError('batchSelectionInvalid', '当前项目全部同步只支持 project scope sync。');
3222
- }
3223
- if (intent.selection.kind === 'currentProjectIntentTargets') {
3224
- const targets = intent.selection.targets;
3225
- const canonical = [...new Map(targets.map((target) => [
3226
- projectReminderTargetKey(target), target,
3227
- ])).values()].sort((left, right) => projectReminderTargetKey(left)
3228
- .localeCompare(projectReminderTargetKey(right), 'en'));
3229
- if (targets.length === 0
3230
- || targets.some((target) => target.scope !== 'project')
3231
- || JSON.stringify(targets) !== JSON.stringify(canonical)) {
3232
- throw new TuiOperationBlockedError('batchSelectionInvalid', '当前项目同步目标必须是非空、规范且精确的 project 目标集合。');
3233
- }
3234
- for (const target of targets)
3235
- sourceIdFromPluginId(target.pluginId);
3236
- }
3237
3299
  if (intent.selection.kind === 'selected') {
3238
3300
  const canonical = canonicalPluginIds(intent.selection.pluginIds);
3239
3301
  if (canonical.length === 0) {
@@ -3258,7 +3320,7 @@ export class TuiController {
3258
3320
  if (intent.kind === 'pluginWriteBatch') {
3259
3321
  return intent.selection.kind === 'selected'
3260
3322
  ? `${intent.selection.pluginIds.length} 个插件`
3261
- : '当前项目全部插件';
3323
+ : `${intent.selection.targets.length} 个目标`;
3262
3324
  }
3263
3325
  if ('pluginId' in intent) {
3264
3326
  return this.#viewModel?.plugins.find((plugin) => plugin.pluginId === intent.pluginId)?.displayName
@@ -3464,7 +3526,7 @@ export class TuiController {
3464
3526
  const configImpact = configEvidence ? {
3465
3527
  state: configEvidence.some((group) => group.impactState !== 'known') ? 'unknown' : 'known',
3466
3528
  affectedTargetCount: new Set(configEvidence.flatMap((group) => group.affectedTargets
3467
- .map((target) => `${target.platform}\0${target.scope}`))).size
3529
+ .map((target) => `${target.pluginName ?? ''}\0${target.platform}\0${target.scope}`))).size
3468
3530
  + new Set(configEvidence.flatMap((group) => (group.commandPackages ?? [])
3469
3531
  .map((target) => target.packageId))).size,
3470
3532
  } : undefined;
@@ -3887,6 +3949,19 @@ export class TuiController {
3887
3949
  summary: pluginSummary,
3888
3950
  });
3889
3951
  }
3952
+ const concurrentScopeConflict = ticket.intent.kind === 'pluginWrite'
3953
+ && ticket.intent.action === 'install'
3954
+ && unsuccessfulTargets.length === pluginSummary.targets?.length
3955
+ && unsuccessfulTargets.every((target) => target.errorCode === 'codexExtensionScopeConflict');
3956
+ if (concurrentScopeConflict) {
3957
+ return this.#setBlocked(operationId, 'codexExtensionScopeConflict', {
3958
+ intent: ticket.intent,
3959
+ navigation: 'result',
3960
+ returnRoute: ticket.returnRoute,
3961
+ summary: pluginSummary,
3962
+ writeMayHaveCompleted: false,
3963
+ });
3964
+ }
3890
3965
  const dependencyStillRequired = unsuccessfulTargets.length > 0
3891
3966
  && unsuccessfulTargets.every((target) => target.errorCode === 'dependencyStillRequired');
3892
3967
  return this.#setBlocked(operationId, dependencyStillRequired ? 'dependencyStillRequired' : 'operationFailed', {
@@ -3902,7 +3977,11 @@ export class TuiController {
3902
3977
  });
3903
3978
  }
3904
3979
  const originIssue = ticket.intent.kind === 'pluginWrite' ? ticket.intent.originIssue : undefined;
3980
+ const projectRemovalKeepsDemand = ticket.intent.kind === 'pluginWrite'
3981
+ && ticket.intent.action === 'uninstall' && ticket.intent.scope === 'project'
3982
+ && pluginSummary?.code === 'operationSucceeded';
3905
3983
  if (originIssue && !dependencyPartial
3984
+ && !projectRemovalKeepsDemand
3906
3985
  && this.#viewModel?.issues.some((issue) => originIssueRemains(issue, originIssue))) {
3907
3986
  return this.#setBlocked(operationId, 'issueStillPresent', {
3908
3987
  intent: ticket.intent,
@@ -3912,7 +3991,7 @@ export class TuiController {
3912
3991
  verification: 'issueStillPresent',
3913
3992
  });
3914
3993
  }
3915
- if (ticket.intent.kind === 'configSave' || ticket.intent.kind === 'configReset') {
3994
+ if ((ticket.intent.kind === 'configSave' && !ticket.intent.retryApply) || ticket.intent.kind === 'configReset') {
3916
3995
  const configWrite = typeof execution === 'object' && execution !== null
3917
3996
  ? execution.configWrite
3918
3997
  : undefined;
@@ -3986,7 +4065,7 @@ export class TuiController {
3986
4065
  : ticket.returnRoute)),
3987
4066
  };
3988
4067
  const configResultNeedsAttention = result.state === 'succeeded' && (result.summary.messages?.some((message) => message.startsWith('警告:')
3989
- || message === '提示:其它项目没有被扫描,可能需要在对应项目目录中运行 aw sync。') ?? false);
4068
+ || message === '提示:其它项目没有被扫描,可能需要在对应项目目录中运行 aw plugin sync --context project。') ?? false);
3990
4069
  if ((ticket.intent.kind === 'configSave' || ticket.intent.kind === 'configSaveBatch'
3991
4070
  || ticket.intent.kind === 'configReset')
3992
4071
  && this.#portableRepreparePluginId === ticket.intent.pluginId
@@ -4085,7 +4164,7 @@ export class TuiController {
4085
4164
  ? '设置已保存,但后续应用失败。'
4086
4165
  : '设置已保存,但后续步骤未完成。',
4087
4166
  pluginId: ticket.intent.pluginId,
4088
- configScope: ticket.intent.scope,
4167
+ configScope: 'user', configPlatform: ticket.intent.platform, configContext: ticket.intent.context ?? 'user',
4089
4168
  ...(ticket.intent.kind === 'configSave' && ticket.intent.apply
4090
4169
  ? { configApplyRetryAvailable: true } : {}),
4091
4170
  elapsedMs,
@@ -4279,6 +4358,7 @@ export class TuiController {
4279
4358
  if (this.#disposed)
4280
4359
  return;
4281
4360
  this.#disposed = true;
4361
+ this.#scopedPreparationAbort?.abort();
4282
4362
  this.#invalidatePortableResultRecheck();
4283
4363
  this.#cancelReviewPreparation();
4284
4364
  this.#cancelPortableRequest();
@@ -4687,6 +4767,7 @@ function operationResultSummaryUnsafe(intent, execution) {
4687
4767
  : false,
4688
4768
  configGroups: batch.groups.map((group) => ({
4689
4769
  scope: group.scope,
4770
+ platform: group.platform,
4690
4771
  configSaved: group.configSaved,
4691
4772
  applyRequested: group.applyRequested,
4692
4773
  applyStatus: group.applyStatus,
@@ -4698,7 +4779,7 @@ function operationResultSummaryUnsafe(intent, execution) {
4698
4779
  : []),
4699
4780
  messages: [
4700
4781
  ...batch.groups.map((group) => {
4701
- const label = group.scope === 'project' ? '当前项目' : '个人默认';
4782
+ const label = group.platform ? `${group.platform} 平台配置` : '公共配置';
4702
4783
  return group.configSaved
4703
4784
  ? `${label}已保存${group.errorCode === 'invalidConfig'
4704
4785
  ? ',但当前配置无效,未应用'
@@ -4707,7 +4788,7 @@ function operationResultSummaryUnsafe(intent, execution) {
4707
4788
  }),
4708
4789
  ...publicWarnings.map((warning) => `警告:${warning}`),
4709
4790
  ...(otherProjectsMayRequireSync
4710
- ? ['提示:其它项目没有被扫描,可能需要在对应项目目录中运行 aw sync。']
4791
+ ? ['提示:其它项目没有被扫描,可能需要在对应项目目录中运行 aw plugin sync --context project。']
4711
4792
  : []),
4712
4793
  ],
4713
4794
  };
@@ -4727,7 +4808,7 @@ function operationResultSummaryUnsafe(intent, execution) {
4727
4808
  const target = typeof entry.target === 'object' && entry.target !== null
4728
4809
  ? entry.target
4729
4810
  : entry;
4730
- if ((target.platform !== 'codex' && target.platform !== 'claude')
4811
+ if (!isPlatformTarget(target.platform)
4731
4812
  || (target.scope !== 'user' && target.scope !== 'project')
4732
4813
  || (entry.state !== 'completed' && entry.state !== 'failed' && entry.state !== 'skipped'))
4733
4814
  return [];
@@ -4761,13 +4842,12 @@ function operationResultSummaryUnsafe(intent, execution) {
4761
4842
  ? execution.warnings.filter((warning) => typeof warning === 'string'
4762
4843
  && warning.startsWith('projectConfigShareabilityUnverified:'))
4763
4844
  : [];
4764
- const otherProjectsMayRequireSync = intent.scope === 'user'
4765
- && typeof execution === 'object' && execution !== null
4845
+ const otherProjectsMayRequireSync = typeof execution === 'object' && execution !== null
4766
4846
  && execution.otherProjectsMayRequireSync === true;
4767
4847
  const publicMessages = [
4768
4848
  ...publicWarnings.map((warning) => `警告:${warning}`),
4769
4849
  ...(otherProjectsMayRequireSync
4770
- ? ['提示:其它项目没有被扫描,可能需要在对应项目目录中运行 aw sync。']
4850
+ ? ['提示:其它项目没有被扫描,可能需要在对应项目目录中运行 aw plugin sync --context project。']
4771
4851
  : []),
4772
4852
  ];
4773
4853
  const partial = overallState === 'partial'
@@ -4784,7 +4864,7 @@ function operationResultSummaryUnsafe(intent, execution) {
4784
4864
  message: invalidConfig ? '设置已保存,但没有应用。当前配置仍无效。'
4785
4865
  : failed || partial ? '设置已保存,但插件内容重新应用失败或部分失败。' : '操作已完成。',
4786
4866
  pluginId: intent.pluginId,
4787
- configScope: intent.scope,
4867
+ configScope: 'user', configPlatform: intent.platform, configContext: intent.context ?? 'user',
4788
4868
  configApplyRetryAvailable: intent.kind === 'configSave'
4789
4869
  && intent.apply && !invalidConfig && (applyState === 'partial' || applyState === 'unavailable'),
4790
4870
  ...(intent.kind === 'configSave' && intent.retryApply ? { configRetryApply: true } : {}),
@@ -4804,7 +4884,7 @@ function operationResultSummaryUnsafe(intent, execution) {
4804
4884
  ? { quarantineBasename } : {}),
4805
4885
  }
4806
4886
  : {}),
4807
- ...(targets.length > 0 ? { targets } : {}),
4887
+ ...(targets.length > 0 ? { configTargets: targets } : {}),
4808
4888
  ...(commandResults.length > 0 ? { commandResults,
4809
4889
  recoveryActions: [...new Set(commandResults.flatMap((result) => result.recoveryActions))] } : {}),
4810
4890
  };