@lesliechan721/aw 0.1.0-beta.6 → 0.1.0-beta.60

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