@hominis/fireforge 0.34.2 → 0.36.0

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 (360) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +43 -5
  3. package/dist/bin/fireforge.d.ts +4 -0
  4. package/dist/bin/fireforge.js +25 -6
  5. package/dist/src/cli.js +22 -5
  6. package/dist/src/commands/bootstrap-checks.js +0 -1
  7. package/dist/src/commands/bootstrap.d.ts +2 -1
  8. package/dist/src/commands/bootstrap.js +16 -7
  9. package/dist/src/commands/build.js +17 -3
  10. package/dist/src/commands/config.js +30 -10
  11. package/dist/src/commands/discard.js +24 -5
  12. package/dist/src/commands/doctor/post-rebase-audit.js +0 -1
  13. package/dist/src/commands/doctor-check-core.js +0 -1
  14. package/dist/src/commands/doctor-external-toolchains.d.ts +3 -0
  15. package/dist/src/commands/doctor-external-toolchains.js +54 -0
  16. package/dist/src/commands/doctor-furnace-jar.js +0 -1
  17. package/dist/src/commands/doctor-furnace-manifest-sync.js +0 -1
  18. package/dist/src/commands/doctor-furnace.js +2 -2
  19. package/dist/src/commands/doctor-working-tree.js +0 -1
  20. package/dist/src/commands/doctor.js +4 -1
  21. package/dist/src/commands/download.js +51 -15
  22. package/dist/src/commands/export-all.js +5 -3
  23. package/dist/src/commands/export-flow.js +12 -7
  24. package/dist/src/commands/export-placement-gate.js +0 -1
  25. package/dist/src/commands/export-placement-policy.js +0 -1
  26. package/dist/src/commands/export-shared.d.ts +5 -1
  27. package/dist/src/commands/export-shared.js +18 -6
  28. package/dist/src/commands/export.js +15 -10
  29. package/dist/src/commands/furnace/apply.js +22 -6
  30. package/dist/src/commands/furnace/chrome-doc-remove.js +0 -1
  31. package/dist/src/commands/furnace/chrome-doc-templates.js +0 -1
  32. package/dist/src/commands/furnace/chrome-doc-tests.js +0 -1
  33. package/dist/src/commands/furnace/chrome-doc.js +0 -1
  34. package/dist/src/commands/furnace/create-browser-test.js +0 -1
  35. package/dist/src/commands/furnace/create-dry-run.js +0 -1
  36. package/dist/src/commands/furnace/create-features.js +0 -1
  37. package/dist/src/commands/furnace/create-mochikit.js +0 -1
  38. package/dist/src/commands/furnace/create-readback.js +0 -1
  39. package/dist/src/commands/furnace/create-templates.js +0 -1
  40. package/dist/src/commands/furnace/create-validation.js +0 -1
  41. package/dist/src/commands/furnace/create-xpcshell.js +0 -1
  42. package/dist/src/commands/furnace/create.js +0 -1
  43. package/dist/src/commands/furnace/deploy.js +7 -83
  44. package/dist/src/commands/furnace/diff.js +0 -1
  45. package/dist/src/commands/furnace/index.js +59 -25
  46. package/dist/src/commands/furnace/init.js +0 -1
  47. package/dist/src/commands/furnace/list.js +0 -1
  48. package/dist/src/commands/furnace/override.js +0 -1
  49. package/dist/src/commands/furnace/preview.js +3 -3
  50. package/dist/src/commands/furnace/refresh.js +0 -1
  51. package/dist/src/commands/furnace/remove-state.d.ts +15 -0
  52. package/dist/src/commands/furnace/remove-state.js +47 -1
  53. package/dist/src/commands/furnace/remove.js +3 -9
  54. package/dist/src/commands/furnace/rename-browser-test.js +1 -5
  55. package/dist/src/commands/furnace/rename-helpers.d.ts +6 -0
  56. package/dist/src/commands/furnace/rename-helpers.js +30 -1
  57. package/dist/src/commands/furnace/rename-xpcshell.js +7 -6
  58. package/dist/src/commands/furnace/rename.js +15 -36
  59. package/dist/src/commands/furnace/scan.js +0 -1
  60. package/dist/src/commands/furnace/status.js +0 -1
  61. package/dist/src/commands/furnace/sync.js +76 -4
  62. package/dist/src/commands/furnace/validate.js +0 -1
  63. package/dist/src/commands/furnace/validation-output.js +0 -1
  64. package/dist/src/commands/import.js +35 -9
  65. package/dist/src/commands/lint-per-patch.js +19 -7
  66. package/dist/src/commands/lint.js +13 -3
  67. package/dist/src/commands/manifest.js +1 -2
  68. package/dist/src/commands/package.js +2 -1
  69. package/dist/src/commands/patch/compact.js +1 -1
  70. package/dist/src/commands/patch/delete.js +1 -1
  71. package/dist/src/commands/patch/index.js +1 -1
  72. package/dist/src/commands/patch/lint-ignore.js +21 -6
  73. package/dist/src/commands/patch/move-files.js +1 -1
  74. package/dist/src/commands/patch/patch-context.js +0 -1
  75. package/dist/src/commands/patch/rename.js +4 -4
  76. package/dist/src/commands/patch/reorder.js +3 -4
  77. package/dist/src/commands/patch/split-plan.js +2 -1
  78. package/dist/src/commands/patch/split.js +4 -4
  79. package/dist/src/commands/patch/staged-dependency.js +1 -1
  80. package/dist/src/commands/patch/tier.js +18 -5
  81. package/dist/src/commands/re-export-bulk-scan.js +0 -1
  82. package/dist/src/commands/re-export-files.js +1 -1
  83. package/dist/src/commands/re-export-options.js +0 -1
  84. package/dist/src/commands/re-export-scan.js +0 -1
  85. package/dist/src/commands/re-export.js +6 -4
  86. package/dist/src/commands/rebase/abort.js +2 -9
  87. package/dist/src/commands/rebase/confirm.js +0 -1
  88. package/dist/src/commands/rebase/conflict-summary.js +0 -1
  89. package/dist/src/commands/rebase/continue.js +0 -1
  90. package/dist/src/commands/rebase/index.d.ts +1 -1
  91. package/dist/src/commands/rebase/index.js +10 -15
  92. package/dist/src/commands/rebase/patch-loop.js +38 -7
  93. package/dist/src/commands/rebase/summary.js +2 -3
  94. package/dist/src/commands/rebase.js +0 -1
  95. package/dist/src/commands/register.js +1 -1
  96. package/dist/src/commands/reset.js +16 -9
  97. package/dist/src/commands/resolve.js +2 -2
  98. package/dist/src/commands/run.js +18 -6
  99. package/dist/src/commands/setup-support.js +0 -1
  100. package/dist/src/commands/setup.js +1 -2
  101. package/dist/src/commands/source.js +1 -2
  102. package/dist/src/commands/status-output.js +1 -12
  103. package/dist/src/commands/status.js +147 -133
  104. package/dist/src/commands/test-appdir.js +0 -1
  105. package/dist/src/commands/test-diagnose.js +18 -2
  106. package/dist/src/commands/test-modes.d.ts +15 -0
  107. package/dist/src/commands/test-modes.js +61 -0
  108. package/dist/src/commands/test-register.d.ts +4 -0
  109. package/dist/src/commands/test-register.js +64 -0
  110. package/dist/src/commands/test-run.d.ts +23 -7
  111. package/dist/src/commands/test-run.js +15 -14
  112. package/dist/src/commands/test.d.ts +0 -4
  113. package/dist/src/commands/test.js +95 -56
  114. package/dist/src/commands/token-coverage.js +0 -1
  115. package/dist/src/commands/token.js +1 -2
  116. package/dist/src/commands/typecheck.js +1 -1
  117. package/dist/src/commands/verify.js +3 -9
  118. package/dist/src/commands/watch.js +5 -1
  119. package/dist/src/commands/wire.js +1 -1
  120. package/dist/src/core/ast-utils.js +0 -1
  121. package/dist/src/core/brand-validation.js +0 -1
  122. package/dist/src/core/branding.js +0 -1
  123. package/dist/src/core/browser-wire.js +0 -1
  124. package/dist/src/core/build-audit-platform.js +0 -1
  125. package/dist/src/core/build-audit-registration.js +0 -1
  126. package/dist/src/core/build-audit-resolve.js +0 -1
  127. package/dist/src/core/build-audit-transforms.js +0 -1
  128. package/dist/src/core/build-audit.js +0 -1
  129. package/dist/src/core/build-baseline-types.js +0 -1
  130. package/dist/src/core/build-baseline.js +0 -1
  131. package/dist/src/core/build-prepare.js +21 -7
  132. package/dist/src/core/config-mutate.js +0 -1
  133. package/dist/src/core/config-paths.d.ts +2 -2
  134. package/dist/src/core/config-paths.js +6 -1
  135. package/dist/src/core/config-state.js +0 -1
  136. package/dist/src/core/config-validate-patch-policy.js +0 -1
  137. package/dist/src/core/config-validate-test-toolchains.d.ts +8 -0
  138. package/dist/src/core/config-validate-test-toolchains.js +105 -0
  139. package/dist/src/core/config-validate.js +3 -1
  140. package/dist/src/core/config.js +0 -1
  141. package/dist/src/core/destructive.js +0 -1
  142. package/dist/src/core/diff-hunks.js +13 -7
  143. package/dist/src/core/engine-changes.js +0 -1
  144. package/dist/src/core/engine-session-lock.d.ts +12 -0
  145. package/dist/src/core/engine-session-lock.js +45 -0
  146. package/dist/src/core/file-lock.d.ts +8 -0
  147. package/dist/src/core/file-lock.js +157 -48
  148. package/dist/src/core/firefox-archive.d.ts +4 -0
  149. package/dist/src/core/firefox-archive.js +6 -1
  150. package/dist/src/core/firefox-cache.js +67 -8
  151. package/dist/src/core/firefox-download.js +0 -1
  152. package/dist/src/core/firefox-extract.d.ts +21 -1
  153. package/dist/src/core/firefox-extract.js +178 -9
  154. package/dist/src/core/firefox-ignorefile.js +0 -1
  155. package/dist/src/core/firefox.d.ts +12 -0
  156. package/dist/src/core/firefox.js +64 -2
  157. package/dist/src/core/furnace-apply-dry-run.js +0 -1
  158. package/dist/src/core/furnace-apply-ftl.d.ts +7 -3
  159. package/dist/src/core/furnace-apply-ftl.js +10 -5
  160. package/dist/src/core/furnace-apply-helpers.js +0 -1
  161. package/dist/src/core/furnace-apply-output.js +2 -2
  162. package/dist/src/core/furnace-apply.d.ts +7 -1
  163. package/dist/src/core/furnace-apply.js +47 -13
  164. package/dist/src/core/furnace-checksum-utils.js +0 -1
  165. package/dist/src/core/furnace-config-array-utils.js +0 -1
  166. package/dist/src/core/furnace-config-custom.js +0 -1
  167. package/dist/src/core/furnace-config-order.js +0 -1
  168. package/dist/src/core/furnace-config-tokens.js +0 -1
  169. package/dist/src/core/furnace-config-validate.js +0 -1
  170. package/dist/src/core/furnace-config.d.ts +11 -0
  171. package/dist/src/core/furnace-config.js +37 -1
  172. package/dist/src/core/furnace-constants.js +0 -1
  173. package/dist/src/core/furnace-css-fragments.js +0 -1
  174. package/dist/src/core/furnace-graph-utils.js +0 -1
  175. package/dist/src/core/furnace-jsconfig.d.ts +2 -3
  176. package/dist/src/core/furnace-jsconfig.js +25 -18
  177. package/dist/src/core/furnace-marker.js +0 -1
  178. package/dist/src/core/furnace-operation.js +0 -1
  179. package/dist/src/core/furnace-refresh.js +0 -1
  180. package/dist/src/core/furnace-registration-ast.d.ts +14 -0
  181. package/dist/src/core/furnace-registration-ast.js +1 -2
  182. package/dist/src/core/furnace-registration-remove.js +0 -1
  183. package/dist/src/core/furnace-registration-validate.js +1 -5
  184. package/dist/src/core/furnace-registration.js +0 -1
  185. package/dist/src/core/furnace-rollback.js +0 -1
  186. package/dist/src/core/furnace-scanner.js +0 -1
  187. package/dist/src/core/furnace-staleness.js +0 -1
  188. package/dist/src/core/furnace-state-persist.d.ts +72 -0
  189. package/dist/src/core/furnace-state-persist.js +118 -0
  190. package/dist/src/core/furnace-step-errors.d.ts +23 -0
  191. package/dist/src/core/furnace-step-errors.js +25 -0
  192. package/dist/src/core/furnace-stories.js +0 -1
  193. package/dist/src/core/furnace-validate-accessibility.js +0 -1
  194. package/dist/src/core/furnace-validate-checks.js +0 -1
  195. package/dist/src/core/furnace-validate-compatibility.js +0 -1
  196. package/dist/src/core/furnace-validate-helpers.js +0 -1
  197. package/dist/src/core/furnace-validate-registration.js +7 -7
  198. package/dist/src/core/furnace-validate-structure.d.ts +8 -0
  199. package/dist/src/core/furnace-validate-structure.js +11 -2
  200. package/dist/src/core/furnace-validate.js +0 -1
  201. package/dist/src/core/furnace-version-drift.d.ts +6 -3
  202. package/dist/src/core/furnace-version-drift.js +6 -4
  203. package/dist/src/core/git-base.js +0 -1
  204. package/dist/src/core/git-diff.js +0 -1
  205. package/dist/src/core/git-file-ops.js +0 -1
  206. package/dist/src/core/git-status.d.ts +13 -0
  207. package/dist/src/core/git-status.js +32 -2
  208. package/dist/src/core/git.d.ts +10 -1
  209. package/dist/src/core/git.js +65 -8
  210. package/dist/src/core/license-headers.d.ts +16 -0
  211. package/dist/src/core/license-headers.js +19 -1
  212. package/dist/src/core/lint-cache.js +0 -1
  213. package/dist/src/core/lint-projection.js +0 -1
  214. package/dist/src/core/mach-build-artifacts.js +0 -1
  215. package/dist/src/core/mach-error-hints.js +27 -1
  216. package/dist/src/core/mach-mozconfig.js +0 -1
  217. package/dist/src/core/mach-python.js +0 -1
  218. package/dist/src/core/mach-resource-shim.js +0 -1
  219. package/dist/src/core/mach.js +0 -1
  220. package/dist/src/core/manifest-helpers.js +0 -1
  221. package/dist/src/core/manifest-register.js +0 -1
  222. package/dist/src/core/manifest-rules.js +0 -1
  223. package/dist/src/core/manifest-tokenizers.js +0 -1
  224. package/dist/src/core/marionette-port.d.ts +8 -0
  225. package/dist/src/core/marionette-port.js +34 -3
  226. package/dist/src/core/marionette-preflight.js +0 -1
  227. package/dist/src/core/markdown-table.js +0 -1
  228. package/dist/src/core/ownership-table.js +2 -9
  229. package/dist/src/core/parser-fallback.js +0 -1
  230. package/dist/src/core/patch-apply-fuzz.d.ts +27 -9
  231. package/dist/src/core/patch-apply-fuzz.js +71 -34
  232. package/dist/src/core/patch-apply.d.ts +30 -0
  233. package/dist/src/core/patch-apply.js +152 -39
  234. package/dist/src/core/patch-artifact-normalize.d.ts +18 -5
  235. package/dist/src/core/patch-artifact-normalize.js +19 -15
  236. package/dist/src/core/patch-export-coverage.js +0 -1
  237. package/dist/src/core/patch-export-metadata.js +0 -1
  238. package/dist/src/core/patch-export-update.js +2 -3
  239. package/dist/src/core/patch-export.d.ts +2 -6
  240. package/dist/src/core/patch-export.js +24 -48
  241. package/dist/src/core/patch-files.js +0 -1
  242. package/dist/src/core/patch-identifier-suggest.js +0 -1
  243. package/dist/src/core/patch-lint-checkjs.js +0 -1
  244. package/dist/src/core/patch-lint-chrome-jsdoc.js +0 -1
  245. package/dist/src/core/patch-lint-cross.js +0 -1
  246. package/dist/src/core/patch-lint-css.js +0 -1
  247. package/dist/src/core/patch-lint-diff-tag.js +0 -1
  248. package/dist/src/core/patch-lint-diff.d.ts +2 -1
  249. package/dist/src/core/patch-lint-diff.js +17 -32
  250. package/dist/src/core/patch-lint-imports.js +0 -1
  251. package/dist/src/core/patch-lint-jsdoc.js +0 -1
  252. package/dist/src/core/patch-lint-observer.js +0 -1
  253. package/dist/src/core/patch-lint-ownership.js +0 -1
  254. package/dist/src/core/patch-lint-reexports.js +0 -1
  255. package/dist/src/core/patch-lint.d.ts +37 -2
  256. package/dist/src/core/patch-lint.js +48 -24
  257. package/dist/src/core/patch-lock.js +0 -1
  258. package/dist/src/core/patch-manifest-consistency.js +9 -1
  259. package/dist/src/core/patch-manifest-io.d.ts +16 -0
  260. package/dist/src/core/patch-manifest-io.js +37 -12
  261. package/dist/src/core/patch-manifest-query.d.ts +6 -0
  262. package/dist/src/core/patch-manifest-query.js +27 -12
  263. package/dist/src/core/patch-manifest-resolve.js +0 -1
  264. package/dist/src/core/patch-manifest-validate.js +0 -1
  265. package/dist/src/core/patch-manifest.d.ts +1 -1
  266. package/dist/src/core/patch-manifest.js +1 -2
  267. package/dist/src/core/patch-parse.d.ts +59 -0
  268. package/dist/src/core/patch-parse.js +209 -92
  269. package/dist/src/core/patch-policy.js +0 -1
  270. package/dist/src/core/patch-registration-refs.js +16 -26
  271. package/dist/src/core/patch-source-metadata.js +0 -1
  272. package/dist/src/core/patch-transform.d.ts +4 -0
  273. package/dist/src/core/patch-transform.js +30 -38
  274. package/dist/src/core/rebase-session.js +0 -1
  275. package/dist/src/core/register-browser-content.js +0 -1
  276. package/dist/src/core/register-module.js +0 -1
  277. package/dist/src/core/register-result.js +0 -1
  278. package/dist/src/core/register-scaffold.js +0 -1
  279. package/dist/src/core/register-shared-css.js +0 -1
  280. package/dist/src/core/register-test-manifest.js +0 -1
  281. package/dist/src/core/register-xpcshell-test.js +0 -1
  282. package/dist/src/core/shared-ftl.js +0 -1
  283. package/dist/src/core/signal-critical.js +0 -1
  284. package/dist/src/core/smoke-patterns.js +0 -1
  285. package/dist/src/core/state-file.js +0 -1
  286. package/dist/src/core/status-classify.d.ts +16 -0
  287. package/dist/src/core/status-classify.js +24 -16
  288. package/dist/src/core/test-harness-crash.d.ts +64 -7
  289. package/dist/src/core/test-harness-crash.js +196 -15
  290. package/dist/src/core/test-harness-output.js +0 -1
  291. package/dist/src/core/test-path-scope.d.ts +62 -0
  292. package/dist/src/core/test-path-scope.js +148 -0
  293. package/dist/src/core/test-stale-check.js +0 -1
  294. package/dist/src/core/test-stale-symlink.js +0 -1
  295. package/dist/src/core/test-xpcshell-retry.js +0 -1
  296. package/dist/src/core/token-coverage.js +0 -1
  297. package/dist/src/core/token-dark-mode.js +0 -1
  298. package/dist/src/core/token-docs.js +0 -1
  299. package/dist/src/core/token-manager.js +0 -1
  300. package/dist/src/core/token-scaffold.js +0 -1
  301. package/dist/src/core/token-variant.js +0 -1
  302. package/dist/src/core/toolchain-preflight.d.ts +85 -0
  303. package/dist/src/core/toolchain-preflight.js +256 -0
  304. package/dist/src/core/typecheck-shim.d.ts +4 -1
  305. package/dist/src/core/typecheck-shim.js +61 -18
  306. package/dist/src/core/typecheck.js +0 -1
  307. package/dist/src/core/wire-destroy.js +0 -1
  308. package/dist/src/core/wire-dom-fragment.js +0 -1
  309. package/dist/src/core/wire-init.js +0 -1
  310. package/dist/src/core/wire-subscript.js +0 -1
  311. package/dist/src/core/wire-targets.js +0 -1
  312. package/dist/src/core/wire-utils.js +0 -1
  313. package/dist/src/core/xpcshell-appdir.js +1 -4
  314. package/dist/src/errors/base.d.ts +29 -0
  315. package/dist/src/errors/base.js +35 -1
  316. package/dist/src/errors/build.js +0 -1
  317. package/dist/src/errors/codes.d.ts +6 -0
  318. package/dist/src/errors/codes.js +6 -1
  319. package/dist/src/errors/config.js +0 -1
  320. package/dist/src/errors/download.js +2 -1
  321. package/dist/src/errors/furnace.d.ts +11 -1
  322. package/dist/src/errors/furnace.js +6 -5
  323. package/dist/src/errors/git.js +0 -1
  324. package/dist/src/errors/patch.d.ts +17 -0
  325. package/dist/src/errors/patch.js +29 -1
  326. package/dist/src/errors/rebase.js +0 -1
  327. package/dist/src/errors/run.js +1 -1
  328. package/dist/src/index.js +0 -1
  329. package/dist/src/types/cli.js +0 -1
  330. package/dist/src/types/commands/index.js +0 -1
  331. package/dist/src/types/commands/options.d.ts +20 -5
  332. package/dist/src/types/commands/options.js +0 -1
  333. package/dist/src/types/commands/patches.d.ts +8 -0
  334. package/dist/src/types/commands/patches.js +0 -1
  335. package/dist/src/types/commands/project.d.ts +11 -2
  336. package/dist/src/types/commands/project.js +0 -1
  337. package/dist/src/types/config.d.ts +32 -0
  338. package/dist/src/types/config.js +0 -1
  339. package/dist/src/types/furnace.d.ts +10 -0
  340. package/dist/src/types/furnace.js +0 -1
  341. package/dist/src/types/index.js +0 -1
  342. package/dist/src/types/typecheck.js +0 -1
  343. package/dist/src/utils/elapsed.js +0 -1
  344. package/dist/src/utils/errors.js +0 -1
  345. package/dist/src/utils/fs.js +37 -5
  346. package/dist/src/utils/logger.d.ts +7 -0
  347. package/dist/src/utils/logger.js +85 -3
  348. package/dist/src/utils/options.d.ts +15 -0
  349. package/dist/src/utils/options.js +25 -1
  350. package/dist/src/utils/package-root.js +11 -4
  351. package/dist/src/utils/parse.js +0 -1
  352. package/dist/src/utils/paths.js +0 -1
  353. package/dist/src/utils/platform.js +0 -1
  354. package/dist/src/utils/process.d.ts +33 -4
  355. package/dist/src/utils/process.js +183 -28
  356. package/dist/src/utils/regex.d.ts +13 -3
  357. package/dist/src/utils/regex.js +53 -4
  358. package/dist/src/utils/validation.d.ts +0 -12
  359. package/dist/src/utils/validation.js +13 -26
  360. package/package.json +8 -7
