@mrpatronz/nexusflow 2.10.0 → 2.10.2

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 (95) hide show
  1. package/README.md +5 -0
  2. package/desktop/assets/icon.png +0 -0
  3. package/dist/agent/ClaudeCliAdapter.d.ts +2 -2
  4. package/dist/agent/ClaudeCliAdapter.d.ts.map +1 -1
  5. package/dist/agent/ClaudeCliAdapter.js +6 -4
  6. package/dist/agent/ClaudeCliAdapter.js.map +1 -1
  7. package/dist/agent/CliAdapterBase.d.ts.map +1 -1
  8. package/dist/agent/CliAdapterBase.js +8 -1
  9. package/dist/agent/CliAdapterBase.js.map +1 -1
  10. package/dist/agent/cliAvailability.d.ts +2 -5
  11. package/dist/agent/cliAvailability.d.ts.map +1 -1
  12. package/dist/agent/cliAvailability.js +6 -41
  13. package/dist/agent/cliAvailability.js.map +1 -1
  14. package/dist/agent/session.d.ts +5 -0
  15. package/dist/agent/session.d.ts.map +1 -1
  16. package/dist/agent/session.js +8 -0
  17. package/dist/agent/session.js.map +1 -1
  18. package/dist/commands/create.d.ts.map +1 -1
  19. package/dist/commands/create.js +40 -0
  20. package/dist/commands/create.js.map +1 -1
  21. package/dist/commands/desktop.d.ts +2 -0
  22. package/dist/commands/desktop.d.ts.map +1 -1
  23. package/dist/commands/desktop.js +102 -31
  24. package/dist/commands/desktop.js.map +1 -1
  25. package/dist/core/workspace.d.ts +10 -1
  26. package/dist/core/workspace.d.ts.map +1 -1
  27. package/dist/core/workspace.js +21 -4
  28. package/dist/core/workspace.js.map +1 -1
  29. package/dist/core/worktree.d.ts +10 -4
  30. package/dist/core/worktree.d.ts.map +1 -1
  31. package/dist/core/worktree.js +34 -47
  32. package/dist/core/worktree.js.map +1 -1
  33. package/dist/generators/base.d.ts.map +1 -1
  34. package/dist/generators/base.js +0 -1
  35. package/dist/generators/base.js.map +1 -1
  36. package/dist/gui/assets/AgentsPage-DbivhmK0.js +3 -0
  37. package/dist/gui/assets/DashboardPage-D50Qrm3e.js +1 -0
  38. package/dist/gui/assets/{GettingStartedPage-BKeuSHZQ.js → GettingStartedPage-1MOTng3B.js} +1 -1
  39. package/dist/gui/assets/{NullStore-DuSDhGOe.js → NullStore-tdPen1kp.js} +1 -1
  40. package/dist/gui/assets/ProjectsPage-srKgf7VN.js +1 -0
  41. package/dist/gui/assets/ScaffoldRepoInline-CP0yn5mw.js +1 -0
  42. package/dist/gui/assets/SettingsPage-DWmrLV8o.js +1 -0
  43. package/dist/gui/assets/SkillsPage-CKjTXdrd.js +6 -0
  44. package/dist/gui/assets/StartWorkPage-BCUnJ9k8.js +1 -0
  45. package/dist/gui/assets/{StrategiesPage-BRQMly_d.js → StrategiesPage-CdCbbIu-.js} +2 -2
  46. package/dist/gui/assets/WorkroomsPage-BcT-mGtT.js +4 -0
  47. package/dist/gui/assets/WorkspacesPage-CYFCm-7_.js +2 -0
  48. package/dist/gui/assets/{alert-dialog-BHEUQVyS.js → alert-dialog-u7qgb9Hq.js} +1 -1
  49. package/dist/gui/assets/badge-DwXvB44H.js +1 -0
  50. package/dist/gui/assets/chunk-BV7QT456-DfUBKG-z.js +3 -0
  51. package/dist/gui/assets/index-3tD_IeyQ.js +55 -0
  52. package/dist/gui/assets/index-CK5YRfPV.css +2 -0
  53. package/dist/gui/assets/tabs-BrHKp5bD.js +1 -0
  54. package/dist/gui/assets/{useAriaLabelledBy-BmJErI5a.js → useAriaLabelledBy-C4zV-1Ra.js} +1 -1
  55. package/dist/gui/assets/{vsc-eMRaEwN3.js → vsc-D80c4gBT.js} +1 -1
  56. package/dist/gui/index.html +5 -8
  57. package/dist/orchestration/runner.d.ts.map +1 -1
  58. package/dist/orchestration/runner.js +28 -10
  59. package/dist/orchestration/runner.js.map +1 -1
  60. package/dist/server.d.ts.map +1 -1
  61. package/dist/server.js +116 -10
  62. package/dist/server.js.map +1 -1
  63. package/dist/utils/repo-freshness.d.ts +106 -0
  64. package/dist/utils/repo-freshness.d.ts.map +1 -0
  65. package/dist/utils/repo-freshness.js +405 -0
  66. package/dist/utils/repo-freshness.js.map +1 -0
  67. package/dist/utils/session-finder.d.ts.map +1 -1
  68. package/dist/utils/session-finder.js +23 -8
  69. package/dist/utils/session-finder.js.map +1 -1
  70. package/dist/utils/terminal-launch.d.ts +4 -2
  71. package/dist/utils/terminal-launch.d.ts.map +1 -1
  72. package/dist/utils/terminal-launch.js +26 -16
  73. package/dist/utils/terminal-launch.js.map +1 -1
  74. package/dist/utils/user-paths.d.ts +23 -0
  75. package/dist/utils/user-paths.d.ts.map +1 -0
  76. package/dist/utils/user-paths.js +126 -0
  77. package/dist/utils/user-paths.js.map +1 -0
  78. package/package.json +6 -2
  79. package/dist/gui/assets/AgentsPage-CnXzZFo1.js +0 -3
  80. package/dist/gui/assets/DashboardPage-rm3hKgsw.js +0 -1
  81. package/dist/gui/assets/ProjectsPage-CioaA4uf.js +0 -1
  82. package/dist/gui/assets/ScaffoldRepoInline-DKZ1rPmE.js +0 -1
  83. package/dist/gui/assets/Separator-BgWyHDtg.js +0 -1
  84. package/dist/gui/assets/SettingsPage-BevO8oge.js +0 -1
  85. package/dist/gui/assets/SkillsPage-D2stJRWy.js +0 -6
  86. package/dist/gui/assets/StartWorkPage-C8a6eN90.js +0 -1
  87. package/dist/gui/assets/WorkroomsPage-KqBkRD-x.js +0 -4
  88. package/dist/gui/assets/WorkspacesPage-DcA2i5HN.js +0 -2
  89. package/dist/gui/assets/badge-DTBzXOWn.js +0 -1
  90. package/dist/gui/assets/index-BB4BuHBQ.css +0 -2
  91. package/dist/gui/assets/index-BTe-_1Mu.js +0 -57
  92. package/dist/gui/assets/isElementDisabled-BrwmCB1d.js +0 -1
  93. package/dist/gui/assets/menu-d_G9km1-.js +0 -1
  94. package/dist/gui/assets/tabs-BHwTt4t1.js +0 -1
  95. package/dist/gui/assets/useValueChanged-WS99hTmm.js +0 -1
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @module utils/repo-freshness
3
+ * Base repository freshness inspection and safe fast-forward synchronization.
4
+ *
5
+ * Helps developers detect when a base repository's default branch (or custom branch)
6
+ * is behind remote tracking commits before creating a new workspace, and safely
7
+ * fast-forwards clean repositories without disturbing dirty working trees.
8
+ */
9
+ export type FreshnessStatus = 'up-to-date' | 'behind' | 'ahead' | 'diverged' | 'untracked' | 'offline' | 'error';
10
+ export interface RepoFreshness {
11
+ /** Absolute path to the repository. */
12
+ repoPath: string;
13
+ /** Repository name (directory basename). */
14
+ repoName: string;
15
+ /** The branch inspected. */
16
+ branch: string;
17
+ /** The repository's detected default branch. */
18
+ defaultBranch: string;
19
+ /** Remote tracking branch ref, e.g. 'origin/main' or 'upstream/main', or null if untracked. */
20
+ trackingBranch: string | null;
21
+ /** The name of the remote, e.g. 'origin' or 'upstream', or null. */
22
+ remoteName: string | null;
23
+ /** Whether the repository has at least one remote configured. */
24
+ hasRemote: boolean;
25
+ /** Whether the working tree is clean (no uncommitted or untracked changes). */
26
+ isClean: boolean;
27
+ /** Number of commits local is ahead of tracking branch. */
28
+ ahead: number;
29
+ /** Number of commits local is behind tracking branch. */
30
+ behind: number;
31
+ /** Classified freshness status. */
32
+ status: FreshnessStatus;
33
+ /** Human-readable status message. */
34
+ message: string;
35
+ /** Error detail if an error occurred during fetch or inspection. */
36
+ error?: string;
37
+ }
38
+ export type FastForwardStatus = 'up-to-date' | 'fast-forwarded' | 'ahead' | 'dirty' | 'diverged' | 'untracked' | 'offline' | 'error';
39
+ export interface FastForwardResult {
40
+ success: boolean;
41
+ repoPath: string;
42
+ repoName: string;
43
+ branch: string;
44
+ status: FastForwardStatus;
45
+ message: string;
46
+ ahead: number;
47
+ behind: number;
48
+ error?: string;
49
+ }
50
+ export interface CheckFreshnessOptions {
51
+ /** Whether to run `git fetch` before checking status (default: true). */
52
+ fetch?: boolean;
53
+ /** Timeout in milliseconds for git network operations (default: 10000ms). */
54
+ timeoutMs?: number;
55
+ }
56
+ /**
57
+ * Gets the configured or inferred remote tracking branch for a given branch in a repository.
58
+ * Tries `git rev-parse --abbrev-ref <branch>@{u}` first. If no upstream is configured,
59
+ * scans available remotes ('origin', 'upstream', etc.) for a matching remote branch ref.
60
+ */
61
+ export declare function resolveTrackingBranch(repoPath: string, branch: string): Promise<{
62
+ trackingBranch: string | null;
63
+ remoteName: string | null;
64
+ hasRemote: boolean;
65
+ }>;
66
+ /**
67
+ * Checks whether the repository working tree is clean.
68
+ */
69
+ export declare function isRepoClean(repoPath: string): Promise<boolean>;
70
+ /**
71
+ * Checks the freshness of a branch against its remote tracking branch.
72
+ */
73
+ export declare function checkRepoFreshness(repoPath: string, targetBranch?: string, options?: CheckFreshnessOptions): Promise<RepoFreshness>;
74
+ /**
75
+ * Safely fast-forwards a repository branch to its remote tracking branch.
76
+ *
77
+ * Rules:
78
+ * 1. If up-to-date, does nothing and reports success.
79
+ * 2. If diverged, fails cleanly without modifying anything.
80
+ * 3. If the inspected branch is currently checked out:
81
+ * - Fails if the working tree is dirty (protects user's uncommitted work).
82
+ * - Runs `git merge --ff-only <trackingBranch>` if clean.
83
+ * 4. If another branch is checked out:
84
+ * - Safely fast-forwards the ref via `git fetch <remote> <remoteBranch>:<branch>`
85
+ * or `git update-ref` without touching the active working directory.
86
+ */
87
+ export declare function fastForwardBranch(repoPath: string, targetBranch?: string, options?: {
88
+ timeoutMs?: number;
89
+ }): Promise<FastForwardResult>;
90
+ /**
91
+ * Checks freshness for a batch of repositories.
92
+ */
93
+ export declare function checkReposFreshness(repos: Array<{
94
+ path: string;
95
+ branch?: string;
96
+ }>, options?: CheckFreshnessOptions): Promise<RepoFreshness[]>;
97
+ /**
98
+ * Fast-forwards base branches for a batch of repositories.
99
+ */
100
+ export declare function fastForwardRepos(repos: Array<{
101
+ path: string;
102
+ branch?: string;
103
+ }>, options?: {
104
+ timeoutMs?: number;
105
+ }): Promise<FastForwardResult[]>;
106
+ //# sourceMappingURL=repo-freshness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-freshness.d.ts","sourceRoot":"","sources":["../../src/utils/repo-freshness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,405 @@
1
+ /**
2
+ * @module utils/repo-freshness
3
+ * Base repository freshness inspection and safe fast-forward synchronization.
4
+ *
5
+ * Helps developers detect when a base repository's default branch (or custom branch)
6
+ * is behind remote tracking commits before creating a new workspace, and safely
7
+ * fast-forwards clean repositories without disturbing dirty working trees.
8
+ */
9
+ import { execa } from 'execa';
10
+ import { detectDefaultBranch } from './git.js';
11
+ /**
12
+ * Gets the configured or inferred remote tracking branch for a given branch in a repository.
13
+ * Tries `git rev-parse --abbrev-ref <branch>@{u}` first. If no upstream is configured,
14
+ * scans available remotes ('origin', 'upstream', etc.) for a matching remote branch ref.
15
+ */
16
+ export async function resolveTrackingBranch(repoPath, branch) {
17
+ // 1. Check if git has an explicit upstream tracking branch configured
18
+ try {
19
+ const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', `${branch}@{u}`], { cwd: repoPath });
20
+ const tracking = stdout.trim();
21
+ if (tracking && !tracking.endsWith('@{u}')) {
22
+ const slashIndex = tracking.indexOf('/');
23
+ const remoteName = slashIndex > 0 ? tracking.slice(0, slashIndex) : null;
24
+ return { trackingBranch: tracking, remoteName, hasRemote: true };
25
+ }
26
+ }
27
+ catch {
28
+ // No explicit upstream configured
29
+ }
30
+ // 2. Discover remotes
31
+ let remotes = [];
32
+ try {
33
+ const { stdout } = await execa('git', ['remote'], { cwd: repoPath });
34
+ remotes = stdout
35
+ .split('\n')
36
+ .map((r) => r.trim())
37
+ .filter(Boolean);
38
+ }
39
+ catch {
40
+ return { trackingBranch: null, remoteName: null, hasRemote: false };
41
+ }
42
+ if (remotes.length === 0) {
43
+ return { trackingBranch: null, remoteName: null, hasRemote: false };
44
+ }
45
+ // Prioritize origin, then upstream, then any other remote
46
+ const candidateRemotes = [
47
+ ...remotes.filter((r) => r === 'origin'),
48
+ ...remotes.filter((r) => r === 'upstream'),
49
+ ...remotes.filter((r) => r !== 'origin' && r !== 'upstream'),
50
+ ];
51
+ for (const remote of candidateRemotes) {
52
+ try {
53
+ await execa('git', ['rev-parse', '--verify', `refs/remotes/${remote}/${branch}`], {
54
+ cwd: repoPath,
55
+ });
56
+ return { trackingBranch: `${remote}/${branch}`, remoteName: remote, hasRemote: true };
57
+ }
58
+ catch {
59
+ // Remote branch does not exist on this remote
60
+ }
61
+ }
62
+ return { trackingBranch: null, remoteName: candidateRemotes[0] ?? null, hasRemote: true };
63
+ }
64
+ /**
65
+ * Checks whether the repository working tree is clean.
66
+ */
67
+ export async function isRepoClean(repoPath) {
68
+ try {
69
+ const { stdout } = await execa('git', ['status', '--porcelain'], { cwd: repoPath });
70
+ return stdout.trim().length === 0;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ /**
77
+ * Checks the freshness of a branch against its remote tracking branch.
78
+ */
79
+ export async function checkRepoFreshness(repoPath, targetBranch, options = {}) {
80
+ const repoName = repoPath.split(/[\/\\]/).filter(Boolean).pop() || 'repo';
81
+ let defaultBranch = 'main';
82
+ try {
83
+ defaultBranch = await detectDefaultBranch(repoPath);
84
+ }
85
+ catch {
86
+ // fallback
87
+ }
88
+ const branch = targetBranch || defaultBranch;
89
+ const isClean = await isRepoClean(repoPath);
90
+ const { trackingBranch, remoteName, hasRemote } = await resolveTrackingBranch(repoPath, branch);
91
+ if (!hasRemote) {
92
+ return {
93
+ repoPath,
94
+ repoName,
95
+ branch,
96
+ defaultBranch,
97
+ trackingBranch: null,
98
+ remoteName: null,
99
+ hasRemote: false,
100
+ isClean,
101
+ ahead: 0,
102
+ behind: 0,
103
+ status: 'untracked',
104
+ message: 'Local repository (no remotes configured)',
105
+ };
106
+ }
107
+ let fetchFailed = false;
108
+ let fetchErrorMsg;
109
+ // Run fetch if requested and remote is known
110
+ if (options.fetch !== false && remoteName) {
111
+ try {
112
+ await execa('git', ['fetch', remoteName], {
113
+ cwd: repoPath,
114
+ timeout: options.timeoutMs ?? 10000,
115
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
116
+ });
117
+ }
118
+ catch (err) {
119
+ fetchFailed = true;
120
+ fetchErrorMsg = err instanceof Error ? err.message : String(err);
121
+ }
122
+ }
123
+ // Re-verify tracking branch in case fetch just populated it
124
+ let resolvedTracking = trackingBranch;
125
+ if (!resolvedTracking && remoteName) {
126
+ try {
127
+ await execa('git', ['rev-parse', '--verify', `refs/remotes/${remoteName}/${branch}`], {
128
+ cwd: repoPath,
129
+ });
130
+ resolvedTracking = `${remoteName}/${branch}`;
131
+ }
132
+ catch {
133
+ // Still untracked
134
+ }
135
+ }
136
+ if (!resolvedTracking) {
137
+ return {
138
+ repoPath,
139
+ repoName,
140
+ branch,
141
+ defaultBranch,
142
+ trackingBranch: null,
143
+ remoteName,
144
+ hasRemote: true,
145
+ isClean,
146
+ ahead: 0,
147
+ behind: 0,
148
+ status: fetchFailed ? 'offline' : 'untracked',
149
+ message: fetchFailed
150
+ ? `Could not reach remote ${remoteName} (offline)`
151
+ : `Branch "${branch}" has no tracking branch on ${remoteName}`,
152
+ error: fetchErrorMsg,
153
+ };
154
+ }
155
+ // Calculate ahead / behind counts
156
+ try {
157
+ const { stdout } = await execa('git', ['rev-list', '--left-right', '--count', `${branch}...${resolvedTracking}`], { cwd: repoPath });
158
+ const [aheadRaw, behindRaw] = stdout.trim().split(/\s+/);
159
+ const ahead = parseInt(aheadRaw ?? '0', 10) || 0;
160
+ const behind = parseInt(behindRaw ?? '0', 10) || 0;
161
+ let status;
162
+ let message;
163
+ if (ahead === 0 && behind === 0) {
164
+ status = 'up-to-date';
165
+ message = `Up to date with ${resolvedTracking}`;
166
+ }
167
+ else if (ahead === 0 && behind > 0) {
168
+ status = 'behind';
169
+ message = `${behind} commit${behind === 1 ? '' : 's'} behind ${resolvedTracking}`;
170
+ }
171
+ else if (ahead > 0 && behind === 0) {
172
+ status = 'ahead';
173
+ message = `${ahead} commit${ahead === 1 ? '' : 's'} ahead of ${resolvedTracking}`;
174
+ }
175
+ else {
176
+ status = 'diverged';
177
+ message = `Diverged (${ahead} ahead, ${behind} behind ${resolvedTracking})`;
178
+ }
179
+ if (fetchFailed) {
180
+ message += ' (offline - using cached refs)';
181
+ }
182
+ return {
183
+ repoPath,
184
+ repoName,
185
+ branch,
186
+ defaultBranch,
187
+ trackingBranch: resolvedTracking,
188
+ remoteName,
189
+ hasRemote: true,
190
+ isClean,
191
+ ahead,
192
+ behind,
193
+ status,
194
+ message,
195
+ error: fetchErrorMsg,
196
+ };
197
+ }
198
+ catch (err) {
199
+ return {
200
+ repoPath,
201
+ repoName,
202
+ branch,
203
+ defaultBranch,
204
+ trackingBranch: resolvedTracking,
205
+ remoteName,
206
+ hasRemote: true,
207
+ isClean,
208
+ ahead: 0,
209
+ behind: 0,
210
+ status: 'error',
211
+ message: `Failed to compare branch ${branch} with ${resolvedTracking}`,
212
+ error: err instanceof Error ? err.message : String(err),
213
+ };
214
+ }
215
+ }
216
+ /**
217
+ * Safely fast-forwards a repository branch to its remote tracking branch.
218
+ *
219
+ * Rules:
220
+ * 1. If up-to-date, does nothing and reports success.
221
+ * 2. If diverged, fails cleanly without modifying anything.
222
+ * 3. If the inspected branch is currently checked out:
223
+ * - Fails if the working tree is dirty (protects user's uncommitted work).
224
+ * - Runs `git merge --ff-only <trackingBranch>` if clean.
225
+ * 4. If another branch is checked out:
226
+ * - Safely fast-forwards the ref via `git fetch <remote> <remoteBranch>:<branch>`
227
+ * or `git update-ref` without touching the active working directory.
228
+ */
229
+ export async function fastForwardBranch(repoPath, targetBranch, options = {}) {
230
+ const freshness = await checkRepoFreshness(repoPath, targetBranch, {
231
+ fetch: true,
232
+ timeoutMs: options.timeoutMs,
233
+ });
234
+ const { repoName, branch, trackingBranch, remoteName, isClean, ahead, behind, status } = freshness;
235
+ if (status === 'up-to-date') {
236
+ return {
237
+ success: true,
238
+ repoPath,
239
+ repoName,
240
+ branch,
241
+ status: 'up-to-date',
242
+ message: `Already up to date with ${trackingBranch}`,
243
+ ahead: 0,
244
+ behind: 0,
245
+ };
246
+ }
247
+ if (status === 'ahead') {
248
+ return {
249
+ success: true,
250
+ repoPath,
251
+ repoName,
252
+ branch,
253
+ status: 'ahead',
254
+ message: `Branch "${branch}" is ahead of ${trackingBranch} by ${ahead} commit(s)`,
255
+ ahead,
256
+ behind: 0,
257
+ };
258
+ }
259
+ if (status === 'diverged') {
260
+ return {
261
+ success: false,
262
+ repoPath,
263
+ repoName,
264
+ branch,
265
+ status: 'diverged',
266
+ message: `Cannot fast-forward: branch "${branch}" has diverged from ${trackingBranch} (${ahead} ahead, ${behind} behind). Manual merge or rebase required.`,
267
+ ahead,
268
+ behind,
269
+ };
270
+ }
271
+ if (status === 'untracked') {
272
+ return {
273
+ success: false,
274
+ repoPath,
275
+ repoName,
276
+ branch,
277
+ status: 'untracked',
278
+ message: freshness.message,
279
+ ahead: 0,
280
+ behind: 0,
281
+ };
282
+ }
283
+ if (status === 'offline') {
284
+ return {
285
+ success: false,
286
+ repoPath,
287
+ repoName,
288
+ branch,
289
+ status: 'offline',
290
+ message: freshness.message,
291
+ ahead: 0,
292
+ behind: 0,
293
+ error: freshness.error,
294
+ };
295
+ }
296
+ if (status === 'error' || !trackingBranch) {
297
+ return {
298
+ success: false,
299
+ repoPath,
300
+ repoName,
301
+ branch,
302
+ status: 'error',
303
+ message: freshness.message,
304
+ ahead: 0,
305
+ behind: 0,
306
+ error: freshness.error,
307
+ };
308
+ }
309
+ // Target branch is behind. Determine currently checked out branch
310
+ let currentBranch = '';
311
+ try {
312
+ const { stdout } = await execa('git', ['branch', '--show-current'], { cwd: repoPath });
313
+ currentBranch = stdout.trim();
314
+ }
315
+ catch {
316
+ // detached HEAD or error
317
+ }
318
+ try {
319
+ if (currentBranch === branch) {
320
+ // Currently checked out: require clean working tree
321
+ if (!isClean) {
322
+ return {
323
+ success: false,
324
+ repoPath,
325
+ repoName,
326
+ branch,
327
+ status: 'dirty',
328
+ message: `Cannot fast-forward: working tree has uncommitted changes in "${branch}". Stash or commit changes first.`,
329
+ ahead,
330
+ behind,
331
+ };
332
+ }
333
+ await execa('git', ['merge', '--ff-only', trackingBranch], { cwd: repoPath });
334
+ return {
335
+ success: true,
336
+ repoPath,
337
+ repoName,
338
+ branch,
339
+ status: 'fast-forwarded',
340
+ message: `Fast-forwarded "${branch}" to ${trackingBranch} (${behind} commit${behind === 1 ? '' : 's'})`,
341
+ ahead: 0,
342
+ behind: 0,
343
+ };
344
+ }
345
+ else {
346
+ // Not checked out: verify ancestor and update ref safely
347
+ await execa('git', ['merge-base', '--is-ancestor', branch, trackingBranch], { cwd: repoPath });
348
+ // Fast-forward local ref from remote ref without checkout
349
+ const rName = remoteName || trackingBranch.split('/')[0];
350
+ const remoteBranchPart = trackingBranch.slice(rName.length + 1);
351
+ try {
352
+ await execa('git', ['fetch', rName, `${remoteBranchPart}:${branch}`], {
353
+ cwd: repoPath,
354
+ timeout: options.timeoutMs ?? 10000,
355
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
356
+ });
357
+ }
358
+ catch {
359
+ // If git fetch refuses or network is offline, update ref directly since ancestry is verified
360
+ const { stdout: commitSha } = await execa('git', ['rev-parse', trackingBranch], { cwd: repoPath });
361
+ await execa('git', ['update-ref', `refs/heads/${branch}`, commitSha.trim()], { cwd: repoPath });
362
+ }
363
+ return {
364
+ success: true,
365
+ repoPath,
366
+ repoName,
367
+ branch,
368
+ status: 'fast-forwarded',
369
+ message: `Fast-forwarded "${branch}" to ${trackingBranch} (${behind} commit${behind === 1 ? '' : 's'})`,
370
+ ahead: 0,
371
+ behind: 0,
372
+ };
373
+ }
374
+ }
375
+ catch (err) {
376
+ return {
377
+ success: false,
378
+ repoPath,
379
+ repoName,
380
+ branch,
381
+ status: 'error',
382
+ message: `Failed to fast-forward "${branch}" to ${trackingBranch}: ${err instanceof Error ? err.message : String(err)}`,
383
+ ahead,
384
+ behind,
385
+ error: err instanceof Error ? err.message : String(err),
386
+ };
387
+ }
388
+ }
389
+ /**
390
+ * Checks freshness for a batch of repositories.
391
+ */
392
+ export async function checkReposFreshness(repos, options = {}) {
393
+ return Promise.all(repos.map((r) => checkRepoFreshness(r.path, r.branch, options)));
394
+ }
395
+ /**
396
+ * Fast-forwards base branches for a batch of repositories.
397
+ */
398
+ export async function fastForwardRepos(repos, options = {}) {
399
+ const results = [];
400
+ for (const repo of repos) {
401
+ results.push(await fastForwardBranch(repo.path, repo.branch, options));
402
+ }
403
+ return results;
404
+ }
405
+ //# sourceMappingURL=repo-freshness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-freshness.js","sourceRoot":"","sources":["../../src/utils/repo-freshness.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-finder.d.ts","sourceRoot":"","sources":["../../src/utils/session-finder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,eAAO,MAAM,aAAa,QAAoE,CAAC;AAE/F,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAEhD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAO9E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAGvE;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAOpD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAUpE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAOrD;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAI7E;AA4CD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAaT;AAED;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,OAAO,CAAC,CAgDlB;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAiSxG;AAgBD,mFAAmF;AACnF,wBAAgB,uBAAuB,IAAI,IAAI,CAI9C;AA2FD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CA4DxB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA2KvG"}
1
+ {"version":3,"file":"session-finder.d.ts","sourceRoot":"","sources":["../../src/utils/session-finder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvE,eAAO,MAAM,aAAa,QAAoE,CAAC;AAE/F,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAEhD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAO9E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAGvE;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAOpD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAUpE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAOrD;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAI7E;AA4CD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAaT;AAED;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,OAAO,CAAC,CAgDlB;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAgTxG;AAgBD,mFAAmF;AACnF,wBAAgB,uBAAuB,IAAI,IAAI,CAI9C;AA2FD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CA4DxB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA2KvG"}
@@ -323,6 +323,21 @@ export async function findSessions(workspacePath, repoPaths = []) {
323
323
  }
324
324
  return false;
325
325
  };
326
+ const resolveTargetCwd = (sessPath) => {
327
+ if (!sessPath)
328
+ return workspacePath;
329
+ const normSess = cleanPath(sessPath);
330
+ for (let i = 0; i < repoPaths.length; i++) {
331
+ const r = normRepos[i];
332
+ if (normSess === r || normSess.startsWith(r + sep)) {
333
+ return repoPaths[i];
334
+ }
335
+ }
336
+ if (normSess === normWorkspace || normSess.startsWith(normWorkspace + sep)) {
337
+ return sessPath;
338
+ }
339
+ return workspacePath;
340
+ };
326
341
  // ─── 1. Scan Antigravity Sessions ──────────────────────────────────────
327
342
  const agDir = getAntigravityDir();
328
343
  const agHistoryPath = path.join(agDir, 'history.jsonl');
@@ -383,7 +398,7 @@ export async function findSessions(workspacePath, repoPaths = []) {
383
398
  createdAt,
384
399
  updatedAt,
385
400
  messageCount,
386
- workspacePath,
401
+ workspacePath: resolveTargetCwd(convData.entries[0]?.workspace),
387
402
  });
388
403
  }
