@hominis/fireforge 0.45.0 → 0.46.1

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 (662) hide show
  1. package/CHANGELOG.md +409 -446
  2. package/dist/bin/fireforge.d.ts +1 -1
  3. package/dist/bin/fireforge.js +21 -15
  4. package/dist/build-info.json +4 -4
  5. package/dist/src/cli-usage-error.d.ts +46 -0
  6. package/dist/src/cli-usage-error.js +81 -0
  7. package/dist/src/cli.d.ts +7 -2
  8. package/dist/src/cli.js +47 -47
  9. package/dist/src/commands/bootstrap-checks.js +2 -1
  10. package/dist/src/commands/bootstrap.js +3 -4
  11. package/dist/src/commands/build.js +27 -17
  12. package/dist/src/commands/config.js +9 -9
  13. package/dist/src/commands/destructive-decision.d.ts +19 -0
  14. package/dist/src/commands/destructive-decision.js +31 -0
  15. package/dist/src/commands/discard.js +5 -5
  16. package/dist/src/commands/doctor-check-core.d.ts +4 -10
  17. package/dist/src/commands/doctor-check-core.js +0 -8
  18. package/dist/src/commands/doctor-external-toolchains.js +2 -2
  19. package/dist/src/commands/doctor-furnace-config-sync.d.ts +2 -2
  20. package/dist/src/commands/doctor-furnace-config-sync.js +20 -20
  21. package/dist/src/commands/doctor-furnace-jar.d.ts +1 -1
  22. package/dist/src/commands/doctor-furnace-jar.js +1 -1
  23. package/dist/src/commands/doctor-furnace.js +22 -22
  24. package/dist/src/commands/doctor-orphaned-harness.d.ts +10 -17
  25. package/dist/src/commands/doctor-orphaned-harness.js +14 -23
  26. package/dist/src/commands/doctor-patch-manifest.d.ts +2 -1
  27. package/dist/src/commands/doctor-patch-manifest.js +7 -6
  28. package/dist/src/commands/doctor-post-rebase-audit.js +3 -3
  29. package/dist/src/commands/doctor-source-pin.d.ts +12 -12
  30. package/dist/src/commands/doctor-source-pin.js +14 -14
  31. package/dist/src/commands/doctor-working-tree.d.ts +3 -3
  32. package/dist/src/commands/doctor-working-tree.js +3 -3
  33. package/dist/src/commands/doctor.d.ts +3 -1
  34. package/dist/src/commands/doctor.js +15 -16
  35. package/dist/src/commands/download.js +50 -37
  36. package/dist/src/commands/export-all.js +29 -12
  37. package/dist/src/commands/export-flow.d.ts +5 -5
  38. package/dist/src/commands/export-flow.js +29 -59
  39. package/dist/src/commands/export-placement-conflicts.d.ts +3 -3
  40. package/dist/src/commands/export-placement-conflicts.js +12 -12
  41. package/dist/src/commands/export-placement-gate.d.ts +2 -2
  42. package/dist/src/commands/export-placement-gate.js +7 -13
  43. package/dist/src/commands/export-placement-policy.d.ts +1 -1
  44. package/dist/src/commands/export-placement-policy.js +4 -6
  45. package/dist/src/commands/export-shared.d.ts +51 -32
  46. package/dist/src/commands/export-shared.js +17 -32
  47. package/dist/src/commands/export.js +14 -5
  48. package/dist/src/commands/furnace/apply.js +16 -16
  49. package/dist/src/commands/furnace/authoring-config.d.ts +13 -0
  50. package/dist/src/commands/furnace/authoring-config.js +19 -0
  51. package/dist/src/commands/furnace/chrome-doc-remove.d.ts +1 -1
  52. package/dist/src/commands/furnace/chrome-doc-remove.js +2 -2
  53. package/dist/src/commands/furnace/chrome-doc-templates.d.ts +14 -14
  54. package/dist/src/commands/furnace/chrome-doc-templates.js +17 -17
  55. package/dist/src/commands/furnace/chrome-doc-tests.d.ts +2 -2
  56. package/dist/src/commands/furnace/chrome-doc-tests.js +4 -4
  57. package/dist/src/commands/furnace/chrome-doc.d.ts +8 -8
  58. package/dist/src/commands/furnace/chrome-doc.js +18 -18
  59. package/dist/src/commands/furnace/create-browser-test.d.ts +1 -1
  60. package/dist/src/commands/furnace/create-browser-test.js +7 -12
  61. package/dist/src/commands/furnace/create-dry-run.d.ts +4 -4
  62. package/dist/src/commands/furnace/create-dry-run.js +6 -9
  63. package/dist/src/commands/furnace/create-features.d.ts +2 -2
  64. package/dist/src/commands/furnace/create-features.js +2 -2
  65. package/dist/src/commands/furnace/create-mochikit.d.ts +4 -4
  66. package/dist/src/commands/furnace/create-mochikit.js +7 -7
  67. package/dist/src/commands/furnace/create-templates.d.ts +31 -15
  68. package/dist/src/commands/furnace/create-templates.js +20 -19
  69. package/dist/src/commands/furnace/create-xpcshell.d.ts +5 -5
  70. package/dist/src/commands/furnace/create-xpcshell.js +8 -8
  71. package/dist/src/commands/furnace/create.d.ts +1 -1
  72. package/dist/src/commands/furnace/create.js +35 -29
  73. package/dist/src/commands/furnace/deploy.js +15 -6
  74. package/dist/src/commands/furnace/diff.js +7 -7
  75. package/dist/src/commands/furnace/index.js +1 -1
  76. package/dist/src/commands/furnace/init.js +17 -16
  77. package/dist/src/commands/furnace/list.js +2 -2
  78. package/dist/src/commands/furnace/override.d.ts +1 -1
  79. package/dist/src/commands/furnace/override.js +43 -34
  80. package/dist/src/commands/furnace/preview.js +17 -17
  81. package/dist/src/commands/furnace/refresh.js +15 -7
  82. package/dist/src/commands/furnace/remove-mutations.d.ts +9 -10
  83. package/dist/src/commands/furnace/remove-mutations.js +19 -19
  84. package/dist/src/commands/furnace/remove-state.d.ts +1 -1
  85. package/dist/src/commands/furnace/remove-state.js +1 -1
  86. package/dist/src/commands/furnace/remove.js +4 -8
  87. package/dist/src/commands/furnace/rename-browser-test.js +10 -15
  88. package/dist/src/commands/furnace/rename-xpcshell.d.ts +2 -2
  89. package/dist/src/commands/furnace/rename-xpcshell.js +3 -3
  90. package/dist/src/commands/furnace/rename.js +36 -36
  91. package/dist/src/commands/furnace/scan.js +3 -3
  92. package/dist/src/commands/furnace/status.js +19 -16
  93. package/dist/src/commands/furnace/sync.js +6 -6
  94. package/dist/src/commands/furnace/test-file-name.d.ts +28 -0
  95. package/dist/src/commands/furnace/test-file-name.js +39 -0
  96. package/dist/src/commands/furnace/validate.js +11 -12
  97. package/dist/src/commands/furnace/validation-output.d.ts +21 -9
  98. package/dist/src/commands/furnace/validation-output.js +3 -8
  99. package/dist/src/commands/import.js +22 -29
  100. package/dist/src/commands/lint-per-patch.d.ts +2 -2
  101. package/dist/src/commands/lint-per-patch.js +27 -12
  102. package/dist/src/commands/lint-per-run-checkjs.d.ts +14 -15
  103. package/dist/src/commands/lint-per-run-checkjs.js +14 -15
  104. package/dist/src/commands/lint-report.d.ts +3 -3
  105. package/dist/src/commands/lint-report.js +4 -4
  106. package/dist/src/commands/lint.d.ts +5 -4
  107. package/dist/src/commands/lint.js +36 -36
  108. package/dist/src/commands/manifest.d.ts +4 -4
  109. package/dist/src/commands/manifest.js +1 -1
  110. package/dist/src/commands/package.js +4 -6
  111. package/dist/src/commands/patch/compact.d.ts +2 -2
  112. package/dist/src/commands/patch/compact.js +19 -20
  113. package/dist/src/commands/patch/delete.d.ts +1 -1
  114. package/dist/src/commands/patch/delete.js +23 -33
  115. package/dist/src/commands/patch/entry-projection.d.ts +13 -0
  116. package/dist/src/commands/patch/entry-projection.js +24 -0
  117. package/dist/src/commands/patch/lint-ignore.d.ts +3 -12
  118. package/dist/src/commands/patch/lint-ignore.js +33 -44
  119. package/dist/src/commands/patch/move-files-create.d.ts +2 -2
  120. package/dist/src/commands/patch/move-files-create.js +29 -15
  121. package/dist/src/commands/patch/move-files-into.d.ts +2 -2
  122. package/dist/src/commands/patch/move-files-into.js +41 -37
  123. package/dist/src/commands/patch/move-files.js +5 -14
  124. package/dist/src/commands/patch/patch-context.d.ts +1 -1
  125. package/dist/src/commands/patch/rename.d.ts +4 -4
  126. package/dist/src/commands/patch/rename.js +35 -49
  127. package/dist/src/commands/patch/reorder.js +35 -48
  128. package/dist/src/commands/patch/split-plan.d.ts +7 -8
  129. package/dist/src/commands/patch/split-plan.js +10 -13
  130. package/dist/src/commands/patch/split.d.ts +2 -2
  131. package/dist/src/commands/patch/split.js +50 -48
  132. package/dist/src/commands/patch/staged-dependency-validate.d.ts +1 -1
  133. package/dist/src/commands/patch/staged-dependency-validate.js +3 -3
  134. package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
  135. package/dist/src/commands/patch/staged-dependency.js +22 -28
  136. package/dist/src/commands/patch/tier.d.ts +2 -2
  137. package/dist/src/commands/patch/tier.js +20 -30
  138. package/dist/src/commands/patch-rename-map.d.ts +19 -0
  139. package/dist/src/commands/patch-rename-map.js +36 -0
  140. package/dist/src/commands/pending-resolution.d.ts +15 -0
  141. package/dist/src/commands/pending-resolution.js +25 -0
  142. package/dist/src/commands/re-export-adjacent.d.ts +25 -3
  143. package/dist/src/commands/re-export-adjacent.js +40 -24
  144. package/dist/src/commands/re-export-bulk-scan.d.ts +3 -3
  145. package/dist/src/commands/re-export-bulk-scan.js +23 -27
  146. package/dist/src/commands/re-export-drift.d.ts +10 -10
  147. package/dist/src/commands/re-export-drift.js +33 -33
  148. package/dist/src/commands/re-export-files.d.ts +2 -3
  149. package/dist/src/commands/re-export-files.js +52 -44
  150. package/dist/src/commands/re-export-lint.d.ts +29 -30
  151. package/dist/src/commands/re-export-lint.js +36 -29
  152. package/dist/src/commands/re-export-options.d.ts +1 -1
  153. package/dist/src/commands/re-export-options.js +1 -1
  154. package/dist/src/commands/re-export-register.d.ts +1 -1
  155. package/dist/src/commands/re-export-register.js +1 -1
  156. package/dist/src/commands/re-export-scan.d.ts +1 -1
  157. package/dist/src/commands/re-export-scan.js +12 -9
  158. package/dist/src/commands/re-export-single.d.ts +23 -4
  159. package/dist/src/commands/re-export-single.js +39 -31
  160. package/dist/src/commands/re-export.js +35 -17
  161. package/dist/src/commands/rebase/abort.js +9 -14
  162. package/dist/src/commands/rebase/continue.js +20 -19
  163. package/dist/src/commands/rebase/index.d.ts +1 -1
  164. package/dist/src/commands/rebase/index.js +5 -5
  165. package/dist/src/commands/rebase/patch-loop.d.ts +2 -2
  166. package/dist/src/commands/rebase/patch-loop.js +20 -24
  167. package/dist/src/commands/register.js +5 -5
  168. package/dist/src/commands/reset.js +1 -1
  169. package/dist/src/commands/resolve.d.ts +2 -2
  170. package/dist/src/commands/resolve.js +21 -13
  171. package/dist/src/commands/run.d.ts +0 -13
  172. package/dist/src/commands/run.js +38 -51
  173. package/dist/src/commands/setup-support.d.ts +16 -4
  174. package/dist/src/commands/setup-support.js +20 -20
  175. package/dist/src/commands/setup.js +4 -10
  176. package/dist/src/commands/source.js +9 -2
  177. package/dist/src/commands/status-check.d.ts +3 -3
  178. package/dist/src/commands/status-check.js +6 -6
  179. package/dist/src/commands/status-json.d.ts +14 -13
  180. package/dist/src/commands/status-json.js +9 -8
  181. package/dist/src/commands/status-output.d.ts +3 -3
  182. package/dist/src/commands/status-output.js +5 -5
  183. package/dist/src/commands/status-ownership.d.ts +3 -3
  184. package/dist/src/commands/status-ownership.js +3 -3
  185. package/dist/src/commands/status.js +25 -26
  186. package/dist/src/commands/test-build-phase.d.ts +3 -3
  187. package/dist/src/commands/test-build-phase.js +48 -24
  188. package/dist/src/commands/test-diagnose.d.ts +1 -1
  189. package/dist/src/commands/test-diagnose.js +20 -20
  190. package/dist/src/commands/test-modes.d.ts +4 -4
  191. package/dist/src/commands/test-modes.js +4 -5
  192. package/dist/src/commands/test-register.js +5 -3
  193. package/dist/src/commands/test-run.d.ts +11 -11
  194. package/dist/src/commands/test-run.js +40 -33
  195. package/dist/src/commands/test-stale-gate.d.ts +12 -12
  196. package/dist/src/commands/test-stale-gate.js +17 -17
  197. package/dist/src/commands/test-verdict.d.ts +19 -5
  198. package/dist/src/commands/test-verdict.js +26 -11
  199. package/dist/src/commands/test.d.ts +3 -3
  200. package/dist/src/commands/test.js +73 -36
  201. package/dist/src/commands/token-coverage.js +3 -3
  202. package/dist/src/commands/token-list.d.ts +26 -0
  203. package/dist/src/commands/token-list.js +153 -0
  204. package/dist/src/commands/token.js +26 -10
  205. package/dist/src/commands/tree.js +26 -21
  206. package/dist/src/commands/typecheck.d.ts +6 -6
  207. package/dist/src/commands/typecheck.js +16 -16
  208. package/dist/src/commands/verify.d.ts +3 -3
  209. package/dist/src/commands/verify.js +10 -10
  210. package/dist/src/commands/watch.js +11 -11
  211. package/dist/src/commands/wire.js +17 -16
  212. package/dist/src/core/ast-utils.d.ts +7 -7
  213. package/dist/src/core/ast-utils.js +8 -8
  214. package/dist/src/core/branding.d.ts +5 -12
  215. package/dist/src/core/branding.js +6 -5
  216. package/dist/src/core/browser-wire.d.ts +1 -1
  217. package/dist/src/core/browser-wire.js +10 -7
  218. package/dist/src/core/build-audit-platform.d.ts +32 -3
  219. package/dist/src/core/build-audit-platform.js +153 -13
  220. package/dist/src/core/build-audit-registration.d.ts +5 -5
  221. package/dist/src/core/build-audit-registration.js +10 -9
  222. package/dist/src/core/build-audit-resolve.d.ts +5 -5
  223. package/dist/src/core/build-audit-resolve.js +6 -6
  224. package/dist/src/core/build-audit-skip.d.ts +57 -0
  225. package/dist/src/core/build-audit-skip.js +112 -0
  226. package/dist/src/core/build-audit-transforms.d.ts +2 -2
  227. package/dist/src/core/build-audit-transforms.js +3 -3
  228. package/dist/src/core/build-audit.d.ts +21 -8
  229. package/dist/src/core/build-audit.js +142 -40
  230. package/dist/src/core/build-baseline-types.d.ts +19 -18
  231. package/dist/src/core/build-baseline-types.js +1 -1
  232. package/dist/src/core/build-baseline.d.ts +48 -31
  233. package/dist/src/core/build-baseline.js +20 -43
  234. package/dist/src/core/build-jar-escalation.d.ts +8 -8
  235. package/dist/src/core/build-jar-escalation.js +18 -17
  236. package/dist/src/core/build-overwrite-guard.d.ts +4 -4
  237. package/dist/src/core/build-overwrite-guard.js +14 -14
  238. package/dist/src/core/build-prepare.d.ts +4 -12
  239. package/dist/src/core/build-prepare.js +22 -26
  240. package/dist/src/core/config-mutate.js +4 -4
  241. package/dist/src/core/config-paths.d.ts +2 -6
  242. package/dist/src/core/config-paths.js +4 -2
  243. package/dist/src/core/config-state.d.ts +0 -6
  244. package/dist/src/core/config-state.js +3 -21
  245. package/dist/src/core/config-validate-build-audit.d.ts +8 -0
  246. package/dist/src/core/config-validate-build-audit.js +80 -0
  247. package/dist/src/core/config-validate-file-size.d.ts +1 -1
  248. package/dist/src/core/config-validate-file-size.js +3 -3
  249. package/dist/src/core/config-validate-patch-policy.d.ts +2 -2
  250. package/dist/src/core/config-validate-patch-policy.js +3 -3
  251. package/dist/src/core/config-validate-test-toolchains.d.ts +2 -2
  252. package/dist/src/core/config-validate-test-toolchains.js +4 -5
  253. package/dist/src/core/config-validate.js +22 -12
  254. package/dist/src/core/config.d.ts +12 -12
  255. package/dist/src/core/config.js +21 -25
  256. package/dist/src/core/coverage-extend.d.ts +16 -16
  257. package/dist/src/core/coverage-extend.js +20 -20
  258. package/dist/src/core/destructive.d.ts +14 -14
  259. package/dist/src/core/destructive.js +9 -9
  260. package/dist/src/core/diff-hunks.d.ts +2 -2
  261. package/dist/src/core/diff-hunks.js +7 -7
  262. package/dist/src/core/discard-baseline.d.ts +10 -10
  263. package/dist/src/core/discard-baseline.js +22 -23
  264. package/dist/src/core/display-state.d.ts +11 -11
  265. package/dist/src/core/display-state.js +12 -12
  266. package/dist/src/core/engine-changes.d.ts +2 -2
  267. package/dist/src/core/engine-changes.js +2 -2
  268. package/dist/src/core/engine-precondition.d.ts +5 -13
  269. package/dist/src/core/engine-precondition.js +5 -10
  270. package/dist/src/core/engine-session-lock.d.ts +18 -18
  271. package/dist/src/core/engine-session-lock.js +50 -54
  272. package/dist/src/core/export-moved-code-shape.d.ts +7 -7
  273. package/dist/src/core/export-moved-code-shape.js +10 -10
  274. package/dist/src/core/file-lock-owner.d.ts +103 -0
  275. package/dist/src/core/file-lock-owner.js +259 -0
  276. package/dist/src/core/file-lock.d.ts +14 -19
  277. package/dist/src/core/file-lock.js +90 -128
  278. package/dist/src/core/firefox-cache.d.ts +13 -1
  279. package/dist/src/core/firefox-cache.js +61 -36
  280. package/dist/src/core/firefox-download.d.ts +15 -0
  281. package/dist/src/core/firefox-download.js +38 -22
  282. package/dist/src/core/firefox-extract.d.ts +8 -7
  283. package/dist/src/core/firefox-extract.js +45 -27
  284. package/dist/src/core/firefox.d.ts +34 -36
  285. package/dist/src/core/firefox.js +13 -44
  286. package/dist/src/core/furnace-apply-dry-run.d.ts +1 -6
  287. package/dist/src/core/furnace-apply-dry-run.js +2 -8
  288. package/dist/src/core/furnace-apply-ftl.d.ts +5 -5
  289. package/dist/src/core/furnace-apply-ftl.js +8 -8
  290. package/dist/src/core/furnace-apply-helpers.d.ts +9 -20
  291. package/dist/src/core/furnace-apply-helpers.js +28 -39
  292. package/dist/src/core/furnace-apply-output.js +1 -1
  293. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +9 -9
  294. package/dist/src/core/furnace-apply-overwrite-warn.js +13 -11
  295. package/dist/src/core/furnace-apply-undeploy-actions.d.ts +1 -1
  296. package/dist/src/core/furnace-apply-undeploy-actions.js +1 -1
  297. package/dist/src/core/furnace-apply.d.ts +4 -4
  298. package/dist/src/core/furnace-apply.js +50 -29
  299. package/dist/src/core/furnace-checksum-utils.d.ts +5 -5
  300. package/dist/src/core/furnace-checksum-utils.js +5 -5
  301. package/dist/src/core/furnace-config-custom.d.ts +1 -1
  302. package/dist/src/core/furnace-config-custom.js +2 -2
  303. package/dist/src/core/furnace-config-order.js +1 -1
  304. package/dist/src/core/furnace-config-tokens.d.ts +3 -3
  305. package/dist/src/core/furnace-config-tokens.js +3 -3
  306. package/dist/src/core/furnace-config-validate.js +2 -2
  307. package/dist/src/core/furnace-config.d.ts +5 -5
  308. package/dist/src/core/furnace-config.js +40 -53
  309. package/dist/src/core/furnace-constants.d.ts +6 -6
  310. package/dist/src/core/furnace-constants.js +9 -8
  311. package/dist/src/core/furnace-css-fragments.d.ts +6 -6
  312. package/dist/src/core/furnace-css-fragments.js +11 -11
  313. package/dist/src/core/furnace-dir-entry.d.ts +20 -0
  314. package/dist/src/core/furnace-dir-entry.js +21 -0
  315. package/dist/src/core/furnace-jsconfig.d.ts +7 -7
  316. package/dist/src/core/furnace-jsconfig.js +22 -20
  317. package/dist/src/core/furnace-operation.d.ts +22 -19
  318. package/dist/src/core/furnace-operation.js +38 -34
  319. package/dist/src/core/furnace-precondition.d.ts +10 -10
  320. package/dist/src/core/furnace-precondition.js +8 -8
  321. package/dist/src/core/furnace-refresh.d.ts +18 -6
  322. package/dist/src/core/furnace-refresh.js +5 -8
  323. package/dist/src/core/furnace-registration-ast.d.ts +2 -3
  324. package/dist/src/core/furnace-registration-ast.js +9 -12
  325. package/dist/src/core/furnace-registration-remove.d.ts +4 -4
  326. package/dist/src/core/furnace-registration-remove.js +6 -6
  327. package/dist/src/core/furnace-registration-validate.d.ts +2 -2
  328. package/dist/src/core/furnace-registration-validate.js +2 -2
  329. package/dist/src/core/furnace-registration.d.ts +7 -6
  330. package/dist/src/core/furnace-registration.js +29 -36
  331. package/dist/src/core/furnace-rollback.d.ts +1 -1
  332. package/dist/src/core/furnace-rollback.js +5 -5
  333. package/dist/src/core/furnace-scanner.js +3 -6
  334. package/dist/src/core/furnace-stale-export.d.ts +17 -16
  335. package/dist/src/core/furnace-stale-export.js +16 -15
  336. package/dist/src/core/furnace-staleness.d.ts +2 -2
  337. package/dist/src/core/furnace-staleness.js +2 -2
  338. package/dist/src/core/furnace-state-persist.d.ts +12 -12
  339. package/dist/src/core/furnace-state-persist.js +10 -10
  340. package/dist/src/core/furnace-step-errors.d.ts +6 -6
  341. package/dist/src/core/furnace-step-errors.js +6 -6
  342. package/dist/src/core/furnace-validate-accessibility.js +14 -14
  343. package/dist/src/core/furnace-validate-compatibility.js +9 -9
  344. package/dist/src/core/furnace-validate-registration.d.ts +2 -2
  345. package/dist/src/core/furnace-validate-registration.js +18 -20
  346. package/dist/src/core/furnace-validate-structure.d.ts +1 -1
  347. package/dist/src/core/furnace-validate-structure.js +4 -4
  348. package/dist/src/core/furnace-validate.js +9 -9
  349. package/dist/src/core/furnace-version-drift.d.ts +5 -5
  350. package/dist/src/core/furnace-version-drift.js +5 -5
  351. package/dist/src/core/git-base.d.ts +6 -4
  352. package/dist/src/core/git-base.js +29 -7
  353. package/dist/src/core/git-diff-new-file.d.ts +40 -0
  354. package/dist/src/core/git-diff-new-file.js +119 -0
  355. package/dist/src/core/git-diff.d.ts +13 -13
  356. package/dist/src/core/git-diff.js +121 -189
  357. package/dist/src/core/git-file-ops.d.ts +6 -13
  358. package/dist/src/core/git-file-ops.js +11 -35
  359. package/dist/src/core/git-readonly-index.d.ts +12 -14
  360. package/dist/src/core/git-readonly-index.js +12 -16
  361. package/dist/src/core/git-status.d.ts +9 -2
  362. package/dist/src/core/git-status.js +23 -11
  363. package/dist/src/core/git.d.ts +3 -9
  364. package/dist/src/core/git.js +28 -57
  365. package/dist/src/core/harness-orphans.d.ts +17 -17
  366. package/dist/src/core/harness-orphans.js +28 -27
  367. package/dist/src/core/history-log.d.ts +19 -0
  368. package/dist/src/core/history-log.js +29 -0
  369. package/dist/src/core/license-headers.d.ts +19 -18
  370. package/dist/src/core/license-headers.js +29 -28
  371. package/dist/src/core/mach-build-artifacts.d.ts +21 -21
  372. package/dist/src/core/mach-build-artifacts.js +23 -23
  373. package/dist/src/core/mach-error-hints.d.ts +5 -10
  374. package/dist/src/core/mach-error-hints.js +14 -14
  375. package/dist/src/core/mach-known-noise-filter.d.ts +42 -38
  376. package/dist/src/core/mach-known-noise-filter.js +57 -56
  377. package/dist/src/core/mach-mozconfig.d.ts +6 -6
  378. package/dist/src/core/mach-mozconfig.js +17 -16
  379. package/dist/src/core/mach-python.d.ts +3 -0
  380. package/dist/src/core/mach-python.js +13 -7
  381. package/dist/src/core/mach-resource-shim.d.ts +24 -12
  382. package/dist/src/core/mach-resource-shim.js +124 -21
  383. package/dist/src/core/mach.d.ts +59 -73
  384. package/dist/src/core/mach.js +47 -115
  385. package/dist/src/core/marionette-port.d.ts +15 -30
  386. package/dist/src/core/marionette-port.js +46 -89
  387. package/dist/src/core/marionette-preflight.d.ts +6 -6
  388. package/dist/src/core/marionette-preflight.js +32 -50
  389. package/dist/src/core/markdown-table.d.ts +9 -9
  390. package/dist/src/core/markdown-table.js +14 -14
  391. package/dist/src/core/mochitest-server-port.d.ts +8 -8
  392. package/dist/src/core/mochitest-server-port.js +17 -17
  393. package/dist/src/core/module-resolution-preflight.d.ts +8 -8
  394. package/dist/src/core/module-resolution-preflight.js +11 -11
  395. package/dist/src/core/moz-manifest-helpers.d.ts +7 -7
  396. package/dist/src/core/moz-manifest-helpers.js +7 -7
  397. package/dist/src/core/moz-manifest-register.d.ts +1 -1
  398. package/dist/src/core/moz-manifest-register.js +2 -2
  399. package/dist/src/core/moz-manifest-rules.js +46 -42
  400. package/dist/src/core/moz-manifest-tokenizers.js +1 -1
  401. package/dist/src/core/ownership-table.d.ts +5 -5
  402. package/dist/src/core/ownership-table.js +7 -7
  403. package/dist/src/core/parser-fallback.d.ts +3 -3
  404. package/dist/src/core/parser-fallback.js +3 -3
  405. package/dist/src/core/patch-apply-fuzz.d.ts +5 -5
  406. package/dist/src/core/patch-apply-fuzz.js +13 -13
  407. package/dist/src/core/patch-apply.d.ts +13 -14
  408. package/dist/src/core/patch-apply.js +36 -42
  409. package/dist/src/core/patch-export-coverage.d.ts +0 -9
  410. package/dist/src/core/patch-export-coverage.js +1 -30
  411. package/dist/src/core/patch-export-update.d.ts +21 -4
  412. package/dist/src/core/patch-export-update.js +7 -6
  413. package/dist/src/core/patch-export.d.ts +20 -30
  414. package/dist/src/core/patch-export.js +55 -77
  415. package/dist/src/core/patch-files.d.ts +0 -4
  416. package/dist/src/core/patch-files.js +0 -11
  417. package/dist/src/core/patch-identifier-suggest.d.ts +1 -1
  418. package/dist/src/core/patch-identifier-suggest.js +4 -4
  419. package/dist/src/core/patch-lint-binary.d.ts +12 -18
  420. package/dist/src/core/patch-lint-cache.d.ts +18 -2
  421. package/dist/src/core/patch-lint-cache.js +16 -19
  422. package/dist/src/core/patch-lint-checkjs.d.ts +55 -38
  423. package/dist/src/core/patch-lint-checkjs.js +78 -64
  424. package/dist/src/core/patch-lint-chrome-jsdoc.d.ts +8 -9
  425. package/dist/src/core/patch-lint-chrome-jsdoc.js +10 -11
  426. package/dist/src/core/patch-lint-creators.d.ts +5 -9
  427. package/dist/src/core/patch-lint-creators.js +3 -3
  428. package/dist/src/core/patch-lint-cross.d.ts +27 -19
  429. package/dist/src/core/patch-lint-cross.js +67 -110
  430. package/dist/src/core/patch-lint-css.d.ts +1 -1
  431. package/dist/src/core/patch-lint-css.js +15 -11
  432. package/dist/src/core/patch-lint-diff-tag.d.ts +5 -5
  433. package/dist/src/core/patch-lint-diff-tag.js +5 -5
  434. package/dist/src/core/patch-lint-diff.d.ts +4 -4
  435. package/dist/src/core/patch-lint-diff.js +4 -4
  436. package/dist/src/core/patch-lint-file-size.d.ts +3 -3
  437. package/dist/src/core/patch-lint-file-size.js +6 -6
  438. package/dist/src/core/patch-lint-forward-registration.d.ts +58 -0
  439. package/dist/src/core/patch-lint-forward-registration.js +210 -0
  440. package/dist/src/core/patch-lint-jsdoc.d.ts +2 -2
  441. package/dist/src/core/patch-lint-jsdoc.js +46 -46
  442. package/dist/src/core/patch-lint-module-registration.d.ts +3 -9
  443. package/dist/src/core/patch-lint-module-registration.js +1 -1
  444. package/dist/src/core/patch-lint-mozbuild.d.ts +1 -1
  445. package/dist/src/core/patch-lint-mozbuild.js +10 -10
  446. package/dist/src/core/patch-lint-observer.d.ts +3 -3
  447. package/dist/src/core/patch-lint-observer.js +7 -7
  448. package/dist/src/core/patch-lint-ownership.d.ts +6 -6
  449. package/dist/src/core/patch-lint-ownership.js +6 -6
  450. package/dist/src/core/patch-lint-prettier.d.ts +6 -6
  451. package/dist/src/core/patch-lint-prettier.js +7 -7
  452. package/dist/src/core/patch-lint-queue-types.d.ts +60 -0
  453. package/dist/src/core/patch-lint-queue-types.js +12 -0
  454. package/dist/src/core/patch-lint-staged-registration.d.ts +61 -0
  455. package/dist/src/core/patch-lint-staged-registration.js +90 -0
  456. package/dist/src/core/patch-lint-unmanaged-companion.d.ts +23 -23
  457. package/dist/src/core/patch-lint-unmanaged-companion.js +26 -26
  458. package/dist/src/core/patch-lint.d.ts +41 -35
  459. package/dist/src/core/patch-lint.js +52 -49
  460. package/dist/src/core/patch-lock.d.ts +1 -1
  461. package/dist/src/core/patch-lock.js +11 -11
  462. package/dist/src/core/patch-manifest-consistency.d.ts +7 -7
  463. package/dist/src/core/patch-manifest-consistency.js +16 -17
  464. package/dist/src/core/patch-manifest-files-affected.d.ts +1 -1
  465. package/dist/src/core/patch-manifest-files-affected.js +3 -3
  466. package/dist/src/core/patch-manifest-io.d.ts +14 -14
  467. package/dist/src/core/patch-manifest-io.js +35 -34
  468. package/dist/src/core/patch-manifest-query.d.ts +1 -1
  469. package/dist/src/core/patch-manifest-query.js +4 -4
  470. package/dist/src/core/patch-manifest-resolve.d.ts +5 -5
  471. package/dist/src/core/patch-manifest-resolve.js +7 -7
  472. package/dist/src/core/patch-manifest-validate.js +3 -3
  473. package/dist/src/core/patch-manifest.d.ts +2 -2
  474. package/dist/src/core/patch-manifest.js +2 -2
  475. package/dist/src/core/patch-parse.d.ts +32 -21
  476. package/dist/src/core/patch-parse.js +40 -15
  477. package/dist/src/core/patch-policy.js +15 -27
  478. package/dist/src/core/patch-registration-refs.d.ts +4 -4
  479. package/dist/src/core/patch-registration-refs.js +7 -7
  480. package/dist/src/core/patch-source-metadata.d.ts +1 -3
  481. package/dist/src/core/patch-source-metadata.js +0 -4
  482. package/dist/src/core/patch-transform.d.ts +8 -16
  483. package/dist/src/core/patch-transform.js +13 -23
  484. package/dist/src/core/process-cpu-time.d.ts +1 -1
  485. package/dist/src/core/process-cpu-time.js +6 -6
  486. package/dist/src/core/rebase-session.d.ts +9 -15
  487. package/dist/src/core/rebase-session.js +13 -25
  488. package/dist/src/core/register-browser-content.js +3 -2
  489. package/dist/src/core/register-browser-test.js +2 -1
  490. package/dist/src/core/register-module.js +2 -1
  491. package/dist/src/core/register-result.d.ts +1 -1
  492. package/dist/src/core/register-result.js +1 -1
  493. package/dist/src/core/register-scaffold.d.ts +2 -2
  494. package/dist/src/core/register-scaffold.js +8 -7
  495. package/dist/src/core/register-shared-css.d.ts +3 -3
  496. package/dist/src/core/register-shared-css.js +6 -5
  497. package/dist/src/core/register-xpcshell-test.d.ts +2 -2
  498. package/dist/src/core/register-xpcshell-test.js +2 -2
  499. package/dist/src/core/run-log-redact.d.ts +38 -0
  500. package/dist/src/core/run-log-redact.js +69 -0
  501. package/dist/src/core/run-log.d.ts +21 -10
  502. package/dist/src/core/run-log.js +76 -27
  503. package/dist/src/core/shared-ftl.d.ts +2 -2
  504. package/dist/src/core/shared-ftl.js +6 -6
  505. package/dist/src/core/signal-critical.d.ts +2 -2
  506. package/dist/src/core/signal-critical.js +4 -6
  507. package/dist/src/core/smoke-patterns.d.ts +5 -6
  508. package/dist/src/core/smoke-patterns.js +15 -20
  509. package/dist/src/core/state-file.d.ts +15 -4
  510. package/dist/src/core/state-file.js +14 -1
  511. package/dist/src/core/status-binary.d.ts +8 -8
  512. package/dist/src/core/status-binary.js +12 -12
  513. package/dist/src/core/status-classify.d.ts +17 -17
  514. package/dist/src/core/status-classify.js +13 -12
  515. package/dist/src/core/test-changed-prefs.d.ts +12 -12
  516. package/dist/src/core/test-changed-prefs.js +40 -25
  517. package/dist/src/core/test-harness-crash.d.ts +39 -39
  518. package/dist/src/core/test-harness-crash.js +69 -69
  519. package/dist/src/core/test-harness-output.d.ts +7 -7
  520. package/dist/src/core/test-harness-output.js +7 -7
  521. package/dist/src/core/test-harness-verdict-notes.d.ts +13 -14
  522. package/dist/src/core/test-harness-verdict-notes.js +4 -4
  523. package/dist/src/core/test-path-scope.d.ts +8 -8
  524. package/dist/src/core/test-path-scope.js +11 -11
  525. package/dist/src/core/test-stale-check.d.ts +22 -23
  526. package/dist/src/core/test-stale-check.js +31 -30
  527. package/dist/src/core/test-stale-symlink.d.ts +1 -1
  528. package/dist/src/core/test-stale-symlink.js +1 -1
  529. package/dist/src/core/test-stall-triage.d.ts +9 -9
  530. package/dist/src/core/test-stall-triage.js +22 -22
  531. package/dist/src/core/test-xpcshell-retry.d.ts +26 -7
  532. package/dist/src/core/test-xpcshell-retry.js +14 -11
  533. package/dist/src/core/token-category.d.ts +35 -2
  534. package/dist/src/core/token-category.js +67 -58
  535. package/dist/src/core/token-coverage.js +4 -5
  536. package/dist/src/core/token-dark-mode.d.ts +9 -9
  537. package/dist/src/core/token-dark-mode.js +13 -25
  538. package/dist/src/core/token-docs.d.ts +1 -1
  539. package/dist/src/core/token-inventory.d.ts +56 -0
  540. package/dist/src/core/token-inventory.js +109 -0
  541. package/dist/src/core/token-manager.d.ts +4 -4
  542. package/dist/src/core/token-manager.js +22 -22
  543. package/dist/src/core/token-scaffold.d.ts +4 -4
  544. package/dist/src/core/token-scaffold.js +4 -4
  545. package/dist/src/core/token-variant.d.ts +14 -14
  546. package/dist/src/core/token-variant.js +52 -53
  547. package/dist/src/core/toolchain-preflight.d.ts +15 -15
  548. package/dist/src/core/toolchain-preflight.js +23 -23
  549. package/dist/src/core/tree-cow.d.ts +3 -5
  550. package/dist/src/core/tree-cow.js +8 -8
  551. package/dist/src/core/tree-guard.d.ts +3 -3
  552. package/dist/src/core/tree-guard.js +23 -23
  553. package/dist/src/core/tree-store.d.ts +15 -15
  554. package/dist/src/core/tree-store.js +39 -38
  555. package/dist/src/core/typecheck-shim.d.ts +16 -15
  556. package/dist/src/core/typecheck-shim.js +26 -25
  557. package/dist/src/core/typecheck.d.ts +12 -12
  558. package/dist/src/core/typecheck.js +33 -33
  559. package/dist/src/core/wire-destroy.d.ts +2 -2
  560. package/dist/src/core/wire-destroy.js +5 -5
  561. package/dist/src/core/wire-dom-fragment.d.ts +3 -3
  562. package/dist/src/core/wire-dom-fragment.js +12 -23
  563. package/dist/src/core/wire-init.d.ts +2 -2
  564. package/dist/src/core/wire-init.js +7 -7
  565. package/dist/src/core/wire-subscript.d.ts +1 -1
  566. package/dist/src/core/wire-subscript.js +3 -3
  567. package/dist/src/core/wire-utils.d.ts +9 -9
  568. package/dist/src/core/wire-utils.js +16 -28
  569. package/dist/src/core/xpcshell-appdir.d.ts +20 -20
  570. package/dist/src/core/xpcshell-appdir.js +28 -30
  571. package/dist/src/core/xpcshell-profile-dir.d.ts +3 -3
  572. package/dist/src/core/xpcshell-profile-dir.js +7 -7
  573. package/dist/src/errors/base.d.ts +69 -16
  574. package/dist/src/errors/base.js +95 -26
  575. package/dist/src/errors/build.d.ts +6 -6
  576. package/dist/src/errors/build.js +26 -34
  577. package/dist/src/errors/codes.d.ts +10 -10
  578. package/dist/src/errors/codes.js +10 -10
  579. package/dist/src/errors/config.d.ts +0 -14
  580. package/dist/src/errors/config.js +13 -34
  581. package/dist/src/errors/download.d.ts +13 -0
  582. package/dist/src/errors/download.js +53 -24
  583. package/dist/src/errors/furnace.d.ts +2 -2
  584. package/dist/src/errors/furnace.js +8 -9
  585. package/dist/src/errors/git.d.ts +9 -9
  586. package/dist/src/errors/git.js +21 -31
  587. package/dist/src/errors/patch.d.ts +3 -3
  588. package/dist/src/errors/patch.js +11 -13
  589. package/dist/src/errors/rebase.d.ts +1 -1
  590. package/dist/src/errors/rebase.js +8 -8
  591. package/dist/src/errors/run.d.ts +1 -1
  592. package/dist/src/errors/run.js +1 -1
  593. package/dist/src/index.d.ts +4 -4
  594. package/dist/src/index.js +4 -4
  595. package/dist/src/types/commands/options.d.ts +155 -200
  596. package/dist/src/types/commands/patches.d.ts +19 -19
  597. package/dist/src/types/commands/project.d.ts +3 -3
  598. package/dist/src/types/config.d.ts +52 -19
  599. package/dist/src/types/furnace.d.ts +17 -17
  600. package/dist/src/types/index.d.ts +0 -1
  601. package/dist/src/types/json.d.ts +3 -3
  602. package/dist/src/types/json.js +2 -2
  603. package/dist/src/types/typecheck.d.ts +3 -3
  604. package/dist/src/utils/assert.d.ts +4 -16
  605. package/dist/src/utils/assert.js +7 -24
  606. package/dist/src/utils/build-info.d.ts +6 -6
  607. package/dist/src/utils/build-info.js +6 -6
  608. package/dist/src/utils/child-env.d.ts +7 -6
  609. package/dist/src/utils/child-env.js +5 -5
  610. package/dist/src/utils/concurrency.d.ts +2 -2
  611. package/dist/src/utils/concurrency.js +2 -2
  612. package/dist/src/utils/elapsed.js +10 -1
  613. package/dist/src/utils/errors.d.ts +8 -8
  614. package/dist/src/utils/errors.js +8 -8
  615. package/dist/src/utils/fs.d.ts +1 -1
  616. package/dist/src/utils/fs.js +15 -20
  617. package/dist/src/utils/hash.d.ts +12 -0
  618. package/dist/src/utils/hash.js +16 -0
  619. package/dist/src/utils/line-dispatch.d.ts +15 -0
  620. package/dist/src/utils/line-dispatch.js +48 -0
  621. package/dist/src/utils/logger.d.ts +4 -4
  622. package/dist/src/utils/logger.js +11 -11
  623. package/dist/src/utils/machine-output.js +2 -2
  624. package/dist/src/utils/options.d.ts +14 -20
  625. package/dist/src/utils/options.js +26 -27
  626. package/dist/src/utils/package-root.js +5 -14
  627. package/dist/src/utils/paths.d.ts +4 -4
  628. package/dist/src/utils/paths.js +4 -4
  629. package/dist/src/utils/platform.d.ts +0 -4
  630. package/dist/src/utils/process-group.d.ts +10 -9
  631. package/dist/src/utils/process-group.js +37 -36
  632. package/dist/src/utils/process.d.ts +70 -55
  633. package/dist/src/utils/process.js +129 -179
  634. package/dist/src/utils/ps-duration.d.ts +4 -4
  635. package/dist/src/utils/ps-duration.js +9 -9
  636. package/dist/src/utils/regex.d.ts +4 -4
  637. package/dist/src/utils/regex.js +7 -7
  638. package/dist/src/utils/sleep.d.ts +26 -0
  639. package/dist/src/utils/sleep.js +31 -0
  640. package/dist/src/utils/stdio-drain.d.ts +7 -7
  641. package/dist/src/utils/stdio-drain.js +8 -8
  642. package/dist/src/utils/validation.d.ts +12 -49
  643. package/dist/src/utils/validation.js +26 -36
  644. package/docs/README.md +2 -2
  645. package/docs/configuration.md +163 -69
  646. package/docs/exit-codes.md +66 -49
  647. package/docs/furnace.md +20 -19
  648. package/docs/lifecycle-invariants.md +414 -247
  649. package/docs/machine-output.md +58 -34
  650. package/docs/patch-workflow.md +48 -44
  651. package/docs/run-logs.md +72 -28
  652. package/docs/testing.md +105 -98
  653. package/docs/verification-trees.md +23 -22
  654. package/package.json +7 -6
  655. package/dist/src/commands/rebase.d.ts +0 -4
  656. package/dist/src/commands/rebase.js +0 -5
  657. package/dist/src/core/patch-artifact-normalize.d.ts +0 -21
  658. package/dist/src/core/patch-artifact-normalize.js +0 -24
  659. package/dist/src/core/patch-lint-reexports.d.ts +0 -7
  660. package/dist/src/core/patch-lint-reexports.js +0 -8
  661. package/dist/src/core/wire-targets.d.ts +0 -7
  662. package/dist/src/core/wire-targets.js +0 -8