@@ -78,4 +78,3 @@ export async function withDryRunReExportLock(fireforgeDir, isDryRun, operation)
78
78
  onStaleLockMessage: (ageMs) => `Removing stale FireForge re-export dry-run lock (age: ${Math.round(ageMs / 1000)}s). A previous dry-run process may have crashed.`,
79
79
  });
80
80
  }
81
- //# sourceMappingURL=re-export-bulk-scan.js.map
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join } from 'node:path';
3
+ import { getProjectPaths } from '../core/config.js';
3
4
  import { appendHistory, confirmDestructive } from '../core/destructive.js';
4
5
  import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
5
6
  import { computeProjectedLintRegressions } from '../core/lint-projection.js';
@@ -260,4 +261,3 @@ export async function reExportFilesInPlace(paths, selectedPatches, options, conf
260
261
  success(`Re-exported ${target.filename}`);
261
262
  outro('Re-export complete');
262
263
  }
263
- //# sourceMappingURL=re-export-files.js.map
@@ -83,4 +83,3 @@ export function validateReExportOptionCombinations(patches, options) {
83
83
  throw new InvalidArgumentError('--tier and --lint-ignore require explicit patch identifiers and cannot be combined with --all (different patches typically need different metadata).', '--all');
84
84
  }
85
85
  }
