linguclaw 0.4.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.
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/agent-system.d.ts +196 -0
- package/dist/agent-system.d.ts.map +1 -0
- package/dist/agent-system.js +738 -0
- package/dist/agent-system.js.map +1 -0
- package/dist/alphabeta.d.ts +54 -0
- package/dist/alphabeta.d.ts.map +1 -0
- package/dist/alphabeta.js +193 -0
- package/dist/alphabeta.js.map +1 -0
- package/dist/browser.d.ts +62 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +224 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +565 -0
- package/dist/cli.js.map +1 -0
- package/dist/code-parser.d.ts +39 -0
- package/dist/code-parser.d.ts.map +1 -0
- package/dist/code-parser.js +385 -0
- package/dist/code-parser.js.map +1 -0
- package/dist/config.d.ts +66 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +232 -0
- package/dist/config.js.map +1 -0
- package/dist/core/engine.d.ts +359 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +127 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/daemon.d.ts +29 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +212 -0
- package/dist/daemon.js.map +1 -0
- package/dist/email-receiver.d.ts +63 -0
- package/dist/email-receiver.d.ts.map +1 -0
- package/dist/email-receiver.js +553 -0
- package/dist/email-receiver.js.map +1 -0
- package/dist/git-integration.d.ts +180 -0
- package/dist/git-integration.d.ts.map +1 -0
- package/dist/git-integration.js +850 -0
- package/dist/git-integration.js.map +1 -0
- package/dist/inbox.d.ts +84 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +198 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/cpp.d.ts +51 -0
- package/dist/languages/cpp.d.ts.map +1 -0
- package/dist/languages/cpp.js +930 -0
- package/dist/languages/cpp.js.map +1 -0
- package/dist/languages/csharp.d.ts +79 -0
- package/dist/languages/csharp.d.ts.map +1 -0
- package/dist/languages/csharp.js +1776 -0
- package/dist/languages/csharp.js.map +1 -0
- package/dist/languages/go.d.ts +50 -0
- package/dist/languages/go.d.ts.map +1 -0
- package/dist/languages/go.js +882 -0
- package/dist/languages/go.js.map +1 -0
- package/dist/languages/java.d.ts +47 -0
- package/dist/languages/java.d.ts.map +1 -0
- package/dist/languages/java.js +649 -0
- package/dist/languages/java.js.map +1 -0
- package/dist/languages/python.d.ts +47 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +655 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/rust.d.ts +61 -0
- package/dist/languages/rust.d.ts.map +1 -0
- package/dist/languages/rust.js +1064 -0
- package/dist/languages/rust.js.map +1 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +133 -0
- package/dist/logger.js.map +1 -0
- package/dist/longterm-memory.d.ts +47 -0
- package/dist/longterm-memory.d.ts.map +1 -0
- package/dist/longterm-memory.js +300 -0
- package/dist/longterm-memory.js.map +1 -0
- package/dist/memory.d.ts +42 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +274 -0
- package/dist/memory.js.map +1 -0
- package/dist/messaging.d.ts +103 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/messaging.js +645 -0
- package/dist/messaging.js.map +1 -0
- package/dist/multi-provider.d.ts +69 -0
- package/dist/multi-provider.d.ts.map +1 -0
- package/dist/multi-provider.js +484 -0
- package/dist/multi-provider.js.map +1 -0
- package/dist/orchestrator.d.ts +65 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +441 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/plugins.d.ts +52 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +215 -0
- package/dist/plugins.js.map +1 -0
- package/dist/prism-orchestrator.d.ts +26 -0
- package/dist/prism-orchestrator.d.ts.map +1 -0
- package/dist/prism-orchestrator.js +191 -0
- package/dist/prism-orchestrator.js.map +1 -0
- package/dist/prism.d.ts +46 -0
- package/dist/prism.d.ts.map +1 -0
- package/dist/prism.js +188 -0
- package/dist/prism.js.map +1 -0
- package/dist/privacy.d.ts +23 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +220 -0
- package/dist/privacy.js.map +1 -0
- package/dist/proactive.d.ts +30 -0
- package/dist/proactive.d.ts.map +1 -0
- package/dist/proactive.js +260 -0
- package/dist/proactive.js.map +1 -0
- package/dist/refactoring-engine.d.ts +100 -0
- package/dist/refactoring-engine.d.ts.map +1 -0
- package/dist/refactoring-engine.js +717 -0
- package/dist/refactoring-engine.js.map +1 -0
- package/dist/resilience.d.ts +43 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +200 -0
- package/dist/resilience.js.map +1 -0
- package/dist/safety.d.ts +40 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +133 -0
- package/dist/safety.js.map +1 -0
- package/dist/sandbox.d.ts +33 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +173 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scheduler.d.ts +72 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +374 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/semantic-memory.d.ts +70 -0
- package/dist/semantic-memory.d.ts.map +1 -0
- package/dist/semantic-memory.js +430 -0
- package/dist/semantic-memory.js.map +1 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +575 -0
- package/dist/skills.js.map +1 -0
- package/dist/static/dashboard.html +853 -0
- package/dist/static/hub.html +772 -0
- package/dist/static/index.html +818 -0
- package/dist/static/logo.svg +24 -0
- package/dist/static/workflow-editor.html +913 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +303 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +295 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +90 -0
- package/dist/types.js.map +1 -0
- package/dist/web.d.ts +76 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2139 -0
- package/dist/web.js.map +1 -0
- package/dist/workflow-engine.d.ts +114 -0
- package/dist/workflow-engine.d.ts.map +1 -0
- package/dist/workflow-engine.js +855 -0
- package/dist/workflow-engine.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Git Integration for LinguClaw
|
|
4
|
+
* Advanced Git operations with blame, diff, history, and branch management
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.GitIntegration = void 0;
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const logger_1 = require("./logger");
|
|
45
|
+
const logger = (0, logger_1.getLogger)();
|
|
46
|
+
class GitIntegration {
|
|
47
|
+
repoPath;
|
|
48
|
+
isGitRepo = false;
|
|
49
|
+
constructor(repoPath) {
|
|
50
|
+
this.repoPath = path.resolve(repoPath);
|
|
51
|
+
this.checkIsRepo();
|
|
52
|
+
}
|
|
53
|
+
checkIsRepo() {
|
|
54
|
+
try {
|
|
55
|
+
const gitDir = path.join(this.repoPath, '.git');
|
|
56
|
+
this.isGitRepo = fs.existsSync(gitDir) && fs.statSync(gitDir).isDirectory();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
this.isGitRepo = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
isRepository() {
|
|
63
|
+
return this.isGitRepo;
|
|
64
|
+
}
|
|
65
|
+
execGit(args) {
|
|
66
|
+
try {
|
|
67
|
+
return (0, child_process_1.execSync)(`git ${args.join(' ')}`, {
|
|
68
|
+
cwd: this.repoPath,
|
|
69
|
+
encoding: 'utf-8',
|
|
70
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
71
|
+
}).trim();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logger.error(`Git command failed: git ${args.join(' ')} - ${error.message}`);
|
|
75
|
+
throw new Error(`Git command failed: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ============================================
|
|
79
|
+
// COMMIT OPERATIONS
|
|
80
|
+
// ============================================
|
|
81
|
+
getLog(options = {}) {
|
|
82
|
+
if (!this.isGitRepo)
|
|
83
|
+
return [];
|
|
84
|
+
const format = '%H|%h|%an|%ae|%ad|%s';
|
|
85
|
+
const args = ['log', `--format=${format}`, '--date=iso'];
|
|
86
|
+
if (options.maxCount)
|
|
87
|
+
args.push('-n', options.maxCount.toString());
|
|
88
|
+
if (options.since)
|
|
89
|
+
args.push(`--since=${options.since.toISOString()}`);
|
|
90
|
+
if (options.until)
|
|
91
|
+
args.push(`--until=${options.until.toISOString()}`);
|
|
92
|
+
if (options.author)
|
|
93
|
+
args.push(`--author=${options.author}`);
|
|
94
|
+
if (options.grep)
|
|
95
|
+
args.push(`--grep=${options.grep}`);
|
|
96
|
+
if (options.file)
|
|
97
|
+
args.push('--follow', '--', options.file);
|
|
98
|
+
if (options.branch)
|
|
99
|
+
args.push(options.branch);
|
|
100
|
+
const output = this.execGit(args);
|
|
101
|
+
if (!output)
|
|
102
|
+
return [];
|
|
103
|
+
return output.split('\n').map(line => {
|
|
104
|
+
const parts = line.split('|');
|
|
105
|
+
return {
|
|
106
|
+
hash: parts[0],
|
|
107
|
+
shortHash: parts[1],
|
|
108
|
+
author: parts[2],
|
|
109
|
+
email: parts[3],
|
|
110
|
+
date: new Date(parts[4]),
|
|
111
|
+
message: parts[5],
|
|
112
|
+
filesChanged: 0,
|
|
113
|
+
insertions: 0,
|
|
114
|
+
deletions: 0,
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
getCommitDetails(hash) {
|
|
119
|
+
if (!this.isGitRepo)
|
|
120
|
+
return null;
|
|
121
|
+
try {
|
|
122
|
+
const format = '%H|%h|%an|%ae|%ad|%s';
|
|
123
|
+
const output = this.execGit(['show', hash, `--format=${format}`, '--date=iso', '--quiet', '--numstat']);
|
|
124
|
+
const lines = output.split('\n');
|
|
125
|
+
const mainInfo = lines[0].split('|');
|
|
126
|
+
let filesChanged = 0;
|
|
127
|
+
let insertions = 0;
|
|
128
|
+
let deletions = 0;
|
|
129
|
+
for (const line of lines.slice(1)) {
|
|
130
|
+
const match = line.match(/(\d+)\s+(\d+)\s+/);
|
|
131
|
+
if (match) {
|
|
132
|
+
filesChanged++;
|
|
133
|
+
insertions += parseInt(match[1]) || 0;
|
|
134
|
+
deletions += parseInt(match[2]) || 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
hash: mainInfo[0],
|
|
139
|
+
shortHash: mainInfo[1],
|
|
140
|
+
author: mainInfo[2],
|
|
141
|
+
email: mainInfo[3],
|
|
142
|
+
date: new Date(mainInfo[4]),
|
|
143
|
+
message: mainInfo[5],
|
|
144
|
+
filesChanged,
|
|
145
|
+
insertions,
|
|
146
|
+
deletions,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ============================================
|
|
154
|
+
// BLAME OPERATIONS
|
|
155
|
+
// ============================================
|
|
156
|
+
blame(filePath, options = {}) {
|
|
157
|
+
if (!this.isGitRepo)
|
|
158
|
+
return [];
|
|
159
|
+
const fullPath = path.join(this.repoPath, filePath);
|
|
160
|
+
if (!fs.existsSync(fullPath)) {
|
|
161
|
+
throw new Error(`File not found: ${filePath}`);
|
|
162
|
+
}
|
|
163
|
+
const args = ['blame', '--line-porcelain'];
|
|
164
|
+
if (options.lineStart && options.lineEnd) {
|
|
165
|
+
args.push(`-L ${options.lineStart},${options.lineEnd}`);
|
|
166
|
+
}
|
|
167
|
+
args.push(filePath);
|
|
168
|
+
const output = this.execGit(args);
|
|
169
|
+
const lines = output.split('\n');
|
|
170
|
+
const result = [];
|
|
171
|
+
let currentBlame = {};
|
|
172
|
+
let currentLine = '';
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
if (line.match(/^[0-9a-f]{40} \d+ \d+/)) {
|
|
175
|
+
// New blame entry starts
|
|
176
|
+
if (currentBlame.line !== undefined) {
|
|
177
|
+
result.push(currentBlame);
|
|
178
|
+
}
|
|
179
|
+
const parts = line.split(' ');
|
|
180
|
+
currentBlame = {
|
|
181
|
+
line: parseInt(parts[2]),
|
|
182
|
+
commit: parts[0],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
else if (line.startsWith('author ')) {
|
|
186
|
+
currentBlame.author = line.substring(7);
|
|
187
|
+
}
|
|
188
|
+
else if (line.startsWith('author-time ')) {
|
|
189
|
+
currentBlame.date = new Date(parseInt(line.substring(12)) * 1000);
|
|
190
|
+
}
|
|
191
|
+
else if (line.startsWith('summary ')) {
|
|
192
|
+
currentBlame.summary = line.substring(8);
|
|
193
|
+
}
|
|
194
|
+
else if (line.startsWith('\t')) {
|
|
195
|
+
currentBlame.content = line.substring(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Add the last entry
|
|
199
|
+
if (currentBlame.line !== undefined) {
|
|
200
|
+
result.push(currentBlame);
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
getBlameSummary(filePath) {
|
|
205
|
+
const blame = this.blame(filePath);
|
|
206
|
+
const authorLines = new Map();
|
|
207
|
+
for (const line of blame) {
|
|
208
|
+
const count = authorLines.get(line.author) || 0;
|
|
209
|
+
authorLines.set(line.author, count + 1);
|
|
210
|
+
}
|
|
211
|
+
const total = blame.length;
|
|
212
|
+
return Array.from(authorLines.entries())
|
|
213
|
+
.map(([author, lines]) => ({
|
|
214
|
+
author,
|
|
215
|
+
lines,
|
|
216
|
+
percentage: Math.round((lines / total) * 100),
|
|
217
|
+
}))
|
|
218
|
+
.sort((a, b) => b.lines - a.lines);
|
|
219
|
+
}
|
|
220
|
+
// ============================================
|
|
221
|
+
// DIFF OPERATIONS
|
|
222
|
+
// ============================================
|
|
223
|
+
diff(options = {}) {
|
|
224
|
+
if (!this.isGitRepo)
|
|
225
|
+
return [];
|
|
226
|
+
const args = ['diff', '--no-ext-diff', '-p', '--diff-filter=MADCR'];
|
|
227
|
+
if (options.staged || options.cached)
|
|
228
|
+
args.push('--cached');
|
|
229
|
+
if (options.from && options.to) {
|
|
230
|
+
args.push(`${options.from}..${options.to}`);
|
|
231
|
+
}
|
|
232
|
+
else if (options.from) {
|
|
233
|
+
args.push(options.from);
|
|
234
|
+
}
|
|
235
|
+
if (options.file)
|
|
236
|
+
args.push('--', options.file);
|
|
237
|
+
const output = this.execGit(args);
|
|
238
|
+
return this.parseDiffOutput(output);
|
|
239
|
+
}
|
|
240
|
+
parseDiffOutput(output) {
|
|
241
|
+
const diffs = [];
|
|
242
|
+
const files = output.split('diff --git');
|
|
243
|
+
for (const file of files.slice(1)) {
|
|
244
|
+
const diff = {
|
|
245
|
+
oldFile: '',
|
|
246
|
+
newFile: '',
|
|
247
|
+
hunks: [],
|
|
248
|
+
isBinary: false,
|
|
249
|
+
};
|
|
250
|
+
const lines = file.split('\n');
|
|
251
|
+
for (const line of lines) {
|
|
252
|
+
if (line.startsWith('--- ')) {
|
|
253
|
+
diff.oldFile = line.substring(4).replace(/^a\//, '');
|
|
254
|
+
}
|
|
255
|
+
else if (line.startsWith('+++ ')) {
|
|
256
|
+
diff.newFile = line.substring(4).replace(/^b\//, '');
|
|
257
|
+
}
|
|
258
|
+
else if (line.includes('Binary files')) {
|
|
259
|
+
diff.isBinary = true;
|
|
260
|
+
}
|
|
261
|
+
else if (line.startsWith('@@')) {
|
|
262
|
+
// New hunk
|
|
263
|
+
const match = line.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/);
|
|
264
|
+
if (match) {
|
|
265
|
+
diff.hunks.push({
|
|
266
|
+
oldStart: parseInt(match[1]),
|
|
267
|
+
oldCount: parseInt(match[2]) || 1,
|
|
268
|
+
newStart: parseInt(match[3]),
|
|
269
|
+
newCount: parseInt(match[4]) || 1,
|
|
270
|
+
lines: [],
|
|
271
|
+
header: line,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else if (diff.hunks.length > 0 && line.length > 0) {
|
|
276
|
+
const hunk = diff.hunks[diff.hunks.length - 1];
|
|
277
|
+
const type = line[0] === '+' ? 'added' : line[0] === '-' ? 'removed' : 'context';
|
|
278
|
+
hunk.lines.push({
|
|
279
|
+
type,
|
|
280
|
+
content: line.substring(1),
|
|
281
|
+
oldLine: type !== 'added' ? hunk.oldStart + hunk.lines.filter(l => l.type !== 'added').length - 1 : undefined,
|
|
282
|
+
newLine: type !== 'removed' ? hunk.newStart + hunk.lines.filter(l => l.type !== 'removed').length - 1 : undefined,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
diffs.push(diff);
|
|
287
|
+
}
|
|
288
|
+
return diffs;
|
|
289
|
+
}
|
|
290
|
+
getFileDiff(filePath, fromCommit, toCommit) {
|
|
291
|
+
const diffs = this.diff({
|
|
292
|
+
from: fromCommit,
|
|
293
|
+
to: toCommit,
|
|
294
|
+
file: filePath,
|
|
295
|
+
});
|
|
296
|
+
return diffs[0] || null;
|
|
297
|
+
}
|
|
298
|
+
// ============================================
|
|
299
|
+
// BRANCH OPERATIONS
|
|
300
|
+
// ============================================
|
|
301
|
+
getBranches(options = {}) {
|
|
302
|
+
if (!this.isGitRepo)
|
|
303
|
+
return [];
|
|
304
|
+
const args = ['branch', '-v'];
|
|
305
|
+
if (options.remote)
|
|
306
|
+
args.push('-r');
|
|
307
|
+
if (options.all)
|
|
308
|
+
args.push('-a');
|
|
309
|
+
const output = this.execGit(args);
|
|
310
|
+
const branches = [];
|
|
311
|
+
for (const line of output.split('\n')) {
|
|
312
|
+
if (!line.trim())
|
|
313
|
+
continue;
|
|
314
|
+
const isCurrent = line.startsWith('*');
|
|
315
|
+
const cleanLine = line.replace(/^\*\s*/, '').trim();
|
|
316
|
+
const match = cleanLine.match(/^(\S+)\s+(\S+)\s+\[(.+?)\]\s+(.+)/);
|
|
317
|
+
if (match) {
|
|
318
|
+
branches.push({
|
|
319
|
+
name: match[1],
|
|
320
|
+
isCurrent,
|
|
321
|
+
isRemote: match[1].startsWith('remotes/'),
|
|
322
|
+
upstream: match[3],
|
|
323
|
+
lastCommit: match[4],
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const parts = cleanLine.split(/\s+/);
|
|
328
|
+
branches.push({
|
|
329
|
+
name: parts[0],
|
|
330
|
+
isCurrent,
|
|
331
|
+
isRemote: parts[0].startsWith('remotes/'),
|
|
332
|
+
lastCommit: parts[1] || undefined,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return branches;
|
|
337
|
+
}
|
|
338
|
+
createBranch(name, from) {
|
|
339
|
+
if (!this.isGitRepo)
|
|
340
|
+
return false;
|
|
341
|
+
try {
|
|
342
|
+
const args = ['checkout', '-b', name];
|
|
343
|
+
if (from)
|
|
344
|
+
args.push(from);
|
|
345
|
+
this.execGit(args);
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
checkoutBranch(name) {
|
|
353
|
+
if (!this.isGitRepo)
|
|
354
|
+
return false;
|
|
355
|
+
try {
|
|
356
|
+
this.execGit(['checkout', name]);
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
deleteBranch(name, force = false) {
|
|
364
|
+
if (!this.isGitRepo)
|
|
365
|
+
return false;
|
|
366
|
+
try {
|
|
367
|
+
const args = ['branch', force ? '-D' : '-d', name];
|
|
368
|
+
this.execGit(args);
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
mergeBranch(source, target, options = {}) {
|
|
376
|
+
if (!this.isGitRepo)
|
|
377
|
+
return false;
|
|
378
|
+
try {
|
|
379
|
+
// Checkout target if specified
|
|
380
|
+
if (target) {
|
|
381
|
+
this.checkoutBranch(target);
|
|
382
|
+
}
|
|
383
|
+
const args = ['merge'];
|
|
384
|
+
if (options.noFastForward)
|
|
385
|
+
args.push('--no-ff');
|
|
386
|
+
if (options.squash)
|
|
387
|
+
args.push('--squash');
|
|
388
|
+
args.push(source);
|
|
389
|
+
this.execGit(args);
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
rebaseBranch(upstream, branch) {
|
|
397
|
+
if (!this.isGitRepo)
|
|
398
|
+
return false;
|
|
399
|
+
try {
|
|
400
|
+
if (branch) {
|
|
401
|
+
this.checkoutBranch(branch);
|
|
402
|
+
}
|
|
403
|
+
this.execGit(['rebase', upstream]);
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// ============================================
|
|
411
|
+
// STATUS OPERATIONS
|
|
412
|
+
// ============================================
|
|
413
|
+
getStatus() {
|
|
414
|
+
if (!this.isGitRepo)
|
|
415
|
+
return [];
|
|
416
|
+
const output = this.execGit(['status', '--porcelain', '-u']);
|
|
417
|
+
if (!output)
|
|
418
|
+
return [];
|
|
419
|
+
return output.split('\n').map(line => {
|
|
420
|
+
const staged = line[0] !== ' ' && line[0] !== '?';
|
|
421
|
+
const unstaged = line[1] !== ' ';
|
|
422
|
+
const statusMap = {
|
|
423
|
+
'M': 'modified',
|
|
424
|
+
'A': 'added',
|
|
425
|
+
'D': 'deleted',
|
|
426
|
+
'R': 'renamed',
|
|
427
|
+
'C': 'copied',
|
|
428
|
+
'?': 'untracked',
|
|
429
|
+
'!': 'ignored',
|
|
430
|
+
};
|
|
431
|
+
const code = staged ? line[0] : line[1];
|
|
432
|
+
let filePath = line.substring(3).trim();
|
|
433
|
+
// Handle renamed files (R100 old -> new)
|
|
434
|
+
let originalPath;
|
|
435
|
+
if (code === 'R' && filePath.includes(' -> ')) {
|
|
436
|
+
const parts = filePath.split(' -> ');
|
|
437
|
+
originalPath = parts[0];
|
|
438
|
+
filePath = parts[1];
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
path: filePath,
|
|
442
|
+
status: statusMap[code] || 'modified',
|
|
443
|
+
staged,
|
|
444
|
+
originalPath,
|
|
445
|
+
};
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
getUntrackedFiles() {
|
|
449
|
+
return this.getStatus()
|
|
450
|
+
.filter(s => s.status === 'untracked')
|
|
451
|
+
.map(s => s.path);
|
|
452
|
+
}
|
|
453
|
+
getModifiedFiles() {
|
|
454
|
+
return this.getStatus()
|
|
455
|
+
.filter(s => s.status === 'modified' || s.status === 'added')
|
|
456
|
+
.map(s => s.path);
|
|
457
|
+
}
|
|
458
|
+
// ============================================
|
|
459
|
+
// STASH OPERATIONS
|
|
460
|
+
// ============================================
|
|
461
|
+
getStashes() {
|
|
462
|
+
if (!this.isGitRepo)
|
|
463
|
+
return [];
|
|
464
|
+
const output = this.execGit(['stash', 'list', '--format=%H|%gd|%gs']);
|
|
465
|
+
if (!output)
|
|
466
|
+
return [];
|
|
467
|
+
return output.split('\n').map((line, index) => {
|
|
468
|
+
const parts = line.split('|');
|
|
469
|
+
return {
|
|
470
|
+
index,
|
|
471
|
+
message: parts[2] || parts[1],
|
|
472
|
+
branch: '',
|
|
473
|
+
hash: parts[0],
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
stash(message, options = {}) {
|
|
478
|
+
if (!this.isGitRepo)
|
|
479
|
+
return false;
|
|
480
|
+
try {
|
|
481
|
+
const args = ['stash', 'push'];
|
|
482
|
+
if (message)
|
|
483
|
+
args.push('-m', message);
|
|
484
|
+
if (options.includeUntracked)
|
|
485
|
+
args.push('-u');
|
|
486
|
+
if (options.keepIndex)
|
|
487
|
+
args.push('--keep-index');
|
|
488
|
+
this.execGit(args);
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
stashPop(index = 0) {
|
|
496
|
+
if (!this.isGitRepo)
|
|
497
|
+
return false;
|
|
498
|
+
try {
|
|
499
|
+
this.execGit(['stash', 'pop', `stash@{${index}}`]);
|
|
500
|
+
return true;
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
stashApply(index = 0) {
|
|
507
|
+
if (!this.isGitRepo)
|
|
508
|
+
return false;
|
|
509
|
+
try {
|
|
510
|
+
this.execGit(['stash', 'apply', `stash@{${index}}`]);
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
stashDrop(index = 0) {
|
|
518
|
+
if (!this.isGitRepo)
|
|
519
|
+
return false;
|
|
520
|
+
try {
|
|
521
|
+
this.execGit(['stash', 'drop', `stash@{${index}}`]);
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// ============================================
|
|
529
|
+
// TAG OPERATIONS
|
|
530
|
+
// ============================================
|
|
531
|
+
getTags() {
|
|
532
|
+
if (!this.isGitRepo)
|
|
533
|
+
return [];
|
|
534
|
+
const output = this.execGit(['tag', '-l', '-n1']);
|
|
535
|
+
if (!output)
|
|
536
|
+
return [];
|
|
537
|
+
return output.split('\n').map(line => {
|
|
538
|
+
const parts = line.trim().split(/\s+/);
|
|
539
|
+
return {
|
|
540
|
+
name: parts[0],
|
|
541
|
+
message: parts.slice(1).join(' ') || undefined,
|
|
542
|
+
hash: '',
|
|
543
|
+
};
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
createTag(name, message, commit) {
|
|
547
|
+
if (!this.isGitRepo)
|
|
548
|
+
return false;
|
|
549
|
+
try {
|
|
550
|
+
const args = ['tag'];
|
|
551
|
+
if (message)
|
|
552
|
+
args.push('-a', '-m', message);
|
|
553
|
+
args.push(name);
|
|
554
|
+
if (commit)
|
|
555
|
+
args.push(commit);
|
|
556
|
+
this.execGit(args);
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
deleteTag(name) {
|
|
564
|
+
if (!this.isGitRepo)
|
|
565
|
+
return false;
|
|
566
|
+
try {
|
|
567
|
+
this.execGit(['tag', '-d', name]);
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
pushTag(name, remote = 'origin') {
|
|
575
|
+
if (!this.isGitRepo)
|
|
576
|
+
return false;
|
|
577
|
+
try {
|
|
578
|
+
this.execGit(['push', remote, name]);
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// ============================================
|
|
586
|
+
// REMOTE OPERATIONS
|
|
587
|
+
// ============================================
|
|
588
|
+
getRemotes() {
|
|
589
|
+
if (!this.isGitRepo)
|
|
590
|
+
return [];
|
|
591
|
+
try {
|
|
592
|
+
const output = this.execGit(['remote', '-v']);
|
|
593
|
+
const remotes = [];
|
|
594
|
+
for (const line of output.split('\n')) {
|
|
595
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+\((\w+)\)/);
|
|
596
|
+
if (match) {
|
|
597
|
+
remotes.push({
|
|
598
|
+
name: match[1],
|
|
599
|
+
url: match[2],
|
|
600
|
+
fetch: match[3],
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return remotes;
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
return [];
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
fetch(remote, branch) {
|
|
611
|
+
if (!this.isGitRepo)
|
|
612
|
+
return false;
|
|
613
|
+
try {
|
|
614
|
+
const args = ['fetch'];
|
|
615
|
+
if (remote)
|
|
616
|
+
args.push(remote);
|
|
617
|
+
if (branch)
|
|
618
|
+
args.push(branch);
|
|
619
|
+
this.execGit(args);
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
622
|
+
catch {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
pull(remote, branch, options = {}) {
|
|
627
|
+
if (!this.isGitRepo)
|
|
628
|
+
return false;
|
|
629
|
+
try {
|
|
630
|
+
const args = ['pull'];
|
|
631
|
+
if (options.rebase)
|
|
632
|
+
args.push('--rebase');
|
|
633
|
+
if (remote)
|
|
634
|
+
args.push(remote);
|
|
635
|
+
if (branch)
|
|
636
|
+
args.push(branch);
|
|
637
|
+
this.execGit(args);
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
catch {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
push(remote, branch, options = {}) {
|
|
645
|
+
if (!this.isGitRepo)
|
|
646
|
+
return false;
|
|
647
|
+
try {
|
|
648
|
+
const args = ['push'];
|
|
649
|
+
if (options.force)
|
|
650
|
+
args.push('--force-with-lease');
|
|
651
|
+
if (options.setUpstream)
|
|
652
|
+
args.push('-u');
|
|
653
|
+
if (remote)
|
|
654
|
+
args.push(remote);
|
|
655
|
+
if (branch)
|
|
656
|
+
args.push(branch);
|
|
657
|
+
this.execGit(args);
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
catch {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// ============================================
|
|
665
|
+
// ADVANCED ANALYSIS
|
|
666
|
+
// ============================================
|
|
667
|
+
getContributors() {
|
|
668
|
+
if (!this.isGitRepo)
|
|
669
|
+
return [];
|
|
670
|
+
try {
|
|
671
|
+
const output = this.execGit(['shortlog', '-sne', '--all']);
|
|
672
|
+
const contributors = [];
|
|
673
|
+
for (const line of output.split('\n')) {
|
|
674
|
+
const match = line.match(/^\s*(\d+)\s+(.+?)\s+<(.+?)>$/);
|
|
675
|
+
if (match) {
|
|
676
|
+
contributors.push({
|
|
677
|
+
name: match[2],
|
|
678
|
+
email: match[3],
|
|
679
|
+
commits: parseInt(match[1]),
|
|
680
|
+
linesAdded: 0,
|
|
681
|
+
linesDeleted: 0,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return contributors;
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
return [];
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
getCodeChurn(filePath, since) {
|
|
692
|
+
if (!this.isGitRepo)
|
|
693
|
+
return [];
|
|
694
|
+
const args = ['log', '--format=%ad', '--date=short', '--numstat'];
|
|
695
|
+
if (since)
|
|
696
|
+
args.push(`--since=${since.toISOString()}`);
|
|
697
|
+
if (filePath)
|
|
698
|
+
args.push('--follow', '--', filePath);
|
|
699
|
+
const output = this.execGit(args);
|
|
700
|
+
const churn = new Map();
|
|
701
|
+
let currentDate = '';
|
|
702
|
+
for (const line of output.split('\n')) {
|
|
703
|
+
if (line.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
704
|
+
currentDate = line;
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
const match = line.match(/(\d+)\s+(\d+)\s+/);
|
|
708
|
+
if (match && currentDate) {
|
|
709
|
+
const existing = churn.get(currentDate) || { insertions: 0, deletions: 0 };
|
|
710
|
+
existing.insertions += parseInt(match[1]) || 0;
|
|
711
|
+
existing.deletions += parseInt(match[2]) || 0;
|
|
712
|
+
churn.set(currentDate, existing);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return Array.from(churn.entries())
|
|
717
|
+
.map(([date, stats]) => ({ date, ...stats }))
|
|
718
|
+
.sort((a, b) => a.date.localeCompare(b.date));
|
|
719
|
+
}
|
|
720
|
+
getFileHistory(filePath) {
|
|
721
|
+
return this.getLog({ file: filePath, maxCount: 50 });
|
|
722
|
+
}
|
|
723
|
+
findBugs(filePath) {
|
|
724
|
+
const blame = this.blame(filePath);
|
|
725
|
+
// Find lines that might contain bugs based on keywords
|
|
726
|
+
const bugKeywords = ['TODO', 'FIXME', 'HACK', 'BUG', 'XXX', 'temporary', 'workaround'];
|
|
727
|
+
return blame.filter(line => bugKeywords.some(keyword => line.content.toLowerCase().includes(keyword.toLowerCase())));
|
|
728
|
+
}
|
|
729
|
+
getRepositoryStats() {
|
|
730
|
+
if (!this.isGitRepo) {
|
|
731
|
+
return {
|
|
732
|
+
totalCommits: 0,
|
|
733
|
+
totalFiles: 0,
|
|
734
|
+
branches: 0,
|
|
735
|
+
tags: 0,
|
|
736
|
+
contributors: 0,
|
|
737
|
+
linesOfCode: 0,
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
const totalCommits = parseInt(this.execGit(['rev-list', '--count', 'HEAD'])) || 0;
|
|
742
|
+
const totalFiles = this.execGit(['ls-files']).split('\n').length;
|
|
743
|
+
const branches = this.getBranches({ all: true }).length;
|
|
744
|
+
const tags = this.getTags().length;
|
|
745
|
+
const contributors = this.getContributors().length;
|
|
746
|
+
// Count lines of code (simplified)
|
|
747
|
+
let linesOfCode = 0;
|
|
748
|
+
try {
|
|
749
|
+
const files = this.execGit(['ls-files']).split('\n');
|
|
750
|
+
for (const file of files.slice(0, 100)) { // Sample first 100 files
|
|
751
|
+
if (file.match(/\.(ts|js|py|java|go|rs|cpp|c|cs|tsx|jsx|php|rb|swift|kt)$/)) {
|
|
752
|
+
linesOfCode += 100; // Rough estimate
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
linesOfCode = Math.round(linesOfCode * (totalFiles / Math.min(totalFiles, 100)));
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
linesOfCode = totalFiles * 50; // Fallback estimate
|
|
759
|
+
}
|
|
760
|
+
return {
|
|
761
|
+
totalCommits,
|
|
762
|
+
totalFiles,
|
|
763
|
+
branches,
|
|
764
|
+
tags,
|
|
765
|
+
contributors,
|
|
766
|
+
linesOfCode,
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
catch {
|
|
770
|
+
return {
|
|
771
|
+
totalCommits: 0,
|
|
772
|
+
totalFiles: 0,
|
|
773
|
+
branches: 0,
|
|
774
|
+
tags: 0,
|
|
775
|
+
contributors: 0,
|
|
776
|
+
linesOfCode: 0,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// ============================================
|
|
781
|
+
// WORKTREE OPERATIONS
|
|
782
|
+
// ============================================
|
|
783
|
+
getWorktrees() {
|
|
784
|
+
if (!this.isGitRepo)
|
|
785
|
+
return [];
|
|
786
|
+
try {
|
|
787
|
+
const output = this.execGit(['worktree', 'list', '--porcelain']);
|
|
788
|
+
const worktrees = [];
|
|
789
|
+
let current = {};
|
|
790
|
+
for (const line of output.split('\n')) {
|
|
791
|
+
if (line.startsWith('worktree ')) {
|
|
792
|
+
if (current.path)
|
|
793
|
+
worktrees.push(current);
|
|
794
|
+
current = { path: line.substring(9), isMain: false, isLocked: false };
|
|
795
|
+
}
|
|
796
|
+
else if (line.startsWith('HEAD ')) {
|
|
797
|
+
current.commit = line.substring(5);
|
|
798
|
+
}
|
|
799
|
+
else if (line.startsWith('branch ')) {
|
|
800
|
+
current.branch = line.substring(7).replace('refs/heads/', '');
|
|
801
|
+
}
|
|
802
|
+
else if (line === 'bare') {
|
|
803
|
+
current.isMain = true;
|
|
804
|
+
}
|
|
805
|
+
else if (line === 'locked') {
|
|
806
|
+
current.isLocked = true;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (current.path)
|
|
810
|
+
worktrees.push(current);
|
|
811
|
+
return worktrees;
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
return [];
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
createWorktree(path, branch) {
|
|
818
|
+
if (!this.isGitRepo)
|
|
819
|
+
return false;
|
|
820
|
+
try {
|
|
821
|
+
const args = ['worktree', 'add'];
|
|
822
|
+
if (branch)
|
|
823
|
+
args.push('-b', branch);
|
|
824
|
+
args.push(path);
|
|
825
|
+
this.execGit(args);
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
catch {
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
removeWorktree(path, force = false) {
|
|
833
|
+
if (!this.isGitRepo)
|
|
834
|
+
return false;
|
|
835
|
+
try {
|
|
836
|
+
const args = ['worktree', 'remove'];
|
|
837
|
+
if (force)
|
|
838
|
+
args.push('--force');
|
|
839
|
+
args.push(path);
|
|
840
|
+
this.execGit(args);
|
|
841
|
+
return true;
|
|
842
|
+
}
|
|
843
|
+
catch {
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
exports.GitIntegration = GitIntegration;
|
|
849
|
+
exports.default = GitIntegration;
|
|
850
|
+
//# sourceMappingURL=git-integration.js.map
|