gitpanic 1.0.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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +258 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +111 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core.d.ts +20 -0
  8. package/dist/core.d.ts.map +1 -0
  9. package/dist/core.js +55 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/detectors/index.d.ts +41 -0
  12. package/dist/detectors/index.d.ts.map +1 -0
  13. package/dist/detectors/index.js +258 -0
  14. package/dist/detectors/index.js.map +1 -0
  15. package/dist/git/executor.d.ts +15 -0
  16. package/dist/git/executor.d.ts.map +1 -0
  17. package/dist/git/executor.js +62 -0
  18. package/dist/git/executor.js.map +1 -0
  19. package/dist/git/reflog.d.ts +18 -0
  20. package/dist/git/reflog.d.ts.map +1 -0
  21. package/dist/git/reflog.js +58 -0
  22. package/dist/git/reflog.js.map +1 -0
  23. package/dist/git/status.d.ts +22 -0
  24. package/dist/git/status.d.ts.map +1 -0
  25. package/dist/git/status.js +72 -0
  26. package/dist/git/status.js.map +1 -0
  27. package/dist/recoveries/index.d.ts +50 -0
  28. package/dist/recoveries/index.d.ts.map +1 -0
  29. package/dist/recoveries/index.js +422 -0
  30. package/dist/recoveries/index.js.map +1 -0
  31. package/dist/src/core.d.ts +20 -0
  32. package/dist/src/core.d.ts.map +1 -0
  33. package/dist/src/core.js +55 -0
  34. package/dist/src/core.js.map +1 -0
  35. package/dist/src/detectors/index.d.ts +41 -0
  36. package/dist/src/detectors/index.d.ts.map +1 -0
  37. package/dist/src/detectors/index.js +258 -0
  38. package/dist/src/detectors/index.js.map +1 -0
  39. package/dist/src/git/executor.d.ts +15 -0
  40. package/dist/src/git/executor.d.ts.map +1 -0
  41. package/dist/src/git/executor.js +62 -0
  42. package/dist/src/git/executor.js.map +1 -0
  43. package/dist/src/git/reflog.d.ts +18 -0
  44. package/dist/src/git/reflog.d.ts.map +1 -0
  45. package/dist/src/git/reflog.js +58 -0
  46. package/dist/src/git/reflog.js.map +1 -0
  47. package/dist/src/git/status.d.ts +22 -0
  48. package/dist/src/git/status.d.ts.map +1 -0
  49. package/dist/src/git/status.js +72 -0
  50. package/dist/src/git/status.js.map +1 -0
  51. package/dist/src/recoveries/index.d.ts +50 -0
  52. package/dist/src/recoveries/index.d.ts.map +1 -0
  53. package/dist/src/recoveries/index.js +422 -0
  54. package/dist/src/recoveries/index.js.map +1 -0
  55. package/package.json +42 -0