86
- //# sourceMappingURL=re-export-options.js.map
@@ -144,4 +144,3 @@ export function assertScanFileAdditionsHaveDiffHunks(args) {
144
144
  return;
145
145
  throw new InvalidArgumentError(`Refusing to re-export ${patchFilename} with --scan-file because ${noDiffScanFiles.length} explicit added path${noDiffScanFiles.length === 1 ? '' : 's'} produced no diff hunks (${noDiffScanFiles.join(', ')}). Remove unchanged paths or modify them before retrying.`, '--scan-file');
146
146
  }
147
- //# sourceMappingURL=re-export-scan.js.map
@@ -1,8 +1,10 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
+ /* eslint-disable max-lines -- Re-export is at the guardrail; splitting register plumbing is unrelated to this fix. */
2
3
  import { dirname, join } from 'node:path';
3
4
  import { multiselect } from '@clack/prompts';
4
- import { Option } from 'commander';
5
+ import { Command, Option } from 'commander';
5
6
  import { getProjectPaths, loadConfig } from '../core/config.js';
7
+ import { withEngineSessionLock } from '../core/engine-session-lock.js';
6
8
  import { isGitRepository } from '../core/git.js';
7
9
  import { getDiffForFilesAgainstHead } from '../core/git-diff.js';
8
10
  import { getModifiedFilesInDir, getUntrackedFilesInDir } from '../core/git-status.js';
