@lesliechan721/aw 0.1.0-beta.5 → 0.1.0-beta.51

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 (569) 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-root.mjs +4 -4
  11. package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -1
  12. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
  13. package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
  14. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  15. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -92
  16. package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -124
  17. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  18. package/dist/authoring/scripts/plugin-status.mjs +8 -11
  19. package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
  20. package/dist/authoring/scripts/remove-plugin.mjs +3 -2
  21. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
  22. package/dist/authoring/scripts/validate-plugin.mjs +346 -1131
  23. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  24. package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
  25. package/dist/scripts/aw-skill-eval-materializer.js +306 -0
  26. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  27. package/dist/scripts/bundle-plugins.d.ts +1 -0
  28. package/dist/scripts/bundle-plugins.js +20 -5
  29. package/dist/scripts/bundle-plugins.js.map +1 -1
  30. package/dist/scripts/plugin-staging.d.ts +7 -9
  31. package/dist/scripts/plugin-staging.js +18 -29
  32. package/dist/scripts/plugin-staging.js.map +1 -1
  33. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  34. package/dist/scripts/test-plugin-scripts.js +47 -76
  35. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  36. package/dist/scripts/test-workers.d.mts +24 -0
  37. package/dist/scripts/test-workers.mjs +59 -0
  38. package/dist/scripts/test-workers.mjs.map +1 -0
  39. package/dist/src/adapters/helpers.js +2 -2
  40. package/dist/src/adapters/helpers.js.map +1 -1
  41. package/dist/src/authoring/workspace-commands.d.ts +17 -1
  42. package/dist/src/authoring/workspace-commands.js +21 -4
  43. package/dist/src/authoring/workspace-commands.js.map +1 -1
  44. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  45. package/dist/src/commands/aw-capabilities.js +263 -0
  46. package/dist/src/commands/aw-capabilities.js.map +1 -0
  47. package/dist/src/commands/catalog-dev-source.d.ts +154 -0
  48. package/dist/src/commands/catalog-dev-source.js +655 -0
  49. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  50. package/dist/src/commands/catalog-generation.d.ts +21 -0
  51. package/dist/src/commands/catalog-generation.js +9 -0
  52. package/dist/src/commands/catalog-generation.js.map +1 -0
  53. package/dist/src/commands/catalog-publication-inspection.d.ts +71 -30
  54. package/dist/src/commands/catalog-publication-inspection.js +475 -63
  55. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  56. package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
  57. package/dist/src/commands/catalog-publication-journal.js +292 -0
  58. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  59. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  60. package/dist/src/commands/catalog-publication-target.js +95 -0
  61. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  62. package/dist/src/commands/catalog-publish.d.ts +73 -15
  63. package/dist/src/commands/catalog-publish.js +701 -212
  64. package/dist/src/commands/catalog-publish.js.map +1 -1
  65. package/dist/src/commands/catalog-source.d.ts +60 -22
  66. package/dist/src/commands/catalog-source.js +311 -91
  67. package/dist/src/commands/catalog-source.js.map +1 -1
  68. package/dist/src/commands/catalog-status.d.ts +18 -22
  69. package/dist/src/commands/catalog-status.js +81 -115
  70. package/dist/src/commands/catalog-status.js.map +1 -1
  71. package/dist/src/commands/catalog-store.d.ts +104 -0
  72. package/dist/src/commands/catalog-store.js +685 -0
  73. package/dist/src/commands/catalog-store.js.map +1 -0
  74. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  75. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  76. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  77. package/dist/src/commands/console.js +28 -17
  78. package/dist/src/commands/console.js.map +1 -1
  79. package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
  80. package/dist/src/commands/dependency-operation-plan.js +289 -7
  81. package/dist/src/commands/dependency-operation-plan.js.map +1 -1
  82. package/dist/src/commands/init.d.ts +5 -11
  83. package/dist/src/commands/init.js +30 -51
  84. package/dist/src/commands/init.js.map +1 -1
  85. package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
  86. package/dist/src/commands/offline-cleanup-evidence.js +181 -0
  87. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  88. package/dist/src/commands/operation-plan.d.ts +146 -0
  89. package/dist/src/commands/operation-plan.js +71 -0
  90. package/dist/src/commands/operation-plan.js.map +1 -0
  91. package/dist/src/commands/operation-planner.d.ts +46 -0
  92. package/dist/src/commands/operation-planner.js +141 -0
  93. package/dist/src/commands/operation-planner.js.map +1 -0
  94. package/dist/src/commands/options.d.ts +9 -7
  95. package/dist/src/commands/options.js +83 -21
  96. package/dist/src/commands/options.js.map +1 -1
  97. package/dist/src/commands/plugin-capabilities.js +23 -3
  98. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  99. package/dist/src/commands/rebuild-command.d.ts +77 -31
  100. package/dist/src/commands/rebuild-command.js +4862 -729
  101. package/dist/src/commands/rebuild-command.js.map +1 -1
  102. package/dist/src/commands/rebuild-integration.d.ts +131 -21
  103. package/dist/src/commands/rebuild-integration.js +2866 -1339
  104. package/dist/src/commands/rebuild-integration.js.map +1 -1
  105. package/dist/src/commands/rebuild-operations.d.ts +58 -30
  106. package/dist/src/commands/rebuild-operations.js +260 -319
  107. package/dist/src/commands/rebuild-operations.js.map +1 -1
  108. package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
  109. package/dist/src/commands/reconcile-operation-plan.js +47 -0
  110. package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
  111. package/dist/src/commands/review-inspection.d.ts +60 -0
  112. package/dist/src/commands/review-inspection.js +422 -0
  113. package/dist/src/commands/review-inspection.js.map +1 -0
  114. package/dist/src/commands/review-preparation.d.ts +49 -0
  115. package/dist/src/commands/review-preparation.js +427 -0
  116. package/dist/src/commands/review-preparation.js.map +1 -0
  117. package/dist/src/commands/runtime.d.ts +1 -5
  118. package/dist/src/commands/runtime.js +24 -50
  119. package/dist/src/commands/runtime.js.map +1 -1
  120. package/dist/src/commands/skill-eval.d.ts +39 -0
  121. package/dist/src/commands/skill-eval.js +400 -0
  122. package/dist/src/commands/skill-eval.js.map +1 -0
  123. package/dist/src/commands/status-v6.d.ts +279 -0
  124. package/dist/src/commands/status-v6.js +2787 -0
  125. package/dist/src/commands/status-v6.js.map +1 -0
  126. package/dist/src/commands/sync-command.d.ts +40 -0
  127. package/dist/src/commands/sync-command.js +250 -0
  128. package/dist/src/commands/sync-command.js.map +1 -0
  129. package/dist/src/commands/wizard.d.ts +8 -5
  130. package/dist/src/commands/wizard.js +6 -4
  131. package/dist/src/commands/wizard.js.map +1 -1
  132. package/dist/src/commands/write-commands.d.ts +9 -3
  133. package/dist/src/commands/write-commands.js +127 -31
  134. package/dist/src/commands/write-commands.js.map +1 -1
  135. package/dist/src/constants.d.ts +7 -1
  136. package/dist/src/constants.js +7 -1
  137. package/dist/src/constants.js.map +1 -1
  138. package/dist/src/eval/manifest-v1.d.ts +68 -0
  139. package/dist/src/eval/manifest-v1.js +353 -0
  140. package/dist/src/eval/manifest-v1.js.map +1 -0
  141. package/dist/src/generators/local-plugin.d.ts +5 -1
  142. package/dist/src/generators/local-plugin.js +236 -109
  143. package/dist/src/generators/local-plugin.js.map +1 -1
  144. package/dist/src/index.d.ts +10 -4
  145. package/dist/src/index.js +1752 -608
  146. package/dist/src/index.js.map +1 -1
  147. package/dist/src/output/command-receipt.d.ts +43 -46
  148. package/dist/src/output/command-receipt.js +256 -80
  149. package/dist/src/output/command-receipt.js.map +1 -1
  150. package/dist/src/output/model.d.ts +3 -44
  151. package/dist/src/output/model.js +1 -144
  152. package/dist/src/output/model.js.map +1 -1
  153. package/dist/src/output/rebuild-progress.js +1 -13
  154. package/dist/src/output/rebuild-progress.js.map +1 -1
  155. package/dist/src/portable/commands.d.ts +46 -7
  156. package/dist/src/portable/commands.js +349 -37
  157. package/dist/src/portable/commands.js.map +1 -1
  158. package/dist/src/portable/materializer.d.ts +17 -4
  159. package/dist/src/portable/materializer.js +179 -41
  160. package/dist/src/portable/materializer.js.map +1 -1
  161. package/dist/src/portable/skills-cli.d.ts +15 -7
  162. package/dist/src/portable/skills-cli.js +74 -20
  163. package/dist/src/portable/skills-cli.js.map +1 -1
  164. package/dist/src/review/eval-source-input.d.ts +43 -0
  165. package/dist/src/review/eval-source-input.js +207 -0
  166. package/dist/src/review/eval-source-input.js.map +1 -0
  167. package/dist/src/review/review-profile.d.ts +17 -0
  168. package/dist/src/review/review-profile.js +234 -0
  169. package/dist/src/review/review-profile.js.map +1 -0
  170. package/dist/src/review/review-selection.d.ts +30 -0
  171. package/dist/src/review/review-selection.js +527 -0
  172. package/dist/src/review/review-selection.js.map +1 -0
  173. package/dist/src/review/review-types.d.ts +68 -0
  174. package/dist/src/review/review-types.js +16 -0
  175. package/dist/src/review/review-types.js.map +1 -0
  176. package/dist/src/review/skill-materialization.d.ts +13 -0
  177. package/dist/src/review/skill-materialization.js +75 -0
  178. package/dist/src/review/skill-materialization.js.map +1 -0
  179. package/dist/src/review/source-binding.d.ts +183 -0
  180. package/dist/src/review/source-binding.js +708 -0
  181. package/dist/src/review/source-binding.js.map +1 -0
  182. package/dist/src/tui/catalog-freshness.d.ts +1 -0
  183. package/dist/src/tui/catalog-freshness.js +2 -0
  184. package/dist/src/tui/catalog-freshness.js.map +1 -0
  185. package/dist/src/tui/catalog-generation.d.ts +21 -0
  186. package/dist/src/tui/catalog-generation.js +156 -0
  187. package/dist/src/tui/catalog-generation.js.map +1 -0
  188. package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
  189. package/dist/src/tui/catalog-sources/projection.js +17 -0
  190. package/dist/src/tui/catalog-sources/projection.js.map +1 -0
  191. package/dist/src/tui/catalog-sources/review.d.ts +3 -0
  192. package/dist/src/tui/catalog-sources/review.js +13 -0
  193. package/dist/src/tui/catalog-sources/review.js.map +1 -0
  194. package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
  195. package/dist/src/tui/catalog-sources/screens.js +40 -0
  196. package/dist/src/tui/catalog-sources/screens.js.map +1 -0
  197. package/dist/src/tui/catalog-sources/session.d.ts +32 -0
  198. package/dist/src/tui/catalog-sources/session.js +300 -0
  199. package/dist/src/tui/catalog-sources/session.js.map +1 -0
  200. package/dist/src/tui/catalog-sources/types.d.ts +54 -0
  201. package/dist/src/tui/catalog-sources/types.js +2 -0
  202. package/dist/src/tui/catalog-sources/types.js.map +1 -0
  203. package/dist/src/tui/catalog-store-task.d.ts +16 -0
  204. package/dist/src/tui/catalog-store-task.js +19 -0
  205. package/dist/src/tui/catalog-store-task.js.map +1 -0
  206. package/dist/src/tui/clipboard.d.ts +13 -0
  207. package/dist/src/tui/clipboard.js +95 -0
  208. package/dist/src/tui/clipboard.js.map +1 -0
  209. package/dist/src/tui/components.d.ts +110 -3
  210. package/dist/src/tui/components.js +183 -36
  211. package/dist/src/tui/components.js.map +1 -1
  212. package/dist/src/tui/config-array-draft.d.ts +29 -0
  213. package/dist/src/tui/config-array-draft.js +48 -0
  214. package/dist/src/tui/config-array-draft.js.map +1 -0
  215. package/dist/src/tui/config-leaf-editor.d.ts +16 -0
  216. package/dist/src/tui/config-leaf-editor.js +206 -0
  217. package/dist/src/tui/config-leaf-editor.js.map +1 -0
  218. package/dist/src/tui/config-value-presentation.d.ts +20 -0
  219. package/dist/src/tui/config-value-presentation.js +104 -0
  220. package/dist/src/tui/config-value-presentation.js.map +1 -0
  221. package/dist/src/tui/console-app-v4.d.ts +14 -4
  222. package/dist/src/tui/console-app-v4.js +8941 -1872
  223. package/dist/src/tui/console-app-v4.js.map +1 -1
  224. package/dist/src/tui/content-item-card.d.ts +13 -0
  225. package/dist/src/tui/content-item-card.js +51 -0
  226. package/dist/src/tui/content-item-card.js.map +1 -0
  227. package/dist/src/tui/controller-runtime.d.ts +10 -2
  228. package/dist/src/tui/controller-runtime.js +3211 -388
  229. package/dist/src/tui/controller-runtime.js.map +1 -1
  230. package/dist/src/tui/controller.d.ts +615 -45
  231. package/dist/src/tui/controller.js +3558 -768
  232. package/dist/src/tui/controller.js.map +1 -1
  233. package/dist/src/tui/dependency-graph-view.d.ts +39 -0
  234. package/dist/src/tui/dependency-graph-view.js +121 -0
  235. package/dist/src/tui/dependency-graph-view.js.map +1 -0
  236. package/dist/src/tui/dependency-graph.d.ts +81 -0
  237. package/dist/src/tui/dependency-graph.js +452 -0
  238. package/dist/src/tui/dependency-graph.js.map +1 -0
  239. package/dist/src/tui/diagnostic-projection.d.ts +23 -0
  240. package/dist/src/tui/diagnostic-projection.js +567 -0
  241. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  242. package/dist/src/tui/diagnostics.d.ts +3 -3
  243. package/dist/src/tui/diagnostics.js +14 -2
  244. package/dist/src/tui/diagnostics.js.map +1 -1
  245. package/dist/src/tui/entity-cards.d.ts +55 -0
  246. package/dist/src/tui/entity-cards.js +220 -0
  247. package/dist/src/tui/entity-cards.js.map +1 -0
  248. package/dist/src/tui/glossary.d.ts +23 -0
  249. package/dist/src/tui/glossary.js +42 -0
  250. package/dist/src/tui/glossary.js.map +1 -0
  251. package/dist/src/tui/index.d.ts +6 -1
  252. package/dist/src/tui/index.js +5 -1
  253. package/dist/src/tui/index.js.map +1 -1
  254. package/dist/src/tui/input-display.d.ts +9 -0
  255. package/dist/src/tui/input-display.js +58 -0
  256. package/dist/src/tui/input-display.js.map +1 -0
  257. package/dist/src/tui/keys.d.ts +44 -0
  258. package/dist/src/tui/keys.js +43 -15
  259. package/dist/src/tui/keys.js.map +1 -1
  260. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  261. package/dist/src/tui/lifecycle-action-view.js +36 -0
  262. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  263. package/dist/src/tui/model.d.ts +223 -31
  264. package/dist/src/tui/model.js +1510 -397
  265. package/dist/src/tui/model.js.map +1 -1
  266. package/dist/src/tui/operation-diagnostic-screens.d.ts +8 -0
  267. package/dist/src/tui/operation-diagnostic-screens.js +71 -0
  268. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  269. package/dist/src/tui/operation-history.d.ts +81 -0
  270. package/dist/src/tui/operation-history.js +136 -0
  271. package/dist/src/tui/operation-history.js.map +1 -0
  272. package/dist/src/tui/operation-review-planner.d.ts +21 -0
  273. package/dist/src/tui/operation-review-planner.js +1295 -0
  274. package/dist/src/tui/operation-review-planner.js.map +1 -0
  275. package/dist/src/tui/plugin-content.d.ts +31 -0
  276. package/dist/src/tui/plugin-content.js +680 -0
  277. package/dist/src/tui/plugin-content.js.map +1 -0
  278. package/dist/src/tui/plugin-detail.d.ts +52 -0
  279. package/dist/src/tui/plugin-detail.js +165 -0
  280. package/dist/src/tui/plugin-detail.js.map +1 -0
  281. package/dist/src/tui/plugin-write-task.d.ts +69 -0
  282. package/dist/src/tui/plugin-write-task.js +286 -0
  283. package/dist/src/tui/plugin-write-task.js.map +1 -0
  284. package/dist/src/tui/project-inventory.d.ts +68 -0
  285. package/dist/src/tui/project-inventory.js +203 -0
  286. package/dist/src/tui/project-inventory.js.map +1 -0
  287. package/dist/src/tui/prompt.js +4 -15
  288. package/dist/src/tui/prompt.js.map +1 -1
  289. package/dist/src/tui/query-header.d.ts +30 -0
  290. package/dist/src/tui/query-header.js +40 -0
  291. package/dist/src/tui/query-header.js.map +1 -0
  292. package/dist/src/tui/reader.d.ts +37 -0
  293. package/dist/src/tui/reader.js +47 -0
  294. package/dist/src/tui/reader.js.map +1 -0
  295. package/dist/src/tui/router.d.ts +197 -10
  296. package/dist/src/tui/router.js +338 -50
  297. package/dist/src/tui/router.js.map +1 -1
  298. package/dist/src/tui/screen-registry.d.ts +701 -0
  299. package/dist/src/tui/screen-registry.js +230 -0
  300. package/dist/src/tui/screen-registry.js.map +1 -0
  301. package/dist/src/tui/session-console-app.d.ts +6 -0
  302. package/dist/src/tui/session-console-app.js +13 -0
  303. package/dist/src/tui/session-console-app.js.map +1 -0
  304. package/dist/src/tui/session-host.d.ts +45 -0
  305. package/dist/src/tui/session-host.js +209 -0
  306. package/dist/src/tui/session-host.js.map +1 -0
  307. package/dist/src/tui/source-detail-card.d.ts +29 -0
  308. package/dist/src/tui/source-detail-card.js +45 -0
  309. package/dist/src/tui/source-detail-card.js.map +1 -0
  310. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  311. package/dist/src/tui/source-ref-presets.js +11 -0
  312. package/dist/src/tui/source-ref-presets.js.map +1 -0
  313. package/dist/src/tui/theme.d.ts +12 -0
  314. package/dist/src/tui/theme.js +12 -0
  315. package/dist/src/tui/theme.js.map +1 -1
  316. package/dist/src/tui/viewport.d.ts +1 -0
  317. package/dist/src/tui/viewport.js +23 -0
  318. package/dist/src/tui/viewport.js.map +1 -1
  319. package/dist/src/tui/views.js +31 -3
  320. package/dist/src/tui/views.js.map +1 -1
  321. package/dist/src/tui/wizard.js +37 -13
  322. package/dist/src/tui/wizard.js.map +1 -1
  323. package/dist/src/types.d.ts +244 -20
  324. package/dist/src/types.js +1 -0
  325. package/dist/src/types.js.map +1 -1
  326. package/dist/src/utils/agent-version.d.ts +7 -1
  327. package/dist/src/utils/agent-version.js +41 -8
  328. package/dist/src/utils/agent-version.js.map +1 -1
  329. package/dist/src/utils/artifact-plan.d.ts +5 -4
  330. package/dist/src/utils/artifact-plan.js +18 -7
  331. package/dist/src/utils/artifact-plan.js.map +1 -1
  332. package/dist/src/utils/artifact-renderer.d.ts +17 -7
  333. package/dist/src/utils/artifact-renderer.js +214 -216
  334. package/dist/src/utils/artifact-renderer.js.map +1 -1
  335. package/dist/src/utils/artifact-request.d.ts +21 -0
  336. package/dist/src/utils/artifact-request.js +78 -0
  337. package/dist/src/utils/artifact-request.js.map +1 -0
  338. package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
  339. package/dist/src/utils/aw-home-mutation-lock.js +32 -4
  340. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
  341. package/dist/src/utils/bundle-integrity.d.ts +0 -1
  342. package/dist/src/utils/bundle-integrity.js +0 -25
  343. package/dist/src/utils/bundle-integrity.js.map +1 -1
  344. package/dist/src/utils/bundle-layout.js +22 -29
  345. package/dist/src/utils/bundle-layout.js.map +1 -1
  346. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  347. package/dist/src/utils/catalog-capability-projection.js +14 -0
  348. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  349. package/dist/src/utils/catalog-digests.d.ts +10 -0
  350. package/dist/src/utils/catalog-digests.js +57 -20
  351. package/dist/src/utils/catalog-digests.js.map +1 -1
  352. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  353. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  354. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  355. package/dist/src/utils/catalog-generation-error.d.ts +16 -0
  356. package/dist/src/utils/catalog-generation-error.js +30 -0
  357. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  358. package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
  359. package/dist/src/utils/catalog-generation-resolver.js +273 -0
  360. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  361. package/dist/src/utils/catalog-generation-service.d.ts +172 -0
  362. package/dist/src/utils/catalog-generation-service.js +1271 -0
  363. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  364. package/dist/src/utils/catalog-generation-state.d.ts +22 -0
  365. package/dist/src/utils/catalog-generation-state.js +192 -0
  366. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  367. package/dist/src/utils/catalog-generation-store.d.ts +58 -0
  368. package/dist/src/utils/catalog-generation-store.js +452 -0
  369. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  370. package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
  371. package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
  372. package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
  373. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  374. package/dist/src/utils/catalog-operation-context.js +142 -0
  375. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  376. package/dist/src/utils/catalog-public-error.d.ts +7 -3
  377. package/dist/src/utils/catalog-public-error.js +28 -8
  378. package/dist/src/utils/catalog-public-error.js.map +1 -1
  379. package/dist/src/utils/catalog-runtime-projection.d.ts +3 -0
  380. package/dist/src/utils/catalog-runtime-projection.js +5 -40
  381. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  382. package/dist/src/utils/catalog-snapshot.d.ts +5 -3
  383. package/dist/src/utils/catalog-snapshot.js +11 -3
  384. package/dist/src/utils/catalog-snapshot.js.map +1 -1
  385. package/dist/src/utils/catalog-source-identity.d.ts +1 -0
  386. package/dist/src/utils/catalog-source-identity.js +21 -2
  387. package/dist/src/utils/catalog-source-identity.js.map +1 -1
  388. package/dist/src/utils/catalog-source-impact.d.ts +21 -0
  389. package/dist/src/utils/catalog-source-impact.js +344 -0
  390. package/dist/src/utils/catalog-source-impact.js.map +1 -0
  391. package/dist/src/utils/catalog-source-references.d.ts +3 -2
  392. package/dist/src/utils/catalog-source-references.js +195 -347
  393. package/dist/src/utils/catalog-source-references.js.map +1 -1
  394. package/dist/src/utils/catalog-sources.d.ts +15 -1
  395. package/dist/src/utils/catalog-sources.js +206 -27
  396. package/dist/src/utils/catalog-sources.js.map +1 -1
  397. package/dist/src/utils/codex-config.d.ts +2 -0
  398. package/dist/src/utils/codex-config.js +26 -5
  399. package/dist/src/utils/codex-config.js.map +1 -1
  400. package/dist/src/utils/codex-manager.d.ts +79 -0
  401. package/dist/src/utils/codex-manager.js +747 -0
  402. package/dist/src/utils/codex-manager.js.map +1 -0
  403. package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
  404. package/dist/src/utils/codex-marketplace-path.js +45 -0
  405. package/dist/src/utils/codex-marketplace-path.js.map +1 -0
  406. package/dist/src/utils/codex-native-observation.d.ts +49 -0
  407. package/dist/src/utils/codex-native-observation.js +154 -0
  408. package/dist/src/utils/codex-native-observation.js.map +1 -0
  409. package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
  410. package/dist/src/utils/codex-native-render-receipt.js +205 -0
  411. package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
  412. package/dist/src/utils/codex-reconciler.d.ts +126 -0
  413. package/dist/src/utils/codex-reconciler.js +298 -0
  414. package/dist/src/utils/codex-reconciler.js.map +1 -0
  415. package/dist/src/utils/codex-shared-native.d.ts +43 -0
  416. package/dist/src/utils/codex-shared-native.js +81 -0
  417. package/dist/src/utils/codex-shared-native.js.map +1 -0
  418. package/dist/src/utils/codex-skill-staging.d.ts +2 -0
  419. package/dist/src/utils/codex-skill-staging.js.map +1 -1
  420. package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
  421. package/dist/src/utils/codex-surface-analysis.js +247 -0
  422. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  423. package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
  424. package/dist/src/utils/dynamic-skill-tree.js +35 -5
  425. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  426. package/dist/src/utils/git-catalog-provider.d.ts +16 -0
  427. package/dist/src/utils/git-catalog-provider.js +177 -145
  428. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  429. package/dist/src/utils/local-marketplace.d.ts +18 -1
  430. package/dist/src/utils/local-marketplace.js +354 -84
  431. package/dist/src/utils/local-marketplace.js.map +1 -1
  432. package/dist/src/utils/manifest.d.ts +1 -3
  433. package/dist/src/utils/manifest.js +1 -3
  434. package/dist/src/utils/manifest.js.map +1 -1
  435. package/dist/src/utils/multi-source-catalog.d.ts +3 -9
  436. package/dist/src/utils/multi-source-catalog.js +54 -110
  437. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  438. package/dist/src/utils/native-cli.d.ts +55 -7
  439. package/dist/src/utils/native-cli.js +400 -58
  440. package/dist/src/utils/native-cli.js.map +1 -1
  441. package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
  442. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  443. package/dist/src/utils/ownership.d.ts +84 -20
  444. package/dist/src/utils/ownership.js +179 -222
  445. package/dist/src/utils/ownership.js.map +1 -1
  446. package/dist/src/utils/path-catalog-provider.d.ts +1 -1
  447. package/dist/src/utils/path-catalog-provider.js +54 -65
  448. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  449. package/dist/src/utils/paths.d.ts +0 -1
  450. package/dist/src/utils/paths.js +0 -1
  451. package/dist/src/utils/paths.js.map +1 -1
  452. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  453. package/dist/src/utils/plugin-config-impact.js +298 -0
  454. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  455. package/dist/src/utils/plugin-config-write.d.ts +52 -27
  456. package/dist/src/utils/plugin-config-write.js +1000 -296
  457. package/dist/src/utils/plugin-config-write.js.map +1 -1
  458. package/dist/src/utils/plugin-config.d.ts +153 -93
  459. package/dist/src/utils/plugin-config.js +1116 -968
  460. package/dist/src/utils/plugin-config.js.map +1 -1
  461. package/dist/src/utils/plugin-intent-state.d.ts +49 -0
  462. package/dist/src/utils/plugin-intent-state.js +320 -0
  463. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  464. package/dist/src/utils/plugin-scripts.d.ts +65 -125
  465. package/dist/src/utils/plugin-scripts.js +484 -524
  466. package/dist/src/utils/plugin-scripts.js.map +1 -1
  467. package/dist/src/utils/process-tree.d.ts +2 -2
  468. package/dist/src/utils/process-tree.js +1 -1
  469. package/dist/src/utils/process-tree.js.map +1 -1
  470. package/dist/src/utils/project-boundary.d.ts +11 -13
  471. package/dist/src/utils/project-boundary.js +28 -34
  472. package/dist/src/utils/project-boundary.js.map +1 -1
  473. package/dist/src/utils/runtime-config.d.ts +20 -0
  474. package/dist/src/utils/runtime-config.js +75 -0
  475. package/dist/src/utils/runtime-config.js.map +1 -0
  476. package/dist/src/utils/safe-fs.d.ts +21 -0
  477. package/dist/src/utils/safe-fs.js +111 -0
  478. package/dist/src/utils/safe-fs.js.map +1 -0
  479. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  480. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  481. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  482. package/dist/src/utils/sensitive-content.d.ts +4 -0
  483. package/dist/src/utils/sensitive-content.js +36 -0
  484. package/dist/src/utils/sensitive-content.js.map +1 -0
  485. package/dist/src/utils/state-paths.js +7 -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 +7 -0
  491. package/dist/src/utils/windows-claude-shim.js +10 -0
  492. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  493. package/dist/src/utils/windows-npm-shim.d.ts +14 -0
  494. package/dist/src/utils/windows-npm-shim.js +140 -0
  495. package/dist/src/utils/windows-npm-shim.js.map +1 -0
  496. package/dist/vitest.config.js +92 -10
  497. package/dist/vitest.config.js.map +1 -1
  498. package/package.json +27 -18
  499. package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
  500. package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
  501. package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
  502. package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
  503. package/dist/scripts/aw-skill-eval-engine.js +0 -31
  504. package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
  505. package/dist/scripts/codex-eval-app-server.d.ts +0 -89
  506. package/dist/scripts/codex-eval-app-server.js +0 -1228
  507. package/dist/scripts/codex-eval-app-server.js.map +0 -1
  508. package/dist/scripts/codex-eval-isolation.d.ts +0 -61
  509. package/dist/scripts/codex-eval-isolation.js +0 -318
  510. package/dist/scripts/codex-eval-isolation.js.map +0 -1
  511. package/dist/scripts/codex-eval-process-management.d.ts +0 -8
  512. package/dist/scripts/codex-eval-process-management.js +0 -69
  513. package/dist/scripts/codex-eval-process-management.js.map +0 -1
  514. package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
  515. package/dist/scripts/codex-eval-process-tree.js +0 -803
  516. package/dist/scripts/codex-eval-process-tree.js.map +0 -1
  517. package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
  518. package/dist/scripts/codex-eval-runtime-config.js +0 -843
  519. package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
  520. package/dist/scripts/codex-trigger-eval.d.ts +0 -84
  521. package/dist/scripts/codex-trigger-eval.js +0 -968
  522. package/dist/scripts/codex-trigger-eval.js.map +0 -1
  523. package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
  524. package/dist/scripts/eval-plugin-skills-audited.js +0 -261
  525. package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
  526. package/dist/scripts/eval-plugin-skills.d.ts +0 -45
  527. package/dist/scripts/eval-plugin-skills.js +0 -1586
  528. package/dist/scripts/eval-plugin-skills.js.map +0 -1
  529. package/dist/scripts/eval-run-layout.d.ts +0 -17
  530. package/dist/scripts/eval-run-layout.js +0 -26
  531. package/dist/scripts/eval-run-layout.js.map +0 -1
  532. package/dist/scripts/plugin-eval-contract.d.ts +0 -185
  533. package/dist/scripts/plugin-eval-contract.js +0 -695
  534. package/dist/scripts/plugin-eval-contract.js.map +0 -1
  535. package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
  536. package/dist/scripts/plugin-eval-materializer.js +0 -421
  537. package/dist/scripts/plugin-eval-materializer.js.map +0 -1
  538. package/dist/scripts/skill-reporting.d.ts +0 -212
  539. package/dist/scripts/skill-reporting.js +0 -922
  540. package/dist/scripts/skill-reporting.js.map +0 -1
  541. package/dist/src/commands/review-publication.d.ts +0 -99
  542. package/dist/src/commands/review-publication.js +0 -904
  543. package/dist/src/commands/review-publication.js.map +0 -1
  544. package/dist/src/commands/status-v4.d.ts +0 -123
  545. package/dist/src/commands/status-v4.js +0 -905
  546. package/dist/src/commands/status-v4.js.map +0 -1
  547. package/dist/src/commands/status.d.ts +0 -93
  548. package/dist/src/commands/status.js +0 -442
  549. package/dist/src/commands/status.js.map +0 -1
  550. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
  551. package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
  552. package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
  553. package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
  554. package/dist/src/utils/catalog-manifest-v3.js +0 -3
  555. package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
  556. package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
  557. package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
  558. package/dist/src/utils/catalog-source-cache.d.ts +0 -62
  559. package/dist/src/utils/catalog-source-cache.js +0 -296
  560. package/dist/src/utils/catalog-source-cache.js.map +0 -1
  561. package/dist/src/utils/codex-project-surface.d.ts +0 -3
  562. package/dist/src/utils/codex-project-surface.js +0 -49
  563. package/dist/src/utils/codex-project-surface.js.map +0 -1
  564. package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
  565. package/dist/src/utils/lifecycle-active-store.js +0 -1276
  566. package/dist/src/utils/lifecycle-active-store.js.map +0 -1
  567. package/dist/src/utils/plugin-release.d.ts +0 -2
  568. package/dist/src/utils/plugin-release.js +0 -2
  569. package/dist/src/utils/plugin-release.js.map +0 -1
