@lesliechan721/aw 0.1.0-beta.4 → 0.1.0-beta.40

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