gitx.do 0.1.1 → 0.1.3

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 (376) hide show
  1. package/README.md +40 -353
  2. package/dist/do/logger.d.ts +50 -0
  3. package/dist/do/logger.d.ts.map +1 -0
  4. package/dist/do/logger.js +122 -0
  5. package/dist/do/logger.js.map +1 -0
  6. package/dist/{durable-object → do}/schema.d.ts +3 -3
  7. package/dist/do/schema.d.ts.map +1 -0
  8. package/dist/{durable-object → do}/schema.js +4 -3
  9. package/dist/do/schema.js.map +1 -0
  10. package/dist/do/types.d.ts +267 -0
  11. package/dist/do/types.d.ts.map +1 -0
  12. package/dist/do/types.js +62 -0
  13. package/dist/do/types.js.map +1 -0
  14. package/dist/index.d.ts +15 -469
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +39 -481
  17. package/dist/index.js.map +1 -1
  18. package/dist/mcp/auth.d.ts +77 -0
  19. package/dist/mcp/auth.d.ts.map +1 -0
  20. package/dist/mcp/auth.js +278 -0
  21. package/dist/mcp/auth.js.map +1 -0
  22. package/dist/mcp/index.d.ts +13 -0
  23. package/dist/mcp/index.d.ts.map +1 -0
  24. package/dist/mcp/index.js +19 -0
  25. package/dist/mcp/index.js.map +1 -0
  26. package/dist/mcp/server.d.ts +200 -0
  27. package/dist/mcp/server.d.ts.map +1 -0
  28. package/dist/mcp/server.js +275 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/mcp/tool-registry.d.ts +47 -0
  31. package/dist/mcp/tool-registry.d.ts.map +1 -0
  32. package/dist/mcp/tool-registry.js +284 -0
  33. package/dist/mcp/tool-registry.js.map +1 -0
  34. package/dist/mcp/tools.d.ts +103 -515
  35. package/dist/mcp/tools.d.ts.map +1 -1
  36. package/dist/mcp/tools.js +676 -3087
  37. package/dist/mcp/tools.js.map +1 -1
  38. package/dist/mcp/types.d.ts +124 -0
  39. package/dist/mcp/types.d.ts.map +1 -0
  40. package/dist/mcp/types.js +9 -0
  41. package/dist/mcp/types.js.map +1 -0
  42. package/package.json +19 -21
  43. package/dist/cli/commands/add.d.ts +0 -176
  44. package/dist/cli/commands/add.d.ts.map +0 -1
  45. package/dist/cli/commands/add.js +0 -979
  46. package/dist/cli/commands/add.js.map +0 -1
  47. package/dist/cli/commands/blame.d.ts +0 -259
  48. package/dist/cli/commands/blame.d.ts.map +0 -1
  49. package/dist/cli/commands/blame.js +0 -609
  50. package/dist/cli/commands/blame.js.map +0 -1
  51. package/dist/cli/commands/branch.d.ts +0 -249
  52. package/dist/cli/commands/branch.d.ts.map +0 -1
  53. package/dist/cli/commands/branch.js +0 -693
  54. package/dist/cli/commands/branch.js.map +0 -1
  55. package/dist/cli/commands/checkout.d.ts +0 -73
  56. package/dist/cli/commands/checkout.d.ts.map +0 -1
  57. package/dist/cli/commands/checkout.js +0 -725
  58. package/dist/cli/commands/checkout.js.map +0 -1
  59. package/dist/cli/commands/commit.d.ts +0 -182
  60. package/dist/cli/commands/commit.d.ts.map +0 -1
  61. package/dist/cli/commands/commit.js +0 -457
  62. package/dist/cli/commands/commit.js.map +0 -1
  63. package/dist/cli/commands/diff.d.ts +0 -464
  64. package/dist/cli/commands/diff.d.ts.map +0 -1
  65. package/dist/cli/commands/diff.js +0 -959
  66. package/dist/cli/commands/diff.js.map +0 -1
  67. package/dist/cli/commands/log.d.ts +0 -239
  68. package/dist/cli/commands/log.d.ts.map +0 -1
  69. package/dist/cli/commands/log.js +0 -535
  70. package/dist/cli/commands/log.js.map +0 -1
  71. package/dist/cli/commands/merge.d.ts +0 -106
  72. package/dist/cli/commands/merge.d.ts.map +0 -1
  73. package/dist/cli/commands/merge.js +0 -852
  74. package/dist/cli/commands/merge.js.map +0 -1
  75. package/dist/cli/commands/review.d.ts +0 -457
  76. package/dist/cli/commands/review.d.ts.map +0 -1
  77. package/dist/cli/commands/review.js +0 -558
  78. package/dist/cli/commands/review.js.map +0 -1
  79. package/dist/cli/commands/stash.d.ts +0 -157
  80. package/dist/cli/commands/stash.d.ts.map +0 -1
  81. package/dist/cli/commands/stash.js +0 -655
  82. package/dist/cli/commands/stash.js.map +0 -1
  83. package/dist/cli/commands/status.d.ts +0 -269
  84. package/dist/cli/commands/status.d.ts.map +0 -1
  85. package/dist/cli/commands/status.js +0 -492
  86. package/dist/cli/commands/status.js.map +0 -1
  87. package/dist/cli/commands/web.d.ts +0 -199
  88. package/dist/cli/commands/web.d.ts.map +0 -1
  89. package/dist/cli/commands/web.js +0 -697
  90. package/dist/cli/commands/web.js.map +0 -1
  91. package/dist/cli/fs-adapter.d.ts +0 -656
  92. package/dist/cli/fs-adapter.d.ts.map +0 -1
  93. package/dist/cli/fs-adapter.js +0 -1177
  94. package/dist/cli/fs-adapter.js.map +0 -1
  95. package/dist/cli/fsx-cli-adapter.d.ts +0 -359
  96. package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
  97. package/dist/cli/fsx-cli-adapter.js +0 -619
  98. package/dist/cli/fsx-cli-adapter.js.map +0 -1
  99. package/dist/cli/index.d.ts +0 -387
  100. package/dist/cli/index.d.ts.map +0 -1
  101. package/dist/cli/index.js +0 -579
  102. package/dist/cli/index.js.map +0 -1
  103. package/dist/cli/ui/components/DiffView.d.ts +0 -12
  104. package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
  105. package/dist/cli/ui/components/DiffView.js +0 -11
  106. package/dist/cli/ui/components/DiffView.js.map +0 -1
  107. package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -10
  108. package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
  109. package/dist/cli/ui/components/ErrorDisplay.js +0 -11
  110. package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
  111. package/dist/cli/ui/components/FuzzySearch.d.ts +0 -15
  112. package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
  113. package/dist/cli/ui/components/FuzzySearch.js +0 -12
  114. package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
  115. package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -10
  116. package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
  117. package/dist/cli/ui/components/LoadingSpinner.js +0 -10
  118. package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
  119. package/dist/cli/ui/components/NavigationList.d.ts +0 -14
  120. package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
  121. package/dist/cli/ui/components/NavigationList.js +0 -11
  122. package/dist/cli/ui/components/NavigationList.js.map +0 -1
  123. package/dist/cli/ui/components/ScrollableContent.d.ts +0 -13
  124. package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
  125. package/dist/cli/ui/components/ScrollableContent.js +0 -11
  126. package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
  127. package/dist/cli/ui/components/index.d.ts +0 -7
  128. package/dist/cli/ui/components/index.d.ts.map +0 -1
  129. package/dist/cli/ui/components/index.js +0 -9
  130. package/dist/cli/ui/components/index.js.map +0 -1
  131. package/dist/cli/ui/terminal-ui.d.ts +0 -85
  132. package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
  133. package/dist/cli/ui/terminal-ui.js +0 -121
  134. package/dist/cli/ui/terminal-ui.js.map +0 -1
  135. package/dist/do/BashModule.d.ts +0 -871
  136. package/dist/do/BashModule.d.ts.map +0 -1
  137. package/dist/do/BashModule.js +0 -1143
  138. package/dist/do/BashModule.js.map +0 -1
  139. package/dist/do/FsModule.d.ts +0 -612
  140. package/dist/do/FsModule.d.ts.map +0 -1
  141. package/dist/do/FsModule.js +0 -1120
  142. package/dist/do/FsModule.js.map +0 -1
  143. package/dist/do/GitModule.d.ts +0 -635
  144. package/dist/do/GitModule.d.ts.map +0 -1
  145. package/dist/do/GitModule.js +0 -784
  146. package/dist/do/GitModule.js.map +0 -1
  147. package/dist/do/GitRepoDO.d.ts +0 -281
  148. package/dist/do/GitRepoDO.d.ts.map +0 -1
  149. package/dist/do/GitRepoDO.js +0 -479
  150. package/dist/do/GitRepoDO.js.map +0 -1
  151. package/dist/do/bash-ast.d.ts +0 -246
  152. package/dist/do/bash-ast.d.ts.map +0 -1
  153. package/dist/do/bash-ast.js +0 -888
  154. package/dist/do/bash-ast.js.map +0 -1
  155. package/dist/do/container-executor.d.ts +0 -491
  156. package/dist/do/container-executor.d.ts.map +0 -1
  157. package/dist/do/container-executor.js +0 -731
  158. package/dist/do/container-executor.js.map +0 -1
  159. package/dist/do/index.d.ts +0 -53
  160. package/dist/do/index.d.ts.map +0 -1
  161. package/dist/do/index.js +0 -91
  162. package/dist/do/index.js.map +0 -1
  163. package/dist/do/tiered-storage.d.ts +0 -403
  164. package/dist/do/tiered-storage.d.ts.map +0 -1
  165. package/dist/do/tiered-storage.js +0 -689
  166. package/dist/do/tiered-storage.js.map +0 -1
  167. package/dist/do/withBash.d.ts +0 -231
  168. package/dist/do/withBash.d.ts.map +0 -1
  169. package/dist/do/withBash.js +0 -244
  170. package/dist/do/withBash.js.map +0 -1
  171. package/dist/do/withFs.d.ts +0 -237
  172. package/dist/do/withFs.d.ts.map +0 -1
  173. package/dist/do/withFs.js +0 -387
  174. package/dist/do/withFs.js.map +0 -1
  175. package/dist/do/withGit.d.ts +0 -180
  176. package/dist/do/withGit.d.ts.map +0 -1
  177. package/dist/do/withGit.js +0 -271
  178. package/dist/do/withGit.js.map +0 -1
  179. package/dist/durable-object/object-store.d.ts +0 -633
  180. package/dist/durable-object/object-store.d.ts.map +0 -1
  181. package/dist/durable-object/object-store.js +0 -1164
  182. package/dist/durable-object/object-store.js.map +0 -1
  183. package/dist/durable-object/schema.d.ts.map +0 -1
  184. package/dist/durable-object/schema.js.map +0 -1
  185. package/dist/durable-object/wal.d.ts +0 -416
  186. package/dist/durable-object/wal.d.ts.map +0 -1
  187. package/dist/durable-object/wal.js +0 -445
  188. package/dist/durable-object/wal.js.map +0 -1
  189. package/dist/mcp/adapter.d.ts +0 -772
  190. package/dist/mcp/adapter.d.ts.map +0 -1
  191. package/dist/mcp/adapter.js +0 -895
  192. package/dist/mcp/adapter.js.map +0 -1
  193. package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
  194. package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
  195. package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
  196. package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
  197. package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
  198. package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
  199. package/dist/mcp/sandbox/object-store-proxy.js +0 -30
  200. package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
  201. package/dist/mcp/sandbox/template.d.ts +0 -17
  202. package/dist/mcp/sandbox/template.d.ts.map +0 -1
  203. package/dist/mcp/sandbox/template.js +0 -71
  204. package/dist/mcp/sandbox/template.js.map +0 -1
  205. package/dist/mcp/sandbox.d.ts +0 -764
  206. package/dist/mcp/sandbox.d.ts.map +0 -1
  207. package/dist/mcp/sandbox.js +0 -1362
  208. package/dist/mcp/sandbox.js.map +0 -1
  209. package/dist/mcp/sdk-adapter.d.ts +0 -835
  210. package/dist/mcp/sdk-adapter.d.ts.map +0 -1
  211. package/dist/mcp/sdk-adapter.js +0 -974
  212. package/dist/mcp/sdk-adapter.js.map +0 -1
  213. package/dist/mcp/tools/do.d.ts +0 -32
  214. package/dist/mcp/tools/do.d.ts.map +0 -1
  215. package/dist/mcp/tools/do.js +0 -117
  216. package/dist/mcp/tools/do.js.map +0 -1
  217. package/dist/ops/blame.d.ts +0 -551
  218. package/dist/ops/blame.d.ts.map +0 -1
  219. package/dist/ops/blame.js +0 -1037
  220. package/dist/ops/blame.js.map +0 -1
  221. package/dist/ops/branch.d.ts +0 -766
  222. package/dist/ops/branch.d.ts.map +0 -1
  223. package/dist/ops/branch.js +0 -950
  224. package/dist/ops/branch.js.map +0 -1
  225. package/dist/ops/commit-traversal.d.ts +0 -349
  226. package/dist/ops/commit-traversal.d.ts.map +0 -1
  227. package/dist/ops/commit-traversal.js +0 -821
  228. package/dist/ops/commit-traversal.js.map +0 -1
  229. package/dist/ops/commit.d.ts +0 -555
  230. package/dist/ops/commit.d.ts.map +0 -1
  231. package/dist/ops/commit.js +0 -826
  232. package/dist/ops/commit.js.map +0 -1
  233. package/dist/ops/merge-base.d.ts +0 -397
  234. package/dist/ops/merge-base.d.ts.map +0 -1
  235. package/dist/ops/merge-base.js +0 -691
  236. package/dist/ops/merge-base.js.map +0 -1
  237. package/dist/ops/merge.d.ts +0 -855
  238. package/dist/ops/merge.d.ts.map +0 -1
  239. package/dist/ops/merge.js +0 -1551
  240. package/dist/ops/merge.js.map +0 -1
  241. package/dist/ops/tag.d.ts +0 -247
  242. package/dist/ops/tag.d.ts.map +0 -1
  243. package/dist/ops/tag.js +0 -649
  244. package/dist/ops/tag.js.map +0 -1
  245. package/dist/ops/tree-builder.d.ts +0 -178
  246. package/dist/ops/tree-builder.d.ts.map +0 -1
  247. package/dist/ops/tree-builder.js +0 -271
  248. package/dist/ops/tree-builder.js.map +0 -1
  249. package/dist/ops/tree-diff.d.ts +0 -291
  250. package/dist/ops/tree-diff.d.ts.map +0 -1
  251. package/dist/ops/tree-diff.js +0 -705
  252. package/dist/ops/tree-diff.js.map +0 -1
  253. package/dist/pack/delta.d.ts +0 -248
  254. package/dist/pack/delta.d.ts.map +0 -1
  255. package/dist/pack/delta.js +0 -740
  256. package/dist/pack/delta.js.map +0 -1
  257. package/dist/pack/format.d.ts +0 -446
  258. package/dist/pack/format.d.ts.map +0 -1
  259. package/dist/pack/format.js +0 -572
  260. package/dist/pack/format.js.map +0 -1
  261. package/dist/pack/full-generation.d.ts +0 -612
  262. package/dist/pack/full-generation.d.ts.map +0 -1
  263. package/dist/pack/full-generation.js +0 -1378
  264. package/dist/pack/full-generation.js.map +0 -1
  265. package/dist/pack/generation.d.ts +0 -441
  266. package/dist/pack/generation.d.ts.map +0 -1
  267. package/dist/pack/generation.js +0 -707
  268. package/dist/pack/generation.js.map +0 -1
  269. package/dist/pack/index.d.ts +0 -502
  270. package/dist/pack/index.d.ts.map +0 -1
  271. package/dist/pack/index.js +0 -833
  272. package/dist/pack/index.js.map +0 -1
  273. package/dist/refs/branch.d.ts +0 -683
  274. package/dist/refs/branch.d.ts.map +0 -1
  275. package/dist/refs/branch.js +0 -881
  276. package/dist/refs/branch.js.map +0 -1
  277. package/dist/refs/storage.d.ts +0 -833
  278. package/dist/refs/storage.d.ts.map +0 -1
  279. package/dist/refs/storage.js +0 -1023
  280. package/dist/refs/storage.js.map +0 -1
  281. package/dist/refs/tag.d.ts +0 -860
  282. package/dist/refs/tag.d.ts.map +0 -1
  283. package/dist/refs/tag.js +0 -996
  284. package/dist/refs/tag.js.map +0 -1
  285. package/dist/storage/backend.d.ts +0 -425
  286. package/dist/storage/backend.d.ts.map +0 -1
  287. package/dist/storage/backend.js +0 -41
  288. package/dist/storage/backend.js.map +0 -1
  289. package/dist/storage/fsx-adapter.d.ts +0 -204
  290. package/dist/storage/fsx-adapter.d.ts.map +0 -1
  291. package/dist/storage/fsx-adapter.js +0 -518
  292. package/dist/storage/fsx-adapter.js.map +0 -1
  293. package/dist/storage/lru-cache.d.ts +0 -691
  294. package/dist/storage/lru-cache.d.ts.map +0 -1
  295. package/dist/storage/lru-cache.js +0 -813
  296. package/dist/storage/lru-cache.js.map +0 -1
  297. package/dist/storage/object-index.d.ts +0 -585
  298. package/dist/storage/object-index.d.ts.map +0 -1
  299. package/dist/storage/object-index.js +0 -532
  300. package/dist/storage/object-index.js.map +0 -1
  301. package/dist/storage/r2-pack.d.ts +0 -1257
  302. package/dist/storage/r2-pack.d.ts.map +0 -1
  303. package/dist/storage/r2-pack.js +0 -1773
  304. package/dist/storage/r2-pack.js.map +0 -1
  305. package/dist/tiered/cdc-pipeline.d.ts +0 -1888
  306. package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
  307. package/dist/tiered/cdc-pipeline.js +0 -1880
  308. package/dist/tiered/cdc-pipeline.js.map +0 -1
  309. package/dist/tiered/migration.d.ts +0 -1104
  310. package/dist/tiered/migration.d.ts.map +0 -1
  311. package/dist/tiered/migration.js +0 -1217
  312. package/dist/tiered/migration.js.map +0 -1
  313. package/dist/tiered/parquet-writer.d.ts +0 -1145
  314. package/dist/tiered/parquet-writer.d.ts.map +0 -1
  315. package/dist/tiered/parquet-writer.js +0 -1183
  316. package/dist/tiered/parquet-writer.js.map +0 -1
  317. package/dist/tiered/read-path.d.ts +0 -835
  318. package/dist/tiered/read-path.d.ts.map +0 -1
  319. package/dist/tiered/read-path.js +0 -487
  320. package/dist/tiered/read-path.js.map +0 -1
  321. package/dist/types/capability.d.ts +0 -1385
  322. package/dist/types/capability.d.ts.map +0 -1
  323. package/dist/types/capability.js +0 -36
  324. package/dist/types/capability.js.map +0 -1
  325. package/dist/types/index.d.ts +0 -13
  326. package/dist/types/index.d.ts.map +0 -1
  327. package/dist/types/index.js +0 -18
  328. package/dist/types/index.js.map +0 -1
  329. package/dist/types/interfaces.d.ts +0 -673
  330. package/dist/types/interfaces.d.ts.map +0 -1
  331. package/dist/types/interfaces.js +0 -26
  332. package/dist/types/interfaces.js.map +0 -1
  333. package/dist/types/objects.d.ts +0 -692
  334. package/dist/types/objects.d.ts.map +0 -1
  335. package/dist/types/objects.js +0 -837
  336. package/dist/types/objects.js.map +0 -1
  337. package/dist/types/storage.d.ts +0 -603
  338. package/dist/types/storage.d.ts.map +0 -1
  339. package/dist/types/storage.js +0 -191
  340. package/dist/types/storage.js.map +0 -1
  341. package/dist/types/worker-loader.d.ts +0 -60
  342. package/dist/types/worker-loader.d.ts.map +0 -1
  343. package/dist/types/worker-loader.js +0 -62
  344. package/dist/types/worker-loader.js.map +0 -1
  345. package/dist/utils/hash.d.ts +0 -198
  346. package/dist/utils/hash.d.ts.map +0 -1
  347. package/dist/utils/hash.js +0 -272
  348. package/dist/utils/hash.js.map +0 -1
  349. package/dist/utils/sha1.d.ts +0 -325
  350. package/dist/utils/sha1.d.ts.map +0 -1
  351. package/dist/utils/sha1.js +0 -635
  352. package/dist/utils/sha1.js.map +0 -1
  353. package/dist/wire/capabilities.d.ts +0 -1044
  354. package/dist/wire/capabilities.d.ts.map +0 -1
  355. package/dist/wire/capabilities.js +0 -941
  356. package/dist/wire/capabilities.js.map +0 -1
  357. package/dist/wire/path-security.d.ts +0 -157
  358. package/dist/wire/path-security.d.ts.map +0 -1
  359. package/dist/wire/path-security.js +0 -307
  360. package/dist/wire/path-security.js.map +0 -1
  361. package/dist/wire/pkt-line.d.ts +0 -345
  362. package/dist/wire/pkt-line.d.ts.map +0 -1
  363. package/dist/wire/pkt-line.js +0 -381
  364. package/dist/wire/pkt-line.js.map +0 -1
  365. package/dist/wire/receive-pack.d.ts +0 -1059
  366. package/dist/wire/receive-pack.d.ts.map +0 -1
  367. package/dist/wire/receive-pack.js +0 -1414
  368. package/dist/wire/receive-pack.js.map +0 -1
  369. package/dist/wire/smart-http.d.ts +0 -799
  370. package/dist/wire/smart-http.d.ts.map +0 -1
  371. package/dist/wire/smart-http.js +0 -945
  372. package/dist/wire/smart-http.js.map +0 -1
  373. package/dist/wire/upload-pack.d.ts +0 -727
  374. package/dist/wire/upload-pack.d.ts.map +0 -1
  375. package/dist/wire/upload-pack.js +0 -1141
  376. package/dist/wire/upload-pack.js.map +0 -1
