codmir 0.4.0 → 0.6.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/dist/{chunk-EBO3CZXG.mjs → chunk-3RG5ZIWI.js} +1 -6
- package/dist/chunk-5XZIEJ3P.js +231 -0
- package/dist/chunk-BQMZNWYV.js +295 -0
- package/dist/context/index.js +9 -0
- package/dist/index.js +10046 -302
- package/dist/sync/index.js +7 -0
- package/package.json +52 -74
- package/LICENSE +0 -55
- package/README.md +0 -547
- package/dist/analyze-LULBI4ZC.mjs +0 -7
- package/dist/chunk-ASGAT3Z5.mjs +0 -756
- package/dist/chunk-GU32P57R.mjs +0 -343
- package/dist/cli/index.d.mts +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +0 -2607
- package/dist/cli/index.mjs +0 -1472
- package/dist/index.d.mts +0 -295
- package/dist/index.d.ts +0 -295
- package/dist/index.mjs +0 -7
- package/dist/voice-agent/index.d.mts +0 -134
- package/dist/voice-agent/index.d.ts +0 -134
- package/dist/voice-agent/index.js +0 -220
- package/dist/voice-agent/index.mjs +0 -187
- package/dist/voice-daemon/index.d.mts +0 -354
- package/dist/voice-daemon/index.d.ts +0 -354
- package/dist/voice-daemon/index.js +0 -1089
- package/dist/voice-daemon/index.mjs +0 -1046
- package/runkit-example.js +0 -36
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
1
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
2
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
3
|
}) : x)(function(x) {
|
|
5
4
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
6
|
});
|
|
8
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
9
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
-
};
|
|
11
7
|
|
|
12
8
|
export {
|
|
13
|
-
__require
|
|
14
|
-
__commonJS
|
|
9
|
+
__require
|
|
15
10
|
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// src/sync/cloud-sync.ts
|
|
2
|
+
import WebSocket from "ws";
|
|
3
|
+
var WebSocketCloudSync = class {
|
|
4
|
+
config;
|
|
5
|
+
ws = null;
|
|
6
|
+
messageHandlers = /* @__PURE__ */ new Set();
|
|
7
|
+
pendingMessages = [];
|
|
8
|
+
reconnectAttempts = 0;
|
|
9
|
+
maxReconnectAttempts = 5;
|
|
10
|
+
heartbeatInterval = null;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
async connect() {
|
|
15
|
+
if (!this.config.enabled || !this.config.apiUrl) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const wsUrl = this.config.apiUrl.replace("https://", "wss://").replace("http://", "ws://");
|
|
20
|
+
const url = `${wsUrl}/api/agent/ws?apiKey=${this.config.apiKey || ""}`;
|
|
21
|
+
this.ws = new WebSocket(url);
|
|
22
|
+
this.ws.on("open", () => {
|
|
23
|
+
console.log("[CloudSync] Connected to Codmir cloud");
|
|
24
|
+
this.reconnectAttempts = 0;
|
|
25
|
+
for (const msg of this.pendingMessages) {
|
|
26
|
+
this.emitEvent(msg);
|
|
27
|
+
}
|
|
28
|
+
this.pendingMessages = [];
|
|
29
|
+
this.startHeartbeat();
|
|
30
|
+
resolve();
|
|
31
|
+
});
|
|
32
|
+
this.ws.on("message", (data) => {
|
|
33
|
+
try {
|
|
34
|
+
const message = JSON.parse(data.toString());
|
|
35
|
+
for (const handler of this.messageHandlers) {
|
|
36
|
+
handler(message);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error("[CloudSync] Failed to parse message:", error);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
this.ws.on("close", () => {
|
|
43
|
+
console.log("[CloudSync] Disconnected from cloud");
|
|
44
|
+
this.stopHeartbeat();
|
|
45
|
+
this.attemptReconnect();
|
|
46
|
+
});
|
|
47
|
+
this.ws.on("error", (error) => {
|
|
48
|
+
console.error("[CloudSync] WebSocket error:", error.message);
|
|
49
|
+
reject(error);
|
|
50
|
+
});
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
if (this.ws?.readyState !== WebSocket.OPEN) {
|
|
53
|
+
reject(new Error("Connection timeout"));
|
|
54
|
+
}
|
|
55
|
+
}, 1e4);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
startHeartbeat() {
|
|
59
|
+
this.heartbeatInterval = setInterval(() => {
|
|
60
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
61
|
+
this.ws.ping();
|
|
62
|
+
}
|
|
63
|
+
}, 3e4);
|
|
64
|
+
}
|
|
65
|
+
stopHeartbeat() {
|
|
66
|
+
if (this.heartbeatInterval) {
|
|
67
|
+
clearInterval(this.heartbeatInterval);
|
|
68
|
+
this.heartbeatInterval = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
attemptReconnect() {
|
|
72
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
73
|
+
console.log("[CloudSync] Max reconnect attempts reached");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.reconnectAttempts++;
|
|
77
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
|
|
78
|
+
console.log(`[CloudSync] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
this.connect().catch(() => {
|
|
81
|
+
});
|
|
82
|
+
}, delay);
|
|
83
|
+
}
|
|
84
|
+
disconnect() {
|
|
85
|
+
this.stopHeartbeat();
|
|
86
|
+
if (this.ws) {
|
|
87
|
+
this.ws.close();
|
|
88
|
+
this.ws = null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async createTask(session) {
|
|
92
|
+
const task = {
|
|
93
|
+
id: session.taskId || session.id,
|
|
94
|
+
userId: this.config.userId || "anonymous",
|
|
95
|
+
projectId: this.config.projectId,
|
|
96
|
+
title: session.prompt.slice(0, 100),
|
|
97
|
+
description: session.prompt,
|
|
98
|
+
status: session.status,
|
|
99
|
+
source: "cli",
|
|
100
|
+
workspaceRoot: session.workspaceRoot,
|
|
101
|
+
steps: session.steps,
|
|
102
|
+
files: session.files,
|
|
103
|
+
createdAt: new Date(session.createdAt).toISOString(),
|
|
104
|
+
updatedAt: new Date(session.updatedAt).toISOString()
|
|
105
|
+
};
|
|
106
|
+
if (this.ws?.readyState !== WebSocket.OPEN) {
|
|
107
|
+
return this.createTaskREST(task);
|
|
108
|
+
}
|
|
109
|
+
this.ws.send(JSON.stringify({
|
|
110
|
+
type: "task.create",
|
|
111
|
+
task
|
|
112
|
+
}));
|
|
113
|
+
return task;
|
|
114
|
+
}
|
|
115
|
+
async createTaskREST(task) {
|
|
116
|
+
if (!this.config.apiUrl) {
|
|
117
|
+
return task;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const response = await fetch(`${this.config.apiUrl}/api/tasks`, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
"Content-Type": "application/json",
|
|
124
|
+
"Authorization": `Bearer ${this.config.apiKey}`
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify(task)
|
|
127
|
+
});
|
|
128
|
+
if (response.ok) {
|
|
129
|
+
return await response.json();
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error("[CloudSync] Failed to create task via REST:", error);
|
|
133
|
+
}
|
|
134
|
+
return task;
|
|
135
|
+
}
|
|
136
|
+
async updateTask(taskId, updates) {
|
|
137
|
+
const payload = {
|
|
138
|
+
type: "task.update",
|
|
139
|
+
taskId,
|
|
140
|
+
updates: {
|
|
141
|
+
status: updates.status,
|
|
142
|
+
steps: updates.steps,
|
|
143
|
+
files: updates.files,
|
|
144
|
+
error: updates.error,
|
|
145
|
+
result: updates.result,
|
|
146
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
147
|
+
completedAt: updates.completedAt ? new Date(updates.completedAt).toISOString() : void 0
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
151
|
+
this.ws.send(JSON.stringify(payload));
|
|
152
|
+
} else {
|
|
153
|
+
this.updateTaskREST(taskId, payload.updates).catch(() => {
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async updateTaskREST(taskId, updates) {
|
|
158
|
+
if (!this.config.apiUrl) return;
|
|
159
|
+
try {
|
|
160
|
+
await fetch(`${this.config.apiUrl}/api/tasks/${taskId}`, {
|
|
161
|
+
method: "PATCH",
|
|
162
|
+
headers: {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
"Authorization": `Bearer ${this.config.apiKey}`
|
|
165
|
+
},
|
|
166
|
+
body: JSON.stringify(updates)
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("[CloudSync] Failed to update task via REST:", error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
emitEvent(event) {
|
|
173
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
174
|
+
this.ws.send(JSON.stringify({
|
|
175
|
+
type: "event",
|
|
176
|
+
event
|
|
177
|
+
}));
|
|
178
|
+
} else {
|
|
179
|
+
this.pendingMessages.push(event);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
onMessage(handler) {
|
|
183
|
+
this.messageHandlers.add(handler);
|
|
184
|
+
return () => this.messageHandlers.delete(handler);
|
|
185
|
+
}
|
|
186
|
+
isConnected() {
|
|
187
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var NoopCloudSync = class {
|
|
191
|
+
async connect() {
|
|
192
|
+
}
|
|
193
|
+
disconnect() {
|
|
194
|
+
}
|
|
195
|
+
async createTask(session) {
|
|
196
|
+
return {
|
|
197
|
+
id: session.id,
|
|
198
|
+
userId: "local",
|
|
199
|
+
title: session.prompt.slice(0, 100),
|
|
200
|
+
description: session.prompt,
|
|
201
|
+
status: session.status,
|
|
202
|
+
source: "cli",
|
|
203
|
+
workspaceRoot: session.workspaceRoot,
|
|
204
|
+
steps: session.steps,
|
|
205
|
+
files: session.files,
|
|
206
|
+
createdAt: new Date(session.createdAt).toISOString(),
|
|
207
|
+
updatedAt: new Date(session.updatedAt).toISOString()
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async updateTask() {
|
|
211
|
+
}
|
|
212
|
+
emitEvent() {
|
|
213
|
+
}
|
|
214
|
+
onMessage() {
|
|
215
|
+
return () => {
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
isConnected() {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
function createCloudSync(config) {
|
|
223
|
+
if (!config?.enabled) {
|
|
224
|
+
return new NoopCloudSync();
|
|
225
|
+
}
|
|
226
|
+
return new WebSocketCloudSync(config);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export {
|
|
230
|
+
createCloudSync
|
|
231
|
+
};
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
// src/context/local-context-provider.ts
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { spawn, exec } from "child_process";
|
|
5
|
+
import { promisify } from "util";
|
|
6
|
+
import { glob } from "glob";
|
|
7
|
+
var execPromise = promisify(exec);
|
|
8
|
+
var LocalContextProvider = class {
|
|
9
|
+
workspaceRoot;
|
|
10
|
+
askUserHandler;
|
|
11
|
+
notificationHandler;
|
|
12
|
+
constructor(workspaceRoot, options) {
|
|
13
|
+
this.workspaceRoot = path.resolve(workspaceRoot);
|
|
14
|
+
this.askUserHandler = options?.askUserHandler;
|
|
15
|
+
this.notificationHandler = options?.notificationHandler;
|
|
16
|
+
}
|
|
17
|
+
getWorkspaceRoot() {
|
|
18
|
+
return this.workspaceRoot;
|
|
19
|
+
}
|
|
20
|
+
resolvePath(relativePath) {
|
|
21
|
+
const resolved = path.resolve(this.workspaceRoot, relativePath);
|
|
22
|
+
if (!resolved.startsWith(this.workspaceRoot)) {
|
|
23
|
+
throw new Error(`Path escapes workspace: ${relativePath}`);
|
|
24
|
+
}
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
async readFile(filePath, options) {
|
|
28
|
+
const fullPath = this.resolvePath(filePath);
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
31
|
+
const lines = content.split("\n");
|
|
32
|
+
const lineCount = lines.length;
|
|
33
|
+
let resultContent = content;
|
|
34
|
+
let truncated = false;
|
|
35
|
+
if (options?.startLine || options?.endLine) {
|
|
36
|
+
const start = (options.startLine || 1) - 1;
|
|
37
|
+
const end = options.endLine || lines.length;
|
|
38
|
+
resultContent = lines.slice(start, end).join("\n");
|
|
39
|
+
truncated = start > 0 || end < lines.length;
|
|
40
|
+
}
|
|
41
|
+
if (options?.maxLength && resultContent.length > options.maxLength) {
|
|
42
|
+
resultContent = resultContent.slice(0, options.maxLength);
|
|
43
|
+
truncated = true;
|
|
44
|
+
}
|
|
45
|
+
const ext = path.extname(fullPath).slice(1);
|
|
46
|
+
const languageMap = {
|
|
47
|
+
ts: "typescript",
|
|
48
|
+
tsx: "typescript",
|
|
49
|
+
js: "javascript",
|
|
50
|
+
jsx: "javascript",
|
|
51
|
+
py: "python",
|
|
52
|
+
rb: "ruby",
|
|
53
|
+
go: "go",
|
|
54
|
+
rs: "rust",
|
|
55
|
+
java: "java",
|
|
56
|
+
c: "c",
|
|
57
|
+
cpp: "cpp",
|
|
58
|
+
h: "c",
|
|
59
|
+
hpp: "cpp",
|
|
60
|
+
cs: "csharp",
|
|
61
|
+
php: "php",
|
|
62
|
+
swift: "swift",
|
|
63
|
+
kt: "kotlin",
|
|
64
|
+
scala: "scala",
|
|
65
|
+
sh: "bash",
|
|
66
|
+
bash: "bash",
|
|
67
|
+
zsh: "bash",
|
|
68
|
+
fish: "fish",
|
|
69
|
+
ps1: "powershell",
|
|
70
|
+
sql: "sql",
|
|
71
|
+
json: "json",
|
|
72
|
+
yaml: "yaml",
|
|
73
|
+
yml: "yaml",
|
|
74
|
+
xml: "xml",
|
|
75
|
+
html: "html",
|
|
76
|
+
css: "css",
|
|
77
|
+
scss: "scss",
|
|
78
|
+
less: "less",
|
|
79
|
+
md: "markdown",
|
|
80
|
+
txt: "plaintext"
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
content: resultContent,
|
|
84
|
+
path: filePath,
|
|
85
|
+
language: languageMap[ext] || "plaintext",
|
|
86
|
+
lineCount,
|
|
87
|
+
truncated
|
|
88
|
+
};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error.code === "ENOENT") {
|
|
91
|
+
throw new Error(`File not found: ${filePath}`);
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async writeFile(filePath, content) {
|
|
97
|
+
const fullPath = this.resolvePath(filePath);
|
|
98
|
+
const dir = path.dirname(fullPath);
|
|
99
|
+
await fs.mkdir(dir, { recursive: true });
|
|
100
|
+
await fs.writeFile(fullPath, content, "utf-8");
|
|
101
|
+
}
|
|
102
|
+
async deleteFile(filePath) {
|
|
103
|
+
const fullPath = this.resolvePath(filePath);
|
|
104
|
+
await fs.unlink(fullPath);
|
|
105
|
+
}
|
|
106
|
+
async listDirectory(dirPath) {
|
|
107
|
+
const fullPath = this.resolvePath(dirPath);
|
|
108
|
+
const entries = await fs.readdir(fullPath, { withFileTypes: true });
|
|
109
|
+
const result = [];
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
const entryPath = path.join(dirPath, entry.name);
|
|
112
|
+
const stat2 = await fs.stat(path.join(fullPath, entry.name)).catch(() => null);
|
|
113
|
+
result.push({
|
|
114
|
+
name: entry.name,
|
|
115
|
+
path: entryPath,
|
|
116
|
+
type: entry.isDirectory() ? "directory" : "file",
|
|
117
|
+
size: stat2?.size
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
async fileExists(filePath) {
|
|
123
|
+
const fullPath = this.resolvePath(filePath);
|
|
124
|
+
try {
|
|
125
|
+
await fs.access(fullPath);
|
|
126
|
+
return true;
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async searchFiles(query, options) {
|
|
132
|
+
const results = [];
|
|
133
|
+
const maxResults = options?.maxResults || 100;
|
|
134
|
+
const includePatterns = options?.include || ["**/*"];
|
|
135
|
+
const excludePatterns = options?.exclude || ["**/node_modules/**", "**/.git/**", "**/dist/**"];
|
|
136
|
+
for (const pattern of includePatterns) {
|
|
137
|
+
const files = await glob(pattern, {
|
|
138
|
+
cwd: this.workspaceRoot,
|
|
139
|
+
ignore: excludePatterns,
|
|
140
|
+
nodir: true,
|
|
141
|
+
absolute: false
|
|
142
|
+
});
|
|
143
|
+
for (const file of files) {
|
|
144
|
+
if (results.length >= maxResults) break;
|
|
145
|
+
try {
|
|
146
|
+
const content = await this.readFile(file);
|
|
147
|
+
const lines = content.content.split("\n");
|
|
148
|
+
const regex = new RegExp(query, options?.caseSensitive ? "g" : "gi");
|
|
149
|
+
for (let i = 0; i < lines.length; i++) {
|
|
150
|
+
const line = lines[i];
|
|
151
|
+
const matches = line.matchAll(regex);
|
|
152
|
+
for (const match of matches) {
|
|
153
|
+
if (results.length >= maxResults) break;
|
|
154
|
+
results.push({
|
|
155
|
+
path: file,
|
|
156
|
+
line: i + 1,
|
|
157
|
+
column: (match.index || 0) + 1,
|
|
158
|
+
match: match[0],
|
|
159
|
+
context: line.trim()
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return results;
|
|
168
|
+
}
|
|
169
|
+
async runCommand(command, options) {
|
|
170
|
+
const cwd = options?.cwd ? this.resolvePath(options.cwd) : this.workspaceRoot;
|
|
171
|
+
const timeout = options?.timeout || 3e4;
|
|
172
|
+
const startTime = Date.now();
|
|
173
|
+
return new Promise((resolve2, reject) => {
|
|
174
|
+
const proc = spawn("bash", ["-c", command], {
|
|
175
|
+
cwd,
|
|
176
|
+
env: { ...process.env, ...options?.env },
|
|
177
|
+
timeout
|
|
178
|
+
});
|
|
179
|
+
let stdout = "";
|
|
180
|
+
let stderr = "";
|
|
181
|
+
proc.stdout.on("data", (data) => {
|
|
182
|
+
stdout += data.toString();
|
|
183
|
+
});
|
|
184
|
+
proc.stderr.on("data", (data) => {
|
|
185
|
+
stderr += data.toString();
|
|
186
|
+
});
|
|
187
|
+
proc.on("close", (code) => {
|
|
188
|
+
resolve2({
|
|
189
|
+
exitCode: code ?? 0,
|
|
190
|
+
stdout,
|
|
191
|
+
stderr,
|
|
192
|
+
duration: Date.now() - startTime
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
proc.on("error", (error) => {
|
|
196
|
+
reject(error);
|
|
197
|
+
});
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
proc.kill("SIGTERM");
|
|
200
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
201
|
+
}, timeout);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
async getGitStatus() {
|
|
205
|
+
try {
|
|
206
|
+
const branchResult = await execPromise("git rev-parse --abbrev-ref HEAD", {
|
|
207
|
+
cwd: this.workspaceRoot
|
|
208
|
+
});
|
|
209
|
+
const branch = branchResult.stdout.trim();
|
|
210
|
+
const statusResult = await execPromise("git status --porcelain", {
|
|
211
|
+
cwd: this.workspaceRoot
|
|
212
|
+
});
|
|
213
|
+
const staged = [];
|
|
214
|
+
const modified = [];
|
|
215
|
+
const untracked = [];
|
|
216
|
+
const deleted = [];
|
|
217
|
+
for (const line of statusResult.stdout.split("\n")) {
|
|
218
|
+
if (!line) continue;
|
|
219
|
+
const status = line.substring(0, 2);
|
|
220
|
+
const file = line.substring(3);
|
|
221
|
+
if (status.includes("?")) {
|
|
222
|
+
untracked.push(file);
|
|
223
|
+
} else if (status.includes("D")) {
|
|
224
|
+
deleted.push(file);
|
|
225
|
+
} else if (status[0] !== " ") {
|
|
226
|
+
staged.push(file);
|
|
227
|
+
} else if (status[1] !== " ") {
|
|
228
|
+
modified.push(file);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
let ahead = 0;
|
|
232
|
+
let behind = 0;
|
|
233
|
+
try {
|
|
234
|
+
const trackingResult = await execPromise(
|
|
235
|
+
"git rev-list --left-right --count HEAD...@{upstream}",
|
|
236
|
+
{ cwd: this.workspaceRoot }
|
|
237
|
+
);
|
|
238
|
+
const [a, b] = trackingResult.stdout.trim().split(" ").map(Number);
|
|
239
|
+
ahead = a || 0;
|
|
240
|
+
behind = b || 0;
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
return { branch, ahead, behind, staged, modified, untracked, deleted };
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return {
|
|
246
|
+
branch: "unknown",
|
|
247
|
+
ahead: 0,
|
|
248
|
+
behind: 0,
|
|
249
|
+
staged: [],
|
|
250
|
+
modified: [],
|
|
251
|
+
untracked: [],
|
|
252
|
+
deleted: []
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async getGitDiff(options) {
|
|
257
|
+
let cmd = "git diff";
|
|
258
|
+
if (options?.staged) {
|
|
259
|
+
cmd += " --staged";
|
|
260
|
+
}
|
|
261
|
+
if (options?.commit) {
|
|
262
|
+
cmd += ` ${options.commit}`;
|
|
263
|
+
}
|
|
264
|
+
if (options?.path) {
|
|
265
|
+
cmd += ` -- ${options.path}`;
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const result = await execPromise(cmd, { cwd: this.workspaceRoot });
|
|
269
|
+
return result.stdout;
|
|
270
|
+
} catch {
|
|
271
|
+
return "";
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async askUser(question, options) {
|
|
275
|
+
if (this.askUserHandler) {
|
|
276
|
+
return this.askUserHandler(question, options);
|
|
277
|
+
}
|
|
278
|
+
throw new Error("No askUser handler configured");
|
|
279
|
+
}
|
|
280
|
+
showNotification(message, type) {
|
|
281
|
+
if (this.notificationHandler) {
|
|
282
|
+
this.notificationHandler(message, type);
|
|
283
|
+
} else {
|
|
284
|
+
console.log(`[${type || "info"}] ${message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
function createLocalContextProvider(workspaceRoot, options) {
|
|
289
|
+
return new LocalContextProvider(workspaceRoot, options);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export {
|
|
293
|
+
LocalContextProvider,
|
|
294
|
+
createLocalContextProvider
|
|
295
|
+
};
|