389
404
  }
@@ -392,11 +407,11 @@ export async function findSessions(workspacePath, repoPaths = []) {
392
407
  const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
393
408
  const claudeProjectsDir = path.join(claudeConfigDir, 'projects');
394
409
  try {
395
- const candidateFolders = [
396
- getClaudeProjectFolderName(workspacePath),
397
- ...repoPaths.map(r => getClaudeProjectFolderName(r))
410
+ const candidateTargets = [
411
+ { folder: getClaudeProjectFolderName(workspacePath), targetPath: workspacePath },
412
+ ...repoPaths.map(r => ({ folder: getClaudeProjectFolderName(r), targetPath: r }))
398
413
  ];
399
- for (const folder of candidateFolders) {
414
+ for (const { folder, targetPath } of candidateTargets) {
400
415
  const projectPath = path.join(claudeProjectsDir, folder);
401
416
  try {
402
417
  const files = await fs.readdir(projectPath);
@@ -444,7 +459,7 @@ export async function findSessions(workspacePath, repoPaths = []) {
444
459
  createdAt: createdAt || new Date().toISOString(),
445
460
  updatedAt: updatedAt || new Date().toISOString(),
446
461
  messageCount,
447
- workspacePath,
462
+ workspacePath: targetPath,
448
463
  });
449
464
  }
450
465
  catch { }
@@ -528,7 +543,7 @@ export async function findSessions(workspacePath, repoPaths = []) {
528
543
  createdAt: createdAt || new Date().toISOString(),
529
544
  updatedAt: updatedAt || new Date().toISOString(),
530
545
  messageCount,
531
- workspacePath,
546
+ workspacePath: resolveTargetCwd(sessionCwd || undefined),
532
547
  });
533
548
  }
534
549
  catch { }
@@ -571,7 +586,7 @@ export async function findSessions(workspacePath, repoPaths = []) {
571
586
  createdAt: row.created_at || new Date().toISOString(),
572
587
  updatedAt: row.updated_at || new Date().toISOString(),
573
588
  messageCount,
574
- workspacePath,
589
+ workspacePath: resolveTargetCwd(row.cwd),
575
590
  });
576
591
  }
577
592
  }