@lesliechan721/aw 0.1.0-beta.3 → 0.1.0-beta.30

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 (529) hide show
  1. package/dist/authoring/assets/aw-config.mjs +100 -501
  2. package/dist/authoring/assets/aw-config.mjs.map +1 -1
  3. package/dist/authoring/scripts/add-skill.mjs +4 -4
  4. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  5. package/dist/authoring/scripts/init-plugin.mjs +67 -97
  6. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  7. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +15 -0
  8. package/dist/authoring/scripts/lib/plugin-version-check.mjs +239 -232
  9. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  10. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
  11. package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
  12. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  13. package/dist/authoring/scripts/plugin-status.mjs +8 -11
  14. package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
  15. package/dist/authoring/scripts/remove-plugin.mjs +367 -0
  16. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
  17. package/dist/authoring/scripts/validate-plugin.mjs +307 -1173
  18. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  19. package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
  20. package/dist/scripts/aw-skill-eval-materializer.js +306 -0
  21. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  22. package/dist/scripts/bundle-plugins.d.ts +1 -2
  23. package/dist/scripts/bundle-plugins.js +24 -7
  24. package/dist/scripts/bundle-plugins.js.map +1 -1
  25. package/dist/scripts/plugin-staging.d.ts +7 -24
  26. package/dist/scripts/plugin-staging.js +22 -142
  27. package/dist/scripts/plugin-staging.js.map +1 -1
  28. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  29. package/dist/scripts/test-plugin-scripts.js +27 -75
  30. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  31. package/dist/src/adapters/helpers.js +2 -2
  32. package/dist/src/adapters/helpers.js.map +1 -1
  33. package/dist/src/authoring/workspace-commands.d.ts +26 -2
  34. package/dist/src/authoring/workspace-commands.js +44 -19
  35. package/dist/src/authoring/workspace-commands.js.map +1 -1
  36. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  37. package/dist/src/commands/aw-capabilities.js +256 -0
  38. package/dist/src/commands/aw-capabilities.js.map +1 -0
  39. package/dist/src/commands/catalog-dev-source.d.ts +154 -0
  40. package/dist/src/commands/catalog-dev-source.js +655 -0
  41. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  42. package/dist/src/commands/catalog-generation.d.ts +21 -0
  43. package/dist/src/commands/catalog-generation.js +9 -0
  44. package/dist/src/commands/catalog-generation.js.map +1 -0
  45. package/dist/src/commands/catalog-publication-inspection.d.ts +138 -0
  46. package/dist/src/commands/catalog-publication-inspection.js +757 -0
  47. package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
  48. package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
  49. package/dist/src/commands/catalog-publication-journal.js +292 -0
  50. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  51. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  52. package/dist/src/commands/catalog-publication-target.js +95 -0
  53. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  54. package/dist/src/commands/catalog-publish.d.ts +77 -7
  55. package/dist/src/commands/catalog-publish.js +746 -316
  56. package/dist/src/commands/catalog-publish.js.map +1 -1
  57. package/dist/src/commands/catalog-source.d.ts +60 -22
  58. package/dist/src/commands/catalog-source.js +311 -91
  59. package/dist/src/commands/catalog-source.js.map +1 -1
  60. package/dist/src/commands/catalog-status.d.ts +56 -0
  61. package/dist/src/commands/catalog-status.js +217 -0
  62. package/dist/src/commands/catalog-status.js.map +1 -0
  63. package/dist/src/commands/catalog-store.d.ts +104 -0
  64. package/dist/src/commands/catalog-store.js +685 -0
  65. package/dist/src/commands/catalog-store.js.map +1 -0
  66. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  67. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  68. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  69. package/dist/src/commands/console.js +28 -17
  70. package/dist/src/commands/console.js.map +1 -1
  71. package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
  72. package/dist/src/commands/dependency-operation-plan.js +289 -7
  73. package/dist/src/commands/dependency-operation-plan.js.map +1 -1
  74. package/dist/src/commands/init.d.ts +5 -11
  75. package/dist/src/commands/init.js +30 -51
  76. package/dist/src/commands/init.js.map +1 -1
  77. package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
  78. package/dist/src/commands/offline-cleanup-evidence.js +181 -0
  79. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  80. package/dist/src/commands/operation-plan.d.ts +127 -0
  81. package/dist/src/commands/operation-plan.js +71 -0
  82. package/dist/src/commands/operation-plan.js.map +1 -0
  83. package/dist/src/commands/operation-planner.d.ts +46 -0
  84. package/dist/src/commands/operation-planner.js +141 -0
  85. package/dist/src/commands/operation-planner.js.map +1 -0
  86. package/dist/src/commands/options.d.ts +9 -7
  87. package/dist/src/commands/options.js +83 -21
  88. package/dist/src/commands/options.js.map +1 -1
  89. package/dist/src/commands/plugin-capabilities.d.ts +22 -0
  90. package/dist/src/commands/plugin-capabilities.js +159 -0
  91. package/dist/src/commands/plugin-capabilities.js.map +1 -0
  92. package/dist/src/commands/rebuild-command.d.ts +77 -30
  93. package/dist/src/commands/rebuild-command.js +3993 -711
  94. package/dist/src/commands/rebuild-command.js.map +1 -1
  95. package/dist/src/commands/rebuild-integration.d.ts +120 -21
  96. package/dist/src/commands/rebuild-integration.js +2765 -1326
  97. package/dist/src/commands/rebuild-integration.js.map +1 -1
  98. package/dist/src/commands/rebuild-operations.d.ts +58 -30
  99. package/dist/src/commands/rebuild-operations.js +259 -319
  100. package/dist/src/commands/rebuild-operations.js.map +1 -1
  101. package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
  102. package/dist/src/commands/reconcile-operation-plan.js +47 -0
  103. package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
  104. package/dist/src/commands/review-inspection.d.ts +228 -0
  105. package/dist/src/commands/review-inspection.js +1341 -0
  106. package/dist/src/commands/review-inspection.js.map +1 -0
  107. package/dist/src/commands/review-preparation.d.ts +49 -0
  108. package/dist/src/commands/review-preparation.js +427 -0
  109. package/dist/src/commands/review-preparation.js.map +1 -0
  110. package/dist/src/commands/runtime.d.ts +1 -5
  111. package/dist/src/commands/runtime.js +24 -50
  112. package/dist/src/commands/runtime.js.map +1 -1
  113. package/dist/src/commands/skill-eval.d.ts +38 -0
  114. package/dist/src/commands/skill-eval.js +388 -0
  115. package/dist/src/commands/skill-eval.js.map +1 -0
  116. package/dist/src/commands/status-v6.d.ts +279 -0
  117. package/dist/src/commands/status-v6.js +2697 -0
  118. package/dist/src/commands/status-v6.js.map +1 -0
  119. package/dist/src/commands/sync-command.d.ts +40 -0
  120. package/dist/src/commands/sync-command.js +250 -0
  121. package/dist/src/commands/sync-command.js.map +1 -0
  122. package/dist/src/commands/wizard.d.ts +3 -4
  123. package/dist/src/commands/wizard.js +2 -2
  124. package/dist/src/commands/wizard.js.map +1 -1
  125. package/dist/src/commands/write-commands.d.ts +9 -3
  126. package/dist/src/commands/write-commands.js +124 -31
  127. package/dist/src/commands/write-commands.js.map +1 -1
  128. package/dist/src/constants.d.ts +7 -1
  129. package/dist/src/constants.js +7 -1
  130. package/dist/src/constants.js.map +1 -1
  131. package/dist/src/eval/manifest-v1.d.ts +71 -0
  132. package/dist/src/eval/manifest-v1.js +370 -0
  133. package/dist/src/eval/manifest-v1.js.map +1 -0
  134. package/dist/src/generators/local-plugin.d.ts +5 -1
  135. package/dist/src/generators/local-plugin.js +225 -104
  136. package/dist/src/generators/local-plugin.js.map +1 -1
  137. package/dist/src/index.d.ts +10 -4
  138. package/dist/src/index.js +1787 -476
  139. package/dist/src/index.js.map +1 -1
  140. package/dist/src/output/command-receipt.d.ts +43 -46
  141. package/dist/src/output/command-receipt.js +256 -80
  142. package/dist/src/output/command-receipt.js.map +1 -1
  143. package/dist/src/output/model.d.ts +3 -44
  144. package/dist/src/output/model.js +1 -144
  145. package/dist/src/output/model.js.map +1 -1
  146. package/dist/src/output/rebuild-progress.js +1 -13
  147. package/dist/src/output/rebuild-progress.js.map +1 -1
  148. package/dist/src/portable/commands.d.ts +46 -7
  149. package/dist/src/portable/commands.js +349 -37
  150. package/dist/src/portable/commands.js.map +1 -1
  151. package/dist/src/portable/materializer.d.ts +17 -4
  152. package/dist/src/portable/materializer.js +160 -38
  153. package/dist/src/portable/materializer.js.map +1 -1
  154. package/dist/src/portable/skills-cli.d.ts +15 -7
  155. package/dist/src/portable/skills-cli.js +66 -19
  156. package/dist/src/portable/skills-cli.js.map +1 -1
  157. package/dist/src/review/eval-source-input.d.ts +43 -0
  158. package/dist/src/review/eval-source-input.js +207 -0
  159. package/dist/src/review/eval-source-input.js.map +1 -0
  160. package/dist/src/review/review-profile.d.ts +17 -0
  161. package/dist/src/review/review-profile.js +177 -0
  162. package/dist/src/review/review-profile.js.map +1 -0
  163. package/dist/src/review/review-selection.d.ts +30 -0
  164. package/dist/src/review/review-selection.js +527 -0
  165. package/dist/src/review/review-selection.js.map +1 -0
  166. package/dist/src/review/review-types.d.ts +68 -0
  167. package/dist/src/review/review-types.js +16 -0
  168. package/dist/src/review/review-types.js.map +1 -0
  169. package/dist/src/review/skill-materialization.d.ts +13 -0
  170. package/dist/src/review/skill-materialization.js +75 -0
  171. package/dist/src/review/skill-materialization.js.map +1 -0
  172. package/dist/src/review/source-binding.d.ts +182 -0
  173. package/dist/src/review/source-binding.js +682 -0
  174. package/dist/src/review/source-binding.js.map +1 -0
  175. package/dist/src/tui/catalog-freshness.d.ts +1 -0
  176. package/dist/src/tui/catalog-freshness.js +2 -0
  177. package/dist/src/tui/catalog-freshness.js.map +1 -0
  178. package/dist/src/tui/catalog-generation.d.ts +21 -0
  179. package/dist/src/tui/catalog-generation.js +156 -0
  180. package/dist/src/tui/catalog-generation.js.map +1 -0
  181. package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
  182. package/dist/src/tui/catalog-sources/projection.js +17 -0
  183. package/dist/src/tui/catalog-sources/projection.js.map +1 -0
  184. package/dist/src/tui/catalog-sources/review.d.ts +3 -0
  185. package/dist/src/tui/catalog-sources/review.js +13 -0
  186. package/dist/src/tui/catalog-sources/review.js.map +1 -0
  187. package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
  188. package/dist/src/tui/catalog-sources/screens.js +40 -0
  189. package/dist/src/tui/catalog-sources/screens.js.map +1 -0
  190. package/dist/src/tui/catalog-sources/session.d.ts +28 -0
  191. package/dist/src/tui/catalog-sources/session.js +295 -0
  192. package/dist/src/tui/catalog-sources/session.js.map +1 -0
  193. package/dist/src/tui/catalog-sources/types.d.ts +53 -0
  194. package/dist/src/tui/catalog-sources/types.js +2 -0
  195. package/dist/src/tui/catalog-sources/types.js.map +1 -0
  196. package/dist/src/tui/catalog-store-task.d.ts +16 -0
  197. package/dist/src/tui/catalog-store-task.js +19 -0
  198. package/dist/src/tui/catalog-store-task.js.map +1 -0
  199. package/dist/src/tui/components.d.ts +128 -0
  200. package/dist/src/tui/components.js +251 -7
  201. package/dist/src/tui/components.js.map +1 -1
  202. package/dist/src/tui/console-app-v4.d.ts +14 -4
  203. package/dist/src/tui/console-app-v4.js +9248 -1461
  204. package/dist/src/tui/console-app-v4.js.map +1 -1
  205. package/dist/src/tui/controller-runtime.d.ts +13 -1
  206. package/dist/src/tui/controller-runtime.js +3338 -410
  207. package/dist/src/tui/controller-runtime.js.map +1 -1
  208. package/dist/src/tui/controller.d.ts +614 -40
  209. package/dist/src/tui/controller.js +3478 -708
  210. package/dist/src/tui/controller.js.map +1 -1
  211. package/dist/src/tui/dependency-graph.d.ts +81 -0
  212. package/dist/src/tui/dependency-graph.js +455 -0
  213. package/dist/src/tui/dependency-graph.js.map +1 -0
  214. package/dist/src/tui/diagnostic-projection.d.ts +23 -0
  215. package/dist/src/tui/diagnostic-projection.js +567 -0
  216. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  217. package/dist/src/tui/diagnostics.d.ts +30 -0
  218. package/dist/src/tui/diagnostics.js +76 -0
  219. package/dist/src/tui/diagnostics.js.map +1 -0
  220. package/dist/src/tui/glossary.d.ts +23 -0
  221. package/dist/src/tui/glossary.js +42 -0
  222. package/dist/src/tui/glossary.js.map +1 -0
  223. package/dist/src/tui/index.d.ts +11 -4
  224. package/dist/src/tui/index.js +8 -2
  225. package/dist/src/tui/index.js.map +1 -1
  226. package/dist/src/tui/input-display.d.ts +8 -0
  227. package/dist/src/tui/input-display.js +36 -0
  228. package/dist/src/tui/input-display.js.map +1 -0
  229. package/dist/src/tui/keys.d.ts +44 -0
  230. package/dist/src/tui/keys.js +43 -15
  231. package/dist/src/tui/keys.js.map +1 -1
  232. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  233. package/dist/src/tui/lifecycle-action-view.js +36 -0
  234. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  235. package/dist/src/tui/model.d.ts +235 -27
  236. package/dist/src/tui/model.js +1652 -304
  237. package/dist/src/tui/model.js.map +1 -1
  238. package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
  239. package/dist/src/tui/operation-diagnostic-screens.js +102 -0
  240. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  241. package/dist/src/tui/operation-history.d.ts +81 -0
  242. package/dist/src/tui/operation-history.js +135 -0
  243. package/dist/src/tui/operation-history.js.map +1 -0
  244. package/dist/src/tui/operation-review-planner.d.ts +21 -0
  245. package/dist/src/tui/operation-review-planner.js +1254 -0
  246. package/dist/src/tui/operation-review-planner.js.map +1 -0
  247. package/dist/src/tui/plugin-content.d.ts +23 -0
  248. package/dist/src/tui/plugin-content.js +336 -0
  249. package/dist/src/tui/plugin-content.js.map +1 -0
  250. package/dist/src/tui/plugin-write-task.d.ts +69 -0
  251. package/dist/src/tui/plugin-write-task.js +286 -0
  252. package/dist/src/tui/plugin-write-task.js.map +1 -0
  253. package/dist/src/tui/prompt.js +4 -15
  254. package/dist/src/tui/prompt.js.map +1 -1
  255. package/dist/src/tui/router.d.ts +183 -11
  256. package/dist/src/tui/router.js +352 -29
  257. package/dist/src/tui/router.js.map +1 -1
  258. package/dist/src/tui/screen-registry.d.ts +702 -0
  259. package/dist/src/tui/screen-registry.js +231 -0
  260. package/dist/src/tui/screen-registry.js.map +1 -0
  261. package/dist/src/tui/session-console-app.d.ts +6 -0
  262. package/dist/src/tui/session-console-app.js +13 -0
  263. package/dist/src/tui/session-console-app.js.map +1 -0
  264. package/dist/src/tui/session-host.d.ts +45 -0
  265. package/dist/src/tui/session-host.js +209 -0
  266. package/dist/src/tui/session-host.js.map +1 -0
  267. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  268. package/dist/src/tui/source-ref-presets.js +11 -0
  269. package/dist/src/tui/source-ref-presets.js.map +1 -0
  270. package/dist/src/tui/theme.d.ts +18 -10
  271. package/dist/src/tui/theme.js +18 -10
  272. package/dist/src/tui/theme.js.map +1 -1
  273. package/dist/src/tui/viewport.d.ts +20 -0
  274. package/dist/src/tui/viewport.js +29 -0
  275. package/dist/src/tui/viewport.js.map +1 -0
  276. package/dist/src/tui/views.js +31 -3
  277. package/dist/src/tui/views.js.map +1 -1
  278. package/dist/src/tui/wizard.js +11 -9
  279. package/dist/src/tui/wizard.js.map +1 -1
  280. package/dist/src/types.d.ts +242 -20
  281. package/dist/src/types.js +1 -0
  282. package/dist/src/types.js.map +1 -1
  283. package/dist/src/utils/agent-version.d.ts +7 -1
  284. package/dist/src/utils/agent-version.js +41 -8
  285. package/dist/src/utils/agent-version.js.map +1 -1
  286. package/dist/src/utils/artifact-plan.d.ts +5 -4
  287. package/dist/src/utils/artifact-plan.js +18 -7
  288. package/dist/src/utils/artifact-plan.js.map +1 -1
  289. package/dist/src/utils/artifact-renderer.d.ts +13 -7
  290. package/dist/src/utils/artifact-renderer.js +196 -219
  291. package/dist/src/utils/artifact-renderer.js.map +1 -1
  292. package/dist/src/utils/artifact-request.d.ts +21 -0
  293. package/dist/src/utils/artifact-request.js +82 -0
  294. package/dist/src/utils/artifact-request.js.map +1 -0
  295. package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
  296. package/dist/src/utils/aw-home-mutation-lock.js +32 -4
  297. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
  298. package/dist/src/utils/bundle-integrity.d.ts +0 -1
  299. package/dist/src/utils/bundle-integrity.js +0 -25
  300. package/dist/src/utils/bundle-integrity.js.map +1 -1
  301. package/dist/src/utils/bundle-layout.js +14 -29
  302. package/dist/src/utils/bundle-layout.js.map +1 -1
  303. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  304. package/dist/src/utils/catalog-capability-projection.js +14 -0
  305. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  306. package/dist/src/utils/catalog-digests.d.ts +10 -0
  307. package/dist/src/utils/catalog-digests.js +50 -2
  308. package/dist/src/utils/catalog-digests.js.map +1 -1
  309. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  310. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  311. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  312. package/dist/src/utils/catalog-generation-error.d.ts +16 -0
  313. package/dist/src/utils/catalog-generation-error.js +30 -0
  314. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  315. package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
  316. package/dist/src/utils/catalog-generation-resolver.js +273 -0
  317. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  318. package/dist/src/utils/catalog-generation-service.d.ts +172 -0
  319. package/dist/src/utils/catalog-generation-service.js +1271 -0
  320. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  321. package/dist/src/utils/catalog-generation-state.d.ts +22 -0
  322. package/dist/src/utils/catalog-generation-state.js +192 -0
  323. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  324. package/dist/src/utils/catalog-generation-store.d.ts +58 -0
  325. package/dist/src/utils/catalog-generation-store.js +452 -0
  326. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  327. package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
  328. package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
  329. package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
  330. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  331. package/dist/src/utils/catalog-operation-context.js +142 -0
  332. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  333. package/dist/src/utils/catalog-public-error.d.ts +7 -3
  334. package/dist/src/utils/catalog-public-error.js +28 -8
  335. package/dist/src/utils/catalog-public-error.js.map +1 -1
  336. package/dist/src/utils/catalog-runtime-projection.d.ts +4 -7
  337. package/dist/src/utils/catalog-runtime-projection.js +7 -102
  338. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  339. package/dist/src/utils/catalog-snapshot.d.ts +5 -3
  340. package/dist/src/utils/catalog-snapshot.js +11 -3
  341. package/dist/src/utils/catalog-snapshot.js.map +1 -1
  342. package/dist/src/utils/catalog-source-identity.d.ts +1 -0
  343. package/dist/src/utils/catalog-source-identity.js +21 -2
  344. package/dist/src/utils/catalog-source-identity.js.map +1 -1
  345. package/dist/src/utils/catalog-source-impact.d.ts +21 -0
  346. package/dist/src/utils/catalog-source-impact.js +344 -0
  347. package/dist/src/utils/catalog-source-impact.js.map +1 -0
  348. package/dist/src/utils/catalog-source-references.d.ts +3 -2
  349. package/dist/src/utils/catalog-source-references.js +195 -347
  350. package/dist/src/utils/catalog-source-references.js.map +1 -1
  351. package/dist/src/utils/catalog-sources.d.ts +15 -1
  352. package/dist/src/utils/catalog-sources.js +206 -27
  353. package/dist/src/utils/catalog-sources.js.map +1 -1
  354. package/dist/src/utils/codex-config.d.ts +2 -0
  355. package/dist/src/utils/codex-config.js +26 -5
  356. package/dist/src/utils/codex-config.js.map +1 -1
  357. package/dist/src/utils/codex-manager.d.ts +79 -0
  358. package/dist/src/utils/codex-manager.js +745 -0
  359. package/dist/src/utils/codex-manager.js.map +1 -0
  360. package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
  361. package/dist/src/utils/codex-marketplace-path.js +45 -0
  362. package/dist/src/utils/codex-marketplace-path.js.map +1 -0
  363. package/dist/src/utils/codex-native-observation.d.ts +49 -0
  364. package/dist/src/utils/codex-native-observation.js +154 -0
  365. package/dist/src/utils/codex-native-observation.js.map +1 -0
  366. package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
  367. package/dist/src/utils/codex-native-render-receipt.js +205 -0
  368. package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
  369. package/dist/src/utils/codex-reconciler.d.ts +126 -0
  370. package/dist/src/utils/codex-reconciler.js +298 -0
  371. package/dist/src/utils/codex-reconciler.js.map +1 -0
  372. package/dist/src/utils/codex-shared-native.d.ts +43 -0
  373. package/dist/src/utils/codex-shared-native.js +81 -0
  374. package/dist/src/utils/codex-shared-native.js.map +1 -0
  375. package/dist/src/utils/codex-skill-staging.d.ts +2 -0
  376. package/dist/src/utils/codex-skill-staging.js.map +1 -1
  377. package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
  378. package/dist/src/utils/codex-surface-analysis.js +247 -0
  379. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  380. package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
  381. package/dist/src/utils/dynamic-skill-tree.js +36 -6
  382. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  383. package/dist/src/utils/git-catalog-provider.d.ts +18 -0
  384. package/dist/src/utils/git-catalog-provider.js +170 -144
  385. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  386. package/dist/src/utils/local-marketplace.d.ts +15 -1
  387. package/dist/src/utils/local-marketplace.js +307 -84
  388. package/dist/src/utils/local-marketplace.js.map +1 -1
  389. package/dist/src/utils/manifest.d.ts +1 -4
  390. package/dist/src/utils/manifest.js +1 -4
  391. package/dist/src/utils/manifest.js.map +1 -1
  392. package/dist/src/utils/multi-source-catalog.d.ts +3 -9
  393. package/dist/src/utils/multi-source-catalog.js +54 -110
  394. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  395. package/dist/src/utils/native-cli.d.ts +55 -7
  396. package/dist/src/utils/native-cli.js +386 -57
  397. package/dist/src/utils/native-cli.js.map +1 -1
  398. package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
  399. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  400. package/dist/src/utils/ownership.d.ts +84 -20
  401. package/dist/src/utils/ownership.js +179 -222
  402. package/dist/src/utils/ownership.js.map +1 -1
  403. package/dist/src/utils/path-catalog-provider.d.ts +1 -1
  404. package/dist/src/utils/path-catalog-provider.js +54 -65
  405. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  406. package/dist/src/utils/paths.d.ts +0 -1
  407. package/dist/src/utils/paths.js +0 -1
  408. package/dist/src/utils/paths.js.map +1 -1
  409. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  410. package/dist/src/utils/plugin-config-impact.js +298 -0
  411. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  412. package/dist/src/utils/plugin-config-write.d.ts +52 -27
  413. package/dist/src/utils/plugin-config-write.js +990 -298
  414. package/dist/src/utils/plugin-config-write.js.map +1 -1
  415. package/dist/src/utils/plugin-config.d.ts +140 -93
  416. package/dist/src/utils/plugin-config.js +992 -980
  417. package/dist/src/utils/plugin-config.js.map +1 -1
  418. package/dist/src/utils/plugin-intent-state.d.ts +49 -0
  419. package/dist/src/utils/plugin-intent-state.js +320 -0
  420. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  421. package/dist/src/utils/plugin-scripts.d.ts +62 -125
  422. package/dist/src/utils/plugin-scripts.js +468 -525
  423. package/dist/src/utils/plugin-scripts.js.map +1 -1
  424. package/dist/src/utils/process-tree.d.ts +2 -2
  425. package/dist/src/utils/process-tree.js +1 -1
  426. package/dist/src/utils/process-tree.js.map +1 -1
  427. package/dist/src/utils/project-boundary.d.ts +11 -13
  428. package/dist/src/utils/project-boundary.js +28 -34
  429. package/dist/src/utils/project-boundary.js.map +1 -1
  430. package/dist/src/utils/safe-fs.d.ts +21 -0
  431. package/dist/src/utils/safe-fs.js +111 -0
  432. package/dist/src/utils/safe-fs.js.map +1 -0
  433. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  434. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  435. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  436. package/dist/src/utils/sensitive-content.d.ts +4 -0
  437. package/dist/src/utils/sensitive-content.js +36 -0
  438. package/dist/src/utils/sensitive-content.js.map +1 -0
  439. package/dist/src/utils/skill-validation.d.ts +1 -1
  440. package/dist/src/utils/skill-validation.js +1 -1
  441. package/dist/src/utils/skill-validation.js.map +1 -1
  442. package/dist/src/utils/state-paths.d.ts +1 -0
  443. package/dist/src/utils/state-paths.js +12 -2
  444. package/dist/src/utils/state-paths.js.map +1 -1
  445. package/dist/src/utils/target-eligibility.d.ts +8 -2
  446. package/dist/src/utils/target-eligibility.js +17 -8
  447. package/dist/src/utils/target-eligibility.js.map +1 -1
  448. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  449. package/dist/src/utils/windows-claude-shim.js +141 -0
  450. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  451. package/dist/vitest.config.js +91 -10
  452. package/dist/vitest.config.js.map +1 -1
  453. package/package.json +22 -19
  454. package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
  455. package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
  456. package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
  457. package/dist/scripts/aw-skill-eval-engine.js +0 -31
  458. package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
  459. package/dist/scripts/codex-eval-app-server.d.ts +0 -89
  460. package/dist/scripts/codex-eval-app-server.js +0 -1228
  461. package/dist/scripts/codex-eval-app-server.js.map +0 -1
  462. package/dist/scripts/codex-eval-isolation.d.ts +0 -61
  463. package/dist/scripts/codex-eval-isolation.js +0 -318
  464. package/dist/scripts/codex-eval-isolation.js.map +0 -1
  465. package/dist/scripts/codex-eval-process-management.d.ts +0 -8
  466. package/dist/scripts/codex-eval-process-management.js +0 -69
  467. package/dist/scripts/codex-eval-process-management.js.map +0 -1
  468. package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
  469. package/dist/scripts/codex-eval-process-tree.js +0 -803
  470. package/dist/scripts/codex-eval-process-tree.js.map +0 -1
  471. package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
  472. package/dist/scripts/codex-eval-runtime-config.js +0 -843
  473. package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
  474. package/dist/scripts/codex-trigger-eval.d.ts +0 -84
  475. package/dist/scripts/codex-trigger-eval.js +0 -968
  476. package/dist/scripts/codex-trigger-eval.js.map +0 -1
  477. package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
  478. package/dist/scripts/eval-plugin-skills-audited.js +0 -261
  479. package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
  480. package/dist/scripts/eval-plugin-skills.d.ts +0 -45
  481. package/dist/scripts/eval-plugin-skills.js +0 -1586
  482. package/dist/scripts/eval-plugin-skills.js.map +0 -1
  483. package/dist/scripts/eval-run-layout.d.ts +0 -17
  484. package/dist/scripts/eval-run-layout.js +0 -26
  485. package/dist/scripts/eval-run-layout.js.map +0 -1
  486. package/dist/scripts/plugin-eval-contract.d.ts +0 -185
  487. package/dist/scripts/plugin-eval-contract.js +0 -695
  488. package/dist/scripts/plugin-eval-contract.js.map +0 -1
  489. package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
  490. package/dist/scripts/plugin-eval-materializer.js +0 -421
  491. package/dist/scripts/plugin-eval-materializer.js.map +0 -1
  492. package/dist/scripts/skill-reporting.d.ts +0 -212
  493. package/dist/scripts/skill-reporting.js +0 -922
  494. package/dist/scripts/skill-reporting.js.map +0 -1
  495. package/dist/src/commands/status-v4.d.ts +0 -102
  496. package/dist/src/commands/status-v4.js +0 -844
  497. package/dist/src/commands/status-v4.js.map +0 -1
  498. package/dist/src/commands/status.d.ts +0 -93
  499. package/dist/src/commands/status.js +0 -442
  500. package/dist/src/commands/status.js.map +0 -1
  501. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
  502. package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
  503. package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
  504. package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
  505. package/dist/src/utils/catalog-manifest-v3.js +0 -3
  506. package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
  507. package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
  508. package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
  509. package/dist/src/utils/catalog-source-cache.d.ts +0 -61
  510. package/dist/src/utils/catalog-source-cache.js +0 -276
  511. package/dist/src/utils/catalog-source-cache.js.map +0 -1
  512. package/dist/src/utils/codex-project-surface.d.ts +0 -3
  513. package/dist/src/utils/codex-project-surface.js +0 -49
  514. package/dist/src/utils/codex-project-surface.js.map +0 -1
  515. package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
  516. package/dist/src/utils/lifecycle-active-store.js +0 -1276
  517. package/dist/src/utils/lifecycle-active-store.js.map +0 -1
  518. package/dist/src/utils/plugin-authoring-capabilities.d.ts +0 -23
  519. package/dist/src/utils/plugin-authoring-capabilities.js +0 -37
  520. package/dist/src/utils/plugin-authoring-capabilities.js.map +0 -1
  521. package/dist/src/utils/plugin-authoring-capabilities.json +0 -92
  522. package/dist/src/utils/plugin-release.d.ts +0 -2
  523. package/dist/src/utils/plugin-release.js +0 -2
  524. package/dist/src/utils/plugin-release.js.map +0 -1
  525. package/dist/src/utils/skill-frontmatter-capabilities.d.ts +0 -201
  526. package/dist/src/utils/skill-frontmatter-capabilities.js +0 -5
  527. package/dist/src/utils/skill-frontmatter-capabilities.js.map +0 -1
  528. package/dist/src/utils/skill-frontmatter-capabilities.json +0 -200
  529. /package/dist/authoring/scripts/{check-plugin-versions.d.mts → remove-plugin.d.mts} +0 -0