@@ -421,13 +423,13 @@ export function registerReExport(program, { getProjectRoot, withErrorHandling })
421
423
  .option('--lint-ignore <check-id>', 'Append a lint check ID to the patch\'s PatchMetadata.lintIgnore (union, de-duped, repeatable). Mutually exclusive with --all. Use "fireforge patch lint-ignore" for --remove / --clear.', (value, prev) => [...prev, value], [])
422
424
  .action(withErrorHandling(async (patches, options) => {
423
425
  const { tier, lintIgnore, scanFile, scanFiles, ...rest } = options;
424
- await reExportCommand(getProjectRoot(), patches, {
426
+ const projectRoot = getProjectRoot();
427
+ await withEngineSessionLock(projectRoot, 're-export', () => reExportCommand(projectRoot, patches, {
425
428
  ...pickDefined(rest),
426
429
  ...(scanFile !== undefined && scanFile.length > 0 ? { scanFiles: scanFile } : {}),
427
430
  ...(scanFiles !== undefined ? { scanFilesManifest: scanFiles } : {}),
428
431
  ...(tier !== undefined ? { tier: tier } : {}),
429
432
  ...(lintIgnore !== undefined && lintIgnore.length > 0 ? { lintIgnore } : {}),
430
- });
433
+ }));
431
434
  }));
