shipscreens 0.1.6 → 0.1.7

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 (531) hide show
  1. package/README.md +14 -4
  2. package/bin/shipscreens.js +352 -18
  3. package/cli/commands/captureScreenshots.js +409 -0
  4. package/cli/commands/importScreenshots.js +377 -0
  5. package/cli/commands/inspectReleaseContext.js +607 -0
  6. package/cli/commands/planRelease.js +324 -0
  7. package/cli/commands/writeStarterConfig.js +233 -0
  8. package/cli/mcpServer.js +172 -3
  9. package/dist/.well-known/ai-agents.json +58 -11
  10. package/dist/.well-known/llms-full.txt +80 -17
  11. package/dist/.well-known/llms.txt +38 -12
  12. package/dist/.well-known/mcp-server.json +4 -4
  13. package/dist/ai-agents.json +58 -11
  14. package/dist/app/assets/AgentCli-nIqgOG-i.js +17 -0
  15. package/dist/app/assets/AgentCli-nIqgOG-i.js.map +1 -0
  16. package/dist/app/assets/AgentHandoffPanel-Bf_AJepG.js +2 -0
  17. package/dist/app/assets/AgentHandoffPanel-Bf_AJepG.js.map +1 -0
  18. package/dist/app/assets/AgentStarterWorkflowFigure-DW4SMMy7.js +2 -0
  19. package/dist/app/assets/AgentStarterWorkflowFigure-DW4SMMy7.js.map +1 -0
  20. package/dist/app/assets/App-BRKxkubQ.css +1 -0
  21. package/dist/app/assets/App-CZiyX8-L.js +14 -0
  22. package/dist/app/assets/App-CZiyX8-L.js.map +1 -0
  23. package/dist/app/assets/AppReviewAso-DYOCuqgV.js +4 -0
  24. package/dist/app/assets/AppReviewAso-DYOCuqgV.js.map +1 -0
  25. package/dist/app/assets/Cancelled-CP6V6uDb.js +2 -0
  26. package/dist/app/assets/Cancelled-CP6V6uDb.js.map +1 -0
  27. package/dist/app/assets/CopyButton-BFCx6EPg.js +2 -0
  28. package/dist/app/assets/CopyButton-BFCx6EPg.js.map +1 -0
  29. package/dist/app/assets/DesignPartner-B4J7f0Bx.js +4 -0
  30. package/dist/app/assets/DesignPartner-B4J7f0Bx.js.map +1 -0
  31. package/dist/app/assets/FastlaneScreenshots-cdZE6cGY.js +90 -0
  32. package/dist/app/assets/FastlaneScreenshots-cdZE6cGY.js.map +1 -0
  33. package/dist/app/assets/FirstExport-qfY7uE8r.js +6 -0
  34. package/dist/app/assets/FirstExport-qfY7uE8r.js.map +1 -0
  35. package/dist/app/assets/FitPanel-B1J0V1jK.js +2 -0
  36. package/dist/app/assets/FitPanel-B1J0V1jK.js.map +1 -0
  37. package/dist/app/assets/FivePanelFramework-C4yq58kM.js +62 -0
  38. package/dist/app/assets/FivePanelFramework-C4yq58kM.js.map +1 -0
  39. package/dist/app/assets/ForgotPassword-KF_HFGyb.js +2 -0
  40. package/dist/app/assets/ForgotPassword-KF_HFGyb.js.map +1 -0
  41. package/dist/app/assets/Landing-B_CvZDXJ.js +2 -0
  42. package/dist/app/assets/Landing-B_CvZDXJ.js.map +1 -0
  43. package/dist/app/assets/LanguageSwitcher-hFv9zl1u.js +2 -0
  44. package/dist/app/assets/LanguageSwitcher-hFv9zl1u.js.map +1 -0
  45. package/dist/app/assets/Legal-BwGbEjDk.js +2 -0
  46. package/dist/app/assets/Legal-BwGbEjDk.js.map +1 -0
  47. package/dist/app/assets/NotFound-Ba1Bw7uu.js +2 -0
  48. package/dist/app/assets/NotFound-Ba1Bw7uu.js.map +1 -0
  49. package/dist/app/assets/Pricing-Dt-V4Gqc.js +20 -0
  50. package/dist/app/assets/Pricing-Dt-V4Gqc.js.map +1 -0
  51. package/dist/app/assets/Privacy-C6MrXYPO.js +2 -0
  52. package/dist/app/assets/Privacy-C6MrXYPO.js.map +1 -0
  53. package/dist/app/assets/Proof-C3DDFAhm.js +20 -0
  54. package/dist/app/assets/Proof-C3DDFAhm.js.map +1 -0
  55. package/dist/app/assets/ReleaseFitIntake-DBeb9AFJ.js +27 -0
  56. package/dist/app/assets/ReleaseFitIntake-DBeb9AFJ.js.map +1 -0
  57. package/dist/app/assets/SalesEntryQuickRequestCard-Bfa93AYu.js +2 -0
  58. package/dist/app/assets/SalesEntryQuickRequestCard-Bfa93AYu.js.map +1 -0
  59. package/dist/app/assets/Security-C1OmAgom.js +2 -0
  60. package/dist/app/assets/Security-C1OmAgom.js.map +1 -0
  61. package/dist/app/assets/SignIn-C_wwnmvu.js +2 -0
  62. package/dist/app/assets/SignIn-C_wwnmvu.js.map +1 -0
  63. package/dist/app/assets/SignUp-Dm5HalC6.js +2 -0
  64. package/dist/app/assets/SignUp-Dm5HalC6.js.map +1 -0
  65. package/dist/app/assets/SiteHeader-CJqQ0pL3.js +2 -0
  66. package/dist/app/assets/SiteHeader-CJqQ0pL3.js.map +1 -0
  67. package/dist/app/assets/StickyMobileCta-REC3DROG.js +2 -0
  68. package/dist/app/assets/StickyMobileCta-REC3DROG.js.map +1 -0
  69. package/dist/app/assets/StoreAssetsPipeline-DNnnBp0D.js +6 -0
  70. package/dist/app/assets/StoreAssetsPipeline-DNnnBp0D.js.map +1 -0
  71. package/dist/app/assets/Success-D6b4wLPD.js +2 -0
  72. package/dist/app/assets/Success-D6b4wLPD.js.map +1 -0
  73. package/dist/app/assets/Terms-BMt39FQK.js +2 -0
  74. package/dist/app/assets/Terms-BMt39FQK.js.map +1 -0
  75. package/dist/app/assets/ToolGuardrailsPanel-Cclxwdrb.js +2 -0
  76. package/dist/app/assets/ToolGuardrailsPanel-Cclxwdrb.js.map +1 -0
  77. package/dist/app/assets/ToolsIndex-d1szo5bn.js +6 -0
  78. package/dist/app/assets/ToolsIndex-d1szo5bn.js.map +1 -0
  79. package/dist/app/assets/TrustSalesEntryPanel-DuncpgMs.js +2 -0
  80. package/dist/app/assets/TrustSalesEntryPanel-DuncpgMs.js.map +1 -0
  81. package/dist/app/assets/WorkloadCalculator-BbFWF2Ls.js +5 -0
  82. package/dist/app/assets/WorkloadCalculator-BbFWF2Ls.js.map +1 -0
  83. package/dist/app/assets/agentHandoff-BGmHqDwv.js +2 -0
  84. package/dist/app/assets/agentHandoff-BGmHqDwv.js.map +1 -0
  85. package/dist/app/assets/apiClient-3WVm94VW.js +2 -0
  86. package/dist/app/assets/apiClient-3WVm94VW.js.map +1 -0
  87. package/dist/app/assets/calculator-CofuwQa8.js +2 -0
  88. package/dist/app/assets/calculator-CofuwQa8.js.map +1 -0
  89. package/dist/app/assets/check-Fm2iQO64.js +2 -0
  90. package/dist/app/assets/check-Fm2iQO64.js.map +1 -0
  91. package/dist/app/assets/chevron-down-BIXHKpnf.js +2 -0
  92. package/dist/app/assets/chevron-down-BIXHKpnf.js.map +1 -0
  93. package/dist/app/assets/chunk-BHe-jwch.js +1 -0
  94. package/dist/app/assets/circle-check-X08qqy40.js +2 -0
  95. package/dist/app/assets/circle-check-X08qqy40.js.map +1 -0
  96. package/dist/app/assets/copy-KD-LYJ0z.js +2 -0
  97. package/dist/app/assets/copy-KD-LYJ0z.js.map +1 -0
  98. package/dist/app/assets/crop-K1LOmRty.js +2 -0
  99. package/dist/app/assets/crop-K1LOmRty.js.map +1 -0
  100. package/dist/app/assets/designPartnerRequest-DYqMElJd.js +2 -0
  101. package/dist/app/assets/designPartnerRequest-DYqMElJd.js.map +1 -0
  102. package/dist/app/assets/designPartnerSalesEntry-BpRINg3I.js +2 -0
  103. package/dist/app/assets/designPartnerSalesEntry-BpRINg3I.js.map +1 -0
  104. package/dist/app/assets/download-BXzoc-Y3.js +2 -0
  105. package/dist/app/assets/download-BXzoc-Y3.js.map +1 -0
  106. package/dist/app/assets/eye-CHvLhG2r.js +2 -0
  107. package/dist/app/assets/eye-CHvLhG2r.js.map +1 -0
  108. package/dist/app/assets/file-text-CU-ETLz1.js +2 -0
  109. package/dist/app/assets/file-text-CU-ETLz1.js.map +1 -0
  110. package/dist/app/assets/folder-tree-raVH0Y5V.js +2 -0
  111. package/dist/app/assets/folder-tree-raVH0Y5V.js.map +1 -0
  112. package/dist/app/assets/grid-3x3-cWuhGQfz.js +2 -0
  113. package/dist/app/assets/grid-3x3-cWuhGQfz.js.map +1 -0
  114. package/dist/app/assets/hardeningOffer-tkjlPDU-.js +2 -0
  115. package/dist/app/assets/hardeningOffer-tkjlPDU-.js.map +1 -0
  116. package/dist/app/assets/hero/agent-starter-workflow.png +0 -0
  117. package/dist/app/assets/hero/agent-starter-workflow.webp +0 -0
  118. package/dist/app/assets/html2canvas-pro.esm-OQkwCdu_.js +6 -0
  119. package/dist/app/assets/html2canvas-pro.esm-OQkwCdu_.js.map +1 -0
  120. package/dist/app/assets/index-BW_YmKvl.css +2 -0
  121. package/dist/app/assets/index-D7D6ZRCW.js +212 -0
  122. package/dist/app/assets/index-D7D6ZRCW.js.map +1 -0
  123. package/dist/app/assets/jszip.min-XyfP2hUJ.js +3 -0
  124. package/dist/app/assets/jszip.min-XyfP2hUJ.js.map +1 -0
  125. package/dist/app/assets/layout-grid-Cq_e979G.js +2 -0
  126. package/dist/app/assets/layout-grid-Cq_e979G.js.map +1 -0
  127. package/dist/app/assets/lock-Di7P18Ts.js +2 -0
  128. package/dist/app/assets/lock-Di7P18Ts.js.map +1 -0
  129. package/dist/app/assets/rocket-DHdrCHKD.js +2 -0
  130. package/dist/app/assets/rocket-DHdrCHKD.js.map +1 -0
  131. package/dist/app/assets/shield-check-CxpHNdp0.js +2 -0
  132. package/dist/app/assets/shield-check-CxpHNdp0.js.map +1 -0
  133. package/dist/app/assets/smartphone-DXKiqu57.js +2 -0
  134. package/dist/app/assets/smartphone-DXKiqu57.js.map +1 -0
  135. package/dist/app/assets/stripeClient-CuZgvzca.js +2 -0
  136. package/dist/app/assets/stripeClient-CuZgvzca.js.map +1 -0
  137. package/dist/app/assets/terminal-J0iyJTv2.js +2 -0
  138. package/dist/app/assets/terminal-J0iyJTv2.js.map +1 -0
  139. package/dist/app/assets/triangle-alert-BA0HP6zD.js +2 -0
  140. package/dist/app/assets/triangle-alert-BA0HP6zD.js.map +1 -0
  141. package/dist/app/assets/useAuth-CoS4r8yi.js +22 -0
  142. package/dist/app/assets/useAuth-CoS4r8yi.js.map +1 -0
  143. package/dist/app/assets/users-C-fz7pbn.js +2 -0
  144. package/dist/app/assets/users-C-fz7pbn.js.map +1 -0
  145. package/dist/app/index.html +9 -8
  146. package/dist/assets/AgentCli-nIqgOG-i.js +17 -0
  147. package/dist/assets/AgentCli-nIqgOG-i.js.map +1 -0
  148. package/dist/assets/AgentHandoffPanel-Bf_AJepG.js +2 -0
  149. package/dist/assets/AgentHandoffPanel-Bf_AJepG.js.map +1 -0
  150. package/dist/assets/AgentStarterWorkflowFigure-DW4SMMy7.js +2 -0
  151. package/dist/assets/AgentStarterWorkflowFigure-DW4SMMy7.js.map +1 -0
  152. package/dist/assets/App-BRKxkubQ.css +1 -0
  153. package/dist/assets/App-CZiyX8-L.js +14 -0
  154. package/dist/assets/App-CZiyX8-L.js.map +1 -0
  155. package/dist/assets/AppReviewAso-DYOCuqgV.js +4 -0
  156. package/dist/assets/AppReviewAso-DYOCuqgV.js.map +1 -0
  157. package/dist/assets/Cancelled-CP6V6uDb.js +2 -0
  158. package/dist/assets/Cancelled-CP6V6uDb.js.map +1 -0
  159. package/dist/assets/CopyButton-BFCx6EPg.js +2 -0
  160. package/dist/assets/CopyButton-BFCx6EPg.js.map +1 -0
  161. package/dist/assets/DesignPartner-B4J7f0Bx.js +4 -0
  162. package/dist/assets/DesignPartner-B4J7f0Bx.js.map +1 -0
  163. package/dist/assets/FastlaneScreenshots-cdZE6cGY.js +90 -0
  164. package/dist/assets/FastlaneScreenshots-cdZE6cGY.js.map +1 -0
  165. package/dist/assets/FirstExport-qfY7uE8r.js +6 -0
  166. package/dist/assets/FirstExport-qfY7uE8r.js.map +1 -0
  167. package/dist/assets/FitPanel-B1J0V1jK.js +2 -0
  168. package/dist/assets/FitPanel-B1J0V1jK.js.map +1 -0
  169. package/dist/assets/FivePanelFramework-C4yq58kM.js +62 -0
  170. package/dist/assets/FivePanelFramework-C4yq58kM.js.map +1 -0
  171. package/dist/assets/ForgotPassword-KF_HFGyb.js +2 -0
  172. package/dist/assets/ForgotPassword-KF_HFGyb.js.map +1 -0
  173. package/dist/assets/Landing-B_CvZDXJ.js +2 -0
  174. package/dist/assets/Landing-B_CvZDXJ.js.map +1 -0
  175. package/dist/assets/LanguageSwitcher-hFv9zl1u.js +2 -0
  176. package/dist/assets/LanguageSwitcher-hFv9zl1u.js.map +1 -0
  177. package/dist/assets/Legal-BwGbEjDk.js +2 -0
  178. package/dist/assets/Legal-BwGbEjDk.js.map +1 -0
  179. package/dist/assets/NotFound-Ba1Bw7uu.js +2 -0
  180. package/dist/assets/NotFound-Ba1Bw7uu.js.map +1 -0
  181. package/dist/assets/Pricing-Dt-V4Gqc.js +20 -0
  182. package/dist/assets/Pricing-Dt-V4Gqc.js.map +1 -0
  183. package/dist/assets/Privacy-C6MrXYPO.js +2 -0
  184. package/dist/assets/Privacy-C6MrXYPO.js.map +1 -0
  185. package/dist/assets/Proof-C3DDFAhm.js +20 -0
  186. package/dist/assets/Proof-C3DDFAhm.js.map +1 -0
  187. package/dist/assets/ReleaseFitIntake-DBeb9AFJ.js +27 -0
  188. package/dist/assets/ReleaseFitIntake-DBeb9AFJ.js.map +1 -0
  189. package/dist/assets/SalesEntryQuickRequestCard-Bfa93AYu.js +2 -0
  190. package/dist/assets/SalesEntryQuickRequestCard-Bfa93AYu.js.map +1 -0
  191. package/dist/assets/Security-C1OmAgom.js +2 -0
  192. package/dist/assets/Security-C1OmAgom.js.map +1 -0
  193. package/dist/assets/SignIn-C_wwnmvu.js +2 -0
  194. package/dist/assets/SignIn-C_wwnmvu.js.map +1 -0
  195. package/dist/assets/SignUp-Dm5HalC6.js +2 -0
  196. package/dist/assets/SignUp-Dm5HalC6.js.map +1 -0
  197. package/dist/assets/SiteHeader-CJqQ0pL3.js +2 -0
  198. package/dist/assets/SiteHeader-CJqQ0pL3.js.map +1 -0
  199. package/dist/assets/StickyMobileCta-REC3DROG.js +2 -0
  200. package/dist/assets/StickyMobileCta-REC3DROG.js.map +1 -0
  201. package/dist/assets/StoreAssetsPipeline-DNnnBp0D.js +6 -0
  202. package/dist/assets/StoreAssetsPipeline-DNnnBp0D.js.map +1 -0
  203. package/dist/assets/Success-D6b4wLPD.js +2 -0
  204. package/dist/assets/Success-D6b4wLPD.js.map +1 -0
  205. package/dist/assets/Terms-BMt39FQK.js +2 -0
  206. package/dist/assets/Terms-BMt39FQK.js.map +1 -0
  207. package/dist/assets/ToolGuardrailsPanel-Cclxwdrb.js +2 -0
  208. package/dist/assets/ToolGuardrailsPanel-Cclxwdrb.js.map +1 -0
  209. package/dist/assets/ToolsIndex-d1szo5bn.js +6 -0
  210. package/dist/assets/ToolsIndex-d1szo5bn.js.map +1 -0
  211. package/dist/assets/TrustSalesEntryPanel-DuncpgMs.js +2 -0
  212. package/dist/assets/TrustSalesEntryPanel-DuncpgMs.js.map +1 -0
  213. package/dist/assets/WorkloadCalculator-BbFWF2Ls.js +5 -0
  214. package/dist/assets/WorkloadCalculator-BbFWF2Ls.js.map +1 -0
  215. package/dist/assets/agentHandoff-BGmHqDwv.js +2 -0
  216. package/dist/assets/agentHandoff-BGmHqDwv.js.map +1 -0
  217. package/dist/assets/apiClient-3WVm94VW.js +2 -0
  218. package/dist/assets/apiClient-3WVm94VW.js.map +1 -0
  219. package/dist/assets/calculator-CofuwQa8.js +2 -0
  220. package/dist/assets/calculator-CofuwQa8.js.map +1 -0
  221. package/dist/assets/check-Fm2iQO64.js +2 -0
  222. package/dist/assets/check-Fm2iQO64.js.map +1 -0
  223. package/dist/assets/chevron-down-BIXHKpnf.js +2 -0
  224. package/dist/assets/chevron-down-BIXHKpnf.js.map +1 -0
  225. package/dist/assets/chunk-BHe-jwch.js +1 -0
  226. package/dist/assets/circle-check-X08qqy40.js +2 -0
  227. package/dist/assets/circle-check-X08qqy40.js.map +1 -0
  228. package/dist/assets/copy-KD-LYJ0z.js +2 -0
  229. package/dist/assets/copy-KD-LYJ0z.js.map +1 -0
  230. package/dist/assets/crop-K1LOmRty.js +2 -0
  231. package/dist/assets/crop-K1LOmRty.js.map +1 -0
  232. package/dist/assets/designPartnerRequest-DYqMElJd.js +2 -0
  233. package/dist/assets/designPartnerRequest-DYqMElJd.js.map +1 -0
  234. package/dist/assets/designPartnerSalesEntry-BpRINg3I.js +2 -0
  235. package/dist/assets/designPartnerSalesEntry-BpRINg3I.js.map +1 -0
  236. package/dist/assets/download-BXzoc-Y3.js +2 -0
  237. package/dist/assets/download-BXzoc-Y3.js.map +1 -0
  238. package/dist/assets/eye-CHvLhG2r.js +2 -0
  239. package/dist/assets/eye-CHvLhG2r.js.map +1 -0
  240. package/dist/assets/file-text-CU-ETLz1.js +2 -0
  241. package/dist/assets/file-text-CU-ETLz1.js.map +1 -0
  242. package/dist/assets/folder-tree-raVH0Y5V.js +2 -0
  243. package/dist/assets/folder-tree-raVH0Y5V.js.map +1 -0
  244. package/dist/assets/grid-3x3-cWuhGQfz.js +2 -0
  245. package/dist/assets/grid-3x3-cWuhGQfz.js.map +1 -0
  246. package/dist/assets/hardeningOffer-tkjlPDU-.js +2 -0
  247. package/dist/assets/hardeningOffer-tkjlPDU-.js.map +1 -0
  248. package/dist/assets/hero/agent-starter-workflow.png +0 -0
  249. package/dist/assets/hero/agent-starter-workflow.webp +0 -0
  250. package/dist/assets/html2canvas-pro.esm-OQkwCdu_.js +6 -0
  251. package/dist/assets/html2canvas-pro.esm-OQkwCdu_.js.map +1 -0
  252. package/dist/assets/index-BW_YmKvl.css +2 -0
  253. package/dist/assets/index-D7D6ZRCW.js +212 -0
  254. package/dist/assets/index-D7D6ZRCW.js.map +1 -0
  255. package/dist/assets/jszip.min-XyfP2hUJ.js +3 -0
  256. package/dist/assets/jszip.min-XyfP2hUJ.js.map +1 -0
  257. package/dist/assets/layout-grid-Cq_e979G.js +2 -0
  258. package/dist/assets/layout-grid-Cq_e979G.js.map +1 -0
  259. package/dist/assets/lock-Di7P18Ts.js +2 -0
  260. package/dist/assets/lock-Di7P18Ts.js.map +1 -0
  261. package/dist/assets/rocket-DHdrCHKD.js +2 -0
  262. package/dist/assets/rocket-DHdrCHKD.js.map +1 -0
  263. package/dist/assets/shield-check-CxpHNdp0.js +2 -0
  264. package/dist/assets/shield-check-CxpHNdp0.js.map +1 -0
  265. package/dist/assets/smartphone-DXKiqu57.js +2 -0
  266. package/dist/assets/smartphone-DXKiqu57.js.map +1 -0
  267. package/dist/assets/stripeClient-CuZgvzca.js +2 -0
  268. package/dist/assets/stripeClient-CuZgvzca.js.map +1 -0
  269. package/dist/assets/terminal-J0iyJTv2.js +2 -0
  270. package/dist/assets/terminal-J0iyJTv2.js.map +1 -0
  271. package/dist/assets/triangle-alert-BA0HP6zD.js +2 -0
  272. package/dist/assets/triangle-alert-BA0HP6zD.js.map +1 -0
  273. package/dist/assets/useAuth-CoS4r8yi.js +22 -0
  274. package/dist/assets/useAuth-CoS4r8yi.js.map +1 -0
  275. package/dist/assets/users-C-fz7pbn.js +2 -0
  276. package/dist/assets/users-C-fz7pbn.js.map +1 -0
  277. package/dist/cancelled/index.html +3 -2
  278. package/dist/design-partner/index.html +6 -5
  279. package/dist/downloads/shipscreens-github-actions-template.yml +1 -1
  280. package/dist/forgot-password/index.html +3 -2
  281. package/dist/index.html +9 -8
  282. package/dist/legal/index.html +3 -2
  283. package/dist/llms-full.txt +80 -17
  284. package/dist/llms.txt +38 -12
  285. package/dist/mcp-server.json +4 -4
  286. package/dist/pricing/index.html +6 -5
  287. package/dist/privacy/index.html +3 -2
  288. package/dist/proof/index.html +6 -5
  289. package/dist/security/index.html +3 -2
  290. package/dist/signin/index.html +3 -2
  291. package/dist/signup/index.html +3 -2
  292. package/dist/success/index.html +3 -2
  293. package/dist/terms/index.html +3 -2
  294. package/dist/tools/5-panel-screenshot-framework/index.html +6 -5
  295. package/dist/tools/agent-cli/index.html +8 -7
  296. package/dist/tools/app-review-aso/index.html +6 -5
  297. package/dist/tools/app-store-localization-screenshot-automation/index.html +8 -7
  298. package/dist/tools/app-store-screenshot-automation/index.html +8 -7
  299. package/dist/tools/app-store-screenshot-requirements-checklist/index.html +8 -7
  300. package/dist/tools/claude-code-mcp-screenshot-automation/index.html +8 -7
  301. package/dist/tools/cursor-mcp-screenshot-automation/index.html +8 -7
  302. package/dist/tools/deterministic-screenshot-reruns/index.html +8 -7
  303. package/dist/tools/device-locale-screenshot-matrix/index.html +8 -7
  304. package/dist/tools/fastlane-screenshot-folder-structure/index.html +8 -7
  305. package/dist/tools/fastlane-screenshot-pipeline/index.html +8 -7
  306. package/dist/tools/fastlane-screenshots/index.html +6 -5
  307. package/dist/tools/first-export/index.html +6 -5
  308. package/dist/tools/gemini-cli-mcp-screenshot-automation/index.html +8 -7
  309. package/dist/tools/google-play-screenshot-automation/index.html +8 -7
  310. package/dist/tools/google-play-screenshot-requirements-checklist/index.html +8 -7
  311. package/dist/tools/index.html +6 -5
  312. package/dist/tools/ipad-screenshot-review-requirements/index.html +8 -7
  313. package/dist/tools/openai-responses-tool-screenshot-automation/index.html +8 -7
  314. package/dist/tools/screenshot-ci-pipeline/index.html +8 -7
  315. package/dist/tools/screenshot-naming-order-drift/index.html +8 -7
  316. package/dist/tools/screenshot-workload-calculator/index.html +6 -5
  317. package/dist/tools/store-assets-pipeline/index.html +3 -2
  318. package/dist/tools/vscode-copilot-mcp-screenshot-automation/index.html +8 -7
  319. package/package.json +7 -7
  320. package/dist/app/assets/AgentCli-xKYtzL3B.js +0 -17
  321. package/dist/app/assets/AgentCli-xKYtzL3B.js.map +0 -1
  322. package/dist/app/assets/App-C1cNw_fn.js +0 -13
  323. package/dist/app/assets/App-C1cNw_fn.js.map +0 -1
  324. package/dist/app/assets/App-oQfE_p3A.css +0 -1
  325. package/dist/app/assets/AppReviewAso-CaIT-L7Z.js +0 -4
  326. package/dist/app/assets/AppReviewAso-CaIT-L7Z.js.map +0 -1
  327. package/dist/app/assets/Cancelled-Dmw7PTD-.js +0 -2
  328. package/dist/app/assets/Cancelled-Dmw7PTD-.js.map +0 -1
  329. package/dist/app/assets/CopyButton-CPRHmu8l.js +0 -2
  330. package/dist/app/assets/CopyButton-CPRHmu8l.js.map +0 -1
  331. package/dist/app/assets/DesignPartner-Ckp1GhNv.js +0 -4
  332. package/dist/app/assets/DesignPartner-Ckp1GhNv.js.map +0 -1
  333. package/dist/app/assets/FastlaneScreenshots-CB-zv5Us.js +0 -100
  334. package/dist/app/assets/FastlaneScreenshots-CB-zv5Us.js.map +0 -1
  335. package/dist/app/assets/FirstExport-RXbvVQdK.js +0 -6
  336. package/dist/app/assets/FirstExport-RXbvVQdK.js.map +0 -1
  337. package/dist/app/assets/FitPanel-ic91y-49.js +0 -2
  338. package/dist/app/assets/FitPanel-ic91y-49.js.map +0 -1
  339. package/dist/app/assets/FivePanelFramework-BZKNcycb.js +0 -62
  340. package/dist/app/assets/FivePanelFramework-BZKNcycb.js.map +0 -1
  341. package/dist/app/assets/ForgotPassword-CKOfAvWk.js +0 -2
  342. package/dist/app/assets/ForgotPassword-CKOfAvWk.js.map +0 -1
  343. package/dist/app/assets/Landing-D0cuDDUk.js +0 -2
  344. package/dist/app/assets/Landing-D0cuDDUk.js.map +0 -1
  345. package/dist/app/assets/LanguageSwitcher-BrToLEgF.js +0 -2
  346. package/dist/app/assets/LanguageSwitcher-BrToLEgF.js.map +0 -1
  347. package/dist/app/assets/Legal-Cm-hVzjY.js +0 -2
  348. package/dist/app/assets/Legal-Cm-hVzjY.js.map +0 -1
  349. package/dist/app/assets/NotFound-B0I6I440.js +0 -2
  350. package/dist/app/assets/NotFound-B0I6I440.js.map +0 -1
  351. package/dist/app/assets/Pricing-p-NIjjtK.js +0 -29
  352. package/dist/app/assets/Pricing-p-NIjjtK.js.map +0 -1
  353. package/dist/app/assets/Privacy-BhfPkK0y.js +0 -2
  354. package/dist/app/assets/Privacy-BhfPkK0y.js.map +0 -1
  355. package/dist/app/assets/Proof-D-7QbTGX.js +0 -20
  356. package/dist/app/assets/Proof-D-7QbTGX.js.map +0 -1
  357. package/dist/app/assets/Security-DFafJQIT.js +0 -2
  358. package/dist/app/assets/Security-DFafJQIT.js.map +0 -1
  359. package/dist/app/assets/SignIn--6nNsUCm.js +0 -2
  360. package/dist/app/assets/SignIn--6nNsUCm.js.map +0 -1
  361. package/dist/app/assets/SignUp-yh5ydmFf.js +0 -2
  362. package/dist/app/assets/SignUp-yh5ydmFf.js.map +0 -1
  363. package/dist/app/assets/SiteHeader-NTK-cM9O.js +0 -2
  364. package/dist/app/assets/SiteHeader-NTK-cM9O.js.map +0 -1
  365. package/dist/app/assets/StickyMobileCta-DVXAyhaH.js +0 -2
  366. package/dist/app/assets/StickyMobileCta-DVXAyhaH.js.map +0 -1
  367. package/dist/app/assets/StoreAssetsPipeline-Cbs1bNK5.js +0 -6
  368. package/dist/app/assets/StoreAssetsPipeline-Cbs1bNK5.js.map +0 -1
  369. package/dist/app/assets/Success-C3n0m1XU.js +0 -2
  370. package/dist/app/assets/Success-C3n0m1XU.js.map +0 -1
  371. package/dist/app/assets/Terms-C72ybAwA.js +0 -2
  372. package/dist/app/assets/Terms-C72ybAwA.js.map +0 -1
  373. package/dist/app/assets/ToolGuardrailsPanel-meKxUeSB.js +0 -2
  374. package/dist/app/assets/ToolGuardrailsPanel-meKxUeSB.js.map +0 -1
  375. package/dist/app/assets/ToolsIndex-CnvNTvyW.js +0 -6
  376. package/dist/app/assets/ToolsIndex-CnvNTvyW.js.map +0 -1
  377. package/dist/app/assets/WorkloadCalculator-CK4qVUcQ.js +0 -5
  378. package/dist/app/assets/WorkloadCalculator-CK4qVUcQ.js.map +0 -1
  379. package/dist/app/assets/arrow-right-Dglia5EE.js +0 -2
  380. package/dist/app/assets/arrow-right-Dglia5EE.js.map +0 -1
  381. package/dist/app/assets/calculator-CAgtsVuH.js +0 -2
  382. package/dist/app/assets/calculator-CAgtsVuH.js.map +0 -1
  383. package/dist/app/assets/check-B5h9hATV.js +0 -2
  384. package/dist/app/assets/check-B5h9hATV.js.map +0 -1
  385. package/dist/app/assets/chevron-down-By2Ts8dG.js +0 -2
  386. package/dist/app/assets/chevron-down-By2Ts8dG.js.map +0 -1
  387. package/dist/app/assets/circle-check-DaJ6APMi.js +0 -2
  388. package/dist/app/assets/circle-check-DaJ6APMi.js.map +0 -1
  389. package/dist/app/assets/clipboard-copy-IDfWU9SE.js +0 -2
  390. package/dist/app/assets/clipboard-copy-IDfWU9SE.js.map +0 -1
  391. package/dist/app/assets/copy-KSn4Rc2b.js +0 -2
  392. package/dist/app/assets/copy-KSn4Rc2b.js.map +0 -1
  393. package/dist/app/assets/crop-C9hwiw0i.js +0 -2
  394. package/dist/app/assets/crop-C9hwiw0i.js.map +0 -1
  395. package/dist/app/assets/download-eqwxO8bs.js +0 -2
  396. package/dist/app/assets/download-eqwxO8bs.js.map +0 -1
  397. package/dist/app/assets/eye-Dg61f0nU.js +0 -2
  398. package/dist/app/assets/eye-Dg61f0nU.js.map +0 -1
  399. package/dist/app/assets/file-text-BQPKCil3.js +0 -2
  400. package/dist/app/assets/file-text-BQPKCil3.js.map +0 -1
  401. package/dist/app/assets/folder-tree-9hq6oigy.js +0 -2
  402. package/dist/app/assets/folder-tree-9hq6oigy.js.map +0 -1
  403. package/dist/app/assets/grid-3x3-BHXRyOIx.js +0 -2
  404. package/dist/app/assets/grid-3x3-BHXRyOIx.js.map +0 -1
  405. package/dist/app/assets/index-my6BmN7T.js +0 -204
  406. package/dist/app/assets/index-my6BmN7T.js.map +0 -1
  407. package/dist/app/assets/index-rv91vbLH.css +0 -1
  408. package/dist/app/assets/layout-grid-DDQvnEgx.js +0 -2
  409. package/dist/app/assets/layout-grid-DDQvnEgx.js.map +0 -1
  410. package/dist/app/assets/rocket-0KMviRpJ.js +0 -2
  411. package/dist/app/assets/rocket-0KMviRpJ.js.map +0 -1
  412. package/dist/app/assets/shield-check-BvKEQ2hc.js +0 -2
  413. package/dist/app/assets/shield-check-BvKEQ2hc.js.map +0 -1
  414. package/dist/app/assets/signIn-Ckla1_0O.js +0 -2
  415. package/dist/app/assets/signIn-Ckla1_0O.js.map +0 -1
  416. package/dist/app/assets/smartphone-I0w1b2DV.js +0 -2
  417. package/dist/app/assets/smartphone-I0w1b2DV.js.map +0 -1
  418. package/dist/app/assets/terminal-CLpUSQqd.js +0 -2
  419. package/dist/app/assets/terminal-CLpUSQqd.js.map +0 -1
  420. package/dist/app/assets/triangle-alert-w7TZ0yy2.js +0 -2
  421. package/dist/app/assets/triangle-alert-w7TZ0yy2.js.map +0 -1
  422. package/dist/app/assets/useAuth-N3hsUaDj.js +0 -4
  423. package/dist/app/assets/useAuth-N3hsUaDj.js.map +0 -1
  424. package/dist/app/assets/users-DsQdkuR3.js +0 -2
  425. package/dist/app/assets/users-DsQdkuR3.js.map +0 -1
  426. package/dist/assets/AgentCli-xKYtzL3B.js +0 -17
  427. package/dist/assets/AgentCli-xKYtzL3B.js.map +0 -1
  428. package/dist/assets/App-C1cNw_fn.js +0 -13
  429. package/dist/assets/App-C1cNw_fn.js.map +0 -1
  430. package/dist/assets/App-oQfE_p3A.css +0 -1
  431. package/dist/assets/AppReviewAso-CaIT-L7Z.js +0 -4
  432. package/dist/assets/AppReviewAso-CaIT-L7Z.js.map +0 -1
  433. package/dist/assets/Cancelled-Dmw7PTD-.js +0 -2
  434. package/dist/assets/Cancelled-Dmw7PTD-.js.map +0 -1
  435. package/dist/assets/CopyButton-CPRHmu8l.js +0 -2
  436. package/dist/assets/CopyButton-CPRHmu8l.js.map +0 -1
  437. package/dist/assets/DesignPartner-Ckp1GhNv.js +0 -4
  438. package/dist/assets/DesignPartner-Ckp1GhNv.js.map +0 -1
  439. package/dist/assets/FastlaneScreenshots-CB-zv5Us.js +0 -100
  440. package/dist/assets/FastlaneScreenshots-CB-zv5Us.js.map +0 -1
  441. package/dist/assets/FirstExport-RXbvVQdK.js +0 -6
  442. package/dist/assets/FirstExport-RXbvVQdK.js.map +0 -1
  443. package/dist/assets/FitPanel-ic91y-49.js +0 -2
  444. package/dist/assets/FitPanel-ic91y-49.js.map +0 -1
  445. package/dist/assets/FivePanelFramework-BZKNcycb.js +0 -62
  446. package/dist/assets/FivePanelFramework-BZKNcycb.js.map +0 -1
  447. package/dist/assets/ForgotPassword-CKOfAvWk.js +0 -2
  448. package/dist/assets/ForgotPassword-CKOfAvWk.js.map +0 -1
  449. package/dist/assets/Landing-D0cuDDUk.js +0 -2
  450. package/dist/assets/Landing-D0cuDDUk.js.map +0 -1
  451. package/dist/assets/LanguageSwitcher-BrToLEgF.js +0 -2
  452. package/dist/assets/LanguageSwitcher-BrToLEgF.js.map +0 -1
  453. package/dist/assets/Legal-Cm-hVzjY.js +0 -2
  454. package/dist/assets/Legal-Cm-hVzjY.js.map +0 -1
  455. package/dist/assets/NotFound-B0I6I440.js +0 -2
  456. package/dist/assets/NotFound-B0I6I440.js.map +0 -1
  457. package/dist/assets/Pricing-p-NIjjtK.js +0 -29
  458. package/dist/assets/Pricing-p-NIjjtK.js.map +0 -1
  459. package/dist/assets/Privacy-BhfPkK0y.js +0 -2
  460. package/dist/assets/Privacy-BhfPkK0y.js.map +0 -1
  461. package/dist/assets/Proof-D-7QbTGX.js +0 -20
  462. package/dist/assets/Proof-D-7QbTGX.js.map +0 -1
  463. package/dist/assets/Security-DFafJQIT.js +0 -2
  464. package/dist/assets/Security-DFafJQIT.js.map +0 -1
  465. package/dist/assets/SignIn--6nNsUCm.js +0 -2
  466. package/dist/assets/SignIn--6nNsUCm.js.map +0 -1
  467. package/dist/assets/SignUp-yh5ydmFf.js +0 -2
  468. package/dist/assets/SignUp-yh5ydmFf.js.map +0 -1
  469. package/dist/assets/SiteHeader-NTK-cM9O.js +0 -2
  470. package/dist/assets/SiteHeader-NTK-cM9O.js.map +0 -1
  471. package/dist/assets/StickyMobileCta-DVXAyhaH.js +0 -2
  472. package/dist/assets/StickyMobileCta-DVXAyhaH.js.map +0 -1
  473. package/dist/assets/StoreAssetsPipeline-Cbs1bNK5.js +0 -6
  474. package/dist/assets/StoreAssetsPipeline-Cbs1bNK5.js.map +0 -1
  475. package/dist/assets/Success-C3n0m1XU.js +0 -2
  476. package/dist/assets/Success-C3n0m1XU.js.map +0 -1
  477. package/dist/assets/Terms-C72ybAwA.js +0 -2
  478. package/dist/assets/Terms-C72ybAwA.js.map +0 -1
  479. package/dist/assets/ToolGuardrailsPanel-meKxUeSB.js +0 -2
  480. package/dist/assets/ToolGuardrailsPanel-meKxUeSB.js.map +0 -1
  481. package/dist/assets/ToolsIndex-CnvNTvyW.js +0 -6
  482. package/dist/assets/ToolsIndex-CnvNTvyW.js.map +0 -1
  483. package/dist/assets/WorkloadCalculator-CK4qVUcQ.js +0 -5
  484. package/dist/assets/WorkloadCalculator-CK4qVUcQ.js.map +0 -1
  485. package/dist/assets/arrow-right-Dglia5EE.js +0 -2
  486. package/dist/assets/arrow-right-Dglia5EE.js.map +0 -1
  487. package/dist/assets/calculator-CAgtsVuH.js +0 -2
  488. package/dist/assets/calculator-CAgtsVuH.js.map +0 -1
  489. package/dist/assets/check-B5h9hATV.js +0 -2
  490. package/dist/assets/check-B5h9hATV.js.map +0 -1
  491. package/dist/assets/chevron-down-By2Ts8dG.js +0 -2
  492. package/dist/assets/chevron-down-By2Ts8dG.js.map +0 -1
  493. package/dist/assets/circle-check-DaJ6APMi.js +0 -2
  494. package/dist/assets/circle-check-DaJ6APMi.js.map +0 -1
  495. package/dist/assets/clipboard-copy-IDfWU9SE.js +0 -2
  496. package/dist/assets/clipboard-copy-IDfWU9SE.js.map +0 -1
  497. package/dist/assets/copy-KSn4Rc2b.js +0 -2
  498. package/dist/assets/copy-KSn4Rc2b.js.map +0 -1
  499. package/dist/assets/crop-C9hwiw0i.js +0 -2
  500. package/dist/assets/crop-C9hwiw0i.js.map +0 -1
  501. package/dist/assets/download-eqwxO8bs.js +0 -2
  502. package/dist/assets/download-eqwxO8bs.js.map +0 -1
  503. package/dist/assets/eye-Dg61f0nU.js +0 -2
  504. package/dist/assets/eye-Dg61f0nU.js.map +0 -1
  505. package/dist/assets/file-text-BQPKCil3.js +0 -2
  506. package/dist/assets/file-text-BQPKCil3.js.map +0 -1
  507. package/dist/assets/folder-tree-9hq6oigy.js +0 -2
  508. package/dist/assets/folder-tree-9hq6oigy.js.map +0 -1
  509. package/dist/assets/grid-3x3-BHXRyOIx.js +0 -2
  510. package/dist/assets/grid-3x3-BHXRyOIx.js.map +0 -1
  511. package/dist/assets/index-my6BmN7T.js +0 -204
  512. package/dist/assets/index-my6BmN7T.js.map +0 -1
  513. package/dist/assets/index-rv91vbLH.css +0 -1
  514. package/dist/assets/layout-grid-DDQvnEgx.js +0 -2
  515. package/dist/assets/layout-grid-DDQvnEgx.js.map +0 -1
  516. package/dist/assets/rocket-0KMviRpJ.js +0 -2
  517. package/dist/assets/rocket-0KMviRpJ.js.map +0 -1
  518. package/dist/assets/shield-check-BvKEQ2hc.js +0 -2
  519. package/dist/assets/shield-check-BvKEQ2hc.js.map +0 -1
  520. package/dist/assets/signIn-Ckla1_0O.js +0 -2
  521. package/dist/assets/signIn-Ckla1_0O.js.map +0 -1
  522. package/dist/assets/smartphone-I0w1b2DV.js +0 -2
  523. package/dist/assets/smartphone-I0w1b2DV.js.map +0 -1
  524. package/dist/assets/terminal-CLpUSQqd.js +0 -2
  525. package/dist/assets/terminal-CLpUSQqd.js.map +0 -1
  526. package/dist/assets/triangle-alert-w7TZ0yy2.js +0 -2
  527. package/dist/assets/triangle-alert-w7TZ0yy2.js.map +0 -1
  528. package/dist/assets/useAuth-N3hsUaDj.js +0 -4
  529. package/dist/assets/useAuth-N3hsUaDj.js.map +0 -1
  530. package/dist/assets/users-DsQdkuR3.js +0 -2
  531. package/dist/assets/users-DsQdkuR3.js.map +0 -1