@@ -1,52 +1,28 @@
1
1
  // SPDX-License-Identifier: EUPL-1.2
2
- import { mkdtemp, rm, stat, writeFile } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import { basename, join } from 'node:path';
2
+ import { stat } from 'node:fs/promises';
3
+ import { join } from 'node:path';
5
4
  import { GitError } from '../errors/git.js';
6
5
  import { toError } from '../utils/errors.js';
7
- import { pathExists, readText } from '../utils/fs.js';
6
+ import { pathExists } from '../utils/fs.js';
8
7
  import { verbose } from '../utils/logger.js';
9
- import { exec } from '../utils/process.js';
10
- import { chunkPathspecs, ensureGit, git } from './git-base.js';
11
- import { fileExistsInHead, hashObjectBatch, isBinaryFile, listTrackedInHead, } from './git-file-ops.js';
12
- import { mintDisposableGitIndex, readOnlyGitIndexEnv } from './git-readonly-index.js';
8
+ import { chunkPathspecs, git } from './git-base.js';
9
+ import { readNewFileContent, withForcedBinaryAttribute } from './git-diff-new-file.js';
10
+ import { fileExistsInHead, hashObjectBatch, listTrackedInHead } from './git-file-ops.js';
11
+ import { mintDisposableGitIndex } from './git-readonly-index.js';
13
12
  import { getUntrackedFiles, getUntrackedFilesInDir } from './git-status.js';