package/dist/src/index.js CHANGED
@@ -3,39 +3,48 @@ 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
- import { computeReviewComponentDigest, listReviewRecords, parseReviewAttachment, publishReviewRecord, showReviewRecord, verifyReviewRecord, } from './commands/review-publication.js';
34
- import { resolveRepoRoot } from './utils/state-paths.js';
38
+ import { createReviewInspectionFailure, getReviewInspectionExitCode, inspectReviewTarget, } from './commands/review-inspection.js';
39
+ import { prepareReviewTarget, } from './commands/review-preparation.js';
35
40
  import { runCatalogBuildCommand, runCatalogInitCommand, runCatalogValidateCommand, runCatalogVersionCommand, runPluginBuildCommand, runPluginTestCommand, } from './authoring/workspace-commands.js';
36
- import { runCatalogPublishCommand } from './commands/catalog-publish.js';
41
+ import { runCatalogPublishApplyCommand, runCatalogPublishPlanCommand, } from './commands/catalog-publish.js';
42
+ import { catalogPublicationFailure, CatalogPublicationInspectionError, } from './commands/catalog-publication-inspection.js';
37
43
  import { getCatalogStatus, renderCatalogStatus } from './commands/catalog-status.js';
44
+ import { activateCatalogDevSource, catalogDevSourceError, createCatalogDevFailureReceipt, deactivateCatalogDevSource, getCatalogDevSourceStatus, } from './commands/catalog-dev-source.js';
38
45
  import { getPluginCapabilitiesResult, parsePluginCapabilitySurface, renderPluginCapabilities, } from './commands/plugin-capabilities.js';
