lula2 0.6.6-nightly.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +1 -0
  2. package/dist/_app/immutable/assets/0.KSamNhnP.css +1 -0
  3. package/dist/_app/immutable/chunks/24UDoAn4.js +65 -0
  4. package/dist/_app/immutable/chunks/B0ygo1VA.js +2 -0
  5. package/dist/_app/immutable/chunks/B2nEDjq4.js +1 -0
  6. package/dist/_app/immutable/chunks/BNHHvtTX.js +1 -0
  7. package/dist/_app/immutable/chunks/CpBmCwmc.js +1 -0
  8. package/dist/_app/immutable/chunks/CttDkklr.js +2 -0
  9. package/dist/_app/immutable/chunks/P3psI8RV.js +1 -0
  10. package/dist/_app/immutable/chunks/iLqChAUt.js +1 -0
  11. package/dist/_app/immutable/chunks/iYE0hyoB.js +1 -0
  12. package/dist/_app/immutable/chunks/l0xMBaDV.js +1 -0
  13. package/dist/_app/immutable/entry/app.DK674slU.js +2 -0
  14. package/dist/_app/immutable/entry/start.BzhV34ug.js +1 -0
  15. package/dist/_app/immutable/nodes/0.CX00wLgP.js +2 -0
  16. package/dist/_app/immutable/nodes/1.eWlwSy7C.js +1 -0
  17. package/dist/_app/immutable/nodes/2.C8hXOpRf.js +1 -0
  18. package/dist/_app/immutable/nodes/3.DeLiyve3.js +1 -0
  19. package/dist/_app/immutable/nodes/{4.CjJtAzwd.js → 4.CSVxIlBM.js} +1 -1
  20. package/dist/_app/version.json +1 -1
  21. package/dist/cli/commands/ui.js +281 -14
  22. package/dist/cli/server/index.js +281 -14
  23. package/dist/cli/server/server.js +281 -14
  24. package/dist/cli/server/serverState.js +247 -3
  25. package/dist/cli/server/spreadsheetRoutes.js +821 -2
  26. package/dist/cli/server/websocketServer.js +281 -14
  27. package/dist/index.html +11 -11
  28. package/dist/index.js +281 -14
  29. package/package.json +21 -20
  30. package/src/lib/components/git-status/GitStatusDropdown.svelte +261 -0
  31. package/src/lib/components/git-status/index.ts +4 -0
  32. package/src/lib/types.ts +19 -0
  33. package/src/routes/+layout.svelte +4 -0
  34. package/dist/_app/immutable/assets/0.DT0yw00X.css +0 -1
  35. package/dist/_app/immutable/chunks/BAMA-SMn.js +0 -1
  36. package/dist/_app/immutable/chunks/BIpNkEdo.js +0 -65
  37. package/dist/_app/immutable/chunks/BOeu7SQt.js +0 -2
  38. package/dist/_app/immutable/chunks/Bvx51L6t.js +0 -1
  39. package/dist/_app/immutable/chunks/DQTRhwGS.js +0 -1
  40. package/dist/_app/immutable/chunks/DkIUt-Ae.js +0 -1
  41. package/dist/_app/immutable/chunks/DoNUPQ2F.js +0 -1
  42. package/dist/_app/immutable/chunks/DsnmJJEf.js +0 -1
  43. package/dist/_app/immutable/chunks/kqS9jm6m.js +0 -2
  44. package/dist/_app/immutable/chunks/oPg1Ic49.js +0 -1
  45. package/dist/_app/immutable/entry/app.Dqwn7sww.js +0 -2
  46. package/dist/_app/immutable/entry/start.B-O5NM7y.js +0 -1
  47. package/dist/_app/immutable/nodes/0.CZ3i370e.js +0 -2
  48. package/dist/_app/immutable/nodes/1.C9kqHM-h.js +0 -1
  49. package/dist/_app/immutable/nodes/2.BDP2l8IW.js +0 -1
  50. package/dist/_app/immutable/nodes/3.HVBwzXF9.js +0 -1
@@ -2437,13 +2437,10 @@ var init_yamlDiff = __esm({
2437
2437
  });
2438
2438
 
2439
2439
  // cli/server/infrastructure/gitHistory.ts