14
- async function execGitWithAllowedExitCodes(repoDir, args, allowedExitCodes = [0], envOverride) {
15
- // Honour a read-only command's private-index scope: the `--intent-to-add`
16
- // / `update-index` staging dance below is a genuine
17
- // index write, and inside a read-only command it must land on the
18
- // private index, not the primary checkout's.
19
- //
20
- // `envOverride` wins over that scope: a caller that minted an index for
21
- // one specific invocation has said something more specific than the
22
- // ambient scope did.
23
- const readOnlyIndexEnv = readOnlyGitIndexEnv(repoDir);
24
- const env = readOnlyIndexEnv !== undefined || envOverride !== undefined
25
- ? { ...readOnlyIndexEnv, ...envOverride }
26
- : undefined;
27
- const result = await exec('git', args, {
28
- cwd: repoDir,
29
- ...(env !== undefined ? { env } : {}),
30
- });
31
- if (allowedExitCodes.includes(result.exitCode)) {
32
- return result;
33
- }
34
- throw new GitError(result.stderr.trim() || 'Git command failed', args.join(' '));
35
- }
36
13
  /**
37
14
  * Gets the diff for a specific file.
38
15
  *
39
16
  * `--binary` is load-bearing: without it git degrades a binary file's section
40
17
  * to the informational `Binary files a/x and b/x differ`, which carries none
41
18
  * of the bytes and cannot be replayed by `git apply`. It only expands the
42
- * BINARY section's index line to full hashes (text sections keep their
19
+ * binary section's index line to full hashes (text sections keep their
43
20
  * abbreviated form), so text output stays byte-identical.
44
21
  * @param repoDir - Repository directory
45
22
  * @param filePath - Path to the file (relative to repo)
46
23
  * @returns Diff content
47
24
  */