46
+ import { AwCapabilityUnsupportedError, checkCurrentAw, describeCurrentAw, inspectPluginRequirements, } from './commands/aw-capabilities.js';
47
+ import { prepareSkillEval } from './commands/skill-eval.js';
39
48
  function createDefaultIo() {
40
49
  return {
41
50
  stdout: (text) => process.stdout.write(text),
@@ -45,6 +54,50 @@ function createDefaultIo() {
45
54
  function writeLine(write, line) {
46
55
  write(`${line}\n`);
47
56
  }
57
+ function strictValueOption(flags, description, choices) {
58
+ let seen = false;
59
+ const option = new Option(flags, description);
60
+ return option.argParser((value) => {
61
+ if (seen)
62
+ throw new InvalidArgumentError(`${option.long} can be specified only once.`);
63
+ if (choices && !choices.includes(value)) {
64
+ throw new InvalidArgumentError(`${option.long} must be one of: ${choices.join(', ')}.`);
65
+ }
66
+ seen = true;
67
+ return value;
68
+ });
69
+ }
70
+ function collectUniqueOption(value, previous) {
71
+ return previous.includes(value) ? previous : [...previous, value];
72
+ }
73
+ function renderReviewSkills(write, skills, issues) {
74
+ for (const skill of skills) {
75
+ writeLine(write, `${skill.skill}\t${skill.profile.platform}/${skill.profile.scope}\t${skill.state}\t${skill.finalPath ?? '-'}`);
76
+ }
77
+ for (const issue of issues)
78
+ writeLine(write, `${issue.code}: ${issue.message}`);
79
+ }
80
+ function renderReviewInspection(write, receipt) {
81
+ writeLine(write, `Review inspection (${receipt.view}): ${receipt.ok ? 'ok' : 'failed'}`);
82
+ renderReviewSkills(write, receipt.skills, receipt.issues);
83
+ }
84
+ function renderReviewPreparation(write, receipt) {
85
+ writeLine(write, `Review preparation: ${receipt.ok ? 'complete' : 'failed'}`);
86
+ if (receipt.sourceBindingPath)
87
+ writeLine(write, `Source binding: ${receipt.sourceBindingPath}`);
88
+ renderReviewSkills(write, receipt.skills, receipt.issues);
89
+ }
90
+ function createReviewPreparationFailure(message, code = 'invalidArguments') {
91
+ return {
92
+ schemaVersion: 1,
93
+ command: 'review:prepare',
94
+ ok: false,
95
+ target: null,
96
+ sourceBindingPath: null,
97
+ skills: [],
98
+ issues: [{ code, path: null, message }],
99
+ };
100
+ }
48
101
  function writeWarnings(write, warnings, options = {}) {
49
102
  for (const warning of formatWarnings(warnings, options)) {
50
103
  writeLine(write, warning);
@@ -56,10 +109,266 @@ function resolveCliVersion() {
56
109
  return assertStrictSemVer(packageJson.version, 'packages/cli/package.json version');
57
110
  }
58
111
  function resolveCommandReceiptExitCode(result) {
59
- if (result.overallState === 'failed') {
60
- return 1;
112
+ return result.overallState === 'succeeded' ? 0 : 1;
113
+ }
114
+ class WriteCommandRequestError extends Error {
115
+ code = 'invalidArguments';
116
+ }
117
+ function normalizeWriteRequest(normalize) {
118
+ try {
119
+ return normalize();
120
+ }
121
+ catch (error) {
122
+ throw new WriteCommandRequestError(error instanceof Error ? error.message : String(error));
123
+ }
124
+ }
125
+ function writeCommandFailure(error) {
126
+ const message = redactString(error instanceof Error ? error.message : String(error));
127
+ if (error instanceof CommanderError || error instanceof WriteCommandRequestError) {
128
+ return { code: 'invalidArguments', message };
129
+ }
130
+ const explicitCode = error?.code;
131
+ if (typeof explicitCode === 'string' && /^[A-Za-z][A-Za-z0-9]*$/u.test(explicitCode)) {
132
+ return { code: explicitCode, message };
133
+ }
134
+ const separator = message.indexOf(':');
135
+ const prefix = separator > 0 ? message.slice(0, separator) : '';
136
+ return {
137
+ code: /^[A-Za-z][A-Za-z0-9]*$/u.test(prefix) ? prefix : 'writeCommandFailed',
138
+ message,
139
+ };
140
+ }
141
+ function preparseWriteReceiptRequest(argv) {
142
+ const request = { json: false, plugins: [] };
143
+ let positionalOnly = false;
144
+ for (let index = 1; index < argv.length; index += 1) {
145
+ const token = argv[index];
146
+ if (positionalOnly) {
147
+ request.plugins.push(token);
148
+ continue;
149
+ }
150
+ if (token === '--') {
151
+ positionalOnly = true;
152
+ continue;
153
+ }
154
+ if (token === '--json') {
155
+ request.json = true;
156
+ continue;
157
+ }
158
+ if (token === '--platform' || token === '--scope') {
159
+ const value = argv[index + 1];
160
+ if (token === '--platform')
161
+ request.platform = value;
162
+ else
163
+ request.scope = value;
164
+ index += 1;
165
+ continue;
166
+ }
167
+ if (token.startsWith('--platform=')) {
168
+ request.platform = token.slice('--platform='.length);
169
+ continue;
170
+ }
171
+ if (token.startsWith('--scope=')) {
172
+ request.scope = token.slice('--scope='.length);
173
+ continue;
174
+ }
175
+ if (token === '--dependency-scope' || token === '--script-timeout' || token === '--log-file') {
176
+ index += 1;
177
+ continue;
178
+ }
179
+ if (token.startsWith('--'))
180
+ continue;
181
+ if (token.startsWith('-') && token !== '-') {
182
+ const shortOptions = token.slice(1);
183
+ for (let offset = 0; offset < shortOptions.length; offset += 1) {
184
+ const option = shortOptions[offset];
185
+ if (option === 'j') {
186
+ request.json = true;
187
+ continue;
188
+ }
189
+ if (option === 'p' || option === 's') {
190
+ const value = shortOptions.slice(offset + 1) || argv[index + 1];
191
+ if (option === 'p')
192
+ request.platform = value;
193
+ else
194
+ request.scope = value;
195
+ if (shortOptions.length === offset + 1)
196
+ index += 1;
197
+ break;
198
+ }
199
+ }
200
+ continue;
201
+ }
202
+ request.plugins.push(token);
203
+ }
204
+ return request;
205
+ }
206
+ function jsonWriteCommandRequest(argv) {
207
+ const command = argv[0];
208
+ if (!['install', 'reinstall', 'uninstall', 'cleanup', 'sync'].includes(command ?? '')) {
209
+ return null;
210
+ }
211
+ const request = preparseWriteReceiptRequest(argv);
212
+ if (!request.json)
213
+ return null;
214
+ const rawPlatform = request.platform;
215
+ const platform = rawPlatform === 'codex' || rawPlatform === 'claude' || rawPlatform === 'both'
216
+ ? rawPlatform
217
+ : 'codex';
218
+ return {
219
+ command: command,
220
+ plugins: request.plugins,
221
+ platform,
222
+ scope: request.scope === 'project' ? 'project' : 'user',
223
+ };
224
+ }
225
+ function writeCommandFailureReceipt(write, request, error) {
226
+ const platforms = request.platform === 'both'
227
+ ? ['codex', 'claude']
228
+ : [request.platform];
229
+ writeJsonReceipt(write, buildRebuildReceiptJson(request.command, {
230
+ platforms,
231
+ plugins: request.plugins,
232
+ warnings: [],
233
+ overallState: 'failed',
234
+ errors: [writeCommandFailure(error)],
235
+ receiptTargets: request.plugins.flatMap((selector) => platforms.map((platform) => {
236
+ let pluginName = selector;
237
+ let sourceId;
238
+ try {
239
+ const parsed = parsePluginId(selector);
240
+ pluginName = parsed.pluginName;
241
+ sourceId = parsed.sourceId;
242
+ }
243
+ catch {
244
+ // Keep the raw selector in the failed receipt.
245
+ }
246
+ return {
247
+ pluginName,
248
+ pluginId: selector,
249
+ ...(sourceId ? { sourceId } : {}),
250
+ platform,
251
+ scope: request.scope,
252
+ };
253
+ })),
254
+ }));
255
+ }
256
+ function scanConfigMutationShortOptions(token, command) {
257
+ if (!/^-[^-].*/u.test(token))
258
+ return { json: false, consumesNextValue: false };
259
+ let json = false;
260
+ for (let index = 1; index < token.length; index += 1) {
261
+ const option = token[index];
262
+ if (option === 's') {
263
+ return {
264
+ json,
265
+ consumesNextValue: index === token.length - 1,
266
+ };
267
+ }
268
+ if (option === 'j') {
269
+ json = true;
270
+ continue;
271
+ }
272
+ if (option === 'v' || option === 'D' || (option === 'y' && command === 'config:reset')) {
273
+ continue;
274
+ }
275
+ break;
276
+ }
277
+ return { json, consumesNextValue: false };
278
+ }
279
+ function jsonConfigMutationRequest(argv) {
280
+ if (argv[0] !== 'config' || !['set', 'unset', 'reset'].includes(argv[1] ?? '')) {
281
+ return null;
282
+ }
283
+ const command = `config:${argv[1]}`;
284
+ let json = false;
285
+ let plugin;
286
+ const valueOptions = new Set(['--scope', '--value-json', '--preset', '--log-file']);
287
+ for (let index = 2; index < argv.length; index += 1) {
288
+ const token = argv[index];
289
+ if (token === '--')
290
+ break;
291
+ if (valueOptions.has(token)) {
292
+ index += 1;
293
+ continue;
294
+ }
295
+ if (token === '--json') {
296
+ json = true;
297
+ continue;
298
+ }
299
+ const shortOptions = scanConfigMutationShortOptions(token, command);
300
+ if (shortOptions.json)
301
+ json = true;
302
+ if (shortOptions.consumesNextValue)
303
+ index += 1;
304
+ if (token.startsWith('-'))
305
+ continue;
306
+ plugin ??= token;
307
+ }
308
+ if (!json)
309
+ return null;
310
+ return {
311
+ command,
312
+ ...(plugin ? { plugin } : {}),
313
+ };
314
+ }
315
+ function configMutationFailure(error, env, cwd) {
316
+ const configWrite = configWriteStateForError(error);
317
+ if (error instanceof CommanderError) {
318
+ return {
319
+ code: 'invalidArguments',
320
+ message: 'error: invalid config mutation arguments',
321
+ configWrite,
322
+ };
323
+ }
324
+ const message = catalogPublicationFailure({
325
+ error,
326
+ env,
327
+ sensitiveRoots: [cwd],
328
+ }).message;
329
+ if (message.includes('Usage:'))
330
+ return { code: 'invalidArguments', message, configWrite };
331
+ const generationCode = catalogGenerationErrorCode(error);
332
+ if (generationCode === 'catalogGenerationUnavailable'
333
+ || generationCode === 'catalogGenerationSourceMissing') {
334
+ return { code: 'catalogSourceUnavailable', message, configWrite };
335
+ }
336
+ if (generationCode !== 'catalogGenerationFailed')
337
+ return { code: generationCode, message, configWrite };
338
+ const explicitCode = error?.code;
339
+ if (typeof explicitCode === 'string' && /^[A-Za-z][A-Za-z0-9]*$/u.test(explicitCode)) {
340
+ return { code: explicitCode, message, configWrite };
341
+ }
342
+ if (message.includes('legacyStateUnsupported'))
343
+ return { code: 'legacyStateUnsupported', message, configWrite };
344
+ if (message.includes('sourceChanged'))
345
+ return { code: 'sourceChanged', message, configWrite };
346
+ if (message.includes('sourceMissing'))
347
+ return { code: 'sourceMissing', message, configWrite };
348
+ if (message.startsWith('Cannot resolve required catalog sources:')) {
349
+ return { code: 'catalogSourceUnavailable', message, configWrite };
350
+ }
351
+ const prefix = message.match(/^([A-Za-z][A-Za-z0-9]*):/u)?.[1];
352
+ return { code: prefix ?? 'configMutationFailed', message, configWrite };
353
+ }
354
+ function writeConfigMutationFailureReceipt(write, request, failure) {
355
+ let plugin;
356
+ try {
357
+ if (request.plugin) {
358
+ parsePluginId(request.plugin);
359
+ plugin = request.plugin;
360
+ }
61
361
  }
62
- return 0;
362
+ catch {
363
+ // Do not echo an invalid selector from raw argv in an early failure receipt.
364
+ }
365
+ writeJsonReceipt(write, buildConfigMutationReceipt(request.command, {
366
+ ok: false,
367
+ overallState: 'failed',
368
+ configWrite: failure.configWrite,
369
+ ...(plugin ? { plugin } : {}),
370
+ errors: [failure],
371
+ }));
63
372
  }
64
373
  async function runAuthoringScript(options) {
65
374
  const scriptPath = path.join(resolveCliPackageRoot(), 'dist', 'authoring', 'scripts', options.scriptName);
@@ -102,47 +411,55 @@ function optionTokens(flag, value) {
102
411
  function writeCommandUsage(command) {
103
412
  const cardinality = command === 'reinstall' || command === 'cleanup' ? '<plugin>' : '<plugin...>';
104
413
  const scope = command === 'cleanup' ? ' --scope <project|user>' : ' [--scope <project|user>]';
105
- const all = command === 'upgrade' ? ' 或 aw upgrade --all --platform both --scope user' : '';
106
414
  return [
107
415
  `${command} 命令缺少必要参数。`,
108
416
  `Usage: aw ${command} ${cardinality} --platform <codex|claude|both>${scope}`,
109
- `Example: aw ${command} example-plugin --platform codex --scope user${all}`,
417
+ `Example: aw ${command} example-plugin --platform codex --scope user`,
110
418
  ].join('\n');
111
419
  }
112
420
  function configCommandUsage(command) {
113
421
  const argumentsByCommand = {
114
422
  list: '<plugin>',
115
423
  get: '<plugin> [key]',
116
- set: '<plugin> <key> <value>',
424
+ set: '<plugin> <field> --scope <project|user> (--value-json <json> | --value-stdin | --preset <id>)',
117
425
  unset: '<plugin> <key>',
118
426
  reset: '<plugin> --scope <project|user> --yes',
119
427
  };
120
428
  return [
121
429
  `config ${command} 命令缺少必要参数。`,
122
430
  `Usage: aw config ${command} ${argumentsByCommand[command]}`,
123
- `Example: aw config ${command} example-plugin${command === 'set' ? ' key value' : command === 'unset' ? ' key' : command === 'reset' ? ' --scope user --yes' : ''}`,
431
+ `Example: aw config ${command} example-plugin${command === 'set' ? ' mode --scope user --value-json \'"safe"\'' : command === 'unset' ? ' key' : command === 'reset' ? ' --scope user --yes' : ''}`,
124
432
  ].join('\n');
125
433
  }
126
- function assertUpgradeAllConflict(pluginNames, all) {
127
- if (all && pluginNames.length > 0) {
128
- throw new Error('upgrade 命令不能同时使用插件名和 `--all`。');
434
+ async function readUtf8Stdin(stdin) {
435
+ const chunks = [];
436
+ for await (const chunk of stdin) {
437
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : Buffer.from(chunk));
129
438
  }
439
+ return Buffer.concat(chunks).toString('utf8');
130
440
  }
131
441
  function equivalentWriteCommand(command, selection, rawOptions) {
132
442
  return shellCommand(buildEquivalentWriteCommandTokens(command, selection, rawOptions));
133
443
  }
134
444
  export function buildEquivalentWriteCommandTokens(command, selection, rawOptions) {
135
- const all = command === 'upgrade' && rawOptions.all === true;
136
445
  return [
137
446
  'aw',
138
447
  command,
139
- ...(all ? ['--all'] : []),
140
448
  '--platform',
141
449
  selection.platform ?? 'both',
142
450
  '--scope',
143
451
  selection.scope ?? 'user',
144
- ...(command === 'cleanup' ? [] : optionTokens('--script-timeout', rawOptions.scriptTimeout)),
145
- ...(!all && selection.plugins.length > 0 ? ['--', ...selection.plugins] : []),
452
+ ...(rawOptions.allowManagedCodexInstall === true
453
+ ? ['--allow-managed-codex-install']
454
+ : []),
455
+ ...(command === 'cleanup' ? ['--yes'] : []),
456
+ ...(command === 'install'
457
+ ? (rawOptions.dependencyScope ?? []).flatMap((value) => ['--dependency-scope', value])
458
+ : []),
459
+ ...((command === 'install' || command === 'reinstall')
460
+ ? optionTokens('--script-timeout', rawOptions.scriptTimeout)
461
+ : []),
462
+ ...(selection.plugins.length > 0 ? ['--', ...selection.plugins] : []),
146
463
  ];
147
464
  }
148
465
  function equivalentConfigCommand(options) {
@@ -154,39 +471,14 @@ function equivalentConfigCommandProjection(resolve) {
154
471
  equivalentCommandTokens: (selection) => buildEquivalentConfigCommandTokens(resolve(selection)),
155
472
  };
156
473
  }
157
- function configSetEquivalentOptions(selection, candidates, rawOptions) {
158
- const field = candidates.find((entry) => entry.pluginName === selection.plugins[0])
159
- ?.configSchema?.[selection.key ?? ''];
160
- const placeholder = configFieldPlaceholder(field);
161
- return {
162
- command: 'set',
163
- plugin: selection.plugins[0],
164
- key: selection.key,
165
- value: placeholder
166
- ? rawOptions.valueJson ? JSON.stringify(placeholder) : placeholder
167
- : formatConfigCommandValue(selection.value, rawOptions.valueJson === true),
168
- scope: selection.scope ?? 'user',
169
- projectLayer: selection.projectLayer,
170
- valueJson: rawOptions.valueJson,
171
- };
172
- }
173
- function configFieldPlaceholder(field) {
174
- if (field?.sensitive)
175
- return '<sensitive-value>';
176
- if (field?.type === 'file')
177
- return '<file-path>';
178
- if (field?.type === 'directory')
179
- return '<directory-path>';
180
- return undefined;
181
- }
182
474
  export function buildEquivalentConfigCommandTokens(options) {
183
475
  return [
184
476
  'aw',
185
477
  'config',
186
478
  options.command,
187
479
  ...optionTokens('--scope', options.scope),
188
- ...(options.projectLayer === 'local' ? ['--local'] : []),
189
- ...(options.valueJson ? ['--value-json'] : []),
480
+ ...optionTokens('--value-json', options.valueJson),
481
+ ...optionTokens('--preset', options.preset),
190
482
  ...(options.confirmed ? ['--yes'] : []),
191
483
  '--',
192
484
  options.plugin,
@@ -209,7 +501,6 @@ async function loadWizardCandidates(context, rawOptions) {
209
501
  cwd: context.cwd,
210
502
  scope: rawOptions.scope,
211
503
  platform: rawOptions.platform,
212
- includeCatalogOnly: true,
213
504
  }, context);
214
505
  return toWizardCandidates(loaded.items);
215
506
  }
@@ -237,7 +528,6 @@ async function loadStoredConfigWizardCandidates(context, locations, options = {}
237
528
  cwd: context.cwd,
238
529
  env: context.env,
239
530
  scope: location.scope,
240
- ...(location.scope === 'project' ? { projectLayer: location.projectLayer } : {}),
241
531
  });
242
532
  for (const store of stores) {
243
533
  const keys = Object.keys(store.values);
@@ -245,12 +535,11 @@ async function loadStoredConfigWizardCandidates(context, locations, options = {}
245
535
  continue;
246
536
  const existing = storesByPlugin.get(store.pluginId) ?? {
247
537
  sourceId: store.sourceId,
248
- sourceIdentity: store.sourceIdentity,
538
+ ...(store.binding.kind === 'source'
539
+ ? { sourceIdentity: store.binding.sourceIdentity }
540
+ : {}),
249
541
  keys: new Set(),
250
542
  };
251
- if (existing.sourceIdentity !== store.sourceIdentity) {
252
- throw new Error(`sourceChanged: ${store.pluginId} config stores have different source identities.`);
253
- }
254
543
  for (const key of keys)
255
544
  existing.keys.add(key);
256
545
  storesByPlugin.set(store.pluginId, existing);
@@ -262,26 +551,53 @@ async function loadStoredConfigWizardCandidates(context, locations, options = {}
262
551
  pluginName: pluginId,
263
552
  pluginId,
264
553
  sourceId: store.sourceId,
265
- sourceIdentity: store.sourceIdentity,
554
+ ...(store.sourceIdentity ? { sourceIdentity: store.sourceIdentity } : {}),
266
555
  targets: [],
267
556
  configKeys: [...store.keys].sort((left, right) => left.localeCompare(right)),
268
557
  }));
269
558
  }
270
559
  function configReadStoreLocations(rawOptions) {
271
- if (rawOptions.local)
272
- return [{ scope: 'project', projectLayer: 'local' }];
273
560
  if (rawOptions.scope === 'user')
274
- return [{ scope: 'user', projectLayer: 'shared' }];
561
+ return [{ scope: 'user' }];
275
562
  if (rawOptions.scope === 'project')
276
- return [{ scope: 'project', projectLayer: 'shared' }];
563
+ return [{ scope: 'project' }];
277
564
  return [
278
- { scope: 'user', projectLayer: 'shared' },
279
- { scope: 'project', projectLayer: 'shared' },
280
- { scope: 'project', projectLayer: 'local' },
565
+ { scope: 'user' },
566
+ { scope: 'project' },
281
567
  ];
282
568
  }
283
569
  function overallResultState(value) {
284
- return value.overallState;
570
+ return value.overallState === 'succeeded' ? 'succeeded' : 'failed';
571
+ }
572
+ async function resolveStoredConfigResetIdentity(options) {
573
+ const parsed = parsePluginId(options.selector);
574
+ if (parsed.sourceId) {
575
+ return {
576
+ pluginName: parsed.pluginName,
577
+ pluginId: options.selector,
578
+ sourceId: parsed.sourceId,
579
+ };
580
+ }
581
+ const matches = (await listNamespacedPluginConfigStores({
582
+ cwd: options.context.cwd,
583
+ env: options.context.env,
584
+ scope: options.scope,
585
+ })).filter((store) => store.pluginName === parsed.pluginName);
586
+ if (matches.length === 0) {
587
+ throw new Error(`configStoreMissing: ${options.selector} has no config in the selected store.`);
588
+ }
589
+ if (matches.length > 1) {
590
+ throw new Error(`configTargetAmbiguous: ${options.selector} exists in multiple source namespaces: ${matches.map((store) => store.pluginId).join(', ')}. Use a full pluginId.`);
591
+ }
592
+ const [match] = matches;
593
+ return {
594
+ pluginName: match.pluginName,
595
+ pluginId: match.pluginId,
596
+ sourceId: match.sourceId,
597
+ ...(match.binding.kind === 'source'
598
+ ? { sourceIdentity: match.binding.sourceIdentity }
599
+ : { logicalIdentity: match.binding.logicalIdentity }),
600
+ };
285
601
  }
286
602
  async function resolveStoredOnlyConfigIdentity(options) {
287
603
  const parsed = parsePluginId(options.selector);
@@ -291,33 +607,43 @@ async function resolveStoredOnlyConfigIdentity(options) {
291
607
  cwd: options.context.cwd,
292
608
  env: options.context.env,
293
609
  scope: location.scope,
294
- ...(location.scope === 'project' ? { projectLayer: location.projectLayer } : {}),
295
610
  })))).flat().filter((store) => store.pluginId === options.selector);
296
611
  if (stores.length === 0)
297
612
  throw new Error(`Plugin "${options.selector}" does not have a namespaced config store.`);
298
- const identities = new Set(stores.map((store) => store.sourceIdentity));
299
- if (identities.size !== 1)
300
- throw new Error(`sourceChanged: ${options.selector} config stores have different source identities.`);
301
- const sourceIdentity = [...identities][0];
302
- await withResolvedMultiSourceCatalog({
613
+ const sourceBindings = stores.flatMap((store) => store.binding.kind === 'source'
614
+ ? [store.binding.sourceIdentity] : []);
615
+ const catalogBindings = stores.flatMap((store) => store.binding.kind === 'catalog'
616
+ ? [store.binding.logicalIdentity] : []);
617
+ if (new Set(sourceBindings).size > 1 || new Set(catalogBindings).size > 1) {
618
+ throw new Error(`identityMismatch: ${options.selector} config stores have different bindings.`);
619
+ }
620
+ const currentSource = await withResolvedMultiSourceCatalog({
303
621
  cliVersion: resolveCliVersion(),
304
- freshnessPolicy: 'cacheFirst',
305
622
  sourceIds: [parsed.sourceId],
306
623
  env: options.context.env,
307
624
  consume: async (catalog) => {
308
625
  const source = catalog.sources.find((entry) => entry.sourceId === parsed.sourceId);
309
- if (!source?.resolution)
626
+ if (!source?.resolution) {
310
627
  throw source?.error ?? new Error(`Catalog source "${parsed.sourceId}" is unavailable.`);
311
- if (source.resolution.sourceIdentity !== sourceIdentity) {
312
- throw new Error(`sourceChanged: ${options.selector} config source identity does not match the current source.`);
313
628
  }
629
+ return {
630
+ sourceIdentity: source.resolution.sourceIdentity,
631
+ logicalIdentity: source.resolution.logicalIdentity,
632
+ };
314
633
  },
315
634
  });
635
+ if (sourceBindings[0] && sourceBindings[0] !== currentSource.sourceIdentity) {
636
+ throw new Error(`identityMismatch: ${options.selector} user config does not match the current source identity.`);
637
+ }
638
+ if (catalogBindings[0] && catalogBindings[0] !== currentSource.logicalIdentity) {
639
+ throw new Error(`identityMismatch: ${options.selector} project config does not match the current Catalog logical identity.`);
640
+ }
316
641
  return {
317
642
  pluginName: parsed.pluginName,
318
643
  pluginId: options.selector,
319
644
  sourceId: parsed.sourceId,
320
- sourceIdentity,
645
+ sourceIdentity: currentSource.sourceIdentity,
646
+ logicalIdentity: currentSource.logicalIdentity,
321
647
  };
322
648
  }
323
649
  async function resolveMultiSourceConfigRead(options) {
@@ -326,26 +652,21 @@ async function resolveMultiSourceConfigRead(options) {
326
652
  env: options.context.env,
327
653
  });
328
654
  assertCatalogSourceReferencesSupported(inventory);
329
- const projectLayer = resolveConfigProjectLayer(options.rawOptions);
330
655
  return withResolvedCatalogPlugin({
331
656
  selector: options.selector,
332
657
  cliVersion: resolveCliVersion(),
333
- freshnessPolicy: 'cacheFirst',
334
658
  env: options.context.env,
335
659
  consume: async ({ pluginId, source, plugin }) => {
336
660
  const result = await resolveConfigRead({
337
661
  pluginName: plugin.manifest.name,
338
662
  plugin,
339
- scope: options.rawOptions.local
340
- ? 'project'
341
- : resolveConfigReadResolution(options.rawOptions.scope),
342
- projectLayer,
663
+ scope: resolveConfigReadResolution(options.rawOptions.scope),
343
664
  cwd: options.context.cwd,
344
665
  env: options.context.env,
345
666
  });
346
667
  if (options.key !== undefined) {
347
668
  const field = findConfigFieldEntry(result.fields, options.key);
348
- if (!field && plugin.manifest.config?.schema?.[options.key] === undefined) {
669
+ if (!field && plugin.manifest.config?.fields?.[options.key] === undefined) {
349
670
  throw new Error(`Unknown config key "${options.key}".`);
350
671
  }
351
672
  if (field)
@@ -371,12 +692,10 @@ async function resolveMultiSourceConfigRead(options) {
371
692
  const result = await resolveConfigRead({
372
693
  sourceId: stored.sourceId,
373
694
  sourceIdentity: stored.sourceIdentity,
695
+ logicalIdentity: stored.logicalIdentity,
374
696
  pluginName: stored.pluginName,
375
697
  plugin: null,
376
- scope: options.rawOptions.local
377
- ? 'project'
378
- : resolveConfigReadResolution(options.rawOptions.scope),
379
- projectLayer,
698
+ scope: resolveConfigReadResolution(options.rawOptions.scope),
380
699
  cwd: options.context.cwd,
381
700
  env: options.context.env,
382
701
  });
@@ -389,36 +708,53 @@ async function resolveMultiSourceConfigRead(options) {
389
708
  return { ...stored, result };
390
709
  });
391
710
  }
392
- async function executeWriteCommand(command, pluginNames, rawOptions, context) {
711
+ export function createManagedCodexAuthorizer(rawOptions, context) {
712
+ if (rawOptions.allowManagedCodexInstall === true)
713
+ return async () => true;
714
+ if (!(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true)
715
+ || !context.prompt.confirm)
716
+ return undefined;
717
+ return async () => {
718
+ const response = await context.prompt.confirm({
719
+ message: '系统中没有可用的 Codex CLI。是否获取本次操作所需的 AW-managed Codex runtime?',
720
+ defaultValue: false,
721
+ });
722
+ return !response.cancelled && response.value === true;
723
+ };
724
+ }
725
+ async function executeWriteCommand(command, pluginNames, rawOptions, context, directCli = false, expectedPlanFingerprint) {
393
726
  const raw = { cwd: context.cwd, ...rawOptions };
394
- const commandOptions = command === 'install'
727
+ const commandOptions = normalizeWriteRequest(() => command === 'install'
395
728
  ? normalizeInstallCommandOptions(raw)
396
- : command === 'upgrade'
397
- ? normalizeUpgradeCommandOptions(raw)
398
- : command === 'uninstall'
399
- ? normalizeUninstallCommandOptions(raw)
400
- : command === 'cleanup'
401
- ? normalizeCleanupCommandOptions(raw)
402
- : normalizeReinstallCommandOptions(raw);
729
+ : command === 'uninstall'
730
+ ? normalizeUninstallCommandOptions(raw)
731
+ : command === 'cleanup'
732
+ ? normalizeCleanupCommandOptions(raw)
733
+ : normalizeReinstallCommandOptions(raw));
403
734
  await emitCommandEvent(context, {
404
735
  type: 'options:normalized',
405
736
  data: { plugins: pluginNames, options: commandOptions },
406
737
  });
738
+ const managedCodexAuthorizer = createManagedCodexAuthorizer(rawOptions, context);
739
+ const runtime = {
740
+ ...context,
741
+ directCli,
742
+ preApplyConfirmed: rawOptions.yes === true,
743
+ ...(expectedPlanFingerprint ? { expectedPlanFingerprint } : {}),
744
+ ...(managedCodexAuthorizer ? { managedCodexAuthorizer } : {}),
745
+ };
407
746
  switch (command) {
408
- case 'install': return installCommand(pluginNames, commandOptions, context);
409
- case 'upgrade': return upgradeCommand(pluginNames, commandOptions, context);
410
- case 'uninstall': return uninstallCommand(pluginNames, commandOptions, context);
411
- case 'cleanup': return cleanupCommand(pluginNames, commandOptions, context);
412
- case 'reinstall': return reinstallCommand(pluginNames, commandOptions, context);
747
+ case 'install': return installCommand(pluginNames, commandOptions, runtime);
748
+ case 'uninstall': return uninstallCommand(pluginNames, commandOptions, runtime);
749
+ case 'cleanup': return cleanupCommand(pluginNames, commandOptions, runtime);
750
+ case 'reinstall': return reinstallCommand(pluginNames, commandOptions, runtime);
413
751
  }
414
752
  }
415
753
  async function runWriteCommandAction(command, pluginNames, rawOptions, context) {
416
754
  if ((command === 'cleanup' || command === 'reinstall') && pluginNames.length > 1) {
417
- throw new Error(`${command} 命令当前只支持单个插件目标。`);
755
+ throw new WriteCommandRequestError(`${command} 命令当前只支持单个插件目标。`);
418
756
  }
419
- if (command === 'upgrade')
420
- assertUpgradeAllConflict(pluginNames, rawOptions.all === true);
421
- const missingPlugin = pluginNames.length === 0 && !(command === 'upgrade' && rawOptions.all === true);
757
+ const missingPlugin = pluginNames.length === 0;
422
758
  const missingPlatform = rawOptions.platform === undefined;
423
759
  const missingScope = command === 'cleanup' && rawOptions.scope === undefined;
424
760
  const missing = missingPlugin || missingPlatform || missingScope;
@@ -431,19 +767,42 @@ async function runWriteCommandAction(command, pluginNames, rawOptions, context)
431
767
  };
432
768
  void validationOptions;
433
769
  if (!missing) {
770
+ if (command === 'cleanup' && rawOptions.force !== true && rawOptions.yes !== true
771
+ && isWizardAvailable(context, rawOptions) && context.prompt.confirm) {
772
+ const preview = await cleanupCommand(pluginNames, normalizeCleanupCommandOptions({
773
+ cwd: context.cwd,
774
+ ...rawOptions,
775
+ }), { ...context, previewOperationPlan: true });
776
+ if ((preview.removedPluginCleanupIds?.length ?? 0) > 0) {
777
+ if (!preview.operationPlan) {
778
+ throw new WriteCommandRequestError('cleanupPlanUnavailable: 无法建立可信的整体清理预览。');
779
+ }
780
+ const response = await context.prompt.confirm({
781
+ message: `清理已删除插件 ${preview.removedPluginCleanupIds.join('、')}:将处理 Codex、Claude Code 的用户与当前项目位置,删除保存设置、安装意图、专属输出和共享原生安装。其他项目可能因此无法继续使用该插件;其本地记录不会修改。即使指定了 --platform 或 --scope,本次仍覆盖上述全部位置。继续?`,
782
+ defaultValue: false,
783
+ });
784
+ if (response.cancelled || response.value !== true)
785
+ return { cancelled: true };
786
+ return {
787
+ cancelled: false,
788
+ result: await executeWriteCommand(command, pluginNames, {
789
+ ...rawOptions, yes: true,
790
+ }, context, true, preview.operationPlan.planFingerprint),
791
+ };
792
+ }
793
+ }
434
794
  return {
435
795
  cancelled: false,
436
- result: await executeWriteCommand(command, pluginNames, rawOptions, context),
796
+ result: await executeWriteCommand(command, pluginNames, rawOptions, context, true),
437
797
  };
438
798
  }
439
799
  if (!isWizardAvailable(context, rawOptions)) {
440
- throw new Error(writeCommandUsage(command));
800
+ throw new WriteCommandRequestError(writeCommandUsage(command));
441
801
  }
442
802
  const loaded = await loadConsoleItemsResult({
443
803
  cwd: context.cwd,
444
804
  scope: rawOptions.scope,
445
805
  platform: rawOptions.platform,
446
- includeCatalogOnly: true,
447
806
  }, context);
448
807
  if (loaded.catalogUnavailable) {
449
808
  throw new Error('插件目录不可用。请运行 `aw init` 或 `aw init --force`,或使用完整参数重试。');
@@ -474,11 +833,37 @@ async function runWriteCommandAction(command, pluginNames, rawOptions, context)
474
833
  equivalentCommandTokens: (selection) => buildEquivalentWriteCommandTokens(command, selection, rawOptions),
475
834
  isPluginSelectable: (candidate, selection) => writeCandidateEligibility(command, candidate, selection),
476
835
  resultState: overallResultState,
477
- execute: (selection) => executeWriteCommand(command, selection.plugins, {
836
+ ...(command === 'cleanup' ? {
837
+ reviewDetails: async (selection) => {
838
+ const preview = await cleanupCommand(selection.plugins, normalizeCleanupCommandOptions({
839
+ cwd: context.cwd,
840
+ ...rawOptions,
841
+ platform: selection.platform,
842
+ scope: selection.scope,
843
+ }), { ...context, previewOperationPlan: true });
844
+ if ((preview.removedPluginCleanupIds?.length ?? 0) === 0)
845
+ return { lines: [] };
846
+ if (!preview.operationPlan) {
847
+ throw new WriteCommandRequestError('cleanupPlanUnavailable: 无法建立可信的整体清理预览。');
848
+ }
849
+ return {
850
+ lines: [
851
+ `已删除插件:${preview.removedPluginCleanupIds.join('、')}`,
852
+ '实际清理范围:Codex、Claude Code;用户与当前项目。',
853
+ '将删除保存设置、安装意图、专属输出和共享原生安装。',
854
+ '其他项目可能失去此插件;其本地记录不会修改。',
855
+ '当前 --platform / --scope 选择不会限制整体清理范围。',
856
+ ],
857
+ expectedPlanFingerprint: preview.operationPlan.planFingerprint,
858
+ };
859
+ },
860
+ } : {}),
861
+ execute: (selection, expectedPlanFingerprint) => executeWriteCommand(command, selection.plugins, {
478
862
  ...rawOptions,
479
863
  platform: selection.platform,
480
864
  scope: selection.scope,
481
- }, context),
865
+ ...(command === 'cleanup' ? { yes: true } : {}),
866
+ }, context, false, expectedPlanFingerprint),
482
867
  });
483
868
  if (result.cancelled)
484
869
  return { cancelled: true };
@@ -589,34 +974,78 @@ async function runActionWithContext(context, action) {
589
974
  function writeJsonReceipt(write, payload) {
590
975
  writeLine(write, JSON.stringify(payload, null, 2));
591
976
  }
592
- function configLayerLabel(scope, projectLayer = 'shared') {
977
+ function configLayerLabel(scope) {
593
978
  if (scope === 'user') {
594
979
  return '个人';
595
980
  }
596
- return projectLayer === 'local' ? '当前项目(本机私有覆盖)' : '当前项目';
981
+ return '当前项目';
597
982
  }
598
983
  function writeConfigApplyNotice(write, receipt) {
599
- if (receipt.targetResults.length === 0) {
600
- const message = receipt.applySkippedReason === 'noConfigChange'
984
+ const sanitizeMessage = (message) => redactString(message)
985
+ .replace(/https?:\/\/\S+/giu, '[REDACTED URL]')
986
+ .replace(/file:\/\/\S+/giu, '[REDACTED PATH]')
987
+ .replace(/(?:file:\/\/)?(?:\/[\w.@+-]+){2,}/gu, '[REDACTED PATH]')
988
+ .replace(/[A-Za-z]:\\(?:[^\s\\]+\\)+[^\s\\]*/gu, '[REDACTED PATH]');
989
+ const writeTargetFailures = () => {
990
+ for (const entry of receipt.apply.targetResults.filter((target) => target.state === 'failed' || target.state === 'skipped')) {
991
+ const reason = entry.message
992
+ ? `${entry.errorCode ? `[${entry.errorCode}] ` : ''}${sanitizeMessage(entry.message)}`
993
+ : entry.errorCode
994
+ ? `失败原因:${entry.errorCode}。`
995
+ : '未提供安全失败原因。';
996
+ const command = `aw reinstall ${receipt.plugin} -p ${entry.target.platform} -s ${entry.target.scope}`;
997
+ const recovery = entry.errorCode === 'invalidConfig'
998
+ ? ' 请修复无效配置后运行 aw sync。'
999
+ : entry.errorCode === 'preApplyAuthorizationRequired'
1000
+ || entry.errorCode === 'sharedNativeConfigApplyConfirmationRequired'
1001
+ || entry.errorCode === 'sharedNativeImpactAuthorizationRequired'
1002
+ ? ` 请运行 ${command} --yes 重试。`
1003
+ : entry.state === 'failed'
1004
+ ? ` 请在解决失败原因后运行 ${command}。`
1005
+ : '';
1006
+ writeLine(write, `${entry.target.platform}/${entry.target.scope}:${reason}${recovery}`);
1007
+ }
1008
+ };
1009
+ const targetResults = receipt.apply.targetResults;
1010
+ if (receipt.apply.state === 'skipped') {
1011
+ const message = receipt.apply.reason === 'noConfigChange'
601
1012
  ? '配置内容没有变化,因此没有运行 reinstall。'
602
- : '配置已保存。当前没有可见的 AW 安装目标,因此没有应用配置。';
1013
+ : receipt.apply.reason === 'noEffectiveConfigChange'
1014
+ ? '配置存储已更新,但有效配置没有变化,因此没有运行 reinstall。'
1015
+ : receipt.apply.reason === 'notRequested'
1016
+ ? '配置已保存,但未请求应用配置。'
1017
+ : '配置已保存。当前没有可见的 AW 安装目标,因此没有应用配置。';
603
1018
  writeLine(write, message);
604
1019
  }
605
- else {
606
- const completed = receipt.targetResults.filter((entry) => entry.state === 'completed').length;
607
- const failed = receipt.targetResults.filter((entry) => entry.state === 'failed');
608
- writeLine(write, `配置已保存。已重装 ${completed} 个当前可见目标;失败 ${failed.length} 个。`);
609
- for (const entry of failed) {
610
- writeLine(write, `${entry.target.platform}/${entry.target.scope}:${entry.message ?? 'reinstall 失败。'} `
611
- + `请重试 aw reinstall ${receipt.plugin} -p ${entry.target.platform} -s ${entry.target.scope}。`);
612
- }
1020
+ else if (receipt.apply.state === 'unavailable') {
1021
+ const fallbackMessages = {
1022
+ invalidConfig: '可见目标的有效配置无效。',
1023
+ schemaUnavailable: '配置 schema 不可用。',
1024
+ sourceUnavailable: '插件来源不可用。',
1025
+ effectiveConfigUnavailable: '无法读取有效配置。',
1026
+ targetDiscoveryFailed: '无法发现可应用配置的目标。',
1027
+ };
1028
+ const detail = receipt.apply.error
1029
+ ? `${receipt.apply.error.code}:${sanitizeMessage(receipt.apply.error.message)}`
1030
+ : `${receipt.apply.reason}:${fallbackMessages[receipt.apply.reason]}`;
1031
+ const recovery = receipt.apply.reason === 'invalidConfig'
1032
+ ? '请修复无效配置后运行 aw sync。'
1033
+ : receipt.apply.reason === 'effectiveConfigUnavailable'
1034
+ ? '请修复配置后运行 aw sync。'
1035
+ : receipt.apply.reason === 'schemaUnavailable' || receipt.apply.reason === 'sourceUnavailable'
1036
+ ? '请修复插件来源或配置后运行 aw sync。'
1037
+ : '请修复来源或配置后运行 aw sync。';
1038
+ writeLine(write, `配置已保存,但当前无法应用配置。${detail} ${recovery}`);
1039
+ writeTargetFailures();
613
1040
  }
614
- if (receipt.otherProjectsRequireReinstall) {
615
- writeLine(write, '其它项目没有被扫描。请进入每个项目目录并运行对应的 aw reinstall 命令。');
1041
+ else {
1042
+ const completed = targetResults.filter((entry) => entry.state === 'completed').length;
1043
+ const failed = targetResults.filter((entry) => entry.state === 'failed').length;
1044
+ const skipped = targetResults.filter((entry) => entry.state === 'skipped').length;
1045
+ writeLine(write, `配置已保存。已重装 ${completed} 个当前可见目标;失败 ${failed} 个;跳过 ${skipped} 个。`);
1046
+ writeTargetFailures();
616
1047
  }
617
- }
618
- function writeProjectLocalConfigNotice(write) {
619
- writeLine(write, '当前显示的是当前项目的本机私有覆盖层,不作为 repo-visible artifact 渲染输入。');
1048
+ writeWarnings(write, receipt.warnings);
620
1049
  }
621
1050
  function renderConfigFieldState(field) {
622
1051
  if (field.valueState === 'invalid') {
@@ -636,13 +1065,15 @@ function writeConfigReadHuman(write, resolved, pluginName) {
636
1065
  }
637
1066
  if (field.kind === 'manifest' && field.valueState === 'invalid') {
638
1067
  const sourceScope = field.sourceLayer === 'project'
639
- ? ' -s project'
640
- : field.sourceLayer === 'local'
641
- ? ' -s project --local'
642
- : ' -s user';
643
- writeLine(write, ` aw config set ${pluginName} ${field.key} <value>${sourceScope}`);
644
- writeLine(write, ` aw config unset ${pluginName} ${field.key}${sourceScope}`);
645
- writeLine(write, ` aw config reset ${pluginName}${sourceScope}`);
1068
+ ? 'project'
1069
+ : 'user';
1070
+ const definition = resolved.fieldDefinitions?.[field.key];
1071
+ if (definition) {
1072
+ writeLine(write, ` ${formatPluginConfigSetRecoveryCommand(pluginName, field.key, definition, sourceScope)}`);
1073
+ }
1074
+ const scopeOption = ` -s ${sourceScope}`;
1075
+ writeLine(write, ` aw config unset ${pluginName} ${field.key}${scopeOption}`);
1076
+ writeLine(write, ` aw config reset ${pluginName}${scopeOption}`);
646
1077
  }
647
1078
  }
648
1079
  }
@@ -676,10 +1107,33 @@ export function buildProgram(options = {}) {
676
1107
  const plugin = program
677
1108
  .command('plugin')
678
1109
  .description('Create and validate AW plugin source');
1110
+ const self = program
1111
+ .command('self')
1112
+ .description('Describe and check this AW executable');
1113
+ self
1114
+ .command('describe')
1115
+ .description('Describe implemented AW capabilities')
1116
+ .requiredOption('--json', 'emit one machine-readable JSON object')
1117
+ .action(() => {
1118
+ writeJsonReceipt(io.stdout, describeCurrentAw(resolveCliVersion()));
1119
+ });
1120
+ self
1121
+ .command('check')
1122
+ .description('Check nearby plugin requirements against this AW executable')
1123
+ .option('--from <path>', 'start discovery from this file or directory')
1124
+ .requiredOption('--json', 'emit one machine-readable JSON object')
1125
+ .action(async (rawOptions) => {
1126
+ const result = await checkCurrentAw({
1127
+ awVersion: resolveCliVersion(),
1128
+ from: path.resolve(cwd, rawOptions.from ?? '.'),
1129
+ });
1130
+ writeJsonReceipt(io.stdout, result);
1131
+ commandExitState.exitCode = result.ok ? 0 : 1;
1132
+ });
679
1133
  plugin
680
1134
  .command('capabilities')
681
- .description('Show the current plugin authoring capability contract')
682
- .option('--surface <surface>', 'manifest | build-config | extensions | slash-commands | mcp-apps | config | lifecycle | artifacts')
1135
+ .description('Show the current plugin manifest authoring surface')
1136
+ .option('--surface <surface>', 'manifest | build-config | extensions | slash-commands | mcp-apps | config | pre-apply | artifacts')
683
1137
  .option('--json', 'emit one machine-readable JSON object', false)
684
1138
  .action((rawOptions) => {
685
1139
  try {
@@ -701,160 +1155,207 @@ export function buildProgram(options = {}) {
701
1155
  commandExitState.exitCode = 1;
702
1156
  }
703
1157
  });
704
- const skill = program
705
- .command('skill')
706
- .description('Install, materialize, or validate Agent Skills');
707
- const review = program
708
- .command('review')
709
- .description('Publish and inspect immutable review records');
710
- const reviewRecordDir = (repoRoot, record) => path.relative(fs.realpathSync(repoRoot), record.root).split(path.sep).join('/');
711
- const reviewJsonRecord = (repoRoot, record) => ({
712
- recordDir: reviewRecordDir(repoRoot, record),
713
- manifest: record.manifest,
714
- });
715
- review
716
- .command('digest')
717
- .description('Compute one canonical workflow component digest')
718
- .argument('<path>', 'repository file or directory')
719
- .option('--json', 'emit one component digest JSON object', false)
720
- .action(async (componentPath, rawOptions) => {
721
- const repoRoot = await resolveRepoRoot(cwd);
722
- const result = await computeReviewComponentDigest({ repoRoot, path: componentPath });
723
- if (rawOptions.json) {
1158
+ plugin
1159
+ .command('requirements')
1160
+ .description('Inspect or update effective AW capabilities required by a plugin')
1161
+ .argument('<plugin-directory>', 'plugin source directory')
1162
+ .option('--workspace-root <root>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1163
+ .option('--add <capability>', 'add one declared AW capability')
1164
+ .option('--remove <capability>', 'remove one declared AW capability')
1165
+ .requiredOption('--json', 'emit one machine-readable JSON object')
1166
+ .action(async (pluginDirectory, rawOptions) => {
1167
+ try {
1168
+ const result = await inspectPluginRequirements({
1169
+ pluginDirectory,
1170
+ ...(rawOptions.workspaceRoot ? { workspaceRoot: rawOptions.workspaceRoot } : {}),
1171
+ ...(rawOptions.add ? { add: rawOptions.add } : {}),
1172
+ ...(rawOptions.remove ? { remove: rawOptions.remove } : {}),
1173
+ cwd,
1174
+ });
1175
+ writeJsonReceipt(io.stdout, result);
1176
+ if (result.observations.some((observation) => observation.blocking)) {
1177
+ commandExitState.exitCode = 1;
1178
+ }
1179
+ }
1180
+ catch (error) {
724
1181
  writeJsonReceipt(io.stdout, {
725
1182
  schemaVersion: 1,
726
- command: 'review:digest',
727
- ok: true,
728
- ...result,
1183
+ command: 'plugin:requirements',
1184
+ ok: false,
1185
+ error: error instanceof Error ? error.message : String(error),
729
1186
  });
730
- return;
1187
+ commandExitState.exitCode = 1;
731
1188
  }
732
- writeLine(io.stdout, `${result.path}: ${result.digest}`);
733
1189
  });
734
- review
735
- .command('publish')
736
- .description('Publish one immutable review record')
737
- .requiredOption('--kind <skill|workflow|aw-plugin|aw-workflow>', 'review target kind')
738
- .requiredOption('--target <path-or-id>', 'review target path or ID')
739
- .requiredOption('--report <report.md>', 'review report Markdown file')
740
- .option('--scope <scope.json>', 'workflow scope JSON file')
741
- .option('--result <review-result.json>', 'review result JSON file')
742
- .option('--check <kind=path>', 'archive one JSON check attachment', (value, previous) => [
743
- ...previous,
744
- parseReviewAttachment(value, 'Check'),
745
- ], [])
746
- .option('--evidence <kind=path>', 'archive one JSON evidence attachment', (value, previous) => [
747
- ...previous,
748
- parseReviewAttachment(value, 'Evidence'),
749
- ], [])
750
- .option('--release-check <release-check.json>', 'release check JSON file')
751
- .option('--supersedes <review-id>', 'review ID that this record supersedes')
752
- .option('--json', 'emit one review record JSON object', false)
753
- .action(async (rawOptions) => {
754
- const repoRoot = await resolveRepoRoot(cwd);
755
- const record = await publishReviewRecord({
756
- repoRoot,
757
- kind: rawOptions.kind,
758
- target: rawOptions.target,
759
- report: rawOptions.report,
760
- ...(rawOptions.scope === undefined ? {} : { scope: rawOptions.scope }),
761
- ...(rawOptions.result === undefined ? {} : { result: rawOptions.result }),
762
- ...(rawOptions.check.length === 0 ? {} : { checks: rawOptions.check }),
763
- ...(rawOptions.evidence.length === 0 ? {} : { evidence: rawOptions.evidence }),
764
- ...(rawOptions.releaseCheck === undefined ? {} : { releaseCheck: rawOptions.releaseCheck }),
765
- ...(rawOptions.supersedes === undefined ? {} : { supersedes: rawOptions.supersedes }),
766
- });
767
- const recordDir = reviewRecordDir(repoRoot, record);
768
- if (rawOptions.json) {
1190
+ const skill = program
1191
+ .command('skill')
1192
+ .description('Install, materialize, or validate Agent Skills');
1193
+ const skillEval = skill
1194
+ .command('eval')
1195
+ .description('Prepare or diagnose deterministic Agent Skill Eval inputs');
1196
+ skillEval
1197
+ .command('doctor')
1198
+ .description('Inspect Agent Skill Eval provider readiness without model execution')
1199
+ .requiredOption('--json', 'emit one provider doctor JSON object')
1200
+ .action(async () => {
1201
+ try {
1202
+ writeJsonReceipt(io.stdout, await doctorEvalProviders({ sourceEnv: env }));
1203
+ }
1204
+ catch (error) {
769
1205
  writeJsonReceipt(io.stdout, {
770
- schemaVersion: 1,
771
- command: 'review:publish',
772
- ok: true,
773
- ...reviewJsonRecord(repoRoot, record),
774
- existing: record.existing,
1206
+ ok: false,
1207
+ error: redactString(error instanceof Error ? error.message : String(error)),
775
1208
  });
776
- return;
1209
+ commandExitState.exitCode = 1;
777
1210
  }
778
- writeLine(io.stdout, `${record.existing ? 'Existing' : 'Published'} review ${record.manifest.reviewId}: ${recordDir}`);
779
1211
  });
780
- review
781
- .command('verify')
782
- .description('Verify one archived review record')
783
- .argument('<report-dir>', 'review record directory')
784
- .option('--json', 'emit one review record JSON object', false)
785
- .action(async (reportDir, rawOptions) => {
786
- const repoRoot = await resolveRepoRoot(cwd);
787
- const record = await verifyReviewRecord({ repoRoot, reportDir });
788
- const relativeRecordDir = reviewRecordDir(repoRoot, record);
789
- if (rawOptions.json) {
1212
+ skillEval
1213
+ .command('prepare')
1214
+ .description('Prepare one complete Agent Skill Eval input directory')
1215
+ .argument('<plugin>', 'canonical Workspace plugin name')
1216
+ .argument('<skill>', 'owner Skill name')
1217
+ .option('--suite <suite>', 'Eval suite ID')
1218
+ .option('--profile <profile>', 'Eval profile ID')
1219
+ .option('--config-file <path>', 'non-sensitive plugin config overrides for the Eval profile')
1220
+ .option('--workspace-root <root>', 'Catalog Workspace root')
1221
+ .option('--split <split>', 'case split filter')
1222
+ .option('--case <id>', 'case ID filter')
1223
+ .option('--runs <count>', 'runs per selected case')
1224
+ .option('--timeout-ms <milliseconds>', 'timeout for each provider attempt')
1225
+ .option('--baseline <mode>', 'append without-skill comparison')
1226
+ .option('--previous-version <runtime-skill-root>', 'append a previous-version comparison')
1227
+ .option('--keep-workspace', 'retain the preparation materialization workspace', false)
1228
+ .requiredOption('--output <spec-directory>', 'new EvalSpec directory')
1229
+ .requiredOption('--json', 'emit one preparation receipt JSON object')
1230
+ .action(async (pluginName, skillName, rawOptions) => {
1231
+ try {
1232
+ const workspace = await resolveCatalogWorkspaceRoot({
1233
+ cwd,
1234
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1235
+ mode: 'existing',
1236
+ env,
1237
+ });
1238
+ const runs = rawOptions.runs === undefined
1239
+ ? undefined
1240
+ : (/^[1-9][0-9]*$/u.test(rawOptions.runs) ? Number(rawOptions.runs) : Number.NaN);
1241
+ const timeoutMs = rawOptions.timeoutMs === undefined
1242
+ ? undefined
1243
+ : (/^[1-9][0-9]*$/u.test(rawOptions.timeoutMs) ? Number(rawOptions.timeoutMs) : Number.NaN);
1244
+ const receipt = await prepareSkillEval({
1245
+ workspaceRoot: workspace.workspaceRoot,
1246
+ workspaceRootResolvedFrom: workspace.workspaceRootResolvedFrom,
1247
+ pluginName,
1248
+ skillName,
1249
+ ...(rawOptions.suite === undefined ? {} : { suiteId: rawOptions.suite }),
1250
+ ...(rawOptions.profile === undefined ? {} : { profileId: rawOptions.profile }),
1251
+ ...(rawOptions.configFile === undefined ? {} : { configFile: path.resolve(cwd, rawOptions.configFile) }),
1252
+ output: rawOptions.output,
1253
+ cwd,
1254
+ env,
1255
+ ...(rawOptions.split === undefined ? {} : { split: rawOptions.split }),
1256
+ ...(rawOptions.case === undefined ? {} : { caseId: rawOptions.case }),
1257
+ ...(runs === undefined ? {} : { runs }),
1258
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
1259
+ ...(rawOptions.baseline === undefined ? {} : {
1260
+ baseline: rawOptions.baseline,
1261
+ }),
1262
+ ...(rawOptions.previousVersion === undefined ? {} : {
1263
+ previousVersion: rawOptions.previousVersion,
1264
+ }),
1265
+ ...(rawOptions.keepWorkspace === true ? { keepWorkspace: true } : {}),
1266
+ });
1267
+ writeJsonReceipt(io.stdout, receipt);
1268
+ }
1269
+ catch (error) {
790
1270
  writeJsonReceipt(io.stdout, {
791
1271
  schemaVersion: 1,
792
- command: 'review:verify',
793
- ok: true,
794
- ...reviewJsonRecord(repoRoot, record),
1272
+ command: 'skill:eval:prepare',
1273
+ ok: false,
1274
+ errors: [{
1275
+ code: 'evalPreparationFailed',
1276
+ message: redactString(error instanceof Error ? error.message : String(error)),
1277
+ }],
795
1278
  });
796
- return;
1279
+ commandExitState.exitCode = 1;
797
1280
  }
798
- writeLine(io.stdout, `Verified review ${record.manifest.reviewId}: ${relativeRecordDir}`);
799
1281
  });
1282
+ const review = program
1283
+ .command('review')
1284
+ .description('Inspect and prepare Skill review materializations');
800
1285
  review
801
- .command('list')
802
- .description('List immutable review records')
803
- .option('--kind <kind>', 'filter by review target kind')
804
- .option('--target <id>', 'filter by review target ID')
805
- .option('--json', 'emit one review records JSON object', false)
806
- .action(async (rawOptions) => {
807
- const repoRoot = await resolveRepoRoot(cwd);
808
- const records = await listReviewRecords({
809
- repoRoot,
810
- ...(rawOptions.kind === undefined ? {} : { kind: rawOptions.kind }),
811
- ...(rawOptions.target === undefined ? {} : { targetId: rawOptions.target }),
812
- });
813
- if (rawOptions.json) {
814
- writeJsonReceipt(io.stdout, {
815
- schemaVersion: 1,
816
- command: 'review:list',
817
- ok: true,
818
- records: records.map((record) => reviewJsonRecord(repoRoot, record)),
1286
+ .command('inspect')
1287
+ .description('Inspect current AW Review source and materialization facts')
1288
+ .argument('<target>', 'Catalog Workspace, Plugin, or source Skill path')
1289
+ .addOption(strictValueOption('--workspace-root <root>', 'explicit Catalog Workspace root'))
1290
+ .option('--skill <name>', 'select one declared Skill', collectUniqueOption, [])
1291
+ .addOption(strictValueOption('--platform <platform>', 'select codex, claude, or agent-skills', ['codex', 'claude', 'agent-skills']))
1292
+ .addOption(strictValueOption('--scope <scope>', 'select user or project scope', ['user', 'project']))
1293
+ .addOption(strictValueOption('--config <file>', 'review config JSON file'))
1294
+ .option('--materialization <receipt>', 'validate one aw-source-binding receipt', collectUniqueOption, [])
1295
+ .addOption(strictValueOption('--view <view>', 'select the review navigation view', ['review']).default('review'))
1296
+ .option('--json', 'emit one Review inspection JSON object', false)
1297
+ .action(async (target, rawOptions) => {
1298
+ try {
1299
+ const receipt = await inspectReviewTarget({
1300
+ cwd,
1301
+ target,
1302
+ ...(rawOptions.workspaceRoot === undefined
1303
+ ? {}
1304
+ : { workspaceRoot: rawOptions.workspaceRoot }),
1305
+ skills: rawOptions.skill,
1306
+ ...(rawOptions.platform === undefined ? {} : { platform: rawOptions.platform }),
1307
+ ...(rawOptions.scope === undefined ? {} : { scope: rawOptions.scope }),
1308
+ ...(rawOptions.config === undefined ? {} : { configFile: rawOptions.config }),
1309
+ materializations: [...new Set(rawOptions.materialization
1310
+ .map((entry) => path.resolve(cwd, entry)))],
1311
+ view: rawOptions.view,
1312
+ env,
819
1313
  });
820
- return;
1314
+ if (rawOptions.json)
1315
+ writeJsonReceipt(io.stdout, receipt);
1316
+ else
1317
+ renderReviewInspection(io.stdout, receipt);
1318
+ commandExitState.exitCode = 0;
821
1319
  }
822
- for (const record of records) {
823
- writeLine(io.stdout, [
824
- record.manifest.reviewId,
825
- record.manifest.target.kind,
826
- record.manifest.target.id,
827
- record.manifest.publishedAt,
828
- ].join(' '));
1320
+ catch (error) {
1321
+ const receipt = createReviewInspectionFailure(error);
1322
+ if (rawOptions.json)
1323
+ writeJsonReceipt(io.stdout, receipt);
1324
+ else
1325
+ renderReviewInspection(io.stdout, receipt);
1326
+ commandExitState.exitCode = getReviewInspectionExitCode(error);
829
1327
  }
830
1328
  });
831
1329
  review
832
- .command('show')
833
- .description('Show one immutable review record')
834
- .argument('<review-id>', 'review record ID')
835
- .option('--json', 'emit one review record JSON object', false)
836
- .action(async (reviewId, rawOptions) => {
837
- const repoRoot = await resolveRepoRoot(cwd);
838
- const record = await showReviewRecord({ repoRoot, reviewId });
839
- if (rawOptions.json) {
840
- writeJsonReceipt(io.stdout, {
841
- schemaVersion: 1,
842
- command: 'review:show',
843
- ok: true,
844
- ...reviewJsonRecord(repoRoot, record),
845
- });
846
- return;
847
- }
848
- const manifest = record.manifest;
849
- writeLine(io.stdout, `Review: ${manifest.reviewId}`);
850
- writeLine(io.stdout, `Target: ${manifest.target.kind} ${manifest.target.id}`);
851
- writeLine(io.stdout, `Published: ${manifest.publishedAt}`);
852
- writeLine(io.stdout, `Report: ${manifest.report.path}`);
853
- writeLine(io.stdout, `Result: ${manifest.result?.path ?? 'none'}`);
854
- writeLine(io.stdout, `Checks: ${manifest.checks.map((entry) => entry.kind).join(', ') || 'none'}`);
855
- writeLine(io.stdout, `Evidence: ${manifest.evidence.map((entry) => entry.kind).join(', ') || 'none'}`);
856
- writeLine(io.stdout, `Release check: ${manifest.releaseCheck?.path ?? 'none'}`);
857
- writeLine(io.stdout, `Supersedes: ${manifest.supersedes ?? 'none'}`);
1330
+ .command('prepare')
1331
+ .description('Prepare deterministic Skill finals for AW Review')
1332
+ .argument('<target>', 'Plugin or source Skill path')
1333
+ .addOption(strictValueOption('--workspace-root <root>', 'explicit Catalog Workspace root'))
1334
+ .option('--skill <name>', 'select one declared Skill', collectUniqueOption, [])
1335
+ .addOption(strictValueOption('--platform <platform>', 'select codex, claude, or agent-skills', ['codex', 'claude', 'agent-skills']).makeOptionMandatory())
1336
+ .addOption(strictValueOption('--scope <scope>', 'select user or project scope', ['user', 'project']).makeOptionMandatory())
1337
+ .addOption(strictValueOption('--config <file>', 'review config JSON file'))
1338
+ .addOption(strictValueOption('--out <new-dir>', 'new output directory').makeOptionMandatory())
1339
+ .option('--json', 'emit one Review preparation JSON object', false)
1340
+ .action(async (target, rawOptions) => {
1341
+ const result = await prepareReviewTarget({
1342
+ cwd,
1343
+ target,
1344
+ ...(rawOptions.workspaceRoot === undefined
1345
+ ? {}
1346
+ : { workspaceRoot: rawOptions.workspaceRoot }),
1347
+ skills: rawOptions.skill,
1348
+ platform: rawOptions.platform,
1349
+ scope: rawOptions.scope,
1350
+ ...(rawOptions.config === undefined ? {} : { configFile: rawOptions.config }),
1351
+ out: rawOptions.out,
1352
+ env,
1353
+ });
1354
+ if (rawOptions.json)
1355
+ writeJsonReceipt(io.stdout, result.receipt);
1356
+ else
1357
+ renderReviewPreparation(io.stdout, result.receipt);
1358
+ commandExitState.exitCode = result.exitCode;
858
1359
  });
859
1360
  skill
860
1361
  .command('install')
@@ -864,15 +1365,17 @@ export function buildProgram(options = {}) {
864
1365
  .option('--skill <name...>', 'install only selected Skill names')
865
1366
  .option('--agent <id...>', 'install to explicit Agent IDs')
866
1367
  .option('-y, --yes', 'confirm the reviewed install', false)
867
- .option('-j, --json', 'emit one receipt v4 JSON object', false)
1368
+ .option('-j, --json', 'emit one receipt v5 JSON object', false)
868
1369
  .action(async (selector, rawOptions) => {
869
1370
  const context = createActionContext('skill:install', {});
870
1371
  const writeFailure = (error) => {
871
- const code = error instanceof PortableCommandError || error instanceof SkillsCliError
872
- ? error.code
873
- : 'portableInstallFailed';
1372
+ const capabilityError = error instanceof AwCapabilityUnsupportedError ? error : undefined;
1373
+ const code = capabilityError?.code
1374
+ ?? (error instanceof PortableCommandError || error instanceof SkillsCliError
1375
+ ? error.code
1376
+ : 'portableInstallFailed');
874
1377
  writeJsonReceipt(io.stdout, {
875
- receiptSchemaVersion: 4,
1378
+ receiptSchemaVersion: 5,
876
1379
  command: 'skill:install',
877
1380
  ok: false,
878
1381
  status: 'failed',
@@ -888,7 +1391,15 @@ export function buildProgram(options = {}) {
888
1391
  materializationDigest: null,
889
1392
  verification: { records: [], missing: [] },
890
1393
  warnings: [],
891
- failures: [{ code, message: `${code}: portable Skill 安装未执行。` }],
1394
+ failures: [{
1395
+ code,
1396
+ message: capabilityError?.message ?? `${code}: portable Skill 安装未执行。`,
1397
+ ...(capabilityError ? {
1398
+ plugin: capabilityError.plugin,
1399
+ missingCapabilities: capabilityError.missingCapabilities,
1400
+ availableCapabilities: capabilityError.availableCapabilities,
1401
+ } : {}),
1402
+ }],
892
1403
  });
893
1404
  };
894
1405
  if (rawOptions.json && !rawOptions.yes) {
@@ -926,6 +1437,7 @@ export function buildProgram(options = {}) {
926
1437
  else {
927
1438
  const sanitize = (value) => redactString(value)
928
1439
  .replace(/https?:\/\/\S+/giu, '[REDACTED URL]')
1440
+ .replace(/file:\/\/\S+/giu, '[REDACTED PATH]')
929
1441
  .replace(/(?:file:\/\/)?(?:\/[\w.@+-]+){2,}/gu, '[REDACTED PATH]')
930
1442
  .replace(/[A-Za-z]:\\(?:[^\s\\]+\\)+[^\s\\]*/gu, '[REDACTED PATH]');
931
1443
  const lines = new Set();
@@ -958,7 +1470,7 @@ export function buildProgram(options = {}) {
958
1470
  .requiredOption('-s, --scope <scope>', 'project or user')
959
1471
  .requiredOption('--out <dir>', 'output directory')
960
1472
  .option('--skill <name...>', 'write only selected Skill names')
961
- .option('-j, --json', 'emit one receipt v4 JSON object', false)
1473
+ .option('-j, --json', 'emit one receipt v5 JSON object', false)
962
1474
  .action(async (selector, rawOptions) => {
963
1475
  const context = createActionContext('skill:materialize', {});
964
1476
  try {
@@ -979,11 +1491,13 @@ export function buildProgram(options = {}) {
979
1491
  catch (error) {
980
1492
  if (!rawOptions.json)
981
1493
  throw error;
982
- const code = error instanceof PortableCommandError || error instanceof SkillsCliError
983
- ? error.code
984
- : 'portableMaterializeFailed';
1494
+ const capabilityError = error instanceof AwCapabilityUnsupportedError ? error : undefined;
1495
+ const code = capabilityError?.code
1496
+ ?? (error instanceof PortableCommandError || error instanceof SkillsCliError
1497
+ ? error.code
1498
+ : 'portableMaterializeFailed');
985
1499
  writeJsonReceipt(io.stdout, {
986
- receiptSchemaVersion: 4,
1500
+ receiptSchemaVersion: 5,
987
1501
  command: 'skill:materialize',
988
1502
  ok: false,
989
1503
  plugin: { id: selector },
@@ -995,7 +1509,15 @@ export function buildProgram(options = {}) {
995
1509
  configDigest: null,
996
1510
  materializationDigest: null,
997
1511
  outputDigest: null,
998
- errors: [{ code, message: `${code}: portable Skill 导出未执行。` }],
1512
+ errors: [{
1513
+ code,
1514
+ message: capabilityError?.message ?? `${code}: portable Skill 导出未执行。`,
1515
+ ...(capabilityError ? {
1516
+ plugin: capabilityError.plugin,
1517
+ missingCapabilities: capabilityError.missingCapabilities,
1518
+ availableCapabilities: capabilityError.availableCapabilities,
1519
+ } : {}),
1520
+ }],
999
1521
  });
1000
1522
  commandExitState.exitCode = 1;
1001
1523
  }
@@ -1007,7 +1529,7 @@ export function buildProgram(options = {}) {
1007
1529
  '',
1008
1530
  'During development, keep the current stable target when production content changes.',
1009
1531
  '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.',
1532
+ 'Publish only as an explicit later step; inspect `aw catalog publish plan --help` first.',
1011
1533
  '',
1012
1534
  ].join('\n'))
1013
1535
  .argument('<plugin-name>', 'lowercase hyphen-case plugin name')
@@ -1019,14 +1541,12 @@ export function buildProgram(options = {}) {
1019
1541
  .option('--skill-short-description <text>', 'initial Skill UI summary (25-64 characters)')
1020
1542
  .option('--skill-invocation <mode>', 'model | manual | claude-model-only (default: model)')
1021
1543
  .option('--version <semver>', 'initial stable plugin version (default: 0.1.0)')
1022
- .option('--min-version <semver>', 'compatibility.aw.minVersion')
1023
- .option('--catalog-min-version <semver>', 'target catalog compatibility minimum')
1024
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1544
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1025
1545
  .option('--no-skills', 'create the plugin without a skills directory')
1026
1546
  .option('--with-mcp', 'add a minimal .mcp.json', false)
1027
1547
  .option('--with-apps', 'add a minimal .app.json', false)
1028
1548
  .option('--with-config-api', 'add AW config schema and helper', false)
1029
- .option('--with-lifecycle', 'add a lifecycle script sample', false);
1549
+ .option('--with-pre-apply', 'add an idempotent Node preApply environment check', false);
1030
1550
  plugin
1031
1551
  .command('add-skill')
1032
1552
  .description('Add one static Skill scaffold to an existing plugin')
@@ -1034,7 +1554,7 @@ export function buildProgram(options = {}) {
1034
1554
  '',
1035
1555
  'During development, keep the current stable target when production content changes.',
1036
1556
  'Next, run `aw plugin validate`, `aw plugin test`, `aw plugin build`, and `aw catalog build`.',
1037
- 'Publish only as an explicit later step; inspect `aw catalog publish --help` first.',
1557
+ 'Publish only as an explicit later step; inspect `aw catalog publish plan --help` first.',
1038
1558
  '',
1039
1559
  ].join('\n'))
1040
1560
  .argument('<plugin-name>', 'existing lowercase hyphen-case plugin name')
@@ -1042,35 +1562,39 @@ export function buildProgram(options = {}) {
1042
1562
  .option('--skill-description <text>', 'Skill goal and discovery description (required)')
1043
1563
  .option('--skill-short-description <text>', 'Skill UI summary (25-64 characters)')
1044
1564
  .option('--skill-invocation <mode>', 'model | manual | claude-model-only (default: model)')
1045
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)');
1565
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)');
1046
1566
  plugin
1047
1567
  .command('remove')
1048
1568
  .description('Remove one committed and unchanged canonical plugin source tree')
1049
1569
  .argument('<plugin-name>', 'existing lowercase hyphen-case plugin name')
1050
- .requiredOption('--workspace-root <path>', 'Catalog Workspace root')
1570
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1051
1571
  .option('--yes', 'confirm plugin source deletion', false)
1052
1572
  .option('--json', 'emit one schema 1 JSON object', false);
1053
1573
  plugin
1054
1574
  .command('validate')
1055
1575
  .description('Validate one source plugin directory')
1056
1576
  .argument('<plugin-directory>', 'source plugin directory')
1057
- .option('--catalog-min-version <semver>', 'validate compatibility against a target catalog')
1058
1577
  .option('--strict-agent-skills', 'allow only Agent Skills portable core frontmatter', false)
1059
1578
  .option('--claude-skill-profile <profile>', 'minimum | current (default: minimum)')
1060
1579
  .option('--version-base <git-ref>', 'production bundle comparison base (default: HEAD)')
1061
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1580
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1062
1581
  .option('--json', 'emit one machine-readable JSON result', false);
1063
1582
  plugin
1064
1583
  .command('test')
1065
1584
  .description('Run deterministic Node tests declared by one source plugin')
1066
1585
  .argument('<plugin-directory>', 'source plugin directory')
1067
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1586
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1068
1587
  .option('--json', 'emit one machine-readable JSON result', false)
1069
1588
  .action(async (pluginDirectory, rawOptions) => {
1070
1589
  try {
1590
+ const workspace = await resolveCatalogWorkspaceRoot({
1591
+ cwd: path.resolve(cwd, pluginDirectory),
1592
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: path.resolve(cwd, rawOptions.workspaceRoot) }),
1593
+ mode: 'existing',
1594
+ });
1071
1595
  commandExitState.exitCode = await runPluginTestCommand({
1072
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1073
- pluginDirectory,
1596
+ ...workspace,
1597
+ pluginDirectory: path.resolve(cwd, pluginDirectory),
1074
1598
  json: rawOptions.json,
1075
1599
  io,
1076
1600
  });
@@ -1087,15 +1611,20 @@ export function buildProgram(options = {}) {
1087
1611
  .command('build')
1088
1612
  .description('Build and inspect one source plugin bundle')
1089
1613
  .argument('<plugin-directory>', 'source plugin directory')
1090
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1614
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from plugin directory)')
1091
1615
  .option('--out <path>', 'output path inside the Catalog Workspace')
1092
1616
  .option('--force', 'replace an existing generated output', false)
1093
1617
  .option('--json', 'emit one machine-readable JSON result', false)
1094
1618
  .action(async (pluginDirectory, rawOptions) => {
1095
1619
  try {
1620
+ const workspace = await resolveCatalogWorkspaceRoot({
1621
+ cwd: path.resolve(cwd, pluginDirectory),
1622
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: path.resolve(cwd, rawOptions.workspaceRoot) }),
1623
+ mode: 'existing',
1624
+ });
1096
1625
  await runPluginBuildCommand({
1097
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1098
- pluginDirectory,
1626
+ ...workspace,
1627
+ pluginDirectory: path.resolve(cwd, pluginDirectory),
1099
1628
  out: rawOptions.out,
1100
1629
  force: rawOptions.force,
1101
1630
  json: rawOptions.json,
@@ -1117,15 +1646,7 @@ export function buildProgram(options = {}) {
1117
1646
  .option('--all', 'check every current Catalog Workspace plugin', false)
1118
1647
  .option('--base <git-ref>', 'explicit Git comparison base')
1119
1648
  .option('--json', 'emit one schema v3 JSON document', false)
1120
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)');
1121
- plugin
1122
- .command('check-versions')
1123
- .description('Deprecated alias for plugin status')
1124
- .argument('[plugin-name]', 'one explicit plugin name')
1125
- .option('--all', 'check every plugin changed relative to the Git base', false)
1126
- .option('--base <git-ref>', 'explicit Git comparison base')
1127
- .option('--json', 'emit one schema v3 JSON document', false)
1128
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)');
1649
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)');
1129
1650
  skill
1130
1651
  .command('validate')
1131
1652
  .description('Validate one Agent Skill directory')
@@ -1139,7 +1660,7 @@ export function buildProgram(options = {}) {
1139
1660
  catalog
1140
1661
  .command('init')
1141
1662
  .description('Initialize a local AW Catalog Workspace')
1142
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1663
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor, then Git worktree root, then current directory)')
1143
1664
  .option('--owner-name <name>', 'catalog owner name')
1144
1665
  .option('--owner-url <url>', 'catalog owner URL')
1145
1666
  .option('--display-name <name>', 'catalog display name')
@@ -1148,9 +1669,15 @@ export function buildProgram(options = {}) {
1148
1669
  .option('--json', 'emit one machine-readable JSON result', false)
1149
1670
  .action(async (rawOptions) => {
1150
1671
  try {
1672
+ const workspace = await resolveCatalogWorkspaceRoot({
1673
+ cwd,
1674
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1675
+ mode: 'initialize',
1676
+ env,
1677
+ });
1151
1678
  await runCatalogInitCommand({
1152
1679
  ...rawOptions,
1153
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1680
+ ...workspace,
1154
1681
  io,
1155
1682
  });
1156
1683
  }
@@ -1167,12 +1694,17 @@ export function buildProgram(options = {}) {
1167
1694
  .description('Read or set the Catalog version')
1168
1695
  .argument('[version]', 'stable or x.y.z-beta.N catalog version')
1169
1696
  .option('--bump <level>', 'bump the stable SemVer base: patch, minor, or major')
1170
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1697
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1171
1698
  .option('--json', 'emit one machine-readable JSON result', false)
1172
1699
  .action(async (version, rawOptions) => {
1173
1700
  try {
1701
+ const workspace = await resolveCatalogWorkspaceRoot({
1702
+ cwd,
1703
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1704
+ mode: 'existing',
1705
+ });
1174
1706
  await runCatalogVersionCommand({
1175
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1707
+ ...workspace,
1176
1708
  version,
1177
1709
  bump: rawOptions.bump,
1178
1710
  json: rawOptions.json,
@@ -1190,14 +1722,19 @@ export function buildProgram(options = {}) {
1190
1722
  catalog
1191
1723
  .command('build')
1192
1724
  .description('Build a deterministic development Catalog snapshot')
1193
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1725
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1194
1726
  .option('--out <path>', 'output path inside the Catalog Workspace')
1195
1727
  .option('--force', 'replace an existing generated output', false)
1196
1728
  .option('--json', 'emit one machine-readable JSON result', false)
1197
1729
  .action(async (rawOptions) => {
1198
1730
  try {
1731
+ const workspace = await resolveCatalogWorkspaceRoot({
1732
+ cwd,
1733
+ ...(rawOptions.workspaceRoot === undefined ? {} : { explicitRoot: rawOptions.workspaceRoot }),
1734
+ mode: 'existing',
1735
+ });
1199
1736
  await runCatalogBuildCommand({
1200
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1737
+ ...workspace,
1201
1738
  out: rawOptions.out,
1202
1739
  force: rawOptions.force,
1203
1740
  json: rawOptions.json,
@@ -1214,7 +1751,7 @@ export function buildProgram(options = {}) {
1214
1751
  });
1215
1752
  catalog
1216
1753
  .command('validate')
1217
- .description('Validate a schema 4 Catalog snapshot')
1754
+ .description('Validate a schema 7 Catalog snapshot')
1218
1755
  .argument('<snapshot-directory>', 'Catalog snapshot directory')
1219
1756
  .option('--json', 'emit one machine-readable JSON result', false)
1220
1757
  .action(async (snapshotDirectory, rawOptions) => {
@@ -1235,18 +1772,13 @@ export function buildProgram(options = {}) {
1235
1772
  });
1236
1773
  catalog
1237
1774
  .command('status')
1238
- .description('Inspect a Catalog Workspace; --preflight executes plugin validation and build code')
1239
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1240
- .option('--remote <name>', 'configured Git remote name for preflight (default: origin)')
1241
- .option('--branch <name>', 'dedicated Catalog branch for preflight (default: catalog)')
1242
- .option('--preflight', 'run publication validation and temporary plugin builds (executes plugin code)', false)
1775
+ .description('Inspect static Catalog Workspace, snapshot, and Git facts')
1776
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1243
1777
  .option('--json', 'emit one machine-readable JSON status object', false)
1244
1778
  .action(async (rawOptions) => {
1245
1779
  const receipt = await getCatalogStatus({
1246
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1247
- remote: rawOptions.remote,
1248
- branch: rawOptions.branch,
1249
- preflight: rawOptions.preflight,
1780
+ cwd,
1781
+ workspaceRoot: rawOptions.workspaceRoot,
1250
1782
  env,
1251
1783
  });
1252
1784
  if (rawOptions.json)
@@ -1255,23 +1787,74 @@ export function buildProgram(options = {}) {
1255
1787
  io.stdout(renderCatalogStatus(receipt));
1256
1788
  commandExitState.exitCode = receipt.ok ? 0 : 1;
1257
1789
  });
1258
- catalog
1790
+ const catalogPublish = catalog
1259
1791
  .command('publish')
1260
- .description('Publish a versioned Catalog snapshot to a configured Git remote')
1261
- .option('--workspace-root <path>', 'Catalog Workspace root (default: current directory)')
1262
- .option('--remote <name>', 'configured Git remote name (default: origin)')
1792
+ .description('Plan or apply a versioned Catalog publication');
1793
+ catalogPublish
1794
+ .command('plan')
1795
+ .description('Prepare and inspect the exact Catalog publication plan')
1796
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1797
+ .option('--remote <name>', 'Git remote (default: origin, otherwise sole remote; explicit if ambiguous)')
1263
1798
  .option('--branch <name>', 'dedicated Catalog branch (default: catalog)')
1264
- .option('--expected-descriptor-digest <digest>', 'required exact descriptor returned by publication preflight')
1265
- .option('-y, --yes', 'confirm publication without a prompt', false)
1266
1799
  .option('--json', 'emit one machine-readable JSON result', false)
1267
1800
  .action(async (rawOptions) => {
1268
1801
  try {
1269
- const context = createActionContext('catalog:publish', {});
1270
- const result = await runCatalogPublishCommand({
1271
- workspaceRoot: path.resolve(cwd, rawOptions.workspaceRoot ?? '.'),
1802
+ const result = await runCatalogPublishPlanCommand({
1803
+ cwd,
1804
+ workspaceRoot: rawOptions.workspaceRoot,
1272
1805
  remote: rawOptions.remote,
1273
1806
  branch: rawOptions.branch,
1274
- expectedDescriptorDigest: rawOptions.expectedDescriptorDigest,
1807
+ env,
1808
+ io,
1809
+ });
1810
+ if (rawOptions.json)
1811
+ writeJsonReceipt(io.stdout, result);
1812
+ else {
1813
+ writeLine(io.stdout, `Workspace identity: ${result.workspaceIdentity}`);
1814
+ writeLine(io.stdout, `Source HEAD: ${result.sourceHead}`);
1815
+ writeLine(io.stdout, `Remote identity: ${result.descriptor.remote.identityDigest}`);
1816
+ writeLine(io.stdout, `Remote: ${result.remote} (${result.remoteUrl})`);
1817
+ writeLine(io.stdout, `Branch: refs/heads/${result.branch}`);
1818
+ writeLine(io.stdout, `Parent: ${result.parent ?? '<root>'}`);
1819
+ writeLine(io.stdout, `Tag: refs/tags/${result.tag}`);
1820
+ writeLine(io.stdout, `Snapshot digest: ${result.snapshotDigest}`);
1821
+ writeLine(io.stdout, `Expected commit: ${result.expectedCommit}`);
1822
+ writeLine(io.stdout, `Plan digest: ${result.planDigest}`);
1823
+ }
1824
+ }
1825
+ catch (error) {
1826
+ const failure = catalogPublicationFailure({
1827
+ error,
1828
+ env,
1829
+ sensitiveRoots: [
1830
+ cwd,
1831
+ ...(rawOptions.workspaceRoot ? [path.resolve(cwd, rawOptions.workspaceRoot)] : []),
1832
+ ],
1833
+ });
1834
+ if (rawOptions.json)
1835
+ writeJsonReceipt(io.stdout, failure);
1836
+ writeLine(io.stderr, failure.message);
1837
+ commandExitState.exitCode = 1;
1838
+ }
1839
+ });
1840
+ catalogPublish
1841
+ .command('apply')
1842
+ .description('Apply an authorized Catalog publication plan')
1843
+ .option('--workspace-root <path>', 'Catalog Workspace root (default: nearest ancestor from current directory)')
1844
+ .option('--remote <name>', 'Git remote (default: origin, otherwise sole remote; explicit if ambiguous)')
1845
+ .option('--branch <name>', 'dedicated Catalog branch (default: catalog)')
1846
+ .requiredOption('--expected-plan-digest <sha256>', 'exact plan digest returned by catalog publish plan')
1847
+ .option('-y, --yes', 'confirm publication without a prompt', false)
1848
+ .option('--json', 'emit one machine-readable JSON result', false)
1849
+ .action(async (rawOptions) => {
1850
+ try {
1851
+ const context = createActionContext('catalog:publish:apply', {});
1852
+ const result = await runCatalogPublishApplyCommand({
1853
+ cwd,
1854
+ workspaceRoot: rawOptions.workspaceRoot,
1855
+ remote: rawOptions.remote,
1856
+ branch: rawOptions.branch,
1857
+ expectedPlanDigest: rawOptions.expectedPlanDigest,
1275
1858
  yes: rawOptions.yes,
1276
1859
  json: rawOptions.json,
1277
1860
  env,
@@ -1297,14 +1880,95 @@ export function buildProgram(options = {}) {
1297
1880
  }
1298
1881
  }
1299
1882
  catch (error) {
1300
- const message = error instanceof Error ? error.message : String(error);
1883
+ const failure = catalogPublicationFailure({
1884
+ error,
1885
+ env,
1886
+ sensitiveRoots: [
1887
+ cwd,
1888
+ ...(rawOptions.workspaceRoot ? [path.resolve(cwd, rawOptions.workspaceRoot)] : []),
1889
+ ],
1890
+ });
1301
1891
  if (rawOptions.json)
1302
- writeJsonReceipt(io.stdout, {
1303
- command: 'catalog:publish',
1304
- ok: false,
1305
- errors: [{ message }],
1306
- });
1307
- writeLine(io.stderr, message);
1892
+ writeJsonReceipt(io.stdout, failure);
1893
+ writeLine(io.stderr, failure.message);
1894
+ commandExitState.exitCode = 1;
1895
+ }
1896
+ });
1897
+ const catalogDev = catalog
1898
+ .command('dev')
1899
+ .description('Manage the current Catalog Workspace development source');
1900
+ catalogDev
1901
+ .command('activate')
1902
+ .option('--workspace-root <path>', 'explicit Catalog Workspace root')
1903
+ .option('--source-id <id>', 'explicit managed source ID')
1904
+ .option('--primary', 'make the development source primary', false)
1905
+ .option('--force-build', 'rebuild a current snapshot', false)
1906
+ .option('-y, --yes', 'confirm affected or unknown source references', false)
1907
+ .requiredOption('--json', 'emit one schema 2 JSON receipt')
1908
+ .action(async (rawOptions) => {
1909
+ try {
1910
+ const receipt = await activateCatalogDevSource({
1911
+ cwd,
1912
+ workspaceRoot: rawOptions.workspaceRoot,
1913
+ sourceId: rawOptions.sourceId,
1914
+ primary: rawOptions.primary,
1915
+ forceBuild: rawOptions.forceBuild,
1916
+ yes: rawOptions.yes,
1917
+ cliVersion: resolveCliVersion(),
1918
+ env,
1919
+ });
1920
+ writeJsonReceipt(io.stdout, receipt);
1921
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
1922
+ }
1923
+ catch (error) {
1924
+ const failure = catalogDevSourceError(error);
1925
+ writeJsonReceipt(io.stdout, createCatalogDevFailureReceipt({
1926
+ command: 'activate', cwd, workspaceRoot: rawOptions.workspaceRoot,
1927
+ sourceId: rawOptions.sourceId, ...failure,
1928
+ }));
1929
+ commandExitState.exitCode = 1;
1930
+ }
1931
+ });
1932
+ catalogDev
1933
+ .command('status')
1934
+ .option('--workspace-root <path>', 'explicit Catalog Workspace root')
1935
+ .requiredOption('--json', 'emit one schema 4 JSON receipt')
1936
+ .action(async (rawOptions) => {
1937
+ const receipt = await getCatalogDevSourceStatus({
1938
+ cwd,
1939
+ workspaceRoot: rawOptions.workspaceRoot,
1940
+ cliVersion: resolveCliVersion(),
1941
+ env,
1942
+ });
1943
+ writeJsonReceipt(io.stdout, receipt);
1944
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
1945
+ });
1946
+ catalogDev
1947
+ .command('deactivate')
1948
+ .option('--workspace-root <path>', 'explicit Catalog Workspace root')
1949
+ .option('--source-id <id>', 'recover one managed source by exact ID')
1950
+ .option('--replacement-primary <id>', 'replacement when the target source is primary')
1951
+ .option('-y, --yes', 'confirm source impact or recovery by source ID', false)
1952
+ .requiredOption('--json', 'emit one schema 4 JSON receipt')
1953
+ .action(async (rawOptions) => {
1954
+ try {
1955
+ const receipt = await deactivateCatalogDevSource({
1956
+ cwd,
1957
+ workspaceRoot: rawOptions.workspaceRoot,
1958
+ sourceId: rawOptions.sourceId,
1959
+ replacementPrimary: rawOptions.replacementPrimary,
1960
+ yes: rawOptions.yes,
1961
+ env,
1962
+ });
1963
+ writeJsonReceipt(io.stdout, receipt);
1964
+ commandExitState.exitCode = receipt.ok ? 0 : 1;
1965
+ }
1966
+ catch (error) {
1967
+ const failure = catalogDevSourceError(error);
1968
+ writeJsonReceipt(io.stdout, createCatalogDevFailureReceipt({
1969
+ command: 'deactivate', cwd, workspaceRoot: rawOptions.workspaceRoot,
1970
+ sourceId: rawOptions.sourceId, ...failure,
1971
+ }));
1308
1972
  commandExitState.exitCode = 1;
1309
1973
  }
1310
1974
  });
@@ -1316,52 +1980,70 @@ export function buildProgram(options = {}) {
1316
1980
  const message = failure instanceof Error ? failure.message : String(error);
1317
1981
  if (json) {
1318
1982
  writeJsonReceipt(io.stdout, {
1319
- receiptSchemaVersion: 4,
1983
+ receiptSchemaVersion: 6,
1320
1984
  command,
1321
1985
  ok: false,
1322
1986
  changed: false,
1323
1987
  sourceId,
1324
- references: failure.inventory?.references ?? [],
1988
+ primaryChange: failure.impact?.primaryChange ?? null,
1989
+ references: failure.impact?.references ?? failure.inventory?.references ?? [],
1325
1990
  errors: [{ code: failure.code ?? failure.name ?? 'catalogSourceMutationFailed', message }],
1326
1991
  });
1327
1992
  }
1328
1993
  writeLine(io.stderr, message);
1329
1994
  commandExitState.exitCode = 1;
1330
1995
  };
1331
- const writeConfigMutationFailure = (command, plugin, json, error) => {
1332
- const message = error instanceof Error ? error.message : String(error);
1333
- const code = message.includes('legacyStateUnsupported')
1334
- ? 'legacyStateUnsupported'
1335
- : message.includes('sourceChanged')
1336
- ? 'sourceChanged'
1337
- : message.includes('sourceMissing')
1338
- ? 'sourceMissing'
1339
- : message.startsWith('Cannot resolve required catalog sources:')
1340
- ? 'catalogSourceUnavailable'
1341
- : message.split(':', 1)[0] || 'configMutationFailed';
1342
- if (json && !message.includes('Usage:')) {
1996
+ const writeCatalogGenerationFailure = (command, sourceId, json, error) => {
1997
+ const failure = sanitizeCatalogSourceError(error, env);
1998
+ const message = redactString(failure.message);
1999
+ if (json) {
1343
2000
  writeJsonReceipt(io.stdout, {
1344
- receiptSchemaVersion: 4,
2001
+ ...(command === 'catalog:source:sync'
2002
+ ? { catalogGenerationSchemaVersion: 1 }
2003
+ : { catalogGenerationStatusSchemaVersion: 2 }),
1345
2004
  command,
1346
2005
  ok: false,
1347
- overallState: 'failed',
1348
- ...(plugin ? { plugin } : {}),
1349
- errors: [{ code, message }],
2006
+ ...(sourceId ? { sourceId } : {}),
2007
+ ...(failure.impact ? { impact: failure.impact } : {}),
2008
+ errors: [{ code: catalogGenerationErrorCode(error), message }],
1350
2009
  });
1351
2010
  }
1352
2011
  writeLine(io.stderr, message);
1353
2012
  commandExitState.exitCode = 1;
1354
2013
  };
1355
- const withResolvedConfigMutationPlugin = (context, selector, consume) => withAwHomeMutationLock({
1356
- env: context.env,
1357
- action: (awHomeMutationToken) => withResolvedCatalogPlugin({
1358
- selector,
1359
- cliVersion: resolveCliVersion(),
1360
- freshnessPolicy: 'requireRemote',
2014
+ const writeConfigMutationFailure = (command, plugin, json, error) => {
2015
+ const failure = configMutationFailure(error, env, cwd);
2016
+ if (json) {
2017
+ writeConfigMutationFailureReceipt(io.stdout, {
2018
+ command,
2019
+ ...(plugin ? { plugin } : {}),
2020
+ }, failure);
2021
+ }
2022
+ const message = json
2023
+ ? failure.message
2024
+ : redactString(sanitizeCatalogSourceErrorMessage(error instanceof Error ? error.message : String(error), env, [cwd]));
2025
+ writeLine(io.stderr, message);
2026
+ commandExitState.exitCode = 1;
2027
+ };
2028
+ const withResolvedConfigMutationPlugin = (context, selector, consume) => (async () => {
2029
+ const parsed = parsePluginId(selector);
2030
+ return withAwHomeMutationLock({
1361
2031
  env: context.env,
1362
- consume: (resolution) => consume(resolution, awHomeMutationToken),
1363
- }),
1364
- });
2032
+ action: async (awHomeMutationToken) => {
2033
+ const catalogOperationContext = await resolveCatalogOperationContext({
2034
+ ...(parsed.sourceId ? { sourceIds: [parsed.sourceId] } : {}),
2035
+ env: context.env,
2036
+ });
2037
+ return withResolvedCatalogPlugin({
2038
+ selector,
2039
+ cliVersion: resolveCliVersion(),
2040
+ operationContext: catalogOperationContext,
2041
+ env: context.env,
2042
+ consume: (resolution) => consume(resolution, awHomeMutationToken, catalogOperationContext),
2043
+ });
2044
+ },
2045
+ });
2046
+ })();
1365
2047
  catalogSource
1366
2048
  .command('list')
1367
2049
  .option('-j, --json', 'print catalog sources as JSON', false)
@@ -1376,6 +2058,70 @@ export function buildProgram(options = {}) {
1376
2058
  writeLine(io.stdout, `${source.primary ? '*' : '-'} ${source.id} ${source.type} ${source.sourceIdentity}`);
1377
2059
  }
1378
2060
  });
2061
+ catalogSource
2062
+ .command('sync')
2063
+ .argument('<id>')
2064
+ .option('--check', 'fetch and validate without changing the Catalog store or Active state', false)
2065
+ .option('--replace-damaged', 'replace a damaged Active entry with the current candidate', false)
2066
+ .option('-y, --yes', 'confirm affected, unknown, or damaged Active replacement impact', false)
2067
+ .option('-j, --json', 'print the source sync result as JSON', false)
2068
+ .action(async (id, rawOptions) => {
2069
+ try {
2070
+ if (rawOptions.check && rawOptions.replaceDamaged) {
2071
+ throw new Error('catalog source sync --check cannot use --replace-damaged.');
2072
+ }
2073
+ const result = await syncCatalogSourceCommand({
2074
+ sourceId: id,
2075
+ cliVersion: resolveCliVersion(),
2076
+ cwd,
2077
+ check: rawOptions.check === true,
2078
+ impactConfirmed: rawOptions.yes === true,
2079
+ replaceDamaged: rawOptions.replaceDamaged === true,
2080
+ env,
2081
+ });
2082
+ if (rawOptions.json) {
2083
+ writeJsonReceipt(io.stdout, {
2084
+ ...result,
2085
+ command: 'catalog:source:sync',
2086
+ ok: true,
2087
+ });
2088
+ }
2089
+ else {
2090
+ writeLine(io.stdout, rawOptions.check
2091
+ ? `Catalog source ${id} is valid.`
2092
+ : `Catalog source ${id}: ${result.outcome}.`);
2093
+ }
2094
+ }
2095
+ catch (error) {
2096
+ writeCatalogGenerationFailure('catalog:source:sync', id, rawOptions.json === true, error);
2097
+ }
2098
+ });
2099
+ catalogSource
2100
+ .command('status')
2101
+ .argument('[id]')
2102
+ .option('-j, --json', 'print offline Catalog source status schema 2 as JSON', false)
2103
+ .action(async (id, rawOptions) => {
2104
+ try {
2105
+ const result = await catalogSourceStatusCommand({ sourceId: id, env });
2106
+ if (rawOptions.json) {
2107
+ writeJsonReceipt(io.stdout, result);
2108
+ }
2109
+ else {
2110
+ for (const source of result.sources) {
2111
+ const active = source.active
2112
+ ? `Active ${source.active.snapshotDigest}`
2113
+ : 'no Active';
2114
+ const readiness = source.compatibility?.state === 'unsupported'
2115
+ ? `incompatible (required: ${source.compatibility.requiredCapabilities.join(', ')}; missing: ${source.compatibility.missingCapabilities.join(', ')})`
2116
+ : source.activeReady ? 'ready' : 'not ready';
2117
+ writeLine(io.stdout, `${source.sourceId}: ${active}, ${readiness}`);
2118
+ }
2119
+ }
2120
+ }
2121
+ catch (error) {
2122
+ writeCatalogGenerationFailure('catalog:source:status', id, rawOptions.json === true, error);
2123
+ }
2124
+ });
1379
2125
  catalogSource
1380
2126
  .command('add')
1381
2127
  .argument('<id>')
@@ -1384,7 +2130,9 @@ export function buildProgram(options = {}) {
1384
2130
  .addOption(new Option('--ref <ref>', 'use an explicit Git ref'))
1385
2131
  .addOption(new Option('--default-ref', 'select the Git ref from the CLI release channel'))
1386
2132
  .option('--subdir <path>', 'use a catalog snapshot below the Git repository root')
2133
+ .option('--no-auto-check', 'disable automatic update checks for this source')
1387
2134
  .option('--primary', 'make this source primary', false)
2135
+ .option('-y, --yes', 'confirm affected or unknown primary source references', false)
1388
2136
  .option('-j, --json', 'print mutation receipt as JSON', false)
1389
2137
  .action(async (id, rawOptions) => {
1390
2138
  try {
@@ -1395,7 +2143,7 @@ export function buildProgram(options = {}) {
1395
2143
  throw new Error('Git catalog source add requires exactly one of `--ref` or `--default-ref`.');
1396
2144
  }
1397
2145
  if (rawOptions.path !== undefined && (rawOptions.ref !== undefined || rawOptions.defaultRef === true || rawOptions.subdir !== undefined)) {
1398
- throw new Error('Path catalog source add only accepts `--path` and `--primary`.');
2146
+ throw new Error('Path catalog source add only accepts `--path`, `--primary`, `--no-auto-check`, and `--yes`.');
1399
2147
  }
1400
2148
  const source = rawOptions.git !== undefined
1401
2149
  ? {
@@ -1405,18 +2153,32 @@ export function buildProgram(options = {}) {
1405
2153
  url: rawOptions.git,
1406
2154
  refPolicy: explicitRefPolicy(rawOptions.ref, rawOptions.defaultRef === true),
1407
2155
  ...(rawOptions.subdir ? { subdir: rawOptions.subdir } : {}),
2156
+ ...(rawOptions.autoCheck === false ? { autoCheck: false } : {}),
1408
2157
  }
1409
2158
  : {
1410
2159
  id,
1411
2160
  type: 'path',
1412
2161
  primary: rawOptions.primary === true,
1413
2162
  path: rawOptions.path,
2163
+ ...(rawOptions.autoCheck === false ? { autoCheck: false } : {}),
1414
2164
  };
2165
+ const preview = source.primary
2166
+ ? await preflightCatalogSourceMutation({
2167
+ request: { action: 'add', source }, cwd, env,
2168
+ })
2169
+ : undefined;
1415
2170
  const receipt = await addCatalogSourceCommand({
1416
2171
  source,
1417
2172
  cliVersion: resolveCliVersion(),
1418
2173
  cwd,
1419
2174
  env,
2175
+ ...(preview ? {
2176
+ expectedConfigDigest: preview.configDigest,
2177
+ expectedProposedConfigDigest: preview.proposedConfigDigest,
2178
+ expectedImpactDigest: preview.impactDigest,
2179
+ } : {}),
2180
+ requireImpactConfirmation: true,
2181
+ confirmImpact: rawOptions.yes === true,
1420
2182
  });
1421
2183
  if (rawOptions.json)
1422
2184
  writeJsonReceipt(io.stdout, receipt);
@@ -1438,6 +2200,9 @@ export function buildProgram(options = {}) {
1438
2200
  .addOption(new Option('--default-ref', 'select the Git ref from the CLI release channel'))
1439
2201
  .addOption(new Option('--subdir <path>', 'update the Git catalog subdirectory'))
1440
2202
  .addOption(new Option('--no-subdir', 'remove the Git catalog subdirectory'))
2203
+ .addOption(new Option('--auto-check', 'enable automatic update checks for this source'))
2204
+ .addOption(new Option('--no-auto-check', 'disable automatic update checks for this source'))
2205
+ .option('-y, --yes', 'confirm a source identity change', false)
1441
2206
  .option('-j, --json', 'print mutation receipt as JSON', false)
1442
2207
  .action(async (id, rawOptions) => {
1443
2208
  try {
@@ -1451,15 +2216,25 @@ export function buildProgram(options = {}) {
1451
2216
  || rawOptions.path !== undefined
1452
2217
  || rawOptions.ref !== undefined
1453
2218
  || rawOptions.defaultRef === true
1454
- || rawOptions.subdir !== undefined;
2219
+ || rawOptions.subdir !== undefined
2220
+ || rawOptions.autoCheck !== undefined;
1455
2221
  if (!hasUpdate)
1456
2222
  throw new Error('catalog source update requires at least one update option.');
1457
2223
  if (rawOptions.path !== undefined && (rawOptions.ref !== undefined || rawOptions.defaultRef === true || rawOptions.subdir !== undefined))
1458
- throw new Error('Path catalog source update only accepts `--path`.');
2224
+ throw new Error('Path catalog source update only accepts `--path` and auto-check options.');
2225
+ const hasGitUpdate = rawOptions.git !== undefined
2226
+ || rawOptions.ref !== undefined
2227
+ || rawOptions.defaultRef === true
2228
+ || rawOptions.subdir !== undefined;
1459
2229
  const update = rawOptions.path !== undefined
1460
- ? { type: 'path', path: rawOptions.path }
1461
- : {
2230
+ ? {
2231
+ type: 'path',
2232
+ path: rawOptions.path,
2233
+ ...(rawOptions.autoCheck !== undefined ? { autoCheck: rawOptions.autoCheck } : {}),
2234
+ }
2235
+ : hasGitUpdate ? {
1462
2236
  type: 'git',
2237
+ ...(rawOptions.autoCheck !== undefined ? { autoCheck: rawOptions.autoCheck } : {}),
1463
2238
  ...(rawOptions.git !== undefined ? { url: rawOptions.git } : {}),
1464
2239
  ...(rawOptions.ref !== undefined || rawOptions.defaultRef === true
1465
2240
  ? { refPolicy: explicitRefPolicy(rawOptions.ref, rawOptions.defaultRef === true) }
@@ -1467,13 +2242,22 @@ export function buildProgram(options = {}) {
1467
2242
  ...(rawOptions.subdir !== undefined
1468
2243
  ? { subdir: rawOptions.subdir === false ? null : rawOptions.subdir }
1469
2244
  : {}),
1470
- };
2245
+ }
2246
+ : { autoCheck: rawOptions.autoCheck };
2247
+ const preview = await preflightCatalogSourceMutation({
2248
+ request: { action: 'update', sourceId: id, update }, cwd, env,
2249
+ });
1471
2250
  const receipt = await updateCatalogSourceCommand({
1472
2251
  sourceId: id,
1473
2252
  update,
1474
2253
  cliVersion: resolveCliVersion(),
1475
2254
  cwd,
1476
2255
  env,
2256
+ expectedConfigDigest: preview.configDigest,
2257
+ expectedProposedConfigDigest: preview.proposedConfigDigest,
2258
+ expectedImpactDigest: preview.impactDigest,
2259
+ requireIdentityChangeConfirmation: true,
2260
+ confirmIdentityChange: rawOptions.yes === true,
1477
2261
  });
1478
2262
  if (rawOptions.json)
1479
2263
  writeJsonReceipt(io.stdout, receipt);
@@ -1489,10 +2273,17 @@ export function buildProgram(options = {}) {
1489
2273
  catalogSource
1490
2274
  .command('set-primary')
1491
2275
  .argument('<id>')
2276
+ .option('-y, --yes', 'confirm affected or unknown primary source references', false)
1492
2277
  .option('-j, --json', 'print mutation receipt as JSON', false)
1493
2278
  .action(async (id, rawOptions) => {
1494
2279
  try {
1495
- const receipt = await setPrimaryCatalogSourceCommand({ sourceId: id, cwd, env });
2280
+ const receipt = await setPrimaryCatalogSourceCommand({
2281
+ sourceId: id,
2282
+ cwd,
2283
+ env,
2284
+ requireImpactConfirmation: true,
2285
+ confirmImpact: rawOptions.yes === true,
2286
+ });
1496
2287
  if (rawOptions.json)
1497
2288
  writeJsonReceipt(io.stdout, receipt);
1498
2289
  else
@@ -1505,10 +2296,17 @@ export function buildProgram(options = {}) {
1505
2296
  catalogSource
1506
2297
  .command('remove')
1507
2298
  .argument('<id>')
2299
+ .option('-y, --yes', 'confirm affected or unknown source references', false)
1508
2300
  .option('-j, --json', 'print mutation receipt as JSON', false)
1509
2301
  .action(async (id, rawOptions) => {
1510
2302
  try {
1511
- const receipt = await removeCatalogSourceCommand({ sourceId: id, cwd, env });
2303
+ const receipt = await removeCatalogSourceCommand({
2304
+ sourceId: id,
2305
+ cwd,
2306
+ env,
2307
+ requireImpactConfirmation: true,
2308
+ confirmImpact: rawOptions.yes === true,
2309
+ });
1512
2310
  if (rawOptions.json)
1513
2311
  writeJsonReceipt(io.stdout, receipt);
1514
2312
  else
@@ -1518,9 +2316,102 @@ export function buildProgram(options = {}) {
1518
2316
  await writeCatalogSourceFailure('catalog:source:remove', id, rawOptions.json === true, error);
1519
2317
  }
1520
2318
  });
2319
+ const catalogStore = catalog
2320
+ .command('store')
2321
+ .description('Inspect or prune the immutable Catalog store');
2322
+ catalogStore
2323
+ .command('status')
2324
+ .option('-j, --json', 'print Catalog store status as JSON', false)
2325
+ .action(async (rawOptions) => {
2326
+ try {
2327
+ const result = await catalogStoreStatus({ env });
2328
+ if (rawOptions.json) {
2329
+ writeJsonReceipt(io.stdout, result);
2330
+ }
2331
+ else if (result.entries.length === 0) {
2332
+ writeLine(io.stdout, 'Catalog store is empty.');
2333
+ }
2334
+ else {
2335
+ for (const entry of result.entries) {
2336
+ const bytes = entry.reclaimableBytes === null ? 'unknown bytes' : `${entry.reclaimableBytes} bytes`;
2337
+ writeLine(io.stdout, `${entry.storeKey}: ${entry.category}, ${entry.status}, ${entry.reason}, ${bytes}`);
2338
+ }
2339
+ writeLine(io.stdout, `Reclaimable: ${result.totals.reclaimableEntries} item(s).`);
2340
+ if (result.errors.length > 0) {
2341
+ writeLine(io.stderr, `${result.errors.length} item(s) could not be fully inspected and were retained.`);
2342
+ }
2343
+ }
2344
+ commandExitState.exitCode = result.ok ? 0 : 1;
2345
+ }
2346
+ catch (error) {
2347
+ const failure = sanitizeCatalogSourceError(error, env);
2348
+ const message = redactString(failure.message);
2349
+ if (rawOptions.json)
2350
+ writeJsonReceipt(io.stdout, {
2351
+ catalogStoreSchemaVersion: 2,
2352
+ command: 'catalog:store:status',
2353
+ ok: false,
2354
+ entries: [],
2355
+ errors: [{ code: catalogStoreErrorCode(error), message }],
2356
+ });
2357
+ writeLine(io.stderr, message);
2358
+ commandExitState.exitCode = 1;
2359
+ }
2360
+ });
2361
+ catalogStore
2362
+ .command('prune')
2363
+ .option('--dry-run', 'inspect reclaimable items without deleting them', false)
2364
+ .option('-y, --yes', 'delete items classified as safely reclaimable', false)
2365
+ .option('-j, --json', 'print the prune receipt as JSON', false)
2366
+ .action(async (rawOptions) => {
2367
+ const dryRun = rawOptions.yes !== true;
2368
+ try {
2369
+ if (rawOptions.dryRun === true && rawOptions.yes === true) {
2370
+ throw Object.assign(new Error('catalog store prune cannot combine `--dry-run` and `--yes`.'), {
2371
+ code: 'invalidArguments',
2372
+ });
2373
+ }
2374
+ const result = await catalogStorePrune({ dryRun, env });
2375
+ if (rawOptions.json) {
2376
+ writeJsonReceipt(io.stdout, result);
2377
+ }
2378
+ else if (dryRun) {
2379
+ writeLine(io.stdout, `Dry run: ${result.entries.filter((entry) => entry.status === 'reclaimable').length} item(s) are reclaimable.`);
2380
+ writeLine(io.stdout, 'Run `aw catalog store prune --yes` to delete them.');
2381
+ if (result.errors.length > 0)
2382
+ writeLine(io.stderr, `${result.errors.length} item(s) could not be fully inspected and were retained.`);
2383
+ }
2384
+ else {
2385
+ writeLine(io.stdout, `Deleted ${result.deleted.length} Catalog store item(s) (${result.deletedBytes} bytes).`);
2386
+ if (result.errors.length > 0)
2387
+ writeLine(io.stderr, `${result.errors.length} item(s) could not be fully inspected or deleted and were retained.`);
2388
+ }
2389
+ commandExitState.exitCode = result.ok ? 0 : 1;
2390
+ }
2391
+ catch (error) {
2392
+ const failure = sanitizeCatalogSourceError(error, env);
2393
+ const message = redactString(failure.message);
2394
+ const code = catalogStoreErrorCode(error);
2395
+ if (rawOptions.json)
2396
+ writeJsonReceipt(io.stdout, {
2397
+ catalogStoreSchemaVersion: 2,
2398
+ command: 'catalog:store:prune',
2399
+ ok: false,
2400
+ overallState: 'failed',
2401
+ dryRun,
2402
+ entries: [],
2403
+ deleted: [],
2404
+ retained: [],
2405
+ errors: [{ code, message }],
2406
+ reclaimableBytes: null,
2407
+ deletedBytes: 0,
2408
+ });
2409
+ writeLine(io.stderr, message);
2410
+ commandExitState.exitCode = 1;
2411
+ }
2412
+ });
1521
2413
  program
1522
2414
  .command('init')
1523
- .option('-f, --force', 're-fetch and rebuild the current catalog cache entry', false)
1524
2415
  .option('-j, --json', 'print receipt as JSON', false)
1525
2416
  .option('-v, --verbose', 'show command diagnostics', false)
1526
2417
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1528,8 +2419,6 @@ export function buildProgram(options = {}) {
1528
2419
  .action(async (rawOptions) => {
1529
2420
  const context = createActionContext('init', rawOptions);
1530
2421
  const result = await runActionWithContext(context, () => initCommand({
1531
- force: rawOptions.force,
1532
- cliVersion: resolveCliVersion(),
1533
2422
  env: context.env,
1534
2423
  }));
1535
2424
  if (rawOptions.json) {
@@ -1540,7 +2429,6 @@ export function buildProgram(options = {}) {
1540
2429
  writeLine(io.stdout, line);
1541
2430
  }
1542
2431
  }
1543
- writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
1544
2432
  for (const source of result.sources) {
1545
2433
  if (!source.ok && source.error)
1546
2434
  writeLine(io.stderr, `${source.id}: ${source.error}`);
@@ -1548,53 +2436,79 @@ export function buildProgram(options = {}) {
1548
2436
  commandExitState.exitCode = result.ok ? 0 : 1;
1549
2437
  });
1550
2438
  program
1551
- .command('install')
2439
+ .command('sync')
2440
+ .description('Reconcile actual state from existing desired intent')
1552
2441
  .argument('[plugins...]')
1553
- .option('-s, --scope <scope>', 'project or user')
1554
- .option('-p, --platform <platform>', 'codex | claude | both')
1555
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
1556
- .option('-j, --json', 'print receipt as JSON', false)
2442
+ .requiredOption('-s, --scope <scope>', 'project or user')
2443
+ .requiredOption('-p, --platform <platform>', 'codex, claude, or both')
2444
+ .option('-a, --all', 'reconcile every desired plugin in the selected scope', false)
2445
+ .option('--refresh', 'refresh only Catalog sources required by selected intent roots', false)
2446
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2447
+ .option('-y, --yes', 'authorize preApply and shared native replacement or forced repair', false)
2448
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2449
+ .option('-j, --json', 'print sync receipt as JSON', false)
1557
2450
  .option('-v, --verbose', 'show command diagnostics', false)
1558
2451
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1559
2452
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1560
2453
  .action(async (plugins, rawOptions) => {
1561
- const context = createActionContext('install', rawOptions);
1562
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('install', plugins ?? [], rawOptions, context));
1563
- if (outcome.cancelled)
1564
- return;
1565
- const receipt = outcome.result;
2454
+ const context = createActionContext('sync', rawOptions);
2455
+ const normalized = normalizeWriteRequest(() => normalizeInstallCommandOptions({
2456
+ cwd: context.cwd,
2457
+ scope: rawOptions.scope,
2458
+ platform: rawOptions.platform,
2459
+ scriptTimeout: rawOptions.scriptTimeout,
2460
+ allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2461
+ }));
2462
+ const syncPlatform = normalized.platform;
2463
+ const result = await runActionWithContext(context, () => syncCommand(plugins ?? [], {
2464
+ cwd: context.cwd,
2465
+ env: context.env,
2466
+ platform: syncPlatform,
2467
+ scope: normalized.scope,
2468
+ all: rawOptions.all,
2469
+ refresh: rawOptions.refresh,
2470
+ cliVersion: resolveCliVersion(),
2471
+ yes: rawOptions.yes,
2472
+ json: rawOptions.json,
2473
+ interactive: context.interactive && context.isTty && !context.isCi,
2474
+ prompt: context.prompt,
2475
+ allowManagedCodexInstall: rawOptions.allowManagedCodexInstall,
2476
+ scriptTimeoutMs: normalized.scriptTimeoutMs,
2477
+ }, context));
1566
2478
  if (rawOptions.json) {
1567
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('install', receipt));
2479
+ writeJsonReceipt(io.stdout, buildRebuildReceiptJson('sync', result));
1568
2480
  }
1569
2481
  else {
1570
- for (const line of renderRebuildReceipt('install', receipt)) {
2482
+ for (const line of renderRebuildReceipt('sync', result))
1571
2483
  writeLine(io.stdout, line);
1572
- }
1573
2484
  }
1574
- writeWarnings(io.stderr, receipt.warnings, { actionableOnly: true });
1575
- commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
2485
+ writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
2486
+ commandExitState.exitCode = resolveCommandReceiptExitCode(result);
1576
2487
  });
1577
2488
  program
1578
- .command('uninstall')
2489
+ .command('install')
1579
2490
  .argument('[plugins...]')
1580
2491
  .option('-s, --scope <scope>', 'project or user')
1581
2492
  .option('-p, --platform <platform>', 'codex | claude | both')
1582
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
2493
+ .option('--dependency-scope <pluginId[#platform]=scope>', 'place one dependency at project or user scope; repeat for more dependencies', (value, previous) => [...previous, value], [])
2494
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2495
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2496
+ .option('-y, --yes', 'authorize preApply and shared Codex native replacement', false)
1583
2497
  .option('-j, --json', 'print receipt as JSON', false)
1584
2498
  .option('-v, --verbose', 'show command diagnostics', false)
1585
2499
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1586
2500
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1587
2501
  .action(async (plugins, rawOptions) => {
1588
- const context = createActionContext('uninstall', rawOptions);
1589
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('uninstall', plugins ?? [], rawOptions, context));
2502
+ const context = createActionContext('install', rawOptions);
2503
+ const outcome = await runActionWithContext(context, () => runWriteCommandAction('install', plugins ?? [], rawOptions, context));
1590
2504
  if (outcome.cancelled)
1591
2505
  return;
1592
2506
  const receipt = outcome.result;
1593
2507
  if (rawOptions.json) {
1594
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('uninstall', receipt));
2508
+ writeJsonReceipt(io.stdout, buildRebuildReceiptJson('install', receipt));
1595
2509
  }
1596
2510
  else {
1597
- for (const line of renderRebuildReceipt('uninstall', receipt)) {
2511
+ for (const line of renderRebuildReceipt('install', receipt)) {
1598
2512
  writeLine(io.stdout, line);
1599
2513
  }
1600
2514
  }
@@ -1602,27 +2516,26 @@ export function buildProgram(options = {}) {
1602
2516
  commandExitState.exitCode = resolveCommandReceiptExitCode(receipt);
1603
2517
  });
1604
2518
  program
1605
- .command('upgrade')
2519
+ .command('uninstall')
1606
2520
  .argument('[plugins...]')
1607
- .option('-a, --all', 'upgrade all plugins', false)
1608
2521
  .option('-s, --scope <scope>', 'project or user')
1609
2522
  .option('-p, --platform <platform>', 'codex | claude | both')
1610
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
2523
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
1611
2524
  .option('-j, --json', 'print receipt as JSON', false)
1612
2525
  .option('-v, --verbose', 'show command diagnostics', false)
1613
2526
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1614
2527
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1615
2528
  .action(async (plugins, rawOptions) => {
1616
- const context = createActionContext('upgrade', rawOptions);
1617
- const outcome = await runActionWithContext(context, () => runWriteCommandAction('upgrade', plugins ?? [], rawOptions, context));
2529
+ const context = createActionContext('uninstall', rawOptions);
2530
+ const outcome = await runActionWithContext(context, () => runWriteCommandAction('uninstall', plugins ?? [], rawOptions, context));
1618
2531
  if (outcome.cancelled)
1619
2532
  return;
1620
2533
  const receipt = outcome.result;
1621
2534
  if (rawOptions.json) {
1622
- writeJsonReceipt(io.stdout, buildRebuildReceiptJson('upgrade', receipt));
2535
+ writeJsonReceipt(io.stdout, buildRebuildReceiptJson('uninstall', receipt));
1623
2536
  }
1624
2537
  else {
1625
- for (const line of renderRebuildReceipt('upgrade', receipt)) {
2538
+ for (const line of renderRebuildReceipt('uninstall', receipt)) {
1626
2539
  writeLine(io.stdout, line);
1627
2540
  }
1628
2541
  }
@@ -1635,7 +2548,9 @@ export function buildProgram(options = {}) {
1635
2548
  .option('-s, --scope <scope>', 'project or user')
1636
2549
  .option('-p, --platform <platform>', 'codex | claude | both')
1637
2550
  .option('-j, --json', 'print cleanup result as JSON', false)
1638
- .option('--force', 'remove exclusively owned plugin output without dependency protection', false)
2551
+ .option('--force', 'use exact shallow evidence for exclusively owned damaged output; preserve foreign/shared content; no deleted-plugin-wide cleanup', false)
2552
+ .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)
2553
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
1639
2554
  .option('-v, --verbose', 'show command diagnostics', false)
1640
2555
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1641
2556
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
@@ -1654,14 +2569,16 @@ export function buildProgram(options = {}) {
1654
2569
  }
1655
2570
  }
1656
2571
  writeWarnings(io.stderr, result.warnings, { actionableOnly: true });
1657
- commandExitState.exitCode = result.overallState === 'failed' ? 1 : 0;
2572
+ commandExitState.exitCode = resolveCommandReceiptExitCode(result);
1658
2573
  });
1659
2574
  program
1660
2575
  .command('reinstall')
1661
2576
  .argument('[plugins...]')
1662
2577
  .option('-s, --scope <scope>', 'project or user')
1663
2578
  .option('-p, --platform <platform>', 'codex | claude | both')
1664
- .option('--script-timeout <ms>', 'override plugin lifecycle script timeout in milliseconds')
2579
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2580
+ .option('--allow-managed-codex-install', 'allow this command to acquire or rebuild the pinned AW-managed Codex runtime', false)
2581
+ .option('-y, --yes', 'authorize preApply and shared Codex native replacement or forced reinstall', false)
1665
2582
  .option('-j, --json', 'print receipt as JSON', false)
1666
2583
  .option('-v, --verbose', 'show command diagnostics', false)
1667
2584
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1691,7 +2608,6 @@ export function buildProgram(options = {}) {
1691
2608
  .command('list')
1692
2609
  .argument('[plugin]')
1693
2610
  .option('-s, --scope <scope>', 'read only project or user config')
1694
- .option('--local', 'read only the current project local override layer', false)
1695
2611
  .option('-j, --json', 'print config as JSON', false)
1696
2612
  .option('-v, --verbose', 'show command diagnostics', false)
1697
2613
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1699,9 +2615,7 @@ export function buildProgram(options = {}) {
1699
2615
  .action(async (pluginName, rawOptions) => {
1700
2616
  const context = createActionContext('config:list', rawOptions);
1701
2617
  const outcome = await runActionWithContext(context, async () => {
1702
- resolveConfigProjectLayer(rawOptions);
1703
- if (!rawOptions.local)
1704
- resolveConfigReadResolution(rawOptions.scope);
2618
+ resolveConfigReadResolution(rawOptions.scope);
1705
2619
  const execute = async (selectedPlugin) => resolveMultiSourceConfigRead({
1706
2620
  selector: selectedPlugin,
1707
2621
  rawOptions,
@@ -1726,7 +2640,6 @@ export function buildProgram(options = {}) {
1726
2640
  command: 'list',
1727
2641
  plugin: selection.plugins[0],
1728
2642
  scope: rawOptions.scope,
1729
- projectLayer: rawOptions.local ? 'local' : 'shared',
1730
2643
  })),
1731
2644
  execute: (selection) => execute(selection.plugins[0]),
1732
2645
  });
@@ -1748,8 +2661,7 @@ export function buildProgram(options = {}) {
1748
2661
  pluginId: resolved.pluginId,
1749
2662
  sourceId: resolved.sourceId,
1750
2663
  sourceIdentity: resolved.sourceIdentity,
1751
- scope: rawOptions.local ? 'project' : rawOptions.scope ?? 'effective',
1752
- projectLayer: rawOptions.local ? 'local' : 'shared',
2664
+ scope: rawOptions.scope ?? 'effective',
1753
2665
  ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
1754
2666
  fields: resolved.result.fields,
1755
2667
  });
@@ -1763,7 +2675,6 @@ export function buildProgram(options = {}) {
1763
2675
  .argument('[plugin]')
1764
2676
  .argument('[key]')
1765
2677
  .option('-s, --scope <scope>', 'read only project or user config')
1766
- .option('--local', 'read only the current project local override layer', false)
1767
2678
  .option('-j, --json', 'print config as JSON', false)
1768
2679
  .option('-v, --verbose', 'show command diagnostics', false)
1769
2680
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1771,9 +2682,7 @@ export function buildProgram(options = {}) {
1771
2682
  .action(async (pluginName, key, rawOptions) => {
1772
2683
  const context = createActionContext('config:get', rawOptions);
1773
2684
  const outcome = await runActionWithContext(context, async () => {
1774
- resolveConfigProjectLayer(rawOptions);
1775
- if (!rawOptions.local)
1776
- resolveConfigReadResolution(rawOptions.scope);
2685
+ resolveConfigReadResolution(rawOptions.scope);
1777
2686
  const execute = async (selectedPlugin) => resolveMultiSourceConfigRead({
1778
2687
  selector: selectedPlugin,
1779
2688
  ...(key === undefined ? {} : { key }),
@@ -1800,7 +2709,6 @@ export function buildProgram(options = {}) {
1800
2709
  plugin: selection.plugins[0],
1801
2710
  key,
1802
2711
  scope: rawOptions.scope,
1803
- projectLayer: rawOptions.local ? 'local' : 'shared',
1804
2712
  })),
1805
2713
  execute: (selection) => execute(selection.plugins[0]),
1806
2714
  });
@@ -1823,8 +2731,7 @@ export function buildProgram(options = {}) {
1823
2731
  sourceId: resolved.sourceId,
1824
2732
  sourceIdentity: resolved.sourceIdentity,
1825
2733
  key,
1826
- scope: rawOptions.local ? 'project' : rawOptions.scope ?? 'effective',
1827
- projectLayer: rawOptions.local ? 'local' : 'shared',
2734
+ scope: rawOptions.scope ?? 'effective',
1828
2735
  ...(resolved.result.schema ? { schema: resolved.result.schema } : {}),
1829
2736
  fields: resolved.result.fields,
1830
2737
  });
@@ -1836,47 +2743,119 @@ export function buildProgram(options = {}) {
1836
2743
  config
1837
2744
  .command('set')
1838
2745
  .argument('[plugin]')
1839
- .argument('[key]')
1840
- .argument('[value]')
2746
+ .argument('[field]')
1841
2747
  .option('-s, --scope <scope>', 'project or user')
1842
- .option('--local', 'write only the current project local override layer', false)
1843
- .option('--value-json', 'parse value as a JSON literal', false)
2748
+ .option('--value-json <json>', 'set the field to an exact JSON value')
2749
+ .option('--value-stdin', 'read the exact string value from stdin until EOF', false)
2750
+ .option('--preset <id>', 'set the field to the current manifest preset value')
2751
+ .option('--apply', 'apply the saved config to visible desired placements', false)
2752
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2753
+ .option('-y, --yes', 'confirm preApply and shared Codex native config apply', false)
1844
2754
  .option('-j, --json', 'print receipt as JSON', false)
1845
2755
  .option('-v, --verbose', 'show command diagnostics', false)
1846
2756
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
1847
2757
  .option('--log-file <path>', 'write debug diagnostics to the specified file')
1848
- .action(async (pluginName, key, rawValue, rawOptions) => {
2758
+ .action(async (pluginName, key, rawOptions) => {
2759
+ if (!pluginName || !key)
2760
+ throw new Error(configCommandUsage('set'));
1849
2761
  const context = createActionContext('config:set', rawOptions);
1850
2762
  let outcome;
1851
2763
  try {
1852
2764
  outcome = await runActionWithContext(context, async () => {
1853
2765
  const scope = resolveConfigWriteScope(rawOptions.scope);
1854
- const projectLayer = resolveConfigProjectLayer(rawOptions);
1855
- const execute = async (selectedPlugin, selectedKey, inputValue) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken) => {
1856
- if (!plugin.manifest.config?.schema) {
1857
- throw new Error(`configSchemaUnavailable: Plugin "${selectedPlugin}" does not declare an AW config schema.`);
2766
+ const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2767
+ const explicitValueCount = [
2768
+ rawOptions.valueJson !== undefined,
2769
+ rawOptions.valueStdin === true,
2770
+ rawOptions.preset !== undefined,
2771
+ ].filter(Boolean).length;
2772
+ if (explicitValueCount > 1
2773
+ || (explicitValueCount === 0
2774
+ && !(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true))) {
2775
+ throw new Error('Use exactly one of --value-json <json>, --value-stdin, or --preset <id>.');
2776
+ }
2777
+ return withResolvedConfigMutationPlugin(context, pluginName, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
2778
+ if (!plugin.manifest.config?.fields) {
2779
+ throw new Error(`configSchemaUnavailable: Plugin "${pluginName}" does not declare an AW config schema.`);
1858
2780
  }
1859
- if (!plugin.manifest.config.schema[selectedKey]) {
1860
- throw new Error(`Unknown config key "${selectedKey}".`);
2781
+ const field = plugin.manifest.config.fields[key];
2782
+ if (!field) {
2783
+ throw new Error(`Unknown config key "${key}".`);
2784
+ }
2785
+ const sensitive = field.sensitive === true;
2786
+ if (sensitive && scope !== 'user') {
2787
+ const error = new Error('sensitiveProjectConfigUnsupported: Sensitive config can only be set in user scope.');
2788
+ error.code = 'sensitiveProjectConfigUnsupported';
2789
+ throw error;
2790
+ }
2791
+ if (sensitive && (rawOptions.valueJson !== undefined || rawOptions.preset !== undefined)) {
2792
+ const error = new Error('sensitiveConfigInputUnsupported: Sensitive config cannot use --value-json or --preset.');
2793
+ error.code = 'sensitiveConfigInputUnsupported';
2794
+ throw error;
2795
+ }
2796
+ if (!sensitive && explicitValueCount !== 1) {
2797
+ throw new Error('Use exactly one of --value-json <json>, --value-stdin, or --preset <id>.');
2798
+ }
2799
+ let inputValue;
2800
+ if (rawOptions.valueStdin === true) {
2801
+ inputValue = await readUtf8Stdin(context.stdin);
2802
+ }
2803
+ else if (sensitive) {
2804
+ if (!(context.interactive && context.isTty && !context.isCi && rawOptions.json !== true)
2805
+ || !context.prompt.password) {
2806
+ const error = new Error('sensitiveConfigStdinRequired: Use --value-stdin for sensitive config outside an interactive terminal.');
2807
+ error.code = 'sensitiveConfigStdinRequired';
2808
+ throw error;
2809
+ }
2810
+ const response = await context.prompt.password({
2811
+ message: `输入 ${field.title ?? key}`,
2812
+ mask: true,
2813
+ });
2814
+ if (response.cancelled || response.action !== 'select' || response.value === undefined) {
2815
+ const error = new Error('sensitiveConfigInputCancelled: Sensitive config input was cancelled.');
2816
+ error.code = 'sensitiveConfigInputCancelled';
2817
+ throw error;
2818
+ }
2819
+ inputValue = response.value;
2820
+ }
2821
+ else if (rawOptions.valueJson !== undefined) {
2822
+ try {
2823
+ inputValue = JSON.parse(rawOptions.valueJson);
2824
+ }
2825
+ catch {
2826
+ throw new Error('--value-json must contain valid JSON.');
2827
+ }
2828
+ }
2829
+ else {
2830
+ const preset = field.choices?.presets.find((entry) => entry.id === rawOptions.preset);
2831
+ if (!preset)
2832
+ throw new Error(`Unknown preset "${rawOptions.preset}" for config key "${key}".`);
2833
+ inputValue = structuredClone(preset.value);
1861
2834
  }
1862
2835
  const writeReceipt = await applyPluginConfigWrite({
1863
2836
  plugin,
1864
2837
  pluginName: plugin.manifest.name,
1865
2838
  scope,
1866
- projectLayer,
1867
2839
  cwd: context.cwd,
1868
2840
  env: context.env,
1869
2841
  nativeCliRunner: context.nativeCliRunner,
1870
- rebuildRuntime: { ...context, awHomeMutationToken },
2842
+ rebuildRuntime: {
2843
+ ...context,
2844
+ awHomeMutationToken,
2845
+ catalogOperationContext,
2846
+ expectedGenerationSetDigest: catalogOperationContext.generationSetDigest,
2847
+ ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }),
2848
+ },
1871
2849
  awHomeMutationToken,
1872
2850
  request: {
1873
- updates: { [selectedKey]: inputValue },
2851
+ updates: { [key]: inputValue },
1874
2852
  },
2853
+ apply: rawOptions.apply === true,
2854
+ sharedNativeApplyConfirmed: rawOptions.yes === true,
2855
+ preApplyConfirmed: rawOptions.yes === true,
1875
2856
  });
1876
- return {
2857
+ return buildConfigMutationReceipt('config:set', {
1877
2858
  ok: writeReceipt.overallState === 'succeeded',
1878
- receiptSchemaVersion: 4,
1879
- command: 'config:set',
1880
2859
  ...writeReceipt,
1881
2860
  plugin: plugin.manifest.name,
1882
2861
  pluginName: plugin.manifest.name,
@@ -1884,104 +2863,39 @@ export function buildProgram(options = {}) {
1884
2863
  sourceId: source.sourceId,
1885
2864
  sourceIdentity: source.sourceIdentity,
1886
2865
  affectedFields: [{
1887
- key: selectedKey,
1888
- title: plugin?.manifest.config?.schema?.[selectedKey]?.title ?? selectedKey,
2866
+ key,
2867
+ title: field.title ?? key,
1889
2868
  kind: 'manifest',
1890
2869
  reserved: false,
1891
2870
  action: 'set',
1892
2871
  scope,
1893
- ...(scope === 'project' ? { projectLayer } : {}),
1894
2872
  }],
1895
- };
1896
- });
1897
- if (pluginName && key && rawValue !== undefined) {
1898
- const inputValue = parseConfigValue(rawValue, rawOptions.valueJson === true);
1899
- return {
1900
- cancelled: false,
1901
- key,
1902
- value: await execute(pluginName, key, inputValue),
1903
- };
1904
- }
1905
- if (!isWizardAvailable(context, rawOptions))
1906
- throw new Error(configCommandUsage('set'));
1907
- const candidates = await loadWizardCandidates(context, {});
1908
- if (pluginName) {
1909
- const candidate = candidates.find((entry) => entry.pluginName === pluginName);
1910
- if (!candidate)
1911
- throw new Error(`Unknown plugin "${pluginName}".`);
1912
- if (!hasConfigSchemaKeys(candidate)) {
1913
- throw new Error(`configSchemaUnavailable: Plugin "${pluginName}" does not declare an AW config schema.`);
1914
- }
1915
- if (key && !candidate.configSchema?.[key])
1916
- throw new Error(`Unknown config key "${key}".`);
1917
- }
1918
- const configurableCandidates = candidates.filter(hasConfigSchemaKeys);
1919
- if (configurableCandidates.length === 0) {
1920
- throw new Error('configSchemaUnavailable: No current plugin declares an AW config schema.');
1921
- }
1922
- const result = await runCommandWizard(context, {
1923
- command: 'config:set',
1924
- candidates: configurableCandidates,
1925
- initial: {
1926
- plugins: pluginName ? [pluginName] : [],
1927
- key,
1928
- ...(rawValue !== undefined ? { value: rawValue } : {}),
1929
- scope,
1930
- projectLayer,
1931
- },
1932
- pluginMode: pluginName ? 'none' : 'single',
1933
- valueJson: rawOptions.valueJson === true,
1934
- promptForKey: key === undefined,
1935
- promptForValue: rawValue === undefined,
1936
- ...equivalentConfigCommandProjection((selection) => configSetEquivalentOptions(selection, candidates, rawOptions)),
1937
- commandIsTemplate: (selection) => candidates.find((entry) => entry.pluginName === selection.plugins[0])?.configSchema?.[selection.key ?? ''] !== undefined
1938
- && configFieldPlaceholder(candidates.find((entry) => entry.pluginName === selection.plugins[0])?.configSchema?.[selection.key ?? '']) !== undefined,
1939
- resultState: overallResultState,
1940
- execute: async (selection) => {
1941
- const selectedPlugin = selection.plugins[0];
1942
- const selectedKey = selection.key;
1943
- const schema = candidates.find((entry) => entry.pluginName === selectedPlugin).configSchema;
1944
- const updates = await coercePluginConfigUpdates({
1945
- schema,
1946
- updates: {
1947
- [selectedKey]: parseConfigValue(selection.value, rawOptions.valueJson === true),
1948
- },
1949
- cwd: context.cwd,
1950
- });
1951
- return execute(selectedPlugin, selectedKey, updates[selectedKey]);
1952
- },
2873
+ });
1953
2874
  });
1954
- return result.cancelled
1955
- ? { cancelled: true }
1956
- : { cancelled: false, key: result.selection.key, value: result.value };
1957
2875
  });
1958
2876
  }
1959
2877
  catch (error) {
1960
2878
  writeConfigMutationFailure('config:set', pluginName, rawOptions.json === true, error);
1961
2879
  return;
1962
2880
  }
1963
- if (outcome.cancelled)
1964
- return;
1965
- const receipt = outcome.value;
1966
- const selectedKey = outcome.key;
2881
+ const receipt = outcome;
1967
2882
  if (rawOptions.json) {
1968
2883
  writeJsonReceipt(io.stdout, receipt);
1969
2884
  }
1970
2885
  else {
1971
- writeLine(io.stdout, `已写入 ${receipt.plugin}.${selectedKey}(${configLayerLabel(receipt.scope, receipt.projectLayer)}配置)。`);
1972
- if (receipt.projectLayer === 'local') {
1973
- writeProjectLocalConfigNotice(io.stdout);
1974
- }
2886
+ writeLine(io.stdout, `已写入 ${receipt.plugin}.${key}(${configLayerLabel(receipt.scope)}配置)。`);
1975
2887
  writeConfigApplyNotice(io.stderr, receipt);
1976
2888
  }
1977
- commandExitState.exitCode = receipt.overallState === 'failed' ? 1 : 0;
2889
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
1978
2890
  });
1979
2891
  config
1980
2892
  .command('unset')
1981
2893
  .argument('[plugin]')
1982
2894
  .argument('[key]')
1983
2895
  .option('-s, --scope <scope>', 'project or user')
1984
- .option('--local', 'write only the current project local override layer', false)
2896
+ .option('--apply', 'apply the saved config to visible desired placements', false)
2897
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
2898
+ .option('-y, --yes', 'confirm preApply and shared Codex native config apply', false)
1985
2899
  .option('-j, --json', 'print receipt as JSON', false)
1986
2900
  .option('-v, --verbose', 'show command diagnostics', false)
1987
2901
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -1992,19 +2906,17 @@ export function buildProgram(options = {}) {
1992
2906
  try {
1993
2907
  outcome = await runActionWithContext(context, async () => {
1994
2908
  const scope = resolveConfigWriteScope(rawOptions.scope);
1995
- const projectLayer = resolveConfigProjectLayer(rawOptions);
1996
- const execute = async (selectedPlugin, selectedKey) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken) => {
1997
- if (plugin.manifest.config?.schema && !plugin.manifest.config.schema[selectedKey]) {
1998
- throw new Error(`Unknown config key "${selectedKey}".`);
1999
- }
2909
+ const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2910
+ const execute = async (selectedPlugin, selectedKey, confirmed) => withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken, catalogOperationContext) => {
2000
2911
  const store = await readPluginConfigStore({
2001
2912
  sourceId: source.sourceId,
2002
2913
  sourceIdentity: source.sourceIdentity,
2914
+ logicalIdentity: source.logicalIdentity,
2003
2915
  pluginName: plugin.manifest.name,
2004
2916
  scope,
2005
- ...(scope === 'project' ? { projectLayer } : {}),
2006
2917
  cwd: context.cwd,
2007
2918
  env: context.env,
2919
+ schema: plugin.manifest.config?.fields,
2008
2920
  });
2009
2921
  const existed = Object.hasOwn(store.values, selectedKey);
2010
2922
  if (!existed)
@@ -2013,20 +2925,26 @@ export function buildProgram(options = {}) {
2013
2925
  plugin,
2014
2926
  pluginName: plugin.manifest.name,
2015
2927
  scope,
2016
- projectLayer,
2017
2928
  cwd: context.cwd,
2018
2929
  env: context.env,
2019
2930
  nativeCliRunner: context.nativeCliRunner,
2020
- rebuildRuntime: { ...context, awHomeMutationToken },
2931
+ rebuildRuntime: {
2932
+ ...context,
2933
+ awHomeMutationToken,
2934
+ catalogOperationContext,
2935
+ expectedGenerationSetDigest: catalogOperationContext.generationSetDigest,
2936
+ ...(scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs }),
2937
+ },
2021
2938
  awHomeMutationToken,
2022
2939
  request: {
2023
2940
  removeKeys: [selectedKey],
2024
2941
  },
2942
+ apply: rawOptions.apply === true,
2943
+ sharedNativeApplyConfirmed: confirmed,
2944
+ preApplyConfirmed: rawOptions.yes === true,
2025
2945
  });
2026
- return {
2946
+ return buildConfigMutationReceipt('config:unset', {
2027
2947
  ok: writeReceipt.overallState === 'succeeded',
2028
- receiptSchemaVersion: 4,
2029
- command: 'config:unset',
2030
2948
  ...writeReceipt,
2031
2949
  plugin: plugin.manifest.name,
2032
2950
  pluginName: plugin.manifest.name,
@@ -2037,17 +2955,20 @@ export function buildProgram(options = {}) {
2037
2955
  existed,
2038
2956
  affectedFields: [{
2039
2957
  key: selectedKey,
2040
- title: plugin?.manifest.config?.schema?.[selectedKey]?.title ?? selectedKey,
2958
+ title: plugin?.manifest.config?.fields?.[selectedKey]?.title ?? selectedKey,
2041
2959
  kind: 'manifest',
2042
2960
  reserved: false,
2043
2961
  action: 'unset',
2044
2962
  scope,
2045
- ...(scope === 'project' ? { projectLayer } : {}),
2963
+ ...(!plugin.manifest.config?.fields?.[selectedKey] ? { stale: true } : {}),
2046
2964
  }],
2047
- };
2965
+ });
2048
2966
  });
2049
2967
  if (pluginName && key) {
2050
- return { cancelled: false, value: await execute(pluginName, key) };
2968
+ return {
2969
+ cancelled: false,
2970
+ value: await execute(pluginName, key, rawOptions.yes === true),
2971
+ };
2051
2972
  }
2052
2973
  if (!isWizardAvailable(context, rawOptions))
2053
2974
  throw new Error(configCommandUsage('unset'));
@@ -2063,7 +2984,7 @@ export function buildProgram(options = {}) {
2063
2984
  const result = await runCommandWizard(context, {
2064
2985
  command: 'config:unset',
2065
2986
  candidates,
2066
- initial: { plugins: pluginName ? [pluginName] : [], key, scope, projectLayer },
2987
+ initial: { plugins: pluginName ? [pluginName] : [], key, scope },
2067
2988
  pluginMode: pluginName ? 'none' : 'single',
2068
2989
  promptForKey: key === undefined,
2069
2990
  destructive: true,
@@ -2072,10 +2993,9 @@ export function buildProgram(options = {}) {
2072
2993
  plugin: selection.plugins[0],
2073
2994
  key: selection.key,
2074
2995
  scope: selection.scope ?? 'user',
2075
- projectLayer: selection.projectLayer,
2076
2996
  })),
2077
2997
  resultState: overallResultState,
2078
- execute: (selection) => execute(selection.plugins[0], selection.key),
2998
+ execute: (selection) => execute(selection.plugins[0], selection.key, true),
2079
2999
  });
2080
3000
  return result.cancelled
2081
3001
  ? { cancelled: true }
@@ -2094,21 +3014,19 @@ export function buildProgram(options = {}) {
2094
3014
  }
2095
3015
  else {
2096
3016
  writeLine(io.stdout, receipt.existed
2097
- ? `已删除 ${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope, receipt.projectLayer)}配置)。`
2098
- : `${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope, receipt.projectLayer)}配置)原本不存在。`);
2099
- if (receipt.projectLayer === 'local') {
2100
- writeProjectLocalConfigNotice(io.stdout);
2101
- }
3017
+ ? `已删除 ${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope)}配置)。`
3018
+ : `${receipt.plugin}.${receipt.removedKey}(${configLayerLabel(receipt.scope)}配置)原本不存在。`);
2102
3019
  writeConfigApplyNotice(io.stderr, receipt);
2103
3020
  }
2104
- commandExitState.exitCode = receipt.overallState === 'failed' ? 1 : 0;
3021
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2105
3022
  });
2106
3023
  config
2107
3024
  .command('reset')
2108
3025
  .argument('[plugin]')
2109
3026
  .option('-s, --scope <scope>', 'project or user')
2110
- .option('--local', 'reset only the current project local override layer', false)
2111
- .option('-y, --yes', 'confirm destructive reset', false)
3027
+ .option('--apply', 'apply the saved config to visible desired placements', false)
3028
+ .option('--script-timeout <ms>', 'override plugin preApply script timeout in milliseconds')
3029
+ .option('-y, --yes', 'confirm destructive reset and preApply/shared Codex native config apply', false)
2112
3030
  .option('-j, --json', 'print receipt as JSON', false)
2113
3031
  .option('-v, --verbose', 'show command diagnostics', false)
2114
3032
  .option('-D, --debug', 'write debug diagnostics to a log file', false)
@@ -2118,61 +3036,79 @@ export function buildProgram(options = {}) {
2118
3036
  let outcome;
2119
3037
  try {
2120
3038
  outcome = await runActionWithContext(context, async () => {
2121
- const execute = async (selectedPlugin, selectedScope, projectLayer, confirmed) => {
3039
+ const execute = async (selectedPlugin, selectedScope, confirmed) => {
3040
+ const scriptTimeoutMs = resolveScriptTimeoutMs(rawOptions);
2122
3041
  if (!confirmed) {
2123
- throw new Error(`Reset requires confirmation. Re-run \`aw config reset ${selectedPlugin} -s ${selectedScope}${projectLayer === 'local' ? ' --local' : ''} --yes\` to delete this store.`);
3042
+ throw new Error(`Reset requires confirmation. Re-run \`aw config reset ${selectedPlugin} -s ${selectedScope} --yes\` to delete this store.`);
2124
3043
  }
2125
- return withResolvedConfigMutationPlugin(context, selectedPlugin, async ({ pluginId, source, plugin }, awHomeMutationToken) => {
3044
+ const located = await resolveStoredConfigResetIdentity({
3045
+ selector: selectedPlugin,
3046
+ context,
3047
+ scope: selectedScope,
3048
+ });
3049
+ const resetLocatedStore = async (plugin, pluginError) => {
2126
3050
  const affectedFields = await describeResetAffectedFields({
2127
- pluginName: plugin.manifest.name,
3051
+ sourceId: located.sourceId,
3052
+ sourceIdentity: located.sourceIdentity,
3053
+ logicalIdentity: located.logicalIdentity,
3054
+ pluginName: located.pluginName,
2128
3055
  plugin,
3056
+ pluginError,
2129
3057
  scope: selectedScope,
2130
- projectLayer,
2131
3058
  cwd: context.cwd,
2132
3059
  env: context.env,
2133
- });
3060
+ }).catch(() => []);
2134
3061
  const resetReceipt = await resetPluginConfigWithHelper({
2135
- pluginName: plugin.manifest.name,
3062
+ sourceId: located.sourceId,
3063
+ sourceIdentity: located.sourceIdentity,
3064
+ logicalIdentity: located.logicalIdentity,
3065
+ pluginName: located.pluginName,
2136
3066
  scope: selectedScope,
2137
- projectLayer,
2138
3067
  cwd: context.cwd,
2139
3068
  env: context.env,
2140
3069
  nativeCliRunner: context.nativeCliRunner,
2141
- rebuildRuntime: { ...context, awHomeMutationToken },
2142
- awHomeMutationToken,
2143
3070
  plugin,
3071
+ pluginError,
3072
+ apply: rawOptions.apply === true,
3073
+ rebuildRuntime: scriptTimeoutMs === undefined ? undefined : { scriptTimeoutMs },
3074
+ sharedNativeApplyConfirmed: confirmed,
3075
+ preApplyConfirmed: rawOptions.yes === true,
2144
3076
  });
2145
- return {
3077
+ return buildConfigMutationReceipt('config:reset', {
2146
3078
  ok: resetReceipt.overallState === 'succeeded',
2147
- receiptSchemaVersion: 4,
2148
- command: 'config:reset',
2149
3079
  ...resetReceipt,
2150
- plugin: plugin.manifest.name,
2151
- pluginName: plugin.manifest.name,
2152
- pluginId,
2153
- sourceId: source.sourceId,
2154
- sourceIdentity: source.sourceIdentity,
3080
+ plugin: located.pluginName,
3081
+ pluginName: located.pluginName,
3082
+ pluginId: located.pluginId,
3083
+ sourceId: located.sourceId,
3084
+ ...(located.sourceIdentity ? { sourceIdentity: located.sourceIdentity } : {}),
2155
3085
  affectedFields,
2156
- };
2157
- }).catch((error) => {
2158
- const message = error instanceof Error ? error.message : String(error);
2159
- if (message.includes('Catalog source')
2160
- || message.startsWith('Cannot resolve required catalog sources:')) {
2161
- throw new Error(`sourceMissing: ${selectedPlugin} source is unavailable; config store was not changed.`);
2162
- }
2163
- if (message.includes('does not exist in the required catalog source set')) {
2164
- throw new Error(`configSchemaUnavailable: Plugin "${selectedPlugin}" is unavailable; config store was not changed.`);
2165
- }
2166
- throw error;
2167
- });
3086
+ });
3087
+ };
3088
+ let resetStarted = false;
3089
+ try {
3090
+ return await withResolvedCatalogPlugin({
3091
+ selector: located.pluginId,
3092
+ cliVersion: resolveCliVersion(),
3093
+ env: context.env,
3094
+ consume: async ({ plugin }) => {
3095
+ resetStarted = true;
3096
+ return resetLocatedStore(plugin, null);
3097
+ },
3098
+ });
3099
+ }
3100
+ catch (error) {
3101
+ if (resetStarted)
3102
+ throw error;
3103
+ return resetLocatedStore(null, error instanceof Error ? error : new Error(String(error)));
3104
+ }
2168
3105
  };
2169
3106
  const scope = resolveConfigWriteScope(rawOptions.scope);
2170
- const projectLayer = resolveConfigProjectLayer(rawOptions);
2171
3107
  const needsWizard = pluginName === undefined || rawOptions.yes !== true;
2172
3108
  if (!needsWizard) {
2173
3109
  return {
2174
3110
  cancelled: false,
2175
- value: await execute(pluginName, scope, projectLayer, true),
3111
+ value: await execute(pluginName, scope, true),
2176
3112
  };
2177
3113
  }
2178
3114
  if (!isWizardAvailable(context, rawOptions)) {
@@ -2180,16 +3116,16 @@ export function buildProgram(options = {}) {
2180
3116
  throw new Error(configCommandUsage('reset'));
2181
3117
  return {
2182
3118
  cancelled: false,
2183
- value: await execute(pluginName, scope, projectLayer, false),
3119
+ value: await execute(pluginName, scope, false),
2184
3120
  };
2185
3121
  }
2186
3122
  const currentCandidates = pluginName ? [] : await loadWizardCandidates(context, {});
2187
3123
  const candidatesByScope = new Map();
2188
3124
  if (!pluginName && (rawOptions.scope === undefined || scope === 'user')) {
2189
- candidatesByScope.set('user', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'user', projectLayer: 'shared' }], { includeEmptyStores: true })));
3125
+ candidatesByScope.set('user', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'user' }], { includeEmptyStores: true })));
2190
3126
  }
2191
3127
  if (!pluginName && (rawOptions.scope === undefined || scope === 'project')) {
2192
- candidatesByScope.set('project', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'project', projectLayer }], { includeEmptyStores: true })));
3128
+ candidatesByScope.set('project', mergeWizardCandidates(currentCandidates, await loadStoredConfigWizardCandidates(context, [{ scope: 'project' }], { includeEmptyStores: true })));
2193
3129
  }
2194
3130
  const candidates = mergeWizardCandidates(candidatesByScope.get('user') ?? [], candidatesByScope.get('project') ?? []);
2195
3131
  if (!pluginName && candidates.length === 0) {
@@ -2198,7 +3134,7 @@ export function buildProgram(options = {}) {
2198
3134
  const result = await runCommandWizard(context, {
2199
3135
  command: 'config:reset',
2200
3136
  candidates,
2201
- initial: { plugins: pluginName ? [pluginName] : [], scope, projectLayer },
3137
+ initial: { plugins: pluginName ? [pluginName] : [], scope },
2202
3138
  pluginMode: pluginName ? 'none' : 'single',
2203
3139
  promptForScope: rawOptions.scope === undefined,
2204
3140
  destructive: true,
@@ -2209,11 +3145,10 @@ export function buildProgram(options = {}) {
2209
3145
  command: 'reset',
2210
3146
  plugin: selection.plugins[0],
2211
3147
  scope: selection.scope ?? 'user',
2212
- projectLayer: selection.projectLayer,
2213
3148
  confirmed: true,
2214
3149
  })),
2215
3150
  resultState: overallResultState,
2216
- execute: (selection) => execute(selection.plugins[0], selection.scope ?? 'user', selection.projectLayer ?? 'shared', true),
3151
+ execute: (selection) => execute(selection.plugins[0], selection.scope ?? 'user', true),
2217
3152
  });
2218
3153
  return result.cancelled
2219
3154
  ? { cancelled: true }
@@ -2232,14 +3167,11 @@ export function buildProgram(options = {}) {
2232
3167
  }
2233
3168
  else {
2234
3169
  writeLine(io.stdout, receipt.deleted
2235
- ? `已重置 ${receipt.plugin} 的${configLayerLabel(receipt.scope, receipt.projectLayer)}配置。`
2236
- : `${receipt.plugin} 没有可重置的${configLayerLabel(receipt.scope, receipt.projectLayer)}配置。`);
2237
- if (receipt.projectLayer === 'local') {
2238
- writeProjectLocalConfigNotice(io.stdout);
2239
- }
3170
+ ? `已重置 ${receipt.plugin} 的${configLayerLabel(receipt.scope)}配置。`
3171
+ : `${receipt.plugin} 没有可重置的${configLayerLabel(receipt.scope)}配置。`);
2240
3172
  writeConfigApplyNotice(io.stderr, receipt);
2241
3173
  }
2242
- commandExitState.exitCode = receipt.overallState === 'failed' ? 1 : 0;
3174
+ commandExitState.exitCode = receipt.overallState === 'succeeded' ? 0 : 1;
2243
3175
  });
2244
3176
  program
2245
3177
  .command('status')
@@ -2252,7 +3184,7 @@ export function buildProgram(options = {}) {
2252
3184
  .option('-j, --json', 'print status as JSON', false)
2253
3185
  .action(async (plugins, rawOptions) => {
2254
3186
  const context = createActionContext('status', rawOptions);
2255
- const result = await runActionWithContext(context, () => statusCommandV4({
3187
+ const result = await runActionWithContext(context, () => statusCommandV8({
2256
3188
  plugins: plugins ?? [],
2257
3189
  cwd,
2258
3190
  env: context.env,
@@ -2265,11 +3197,11 @@ export function buildProgram(options = {}) {
2265
3197
  commandExitState.exitCode = result.hasErrors ? 1 : 0;
2266
3198
  writeLine(io.stdout, result.output);
2267
3199
  for (const source of result.payload.catalog.sources) {
2268
- if (source.error)
3200
+ if (rawOptions.json && source.error)
2269
3201
  writeLine(io.stderr, `${source.id}: ${source.error}`);
2270
3202
  }
2271
3203
  for (const error of result.payload.errors) {
2272
- if (error.code !== 'nativeStateUnreadable' && error.code !== 'stateInventoryFailed') {
3204
+ if (rawOptions.json && error.code !== 'nativeStateUnreadable' && error.code !== 'stateInventoryFailed') {
2273
3205
  writeLine(io.stderr, error.message);
2274
3206
  }
2275
3207
  }
@@ -2294,11 +3226,24 @@ export async function runCli(argv, options = {}) {
2294
3226
  ...createDefaultIo(),
2295
3227
  ...options.io,
2296
3228
  };
3229
+ const reviewCommand = argv[0] === 'review' && ['inspect', 'prepare'].includes(argv[1] ?? '')
3230
+ ? argv[1]
3231
+ : null;
3232
+ const jsonReviewCommand = reviewCommand !== null && argv.includes('--json');
2297
3233
  const jsonAuthoringCommand = argv.includes('--json')
2298
- && ((argv[0] === 'catalog' && ['init', 'version', 'build', 'validate', 'status', 'publish'].includes(argv[1] ?? ''))
2299
- || (argv[0] === 'plugin' && ['build', 'test'].includes(argv[1] ?? '')))
3234
+ && ((argv[0] === 'catalog' && ['init', 'context', 'version', 'build', 'validate', 'status', 'publish'].includes(argv[1] ?? ''))
3235
+ || (argv[0] === 'plugin' && ['build', 'test', 'validate', 'status', 'remove'].includes(argv[1] ?? ''))
3236
+ || reviewCommand !== null)
2300
3237
  ? `${argv[0]}:${argv[1]}`
2301
3238
  : null;
3239
+ const jsonCatalogDevCommand = argv.includes('--json')
3240
+ && argv[0] === 'catalog'
3241
+ && argv[1] === 'dev'
3242
+ && ['activate', 'status', 'deactivate'].includes(argv[2] ?? '')
3243
+ ? argv[2]
3244
+ : null;
3245
+ const jsonWriteRequest = jsonWriteCommandRequest(argv);
3246
+ const jsonConfigMutation = jsonConfigMutationRequest(argv);
2302
3247
  try {
2303
3248
  const authoringScriptByCommand = {
2304
3249
  init: 'init-plugin.mjs',
@@ -2306,7 +3251,6 @@ export async function runCli(argv, options = {}) {
2306
3251
  remove: 'remove-plugin.mjs',
2307
3252
  validate: 'validate-plugin.mjs',
2308
3253
  status: 'plugin-status.mjs',
2309
- 'check-versions': 'check-plugin-versions.mjs',
2310
3254
  };
2311
3255
  const authoringScript = argv[0] === 'plugin'
2312
3256
  ? authoringScriptByCommand[argv[1] ?? '']
@@ -2315,29 +3259,85 @@ export async function runCli(argv, options = {}) {
2315
3259
  : undefined;
2316
3260
  if (authoringScript && !argv.includes('--help') && !argv.includes('-h')) {
2317
3261
  const authoringArgs = argv.slice(2);
3262
+ let authoringEnv = options.env ?? process.env;
2318
3263
  if (argv[0] === 'plugin'
2319
- && argv[1] === 'validate'
2320
- && !authoringArgs.some((token) => token === '--workspace-root' || token.startsWith('--workspace-root='))) {
2321
- authoringArgs.push('--workspace-root', options.cwd ?? process.cwd());
3264
+ && !authoringArgs.some((token) => token === '--plugin-root' || token.startsWith('--plugin-root='))) {
3265
+ let explicitRoot;
3266
+ for (let index = 0; index < authoringArgs.length; index += 1) {
3267
+ const token = authoringArgs[index];
3268
+ if (token.startsWith('--workspace-root='))
3269
+ explicitRoot = token.slice('--workspace-root='.length);
3270
+ if (token === '--workspace-root')
3271
+ explicitRoot = authoringArgs[index + 1];
3272
+ }
3273
+ const originalCwd = options.cwd ?? process.cwd();
3274
+ let discoveryCwd = originalCwd;
3275
+ if (argv[1] === 'validate') {
3276
+ const valueOptions = new Set(['--workspace-root', '--claude-skill-profile', '--version-base']);
3277
+ for (let index = 0; index < authoringArgs.length; index += 1) {
3278
+ const token = authoringArgs[index];
3279
+ if (valueOptions.has(token)) {
3280
+ index += 1;
3281
+ continue;
3282
+ }
3283
+ if (!token.startsWith('-')) {
3284
+ discoveryCwd = path.resolve(originalCwd, token);
3285
+ authoringArgs[index] = discoveryCwd;
3286
+ break;
3287
+ }
3288
+ }
3289
+ }
3290
+ const workspace = await resolveCatalogWorkspaceRoot({
3291
+ cwd: discoveryCwd,
3292
+ ...(explicitRoot === undefined ? {} : { explicitRoot: path.resolve(options.cwd ?? process.cwd(), explicitRoot) }),
3293
+ mode: 'existing',
3294
+ });
3295
+ authoringEnv = {
3296
+ ...authoringEnv,
3297
+ AW_AUTHORING_WORKSPACE_ROOT: workspace.workspaceRoot,
3298
+ AW_AUTHORING_WORKSPACE_ROOT_RESOLVED_FROM: workspace.workspaceRootResolvedFrom,
3299
+ };
3300
+ if (explicitRoot === undefined)
3301
+ authoringArgs.push('--workspace-root', workspace.workspaceRoot);
2322
3302
  }
2323
3303
  return runAuthoringScript({
2324
3304
  scriptName: authoringScript,
2325
3305
  args: authoringArgs,
2326
3306
  cwd: options.cwd ?? process.cwd(),
2327
- env: options.env ?? process.env,
3307
+ env: authoringEnv,
2328
3308
  io,
2329
3309
  });
2330
3310
  }
2331
- if (argv.some((token) => token === '--subdir' || token.startsWith('--subdir='))
3311
+ if (!jsonConfigMutation
3312
+ && argv.some((token) => token === '--subdir' || token.startsWith('--subdir='))
2332
3313
  && argv.includes('--no-subdir')) {
2333
3314
  const message = 'option --subdir cannot be used with option --no-subdir';
2334
3315
  if (argv.includes('--json') && argv[0] === 'catalog' && argv[1] === 'source' && argv[2] === 'update') {
2335
3316
  writeJsonReceipt(io.stdout, {
2336
- receiptSchemaVersion: 4,
3317
+ receiptSchemaVersion: 6,
2337
3318
  command: 'catalog:source:update',
2338
3319
  ok: false,
2339
3320
  changed: false,
2340
3321
  sourceId: argv[3] ?? '',
3322
+ primaryChange: null,
3323
+ references: [],
3324
+ errors: [{ code: 'catalogSourceOptionConflict', message }],
3325
+ });
3326
+ }
3327
+ writeLine(io.stderr, `error: ${message}`);
3328
+ return 1;
3329
+ }
3330
+ if (argv[0] === 'catalog' && argv[1] === 'source' && argv[2] === 'update'
3331
+ && argv.includes('--auto-check') && argv.includes('--no-auto-check')) {
3332
+ const message = 'option --auto-check cannot be used with option --no-auto-check';
3333
+ if (argv.includes('--json') || argv.includes('-j')) {
3334
+ writeJsonReceipt(io.stdout, {
3335
+ receiptSchemaVersion: 6,
3336
+ command: 'catalog:source:update',
3337
+ ok: false,
3338
+ changed: false,
3339
+ sourceId: argv[3] ?? '',
3340
+ primaryChange: null,
2341
3341
  references: [],
2342
3342
  errors: [{ code: 'catalogSourceOptionConflict', message }],
2343
3343
  });
@@ -2365,9 +3365,30 @@ export async function runCli(argv, options = {}) {
2365
3365
  }
2366
3366
  return await runActionWithContext(context, () => runConsoleController(context));
2367
3367
  }
3368
+ let catalogPublicationActionStarted = false;
3369
+ let configMutationActionStarted = false;
3370
+ const programIo = jsonCatalogDevCommand || jsonWriteRequest || jsonReviewCommand
3371
+ ? { ...io, stderr: () => undefined }
3372
+ : jsonConfigMutation
3373
+ ? {
3374
+ ...io,
3375
+ stderr: (text) => {
3376
+ if (configMutationActionStarted)
3377
+ io.stderr(text);
3378
+ },
3379
+ }
3380
+ : jsonAuthoringCommand === 'catalog:publish'
3381
+ ? {
3382
+ ...io,
3383
+ stderr: (text) => {
3384
+ if (catalogPublicationActionStarted)
3385
+ io.stderr(text);
3386
+ },
3387
+ }
3388
+ : io;
2368
3389
  const program = buildProgram({
2369
3390
  ...options,
2370
- io,
3391
+ io: programIo,
2371
3392
  diagnostics: {
2372
3393
  ...options.diagnostics,
2373
3394
  metadata: {
@@ -2376,16 +3397,139 @@ export async function runCli(argv, options = {}) {
2376
3397
  },
2377
3398
  },
2378
3399
  });
3400
+ if (jsonAuthoringCommand === 'catalog:publish') {
3401
+ program.hook('preAction', () => {
3402
+ catalogPublicationActionStarted = true;
3403
+ });
3404
+ }
3405
+ if (jsonConfigMutation) {
3406
+ program.hook('preAction', () => {
3407
+ configMutationActionStarted = true;
3408
+ });
3409
+ }
2379
3410
  await program.parseAsync(['node', 'aw', ...argv]);
2380
3411
  return program.__awCommandExitState?.exitCode ?? 0;
2381
3412
  }
2382
3413
  catch (error) {
2383
- if (jsonAuthoringCommand && error instanceof CommanderError) {
2384
- writeJsonReceipt(io.stdout, {
2385
- command: jsonAuthoringCommand,
2386
- ok: false,
2387
- errors: [{ code: 'invalidArguments', message: error.message }],
3414
+ if (reviewCommand && error instanceof CommanderError) {
3415
+ if (error.exitCode === 0)
3416
+ return 0;
3417
+ if (jsonReviewCommand) {
3418
+ const receipt = reviewCommand === 'inspect'
3419
+ ? createReviewInspectionFailure(new Error(error.message), 'invalidArguments')
3420
+ : createReviewPreparationFailure(error.message);
3421
+ writeJsonReceipt(io.stdout, receipt);
3422
+ }
3423
+ return 2;
3424
+ }
3425
+ if (jsonCatalogDevCommand) {
3426
+ const workspaceRootIndex = argv.findIndex((token) => token === '--workspace-root');
3427
+ const workspaceRootOption = argv.find((token) => token.startsWith('--workspace-root='));
3428
+ const workspaceRoot = workspaceRootOption?.slice('--workspace-root='.length)
3429
+ ?? (workspaceRootIndex >= 0 ? argv[workspaceRootIndex + 1] : undefined);
3430
+ const sourceIdIndex = argv.findIndex((token) => token === '--source-id');
3431
+ const sourceIdOption = argv.find((token) => token.startsWith('--source-id='));
3432
+ const sourceId = sourceIdOption?.slice('--source-id='.length)
3433
+ ?? (sourceIdIndex >= 0 ? argv[sourceIdIndex + 1] : undefined);
3434
+ const failure = error instanceof CommanderError
3435
+ ? { code: 'invalidArguments', message: error.message }
3436
+ : catalogDevSourceError(error);
3437
+ writeJsonReceipt(io.stdout, createCatalogDevFailureReceipt({
3438
+ command: jsonCatalogDevCommand,
3439
+ cwd: options.cwd ?? process.cwd(),
3440
+ workspaceRoot,
3441
+ sourceId,
3442
+ ...failure,
3443
+ }));
3444
+ return 1;
3445
+ }
3446
+ if (jsonWriteRequest) {
3447
+ writeCommandFailureReceipt(io.stdout, jsonWriteRequest, error);
3448
+ return 1;
3449
+ }
3450
+ if (jsonConfigMutation) {
3451
+ if (error instanceof CommanderError && error.exitCode === 0)
3452
+ return 0;
3453
+ const failure = configMutationFailure(error, options.env ?? process.env, options.cwd ?? process.cwd());
3454
+ writeConfigMutationFailureReceipt(io.stdout, error instanceof CommanderError
3455
+ ? { command: jsonConfigMutation.command }
3456
+ : jsonConfigMutation, failure);
3457
+ writeLine(io.stderr, failure.message);
3458
+ return 1;
3459
+ }
3460
+ if (jsonAuthoringCommand === 'catalog:publish' && error instanceof CommanderError) {
3461
+ const failure = catalogPublicationFailure({
3462
+ error: new CatalogPublicationInspectionError('invalidArguments', error.message),
3463
+ env: options.env,
3464
+ sensitiveRoots: [options.cwd ?? process.cwd()],
2388
3465
  });
3466
+ writeJsonReceipt(io.stdout, failure);
3467
+ writeLine(io.stderr, failure.message);
3468
+ return 1;
3469
+ }
3470
+ if (jsonAuthoringCommand && !(error instanceof CommanderError)) {
3471
+ const message = error instanceof Error ? error.message : String(error);
3472
+ if (jsonAuthoringCommand === 'plugin:validate') {
3473
+ writeJsonReceipt(io.stdout, {
3474
+ ok: false,
3475
+ profile: 'unknown',
3476
+ warnings: [],
3477
+ errors: [message],
3478
+ });
3479
+ }
3480
+ else if (jsonAuthoringCommand === 'plugin:status') {
3481
+ writeJsonReceipt(io.stdout, {
3482
+ schemaVersion: 3,
3483
+ policy: 'development',
3484
+ ok: false,
3485
+ base: null,
3486
+ requestedBase: null,
3487
+ baseCommit: null,
3488
+ checkedPlugins: 0,
3489
+ checks: [],
3490
+ workspaceRoot: null,
3491
+ workspaceRootResolvedFrom: null,
3492
+ errors: [{ code: 'workspaceResolutionFailed', message }],
3493
+ });
3494
+ }
3495
+ else if (jsonAuthoringCommand === 'plugin:remove') {
3496
+ writeJsonReceipt(io.stdout, {
3497
+ schemaVersion: 1,
3498
+ command: 'plugin:remove',
3499
+ ok: false,
3500
+ workspaceRoot: null,
3501
+ workspaceRootResolvedFrom: null,
3502
+ baseCommit: null,
3503
+ plugin: null,
3504
+ removed: false,
3505
+ errors: [{ code: 'workspaceResolutionFailed', message }],
3506
+ });
3507
+ }
3508
+ else if (jsonAuthoringCommand === 'review:inspect') {
3509
+ writeJsonReceipt(io.stdout, createReviewInspectionFailure(error));
3510
+ }
3511
+ else if (jsonAuthoringCommand === 'review:prepare') {
3512
+ writeJsonReceipt(io.stdout, createReviewPreparationFailure(message, 'reviewPreparationFailed'));
3513
+ }
3514
+ else {
3515
+ writeJsonReceipt(io.stdout, {
3516
+ command: jsonAuthoringCommand,
3517
+ ok: false,
3518
+ errors: [{ code: 'workspaceResolutionFailed', message }],
3519
+ });
3520
+ }
3521
+ return 1;
3522
+ }
3523
+ if (jsonAuthoringCommand && error instanceof CommanderError) {
3524
+ writeJsonReceipt(io.stdout, jsonAuthoringCommand === 'review:inspect'
3525
+ ? createReviewInspectionFailure(new Error(error.message), 'invalidArguments')
3526
+ : jsonAuthoringCommand === 'review:prepare'
3527
+ ? createReviewPreparationFailure(error.message)
3528
+ : {
3529
+ command: jsonAuthoringCommand,
3530
+ ok: false,
3531
+ errors: [{ code: 'invalidArguments', message: error.message }],
3532
+ });
2389
3533
  }
2390
3534
  return handleCliError(error, io.stderr);
2391
3535
  }