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

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