48
25
  export async function getFileDiff(repoDir, filePath) {
49
- await ensureGit();
50
26
  return git(['diff', '--binary', 'HEAD', '--', filePath], repoDir);
51
27
  }
52
28
  /**
@@ -66,7 +42,7 @@ function abbreviateBlobHash(fullHash) {
66
42
  /**
67
43
  * Pure formatter for a new (untracked) file's unified diff. Shared by the
68
44
  * batched cold-run path in {@link getDiffForFilesAgainstHead} and the
69
- * standalone {@link generateNewFileDiff} — the only thing that differs is
45
+ * standalone {@link generateNewFileDiff}. The only thing that differs is
70
46
  * where `blobHash` comes from (a single batched `git hash-object` vs a
71
47
  * per-file one), never the formatting. Preserves the empty-file form, the
72
48
  * trailing-newline handling, and the "No newline at end of file" marker
@@ -113,22 +89,36 @@ function buildNewFileDiffBody(filePath, content, blobHash) {
113
89
  return diffLines.join('\n') + '\n';
114
90
  }
115
91
  /**
116
- * Generates a unified diff for a new (untracked) file.
92
+ * Defensive check shared by the new-file readers: a directory here means a
93
+ * caller bypassed the expansion layers and handed the leaf reader a path it
94
+ * cannot read. Surfaces an actionable message naming the offending path
95
+ * rather than the raw `EISDIR` the read would throw. This is a recurring bug
96
+ * class, hence the guard at every leaf.
97
+ * @param repoDir - Repository directory
98
+ * @param filePath - Path to the file (relative to repo)
99
+ */
100
+ async function rejectDirectory(repoDir, filePath) {
101
+ const fileStat = await stat(join(repoDir, filePath));
102
+ if (fileStat.isDirectory()) {
103
+ throw new GitError(`expected a file but found a directory at '${filePath}' — caller must expand directory entries before diffing`, `hash-object ${filePath}`);
104
+ }
105
+ }
106
+ /**
107
+ * Generates a unified diff for a new (untracked) file. A file that is binary
108
+ * or not valid UTF-8 (see {@link readNewFileContent}) is routed through
109
+ * {@link generateBinaryFilePatch} instead, because a text body cannot carry
110
+ * its bytes faithfully.
117
111
  * @param repoDir - Repository directory
118
112
  * @param filePath - Path to the file (relative to repo)
119
113
  * @returns Diff content in unified diff format
120
114
  */