package/dist/src/index.js CHANGED
@@ -3,37 +3,50 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { spawn } from 'node:child_process';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { Command, CommanderError, Option } from 'commander';
6
+ import { Command, CommanderError, InvalidArgumentError, Option } from 'commander';
7
+ import { doctorEvalProviders } from '@lesliechan721/agent-skill-eval';
7
8
  import { initCommand } from './commands/init.js';
8
- import { addCatalogSourceCommand, explicitRefPolicy, listCatalogSources, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from './commands/catalog-source.js';
9
- import { cleanupWriteCommand as cleanupCommand, installWriteCommand as installCommand, reinstallWriteCommand as reinstallCommand, uninstallWriteCommand as uninstallCommand, upgradeWriteCommand as upgradeCommand, } from './commands/write-commands.js';
9
+ import { resolveCatalogWorkspaceRoot } from './commands/catalog-workspace-resolution.js';
10
+ import { addCatalogSourceCommand, preflightCatalogSourceMutation, explicitRefPolicy, listCatalogSources, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from './commands/catalog-source.js';
11
+ import { catalogSourceStatusCommand, syncCatalogSourceCommand, } from './commands/catalog-generation.js';
12
+ 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';
10
15
  import { formatInteractiveConsoleUnavailableMessage, runConsoleController, } from './commands/console.js';
11
16
  import { runCommandWizard, toWizardCandidates, WizardExecutionError, } from './commands/wizard.js';
12
17
  import { createCliExecutionContext, emitCommandEvent, } from './commands/runtime.js';
13
- import { loadConsoleItemsResult } from './commands/status.js';
14
- import { statusCommandV4 } from './commands/status-v4.js';
15
- import { normalizeUninstallCommandOptions, normalizeCleanupCommandOptions, resolveConfigReadResolution, resolveConfigProjectLayer, resolveConfigWriteScope, normalizeInstallCommandOptions, normalizeReinstallCommandOptions, normalizeUpgradeCommandOptions, } from './commands/options.js';
18
+ import { loadConsoleItemsResult, statusCommandV8 } from './commands/status-v6.js';
19
+ import { normalizeUninstallCommandOptions, normalizeCleanupCommandOptions, resolveConfigReadResolution, resolveConfigWriteScope, normalizeInstallCommandOptions, normalizeReinstallCommandOptions, resolveScriptTimeoutMs, } from './commands/options.js';
16
20
  import { isCommandWarningError, } from './types.js';
17
21
  import { formatWarnings } from './output/reporter.js';
18
22
  import { redactString, redactValue } from './output/redact.js';
19
23
  import { renderShellCommand, } from './output/equivalent-command.js';
20
24
  import { buildRebuildReceiptJson, buildInitReceiptJson, renderRebuildReceipt, renderInitReceipt, } from './output/command-receipt.js';
21
25
  import { resolveCliPackageRoot } from './utils/paths.js';
22
- import { coercePluginConfigUpdates, findConfigFieldEntry, listNamespacedPluginConfigStores, listPluginConfigStorePluginNames, readPluginConfigStore, renderPluginConfigValueForDisplay, resolveConfigRead, } from './utils/plugin-config.js';
23
- import { applyPluginConfigWrite, describeResetAffectedFields, resetPluginConfigWithHelper, } from './utils/plugin-config-write.js';
26
+ import { findConfigFieldEntry, formatPluginConfigSetRecoveryCommand, listNamespacedPluginConfigStores, listPluginConfigStorePluginNames, readPluginConfigStore, renderPluginConfigValueForDisplay, resolveConfigRead, } from './utils/plugin-config.js';
27
+ import { applyPluginConfigWrite, buildConfigMutationReceipt, configWriteStateForError, describeResetAffectedFields, resetPluginConfigWithHelper, } from './utils/plugin-config-write.js';
24
28
  import { assertStrictSemVer } from './utils/manifest.js';
25
29
  import { writeCandidateEligibility } from './utils/target-eligibility.js';
26
30
  import { assertCatalogSourceReferencesSupported, inventoryLegacyConfigReferences, } from './utils/catalog-source-references.js';
27
31
  import { parsePluginId, withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, } from './utils/multi-source-catalog.js';
28
- import { sanitizeCatalogSourceError } from './utils/catalog-public-error.js';
29
- import { formatConfigCommandValue, parseConfigValue, } from './utils/config-value.js';
32
+ import { sanitizeCatalogSourceError, sanitizeCatalogSourceErrorMessage, } from './utils/catalog-public-error.js';
33
+ import { catalogGenerationErrorCode } from './utils/catalog-generation-error.js';
34
+ import { resolveCatalogOperationContext, } from './utils/catalog-operation-context.js';
30
35
  import { withAwHomeMutationLock, } from './utils/aw-home-mutation-lock.js';
31
36
  import { installPortableSkills, materializePortableSkillsToDirectory, PortableCommandError, renderPortableReview, } from './portable/commands.js';
32
37
  import { SkillsCliError } from './portable/skills-cli.js';
33
38
  import { computeReviewComponentDigest, listReviewRecords, parseReviewAttachment, publishReviewRecord, showReviewRecord, verifyReviewRecord, } from './commands/review-publication.js';
39
+ import { createReviewInspectionFailure, getReviewInspectionExitCode, inspectReviewTarget, } from './commands/review-inspection.js';
40
+ import { prepareReviewTarget, } from './commands/review-preparation.js';
34
41
  import { resolveRepoRoot } from './utils/state-paths.js';
35
42
  import { runCatalogBuildCommand, runCatalogInitCommand, runCatalogValidateCommand, runCatalogVersionCommand, runPluginBuildCommand, runPluginTestCommand, } from './authoring/workspace-commands.js';
36
- import { runCatalogPublishCommand } from './commands/catalog-publish.js';
43
+ import { runCatalogPublishApplyCommand, runCatalogPublishPlanCommand, } from './commands/catalog-publish.js';
44
+ import { catalogPublicationFailure, CatalogPublicationInspectionError, } from './commands/catalog-publication-inspection.js';
45
+ import { getCatalogStatus, renderCatalogStatus } from './commands/catalog-status.js';
46
+ import { activateCatalogDevSource, catalogDevSourceError, createCatalogDevFailureReceipt, deactivateCatalogDevSource, getCatalogDevSourceStatus, } from './commands/catalog-dev-source.js';
47
+ import { getPluginCapabilitiesResult, parsePluginCapabilitySurface, renderPluginCapabilities, } from './commands/plugin-capabilities.js';
48
+ import { AwCapabilityUnsupportedError, checkCurrentAw, describeCurrentAw, inspectPluginRequirements, } from './commands/aw-capabilities.js';
49
+ import { prepareSkillEval } from './commands/skill-eval.js';
37
50
  function createDefaultIo() {
38
51
  return {
39
52
  stdout: (text) => process.stdout.write(text),
@@ -43,6 +56,62 @@ function createDefaultIo() {
43
56
  function writeLine(write, line) {
44
57
  write(`${line}\n`);
45
58
  }
59
+ function strictValueOption(flags, description, choices) {
60
+ let seen = false;
61
+ const option = new Option(flags, description);
62
+ return option.argParser((value) => {
63
+ if (seen)
64
+ throw new InvalidArgumentError(`${option.long} can be specified only once.`);
65
+ if (choices && !choices.includes(value)) {
66
+ throw new InvalidArgumentError(`${option.long} must be one of: ${choices.join(', ')}.`);
67
+ }
68
+ seen = true;
69
+ return value;
70
+ });
71
+ }
72
+ function collectUniqueOption(value, previous) {
73
+ return previous.includes(value) ? previous : [...previous, value];
74
+ }
75
+ function renderReviewSkills(write, skills, issues) {
76
+ for (const skill of skills) {
77
+ writeLine(write, `${skill.skill}\t${skill.profile.platform}/${skill.profile.scope}\t${skill.state}\t${skill.finalPath ?? '-'}`);
78
+ }
79
+ for (const issue of issues)
80
+ writeLine(write, `${issue.code}: ${issue.message}`);
81
+ }
82
+ function renderReviewInspection(write, receipt) {
83
+ writeLine(write, `Review inspection (${receipt.view}): ${receipt.ok ? 'ok' : 'failed'}`);
84
+ if (receipt.view === 'review') {
85
+ renderReviewSkills(write, receipt.skills, receipt.issues);
86
+ return;
87
+ }
88
+ for (const issue of receipt.issues)
89
+ writeLine(write, `${issue.code}: ${issue.message}`);
90
+ }
91
+ function renderReviewPreparation(write, receipt) {
92
+ writeLine(write, `Review preparation: ${receipt.ok ? 'complete' : 'failed'}`);
93
+ if (receipt.sourceBindingPath)
94
+ writeLine(write, `Source binding: ${receipt.sourceBindingPath}`);
95
+ renderReviewSkills(write, receipt.skills, receipt.issues);
96
+ }
97
+ function requestedReviewView(argv) {
98
+ const inline = argv.find((token) => token.startsWith('--view='));
99
+ if (inline === '--view=release')
100
+ return 'release';
101
+ const index = argv.indexOf('--view');
102
+ return index >= 0 && argv[index + 1] === 'release' ? 'release' : 'review';
103
+ }
104
+ function createReviewPreparationFailure(message, code = 'invalidArguments') {
105
+ return {
106
+ schemaVersion: 1,
107
+ command: 'review:prepare',
108
+ ok: false,
109
+ target: null,
110
+ sourceBindingPath: null,
111
+ skills: [],
112
+ issues: [{ code, path: null, message }],
113
+ };
114
+ }
46
115
  function writeWarnings(write, warnings, options = {}) {
47
116
  for (const warning of formatWarnings(warnings, options)) {
48
117
  writeLine(write, warning);
@@ -54,10 +123,266 @@ function resolveCliVersion() {
54
123
  return assertStrictSemVer(packageJson.version, 'packages/cli/package.json version');
55
124
  }
56
125
  function resolveCommandReceiptExitCode(result) {
57
- if (result.overallState === 'failed') {
58
- return 1;
126
+ return result.overallState === 'succeeded' ? 0 : 1;
127
+ }
128
+ class WriteCommandRequestError extends Error {
129
+ code = 'invalidArguments';
130
+ }
131
+ function normalizeWriteRequest(normalize) {
132
+ try {
133
+ return normalize();
134
+ }
135
+ catch (error) {
136
+ throw new WriteCommandRequestError(error instanceof Error ? error.message : String(error));
137
+ }
138
+ }
139
+ function writeCommandFailure(error) {
140
+ const message = redactString(error instanceof Error ? error.message : String(error));
141
+ if (error instanceof CommanderError || error instanceof WriteCommandRequestError) {
142
+ return { code: 'invalidArguments', message };
143
+ }
144
+ const explicitCode = error?.code;
145
+ if (typeof explicitCode === 'string' && /^[A-Za-z][A-Za-z0-9]*$/u.test(explicitCode)) {
146
+ return { code: explicitCode, message };
147
+ }
148
+ const separator = message.indexOf(':');
149
+ const prefix = separator > 0 ? message.slice(0, separator) : '';
150
+ return {
151
+ code: /^[A-Za-z][A-Za-z0-9]*$/u.test(prefix) ? prefix : 'writeCommandFailed',
152
+ message,
153
+ };
154
+ }
155
+ function preparseWriteReceiptRequest(argv) {
156
+ const request = { json: false, plugins: [] };
157
+ let positionalOnly = false;
158
+ for (let index = 1; index < argv.length; index += 1) {
159
+ const token = argv[index];
160
+ if (positionalOnly) {
161
+ request.plugins.push(token);
162
+ continue;
163
+ }
164
+ if (token === '--') {
165
+ positionalOnly = true;
166
+ continue;
167
+ }
168
+ if (token === '--json') {
169
+ request.json = true;
170
+ continue;
171
+ }
172
+ if (token === '--platform' || token === '--scope') {
173
+ const value = argv[index + 1];
174
+ if (token === '--platform')
175
+ request.platform = value;
176
+ else
177
+ request.scope = value;
178
+ index += 1;
179
+ continue;
180
+ }
181
+ if (token.startsWith('--platform=')) {
182
+ request.platform = token.slice('--platform='.length);
183
+ continue;
184
+ }
185
+ if (token.startsWith('--scope=')) {
186
+ request.scope = token.slice('--scope='.length);
187
+ continue;
188
+ }
189
+ if (token === '--dependency-scope' || token === '--script-timeout' || token === '--log-file') {
190
+ index += 1;
191
+ continue;
192
+ }
193
+ if (token.startsWith('--'))
194
+ continue;
195
+ if (token.startsWith('-') && token !== '-') {
196
+ const shortOptions = token.slice(1);
197
+ for (let offset = 0; offset < shortOptions.length; offset += 1) {
198
+ const option = shortOptions[offset];
199
+ if (option === 'j') {
200
+ request.json = true;
201
+ continue;
202
+ }
203
+ if (option === 'p' || option === 's') {
204
+ const value = shortOptions.slice(offset + 1) || argv[index + 1];
205
+ if (option === 'p')
206
+ request.platform = value;
207
+ else
208
+ request.scope = value;
209
+ if (shortOptions.length === offset + 1)
210
+ index += 1;
211
+ break;
212
+ }
213
+ }
214
+ continue;
215
+ }
216
+ request.plugins.push(token);
217
+ }
218
+ return request;
219
+ }
220
+ function jsonWriteCommandRequest(argv) {
221
+ const command = argv[0];
222
+ if (!['install', 'reinstall', 'uninstall', 'cleanup', 'sync'].includes(command ?? '')) {
223
+ return null;
224
+ }
225
+ const request = preparseWriteReceiptRequest(argv);
226
+ if (!request.json)
227
+ return null;
228
+ const rawPlatform = request.platform;
229
+ const platform = rawPlatform === 'codex' || rawPlatform === 'claude' || rawPlatform === 'both'
230
+ ? rawPlatform
231
+ : 'codex';
232
+ return {
233
+ command: command,
234
+ plugins: request.plugins,
235
+ platform,
236
+ scope: request.scope === 'project' ? 'project' : 'user',
237
+ };
238
+ }
239
+ function writeCommandFailureReceipt(write, request, error) {
240
+ const platforms = request.platform === 'both'
241
+ ? ['codex', 'claude']
242
+ : [request.platform];
243
+ writeJsonReceipt(write, buildRebuildReceiptJson(request.command, {
244
+ platforms,
245
+ plugins: request.plugins,
246
+ warnings: [],
247
+ overallState: 'failed',
248
+ errors: [writeCommandFailure(error)],
249
+ receiptTargets: request.plugins.flatMap((selector) => platforms.map((platform) => {
250
+ let pluginName = selector;
251
+ let sourceId;
252
+ try {
253
+ const parsed = parsePluginId(selector);
254
+ pluginName = parsed.pluginName;
255
+ sourceId = parsed.sourceId;
256
+ }
257
+ catch {
258
+ // Keep the raw selector in the failed receipt.
259
+ }
260
+ return {
261
+ pluginName,
262
+ pluginId: selector,
263
+ ...(sourceId ? { sourceId } : {}),
264
+ platform,
265
+ scope: request.scope,
266
+ };
267
+ })),
268
+ }));
269
+ }
270
+ function scanConfigMutationShortOptions(token, command) {
271
+ if (!/^-[^-].*/u.test(token))
272
+ return { json: false, consumesNextValue: false };
273
+ let json = false;
274
+ for (let index = 1; index < token.length; index += 1) {
275
+ const option = token[index];
276
+ if (option === 's') {
277
+ return {
278
+ json,
279
+ consumesNextValue: index === token.length - 1,
280
+ };
281
+ }
282
+ if (option === 'j') {
283
+ json = true;
284
+ continue;
285
+ }
286
+ if (option === 'v' || option === 'D' || (option === 'y' && command === 'config:reset')) {
287
+ continue;
288
+ }
289
+ break;
290
+ }
291
+ return { json, consumesNextValue: false };
292
+ }
293
+ function jsonConfigMutationRequest(argv) {
294
+ if (argv[0] !== 'config' || !['set', 'unset', 'reset'].includes(argv[1] ?? '')) {
295
+ return null;
296
+ }
297
+ const command = `config:${argv[1]}`;
298
+ let json = false;
299
+ let plugin;
300
+ const valueOptions = new Set(['--scope', '--value-json', '--preset', '--log-file']);
301
+ for (let index = 2; index < argv.length; index += 1) {
302
+ const token = argv[index];
303
+ if (token === '--')
304
+ break;
305
+ if (valueOptions.has(token)) {
306
+ index += 1;
307
+ continue;
308
+ }
309
+ if (token === '--json') {
310
+ json = true;
311
+ continue;
312
+ }
313
+ const shortOptions = scanConfigMutationShortOptions(token, command);
314
+ if (shortOptions.json)
315
+ json = true;
316
+ if (shortOptions.consumesNextValue)
317
+ index += 1;
318
+ if (token.startsWith('-'))
319
+ continue;
320
+ plugin ??= token;
321
+ }
322
+ if (!json)
323
+ return null;
324
+ return {
325
+ command,
326
+ ...(plugin ? { plugin } : {}),
327
+ };
328
+ }
329
+ function configMutationFailure(error, env, cwd) {
330
+ const configWrite = configWriteStateForError(error);
331
+ if (error instanceof CommanderError) {
332
+ return {
333
+ code: 'invalidArguments',
334
+ message: 'error: invalid config mutation arguments',
335
+ configWrite,
336
+ };
337
+ }
338
+ const message = catalogPublicationFailure({
339
+ error,
340
+ env,
341
+ sensitiveRoots: [cwd],
342
+ }).message;
343
+ if (message.includes('Usage:'))
344
+ return { code: 'invalidArguments', message, configWrite };
345
+ const generationCode = catalogGenerationErrorCode(error);
346
+ if (generationCode === 'catalogGenerationUnavailable'
347
+ || generationCode === 'catalogGenerationSourceMissing') {
348
+ return { code: 'catalogSourceUnavailable', message, configWrite };
349
+ }
350
+ if (generationCode !== 'catalogGenerationFailed')
351
+ return { code: generationCode, message, configWrite };
352
+ const explicitCode = error?.code;
353
+ if (typeof explicitCode === 'string' && /^[A-Za-z][A-Za-z0-9]*$/u.test(explicitCode)) {
354
+ return { code: explicitCode, message, configWrite };
355
+ }
356
+ if (message.includes('legacyStateUnsupported'))
357
+ return { code: 'legacyStateUnsupported', message, configWrite };
358
+ if (message.includes('sourceChanged'))
359
+ return { code: 'sourceChanged', message, configWrite };
360
+ if (message.includes('sourceMissing'))
361
+ return { code: 'sourceMissing', message, configWrite };
362
+ if (message.startsWith('Cannot resolve required catalog sources:')) {
363
+ return { code: 'catalogSourceUnavailable', message, configWrite };
364
+ }
365
+ const prefix = message.match(/^([A-Za-z][A-Za-z0-9]*):/u)?.[1];
366
+ return { code: prefix ?? 'configMutationFailed', message, configWrite };
367
+ }
368
+ function writeConfigMutationFailureReceipt(write, request, failure) {
369
+ let plugin;
370
+ try {
371
+ if (request.plugin) {
372
+ parsePluginId(request.plugin);
373
+ plugin = request.plugin;
374
+ }
59
375
  }
60
- return 0;
376
+ catch {
377
+ // Do not echo an invalid selector from raw argv in an early failure receipt.
378
+ }
379
+ writeJsonReceipt(write, buildConfigMutationReceipt(request.command, {
380
+ ok: false,
381
+ overallState: 'failed',
382
+ configWrite: failure.configWrite,
383
+ ...(plugin ? { plugin } : {}),
384
+ errors: [failure],
385
+ }));
61
386
  }
62
387
  async function runAuthoringScript(options) {
63
388
  const scriptPath = path.join(resolveCliPackageRoot(), 'dist', 'authoring', 'scripts', options.scriptName);
@@ -100,47 +425,54 @@ function optionTokens(flag, value) {
100
425
  function writeCommandUsage(command) {
101
426
  const cardinality = command === 'reinstall' || command === 'cleanup' ? '<plugin>' : '<plugin...>';
102
427
  const scope = command === 'cleanup' ? ' --scope <project|user>' : ' [--scope <project|user>]';
103
- const all = command === 'upgrade' ? ' 或 aw upgrade --all --platform both --scope user' : '';
104
428
  return [
105
429
  `${command} 命令缺少必要参数。`,
106
430
  `Usage: aw ${command} ${cardinality} --platform <codex|claude|both>${scope}`,
107
- `Example: aw ${command} example-plugin --platform codex --scope user${all}`,
431
+ `Example: aw ${command} example-plugin --platform codex --scope user`,
108
432
  ].join('\n');
109
433
  }
110
434
  function configCommandUsage(command) {
111
435
  const argumentsByCommand = {
112
436
  list: '<plugin>',
113
437
  get: '<plugin> [key]',
114
- set: '<plugin> <key> <value>',
438
+ set: '<plugin> <field> --scope <project|user> (--value-json <json> | --value-stdin | --preset <id>)',
115
439
  unset: '<plugin> <key>',
116
440
  reset: '<plugin> --scope <project|user> --yes',
117
441
  };
118
442
  return [
119
443
  `config ${command} 命令缺少必要参数。`,
120
444
  `Usage: aw config ${command} ${argumentsByCommand[command]}`,
121
- `Example: aw config ${command} example-plugin${command === 'set' ? ' key value' : command === 'unset' ? ' key' : command === 'reset' ? ' --scope user --yes' : ''}`,
445
+ `Example: aw config ${command} example-plugin${command === 'set' ? ' mode --scope user --value-json \'"safe"\'' : command === 'unset' ? ' key' : command === 'reset' ? ' --scope user --yes' : ''}`,
122
446
  ].join('\n');
123
447
  }
124
- function assertUpgradeAllConflict(pluginNames, all) {
125
- if (all && pluginNames.length > 0) {
126
- throw new Error('upgrade 命令不能同时使用插件名和 `--all`。');
448
+ async function readUtf8Stdin(stdin) {
449
+ const chunks = [];
450
+ for await (const chunk of stdin) {
451
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : Buffer.from(chunk));
127
452
  }
453
+ return Buffer.concat(chunks).toString('utf8');
128
454
  }
129
455
  function equivalentWriteCommand(command, selection, rawOptions) {
130
456
  return shellCommand(buildEquivalentWriteCommandTokens(command, selection, rawOptions));
131
457
  }
132
458
  export function buildEquivalentWriteCommandTokens(command, selection, rawOptions) {
133
- const all = command === 'upgrade' && rawOptions.all === true;
134
459
  return [
135
460
  'aw',
136
461
  command,
137
- ...(all ? ['--all'] : []),
138
462
  '--platform',
139
463
  selection.platform ?? 'both',
140
464
  '--scope',
141
465
  selection.scope ?? 'user',
142
- ...(command === 'cleanup' ? [] : optionTokens('--script-timeout', rawOptions.scriptTimeout)),
143
- ...(!all && selection.plugins.length > 0 ? ['--', ...selection.plugins] : []),
466
+ ...(rawOptions.allowManagedCodexInstall === true
467
+ ? ['--allow-managed-codex-install']
468
+ : []),
469
+ ...(command === 'install'
470
+ ? (rawOptions.dependencyScope ?? []).flatMap((value) => ['--dependency-scope', value])
471
+ : []),
472
+ ...((command === 'install' || command === 'reinstall')
473
+ ? optionTokens('--script-timeout', rawOptions.scriptTimeout)
474
+ : []),
475
+ ...(selection.plugins.length > 0 ? ['--', ...selection.plugins] : []),
144
476
  ];
145
477
  }
146
478
  function equivalentConfigCommand(options) {
@@ -152,39 +484,14 @@ function equivalentConfigCommandProjection(resolve) {
152
484
  equivalentCommandTokens: (selection) => buildEquivalentConfigCommandTokens(resolve(selection)),
153
485
  };
154
486
  }
155
- function configSetEquivalentOptions(selection, candidates, rawOptions) {
156
- const field = candidates.find((entry) => entry.pluginName === selection.plugins[0])
157
- ?.configSchema?.[selection.key ?? ''];
158
- const placeholder = configFieldPlaceholder(field);
159
- return {
160
- command: 'set',
161
- plugin: selection.plugins[0],
162
- key: selection.key,
163
- value: placeholder
164
- ? rawOptions.valueJson ? JSON.stringify(placeholder) : placeholder
165
- : formatConfigCommandValue(selection.value, rawOptions.valueJson === true),
166
- scope: selection.scope ?? 'user',
167
- projectLayer: selection.projectLayer,
168
- valueJson: rawOptions.valueJson,
169
- };
170
- }
171
- function configFieldPlaceholder(field) {
172
- if (field?.sensitive)
173
- return '<sensitive-value>';
174
- if (field?.type === 'file')
175
- return '<file-path>';
176
- if (field?.type === 'directory')
177
- return '<directory-path>';
178
- return undefined;
179
- }
180
487
  export function buildEquivalentConfigCommandTokens(options) {
181
488
  return [
182
489
  'aw',
183
490
  'config',
184
491
  options.command,
185
492
  ...optionTokens('--scope', options.scope),
186
- ...(options.projectLayer === 'local' ? ['--local'] : []),
187
- ...(options.valueJson ? ['--value-json'] : []),
493
+ ...optionTokens('--value-json', options.valueJson),
494
+ ...optionTokens('--preset', options.preset),
188
495
  ...(options.confirmed ? ['--yes'] : []),
189
496
  '--',
190
497
  options.plugin,
@@ -207,7 +514,6 @@ async function loadWizardCandidates(context, rawOptions) {
207
514
  cwd: context.cwd,
208
515
  scope: rawOptions.scope,
209
516
  platform: rawOptions.platform,
210
- includeCatalogOnly: true,
211
517
  }, context);
212
518
  return toWizardCandidates(loaded.items);
213
519
  }
@@ -235,7 +541,6 @@ async function loadStoredConfigWizardCandidates(context, locations, options = {}
235
541
  cwd: context.cwd,
236
542
  env: context.env,
237
543
  scope: location.scope,
238
- ...(location.scope === 'project' ? { projectLayer: location.projectLayer } : {}),
239
544
  });
240
545
  for (const store of stores) {
241
546
  const keys = Object.keys(store.values);
@@ -243,12 +548,11 @@ async function loadStoredConfigWizardCandidates(context, locations, options = {}
243
548
  continue;
244
549
  const existing = storesByPlugin.get(store.pluginId) ?? {
245
550
  sourceId: store.sourceId,
246
- sourceIdentity: store.sourceIdentity,
551
+ ...(store.binding.kind === 'source'
552
+ ? { sourceIdentity: store.binding.sourceIdentity }
553
+ : {}),
247
554
  keys: new Set(),
248
555
  };
249
- if (existing.sourceIdentity !== store.sourceIdentity) {
250
- throw new Error(`sourceChanged: ${store.pluginId} config stores have different source identities.`);
251
- }
252
556
  for (const key of keys)
253
557
  existing.keys.add(key);
254
558
  storesByPlugin.set(store.pluginId, existing);
@@ -260,26 +564,53 @@ async function loadStoredConfigWizardCandidates(context, locations, options = {}
260
564
  pluginName: pluginId,
261
565
  pluginId,
262
566
  sourceId: store.sourceId,
263
- sourceIdentity: store.sourceIdentity,
567
+ ...(store.sourceIdentity ? { sourceIdentity: store.sourceIdentity } : {}),
264
568
  targets: [],
265
569
  configKeys: [...store.keys].sort((left, right) => left.localeCompare(right)),
266
570
  }));
267
571
  }
268
572
  function configReadStoreLocations(rawOptions) {
269
- if (rawOptions.local)
270
- return [{ scope: 'project', projectLayer: 'local' }];
271
573
  if (rawOptions.scope === 'user')
272
- return [{ scope: 'user', projectLayer: 'shared' }];
574
+ return [{ scope: 'user' }];
273
575
  if (rawOptions.scope === 'project')
274
- return [{ scope: 'project', projectLayer: 'shared' }];
576
+ return [{ scope: 'project' }];
275
577
  return [
276
- { scope: 'user', projectLayer: 'shared' },
277
- { scope: 'project', projectLayer: 'shared' },
278
- { scope: 'project', projectLayer: 'local' },
578
+ { scope: 'user' },
579
+ { scope: 'project' },
279
580
  ];
280
581
  }
281
582
  function overallResultState(value) {
282
- return value.overallState;
583
+ return value.overallState === 'succeeded' ? 'succeeded' : 'failed';
584
+ }
585
+ async function resolveStoredConfigResetIdentity(options) {
586
+ const parsed = parsePluginId(options.selector);
587
+ if (parsed.sourceId) {
588
+ return {
589
+ pluginName: parsed.pluginName,
590
+ pluginId: options.selector,
591
+ sourceId: parsed.sourceId,
592
+ };
593
+ }
594
+ const matches = (await listNamespacedPluginConfigStores({
595
+ cwd: options.context.cwd,
596
+ env: options.context.env,
597
+ scope: options.scope,
598
+ })).filter((store) => store.pluginName === parsed.pluginName);
599
+ if (matches.length === 0) {
600
+ throw new Error(`configStoreMissing: ${options.selector} has no config in the selected store.`);
601
+ }
602
+ if (matches.length > 1) {
603
+ throw new Error(`configTargetAmbiguous: ${options.selector} exists in multiple source namespaces: ${matches.map((store) => store.pluginId).join(', ')}. Use a full pluginId.`);
604
+ }
605
+ const [match] = matches;
606
+ return {
607
+ pluginName: match.pluginName,
608
+ pluginId: match.pluginId,
609
+ sourceId: match.sourceId,
610
+ ...(match.binding.kind === 'source'
611
+ ? { sourceIdentity: match.binding.sourceIdentity }
612
+ : { logicalIdentity: match.binding.logicalIdentity }),
613
+ };
283
614
  }
284
615
  async function resolveStoredOnlyConfigIdentity(options) {
285
616
  const parsed = parsePluginId(options.selector);
@@ -289,33 +620,43 @@ async function resolveStoredOnlyConfigIdentity(options) {
289
620
  cwd: options.context.cwd,
290
621
  env: options.context.env,
291
622
  scope: location.scope,
292
- ...(location.scope === 'project' ? { projectLayer: location.projectLayer } : {}),
293
623
  })))).flat().filter((store) => store.pluginId === options.selector);
294
624
  if (stores.length === 0)
295
625
  throw new Error(`Plugin "${options.selector}" does not have a namespaced config store.`);
296
- const identities = new Set(stores.map((store) => store.sourceIdentity));
297
- if (identities.size !== 1)
298
- throw new Error(`sourceChanged: ${options.selector} config stores have different source identities.`);
299
- const sourceIdentity = [...identities][0];
300
- await withResolvedMultiSourceCatalog({
626
+ const sourceBindings = stores.flatMap((store) => store.binding.kind === 'source'
627
+ ? [store.binding.sourceIdentity] : []);
628
+ const catalogBindings = stores.flatMap((store) => store.binding.kind === 'catalog'
629
+ ? [store.binding.logicalIdentity] : []);
630
+ if (new Set(sourceBindings).size > 1 || new Set(catalogBindings).size > 1) {
631
+ throw new Error(`identityMismatch: ${options.selector} config stores have different bindings.`);
632
+ }
633
+ const currentSource = await withResolvedMultiSourceCatalog({
301
634
  cliVersion: resolveCliVersion(),
302
- freshnessPolicy: 'cacheFirst',
303
635
  sourceIds: [parsed.sourceId],
304
636
  env: options.context.env,
305
637
  consume: async (catalog) => {
306
638
  const source = catalog.sources.find((entry) => entry.sourceId === parsed.sourceId);
307
- if (!source?.resolution)
639
+ if (!source?.resolution) {
308
640
  throw source?.error ?? new Error(`Catalog source "${parsed.sourceId}" is unavailable.`);
309
- if (source.resolution.sourceIdentity !== sourceIdentity) {
310
- throw new Error(`sourceChanged: ${options.selector} config source identity does not match the current source.`);
311
641
  }
642
+ return {
643
+ sourceIdentity: source.resolution.sourceIdentity,
644
+ logicalIdentity: source.resolution.logicalIdentity,
645
+ };
312
646
  },
313
647
  });
648
+ if (sourceBindings[0] && sourceBindings[0] !== currentSource.sourceIdentity) {
649
+ throw new Error(`identityMismatch: ${options.selector} user config does not match the current source identity.`);
650
+ }
651
+ if (catalogBindings[0] && catalogBindings[0] !== currentSource.logicalIdentity) {
652
+ throw new Error(`identityMismatch: ${options.selector} project config does not match the current Catalog logical identity.`);
653
+ }
314
654
  return {
315
655
  pluginName: parsed.pluginName,
316
656
  pluginId: options.selector,
317
657
  sourceId: parsed.sourceId,
318
- sourceIdentity,
658
+ sourceIdentity: currentSource.sourceIdentity,
659
+ logicalIdentity: currentSource.logicalIdentity,
319
660
  };
320
661
  }
321
662
  async function resolveMultiSourceConfigRead(options) {
@@ -324,26 +665,21 @@ async function resolveMultiSourceConfigRead(options) {
324
665
  env: options.context.env,
325
666
  });
326
667
  assertCatalogSourceReferencesSupported(inventory);
327
- const projectLayer = resolveConfigProjectLayer(options.rawOptions);
328
668
  return withResolvedCatalogPlugin({
329
669
  selector: options.selector,
330
670
  cliVersion: resolveCliVersion(),
331
- freshnessPolicy: 'cacheFirst',
332
671
  env: options.context.env,
333
672
  consume: async ({ pluginId, source, plugin }) => {
334
673
  const result = await resolveConfigRead({
335
674
  pluginName: plugin.manifest.name,
336
675
  plugin,
337
- scope: options.rawOptions.local
338
- ? 'project'
339
- : resolveConfigReadResolution(options.rawOptions.scope),
340
- projectLayer,
676
+ scope: resolveConfigReadResolution(options.rawOptions.scope),
341
677
  cwd: options.context.cwd,
342
678
  env: options.context.env,
343
679
  });
344
680
  if (options.key !== undefined) {
345
681
  const field = findConfigFieldEntry(result.fields, options.key);
346
- if (!field && plugin.manifest.config?.schema?.[options.key] === undefined) {
682
+ if (!field && plugin.manifest.config?.fields?.[options.key] === undefined) {
347
683
  throw new Error(`Unknown config key "${options.key}".`);
348
684
  }
349
685
  if (field)
@@ -369,12 +705,10 @@ async function resolveMultiSourceConfigRead(options) {
369
705
  const result = await resolveConfigRead({
370
706
  sourceId: stored.sourceId,
371
707
  sourceIdentity: stored.sourceIdentity,
708
+ logicalIdentity: stored.logicalIdentity,
372
709
  pluginName: stored.pluginName,
373
710
  plugin: null,
374
- scope: options.rawOptions.local
375
- ? 'project'
376
- : resolveConfigReadResolution(options.rawOptions.scope),
377
- projectLayer,
711
+ scope: resolveConfigReadResolution(options.rawOptions.scope),
378
712
  cwd: options.context.cwd,
379
713
  env: options.context.env,
380
714
  });
@@ -387,36 +721,52 @@ async function resolveMultiSourceConfigRead(options) {
387
721
  return { ...stored, result };
388
722
  });
389
723
  }
390
- async function executeWriteCommand(command, pluginNames, rawOptions, context) {
724
+ export function createManagedCodexAuthorizer(rawOptions, context) {
725
+ if (rawOptions.allowManagedCodexInstall === true)
726
+ return async () => true;
727
+ if (!(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true)
728
+ || !context.prompt.confirm)
729
+ return undefined;
730
+ return async () => {
731
+ const response = await context.prompt.confirm({
732
+ message: '系统中没有可用的 Codex CLI。是否获取本次操作所需的 AW-managed Codex runtime?',
733
+ defaultValue: false,
734
+ });
735
+ return !response.cancelled && response.value === true;
736
+ };
737
+ }
738
+ async function executeWriteCommand(command, pluginNames, rawOptions, context, directCli = false) {
391
739
  const raw = { cwd: context.cwd, ...rawOptions };
392
- const commandOptions = command === 'install'
740
+ const commandOptions = normalizeWriteRequest(() => command === 'install'
393
741
  ? normalizeInstallCommandOptions(raw)
394
- : command === 'upgrade'
395
- ? normalizeUpgradeCommandOptions(raw)
396
- : command === 'uninstall'
397
- ? normalizeUninstallCommandOptions(raw)
398
- : command === 'cleanup'
399
- ? normalizeCleanupCommandOptions(raw)
400
- : normalizeReinstallCommandOptions(raw);
742
+ : command === 'uninstall'
743
+ ? normalizeUninstallCommandOptions(raw)
744
+ : command === 'cleanup'
745
+ ? normalizeCleanupCommandOptions(raw)
746
+ : normalizeReinstallCommandOptions(raw));
401
747
  await emitCommandEvent(context, {
402
748
  type: 'options:normalized',
403
749
  data: { plugins: pluginNames, options: commandOptions },
404
750
  });
751
+ const managedCodexAuthorizer = createManagedCodexAuthorizer(rawOptions, context);
752
+ const runtime = {
753
+ ...context,
754
+ directCli,
755
+ preApplyConfirmed: rawOptions.yes === true,
756
+ ...(managedCodexAuthorizer ? { managedCodexAuthorizer } : {}),
757
+ };
405
758
  switch (command) {
406
- case 'install': return installCommand(pluginNames, commandOptions, context);
407
- case 'upgrade': return upgradeCommand(pluginNames, commandOptions, context);
408
- case 'uninstall': return uninstallCommand(pluginNames, commandOptions, context);
409
- case 'cleanup': return cleanupCommand(pluginNames, commandOptions, context);
410
- case 'reinstall': return reinstallCommand(pluginNames, commandOptions, context);
759
+ case 'install': return installCommand(pluginNames, commandOptions, runtime);
760
+ case 'uninstall': return uninstallCommand(pluginNames, commandOptions, runtime);
761
+ case 'cleanup': return cleanupCommand(pluginNames, commandOptions, runtime);
762
+ case 'reinstall': return reinstallCommand(pluginNames, commandOptions, runtime);
411
763
  }
412
764
  }
413
765
  async function runWriteCommandAction(command, pluginNames, rawOptions, context) {
414
766
  if ((command === 'cleanup' || command === 'reinstall') && pluginNames.length > 1) {
415
- throw new Error(`${command} 命令当前只支持单个插件目标。`);
767
+ throw new WriteCommandRequestError(`${command} 命令当前只支持单个插件目标。`);
416
768
  }
417
- if (command === 'upgrade')
418
- assertUpgradeAllConflict(pluginNames, rawOptions.all === true);
419
- const missingPlugin = pluginNames.length === 0 && !(command === 'upgrade' && rawOptions.all === true);
769
+ const missingPlugin = pluginNames.length === 0;
420
770
  const missingPlatform = rawOptions.platform === undefined;
421
771
  const missingScope = command === 'cleanup' && rawOptions.scope === undefined;
422
772
  const missing = missingPlugin || missingPlatform || missingScope;
@@ -431,17 +781,16 @@ async function runWriteCommandAction(command, pluginNames, rawOptions, context)
431
781
  if (!missing) {
432
782
  return {
433
783
  cancelled: false,
434
- result: await executeWriteCommand(command, pluginNames, rawOptions, context),
784
+ result: await executeWriteCommand(command, pluginNames, rawOptions, context, true),
435
785
  };
436
786
  }
437
787
  if (!isWizardAvailable(context, rawOptions)) {
438
- throw new Error(writeCommandUsage(command));
788
+ throw new WriteCommandRequestError(writeCommandUsage(command));
439
789
  }
440
790
  const loaded = await loadConsoleItemsResult({
441
791
  cwd: context.cwd,
442
792
  scope: rawOptions.scope,
443
793
  platform: rawOptions.platform,
444
- includeCatalogOnly: true,
445
794
  }, context);
446
795
  if (loaded.catalogUnavailable) {
447
796
  throw new Error('插件目录不可用。请运行 `aw init` 或 `aw init --force`,或使用完整参数重试。');
@@ -587,34 +936,77 @@ async function runActionWithContext(context, action) {
587
936
  function writeJsonReceipt(write, payload) {
588
937
  writeLine(write, JSON.stringify(payload, null, 2));
589
938
  }
590
- function configLayerLabel(scope, projectLayer = 'shared') {
939
+ function configLayerLabel(scope) {
591
940
  if (scope === 'user') {
592
941
  return '个人';
593
942
  }
594
- return projectLayer === 'local' ? '当前项目(本机私有覆盖)' : '当前项目';
943
+ return '当前项目';
595
944
  }
596
945
  function writeConfigApplyNotice(write, receipt) {
597
- if (receipt.targetResults.length === 0) {
598
- const message = receipt.applySkippedReason === 'noConfigChange'
946
+ const sanitizeMessage = (message) => redactString(message)
947
+ .replace(/https?:\/\/\S+/giu, '[REDACTED URL]')
948
+ .replace(/(?:file:\/\/)?(?:\/[\w.@+-]+){2,}/gu, '[REDACTED PATH]')
949
+ .replace(/[A-Za-z]:\\(?:[^\s\\]+\\)+[^\s\\]*/gu, '[REDACTED PATH]');
950
+ const writeTargetFailures = () => {
951
+ for (const entry of receipt.apply.targetResults.filter((target) => target.state === 'failed' || target.state === 'skipped')) {
952
+ const reason = entry.message
953
+ ? `${entry.errorCode ? `[${entry.errorCode}] ` : ''}${sanitizeMessage(entry.message)}`
954
+ : entry.errorCode
955
+ ? `失败原因:${entry.errorCode}。`
956
+ : '未提供安全失败原因。';
957
+ const command = `aw reinstall ${receipt.plugin} -p ${entry.target.platform} -s ${entry.target.scope}`;
958
+ const recovery = entry.errorCode === 'invalidConfig'
959
+ ? ' 请修复无效配置后运行 aw sync。'
960
+ : entry.errorCode === 'preApplyAuthorizationRequired'
961
+ || entry.errorCode === 'sharedNativeConfigApplyConfirmationRequired'
962
+ || entry.errorCode === 'sharedNativeImpactAuthorizationRequired'
963
+ ? ` 请运行 ${command} --yes 重试。`
964
+ : entry.state === 'failed'
965
+ ? ` 请在解决失败原因后运行 ${command}。`
966
+ : '';
967
+ writeLine(write, `${entry.target.platform}/${entry.target.scope}:${reason}${recovery}`);
968
+ }
969
+ };
970
+ const targetResults = receipt.apply.targetResults;
971
+ if (receipt.apply.state === 'skipped') {
972
+ const message = receipt.apply.reason === 'noConfigChange'
599
973
  ? '配置内容没有变化,因此没有运行 reinstall。'
600
- : '配置已保存。当前没有可见的 AW 安装目标,因此没有应用配置。';
974
+ : receipt.apply.reason === 'noEffectiveConfigChange'
975
+ ? '配置存储已更新,但有效配置没有变化,因此没有运行 reinstall。'
976
+ : receipt.apply.reason === 'notRequested'
977
+ ? '配置已保存,但未请求应用配置。'
978
+ : '配置已保存。当前没有可见的 AW 安装目标,因此没有应用配置。';
601
979
  writeLine(write, message);
602
980
  }
603
- else {
604
- const completed = receipt.targetResults.filter((entry) => entry.state === 'completed').length;
605
- const failed = receipt.targetResults.filter((entry) => entry.state === 'failed');
606
- writeLine(write, `配置已保存。已重装 ${completed} 个当前可见目标;失败 ${failed.length} 个。`);
607
- for (const entry of failed) {
608
- writeLine(write, `${entry.target.platform}/${entry.target.scope}:${entry.message ?? 'reinstall 失败。'} `
609
- + `请重试 aw reinstall ${receipt.plugin} -p ${entry.target.platform} -s ${entry.target.scope}。`);
610
- }
981
+ else if (receipt.apply.state === 'unavailable') {
982
+ const fallbackMessages = {
983
+ invalidConfig: '可见目标的有效配置无效。',
984
+ schemaUnavailable: '配置 schema 不可用。',
985
+ sourceUnavailable: '插件来源不可用。',
986
+ effectiveConfigUnavailable: '无法读取有效配置。',
987
+ targetDiscoveryFailed: '无法发现可应用配置的目标。',
988
+ };
989
+ const detail = receipt.apply.error
990
+ ? `${receipt.apply.error.code}:${sanitizeMessage(receipt.apply.error.message)}`
991
+ : `${receipt.apply.reason}:${fallbackMessages[receipt.apply.reason]}`;
992
+ const recovery = receipt.apply.reason === 'invalidConfig'
993
+ ? '请修复无效配置后运行 aw sync。'
994
+ : receipt.apply.reason === 'effectiveConfigUnavailable'
995
+ ? '请修复配置后运行 aw sync。'
996
+ : receipt.apply.reason === 'schemaUnavailable' || receipt.apply.reason === 'sourceUnavailable'
997
+ ? '请修复插件来源或配置后运行 aw sync。'
998
+ : '请修复来源或配置后运行 aw sync。';
999
+ writeLine(write, `配置已保存,但当前无法应用配置。${detail} ${recovery}`);
1000
+ writeTargetFailures();
611
1001
  }
612
- if (receipt.otherProjectsRequireReinstall) {
613
- writeLine(write, '其它项目没有被扫描。请进入每个项目目录并运行对应的 aw reinstall 命令。');
1002
+ else {
1003
+ const completed = targetResults.filter((entry) => entry.state === 'completed').length;
1004
+ const failed = targetResults.filter((entry) => entry.state === 'failed').length;
1005
+ const skipped = targetResults.filter((entry) => entry.state === 'skipped').length;
1006
+ writeLine(write, `配置已保存。已重装 ${completed} 个当前可见目标;失败 ${failed} 个;跳过 ${skipped} 个。`);
1007
+ writeTargetFailures();
614
1008
  }
615
- }
616
- function writeProjectLocalConfigNotice(write) {
617
- writeLine(write, '当前显示的是当前项目的本机私有覆盖层,不作为 repo-visible artifact 渲染输入。');
1009
+ writeWarnings(write, receipt.warnings);
618
1010
  }
619
1011
  function renderConfigFieldState(field) {
620
1012
  if (field.valueState === 'invalid') {
@@ -634,13 +1026,15 @@ function writeConfigReadHuman(write, resolved, pluginName) {
634
1026
  }
635
1027
  if (field.kind === 'manifest' && field.valueState === 'invalid') {
636
1028
  const sourceScope = field.sourceLayer === 'project'
637
- ? ' -s project'
638
- : field.sourceLayer === 'local'
639
- ? ' -s project --local'
640
- : ' -s user';
641
- writeLine(write, ` aw config set ${pluginName} ${field.key} <value>${sourceScope}`);
642
- writeLine(write, ` aw config unset ${pluginName} ${field.key}${sourceScope}`);
643
- writeLine(write, ` aw config reset ${pluginName}${sourceScope}`);
1029
+ ? 'project'
1030
+ : 'user';
1031
+ const definition = resolved.fieldDefinitions?.[field.key];
1032
+ if (definition) {
1033
+ writeLine(write, ` ${formatPluginConfigSetRecoveryCommand(pluginName, field.key, definition, sourceScope)}`);
1034
+ }
1035
+ const scopeOption = ` -s ${sourceScope}`;
1036
+ writeLine(write, ` aw config unset ${pluginName} ${field.key}${scopeOption}`);
1037
+ writeLine(write, ` aw config reset ${pluginName}${scopeOption}`);
644
1038
  }
645
1039
  }
646
1040
  }
@@ -674,9 +1068,176 @@ export function buildProgram(options = {}) {
674
1068
  const plugin = program
675
1069
  .command('plugin')
676
1070
  .description('Create and validate AW plugin source');
1071
+ const self = program
1072
+ .command('self')
1073
+ .description('Describe and check this AW executable');
1074
+ self
1075
+ .command('describe')
1076
+ .description('Describe implemented AW capabilities')
1077
+ .requiredOption('--json', 'emit one machine-readable JSON object')
1078
+ .action(() => {
1079
+ writeJsonReceipt(io.stdout, describeCurrentAw(resolveCliVersion()));
1080
+ });
1081
+ self
1082
+ .command('check')
1083
+ .description('Check nearby plugin requirements against this AW executable')
1084
+ .option('--from <path>', 'start discovery from this file or directory')
1085
+ .requiredOption('--json', 'emit one machine-readable JSON object')
1086
+ .action(async (rawOptions) => {
1087
+ const result = await checkCurrentAw({
1088
+ awVersion: resolveCliVersion(),
1089
+ from: path.resolve(cwd, rawOptions.from ?? '.'),
1090
+ });
1091
+ writeJsonReceipt(io.stdout, result);
1092
+ commandExitState.exitCode = result.ok ? 0 : 1;
1093
+ });
1094
+ plugin
1095
+ .command('capabilities')
1096
+ .description('Show the current plugin manifest authoring surface')
1097
+ .option('--surface <surface>', 'manifest | build-config | extensions | slash-commands | mcp-apps | config | pre-apply | artifacts')
1098
+ .option('--json', 'emit one machine-readable JSON object', false)
1099
+ .action((rawOptions) => {
1100
+ try {
1101
+ const surface = rawOptions.surface === undefined
1102
+ ? undefined
1103
+ : parsePluginCapabilitySurface(rawOptions.surface);
1104
+ const result = getPluginCapabilitiesResult({
1105
+ awVersion: resolveCliVersion(),
1106
+ ...(surface ? { surface } : {}),
1107
+ });
1108
+ if (rawOptions.json)
1109
+ writeJsonReceipt(io.stdout, result);
1110
+ else
1111
+ for (const line of renderPluginCapabilities(result))
1112
+ writeLine(io.stdout, line);
1113
+ }
1114
+ catch (error) {
1115
+ writeLine(io.stderr, error instanceof Error ? error.message : String(error));
1116
+ commandExitState.exitCode = 1;
1117
+ }
1118
+ });
1119
+ plugin
1120
+ .command('requirements')
1121
+ .description('Inspect or update effective AW capabilities required by a plugin')
1122
+ .argument('<plugin-directory>', 'plugin source directory')
1123
+ .option('--workspace-root <root>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1124
+ .option('--add <capability>', 'add one declared AW capability')
1125
+ .option('--remove <capability>', 'remove one declared AW capability')
1126
+ .requiredOption('--json', 'emit one machine-readable JSON object')
1127
+ .action(async (pluginDirectory, rawOptions) => {
1128
+ try {
1129
+ const result = await inspectPluginRequirements({
1130
+ pluginDirectory,
1131
+ ...(rawOptions.workspaceRoot ? { workspaceRoot: rawOptions.workspaceRoot } : {}),
1132
+ ...(rawOptions.add ? { add: rawOptions.add } : {}),
1133
+ ...(rawOptions.remove ? { remove: rawOptions.remove } : {}),
1134
+ cwd,
1135
+ });
1136
+ writeJsonReceipt(io.stdout, result);
1137
+ if (result.observations.some((observation) => observation.blocking)) {
1138
+ commandExitState.exitCode = 1;
1139
+ }
1140
+ }
1141
+ catch (error) {
1142
+ writeJsonReceipt(io.stdout, {
1143
+ schemaVersion: 1,
1144
+ command: 'plugin:requirements',
1145
+ ok: false,
1146
+ error: error instanceof Error ? error.message : String(error),
1147
+ });
1148
+ commandExitState.exitCode = 1;
1149
+ }
1150
+ });
677
1151
  const skill = program
678
1152
  .command('skill')
679
1153
  .description('Install, materialize, or validate Agent Skills');
1154
+ const skillEval = skill
1155
+ .command('eval')
1156
+ .description('Prepare or diagnose deterministic Agent Skill Eval inputs');
1157
+ skillEval
1158
+ .command('doctor')
1159
+ .description('Inspect Agent Skill Eval provider readiness without model execution')
1160
+ .requiredOption('--json', 'emit one provider doctor JSON object')
1161
+ .action(async () => {
1162
+ try {
1163
+ writeJsonReceipt(io.stdout, await doctorEvalProviders({ sourceEnv: env }));
1164
+ }
1165
+ catch (error) {
1166
+ writeJsonReceipt(io.stdout, {
1167
+ ok: false,
1168
+ error: redactString(error instanceof Error ? error.message : String(error)),
1169
+ });
1170
+ commandExitState.exitCode = 1;
1171
+ }
1172
+ });
1173
+ skillEval
1174
+ .command('prepare')
1175
+ .description('Prepare one complete Agent Skill Eval input directory')
1176
+ .argument('<plugin>', 'canonical Workspace plugin name')
1177
+ .argument('<skill>', 'owner Skill name')
1178
+ .option('--suite <suite>', 'Eval suite ID')
1179
+ .option('--profile <profile>', 'Eval profile ID')
1180
+ .option('--workspace-root <root>', 'Catalog Workspace root')
1181
+ .option('--split <split>', 'case split filter')
1182
+ .option('--case <id>', 'case ID filter')
1183
+ .option('--runs <count>', 'runs per selected case')
1184
+ .option('--timeout-ms <milliseconds>', 'timeout for each provider attempt')
1185
+ .option('--baseline <mode>', 'append without-skill comparison')
1186
+ .option('--previous-version <runtime-skill-root>', 'append a previous-version comparison')
1187
+ .option('--keep-workspace', 'retain the preparation materialization workspace', false)
1188
+ .requiredOption('--output <spec-directory>', 'new EvalSpec directory')
1189
+ .requiredOption('--json', 'emit one preparation receipt JSON object')
1190
+ .action(async (pluginName, skillName, rawOptions) => {
1191
+ try {
1192
+ const workspace = await resolveCatalogWorkspaceRoot({
1193
+ cwd,
1194
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1195
+ mode: 'existing',
1196
+ env,
1197
+ });
1198
+ const runs = rawOptions.runs === undefined
1199
+ ? undefined
1200
+ : (/^[1-9][0-9]*$/u.test(rawOptions.runs) ? Number(rawOptions.runs) : Number.NaN);
1201
+ const timeoutMs = rawOptions.timeoutMs === undefined
1202
+ ? undefined
1203
+ : (/^[1-9][0-9]*$/u.test(rawOptions.timeoutMs) ? Number(rawOptions.timeoutMs) : Number.NaN);
1204
+ const receipt = await prepareSkillEval({
1205
+ workspaceRoot: workspace.workspaceRoot,
1206
+ workspaceRootResolvedFrom: workspace.workspaceRootResolvedFrom,
1207
+ pluginName,
1208
+ skillName,
1209
+ ...(rawOptions.suite === undefined ? {} : { suiteId: rawOptions.suite }),
1210
+ ...(rawOptions.profile === undefined ? {} : { profileId: rawOptions.profile }),
1211
+ output: rawOptions.output,
1212
+ cwd,
1213
+ env,
1214
+ ...(rawOptions.split === undefined ? {} : { split: rawOptions.split }),
1215
+ ...(rawOptions.case === undefined ? {} : { caseId: rawOptions.case }),
1216
+ ...(runs === undefined ? {} : { runs }),
1217
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
1218
+ ...(rawOptions.baseline === undefined ? {} : {
1219
+ baseline: rawOptions.baseline,
1220
+ }),
1221
+ ...(rawOptions.previousVersion === undefined ? {} : {
1222
+ previousVersion: rawOptions.previousVersion,
1223
+ }),
1224
+ ...(rawOptions.keepWorkspace === true ? { keepWorkspace: true } : {}),
1225
+ });
1226
+ writeJsonReceipt(io.stdout, receipt);
1227
+ }
1228
+ catch (error) {
1229
+ writeJsonReceipt(io.stdout, {
1230
+ schemaVersion: 1,
1231
+ command: 'skill:eval:prepare',
1232
+ ok: false,
1233
+ errors: [{
1234
+ code: 'evalPreparationFailed',
1235
+ message: redactString(error instanceof Error ? error.message : String(error)),
1236
+ }],
1237
+ });
1238
+ commandExitState.exitCode = 1;
1239
+ }
1240
+ });
680
1241
  const review = program
681
1242
  .command('review')
682
1243
  .description('Publish and inspect immutable review records');
@@ -685,6 +1246,81 @@ export function buildProgram(options = {}) {
685
1246
  recordDir: reviewRecordDir(repoRoot, record),
686
1247
  manifest: record.manifest,
687
1248
  });
1249
+ review
1250
+ .command('inspect')
1251
+ .description('Inspect current AW Review source and materialization facts')
1252
+ .argument('<target>', 'Catalog Workspace, Plugin, or source Skill path')
1253
+ .addOption(strictValueOption('--workspace-root <root>', 'explicit Catalog Workspace root'))
1254
+ .option('--skill <name>', 'select one declared Skill', collectUniqueOption, [])
1255
+ .addOption(strictValueOption('--platform <platform>', 'select codex, claude, or agent-skills', ['codex', 'claude', 'agent-skills']))
1256
+ .addOption(strictValueOption('--scope <scope>', 'select user or project scope', ['user', 'project']))
1257
+ .addOption(strictValueOption('--config <file>', 'review config JSON file'))
1258
+ .option('--materialization <receipt>', 'validate one aw-source-binding receipt', collectUniqueOption, [])
1259
+ .addOption(strictValueOption('--view <view>', 'select review or release view', ['review', 'release']).default('review'))
1260
+ .option('--json', 'emit one Review inspection JSON object', false)
1261
+ .action(async (target, rawOptions) => {
1262
+ try {
1263
+ const receipt = await inspectReviewTarget({
1264
+ cwd,
1265
+ target,
1266
+ ...(rawOptions.workspaceRoot === undefined
1267
+ ? {}
1268
+ : { workspaceRoot: rawOptions.workspaceRoot }),
1269
+ skills: rawOptions.skill,
1270
+ ...(rawOptions.platform === undefined ? {} : { platform: rawOptions.platform }),
1271
+ ...(rawOptions.scope === undefined ? {} : { scope: rawOptions.scope }),
1272
+ ...(rawOptions.config === undefined ? {} : { configFile: rawOptions.config }),
1273
+ materializations: [...new Set(rawOptions.materialization
1274
+ .map((entry) => path.resolve(cwd, entry)))],
1275
+ view: rawOptions.view,
1276
+ env,
1277
+ });
1278
+ if (rawOptions.json)
1279
+ writeJsonReceipt(io.stdout, receipt);
1280
+ else
1281
+ renderReviewInspection(io.stdout, receipt);
1282
+ commandExitState.exitCode = 0;
1283
+ }
1284
+ catch (error) {
1285
+ const receipt = createReviewInspectionFailure(error, rawOptions.view);
1286
+ if (rawOptions.json)
1287
+ writeJsonReceipt(io.stdout, receipt);
1288
+ else
1289
+ renderReviewInspection(io.stdout, receipt);
1290
+ commandExitState.exitCode = getReviewInspectionExitCode(error);
1291
+ }
1292
+ });
1293
+ review
1294
+ .command('prepare')
1295
+ .description('Prepare deterministic Skill finals for AW Review')
1296
+ .argument('<target>', 'Plugin or source Skill path')
1297
+ .addOption(strictValueOption('--workspace-root <root>', 'explicit Catalog Workspace root'))
1298
+ .option('--skill <name>', 'select one declared Skill', collectUniqueOption, [])
1299
+ .addOption(strictValueOption('--platform <platform>', 'select codex, claude, or agent-skills', ['codex', 'claude', 'agent-skills']).makeOptionMandatory())
1300
+ .addOption(strictValueOption('--scope <scope>', 'select user or project scope', ['user', 'project']).makeOptionMandatory())
1301
+ .addOption(strictValueOption('--config <file>', 'review config JSON file'))
1302
+ .addOption(strictValueOption('--out <new-dir>', 'new output directory').makeOptionMandatory())
1303
+ .option('--json', 'emit one Review preparation JSON object', false)
1304
+ .action(async (target, rawOptions) => {
1305
+ const result = await prepareReviewTarget({
1306
+ cwd,
1307
+ target,
1308
+ ...(rawOptions.workspaceRoot === undefined
1309
+ ? {}
1310
+ : { workspaceRoot: rawOptions.workspaceRoot }),
1311
+ skills: rawOptions.skill,
1312
+ platform: rawOptions.platform,
1313
+ scope: rawOptions.scope,
1314
+ ...(rawOptions.config === undefined ? {} : { configFile: rawOptions.config }),
1315
+ out: rawOptions.out,
1316
+ env,
1317
+ });
1318
+ if (rawOptions.json)
1319
+ writeJsonReceipt(io.stdout, result.receipt);
1320
+ else
1321
+ renderReviewPreparation(io.stdout, result.receipt);
1322
+ commandExitState.exitCode = result.exitCode;
1323
+ });
688
1324
  review
689
1325
  .command('digest')
690
1326
  .description('Compute one canonical workflow component digest')
@@ -837,15 +1473,17 @@ export function buildProgram(options = {}) {
837
1473
  .option('--skill <name...>', 'install only selected Skill names')
838
1474
  .option('--agent <id...>', 'install to explicit Agent IDs')
839
1475
  .option('-y, --yes', 'confirm the reviewed install', false)
840
- .option('-j, --json', 'emit one receipt v4 JSON object', false)
1476
+ .option('-j, --json', 'emit one receipt v5 JSON object', false)
841
1477
  .action(async (selector, rawOptions) => {
842
1478
  const context = createActionContext('skill:install', {});
843
1479
  const writeFailure = (error) => {
844
- const code = error instanceof PortableCommandError || error instanceof SkillsCliError
845
- ? error.code
846
- : 'portableInstallFailed';
1480
+ const capabilityError = error instanceof AwCapabilityUnsupportedError ? error : undefined;
1481
+ const code = capabilityError?.code
1482
+ ?? (error instanceof PortableCommandError || error instanceof SkillsCliError
1483
+ ? error.code
1484
+ : 'portableInstallFailed');
847
1485
  writeJsonReceipt(io.stdout, {
848
- receiptSchemaVersion: 4,
1486
+ receiptSchemaVersion: 5,
849
1487
  command: 'skill:install',
850
1488
  ok: false,
851
1489
  status: 'failed',
@@ -861,7 +1499,15 @@ export function buildProgram(options = {}) {
861
1499
  materializationDigest: null,
862
1500
  verification: { records: [], missing: [] },
863
1501
  warnings: [],
864
- failures: [{ code, message: `${code}: portable Skill 安装未执行。` }],
1502
+ failures: [{
1503
+ code,
1504
+ message: capabilityError?.message ?? `${code}: portable Skill 安装未执行。`,
1505
+ ...(capabilityError ? {
1506
+ plugin: capabilityError.plugin,
1507
+ missingCapabilities: capabilityError.missingCapabilities,
1508
+ availableCapabilities: capabilityError.availableCapabilities,
1509
+ } : {}),
1510
+ }],
865
1511
  });
866
1512
  };
867
1513
  if (rawOptions.json && !rawOptions.yes) {
@@ -931,7 +1577,7 @@ export function buildProgram(options = {}) {
931
1577
  .requiredOption('-s, --scope <scope>', 'project or user')
932
1578
  .requiredOption('--out <dir>', 'output directory')
933
1579
  .option('--skill <name...>', 'write only selected Skill names')
934
- .option('-j, --json', 'emit one receipt v4 JSON object', false)
1580
+ .option('-j, --json', 'emit one receipt v5 JSON object', false)
935
1581
  .action(async (selector, rawOptions) => {
936
1582
  const context = createActionContext('skill:materialize', {});
937
1583
  try {
@@ -952,11 +1598,13 @@ export function buildProgram(options = {}) {
952
1598
  catch (error) {
953
1599
  if (!rawOptions.json)
954
1600
  throw error;
955
- const code = error instanceof PortableCommandError || error instanceof SkillsCliError
956
- ? error.code
957
- : 'portableMaterializeFailed';
1601
+ const capabilityError = error instanceof AwCapabilityUnsupportedError ? error : undefined;
1602
+ const code = capabilityError?.code
1603
+ ?? (error instanceof PortableCommandError || error instanceof SkillsCliError
1604
+ ? error.code
1605
+ : 'portableMaterializeFailed');
958
1606
  writeJsonReceipt(io.stdout, {
959
- receiptSchemaVersion: 4,
1607
+ receiptSchemaVersion: 5,
960
1608
  command: 'skill:materialize',
961
1609
  ok: false,
962
1610
  plugin: { id: selector },
@@ -968,7 +1616,15 @@ export function buildProgram(options = {}) {
968
1616
  configDigest: null,
969
1617
  materializationDigest: null,
970
1618
  outputDigest: null,
971
- errors: [{ code, message: `${code}: portable Skill 导出未执行。` }],
1619
+ errors: [{
1620
+ code,
1621
+ message: capabilityError?.message ?? `${code}: portable Skill 导出未执行。`,
1622
+ ...(capabilityError ? {
1623
+ plugin: capabilityError.plugin,
1624
+ missingCapabilities: capabilityError.missingCapabilities,
1625
+ availableCapabilities: capabilityError.availableCapabilities,
1626
+ } : {}),
1627
+ }],
972
1628
  });
973
1629
  commandExitState.exitCode = 1;
974
1630
  }
@@ -980,7 +1636,7 @@ export function buildProgram(options = {}) {
980
1636
  '',
981
1637
  'During development, keep the current stable target when production content changes.',
982
1638
  'Next, run `aw plugin validate`, `aw plugin test`, `aw plugin build`, and `aw catalog build`.',
983
- 'Publish only as an explicit later step; inspect `aw catalog publish --help` first.',
1639
+ 'Publish only as an explicit later step; inspect `aw catalog publish plan --help` first.',
984
1640
  '',
985
1641
  ].join('\n'))
986
1642
  .argument('<plugin-name>', 'lowercase hyphen-case plugin name')
@@ -992,14 +1648,12 @@ export function buildProgram(options = {}) {
992
1648
  .option('--skill-short-description <text>', 'initial Skill UI summary (25-64 characters)')
993
1649
  .option('--skill-invocation <mode>', 'model | manual | claude-model-only (default: model)')
994
1650
  .option('--version <semver>', 'initial stable plugin version (default: 0.1.0)')
995
- .option('--min-version <semver>', 'compatibility.aw.minVersion')
996
- .option('--catalog-min-version <semver>', 'target catalog compatibility minimum')
997
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1651
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
998
1652
  .option('--no-skills', 'create the plugin without a skills directory')
999
1653
  .option('--with-mcp', 'add a minimal .mcp.json', false)
1000
1654
  .option('--with-apps', 'add a minimal .app.json', false)
1001
1655
  .option('--with-config-api', 'add AW config schema and helper', false)
1002
- .option('--with-lifecycle', 'add a lifecycle script sample', false);
1656
+ .option('--with-pre-apply', 'add an idempotent Node preApply environment check', false);
1003
1657
  plugin
1004
1658
  .command('add-skill')
1005
1659
  .description('Add one static Skill scaffold to an existing plugin')
@@ -1007,7 +1661,7 @@ export function buildProgram(options = {}) {
1007
1661
  '',
1008
1662
  'During development, keep the current stable target when production content changes.',
1009
1663
  'Next, run `aw plugin validate`, `aw plugin test`, `aw plugin build`, and `aw catalog build`.',
1010
- 'Publish only as an explicit later step; inspect `aw catalog publish --help` first.',
1664
+ 'Publish only as an explicit later step; inspect `aw catalog publish plan --help` first.',
1011
1665
  '',
1012
1666
  ].join('\n'))
1013
1667
  .argument('<plugin-name>', 'existing lowercase hyphen-case plugin name')
@@ -1015,28 +1669,39 @@ export function buildProgram(options = {}) {
1015
1669
  .option('--skill-description <text>', 'Skill goal and discovery description (required)')
1016
1670
  .option('--skill-short-description <text>', 'Skill UI summary (25-64 characters)')
1017
1671
  .option('--skill-invocation <mode>', 'model | manual | claude-model-only (default: model)')
1018
- .option('--workspace-root <path>', 'workspace root (default: current directory)');
1672
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)');
1673
+ plugin
1674
+ .command('remove')
1675
+ .description('Remove one committed and unchanged canonical plugin source tree')
1676
+ .argument('<plugin-name>', 'existing lowercase hyphen-case plugin name')
1677
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1678
+ .option('--yes', 'confirm plugin source deletion', false)
1679
+ .option('--json', 'emit one schema 1 JSON object', false);
1019
1680
  plugin
1020
1681
  .command('validate')
1021
1682
  .description('Validate one source plugin directory')
1022
1683
  .argument('<plugin-directory>', 'source plugin directory')
1023
- .option('--catalog-min-version <semver>', 'validate compatibility against a target catalog')
1024
1684
  .option('--strict-agent-skills', 'allow only Agent Skills portable core frontmatter', false)
1025
1685
  .option('--claude-skill-profile <profile>', 'minimum | current (default: minimum)')
1026
1686
  .option('--version-base <git-ref>', 'production bundle comparison base (default: HEAD)')
1027
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1687
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1028
1688
  .option('--json', 'emit one machine-readable JSON result', false);
1029
1689
  plugin
1030
1690
  .command('test')
1031
1691
  .description('Run deterministic Node tests declared by one source plugin')
1032
1692
  .argument('<plugin-directory>', 'source plugin directory')
1033
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1693
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1034
1694
  .option('--json', 'emit one machine-readable JSON result', false)
1035
1695
  .action(async (pluginDirectory, rawOptions) => {
1036
1696
  try {
1697
+ const workspace = await resolveCatalogWorkspaceRoot({
1698
+ cwd: path.resolve(cwd, pluginDirectory),
1699
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: path.resolve(cwd, rawOptions.workspaceRoot) }),
1700
+ mode: 'existing',
1701
+ });
1037
1702
  commandExitState.exitCode = await runPluginTestCommand({
1038
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1039
- pluginDirectory,
1703
+ ...workspace,
1704
+ pluginDirectory: path.resolve(cwd, pluginDirectory),
1040
1705
  json: rawOptions.json,
1041
1706
  io,
1042
1707
  });
@@ -1053,15 +1718,20 @@ export function buildProgram(options = {}) {
1053
1718
  .command('build')
1054
1719
  .description('Build and inspect one source plugin bundle')
1055
1720
  .argument('<plugin-directory>', 'source plugin directory')
1056
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1057
- .option('--out <path>', 'output path inside the workspace')
1721
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1722
+ .option('--out <path>', 'output path inside the Catalog Workspace')
1058
1723
  .option('--force', 'replace an existing generated output', false)
1059
1724
  .option('--json', 'emit one machine-readable JSON result', false)
1060
1725
  .action(async (pluginDirectory, rawOptions) => {
1061
1726
  try {
1727
+ const workspace = await resolveCatalogWorkspaceRoot({
1728
+ cwd: path.resolve(cwd, pluginDirectory),
1729
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: path.resolve(cwd, rawOptions.workspaceRoot) }),
1730
+ mode: 'existing',
1731
+ });
1062
1732
  await runPluginBuildCommand({
1063
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1064
- pluginDirectory,
1733
+ ...workspace,
1734
+ pluginDirectory: path.resolve(cwd, pluginDirectory),
1065
1735
  out: rawOptions.out,
1066
1736
  force: rawOptions.force,
1067
1737
  json: rawOptions.json,
@@ -1080,18 +1750,10 @@ export function buildProgram(options = {}) {
1080
1750
  .command('status')
1081
1751
  .description('Show current plugin status or compare against an explicit Git base')
1082
1752
  .argument('[plugin-name]', 'one explicit plugin name')
1083
- .option('--all', 'check every current workspace plugin', false)
1084
- .option('--base <git-ref>', 'explicit Git comparison base')
1085
- .option('--json', 'emit one schema v3 JSON document', false)
1086
- .option('--workspace-root <path>', 'workspace root (default: current directory)');
1087
- plugin
1088
- .command('check-versions')
1089
- .description('Deprecated alias for plugin status')
1090
- .argument('[plugin-name]', 'one explicit plugin name')
1091
- .option('--all', 'check every plugin changed relative to the Git base', false)
1753
+ .option('--all', 'check every current Catalog Workspace plugin', false)
1092
1754
  .option('--base <git-ref>', 'explicit Git comparison base')
1093
1755
  .option('--json', 'emit one schema v3 JSON document', false)
1094
- .option('--workspace-root <path>', 'workspace root (default: current directory)');
1756
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)');
1095
1757
  skill
1096
1758
  .command('validate')
1097
1759
  .description('Validate one Agent Skill directory')
@@ -1104,19 +1766,25 @@ export function buildProgram(options = {}) {
1104
1766
  .description('Build and manage catalogs');
1105
1767
  catalog
1106
1768
  .command('init')
1107
- .description('Initialize a local AW plugin workspace')
1108
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1769
+ .description('Initialize a local AW Catalog Workspace')
1770
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor, then Git worktree root, then current directory)')
1109
1771
  .option('--owner-name <name>', 'catalog owner name')
1110
1772
  .option('--owner-url <url>', 'catalog owner URL')
1111
1773
  .option('--display-name <name>', 'catalog display name')
1112
1774
  .option('--catalog-version <version>', 'initial stable or x.y.z-beta.N catalog version (default: 0.1.0)')
1113
- .option('--force', 'repair a matching managed workspace scaffold', false)
1775
+ .option('--force', 'repair a matching managed Catalog Workspace scaffold', false)
1114
1776
  .option('--json', 'emit one machine-readable JSON result', false)
1115
1777
  .action(async (rawOptions) => {
1116
1778
  try {
1779
+ const workspace = await resolveCatalogWorkspaceRoot({
1780
+ cwd,
1781
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1782
+ mode: 'initialize',
1783
+ env,
1784
+ });
1117
1785
  await runCatalogInitCommand({
1118
1786
  ...rawOptions,
1119
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1787
+ ...workspace,
1120
1788
  io,
1121
1789
  });
1122
1790
  }
@@ -1130,15 +1798,22 @@ export function buildProgram(options = {}) {
1130
1798
  });
1131
1799
  catalog
1132
1800
  .command('version')
1133
- .description('Read or set the workspace catalog version')
1801
+ .description('Read or set the Catalog version')
1134
1802
  .argument('[version]', 'stable or x.y.z-beta.N catalog version')
1135
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1803
+ .option('--bump <level>', 'bump the stable SemVer base: patch, minor, or major')
1804
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1136
1805
  .option('--json', 'emit one machine-readable JSON result', false)
1137
1806
  .action(async (version, rawOptions) => {
1138
1807
  try {
1808
+ const workspace = await resolveCatalogWorkspaceRoot({
1809
+ cwd,
1810
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1811
+ mode: 'existing',
1812
+ });
1139
1813
  await runCatalogVersionCommand({
1140
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1814
+ ...workspace,
1141
1815
  version,
1816
+ bump: rawOptions.bump,
1142
1817
  json: rawOptions.json,
1143
1818
  io,
1144
1819
  });
@@ -1154,14 +1829,19 @@ export function buildProgram(options = {}) {
1154
1829
  catalog
1155
1830
  .command('build')
1156
1831
  .description('Build a deterministic development Catalog snapshot')
1157
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1158
- .option('--out <path>', 'output path inside the workspace')
1832
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1833
+ .option('--out <path>', 'output path inside the Catalog Workspace')
1159
1834
  .option('--force', 'replace an existing generated output', false)
1160
1835
  .option('--json', 'emit one machine-readable JSON result', false)
1161
1836
  .action(async (rawOptions) => {
1162
1837
  try {
1838
+ const workspace = await resolveCatalogWorkspaceRoot({
1839
+ cwd,
1840
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1841
+ mode: 'existing',
1842
+ });
1163
1843
  await runCatalogBuildCommand({
1164
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1844
+ ...workspace,
1165
1845
  out: rawOptions.out,
1166
1846
  force: rawOptions.force,
1167
1847
  json: rawOptions.json,
@@ -1178,7 +1858,7 @@ export function buildProgram(options = {}) {
1178
1858
  });
1179
1859
  catalog
1180
1860
  .command('validate')
1181
- .description('Validate a schema 4 Catalog snapshot')
1861
+ .description('Validate a schema 7 Catalog snapshot')
1182
1862
  .argument('<snapshot-directory>', 'Catalog snapshot directory')
1183
1863
  .option('--json', 'emit one machine-readable JSON result', false)
1184
1864
  .action(async (snapshotDirectory, rawOptions) => {
@@ -1198,53 +1878,204 @@ export function buildProgram(options = {}) {
1198
1878
  }
1199
1879
  });
1200
1880
  catalog
1881
+ .command('status')
1882
+ .description('Inspect static Catalog Workspace, snapshot, and Git facts')
1883
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1884
+ .option('--json', 'emit one machine-readable JSON status object', false)
1885
+ .action(async (rawOptions) => {
1886
+ const receipt = await getCatalogStatus({
1887
+ cwd,
1888
+ workspaceRoot: rawOptions.workspaceRoot,
1889
+ env,
1890
+ });
1891
+ if (rawOptions.json)
1892
+ writeJsonReceipt(io.stdout, receipt);
1893
+ else
1894
+ io.stdout(renderCatalogStatus(receipt));
1895
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
1896
+ });
1897
+ const catalogPublish = catalog
1201
1898
  .command('publish')
1202
- .description('Publish a versioned Catalog snapshot to a configured Git remote')
1203
- .option('--workspace-root <path>', 'workspace root (default: current directory)')
1204
- .option('--remote <name>', 'configured Git remote name (default: origin)')
1899
+ .description('Plan or apply a versioned Catalog publication');
1900
+ catalogPublish
1901
+ .command('plan')
1902
+ .description('Prepare and inspect the exact Catalog publication plan')
1903
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1904
+ .option('--remote <name>', 'Git remote (default: origin, otherwise sole remote; explicit if ambiguous)')
1205
1905
  .option('--branch <name>', 'dedicated Catalog branch (default: catalog)')
1206
- .option('-y, --yes', 'confirm publication without a prompt', false)
1207
1906
  .option('--json', 'emit one machine-readable JSON result', false)
1208
1907
  .action(async (rawOptions) => {
1209
1908
  try {
1210
- const context = createActionContext('catalog:publish', {});
1211
- const result = await runCatalogPublishCommand({
1212
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1909
+ const result = await runCatalogPublishPlanCommand({
1910
+ cwd,
1911
+ workspaceRoot: rawOptions.workspaceRoot,
1213
1912
  remote: rawOptions.remote,
1214
1913
  branch: rawOptions.branch,
1215
- yes: rawOptions.yes,
1216
- json: rawOptions.json,
1217
1914
  env,
1218
- interactive: context.interactive,
1219
- ...(context.prompt.confirm ? {
1220
- confirm: async (message) => {
1221
- const response = await context.prompt.confirm({ message, defaultValue: false });
1222
- return !response.cancelled && response.value === true;
1223
- },
1224
- } : {}),
1225
1915
  io,
1226
1916
  });
1227
- if (!result)
1228
- return;
1229
1917
  if (rawOptions.json)
1230
1918
  writeJsonReceipt(io.stdout, result);
1231
1919
  else {
1232
- writeLine(io.stdout, `Published Catalog ${result.catalogVersion} to ${result.remote} (${result.pushUrl}).`);
1920
+ writeLine(io.stdout, `Workspace identity: ${result.workspaceIdentity}`);
1921
+ writeLine(io.stdout, `Source HEAD: ${result.sourceHead}`);
1922
+ writeLine(io.stdout, `Remote identity: ${result.descriptor.remote.identityDigest}`);
1923
+ writeLine(io.stdout, `Remote: ${result.remote} (${result.remoteUrl})`);
1233
1924
  writeLine(io.stdout, `Branch: refs/heads/${result.branch}`);
1925
+ writeLine(io.stdout, `Parent: ${result.parent ?? '<root>'}`);
1234
1926
  writeLine(io.stdout, `Tag: refs/tags/${result.tag}`);
1235
- writeLine(io.stdout, `Commit: ${result.commit}`);
1236
- writeLine(io.stdout, `Add it as a source: aw catalog source add <id> --git ${result.fetchUrl} --ref refs/heads/${result.branch}`);
1927
+ writeLine(io.stdout, `Snapshot digest: ${result.snapshotDigest}`);
1928
+ writeLine(io.stdout, `Expected commit: ${result.expectedCommit}`);
1929
+ writeLine(io.stdout, `Plan digest: ${result.planDigest}`);
1237
1930
  }
1238
1931
  }
1239
1932
  catch (error) {
1240
- const message = error instanceof Error ? error.message : String(error);
1241
- if (rawOptions.json)
1242
- writeJsonReceipt(io.stdout, {
1243
- command: 'catalog:publish',
1244
- ok: false,
1245
- errors: [{ message }],
1246
- });
1247
- writeLine(io.stderr, message);
1933
+ const failure = catalogPublicationFailure({
1934
+ error,
1935
+ env,
1936
+ sensitiveRoots: [
1937
+ cwd,
1938
+ ...(rawOptions.workspaceRoot ? [path.resolve(cwd, rawOptions.workspaceRoot)] : []),
1939
+ ],
1940
+ });
1941
+ if (rawOptions.json)
1942
+ writeJsonReceipt(io.stdout, failure);
1943
+ writeLine(io.stderr, failure.message);
1944
+ commandExitState.exitCode = 1;
1945
+ }
1946
+ });
1947
+ catalogPublish
1948
+ .command('apply')
1949
+ .description('Apply an authorized Catalog publication plan')
1950
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1951
+ .option('--remote <name>', 'Git remote (default: origin, otherwise sole remote; explicit if ambiguous)')
1952
+ .option('--branch <name>', 'dedicated Catalog branch (default: catalog)')
1953
+ .requiredOption('--expected-plan-digest <sha256>', 'exact plan digest returned by catalog publish plan')
1954
+ .option('-y, --yes', 'confirm publication without a prompt', false)
1955
+ .option('--json', 'emit one machine-readable JSON result', false)
1956
+ .action(async (rawOptions) => {
1957
+ try {
1958
+ const context = createActionContext('catalog:publish:apply', {});
1959
+ const result = await runCatalogPublishApplyCommand({
1960
+ cwd,
1961
+ workspaceRoot: rawOptions.workspaceRoot,
1962
+ remote: rawOptions.remote,
1963
+ branch: rawOptions.branch,
1964
+ expectedPlanDigest: rawOptions.expectedPlanDigest,
1965
+ yes: rawOptions.yes,
1966
+ json: rawOptions.json,
1967
+ env,
1968
+ interactive: context.interactive,
1969
+ ...(context.prompt.confirm ? {
1970
+ confirm: async (message) => {
1971
+ const response = await context.prompt.confirm({ message, defaultValue: false });
1972
+ return !response.cancelled && response.value === true;
1973
+ },
1974
+ } : {}),
1975
+ io,
1976
+ });
1977
+ if (!result)
1978
+ return;
1979
+ if (rawOptions.json)
1980
+ writeJsonReceipt(io.stdout, result);
1981
+ else {
1982
+ writeLine(io.stdout, `Published Catalog ${result.catalogVersion} to ${result.remote} (${result.pushUrl}).`);
1983
+ writeLine(io.stdout, `Branch: refs/heads/${result.branch}`);
1984
+ writeLine(io.stdout, `Tag: refs/tags/${result.tag}`);
1985
+ writeLine(io.stdout, `Commit: ${result.commit}`);
1986
+ writeLine(io.stdout, `Add it as a source: aw catalog source add <id> --git ${result.fetchUrl} --ref refs/heads/${result.branch}`);
1987
+ }
1988
+ }
1989
+ catch (error) {
1990
+ const failure = catalogPublicationFailure({
1991
+ error,
1992
+ env,
1993
+ sensitiveRoots: [
1994
+ cwd,
1995
+ ...(rawOptions.workspaceRoot ? [path.resolve(cwd, rawOptions.workspaceRoot)] : []),
1996
+ ],
1997
+ });
1998
+ if (rawOptions.json)
1999
+ writeJsonReceipt(io.stdout, failure);
2000
+ writeLine(io.stderr, failure.message);
2001
+ commandExitState.exitCode = 1;
2002
+ }
2003
+ });
2004
+ const catalogDev = catalog
2005
+ .command('dev')
2006
+ .description('Manage the current Catalog Workspace development source');
2007
+ catalogDev
2008
+ .command('activate')
2009
+ .option('--workspace-root <path>', 'explicit Catalog Workspace root')
2010
+ .option('--source-id <id>', 'explicit managed source ID')
2011
+ .option('--primary', 'make the development source primary', false)
2012
+ .option('--force-build', 'rebuild a current snapshot', false)
2013
+ .option('-y, --yes', 'confirm affected or unknown source references', false)
2014
+ .requiredOption('--json', 'emit one schema 2 JSON receipt')
2015
+ .action(async (rawOptions) => {
2016
+ try {
2017
+ const receipt = await activateCatalogDevSource({
2018
+ cwd,
2019
+ workspaceRoot: rawOptions.workspaceRoot,
2020
+ sourceId: rawOptions.sourceId,
2021
+ primary: rawOptions.primary,
2022
+ forceBuild: rawOptions.forceBuild,
2023
+ yes: rawOptions.yes,
2024
+ cliVersion: resolveCliVersion(),
2025
+ env,
2026
+ });
2027
+ writeJsonReceipt(io.stdout, receipt);
2028
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
2029
+ }
2030
+ catch (error) {
2031
+ const failure = catalogDevSourceError(error);
2032
+ writeJsonReceipt(io.stdout, createCatalogDevFailureReceipt({
2033
+ command: 'activate', cwd, workspaceRoot: rawOptions.workspaceRoot,
2034
+ sourceId: rawOptions.sourceId, ...failure,
2035
+ }));
2036
+ commandExitState.exitCode = 1;
2037
+ }
2038
+ });
2039
+ catalogDev
2040
+ .command('status')
2041
+ .option('--workspace-root <path>', 'explicit Catalog Workspace root')
2042
+ .requiredOption('--json', 'emit one schema 4 JSON receipt')
2043
+ .action(async (rawOptions) => {
2044
+ const receipt = await getCatalogDevSourceStatus({
2045
+ cwd,
2046
+ workspaceRoot: rawOptions.workspaceRoot,
2047
+ cliVersion: resolveCliVersion(),
2048
+ env,
2049
+ });
2050
+ writeJsonReceipt(io.stdout, receipt);
2051
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
2052
+ });
2053
+ catalogDev
2054
+ .command('deactivate')
2055
+ .option('--workspace-root <path>', 'explicit Catalog Workspace root')
2056
+ .option('--source-id <id>', 'recover one managed source by exact ID')
2057
+ .option('--replacement-primary <id>', 'replacement when the target source is primary')
2058
+ .option('-y, --yes', 'confirm source impact or recovery by source ID', false)
2059
+ .requiredOption('--json', 'emit one schema 4 JSON receipt')
2060
+ .action(async (rawOptions) => {
2061
+ try {
2062
+ const receipt = await deactivateCatalogDevSource({
2063
+ cwd,
2064
+ workspaceRoot: rawOptions.workspaceRoot,
2065
+ sourceId: rawOptions.sourceId,
2066
+ replacementPrimary: rawOptions.replacementPrimary,
2067
+ yes: rawOptions.yes,
2068
+ env,
2069
+ });
2070
+ writeJsonReceipt(io.stdout, receipt);
2071
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
2072
+ }
2073
+ catch (error) {
2074
+ const failure = catalogDevSourceError(error);
2075
+ writeJsonReceipt(io.stdout, createCatalogDevFailureReceipt({
2076
+ command: 'deactivate', cwd, workspaceRoot: rawOptions.workspaceRoot,
2077
+ sourceId: rawOptions.sourceId, ...failure,
2078
+ }));
1248
2079
  commandExitState.exitCode = 1;
1249
2080
  }
1250
2081
  });
@@ -1256,52 +2087,70 @@ export function buildProgram(options = {}) {
1256
2087
  const message = failure instanceof Error ? failure.message : String(error);
1257
2088
  if (json) {
1258
2089
  writeJsonReceipt(io.stdout, {
1259
- receiptSchemaVersion: 4,
2090
+ receiptSchemaVersion: 6,
1260
2091
  command,
1261
2092
  ok: false,
1262
2093
  changed: false,
1263
2094
  sourceId,
1264
- references: failure.inventory?.references ?? [],
2095
+ primaryChange: failure.impact?.primaryChange ?? null,
2096
+ references: failure.impact?.references ?? failure.inventory?.references ?? [],
1265
2097
  errors: [{ code: failure.code ?? failure.name ?? 'catalogSourceMutationFailed', message }],
1266
2098
  });
1267
2099
  }
1268
2100
  writeLine(io.stderr, message);
1269
2101
  commandExitState.exitCode = 1;
1270
2102
  };
1271
- const writeConfigMutationFailure = (command, plugin, json, error) => {
1272
- const message = error instanceof Error ? error.message : String(error);
1273
- const code = message.includes('legacyStateUnsupported')
1274
- ? 'legacyStateUnsupported'
1275
- : message.includes('sourceChanged')
1276
- ? 'sourceChanged'
1277
- : message.includes('sourceMissing')
1278
- ? 'sourceMissing'
1279
- : message.startsWith('Cannot resolve required catalog sources:')
1280
- ? 'catalogSourceUnavailable'
1281
- : message.split(':', 1)[0] || 'configMutationFailed';
1282
- if (json && !message.includes('Usage:')) {
2103
+ const writeCatalogGenerationFailure = (command, sourceId, json, error) => {
2104
+ const failure = sanitizeCatalogSourceError(error, env);
2105
+ const message = redactString(failure.message);
2106
+ if (json) {
1283
2107
  writeJsonReceipt(io.stdout, {
1284
- receiptSchemaVersion: 4,
2108
+ ...(command === 'catalog:source:sync'
2109
+ ? { catalogGenerationSchemaVersion: 1 }
2110
+ : { catalogGenerationStatusSchemaVersion: 2 }),
1285
2111
  command,
1286
2112
  ok: false,
1287
- overallState: 'failed',
1288
- ...(plugin ? { plugin } : {}),
1289
- errors: [{ code, message }],
2113
+ ...(sourceId ? { sourceId } : {}),
2114
+ ...(failure.impact ? { impact: failure.impact } : {}),
2115
+ errors: [{ code: catalogGenerationErrorCode(error), message }],
1290
2116
  });
1291
2117
  }
1292
2118
  writeLine(io.stderr, message);
1293
2119
  commandExitState.exitCode = 1;
1294
2120
  };
1295
- const withResolvedConfigMutationPlugin = (context, selector, consume) => withAwHomeMutationLock({
1296
- env: context.env,
1297
- action: (awHomeMutationToken) => withResolvedCatalogPlugin({
1298
- selector,
1299
- cliVersion: resolveCliVersion(),
1300
- freshnessPolicy: 'requireRemote',
2121
+ const writeConfigMutationFailure = (command, plugin, json, error) => {
2122
+ const failure = configMutationFailure(error, env, cwd);
2123
+ if (json) {
2124
+ writeConfigMutationFailureReceipt(io.stdout, {
2125
+ command,
2126
+ ...(plugin ? { plugin } : {}),
2127
+ }, failure);
2128
+ }
2129
+ const message = json
2130
+ ? failure.message
2131
+ : redactString(sanitizeCatalogSourceErrorMessage(error instanceof Error ? error.message : String(error), env, [cwd]));
2132
+ writeLine(io.stderr, message);
2133
+ commandExitState.exitCode = 1;
2134
+ };
2135
+ const withResolvedConfigMutationPlugin = (context, selector, consume) => (async () => {
2136
+ const parsed = parsePluginId(selector);
2137
+ return withAwHomeMutationLock({
1301
2138
  env: context.env,
1302
- consume: (resolution) => consume(resolution, awHomeMutationToken),
1303
- }),
1304
- });
2139
+ action: async (awHomeMutationToken) => {
2140
+ const catalogOperationContext = await resolveCatalogOperationContext({
2141
+ ...(parsed.sourceId ? { sourceIds: [parsed.sourceId] } : {}),
2142
+ env: context.env,
2143
+ });
2144
+ return withResolvedCatalogPlugin({
2145
+ selector,
2146
+ cliVersion: resolveCliVersion(),
2147
+ operationContext: catalogOperationContext,
2148
+ env: context.env,
2149
+ consume: (resolution) => consume(resolution, awHomeMutationToken, catalogOperationContext),
2150
+ });
2151
+ },
2152
+ });
2153
+ })();
1305
2154
  catalogSource
1306
2155
  .command('list')
1307
2156
  .option('-j, --json', 'print catalog sources as JSON', false)
@@ -1316,6 +2165,70 @@ export function buildProgram(options = {}) {
1316
2165
  writeLine(io.stdout, `${source.primary ? '*' : '-'} ${source.id} ${source.type} ${source.sourceIdentity}`);
1317
2166
  }
1318
2167
  });
2168
+ catalogSource
2169
+ .command('sync')
2170
+ .argument('<id>')
2171
+ .option('--check', 'fetch and validate without changing the Catalog store or Active state', false)
2172
+ .option('--replace-damaged', 'replace a damaged Active entry with the current candidate', false)
2173
+ .option('-y, --yes', 'confirm affected, unknown, or damaged Active replacement impact', false)
2174
+ .option('-j, --json', 'print the source sync result as JSON', false)
2175
+ .action(async (id, rawOptions) => {
2176
+ try {
2177
+ if (rawOptions.check && rawOptions.replaceDamaged) {
2178
+ throw new Error('catalog source sync --check cannot use --replace-damaged.');
2179
+ }
2180
+ const result = await syncCatalogSourceCommand({
2181
+ sourceId: id,
2182
+ cliVersion: resolveCliVersion(),
2183
+ cwd,
2184
+ check: rawOptions.check === true,
2185
+ impactConfirmed: rawOptions.yes === true,
2186
+ replaceDamaged: rawOptions.replaceDamaged === true,
2187
+ env,
2188
+ });
2189
+ if (rawOptions.json) {
2190
+ writeJsonReceipt(io.stdout, {
2191
+ ...result,
2192
+ command: 'catalog:source:sync',
2193
+ ok: true,
2194
+ });
2195
+ }
2196
+ else {
2197
+ writeLine(io.stdout, rawOptions.check
2198
+ ? `Catalog source ${id} is valid.`
2199
+ : `Catalog source ${id}: ${result.outcome}.`);
2200
+ }
2201
+ }
2202
+ catch (error) {
2203
+ writeCatalogGenerationFailure('catalog:source:sync', id, rawOptions.json === true, error);
2204
+ }
2205
+ });
2206
+ catalogSource
2207
+ .command('status')
2208
+ .argument('[id]')
2209
+ .option('-j, --json', 'print offline Catalog source status schema 2 as JSON', false)
2210
+ .action(async (id, rawOptions) => {
2211
+ try {
2212
+ const result = await catalogSourceStatusCommand({ sourceId: id, env });
2213
+ if (rawOptions.json) {
2214
+ writeJsonReceipt(io.stdout, result);
2215
+ }
2216
+ else {
2217
+ for (const source of result.sources) {
2218
+ const active = source.active
2219
+ ? `Active ${source.active.snapshotDigest}`
2220
+ : 'no Active';
2221
+ const readiness = source.compatibility?.state === 'unsupported'
2222
+ ? `incompatible (required: ${source.compatibility.requiredCapabilities.join(', ')}; missing: ${source.compatibility.missingCapabilities.join(', ')})`
2223
+ : source.activeReady ? 'ready' : 'not ready';
2224
+ writeLine(io.stdout, `${source.sourceId}: ${active}, ${readiness}`);
2225
+ }
2226
+ }
2227
+ }
2228
+ catch (error) {
2229
+ writeCatalogGenerationFailure('catalog:source:status', id, rawOptions.json === true, error);
2230
+ }
2231
+ });
1319
2232
  catalogSource
1320
2233
  .command('add')
1321
2234
  .argument('<id>')
@@ -1324,7 +2237,9 @@ export function buildProgram(options = {}) {
1324
2237
  .addOption(new Option('--ref <ref>', 'use an explicit Git ref'))
1325
2238
  .addOption(new Option('--default-ref', 'select the Git ref from the CLI release channel'))
1326
2239
  .option('--subdir <path>', 'use a catalog snapshot below the Git repository root')
2240
+ .option('--no-auto-check', 'disable automatic update checks for this source')
1327
2241
  .option('--primary', 'make this source primary', false)
2242
+ .option('-y, --yes', 'confirm affected or unknown primary source references', false)
1328
2243
  .option('-j, --json', 'print mutation receipt as JSON', false)
1329
2244
  .action(async (id, rawOptions) => {
1330
2245
  try {
@@ -1335,7 +2250,7 @@ export function buildProgram(options = {}) {
1335
2250
  throw new Error('Git catalog source add requires exactly one of `--ref` or `--default-ref`.');
1336
2251
  }
1337
2252
  if (rawOptions.path !== undefined && (rawOptions.ref !== undefined || rawOptions.defaultRef === true || rawOptions.subdir !== undefined)) {
1338
- throw new Error('Path catalog source add only accepts `--path` and `--primary`.');
2253
+ throw new Error('Path catalog source add only accepts `--path`, `--primary`, `--no-auto-check`, and `--yes`.');
1339
2254
  }
1340
2255
  const source = rawOptions.git !== undefined
1341
2256
  ? {
@@ -1345,18 +2260,32 @@ export function buildProgram(options = {}) {
1345
2260
  url: rawOptions.git,
1346
2261
  refPolicy: explicitRefPolicy(rawOptions.ref, rawOptions.defaultRef === true),
1347
2262
  ...(rawOptions.subdir ? { subdir: rawOptions.subdir } : {}),
2263
+ ...(rawOptions.autoCheck === false ? { autoCheck: false } : {}),
1348
2264
  }
1349
2265
  : {
1350
2266
  id,
1351
2267
  type: 'path',
1352
2268
  primary: rawOptions.primary === true,
1353
2269
  path: rawOptions.path,
2270
+ ...(rawOptions.autoCheck === false ? { autoCheck: false } : {}),
1354
2271
  };
2272
+ const preview = source.primary
2273
+ ? await preflightCatalogSourceMutation({
2274
+ request: { action: 'add', source }, cwd, env,
2275
+ })
2276
+ : undefined;
1355
2277
  const receipt = await addCatalogSourceCommand({
1356
2278
  source,
1357
2279
  cliVersion: resolveCliVersion(),
1358
2280
  cwd,
1359
2281
  env,
2282
+ ...(preview ? {
2283
+ expectedConfigDigest: preview.configDigest,
2284
+ expectedProposedConfigDigest: preview.proposedConfigDigest,
2285
+ expectedImpactDigest: preview.impactDigest,
2286
+ } : {}),
2287
+ requireImpactConfirmation: true,
2288
+ confirmImpact: rawOptions.yes === true,
1360
2289
  });
1361
2290
  if (rawOptions.json)
1362
2291
  writeJsonReceipt(io.stdout, receipt);
@@ -1378,6 +2307,9 @@ export function buildProgram(options = {}) {
1378
2307
  .addOption(new Option('--default-ref', 'select the Git ref from the CLI release channel'))
1379
2308
  .addOption(new Option('--subdir <path>', 'update the Git catalog subdirectory'))
1380
2309
  .addOption(new Option('--no-subdir', 'remove the Git catalog subdirectory'))
2310
+ .addOption(new Option('--auto-check', 'enable automatic update checks for this source'))
2311
+ .addOption(new Option('--no-auto-check', 'disable automatic update checks for this source'))
2312
+ .option('-y, --yes', 'confirm a source identity change', false)
1381
2313
  .option('-j, --json', 'print mutation receipt as JSON', false)
1382
2314
  .action(async (id, rawOptions) => {
1383
2315
  try {
@@ -1391,15 +2323,25 @@ export function buildProgram(options = {}) {
1391
2323
  || rawOptions.path !== undefined
1392
2324
  || rawOptions.ref !== undefined
1393
2325
  || rawOptions.defaultRef === true
1394
- || rawOptions.subdir !== undefined;
2326
+ || rawOptions.subdir !== undefined
2327
+ || rawOptions.autoCheck !== undefined;
1395
2328
  if (!hasUpdate)
1396
2329
  throw new Error('catalog source update requires at least one update option.');
1397
2330
  if (rawOptions.path !== undefined && (rawOptions.ref !== undefined || rawOptions.defaultRef === true || rawOptions.subdir !== undefined))
1398
- throw new Error('Path catalog source update only accepts `--path`.');
2331
+ throw new Error('Path catalog source update only accepts `--path` and auto-check options.');
2332
+ const hasGitUpdate = rawOptions.git !== undefined
2333
+ || rawOptions.ref !== undefined
2334
+ || rawOptions.defaultRef === true
2335
+ || rawOptions.subdir !== undefined;
1399
2336
  const update = rawOptions.path !== undefined
1400
- ? { type: 'path', path: rawOptions.path }
1401
- : {
2337
+ ? {
2338
+ type: 'path',
2339
+ path: rawOptions.path,
2340
+ ...(rawOptions.autoCheck !== undefined ? { autoCheck: rawOptions.autoCheck } : {}),
2341
+ }
2342
+ : hasGitUpdate ? {
1402
2343
  type: 'git',
2344
+ ...(rawOptions.autoCheck !== undefined ? { autoCheck: rawOptions.autoCheck } : {}),
1403
2345
  ...(rawOptions.git !== undefined ? { url: rawOptions.git } : {}),
1404
2346
  ...(rawOptions.ref !== undefined || rawOptions.defaultRef === true
1405
2347
  ? { refPolicy: explicitRefPolicy(rawOptions.ref, rawOptions.defaultRef === true) }
@@ -1407,13 +2349,22 @@ export function buildProgram(options = {}) {
1407
2349
  ...(rawOptions.subdir !== undefined
1408
2350
  ? { subdir: rawOptions.subdir === false ? null : rawOptions.subdir }
1409
2351
  : {}),
1410
- };
2352
+ }
2353
+ : { autoCheck: rawOptions.autoCheck };
2354
+ const preview = await preflightCatalogSourceMutation({
2355
+ request: { action: 'update', sourceId: id, update }, cwd, env,
2356
+ });
1411
2357
  const receipt = await updateCatalogSourceCommand({
1412
2358
  sourceId: id,
1413
2359
  update,
1414
2360
  cliVersion: resolveCliVersion(),
1415
2361
  cwd,
1416
2362
  env,
2363
+ expectedConfigDigest: preview.configDigest,
2364
+ expectedProposedConfigDigest: preview.proposedConfigDigest,
2365
+ expectedImpactDigest: preview.impactDigest,
2366
+ requireIdentityChangeConfirmation: true,
2367
+ confirmIdentityChange: rawOptions.yes === true,
1417
2368
  });
1418
2369
  if (rawOptions.json)
1419
2370
  writeJsonReceipt(io.stdout, receipt);
@@ -1429,10 +2380,17 @@ export function buildProgram(options = {}) {
1429
2380
  catalogSource
1430
2381
  .command('set-primary')
1431
2382
  .argument('<id>')
2383
+ .option('-y, --yes', 'confirm affected or unknown primary source references', false)
1432
2384
  .option('-j, --json', 'print mutation receipt as JSON', false)
1433
2385
  .action(async (id, rawOptions) => {
1434
2386
  try {
1435
- const receipt = await setPrimaryCatalogSourceCommand({ sourceId: id, cwd, env });
2387
+ const receipt = await setPrimaryCatalogSourceCommand({
2388
+ sourceId: id,
2389
+ cwd,
2390
+ env,
2391
+ requireImpactConfirmation: true,
2392
+ confirmImpact: rawOptions.yes === true,
2393
+ });
1436
2394
  if (rawOptions.json)
1437
2395
  writeJsonReceipt(io.stdout, receipt);
1438
2396
  else
@@ -1445,10 +2403,17 @@ export function buildProgram(options = {}) {
1445
2403
  catalogSource
1446
2404
  .command('remove')
1447
2405
  .argument('<id>')
2406
+ .option('-y, --yes', 'confirm affected or unknown source references', false)
1448
2407
  .option('-j, --json', 'print mutation receipt as JSON', false)
1449
2408
  .action(async (id, rawOptions) => {
1450
2409
  try {
1451
- const receipt = await removeCatalogSourceCommand({ sourceId: id, cwd, env });
2410
+ const receipt = await removeCatalogSourceCommand({
2411
+ sourceId: id,
2412
+ cwd,
2413
+ env,
2414
+ requireImpactConfirmation: true,
2415
+ confirmImpact: rawOptions.yes === true,
2416
+ });
1452
2417
  if (rawOptions.json)
1453
2418
  writeJsonReceipt(io.stdout, receipt);
1454
2419
  else
@@ -1458,9 +2423,102 @@ export function buildProgram(options = {}) {
1458
2423
  await writeCatalogSourceFailure('catalog:source:remove', id, rawOptions.json === true, error);
1459
2424
  }
1460
2425
  });
2426
+ const catalogStore = catalog
2427
+ .command('store')
2428
+ .description('Inspect or prune the immutable Catalog store');
2429
+ catalogStore
2430
+ .command('status')
2431
+ .option('-j, --json', 'print Catalog store status as JSON', false)
2432
+ .action(async (rawOptions) => {
2433
+ try {
2434
+ const result = await catalogStoreStatus({ env });
2435
+ if (rawOptions.json) {
2436
+ writeJsonReceipt(io.stdout, result);
2437
+ }
2438
+ else if (result.entries.length === 0) {
2439
+ writeLine(io.stdout, 'Catalog store is empty.');
2440
+ }
2441
+ else {
2442
+ for (const entry of result.entries) {
2443
+ const bytes = entry.reclaimableBytes === null ? 'unknown bytes' : `${entry.reclaimableBytes} bytes`;
2444
+ writeLine(io.stdout, `${entry.storeKey}: ${entry.category}, ${entry.status}, ${entry.reason}, ${bytes}`);
2445
+ }
2446
+ writeLine(io.stdout, `Reclaimable: ${result.totals.reclaimableEntries} item(s).`);
2447
+ if (result.errors.length > 0) {
2448
+ writeLine(io.stderr, `${result.errors.length} item(s) could not be fully inspected and were retained.`);
2449
+ }
2450
+ }
2451
+ commandExitState.exitCode = result.ok ? 0 : 1;
2452
+ }
2453
+ catch (error) {
2454
+ const failure = sanitizeCatalogSourceError(error, env);
2455
+ const message = redactString(failure.message);
2456
+ if (rawOptions.json)
2457
+ writeJsonReceipt(io.stdout, {
2458
+ catalogStoreSchemaVersion: 2,
2459
+ command: 'catalog:store:status',
2460
+ ok: false,
2461
+ entries: [],
2462
+ errors: [{ code: catalogStoreErrorCode(error), message }],
2463
+ });
2464
+ writeLine(io.stderr, message);
2465
+ commandExitState.exitCode = 1;
2466
+ }
2467
+ });
2468
+ catalogStore
2469
+ .command('prune')
2470
+ .option('--dry-run', 'inspect reclaimable items without deleting them', false)
2471
+ .option('-y, --yes', 'delete items classified as safely reclaimable', false)
2472
+ .option('-j, --json', 'print the prune receipt as JSON', false)
2473
+ .action(async (rawOptions) => {
2474
+ const dryRun = rawOptions.yes !== true;
2475
+ try {
2476
+ if (rawOptions.dryRun === true && rawOptions.yes === true) {
2477
+ throw Object.assign(new Error('catalog store prune cannot combine `--dry-run` and `--yes`.'), {
2478
+ code: 'invalidArguments',
2479
+ });
2480
+ }
2481
+ const result = await catalogStorePrune({ dryRun, env });
2482
+ if (rawOptions.json) {
2483
+ writeJsonReceipt(io.stdout, result);
2484
+ }
2485
+ else if (dryRun) {
2486
+ writeLine(io.stdout, `Dry run: ${result.entries.filter((entry) => entry.status === 'reclaimable').length} item(s) are reclaimable.`);
2487
+ writeLine(io.stdout, 'Run `aw catalog store prune --yes` to delete them.');
2488
+ if (result.errors.length > 0)
2489
+ writeLine(io.stderr, `${result.errors.length} item(s) could not be fully inspected and were retained.`);
2490
+ }
2491
+ else {
2492
+ writeLine(io.stdout, `Deleted ${result.deleted.length} Catalog store item(s) (${result.deletedBytes} bytes).`);
2493
+ if (result.errors.length > 0)
2494
+ writeLine(io.stderr, `${result.errors.length} item(s) could not be fully inspected or deleted and were retained.`);
2495
+ }
2496
+ commandExitState.exitCode = result.ok ? 0 : 1;
2497
+ }
2498
+ catch (error) {
2499
+ const failure = sanitizeCatalogSourceError(error, env);
2500
+ const message = redactString(failure.message);
2501
+ const code = catalogStoreErrorCode(error);
2502
+ if (rawOptions.json)
2503
+ writeJsonReceipt(io.stdout, {
2504
+ catalogStoreSchemaVersion: 2,
2505
+ command: 'catalog:store:prune',
2506
+ ok: false,
2507
+ overallState: 'failed',
2508
+ dryRun,
2509
+ entries: [],
2510
+ deleted: [],
2511
+ retained: [],
2512
+ errors: [{ code, message }],
2513
+ reclaimableBytes: null,
2514
+ deletedBytes: 0,
2515
+ });
2516
+ writeLine(io.stderr, message);
2517
+ commandExitState.exitCode = 1;
2518
+ }
2519
+ });
1461
2520
  program
1462
2521
  .command('init')
1463
- .option('-f, --force', 're-fetch and rebuild the current catalog cache entry', false)
1464
2522
  .option('-j, --json', 'print receipt as JSON', false)
1465
2523
  .option('-v, --verbose', 'show command diagnostics', false)
1466
2524
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1468,8 +2526,6 @@ export function buildProgram(options = {}) {
1468
2526
  .action(async (rawOptions) => {
1469
2527
  const context = createActionContext('init', rawOptions);
1470
2528
  const result = await runActionWithContext(context, () => initCommand({
1471
- force: rawOptions.force,
1472
- cliVersion: resolveCliVersion(),
1473
2529
  env: context.env,
1474
2530
  }));
1475
2531
  if (rawOptions.json) {
@@ -1480,7 +2536,6 @@ export function buildProgram(options = {}) {
1480
2536
  writeLine(io.stdout, line);
1481
2537
  }
1482
2538
  }
1483
- writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
1484
2539
  for (const source of result.sources) {
1485
2540
  if (!source.ok && source.error)
1486
2541
  writeLine(io.stderr, `${source.id}: ${source.error}`);
@@ -1488,53 +2543,79 @@ export function buildProgram(options = {}) {
1488
2543
  commandExitState.exitCode = result.ok ? 0 : 1;
1489
2544
  });
1490
2545
  program
1491
- .command('install')
2546
+ .command('sync')
2547
+ .description('Reconcile actual state from existing desired intent')
1492
2548
  .argument('[plugins...]')
1493
- .option('-s, --scope <scope>', 'project or user')
1494
- .option('-p, --platform <platform>', 'codex | claude | both')
1495
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
1496
- .option('-j, --json', 'print receipt as JSON', false)
2549
+ .requiredOption('-s, --scope <scope>', 'project or user')
2550
+ .requiredOption('-p, --platform <platform>', 'codex, claude, or both')
2551
+ .option('-a, --all', 'reconcile every desired plugin in the selected scope', false)
2552
+ .option('--refresh', 'refresh only Catalog sources required by selected intent roots', false)
2553
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2554
+ .option('-y, --yes', 'authorize preApply and shared native replacement or forced repair', false)
2555
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2556
+ .option('-j, --json', 'print sync receipt as JSON', false)
1497
2557
  .option('-v, --verbose', 'show command diagnostics', false)
1498
2558
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1499
2559
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1500
2560
  .action(async (plugins, rawOptions) => {
1501
- const context = createActionContext('install', rawOptions);
1502
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('install', plugins ?? [], rawOptions, context));
1503
- if (outcome.cancelled)
1504
- return;
1505
- const receipt = outcome.result;
2561
+ const context = createActionContext('sync', rawOptions);
2562
+ const normalized = normalizeWriteRequest(() => normalizeInstallCommandOptions({
2563
+ cwd: context.cwd,
2564
+ scope: rawOptions.scope,
2565
+ platform: rawOptions.platform,
2566
+ scriptTimeout: rawOptions.scriptTimeout,
2567
+ allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2568
+ }));
2569
+ const syncPlatform = normalized.platform;
2570
+ const result = await runActionWithContext(context, () => syncCommand(plugins ?? [], {
2571
+ cwd: context.cwd,
2572
+ env: context.env,
2573
+ platform: syncPlatform,
2574
+ scope: normalized.scope,
2575
+ all: rawOptions.all,
2576
+ refresh: rawOptions.refresh,
2577
+ cliVersion: resolveCliVersion(),
2578
+ yes: rawOptions.yes,
2579
+ json: rawOptions.json,
2580
+ interactive: context.interactive && context.isTty && !context.isCi,
2581
+ prompt: context.prompt,
2582
+ allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2583
+ scriptTimeoutMs: normalized.scriptTimeoutMs,
2584
+ }, context));
1506
2585
  if (rawOptions.json) {
1507
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('install', receipt));
2586
+ writeJsonReceipt(io.stdout, buildRebuildReceiptJson('sync', result));
1508
2587
  }
1509
2588
  else {
1510
- for (const line of renderRebuildReceipt('install', receipt)) {
2589
+ for (const line of renderRebuildReceipt('sync', result))
1511
2590
  writeLine(io.stdout, line);
1512
- }
1513
2591
  }
1514
- writeWarnings(io.stderr, receipt.warnings, { actionableOnly: true });
1515
- commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
2592
+ writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
2593
+ commandExitState.exitCode = resolveCommandReceiptExitCode(result);
1516
2594
  });
1517
2595
  program
1518
- .command('uninstall')
2596
+ .command('install')
1519
2597
  .argument('[plugins...]')
1520
2598
  .option('-s, --scope <scope>', 'project or user')
1521
2599
  .option('-p, --platform <platform>', 'codex | claude | both')
1522
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
2600
+ .option('--dependency-scope <pluginId[#platform]=scope>', 'place one dependency at project or user scope; repeat for more dependencies', (value, previous) => [...previous, value], [])
2601
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2602
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2603
+ .option('-y, --yes', 'authorize preApply and shared Codex native replacement', false)
1523
2604
  .option('-j, --json', 'print receipt as JSON', false)
1524
2605
  .option('-v, --verbose', 'show command diagnostics', false)
1525
2606
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1526
2607
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1527
2608
  .action(async (plugins, rawOptions) => {
1528
- const context = createActionContext('uninstall', rawOptions);
1529
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('uninstall', plugins ?? [], rawOptions, context));
2609
+ const context = createActionContext('install', rawOptions);
2610
+ const outcome = await runActionWithContext(context, () => runWriteCommandAction('install', plugins ?? [], rawOptions, context));
1530
2611
  if (outcome.cancelled)
1531
2612
  return;
1532
2613
  const receipt = outcome.result;
1533
2614
  if (rawOptions.json) {
1534
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('uninstall', receipt));
2615
+ writeJsonReceipt(io.stdout, buildRebuildReceiptJson('install', receipt));
1535
2616
  }
1536
2617
  else {
1537
- for (const line of renderRebuildReceipt('uninstall', receipt)) {
2618
+ for (const line of renderRebuildReceipt('install', receipt)) {
1538
2619
  writeLine(io.stdout, line);
1539
2620
  }
1540
2621
  }
@@ -1542,27 +2623,26 @@ export function buildProgram(options = {}) {
1542
2623
  commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
1543
2624
  });
1544
2625
  program
1545
- .command('upgrade')
2626
+ .command('uninstall')
1546
2627
  .argument('[plugins...]')
1547
- .option('-a, --all', 'upgrade all plugins', false)
1548
2628
  .option('-s, --scope <scope>', 'project or user')
1549
2629
  .option('-p, --platform <platform>', 'codex | claude | both')
1550
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
2630
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
1551
2631
  .option('-j, --json', 'print receipt as JSON', false)
1552
2632
  .option('-v, --verbose', 'show command diagnostics', false)
1553
2633
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1554
2634
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1555
2635
  .action(async (plugins, rawOptions) => {
1556
- const context = createActionContext('upgrade', rawOptions);
1557
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('upgrade', plugins ?? [], rawOptions, context));
2636
+ const context = createActionContext('uninstall', rawOptions);
2637
+ const outcome = await runActionWithContext(context, () => runWriteCommandAction('uninstall', plugins ?? [], rawOptions, context));
1558
2638
  if (outcome.cancelled)
1559
2639
  return;
1560
2640
  const receipt = outcome.result;
1561
2641
  if (rawOptions.json) {
1562
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('upgrade', receipt));
2642
+ writeJsonReceipt(io.stdout, buildRebuildReceiptJson('uninstall', receipt));
1563
2643
  }
1564
2644
  else {
1565
- for (const line of renderRebuildReceipt('upgrade', receipt)) {
2645
+ for (const line of renderRebuildReceipt('uninstall', receipt)) {
1566
2646
  writeLine(io.stdout, line);
1567
2647
  }
1568
2648
  }
@@ -1575,7 +2655,8 @@ export function buildProgram(options = {}) {
1575
2655
  .option('-s, --scope <scope>', 'project or user')
1576
2656
  .option('-p, --platform <platform>', 'codex | claude | both')
1577
2657
  .option('-j, --json', 'print cleanup result as JSON', false)
1578
- .option('--force', 'remove exclusively owned plugin output without dependency protection', false)
2658
+ .option('--force', 'use exact shallow evidence for exclusively owned damaged output; preserve foreign and shared content', false)
2659
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
1579
2660
  .option('-v, --verbose', 'show command diagnostics', false)
1580
2661
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1581
2662
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
@@ -1594,14 +2675,16 @@ export function buildProgram(options = {}) {
1594
2675
  }
1595
2676
  }
1596
2677
  writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
1597
- commandExitState.exitCode = result.overallState === 'failed' ? 1 : 0;
2678
+ commandExitState.exitCode = resolveCommandReceiptExitCode(result);
1598
2679
  });
1599
2680
  program
1600
2681
  .command('reinstall')
1601
2682
  .argument('[plugins...]')
1602
2683
  .option('-s, --scope <scope>', 'project or user')
1603
2684
  .option('-p, --platform <platform>', 'codex | claude | both')
1604
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
2685
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2686
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2687
+ .option('-y, --yes', 'authorize preApply and shared Codex native replacement or forced reinstall', false)
1605
2688
  .option('-j, --json', 'print receipt as JSON', false)
1606
2689
  .option('-v, --verbose', 'show command diagnostics', false)
1607
2690
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1631,7 +2714,6 @@ export function buildProgram(options = {}) {
1631
2714
  .command('list')
1632
2715
  .argument('[plugin]')
1633
2716
  .option('-s, --scope <scope>', 'read only project or user config')
1634
- .option('--local', 'read only the current project local override layer', false)
1635
2717
  .option('-j, --json', 'print config as JSON', false)
1636
2718
  .option('-v, --verbose', 'show command diagnostics', false)
1637
2719
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1639,9 +2721,7 @@ export function buildProgram(options = {}) {
1639
2721
  .action(async (pluginName, rawOptions) => {
1640
2722
  const context = createActionContext('config:list', rawOptions);
1641
2723
  const outcome = await runActionWithContext(context, async () => {
1642
- resolveConfigProjectLayer(rawOptions);
1643
- if (!rawOptions.local)
1644
- resolveConfigReadResolution(rawOptions.scope);
2724
+ resolveConfigReadResolution(rawOptions.scope);
1645
2725
  const execute = async (selectedPlugin) => resolveMultiSourceConfigRead({
1646
2726
  selector: selectedPlugin,
1647
2727
  rawOptions,
@@ -1666,7 +2746,6 @@ export function buildProgram(options = {}) {
1666
2746
  command: 'list',
1667
2747
  plugin: selection.plugins[0],
1668
2748
  scope: rawOptions.scope,
1669
- projectLayer: rawOptions.local ? 'local' : 'shared',
1670
2749
  })),
1671
2750
  execute: (selection) => execute(selection.plugins[0]),
1672
2751
  });
@@ -1688,8 +2767,7 @@ export function buildProgram(options = {}) {
1688
2767
  pluginId: resolved.pluginId,
1689
2768
  sourceId: resolved.sourceId,
1690
2769
  sourceIdentity: resolved.sourceIdentity,
1691
- scope: rawOptions.local ? 'project' : rawOptions.scope ?? 'effective',
1692
- projectLayer: rawOptions.local ? 'local' : 'shared',
2770
+ scope: rawOptions.scope ?? 'effective',
1693
2771
  ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
1694
2772
  fields: resolved.result.fields,
1695
2773
  });
@@ -1703,7 +2781,6 @@ export function buildProgram(options = {}) {
1703
2781
  .argument('[plugin]')
1704
2782
  .argument('[key]')
1705
2783
  .option('-s, --scope <scope>', 'read only project or user config')
1706
- .option('--local', 'read only the current project local override layer', false)
1707
2784
  .option('-j, --json', 'print config as JSON', false)
1708
2785
  .option('-v, --verbose', 'show command diagnostics', false)
1709
2786
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1711,9 +2788,7 @@ export function buildProgram(options = {}) {
1711
2788
  .action(async (pluginName, key, rawOptions) => {
1712
2789
  const context = createActionContext('config:get', rawOptions);
1713
2790
  const outcome = await runActionWithContext(context, async () => {
1714
- resolveConfigProjectLayer(rawOptions);
1715
- if (!rawOptions.local)
1716
- resolveConfigReadResolution(rawOptions.scope);
2791
+ resolveConfigReadResolution(rawOptions.scope);
1717
2792
  const execute = async (selectedPlugin) => resolveMultiSourceConfigRead({
1718
2793
  selector: selectedPlugin,
1719
2794
  ...(key === undefined ? {} : { key }),
@@ -1740,7 +2815,6 @@ export function buildProgram(options = {}) {
1740
2815
  plugin: selection.plugins[0],
1741
2816
  key,
1742
2817
  scope: rawOptions.scope,
1743
- projectLayer: rawOptions.local ? 'local' : 'shared',
1744
2818
  })),
1745
2819
  execute: (selection) => execute(selection.plugins[0]),
1746
2820
  });
@@ -1763,8 +2837,7 @@ export function buildProgram(options = {}) {
1763
2837
  sourceId: resolved.sourceId,
1764
2838
  sourceIdentity: resolved.sourceIdentity,
1765
2839
  key,
1766
- scope: rawOptions.local ? 'project' : rawOptions.scope ?? 'effective',
1767
- projectLayer: rawOptions.local ? 'local' : 'shared',
2840
+ scope: rawOptions.scope ?? 'effective',
1768
2841
  ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
1769
2842
  fields: resolved.result.fields,
1770
2843
  });
@@ -1776,47 +2849,119 @@ export function buildProgram(options = {}) {
1776
2849
  config
1777
2850
  .command('set')
1778
2851
  .argument('[plugin]')
1779
- .argument('[key]')
1780
- .argument('[value]')
2852
+ .argument('[field]')
1781
2853
  .option('-s, --scope <scope>', 'project or user')
1782
- .option('--local', 'write only the current project local override layer', false)
1783
- .option('--value-json', 'parse value as a JSON literal', false)
2854
+ .option('--value-json <json>', 'set the field to an exact JSON value')
2855
+ .option('--value-stdin', 'read the exact string value from stdin until EOF', false)
2856
+ .option('--preset <id>', 'set the field to the current manifest preset value')
2857
+ .option('--apply', 'apply the saved config to visible desired placements', false)
2858
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2859
+ .option('-y, --yes', 'confirm preApply and shared Codex native config apply', false)
1784
2860
  .option('-j, --json', 'print receipt as JSON', false)
1785
2861
  .option('-v, --verbose', 'show command diagnostics', false)
1786
2862
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1787
2863
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1788
- .action(async (pluginName, key, rawValue, rawOptions) => {
2864
+ .action(async (pluginName, key, rawOptions) => {
2865
+ if (!pluginName || !key)
2866
+ throw new Error(configCommandUsage('set'));
1789
2867
  const context = createActionContext('config:set', rawOptions);
1790
2868
  let outcome;
1791
2869
  try {
1792
2870
  outcome = await runActionWithContext(context, async () => {
1793
2871
  const scope = resolveConfigWriteScope(rawOptions.scope);
1794
- const projectLayer = resolveConfigProjectLayer(rawOptions);
1795
- const execute = async (selectedPlugin, selectedKey, inputValue) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken) => {
1796
- if (!plugin.manifest.config?.schema) {
1797
- throw new Error(`configSchemaUnavailable: Plugin "${selectedPlugin}" does not declare an AW config schema.`);
2872
+ const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2873
+ const explicitValueCount = [
2874
+ rawOptions.valueJson !== undefined,
2875
+ rawOptions.valueStdin === true,
2876
+ rawOptions.preset !== undefined,
2877
+ ].filter(Boolean).length;
2878
+ if (explicitValueCount > 1
2879
+ || (explicitValueCount === 0
2880
+ && !(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true))) {
2881
+ throw new Error('Use exactly one of --value-json <json>, --value-stdin, or --preset <id>.');
2882
+ }
2883
+ return withResolvedConfigMutationPlugin(context, pluginName, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
2884
+ if (!plugin.manifest.config?.fields) {
2885
+ throw new Error(`configSchemaUnavailable: Plugin "${pluginName}" does not declare an AW config schema.`);
1798
2886
  }
1799
- if (!plugin.manifest.config.schema[selectedKey]) {
1800
- throw new Error(`Unknown config key "${selectedKey}".`);
2887
+ const field = plugin.manifest.config.fields[key];
2888
+ if (!field) {
2889
+ throw new Error(`Unknown config key "${key}".`);
2890
+ }
2891
+ const sensitive = field.sensitive === true;
2892
+ if (sensitive && scope !== 'user') {
2893
+ const error = new Error('sensitiveProjectConfigUnsupported: Sensitive config can only be set in user scope.');
2894
+ error.code = 'sensitiveProjectConfigUnsupported';
2895
+ throw error;
2896
+ }
2897
+ if (sensitive && (rawOptions.valueJson !== undefined || rawOptions.preset !== undefined)) {
2898
+ const error = new Error('sensitiveConfigInputUnsupported: Sensitive config cannot use --value-json or --preset.');
2899
+ error.code = 'sensitiveConfigInputUnsupported';
2900
+ throw error;
2901
+ }
2902
+ if (!sensitive && explicitValueCount !== 1) {
2903
+ throw new Error('Use exactly one of --value-json <json>, --value-stdin, or --preset <id>.');
2904
+ }
2905
+ let inputValue;
2906
+ if (rawOptions.valueStdin === true) {
2907
+ inputValue = await readUtf8Stdin(context.stdin);
2908
+ }
2909
+ else if (sensitive) {
2910
+ if (!(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true)
2911
+ || !context.prompt.password) {
2912
+ const error = new Error('sensitiveConfigStdinRequired: Use --value-stdin for sensitive config outside an interactive terminal.');
2913
+ error.code = 'sensitiveConfigStdinRequired';
2914
+ throw error;
2915
+ }
2916
+ const response = await context.prompt.password({
2917
+ message: `输入 ${field.title ?? key}`,
2918
+ mask: true,
2919
+ });
2920
+ if (response.cancelled || response.action !== 'select' || response.value === undefined) {
2921
+ const error = new Error('sensitiveConfigInputCancelled: Sensitive config input was cancelled.');
2922
+ error.code = 'sensitiveConfigInputCancelled';
2923
+ throw error;
2924
+ }
2925
+ inputValue = response.value;
2926
+ }
2927
+ else if (rawOptions.valueJson !== undefined) {
2928
+ try {
2929
+ inputValue = JSON.parse(rawOptions.valueJson);
2930
+ }
2931
+ catch {
2932
+ throw new Error('--value-json must contain valid JSON.');
2933
+ }
2934
+ }
2935
+ else {
2936
+ const preset = field.choices?.presets.find((entry) => entry.id === rawOptions.preset);
2937
+ if (!preset)
2938
+ throw new Error(`Unknown preset "${rawOptions.preset}" for config key "${key}".`);
2939
+ inputValue = structuredClone(preset.value);
1801
2940
  }
1802
2941
  const writeReceipt = await applyPluginConfigWrite({
1803
2942
  plugin,
1804
2943
  pluginName: plugin.manifest.name,
1805
2944
  scope,
1806
- projectLayer,
1807
2945
  cwd: context.cwd,
1808
2946
  env: context.env,
1809
2947
  nativeCliRunner: context.nativeCliRunner,
1810
- rebuildRuntime: { ...context, awHomeMutationToken },
2948
+ rebuildRuntime: {
2949
+ ...context,
2950
+ awHomeMutationToken,
2951
+ catalogOperationContext,
2952
+ expectedGenerationSetDigest: catalogOperationContext.generationSetDigest,
2953
+ ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }),
2954
+ },
1811
2955
  awHomeMutationToken,
1812
2956
  request: {
1813
- updates: { [selectedKey]: inputValue },
2957
+ updates: { [key]: inputValue },
1814
2958
  },
2959
+ apply: rawOptions.apply === true,
2960
+ sharedNativeApplyConfirmed: rawOptions.yes === true,
2961
+ preApplyConfirmed: rawOptions.yes === true,
1815
2962
  });
1816
- return {
2963
+ return buildConfigMutationReceipt('config:set', {
1817
2964
  ok: writeReceipt.overallState === 'succeeded',
1818
- receiptSchemaVersion: 4,
1819
- command: 'config:set',
1820
2965
  ...writeReceipt,
1821
2966
  plugin: plugin.manifest.name,
1822
2967
  pluginName: plugin.manifest.name,
@@ -1824,104 +2969,39 @@ export function buildProgram(options = {}) {
1824
2969
  sourceId: source.sourceId,
1825
2970
  sourceIdentity: source.sourceIdentity,
1826
2971
  affectedFields: [{
1827
- key: selectedKey,
1828
- title: plugin?.manifest.config?.schema?.[selectedKey]?.title ?? selectedKey,
2972
+ key,
2973
+ title: field.title ?? key,
1829
2974
  kind: 'manifest',
1830
2975
  reserved: false,
1831
2976
  action: 'set',
1832
2977
  scope,
1833
- ...(scope === 'project' ? { projectLayer } : {}),
1834
2978
  }],
1835
- };
1836
- });
1837
- if (pluginName && key && rawValue !== undefined) {
1838
- const inputValue = parseConfigValue(rawValue, rawOptions.valueJson === true);
1839
- return {
1840
- cancelled: false,
1841
- key,
1842
- value: await execute(pluginName, key, inputValue),
1843
- };
1844
- }
1845
- if (!isWizardAvailable(context, rawOptions))
1846
- throw new Error(configCommandUsage('set'));
1847
- const candidates = await loadWizardCandidates(context, {});
1848
- if (pluginName) {
1849
- const candidate = candidates.find((entry) => entry.pluginName === pluginName);
1850
- if (!candidate)
1851
- throw new Error(`Unknown plugin "${pluginName}".`);
1852
- if (!hasConfigSchemaKeys(candidate)) {
1853
- throw new Error(`configSchemaUnavailable: Plugin "${pluginName}" does not declare an AW config schema.`);
1854
- }
1855
- if (key && !candidate.configSchema?.[key])
1856
- throw new Error(`Unknown config key "${key}".`);
1857
- }
1858
- const configurableCandidates = candidates.filter(hasConfigSchemaKeys);
1859
- if (configurableCandidates.length === 0) {
1860
- throw new Error('configSchemaUnavailable: No current plugin declares an AW config schema.');
1861
- }
1862
- const result = await runCommandWizard(context, {
1863
- command: 'config:set',
1864
- candidates: configurableCandidates,
1865
- initial: {
1866
- plugins: pluginName ? [pluginName] : [],
1867
- key,
1868
- ...(rawValue !== undefined ? { value: rawValue } : {}),
1869
- scope,
1870
- projectLayer,
1871
- },
1872
- pluginMode: pluginName ? 'none' : 'single',
1873
- valueJson: rawOptions.valueJson === true,
1874
- promptForKey: key === undefined,
1875
- promptForValue: rawValue === undefined,
1876
- ...equivalentConfigCommandProjection((selection) => configSetEquivalentOptions(selection, candidates, rawOptions)),
1877
- commandIsTemplate: (selection) => candidates.find((entry) => entry.pluginName === selection.plugins[0])?.configSchema?.[selection.key ?? ''] !== undefined
1878
- && configFieldPlaceholder(candidates.find((entry) => entry.pluginName === selection.plugins[0])?.configSchema?.[selection.key ?? '']) !== undefined,
1879
- resultState: overallResultState,
1880
- execute: async (selection) => {
1881
- const selectedPlugin = selection.plugins[0];
1882
- const selectedKey = selection.key;
1883
- const schema = candidates.find((entry) => entry.pluginName === selectedPlugin).configSchema;
1884
- const updates = await coercePluginConfigUpdates({
1885
- schema,
1886
- updates: {
1887
- [selectedKey]: parseConfigValue(selection.value, rawOptions.valueJson === true),
1888
- },
1889
- cwd: context.cwd,
1890
- });
1891
- return execute(selectedPlugin, selectedKey, updates[selectedKey]);
1892
- },
2979
+ });
1893
2980
  });
1894
- return result.cancelled
1895
- ? { cancelled: true }
1896
- : { cancelled: false, key: result.selection.key, value: result.value };
1897
2981
  });
1898
2982
  }
1899
2983
  catch (error) {
1900
2984
  writeConfigMutationFailure('config:set', pluginName, rawOptions.json === true, error);
1901
2985
  return;
1902
2986
  }
1903
- if (outcome.cancelled)
1904
- return;
1905
- const receipt = outcome.value;
1906
- const selectedKey = outcome.key;
2987
+ const receipt = outcome;
1907
2988
  if (rawOptions.json) {
1908
2989
  writeJsonReceipt(io.stdout, receipt);
1909
2990
  }
1910
2991
  else {
1911
- writeLine(io.stdout, `已写入 ${receipt.plugin}.${selectedKey}(${configLayerLabel(receipt.scope, receipt.projectLayer)}配置)。`);
1912
- if (receipt.projectLayer === 'local') {
1913
- writeProjectLocalConfigNotice(io.stdout);
1914
- }
2992
+ writeLine(io.stdout, `已写入 ${receipt.plugin}.${key}(${configLayerLabel(receipt.scope)}配置)。`);
1915
2993
  writeConfigApplyNotice(io.stderr, receipt);
1916
2994
  }
1917
- commandExitState.exitCode = receipt.overallState === 'failed' ? 1 : 0;
2995
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
1918
2996
  });
1919
2997
  config
1920
2998
  .command('unset')
1921
2999
  .argument('[plugin]')
1922
3000
  .argument('[key]')
1923
3001
  .option('-s, --scope <scope>', 'project or user')
1924
- .option('--local', 'write only the current project local override layer', false)
3002
+ .option('--apply', 'apply the saved config to visible desired placements', false)
3003
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
3004
+ .option('-y, --yes', 'confirm preApply and shared Codex native config apply', false)
1925
3005
  .option('-j, --json', 'print receipt as JSON', false)
1926
3006
  .option('-v, --verbose', 'show command diagnostics', false)
1927
3007
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1932,19 +3012,17 @@ export function buildProgram(options = {}) {
1932
3012
  try {
1933
3013
  outcome = await runActionWithContext(context, async () => {
1934
3014
  const scope = resolveConfigWriteScope(rawOptions.scope);
1935
- const projectLayer = resolveConfigProjectLayer(rawOptions);
1936
- const execute = async (selectedPlugin, selectedKey) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken) => {
1937
- if (plugin.manifest.config?.schema && !plugin.manifest.config.schema[selectedKey]) {
1938
- throw new Error(`Unknown config key "${selectedKey}".`);
1939
- }
3015
+ const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
3016
+ const execute = async (selectedPlugin, selectedKey, confirmed) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
1940
3017
  const store = await readPluginConfigStore({
1941
3018
  sourceId: source.sourceId,
1942
3019
  sourceIdentity: source.sourceIdentity,
3020
+ logicalIdentity: source.logicalIdentity,
1943
3021
  pluginName: plugin.manifest.name,
1944
3022
  scope,
1945
- ...(scope === 'project' ? { projectLayer } : {}),
1946
3023
  cwd: context.cwd,
1947
3024
  env: context.env,
3025
+ schema: plugin.manifest.config?.fields,
1948
3026
  });
1949
3027
  const existed = Object.hasOwn(store.values, selectedKey);
1950
3028
  if (!existed)
@@ -1953,20 +3031,26 @@ export function buildProgram(options = {}) {
1953
3031
  plugin,
1954
3032
  pluginName: plugin.manifest.name,
1955
3033
  scope,
1956
- projectLayer,
1957
3034
  cwd: context.cwd,
1958
3035
  env: context.env,
1959
3036
  nativeCliRunner: context.nativeCliRunner,
1960
- rebuildRuntime: { ...context, awHomeMutationToken },
3037
+ rebuildRuntime: {
3038
+ ...context,
3039
+ awHomeMutationToken,
3040
+ catalogOperationContext,
3041
+ expectedGenerationSetDigest: catalogOperationContext.generationSetDigest,
3042
+ ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }),
3043
+ },
1961
3044
  awHomeMutationToken,
1962
3045
  request: {
1963
3046
  removeKeys: [selectedKey],
1964
3047
  },
3048
+ apply: rawOptions.apply === true,
3049
+ sharedNativeApplyConfirmed: confirmed,
3050
+ preApplyConfirmed: rawOptions.yes === true,
1965
3051
  });
1966
- return {
3052
+ return buildConfigMutationReceipt('config:unset', {
1967
3053
  ok: writeReceipt.overallState === 'succeeded',
1968
- receiptSchemaVersion: 4,
1969
- command: 'config:unset',
1970
3054
  ...writeReceipt,
1971
3055
  plugin: plugin.manifest.name,
1972
3056
  pluginName: plugin.manifest.name,
@@ -1977,17 +3061,20 @@ export function buildProgram(options = {}) {
1977
3061
  existed,
1978
3062
  affectedFields: [{
1979
3063
  key: selectedKey,
1980
- title: plugin?.manifest.config?.schema?.[selectedKey]?.title ?? selectedKey,
3064
+ title: plugin?.manifest.config?.fields?.[selectedKey]?.title ?? selectedKey,
1981
3065
  kind: 'manifest',
1982
3066
  reserved: false,
1983
3067
  action: 'unset',
1984
3068
  scope,
1985
- ...(scope === 'project' ? { projectLayer } : {}),
3069
+ ...(!plugin.manifest.config?.fields?.[selectedKey] ? { stale: true } : {}),
1986
3070
  }],
1987
- };
3071
+ });
1988
3072
  });
1989
3073
  if (pluginName && key) {
1990
- return { cancelled: false, value: await execute(pluginName, key) };
3074
+ return {
3075
+ cancelled: false,
3076
+ value: await execute(pluginName, key, rawOptions.yes === true),
3077
+ };
1991
3078
  }
1992
3079
  if (!isWizardAvailable(context, rawOptions))
1993
3080
  throw new Error(configCommandUsage('unset'));
@@ -2003,7 +3090,7 @@ export function buildProgram(options = {}) {
2003
3090
  const result = await runCommandWizard(context, {
2004
3091
  command: 'config:unset',
2005
3092
  candidates,
2006
- initial: { plugins: pluginName ? [pluginName] : [], key, scope, projectLayer },
3093
+ initial: { plugins: pluginName ? [pluginName] : [], key, scope },
2007
3094
  pluginMode: pluginName ? 'none' : 'single',
2008
3095
  promptForKey: key === undefined,
2009
3096
  destructive: true,
@@ -2012,10 +3099,9 @@ export function buildProgram(options = {}) {
2012
3099
  plugin: selection.plugins[0],
2013
3100
  key: selection.key,
2014
3101
  scope: selection.scope ?? 'user',
2015
- projectLayer: selection.projectLayer,
2016
3102
  })),
2017
3103
  resultState: overallResultState,
2018
- execute: (selection) => execute(selection.plugins[0], selection.key),
3104
+ execute: (selection) => execute(selection.plugins[0], selection.key, true),
2019
3105
  });
2020
3106
  return result.cancelled
2021
3107
  ? { cancelled: true }
@@ -2034,21 +3120,19 @@ export function buildProgram(options = {}) {
2034
3120
  }
2035
3121
  else {
2036
3122
  writeLine(io.stdout, receipt.existed
2037
- ? `已删除 ${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope, receipt.projectLayer)}配置)。`
2038
- : `${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope, receipt.projectLayer)}配置)原本不存在。`);
2039
- if (receipt.projectLayer === 'local') {
2040
- writeProjectLocalConfigNotice(io.stdout);
2041
- }
3123
+ ? `已删除 ${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope)}配置)。`
3124
+ : `${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope)}配置)原本不存在。`);
2042
3125
  writeConfigApplyNotice(io.stderr, receipt);
2043
3126
  }
2044
- commandExitState.exitCode = receipt.overallState === 'failed' ? 1 : 0;
3127
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2045
3128
  });
2046
3129
  config
2047
3130
  .command('reset')
2048
3131
  .argument('[plugin]')
2049
3132
  .option('-s, --scope <scope>', 'project or user')
2050
- .option('--local', 'reset only the current project local override layer', false)
2051
- .option('-y, --yes', 'confirm destructive reset', false)
3133
+ .option('--apply', 'apply the saved config to visible desired placements', false)
3134
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
3135
+ .option('-y, --yes', 'confirm destructive reset and preApply/shared Codex native config apply', false)
2052
3136
  .option('-j, --json', 'print receipt as JSON', false)
2053
3137
  .option('-v, --verbose', 'show command diagnostics', false)
2054
3138
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -2058,61 +3142,79 @@ export function buildProgram(options = {}) {
2058
3142
  let outcome;
2059
3143
  try {
2060
3144
  outcome = await runActionWithContext(context, async () => {
2061
- const execute = async (selectedPlugin, selectedScope, projectLayer, confirmed) => {
3145
+ const execute = async (selectedPlugin, selectedScope, confirmed) => {
3146
+ const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2062
3147
  if (!confirmed) {
2063
- throw new Error(`Reset requires confirmation. Re-run \`aw config reset ${selectedPlugin} -s ${selectedScope}${projectLayer === 'local' ? ' --local' : ''} --yes\` to delete this store.`);
3148
+ throw new Error(`Reset requires confirmation. Re-run \`aw config reset ${selectedPlugin} -s ${selectedScope} --yes\` to delete this store.`);
2064
3149
  }
2065
- return withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken) => {
3150
+ const located = await resolveStoredConfigResetIdentity({
3151
+ selector: selectedPlugin,
3152
+ context,
3153
+ scope: selectedScope,
3154
+ });
3155
+ const resetLocatedStore = async (plugin, pluginError) => {
2066
3156
  const affectedFields = await describeResetAffectedFields({
2067
- pluginName: plugin.manifest.name,
3157
+ sourceId: located.sourceId,
3158
+ sourceIdentity: located.sourceIdentity,
3159
+ logicalIdentity: located.logicalIdentity,
3160
+ pluginName: located.pluginName,
2068
3161
  plugin,
3162
+ pluginError,
2069
3163
  scope: selectedScope,
2070
- projectLayer,
2071
3164
  cwd: context.cwd,
2072
3165
  env: context.env,
2073
- });
3166
+ }).catch(() => []);
2074
3167
  const resetReceipt = await resetPluginConfigWithHelper({
2075
- pluginName: plugin.manifest.name,
3168
+ sourceId: located.sourceId,
3169
+ sourceIdentity: located.sourceIdentity,
3170
+ logicalIdentity: located.logicalIdentity,
3171
+ pluginName: located.pluginName,
2076
3172
  scope: selectedScope,
2077
- projectLayer,
2078
3173
  cwd: context.cwd,
2079
3174
  env: context.env,
2080
3175
  nativeCliRunner: context.nativeCliRunner,
2081
- rebuildRuntime: { ...context, awHomeMutationToken },
2082
- awHomeMutationToken,
2083
3176
  plugin,
3177
+ pluginError,
3178
+ apply: rawOptions.apply === true,
3179
+ rebuildRuntime: scriptTimeoutMs === undefined ? undefined : { scriptTimeoutMs },
3180
+ sharedNativeApplyConfirmed: confirmed,
3181
+ preApplyConfirmed: rawOptions.yes === true,
2084
3182
  });
2085
- return {
3183
+ return buildConfigMutationReceipt('config:reset', {
2086
3184
  ok: resetReceipt.overallState === 'succeeded',
2087
- receiptSchemaVersion: 4,
2088
- command: 'config:reset',
2089
3185
  ...resetReceipt,
2090
- plugin: plugin.manifest.name,
2091
- pluginName: plugin.manifest.name,
2092
- pluginId,
2093
- sourceId: source.sourceId,
2094
- sourceIdentity: source.sourceIdentity,
3186
+ plugin: located.pluginName,
3187
+ pluginName: located.pluginName,
3188
+ pluginId: located.pluginId,
3189
+ sourceId: located.sourceId,
3190
+ ...(located.sourceIdentity ? { sourceIdentity: located.sourceIdentity } : {}),
2095
3191
  affectedFields,
2096
- };
2097
- }).catch((error) => {
2098
- const message = error instanceof Error ? error.message : String(error);
2099
- if (message.includes('Catalog source')
2100
- || message.startsWith('Cannot resolve required catalog sources:')) {
2101
- throw new Error(`sourceMissing: ${selectedPlugin} source is unavailable; config store was not changed.`);
2102
- }
2103
- if (message.includes('does not exist in the required catalog source set')) {
2104
- throw new Error(`configSchemaUnavailable: Plugin "${selectedPlugin}" is unavailable; config store was not changed.`);
2105
- }
2106
- throw error;
2107
- });
3192
+ });
3193
+ };
3194
+ let resetStarted = false;
3195
+ try {
3196
+ return await withResolvedCatalogPlugin({
3197
+ selector: located.pluginId,
3198
+ cliVersion: resolveCliVersion(),
3199
+ env: context.env,
3200
+ consume: async ({ plugin }) => {
3201
+ resetStarted = true;
3202
+ return resetLocatedStore(plugin, null);
3203
+ },
3204
+ });
3205
+ }
3206
+ catch (error) {
3207
+ if (resetStarted)
3208
+ throw error;
3209
+ return resetLocatedStore(null, error instanceof Error ? error : new Error(String(error)));
3210
+ }
2108
3211
  };
2109
3212
  const scope = resolveConfigWriteScope(rawOptions.scope);
2110
- const projectLayer = resolveConfigProjectLayer(rawOptions);
2111
3213
  const needsWizard = pluginName === undefined || rawOptions.yes !== true;
2112
3214
  if (!needsWizard) {
2113
3215
  return {
2114
3216
  cancelled: false,
2115
- value: await execute(pluginName, scope, projectLayer, true),
3217
+ value: await execute(pluginName, scope, true),
2116
3218
  };
2117
3219
  }
2118
3220
  if (!isWizardAvailable(context, rawOptions)) {
@@ -2120,16 +3222,16 @@ export function buildProgram(options = {}) {
2120
3222
  throw new Error(configCommandUsage('reset'));
2121
3223
  return {
2122
3224
  cancelled: false,
2123
- value: await execute(pluginName, scope, projectLayer, false),
3225
+ value: await execute(pluginName, scope, false),
2124
3226
  };
2125
3227
  }
2126
3228
  const currentCandidates = pluginName ? [] : await loadWizardCandidates(context, {});
2127
3229
  const candidatesByScope = new Map();
2128
3230
  if (!pluginName && (rawOptions.scope === undefined || scope === 'user')) {
2129
- candidatesByScope.set('user', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'user', projectLayer: 'shared' }], { includeEmptyStores: true })));
3231
+ candidatesByScope.set('user', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'user' }], { includeEmptyStores: true })));
2130
3232
  }
2131
3233
  if (!pluginName && (rawOptions.scope === undefined || scope === 'project')) {
2132
- candidatesByScope.set('project', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'project', projectLayer }], { includeEmptyStores: true })));
3234
+ candidatesByScope.set('project', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'project' }], { includeEmptyStores: true })));
2133
3235
  }
2134
3236
  const candidates = mergeWizardCandidates(candidatesByScope.get('user') ?? [], candidatesByScope.get('project') ?? []);
2135
3237
  if (!pluginName && candidates.length === 0) {
@@ -2138,7 +3240,7 @@ export function buildProgram(options = {}) {
2138
3240
  const result = await runCommandWizard(context, {
2139
3241
  command: 'config:reset',
2140
3242
  candidates,
2141
- initial: { plugins: pluginName ? [pluginName] : [], scope, projectLayer },
3243
+ initial: { plugins: pluginName ? [pluginName] : [], scope },
2142
3244
  pluginMode: pluginName ? 'none' : 'single',
2143
3245
  promptForScope: rawOptions.scope === undefined,
2144
3246
  destructive: true,
@@ -2149,11 +3251,10 @@ export function buildProgram(options = {}) {
2149
3251
  command: 'reset',
2150
3252
  plugin: selection.plugins[0],
2151
3253
  scope: selection.scope ?? 'user',
2152
- projectLayer: selection.projectLayer,
2153
3254
  confirmed: true,
2154
3255
  })),
2155
3256
  resultState: overallResultState,
2156
- execute: (selection) => execute(selection.plugins[0], selection.scope ?? 'user', selection.projectLayer ?? 'shared', true),
3257
+ execute: (selection) => execute(selection.plugins[0], selection.scope ?? 'user', true),
2157
3258
  });
2158
3259
  return result.cancelled
2159
3260
  ? { cancelled: true }
@@ -2172,14 +3273,11 @@ export function buildProgram(options = {}) {
2172
3273
  }
2173
3274
  else {
2174
3275
  writeLine(io.stdout, receipt.deleted
2175
- ? `已重置 ${receipt.plugin} 的${configLayerLabel(receipt.scope, receipt.projectLayer)}配置。`
2176
- : `${receipt.plugin} 没有可重置的${configLayerLabel(receipt.scope, receipt.projectLayer)}配置。`);
2177
- if (receipt.projectLayer === 'local') {
2178
- writeProjectLocalConfigNotice(io.stdout);
2179
- }
3276
+ ? `已重置 ${receipt.plugin} 的${configLayerLabel(receipt.scope)}配置。`
3277
+ : `${receipt.plugin} 没有可重置的${configLayerLabel(receipt.scope)}配置。`);
2180
3278
  writeConfigApplyNotice(io.stderr, receipt);
2181
3279
  }
2182
- commandExitState.exitCode = receipt.overallState === 'failed' ? 1 : 0;
3280
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2183
3281
  });
2184
3282
  program
2185
3283
  .command('status')
@@ -2192,7 +3290,7 @@ export function buildProgram(options = {}) {
2192
3290
  .option('-j, --json', 'print status as JSON', false)
2193
3291
  .action(async (plugins, rawOptions) => {
2194
3292
  const context = createActionContext('status', rawOptions);
2195
- const result = await runActionWithContext(context, () => statusCommandV4({
3293
+ const result = await runActionWithContext(context, () => statusCommandV8({
2196
3294
  plugins: plugins ?? [],
2197
3295
  cwd,
2198
3296
  env: context.env,
@@ -2205,11 +3303,11 @@ export function buildProgram(options = {}) {
2205
3303
  commandExitState.exitCode = result.hasErrors ? 1 : 0;
2206
3304
  writeLine(io.stdout, result.output);
2207
3305
  for (const source of result.payload.catalog.sources) {
2208
- if (source.error)
3306
+ if (rawOptions.json && source.error)
2209
3307
  writeLine(io.stderr, `${source.id}: ${source.error}`);
2210
3308
  }
2211
3309
  for (const error of result.payload.errors) {
2212
- if (error.code !== 'nativeStateUnreadable' && error.code !== 'stateInventoryFailed') {
3310
+ if (rawOptions.json && error.code !== 'nativeStateUnreadable' && error.code !== 'stateInventoryFailed') {
2213
3311
  writeLine(io.stderr, error.message);
2214
3312
  }
2215
3313
  }
@@ -2234,18 +3332,31 @@ export async function runCli(argv, options = {}) {
2234
3332
  ...createDefaultIo(),
2235
3333
  ...options.io,
2236
3334
  };
3335
+ const reviewCommand = argv[0] === 'review' && ['inspect', 'prepare'].includes(argv[1] ?? '')
3336
+ ? argv[1]
3337
+ : null;
3338
+ const jsonReviewCommand = reviewCommand !== null && argv.includes('--json');
2237
3339
  const jsonAuthoringCommand = argv.includes('--json')
2238
- && ((argv[0] === 'catalog' && ['init', 'version', 'build', 'validate', 'publish'].includes(argv[1] ?? ''))
2239
- || (argv[0] === 'plugin' && ['build', 'test'].includes(argv[1] ?? '')))
3340
+ && ((argv[0] === 'catalog' && ['init', 'context', 'version', 'build', 'validate', 'status', 'publish'].includes(argv[1] ?? ''))
3341
+ || (argv[0] === 'plugin' && ['build', 'test', 'validate', 'status', 'remove'].includes(argv[1] ?? ''))
3342
+ || reviewCommand !== null)
2240
3343
  ? `${argv[0]}:${argv[1]}`
2241
3344
  : null;
3345
+ const jsonCatalogDevCommand = argv.includes('--json')
3346
+ && argv[0] === 'catalog'
3347
+ && argv[1] === 'dev'
3348
+ && ['activate', 'status', 'deactivate'].includes(argv[2] ?? '')
3349
+ ? argv[2]
3350
+ : null;
3351
+ const jsonWriteRequest = jsonWriteCommandRequest(argv);
3352
+ const jsonConfigMutation = jsonConfigMutationRequest(argv);
2242
3353
  try {
2243
3354
  const authoringScriptByCommand = {
2244
3355
  init: 'init-plugin.mjs',
2245
3356
  'add-skill': 'add-skill.mjs',
3357
+ remove: 'remove-plugin.mjs',
2246
3358
  validate: 'validate-plugin.mjs',
2247
3359
  status: 'plugin-status.mjs',
2248
- 'check-versions': 'check-plugin-versions.mjs',
2249
3360
  };
2250
3361
  const authoringScript = argv[0] === 'plugin'
2251
3362
  ? authoringScriptByCommand[argv[1] ?? '']
@@ -2254,29 +3365,85 @@ export async function runCli(argv, options = {}) {
2254
3365
  : undefined;
2255
3366
  if (authoringScript && !argv.includes('--help') && !argv.includes('-h')) {
2256
3367
  const authoringArgs = argv.slice(2);
3368
+ let authoringEnv = options.env ?? process.env;
2257
3369
  if (argv[0] === 'plugin'
2258
- && argv[1] === 'validate'
2259
- && !authoringArgs.some((token) => token === '--workspace-root' || token.startsWith('--workspace-root='))) {
2260
- authoringArgs.push('--workspace-root', options.cwd ?? process.cwd());
3370
+ && !authoringArgs.some((token) => token === '--plugin-root' || token.startsWith('--plugin-root='))) {
3371
+ let explicitRoot;
3372
+ for (let index = 0; index < authoringArgs.length; index += 1) {
3373
+ const token = authoringArgs[index];
3374
+ if (token.startsWith('--workspace-root='))
3375
+ explicitRoot = token.slice('--workspace-root='.length);
3376
+ if (token === '--workspace-root')
3377
+ explicitRoot = authoringArgs[index + 1];
3378
+ }
3379
+ const originalCwd = options.cwd ?? process.cwd();
3380
+ let discoveryCwd = originalCwd;
3381
+ if (argv[1] === 'validate') {
3382
+ const valueOptions = new Set(['--workspace-root', '--claude-skill-profile', '--version-base']);
3383
+ for (let index = 0; index < authoringArgs.length; index += 1) {
3384
+ const token = authoringArgs[index];
3385
+ if (valueOptions.has(token)) {
3386
+ index += 1;
3387
+ continue;
3388
+ }
3389
+ if (!token.startsWith('-')) {
3390
+ discoveryCwd = path.resolve(originalCwd, token);
3391
+ authoringArgs[index] = discoveryCwd;
3392
+ break;
3393
+ }
3394
+ }
3395
+ }
3396
+ const workspace = await resolveCatalogWorkspaceRoot({
3397
+ cwd: discoveryCwd,
3398
+ ...(explicitRoot === undefined ? {} : { explicitRoot: path.resolve(options.cwd ?? process.cwd(), explicitRoot) }),
3399
+ mode: 'existing',
3400
+ });
3401
+ authoringEnv = {
3402
+ ...authoringEnv,
3403
+ AW_AUTHORING_WORKSPACE_ROOT: workspace.workspaceRoot,
3404
+ AW_AUTHORING_WORKSPACE_ROOT_RESOLVED_FROM: workspace.workspaceRootResolvedFrom,
3405
+ };
3406
+ if (explicitRoot === undefined)
3407
+ authoringArgs.push('--workspace-root', workspace.workspaceRoot);
2261
3408
  }
2262
3409
  return runAuthoringScript({
2263
3410
  scriptName: authoringScript,
2264
3411
  args: authoringArgs,
2265
3412
  cwd: options.cwd ?? process.cwd(),
2266
- env: options.env ?? process.env,
3413
+ env: authoringEnv,
2267
3414
  io,
2268
3415
  });
2269
3416
  }
2270
- if (argv.some((token) => token === '--subdir' || token.startsWith('--subdir='))
3417
+ if (!jsonConfigMutation
3418
+ && argv.some((token) => token === '--subdir' || token.startsWith('--subdir='))
2271
3419
  && argv.includes('--no-subdir')) {
2272
3420
  const message = 'option --subdir cannot be used with option --no-subdir';
2273
3421
  if (argv.includes('--json') && argv[0] === 'catalog' && argv[1] === 'source' && argv[2] === 'update') {
2274
3422
  writeJsonReceipt(io.stdout, {
2275
- receiptSchemaVersion: 4,
3423
+ receiptSchemaVersion: 6,
3424
+ command: 'catalog:source:update',
3425
+ ok: false,
3426
+ changed: false,
3427
+ sourceId: argv[3] ?? '',
3428
+ primaryChange: null,
3429
+ references: [],
3430
+ errors: [{ code: 'catalogSourceOptionConflict', message }],
3431
+ });
3432
+ }
3433
+ writeLine(io.stderr, `error: ${message}`);
3434
+ return 1;
3435
+ }
3436
+ if (argv[0] === 'catalog' && argv[1] === 'source' && argv[2] === 'update'
3437
+ && argv.includes('--auto-check') && argv.includes('--no-auto-check')) {
3438
+ const message = 'option --auto-check cannot be used with option --no-auto-check';
3439
+ if (argv.includes('--json') || argv.includes('-j')) {
3440
+ writeJsonReceipt(io.stdout, {
3441
+ receiptSchemaVersion: 6,
2276
3442
  command: 'catalog:source:update',
2277
3443
  ok: false,
2278
3444
  changed: false,
2279
3445
  sourceId: argv[3] ?? '',
3446
+ primaryChange: null,
2280
3447
  references: [],
2281
3448
  errors: [{ code: 'catalogSourceOptionConflict', message }],
2282
3449
  });
@@ -2304,9 +3471,30 @@ export async function runCli(argv, options = {}) {
2304
3471
  }
2305
3472
  return await runActionWithContext(context, () => runConsoleController(context));
2306
3473
  }
3474
+ let catalogPublicationActionStarted = false;
3475
+ let configMutationActionStarted = false;
3476
+ const programIo = jsonCatalogDevCommand || jsonWriteRequest || jsonReviewCommand
3477
+ ? { ...io, stderr: () => undefined }
3478
+ : jsonConfigMutation
3479
+ ? {
3480
+ ...io,
3481
+ stderr: (text) => {
3482
+ if (configMutationActionStarted)
3483
+ io.stderr(text);
3484
+ },
3485
+ }
3486
+ : jsonAuthoringCommand === 'catalog:publish'
3487
+ ? {
3488
+ ...io,
3489
+ stderr: (text) => {
3490
+ if (catalogPublicationActionStarted)
3491
+ io.stderr(text);
3492
+ },
3493
+ }
3494
+ : io;
2307
3495
  const program = buildProgram({
2308
3496
  ...options,
2309
- io,
3497
+ io: programIo,
2310
3498
  diagnostics: {
2311
3499
  ...options.diagnostics,
2312
3500
  metadata: {
@@ -2315,16 +3503,139 @@ export async function runCli(argv, options = {}) {
2315
3503
  },
2316
3504
  },
2317
3505
  });
3506
+ if (jsonAuthoringCommand === 'catalog:publish') {
3507
+ program.hook('preAction', () => {
3508
+ catalogPublicationActionStarted = true;
3509
+ });
3510
+ }
3511
+ if (jsonConfigMutation) {
3512
+ program.hook('preAction', () => {
3513
+ configMutationActionStarted = true;
3514
+ });
3515
+ }
2318
3516
  await program.parseAsync(['node', 'aw', ...argv]);
2319
3517
  return program.__awCommandExitState?.exitCode ?? 0;
2320
3518
  }
2321
3519
  catch (error) {
2322
- if (jsonAuthoringCommand && error instanceof CommanderError) {
2323
- writeJsonReceipt(io.stdout, {
2324
- command: jsonAuthoringCommand,
2325
- ok: false,
2326
- errors: [{ code: 'invalidArguments', message: error.message }],
3520
+ if (reviewCommand && error instanceof CommanderError) {
3521
+ if (error.exitCode === 0)
3522
+ return 0;
3523
+ if (jsonReviewCommand) {
3524
+ const receipt = reviewCommand === 'inspect'
3525
+ ? createReviewInspectionFailure(new Error(error.message), requestedReviewView(argv), 'invalidArguments')
3526
+ : createReviewPreparationFailure(error.message);
3527
+ writeJsonReceipt(io.stdout, receipt);
3528
+ }
3529
+ return 2;
3530
+ }
3531
+ if (jsonCatalogDevCommand) {
3532
+ const workspaceRootIndex = argv.findIndex((token) => token === '--workspace-root');
3533
+ const workspaceRootOption = argv.find((token) => token.startsWith('--workspace-root='));
3534
+ const workspaceRoot = workspaceRootOption?.slice('--workspace-root='.length)
3535
+ ?? (workspaceRootIndex >= 0 ? argv[workspaceRootIndex + 1] : undefined);
3536
+ const sourceIdIndex = argv.findIndex((token) => token === '--source-id');
3537
+ const sourceIdOption = argv.find((token) => token.startsWith('--source-id='));
3538
+ const sourceId = sourceIdOption?.slice('--source-id='.length)
3539
+ ?? (sourceIdIndex >= 0 ? argv[sourceIdIndex + 1] : undefined);
3540
+ const failure = error instanceof CommanderError
3541
+ ? { code: 'invalidArguments', message: error.message }
3542
+ : catalogDevSourceError(error);
3543
+ writeJsonReceipt(io.stdout, createCatalogDevFailureReceipt({
3544
+ command: jsonCatalogDevCommand,
3545
+ cwd: options.cwd ?? process.cwd(),
3546
+ workspaceRoot,
3547
+ sourceId,
3548
+ ...failure,
3549
+ }));
3550
+ return 1;
3551
+ }
3552
+ if (jsonWriteRequest) {
3553
+ writeCommandFailureReceipt(io.stdout, jsonWriteRequest, error);
3554
+ return 1;
3555
+ }
3556
+ if (jsonConfigMutation) {
3557
+ if (error instanceof CommanderError && error.exitCode === 0)
3558
+ return 0;
3559
+ const failure = configMutationFailure(error, options.env ?? process.env, options.cwd ?? process.cwd());
3560
+ writeConfigMutationFailureReceipt(io.stdout, error instanceof CommanderError
3561
+ ? { command: jsonConfigMutation.command }
3562
+ : jsonConfigMutation, failure);
3563
+ writeLine(io.stderr, failure.message);
3564
+ return 1;
3565
+ }
3566
+ if (jsonAuthoringCommand === 'catalog:publish' && error instanceof CommanderError) {
3567
+ const failure = catalogPublicationFailure({
3568
+ error: new CatalogPublicationInspectionError('invalidArguments', error.message),
3569
+ env: options.env,
3570
+ sensitiveRoots: [options.cwd ?? process.cwd()],
2327
3571
  });
3572
+ writeJsonReceipt(io.stdout, failure);
3573
+ writeLine(io.stderr, failure.message);
3574
+ return 1;
3575
+ }
3576
+ if (jsonAuthoringCommand && !(error instanceof CommanderError)) {
3577
+ const message = error instanceof Error ? error.message : String(error);
3578
+ if (jsonAuthoringCommand === 'plugin:validate') {
3579
+ writeJsonReceipt(io.stdout, {
3580
+ ok: false,
3581
+ profile: 'unknown',
3582
+ warnings: [],
3583
+ errors: [message],
3584
+ });
3585
+ }
3586
+ else if (jsonAuthoringCommand === 'plugin:status') {
3587
+ writeJsonReceipt(io.stdout, {
3588
+ schemaVersion: 3,
3589
+ policy: 'development',
3590
+ ok: false,
3591
+ base: null,
3592
+ requestedBase: null,
3593
+ baseCommit: null,
3594
+ checkedPlugins: 0,
3595
+ checks: [],
3596
+ workspaceRoot: null,
3597
+ workspaceRootResolvedFrom: null,
3598
+ errors: [{ code: 'workspaceResolutionFailed', message }],
3599
+ });
3600
+ }
3601
+ else if (jsonAuthoringCommand === 'plugin:remove') {
3602
+ writeJsonReceipt(io.stdout, {
3603
+ schemaVersion: 1,
3604
+ command: 'plugin:remove',
3605
+ ok: false,
3606
+ workspaceRoot: null,
3607
+ workspaceRootResolvedFrom: null,
3608
+ baseCommit: null,
3609
+ plugin: null,
3610
+ removed: false,
3611
+ errors: [{ code: 'workspaceResolutionFailed', message }],
3612
+ });
3613
+ }
3614
+ else if (jsonAuthoringCommand === 'review:inspect') {
3615
+ writeJsonReceipt(io.stdout, createReviewInspectionFailure(error, requestedReviewView(argv)));
3616
+ }
3617
+ else if (jsonAuthoringCommand === 'review:prepare') {
3618
+ writeJsonReceipt(io.stdout, createReviewPreparationFailure(message, 'reviewPreparationFailed'));
3619
+ }
3620
+ else {
3621
+ writeJsonReceipt(io.stdout, {
3622
+ command: jsonAuthoringCommand,
3623
+ ok: false,
3624
+ errors: [{ code: 'workspaceResolutionFailed', message }],
3625
+ });
3626
+ }
3627
+ return 1;
3628
+ }
3629
+ if (jsonAuthoringCommand && error instanceof CommanderError) {
3630
+ writeJsonReceipt(io.stdout, jsonAuthoringCommand === 'review:inspect'
3631
+ ? createReviewInspectionFailure(new Error(error.message), requestedReviewView(argv), 'invalidArguments')
3632
+ : jsonAuthoringCommand === 'review:prepare'
3633
+ ? createReviewPreparationFailure(error.message)
3634
+ : {
3635
+ command: jsonAuthoringCommand,
3636
+ ok: false,
3637
+ errors: [{ code: 'invalidArguments', message: error.message }],
3638
+ });
2328
3639
  }
2329
3640
  return handleCliError(error, io.stderr);
2330
3641
  }