@@ -0,0 +1,607 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { collectAssetReferences } from '../assetRefs.js';
4
+ import { loadShipScreensConfig, resolveDefaultConfigPath } from '../loadConfig.js';
5
+ import { resolveOutputPath } from '../outputPaths.js';
6
+ import {
7
+ createSolidPng,
8
+ PLACEHOLDER_COLOR,
9
+ PLACEHOLDER_HEIGHT,
10
+ PLACEHOLDER_WIDTH,
11
+ } from './init.js';
12
+
13
+ const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp']);
14
+ const SKIP_DIRS = new Set(['.git', '.hg', '.svn', 'node_modules', '.next', '.nuxt', 'coverage']);
15
+ const MAX_SCAN_DEPTH = 5;
16
+ const MAX_SAMPLE_FILES = 12;
17
+ const MAX_IMAGE_COUNT = 5000;
18
+
19
+ function compareStrings(a, b) {
20
+ if (a === b) return 0;
21
+ return a < b ? -1 : 1;
22
+ }
23
+
24
+ function toPosixPath(value) {
25
+ return String(value || '').replaceAll('\\', '/');
26
+ }
27
+
28
+ function relativeTo(baseDir, targetPath) {
29
+ const rel = path.relative(baseDir, targetPath);
30
+ return toPosixPath(rel || '.');
31
+ }
32
+
33
+ async function statSafe(filePath) {
34
+ try {
35
+ return await fs.stat(filePath);
36
+ } catch (err) {
37
+ if (err?.code === 'ENOENT') return null;
38
+ throw err;
39
+ }
40
+ }
41
+
42
+ async function resolveConfigCandidate({ targetDir, configPath }) {
43
+ if (configPath) {
44
+ return path.isAbsolute(configPath) ? configPath : path.resolve(targetDir, configPath);
45
+ }
46
+ return resolveDefaultConfigPath(targetDir);
47
+ }
48
+
49
+ function getAndroidBucket(device) {
50
+ return device.androidBucket || 'phoneScreenshots';
51
+ }
52
+
53
+ function summarizeConfig({ config, configDir, targetDir }) {
54
+ const devices = Array.isArray(config.devices) ? config.devices : [];
55
+ const iosDevices = devices.filter((device) => device.platform !== 'android');
56
+ const androidDevices = devices.filter((device) => device.platform === 'android');
57
+ const locales = (config.locales || []).map((locale) => locale.id).sort(compareStrings);
58
+
59
+ const iosDir = resolveOutputPath({
60
+ outRoot: configDir,
61
+ value: config.output?.directory,
62
+ fallback: 'fastlane/screenshots',
63
+ });
64
+ const androidDir = resolveOutputPath({
65
+ outRoot: configDir,
66
+ value: config.output?.androidDirectory,
67
+ fallback: 'fastlane/metadata/android',
68
+ });
69
+
70
+ return {
71
+ schemaVersion: config.schemaVersion || config.version || null,
72
+ variant: config.variant || config.experiment?.variant || config.asoExperiment?.variant || null,
73
+ locales,
74
+ localeCount: locales.length,
75
+ slideCount: Array.isArray(config.slides) ? config.slides.length : 0,
76
+ devices: devices.map((device) => ({
77
+ id: device.id,
78
+ platform: device.platform || null,
79
+ androidBucket: device.platform === 'android' ? getAndroidBucket(device) : null,
80
+ label: device.label || null,
81
+ })),
82
+ iosDevices: iosDevices.map((device) => device.id).sort(compareStrings),
83
+ androidDevices: androidDevices.map((device) => ({
84
+ id: device.id,
85
+ bucket: getAndroidBucket(device),
86
+ })),
87
+ output: {
88
+ iosDir,
89
+ iosDirRelative: relativeTo(targetDir, iosDir),
90
+ androidDir,
91
+ androidDirRelative: relativeTo(targetDir, androidDir),
92
+ },
93
+ };
94
+ }
95
+
96
+ async function loadConfigContext({ targetDir, configPath }) {
97
+ const resolvedConfigPath = await resolveConfigCandidate({ targetDir, configPath });
98
+ if (!resolvedConfigPath) {
99
+ return {
100
+ path: path.join(targetDir, 'shipscreens.config.yml'),
101
+ relativePath: 'shipscreens.config.yml',
102
+ exists: false,
103
+ valid: false,
104
+ error: null,
105
+ summary: null,
106
+ config: null,
107
+ configDir: targetDir,
108
+ };
109
+ }
110
+
111
+ const exists = Boolean(await statSafe(resolvedConfigPath));
112
+ if (!exists) {
113
+ return {
114
+ path: resolvedConfigPath,
115
+ relativePath: relativeTo(targetDir, resolvedConfigPath),
116
+ exists: false,
117
+ valid: false,
118
+ error: null,
119
+ summary: null,
120
+ config: null,
121
+ configDir: path.dirname(resolvedConfigPath),
122
+ };
123
+ }
124
+
125
+ try {
126
+ const loaded = await loadShipScreensConfig(resolvedConfigPath);
127
+ return {
128
+ path: loaded.configPath,
129
+ relativePath: relativeTo(targetDir, loaded.configPath),
130
+ exists: true,
131
+ valid: true,
132
+ error: null,
133
+ summary: summarizeConfig({
134
+ config: loaded.config,
135
+ configDir: loaded.configDir,
136
+ targetDir,
137
+ }),
138
+ config: loaded.config,
139
+ configDir: loaded.configDir,
140
+ };
141
+ } catch (err) {
142
+ return {
143
+ path: resolvedConfigPath,
144
+ relativePath: relativeTo(targetDir, resolvedConfigPath),
145
+ exists: true,
146
+ valid: false,
147
+ error: err?.message || String(err),
148
+ summary: null,
149
+ config: null,
150
+ configDir: path.dirname(resolvedConfigPath),
151
+ };
152
+ }
153
+ }
154
+
155
+ function addRootCandidate(candidates, seen, { kind, label, rootPath }) {
156
+ const resolved = path.resolve(rootPath);
157
+ const key = resolved.toLowerCase();
158
+ if (seen.has(key)) return;
159
+ seen.add(key);
160
+ candidates.push({ kind, label, path: resolved });
161
+ }
162
+
163
+ function createRootCandidates({ targetDir, configContext }) {
164
+ const candidates = [];
165
+ const seen = new Set();
166
+
167
+ if (configContext.valid && configContext.summary?.output) {
168
+ addRootCandidate(candidates, seen, {
169
+ kind: 'config_ios_output',
170
+ label: 'Configured iOS Fastlane output',
171
+ rootPath: configContext.summary.output.iosDir,
172
+ });
173
+ addRootCandidate(candidates, seen, {
174
+ kind: 'config_android_output',
175
+ label: 'Configured Android Fastlane output',
176
+ rootPath: configContext.summary.output.androidDir,
177
+ });
178
+ }
179
+
180
+ [
181
+ ['fastlane_ios', 'Conventional iOS Fastlane screenshots', path.join(targetDir, 'fastlane', 'screenshots')],
182
+ [
183
+ 'fastlane_android',
184
+ 'Conventional Android Fastlane metadata',
185
+ path.join(targetDir, 'fastlane', 'metadata', 'android'),
186
+ ],
187
+ ['source_screenshots', 'Source screenshots', path.join(targetDir, 'screenshots')],
188
+ ['captured_screenshots', 'Captured device/emulator screenshots', path.join(targetDir, 'screenshots', 'captured')],
189
+ ['source_screenshot', 'Source screenshot', path.join(targetDir, 'screenshot')],
190
+ ['assets_screenshots', 'Asset screenshots', path.join(targetDir, 'assets', 'screenshots')],
191
+ ['assets', 'Assets', path.join(targetDir, 'assets')],
192
+ ['public_screenshots', 'Public screenshots', path.join(targetDir, 'public', 'screenshots')],
193
+ ['app_store_screenshots', 'App Store screenshots', path.join(targetDir, 'app-store-screenshots')],
194
+ ['play_store_screenshots', 'Play Store screenshots', path.join(targetDir, 'play-store-screenshots')],
195
+ ].forEach(([kind, label, rootPath]) => addRootCandidate(candidates, seen, { kind, label, rootPath }));
196
+
197
+ return candidates;
198
+ }
199
+
200
+ async function scanImageRoot({ targetDir, candidate }) {
201
+ const stat = await statSafe(candidate.path);
202
+ const result = {
203
+ kind: candidate.kind,
204
+ label: candidate.label,
205
+ path: candidate.path,
206
+ relativePath: relativeTo(targetDir, candidate.path),
207
+ exists: Boolean(stat),
208
+ isDirectory: Boolean(stat?.isDirectory()),
209
+ imageFiles: 0,
210
+ extensions: {},
211
+ sample: [],
212
+ truncated: false,
213
+ };
214
+ if (!stat) return result;
215
+
216
+ const addImage = (filePath) => {
217
+ if (result.imageFiles >= MAX_IMAGE_COUNT) {
218
+ result.truncated = true;
219
+ return;
220
+ }
221
+ const ext = path.extname(filePath).toLowerCase();
222
+ if (!IMAGE_EXTENSIONS.has(ext)) return;
223
+ result.imageFiles += 1;
224
+ result.extensions[ext.slice(1)] = (result.extensions[ext.slice(1)] || 0) + 1;
225
+ if (result.sample.length < MAX_SAMPLE_FILES) {
226
+ result.sample.push(relativeTo(candidate.path, filePath));
227
+ }
228
+ };
229
+
230
+ if (stat.isFile()) {
231
+ addImage(candidate.path);
232
+ return result;
233
+ }
234
+ if (!stat.isDirectory()) return result;
235
+
236
+ const walk = async (dirPath, depth) => {
237
+ if (depth < 0 || result.imageFiles >= MAX_IMAGE_COUNT) {
238
+ result.truncated = true;
239
+ return;
240
+ }
241
+ let entries = [];
242
+ try {
243
+ entries = await fs.readdir(dirPath, { withFileTypes: true });
244
+ } catch (err) {
245
+ if (err?.code === 'ENOENT' || err?.code === 'EACCES' || err?.code === 'EPERM') return;
246
+ throw err;
247
+ }
248
+ entries.sort((a, b) => compareStrings(a.name, b.name));
249
+ for (const entry of entries) {
250
+ if (result.imageFiles >= MAX_IMAGE_COUNT) {
251
+ result.truncated = true;
252
+ return;
253
+ }
254
+ const childPath = path.join(dirPath, entry.name);
255
+ if (entry.isDirectory()) {
256
+ if (SKIP_DIRS.has(entry.name)) continue;
257
+ await walk(childPath, depth - 1);
258
+ continue;
259
+ }
260
+ if (entry.isFile()) addImage(childPath);
261
+ }
262
+ };
263
+
264
+ await walk(candidate.path, MAX_SCAN_DEPTH);
265
+ result.sample.sort(compareStrings);
266
+ result.extensions = Object.fromEntries(
267
+ Object.entries(result.extensions).sort(([a], [b]) => compareStrings(a, b))
268
+ );
269
+ return result;
270
+ }
271
+
272
+ async function inspectRoots({ targetDir, configContext }) {
273
+ const candidates = createRootCandidates({ targetDir, configContext });
274
+ const roots = [];
275
+ for (const candidate of candidates) {
276
+ roots.push(await scanImageRoot({ targetDir, candidate }));
277
+ }
278
+ return roots;
279
+ }
280
+
281
+ async function inspectMissingAssets({ targetDir, configContext }) {
282
+ if (!configContext.valid || !configContext.config) return [];
283
+ const refsByPath = new Map();
284
+ collectAssetReferences(configContext.config).forEach((ref) => {
285
+ const existing = refsByPath.get(ref.path) || [];
286
+ existing.push(ref.pointer);
287
+ refsByPath.set(ref.path, existing);
288
+ });
289
+
290
+ const missing = [];
291
+ for (const [assetPath, pointers] of [...refsByPath.entries()].sort(([a], [b]) => compareStrings(a, b))) {
292
+ const absolutePath = path.resolve(configContext.configDir, assetPath);
293
+ const stat = await statSafe(absolutePath);
294
+ if (stat?.isFile()) continue;
295
+ missing.push({
296
+ path: assetPath,
297
+ absolutePath,
298
+ relativePath: relativeTo(targetDir, absolutePath),
299
+ pointers,
300
+ });
301
+ }
302
+ return missing;
303
+ }
304
+
305
+ async function inspectPlaceholderAssets({ targetDir, configContext }) {
306
+ if (!configContext.valid || !configContext.config) return [];
307
+ const placeholder = createSolidPng({
308
+ width: PLACEHOLDER_WIDTH,
309
+ height: PLACEHOLDER_HEIGHT,
310
+ color: PLACEHOLDER_COLOR,
311
+ });
312
+ const refsByPath = new Map();
313
+ collectAssetReferences(configContext.config).forEach((ref) => {
314
+ const existing = refsByPath.get(ref.path) || [];
315
+ existing.push(ref.pointer);
316
+ refsByPath.set(ref.path, existing);
317
+ });
318
+
319
+ const placeholders = [];
320
+ for (const [assetPath, pointers] of [...refsByPath.entries()].sort(([a], [b]) => compareStrings(a, b))) {
321
+ const absolutePath = path.resolve(configContext.configDir, assetPath);
322
+ const stat = await statSafe(absolutePath);
323
+ if (!stat?.isFile()) continue;
324
+ const buffer = await fs.readFile(absolutePath);
325
+ if (buffer.length !== placeholder.length || !buffer.equals(placeholder)) continue;
326
+ placeholders.push({
327
+ path: assetPath,
328
+ absolutePath,
329
+ relativePath: relativeTo(targetDir, absolutePath),
330
+ pointers,
331
+ });
332
+ }
333
+ return placeholders;
334
+ }
335
+
336
+ async function inspectFileSignals({ targetDir, configContext }) {
337
+ const dedupe = new Set();
338
+ const makeSignal = async (kind, filePath) => {
339
+ const resolved = path.resolve(filePath);
340
+ const key = `${kind}:${resolved.toLowerCase()}`;
341
+ if (dedupe.has(key)) return null;
342
+ dedupe.add(key);
343
+ const stat = await statSafe(resolved);
344
+ return {
345
+ kind,
346
+ path: resolved,
347
+ relativePath: relativeTo(targetDir, resolved),
348
+ exists: Boolean(stat?.isFile()),
349
+ };
350
+ };
351
+
352
+ const configDir = configContext.configDir || targetDir;
353
+ const candidates = [
354
+ ['copy_csv', path.join(targetDir, 'copy.csv')],
355
+ ['copy_csv', path.join(configDir, 'copy.csv')],
356
+ ['lockfile', path.join(targetDir, 'shipscreens.lock.json')],
357
+ ['lockfile', path.join(configDir, 'shipscreens.lock.json')],
358
+ ['mcp_config', path.join(targetDir, '.mcp.json')],
359
+ ['mcp_config', path.join(targetDir, '.cursor', 'mcp.json')],
360
+ ['mcp_config', path.join(targetDir, '.vscode', 'mcp.json')],
361
+ ['mcp_config', path.join(targetDir, '.gemini', 'settings.json')],
362
+ ];
363
+
364
+ const signals = [];
365
+ for (const [kind, filePath] of candidates) {
366
+ const signal = await makeSignal(kind, filePath);
367
+ if (signal) signals.push(signal);
368
+ }
369
+ return signals;
370
+ }
371
+
372
+ function inferStoreTarget({ configContext, roots }) {
373
+ if (configContext.valid && configContext.summary) {
374
+ const iosCount = configContext.summary.iosDevices.length;
375
+ const androidCount = configContext.summary.androidDevices.length;
376
+ if (iosCount && androidCount) {
377
+ return {
378
+ profile: 'any',
379
+ confidence: 'high',
380
+ source: 'shipscreens.config.yml devices',
381
+ reasons: [`${iosCount} iOS device(s) and ${androidCount} Android device(s) configured.`],
382
+ };
383
+ }
384
+ if (iosCount) {
385
+ return {
386
+ profile: 'appstore',
387
+ confidence: 'high',
388
+ source: 'shipscreens.config.yml devices',
389
+ reasons: [`${iosCount} iOS device(s) configured and no Android devices configured.`],
390
+ };
391
+ }
392
+ if (androidCount) {
393
+ return {
394
+ profile: 'playstore',
395
+ confidence: 'high',
396
+ source: 'shipscreens.config.yml devices',
397
+ reasons: [`${androidCount} Android device(s) configured and no iOS devices configured.`],
398
+ };
399
+ }
400
+ }
401
+
402
+ const iosImages = roots
403
+ .filter((root) => ['config_ios_output', 'fastlane_ios'].includes(root.kind))
404
+ .reduce((sum, root) => sum + root.imageFiles, 0);
405
+ const androidImages = roots
406
+ .filter((root) => ['config_android_output', 'fastlane_android'].includes(root.kind))
407
+ .reduce((sum, root) => sum + root.imageFiles, 0);
408
+
409
+ if (iosImages && androidImages) {
410
+ return {
411
+ profile: 'any',
412
+ confidence: 'medium',
413
+ source: 'existing Fastlane roots',
414
+ reasons: [`Found ${iosImages} iOS image(s) and ${androidImages} Android image(s) in Fastlane roots.`],
415
+ };
416
+ }
417
+ if (iosImages) {
418
+ return {
419
+ profile: 'appstore',
420
+ confidence: 'medium',
421
+ source: 'existing Fastlane roots',
422
+ reasons: [`Found ${iosImages} iOS image(s) in Fastlane screenshots root.`],
423
+ };
424
+ }
425
+ if (androidImages) {
426
+ return {
427
+ profile: 'playstore',
428
+ confidence: 'medium',
429
+ source: 'existing Fastlane roots',
430
+ reasons: [`Found ${androidImages} Android image(s) in Fastlane metadata root.`],
431
+ };
432
+ }
433
+
434
+ return {
435
+ profile: 'unknown',
436
+ confidence: 'low',
437
+ source: 'not detected',
438
+ reasons: ['No valid ShipScreens config or populated Fastlane roots were detected.'],
439
+ };
440
+ }
441
+
442
+ function buildGuidance({ configContext, roots, missingAssets, placeholderAssets, storeTarget, fileSignals }) {
443
+ const warnings = [];
444
+ const questions = [];
445
+ const nextSteps = [];
446
+
447
+ if (!configContext.exists) {
448
+ warnings.push('No shipscreens.config.yml was detected in the inspected workspace.');
449
+ questions.push('Which store target should be built first: appstore, playstore, or both?');
450
+ questions.push('Which device and locale scope is required for this release?');
451
+ } else if (!configContext.valid) {
452
+ warnings.push(`Config exists but is invalid: ${String(configContext.error || '').split('\n')[0]}`);
453
+ nextSteps.push('Fix shipscreens.config.yml schema errors before build/verify/stage.');
454
+ }
455
+
456
+ if (missingAssets.length) {
457
+ warnings.push(`${missingAssets.length} referenced asset path(s) are missing.`);
458
+ nextSteps.push('Replace missing PNG assets or update their paths in shipscreens.config.yml.');
459
+ }
460
+
461
+ if (placeholderAssets.length) {
462
+ warnings.push(`${placeholderAssets.length} config-referenced placeholder PNG(s) still need real screenshots.`);
463
+ nextSteps.push('Run shipscreens import-screenshots --from <dir> --json or replace placeholder PNGs under assets/.');
464
+ }
465
+
466
+ const existingImageRoots = roots.filter((root) => root.exists && root.imageFiles > 0);
467
+ if (!existingImageRoots.length) {
468
+ warnings.push('No existing screenshot/image root with image files was detected.');
469
+ questions.push('Where are the current base screenshots or Fastlane output folders?');
470
+ nextSteps.push(
471
+ 'If a real device or emulator is already showing the app, run shipscreens capture-screenshots --platform auto --out screenshots --slot 1 --json for each panel, then import-screenshots.'
472
+ );
473
+ }
474
+
475
+ if (storeTarget.profile === 'unknown') {
476
+ questions.push('Should the first proof target App Store, Google Play, or both?');
477
+ }
478
+
479
+ const hasCopyCsv = fileSignals.some((signal) => signal.kind === 'copy_csv' && signal.exists);
480
+ if (configContext.valid && !hasCopyCsv) {
481
+ questions.push('Is the panel copy already final in shipscreens.config.yml, or should the agent create/import copy.csv?');
482
+ }
483
+
484
+ if (!configContext.exists) {
485
+ nextSteps.push('Run shipscreens write-starter-config --profile <appstore|playstore|any> --json to create the starter config.');
486
+ } else if (configContext.valid && !missingAssets.length) {
487
+ const profile = storeTarget.profile === 'unknown' ? 'any' : storeTarget.profile;
488
+ nextSteps.push(`Run shipscreens plan --profile ${profile} --json to preview output paths.`);
489
+ nextSteps.push(`Run shipscreens agent-run --mode raw --zip --out out/agent --stage --profile ${profile}.`);
490
+ }
491
+
492
+ nextSteps.push('Return the staged root plus shipscreens.agent-report.json / shipscreens.agent-summary.md to the human reviewer.');
493
+
494
+ return {
495
+ warnings: [...new Set(warnings)],
496
+ questions: [...new Set(questions)],
497
+ nextSteps: [...new Set(nextSteps)],
498
+ };
499
+ }
500
+
501
+ function buildAgentHandoff({ storeTarget }) {
502
+ const profile = storeTarget.profile === 'unknown' ? '<appstore|playstore|any>' : storeTarget.profile;
503
+ return (
504
+ 'Ship release-safe screenshots from this repo. First inspect release context, then use ShipScreens MCP or CLI ' +
505
+ `to write/update config and copy if needed, run validate/build/verify/stage with profile=${profile}, ` +
506
+ 'and return the staged upload root plus shipscreens.agent-report.json / shipscreens.agent-summary.md. ' +
507
+ 'Ask only if store target, device/locale scope, or panel #1 messaging is ambiguous.'
508
+ );
509
+ }
510
+
511
+ export async function runInspectReleaseContext({ dir, configPath } = {}) {
512
+ const targetDir = path.resolve(process.cwd(), dir || '.');
513
+ const targetStat = await statSafe(targetDir);
514
+ if (!targetStat?.isDirectory()) {
515
+ throw new Error(`Workspace directory not found: ${targetDir}`);
516
+ }
517
+
518
+ const configContext = await loadConfigContext({ targetDir, configPath });
519
+ const roots = await inspectRoots({ targetDir, configContext });
520
+ const missingAssets = await inspectMissingAssets({ targetDir, configContext });
521
+ const placeholderAssets = await inspectPlaceholderAssets({ targetDir, configContext });
522
+ const fileSignals = await inspectFileSignals({ targetDir, configContext });
523
+ const storeTarget = inferStoreTarget({ configContext, roots });
524
+ const guidance = buildGuidance({
525
+ configContext,
526
+ roots,
527
+ missingAssets,
528
+ placeholderAssets,
529
+ storeTarget,
530
+ fileSignals,
531
+ });
532
+
533
+ return {
534
+ version: 1,
535
+ generatedAt: new Date().toISOString(),
536
+ workspace: targetDir,
537
+ config: {
538
+ path: configContext.path,
539
+ relativePath: configContext.relativePath,
540
+ exists: configContext.exists,
541
+ valid: configContext.valid,
542
+ error: configContext.error,
543
+ summary: configContext.summary,
544
+ },
545
+ storeTarget,
546
+ screenshotRoots: roots,
547
+ missingAssets,
548
+ placeholderAssets,
549
+ files: fileSignals,
550
+ warnings: guidance.warnings,
551
+ questions: guidance.questions,
552
+ nextSteps: guidance.nextSteps,
553
+ agentHandoff: buildAgentHandoff({ storeTarget }),
554
+ };
555
+ }
556
+
557
+ export function renderInspectReleaseContextSummary(result) {
558
+ const existingRoots = result.screenshotRoots
559
+ .filter((root) => root.exists && root.imageFiles > 0)
560
+ .sort((a, b) => compareStrings(a.relativePath, b.relativePath));
561
+ const signals = result.files.filter((signal) => signal.exists).sort((a, b) => compareStrings(a.relativePath, b.relativePath));
562
+
563
+ const lines = [
564
+ 'ShipScreens release context',
565
+ `- Workspace: ${result.workspace}`,
566
+ `- Config: ${result.config.exists ? result.config.relativePath : 'not found'} (${result.config.valid ? 'valid' : 'not ready'})`,
567
+ `- Store target: ${result.storeTarget.profile} (${result.storeTarget.confidence}, ${result.storeTarget.source})`,
568
+ ];
569
+
570
+ if (result.config.summary) {
571
+ lines.push(
572
+ `- Locales: ${result.config.summary.locales.join(', ') || 'none'}`,
573
+ `- Slides: ${result.config.summary.slideCount}`,
574
+ `- iOS devices: ${result.config.summary.iosDevices.join(', ') || 'none'}`,
575
+ `- Android devices: ${result.config.summary.androidDevices.map((d) => `${d.id}:${d.bucket}`).join(', ') || 'none'}`
576
+ );
577
+ }
578
+
579
+ lines.push('', 'Existing screenshot roots:');
580
+ if (existingRoots.length) {
581
+ existingRoots.slice(0, 8).forEach((root) => {
582
+ lines.push(`- ${root.relativePath}: ${root.imageFiles} image(s)`);
583
+ });
584
+ } else {
585
+ lines.push('- none detected');
586
+ }
587
+
588
+ lines.push('', 'File signals:');
589
+ if (signals.length) {
590
+ signals.forEach((signal) => lines.push(`- ${signal.kind}: ${signal.relativePath}`));
591
+ } else {
592
+ lines.push('- none detected');
593
+ }
594
+
595
+ lines.push('', 'Warnings:');
596
+ if (result.warnings.length) {
597
+ result.warnings.forEach((warning) => lines.push(`- ${warning}`));
598
+ } else {
599
+ lines.push('- none');
600
+ }
601
+
602
+ lines.push('', 'Next steps:');
603
+ result.nextSteps.forEach((step) => lines.push(`- ${step}`));
604
+
605
+ lines.push('', 'Agent handoff:', result.agentHandoff);
606
+ return `${lines.join('\n')}\n`;
607
+ }