@lesliechan721/aw 0.1.0-beta.64 → 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 +405 -1626
  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 +9 -2
  267. package/dist/src/utils/plugin-config-references.js +75 -85
  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
package/dist/src/index.js CHANGED
@@ -1,41 +1,36 @@
1
1
  #!/usr/bin/env node
2
+ import { describeNativeToolClosure } from './utils/native-tool-review.js';
2
3
  import fs from 'node:fs';
3
4
  import path from 'node:path';
4
5
  import { spawn } from 'node:child_process';
5
6
  import { fileURLToPath } from 'node:url';
6
7
  import { Command, CommanderError, InvalidArgumentError, Option } from 'commander';
7
8
  import { doctorEvalProviders } from '@lesliechan721/agent-skill-eval';
9
+ import { compilePluginConfigDefinition, PLATFORM_TARGETS, PORTABLE_PLATFORM_TARGETS } from '@lesliechan721/aw-plugin-core/manifest';
8
10
  import { initCommand } from './commands/init.js';
9
11
  import { resolveCatalogWorkspaceRoot } from './commands/catalog-workspace-resolution.js';
10
12
  import { addCatalogSourceCommand, preflightCatalogSourceMutation, explicitRefPolicy, listCatalogSources, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from './commands/catalog-source.js';
11
13
  import { catalogSourceStatusCommand, syncCatalogSourceCommand, } from './commands/catalog-generation.js';
12
14
  import { catalogStoreErrorCode, catalogStorePrune, catalogStoreStatus, } from './commands/catalog-store.js';
13
- import { cleanupWriteCommand as cleanupCommand, installWriteCommand as installCommand, reinstallWriteCommand as reinstallCommand, uninstallWriteCommand as uninstallCommand, } from './commands/write-commands.js';
14
- import { syncCommand } from './commands/sync-command.js';
15
15
  import { formatInteractiveConsoleUnavailableMessage, runConsoleController, } from './commands/console.js';
16
- import { runCommandWizard, toWizardCandidates, WizardExecutionError, } from './commands/wizard.js';
16
+ import { WizardExecutionError, } from './commands/wizard.js';
17
17
  import { createCliExecutionContext, emitCommandEvent, } from './commands/runtime.js';
18
- import { loadConsoleItemsResult, statusCommandV8 } from './commands/status-v6.js';
19
- import { normalizeUninstallCommandOptions, normalizeCleanupCommandOptions, resolveConfigReadResolution, resolveConfigWriteScope, normalizeInstallCommandOptions, normalizeReinstallCommandOptions, resolveScriptTimeoutMs, } from './commands/options.js';
18
+ import { parseContextValue, parsePlatformTargetValue, parseScopeValue, resolveScriptTimeoutMs, } from './commands/options.js';
20
19
  import { isCommandWarningError, } from './types.js';
21
20
  import { formatWarnings } from './output/reporter.js';
22
21
  import { redactString, redactValue } from './output/redact.js';
23
- import { renderShellCommand, } from './output/equivalent-command.js';
24
- import { buildRebuildReceiptJson, buildInitReceiptJson, renderRebuildReceipt, renderInitReceipt, } from './output/command-receipt.js';
22
+ import { buildInitReceiptJson, renderInitReceipt, } from './output/command-receipt.js';
25
23
  import { resolveCliPackageRoot } from './utils/paths.js';
26
- import { prepareNativeCliExecution } from './utils/native-cli.js';
27
- import { findConfigFieldEntry, formatPluginConfigSetRecoveryCommand, listNamespacedPluginConfigStores, listPluginConfigStorePluginNames, readPluginConfigStore, renderPluginConfigValueForDisplay, resolveConfigRead, } from './utils/plugin-config.js';
28
- import { applyPluginConfigWrite, buildConfigMutationReceipt, configWriteStateForError, describeResetAffectedFields, resetPluginConfigWithHelper, } from './utils/plugin-config-write.js';
24
+ import { findConfigFieldEntry, formatPluginConfigSetRecoveryCommand, listNamespacedPluginConfigStores, readPluginConfigStore, renderPluginConfigValueForDisplay, resolveConfigRead, } from './utils/plugin-config.js';
25
+ import { applyPluginConfigWrite, applyPluginConfig, buildConfigMutationReceipt, configWriteStateForError, resetPluginConfigWithHelper, } from './utils/plugin-config-write.js';
29
26
  import { assertStrictSemVer } from './utils/manifest.js';
30
- import { writeCandidateEligibility } from './utils/target-eligibility.js';
31
27
  import { assertCatalogSourceReferencesSupported, inventoryLegacyConfigReferences, } from './utils/catalog-source-references.js';
32
28
  import { parsePluginId, withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, } from './utils/multi-source-catalog.js';
33
29
  import { sanitizeCatalogSourceError, sanitizeCatalogSourceErrorMessage, } from './utils/catalog-public-error.js';
34
30
  import { catalogGenerationErrorCode } from './utils/catalog-generation-error.js';
35
31
  import { resolveCatalogOperationContext, } from './utils/catalog-operation-context.js';
36
32
  import { withAwHomeMutationLock, } from './utils/aw-home-mutation-lock.js';
37
- import { installPortableSkills, materializePortableSkillsToDirectory, PortableCommandError, renderPortableReview, } from './portable/commands.js';
38
- import { SkillsCliError } from './portable/skills-cli.js';
33
+ import { materializePortableSkillsToDirectory, PortableCommandError } from './portable/commands.js';
39
34
  import { createReviewInspectionFailure, getReviewInspectionExitCode, inspectReviewTarget, } from './commands/review-inspection.js';
40
35
  import { prepareReviewTarget, } from './commands/review-preparation.js';
41
36
  import { runCatalogBuildCommand, runCatalogInitCommand, runCatalogValidateCommand, runCatalogVersionCommand, runPluginBuildCommand, runPluginTestCommand, } from './authoring/workspace-commands.js';
@@ -46,6 +41,13 @@ import { activateCatalogDevSource, catalogDevSourceError, createCatalogDevFailur
46
41
  import { getPluginCapabilitiesResult, parsePluginCapabilitySurface, renderPluginCapabilities, } from './commands/plugin-capabilities.js';
47
42
  import { AwCapabilityUnsupportedError, checkCurrentAw, describeCurrentAw, inspectPluginRequirements, } from './commands/aw-capabilities.js';
48
43
  import { prepareSkillEval } from './commands/skill-eval.js';
44
+ import { readRecoveryStatus, checkRecovery } from './recovery/status.js';
45
+ import { intentAdd, intentRemove, intentList } from './commands/intent-command.js';
46
+ import { targetsSet, targetsList, targetsChangeExclusion } from './commands/targets-command.js';
47
+ import { runRecovery } from './recovery/coordinator.js';
48
+ import { runRetire } from './recovery/retirement.js';
49
+ import { normalizeAgents } from './intent/deployment-preferences.js';
50
+ import { resolveWorkContext } from './intent/work-context.js';
49
51
  import { registerManagedCommandCli, runManagedCommandCli } from './commands/managed-commands.js';
50
52
  function createDefaultIo() {
51
53
  return {
@@ -69,7 +71,7 @@ function strictValueOption(flags, description, choices) {
69
71
  return value;
70
72
  });
71
73
  }
72
- function collectUniqueOption(value, previous) {
74
+ function collectUniqueOption(value, previous = []) {
73
75
  return previous.includes(value) ? previous : [...previous, value];
74
76
  }
75
77
  function renderReviewSkills(write, skills, issues) {
@@ -110,9 +112,6 @@ function resolveCliVersion() {
110
112
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
111
113
  return assertStrictSemVer(packageJson.version, 'packages/cli/package.json version');
112
114
  }
113
- function resolveCommandReceiptExitCode(result) {
114
- return result.overallState === 'succeeded' ? 0 : 1;
115
- }
116
115
  class WriteCommandRequestError extends Error {
117
116
  code = 'invalidArguments';
118
117
  }
@@ -140,122 +139,47 @@ function writeCommandFailure(error) {
140
139
  message,
141
140
  };
142
141
  }
143
- function preparseWriteReceiptRequest(argv) {
144
- const request = { json: false, plugins: [] };
145
- let positionalOnly = false;
146
- for (let index = 1; index < argv.length; index += 1) {
147
- const token = argv[index];
148
- if (positionalOnly) {
149
- request.plugins.push(token);
150
- continue;
151
- }
152
- if (token === '--') {
153
- positionalOnly = true;
154
- continue;
155
- }
156
- if (token === '--json') {
157
- request.json = true;
158
- continue;
159
- }
160
- if (token === '--platform' || token === '--scope') {
161
- const value = argv[index + 1];
162
- if (token === '--platform')
163
- request.platform = value;
164
- else
165
- request.scope = value;
166
- index += 1;
167
- continue;
168
- }
169
- if (token.startsWith('--platform=')) {
170
- request.platform = token.slice('--platform='.length);
171
- continue;
172
- }
173
- if (token.startsWith('--scope=')) {
174
- request.scope = token.slice('--scope='.length);
175
- continue;
176
- }
177
- if (token === '--dependency-scope' || token === '--script-timeout' || token === '--log-file') {
178
- index += 1;
179
- continue;
180
- }
181
- if (token.startsWith('--'))
182
- continue;
183
- if (token.startsWith('-') && token !== '-') {
184
- const shortOptions = token.slice(1);
185
- for (let offset = 0; offset < shortOptions.length; offset += 1) {
186
- const option = shortOptions[offset];
187
- if (option === 'j') {
188
- request.json = true;
189
- continue;
190
- }
191
- if (option === 'p' || option === 's') {
192
- const value = shortOptions.slice(offset + 1) || argv[index + 1];
193
- if (option === 'p')
194
- request.platform = value;
195
- else
196
- request.scope = value;
197
- if (shortOptions.length === offset + 1)
198
- index += 1;
199
- break;
200
- }
201
- }
202
- continue;
203
- }
204
- request.plugins.push(token);
205
- }
206
- return request;
207
- }
208
- function jsonWriteCommandRequest(argv) {
209
- const command = argv[0];
210
- if (!['install', 'reinstall', 'uninstall', 'cleanup', 'sync'].includes(command ?? '')) {
142
+ function recoveryRequest(argv) {
143
+ const nested = argv[0] === 'plugin';
144
+ const args = nested ? argv.slice(1) : argv;
145
+ const family = args[0] ?? '';
146
+ if (nested && ['remove', 'status'].includes(family)
147
+ && args.some((value) => value === '--workspace-root' || value.startsWith('--workspace-root=')))
211
148
  return null;
212
- }
213
- const request = preparseWriteReceiptRequest(argv);
214
- if (!request.json)
149
+ if (!['intent', 'targets', 'sync', 'install', 'remove', 'reinstall', 'uninstall', 'cleanup', 'status', 'project'].includes(family))
215
150
  return null;
216
- const rawPlatform = request.platform;
217
- const platform = rawPlatform === 'codex' || rawPlatform === 'claude' || rawPlatform === 'both'
218
- ? rawPlatform
219
- : 'codex';
151
+ let rawScope;
152
+ let json = false;
153
+ for (let index = 1; index < args.length; index++) {
154
+ const value = args[index];
155
+ if (value === '--')
156
+ break;
157
+ if (value === '--scope')
158
+ rawScope = args[++index];
159
+ else if (value.startsWith('--scope='))
160
+ rawScope = value.slice('--scope='.length);
161
+ else if (value === '--json' || (/^-[ajvyD]+$/u.test(value) && value.includes('j')))
162
+ json = true;
163
+ else if (['--target', '--context', '--script-timeout', '--log-file'].includes(value))
164
+ index++;
165
+ }
220
166
  return {
221
- command: command,
222
- plugins: request.plugins,
223
- platform,
224
- scope: request.scope === 'project' ? 'project' : 'user',
167
+ command: ['intent', 'targets', 'project'].includes(family) ? `${family}:${args[1] ?? ''}` : family,
168
+ scope: rawScope === 'user' || rawScope === 'project' ? rawScope : null,
169
+ json,
225
170
  };
226
171
  }
227
- function writeCommandFailureReceipt(write, request, error) {
228
- const platforms = request.platform === 'both'
229
- ? ['codex', 'claude']
230
- : [request.platform];
231
- writeJsonReceipt(write, buildRebuildReceiptJson(request.command, {
232
- platforms,
233
- plugins: request.plugins,
234
- warnings: [],
235
- overallState: 'failed',
236
- errors: [writeCommandFailure(error)],
237
- receiptTargets: request.plugins.flatMap((selector) => platforms.map((platform) => {
238
- let pluginName = selector;
239
- let sourceId;
240
- try {
241
- const parsed = parsePluginId(selector);
242
- pluginName = parsed.pluginName;
243
- sourceId = parsed.sourceId;
244
- }
245
- catch {
246
- // Keep the raw selector in the failed receipt.
247
- }
248
- return {
249
- pluginName,
250
- pluginId: selector,
251
- ...(sourceId ? { sourceId } : {}),
252
- platform,
253
- scope: request.scope,
254
- };
255
- })),
256
- }));
172
+ function recoveryFailureReceipt(command, scope, error) {
173
+ const failure = writeCommandFailure(error);
174
+ return {
175
+ schemaVersion: 1, kind: 'aw-recovery', scope, command, ok: false,
176
+ intent: { state: 'unreadable', roots: null }, requestedRoots: [], requestedTargets: [],
177
+ selection: { agents: [], origin: 'none', preferences: 'unreadable' },
178
+ blockers: [failure], errors: [failure], targetResults: [], remainingTargets: [], recoveryActions: [],
179
+ declarationChanged: false, preferencesChanged: false, configChanged: false, outputsMayHaveChanged: false,
180
+ };
257
181
  }
258
- function scanConfigMutationShortOptions(token, command) {
182
+ function scanConfigMutationShortOptions(token) {
259
183
  if (!/^-[^-].*/u.test(token))
260
184
  return { json: false, consumesNextValue: false };
261
185
  let json = false;
@@ -271,7 +195,7 @@ function scanConfigMutationShortOptions(token, command) {
271
195
  json = true;
272
196
  continue;
273
197
  }
274
- if (option === 'v' || option === 'D' || (option === 'y' && command === 'config:reset')) {
198
+ if (option === 'v' || option === 'D' || option === 'y') {
275
199
  continue;
276
200
  }
277
201
  break;
@@ -279,15 +203,16 @@ function scanConfigMutationShortOptions(token, command) {
279
203
  return { json, consumesNextValue: false };
280
204
  }
281
205
  function jsonConfigMutationRequest(argv) {
282
- if (argv[0] !== 'config' || !['set', 'unset', 'reset'].includes(argv[1] ?? '')) {
206
+ const args = argv[0] === 'plugin' ? argv.slice(1) : argv;
207
+ if (args[0] !== 'config' || !['set', 'edit', 'unset', 'reset', 'apply'].includes(args[1] ?? '')) {
283
208
  return null;
284
209
  }
285
- const command = `config:${argv[1]}`;
210
+ const command = `config:${args[1]}`;
286
211
  let json = false;
287
212
  let plugin;
288
- const valueOptions = new Set(['--scope', '--value-json', '--preset', '--log-file']);
289
- for (let index = 2; index < argv.length; index += 1) {
290
- const token = argv[index];
213
+ const valueOptions = new Set(['--platform', '--context', '--target', '--value-json', '--preset', '--log-file', '--script-timeout']);
214
+ for (let index = 2; index < args.length; index += 1) {
215
+ const token = args[index];
291
216
  if (token === '--')
292
217
  break;
293
218
  if (valueOptions.has(token)) {
@@ -298,7 +223,7 @@ function jsonConfigMutationRequest(argv) {
298
223
  json = true;
299
224
  continue;
300
225
  }
301
- const shortOptions = scanConfigMutationShortOptions(token, command);
226
+ const shortOptions = scanConfigMutationShortOptions(token);
302
227
  if (shortOptions.json)
303
228
  json = true;
304
229
  if (shortOptions.consumesNextValue)
@@ -314,6 +239,24 @@ function jsonConfigMutationRequest(argv) {
314
239
  ...(plugin ? { plugin } : {}),
315
240
  };
316
241
  }
242
+ function jsonConfigReadRequested(argv) {
243
+ if (argv[0] !== 'plugin' || argv[1] !== 'config'
244
+ || !['schema', 'list', 'get'].includes(argv[2] ?? ''))
245
+ return false;
246
+ const values = new Set(['--platform', '--context', '--target', '--script-timeout', '--log-file']);
247
+ for (let index = 3; index < argv.length; index++) {
248
+ const token = argv[index];
249
+ if (token === '--')
250
+ break;
251
+ if (values.has(token)) {
252
+ index++;
253
+ continue;
254
+ }
255
+ if (token === '--json' || /^-[jvD]+$/u.test(token) && token.includes('j'))
256
+ return true;
257
+ }
258
+ return false;
259
+ }
317
260
  function configMutationFailure(error, env, cwd) {
318
261
  const configWrite = configWriteStateForError(error);
319
262
  if (error instanceof CommanderError) {
@@ -404,35 +347,6 @@ function toCliDiagnosticsOptions(commandName, rawOptions, runtimeDiagnostics) {
404
347
  function isWizardAvailable(context, rawOptions) {
405
348
  return context.interactive && context.isTty && !context.isCi && rawOptions.json !== true;
406
349
  }
407
- function shellCommand(tokens) {
408
- return renderShellCommand(tokens, 'posix');
409
- }
410
- function optionTokens(flag, value) {
411
- return value === undefined ? [] : [flag, value];
412
- }
413
- function writeCommandUsage(command) {
414
- const cardinality = command === 'reinstall' || command === 'cleanup' ? '<plugin>' : '<plugin...>';
415
- const scope = command === 'cleanup' ? ' --scope <project|user>' : ' [--scope <project|user>]';
416
- return [
417
- `${command} 命令缺少必要参数。`,
418
- `Usage: aw ${command} ${cardinality} --platform <codex|claude|both>${scope}`,
419
- `Example: aw ${command} example-plugin --platform codex --scope user`,
420
- ].join('\n');
421
- }
422
- function configCommandUsage(command) {
423
- const argumentsByCommand = {
424
- list: '<plugin>',
425
- get: '<plugin> [key]',
426
- set: '<plugin> <field> --scope <project|user> (--value-json <json> | --value-stdin | --preset <id>)',
427
- unset: '<plugin> <key>',
428
- reset: '<plugin> --scope <project|user> --yes',
429
- };
430
- return [
431
- `config ${command} 命令缺少必要参数。`,
432
- `Usage: aw config ${command} ${argumentsByCommand[command]}`,
433
- `Example: aw config ${command} example-plugin${command === 'set' ? ' mode --scope user --value-json \'"safe"\'' : command === 'unset' ? ' key' : command === 'reset' ? ' --scope user --yes' : ''}`,
434
- ].join('\n');
435
- }
436
350
  async function readUtf8Stdin(stdin) {
437
351
  const chunks = [];
438
352
  for await (const chunk of stdin) {
@@ -440,137 +354,6 @@ async function readUtf8Stdin(stdin) {
440
354
  }
441
355
  return Buffer.concat(chunks).toString('utf8');
442
356
  }
443
- function equivalentWriteCommand(command, selection, rawOptions) {
444
- return shellCommand(buildEquivalentWriteCommandTokens(command, selection, rawOptions));
445
- }
446
- export function buildEquivalentWriteCommandTokens(command, selection, rawOptions) {
447
- return [
448
- 'aw',
449
- command,
450
- '--platform',
451
- selection.platform ?? 'both',
452
- '--scope',
453
- selection.scope ?? 'user',
454
- ...(rawOptions.allowManagedCodexInstall === true
455
- ? ['--allow-managed-codex-install']
456
- : []),
457
- ...(command === 'cleanup' ? ['--yes'] : []),
458
- ...(command === 'install'
459
- ? (rawOptions.dependencyScope ?? []).flatMap((value) => ['--dependency-scope', value])
460
- : []),
461
- ...((command === 'install' || command === 'reinstall')
462
- ? optionTokens('--script-timeout', rawOptions.scriptTimeout)
463
- : []),
464
- ...(selection.plugins.length > 0 ? ['--', ...selection.plugins] : []),
465
- ];
466
- }
467
- function equivalentConfigCommand(options) {
468
- return shellCommand(buildEquivalentConfigCommandTokens(options));
469
- }
470
- function equivalentConfigCommandProjection(resolve) {
471
- return {
472
- equivalentCommand: (selection) => equivalentConfigCommand(resolve(selection)),
473
- equivalentCommandTokens: (selection) => buildEquivalentConfigCommandTokens(resolve(selection)),
474
- };
475
- }
476
- export function buildEquivalentConfigCommandTokens(options) {
477
- return [
478
- 'aw',
479
- 'config',
480
- options.command,
481
- ...optionTokens('--scope', options.scope),
482
- ...optionTokens('--value-json', options.valueJson),
483
- ...optionTokens('--preset', options.preset),
484
- ...(options.confirmed ? ['--yes'] : []),
485
- '--',
486
- options.plugin,
487
- ...(options.key === undefined ? [] : [options.key]),
488
- ...(options.value === undefined ? [] : [options.value]),
489
- ];
490
- }
491
- function hasSelectableWriteCandidate(command, candidates, selection, options) {
492
- const platforms = options.missingPlatform
493
- ? ['codex', 'claude', 'both']
494
- : [selection.platform ?? 'both'];
495
- const scopes = options.missingScope ? ['user', 'project'] : [selection.scope ?? 'user'];
496
- return candidates.some((candidate) => platforms.some((platform) => scopes.some((scope) => writeCandidateEligibility(command, candidate, { ...selection, platform, scope }).selectable)));
497
- }
498
- function hasConfigSchemaKeys(candidate) {
499
- return candidate.configSchema !== undefined && Object.keys(candidate.configSchema).length > 0;
500
- }
501
- async function loadWizardCandidates(context, rawOptions) {
502
- const loaded = await loadConsoleItemsResult({
503
- cwd: context.cwd,
504
- scope: rawOptions.scope,
505
- platform: rawOptions.platform,
506
- }, context);
507
- return toWizardCandidates(loaded.items);
508
- }
509
- function mergeWizardCandidates(current, stored) {
510
- const merged = new Map(current.map((candidate) => [candidate.pluginName, candidate]));
511
- for (const candidate of stored) {
512
- const existing = merged.get(candidate.pluginName);
513
- if (!existing) {
514
- merged.set(candidate.pluginName, candidate);
515
- }
516
- else if (!existing.configSchema) {
517
- merged.set(candidate.pluginName, { ...candidate, ...existing });
518
- }
519
- }
520
- return [...merged.values()];
521
- }
522
- async function loadStoredConfigWizardCandidates(context, locations, options = {}) {
523
- const legacy = await listPluginConfigStorePluginNames({ cwd: context.cwd, env: context.env });
524
- if (legacy.length > 0) {
525
- throw new Error('legacyStateUnsupported: legacy bare-name config stores are not supported.');
526
- }
527
- const storesByPlugin = new Map();
528
- for (const location of locations) {
529
- const stores = await listNamespacedPluginConfigStores({
530
- cwd: context.cwd,
531
- env: context.env,
532
- scope: location.scope,
533
- });
534
- for (const store of stores) {
535
- const keys = Object.keys(store.values);
536
- if (keys.length === 0 && options.includeEmptyStores !== true)
537
- continue;
538
- const existing = storesByPlugin.get(store.pluginId) ?? {
539
- sourceId: store.sourceId,
540
- ...(store.binding.kind === 'source'
541
- ? { sourceIdentity: store.binding.sourceIdentity }
542
- : {}),
543
- keys: new Set(),
544
- };
545
- for (const key of keys)
546
- existing.keys.add(key);
547
- storesByPlugin.set(store.pluginId, existing);
548
- }
549
- }
550
- return [...storesByPlugin]
551
- .sort(([left], [right]) => left.localeCompare(right))
552
- .map(([pluginId, store]) => ({
553
- pluginName: pluginId,
554
- pluginId,
555
- sourceId: store.sourceId,
556
- ...(store.sourceIdentity ? { sourceIdentity: store.sourceIdentity } : {}),
557
- targets: [],
558
- configKeys: [...store.keys].sort((left, right) => left.localeCompare(right)),
559
- }));
560
- }
561
- function configReadStoreLocations(rawOptions) {
562
- if (rawOptions.scope === 'user')
563
- return [{ scope: 'user' }];
564
- if (rawOptions.scope === 'project')
565
- return [{ scope: 'project' }];
566
- return [
567
- { scope: 'user' },
568
- { scope: 'project' },
569
- ];
570
- }
571
- function overallResultState(value) {
572
- return value.overallState === 'succeeded' ? 'succeeded' : 'failed';
573
- }
574
357
  async function resolveStoredConfigResetIdentity(options) {
575
358
  const parsed = parsePluginId(options.selector);
576
359
  if (parsed.sourceId) {
@@ -596,27 +379,21 @@ async function resolveStoredConfigResetIdentity(options) {
596
379
  pluginName: match.pluginName,
597
380
  pluginId: match.pluginId,
598
381
  sourceId: match.sourceId,
599
- ...(match.binding.kind === 'source'
600
- ? { sourceIdentity: match.binding.sourceIdentity }
601
- : { logicalIdentity: match.binding.logicalIdentity }),
382
+ sourceIdentity: match.binding.sourceIdentity,
602
383
  };
603
384
  }
604
385
  async function resolveStoredOnlyConfigIdentity(options) {
605
386
  const parsed = parsePluginId(options.selector);
606
387
  if (!parsed.sourceId)
607
388
  throw new Error(`Plugin "${options.selector}" is not a qualified stored config target.`);
608
- const stores = (await Promise.all(options.locations.map((location) => listNamespacedPluginConfigStores({
609
- cwd: options.context.cwd,
610
- env: options.context.env,
611
- scope: location.scope,
612
- })))).flat().filter((store) => store.pluginId === options.selector);
389
+ const stores = (await listNamespacedPluginConfigStores({
390
+ cwd: options.context.cwd, env: options.context.env, scope: 'user',
391
+ })).filter((store) => store.pluginId === options.selector);
613
392
  if (stores.length === 0)
614
393
  throw new Error(`Plugin "${options.selector}" does not have a namespaced config store.`);
615
394
  const sourceBindings = stores.flatMap((store) => store.binding.kind === 'source'
616
395
  ? [store.binding.sourceIdentity] : []);
617
- const catalogBindings = stores.flatMap((store) => store.binding.kind === 'catalog'
618
- ? [store.binding.logicalIdentity] : []);
619
- if (new Set(sourceBindings).size > 1 || new Set(catalogBindings).size > 1) {
396
+ if (new Set(sourceBindings).size > 1) {
620
397
  throw new Error(`identityMismatch: ${options.selector} config stores have different bindings.`);
621
398
  }
622
399
  const currentSource = await withResolvedMultiSourceCatalog({
@@ -637,9 +414,6 @@ async function resolveStoredOnlyConfigIdentity(options) {
637
414
  if (sourceBindings[0] && sourceBindings[0] !== currentSource.sourceIdentity) {
638
415
  throw new Error(`identityMismatch: ${options.selector} user config does not match the current source identity.`);
639
416
  }
640
- if (catalogBindings[0] && catalogBindings[0] !== currentSource.logicalIdentity) {
641
- throw new Error(`identityMismatch: ${options.selector} project config does not match the current Catalog logical identity.`);
642
- }
643
417
  return {
644
418
  pluginName: parsed.pluginName,
645
419
  pluginId: options.selector,
@@ -652,6 +426,7 @@ async function resolveMultiSourceConfigRead(options) {
652
426
  const inventory = await inventoryLegacyConfigReferences({
653
427
  cwd: options.context.cwd,
654
428
  env: options.context.env,
429
+ scope: options.rawOptions.context === 'project' ? 'project' : 'user',
655
430
  });
656
431
  assertCatalogSourceReferencesSupported(inventory);
657
432
  return withResolvedCatalogPlugin({
@@ -662,13 +437,14 @@ async function resolveMultiSourceConfigRead(options) {
662
437
  const result = await resolveConfigRead({
663
438
  pluginName: plugin.manifest.name,
664
439
  plugin,
665
- scope: resolveConfigReadResolution(options.rawOptions.scope),
440
+ scope: options.rawOptions.context === 'project' ? 'project' : 'user',
441
+ platform: options.rawOptions.platform,
666
442
  cwd: options.context.cwd,
667
443
  env: options.context.env,
668
444
  });
669
445
  if (options.key !== undefined) {
670
446
  const field = findConfigFieldEntry(result.fields, options.key);
671
- if (!field && plugin.manifest.config?.fields?.[options.key] === undefined) {
447
+ if (!field && compilePluginConfigDefinition(plugin.manifest.config, options.rawOptions.platform).fields[options.key] === undefined) {
672
448
  throw new Error(`Unknown config key "${options.key}".`);
673
449
  }
674
450
  if (field)
@@ -689,7 +465,6 @@ async function resolveMultiSourceConfigRead(options) {
689
465
  const stored = await resolveStoredOnlyConfigIdentity({
690
466
  selector: options.selector,
691
467
  context: options.context,
692
- locations: configReadStoreLocations(options.rawOptions),
693
468
  });
694
469
  const result = await resolveConfigRead({
695
470
  sourceId: stored.sourceId,
@@ -697,7 +472,8 @@ async function resolveMultiSourceConfigRead(options) {
697
472
  logicalIdentity: stored.logicalIdentity,
698
473
  pluginName: stored.pluginName,
699
474
  plugin: null,
700
- scope: resolveConfigReadResolution(options.rawOptions.scope),
475
+ scope: options.rawOptions.context === 'project' ? 'project' : 'user',
476
+ platform: options.rawOptions.platform,
701
477
  cwd: options.context.cwd,
702
478
  env: options.context.env,
703
479
  });
@@ -710,6 +486,46 @@ async function resolveMultiSourceConfigRead(options) {
710
486
  return { ...stored, result };
711
487
  });
712
488
  }
489
+ function renderRecoveryReceipt(write, value) {
490
+ writeLine(write, `${value.command} [${value.scope}]: ${value.ok ? 'ok' : 'failed'}`);
491
+ writeLine(write, `需求:${value.intent.roots?.join('、') || (value.intent.roots === null ? '不可读' : '无')}`);
492
+ writeLine(write, `本机工具:${value.selection.agents.join('、') || '未配置'}`);
493
+ writeLine(write, `变化:声明 ${value.declarationChanged ? '已保存' : '无'};偏好 ${value.preferencesChanged ? '已保存' : '无'};配置 ${value.configChanged ? '已保存' : '无'};输出 ${value.outputsMayHaveChanged ? '可能已变更' : '无'}`);
494
+ for (const target of value.targetResults) {
495
+ writeLine(write, `${target.pluginId} / ${target.agent} [${target.scope}]: ${target.execution}; ${target.content ?? target.observation}`);
496
+ }
497
+ for (const result of value.commandResults ?? []) {
498
+ const producer = result.producer;
499
+ writeLine(write, `托管命令 ${producer.sourceId}/${producer.pluginName}/${producer.key} [${producer.scope}]: ${result.state}`);
500
+ }
501
+ for (const change of value.nativeToolChanges ?? []) {
502
+ for (const phase of ['before', 'after']) {
503
+ const closure = change[phase];
504
+ if (closure)
505
+ for (const description of describeNativeToolClosure(closure)) {
506
+ writeLine(write, `原生工具 ${change.producer.sourceId}/${change.producer.pluginName} ${phase}: ${description}`);
507
+ }
508
+ }
509
+ }
510
+ const diagnostics = new Map([...value.blockers, ...value.errors,
511
+ ...value.targetResults.flatMap((target) => target.blockers),
512
+ ...(value.commandResults ?? []).flatMap((result) => result.errors),
513
+ ].map((item) => [`${item.code}:${item.message}`, item]));
514
+ for (const item of diagnostics.values())
515
+ writeLine(write, `${item.code}: ${item.message}`);
516
+ for (const action of value.recoveryActions)
517
+ writeLine(write, action);
518
+ }
519
+ function renderRecoveryStatus(write, value) {
520
+ writeLine(write, `status [${value.scope}]: ${value.ok ? 'ok' : 'failed'}`);
521
+ writeLine(write, `本机工具:${value.selection.agents.join('、') || '未配置'}`);
522
+ writeLine(write, `全部满足:${value.allSatisfied === null ? '未知' : value.allSatisfied ? '是' : '否'}`);
523
+ for (const root of value.inventory ?? []) {
524
+ writeLine(write, `${root.pluginId}: ${root.targetResults.length ? root.targetResults.map((target) => `${target.agent}/${target.content ?? target.observation}`).join(', ') : 'localTargetsRequired'}`);
525
+ }
526
+ for (const error of value.errors)
527
+ writeLine(write, `${error.code}: ${error.message}`);
528
+ }
713
529
  export function createManagedCodexAuthorizer(rawOptions, context) {
714
530
  if (rawOptions.allowManagedCodexInstall === true)
715
531
  return async () => true;
@@ -724,152 +540,14 @@ export function createManagedCodexAuthorizer(rawOptions, context) {
724
540
  return !response.cancelled && response.value === true;
725
541
  };
726
542
  }
727
- async function executeWriteCommand(command, pluginNames, rawOptions, context, directCli = false, expectedPlanFingerprint) {
728
- const raw = { cwd: context.cwd, ...rawOptions };
729
- const commandOptions = normalizeWriteRequest(() => command === 'install'
730
- ? normalizeInstallCommandOptions(raw)
731
- : command === 'uninstall'
732
- ? normalizeUninstallCommandOptions(raw)
733
- : command === 'cleanup'
734
- ? normalizeCleanupCommandOptions(raw)
735
- : normalizeReinstallCommandOptions(raw));
736
- await emitCommandEvent(context, {
737
- type: 'options:normalized',
738
- data: { plugins: pluginNames, options: commandOptions },
739
- });
543
+ function recoveryRuntime(context, rawOptions, directCli) {
740
544
  const managedCodexAuthorizer = createManagedCodexAuthorizer(rawOptions, context);
741
- const runtime = {
545
+ return {
742
546
  ...context,
743
547
  directCli,
744
548
  preApplyConfirmed: rawOptions.yes === true,
745
- ...(expectedPlanFingerprint ? { expectedPlanFingerprint } : {}),
746
549
  ...(managedCodexAuthorizer ? { managedCodexAuthorizer } : {}),
747
550
  };
748
- switch (command) {
749
- case 'install': return installCommand(pluginNames, commandOptions, runtime);
750
- case 'uninstall': return uninstallCommand(pluginNames, commandOptions, runtime);
751
- case 'cleanup': return cleanupCommand(pluginNames, commandOptions, runtime);
752
- case 'reinstall': return reinstallCommand(pluginNames, commandOptions, runtime);
753
- }
754
- }
755
- async function runWriteCommandAction(command, pluginNames, rawOptions, context) {
756
- if ((command === 'cleanup' || command === 'reinstall') && pluginNames.length > 1) {
757
- throw new WriteCommandRequestError(`${command} 命令当前只支持单个插件目标。`);
758
- }
759
- const missingPlugin = pluginNames.length === 0;
760
- const missingPlatform = rawOptions.platform === undefined;
761
- const missingScope = command === 'cleanup' && rawOptions.scope === undefined;
762
- const missing = missingPlugin || missingPlatform || missingScope;
763
- // Validate every value already supplied by the caller before opening the TUI.
764
- const validationOptions = {
765
- cwd: context.cwd,
766
- ...rawOptions,
767
- platform: rawOptions.platform ?? 'both',
768
- ...(command === 'cleanup' ? { scope: rawOptions.scope ?? 'user' } : {}),
769
- };
770
- void validationOptions;
771
- if (!missing) {
772
- if (command === 'cleanup' && rawOptions.force !== true && rawOptions.yes !== true
773
- && isWizardAvailable(context, rawOptions) && context.prompt.confirm) {
774
- const preview = await cleanupCommand(pluginNames, normalizeCleanupCommandOptions({
775
- cwd: context.cwd,
776
- ...rawOptions,
777
- }), { ...context, previewOperationPlan: true });
778
- if ((preview.removedPluginCleanupIds?.length ?? 0) > 0) {
779
- if (!preview.operationPlan) {
780
- throw new WriteCommandRequestError('cleanupPlanUnavailable: 无法建立可信的整体清理预览。');
781
- }
782
- const response = await context.prompt.confirm({
783
- message: `清理已删除插件 ${preview.removedPluginCleanupIds.join('、')}:将处理 Codex、Claude Code 的用户与当前项目位置,删除保存设置、安装意图、专属输出和共享原生安装。其他项目可能因此无法继续使用该插件;其本地记录不会修改。即使指定了 --platform 或 --scope,本次仍覆盖上述全部位置。继续?`,
784
- defaultValue: false,
785
- });
786
- if (response.cancelled || response.value !== true)
787
- return { cancelled: true };
788
- return {
789
- cancelled: false,
790
- result: await executeWriteCommand(command, pluginNames, {
791
- ...rawOptions, yes: true,
792
- }, context, true, preview.operationPlan.planFingerprint),
793
- };
794
- }
795
- }
796
- return {
797
- cancelled: false,
798
- result: await executeWriteCommand(command, pluginNames, rawOptions, context, true),
799
- };
800
- }
801
- if (!isWizardAvailable(context, rawOptions)) {
802
- throw new WriteCommandRequestError(writeCommandUsage(command));
803
- }
804
- const loaded = await loadConsoleItemsResult({
805
- cwd: context.cwd,
806
- scope: rawOptions.scope,
807
- platform: rawOptions.platform,
808
- }, context);
809
- if (loaded.catalogUnavailable) {
810
- throw new Error('插件目录不可用。请运行 `aw init` 或 `aw init --force`,或使用完整参数重试。');
811
- }
812
- const candidates = missingPlugin ? toWizardCandidates(loaded.items) : [];
813
- const initialSelection = {
814
- plugins: pluginNames,
815
- platform: rawOptions.platform,
816
- scope: (rawOptions.scope ?? 'user'),
817
- };
818
- if (missingPlugin && !hasSelectableWriteCandidate(command, candidates, initialSelection, {
819
- missingPlatform,
820
- missingScope,
821
- })) {
822
- throw new Error('没有可执行此操作的目标。请运行 `aw status` 检查当前状态。');
823
- }
824
- const result = await runCommandWizard(context, {
825
- command,
826
- candidates,
827
- initial: initialSelection,
828
- pluginMode: missingPlugin
829
- ? command === 'cleanup' || command === 'reinstall' ? 'single' : 'multiple'
830
- : 'none',
831
- promptForPlatform: missingPlatform,
832
- promptForScope: missingScope,
833
- destructive: command === 'uninstall' || command === 'cleanup',
834
- equivalentCommand: (selection) => equivalentWriteCommand(command, selection, rawOptions),
835
- equivalentCommandTokens: (selection) => buildEquivalentWriteCommandTokens(command, selection, rawOptions),
836
- isPluginSelectable: (candidate, selection) => writeCandidateEligibility(command, candidate, selection),
837
- resultState: overallResultState,
838
- ...(command === 'cleanup' ? {
839
- reviewDetails: async (selection) => {
840
- const preview = await cleanupCommand(selection.plugins, normalizeCleanupCommandOptions({
841
- cwd: context.cwd,
842
- ...rawOptions,
843
- platform: selection.platform,
844
- scope: selection.scope,
845
- }), { ...context, previewOperationPlan: true });
846
- if ((preview.removedPluginCleanupIds?.length ?? 0) === 0)
847
- return { lines: [] };
848
- if (!preview.operationPlan) {
849
- throw new WriteCommandRequestError('cleanupPlanUnavailable: 无法建立可信的整体清理预览。');
850
- }
851
- return {
852
- lines: [
853
- `已删除插件:${preview.removedPluginCleanupIds.join('、')}`,
854
- '实际清理范围:Codex、Claude Code;用户与当前项目。',
855
- '将删除保存设置、安装意图、专属输出和共享原生安装。',
856
- '其他项目可能失去此插件;其本地记录不会修改。',
857
- '当前 --platform / --scope 选择不会限制整体清理范围。',
858
- ],
859
- expectedPlanFingerprint: preview.operationPlan.planFingerprint,
860
- };
861
- },
862
- } : {}),
863
- execute: (selection, expectedPlanFingerprint) => executeWriteCommand(command, selection.plugins, {
864
- ...rawOptions,
865
- platform: selection.platform,
866
- scope: selection.scope,
867
- ...(command === 'cleanup' ? { yes: true } : {}),
868
- }, context, false, expectedPlanFingerprint),
869
- });
870
- if (result.cancelled)
871
- return { cancelled: true };
872
- return { cancelled: false, result: result.value };
873
551
  }
874
552
  function parseBareConsoleOptions(argv) {
875
553
  if (argv.length === 0) {
@@ -976,12 +654,6 @@ async function runActionWithContext(context, action) {
976
654
  function writeJsonReceipt(write, payload) {
977
655
  writeLine(write, JSON.stringify(payload, null, 2));
978
656
  }
979
- function configLayerLabel(scope) {
980
- if (scope === 'user') {
981
- return '个人';
982
- }
983
- return '当前项目';
984
- }
985
657
  function writeConfigApplyNotice(write, receipt) {
986
658
  const sanitizeMessage = (message) => redactString(message)
987
659
  .replace(/https?:\/\/\S+/giu, '[REDACTED URL]')
@@ -995,9 +667,9 @@ function writeConfigApplyNotice(write, receipt) {
995
667
  : entry.errorCode
996
668
  ? `失败原因:${entry.errorCode}。`
997
669
  : '未提供安全失败原因。';
998
- const command = `aw reinstall ${receipt.plugin} -p ${entry.target.platform} -s ${entry.target.scope}`;
670
+ const command = `aw plugin sync ${receipt.plugin} --rebuild --target ${entry.target.platform} --context ${entry.target.scope}`;
999
671
  const recovery = entry.errorCode === 'invalidConfig'
1000
- ? ' 请修复无效配置后运行 aw sync。'
672
+ ? ` 请修复无效配置后运行 ${command}。`
1001
673
  : entry.errorCode === 'preApplyAuthorizationRequired'
1002
674
  || entry.errorCode === 'sharedNativeConfigApplyConfirmationRequired'
1003
675
  || entry.errorCode === 'sharedNativeImpactAuthorizationRequired'
@@ -1011,9 +683,9 @@ function writeConfigApplyNotice(write, receipt) {
1011
683
  const targetResults = receipt.apply.targetResults;
1012
684
  if (receipt.apply.state === 'skipped') {
1013
685
  const message = receipt.apply.reason === 'noConfigChange'
1014
- ? '配置内容没有变化,因此没有运行 reinstall。'
686
+ ? '配置内容没有变化,因此没有刷新输出。'
1015
687
  : receipt.apply.reason === 'noEffectiveConfigChange'
1016
- ? '配置存储已更新,但有效配置没有变化,因此没有运行 reinstall。'
688
+ ? '配置存储已更新,但有效配置没有变化,因此没有刷新输出。'
1017
689
  : receipt.apply.reason === 'notRequested'
1018
690
  ? '配置已保存,但未请求应用配置。'
1019
691
  : '配置已保存。当前没有可见的 AW 安装目标,因此没有应用配置。';
@@ -1031,12 +703,12 @@ function writeConfigApplyNotice(write, receipt) {
1031
703
  ? `${receipt.apply.error.code}:${sanitizeMessage(receipt.apply.error.message)}`
1032
704
  : `${receipt.apply.reason}:${fallbackMessages[receipt.apply.reason]}`;
1033
705
  const recovery = receipt.apply.reason === 'invalidConfig'
1034
- ? '请修复无效配置后运行 aw sync。'
706
+ ? '请修复无效配置,再运行 aw plugin sync <source/plugin>。'
1035
707
  : receipt.apply.reason === 'effectiveConfigUnavailable'
1036
- ? '请修复配置后运行 aw sync。'
708
+ ? '请修复配置,再运行 aw plugin sync <source/plugin>。'
1037
709
  : receipt.apply.reason === 'schemaUnavailable' || receipt.apply.reason === 'sourceUnavailable'
1038
- ? '请修复插件来源或配置后运行 aw sync。'
1039
- : '请修复来源或配置后运行 aw sync。';
710
+ ? '请修复插件来源或配置,再运行 aw plugin sync <source/plugin>。'
711
+ : '请修复来源或配置,再运行 aw plugin sync <source/plugin>。';
1040
712
  writeLine(write, `配置已保存,但当前无法应用配置。${detail} ${recovery}`);
1041
713
  writeTargetFailures();
1042
714
  }
@@ -1044,7 +716,7 @@ function writeConfigApplyNotice(write, receipt) {
1044
716
  const completed = targetResults.filter((entry) => entry.state === 'completed').length;
1045
717
  const failed = targetResults.filter((entry) => entry.state === 'failed').length;
1046
718
  const skipped = targetResults.filter((entry) => entry.state === 'skipped').length;
1047
- writeLine(write, `配置已保存。已重装 ${completed} 个当前可见目标;失败 ${failed} 个;跳过 ${skipped} 个。`);
719
+ writeLine(write, `配置已保存。已刷新 ${completed} 个当前可见目标;失败 ${failed} 个;跳过 ${skipped} 个。`);
1048
720
  writeTargetFailures();
1049
721
  }
1050
722
  writeWarnings(write, receipt.warnings);
@@ -1066,16 +738,14 @@ function writeConfigReadHuman(write, resolved, pluginName) {
1066
738
  writeLine(write, ` stored=${field.storedSourceLayer} ${renderPluginConfigValueForDisplay(field.storedValue)}`);
1067
739
  }
1068
740
  if (field.kind === 'manifest' && field.valueState === 'invalid') {
1069
- const sourceScope = field.sourceLayer === 'project'
1070
- ? 'project'
1071
- : 'user';
741
+ const context = resolved.context ?? 'user';
1072
742
  const definition = resolved.fieldDefinitions?.[field.key];
1073
743
  if (definition) {
1074
- writeLine(write, ` ${formatPluginConfigSetRecoveryCommand(pluginName, field.key, definition, sourceScope)}`);
744
+ writeLine(write, ` ${formatPluginConfigSetRecoveryCommand(pluginName, field.key, definition, context, resolved.platform)}`);
1075
745
  }
1076
- const scopeOption = ` -s ${sourceScope}`;
1077
- writeLine(write, ` aw config unset ${pluginName} ${field.key}${scopeOption}`);
1078
- writeLine(write, ` aw config reset ${pluginName}${scopeOption}`);
746
+ const profileOptions = ` --context ${context}${resolved.platform ? ` --platform ${resolved.platform}` : ''}`;
747
+ writeLine(write, ` aw plugin config unset ${pluginName} ${field.key}${profileOptions}`);
748
+ writeLine(write, ` aw plugin config reset ${pluginName}${profileOptions} --yes`);
1079
749
  }
1080
750
  }
1081
751
  }
@@ -1108,7 +778,7 @@ export function buildProgram(options = {}) {
1108
778
  });
1109
779
  const plugin = program
1110
780
  .command('plugin')
1111
- .description('Create and validate AW plugin source');
781
+ .description('Manage plugin demand, outputs, configuration, and author sources');
1112
782
  registerManagedCommandCli(program, () => createActionContext('command', {}), {
1113
783
  cliEntry: fileURLToPath(import.meta.url),
1114
784
  cliVersion: resolveCliVersion(),
@@ -1296,7 +966,7 @@ export function buildProgram(options = {}) {
1296
966
  .argument('<target>', 'Catalog Workspace, Plugin, or source Skill path')
1297
967
  .addOption(strictValueOption('--workspace-root <root>', 'explicit Catalog Workspace root'))
1298
968
  .option('--skill <name>', 'select one declared Skill', collectUniqueOption, [])
1299
- .addOption(strictValueOption('--platform <platform>', 'select codex, claude, or agent-skills', ['codex', 'claude', 'agent-skills']))
969
+ .addOption(strictValueOption('--platform <platform>', 'select a canonical platform target', PLATFORM_TARGETS))
1300
970
  .addOption(strictValueOption('--scope <scope>', 'select user or project scope', ['user', 'project']))
1301
971
  .addOption(strictValueOption('--config <file>', 'review config JSON file'))
1302
972
  .option('--materialization <receipt>', 'validate one aw-source-binding receipt', collectUniqueOption, [])
@@ -1340,7 +1010,7 @@ export function buildProgram(options = {}) {
1340
1010
  .argument('<target>', 'Plugin or source Skill path')
1341
1011
  .addOption(strictValueOption('--workspace-root <root>', 'explicit Catalog Workspace root'))
1342
1012
  .option('--skill <name>', 'select one declared Skill', collectUniqueOption, [])
1343
- .addOption(strictValueOption('--platform <platform>', 'select codex, claude, or agent-skills', ['codex', 'claude', 'agent-skills']).makeOptionMandatory())
1013
+ .addOption(strictValueOption('--platform <platform>', 'select a canonical platform target', PLATFORM_TARGETS).makeOptionMandatory())
1344
1014
  .addOption(strictValueOption('--scope <scope>', 'select user or project scope', ['user', 'project']).makeOptionMandatory())
1345
1015
  .addOption(strictValueOption('--config <file>', 'review config JSON file'))
1346
1016
  .addOption(strictValueOption('--out <new-dir>', 'new output directory').makeOptionMandatory())
@@ -1365,117 +1035,12 @@ export function buildProgram(options = {}) {
1365
1035
  renderReviewPreparation(io.stdout, result.receipt);
1366
1036
  commandExitState.exitCode = result.exitCode;
1367
1037
  });
1368
- skill
1369
- .command('install')
1370
- .description('Install portable Skills through the skills CLI')
1371
- .argument('<plugin>', 'plugin or source/plugin selector')
1372
- .requiredOption('-s, --scope <scope>', 'project or user')
1373
- .option('--skill <name...>', 'install only selected Skill names')
1374
- .option('--agent <id...>', 'install to explicit Agent IDs')
1375
- .option('-y, --yes', 'confirm the reviewed install', false)
1376
- .option('-j, --json', 'emit one receipt v5 JSON object', false)
1377
- .action(async (selector, rawOptions) => {
1378
- const context = createActionContext('skill:install', {});
1379
- const writeFailure = (error) => {
1380
- const capabilityError = error instanceof AwCapabilityUnsupportedError ? error : undefined;
1381
- const code = capabilityError?.code
1382
- ?? (error instanceof PortableCommandError || error instanceof SkillsCliError
1383
- ? error.code
1384
- : 'portableInstallFailed');
1385
- writeJsonReceipt(io.stdout, {
1386
- receiptSchemaVersion: 5,
1387
- command: 'skill:install',
1388
- ok: false,
1389
- status: 'failed',
1390
- plugin: { id: selector },
1391
- scope: rawOptions.scope,
1392
- selectedSkills: [],
1393
- ...(rawOptions.skill ? { requestedSkills: rawOptions.skill } : {}),
1394
- agentMode: rawOptions.agent ? 'explicit' : 'automatic',
1395
- requestedAgents: rawOptions.agent ?? [],
1396
- skillsCliVersion: null,
1397
- collisions: [],
1398
- dependencies: [],
1399
- materializationDigest: null,
1400
- verification: { records: [], missing: [] },
1401
- warnings: [],
1402
- failures: [{
1403
- code,
1404
- message: capabilityError?.message ?? `${code}: portable Skill 安装未执行。`,
1405
- ...(capabilityError ? {
1406
- plugin: capabilityError.plugin,
1407
- missingCapabilities: capabilityError.missingCapabilities,
1408
- availableCapabilities: capabilityError.availableCapabilities,
1409
- } : {}),
1410
- }],
1411
- });
1412
- };
1413
- if (rawOptions.json && !rawOptions.yes) {
1414
- writeFailure(new PortableCommandError('confirmationRequired', '写入型 --json 必须同时使用 --yes。'));
1415
- commandExitState.exitCode = 1;
1416
- return;
1417
- }
1418
- try {
1419
- const receipt = await runActionWithContext(context, () => installPortableSkills({
1420
- selector,
1421
- scope: rawOptions.scope,
1422
- ...(rawOptions.skill ? { requestedSkills: rawOptions.skill } : {}),
1423
- ...(rawOptions.agent ? { requestedAgents: rawOptions.agent } : {}),
1424
- yes: rawOptions.yes,
1425
- cwd: context.cwd,
1426
- cliVersion: resolveCliVersion(),
1427
- env: context.env,
1428
- ...(context.interactive && context.prompt.confirm ? {
1429
- confirm: async () => context.prompt.confirm({
1430
- message: '确认按以上 Review 安装 portable Skills?',
1431
- defaultValue: false,
1432
- }),
1433
- } : {}),
1434
- ...(!rawOptions.json ? {
1435
- onReview: (review) => {
1436
- for (const line of renderPortableReview(review))
1437
- writeLine(io.stdout, line);
1438
- },
1439
- } : {}),
1440
- }));
1441
- if (rawOptions.json)
1442
- writeJsonReceipt(io.stdout, receipt);
1443
- else if (receipt.ok)
1444
- writeLine(io.stdout, `portable Skill 安装完成:${receipt.selectedSkills.length} 项。`);
1445
- else {
1446
- const sanitize = (value) => redactString(value)
1447
- .replace(/https?:\/\/\S+/giu, '[REDACTED URL]')
1448
- .replace(/file:\/\/\S+/giu, '[REDACTED PATH]')
1449
- .replace(/(?:file:\/\/)?(?:\/[\w.@+-]+){2,}/gu, '[REDACTED PATH]')
1450
- .replace(/[A-Za-z]:\\(?:[^\s\\]+\\)+[^\s\\]*/gu, '[REDACTED PATH]');
1451
- const lines = new Set();
1452
- for (const failure of receipt.failures) {
1453
- lines.add(`${failure.code}: ${sanitize(failure.message)}`);
1454
- }
1455
- for (const warning of receipt.warnings)
1456
- lines.add(`警告:${sanitize(warning)}`);
1457
- const recoveries = receipt.failures.map((failure) => failure.recovery).filter(Boolean);
1458
- const recovery = recoveries.find((entry) => entry.includes('aw skill install')
1459
- && entry.includes('skills remove'))
1460
- ?? '重新运行 aw skill install;如需清理外部写入,请使用 skills remove。';
1461
- lines.add(`恢复:${sanitize(recovery)}`);
1462
- for (const line of lines)
1463
- writeLine(io.stderr, line);
1464
- }
1465
- commandExitState.exitCode = receipt.ok ? 0 : 1;
1466
- }
1467
- catch (error) {
1468
- if (!rawOptions.json)
1469
- throw error;
1470
- writeFailure(error);
1471
- commandExitState.exitCode = 1;
1472
- }
1473
- });
1474
1038
  skill
1475
1039
  .command('materialize')
1476
1040
  .description('Write portable Skills to a safe output directory')
1477
1041
  .argument('<plugin>', 'plugin or source/plugin selector')
1478
- .requiredOption('-s, --scope <scope>', 'project or user')
1042
+ .addOption(strictValueOption('--platform <target>', 'Portable platform target', PORTABLE_PLATFORM_TARGETS).makeOptionMandatory())
1043
+ .addOption(strictValueOption('-s, --scope <scope>', 'artifact scope', ['user', 'project']).makeOptionMandatory())
1479
1044
  .requiredOption('--out <dir>', 'output directory')
1480
1045
  .option('--skill <name...>', 'write only selected Skill names')
1481
1046
  .option('-j, --json', 'emit one receipt v5 JSON object', false)
@@ -1484,6 +1049,7 @@ export function buildProgram(options = {}) {
1484
1049
  try {
1485
1050
  const receipt = await runActionWithContext(context, () => materializePortableSkillsToDirectory({
1486
1051
  selector,
1052
+ platform: rawOptions.platform,
1487
1053
  scope: rawOptions.scope,
1488
1054
  out: rawOptions.out,
1489
1055
  ...(rawOptions.skill ? { requestedSkills: rawOptions.skill } : {}),
@@ -1501,7 +1067,7 @@ export function buildProgram(options = {}) {
1501
1067
  throw error;
1502
1068
  const capabilityError = error instanceof AwCapabilityUnsupportedError ? error : undefined;
1503
1069
  const code = capabilityError?.code
1504
- ?? (error instanceof PortableCommandError || error instanceof SkillsCliError
1070
+ ?? (error instanceof PortableCommandError
1505
1071
  ? error.code
1506
1072
  : 'portableMaterializeFailed');
1507
1073
  writeJsonReceipt(io.stdout, {
@@ -1510,6 +1076,7 @@ export function buildProgram(options = {}) {
1510
1076
  ok: false,
1511
1077
  plugin: { id: selector },
1512
1078
  scope: rawOptions.scope,
1079
+ platform: rawOptions.platform,
1513
1080
  out: path.resolve(context.cwd, rawOptions.out),
1514
1081
  selectedSkills: [],
1515
1082
  ...(rawOptions.skill ? { requestedSkills: rawOptions.skill } : {}),
@@ -1571,13 +1138,6 @@ export function buildProgram(options = {}) {
1571
1138
  .option('--skill-short-description <text>', 'Skill UI summary (25-64 characters)')
1572
1139
  .option('--skill-invocation <mode>', 'model | manual | claude-model-only (default: model)')
1573
1140
  .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)');
1574
- plugin
1575
- .command('remove')
1576
- .description('Remove one committed and unchanged canonical plugin source tree')
1577
- .argument('<plugin-name>', 'existing lowercase hyphen-case plugin name')
1578
- .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1579
- .option('--yes', 'confirm plugin source deletion', false)
1580
- .option('--json', 'emit one schema 1 JSON object', false);
1581
1141
  plugin
1582
1142
  .command('validate')
1583
1143
  .description('Validate one source plugin directory')
@@ -1651,14 +1211,6 @@ export function buildProgram(options = {}) {
1651
1211
  commandExitState.exitCode = 1;
1652
1212
  }
1653
1213
  });
1654
- plugin
1655
- .command('status')
1656
- .description('Show current plugin status or compare against an explicit Git base')
1657
- .argument('[plugin-name]', 'one explicit plugin name')
1658
- .option('--all', 'check every current Catalog Workspace plugin', false)
1659
- .option('--base <git-ref>', 'explicit Git comparison base')
1660
- .option('--json', 'emit one schema v3 JSON document', false)
1661
- .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)');
1662
1214
  skill
1663
1215
  .command('validate')
1664
1216
  .description('Validate one Agent Skill directory')
@@ -2035,12 +1587,10 @@ export function buildProgram(options = {}) {
2035
1587
  ? failure.message
2036
1588
  : redactString(sanitizeCatalogSourceErrorMessage(error instanceof Error ? error.message : String(error), env, [cwd]));
2037
1589
  writeLine(io.stderr, message);
2038
- commandExitState.exitCode = 1;
1590
+ commandExitState.exitCode = failure.code === 'invalidArguments' ? 2 : 1;
2039
1591
  };
2040
- const withResolvedConfigMutationPlugin = (context, selector, consume, apply = false) => (async () => {
1592
+ const withResolvedConfigMutationPlugin = (context, selector, consume, _apply = false) => (async () => {
2041
1593
  const parsed = parsePluginId(selector);
2042
- if (apply)
2043
- await prepareNativeCliExecution({ env: context.env, runner: context.nativeCliRunner });
2044
1594
  return withAwHomeMutationLock({
2045
1595
  env: context.env,
2046
1596
  action: async (awHomeMutationToken) => {
@@ -2449,777 +1999,409 @@ export function buildProgram(options = {}) {
2449
1999
  }
2450
2000
  commandExitState.exitCode = result.ok ? 0 : 1;
2451
2001
  });
2452
- program
2453
- .command('sync')
2454
- .description('Reconcile actual state from existing desired intent')
2455
- .argument('[plugins...]')
2456
- .requiredOption('-s, --scope <scope>', 'project or user')
2457
- .requiredOption('-p, --platform <platform>', 'codex, claude, or both')
2458
- .option('-a, --all', 'reconcile every desired plugin in the selected scope', false)
2459
- .option('--refresh', 'refresh only Catalog sources required by selected intent roots', false)
2460
- .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2461
- .option('-y, --yes', 'authorize preApply and shared native replacement or forced repair', false)
2462
- .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2463
- .option('-j, --json', 'print sync receipt as JSON', false)
2464
- .option('-v, --verbose', 'show command diagnostics', false)
2465
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2466
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2467
- .action(async (plugins, rawOptions) => {
2468
- const context = createActionContext('sync', rawOptions);
2469
- const normalized = normalizeWriteRequest(() => normalizeInstallCommandOptions({
2470
- cwd: context.cwd,
2471
- scope: rawOptions.scope,
2472
- platform: rawOptions.platform,
2473
- scriptTimeout: rawOptions.scriptTimeout,
2474
- allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2475
- }));
2476
- const syncPlatform = normalized.platform;
2477
- const result = await runActionWithContext(context, () => syncCommand(plugins ?? [], {
2478
- cwd: context.cwd,
2479
- env: context.env,
2480
- platform: syncPlatform,
2481
- scope: normalized.scope,
2482
- all: rawOptions.all,
2483
- refresh: rawOptions.refresh,
2484
- cliVersion: resolveCliVersion(),
2485
- yes: rawOptions.yes,
2486
- json: rawOptions.json,
2487
- interactive: context.interactive && context.isTty && !context.isCi,
2488
- prompt: context.prompt,
2489
- allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2490
- scriptTimeoutMs: normalized.scriptTimeoutMs,
2491
- }, context));
2492
- if (rawOptions.json) {
2493
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('sync', result));
2494
- }
2495
- else {
2496
- for (const line of renderRebuildReceipt('sync', result))
2497
- writeLine(io.stdout, line);
2498
- }
2499
- writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
2500
- commandExitState.exitCode = resolveCommandReceiptExitCode(result);
2501
- });
2502
- program
2503
- .command('install')
2504
- .argument('[plugins...]')
2505
- .option('-s, --scope <scope>', 'project or user')
2506
- .option('-p, --platform <platform>', 'codex | claude | both')
2507
- .option('--dependency-scope <pluginId[#platform]=scope>', 'place one dependency at project or user scope; repeat for more dependencies', (value, previous) => [...previous, value], [])
2508
- .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2509
- .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2510
- .option('-y, --yes', 'authorize preApply and shared Codex native replacement', false)
2511
- .option('-j, --json', 'print receipt as JSON', false)
2512
- .option('-v, --verbose', 'show command diagnostics', false)
2513
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2514
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2515
- .action(async (plugins, rawOptions) => {
2516
- const context = createActionContext('install', rawOptions);
2517
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('install', plugins ?? [], rawOptions, context));
2518
- if (outcome.cancelled)
2519
- return;
2520
- const receipt = outcome.result;
2521
- if (rawOptions.json) {
2522
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('install', receipt));
2523
- }
2524
- else {
2525
- for (const line of renderRebuildReceipt('install', receipt)) {
2526
- writeLine(io.stdout, line);
2527
- }
2528
- }
2529
- writeWarnings(io.stderr, receipt.warnings, { actionableOnly: true });
2530
- commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
2531
- });
2532
- program
2533
- .command('uninstall')
2534
- .argument('[plugins...]')
2535
- .option('-s, --scope <scope>', 'project or user')
2536
- .option('-p, --platform <platform>', 'codex | claude | both')
2537
- .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2538
- .option('-y, --yes', 'confirm changes to installed native tool declarations', false)
2539
- .option('-j, --json', 'print receipt as JSON', false)
2002
+ const recoveryCommand = (command) => command
2003
+ .option('-j, --json', 'print one recovery receipt as JSON', false)
2540
2004
  .option('-v, --verbose', 'show command diagnostics', false)
2541
2005
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
2542
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2543
- .action(async (plugins, rawOptions) => {
2544
- const context = createActionContext('uninstall', rawOptions);
2545
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('uninstall', plugins ?? [], rawOptions, context));
2546
- if (outcome.cancelled)
2547
- return;
2548
- const receipt = outcome.result;
2549
- if (rawOptions.json) {
2550
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('uninstall', receipt));
2551
- }
2552
- else {
2553
- for (const line of renderRebuildReceipt('uninstall', receipt)) {
2554
- writeLine(io.stdout, line);
2555
- }
2556
- }
2557
- writeWarnings(io.stderr, receipt.warnings, { actionableOnly: true });
2558
- commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
2559
- });
2560
- program
2561
- .command('cleanup')
2562
- .argument('[plugins...]')
2563
- .option('-s, --scope <scope>', 'project or user')
2564
- .option('-p, --platform <platform>', 'codex | claude | both')
2565
- .option('-j, --json', 'print cleanup result as JSON', false)
2566
- .option('--force', 'use exact shallow evidence for exclusively owned damaged output; preserve foreign/shared content; no deleted-plugin-wide cleanup', false)
2567
- .option('-y, --yes', 'authorize cleanup of a deleted plugin across Codex, Claude Code, user and current project, including saved settings and shared native install', false)
2568
- .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2569
- .option('-v, --verbose', 'show command diagnostics', false)
2570
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2571
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2572
- .action(async (plugins, rawOptions) => {
2573
- const context = createActionContext('cleanup', rawOptions);
2574
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('cleanup', plugins ?? [], rawOptions, context));
2575
- if (outcome.cancelled)
2576
- return;
2577
- const result = outcome.result;
2578
- if (rawOptions.json) {
2579
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('cleanup', result));
2580
- }
2581
- else {
2582
- for (const line of renderRebuildReceipt('cleanup', result)) {
2583
- writeLine(io.stdout, line);
2584
- }
2585
- }
2586
- writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
2587
- commandExitState.exitCode = resolveCommandReceiptExitCode(result);
2588
- });
2589
- program
2590
- .command('reinstall')
2591
- .argument('[plugins...]')
2592
- .option('-s, --scope <scope>', 'project or user')
2593
- .option('-p, --platform <platform>', 'codex | claude | both')
2594
- .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2595
- .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2596
- .option('-y, --yes', 'authorize preApply and shared Codex native replacement or forced reinstall', false)
2597
- .option('-j, --json', 'print receipt as JSON', false)
2598
- .option('-v, --verbose', 'show command diagnostics', false)
2599
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2600
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2601
- .action(async (plugins, rawOptions) => {
2602
- const context = createActionContext('reinstall', rawOptions);
2603
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('reinstall', plugins ?? [], rawOptions, context));
2604
- if (outcome.cancelled)
2605
- return;
2606
- const receipt = outcome.result;
2607
- if (rawOptions.json) {
2608
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('reinstall', receipt));
2609
- }
2610
- else {
2611
- const lines = renderRebuildReceipt('reinstall', receipt);
2612
- for (const line of lines) {
2613
- writeLine(io.stdout, line);
2614
- }
2615
- }
2616
- writeWarnings(io.stderr, receipt.warnings, { actionableOnly: true });
2617
- commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
2618
- });
2619
- const config = program
2620
- .command('config')
2621
- .description('Manage AW plugin configuration');
2622
- config
2623
- .command('list')
2624
- .argument('[plugin]')
2625
- .option('-s, --scope <scope>', 'read only project or user config')
2626
- .option('-j, --json', 'print config as JSON', false)
2627
- .option('-v, --verbose', 'show command diagnostics', false)
2628
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2629
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2630
- .action(async (pluginName, rawOptions) => {
2631
- const context = createActionContext('config:list', rawOptions);
2632
- const outcome = await runActionWithContext(context, async () => {
2633
- resolveConfigReadResolution(rawOptions.scope);
2634
- const execute = async (selectedPlugin) => resolveMultiSourceConfigRead({
2635
- selector: selectedPlugin,
2636
- rawOptions,
2637
- context,
2638
- });
2639
- if (pluginName)
2640
- return { cancelled: false, pluginName, value: await execute(pluginName) };
2641
- if (!isWizardAvailable(context, rawOptions))
2642
- throw new Error(configCommandUsage('list'));
2643
- const currentCandidates = await loadWizardCandidates(context, {});
2644
- const storedCandidates = await loadStoredConfigWizardCandidates(context, configReadStoreLocations(rawOptions));
2645
- const candidates = mergeWizardCandidates(currentCandidates, storedCandidates);
2646
- if (candidates.length === 0) {
2647
- throw new Error('No plugin config candidates exist in the selected location.');
2648
- }
2649
- const result = await runCommandWizard(context, {
2650
- command: 'config:list',
2651
- candidates,
2652
- initial: { plugins: [], scope: rawOptions.scope },
2653
- pluginMode: 'single',
2654
- ...equivalentConfigCommandProjection((selection) => ({
2655
- command: 'list',
2656
- plugin: selection.plugins[0],
2657
- scope: rawOptions.scope,
2658
- })),
2659
- execute: (selection) => execute(selection.plugins[0]),
2660
- });
2661
- return result.cancelled
2662
- ? { cancelled: true }
2663
- : { cancelled: false, pluginName: result.selection.plugins[0], value: result.value };
2006
+ .option('--log-file <path>', 'write debug diagnostics to the specified file');
2007
+ const scopedCommand = (command) => recoveryCommand(command)
2008
+ .addOption(strictValueOption('--scope <scope>', 'requirement or physical scope', ['user', 'project']).makeOptionMandatory());
2009
+ const contextualCommand = (command) => recoveryCommand(command)
2010
+ .addOption(strictValueOption('--context <context>', 'user only, or user and current project', ['user', 'project']));
2011
+ const recoveryScope = (rawOptions) => normalizeWriteRequest(() => parseScopeValue(rawOptions.scope));
2012
+ const recoveryTargets = (rawOptions) => rawOptions.target === undefined
2013
+ ? undefined : normalizeWriteRequest(() => normalizeAgents(typeof rawOptions.target === 'string' ? [rawOptions.target] : rawOptions.target));
2014
+ const recoveryContext = (rawOptions) => rawOptions.context === undefined
2015
+ ? undefined : normalizeWriteRequest(() => parseContextValue(rawOptions.context));
2016
+ const runRecoveryAction = async (command, rawOptions, action) => {
2017
+ const context = createActionContext(command, rawOptions);
2018
+ const result = await runActionWithContext(context, () => action(context));
2019
+ if (rawOptions.json)
2020
+ writeJsonReceipt(io.stdout, result);
2021
+ else
2022
+ renderRecoveryReceipt(io.stdout, result);
2023
+ commandExitState.exitCode = result.ok ? 0
2024
+ : result.errors.some((error) => error.code === 'invalidArguments') ? 2 : 1;
2025
+ };
2026
+ const showWorkContext = async (context, rawOptions) => {
2027
+ if (rawOptions.scope === 'project' && rawOptions.context === 'user') {
2028
+ throw new WriteCommandRequestError('--scope project cannot be combined with --context user.');
2029
+ }
2030
+ const work = await resolveWorkContext({ cwd: context.cwd, context: recoveryContext(rawOptions),
2031
+ ...(rawOptions.scope === undefined ? {} : { scope: recoveryScope(rawOptions) }) });
2032
+ if (!rawOptions.json)
2033
+ writeLine(io.stdout, work.projectRoot
2034
+ ? `工作上下文:用户与当前项目 ${work.projectRoot}` : '工作上下文:仅用户');
2035
+ return work;
2036
+ };
2037
+ const intent = plugin.command('intent').description('Manage user or project plugin requirements');
2038
+ scopedCommand(intent.command('add').argument('<plugins...>'))
2039
+ .action(async (plugins, rawOptions) => runRecoveryAction('intent:add', rawOptions, (context) => intentAdd({ scope: recoveryScope(rawOptions), cwd: context.cwd, env: context.env, selectors: plugins })));
2040
+ scopedCommand(intent.command('remove').argument('<plugins...>'))
2041
+ .action(async (plugins, rawOptions) => runRecoveryAction('intent:remove', rawOptions, (context) => intentRemove({ scope: recoveryScope(rawOptions), cwd: context.cwd, env: context.env, selectors: plugins })));
2042
+ scopedCommand(intent.command('list'))
2043
+ .action(async (rawOptions) => runRecoveryAction('intent:list', rawOptions, (context) => intentList({ scope: recoveryScope(rawOptions), cwd: context.cwd, env: context.env })));
2044
+ scopedCommand(intent.command('retire').argument('<plugin>'))
2045
+ .description('Retire a plugin proven absent from its healthy Catalog')
2046
+ .option('--target <target>', 'limit output changes to a platform target', collectUniqueOption)
2047
+ .option('--delete-config', 'also delete saved user plugin configuration', false)
2048
+ .option('-y, --yes', 'confirm the reviewed retirement changes', false)
2049
+ .option('--allow-managed-codex-install', 'allow acquisition of the pinned AW-managed Codex runtime', false)
2050
+ .action(async (selector, rawOptions) => runRecoveryAction('intent:retire', rawOptions, (context) => runRetire({ scope: recoveryScope(rawOptions), cwd: context.cwd, env: context.env, selector,
2051
+ cliVersion: resolveCliVersion(), targets: recoveryTargets(rawOptions),
2052
+ deleteConfig: rawOptions.deleteConfig, yes: rawOptions.yes,
2053
+ interactive: isWizardAvailable(context, rawOptions), prompt: context.prompt,
2054
+ }, recoveryRuntime(context, rawOptions, true))));
2055
+ const targets = plugin.command('targets').description('Manage the local platform policy');
2056
+ contextualCommand(targets.command('set').argument('[targets...]'))
2057
+ .option('--none', 'save an empty target set', false)
2058
+ .option('--apply', 'apply the saved target policy in this work context', false)
2059
+ .option('-y, --yes', 'confirm reviewed output changes', false)
2060
+ .action(async (agents, rawOptions) => {
2061
+ if (rawOptions.none ? agents.length > 0 : agents.length === 0) {
2062
+ throw new WriteCommandRequestError('Use target IDs or --none, but not both.');
2063
+ }
2064
+ const normalized = normalizeWriteRequest(() => normalizeAgents(agents));
2065
+ await runRecoveryAction('targets:set', rawOptions, async (context) => {
2066
+ await showWorkContext(context, rawOptions);
2067
+ return targetsSet({ cwd: context.cwd, env: context.env, targets: normalized,
2068
+ context: recoveryContext(rawOptions), apply: rawOptions.apply, yes: rawOptions.yes,
2069
+ cliVersion: resolveCliVersion(), runtime: recoveryRuntime(context, rawOptions, true) });
2664
2070
  });
2665
- if (outcome.cancelled)
2666
- return;
2667
- const resolved = outcome.value;
2668
- const selectedPluginName = resolved.pluginName;
2669
- if (rawOptions.json) {
2670
- writeJsonReceipt(io.stdout, {
2671
- ok: true,
2672
- configSchemaVersion: 4,
2673
- command: 'config:list',
2674
- plugin: selectedPluginName,
2675
- pluginName: selectedPluginName,
2676
- pluginId: resolved.pluginId,
2677
- sourceId: resolved.sourceId,
2678
- sourceIdentity: resolved.sourceIdentity,
2679
- scope: rawOptions.scope ?? 'effective',
2680
- ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
2681
- fields: resolved.result.fields,
2682
- });
2683
- }
2684
- else {
2685
- writeConfigReadHuman(io.stdout, resolved.result, resolved.pluginId);
2686
- }
2687
2071
  });
2688
- config
2689
- .command('get')
2690
- .argument('[plugin]')
2691
- .argument('[key]')
2692
- .option('-s, --scope <scope>', 'read only project or user config')
2693
- .option('-j, --json', 'print config as JSON', false)
2694
- .option('-v, --verbose', 'show command diagnostics', false)
2695
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2696
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2697
- .action(async (pluginName, key, rawOptions) => {
2698
- const context = createActionContext('config:get', rawOptions);
2699
- const outcome = await runActionWithContext(context, async () => {
2700
- resolveConfigReadResolution(rawOptions.scope);
2701
- const execute = async (selectedPlugin) => resolveMultiSourceConfigRead({
2702
- selector: selectedPlugin,
2703
- ...(key === undefined ? {} : { key }),
2704
- rawOptions,
2705
- context,
2072
+ contextualCommand(targets.command('list'))
2073
+ .action(async (rawOptions) => runRecoveryAction('targets:list', rawOptions, (context) => targetsList({ cwd: context.cwd, env: context.env, context: recoveryContext(rawOptions) })));
2074
+ for (const action of ['exclude', 'include']) {
2075
+ contextualCommand(targets.command(action).argument('<plugin>'))
2076
+ .addOption(strictValueOption('--target <target>', 'one canonical platform target', PLATFORM_TARGETS).makeOptionMandatory())
2077
+ .option('--apply', 'apply the saved exclusion policy in this work context', false)
2078
+ .option('-y, --yes', 'confirm reviewed output changes', false)
2079
+ .action(async (selector, rawOptions) => {
2080
+ await runRecoveryAction(`targets:${action}`, rawOptions, async (context) => {
2081
+ await showWorkContext(context, rawOptions);
2082
+ return targetsChangeExclusion({ cwd: context.cwd, env: context.env, context: recoveryContext(rawOptions),
2083
+ selector, target: rawOptions.target, excluded: action === 'exclude', apply: rawOptions.apply,
2084
+ yes: rawOptions.yes, cliVersion: resolveCliVersion(), runtime: recoveryRuntime(context, rawOptions, true) });
2706
2085
  });
2707
- if (pluginName)
2708
- return { cancelled: false, pluginName, value: await execute(pluginName) };
2709
- if (!isWizardAvailable(context, rawOptions))
2710
- throw new Error(configCommandUsage('get'));
2711
- const currentCandidates = await loadWizardCandidates(context, {});
2712
- const storedCandidates = await loadStoredConfigWizardCandidates(context, configReadStoreLocations(rawOptions));
2713
- const candidates = mergeWizardCandidates(currentCandidates, storedCandidates);
2714
- if (candidates.length === 0) {
2715
- throw new Error('No plugin config candidates exist in the selected location.');
2086
+ });
2087
+ }
2088
+ for (const command of ['sync', 'install', 'remove', 'cleanup']) {
2089
+ const entry = command === 'cleanup'
2090
+ ? scopedCommand(plugin.command(command).argument('[plugins...]'))
2091
+ : contextualCommand(plugin.command(command).argument(command === 'sync' ? '[plugins...]' : '<plugins...>'));
2092
+ entry.option('-y, --yes', 'authorize reviewed scripts or output changes', false)
2093
+ .option('--allow-managed-codex-install', 'allow acquisition of the pinned AW-managed Codex runtime', false);
2094
+ if (command === 'install' || command === 'remove')
2095
+ entry
2096
+ .addOption(strictValueOption('--scope <scope>', 'requirement document to edit', ['user', 'project']));
2097
+ if (command === 'remove')
2098
+ entry.option('--workspace-root <path>', 'delete plugin source from this explicit Catalog Workspace');
2099
+ if (command === 'sync')
2100
+ entry
2101
+ .option('--target <target>', 'limit execution to a canonical platform target', collectUniqueOption)
2102
+ .option('--rebuild', 'rebuild selected desired outputs', false)
2103
+ .option('--check', 'observe without preparing or writing', false)
2104
+ .option('--refresh', 'refresh required Catalog sources before review', false);
2105
+ if (command === 'cleanup')
2106
+ entry
2107
+ .addOption(strictValueOption('--target <target>', 'physical platform target to clean', PLATFORM_TARGETS).makeOptionMandatory())
2108
+ .option('--force', 'clean provably owned output despite damaged weak state', false);
2109
+ if (command === 'install' || command === 'sync')
2110
+ entry
2111
+ .option('--script-timeout <ms>', 'override plugin preApply timeout in milliseconds');
2112
+ entry.action(async (plugins, rawOptions) => {
2113
+ if (rawOptions.check && (rawOptions.refresh || rawOptions.rebuild)) {
2114
+ throw new WriteCommandRequestError('--check cannot be combined with --refresh or --rebuild.');
2716
2115
  }
2717
- const result = await runCommandWizard(context, {
2718
- command: 'config:get',
2719
- candidates,
2720
- initial: { plugins: [], key, scope: rawOptions.scope },
2721
- pluginMode: 'single',
2722
- ...equivalentConfigCommandProjection((selection) => ({
2723
- command: 'get',
2724
- plugin: selection.plugins[0],
2725
- key,
2726
- scope: rawOptions.scope,
2727
- })),
2728
- execute: (selection) => execute(selection.plugins[0]),
2116
+ await runRecoveryAction(command, rawOptions, async (context) => {
2117
+ if (command !== 'cleanup')
2118
+ await showWorkContext(context, rawOptions);
2119
+ const targetValues = typeof rawOptions.target === 'string' ? [rawOptions.target] : rawOptions.target;
2120
+ const options = {
2121
+ ...(rawOptions.scope === undefined ? {} : { scope: recoveryScope(rawOptions) }),
2122
+ context: recoveryContext(rawOptions), cwd: context.cwd, env: context.env, cliVersion: resolveCliVersion(),
2123
+ targets: targetValues === undefined ? undefined : normalizeWriteRequest(() => normalizeAgents(targetValues)),
2124
+ all: command === 'sync' && plugins.length === 0,
2125
+ yes: rawOptions.yes, force: rawOptions.force, refresh: rawOptions.refresh,
2126
+ allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2127
+ scriptTimeoutMs: normalizeWriteRequest(() => resolveScriptTimeoutMs(rawOptions)),
2128
+ interactive: isWizardAvailable(context, rawOptions), prompt: context.prompt,
2129
+ };
2130
+ const backendCommand = command === 'remove' ? 'uninstall'
2131
+ : command === 'sync' && rawOptions.rebuild ? 'reinstall' : command;
2132
+ const result = rawOptions.check ? await checkRecovery(plugins, options)
2133
+ : await runRecovery(backendCommand, plugins, options, recoveryRuntime(context, rawOptions, true));
2134
+ return { ...result, command };
2729
2135
  });
2730
- return result.cancelled
2731
- ? { cancelled: true }
2732
- : { cancelled: false, pluginName: result.selection.plugins[0], value: result.value };
2733
2136
  });
2734
- if (outcome.cancelled)
2735
- return;
2736
- const resolved = outcome.value;
2737
- const selectedPluginName = resolved.pluginName;
2738
- if (rawOptions.json) {
2739
- writeJsonReceipt(io.stdout, {
2740
- ok: true,
2741
- configSchemaVersion: 4,
2742
- command: 'config:get',
2743
- plugin: selectedPluginName,
2744
- pluginName: selectedPluginName,
2745
- pluginId: resolved.pluginId,
2746
- sourceId: resolved.sourceId,
2747
- sourceIdentity: resolved.sourceIdentity,
2748
- key,
2749
- scope: rawOptions.scope ?? 'effective',
2750
- ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
2751
- fields: resolved.result.fields,
2752
- });
2753
- }
2754
- else {
2755
- writeConfigReadHuman(io.stdout, resolved.result, resolved.pluginId);
2756
- }
2757
- });
2758
- config
2759
- .command('set')
2760
- .argument('[plugin]')
2761
- .argument('[field]')
2762
- .option('-s, --scope <scope>', 'project or user')
2763
- .option('--value-json <json>', 'set the field to an exact JSON value')
2764
- .option('--value-stdin', 'read the exact string value from stdin until EOF', false)
2765
- .option('--preset <id>', 'set the field to the current manifest preset value')
2766
- .option('--apply', 'apply the saved config to visible desired placements', false)
2767
- .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2768
- .option('-y, --yes', 'confirm preApply and shared Codex native config apply', false)
2769
- .option('-j, --json', 'print receipt as JSON', false)
2137
+ }
2138
+ const config = plugin.command('config').description('Manage user common and platform configuration');
2139
+ const configCommand = (command) => command
2140
+ .addOption(strictValueOption('--platform <target>', 'configuration platform; omitted means common', PLATFORM_TARGETS))
2141
+ .addOption(strictValueOption('--context <context>', 'definition and reference context', ['user', 'project']))
2142
+ .option('-j, --json', 'print one JSON object', false)
2770
2143
  .option('-v, --verbose', 'show command diagnostics', false)
2771
2144
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
2772
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2773
- .action(async (pluginName, key, rawOptions) => {
2774
- if (!pluginName || !key)
2775
- throw new Error(configCommandUsage('set'));
2776
- const context = createActionContext('config:set', rawOptions);
2777
- let outcome;
2778
- try {
2779
- outcome = await runActionWithContext(context, async () => {
2780
- const scope = resolveConfigWriteScope(rawOptions.scope);
2781
- const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2782
- const explicitValueCount = [
2783
- rawOptions.valueJson !== undefined,
2784
- rawOptions.valueStdin === true,
2785
- rawOptions.preset !== undefined,
2786
- ].filter(Boolean).length;
2787
- if (explicitValueCount > 1
2788
- || (explicitValueCount === 0
2789
- && !(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true))) {
2790
- throw new Error('Use exactly one of --value-json <json>, --value-stdin, or --preset <id>.');
2145
+ .option('--log-file <path>', 'write debug diagnostics to the specified file');
2146
+ const configMutation = (command) => configCommand(command)
2147
+ .option('--apply', 'refresh existing affected outputs after saving', false)
2148
+ .option('--script-timeout <ms>', 'override plugin preApply timeout in milliseconds')
2149
+ .option('-y, --yes', 'confirm reviewed output changes', false);
2150
+ const configContext = async (context, rawOptions, apply = rawOptions.apply === true) => {
2151
+ const requested = rawOptions.context === undefined ? apply ? undefined : 'user'
2152
+ : normalizeWriteRequest(() => parseContextValue(rawOptions.context));
2153
+ const work = await resolveWorkContext({ cwd: context.cwd, context: requested });
2154
+ if (apply && !rawOptions.json)
2155
+ writeLine(io.stdout, work.projectRoot
2156
+ ? `工作上下文:用户与当前项目 ${work.projectRoot}` : '工作上下文:仅用户');
2157
+ return work;
2158
+ };
2159
+ const configPlatform = (rawOptions) => rawOptions.platform === undefined
2160
+ ? undefined : normalizeWriteRequest(() => parsePlatformTargetValue(rawOptions.platform));
2161
+ for (const command of ['schema', 'list', 'get']) {
2162
+ const entry = configCommand(config.command(command).argument('<plugin>'));
2163
+ if (command === 'get')
2164
+ entry.argument('[key]');
2165
+ entry.action(async (...args) => {
2166
+ const selector = args[0];
2167
+ const key = command === 'get' ? args[1] : undefined;
2168
+ const rawOptions = args[command === 'get' ? 2 : 1];
2169
+ const context = createActionContext(`config:${command}`, rawOptions);
2170
+ const work = await configContext(context, rawOptions);
2171
+ const resolved = await runActionWithContext(context, () => resolveMultiSourceConfigRead({
2172
+ selector, key, rawOptions: { ...rawOptions, context: work.context }, context,
2173
+ }));
2174
+ if (rawOptions.json)
2175
+ writeJsonReceipt(io.stdout, {
2176
+ ok: true, configSchemaVersion: 5, command: `config:${command}`,
2177
+ plugin: resolved.pluginName, pluginName: resolved.pluginName, pluginId: resolved.pluginId,
2178
+ sourceId: resolved.sourceId, sourceIdentity: resolved.sourceIdentity,
2179
+ context: work, platform: configPlatform(rawOptions) ?? 'common',
2180
+ ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
2181
+ fields: command === 'schema' ? resolved.result.fieldDefinitions ?? {} : resolved.result.fields,
2182
+ ...(key === undefined ? {} : { key }),
2183
+ });
2184
+ else if (command === 'schema')
2185
+ writeJsonReceipt(io.stdout, resolved.result.fieldDefinitions ?? {});
2186
+ else
2187
+ writeConfigReadHuman(io.stdout, resolved.result, resolved.pluginId);
2188
+ });
2189
+ }
2190
+ for (const command of ['set', 'edit', 'unset']) {
2191
+ const entry = configMutation(config.command(command).argument('<plugin>'));
2192
+ if (command !== 'edit')
2193
+ entry.argument('<field>');
2194
+ if (command === 'set')
2195
+ entry
2196
+ .option('--value-json <json>', 'set an exact JSON value')
2197
+ .option('--value-stdin', 'read the exact string value until stdin EOF', false)
2198
+ .option('--preset <id>', 'use a declared preset');
2199
+ entry.action(async (...args) => {
2200
+ const selector = args[0];
2201
+ const rawOptions = args[command === 'edit' ? 1 : 2];
2202
+ let key = command === 'edit' ? undefined : args[1];
2203
+ const context = createActionContext(`config:${command}`, rawOptions);
2204
+ try {
2205
+ const work = await configContext(context, rawOptions);
2206
+ const platform = configPlatform(rawOptions);
2207
+ const scriptTimeoutMs = normalizeWriteRequest(() => resolveScriptTimeoutMs(rawOptions));
2208
+ if (command === 'edit' && (!isWizardAvailable(context, rawOptions) || !context.prompt.select || !context.prompt.input)) {
2209
+ throw new WriteCommandRequestError('config edit requires an interactive terminal. Use config set for scripts.');
2791
2210
  }
2792
- return withResolvedConfigMutationPlugin(context, pluginName, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
2793
- if (!plugin.manifest.config?.fields) {
2794
- throw new Error(`configSchemaUnavailable: Plugin "${pluginName}" does not declare an AW config schema.`);
2795
- }
2796
- const field = plugin.manifest.config.fields[key];
2797
- if (!field) {
2798
- throw new Error(`Unknown config key "${key}".`);
2799
- }
2800
- const sensitive = field.sensitive === true;
2801
- if (sensitive && scope !== 'user') {
2802
- const error = new Error('sensitiveProjectConfigUnsupported: Sensitive config can only be set in user scope.');
2803
- error.code = 'sensitiveProjectConfigUnsupported';
2804
- throw error;
2805
- }
2806
- if (sensitive && (rawOptions.valueJson !== undefined || rawOptions.preset !== undefined)) {
2807
- const error = new Error('sensitiveConfigInputUnsupported: Sensitive config cannot use --value-json or --preset.');
2808
- error.code = 'sensitiveConfigInputUnsupported';
2809
- throw error;
2211
+ const explicitValueCount = [rawOptions.valueJson !== undefined, rawOptions.valueStdin === true,
2212
+ rawOptions.preset !== undefined].filter(Boolean).length;
2213
+ if (command === 'set' && (explicitValueCount > 1 || explicitValueCount === 0
2214
+ && !(context.interactive && context.isTty && !context.isCi && !rawOptions.json))) {
2215
+ throw new WriteCommandRequestError('Use exactly one of --value-json, --value-stdin, or --preset.');
2216
+ }
2217
+ const receipt = await runActionWithContext(context, () => withResolvedConfigMutationPlugin(context, selector, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
2218
+ if (!plugin.manifest.config)
2219
+ throw Object.assign(new Error(`configSchemaUnavailable: Plugin "${plugin.manifest.name}" does not declare an AW config schema.`), { code: 'configSchemaUnavailable' });
2220
+ const fields = compilePluginConfigDefinition(plugin.manifest.config, platform).fields;
2221
+ if (command === 'edit') {
2222
+ const selected = await context.prompt.select({ message: `编辑用户${platform ?? 'common'}配置`,
2223
+ choices: Object.entries(fields).map(([value, field]) => ({ value, label: field.title ?? value })) });
2224
+ if (selected.cancelled || selected.action !== 'select' || selected.value === undefined)
2225
+ return undefined;
2226
+ key = selected.value;
2810
2227
  }
2811
- if (!sensitive && explicitValueCount !== 1) {
2812
- throw new Error('Use exactly one of --value-json <json>, --value-stdin, or --preset <id>.');
2228
+ const field = fields[key];
2229
+ const store = await readPluginConfigStore({ sourceId: source.sourceId,
2230
+ sourceIdentity: source.sourceIdentity, logicalIdentity: source.logicalIdentity,
2231
+ pluginName: plugin.manifest.name, scope: 'user', platform, cwd: context.cwd, env: context.env,
2232
+ schema: fields });
2233
+ if (command === 'unset') {
2234
+ if (!Object.hasOwn(store.values, key))
2235
+ throw new WriteCommandRequestError(`Unknown config key "${key}" in the selected layer.`);
2813
2236
  }
2237
+ else if (!field)
2238
+ throw new WriteCommandRequestError(`Unknown config key "${key}".`);
2814
2239
  let inputValue;
2815
- if (rawOptions.valueStdin === true) {
2816
- inputValue = await readUtf8Stdin(context.stdin);
2817
- }
2818
- else if (sensitive) {
2819
- if (!(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true)
2820
- || !context.prompt.password) {
2821
- const error = new Error('sensitiveConfigStdinRequired: Use --value-stdin for sensitive config outside an interactive terminal.');
2822
- error.code = 'sensitiveConfigStdinRequired';
2823
- throw error;
2240
+ if (command !== 'unset') {
2241
+ const sensitive = field.sensitive === true;
2242
+ if (sensitive && (rawOptions.valueJson !== undefined || rawOptions.preset !== undefined)) {
2243
+ throw new Error('sensitiveConfigInputUnsupported: Sensitive config requires --value-stdin.');
2824
2244
  }
2825
- const response = await context.prompt.password({
2826
- message: `输入 ${field.title ?? key}`,
2827
- mask: true,
2828
- });
2829
- if (response.cancelled || response.action !== 'select' || response.value === undefined) {
2830
- const error = new Error('sensitiveConfigInputCancelled: Sensitive config input was cancelled.');
2831
- error.code = 'sensitiveConfigInputCancelled';
2832
- throw error;
2245
+ if (rawOptions.valueStdin)
2246
+ inputValue = await readUtf8Stdin(context.stdin);
2247
+ else if (sensitive) {
2248
+ if (!context.interactive || !context.isTty || context.isCi || rawOptions.json || !context.prompt.password) {
2249
+ throw new Error('sensitiveConfigStdinRequired: Use --value-stdin outside an interactive terminal.');
2250
+ }
2251
+ const response = await context.prompt.password({ message: `输入 ${field.title ?? key}`, mask: true });
2252
+ if (response.cancelled || response.action !== 'select' || response.value === undefined)
2253
+ return undefined;
2254
+ inputValue = response.value;
2833
2255
  }
2834
- inputValue = response.value;
2835
- }
2836
- else if (rawOptions.valueJson !== undefined) {
2837
- try {
2838
- inputValue = JSON.parse(rawOptions.valueJson);
2256
+ else if (command === 'edit' || explicitValueCount === 0) {
2257
+ if (!context.prompt.input)
2258
+ throw new WriteCommandRequestError('Use --value-json, --value-stdin, or --preset.');
2259
+ const response = await context.prompt.input({ message: `输入 ${field.title ?? key} 的 JSON 值`,
2260
+ defaultValue: JSON.stringify(store.values[key]) });
2261
+ if (response.cancelled || response.action !== 'select' || response.value === undefined)
2262
+ return undefined;
2263
+ try {
2264
+ inputValue = JSON.parse(response.value);
2265
+ }
2266
+ catch {
2267
+ throw new WriteCommandRequestError('Enter a valid JSON value.');
2268
+ }
2839
2269
  }
2840
- catch {
2841
- throw new Error('--value-json must contain valid JSON.');
2270
+ else if (rawOptions.valueJson !== undefined) {
2271
+ try {
2272
+ inputValue = JSON.parse(rawOptions.valueJson);
2273
+ }
2274
+ catch {
2275
+ throw new WriteCommandRequestError('--value-json must contain valid JSON.');
2276
+ }
2277
+ }
2278
+ else {
2279
+ const preset = field.choices?.presets.find((candidate) => candidate.id === rawOptions.preset);
2280
+ if (!preset)
2281
+ throw new WriteCommandRequestError(`Unknown preset "${rawOptions.preset}".`);
2282
+ inputValue = structuredClone(preset.value);
2842
2283
  }
2843
2284
  }
2844
- else {
2845
- const preset = field.choices?.presets.find((entry) => entry.id === rawOptions.preset);
2846
- if (!preset)
2847
- throw new Error(`Unknown preset "${rawOptions.preset}" for config key "${key}".`);
2848
- inputValue = structuredClone(preset.value);
2849
- }
2850
- const writeReceipt = await applyPluginConfigWrite({
2851
- plugin,
2852
- pluginName: plugin.manifest.name,
2853
- scope,
2854
- cwd: context.cwd,
2855
- env: context.env,
2856
- nativeCliRunner: context.nativeCliRunner,
2857
- rebuildRuntime: {
2858
- ...context,
2859
- awHomeMutationToken,
2860
- catalogOperationContext,
2285
+ const writeReceipt = await applyPluginConfigWrite({ plugin, pluginName: plugin.manifest.name,
2286
+ scope: work.context, context: work.context, platform, cliVersion: resolveCliVersion(),
2287
+ cwd: context.cwd, env: context.env, nativeCliRunner: context.nativeCliRunner,
2288
+ rebuildRuntime: { ...context, awHomeMutationToken, catalogOperationContext,
2861
2289
  expectedGenerationSetDigest: catalogOperationContext.generationSetDigest,
2862
- ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }),
2863
- },
2864
- awHomeMutationToken,
2865
- request: {
2866
- updates: { [key]: inputValue },
2867
- },
2868
- apply: rawOptions.apply === true,
2869
- sharedNativeApplyConfirmed: rawOptions.yes === true,
2870
- preApplyConfirmed: rawOptions.yes === true,
2871
- });
2872
- return buildConfigMutationReceipt('config:set', {
2873
- ok: writeReceipt.overallState === 'succeeded',
2874
- ...writeReceipt,
2875
- plugin: plugin.manifest.name,
2876
- pluginName: plugin.manifest.name,
2877
- pluginId,
2878
- sourceId: source.sourceId,
2879
- sourceIdentity: source.sourceIdentity,
2880
- affectedFields: [{
2881
- key,
2882
- title: field.title ?? key,
2883
- kind: 'manifest',
2884
- reserved: false,
2885
- action: 'set',
2886
- scope,
2887
- }],
2290
+ ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }) },
2291
+ awHomeMutationToken, request: command === 'unset' ? { removeKeys: [key] } : { updates: { [key]: inputValue } },
2292
+ apply: rawOptions.apply === true, sharedNativeApplyConfirmed: rawOptions.yes === true,
2293
+ preApplyConfirmed: rawOptions.yes === true });
2294
+ return buildConfigMutationReceipt(`config:${command}`, {
2295
+ ...writeReceipt, ok: writeReceipt.overallState === 'succeeded', plugin: plugin.manifest.name,
2296
+ pluginName: plugin.manifest.name, pluginId, sourceId: source.sourceId, sourceIdentity: source.sourceIdentity,
2297
+ context: work, platform: platform ?? 'common',
2298
+ ...(command === 'unset' ? { removedKey: key, existed: true } : {}),
2299
+ affectedFields: [{ key: key, title: field?.title ?? key, kind: 'manifest', reserved: false,
2300
+ action: command === 'unset' ? 'unset' : 'set', scope: 'user',
2301
+ ...(writeReceipt.staleRemovedKeys?.includes(key) ? { stale: true } : {}) }],
2888
2302
  });
2889
- }, rawOptions.apply === true);
2890
- });
2891
- }
2892
- catch (error) {
2893
- writeConfigMutationFailure('config:set', pluginName, rawOptions.json === true, error);
2894
- return;
2895
- }
2896
- const receipt = outcome;
2897
- if (rawOptions.json) {
2898
- writeJsonReceipt(io.stdout, receipt);
2899
- }
2900
- else {
2901
- writeLine(io.stdout, `已写入 ${receipt.plugin}.${key}(${configLayerLabel(receipt.scope)}配置)。`);
2902
- writeConfigApplyNotice(io.stderr, receipt);
2903
- }
2904
- commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2905
- });
2906
- config
2907
- .command('unset')
2908
- .argument('[plugin]')
2909
- .argument('[key]')
2910
- .option('-s, --scope <scope>', 'project or user')
2911
- .option('--apply', 'apply the saved config to visible desired placements', false)
2912
- .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2913
- .option('-y, --yes', 'confirm preApply and shared Codex native config apply', false)
2914
- .option('-j, --json', 'print receipt as JSON', false)
2915
- .option('-v, --verbose', 'show command diagnostics', false)
2916
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
2917
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
2918
- .action(async (pluginName, key, rawOptions) => {
2919
- const context = createActionContext('config:unset', rawOptions);
2920
- let outcome;
2921
- try {
2922
- outcome = await runActionWithContext(context, async () => {
2923
- const scope = resolveConfigWriteScope(rawOptions.scope);
2924
- const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2925
- const execute = async (selectedPlugin, selectedKey, confirmed) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
2926
- const store = await readPluginConfigStore({
2927
- sourceId: source.sourceId,
2928
- sourceIdentity: source.sourceIdentity,
2929
- logicalIdentity: source.logicalIdentity,
2930
- pluginName: plugin.manifest.name,
2931
- scope,
2932
- cwd: context.cwd,
2933
- env: context.env,
2934
- schema: plugin.manifest.config?.fields,
2935
- });
2936
- const existed = Object.hasOwn(store.values, selectedKey);
2937
- if (!existed)
2938
- throw new Error(`Unknown config key "${selectedKey}".`);
2939
- const writeReceipt = await applyPluginConfigWrite({
2940
- plugin,
2941
- pluginName: plugin.manifest.name,
2942
- scope,
2943
- cwd: context.cwd,
2944
- env: context.env,
2945
- nativeCliRunner: context.nativeCliRunner,
2946
- rebuildRuntime: {
2947
- ...context,
2948
- awHomeMutationToken,
2949
- catalogOperationContext,
2950
- expectedGenerationSetDigest: catalogOperationContext.generationSetDigest,
2951
- ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }),
2952
- },
2953
- awHomeMutationToken,
2954
- request: {
2955
- removeKeys: [selectedKey],
2956
- },
2957
- apply: rawOptions.apply === true,
2958
- sharedNativeApplyConfirmed: confirmed,
2959
- preApplyConfirmed: rawOptions.yes === true,
2960
- });
2961
- return buildConfigMutationReceipt('config:unset', {
2962
- ok: writeReceipt.overallState === 'succeeded',
2963
- ...writeReceipt,
2964
- plugin: plugin.manifest.name,
2965
- pluginName: plugin.manifest.name,
2966
- pluginId,
2967
- sourceId: source.sourceId,
2968
- sourceIdentity: source.sourceIdentity,
2969
- removedKey: selectedKey,
2970
- existed,
2971
- affectedFields: [{
2972
- key: selectedKey,
2973
- title: plugin?.manifest.config?.fields?.[selectedKey]?.title ?? selectedKey,
2974
- kind: 'manifest',
2975
- reserved: false,
2976
- action: 'unset',
2977
- scope,
2978
- ...(!plugin.manifest.config?.fields?.[selectedKey] ? { stale: true } : {}),
2979
- }],
2980
- });
2981
- }, rawOptions.apply === true);
2982
- if (pluginName && key) {
2983
- return {
2984
- cancelled: false,
2985
- value: await execute(pluginName, key, rawOptions.yes === true),
2986
- };
2987
- }
2988
- if (!isWizardAvailable(context, rawOptions))
2989
- throw new Error(configCommandUsage('unset'));
2990
- const loadedCurrentCandidates = await loadWizardCandidates(context, {});
2991
- const currentCandidates = loadedCurrentCandidates.filter(hasConfigSchemaKeys);
2992
- const candidates = currentCandidates;
2993
- if (pluginName && !candidates.some((entry) => entry.pluginName === pluginName)) {
2994
- throw new Error(`Unknown plugin "${pluginName}".`);
2303
+ }, rawOptions.apply === true));
2304
+ if (!receipt)
2305
+ return;
2306
+ if (rawOptions.json)
2307
+ writeJsonReceipt(io.stdout, receipt);
2308
+ else {
2309
+ writeLine(io.stdout, `已保存 ${receipt.plugin}.${key}(用户 ${platform ?? 'common'} 配置)。`);
2310
+ writeConfigApplyNotice(io.stderr, receipt);
2995
2311
  }
2996
- if (candidates.length === 0) {
2997
- throw new Error('No plugin config candidates exist in the selected location.');
2998
- }
2999
- const result = await runCommandWizard(context, {
3000
- command: 'config:unset',
3001
- candidates,
3002
- initial: { plugins: pluginName ? [pluginName] : [], key, scope },
3003
- pluginMode: pluginName ? 'none' : 'single',
3004
- promptForKey: key === undefined,
3005
- destructive: true,
3006
- ...equivalentConfigCommandProjection((selection) => ({
3007
- command: 'unset',
3008
- plugin: selection.plugins[0],
3009
- key: selection.key,
3010
- scope: selection.scope ?? 'user',
3011
- })),
3012
- resultState: overallResultState,
3013
- execute: (selection) => execute(selection.plugins[0], selection.key, true),
3014
- });
3015
- return result.cancelled
3016
- ? { cancelled: true }
3017
- : { cancelled: false, value: result.value };
3018
- });
3019
- }
3020
- catch (error) {
3021
- writeConfigMutationFailure('config:unset', pluginName, rawOptions.json === true, error);
3022
- return;
3023
- }
3024
- if (outcome.cancelled)
3025
- return;
3026
- const receipt = outcome.value;
3027
- if (rawOptions.json) {
3028
- writeJsonReceipt(io.stdout, receipt);
3029
- }
3030
- else {
3031
- writeLine(io.stdout, receipt.existed
3032
- ? `已删除 ${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope)}配置)。`
3033
- : `${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope)}配置)原本不存在。`);
3034
- writeConfigApplyNotice(io.stderr, receipt);
3035
- }
3036
- commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
3037
- });
3038
- config
3039
- .command('reset')
3040
- .argument('[plugin]')
3041
- .option('-s, --scope <scope>', 'project or user')
3042
- .option('--apply', 'apply the saved config to visible desired placements', false)
3043
- .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
3044
- .option('-y, --yes', 'confirm destructive reset and preApply/shared Codex native config apply', false)
3045
- .option('-j, --json', 'print receipt as JSON', false)
3046
- .option('-v, --verbose', 'show command diagnostics', false)
3047
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
3048
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
3049
- .action(async (pluginName, rawOptions) => {
2312
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2313
+ }
2314
+ catch (error) {
2315
+ writeConfigMutationFailure(`config:${command}`, selector, rawOptions.json === true, error);
2316
+ }
2317
+ });
2318
+ }
2319
+ configMutation(config.command('reset').argument('<plugin>'))
2320
+ .action(async (selector, rawOptions) => {
3050
2321
  const context = createActionContext('config:reset', rawOptions);
3051
- let outcome;
3052
2322
  try {
3053
- outcome = await runActionWithContext(context, async () => {
3054
- const execute = async (selectedPlugin, selectedScope, confirmed) => {
3055
- const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
3056
- if (!confirmed) {
3057
- throw new Error(`Reset requires confirmation. Re-run \`aw config reset ${selectedPlugin} -s ${selectedScope} --yes\` to delete this store.`);
3058
- }
3059
- const located = await resolveStoredConfigResetIdentity({
3060
- selector: selectedPlugin,
3061
- context,
3062
- scope: selectedScope,
3063
- });
3064
- const resetLocatedStore = async (plugin, pluginError) => {
3065
- const affectedFields = await describeResetAffectedFields({
3066
- sourceId: located.sourceId,
3067
- sourceIdentity: located.sourceIdentity,
3068
- logicalIdentity: located.logicalIdentity,
3069
- pluginName: located.pluginName,
3070
- plugin,
3071
- pluginError,
3072
- scope: selectedScope,
3073
- cwd: context.cwd,
3074
- env: context.env,
3075
- }).catch(() => []);
3076
- const resetReceipt = await resetPluginConfigWithHelper({
3077
- sourceId: located.sourceId,
3078
- sourceIdentity: located.sourceIdentity,
3079
- logicalIdentity: located.logicalIdentity,
3080
- pluginName: located.pluginName,
3081
- scope: selectedScope,
3082
- cwd: context.cwd,
3083
- env: context.env,
3084
- nativeCliRunner: context.nativeCliRunner,
3085
- plugin,
3086
- pluginError,
3087
- apply: rawOptions.apply === true,
3088
- rebuildRuntime: scriptTimeoutMs === undefined ? undefined : { scriptTimeoutMs },
3089
- sharedNativeApplyConfirmed: confirmed,
3090
- preApplyConfirmed: rawOptions.yes === true,
3091
- });
3092
- return buildConfigMutationReceipt('config:reset', {
3093
- ok: resetReceipt.overallState === 'succeeded',
3094
- ...resetReceipt,
3095
- plugin: located.pluginName,
3096
- pluginName: located.pluginName,
3097
- pluginId: located.pluginId,
3098
- sourceId: located.sourceId,
3099
- ...(located.sourceIdentity ? { sourceIdentity: located.sourceIdentity } : {}),
3100
- affectedFields,
3101
- });
3102
- };
3103
- let resetStarted = false;
3104
- try {
3105
- return await withResolvedCatalogPlugin({
3106
- selector: located.pluginId,
3107
- cliVersion: resolveCliVersion(),
3108
- env: context.env,
3109
- consume: async ({ plugin }) => {
3110
- resetStarted = true;
3111
- return resetLocatedStore(plugin, null);
3112
- },
3113
- });
3114
- }
3115
- catch (error) {
3116
- if (resetStarted)
3117
- throw error;
3118
- return resetLocatedStore(null, error instanceof Error ? error : new Error(String(error)));
3119
- }
3120
- };
3121
- const scope = resolveConfigWriteScope(rawOptions.scope);
3122
- const needsWizard = pluginName === undefined || rawOptions.yes !== true;
3123
- if (!needsWizard) {
3124
- return {
3125
- cancelled: false,
3126
- value: await execute(pluginName, scope, true),
3127
- };
3128
- }
3129
- if (!isWizardAvailable(context, rawOptions)) {
3130
- if (!pluginName)
3131
- throw new Error(configCommandUsage('reset'));
3132
- return {
3133
- cancelled: false,
3134
- value: await execute(pluginName, scope, false),
3135
- };
3136
- }
3137
- const currentCandidates = pluginName ? [] : await loadWizardCandidates(context, {});
3138
- const candidatesByScope = new Map();
3139
- if (!pluginName && (rawOptions.scope === undefined || scope === 'user')) {
3140
- candidatesByScope.set('user', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'user' }], { includeEmptyStores: true })));
3141
- }
3142
- if (!pluginName && (rawOptions.scope === undefined || scope === 'project')) {
3143
- candidatesByScope.set('project', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'project' }], { includeEmptyStores: true })));
2323
+ const work = await configContext(context, rawOptions);
2324
+ const platform = configPlatform(rawOptions);
2325
+ if (!rawOptions.yes) {
2326
+ if (!isWizardAvailable(context, rawOptions) || !context.prompt.confirm)
2327
+ throw new WriteCommandRequestError('config reset requires --yes.');
2328
+ const confirmed = await context.prompt.confirm({ message: `删除 ${selector} 的用户 ${platform ?? 'common'} 覆盖?`, defaultValue: false });
2329
+ if (confirmed.cancelled || confirmed.value !== true)
2330
+ return;
2331
+ }
2332
+ const located = await resolveStoredConfigResetIdentity({ selector, context, scope: 'user' });
2333
+ const resetStore = async (plugin, pluginError) => resetPluginConfigWithHelper({ ...located, plugin, pluginError, platform, scope: work.context,
2334
+ context: work.context, cliVersion: resolveCliVersion(), cwd: context.cwd, env: context.env,
2335
+ nativeCliRunner: context.nativeCliRunner, apply: rawOptions.apply === true,
2336
+ rebuildRuntime: { ...context, scriptTimeoutMs: resolveScriptTimeoutMs(rawOptions) },
2337
+ sharedNativeApplyConfirmed: true, preApplyConfirmed: rawOptions.yes === true });
2338
+ let started = false;
2339
+ const resetReceipt = await runActionWithContext(context, async () => {
2340
+ try {
2341
+ return await withResolvedCatalogPlugin({ selector: located.pluginId, cliVersion: resolveCliVersion(), env: context.env,
2342
+ consume: ({ plugin }) => { started = true; return resetStore(plugin, null); } });
3144
2343
  }
3145
- const candidates = mergeWizardCandidates(candidatesByScope.get('user') ?? [], candidatesByScope.get('project') ?? []);
3146
- if (!pluginName && candidates.length === 0) {
3147
- throw new Error('No plugin config candidates exist in the selected location.');
2344
+ catch (error) {
2345
+ if (started)
2346
+ throw error;
2347
+ return resetStore(null, error instanceof Error ? error : new Error(String(error)));
3148
2348
  }
3149
- const result = await runCommandWizard(context, {
3150
- command: 'config:reset',
3151
- candidates,
3152
- initial: { plugins: pluginName ? [pluginName] : [], scope },
3153
- pluginMode: pluginName ? 'none' : 'single',
3154
- promptForScope: rawOptions.scope === undefined,
3155
- destructive: true,
3156
- isPluginVisible: (candidate, selection) => candidatesByScope
3157
- .get(selection.scope ?? 'user')
3158
- ?.some((entry) => entry.pluginName === candidate.pluginName) !== false,
3159
- ...equivalentConfigCommandProjection((selection) => ({
3160
- command: 'reset',
3161
- plugin: selection.plugins[0],
3162
- scope: selection.scope ?? 'user',
3163
- confirmed: true,
3164
- })),
3165
- resultState: overallResultState,
3166
- execute: (selection) => execute(selection.plugins[0], selection.scope ?? 'user', true),
3167
- });
3168
- return result.cancelled
3169
- ? { cancelled: true }
3170
- : { cancelled: false, value: result.value };
3171
2349
  });
2350
+ const receipt = buildConfigMutationReceipt('config:reset', { ...resetReceipt,
2351
+ ok: resetReceipt.overallState === 'succeeded', ...located, plugin: located.pluginName,
2352
+ context: work, platform: platform ?? 'common' });
2353
+ if (rawOptions.json)
2354
+ writeJsonReceipt(io.stdout, receipt);
2355
+ else {
2356
+ writeLine(io.stdout, `已恢复 ${receipt.plugin} 的用户 ${platform ?? 'common'} 默认配置。`);
2357
+ writeConfigApplyNotice(io.stderr, receipt);
2358
+ }
2359
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
3172
2360
  }
3173
2361
  catch (error) {
3174
- writeConfigMutationFailure('config:reset', pluginName, rawOptions.json === true, error);
3175
- return;
3176
- }
3177
- if (outcome.cancelled)
3178
- return;
3179
- const receipt = outcome.value;
3180
- if (rawOptions.json) {
3181
- writeJsonReceipt(io.stdout, receipt);
2362
+ writeConfigMutationFailure('config:reset', selector, rawOptions.json === true, error);
3182
2363
  }
2364
+ });
2365
+ contextualCommand(config.command('apply').argument('<plugin>'))
2366
+ .option('--target <target>', 'limit existing-output refresh to this platform target', collectUniqueOption)
2367
+ .option('-y, --yes', 'confirm reviewed output changes', false)
2368
+ .option('--script-timeout <ms>', 'override plugin preApply timeout in milliseconds')
2369
+ .action(async (selector, rawOptions) => {
2370
+ const context = createActionContext('config:apply', rawOptions);
2371
+ const work = await configContext(context, rawOptions, true);
2372
+ const targets = rawOptions.target?.map((target) => normalizeWriteRequest(() => parsePlatformTargetValue(target)));
2373
+ const receipt = await runActionWithContext(context, () => withResolvedConfigMutationPlugin(context, selector, async ({ plugin, pluginId, source }, awHomeMutationToken) => ({
2374
+ ...await applyPluginConfig({ plugin, scope: work.context, context: work.context, targets,
2375
+ cliVersion: resolveCliVersion(), cwd: context.cwd, env: context.env, awHomeMutationToken,
2376
+ nativeCliRunner: context.nativeCliRunner, sharedNativeApplyConfirmed: rawOptions.yes === true,
2377
+ preApplyConfirmed: rawOptions.yes === true, rebuildRuntime: { ...context, scriptTimeoutMs: resolveScriptTimeoutMs(rawOptions) } }),
2378
+ command: 'config:apply', plugin: plugin.manifest.name, pluginId, sourceId: source.sourceId,
2379
+ context: work,
2380
+ }), true));
2381
+ const result = buildConfigMutationReceipt('config:apply', { ...receipt,
2382
+ ok: receipt.overallState === 'succeeded' });
2383
+ if (rawOptions.json)
2384
+ writeJsonReceipt(io.stdout, result);
3183
2385
  else {
3184
- writeLine(io.stdout, receipt.deleted
3185
- ? `已重置 ${receipt.plugin} 的${configLayerLabel(receipt.scope)}配置。`
3186
- : `${receipt.plugin} 没有可重置的${configLayerLabel(receipt.scope)}配置。`);
3187
- writeConfigApplyNotice(io.stderr, receipt);
2386
+ writeLine(io.stdout, `配置应用:${result.ok ? '完成' : '部分失败'}。`);
2387
+ writeConfigApplyNotice(io.stderr, { ...result, warnings: [] });
3188
2388
  }
3189
- commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2389
+ commandExitState.exitCode = result.ok ? 0 : 1;
3190
2390
  });
3191
- program
3192
- .command('status')
3193
- .argument('[plugins...]')
3194
- .option('-s, --scope <scope>', 'filter by scope')
3195
- .option('-p, --platform <platform>', 'filter by platform')
3196
- .option('-v, --verbose', 'show status details and command diagnostics', false)
3197
- .option('-D, --debug', 'write debug diagnostics to a log file', false)
3198
- .option('--log-file <path>', 'write debug diagnostics to the specified file')
3199
- .option('-j, --json', 'print status as JSON', false)
3200
- .action(async (plugins, rawOptions) => {
2391
+ contextualCommand(plugin.command('status'))
2392
+ .description('Observe user and current project demand and output state')
2393
+ .action(async (rawOptions) => {
3201
2394
  const context = createActionContext('status', rawOptions);
3202
- const result = await runActionWithContext(context, () => statusCommandV8({
3203
- plugins: plugins ?? [],
3204
- cwd,
3205
- env: context.env,
3206
- cliVersion: resolveCliVersion(),
3207
- platform: rawOptions.platform,
3208
- scope: rawOptions.scope,
3209
- json: rawOptions.json,
3210
- verbose: rawOptions.verbose,
2395
+ await showWorkContext(context, rawOptions);
2396
+ const result = await runActionWithContext(context, () => readRecoveryStatus({
2397
+ context: recoveryContext(rawOptions), cwd: context.cwd, env: context.env, cliVersion: resolveCliVersion(),
2398
+ nativeCliRunner: context.nativeCliRunner,
3211
2399
  }));
3212
- commandExitState.exitCode = result.hasErrors ? 1 : 0;
3213
- writeLine(io.stdout, result.output);
3214
- for (const source of result.payload.catalog.sources) {
3215
- if (rawOptions.json && source.error)
3216
- writeLine(io.stderr, `${source.id}: ${source.error}`);
3217
- }
3218
- for (const error of result.payload.errors) {
3219
- if (rawOptions.json && error.code !== 'nativeStateUnreadable' && error.code !== 'stateInventoryFailed') {
3220
- writeLine(io.stderr, error.message);
3221
- }
3222
- }
2400
+ if (rawOptions.json)
2401
+ writeJsonReceipt(io.stdout, result);
2402
+ else
2403
+ renderRecoveryStatus(io.stdout, result);
2404
+ commandExitState.exitCode = result.errors.length ? 1 : 0;
3223
2405
  });
3224
2406
  Object.assign(program, { __awCommandExitState: commandExitState });
3225
2407
  return program;
@@ -3251,9 +2433,16 @@ export async function runCli(argv, options = {}) {
3251
2433
  ? argv[1]
3252
2434
  : null;
3253
2435
  const jsonReviewCommand = reviewCommand !== null && argv.includes('--json');
2436
+ const jsonMaterialize = argv[0] === 'skill' && argv[1] === 'materialize'
2437
+ && (argv.includes('--json') || argv.includes('-j'));
2438
+ const jsonConfigCommand = jsonConfigReadRequested(argv);
2439
+ const explicitWorkspace = argv.some((token) => token === '--workspace-root' || token.startsWith('--workspace-root='));
2440
+ const authoringPluginCommand = argv[0] === 'plugin'
2441
+ && (['build', 'test', 'validate', 'init', 'add-skill'].includes(argv[1] ?? '')
2442
+ || ['remove', 'status'].includes(argv[1] ?? '') && explicitWorkspace);
3254
2443
  const jsonAuthoringCommand = argv.includes('--json')
3255
2444
  && ((argv[0] === 'catalog' && ['init', 'context', 'version', 'build', 'validate', 'status', 'publish'].includes(argv[1] ?? ''))
3256
- || (argv[0] === 'plugin' && ['build', 'test', 'validate', 'status', 'remove'].includes(argv[1] ?? ''))
2445
+ || authoringPluginCommand
3257
2446
  || reviewCommand !== null)
3258
2447
  ? `${argv[0]}:${argv[1]}`
3259
2448
  : null;
@@ -3263,7 +2452,8 @@ export async function runCli(argv, options = {}) {
3263
2452
  && ['activate', 'status', 'deactivate'].includes(argv[2] ?? '')
3264
2453
  ? argv[2]
3265
2454
  : null;
3266
- const jsonWriteRequest = jsonWriteCommandRequest(argv);
2455
+ const recovery = recoveryRequest(argv);
2456
+ const jsonRecoveryRequest = recovery?.json ? recovery : null;
3267
2457
  const jsonConfigMutation = jsonConfigMutationRequest(argv);
3268
2458
  try {
3269
2459
  const authoringScriptByCommand = {
@@ -3273,12 +2463,13 @@ export async function runCli(argv, options = {}) {
3273
2463
  validate: 'validate-plugin.mjs',
3274
2464
  status: 'plugin-status.mjs',
3275
2465
  };
3276
- const authoringScript = argv[0] === 'plugin'
2466
+ const authoringScript = authoringPluginCommand
3277
2467
  ? authoringScriptByCommand[argv[1] ?? '']
3278
2468
  : argv[0] === 'skill' && argv[1] === 'validate'
3279
2469
  ? 'validate-skill.mjs'
3280
2470
  : undefined;
3281
- if (authoringScript && !argv.includes('--help') && !argv.includes('-h')) {
2471
+ if (authoringScript && ((!argv.includes('--help') && !argv.includes('-h'))
2472
+ || explicitWorkspace && ['remove', 'status'].includes(argv[1] ?? ''))) {
3282
2473
  const authoringArgs = argv.slice(2);
3283
2474
  let authoringEnv = options.env ?? process.env;
3284
2475
  if (argv[0] === 'plugin'
@@ -3388,7 +2579,7 @@ export async function runCli(argv, options = {}) {
3388
2579
  }
3389
2580
  let catalogPublicationActionStarted = false;
3390
2581
  let configMutationActionStarted = false;
3391
- const programIo = jsonCatalogDevCommand || jsonWriteRequest || jsonReviewCommand
2582
+ const programIo = jsonCatalogDevCommand || jsonRecoveryRequest || jsonReviewCommand || jsonMaterialize || jsonConfigCommand
3392
2583
  ? { ...io, stderr: () => undefined }
3393
2584
  : jsonConfigMutation
3394
2585
  ? {
@@ -3432,6 +2623,16 @@ export async function runCli(argv, options = {}) {
3432
2623
  return program.__awCommandExitState?.exitCode ?? 0;
3433
2624
  }
3434
2625
  catch (error) {
2626
+ if (jsonMaterialize && error instanceof CommanderError) {
2627
+ if (error.exitCode === 0)
2628
+ return 0;
2629
+ writeJsonReceipt(io.stdout, {
2630
+ receiptSchemaVersion: 5, command: 'skill:materialize', ok: false,
2631
+ selectedSkills: [], dependencies: [], configDigest: null, materializationDigest: null,
2632
+ outputDigest: null, errors: [{ code: 'invalidArguments', message: error.message }],
2633
+ });
2634
+ return 2;
2635
+ }
3435
2636
  if (reviewCommand && error instanceof CommanderError) {
3436
2637
  if (error.exitCode === 0)
3437
2638
  return 0;
@@ -3464,9 +2665,36 @@ export async function runCli(argv, options = {}) {
3464
2665
  }));
3465
2666
  return 1;
3466
2667
  }
3467
- if (jsonWriteRequest) {
3468
- writeCommandFailureReceipt(io.stdout, jsonWriteRequest, error);
3469
- return 1;
2668
+ if (jsonConfigCommand && !jsonConfigMutation) {
2669
+ if (error instanceof CommanderError && error.exitCode === 0)
2670
+ return 0;
2671
+ const failure = configMutationFailure(error, options.env ?? process.env, options.cwd ?? process.cwd());
2672
+ writeJsonReceipt(io.stdout, { ok: false, command: `config:${argv[2] ?? ''}`, errors: [failure] });
2673
+ return failure.code === 'invalidArguments' ? 2 : 1;
2674
+ }
2675
+ if (recovery) {
2676
+ if (error instanceof CommanderError && error.exitCode === 0)
2677
+ return 0;
2678
+ const failure = writeCommandFailure(error);
2679
+ if (argv[0] === 'project' || argv.some((value) => value === '-p' || value === '--platform' || value.startsWith('--platform='))) {
2680
+ failure.message += '\n使用 aw plugin intent、aw plugin targets 或 aw plugin install/remove/sync/cleanup/status。安装归属使用 --scope;工作上下文使用 --context。';
2681
+ }
2682
+ if (jsonRecoveryRequest) {
2683
+ if (recovery.command === 'status')
2684
+ writeJsonReceipt(io.stdout, {
2685
+ schemaVersion: 1, kind: 'aw-recovery-status', scope: recovery.scope, ok: false,
2686
+ intent: { state: 'unreadable', roots: null },
2687
+ selection: { agents: [], origin: 'none', preferences: 'unreadable' },
2688
+ inventory: null, extraObserved: [], coverage: null, counts: null, allSatisfied: null,
2689
+ errors: [failure],
2690
+ });
2691
+ else
2692
+ writeJsonReceipt(io.stdout, { ...recoveryFailureReceipt(recovery.command, recovery.scope, error),
2693
+ blockers: [failure], errors: [failure] });
2694
+ }
2695
+ else
2696
+ writeLine(io.stderr, failure.message);
2697
+ return failure.code === 'invalidArguments' ? 2 : 1;
3470
2698
  }
3471
2699
  if (jsonConfigMutation) {
3472
2700
  if (error instanceof CommanderError && error.exitCode === 0)
@@ -3476,7 +2704,15 @@ export async function runCli(argv, options = {}) {
3476
2704
  ? { command: jsonConfigMutation.command }
3477
2705
  : jsonConfigMutation, failure);
3478
2706
  writeLine(io.stderr, failure.message);
3479
- return 1;
2707
+ return failure.code === 'invalidArguments' ? 2 : 1;
2708
+ }
2709
+ if ((argv[0] === 'plugin' && argv[1] === 'config'
2710
+ || argv[0] === 'skill' && argv[1] === 'materialize')
2711
+ && (error instanceof CommanderError || error instanceof WriteCommandRequestError)) {
2712
+ if (error instanceof CommanderError)
2713
+ return error.exitCode === 0 ? 0 : 2;
2714
+ writeLine(io.stderr, error.message);
2715
+ return 2;
3480
2716
  }
3481
2717
  if (jsonAuthoringCommand === 'catalog:publish' && error instanceof CommanderError) {
3482
2718
  const failure = catalogPublicationFailure({