2440
- var gitHistory_exports = {};
2441
- __export(gitHistory_exports, {
2442
- GitHistoryUtil: () => GitHistoryUtil
2443
- });
2444
2440
  import * as fs2 from "fs";
2445
2441
  import * as git from "isomorphic-git";
2446
2442
  import { relative } from "path";
2443
+ import { execSync } from "child_process";
2447
2444
  var GitHistoryUtil;
2448
2445
  var init_gitHistory = __esm({
2449
2446
  "cli/server/infrastructure/gitHistory.ts"() {
@@ -2451,8 +2448,30 @@ var init_gitHistory = __esm({
2451
2448
  init_yamlDiff();
2452
2449
  GitHistoryUtil = class {
2453
2450
  baseDir;
2454
- constructor(baseDir) {
2451
+ execSync;
2452
+ constructor(baseDir, execSyncFn) {
2455
2453
  this.baseDir = baseDir;
2454
+ this.execSync = execSyncFn || execSync;
2455
+ }
2456
+ /**
2457
+ * Execute a git command using native git binary with credentials support
2458
+ */
2459
+ async executeGitCommand(command, cwd) {
2460
+ try {
2461
+ const workingDir = cwd || await git.findRoot({ fs: fs2, filepath: process.cwd() });
2462
+ const output = this.execSync(command, {
2463
+ cwd: workingDir,
2464
+ encoding: "utf8",
2465
+ stdio: ["pipe", "pipe", "pipe"]
2466
+ });
2467
+ return { success: true, output: output.toString() };
2468
+ } catch (error) {
2469
+ return {
2470
+ success: false,
2471
+ output: "",
2472
+ error: error.stderr?.toString() || error.message
2473
+ };
2474
+ }
2456
2475
  }
2457
2476
  /**
2458
2477
  * Check if the directory is a git repository
@@ -2636,7 +2655,7 @@ var init_gitHistory = __esm({
2636
2655
  }
2637
2656
  const currentLines = currentContent ? currentContent.split("\n") : [];
2638
2657
  const parentLines = parentContent ? parentContent.split("\n") : [];
2639
- const diff = this.createSimpleDiff(parentLines, currentLines, relativePath);
2658
+ const diff = await this.createSimpleDiff(parentLines, currentLines, relativePath);
2640
2659
  const { insertions, deletions } = this.countChanges(parentLines, currentLines);
2641
2660
  const isMappingFile = relativePath.includes("-mappings.yaml");
2642
2661
  const yamlDiff = createYamlDiff(parentContent || "", currentContent || "", isMappingFile);
@@ -2668,7 +2687,7 @@ var init_gitHistory = __esm({
2668
2687
  /**
2669
2688
  * Create a simple unified diff between two file versions
2670
2689
  */
2671
- createSimpleDiff(oldLines, newLines, filepath) {
2690
+ async createSimpleDiff(oldLines, newLines, filepath) {
2672
2691
  const diffLines = [];
2673
2692
  diffLines.push(`--- a/${filepath}`);
2674
2693
  diffLines.push(`+++ b/${filepath}`);
@@ -2750,6 +2769,227 @@ var init_gitHistory = __esm({
2750
2769
  };
2751
2770
  }
2752
2771
  }
2772
+ /**
2773
+ * Get current branch name
2774
+ */
2775
+ async getCurrentBranch() {
2776
+ try {
2777
+ const isGitRepo = await this.isGitRepository();
2778
+ if (!isGitRepo) {
2779
+ return null;
2780
+ }
2781
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
2782
+ const branch = await git.currentBranch({ fs: fs2, dir: gitRoot });
2783
+ return branch || null;
2784
+ } catch (error) {
2785
+ console.error("Error getting current branch:", error);
2786
+ return null;
2787
+ }
2788
+ }
2789
+ /**
2790
+ * Get git status information
2791
+ */
2792
+ async getGitStatus() {
2793
+ try {
2794
+ const isGitRepo = await this.isGitRepository();
2795
+ if (!isGitRepo) {
2796
+ return {
2797
+ isGitRepository: false,
2798
+ currentBranch: null,
2799
+ branchInfo: null,
2800
+ canPull: false,
2801
+ canPush: false
2802
+ };
2803
+ }
2804
+ const currentBranch2 = await this.getCurrentBranch();
2805
+ if (!currentBranch2) {
2806
+ return {
2807
+ isGitRepository: true,
2808
+ currentBranch: null,
2809
+ branchInfo: null,
2810
+ canPull: false,
2811
+ canPush: false
2812
+ };
2813
+ }
2814
+ const branchInfo = await this.getBranchInfo(currentBranch2);
2815
+ return {
2816
+ isGitRepository: true,
2817
+ currentBranch: currentBranch2,
2818
+ branchInfo,
2819
+ canPull: branchInfo?.isBehind || false,
2820
+ canPush: branchInfo?.isAhead || false
2821
+ };
2822
+ } catch (error) {
2823
+ console.error("Error getting git status:", error);
2824
+ return {
2825
+ isGitRepository: false,
2826
+ currentBranch: null,
2827
+ branchInfo: null,
2828
+ canPull: false,
2829
+ canPush: false
2830
+ };
2831
+ }
2832
+ }
2833
+ /**
2834
+ * Get branch comparison information
2835
+ */
2836
+ async getBranchInfo(branchName) {
2837
+ try {
2838
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
2839
+ const localCommits = await git.log({ fs: fs2, dir: gitRoot, ref: branchName });
2840
+ try {
2841
+ const remotes = await git.listRemotes({ fs: fs2, dir: gitRoot });
2842
+ for (const remote of remotes) {
2843
+ try {
2844
+ const fetchResult = await this.executeGitCommand(`git fetch ${remote.remote}`, gitRoot);
2845
+ if (!fetchResult.success) {
2846
+ console.warn(`Could not fetch from remote ${remote.remote}:`, fetchResult.error);
2847
+ }
2848
+ } catch (fetchError) {
2849
+ console.warn(`Could not fetch from remote ${remote.remote}:`, fetchError);
2850
+ }
2851
+ }
2852
+ let remoteCommits = [];
2853
+ let foundRemote = false;
2854
+ for (const remote of remotes) {
2855
+ const remoteBranchRef = `${remote.remote}/${branchName}`;
2856
+ try {
2857
+ remoteCommits = await git.log({ fs: fs2, dir: gitRoot, ref: remoteBranchRef });
2858
+ foundRemote = true;
2859
+ break;
2860
+ } catch (error) {
2861
+ console.warn(`Could not get commits for ${remoteBranchRef}: ${error}`);
2862
+ }
2863
+ }
2864
+ if (!foundRemote || remoteCommits.length === 0) {
2865
+ const lastCommit2 = localCommits[0];
2866
+ return {
2867
+ currentBranch: branchName,
2868
+ isAhead: false,
2869
+ isBehind: false,
2870
+ aheadCount: 0,
2871
+ behindCount: 0,
2872
+ lastCommitDate: lastCommit2 ? new Date(lastCommit2.commit.author.timestamp * 1e3).toISOString() : null,
2873
+ lastCommitMessage: lastCommit2?.commit.message || null,
2874
+ hasUnpushedChanges: false
2875
+ };
2876
+ }
2877
+ const localHashes = new Set(localCommits.map((c) => c.oid));
2878
+ const remoteHashes = new Set(remoteCommits.map((c) => c.oid));
2879
+ const aheadCount = localCommits.filter((c) => !remoteHashes.has(c.oid)).length;
2880
+ const behindCount = remoteCommits.filter((c) => !localHashes.has(c.oid)).length;
2881
+ const lastCommit = localCommits[0];
2882
+ return {
2883
+ currentBranch: branchName,
2884
+ isAhead: aheadCount > 0,
2885
+ isBehind: behindCount > 0,
2886
+ aheadCount,
2887
+ behindCount,
2888
+ lastCommitDate: lastCommit ? new Date(lastCommit.commit.author.timestamp * 1e3).toISOString() : null,
2889
+ lastCommitMessage: lastCommit?.commit.message || null,
2890
+ hasUnpushedChanges: aheadCount > 0
2891
+ };
2892
+ } catch {
2893
+ const lastCommit = localCommits[0];
2894
+ return {
2895
+ currentBranch: branchName,
2896
+ isAhead: false,
2897
+ isBehind: false,
2898
+ aheadCount: 0,
2899
+ behindCount: 0,
2900
+ lastCommitDate: lastCommit ? new Date(lastCommit.commit.author.timestamp * 1e3).toISOString() : null,
2901
+ lastCommitMessage: lastCommit?.commit.message || null,
2902
+ hasUnpushedChanges: false
2903
+ };
2904
+ }
2905
+ } catch (error) {
2906
+ console.error("Error getting branch info:", error);
2907
+ return null;
2908
+ }
2909
+ }
2910
+ /**
2911
+ * Fetch updates from remote repositories using native git command
2912
+ */
2913
+ async fetchFromRemotes() {
2914
+ try {
2915
+ const isGitRepo = await this.isGitRepository();
2916
+ if (!isGitRepo) {
2917
+ return { success: false, message: "Not a git repository", details: [] };
2918
+ }
2919
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
2920
+ const remotes = await git.listRemotes({ fs: fs2, dir: gitRoot });
2921
+ if (remotes.length === 0) {
2922
+ return { success: true, message: "No remotes configured", details: [] };
2923
+ }
2924
+ const details = [];
2925
+ let hasErrors = false;
2926
+ for (const remote of remotes) {
2927
+ try {
2928
+ const fetchResult = await this.executeGitCommand(`git fetch ${remote.remote}`, gitRoot);
2929
+ if (fetchResult.success) {
2930
+ details.push(`Fetched from ${remote.remote}`);
2931
+ } else {
2932
+ details.push(`Failed to fetch from ${remote.remote}: ${fetchResult.error}`);
2933
+ hasErrors = true;
2934
+ }
2935
+ } catch (error) {
2936
+ details.push(`Error fetching from ${remote.remote}: ${error}`);
2937
+ hasErrors = true;
2938
+ }
2939
+ }
2940
+ return {
2941
+ success: !hasErrors,
2942
+ message: hasErrors ? "Fetch completed with some errors" : "Successfully fetched from all remotes",
2943
+ details
2944
+ };
2945
+ } catch (error) {
2946
+ console.error("Error fetching from remotes:", error);
2947
+ return {
2948
+ success: false,
2949
+ message: error instanceof Error ? error.message : "Unknown error occurred",
2950
+ details: []
2951
+ };
2952
+ }
2953
+ }
2954
+ /**
2955
+ * Pull changes from remote using native git command
2956
+ */
2957
+ async pullChanges() {
2958
+ try {
2959
+ const isGitRepo = await this.isGitRepository();
2960
+ if (!isGitRepo) {
2961
+ return { success: false, message: "Not a git repository" };
2962
+ }
2963
+ const currentBranch2 = await this.getCurrentBranch();
2964
+ if (!currentBranch2) {
2965
+ return { success: false, message: "No current branch found" };
2966
+ }
2967
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
2968
+ const remotes = await git.listRemotes({ fs: fs2, dir: gitRoot });
2969
+ if (remotes.length === 0) {
2970
+ return { success: false, message: "No remotes configured" };
2971
+ }
2972
+ const targetRemote = remotes[0].remote;
2973
+ const pullCommand = `git pull ${targetRemote} ${currentBranch2}`;
2974
+ const pullResult = await this.executeGitCommand(pullCommand, gitRoot);
2975
+ if (!pullResult.success) {
2976
+ return {
2977
+ success: false,
2978
+ message: `Failed to pull changes: ${pullResult.error}`
2979
+ };
2980
+ }
2981
+ return {
2982
+ success: true,
2983
+ message: pullResult.output || "Successfully pulled changes"
2984
+ };
2985
+ } catch (error) {
2986
+ console.error("Error pulling changes:", error);
2987
+ return {
2988
+ success: false,
2989
+ message: error instanceof Error ? error.message : "Unknown error occurred"
2990
+ };
2991
+ }
2992
+ }
2753
2993
  };
2754
2994
  }
2755
2995
  });
@@ -3600,6 +3840,7 @@ var init_spreadsheetRoutes = __esm({
3600
3840
  "use strict";
3601
3841
  init_debug();
3602
3842
  init_serverState();
3843
+ init_gitHistory();
3603
3844
  MAX_HEADER_CANDIDATES = 5;
3604
3845
  PREVIEW_COLUMNS = 4;
3605
3846
  router = express.Router();
@@ -3919,6 +4160,32 @@ var init_spreadsheetRoutes = __esm({
3919
4160
  res.status(500).json({ error: "Failed to get sheet previews" });
3920
4161
  }
3921
4162
  });
4163
+ router.get("/git-status", async (req, res) => {
4164
+ try {
4165
+ const state = getServerState();
4166
+ const gitUtil = new GitHistoryUtil(state.CONTROL_SET_DIR);
4167
+ const gitStatus = await gitUtil.getGitStatus();
4168
+ res.json(gitStatus);
4169
+ } catch (error) {
4170
+ console.error("Error getting git status:", error);
4171
+ res.status(500).json({ error: "Failed to get git status" });
4172
+ }
4173
+ });
4174
+ router.post("/git-pull", async (req, res) => {
4175
+ try {
4176
+ const state = getServerState();
4177
+ const gitUtil = new GitHistoryUtil(state.CONTROL_SET_DIR);
4178
+ const result = await gitUtil.pullChanges();
4179
+ if (result.success) {
4180
+ res.json(result);
4181
+ } else {
4182
+ res.status(400).json(result);
4183
+ }
4184
+ } catch (error) {
4185
+ console.error("Error pulling changes:", error);
4186
+ res.status(500).json({ error: "Failed to pull changes" });
4187
+ }
4188
+ });
3922
4189
  spreadsheetRoutes_default = router;
3923
4190
  }
3924
4191
  });
@@ -4795,6 +5062,7 @@ import { readFileSync as readFileSync4 } from "fs";
4795
5062
  init_debug();
4796
5063
  init_controlHelpers();
4797
5064
  init_serverState();
5065
+ init_gitHistory();
4798
5066
  import * as yaml5 from "js-yaml";
4799
5067
  import { join as join5 } from "path";
4800
5068
  import { WebSocket, WebSocketServer } from "ws";
@@ -4958,8 +5226,7 @@ var WebSocketManager = class {
4958
5226
  if (!control.id) {
4959
5227
  control.id = controlId;
4960
5228
  }
4961
- const { GitHistoryUtil: GitHistoryUtil2 } = await Promise.resolve().then(() => (init_gitHistory(), gitHistory_exports));
4962
- const { execSync } = await import("child_process");
5229
+ const { execSync: execSync2 } = await import("child_process");
4963
5230
  let timeline = null;
4964
5231
  try {
4965
5232
  const currentPath2 = getCurrentControlSetPath();
@@ -4999,7 +5266,7 @@ var WebSocketManager = class {
4999
5266
  console.error(`Could not find file for control ${control.id}`);
5000
5267
  timeline = null;
5001
5268
  } else {
5002
- const gitUtil = new GitHistoryUtil2(currentPath2);
5269
+ const gitUtil = new GitHistoryUtil(currentPath2);
5003
5270
  const controlHistory = await gitUtil.getFileHistory(controlPath);
5004
5271
  debug(`Git history for ${control.id}:`, {
5005
5272
  path: controlPath,
@@ -5020,12 +5287,12 @@ var WebSocketManager = class {
5020
5287
  let hasPendingChanges = false;
5021
5288
  try {
5022
5289
  try {
5023
- execSync(`git ls-files --error-unmatch "${controlPath}"`, {
5290
+ execSync2(`git ls-files --error-unmatch "${controlPath}"`, {
5024
5291
  encoding: "utf8",
5025
5292
  cwd: process.cwd(),
5026
5293
  stdio: "pipe"
5027
5294
  });
5028
- const gitStatus = execSync(`git status --porcelain "${controlPath}"`, {
5295
+ const gitStatus = execSync2(`git status --porcelain "${controlPath}"`, {
5029
5296
  encoding: "utf8",
5030
5297
  cwd: process.cwd()
5031
5298
  }).trim();
@@ -5044,12 +5311,12 @@ var WebSocketManager = class {
5044
5311
  if (existsSync5(mappingPath)) {
5045
5312
  try {
5046
5313
  try {
5047
- execSync(`git ls-files --error-unmatch "${mappingPath}"`, {
5314
+ execSync2(`git ls-files --error-unmatch "${mappingPath}"`, {
5048
5315
  encoding: "utf8",
5049
5316
  cwd: process.cwd(),
5050
5317
  stdio: "pipe"
5051
5318
  });
5052
- const gitStatus = execSync(`git status --porcelain "${mappingPath}"`, {
5319
+ const gitStatus = execSync2(`git status --porcelain "${mappingPath}"`, {
5053
5320
  encoding: "utf8",
5054
5321
  cwd: process.cwd()
5055
5322
  }).trim();
@@ -608,6 +608,7 @@ var FileStore = class {
608
608
  import * as fs2 from "fs";
609
609
  import * as git from "isomorphic-git";
610
610
  import { relative } from "path";
611
+ import { execSync } from "child_process";
611
612
 
612
613
  // cli/server/infrastructure/yamlDiff.ts
613
614
  import * as yaml3 from "js-yaml";
@@ -858,8 +859,30 @@ function generateSummary(changes) {
858
859
  // cli/server/infrastructure/gitHistory.ts
859
860
  var GitHistoryUtil = class {
860
861
  baseDir;
861
- constructor(baseDir) {
862
+ execSync;
863
+ constructor(baseDir, execSyncFn) {
862
864
  this.baseDir = baseDir;
865
+ this.execSync = execSyncFn || execSync;
866
+ }
867
+ /**
868
+ * Execute a git command using native git binary with credentials support
869
+ */
870
+ async executeGitCommand(command, cwd) {
871
+ try {
872
+ const workingDir = cwd || await git.findRoot({ fs: fs2, filepath: process.cwd() });
873
+ const output = this.execSync(command, {
874
+ cwd: workingDir,
875
+ encoding: "utf8",
876
+ stdio: ["pipe", "pipe", "pipe"]
877
+ });
878
+ return { success: true, output: output.toString() };
879
+ } catch (error) {
880
+ return {
881
+ success: false,
882
+ output: "",
883
+ error: error.stderr?.toString() || error.message
884
+ };
885
+ }
863
886
  }
864
887
  /**
865
888
  * Check if the directory is a git repository
@@ -1043,7 +1066,7 @@ var GitHistoryUtil = class {
1043
1066
  }
1044
1067
  const currentLines = currentContent ? currentContent.split("\n") : [];
1045
1068
  const parentLines = parentContent ? parentContent.split("\n") : [];
1046
- const diff = this.createSimpleDiff(parentLines, currentLines, relativePath);
1069
+ const diff = await this.createSimpleDiff(parentLines, currentLines, relativePath);
1047
1070
  const { insertions, deletions } = this.countChanges(parentLines, currentLines);
1048
1071
  const isMappingFile = relativePath.includes("-mappings.yaml");
1049
1072
  const yamlDiff = createYamlDiff(parentContent || "", currentContent || "", isMappingFile);
@@ -1075,7 +1098,7 @@ var GitHistoryUtil = class {
1075
1098
  /**
1076
1099
  * Create a simple unified diff between two file versions
1077
1100
  */
1078
- createSimpleDiff(oldLines, newLines, filepath) {
1101
+ async createSimpleDiff(oldLines, newLines, filepath) {
1079
1102
  const diffLines = [];
1080
1103
  diffLines.push(`--- a/${filepath}`);
1081
1104
  diffLines.push(`+++ b/${filepath}`);
@@ -1157,6 +1180,227 @@ var GitHistoryUtil = class {
1157
1180
  };
1158
1181
  }
1159
1182
  }
1183
+ /**
1184
+ * Get current branch name
1185
+ */
1186
+ async getCurrentBranch() {
1187
+ try {
1188
+ const isGitRepo = await this.isGitRepository();
1189
+ if (!isGitRepo) {
1190
+ return null;
1191
+ }
1192
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
1193
+ const branch = await git.currentBranch({ fs: fs2, dir: gitRoot });
1194
+ return branch || null;
1195
+ } catch (error) {
1196
+ console.error("Error getting current branch:", error);
1197
+ return null;
1198
+ }
1199
+ }
1200
+ /**
1201
+ * Get git status information
1202
+ */
1203
+ async getGitStatus() {
1204
+ try {
1205
+ const isGitRepo = await this.isGitRepository();
1206
+ if (!isGitRepo) {
1207
+ return {
1208
+ isGitRepository: false,
1209
+ currentBranch: null,
1210
+ branchInfo: null,
1211
+ canPull: false,
1212
+ canPush: false
1213
+ };
1214
+ }
1215
+ const currentBranch2 = await this.getCurrentBranch();
1216
+ if (!currentBranch2) {
1217
+ return {
1218
+ isGitRepository: true,
1219
+ currentBranch: null,
1220
+ branchInfo: null,
1221
+ canPull: false,
1222
+ canPush: false
1223
+ };
1224
+ }
1225
+ const branchInfo = await this.getBranchInfo(currentBranch2);
1226
+ return {
1227
+ isGitRepository: true,
1228
+ currentBranch: currentBranch2,
1229
+ branchInfo,
1230
+ canPull: branchInfo?.isBehind || false,
1231
+ canPush: branchInfo?.isAhead || false
1232
+ };
1233
+ } catch (error) {
1234
+ console.error("Error getting git status:", error);
1235
+ return {
1236
+ isGitRepository: false,
1237
+ currentBranch: null,
1238
+ branchInfo: null,
1239
+ canPull: false,
1240
+ canPush: false
1241
+ };
1242
+ }
1243
+ }
1244
+ /**
1245
+ * Get branch comparison information
1246
+ */
1247
+ async getBranchInfo(branchName) {
1248
+ try {
1249
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
1250
+ const localCommits = await git.log({ fs: fs2, dir: gitRoot, ref: branchName });
1251
+ try {
1252
+ const remotes = await git.listRemotes({ fs: fs2, dir: gitRoot });
1253
+ for (const remote of remotes) {
1254
+ try {
1255
+ const fetchResult = await this.executeGitCommand(`git fetch ${remote.remote}`, gitRoot);
1256
+ if (!fetchResult.success) {
1257
+ console.warn(`Could not fetch from remote ${remote.remote}:`, fetchResult.error);
1258
+ }
1259
+ } catch (fetchError) {
1260
+ console.warn(`Could not fetch from remote ${remote.remote}:`, fetchError);
1261
+ }
1262
+ }
1263
+ let remoteCommits = [];
1264
+ let foundRemote = false;
1265
+ for (const remote of remotes) {
1266
+ const remoteBranchRef = `${remote.remote}/${branchName}`;
1267
+ try {
1268
+ remoteCommits = await git.log({ fs: fs2, dir: gitRoot, ref: remoteBranchRef });
1269
+ foundRemote = true;
1270
+ break;
1271
+ } catch (error) {
1272
+ console.warn(`Could not get commits for ${remoteBranchRef}: ${error}`);
1273
+ }
1274
+ }
1275
+ if (!foundRemote || remoteCommits.length === 0) {
1276
+ const lastCommit2 = localCommits[0];
1277
+ return {
1278
+ currentBranch: branchName,
1279
+ isAhead: false,
1280
+ isBehind: false,
1281
+ aheadCount: 0,
1282
+ behindCount: 0,
1283
+ lastCommitDate: lastCommit2 ? new Date(lastCommit2.commit.author.timestamp * 1e3).toISOString() : null,
1284
+ lastCommitMessage: lastCommit2?.commit.message || null,
1285
+ hasUnpushedChanges: false
1286
+ };
1287
+ }
1288
+ const localHashes = new Set(localCommits.map((c) => c.oid));
1289
+ const remoteHashes = new Set(remoteCommits.map((c) => c.oid));
1290
+ const aheadCount = localCommits.filter((c) => !remoteHashes.has(c.oid)).length;
1291
+ const behindCount = remoteCommits.filter((c) => !localHashes.has(c.oid)).length;
1292
+ const lastCommit = localCommits[0];
1293
+ return {
1294
+ currentBranch: branchName,
1295
+ isAhead: aheadCount > 0,
1296
+ isBehind: behindCount > 0,
1297
+ aheadCount,
1298
+ behindCount,
1299
+ lastCommitDate: lastCommit ? new Date(lastCommit.commit.author.timestamp * 1e3).toISOString() : null,
1300
+ lastCommitMessage: lastCommit?.commit.message || null,
1301
+ hasUnpushedChanges: aheadCount > 0
1302
+ };
1303
+ } catch {
1304
+ const lastCommit = localCommits[0];
1305
+ return {
1306
+ currentBranch: branchName,
1307
+ isAhead: false,
1308
+ isBehind: false,
1309
+ aheadCount: 0,
1310
+ behindCount: 0,
1311
+ lastCommitDate: lastCommit ? new Date(lastCommit.commit.author.timestamp * 1e3).toISOString() : null,
1312
+ lastCommitMessage: lastCommit?.commit.message || null,
1313
+ hasUnpushedChanges: false
1314
+ };
1315
+ }
1316
+ } catch (error) {
1317
+ console.error("Error getting branch info:", error);
1318
+ return null;
1319
+ }
1320
+ }
1321
+ /**
1322
+ * Fetch updates from remote repositories using native git command
1323
+ */
1324
+ async fetchFromRemotes() {
1325
+ try {
1326
+ const isGitRepo = await this.isGitRepository();
1327
+ if (!isGitRepo) {
1328
+ return { success: false, message: "Not a git repository", details: [] };
1329
+ }
1330
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
1331
+ const remotes = await git.listRemotes({ fs: fs2, dir: gitRoot });
1332
+ if (remotes.length === 0) {
1333
+ return { success: true, message: "No remotes configured", details: [] };
1334
+ }
1335
+ const details = [];
1336
+ let hasErrors = false;
1337
+ for (const remote of remotes) {
1338
+ try {
1339
+ const fetchResult = await this.executeGitCommand(`git fetch ${remote.remote}`, gitRoot);
1340
+ if (fetchResult.success) {
1341
+ details.push(`Fetched from ${remote.remote}`);
1342
+ } else {
1343
+ details.push(`Failed to fetch from ${remote.remote}: ${fetchResult.error}`);
1344
+ hasErrors = true;
1345
+ }
1346
+ } catch (error) {
1347
+ details.push(`Error fetching from ${remote.remote}: ${error}`);
1348
+ hasErrors = true;
1349
+ }
1350
+ }
1351
+ return {
1352
+ success: !hasErrors,
1353
+ message: hasErrors ? "Fetch completed with some errors" : "Successfully fetched from all remotes",
1354
+ details
1355
+ };
1356
+ } catch (error) {
1357
+ console.error("Error fetching from remotes:", error);
1358
+ return {
1359
+ success: false,
1360
+ message: error instanceof Error ? error.message : "Unknown error occurred",
1361
+ details: []
1362
+ };
1363
+ }
1364
+ }
1365
+ /**
1366
+ * Pull changes from remote using native git command
1367
+ */
1368
+ async pullChanges() {
1369
+ try {
1370
+ const isGitRepo = await this.isGitRepository();
1371
+ if (!isGitRepo) {
1372
+ return { success: false, message: "Not a git repository" };
1373
+ }
1374
+ const currentBranch2 = await this.getCurrentBranch();
1375
+ if (!currentBranch2) {
1376
+ return { success: false, message: "No current branch found" };
1377
+ }
1378
+ const gitRoot = await git.findRoot({ fs: fs2, filepath: process.cwd() });
1379
+ const remotes = await git.listRemotes({ fs: fs2, dir: gitRoot });
1380
+ if (remotes.length === 0) {
1381
+ return { success: false, message: "No remotes configured" };
1382
+ }
1383
+ const targetRemote = remotes[0].remote;
1384
+ const pullCommand = `git pull ${targetRemote} ${currentBranch2}`;
1385
+ const pullResult = await this.executeGitCommand(pullCommand, gitRoot);
1386
+ if (!pullResult.success) {
1387
+ return {
1388
+ success: false,
1389
+ message: `Failed to pull changes: ${pullResult.error}`
1390
+ };
1391
+ }
1392
+ return {
1393
+ success: true,
1394
+ message: pullResult.output || "Successfully pulled changes"
1395
+ };
1396
+ } catch (error) {
1397
+ console.error("Error pulling changes:", error);
1398
+ return {
1399
+ success: false,
1400
+ message: error instanceof Error ? error.message : "Unknown error occurred"
1401
+ };
1402
+ }
1403
+ }
1160
1404
  };
1161
1405
 
1162
1406
  // cli/server/serverState.ts