432
435
  }
433
- //# sourceMappingURL=re-export.js.map
@@ -3,11 +3,10 @@
3
3
  * Rebase abort flow.
4
4
  */
5
5
  import { getProjectPaths, updateState } from '../../core/config.js';
6
- import { getFurnacePaths, updateFurnaceState } from '../../core/furnace-config.js';
6
+ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
7
7
  import { resetChanges } from '../../core/git.js';
8
8
  import { clearRebaseSession, loadRebaseSession } from '../../core/rebase-session.js';
9
9
  import { NoRebaseSessionError } from '../../errors/rebase.js';
10
- import { pathExists } from '../../utils/fs.js';
11
10
  import { intro, outro, spinner, success } from '../../utils/logger.js';
12
11
  import { confirmDirtyEngineReset } from './confirm.js';
13
12
  /**
@@ -47,12 +46,7 @@ export async function handleAbort(projectRoot, yes) {
47
46
  // misleading now that reset already succeeded). The rebase session is
48
47
  // still kept on disk so the user can retry the abort and let it
49
48
  // idempotently re-clear furnace state.
50
- const furnacePaths = getFurnacePaths(projectRoot);
51
- if (await pathExists(furnacePaths.furnaceState)) {
52
- await updateFurnaceState(projectRoot, (current) => ({
53
- ...(current.pendingRepair ? { pendingRepair: current.pendingRepair } : {}),
54
- }));
55
- }
49
+ await clearAppliedFurnaceState(projectRoot);
56
50
  // Step 3: clear pending resolution transactionally.
57
51
  await updateState(projectRoot, (current) => {
58
52
  if (!current.pendingResolution)
@@ -67,4 +61,3 @@ export async function handleAbort(projectRoot, yes) {
67
61
  success('Rebase aborted and session cleared.');
68
62
  outro('Rebase aborted');
69
63
  }
70
- //# sourceMappingURL=abort.js.map
@@ -30,4 +30,3 @@ export async function confirmDirtyEngineReset({ engineDir, yes, nonInteractiveCo
30
30
  }
31
31
  return true;
32
32
  }
33
- //# sourceMappingURL=confirm.js.map
@@ -35,4 +35,3 @@ export function buildRebaseConflictSummary(args) {
35
35
  ],
36
36
  };
37
37
  }
38
- //# sourceMappingURL=conflict-summary.js.map
@@ -104,4 +104,3 @@ export async function handleContinue(projectRoot, maxFuzz) {
104
104
  // Continue applying remaining patches
105
105
  await runPatchLoop(projectRoot, session, paths, maxFuzz);
106
106
  }
107
- //# sourceMappingURL=continue.js.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Orchestrates the full patch-rebase workflow:
5
5
  * 1. Reset engine to baseline
6
- * 2. Apply each patch with escalating fuzz
6
+ * 2. Apply each patch with escalating context reduction (git apply -C<n>)
7
7
  * 3. Pause on failures for manual resolution
8
8
  * 4. Re-export successfully applied patches with the new version stamp
9
9
  *
@@ -4,14 +4,15 @@
4
4
  *
5
5
  * Orchestrates the full patch-rebase workflow:
6
6
  * 1. Reset engine to baseline
7
- * 2. Apply each patch with escalating fuzz
7
+ * 2. Apply each patch with escalating context reduction (git apply -C<n>)
8
8
  * 3. Pause on failures for manual resolution
9
9
  * 4. Re-export successfully applied patches with the new version stamp
10
10
  *
11
11
  * Supports `--continue` (resume after manual fix) and `--abort` (cancel).
12
12
  */
13
+ import { Command } from 'commander';
13
14
  import { getProjectPaths, loadConfig } from '../../core/config.js';
14
- import { getFurnacePaths, updateFurnaceState } from '../../core/furnace-config.js';
15
+ import { clearAppliedFurnaceState } from '../../core/furnace-config.js';
15
16
  import { getHead, isGitRepository, isMissingHeadError, resetChanges } from '../../core/git.js';
16
17
  import { discoverPatches } from '../../core/patch-files.js';
17
18
  import { loadPatchesManifest } from '../../core/patch-manifest.js';
@@ -21,7 +22,8 @@ import { GeneralError } from '../../errors/base.js';
21
22
  import { RebaseSessionExistsError } from '../../errors/rebase.js';
22
23
  import { pathExists } from '../../utils/fs.js';
23
24
  import { info, intro, outro, spinner } from '../../utils/logger.js';
24
- import { pickDefined } from '../../utils/options.js';
25
+ import { commanderArgParser, pickDefined } from '../../utils/options.js';
26
+ import { parsePositiveIntegerFlag } from '../../utils/validation.js';
25
27
  import { handleAbort } from './abort.js';
26
28
  import { confirmDirtyEngineReset } from './confirm.js';
27
29
  import { handleContinue } from './continue.js';
@@ -78,9 +80,9 @@ async function handleFreshStart(projectRoot, options) {
78
80
  }
79
81
  info(`Rebasing patches: ${fromVersion} → ${currentVersion}`);
80
82
  info(`Found ${manifest.patches.length} patch(es)`);
81
- info(`Max fuzz factor: ${maxFuzz}`);
83
+ info(`Max context-reduction steps (fuzz-like): ${maxFuzz}`);
82
84
  if (isDryRun) {
83
- info('[dry-run] Would reset engine, apply patches with fuzz, and re-export.');
85
+ info('[dry-run] Would reset engine, apply patches with context reduction, and re-export.');
84
86
  outro('Dry run complete');
85
87
  return;
86
88
  }