@@ -0,0 +1,258 @@
1
+ import { ReflogParser } from '../git/reflog.js';
2
+ import { StatusAnalyzer } from '../git/status.js';
3
+ export class BaseDetector {
4
+ git;
5
+ reflog;
6
+ status;
7
+ constructor(git) {
8
+ this.git = git;
9
+ this.reflog = new ReflogParser(git);
10
+ this.status = new StatusAnalyzer(git);
11
+ }
12
+ }
13
+ export class AccidentalCommitDetector extends BaseDetector {
14
+ async detect() {
15
+ const status = await this.status.analyze();
16
+ const lastCommit = status.lastCommitSha;
17
+ if (!lastCommit)
18
+ return null;
19
+ // Check if commit is very recent (< 2 minutes) and has changes
20
+ const timeDiff = Date.now() - status.lastCommitDate.getTime();
21
+ const isRecent = timeDiff < 2 * 60 * 1000; // 2 minutes
22
+ if (!isRecent)
23
+ return null;
24
+ return {
25
+ id: 'accidental-commit',
26
+ title: 'Accidental Commit',
27
+ description: `You just committed "${status.lastCommitMessage}" ${Math.floor(timeDiff / 1000)} seconds ago.`,
28
+ severity: 'low',
29
+ confidence: 0.8,
30
+ metadata: {
31
+ commitSha: lastCommit,
32
+ commitMessage: status.lastCommitMessage,
33
+ branch: status.currentBranch
34
+ }
35
+ };
36
+ }
37
+ }
38
+ export class DetachedHeadDetector extends BaseDetector {
39
+ async detect() {
40
+ const status = await this.status.analyze();
41
+ if (!status.isDetached)
42
+ return null;
43
+ const reflogEntries = await this.reflog.getReflog(5);
44
+ const lastBranchCommit = reflogEntries.find(e => e.message.startsWith('checkout:') && !e.message.includes('HEAD'));
45
+ return {
46
+ id: 'detached-head',
47
+ title: 'Detached HEAD',
48
+ description: 'You are in detached HEAD state. Changes here can be lost.',
49
+ severity: 'medium',
50
+ confidence: 1.0,
51
+ metadata: {
52
+ currentSha: status.lastCommitSha,
53
+ lastBranch: lastBranchCommit?.ref || 'unknown',
54
+ lastBranchSha: lastBranchCommit?.sha || null
55
+ }
56
+ };
57
+ }
58
+ }
59
+ export class DeletedBranchDetector extends BaseDetector {
60
+ async detect() {
61
+ const reflog = await this.reflog.getReflog(20);
62
+ // Look for recent branch deletions in reflog
63
+ const deletions = reflog.filter(entry => entry.message.includes('delete branch') ||
64
+ entry.message.match(/deleted\s+branch/i));
65
+ if (deletions.length === 0)
66
+ return null;
67
+ const latest = deletions[0];
68
+ const timeDiff = Date.now() - latest.timestamp.getTime();
69
+ const isRecent = timeDiff < 10 * 60 * 1000; // 10 minutes
70
+ if (!isRecent)
71
+ return null;
72
+ const branchMatch = latest.message.match(/branch\s+(\S+)/i);
73
+ const branchName = branchMatch ? branchMatch[1] : 'unknown';
74
+ return {
75
+ id: 'deleted-branch',
76
+ title: 'Deleted Branch',
77
+ description: `Branch "${branchName}" was deleted ${Math.floor(timeDiff / 1000)} seconds ago.`,
78
+ severity: 'high',
79
+ confidence: 0.9,
80
+ metadata: {
81
+ branchName,
82
+ deletedSha: latest.sha,
83
+ timestamp: latest.timestamp
84
+ }
85
+ };
86
+ }
87
+ }
88
+ export class ForcePushDetector extends BaseDetector {
89
+ async detect() {
90
+ const status = await this.status.analyze();
91
+ if (!status.currentBranch)
92
+ return null;
93
+ const forcePushes = await this.reflog.findForcePush(status.currentBranch);
94
+ if (forcePushes.length === 0)
95
+ return null;
96
+ const latest = forcePushes[0];
97
+ const timeDiff = Date.now() - latest.timestamp.getTime();
98
+ const isRecent = timeDiff < 5 * 60 * 1000; // 5 minutes
99
+ if (!isRecent)
100
+ return null;
101
+ return {
102
+ id: 'force-push',
103
+ title: 'Recent Force Push',
104
+ description: `You force-pushed to ${status.currentBranch} ${Math.floor(timeDiff / 1000)} seconds ago. This may have overwritten commits.`,
105
+ severity: 'high',
106
+ confidence: 0.95,
107
+ metadata: {
108
+ branch: status.currentBranch,
109
+ forcePushSha: latest.sha,
110
+ timestamp: latest.timestamp
111
+ }
112
+ };
113
+ }
114
+ }
115
+ export class BotchedMergeDetector extends BaseDetector {
116
+ async detect() {
117
+ const status = await this.status.analyze();
118
+ // Check for MERGE_HEAD (merge in progress)
119
+ const mergeHeadResult = await this.git.exec(['rev-parse', '--quiet', '--verify', 'MERGE_HEAD']);
120
+ if (mergeHeadResult.code === 0) {
121
+ return {
122
+ id: 'botched-merge',
123
+ title: 'Merge in Progress',
124
+ description: 'You have an unresolved merge. Conflicts need to be resolved.',
125
+ severity: 'medium',
126
+ confidence: 1.0,
127
+ metadata: {
128
+ mergeHeadSha: mergeHeadResult.stdout.trim(),
129
+ branch: status.currentBranch
130
+ }
131
+ };
132
+ }
133
+ // Check for recent failed merge in reflog
134
+ const reflog = await this.reflog.getReflog(10);
135
+ const failedMerge = reflog.find(entry => entry.message.startsWith('merge') &&
136
+ entry.message.includes('Fast-forward'));
137
+ if (failedMerge) {
138
+ const timeDiff = Date.now() - failedMerge.timestamp.getTime();
139
+ if (timeDiff < 3 * 60 * 1000) { // 3 minutes
140
+ return {
141
+ id: 'botched-merge',
142
+ title: 'Recent Merge Issue',
143
+ description: 'A merge was recently aborted or encountered issues.',
144
+ severity: 'medium',
145
+ confidence: 0.7,
146
+ metadata: {
147
+ mergeRef: failedMerge.sha,
148
+ timestamp: failedMerge.timestamp
149
+ }
150
+ };
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+ }
156
+ export class UncommittedChangesDetector extends BaseDetector {
157
+ async detect() {
158
+ const status = await this.status.analyze();
159
+ if (!status.hasUncommittedChanges)
160
+ return null;
161
+ const fileCount = status.stagedFiles.length +
162
+ status.unstagedFiles.length +
163
+ status.untrackedFiles.length;
164
+ if (fileCount === 0)
165
+ return null;
166
+ let description = `You have ${fileCount} uncommitted ${fileCount === 1 ? 'file' : 'files'}.`;
167
+ if (status.stagedFiles.length > 0) {
168
+ description += ` ${status.stagedFiles.length} staged.`;
169
+ }
170
+ if (status.unstagedFiles.length > 0) {
171
+ description += ` ${status.unstagedFiles.length} modified.`;
172
+ }
173
+ if (status.untrackedFiles.length > 0) {
174
+ description += ` ${status.untrackedFiles.length} untracked.`;
175
+ }
176
+ return {
177
+ id: 'uncommitted-changes',
178
+ title: 'Uncommitted Changes',
179
+ description,
180
+ severity: 'low',
181
+ confidence: 1.0,
182
+ metadata: {
183
+ stagedCount: status.stagedFiles.length,
184
+ unstagedCount: status.unstagedFiles.length,
185
+ untrackedCount: status.untrackedFiles.length,
186
+ files: {
187
+ staged: status.stagedFiles,
188
+ unstaged: status.unstagedFiles,
189
+ untracked: status.untrackedFiles
190
+ }
191
+ }
192
+ };
193
+ }
194
+ }
195
+ export class WrongBranchCommitDetector extends BaseDetector {
196
+ async detect() {
197
+ const status = await this.status.analyze();
198
+ if (!status.currentBranch || status.currentBranch === 'main' || status.currentBranch === 'master') {
199
+ return null;
200
+ }
201
+ // Check if the commit message looks like it belongs to main/master
202
+ const message = status.lastCommitMessage.toLowerCase();
203
+ const mainKeywords = ['chore:', 'fix:', 'hotfix:', 'refactor:', 'docs:', 'test:'];
204
+ const hasMainKeyword = mainKeywords.some(keyword => message.startsWith(keyword));
205
+ if (!hasMainKeyword)
206
+ return null;
207
+ const timeDiff = Date.now() - status.lastCommitDate.getTime();
208
+ const isRecent = timeDiff < 5 * 60 * 1000; // 5 minutes
209
+ if (!isRecent)
210
+ return null;
211
+ return {
212
+ id: 'wrong-branch-commit',
213
+ title: 'Potential Wrong Branch Commit',
214
+ description: `You committed "${status.lastCommitMessage}" on ${status.currentBranch}. This might belong on main/master.`,
215
+ severity: 'medium',
216
+ confidence: 0.5,
217
+ metadata: {
218
+ branch: status.currentBranch,
219
+ commitSha: status.lastCommitSha,
220
+ commitMessage: status.lastCommitMessage
221
+ }
222
+ };
223
+ }
224
+ }
225
+ export async function runAllDetectors(git) {
226
+ const detectors = [
227
+ new DetachedHeadDetector(git),
228
+ new DeletedBranchDetector(git),
229
+ new ForcePushDetector(git),
230
+ new BotchedMergeDetector(git),
231
+ new AccidentalCommitDetector(git),
232
+ new UncommittedChangesDetector(git),
233
+ new WrongBranchCommitDetector(git)
234
+ ];
235
+ const disasters = [];
236
+ for (const detector of detectors) {
237
+ try {
238
+ const result = await detector.detect();
239
+ if (result) {
240
+ disasters.push(result);
241
+ }
242
+ }
243
+ catch (error) {
244
+ // Ignore detector errors
245
+ console.error(`Detector error: ${error}`);
246
+ }
247
+ }
248
+ // Sort by confidence, then severity
249
+ disasters.sort((a, b) => {
250
+ if (b.confidence !== a.confidence) {
251
+ return b.confidence - a.confidence;
252
+ }
253
+ const severityOrder = { high: 0, medium: 1, low: 2 };
254
+ return severityOrder[a.severity] - severityOrder[b.severity];
255
+ });
256
+ return disasters;
257
+ }
258
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface GitCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ code: number;
5
+ }
6
+ export declare class GitExecutor {
7
+ exec(args: string[], dryRun?: boolean): Promise<GitCommandResult>;
8
+ getCurrentBranch(): Promise<string | null>;
9
+ isGitRepo(): Promise<boolean>;
10
+ hasUncommittedChanges(): Promise<boolean>;
11
+ getLastCommitSha(): Promise<string | null>;
12
+ getCommitMessage(sha: string): Promise<string>;
13
+ getCommitDate(sha: string): Promise<Date>;
14
+ }
15
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/git/executor.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,WAAW;IAChB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwB/D,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO1C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAK7B,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;IAKzC,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAM1C,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM9C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKhD"}
@@ -0,0 +1,62 @@
1
+ import { execFile } from 'child_process';
2
+ import { promisify } from 'util';
3
+ const execFileAsync = promisify(execFile);
4
+ export class GitExecutor {
5
+ async exec(args, dryRun = false) {
6
+ if (dryRun) {
7
+ return {
8
+ stdout: '',
9
+ stderr: `[DRY RUN] Would execute: git ${args.join(' ')}`,
10
+ code: 0
11
+ };
12
+ }
13
+ try {
14
+ const { stdout, stderr } = await execFileAsync('git', args, {
15
+ cwd: process.cwd(),
16
+ maxBuffer: 10 * 1024 * 1024 // 10MB buffer
17
+ });
18
+ return { stdout, stderr, code: 0 };
19
+ }
20
+ catch (error) {
21
+ return {
22
+ stdout: error.stdout || '',
23
+ stderr: error.stderr || error.message,
24
+ code: error.code || 1
25
+ };
26
+ }
27
+ }
28
+ async getCurrentBranch() {
29
+ const result = await this.exec(['rev-parse', '--abbrev-ref', 'HEAD']);
30
+ if (result.code !== 0)
31
+ return null;
32
+ const branch = result.stdout.trim();
33
+ return branch === 'HEAD' ? null : branch;
34
+ }
35
+ async isGitRepo() {
36
+ const result = await this.exec(['rev-parse', '--is-inside-work-tree']);
37
+ return result.stdout.trim() === 'true';
38
+ }
39
+ async hasUncommittedChanges() {
40
+ const result = await this.exec(['status', '--porcelain']);
41
+ return result.stdout.trim().length > 0;
42
+ }
43
+ async getLastCommitSha() {
44
+ const result = await this.exec(['rev-parse', 'HEAD']);
45
+ if (result.code !== 0)
46
+ return null;
47
+ return result.stdout.trim();
48
+ }
49
+ async getCommitMessage(sha) {
50
+ const result = await this.exec(['log', '-1', '--pretty=%B', sha]);
51
+ if (result.code !== 0)
52
+ return 'Unknown commit';
53
+ return result.stdout.trim();
54
+ }
55
+ async getCommitDate(sha) {
56
+ const result = await this.exec(['log', '-1', '--format=%ct', sha]);
57
+ if (result.code !== 0)
58
+ return new Date();
59
+ return new Date(parseInt(result.stdout.trim()) * 1000);
60
+ }
61
+ }
62
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/git/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { GitExecutor } from './executor.js';
2
+ export interface ReflogEntry {
3
+ sha: string;
4
+ ref: string;
5
+ message: string;
6
+ timestamp: Date;
7
+ index: number;
8
+ }
9
+ export declare class ReflogParser {
10
+ private git;
11
+ constructor(git: GitExecutor);
12
+ getReflog(limit?: number): Promise<ReflogEntry[]>;
13
+ findBeforeDate(date: Date): Promise<ReflogEntry | null>;
14
+ findBranchDelete(branchName: string): Promise<ReflogEntry | null>;
15
+ findForcePush(branchName: string): Promise<ReflogEntry[]>;
16
+ getPreviousState(steps?: number): Promise<string | null>;
17
+ }
18
+ //# sourceMappingURL=reflog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflog.d.ts","sourceRoot":"","sources":["../../src/git/reflog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,WAAW;IAE9B,SAAS,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsC7C,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAKvD,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAQjE,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAQzD,gBAAgB,CAAC,KAAK,SAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAK1D"}
@@ -0,0 +1,58 @@
1
+ export class ReflogParser {
2
+ git;
3
+ constructor(git) {
4
+ this.git = git;
5
+ }
6
+ async getReflog(limit = 20) {
7
+ const result = await this.git.exec(['reflog', '-n', `${limit}`, '--pretty=format:%H %gs']);
8
+ if (result.code !== 0) {
9
+ return [];
10
+ }
11
+ const entries = [];
12
+ const lines = result.stdout.trim().split('\n');
13
+ for (let i = 0; i < lines.length; i++) {
14
+ const line = lines[i].trim();
15
+ if (!line)
16
+ continue;
17
+ const parts = line.split(' ', 3);
18
+ if (parts.length < 3)
19
+ continue;
20
+ const [sha, ref, ...messageParts] = parts;
21
+ const message = messageParts.join(' ');
22
+ // Get timestamp using git log
23
+ const dateResult = await this.git.exec(['log', '-1', '--format=%ct', sha]);
24
+ const timestamp = dateResult.code === 0
25
+ ? new Date(parseInt(dateResult.stdout.trim()) * 1000)
26
+ : new Date();
27
+ entries.push({
28
+ sha,
29
+ ref: ref || 'HEAD',
30
+ message,
31
+ timestamp,
32
+ index: i
33
+ });
34
+ }
35
+ return entries;
36
+ }
37
+ async findBeforeDate(date) {
38
+ const entries = await this.getReflog(50);
39
+ return entries.find(entry => entry.timestamp < date) || null;
40
+ }
41
+ async findBranchDelete(branchName) {
42
+ const entries = await this.getReflog(50);
43
+ return entries.find(entry => entry.message.includes(`delete branch`) ||
44
+ entry.message.includes(branchName)) || null;
45
+ }
46
+ async findForcePush(branchName) {
47
+ const entries = await this.getReflog(50);
48
+ return entries.filter(entry => entry.message.includes('force-update') ||
49
+ entry.message.includes('forced update'));
50
+ }
51
+ async getPreviousState(steps = 1) {
52
+ const result = await this.git.exec(['rev-parse', `HEAD@{${steps}}`]);
53
+ if (result.code !== 0)
54
+ return null;
55
+ return result.stdout.trim();
56
+ }
57
+ }
58
+ //# sourceMappingURL=reflog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflog.js","sourceRoot":"","sources":["../../src/git/reflog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { GitExecutor } from './executor.js';
2
+ export interface GitStatus {
3
+ currentBranch: string | null;
4
+ isDetached: boolean;
5
+ hasUncommittedChanges: boolean;
6
+ stagedFiles: string[];
7
+ unstagedFiles: string[];
8
+ untrackedFiles: string[];
9
+ aheadCount: number;
10
+ behindCount: number;
11
+ lastCommitSha: string | null;
12
+ lastCommitMessage: string;
13
+ lastCommitDate: Date;
14
+ }
15
+ export declare class StatusAnalyzer {
16
+ private git;
17
+ constructor(git: GitExecutor);
18
+ analyze(): Promise<GitStatus>;
19
+ isBehindOrigin(): Promise<boolean>;
20
+ isAheadOfOrigin(): Promise<boolean>;
21
+ }
22
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/git/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,IAAI,CAAC;CACtB;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,WAAW;IAE9B,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;IAgE7B,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAKlC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;CAI1C"}
@@ -0,0 +1,72 @@
1
+ export class StatusAnalyzer {
2
+ git;
3
+ constructor(git) {
4
+ this.git = git;
5
+ }
6
+ async analyze() {
7
+ const currentBranch = await this.git.getCurrentBranch();
8
+ const isDetached = currentBranch === null;
9
+ const uncommittedResult = await this.git.exec(['status', '--porcelain']);
10
+ const hasUncommittedChanges = uncommittedResult.stdout.trim().length > 0;
11
+ const stagedFiles = [];
12
+ const unstagedFiles = [];
13
+ const untrackedFiles = [];
14
+ if (hasUncommittedChanges) {
15
+ const lines = uncommittedResult.stdout.trim().split('\n');
16
+ for (const line of lines) {
17
+ if (!line || line.length < 2)
18
+ continue;
19
+ const status = line.substring(0, 2);
20
+ const filepath = line.substring(3).trim();
21
+ if (status.startsWith('?')) {
22
+ untrackedFiles.push(filepath);
23
+ }
24
+ else if (status[1] === 'M' || status[1] === 'D') {
25
+ unstagedFiles.push(filepath);
26
+ }
27
+ else if (status[0] === 'M' || status[0] === 'A' || status[0] === 'D') {
28
+ stagedFiles.push(filepath);
29
+ }
30
+ }
31
+ }
32
+ let aheadCount = 0;
33
+ let behindCount = 0;
34
+ if (currentBranch && !isDetached) {
35
+ const aheadBehind = await this.git.exec(['rev-list', '--count', '--left-right', `origin/${currentBranch}...HEAD`]);
36
+ if (aheadBehind.code === 0) {
37
+ const [behind, ahead] = aheadBehind.stdout.trim().split('\t').map(Number);
38
+ behindCount = behind || 0;
39
+ aheadCount = ahead || 0;
40
+ }
41
+ }
42
+ const lastCommitSha = await this.git.getLastCommitSha();
43
+ const lastCommitMessage = lastCommitSha
44
+ ? await this.git.getCommitMessage(lastCommitSha)
45
+ : 'No commits';
46
+ const lastCommitDate = lastCommitSha
47
+ ? await this.git.getCommitDate(lastCommitSha)
48
+ : new Date();
49
+ return {
50
+ currentBranch,
51
+ isDetached,
52
+ hasUncommittedChanges,
53
+ stagedFiles,
54
+ unstagedFiles,
55
+ untrackedFiles,
56
+ aheadCount,
57
+ behindCount,
58
+ lastCommitSha,
59
+ lastCommitMessage,
60
+ lastCommitDate
61
+ };
62
+ }
63
+ async isBehindOrigin() {
64
+ const status = await this.analyze();
65
+ return status.behindCount > 0;
66
+ }
67
+ async isAheadOfOrigin() {
68
+ const status = await this.analyze();
69
+ return status.aheadCount > 0;
70
+ }
71
+ }
72
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/git/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { Disaster } from '../detectors/index.js';
2
+ import { GitExecutor } from '../git/executor.js';
3
+ export interface RecoveryStep {
4
+ command: string;
5
+ description: string;
6
+ riskLevel: 'safe' | 'medium' | 'high';
7
+ }
8
+ export interface RecoveryOption {
9
+ id: string;
10
+ title: string;
11
+ description: string;
12
+ riskLevel: 'safe' | 'medium' | 'high';
13
+ steps: RecoveryStep[];
14
+ }
15
+ export declare abstract class BaseRecovery {
16
+ protected git: GitExecutor;
17
+ constructor(git: GitExecutor);
18
+ abstract getOptions(disaster: Disaster): RecoveryOption[];
19
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
20
+ }
21
+ export declare class DetachedHeadRecovery extends BaseRecovery {
22
+ getOptions(disaster: Disaster): RecoveryOption[];
23
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
24
+ }
25
+ export declare class DeletedBranchRecovery extends BaseRecovery {
26
+ getOptions(disaster: Disaster): RecoveryOption[];
27
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
28
+ }
29
+ export declare class ForcePushRecovery extends BaseRecovery {
30
+ getOptions(disaster: Disaster): RecoveryOption[];
31
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
32
+ }
33
+ export declare class BotchedMergeRecovery extends BaseRecovery {
34
+ getOptions(disaster: Disaster): RecoveryOption[];
35
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
36
+ }
37
+ export declare class AccidentalCommitRecovery extends BaseRecovery {
38
+ getOptions(disaster: Disaster): RecoveryOption[];
39
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
40
+ }
41
+ export declare class UncommittedChangesRecovery extends BaseRecovery {
42
+ getOptions(disaster: Disaster): RecoveryOption[];
43
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
44
+ }
45
+ export declare class WrongBranchCommitRecovery extends BaseRecovery {
46
+ getOptions(disaster: Disaster): RecoveryOption[];
47
+ execute(optionId: string, disaster: Disaster, dryRun?: boolean): Promise<boolean>;
48
+ }
49
+ export declare function getRecoveryForDisaster(disaster: Disaster, git: GitExecutor): BaseRecovery;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recoveries/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IACtC,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,8BAAsB,YAAY;IACpB,SAAS,CAAC,GAAG,EAAE,WAAW;gBAAhB,GAAG,EAAE,WAAW;IAEtC,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IAEnD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAGtF;AAED,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IAiC1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAiBtF;AAED,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IAsC1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAkBtF;AAED,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IAgD1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAQtF;AAED,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IAyC1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAQtF;AAED,qBAAa,wBAAyB,SAAQ,YAAY;IACxD,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IA8C1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAkBtF;AAED,qBAAa,0BAA2B,SAAQ,YAAY;IAC1D,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IAwD1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAwBtF;AAED,qBAAa,yBAA0B,SAAQ,YAAY;IACzD,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;IA0C1C,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAUtF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,GAAG,YAAY,CAmBzF"}