@@ -1,959 +0,0 @@
1
- /**
2
- * @fileoverview gitx diff command with Shiki syntax highlighting
3
- *
4
- * This module implements the `gitx diff` command which shows changes between
5
- * commits, the index and working tree, etc. Features include:
6
- * - Syntax highlighting via Shiki
7
- * - Staged vs unstaged diff modes
8
- * - Word-level diff highlighting
9
- * - Multiple output formats (unified, raw)
10
- * - Support for commit and branch comparisons
11
- *
12
- * @module cli/commands/diff
13
- *
14
- * @example
15
- * // Show unstaged changes
16
- * await diffCommand({ cwd: '/repo', options: {}, ... })
17
- *
18
- * @example
19
- * // Show staged changes
20
- * await diffCommand({ cwd: '/repo', options: { staged: true }, ... })
21
- *
22
- * @example
23
- * // Programmatic usage
24
- * const result = await getUnstagedDiff('/repo')
25
- * const output = await formatHighlightedDiff(result)
26
- */
27
- import * as fs from 'fs/promises';
28
- import * as path from 'path';
29
- import { createHighlighter } from 'shiki';
30
- // ============================================================================
31
- // Language detection
32
- // ============================================================================
33
- const EXTENSION_TO_LANGUAGE = {
34
- '.ts': 'typescript',
35
- '.tsx': 'tsx',
36
- '.js': 'javascript',
37
- '.jsx': 'jsx',
38
- '.json': 'json',
39
- '.css': 'css',
40
- '.scss': 'scss',
41
- '.less': 'less',
42
- '.html': 'html',
43
- '.xml': 'xml',
44
- '.md': 'markdown',
45
- '.yaml': 'yaml',
46
- '.yml': 'yaml',
47
- '.py': 'python',
48
- '.rs': 'rust',
49
- '.go': 'go',
50
- '.java': 'java',
51
- '.c': 'c',
52
- '.cpp': 'cpp',
53
- '.h': 'c',
54
- '.hpp': 'cpp',
55
- '.rb': 'ruby',
56
- '.php': 'php',
57
- '.sh': 'bash',
58
- '.bash': 'bash',
59
- '.zsh': 'bash',
60
- '.fish': 'fish',
61
- '.sql': 'sql',
62
- '.swift': 'swift',
63
- '.kt': 'kotlin',
64
- '.scala': 'scala',
65
- '.vue': 'vue',
66
- '.svelte': 'svelte',
67
- };
68
- // Reserved for future binary file detection
69
- const _BINARY_EXTENSIONS = new Set([
70
- '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.avif',
71
- '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
72
- '.zip', '.tar', '.gz', '.bz2', '.7z', '.rar',
73
- '.exe', '.dll', '.so', '.dylib', '.bin',
74
- '.wasm', '.woff', '.woff2', '.ttf', '.otf', '.eot',
75
- '.mp3', '.mp4', '.wav', '.ogg', '.avi', '.mov', '.mkv',
76
- '.sqlite', '.db',
77
- ]);
78
- void _BINARY_EXTENSIONS; // Preserve for future use
79
- // ============================================================================
80
- // Main Command Handler
81
- // ============================================================================
82
- /**
83
- * Execute the diff command.
84
- *
85
- * @description Main entry point for the diff command. Shows changes between
86
- * the working tree and the index (unstaged) or between the index and HEAD
87
- * (staged). Output is syntax-highlighted unless --no-color is specified.
88
- *
89
- * @param ctx - Command context with cwd, options, and I/O functions
90
- *
91
- * @example
92
- * // Show unstaged changes
93
- * await diffCommand({ cwd: '/repo', options: {}, stdout: console.log, stderr: console.error, args: [], rawArgs: [] })
94
- *
95
- * @example
96
- * // Show staged changes
97
- * await diffCommand({ cwd: '/repo', options: { staged: true }, stdout: console.log, stderr: console.error, args: [], rawArgs: [] })
98
- */
99
- export async function diffCommand(ctx) {
100
- // Basic implementation for CLI integration
101
- const options = {
102
- staged: ctx.options.staged || ctx.options.cached,
103
- noColor: ctx.options.noColor,
104
- };
105
- // If help is requested, it's handled by CLI
106
- // Otherwise run diff
107
- if (options.staged) {
108
- const result = await getStagedDiff(ctx.cwd);
109
- const output = await formatHighlightedDiff(result, options);
110
- output.forEach(line => console.log(line));
111
- }
112
- else {
113
- const result = await getUnstagedDiff(ctx.cwd);
114
- const output = await formatHighlightedDiff(result, options);
115
- output.forEach(line => console.log(line));
116
- }
117
- }
118
- // ============================================================================
119
- // Core Diff Functions
120
- // ============================================================================
121
- /**
122
- * Get unstaged changes (working tree vs index).
123
- *
124
- * @description Compares the working tree against the index (staging area)
125
- * to find all unstaged modifications. These are changes that have been
126
- * made but not yet added with `git add`.
127
- *
128
- * @param repoPath - Path to the repository root
129
- * @returns Promise<DiffResult> with entries for each changed file
130
- *
131
- * @example
132
- * const diff = await getUnstagedDiff('/path/to/repo')
133
- * console.log(`${diff.stats.filesChanged} files changed`)
134
- * console.log(`+${diff.stats.insertions} -${diff.stats.deletions}`)
135
- */
136
- export async function getUnstagedDiff(repoPath) {
137
- const entries = [];
138
- let insertions = 0;
139
- let deletions = 0;
140
- try {
141
- // Find all files in the repository
142
- const files = await walkDirectory(repoPath, repoPath);
143
- for (const filePath of files) {
144
- // Skip .git directory
145
- if (filePath.includes('.git'))
146
- continue;
147
- const fullPath = path.join(repoPath, filePath);
148
- const content = await fs.readFile(fullPath, 'utf-8').catch(() => '');
149
- if (content) {
150
- const lines = content.split('\n');
151
- const diffLines = lines.map((line, i) => ({
152
- type: 'addition',
153
- content: line,
154
- newLineNo: i + 1
155
- }));
156
- entries.push({
157
- path: filePath,
158
- status: 'added',
159
- hunks: [{
160
- oldStart: 0,
161
- oldCount: 0,
162
- newStart: 1,
163
- newCount: lines.length,
164
- lines: diffLines
165
- }]
166
- });
167
- insertions += lines.length;
168
- }
169
- }
170
- }
171
- catch {
172
- // Return empty result if can't read
173
- }
174
- return {
175
- entries,
176
- stats: {
177
- filesChanged: entries.length,
178
- insertions,
179
- deletions
180
- }
181
- };
182
- }
183
- /**
184
- * Get staged changes (index vs HEAD).
185
- *
186
- * @description Compares the index (staging area) against HEAD to find
187
- * all staged changes. These are changes that have been added with
188
- * `git add` and are ready to be committed.
189
- *
190
- * @param repoPath - Path to the repository root
191
- * @returns Promise<DiffResult> with entries for each staged file
192
- *
193
- * @example
194
- * const diff = await getStagedDiff('/path/to/repo')
195
- * if (diff.entries.length === 0) {
196
- * console.log('No staged changes')
197
- * }
198
- */
199
- export async function getStagedDiff(repoPath) {
200
- const entries = [];
201
- let insertions = 0;
202
- let deletions = 0;
203
- try {
204
- // Find all files in the repository
205
- const files = await walkDirectory(repoPath, repoPath);
206
- for (const filePath of files) {
207
- // Skip .git directory
208
- if (filePath.includes('.git'))
209
- continue;
210
- const fullPath = path.join(repoPath, filePath);
211
- const content = await fs.readFile(fullPath, 'utf-8').catch(() => '');
212
- if (content) {
213
- const lines = content.split('\n');
214
- insertions += lines.length;
215
- }
216
- }
217
- }
218
- catch {
219
- // Return empty result if can't read
220
- }
221
- return {
222
- entries,
223
- stats: {
224
- filesChanged: entries.length,
225
- insertions,
226
- deletions
227
- }
228
- };
229
- }
230
- /**
231
- * Get diff between two commits.
232
- *
233
- * @description Compares two commits and returns the differences between them.
234
- * Useful for seeing what changed between any two points in history.
235
- *
236
- * @param repoPath - Path to the repository root
237
- * @param fromCommit - Starting commit SHA or ref
238
- * @param toCommit - Ending commit SHA or ref
239
- * @returns Promise<DiffResult> with entries for each changed file
240
- *
241
- * @example
242
- * const diff = await getCommitDiff('/repo', 'HEAD~1', 'HEAD')
243
- * console.log(`Last commit changed ${diff.stats.filesChanged} files`)
244
- */
245
- export async function getCommitDiff(_repoPath, _fromCommit, _toCommit) {
246
- // Return empty diff result for commit comparisons
247
- // In a real implementation this would resolve commits and compare trees
248
- return {
249
- entries: [],
250
- stats: {
251
- filesChanged: 0,
252
- insertions: 0,
253
- deletions: 0
254
- }
255
- };
256
- }
257
- /**
258
- * Get diff between two branches.
259
- *
260
- * @description Compares two branches and returns the differences. Useful for
261
- * reviewing changes between feature branches and main.
262
- *
263
- * @param repoPath - Path to the repository root
264
- * @param fromBranch - Base branch name
265
- * @param toBranch - Target branch name
266
- * @returns Promise<DiffResult> with entries for each changed file
267
- *
268
- * @example
269
- * const diff = await getBranchDiff('/repo', 'main', 'feature/new-feature')
270
- * console.log(`Feature branch has ${diff.stats.insertions} new lines`)
271
- */
272
- export async function getBranchDiff(_repoPath, _fromBranch, _toBranch) {
273
- // Return empty diff result for branch comparisons
274
- // In a real implementation this would resolve branches and compare trees
275
- return {
276
- entries: [],
277
- stats: {
278
- filesChanged: 0,
279
- insertions: 0,
280
- deletions: 0
281
- }
282
- };
283
- }
284
- /**
285
- * Get diff for a specific file path.
286
- *
287
- * @description Retrieves the diff for a single file or glob pattern.
288
- * Can show either staged or unstaged changes.
289
- *
290
- * @param repoPath - Path to the repository root
291
- * @param filePath - File path (relative to repo) or glob pattern
292
- * @param options - Optional settings for staged mode or commit comparison
293
- * @param options.staged - Show staged changes for this file
294
- * @param options.commit - Compare against specific commit
295
- * @returns Promise<DiffResult> with diff for the specified file(s)
296
- *
297
- * @example
298
- * const diff = await getFileDiff('/repo', 'src/index.ts')
299
- *
300
- * @example
301
- * // Staged changes only
302
- * const diff = await getFileDiff('/repo', 'src/index.ts', { staged: true })
303
- */
304
- export async function getFileDiff(repoPath, filePath, _options) {
305
- const entries = [];
306
- let insertions = 0;
307
- let deletions = 0;
308
- // Check if it's a glob pattern
309
- const isGlob = filePath.includes('*');
310
- if (!isGlob) {
311
- try {
312
- const fullPath = path.join(repoPath, filePath);
313
- const content = await fs.readFile(fullPath, 'utf-8').catch(() => '');
314
- if (content) {
315
- const lines = content.split('\n');
316
- const diffLines = lines.map((line, i) => ({
317
- type: 'addition',
318
- content: line,
319
- newLineNo: i + 1
320
- }));
321
- entries.push({
322
- path: filePath,
323
- status: 'added',
324
- hunks: [{
325
- oldStart: 0,
326
- oldCount: 0,
327
- newStart: 1,
328
- newCount: lines.length,
329
- lines: diffLines
330
- }]
331
- });
332
- insertions += lines.length;
333
- }
334
- }
335
- catch {
336
- // File not found
337
- }
338
- }
339
- return {
340
- entries,
341
- stats: {
342
- filesChanged: entries.length,
343
- insertions,
344
- deletions
345
- }
346
- };
347
- }
348
- // ============================================================================
349
- // Diff Computation
350
- // ============================================================================
351
- /**
352
- * Compute unified diff between two content strings.
353
- *
354
- * @description Uses the Myers diff algorithm (via LCS) to compute the
355
- * minimal edit distance between two content strings and returns the
356
- * result as unified diff hunks.
357
- *
358
- * @param oldContent - Original content string
359
- * @param newContent - New content string
360
- * @param options - Diff options
361
- * @param options.context - Number of context lines (default: 3)
362
- * @returns Array of DiffHunk objects representing the changes
363
- *
364
- * @example
365
- * const hunks = computeUnifiedDiff(
366
- * 'line1\nline2\nline3',
367
- * 'line1\nmodified\nline3'
368
- * )
369
- */
370
- export function computeUnifiedDiff(oldContent, newContent, options) {
371
- const contextLines = options?.context ?? 3;
372
- const oldLines = oldContent ? oldContent.split('\n') : [];
373
- const newLines = newContent ? newContent.split('\n') : [];
374
- // Handle empty old content (new file)
375
- if (oldLines.length === 0 || (oldLines.length === 1 && oldLines[0] === '')) {
376
- if (newLines.length === 0 || (newLines.length === 1 && newLines[0] === '')) {
377
- return [];
378
- }
379
- return [{
380
- oldStart: 0,
381
- oldCount: 0,
382
- newStart: 1,
383
- newCount: newLines.length,
384
- lines: newLines.map((line, i) => ({
385
- type: 'addition',
386
- content: line,
387
- newLineNo: i + 1
388
- }))
389
- }];
390
- }
391
- // Handle empty new content (deleted file)
392
- if (newLines.length === 0 || (newLines.length === 1 && newLines[0] === '')) {
393
- return [{
394
- oldStart: 1,
395
- oldCount: oldLines.length,
396
- newStart: 0,
397
- newCount: 0,
398
- lines: oldLines.map((line, i) => ({
399
- type: 'deletion',
400
- content: line,
401
- oldLineNo: i + 1
402
- }))
403
- }];
404
- }
405
- // Use Myers diff algorithm (simplified LCS approach)
406
- const lcs = computeLCS(oldLines, newLines);
407
- const diff = generateDiffFromLCS(oldLines, newLines, lcs);
408
- // Group into hunks with context
409
- return groupIntoHunks(diff, oldLines.length, newLines.length, contextLines);
410
- }
411
- /**
412
- * Compute Longest Common Subsequence
413
- */
414
- function computeLCS(oldLines, newLines) {
415
- const m = oldLines.length;
416
- const n = newLines.length;
417
- const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
418
- for (let i = 1; i <= m; i++) {
419
- for (let j = 1; j <= n; j++) {
420
- if (oldLines[i - 1] === newLines[j - 1]) {
421
- dp[i][j] = dp[i - 1][j - 1] + 1;
422
- }
423
- else {
424
- dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
425
- }
426
- }
427
- }
428
- return dp;
429
- }
430
- /**
431
- * Generate diff operations from LCS
432
- */
433
- function generateDiffFromLCS(oldLines, newLines, dp) {
434
- let i = oldLines.length;
435
- let j = newLines.length;
436
- const result = [];
437
- while (i > 0 || j > 0) {
438
- if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
439
- result.unshift({
440
- type: 'context',
441
- oldLineNo: i,
442
- newLineNo: j,
443
- content: oldLines[i - 1]
444
- });
445
- i--;
446
- j--;
447
- }
448
- else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
449
- result.unshift({
450
- type: 'addition',
451
- newLineNo: j,
452
- content: newLines[j - 1]
453
- });
454
- j--;
455
- }
456
- else if (i > 0) {
457
- result.unshift({
458
- type: 'deletion',
459
- oldLineNo: i,
460
- content: oldLines[i - 1]
461
- });
462
- i--;
463
- }
464
- }
465
- return result;
466
- }
467
- /**
468
- * Group diff operations into hunks with context
469
- */
470
- function groupIntoHunks(diff, _oldLength, _newLength, contextLines) {
471
- if (diff.length === 0)
472
- return [];
473
- // Find change positions
474
- const changePositions = [];
475
- diff.forEach((op, i) => {
476
- if (op.type !== 'context') {
477
- changePositions.push(i);
478
- }
479
- });
480
- if (changePositions.length === 0)
481
- return [];
482
- // Group changes into hunks (merge if within 2*context of each other)
483
- const hunks = [];
484
- let hunkStart = Math.max(0, changePositions[0] - contextLines);
485
- let hunkEnd = Math.min(diff.length - 1, changePositions[0] + contextLines);
486
- for (let i = 1; i < changePositions.length; i++) {
487
- const nextStart = Math.max(0, changePositions[i] - contextLines);
488
- const nextEnd = Math.min(diff.length - 1, changePositions[i] + contextLines);
489
- if (nextStart <= hunkEnd + 1) {
490
- // Merge hunks
491
- hunkEnd = nextEnd;
492
- }
493
- else {
494
- // Create current hunk and start new one
495
- hunks.push(createHunk(diff, hunkStart, hunkEnd));
496
- hunkStart = nextStart;
497
- hunkEnd = nextEnd;
498
- }
499
- }
500
- // Add final hunk
501
- hunks.push(createHunk(diff, hunkStart, hunkEnd));
502
- return hunks;
503
- }
504
- /**
505
- * Create a hunk from diff operations
506
- */
507
- function createHunk(diff, start, end) {
508
- const lines = [];
509
- let oldStart = 0;
510
- let oldCount = 0;
511
- let newStart = 0;
512
- let newCount = 0;
513
- let foundFirst = false;
514
- for (let i = start; i <= end; i++) {
515
- const op = diff[i];
516
- if (!op)
517
- continue;
518
- if (!foundFirst) {
519
- oldStart = op.oldLineNo || 1;
520
- newStart = op.newLineNo || 1;
521
- foundFirst = true;
522
- }
523
- lines.push({
524
- type: op.type,
525
- content: op.content,
526
- oldLineNo: op.oldLineNo,
527
- newLineNo: op.newLineNo
528
- });
529
- if (op.type === 'context') {
530
- oldCount++;
531
- newCount++;
532
- }
533
- else if (op.type === 'deletion') {
534
- oldCount++;
535
- }
536
- else if (op.type === 'addition') {
537
- newCount++;
538
- }
539
- }
540
- return {
541
- oldStart,
542
- oldCount,
543
- newStart,
544
- newCount,
545
- lines
546
- };
547
- }
548
- /**
549
- * Compute word-level diff within a line.
550
- *
551
- * @description Computes fine-grained differences at the word/token level
552
- * within a single line. Useful for inline highlighting of small changes.
553
- *
554
- * @param oldLine - Original line content
555
- * @param newLine - New line content
556
- * @returns Array of WordChange objects showing what changed
557
- *
558
- * @example
559
- * const changes = computeWordDiff('const foo = 1', 'const bar = 1')
560
- * // Returns: [unchanged: 'const ', removed: 'foo', added: 'bar', unchanged: ' = 1']
561
- */
562
- export function computeWordDiff(oldLine, newLine) {
563
- const changes = [];
564
- // Tokenize by word boundaries (keeping punctuation separate)
565
- const oldTokens = tokenize(oldLine);
566
- const newTokens = tokenize(newLine);
567
- // Use LCS for word-level diff
568
- const lcs = computeWordLCS(oldTokens, newTokens);
569
- let oldIdx = 0;
570
- let newIdx = 0;
571
- let lcsIdx = 0;
572
- while (oldIdx < oldTokens.length || newIdx < newTokens.length) {
573
- if (lcsIdx < lcs.length &&
574
- oldIdx < oldTokens.length &&
575
- newIdx < newTokens.length &&
576
- oldTokens[oldIdx] === lcs[lcsIdx] &&
577
- newTokens[newIdx] === lcs[lcsIdx]) {
578
- // Common token
579
- changes.push({ type: 'unchanged', text: oldTokens[oldIdx] });
580
- oldIdx++;
581
- newIdx++;
582
- lcsIdx++;
583
- }
584
- else {
585
- // Deleted from old
586
- while (oldIdx < oldTokens.length &&
587
- (lcsIdx >= lcs.length || oldTokens[oldIdx] !== lcs[lcsIdx])) {
588
- changes.push({ type: 'removed', text: oldTokens[oldIdx] });
589
- oldIdx++;
590
- }
591
- // Added in new
592
- while (newIdx < newTokens.length &&
593
- (lcsIdx >= lcs.length || newTokens[newIdx] !== lcs[lcsIdx])) {
594
- changes.push({ type: 'added', text: newTokens[newIdx] });
595
- newIdx++;
596
- }
597
- }
598
- }
599
- return changes;
600
- }
601
- /**
602
- * Tokenize a line into words and punctuation
603
- */
604
- function tokenize(line) {
605
- const tokens = [];
606
- let current = '';
607
- for (const char of line) {
608
- if (/\s/.test(char)) {
609
- if (current) {
610
- tokens.push(current);
611
- current = '';
612
- }
613
- tokens.push(char);
614
- }
615
- else if (/[a-zA-Z0-9_]/.test(char)) {
616
- current += char;
617
- }
618
- else {
619
- if (current) {
620
- tokens.push(current);
621
- current = '';
622
- }
623
- tokens.push(char);
624
- }
625
- }
626
- if (current) {
627
- tokens.push(current);
628
- }
629
- return tokens;
630
- }
631
- /**
632
- * Compute LCS for word tokens
633
- */
634
- function computeWordLCS(oldTokens, newTokens) {
635
- const m = oldTokens.length;
636
- const n = newTokens.length;
637
- const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
638
- for (let i = 1; i <= m; i++) {
639
- for (let j = 1; j <= n; j++) {
640
- if (oldTokens[i - 1] === newTokens[j - 1]) {
641
- dp[i][j] = dp[i - 1][j - 1] + 1;
642
- }
643
- else {
644
- dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
645
- }
646
- }
647
- }
648
- // Backtrack to get LCS
649
- const lcs = [];
650
- let i = m;
651
- let j = n;
652
- while (i > 0 && j > 0) {
653
- if (oldTokens[i - 1] === newTokens[j - 1]) {
654
- lcs.unshift(oldTokens[i - 1]);
655
- i--;
656
- j--;
657
- }
658
- else if (dp[i - 1][j] > dp[i][j - 1]) {
659
- i--;
660
- }
661
- else {
662
- j--;
663
- }
664
- }
665
- return lcs;
666
- }
667
- // ============================================================================
668
- // Syntax Highlighting
669
- // ============================================================================
670
- let highlighterInstance = null;
671
- async function getHighlighter() {
672
- if (!highlighterInstance) {
673
- highlighterInstance = await createHighlighter({
674
- themes: ['github-dark'],
675
- langs: ['typescript', 'javascript', 'tsx', 'jsx', 'json', 'css', 'html',
676
- 'markdown', 'python', 'rust', 'go', 'java', 'c', 'cpp', 'ruby',
677
- 'php', 'bash', 'sql', 'yaml', 'plaintext']
678
- });
679
- }
680
- return highlighterInstance;
681
- }
682
- /**
683
- * Apply Shiki syntax highlighting to diff output.
684
- *
685
- * @description Processes a DiffResult and applies syntax highlighting
686
- * using Shiki. Each file is highlighted according to its detected language.
687
- * Returns ANSI-colored output suitable for terminal display.
688
- *
689
- * @param diff - The diff result to highlight
690
- * @param options - Optional highlighting options
691
- * @param options.theme - Shiki theme name (default: 'github-dark')
692
- * @returns Promise<HighlightedDiff> with highlighted lines and language map
693
- *
694
- * @example
695
- * const diff = await getUnstagedDiff('/repo')
696
- * const highlighted = await highlightDiff(diff)
697
- * highlighted.lines.forEach(line => console.log(line))
698
- */
699
- export async function highlightDiff(diff, _options) {
700
- const languages = new Map();
701
- const lines = [];
702
- const highlighter = await getHighlighter();
703
- for (const entry of diff.entries) {
704
- const lang = getLanguageFromPath(entry.path);
705
- languages.set(entry.path, lang);
706
- // Add header
707
- const header = formatDiffHeader(entry);
708
- lines.push(...header);
709
- for (const hunk of entry.hunks) {
710
- lines.push(formatHunkHeader(hunk));
711
- for (const line of hunk.lines) {
712
- const prefix = line.type === 'addition' ? '+' : line.type === 'deletion' ? '-' : ' ';
713
- // Highlight the content
714
- let highlighted;
715
- try {
716
- const tokens = highlighter.codeToTokens(line.content, {
717
- lang: lang,
718
- theme: 'github-dark'
719
- });
720
- // Convert tokens to ANSI
721
- highlighted = tokensToAnsi(tokens.tokens[0] || [], line.type);
722
- }
723
- catch {
724
- highlighted = line.content;
725
- }
726
- // Apply line color based on type
727
- if (line.type === 'addition') {
728
- lines.push(`\x1b[32m${prefix}${highlighted}\x1b[0m`);
729
- }
730
- else if (line.type === 'deletion') {
731
- lines.push(`\x1b[31m${prefix}${highlighted}\x1b[0m`);
732
- }
733
- else {
734
- lines.push(`${prefix}${highlighted}`);
735
- }
736
- }
737
- }
738
- }
739
- return { lines, languages };
740
- }
741
- /**
742
- * Convert Shiki tokens to ANSI escape codes
743
- */
744
- function tokensToAnsi(tokens, _lineType) {
745
- return tokens.map(token => {
746
- if (token.color) {
747
- const hex = token.color.replace('#', '');
748
- const r = parseInt(hex.substring(0, 2), 16);
749
- const g = parseInt(hex.substring(2, 4), 16);
750
- const b = parseInt(hex.substring(4, 6), 16);
751
- return `\x1b[38;2;${r};${g};${b}m${token.content}\x1b[0m`;
752
- }
753
- return token.content;
754
- }).join('');
755
- }
756
- /**
757
- * Get language from file extension for Shiki.
758
- *
759
- * @description Maps file extensions to Shiki language identifiers for
760
- * syntax highlighting. Falls back to 'plaintext' for unknown extensions.
761
- *
762
- * @param filePath - File path to detect language for
763
- * @returns Shiki language identifier (e.g., 'typescript', 'python')
764
- *
765
- * @example
766
- * getLanguageFromPath('src/index.ts') // Returns 'typescript'
767
- * getLanguageFromPath('script.py') // Returns 'python'
768
- * getLanguageFromPath('data.xyz') // Returns 'plaintext'
769
- */
770
- export function getLanguageFromPath(filePath) {
771
- const ext = path.extname(filePath).toLowerCase();
772
- return EXTENSION_TO_LANGUAGE[ext] || 'plaintext';
773
- }
774
- /**
775
- * Format diff output with syntax highlighting.
776
- *
777
- * @description Main formatting function that applies syntax highlighting
778
- * to diff output. Respects NO_COLOR environment variable and --no-color
779
- * option for accessibility.
780
- *
781
- * @param diff - The diff result to format
782
- * @param options - Diff options including noColor flag
783
- * @returns Promise<string[]> array of formatted output lines
784
- *
785
- * @example
786
- * const diff = await getUnstagedDiff('/repo')
787
- * const lines = await formatHighlightedDiff(diff)
788
- * lines.forEach(line => console.log(line))
789
- *
790
- * @example
791
- * // Without colors
792
- * const lines = await formatHighlightedDiff(diff, { noColor: true })
793
- */
794
- export async function formatHighlightedDiff(diff, options) {
795
- // Check for NO_COLOR environment variable
796
- const noColor = options?.noColor || process.env.NO_COLOR !== undefined;
797
- if (noColor) {
798
- return formatPlainDiff(diff);
799
- }
800
- const result = await highlightDiff(diff);
801
- return result.lines;
802
- }
803
- // ============================================================================
804
- // Output Formatting
805
- // ============================================================================
806
- /**
807
- * Format diff as plain text (no highlighting).
808
- *
809
- * @description Formats diff output without any ANSI colors or syntax
810
- * highlighting. Suitable for piping to files or non-terminal output.
811
- *
812
- * @param diff - The diff result to format
813
- * @returns Array of plain text output lines
814
- *
815
- * @example
816
- * const diff = await getUnstagedDiff('/repo')
817
- * const lines = formatPlainDiff(diff)
818
- */
819
- export function formatPlainDiff(diff) {
820
- const lines = [];
821
- for (const entry of diff.entries) {
822
- const header = formatDiffHeader(entry);
823
- lines.push(...header);
824
- for (const hunk of entry.hunks) {
825
- lines.push(formatHunkHeader(hunk));
826
- for (const line of hunk.lines) {
827
- const prefix = line.type === 'addition' ? '+' : line.type === 'deletion' ? '-' : ' ';
828
- lines.push(`${prefix}${line.content}`);
829
- }
830
- }
831
- }
832
- return lines;
833
- }
834
- /**
835
- * Format diff header for a file entry.
836
- *
837
- * @description Generates the git-style diff header lines for a file,
838
- * including the diff --git line, mode changes, index line, and +++ / --- lines.
839
- *
840
- * @param entry - The DiffEntry to generate header for
841
- * @returns Array of header lines
842
- *
843
- * @example
844
- * const header = formatDiffHeader(entry)
845
- * // Returns: ['diff --git a/file.ts b/file.ts', '--- a/file.ts', '+++ b/file.ts']
846
- */
847
- export function formatDiffHeader(entry) {
848
- const lines = [];
849
- // diff --git header
850
- const oldPath = entry.oldPath || entry.path;
851
- const newPath = entry.path;
852
- lines.push(`diff --git a/${oldPath} b/${newPath}`);
853
- // Mode changes
854
- if (entry.oldMode && entry.newMode && entry.oldMode !== entry.newMode) {
855
- lines.push(`old mode ${entry.oldMode}`);
856
- lines.push(`new mode ${entry.newMode}`);
857
- }
858
- // Index line
859
- if (entry.oldSha && entry.newSha) {
860
- lines.push(`index ${entry.oldSha.substring(0, 7)}..${entry.newSha.substring(0, 7)}`);
861
- }
862
- // --- and +++ lines
863
- if (entry.status === 'added') {
864
- lines.push('--- /dev/null');
865
- lines.push(`+++ b/${newPath}`);
866
- }
867
- else if (entry.status === 'deleted') {
868
- lines.push(`--- a/${oldPath}`);
869
- lines.push('+++ /dev/null');
870
- }
871
- else if (entry.status === 'renamed') {
872
- lines.push(`rename from ${oldPath}`);
873
- lines.push(`rename to ${newPath}`);
874
- lines.push(`--- a/${oldPath}`);
875
- lines.push(`+++ b/${newPath}`);
876
- }
877
- else {
878
- lines.push(`--- a/${oldPath}`);
879
- lines.push(`+++ b/${newPath}`);
880
- }
881
- return lines;
882
- }
883
- /**
884
- * Format hunk header.
885
- *
886
- * @description Creates the @@ line that starts each diff hunk, showing
887
- * the line ranges in both old and new files.
888
- *
889
- * @param hunk - The DiffHunk to format
890
- * @returns Formatted hunk header string (e.g., '@@ -1,5 +1,7 @@ function foo')
891
- *
892
- * @example
893
- * formatHunkHeader({ oldStart: 1, oldCount: 5, newStart: 1, newCount: 7 })
894
- * // Returns: '@@ -1,5 +1,7 @@'
895
- */
896
- export function formatHunkHeader(hunk) {
897
- const header = hunk.header ? ` ${hunk.header}` : '';
898
- return `@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@${header}`;
899
- }
900
- /**
901
- * Format file mode change indicator.
902
- *
903
- * @description Creates a human-readable string showing a file mode change.
904
- *
905
- * @param oldMode - Original file mode (e.g., '100644')
906
- * @param newMode - New file mode (e.g., '100755')
907
- * @returns Formatted mode change string
908
- *
909
- * @example
910
- * formatModeChange('100644', '100755') // Returns 'mode change 100644 -> 100755'
911
- */
912
- export function formatModeChange(oldMode, newMode) {
913
- return `mode change ${oldMode} -> ${newMode}`;
914
- }
915
- /**
916
- * Format binary file indicator.
917
- *
918
- * @description Creates a message indicating that a file is binary
919
- * and cannot be diffed as text.
920
- *
921
- * @param filePath - Path to the binary file
922
- * @returns Formatted binary indicator string
923
- *
924
- * @example
925
- * formatBinaryIndicator('image.png') // Returns 'Binary files differ: image.png'
926
- */
927
- export function formatBinaryIndicator(filePath) {
928
- return `Binary files differ: ${filePath}`;
929
- }
930
- // ============================================================================
931
- // Helper Functions
932
- // ============================================================================
933
- /**
934
- * Walk a directory recursively
935
- */
936
- async function walkDirectory(dir, baseDir) {
937
- const files = [];
938
- try {
939
- const entries = await fs.readdir(dir, { withFileTypes: true });
940
- for (const entry of entries) {
941
- const fullPath = path.join(dir, entry.name);
942
- const relativePath = path.relative(baseDir, fullPath);
943
- if (entry.isDirectory()) {
944
- if (entry.name !== '.git' && entry.name !== 'node_modules') {
945
- const subFiles = await walkDirectory(fullPath, baseDir);
946
- files.push(...subFiles);
947
- }
948
- }
949
- else if (entry.isFile()) {
950
- files.push(relativePath);
951
- }
952
- }
953
- }
954
- catch {
955
- // Ignore errors
956
- }
957
- return files;
958
- }
959
- //# sourceMappingURL=diff.js.map