@@ -102,13 +104,7 @@ async function handleFreshStart(projectRoot, options) {
102
104
  await resetChanges(paths.engine);
103
105
  resetSpinner.stop('Engine reset to baseline');
104
106
  // Clear Furnace state — the engine no longer contains deployed components.
105
- // Preserve pendingRepair since that tracks authoring-side rollback issues.
106
- const furnacePaths = getFurnacePaths(projectRoot);
107
- if (await pathExists(furnacePaths.furnaceState)) {
108
- await updateFurnaceState(projectRoot, (current) => ({
109
- ...(current.pendingRepair ? { pendingRepair: current.pendingRepair } : {}),
110
- }));
111
- }
107
+ await clearAppliedFurnaceState(projectRoot);
112
108
  // Create rebase session
113
109
  const allPatches = await discoverPatches(paths.patches);
114
110
  const session = {
@@ -147,14 +143,13 @@ export async function rebaseCommand(projectRoot, options = {}) {
147
143
  export function registerRebase(program, { getProjectRoot, withErrorHandling }) {
148
144
  program
149
145
  .command('rebase')
150
- .description('Semi-automated Firefox source version upgrade — apply patches with fuzz and re-export')
146
+ .description('Semi-automated Firefox source version upgrade — apply patches with drift tolerance and re-export')
151
147
  .option('--continue', 'Resume after manually resolving a failed patch')
152
148
  .option('--abort', 'Cancel the rebase and restore engine to pre-rebase state')
153
149
  .option('--dry-run', 'Show what would happen without modifying anything')
154
- .option('--max-fuzz <n>', 'Maximum fuzz factor for git apply (default: 3)', parseInt)
150
+ .option('--max-fuzz <n>', 'Maximum context-reduction steps for git apply -C<n> (fuzz-like drift tolerance; default: 3)', commanderArgParser((v) => parsePositiveIntegerFlag('--max-fuzz', v)))
155
151
  .option('-y, --yes', 'Skip dirty-tree confirmation prompt')
156
152
  .action(withErrorHandling(async (options) => {
157
153
  await rebaseCommand(getProjectRoot(), pickDefined(options));
158
154
  }));
159
155
  }
160
- //# sourceMappingURL=index.js.map
@@ -76,7 +76,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz) {
76
76
  success(` ${entry.filename} — applied cleanly`);
77
77
  }
78
78
  else {
79
- warn(` ${entry.filename} — applied with fuzz=${result.fuzzFactor}`);
79
+ warn(` ${entry.filename} — applied with context reduction (step ${result.fuzzFactor})`);
80
80
  }
81
81
  }
82
82
  else {
@@ -129,7 +129,7 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz) {
129
129
  // patch was tested against if we proceeded to stamp. Refuse to claim
130
130
  // success and leave the session in place so the user can recover via
131
131
  // `fireforge rebase --continue` after fixing the underlying cause.
132
- const reExportFailures = await reExportAppliedPatches(session, paths);
132
+ const { failures: reExportFailures, overlapSkipped } = await reExportAppliedPatches(session, paths);
133
133
  if (reExportFailures.length > 0) {
134
134
  for (const f of reExportFailures) {
135
135
  error(` ${f.filename}: ${f.error}`);
@@ -138,10 +138,20 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz) {
138
138
  `Versions were not stamped and the rebase session has been kept so you can retry. ` +
139
139
  `Fix the underlying cause shown above, then re-run "fireforge rebase --continue".`);
140
140
  }
141
- // Stamp versions
141
+ if (overlapSkipped.length > 0) {
142
+ warn(`${overlapSkipped.length} patch(es) share files with other patches and were NOT ` +
143
+ `re-exported or version-stamped: ${overlapSkipped.join(', ')}. Their .patch files ` +
144
+ `still describe the pre-rebase source. Re-export them manually (e.g. ` +
145
+ `"fireforge re-export") and verify the overlapping hunks before the next import.`);
146
+ }
147
+ // Stamp versions — only for patches whose .patch file now actually
148
+ // reflects the new source (overlap-skipped ones keep their old stamp so
149
+ // the queue does not lie about what each patch was tested against).
150
+ const overlapSkippedSet = new Set(overlapSkipped);
142
151
  const appliedFilenames = session.patches
143
152
  .filter((p) => p.status === 'applied-clean' || p.status === 'applied-fuzz' || p.status === 'resolved')
144
- .map((p) => p.filename);
153
+ .map((p) => p.filename)
154
+ .filter((filename) => !overlapSkippedSet.has(filename));
145
155
  if (appliedFilenames.length > 0) {
146
156
  await stampPatchVersions(paths.patches, appliedFilenames, session.toVersion, session.toProduct);
147
157
  }
@@ -181,9 +191,24 @@ export async function runPatchLoop(projectRoot, session, paths, maxFuzz) {
181
191
  }
182
192
  async function reExportAppliedPatches(session, paths) {
183
193
  const failures = [];
194
+ const overlapSkipped = [];
184
195
  const manifest = await loadPatchesManifest(paths.patches);
185
196
  if (!manifest)
186
- return failures;
197
+ return { failures, overlapSkipped };
198
+ // Re-export writes `getDiffForFilesAgainstHead(files)` — the CUMULATIVE
199
+ // diff of those files. For a file claimed by two patches (a supported
200
+ // `--allow-overlap` queue), that diff contains BOTH patches' hunks, so
201
+ // rewriting each patch with it would duplicate the shared file's hunks
202
+ // into every owner: the very next import then fails or silently
203
+ // double-materialises. Refuse to auto-re-export any patch whose files
204
+ // overlap another patch; the operator re-exports those manually where
205
+ // per-patch attribution is possible.
206
+ const fileOwners = new Map();
207
+ for (const patch of manifest.patches) {
208
+ for (const file of patch.filesAffected) {
209
+ fileOwners.set(file, (fileOwners.get(file) ?? 0) + 1);
210
+ }
211
+ }
187
212
  const s = spinner('Re-exporting patches...');
188
213
  const reExportable = session.patches.filter((entry) => entry.status === 'applied-clean' || entry.status === 'applied-fuzz');
189
214
  const startedAt = Date.now();
@@ -191,6 +216,13 @@ async function reExportAppliedPatches(session, paths) {
191
216
  const meta = manifest.patches.find((p) => p.filename === entry.filename);
192
217
  if (!meta)
193
218
  continue;
219
+ const sharedFiles = meta.filesAffected.filter((f) => (fileOwners.get(f) ?? 0) > 1);
220
+ if (sharedFiles.length > 0) {
221
+ overlapSkipped.push(entry.filename);
222
+ warn(`Skipping re-export of ${entry.filename}: file(s) shared with other patches ` +
223
+ `(${sharedFiles.join(', ')}). Re-export it manually once the overlap is resolved.`);
224
+ continue;
225
+ }
194
226
  const existingFiles = [];
195
227
  for (const f of meta.filesAffected) {
196
228
  if (await pathExists(join(paths.engine, f))) {
@@ -224,6 +256,5 @@ async function reExportAppliedPatches(session, paths) {
224
256
  else {
225
257
  s.stop('Patches re-exported');
226
258
  }
227
- return failures;
259
+ return { failures, overlapSkipped };
228
260
  }
229
- //# sourceMappingURL=patch-loop.js.map
@@ -11,7 +11,7 @@ function statusLabel(status, fuzzFactor) {
11
11
  case 'applied-clean':
12
12
  return 'applied cleanly';
13
13
  case 'applied-fuzz':
14
- return `applied with fuzz=${fuzzFactor ?? '?'}`;
14
+ return `applied with context reduction (step ${fuzzFactor ?? '?'})`;
15
15
  case 'resolved':
16
16
  return 'RESOLVED manually';
17
17
  case 'failed':
@@ -43,6 +43,5 @@ export function printSummary(session) {
43
43
  const failed = session.patches.filter((p) => p.status === 'failed').length;
44
44
  const total = session.patches.length;
45
45
  info('');
46
- info(`Results: ${total} total: ${clean} clean, ${fuzz} fuzz-applied, ${resolved} manually resolved, ${failed} failed`);
46
+ info(`Results: ${total} total: ${clean} clean, ${fuzz} context-reduced, ${resolved} manually resolved, ${failed} failed`);
47
47
  }
48
- //# sourceMappingURL=summary.js.map
@@ -3,4 +3,3 @@
3
3
  * `fireforge rebase` — re-exports from `./rebase/` sub-modules.
4
4
  */
5
5
  export { rebaseCommand, registerRebase } from './rebase/index.js';
6
- //# sourceMappingURL=rebase.js.map
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join } from 'node:path';
3
+ import { Command } from 'commander';
3
4
  import { getProjectPaths } from '../core/config.js';
4
5
  import { registerFile } from '../core/manifest-rules.js';
5
6
  import { InvalidArgumentError } from '../errors/base.js';
@@ -104,4 +105,3 @@ export function registerRegister(program, { getProjectRoot, withErrorHandling })
104
105
  await registerCommand(getProjectRoot(), path, pickDefined(options));
105
106
  }));
106
107
  }
107
- //# sourceMappingURL=register.js.map
@@ -1,8 +1,9 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { confirm } from '@clack/prompts';
3
+ import { Command } from 'commander';
3
4
  import { getProjectPaths } from '../core/config.js';
4
- import { getFurnacePaths, updateFurnaceState } from '../core/furnace-config.js';
5
- import { hasChanges, isGitRepository, resetChanges } from '../core/git.js';
5
+ import { clearAppliedFurnaceState } from '../core/furnace-config.js';
6
+ import { getHead, hasChanges, isGitRepository, isMissingHeadError, resetChanges, } from '../core/git.js';
6
7
  import { expandUntrackedDirectoryEntries, getWorkingTreeStatus } from '../core/git-status.js';
7
8
  import { GeneralError, InvalidArgumentError } from '../errors/base.js';
8
9
  import { pathExists } from '../utils/fs.js';
@@ -24,6 +25,18 @@ export async function resetCommand(projectRoot, options) {
24
25
  if (!(await isGitRepository(paths.engine))) {
25
26
  throw new GeneralError('Engine directory is not a git repository. Run "fireforge download" to initialize.');
26
27
  }
28
+ // Unborn-HEAD guard (mirrors status): an interrupted download leaves a
29
+ // repo with no baseline commit, where the entire ~300k-file tree reads
30
+ // as untracked — a reset against that state is never what the operator
31
+ // wants, and the guidance names the actual fix.
32
+ try {
33
+ await getHead(paths.engine);
34
+ }
35
+ catch (headError) {
36
+ if (!isMissingHeadError(headError))
37
+ throw headError;
38
+ throw new GeneralError('Engine repository has no baseline commit yet — a previous "fireforge download" was interrupted before git created the initial Firefox source commit. Re-run "fireforge download --force" to recreate the baseline repository cleanly.');
39
+ }
27
40
  // Check for changes
28
41
  const hasUncommittedChanges = await hasChanges(paths.engine);
29
42
  if (!hasUncommittedChanges) {
@@ -71,12 +84,7 @@ export async function resetCommand(projectRoot, options) {
71
84
  // consistent regardless of the drift oracle.) Preserve pendingRepair:
72
85
  // authoring-side rollback markers are about the workspace/component
73
86
  // tree, not the engine checkout, so reset must not silently forget them.
74
- const furnacePaths = getFurnacePaths(projectRoot);
75
- if (await pathExists(furnacePaths.furnaceState)) {
76
- await updateFurnaceState(projectRoot, (current) => ({
77
- ...(current.pendingRepair ? { pendingRepair: current.pendingRepair } : {}),
78
- }));
79
- }
87
+ await clearAppliedFurnaceState(projectRoot);
80
88
  s.stop('Changes reset');
81
89
  outro('Working tree restored to clean state');
82
90
  }
@@ -96,4 +104,3 @@ export function registerReset(program, { getProjectRoot, withErrorHandling }) {
96
104
  await resetCommand(getProjectRoot(), pickDefined(options));
97
105
  }));
98
106
  }
99
- //# sourceMappingURL=reset.js.map
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { join } from 'node:path';
3
3
  import { confirm } from '@clack/prompts';
4
+ import { Command } from 'commander';
4
5
  import { getProjectPaths, loadConfig, loadState, updateState } from '../core/config.js';
5
6
  import { isGitRepository } from '../core/git.js';
6
7
  import { getStagedDiffForFiles } from '../core/git-diff.js';
@@ -43,7 +44,7 @@ export async function resolveCommand(projectRoot, options = {}) {
43
44
  // pipe / CI shell could otherwise commit whatever the engine
44
45
  // currently contains. 2026-04-21 eval (Finding #18): a scripted
45
46
  // recovery flow was dead-ended by the unconditional TTY refusal.
46
- if (!process.stdin.isTTY && !options.yes) {
47
+ if (!(process.stdin.isTTY && process.stdout.isTTY) && !options.yes) {
47
48
  throw new GeneralError('Cannot run "fireforge resolve" in non-interactive mode. Use a terminal with TTY support, or pass "--yes" to skip the interactive confirmation once the manual merge is complete.');
48
49
  }
49
50
  if (!options.yes) {
@@ -167,4 +168,3 @@ export function registerResolve(program, { getProjectRoot, withErrorHandling })
167
168
  await resolveCommand(getProjectRoot(), options);
168
169
  }));
169
170
  }