121
115
  export async function generateNewFileDiff(repoDir, filePath) {
122
116
  const fullPath = join(repoDir, filePath);
123
- // Defensive check: a directory here means a caller bypassed the expansion
124
- // layers and handed the leaf reader a path it cannot read. Surface it with
125
- // an actionable message naming the offending path rather than the raw
126
- // `EISDIR` that `readText` would throw.
127
- const fileStat = await stat(fullPath);
128
- if (fileStat.isDirectory()) {
129
- throw new GitError(`expected a file but found a directory at '${filePath}' — caller must expand directory entries before diffing`, `hash-object ${filePath}`);
117
+ await rejectDirectory(repoDir, filePath);
118
+ const file = await readNewFileContent(repoDir, filePath);
119
+ if (file.binary) {
120
+ return generateBinaryFilePatch(repoDir, filePath);
130
121
  }
131
- const content = await readText(fullPath);
132
122
  // Compute the abbreviated git blob hash for the index line
133
123
  let blobHash = '0000000000';
134
124
  try {
@@ -138,7 +128,7 @@ export async function generateNewFileDiff(repoDir, filePath) {
138
128
  catch (error) {
139
129
  verbose(`git hash-object failed for ${filePath}; falling back to zero blob hash: ${toError(error).message}`);
140
130
  }
141
- return buildNewFileDiffBody(filePath, content, blobHash);
131
+ return buildNewFileDiffBody(filePath, file.content, blobHash);
142
132
  }
143
133
  /**
144
134
  * Generates a patch for a file.
@@ -150,7 +140,6 @@ export async function generateNewFileDiff(repoDir, filePath) {
150
140
  * @returns Diff content in unified diff format
151
141
  */
152
142
  export async function generateFullFilePatch(repoDir, filePath) {
153
- await ensureGit();
154
143
  // If file exists in HEAD, use standard git diff HEAD -- <file>
155
144
  // This generates a contextual diff that is safer for rebasing
156
145
  if (await fileExistsInHead(repoDir, filePath)) {
@@ -159,76 +148,26 @@ export async function generateFullFilePatch(repoDir, filePath) {
159
148
  // If file is new/untracked, use the full-file "new file" format
160
149
  return generateNewFileDiff(repoDir, filePath);
161
150
  }
162
- /**
163
- * Generates a unified diff between base content and current file content.
164
- * @param repoDir - Repository directory
165
- * @param filePath - Path to the file (relative to repo)
166
- * @param baseContent - The base content to diff against
167
- * @returns Unified diff in git format
168
- */
169
- export async function generateModificationDiff(repoDir, filePath, baseContent) {
170
- const fullPath = join(repoDir, filePath);
171
- const currentContent = await readText(fullPath);
172
- // If contents are identical, return empty diff
173
- if (baseContent === currentContent) {
174
- return '';
175
- }
176
- const tempDir = await mkdtemp(join(tmpdir(), 'fireforge-diff-'));
177
- const tempFile = join(tempDir, basename(filePath));
178
- try {
179
- await writeFile(tempFile, baseContent);
180
- // git diff --no-index exits code 1 when files differ — that's normal
181
- const result = await execGitWithAllowedExitCodes(repoDir, ['diff', '--no-index', '--', tempFile, fullPath], [0, 1]);
182
- const output = result.stdout;
183
- if (!output) {
184
- return '';
185
- }
186
- // Post-process: fix paths in the diff header only (before the first @@ hunk)
187
- const lines = output.split('\n');
188
- let pastHeader = false;
189
- const fixedLines = lines.map((line) => {
190
- if (!pastHeader && line.startsWith('@@')) {
191
- pastHeader = true;
192
- }
193
- if (!pastHeader) {
194
- if (line.startsWith('diff --git')) {
195
- return `diff --git a/${filePath} b/${filePath}`;
196
- }
197
- if (line.startsWith('--- ')) {
198
- return `--- a/${filePath}`;
199
- }
200
- if (line.startsWith('+++ ')) {
201
- return `+++ b/${filePath}`;
202
- }
203
- }
204
- return line;
205
- });
206
- return fixedLines.join('\n');
207
- }
208
- finally {
209
- await rm(tempDir, { recursive: true, force: true });
210
- }
211
- }
212
151
  /**
213
152
  * Gets the diff for all modified files, including untracked (new) files.
214
153
  *
215
- * Tracked binary files round-trip through `git diff --binary`; untracked ones
154
+ * Tracked binary files round-trip through `git diff --binary`. Untracked ones
216
155
  * go through {@link generateBinaryFilePatch} below. Both arms must stay
217
- * binary-aware — see {@link getFileDiff} for why.
156
+ * binary-aware. See {@link getFileDiff} for why.
218
157
  * @param repoDir - Repository directory
219
158
  * @returns Diff content
220
159
  */
221
160
  export async function getAllDiff(repoDir) {
222
- await ensureGit();
223
161
  // Get diff for tracked files
224
162
  const trackedDiff = await git(['diff', '--binary', 'HEAD'], repoDir);
225
163
  // Get untracked files (properly expanded, not directories)
226
164
  const untrackedFiles = await getUntrackedFiles(repoDir);
227
- // Classify first. `isBinaryFile` reads the file's first 8 KB directly and
228
- // spawns nothing, so the only per-file process in this loop is the
229
- // `git hash-object` inside `generateNewFileDiff` — one spawn per untracked
230
- // TEXT file, uncapped, against a Firefox tree where "untracked" can mean a
231
- // very large number. Hence classify → one batched hash → render.
165
+ // Classify first. `readNewFileContent` reads the file directly and spawns
166
+ // nothing, so the only per-file process in this loop would be the
167
+ // `git hash-object` inside `generateNewFileDiff`: one spawn per untracked
168
+ // text file, uncapped, against a Firefox tree where "untracked" can mean a
169
+ // very large number. Hence classify → one batched hash → render. The
170
+ // classification carries each text file's content so it is read once.
232
171
  //
233
172
  // Everything else here is deliberately untouched: `git diff HEAD` (with
234
173
  // renames, unlike the `--no-renames` scoped sibling), the `ls-files`
@@ -236,42 +175,35 @@ export async function getAllDiff(repoDir) {
236
175
  // sentinel are all observable. `getAllDiff`'s output is SHA-256'd into
237
176
  // every tree fingerprint (tree-store.ts) and written verbatim into `.patch`
238
177
  // files (export-all.ts), so its bytes are a contract.
239
- const binaryFiles = [];
240
- const textFiles = [];
178
+ const classified = new Map();
241
179
  for (const file of untrackedFiles) {
242
- if (await isBinaryFile(repoDir, file)) {
243
- binaryFiles.push(file);
244
- }
245
- else {
246
- textFiles.push(file);
247
- }
180
+ await rejectDirectory(repoDir, file);
181
+ classified.set(file, await readNewFileContent(repoDir, file));
248
182
  }
249
- const textHashes = await hashObjectBatch(repoDir, textFiles.map((file) => join(repoDir, file)));
250
- // Binary patches stage into the index and must stay serial; text patches
183
+ const textHashes = await hashObjectBatch(repoDir, untrackedFiles
184
+ .filter((file) => classified.get(file)?.binary === false)
185
+ .map((file) => join(repoDir, file)));
186
+ // Binary patches stage into the index and must stay serial. Text patches
251
187
  // are pure formatting over the already-batched hashes.
252
188
  const binaryDiffs = new Map();
253
- for (const file of binaryFiles) {
254
- binaryDiffs.set(file, await generateBinaryFilePatch(repoDir, file));
189
+ for (const file of untrackedFiles) {
190
+ if (classified.get(file)?.binary === true) {
191
+ binaryDiffs.set(file, await generateBinaryFilePatch(repoDir, file));
192
+ }
255
193
  }
256
194
  const untrackedDiffs = [];
257
195
  for (const file of untrackedFiles) {
258
- const binary = binaryDiffs.get(file);
259
- if (binary !== undefined) {
260
- untrackedDiffs.push(binary);
196
+ const entry = classified.get(file);
197
+ if (entry === undefined || entry.binary) {
198
+ const binary = binaryDiffs.get(file);
199
+ if (binary !== undefined)
200
+ untrackedDiffs.push(binary);
261
201
  continue;
262
202
  }
263
203
  const fullPath = join(repoDir, file);
264
- // Same defensive directory check `generateNewFileDiff` carries: a
265
- // directory here means a caller bypassed the expansion layers, and its
266
- // comment records this as a recurring bug class.
267
- const fileStat = await stat(fullPath);
268
- if (fileStat.isDirectory()) {
269
- throw new GitError(`expected a file but found a directory at '${file}' — caller must expand directory entries before diffing`, `hash-object ${file}`);
270
- }
271
- const content = await readText(fullPath);
272
- untrackedDiffs.push(buildNewFileDiffBody(file, content, abbreviateBlobHash(textHashes.get(fullPath))));
204
+ untrackedDiffs.push(buildNewFileDiffBody(file, entry.content, abbreviateBlobHash(textHashes.get(fullPath))));
273
205
  }
274
- // Combine all diffs — each already ends with \n, so concatenate directly
206
+ // Combine all diffs. Each already ends with \n, so concatenate directly
275
207
  // to avoid inserting blank lines between diff sections.
276
208
  const allDiffs = [trackedDiff, ...untrackedDiffs].filter((d) => d.trim().length > 0);
277
209
  const combined = allDiffs.join('');
@@ -283,8 +215,8 @@ export async function getAllDiff(repoDir) {
283
215
  * to (but not including) the next such line. Boundary detection is anchored to
284
216
  * column 0 because every diff *body* line is prefixed by a space, `+`, `-`,
285
217
  * `\`, or `@`, so a context or added line that merely contains the text
286
- * `diff --git` can never be mistaken for a header. Paths are deliberately NOT
287
- * parsed out of the header here — see {@link buildTrackedSections}.
218
+ * `diff --git` can never be mistaken for a header. Paths are not parsed out
219
+ * of the header here on purpose. See {@link buildTrackedSections}.
288
220
  * @param combined - Combined `git diff` stdout
289
221
  * @returns File sections in git's emission order
290
222
  */
@@ -311,18 +243,18 @@ function splitDiffSections(combined) {
311
243
  *
312
244
  * `--binary` is load-bearing: a tracked binary file would otherwise degrade to
313
245
  * the un-appliable `Binary files a/x and b/x differ` stub. It expands only the
314
- * binary section's index line to full hashes, so text sections are unaffected
315
- * — see {@link getFileDiff}.
246
+ * binary section's index line to full hashes, so text sections are
247
+ * unaffected. See {@link getFileDiff}.
316
248
  *
317
249
  * `--no-renames` is load-bearing: a multi-path diff under a user's
318
250
  * `diff.renames=true`/`=copies` could otherwise emit a single 2-path rename
319
251
  * section (`a/<old> b/<new>`) that a single-path `git diff HEAD -- <file>` can
320
- * never produce; `--no-renames` re-splits it into the same delete + add bytes
252
+ * never produce. `--no-renames` re-splits it into the same delete + add bytes
321
253
  * the per-file loop emitted.
322
254
  *
323
- * Sections are attributed to paths by POSITION against a companion
255
+ * Sections are attributed to paths by position against a companion
324
256
  * `git diff --no-renames HEAD -z --name-only` (raw, unquoted, NUL-delimited
325
- * paths, emitted in the same order as the sections) — never by parsing the
257
+ * paths, emitted in the same order as the sections), never by parsing the
326
258
  * `diff --git` header, which is ambiguous or unparseable under `core.quotePath`
327
259
  * (non-ASCII paths are C-quoted), paths containing spaces, or
328
260
  * `diff.noprefix`/`diff.mnemonicPrefix`. If the section and name counts ever
@@ -341,14 +273,14 @@ async function buildTrackedSections(repoDir, trackedFiles) {
341
273
  const sections = splitDiffSections(combined);
342
274
  if (sections.length === names.length) {
343
275
  for (let i = 0; i < names.length; i++) {
344
- // Exact-path keys (raw git bytes, same encoding as the inputs) — never
276
+ // Exact-path keys (raw git bytes, same encoding as the inputs), never
345
277
  // a substring/startsWith match, so `foo.txt` cannot capture
346
278
  // `foo.txt.bak`'s section.
347
279
  sectionsByPath.set(names[i], sections[i]);
348
280
  }
349
281
  continue;
350
282
  }
351
- // Counts disagree — recover the exact pre-batch per-file bytes for this
283
+ // Counts disagree, so recover the exact pre-batch per-file bytes for this
352
284
  // chunk rather than risk dropping or mis-keying a section.
353
285
  for (const file of chunk) {
354
286
  const diff = await getFileDiff(repoDir, file);
@@ -361,7 +293,7 @@ async function buildTrackedSections(repoDir, trackedFiles) {
361
293
  /**
362
294
  * Builds a combined diff against HEAD for the provided files without touching
363
295
  * the real git index. Tracked files use `git diff --binary HEAD` (binary
364
- * sections included); untracked files use synthesized new-file diffs, or
296
+ * sections included). Untracked files use synthesized new-file diffs, or
365
297
  * {@link generateBinaryFilePatch} when the file is binary.
366
298
  *
367
299
  * Performance: the work is batched into a handful of `git` invocations (one
@@ -376,14 +308,13 @@ async function buildTrackedSections(repoDir, trackedFiles) {
376
308
  * @returns Combined diff content
377
309
  */
378
310
  export async function getDiffForFilesAgainstHead(repoDir, files) {
379
- await ensureGit();
380
311
  // Expand any directory entries (paths ending with `/`) into their
381
312
  // individual untracked files before diffing. `git status --porcelain=v1`
382
313
  // reports collapsed untracked directories as `?? dir/`, and every caller
383
314
  // that feeds the aggregate working-tree state into this function must not
384
315
  // trigger an EISDIR when the diff pass reads `dir/` as if it were a file.
385
316
  // The caller-side expansion in `lint.ts` and `export-all.ts` covers the
386
- // common path; guarding here makes a single bad call site harmless.
317
+ // common path. Guarding here makes a single bad call site harmless.
387
318
  const expandedFiles = [];
388
319
  for (const file of files) {
389
320
  if (file.endsWith('/')) {
@@ -405,7 +336,7 @@ export async function getDiffForFilesAgainstHead(repoDir, files) {
405
336
  const trackedSections = await buildTrackedSections(repoDir, uniqueFiles.filter((file) => tracked.has(file)));
406
337
  // Classify the non-tracked files. Directory and binary entries keep their
407
338
  // per-file handling (rare, and binary patches mutate the index so they must
408
- // stay serial); plain new text files are collected for a single batched
339
+ // stay serial). Plain new text files are collected for a single batched
409
340
  // `git hash-object`. Results land in `sectionByFile`, keyed by path, to be
410
341
  // emitted in sorted order below.
411
342
  const sectionByFile = new Map();
@@ -422,7 +353,7 @@ export async function getDiffForFilesAgainstHead(repoDir, files) {
422
353
  // the trailing slash `expandUntrackedDirectoryEntries` would have
423
354
  // produced (caller stripped it, submodule entry, tracked-file-replaced-
424
355
  // by-dir). Expand it via the same helper used by the slash branch and
425
- // recurse so each contained file is diffed individually; fail loud when
356
+ // recurse so each contained file is diffed individually. Fail loud when
426
357
  // the directory has no readable content rather than silently skipping it.
427
358
  const fileStat = await stat(fullPath);
428
359
  if (fileStat.isDirectory()) {
@@ -435,29 +366,31 @@ export async function getDiffForFilesAgainstHead(repoDir, files) {
435
366
  sectionByFile.set(file, innerDiff);
436
367
  continue;
437
368
  }
438
- if (await isBinaryFile(repoDir, file)) {
369
+ // Binary (by git's NUL heuristic or because the bytes are not valid
370
+ // UTF-8) takes the byte-faithful binary-patch arm.
371
+ const newFile = await readNewFileContent(repoDir, file);
372
+ if (newFile.binary) {
439
373
  const diff = await generateBinaryFilePatch(repoDir, file);
440
374
  if (diff.trim())
441
375
  sectionByFile.set(file, diff);
442
376
  continue;
443
377
  }
444
- newTextFiles.push(file);
378
+ newTextFiles.push({ file, content: newFile.content });
445
379
  }
446
380
  // Batch 3: blob hashes for every new text file in one `git hash-object`.
447
381
  // A miss (rare: a path became unreadable after the stat above) falls back to
448
382
  // the zero hash with the same verbose log the per-file path emitted.
449
- const blobHashes = await hashObjectBatch(repoDir, newTextFiles.map((file) => join(repoDir, file)));
450
- for (const file of newTextFiles) {
451
- const fullPath = join(repoDir, file);
452
- const fullHash = blobHashes.get(fullPath);
383
+ const blobHashes = await hashObjectBatch(repoDir, newTextFiles.map(({ file }) => join(repoDir, file)));
384
+ for (const { file, content } of newTextFiles) {
385
+ const fullHash = blobHashes.get(join(repoDir, file));
453
386
  if (fullHash === undefined) {
454
387
  verbose(`git hash-object failed for ${file}; falling back to zero blob hash`);
455
388
  }
456
- const body = buildNewFileDiffBody(file, await readText(fullPath), abbreviateBlobHash(fullHash));
389
+ const body = buildNewFileDiffBody(file, content, abbreviateBlobHash(fullHash));
457
390
  if (body.trim())
458
391
  sectionByFile.set(file, body);
459
392
  }
460
- // Reassemble in the sorted `uniqueFiles` order — NOT git's section order,
393
+ // Reassemble in the sorted `uniqueFiles` order, not git's section order,
461
394
  // which uses git's own collation and diverges from JS `.sort()` for
462
395
  // non-ASCII paths. Driving emission off `uniqueFiles` (as the previous
463
396
  // per-file loop did) keeps the combined output byte-identical. Do not change
@@ -484,13 +417,12 @@ export async function getDiffForFilesAgainstHead(repoDir, files) {
484
417
  * @returns Diff content for the staged files
485
418
  */
486
419
  export async function getStagedDiffForFiles(repoDir, files) {
487
- await ensureGit();
488
420
  return git(['diff', '--binary', '--cached', 'HEAD', '--', ...files], repoDir);
489
421
  }
490
422
  /**
491
423
  * Serializes the index-mutating untracked-binary path. The bounded per-patch
492
424
  * lint pool can call {@link getDiffForFilesAgainstHead} (and thus this) for
493
- * several patches at once; two concurrent `git add --intent-to-add` / `git
425
+ * several patches at once. Two concurrent `git add --intent-to-add` / `git
494
426
  * reset` sequences would collide on `.git/index.lock` (a hard failure) or
495
427
  * interleave one file's stage with another's unstage. This process-level
496
428
  * promise chain runs the staging sequences one at a time. Read-only callers
@@ -507,28 +439,37 @@ function runWithBinaryStagingLock(task) {
507
439
  * Generates a GIT binary patch for a binary file.
508
440
  * For tracked files, uses `git diff --binary HEAD`.
509
441
  * For untracked files, temporarily stages with `--intent-to-add` to produce a diff.
442
+ *
443
+ * Every `git diff` here runs under {@link withForcedBinaryAttribute}: a
444
+ * caller sends a file down this arm because a text body cannot carry its
445
+ * bytes (NUL, or not valid UTF-8), and git's own NUL-only heuristic would
446
+ * otherwise still render the non-UTF-8 case as text.
510
447
  * @param repoDir - Repository directory
511
448
  * @param filePath - File path (relative to repo root)
512
449
  * @returns The binary diff string, or empty string if no diff
513
450
  */
514
451
  export async function generateBinaryFilePatch(repoDir, filePath) {
515
- await ensureGit();
516
- // Try tracked file diff first (read-only — no index lock needed)
517
- const result = await execGitWithAllowedExitCodes(repoDir, [
518
- 'diff',
519
- '--binary',
520
- 'HEAD',
521
- '--',
522
- filePath,
523
- ]);
524
- if (result.stdout.trim())
525
- return result.stdout;
452
+ return withForcedBinaryAttribute(filePath, (gitConfigArgs) => generateBinaryFilePatchWithConfig(repoDir, filePath, gitConfigArgs));
453
+ }
454
+ /**
455
+ * The body of {@link generateBinaryFilePatch}, with the forced-binary config
456
+ * arguments already resolved.
457
+ * @param repoDir - Repository directory
458
+ * @param filePath - File path (relative to repo root)
459
+ * @param gitConfigArgs - `-c` arguments spliced ahead of every `diff`
460
+ * @returns The binary diff string, or empty string if no diff
461
+ */
462
+ async function generateBinaryFilePatchWithConfig(repoDir, filePath, gitConfigArgs) {
463
+ // Try tracked file diff first (read-only, so no index lock needed)
464
+ const tracked = await git([...gitConfigArgs, 'diff', '--binary', 'HEAD', '--', filePath], repoDir);
465
+ if (tracked.trim())
466
+ return tracked;
526
467
  // The diff for an untracked file has to come from an index entry, so one
527
- // has to be written. Writing it to a PRIVATE index is what keeps that
468
+ // has to be written. Writing it to a private index is what keeps that
528
469
  // write invisible: the primary `.git/index` never moves, so a concurrent
529
470
  // `fireforge test` fingerprinting `engine/` with `git status` cannot
530
471
  // observe the transient `--intent-to-add` entry and void its own verdict
531
- // on our bookkeeping. That was a real failure mode — parallel gate lanes
472
+ // on our bookkeeping. That was a real failure mode: parallel gate lanes
532
473
  // killed three runs with `FAIL reason=inconclusive` on branding PNGs
533
474
  // flapping between `A` and `??`.
534
475
  //
@@ -539,9 +480,10 @@ export async function generateBinaryFilePatch(repoDir, filePath) {
539
480
  const disposable = await mintDisposableGitIndex(repoDir);
540
481
  if (disposable !== undefined) {
541
482
  try {
542
- await execGitWithAllowedExitCodes(repoDir, ['add', '--intent-to-add', '--', filePath], [0], disposable.env);
543
- const diffResult = await execGitWithAllowedExitCodes(repoDir, ['diff', '--binary', '--', filePath], [0], disposable.env);
544
- return diffResult.stdout;
483
+ await git(['add', '--intent-to-add', '--', filePath], repoDir, { env: disposable.env });
484
+ return await git([...gitConfigArgs, 'diff', '--binary', '--', filePath], repoDir, {
485
+ env: disposable.env,
486
+ });
545
487
  }
546
488
  finally {
547
489
  await disposable.dispose();
@@ -556,22 +498,16 @@ export async function generateBinaryFilePatch(repoDir, filePath) {
556
498
  verbose(`Binary patch for ${filePath} is staging on the shared index (no private index).`);
557
499
  return runWithBinaryStagingLock(async () => {
558
500
  // Snapshot the path's exact index entry before touching it. A blanket
559
- // `git reset HEAD -- <file>` cleanup restores the path to its HEAD state
560
- // — for a path absent from HEAD that means *removing* whatever entry the
501
+ // `git reset HEAD -- <file>` cleanup restores the path to its HEAD state.
502
+ // For a path absent from HEAD that means *removing* whatever entry the
561
503
  // index carried, silently discarding any staged state that arrived
562
504
  // between the read-only diff above and this staging block (a concurrent
563
505
  // writer, or a staged-add whose worktree file was deleted). Restoring
564
506
  // the captured entry is exact regardless of how the entry got there.
565
- const priorEntry = (await execGitWithAllowedExitCodes(repoDir, ['ls-files', '--stage', '--', filePath])).stdout;
507
+ const priorEntry = await git(['ls-files', '--stage', '--', filePath], repoDir);
566
508
  try {
567
- await execGitWithAllowedExitCodes(repoDir, ['add', '--intent-to-add', '--', filePath]);
568
- const diffResult = await execGitWithAllowedExitCodes(repoDir, [
569
- 'diff',
570
- '--binary',
571
- '--',
572
- filePath,
573
- ]);
574
- return diffResult.stdout;
509
+ await git(['add', '--intent-to-add', '--', filePath], repoDir);
510
+ return await git([...gitConfigArgs, 'diff', '--binary', '--', filePath], repoDir);
575
511
  }
576
512
  finally {
577
513
  await restoreIndexEntry(repoDir, filePath, priorEntry);
@@ -581,11 +517,12 @@ export async function generateBinaryFilePatch(repoDir, filePath) {
581
517
  /**
582
518
  * Restores a path's index entry to a previously captured `ls-files --stage`
583
519
  * snapshot after a temporary `--intent-to-add` staging. No prior entry means
584
- * the temporary entry is dropped via `reset HEAD` (the path's HEAD state IS
585
- * "absent" — this branch only runs for files untracked in HEAD). A prior
520
+ * the temporary entry is dropped via `reset HEAD` (the path's HEAD state is
521
+ * "absent", and this branch only runs for files untracked in HEAD). A prior
586
522
  * stage-0 entry is re-pointed at its recorded mode and blob. Unmerged entries
587
- * (stages 1–3) cannot be rebuilt through `--cacheinfo`; that degenerate state
588
- * keeps the legacy reset-to-HEAD behavior rather than corrupting the conflict.
523
+ * (stages 1 to 3) cannot be rebuilt through `--cacheinfo`. That degenerate
524
+ * state keeps the legacy reset-to-HEAD behavior rather than corrupting the
525
+ * conflict.
589
526
  * @param repoDir - Repository directory
590
527
  * @param filePath - File path (relative to repo root)
591
528
  * @param priorEntry - Raw `git ls-files --stage -- <file>` output from before staging
@@ -595,13 +532,8 @@ async function restoreIndexEntry(repoDir, filePath, priorEntry) {
595
532
  const mode = entryMatch?.[1];
596
533
  const oid = entryMatch?.[2];
597
534
  if (mode === undefined || oid === undefined) {
598
- await execGitWithAllowedExitCodes(repoDir, ['reset', 'HEAD', '--', filePath]);
535
+ await git(['reset', 'HEAD', '--', filePath], repoDir);
599
536
  return;
600
537
  }
601
- await execGitWithAllowedExitCodes(repoDir, [
602
- 'update-index',
603
- '--add',
604
- '--cacheinfo',
605
- `${mode},${oid},${filePath}`,
606
- ]);
538
+ await git(['update-index', '--add', '--cacheinfo', `${mode},${oid},${filePath}`], repoDir);
607
539
  }
@@ -33,10 +33,10 @@ export declare function fileExistsInHead(repoDir: string, filePath: string): Pro
33
33
  /**
34
34
  * Batched equivalent of {@link fileExistsInHead}: returns the subset of
35
35
  * `files` that are tracked in HEAD, using a single `git ls-tree` per ARG_MAX
36
- * chunk instead of one spawn per file. This is the cold-run hot path — a
37
- * Firefox-sized checkout has hundreds of affected files.
36
+ * chunk instead of one spawn per file. This is the cold-run hot path, and
37
+ * a Firefox-sized checkout has hundreds of affected files.
38
38
  *
39
- * `-r` lists nested blobs by full repo-relative path; `--name-only -z` makes
39
+ * `-r` lists nested blobs by full repo-relative path. `--name-only -z` makes
40
40
  * the output a trivial NUL-split with no quoting to undo. Membership in the
41
41
  * returned Set is exactly `await fileExistsInHead(repoDir, file)` for any
42
42
  * non-directory `file`. Throws (via {@link git}) when HEAD itself is
@@ -56,10 +56,10 @@ export declare function listTrackedInHead(repoDir: string, files: string[]): Pro
56
56
  * Uses {@link import('../utils/process.js').exec} rather than {@link git}
57
57
  * (which throws on a non-zero exit) because `git hash-object f1 f2 …` is
58
58
  * all-or-nothing: it aborts at the first unreadable path and emits nothing
59
- * for the rest. To keep the per-file contract — where one bad path zeroes
60
- * only its own index line — a chunk that does not return exactly one hash
59
+ * for the rest. To keep the per-file contract, where one bad path zeroes
60
+ * only its own index line, a chunk that does not return exactly one hash
61
61
  * per input falls back to hashing that chunk's paths individually. A path
62
- * that is still unhashable is left out of the map; the caller applies the
62
+ * that is still unhashable is left out of the map. The caller applies the
63
63
  * `0000000000` zero-hash fallback for any miss. Hashing stays in git rather
64
64
  * than in-process so filters and `.gitattributes` are applied per path and
65
65
  * the result cannot diverge under `core.autocrlf`/`text` attributes.
@@ -77,13 +77,6 @@ export declare function hashObjectBatch(repoDir: string, fullPaths: string[]): P
77
77
  * @returns File content or null if file doesn't exist at that ref
78
78
  */
79
79
  export declare function getFileContentAtRef(repoDir: string, filePath: string, ref?: string): Promise<string | null>;
80
- /**
81
- * Gets the content of a file from HEAD commit.
82
- * @param repoDir - Repository directory
83
- * @param filePath - Path to the file (relative to repo)
84
- * @returns File content or null if file doesn't exist in HEAD
85
- */
86
- export declare function getFileContentFromHead(repoDir: string, filePath: string): Promise<string | null>;
87
80
  /**
88
81
  * Checks if a file is binary by looking for NUL bytes in the first 8KB.
89
82
  * Uses the same heuristic as git.