170
- //# sourceMappingURL=resolve.js.map
@@ -2,6 +2,7 @@
2
2
  import { createWriteStream } from 'node:fs';
3
3
  import { readdir, readFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
+ import { Command } from 'commander';
5
6
  import { getProjectPaths, loadConfig } from '../core/config.js';
6
7
  import { warnIfFurnaceStale } from '../core/furnace-staleness.js';
7
8
  import { buildArtifactMismatchMessage, hasBuildArtifacts, hasRunnableBundle, run, runMachSmoke, } from '../core/mach.js';
@@ -13,7 +14,7 @@ import { SmokeRunError } from '../errors/run.js';
13
14
  import { toError } from '../utils/errors.js';
14
15
  import { pathExists, removeDir, removeFile } from '../utils/fs.js';
15
16
  import { info, intro, verbose, warn } from '../utils/logger.js';
16
- import { pickDefined } from '../utils/options.js';
17
+ import { commanderArgParser, pickDefined } from '../utils/options.js';
17
18
  /**
18
19
  * Exit code returned by smoke-run mode when the captured console stream
19
20
  * produced one or more error lines that did NOT match the operator's
@@ -124,7 +125,7 @@ export async function runCommand(projectRoot, options = {}) {
124
125
  return;
125
126
  }
126
127
  info('Launching browser...\n');
127
- const exitCode = await run(paths.engine);
128
+ const exitCode = await run(paths.engine, options.headless ? ['--headless'] : []);
128
129
  // Exit-code whitelist:
129
130
  // 0 — clean shutdown
130
131
  // 130 — SIGINT (Ctrl+C), user-initiated termination
@@ -212,11 +213,22 @@ async function runSmokeExit(engineDir, options) {
212
213
  }
213
214
  findings.push({ stream, line });
214
215
  };
216
+ // A headed smoke window on a developer desktop absorbs live input:
217
+ // during the drill a human interacted with the window mid-run, the
218
+ // password-manager import scan probed an unreadable Chrome profile
219
+ // dir, and the resulting console errors failed the smoke run looking
220
+ // like a product regression. CI hosts (CI env var set) are assumed
221
+ // display-free/unattended, so the notice stays quiet there.
222
+ if (!options.headless && !process.env['CI']) {
223
+ warn('Headed smoke window: keyboard/mouse input during the window will contaminate the ' +
224
+ 'console capture and can fail the run. Pass --headless (or run on an unattended host) ' +
225
+ 'for reliable smoke checks.');
226
+ }
215
227
  info(`Launching browser (smoke-exit after ${String(smokeExit)}s)...\n`);
216
228
  const startedAt = Date.now();
217
229
  let result;
218
230
  try {
219
- result = await runMachSmoke(['run'], engineDir, {
231
+ result = await runMachSmoke(options.headless ? ['run', '--headless'] : ['run'], engineDir, {
220
232
  smokeTimeoutMs,
221
233
  onStdoutLine: (line) => {
222
234
  handleLine('stdout', line);
@@ -319,21 +331,21 @@ export function registerRun(program, { getProjectRoot, withErrorHandling }) {
319
331
  program
320
332
  .command('run')
321
333
  .description('Launch the built browser')
322
- .option('--smoke-exit <seconds>', 'Smoke-run mode (POSIX only): launch, capture console, SIGTERM the process group after <seconds>. Exit 0 on a clean window, 12 on unallowed errors, 13 on launch failure.', (value) => {
334
+ .option('--smoke-exit <seconds>', 'Smoke-run mode (POSIX only): launch, capture console, SIGTERM the process group after <seconds>. Exit 0 on a clean window, 12 on unallowed errors, 13 on launch failure.', commanderArgParser((value) => {
323
335
  const parsed = Number.parseInt(value, 10);
324
336
  if (!Number.isFinite(parsed) || parsed < 1 || String(parsed) !== value.trim()) {
325
337
  throw new Error(`--smoke-exit expects a positive integer number of seconds (got "${value}").`);
326
338
  }
327
339
  return parsed;
328
- })
340
+ }))
329
341
  .option('--console-allow <regex>', 'Allowlist regex (repeatable). Lines that match any entry do not count toward the smoke exit code.', (value, acc) => {
330
342
  acc.push(value);
331
343
  return acc;
332
344
  }, [])
333
345
  .option('--console-allow-file <path>', 'Newline-delimited allowlist regex file. Blank lines and # comments are ignored.')
334
346
  .option('--capture-console <file>', 'Mirror captured console output to <file> for post-exit inspection.')
347
+ .option('--headless', 'Launch the browser with --headless. Recommended for --smoke-exit on a shared desktop: input into a headed smoke window contaminates the console capture.')
335
348
  .action(withErrorHandling(async (options) => {
336
349
  await runCommand(getProjectRoot(), pickDefined(options));
337
350
  }));
338
351
  }
339
- //# sourceMappingURL=run.js.map
@@ -388,4 +388,3 @@ export async function writeSetupProjectFiles(projectRoot, config) {
388
388
  await writeText(destPath, content);
389
389
  }
390
390
  }
391
- //# sourceMappingURL=setup-support.js.map
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { resolve } from 'node:path';
3
3
  import { confirm } from '@clack/prompts';
4
- import { Option } from 'commander';
4
+ import { Command, Option } from 'commander';
5
5
  import { configExists } from '../core/config.js';
6
6
  import { ConfigError } from '../errors/config.js';
7
7
  import { cancel, intro, isCancel, note, outro, spinner } from '../utils/logger.js';
@@ -93,4 +93,3 @@ export function registerSetup(program, { withErrorHandling }) {
93
93
  await setupCommand(resolve(process.cwd()), setupOptions);
94
94
  }));
95
95
  }
96
- //# sourceMappingURL=setup.js.map
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
- import { Option } from 'commander';
2
+ import { Command, Option } from 'commander';
3
3
  import { configExists, loadRawConfigDocument, validateConfig, withConfigFileLock, writeConfigDocument, } from '../core/config.js';
4
4
  import { resolveArchive } from '../core/firefox-archive.js';
5
5
  import { GeneralError, InvalidArgumentError } from '../errors/base.js';
@@ -92,4 +92,3 @@ export function registerSource(program, { getProjectRoot, withErrorHandling }) {
92
92
  });
93
93
  }));
94
94
  }
95
- //# sourceMappingURL=source.js.map
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
2
  import { isFileRegistered, matchesRegistrablePattern } from '../core/manifest-rules.js';
3
+ import { getPrimaryStatusCode } from '../core/status-classify.js';
3
4
  import { GeneralError } from '../errors/base.js';
4
5
  import { info, outro, warn } from '../utils/logger.js';
5
6
  const STATUS_DESCRIPTIONS = {
@@ -15,17 +16,6 @@ const STATUS_DESCRIPTIONS = {
15
16
  function getStatusDescription(code) {
16
17
  return STATUS_DESCRIPTIONS[code] ?? 'changed';
17
18
  }
18
- function getPrimaryStatusCode(status) {
19
- if (status.includes('?'))
20
- return '?';
21
- if (status.includes('!'))
22
- return '!';
23
- for (const code of status) {
24
- if (code !== ' ')
25
- return code;
26
- }
27
- return status;
28
- }
29
19
  function isNewFileStatus(status) {
30
20
  const code = getPrimaryStatusCode(status);
31
21
  return code === '?' || code === 'A';
@@ -192,4 +182,3 @@ export async function renderDefaultStatus(totalModified, buckets, projectRoot, b
192
182
  .map((section) => `${section.files.length} ${section.label}`);
193
183
  outro(parts.join(', '));
194
184
  }
195
- //# sourceMappingURL=status-output.js.map